spectre/analysis/
ac.rs

1//! Spectre AC small-signal analysis options and data structures.
2
3use crate::{InstanceTail, SimSignal, Spectre};
4use arcstr::ArcStr;
5use num::complex::Complex64;
6use rust_decimal::Decimal;
7use scir::{NamedSliceOne, SliceOnePath};
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10use std::sync::Arc;
11use substrate::{
12    schematic::conv::ConvertedNodePath,
13    simulation::{
14        Analysis, SimulationContext, Simulator, SupportedBy,
15        data::{Save, SaveFreq, SaveOutput},
16    },
17    types::schematic::{NestedNode, NestedTerminal, RawNestedNode},
18};
19
20use super::Sweep;
21
22/// An AC analysis.
23#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
24pub struct Ac {
25    /// Start frequency (Hz).
26    ///
27    /// Defaults to 0.
28    pub start: Decimal,
29    /// Stop frequency (Hz).
30    pub stop: Decimal,
31    /// The sweep kind and number of points.
32    pub sweep: Sweep,
33}
34
35/// The result of an AC analysis.
36#[derive(Debug, Clone)]
37pub struct Output {
38    /// The frequency points of the AC simulation.
39    pub freq: Arc<Vec<f64>>,
40    /// A map from signal name to values.
41    pub raw_values: HashMap<ArcStr, Arc<Vec<Complex64>>>,
42    /// A map from a save ID to a raw value identifier.
43    pub(crate) saved_values: HashMap<u64, ArcStr>,
44}
45
46/// An identifier for a saved AC voltage.
47#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)]
48pub struct VoltageSaveKey(pub(crate) u64);
49
50/// An identifier for a saved AC current.
51#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
52pub struct CurrentSaveKey(pub(crate) Vec<u64>);
53
54impl Analysis for Ac {
55    type Output = Output;
56}
57
58impl SupportedBy<Spectre> for Ac {
59    fn into_input(self, inputs: &mut Vec<<Spectre as Simulator>::Input>) {
60        inputs.push(self.into());
61    }
62    fn from_output(
63        outputs: &mut impl Iterator<Item = <Spectre as Simulator>::Output>,
64    ) -> <Self as Analysis>::Output {
65        let item = outputs.next().unwrap();
66        item.try_into().unwrap()
67    }
68}
69
70impl Save<Spectre, Ac> for SaveOutput {
71    type SaveKey = ();
72    type Saved = Output;
73
74    fn save(
75        &self,
76        _ctx: &SimulationContext<Spectre>,
77        _opts: &mut <Spectre as Simulator>::Options,
78    ) -> <Self as Save<Spectre, Ac>>::SaveKey {
79    }
80
81    fn from_saved(
82        output: &<Ac as Analysis>::Output,
83        _key: &<Self as Save<Spectre, Ac>>::SaveKey,
84    ) -> <Self as Save<Spectre, Ac>>::Saved {
85        output.clone()
86    }
87}
88
89impl Save<Spectre, Ac> for SaveFreq {
90    type SaveKey = ();
91    type Saved = Arc<Vec<f64>>;
92
93    fn save(
94        &self,
95        _ctx: &SimulationContext<Spectre>,
96        _opts: &mut <Spectre as Simulator>::Options,
97    ) -> <Self as Save<Spectre, Ac>>::SaveKey {
98    }
99
100    fn from_saved(
101        output: &<Ac as Analysis>::Output,
102        _key: &<Self as Save<Spectre, Ac>>::SaveKey,
103    ) -> <Self as Save<Spectre, Ac>>::Saved {
104        output.freq.clone()
105    }
106}
107
108impl Save<Spectre, Ac> for NestedNode {
109    type SaveKey = VoltageSaveKey;
110    type Saved = Arc<Vec<Complex64>>;
111
112    fn save(
113        &self,
114        ctx: &SimulationContext<Spectre>,
115        opts: &mut <Spectre as Simulator>::Options,
116    ) -> <Self as Save<Spectre, Ac>>::SaveKey {
117        opts.save_ac_voltage(SimSignal::ScirVoltage(
118            match ctx.lib.convert_node_path(&self.path()).unwrap() {
119                ConvertedNodePath::Cell(path) => path.clone(),
120                ConvertedNodePath::Primitive {
121                    instances, port, ..
122                } => SliceOnePath::new(instances.clone(), NamedSliceOne::new(port.clone())),
123            },
124        ))
125    }
126
127    fn from_saved(
128        output: &<Ac as Analysis>::Output,
129        key: &<Self as Save<Spectre, Ac>>::SaveKey,
130    ) -> <Self as Save<Spectre, Ac>>::Saved {
131        output
132            .raw_values
133            .get(output.saved_values.get(&key.0).unwrap())
134            .unwrap()
135            .clone()
136    }
137}
138
139impl Save<Spectre, Ac> for RawNestedNode {
140    type SaveKey = VoltageSaveKey;
141    type Saved = Arc<Vec<Complex64>>;
142
143    fn save(
144        &self,
145        ctx: &SimulationContext<Spectre>,
146        opts: &mut <Spectre as Simulator>::Options,
147    ) -> <Self as Save<Spectre, Ac>>::SaveKey {
148        let itail = InstanceTail {
149            instance: ctx.lib.convert_instance_path(self.instances()).unwrap(),
150            tail: self.tail().clone(),
151        };
152        opts.save_ac_voltage(itail)
153    }
154
155    fn from_saved(
156        output: &<Ac as Analysis>::Output,
157        key: &<Self as Save<Spectre, Ac>>::SaveKey,
158    ) -> <Self as Save<Spectre, Ac>>::Saved {
159        output
160            .raw_values
161            .get(output.saved_values.get(&key.0).unwrap())
162            .unwrap()
163            .clone()
164    }
165}
166
167/// Data saved from a nested terminal in an AC simulation.
168pub struct NestedTerminalOutput {
169    /// The voltage at the terminal.
170    pub v: Arc<Vec<Complex64>>,
171    /// The current flowing through the terminal.
172    pub i: Arc<Vec<Complex64>>,
173}
174
175impl Save<Spectre, Ac> for NestedTerminal {
176    type SaveKey = (VoltageSaveKey, CurrentSaveKey);
177    type Saved = NestedTerminalOutput;
178
179    fn save(
180        &self,
181        ctx: &SimulationContext<Spectre>,
182        opts: &mut <Spectre as Simulator>::Options,
183    ) -> <Self as Save<Spectre, Ac>>::SaveKey {
184        (
185            <NestedNode as Save<Spectre, Ac>>::save(self, ctx, opts),
186            CurrentSaveKey(
187                ctx.lib
188                    .convert_terminal_path(&self.path())
189                    .unwrap()
190                    .into_iter()
191                    .flat_map(|path| {
192                        opts.save_tran_current(SimSignal::ScirCurrent(match path {
193                            ConvertedNodePath::Cell(path) => path.clone(),
194                            ConvertedNodePath::Primitive {
195                                instances, port, ..
196                            } => SliceOnePath::new(
197                                instances.clone(),
198                                NamedSliceOne::new(port.clone()),
199                            ),
200                        }))
201                        .0
202                    })
203                    .collect(),
204            ),
205        )
206    }
207
208    fn from_saved(
209        output: &<Ac as Analysis>::Output,
210        key: &<Self as Save<Spectre, Ac>>::SaveKey,
211    ) -> <Self as Save<Spectre, Ac>>::Saved {
212        let v = output
213            .raw_values
214            .get(output.saved_values.get(&key.0.0).unwrap())
215            .unwrap()
216            .clone();
217        let currents: Vec<Arc<Vec<Complex64>>> = key
218            .1
219            .0
220            .iter()
221            .map(|key| {
222                output
223                    .raw_values
224                    .get(output.saved_values.get(key).unwrap())
225                    .unwrap()
226                    .clone()
227            })
228            .collect();
229
230        let mut total_current = vec![Complex64::ZERO; output.freq.len()];
231        for tran_current in currents {
232            for (i, current) in tran_current.iter().enumerate() {
233                total_current[i] += *current;
234            }
235        }
236        NestedTerminalOutput {
237            v,
238            i: Arc::new(total_current),
239        }
240    }
241}