/**********
Copyright 1990 Regents of the University of California.  All rights reserved.
Author: 1985 Thomas L. Quarles
Modified: Apr 2000 - Paolo Nenzi
**********/

#include "ngspice/ngspice.h"
#include "ngspice/cktdefs.h"
#include "resdefs.h"
#include "ngspice/sperror.h"


/* actually load the current resistance value into the sparse matrix
 * previously provided */
int
RESload(GENmodel *inModel, CKTcircuit *ckt)
{
    RESmodel *model = (RESmodel *)inModel;

    /*  loop through all the resistor models */
    for( ; model != NULL; model = RESnextModel(model)) {
        RESinstance *here;

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

            if (here->RESdangling) {
                /* Eldo-style topology reduction: dangling (degree-1) resistor.
                 * Remove it from the system, pin the floating node(s) with a
                 * unit diagonal (set in CKTtopologyReduce()). */
                if (here->RESdangling & 1) *(here->RESposPosPtr) += 1.0;
                if (here->RESdangling & 2) *(here->RESnegNegPtr) += 1.0;
                here->REScurrent = 0.0;
                continue;
            }

            if (here->RESvc1Given || here->RESvc2Given ||
                model->RESvc1Given || model->RESvc2Given ||
                here->RESpvc1Given || model->RESpvc1Given) {
                /* Voltage-dependent resistance (static nonlinear conductance):
                 *   R(V) = Rbase * fpoly(V) * ftanh(V),  I = V/R(V)
                 * loaded with the Newton companion model geq=dI/dV, ieq=I-geq*V.
                 * Both factors are even in V, so this is polarity independent.
                 *
                 *   fpoly = 1 + vc1*|V| + vc2*V^2          (polynomial form)
                 *   ftanh = 1 + pvc1*(tanh(pvc2*|E| + pvc3) - tanh(pvc3))
                 * with the high-field "field" E = |V|/Leff (foundry poly-R law).
                 * All coefficients: instance overrides model, default 0. */
                double vc1 = here->RESvc1Given ? here->RESvc1 :
                             (model->RESvc1Given ? model->RESvoltCoeff1 : 0.0);
                double vc2 = here->RESvc2Given ? here->RESvc2 :
                             (model->RESvc2Given ? model->RESvoltCoeff2 : 0.0);
                double vp = *(ckt->CKTrhsOld+here->RESposNode) -
                            *(ckt->CKTrhsOld+here->RESnegNode);
                double Rbase = (here->RESconduct != 0.0) ? 1.0/here->RESconduct : 1e12;
                /* The foundry's voltage terms use |V| (and tanh of |V|/Leff).
                 * |V| has a derivative discontinuity (sign(V)) at V=0 -- right
                 * where the device usually sits -- which is bad for the
                 * Newton-Raphson Jacobian.  Realize it with a smooth
                 * |V| ~ sqrt(V^2+d^2): identical to |V| for |V|>>d, but with a
                 * continuous derivative V/sqrt(V^2+d^2) through zero.  This is
                 * something the native device can do that the behavioral abs()
                 * form cannot. */
                /* smoothing width (corner radius) for |V|: instance overrides
                 * model, default 1 mV (cf. Eldo mmsmooth). */
                double dlt = here->RESvsmoothGiven ? here->RESvsmooth :
                             (model->RESvsmoothGiven ? model->RESvoltSmooth : 1e-3);
                if (dlt < 1e-12)
                    dlt = 1e-12;               /* keep sqrt(V^2+dlt^2) > 0 */
                double sabs  = sqrt(vp*vp + dlt*dlt);  /* smooth |V| */
                double dsabs = vp / sabs;              /* d|V|/dV, continuous */

                /* polynomial voltage factor */
                double fpoly  = 1.0 + vc1*sabs + vc2*vp*vp;
                double dfpoly = vc1*dsabs + 2.0*vc2*vp;   /* d(fpoly)/dV */

                /* high-field tanh factor (foundry poly-R high-field term):
                 *   ftanh = 1 + pvc1*(tanh(pvc2*|E| + pvc3) - tanh(pvc3))
                 * E = |V|/Leff with Leff = (L - 2*short), the electrical length
                 * used for the conductance (restemp.c).  For value-defined
                 * resistors (no geometry, e.g. the poly-R end segments) there is
                 * no physical length -- RESlength defaults to defl in ressetup --
                 * so Leff collapses to 1 and the field reduces to |V|, matching
                 * the foundry's end-resistor law that uses abs(v) directly. */
                double ftanh = 1.0, dftanh = 0.0;
                if (here->RESpvc1Given || model->RESpvc1Given) {
                    double pvc1 = here->RESpvc1Given ? here->RESpvc1 :
                                  (model->RESpvc1Given ? model->RESpVoltCoeff1 : 0.0);
                    double pvc2 = here->RESpvc2Given ? here->RESpvc2 :
                                  (model->RESpvc2Given ? model->RESpVoltCoeff2 : 0.0);
                    double pvc3 = here->RESpvc3Given ? here->RESpvc3 :
                                  (model->RESpvc3Given ? model->RESpVoltCoeff3 : 0.0);
                    double Leff;
                    if (here->RESresGiven) {
                        Leff = 1.0;             /* value-defined: field = |V| */
                    } else {
                        Leff = here->RESlength - 2.0*model->RESshort;
                        if (Leff < 1e-12)
                            Leff = 1.0;         /* no geometry -> field = |V| */
                    }
                    double ee  = sabs  / Leff;  /* smooth |field| */
                    double dee = dsabs / Leff;  /* d|field|/dV */
                    double th  = tanh(pvc2*ee + pvc3);
                    ftanh  = 1.0 + pvc1*(th - tanh(pvc3));
                    dftanh = pvc1*(1.0 - th*th)*pvc2*dee;  /* d(ftanh)/dV */
                }

                double f = fpoly * ftanh;
                if (f < 1e-3)
                    f = 1e-3;                 /* keep resistance positive */
                double R = Rbase * f;
                double g = 1.0 / R;
                double cur = g * vp;
                double dRdV = Rbase * (dfpoly*ftanh + fpoly*dftanh);
                double geq = g * (1.0 - vp*dRdV/R);   /* dI/dV */
                double ieq = cur - geq*vp;
                here->REScurrent = cur;
                *(here->RESposPosPtr) += geq;
                *(here->RESnegNegPtr) += geq;
                *(here->RESposNegPtr) -= geq;
                *(here->RESnegPosPtr) -= geq;
                *(ckt->CKTrhs+here->RESposNode) -= ieq;
                *(ckt->CKTrhs+here->RESnegNode) += ieq;
            } else {
                here->REScurrent = (*(ckt->CKTrhsOld+here->RESposNode) -
                                    *(ckt->CKTrhsOld+here->RESnegNode)) * here->RESconduct;

                *(here->RESposPosPtr) += here->RESconduct;
                *(here->RESnegNegPtr) += here->RESconduct;
                *(here->RESposNegPtr) -= here->RESconduct;
                *(here->RESnegPosPtr) -= here->RESconduct;
            }
        }
    }
    return(OK);
}


/* actually load the current resistance value into the sparse matrix
 * previously provided */
int
RESacload(GENmodel *inModel, CKTcircuit *ckt)
{
    RESmodel *model = (RESmodel *)inModel;
    double g;

    NG_IGNORE(ckt);

    /*  loop through all the resistor models */
    for( ; model != NULL; model = RESnextModel(model)) {
        RESinstance *here;

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

            if (here->RESacresGiven)
                g = here->RESacConduct;
            else
                g = here->RESconduct;

            *(here->RESposPosPtr) += g;
            *(here->RESnegNegPtr) += g;
            *(here->RESposNegPtr) -= g;
            *(here->RESnegPosPtr) -= g;
        }
    }
    return(OK);
}
