/**********
Copyright 1990 Regents of the University of California.  All rights reserved.
Author: 1985 Thomas L. Quarles
Modified: 2000 AlansFixes
Modified by Paolo Nenzi 2003, Dietmar Warning 2012 and Arpad Buermen 2025
**********/

#include "ngspice/ngspice.h"
#include "ngspice/devdefs.h"
#include "ngspice/cktdefs.h"
#include "diodefs.h"
#include "ngspice/const.h"
#include "ngspice/trandefs.h"
#include "ngspice/sperror.h"
#include "ngspice/suffix.h"

int
DIOload(GENmodel *inModel, CKTcircuit *ckt)
        /* actually load the current resistance value into the
         * sparse matrix previously provided
         */
{
    DIOmodel *model = (DIOmodel*)inModel;
    DIOinstance *here;
    double arg;
    double argsw;
    double capd, capdsw=0.0;
    double cd, cdb, cdsw, cdb_dT, cdsw_dT;
    double cdeq;
    double cdhat, cdhatsw=0.0;
    double ceq;
    double csat;    /* area-scaled saturation current */
    double csatsw;  /* perimeter-scaled saturation current */
    double czero;
    double czof2;
    double argSW;
    double czeroSW;
    double czof2SW;
    double sargSW;
    double sqrt_ikx;

    double delvd, delvdsw=0.0; /* change in diode voltage temporary */
    double evd;
    double evrev;
    double gd, gdb, gdsw, gen_fac, gen_fac_vd;
    double t1, evd_rec, cdb_rec, gdb_rec, cdb_rec_dT;
    double geq;
    double gspr;    /* area-scaled conductance */
    double gsprsw;  /* perim-scaled conductance */
    double sarg;
#ifndef NOBYPASS
    double tol;     /* temporary for tolerence calculations */
#endif
    double vd, vdsw=0.0; /* current diode voltage */
    double vdtemp;
    double vt;      /* K t / Q */
    double vte, vtesw, vtetun, vtebrk, vterec;
    int Check_dio=0, Check_dio_sw=0, Check_th;
    int error;
    int SenCond=0;    /* sensitivity condition */
    double diffcharge, deplcharge, deplchargeSW, diffcap, deplcap, deplcapSW;

    double deldelTemp, delTemp, Temp;
    double ceqqth=0.0, Ith=0.0, gcTt=0.0, vrs=0.0, vrssw=0.0;
    double dIdio_dT, dIth_dVdio=0.0, dIrs_dT=0.0, dIth_dVrs=0.0, dIth_dT=0.0;
    double dIdioSw_dT=0.0, dIth_dVdioSw=0.0, dIth_dVrssw=0.0, dIrssw_dT=0.0;
    double argsw_dT, csat_dT, csatsw_dT;
    /* rev-rec */
    double cdres, gdres;
    double vqp;
    double capsr, gqcsr, cqcsr;

    /*  loop through all the diode models */
    for( ; model != NULL; model = DIOnextModel(model)) {

        /* loop through all the instances of the model */
        for (here = DIOinstances(model); here != NULL ;
                here=DIOnextInstance(here)) {

            int selfheat = ((here->DIOtempNode > 0) && (here->DIOthermal) && (model->DIOrth0Given));
            int revrec = ((here->DIOqpNode > 0) && (model->DIOsoftRevRecParam!=0) && (here->DIOtTransitTime!=0));

            /*
             *     this routine loads diodes for dc and transient analyses.
             */

            if (selfheat)
                Check_th = 1;
            else
                Check_th = 0;

            if(ckt->CKTsenInfo){
                if((ckt->CKTsenInfo->SENstatus == PERTURBATION)
                        && (here->DIOsenPertFlag == OFF))continue;
                SenCond = here->DIOsenPertFlag;

#ifdef SENSDEBUG
                printf("DIOload \n");
#endif /* SENSDEBUG */

            }
            cdsw = 0.0;
            cdsw_dT = 0.0;
            gdsw = 0.0;
            delTemp = 0.0;
            vt = CONSTKoverQ * here->DIOtemp;
            vte = model->DIOemissionCoeff * vt;
            vtesw = model->DIOswEmissionCoeff * vt;
            vtebrk = model->DIObrkdEmissionCoeff * vt;
            vterec = model->DIOrecEmissionCoeff*vt;
            gspr = here->DIOtConductance;
            gsprsw = here->DIOtConductanceSW;
            /*
             *   initialization
             */

            if(SenCond){

#ifdef SENSDEBUG
                printf("DIOsenPertFlag = ON \n");
#endif /* SENSDEBUG */

                if((ckt->CKTsenInfo->SENmode == TRANSEN)&&
                        (ckt->CKTmode & MODEINITTRAN)) {
                    vd = *(ckt->CKTstate1 + here->DIOvoltage);
                    if (model->DIOresistSWGiven) vdsw = *(ckt->CKTstate1 + here->DIOvoltageSW);
                    delTemp = *(ckt->CKTstate1 + here->DIOdeltemp);
                    vqp = *(ckt->CKTstate1 + here->DIOqp);
                } else{
                    vd = *(ckt->CKTstate0 + here->DIOvoltage);
                    if (model->DIOresistSWGiven) vdsw = *(ckt->CKTstate0 + here->DIOvoltageSW);
                    delTemp = *(ckt->CKTstate0 + here->DIOdeltemp);
                    vqp = *(ckt->CKTstate0 + here->DIOqp);
                }

#ifdef SENSDEBUG
                printf("vd = %.7e \n",vd);
#endif /* SENSDEBUG */
                goto next1;
            }

            Check_dio=1; Check_dio_sw=1;
            if(ckt->CKTmode & MODEINITSMSIG) {
                vd= *(ckt->CKTstate0 + here->DIOvoltage);
                if (model->DIOresistSWGiven) vdsw = *(ckt->CKTstate0 + here->DIOvoltageSW);
                delTemp = *(ckt->CKTstate0 + here->DIOdeltemp);
                vqp= *(ckt->CKTstate0 + here->DIOqp);
            } else if (ckt->CKTmode & MODEINITTRAN) {
                vd= *(ckt->CKTstate1 + here->DIOvoltage);
                if (model->DIOresistSWGiven) vdsw = *(ckt->CKTstate1 + here->DIOvoltageSW);
                delTemp = *(ckt->CKTstate1 + here->DIOdeltemp);
                vqp= *(ckt->CKTstate1 + here->DIOqp);
            } else if ( (ckt->CKTmode & MODEINITJCT) &&
                    (ckt->CKTmode & MODETRANOP) && (ckt->CKTmode & MODEUIC) ) {
                vd=here->DIOinitCond;
                if (model->DIOresistSWGiven) vdsw = here->DIOinitCond;
                vqp=0;
            } else if ( (ckt->CKTmode & MODEINITJCT) && here->DIOoff) {
                vd=vdsw=0;
                delTemp = 0.0;
                vqp=0;
            } else if ( ckt->CKTmode & MODEINITJCT) {
                vd=here->DIOtVcrit;
                vdsw=here->DIOtVcritSW;
                delTemp = 0.0;
                vqp=0;
            } else if ( ckt->CKTmode & MODEINITFIX && here->DIOoff) {
                vd=vdsw=0;
                delTemp = 0.0;
                vqp=0;
            } else {
#ifndef PREDICTOR
                if (ckt->CKTmode & MODEINITPRED) {
                    *(ckt->CKTstate0 + here->DIOvoltage) =
                            *(ckt->CKTstate1 + here->DIOvoltage);
                    vd = DEVpred(ckt,here->DIOvoltage);
                    *(ckt->CKTstate0 + here->DIOcurrent) =
                            *(ckt->CKTstate1 + here->DIOcurrent);
                    *(ckt->CKTstate0 + here->DIOconduct) =
                            *(ckt->CKTstate1 + here->DIOconduct);
                    *(ckt->CKTstate0 + here->DIOdeltemp) =
                            *(ckt->CKTstate1 + here->DIOdeltemp);
                    delTemp = DEVpred(ckt,here->DIOdeltemp);
                    *(ckt->CKTstate0 + here->DIOdIdio_dT) =
                            *(ckt->CKTstate1 + here->DIOdIdio_dT);
                    *(ckt->CKTstate0+here->DIOqth) =
                            *(ckt->CKTstate1+here->DIOqth);
                    if (model->DIOresistSWGiven) {
                        vdsw = DEVpred(ckt,here->DIOvoltageSW);
                        *(ckt->CKTstate0 + here->DIOdIdioSW_dT) =
                                *(ckt->CKTstate1 + here->DIOdIdioSW_dT);
                    }
                    vqp = DEVpred(ckt,here->DIOqp);
                    *(ckt->CKTstate0 + here->DIOresCurrent) =
                            *(ckt->CKTstate1 + here->DIOresCurrent);
                    *(ckt->CKTstate0 + here->DIOresConduct) =
                            *(ckt->CKTstate1 + here->DIOresConduct);
                    *(ckt->CKTstate0 + here->DIOcqcsr) =
                            *(ckt->CKTstate1 + here->DIOcqcsr);
                    *(ckt->CKTstate0 + here->DIOgqcsr) =
                            *(ckt->CKTstate1 + here->DIOgqcsr);
                } else {
#endif /* PREDICTOR */
                    vd = *(ckt->CKTrhsOld+here->DIOposPrimeNode)-
                            *(ckt->CKTrhsOld + here->DIOnegNode);
                    if (model->DIOresistSWGiven) vdsw = *(ckt->CKTrhsOld+here->DIOposSwPrimeNode)-
                                                             *(ckt->CKTrhsOld + here->DIOnegNode);

                    if (selfheat)
                        delTemp = *(ckt->CKTrhsOld + here->DIOtempNode);
                    else
                        delTemp = 0.0;
                    *(ckt->CKTstate0+here->DIOqth) = model->DIOcth0 * delTemp;
                    if((ckt->CKTmode & MODEINITTRAN)) {
                        *(ckt->CKTstate1+here->DIOqth) =
                            *(ckt->CKTstate0+here->DIOqth);
                    }
                    vqp = *(ckt->CKTrhsOld+here->DIOqpNode);
#ifndef PREDICTOR
                }
#endif /* PREDICTOR */
                delvd=vd- *(ckt->CKTstate0 + here->DIOvoltage);
                deldelTemp = delTemp - *(ckt->CKTstate0 + here->DIOdeltemp);
                cdhat= *(ckt->CKTstate0 + here->DIOcurrent) +
                        *(ckt->CKTstate0 + here->DIOconduct) * delvd +
                        *(ckt->CKTstate0 + here->DIOdIdio_dT) * deldelTemp;
                if (model->DIOresistSWGiven) {
                    delvdsw=vdsw - *(ckt->CKTstate0 + here->DIOvoltageSW);
                    cdhatsw = *(ckt->CKTstate0 + here->DIOconductSW) * delvdsw +
                              *(ckt->CKTstate0 + here->DIOdIdioSW_dT) * deldelTemp;
                }
                /*
                 *   bypass if solution has not changed
                 */
#ifndef NOBYPASS
                if ((!(ckt->CKTmode & MODEINITPRED)) && (ckt->CKTbypass)) {
                    tol=ckt->CKTvoltTol + ckt->CKTreltol*
                        MAX(fabs(vd),fabs(*(ckt->CKTstate0 +here->DIOvoltage)));
                    if (fabs(delvd) < tol) {
                        tol=ckt->CKTreltol* MAX(fabs(cdhat),
                                fabs(*(ckt->CKTstate0 + here->DIOcurrent)))+
                                ckt->CKTabstol;
                        if (fabs(cdhat- *(ckt->CKTstate0 + here->DIOcurrent))
                                < tol) {
                            if ((here->DIOtempNode == 0) ||
                                (fabs(deldelTemp) < (ckt->CKTreltol * MAX(fabs(delTemp),
                                      fabs(*(ckt->CKTstate0+here->DIOdeltemp)))+
                                      ckt->CKTvoltTol*1e4))) {
                                if ((!model->DIOresistSWGiven) || (fabs(delvdsw) < ckt->CKTvoltTol + ckt->CKTreltol *
                                                         MAX(fabs(vdsw),fabs(*(ckt->CKTstate0+here->DIOvoltageSW))))) {
                                    if ((!model->DIOresistSWGiven) || (fabs(cdhatsw- *(ckt->CKTstate0 + here->DIOcurrentSW))
                                                             < ckt->CKTreltol* MAX(fabs(cdhatsw),
                                                             fabs(*(ckt->CKTstate0 + here->DIOcurrentSW)))+ckt->CKTabstol)) {
                                        vd= *(ckt->CKTstate0 + here->DIOvoltage);
                                        cd= *(ckt->CKTstate0 + here->DIOcurrent);
                                        gd= *(ckt->CKTstate0 + here->DIOconduct);
                                        delTemp = *(ckt->CKTstate0 + here->DIOdeltemp);
                                        dIdio_dT= *(ckt->CKTstate0 + here->DIOdIdio_dT);
                                        if (model->DIOresistSWGiven) {
                                            vdsw= *(ckt->CKTstate0 + here->DIOvoltageSW);
                                            cdsw= *(ckt->CKTstate0 + here->DIOcurrentSW);
                                            gdsw= *(ckt->CKTstate0 + here->DIOconductSW);
                                            dIdioSw_dT= *(ckt->CKTstate0 + here->DIOdIdioSW_dT);
                                        }
                                        vqp= *(ckt->CKTstate0 + here->DIOqp);
                                        cdres= *(ckt->CKTstate0 + here->DIOresCurrent);
                                        gdres= *(ckt->CKTstate0 + here->DIOresConduct);
                                        cqcsr= *(ckt->CKTstate0 + here->DIOcqcsr);
                                        gqcsr= *(ckt->CKTstate0 + here->DIOgqcsr);
                                        goto load;
                                    }
                                }
                            }
                        }
                    }
                }
#endif /* NOBYPASS */
                /*
                 *   limit new junction voltage
                 */
                if ( (model->DIObreakdownVoltageGiven) &&
                        (vd < MIN(0,-here->DIOtBrkdwnV+10*vtebrk))) {
                    vdtemp = -(vd+here->DIOtBrkdwnV);
                    vdtemp = DEVpnjlim(vdtemp,
                            -(*(ckt->CKTstate0 + here->DIOvoltage) +
                            here->DIOtBrkdwnV),vtebrk,
                            here->DIOtVcrit,&Check_dio);
                    vd = -(vdtemp+here->DIOtBrkdwnV);
                } else {
                    vd = DEVpnjlim(vd,*(ckt->CKTstate0 + here->DIOvoltage),
                            vte,here->DIOtVcrit,&Check_dio);
                }
                if (model->DIOresistSWGiven) {
                    if ( (model->DIObreakdownVoltageGiven) &&
                            (vdsw < MIN(0,-here->DIOtBrkdwnV+10*vtebrk))) {
                        vdtemp = -(vdsw+here->DIOtBrkdwnV);
                        vdtemp = DEVpnjlim(vdtemp,
                                -(*(ckt->CKTstate0 + here->DIOvoltageSW) +
                                here->DIOtBrkdwnV),vtebrk,
                                here->DIOtVcritSW,&Check_dio_sw);
                        vdsw = -(vdtemp+here->DIOtBrkdwnV);
                    } else {
                        vdsw = DEVpnjlim(vdsw,*(ckt->CKTstate0 + here->DIOvoltageSW),
                                  vtesw,here->DIOtVcritSW,&Check_dio_sw);
                    }
                }
                if (selfheat)
                    delTemp = DEVlimitlog(delTemp,
                        *(ckt->CKTstate0 + here->DIOdeltemp), 100, &Check_th);
                else
                    delTemp = 0.0;
            }
            /*
             *   compute dc current and derivitives
             */
next1:
            if (selfheat) {
                Temp = here->DIOtemp + delTemp;
                DIOtempUpdate(model, here, Temp, ckt);
                vt = CONSTKoverQ * Temp;
                vte = model->DIOemissionCoeff * vt;
                vtebrk = model->DIObrkdEmissionCoeff * vt;
            } else {
                Temp = here->DIOtemp;
            }

            csat = here->DIOtSatCur;
            csat_dT = here->DIOtSatCur_dT;
            csatsw = here->DIOtSatSWCur;
            csatsw_dT = here->DIOtSatSWCur_dT;
            gspr = here->DIOtConductance;
            gsprsw = here->DIOtConductanceSW;

            if (model->DIOsatSWCurGiven) {               /* sidewall current */
                double vds;
                if (model->DIOresistSWGiven)
                    vds = vdsw;                          /* sidewall voltage used */
                else
                    vds = vd;                            /* common voltage used */

                if (model->DIOswEmissionCoeffGiven) {    /* with own characteristic */

                    if (vds >= -3*vtesw) {               /* forward */

                        evd = exp(vds/vtesw);
                        cdsw = csatsw*(evd-1);
                        gdsw = csatsw*evd/vtesw;
                        cdsw_dT = csatsw_dT * (evd - 1) - csatsw * vds * evd / (vtesw * Temp);

                    } else if ((!(model->DIObreakdownVoltageGiven)) ||
                            vds >= -here->DIOtBrkdwnV) { /* reverse */

                        argsw = 3*vtesw/(vds*CONSTe);
                        argsw = argsw * argsw * argsw;
                        argsw_dT = 3 * argsw / Temp;
                        cdsw = -csatsw*(1+argsw);
                        gdsw = csatsw*3*argsw/vds;
                        cdsw_dT = -csatsw_dT - (csatsw_dT*argsw + csatsw*argsw_dT);

                    } else if (!model->DIOresistSWGiven){ /* breakdown, but not for separate sidewall diode */
                        double evrev_dT;

                        evrev = exp(-(here->DIOtBrkdwnV+vds)/vtebrk);
                        evrev_dT = (here->DIOtBrkdwnV+vds)*evrev/(vtebrk*Temp);
                        cdsw = -csatsw*evrev;
                        gdsw = csatsw*evrev/vtebrk;
                        cdsw_dT = -(csatsw_dT*evrev + csatsw*evrev_dT);

                    }

                }

            }

            /*
             *   temperature dependent diode saturation current and derivative
             */

            if (vd >= -3*vte) {      /* bottom and sidewall current forward with common voltage */
                                     /* and with common characteristic */
                evd = exp(vd/vte);
                cdb = csat*(evd-1);
                gdb = csat*evd/vte;
                cdb_dT = csat_dT * (evd - 1) - csat * vd * evd / (vte * Temp);
                if ((model->DIOsatSWCurGiven)&&(!model->DIOswEmissionCoeffGiven)) {
                    cdsw = csatsw*(evd-1);
                    gdsw = csatsw*evd/vte;
                    cdsw_dT = csatsw_dT * (evd - 1) - csatsw * vd * evd / (vte * Temp);
                }
                if (model->DIOrecSatCurGiven) { /* recombination current */
                    evd_rec = exp(vd/vterec);
                    cdb_rec = here->DIOtRecSatCur*(evd_rec-1);
                    gdb_rec = here->DIOtRecSatCur*evd_rec/vterec;
                    cdb_rec_dT = here->DIOtRecSatCur_dT * (evd_rec - 1)
                                -here->DIOtRecSatCur * vd * evd_rec / (vterec*Temp);
                    t1 = pow((1-vd/here->DIOtJctPot), 2) + 0.005;
                    gen_fac = pow(t1, here->DIOtGradingCoeff/2);
                    gen_fac_vd = -here->DIOtGradingCoeff * (1-vd/here->DIOtJctPot)
                                                         * pow(t1, (here->DIOtGradingCoeff/2-1));
                    cdb_rec = cdb_rec * gen_fac;
                    gdb_rec = gdb_rec * gen_fac + cdb_rec * gen_fac_vd;
                    cdb = cdb + cdb_rec;
                    gdb = gdb + gdb_rec;
                    cdb_dT = cdb_dT + cdb_rec_dT*gen_fac;
                }

            } else if ((!(model->DIObreakdownVoltageGiven)) ||
                    vd >= -here->DIOtBrkdwnV) { /* reverse */

                double darg_dT;

                arg = 3*vte/(vd*CONSTe);
                arg = arg * arg * arg;
                darg_dT = 3 * arg / Temp;
                if (model->DIOrecSatCurGiven) {
                    evd_rec = exp((-3*vte)/vterec);
                    cdb_rec = here->DIOtRecSatCur*(evd_rec-1);
                    t1 = pow((1-(-3*vte)/here->DIOtJctPot), 2) + 0.005;
                    gen_fac = pow(t1, here->DIOtGradingCoeff/2);
                    cdb = -csat*(1+arg) + gen_fac*cdb_rec;
                } else {
                    cdb = -csat*(1+arg);
                }
                gdb = csat*3*arg/vd;
                cdb_dT = -csat_dT - (csat_dT*arg + csat*darg_dT);
                if ((model->DIOsatSWCurGiven)&&(!model->DIOswEmissionCoeffGiven)) {
                    cdsw = -csatsw*(1+arg);
                    gdsw = csatsw*3*arg/vd;
                    cdsw_dT = -csatsw_dT - (csatsw_dT*arg + csatsw*darg_dT);
                }

            } else {                            /* breakdown */

                double evrev_dT;

                evrev = exp(-(here->DIOtBrkdwnV+vd)/vtebrk);
                evrev_dT = (here->DIOtBrkdwnV+vd)*evrev/(vtebrk*Temp);
                if (model->DIOrecSatCurGiven) {
                    evd_rec = exp((-3*vte)/vterec);
                    cdb_rec = here->DIOtRecSatCur*(evd_rec-1);
                    t1 = pow((1-(-3*vte)/here->DIOtJctPot), 2) + 0.005;
                    gen_fac = pow(t1, here->DIOtGradingCoeff/2);
                    cdb = -csat*evrev + gen_fac*cdb_rec;
                } else {
                    cdb = -csat*evrev;
                }
                gdb = csat*evrev/vtebrk;
                cdb_dT = -(csat_dT*evrev + csat*evrev_dT);
                if ((model->DIOsatSWCurGiven)
                    &&(!model->DIOswEmissionCoeffGiven)
                    &&(!model->DIOresistSWGiven)) { /* no breakdown for separate sidewall diode */
                    evrev = exp(-(here->DIOtBrkdwnV+vdsw)/vtebrk);
                    evrev_dT = (here->DIOtBrkdwnV+vdsw)*evrev/(vtebrk*Temp);
                    cdsw = -csatsw*evrev;
                    gdsw = csatsw*evrev/vtebrk;
                    cdsw_dT = -(csatsw_dT*evrev + csatsw*evrev_dT);
                }

            }

            if (model->DIOtunSatSWCurGiven) {    /* tunnel sidewall current */

                vtetun = model->DIOtunEmissionCoeff * vt;
                evd = exp(-vd/vtetun);

                cdsw = cdsw - here->DIOtTunSatSWCur * (evd - 1);
                gdsw = gdsw + here->DIOtTunSatSWCur * evd / vtetun;
                cdsw_dT = cdsw_dT - here->DIOtTunSatSWCur_dT * (evd - 1)
                                  - here->DIOtTunSatSWCur * vd * evd / (vtetun * Temp);

            }

            if (model->DIOtunSatCurGiven) {      /* tunnel bottom current */

                vtetun = model->DIOtunEmissionCoeff * vt;
                evd = exp(-vd/vtetun);

                cdb = cdb - here->DIOtTunSatCur * (evd - 1);
                gdb = gdb + here->DIOtTunSatCur * evd / vtetun;
                cdb_dT = cdb_dT - here->DIOtTunSatCur_dT * (evd - 1)
                                - here->DIOtTunSatCur * vd * evd / (vtetun * Temp);

            }

            if (vd >= -3*vte) { /* limit forward */

                if ( (model->DIOforwardKneeCurrentGiven) && (cdb > 1.0e-18) ) {
                    double ikf_area_m = here->DIOforwardKneeCurrent;
                    sqrt_ikx = sqrt(cdb/ikf_area_m);
                    gdb = ((1+sqrt_ikx)*gdb - cdb*gdb/(2*sqrt_ikx*ikf_area_m))/(1+2*sqrt_ikx + cdb/ikf_area_m);
                    cdb = cdb/(1+sqrt_ikx);
                }

            } else {            /* limit reverse */

                if ( (model->DIOreverseKneeCurrentGiven) && (cdb < -1.0e-18) ) {
                    double ikr_area_m = here->DIOreverseKneeCurrent;
                    sqrt_ikx = sqrt(cdb/(-ikr_area_m));
                    gdb = ((1+sqrt_ikx)*gdb + cdb*gdb/(2*sqrt_ikx*ikr_area_m))/(1+2*sqrt_ikx - cdb/ikr_area_m);
                    cdb = cdb/(1+sqrt_ikx);
                }
            }

            if ( (model->DIOforwardSWKneeCurrentGiven) && (cdsw > 1.0e-18) ) {
                double ikp_peri_m = here->DIOforwardSWKneeCurrent;
                sqrt_ikx = sqrt(cdsw/ikp_peri_m);
                gdsw = ((1+sqrt_ikx)*gdsw - cdsw*gdsw/(2*sqrt_ikx*ikp_peri_m))/(1+2*sqrt_ikx + cdsw/ikp_peri_m);
                cdsw = cdsw/(1+sqrt_ikx);
            }

            if (!model->DIOresistSWGiven) {
                cd = cdb + cdsw + ckt->CKTgmin*vd;
                gd = gdb + gdsw + ckt->CKTgmin;
                dIdio_dT = cdb_dT + cdsw_dT;
            } else {
                cd = cdb + ckt->CKTgmin*vd;
                gd = gdb + ckt->CKTgmin;
                cdsw = cdsw + ckt->CKTgmin*vdsw;
                gdsw = gdsw + ckt->CKTgmin;
                dIdio_dT = cdb_dT;
                dIdioSw_dT = cdsw_dT;
            }

            cdres = cd;
            gdres = gd;
            cqcsr = 0;
            gqcsr = 0;

            if ((ckt->CKTmode & (MODEDCTRANCURVE | MODETRAN | MODEAC | MODEINITSMSIG)) ||
                     ((ckt->CKTmode & MODETRANOP) && (ckt->CKTmode & MODEUIC))) {
              /*
               *   charge storage elements
               */
                czero=here->DIOtJctCap;
                if (vd < here->DIOtDepCap){
                    arg=1-vd/here->DIOtJctPot;
                    sarg=exp(-here->DIOtGradingCoeff*log(arg));
                    deplcharge = here->DIOtJctPot*czero*(1-arg*sarg)/(1-here->DIOtGradingCoeff);
                    deplcap = czero*sarg;
                } else {
                    czof2=czero/here->DIOtF2;
                    deplcharge = czero*here->DIOtF1+czof2*(here->DIOtF3*(vd-here->DIOtDepCap)+
                                 (here->DIOtGradingCoeff/(here->DIOtJctPot+here->DIOtJctPot))*(vd*vd-here->DIOtDepCap*here->DIOtDepCap));
                    deplcap = czof2*(here->DIOtF3+here->DIOtGradingCoeff*vd/here->DIOtJctPot);
                }
                czeroSW=here->DIOtJctSWCap;
                double vdx;
                if (model->DIOresistSWGiven)
                    vdx = vdsw;
                else
                    vdx = vd;
                if (vdx < here->DIOtDepSWCap){
                    argSW=1-vdx/here->DIOtJctSWPot;
                    sargSW=exp(-model->DIOgradingSWCoeff*log(argSW));
                    deplchargeSW = here->DIOtJctSWPot*czeroSW*(1-argSW*sargSW)/(1-model->DIOgradingSWCoeff);
                    deplcapSW = czeroSW*sargSW;
                } else {
                    czof2SW=czeroSW/here->DIOtF2SW;
                    deplchargeSW = czeroSW*here->DIOtF1+czof2SW*(here->DIOtF3SW*(vdx-here->DIOtDepSWCap)+
                                   (model->DIOgradingSWCoeff/(here->DIOtJctSWPot+here->DIOtJctSWPot))*(vdx*vdx-here->DIOtDepSWCap*here->DIOtDepSWCap));
                    deplcapSW = czof2SW*(here->DIOtF3SW+model->DIOgradingSWCoeff*vdx/here->DIOtJctSWPot);
                }

                if (revrec) {
                    /*
                      soft recovery with TT!=0
                      add only depletion capacitance.
                    */
                    *(ckt->CKTstate0 + here->DIOcapCharge) =
                            deplcharge + deplchargeSW + (here->DIOcmetal + here->DIOcpoly)*vd;

                    capd = deplcap + deplcapSW + here->DIOcmetal + here->DIOcpoly;
                    here->DIOcap = capd;
                    /*
                      DIOcap is now equal only to depletion capacitance + overlap capacitance.
                      Diffusion capacitance is modelled via Qp so there is no clear way to define it.
                    */

                    /* Now prepare the charge for the capacitor connected to the QP node */
                    *(ckt->CKTstate0 + here->DIOsrcapCharge) = here->DIOtTransitTime * vqp;
                    capsr = here->DIOtTransitTime;
                } else {
                    /* no soft recovery of soft recovery with TT=0 (i.e. no soft recovery due to TT=0) */
                    diffcharge = here->DIOtTransitTime*cd;
                    diffcap = here->DIOtTransitTime*gd;
                    if (!model->DIOresistSWGiven) {
                        *(ckt->CKTstate0 + here->DIOcapCharge) =
                                diffcharge + deplcharge + deplchargeSW + (here->DIOcmetal + here->DIOcpoly)*vd;
                        capd = diffcap + deplcap + deplcapSW + here->DIOcmetal + here->DIOcpoly;
                        here->DIOcap = capd;
                    } else {
                        *(ckt->CKTstate0 + here->DIOcapCharge) =
                                diffcharge + deplcharge + (here->DIOcmetal + here->DIOcpoly)*vd;
                        capd = diffcap + deplcap + here->DIOcmetal + here->DIOcpoly;
                        here->DIOcap = capd;
                        *(ckt->CKTstate0 + here->DIOcapChargeSW) =
                                deplcapSW;
                        capdsw = deplcapSW;
                        here->DIOcapSW = capdsw;
                    }

                    *(ckt->CKTstate0 + here->DIOsrcapCharge) = 0;
                    capsr = 0;
                }
                /*
                 *   store small-signal parameters
                 */
                if( (!(ckt->CKTmode & MODETRANOP)) ||
                        (!(ckt->CKTmode & MODEUIC)) ) {
                    if (ckt->CKTmode & MODEINITSMSIG){
                        *(ckt->CKTstate0 + here->DIOcapCurrent) = capd;
                        if (model->DIOresistSWGiven) {
                            *(ckt->CKTstate0 + here->DIOcapCurrentSW) = capdsw;
                        }
                        if(SenCond){
                            *(ckt->CKTstate0 + here->DIOcurrent) = cd;
                            *(ckt->CKTstate0 + here->DIOconduct) = gd;
                            *(ckt->CKTstate0 + here->DIOdIdio_dT) = dIdio_dT;
                            if (model->DIOresistSWGiven) {
                                *(ckt->CKTstate0 + here->DIOcurrentSW) = cdsw;
                                *(ckt->CKTstate0 + here->DIOconductSW) = gdsw;
                                *(ckt->CKTstate0 + here->DIOdIdioSW_dT) = dIdioSw_dT;
                            }
                            *(ckt->CKTstate0 + here->DIOresCurrent) = cdres;
                            *(ckt->CKTstate0 + here->DIOresConduct) = gdres;
                            *(ckt->CKTstate0 + here->DIOcqcsr) = cqcsr;
                            *(ckt->CKTstate0 + here->DIOgqcsr) = gqcsr;
#ifdef SENSDEBUG
                            printf("storing small signal parameters\n");
                            printf("cd = %.7e,vd = %.7e\n",cd,vd);
                            printf("capd = %.7e ,gd = %.7e \n",capd,gd);
#endif /* SENSDEBUG */
                        }
                        continue;
                    }

                    /*
                     *   transient analysis
                     */
                    if(SenCond && (ckt->CKTsenInfo->SENmode == TRANSEN)){
                        *(ckt->CKTstate0 + here->DIOcurrent) = cd;
                        if (model->DIOresistSWGiven)
                            *(ckt->CKTstate0 + here->DIOcurrentSW) = cdsw;
                        *(ckt->CKTstate0 + here->DIOresCurrent) = cdres;
#ifdef SENSDEBUG
                        printf("storing parameters for transient sensitivity\n"
                                );
                        printf("qd = %.7e, capd = %.7e,cd = %.7e\n",
                                *(ckt->CKTstate0 + here->DIOcapCharge),capd,cd);
#endif /* SENSDEBUG */
                        continue;
                    }

                    if (ckt->CKTmode & MODEINITTRAN) {
                        *(ckt->CKTstate1 + here->DIOcapCharge) =
                                *(ckt->CKTstate0 + here->DIOcapCharge);
                        if (model->DIOresistSWGiven)
                            *(ckt->CKTstate1 + here->DIOcapChargeSW) =
                                    *(ckt->CKTstate0 + here->DIOcapChargeSW);
                    }
                    error = NIintegrate(ckt,&geq,&ceq,capd,here->DIOcapCharge);
                    if(error) return(error);
                    gd=gd+geq;
                    cd=cd+*(ckt->CKTstate0 + here->DIOcapCurrent);
                    if (model->DIOresistSWGiven) {
                        error = NIintegrate(ckt,&geq,&ceq,capdsw,here->DIOcapChargeSW);
                        if(error) return(error);
                        gdsw=gdsw+geq;
                        cdsw=cdsw+*(ckt->CKTstate0 + here->DIOcapCurrentSW);
                    }
                    if (ckt->CKTmode & MODEINITTRAN) {
                        *(ckt->CKTstate1 + here->DIOcapCurrent) =
                                *(ckt->CKTstate0 + here->DIOcapCurrent);
                        if (model->DIOresistSWGiven)
                            *(ckt->CKTstate1 + here->DIOcapCurrentSW) =
                                    *(ckt->CKTstate0 + here->DIOcapCurrentSW);
                    }
                    if (revrec) {
                        /* soft recovery subcircuit */
                        if (ckt->CKTmode & MODEINITTRAN) {
                            *(ckt->CKTstate1 + here->DIOsrcapCharge) =
                                    *(ckt->CKTstate0 + here->DIOsrcapCharge);
                        }
                        error = NIintegrate(ckt,&geq,&ceq,capsr,here->DIOsrcapCharge);
                        if(error) return(error);
                        gqcsr = geq;
                        cqcsr = *(ckt->CKTstate0 + here->DIOsrcapCurrent);
                        if (ckt->CKTmode & MODEINITTRAN) {
                            *(ckt->CKTstate1 + here->DIOsrcapCurrent) =
                                    *(ckt->CKTstate0 + here->DIOsrcapCurrent);
                        }
                    }
                    if (selfheat)
                    {
                        error = NIintegrate(ckt, &gcTt, &ceqqth, model->DIOcth0, here->DIOqth);
                        if (error) return(error);
                        if (ckt->CKTmode & MODEINITTRAN) {
                            *(ckt->CKTstate1 + here->DIOcqth) =
                                    *(ckt->CKTstate0 + here->DIOcqth);
                        }
                    }
                }
            }

            if(SenCond) goto next2;

            /*
             *   check convergence
             */
            if ( (!(ckt->CKTmode & MODEINITFIX)) || (!(here->DIOoff))  ) {
                if (!model->DIOresistSWGiven) {
                    if ((Check_th == 1) || (Check_dio == 1)) {
                        ckt->CKTnoncon++;
                        ckt->CKTtroubleElt = (GENinstance *) here;
                    }
                } else {
                    if ((Check_th == 1) || (Check_dio == 1) || (Check_dio_sw == 1)) {
                        ckt->CKTnoncon++;
                        ckt->CKTtroubleElt = (GENinstance *) here;
                    }
                }
            }
next2:      *(ckt->CKTstate0 + here->DIOvoltage) = vd;
            *(ckt->CKTstate0 + here->DIOcurrent) = cd;
            *(ckt->CKTstate0 + here->DIOconduct) = gd;
            *(ckt->CKTstate0 + here->DIOdeltemp) = delTemp;
            *(ckt->CKTstate0 + here->DIOdIdio_dT) = dIdio_dT;
            if (model->DIOresistSWGiven) {
                *(ckt->CKTstate0 + here->DIOvoltageSW) = vdsw;
                *(ckt->CKTstate0 + here->DIOcurrentSW) = cdsw;
                *(ckt->CKTstate0 + here->DIOconductSW) = gdsw;
                *(ckt->CKTstate0 + here->DIOdIdioSW_dT) = dIdioSw_dT;
            }
            *(ckt->CKTstate0 + here->DIOqp) = vqp;
            *(ckt->CKTstate0 + here->DIOresCurrent) = cdres;
            *(ckt->CKTstate0 + here->DIOresConduct) = gdres;
            *(ckt->CKTstate0 + here->DIOcqcsr) = cqcsr;
            *(ckt->CKTstate0 + here->DIOgqcsr) = gqcsr;
            if(SenCond)  continue;

#ifndef NOBYPASS
            load:
#endif
            if (selfheat) {
                double dIrs_dVrs, dIrs_dgspr, dIth_dIrs;
                vrs = *(ckt->CKTrhsOld + here->DIOposNode) - *(ckt->CKTrhsOld + here->DIOposPrimeNode);
                dIrs_dVrs = gspr;
                dIrs_dgspr = vrs;
                dIrs_dT = dIrs_dgspr * here->DIOtConductance_dT;
                Ith = vd*cd + vrs*vrs*gspr; /* Diode dissipated power */
                dIth_dVrs = vrs*gspr;
                dIth_dIrs = vrs;
                dIth_dVrs = dIth_dVrs + dIth_dIrs*dIrs_dVrs;
                dIth_dT = dIth_dIrs*dIrs_dT + dIdio_dT*vd;
                dIth_dVdio = cd + vd*gd;
                here->DIOdIth_dVrs = dIth_dVrs;
                here->DIOgcTt = gcTt;
                here->DIOdIrs_dT = dIrs_dT;
                here->DIOdIth_dVdio = dIth_dVdio;
                here->DIOdIth_dT = dIth_dT;
                if (model->DIOresistSWGiven) {
                    double dIrssw_dVrssw, dIrssw_dgsprsw, dIth_dIrssw;
                    vrssw = *(ckt->CKTrhsOld + here->DIOposNode) - *(ckt->CKTrhsOld + here->DIOposSwPrimeNode);
                    dIrssw_dVrssw = gsprsw;
                    dIrssw_dgsprsw = vrssw;
                    dIrssw_dT = dIrssw_dgsprsw * here->DIOtConductanceSW_dT;
                    Ith = Ith + vdsw*cdsw + vrssw*vrssw*gsprsw; /* Diode dissipated power */
                    dIth_dVrssw = vrssw*gsprsw;
                    dIth_dIrssw = vrssw;
                    dIth_dVrssw = dIth_dVrssw + dIth_dIrssw*dIrssw_dVrssw;
                    dIth_dT = dIth_dT + dIth_dIrssw*dIrssw_dT + dIdioSw_dT*vdsw;
                    dIth_dVdioSw = cdsw + vdsw*gdsw;
                    here->DIOdIth_dVrssw = dIth_dVrssw;
                    here->DIOdIth_dVdio = dIth_dVdioSw;
                    here->DIOdIth_dT = dIth_dT;
                    here->DIOdIrssw_dT = dIrssw_dT;
                }
            }
            /*
             *   load current vector
             */
            cdeq=cd-gd*vd;
            *(ckt->CKTrhs + here->DIOnegNode) += cdeq;
            *(ckt->CKTrhs + here->DIOposPrimeNode) -= cdeq;
            if (selfheat) {
                *(ckt->CKTrhs + here->DIOposNode)      +=  dIrs_dT*delTemp;
                *(ckt->CKTrhs + here->DIOposPrimeNode) +=  dIdio_dT*delTemp - dIrs_dT*delTemp;
                *(ckt->CKTrhs + here->DIOnegNode)      += -dIdio_dT*delTemp;
                *(ckt->CKTrhs + here->DIOtempNode)     +=  Ith - dIth_dVdio*vd - dIth_dVrs*vrs - dIth_dT*delTemp - ceqqth;
            }
            if (model->DIOresistSWGiven) {
                double cdeqsw=cdsw-gdsw*vdsw;
                *(ckt->CKTrhs + here->DIOnegNode) += cdeqsw;
                *(ckt->CKTrhs + here->DIOposSwPrimeNode) -= cdeqsw;
                if (selfheat) {
                    *(ckt->CKTrhs + here->DIOposNode)        +=  dIrssw_dT*delTemp;
                    *(ckt->CKTrhs + here->DIOposSwPrimeNode) +=  dIdioSw_dT*delTemp - dIrssw_dT*delTemp;
                    *(ckt->CKTrhs + here->DIOnegNode)        += -dIdioSw_dT*delTemp;
                    *(ckt->CKTrhs + here->DIOtempNode)       += -dIth_dVdioSw*vdsw - dIth_dVrssw*vrssw;
                }
            }
            /*
             *   load matrix
             */
            *(here->DIOposPosPtr) += gspr;
            *(here->DIOnegNegPtr) += gd;
            *(here->DIOposPrimePosPrimePtr) += (gd + gspr);
            *(here->DIOposPosPrimePtr) -= gspr;
            *(here->DIOnegPosPrimePtr) -= gd;
            *(here->DIOposPrimePosPtr) -= gspr;
            *(here->DIOposPrimeNegPtr) -= gd;
            if (selfheat) {
                (*(here->DIOtempPosPtr)      += -dIth_dVrs);
                (*(here->DIOtempPosPrimePtr) += -dIth_dVdio + dIth_dVrs);
                (*(here->DIOtempNegPtr)      +=  dIth_dVdio);
                (*(here->DIOtempTempPtr)     += -dIth_dT + 1/model->DIOrth0 + gcTt);
                (*(here->DIOposTempPtr)      +=  dIrs_dT);
                (*(here->DIOposPrimeTempPtr) +=  dIdio_dT - dIrs_dT);
                (*(here->DIOnegTempPtr)      += -dIdio_dT);
            }
            if (model->DIOresistSWGiven) {
                *(here->DIOposPosPtr) += gsprsw;
                *(here->DIOnegNegPtr) += gdsw;
                *(here->DIOposSwPrimePosSwPrimePtr) += (gdsw + gsprsw);
                *(here->DIOposPosSwPrimePtr) -= gsprsw;
                *(here->DIOnegPosSwPrimePtr) -= gdsw;
                *(here->DIOposSwPrimePosPtr) -= gsprsw;
                *(here->DIOposSwPrimeNegPtr) -= gdsw;

                if (selfheat) {
                    (*(here->DIOtempPosPtr)        += -dIth_dVrssw);
                    (*(here->DIOtempPosSwPrimePtr) += -dIth_dVdioSw + dIth_dVrssw);
                    (*(here->DIOtempNegPtr)        +=  dIth_dVdioSw);
                    (*(here->DIOposTempPtr)        +=  dIrssw_dT);
                    (*(here->DIOposSwPrimeTempPtr) +=  dIdioSw_dT - dIrssw_dT);
                    (*(here->DIOnegTempPtr)        += -dIdioSw_dT);
                }
            }

            if (revrec) {
                double fac, ceqrr, dcrrdvd, grr;
                double ceqrrd, geqrrd;
                /* QP subcircuit */
                fac = here->DIOtTransitTime / model->DIOsoftRevRecParam;
                dcrrdvd = fac*gdres;
                ceqrr = -fac*cdres + cqcsr + dcrrdvd*vd - gqcsr*vqp;
                grr = 1/model->DIOsoftRevRecParam;
                *(ckt->CKTrhs + here->DIOqpNode) -= ceqrr;
                *(here->DIOqpQpPtr)       += grr + gqcsr;
                *(here->DIOqpPosPrimePtr) += -dcrrdvd;
                *(here->DIOqpNegPtr)      += dcrrdvd;
                /* Contribution to diode current */
                here->DIOqpGain = (1 - model->DIOsoftRevRecParam) / here->DIOtTransitTime;
                /* Linear contribution -(1-vp)/tau*ddt(Qp) */
                geqrrd = here->DIOqpGain*gqcsr;
                ceqrrd = here->DIOqpGain*cqcsr - geqrrd*vqp;
                *(ckt->CKTrhs + here->DIOposPrimeNode) -= ceqrrd;
                *(ckt->CKTrhs + here->DIOnegNode) += ceqrrd;
                *(here->DIOposPrimeQpPtr) += geqrrd;
                *(here->DIOnegQpPtr) += -geqrrd;
            }
        }
    }
    return(OK);
}
