bump to -rc12
[gnucap-felix.git] / src / d_coil.h
blobc95a369c8a73f11ea12f53ad4a72bf1e17d9f847
1 /* -*- C++ -*-
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)
11 * any later version.
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
21 * 02110-1301, USA.
22 *------------------------------------------------------------------
23 * inductor base class used by various inductor implementations
25 #include "e_storag.h"
27 namespace INDUCTANCE {
29 class DEV_INDUCTANCE : public STORAGE {
30 protected:
31 explicit DEV_INDUCTANCE(const DEV_INDUCTANCE& p)
32 :STORAGE(p), _c_model(p._c_model) {}
33 public:
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);}
50 void expand();
51 void tr_iwant_matrix();
52 void tr_begin();
53 void tr_restore();
54 void tr_accept();
55 bool do_tr();
56 void tr_load();
57 void tr_unload();
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;}
66 void do_ac();
67 void ac_load();
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);
75 assert(i < 2);
76 static std::string names[] = {"p", "n"};
77 return names[i];
79 bool _c_model;
80 private:
81 bool has_ic() const;
83 /*--------------------------------------------------------------------------*/
84 inline void DEV_INDUCTANCE::expand()
86 STORAGE::expand();
87 if (_sim->is_first_expand()) {
88 if (!_c_model) {
89 _n[IN1].set_to_ground(this);
90 }else{
91 _n[IN1].new_model_node("." + long_label() + ".i", this);
93 }else{untested();
96 /*--------------------------------------------------------------------------*/
97 inline double DEV_INDUCTANCE::tr_probe_num(const std::string& x)const
99 if (Umatch(x, "flux ")) {
100 return _y[0].f0;
101 }else if (Umatch(x, "ind{uctance} |l ")) { itested();
102 return _y[0].f1;
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 ")) {
110 return has_inode();
111 }else if (Umatch(x, "dflux ")) { untested();
112 return (_y[0].f0 - _y[1].f0);
113 }else{
114 return STORAGE::tr_probe_num(x);
117 /*--------------------------------------------------------------------------*/
118 void DEV_INDUCTANCE::tr_iwant_matrix()
120 if (!_c_model) {
121 tr_iwant_matrix_passive();
122 }else{
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();
143 STORAGE::tr_begin();
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;
150 _m0.x = _i[0].f0;
151 _m0.c0 = _i[0].x; // amps. iof?
152 _m0.c1 = 0; // _i[0].x / tr_involts();
154 if (_c_model) {
155 _loss1 = _loss0 = 1.;
156 } else {
157 _loss1 = _loss0 = 0.;
160 /*--------------------------------------------------------------------------*/
161 void DEV_INDUCTANCE::tr_restore()
164 if(_sim->_time0 >= _time[0]){
165 // nothing.
166 STORAGE::tr_restore();
167 }else if(!_c_model){ incomplete();
168 // impossible?
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
177 // _i[1] = _i[0];
178 if (using_tr_eval()) {
179 assert(_y[0].f1 == value());
180 }else{incomplete();
182 // BUG: compute _y[0].f1 from OP?
183 _y[0].f0 = _y[0].x * _y[0].f1; // charge
184 _y[1] = _y[0];
185 _y1 = _y[0];
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 ) );
190 _m0 = CPOLY1(_i[0]);
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()
199 if (_sim->_uic) {
200 _i[0].f0 = tr_involts();
202 STORAGE::tr_accept();
203 if(_sim->_uic){
204 _i[0].f0 = tr_involts();
205 // assert(_i[0].x != 0);
206 _i[0].f1 = tr_involts() / (_i[0].x+1e-20);
207 _m0.x = _i[0].f0;
208 _m0.c0 = _i[0].x; // amps. iof?
209 _m0.c1 = 0; // _i[0].x / tr_involts();
210 if (_c_model) {
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()) {
224 set_value(Value);
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);
229 } else {
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);
233 assert(c);
234 attach_common(c);
235 assert(has_common());
237 if (const EVAL_BM_ACTION_BASE* x = dynamic_cast<const EVAL_BM_ACTION_BASE*>(common())) {
238 USE(x);
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) {
247 return true;
248 } else { untested();
249 trace2("has_ic", long_label(), x->_ic);
250 return false;
252 } else {
253 return false;
256 /*--------------------------------------------------------------------------*/
257 bool DEV_INDUCTANCE::do_tr()
259 if (using_tr_eval()) {
260 _y[0].x = tr_input_limited(); // _m0.c0 + _m0.c1 * x;
261 tr_eval();
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());
266 }else{
268 }else{
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;
272 assert(converged());
274 store_values();
275 q_load();
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
282 _i[0].x = _y[0].x;
283 _i[0].f1 = 0;
284 if (!_c_model) {
285 _m0.x = 0.;
286 _m0.c0 = _y[0].x;
287 _m0.c1 = 0.;
288 } else {
289 assert(_loss0==1);
291 } else {
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);
299 if (!_c_model) {
300 _m0.x = NOT_VALID;
301 _m0.c1 = 1 / ((_i[0].c1()==0) ? OPT::shortckt : _i[0].c1());
302 if (_sim->uic_now() && has_ic()) {
303 _m0.c0 = _y[0].x;
304 _y[0].f0 = _y[0].x * _y[0].f1;
305 _m0.c1 = 0;
306 } else {
307 _m0.c0 = -_i[0].c0() * _m0.c1;
309 } else {
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
318 } else {
319 _m0.x = NOT_VALID;
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);
326 return converged();
328 /*--------------------------------------------------------------------------*/
329 void DEV_INDUCTANCE::tr_load()
331 if (!_c_model) {
332 tr_load_passive();
333 }else{
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()
342 {untested();
343 _loss0 = _m0.c0 = _m0.c1 = 0.;
344 _sim->mark_inc_mode_bad();
345 tr_load();
347 /*--------------------------------------------------------------------------*/
348 double DEV_INDUCTANCE::tr_input()const
350 if (!_c_model) {
351 return _m0.c0 + _m0.c1 * tr_involts();
352 }else{
353 return _n[IN1].v0();
356 /*--------------------------------------------------------------------------*/
357 double DEV_INDUCTANCE::tr_input_limited()const
359 if (!_c_model) {
360 return _m0.c0 + _m0.c1 * tr_involts_limited();
361 }else{
362 return _n[IN1].v0();
365 /*--------------------------------------------------------------------------*/
366 double DEV_INDUCTANCE::tr_amps()const
368 if (!_c_model) {
369 // m0c0 is "flux"
370 return fixzero((_m0.c1 * tr_involts() + _m0.c0), _m0.c0);
371 }else{
372 return _loss0 * _n[IN1].v0();
375 /*--------------------------------------------------------------------------*/
376 void DEV_INDUCTANCE::ac_iwant_matrix()
378 if (!_c_model) {
379 ac_iwant_matrix_passive();
380 }else{
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()) {
395 ac_eval();
396 }else{
397 assert(_ev == _y[0].f1);
398 // uuh
399 // assert(dynamic_cast<INDUCTANCE::DEV_MUTUAL_L*>(this) || has_tr_eval() || _ev == double(value()));
401 if (!_c_model) {
402 if ((COMPLEX)_ev * _sim->_jomega == 0.) {untested();
403 _acg = 1. / OPT::shortckt;
404 }else{
405 _acg = 1. / ((COMPLEX)_ev * _sim->_jomega);
407 }else{
408 _acg = -(double)_loss0 *(double) _loss0 * (COMPLEX)_ev * _sim->_jomega;
411 /*--------------------------------------------------------------------------*/
412 void DEV_INDUCTANCE::ac_load()
414 if (!_c_model) {
415 ac_load_passive();
416 }else{
417 ac_load_inode(); // 4x \pm loss.
418 ac_load_diagonal_point(_n[IN1], _acg);
421 /*--------------------------------------------------------------------------*/
422 COMPLEX DEV_INDUCTANCE::ac_amps()const
424 if (!_c_model) {
425 return (ac_involts() * _acg);
426 }else{
427 return ( (double)_loss0 * (COMPLEX)(_n[IN1].vac()) );
430 /*--------------------------------------------------------------------------*/
431 } // INDUCTANCE
432 /*--------------------------------------------------------------------------*/
433 // vim:ts=8:sw=2:noet: