План: поле ветра на сетке, согласованное по массе, в compute-шейдерах
Статус: заменён планом docs/plan/air_model.md (три масштаба, среднее поле — Пикар); разделы этого документа переиспользуются по ссылкам оттуда. Было: план (29.09.2026), в работу не взят; кодинг — в отдельной ветке. Документ — для координатора агентов: модули, задачи со скоупом, ключевыми файлами, условиями приёмки и оценкой. Основа — исследование docs/research/slope_wind.md (§8 — слова пилота, §9 — математика, §10 — согласование с термиками и сетью). Пути к файлам проверены по коду на 29.09; новые файлы — предложение, исполнитель может уточнить.
Решения пользователя (не обсуждаются)
- Вариант B’ из §9: поле ветра, согласованное по массе, на трёхмерной сетке, считается в compute-шейдерах Godot (RenderingDevice, GLSL, как облака), не Rust/CUDA.
- Поле считается при загрузке места; пересчёт — только при смене места или устойчивости. Смена направления/силы ветра — без пересчёта (ранг-2, §9).
- Сеть: каждый клиент считает поле сам, полем не обмениваются; небольшое расхождение между видеокартами допустимо. Поле не входит в
world_key/world_hash. - Если расчёт может идти дольше 1 с — этап на экране загрузки «Рассчитываем ветер» / “Computing wind”, окно не замирает.
- Приёмка — слова пилота (§8):
При обтекании склона чем выше по склону и ближе к нему, тем ветер больше усиливается. Это усиление распространяется на 2 высоты горы от подножия. На 2,5 высоты горы уже ламинарное обтекание. Зависит также от крутизны склона — чем круче, тем больше прирастание силы ветра. В седловине ветер сильнее примерно в 1,5 раза, всегда вдоль седла, даже при косом до 15 градусов. Даже может сдуть за гору. При косом ветре другое обтекание, эффект меньше.
- Главное требование — физическая корректность в заявленных пределах модели (любая симуляция — модель с границами: в документах явно указывать, какие законы и приближения учтены и чего модель не умеет). Отзывы пилота — ориентир для проверки, не запрет: провалы и болтанка пилоту нравятся (§1.6) — после изменений сверить, что они остались правдоподобными, а не «не трогать».
Ответы пользователя на вопросы плана (29.09.2026) — меняют план, приоритет над текстом ниже
- Видеокарты пилотов неизвестны, AMD. WF-03 (многосеточный метод) — обязателен, не по условию. Шейдеры — только переносимый Vulkan (без расширений NVIDIA, осторожно с subgroup-операциями, проверить на Mesa/RADV или lavapipe, если есть); отдельный пункт в WF-11: Windows + AMD (у пилота уже были вылеты на Windows — «экран гаснет»).
- Клетка 50 м — через вложенные сетки-клипмапы вокруг пилота (идея пользователя: «делим пространство пополам, внутри — сеткой поменьше; глубина — по расстоянию от игрока»). Задача WF-14 (переписана 29.09):
- 4–5 уровней правильных 3D-сеток одного размера (например 64×64 столбца × 32 σ-уровня), клетка на каждом уровне вдвое крупнее, все с центром на пилоте:
уровень клетка покрывает 0 50 м 3,2 км 1 100 м 6,4 км 2 200 м 12,8 км 3 400 м 25,6 км 4 800 м 51 км (всё место) Всего ≈ 5 × 64×64×32 ≈ 0,65 млн клеток — в десятки раз меньше одной сетки 50 м на всё место; размер окна (64) и число уровней — в конфиге. - Решение сверху вниз: грубый уровень → граничные условия для следующего; обратная связь (мелкий поправляет грубый в зоне наложения — сохранение массы через стыки) — по результатам WF-07/08. Пирамида уровней служит и многосеточным ускорителем (WF-03).
- Сдвиг окон с пилотом, как LOD рельефа: когда пилот уходит от центра уровня на заданную долю окна, уровень сдвигается кратно своей клетке; пересчитывается только новая полоса и то, что она задевает, в фоне, без замирания; до готовности — данные старого/грубого уровня.
- Выборка: самый мелкий уровень, содержащий точку; у края уровня — плавный переход к более грубому; за краем самого грубого — аналитика. Боты и чужие пилоты далеко от игрока — по грубым уровням (достаточно). В сети окна у каждого свои — расхождение допустимо.
- При загрузке места считаются все уровни (стартовый центр — старт); «Рассчитываем ветер» на экране загрузки, если > 1 с (WF-06).
- На потом: дробление не только по расстоянию, но и по сложности рельефа (гребни, седловины) — адаптивное дерево (octree/AMR); на старте не делаем (сложнее для GPU: висячие узлы, неравномерная память).
- Ориентир размеров клетки — результаты прототипа клеточного автомата (docs/plan/heat_ca_prototype.md, исследование сходимости по клетке).
- 4–5 уровней правильных 3D-сеток одного размера (например 64×64 столбца × 32 σ-уровня), клетка на каждом уровне вдвое крупнее, все с центром на пилоте:
- Поле включать по умолчанию сразу (
wind_field.enabled = onпосле WF-08), не дожидаясь полёта пилота. - F3 — стрелки поля в игре (дополнение к WF-09): по F3 рисовать векторы ветра в узлах сетки вокруг пилота (радиус ~1–2 км, несколько уровней по высоте; цвет — вертикальная составляющая: подъём/опускание), повторное нажатие — выключить; без влияния на FPS при выключенном. Смотреть будет пользователь вместе с пилотом на своей машине — отладочная функция, но в обычной сборке по F3.
- Трава по полю (WF-10) — делать сейчас, если не нагружает видеокарту: приёмка — +≤ 0,2 мс GPU на «Высоком».
- Термики и облака на локальном ветре — оценить влияние (новая задача WF-13): расчёт на сетке объединяет модели погоды и ветра — проверить, что меняется, если снос/наклон термиков и облаков брать из поля (грубого), и как это влияет на совпадение мира в сети (поле чуть разное у клиентов — термики разойдутся?). Итог — замеры и рекомендация; внедрять, если расхождение в сети мало (например, снос по грубому полю с огрублением значений) и выигрыш заметен.
- Постоянно падающие тесты — удалены (в main до начала работ: test_ridge_climb_20kmh, test_strong_wind_launch, перебор test_input_launch с известным срывом); слабые старты оставить как есть, если поле их не вытянет.
- Связанная система ветер–термики–погода (docs/research/slope_wind.md §11) — в этот план входят:
- этап 1 (в WF-13, теперь внедрять, не только оценить): снос и наклон термика — из среднего по столбцу ветра грубого поля в точке источника на момент рождения, постоянный на всю жизнь термика (снос остаётся функцией времени,
start_atработает); облака, тени облаков на источниках, облачный шум, пыльные вихри — тем же ветром; сетка клеток термиков, «улицы» и выбор источников — на глобальном ветре (иначе клиенты сети получат разные термики). Приёмка: в седловине термик и облако уходят вдоль седла; провалы не меняются; детерминизм мира — отпечаток с выключенным полем, в сети — позиции термиков расходятся ≤ 20 м за 20 мин; - лёгкая часть этапа 3 (новая задача WF-15, ~1 день): вес вертикали (устойчивость) — по частоте Брента–Вяйсяля N из погодной модели на момент старта (интерполяция между готовыми уровнями, без пересчёта); мягкие обновления дня не должны замораживать N навсегда — пересчёт устойчивости при смене часа допустим через те же уровни.
- на потом (после полёта пилота на поле): инверсия как «крышка» сетки (утром/вечером сильнее «труба», нужен пересчёт) и этап 2 — термики как источники массы в уравнении Пуассона.
- этап 1 (в WF-13, теперь внедрять, не только оценить): снос и наклон термика — из среднего по столбцу ветра грубого поля в точке источника на момент рождения, постоянный на всю жизнь термика (снос остаётся функцией времени,
Цель
Заменить в Atmosphere.air_velocity_at две вещи — горизонтальный ветер (сейчас одно направление на весь мир, без разгона) и склоновый подъём w_ridge (сейчас V·∇h с затуханием 250 м) — на трёхмерное поле, которое само даёт разгон у бровки, толщину возмущения ~2 H, «трубу» в седловине с поворотом к её оси, ослабление при косом ветре и обтекание сопок сбоку. Всё остальное (подветренная эвристика, роторы, термики, фон, турбулентность, волны, грозы) — как сейчас.
Как это почувствует пилот
- На бровке дует сильнее, чем у подножия и перед склоном: против ветра идёшь медленнее, «встал на бровке».
- Полоса подъёма по размеру горы: у низкого узкого гребня — прижата к склону, у большой горы — высокая; на 2–2,5 высоты горы над подножием — ровный ветер без подъёма.
- Круче склон — сильнее разгон и подъём.
- Седловина / перевал (Каянча в Онгудае): ветер ≈ ×1,5, вдоль седла даже при ветре под 15° к нему; за седловиной не «тень», а поток, который может утянуть за гору.
- Косой ветер к склону — подъём и разгон слабее.
- Мелочь рельефа (опушки, лощинки 100 м) больше не бьёт провалами на 100 м над склоном (сетка 100 м и затухание с высотой — само).
- Провалы за гребнем, «ногами к тросам», болтанка — как сейчас.
Не делаем
- Срыв потока и роторы решателем — модель без инерции их не умеет; подветренная эвристика (
_lee_flow, линия тени,lee.*) остаётся поверх поля без изменений. - Термики и облака на локальном ветре — в этом плане нет. Они читают глобальный
WindModel(thermal_field.gd: update_wind_frame,_apply_wind;cloud_layer.gd) и обязаны совпадать у всех клиентов (ключ мира), а поле на разных видеокартах чуть разное. Как подать позже — см. «На потом». - В этом плане турбулентность, подветренное опускание и болтанку ротора не переделываем (считаются от ветра без разгона, §1.6) — не из запрета, а потому что поле по массе их не описывает; менять — отдельной задачей, если физически корректная модель этого потребует.
- Не делаем Rust/GDExtension, офлайн-CFD, нейросеть, запекание полей в данные игры.
- Не шлифуем: «несовершенство = реализм»; после калибровки — полёт пилота, не циклы доводки.
Архитектура
Сетка
| Параметр | Значение по умолчанию | Почему |
|---|---|---|
| Область | весь детальный квадрат локации: 40 × 40 км (встроенные — data/terrain/<id>/meta.json → layers[detail], по координатам — world.json → runtime_terrain.layers[detail].size_km 40) | один расчёт на место; пилот может улететь по маршруту куда угодно в детальном квадрате; пересчёта «при перелёте» нет |
| Горизонталь | 100 м → 400 × 400 столбцов (wind_field.cell_m) | склоны L ≥ 250–300 м — 3+ клетки; 50 м — в 4 раза дороже (решение пользователя — вопрос 2) |
| Вертикаль | 32 σ-уровня (wind_field.levels) по рельефу: σ = (z − h)/(z_top − h), толщина слоя растёт геометрически, первый слой ~10 м в долине | у земли — разрешение для профиля и подъёма у склона; наверху грубо — там возмущение гаснет |
| Потолок | z_top = max(h в области) + 3000 м (top_above_max_m) | у Онгудая (h 500–2500 м) — ~5,5 км; «2–2,5 H от подножия» у самой высокой горы ≈ 4 км над дном долины — внутри области |
| Высоты для сетки | средняя (box-фильтр) высота детального слоя 25 м по клетке 100 м — считается на GPU из загруженного слоя | без алиасинга; кроны DSM сглаживаются сами |
| Ячеек | 400 × 400 × 32 = 5,1 млн |
Память (5,1 млн ячеек, fp32 = 20,5 МБ на скаляр):
| Что | Где | Объём |
|---|---|---|
| Векторы PCG (λ, r, z, p, q) + коэффициенты столбцов | VRAM, на время расчёта | ~140 МБ |
| Уровни многосеточного метода (если WF-03) | VRAM, на время расчёта | +~50 МБ |
| Базисные поля: 2 направления × K = 3 уровня устойчивости, RGBA16F 3D-текстуры | VRAM, пока место загружено | 6 × 41 = ~250 МБ (или освобождать, см. keep_basis_on_gpu) |
| Поле для текущего ветра и устойчивости (3 × fp32) | RAM, PackedFloat32Array | ~61 МБ |
Пик VRAM ~450 МБ — помещается и на карту 2 ГБ; на 4070 SUPER (12 ГБ) — не вопрос.
Уравнения (кратко; точная схема — WF-01, docs/wind_field.md)
- Начальное поле u₀ — нынешний ветер без разгона: горизонтальный, направление базиса (восток или юг), модуль
profile(agl)(WindModel.profile, степенной закон от высоты над сглаженной землёй сетки), вертикаль 0. Множитель от высоты над морем (altitude_factor) иspeed_refв решатель не входят — поле безразмерное. - Поправка: u = u₀ + ∂λ/∂x, v = v₀ + ∂λ/∂y, w = w₀ + T·∂λ/∂z. T — «вес вертикали» (устойчивость): T = 1 — нейтрально (потенциальное обтекание), T < 1 — вертикальное смещение «дорого»: воздух обтекает сбоку и идёт в седловины.
- ∇·u = 0 → ∇·(W∇λ) = −∇·u₀, W = diag(1, 1, T).
- Дискретизация — конечные объёмы на σ-сетке, λ в центрах ячеек, потоки на гранях (сетка C), метрика рельефа — точно (перекрёстные члены x–σ, y–σ). Оператор строится как A = Dᵀ·W·D (D — дискретный градиент, Dᵀ — дивергенция) — симметричный положительно определённый при любой форме рельефа; это обязательное требование (иначе CG не сходится).
- Границы: земля — непротекание (полный поток через нижнюю грань = 0, т. е. нижняя грань из D и из −∇·u₀ исключается — условие Неймана); бока и потолок — λ = 0 (Дирихле, открытые: воздух входит и выходит, поле у края ≈ u₀).
- На выходе — (u, v, w) в центрах ячеек, делённые на |u₀| на этой высоте: G = (g_x, g_z, g_w) — безразмерная «добавка рельефа». Над ровным местом G = (направление ветра, 0).
Ранг-2 и устойчивость
- При фиксированном T поле линейно по u₀: для ветра «куда дует» ŵ = (dx, dz) (
WindModel.dir) G(ŵ) = dx·G_east + dz·G_south. Сила ветра — множитель (G безразмерное). Смена направления/силы ветра — без решателя. - Устойчивость: K = 3 уровня T (
wind_field.stability_T= [0,1; 0,3; 1,0], калибруется в WF-07), 2 решения на уровень = 6 решений на место. Между уровнями — линейно по log T. - T для полёта: число Фруда Fr = U/(N·H_loc), U — ветер прогноза на
wave.wind_reference_agl_m, N —weather.stability_n_per_s(то же, что берётwave_field.gd), H_loc — перепад высот (P90 − P10) в радиусе 10 км от старта; T = clamp(Fr², T_min, 1). Неустойчивый день → T = 1 (нейтрально; конвекция — термики). - SVD по уровням устойчивости не нужен: базисы хранятся на GPU, смешивание — один проход шейдера (ниже).
- По §10.3 ветер и N на полёт постоянны → смешивание делается один раз за полёт (при
set_weather/set_wind), не каждый кадр.
Решатель на GPU
- Локальный RenderingDevice (
RenderingServer.create_local_rendering_device()) — отдельная очередь, не мешает кадру и облакам (cloud_compositor_effect.gdберёт общий RD). Шейдеры — GLSL compute (.glslс#[compute]какRDShaderFileили сборка из текста, как в облаках — выбор WF-02). - Работа порциями:
submit()порции итераций (≤chunk_ms30 мс работы GPU) → на следующем кадреsync()и следующая порция. Главный поток не ждёт GPU, окно живое; на Windows нет срабатывания TDR (сброс драйвера при >2 с на один сабмит). Отдельный поток — только если порции по кадрам не хватит (проверить в WF-02, что локальный RD можно водить изThread). - Метод — PCG (сопряжённые градиенты с предобуславливателем), fp32:
- шаг 1 (WF-02): предобуславливатель — точное решение по столбцу (прогонка/алгоритм Томаса, один поток на столбец, 32 уровня): снимает главную трудность — анизотропию (слой 10 м против клетки 100 м, ×100 связь по вертикали). Ожидаемо 300–600 итераций до относительной невязки 1e-5;
- шаг 2 (WF-03, если не укладываемся в бюджет): предобуславливатель — V-цикл многосеточного метода с огрублением только по горизонтали (400 → 200 → … → 25, столбец целиком) и сглаживанием «красно-чёрные столбцы» (та же прогонка) — классика для атмосферных задач. Ожидаемо 15–30 итераций.
- Якоби/Гаусс–Зейдель без многосеточного — нет (тысячи итераций), прямые методы — нет (§9).
- Скалярные произведения — двухпроходная редукция деревом в фиксированном порядке, без атомиков на float → одинаковый результат при повторном расчёте на той же машине.
- Проверка сходимости — раз в 10 итераций (чтение 1 числа), не каждую.
- Бюджет времени (все 6 решений, при загрузке места):
| Видеокарта | Столбцы (шаг 1), оценка | МС-PCG (шаг 2), оценка | Цель |
|---|---|---|---|
| RTX 4070 SUPER (~500 ГБ/с) | ~0,3 с/решение → 1,5–3 с | ~40–80 мс/решение → <0,5 с | ≤ 3 с |
| Слабая (GTX 1050 / встроенная, 50–110 ГБ/с) | 5–20× дольше → 10–60 с | 0,3–0,8 с/решение → 2–5 с | ≤ 15 с |
Оценки — по пропускной способности памяти (~7 проходов по векторам на итерацию); замер — WF-02. Правило для координатора: если на 4070 SUPER время шага 1 × 10 > 15 с — делать WF-03. Если и так долго — адаптивно: первое решение дольше max_level_solve_s → считаем только уровень T текущего полёта (2 решения), остальные — при смене устойчивости.
Поле на CPU: air_velocity_at
air_velocity_at зовут на CPU физика (3 точки крыла × 120 Гц), боты, птицы, флюгер — поэтому поле читается из оперативной памяти, не из шейдера.
- После решения (и при смене ветра/устойчивости) — проход смешивания на GPU: G = Σ_уровни w_k·(dx·G_east,k + dz·G_south,k) → одна 3D-текстура → одно чтение в RAM (
texture_get_data, ~61 МБ, ~50–100 мс) →WindFieldна CPU. WindField.sample(pos) -> Vector3 (g_x, g_z, g_w): высота сглаженной земли сетки h_g билинейно (свой 2D-массив 400², не реальная земля 25 м — иначе на острых гребнях σ «съезжает»), σ = (y − h_g)/(z_top − h_g), номер уровня — аналитически из геометрической сетки (log), трилинейно 8 узлов × 3 канала. Ниже центра первого слоя — значение первого слоя (профиль у земли даётWindModel.profile, поле — только добавка). Цель — ≤ 6 мкс на вызов в GDScript.- Край области: в полосе
edge_blend_m2000 м к краю G плавно → (ŵ, 0) и w — к нынешнему аналитическомуw_ridge; за краем — нынешняя модель. - Ограничители (калибровка): |G_h| ≤
max_speedup2,2 (на обрывах поле без срыва «разгоняется» нефизично), |w| ≤ridge.max_lift_ms6; ручкиspeedup_gain,w_gain(G_h = ŵ + gain·(G_h − ŵ)) — 1,0 по умолчанию.
Как подключается в atmosphere.gd (правила §1.6):
u = wind.speed_at_pos(agl, y) # как сейчас: профиль × высота над морем, БЕЗ разгона
G = wind_field.sample(pos) # (ŵ, 0) если поля нет
h_vec = u · G.xz · (1 − lee·_lee_wind_red) # было: ŵ · u · (1 − lee·red)
w = fade·(термики, фон) + clamp(u · G.w) · (1 − lee) # G.w вместо w_ridge
lee, _lee_flow, mech, conv, rot, storm, wave — от u БЕЗ разгона, как сейчас
mean_wind_at(pos) = u · G.xz # тот же G, без шума и leemean_wind_atобязан получить тот же G (§10.2), иначеair − mean(порывистость травы вterrain_wind.gd, тестыtest_lee_rotor.gd:98,test_atmosphere.gd:252,test_cloud_suck.gd:91) начнёт ловить разгон как «болтанку».- Подветренная эвристика (линия тени по глобальному
ŵвGroundField) остаётся; за седловиной линия тени и так низкая (против ветра — само понижение), поток поля там сохраняется — «может сдуть за гору». Проверяется в WF-07. GroundFieldне меняется (линия тени, превышение гребня, высота под пилотом). Аналитическийw_ridgeостаётся как запасной путь и для края области.
Трава и шейдеры
- Сразу и бесплатно:
TerrainWindберётmean_wind_atпод камерой → трава у камеры получает разгон и поворот ветра. - Отдельно, по желанию (WF-10): 2D-текстура нижнего уровня G (400², RGBA16F) в
terrain_wind.gdshaderinc— трава на бровке гнётся сильнее, чем в долине, в одном кадре.
Когда пересчитывать
| Событие | Что делаем | Цена |
|---|---|---|
| Загрузка места (встроенного или по координатам) | 6 решений (2 × K) → смешивание → чтение в RAM | этап загрузки «Рассчитываем ветер» |
| Новый полёт на том же месте, другая погода/ветер/устойчивость | только смешивание + чтение | ~0,1 с |
Atmosphere.set_wind (направление, сила), set_weather | смешивание (если поменялись ŵ или T) | ~0,1 с, асинхронно; до готовности — старое поле |
Мягкие обновления дня (_apply_day) | ничего (ветер и N не меняются, §10.3) | 0 |
Смена cell_m/levels/stability_T в конфиге | пересчёт при следующей загрузке | — |
Поле живёт в Atmosphere и переживает configure() (как функции рельефа в GroundField); ключ — рельеф (id локации или lat/lon) + хэш параметров wind_field. Кеша на диске нет (расчёт — секунды).
Экран загрузки
Этап wind («Рассчитываем ветер…» / “Computing wind…”) в LoadProgress после этапа погоды; доля — по порциям решателя (sub(done, total), total = 6 решений × оценка итераций). Вес этапа — в configs/ui.json → loading.stage_weights. Показывается всегда, когда идёт расчёт (на быстрой карте мелькнёт на долю секунды).
Детерминизм и сеть
- Ключ мира не меняется. Поле — производное от рельефа и конфига, в
WorldKeyне входит,WorldKey.VERSIONне поднимаем,world_hashпрежний. Термики и облака поле не читают → «тот же мир» у всех, как сейчас. - Каждый клиент считает своё поле. Физика полёта клиент-авторитетна (
NetPilots.set_local_state, §10.4): разница поля влияет только на ощущение самого пилота. Ботов считает ведущий (send_bot_state) — боты летают в поле ведущего; при смене ведущего возможен незаметный разовый сдвиг. - Ожидаемое расхождение между видеокартами/драйверами: решатель сходится до относительной невязки 1e-5 в fp32 → |ΔG| ≲ 1e-3, т. е. ≤ 0,01 м/с при ветре 10 м/с. Оценка сверху — расхождение GPU с эталоном в float64 (WF-01/WF-02); доказывать на двух разных картах не требуется.
- Клиент без GPU-расчёта (нет Vulkan-compute, ошибка, таймаут) летает на аналитической модели — у него ощущение как сейчас. Для сети допустимо; в журнал — строка «wind_field: analytic (причина)».
- Одиночная игра и тесты детерминизма:
AtmoFingerprint.make_worldявно ставитwind_field.enabled = "off"→test_determinism.gdне зависит от видеокарты (и headless-прогона). Отдельный GPU-тест: два расчёта на одной машине совпадают побитно (допуск 1e-6); GPU против эталона — допуск 1e-3 от |u₀|. - Headless (
tools/check.sh,--headless) — RenderingDevice нет →enabled: "auto"даёт аналитику; все нынешние headless-тесты гоняют старую модель и не ломаются. Тесты поля — в отдельном GPU-прогоне (WF-00).
Запасной путь и конфиг
configs/atmosphere.json → wind_field (каждый ключ с _doc):
| Ключ | По умолчанию | Смысл |
|---|---|---|
enabled | "auto" | auto — GPU если есть, иначе аналитика; on — требовать (ошибка → аналитика + предупреждение); off — аналитика |
cell_m, levels, first_layer_m, top_above_max_m | 100, 32, 10, 3000 | сетка |
stability_T, T_min, fr_relief_radius_m | [0,1, 0,3, 1,0], 0,1, 10000 | устойчивость |
solver, tol, max_iter, chunk_ms | "line_pcg" (или "mg_pcg" после WF-03), 1e-5, 3000, 30 | решатель |
max_total_solve_s, max_level_solve_s | 30, 5 | таймауты: превышен общий → аналитика; превышен на уровень → только уровень полёта |
keep_basis_on_gpu | true | false — базисы освобождаются, смена ветра/устойчивости = пересчёт |
edge_blend_m, max_speedup, speedup_gain, w_gain | 2000, 2,2, 1,0, 1,0 | край и калибровка |
Аналитическая модель (ridge.*, _ridge_* в atmosphere.gd) остаётся без изменений и включается при off, без GPU, при ошибке сборки шейдера, при таймауте и за краем области. Для сравнения пилотом — отладочная клавиша «поле/аналитика» в полёте (только в отладочном режиме, WF-09).
Проверка
Синтетика (WF-01 эталон, WF-07 на GPU)
Рельеф — функции в тесте, ветер 5 м/с, T = 1 если не сказано иное.
- Ровное место: G = (ŵ, 0) с точностью 1e-4; поле = старому ветру (
air_velocity_atсовпадает с аналитикой без склона). - Сохранение массы: дискретная дивергенция после решения ≤ 1e-4 от |∇·u₀|; оператор симметричен (⟨Ax, y⟩ = ⟨x, Ay⟩ на случайных векторах, 1e-5).
- 2D-хребет Аньези (H = 300 м, L = 300 и 800 м) против потенциального обтекания (§1.5, таблица): w на наветренной стороне на 50–300 м AGL — в пределах ±25 %; прирост ветра над гребнем на 20–100 м — ±30 %; крутой > пологого («круче — сильнее»).
- Толщина слоя 2 H: уединённый хребет H = 500 м — на высоте 2 H над подножием разгон ≤ 10 %, |w| ≤ 0,1 м/с·(U/5); на 2,5 H — разгон ≤ 5 % («на 2,5 высоты — ламинарно»); максимум разгона — у бровки у земли («выше по склону и ближе — сильнее»).
- Седловина: две вершины H = 500 м, седловина глубиной 250 м и шириной ~1 км, ветер вдоль оси: скорость на 20–50 м над седловиной / скорость на той же AGL перед склоном = 1,5 ± 0,2 при T «обычного дня» (N = 0,01, U = 5 м/с; калибровка
stability_T— здесь); ветер под 15° к оси — направление потока в седловине на 20 м AGL ≤ 5° от оси; за седловиной на 50 м AGL поток ≥ 0,8 U («может сдуть за гору») иleeтам меньше, чем за вершинами. - Косой ветер: тот же хребет, ветер 0° и 45° к нормали: w(45°)/w(0°) = 0,55–0,8, разгон при 45° меньше, чем при 0°.
- Устойчивость: сопка H = 500 м, T = 0,1 против 1,0: при устойчивом воздухе больше обтекания сбоку (разгон на флангах выше, подъём на наветренном склоне ниже).
- Ранг-2: поле, решённое прямо для ветра 30°, совпадает с cos/sin-смесью базисов до 1e-4.
Реальные места (WF-08, GPU-прогон)
- Онгудай, перевал Каянча (седловина!): срезы поля, разгон на старте
kayancha_south, ветер в перевале. - Аушкуль
ridge_west(хребет ~100 м — предел разрешения 100 м) иaltai/tugaya_south: сейчас это слабые старты (WEAK_CLIMB_SITES);test_ridge_climb_20kmhуже падает наmain— WF-00 фиксирует исходный результат, WF-08 сравнивает до/после и решает судьбу списка слабых стартов (не «подгонять» тест). - Все 10 стартов (
test_ridge_starts.gd): набор за 5 мин при 20 км/ч — таблица до/после вdocs/guide/atmosphere.md; при 8 км/ч — набора нет; подветренная зона — опускание есть. test_lee_rotor.gd,test_atmosphere.gd,test_cloud_suck.gdв режиме поля — допуски пересмотреть только там, где разгон законно меняет числа;tools/flight/roll_sway.gd— раскачка по крену не хуже.
Производительность (WF-11)
- Загрузка: время этапа «Рассчитываем ветер» на 4070 SUPER ≤ 3 с (цель < 1 с с WF-03); оценка на слабую карту ≤ 15 с.
- Нет рывков кадра > 100 мс во время этапа.
air_velocity_at— не дороже нынешнего более чем на 30 % (замер 100 тыс. вызовов); FPS в полёте — без изменений (± 2 %).- VRAM и RAM — по таблице выше ± 20 %.
Визуально (WF-09)
- PNG-срезы: горизонтальный на 20 и 200 м AGL (цвет — разгон |G_h| − 1, стрелки — направление), вертикальный вдоль ветра через старт (цвет — w/U и разгон, изолинии σ) — для Каянчи,
ridge_west, синтетической седловины. - В игре (отладка): сетка стрелок над рельефом на 20/100/300 м AGL, цвет по разгону.
- Скриншоты — в
build/screenshotsс русскими номерными именами (правило проекта).
Пилот (WF-12)
Пилот летает на Каянче (перевал) и одном хребте с отладочной клавишей «поле/аналитика»; вопросы — простым русским текстом через пилота (раздел в questions.md). Итог — записать, не шлифовать.
Ветка и работа агентов
- Вся работа — в ветке
feature/wind-field, в отдельном git worktree~/deltaplan-wf(git worktree add ~/deltaplan-wf -b feature/wind-field main). Вmainничего не коммитить; слияние вmain— после приёмки пользователем (WF-12). - Один агент — одна задача WF-xx. Коммиты — только свои файлы (
git commit -- <пути>, индекс общий), сообщения на русском; коммитить на логичных шагах без вопросов. - Godot — всегда с временным
XDG_DATA_HOME(XDG_DATA_HOME=$(mktemp -d) godot …), профиль пилота не трогать. - GPU-тесты — не headless:
tools/gpu_tests.sh(WF-00). Headlesstools/check.shдолжен оставаться зелёным (кроме уже падающегоtest_ridge_climb_20kmh— пока WF-08 не решит). - Периодически
git merge mainв ветку; конфликты решает координатор. - Видимые изменения — со скриншотами (экран загрузки — ru/en).
CHANGELOG.md— функционально, при приёмке (WF-12).
Агенты: координатор и исполнители
Один координатор (К0, Opus): держит план, раздаёт задачи, принимает (тесты, срезы, замеры), решает «нужен ли WF-03», сливает main, приёмка у пользователя. Модели исполнителей: Opus по умолчанию, Sonnet — где помечено; повтор неудачной задачи — Fable с чёткой спецификацией и приёмкой.
| Задача | Модель | Дни |
|---|---|---|
| WF-00 База и GPU-прогон тестов | Sonnet | 1 |
| WF-01 Численная схема и эталон (numpy) | Opus | 2 |
| WF-02 Решатель на GPU (PCG, столбцы) | Opus | 3 |
| WF-03 Многосеточный предобуславливатель (обязателен, AMD) | Opus | 2–3 |
WF-04 WindField на CPU (хранение, выборка, смешивание) | Opus | 1,5 |
| WF-05 Подключение к атмосфере, конфиг, запасной путь | Opus | 2 |
| WF-06 Этап загрузки «Рассчитываем ветер» | Sonnet | 1 |
| WF-07 Синтетика на GPU и устойчивость | Opus | 2 |
| WF-08 Реальные места и калибровка | Opus | 2 |
| WF-09 Срезы, отладочные стрелки, скриншоты | Sonnet | 1,5 |
| WF-10 Поле для травы (сейчас, ≤ 0,2 мс GPU) | Sonnet | 1 |
| WF-13 Термики, облака, пыльные вихри на поле (этап 1 §11) | Opus | 2,5 |
| WF-15 Устойчивость по погоде (N → вес вертикали) | Sonnet | 1 |
| WF-16 Калибровка по схеме Professor (прогоны → полиномы → χ²) | Opus | 2 |
| WF-14 Вложенные сетки-клипмапы: 4–5 уровней ×2 вокруг пилота, сдвиг окон | Opus | 4 |
| WF-11 Производительность, отказоустойчивость, сеть, Windows | Opus | 1,5 |
| WF-12 Полёт пилота, документация, слияние | К0 + Sonnet | 1 |
| Итого | 1 координатор, 16–17 исполнителей | ~31 (WF-03, WF-10, WF-13…WF-16 — в объёме) |
Волны (одновременно — не больше ~4 исполнителей):
- WF-00, WF-01.
- WF-02, WF-04 (на массивах-заготовках из WF-01).
- WF-05, WF-06 (после API WF-02/WF-04); WF-03 — если замер WF-02 требует.
- WF-07, WF-09.
- WF-08, WF-11, WF-10 (по желанию).
- WF-12.
game.gd правит только WF-06; atmosphere.gd — только WF-05 (остальные — через него или по очереди через К0).
Порядок и зависимости
WF-00 База ─────────────────────────────────────────────┐
WF-01 Схема+эталон ─┬─► WF-02 GPU-решатель ─┬─► WF-03 МС (по условию)
│ ├─► WF-05 Атмосфера ─┬─► WF-07 Синтетика ─► WF-08 Места ─┐
└─► WF-04 WindField ────┘ WF-06 Загрузка ──┤ WF-09 Срезы ──────────────────┤
└─► WF-11 Произв./сеть ──────────┼─► WF-12
WF-10 Трава (опц.) ──────────┘M0. Подготовка
WF-00. База, worktree и GPU-прогон тестов (1 день, Sonnet)
- Скоуп: worktree
~/deltaplan-wf, веткаfeature/wind-field. Записать исходное состояниеmain: полныйtools/check.sh --no-build(какие тесты падают — ожидаемоtest_ridge_climb_20kmh), таблица набораtest_ridge_starts, замерair_velocity_at(100 тыс. вызовов на Каянче, мкс/вызов), FPS в полёте. Скриптtools/gpu_tests.sh: Godot не headless (--audio-driver Dummy, маленькое окно),res://tests/run_tests.tscn -- --filter=<…>+ флаг, по которому тесты поля не пропускаются; без RenderingDevice тесты поля помечаются «пропущено», не «упало». - Ключевые файлы:
tools/gpu_tests.sh(новый),tests/run_tests.gd(пропуск GPU-тестов при отсутствии RD),docs/plan/wind_field_baseline.md(новый, цифры). - Приёмка: базовые цифры записаны; пустой GPU-тест проходит в
tools/gpu_tests.shи «пропущен» в headlesstools/check.sh.
M1. Численная схема и эталон
WF-01. Схема на σ-сетке и эталон на numpy (2 дня, Opus)
- Скоуп: записать точную дискретизацию (σ-сетка с геометрическим растяжением, сетка C, метрика рельефа, A = Dᵀ W D, границы, u₀, нормировка G, формула выбора T по Fr) в
docs/wind_field.md. Эталон на Python/numpy (без scipy — его нет): матрично-свободный PCG с прогонкой по столбцам, float64, сетки до ~128 × 128 × 24. Синтетические рельефы (ровно, хребет Аньези, сопка, седловина) и эталонные выходы — маленькие двоичные файлы для тестов GPU. - Ключевые файлы:
docs/wind_field.md,tools/wind_field/mc_reference.py,tools/wind_field/cases.py,tests/atmosphere/fixtures/wind_field/*.bin(+.jsonс метаданными). - Приёмка: на эталоне выполняются проверки синтетики 1–8 (кроме «на GPU»): ровно → G = (ŵ, 0); дивергенция ≤ 1e-4; симметрия; хребет Аньези против потенциального обтекания в допусках; седловина и 2 H — числа в отчёте (подсказка для калибровки T); прогон
python3 tools/wind_field/mc_reference.py --case all≤ 2 мин.
M2. Решатель на GPU
WF-02. PCG с предобуславливателем по столбцам на RenderingDevice (3 дня, Opus)
- Скоуп:
WindFieldSolver: локальный RD; загрузка детального слоя высот в 2D-текстуру и сглаживание до сетки на GPU; шейдеры: правая часть −∇·u₀, применение A, прогонка по столбцам, axpy, редукция скалярных произведений деревом (без атомиков), выход G и смешивание базисов в 3D-текстуру RGBA16F/RGBA32F; PCG-цикл порциями ≤chunk_ms(submit → sync на следующем кадре; главный поток не ждёт); API:start(heights, meta, cfg),poll() -> прогресс,is_done(),combine(dir, T) -> PackedFloat32Array(асинхронно),free(); сигнал ошибки (нет RD, ошибка компиляции, таймаут). Замер времени на Каянче (все 6 решений) и числа итераций. - Ключевые файлы:
scripts/atmosphere/wind_field_solver.gd(новый),scripts/atmosphere/wind_field/*.glsl(новые),tests/atmosphere/test_wind_field_gpu.gd(новый; GPU-прогон). - Приёмка: на всех заготовках WF-01 результат совпадает с эталоном ≤ 1e-3 от |u₀|; два расчёта подряд совпадают побитно; оператор симметричен (тест); Каянча, 400 × 400 × 32: сходится до 1e-5, время и итерации в отчёте; ни одна порция не дольше 50 мс по GPU-таймстемпам; без RD — понятная ошибка, не падение.
WF-03. Многосеточный предобуславливатель (2–3 дня, Opus; делать, если время WF-02 × 10 > 15 с)
- Скоуп: V-цикл с огрублением только по горизонтали до ~25 × 25 столбцов, сглаживание — красно-чёрная прогонка по столбцам (1–2 прохода), ограничение/продолжение — усреднение/билинейно, на грубом уровне — несколько сглаживаний; тот же PCG снаружи (
solver: "mg_pcg"). - Ключевые файлы:
scripts/atmosphere/wind_field_solver.gd,scripts/atmosphere/wind_field/mg_*.glsl. - Приёмка: те же тесты, что WF-02; на Каянче ≤ 30 итераций до 1e-5 и ≤ 150 мс на решение на 4070 SUPER; прирост VRAM ≤ 60 МБ.
M3. Поле в игре
WF-04. WindField на CPU: хранение, выборка, край (1,5 дня, Opus)
- Скоуп: класс с метаданными сетки (начало, клетка, z_top, растяжение σ), 2D-массив h_g и 3D-массив G (3 × fp32);
sample(pos) -> Vector3(как в разделе «Поле на CPU»), полоса края, ограничители,contains(pos); построение из массивов (заготовки WF-01 или чтение с GPU) — тестируется headless без GPU. - Ключевые файлы:
scripts/atmosphere/wind_field.gd(новый),tests/atmosphere/test_wind_field_sample.gd(новый, headless). - Приёмка: на заготовках выборка совпадает с эталоном в узлах (1e-6) и гладкая между ними (нет скачков на границах уровней/клеток); ниже первого слоя и за краем — как описано; ≤ 6 мкс на вызов (100 тыс. вызовов в тесте, число в отчёте).
WF-05. Подключение к Atmosphere, конфиг, запасной путь (2 дня, Opus)
- Скоуп:
wind_fieldвconfigs/atmosphere.jsonс_doc;Atmosphere: поле переживаетconfigure(),build_wind_field(terrain)(асинхронно, сигнал готовности), смешивание приset_wind/set_weather(выбор T по Fr),air_velocity_atиmean_wind_atпо схеме выше; приoff/без RD/ошибке/за краем — аналитика;AtmoFingerprint.make_world—enabled: "off"; строка в журнал о режиме.calm_air.gd— без изменений. - Ключевые файлы:
scripts/atmosphere/atmosphere.gd,scripts/atmosphere/atmo_fingerprint.gd,configs/atmosphere.json,docs/guide/atmosphere.md(раздел «Поле ветра»). - Приёмка: headless
tools/check.sh— те же результаты, что в базе WF-00 (аналитика не изменилась ни на бит:test_determinismзелёный); с полем над ровным местомair_velocity_at= аналитике без склона (1e-4);mean_wind_atиair_velocity_atсогласованы (air − meanпри выключенной турбулентности и вне lee = только вертикаль);world_hashдляAtmoFingerprint.DEFAULT_KEYпрежний.
WF-06. Этап загрузки «Рассчитываем ветер» (1 день, Sonnet)
- Скоуп: этап
windвLoadProgress(послеweatherвGame, доobjects), текст ru «Рассчитываем ветер…» / en “Computing wind…”, доля —sub()по прогрессу решателя, вес вloading.stage_weights; ожидание —awaitпо кадрам, главный поток не блокируется; отмена загрузки (выход в меню) — решатель останавливается и освобождает GPU; то же место повторно — этап пропускается (поле в памяти). - Ключевые файлы:
scripts/game/game.gd,scripts/core/load_progress.gd(если нужно),scripts/ui/loading_screen.gd,configs/ui.json,locale/ui.csv,tools/loading/load_probe.gd(время этапа). - Приёмка: скриншоты экрана загрузки ru/en на этапе «Рассчитываем ветер»; замер: за время этапа нет кадра > 100 мс (лог длительностей кадров); повторный полёт на том же месте — без этапа; отмена во время этапа — без ошибок и утечек RID.
M4. Физика и калибровка
WF-07. Синтетика на GPU и устойчивость (2 дня, Opus)
- Скоуп: проверки синтетики 1–8 на GPU через
Atmosphere(не только решатель): разгон, 2 H / 2,5 H, круче — сильнее, седловина ×1,5 и поворот к оси при 15°, «сдуть за гору» вместе сlee, косой ветер, устойчивость; калибровкаstability_T,T_min, формулы T(Fr) по седловине;max_speedup. - Ключевые файлы:
tests/atmosphere/test_wind_field_pilot_words.gd(новый; имена проверок — словами пилота, какtest_pilot_words.gd),configs/atmosphere.json. - Приёмка: все проверки 1–8 зелёные в
tools/gpu_tests.sh; числа — вdocs/guide/atmosphere.mdтаблицей «слова пилота → что в модели».
Для модели воздуха (
air_model.md, AM-09) действуют правила калибровки пользователя от 29.09.2026: слова пилота в χ² не входят (только сравнение после), систематика сетки 15–20 % у бровки, известные из литературы параметры не подгоняются.
WF-16. Систематическая калибровка по схеме Professor (идея пользователя, 29.09; инструмент не важен — важна идея; ~2 дня, Opus; вместо «подбора на глаз» в WF-08)
- Скоуп: немного параметров с физическим смыслом и физическими диапазонами (≤ ~10: поток тепла от земли, перемешивание/диффузия, веса устойчивости, калибровочный множитель подхода, запас над рельефом и т. п.); числовые наблюдаемые с погрешностями вместо впечатлений: толщина полосы подъёма ≈ 2 H от подножия (пилот), разгон в седловине ×1,5 (пилот), ослабление при косом ветре, разгон на бровке (Askervein/Taylor–Lee), скорости подъёма в термиках по силе дня и высота подъёма/основание облаков (литература, зондирования, записи вариометра); у слов пилота — большая погрешность.
- Метод (как Professor в HEP): сетка прогонов модели по пространству параметров (сотни прогонов на GPU) → полиномиальная аппроксимация каждой наблюдаемой как функции параметров (быстрая замена модели) → минимум χ² против эталонов с погрешностями → неопределённости и корреляции параметров («eigentunes»), какие параметры данными не определены, какие наблюдаемые тянут в разные стороны (сигнал, где модель физически не дотягивает — не повод крутить дальше). Инструмент — professor2, iminuit/scipy + numpy или иное: не суть.
- Правило: параметров заметно меньше, чем независимых наблюдаемых (иначе переобучение); параметры без физического смысла не вводить; проверка сходимости (результат не зависит от плотности сетки прогонов и степени полинома в разумных пределах).
- Ключевые файлы: tools/research/tune/ (скрипты прогонов, аппроксимации, минимизации), docs/wind_field_tune.md (наблюдаемые, эталоны, погрешности, результат).
- Приёмка: таблица параметров с неопределённостями; χ²/ndf и вклад каждой наблюдаемой; график сходимости; значения внесены в конфиг; повторный прогон с найденными параметрами воспроизводит ориентиры в пределах погрешностей.
WF-08. Реальные места и калибровка (2 дня, Opus)
- Скоуп:
test_ridge_starts,test_lee_rotor,test_atmosphere,test_cloud_suckв режиме поля (GPU-прогон),tools/flight/roll_sway.gd; таблица набора на 10 стартах до/после; Каянча иridge_west— разбор (разрешение 100 м против 50 м дляridge_west— одним прогоном сcell_m50, вывод для вопроса 2);speedup_gain/w_gain— только если без них хуже слов пилота; решение поWEAK_CLIMB_SITESи падающемуtest_ridge_climb_20kmh(обосновать в отчёте, не подгонять). - Ключевые файлы:
tests/atmosphere/test_ridge_starts.gd,tests/atmosphere/test_lee_rotor.gd,configs/atmosphere.json,docs/guide/atmosphere.md(«Склон на стартах»). - Приёмка: ≥ 8 из 10 стартов набирают ≥ 50 м за 5 мин при 20 км/ч (как сейчас), при 8 км/ч набора нет, подветренное опускание есть на всех; провалы: СКО вертикали и частота рывков в подветренной зоне — в пределах ±10 % от базы WF-00 (§1.6); таблица в документации.
M5. Визуализация
WF-09. Срезы поля, отладочные стрелки, скриншоты (1,5 дня, Sonnet)
- Скоуп: инструмент PNG-срезов (горизонтальные 20/200 м AGL, вертикальный вдоль ветра через точку; цвет — разгон и w/U); в игре — отладочный слой стрелок над рельефом и клавиша «поле/аналитика» (только в отладке,
configs/controls.json); скриншоты Каянчи,ridge_west, синтетической седловины. - Ключевые файлы:
tools/wind_field/dump_slices.gd+.tscn(новые),scripts/atmosphere/wind_field_debug.gd(новый),configs/controls.json,tools/shots/wind_field_shot.gd(новый). - Приёмка: срезы и скриншоты в
build/screenshots(номерные русские имена); на срезе Каянчи виден разгон в перевале и над бровкой и спад к ~2 H.
WF-10. Поле для травы (1 день, Sonnet; по желанию, после согласия пользователя)
- Скоуп: 2D-текстура нижнего уровня G в
terrain_wind.gdshaderinc(множитель ветра и поворот в каждой точке),TerrainWindпередаёт её материалам. - Ключевые файлы:
scripts/terrain/terrain_wind.gd,scripts/terrain/terrain_wind.gdshaderinc, материалы травы. - Приёмка: скриншот — трава на бровке гнётся сильнее, чем в долине, в одном кадре; FPS ± 1 %.
M6. Надёжность и выпуск
WF-11. Производительность, отказы, сеть, Windows (1,5 дня, Opus)
- Скоуп: замеры из раздела «Производительность»; отказы: нет RD (headless), ошибка компиляции, таймаут
max_total_solve_s, нехватка VRAM (keep_basis_on_gpu: false), отмена загрузки — везде аналитика и строка в журнал, без падения; сеть: два клиента (встроенный сервер, одна машина) — оба на поле,world_hashсовпадает, отпечаток термиков/облаков совпадает; клиент сenabled: "off"в той же зоне — работает; Windows-сборка — расчёт на Vulkan, без TDR (проверяет пользователь, инструкция в отчёте). - Ключевые файлы:
scripts/atmosphere/wind_field_solver.gd,tools/bench/(замерair_velocity_atи этапа),tests/net/(если нужен тест). - Приёмка: цифры производительности в пределах бюджета (или решение К0 по WF-03); все отказы проверены тестом или вручную с логом; сетевой прогон — отчёт.
WF-12. Полёт пилота, документация, слияние (1 день, К0 + Sonnet)
- Скоуп: сборка для пилота с отладочной клавишей «поле/аналитика»; файл вопросов простым русским языком (что изменилось, что попробовать: Каянча, бровка, косой ветер, седловина; провалы — как были?);
docs/guide/atmosphere.md,CHANGELOG.md; скриншоты вbuild/screenshots. - Ключевые файлы:
questions.md(новый раздел, как принято в проекте),docs/guide/atmosphere.md,CHANGELOG.md. - Приёмка: пилот слетал, ответ записан в
docs/research/slope_wind.md(§11); пользователь принял;feature/wind-fieldслита вmain.
Оценка (дни работы агента)
| Модуль | Дни |
|---|---|
| M0 Подготовка | 1 |
| M1 Схема и эталон | 2 |
| M2 Решатель на GPU (+ многосеточный по условию) | 3 (+2–3) |
| M3 Поле в игре | 4,5 |
| M4 Физика и калибровка | 4 |
| M5 Визуализация (+ трава по желанию) | 1,5 (+1) |
| M6 Надёжность и выпуск | 2,5 |
| Итого | ~31 |
Минимальная версия (WF-00…02, 04…08, 12; без многосеточного, травы и отладочных стрелок — только PNG-срезы) — ~14 дней; годится, если на 4070 SUPER укладываемся в 3 с, а слабой карты у пилотов нет.
Риски
- Схема на σ-сетке (перекрёстные члены метрики, симметрия A) — самое трудное место; поэтому сначала эталон на numpy (WF-01), GPU сверяется с ним.
- Сходимость в fp32 на 5 млн неизвестных: невязка может «застрять» ~1e-5. Достаточно для 0,01 м/с; если нет — остаток считать в fp32 с переcчётом r = b − Aλ раз в 50 итераций.
- Слабая видеокарта / Windows TDR: без WF-03 расчёт на слабой карте — десятки секунд; порции ≤ 30 мс обязательны.
- Локальный RenderingDevice: поведение с потоками и на Windows не проверено в проекте — WF-02 начинает с минимального примера; порции по кадрам на главном потоке — надёжный путь.
- Ощущение на стартах может сдвинуться (набор, полоса подъёма); пилот доволен нынешними провалами — WF-08 сравнивает с базой, отладочная клавиша «поле/аналитика» — для пилота.
- Разрешение 100 м мало для хребта ~100 м (Аушкуль): там поле слабее реального; 50 м — ×4 по времени и памяти (вопрос 2).
- Обрывы: поле без срыва даёт на крутых бровках нефизичный разгон и вертикаль — ограничители
max_speedup,max_lift_ms; подветренная эвристика остаётся главной за гребнем. - Двойное опускание за гребнем: поле само даёт нисходящий поток на подветренном склоне, плюс
lee— как и сейчас (w_ridgeтоже был отрицательным там и тоже умножался на1 − lee); проверяется в WF-08 (±10 % к базе). - Headless-тесты не видят поле — нужна дисциплина GPU-прогона (
tools/gpu_tests.sh) перед приёмкой каждой задачи.
Вопросы пользователю
- Какие видеокарты у пилотов (модель, Windows/Linux)? От этого зависит, нужен ли многосеточный метод (WF-03) и адаптивный режим.
- Клетка 100 м или 50 м? 100 м — по умолчанию (быстро); хребет Аушкуля (~100 м) будет «смазан». WF-08 покажет разницу на
ridge_west, решить после него? - Включать поле по умолчанию сразу после WF-08 или только после полёта пилота (до того —
enabled: "off"в сборке)? - Клавиша «поле/аналитика» для пилота в сборке для проверки — годится (только в отладочной сборке)?
- Трава по полю (WF-10) — нужна сейчас или потом?
- Термики и облака по локальному ветру — нужны вообще? Сейчас они на общем ветре (так проще и совпадает у всех в сети); см. «На потом».
- Слабые старты (
tugaya_south,ridge_west, падающийtest_ridge_climb_20kmh): если поле их не вытянет — оставить слабыми (честно: в жизни работают только в сильный день)?
На потом
- Термики/облака на локальном ветре: снос и наклон термика — по среднему по столбцу G в точке источника, квантованному грубо (направление до 5°, сила до 10 %) с гистерезисом, чтобы у всех клиентов совпадало несмотря на разницу видеокарт; или отдельное грубое поле 1 км на CPU (детерминированно). Только после жалобы пилота «термик висит не там».
- Кеш базисов на диске (если на слабых картах загрузка долгая).
- Поле 50 м вокруг стартов (вложенная сетка) при сохранении 100 м на весь квадрат.
- Поле для точек с карты за пределами детального квадрата (дальний слой 100 м) — если будут маршруты длиннее 40 км.