Add eddy-current losses in the slug -- the missing loss channel
The user caught the evolution reporting 83.9% efficiency, which is unphysical (real coilguns are single-digit %). Root cause: the model's only loss channel was copper resistance; iron had no losses at all, so the reluctance force came "for free" and the optimizer climbed into that corner. Adds eddy-current loss: the solid steel slug acts as a shorted secondary (1-turn transformer), and its reflected resistance R_eddy = (wM)^2/R_e is added to the circuit while the slug is inside the coil (x overlap(x)). Energy now honestly goes to slug heating instead of kinetic. The formula matches the classical solid-cylinder eddy loss (P ~ sigma*w^2*B^2*a^4), so it's physically grounded, not tuned to a target. Wired through schema/JSON (slug resistivity), losses.py, circuit.py, stage.py, and the GPU batch integrator; energy conservation still holds (0.025%). Effect: best efficiency 83.9% -> ~47%, and the distribution is now realistic (median ~0%, most configs single-digit). 47% is still an optimistic ceiling -- it's the optimizer's single best exploit, and the model still omits tube friction, air drag, skin-effect field penetration (skin depth ~0.44mm < slug radius), and timing imperfection. Hysteresis computed but not in the dynamics (negligible, ~1e-4 J vs eddy). 89 tests. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -38,7 +38,7 @@ def _switch(on_resistance_ohm: float = 0.02) -> SwitchSpec:
|
||||
)
|
||||
|
||||
|
||||
def _run(wire, switch, capacitor=CAPACITOR):
|
||||
def _run(wire, switch, capacitor=CAPACITOR, projectile=PROJECTILE):
|
||||
# turns_per_layer выбран так, чтобы длина катушки (~1.7см) была сравнима
|
||||
# со снарядом (2см) — иначе снаряд, войдя глубоко внутрь длинной катушки,
|
||||
# оказывается в плоской зоне перекрытия (dL/dx=0) и сила не действует.
|
||||
@@ -53,7 +53,7 @@ def _run(wire, switch, capacitor=CAPACITOR):
|
||||
sensor_to_coil_distance_m=0.02,
|
||||
charge_voltage_v=350.0,
|
||||
)
|
||||
return run_stage(entry_x_m=-0.05, entry_v_mps=5.0, stage=stage, projectile=PROJECTILE)
|
||||
return run_stage(entry_x_m=-0.05, entry_v_mps=5.0, stage=stage, projectile=projectile)
|
||||
|
||||
|
||||
def test_stage_is_feasible_with_realistic_components():
|
||||
@@ -66,8 +66,16 @@ def test_energy_conserved_exactly_when_lossless():
|
||||
part_number="c-lossless", capacitance_uf=100.0, voltage_v=400.0, esr_ohm=0.0,
|
||||
max_current_a=300.0, price=300.0, source="test",
|
||||
)
|
||||
# снаряд без вихревых потерь: огромное удельное сопротивление -> R_eddy≈0,
|
||||
# чтобы «без потерь» действительно означало отсутствие всех каналов диссипации
|
||||
no_eddy_steel = ProjectileMaterialSpec(
|
||||
name="steel-no-eddy", density_kg_m3=7850.0, mu_r=200.0, b_sat_tesla=1.8,
|
||||
price_per_kg=100.0, source="test", resistivity_ohm_m=1e12,
|
||||
)
|
||||
no_eddy_projectile = ProjectileConfig(material=no_eddy_steel, diameter_m=0.008, length_m=0.02)
|
||||
result = _run(
|
||||
_wire(resistivity_ohm_m=0.0), _switch(on_resistance_ohm=0.0), capacitor=lossless_capacitor
|
||||
_wire(resistivity_ohm_m=0.0), _switch(on_resistance_ohm=0.0),
|
||||
capacitor=lossless_capacitor, projectile=no_eddy_projectile,
|
||||
)
|
||||
assert result.feasible, result.reason
|
||||
assert result.energy_dissipated_j == pytest.approx(0.0, abs=1e-9)
|
||||
|
||||
Reference in New Issue
Block a user