План: поле ветра на сетке, согласованное по массе, в 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) — меняют план, приоритет над текстом ниже

  1. Видеокарты пилотов неизвестны, AMD. WF-03 (многосеточный метод) — обязателен, не по условию. Шейдеры — только переносимый Vulkan (без расширений NVIDIA, осторожно с subgroup-операциями, проверить на Mesa/RADV или lavapipe, если есть); отдельный пункт в WF-11: Windows + AMD (у пилота уже были вылеты на Windows — «экран гаснет»).
  2. Клетка 50 м — через вложенные сетки-клипмапы вокруг пилота (идея пользователя: «делим пространство пополам, внутри — сеткой поменьше; глубина — по расстоянию от игрока»). Задача WF-14 (переписана 29.09):
    • 4–5 уровней правильных 3D-сеток одного размера (например 64×64 столбца × 32 σ-уровня), клетка на каждом уровне вдвое крупнее, все с центром на пилоте:
      уровеньклеткапокрывает
      050 м3,2 км
      1100 м6,4 км
      2200 м12,8 км
      3400 м25,6 км
      4800 м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, исследование сходимости по клетке).
  3. Поле включать по умолчанию сразу (wind_field.enabled = on после WF-08), не дожидаясь полёта пилота.
  4. F3 — стрелки поля в игре (дополнение к WF-09): по F3 рисовать векторы ветра в узлах сетки вокруг пилота (радиус ~1–2 км, несколько уровней по высоте; цвет — вертикальная составляющая: подъём/опускание), повторное нажатие — выключить; без влияния на FPS при выключенном. Смотреть будет пользователь вместе с пилотом на своей машине — отладочная функция, но в обычной сборке по F3.
  5. Трава по полю (WF-10) — делать сейчас, если не нагружает видеокарту: приёмка — +≤ 0,2 мс GPU на «Высоком».
  6. Термики и облака на локальном ветре — оценить влияние (новая задача WF-13): расчёт на сетке объединяет модели погоды и ветра — проверить, что меняется, если снос/наклон термиков и облаков брать из поля (грубого), и как это влияет на совпадение мира в сети (поле чуть разное у клиентов — термики разойдутся?). Итог — замеры и рекомендация; внедрять, если расхождение в сети мало (например, снос по грубому полю с огрублением значений) и выигрыш заметен.
  7. Постоянно падающие тесты — удалены (в main до начала работ: test_ridge_climb_20kmh, test_strong_wind_launch, перебор test_input_launch с известным срывом); слабые старты оставить как есть, если поле их не вытянет.
  8. Связанная система ветер–термики–погода (docs/research/slope_wind.md §11) — в этот план входят:
    • этап 1 (в WF-13, теперь внедрять, не только оценить): снос и наклон термика — из среднего по столбцу ветра грубого поля в точке источника на момент рождения, постоянный на всю жизнь термика (снос остаётся функцией времени, start_at работает); облака, тени облаков на источниках, облачный шум, пыльные вихри — тем же ветром; сетка клеток термиков, «улицы» и выбор источников — на глобальном ветре (иначе клиенты сети получат разные термики). Приёмка: в седловине термик и облако уходят вдоль седла; провалы не меняются; детерминизм мира — отпечаток с выключенным полем, в сети — позиции термиков расходятся ≤ 20 м за 20 мин;
    • лёгкая часть этапа 3 (новая задача WF-15, ~1 день): вес вертикали (устойчивость) — по частоте Брента–Вяйсяля N из погодной модели на момент старта (интерполяция между готовыми уровнями, без пересчёта); мягкие обновления дня не должны замораживать N навсегда — пересчёт устойчивости при смене часа допустим через те же уровни.
    • на потом (после полёта пилота на поле): инверсия как «крышка» сетки (утром/вечером сильнее «труба», нужен пересчёт) и этап 2 — термики как источники массы в уравнении Пуассона.

Цель

