2 * Copyright (C) 2001 Albert Davis
3 * (C) 2014 Felix Salfelder
4 * Author: Albert Davis <aldavis@gnu.org>
6 * This file is part of "Gnucap", the Gnu Circuit Analysis Package
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3, or (at your option)
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 *------------------------------------------------------------------
23 * inductor base class used by various inductor implementations
27 namespace INDUCTANCE
{
29 class DEV_INDUCTANCE
: public STORAGE
{
31 explicit DEV_INDUCTANCE(const DEV_INDUCTANCE
& p
)
32 :STORAGE(p
), _c_model(p
._c_model
) {}
34 explicit DEV_INDUCTANCE()
35 :STORAGE(), _c_model(false) {}
36 public: // override virtual
37 char id_letter()const {return 'L';}
38 std::string
value_name()const {return "l";}
39 std::string
dev_type()const {return "inductor";}
40 uint_t
max_nodes()const {return 2;}
41 uint_t
min_nodes()const {return 2;}
42 uint_t
net_nodes()const {return 2;}
43 uint_t
int_nodes()const {return (!_c_model
) ? 0 : 1;}
44 uint_t
matrix_nodes()const {return net_nodes() + int_nodes();}
46 bool has_inode()const {return _c_model
;}
47 bool has_iv_probe()const {return true;}
48 bool use_obsolete_callback_parse()const {return true;}
49 CARD
* clone()const {return new DEV_INDUCTANCE(*this);}
51 void tr_iwant_matrix();
58 double tr_involts()const {return tr_outvolts();}
59 double tr_input()const;
60 double tr_involts_limited()const {return tr_outvolts_limited();}
61 double tr_input_limited()const;
62 double tr_amps()const;
63 double tr_probe_num(const std::string
&)const;
64 void ac_iwant_matrix();
65 void ac_begin() {_loss1
= _loss0
= ((!_c_model
) ? 0. : 1.); _ev
= _y
[0].f1
;}
68 COMPLEX
ac_involts()const {return ac_outvolts();}
69 COMPLEX
ac_amps()const;
71 void set_param_by_name(string Name
, string Value
);
73 std::string
port_name(uint_t i
)const {itested();
74 assert(i
!= INVALID_NODE
);
76 static std::string names
[] = {"p", "n"};
83 /*--------------------------------------------------------------------------*/
84 inline void DEV_INDUCTANCE::expand()
87 if (_sim
->is_first_expand()) {
89 _n
[IN1
].set_to_ground(this);
91 _n
[IN1
].new_model_node("." + long_label() + ".i", this);
96 /*--------------------------------------------------------------------------*/
97 inline double DEV_INDUCTANCE::tr_probe_num(const std::string
& x
)const
99 if (Umatch(x
, "flux ")) {
101 }else if (Umatch(x
, "ind{uctance} |l ")) { itested();
103 }else if (Umatch(x
, "dldt ")) { untested();
104 return (_y
[0].f1
- _y
[1].f1
) / _dt
;
105 }else if (Umatch(x
, "dl ")) { untested();
106 return (_y
[0].f1
- _y
[1].f1
);
107 }else if (Umatch(x
, "dfdt ")) { untested();
108 return (_y
[0].f0
- _y
[1].f0
) / _dt
;
109 }else if (Umatch(x
, "inode ")) {
111 }else if (Umatch(x
, "dflux ")) { untested();
112 return (_y
[0].f0
- _y
[1].f0
);
114 return STORAGE::tr_probe_num(x
);
117 /*--------------------------------------------------------------------------*/
118 void DEV_INDUCTANCE::tr_iwant_matrix()
121 tr_iwant_matrix_passive();
123 assert(matrix_nodes() == 3);
125 assert(_n
[OUT1
].m_() != (uint_t
) INVALID_NODE
);
126 assert(_n
[OUT2
].m_() != (uint_t
) INVALID_NODE
);
127 assert(_n
[IN1
].m_() != (uint_t
) INVALID_NODE
);
129 _sim
->_aa
.iwant(_n
[OUT1
].m_(),_n
[IN1
].m_());
130 _sim
->_aa
.iwant(_n
[OUT2
].m_(),_n
[IN1
].m_());
132 _sim
->_lu
.iwant(_n
[OUT1
].m_(),_n
[IN1
].m_());
133 _sim
->_lu
.iwant(_n
[OUT2
].m_(),_n
[IN1
].m_());
136 /*--------------------------------------------------------------------------*/
137 void DEV_INDUCTANCE::tr_begin()
139 trace6("DEV_INDUCTANCE::tr_begin", _i
[0].f0
, long_label(), _sim
->_cont
, tr_involts(), _i
[0].x
, tr_outvolts());
140 if (0&& _sim
->_cont
) {
141 _i
[0].f0
= tr_involts();
144 if (0 && _sim
->_cont
) {
145 // i.x = amps, i.f0 = volts, i.f1 = ohms
146 // BUG: move to tr accept (if !uic?)
147 _i
[0].f0
= tr_involts();
148 assert(_i
[0].x
!= 0);
149 _i
[0].f1
= tr_involts() / _i
[0].x
;
151 _m0
.c0
= _i
[0].x
; // amps. iof?
152 _m0
.c1
= 0; // _i[0].x / tr_involts();
155 _loss1
= _loss0
= 1.;
157 _loss1
= _loss0
= 0.;
160 /*--------------------------------------------------------------------------*/
161 void DEV_INDUCTANCE::tr_restore()
164 if(_sim
->_time0
>= _time
[0]){
166 STORAGE::tr_restore();
167 }else if(!_c_model
){ incomplete();
169 STORAGE::tr_restore();
170 }else{ // _sim->_time0 < _time[0]
171 // recover from _freezetime ...
173 _y
[0].x
= tr_input();
174 // _i[0].x = tr_input();
175 // _i[0].f0 = 0.; // amps
176 // _i[0].f1 = 1./OPT::shortckt; // mhos
178 if (using_tr_eval()) {
179 assert(_y
[0].f1
== value());
182 // BUG: compute _y[0].f1 from OP?
183 _y
[0].f0
= _y
[0].x
* _y
[0].f1
; // charge
186 _method_a
= mINVALID
;
187 // _i[0] = differentiate(_y, _i, _time, _method_a);
188 double G
= 1./OPT::shortckt
;
189 _i
[0] = FPOLY1( CPOLY1( 0., -_y
[0].x
* G
, G
) );
191 trace3("DEV_CAPACITANCE::tr_restore from freeze", _y
[0], _i
[0], _i
[1]);
192 STORAGE::tr_restore();
193 _time
[1] = 0.; /// hmm hack.
196 /*--------------------------------------------------------------------------*/
197 void DEV_INDUCTANCE::tr_accept()
200 _i
[0].f0
= tr_involts();
202 STORAGE::tr_accept();
204 _i
[0].f0
= tr_involts();
205 // assert(_i[0].x != 0);
206 _i
[0].f1
= tr_involts() / (_i
[0].x
+1e-20);
208 _m0
.c0
= _i
[0].x
; // amps. iof?
209 _m0
.c1
= 0; // _i[0].x / tr_involts();
211 double idot
= - tr_involts() / (_y
[0].f1
+ 1e-20);
212 _m0
.c0
= idot
* _y
[0].f1
;
213 trace2("coil", _y
[0].f1
, _loss0
);
215 set_converged(false);
218 /*--------------------------------------------------------------------------*/
219 // quick hack. dont know how to do this in general.
220 inline void DEV_INDUCTANCE::set_param_by_name(string Name
, string Value
)
222 trace2("DEV_INDUCTANCE::set_param_by_name", Name
, Value
);
223 if (Umatch(Name
, value_name()) && !has_common()) {
225 } else if (Umatch(Name
, value_name())) { untested();
226 ELEMENT::set_param_by_name(value_name(), value());
227 } else if (has_common()) { untested();
228 ELEMENT::set_param_by_name(Name
, Value
);
230 COMMON_COMPONENT
* c
= bm_dispatcher
["eval_bm_value"]->clone();
231 c
->set_param_by_name("=",value());
232 c
->set_param_by_name(Name
, Value
);
235 assert(has_common());
237 if (const EVAL_BM_ACTION_BASE
* x
= dynamic_cast<const EVAL_BM_ACTION_BASE
*>(common())) {
239 trace2("DEV_INDUCTANCE::set_param_by_name", long_label(), x
->_ic
);
242 /*--------------------------------------------------------------------------*/
243 bool DEV_INDUCTANCE::has_ic() const
245 if (const EVAL_BM_ACTION_BASE
* x
= dynamic_cast<const EVAL_BM_ACTION_BASE
*>(common())) {
246 if (x
->_ic
!= NOT_INPUT
) {
249 trace2("has_ic", long_label(), x
->_ic
);
256 /*--------------------------------------------------------------------------*/
257 bool DEV_INDUCTANCE::do_tr()
259 if (using_tr_eval()) {
260 _y
[0].x
= tr_input_limited(); // _m0.c0 + _m0.c1 * x;
262 if ((!_c_model
) && (_y
[0].f1
== 0.)) {untested();
263 error(bDANGER
, long_label() + ": short circuit, L = 0\n");
264 _y
[0].f1
= OPT::shortckt
;
265 set_converged(conv_check());
269 _y
[0].x
= tr_input(); // _m0.c0 + _m0.c1 * x;
270 assert(_y
[0].f1
== value());
271 _y
[0].f0
= _y
[0].x
* _y
[0].f1
;
277 // i is really voltage ..
278 if (_sim
->uic_now() && has_ic()) {
279 trace4("imitating cs", _y
[0].x
, value(), _y
[0].f1
, tr_involts());
280 // imitate current src...
281 // i.x = amps, i.f0 = volts, i.f1 = ohms
292 trace4("q", _y
[1].x
, _y
[1].f0
, _y
[1].f1
, _sim
->_time0
);
293 trace4("q", _y
[0].x
, _y
[0].f0
, _y
[0].f1
, _sim
->_time0
);
294 trace3("b4", _i
[0].x
, _i
[0].f0
, _i
[0].f1
);
295 _i
[0] = differentiate(_y
, _i
, _time
, _method_a
);
296 trace3("d ", _i
[0].x
, _i
[0].f0
, _i
[0].f1
);
301 _m0
.c1
= 1 / ((_i
[0].c1()==0) ? OPT::shortckt
: _i
[0].c1());
302 if (_sim
->uic_now() && has_ic()) {
304 _y
[0].f0
= _y
[0].x
* _y
[0].f1
;
307 _m0
.c0
= -_i
[0].c0() * _m0
.c1
;
310 if (_sim
->uic_now() && has_ic()) {
311 // i.x = amps, i.f0 = volts, i.f1 = ohms
312 _i
[0].f0
= tr_involts();
313 _i
[0].f1
= 1. / OPT::shortckt
;
314 // m.x = volts, m.c0 = amps, acg = m.c1 = mhos
315 _m0
.x
= 0; //_y[0].x;
316 _m0
.c1
= -_loss0
* _loss0
* _i
[0].c1();
317 _m0
.c0
= _loss0
* _loss0
* _i
[0].c0(); // + tr_involts(); // hmm
320 _m0
.c1
= -_loss0
* _loss0
* _i
[0].c1();
321 _m0
.c0
= _loss0
* _loss0
* _i
[0].c0();
325 trace7("L::do_tr", _i
[0].c0(), _i
[0].c1(), _y
[0].f1
, tr_amps(), tr_involts(), _m0
.c0
, _m0
.c1
);
328 /*--------------------------------------------------------------------------*/
329 void DEV_INDUCTANCE::tr_load()
334 trace6("DEV_INDUCTANCE::tr_load", _m0
.c1
, _m1
.c1
, _m0
.c0
, _m1
.c0
, _loss0
, _loss1
);
335 tr_load_inode(); // load loss.
336 tr_load_diagonal_point(_n
[IN1
], &_m0
.c1
, &_m1
.c1
);
337 tr_load_source_point(_n
[IN1
], &_m0
.c0
, &_m1
.c0
); // load i
340 /*--------------------------------------------------------------------------*/
341 void DEV_INDUCTANCE::tr_unload()
343 _loss0
= _m0
.c0
= _m0
.c1
= 0.;
344 _sim
->mark_inc_mode_bad();
347 /*--------------------------------------------------------------------------*/
348 double DEV_INDUCTANCE::tr_input()const
351 return _m0
.c0
+ _m0
.c1
* tr_involts();
356 /*--------------------------------------------------------------------------*/
357 double DEV_INDUCTANCE::tr_input_limited()const
360 return _m0
.c0
+ _m0
.c1
* tr_involts_limited();
365 /*--------------------------------------------------------------------------*/
366 double DEV_INDUCTANCE::tr_amps()const
370 return fixzero((_m0
.c1
* tr_involts() + _m0
.c0
), _m0
.c0
);
372 return _loss0
* _n
[IN1
].v0();
375 /*--------------------------------------------------------------------------*/
376 void DEV_INDUCTANCE::ac_iwant_matrix()
379 ac_iwant_matrix_passive();
381 assert(matrix_nodes() == 3);
383 assert(_n
[OUT1
].m_() != INVALID_NODE
);
384 assert(_n
[OUT2
].m_() != INVALID_NODE
);
385 assert(_n
[IN1
].m_() != INVALID_NODE
);
387 _sim
->_acx
.iwant(_n
[OUT1
].m_(),_n
[IN1
].m_());
388 _sim
->_acx
.iwant(_n
[OUT2
].m_(),_n
[IN1
].m_());
391 /*--------------------------------------------------------------------------*/
392 void DEV_INDUCTANCE::do_ac()
394 if (using_ac_eval()) {
397 assert(_ev
== _y
[0].f1
);
399 // assert(dynamic_cast<INDUCTANCE::DEV_MUTUAL_L*>(this) || has_tr_eval() || _ev == double(value()));
402 if ((COMPLEX
)_ev
* _sim
->_jomega
== 0.) {untested();
403 _acg
= 1. / OPT::shortckt
;
405 _acg
= 1. / ((COMPLEX
)_ev
* _sim
->_jomega
);
408 _acg
= -(double)_loss0
*(double) _loss0
* (COMPLEX
)_ev
* _sim
->_jomega
;
411 /*--------------------------------------------------------------------------*/
412 void DEV_INDUCTANCE::ac_load()
417 ac_load_inode(); // 4x \pm loss.
418 ac_load_diagonal_point(_n
[IN1
], _acg
);
421 /*--------------------------------------------------------------------------*/
422 COMPLEX
DEV_INDUCTANCE::ac_amps()const
425 return (ac_involts() * _acg
);
427 return ( (double)_loss0
* (COMPLEX
)(_n
[IN1
].vac()) );
430 /*--------------------------------------------------------------------------*/
432 /*--------------------------------------------------------------------------*/
433 // vim:ts=8:sw=2:noet: