File indexing completed on 2024-05-05 05:46:12

0001 /**************************************************************************
0002  *   Copyright (C) 2003-2005 by David Saxton                               *
0003  *   david@bluehaze.org                                                    *
0004  *                                                                         *
0005  *   This program is free software; you can redistribute it and/or modify  *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 2 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  ***************************************************************************/
0010 
0011 #include "nonlinear.h"
0012 
0013 #include <algorithm>
0014 
0015 #include <cmath>
0016 using namespace std;
0017 
0018 const double KTL_MAX_DOUBLE = 1.7976931348623157e+308; ///< 7fefffff ffffffff
0019 const int KTL_MAX_EXPONENT = int(log(KTL_MAX_DOUBLE));
0020 
0021 NonLinear::NonLinear()
0022     : Element()
0023 {
0024 }
0025 
0026 double NonLinear::diodeCurrent(double v, double I_S, double N) const
0027 {
0028     return I_S * (exp(std::min<double>(v / (N * V_T), KTL_MAX_EXPONENT)) - 1);
0029 }
0030 
0031 double NonLinear::diodeConductance(double v, double I_S, double N) const
0032 {
0033     double Vt = V_T * N;
0034     return I_S * exp(std::min<double>(v / Vt, KTL_MAX_EXPONENT)) / Vt;
0035 }
0036 
0037 double NonLinear::diodeVoltage(double V, double V_prev, double N, double V_lim) const
0038 {
0039     double Vt = V_T * N;
0040 
0041     if (V > V_lim && fabs(V - V_prev) > 2 * Vt) {
0042         if (V_prev > 0) {
0043             double arg = (V - V_prev) / Vt;
0044             if (arg > 0)
0045                 V = V_prev + Vt * (2 + log(arg - 2));
0046             else
0047                 V = V_prev - Vt * (2 + log(2 - arg));
0048         } else
0049             V = (V_prev < 0) ? (Vt * log(V / Vt)) : V_lim;
0050     } else {
0051         if (V < 0) {
0052             double arg = (V_prev > 0) ? (-1 - V_prev) : (2 * V_prev - 1);
0053             if (V < arg)
0054                 V = arg;
0055         }
0056     }
0057     return V;
0058 }
0059 
0060 double NonLinear::diodeLimitedVoltage(double I_S, double N) const
0061 {
0062     double Vt = N * V_T;
0063 
0064     return Vt * log(Vt / M_SQRT2 / I_S);
0065 }
0066 
0067 void NonLinear::diodeJunction(double V, double I_S, double N, double *I, double *g) const
0068 {
0069     double Vt = N * V_T;
0070 
0071     if (V < -3 * Vt) {
0072         double a = 3 * Vt / (V * M_E);
0073         a = a * a * a;
0074         *I = -I_S * (1 + a);
0075         *g = +I_S * 3 * a / V;
0076     } else {
0077         double e = exp(std::min<double>(V / Vt, KTL_MAX_EXPONENT));
0078         *I = I_S * (e - 1);
0079         *g = I_S * e / Vt;
0080     }
0081 }
0082 
0083 double NonLinear::fetVoltage(double V, double V_prev, double Vth) const
0084 {
0085     double V_tst_hi = fabs(2 * (V_prev - Vth)) + 2.0;
0086     double V_tst_lo = V_tst_hi / 2;
0087     double V_tox = Vth + 3.5;
0088     double delta_V = V - V_prev;
0089 
0090     if (V_prev >= Vth) {
0091         // on
0092         if (V_prev >= V_tox) {
0093             if (delta_V <= 0) {
0094                 // going off
0095                 if (V >= V_tox) {
0096                     if (-delta_V > V_tst_lo)
0097                         return V_prev - V_tst_lo;
0098 
0099                     return V;
0100                 }
0101 
0102                 return std::max(V, Vth + 2);
0103             }
0104 
0105             // staying on
0106             if (delta_V >= V_tst_hi)
0107                 return V_prev + V_tst_hi;
0108 
0109             return V;
0110         }
0111 
0112         // middle region
0113         if (delta_V <= 0) {
0114             // decreasing
0115             return std::max(V, Vth - 0.5);
0116         }
0117 
0118         // increasing
0119         return std::min(V, Vth + 4);
0120     }
0121 
0122     //  off
0123     if (delta_V <= 0) {
0124         // staying off
0125         if (-delta_V > V_tst_hi)
0126             return V_prev - V_tst_hi;
0127 
0128         return V;
0129     }
0130 
0131     // going on
0132     if (V <= Vth + 0.5) {
0133         if (delta_V > V_tst_lo)
0134             return V_prev + V_tst_lo;
0135 
0136         return V;
0137     }
0138 
0139     return Vth + 0.5;
0140 }
0141 
0142 double NonLinear::fetVoltageDS(double V, double V_prev) const
0143 {
0144     if (V_prev >= 3.5) {
0145         if (V > V_prev)
0146             return std::min(V, 3 * V_prev + 2);
0147         else if (V < 3.5)
0148             return std::max<double>(V, 2);
0149 
0150         return V;
0151     }
0152 
0153     if (V > V_prev)
0154         return std::min<double>(V, 4);
0155 
0156     return std::max(V, -0.5);
0157 }
0158 
0159 void NonLinear::mosDiodeJunction(double V, double I_S, double N, double *I, double *g) const
0160 {
0161     double Vt = N * V_T;
0162 
0163     if (V <= 0) {
0164         *g = I_S / Vt;
0165         *I = *g * V;
0166     } else {
0167         double e = exp(std::min<double>(V / Vt, KTL_MAX_EXPONENT));
0168         *I = I_S * (e - 1);
0169         *g = I_S * e / Vt;
0170     }
0171 
0172     *I += V * I_S;
0173     *g += I_S;
0174 }