1 /*$Id: e_compon.cc,v 26.137 2010/04/10 02:37:33 al Exp $ -*- C++ -*-
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 *------------------------------------------------------------------
22 * Base class for elements of a circuit
24 //testing=script,noswitch 2009.07.13
28 /*--------------------------------------------------------------------------*/
29 COMMON_COMPONENT::COMMON_COMPONENT(const COMMON_COMPONENT
& p
)
35 _modelname(p
._modelname
),
40 /*--------------------------------------------------------------------------*/
41 COMMON_COMPONENT::COMMON_COMPONENT(int c
)
52 /*--------------------------------------------------------------------------*/
53 COMMON_COMPONENT::~COMMON_COMPONENT()
55 trace1("common,destruct", _attach_count
);
56 assert(_attach_count
== 0 || _attach_count
== CC_STATIC
);
58 /*--------------------------------------------------------------------------*/
59 void COMMON_COMPONENT::attach_common(COMMON_COMPONENT
*c
, COMMON_COMPONENT
**to
)
63 // The new and old are the same object. Do nothing.
64 }else if (!c
) {untested();
65 // There is no new common. probably a simple element
68 // No old one, but have a new one.
70 trace1("++1", c
->_attach_count
);
72 }else if (*c
!= **to
) {
73 // They are different, usually by edit.
76 trace1("++2", c
->_attach_count
);
78 }else if (c
->_attach_count
== 0) {
79 // The new and old are identical.
81 // The new one is not used anywhere, so throw it away.
82 trace1("delete", c
->_attach_count
);
85 // The new and old are identical.
87 // The new one is also used somewhere else, so keep it.
90 /*--------------------------------------------------------------------------*/
91 void COMMON_COMPONENT::detach_common(COMMON_COMPONENT
** from
)
95 assert((**from
)._attach_count
> 0);
96 --((**from
)._attach_count
);
97 trace1("--", (**from
)._attach_count
);
98 if ((**from
)._attach_count
== 0) {
99 trace1("delete", (**from
)._attach_count
);
102 trace1("nodelete", (**from
)._attach_count
);
108 /*--------------------------------------------------------------------------*/
109 void COMMON_COMPONENT::attach_model(const COMPONENT
* d
)const
112 _model
= d
->find_model(modelname());
115 /*--------------------------------------------------------------------------*/
116 void COMMON_COMPONENT::parse_modelname(CS
& cmd
)
118 set_modelname(cmd
.ctos(TOKENTERM
));
120 /*--------------------------------------------------------------------------*/
121 // called only by COMMON_COMPONENT::parse_obsolete
122 bool COMMON_COMPONENT::parse_param_list(CS
& cmd
)
124 unsigned start
= cmd
.cursor();
125 unsigned here
= cmd
.cursor();
127 parse_params_obsolete_callback(cmd
); //BUG//callback
128 }while (cmd
.more() && !cmd
.stuck(&here
));
129 return cmd
.gotit(start
);
131 /*--------------------------------------------------------------------------*/
132 void COMMON_COMPONENT::parse_common_obsolete_callback(CS
& cmd
) //used
134 if (cmd
.skip1b('(')) {
135 // start with a paren
136 unsigned start
= cmd
.cursor();
137 parse_param_list(cmd
);
138 if (cmd
.gotit(start
)) { // ( params ( ....
139 // named args before num list
140 if (cmd
.skip1b('(')) { // ( params ( list ) params )
142 if (!cmd
.skip1b(')')) {untested();
143 cmd
.warn(bWARNING
, "need )");
146 }else{ // ( params list params )
147 parse_numlist(cmd
); //BUG//
149 parse_param_list(cmd
);
150 if (!cmd
.skip1b(')')) {untested();
151 cmd
.warn(bWARNING
, "need )");
155 // no named args before num list
156 // but there's a paren
157 // not sure whether it belongs to all args or to num list
158 if (cmd
.skip1b('(')) { // ( ( list ) params )
161 if (!cmd
.skip1b(')')) {untested();
162 cmd
.warn(bWARNING
, "need )");
165 parse_param_list(cmd
);
166 if (!cmd
.skip1b(')')) {untested();
167 cmd
.warn(bWARNING
, "need )");
173 if (cmd
.skip1b(')')) { // ( list ) params
174 // assume it belongs to num list
175 // and named params follow
176 parse_param_list(cmd
);
177 }else{ // ( list params )
178 // assume it belongs to all args
179 parse_param_list(cmd
);
180 if (!cmd
.skip1b(')')) {
181 cmd
.warn(bWARNING
, "need )");
188 // does not start with a paren
189 unsigned start
= cmd
.cursor();
190 parse_param_list(cmd
);
191 if (cmd
.gotit(start
)) {
192 if (cmd
.skip1b('(')) { // params ( list ) params
194 if (!cmd
.skip1b(')')) {untested();
195 cmd
.warn(bWARNING
, "need )");
198 }else if (!(cmd
.is_alpha())) { // params list params
200 }else{ // params (only)
202 }else{ // list params
203 assert(!(cmd
.skip1b('(')));
206 parse_param_list(cmd
);
207 if (cmd
.skip1b(')')) {
208 cmd
.warn(bWARNING
, start
, "need (");
213 /*--------------------------------------------------------------------------*/
214 void COMMON_COMPONENT::print_common_obsolete_callback(OMSTREAM
& o
, LANGUAGE
* lang
)const
217 print_pair(o
, lang
, "tnom", _tnom_c
, _tnom_c
.has_hard_value());
218 print_pair(o
, lang
, "dtemp",_dtemp
, _dtemp
.has_hard_value());
219 print_pair(o
, lang
, "temp", _temp_c
, _temp_c
.has_hard_value());
220 print_pair(o
, lang
, "m", _mfactor
, _mfactor
.has_hard_value());
222 /*--------------------------------------------------------------------------*/
223 void COMMON_COMPONENT::set_param_by_index(int i
, std::string
& Value
, int Offset
)
226 case 0: _tnom_c
= Value
; break;
227 case 1: _dtemp
= Value
; break;
228 case 2: _temp_c
= Value
; break;
229 case 3: _mfactor
= Value
; break;
230 default: throw Exception_Too_Many(i
, 3, Offset
); break;
233 /*--------------------------------------------------------------------------*/
234 bool COMMON_COMPONENT::param_is_printable(int i
)const
237 case 0: return _tnom_c
.has_hard_value();
238 case 1: return _dtemp
.has_hard_value();
239 case 2: return _temp_c
.has_hard_value();
240 case 3: return _mfactor
.has_hard_value();
241 default: return false;
244 /*--------------------------------------------------------------------------*/
245 std::string
COMMON_COMPONENT::param_name(int i
)const
248 case 0: return "tnom";
249 case 1: return "dtemp";
250 case 2: return "temp";
255 /*--------------------------------------------------------------------------*/
256 std::string
COMMON_COMPONENT::param_name(int i
, int j
)const
258 return (j
==0) ? param_name(i
) : "";
260 /*--------------------------------------------------------------------------*/
261 std::string
COMMON_COMPONENT::param_value(int i
)const
264 case 0: return _tnom_c
.string();
265 case 1: return _dtemp
.string();
266 case 2: return _temp_c
.string();
267 case 3: return _mfactor
.string();
271 /*--------------------------------------------------------------------------*/
272 void COMMON_COMPONENT::precalc_first(const CARD_LIST
* Scope
)
275 _tnom_c
.e_val(OPT::tnom_c
, Scope
);
276 _dtemp
.e_val(0., Scope
);
277 _temp_c
.e_val(CKT_BASE::_sim
->_temp_c
+ _dtemp
, Scope
);
278 _mfactor
.e_val(1, Scope
);
279 _value
.e_val(0, Scope
);
281 /*--------------------------------------------------------------------------*/
282 void COMMON_COMPONENT::tr_eval(ELEMENT
*x
)const
287 /*--------------------------------------------------------------------------*/
288 void COMMON_COMPONENT::ac_eval(ELEMENT
*x
)const
293 /*--------------------------------------------------------------------------*/
294 bool COMMON_COMPONENT::operator==(const COMMON_COMPONENT
& x
)const
296 return (_modelname
== x
._modelname
297 && _model
== x
._model
298 && _tnom_c
== x
._tnom_c
299 && _dtemp
== x
._dtemp
300 && _temp_c
== x
._temp_c
301 && _mfactor
== x
._mfactor
302 && _value
== x
._value
);
304 /*--------------------------------------------------------------------------*/
305 void COMMON_COMPONENT::set_param_by_name(std::string Name
, std::string Value
)
307 if (has_parse_params_obsolete_callback()) {untested();
308 std::string
args(Name
+ "=" + Value
);
309 CS
cmd(CS::_STRING
, args
); //obsolete_callback
310 bool ok
= parse_params_obsolete_callback(cmd
); //BUG//callback
311 if (!ok
) {untested();
312 throw Exception_No_Match(Name
);
316 //BUG// ugly linear search
317 for (int i
= param_count() - 1; i
>= 0; --i
) {
318 for (int j
= 0; param_name(i
,j
) != ""; ++j
) {
319 if (Umatch(Name
, param_name(i
,j
) + ' ')) {
320 set_param_by_index(i
, Value
, 0/*offset*/);
328 throw Exception_No_Match(Name
);
331 /*--------------------------------------------------------------------------*/
332 //BUG// This is a kluge for the spice_wrapper, to disable virtual functions.
333 // It is called during expansion only.
335 void COMMON_COMPONENT::Set_param_by_name(std::string Name
, std::string Value
)
337 assert(!has_parse_params_obsolete_callback());
339 //BUG// ugly linear search
340 for (int i
= COMMON_COMPONENT::param_count() - 1; i
>= 0; --i
) {
341 for (int j
= 0; COMMON_COMPONENT::param_name(i
,j
) != ""; ++j
) {
342 if (Umatch(Name
, COMMON_COMPONENT::param_name(i
,j
) + ' ')) {
343 COMMON_COMPONENT::set_param_by_index(i
, Value
, 0/*offset*/);
350 throw Exception_No_Match(Name
);
352 /*--------------------------------------------------------------------------*/
353 bool COMMON_COMPONENT::parse_numlist(CS
&)
357 /*--------------------------------------------------------------------------*/
358 bool COMMON_COMPONENT::parse_params_obsolete_callback(CS
& cmd
)
361 || Get(cmd
, "tnom", &_tnom_c
)
362 || Get(cmd
, "dtemp", &_dtemp
)
363 || Get(cmd
, "temp", &_temp_c
)
364 || Get(cmd
, "m", &_mfactor
)
365 || Get(cmd
, "mfactor",&_mfactor
)
368 /*--------------------------------------------------------------------------*/
369 /*--------------------------------------------------------------------------*/
370 COMPONENT::COMPONENT()
375 _mfactor_fixed(NOT_VALID
),
385 /*--------------------------------------------------------------------------*/
386 COMPONENT::COMPONENT(const COMPONENT
& p
)
390 _mfactor(p
._mfactor
),
391 _mfactor_fixed(p
._mfactor_fixed
),
392 _converged(p
._converged
),
400 attach_common(p
._common
);
401 assert(_common
== p
._common
);
403 /*--------------------------------------------------------------------------*/
404 COMPONENT::~COMPONENT()
412 /*--------------------------------------------------------------------------*/
413 bool COMPONENT::node_is_grounded(int i
)const
417 assert(i
< net_nodes());
418 return _n
[i
].is_grounded();
420 /*--------------------------------------------------------------------------*/
421 bool COMPONENT::node_is_connected(int i
)const
425 assert(i
< net_nodes());
426 return _n
[i
].is_connected();
428 /*--------------------------------------------------------------------------*/
429 void COMPONENT::set_port_by_name(std::string
& int_name
, std::string
& ext_name
)
431 for (int i
=0; i
<max_nodes(); ++i
) {itested();
432 if (int_name
== port_name(i
)) {itested();
433 set_port_by_index(i
, ext_name
);
439 throw Exception_No_Match(int_name
);
441 /*--------------------------------------------------------------------------*/
442 void COMPONENT::set_port_by_index(int num
, std::string
& ext_name
)
444 if (num
<= max_nodes()) {
445 _n
[num
].new_node(ext_name
, this);
446 if (num
+1 > _net_nodes
) {
447 // make the list bigger
450 // it's already big enough, probably assigning out of order
453 throw Exception_Too_Many(num
, max_nodes(), 0/*offset*/);
456 /*--------------------------------------------------------------------------*/
457 void COMPONENT::set_port_to_ground(int num
)
459 if (num
<= max_nodes()) {untested();
460 _n
[num
].set_to_ground(this);
461 if (num
+1 > _net_nodes
) {untested();
466 throw Exception_Too_Many(num
, max_nodes(), 0/*offset*/);
469 /*--------------------------------------------------------------------------*/
470 void COMPONENT::set_dev_type(const std::string
& new_type
)
473 if (new_type
!= dev_type()) {
474 COMMON_COMPONENT
* c
= common()->clone();
476 c
->set_modelname(new_type
);
481 CARD::set_dev_type(new_type
);
484 /*--------------------------------------------------------------------------*/
485 void COMPONENT::print_args_obsolete_callback(OMSTREAM
& o
, LANGUAGE
* lang
)const
488 assert(has_common());
489 common()->print_common_obsolete_callback(o
, lang
);
491 /*--------------------------------------------------------------------------*/
492 void COMPONENT::deflate_common()
495 if (has_common()) {untested();
496 COMMON_COMPONENT
* deflated_common
= mutable_common()->deflate();
497 if (deflated_common
!= common()) {untested();
498 attach_common(deflated_common
);
505 /*--------------------------------------------------------------------------*/
506 void COMPONENT::expand()
510 COMMON_COMPONENT
* new_common
= common()->clone();
511 new_common
->expand(this);
512 COMMON_COMPONENT
* deflated_common
= new_common
->deflate();
513 if (deflated_common
!= common()) {
514 attach_common(deflated_common
);
520 /*--------------------------------------------------------------------------*/
521 void COMPONENT::precalc_first()
523 CARD::precalc_first();
526 mutable_common()->precalc_first(scope());
527 }catch (Exception_Precalc
& e
) {
528 error(bWARNING
, long_label() + ": " + e
.message());
530 _mfactor
= common()->mfactor();
534 _mfactor
.e_val(1, scope());
535 _value
.e_val(0.,scope());
536 trace1(long_label().c_str(), double(_mfactor
));
537 if (const COMPONENT
* o
= prechecked_cast
<const COMPONENT
*>(owner())) {
538 _mfactor_fixed
= o
->mfactor() * _mfactor
;
540 _mfactor_fixed
= _mfactor
;
542 trace1(long_label().c_str(), _mfactor_fixed
);
544 /*--------------------------------------------------------------------------*/
545 void COMPONENT::precalc_last()
547 CARD::precalc_last();
550 mutable_common()->precalc_last(scope());
551 }catch (Exception_Precalc
& e
) {
552 error(bWARNING
, long_label() + ": " + e
.message());
557 /*--------------------------------------------------------------------------*/
558 void COMPONENT::map_nodes()
561 assert(0 <= min_nodes());
562 //assert(min_nodes() <= net_nodes());
563 assert(net_nodes() <= max_nodes());
564 //assert(ext_nodes() + int_nodes() == matrix_nodes());
566 for (int ii
= 0; ii
< ext_nodes()+int_nodes(); ++ii
) {
571 subckt()->map_nodes();
575 /*--------------------------------------------------------------------------*/
576 void COMPONENT::tr_iwant_matrix()
579 assert(matrix_nodes() == 0);
581 subckt()->tr_iwant_matrix();
587 /*--------------------------------------------------------------------------*/
588 void COMPONENT::ac_iwant_matrix()
591 assert(matrix_nodes() == 0);
593 subckt()->ac_iwant_matrix();
599 /*--------------------------------------------------------------------------*/
600 /* set: set parameters, used in model building
602 void COMPONENT::set_parameters(const std::string
& Label
, CARD
*Owner
,
603 COMMON_COMPONENT
*Common
, double Value
,
605 int node_count
, const node_t Nodes
[])
610 attach_common(Common
);
612 assert(node_count
<= net_nodes());
613 notstd::copy_n(Nodes
, node_count
, _n
);
615 /*--------------------------------------------------------------------------*/
616 /* set_slave: force evaluation whenever the owner is evaluated.
618 void COMPONENT::set_slave()
620 mark_always_q_for_eval();
622 subckt()->set_slave();
626 /*--------------------------------------------------------------------------*/
627 void COMPONENT::set_value(double v
, COMMON_COMPONENT
* c
)
636 /*--------------------------------------------------------------------------*/
637 void COMPONENT::set_param_by_name(std::string Name
, std::string Value
)
640 COMMON_COMPONENT
* c
= common()->clone();
642 c
->set_param_by_name(Name
, Value
);
645 CARD::set_param_by_name(Name
, Value
);
648 /*--------------------------------------------------------------------------*/
649 void COMPONENT::set_param_by_index(int i
, std::string
& Value
, int offset
)
651 if (has_common()) {untested();
652 COMMON_COMPONENT
* c
= common()->clone();
654 c
->set_param_by_index(i
, Value
, offset
);
657 switch (COMPONENT::param_count() - 1 - i
) {
658 case 0: _value
= Value
; break;
659 case 1: _mfactor
= Value
; break;
660 default: CARD::set_param_by_index(i
, Value
, offset
);
664 /*--------------------------------------------------------------------------*/
665 bool COMPONENT::param_is_printable(int i
)const
668 return common()->param_is_printable(i
);
670 switch (COMPONENT::param_count() - 1 - i
) {
671 case 0: return value().has_hard_value();
672 case 1: return _mfactor
.has_hard_value();
673 default: return CARD::param_is_printable(i
);
677 /*--------------------------------------------------------------------------*/
678 std::string
COMPONENT::param_name(int i
)const
681 return common()->param_name(i
);
683 switch (COMPONENT::param_count() - 1 - i
) {
684 case 0: return value_name();
686 default: return CARD::param_name(i
);
690 /*--------------------------------------------------------------------------*/
691 std::string
COMPONENT::param_name(int i
, int j
)const
693 if (has_common()) {untested();
694 return common()->param_name(i
,j
);
697 return param_name(i
);
698 }else if (i
>= CARD::param_count()) {untested();
701 return CARD::param_name(i
,j
);
705 /*--------------------------------------------------------------------------*/
706 std::string
COMPONENT::param_value(int i
)const
709 return common()->param_value(i
);
711 switch (COMPONENT::param_count() - 1 - i
) {
712 case 0: return value().string();
713 case 1: return _mfactor
.string();
714 default: return CARD::param_value(i
);
718 /*--------------------------------------------------------------------------*/
719 const std::string
COMPONENT::port_value(int i
)const
723 assert(i
< net_nodes());
724 return _n
[i
].short_label();
726 /*--------------------------------------------------------------------------*/
727 const std::string
COMPONENT::current_port_value(int)const
730 static std::string s
;
733 /*--------------------------------------------------------------------------*/
734 double COMPONENT::tr_probe_num(const std::string
& x
)const
736 CS
cmd(CS::_STRING
, x
);
737 if (cmd
.umatch("v")) {
739 return (nn
> 0 && nn
<= net_nodes()) ? _n
[nn
-1].v0() : NOT_VALID
;
740 }else if (Umatch(x
, "error{time} |next{time} ")) {
741 return (_time_by
._error_estimate
< BIGBIG
) ? _time_by
._error_estimate
: 0;
742 }else if (Umatch(x
, "timef{uture} ")) {
743 return (_time_by
._error_estimate
< _time_by
._event
)
744 ? _time_by
._error_estimate
746 }else if (Umatch(x
, "event{time} ")) {
747 return (_time_by
._event
< BIGBIG
) ? _time_by
._event
: 0;
749 return CARD::tr_probe_num(x
);
752 /*--------------------------------------------------------------------------*/
753 const MODEL_CARD
* COMPONENT::find_model(const std::string
& modelname
)const
755 if (modelname
== "") {
756 throw Exception(long_label() + ": missing args -- need model name");
760 const CARD
* c
= NULL
;
763 for (const CARD
* Scope
= this; Scope
&& !c
; Scope
= Scope
->owner()) {
764 // start here, looking out
766 c
= Scope
->find_in_my_scope(modelname
);
767 }catch (Exception_Cant_Find
& e1
) {
768 // didn't find plain model. try binned models
771 // loop over binned models
772 std::string extended_name
= modelname
+ '.' + to_string(++bin_count
);
774 c
= Scope
->find_in_my_scope(extended_name
);
775 }catch (Exception_Cant_Find
& e2
) {
776 // that's all .. looked at all of them
780 const MODEL_CARD
* m
= dynamic_cast<const MODEL_CARD
*>(c
);
781 if (m
&& m
->is_valid(this)) {
782 //matching name and correct bin
791 if (bin_count
<= 1) {
792 throw Exception_Cant_Find(long_label(), modelname
);
794 throw Exception(long_label() + ": no bins match: " + modelname
);
800 // found something, what is it?
802 const MODEL_CARD
* model
= dynamic_cast<const MODEL_CARD
*>(c
);
803 if (!model
) {untested();
804 throw Exception_Type_Mismatch(long_label(), modelname
, ".model");
805 }else if (!model
->is_valid(this)) {itested();
806 error(bWARNING
, long_label() + ", " + modelname
807 + "\nmodel and device parameters are incompatible, using anyway\n");
814 /*--------------------------------------------------------------------------*/
815 /* q_eval: queue this device for evaluation on the next pass,
816 * with a check against doing it twice.
818 void COMPONENT::q_eval()
820 if(!is_q_for_eval()) {
822 _sim
->_evalq_uc
->push_back(this);
826 /*--------------------------------------------------------------------------*/
827 void COMPONENT::tr_queue_eval()
829 if(tr_needs_eval()) {
834 /*--------------------------------------------------------------------------*/
835 TIME_PAIR
COMPONENT::tr_review()
839 return _common
->tr_review(this);
844 /*--------------------------------------------------------------------------*/
845 void COMPONENT::tr_accept()
848 _common
->tr_accept(this);
852 /*--------------------------------------------------------------------------*/
853 bool COMPONENT::use_obsolete_callback_parse()const
856 return common()->use_obsolete_callback_parse();
861 /*--------------------------------------------------------------------------*/
862 bool COMPONENT::use_obsolete_callback_print()const
865 return common()->use_obsolete_callback_print();
870 /*--------------------------------------------------------------------------*/
871 void COMPONENT::obsolete_move_parameters_from_common(const COMMON_COMPONENT
* dc
)
874 _value
= dc
->value();
875 _mfactor
= dc
->mfactor();
877 /*--------------------------------------------------------------------------*/
878 /* volts_limited: transient voltage, best approximation, with limiting
880 double COMPONENT::volts_limited(const node_t
& n1
, const node_t
& n2
)
882 bool limiting
= false;
886 if (v1
< _sim
->_vmin
) {
889 }else if (v1
> _sim
->_vmax
) {
896 if (v2
< _sim
->_vmin
) {
899 }else if (v2
> _sim
->_vmax
) {
905 _sim
->_limiting
= true;
906 if (OPT::dampstrategy
& dsRANGE
) {
907 _sim
->_fulldamp
= true;
908 error(bTRACE
, "range limit damp\n");
910 if (OPT::picky
<= bTRACE
) {untested();
911 error(bNOERROR
,"node limiting (n1,n2,dif) "
912 "was (%g %g %g) now (%g %g %g)\n",
913 n1
.v0(), n2
.v0(), n1
.v0() - n2
.v0(), v1
, v2
, v1
-v2
);
917 return dn_diff(v1
,v2
);
919 /*--------------------------------------------------------------------------*/
920 /*--------------------------------------------------------------------------*/
921 // vim:ts=8:sw=2:noet: