public override void stamp(Circuit sim) { // equations for transformer: // v1 = L1 di1/dt + M di2/dt // v2 = M di1/dt + L2 di2/dt // we invert that to get: // di1/dt = a1 v1 + a2 v2 // di2/dt = a3 v1 + a4 v2 // integrate di1/dt using trapezoidal approx and we get: // i1(t2) = i1(t1) + dt/2 (i1(t1) + i1(t2)) // = i1(t1) + a1 dt/2 v1(t1) + a2 dt/2 v2(t1) + // a1 dt/2 v1(t2) + a2 dt/2 v2(t2) // the norton equivalent of this for i1 is: // a. current source, I = i1(t1) + a1 dt/2 v1(t1) + a2 dt/2 v2(t1) // b. resistor, G = a1 dt/2 // c. current source controlled by voltage v2, G = a2 dt/2 // and for i2: // a. current source, I = i2(t1) + a3 dt/2 v1(t1) + a4 dt/2 v2(t1) // b. resistor, G = a3 dt/2 // c. current source controlled by voltage v2, G = a4 dt/2 // // For backward euler, // // i1(t2) = i1(t1) + a1 dt v1(t2) + a2 dt v2(t2) // // So the current source value is just i1(t1) and we use // dt instead of dt/2 for the resistor and VCCS. // // first winding goes from node 0 to 2, second is from 1 to 3 double l1 = inductance; double l2 = inductance * ratio * ratio; double m = couplingCoef * Math.Sqrt(l1 * l2); // build inverted matrix double deti = 1 / (l1 * l2 - m * m); double ts = isTrapezoidal ? sim.timeStep / 2 : sim.timeStep; a1 = l2 * deti * ts; // we multiply dt/2 into a1..a4 here a2 = -m * deti * ts; a3 = -m * deti * ts; a4 = l1 * deti * ts; sim.stampConductance(lead_node[0], lead_node[2], a1); sim.stampVCCS(lead_node[0], lead_node[2], lead_node[1], lead_node[3], a2); sim.stampVCCS(lead_node[1], lead_node[3], lead_node[0], lead_node[2], a3); sim.stampConductance(lead_node[1], lead_node[3], a4); sim.stampRightSide(lead_node[0]); sim.stampRightSide(lead_node[1]); sim.stampRightSide(lead_node[2]); sim.stampRightSide(lead_node[3]); }
public override void stamp(Circuit sim) { nodes[0] = lead_node[0]; nodes[1] = lead_node[1]; if (isTrapezoidal) { compResistance = 2 * inductance / sim.timeStep; } else { compResistance = inductance / sim.timeStep; // backward euler } sim.stampResistor(nodes[0], nodes[1], compResistance); sim.stampRightSide(nodes[0]); sim.stampRightSide(nodes[1]); }
public override void stamp(Circuit sim) { // equations for transformer: // v1 = L1 di1/dt + M1 di2/dt + M1 di3/dt // v2 = M1 di1/dt + L2 di2/dt + M2 di3/dt // v3 = M1 di1/dt + M2 di2/dt + L2 di3/dt // we invert that to get: // di1/dt = a1 v1 + a2 v2 + a3 v3 // di2/dt = a4 v1 + a5 v2 + a6 v3 // di3/dt = a7 v1 + a8 v2 + a9 v3 // integrate di1/dt using trapezoidal approx and we get: // i1(t2) = i1(t1) + dt/2 (i1(t1) + i1(t2)) // = i1(t1) + a1 dt/2 v1(t1)+a2 dt/2 v2(t1)+a3 dt/2 v3(t3) + // a1 dt/2 v1(t2)+a2 dt/2 v2(t2)+a3 dt/2 v3(t3) // the norton equivalent of this for i1 is: // a. current source, I = i1(t1) + a1 dt/2 v1(t1) + a2 dt/2 v2(t1) // + a3 dt/2 v3(t1) // b. resistor, G = a1 dt/2 // c. current source controlled by voltage v2, G = a2 dt/2 // d. current source controlled by voltage v3, G = a3 dt/2 // and similarly for i2 // // first winding goes from node 0 to 1, second is from 2 to 3 to 4 double l1 = inductance; double cc = .99; // double m1 = .999*Math.sqrt(l1*l2); // mutual inductance between two halves of the second winding // is equal to self-inductance of either half (slightly less // because the coupling is not perfect) // double m2 = .999*l2; // load pre-inverted matrix a[0] = (1 + cc) / (l1 * (1 + cc - 2 * cc * cc)); a[1] = a[2] = a[3] = a[6] = 2 * cc / ((2 * cc * cc - cc - 1) * inductance * ratio); a[4] = a[8] = -4 * (1 + cc) / ((2 * cc * cc - cc - 1) * l1 * ratio * ratio); a[5] = a[7] = 4 * cc / ((2 * cc * cc - cc - 1) * l1 * ratio * ratio); int i; for (i = 0; i != 9; i++) { a[i] *= sim.timeStep / 2; } sim.stampConductance(lead_node[0], lead_node[1], a[0]); sim.stampVCCS(lead_node[0], lead_node[1], lead_node[2], lead_node[3], a[1]); sim.stampVCCS(lead_node[0], lead_node[1], lead_node[3], lead_node[4], a[2]); sim.stampVCCS(lead_node[2], lead_node[3], lead_node[0], lead_node[1], a[3]); sim.stampConductance(lead_node[2], lead_node[3], a[4]); sim.stampVCCS(lead_node[2], lead_node[3], lead_node[3], lead_node[4], a[5]); sim.stampVCCS(lead_node[3], lead_node[4], lead_node[0], lead_node[1], a[6]); sim.stampVCCS(lead_node[3], lead_node[4], lead_node[2], lead_node[3], a[7]); sim.stampConductance(lead_node[3], lead_node[4], a[8]); for (i = 0; i != 5; i++) { sim.stampRightSide(lead_node[i]); } }
public override void stamp(Circuit sim) { // Capacitor companion model using trapezoidal approximation // (Norton equivalent) consists of a current source in // parallel with a resistor. Trapezoidal is more accurate // than backward euler but can cause oscillatory behavior // if RC is small relative to the timestep. if (isTrapezoidal) { compResistance = sim.timeStep / (2 * capacitance); } else { compResistance = sim.timeStep / capacitance; } sim.stampResistor(lead_node[0], lead_node[1], compResistance); sim.stampRightSide(lead_node[0]); sim.stampRightSide(lead_node[1]); }
public void stamp(Circuit sim, double deltaTime, int n0, int n1) { // inductor companion model using trapezoidal or backward euler // approximations (Norton equivalent) consists of a current // source in parallel with a resistor. Trapezoidal is more // accurate than backward euler but can cause oscillatory behavior. // The oscillation is a real problem in circuits with switches. nodes[0] = n0; nodes[1] = n1; if (isTrapezoidal) { compResistance = 2 * inductance / deltaTime; } else { compResistance = inductance / deltaTime; // backward euler } sim.stampResistor(nodes[0], nodes[1], compResistance); sim.stampRightSide(nodes[0]); sim.stampRightSide(nodes[1]); }
public override void step(Circuit sim) { double vd = lead_volt[1] - lead_volt[0]; if (Math.Abs(lastvd - vd) > 0.1) { sim.converged = false; } else if (lead_volt[2] > maxOut + 0.1 || lead_volt[2] < minOut - 0.1) { sim.converged = false; } double x = 0; int vn = sim.nodeCount + voltSource; double dx = 0; if (vd >= maxOut / gain && (lastvd >= 0 || getRand(4) == 1)) { dx = 1E-4; x = maxOut - dx * maxOut / gain; } else if (vd <= minOut / gain && (lastvd <= 0 || getRand(4) == 1)) { dx = 1E-4; x = minOut - dx * minOut / gain; } else { dx = gain; } // newton-raphson sim.stampMatrix(vn, lead_node[0], dx); sim.stampMatrix(vn, lead_node[1], -dx); sim.stampMatrix(vn, lead_node[2], 1); sim.stampRightSide(vn, x); lastvd = vd; }
public override void step(Circuit sim) { double vbc = lead_volt[0] - lead_volt[1]; // typically negative double vbe = lead_volt[0] - lead_volt[2]; // typically positive if (Math.Abs(vbc - lastvbc) > 0.01 || Math.Abs(vbe - lastvbe) > 0.01) { sim.converged = false; } gmin = 0; if (sim.subIterations > 100) { // if we have trouble converging, put a conductance in parallel with // all P-N junctions. Gradually increase the conductance value for each iteration. gmin = Math.Exp(-9 * Math.Log(10) * (1 - sim.subIterations / 3000.0)); if (gmin > .1) { gmin = .1; } } vbc = pnp * limitStep(sim, pnp * vbc, pnp * lastvbc); vbe = pnp * limitStep(sim, pnp * vbe, pnp * lastvbe); lastvbc = vbc; lastvbe = vbe; double pcoef = vdcoef * pnp; double expbc = Math.Exp(vbc * pcoef); double expbe = Math.Exp(vbe * pcoef); if (expbe < 1) { expbe = 1; } ie = pnp * leakage * (-(expbe - 1) + rgain * (expbc - 1)); ic = pnp * leakage * (fgain * (expbe - 1) - (expbc - 1)); ib = -(ie + ic); double gee = -leakage * vdcoef * expbe; double gec = rgain * leakage * vdcoef * expbc; double gce = -gee * fgain; double gcc = -gec * (1 / rgain); // stamps from page 302 of Pillage. Node 0 is the base, // node 1 the collector, node 2 the emitter. Also stamp // minimum conductance (gmin) between b,e and b,c sim.stampMatrix(lead_node[0], lead_node[0], -gee - gec - gce - gcc + gmin * 2); sim.stampMatrix(lead_node[0], lead_node[1], gec + gcc - gmin); sim.stampMatrix(lead_node[0], lead_node[2], gee + gce - gmin); sim.stampMatrix(lead_node[1], lead_node[0], gce + gcc - gmin); sim.stampMatrix(lead_node[1], lead_node[1], -gcc + gmin); sim.stampMatrix(lead_node[1], lead_node[2], -gce); sim.stampMatrix(lead_node[2], lead_node[0], gee + gec - gmin); sim.stampMatrix(lead_node[2], lead_node[1], -gec); sim.stampMatrix(lead_node[2], lead_node[2], -gee + gmin); // we are solving for v(k+1), not delta v, so we use formula // 10.5.13, multiplying J by v(k) sim.stampRightSide(lead_node[0], -ib - (gec + gcc) * vbc - (gee + gce) * vbe); sim.stampRightSide(lead_node[1], -ic + gce * vbe + gcc * vbc); sim.stampRightSide(lead_node[2], -ie + gee * vbe + gec * vbc); }
public override void step(Circuit sim) { double[] vs = new double[3]; vs[0] = lead_volt[0]; vs[1] = lead_volt[1]; vs[2] = lead_volt[2]; if (vs[1] > lastv1 + .5) { vs[1] = lastv1 + .5; } if (vs[1] < lastv1 - .5) { vs[1] = lastv1 - .5; } if (vs[2] > lastv2 + .5) { vs[2] = lastv2 + .5; } if (vs[2] < lastv2 - .5) { vs[2] = lastv2 - .5; } int source = 1; int drain = 2; if ((pnp ? -1 : 1) * vs[1] > (pnp ? -1 : 1) * vs[2]) { source = 2; drain = 1; } int gate = 0; double vgs = vs[gate] - vs[source]; double vds = vs[drain] - vs[source]; if (Math.Abs(lastv1 - vs[1]) > .01 || Math.Abs(lastv2 - vs[2]) > .01) { sim.converged = false; } lastv1 = vs[1]; lastv2 = vs[2]; double realvgs = vgs; double realvds = vds; vgs *= (pnp ? -1 : 1); vds *= (pnp ? -1 : 1); ids = 0; gm = 0; double Gds = 0; double beta = getBeta(); if (vgs > .5 && this is JfetElm) { sim.panic("JFET is reverse biased!", this); return; } if (vgs < _threshold) { // should be all zero, but that causes a singular matrix, // so instead we treat it as a large resistor Gds = 1e-8; ids = vds * Gds; mode = 0; } else if (vds < vgs - _threshold) { // linear ids = beta * ((vgs - _threshold) * vds - vds * vds * .5); gm = beta * vds; Gds = beta * (vgs - vds - _threshold); mode = 1; } else { // saturation; Gds = 0 gm = beta * (vgs - _threshold); // use very small Gds to avoid nonconvergence Gds = 1e-8; ids = 0.5 * beta * (vgs - _threshold) * (vgs - _threshold) + (vds - (vgs - _threshold)) * Gds; mode = 2; } double rs = -(pnp ? -1 : 1) * ids + Gds * realvds + gm * realvgs; sim.stampMatrix(lead_node[drain], lead_node[drain], Gds); sim.stampMatrix(lead_node[drain], lead_node[source], -Gds - gm); sim.stampMatrix(lead_node[drain], lead_node[gate], gm); sim.stampMatrix(lead_node[source], lead_node[drain], -Gds); sim.stampMatrix(lead_node[source], lead_node[source], Gds + gm); sim.stampMatrix(lead_node[source], lead_node[gate], -gm); sim.stampRightSide(lead_node[drain], rs); sim.stampRightSide(lead_node[source], -rs); if (source == 2 && (pnp ? -1 : 1) == 1 || source == 1 && (pnp ? -1 : 1) == -1) { ids = -ids; } }
/*public override double getPower() { * return (lead_volt[0] - lead_volt[2]) * current; * }*/ public override void step(Circuit sim) { double[] vs = new double[3]; vs[0] = lead_volt[0]; vs[1] = lead_volt[1]; vs[2] = lead_volt[2]; if (vs[1] > lastv1 + 0.5) { vs[1] = lastv1 + 0.5; } if (vs[1] < lastv1 - 0.5) { vs[1] = lastv1 - 0.5; } if (vs[2] > lastv2 + 0.5) { vs[2] = lastv2 + 0.5; } if (vs[2] < lastv2 - 0.5) { vs[2] = lastv2 - 0.5; } int grid = 1; int cath = 2; int plate = 0; double vgk = vs[grid] - vs[cath]; double vpk = vs[plate] - vs[cath]; if (Math.Abs(lastv0 - vs[0]) > 0.01 || Math.Abs(lastv1 - vs[1]) > 0.01 || Math.Abs(lastv2 - vs[2]) > 0.01) { sim.converged = false; } lastv0 = vs[0]; lastv1 = vs[1]; lastv2 = vs[2]; double ids = 0; double gm = 0; double Gds = 0; double ival = vgk + vpk / mu; currentg = 0; if (vgk > .01) { sim.stampResistor(lead_node[grid], lead_node[cath], gridCurrentR); currentg = vgk / gridCurrentR; } if (ival < 0) { // should be all zero, but that causes a singular matrix, // so instead we treat it as a large resistor Gds = 1E-8; ids = vpk * Gds; } else { ids = Math.Pow(ival, 1.5) / kg1; double q = 1.5 * Math.Sqrt(ival) / kg1; // gm = dids/dgk; // Gds = dids/dpk; Gds = q; gm = q / mu; } currentp = ids; currentc = ids + currentg; double rs = -ids + Gds * vpk + gm * vgk; sim.stampMatrix(lead_node[plate], lead_node[plate], Gds); sim.stampMatrix(lead_node[plate], lead_node[cath], -Gds - gm); sim.stampMatrix(lead_node[plate], lead_node[grid], gm); sim.stampMatrix(lead_node[cath], lead_node[plate], -Gds); sim.stampMatrix(lead_node[cath], lead_node[cath], Gds + gm); sim.stampMatrix(lead_node[cath], lead_node[grid], -gm); sim.stampRightSide(lead_node[plate], rs); sim.stampRightSide(lead_node[cath], -rs); }