use std::collections::HashMap;
use crate::linalg::Pattern;
pub(crate) type Var = usize;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct Slot(pub(crate) usize);
pub(crate) const TRASH: Slot = Slot(0);
pub(crate) struct MatrixBuilder {
n: usize,
map: HashMap<(Var, Var), Slot>,
entries: Vec<(usize, usize)>,
row_slots: Vec<Vec<Slot>>,
}
impl MatrixBuilder {
pub fn new(n: usize) -> Self {
MatrixBuilder {
n,
map: HashMap::new(),
entries: Vec::new(),
row_slots: vec![Vec::new(); n + 1],
}
}
pub fn slot(&mut self, r: Var, c: Var) -> Slot {
if r == 0 || c == 0 {
return TRASH;
}
debug_assert!(r <= self.n && c <= self.n);
if let Some(s) = self.map.get(&(r, c)) {
return *s;
}
self.entries.push((r - 1, c - 1));
let s = Slot(self.entries.len()); self.map.insert((r, c), s);
self.row_slots[r].push(s);
s
}
pub fn pattern(&self) -> Pattern {
Pattern {
n: self.n,
entries: self.entries.clone(),
}
}
pub fn num_slots(&self) -> usize {
self.entries.len() + 1
}
pub fn entries(&self) -> &[(usize, usize)] {
&self.entries
}
pub fn slots_in_row(&self, r: Var) -> &[Slot] {
&self.row_slots[r]
}
pub fn rows_in_col(&self, c: Var) -> Vec<(Var, Slot)> {
let mut v: Vec<(Var, Slot)> = self
.map
.iter()
.filter(|((_, cc), _)| *cc == c)
.map(|((r, _), s)| (*r, *s))
.collect();
v.sort_unstable_by_key(|(r, _)| *r);
v
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum AnKind {
DcOp,
TranOp,
DcSweep,
Tran,
}
pub(crate) struct LoadCtx<'a> {
pub sink: StampSink<'a>,
pub sol: &'a [f64],
pub mode: InitMode,
pub kind: AnKind,
pub uic: bool,
pub time: f64,
pub step: f64,
pub final_time: f64,
pub gmin: f64,
pub reltol: f64,
pub abstol: f64,
pub srcfact: f64,
pub noncon: bool,
pub states: crate::state::StateView<'a>,
pub ag: [f64; 7],
pub order: usize,
pub method: crate::integ::Method,
pub pred_xfact: f64,
pub delta: f64,
pub fault: Option<&'static str>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum InitMode {
Jct,
Fix,
Float,
Tran,
Pred,
Smsig,
}
#[derive(Clone, Copy)]
pub(crate) struct LoadEnv<'a> {
pub sol: &'a [f64],
pub mode: InitMode,
pub kind: AnKind,
pub uic: bool,
pub time: f64,
pub step: f64,
pub final_time: f64,
pub gmin: f64,
pub reltol: f64,
pub abstol: f64,
pub srcfact: f64,
pub ag: [f64; 7],
pub order: usize,
pub method: crate::integ::Method,
pub pred_xfact: f64,
pub delta: f64,
}
impl<'a> LoadEnv<'a> {
pub fn ctx<'s>(&self, sink: StampSink<'s>, states: crate::state::StateView<'s>) -> LoadCtx<'s>
where
'a: 's,
{
LoadCtx {
sink,
sol: self.sol,
mode: self.mode,
kind: self.kind,
uic: self.uic,
time: self.time,
step: self.step,
final_time: self.final_time,
gmin: self.gmin,
reltol: self.reltol,
abstol: self.abstol,
srcfact: self.srcfact,
noncon: false,
states,
ag: self.ag,
order: self.order,
method: self.method,
pred_xfact: self.pred_xfact,
delta: self.delta,
fault: None,
}
}
}
pub(crate) enum StampSink<'a> {
Direct {
values: &'a mut [f64],
rhs: &'a mut [f64],
},
#[cfg_attr(not(feature = "parallel"), allow(dead_code))]
Record(&'a mut StampLog),
}
#[derive(Default)]
#[repr(align(128))]
pub(crate) struct StampLog {
pub values: Vec<(usize, f64)>,
pub rhs: Vec<(usize, f64)>,
}
#[cfg_attr(not(feature = "parallel"), allow(dead_code))]
impl StampLog {
pub fn clear(&mut self) {
self.values.clear();
self.rhs.clear();
}
pub fn replay(&self, values: &mut [f64], rhs: &mut [f64]) {
for &(k, v) in &self.values {
values[k] += v;
}
for &(k, v) in &self.rhs {
rhs[k] += v;
}
}
}
impl LoadCtx<'_> {
#[inline]
pub fn add(&mut self, s: Slot, v: f64) {
match &mut self.sink {
StampSink::Direct { values, .. } => values[s.0] += v,
StampSink::Record(log) => log.values.push((s.0, v)),
}
}
#[inline]
pub fn add_rhs(&mut self, var: Var, v: f64) {
match &mut self.sink {
StampSink::Direct { rhs, .. } => rhs[var] += v,
StampSink::Record(log) => log.rhs.push((var, v)),
}
}
pub fn is_dc(&self) -> bool {
matches!(self.kind, AnKind::DcOp | AnKind::TranOp | AnKind::DcSweep)
}
pub fn integrate(&mut self, cap: f64, q: usize) -> (f64, f64) {
crate::integ::integrate(&mut self.states, &self.ag, self.order, self.method, cap, q)
}
pub fn pred(&self, off: usize) -> f64 {
(1.0 + self.pred_xfact) * self.states.get(1, off)
- self.pred_xfact * self.states.get(2, off)
}
}
pub(crate) struct PzCtx<'a> {
pub g: &'a mut [f64],
pub b: &'a mut [f64],
pub s: num_complex::Complex64,
pub states: &'a crate::state::States,
}
impl PzCtx<'_> {
pub fn add_g(&mut self, sl: Slot, v: f64) {
self.g[sl.0] += v;
}
pub fn add_cap(&mut self, sl: Slot, c: f64) {
self.g[sl.0] += self.s.re * c;
self.b[sl.0] += self.s.im * c;
}
}
pub(crate) type NoiseGen = (Var, Var, f64);
#[derive(Clone, Copy, Debug, PartialEq)]
pub(crate) enum FlickerGen {
PowerLaw {
n1: Var,
n2: Var,
coef: f64,
ef: f64,
},
Unsupported(&'static str),
}
pub(crate) struct NoiseCtx<'a> {
pub re: &'a [f64],
pub im: &'a [f64],
pub freq: f64,
pub temp: f64,
pub states: &'a crate::state::States,
pub sp_sink: Option<&'a std::cell::RefCell<Vec<NoiseGen>>>,
pub flicker_sink: Option<&'a std::cell::RefCell<Vec<FlickerGen>>>,
}
impl NoiseCtx<'_> {
pub fn gain2(&self, n1: Var, n2: Var) -> f64 {
let r = self.re[n1] - self.re[n2];
let i = self.im[n1] - self.im[n2];
r * r + i * i
}
pub fn thermal(&self, n1: Var, n2: Var, g: f64) -> f64 {
let density = 4.0 * crate::consts::BOLTZ * self.temp * g;
self.record(n1, n2, density);
self.gain2(n1, n2) * density
}
pub fn thermal_at(&self, n1: Var, n2: Var, g: f64, temp: f64) -> f64 {
let density = 4.0 * crate::consts::BOLTZ * temp * g;
self.record(n1, n2, density);
self.gain2(n1, n2) * density
}
pub fn shot(&self, n1: Var, n2: Var, current: f64) -> f64 {
let density = 2.0 * crate::consts::CHARGE * current.abs();
self.record(n1, n2, density);
self.gain2(n1, n2) * density
}
pub fn flicker(&self, n1: Var, n2: Var, coef: f64, ef: f64) -> f64 {
if let Some(sink) = self.flicker_sink {
sink.borrow_mut()
.push(FlickerGen::PowerLaw { n1, n2, coef, ef });
}
let f_ef = if ef == 1.0 {
self.freq
} else {
self.freq.powf(ef)
};
self.gain2(n1, n2) * coef / f_ef
}
pub fn flicker_unsupported(&self, what: &'static str) {
if let Some(sink) = self.flicker_sink {
sink.borrow_mut().push(FlickerGen::Unsupported(what));
}
}
fn record(&self, n1: Var, n2: Var, density: f64) {
if let Some(sink) = self.sp_sink {
sink.borrow_mut().push((n1, n2, density));
}
}
}
pub(crate) struct ConvCtx<'a> {
pub rhs: &'a [f64],
pub sol: &'a [f64],
pub states: &'a crate::state::States,
pub reltol: f64,
pub abstol: f64,
pub vntol: f64,
pub gmin: f64,
}
pub(crate) struct AcCtx<'a> {
pub g: &'a mut [f64],
pub b: &'a mut [f64],
pub rhs_re: &'a mut [f64],
pub rhs_im: &'a mut [f64],
pub omega: f64,
pub states: &'a crate::state::States,
pub sp: bool,
}
impl AcCtx<'_> {
pub fn add_g(&mut self, s: Slot, v: f64) {
self.g[s.0] += v;
}
pub fn add_b(&mut self, s: Slot, v: f64) {
self.b[s.0] += v;
}
}
pub(crate) struct AcceptCtx<'a> {
pub time: f64,
pub first: bool,
pub sol: &'a [f64],
pub reltol: f64,
pub abstol: f64,
pub brk: bool,
pub step: f64,
pub final_time: f64,
pub delta_old: &'a [f64; 8],
pub min_break: f64,
pub requests: &'a mut Vec<(f64, bool)>,
pub periodic: Option<&'a PeriodicPast>,
}
#[derive(Clone, Debug)]
pub(crate) struct PeriodicPast {
pub period: f64,
pub phase: Vec<f64>,
pub rows: Vec<Vec<f64>>,
}
pub(crate) const MAX_PAST_SAMPLES: usize = 2_000_000;
impl PeriodicPast {
pub fn samples<'s>(
&'s self,
t0: f64,
from: f64,
x0: &'s [f64],
) -> Option<Vec<(f64, &'s [f64])>> {
let t = self.period;
let copies = ((t0 - from) / t).ceil().max(1.0);
let per = self.phase.len() + 1;
if !(copies.is_finite() && copies * per as f64 <= MAX_PAST_SAMPLES as f64) {
return None;
}
let copies = copies as usize;
let mut out = Vec::with_capacity(copies * per);
for k in (1..=copies).rev() {
let start = t0 - k as f64 * t;
out.push((start, x0));
for (u, row) in self.phase.iter().zip(&self.rows) {
out.push((start + u * t, row.as_slice()));
}
}
Some(out)
}
}
impl AcceptCtx<'_> {
pub fn set_break(&mut self, t: f64) {
self.requests.push((t, false));
}
pub fn set_slope_break(&mut self, t: f64) {
self.requests.push((t, true));
}
}