Guest User

Untitled

a guest
Jan 22nd, 2018
65
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 29.11 KB | None | 0 0
  1. #include <vector>
  2. #include <string>
  3. #include <cstdio>
  4. #include <cmath>
  5.  
  6. // set to 1 to make LU factorization show pivots
  7. #define VERBOSE_LU 0
  8.  
  9. // gMin for diodes etc..
  10. static const double gMin = 1e-12;
  11.  
  12. // voltage tolerance
  13. static const double vTolerance = 5e-5;
  14.  
  15. // thermal voltage for diodes/transistors
  16. static const double vThermal = 0.026;
  17.  
  18. static const unsigned maxIter = 200;
  19.  
  20. //
  21. // General overview
  22. // ----------------
  23. //
  24. // Circuits are built from nodes and Components, where nodes are
  25. // simply positive integers (with 0 designating ground).
  26. //
  27. // Every Component has one or more pins connecting to the circuit
  28. // nodes as well as zero or more internal nets.
  29. //
  30. // While we map pins directly to nets here, the separation would
  31. // be useful if the solver implemented stuff like net-reordering.
  32. //
  33. // MNACell represents a single entry in the solution matrix,
  34. // where we store constants and time-step dependent constants
  35. // separately, plus collect pointers to dynamic variables.
  36. //
  37. // We track enough information here that we only need to stamp once.
  38. //
  39. struct MNACell
  40. {
  41. double g; // simple values (eg. resistor conductance)
  42. double gtimed; // time-scaled values (eg. capacitor conductance)
  43.  
  44. // pointers to dynamic variables, added in once per solve
  45. std::vector<double*> gdyn;
  46.  
  47. double lu, prelu; // lu-solver values and matrix pre-LU cache
  48.  
  49. std::string txt; // text version of MNA for pretty-printing
  50.  
  51. void clear()
  52. {
  53. g = 0;
  54. gtimed = 0;
  55. txt = "";
  56. }
  57.  
  58. void initLU(double stepScale)
  59. {
  60. prelu = g + gtimed * stepScale;
  61. }
  62.  
  63. // restore matrix state and update dynamic values
  64. void updatePre()
  65. {
  66. lu = prelu;
  67. for(int i = 0; i < gdyn.size(); ++i)
  68. {
  69. lu += 0[gdyn[i]];
  70. }
  71. }
  72. };
  73.  
  74. // this is for keeping track of node information
  75. // for the purposes of more intelligent plotting
  76. struct MNANodeInfo
  77. {
  78. enum Type
  79. {
  80. tVoltage,
  81. tCurrent,
  82.  
  83. tCount
  84. };
  85.  
  86. Type type; // one auto-range per unit-type
  87. double scale; // scale factor (eg. charge to voltage)
  88.  
  89. std::string name; // node name for display
  90. };
  91.  
  92. // Stores A and b for A*x - b = 0, where x is the solution.
  93. //
  94. // A is stored as a vector of rows, for easy in-place pivots
  95. //
  96. struct MNASystem
  97. {
  98. typedef std::vector<MNACell> MNAVector;
  99. typedef std::vector<MNAVector> MNAMatrix;
  100.  
  101. // node names - for output
  102. std::vector<MNANodeInfo> nodes;
  103.  
  104. MNAMatrix A;
  105. MNAVector b;
  106.  
  107. double time;
  108.  
  109. void setSize(int n)
  110. {
  111. A.resize(n);
  112. b.resize(n);
  113.  
  114. nodes.resize(n);
  115.  
  116. for(unsigned i = 0; i < n; ++i)
  117. {
  118. b[i].clear();
  119. A[i].resize(n);
  120.  
  121. char buf[16];
  122. sprintf(buf, "v%d", i);
  123. nodes[i].name = buf;
  124. nodes[i].type = MNANodeInfo::tVoltage;
  125. nodes[i].scale = 1;
  126.  
  127. for(unsigned j = 0; j < n; ++j)
  128. {
  129. A[i][j].clear();
  130. }
  131. }
  132.  
  133. time = 0;
  134. }
  135.  
  136. void stampTimed(double g, int r, int c, const std::string & txt)
  137. {
  138. A[r][c].gtimed += g;
  139. A[r][c].txt += txt;
  140. }
  141.  
  142. void stampStatic(double g, int r, int c, const std::string & txt)
  143. {
  144. A[r][c].g += g;
  145. A[r][c].txt += txt;
  146. }
  147. };
  148.  
  149. struct IComponent
  150. {
  151. virtual ~IComponent() {}
  152.  
  153. // return the number of pins for this component
  154. virtual int pinCount() = 0;
  155.  
  156. // return a pointer to array of pin locations
  157. // NOTE: these will eventually be GUI locations to be unified
  158. virtual int * getPinLocs() = 0;
  159.  
  160. // setup pins and calculate the size of the full netlist
  161. // the Component<> will handle this automatically
  162. //
  163. // - netSize is the current size of the netlist
  164. // - pins is an array of circuits nodes
  165. //
  166. virtual void setupNets(int & netSize, int & states, int * pins) = 0;
  167.  
  168. // stamp constants into the matrix
  169. virtual void stamp(MNASystem & m) = 0;
  170.  
  171. // this is for allocating state variables
  172. virtual void setupStates(int & states) {}
  173.  
  174. // update state variables, only tagged nodes
  175. // this is intended for fixed-time compatible
  176. // testing to make sure we can code-gen stuff
  177. virtual void update(MNASystem & m) {}
  178.  
  179. // return true if we're done - will keep iterating
  180. // until all the components are happy
  181. virtual bool newton(MNASystem & m) { return true; }
  182.  
  183. // time-step change, for caps to fix their state-variables
  184. virtual void scaleTime(double told_per_new) {}
  185. };
  186.  
  187. template <int nPins = 0, int nInternalNets = 0>
  188. struct Component : IComponent
  189. {
  190. static const int nNets = nPins + nInternalNets;
  191.  
  192. int pinLoc[nPins];
  193. int nets[nNets];
  194.  
  195. int pinCount() { return nPins; }
  196.  
  197. int * getPinLocs() { return pinLoc; }
  198.  
  199. void setupNets(int & netSize, int & states, int * pins)
  200. {
  201. for(int i = 0; i < nPins; ++i)
  202. {
  203. nets[i] = pins[i];
  204. }
  205.  
  206. for(int i = 0; i < nInternalNets; ++i)
  207. {
  208. nets[nPins + i] = netSize++;
  209. }
  210.  
  211. setupStates(states);
  212. }
  213. };
  214.  
  215. static const int unitValueOffset = 4;
  216. static const int unitValueMax = 8;
  217. static const char * unitValueSuffixes[unitValueMax+1] = {
  218. "p", "n", "u", "m", "", "k", "M", "G"
  219. };
  220.  
  221. static void formatUnitValue(char * buf, double v, const char * unit)
  222. {
  223. int suff = unitValueOffset + int(log(v) / log(10.)) / 3;
  224.  
  225. if(v < 1) suff -= 1;
  226.  
  227. if(suff < 0) suff = 0;
  228. if(suff > unitValueMax) suff = unitValueMax;
  229.  
  230. double vr = v / pow(10., 3*double(suff - unitValueOffset));
  231.  
  232. sprintf(buf, "%.0f%s%s", vr, unitValueSuffixes[suff], unit);
  233. }
  234.  
  235. struct Resistor : Component<2>
  236. {
  237. double r;
  238.  
  239. Resistor(double r, int l0, int l1) : r(r)
  240. {
  241. pinLoc[0] = l0;
  242. pinLoc[1] = l1;
  243. }
  244.  
  245. void stamp(MNASystem & m)
  246. {
  247. char txt[16];
  248. txt[0] = 'R';
  249. formatUnitValue(txt+1, r, "");
  250. double g = 1. / r;
  251. m.stampStatic(+g, nets[0], nets[0], std::string("+") + txt);
  252. m.stampStatic(-g, nets[0], nets[1], std::string("-") + txt);
  253. m.stampStatic(-g, nets[1], nets[0], std::string("-") + txt);
  254. m.stampStatic(+g, nets[1], nets[1], std::string("+") + txt);
  255. }
  256. };
  257.  
  258. struct Capacitor : Component<2, 1>
  259. {
  260. double c;
  261. double stateVar;
  262. double voltage;
  263.  
  264. Capacitor(double c, int l0, int l1) : c(c)
  265. {
  266. pinLoc[0] = l0;
  267. pinLoc[1] = l1;
  268.  
  269. stateVar = 0;
  270. voltage = 0;
  271. }
  272.  
  273. void stamp(MNASystem & m)
  274. {
  275. char buf[16];
  276. formatUnitValue(buf, c, "F");
  277.  
  278. // we can use a trick here, to get the capacitor to
  279. // work on it's own line with direct trapezoidal:
  280. //
  281. // | -g*t +g*t +t | v+
  282. // | +g*t -g*t -t | v-
  283. // | +2*g -2*g -1 | state
  284. //
  285. // the logic with this is that for constant timestep:
  286. //
  287. // i1 = g*v1 - s0 , s0 = g*v0 + i0
  288. // s1 = 2*g*v1 - s0 <-> s0 = 2*g*v1 - s1
  289. //
  290. // then if we substitute back:
  291. // i1 = g*v1 - (2*g*v1 - s1)
  292. // = s1 - g*v1
  293. //
  294. // this way we just need to copy the new state to the
  295. // next timestep and there's no actual integration needed
  296. //
  297. // the "half time-step" error here means that our state
  298. // is 2*c*v - i/t but we fix this for display in update
  299. // and correct the current-part on time-step changes
  300.  
  301. // trapezoidal needs another factor of two for the g
  302. // since c*(v1 - v0) = (i1 + i0)/(2*t), where t = 1/T
  303. double g = 2*c;
  304.  
  305. m.stampTimed(+1, nets[0], nets[2], "+t");
  306. m.stampTimed(-1, nets[1], nets[2], "-t");
  307.  
  308. m.stampTimed(-g, nets[0], nets[0], std::string("-t*") + buf);
  309. m.stampTimed(+g, nets[0], nets[1], std::string("+t*") + buf);
  310. m.stampTimed(+g, nets[1], nets[0], std::string("+t*") + buf);
  311. m.stampTimed(-g, nets[1], nets[1], std::string("-t*") + buf);
  312.  
  313. m.stampStatic(+2*g, nets[2], nets[0], std::string("+2*") + buf);
  314. m.stampStatic(-2*g, nets[2], nets[1], std::string("-2*") + buf);
  315.  
  316. m.stampStatic(-1, nets[2], nets[2], "-1");
  317.  
  318. // see the comment about v:C[%d] below
  319. sprintf(buf, "q:C:%d,%d", pinLoc[0], pinLoc[1]);
  320. m.b[nets[2]].gdyn.push_back(&stateVar);
  321. m.b[nets[2]].txt = buf;
  322.  
  323. // this isn't quite right as state stores 2*c*v - i/t
  324. // however, we'll fix this in updateFull() for display
  325. sprintf(buf, "v:C:%d,%d", pinLoc[0], pinLoc[1]);
  326. m.nodes[nets[2]].name = buf;
  327. m.nodes[nets[2]].scale = 1 / c;
  328. }
  329.  
  330. void update(MNASystem & m)
  331. {
  332. stateVar = m.b[nets[2]].lu;
  333.  
  334. // solve legit voltage from the pins
  335. voltage = m.b[nets[0]].lu - m.b[nets[1]].lu;
  336.  
  337. // then we can store this for display here
  338. // since this value won't be used at this point
  339. m.b[nets[2]].lu = c*voltage;
  340. }
  341.  
  342. void scaleTime(double told_per_new)
  343. {
  344. // the state is 2*c*voltage - i/t0
  345. // so we subtract out the voltage, scale current
  346. // and then add the voltage back to get new state
  347. //
  348. // note that this also works if the old rate is infinite
  349. // (ie. t0=0) when going from DC analysis to transient
  350. //
  351. double qq = 2*c*voltage;
  352. stateVar = qq + (stateVar - qq)*told_per_new;
  353. }
  354. };
  355.  
  356. struct Voltage : Component<2, 1>
  357. {
  358. double v;
  359.  
  360. Voltage(double v, int l0, int l1) : v(v)
  361. {
  362. pinLoc[0] = l0;
  363. pinLoc[1] = l1;
  364. }
  365.  
  366. void stamp(MNASystem & m)
  367. {
  368. m.stampStatic(-1, nets[0], nets[2], "-1");
  369. m.stampStatic(+1, nets[1], nets[2], "+1");
  370.  
  371. m.stampStatic(+1, nets[2], nets[0], "+1");
  372. m.stampStatic(-1, nets[2], nets[1], "-1");
  373.  
  374. char buf[16];
  375. sprintf(buf, "%+.2gV", v);
  376. m.b[nets[2]].g = v;
  377. m.b[nets[2]].txt = buf;
  378.  
  379. sprintf(buf, "i:V(%+.2g):%d,%d", v, pinLoc[0], pinLoc[1]);
  380. m.nodes[nets[2]].name = buf;
  381. m.nodes[nets[2]].type = MNANodeInfo::tCurrent;
  382. }
  383. };
  384.  
  385. // probe a differential voltage
  386. // also forces this voltage to actually get solved :)
  387. struct Probe : Component<2, 1>
  388. {
  389. Probe(int l0, int l1)
  390. {
  391. pinLoc[0] = l0;
  392. pinLoc[1] = l1;
  393. }
  394.  
  395. void stamp(MNASystem & m)
  396. {
  397. // vp + vn - vd = 0
  398. m.stampStatic(+1, nets[2], nets[0], "+1");
  399. m.stampStatic(-1, nets[2], nets[1], "-1");
  400. m.stampStatic(-1, nets[2], nets[2], "-1");
  401.  
  402. m.nodes[nets[2]].name = "v:probe";
  403. }
  404.  
  405. void update(MNASystem & m)
  406. {
  407. // we could do output here :)
  408. }
  409. };
  410.  
  411. // function voltage generator
  412. struct Function : Component<2,1>
  413. {
  414. typedef double (*FuncPtr)(double t);
  415.  
  416. FuncPtr fn;
  417. double v;
  418.  
  419. Function(FuncPtr fn, int l0, int l1) : fn(fn)
  420. {
  421. pinLoc[0] = l0;
  422. pinLoc[1] = l1;
  423.  
  424. v = fn(0);
  425. }
  426.  
  427. void stamp(MNASystem & m)
  428. {
  429. // this is identical to voltage source
  430. // except voltage is dynanic
  431. m.stampStatic(-1, nets[0], nets[2], "-1");
  432. m.stampStatic(+1, nets[1], nets[2], "+1");
  433.  
  434. m.stampStatic(+1, nets[2], nets[0], "+1");
  435. m.stampStatic(-1, nets[2], nets[1], "-1");
  436.  
  437. char buf[16];
  438. m.b[nets[2]].gdyn.push_back(&v);
  439. sprintf(buf, "Vfn:%d,%d", pinLoc[0], pinLoc[1]);
  440. m.b[nets[2]].txt = buf;
  441.  
  442. sprintf(buf, "i:Vfn:%d,%d", pinLoc[0], pinLoc[1]);
  443. m.nodes[nets[2]].name = buf;
  444. m.nodes[nets[2]].type = MNANodeInfo::tCurrent;
  445. }
  446.  
  447. void update(MNASystem & m)
  448. {
  449. v = fn(m.time);
  450. }
  451.  
  452. };
  453.  
  454. // POD-struct for PN-junction data, for diodes and BJTs
  455. //
  456. struct JunctionPN
  457. {
  458. // variables
  459. double geq, ieq, veq;
  460.  
  461. // parameters
  462. double is, nvt, rnvt, vcrit;
  463. };
  464.  
  465. void initJunctionPN(JunctionPN & pn, double is, double n)
  466. {
  467. pn.is = is;
  468. pn.nvt = n * vThermal;
  469. pn.rnvt = 1 / pn.nvt;
  470. pn.vcrit = pn.nvt * log(pn.nvt / (pn.is * sqrt(2.)));
  471. }
  472.  
  473. // linearize junction at the specified voltage
  474. //
  475. // ideally we could handle series resistance here as well
  476. // to avoid putting it on a separate node, but not sure how
  477. // to make that work as it looks like we'd need Lambert-W then
  478. void linearizeJunctionPN(JunctionPN & pn, double v)
  479. {
  480. double e = pn.is * exp(v * pn.rnvt);
  481. double i = e - pn.is + gMin * v;
  482. double g = e * pn.rnvt + gMin;
  483.  
  484. pn.geq = g;
  485. pn.ieq = v*g - i;
  486. pn.veq = v;
  487. }
  488.  
  489. // returns true if junction is good enough
  490. bool newtonJunctionPN(JunctionPN & pn, double v)
  491. {
  492. double dv = v - pn.veq;
  493. if(fabs(dv) < vTolerance) return true;
  494.  
  495. // check critical voltage and adjust voltage if over
  496. if(v > pn.vcrit)
  497. {
  498. // this formula comes from Qucs documentation
  499. v = pn.veq + pn.nvt*log((std::max)(pn.is, 1+dv*pn.rnvt));
  500. }
  501.  
  502. linearizeJunctionPN(pn, v);
  503.  
  504. return false;
  505. }
  506.  
  507. struct Diode : Component<2, 2>
  508. {
  509. JunctionPN pn;
  510.  
  511. // should make these parameters
  512. double rs;
  513.  
  514. // l0 -->|-- l1 -- parameters default to approx 1N4148
  515. Diode(int l0, int l1,
  516. double rs = 10., double is = 35e-12, double n = 1.24)
  517. : rs(rs)
  518. {
  519. pinLoc[0] = l0;
  520. pinLoc[1] = l1;
  521.  
  522. initJunctionPN(pn, is, n);
  523.  
  524. // FIXME: move init to some restart routine?
  525.  
  526. // initial condition v = 0
  527. linearizeJunctionPN(pn, 0);
  528. }
  529.  
  530. bool newton(MNASystem & m)
  531. {
  532. return newtonJunctionPN(pn, m.b[nets[2]].lu);
  533. }
  534.  
  535. void stamp(MNASystem & m)
  536. {
  537. // Diode could be built with 3 extra nodes:
  538. //
  539. // | . . . . +1 | V+
  540. // | . . . . -1 | V-
  541. // | . . grs -grs -1 | v:D
  542. // | . . -grs grs+geq . | v:pn = ieq
  543. // | -1 +1 +1 . . | i:pn
  544. //
  545. // Here grs is the 1/rs series conductance.
  546. //
  547. // This gives us the junction voltage (v:pn) and
  548. // current (i:pn) and the composite voltage (v:D).
  549. //
  550. // The i:pn row is an ideal transformer connecting
  551. // the floating diode to the ground-referenced v:D
  552. // where we connect the series resistance to v:pn
  553. // that solves the diode equation with Newton.
  554. //
  555. // We can then add the 3rd row to the bottom 2 with
  556. // multipliers 1 and -rs = -1/grs and drop it:
  557. //
  558. // | . . . +1 | V+
  559. // | . . . -1 | V-
  560. // | . . geq -1 | v:pn = ieq
  561. // | -1 +1 +1 rs | i:pn
  562. //
  563. // Note that only the v:pn row here is non-linear.
  564. //
  565. // We could even do away without the separate row for
  566. // the current, which would lead to the following:
  567. //
  568. // | +grs -grs -grs |
  569. // | -grs +grs +grs |
  570. // | -grs +grs +grs+geq | = ieq
  571. //
  572. // In practice we keep the current row since it's
  573. // nice to have it as an output anyway.
  574. //
  575. m.stampStatic(-1, nets[3], nets[0], "-1");
  576. m.stampStatic(+1, nets[3], nets[1], "+1");
  577. m.stampStatic(+1, nets[3], nets[2], "+1");
  578.  
  579. m.stampStatic(+1, nets[0], nets[3], "+1");
  580. m.stampStatic(-1, nets[1], nets[3], "-1");
  581. m.stampStatic(-1, nets[2], nets[3], "-1");
  582.  
  583. m.stampStatic(rs, nets[3], nets[3], "rs:pn");
  584.  
  585. m.A[nets[2]][nets[2]].gdyn.push_back(&pn.geq);
  586. m.A[nets[2]][nets[2]].txt = "gm:D";
  587. m.b[nets[2]].gdyn.push_back(&pn.ieq);
  588.  
  589. char buf[16];
  590. sprintf(buf, "i0:D:%d,%d", pinLoc[0], pinLoc[1]);
  591. m.b[nets[2]].txt = buf;
  592.  
  593. sprintf(buf, "v:D:%d,%d", pinLoc[0], pinLoc[1]);
  594. m.nodes[nets[2]].name = buf;
  595.  
  596. sprintf(buf, "i:D:%d,%d", pinLoc[0], pinLoc[1]);
  597. m.nodes[nets[3]].name = buf;
  598. m.nodes[nets[3]].type = MNANodeInfo::tCurrent;
  599.  
  600. }
  601. };
  602.  
  603. struct BJT : Component<3, 4>
  604. {
  605. // emitter and collector junctions
  606. JunctionPN pnC, pnE;
  607.  
  608. // forward and reverse alpha
  609. double af, ar, rsbc, rsbe;
  610.  
  611. bool pnp;
  612.  
  613. BJT(int b, int c, int e, bool pnp = false) : pnp(pnp)
  614. {
  615. pinLoc[0] = b;
  616. pinLoc[1] = c;
  617. pinLoc[2] = e;
  618.  
  619. // this attempts a 2n3904-style small-signal
  620. // transistor, although the values are a bit
  621. // arbitrarily set to "something reasonable"
  622.  
  623. // forward and reverse beta
  624. double bf = 200;
  625. double br = 20;
  626.  
  627. // forward and reverse alpha
  628. af = bf / (1 + bf);
  629. ar = br / (1 + br);
  630.  
  631. // these are just rb+re and rb+rc
  632. // this is not necessarily the best way to
  633. // do anything, but having junction series
  634. // resistances helps handle degenerate cases
  635. rsbc = 5.8376+0.0001;
  636. rsbe = 5.8376+2.65711;
  637.  
  638. //
  639. // the basic rule is that:
  640. // af * ise = ar * isc = is
  641. //
  642. // FIXME: with non-equal ideality factors
  643. // we can get non-sensical results, why?
  644. //
  645. double is = 6.734e-15;
  646. double n = 1.24;
  647. initJunctionPN(pnE, is / af, n);
  648. initJunctionPN(pnC, is / ar, n);
  649.  
  650. linearizeJunctionPN(pnE, 0);
  651. linearizeJunctionPN(pnC, 0);
  652. }
  653.  
  654. bool newton(MNASystem & m)
  655. {
  656. return newtonJunctionPN(pnC, m.b[nets[3]].lu)
  657. & newtonJunctionPN(pnE, m.b[nets[4]].lu);
  658. }
  659.  
  660. void stamp(MNASystem & m)
  661. {
  662. // The basic idea here is the same as with diodes
  663. // except we do it once for each junction.
  664. //
  665. // With the transfer currents sourced from the
  666. // diode currents, NPN then looks like this:
  667. //
  668. // 0 | . . . . . 1-ar 1-af | vB
  669. // 1 | . . . . . -1 +af | vC
  670. // 2 | . . . . . +ar -1 | vE
  671. // 3 | . . . gc . -1 . | v:Qbc = ic
  672. // 4 | . . . . ge . -1 | v:Qbe = ie
  673. // 5 | -1 +1 . +1 . rsbc . | i:Qbc
  674. // 6 | -1 . +1 . +1 . rsbe | i:Qbe
  675. // ------------------------
  676. // 0 1 2 3 4 5 6
  677. //
  678. // For PNP version, we simply flip the junctions
  679. // by changing signs of (3,5),(5,3) and (4,6),(6,4).
  680. //
  681. // Also just like diodes, we have junction series
  682. // resistances, rather than terminal resistances.
  683. //
  684. // This works just as well, but should be kept
  685. // in mind when fitting particular transistors.
  686. //
  687.  
  688. // diode currents to external base
  689. m.stampStatic(1-ar, nets[0], nets[5], "1-ar");
  690. m.stampStatic(1-af, nets[0], nets[6], "1-af");
  691.  
  692. // diode currents to external collector and emitter
  693. m.stampStatic(-1, nets[1], nets[5], "-1");
  694. m.stampStatic(-1, nets[2], nets[6], "-1");
  695.  
  696. // series resistances
  697. m.stampStatic(rsbc, nets[5], nets[5], "rsbc");
  698. m.stampStatic(rsbe, nets[6], nets[6], "rsbe");
  699.  
  700. // current - junction connections
  701. // for the PNP case we flip the signs of these
  702. // to flip the diode junctions wrt. the above
  703. if(pnp)
  704. {
  705. m.stampStatic(-1, nets[5], nets[3], "-1");
  706. m.stampStatic(+1, nets[3], nets[5], "+1");
  707.  
  708. m.stampStatic(-1, nets[6], nets[4], "-1");
  709. m.stampStatic(+1, nets[4], nets[6], "+1");
  710.  
  711. }
  712. else
  713. {
  714. m.stampStatic(+1, nets[5], nets[3], "+1");
  715. m.stampStatic(-1, nets[3], nets[5], "-1");
  716.  
  717. m.stampStatic(+1, nets[6], nets[4], "+1");
  718. m.stampStatic(-1, nets[4], nets[6], "-1");
  719. }
  720.  
  721. // external voltages to collector current
  722. m.stampStatic(-1, nets[5], nets[0], "-1");
  723. m.stampStatic(+1, nets[5], nets[1], "+1");
  724.  
  725. // external voltages to emitter current
  726. m.stampStatic(-1, nets[6], nets[0], "-1");
  727. m.stampStatic(+1, nets[6], nets[2], "+1");
  728.  
  729. // source transfer currents to external pins
  730. m.stampStatic(+ar, nets[2], nets[5], "+ar");
  731. m.stampStatic(+af, nets[1], nets[6], "+af");
  732.  
  733. char buf[16];
  734.  
  735. // dynamic variables
  736. m.A[nets[3]][nets[3]].gdyn.push_back(&pnC.geq);
  737. m.A[nets[3]][nets[3]].txt = "gm:Qbc";
  738. m.b[nets[3]].gdyn.push_back(&pnC.ieq);
  739. sprintf(buf, "i0:Q:%d,%d,%d:cb", pinLoc[0], pinLoc[1], pinLoc[2]);
  740. m.b[nets[3]].txt = buf;
  741.  
  742. m.A[nets[4]][nets[4]].gdyn.push_back(&pnE.geq);
  743. m.A[nets[4]][nets[4]].txt = "gm:Qbe";
  744. m.b[nets[4]].gdyn.push_back(&pnE.ieq);
  745. sprintf(buf, "i0:Q:%d,%d,%d:eb", pinLoc[0], pinLoc[1], pinLoc[2]);
  746. m.b[nets[4]].txt = buf;
  747.  
  748. sprintf(buf, "v:Q:%d,%d,%d:%s",
  749. pinLoc[0], pinLoc[1], pinLoc[2], pnp ? "cb" : "bc");
  750. m.nodes[nets[3]].name = buf;
  751.  
  752. sprintf(buf, "v:Q:%d,%d,%d:%s",
  753. pinLoc[0], pinLoc[1], pinLoc[2], pnp ? "eb" : "be");
  754. m.nodes[nets[4]].name = buf;
  755.  
  756. sprintf(buf, "i:Q:%d,%d,%d:bc", pinLoc[0], pinLoc[1], pinLoc[2]);
  757. m.nodes[nets[5]].name = buf;
  758. m.nodes[nets[5]].type = MNANodeInfo::tCurrent;
  759. m.nodes[nets[5]].scale = 1 - ar;
  760.  
  761. sprintf(buf, "i:Q:%d,%d,%d:be", pinLoc[0], pinLoc[1], pinLoc[2]);
  762. m.nodes[nets[6]].name = buf;
  763. m.nodes[nets[6]].type = MNANodeInfo::tCurrent;
  764. m.nodes[nets[6]].scale = 1 - af;
  765. }
  766. };
  767.  
  768.  
  769. struct NetList
  770. {
  771. typedef std::vector<IComponent*> ComponentList;
  772.  
  773. NetList(int nodes) : nets(nodes), states(0)
  774. {
  775.  
  776. }
  777.  
  778. void addComponent(IComponent * c)
  779. {
  780. // this is a bit "temporary" for now
  781. c->setupNets(nets, states, c->getPinLocs());
  782. components.push_back(c);
  783. }
  784.  
  785. void buildSystem()
  786. {
  787. system.setSize(nets);
  788. for(int i = 0; i < components.size(); ++i)
  789. {
  790. components[i]->stamp(system);
  791. }
  792. printf("Prepare for DC analysis..\n");
  793. setStepScale(0);
  794. tStep = 0;
  795. }
  796.  
  797. void dumpMatrix()
  798. {
  799. std::vector<int> maxWidth(nets);
  800.  
  801. for(int i = 0; i < nets; ++i) maxWidth[i] = 1;
  802. int nnMax = 1;
  803.  
  804. for(int i = 0; i < nets; ++i)
  805. {
  806. nnMax = std::max(nnMax, (int)system.nodes[i].name.size());
  807. for(int j = 0; j < nets; ++j)
  808. {
  809. maxWidth[j] = std::max(maxWidth[j],
  810. (int)system.A[i][j].txt.size());
  811. }
  812. }
  813.  
  814.  
  815. char buf[1024];
  816. for(unsigned i = 0; i < nets; ++i)
  817. {
  818. int off = sprintf(buf, "%2d: | ", i);
  819. for(int j = 0; j < nets; ++j)
  820. {
  821. off += sprintf(buf+off,
  822. " %*s ", maxWidth[j],
  823. system.A[i][j].txt.size()
  824. ? system.A[i][j].txt.c_str()
  825. : ((system.A[i][j].lu==0) ? "." : "#"));
  826. }
  827. sprintf(buf+off, " | %-*s = %s",
  828. nnMax, system.nodes[i].name.c_str(),
  829. system.b[i].txt.size()
  830. ? system.b[i].txt.c_str() : (!i ? "ground" : "0"));
  831.  
  832. puts(buf);
  833. }
  834. }
  835.  
  836. void setTimeStep(double tStepSize)
  837. {
  838. for(int i = 0; i < components.size(); ++i)
  839. {
  840. components[i]->scaleTime(tStep / tStepSize);
  841. }
  842.  
  843. tStep = tStepSize;
  844. double stepScale = 1. / tStep;
  845. printf("timeStep changed to %.2g (%.2g Hz)\n", tStep, stepScale);
  846. setStepScale(stepScale);
  847. }
  848.  
  849. void simulateTick()
  850. {
  851. int iter;
  852. for(iter = 0; iter < maxIter; ++iter)
  853. {
  854. // restore matrix state and add dynamic values
  855. updatePre();
  856. luFactor();
  857. luForward();
  858. luSolve();
  859.  
  860. if(newton()) break;
  861. }
  862.  
  863. system.time += tStep;
  864.  
  865. update();
  866.  
  867. printf(" %02.4f |", system.time);
  868. int fillPost = 0;
  869. for(int i = 1; i < nets; ++i)
  870. {
  871. printf("\t%+.4e", system.b[i].lu * system.nodes[i].scale);
  872. for(int j = 1; j < nets; ++j)
  873. {
  874. if(system.A[i][j].lu != 0) ++fillPost;
  875. }
  876. }
  877. printf("\t %d iters, LU density: %.1f%%\n",
  878. iter, 100 * fillPost / ((nets-1.f)*(nets-1.f)));
  879. }
  880.  
  881. void printHeaders()
  882. {
  883. printf("\n time: | ");
  884. for(int i = 1; i < nets; ++i)
  885. {
  886. printf("%16s", system.nodes[i].name.c_str());
  887. }
  888. printf("\n\n");
  889. }
  890.  
  891. // plotting and such would want to use this
  892. const MNASystem & getMNA() { return system; }
  893.  
  894. protected:
  895. double tStep;
  896.  
  897. int nets, states;
  898. ComponentList components;
  899.  
  900. MNASystem system;
  901.  
  902. void update()
  903. {
  904. for(int i = 0; i < components.size(); ++i)
  905. {
  906. components[i]->update(system);
  907. }
  908. }
  909.  
  910. // return true if we're done
  911. bool newton()
  912. {
  913. bool done = 1;
  914. for(int i = 0; i < components.size(); ++i)
  915. {
  916. done &= components[i]->newton(system);
  917. }
  918. return done;
  919. }
  920.  
  921. void initLU(double stepScale)
  922. {
  923. for(int i = 0; i < nets; ++i)
  924. {
  925. system.b[i].initLU(stepScale);
  926. for(int j = 0; j < nets; ++j)
  927. {
  928. system.A[i][j].initLU(stepScale);
  929. }
  930. }
  931. }
  932.  
  933. void setStepScale(double stepScale)
  934. {
  935. // initialize matrix for LU and save it to cache
  936. initLU(stepScale);
  937.  
  938. int fill = 0;
  939. for(int i = 1; i < nets; ++i)
  940. {
  941. for(int j = 1; j < nets; ++j)
  942. {
  943. if(system.A[i][j].prelu != 0
  944. || system.A[i][j].gdyn.size()) ++fill;
  945. }
  946. }
  947. printf("MNA density %.1f%%\n", 100 * fill / ((nets-1.)*(nets-1.)));
  948. }
  949.  
  950. void updatePre()
  951. {
  952. for(int i = 0; i < nets; ++i)
  953. {
  954. system.b[i].updatePre();
  955. for(int j = 0; j < nets; ++j)
  956. {
  957. system.A[i][j].updatePre();
  958. }
  959. }
  960. }
  961.  
  962. void luFactor()
  963. {
  964. int p;
  965. for(p = 1; p < nets; ++p)
  966. {
  967. // FIND PIVOT
  968. {
  969. int pr = p;
  970. for(int r = p; r < nets; ++r)
  971. {
  972. if(fabs(system.A[r][p].lu)
  973. > fabs(system.A[pr][p].lu))
  974. {
  975. pr = r;
  976. }
  977. }
  978. // swap if necessary
  979. if(pr != p)
  980. {
  981. std::swap(system.A[p], system.A[pr]);
  982. std::swap(system.b[p], system.b[pr]);
  983. }
  984. #if VERBOSE_LU
  985. printf("pivot %d (from %d): %+.2e\n",
  986. p, pr, system.A[p][p].lu);
  987. #endif
  988. }
  989. if(0 == system.A[p][p].lu)
  990. {
  991. printf("Failed to find a pivot!!");
  992. return;
  993. }
  994.  
  995. // take reciprocal for D entry
  996. system.A[p][p].lu = 1 / system.A[p][p].lu;
  997.  
  998. // perform reduction on rows below
  999. for(int r = p+1; r < nets; ++r)
  1000. {
  1001. if(system.A[r][p].lu == 0) continue;
  1002.  
  1003. system.A[r][p].lu *= system.A[p][p].lu;
  1004. for(int c = p+1; c < nets; ++c)
  1005. {
  1006. if(system.A[p][c].lu == 0) continue;
  1007.  
  1008. system.A[r][c].lu -=
  1009. system.A[p][c].lu * system.A[r][p].lu;
  1010. }
  1011.  
  1012. }
  1013. }
  1014. }
  1015.  
  1016. // this does forward substitution for the solution vector
  1017. int luForward()
  1018. {
  1019. int p;
  1020. for(p = 1; p < nets; ++p)
  1021. {
  1022. // perform reduction on rows below
  1023. for(int r = p+1; r < nets; ++r)
  1024. {
  1025. if(system.A[r][p].lu == 0) continue;
  1026. if(system.b[p].lu == 0) continue;
  1027.  
  1028. system.b[r].lu -= system.b[p].lu * system.A[r][p].lu;
  1029. }
  1030. }
  1031. return p;
  1032. }
  1033.  
  1034. // solves nodes backwards from limit-1
  1035. // if solveAll is true, solves all the nodes
  1036. // otherwise if solveNoIter is true, solves until !wantUpdate
  1037. // if both flags are false, solves until !wantIter
  1038. int luSolve()
  1039. {
  1040. for(int r = nets; --r;)
  1041. {
  1042. //printf("solve node %d\n", r);
  1043. for(int s = r+1; s < nets; ++s)
  1044. {
  1045. system.b[r].lu -= system.b[s].lu * system.A[r][s].lu;
  1046. }
  1047.  
  1048. system.b[r].lu *= system.A[r][r].lu;
  1049. }
  1050. return 1;
  1051. }
  1052. };
  1053.  
  1054.  
  1055. double fnGen(double t)
  1056. {
  1057. if(t < 0.0001) return 0;
  1058. return (fmod(2000*t, 1) > (.5+.4*sin(2*acos(-1)*100*t))) ? .25 : -.25;
  1059. }
  1060.  
  1061. int main()
  1062. {
  1063. /*
  1064.  
  1065. BJT test circuit
  1066.  
  1067. this is kinda good at catching problems :)
  1068.  
  1069. */
  1070. NetList * net = new NetList(8);
  1071.  
  1072. // node 1 is +12V
  1073. net->addComponent(new Voltage(+12, 1, 0));
  1074.  
  1075. // bias for base
  1076. net->addComponent(new Resistor(100e3, 2, 1));
  1077. net->addComponent(new Resistor(11e3, 2, 0));
  1078.  
  1079. // input cap and function
  1080. net->addComponent(new Capacitor(.1e-6, 2, 5));
  1081. net->addComponent(new Resistor(1e3, 5, 6));
  1082. net->addComponent(new Function(fnGen, 6, 0));
  1083.  
  1084. // collector resistance
  1085. net->addComponent(new Resistor(10e3, 1, 3));
  1086.  
  1087. // emitter resistance
  1088. net->addComponent(new Resistor(680, 4, 0));
  1089.  
  1090. // bjt on b:2,c:3,e:4
  1091. net->addComponent(new BJT(2,3,4));
  1092.  
  1093. // output emitter follower (direct rail collector?)
  1094. net->addComponent(new BJT(3, 1, 7));
  1095. net->addComponent(new Resistor(100e3, 7, 0));
  1096.  
  1097. net->addComponent(new Probe(7, 0));
  1098.  
  1099. net->buildSystem();
  1100.  
  1101. // get DC solution (optional)
  1102. net->simulateTick();
  1103. net->setTimeStep(1e-4);
  1104. net->printHeaders();
  1105.  
  1106. for(int i = 0; i < 25; ++i)
  1107. {
  1108. net->simulateTick();
  1109. }
  1110. net->printHeaders();
  1111.  
  1112. net->dumpMatrix();
  1113. };
Add Comment
Please, Sign In to add comment