pub trait Component {
fn nodes(&self) -> (i32, i32);
fn get_conductance(&self, dt: f32) -> f32;
fn get_current_source(&self, dt: f32) -> f32;
fn update_state(&mut self, v_a: f32, v_b: f32, dt: f32);
}
pub struct Resistor {
node_a: i32,
node_b: i32,
conductance: f32,
}
impl Resistor {
pub fn new(n1: i32, n2: i32, resistance: f32) -> Self {
Self {
node_a: n1,
node_b: n2,
conductance: 1.0 / resistance,
}
}
}
impl Component for Resistor {
fn nodes(&self) -> (i32, i32) {
(self.node_a, self.node_b)
}
fn get_conductance(&self, _dt: f32) -> f32 {
self.conductance
}
fn get_current_source(&self, _dt: f32) -> f32 {
0.0
}
fn update_state(&mut self, _v_a: f32, _v_b: f32, _dt: f32) {}
}
pub struct Capacitor {
node_a: i32,
node_b: i32,
capacitance: f32,
prev_voltage: f32,
}
impl Capacitor {
pub fn new(n1: i32, n2: i32, capacitance: f32) -> Self {
Self {
node_a: n1,
node_b: n2,
capacitance,
prev_voltage: 0.0,
}
}
}
impl Component for Capacitor {
fn nodes(&self) -> (i32, i32) {
(self.node_a, self.node_b)
}
fn get_conductance(&self, dt: f32) -> f32 {
self.capacitance / dt
}
fn get_current_source(&self, dt: f32) -> f32 {
(self.capacitance / dt) * self.prev_voltage
}
fn update_state(&mut self, v_a: f32, v_b: f32, _dt: f32) {
self.prev_voltage = v_a - v_b;
}
}
pub struct Inductor {
node_a: i32,
node_b: i32,
inductance: f32,
prev_current: f32,
}
impl Inductor {
pub fn new(n1: i32, n2: i32, inductance: f32) -> Self {
Self {
node_a: n1,
node_b: n2,
inductance,
prev_current: 0.0,
}
}
}
impl Component for Inductor {
fn nodes(&self) -> (i32, i32) {
(self.node_a, self.node_b)
}
fn get_conductance(&self, dt: f32) -> f32 {
dt / self.inductance
}
fn get_current_source(&self, _dt: f32) -> f32 {
-self.prev_current
}
fn update_state(&mut self, v_a: f32, v_b: f32, dt: f32) {
let voltage = v_a - v_b;
self.prev_current += (voltage * dt) / self.inductance;
}
}
pub struct Circuit {
components: Vec<Box<dyn Component + Send + Sync>>,
num_nodes: usize,
y_static: Box<[f32]>,
y_work: Box<[f32]>,
j: Box<[f32]>,
nodes: Box<[f32]>,
dt: f32,
}
impl Circuit {
pub fn new(sample_rate: f32, num_nodes: usize) -> Self {
let matrix_size = num_nodes * num_nodes;
Self {
components: Vec::new(),
num_nodes,
y_static: vec![0.0; matrix_size].into_boxed_slice(),
y_work: vec![0.0; matrix_size].into_boxed_slice(),
j: vec![0.0; num_nodes].into_boxed_slice(),
nodes: vec![0.0; num_nodes].into_boxed_slice(),
dt: 1.0 / sample_rate,
}
}
pub fn get_nodes(&self) -> usize {
self.num_nodes
}
pub fn get_devices(&self) -> usize {
self.components.len()
}
pub fn add_component(&mut self, component: Box<dyn Component + Send + Sync>) {
self.components.push(component);
}
pub fn preprocess(&mut self, impedance: f32) {
let n = self.num_nodes;
self.y_static.fill(0.0);
for comp in &self.components {
let (n1, n2) = comp.nodes();
let g = comp.get_conductance(self.dt);
if n1 > 0 {
self.y_static[(n1 as usize - 1) * n + (n1 as usize - 1)] += g;
}
if n2 > 0 {
self.y_static[(n2 as usize - 1) * n + (n2 as usize - 1)] += g;
}
if n1 > 0 && n2 > 0 {
self.y_static[(n1 as usize - 1) * n + (n2 as usize - 1)] -= g;
self.y_static[(n2 as usize - 1) * n + (n1 as usize - 1)] -= g;
}
}
if n >= 1 {
self.y_static[0] += impedance;
}
}
fn solve_linear_system(&mut self) {
let n = self.num_nodes;
self.y_work.copy_from_slice(&self.y_static);
for i in 0..n {
let mut pivot = i;
let mut max_val = self.y_work[i * n + i].abs();
for k in (i + 1)..n {
let val = self.y_work[k * n + i].abs();
if val > max_val {
max_val = val;
pivot = k;
}
}
if pivot != i {
for col in i..n {
self.y_work.swap(i * n + col, pivot * n + col);
}
self.j.swap(i, pivot);
}
let pivot_val = self.y_work[i * n + i];
if pivot_val.abs() < 1e-6 {
continue;
}
for k in (i + 1)..n {
let factor = self.y_work[k * n + i] / pivot_val;
for j in i..n {
self.y_work[k * n + j] -= factor * self.y_work[i * n + j];
}
self.j[k] -= factor * self.j[i];
}
}
for i in (0..n).rev() {
let mut sum = 0.0;
for j in (i + 1)..n {
sum += self.y_work[i * n + j] * self.nodes[j];
}
self.nodes[i] = (self.j[i] - sum) / self.y_work[i * n + i];
}
}
pub fn process(&mut self, input_voltage: f32, probe_node: usize) -> f32 {
let n = self.num_nodes;
self.j.fill(0.0);
let g_source = 1.0 / 0.1;
self.j[0] += input_voltage * g_source;
for comp in &self.components {
let is = comp.get_current_source(self.dt);
if is == 0.0 {
continue;
}
let (n1, n2) = comp.nodes();
if n1 > 0 {
self.j[n1 as usize - 1] -= is;
}
if n2 > 0 {
self.j[n2 as usize - 1] += is;
}
}
self.solve_linear_system();
for comp in &mut self.components {
let (n1, n2) = comp.nodes();
let v1 = if n1 == 0 {
0.0
} else {
self.nodes[n1 as usize - 1]
};
let v2 = if n2 == 0 {
0.0
} else {
self.nodes[n2 as usize - 1]
};
comp.update_state(v1, v2, self.dt);
}
if probe_node == 0 || probe_node > n {
return 0.0;
}
self.nodes[probe_node - 1]
}
}