spectre/analysis/
tran.rs

1//! Spectre transient analysis options and data structures.
2
3use crate::{ErrPreset, InstanceTail, SimSignal, Spectre};
4use arcstr::ArcStr;
5use rust_decimal::Decimal;
6use scir::{NamedSliceOne, SliceOnePath};
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9use std::sync::Arc;
10use substrate::schematic::conv::ConvertedNodePath;
11use substrate::simulation::data::{Save, SaveOutput, SaveTime};
12use substrate::simulation::waveform::{TimePoint, TimeWaveform, WaveformRef};
13use substrate::simulation::{Analysis, SimulationContext, Simulator, SupportedBy};
14use substrate::types::schematic::{NestedNode, NestedTerminal, RawNestedNode};
15
16/// A transient analysis.
17#[derive(Clone, Default, Debug, Eq, PartialEq, Serialize, Deserialize)]
18pub struct Tran {
19    /// Stop time (sec).
20    pub stop: Decimal,
21    /// Start time (sec).
22    ///
23    /// Defaults to 0.
24    pub start: Option<Decimal>,
25
26    /// The error preset.
27    pub errpreset: Option<ErrPreset>,
28
29    /// The maximum frequency for noise power spectral density.
30    ///
31    /// A nonzero value turns on noise sources during transient analysis.
32    ///
33    /// Defaults to 0.
34    pub noise_fmax: Option<Decimal>,
35
36    /// The minimum frequency for noise power spectral density.
37    pub noise_fmin: Option<Decimal>,
38}
39
40/// The result of a transient analysis.
41#[derive(Debug, Clone)]
42pub struct Output {
43    /// The time points of the transient simulation.
44    pub time: Arc<Vec<f64>>,
45    /// A map from signal name to values.
46    pub raw_values: HashMap<ArcStr, Arc<Vec<f64>>>,
47    /// A map from a save ID to a raw value identifier.
48    pub(crate) saved_values: HashMap<u64, ArcStr>,
49}
50
51/// An output transient waveform.
52#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize, Deserialize)]
53pub struct OutputWaveform {
54    /// Time samples.
55    pub t: Arc<Vec<f64>>,
56    /// Values corresponding to time samples in `t`.
57    pub x: Arc<Vec<f64>>,
58}
59
60impl OutputWaveform {
61    /// Converts an [`OutputWaveform`] to a [`WaveformRef`].
62    pub fn as_ref(&self) -> WaveformRef<'_, f64> {
63        WaveformRef::new(&self.t, &self.x)
64    }
65}
66
67impl TimeWaveform for OutputWaveform {
68    type Data = f64;
69    fn get(&self, idx: usize) -> Option<TimePoint<f64>> {
70        self.as_ref().get(idx)
71    }
72
73    fn len(&self) -> usize {
74        self.t.len()
75    }
76}
77
78impl Save<Spectre, Tran> for SaveOutput {
79    type SaveKey = ();
80    type Saved = Output;
81
82    fn save(
83        &self,
84        _ctx: &SimulationContext<Spectre>,
85        _opts: &mut <Spectre as Simulator>::Options,
86    ) -> <Self as Save<Spectre, Tran>>::SaveKey {
87    }
88
89    fn from_saved(
90        output: &<Tran as Analysis>::Output,
91        _key: &<Self as Save<Spectre, Tran>>::SaveKey,
92    ) -> <Self as Save<Spectre, Tran>>::Saved {
93        output.clone()
94    }
95}
96
97impl Save<Spectre, Tran> for SaveTime {
98    type SaveKey = ();
99    type Saved = Arc<Vec<f64>>;
100
101    fn save(
102        &self,
103        _ctx: &SimulationContext<Spectre>,
104        _opts: &mut <Spectre as Simulator>::Options,
105    ) -> <Self as Save<Spectre, Tran>>::SaveKey {
106    }
107
108    fn from_saved(
109        output: &<Tran as Analysis>::Output,
110        _key: &<Self as Save<Spectre, Tran>>::SaveKey,
111    ) -> <Self as Save<Spectre, Tran>>::Saved {
112        output.time.clone()
113    }
114}
115
116/// An identifier for a saved transient voltage.
117#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)]
118pub struct VoltageSaveKey(pub(crate) u64);
119
120impl Save<Spectre, Tran> for NestedNode {
121    type SaveKey = VoltageSaveKey;
122    type Saved = OutputWaveform;
123
124    fn save(
125        &self,
126        ctx: &SimulationContext<Spectre>,
127        opts: &mut <Spectre as Simulator>::Options,
128    ) -> <Self as Save<Spectre, Tran>>::SaveKey {
129        opts.save_tran_voltage(SimSignal::ScirVoltage(
130            match ctx.lib.convert_node_path(&self.path()).unwrap() {
131                ConvertedNodePath::Cell(path) => path.clone(),
132                ConvertedNodePath::Primitive {
133                    instances, port, ..
134                } => SliceOnePath::new(instances.clone(), NamedSliceOne::new(port.clone())),
135            },
136        ))
137    }
138
139    fn from_saved(
140        output: &<Tran as Analysis>::Output,
141        key: &<Self as Save<Spectre, Tran>>::SaveKey,
142    ) -> <Self as Save<Spectre, Tran>>::Saved {
143        OutputWaveform {
144            t: output.time.clone(),
145            x: output
146                .raw_values
147                .get(output.saved_values.get(&key.0).unwrap())
148                .unwrap()
149                .clone(),
150        }
151    }
152}
153
154impl Save<Spectre, Tran> for RawNestedNode {
155    type SaveKey = VoltageSaveKey;
156    type Saved = OutputWaveform;
157
158    fn save(
159        &self,
160        ctx: &SimulationContext<Spectre>,
161        opts: &mut <Spectre as Simulator>::Options,
162    ) -> <Self as Save<Spectre, Tran>>::SaveKey {
163        let itail = InstanceTail {
164            instance: ctx.lib.convert_instance_path(self.instances()).unwrap(),
165            tail: self.tail().clone(),
166        };
167        opts.save_tran_voltage(itail)
168    }
169
170    fn from_saved(
171        output: &<Tran as Analysis>::Output,
172        key: &<Self as Save<Spectre, Tran>>::SaveKey,
173    ) -> <Self as Save<Spectre, Tran>>::Saved {
174        let name = output
175            .saved_values
176            .get(&key.0)
177            .expect("failed to retrieve name corresponding to saved key");
178        OutputWaveform {
179            t: output.time.clone(),
180            x: output
181                .raw_values
182                .get(name.as_str())
183                .unwrap_or_else(|| panic!("no value for {name} found"))
184                .clone(),
185        }
186    }
187}
188
189/// An identifier for a saved transient current.
190#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
191pub struct CurrentSaveKey(pub(crate) Vec<u64>);
192
193/// Data saved from a nested terminal in a transient simulation.
194pub struct NestedTerminalOutput {
195    /// The voltage at the terminal.
196    pub v: OutputWaveform,
197    /// The current flowing through the terminal.
198    pub i: OutputWaveform,
199}
200
201impl Save<Spectre, Tran> for NestedTerminal {
202    type SaveKey = (VoltageSaveKey, CurrentSaveKey);
203    type Saved = NestedTerminalOutput;
204
205    fn save(
206        &self,
207        ctx: &SimulationContext<Spectre>,
208        opts: &mut <Spectre as Simulator>::Options,
209    ) -> <Self as Save<Spectre, Tran>>::SaveKey {
210        (
211            <NestedNode as Save<Spectre, Tran>>::save(self, ctx, opts),
212            CurrentSaveKey(
213                ctx.lib
214                    .convert_terminal_path(&self.path())
215                    .unwrap()
216                    .into_iter()
217                    .flat_map(|path| {
218                        opts.save_tran_current(SimSignal::ScirCurrent(match path {
219                            ConvertedNodePath::Cell(path) => path.clone(),
220                            ConvertedNodePath::Primitive {
221                                instances, port, ..
222                            } => SliceOnePath::new(
223                                instances.clone(),
224                                NamedSliceOne::new(port.clone()),
225                            ),
226                        }))
227                        .0
228                    })
229                    .collect(),
230            ),
231        )
232    }
233
234    fn from_saved(
235        output: &<Tran as Analysis>::Output,
236        key: &<Self as Save<Spectre, Tran>>::SaveKey,
237    ) -> <Self as Save<Spectre, Tran>>::Saved {
238        let v = OutputWaveform {
239            t: output.time.clone(),
240            x: output
241                .raw_values
242                .get(output.saved_values.get(&key.0.0).unwrap())
243                .unwrap()
244                .clone(),
245        };
246        let currents: Vec<Arc<Vec<f64>>> = key
247            .1
248            .0
249            .iter()
250            .map(|key| {
251                output
252                    .raw_values
253                    .get(output.saved_values.get(key).unwrap())
254                    .unwrap()
255                    .clone()
256            })
257            .collect();
258
259        let mut total_current = vec![0.; output.time.len()];
260        for tran_current in currents {
261            for (i, current) in tran_current.iter().enumerate() {
262                total_current[i] += *current;
263            }
264        }
265        NestedTerminalOutput {
266            v,
267            i: OutputWaveform {
268                t: output.time.clone(),
269                x: Arc::new(total_current),
270            },
271        }
272    }
273}
274
275impl Analysis for Tran {
276    type Output = Output;
277}
278
279impl SupportedBy<Spectre> for Tran {
280    fn into_input(self, inputs: &mut Vec<<Spectre as Simulator>::Input>) {
281        inputs.push(self.into());
282    }
283    fn from_output(
284        outputs: &mut impl Iterator<Item = <Spectre as Simulator>::Output>,
285    ) -> <Self as Analysis>::Output {
286        let item = outputs.next().unwrap();
287        item.try_into().unwrap()
288    }
289}