Ejemplo n.º 1
0
        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());
        }
Ejemplo n.º 2
0
        /// <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
                );
        }
Ejemplo n.º 3
0
        /// <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
                );
        }
Ejemplo n.º 4
0
        /// <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
                );
        }