Populate real retail component database (Stage 1 complete)

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>
This commit is contained in:
jze9
2026-07-06 20:06:27 +05:00
parent f8f6eaa765
commit f22fd89612
7 changed files with 351 additions and 4 deletions

View File

@@ -0,0 +1,70 @@
"""Дымовой тест: реальная база компонентов (Этап 1) действительно прогоняется.
Не проверяет конкретные числа (это будет делать sweep/оптимизатор) — только
то, что хотя бы одна разумная комбинация реальных деталей из
components/data/*.json физически прогоняется через полную цепочку без
исключений, и что честная отбраковка (feasible=False) тоже доступна и не
падает, если комбинация неудачная.
"""
from gausse.components.database import ComponentDatabase
from gausse.sim.coilgun import CoilgunConfig, run_coilgun
from gausse.sim.stage import ProjectileConfig, StageConfig
def _find(items, **kwargs):
for item in items:
if all(getattr(item, k) == v for k, v in kwargs.items()):
return item
raise AssertionError(f"не найден компонент с {kwargs} среди {len(items)} записей")
def test_real_component_database_loads_and_is_nonempty():
db = ComponentDatabase.load()
assert len(db.wires) > 0
assert len(db.capacitors) > 0
assert len(db.switches) > 0
assert len(db.sensors) > 0
assert len(db.projectile_materials) > 0
def test_real_component_combination_runs_end_to_end():
db = ComponentDatabase.load()
wire = _find(db.wires, part_id="cu-petv2-1.0mm")
capacitor = _find(db.capacitors, part_number="cap-470uf-400v")
switch = _find(db.switches, part_number="IRFP250PBF")
sensor = _find(db.sensors, part_number="TCST2103")
steel = _find(db.projectile_materials, name="Ст3 (конструкционная сталь)")
projectile = ProjectileConfig(material=steel, diameter_m=0.008, length_m=0.02)
stage = StageConfig(
wire=wire,
capacitor=capacitor,
switch=switch,
sensor=sensor,
tube_od_m=0.01,
turns_per_layer=20,
layers=4,
sensor_to_coil_distance_m=0.02,
charge_voltage_v=350.0,
)
config = CoilgunConfig(
stages=[stage, stage],
inter_stage_gaps_m=[0.05],
projectile=projectile,
initial_x_m=-0.05,
initial_v_mps=5.0,
)
result = run_coilgun(config)
# Реальные детали могут дать как удачную, так и провальную (feasible=False)
# конфигурацию — это тоже честный результат. Тест лишь гарантирует, что
# весь путь (реальные JSON -> физика -> цепочка) не падает с исключением
# и возвращает осмысленный, самосогласованный результат в любом случае.
if result.feasible:
assert result.exit_v_mps is not None
assert 0.0 <= result.efficiency <= 1.0
else:
assert result.reason is not None
assert result.failed_stage_index is not None