Skip to main content

rill_core_model/
lib.rs

1//! Wave Digital Filter (WDF) core and physical modeling — elements, adapters,
2//! analysis, and resonant models for analog circuit and acoustic simulation.
3//!
4//! All types are generic over [`rill_core::Transcendental`], supporting both `f32`
5//! and `f64`. SIMD-accelerated batch processing is available via
6//! `process_incident_vector` methods on [`Resistor`], [`Capacitor`],
7//! [`Inductor`], and [`Diode`], plus the free function
8//! `elements::process_batch_simd`.
9//!
10//! ## Modules
11//! - `macros` — WDF eDSL macros for defining elements and filters
12//! - `analysis` — frequency response and distortion analysis
13//! - `constants` — physical constants and tolerances
14//! - `wdf` — WDF-based filter models
15//! - `string` — 1D waveguide string models with stiffness and damping
16//! - `plate` — 2D FDTD waveguide mesh for plates and membranes
17//! - `modal` — modal synthesis via parallel resonant filter banks
18//! - `cavity` — Helmholtz cavity resonators and coupled cavity arrays
19//!
20//! # Design
21//!
22//! WDF elements are built around the [`WdfElement`] trait, which defines a
23//! port resistance, wave processing, and state update cycle. Elements can be
24//! combined via [`SeriesAdapter`] and [`ParallelAdapter`] to form arbitrary
25//! linear circuits. Nonlinear elements like [`Diode`] use Newton-Raphson
26//! iteration for implicit solution.
27//!
28//! References:
29//! - A. Fettweis, "Wave Digital Filters: Theory and Practice" (1986)
30//! - K. J. Werner et al., "An Improved and Generalized Diode Clipper
31//!   Model for Wave Digital Filters" (2015)
32
33#![warn(missing_docs)]
34#![deny(unsafe_code)]
35
36pub use rill_core::Transcendental;
37
38/// WDF eDSL macros for defining elements and filters
39pub mod macros;
40
41mod adapters;
42/// Frequency response and distortion analysis
43pub mod analysis;
44/// Physical constants and tolerances
45pub mod constants;
46mod elements;
47/// Analog tape recording and playback head models
48pub mod tape;
49
50/// WDF-based filter models
51pub mod wdf;
52
53/// Helmholtz cavity resonators and coupled cavity arrays
54pub mod cavity;
55/// Modal synthesis via parallel resonant filter banks
56pub mod modal;
57/// 2D FDTD waveguide mesh for plates and membranes
58pub mod plate;
59/// 1D waveguide string models with stiffness and damping
60pub mod string;
61
62pub use adapters::{ParallelAdapter, SeriesAdapter};
63pub use cavity::{CavityArray, HelmholtzCavity};
64pub use elements::{Capacitor, Diode, Inductor, OpAmp, Resistor};
65pub use modal::ModalModel;
66pub use plate::PlateModel;
67pub use string::StringModel;
68
69pub mod register;
70
71/// Base WDF element trait.
72///
73/// Every WDF element has a port resistance and processes incident
74/// waves to produce reflected waves. For SIMD batch processing,
75/// see the `process_incident_vector` methods on concrete element types.
76pub trait WdfElement<T: Transcendental>: Send + Sync {
77    /// Port resistance
78    fn port_resistance(&self) -> T;
79
80    /// Process incident wave, return reflected wave
81    fn process_incident(&mut self, a: T) -> T;
82
83    /// Update internal state (called after wave computation)
84    fn update_state(&mut self);
85
86    /// Current voltage across the element
87    fn voltage(&self) -> T;
88
89    /// Current current through the element
90    fn current(&self) -> T;
91
92    /// Reset to initial state
93    fn reset(&mut self);
94}
95
96/// Wave variables: a (incident), b (reflected)
97#[derive(Debug, Clone, Copy)]
98pub struct WaveVariables<T: Transcendental> {
99    /// Incident wave
100    pub a: T,
101    /// Reflected wave
102    pub b: T,
103}
104
105impl<T: Transcendental> WaveVariables<T> {
106    /// Create zero wave variables
107    pub fn new() -> Self {
108        Self {
109            a: T::ZERO,
110            b: T::ZERO,
111        }
112    }
113
114    /// Compute voltage and current from wave variables
115    pub fn to_voltage_current(&self, port_resistance: T) -> (T, T) {
116        let two = T::from_f32(2.0);
117        let v = (self.a + self.b) / two;
118        let i = (self.a - self.b) / (two * port_resistance);
119        (v, i)
120    }
121
122    /// Compute wave variables from voltage and current
123    pub fn from_voltage_current(v: T, i: T, port_resistance: T) -> Self {
124        let a = v + port_resistance * i;
125        let b = v - port_resistance * i;
126        Self { a, b }
127    }
128}
129
130impl<T: Transcendental> Default for WaveVariables<T> {
131    fn default() -> Self {
132        Self::new()
133    }
134}
135
136#[cfg(test)]
137mod tests {
138    use super::*;
139
140    #[test]
141    fn test_wave_variables() {
142        let wv: WaveVariables<f64> = WaveVariables::new();
143        assert_eq!(wv.a, 0.0);
144        assert_eq!(wv.b, 0.0);
145    }
146
147    #[test]
148    fn test_wave_to_voltage_current() {
149        let wv: WaveVariables<f64> = WaveVariables { a: 2.0, b: 0.5 };
150        let (v, i) = wv.to_voltage_current(100.0);
151        assert!((v - 1.25).abs() < 1e-10);
152        assert!((i - 0.0075).abs() < 1e-10);
153    }
154
155    #[test]
156    fn test_voltage_current_to_wave() {
157        let wv: WaveVariables<f64> = WaveVariables::from_voltage_current(1.0, 0.01, 100.0);
158        assert!((wv.a - 2.0).abs() < 1e-10);
159        assert!((wv.b - 0.0).abs() < 1e-10);
160    }
161}