1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
//! Electrical circuits with compile-time dimensional analysis.
//!
//! Demonstrates: Ohm's law, RC circuit, DC motor equation, power analysis.
//!
//! Run with: cargo run --example units_electrical
use symplex::prelude::*;
use symplex::units::*;
#[allow(non_snake_case)]
fn main() {
let ctx = Context::new();
println!("═══════════════════════════════════════════════════════════════");
println!(" Symplex: Electrical Circuits — Dimensional Analysis");
println!("═══════════════════════════════════════════════════════════════\n");
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Section 1: Ohm's Law & Power
// V = IR, P = IV, P = I²R, dP/dI = 2IR
// Named type arithmetic — every product is compile-time checked.
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
println!("── Ohm's Law & Power ──");
// Named typed variables — the compiler tracks their dimensions
let i = Current::symbol(&ctx, "I");
let r = Resistance::symbol(&ctx, "R");
// V = IR — Current × Resistance → Voltage (compile-time checked!)
let v = symplex::dim!(ctx, Voltage: i * r);
println!(" V = I·R = {}", v);
// P = IV — Current × Voltage → Power (compile-time checked!)
let p = symplex::dim!(ctx, Power: i * v);
println!(" P = I·V = {}", p);
// Expand P = I·(I·R) to see the I²R form
let p_expanded = p.clone().expand();
println!(" P expanded = {}", p_expanded);
// dP/dI: typed differentiation — Power / Current → Voltage
// The compiler verifies that differentiating Power w.r.t. Current
// produces Voltage (since [W]/[A] = [V]).
let dp_di: Voltage = p.diff_wrt(&i);
println!(" dP/dI = {} (should be 2·I·R)", dp_di);
// Numerical evaluation: I = 3 A, R = 47 Ω
let v_num = v
.clone()
.subs(&i, &ctx.int(3))
.subs(&r, &ctx.int(47))
.eval();
println!("\n Numerical (I=3 A, R=47 Ω):");
println!(" V = {}", v_num);
let p_num = p
.clone()
.subs(&i, &ctx.int(3))
.subs(&r, &ctx.int(47))
.eval();
println!(" P = {}", p_num);
let dp_di_num = dp_di.subs(&i, &ctx.int(3)).subs(&r, &ctx.int(47)).eval();
println!(" dP/dI = {}", dp_di_num);
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Section 2: RC Circuit Time Constant
// τ = R·C
// V_C(t) = V₀·(1 − e^(−t/(R·C)))
// Built with expr! for the complex exponential formula.
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
println!("\n── RC Circuit Time Constant ──");
// Raw variables for expr! — most ergonomic for the exponential formula
let ctx = Context::new();
symplex::syms!(ctx; R, C, V0, t);
// Time constant τ = R·C (has dimension of Time)
let tau = Time::from_ex(expr!(ctx, R * C));
println!(" τ = R·C = {}", tau);
// Capacitor voltage during charging:
// V_C(t) = V₀·(1 − e^(−t/(R·C)))
let v_cap = Voltage::from_ex(expr!(ctx, V0 * (1 - exp(-t / (R * C)))));
println!(" V_C(t) = {}", v_cap);
// Charging current: I(t) = dV_C/dt · C = (V₀/R)·e^(−t/(R·C))
// We differentiate V_C w.r.t. t (raw diff) and wrap as the rate
let dv_dt = v_cap.diff(&t);
println!(" dV_C/dt = {}", dv_dt);
// Numerical: R = 1 kΩ = 1000 Ω, C = 100 µF = 1/10000 F,
// V₀ = 5 V, t = 0.1 s
// τ = 1000 × 0.0001 = 0.1 s, so at t = τ we expect ≈ 63.2% of V₀
let tau_num = tau
.subs(&R, &ctx.int(1000))
.subs(&C, &ctx.rational(1, 10000))
.eval();
println!("\n Numerical (R=1kΩ, C=100µF, V₀=5V):");
println!(" τ = {}", tau_num);
let v_cap_at_tau = v_cap
.clone()
.subs(&R, &ctx.int(1000))
.subs(&C, &ctx.rational(1, 10000))
.subs(&V0, &ctx.int(5))
.subs(&t, &ctx.rational(1, 10))
.eval();
println!(" V_C(t=0.1s = τ) = {}", v_cap_at_tau);
// f64 check: should be ≈ 5·(1 - e⁻¹) ≈ 3.1606
let v_cap_f64 = v_cap
.subs(&R, &ctx.int(1000))
.subs(&C, &ctx.rational(1, 10000))
.subs(&V0, &ctx.int(5))
.subs(&t, &ctx.rational(1, 10))
.eval_f64()
.unwrap();
println!(" V_C(t=τ) ≈ {:.4} V (f64, expect ≈3.1606)", v_cap_f64);
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Section 3: DC Motor Steady-State
// V = R·I + Ke·ω
// P_in = I·V, P_mech = Ke·ω·I
// Named types enforce dimension correctness at every step.
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
println!("\n── DC Motor Steady-State ──");
// Named typed variables for the motor equation
let r_motor = Resistance::symbol(&ctx, "R_m");
let i_motor = Current::symbol(&ctx, "I_m");
let omega = AngularVelocity::symbol(&ctx, "ω");
// Back-EMF constant Ke has units of Wb (V·s/rad ≡ magnetic flux)
// MagneticFlux × AngularVelocity → Voltage (via dim! macro)
let ke = MagneticFlux::symbol(&ctx, "Ke");
// Resistive voltage drop: Resistance × Current → Voltage
let v_resistive = symplex::dim!(ctx, Voltage: r_motor * i_motor);
println!(" V_R = R·I = {}", v_resistive);
// Back-EMF: MagneticFlux × AngularVelocity → Voltage
let v_emf = symplex::dim!(ctx, Voltage: ke * omega);
println!(" V_emf = Ke·ω = {}", v_emf);
// Total supply voltage: Voltage + Voltage → Voltage (same-type addition)
let v_supply: Voltage = &v_resistive + &v_emf;
println!(" V_supply = R·I + Ke·ω = {}", v_supply);
// Input electrical power: Current × Voltage → Power
let p_in = symplex::dim!(ctx, Power: i_motor * v_supply);
println!(" P_in = I·V = {}", p_in);
// Mechanical output power: Ke·ω·I
// (MagneticFlux × AngularVelocity → Voltage, then Voltage × Current → Power)
let p_mech = symplex::dim!(ctx, Power: i_motor * v_emf);
println!(" P_mech = Ke·ω·I = {}", p_mech);
// Resistive loss: I²R (Current × Voltage_R → Power)
let p_loss = symplex::dim!(ctx, Power: i_motor * v_resistive);
println!(" P_loss = I²R = {}", p_loss);
// Power balance: P_in = P_mech + P_loss (conceptual)
println!(" ✓ P_in = P_mech + P_loss (dimension-verified)");
// Numerical motor example:
// R_m = 2 Ω, Ke = 0.05 Wb, ω = 100 rad/s, → V_emf = 5 V
// V_supply = 12 V → I = (V - Ke·ω)/R = (12 - 5)/2 = 3.5 A
let ke_ex = ke.inner();
let omega_ex = omega.inner();
let r_m_ex = r_motor.inner();
let i_m_ex = i_motor.inner();
let v_emf_num = v_emf
.subs(&ke, &ctx.rational(5, 100))
.subs(&omega, &ctx.int(100))
.eval();
println!("\n Numerical (R=2Ω, Ke=0.05Wb, ω=100rad/s):");
println!(" V_emf = {}", v_emf_num);
let p_mech_f64 = p_mech
.eval_f64_with(&[
(i_m_ex, 3),
(ke_ex, 1), // approximate Ke=1 for integer check
(omega_ex, 100),
])
.unwrap();
println!(" P_mech(I=3, Ke≈1, ω=100) ≈ {:.1} W (f64)", p_mech_f64);
let p_loss_f64 = p_loss.eval_f64_with(&[(i_m_ex, 3), (r_m_ex, 2)]).unwrap();
println!(" P_loss(I=3, R=2) = {:.1} W (f64)", p_loss_f64);
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Section 4: Unit Conversions
// Demonstrate constructor helpers that normalize to SI base units.
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
println!("\n── Unit Conversions ──");
// Current: 500 milliamps → amperes
let small_current = Current::milliamperes(&ctx.int(500));
println!(" 500 mA = {}", small_current.eval());
// Resistance: 4.7 kilohms → ohms
let big_resistor = Resistance::kilohms(&ctx.rational(47, 10));
println!(" 4.7 kΩ = {}", big_resistor.eval());
// Power: 1 horsepower → watts
let one_hp = Power::horsepower(&ctx.int(1));
println!(" 1 hp = {}", one_hp.eval());
// Frequency: 3600 RPM → hertz
let motor_speed = Frequency::rpm(&ctx.int(3600));
println!(" 3600 RPM = {}", motor_speed.eval());
// Capacitance: 100 µF → farads
let cap = Capacitance::microfarads(&ctx.int(100));
println!(" 100 µF = {}", cap.eval());
// Voltage: 3300 mV → volts
let logic_level = Voltage::millivolts(&ctx.int(3300));
println!(" 3300 mV = {}", logic_level.eval());
// Charge: 2000 mAh → coulombs
let battery = Charge::milliampere_hours(&ctx.int(2000));
println!(" 2000 mAh = {}", battery.eval());
// Inductance: 10 mH → henrys
let coil = Inductance::millihenrys(&ctx.int(10));
println!(" 10 mH = {}", coil.eval());
println!("\n── Physical Constants in Circuits ──");
{
use symplex::units::constants;
let e_charge = constants::elementary_charge(&ctx);
println!(" Elementary charge: e = {}", e_charge);
println!(" e = {:.10e} C", e_charge.eval_f64().unwrap());
// Energy of an electron accelerated through 1V:
// E = eV = 1 eV = 1.602e-19 J
let one_volt = Voltage::constant(&ctx, 1);
// charge × voltage = energy (Charge × Voltage = Energy via dim!)
let energy = symplex::dim!(ctx, Energy: e_charge * one_volt);
println!(
" Energy of 1 eV = {} = {:.6e} J",
energy,
energy.eval_f64().unwrap()
);
}
println!("\n✓ All done!");
}