Заменить в 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)

  1. Начальное поле u₀ — нынешний ветер без разгона: горизонтальный, направление базиса (восток или юг), модуль profile(agl) (WindModel.profile, степенной закон от высоты над сглаженной землёй сетки), вертикаль 0. Множитель от высоты над морем (altitude_factor) и speed_ref в решатель не входят — поле безразмерное.
  2. Поправка: u = u₀ + ∂λ/∂x, v = v₀ + ∂λ/∂y, w = w₀ + T·∂λ/∂z. T — «вес вертикали» (устойчивость): T = 1 — нейтрально (потенциальное обтекание), T < 1 — вертикальное смещение «дорого»: воздух обтекает сбоку и идёт в седловины.
  3. ∇·u = 0 → ∇·(W∇λ) = −∇·u₀, W = diag(1, 1, T).
  4. Дискретизация — конечные объёмы на σ-сетке, λ в центрах ячеек, потоки на гранях (сетка C), метрика рельефа — точно (перекрёстные члены x–σ, y–σ). Оператор строится как A = Dᵀ·W·D (D — дискретный градиент, Dᵀ — дивергенция) — симметричный положительно определённый при любой форме рельефа; это обязательное требование (иначе CG не сходится).
  5. Границы: земля — непротекание (полный поток через нижнюю грань = 0, т. е. нижняя грань из D и из −∇·u₀ исключается — условие Неймана); бока и потолок — λ = 0 (Дирихле, открытые: воздух входит и выходит, поле у края ≈ u₀).
  6. На выходе — (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_ms 30 мс работы 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_m 2000 м к краю G плавно → (ŵ, 0) и w — к нынешнему аналитическому w_ridge; за краем — нынешняя модель.
  • Ограничители (калибровка): |G_h| ≤ max_speedup 2,2 (на обрывах поле без срыва «разгоняется» нефизично), |w| ≤ ridge.max_lift_ms 6; ручки 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, без шума и lee
  • mean_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_m100, 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_s30, 5таймауты: превышен общий → аналитика; превышен на уровень → только уровень полёта
keep_basis_on_gputruefalse — базисы освобождаются, смена ветра/устойчивости = пересчёт
edge_blend_m, max_speedup, speedup_gain, w_gain2000, 2,2, 1,0, 1,0край и калибровка

Аналитическая модель (ridge.*, _ridge_* в atmosphere.gd) остаётся без изменений и включается при off, без GPU, при ошибке сборки шейдера, при таймауте и за краем области. Для сравнения пилотом — отладочная клавиша «поле/аналитика» в полёте (только в отладочном режиме, WF-09).


Проверка

Синтетика (WF-01 эталон, WF-07 на GPU)

Рельеф — функции в тесте, ветер 5 м/с, T = 1 если не сказано иное.

  1. Ровное место: G = (ŵ, 0) с точностью 1e-4; поле = старому ветру (air_velocity_at совпадает с аналитикой без склона).
  2. Сохранение массы: дискретная дивергенция после решения ≤ 1e-4 от |∇·u₀|; оператор симметричен (⟨Ax, y⟩ = ⟨x, Ay⟩ на случайных векторах, 1e-5).
  3. 2D-хребет Аньези (H = 300 м, L = 300 и 800 м) против потенциального обтекания (§1.5, таблица): w на наветренной стороне на 50–300 м AGL — в пределах ±25 %; прирост ветра над гребнем на 20–100 м — ±30 %; крутой > пологого («круче — сильнее»).
  4. Толщина слоя 2 H: уединённый хребет H = 500 м — на высоте 2 H над подножием разгон ≤ 10 %, |w| ≤ 0,1 м/с·(U/5); на 2,5 H — разгон ≤ 5 % («на 2,5 высоты — ламинарно»); максимум разгона — у бровки у земли («выше по склону и ближе — сильнее»).
  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 там меньше, чем за вершинами.
  6. Косой ветер: тот же хребет, ветер 0° и 45° к нормали: w(45°)/w(0°) = 0,55–0,8, разгон при 45° меньше, чем при 0°.
  7. Устойчивость: сопка H = 500 м, T = 0,1 против 1,0: при устойчивом воздухе больше обтекания сбоку (разгон на флангах выше, подъём на наветренном склоне ниже).
  8. Ранг-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). Headless tools/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-прогон тестовSonnet1
