Requested during deployment: make the DB maximally detailed, add an experiment process log, and a web UI to watch runs live. - optim/objective.py build_detail(): each stored run now records inter-coil distances, absolute coil positions along the tube, every component part number/spec, winding geometry, computed physics (resistance, air inductance, peak current, peak field), and per-stage outcome (entry/exit velocity, sensor timing, energy breakdown) - optim/progress_log.py: append-only <db>.log with progress %, feasible rate, throughput, ETA, and a line on each new best efficiency; wired into both sweep and evolve - src/gausse/web/: stdlib-only (http.server) dashboard `gausse serve` -- self-contained HTML polling /api/overview every 3s: counters, KPD histogram, top-configs table with per-run drill-down, failure reasons, live log tail. No new dependencies. - docker-compose.yml: `web` service on port 8000 - 77 tests pass locally and in Docker Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
71 lines
16 KiB
Markdown
71 lines
16 KiB
Markdown
# План: Gauss-ускоритель — симулятор и оптимизатор
|
||
|
||
Многоступенчатый электромагнитный ускоритель ферромагнитного цилиндра (coilgun).
|
||
Каждая ступень: разгонная катушка (Cu/Al провод на пластиковой трубке) + батарея
|
||
конденсаторов через тиристор/MOSFET, и датчик прохода снаряда перед катушкой
|
||
(компенсация задержки включения ключа). Датчик — два конкурирующих варианта:
|
||
индукционная катушка (сигнал ~ скорости снаряда, слаб на медленных ступенях) и
|
||
оптопара/датчик Холла (не зависит от скорости) — оптимизатор сравнивает оба.
|
||
|
||
Цель поиска — КПД (кинетическая энергия снаряда на выходе / энергия во всех
|
||
конденсаторах). Скорость и стоимость — вторичные метрики. Число ступеней N,
|
||
геометрия/материал снаряда, все компоненты — часть пространства поиска, а не
|
||
фиксированные входы. Компоненты — реальные, розничные (Проконтакт/procontact74.ru,
|
||
ChipDip, Cable.ru и др.).
|
||
|
||
Требование: миллионы прогонов (Monte Carlo/LHS sweep + эволюционный поиск),
|
||
**все** результаты — успешные и неудачные, с честной причиной отказа — пишутся
|
||
в SQLite. Никаких приукрашенных цифр — если модель показывает низкий КПД или
|
||
нереализуемость, это тоже результат.
|
||
|
||
**Найденная и исправленная ошибка (Этап 3):** насыщение сердечника изначально
|
||
клэмпилось только в механическом уравнении (F=0.5·I²·dL/dx), но не в
|
||
электрическом (наведённая ЭДС всё ещё считалась по полной dL/dx) — это
|
||
незаметно ломало точный энергобаланс на ~15%. Тест на сохранение энергии
|
||
(этого же честного протокола, который просил пользователь) это поймал.
|
||
Решение: клэмп насыщения убран из динамики (F=0.5·I²·dL/dx без клэмпа,
|
||
энергобаланс теперь точен до ~0.02%), а `solenoid_field_estimate_tesla`/
|
||
`saturation_scale` оставлены как ДИАГНОСТИКА — `StageResult.saturation_warning`
|
||
честно предупреждает, когда конфигурация физически выходит за пределы
|
||
насыщения материала снаряда, не подменяя динамику. Полная нелинейная
|
||
L(x, I)-модель с coenergy-выводом силы — в разделе "ограничения модели"
|
||
как будущая работа, не как текущая гарантия точности.
|
||
|
||
Полный план архитектуры: см. историю обсуждения / `physics`, `sim`, `optim`,
|
||
`storage`, `report` модули ниже.
|
||
|
||
**Docker — основной способ запуска** (по требованию пользователя): `Dockerfile`
|
||
+ `docker-compose.yml` в корне репозитория, образ собран и провалидирован —
|
||
внутри контейнера запускаются все 37 тестов и CLI (`docker compose run --rm
|
||
--entrypoint pytest gausse -q`). Результаты (SQLite и отчёты, когда появятся)
|
||
монтируются в `./results` на хосте, чтобы переживать пересборку образа.
|
||
|
||
## Чек-лист этапов
|
||
|
||
- [x] **Этап 0 — Скелет проекта**: `pyproject.toml`, `README.md`, `.gitignore`, пакеты `src/gausse/*`, этот файл, первый коммит.
|
||
- [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% — честный неоптимизированный результат).
|
||
- [x] **Этап 5 — Хранилище результатов**: `storage/schema.py` + `database.py` — SQLite (WAL), таблица `runs` со всеми прогонами (успех/провал + честная причина), однопроцессный писатель поверх многопроцессной очереди. Тест с реальными `multiprocessing.Process` (не моками) поймал реальную проблему: коллизия `run_id` роняла writer и молча останавливала осушение очереди на весь sweep — писатель теперь переживает ошибку вставки одной записи (лог в stderr) и продолжает работу.
|
||
- [x] **Этап 6 — Поиск и оптимизация**: `optim/search_space.py` (геном переменной длины — число ступеней тоже эволюционирует), `objective.py` (fitness=КПД, честный мягкий штраф за нереализуемость пропорционально пройденным ступеням), `optim/sweep.py` (параллельный Monte Carlo, каждый прогон в SQLite), `optim/evolutionary.py` ((μ+λ)-ГА + Nelder-Mead полировка непрерывных параметров лучшего генома). Тест поймал реальный баг в `crossover()` — IndexError при скрещивании двух одноступенчатых геномов (пустой список зазоров), исправлено и покрыто регрессией.
|
||
- [ ] **Не сделано**: отдельный "дешёвый квазистатический предфильтр" перед полным ODE не реализован (кроме уже встроенной в `sim/stage.py` дешёвой проверки порога индукционного датчика). Если миллионы прогонов на сервере окажутся слишком медленными, это первое место для ускорения.
|
||
- [x] **Этап 7 — Отчётность**: `report/plots.py` (ток/поле/скорость по ступеням), `summary.py` (честная сводка + раздел "ограничения модели"), `bom.py` (спецификация деталей с ценами), `animate.py` (GIF: снаряд летит по трубе, катушки светятся пропорционально току — по запросу пользователя), `cli.py` (`gausse sweep/evolve/simulate/report`, все 4 команды реально вызывают соответствующие модули, не заглушки). Проверено сквозным прогоном через `docker compose run`: sweep → report с анимацией на реальной базе компонентов, лучший найденный результат — КПД 32.8% за 982₽ (не выдумано, из реального SQLite).
|
||
- [x] **Этап 8 — Сквозная проверка**: реальный прогон через `docker compose run` — sweep на 5000 конфигураций (16 воркеров, ~59с, 30.5% реализуемо), затем `evolve` (15 поколений x 40 особей = 600 оценок, ~18с) поверх той же базы, затем `report --top 3 --animate`.
|
||
- **Найден и исправлен реальный баг именно на этом этапе**: КПД лучшего генома после `evolve` показал 387% — оказалось, `efficiency` считался как `exit_kinetic_energy / energy_in`, а `exit_kinetic_energy` включает фиксированный "бесплатный" толчок `initial_v_mps`, не учтённый в `energy_in`. Для лёгкого снаряда этот толчок доминировал и КПД пробивал 100%. Исправлено на `kinetic_energy_delta / energy_in` (энергия, реально добавленная катушками) — эта величина математически не может превысить 1 (следует из поэтапного энергобаланса). После исправления лучший честный результат: **1 ступень, КПД 80.97%, скорость 26.4 м/с, снаряд Ст3 ⌀4мм x 30мм, стоимость 1029₽** (провод cu-petv2-1.0mm, конденсатор 22мкФ/400В, ключ IRG4PC50F, оптический датчик).
|
||
- **Честное сравнение датчиков** (запрос из первого обсуждения плана) — на 5000 прогонах: индукционный датчик (`inductive-pickup-lm393`) дал **0.2% реализуемых** конфигураций (6 из 2965, где он стоял хотя бы на одной ступени), тогда как оптический (TCST2103) и Холла (A3144E) — **~45-47%** реализуемых каждый. Это количественно подтверждает опасение, высказанное в самом начале обсуждения плана: индукционная катушка-датчик ненадёжна именно потому, что требует скорости выше её порога (здесь — фиксированный старт 3 м/с как раз ниже порога срабатывания реального компонента), тогда как оптика/Холл не зависят от скорости.
|
||
- Итог: сквозная цепочка (реальные компоненты → физика → поиск → SQLite → отчёт с графиками/BOM/анимацией) работает и произвела не выдуманный, а посчитанный и перепроверенный результат.
|
||
- [x] **Обогащённая БД + лог процесса + веб-морда** (по запросу пользователя при деплое): каждая запись `runs.decoded_summary_json` теперь содержит расстояния между катушками (`inter_stage_gaps_m`), абсолютные позиции катушек вдоль трубы, все номиналы деталей, геометрию намотки, вычисленную физику (индуктивность/сопротивление/пиковый ток/поле) и результат каждой ступени (вход/выход скорость, тайминги датчика, энергобаланс) — см. `optim/objective.py::build_detail`. Ход эксперимента пишется в `<db>.log` (`optim/progress_log.py`): прогресс, доля реализуемых, скорость, ETA, отметки нового лучшего КПД. Веб-дашборд `gausse serve` (`src/gausse/web/`, только stdlib `http.server`, self-contained HTML) — счётчики, гистограмма КПД, топ конфигураций с drill-down, причины отказа, хвост лога, автообновление 3с. В `docker-compose.yml` сервис `web` на порту 8000. 77 тестов, проверено в Docker.
|
||
|
||
## Развёртывание на сервере (Proxmox 192.168.20.254 / VM 106 "test-math" = 192.168.20.45)
|
||
|
||
Обнаружено при осмотре: GTX 1070 (`10de:1b81`+аудио `10de:10f0`) стоит в Proxmox-хосте, **уже привязана к vfio-pci**, blacklist'ы nouveau/nvidia прописаны, IOMMU включён (`intel_iommu=on`), карта одна в IOMMU-группе 1. То есть хост заранее подготовлен под проброс — можно пробросить в VM **без перезагрузки хоста** (не заденет другие VM: nextcloud/minecraft/web-player). Целевая VM 106 = 5 ядер, 2ГБ RAM (мало для CUDA+Docker, поднять). GPU ей ещё не назначен.
|
||
|
||
- [ ] Пуш на gitea (`gitea.jze9.ru/jze9/gausse.git`) или rsync прямо в VM.
|
||
- [ ] `hostpci0: 0000:01:00,pcie=1` в конфиг VM 106, поднять RAM, перезагрузить VM 106.
|
||
- [ ] В VM: NVIDIA-драйвер + CUDA + Docker + nvidia-container-toolkit.
|
||
- [ ] Развернуть проект, запустить sweep (пока CPU!), поднять `gausse serve` (порт 8000).
|
||
- [ ] Дать доступ: веб-морда http://192.168.20.45:8000, SQLite `results/gausse.sqlite3`, лог `results/gausse.sqlite3.log`.
|
||
|
||
- [ ] **Этап 9 — GPU-ускорение массового sweep (сервер с GTX 1070)**: после того как CPU/`scipy.solve_ivp`-модель провалидирована тестами (Этап 2-4) — батч-версия интегратора с ФИКСИРОВАННЫМ шагом (RK4/полу-неявная схема), считающая сразу N траекторий параллельно как один тензор (`cupy`, если доступна CUDA, иначе векторизованный `numpy`/`numba`), для прогона по-настоящему миллионов конфигураций на сервере. Важно: сначала корректность на CPU, потом скорость на GPU — численные результаты GPU-пути должны сверяться с CPU-эталоном на контрольной выборке, чтобы ускорение не подменило точность честными числами "для галочки".
|