2 * Copyright (C) 2001 Albert Davis
3 * Author: Albert Davis <aldavis@gnu.org>
5 * This file is part of "Gnucap", the Gnu Circuit Analysis Package
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3, or (at your option)
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 *------------------------------------------------------------------
30 #include "io_matrix.h"
31 /*--------------------------------------------------------------------------*/
33 /*--------------------------------------------------------------------------*/
34 class DCOP
: public SIM
{ //
37 scUSER
= 1, /* user requested */
38 scSKIP
= 3, /* effect of "skip" parameter */
39 scITER_R
= 4, /* iter count exceeds itl4 (reducing) */
40 scITER_A
= 5, /* iter count exceeds itl3 (holding) */
41 scTE
= 6, /* truncation error, or device stuff */
42 scINITIAL
= 9, /* initial guess */
43 scREJECT
= 10, /* rejected previous time step */
44 scZERO
= 11, /* fixed zero time step */
45 scSMALL
= 12, /* time step too small */
46 scNO_ADVANCE
= 13, /* after all that it still didn't advance */
47 scLAST
= 15 /* last step */
49 void set_step_cause(STEP_CAUSE C
) {::status
.control
= C
;}
50 STEP_CAUSE
step_cause()const {return STEP_CAUSE(::status
.control
);}
55 void options(CS
&, int);
56 std::string
label()const{untested(); return "dc";}
59 void sweep_recursive(int);
62 explicit DCOP(const DCOP
&): SIM() {unreachable(); incomplete();}
70 PARAMETER
<double> _start
[DCNEST
];
71 PARAMETER
<double> _stop
[DCNEST
];
72 PARAMETER
<double> _step_in
[DCNEST
];
74 double _val_by_user_request
[DCNEST
];
75 double _sweepdamp
[DCNEST
];
77 double* (_sweepval
[DCNEST
]); /* pointer to thing to sweep, dc command */
78 ELEMENT
* (_zap
[DCNEST
]); /* to branch to zap, for re-expand */
79 CARDSTASH _stash
[DCNEST
]; /* store std values of elements being swept */
80 bool _loop
[DCNEST
]; /* flag: do it again backwards */
81 bool _reverse_in
[DCNEST
]; /* flag: sweep backwards, input */
82 bool _reverse
[DCNEST
]; /* flag: sweep backwards, working */
83 bool _cont
; /* flag: continue from previous run */
84 TRACE _trace
; /* enum: show extended diagnostics */
86 bool _ever_converged
; /* don't try to step back otherwise... */
87 enum {ONE_PT
, LIN_STEP
, LIN_PTS
, TIMES
, OCTAVE
, DECADE
} _stepmode
[DCNEST
];
89 /*--------------------------------------------------------------------------*/
90 class DC
: public DCOP
{ //
92 explicit DC(): DCOP() {}
94 void do_it(CS
&, CARD_LIST
*);
97 explicit DC(const DC
&): DCOP() {unreachable(); incomplete();}
99 /*--------------------------------------------------------------------------*/
100 class OP
: public DCOP
{ //
102 explicit OP(): DCOP() {}
104 void do_it(CS
&, CARD_LIST
*);
107 explicit OP(const OP
&): DCOP() {unreachable(); incomplete();}
109 /*--------------------------------------------------------------------------*/
110 /*--------------------------------------------------------------------------*/
111 void DC::do_it(CS
& Cmd
, CARD_LIST
* Scope
)
115 _sim
->set_command_dc();
116 _sim
->_phase
= p_INIT_DC
;
117 ::status
.dc
.reset().start();
119 _sim
->_has_op
= s_DC
;
123 /*--------------------------------------------------------------------------*/
124 void OP::do_it(CS
& Cmd
, CARD_LIST
* Scope
)
128 _sim
->set_command_op();
129 _sim
->_phase
= p_INIT_DC
;
130 ::status
.op
.reset().start();
132 _sim
->_has_op
= s_OP
;
136 /*--------------------------------------------------------------------------*/
137 /*--------------------------------------------------------------------------*/
145 for (int ii
= 0; ii
< DCNEST
; ++ii
) {
147 _reverse_in
[ii
] = false;
148 _reverse
[ii
] = false;
151 _sweepval
[ii
]=&_sim
->_genout
;
153 _stepmode
[ii
] = ONE_PT
;
156 //BUG// in SIM. should be initialized there.
160 /*--------------------------------------------------------------------------*/
161 void DCOP::finish(void)
163 for (int ii
= 0; ii
< _n_sweeps
; ++ii
) {
164 if (_zap
[ii
]) { // component
165 _stash
[ii
].restore();
166 _zap
[ii
]->dec_probes();
167 _zap
[ii
]->precalc_first();
168 _zap
[ii
]->precalc_last();
173 /*--------------------------------------------------------------------------*/
174 void OP::setup(CS
& Cmd
)
176 _sim
->_temp_c
= OPT::temp_c
;
180 _out
.reset(); //BUG// don't know why this is needed */
181 bool ploton
= IO::plotset
&& plotlist().size() > 0;
184 _sweepval
[0] = &(_sim
->_temp_c
);
186 if (Cmd
.match1("'\"({") || Cmd
.is_float()) {
188 if (Cmd
.match1("'\"({") || Cmd
.is_float()) {
191 _stop
[0] = _start
[0];
200 // _sim->_temp_c = temp_c_in;
203 Cmd
.check(bWARNING
, "what's this?");
206 IO::plotout
= (ploton
) ? IO::mstdout
: OMSTREAM();
209 _start
[0].e_val(OPT::temp_c
, _scope
);
212 /*--------------------------------------------------------------------------*/
213 void DC::setup(CS
& Cmd
)
215 _sim
->_temp_c
= OPT::temp_c
;
219 _out
.reset(); //BUG// don't know why this is needed */
220 bool ploton
= IO::plotset
&& plotlist().size() > 0;
223 for (_n_sweeps
= 0; Cmd
.more() && _n_sweeps
< DCNEST
; ++_n_sweeps
) {
224 CARD_LIST::fat_iterator ci
= findbranch(Cmd
, &CARD_LIST::card_list
);
225 if (!ci
.is_end()) { // sweep a component
226 if (ELEMENT
* c
= dynamic_cast<ELEMENT
*>(*ci
)) {
229 throw Exception("dc/op: can't sweep " + (**ci
).long_label() + '\n');
231 }else if (Cmd
.is_float()) { // sweep the generator
232 _zap
[_n_sweeps
] = NULL
;
234 // leave as it was .. repeat Cmd with no args
237 if (Cmd
.match1("'\"({") || Cmd
.is_float()) { // set up parameters
238 _start
[_n_sweeps
] = "NA";
239 _stop
[_n_sweeps
] = "NA";
240 Cmd
>> _start
[_n_sweeps
] >> _stop
[_n_sweeps
];
241 _step
[_n_sweeps
] = 0.;
243 // leave it as it was .. repeat Cmd with no args
247 options(Cmd
,_n_sweeps
);
248 // _sim->_temp_c = temp_c_in;
252 Cmd
.check(bWARNING
, "what's this?");
254 IO::plotout
= (ploton
) ? IO::mstdout
: OMSTREAM();
257 assert(_n_sweeps
> 0);
258 for (int ii
= 0; ii
< _n_sweeps
; ++ii
) {
259 _start
[ii
].e_val(0., _scope
);
262 if (_zap
[ii
]) { // component
263 _stash
[ii
] = _zap
[ii
]; // stash the std value
264 _zap
[ii
]->inc_probes(); // we need to keep track of it
265 _zap
[ii
]->set_value(_zap
[ii
]->value(),0); // zap out extensions
266 _zap
[ii
]->set_constant(false); // so it will be updated
267 _sweepval
[ii
] = _zap
[ii
]->set__value(); // point to value to patch
269 _sweepval
[ii
] = &_sim
->_genout
; // point to value to patch
274 /*--------------------------------------------------------------------------*/
275 void DCOP::fix_args(int Nest
)
277 _stop
[Nest
].e_val(_start
[Nest
], _scope
);
278 _step_in
[Nest
].e_val(0., _scope
);
279 _step
[Nest
] = _step_in
[Nest
];
281 switch (_stepmode
[Nest
]) { untested();
284 _linswp
[Nest
] = true;
286 case LIN_PTS
:untested();
287 if (_step
[Nest
] <= 2.) { untested();
291 _linswp
[Nest
] = true;
294 if (_step
[Nest
] == 0. && _start
[Nest
] != 0.) { untested();
295 _step
[Nest
] = _stop
[Nest
] / _start
[Nest
];
298 _linswp
[Nest
] = false;
300 case OCTAVE
:untested();
301 if (_step
[Nest
] == 0.) {untested();
305 _step
[Nest
] = pow(2.00000001, 1./_step
[Nest
]);
306 _linswp
[Nest
] = false;
309 if (_step
[Nest
] == 0.) {untested();
313 _step
[Nest
] = pow(10., 1./_step
[Nest
]);
314 _linswp
[Nest
] = false;
318 if (_step
[Nest
] == 0.) { // prohibit log sweep from 0
319 _step
[Nest
] = _stop
[Nest
] - _start
[Nest
];
320 _linswp
[Nest
] = true;
324 /*--------------------------------------------------------------------------*/
325 void DCOP::options(CS
& Cmd
, int Nest
)
327 bool _dump_matrix
=false;
328 _sim
->_uic
= _loop
[Nest
] = _reverse_in
[Nest
] = false;
329 unsigned here
= Cmd
.cursor();
332 || (Cmd
.match1("'\"({") && ((Cmd
>> _step_in
[Nest
]), (_stepmode
[Nest
] = LIN_STEP
)))
333 || (Cmd
.is_float() && ((Cmd
>> _step_in
[Nest
]), (_stepmode
[Nest
] = LIN_STEP
)))
334 || (Get(Cmd
, "*", &_step_in
[Nest
]) && (_stepmode
[Nest
] = TIMES
))
335 || (Get(Cmd
, "+", &_step_in
[Nest
]) && (_stepmode
[Nest
] = LIN_STEP
))
336 || Get(Cmd
, "dm", &_dump_matrix
)
337 || (Get(Cmd
, "by", &_step_in
[Nest
]) && (_stepmode
[Nest
] = LIN_STEP
))
338 || (Get(Cmd
, "step", &_step_in
[Nest
]) && (_stepmode
[Nest
] = LIN_STEP
))
339 || (Get(Cmd
, "d{ecade}", &_step_in
[Nest
]) && (_stepmode
[Nest
] = DECADE
))
340 || (Get(Cmd
, "ti{mes}", &_step_in
[Nest
]) && (_stepmode
[Nest
] = TIMES
))
341 || (Get(Cmd
, "lin", &_step_in
[Nest
]) && (_stepmode
[Nest
] = LIN_PTS
))
342 || (Get(Cmd
, "o{ctave}", &_step_in
[Nest
]) && (_stepmode
[Nest
] = OCTAVE
))
343 || Get(Cmd
, "c{ontinue}", &_cont
)
344 || Get(Cmd
, "uic", &_sim
->_uic
)
345 || Get(Cmd
, "dt{emp}", &(_sim
->_temp_c
), mOFFSET
, OPT::temp_c
)
346 || Get(Cmd
, "lo{op}", &_loop
[Nest
])
347 || Get(Cmd
, "re{verse}", &_reverse_in
[Nest
])
348 || Get(Cmd
, "te{mperature}",&(_sim
->_temp_c
))
349 || (Cmd
.umatch("tr{ace} {=}") &&
351 || Set(Cmd
, "n{one}", &_trace
, tNONE
)
352 || Set(Cmd
, "o{ff}", &_trace
, tNONE
)
353 || Set(Cmd
, "w{arnings}", &_trace
, tUNDER
)
354 || Set(Cmd
, "a{lltime}", &_trace
, tALLTIME
)
355 || Set(Cmd
, "r{ejected}", &_trace
, tREJECTED
)
356 || Set(Cmd
, "i{terations}",&_trace
, tITERATION
)
357 || Set(Cmd
, "v{erbose}", &_trace
, tVERBOSE
)
358 || Cmd
.warn(bWARNING
,
359 "need none, off, warnings, iterations, verbose")
363 }while (Cmd
.more() && !Cmd
.stuck(&here
));
366 _trace
= (TRACE
) (_trace
| (int)tMATRIX
);
370 /*--------------------------------------------------------------------------*/
373 // _sim->_age= true; // hack?
374 head(_start
[0], _stop
[0], " ");
375 _sim
->_bypass_ok
= false;
376 _sim
->set_inc_mode_bad();
378 _sim
->restore_voltages();
379 CARD_LIST::card_list
.tr_restore();
382 CARD_LIST::card_list
.tr_begin();
385 set_step_cause(scUSER
);
387 _ever_converged
= false;
388 for (int ii
= 0; ii
< _n_sweeps
; ++ii
) {
390 }else if (_zap
[ii
]->is_constant()) {
391 CARD_LIST::card_list
.q_hack(_zap
[ii
]);
395 sweep_recursive(_n_sweeps
-1);
396 _sim
->pop_voltages();
397 _sim
->_has_op
= _sim
->_mode
;
398 _sim
->keep_voltages();
400 /*--------------------------------------------------------------------------*/
401 static double mul(double a
,double b
){ return a
*b
; }
402 static double sub(double a
,double b
){ return a
-b
; }
403 static double div(double a
,double b
){ return a
/b
; }
404 static double add(double a
,double b
){ return a
+b
; }
405 static bool ge(double a
,double b
){ return a
>=b
; }
406 static bool le(double a
,double b
){ return a
<=b
; }
407 /*--------------------------------------------------------------------------*/
408 void DCOP::sweep_recursive(int Nest
)
410 for (int ii
= int(_sim
->_lu
.size()); ii
>= 0; --ii
) {
411 assert(!_sim
->_nstat
[ii
].discont());
413 static unsigned extra_steps
;
415 assert(Nest
< DCNEST
);
417 OPT::ITL itl
= OPT::DCBIAS
;
421 double (*step
)(double a
, double b
) = add
;
422 double (*back
)(double a
, double b
) = sub
;
423 if (!_linswp
[Nest
]) {
427 if (_reverse
[Nest
]) {
428 std::swap(step
,back
);
431 trace3("DCOP::sweep_recursive", Nest
, *(_sweepval
[Nest
]), _step
[Nest
]);
435 // _sim->_temp_c = temp_c_in;
437 sweep_recursive(Nest
-1);
438 if(_converged
|| !_ever_converged
){
440 _sim
->pop_voltages();
441 _sim
->restore_voltages();
443 _sim
->_has_op
= _sim
->_mode
;
444 _sim
->keep_voltages(true); // push values for sweep. hmmm
447 trace2("not converged II", Nest
, *(_sweepval
[Nest
]));
452 if (_sim
->command_is_op()) {
453 CARD_LIST::card_list
.precalc_last();
456 for (int ii
= 0; ii
< _n_sweeps
; ++ii
) {
461 _converged
= solve_with_homotopy(itl
,_trace
);
462 _ever_converged
|= _converged
;
463 ++::status
.hidden_steps
;
465 (_trace
>= tREJECTED
)
467 || (_converged
&& ((_trace
>= tALLTIME
)
468 || (step_cause() == scUSER
)));
469 trace4("outdata?", _trace
, _converged
, printnow
, _ever_converged
);
472 if(extra_steps
> 100){ untested();
473 throw Exception("dc stepping did not succeed");
477 trace3("DCOP::sweep_recursive noconv", Nest
, *_sweepval
[Nest
], _ever_converged
);
478 error(bWARNING
, "did not converge\n");
479 _sim
->restore_voltages();
480 }else if (!_ever_converged
) { incomplete();
482 _sim
->keep_voltages(true); // push values for sweep. hmmm
485 ::status
.accept
.start();
487 CARD_LIST::card_list
.tr_accept();
488 ::status
.accept
.stop();
489 _sim
->keep_voltages();
491 _sim
->_has_op
= _sim
->_mode
;
492 _sim
->keep_voltages(true); // push values for sweep. hmmm
497 fixzero(_sweepval
[Nest
], _step
[Nest
]); // hack
499 outdata(*_sweepval
[Nest
]);
501 outdata(- *_sweepval
[Nest
]);
503 ::status
.hidden_steps
= 0;
507 if (!_converged
&& firstloop
&& Nest
) { untested();
508 trace1("didnt converge in first", Nest
);
511 // ::status.accept.start();
512 // _sim->set_limit();
513 // CARD_LIST::card_list.tr_accept();
514 // ::status.accept.stop();
515 // _sim->_has_op = _sim->_mode;
516 // _sim->keep_voltages();
517 // outdata(*_sweepval[Nest]); ??
523 if(step_cause() != scUSER
){
524 trace1("firststep nouser", Nest
);
528 // UGLY. next may have changed _reverse[Nest]
531 if (!_linswp
[Nest
]) {
535 if (_reverse
[Nest
]) {
536 std::swap(step
,back
);
541 if ((firstloop
|| _converged
) && step_cause() == scUSER
) {
542 _val_by_user_request
[Nest
] = step(_val_by_user_request
[Nest
], _step
[Nest
]);
543 trace2("ordered next step loop", Nest
, _val_by_user_request
[Nest
]);
546 } while (next(Nest
));
548 // _sim->pop_voltages();
550 /*--------------------------------------------------------------------------*/
551 void DCOP::first(int Nest
)
554 assert(Nest
< DCNEST
);
557 assert(_sweepval
[Nest
]);
559 if (ELEMENT
* c
= dynamic_cast<ELEMENT
*>(_zap
[Nest
])) {
560 c
->set_constant(false);
561 // because of extra precalc_last could set constant to true
562 // will be obsolete, once pointer hack is fixed
564 // not needed if not sweeping an element
567 *_sweepval
[Nest
] = _start
[Nest
];
570 // set_step_cause(scUSER);
573 assert(step_cause());
575 _val_by_user_request
[Nest
] = _start
[Nest
];
576 _sweepdamp
[Nest
] = 1;
577 if (ELEMENT
* c
= dynamic_cast<ELEMENT
*>(_zap
[Nest
])) {
578 // because of extra precalc_last
579 // obsolete, once pointer hack is fixed
580 c
->set_constant(false);
581 trace1("zapq", _zap
[Nest
]->long_label());
582 // CARD_LIST::card_list.q_hack(_zap[Nest]);
585 _reverse
[Nest
] = false;
586 if (_reverse_in
[Nest
]) {
587 _converged
= true; //?
588 double (*step
)(double a
, double b
) = add
;
589 double (*back
)(double a
, double b
) = sub
;
590 if (!_linswp
[Nest
]) {
595 _val_by_user_request
[Nest
] = step(*(_sweepval
[Nest
]), _step
[Nest
]);
597 _val_by_user_request
[Nest
] = back(*(_sweepval
[Nest
]), _step
[Nest
]);
598 _reverse
[Nest
] = true;
603 _sim
->_phase
= p_INIT_DC
;
605 /*--------------------------------------------------------------------------*/
606 bool DCOP::next(int Nest
)
608 double sweepval
= NOT_VALID
;
609 trace2("next", Nest
, *(_sweepval
[Nest
]));
612 double (*step
)(double a
, double b
) = add
;
613 double (*back
)(double a
, double b
) = sub
;
614 bool (*further
)(double a
, double b
) = ge
;
615 double (*scale
)(double a
, double b
) = mul
;
616 if (!_linswp
[Nest
]) {
623 double fudge
= scale(_step
[Nest
], 1e-6);
624 if (_reverse
[Nest
]) {
625 trace2("next, reverse", *_sweepval
[Nest
], _val_by_user_request
[Nest
]);
626 std::swap(step
,back
);
627 if (_step
[Nest
] < 0) {
632 fudge
= scale(_step
[Nest
], -1e-6);
633 } else if (_step
[Nest
] < 0) {
636 if (_step
[Nest
] == nothing
) {
638 set_step_cause(scZERO
);
639 }else if (!_converged
&& _ever_converged
) {
640 if (_sweepdamp
[Nest
]<OPT::dtmin
) { untested();
641 throw Exception("step too small (does not converge)");
644 _sweepdamp
[Nest
] /= 2.;
645 trace2("reducing step by", _sweepdamp
[Nest
], Nest
);
646 sweepval
= back(*_sweepval
[Nest
], scale(_step
[Nest
],_sweepdamp
[Nest
]));
647 trace2("next at", sweepval
, _val_by_user_request
[Nest
]);
649 set_step_cause(scREJECT
);
651 if (_sweepdamp
[Nest
] != 1) {
652 trace3("recovered from", _sweepdamp
[Nest
], Nest
, sweepval
);
653 set_step_cause(scTE
);
656 _sweepdamp
[Nest
] *= 1.4;
657 _sweepdamp
[Nest
] = std::min(_sweepdamp
[Nest
],1.);
658 sweepval
= step(*_sweepval
[Nest
], scale(_step
[Nest
],_sweepdamp
[Nest
]));
659 fixzero(&sweepval
, _step
[Nest
]);
660 ok
= in_order(back(_start
[Nest
],fudge
), sweepval
, step(_stop
[Nest
],fudge
));
662 }else if (!_reverse
[Nest
] && !ok
&& _loop
[Nest
]) {
663 _reverse
[Nest
] = true;
664 if (_step
[Nest
] < 0) {
669 fudge
= scale(_step
[Nest
], -.1);
670 sweepval
= back(*_sweepval
[Nest
], _step
[Nest
]);
671 std::swap(step
,back
);
672 ok
= in_order(back(_start
[Nest
],fudge
), sweepval
, step(_stop
[Nest
],fudge
));
674 trace2("BUG?", sweepval
, *_sweepval
[Nest
]);
675 _val_by_user_request
[Nest
] = sweepval
; // BUG: here?
676 }else if(_reverse_in
[Nest
]){
677 // hmm maybe _reverse_in && !_reverse?
678 trace2("reverse end?", sweepval
, *_sweepval
[Nest
]);
679 *_sweepval
[Nest
] = sweepval
;
684 double v
= _val_by_user_request
[Nest
];
686 }else if (further(step(sweepval
, scale(_step
[Nest
],1e-6) ), v
)) {
687 trace5("userstep at", v
, sweepval
, ok
, _reverse
[Nest
], *_sweepval
[Nest
]);
688 set_step_cause(scUSER
); // here?!
693 _sim
->_phase
= p_DC_SWEEP
;
694 // *(_sweepval[Nest]) = sweepval; // ouch.
696 assert(sweepval
!= NOT_VALID
);
697 *(_sweepval
[Nest
]) = sweepval
;
700 trace3("not ok at", v
, sweepval
, *(_sweepval
[Nest
]));
701 //assert(sweepval == NOT_VALID);
705 /*--------------------------------------------------------------------------*/
708 static DISPATCHER
<CMD
>::INSTALL
d2(&command_dispatcher
, "dc", &p2
);
709 static DISPATCHER
<CMD
>::INSTALL
d4(&command_dispatcher
, "op", &p4
);
711 /*--------------------------------------------------------------------------*/
712 /*--------------------------------------------------------------------------*/
713 // vim:ts=8:sw=2:noet: