1use crate::consts::{BOLTZ, CHARGE, KOVERQ, REFTEMP, ROOT2};
10use crate::dderivs::Dderivs;
11use crate::diode::pnjlim;
12use crate::disto::{DfCoeffs, DistoCtx, DistoMode, DistoSetupCtx, Kern, d1_term, df_term, jomega};
13use crate::matrix::{AcCtx, AnKind, InitMode, LoadCtx, MatrixBuilder, Slot, TRASH, Var};
14
15#[derive(Clone, Debug)]
18pub struct BjtModel {
19 pub npn: bool,
21 pub is: f64,
23 pub bf: f64,
25 pub nf: f64,
27 pub vaf: f64,
29 pub ikf: f64,
31 pub ise: f64,
33 pub ne: f64,
35 pub br: f64,
37 pub nr: f64,
39 pub var: f64,
41 pub ikr: f64,
43 pub isc: f64,
45 pub nc: f64,
47 pub rb: f64,
49 pub irb: f64,
51 pub rbm: Option<f64>,
53 pub re: f64,
55 pub rc: f64,
57 pub cje: f64,
59 pub vje: f64,
61 pub mje: f64,
63 pub tf: f64,
65 pub xtf: f64,
67 pub vtf: f64,
69 pub itf: f64,
71 pub ptf: f64,
73 pub cjc: f64,
75 pub vjc: f64,
77 pub mjc: f64,
79 pub xcjc: f64,
81 pub tr: f64,
83 pub cjs: f64,
85 pub vjs: f64,
87 pub mjs: f64,
89 pub xtb: f64,
91 pub eg: f64,
93 pub xti: f64,
95 pub fc: Option<f64>,
97 pub kf: f64,
99 pub af: f64,
100 pub tnom: Option<f64>,
102}
103
104impl BjtModel {
105 pub fn new(npn: bool) -> Self {
106 BjtModel {
107 npn,
108 is: 1e-16,
109 bf: 100.0,
110 nf: 1.0,
111 vaf: 0.0,
112 ikf: 0.0,
113 ise: 0.0,
114 ne: 1.5,
115 br: 1.0,
116 nr: 1.0,
117 var: 0.0,
118 ikr: 0.0,
119 isc: 0.0,
120 nc: 2.0,
121 rb: 0.0,
122 irb: 0.0,
123 rbm: None,
124 re: 0.0,
125 rc: 0.0,
126 cje: 0.0,
127 vje: 0.75,
128 mje: 0.33,
129 tf: 0.0,
130 xtf: 0.0,
131 vtf: 0.0,
132 itf: 0.0,
133 ptf: 0.0,
134 cjc: 0.0,
135 vjc: 0.75,
136 mjc: 0.33,
137 xcjc: 1.0,
138 tr: 0.0,
139 cjs: 0.0,
140 vjs: 0.75,
141 mjs: 0.0,
142 xtb: 0.0,
143 eg: 1.11,
144 xti: 3.0,
145 fc: None,
146 kf: 0.0,
147 af: 1.0,
148 tnom: None,
149 }
150 }
151}
152
153pub(crate) struct BjtTemp {
155 pub temp: f64,
159 pub type_: f64, pub inv_early_f: f64,
161 pub inv_early_r: f64,
162 pub inv_roll_f: f64,
163 pub inv_roll_r: f64,
164 pub col_conduct: f64,
165 pub emit_conduct: f64,
166 pub base_resist: f64,
167 pub min_base_resist: f64,
168 pub irb: f64,
169 pub tt_vbc_factor: f64,
170 pub excess_phase_factor: f64,
171 pub f2: f64,
172 pub f3: f64,
173 pub f6: f64,
174 pub f7: f64,
175 pub t_sat_cur: f64,
177 pub t_beta_f: f64,
178 pub t_beta_r: f64,
179 pub t_be_leak: f64,
180 pub t_bc_leak: f64,
181 pub t_be_cap: f64,
182 pub t_be_pot: f64,
183 pub t_bc_cap: f64,
184 pub t_bc_pot: f64,
185 pub t_dep_cap: f64,
186 pub t_f1: f64,
187 pub t_f4: f64,
188 pub t_f5: f64,
189 pub t_vcrit: f64,
190 pub vt: f64,
191 pub nf: f64,
193 pub nr: f64,
194 pub ne: f64,
195 pub nc: f64,
196 pub tf: f64,
197 pub tr: f64,
198 pub xtf: f64,
199 pub itf: f64,
200 pub xcjc: f64,
201 pub cjs: f64,
202 pub vjs: f64,
203 pub mjs: f64,
204 pub mje: f64,
205 pub mjc: f64,
206 pub kf: f64,
207 pub af: f64,
208 pub notes: Vec<String>,
209}
210
211fn egfet(t: f64) -> f64 {
212 1.16 - (7.02e-4 * t * t) / (t + 1108.0)
213}
214
215impl BjtTemp {
216 pub fn new(name: &str, m: &BjtModel, temp: f64, default_tnom: f64) -> Self {
217 let mut notes = Vec::new();
218 let tnom = m.tnom.unwrap_or(default_tnom);
219 let fact1 = tnom / REFTEMP;
220
221 let fc = match m.fc {
222 Some(fc) if fc > 0.9999 => {
223 notes.push(format!("BJT model {name}, parameter fc limited to 0.9999"));
224 0.9999
225 }
226 Some(fc) => fc,
227 None => 0.5,
228 };
229 let xfc = f64::ln(1.0 - fc);
230 let f2 = f64::exp((1.0 + m.mje) * xfc);
231 let f3 = 1.0 - fc * (1.0 + m.mje);
232 let f6 = f64::exp((1.0 + m.mjc) * xfc);
233 let f7 = 1.0 - fc * (1.0 + m.mjc);
234
235 let inv = |given: f64| if given != 0.0 { 1.0 / given } else { 0.0 };
236
237 let vt = temp * KOVERQ;
238 let fact2 = temp / REFTEMP;
239 let eg_t = egfet(temp);
240 let arg = -eg_t / (2.0 * BOLTZ * temp) + 1.1150877 / (BOLTZ * (REFTEMP + REFTEMP));
241 let pbfact = -2.0 * vt * (1.5 * f64::ln(fact2) + CHARGE * arg);
242 let ratlog = f64::ln(temp / tnom);
243 let ratio1 = temp / tnom - 1.0;
244 let factlog = ratio1 * m.eg / vt + m.xti * ratlog;
245 let factor = f64::exp(factlog);
246 let t_sat_cur = m.is * factor;
247 let bfactor = f64::exp(ratlog * m.xtb);
248 let t_beta_f = m.bf * bfactor;
249 let t_beta_r = m.br * bfactor;
250 let t_be_leak = m.ise * f64::exp(factlog / m.ne) / bfactor;
251 let t_bc_leak = m.isc * f64::exp(factlog / m.nc) / bfactor;
252
253 let junction = |pot: f64, mj: f64, cap: f64| {
254 let pbo = (pot - {
255 let eg_nom = egfet(tnom);
257 let arg1 = -eg_nom / (BOLTZ * 2.0 * tnom) + 1.1150877 / (2.0 * BOLTZ * REFTEMP);
258 let vtnom = tnom * KOVERQ;
259 -2.0 * vtnom * (1.5 * f64::ln(fact1) + CHARGE * arg1)
260 }) / fact1;
261 let gmaold = (pot - pbo) / pbo;
262 let mut tcap = cap / (1.0 + mj * (4e-4 * (tnom - REFTEMP) - gmaold));
263 let tpot = fact2 * pbo + pbfact;
264 let gmanew = (tpot - pbo) / pbo;
265 tcap *= 1.0 + mj * (4e-4 * (temp - REFTEMP) - gmanew);
266 (tcap, tpot)
267 };
268 let (t_be_cap, t_be_pot) = junction(m.vje, m.mje, m.cje);
269 let (t_bc_cap, t_bc_pot) = junction(m.vjc, m.mjc, m.cjc);
270
271 let t_dep_cap = fc * t_be_pot;
272 let t_f1 = t_be_pot * (1.0 - f64::exp((1.0 - m.mje) * xfc)) / (1.0 - m.mje);
273 let t_f4 = fc * t_bc_pot;
274 let t_f5 = t_bc_pot * (1.0 - f64::exp((1.0 - m.mjc) * xfc)) / (1.0 - m.mjc);
275 let t_vcrit = vt * f64::ln(vt / (ROOT2 * m.is));
276
277 BjtTemp {
278 temp,
279 type_: if m.npn { 1.0 } else { -1.0 },
280 inv_early_f: inv(m.vaf),
281 inv_early_r: inv(m.var),
282 inv_roll_f: inv(m.ikf),
283 inv_roll_r: inv(m.ikr),
284 col_conduct: inv(m.rc),
285 emit_conduct: inv(m.re),
286 base_resist: m.rb,
287 min_base_resist: m.rbm.unwrap_or(m.rb),
288 irb: m.irb,
289 tt_vbc_factor: if m.vtf != 0.0 {
290 1.0 / (m.vtf * 1.44)
291 } else {
292 0.0
293 },
294 excess_phase_factor: (m.ptf / (180.0 / std::f64::consts::PI)) * m.tf,
295 f2,
296 f3,
297 f6,
298 f7,
299 t_sat_cur,
300 t_beta_f,
301 t_beta_r,
302 t_be_leak,
303 t_bc_leak,
304 t_be_cap,
305 t_be_pot,
306 t_bc_cap,
307 t_bc_pot,
308 t_dep_cap,
309 t_f1,
310 t_f4,
311 t_f5,
312 t_vcrit,
313 vt,
314 nf: m.nf,
315 nr: m.nr,
316 ne: m.ne,
317 nc: m.nc,
318 tf: m.tf,
319 tr: m.tr,
320 xtf: m.xtf,
321 itf: m.itf,
322 xcjc: m.xcjc,
323 cjs: m.cjs,
324 vjs: m.vjs,
325 mjs: m.mjs,
326 mje: m.mje,
327 mjc: m.mjc,
328 kf: m.kf,
329 af: m.af,
330 notes,
331 }
332 }
333}
334
335const VBE: usize = 0;
337const VBC: usize = 1;
338const CC: usize = 2;
339const CB: usize = 3;
340const GPI: usize = 4;
341const GMU: usize = 5;
342const GM: usize = 6;
343const GO: usize = 7;
344const QBE: usize = 8;
345const CQBE: usize = 9;
346const QBC: usize = 10;
347const CQBC: usize = 11;
348const QCS: usize = 12;
349const CQCS: usize = 13;
350const QBX: usize = 14;
351const CQBX: usize = 15;
352const GX: usize = 16;
353const CEXBC: usize = 17;
354const GEQCB: usize = 18;
355const GCCS: usize = 19;
356const GEQBX: usize = 20;
357pub(crate) const BJT_NUM_STATES: usize = 21;
358
359pub(crate) struct BjtI {
360 col: Var,
361 base: Var,
362 emit: Var,
363 subst: Var,
364 col_p: Var,
365 base_p: Var,
366 emit_p: Var,
367 t: BjtTemp,
368 area: f64,
369 off: bool,
370 ic_vbe: f64,
371 ic_vce: f64,
372 ic_given: crate::devices::IcGiven,
373 st: usize,
374 s: [Slot; 23],
375 d: BjtDistoCoeffs,
376}
377
378#[derive(Clone, Copy, Debug, Default)]
384struct BjtDistoCoeffs {
385 ic: DfCoeffs,
386 ib: DfCoeffs,
387 ibb: DfCoeffs,
388 qbe: DfCoeffs,
389 capbc2: f64,
390 capbc3: f64,
391 capbx2: f64,
392 capbx3: f64,
393 capsc2: f64,
394 capsc3: f64,
395}
396
397pub(crate) fn taylor_coeffs(d: &Dderivs, typ: f64) -> DfCoeffs {
402 DfCoeffs {
403 cxx: 0.5 * typ * d.d2_p2,
404 cyy: 0.5 * typ * d.d2_q2,
405 czz: 0.5 * typ * d.d2_r2,
406 cxy: typ * d.d2_pq,
407 cyz: typ * d.d2_qr,
408 cxz: typ * d.d2_pr,
409 cxxx: d.d3_p3 / 6.,
410 cyyy: d.d3_q3 / 6.,
411 czzz: d.d3_r3 / 6.,
412 cxxy: 0.5 * d.d3_p2q,
413 cxxz: 0.5 * d.d3_p2r,
414 cxyy: 0.5 * d.d3_pq2,
415 cyyz: 0.5 * d.d3_q2r,
416 cxzz: 0.5 * d.d3_pr2,
417 cyzz: 0.5 * d.d3_qr2,
418 cxyz: d.d3_pqr,
419 }
420}
421
422fn xy_only(c: DfCoeffs) -> DfCoeffs {
425 DfCoeffs {
426 cxx: c.cxx,
427 cyy: c.cyy,
428 cxy: c.cxy,
429 cxxx: c.cxxx,
430 cyyy: c.cyyy,
431 cxxy: c.cxxy,
432 cxyy: c.cxyy,
433 ..DfCoeffs::default()
434 }
435}
436
437fn delay(r: f64, i: f64, theta: f64) -> (f64, f64) {
440 (
441 r * theta.cos() + i * theta.sin(),
442 i * theta.cos() - r * theta.sin(),
443 )
444}
445
446fn delayed(mode: DistoMode, x: &Kern, td: f64, omega1: f64, omega2: f64) -> Kern {
450 let mut z = *x;
451 (z.r1h1, z.i1h1) = delay(x.r1h1, x.i1h1, omega1 * td);
452 match mode {
453 DistoMode::TwoF1 => {}
454 DistoMode::ThrF1 => {
455 (z.r2h11, z.i2h11) = delay(x.r2h11, x.i2h11, 2.0 * omega1 * td);
456 }
457 DistoMode::F1pF2 => {
458 (z.r1h2, z.i1h2) = delay(x.r1h2, x.i1h2, omega2 * td);
459 }
460 DistoMode::F1mF2 => {
461 (z.r1h2, z.i1h2) = delay(x.r1h2, x.i1h2, -omega2 * td);
462 }
463 DistoMode::TwoF1mF2 => {
464 (z.r1h2, z.i1h2) = delay(x.r1h2, x.i1h2, -omega2 * td);
465 (z.r2h11, z.i2h11) = delay(x.r2h11, x.i2h11, 2.0 * omega1 * td);
466 (z.h2f1f2, z.ih2f1f2) = delay(x.h2f1f2, x.ih2f1f2, (omega1 - omega2) * td);
467 }
468 }
469 z
470}
471
472const COL_COLP: usize = 0;
474const BASE_BASEP: usize = 1;
475const EMIT_EMITP: usize = 2;
476const COLP_COL: usize = 3;
477const COLP_BASEP: usize = 4;
478const COLP_EMITP: usize = 5;
479const BASEP_BASE: usize = 6;
480const BASEP_COLP: usize = 7;
481const BASEP_EMITP: usize = 8;
482const EMITP_EMIT: usize = 9;
483const EMITP_COLP: usize = 10;
484const EMITP_BASEP: usize = 11;
485const COL_COL: usize = 12;
486const BASE_BASE: usize = 13;
487const EMIT_EMIT: usize = 14;
488const COLP_COLP: usize = 15;
489const BASEP_BASEP: usize = 16;
490const EMITP_EMITP: usize = 17;
491const SUBST_SUBST: usize = 18;
492const COLP_SUBST: usize = 19;
493const SUBST_COLP: usize = 20;
494const BASE_COLP: usize = 21;
495const COLP_BASE: usize = 22;
496
497impl BjtI {
498 #[allow(clippy::too_many_arguments)]
499 pub fn new(
500 nodes: [Var; 4], primes: [Var; 3], t: BjtTemp,
503 area: f64,
504 off: bool,
505 ic: (f64, f64), st: usize,
507 ) -> Self {
508 BjtI {
509 col: nodes[0],
510 base: nodes[1],
511 emit: nodes[2],
512 subst: nodes[3],
513 col_p: primes[0],
514 base_p: primes[1],
515 emit_p: primes[2],
516 t,
517 area,
518 off,
519 ic_vbe: ic.0,
520 ic_vce: ic.1,
521 ic_given: crate::devices::IcGiven::ALL,
522 st,
523 s: [TRASH; 23],
524 d: BjtDistoCoeffs::default(),
525 }
526 }
527}
528
529impl crate::devices::Device for BjtI {
530 fn par_load(&self) -> bool {
531 true
532 }
533
534 fn set_ic(&mut self, rhs: &[f64]) {
537 use crate::devices::getic;
538 let g = self.ic_given;
539 getic(g, 0, &mut self.ic_vbe, rhs, self.base, self.emit);
540 getic(g, 1, &mut self.ic_vce, rhs, self.col, self.emit);
541 }
542
543 fn set_ic_given(&mut self, given: crate::devices::IcGiven) {
544 self.ic_given = given;
545 }
546
547 fn conv_test(&self, ctx: &crate::matrix::ConvCtx) -> bool {
550 let typ = self.t.type_;
551 let st = self.st;
552 let vbe = typ * (ctx.sol[self.base_p] - ctx.sol[self.emit_p]);
553 let vbc = typ * (ctx.sol[self.base_p] - ctx.sol[self.col_p]);
554 let delvbe = vbe - ctx.states.get(0, st + VBE);
555 let delvbc = vbc - ctx.states.get(0, st + VBC);
556 let cc = ctx.states.get(0, st + CC);
557 let cb = ctx.states.get(0, st + CB);
558 let gm = ctx.states.get(0, st + GM);
559 let go = ctx.states.get(0, st + GO);
560 let gpi = ctx.states.get(0, st + GPI);
561 let gmu = ctx.states.get(0, st + GMU);
562 let cchat = cc + (gm + go) * delvbe - (go + gmu) * delvbc;
563 let cbhat = cb + gpi * delvbe + gmu * delvbc;
564 let tol = ctx.reltol * f64::max(cchat.abs(), cc.abs()) + ctx.abstol;
565 if (cchat - cc).abs() > tol {
566 return false;
567 }
568 let tol = ctx.reltol * f64::max(cbhat.abs(), cb.abs()) + ctx.abstol;
569 (cbhat - cb).abs() <= tol
570 }
571
572 fn noise_suffixes(&self) -> Option<&'static [&'static str]> {
575 Some(&["_rc", "_rb", "_re", "_ic", "_ib", "_1overf", ""])
576 }
577
578 fn noise_dens(&self, ctx: &crate::matrix::NoiseCtx) -> Vec<f64> {
579 const N_MINLOG: f64 = 1e-38;
580 let st = self.st;
581 let temp = self.t.temp;
584 let rc = ctx.thermal_at(self.col_p, self.col, self.t.col_conduct * self.area, temp);
585 let rb = ctx.thermal_at(self.base_p, self.base, ctx.states.get(0, st + GX), temp);
586 let re = ctx.thermal_at(
587 self.emit_p,
588 self.emit,
589 self.t.emit_conduct * self.area,
590 temp,
591 );
592 let ic = ctx.shot(self.col_p, self.emit_p, ctx.states.get(0, st + CC));
593 let ib = ctx.shot(self.base_p, self.emit_p, ctx.states.get(0, st + CB));
594 let cb = ctx.states.get(0, st + CB);
595 let flicker = ctx.flicker(
596 self.base_p,
597 self.emit_p,
598 self.t.kf * f64::exp(self.t.af * f64::ln(f64::max(cb.abs(), N_MINLOG))),
599 1.0,
600 );
601 let total = rc + rb + re + ic + ib + flicker;
602 vec![rc, rb, re, ic, ib, flicker, total]
603 }
604
605 fn alloc(&mut self, mb: &mut MatrixBuilder) {
606 let (c, b, e, s) = (self.col, self.base, self.emit, self.subst);
607 let (cp, bp, ep) = (self.col_p, self.base_p, self.emit_p);
608 self.s = [
609 mb.slot(c, cp),
610 mb.slot(b, bp),
611 mb.slot(e, ep),
612 mb.slot(cp, c),
613 mb.slot(cp, bp),
614 mb.slot(cp, ep),
615 mb.slot(bp, b),
616 mb.slot(bp, cp),
617 mb.slot(bp, ep),
618 mb.slot(ep, e),
619 mb.slot(ep, cp),
620 mb.slot(ep, bp),
621 mb.slot(c, c),
622 mb.slot(b, b),
623 mb.slot(e, e),
624 mb.slot(cp, cp),
625 mb.slot(bp, bp),
626 mb.slot(ep, ep),
627 mb.slot(s, s),
628 mb.slot(cp, s),
629 mb.slot(s, cp),
630 mb.slot(b, cp),
631 mb.slot(cp, b),
632 ];
633 }
634
635 #[allow(clippy::too_many_lines)]
636 fn load(&mut self, ctx: &mut LoadCtx) {
637 let t = &self.t;
638 let typ = t.type_;
639 let vt = t.vt;
640 let st = self.st;
641
642 let csat = t.t_sat_cur * self.area;
643 let rbpr = t.min_base_resist / self.area;
644 let rbpi = t.base_resist / self.area - rbpr;
645 let gcpr = t.col_conduct * self.area;
646 let gepr = t.emit_conduct * self.area;
647 let oik = t.inv_roll_f / self.area;
648 let c2 = t.t_be_leak * self.area;
649 let vte = t.ne * vt;
650 let oikr = t.inv_roll_r / self.area;
651 let c4 = t.t_bc_leak * self.area;
652 let vtc = t.nc * vt;
653 let td = t.excess_phase_factor;
654 let xjrb = t.irb * self.area;
655
656 let mut gccs = 0.0;
657 let mut geqbx = 0.0;
658 let mut geqcb = 0.0;
659
660 let mut icheck = true;
662 let (mut vbe, mut vbc);
663 let (vbx, vcs);
664 match ctx.mode {
665 InitMode::Smsig => {
666 vbe = ctx.states.get(0, st + VBE);
667 vbc = ctx.states.get(0, st + VBC);
668 vbx = typ * (ctx.sol[self.base] - ctx.sol[self.col_p]);
669 vcs = typ * (ctx.sol[self.subst] - ctx.sol[self.col_p]);
670 }
671 InitMode::Tran => {
672 vbe = ctx.states.get(1, st + VBE);
673 vbc = ctx.states.get(1, st + VBC);
674 if ctx.kind == AnKind::Tran && ctx.uic {
675 vbx = typ * (self.ic_vbe - self.ic_vce);
676 vcs = 0.0;
677 } else {
678 vbx = typ * (ctx.sol[self.base] - ctx.sol[self.col_p]);
679 vcs = typ * (ctx.sol[self.subst] - ctx.sol[self.col_p]);
680 }
681 }
682 InitMode::Jct if ctx.kind == AnKind::TranOp && ctx.uic => {
683 vbe = typ * self.ic_vbe;
684 let vce = typ * self.ic_vce;
685 vbc = vbe - vce;
686 vbx = vbc;
687 vcs = 0.0;
688 }
689 InitMode::Jct if !self.off => {
690 vbe = t.t_vcrit;
691 vbc = 0.0;
692 vcs = 0.0;
694 vbx = 0.0;
695 }
696 InitMode::Jct => {
698 vbe = 0.0;
699 vbc = 0.0;
700 vcs = 0.0;
701 vbx = 0.0;
702 }
703 InitMode::Fix if self.off => {
704 vbe = 0.0;
705 vbc = 0.0;
706 vcs = 0.0;
707 vbx = 0.0;
708 }
709 _ => {
710 if ctx.mode == InitMode::Pred {
711 let xfact = ctx.pred_xfact;
712 let s1 = |o: usize| ctx.states.get(1, st + o);
713 vbe = (1.0 + xfact) * s1(VBE) - xfact * ctx.states.get(2, st + VBE);
714 vbc = (1.0 + xfact) * s1(VBC) - xfact * ctx.states.get(2, st + VBC);
715 for o in [VBE, VBC, CC, CB, GPI, GMU, GM, GO, GX] {
716 let v = ctx.states.get(1, st + o);
717 ctx.states.set(0, st + o, v);
718 }
719 } else {
720 vbe = typ * (ctx.sol[self.base_p] - ctx.sol[self.emit_p]);
721 vbc = typ * (ctx.sol[self.base_p] - ctx.sol[self.col_p]);
722 }
723 vbx = typ * (ctx.sol[self.base] - ctx.sol[self.col_p]);
724 vcs = typ * (ctx.sol[self.subst] - ctx.sol[self.col_p]);
725 let mut ichk1 = true;
727 vbe = pnjlim(vbe, ctx.states.get(0, st + VBE), vt, t.t_vcrit, &mut icheck);
728 vbc = pnjlim(vbc, ctx.states.get(0, st + VBC), vt, t.t_vcrit, &mut ichk1);
729 if ichk1 {
730 icheck = true;
731 }
732 }
733 }
734
735 let e = crate::consts::E;
743 let vtn = vt * t.nf;
744 let (cbe, gbe, mut cben, mut gben);
745 if vbe >= -3.0 * vtn {
746 let evbe = f64::exp(vbe / vtn);
747 cbe = csat * (evbe - 1.0);
748 gbe = csat * evbe / vtn;
749 } else {
750 let arg = 3.0 * vtn / (vbe * e);
751 let arg = arg * arg * arg;
752 cbe = -csat * (1.0 + arg);
753 gbe = csat * 3.0 * arg / vbe;
754 }
755 if c2 == 0.0 {
756 cben = 0.0;
757 gben = 0.0;
758 } else if vbe >= -3.0 * vte {
759 let evben = f64::exp(vbe / vte);
760 cben = c2 * (evben - 1.0);
761 gben = c2 * evben / vte;
762 } else {
763 let arg = 3.0 * vte / (vbe * e);
764 let arg = arg * arg * arg;
765 cben = -c2 * (1.0 + arg);
766 gben = c2 * 3.0 * arg / vbe;
767 }
768 gben += ctx.gmin;
769 cben += ctx.gmin * vbe;
770 let vtn = vt * t.nr;
771 let (cbc, gbc, mut cbcn, mut gbcn);
772 if vbc >= -3.0 * vtn {
773 let evbc = f64::exp(vbc / vtn);
774 cbc = csat * (evbc - 1.0);
775 gbc = csat * evbc / vtn;
776 } else {
777 let arg = 3.0 * vtn / (vbc * e);
778 let arg = arg * arg * arg;
779 cbc = -csat * (1.0 + arg);
780 gbc = csat * 3.0 * arg / vbc;
781 }
782 if c4 == 0.0 {
783 cbcn = 0.0;
784 gbcn = 0.0;
785 } else if vbc >= -3.0 * vtc {
786 let evbcn = f64::exp(vbc / vtc);
787 cbcn = c4 * (evbcn - 1.0);
788 gbcn = c4 * evbcn / vtc;
789 } else {
790 let arg = 3.0 * vtc / (vbc * e);
791 let arg = arg * arg * arg;
792 cbcn = -c4 * (1.0 + arg);
793 gbcn = c4 * 3.0 * arg / vbc;
794 }
795 gbcn += ctx.gmin;
796 cbcn += ctx.gmin * vbc;
797
798 let q1 = 1.0 / (1.0 - t.inv_early_f * vbc - t.inv_early_r * vbe);
800 let (qb, dqbdve, dqbdvc);
801 if oik == 0.0 && oikr == 0.0 {
802 qb = q1;
803 dqbdve = q1 * qb * t.inv_early_r;
804 dqbdvc = q1 * qb * t.inv_early_f;
805 } else {
806 let q2 = oik * cbe + oikr * cbc;
807 let arg = f64::max(0.0, 1.0 + 4.0 * q2);
808 let mut sqarg = 1.0;
809 if arg != 0.0 {
810 sqarg = arg.sqrt();
811 }
812 qb = q1 * (1.0 + sqarg) / 2.0;
813 dqbdve = q1 * (qb * t.inv_early_r + oik * gbe / sqarg);
814 dqbdvc = q1 * (qb * t.inv_early_f + oikr * gbc / sqarg);
815 }
816
817 let mut cc = 0.0;
819 let mut cex = cbe;
820 let mut gex = gbe;
821 if ctx.kind == AnKind::Tran && td != 0.0 {
822 let arg1 = ctx.delta / td;
823 let arg2 = 3.0 * arg1;
824 let arg1 = arg2 * arg1;
825 let denom = 1.0 + arg1 + arg2;
826 let arg3 = arg1 / denom;
827 if ctx.mode == InitMode::Tran {
828 ctx.states.set(1, st + CEXBC, cbe / qb);
829 let v = ctx.states.get(1, st + CEXBC);
830 ctx.states.set(2, st + CEXBC, v);
831 }
832 cc = (ctx.states.get(1, st + CEXBC) * (1.0 + ctx.pred_xfact + arg2)
833 - ctx.states.get(2, st + CEXBC) * ctx.pred_xfact)
834 / denom;
835 cex = cbe * arg3;
836 gex = gbe * arg3;
837 ctx.states.set(0, st + CEXBC, cc + cex / qb);
838 }
839
840 let mut cc = cc + (cex - cbc) / qb - cbc / t.t_beta_r - cbcn;
842 let mut cb = cbe / t.t_beta_f + cben + cbc / t.t_beta_r + cbcn;
843 let mut gx = rbpr + rbpi / qb;
844 if xjrb != 0.0 {
845 let arg1 = f64::max(cb / xjrb, 1e-9);
846 let arg2 = (-1.0 + (1.0 + 14.59025 * arg1).sqrt()) / 2.4317 / arg1.sqrt();
847 let arg1t = arg2.tan();
848 gx = rbpr + 3.0 * rbpi * (arg1t - arg2) / arg2 / arg1t / arg1t;
849 }
850 if gx != 0.0 {
851 gx = 1.0 / gx;
852 }
853 let mut gpi = gbe / t.t_beta_f + gben;
854 let mut gmu = gbc / t.t_beta_r + gbcn;
855 let go = (gbc + (cex - cbc) * dqbdvc / qb) / qb;
856 let gm = (gex - (cex - cbc) * dqbdve / qb) / qb - go;
857
858 let smsig = ctx.mode == InitMode::Smsig;
860 let tranop_uic = ctx.kind == AnKind::TranOp && ctx.uic;
861 if ctx.kind == AnKind::Tran || tranop_uic || smsig {
862 let tf = t.tf;
863 let tr = t.tr;
864 let czbe = t.t_be_cap * self.area;
865 let pe = t.t_be_pot;
866 let xme = t.mje;
867 let cdis = t.xcjc;
868 let ctot = t.t_bc_cap * self.area;
869 let czbc = ctot * cdis;
870 let czbx = ctot - czbc;
871 let pc = t.t_bc_pot;
872 let xmc = t.mjc;
873 let fcpe = t.t_dep_cap;
874 let czcs = t.cjs * self.area;
875 let ps = t.vjs;
876 let xms = t.mjs;
877 let xtf = t.xtf;
878 let ovtf = t.tt_vbc_factor;
879 let xjtf = t.itf * self.area;
880
881 let (mut cbe_q, mut gbe_q) = (cbe, gbe);
882 if tf != 0.0 && vbe > 0.0 {
883 let mut argtf = 0.0;
884 let mut arg2 = 0.0;
885 let mut arg3 = 0.0;
886 if xtf != 0.0 {
887 argtf = xtf;
888 if ovtf != 0.0 {
889 argtf *= f64::exp(vbc * ovtf);
890 }
891 arg2 = argtf;
892 if xjtf != 0.0 {
893 let temp = cbe / (cbe + xjtf);
894 argtf = argtf * temp * temp;
895 arg2 = argtf * (3.0 - temp - temp);
896 }
897 arg3 = cbe * argtf * ovtf;
898 }
899 cbe_q = cbe * (1.0 + argtf) / qb;
900 gbe_q = (gbe * (1.0 + arg2) - cbe_q * dqbdve) / qb;
901 geqcb = tf * (arg3 - cbe_q * dqbdvc) / qb;
902 }
903
904 #[allow(clippy::needless_late_init)] let capbe;
906 if vbe < fcpe {
907 let arg = 1.0 - vbe / pe;
908 let sarg = f64::exp(-xme * f64::ln(arg));
909 ctx.states.set(
910 0,
911 st + QBE,
912 tf * cbe_q + pe * czbe * (1.0 - arg * sarg) / (1.0 - xme),
913 );
914 capbe = tf * gbe_q + czbe * sarg;
915 } else {
916 let f1 = t.t_f1;
917 let f2 = t.f2;
918 let f3 = t.f3;
919 let czbef2 = czbe / f2;
920 ctx.states.set(
921 0,
922 st + QBE,
923 tf * cbe_q
924 + czbe * f1
925 + czbef2
926 * (f3 * (vbe - fcpe) + (xme / (pe + pe)) * (vbe * vbe - fcpe * fcpe)),
927 );
928 capbe = tf * gbe_q + czbef2 * (f3 + xme * vbe / pe);
929 }
930 let fcpc = t.t_f4;
931 let f1 = t.t_f5;
932 let f2 = t.f6;
933 let f3 = t.f7;
934 #[allow(clippy::needless_late_init)] let capbc;
936 if vbc < fcpc {
937 let arg = 1.0 - vbc / pc;
938 let sarg = f64::exp(-xmc * f64::ln(arg));
939 ctx.states.set(
940 0,
941 st + QBC,
942 tr * cbc + pc * czbc * (1.0 - arg * sarg) / (1.0 - xmc),
943 );
944 capbc = tr * gbc + czbc * sarg;
945 } else {
946 let czbcf2 = czbc / f2;
947 ctx.states.set(
948 0,
949 st + QBC,
950 tr * cbc
951 + czbc * f1
952 + czbcf2
953 * (f3 * (vbc - fcpc) + (xmc / (pc + pc)) * (vbc * vbc - fcpc * fcpc)),
954 );
955 capbc = tr * gbc + czbcf2 * (f3 + xmc * vbc / pc);
956 }
957 #[allow(clippy::needless_late_init)] let capbx;
959 if vbx < fcpc {
960 let arg = 1.0 - vbx / pc;
961 let sarg = f64::exp(-xmc * f64::ln(arg));
962 ctx.states
963 .set(0, st + QBX, pc * czbx * (1.0 - arg * sarg) / (1.0 - xmc));
964 capbx = czbx * sarg;
965 } else {
966 let czbxf2 = czbx / f2;
967 ctx.states.set(
968 0,
969 st + QBX,
970 czbx * f1
971 + czbxf2
972 * (f3 * (vbx - fcpc) + (xmc / (pc + pc)) * (vbx * vbx - fcpc * fcpc)),
973 );
974 capbx = czbxf2 * (f3 + xmc * vbx / pc);
975 }
976 #[allow(clippy::needless_late_init)] let capcs;
978 if vcs < 0.0 {
979 let arg = 1.0 - vcs / ps;
980 let sarg = f64::exp(-xms * f64::ln(arg));
981 ctx.states
982 .set(0, st + QCS, ps * czcs * (1.0 - arg * sarg) / (1.0 - xms));
983 capcs = czcs * sarg;
984 } else {
985 ctx.states
986 .set(0, st + QCS, vcs * czcs * (1.0 + xms * vcs / (2.0 * ps)));
987 capcs = czcs * (1.0 + xms * vcs / ps);
988 }
989
990 if !tranop_uic {
991 if smsig {
992 ctx.states.set(0, st + CQBE, capbe);
995 ctx.states.set(0, st + CQBC, capbc);
996 ctx.states.set(0, st + CQCS, capcs);
997 ctx.states.set(0, st + CQBX, capbx);
998 ctx.states.set(0, st + CEXBC, geqcb);
999 return;
1000 }
1001 if ctx.mode == InitMode::Tran {
1002 for o in [QBE, QBC, QBX, QCS] {
1003 let v = ctx.states.get(0, st + o);
1004 ctx.states.set(1, st + o, v);
1005 }
1006 }
1007 let (geq, _ceq) = ctx.integrate(capbe, st + QBE);
1008 geqcb *= ctx.ag[0];
1009 gpi += geq;
1010 cb += ctx.states.get(0, st + CQBE);
1011 let (geq, _ceq) = ctx.integrate(capbc, st + QBC);
1012 gmu += geq;
1013 cb += ctx.states.get(0, st + CQBC);
1014 cc -= ctx.states.get(0, st + CQBC);
1015 if ctx.mode == InitMode::Tran {
1016 for o in [CQBE, CQBC] {
1017 let v = ctx.states.get(0, st + o);
1018 ctx.states.set(1, st + o, v);
1019 }
1020 }
1021 }
1022
1023 if ctx.kind == AnKind::Tran {
1025 let (g, _) = ctx.integrate(capcs, st + QCS);
1026 gccs = g;
1027 let (g, _) = ctx.integrate(capbx, st + QBX);
1028 geqbx = g;
1029 if ctx.mode == InitMode::Tran {
1030 for o in [CQBX, CQCS] {
1031 let v = ctx.states.get(0, st + o);
1032 ctx.states.set(1, st + o, v);
1033 }
1034 }
1035 }
1036 }
1037
1038 if !(ctx.mode == InitMode::Fix && self.off) && icheck {
1040 ctx.noncon = true;
1041 }
1042
1043 ctx.states.set(0, st + VBE, vbe);
1044 ctx.states.set(0, st + VBC, vbc);
1045 ctx.states.set(0, st + CC, cc);
1046 ctx.states.set(0, st + CB, cb);
1047 ctx.states.set(0, st + GPI, gpi);
1048 ctx.states.set(0, st + GMU, gmu);
1049 ctx.states.set(0, st + GM, gm);
1050 ctx.states.set(0, st + GO, go);
1051 ctx.states.set(0, st + GX, gx);
1052 ctx.states.set(0, st + GEQCB, geqcb);
1053 ctx.states.set(0, st + GCCS, gccs);
1054 ctx.states.set(0, st + GEQBX, geqbx);
1055
1056 let ceqcs = typ * (ctx.states.get(0, st + CQCS) - vcs * gccs);
1058 let ceqbx = typ * (ctx.states.get(0, st + CQBX) - vbx * geqbx);
1059 let ceqbe = typ * (cc + cb - vbe * (gm + go + gpi) + vbc * (go - geqcb));
1060 let ceqbc = typ * (-cc + vbe * (gm + go) - vbc * (gmu + go));
1061
1062 ctx.add_rhs(self.base, -ceqbx);
1063 ctx.add_rhs(self.col_p, ceqcs + ceqbx + ceqbc);
1064 ctx.add_rhs(self.base_p, -ceqbe - ceqbc);
1065 ctx.add_rhs(self.emit_p, ceqbe);
1066 ctx.add_rhs(self.subst, -ceqcs);
1067
1068 ctx.add(self.s[COL_COL], gcpr);
1070 ctx.add(self.s[BASE_BASE], gx + geqbx);
1071 ctx.add(self.s[EMIT_EMIT], gepr);
1072 ctx.add(self.s[COLP_COLP], gmu + go + gcpr + gccs + geqbx);
1073 ctx.add(self.s[BASEP_BASEP], gx + gpi + gmu + geqcb);
1074 ctx.add(self.s[EMITP_EMITP], gpi + gepr + gm + go);
1075 ctx.add(self.s[COL_COLP], -gcpr);
1076 ctx.add(self.s[BASE_BASEP], -gx);
1077 ctx.add(self.s[EMIT_EMITP], -gepr);
1078 ctx.add(self.s[COLP_COL], -gcpr);
1079 ctx.add(self.s[COLP_BASEP], -gmu + gm);
1080 ctx.add(self.s[COLP_EMITP], -gm - go);
1081 ctx.add(self.s[BASEP_BASE], -gx);
1082 ctx.add(self.s[BASEP_COLP], -gmu - geqcb);
1083 ctx.add(self.s[BASEP_EMITP], -gpi);
1084 ctx.add(self.s[EMITP_EMIT], -gepr);
1085 ctx.add(self.s[EMITP_COLP], -go + geqcb);
1086 ctx.add(self.s[EMITP_BASEP], -gpi - gm - geqcb);
1087 ctx.add(self.s[SUBST_SUBST], gccs);
1088 ctx.add(self.s[COLP_SUBST], -gccs);
1089 ctx.add(self.s[SUBST_COLP], -gccs);
1090 ctx.add(self.s[BASE_COLP], -geqbx);
1091 ctx.add(self.s[COLP_BASE], -geqbx);
1092 }
1093
1094 fn trunc(&self, t: &crate::integ::TerrCtx, time_step: &mut f64) {
1095 crate::integ::terr(self.st + QBE, t, time_step);
1096 crate::integ::terr(self.st + QBC, t, time_step);
1097 crate::integ::terr(self.st + QCS, t, time_step);
1098 }
1099
1100 fn load_ac(&mut self, ctx: &mut AcCtx) {
1102 let st = self.st;
1103 let gcpr = self.t.col_conduct * self.area;
1104 let gepr = self.t.emit_conduct * self.area;
1105 let gpi = ctx.states.get(0, st + GPI);
1106 let gmu = ctx.states.get(0, st + GMU);
1107 let mut gm = ctx.states.get(0, st + GM);
1108 let go = ctx.states.get(0, st + GO);
1109 let mut xgm = 0.0;
1110 let td = self.t.excess_phase_factor;
1111 if td != 0.0 {
1112 let arg = td * ctx.omega;
1113 gm += go;
1114 xgm = -gm * arg.sin();
1115 gm = gm * arg.cos() - go;
1116 }
1117 let gx = ctx.states.get(0, st + GX);
1118 let xcpi = ctx.states.get(0, st + CQBE) * ctx.omega;
1119 let xcmu = ctx.states.get(0, st + CQBC) * ctx.omega;
1120 let xcbx = ctx.states.get(0, st + CQBX) * ctx.omega;
1121 let xccs = ctx.states.get(0, st + CQCS) * ctx.omega;
1122 let xcmcb = ctx.states.get(0, st + CEXBC) * ctx.omega;
1123
1124 ctx.add_g(self.s[COL_COL], gcpr);
1125 ctx.add_g(self.s[BASE_BASE], gx);
1126 ctx.add_b(self.s[BASE_BASE], xcbx);
1127 ctx.add_g(self.s[EMIT_EMIT], gepr);
1128 ctx.add_g(self.s[COLP_COLP], gmu + go + gcpr);
1129 ctx.add_b(self.s[COLP_COLP], xcmu + xccs + xcbx);
1130 ctx.add_g(self.s[BASEP_BASEP], gx + gpi + gmu);
1131 ctx.add_b(self.s[BASEP_BASEP], xcpi + xcmu + xcmcb);
1132 ctx.add_g(self.s[EMITP_EMITP], gpi + gepr + gm + go);
1133 ctx.add_b(self.s[EMITP_EMITP], xcpi + xgm);
1134 ctx.add_g(self.s[COL_COLP], -gcpr);
1135 ctx.add_g(self.s[BASE_BASEP], -gx);
1136 ctx.add_g(self.s[EMIT_EMITP], -gepr);
1137 ctx.add_g(self.s[COLP_COL], -gcpr);
1138 ctx.add_g(self.s[COLP_BASEP], -gmu + gm);
1139 ctx.add_b(self.s[COLP_BASEP], -xcmu + xgm);
1140 ctx.add_g(self.s[COLP_EMITP], -gm - go);
1141 ctx.add_b(self.s[COLP_EMITP], -xgm);
1142 ctx.add_g(self.s[BASEP_BASE], -gx);
1143 ctx.add_g(self.s[BASEP_COLP], -gmu);
1144 ctx.add_b(self.s[BASEP_COLP], -xcmu - xcmcb);
1145 ctx.add_g(self.s[BASEP_EMITP], -gpi);
1146 ctx.add_b(self.s[BASEP_EMITP], -xcpi);
1147 ctx.add_g(self.s[EMITP_EMIT], -gepr);
1148 ctx.add_g(self.s[EMITP_COLP], -go);
1149 ctx.add_b(self.s[EMITP_COLP], xcmcb);
1150 ctx.add_g(self.s[EMITP_BASEP], -gpi - gm);
1151 ctx.add_b(self.s[EMITP_BASEP], -xcpi - xgm - xcmcb);
1152 ctx.add_b(self.s[SUBST_SUBST], xccs);
1153 ctx.add_b(self.s[COLP_SUBST], -xccs);
1154 ctx.add_b(self.s[SUBST_COLP], -xccs);
1155 ctx.add_b(self.s[BASE_COLP], -xcbx);
1156 ctx.add_b(self.s[COLP_BASE], -xcbx);
1157 }
1158
1159 fn load_pz(&mut self, ctx: &mut crate::matrix::PzCtx) -> Result<(), &'static str> {
1169 let st = self.st;
1170 let gcpr = self.t.col_conduct * self.area;
1171 let gepr = self.t.emit_conduct * self.area;
1172 let gpi = ctx.states.get(0, st + GPI);
1173 let gmu = ctx.states.get(0, st + GMU);
1174 let gm = ctx.states.get(0, st + GM);
1175 let go = ctx.states.get(0, st + GO);
1176 let xgm = 0.0;
1177 let gx = ctx.states.get(0, st + GX);
1178 let xcpi = ctx.states.get(0, st + CQBE);
1179 let xcmu = ctx.states.get(0, st + CQBC);
1180 let xcbx = ctx.states.get(0, st + CQBX);
1181 let xccs = ctx.states.get(0, st + CQCS);
1182 let xcmcb = ctx.states.get(0, st + CEXBC);
1183
1184 ctx.add_g(self.s[COL_COL], gcpr);
1185 ctx.add_g(self.s[BASE_BASE], gx);
1186 ctx.add_cap(self.s[BASE_BASE], xcbx);
1187 ctx.add_g(self.s[EMIT_EMIT], gepr);
1188 ctx.add_g(self.s[COLP_COLP], gmu + go + gcpr);
1189 ctx.add_cap(self.s[COLP_COLP], xcmu + xccs + xcbx);
1190 ctx.add_g(self.s[BASEP_BASEP], gx + gpi + gmu);
1191 ctx.add_cap(self.s[BASEP_BASEP], xcpi + xcmu + xcmcb);
1192 ctx.add_g(self.s[EMITP_EMITP], gpi + gepr + gm + go);
1193 ctx.add_cap(self.s[EMITP_EMITP], xcpi + xgm);
1194 ctx.add_g(self.s[COL_COLP], -gcpr);
1195 ctx.add_g(self.s[BASE_BASEP], -gx);
1196 ctx.add_g(self.s[EMIT_EMITP], -gepr);
1197 ctx.add_g(self.s[COLP_COL], -gcpr);
1198 ctx.add_g(self.s[COLP_BASEP], -gmu + gm);
1199 ctx.add_cap(self.s[COLP_BASEP], -xcmu + xgm);
1200 ctx.add_g(self.s[COLP_EMITP], -gm - go);
1201 ctx.add_cap(self.s[COLP_EMITP], -xgm);
1202 ctx.add_g(self.s[BASEP_BASE], -gx);
1203 ctx.add_g(self.s[BASEP_COLP], -gmu);
1204 ctx.add_cap(self.s[BASEP_COLP], -xcmu - xcmcb);
1205 ctx.add_g(self.s[BASEP_EMITP], -gpi);
1206 ctx.add_cap(self.s[BASEP_EMITP], -xcpi);
1207 ctx.add_g(self.s[EMITP_EMIT], -gepr);
1208 ctx.add_g(self.s[EMITP_COLP], -go);
1209 ctx.add_cap(self.s[EMITP_COLP], xcmcb);
1210 ctx.add_g(self.s[EMITP_BASEP], -gpi - gm);
1211 ctx.add_cap(self.s[EMITP_BASEP], -xcpi - xgm - xcmcb);
1212 ctx.add_cap(self.s[SUBST_SUBST], xccs);
1213 ctx.add_cap(self.s[COLP_SUBST], -xccs);
1214 ctx.add_cap(self.s[SUBST_COLP], -xccs);
1215 ctx.add_cap(self.s[BASE_COLP], -xcbx);
1216 ctx.add_cap(self.s[COLP_BASE], -xcbx);
1217 Ok(())
1218 }
1219
1220 #[allow(clippy::too_many_lines)]
1231 fn disto_setup(&mut self, ctx: &DistoSetupCtx) -> Result<(), &'static str> {
1232 let t = &self.t;
1233 let sol = ctx.sol;
1234 let gmin = ctx.gmin;
1235 let typ = t.type_;
1236 let area = self.area;
1237 let vt = t.vt;
1238
1239 let d_p = Dderivs::var_p();
1240 let d_r = Dderivs::var_r();
1241 let zero = Dderivs::default();
1242
1243 let csat = t.t_sat_cur * area;
1244 let rbpr = t.min_base_resist / area;
1245 let rbpi = t.base_resist / area - rbpr;
1246 let oik = t.inv_roll_f / area;
1247 let c2 = t.t_be_leak * area;
1248 let vte = t.ne * vt;
1249 let oikr = t.inv_roll_r / area;
1250 let c4 = t.t_bc_leak * area;
1251 let vtc = t.nc * vt;
1252 let xjrb = t.irb * area;
1253
1254 let vbe = typ * (sol[self.base_p] - sol[self.emit_p]);
1255 let vbc = typ * (sol[self.base] - sol[self.col_p]);
1256 let vbx = typ * (sol[self.base] - sol[self.col_p]);
1257 let vsc = typ * (sol[self.subst] - sol[self.col_p]);
1258 let vbb = typ * (sol[self.base] - sol[self.base_p]);
1259
1260 let vtn = vt * t.nf;
1263 let (cbe, gbe, gbe2, gbe3, cben, gben, gben2, gben3);
1264 if vbe > -5.0 * vtn {
1265 let evbe = f64::exp(vbe / vtn);
1266 cbe = csat * (evbe - 1.0) + gmin * vbe;
1267 gbe = csat * evbe / vtn + gmin;
1268 gbe2 = csat * evbe / vtn / vtn;
1269 gbe3 = gbe2 / vtn;
1270 if c2 == 0.0 {
1271 cben = 0.0;
1272 (gben, gben2, gben3) = (0.0, 0.0, 0.0);
1273 } else {
1274 let evben = f64::exp(vbe / vte);
1275 cben = c2 * (evben - 1.0);
1276 gben = c2 * evben / vte;
1277 gben2 = gben / vte;
1278 gben3 = gben2 / vte;
1279 }
1280 } else {
1281 gbe = -csat / vbe + gmin;
1282 (gbe2, gbe3, gben2, gben3) = (0.0, 0.0, 0.0, 0.0);
1283 cbe = gbe * vbe;
1284 gben = -c2 / vbe;
1285 cben = gben * vbe;
1286 }
1287 let vtn = vt * t.nr;
1288 let (cbc, gbc, gbc2, gbc3, cbcn, gbcn, gbcn2, gbcn3);
1289 if vbc > -5.0 * vtn {
1290 let evbc = f64::exp(vbc / vtn);
1291 cbc = csat * (evbc - 1.0) + gmin * vbc;
1292 gbc = csat * evbc / vtn + gmin;
1293 gbc2 = csat * evbc / vtn / vtn;
1294 gbc3 = gbc2 / vtn;
1295 if c4 == 0.0 {
1296 cbcn = 0.0;
1297 (gbcn, gbcn2, gbcn3) = (0.0, 0.0, 0.0);
1298 } else {
1299 let evbcn = f64::exp(vbc / vtc);
1300 cbcn = c4 * (evbcn - 1.0);
1301 gbcn = c4 * evbcn / vtc;
1302 gbcn2 = gbcn / vtc;
1303 gbcn3 = gbcn2 / vtc;
1304 }
1305 } else {
1306 gbc = -csat / vbc + gmin;
1307 (gbc2, gbc3) = (0.0, 0.0);
1308 cbc = gbc * vbc;
1309 gbcn = -c4 / vbc;
1310 (gbcn2, gbcn3) = (0.0, 0.0);
1311 cbcn = gbcn * vbc;
1312 }
1313
1314 let d_dummy = Dderivs {
1316 value: 1.0 - t.inv_early_f * vbc - t.inv_early_r * vbe,
1317 d1_p: -t.inv_early_r,
1318 d1_q: -t.inv_early_f,
1319 ..zero
1320 };
1321 let d_q1 = d_dummy.inv();
1322 let d_qb = if oik == 0.0 && oikr == 0.0 {
1323 d_q1
1324 } else {
1325 let q2 = oik * cbe + oikr * cbc;
1326 let d_q2 = Dderivs {
1327 value: q2,
1328 d1_p: oik * gbe,
1329 d1_q: oikr * gbc,
1330 d2_p2: oik * gbe2,
1331 d2_q2: oikr * gbc2,
1332 d3_p3: oik * gbe3,
1333 d3_q3: oikr * gbc3,
1334 ..zero
1335 };
1336 let arg = f64::max(0.0, 1.0 + 4.0 * q2);
1337 let d_arg = if arg == 0.0 {
1338 zero
1339 } else {
1340 let mut a = d_q2.times(4.0);
1341 a.value += 1.0;
1342 a
1343 };
1344 let d_sqarg = if arg != 0.0 {
1345 d_arg.sqrt()
1346 } else {
1347 Dderivs::constant(1.0)
1348 };
1349 let mut d_dummy = d_sqarg;
1350 d_dummy.value += 1.0;
1351 d_q1.mult(&d_dummy).times(0.5)
1352 };
1353
1354 let d_dummy = Dderivs {
1356 value: cbe - cbc,
1357 d1_r: gbe,
1358 d2_r2: gbe2,
1359 d3_r3: gbe3,
1360 d1_q: -gbc,
1361 d2_q2: -gbc2,
1362 d3_q3: -gbc3,
1363 ..zero
1364 };
1365 let mut d_ic = d_dummy.div(&d_qb);
1366 d_ic.value -= cbc / t.t_beta_r + cbcn;
1367 d_ic.d1_q -= gbc / t.t_beta_r + gbcn;
1368 d_ic.d2_q2 -= gbc2 / t.t_beta_r + gbcn2;
1369 d_ic.d3_q3 -= gbc3 / t.t_beta_r + gbcn3;
1370
1371 let mut cb = 0.0;
1373 let d_ibb = if !(rbpr == 0.0 && rbpi == 0.0) {
1374 cb = cbe / t.t_beta_f + cben + cbc / t.t_beta_r + cbcn;
1375 if cb != 0.0 {
1376 if xjrb != 0.0 && rbpi != 0.0 {
1377 let d_dummy = Dderivs {
1379 value: f64::max(cb / xjrb, 1e-9),
1380 d1_p: 1.0 / xjrb,
1381 ..zero
1382 };
1383 let d_dummy = d_dummy.sqrt().times(2.4317);
1384 let mut d_dummy2 = Dderivs {
1385 value: 1.0 + 14.59025 * f64::max(cb / xjrb, 1e-9),
1386 d1_p: 14.59025 / xjrb,
1387 ..zero
1388 }
1389 .sqrt();
1390 d_dummy2.value -= 1.0;
1391 let d_z = d_dummy2.div(&d_dummy);
1392 let d_tanz = d_z.tan();
1393 let d_dummy = d_z.times(-1.0).plus(&d_tanz);
1394 let d_dummy2 = d_tanz.mult(&d_tanz).mult(&d_z);
1395 let mut d_rbb = d_dummy.div(&d_dummy2).times(3.0 * rbpi);
1396 d_rbb.value += rbpr;
1397 let d_vbb = d_rbb.mult(&d_p);
1398 let (mut gbb1, mut gbb2, mut gbb3) = (0.0, 0.0, 0.0);
1402 if d_vbb.d1_p != 0.0 {
1403 gbb1 = 1.0 / d_vbb.d1_p;
1404 gbb2 = -(d_vbb.d2_p2 * 0.5) * gbb1 * gbb1;
1405 gbb3 = gbb1
1406 * gbb1
1407 * gbb1
1408 * gbb1
1409 * (-(d_vbb.d3_p3 / 6.0)
1410 + 2.0 * (d_vbb.d2_p2 * 0.5) * (d_vbb.d2_p2 * 0.5) * gbb1);
1411 }
1412 Dderivs {
1413 value: cb,
1414 d1_r: gbb1,
1415 d2_r2: 2.0 * gbb2,
1416 d3_r3: 6.0 * gbb3,
1417 ..zero
1418 }
1419 } else {
1420 let mut d_rbb = zero;
1422 if rbpi != 0.0 {
1423 d_rbb = d_qb.inv().times(rbpi);
1424 }
1425 d_rbb.value += rbpr;
1426 let d_ibb = Dderivs { value: vbb, ..d_r };
1427 d_ibb.div(&d_rbb)
1428 }
1429 } else {
1430 let mut d_ibb = d_r;
1431 if rbpr != 0.0 {
1432 d_ibb.d1_r = 1.0 / rbpr;
1433 }
1434 d_ibb
1435 }
1436 } else {
1437 zero
1438 };
1439
1440 let d_ib = Dderivs {
1442 value: cb,
1443 d1_p: gbe / t.t_beta_f + gben,
1444 d2_p2: gbe2 / t.t_beta_f + gben2,
1445 d3_p3: gbe3 / t.t_beta_f + gben3,
1446 d1_q: gbc / t.t_beta_r + gbcn,
1447 d2_q2: gbc2 / t.t_beta_r + gbcn2,
1448 d3_q3: gbc3 / t.t_beta_r + gbcn3,
1449 ..zero
1450 };
1451
1452 let tf = t.tf;
1454 let czbe = t.t_be_cap * area;
1455 let pe = t.t_be_pot;
1456 let xme = t.mje;
1457 let cdis = t.xcjc;
1458 let ctot = t.t_bc_cap * area;
1459 let czbc = ctot * cdis;
1460 let czbx = ctot - czbc;
1461 let pc = t.t_bc_pot;
1462 let xmc = t.mjc;
1463 let fcpe = t.t_dep_cap;
1464 let czcs = t.cjs * area;
1465 let ps = t.vjs;
1466 let xms = t.mjs;
1467 let xtf = t.xtf;
1468 let ovtf = t.tt_vbc_factor;
1469 let xjtf = t.itf * area;
1470 let mut d_qbe = if tf != 0.0 && vbe > 0.0 {
1471 let d_cbe = Dderivs {
1472 value: cbe,
1473 d1_p: gbe,
1474 d2_p2: gbe2,
1475 d3_p3: gbe3,
1476 ..zero
1477 };
1478 let d_tff = if xtf != 0.0 {
1479 let d_dummy = if ovtf != 0.0 {
1480 Dderivs {
1481 value: vbc * ovtf,
1482 d1_q: ovtf,
1483 ..zero
1484 }
1485 .exp()
1486 } else {
1487 Dderivs::constant(1.0)
1488 };
1489 let d_dummy2 = if xjtf != 0.0 {
1490 let mut den = d_cbe;
1491 den.value += xjtf;
1492 let r = d_cbe.div(&den);
1493 r.mult(&r)
1494 } else {
1495 Dderivs::constant(1.0)
1496 };
1497 let mut d_tff = d_dummy.mult(&d_dummy2).times(tf * xtf);
1498 d_tff.value += tf;
1499 d_tff
1500 } else {
1501 Dderivs::constant(tf)
1502 };
1503 d_tff.div(&d_qb).mult(&d_cbe)
1505 } else {
1506 zero
1507 };
1508 let (lcapbe1, lcapbe2, lcapbe3);
1509 if vbe < fcpe {
1510 let arg = 1.0 - vbe / pe;
1511 let sarg = f64::exp(-xme * f64::ln(arg));
1512 lcapbe1 = czbe * sarg;
1513 lcapbe2 = 0.5 * czbe * xme * sarg / (arg * pe);
1514 lcapbe3 = czbe * xme * (xme + 1.0) * sarg / (arg * arg * pe * pe * 6.0);
1515 } else {
1516 let czbef2 = czbe / t.f2;
1517 lcapbe1 = czbef2 * (t.f3 + xme * vbe / pe);
1518 lcapbe2 = 0.5 * xme * czbef2 / pe;
1519 lcapbe3 = 0.0;
1520 }
1521 d_qbe.d1_p += lcapbe1;
1522 d_qbe.d2_p2 += lcapbe2 * 2.;
1523 d_qbe.d3_p3 += lcapbe3 * 6.;
1524
1525 let fcpc = t.t_f4;
1526 let dep = |v: f64, cz: f64| {
1527 if v < fcpc {
1528 let arg = 1.0 - v / pc;
1529 let sarg = f64::exp(-xmc * f64::ln(arg));
1530 (
1531 0.5 * cz * xmc * sarg / (arg * pc),
1532 cz * xmc * (xmc + 1.0) * sarg / (arg * arg * pc * pc * 6.0),
1533 )
1534 } else {
1535 let czf2 = cz / t.f6;
1536 (0.5 * xmc * czf2 / pc, 0.0)
1537 }
1538 };
1539 let (capbc2, capbc3) = dep(vbc, czbc);
1540 let (capbx2, capbx3) = dep(vbx, czbx);
1541 let (capsc2, capsc3) = if vsc < 0.0 {
1542 let arg = 1.0 - vsc / ps;
1543 let sarg = f64::exp(-xms * f64::ln(arg));
1544 (
1545 0.5 * czcs * xms * sarg / (arg * ps),
1546 czcs * xms * (xms + 1.0) * sarg / (arg * arg * ps * ps * 6.0),
1547 )
1548 } else {
1549 (czcs * 0.5 * xms / ps, 0.0)
1550 };
1551
1552 self.d = BjtDistoCoeffs {
1553 ic: taylor_coeffs(&d_ic, typ),
1554 ib: xy_only(taylor_coeffs(&d_ib, typ)),
1555 ibb: taylor_coeffs(&d_ibb, typ),
1556 qbe: xy_only(taylor_coeffs(&d_qbe, typ)),
1557 capbc2,
1558 capbc3,
1559 capbx2,
1560 capbx3,
1561 capsc2,
1562 capsc3,
1563 };
1564 Ok(())
1565 }
1566
1567 fn disto_load(&self, ctx: &mut DistoCtx) {
1575 let d = &self.d;
1576 let t = &self.t;
1577 let mode = ctx.mode;
1578 let omega = ctx.omega;
1579 let td = t.excess_phase_factor;
1580 let (b, bp, cp, ep, s) = (self.base, self.base_p, self.col_p, self.emit_p, self.subst);
1581 let none = Kern::default();
1582
1583 let kx = ctx.h.kern(mode, bp, ep);
1584 let ky = ctx.h.kern(mode, bp, cp);
1585 let mut kz = if td != 0.0 {
1586 delayed(mode, &kx, td, ctx.omega1, ctx.omega2)
1587 } else {
1588 kx
1589 };
1590
1591 let ic = df_term(mode, &d.ic, &kx, &ky, &kz);
1592 ctx.stamp(cp, ep, ic);
1593 let ib = df_term(mode, &d.ib, &kx, &ky, &none);
1594 ctx.stamp(bp, ep, ib);
1595
1596 if !(t.min_base_resist == 0.0 && t.base_resist == t.min_base_resist) {
1597 kz = ctx.h.kern(mode, b, bp);
1598 if mode == DistoMode::F1mF2 {
1599 kz.i1h2 = -kz.i1h2;
1600 }
1601 let ibb = df_term(mode, &d.ibb, &kx, &ky, &kz);
1602 ctx.stamp(b, bp, ibb);
1603 }
1604
1605 let qbe = jomega(omega, df_term(mode, &d.qbe, &kx, &ky, &none));
1606 ctx.stamp(bp, ep, qbe);
1607
1608 let kbx = kz.add(&ky);
1609 let qbx = jomega(omega, d1_term(mode, d.capbx2, d.capbx3, &kbx));
1610 ctx.stamp(b, cp, qbx);
1611
1612 let qbc = jomega(omega, d1_term(mode, d.capbc2, d.capbc3, &ky));
1613 ctx.stamp(bp, cp, qbc);
1614
1615 let mut ksc = ctx.h.kern(mode, s, cp);
1616 if mode == DistoMode::F1mF2 {
1617 ksc.i1h2 = -ksc.i1h2;
1618 }
1619 let qsc = jomega(omega, d1_term(mode, d.capsc2, d.capsc3, &ksc));
1620 ctx.stamp(s, cp, qsc);
1621 }
1622}