1use crate::dae::DAE;
23use crate::kernel::{Domain, ExprId, ExprPool};
24
25#[derive(Clone, Debug, PartialEq, Eq)]
31pub struct Port {
32 pub name: String,
34 pub potential: ExprId,
36 pub flow: ExprId,
38}
39
40impl Port {
41 pub fn new(name: &str, pool: &ExprPool) -> Self {
43 let potential = pool.symbol(format!("{name}.v"), Domain::Real);
44 let flow = pool.symbol(format!("{name}.i"), Domain::Real);
45 Port {
46 name: name.to_string(),
47 potential,
48 flow,
49 }
50 }
51}
52
53#[derive(Clone, Debug)]
59pub struct Component {
60 pub name: String,
62 pub ports: Vec<Port>,
64 pub equations: Vec<ExprId>,
66 pub internal_vars: Vec<ExprId>,
68 pub internal_derivs: Vec<ExprId>,
70}
71
72impl Component {
73 pub fn new(name: &str) -> Self {
75 Component {
76 name: name.to_string(),
77 ports: vec![],
78 equations: vec![],
79 internal_vars: vec![],
80 internal_derivs: vec![],
81 }
82 }
83
84 pub fn add_port(mut self, port: Port) -> Self {
86 self.ports.push(port);
87 self
88 }
89
90 pub fn add_equation(mut self, eq: ExprId) -> Self {
92 self.equations.push(eq);
93 self
94 }
95
96 pub fn add_internal_var(mut self, var: ExprId, deriv: ExprId) -> Self {
98 self.internal_vars.push(var);
99 self.internal_derivs.push(deriv);
100 self
101 }
102
103 pub fn port(&self, name: &str) -> Option<&Port> {
105 self.ports.iter().find(|p| p.name == name)
106 }
107}
108
109#[derive(Clone, Debug)]
115struct Connection {
116 port_a: Port,
117 port_b: Port,
118}
119
120#[derive(Clone, Debug, Default)]
122pub struct System {
123 components: Vec<Component>,
124 connections: Vec<Connection>,
125}
126
127impl System {
128 pub fn new() -> Self {
129 System::default()
130 }
131
132 pub fn add_component(&mut self, component: Component) {
134 self.components.push(component);
135 }
136
137 pub fn connect(&mut self, port_a: &Port, port_b: &Port) {
143 self.connections.push(Connection {
144 port_a: port_a.clone(),
145 port_b: port_b.clone(),
146 });
147 }
148
149 pub fn flatten(&self, time_var: ExprId, pool: &ExprPool) -> DAE {
154 let mut equations: Vec<ExprId> = Vec::new();
155 let mut variables: Vec<ExprId> = Vec::new();
156 let mut derivatives: Vec<ExprId> = Vec::new();
157
158 for comp in &self.components {
160 equations.extend_from_slice(&comp.equations);
161
162 for port in &comp.ports {
164 add_var_if_new(port.potential, pool, &mut variables, &mut derivatives);
165 add_var_if_new(port.flow, pool, &mut variables, &mut derivatives);
166 }
167
168 for (&var, &deriv) in comp.internal_vars.iter().zip(comp.internal_derivs.iter()) {
170 if !variables.contains(&var) {
171 variables.push(var);
172 derivatives.push(deriv);
173 }
174 }
175 }
176
177 let neg_one = pool.integer(-1_i32);
179 for conn in &self.connections {
180 let neg_b = pool.mul(vec![neg_one, conn.port_b.potential]);
182 let pot_eq = pool.add(vec![conn.port_a.potential, neg_b]);
183 equations.push(pot_eq);
184
185 let flow_eq = pool.add(vec![conn.port_a.flow, conn.port_b.flow]);
187 equations.push(flow_eq);
188 }
189
190 DAE::new(equations, variables, derivatives, time_var)
191 }
192}
193
194fn add_var_if_new(
196 var: ExprId,
197 pool: &ExprPool,
198 variables: &mut Vec<ExprId>,
199 derivatives: &mut Vec<ExprId>,
200) {
201 if variables.contains(&var) {
202 return;
203 }
204 let deriv_name = pool.with(var, |d| match d {
205 crate::kernel::ExprData::Symbol { name, .. } => format!("d{name}/dt"),
206 _ => "d?/dt".to_string(),
207 });
208 let deriv = pool.symbol(&deriv_name, Domain::Real);
209 variables.push(var);
210 derivatives.push(deriv);
211}
212
213pub fn resistor(name: &str, resistance: ExprId, pool: &ExprPool) -> Component {
219 let port_p = Port::new(&format!("{name}.p"), pool);
220 let port_n = Port::new(&format!("{name}.n"), pool);
221
222 let v = pool.add(vec![
224 port_p.potential,
225 pool.mul(vec![pool.integer(-1_i32), port_n.potential]),
226 ]);
227 let i = port_p.flow;
229
230 let ri = pool.mul(vec![resistance, i]);
232 let eq = pool.add(vec![v, pool.mul(vec![pool.integer(-1_i32), ri])]);
233
234 Component::new(name)
235 .add_port(port_p)
236 .add_port(port_n)
237 .add_equation(eq)
238}
239
240pub fn capacitor(name: &str, capacitance: ExprId, pool: &ExprPool) -> Component {
242 let port_p = Port::new(&format!("{name}.p"), pool);
243 let port_n = Port::new(&format!("{name}.n"), pool);
244
245 let v_name = format!("{name}.vc");
247 let v = pool.symbol(&v_name, Domain::Real);
248 let dv_name = format!("d{v_name}/dt");
249 let dv = pool.symbol(&dv_name, Domain::Real);
250
251 let i = port_p.flow;
253 let c_dv = pool.mul(vec![capacitance, dv]);
254 let eq = pool.add(vec![c_dv, pool.mul(vec![pool.integer(-1_i32), i])]);
255
256 let neg_vn = pool.mul(vec![pool.integer(-1_i32), port_n.potential]);
258 let v_def = pool.add(vec![
259 v,
260 pool.mul(vec![
261 pool.integer(-1_i32),
262 pool.add(vec![port_p.potential, neg_vn]),
263 ]),
264 ]);
265
266 Component::new(name)
267 .add_port(port_p)
268 .add_port(port_n)
269 .add_equation(eq)
270 .add_equation(v_def)
271 .add_internal_var(v, dv)
272}
273
274pub fn voltage_source(name: &str, voltage: ExprId, pool: &ExprPool) -> Component {
276 let port_p = Port::new(&format!("{name}.p"), pool);
277 let port_n = Port::new(&format!("{name}.n"), pool);
278
279 let neg_v = pool.mul(vec![pool.integer(-1_i32), port_n.potential]);
280 let diff_v = pool.add(vec![port_p.potential, neg_v]);
281 let neg_voltage = pool.mul(vec![pool.integer(-1_i32), voltage]);
282 let eq = pool.add(vec![diff_v, neg_voltage]);
283
284 Component::new(name)
285 .add_port(port_p)
286 .add_port(port_n)
287 .add_equation(eq)
288}
289
290#[cfg(test)]
295mod tests {
296 use super::*;
297 use crate::kernel::ExprPool;
298
299 fn p() -> ExprPool {
300 ExprPool::new()
301 }
302
303 #[test]
304 fn port_creation() {
305 let pool = p();
306 let port = Port::new("pin1", &pool);
307 assert_eq!(port.name, "pin1");
308 assert_ne!(port.potential, port.flow);
310 }
311
312 #[test]
313 fn resistor_has_one_equation() {
314 let pool = p();
315 let r_val = pool.symbol("R", crate::kernel::Domain::Real);
316 let comp = resistor("R1", r_val, &pool);
317 assert_eq!(comp.equations.len(), 1);
318 assert_eq!(comp.ports.len(), 2);
319 }
320
321 #[test]
322 fn capacitor_has_two_equations() {
323 let pool = p();
324 let c_val = pool.symbol("C", crate::kernel::Domain::Real);
325 let comp = capacitor("C1", c_val, &pool);
326 assert_eq!(comp.equations.len(), 2);
327 assert_eq!(comp.internal_vars.len(), 1);
328 }
329
330 #[test]
331 fn system_flatten_rc_circuit() {
332 let pool = p();
334 let r_val = pool.symbol("R", crate::kernel::Domain::Real);
335 let c_val = pool.symbol("C", crate::kernel::Domain::Real);
336 let v_src = pool.symbol("Vs", crate::kernel::Domain::Real);
337 let t = pool.symbol("t", crate::kernel::Domain::Real);
338
339 let res = resistor("R1", r_val, &pool);
340 let cap = capacitor("C1", c_val, &pool);
341 let src = voltage_source("V1", v_src, &pool);
342
343 let src_p = src.port("V1.p").unwrap().clone();
344 let src_n = src.port("V1.n").unwrap().clone();
345 let res_p = res.port("R1.p").unwrap().clone();
346 let res_n = res.port("R1.n").unwrap().clone();
347 let cap_p = cap.port("C1.p").unwrap().clone();
348 let cap_n = cap.port("C1.n").unwrap().clone();
349
350 let mut sys = System::new();
351 sys.add_component(src);
352 sys.add_component(res);
353 sys.add_component(cap);
354 sys.connect(&src_p, &res_p);
355 sys.connect(&res_n, &cap_p);
356 sys.connect(&cap_n, &src_n);
357
358 let dae = sys.flatten(t, &pool);
359 assert!(dae.n_equations() >= 3); assert!(dae.n_variables() >= 4); }
363
364 #[test]
365 fn connect_generates_two_equations() {
366 let pool = p();
367 let t = pool.symbol("t", crate::kernel::Domain::Real);
368 let r_val = pool.symbol("R", crate::kernel::Domain::Real);
369 let comp1 = resistor("R1", r_val, &pool);
370 let comp2 = resistor("R2", r_val, &pool);
371 let port_a = comp1.port("R1.n").unwrap().clone();
372 let port_b = comp2.port("R2.p").unwrap().clone();
373 let mut sys = System::new();
374 sys.add_component(comp1);
375 sys.add_component(comp2);
376 sys.connect(&port_a, &port_b);
377 let dae = sys.flatten(t, &pool);
378 assert_eq!(dae.n_equations(), 4);
380 }
381}