- optim/worker_context.py: shared per-process DB/bounds init, extracted
from sweep.py so evolutionary.py doesn't reach into another module's
private state
- optim/objective.py: build_run_record() shared between sweep and
evolutionary so both write identically-shaped rows
- optim/evolutionary.py: tournament selection, whole-stage-swap crossover,
elitism, structural mutation (stage count itself evolves), then a serial
Nelder-Mead polish of the best genome's continuous parameters with
discrete component choices frozen
- Found and fixed a real crash: crossover() indexed into an empty
inter_stage_gaps_m list when both parents had only 1 stage (0 gaps),
raising IndexError. Fixed the fallback to only choose from gaps that
actually exist, defaulting to a neutral value (clipped later by
repair()) when neither parent has one. Added a regression test.
- Verified 59/59 tests pass both locally and inside the Docker image
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
optim/sweep.py: ProcessPoolExecutor workers sample+evaluate genomes in
parallel; each result (feasible or not) is pushed through the
queue-backed single writer built in Stage 5. Verified with real
multiprocessing (not mocks): 12-run sweep across 2 worker processes
lands all 12 rows in SQLite with parseable genome/summary JSON, and two
independent sweeps with the same seed produce byte-identical results.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- optim/search_space.py: Genome encodes only indices into the real
component database plus continuous/discrete geometry parameters; number
of stages is itself mutable via add/remove/duplicate-stage operators.
repair() clips everything back into bounds after mutation/crossover so
decode() never sees an invalid genome.
- optim/objective.py: evaluate() decodes a genome, computes real BOM cost
from wire length/price and component prices, runs the full chain, and
scores fitness = efficiency for feasible runs or a soft penalty scaled
by how many stages it got through for infeasible ones (so the GA gets
gradient instead of a wall).
Tested against the real component database (not synthetic fixtures) —
including an explicit infeasible case using the real inductive sensor's
actual sensitivity/threshold values.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- storage/schema.py: `runs` table recording every evaluated configuration,
feasible or not, with an honest infeasible_reason instead of dropping
failures
- storage/database.py: WAL-mode SQLite, single-writer-over-a-queue pattern
for safe concurrent writes from many sweep worker processes
- Testing with real multiprocessing.Process (not mocks) caught a genuine
bug: a duplicate run_id raised inside the writer loop and killed the
writer process, silently halting queue drainage for the rest of a
sweep. Fixed by catching the insert error per-record and logging to
stderr instead of crashing; added a regression test for it.
- Verified locally and inside the Docker image (43/43 tests both ways)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Wire (Cu/Al ПЭТВ-2), capacitors (350-450V electrolytic bank), switches
(КУ202Н/BT151 SCR, IRFP250/IRFP4468 MOSFET, IRG4PC50F IGBT), sensors
(A3144E Hall, TCST2103 optical, LM393-based inductive pickup), and
projectile materials (Ст3, Сталь 10, армко-железо) sourced from
procontact74.ru/chipdip.ru/cable.ru research.
Every entry's `source` field states plainly whether the number is a
REAL scraped retail price/spec (with URL) or an ОЦЕНКА (estimate) with
its basis (e.g. aluminum magnet wire has no self-service retail price
in RU stores, so it's priced as a fraction of equivalent copper) — no
value is presented as real when it isn't. Added a smoke test proving
the real database runs end-to-end through the full physics + chaining
pipeline, not just against synthetic test fixtures.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- sim/coilgun.py: chains StageConfig results through a global coordinate,
stops honestly on the first infeasible stage (with which stage and why),
and computes overall efficiency = exit KE / total capacitor energy in
- Discovered while wiring up the chain: a high-capacitance single-stage test
configuration let current stay high past the coil center, decelerating and
reversing the slug (exit velocity negative). This is a real coilgun tuning
failure mode (not a bug) since KE ~ v^2 doesn't care about direction and
the energy balance still holds to ~1%. Kept it as an explicit regression
test (test_overpowered_discharge_can_fling_slug_backward) instead of
quietly picking parameters that hide it, and tightened the "kinetic energy
increases" test to also require forward (exit_v > 0) motion.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- physics/sensors.py: optical/Hall (velocity-independent) and inductive
(velocity-scaled, sech^2 spatial sensitivity) trigger events for solve_ivp
- sim/stage.py: flight-to-trigger -> fire delay -> discharge -> energy
accounting, returning StageResult(feasible=False, reason=...) instead of
raising when a sensor never fires or discharge never commutates
- Found and fixed a real bug caught by the energy-conservation test: the
saturation clamp was applied to the mechanical force but not the
electrical back-EMF term, silently breaking energy balance by ~15%.
Removed the dynamic clamp (documented as a deferred nonlinear-L(x,I)
limitation) and kept saturation as a diagnostic-only warning
(StageResult.saturation_warning) so numbers stay honest rather than
quietly wrong. Balance error is now ~0.02%.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- components/schema.py + database.py: typed loader for real retail parts
(wire, capacitors, switches, sensors, projectile materials)
- physics/inductance.py: multilayer solenoid inductance (Wheeler) with
ferromagnetic-slug coupling via smooth overlap model and analytic dL/dx
- physics/force.py: F = 0.5*I^2*dL/dx with a saturation clamp
- physics/circuit.py: coupled [Q,I,x,v] discharge ODE
- Validated against textbook RLC analytical solutions (under/over/critically
damped) and energy conservation, not just spot-checked by eye
- PLAN.md updated with animation (per-run field visualization) and
GPU-accelerated batch sweep as explicit later stages
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>