added web front end
This commit is contained in:
159
sim_toroid.py
159
sim_toroid.py
@@ -329,43 +329,34 @@ class ToroidSimulator:
|
||||
|
||||
turns_ratio = Ns / Np # a = Ns/Np
|
||||
|
||||
# --- Flux density ---------------------------------------------------
|
||||
# The primary voltage applied to the winding minus the resistive drop
|
||||
# sets the flux. We iterate once: compute ideal Ip, correct Vp seen by
|
||||
# core, then recompute.
|
||||
#
|
||||
# Iteration:
|
||||
# Pass 0 (ideal): B from full Vp
|
||||
# Pass 1: B from (Vp - Ip_0 * Rp) where Ip_0 is ideal primary current
|
||||
#
|
||||
# --- Circuit solution (exact voltage divider) -----------------------
|
||||
Ae_m2 = self._effective_Ae_mm2() * 1e-6
|
||||
|
||||
def _compute_op(Vp_core: float):
|
||||
"""Compute operating point given effective core voltage."""
|
||||
B = Vp_core / (4.44 * Np * Ae_m2 * freq_hz)
|
||||
# Ideal open-circuit secondary voltage
|
||||
Vs_oc = complex(Vp_core * turns_ratio, 0.0)
|
||||
# Secondary loop: Vs_oc = Is * (Rs + Z_load)
|
||||
Z_sec_total = complex(Rs, 0.0) + Z_load_complex
|
||||
Is_phasor = Vs_oc / Z_sec_total
|
||||
# Voltage across load
|
||||
Vs_phasor = Is_phasor * Z_load_complex
|
||||
# Reflect secondary current to primary (ideal transformer)
|
||||
Ip_reflected = Is_phasor / turns_ratio # phasor, Amperes
|
||||
return B, Is_phasor, Vs_phasor, Ip_reflected
|
||||
# Exact voltage divider: solve for Vp_core analytically.
|
||||
#
|
||||
# Circuit: Vp --[Rp]-- Vp_core --[ideal xfmr]-- [Rs + Z_load]
|
||||
#
|
||||
# Secondary impedance reflected to primary:
|
||||
# Z_sec_ref = (Rs + Z_load) / turns_ratio²
|
||||
# Voltage divider:
|
||||
# Vp_core = Vp * Z_sec_ref / (Rp + Z_sec_ref)
|
||||
# Then:
|
||||
# Is = Vp_core * turns_ratio / (Rs + Z_load)
|
||||
# Ip = Is * turns_ratio (= Vp_core / Z_sec_ref / turns_ratio ... simplified)
|
||||
|
||||
# Pass 0: ideal (ignore primary drop for now)
|
||||
_, Is0, _, Ip0 = _compute_op(Vp_rms)
|
||||
Ip0_mag = abs(Ip0)
|
||||
Z_sec_total = complex(Rs, 0.0) + Z_load_complex
|
||||
Z_sec_ref = Z_sec_total / (turns_ratio ** 2) # reflected to primary
|
||||
Z_total_primary = complex(Rp, 0.0) + Z_sec_ref
|
||||
|
||||
# Pass 1: correct for primary winding voltage drop
|
||||
# Primary drop is Ip * Rp (in phase with current; Rp is real)
|
||||
# Vp_core ≈ Vp_rms - Ip0 * Rp (phasor subtraction)
|
||||
# For simplicity treat Ip0 as real-valued magnitude for the correction
|
||||
# (conservative: subtracts in phase with voltage)
|
||||
Vp_core = max(Vp_rms - Ip0_mag * Rp, 0.0)
|
||||
Vp_core_phasor = complex(Vp_rms, 0.0) * Z_sec_ref / Z_total_primary
|
||||
Vp_core = abs(Vp_core_phasor)
|
||||
|
||||
B_peak_T, Is_phasor, Vs_phasor, Ip_phasor = _compute_op(Vp_core)
|
||||
B_peak_T = Vp_core / (4.44 * Np * Ae_m2 * freq_hz)
|
||||
|
||||
Vs_oc = Vp_core_phasor * turns_ratio
|
||||
Is_phasor = Vs_oc / Z_sec_total
|
||||
Vs_phasor = Is_phasor * Z_load_complex
|
||||
Ip_phasor = Is_phasor * turns_ratio
|
||||
|
||||
Is_rms = abs(Is_phasor)
|
||||
Vs_rms_out = abs(Vs_phasor)
|
||||
@@ -876,69 +867,69 @@ if __name__ == "__main__":
|
||||
constraints = SimConstraints(
|
||||
B_max_T=1.0,
|
||||
Vp_max=50.0,
|
||||
Vs_max=90.0,
|
||||
Ip_max=5.0,
|
||||
Vs_max=120.0,
|
||||
Ip_max=3.0,
|
||||
Is_max=2.0,
|
||||
P_out_max_W=25.0,
|
||||
)
|
||||
|
||||
print("=== Single operating point (10 ohm resistive load) ===")
|
||||
result = sim.simulate(
|
||||
Vp_rms=12.0,
|
||||
freq_hz=256.0,
|
||||
primary_tap=2,
|
||||
secondary_tap=1,
|
||||
Z_load=(100.0, 0.0),
|
||||
constraints=constraints,
|
||||
)
|
||||
print(result)
|
||||
print()
|
||||
# print("=== Single operating point (10 ohm resistive load) ===")
|
||||
# result = sim.simulate(
|
||||
# Vp_rms=12.0,
|
||||
# freq_hz=256.0,
|
||||
# primary_tap=2,
|
||||
# secondary_tap=1,
|
||||
# Z_load=(100.0, 0.0),
|
||||
# constraints=constraints,
|
||||
# )
|
||||
# print(result)
|
||||
# print()
|
||||
|
||||
print("=== Complex load (8 ohm + 1 mH at 50 kHz -> X ~= 314 ohm) ===")
|
||||
X = 2 * math.pi * 50_000.0 * 1e-3
|
||||
result2 = sim.simulate(
|
||||
Vp_rms=24.0,
|
||||
freq_hz=50_000.0,
|
||||
primary_tap=2,
|
||||
secondary_tap=1,
|
||||
Z_load=(8.0, X),
|
||||
constraints=constraints,
|
||||
)
|
||||
print(result2)
|
||||
print()
|
||||
# print("=== Complex load (8 ohm + 1 mH at 50 kHz -> X ~= 314 ohm) ===")
|
||||
# X = 2 * math.pi * 50_000.0 * 1e-3
|
||||
# result2 = sim.simulate(
|
||||
# Vp_rms=24.0,
|
||||
# freq_hz=50_000.0,
|
||||
# primary_tap=2,
|
||||
# secondary_tap=1,
|
||||
# Z_load=(8.0, X),
|
||||
# constraints=constraints,
|
||||
# )
|
||||
# print(result2)
|
||||
# print()
|
||||
|
||||
print("=== Tap sweep ===")
|
||||
all_results = sweep_taps(sim, 24.0, 50_000.0, (10.0, 0.0), constraints)
|
||||
for r in all_results:
|
||||
feasible = "OK" if r.feasible else "VIOL"
|
||||
print(
|
||||
f" P{r.primary_tap}/S{r.secondary_tap} "
|
||||
f"Np={r.Np_eff:3d} Ns={r.Ns_eff:3d} "
|
||||
f"Vs={r.Vs_rms:.3f}V Is={r.Is_rms:.4f}A "
|
||||
f"P_out={r.P_out_W:.3f}W eff={r.efficiency*100:.2f}% [{feasible}]"
|
||||
)
|
||||
# print("=== Tap sweep ===")
|
||||
# all_results = sweep_taps(sim, 24.0, 50_000.0, (10.0, 0.0), constraints)
|
||||
# for r in all_results:
|
||||
# feasible = "OK" if r.feasible else "VIOL"
|
||||
# print(
|
||||
# f" P{r.primary_tap}/S{r.secondary_tap} "
|
||||
# f"Np={r.Np_eff:3d} Ns={r.Ns_eff:3d} "
|
||||
# f"Vs={r.Vs_rms:.3f}V Is={r.Is_rms:.4f}A "
|
||||
# f"P_out={r.P_out_W:.3f}W eff={r.efficiency*100:.2f}% [{feasible}]"
|
||||
# )
|
||||
|
||||
print()
|
||||
print("=== Optimize: find best taps + Vp to deliver ~10 W at 256 Hz ===")
|
||||
opt = sim.optimize(
|
||||
freq_hz=256.0,
|
||||
Z_load=(10.0, 0.0),
|
||||
target_power_W=25.0,
|
||||
constraints=constraints,
|
||||
Vp_min=1.0,
|
||||
Vp_max=50.0,
|
||||
Vp_steps=200,
|
||||
power_tol_pct=2.0,
|
||||
)
|
||||
if opt is None:
|
||||
print(" No feasible solution found.")
|
||||
else:
|
||||
print(opt)
|
||||
# print()
|
||||
# print("=== Optimize: find best taps + Vp to deliver ~10 W at 256 Hz ===")
|
||||
# opt = sim.optimize(
|
||||
# freq_hz=256.0,
|
||||
# Z_load=(10.0, 0.0),
|
||||
# target_power_W=25.0,
|
||||
# constraints=constraints,
|
||||
# Vp_min=1.0,
|
||||
# Vp_max=50.0,
|
||||
# Vp_steps=200,
|
||||
# power_tol_pct=2.0,
|
||||
# )
|
||||
# if opt is None:
|
||||
# print(" No feasible solution found.")
|
||||
# else:
|
||||
# print(opt)
|
||||
|
||||
print()
|
||||
print("=== Operating-point sweep (3 freqs x 3 loads) ===")
|
||||
freqs = [256.0, 870.0, 3140.0, 8900.0]
|
||||
loads = [(50.0, 0.0), (100.0, 0.0), (200.0, 0.0), (600.0, 0.0)]
|
||||
loads = [(10.0, 0.0), (50.0, 0.0), (100.0, 0.0), (200.0, 0.0), (600.0, 0.0), (2000.0, 0.0)]
|
||||
entries = sweep_operating_points(
|
||||
sim=sim,
|
||||
frequencies=freqs,
|
||||
|
||||
Reference in New Issue
Block a user