1use std::collections::HashMap;
7
8use crate::diode::{DiodeModel, DiodeTemp};
9use crate::error::Error;
10use crate::matrix::Var;
11use crate::options::Options;
12use crate::trnoise::NoiseWave;
13
14#[derive(Clone, Debug)]
16pub struct Resistor {
17 pub name: String,
18 pub nodes: [String; 2],
19 pub value: f64,
20}
21
22#[derive(Clone, Copy, Debug, Default)]
28pub(crate) struct ResTemp {
29 pub tc1: f64,
30 pub tc2: f64,
31 pub tnom: Option<f64>,
33 pub temp: Option<f64>,
35 pub dtemp: f64,
39}
40
41#[derive(Clone, Debug)]
43pub struct Capacitor {
44 pub name: String,
45 pub nodes: [String; 2],
46 pub value: f64,
47 pub ic: Option<f64>,
49}
50
51#[derive(Clone, Debug)]
53pub struct Inductor {
54 pub name: String,
55 pub nodes: [String; 2],
56 pub value: f64,
57 pub ic: Option<f64>,
59}
60
61#[derive(Clone, Debug)]
63pub struct Mutual {
64 pub name: String,
65 pub l1: String,
66 pub l2: String,
67 pub k: f64,
68}
69
70#[derive(Clone, Debug)]
72pub struct Vsource {
73 pub name: String,
74 pub nodes: [String; 2],
75 pub dc: Option<f64>,
78 pub wave: Option<crate::srcfn::Wave>,
79 pub ac: Option<(f64, f64)>,
81 pub distof1: Option<(f64, f64)>,
83 pub distof2: Option<(f64, f64)>,
85 pub port: Option<RfPort>,
90}
91
92#[derive(Clone, Copy, Debug, PartialEq)]
98pub struct RfPort {
99 pub num: u32,
101 pub z0: f64,
104}
105
106impl RfPort {
107 pub(crate) fn is_port(&self) -> bool {
109 self.num > 0 && self.z0 > 0.0
110 }
111}
112
113#[derive(Clone, Debug)]
116pub struct Isource {
117 pub name: String,
118 pub nodes: [String; 2],
119 pub dc: Option<f64>,
120 pub wave: Option<crate::srcfn::Wave>,
121 pub ac: Option<(f64, f64)>,
123 pub distof1: Option<(f64, f64)>,
125 pub distof2: Option<(f64, f64)>,
127}
128
129#[derive(Clone, Debug)]
131pub struct Vcvs {
132 pub name: String,
133 pub nodes: [String; 4],
134 pub gain: f64,
135}
136
137#[derive(Clone, Debug)]
139pub struct Vccs {
140 pub name: String,
141 pub nodes: [String; 4],
142 pub gain: f64,
143}
144
145#[derive(Clone, Debug)]
147pub struct Cccs {
148 pub name: String,
149 pub nodes: [String; 2],
150 pub ctrl: String,
151 pub gain: f64,
152}
153
154#[derive(Clone, Debug)]
156pub struct Ccvs {
157 pub name: String,
158 pub nodes: [String; 2],
159 pub ctrl: String,
160 pub gain: f64,
161}
162
163#[derive(Clone, Debug)]
172pub struct Asrc {
173 pub name: String,
174 pub nodes: [String; 2],
175 pub kind: dune_netlist::ir::SourceKind,
176 pub expr: dune_netlist::ir::Expr,
177}
178
179#[derive(Clone, Debug)]
183pub struct Bjt {
184 pub name: String,
185 pub nodes: [String; 4],
187 pub model: crate::bjt::BjtModel,
188 pub area: f64,
189 pub off: bool,
190 pub ic: (f64, f64),
192 pub temp: Option<f64>,
194}
195
196#[derive(Clone, Debug)]
201pub struct Mos1 {
202 pub name: String,
203 pub nodes: [String; 4],
205 pub model: crate::mos1::Mos1Model,
206 pub l: Option<f64>,
207 pub w: Option<f64>,
208 pub ad: Option<f64>,
209 pub as_: Option<f64>,
210 pub pd: f64,
211 pub ps: f64,
212 pub nrd: f64,
214 pub nrs: f64,
215 pub off: bool,
216 pub ic: (f64, f64, f64),
218 pub temp: Option<f64>,
220}
221
222#[derive(Clone, Debug)]
226pub struct Mos2 {
227 pub name: String,
228 pub nodes: [String; 4],
230 pub model: crate::mos2::Mos2Model,
231 pub l: Option<f64>,
232 pub w: Option<f64>,
233 pub ad: Option<f64>,
234 pub as_: Option<f64>,
235 pub pd: f64,
236 pub ps: f64,
237 pub nrd: f64,
238 pub nrs: f64,
239 pub off: bool,
240 pub ic: (f64, f64, f64),
242 pub temp: Option<f64>,
244}
245
246#[derive(Clone, Debug)]
250pub struct Mos3 {
251 pub name: String,
252 pub nodes: [String; 4],
254 pub model: crate::mos3::Mos3Model,
255 pub l: Option<f64>,
256 pub w: Option<f64>,
257 pub ad: Option<f64>,
258 pub as_: Option<f64>,
259 pub pd: f64,
260 pub ps: f64,
261 pub nrd: f64,
262 pub nrs: f64,
263 pub off: bool,
264 pub ic: (f64, f64, f64),
266 pub temp: Option<f64>,
268}
269
270#[derive(Clone, Debug)]
272pub struct Mos6 {
273 pub name: String,
274 pub nodes: [String; 4],
276 pub model: crate::mos6::Mos6Model,
277 pub l: Option<f64>,
278 pub w: Option<f64>,
279 pub ad: Option<f64>,
280 pub as_: Option<f64>,
281 pub pd: f64,
282 pub ps: f64,
283 pub nrd: f64,
285 pub nrs: f64,
286 pub off: bool,
287 pub ic: (f64, f64, f64),
289 pub temp: Option<f64>,
291}
292
293#[derive(Clone, Debug)]
295pub struct Bsim1 {
296 pub name: String,
297 pub nodes: [String; 4],
299 pub model: crate::bsim1::Bsim1Model,
300 pub l: Option<f64>,
301 pub w: Option<f64>,
302 pub ad: Option<f64>,
303 pub as_: Option<f64>,
304 pub pd: f64,
305 pub ps: f64,
306 pub nrd: f64,
307 pub nrs: f64,
308 pub off: bool,
309 pub ic: (f64, f64, f64),
311}
312
313#[derive(Clone, Debug)]
315pub struct Bsim2 {
316 pub name: String,
317 pub nodes: [String; 4],
319 pub model: crate::bsim2::Bsim2Model,
320 pub l: Option<f64>,
321 pub w: Option<f64>,
322 pub ad: Option<f64>,
323 pub as_: Option<f64>,
324 pub pd: f64,
325 pub ps: f64,
326 pub nrd: f64,
327 pub nrs: f64,
328 pub off: bool,
329 pub ic: (f64, f64, f64),
331}
332
333#[derive(Clone, Debug)]
339pub struct Bsim3 {
340 pub name: String,
341 pub nodes: [String; 4],
343 pub model: crate::bsim3::Bsim3Model,
344 pub l: Option<f64>,
345 pub w: Option<f64>,
346 pub ad: f64,
347 pub as_: f64,
348 pub pd: f64,
349 pub ps: f64,
350 pub nrd: f64,
351 pub nrs: f64,
352 pub m: f64,
354 pub delvto: f64,
356 pub mulu0: f64,
358 pub off: bool,
359 pub ic: (f64, f64, f64),
361}
362
363#[derive(Clone, Debug)]
365pub struct Diode {
366 pub name: String,
367 pub nodes: [String; 2],
368 pub model: DiodeModel,
369 pub area: f64,
370 pub off: bool,
371 pub temp: Option<f64>,
374 pub ic: Option<f64>,
376}
377
378#[derive(Clone, Debug)]
382pub struct Jfet {
383 pub name: String,
384 pub nodes: [String; 3],
386 pub model: crate::jfet::JfetModel,
387 pub area: f64,
388 pub off: bool,
389 pub ic: (f64, f64),
391 pub temp: Option<f64>,
393}
394
395#[derive(Clone, Debug)]
399pub struct Mes {
400 pub name: String,
401 pub nodes: [String; 3],
403 pub model: crate::mes::MesModel,
404 pub area: f64,
405 pub off: bool,
406 pub ic: (f64, f64),
408}
409
410#[derive(Clone, Debug)]
415pub struct Ltra {
416 pub name: String,
417 pub nodes: [String; 4],
419 pub model: crate::ltra::LtraModel,
420}
421
422#[derive(Clone, Debug)]
430pub struct Tra {
431 pub name: String,
432 pub nodes: [String; 4],
434 pub z0: f64,
436 pub td: Option<f64>,
438 pub f: Option<f64>,
440 pub nl: Option<f64>,
442 pub ic: [f64; 4],
445}
446
447#[derive(Clone, Debug)]
450pub struct SwitchV {
451 pub name: String,
452 pub nodes: [String; 4],
454 pub model: crate::switch::SwModel,
455 pub on: bool,
458}
459
460#[derive(Clone, Debug)]
464pub struct SwitchI {
465 pub name: String,
466 pub nodes: [String; 2],
467 pub ctrl: String,
468 pub model: crate::switch::CswModel,
469 pub on: bool,
471}
472
473#[derive(Clone, Debug)]
474pub enum Spec {
475 Bjt(Box<Bjt>),
476 Mos1(Box<Mos1>),
477 Mos2(Box<Mos2>),
478 Mos3(Box<Mos3>),
479 Mos6(Box<Mos6>),
480 Bsim1(Box<Bsim1>),
481 Bsim2(Box<Bsim2>),
482 Bsim3(Box<Bsim3>),
483 Resistor(Resistor),
484 Capacitor(Capacitor),
485 Inductor(Inductor),
486 Mutual(Mutual),
487 Vsource(Vsource),
488 Isource(Isource),
489 Vcvs(Vcvs),
490 Vccs(Vccs),
491 Cccs(Cccs),
492 Ccvs(Ccvs),
493 Diode(Diode),
494 Jfet(Box<Jfet>),
495 Mes(Box<Mes>),
496 Ltra(Box<Ltra>),
497 Tra(Box<Tra>),
498 SwitchV(SwitchV),
499 SwitchI(SwitchI),
500 Asrc(Box<Asrc>),
501}
502
503macro_rules! impl_into_spec {
504 ($($t:ident),*) => {$(
505 impl From<$t> for Spec {
506 fn from(s: $t) -> Spec { Spec::$t(s) }
507 }
508 )*};
509}
510impl_into_spec!(
511 Resistor, Capacitor, Inductor, Mutual, Vsource, Isource, Vcvs, Vccs, Cccs, Ccvs, Diode,
512 SwitchV, SwitchI
513);
514
515impl From<Bjt> for Spec {
516 fn from(s: Bjt) -> Spec {
517 Spec::Bjt(Box::new(s))
518 }
519}
520
521impl From<Mos1> for Spec {
522 fn from(s: Mos1) -> Spec {
523 Spec::Mos1(Box::new(s))
524 }
525}
526
527impl From<Mos2> for Spec {
528 fn from(s: Mos2) -> Spec {
529 Spec::Mos2(Box::new(s))
530 }
531}
532
533impl From<Mos3> for Spec {
534 fn from(s: Mos3) -> Spec {
535 Spec::Mos3(Box::new(s))
536 }
537}
538
539impl From<Mos6> for Spec {
540 fn from(s: Mos6) -> Spec {
541 Spec::Mos6(Box::new(s))
542 }
543}
544
545impl From<Bsim1> for Spec {
546 fn from(s: Bsim1) -> Spec {
547 Spec::Bsim1(Box::new(s))
548 }
549}
550
551impl From<Bsim2> for Spec {
552 fn from(s: Bsim2) -> Spec {
553 Spec::Bsim2(Box::new(s))
554 }
555}
556
557impl From<Bsim3> for Spec {
558 fn from(s: Bsim3) -> Spec {
559 Spec::Bsim3(Box::new(s))
560 }
561}
562
563impl From<Jfet> for Spec {
564 fn from(s: Jfet) -> Spec {
565 Spec::Jfet(Box::new(s))
566 }
567}
568
569impl From<Mes> for Spec {
570 fn from(s: Mes) -> Spec {
571 Spec::Mes(Box::new(s))
572 }
573}
574
575impl From<Ltra> for Spec {
576 fn from(s: Ltra) -> Spec {
577 Spec::Ltra(Box::new(s))
578 }
579}
580
581impl From<Asrc> for Spec {
582 fn from(s: Asrc) -> Spec {
583 Spec::Asrc(Box::new(s))
584 }
585}
586
587impl From<Tra> for Spec {
588 fn from(s: Tra) -> Spec {
589 Spec::Tra(Box::new(s))
590 }
591}
592
593impl Spec {
594 pub(crate) fn name(&self) -> &str {
595 match self {
596 Spec::Bjt(s) => &s.name,
597 Spec::Mos1(s) => &s.name,
598 Spec::Mos2(s) => &s.name,
599 Spec::Mos3(s) => &s.name,
600 Spec::Mos6(s) => &s.name,
601 Spec::Bsim1(s) => &s.name,
602 Spec::Bsim2(s) => &s.name,
603 Spec::Bsim3(s) => &s.name,
604 Spec::Resistor(s) => &s.name,
605 Spec::Capacitor(s) => &s.name,
606 Spec::Inductor(s) => &s.name,
607 Spec::Mutual(s) => &s.name,
608 Spec::Vsource(s) => &s.name,
609 Spec::Isource(s) => &s.name,
610 Spec::Vcvs(s) => &s.name,
611 Spec::Vccs(s) => &s.name,
612 Spec::Cccs(s) => &s.name,
613 Spec::Ccvs(s) => &s.name,
614 Spec::Diode(s) => &s.name,
615 Spec::Jfet(s) => &s.name,
616 Spec::Mes(s) => &s.name,
617 Spec::Ltra(s) => &s.name,
618 Spec::Tra(s) => &s.name,
619 Spec::SwitchV(s) => &s.name,
620 Spec::SwitchI(s) => &s.name,
621 Spec::Asrc(s) => &s.name,
622 }
623 }
624}
625
626#[derive(Clone, Default)]
633pub struct Circuit {
634 pub title: String,
635 pub(crate) specs: Vec<Spec>,
636 pub(crate) nodesets: Vec<(String, f64)>,
638 pub(crate) ics: Vec<(String, f64)>,
641 pub(crate) ic_given: HashMap<String, crate::devices::IcGiven>,
646 pub(crate) res_temp: HashMap<String, ResTemp>,
652 pub(crate) dtemp: HashMap<String, f64>,
658 pub(crate) warm: crate::session::WarmSlot,
662}
663
664impl Circuit {
665 pub fn new(title: impl Into<String>) -> Self {
666 Circuit {
667 title: title.into(),
668 ..Circuit::default()
669 }
670 }
671
672 pub fn add(&mut self, spec: impl Into<Spec>) -> Result<(), Error> {
675 let mut spec = spec.into();
676 normalize(&mut spec);
677 let name = spec.name();
678 if name.is_empty() {
679 return Err(Error::Build("device name cannot be empty".into()));
680 }
681 if self.specs.iter().any(|s| s.name() == name) {
682 return Err(Error::Build(format!("device `{name}` already defined")));
683 }
684 if let Spec::Resistor(r) = &spec
685 && (r.value == 0.0 || !r.value.is_finite())
686 {
687 return Err(Error::Build(format!(
690 "resistor `{}` cannot have zero resistance",
691 r.name
692 )));
693 }
694 self.specs.push(spec);
695 Ok(())
696 }
697
698 pub fn nodeset(&mut self, node: impl Into<String>, value: f64) {
700 self.nodesets
701 .push((node.into().to_ascii_lowercase(), value));
702 }
703
704 pub fn ic(&mut self, node: impl Into<String>, value: f64) {
707 self.ics.push((node.into().to_ascii_lowercase(), value));
708 }
709
710 pub(crate) fn set_res_temp(&mut self, name: &str, t: ResTemp) {
713 self.res_temp.insert(name.to_ascii_lowercase(), t);
714 }
715
716 pub(crate) fn set_dtemp(&mut self, name: &str, dtemp: f64) {
719 self.dtemp.insert(name.to_ascii_lowercase(), dtemp);
720 }
721
722 fn instance_temp(&self, name: &str, temp: Option<f64>, opts: &Options) -> f64 {
726 temp.unwrap_or_else(|| opts.temp + self.dtemp.get(name).copied().unwrap_or(0.0))
727 }
728
729 pub(crate) fn set_ic_given(&mut self, name: &str, given: crate::devices::IcGiven) {
732 self.ic_given.insert(name.to_ascii_lowercase(), given);
733 }
734}
735
736fn normalize(spec: &mut Spec) {
737 #[allow(clippy::ptr_arg)] fn lc(s: &mut String) {
739 s.make_ascii_lowercase();
740 }
741 match spec {
742 Spec::Resistor(s) => {
743 lc(&mut s.name);
744 s.nodes.iter_mut().for_each(lc);
745 }
746 Spec::Capacitor(s) => {
747 lc(&mut s.name);
748 s.nodes.iter_mut().for_each(lc);
749 }
750 Spec::Inductor(s) => {
751 lc(&mut s.name);
752 s.nodes.iter_mut().for_each(lc);
753 }
754 Spec::Mutual(s) => {
755 lc(&mut s.name);
756 lc(&mut s.l1);
757 lc(&mut s.l2);
758 }
759 Spec::Vsource(s) => {
760 lc(&mut s.name);
761 s.nodes.iter_mut().for_each(lc);
762 }
763 Spec::Isource(s) => {
764 lc(&mut s.name);
765 s.nodes.iter_mut().for_each(lc);
766 }
767 Spec::Vcvs(s) => {
768 lc(&mut s.name);
769 s.nodes.iter_mut().for_each(lc);
770 }
771 Spec::Vccs(s) => {
772 lc(&mut s.name);
773 s.nodes.iter_mut().for_each(lc);
774 }
775 Spec::Cccs(s) => {
776 lc(&mut s.name);
777 s.nodes.iter_mut().for_each(lc);
778 lc(&mut s.ctrl);
779 }
780 Spec::Ccvs(s) => {
781 lc(&mut s.name);
782 s.nodes.iter_mut().for_each(lc);
783 lc(&mut s.ctrl);
784 }
785 Spec::Diode(s) => {
786 lc(&mut s.name);
787 s.nodes.iter_mut().for_each(lc);
788 }
789 Spec::Bjt(s) => {
790 lc(&mut s.name);
791 s.nodes.iter_mut().for_each(lc);
792 }
793 Spec::Mos1(s) => {
794 lc(&mut s.name);
795 s.nodes.iter_mut().for_each(lc);
796 }
797 Spec::Mos2(s) => {
798 lc(&mut s.name);
799 s.nodes.iter_mut().for_each(lc);
800 }
801 Spec::Mos3(s) => {
802 lc(&mut s.name);
803 s.nodes.iter_mut().for_each(lc);
804 }
805 Spec::Mos6(s) => {
806 lc(&mut s.name);
807 s.nodes.iter_mut().for_each(lc);
808 }
809 Spec::Bsim1(s) => {
810 lc(&mut s.name);
811 s.nodes.iter_mut().for_each(lc);
812 }
813 Spec::Bsim2(s) => {
814 lc(&mut s.name);
815 s.nodes.iter_mut().for_each(lc);
816 }
817 Spec::Bsim3(s) => {
818 lc(&mut s.name);
819 s.nodes.iter_mut().for_each(lc);
820 }
821 Spec::Jfet(s) => {
822 lc(&mut s.name);
823 s.nodes.iter_mut().for_each(lc);
824 }
825 Spec::Mes(s) => {
826 lc(&mut s.name);
827 s.nodes.iter_mut().for_each(lc);
828 }
829 Spec::Ltra(s) => {
830 lc(&mut s.name);
831 s.nodes.iter_mut().for_each(lc);
832 }
833 Spec::Tra(s) => {
834 lc(&mut s.name);
835 s.nodes.iter_mut().for_each(lc);
836 }
837 Spec::SwitchV(s) => {
838 lc(&mut s.name);
839 s.nodes.iter_mut().for_each(lc);
840 }
841 Spec::SwitchI(s) => {
842 lc(&mut s.name);
843 s.nodes.iter_mut().for_each(lc);
844 lc(&mut s.ctrl);
845 }
846 Spec::Asrc(s) => {
847 lc(&mut s.name);
848 s.nodes.iter_mut().for_each(lc);
849 s.expr.visit_refs_mut(&mut |n| lc(n), &mut |d| lc(d));
850 }
851 }
852}
853
854pub(crate) struct Vars {
858 pub names: Vec<String>,
859 pub kinds: Vec<VarKind>,
860}
861
862pub(crate) fn saved_node(name: &str) -> bool {
868 const HIDDEN: [&str; 6] = [
869 "#internal",
870 "#source",
871 "#drain",
872 "#collector",
873 "#emitter",
874 "#base",
875 ];
876 !HIDDEN.iter().any(|h| name.contains(h))
877}
878
879#[derive(Clone, Copy, Debug, PartialEq, Eq)]
880pub(crate) enum VarKind {
881 Ground,
882 Node,
883 Branch,
884}
885
886impl Vars {
887 fn new() -> Self {
888 Vars {
889 names: vec!["0".into()],
890 kinds: vec![VarKind::Ground],
891 }
892 }
893
894 pub fn len(&self) -> usize {
895 self.names.len()
896 }
897
898 fn push(&mut self, name: String, kind: VarKind) -> Var {
899 self.names.push(name);
900 self.kinds.push(kind);
901 self.names.len() - 1
902 }
903}
904
905#[derive(Clone, Debug)]
910pub(crate) struct PortInfo {
911 pub name: String,
912 pub num: u32,
913 pub z0: f64,
914 pub pos: Var,
915 pub neg: Var,
916 pub br: Var,
917}
918
919pub(crate) struct Built {
920 pub vars: Vars,
921 pub devices: Vec<Box<dyn crate::devices::Device>>,
922 pub names: Vec<String>,
925 pub nodesets: Vec<(Var, f64)>,
926 pub ics: Vec<(Var, f64)>,
927 pub sources: HashMap<String, (usize, bool)>,
930 pub ports: Vec<PortInfo>,
932 pub num_states: usize,
934 #[cfg_attr(not(feature = "parallel"), allow(dead_code))]
939 pub state_ranges: Vec<(usize, usize)>,
940 pub notes: Vec<String>,
942 pub warm: crate::session::WarmSlot,
944}
945
946impl Circuit {
947 pub(crate) fn build(&self, opts: &Options) -> Result<Built, Error> {
950 use crate::devices::*;
951
952 let mut vars = Vars::new();
953 let mut node_ids: HashMap<String, Var> = HashMap::new();
954 node_ids.insert("0".into(), 0);
955 let node = |vars: &mut Vars, node_ids: &mut HashMap<String, Var>, name: &str| -> Var {
956 *node_ids
957 .entry(name.to_string())
958 .or_insert_with(|| vars.push(name.to_string(), VarKind::Node))
959 };
960
961 let mut branches: HashMap<String, Var> = HashMap::new();
966 let mut st = crate::state::StateAlloc::new();
967 let mut state_base: Vec<usize> = Vec::with_capacity(self.specs.len());
968 let mut state_ranges: Vec<(usize, usize)> = Vec::with_capacity(self.specs.len());
969 let mut inductors: HashMap<String, (usize, f64)> = HashMap::new(); for spec in &self.specs {
971 let first = st.total();
972 state_base.push(match spec {
973 Spec::Bjt(_) => st.alloc(crate::bjt::BJT_NUM_STATES),
974 Spec::Mos1(_) => st.alloc(crate::mos1::MOS1_NUM_STATES),
975 Spec::Mos2(_) => st.alloc(crate::mos2::MOS2_NUM_STATES),
976 Spec::Mos3(_) => st.alloc(crate::mos3::MOS3_NUM_STATES),
977 Spec::Mos6(_) => st.alloc(crate::mos6::MOS6_NUM_STATES),
978 Spec::Bsim1(_) => st.alloc(crate::bsim1::BSIM1_NUM_STATES),
979 Spec::Bsim2(_) => st.alloc(crate::bsim2::BSIM2_NUM_STATES),
980 Spec::Bsim3(_) => st.alloc(crate::bsim3::BSIM3_NUM_STATES),
981 Spec::Capacitor(_) => st.alloc(2),
982 Spec::Inductor(s) => {
983 let flux = st.alloc(2);
984 inductors.insert(s.name.clone(), (flux, s.value));
985 flux
986 }
987 Spec::Diode(_) => st.alloc(5),
988 Spec::Jfet(_) => st.alloc(crate::jfet::FET3_NUM_STATES),
989 Spec::Mes(_) => st.alloc(crate::mes::MES_NUM_STATES),
990 Spec::SwitchV(_) | Spec::SwitchI(_) => st.alloc(crate::switch::SW_NUM_STATES),
991 _ => 0,
992 });
993 state_ranges.push((first, st.total()));
994 let node_names: &[String] = match spec {
995 Spec::Bjt(s) => &s.nodes,
996 Spec::Mos1(s) => &s.nodes,
997 Spec::Mos2(s) => &s.nodes,
998 Spec::Mos3(s) => &s.nodes,
999 Spec::Mos6(s) => &s.nodes,
1000 Spec::Bsim1(s) => &s.nodes,
1001 Spec::Bsim2(s) => &s.nodes,
1002 Spec::Bsim3(s) => &s.nodes,
1003 Spec::Resistor(s) => &s.nodes,
1004 Spec::Capacitor(s) => &s.nodes,
1005 Spec::Inductor(s) => &s.nodes,
1006 Spec::Mutual(_) => &[],
1007 Spec::Vsource(s) => &s.nodes,
1008 Spec::Isource(s) => &s.nodes,
1009 Spec::Vcvs(s) => &s.nodes,
1010 Spec::Vccs(s) => &s.nodes,
1011 Spec::Cccs(s) => &s.nodes,
1012 Spec::Ccvs(s) => &s.nodes,
1013 Spec::Diode(s) => &s.nodes,
1014 Spec::Jfet(s) => &s.nodes,
1015 Spec::Mes(s) => &s.nodes,
1016 Spec::Ltra(s) => &s.nodes,
1017 Spec::Tra(s) => &s.nodes,
1018 Spec::SwitchV(s) => &s.nodes,
1019 Spec::SwitchI(s) => &s.nodes,
1020 Spec::Asrc(s) => &s.nodes,
1021 };
1022 for n in node_names {
1023 node(&mut vars, &mut node_ids, n);
1024 }
1025 if let Spec::Asrc(s) = spec {
1029 let mut refs = Vec::new();
1030 let mut e = s.expr.clone();
1031 e.visit_refs_mut(&mut |n| refs.push(n.clone()), &mut |_| {});
1032 for n in &refs {
1033 node(&mut vars, &mut node_ids, n);
1034 }
1035 if s.kind == dune_netlist::ir::SourceKind::Voltage {
1036 let br = vars.push(s.name.clone(), VarKind::Branch);
1037 branches.insert(s.name.clone(), br);
1038 }
1039 }
1040 match spec {
1041 Spec::Vsource(s) => {
1042 let br = vars.push(s.name.clone(), VarKind::Branch);
1043 branches.insert(s.name.clone(), br);
1044 if s.port.is_some_and(|p| p.is_port()) {
1048 node(&mut vars, &mut node_ids, &format!("{}#res", s.name));
1049 }
1050 }
1051 Spec::Inductor(s) => {
1052 let br = vars.push(s.name.clone(), VarKind::Branch);
1053 branches.insert(s.name.clone(), br);
1054 }
1055 Spec::Vcvs(s) => {
1056 let br = vars.push(s.name.clone(), VarKind::Branch);
1057 branches.insert(s.name.clone(), br);
1058 }
1059 Spec::Ccvs(s) => {
1060 let br = vars.push(s.name.clone(), VarKind::Branch);
1061 branches.insert(s.name.clone(), br);
1062 }
1063 Spec::Ltra(s) => {
1067 node(&mut vars, &mut node_ids, &format!("{}#i1", s.name));
1068 node(&mut vars, &mut node_ids, &format!("{}#i2", s.name));
1069 }
1070 Spec::Tra(s) => {
1073 for suffix in ["i1", "i2", "int1", "int2"] {
1074 node(&mut vars, &mut node_ids, &format!("{}#{suffix}", s.name));
1075 }
1076 }
1077 Spec::Diode(s) if s.model.rs != 0.0 => {
1080 node(&mut vars, &mut node_ids, &format!("{}#internal", s.name));
1081 }
1082 Spec::Jfet(s) => {
1085 if s.model.rs != 0.0 {
1086 node(&mut vars, &mut node_ids, &format!("{}#source", s.name));
1087 }
1088 if s.model.rd != 0.0 {
1089 node(&mut vars, &mut node_ids, &format!("{}#drain", s.name));
1090 }
1091 }
1092 Spec::Mes(s) => {
1093 if s.model.rs != 0.0 {
1094 node(&mut vars, &mut node_ids, &format!("{}#source", s.name));
1095 }
1096 if s.model.rd != 0.0 {
1097 node(&mut vars, &mut node_ids, &format!("{}#drain", s.name));
1098 }
1099 }
1100 Spec::Bjt(s) => {
1103 if s.model.rc != 0.0 {
1104 node(&mut vars, &mut node_ids, &format!("{}#collector", s.name));
1105 }
1106 if s.model.rb != 0.0 {
1107 node(&mut vars, &mut node_ids, &format!("{}#base", s.name));
1108 }
1109 if s.model.re != 0.0 {
1110 node(&mut vars, &mut node_ids, &format!("{}#emitter", s.name));
1111 }
1112 }
1113 Spec::Mos1(s) => {
1116 if s.model.needs_prime(s.nrd) {
1117 node(&mut vars, &mut node_ids, &format!("{}#drain", s.name));
1118 }
1119 if s.model.needs_prime(s.nrs) {
1120 node(&mut vars, &mut node_ids, &format!("{}#source", s.name));
1121 }
1122 }
1123 Spec::Mos2(s) => {
1124 if s.model.needs_prime(s.nrd) {
1125 node(
1126 &mut vars,
1127 &mut node_ids,
1128 &format!("{}#internal#drain", s.name),
1129 );
1130 }
1131 if s.model.needs_prime(s.nrs) {
1132 node(
1133 &mut vars,
1134 &mut node_ids,
1135 &format!("{}#internal#source", s.name),
1136 );
1137 }
1138 }
1139 Spec::Mos3(s) => {
1140 if s.model.needs_prime(s.nrd) {
1141 node(
1142 &mut vars,
1143 &mut node_ids,
1144 &format!("{}#internal#drain", s.name),
1145 );
1146 }
1147 if s.model.needs_prime(s.nrs) {
1148 node(
1149 &mut vars,
1150 &mut node_ids,
1151 &format!("{}#internal#source", s.name),
1152 );
1153 }
1154 }
1155 Spec::Mos6(s) => {
1157 if s.model.needs_prime(s.nrd) {
1158 node(&mut vars, &mut node_ids, &format!("{}#drain", s.name));
1159 }
1160 if s.model.needs_prime(s.nrs) {
1161 node(&mut vars, &mut node_ids, &format!("{}#source", s.name));
1162 }
1163 }
1164 Spec::Bsim1(s) => {
1166 if s.model.needs_prime(s.nrd) {
1167 node(&mut vars, &mut node_ids, &format!("{}#drain", s.name));
1168 }
1169 if s.model.needs_prime(s.nrs) {
1170 node(&mut vars, &mut node_ids, &format!("{}#source", s.name));
1171 }
1172 }
1173 Spec::Bsim2(s) => {
1174 if s.model.needs_prime(s.nrd) {
1175 node(&mut vars, &mut node_ids, &format!("{}#drain", s.name));
1176 }
1177 if s.model.needs_prime(s.nrs) {
1178 node(&mut vars, &mut node_ids, &format!("{}#source", s.name));
1179 }
1180 }
1181 Spec::Bsim3(s) => {
1183 if s.model.needs_prime(s.nrd) {
1184 node(&mut vars, &mut node_ids, &format!("{}#drain", s.name));
1185 }
1186 if s.model.needs_prime(s.nrs) {
1187 node(&mut vars, &mut node_ids, &format!("{}#source", s.name));
1188 }
1189 }
1190 _ => {}
1191 }
1192 }
1193 let num_states = st.total();
1194
1195 let mut devices: Vec<Box<dyn Device>> = Vec::new();
1197 let mut names: Vec<String> = Vec::new();
1198 let mut sources: HashMap<String, (usize, bool)> = HashMap::new();
1199 let mut ports: Vec<PortInfo> = Vec::new();
1200 let mut notes = Vec::new();
1201 let get = |node_ids: &HashMap<String, Var>, n: &str| node_ids[n];
1202 let mut bsim3_models: Vec<crate::bsim3::Bsim3Knots> = Vec::new();
1206 for (spec, &base) in self.specs.iter().zip(&state_base) {
1207 names.push(spec.name().to_string());
1208 match spec {
1209 Spec::Bjt(s) => {
1210 let col = get(&node_ids, &s.nodes[0]);
1211 let bse = get(&node_ids, &s.nodes[1]);
1212 let emi = get(&node_ids, &s.nodes[2]);
1213 let sub = get(&node_ids, &s.nodes[3]);
1214 let prime = |rz: f64, suffix: &str, outer: Var| {
1215 if rz != 0.0 {
1216 node_ids[&format!("{}#{suffix}", s.name)]
1217 } else {
1218 outer
1219 }
1220 };
1221 let primes = [
1222 prime(s.model.rc, "collector", col),
1223 prime(s.model.rb, "base", bse),
1224 prime(s.model.re, "emitter", emi),
1225 ];
1226 let temp = self.instance_temp(&s.name, s.temp, opts);
1227 let bt = crate::bjt::BjtTemp::new(&s.name, &s.model, temp, opts.tnom);
1228 notes.extend(bt.notes.iter().cloned());
1229 devices.push(Box::new(crate::bjt::BjtI::new(
1230 [col, bse, emi, sub],
1231 primes,
1232 bt,
1233 s.area,
1234 s.off,
1235 s.ic,
1236 base,
1237 )));
1238 }
1239 Spec::Mos1(s) => {
1240 let d = get(&node_ids, &s.nodes[0]);
1241 let gt = get(&node_ids, &s.nodes[1]);
1242 let so = get(&node_ids, &s.nodes[2]);
1243 let b = get(&node_ids, &s.nodes[3]);
1244 let dp = if s.model.needs_prime(s.nrd) {
1245 node_ids[&format!("{}#drain", s.name)]
1246 } else {
1247 d
1248 };
1249 let sp = if s.model.needs_prime(s.nrs) {
1250 node_ids[&format!("{}#source", s.name)]
1251 } else {
1252 so
1253 };
1254 let geom = crate::mos1::MosGeom {
1255 l: s.l.unwrap_or(opts.defl),
1256 w: s.w.unwrap_or(opts.defw),
1257 ad: s.ad.unwrap_or(opts.defad),
1258 as_: s.as_.unwrap_or(opts.defas),
1259 pd: s.pd,
1260 ps: s.ps,
1261 nrd: s.nrd,
1262 nrs: s.nrs,
1263 };
1264 let temp = self.instance_temp(&s.name, s.temp, opts);
1265 let mt = crate::mos1::Mos1Temp::new(&s.name, &s.model, &geom, temp, opts.tnom)
1266 .map_err(Error::Build)?;
1267 notes.extend(mt.notes.iter().cloned());
1268 devices.push(Box::new(crate::mos1::Mos1I::new(
1269 [d, gt, so, b],
1270 [dp, sp],
1271 mt,
1272 geom,
1273 s.off,
1274 s.ic,
1275 base,
1276 )));
1277 }
1278 Spec::Mos2(s) => {
1279 let d = get(&node_ids, &s.nodes[0]);
1280 let gt = get(&node_ids, &s.nodes[1]);
1281 let so = get(&node_ids, &s.nodes[2]);
1282 let b = get(&node_ids, &s.nodes[3]);
1283 let dp = if s.model.needs_prime(s.nrd) {
1284 node_ids[&format!("{}#internal#drain", s.name)]
1285 } else {
1286 d
1287 };
1288 let sp = if s.model.needs_prime(s.nrs) {
1289 node_ids[&format!("{}#internal#source", s.name)]
1290 } else {
1291 so
1292 };
1293 let geom = crate::mos1::MosGeom {
1294 l: s.l.unwrap_or(opts.defl),
1295 w: s.w.unwrap_or(opts.defw),
1296 ad: s.ad.unwrap_or(opts.defad),
1297 as_: s.as_.unwrap_or(opts.defas),
1298 pd: s.pd,
1299 ps: s.ps,
1300 nrd: s.nrd,
1301 nrs: s.nrs,
1302 };
1303 let temp = self.instance_temp(&s.name, s.temp, opts);
1304 let mt = crate::mos2::Mos2Temp::new(&s.name, &s.model, &geom, temp, opts.tnom)
1305 .map_err(Error::Build)?;
1306 notes.extend(mt.notes.iter().cloned());
1307 devices.push(Box::new(crate::mos2::Mos2I::new(
1308 [d, gt, so, b],
1309 [dp, sp],
1310 mt,
1311 geom,
1312 s.off,
1313 s.ic,
1314 base,
1315 )));
1316 }
1317 Spec::Mos3(s) => {
1318 let d = get(&node_ids, &s.nodes[0]);
1319 let gt = get(&node_ids, &s.nodes[1]);
1320 let so = get(&node_ids, &s.nodes[2]);
1321 let b = get(&node_ids, &s.nodes[3]);
1322 let dp = if s.model.needs_prime(s.nrd) {
1323 node_ids[&format!("{}#internal#drain", s.name)]
1324 } else {
1325 d
1326 };
1327 let sp = if s.model.needs_prime(s.nrs) {
1328 node_ids[&format!("{}#internal#source", s.name)]
1329 } else {
1330 so
1331 };
1332 let geom = crate::mos1::MosGeom {
1333 l: s.l.unwrap_or(opts.defl),
1334 w: s.w.unwrap_or(opts.defw),
1335 ad: s.ad.unwrap_or(opts.defad),
1336 as_: s.as_.unwrap_or(opts.defas),
1337 pd: s.pd,
1338 ps: s.ps,
1339 nrd: s.nrd,
1340 nrs: s.nrs,
1341 };
1342 let temp = self.instance_temp(&s.name, s.temp, opts);
1343 let mt = crate::mos3::Mos3Temp::new(&s.name, &s.model, &geom, temp, opts.tnom)
1344 .map_err(Error::Build)?;
1345 devices.push(Box::new(crate::mos3::Mos3I::new(
1346 [d, gt, so, b],
1347 [dp, sp],
1348 mt,
1349 geom,
1350 s.off,
1351 s.ic,
1352 base,
1353 )));
1354 }
1355 Spec::Mos6(s) => {
1356 let d = get(&node_ids, &s.nodes[0]);
1357 let gt = get(&node_ids, &s.nodes[1]);
1358 let so = get(&node_ids, &s.nodes[2]);
1359 let b = get(&node_ids, &s.nodes[3]);
1360 let dp = if s.model.needs_prime(s.nrd) {
1361 node_ids[&format!("{}#drain", s.name)]
1362 } else {
1363 d
1364 };
1365 let sp = if s.model.needs_prime(s.nrs) {
1366 node_ids[&format!("{}#source", s.name)]
1367 } else {
1368 so
1369 };
1370 let geom = crate::mos1::MosGeom {
1372 l: s.l.unwrap_or(opts.defl),
1373 w: s.w.unwrap_or(opts.defw),
1374 ad: s.ad.unwrap_or(opts.defad),
1375 as_: s.as_.unwrap_or(opts.defas),
1376 pd: s.pd,
1377 ps: s.ps,
1378 nrd: if s.nrd == 0.0 { 1.0 } else { s.nrd },
1379 nrs: if s.nrs == 0.0 { 1.0 } else { s.nrs },
1380 };
1381 let temp = self.instance_temp(&s.name, s.temp, opts);
1382 let mt = crate::mos6::Mos6Temp::new(&s.name, &s.model, &geom, temp, opts.tnom)
1383 .map_err(Error::Build)?;
1384 notes.extend(mt.notes.iter().cloned());
1385 devices.push(Box::new(crate::mos6::Mos6I::new(
1386 [d, gt, so, b],
1387 [dp, sp],
1388 mt,
1389 geom,
1390 s.off,
1391 s.ic,
1392 base,
1393 )));
1394 }
1395 Spec::Bsim1(s) => {
1396 let d = get(&node_ids, &s.nodes[0]);
1397 let gt = get(&node_ids, &s.nodes[1]);
1398 let so = get(&node_ids, &s.nodes[2]);
1399 let b = get(&node_ids, &s.nodes[3]);
1400 let dp = if s.model.needs_prime(s.nrd) {
1401 node_ids[&format!("{}#drain", s.name)]
1402 } else {
1403 d
1404 };
1405 let sp = if s.model.needs_prime(s.nrs) {
1406 node_ids[&format!("{}#source", s.name)]
1407 } else {
1408 so
1409 };
1410 let geom = crate::mos1::MosGeom {
1413 l: s.l.unwrap_or(5e-6),
1414 w: s.w.unwrap_or(5e-6),
1415 ad: s.ad.unwrap_or(0.0),
1416 as_: s.as_.unwrap_or(0.0),
1417 pd: s.pd,
1418 ps: s.ps,
1419 nrd: s.nrd,
1420 nrs: s.nrs,
1421 };
1422 let sz = crate::bsim1::Bsim1Size::new(&s.name, &s.model, &geom)
1423 .map_err(Error::Build)?;
1424 devices.push(Box::new(crate::bsim1::Bsim1I::new(
1425 [d, gt, so, b],
1426 [dp, sp],
1427 sz,
1428 geom,
1429 s.off,
1430 s.ic,
1431 base,
1432 )));
1433 }
1434 Spec::Bsim2(s) => {
1435 let d = get(&node_ids, &s.nodes[0]);
1436 let gt = get(&node_ids, &s.nodes[1]);
1437 let so = get(&node_ids, &s.nodes[2]);
1438 let b = get(&node_ids, &s.nodes[3]);
1439 let dp = if s.model.needs_prime(s.nrd) {
1440 node_ids[&format!("{}#drain", s.name)]
1441 } else {
1442 d
1443 };
1444 let sp = if s.model.needs_prime(s.nrs) {
1445 node_ids[&format!("{}#source", s.name)]
1446 } else {
1447 so
1448 };
1449 let geom = crate::mos1::MosGeom {
1452 l: s.l.unwrap_or(5e-6),
1453 w: s.w.unwrap_or(5e-6),
1454 ad: s.ad.unwrap_or(0.0),
1455 as_: s.as_.unwrap_or(0.0),
1456 pd: s.pd,
1457 ps: s.ps,
1458 nrd: s.nrd,
1459 nrs: s.nrs,
1460 };
1461 let sz = crate::bsim2::Bsim2Size::new(&s.name, &s.model, &geom)
1462 .map_err(Error::Build)?;
1463 devices.push(Box::new(crate::bsim2::Bsim2I::new(
1464 [d, gt, so, b],
1465 [dp, sp],
1466 sz,
1467 geom,
1468 s.off,
1469 s.ic,
1470 base,
1471 )));
1472 }
1473 Spec::Bsim3(s) => {
1474 let d = get(&node_ids, &s.nodes[0]);
1475 let gt = get(&node_ids, &s.nodes[1]);
1476 let so = get(&node_ids, &s.nodes[2]);
1477 let b = get(&node_ids, &s.nodes[3]);
1478 let dp = if s.model.needs_prime(s.nrd) {
1479 node_ids[&format!("{}#drain", s.name)]
1480 } else {
1481 d
1482 };
1483 let sp = if s.model.needs_prime(s.nrs) {
1484 node_ids[&format!("{}#source", s.name)]
1485 } else {
1486 so
1487 };
1488 let geom = crate::bsim3::Bsim3Geom {
1489 l: s.l.unwrap_or(5e-6),
1490 w: s.w.unwrap_or(5e-6),
1491 ad: s.ad,
1492 as_: s.as_,
1493 pd: s.pd,
1494 ps: s.ps,
1495 nrd: s.nrd,
1496 nrs: s.nrs,
1497 m: s.m,
1498 delvto: s.delvto,
1499 mulu0: s.mulu0,
1500 };
1501 let (model, mt, pp) = crate::bsim3::Bsim3Knots::resolve(
1502 &mut bsim3_models,
1503 &s.name,
1504 &s.model,
1505 geom.l,
1506 geom.w,
1507 opts.temp,
1508 opts.tnom,
1509 &mut notes,
1510 )
1511 .map_err(Error::Build)?;
1512 devices.push(Box::new(crate::bsim3::Bsim3I::new(
1513 [d, gt, so, b],
1514 [dp, sp],
1515 model,
1516 mt,
1517 pp,
1518 geom,
1519 s.off,
1520 s.ic,
1521 base,
1522 )));
1523 }
1524 Spec::Resistor(s) => {
1525 let rt = self.res_temp.get(&s.name).copied().unwrap_or_default();
1526 devices.push(Box::new(ResistorI::with_temp(
1527 s.value,
1528 rt,
1529 opts,
1530 get(&node_ids, &s.nodes[0]),
1531 get(&node_ids, &s.nodes[1]),
1532 )));
1533 }
1534 Spec::Capacitor(s) => {
1535 devices.push(Box::new(CapacitorI::new(
1536 s.value,
1537 s.ic,
1538 get(&node_ids, &s.nodes[0]),
1539 get(&node_ids, &s.nodes[1]),
1540 base,
1541 )));
1542 }
1543 Spec::Inductor(s) => {
1544 devices.push(Box::new(InductorI::new(
1545 s.value,
1546 s.ic.unwrap_or(0.0),
1547 get(&node_ids, &s.nodes[0]),
1548 get(&node_ids, &s.nodes[1]),
1549 branches[&s.name],
1550 base,
1551 )));
1552 }
1553 Spec::Mutual(s) => {
1554 for l in [&s.l1, &s.l2] {
1555 if !inductors.contains_key(l) {
1556 return Err(Error::Build(format!(
1557 "`{}`: coupled inductor `{l}` does not exist",
1558 s.name
1559 )));
1560 }
1561 }
1562 let (flux1, l1) = inductors[&s.l1];
1563 let (flux2, l2) = inductors[&s.l2];
1564 devices.push(Box::new(MutualI::new(
1566 s.k,
1567 (l1 * l2).abs().sqrt(),
1568 branches[&s.l1],
1569 branches[&s.l2],
1570 flux1,
1571 flux2,
1572 )));
1573 }
1574 Spec::Vsource(s) => {
1575 sources.insert(s.name.clone(), (devices.len(), true));
1576 let pos = get(&node_ids, &s.nodes[0]);
1577 let neg = get(&node_ids, &s.nodes[1]);
1578 let br = branches[&s.name];
1579 let noise = NoiseWave::from_wave(s.wave.as_ref(), opts, &s.name)?;
1580 let mut dev = VsourceI::new(s.dc, s.wave.clone(), s.ac, pos, neg, br)
1581 .with_disto(s.distof1, s.distof2)
1582 .with_noise(noise);
1583 if let Some(p) = s.port.filter(RfPort::is_port) {
1584 let res = get(&node_ids, &format!("{}#res", s.name));
1585 dev = dev.with_port(res, p.z0);
1586 ports.push(PortInfo {
1587 name: s.name.clone(),
1588 num: p.num,
1589 z0: p.z0,
1590 pos,
1591 neg,
1592 br,
1593 });
1594 }
1595 devices.push(Box::new(dev));
1596 }
1597 Spec::Isource(s) => {
1598 sources.insert(s.name.clone(), (devices.len(), false));
1599 let noise = NoiseWave::from_wave(s.wave.as_ref(), opts, &s.name)?;
1600 devices.push(Box::new(
1601 IsourceI::new(
1602 s.dc,
1603 s.wave.clone(),
1604 s.ac,
1605 get(&node_ids, &s.nodes[0]),
1606 get(&node_ids, &s.nodes[1]),
1607 )
1608 .with_disto(s.distof1, s.distof2)
1609 .with_noise(noise),
1610 ));
1611 }
1612 Spec::Vcvs(s) => {
1613 let n = [
1614 get(&node_ids, &s.nodes[0]),
1615 get(&node_ids, &s.nodes[1]),
1616 get(&node_ids, &s.nodes[2]),
1617 get(&node_ids, &s.nodes[3]),
1618 ];
1619 devices.push(Box::new(VcvsI::new(s.gain, n, branches[&s.name])));
1620 }
1621 Spec::Vccs(s) => {
1622 let n = [
1623 get(&node_ids, &s.nodes[0]),
1624 get(&node_ids, &s.nodes[1]),
1625 get(&node_ids, &s.nodes[2]),
1626 get(&node_ids, &s.nodes[3]),
1627 ];
1628 devices.push(Box::new(VccsI::new(s.gain, n)));
1629 }
1630 Spec::Cccs(s) => {
1631 let ctrl = ctrl_branch(&branches, &s.name, &s.ctrl)?;
1632 devices.push(Box::new(CccsI::new(
1633 s.gain,
1634 get(&node_ids, &s.nodes[0]),
1635 get(&node_ids, &s.nodes[1]),
1636 ctrl,
1637 )));
1638 }
1639 Spec::Asrc(s) => {
1640 let pos = get(&node_ids, &s.nodes[0]);
1641 let neg = get(&node_ids, &s.nodes[1]);
1642 let voltage = s.kind == dune_netlist::ir::SourceKind::Voltage;
1643 if voltage && pos == neg {
1646 return Err(Error::Build(format!(
1647 "`{}`: voltage B source is shorted (both nodes `{}`)",
1648 s.name, s.nodes[0]
1649 )));
1650 }
1651 let tree = crate::asrc::Tree::new(&s.expr);
1652 let mut cols = Vec::with_capacity(tree.vars.len());
1653 for v in &tree.vars {
1654 cols.push(match v {
1655 crate::asrc::VarRef::Node(n) => get(&node_ids, n),
1656 crate::asrc::VarRef::Branch(d) => {
1659 branches.get(d).copied().ok_or_else(|| {
1660 Error::Build(format!(
1661 "`{}`: unknown controlling source `{d}`",
1662 s.name
1663 ))
1664 })?
1665 }
1666 });
1667 }
1668 let br = if voltage { branches[&s.name] } else { 0 };
1669 devices.push(Box::new(crate::asrc::AsrcI::new(
1670 s.kind, pos, neg, br, tree, cols,
1671 )));
1672 }
1673 Spec::Ccvs(s) => {
1674 let ctrl = ctrl_branch(&branches, &s.name, &s.ctrl)?;
1675 devices.push(Box::new(CcvsI::new(
1676 s.gain,
1677 get(&node_ids, &s.nodes[0]),
1678 get(&node_ids, &s.nodes[1]),
1679 branches[&s.name],
1680 ctrl,
1681 )));
1682 }
1683 Spec::Diode(s) => {
1684 let pos = get(&node_ids, &s.nodes[0]);
1685 let neg = get(&node_ids, &s.nodes[1]);
1686 let pos_prime = if s.model.rs != 0.0 {
1687 get(&node_ids, &format!("{}#internal", s.name))
1688 } else {
1689 pos
1690 };
1691 let temp = self.instance_temp(&s.name, s.temp, opts);
1692 let t = DiodeTemp::new(
1693 &s.name,
1694 &s.model,
1695 s.area,
1696 1.0,
1697 temp,
1698 opts.tnom,
1699 opts.reltol,
1700 );
1701 notes.extend(t.notes.iter().cloned());
1702 devices.push(Box::new(crate::diode::DiodeI::new(
1703 pos,
1704 neg,
1705 pos_prime,
1706 t,
1707 s.area,
1708 s.off,
1709 s.ic.unwrap_or(0.0),
1710 base,
1711 crate::diode::DiodeSensBase {
1712 name: s.name.clone(),
1713 model: s.model.clone(),
1714 temp,
1715 m: 1.0,
1716 default_tnom: opts.tnom,
1717 reltol: opts.reltol,
1718 },
1719 )));
1720 }
1721 Spec::Jfet(s) => {
1722 let (nodes, primes) =
1723 fet3_vars(&node_ids, &s.name, &s.nodes, s.model.rd, s.model.rs);
1724 let temp = self.instance_temp(&s.name, s.temp, opts);
1725 let t = crate::jfet::JfetTemp::new(&s.name, &s.model, temp, opts.tnom);
1726 notes.extend(t.notes.iter().cloned());
1727 devices.push(Box::new(crate::jfet::JfetI::new(
1728 nodes, primes, t, s.area, s.off, s.ic, base,
1729 )));
1730 }
1731 Spec::Mes(s) => {
1732 let (nodes, primes) =
1733 fet3_vars(&node_ids, &s.name, &s.nodes, s.model.rd, s.model.rs);
1734 let t = crate::mes::MesTemp::new(&s.model);
1735 devices.push(Box::new(crate::mes::MesI::new(
1736 nodes, primes, t, s.area, s.off, s.ic, base,
1737 )));
1738 }
1739 Spec::Ltra(s) => {
1740 let n = [
1741 get(&node_ids, &s.nodes[0]),
1742 get(&node_ids, &s.nodes[1]),
1743 get(&node_ids, &s.nodes[2]),
1744 get(&node_ids, &s.nodes[3]),
1745 ];
1746 let br = [
1747 get(&node_ids, &format!("{}#i1", s.name)),
1748 get(&node_ids, &format!("{}#i2", s.name)),
1749 ];
1750 let mut p =
1751 crate::ltra::LtraParams::new(&s.name, &s.model, opts.reltol, opts.abstol)
1752 .map_err(Error::Build)?;
1753 if opts.trytocompact {
1754 p.try_to_compact();
1755 }
1756 notes.extend(p.notes.iter().cloned());
1757 devices.push(Box::new(
1758 crate::ltra::LtraI::new(p, n, br).with_fit(opts.ltra_fit),
1759 ));
1760 }
1761 Spec::Tra(s) => {
1762 let n = [
1763 get(&node_ids, &s.nodes[0]),
1764 get(&node_ids, &s.nodes[1]),
1765 get(&node_ids, &s.nodes[2]),
1766 get(&node_ids, &s.nodes[3]),
1767 ];
1768 let v = |suffix: &str| get(&node_ids, &format!("{}#{suffix}", s.name));
1769 let p = crate::tra::TraParams::new(&s.name, s.z0, s.td, s.f, s.nl, s.ic)
1770 .map_err(Error::Build)?;
1771 devices.push(Box::new(crate::tra::TraI::new(
1772 p,
1773 n,
1774 [v("i1"), v("i2")],
1775 [v("int1"), v("int2")],
1776 )));
1777 }
1778 Spec::SwitchV(s) => {
1779 let m = &s.model;
1780 let p = crate::switch::SwitchParams::new(m.vt, m.vh, m.ron, m.roff, opts.gmin);
1781 let ctrl = crate::switch::Control::Voltage {
1782 pos: get(&node_ids, &s.nodes[2]),
1783 neg: get(&node_ids, &s.nodes[3]),
1784 };
1785 devices.push(Box::new(crate::switch::SwitchInst::new(
1786 p,
1787 get(&node_ids, &s.nodes[0]),
1788 get(&node_ids, &s.nodes[1]),
1789 ctrl,
1790 s.on,
1791 base,
1792 )));
1793 }
1794 Spec::SwitchI(s) => {
1795 let branch = ctrl_branch(&branches, &s.name, &s.ctrl)?;
1797 let m = &s.model;
1798 let p = crate::switch::SwitchParams::new(m.it, m.ih, m.ron, m.roff, opts.gmin);
1799 devices.push(Box::new(crate::switch::SwitchInst::new(
1800 p,
1801 get(&node_ids, &s.nodes[0]),
1802 get(&node_ids, &s.nodes[1]),
1803 crate::switch::Control::Current { branch },
1804 s.on,
1805 base,
1806 )));
1807 }
1808 }
1809 }
1810
1811 for (spec, dev) in self.specs.iter().zip(devices.iter_mut()) {
1813 if let Some(&g) = self.ic_given.get(spec.name()) {
1814 dev.set_ic_given(g);
1815 }
1816 }
1817
1818 let mut nodesets = Vec::new();
1820 for (name, v) in &self.nodesets {
1821 match node_ids.get(name) {
1822 Some(&var) if var != 0 => nodesets.push((var, *v)),
1823 Some(_) => notes.push(".nodeset on ground ignored".to_string()),
1824 None => notes.push(format!(".nodeset names unknown node `{name}`")),
1825 }
1826 }
1827 let mut ics = Vec::new();
1828 for (name, v) in &self.ics {
1829 match node_ids.get(name) {
1830 Some(&var) if var != 0 => ics.push((var, *v)),
1831 Some(_) => notes.push(".ic on ground ignored".to_string()),
1832 None => notes.push(format!(".ic names unknown node `{name}`")),
1833 }
1834 }
1835
1836 let ports = check_ports(ports)?;
1839 Ok(Built {
1840 vars,
1841 devices,
1842 names,
1843 nodesets,
1844 ics,
1845 sources,
1846 ports,
1847 num_states,
1848 state_ranges,
1849 notes,
1850 warm: self.warm.share(),
1851 })
1852 }
1853}
1854
1855fn check_ports(mut ports: Vec<PortInfo>) -> Result<Vec<PortInfo>, Error> {
1862 let count = ports.len();
1863 let mut seen = vec![false; count + 1];
1864 for p in ports.iter().rev() {
1865 let n = p.num as usize;
1866 if n > count {
1867 return Err(Error::Build(format!("{}: incorrect port ordering", p.name)));
1868 }
1869 if std::mem::replace(&mut seen[n], true) {
1870 return Err(Error::Build(format!("{}: duplicate port Index", p.name)));
1871 }
1872 }
1873 ports.sort_by_key(|p| p.num);
1874 Ok(ports)
1875}
1876
1877fn fet3_vars(
1879 node_ids: &HashMap<String, Var>,
1880 name: &str,
1881 nodes: &[String; 3],
1882 rd: f64,
1883 rs: f64,
1884) -> ([Var; 3], [Var; 2]) {
1885 let d = node_ids[&nodes[0]];
1886 let g = node_ids[&nodes[1]];
1887 let s = node_ids[&nodes[2]];
1888 let dp = if rd != 0.0 {
1889 node_ids[&format!("{name}#drain")]
1890 } else {
1891 d
1892 };
1893 let sp = if rs != 0.0 {
1894 node_ids[&format!("{name}#source")]
1895 } else {
1896 s
1897 };
1898 ([d, g, s], [dp, sp])
1899}
1900
1901fn ctrl_branch(branches: &HashMap<String, Var>, name: &str, ctrl: &str) -> Result<Var, Error> {
1902 if !ctrl.starts_with('v') {
1903 return Err(Error::Build(format!(
1904 "`{name}`: controlling source `{ctrl}` must be a voltage source"
1905 )));
1906 }
1907 branches
1908 .get(ctrl)
1909 .copied()
1910 .ok_or_else(|| Error::Build(format!("`{name}`: controlling source `{ctrl}` not found")))
1911}