/* Copyright (C) Paul Falstad and Iain Sharp This file is part of CircuitJS1. CircuitJS1 is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. CircuitJS1 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with CircuitJS1. If not, see <http://www.gnu.org/licenses/>. */ package com.lushprojects.circuitjs1.client; class Diode { int nodes[]; CirSim sim; Diode(CirSim s) { sim = s; nodes = new int[2]; } void setup(double fw, double zv) { fwdrop = fw; zvoltage = zv; vdcoef = Math.log(1/leakage + 1)/fwdrop; vt = 1/vdcoef; // critical voltage for limiting; current is vt/sqrt(2) at // this voltage vcrit = vt * Math.log(vt/(Math.sqrt(2)*leakage)); if (zvoltage == 0) zoffset = 0; else { // calculate offset which will give us 5mA at zvoltage double i = -.005; zoffset = zvoltage-Math.log(-(1+i/leakage))/vdcoef; } } void reset() { lastvoltdiff = 0; } public double leakage = 1e-14; // was 1e-9; double vt, vdcoef, fwdrop, zvoltage, zoffset; double lastvoltdiff; double vcrit; double limitStep(double vnew, double vold) { double arg; double oo = vnew; // check new voltage; has current changed by factor of e^2? if (vnew > vcrit && Math.abs(vnew - vold) > (vt + vt)) { if(vold > 0) { arg = 1 + (vnew - vold) / vt; if(arg > 0) { // adjust vnew so that the current is the same // as in linearized model from previous iteration. // current at vnew = old current * arg vnew = vold + vt * Math.log(arg); // current at v0 = 1uA double v0 = Math.log(1e-6/leakage)*vt; vnew = Math.max(v0, vnew); } else { vnew = vcrit; } } else { // adjust vnew so that the current is the same // as in linearized model from previous iteration. // (1/vt = slope of load line) vnew = vt *Math.log(vnew/vt); } sim.converged = false; //System.out.println(vnew + " " + oo + " " + vold); } else if (vnew < 0 && zoffset != 0) { // for Zener breakdown, use the same logic but translate the values vnew = -vnew - zoffset; vold = -vold - zoffset; if (vnew > vcrit && Math.abs(vnew - vold) > (vt + vt)) { if(vold > 0) { arg = 1 + (vnew - vold) / vt; if(arg > 0) { vnew = vold + vt * Math.log(arg); double v0 = Math.log(1e-6/leakage)*vt; vnew = Math.max(v0, vnew); //System.out.println(oo + " " + vnew); } else { vnew = vcrit; } } else { vnew = vt *Math.log(vnew/vt); } sim.converged = false; } vnew = -(vnew+zoffset); } return vnew; } void stamp(int n0, int n1) { nodes[0] = n0; nodes[1] = n1; sim.stampNonLinear(nodes[0]); sim.stampNonLinear(nodes[1]); } void doStep(double voltdiff) { // used to have .1 here, but needed .01 for peak detector if (Math.abs(voltdiff-lastvoltdiff) > .01) sim.converged = false; voltdiff = limitStep(voltdiff, lastvoltdiff); lastvoltdiff = voltdiff; double gmin = 0; if (sim.subIterations > 100) { // if we have trouble converging, put a conductance in parallel with the diode. // Gradually increase the conductance value for each iteration. gmin = Math.exp(-9*Math.log(10)*(1-sim.subIterations/3000.)); if (gmin > .1) gmin = .1; } if (voltdiff >= 0 || zvoltage == 0) { // regular diode or forward-biased zener double eval = Math.exp(voltdiff*vdcoef); // make diode linear with negative voltages; aids convergence if (voltdiff < 0) eval = 1; double geq = vdcoef*leakage*eval + gmin; double nc = (eval-1)*leakage - geq*voltdiff; sim.stampConductance(nodes[0], nodes[1], geq); sim.stampCurrentSource(nodes[0], nodes[1], nc); } else { // Zener diode /* * I(Vd) = Is * (exp[Vd*C] - exp[(-Vd-Vz)*C] - 1 ) * * geq is I'(Vd) * nc is I(Vd) + I'(Vd)*(-Vd) */ double geq = leakage*vdcoef* ( Math.exp(voltdiff*vdcoef) + Math.exp((-voltdiff-zoffset)*vdcoef) ) + gmin; double nc = leakage* ( Math.exp(voltdiff*vdcoef) - Math.exp((-voltdiff-zoffset)*vdcoef) - 1 ) + geq*(-voltdiff); sim.stampConductance(nodes[0], nodes[1], geq); sim.stampCurrentSource(nodes[0], nodes[1], nc); } } double calculateCurrent(double voltdiff) { if (voltdiff >= 0 || zvoltage == 0) return leakage*(Math.exp(voltdiff*vdcoef)-1); return leakage* ( Math.exp(voltdiff*vdcoef) - Math.exp((-voltdiff-zoffset)*vdcoef) - 1 ); } }