public async Task <IActionResult> UpdateContribution(string partyCode, [FromBody] UpdateContributions updateContributions) { try { if (updateContributions.ContributionsToRemove == null) { updateContributions.ContributionsToRemove = new List <UserContribution>(); } if (updateContributions.NewContributions == null) { updateContributions.NewContributions = new List <UserContribution>(); } if (updateContributions != null && updateContributions.ContributionsToRemove.Count == 0 && updateContributions.NewContributions.Count == 0) { return(Ok()); } PartyGoer partyGoer = await _partyGoerService.GetCurrentPartyGoerAsync(); await _partyService.UpdateContributionsAsync(partyCode, updateContributions.NewContributions.Select(p => CreateContribution(partyGoer, p)).ToList(), updateContributions.ContributionsToRemove.Select(p => CreateContribution(partyGoer, p)).ToList()); } catch (Exception ex) { await _logService.LogExceptionAsync(ex, "Error occurred while trying to add contribution"); return(StatusCode(500)); } return(Ok()); }
/// <inheritdoc/> void IBiasingBehavior.Load() { var con = _contributions; con.Reset(); var vt = Constants.KOverQ * Parameters.Temperature; double DrainSatCur, SourceSatCur; if ((Properties.TempSatCurDensity == 0) || (Parameters.DrainArea == 0) || (Parameters.SourceArea == 0)) { DrainSatCur = Parameters.ParallelMultiplier * Properties.TempSatCur; SourceSatCur = Parameters.ParallelMultiplier * Properties.TempSatCur; } else { DrainSatCur = Parameters.ParallelMultiplier * Properties.TempSatCurDensity * Parameters.DrainArea; SourceSatCur = Parameters.ParallelMultiplier * Properties.TempSatCurDensity * Parameters.SourceArea; } var Beta = Properties.TempTransconductance * Parameters.Width * Parameters.ParallelMultiplier / Properties.EffectiveLength; // Get the current voltages Initialize(out var vgs, out var vds, out var vbs, out var check); var vbd = vbs - vds; var vgd = vgs - vds; if (vbs <= -3 * vt) { con.Bs.G = _config.Gmin; con.Bs.C = con.Bs.G * vbs - SourceSatCur; } else { var evbs = Math.Exp(Math.Min(MaximumExponentArgument, vbs / vt)); con.Bs.G = SourceSatCur * evbs / vt + _config.Gmin; con.Bs.C = SourceSatCur * (evbs - 1) + _config.Gmin * vbs; } if (vbd <= -3 * vt) { con.Bd.G = _config.Gmin; con.Bd.C = con.Bd.G * vbd - DrainSatCur; } else { var evbd = Math.Exp(Math.Min(MaximumExponentArgument, vbd / vt)); con.Bd.G = DrainSatCur * evbd / vt + _config.Gmin; con.Bd.C = DrainSatCur * (evbd - 1) + _config.Gmin * vbd; } if (vds >= 0) { Mode = 1; } else { Mode = -1; } // An example out in the wild once-good, now-bad spaghetti code... Not touching this too much. { /* moseq2(vds,vbs,vgs,gm,gds,gmbs,qg,qc,qb, * cggb,cgdb,cgsb,cbgb,cbdb,cbsb) */ // Note: cgdb, cgsb, cbdb, cbsb never used /* * this routine evaluates the drain current, its derivatives and * the charges associated with the gate, channel and bulk * for mosfets * */ double arg; double sarg; double[] a4 = new double[4], b4 = new double[4], x4 = new double[8], poly4 = new double[8]; double beta1; double dsrgdb; double d2sdb2; double barg; double d2bdb2; double factor; double dbrgdb; double eta; double vbin; double argd = 0.0; double args = 0.0; double argss; double argsd; double argxs = 0.0; double argxd = 0.0; double daddb2; double dasdb2; double dbargd; double dbargs; double dbxwd; double dbxws; double dgddb2; double dgddvb; double dgdvds; double gamasd; double xwd; double xws; double ddxwd; double gammad; double vth; double cfs; double cdonco; double xn = 0.0; double argg = 0.0; double vgst; double sarg3; double sbiarg; double dgdvbs; double body; double gdbdv; double dodvbs; double dodvds = 0.0; double dxndvd = 0.0; double dxndvb = 0.0; double udenom; double dudvgs; double dudvds; double dudvbs; double gammd2; double argv; double vgsx; double ufact; double ueff; double dsdvgs; double dsdvbs; double a1; double a3; double a; double b1; double b3; double b; double c1; double c; double d1; double fi; double p0; double p2; double p3; double p4; double p; double r3; double r; double ro; double s2; double s; double v1; double v2; double xv; double y3; double delta4; double xvalid = 0; double bsarg = 0; double dbsrdb; double bodys = 0; double gdbdvs = 0; double sargv; double xlfact; double dldsat; double xdv; double xlv; double vqchan; double dqdsat; double vl; double dfundg; double dfunds; double dfundb; double xls; double dldvgs; double dldvds; double dldvbs; double dfact; double clfact; double xleff; double deltal; double xwb; double vdson; double cdson; double didvds; double gdson; double gmw; double gbson; double expg; double xld; double xlamda = ModelParameters.Lambda; /* 'local' variables - these switch d & s around appropriately * so that we don't have to worry about vds < 0 */ double lvbs = Mode == 1 ? vbs : vbd; double lvds = Mode * vds; double lvgs = Mode == 1 ? vgs : vgd; double phiMinVbs = Properties.TempPhi - lvbs; double tmp; /* a temporary variable, not used for more than */ /* about 10 lines at a time */ int iknt; int jknt; int i; int j; double sphi; double sphi3; /* * compute some useful quantities */ if (lvbs <= 0.0) { sarg = Math.Sqrt(phiMinVbs); dsrgdb = -0.5 / sarg; d2sdb2 = 0.5 * dsrgdb / phiMinVbs; } else { sphi = Math.Sqrt(Properties.TempPhi); sphi3 = Properties.TempPhi * sphi; sarg = sphi / (1.0 + 0.5 * lvbs / Properties.TempPhi); tmp = sarg / sphi3; dsrgdb = -0.5 * sarg * tmp; d2sdb2 = -dsrgdb * tmp; } if ((lvbs - lvds) <= 0) { barg = Math.Sqrt(phiMinVbs + lvds); dbrgdb = -0.5 / barg; d2bdb2 = 0.5 * dbrgdb / (phiMinVbs + lvds); } else { sphi = Math.Sqrt(Properties.TempPhi); /* added by HT 050523 */ sphi3 = Properties.TempPhi * sphi; /* added by HT 050523 */ barg = sphi / (1.0 + 0.5 * (lvbs - lvds) / Properties.TempPhi); tmp = barg / sphi3; dbrgdb = -0.5 * barg * tmp; d2bdb2 = -dbrgdb * tmp; } /* * calculate threshold voltage (von) * narrow-channel effect */ // XXX constant per device factor = 0.125 * ModelParameters.NarrowFactor * 2.0 * Math.PI * EpsilonSilicon / Properties.OxideCap * Properties.EffectiveLength; // XXX constant per device eta = 1.0 + factor; vbin = Properties.TempVbi * ModelParameters.MosfetType + factor * phiMinVbs; if ((ModelParameters.Gamma > 0.0) || (ModelParameters.SubstrateDoping > 0.0)) { xwd = ModelTemperature.Properties.Xd * barg; xws = ModelTemperature.Properties.Xd * sarg; // Short-channel effect with vds .ne. 0.0 argss = 0.0; argsd = 0.0; dbargs = 0.0; dbargd = 0.0; dgdvds = 0.0; dgddb2 = 0.0; if (ModelParameters.JunctionDepth > 0) { tmp = 2.0 / ModelParameters.JunctionDepth; argxs = 1.0 + xws * tmp; argxd = 1.0 + xwd * tmp; args = Math.Sqrt(argxs); argd = Math.Sqrt(argxd); tmp = .5 * ModelParameters.JunctionDepth / Properties.EffectiveLength; argss = tmp * (args - 1.0); argsd = tmp * (argd - 1.0); } gamasd = ModelParameters.Gamma * (1.0 - argss - argsd); dbxwd = ModelTemperature.Properties.Xd * dbrgdb; dbxws = ModelTemperature.Properties.Xd * dsrgdb; if (ModelParameters.JunctionDepth > 0) { tmp = 0.5 / Properties.EffectiveLength; dbargs = tmp * dbxws / args; dbargd = tmp * dbxwd / argd; dasdb2 = -ModelTemperature.Properties.Xd * (d2sdb2 + dsrgdb * dsrgdb * ModelTemperature.Properties.Xd / (ModelParameters.JunctionDepth * argxs)) / (Properties.EffectiveLength * args); daddb2 = -ModelTemperature.Properties.Xd * (d2bdb2 + dbrgdb * dbrgdb * ModelTemperature.Properties.Xd / (ModelParameters.JunctionDepth * argxd)) / (Properties.EffectiveLength * argd); dgddb2 = -0.5 * ModelParameters.Gamma * (dasdb2 + daddb2); } dgddvb = -ModelParameters.Gamma * (dbargs + dbargd); if (ModelParameters.JunctionDepth > 0) { ddxwd = -dbxwd; dgdvds = -ModelParameters.Gamma * 0.5 * ddxwd / (Properties.EffectiveLength * argd); } } else { gamasd = ModelParameters.Gamma; dgddvb = 0.0; dgdvds = 0.0; dgddb2 = 0.0; } var von = vbin + gamasd * sarg; vth = von; var vdsat = 0.0; if (ModelParameters.FastSurfaceStateDensity != 0.0 && Properties.OxideCap != 0.0) { // XXX constant per model cfs = Constants.Charge * ModelParameters.FastSurfaceStateDensity * 1e4 /*(cm**2/m**2)*/; cdonco = -(gamasd * dsrgdb + dgddvb * sarg) + factor; xn = 1.0 + cfs / Properties.OxideCap * Parameters.ParallelMultiplier * Parameters.Width * Properties.EffectiveLength + cdonco; tmp = vt * xn; von += tmp; argg = 1.0 / tmp; vgst = lvgs - von; } else { vgst = lvgs - von; if (lvgs <= vbin) { // Cutoff region con.Ds.G = 0.0; goto line1050; } } /* * compute some more useful quantities */ sarg3 = sarg * sarg * sarg; /* XXX constant per model */ sbiarg = Math.Sqrt(Properties.TempBulkPotential); gammad = gamasd; dgdvbs = dgddvb; body = barg * barg * barg - sarg3; gdbdv = 2.0 * gammad * (barg * barg * dbrgdb - sarg * sarg * dsrgdb); dodvbs = -factor + dgdvbs * sarg + gammad * dsrgdb; if (ModelParameters.FastSurfaceStateDensity == 0.0) { goto line400; } if (Properties.OxideCap == 0.0) { goto line410; } dxndvb = 2.0 * dgdvbs * dsrgdb + gammad * d2sdb2 + dgddb2 * sarg; dodvbs += vt * dxndvb; dxndvd = dgdvds * dsrgdb; dodvds = dgdvds * sarg + vt * dxndvd; /* * evaluate effective mobility and its derivatives */ line400: if (Properties.OxideCap <= 0.0) { goto line410; } udenom = vgst; tmp = ModelParameters.CriticalField * 100 /* cm/m */ * EpsilonSilicon / ModelTemperature.Properties.OxideCapFactor; if (udenom <= tmp) { goto line410; } ufact = Math.Exp(ModelParameters.CriticalFieldExp * Math.Log(tmp / udenom)); ueff = ModelParameters.SurfaceMobility * 1e-4 /*(m**2/cm**2) */ * ufact; dudvgs = -ufact * ModelParameters.CriticalFieldExp / udenom; dudvds = 0.0; dudvbs = ModelParameters.CriticalFieldExp * ufact * dodvbs / vgst; goto line500; line410: ufact = 1.0; ueff = ModelParameters.SurfaceMobility * 1e-4 /*(m**2/cm**2) */; dudvgs = 0.0; dudvds = 0.0; dudvbs = 0.0; /* * evaluate saturation voltage and its derivatives according to * grove-frohman equation */ line500: vgsx = lvgs; gammad = gamasd / eta; dgdvbs = dgddvb; if (ModelParameters.FastSurfaceStateDensity != 0 && Properties.OxideCap != 0) { vgsx = Math.Max(lvgs, von); } if (gammad > 0) { gammd2 = gammad * gammad; argv = (vgsx - vbin) / eta + phiMinVbs; if (argv <= 0.0) { vdsat = 0.0; dsdvgs = 0.0; dsdvbs = 0.0; } else { arg = Math.Sqrt(1.0 + 4.0 * argv / gammd2); vdsat = (vgsx - vbin) / eta + gammd2 * (1.0 - arg) / 2.0; vdsat = Math.Max(vdsat, 0.0); dsdvgs = (1.0 - 1.0 / arg) / eta; dsdvbs = (gammad * (1.0 - arg) + 2.0 * argv / (gammad * arg)) / eta * dgdvbs + 1.0 / arg + factor * dsdvgs; } } else { vdsat = (vgsx - vbin) / eta; vdsat = Math.Max(vdsat, 0.0); dsdvgs = 1.0; dsdvbs = 0.0; } if (ModelParameters.MaxDriftVelocity > 0) { /* * evaluate saturation voltage and its derivatives * according to baum's theory of scattering velocity * saturation */ v1 = (vgsx - vbin) / eta + phiMinVbs; v2 = phiMinVbs; xv = ModelParameters.MaxDriftVelocity * Properties.EffectiveLength / ueff; a1 = gammad / 0.75; b1 = -2.0 * (v1 + xv); c1 = -2.0 * gammad * xv; d1 = 2.0 * v1 * (v2 + xv) - v2 * v2 - 4.0 / 3.0 * gammad * sarg3; a = -b1; b = a1 * c1 - 4.0 * d1; c = -d1 * (a1 * a1 - 4.0 * b1) - c1 * c1; r = -a * a / 3.0 + b; s = 2.0 * a * a * a / 27.0 - a * b / 3.0 + c; r3 = r * r * r; s2 = s * s; p = s2 / 4.0 + r3 / 27.0; p0 = Math.Abs(p); p2 = Math.Sqrt(p0); if (p < 0) { ro = Math.Sqrt(s2 / 4.0 + p0); ro = Math.Log(ro) / 3.0; ro = Math.Exp(ro); fi = Math.Atan(-2.0 * p2 / s); y3 = 2.0 * ro * Math.Cos(fi / 3.0) - a / 3.0; } else { p3 = -s / 2.0 + p2; p3 = Math.Exp(Math.Log(Math.Abs(p3)) / 3.0); p4 = -s / 2.0 - p2; p4 = Math.Exp(Math.Log(Math.Abs(p4)) / 3.0); y3 = p3 + p4 - a / 3.0; } iknt = 0; a3 = Math.Sqrt(a1 * a1 / 4.0 - b1 + y3); b3 = Math.Sqrt(y3 * y3 / 4.0 - d1); for (i = 1; i <= 4; i++) { a4[i - 1] = a1 / 2.0 + _sig1[i - 1] * a3; b4[i - 1] = y3 / 2.0 + _sig2[i - 1] * b3; delta4 = a4[i - 1] * a4[i - 1] / 4.0 - b4[i - 1]; if (delta4 < 0) { continue; } iknt += 1; tmp = Math.Sqrt(delta4); x4[iknt - 1] = -a4[i - 1] / 2.0 + tmp; iknt += 1; x4[iknt - 1] = -a4[i - 1] / 2.0 - tmp; } jknt = 0; for (j = 1; j <= iknt; j++) { if (x4[j - 1] <= 0) { continue; } /* XXX implement this sanely */ poly4[j - 1] = x4[j - 1] * x4[j - 1] * x4[j - 1] * x4[j - 1] + a1 * x4[j - 1] * x4[j - 1] * x4[j - 1]; poly4[j - 1] = poly4[j - 1] + b1 * x4[j - 1] * x4[j - 1] + c1 * x4[j - 1] + d1; if (Math.Abs(poly4[j - 1]) > 1.0e-6) { continue; } jknt += 1; if (jknt <= 1) { xvalid = x4[j - 1]; } if (x4[j - 1] > xvalid) { continue; } xvalid = x4[j - 1]; } if (jknt > 0) { vdsat = xvalid * xvalid - phiMinVbs; } } // Evaluate effective channel length and its derivatives if (lvds != 0.0) { gammad = gamasd; if ((lvbs - vdsat) <= 0) { bsarg = Math.Sqrt(vdsat + phiMinVbs); dbsrdb = -0.5 / bsarg; } else { sphi = Math.Sqrt(Properties.TempPhi); /* added by HT 050523 */ sphi3 = Properties.TempPhi * sphi; /* added by HT 050523 */ bsarg = sphi / (1.0 + 0.5 * (lvbs - vdsat) / Properties.TempPhi); dbsrdb = -0.5 * bsarg * bsarg / sphi3; } bodys = bsarg * bsarg * bsarg - sarg3; gdbdvs = 2.0 * gammad * (bsarg * bsarg * dbsrdb - sarg * sarg * dsrgdb); if (ModelParameters.MaxDriftVelocity <= 0) { if (ModelParameters.SubstrateDoping == 0.0 || xlamda > 0.0) { dldvgs = 0.0; dldvds = 0.0; dldvbs = 0.0; } else { argv = (lvds - vdsat) / 4.0; sargv = Math.Sqrt(1.0 + argv * argv); arg = Math.Sqrt(argv + sargv); xlfact = ModelTemperature.Properties.Xd / (Properties.EffectiveLength * lvds); xlamda = xlfact * arg; dldsat = lvds * xlamda / (8.0 * sargv); dldvgs = dldsat * dsdvgs; dldvds = -xlamda + dldsat; dldvbs = dldsat * dsdvbs; } } else { argv = (vgsx - vbin) / eta - vdsat; xdv = ModelTemperature.Properties.Xd / Math.Sqrt(ModelParameters.ChannelCharge); xlv = ModelParameters.MaxDriftVelocity * xdv / (2.0 * ueff); vqchan = argv - gammad * bsarg; dqdsat = -1.0 + gammad * dbsrdb; vl = ModelParameters.MaxDriftVelocity * Properties.EffectiveLength; dfunds = vl * dqdsat - ueff * vqchan; dfundg = (vl - ueff * vdsat) / eta; dfundb = -vl * (1.0 + dqdsat - factor / eta) + ueff * (gdbdvs - dgdvbs * bodys / 1.5) / eta; dsdvgs = -dfundg / dfunds; dsdvbs = -dfundb / dfunds; if (ModelParameters.SubstrateDoping == 0.0 || xlamda > 0.0) { dldvgs = 0.0; dldvds = 0.0; dldvbs = 0.0; } else { argv = lvds - vdsat; argv = Math.Max(argv, 0.0); xls = Math.Sqrt(xlv * xlv + argv); dldsat = xdv / (2.0 * xls); xlfact = xdv / (Properties.EffectiveLength * lvds); xlamda = xlfact * (xls - xlv); dldsat /= Properties.EffectiveLength; dldvgs = dldsat * dsdvgs; dldvds = -xlamda + dldsat; dldvbs = dldsat * dsdvbs; } } } else { dldvgs = 0.0; dldvds = 0.0; dldvbs = 0.0; } /* * limit channel shortening at punch-through */ xwb = ModelTemperature.Properties.Xd * sbiarg; xld = Properties.EffectiveLength - xwb; clfact = 1.0 - xlamda * lvds; dldvds = -xlamda - dldvds; xleff = Properties.EffectiveLength * clfact; deltal = xlamda * lvds * Properties.EffectiveLength; if (ModelParameters.SubstrateDoping == 0.0) { xwb = 0.25e-6; } if (xleff < xwb) { xleff = xwb / (1.0 + (deltal - xld) / xwb); clfact = xleff / Properties.EffectiveLength; dfact = xleff * xleff / (xwb * xwb); dldvgs = dfact * dldvgs; dldvds = dfact * dldvds; dldvbs = dfact * dldvbs; } // Evaluate effective beta (effective kp) beta1 = Beta * ufact / clfact; // Test for mode of operation and branch appropriately gammad = gamasd; dgdvbs = dgddvb; if (lvds <= 1.0e-10) { if (lvgs <= von) { if ((ModelParameters.FastSurfaceStateDensity == 0.0) || (Properties.OxideCap == 0.0)) { con.Ds.G = 0.0; goto line1050; } con.Ds.G = beta1 * (von - vbin - gammad * sarg) * Math.Exp(argg * (lvgs - von)); goto line1050; } con.Ds.G = beta1 * (lvgs - vbin - gammad * sarg); goto line1050; } if (ModelParameters.FastSurfaceStateDensity != 0 && Properties.OxideCap != 0) { if (lvgs > von) { goto line900; } } else { if (lvgs > vbin) { goto line900; } goto doneval; } if (lvgs > von) { goto line900; } // Subthreshold region if (vdsat <= 0) { con.Ds.G = 0.0; if (lvgs > vth) { goto doneval; } goto line1050; } vdson = Math.Min(vdsat, lvds); if (lvds > vdsat) { barg = bsarg; body = bodys; gdbdv = gdbdvs; } cdson = beta1 * ((von - vbin - eta * vdson * 0.5) * vdson - gammad * body / 1.5); didvds = beta1 * (von - vbin - eta * vdson - gammad * barg); gdson = -cdson * dldvds / clfact - beta1 * dgdvds * body / 1.5; if (lvds < vdsat) { gdson += didvds; } gbson = -cdson * dldvbs / clfact + beta1 * (dodvbs * vdson + factor * vdson - dgdvbs * body / 1.5 - gdbdv); if (lvds > vdsat) { gbson += didvds * dsdvbs; } expg = Math.Exp(argg * (lvgs - von)); con.Ds.C = cdson * expg; gmw = con.Ds.C * argg; Gm = gmw; if (lvds > vdsat) { Gm = gmw + didvds * dsdvgs * expg; } tmp = gmw * (lvgs - von) / xn; con.Ds.G = gdson * expg - Gm * dodvds - tmp * dxndvd; Gmbs = gbson * expg - Gm * dodvbs - tmp * dxndvb; goto doneval; line900: if (lvds <= vdsat) { // Linear region con.Ds.C = beta1 * ((lvgs - vbin - eta * lvds / 2.0) * lvds - gammad * body / 1.5); arg = con.Ds.C * (dudvgs / ufact - dldvgs / clfact); Gm = arg + beta1 * lvds; arg = con.Ds.C * (dudvds / ufact - dldvds / clfact); con.Ds.G = arg + beta1 * (lvgs - vbin - eta * lvds - gammad * barg - dgdvds * body / 1.5); arg = con.Ds.C * (dudvbs / ufact - dldvbs / clfact); Gmbs = arg - beta1 * (gdbdv + dgdvbs * body / 1.5 - factor * lvds); } else { // Saturation region con.Ds.C = beta1 * ((lvgs - vbin - eta * vdsat / 2.0) * vdsat - gammad * bodys / 1.5); arg = con.Ds.C * (dudvgs / ufact - dldvgs / clfact); Gm = arg + beta1 * vdsat + beta1 * (lvgs - vbin - eta * vdsat - gammad * bsarg) * dsdvgs; con.Ds.G = -con.Ds.C * dldvds / clfact - beta1 * dgdvds * bodys / 1.5; arg = con.Ds.C * (dudvbs / ufact - dldvbs / clfact); Gmbs = arg - beta1 * (gdbdvs + dgdvbs * bodys / 1.5 - factor * vdsat) + beta1 * (lvgs - vbin - eta * vdsat - gammad * bsarg) * dsdvbs; } // Compute charges for "on" region goto doneval; // Finish special cases line1050: con.Ds.C = 0.0; Gm = 0.0; Gmbs = 0.0; // Finished doneval: Von = ModelParameters.MosfetType * von; Vdsat = ModelParameters.MosfetType * vdsat; } // COMPUTE EQUIVALENT DRAIN CURRENT SOURCE Gds = con.Ds.G; Id = Mode * con.Ds.C - con.Bd.C; Vbs = vbs; Vbd = vbd; Vgs = vgs; Vds = vds; // Update with time-dependent calculations UpdateContributions?.Invoke(this, _args); // Check convergence if (!Parameters.Off && _iteration.Mode != IterationModes.Fix) { if (check) { _iteration.IsConvergent = false; } } Gbs = con.Bs.G; Ibs = con.Bs.C; Gbd = con.Bd.G; Ibd = con.Bd.C; // Right hand side contributions double xnrm, xrev; con.Bs.C = ModelParameters.MosfetType * (con.Bs.C - con.Bs.G * vbs); con.Bd.C = ModelParameters.MosfetType * (con.Bd.C - con.Bd.G * vbd); if (Mode >= 0) { xnrm = 1; xrev = 0; con.Ds.C = ModelParameters.MosfetType * (con.Ds.C - con.Ds.G * vds - Gm * vgs - Gmbs * vbs); } else { xnrm = 0; xrev = 1; con.Ds.C = -ModelParameters.MosfetType * (con.Ds.C - con.Ds.G * (-vds) - Gm * vgd - Gmbs * vbd); } _elements.Add( // Y-matrix Properties.DrainConductance, con.Gd.G + con.Gs.G + con.Gb.G, Properties.SourceConductance, con.Bd.G + con.Bs.G + con.Gb.G, Properties.DrainConductance + con.Ds.G + con.Bd.G + xrev * (Gm + Gmbs) + con.Gd.G, Properties.SourceConductance + con.Ds.G + con.Bs.G + xnrm * (Gm + Gmbs) + con.Gs.G, -Properties.DrainConductance, -con.Gb.G, -con.Gd.G, -con.Gs.G, -Properties.SourceConductance, -con.Gb.G, -con.Bd.G, -con.Bs.G, -Properties.DrainConductance, (xnrm - xrev) * Gm - con.Gd.G, -con.Bd.G + (xnrm - xrev) * Gmbs, -con.Ds.G - xnrm * (Gm + Gmbs), -(xnrm - xrev) * Gm - con.Gs.G, -Properties.SourceConductance, -con.Bs.G - (xnrm - xrev) * Gmbs, -con.Ds.G - xrev * (Gm + Gmbs), // Right hand side vector -ModelParameters.MosfetType * (con.Gs.C + con.Gb.C + con.Gd.C), -(con.Bs.C + con.Bd.C - ModelParameters.MosfetType * con.Gb.C), con.Bd.C - con.Ds.C + ModelParameters.MosfetType * con.Gd.C, con.Ds.C + con.Bs.C + ModelParameters.MosfetType * con.Gs.C ); }
/// <inheritdoc/> void IBiasingBehavior.Load() { var con = _contributions; con.Reset(); var vt = Constants.KOverQ * Parameters.Temperature; double DrainSatCur, SourceSatCur; if ((Properties.TempSatCurDensity == 0) || (Parameters.DrainArea == 0) || (Parameters.SourceArea == 0)) { DrainSatCur = Parameters.ParallelMultiplier * Properties.TempSatCur; SourceSatCur = Parameters.ParallelMultiplier * Properties.TempSatCur; } else { DrainSatCur = Properties.TempSatCurDensity * Parameters.ParallelMultiplier * Parameters.DrainArea; SourceSatCur = Properties.TempSatCurDensity * Parameters.ParallelMultiplier * Parameters.SourceArea; } var Beta = Properties.TempTransconductance * Parameters.ParallelMultiplier * Parameters.Width / Properties.EffectiveLength; // Get the current voltages Initialize(out double vgs, out var vds, out var vbs, out var check); var vbd = vbs - vds; var vgd = vgs - vds; /* * Bulk-source and bulk-drain diodes * here we just evaluate the ideal diode current and the * corresponding derivative (conductance). */ if (vbs <= -3 * vt) { con.Bs.G = _config.Gmin; con.Bs.C = con.Bs.G * vbs - SourceSatCur; } else { var evbs = Math.Exp(Math.Min(MaximumExponentArgument, vbs / vt)); con.Bs.G = SourceSatCur * evbs / vt + _config.Gmin; con.Bs.C = SourceSatCur * (evbs - 1) + _config.Gmin * vbs; } if (vbd <= -3 * vt) { con.Bd.G = _config.Gmin; con.Bd.C = con.Bd.G * vbd - DrainSatCur; } else { var evbd = Math.Exp(Math.Min(MaximumExponentArgument, vbd / vt)); con.Bd.G = DrainSatCur * evbd / vt + _config.Gmin; con.Bd.C = DrainSatCur * (evbd - 1) + _config.Gmin * vbd; } // Now to determine whether the user was able to correctly identify the source and drain of his device if (vds >= 0) { Mode = 1; } else { Mode = -1; } { /* * this block of code evaluates the drain current and its * derivatives using the shichman-hodges model and the * charges associated with the gate, channel and bulk for * mosfets * */ /* the following 4 variables are local to this code block until * it is obvious that they can be made global */ double arg; double betap; double sarg; double vgst; if ((Mode > 0 ? vbs : vbd) <= 0) { sarg = Math.Sqrt(Properties.TempPhi - (Mode > 0 ? vbs : vbd)); } else { sarg = Math.Sqrt(Properties.TempPhi); sarg -= (Mode > 0 ? vbs : vbd) / (sarg + sarg); sarg = Math.Max(0, sarg); } var von = (Properties.TempVbi * ModelParameters.MosfetType) + ModelParameters.Gamma * sarg; vgst = (Mode > 0 ? vgs : vgd) - von; var vdsat = Math.Max(vgst, 0); if (sarg <= 0) { arg = 0; } else { arg = ModelParameters.Gamma / (sarg + sarg); } if (vgst <= 0) { // Cutoff region con.Ds.C = 0; Gm = 0; con.Ds.G = 0; Gmbs = 0; } else { // Saturation region betap = Beta * (1 + ModelParameters.Lambda * (vds * Mode)); if (vgst <= (vds * Mode)) { con.Ds.C = betap * vgst * vgst * .5; Gm = betap * vgst; con.Ds.G = ModelParameters.Lambda * Beta * vgst * vgst * .5; Gmbs = Gm * arg; } else { // Linear region con.Ds.C = betap * (vds * Mode) * (vgst - .5 * (vds * Mode)); Gm = betap * (vds * Mode); con.Ds.G = betap * (vgst - (vds * Mode)) + ModelParameters.Lambda * Beta * (vds * Mode) * (vgst - .5 * (vds * Mode)); Gmbs = Gm * arg; } } // now deal with n vs p polarity Von = ModelParameters.MosfetType * von; Vdsat = ModelParameters.MosfetType * vdsat; } // COMPUTE EQUIVALENT DRAIN CURRENT SOURCE Gds = con.Ds.G; Id = Mode * con.Ds.C - con.Bd.C; Vbs = vbs; Vbd = vbd; Vgs = vgs; Vds = vds; // Update with time-dependent calculations UpdateContributions?.Invoke(this, _args); // Check convergence if (!Parameters.Off && _iteration.Mode != IterationModes.Fix) { if (check) { _iteration.IsConvergent = false; } } Gbs = con.Bs.G; Ibs = con.Bs.C; Gbd = con.Bd.G; Ibd = con.Bd.C; // Right hand side vector contributions con.Bs.C = ModelParameters.MosfetType * (con.Bs.C - con.Bs.G * vbs); con.Bd.C = ModelParameters.MosfetType * (con.Bd.C - con.Bd.G * vbd); double xnrm, xrev; if (Mode >= 0) { xnrm = 1; xrev = 0; con.Ds.C = ModelParameters.MosfetType * (con.Ds.C - con.Ds.G * vds - Gm * vgs - Gmbs * vbs); } else { xnrm = 0; xrev = 1; con.Ds.C = -ModelParameters.MosfetType * (con.Ds.C - con.Ds.G * (-vds) - Gm * vgd - Gmbs * vbd); } _elements.Add( // Y-matrix Properties.DrainConductance, con.Gd.G + con.Gs.G + con.Gb.G, Properties.SourceConductance, con.Bd.G + con.Bs.G + con.Gb.G, Properties.DrainConductance + con.Ds.G + con.Bd.G + xrev * (Gm + Gmbs) + con.Gd.G, Properties.SourceConductance + con.Ds.G + con.Bs.G + xnrm * (Gm + Gmbs) + con.Gs.G, -Properties.DrainConductance, -con.Gb.G, -con.Gd.G, -con.Gs.G, -Properties.SourceConductance, -con.Gb.G, -con.Bd.G, -con.Bs.G, -Properties.DrainConductance, (xnrm - xrev) * Gm - con.Gd.G, -con.Bd.G + (xnrm - xrev) * Gmbs, -con.Ds.G - xnrm * (Gm + Gmbs), -(xnrm - xrev) * Gm - con.Gs.G, -Properties.SourceConductance, -con.Bs.G - (xnrm - xrev) * Gmbs, -con.Ds.G - xrev * (Gm + Gmbs), // Right hand side vector -ModelParameters.MosfetType * (con.Gs.C + con.Gb.C + con.Gd.C), -(con.Bs.C + con.Bd.C - ModelParameters.MosfetType * con.Gb.C), con.Bd.C - con.Ds.C + ModelParameters.MosfetType * con.Gd.C, con.Ds.C + con.Bs.C + ModelParameters.MosfetType * con.Gs.C ); }
/// <inheritdoc/> void IBiasingBehavior.Load() { var con = _contributions; con.Reset(); /* first, we compute a few useful values - these could be * pre-computed, but for historical reasons are still done * here. They may be moved at the expense of instance * size */ double DrainSatCur, SourceSatCur; if ((Properties.TempSatCurDensity == 0) || (Parameters.DrainArea == 0) || (Parameters.SourceArea == 0)) { DrainSatCur = Parameters.ParallelMultiplier * Properties.TempSatCur; SourceSatCur = Parameters.ParallelMultiplier * Properties.TempSatCur; } else { DrainSatCur = Parameters.ParallelMultiplier * Properties.TempSatCurDensity * Parameters.DrainArea; SourceSatCur = Parameters.ParallelMultiplier * Properties.TempSatCurDensity * Parameters.SourceArea; } var Beta = Properties.TempTransconductance * Parameters.ParallelMultiplier * Properties.EffectiveWidth / Properties.EffectiveLength; // Get the current voltages Initialize(out var vgs, out var vds, out var vbs, out var check); var vbd = vbs - vds; var vgd = vgs - vds; if (ModelParameters.Version == ModelParameters.Versions.NgSpice) { if (vbs <= -3 * Properties.TempVt) { var arg = 3 * Properties.TempVt / (vbs * Math.E); arg = arg * arg * arg; con.Bs.C = -SourceSatCur * (1 + arg) + _config.Gmin * vbs; con.Bs.G = SourceSatCur * 3 * arg / vbs + _config.Gmin; } else { var evbs = Math.Exp(Math.Min(MaximumExponentArgument, vbs / Properties.TempVt)); con.Bs.G = SourceSatCur * evbs / Properties.TempVt + _config.Gmin; con.Bs.C = SourceSatCur * (evbs - 1) + _config.Gmin * vbs; } if (vbd <= -3 * Properties.TempVt) { var arg = 3 * Properties.TempVt / (vbd * Math.E); arg = arg * arg * arg; con.Bd.C = -DrainSatCur * (1 + arg) + _config.Gmin * vbd; con.Bd.G = DrainSatCur * 3 * arg / vbd + _config.Gmin; } else { var evbd = Math.Exp(Math.Min(MaximumExponentArgument, vbd / Properties.TempVt)); con.Bd.G = DrainSatCur * evbd / Properties.TempVt + _config.Gmin; con.Bd.C = DrainSatCur * (evbd - 1) + _config.Gmin * vbd; } } else { if (vbs <= 0) { con.Bs.G = SourceSatCur / Properties.TempVt; con.Bs.C = con.Bs.G * vbs; con.Bs.G += _config.Gmin; } else { var evbs = Math.Exp(Math.Min(MaximumExponentArgument, vbs / Properties.TempVt)); con.Bs.G = SourceSatCur * evbs / Properties.TempVt + _config.Gmin; con.Bs.C = SourceSatCur * (evbs - 1); } if (vbd <= 0) { con.Bd.G = DrainSatCur / Properties.TempVt; con.Bd.C = con.Bd.G * vbd; con.Bd.G += _config.Gmin; } else { var evbd = Math.Exp(Math.Min(MaximumExponentArgument, vbd / Properties.TempVt)); con.Bd.G = DrainSatCur * evbd / Properties.TempVt + _config.Gmin; con.Bd.C = DrainSatCur * (evbd - 1); } } // Now to determine whether the user was able to correctly identify the source and drain of his device. if (vds >= 0) { Mode = 1; } else { Mode = -1; } { /* * subroutine moseq3(vds,vbs,vgs,gm,gds,gmbs, * qg,qc,qb,cggb,cgdb,cgsb,cbgb,cbdb,cbsb) */ /* this routine evaluates the drain current, its * derivatives and the Constants.Charges associated with the * gate, channel and bulk for mosfets based on * semi-empirical equations */ double coeff0 = 0.0631353e0; double coeff1 = 0.8013292e0; double coeff2 = -0.01110777e0; double oneoverxl; /* 1/effective length */ double eta; /* eta from model after length factor */ double phibs; /* phi - vbs */ double sqphbs; /* square root of phibs */ double dsqdvb; /* */ double sqphis; /* square root of phi */ double sqphs3; /* square root of phi cubed */ double wps; double oneoverxj; /* 1/junction depth */ double xjonxl; /* junction depth/effective length */ double djonxj; double wponxj; double arga; double argb; double argc; double dwpdvb; double dadvb; double dbdvb; double gammas; double fbodys; double fbody; double onfbdy; double qbonco; double vbix; double wconxj; double dfsdvb; double dfbdvb; double dqbdvb; double vth; double dvtdvb; double csonco; double cdonco; double dxndvb = 0.0; double dvodvb = 0.0; double dvodvd = 0.0; double vgsx; double dvtdvd; double onfg; double fgate; double us; double dfgdvg; double dfgdvd; double dfgdvb; double dvsdvg; double dvsdvb; double dvsdvd; double xn = 0.0; double vdsc; double onvdsc = 0.0; double dvsdga; double vdsx; double dcodvb; double cdnorm; double cdo; double cd1; double fdrain = 0.0; double fd2; double dfddvg = 0.0; double dfddvb = 0.0; double dfddvd = 0.0; double gdsat; double cdsat; double gdoncd; double gdonfd; double gdonfg; double dgdvg; double dgdvd; double dgdvb; double emax; double emongd; double demdvg; double demdvd; double demdvb; double delxl; double dldvd; double dldem; double ddldvg; double ddldvd; double ddldvb; double dlonxl; double xlfact; double diddl; double gds0 = 0.0; double emoncd; double ondvt; double onxn; double wfact; double gms; double gmw; double fshort; /* * reference con.Ds.C equations to source and * charge equations to bulk */ var vdsat = 0.0; oneoverxl = 1.0 / Properties.EffectiveLength; eta = ModelParameters.Eta * 8.15e-22 / (ModelTemperature.Properties.OxideCapFactor * Properties.EffectiveLength * Properties.EffectiveLength * Properties.EffectiveLength); // Square root term if ((Mode > 0 ? vbs : vbd) <= 0.0) { phibs = Properties.TempPhi - (Mode > 0 ? vbs : vbd); sqphbs = Math.Sqrt(phibs); dsqdvb = -0.5 / sqphbs; } else { sqphis = Math.Sqrt(Properties.TempPhi); sqphs3 = Properties.TempPhi * sqphis; sqphbs = sqphis / (1.0 + (Mode > 0 ? vbs : vbd) / (Properties.TempPhi + Properties.TempPhi)); phibs = sqphbs * sqphbs; dsqdvb = -phibs / (sqphs3 + sqphs3); } // Short channel effect factor if ((ModelParameters.JunctionDepth != 0.0) && (ModelTemperature.Properties.CoeffDepLayWidth != 0.0)) { wps = ModelTemperature.Properties.CoeffDepLayWidth * sqphbs; oneoverxj = 1.0 / ModelParameters.JunctionDepth; xjonxl = ModelParameters.JunctionDepth * oneoverxl; djonxj = ModelParameters.LateralDiffusion * oneoverxj; wponxj = wps * oneoverxj; wconxj = coeff0 + coeff1 * wponxj + coeff2 * wponxj * wponxj; arga = wconxj + djonxj; argc = wponxj / (1.0 + wponxj); argb = Math.Sqrt(1.0 - argc * argc); fshort = 1.0 - xjonxl * (arga * argb - djonxj); dwpdvb = ModelTemperature.Properties.CoeffDepLayWidth * dsqdvb; dadvb = (coeff1 + coeff2 * (wponxj + wponxj)) * dwpdvb * oneoverxj; dbdvb = -argc * argc * (1.0 - argc) * dwpdvb / (argb * wps); dfsdvb = -xjonxl * (dadvb * argb + arga * dbdvb); } else { fshort = 1.0; dfsdvb = 0.0; } // Body effect gammas = ModelParameters.Gamma * fshort; fbodys = 0.5 * gammas / (sqphbs + sqphbs); fbody = fbodys + ModelTemperature.Properties.NarrowFactor / Properties.EffectiveWidth; onfbdy = 1.0 / (1.0 + fbody); dfbdvb = -fbodys * dsqdvb / sqphbs + fbodys * dfsdvb / fshort; qbonco = gammas * sqphbs + ModelTemperature.Properties.NarrowFactor * phibs / Properties.EffectiveWidth; dqbdvb = gammas * dsqdvb + ModelParameters.Gamma * dfsdvb * sqphbs - ModelTemperature.Properties.NarrowFactor / Properties.EffectiveWidth; // Static feedback effect vbix = Properties.TempVbi * ModelParameters.MosfetType - eta * (Mode * vds); // Threshold voltage vth = vbix + qbonco; dvtdvd = -eta; dvtdvb = dqbdvb; // Joint weak inversion and strong inversion var von = vth; if (ModelParameters.FastSurfaceStateDensity != 0.0) { csonco = Constants.Charge * ModelParameters.FastSurfaceStateDensity * 1e4 /*(cm**2/m**2)*/ * Properties.EffectiveLength * Properties.EffectiveWidth * Parameters.ParallelMultiplier / Properties.OxideCap; cdonco = qbonco / (phibs + phibs); xn = 1.0 + csonco + cdonco; von = vth + Properties.TempVt * xn; dxndvb = dqbdvb / (phibs + phibs) - qbonco * dsqdvb / (phibs * sqphbs); dvodvd = dvtdvd; dvodvb = dvtdvb + Properties.TempVt * dxndvb; } else { // Cutoff region if ((Mode > 0 ? vgs : vgd) <= von) { con.Ds.C = 0.0; Gm = 0.0; con.Ds.G = 0.0; Gmbs = 0.0; goto innerline1000; } } // Device is on vgsx = Math.Max(Mode > 0 ? vgs : vgd, von); // Mobility modulation by gate voltage onfg = 1.0 + ModelParameters.Theta * (vgsx - vth); fgate = 1.0 / onfg; us = Properties.TempSurfaceMobility * 1e-4 /*(m**2/cm**2)*/ * fgate; dfgdvg = -ModelParameters.Theta * fgate * fgate; dfgdvd = -dfgdvg * dvtdvd; dfgdvb = -dfgdvg * dvtdvb; // Saturation voltage vdsat = (vgsx - vth) * onfbdy; if (ModelParameters.MaxDriftVelocity <= 0.0) { dvsdvg = onfbdy; dvsdvd = -dvsdvg * dvtdvd; dvsdvb = -dvsdvg * dvtdvb - vdsat * dfbdvb * onfbdy; } else { vdsc = Properties.EffectiveLength * ModelParameters.MaxDriftVelocity / us; onvdsc = 1.0 / vdsc; arga = (vgsx - vth) * onfbdy; argb = Math.Sqrt(arga * arga + vdsc * vdsc); vdsat = arga + vdsc - argb; dvsdga = (1.0 - arga / argb) * onfbdy; dvsdvg = dvsdga - (1.0 - vdsc / argb) * vdsc * dfgdvg * onfg; dvsdvd = -dvsdvg * dvtdvd; dvsdvb = -dvsdvg * dvtdvb - arga * dvsdga * dfbdvb; } // Current factors in linear region vdsx = Math.Min(Mode * vds, vdsat); if (vdsx == 0.0) { goto line900; } cdo = vgsx - vth - 0.5 * (1.0 + fbody) * vdsx; dcodvb = -dvtdvb - 0.5 * dfbdvb * vdsx; // Normalized drain current cdnorm = cdo * vdsx; Gm = vdsx; if (ModelParameters.Version == ModelParameters.Versions.NgSpice) { if ((Mode * vds) > vdsat) { con.Ds.G = -dvtdvd * vdsx; } else { con.Ds.G = vgsx - vth - (1.0 + fbody + dvtdvd) * vdsx; } } else { con.Ds.G = vgsx - vth - (1.0 + fbody + dvtdvd) * vdsx; } Gmbs = dcodvb * vdsx; // Drain current without velocity saturation effect cd1 = Beta * cdnorm; Beta *= fgate; con.Ds.C = Beta * cdnorm; Gm = Beta * Gm + dfgdvg * cd1; con.Ds.G = Beta * con.Ds.G + dfgdvd * cd1; Gmbs *= Beta; if (ModelParameters.Version == ModelParameters.Versions.NgSpice) { Gmbs += dfgdvb * cd1; } // Celocity saturation factor if (ModelParameters.MaxDriftVelocity > 0.0) { fdrain = 1.0 / (1.0 + vdsx * onvdsc); fd2 = fdrain * fdrain; arga = fd2 * vdsx * onvdsc * onfg; dfddvg = -dfgdvg * arga; if (ModelParameters.Version == ModelParameters.Versions.NgSpice) { if ((Mode * vds) > vdsat) { dfddvd = -dfgdvd * arga; } else { dfddvd = -dfgdvd * arga - fd2 * onvdsc; } } else { dfddvd = -dfgdvd * arga - fd2 * onvdsc; } dfddvb = -dfgdvb * arga; // Drain current Gm = fdrain * Gm + dfddvg * con.Ds.C; con.Ds.G = fdrain * con.Ds.G + dfddvd * con.Ds.C; Gmbs = fdrain * Gmbs + dfddvb * con.Ds.C; con.Ds.C = fdrain * con.Ds.C; Beta *= fdrain; } // Channel length modulation if ((Mode * vds) <= vdsat) { if (ModelParameters.Version == ModelParameters.Versions.NgSpice) { if ((ModelParameters.MaxDriftVelocity > 0.0) || (ModelTemperature.Properties.Alpha == 0.0) || ModelParameters.BadMos) { goto line700; } else { arga = Mode * vds / vdsat; delxl = Math.Sqrt(ModelParameters.Kappa * ModelTemperature.Properties.Alpha * vdsat / 8); dldvd = 4 * delxl * arga * arga * arga / vdsat; arga *= arga; arga *= arga; delxl *= arga; ddldvg = 0.0; ddldvd = -dldvd; ddldvb = 0.0; goto line520; } } else { goto line700; } } if (ModelParameters.MaxDriftVelocity <= 0.0) { goto line510; } if (ModelTemperature.Properties.Alpha == 0.0) { goto line700; } cdsat = con.Ds.C; gdsat = cdsat * (1.0 - fdrain) * onvdsc; gdsat = Math.Max(1.0e-12, gdsat); gdoncd = gdsat / cdsat; gdonfd = gdsat / (1.0 - fdrain); gdonfg = gdsat * onfg; dgdvg = gdoncd * Gm - gdonfd * dfddvg + gdonfg * dfgdvg; dgdvd = gdoncd * con.Ds.G - gdonfd * dfddvd + gdonfg * dfgdvd; dgdvb = gdoncd * Gmbs - gdonfd * dfddvb + gdonfg * dfgdvb; if (ModelParameters.BadMos) { emax = cdsat * oneoverxl / gdsat; } else { emax = ModelParameters.Kappa * cdsat * oneoverxl / gdsat; } emoncd = emax / cdsat; emongd = emax / gdsat; demdvg = emoncd * Gm - emongd * dgdvg; demdvd = emoncd * con.Ds.G - emongd * dgdvd; demdvb = emoncd * Gmbs - emongd * dgdvb; arga = 0.5 * emax * ModelTemperature.Properties.Alpha; argc = ModelParameters.Kappa * ModelTemperature.Properties.Alpha; argb = Math.Sqrt(arga * arga + argc * ((Mode * vds) - vdsat)); delxl = argb - arga; if (ModelParameters.Version == ModelParameters.Versions.NgSpice) { if (argb != 0.0) { dldvd = argc / (argb + argb); dldem = 0.5 * (arga / argb - 1.0) * ModelTemperature.Properties.Alpha; } else { dldvd = 0.0; dldem = 0.0; } } else { dldvd = argc / (argb + argb); dldem = 0.5 * (arga / argb - 1.0) * ModelTemperature.Properties.Alpha; } ddldvg = dldem * demdvg; ddldvd = dldem * demdvd - dldvd; ddldvb = dldem * demdvb; goto line520; line510: if (ModelParameters.Version == ModelParameters.Versions.NgSpice) { if (ModelParameters.BadMos) { delxl = Math.Sqrt(ModelParameters.Kappa * ((Mode * vds) - vdsat) * ModelTemperature.Properties.Alpha); dldvd = 0.5 * delxl / ((Mode * vds) - vdsat); } else { delxl = Math.Sqrt(ModelParameters.Kappa * ModelTemperature.Properties.Alpha * ((Mode * vds) - vdsat + (vdsat / 8))); dldvd = 0.5 * delxl / ((Mode * vds) - vdsat + (vdsat / 8)); } } else { delxl = Math.Sqrt(ModelParameters.Kappa * ((Mode * vds) - vdsat) * ModelTemperature.Properties.Alpha); dldvd = 0.5 * delxl / ((Mode * vds) - vdsat); } ddldvg = 0.0; ddldvd = -dldvd; ddldvb = 0.0; // Punch through approximation line520: if (delxl > (0.5 * Properties.EffectiveLength)) { delxl = Properties.EffectiveLength - (Properties.EffectiveLength * Properties.EffectiveLength / (4.0 * delxl)); arga = 4.0 * (Properties.EffectiveLength - delxl) * (Properties.EffectiveLength - delxl) / (Properties.EffectiveLength * Properties.EffectiveLength); ddldvg *= arga; ddldvd *= arga; ddldvb *= arga; dldvd *= arga; } // Saturation region dlonxl = delxl * oneoverxl; xlfact = 1.0 / (1.0 - dlonxl); if (ModelParameters.Version == ModelParameters.Versions.NgSpice) { con.Ds.C *= xlfact; diddl = con.Ds.C / (Properties.EffectiveLength - delxl); Gm = Gm * xlfact + diddl * ddldvg; Gmbs = Gmbs * xlfact + diddl * ddldvb; gds0 = diddl * ddldvd; Gm += gds0 * dvsdvg; Gmbs += gds0 * dvsdvb; con.Ds.G = con.Ds.G * xlfact + diddl * dldvd + gds0 * dvsdvd; /* con.Ds.G = (con.Ds.G*xlfact)+gds0*dvsdvd- * (cd1*ddldvd/(EffectiveLength*(1-2*dlonxl+dlonxl*dlonxl)));*/ } else { con.Ds.C *= xlfact; diddl = con.Ds.C / (Properties.EffectiveLength - delxl); Gm = Gm * xlfact + diddl * ddldvg; gds0 = con.Ds.G * xlfact + diddl * ddldvd; Gmbs = Gmbs * xlfact + diddl * ddldvb; Gm += gds0 * dvsdvg; Gmbs += gds0 * dvsdvb; con.Ds.G = gds0 * dvsdvd + diddl * dldvd; } // Finish strong inversion case line700: if ((Mode > 0 ? vgs : vgd) < von) { // Weak inversion onxn = 1.0 / xn; ondvt = onxn / Properties.TempVt; wfact = Math.Exp(((Mode > 0 ? vgs : vgd) - von) * ondvt); con.Ds.C *= wfact; gms = Gm * wfact; gmw = con.Ds.C * ondvt; Gm = gmw; if ((Mode * vds) > vdsat) { Gm += gds0 * dvsdvg * wfact; } con.Ds.G = con.Ds.G * wfact + (gms - gmw) * dvodvd; Gmbs = Gmbs * wfact + (gms - gmw) * dvodvb - gmw * ((Mode > 0 ? vgs : vgd) - von) * onxn * dxndvb; } // Charge computation goto innerline1000; // Special case of vds = 0.0d0 line900: Beta *= fgate; con.Ds.C = 0.0; Gm = 0.0; con.Ds.G = Beta * (vgsx - vth); Gmbs = 0.0; if ((ModelParameters.FastSurfaceStateDensity != 0.0) && ((Mode > 0 ? vgs : vgd) < von)) { con.Ds.G *= Math.Exp(((Mode > 0 ? vgs : vgd) - von) / (Properties.TempVt * xn)); } innerline1000 :; // Done Von = ModelParameters.MosfetType * von; Vdsat = ModelParameters.MosfetType * vdsat; } // COMPUTE EQUIVALENT DRAIN CURRENT SOURCE Gds = con.Ds.G; Id = Mode * con.Ds.C - con.Bd.C; Vbs = vbs; Vbd = vbd; Vgs = vgs; Vds = vds; // Update with time-dependent calculations UpdateContributions?.Invoke(this, _args); // Check convergence if (!Parameters.Off || _iteration.Mode != IterationModes.Fix) { if (check) { _iteration.IsConvergent = false; } } // Save things away for next time Gbd = con.Bd.G; Ibd = con.Bd.C; Gbs = con.Bs.G; Ibs = con.Bs.C; // Right hand side contributions double xnrm, xrev; con.Bs.C = ModelParameters.MosfetType * (con.Bs.C - (con.Bs.G - _config.Gmin) * vbs); con.Bd.C = ModelParameters.MosfetType * (con.Bd.C - (con.Bd.G - _config.Gmin) * vbd); if (Mode >= 0) { xnrm = 1; xrev = 0; con.Ds.C = ModelParameters.MosfetType * (con.Ds.C - con.Ds.G * vds - Gm * vgs - Gmbs * vbs); } else { xnrm = 0; xrev = 1; con.Ds.C = -ModelParameters.MosfetType * (con.Ds.C - con.Ds.G * (-vds) - Gm * vgd - Gmbs * vbd); } _elements.Add( // Y-matrix Properties.DrainConductance, con.Gd.G + con.Gs.G + con.Gb.G, Properties.SourceConductance, con.Bd.G + con.Bs.G + con.Gb.G, Properties.DrainConductance + con.Ds.G + con.Bd.G + xrev * (Gm + Gmbs) + con.Gd.G, Properties.SourceConductance + con.Ds.G + con.Bs.G + xnrm * (Gm + Gmbs) + con.Gs.G, -Properties.DrainConductance, -con.Gb.G, -con.Gd.G, -con.Gs.G, -Properties.SourceConductance, -con.Gb.G, -con.Bd.G, -con.Bs.G, -Properties.DrainConductance, (xnrm - xrev) * Gm - con.Gd.G, -con.Bd.G + (xnrm - xrev) * Gmbs, -con.Ds.G - xnrm * (Gm + Gmbs), -(xnrm - xrev) * Gm - con.Gs.G, -Properties.SourceConductance, -con.Bs.G - (xnrm - xrev) * Gmbs, -con.Ds.G - xrev * (Gm + Gmbs), // Right hand side vector -ModelParameters.MosfetType * (con.Gs.C + con.Gb.C + con.Gd.C), -(con.Bs.C + con.Bd.C - ModelParameters.MosfetType * con.Gb.C), con.Bd.C - con.Ds.C + ModelParameters.MosfetType * con.Gd.C, con.Ds.C + con.Bs.C + ModelParameters.MosfetType * con.Gs.C ); }