1 /**********
2 Copyright 1990 Regents of the University of California.  All rights reserved.
3 Author: 1985 Gordon Jacobs
4 Modified: 2001 Jon Engelbert
5 **********/
6 
7 #include "ngspice/ngspice.h"
8 #include "ngspice/cktdefs.h"
9 #include "ngspice/fteext.h"
10 #include "swdefs.h"
11 #include "ngspice/trandefs.h"
12 #include "ngspice/sperror.h"
13 #include "ngspice/suffix.h"
14 
15 
16 int
SWload(GENmodel * inModel,CKTcircuit * ckt)17 SWload(GENmodel *inModel, CKTcircuit *ckt)
18 {
19     SWmodel *model = (SWmodel *) inModel;
20     SWinstance *here;
21     double g_now;
22     double v_ctrl;
23     double previous_state = -1;
24     double current_state = -1;
25     double old_current_state = -1;
26     double REALLY_OFF = 0, REALLY_ON = 1;   // switch is on or off, not in hysteresis region.
27     double HYST_OFF = 2, HYST_ON = 3;       // switch is on or off while control value is in hysteresis region.
28     //    double previous_region = -1;
29     //    double current_region = -1;
30 
31     for (; model; model = SWnextModel(model))
32         for (here = SWinstances(model); here; here = SWnextInstance(here)) {
33 
34             old_current_state = ckt->CKTstate0[here->SWswitchstate];
35             previous_state = ckt->CKTstate1[here->SWswitchstate];
36 
37             v_ctrl =
38                 ckt->CKTrhsOld[here->SWposCntrlNode] -
39                 ckt->CKTrhsOld[here->SWnegCntrlNode];
40 
41             /* decide the state of the switch */
42 
43             if (ckt->CKTmode & (MODEINITFIX | MODEINITJCT)) {
44 
45                 if (here->SWzero_stateGiven) {
46                     /* switch specified "on" */
47                     if (model->SWvHysteresis >= 0 && v_ctrl > model->SWvThreshold + model->SWvHysteresis)
48                         current_state = REALLY_ON;
49                     else if (model->SWvHysteresis < 0 && v_ctrl > model->SWvThreshold - model->SWvHysteresis)
50                         current_state = REALLY_ON;
51                     else
52                         current_state = HYST_ON;
53                 } else {
54                     if (model->SWvHysteresis >= 0 && v_ctrl < model->SWvThreshold - model->SWvHysteresis)
55                         current_state = REALLY_OFF;
56                     else if (model->SWvHysteresis < 0 && v_ctrl < model->SWvThreshold + model->SWvHysteresis)
57                         current_state = REALLY_OFF;
58                     else
59                         current_state = HYST_OFF;
60                 }
61 
62             } else if (ckt->CKTmode & (MODEINITSMSIG)) {
63 
64                 current_state = previous_state;
65 
66             } else if (ckt->CKTmode & (MODEINITFLOAT)) {
67 
68                 /* use state0 since INITTRAN or INITPRED already called */
69                 if (model->SWvHysteresis > 0) {
70                     if (v_ctrl > (model->SWvThreshold + model->SWvHysteresis))
71                         current_state = REALLY_ON;
72                     else if (v_ctrl < (model->SWvThreshold - model->SWvHysteresis))
73                         current_state = REALLY_OFF;
74                     else
75                         current_state = old_current_state;
76                 } else {        // negative hysteresis case.
77                     if (v_ctrl > (model->SWvThreshold - model->SWvHysteresis))
78                         current_state = REALLY_ON;
79                     else if (v_ctrl < (model->SWvThreshold + model->SWvHysteresis))
80                         current_state = REALLY_OFF;
81                     else {  // in hysteresis... change value if going from low to hysteresis, or from hi to hysteresis.
82                         // if previous state was in hysteresis, then don't change the state..
83                         if (previous_state == HYST_OFF || previous_state == HYST_ON)
84                             current_state = previous_state;
85                         else if (previous_state == REALLY_ON)
86                             current_state = HYST_OFF;
87                         else if (previous_state == REALLY_OFF)
88                             current_state = HYST_ON;
89                         else
90                             internalerror("bad value for previous state in swload");
91                     }
92                 }
93 
94                 if (current_state != old_current_state) {
95                     ckt->CKTnoncon++;       /* ensure one more iteration */
96                     ckt->CKTtroubleElt = (GENinstance *) here;
97                 }
98 
99             } else if (ckt->CKTmode & (MODEINITTRAN | MODEINITPRED)) {
100 
101                 if (model->SWvHysteresis > 0) {
102                     if (v_ctrl > (model->SWvThreshold + model->SWvHysteresis))
103                         current_state = REALLY_ON;
104                     else if (v_ctrl < (model->SWvThreshold - model->SWvHysteresis))
105                         current_state = REALLY_OFF;
106                     else
107                         current_state = previous_state;
108                 } else {        // negative hysteresis case.
109                     if (v_ctrl > (model->SWvThreshold - model->SWvHysteresis))
110                         current_state = REALLY_ON;
111                     else if (v_ctrl < (model->SWvThreshold + model->SWvHysteresis))
112                         current_state = REALLY_OFF;
113                     else {
114                         current_state = 0.0;
115                         if (previous_state == HYST_ON || previous_state == HYST_OFF)
116                             current_state = previous_state;
117                         else if (previous_state == REALLY_ON)
118                             current_state = REALLY_OFF;
119                         else if (previous_state == REALLY_OFF)
120                             current_state = REALLY_ON;
121                     }
122                 }
123             }
124 
125             // code added to force the state to be updated.
126             // there is a possible problem.  What if, during the transient analysis, the time is stepped
127             // forward enough to change the switch's state, but that time point is rejected as being too
128             // distant and then the time is pushed back to a time before the switch changed states.
129             // After analyzing the transient code, it seems that this is not a problem because state updating
130             // occurs before the convergence loop in transient processing.
131 
132             ckt->CKTstate0[here->SWswitchstate] = current_state;
133             ckt->CKTstate0[here->SWctrlvalue] = v_ctrl;
134 
135             if (current_state == REALLY_ON || current_state == HYST_ON)
136                 g_now = model->SWonConduct;
137             else
138                 g_now = model->SWoffConduct;
139 
140             here->SWcond = g_now;
141 
142             *(here->SWposPosPtr) += g_now;
143             *(here->SWposNegPtr) -= g_now;
144             *(here->SWnegPosPtr) -= g_now;
145             *(here->SWnegNegPtr) += g_now;
146         }
147 
148     return OK;
149 }
150