Skip to main content

dune_core/
error.rs

1//! Engine errors. Deck-level problems surface as
2//! `dune_netlist::Diagnostic`s during elaboration; these are the
3//! failures of building or running a circuit.
4
5#[derive(Debug, thiserror::Error)]
6pub enum Error {
7    /// Bad programmatic construction (duplicate name, zero-valued
8    /// resistor, dangling reference, …).
9    #[error("{0}")]
10    Build(String),
11
12    /// The deck uses a device the engine does not implement yet.
13    #[error("`{name}`: {what} is not implemented yet")]
14    Unsupported { name: String, what: String },
15
16    /// The deck could not be elaborated into a circuit.
17    #[error("{0}")]
18    Elab(String),
19
20    /// Singular MNA matrix (floating node, voltage-source loop, …).
21    #[error("singular matrix: check node `{0}`")]
22    Singular(String),
23
24    /// `NIiter` gave up (`E_ITERLIM`).
25    #[error("too many iterations without convergence")]
26    IterLim,
27
28    /// The transient step shrank below `delmin` (`E_TIMESTEP`).
29    #[error("timestep too small at t = {time:.6e}")]
30    Timestep { time: f64 },
31
32    /// The transient attempted more internal timepoints than
33    /// [`Options::max_tran_steps`](crate::Options::max_tran_steps)
34    /// allows — Dune's guard against runaway breakpoint cascades
35    /// (D-0022); neither the 3f5 nor ngspice enforces `ITL5`.
36    #[error("transient exceeded {steps} internal steps at t = {time:.6e}")]
37    StepLimit { steps: usize, time: f64 },
38
39    /// `.pss`: the shooting iterations did not bring the period
40    /// mismatch `x(t₀+T) − x(t₀)` within tolerance. `residuals` holds
41    /// each iteration's mismatch in units of the tolerance
42    /// ([`Pss::residuals`](crate::Pss::residuals)). `period` is the
43    /// last period iterate of an autonomous oscillator (where `T` is
44    /// a Newton unknown); `None` for a driven circuit, whose period
45    /// is fixed.
46    #[error(
47        "periodic steady state did not converge in {iterations} shooting iterations \
48         (residual/tolerance per iteration: {}){}",
49        list_e(.residuals),
50        last_period(.period)
51    )]
52    PssConvergence {
53        iterations: usize,
54        residuals: Vec<f64>,
55        period: Option<f64>,
56    },
57
58    /// An output selection named a vector the solution does not have.
59    #[error("no such vector `{0}`")]
60    UnknownVector(String),
61}
62
63/// `; last period 1.234e-6 s` — an autonomous shooting's last `T`.
64fn last_period(p: &Option<f64>) -> String {
65    p.map_or_else(String::new, |t| format!("; last period {t:.6e} s"))
66}
67
68/// `1.234e-3, 5.000e-1` — a residual history in an error message.
69fn list_e(v: &[f64]) -> String {
70    v.iter()
71        .map(|r| format!("{r:.3e}"))
72        .collect::<Vec<_>>()
73        .join(", ")
74}