Нейросеть вместо решателя поля ветра — план
Состояние на 04.10.2026: идущий эксперимент (решение пользователя 04.10). Этап 1 завершён: пилоты П-1, П-2, П-3 (docs/archive/plan/air-nn-p3.md, раздел «0. Итог P3») — заменимость решателя сетью не достигнута, лучшая сеть — P2 на 300 местах (медиана ошибки ветра 0,62 м/с), она в игре с 1.0.5 как «Нейросеть (экспериментально)» (docs/archive/plan/air-onnx.md). Этап 2 идёт отдельно — ветка feature/ann2 (AN-1).
Исходный статус: план (направление выбрано пользователем 02.10.2026). Реализацию начинать по команде пользователя.
Числа времени — из замеров (docs/guide/air-model.md, docs/research/air_field_cache.md) или помечены «оценка».
Связанное: модель воздуха — docs/guide/air-model.md, контракты — docs/contracts/air-model.md (C1–C10), код игры —
scripts/atmosphere/air_model/; эталонный решатель на CuPy — tools/research/air3d/ (solver.py, air.py,
reference.md; GLSL игры — его перенос, C1); проверки по измерениям — tools/research/cases/ (Askervein, Perdigão);
покров WorldCover — docs/plan/osm_vector_pack.md; источники рельефа — docs/research/terrain_sources.md; влажность
в погоде — scripts/atmosphere/weather_model.gd (точка росы, z_lcl).
Решения пользователя (не обсуждаются)
01.10.2026:
- Инференс — ONNX, в Godot — нативным расширением.
- Вход — карты (рельеф, вода, нагрев) + числа погоды/ветра; направление ветра — поворотом карты к одной стороне.
- Выход — u, v, w, θ′ (и w_mech — без нагрева) на нескольких высотах над рельефом.
02.10.2026:
- В рантайме решателя нет. Цель модуля — заменить GPU-решатель Пикара (
AirPicardJob, окна) ONNX-сетями полностью; режим «тёплый старт решателя от сети» снят. - Офлайн — честный решатель на Python + GPU: без ограничений рантайма по времени, сетке и итерациям. Он — единственная «физика» поля; сеть — его сжатие.
- Сеть области 400 м + отдельные сети мельче (окна 100 и 50 м).
- Охват — любая точка рельефа Земли. Объём набора данных — вопрос времени счёта, не сложности: считаем столько, сколько нужно для охвата.
- Пересчёт модели (новая физика → новый набор → новая сеть) за неделю — приемлемо.
- Новые физические входы добавляются через офлайн-решатель и новые каналы сети: поглощение света подложкой (альбедо/покров), влажность.
- Светопоглощение поверхности закладываем сразу (не через шлюз): альбедо по покрову входит в офлайн-решатель NN-2 и в вход сети с первой версии набора.
- Обучение — с честной проверкой сходимости сети: отложенные данные, повторные циклы, кривые обучения (§5).
- Самым первым — пилот: тестовый цикл обучения, чтобы увидеть, что идея вообще рабочая (этап П, шлюз ШП) — до инструментов и всего остального.
- Затем инструменты. До честной физики, большого набора и долгого обучения — весь конвейер (решатель-обёртка, генератор, загрузчики, обучение, проверка, экспорт, инференс) собирается и проверяется сквозными тестовыми прогонами на малых данных: должно быть доказано, что конвейер обучения работает (волна 0, шлюз Ш0).
- Машина счёта: RTX 4070 SUPER, Xeon E5-2666 v3, ОЗУ 32 ГБ; диск под данные — отдельный, размер по §4.3.
- Читаемая структура файлов и каталогов данных — правила и проверка (§4.4).
- Всё, что можно делать детерминированно, — делается детерминированно, скриптом; работу скриптов агенты не делают (§4.5).
- Прерывание на любом месте (Ctrl+C,
kill, падение, выключение питания) — повторный запуск той же команды корректно продолжает конвейер без потерь, дублей и ручной чистки (§4.6). - Параллельный счёт набора закладывается сразу (пакет случаев на GPU, конвейер CPU ↔ GPU — §4.1, задача NN-T2).
addons/debug_draw_3dудаляется полностью (вместе с меню Debug Menu, F2): снимает конфликт символов libstdc++ (STB_GNU_UNIQUE) с ONNX Runtime, найденный пробником NN-7а; графики CPU/GPU не нужны, остаётся только FPS по F1 (perf_text()вscripts/game/debug_overlays.gd); стрелки ветра F5 остаются — перерисовать своим мешем (подтверждено пользователем 02.10), слой не терять (задача NN-7б).
02.10.2026 (итог первого пилота, прогон 2026-10-02_pilot; вердикт скрипта «правим подход и повторяем пилот»):
- Повторный пилот П-2 — на 300 реальных рельефах (не гладких). Готовые 1890 случаев (
main: встроенные, синтетика, 40 процедурных) остаются в наборе; отложенные процедурные — отдельная проверка «чистой аппроксимации». - Рельеф — тем же путём, что вход игры в рантайме: тайлы Terrarium как
scripts/terrain/terrarium_loader.gd(то же сглаживание — его нет, — осреднение в клетки 400 м); обучение видит ровно то, что увидит игра. - Выбор рельефов: 300 квадратов 38,4 км по горам всей суши, стратифицированно по уклону/перепаду (уклон на сетке 400 м до ≥ 0,4, размах до 2–2,5 км — диапазон Онгудая и круче). Целые горные системы — отложены (главный показатель проверки); Онгудай — вне обучения.
- Вход сети: нормировка рельефа прежняя ((h − mean)/1000 м, фиксированные константы, без min-max на случай); добавить карты уклона вдоль и поперёк ветра (по точному направлению ветра), положения в рельефе (относительно окрестности ~2 и ~8 км) и открытости/затенённости на ветер — только из того, что у игры есть без решателя.
- Счёт: только область 400 м (без окон 100 м); 10–12 условий на рельеф; если хватает времени — больше рельефов, а не условий. Цель обучения (поле − профиль притока)/max(U10, 1) — не менять.
- Критерий ШП-2: ветер — ошибка ≤ max(0,3 м/с; 10 % скорости) в ≥ 90 % точек; подъём — < 0,1 м/с в ≥ 90 % точек; отдельно — нет систематического смещения (средняя знаковая ошибка скорости по области и по высотам, в т. ч. у гребней). Оценки: отложенные горные системы (главное), Онгудай, отложенные процедурные, знакомые рельефы + новые условия, кривая по числу рельефов 25/50/100/200/300.
- Пачки и детерминизм: всё детерминированное — скриптом (§4.5); порядок стадиями пачкой — сначала всё скачать (все тайлы всех мест одним прогоном, с продолжением), потом всё обработать одним прогоном, потом счёт решателя, подготовка, обучение, оценка — без циклов «одно место → посмотрел → следующее». Параллельно по CPU (пул процессов) и GPU (несколько случаев одновременно, конвейер подготовка ↔ GPU), шаги разных стадий на GPU не пересекаются. Каждая стадия прерываема и продолжается той же командой (§4.6), вывод — «этап n из N» + одна строка прогресса, подробный лог в файл. Массовый счёт запускается только по согласованию (пользователь сам, в tmux, или фоном).
02.10.2026 (ответы на вопросы координатора П-2):
- Верхний предел размаха высот в квадрате — ~3000 м (Гималаи 4–5 км не берём: счёт решателя растёт с размахом).
- Порог смещения ШП-2 — max(0,1 м/с; 2 %) — подтверждён.
- Проверка по типам рельефа (для всего модуля, в пилоте П-2 — не обязательно): ошибки отдельно по типам — ровный склон-линия (как Аскарово), одиночный «пупырь», двойной пупырь, две параллельные линии, сложный рельеф (§7).
- Кориолис при обучении не учитываем — отражение поперёк ветра как аугментация (вопрос пользователя: на 40 км и при точности решателя Кориолис не важен). Проверено: в решателе силы Кориолиса нет, f_cor — только для высоты слоя; отражение точное (§3.2), в П-2 — случайное отражение в обучении (NN-P5, контракт П2 — координатор).
- Рельеф как набор паттернов (идея пользователя: «как сжатие JPEG — матрица паттернов»): в план модуля, не в пилот — линейная теория обтекания по гармоникам как карта входа и базовая линия (§3.2), покрытие набора в пространстве паттернов (§4).
03.10.2026 (ответы на вопросы координатора П-2, Q1–Q3):
- Отложенные системы + Южные Альпы Новой Зеландии (15 мест): на tiles/v1 отложенные системы положе пула
(медиана уклона 0,068) — добавить крутую систему, перевыбор и нарезка (П6 v2,
tiles/v2/; NN-16). - Цель на несошедшихся решениях — среднее поздних состояний (вариант Б; снимки 500…1000 через 50 без правки решателя, П1 v3, NN-17); ШП-2 — отдельно для сошедшихся и несошедшихся (П3 v3, NN-18).
- Отказ от направления — если медиана ошибки ветра сети на отложенных системах ≥ 1,0 × лучшей базовой линии.
- До утра: на вопросы координатора принимать рекомендованный вариант, а без рекомендации — наименее трудозатратный; П-2 — проверка, стоит ли идея работы. Готовый П-2 (полный ./run_pilot.sh) запустить без дополнительного одобрения, в отдельном окне tmux (2026-10-03) — пользователь спит; ценность идеи ещё не ясна — не тратить силы на шлифовку
- Стратегия: в игре решатель заменяется ONNX-сетью; решатель остаётся офлайн как источник данных. Скорость важнее точности: небольшая систематическая ошибка (порядка 10–15 % ветра на отдельном месте) допустима — пилот примет её за порыв; это игра, а не прогноз погоды. Вместо эмпирического запасного кода — страж области применимости входа. ШП-2 теперь отвечает на вопрос «насколько сеть готова к замене», отказ маловероятен (2026-10-03) — решатель не всегда сходится, новая физика в шейдерах — тяжёлая возня, .onnx поставляется файлом и быстрее на слабых ПК
- Главный вопрос ШП-2 — заменимость: воспроизводит ли сеть решатель в пределах собственной погрешности решателя. Ошибку сети сравнивать с неопределённостью самого решателя: разброс поздних состояний (несошедшиеся), невязка или остаточные колебания у сошедшихся, расхождение решателя с измерениями (Askervein) и между версиями решателя. Сеть ≲ погрешности решателя → они взаимозаменяемы (2026-10-03) — игре нужна замена решателя, а не точность выше той, что даёт сам решатель
1. Цель и что меняется
Цель: этап «Рассчитываем ветер» и пересчёт в полёте — несколько проходов сети на CPU вместо итераций Пикара на GPU; поле не хуже нынешнего на ключевых числах пилота (§7), в любой точке мира, на любой машине.
| Сейчас | После | |
|---|---|---|
| Расчёт поля | GLSL Пикар на RenderingDevice: 100 + 90 итераций (0,73 с GPU на 4070 SUPER), до 3000 и таймаут на слабом ветре; окна 100/50 м — тем же решателем | ONNX на CPU: область — оценка 50–150 мс (Ryzen 5 4500), окна — десятки мс |
| Точность | 1-й порядок переноса, клетка 400/100/50 м: разгон у бровки занижен на 15–20 % (20–100 м над склоном); на слабом ветре нет сходимости | цель обучения — офлайн-решение на мелкой сетке/2-м порядке, до невязки (§2) |
| Видеокарта | нужна; AMD — оценка ×2…×11 медленнее, до ~15 с; разные GPU дают разное поле | не нужна; ORT на CPU, расхождения между машинами ~1e-6 |
| Физика | нагрев только по уклону/экспозиции и маске воды; z0 = 0,1 м везде; влажности нет | + альбедо и доля явного тепла по покрову, z0 по покрову, влажность (§2.3) |
| Смена физики | правка GLSL + CuPy-эталона + калибровка | правка Python → набор → обучение одной командой (≤ неделя) |
Что остаётся в рантайме: подготовка входа на CPU (рельеф блоками, солнце по склонам, вода, покров — то же, что
сейчас в AirPlace, плюс новые карты), сборка WindField из срезов над рельефом, AirFieldSet/подмена,
AirThermals.build, логика окон вокруг пилота и сдвига, подстройка притока под ветер на старте (air-start: проходы
сети вместо проходов решателя — по мс каждый), запасная аналитика wind_field.gd.
Что уходит (окончательный список — в задаче NN-13 по grep): air_picard.glsl, air_picard_job.gd,
air_line*.glsl, air_mg.glsl, air_multigrid.gd, air_stencil/reduce/vec.glsl, air_gpu*.gd, air_window.glsl,
air_window_job.gd, решательная часть air_case.gd/air_window_case.gd, GPU-тесты решателя. air_poisson_job.gd —
по итогу шлюза Ш3 (проекция массы на CPU или не нужна).
2. Офлайн-решатель
2.1. Основа
tools/research/air3d/solver.py (Air3D, CuPy + CUDA RawKernel) — эталон AM-01, от которого перенесён GLSL (C1).
Переносим в tools/air_nn/solver/ как пакет (не «исследование», а инструмент конвейера), с версией: каждый набор
данных и каждая сеть помечаются версией решателя (хеш кода + параметров калибровки).
Первый шаг — связать цепочку: офлайн-решатель в режиме «как игра» (клетка 400/100/50 м, 1-й порядок, тот же критерий остановки) совпадает с GLSL игры в допуске C1 на 4 встроенных местах. Тогда любое последующее отличие поля сети от нынешней игры объяснимо — это улучшение решателя, а не ошибка переноса.
2.2. «Честно» — что меняем, раз время не ограничено
- Сходимость до невязки, без предела 3000 итераций и таймаута. Слабый ветер (≲ 3 м/с днём, штиль): невязки
импульса и θ′ перестают падать (
docs/guide/air-model.md, air-start). Варианты по порядку: продолжение по ветру (решение при 4 м/с → тёплый старт 3 → 2 → 1 м/с), псевдовремя с локальным шагом; если установившегося решения нет — нестационарный счёт и осреднение по времени (среднее поле — ровно то, что игра читает как среднее). Какой путь — решает исследование NN-1. - Разрешение. Решение области на 200 м (или 400 м с вложенными 200 м под выход) и окна с 2-м порядком переноса
или подокном 25 м. Известное: 2-й порядок на крутом рельефе (Аньези H = L) не имеет установившегося решения —
тогда тоже осреднение по времени; клетка 12,5 м + 2-й порядок сходится к Askervein (
reference.md→ «4. Askervein»). Выбор сетки/порядка — по цене и выигрышу на ключевых числах (шлюз Ш1). - Проверка по измерениям: Askervein, Perdigão (есть скрипты), при возможности Bolund — до и после каждой правки физики. Калибровка — не подгонка под пилотов (правило «физика прежде „нравится“»).
2.3. Новая физика поверхности (входы, которых сейчас нет)
- Поглощение света подложкой. Сейчас
AirPlace.solar_flux: H = H0 (330 Вт/м²) × косинус к склону + рассеянная доля, вода — 0. Станет: поглощённое коротковолновое (1 − α)·S по склону → радиационный баланс Rn (+ длинноволновое упрощённо) → почвенный поток G (доля Rn) → явный поток H = (Rn − G)·β/(1 + β), β — отношение Боуэна по покрову и влажности почвы. Альбедо α и β по классам ESA WorldCover (лес, луг, пашня, голый грунт/скалы, снег/лёд, вода, город) + сезон (снег по месяцу и высоте — связать с TODO «термики зимой: лес против снега»). Источник чисел — литература (Stull, Oke «Boundary Layer Climates», FLUXNET), не подбор. - Шероховатость z0 по покрову (сейчас 0,1 м везде): лес ~1 м, луг 0,03–0,1, снег/вода ~10⁻³ м. Сильно меняет ветер у земли над лесом — физически важнее альбедо для пилота на склоне.
- Влажность. Погода игры уже знает точку росы и уровень конденсации (
weather_model.gd:dew_point_c,z_lcl). В решатель: виртуальная температура в плавучести (θ_v ≈ θ(1 + 0,61 q) — поправка порядка 1 К при q 10–15 г/кг, т.е. сравнима с θ′ термиков), испарение — через β (влажная почва → меньше H), z_lcl — потолок слоя/термиков на выход сети. Скрытое тепло конденсации в облаках — вне модели (как сейчас). - Светопоглощение (α по покрову → Rn → H) входит без шлюза (решение пользователя). Остальное (β/влажность почвы, z0 по покрову, θ_v, z_lcl) — сначала замер чувствительности (насколько меняет ключевые числа §7); не меняет заметно — в физику не входит (шлюз Ш2).
3. Сети
3.1. Каскад
- Область 38,4 км (сейчас 96 × 96 по 400 м; выход 96² или 192² — шлюз Ш1).
- Окно 100 м 64 × 64 (6,4 км) вокруг пилота: вход — рельеф/покров окна с полями запаса + поле сети области, интерполированное в окно; выход — поправка к родителю.
- Окно 50 м — так же от окна 100 м. Нужность решается на шлюзе Ш1 (по пилоту ШП): если окно 100 м (при выходе области 192²) держит ключевые числа §7 в допуске, окно 50 м не делаем — каскад из двух сетей, без набора и обучения третьей.
Окна обучаются на выходе обученной сети родителя, а не на эталонном родителе: в игре родитель — сеть, ошибки родителя иначе размножатся в окне. Цель окна — офлайн-решение окна (вложенное в офлайн-решение области, как сейчас клипмапы AM-04). Одна сеть на всю область сразу на 50 м (768²) на CPU — секунды; не берём.
3.2. Вход
Поворот на кратное 90° так, чтобы ветер дул «с запада» в секторе ±45°, остаток угла — вход (cos, sin); поворот вокруг вертикали точный. Отражение поперёк ветра (y′ → −y′) — тоже точное: в решателе нет силы Кориолиса (f_cor — только модуль для высоты пограничного слоя), поэтому отражённый случай с отражёнными картами, поперечной составляющей солнца и остатком угла (sin r → −sin r) — допустимый мир решателя; в выходе меняется знак u⊥ (решение пользователя 02.10). Если офлайн-решатель получит силу Кориолиса или поворот Экмана (NN-1/NN-2) — отражения убрать. Набор ×4 бесплатно.
Карты (float32, фиксированная нормировка, не статистика набора):
| Канал | Нормировка |
|---|---|
| рельеф hc − среднее по области | /1000 м |
| подсеточная неровность (разброс высот в клетке по слою 25–30 м) | /100 м |
| поглощённая радиация по склону на час (или H, если §2.3 не войдёт) | /800 Вт/м² |
| доля воды | 0…1 |
| альбедо α | 0…1 |
| β (Боуэн) или доля испарения | log β / ограничение |
| ln z0 | /ln 1 м |
| (окна) поле родителя: u∥, u⊥, w_mech, w_conv, θ′ на 13 высотах | как выход |
Числа (MLP → FiLM): U10/10, остаток угла, профиль dθ̄/dz (16 значений, К/км), z_i и z_lcl над средним рельефом /1000 м, параметры профиля притока (α, max_profile), облачность, точка росы/q, влажность почвы, широта (|f| Кориолиса), признак «с нагревом / без».
Линейное поле как опора (решение пользователя 02.10 — в план, не в пилот). Рельеф раскладывается по гармоникам (FFT — как DCT в JPEG), отклик ветра на каждую гармонику — передаточная функция линейной теории обтекания холмов (Jackson–Hunt 1975; Mason–Sykes; на ней WAsP, LINCOM, MS-Micro), отклики складываются — поле u, v, w на высотах над рельефом за миллисекунды. Это поле — дополнительная карта входа (сеть учит нелинейную поправку: отрыв, роторы, крутые склоны) и базовая линия в отчётах (сеть обязана её обгонять; сейчас базовая — профиль притока). Проверить: выигрыш на ключевых числах против цены в рантайме (FFT 96² на CPU — доли мс), поведение на крутых склонах (линейная теория там завышает — сети это и править).
Подготовка входа должна быть одинаковой в Python и в игре — главный стык модуля (контракт N1, контрактный тест на совпадение тензоров ≤ 1e-4).
3.3. Выход
На 13 высотах над рельефом (25, 50, 75, 100, 150, 200, 300, 400, 600, 800, 1100, 1500, 2000 м): u∥, u⊥, w_mech, w_conv,
θ′ + толщина слоя h_bl, z_lcl → ~67 каналов. Цель — отклонение от профиля притока, нормированное на max(U10, 1 м/с);
w_conv и θ′ — на конвективные масштабы w*, θ*. Выше 2000 м — к свободному потоку до верха. Сборка 3D WindField
(центры клеток, C3) — перенос срезов над рельефом в высоты над морем на CPU.
3.4. Архитектура
U-Net (CNN) + FiLM: 4–5 понижений — рецептивное поле на всю область (след за гребнем и волны — 10+ км, 25–40 клеток);
локальные детали (бровки, седловины) — сильная сторона свёрток; простые операторы ONNX (Conv, Resize, GroupNorm);
прецедент — WindSeer (CNN-кодер-декодер на RANS над рельефом, windseer/nn/models/ModelEDNN3D.py, потери с
дивергенцией — windseer/nn/losses.py). Запасы, если U-Net систематически ошибается в дальнем следе: FNO (FFT в ONNX —
opset 17, поддержан хуже), трансформер по патчам (жаден к данным; при «объём — вопрос времени» это уже не запрет).
Размер (оценка): 3–8 млн параметров, 6–16 МБ fp16, 6–15 GFLOP на область.
Вне области обучения (U10 > предела набора, перепады рельефа и z_i вне диапазона): ограничение входа краем
диапазона + строка в журнал; аналитика wind_field.gd — только при ошибке ONNX. При охвате «любая точка» диапазоны
рельефа задаёт набор (§4), а не место.
Масса. Потери с дивергенцией; после сети — проекция на бездивергентное поле на CPU (многосетка ~96²×30, оценка десятки мс), если без неё |div| заметен на ключевых числах (шлюз Ш3).
4. Набор данных
Рельеф — вся суша. Выборка вырезок 38,4 × 38,4 км по Copernicus GLO-30 + WorldCover 10 м: стратифицированно по перепаду высот, крутизне, покрову, широте, доле воды (горы всех систем, предгорья, плато, побережья, ледники, равнины — равнинам малая доля). 4 встроенных места — обязательно (но Онгудай/Каянча и ещё одно — в отложенной проверке). Оценка объёма: 3–10 тыс. вырезок; окончательно — по кривой обучения (ошибка на отложенных против объёма: продолжаем считать, пока падает).
Условия — квазислучайная выборка (Sobol) на вырезку: U10 0–15 м/с (гуще 0–4), направление, дата и час (солнце),
t_max, небо, точка росы, влажность почвы, профиль устойчивости из WeatherModel на эти условия. Каждое — пара «с
нагревом / без». Окна — 2–4 положения на решение области (старт, случайные склоны, седловины).
Цена (оценка до NN-1): офлайн-решение пары области на 200 м до невязки — 30–120 с на 4070 SUPER; 30–50 тыс. пар → 250–1700 ч GPU — от 2 недель до 2 месяцев одной картой. Это и есть «вопрос времени»: конвейер считает фоном с продолжения, обучение начинается с первых 5–10 тыс. пар, набор дополняется. Ускорители: аренда GPU (решает пользователь), тёплый старт по соседним часам одной вырезки (уже −40–70 % итераций). Пересчёт после правки физики укладывается в неделю только для части набора → версия набора = версия решателя, переобучение — дообучением на новом наборе (шлюз Ш1 оценит реальную цену).
Покрытие набора в пространстве паттернов (решение пользователя 02.10 — в план). Каждый кусок рельефа (окно ~5–10 км) описывается коэффициентами по паттернам — спектр (FFT/DCT по масштабам и направлениям) или главные компоненты кусков. По ним: (1) отбор мест — равномерно заполнять пространство паттернов, а не только по уклону и размаху; (2) проверка «вне распределения» — насколько кусок места далёк от облака обучающих (ошибку первого пилота — Онгудай круче всего обучения — так видно заранее); (3) в рантайме — флаг «место вне набора» для отчёта/журнала.
4.1. Параллельный счёт (решение пользователя: закладываем сразу)
Машина счёта — RTX 4070 SUPER + Xeon E5-2666 v3 (10 ядер / 20 потоков; docs/research/air_field_cache.md). Замеры
решателя (docs/guide/air-model-gpu.md → «Замеры»): на 96² данные целиком в L2 (48 МБ, ~20 МБ на случай), а V-цикл
упирается в число запусков и барьеры (61–180 запусков по 10–20 мкс), не в память — карта недогружена. На 192²
уже DRAM: resid7 ~85 % полосы, прогонки по y/z — 20–30 % (зебра через клетку). Отсюда:
- Пакет случаев в одном запуске (ось пакета B в ядрах CuPy: одна вырезка × несколько условий или разные вырезки одного размера сетки): запуски и барьеры делятся на B. Ожидание (оценка): на 96² и окнах 64² — ×2–4 к пропускной способности; B > 2 выходит из L2 — оптимум найти замером B = 1, 2, 4, 8, 16. Сходимость у случаев разная: сошедшиеся выходят из пакета, место занимает следующий (пакет «с подменой»).
- Конвейер CPU ↔ GPU: подготовка входа (солнце, покров, профиль погоды) — пул процессов на Xeon (8–16 процессов), решатель — один процесс GPU с очередью, запись — отдельный процесс; GPU не ждёт CPU.
- Несколько потоков CUDA (streams) для разных размеров сетки (область и окна одновременно) — если пакет не загружает карту.
- 192² (честный решатель): пакет даёт меньше (уже DRAM); выигрыш — от ядер: обход зебры плитками, чтобы вторая
чётность шла из L2, шаблон в fp16 (§4.2) — пункты, давно записанные в
air_model_gpu.md. - Общая карта: генератор берёт замок
/tmp/heat_ca_gpu.lock(как остальные исследования) на пакет, а не на весь счёт — между пакетами успевают GPU-тесты других модулей. Обучение и счёт набора одновременно — допустимо по памяти (решатель 1–3 ГБ + обучение 3–5 ГБ из 12), но делят полосу; по умолчанию — по очереди (ночь — счёт, день — обучение), одновременно — по замеру. - Приёмка — в NN-T2: кривая «пар в час от B» и загрузка GPU (
nvidia-smi dmon), выбранный режим.
4.2. Точность чисел: где fp16, где fp32
Точность до сотых м/с в ответе не нужна, но решатель считает в fp32, и это не про точность ответа:
- у fp16 мантисса 11 бит (~3 знака, шаг 5·10⁻⁴ от величины): итерации Пикара и многосетка добавляют малые поправки
к большим значениям — в fp16 они теряются, невязка встаёт на полку ~10⁻³ (в fp32 пол уже ~10⁻⁵ от max|f|,
air_model_gpu.md), сходимость до невязки и проверка массы невозможны; - θ около 300 К в fp16 идёт шагом 0,25 К — больше, чем θ′ термиков (храним отклонения, но фон всё равно нужен);
- на GeForce Ada обычная (не тензорная) арифметика fp16 не быстрее fp32 — выигрыш только в полосе памяти.
Где fp16 полезен:
| Где | Тип | Почему |
|---|---|---|
| счёт решателя (поля, невязки, суммы) | fp32 (суммы баланса — fp64, как сейчас) | сходимость, см. выше |
| хранение коэффициентов шаблона/прогонок в решателе | fp16 (опыт в NN-1) | решатель упирается в полосу; коэффициенты не накапливаются — ожидание ×1,3–1,7 на ядрах 192² (оценка), проверить, что невязка и ключевые числа не меняются |
| образцы набора (цели, карты входа) | fp16 | цели нормированы (отклонение от профиля / U10) — шаг ~10⁻³ от U10, т.е. < 0,01 м/с при 10 м/с; bf16 (8 бит мантиссы) — нет |
| обучение | bf16 смешанная точность (веса и оптимизатор — fp32) | обычная практика, ×2 к скорости на тензорных ядрах |
| ONNX в игре | веса fp16 в файле, счёт fp32 | у CPU игроков нет быстрой fp16-арифметики |
4.3. Форматы и диск
| Данные | Формат | Размер (оценка) |
|---|---|---|
| вырезки рельефа и покрова (источник) | GLO-30 и WorldCover читаются из облачных COG окном (без скачивания тайлов 1°); хранятся рельеф 30 м int16 (дм) + покров 30 м uint8 (класс) + доли классов, Zarr + zstd | ~3 МБ на вырезку → 10 тыс. — ~30 ГБ |
| статические карты входа на вырезку (рельеф, неровность, α, β, z0, вода) | один раз на вырезку, fp16, Zarr | 192² × 8 × 2 Б ≈ 0,6 МБ → 10 тыс. — 6 ГБ |
| образец области (пара с нагревом/без): карта солнца на час + выход 13 высот × 5 каналов + 2D | fp16, Zarr, кусок = образец | 96²: ~1,2 МБ; 192²: ~4,8 МБ |
| образцы окон 100/50 м (2 положения × 2 уровня на образец области) | fp16, Zarr | ~0,7 МБ на окно → ~2,8 МБ на образец области |
| условия, время, невязка, сходимость, деление | state.sqlite при счёте (§4.5) → Parquet для анализа | десятки МБ |
| дешёвый набор «как игра» (multi-fidelity), до 100 тыс. пар 96² + окна | как выше | ~200–400 ГБ |
| честный набор 30–50 тыс. пар (192²/96² + окна) | как выше | ~120–380 ГБ |
| полные 3D-решения — только тест и отложенные места (~15 %) | fp16 + zstd, Zarr | ~20 МБ на пару → ~100–150 ГБ |
| обучение: чекпойнты (веса + AdamW ≈ 100 МБ), Optuna, кросс-проверка, ансамбль | .pt (веса — safetensors) | 20–50 ГБ |
| модели для игры | ONNX (+ JSON метаданных) | 6–16 МБ на сеть, в data/air_nn/ (в git — по решению на Ш3; иначе — загрузка при сборке) |
Итого на одну версию решателя — ~0,5–1 ТБ; держать две версии (текущую и прежнюю для сравнения) — до ~1,5–2 ТБ. Нужен диск: NVMe SSD 2 ТБ (минимум 1 ТБ — тогда одна версия набора и дешёвый набор удаляется после предобучения). Полные 3D-решения всего набора не храним (~1 ТБ на версию): для пересчёта после правки физики тёплый старт берётся из выхода текущей сети, собранного в 3D (как CFDNet), — архив не нужен.
ОЗУ машины счёта — 32 ГБ: набор в память не влезает, обучение читает его потоком из Zarr (DataLoader, 4–8 процессов
с упреждением); с NVMe (2–3 ГБ/с) эпоха на 300 ГБ — ~2–3 мин чтения, сравнимо со счётом обучения. Пул подготовки
входа при счёте (§4.1) — не больше 8 процессов (~1 ГБ каждый).
Почему Zarr: куски по образцу, дописывание и продолжение с места, частичное чтение, параллельная запись процессами,
сжатие zstd; один каталог на версию решателя, путь — параметр конвейера: <диск данных>/air_nn/<версия>/.
4.4. Структура файлов (решение пользователя: читаемая, не «всё в одну папку»)
Правила для всех задач модуля; нарушение — повод не принять задачу.
- Каждый файл — в каталоге своего вида (сырьё, набор, прогон обучения, модель, отчёт); ничего в корне диска
данных и в корне
tools/air_nn/, кроме перечисленного ниже. - В имени каталога — что и какая версия, без «test2», «new», «tmp_final»:
<вид>/<версия решателя>/<имя>. Версия решателя —s<номер>-<короткий хеш>(напримерs3-a1b2c3d), прогон обучения —<дата>_<имя опыта>. - В каждом каталоге набора, прогона и модели —
manifest.json: что это, чем сделано (команда, коммит, версия решателя и контракта), из чего (входные каталоги), размер, дата. Человеку —README.mdв корнях видов. - Временное — только в
tmp/(чистится командой); удалённое по правилам §4.3 — удаляется целиком каталогом, с записью вCHANGES.mdдиска данных. - Пути только через конфиг конвейера (
AIR_NN_DATA=<диск данных>/air_nn), не зашиты в скрипты. rebuild.sh check-layout— проверка: нет лишних файлов в корнях, у каждого каталога естьmanifest.json, имена по шаблону, ссылки манифестов на существующие каталоги. Запускается в конце каждого шага конвейера и в приёмке задач.
Диск данных ($AIR_NN_DATA):
air_nn/
README.md — что где лежит, правила (эта структура)
CHANGES.md — что и когда удалено/пересчитано
tiles/ — вырезки рельефа и покрова (не зависят от версии решателя)
v1/ — версия формата вырезок
manifest.json — источники, лицензии, выборка, стратификация
index.parquet — вырезка: id, координаты, регион, признаки, часть (train/val/test)
tiles.zarr/ — массивы по id
datasets/ — наборы, по версии решателя
s3-a1b2c3d/
cheap/ — «как игра» (multi-fidelity)
honest/
manifest.json
plan.parquet — план условий (Sobol)
state.sqlite — статусы случаев, время, невязка (§4.5)
area.zarr/ win100.zarr/ win50.zarr/
test3d/ — полные 3D-решения теста
runs/ — прогоны обучения
2026-10-20_area_unet_base/
manifest.json config.yaml metrics.csv tb/ checkpoints/{best,last}.pt
models/ — модели, прошедшие проверку
area/s3-a1b2c3d_2026-10-21/ model.onnx meta.json report/
reports/ — отчёты проверки §7 (картинки, таблицы), по дате и шлюзу
tmp/Код (tools/air_nn/ в репозитории):
tools/air_nn/
README.md rebuild.sh pyproject.toml
configs/ — YAML шагов (gen, train, eval) по опытам
air_nn/ — пакет Python
solver/ io/ tiles/ gen/ train/ eval/ export/ layout.py
tests/ — pytest (обратимость, деление, формат, layout)Игра: модели — data/air_nn/<сеть>.onnx + <сеть>.json; расширение — свой каталог (NN-7).
4.5. Детерминированность и скрипты (решение пользователя)
Всё, что можно сделать детерминированно, делает скрипт конвейера, а не агент «руками». Агент пишет и чинит скрипты, запускает их и читает их отчёты; сам решает только то, где нужно суждение (архитектура, разбор ошибки, вывод отчёта, вопрос на шлюз).
Скриптом, а не агентом:
- выбор вырезок, план условий (Sobol с зерном), деление train/val/test (по хешу id вырезки/региона, не вручную);
- счёт, продолжение, повтор упавших, удаление по правилам §4.3, перенос/переименование каталогов;
- метрики, таблицы ключевых чисел, картинки, сравнение с базовыми линиями, кривые — отчёт строит
eval, агент его не пересчитывает и не собирает таблицы вручную; - манифесты,
check-layout, сводки размеров и времени; - экспорт ONNX и проверка ORT, копирование моделей в игру.
Нельзя: разовый Python в чате агента вместо шага конвейера, правка данных/манифестов руками, числа в отчёте, которых
нет в выводе скрипта. Нужна новая операция — сначала шаг или опция в
rebuild.sh, потом запуск.
Детерминированность:
- все зёрна (план, деление, инициализация, перемешивание, аугментация) — в конфиге и в манифесте;
- одинаковый вход и версия кода → те же данные: генерация и решатель — побитно (как сейчас у GLSL, два прогона совпадают); пакетный решатель (§4.1) = одиночный побитно, иначе — в допуске решателя, записанном в манифест;
- обучение:
torch.use_deterministic_algorithmsи фиксированный порядок данных; если детерминированный режим заметно медленнее или часть ядер cuDNN его не поддерживает — допуск «два прогона ± разброс метрик» с замером разброса (NN-T3), записанный в манифест; - каждый результат воспроизводится командой из своего
manifest.json— проверяется в приёмке (NN-T6, NN-8). Пилот (этап П) — по тем же правилам: исследовательский код, но команды и зёрна в README, отчёт — скриптом.
Состояние конвейера — SQLite (решение пользователя). Одна база на набор: datasets/<версия>/<набор>/state.sqlite
(и runs/state.sqlite для прогонов обучения/Optuna). Хранит статусы и метаданные, не массивы (массивы — Zarr):
- таблицы:
cases(id, вырезка, условия, часть, статус planned/running/done/failed, попытки, процесс, время, итерации, невязка, путь в Zarr),batches(пакет GPU, B, время),events(журнал ошибок),meta(версия схемы, решателя, контракта); - захват работы — транзакцией (
UPDATE … SET status='running' … RETURNING), WAL,busy_timeout; пишет в основном процесс записи (§4.1), воркеры — короткими транзакциями; упавший процесс →runningстарше срока возвращается вplannedскриптом; - агенты смотрят статус только через скрипт (
rebuild.sh status— сводка: сколько готово/упало, скорость, ETA, диск), а не запросами руками; правка базы руками запрещена (§4.5); - для анализа — выгрузка в Parquet скриптом;
runs.jsonlне ведём (источник правды — база); - база — только на локальном диске (на сетевой ФС блокировки SQLite ненадёжны); резервная копия —
.backupв конце каждого шага. Задачи агентов и их статусы — по-прежнему журнал модуляdocs/plan/air-nn_progress.md(процесс.claude/workflow.md), в базе их не дублируем.
4.6. Прерывание и продолжение (решение пользователя)
Любой шаг можно прервать в любой момент; та же команда продолжает с места, итог совпадает с непрерванным прогоном (побитно для данных, в допуске §4.5 для обучения). Ручной чистки после обрыва не бывает.
Как:
- Порядок записи — сначала данные, потом статус. Образец пишется в Zarr во временный ключ →
fsync→ переименование (атомарно на локальной ФС) → только затемstatus='done'вstate.sqliteодной транзакцией. Обрыв между шагами даёт «данные есть, статус нет» → при продолжении случай пересчитывается и перезаписывается (детерминированно — тот же результат); «статус есть, данных нет» невозможен. - Аренда случая:
runningс отметкой времени и PID; при старте скрипт возвращает вplannedслучаи, чей процесс мёртв или аренда истекла. Замок GPU (flock) снимается ОС при смерти процесса. - Мягкая остановка (SIGINT/SIGTERM): дописать текущий пакет или бросить его (по флагу), вернуть незавершённые
случаи в
planned, закрыть базу; второй сигнал — немедленный выход (дальше — как жёсткий обрыв). - Шаги идемпотентны.
rebuild.sh allпропускает шаги, чей манифест помеченcompleteи чьи входы (хеши конфигов, версий, входных каталогов) не изменились; незавершённый шаг продолжает, изменившиеся входы — шаг заново в новый каталог (старый не портится). - Обучение: чекпойнт каждые N минут и в конце эпохи, атомарно (временный файл → переименование); в чекпойнте —
веса, оптимизатор, расписание LR, EMA, состояния ГСЧ (Python/NumPy/torch/CUDA), позиция в данных (эпоха, шаг,
порядок перемешивания). Продолжение = непрерванный прогон в допуске §4.5. Optuna — хранилище исследования в
SQLite (
runs/state.sqlite): прерванный подбор продолжается, оборванные пробы помечаются и повторяются. - Загрузка вырезок: докачка по частям с повтором; вырезка готова, только когда все её массивы записаны и проверена сумма.
- Нехватка диска — проверка свободного места перед пакетом; не хватает — мягкая остановка с понятной строкой, не битые файлы.
rebuild.sh statusпоказывает и последствия обрыва: сколько случаев возвращено в очередь, сколько перезаписано.
Проверка — «тест обрывов» скриптом (tools/air_nn/tests/chaos/): прогон мини-шага, kill -9 в случайные моменты
(включая между записью данных и статуса) N раз, продолжение до конца → набор побитно равен непрерванному, без дублей
и потерь, check-layout чист; то же для обучения (обрыв посреди эпохи и при записи чекпойнта) — метрики равны
непрерванному в допуске. Входит в приёмку NN-T2, NN-T3, NN-T6.
5. Обучение и экспорт
5.1. Деление данных — по местам, не по образцам
Случайные 70/30 по образцам здесь врут: один рельеф при разных часах/ветрах даёт похожие поля, сеть «узнаёт» место, и ошибка на проверке выходит заниженной. Поэтому деление — по вырезкам и регионам (пространственные блоки):
| Часть | Доля (оценка) | Зачем | Когда смотрим |
|---|---|---|---|
| обучение | ~75 % вырезок | подгонка весов | всегда |
| проверка (validation) | ~10 % вырезок, другие регионы | ранняя остановка, выбор архитектуры и гиперпараметров | каждую эпоху |
| тест | ~15 %: целые горные системы + Онгудай/Каянча + одно встроенное место | итоговые числа §7 | только на шлюзах (Ш3, Ш4), не для настройки — иначе он становится второй проверкой |
| Соседние вырезки одного региона — в одной части (зазор между частями ≥ 1 вырезки), повёрнутые на 90° копии — в той | |||
| же части, что исходник. |
Повторные циклы (кросс-проверка по регионам, k = 5): обучение 5 раз, каждый раз отложен свой блок регионов → разброс ошибки между прогонами = насколько оценке можно верить и насколько сеть зависит от того, какие горы видела. Дорого (×5 обучения) — делаем на шлюзе Ш3 и после крупных правок, не на каждом дообучении.
5.2. Кривые: сходится ли сеть и хватает ли данных
- Кривая обучения по эпохам (ошибка на обучении и на проверке): расходятся — переобучение (больше данных, регуляризация); обе высоки — сеть мала или вход беден.
- Кривая по объёму набора: обучение на 10/25/50/100 % вырезок, ошибка на тесте. Ещё падает — считать набор дальше (это и есть ответ «сколько считать»); вышла на полку — упираемся в архитектуру/вход, не в данные.
- Ошибка по срезам (сильный/слабый ветер, час, тип рельефа и покрова, широта) — где сеть слаба, туда и досчитывать.
5.3. Приёмы обучения (современная практика для суррогатов физики)
Брать по порядку, каждое — только если улучшает ошибку на проверке:
- База: AdamW, скорость обучения с прогревом и косинусным спадом (или OneCycle), смешанная точность (bf16), ранняя остановка по проверке, EMA весов (скользящее среднее весов — устойчивее итог).
- Аугментация: повороты на 90° и отражение поперёк ветра (§3.2) — точные, бесплатно; отражение — случайно с вероятностью 1/2 при каждом показе примера (время эпохи то же, разнообразие ×2).
- Физика в потерях: штраф дивергенции; веса у земли и над бровками/стартами.
- Подбор гиперпараметров (ширина каналов, глубина U-Net, веса потерь, LR): Optuna, 20–50 коротких прогонов на части данных, оценка — по проверке.
- Ансамбль из 3–5 сетей (разные зёрна): разброс между ними — оценка неуверенности. Даёт два выигрыша: в игре можно среднее (точнее одной сети, ×3–5 времени — по бюджету) или одну сеть; в конвейере — активное обучение: прогнать ансамбль по тысячам ещё не посчитанных вырезок/условий, решателем считать в первую очередь те, где сети расходятся сильнее всего. Так «время счёта» тратится там, где сеть хуже знает.
- Многоуровневая точность (multi-fidelity): предобучение на дешёвых решениях «как игра» (400 м, 1-й порядок — ×10–20 дешевле, их можно насчитать много), дообучение на меньшем наборе честных решений. Обычно резко снижает нужный объём дорогих данных; проверить в NN-6 сравнением «только честные» против «дешёвые + честные».
- Дообучение при смене физики: новая версия решателя → дообучение прежней сети на новом наборе (старт с весов), а не обучение с нуля — так пересчёт укладывается в неделю.
PyTorch на 4070 SUPER (12 ГБ). Оценка времени: 30–60 с на эпоху на 10 тыс. пар, 100–200 эпох — 2–3 ч; на 50 тыс. — ночь;
кросс-проверка ×5 и ансамбль ×3–5 — ночи. Экспорт torch.onnx.export (opset 17), проверка ORT против PyTorch ≤ 1e-4,
веса fp16 (если ORT CPU теряет время на fp16 — fp32). В метаданные ONNX: версия решателя, версия контракта N4,
диапазоны обучения.
Конвейер одной командой: tools/air_nn/rebuild.sh <шаг> — решатель → набор (с продолжения) → обучение → экспорт →
проверка §7 → копирование моделей в data/air_nn/ игры.
6. Рантайм в Godot
- GDExtension (godot-cpp) + ONNX Runtime C API, CPU EP в рабочем потоке, intra-op 4 потока. Основа —
godot-onnx-loader / godot_onnx_extension или своя тонкая обёртка (~300 строк): загрузить модель, задать тензоры,
запустить, вернуть
PackedFloat32Array. Сборки linux-x64, win-x64, macOS universal. +20–35 МБ на платформу. AirRuntime: веткаengine = "nn"рядом с решателем (до шлюза Ш5), затем решатель удаляется. Загрузка: вход места → сеть области (пара: с нагревом и без — один проход с признаком или два) → air-start: k притока 2–3 проходами сети → окна 100/50 →WindField→ термики. Полёт: пересчёт по сроку/смене погоды — те же проходы.- Сеть (мультиплеер): ORT на CPU почти детерминирован (~1e-6), но маска источников термиков от ведущего (C5) остаётся — пороговые столбцы.
- Бюджет (цель, проверить в NN-10): этап «Рассчитываем ветер» на Ryzen 5 4500 без GPU-расчёта ≤ 1,5 с вместе с
подготовкой входа и
AirThermals.build; пересчёт в полёте ≤ 0,3 с работы рабочего потока, кадр не стоит.
Риски: сборка для Windows из Linux (MinGW против MSVC-сборки ORT — через C API и импорт-либу), macOS — подпись dylib и сборка без Mac; размер сборки.
7. Проверка (шлюзы Ш3, Ш4)
- Отложенные места: Онгудай/Каянча и ещё одно встроенное — целиком вне обучения; + 10 % вырезок по регионам (целые горные системы вне обучения — проверка «любой точки»).
- Ключевые числа пилота (колонка старта, 60 м над землёй; пороги из
air_field_cache.md,air_model_sensitivity.md): ошибка сети против офлайн-решения — ветер < 0,3 м/с, подъём < 0,1 м/с в ≥ 90 % случаев. Плюс RMS ветра в 1 км от старта, полоса подъёма склона, провал за гребнем, разгон в седловине (отдельно — занижение пиков), z_lcl/h_bl. - По типам рельефа (решение пользователя 02.10): ошибки отдельно по типам окружения точки — ровный склон-линия (как Аскарово), одиночный «пупырь», двойной пупырь (две вершины с седловиной), две параллельные линии (хребты с долиной), сложный рельеф. Тип — автоматически, скриптом: геоморфоны (Jasiewicz, Stepinski 2013: класс клетки по узору «выше/ниже» в 8 направлениях — гребень, отрог, склон, ложбина, долина, вершина, седловина) → доли классов в окне 5–10 км → тип. Таблица ошибок по типам — в каждом отчёте оценки; отложенные горные системы остаются проверкой «незнакомого места».
- Что изменилось для пилота: офлайн-решение и сеть против нынешнего GLSL на тех же стартах — таблица отличий с объяснением (мельче сетка, сходимость, покров, влажность). Это не ошибка, но пользователь должен их увидеть.
- Термики из поля (
AirThermals.build): число/сила источников против офлайн-поля. - Масса: |div| до и после проекции.
- Внешний эталон решателя — WindNinja (04.10, массосогласованный режим, 21 случай на 6 местах, 60 м): разгон на гребнях и направление совпадают (1,19 против 1,13 от притока, медиана 10°); торможение массивом и затенение (долины 0,84 против 0,30, подветренные склоны 0,97 против 0,34) он не воспроизводит — нет импульса; проверка торможения — только импульсным режимом или наблюдениями. Паспорт: tools/research/windninja/README.md.
- Вне распределения: поле на вырезках с краёв диапазонов — нет взрывов, ограничители
max_speed_ms/max_w_msне срабатывают. - Время ORT: Ryzen 5 4500 (или ближайший доступный — замер), 4070-машина на CPU.
8. Организация работы
Модуль air-nn: ветка feature/air-nn, копия ~/deltaplan-air-nn, координатор dp-coordinator. Задачи — копии
~/deltaplan-air-nn-<задача>, ветки air-nn/<задача>. Данные — вне git (<диск данных>/air_nn/). Долгие счёты
GPU — только через dp job или фоном; GPU одна — набор и обучение не пересекаются во времени с GPU-тестами
других модулей (координатор согласует через главную сессию).
8.1. Контракты (до первого исполнителя, docs/contracts/air-nn.md)
| № | Стык | Владелец → потребители | Что фиксирует |
|---|---|---|---|
| N1 | вход: место + условия → тензоры входа | NN-T1 (Python), NN-4 (игра); расширяет NN-2 → сеть, рантайм | список карт и чисел, нормировки, порядок осей (j — север, как C3), поворот на 90°, единицы, что при отсутствии покрова/воды; контрактный тест: Python и GDScript дают одни тензоры ≤ 1e-4 на 4 местах × 3 часа |
| N2 | офлайн-решение → образец набора | NN-T1 → NN-T2, NN-5 | каналы, 13 высот над рельефом, перенос из 3D-решения в срезы, нормировка целей (§3.3), окна: родитель/цель |
| N3 | формат набора | NN-T2 → NN-T3, NN-6, NN-9, NN-8 | Zarr, схема state.sqlite, версия решателя, деление обучение/проверка/отложенные |
| N4 | ONNX-модель | NN-T4 (затем NN-6/NN-9) → NN-T5, NN-7, NN-10 | имена/формы/типы тензоров, opset, метаданные (версия, диапазоны), область/окно 100/окно 50 |
| N5 | GDExtension API | NN-7 → NN-10 | класс, методы, потоки, ошибки, где лежат библиотеки и модели |
| N6 | выход сети → WindField/AirFieldSet/AirRuntime | NN-T5 (черновик), NN-10 → атмосфера, термики | преемник C3/C7/C9: сборка 3D из срезов, сдвиг окон, air-start, подмена; C1/C8 — снимаются в NN-13 |
8.2. Задачи
Оценки — дни работы агента; «ночи» — счёт GPU фоном.
Этап П — пилот: работает ли идея (решение пользователя: самым первым)
Вопрос один: может ли сеть по карте рельефа и числам погоды выдать поле решателя с полезной точностью — в том числе
на рельефе, которого не видела. Быстро, на готовом, без инструментов волны 0; код — исследовательский, но
воспроизводимый: tools/research/air_nn_pilot/ (README с командами), данные — $AIR_NN_DATA/pilot/ (§4.4).
| № | Задача | Агент | Скоуп, файлы | Приёмка | Оценка |
|---|---|---|---|---|---|
| NN-P | Пилот: набор, обучение, оценка | dp-researcher | данные — генератор tools/research/air_lite/gen.py из ветки research/air-lite без правки решателя (эталон AM-01 «как игра», 4 встроенных места + синтетика, область 400 м и окна 100 м, срезы на 13 высотах, fp16): досчитать план (49/690 готовы; решения air-lite не сохранились — счёт заново) + 20–40 процедурных рельефов (хребты, холмы, седловины, долины со случайными размерами и поворотами — дешёвое разнообразие); сеть — малый U-Net+FiLM только для области 96², поворот на 90°, обучение — часы; оценка — три деления: (а) новые условия на знакомом рельефе, (б) незнакомое место (Онгудай/Каянча вне обучения), (в) кривая по числу рельефов в обучении (5/10/20/40) | таблица ключевых чисел (ветер и подъём на 60 м над стартами, RMS в 1 км) для (а) и (б) против базовых линий: профиль притока без поправки, среднее по набору, регрессия air-lite (out/fit_metrics.json); кривая (в); 5–8 картинок срезов «решатель / сеть / разница»; время инференса ORT на CPU; вывод и рекомендация | 2–3 д + 1–2 ночи GPU |
Шлюз ШП (пользователю), ориентиры, не окончательные пороги §7:
- (а) знакомый рельеф: ошибка ветра на 60 м < 0,3 м/с, подъёма < 0,1 м/с в ≥ 90 % случаев — если нет, сеть или вход не годятся, разбираться до всего остального;
- (б) незнакомое место: заметно лучше базовых линий;
- (в) ошибка на незнакомом рельефе падает с числом рельефов — тогда идея рабочая, и большой набор (§4) — путь к точности; полка на (в) — менять вход/архитектуру, а не считать больше. Итог шлюза: «идём в волну 0», «правим подход и повторяем пилот» или «отказ от направления».
Итог ШП (02.10, отчёт pilot/reports/2026-10-02_pilot/report.md): «правим подход и повторяем пилот». Сеть заметно
лучше базовых линий (ветер на 60 м на знакомом рельефе и отложенных процедурных — в ~2,5 раза точнее профиля
притока; подъём 89–92 % < 0,1 м/с), ORT CPU 19 мс (4 потока); но переобучение (проверка на полке ≈ 0,42 с ~30-й
эпохи) и систематическое завышение ветра на Онгудае при сильном ветре (+14 %) — вход вне диапазона обучения: медианный
уклон 400 м у Онгудая 0,22 (p95 0,39, размах 1964 м) против процедурных 0,016 (p95 0,12, ~720 м). Кривая (в) по ветру
падает — число рельефов главный рычаг. Решения пользователя — выше («02.10.2026, итог первого пилота»).
Этап П-2 — повторный пилот на реальных рельефах (контракты П1 v2, П2 v4, П3 v2, П6 v1). Код — тот же каталог
tools/research/air_nn_pilot/, данные — $AIR_NN_DATA/pilot/. Порядок стадий одной командой run_pilot.sh:
скачивание всех тайлов → нарезка всех мест → счёт решателя (несколько случаев на GPU одновременно) → подготовка
тензоров (пул процессов) → обучение → кривая → оценка → отчёт.
| № | Задача | Агент | Скоуп, файлы | Приёмка | Оценка |
|---|---|---|---|---|---|
| NN-P4 | Рельефы: выбор, скачивание, нарезка | dp-researcher | terrain_cut.py (или пакет pilotnn_terrain/) + configs/terrain.yaml; стадии screen (грубые тайлы → кандидаты по горам суши), select (стратификация по уклону 400 м/размаху, горные системы, отложенные системы), fetch (все тайлы z12 всех мест одним прогоном, пул загрузок, продолжение), cut (все вырезки пулом процессов: путь игры → сетка 25 м → клетки 400 м, признаки, индекс), status; загрузчик мест t_* в places.py; контрактный тест П6 | ~300 мест пула + отложенные системы (по 10–20 мест), гистограммы уклона/размаха пула и отложенных против Онгудая/Алтая/процедурных — диапазон Онгудая покрыт и превышен (уклон p50 до ≥ 0,4 у части мест, размах до 2–2,5 км); побитный повтор вырезки; прерывание fetch/cut (kill -9, повтор той же команды → те же файлы); hc400 совпадает с T.block_mean; объём диска и время стадий | 1–1,5 д + загрузка |
| NN-P5 | Вход и оценка под П-2 | dp-researcher | pilotnn/prep.py (карты П2 v3), split.py (отложенные системы, кривая 25…300 с примесью прочих мест, точка 300 = основная сеть), data.py (несколько наборов), evaluate.py, report.py (критерий П3 v2, смещение, гребни), pilot.py (только чтение нескольких наборов, кривая, оценка), раздел config.yaml (split/eval/curve), тесты | test_prep.py: карты П2 v3 — формулы на аналитическом рельефе (наклонная плоскость: уклон вдоль = известному, поперёк = 0; гора: TPI > 0 на вершине, затенённость > 0 за горой), поворот ×4 = тождество, контракт MAP_NAMES; smoke с подставным индексом П6 проходит до report.md со всеми разделами П3 v2; вывод ШП-2 — правилом из чисел | 1,5–2 д |
| NN-P6 | Генератор П-2: только область, параллельный счёт, замер | dp-researcher | dataset.py (режим «только область», план набора по индексу П6, n_cond условий на место, несколько воркеров на GPU под одним замком, CPU-подготовка параллельно со счётом), airlite_gen.py (решение без окон — без правки решателя), configs/dataset.yaml; тесты | область без окон = срез d400_* прежнего случая побитно (те же условия); кривая «случаев в час от числа воркеров 1…4» и выбор; тест прерывания на воркерах (8/8 побитно, без дублей); пробный замер на 6–10 местах П6 разной крутизны × 2 условия: время случая, доля max, оценка полного счёта (ч GPU, ГБ) | 1–1,5 д |
| NN-P7 | Сборка П-2 в одну команду | dp-researcher (Sonnet) | pilot.py/run_pilot.sh: стадии рельефа и набора П-2 перед прежними, профиль smoke П-2 (несколько мест t_* × 2 условия + прежний smoke), README | ./run_pilot.sh --smoke с нуля до отчёта; прерывание на каждой стадии (Ctrl-C/kill -9) и повтор той же командой → тот же итог; status; оценка полного прогона по замерам NN-P4…P6 и NN-16…18 (tiles/v2, П1 v3, П3 v3) | 0,5–1 д |
| NN-16 | Отложенная система Южные Альпы НЗ (Q1) | dp-researcher (Sonnet) | configs/terrain.yaml (+ система, 15 мест), terrain_cut.py (перевыбор в tiles/v2/, докачка тайлов), tests/test_contract_terrain.py (П6 v2: системы из конфига) | tiles/v2/manifest.json complete: пул 300 + 4 × 15; уклон p50/p95 и размах отложенных по системам против пула; побитный повтор вырезки; контрактный тест П6 v2 | 0,5 д + загрузка |
| NN-17 | Цель несошедшихся — среднее поздних состояний (Q2) | dp-researcher (Opus) | dataset.py/airlite_gen.py (снимки поздних состояний без правки решателя), configs/dataset.yaml (late_mean), tests/test_contract_sample.py (П1 v3) | у max d400_* = среднее снимков (тест против ручного усреднения), у сошедшихся — побитно как v2; late_spread60_p90 на probe; прерывание — побитно; время случая против v2 | 0,5–1 д |
| NN-18 | ШП-2 раздельно по сходимости, вердикт (Q2, Q3) | dp-researcher (Sonnet) | pilotnn/report.py, evaluate.py (группы по статусу решения, разброс), config.yaml, тесты | smoke-отчёт с разделами П3 v3 для обеих групп; вердикт — правилом П3 v3 (отказ → сошедшиеся → правим) | 0,5 д |
| Зависимости: NN-P4, NN-P5, NN-P6 — параллельно (NN-P5 — на подставном индексе П6; NN-P6 — замер на местах П6 после | |||||
| NN-P4); NN-16, NN-17 — параллельно после решений 03.10; NN-18 — после NN-P5; NN-P7 — после NN-P5, NN-16…18. Массовый счёт — только после согласования (готовая команда, время и диск — в отчёте). |
Шлюз ШП-2 (пользователю; правило в config.yaml, числа — из отчёта): главное — отложенные горные системы:
ветер — ошибка ≤ max(0,3 м/с; 10 % скорости решателя) в ≥ 90 % точек области на 60 м; подъём без нагрева и с нагревом
< 0,1 м/с в ≥ 90 % точек; нет систематического смещения скорости (по высотам, по U10, у гребней; порог — П3 v2). Плюс
Онгудай, отложенные процедурные, знакомые рельефы + новые условия, кривая 25/50/100/200/300. Итог: «идём в волну 0»,
«правим подход» или «отказ от направления».
Волна 0 — инструменты и сквозной прогон (решение пользователя: обязательно до массового счёта)
Цель — до дорогого счёта убедиться, что каждый шаг конвейера делает то, что должен, и что сеть на нём вообще
учится. Физика здесь — нынешняя («как игра», 400 м, 1-й порядок), данные — малые; качество сети не цель.
Код — tools/air_nn/ (Python-пакет, pytest), одна точка входа tools/air_nn/rebuild.sh <шаг> (solve, gen,
train, eval, export, check, all), конфиги шагов — YAML с версией, логи и метрики каждого прогона —
в <диск данных>/air_nn/runs/<время>/ (+ TensorBoard или аналог).
| № | Задача | Агент | Скоуп, файлы | Приёмка | Оценка |
|---|---|---|---|---|---|
| NN-0 | Пакет решателя и связка с игрой | dp-researcher | перенос air3d/solver.py + air.py (вход места) в tools/air_nn/solver/, версия по хешу кода и параметров; режим «как игра»; проверка поворота на 90° (Кориолис, приток) | совпадение с GLSL игры в допуске C1 на 4 местах × 3 часа (через tools/research/air3d/gpu_block_refs.py/дампы игры); поле повёрнутого входа = повёрнутое поле в допуске решателя; время одной пары | 1–2 д |
| NN-T1 | Вход и выход: обработчики, структура файлов | dp-researcher | структура §4.4: layout.py (пути из AIR_NN_DATA, манифесты, check-layout), README диска; tools/air_nn/io/: вырезка → карты входа (N1), поворот на 90°, нормировки; 3D-решение → срезы над рельефом (N2) и обратно в 3D; формат шардов (N3), загрузчик PyTorch | check-layout ловит лишний файл в корне и каталог без манифеста (тест); pytest: поворот ×4 = тождество, срезы → 3D → срезы ≤ 1e-5, нормировка ↔ обратная, шард пишется/читается побитно; срезы → 3D на ровном месте совпадают с решением | 2 д |
| NN-T2 | Мини-генератор, параллельный счёт, мини-набор | dp-researcher | tools/air_nn/gen/ в полном виде: Sobol-план, пары с нагревом/без, пакет B случаев в ядрах решателя с подменой сошедшихся, пул CPU-подготовки, процесс записи (§4.1), замок GPU на пакет, Zarr (§4.3), продолжение с места, state.sqlite и rebuild.sh status, деление по регионам §5.1; на малом наборе: 4 места + ~50 вырезок × ~10 условий | пакетный решатель = одиночный побитно или в допуске решателя; кривая «пар в час от B = 1…16» и загрузка GPU, выбранный режим; мини-набор ~500 пар; тест обрывов §4.6 (kill -9 в случайные моменты, включая между данными и статусом) — набор побитно равен непрерванному; деление: ни одна вырезка/регион не попадает в две части (тест); размер образцов на диске со сжатием против §4.3, скорость чтения диска | 3–4 д + ночь |
| NN-T3 | Обучение: каркас и проверки на вменяемость | dp-researcher | tools/air_nn/train/: U-Net+FiLM малого размера, потери, AdamW + LR-расписание, EMA, ранняя остановка, атомарные чекпойнты с состоянием ГСЧ и позицией в данных (§4.6), логирование кривых | тест обрывов §4.6 для обучения (продолжение = непрерванный прогон в допуске); проверки на вменяемость: (1) переобучение одного батча до ошибки ~0 (сеть и потери связаны верно); (2) синтетика с известным ответом — гладкие холмы с аналитическим полем (потенциальное обтекание) — сеть его выучивает; (3) кривая по объёму на мини-наборе (10/25/50/100 %) падает; (4) перемешанные метки → проверка не лучше константы (нет утечки); (5) базовые линии — «профиль притока без поправки» и «среднее по набору» — сеть заметно лучше на проверке | 3 д |
| NN-T4 | Оценка, экспорт, инференс | dp-researcher | tools/air_nn/eval/: ключевые числа §7, срезы и картинки, отчёт одной командой; export: ONNX + метаданные (N4), сравнение ORT ↔ PyTorch | отчёт по мини-сети строится одной командой; ORT ↔ PyTorch ≤ 1e-4; время ORT CPU на область | 1–2 д |
| NN-T5 | Сквозной прогон в игре (заглушка) | dp-engineer | минимальная обёртка ORT (Linux) + чтение мини-ONNX из GDScript, сборка WindField из выхода (N6 черновик), полёт на поле мини-сети на одном месте; та же модель в Python и в игре на одном входе | выход в игре = выход ORT в Python ≤ 1e-4; поле отображается в F3-срезах; время ORT на CPU на машине счёта (Xeon) и на ноутбуке без мощной видеокарты (Ryzen 7 7840HS, 780M) — оценка бюджета для слабых машин; Windows/macOS — не здесь (NN-7) | 2–3 д |
| NN-T6 | Сквозной прогон «всё» | dp-mechanic | rebuild.sh all на мини-наборе с нуля на чистой копии по README | прогон проходит без ручных шагов; check-layout чист; повтор шага по команде из манифеста даёт тот же результат (§4.5); rebuild.sh all, прерванный дважды на разных шагах и запущенный снова, доходит до конца с тем же итогом (§4.6); время каждого шага; отчёт с путями к логам и картинкам | 0,5 д |
Шлюз Ш0 (пользователю): конвейер работает сквозь — числа проверок NN-T3, отчёт мини-сети, время шагов, оценка цены полного набора по замеру. Только после Ш0 — честная физика и массовый счёт.
Волна 1 — честная физика (только Python, игру не трогает)
| № | Задача | Агент | Скоуп, файлы | Приёмка | Оценка |
|---|---|---|---|---|---|
| NN-1 | Честный решатель: сходимость и разрешение | dp-researcher | слабый ветер (продолжение по ветру / псевдовремя / осреднение по времени), область 200 м, окна 2-й порядок или 25 м; Askervein/Perdigão | таблица: невязка и время по ветрам 0,5–12 м/с, доля сошедшихся = 100 % (или осреднено с оценкой разброса); ключевые числа «как игра» против «честно» на 10 стартах; цена пары на 4070; Askervein не хуже нынешнего | 4–6 д + ночи |
| NN-19 | Ускорение решателя: батч случаев (идея пользователя 03.10, до массового счёта NN-5) | dp-researcher (Opus — GPU) | профиль воркера П-2 (py-spy/nsys: доля Python и запуска ядер против счёта; 1 ядро CPU на воркер в 100 % при GPU 100 % — ядра мелкие, сетка 96²); батч 8–16 случаев одним массивом (ось случая в полях решателя, разные места и условия, маска сошедшихся — сошедшийся случай заменяется следующим); слияние операций (cp.fuse/ElementwiseKernel/RawKernel), CUDA Graphs на итерацию, проверка сходимости раз в N итераций; ожидание GPU без спина | случаев в час против нынешних 2 воркеров на probe (цель — в разы); поле батча = поле одиночного счёта в допуске решателя (побитно не требуется, порог — как у теста версии); загрузка SM (nsys), а не только utilization; память GPU на батч; прерывание и продолжение как у dataset.py | 2–3 д |
| NN-20 | Тёплый старт решателя от сети (идея пользователя 03.10; не запускать без решения, рядом с NN-19) | dp-researcher (Opus — GPU) | ответ сети (П-2 или лучшей П-3) → начальное состояние решателя: срезы 13 высот → 3D-сетка, выше 2000 м — профиль притока, турбулентность — из замыкания по этому ветру; несошедшимся — раньше начинать осреднение поздних состояний | 20 случаев (г) разной крутизны и ветра, половина несошедшихся, холодный и тёплый старт: итерации и время до сходимости, разница итоговых полей у сошедшихся ≪ 0,3 м/с (иначе набор «подтягивается» к сети — нельзя), разница late_mean у несошедшихся; оценка: линейная сходимость → ~20–40 % итераций (ошибка старта в 4 раза меньше профиля притока: 0,62 против 2,46 м/с), «в разы» — только если время уходит на крупномасштабную перестройку; складывается с NN-19 | 0,5 д + ~1 ч GPU |
| NN-2 | Физика поверхности: светопоглощение (сразу), Боуэн, z0, влажность | dp-researcher | чтение WorldCover для вырезки, таблица классов → α, β, z0 с источниками; баланс радиации (1 − α)·S → Rn → H — обязательно; β, z0, θ_v, z_lcl — с замером; документ физики (docs/research/air_surface.md) | α в решателе и в N1; Askervein/Perdigão не хуже; чувствительность ключевых чисел к каждому добавлению на 10 стартах × 3 часа; литература к каждому числу; что не вошло и почему | 3–4 д |
| NN-3 | Библиотека вырезок «вся суша» | dp-researcher (Sonnet достаточно) | загрузка GLO-30 + WorldCover по выборке, стратификация (перепад, крутизна, покров, широта, вода), отложенные регионы, кеш; лицензии в terrain_sources.md | 3–10 тыс. вырезок с метаданными, гистограммы признаков против суши, список отложенных регионов; продолжение с места | 2–3 д + загрузка |
Шлюз Ш1 (пользователю): сетка и порядок офлайн-решателя, сетка выхода области (96² или 192²), цена пары, план объёма и сроков счёта, нужна ли аренда GPU. Шлюз Ш2: какие входы §2.3 сверх светопоглощения войдут (по чувствительности).
Волна 2 — набор, сеть области, расширение
| № | Задача | Агент | Скоуп, файлы | Приёмка | Оценка |
|---|---|---|---|---|---|
| NN-4 | Вход сети в игре | dp-engineer | AirPlace + новые карты (покров, α, β, z0, неровность), числа погоды (точка росы, влажность почвы), поворот; данные покрова для встроенных мест и мест с карты (пакет покрова) | контрактный тест N1: тензоры GDScript = Python ≤ 1e-4 на 4 местах × 3 часа; время подготовки на CPU | 3 д |
| NN-5 | Массовый счёт набора | dp-researcher | генератор из NN-T2 на честном решателе (NN-1/NN-2) и вырезках NN-3; окна (позже — на родителе-сети); rebuild.sh gen | первые 5 тыс. пар; доля несошедшихся = 0; сводка по срезам условий | 1 д + недели фоном |
| NN-6 | Сеть области | dp-researcher | каркас NN-T3 на полном наборе: размер сети, приёмы §5.3 по порядку (каждый — с замером на проверке), multi-fidelity, ансамбль, экспорт ONNX, проверка ORT; rebuild.sh train | ORT против PyTorch ≤ 1e-4; кривые по эпохам и по объёму (§5.2); таблица «приём → ошибка на проверке»; ключевые числа §7 на проверке (тест не трогать до Ш3) | 4–6 д + ночи |
| NN-6б | Активное обучение | dp-researcher | ансамбль по непосчитанным вырезкам/условиям → очередь генератора NN-5 по разбросу | очередь работает; ошибка на проверке после досчёта по очереди против досчёта случайного того же объёма | 1–2 д |
| NN-7б | Удалить debug_draw_3d (предусловие NN-7) | dp-engineer | удалить addons/debug_draw_3d и Debug Menu (F2); F5 (стрелки ветра, _wind_frame в scripts/game/debug_overlays.gd) — свой ImmediateMesh/MultiMesh, как меш термиков F6; F1 (perf_text()) остаётся; .godot/extension_list.cfg, ASSETS.md (строки Debug Draw 3D и Debug Menu), configs/controls.json (F2), тесты | grep -ri "debug_draw|DebugDraw|debug_menu" по scripts/ и addons/ пуст; F1 и F5 работают (скриншоты); headless-тесты зелёные; пробник ORT (native/air_nn_probe/) на Linux через обычный dlopen (без dlmopen) при загруженной игре не падает | 0,5 д |
| NN-7 | GDExtension ONNX Runtime | dp-engineer | от заглушки NN-T5 к расширению: addons/air_nn/ или native/air_nn/, godot-cpp + ORT C API, рабочий поток, API N5; сборка linux/win (из Linux), macOS — способ сборки выяснить | инференс пустышки и сети области в headless-тесте; сборки трёх ОС (или шлюз, если macOS без Mac невозможна); время на CPU | 3–5 д |
| NN-8 | Проверка сети области | dp-researcher | кросс-проверка по регионам k = 5 (разброс), тест (первый раз), отчёт §7 по области: отложенные места и регионы, «что изменилось для пилота» против GLSL, термики, масса, вне распределения; картинки | таблица ключевых чисел, 5–8 картинок, рекомендация | 1–2 д |
NN-7 не зависит от NN-6 (пустышка по контракту N4) — идёт с начала волны 2. Шлюз Ш3: точность области достаточна? нужна ли проекция массы? продолжать счёт набора (сколько)?
Волна 3 — окна и игра
| № | Задача | Агент | Скоуп, файлы | Приёмка | Оценка |
|---|---|---|---|---|---|
| NN-9 | Сети окон 100 и 50 м | dp-researcher | набор окон на родителе-сети (вход — выход сети области), обучение, экспорт, проверка §7 для окон (бровка, седловина, стык с родителем) | ключевые числа окон против офлайн-окна; стык окна с родителем не хуже нынешнего (air_clipmap/seam_plot.py) | 4–6 д + ночи |
| NN-10 | Сети в AirRuntime | dp-engineer | ветка engine = "nn": проходы области/окон, сборка WindField (N6), air-start через сеть, сдвиг окон, пересчёт в полёте, термики; конфиг air_model.engine | тесты N6 (форма поля, стык уровней, подмена), полёт на Онгудае/Каянче/месте с карты без GPU-расчёта; бюджет §6 замером; журнал air_model: поле (nn …) | 3–4 д |
| NN-11 | Проекция массы на CPU | dp-engineer | многосетка в GDExtension (если Ш3 решил «нужна») | div | |
| NN-12 | Сборка для полёта | dp-mechanic | сборки Linux/Windows с engine = "nn", скриншоты этапа загрузки и F3-срезов на 4 местах (tools/shots) | сборки, картинки в build/screenshots/air-nn/ | 0,5 д |
Шлюз Ш4: проверка окон. Шлюз Ш5: полёт пользователя (сеть против решателя на тех же стартах) → решение «удаляем решатель».
Волна 4 — удаление решателя и документы
| № | Задача | Агент | Скоуп, файлы | Приёмка | Оценка |
|---|---|---|---|---|---|
| NN-13 | Удалить рантайм-решатель | dp-engineer | файлы §1 «что уходит» по grep, GPU-тесты решателя, RenderingDevice для воздуха, конфиг engine; контракты C1/C3/C7/C8/C9 → N6 | все headless-тесты зелёные; grep по именам решателя пуст в scripts/; загрузка на машине без GPU-расчёта | 1–2 д |
| NN-14 | Прогон тестов и сборок после удаления | dp-mechanic | headless-тесты, tools/gpu_tests.sh, сборки | отчёт: зелёные/известные падения | 0,5 д |
| NN-15 | Документы | dp-writer | docs/guide/air-model.md (раздел поля — сеть, офлайн-решатель, границы модели: что сеть не умеет), tools/air_nn/README.md (пересчёт одной командой), страница для пилотов, дневник | по фактам из отчётов NN-0…NN-13 | 1–2 д |
Итого (оценка): ~9–11 недель (пилот — ~1 неделя календаря, волна 0 — ~2 недели) работы агентов + счёт набора фоном (недели; зависит от Ш1). Параллельно в каждой волне — ≤ 4 исполнителя. Пилот П-3 (план для координатора, 03.10.2026): физическая кодировка входа и выхода на готовых данных П-2 — docs/archive/plan/air-nn-p3.md (задачи П3-А…П3-Д, шлюз ШП-3).
9. Риски
- Охват «любая точка» — главный риск качества: рельеф/покров вне набора → гладкое неверное поле без запасного решателя. Меры: стратифицированная выборка суши, отложенные регионы, кривая обучения, ограничение входов; если сеть слаба в редком классе рельефа — дополнять набор именно им.
- Цена набора при честном решателе — недели GPU; ошибка в физике, найденная поздно, стоит пересчёта (неделя на часть набора — решение пользователя). Меры: шлюзы Ш1/Ш2 до массового счёта, версия набора = версия решателя.
- Сглаживание пиков (след, седловина, бровка) и ошибки w_conv → неверные источники термиков.
- Слабый ветер: если установившегося решения нет, «честное» среднее поле требует нестационарного счёта — дороже в разы (NN-1 оценит).
- Нативное расширение — новая зависимость сборки на трёх ОС; macOS без Mac.
- Отличия от нынешней игры (мельче сетка, покров, влажность) пилот увидит как «ветер стал другим» — это ожидаемо, но должно быть в отчёте NN-8 и в документах.
- Решатель «как игра» не зависит от высоты и широты места (NN-17, 03.10): ρ·cp и θ0 постоянны (air.py:38–40), f_cor — для 51° с. ш. (air.py:513; от него hbl, h_mech, z_sat), фон погоды — климат 50–56° с. ш. с опорой 3000 м (weather.py:155–170). Для мест выше ~3 км и вне 50–56° с. ш. поля не откалиброваны. В волну 1 (NN-1/NN-2): ρ(z), θ(z) в плавучести, f(lat).
10. Открытые вопросы пользователю
- Диск под данные: NVMe SSD 2 ТБ (минимум 1 ТБ, §4.3) — найдётся?
- Аренда GPU для набора (ускоряет недели счёта) — допустима? Если нет — счёт на 4070 SUPER фоном, сеть переобучается по мере роста набора.
- macOS: есть ли доступ к Mac для сборки и проверки расширения? Иначе — сборка через CI (GitHub Actions macOS) или macOS позже.
- Снег по сезону (альбедо 0,6–0,9 против 0,1–0,2 у леса) — в NN-2 вместе со светопоглощением (рекомендация: да, иначе зимой альбедо неверно) или отдельно (TODO «термики зимой»)?
Литература
- Achermann et al., 2024. WindSeer: real-time volumetric wind prediction over complex terrain aboard a small UAV. Nature Communications 15. https://www.nature.com/articles/s41467-024-47778-4 (код https://github.com/ethz-asl/WindSeer, BSD-3, весов нет; CFD-набор — ETH Research Collection, дымовая проверка конвейера обучения)
- Dujardin, Lehning, 2022. Wind-Topo: downscaling near-surface wind fields to high-resolution topography in highly complex terrain with deep learning. QJRMS. https://doi.org/10.1002/qj.4265
- Zhang et al., 2026. Transformer-based Neural Operators for 3D Wind Field Prediction over Complex Mountainous Terrain. arXiv:2605.25679. https://arxiv.org/abs/2605.25679
- Ronneberger et al., 2015. U-Net. https://arxiv.org/abs/1505.04597
- Thuerey et al., 2020. Deep learning methods for RANS simulations of airfoil flows (U-Net). AIAA J. https://arxiv.org/abs/1810.08217
- Li et al., 2021. Fourier Neural Operator for parametric PDEs. ICLR. https://arxiv.org/abs/2010.08895
- Gupta, Brandstetter, 2022. Towards multi-spatiotemporal-scale generalized PDE modeling (PDEArena: U-Net против FNO). https://arxiv.org/abs/2209.15616
- Akiba et al., 2019. Optuna: a next-generation hyperparameter optimization framework. KDD. https://arxiv.org/abs/1907.10902
- Lakshminarayanan et al., 2017. Simple and scalable predictive uncertainty estimation using deep ensembles. NeurIPS. https://arxiv.org/abs/1612.01474
- Roberts et al., 2017. Cross-validation strategies for data with temporal, spatial, hierarchical, or phylogenetic structure. Ecography 40. https://doi.org/10.1111/ecog.02881 (деление по блокам)
- Perez et al., 2018. FiLM: visual reasoning with a general conditioning layer. https://arxiv.org/abs/1709.07871
- Stull, 1988. An Introduction to Boundary Layer Meteorology (баланс энергии поверхности, отношение Боуэна, z0).
- Oke, 1987. Boundary Layer Climates (альбедо и Боуэн по типам поверхности).
- Zanaga et al., 2022. ESA WorldCover 10 m 2021 v200. https://doi.org/10.5281/zenodo.7254221 (CC BY 4.0)
- ONNX Runtime C API — https://onnxruntime.ai/docs/api/c/
- Godot: godot-onnx-loader — https://github.com/DynamicDevices/godot-onnx-loader; godot_onnx_extension — https://github.com/joemarshall/godot_onnx_extension