WF-01 Численная схема и эталон (numpy)Opus2
WF-02 Решатель на GPU (PCG, столбцы)Opus3
WF-03 Многосеточный предобуславливатель (обязателен, AMD)Opus2–3
WF-04 WindField на CPU (хранение, выборка, смешивание)Opus1,5
WF-05 Подключение к атмосфере, конфиг, запасной путьOpus2
WF-06 Этап загрузки «Рассчитываем ветер»Sonnet1
WF-07 Синтетика на GPU и устойчивостьOpus2
WF-08 Реальные места и калибровкаOpus2
WF-09 Срезы, отладочные стрелки, скриншотыSonnet1,5
WF-10 Поле для травы (сейчас, ≤ 0,2 мс GPU)Sonnet1
WF-13 Термики, облака, пыльные вихри на поле (этап 1 §11)Opus2,5
WF-15 Устойчивость по погоде (N → вес вертикали)Sonnet1
WF-16 Калибровка по схеме Professor (прогоны → полиномы → χ²)Opus2
WF-14 Вложенные сетки-клипмапы: 4–5 уровней ×2 вокруг пилота, сдвиг оконOpus4
WF-11 Производительность, отказоустойчивость, сеть, WindowsOpus1,5
WF-12 Полёт пилота, документация, слияниеК0 + Sonnet1
Итого1 координатор, 16–17 исполнителей~31 (WF-03, WF-10, WF-13…WF-16 — в объёме)

Волны (одновременно — не больше ~4 исполнителей):

  1. WF-00, WF-01.
  2. WF-02, WF-04 (на массивах-заготовках из WF-01).
  3. WF-05, WF-06 (после API WF-02/WF-04); WF-03 — если замер WF-02 требует.
  4. WF-07, WF-09.
  5. WF-08, WF-11, WF-10 (по желанию).
  6. 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 и «пропущен» в headless tools/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_m 50, вывод для вопроса 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) перед приёмкой каждой задачи.

Вопросы пользователю

  1. Какие видеокарты у пилотов (модель, Windows/Linux)? От этого зависит, нужен ли многосеточный метод (WF-03) и адаптивный режим.
  2. Клетка 100 м или 50 м? 100 м — по умолчанию (быстро); хребет Аушкуля (~100 м) будет «смазан». WF-08 покажет разницу на ridge_west, решить после него?
  3. Включать поле по умолчанию сразу после WF-08 или только после полёта пилота (до того — enabled: "off" в сборке)?
  4. Клавиша «поле/аналитика» для пилота в сборке для проверки — годится (только в отладочной сборке)?
  5. Трава по полю (WF-10) — нужна сейчас или потом?
  6. Термики и облака по локальному ветру — нужны вообще? Сейчас они на общем ветре (так проще и совпадает у всех в сети); см. «На потом».
  7. Слабые старты (tugaya_south, ridge_west, падающий test_ridge_climb_20kmh): если поле их не вытянет — оставить слабыми (честно: в жизни работают только в сильный день)?

На потом

  • Термики/облака на локальном ветре: снос и наклон термика — по среднему по столбцу G в точке источника, квантованному грубо (направление до 5°, сила до 10 %) с гистерезисом, чтобы у всех клиентов совпадало несмотря на разницу видеокарт; или отдельное грубое поле 1 км на CPU (детерминированно). Только после жалобы пилота «термик висит не там».
  • Кеш базисов на диске (если на слабых картах загрузка долгая).
  • Поле 50 м вокруг стартов (вложенная сетка) при сохранении 100 м на весь квадрат.
  • Поле для точек с карты за пределами детального квадрата (дальний слой 100 м) — если будут маршруты длиннее 40 км.