Skip to main content

rill_core_model/wdf/
diode_clipper.rs

1/// Diode clipper with anti-parallel diodes as a single nonlinear element.
2use crate::constants::NEWTON_TOLERANCE;
3use crate::elements::Resistor;
4
5crate::wdf_element! {
6    name: AntiParallelDiode<T>,
7    params: { is: T, vt: T },
8    state: { rp: T },
9    port_resistance: |s| s.rp,
10    scattering: |s, a| {
11        let tolerance = T::from_f64(NEWTON_TOLERANCE);
12        let guess = s.vt * (T::ONE + a.abs() / (T::from_f32(2.0) * s.rp * s.is)).ln();
13        let mut v = guess.max(T::ZERO);
14        for _ in 0..12 {
15            let ev = (v / s.vt).exp();
16            let env = (-v / s.vt).exp();
17            let i = s.is * (ev - env);
18            let g = s.is * (ev + env) / s.vt;
19            let f = v + s.rp * i - a;
20            if f.abs() < tolerance { break; }
21            let df = T::ONE + s.rp * g;
22            v -= f / df;
23        }
24        let ev = (v / s.vt).exp();
25        let env = (-v / s.vt).exp();
26        let g = s.is * (ev + env) / s.vt;
27        s.rp = T::ONE / g.max(T::from_f64(1e-12));
28        s.voltage = v;
29        T::from_f32(2.0) * v - a
30    },
31    update: |_s| {},
32    reset: |s| { s.rp = T::from_f64(1e-3); },
33}
34
35crate::wdf_compose! {
36    name: DiodeClipper<T>,
37    kind: Series,
38    elements: (Resistor<T>, AntiParallelDiode<T>),
39}
40
41#[cfg(test)]
42mod tests {
43    use super::*;
44    use crate::constants::{BOLTZMANN, ELECTRON_CHARGE};
45    use crate::WdfElement;
46
47    fn make_clipper() -> DiodeClipper<f64> {
48        let r = Resistor::new(1000.0);
49        let vt = BOLTZMANN * 300.0 / ELECTRON_CHARGE;
50        let mut diode = AntiParallelDiode::new(1e-15, vt);
51        diode.reset();
52        DiodeClipper::new(r, diode)
53    }
54
55    #[test]
56    fn test_clipper_positive_clip() {
57        let mut c = make_clipper();
58        WdfElement::process_incident(&mut c, 10.0);
59        c.update_state();
60        let v: f64 = c.right.voltage();
61        assert!(
62            v > 0.0 && v < 1.0,
63            "should clip positive to ~0.6V: got {}",
64            v
65        );
66    }
67
68    #[test]
69    fn test_clipper_negative_clip() {
70        let mut c = make_clipper();
71        WdfElement::process_incident(&mut c, -10.0);
72        c.update_state();
73        let v: f64 = c.right.voltage();
74        assert!(
75            v < 0.0 && v > -1.0,
76            "should clip negative to ~-0.6V: got {}",
77            v
78        );
79    }
80
81    #[test]
82    fn test_clipper_symmetry() {
83        let mut c1 = make_clipper();
84        WdfElement::process_incident(&mut c1, 5.0);
85        c1.update_state();
86        let vp: f64 = c1.right.voltage();
87        let mut c2 = make_clipper();
88        WdfElement::process_incident(&mut c2, -5.0);
89        c2.update_state();
90        let vn: f64 = c2.right.voltage();
91        assert!((vp + vn).abs() < 0.1, "pos={} neg={}", vp, vn);
92    }
93}