diff --git a/PLAN.md b/PLAN.md index 41c1b56..66fb446 100644 --- a/PLAN.md +++ b/PLAN.md @@ -37,10 +37,10 @@ L(x, I)-модель с coenergy-выводом силы — в разделе " ## Чек-лист этапов - [x] **Этап 0 — Скелет проекта**: `pyproject.toml`, `README.md`, `.gitignore`, пакеты `src/gausse/*`, этот файл, первый коммит. -- [ ] **Этап 1 — База реальных компонентов**: провод Cu/Al (сечения, сопротивление, цена/м), конденсаторы (ёмкость/напряжение/ESR/цена, фотовспышечные электролитические как бюджетный вариант), тиристоры (КУ202 и аналоги) и MOSFET/IGBT, датчики (индукционная катушка vs оптопара/Холл), материалы снаряда (сталь/железо: μr, B_sat, плотность, цена/кг) → `components/data/*.json`. -- [ ] **Этап 2 — Физическое ядро**: `physics/constants.py`, `inductance.py` (Уилер + ферромагнитный сердечник + размагничивание), `force.py` (с насыщением), `circuit.py` (ОДУ RLC). Юнит-тесты: аналитическое RLC-решение, согласованность dL/dx. -- [ ] **Этап 3 — Датчики и одна ступень**: `physics/sensors.py` (оба типа как события solve_ivp), `sim/stage.py` (полёт → триггер → разряд → энергобаланс). Тест на сохранение энергии. -- [ ] **Этап 4 — Многоступенчатая цепочка**: `sim/coilgun.py` — сквозная координата, отбраковка нереализуемых конфигураций. +- [x] **Этап 1 — База реальных компонентов**: провод Cu/Al, конденсаторы, тиристоры/MOSFET/IGBT, датчики (Hall/оптика реальные, индукционный — оценка), материалы снаряда → `components/data/*.json`. Каждая запись честно помечена REAL (с URL источника) или ОЦЕНКА (с указанием основания); алюминиевый провод и ёмкости 1000/2200мкФ — низкая уверенность в цене, явно отмечено. +- [x] **Этап 2 — Физическое ядро**: `physics/constants.py`, `inductance.py` (Уилер + ферромагнитный сердечник + размагничивание), `force.py`, `circuit.py` (ОДУ RLC). Юнит-тесты: аналитическое RLC-решение, согласованность dL/dx. +- [x] **Этап 3 — Датчики и одна ступень**: `physics/sensors.py` (оба типа как события solve_ivp), `sim/stage.py` (полёт → триггер → разряд → энергобаланс). Тест на сохранение энергии + найден/исправлен баг насыщения (см. выше). +- [x] **Этап 4 — Многоступенчатая цепочка**: `sim/coilgun.py` — сквозная координата, отбраковка нереализуемых конфигураций. Дымовой тест на реальной базе компонентов (`test_real_components_smoke.py`) подтверждает: полный путь реальные JSON → физика → цепочка работает (пример: 22.3 м/с, КПД 3.4% — честный неоптимизированный результат). - [ ] **Этап 5 — Хранилище результатов**: `storage/schema.py` + `database.py` — SQLite (WAL), таблица `runs` со всеми прогонами (успех/провал + честная причина), однопроцессный писатель поверх многопроцессной очереди. - [ ] **Этап 6 — Поиск и оптимизация**: `optim/search_space.py` (геном переменной длины), `objective.py` (КПД), дешёвый квазистатический предфильтр, `optim/sweep.py` (Monte Carlo/LHS, миллионы прогонов), `optim/evolutionary.py` (ГА + coordinate-descent полировка) — всё пишется в общую таблицу `runs`. - [ ] **Этап 7 — Отчётность**: `report/plots.py`, `summary.py`, `bom.py`, раздел "ограничения модели", `cli.py` (`gausse sweep/evolve/simulate/report`). diff --git a/src/gausse/components/data/capacitors.json b/src/gausse/components/data/capacitors.json new file mode 100644 index 0000000..3a43aee --- /dev/null +++ b/src/gausse/components/data/capacitors.json @@ -0,0 +1,56 @@ +[ + { + "part_number": "cap-22uf-400v", + "capacitance_uf": 22.0, + "voltage_v": 400.0, + "esr_ohm": 0.45, + "max_current_a": 5.0, + "price": 140.0, + "source": "REAL цена: chipdip.ru, категория 'kondensator-400v' (https://www.chipdip.ru/catalog/popular/kondensator-400v); ESR не указан продавцом, ОЦЕНКА 0.3-0.6 Ом (типично для класса, взята середина)" + }, + { + "part_number": "cap-68uf-400v", + "capacitance_uf": 68.0, + "voltage_v": 400.0, + "esr_ohm": 0.3, + "max_current_a": 8.0, + "price": 440.0, + "source": "REAL цена: chipdip.ru, категория 'kondensator-400v' (https://www.chipdip.ru/catalog/popular/kondensator-400v); ESR не указан продавцом, ОЦЕНКА (типично для класса)" + }, + { + "part_number": "cap-180uf-400v", + "capacitance_uf": 180.0, + "voltage_v": 400.0, + "esr_ohm": 0.15, + "max_current_a": 15.0, + "price": 109.9, + "source": "REAL цена: procontact74.ru, CHANG 180uF 400V 105C (https://procontact74.ru/01-elektronnyie-komponentyi-/41-kondensatory/411-elektroliticheskie-kondensatory/180uf-400v-105c-2530-411213/); на странице встречались две цены (109.9₽ и 870₽ у разных продавцов) - взята бюджетная; ESR ОЦЕНКА" + }, + { + "part_number": "cap-470uf-400v", + "capacitance_uf": 470.0, + "voltage_v": 400.0, + "esr_ohm": 0.08, + "max_current_a": 25.0, + "price": 498.0, + "source": "REAL цена: procontact74.ru, JCCON 470uF 400V 105C 30x45мм (https://procontact74.ru/01-elektronnyie-komponentyi-/41-kondensatory/411-elektroliticheskie-kondensatory/kondensator-elektroliticheskiy----470uf-400v-105-c-35-45-cd294-elzet/), полная страница проверена, 325.84₽ от 5шт, 279.29₽ от 25шт; ESR не указан продавцом, ОЦЕНКА (для банок этого размера обычно ниже, ~0.05-0.1 Ом)" + }, + { + "part_number": "cap-1000uf-400v", + "capacitance_uf": 1000.0, + "voltage_v": 400.0, + "esr_ohm": 0.06, + "max_current_a": 40.0, + "price": 700.0, + "source": "ОЦЕНКА (низкая уверенность): товарная категория подтверждена реально на procontact74.ru/asenergi.ru, но точная цена конкретной позиции не подтверждена при парсинге; цена экстраполирована из наблюдаемого тренда ₽/мкФ для банок этого класса" + }, + { + "part_number": "cap-2200uf-400v", + "capacitance_uf": 2200.0, + "voltage_v": 400.0, + "esr_ohm": 0.05, + "max_current_a": 60.0, + "price": 1400.0, + "source": "ОЦЕНКА (низкая уверенность): категория подтверждена на asenergi.ru (https://asenergi.ru/catalog/kondensatory-elektroliticheskie/2200mkf.html), точная цена не подтверждена; цена экстраполирована из тренда ₽/мкФ, брать с осторожностью" + } +] diff --git a/src/gausse/components/data/projectile_materials.json b/src/gausse/components/data/projectile_materials.json new file mode 100644 index 0000000..b34542d --- /dev/null +++ b/src/gausse/components/data/projectile_materials.json @@ -0,0 +1,26 @@ +[ + { + "name": "Ст3 (конструкционная сталь)", + "density_kg_m3": 7850.0, + "mu_r": 550.0, + "b_sat_tesla": 1.9, + "price_per_kg": 55.0, + "source": "Цена REAL: горячекатаный круг Ст3, ros-met.com/metallurg-moskva.ru, ~49-60₽/кг (одна из позиций ~51.4₽/кг за тонну); плотность/μr/B_sat — ОЦЕНКА (стандартные справочные значения для конструкционной стали, не найдено источника с числами конкретно для Ст3)" + }, + { + "name": "Сталь 10 (низкоуглеродистая)", + "density_kg_m3": 7850.0, + "mu_r": 1500.0, + "b_sat_tesla": 2.05, + "price_per_kg": 55.0, + "source": "ОЦЕНКА по всем полям: отдельного розничного объявления на 'Сталь 10' не найдено, цена взята той же полосы, что и Ст3 (~50-70₽/кг); μr выше, чем у Ст3, из-за более высокой чистоты низкоуглеродистой стали — справочная оценка, не измерение" + }, + { + "name": "Армко-железо (чистое железо)", + "density_kg_m3": 7870.0, + "mu_r": 3500.0, + "b_sat_tesla": 2.15, + "price_per_kg": 160.0, + "source": "ОЦЕНКА по всем полям: специализированный товар, не найдено самостоятельной розничной цены; цена оценена как ~3x от Ст3 на основе общего указания '2-4x дороже обычной стали'; μr/B_sat — стандартные справочные значения для хорошо отожжённого чистого железа" + } +] diff --git a/src/gausse/components/data/sensors.json b/src/gausse/components/data/sensors.json new file mode 100644 index 0000000..999dcfd --- /dev/null +++ b/src/gausse/components/data/sensors.json @@ -0,0 +1,25 @@ +[ + { + "part_number": "A3144E", + "kind": "hall", + "propagation_delay_ns": 5000.0, + "price": 19.9, + "source": "REAL: procontact74.ru (https://procontact74.ru/01-elektronnyie-komponentyi-/34-datchiki/datchik-holla-a3144e-34111/), 19.90₽ (6.48₽ от 200шт); время срабатывания на странице не указано, ОЦЕНКА ~5мкс (середина типового диапазона 3-7мкс для А3144-класса)" + }, + { + "part_number": "TCST2103", + "kind": "optical", + "propagation_delay_ns": 9000.0, + "price": 220.0, + "source": "REAL: chipdip.ru (https://www.chipdip.ru/product/tcst2103-optopara-shirokogo-naznacheniya-4-pin-vishay-2693015011), 220₽ (161₽ от 200шт); время включения/выключения 8-10мкс — REAL с даташита Vishay, взята середина диапазона" + }, + { + "part_number": "inductive-pickup-lm393", + "kind": "inductive", + "propagation_delay_ns": 1300.0, + "price": 15.0, + "source": "ОЦЕНКА: не готовый компонент, а самодельная схема — виток(-и) провода (цена уже учтена в wires.json) + компаратор LM393 (~15₽, типовая розничная цена); время задержки 1.3мкс — реальный паспортный параметр LM393, но чувствительность/порог ниже — грубая оценка, не датащит-значение", + "sensitivity_v_per_mps": 0.01, + "threshold_v": 0.05 + } +] diff --git a/src/gausse/components/data/switches.json b/src/gausse/components/data/switches.json new file mode 100644 index 0000000..2879ce3 --- /dev/null +++ b/src/gausse/components/data/switches.json @@ -0,0 +1,68 @@ +[ + { + "part_number": "KU202N", + "kind": "SCR", + "max_current_a": 10.0, + "max_voltage_v": 400.0, + "on_resistance_ohm": null, + "on_voltage_drop_v": 1.5, + "turn_on_time_ns": 10000.0, + "price": 140.0, + "source": "REAL: КУ202Н, chipdip.ru (https://www.chipdip.ru/product/ku202n) 140₽ / procontact74.ru (https://procontact74.ru/44-tiristory/ku202n-tiristor-10a-400v-44011/) 189₽; Von=1.5В с даташита; время включения ОЦЕНКА ~10мкс (типично для тиристора этого класса, точное значение в листинге не приведено)" + }, + { + "part_number": "BT151-650R", + "kind": "SCR", + "max_current_a": 12.0, + "max_voltage_v": 650.0, + "on_resistance_ohm": null, + "on_voltage_drop_v": 1.7, + "turn_on_time_ns": 1500.0, + "price": 190.0, + "source": "REAL: chipdip.ru (https://www.chipdip.ru/product/bt151-650r), ~180-200₽ (BT151-600R вариант подтверждён на 180₽); Von и время включения ОЦЕНКА (типично для тиристора этого класса, не указаны продавцом)" + }, + { + "part_number": "IRFP254PBF", + "kind": "MOSFET", + "max_current_a": 23.0, + "max_voltage_v": 250.0, + "on_resistance_ohm": 0.07, + "on_voltage_drop_v": null, + "turn_on_time_ns": 30.0, + "price": 39.9, + "source": "REAL: procontact74.ru (https://procontact74.ru/01-elektronnyie-komponentyi-/43-tranzistory/polevyie--mosfet-/58406-tranzistorirfp254pbfmosfetnch250v23ato247acoriginalbydemontajnogi10mm/); ВНИМАНИЕ: демонтированная (Б/У) деталь, не новая партия; время включения ОЦЕНКА ~30нс (типично для этого класса MOSFET)" + }, + { + "part_number": "IRFP250PBF", + "kind": "MOSFET", + "max_current_a": 30.0, + "max_voltage_v": 200.0, + "on_resistance_ohm": 0.085, + "on_voltage_drop_v": null, + "turn_on_time_ns": 35.0, + "price": 320.0, + "source": "REAL: chipdip.ru (https://www.chipdip.ru/product/irfp250), новая деталь, 320₽ (276₽ от 15шт, 245₽ от 150шт); Rds(on)=0.085Ом при 18А/10В с даташита; время включения ОЦЕНКА" + }, + { + "part_number": "IRFP4468PBF", + "kind": "MOSFET", + "max_current_a": 180.0, + "max_voltage_v": 100.0, + "on_resistance_ohm": 0.0026, + "on_voltage_drop_v": null, + "turn_on_time_ns": 40.0, + "price": 610.0, + "source": "REAL: chipdip.ru (https://www.chipdip.ru/product/irfp4468pbf), 610₽ (534₽ от 15шт); паспортный максимум 290А при 25°C, взято консервативное практическое значение 180А; Rds(on)=0.0026Ом при 180А/10В с даташита; время включения ОЦЕНКА" + }, + { + "part_number": "IRG4PC50F", + "kind": "IGBT", + "max_current_a": 70.0, + "max_voltage_v": 600.0, + "on_resistance_ohm": null, + "on_voltage_drop_v": 1.8, + "turn_on_time_ns": 300.0, + "price": 504.0, + "source": "REAL: chipdip.ru (https://www.chipdip.ru/product/irg4pc50f-8048990896), ~504₽; Vce(sat) и время включения ОЦЕНКА (типично для этого семейства IGBT, не подтверждено со страницы товара)" + } +] diff --git a/src/gausse/components/data/wires.json b/src/gausse/components/data/wires.json new file mode 100644 index 0000000..a2a739c --- /dev/null +++ b/src/gausse/components/data/wires.json @@ -0,0 +1,102 @@ +[ + { + "part_id": "cu-petv2-0.3mm", + "material": "copper", + "gauge_mm": 0.3, + "insulation_od_mm": 0.37, + "resistivity_ohm_m": 1.68e-08, + "max_current_a": 0.35, + "price_per_m": 3.5, + "source": "REAL: chipdip.ru, ПЭТВ-2 0.3мм, 1050₽/200г-катушка ~300м (https://www.chipdip.ru/product/petv-2-d-0.3-mm-provod-emalirovannyy-obmotochnyy-300m-dlina-9000272019)" + }, + { + "part_id": "cu-petv2-0.71mm", + "material": "copper", + "gauge_mm": 0.71, + "insulation_od_mm": 0.78, + "resistivity_ohm_m": 1.68e-08, + "max_current_a": 1.98, + "price_per_m": 16.2, + "source": "REAL: chipdip.ru, ПЭТВ-2 0.71мм, 890₽/200г-катушка ~55м (https://www.chipdip.ru/product/petv-2-d-0.71-mm)" + }, + { + "part_id": "cu-petv2-1.0mm", + "material": "copper", + "gauge_mm": 1.0, + "insulation_od_mm": 1.08, + "resistivity_ohm_m": 1.68e-08, + "max_current_a": 3.93, + "price_per_m": 8.445, + "source": "REAL: chipdip.ru, ПЭТВ-2 1.0мм, 1200₽/кг (https://www.chipdip.ru/product0/9000289279); ₽/м пересчитано нами из ₽/кг" + }, + { + "part_id": "cu-petv2-1.4mm", + "material": "copper", + "gauge_mm": 1.4, + "insulation_od_mm": 1.48, + "resistivity_ohm_m": 1.68e-08, + "max_current_a": 7.7, + "price_per_m": 45.266, + "source": "REAL: chipdip.ru, ПЭТВ-2 1.4мм, 3610₽/1.1кг-упаковка (https://www.chipdip.ru/product0/8004178560); ₽/м пересчитано нами из ₽/кг" + }, + { + "part_id": "cu-petv2-0.8mm", + "material": "copper", + "gauge_mm": 0.8, + "insulation_od_mm": 0.87, + "resistivity_ohm_m": 1.68e-08, + "max_current_a": 2.51, + "price_per_m": 11.259, + "source": "ОЦЕНКА: товар реален на chipdip.ru (https://www.chipdip.ru/product0/8004178423), но точная цена не подтверждена при парсинге; 2500₽/кг интерполировано между подтверждёнными 0.71мм (~4566₽/кг) и 1.0мм (1200₽/кг)" + }, + { + "part_id": "cu-petv2-2.0mm", + "material": "copper", + "gauge_mm": 2.0, + "insulation_od_mm": 2.08, + "resistivity_ohm_m": 1.68e-08, + "max_current_a": 15.71, + "price_per_m": 30.964, + "source": "ОЦЕНКА: chipdip.ru (https://www.chipdip.ru/product0/9000289280), цена частично 'по запросу', взята середина наблюдаемого диапазона 1000-1200₽/кг" + }, + { + "part_id": "al-0.3mm", + "material": "aluminum", + "gauge_mm": 0.3, + "insulation_od_mm": 0.37, + "resistivity_ohm_m": 2.82e-08, + "max_current_a": 0.28, + "price_per_m": 1.4, + "source": "ОЦЕНКА: алюминиевый эмальпровод в рознице РФ практически не продаётся поштучно (cable.ru и др. требуют запрос цены, см. https://cable.ru/cable/kabel-alyuminij_obmotochnyj_v_razdel-9.php); цена оценена как 0.4x от цены медного провода того же сечения (типовое соотношение стоимости сырья Cu/Al)" + }, + { + "part_id": "al-0.71mm", + "material": "aluminum", + "gauge_mm": 0.71, + "insulation_od_mm": 0.78, + "resistivity_ohm_m": 2.82e-08, + "max_current_a": 1.58, + "price_per_m": 6.48, + "source": "ОЦЕНКА: алюминиевый эмальпровод в рознице РФ практически не продаётся поштучно (cable.ru и др. требуют запрос цены, см. https://cable.ru/cable/kabel-alyuminij_obmotochnyj_v_razdel-9.php); цена оценена как 0.4x от цены медного провода того же сечения (типовое соотношение стоимости сырья Cu/Al)" + }, + { + "part_id": "al-1.0mm", + "material": "aluminum", + "gauge_mm": 1.0, + "insulation_od_mm": 1.08, + "resistivity_ohm_m": 2.82e-08, + "max_current_a": 3.14, + "price_per_m": 3.378, + "source": "ОЦЕНКА: алюминиевый эмальпровод в рознице РФ практически не продаётся поштучно (cable.ru и др. требуют запрос цены, см. https://cable.ru/cable/kabel-alyuminij_obmotochnyj_v_razdel-9.php); цена оценена как 0.4x от цены медного провода того же сечения (типовое соотношение стоимости сырья Cu/Al)" + }, + { + "part_id": "al-1.4mm", + "material": "aluminum", + "gauge_mm": 1.4, + "insulation_od_mm": 1.48, + "resistivity_ohm_m": 2.82e-08, + "max_current_a": 6.16, + "price_per_m": 18.106, + "source": "ОЦЕНКА: алюминиевый эмальпровод в рознице РФ практически не продаётся поштучно (cable.ru и др. требуют запрос цены, см. https://cable.ru/cable/kabel-alyuminij_obmotochnyj_v_razdel-9.php); цена оценена как 0.4x от цены медного провода того же сечения (типовое соотношение стоимости сырья Cu/Al)" + } +] \ No newline at end of file diff --git a/tests/test_real_components_smoke.py b/tests/test_real_components_smoke.py new file mode 100644 index 0000000..133f08c --- /dev/null +++ b/tests/test_real_components_smoke.py @@ -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