Модель воздуха в трёх масштабах

План и задачи — docs/plan/air_model.md; журнал — docs/archive/plan/air-model-progress.md. Здесь — описание того, что уже работает в игре.

Поле на CPU (масштаб 1 → игра)

Среднее поле воздуха (решение Пикара, масштаб 1) в игре читается на CPU: air_velocity_at зовут физика крыла (3 точки × 120 Гц), боты, птицы, колдун. Код: scripts/atmosphere/air_model/wind_field.gd (WindField — один уровень), scripts/atmosphere/air_model/air_field_set.gd (AirFieldSet — набор уровней и подмена), подключение — scripts/atmosphere/atmosphere.gd. Тесты — tests/atmosphere/test_wind_field.gd (на аналитических массивах), tests/atmosphere/test_air_model_atmo.gd (атмосфера с полем прикидки Онгудая).

Раскладка

Сетка — декартова MAC с маской «клетка под землёй» (решение AM-01, tools/research/air3d/reference.md → «Решение: σ-сетка или маска»), Δz постоянный на уровне. На CPU поле хранится в центрах клеток (решатель отдаёт скорости на гранях; перенос в центры — среднее двух граней, как centers() решателя, — делает WindField.from_mac). Центры выбраны, потому что выборка тогда одна и та же для всех каналов (трилинейно по 8 узлам), а точность интерполяции на гранях и в центрах одного порядка.

  • Массив (nz, ny, nx), индекс (k·ny + j)·nx + i, float32. i — восток (x мира), j — север (y = −Z мира), k — вверх (высота над морем). Так же лежат массивы решателя и эталоны AM-01.
  • Центр клетки: x = x0 + (i + ½)·dx, y = y0 + (j + ½)·dx, z = z_bot + (k + ½)·dz; x0, y0 — западный и южный края в координатах решателя (мир: Z = −y).
  • Клетка под землёй, если z её центра < hc столбца (hc — высота рельефа сетки, блочное среднее по клетке); первая воздушная клетка столбца k₁ = ⌈(hc − z_bot)/dz − ½⌉.
  • Каналы: u, v (восток, север), w_mech — вертикаль решения без нагрева (обтекание рельефа ветром), w_conv = w − w_mech (от нагрева), θ′ (К). Разделение — вторым решением без нагрева (docs/plan/air_model.md → «Стык 1 ↔ 2»).
  • Метаданные: dx, dz, x0, y0, z_bot, nx, ny, nz, z0 (шероховатость решателя, 0,1 м).

Построение:

  • WindField.from_arrays(meta, u, v, w_mech, w_conv, theta, hc, max_speed, max_w) — массивы в центрах; через него отдают поле GPU-решатель (AM-03) и библиотека полей (AM-06б). NaN и ∞ → 0, ограничители — за тот же проход. Проход O(n) в GDScript: окно 64 × 64 × 62 (254 тыс. клеток) читается и строится за ~0,18 с — строить в рабочем потоке (класс не трогает сцену), в главном — только set_field.
  • WindField.from_mac(meta, u, v, w, w_mech_faces, theta, cell, hc) — массивы решателя на гранях с ореолом (NZ, NY, NX) = (nz + 2, ny + 2, nx + 2), как эталоны AM-01 (fixtures.py).
  • WindField.load_file(путь) — <путь>.json (метаданные, arrays: {имя: [смещение, длина]} в числах float32, как у эталонов AM-01) + <путь>.bin (float32 LE). Конвертер полей прикидки (npz окна 100 м) — tools/research/air3d/to_game_field.py.

Выборка

WindField.sample(pos, ground_h) -> Vector3 — мир (x — восток, y — вверх = w_mech, z — юг = −v):

  1. Точка → дробные индексы центров; берутся 4 соседних столбца (за краем — ближайший край).
  2. Высота у земли — по настоящей высоте над рельефом. Рельеф сетки (блочное среднее 100 м) отличается от настоящего на десятки метров. Столбец c читается на высоте z_c = y + (hc_c − h)·exp(−agl/dx), где h — настоящая земля под точкой (атмосфера передаёт ground.sample().x), agl = y − h. У земли пилот на 20 м над настоящей землёй читает поле на 20 м над землёй сетки; выше подсеточные неровности гаснут на масштабе клетки (возмущение от неровности размера L затухает с высотой ~ L — потенциальное обтекание), и выборка идёт по абсолютной высоте. Выборка не «проваливается под землю» соседнего столбца на склоне.
  3. По вертикали — линейно между центрами клеток; ниже центра первой воздушной клетки — логарифмический профиль к нулю на z0: значение первой клетки × ln(a/z0)/ln(a₁/z0), a — высота над hc столбца, a₁ — высота центра первой клетки (так же решатель считает трение о землю, C_d = (κ/ln(Δz/2/z0))²). Для θ′ — значение первой клетки (без профиля). Выше верха — верхний уровень.
  4. Билинейно по 4 столбцам. Столбец целиком под землёй даёт 0.

Итог: на ровном месте выше первых клеток — точная трилинейная интерполяция (линейное поле воспроизводится до 5·10⁻⁷ м/с, тест). sample_theta, sample_w_conv — то же для θ′ и w_conv.

Край: edge_weight(pos) — 1 внутри, в полосе edge_blend_cells клеток у боковых граней (dx) и под верхом (dz) плавно (smoothstep) до 0 на границе. contains(pos) — точка внутри по горизонтали и между низом и верхом сетки.

Ограничители (clamp_values: при построении и при подаче поля с другими пределами): |горизонталь| ≤ max_speed_ms, |w_mech|, |w_conv| ≤ max_w_ms — защита от нефизичного решения; в полях прикидки не срабатывают.

Уровни и подмена (AirFieldSet)

  • levels — уровни от мелкого к грубому (клипмапы AM-04: окна 50/100 м, область 400 м). Выборка: мелкий с весом края w₁, остаток (1 − w₁) — следующему, …; что не покрыто — аналитике. sample(pos, ground_h) -> Vector4: xyz — сумма вкладов (м/с), w — доля поля 0..1. Стык уровней — плавный (в тесте: окно 100 м в области 400 м, скачок < 0,05 м/с на 1 м).
  • set_field(поле | [уровни] | null, blend_s) — подмена: новое поле за blend_s секунд времени атмосферы смешивается со старым, доля нового — smoothstep(t/blend_s), монотонно. Старым может быть «нет поля» (аналитика) — так же плавно поле включается и выключается. Время идёт в Atmosphere.step (advance(dt)). Подмена нужна для пересчёта поля каждые recompute_game_min игровых минут и при смене ветра/погоды (AirRuntime, ниже).
  • Подмена во время подмены: «старым» становится снимок текущей смеси (взвешенная сумма наборов уровней, не больше трёх) — выборка не скачет, как бы часто ни приходили новые поля.

Клипмапы (AM-04)

Три уровня: область 400 м (38,4 км, всё место) и два окна 64 × 64 вокруг пилота — 100 м (6,4 км) и 50 м (3,2 км), Δz = Δx/2. Окно решается тем же Пикаром с границей от уровня крупнее (область → окно 100 м → окно 50 м): у краёв окна поле тянется к родителю (зона релаксации 4 клетки), поэтому на стыке нет ступеньки, а внутри окно видит рельеф в своём разрешении (склон, бровка, седловина). При загрузке места все окна считаются с центром на старте; в полёте, когда пилот ушёл от центра окна на четверть его стороны (air_model.window_shift_frac), окно и все мельче пересчитываются фоном с центром у пилота (≈ 1–3 с после сдвига, кадр не стоит), до готовности игра берёт прежние уровни, готовый набор подменяется плавно (blend_s). Термики берут источники с области (подпись сетки для сети при сдвиге окон не меняется).

Стык (Каянча, 12:00, 3 м/с; tools/research/air_clipmap/out/seam_kayancha_h12_U3.png): что видит пилот, пересекая край окна, меняется не быстрее, чем внутри окна над тем же рельефом — за 50 м до 9–12 % скорости на 50 м над землёй (внутри 9–18 %), 1–5 % на 300 м (внутри 1–2 %). Сами уровни у земли различаются на 6–18 % (разрешение рельефа: 400 м занижает разгон на склонах) — полоса края (5 клеток) сглаживает эту разницу. Подробно — docs/guide/air-model-gpu.md → «Клипмапы».

Стык с атмосферой (правило «Стык 1 ↔ 2»)

В Atmosphere.air_velocity_at при включённом поле (доля fw.w > 0):

  • горизонталь — из поля: fw.xz + (1 − fw.w)·аналитика (профиль ветра × высота над морем);
  • механическая вертикаль w_mech — из поля вместо аналитического склонового подъёма w_ridge (в доле 1 − fw.w у края — w_ridge);
  • конвективная w_conv пилоту не отдаётся — её отдадут пузыри термиков (AM-07). До AM-07 термики и фоновое опускание работают как раньше (поверх поля).
  • Масштаб 3 (подветренная зона, болтанка) — по полю, см. «Масштаб 3: возмущения из поля» (AM-08): подветренное опускание и ослабление ветра — только из поля (двойной счёт снят), эвристика за гребнем даёт болтанку слоя смешения по ветру на уровне гребня, рывки (часть болтанки, с нулевым средним) и обратный поток у земли — только где пузырь отрыва сеткой не разрешён; грозы, волны, облака — как в аналитике.
  • mean_wind_at — тот же вес поля: горизонталь поля + w_mech (без подветренной эвристики и шума); без поля — как раньше (вертикаль 0). Порывистость травы (air − mean) не ловит разгон поля.

Выключенное поле — побитно прежняя аналитика: ветка поля не выполняется (tools/bench/air_velocity_bench.gd --compare: 38 400 значений, 0 отличий; тест test_off_and_outside_bitwise_analytic — и вне поля).

Конфиг (configs/atmosphere.json → air_model)

КлючПо умолчаниюСмысл
enabled"auto"auto — поле, если есть, иначе аналитика; on — требовать (нет — аналитика и предупреждение); off — всегда аналитика
edge_blend_cells5полоса края, клеток уровня (окно 100 м — 500 м, область 400 м — 2 км, как губки решателя)
blend_s60время плавной подмены поля, с времени атмосферы
recompute_game_min15пересчёт поля по игровому времени, мин (и при смене ветра/погоды)
timeout_s60предел одного расчёта поля, с стены; превышен — аналитика (загрузка) / прежнее поле (полёт)
max_speed_ms, max_w_ms40, 10ограничители

API атмосферы: set_air_field(поле | [уровни] | null, blend_s = -1) (−1 — blend_s из конфига), set_air_mode("auto" | "on" | "off") (отладка «поле/аналитика», замеры), is_air_field_on(), air_field (AirFieldSet: θ′ и w_conv для масштабов 2 и 3). Поле переживает configure().

Запасной путь и детерминизм

  • Поля нет (headless, нет GPU, ошибка расчёта или чтения, таймаут) → нынешняя аналитика; в журнал одна строка на смену режима: air_model: analytic (<причина>) или air_model: field (<источник>; уровней N).
  • AtmoFingerprint.make_world ставит air_model.enabled = "off" → отпечаток детерминизма и test_determinism не зависят от видеокарты и поля. Поле в ключ мира не входит.

Загрузка и пересчёт поля (AirRuntime, AM-06Б)

scripts/atmosphere/air_model/air_runtime.gd — узел-ребёнок Game; интерфейс — контракт C9 (docs/contracts/air-model.md). Один уровень — область 400 м (38,4 × 38,4 км вокруг центра места); окна 100/50 м вокруг пилота добавит AM-04 тем же путём.

Нейросеть вместо решателя (air_model.engine = "nn", ON-5, O5/O6). Тот же узел и те же сроки; проход — одна задача рабочего потока (AirNnField.run_pass): AirPlace.domain_case → AirNnInput (строка, страж области) → AirNnPrep (карты, числа) → AirOnnx.run (CPU) → to_physical → WindField на сетке решателя (13 AGL → центры клеток по высоте, выше 2 км отклонения от притока гаснут к 3 км). Окон нет (один уровень), GPU не нужен, работает headless; два прохода k — как у решателя. Файл сети: --air-nn-model= → user://air_nn/model.onnx → air_model.nn_model. Отказ (нет расширения, файла, формат ≠ O1, ORT, NaN) — air_model: analytic (нейросеть: …). Как вставить сеть и полетать — data/air_nn/README.md. Замер: tools/air_onnx/nn_load_probe.sh <model.onnx>. Пункт настроек «Ветер над рельефом»: расчёт / упрощённый / нейросеть.

Загрузка. Этап «Рассчитываем ветер» / “Computing wind” (LoadProgress, ключ wind, вес в configs/ui.json → loading.stage_weights) — после этапа «Камни, кусты и дороги», когда известен ветер у старта (встречный на старте — по курсу старта), до расстановки термиков (их источники — из поля). Поле — всегда точное для часа старта, ветра (u10 — ветер прогноза на 10 м у старта, направление атмосферы) и погоды полёта (дневной максимум и небо из прогноза пилота); подаётся в атмосферу сразу, без подмены. Доля полоски — по ходу решателя. Встроенные места, места с карты и сетевая игра — одинаково: в сети каждый клиент считает поле сам в Game.start, общие у всех только источники термиков (их шлёт ведущий, C5). То же место и те же условия («Ещё раз») — этап мгновенный, поле уже в атмосфере.

Порядок и где идёт работа:

  1. вход решателя (AirPlace.domain_case: рельеф блоками, погода на час, солнце по склонам) и подготовка обоих решений (AirCase.prepare, с нагревом и без) — рабочий поток, ~1,2 с;
  2. решатель (AirPicardJob, mech = true) — главный поток порциями: на экране загрузки poll_slice(40 мс), в полёте poll() раз в кадр, порция GPU ≤ 25 мс;
  3. сборка WindField — field_async (рабочий поток), затем set_air_field на главном.

Локальный RenderingDevice и ядра создаются один раз при запуске игры (~0,2 с — кадр запуска и так длинный) и живут до выхода; после каждого расчёта освобождаются только его буферы (~95 МБ).

Полёт. Срок пересчёта — каждые recompute_game_min = 15 игровых минут по часам неба (ускорение времени ×10/×60 учтено: при ×60 срок — раз в 15 с): поле считается на начало срока (12:15, 12:30, …) с тёплого старта от состояния текущего поля и плавно подменяет его за blend_s = 60 с времени атмосферы. Смена ветра (> 0,05 м/с или > 1°) или погоды — внеочередной пересчёт. Очереди нет: пересчёт, не успевший до следующего срока, по окончании сразу сменяется новым — на последний срок. Ошибка или таймаут в полёте — остаётся прежнее поле, следующая попытка на следующем сроке; при загрузке — аналитика и строка air_model: analytic (<причина>). Удачный расчёт — строка air_model: поле 12:15 ч, 3.0 м/с с 151° (<причина>): <с>, итераций [без нагрева, с нагревом]…; k притока …, U над стартом на 10 м … (проход 1: …), проходов N [проходы].

Поле под старт (air-start, C9 v3). Ветер меню — ветер на 10 м над стартом, а решатель задаёт ветер притоком на краю области 38,4 км над сглаженным рельефом: над стартом (гребень, разгон обтекания) поле при том же притоке давало 1,0–2,1 × меню на 10 м (WPC-2). Поэтому загрузка с центром окон и ветром идёт в два прохода: проход 1 — область и окна с множителем притока k₀ (1 или k прошлой загрузки того же места и направления), замер U₁ — горизонталь среднего поля без болтанки на 10 м над землёй старта (по набору уровней с весами края, как выборка атмосферы; земля для сдвига поля по рельефу — сетка GroundField, как у атмосферы); k₁ = k₀·(u10/U₁)^(1/p) (пределы 0,3…3); проход 2 с k₁ — только его поле подаётся в атмосферу. Поле не пропорционально притоку (нагретые склоны дают ветер, не растущий с притоком; толщина слоя ∝ u*): U над стартом ∝ k^p, p — по ветру меню 0,57 (3 м/с), 0,75 (6), 0,90 (10), между — линейно (AirRuntime.INFLOW_P, медианы по парам проходов на 10 стартах, tools/research/air_start/out/passes.csv). Множитель — на весь профиль притока (U10 притока, u* замыкания); α, класс устойчивости и z_sat — по ветру меню; калибровка решателя (k = 1) не меняется. Предел timeout_s — на проход; проход 2 не удался — подаётся поле прохода 1 (строка air_model: проход 2 не удался (<причина>) — поле прохода 1). Без окон или в штиль (u10 < 0,5 м/с, WindProfile.U10_MIN) — один проход с k = 1. Слабый ветер: проход 1 упёрся в предел итераций (3000) хотя бы в одном решении области или окна — второго прохода нет, подаётся поле прохода 1 (air_model: проход 1 не сошёлся (предел итераций) — без подстройки, last_info.not_converged). Пересчёт в полёте — один проход с k загрузки (k в полёте не уточняется: смена часа меняет нагрев и устойчивость).

Границы модели: на 100 м над стартом поле после подстройки на 5–40 % слабее аналитики WindModel (по Taylor & Lee 1984 аналитика здесь завышает на 10–28 %, WPC-2); на слабом ветре (≲ 3 м/с днём, штиль) решатель не сходится за 3000 итераций — невязки импульса и θ′ решения без нагрева перестают падать (малые u* и K, устойчивая стратификация над тонким слоем) — отдельная задача (TODO), загрузка у таких стартов дольше и без подстройки.

Числа (RTX 4070 SUPER, Онгудай, 12:00, 3 м/с со 150°; tests/atmosphere/test_air_runtime_gpu.gd, tools/loading/load_probe.gd --location=ongudai --hour=12 --stage-shot=wind):

ЧтоЗначение
этап «Рассчитываем ветер» в игре (окно 1280×720)3,3–3,9 с: вход места и подготовка 1,2 с (рабочий поток) + решатель 1,6–2 с (GPU 0,73 с, 100 + 90 итераций) + сборка поля
кадр в этапе≤ 94 мс от расчёта (наибольшая доля AirRuntime за кадр 54–61 мс — запуск задачи, опрос ≤ 54 мс); в половине прогонов первый кадр этапа 450–510 мс — хвост этапа «Камни, кусты и дороги» (без этапа ветра такой же кадр 1,5 с приходится на этап «Почти готово»)
пересчёт в полёте (кадры, окно 320×240)3,7–4,1 с стены (GPU 0,8–1,0 с), доля AirRuntime за кадр ≤ 56 мс, опрос ≤ 13 мс, порция GPU ≤ 22 мс
тёплый старт70 + 90 итераций против 100 + 90 холодного: греется только решение без нагрева (Р13 в контрактах)
ускорение ×10 / ×60срок 90 / 15 с реального времени; пересчёт 4 с — 4,5 % / 27 % срока, успевает
поле AirRuntime против AirPicardJob напрямуюу старта (16 точек, 10–400 м) совпадает побитно
подмена через два пересчёта (второй — посреди первой подмены)шаг w_mech ≤ 0,0004 м/с за 0,1 с; график — tools/research/air_runtime/out/blend_trace.png

Ограничения.

  • Каждое новое поле пересобирает источники термиков (AirThermals.build) на главном потоке — ~0,8 с на область 400 м; в полёте это кадр раз в срок пересчёта. В рабочий поток — AM-11 (контракты, Р12).
  • Дата поля — опорная дата погоды (reference_context), а не дата полёта (Р14).
  • Время этапа на слабых видеокартах (AMD — оценка ×2…×11 по памяти) — до ~15 с; предел — timeout_s.

Отладка: полёт на поле из файла

Вместо расчёта при загрузке поле можно подать из файла — аргумент --air-field=<путь> (.json + .bin), его читает Atmosphere один раз за запуск:

cd tools/research/air3d
PY=../heat_ca/.venv/bin/python
F=tools/research/air3d/out/fields   # поля прикидки (вне git)
$PY to_game_field.py $F/W100_h13_U3_d180.npz $F/W100_noheat_U3_d180.npz fields/game/kayancha_w100_h13_U3_d180
cd ../../..
godot --path . -- --location=ongudai --wind=3 --from=180 --hour=12 \
  --air-field=tools/research/air3d/fields/game/kayancha_w100_h13_U3_d180.json

Поле прикидки — окно 100 м 6,4 × 6,4 км у Каянчи (64 × 64 × 62, 5 МБ), 13:00, южный 3 м/с; ветер игры надо задать тот же (поле его не подстраивает). Вне окна — аналитика.

Числа (AM-05)

  • Выборка на аналитических массивах: постоянное поле — ошибка 0; линейное — 4,8·10⁻⁷ м/с; лог-профиль у земли — до 10⁻⁴; вес края монотонен, шаг ≤ 0,01 на 1 м; NaN во входе → конечно.
  • Каянча, поле прикидки (фикстура 16 × 16 × 40 у старта), 50 м над землёй сетки: air_velocity_at = (−0,563; 0,076; −2,455) м/с = поле (u, w_mech, −v) до 10⁻⁴; w_conv 0,103 (пилоту не отдаётся); сумма 0,18 м/с совпадает с «подъёмом у старта на 50 м» прикидки (agl_slice, 0,178). Через край окна на высоте 2400 м — изменение ≤ 0,03 м/с на 5 м.
  • Время air_velocity_at (способ базы AM-00: Каянча, 75 м, 100 000 вызовов в точке): без поля 14,8 мкс, с полем 20,4 мкс (×1,37; база AM-00 — 14,3 мкс). По сетке 6400 точек в окне поля: 16,8 → 25,4 мкс (×1,5). tools/bench/air_velocity_bench.gd.

Отладка: срезы и F3 (AM-10)

  • Срезы (WF-09): tools/wind_field/dump_slices.gd + .tscn — headless-инструмент, без окна. Читает поле из файла (WindField.load_file, тот же формат, что --air-field) и рисует три PNG: два горизонтальных среза (20 и 200 м AGL; цвет — разгон |Gₕ|/U₀ − 1, редкая сетка стрелок направления) и один вертикальный вдоль ветра через точку (цвет — w/U₀, тонкие изолинии θ′ каждые 0,15 К, силуэт рельефа снизу). Термики поверх вертикального среза — по желанию (--location=<id>): текущая модель термиков через Atmosphere.thermals_near (жёлтые треугольники по силе) — не термики из среднего поля 1 (AM-07 делает это параллельно; подпись на срезе так и говорит). Запуск:
    godot --headless --path . res://tools/wind_field/dump_slices.tscn -- \
      --field=tools/research/air3d/fields/game/kayancha_w100_h13_U3_d180.json \
      --out=tools/research/air3d/out/slices --name=kayancha --wind=3 --from=180 --hour=13 \
      [--point=x,y,z] [--location=ongudai] [--extra_out=build/screenshots --extra_prefix=03]
    Точка среза по умолчанию — проба *_agl10 из meta поля (обычно старт), иначе центр поля. На срезе Каянчи видны разгон и подъём над бровкой перевала (полоса подъёма/спада у земли) и явная полоса ускорения над седловиной на 200 м AGL. Вертикальный срез рисует и <имя>.json (полуширина, высота, шкала w, θ′-шаг, место/час/ветер) — tools/wind_field/annotate_slice.py (PIL) накладывает по нему оси (км / м), шкалу цвета w (м/с) и заголовок: GDScript headless без окна текст не рисует, PNG остаётся голой заливкой без подписей до этого шага.
    python3 tools/wind_field/annotate_slice.py \
      tools/research/air3d/out/slices/kayancha_vertikal_vdol_vetra.png --out build/screenshots/04_....png
  • F3 в игре: отдельный узел WindFieldDebug (scripts/atmosphere/wind_field_debug.gd), добавлен в scenes/game/game.tscn рядом с Game (без правок game.gd) — по нажатию F3 строит MultiMesh стрелок горизонтального ветра (Atmosphere.air_velocity_at — та же выборка, что у пилота) в сетке вокруг пилота (радиус/шаг/высоты — экспорты узла), цвет — вертикаль (подъём — голубой, опускание — красный); подпись в углу экрана — «поле» или «аналитика» (is_air_field_on). Повторное F3 — выключить: _process останавливается, MultiMesh и надпись скрыты — без выборок и перестроений, ноль влияния на FPS. F3 раньше в проекте не использовался (нет записи в configs/controls.json/input map — проверено, свободна). Скриншот-инструмент (нужно окно): tools/shots/wind_field_shot.gd — как cloud_shadow_shot.gd (автостарт полёта, ждёт FLYING), включает F3 через WindFieldDebug._toggle() и снимает кадр (--shot), либо меряет среднее GPU-время кадра RenderingServer за --gpu=N кадров (общий кадр окна — для сравнения с полем и без, не только трава/вода). Пример:
    XDG_DATA_HOME=$(mktemp -d) godot --path . --audio-driver Dummy --resolution 1920x1080 \
      res://tools/shots/wind_field_shot.tscn -- --autostart --bots=0 --location=ongudai \
      --site=kayancha_south --wind=3 --from=180 --hour=13 \
      --air-field=tools/research/air3d/fields/game/kayancha_w100_h13_U3_d180.json \
      --out=build/screenshots --tag=kayancha --shot --preset=high
  • Трава и рябь на воде по полю (WF-10): TerrainWind (scripts/terrain/terrain_wind.gd) раз в field_tex_interval_s (configs/world.json → wind_visual) строит 2D-текстуру RGF (u, v горизонтали нижнего слоя поля, AirFieldSet.sample) вокруг камеры и передаёт всем материалам (field_wind_tex/field_wind_origin/field_wind_size_m); источник поля — сам Atmosphere, передан через WorldLink.link → Terrain.set_wind_sources(…, field_src) (duck typing: is_air_field_on()/air_field, без правок atmosphere.gd). В шейдерах — scripts/terrain/terrain_wind.gdshaderinc (grass_wind, per-blade трава) и scripts/terrain/terrain.gdshader (water_wind, рябь) берут field_wind_tex по месту (xz) вместо общего wind_vec, когда field_wind_size_m > 0; без поля или вне текстуры — как раньше. Замер (Каянча, «Высокий», окно 1920×1080, среднее GPU-время кадра за 150 кадров, tools/shots/wind_field_shot.gd --gpu=150, flock /tmp/heat_ca_gpu.lock): с полем 9,09 мс (два прогона: 9,087 и 9,093), без поля 8,991 мс — Δ ≈ 0,10 мс (трава и рябь воды вместе, в бюджете ≤ 0,2 мс; замер по всему кадру окна — трава и вода в нём лишь часть общей стоимости).

Масштаб 2: термики из поля (AM-07)

Нынешняя модель термиков (thermal_field.gd, atmo_thermal.gd: пузыри, ядро Гедеона, радиус по Аллену, рост/зрелость/распад, снос, облака над термиком) осталась; когда есть поле с входом решения, из поля берутся место, сила, радиус, потолок и снос. Ядра — по модели восходящих потоков Аллена (AM-07б): сколько их, какого радиуса и силы — от толщины слоя и w*; поле решает, где их больше; восходящий поток поля сверх несомого ядрами — широкий слабый подъём в «между». Код: scripts/atmosphere/air_model/air_thermals.gd (AirThermals — источники на час), thermal_field.gd (источники поля живут как клетки), atmosphere.gd — одна строка (ThermalField.air). Тесты: tests/atmosphere/test_air_thermals.gd (синтетика + фикстура Каянчи 12:00, tests/atmosphere/fixtures/air_model/thermals/). Замеры и картинки: tools/research/air_thermals/ (README там).

Вход

Уровень — грубейший в AirFieldSet.levels (область места; общий у всех клиентов сети — решение К0 Р10; окна 100/50 м вокруг пилота у каждого свои и для выбора источников не используются). Кроме каналов (w_conv, θ′, u, v, рельеф сетки — WindField.raw_*, C4 v2) нужен вход решения из meta (WindField.heat_flux/z_i/gam/u10): поток тепла H (ny·nx, Вт/м²), z_i (м над морем), dθ̄/dz по уровням (К/м), U10 (м/с). Нет их — термики аналитические, как раньше.

Законы (для столбца поля; H_kin = H/(ρc_p), h = max(z_i − hc, 300 м), ζ = z/h)

ЧтоФормулаОткуда
Масштаб скоростиw* = (g/θ0 · H_kin · h)^(1/3) (WindField.deardorff_wstar, одна функция с возмущениями AM-08; у ядра H — среднее по водосбору источника)Дирдорф (1970), Stull (1988) гл. 4
Избыток частицыΔθ = b·H_kin/w_m, w_m = (u³ + 0,28 w³)^(1/3), b = 6,5Холтслаг–Бовилль (1993), то же замыкание, что у поля (air.py _closure)
Поток массы подсеточной конвекцииF(ζ) = H_kin(1 − ζ)/Δθ = w_m(1 − ζ)/b; по слою F̄ = w_m/(2b)нелокальный нагрев поля H(1 − ζ) несут термики; M = w′θ′/Δθ
Восходящий поток поляΦ = F̄ + max(W̄, 0), W̄ — средний w_conv слоя 0..hподсеточный + организованный (схождение, склоны)
Потолокчастица θ̄ + θ′(у земли) + Δθ всплывает до θ̄ + θ′ поля; не выше кромки погодыметод частицы; инверсия и тёплый слой θ′ гасят сами
Толщина слоя для ядерz_l = потолок частицы − земля (без кромки), не меньше 300 мместная толщина слоя перемешивания: утром над склоном она больше z_i − hc (z_i из прогноза ниже гребней)
Радиус ядраr₂(ζ) = max(10 м, 0,102·ζ^(1/3)(1 − 0,25ζ)·z_l); у верха R = r₂(1) = 0,0765 z_l, ниже — тот же профиль термикаАллен (2006, AIAA 2006-1510) по самолётным замерам Lenschow & Stephens (1980)
Живых ядер на площадьn_A = 0,6/(z_l·r₂(½)) ≈ 8,5/z_l² (доля площади ядер ≈ 0,13)Аллен (2006)
Силаw0 = k·w*, k = e·max ζ^(1/3)(1 − 1,1ζ) = 1,24пик Гедеона с тем же потоком ядра, что средний подъём Аллена w̄ = wζ^(1/3)(1 − 1,1ζ) на уровне максимума (ζ ≈ 0,23); пик самого Аллена там ≈ 2,2 w̄ ≈ 1,0 w; ядра 1–2 w* — Lenschow & Stephens (1980), Young (1988)
Плотность источниковn = n_A·Φ/Φ̂ / p_жизни, но ≤ Φ/(w0·K̄); шаг укладки √(0,696/n), не ближе 2,5 RΦ̂ — средний Φ кандидатов с весом n_A (итог по площади = Аллену), p_жизни — доля времени, когда термик источника жив (доля циклов × жизнь/период ≈ 0,71): живых — n_A; K̄ — средний по времени и столбу поток ядра одного пузыря на 1 м/с (форма, радиус Аллена, жизнь); ядра не несут больше Φ
Широкий подъёморганизованный поток (W̄⁺) сверх несомого ядрами остаётся в w_conv − чистый поток пузырей«между» (ниже); отдельной ручки нет — считается от поля
Снос и наклонсредний ветер поля по столбу источника (земля…потолок), постоянный на жизньWF-13 этап 1
Кольцо опусканиямножитель кольца Гедеона ρ = (1 − O/M)·e⁻¹/∫кольцаподсеточная доля потока ядра возвращается у пузыря (Гедеон ≈ 1,03, чистый ноль), организованная O (W̄⁺) уходит вверх
«Между»w_conv(x, z) − ожидаемый чистый поток пузырей водосбора (по высоте — профиль ядра)среднее (пузыри + между) = w_conv поля; фоновое опускание пресета в поле не действует

Выбор источников: кандидаты — столбцы с H > 0, Φ > 0, столбом частицы ≥ 300 м (минимальная толщина слоя замыкания) и весом поля ≥ ½; по убыванию Φ, кандидат берётся, если в радиусе его плотности нет взятого. Водосбор — ближайшие столбцы (не дальше 2 радиусов): по нему w* (средний H) и доля организованного потока O/M (кольцо, «между»). Точка источника внутри столбца — лучшая по освещённости земли (скалы, опушки, как у клеток), сид — столбец и сид мира. Термик источника живёт как клетка: свой период и фаза, доля циклов, рост/зрелость/распад, облако, Cb и редкие очень сильные — из погоды, как было; радиус — Аллена (у очень сильных — не меньше пресета). Частица не дошла до кромки — облака нет.

AM-07б — что изменилось и почему. В AM-07 ядра с радиусом из пресета (90–170 м) несли весь Φ: сила = поток водосбора / поток одного пузыря (до 2,5 w*), источников — сколько нужно, чтобы унести Φ. Итог 12:00: 4,1 м/с, соседний живой 252 м, 9056 термиков за 4 ч. В слое конвекции восходящий поток несут не только когерентные ядра, которые обрабатывает пилот: доля площади подъёма 0,3–0,4 (Lenschow & Stephens 1980; Stull 1988 гл. 4), ядра — меньшая часть, остальное — широкий слабый подъём. Теперь число, радиус и сила ядер — от z_l и w* по Аллену (2006), а поле решает только, где ядер больше (∝ Φ). Ядра несут 37–45 % Φ днём, 12–21 % утром; остаток организованного потока — широкий подъём в «между» (поле), подсеточный остаток (F̄ сверх ядер) имеет чистый поток ноль и пилоту не выдаётся. Стык C4 не меняется (w_conv пилоту — только через пузыри + «между»), C5 — тоже (меняется лишь, какие столбцы в маске).

Стык с аналитикой: клетки сетки с источником там, где поле покрывает землю (вес ≥ ½), не рождают; вне поля — аналитика как раньше. Поле выключено или нет входа — всё побитно прежнее (тест test_atmosphere_field_region_and_off). Отпечаток детерминизма — с выключенным полем.

Числа (Онгудай, 3 м/с со 150°, ясно, типичный июль; эталон AM-01, make_fields.py)

Приёмка 1 — где термики (плотность источников, 1/км², по классам столбцов окна 100 м; AM-07б):

ПолеПрогретые (верх. четверть H)Слабо прогретые (ниж. четверть)Схождение W̄ > 0,1Опускание W̄ < −0,05Тень H ≤ 0
окно 100 м, 12:004,90,73,70 (56 столбцов)—
окно 100 м, 15:003,21,52,60 (56)—
окно 100 м, 9:0010,63,614,88,60 (129 столбцов)

Сила у всех источников одна доля w* (1,24): 12:00 — 2,7–3,2 м/с, 15:00 — 2,9–3,4, 9:00 — 1,1–1,5. Утром слой тонкий (z_l ~ 300–800 м) — ядра мелкие (R 25–60 м) и частые, как у Аллена; на области 400 м больше одного источника на столбец не бывает (≤ 6,25/км²).

Карты: tools/research/air_thermals/out/fig_sources_<поле>.png (H, W̄, Φ, источники ∝ силе поверх рельефа); было/стало — fig_before_after_kayancha_w100_h12.png, fig_before_after_ongudai_d400_h12.png. Синтетика (тест): в тени (H < 0) источников 0, на оси схождения источник ±150 м, в схождении источники в 2,4 раза гуще (8,7 против 3,6/км²).

Приёмка 2 — потолок: инверсия 20 К/км над z_i = 2000 м → потолок частицы 2061 м; 5,8 К/км → 2212 м (проскок Δθ/Γ); тёплый слой θ′ +2 К на 800 м → 1808 м (≈ 800 м над землёй); в атмосфере термики — до инверсии, а не до кромки 2600 м, без облака; частица выше кромки — до кромки. Реальный рельеф 9:00 (утренняя крышка): столб 575 м (p10–p90 350–830), 12:00 — 1440 м (до кромки), 15:00 — 1640 м.

Приёмка 3 — поток массы (окно 100 м 12:00, Монте-Карло 60 мин, flux.json; AM-07б):

ξядра (w > 0)ожидание ядеркольца (w < 0)«между» + (широкий подъём)«между» −итого пилотуw_conv поля
0,250,0790,088 (0,90)−0,0390,132−0,0160,1560,173 (−10 %)
0,50,1010,121 (0,83)−0,0500,083−0,0010,1330,157 (−15 %)
0,750,1110,137 (0,81)−0,0550,0600,0000,1160,141 (−18 %)

м/с, среднее по площади внутри окна. Ядра / ожидание < 1 — часть потока сносит ветром за край окна (по термикам без пространства — 0,93); итог в пределах ±20 % от поля, как в AM-07. Ядра несут 37 % Φ (окно 12:00), 38 % (15:00), 21 % (9:00); область 400 м — 42/45/12 %. Синтетика в штиль (тест): ядра / ожидание 0,95.

Приёмка 5 — сила, радиус, густота против литературы и базы AM-00 (круг 6 км у Каянчи на области 400 м, круг 2,5 км на окне 100 м; 4 ч; stats.json; база — аналитика в тот же день, та же кромка):

сила ядра, м/с (ср.; p10–p90)R у верха, мпотолок над землёй, мживых на км²соседний живой, мтермиков за 4 ч
поле 12:00 (область 400 м)2,96 (2,70–3,21)161 (137–195)13052,33943792
поле 12:00 (окно 100 м)2,94 (2,65–3,18)156 (135–176)12012,7435768 (круг 2,5 км)
поле 15:003,15 (2,89–3,40)187 (160–219)15381,94623050
поле 9:001,35 (1,13–1,52)44 (медиана; 28–148*)5512,73184302
AM-07 12:00 (было)4,1 (2,4–5,9)90–170 (пресет)1375~5 (оценка)2529056
аналитика / база AM-002,35 (1,1–3,4)104–149990–16200,7650–660 (586–603)~1100 (13 488 на круг 20 км)
Аллен (2006)w̄ ≤ 0,46 w*, пик ≈ 1,0 w* (≈ 2,3 м/с при w* 2,3)0,0765 z_l: 150–190 при z_l 2–2,5 кмz_i8,5/z_l²: 1,4–2,1 днём, ~26 утромПуассон 0,5/√n: 350–420
литератураядра 1–2 w* (Lenschow & Stephens 1980; Young 1988)—между сильными — 1–1,5 z_i (≈ 2–3 км; ячейки конвекции, Stull 1988)

* 9:00 — p90 с термиками аналитики у края круга. Густота теперь по Аллену (живых 1,9–2,7/км² против 8,5/z_l² ≈ 1,4–2,1: чуть выше из-за Φ/Φ̂ — Каянча на прогретом склоне), вдвое реже AM-07, в 2,5–3 раза гуще аналитики. Расстояние 1–1,5 z_i — это масштаб ячеек (сильных термиков), а Аллен считает все ядра, поэтому соседний живой ~0,2 z_l, а не 1–1,5 z_i; разброс силы у ядер теперь узкий (только от w* водосбора), в жизни — шире. Утром (9:00) Аллен даёт ~26/км² мелких ядер; область 400 м держит не больше одного источника на столбец, и Φ-ограничение (ядра не несут больше Φ) — отсюда 2,7/км².

Сеть

Поле у клиентов чуть разное (своя видеокарта), выбор источников — нет: ведущий шлёт ZoneState.thermal_sources (net.proto, C5): подпись сетки уровня "dx,x0,y0,nx,ny" и битовую маску столбцов-источников (бит j·nx + i, base64; область 96 × 96 — 1,5 КБ в каждом ZoneState, 1 Гц). Остальные ставят маску своей атмосфере (ThermalField.set_air_forced, NetFlight. sync_thermal_sources) и считают силу, потолок и снос по своему полю. Новый ведущий переходит на свой выбор. Замер (шум поля 1e-3·|u₀| = 5,4 мм/с, net.json): без ведущего окно 100 м расходится в 24 из 103 столбцов (12:00), 23/85 (15:00), 26/362 (9:00); область 400 м — 46/2860 (12:00), 37/3993 (9:00) (AM-07б; в AM-07 область почти сплошь была источниками — 0/8463). С маской ведущего (тест, шум 3e-3): те же термики, |Δсила| ≤ 0,008 м/с, |Δось| ≤ 0,4 м, |Δпотолок| ≤ 0,3 м. Квантование без протокола не взято: на окне оно не спасает, а на области нужно для редких пороговых столбцов.

Чего не умеет

  • Сила ядра — одна доля w* (Аллен), без разброса у соседних ядер; в жизни сила ядер распределена шире (Lenschow & Stephens 1980). Ширина ячеек (сильные термики через 1–1,5 z_i) не моделируется: ядра Аллена — все восходящие потоки.
  • Геометрия ядер — по z_l (частица), w* — по h = z_i − hc (общая функция с AM-08); утром над гребнями z_l > h.
  • Тень облаков на источники поля не действует: облачность неба уже в H поля (sky.heat), а тень каждого облака без поправки поля вдвое уменьшала поток пузырей (замер 0,48 против 0,98).
  • Улицы (выстраивание по ветру) у источников поля нет: среднее поле валов не даёт.
  • Список источников — на час поля (пересчёт поля — новый список, живые доживают); вход в поле по ходу дня (инсоляция, перистые) до пересчёта не влияет.
  • Мелкие утренние термики со столбом < 300 м не рождаются (порог толщины слоя замыкания).
  • Сборка источников — главный поток: окно 64 × 64 ~0,13 с, область 96 × 96 ~0,55 с + точки источников по освещённости (~2–4 с на область с рельефом) — раз на поле; в рабочий поток — AM-11.

Границы модели: разрешение сетки и перенос

Среднее поле считается с переносом 1-го порядка на клипмапах 400 м (область) и 100/50 м (окна вокруг пилота), без окна 25 м и без поправок.

  • Разгон у бровки занижен на 15–20 % на высотах 20–100 м над склоном. Над стартом Каянча при встречном на старте ветре (нейтрально, 6 м/с) скорость при клетке 50 м и 1-м порядке составляет 0,80 / 0,83 / 0,84 / 0,92 от решения с клеткой 25 м и 2-м порядком на 20 / 30 / 50 / 100 м.
  • Причина — численная диффузия переноса 1-го порядка и разрешение рельефа клеткой: ошибка растёт на крутых и узких склонах и убывает с размером клетки. При клетке 12,5 м и 2-м порядке решение сходится к независимому линейному эталону (потенциальное обтекание + поправка на сдвиг среднего слоя) и к измерениям на холме Askervein (разгон на вершине на 10 м: 0,61 против измеренных 0,82; линейный эталон — 0,78).
  • 2-й порядок переноса не включён: на крутом рельефе у него нет установившегося решения (Аньези H = L), а на реальном рельефе он сходится в 2–3 раза дольше.
  • Путь исправления, если пилоты скажут «на бровке слабо»: 2-й порядок в окнах 100/50 м (время ×2–3) или окно 25 м. Калибровка параметров под это не подкручивается.
  • Числа и воспроизведение — tools/research/air3d/reference.md → «4. Askervein», out/ref/askervein*.json.

Калибровка (AM-09)

Параметры подогнаны по количественным данным (Askervein, подобие конвективного слоя, закон −5/3) по схеме Professor. Подробно — docs/research/air-model-tune.md. Изменился один параметр — длина перемешивания у рельефа: λ = max(40 м, 0,25·h) вместо 0,1·h. Это верхняя граница физичного диапазона, данные тянут выше. Слова пилота в подгонку не входили. Ниже — сравнение с ними после калибровки; модель и коэффициенты по этому сравнению не исправлялись.

Слова пилота против модели после калибровки

Синтетика эталона (tools/research/tune/pilot_check.py, как synth.py AM-01), ветер 5 м/с (на высоте 9 м/с); провалы — точка базы AM-00, 20 км/ч, turb_probe.gd --only=lee на полях с параметрами калибровки.

Слова пилотаЦельМодель (AM-09)До калибровки (AM-01/AM-08)Совпадает?
полоса подъёма ≈ 2 H, выше — спокойно: разгон над хребтом H = L = 500 м на 2 H / 2,5 H над подножием≤ 10 % / ≤ 5 %19 % / 16 % (потенциальное обтекание — 24 % / 16 %)19 % / 17 %нет
седловина ×1,5: скорость на 20 / 50 м над седлом к подходу1,5 ± 0,21,90 / 1,74 (ветер по оси); 1,84 / 1,69 (15°)1,91 / 1,76; 1,84 / 1,70нет (на 50 м — на границе)
в седловине ветер поворачивает к её оси (ветер 15°)≤ 5°4,2°4,1°да
косой ветер 45° — подъём слабее: w(45°)/w(0°)0,55–0,80,72 (50, 100, 200 м)0,72да
провалы за гребнем как в игре до поля: σ_w и частота рывков против базы AM-00 (Синюха / Бияготы / Аушкуль)±20 %σ_w +81 / +90 / +30 %; рывков −17 / −14 / −71 %+80 / +90 / +18 %; −17 / −14 / −70 %нет

Границы модели: калибровка (AM-09)

  • Седловина ×2,05 вместо ×1,5 (×1,9 до А1: выхолаживание с τ гасило и адиабатическое охлаждение поднятого воздуха, см. ниже). При числе Фруда около 2 поток переваливает через весь хребет, и седловина в модели работает не как «труба», а как понижение гребня: у земли над седлом ветер в 2,05 раза сильнее подхода (на 50 м — в 1,87) и слабее, чем над соседней вершиной. Полевое ×1,4–1,6 (Gebbies Pass) и ×1,5 пилота получены, вероятно, при другой устойчивости и геометрии. Устойчивость дня и так приходит из погоды, отдельной «ручки седловины» нет, и подгонять её не стали.
  • Полоса подъёма выше 2 H над крутым хребтом (H = L). На 2 H разгон ещё около 19 %. Потенциальное обтекание того же рельефа даёт 24 %, так что расхождение со словами «≈ 2 H» — свойство крутой геометрии, а не ошибка решателя.
  • Провалы за гребнем — по физике, не по базе (AM-08в, решение пользователя: приёмка σ_w = 0,14·ΔU слоя смешения и обратный поток по Menke 2019, сравнение с базой AM-00 — наблюдение). До AM-08в σ_w в подветренной точке была на 30–90 % больше базы (ΔU от наибольшего ветра столба 600 м, рывки 1,0·ΔU); после — на 25–65 % меньше (ΔU от ветра на уровне гребня, рывки — часть σ_w). Разбор — «Масштаб 3 → Числа».
  • Разгон у гребня и сразу за вершиной занижен даже на мелкой сетке. На холме Askervein при клетке 12,5 м и 2-м порядке модель даёт у вершины на 10 м 0,59 против 0,88 измеренных (в пределах погрешности сетки 0,17), вдоль гребня — на 0,1–0,3 меньше. Сразу за вершиной модель уже тормозит поток (0,19 против 0,68 и −0,07 против 0,40), а в измерениях там ещё разгон. Данные тянут длину перемешивания за физичную границу (λ/h до 0,6). Причины — K-замыкание первого порядка, стационарное среднее поле и неразрешённый внутренний слой. Подкручивать калибровкой нельзя; путь тот же, что для бровки: 2-й порядок и окно 25 м.
  • Болтанка над вершиной и на наветренном склоне завышена. Масштаб 3 считает σ по местной скорости трения, и над разогнанной вершиной ТКЭ растёт в 1,5–4 раза. На Askervein измеренная ТКЭ там почти как на равнине: турбулентность не успевает за разгоном среднего ветра (быстрое искажение, Britter, Hunt & Richards 1981). За гребнем модель совпадает с измерениями в пределах погрешности. После AM-09б (одна длина перемешивания у масштаба 3 и решателя) χ² ТКЭ Askervein 141 → 33, но вершина остаётся завышенной (χ² 10,3 на 4 мачты): быстрого искажения в модели нет, параметром это не исправить. Решение 30.09.2026: оставить границей модели; структурную правку — только если пилот скажет, что над вершиной болтает слишком сильно.
  • За гребнем Синюхи σ_u на 100 м выросла на 52 % при λ/h 0,1 → 0,25 (400 м за гребнем, 2,06 → 3,13 м/с; остальные точки таблицы σ — не больше 4 %). Точка в слое смешения за отрывом, где более длинная длина перемешивания ожидаемо усиливает болтанку. Не подгоняется; проверяется полётом пилота (AM-12).
  • λ/h = 0,25 — временно, до перекалибровки (заменено совместной калибровкой Б1: 0,0158 с полом 40 м, α по устойчивости — см. «Границы модели: профиль притока и длина перемешивания (Б2)»). Вывод AM-09 «данные Askervein тянут λ/h выше» оказался следствием показателя профиля притока α, выбранного вручную (0,17): по профилю опорной мачты RS в данных α ≈ 0,22, и при α 0,20–0,24 χ² падает с 86 до ~54, а λ/h уходит к нижнему краю (docs/research/air-model-sensitivity.md, отбор Морриса 01.10.2026). Askervein определяет по сути одну комбинацию λ/h, α, порядка переноса, local_k, λ (94 % действия — одно направление SVD). Значение пересматривается перекалибровкой по (λ/h, α, z0) с профилем RS в наблюдаемых.
  • Турбулентное число Прандтля Pr_t = 0,85 — постоянное (K_θ = K/Pr_t, один множитель на вертикальную и горизонтальную диффузию тепла; Kays 1994, середина измерений нейтрального слоя 0,74–0,95). Значение выбрано пользователем 30.09.2026 из вариантов 1,0 / 0,74 / 0,85 / 0,95. Зависимости от устойчивости Pr_t(Ri) нет: измерения дают 0,5–0,7 у земли в конвекцию и > 1 при Ri ≳ 0,2 (Li 2019); устойчивость в модели уже в F(Ri) местного K, конвективный перенос — нелокальный источник по слою перемешивания. Влияние на подъём мало (Онгудай 12:00, окно 50 м, Pr_t 1,0 → 0,85): при 3 м/с −0,003 м/с, в штиль −0,035 м/с (0,44 → 0,41).
  • Выхолаживание с τ = 2 ч действует только на диабатическую часть θ′ (от нагрева Q; второй переносимый скаляр θ′_d, А1). Адиабатические θ′ устойчивого обтекания (поднятый воздух холоднее фона на γ·δz) сохраняются и исчезают только перемешиванием — как в физике на масштабе области (радиационное выхолаживание — сутки). τ заменяет то, что средний прогрев слоя за день уже в фоне θ̄(z) погоды; физичнее источник Q − ⟨Q⟩ без τ — другая постановка, не сделано. Следствия (физика модели, не подгоняются): седловина без нагрева от τ не зависит (×2,06 при τ 1800 и 21600 с; до А1 ×1,71 и ×1,99); в штиль подъём у старта ниже — Онгудай 12:00, окно 50 м 0,52 → 0,44 м/с (−15 %, при Pr_t = 1; с Pr_t = 0,85 — 0,41): термики над склоном перехлёстывают верх слоя перемешивания и в устойчивом слое над z_i остывают адиабатически, раньше τ за 2 ч стирал это охлаждение и крышка слоя «протекала»; при 3 м/с разницы нет (1,33 м/с).
  • В штиль сходится дольше, местами не сходится. Без стока 1/τ у полного θ′ область 400 м в штиль 12:00 — 161 итерация вместо 61 (≈ 2 с на 4070 SUPER), без нагрева при ветре +50 % итераций; область 200 м, 3 м/с с нагревом — не сошлась за 3000 итераций (эталон air.py). В штиль и раньше часто не сходилось (окно 50 м, 12:00: 139 из 205 точек Морриса). Это задача сходимости (А2), не откат правки.
  • Шаг сильных термиков (1–1,5 z_i) модель не воспроизводит по построению. Все ядра одной силы (доля w*), порога «сильный» нет. Густота всех ядер совпадает с литературой: N·z_i/L = 1,4–1,5 против 1,2 ± 0,4 (Lenschow & Stephens 1980).
  • Не определены данными и оставлены прежними: пороги признака отрыва масштаба 3, поток тепла с луга H0 = 330 Вт/м², время выхолаживания τ диабатической θ′_d.

Границы модели: профиль притока и длина перемешивания (Б2)

Профиль ветра по высоте — одна функция у решателя (ветер на границах области и окон) и у аналитического ветра (scripts/atmosphere/wind_profile.gd, эталон tools/research/air3d/wind_prof.py, контракт C2 v4). Подробно и числа — docs/archive/plan/air-model-b2.md.

  • α — по устойчивости, а не по местной шероховатости. U(z) = U10·(z/10)^α до z_sat, выше — постоянный. α = 0,24 · r(класс): класс Паскуилла–Тёрнера по таблице Тёрнера (1964; ветер на 10 м и индекс радиации — высота солнца и облачность днём, облачность ночью), r — отношение показателя класса к нейтральному D по Irwin (1979, сельская местность): A, B 0,47; C 0,67; D 1; E 2,33; F 3,67. Полдень, ясно: штиль и 3 м/с — A/B, α 0,11; 6 м/с — D, α 0,24. Облачно — на класс-два ближе к D. Местный z0 в α не входит: зависимость α от z0 (Counihan) с калибровкой не согласуется (Askervein z0 0,05 → α 0,14 по формуле против 0,24 по данным).
  • α_N = 0,24 — эффективный показатель до z_sat, а не лог-профиль. У земли лог-профиль с z0 0,1 м дал бы ≈ 0,15–0,16; 0,24 — совместная калибровка Askervein (α 0,242 ± 0,004) с насыщением z_sat ≈ 450 м. Perdigão над лесом упирается в край сетки (≥ 0,29) — лес в игре не учтён.
  • z_sat = 0,3·h, h = 0,3 u/f* — толщина нейтрального слоя модели: 3 м/с — 208 м, 6 м/с — 415 м. Проверено профилями притока (змей Askervein до 267 м — насыщения нет; лидары Perdigão — рост прекращается на 420–590 м). Для устойчивых E, F (вечер после ≈ 20:25) h = min(0,3 u*/f, 0,4 √(u*·L/f)), L по классу и z0 — Golder 1972 (C2 v5): при 3 м/с z_sat 39 м (E) и 22 м (F), на высоте ≈ 2 × U10, как в нейтраль; сдвиг — в нижних десятках метров. Ночной струйный максимум (ветер над слоем больше геострофического) модель не воспроизводит. Данными притока устойчивые случаи не проверены (калибровка — нейтральная).
  • Облачность в классе устойчивости — доля неба из прогноза (ясно 0, переменная 0,3, облачно 0,85); кромки облаков в классе нет — взята ветка Тёрнера «нижняя граница ниже 7000 футов» (облачно — инсоляция на 2 ступени ниже). Перистая пелена класс не меняет.
  • «Ночь» Тёрнера (час до заката — час после восхода) — солнце ниже 8°: подобрано по Онгудаю в июле (час после восхода ≈ 9°, до заката ≈ 7°); на других широтах и в другие сезоны граница сдвинута на минуты–десятки минут.
  • λ = max(40 м, 0,0158·h) — совместная калибровка Б1: общий λ ≈ 27 м; одним λ модель не может одновременно усилить рециркуляцию в долине за двумя грядами (Perdigão тянет λ вниз) и ослабить след за одиночным холмом (подветренная сторона Askervein тянет вверх) — напряжение структурное, не параметрическое. Пол 40 м — граница устойчивости сходимости (lam 27 м не сходит окно 100 м в штиль). Та же λ у масштаба 3 (FieldTurbulence): выше ≈ 100 м над землёй длина перемешивания болтанки от сдвига теперь 40 м вместо 0,25·h. ТКЭ Askervein (метод AM-09б) с новой λ: модель по-прежнему завышает ТКЭ на наветренном склоне и вершине в 2–3 раза; χ² с одной σ данных 4297 → 3889, с σ сетки 33 → 255 — поля 12,5 и 25 м стали ближе, и завышение больше не покрывается погрешностью сетки (docs/archive/plan/air-model-b2.md).

Масштаб 3: возмущения из поля (AM-08)

Нынешние болтанка, рывки, подветренная эвристика и ротор остаются формой; где есть поле (доля a > 0), сила и масштабы берутся из поля масштабов 1–2 по физике пограничного слоя. Без поля, вне поля и при air_model.enabled = off — побитно прежняя аналитика (tools/bench/air_velocity_bench.gd --compare: 38 400 значений, 0 отличий; база AM-00 в подветренных точках повторяется до знака). Код: Atmosphere._air_velocity_field, scripts/atmosphere/field_turbulence.gd (FieldTurbulence: признак отрыва, σ, рывки); величины поля — WindField.turb_at / AirFieldSet.sample_turb (контракт C4 v3); шум — scripts/atmosphere/gust_spectrum.gd. Коэффициенты — configs/atmosphere.json → turbulence.field_*, lee.field_* (у каждого _doc с источником). Замеры — tools/research/air_turb/ (README там).

Что берётся из поля (по столбцам и в точке)

  • u* = κ|U_r|/ln(a_r/z0) — лог-закон по первой воздушной клетке с центром не ниже dz/2 над рельефом сетки (клетка ниже сидит в «углу» ступеньки маски и тормозится лобовым сопротивлением: u* по ней занижен вдвое — видно на стартах Синюхи и Бияготы); z0 = 0,1 м решателя.
  • Сдвиг |∂U_h/∂z| на высоте точки (производная линейной интерполяции между клетками, ниже первой клетки — производная лог-профиля) и N² = g/θ0·(dθ̄/dz + ∂θ′/∂z) (фон meta.gam + θ′ поля).
  • «Внешний» ветер U_out — наибольшая |U_h| столбца в 600 м над рельефом, и наклон опускания s_d = −min w_mech/U_out в том же слое.
  • w* Дирдорфа по потоку тепла H (сглажен квадратом 1500 м, как Params.k_smooth_m) и h = max(z_i − hc, 300 м) — WindField.deardorff_wstar, та же формула, что у термиков AM-07. Сверка (wstar_check.gd, w* болтанки в точке источника / w* источника): Каянча 12:00 — 1,001 ± 0,038 (54 источника), Синюха — 1,000 ± 0,021 (213), Аушкуль — 1,000 ± 0,010 (196); разница — только в осреднении H (квадрат 1500 м против водосбора источника), до ±9 % в отдельных точках.

Законы

  • Механическая болтанка: u*_m = max(u*·(1 − z/h)^¾, l·|∂U/∂z|), l = 1/(1/κz + 1/λ), λ = max(40 м, 0,25·h) (Прандтль–Блэкадар, то же замыкание, что у решателя, AM-09б; (1 − z/h)^¾ — Nieuwstadt 1984; h — слой перемешивания поля или нейтральный 0,3 u*/f, как у решателя; λ/h и 0,3 — константы AirCase). В приземном слое (ниже 0,05–0,1 h, Stull 1988, §1.5; переход smoothstep) u* стенки заменён u* в точке κ·U(z)/ln(z/z0) по среднему ветру поля, который получает пилот (AS-2: u* столбца берётся по клетке выше и на стартах расходился с ветром в точке на −12…+22 %). σ_w = 1,25 u*_m (Panofsky & Dutton 1984), σ_u = σ_w·(0,177 + 0,000823 h_ft)^(−0,4) (анизотропия MIL-HDBK-1797: 2 у земли → 1 к 300 м, т. е. σ_u ≈ 2,5 u* у земли), σ_v (поперёк ветра) = σ_w·(1 + 0,56·(σ_u/σ_w − 1)) — у земли 1,9 u* (σ_u : σ_v : σ_w = 2,4 : 1,9 : 1,25, Panofsky & Dutton 1984; Kaimal & Finnigan 1994, §1.6), выше — к изотропии вместе с σ_u (до AS-2 σ_v = σ_u: разброс направления на 25 % больше). Пульсации горизонтали кладутся вдоль и поперёк среднего ветра в точке. Рост у рельефа — сам: на бровке разгон поднимает u*, за гребнем — сдвиг оторвавшегося слоя.
  • Устойчивость (Монин–Обухов просто): градиентное Ri = N²/S²; u*_m делится на (1 + 5Ri) — корень функции устойчивости замыкания решателя F = 1/(1 + 5Ri)² (K ∝ l²S·F, u*_местн = √(K·S)). При Ri < 0 поправки нет — неустойчивость даёт конвективная часть. Масштабы вихрей не больше σ_w/N (Hunt, Kaimal & Gaynor 1985): L = 1/(1/L_MIL + N/σ_w).
  • Конвективная: σ_w = w*·√1,8·ξ^⅓(1 − 0,8ξ), ξ = z/h (Lenschow et al. 1980), σ_u = σ_v = 0,6 w*; шум несёт только вертикаль мельче конвективных вихрей: × (L_w/0,22h)^⅓ (пик спектра w на 1,5 z_i — Kaimal et al. 1976; крупнее — термики AM-07 и поле). H ≤ 0 — конвекции нет; нет данных о нагреве в поле — прежняя аналитика по погоде.
  • Сложение механики и конвекции (AS-2) — как в подобии приземного слоя: σ³ = σ_m³ + σ_c³ для каждой компоненты: σ_u/u* = (12 + 0,5 z_i/|L|)^⅓, σ_w/u* = 1,25(1 + 3|z/L|)^⅓ (Panofsky et al. 1977; 0,5 z_i/|L| = 0,5κ(w*/u*)³, т. е. σ_u,c → 0,59 w*, σ_w,c → 1,34 w*(z/z_i)^⅓ — тот же Lenschow у земли). До AS-2 — сумма квадратов: в смешанном режиме (u* ≈ 0,6, w* ≈ 2,2 м/с у стартов в полдень) σ_u на 20–30 % выше подобия.
  • Масштаб конвективной горизонтали (AS-2): дисперсия горизонтали сверх механической (доля 1 − σ_m²/σ²) несётся вихрями масштаба слоя L = 0,22 z_i (пик спектров u, v на ~1,5 z_i, Kaimal et al. 1976; фон Карман — пик k·S на 6,87 L), а не высоты над землёй: у земли это медленные «циклы» ветра (L/U ≈ 1–2 мин), не порывы за секунды. До AS-2 вся σ_u = 0,6 w* шла с L_u(z) ≈ 23 м — на 1,5 м над стартом при 3 м/с ветер разворачивался на 180° за несколько секунд.
  • Перенос вихрей у земли (AS-2): шум один на всю атмосферу и переносится ветром 300 м (advect; местный перенос рвал бы шум сдвигом по высоте), а вихри приземного слоя несёт местный ветер U(z) (гипотеза Тейлора с местной скоростью переноса — Willis & Deardorff 1976; Kaimal & Finnigan 1994, §2.5). У стоящего пилота время вихря — L/U, а шум давал L/advect: при 6 м/с у земли advect/U ≈ 3–4. Масштабы механических вихрей растянуты в r = advect/U раз (1 ≤ r ≤ 8) с весом e^(−agl/10 м): ниже ~размаха крыла пилот стоит, бежит или выравнивает; выше он летит ~10 м/с относительно воздуха, и встречу с вихрями задаёт полёт (там растяжение исказило бы частоту при полёте по ветру и поперёк).
  • За гребнем: признак отрыва lee_f = smoothstep(0,3; 0,7; ex)·smoothstep(0; 0,05; s_d), ex = 1 − (|U|/U_out)/(ln(z/z0)/ln(a_out/z0)) — дефицит скорости против лог-профиля под внешним ветром (прилегающий поток на подветренном склоне Askervein — до ΔS ≈ −0,3…−0,4, в следе и пузыре ex → 1); опускание отличает след от торможения у наветренного подножия. Вместо линии тени 12° (она осталась для аналитики и для доли 1 − a у края поля). В зоне (C4 v4, AM-08в):
    • скачок слоя смешения ΔU = max(U_H − |U|, 0)·lee_f, U_H — |U_h| поля в той же вертикали на уровне гребня, высота земля + max(r, agl) (r — превышение гребня против ветра, GroundField.relief_at): слой смешения отделяет поток над гребнем от следа, и скачок через него — от ветра на уровне гребня, а не от наибольшего ветра столба 600 м (U_out ≈ 2 U_H при 9 м/с у земли: ветер выше гребня разогнан и усилен с высотой);
    • болтанка слоя смешения σ_u = 0,18 ΔU, σ_w = 0,14 ΔU (Bell & Mehta 1990; Pope 2000, §5.4) — берётся большее из неё и механической;
    • рывки вниз — часть этой болтанки, не среднего потока: прежние пятна шума (burst_*), амплитуда 0,42·ΔU·опасность = пик 3σ_w слоя смешения, с нулевым средним (вычитается среднее g по распределению шума — поток массы уже в w_mech) и только при включённой болтанке; их доля дисперсии вычитается из гауссовой части, так что полная σ_w в зоне = 0,14 ΔU (с выключенной болтанкой w в зоне — ровно w поля);
    • обратный поток у земли 0,22·U_H·lee_f·опасность·e^(−agl/0,4r)·(1 − res) — максимум обратной скорости в пузыре отрыва / U100 = 0,22 ± 0,08 (Perdigão, нейтраль, Menke et al. 2019); res = smoothstep(4, 8, L/dx), L = 2,8 r — длина пузыря (Menke 2019: L/H ≈ 2,8), dx — клетка поля в точке (AirFieldSet.sample_dx). Эвристика только там, где пузырь решателем не разрешён: замер AM-08в (ниже, «Числа → Пузырь отрыва») — поле само течёт назад у ~30 % точек зоны при L/dx ≥ 8, у 15 % при 4–8 и почти никогда (0–3 %) при L/dx < 4. В окнах 50/100 м за гребнями выше ~70/140 м пузырь даёт поле, на области 400 м — эвристика;
    • масштаб вихрей ≥ 0,5 × превышение гребня (слой смешения не прижат к земле, Castro & Haque 1987).
  • Двойной счёт снят: с полем w_mech не умножается на (1 − lee), эвристическое опускание и ослабление ветра за гребнем не добавляются (только в аналитической доле у края поля).
  • Спектр: порывы — сумма 8 октав симплекс-шума (масштабы 8…1024 м; одна октава — полоса с пиком k·S на 1,75 масштаба, noise_line.gd), вес октавы — дисперсия спектра фон Кармана в её полосе (продольный — для u, v; поперечный — для w), L по MIL-HDBK-1797 (ниже 1000 футов L_w = h, L_u = h/(0,177 + 0,000823h)^1,2; от 2000 футов 2500 футов), нормировано на СКО 1. Прежний двухмасштабный шум (45 и 300 м, по 2 октавы с весом ½) в инерционном интервале падает как f^(−2,9) и круче f^(−5) выше 0,5 Гц — противоречит −5/3, поэтому с полем заменён.

Числа

У старта, 1,5 м над землёй (AS-2; tools/research/air_start/as2/, сцена игры с полем GPU, ветер меню встречный, 12:00, ясно; σ модели — в точке, теория — подобие для u* в точке и z/L по w*, z_i поля: σ_w = 1,25u*(1 + 3|z/L|)^⅓, σ_u = u*(12 + 0,5z_i/|L|)^⅓, σ_v = u*(1,9³ + 0,5z_i/|L|)^⅓; max|θ| — наибольшее отклонение направления от среднего за 20 с, max|ΔU| — изменение модуля за 0,5 с, по сидам 0/1500/3000; до AS-1 ветер над стартом в 1,0–1,8 раза больше меню):

стартменюU10 / Ū(1,5 м) поляu* в точкеz/Lσ_w до→после / теор.σ_u до→после / теор.σ_v до→после / теор.max|θ| до→послеmax|ΔU| до→послеw̄ поля / U·∇h
Каянча3 м/с3,2 / 2,20,29−0,270,40→0,36 / 0,441,54→1,40 / 1,351,54→1,36 / 1,33178°→30°1,00→0,630,23 / 0,69
Каянча6 м/с7,2 / 4,90,64−0,0250,87→0,80 / 0,822,17→1,84 / 1,742,17→1,62 / 1,5886°→31°3,98→2,140,59 / 1,53
Синюха3 м/с4,7 / 3,90,43−0,120,48→0,54 / 0,601,52→1,44 / 1,381,52→1,33 / 1,3047°→31°1,19→0,970,07 / 1,04
Синюха6 м/с10,7 / 8,80,98−0,0101,07→1,22 / 1,232,42→2,49 / 2,342,42→2,04 / 2,0045°→44°4,21→4,150,21 / 2,33
Биягода3 м/с3,0 / 1,90,26−0,270,40→0,33 / 0,401,62→1,46 / 1,421,62→1,44 / 1,41180°→103°1,83→0,870,22 / 0,69
Биягода6 м/с6,7 / 4,20,58−0,0250,88→0,72 / 0,742,25→1,79 / 1,712,25→1,62 / 1,5866°→38°4,52→2,160,72 / 1,52
Аушкуль3 м/с3,5 / 2,30,31−0,190,42→0,41 / 0,451,66→1,52 / 1,461,66→1,48 / 1,44142°→71°1,43→1,080,13 / 0,83
Аушкуль6 м/с8,5 / 5,70,75−0,0141,03→0,99 / 0,952,48→2,19 / 1,982,48→1,88 / 1,7854°→48°4,89→3,060,45 / 2,01

Признак отрыва на всех наветренных стартах 0 (эвристика обратного потока и слой смешения ни при чём); σ_w у земли ≈ 1,25 u* — это подобие, не ошибка (аналитика гасит вертикаль у земли × agl/60 м и даёт 0,02–0,04). Скачок CF-1 на 4e71747 (окна 100/50 м) — старт попал в мелкое поле с этой σ_w и с наклоном потока вдоль склона (w̄ поля); рывки и развороты давали σ_v = σ_u, сумма квадратов механики и конвекции, конвективная горизонталь с масштабом высоты (L_u ≈ 23 м вместо 0,22 z_i) и перенос шума ветром 300 м (у земли в 2–4 раза быстрее местного: ветер 300 м аналитики с профилем по устойчивости — 4,2 м/с при 3 м/с и 13,6 м/с при 6 м/с меню). При 3 м/с остаётся медленный конвективный «цикл» (σ_u ≈ 0,6 w* при Ū ≈ 2 м/с): Биягода, сид 1500 — 20-с среднее 0,8 м/с, отклонение до 103°; теория даёт σθ ≈ σ_v/Ū ≈ 35–45°.

На ровном (синтетическое поле, лог-профиль, sl_table.gd, 600 с в точке): 6 м/с нейтраль, 1,5 м — σ_u/σ_v/σ_w 1,31/1,22/0,64 → 1,30/0,90/0,62 (теория 1,25/0,99/0,65), время корреляции u 2,7 → 6,3 с, max|ΔU| за 0,5 с (медиана по 20-с окнам) 2,22 → 1,50; на 50–300 м нейтраль — без изменений, кроме σ_v (50 м: 0,88 → 0,76); конвекция H = 300 Вт/м², z_i = 1500 м: 100 м — σ_u/σ_v/σ_w 1,50/1,53/0,87 → 1,38/1,38/0,79, 300 м — 1,45/1,37/1,62 → 1,38/1,30/1,55 (сложение σ³ вместо σ²).

Спектр (turb_probe.gd --only=spectrum, spectrum.py; прямой полёт 12 м/с поперёк ветра 20 км/ч, Аушкуль aushtau_east, 400 с × 50 Гц; наклон log S — log f в 0,1–1 Гц ± 2σ по 8 частям):

u, 150 мw, 150 мu, 400 мw, 400 м
с полем−1,58 ± 0,13−1,70 ± 0,18−2,00 ± 0,27−1,68 ± 0,15
аналитика−2,90 ± 0,24−2,88 ± 0,12−2,96 ± 0,31−2,74 ± 0,13

Картинка — tools/research/air_turb/out/spectrum.png. Выше ~1,2 Гц (λ < 14 м при ~16 м/с относительно шума) спектр обрезан — мельче размаха крыла не несём.

Болтанка от ветра, у бровки, в устойчивом воздухе (--only=table, СКО ряда 120 с в точке, м/с, σ_u / σ_w; синтетическое поле — ровно, лог-профиль, H = 0; Синюха — поле lee_fields.py, 20 км/ч):

Случай20 м100 м300 м
2 м/с, нейтрально0,29 / 0,210,17 / 0,140,11 / 0,12
4 м/с0,82 / 0,380,39 / 0,320,32 / 0,28
6 м/с1,20 / 0,650,61 / 0,540,49 / 0,37
8 м/с1,61 / 0,830,85 / 0,680,63 / 0,49
6 м/с, N = 0,0101,07 / 0,560,18 / 0,150,02 / 0,02
6 м/с, N = 0,018 (инверсия)0,82 / 0,440,07 / 0,060,01 / 0,01
6 м/с, H = 300 Вт/м², z_i = 1500 м1,87 / 0,711,16 / 0,881,32 / 1,06
Синюха, ровно против ветра (−1400 м)1,36 / 0,561,26 / 0,62
Синюха, бровка (старт)2,06 / 0,781,10 / 0,84
Синюха, за гребнем +400 м1,30 / 2,663,13 / 2,18

Растёт с ветром (u* ∝ U), у бровки σ_u на 20 м в 1,5 раза больше, чем на равнине (разгон), в устойчивом воздухе выше приземного слоя почти гаснет (Ri ≫ 1), в конвекцию растёт к середине слоя. Аналитика на ровном месте в 1,5–2,5 раза сильнее: она подобрана под шероховатую местность (σ_u = 0,14·U·(1 + e^(−z/150))), поле — z0 = 0,1 м решателя (луг).

Подветренная зона против базы AM-00 (--only=lee: точка базы, 20 км/ч с обратной стороны, 180 с × 20 Гц, сид 42, погода medium с термиками; «рывок» — |dw/dt| > 3σ(dw/dt) ряда):

Стартбаза: σ_w, рывков/минс полем: σ_w, рывков/минпорог dw/dt, м/с² база → полеlee_f, U_out, ΔU
altai/sinyukha_west (10 м над землёй)1,14, 18,02,05 (+80 %), 15,0 (−17 %)2,0 → 3,41,0; 9,5; 8,5 м/с
askarovo/biyagoda_west (10 м)1,08, 7,32,05 (+90 %), 6,3 (−14 %)1,9 → 4,61,0; 9,9; 9,3 м/с
aushkul/aushtau_east (10 м)1,27, 26,01,66 (+31 %), 7,7 (−70 %)2,0 → 3,10,9; 8,2; 6,6 м/с

Без поля — побитно база (1,139 / 18,0; 1,075 / 7,3; 1,274 / 26,0). Расхождения — физика:

  • σ_w больше: (1) база гасила вертикальные пульсации у земли множителем agl/60 м (на 10 м — 0,17), а в приземном слое σ_w ≈ 1,25u* от высоты почти не зависит, в слое смешения — 0,14 ΔU; (2) ΔU из поля — скачок от 0,5–1 м/с у земли до 8–10 м/с над следом (ветер над гребнем разогнан и выше старта), а база брала ветер 5,6 м/с на высоте пилота.
  • «Рывков» (выбросов за 3σ своей же производной) меньше на Аушкуле: сплошная болтанка слоя смешения сильнее, и те же удары вниз реже выходят за 3σ. В абсолютных числах толчков больше: выше порога базы (2 м/с²) — 84, 246, 66 в минуту. Удары вниз (форма базы) на месте, амплитуда — до ΔU.

Среднее w за гребнем = w поля ± шум (те же точки без термиков, 1800 с): с полем −0,18 / −0,15 / −0,26 м/с при w_mech поля + фон 0,03 / −0,23 / −0,14 м/с — разница 0,20 / 0,08 / 0,12 м/с при стандартной ошибке среднего ~0,4 м/с (σ_w 1,8–2,7 м/с, время корреляции рывков ~20 с); аналитика там же — −4,5…−4,6 м/с (эвристическое опускание 0,7·u·lee + рывки).

За гребнем после AM-08в (C4 v4; Онгудай, 6 выгрузок tools/research/wind_compare, таблица «было / стало» — tools/research/wind_compare/am08v/table.md, am08v_table.py; поле и признак отрыва те же, меняется только эвристика поверх поля; выгрузки — без болтанки, кроме *_turb_air):

9 м/с у землибылосталополе
min w, старт 10 / 20 / 50 м−13,0 / −18,2 / −16,4−0,9 / −1,2 / −1,8−0,8 / −1,0 / −1,4
min w, седловина 10 / 20 / 50 м−14,5 / −18,2 / −16,4−0,6 / −0,9 / −1,5−0,6 / −0,7 / −1,1
среднее w в зоне, старт 10 / 50 / 100 м+0,13 / −0,75 / −1,01−0,24 / −0,84 / −1,07−0,16 / −0,42 / −0,57
возвратных точек, старт 10 / 20 м15,9 / 10,2 %3,2 / 1,8 %3,0 / 1,8 %
возвратных точек, седловина 10 / 20 м32,7 / 27,0 %17,0 / 13,8 %14,7 / 12,9 %
min продольной, старт / седловина 10 м−4,7 / −4,7−0,5 / −2,2−0,5 / −2,2

air от поля по w отличается и фоновым опусканием погоды (−0,1…−0,5 м/с, у земли гаснет). При 3 м/с меняется только min w (рывки до проверки болтанки: −0,6 → −0,3 м/с на 10 м). Остаток возвратных точек сверх поля — эвристика там, где пузырь не разрешён: область 400 м вне окон и низкие бугры (r ≲ 70 м) в окне 50 м, добавка до −1,7 м/с. С болтанкой (*_turb_air, 40 моментов в точке; точки с lee_f > 0,5 и ΔU > 1 м/с): U_H 10–11 м/с, ΔU 3–7 м/с; на 10 м σ_w/ΔU 0,16–0,18 (≈ 0,14 слоя смешения + механическая), на 50–100 м 0,4–0,6 — там больше механическая болтанка сдвига над следом («Чего не умеет»); 3σ_w/U_H 0,3–0,5, крайний минимум из ~3000 значений (w − w поля)/U_H −0,54…−0,74 (≈ 4σ гауссова хвоста механической болтанки, не рывки: рывок ≤ 0,42 ΔU).

Пузырь отрыва (am08v_resolved.py → am08v/resolved.md; точки зоны lee_f > 0 на 10–50 м, 9 м/с, три области): доля точек, где поле само течёт назад, по L/dx = 2,8 r/dx — в окнах 50/100 м 0–2: 0 %, 2–4: 2 %, 4–6: 16 %, 6–8: 15 %, 8–10: 30 %, ≥ 10: 32 %; на области 400 м (L/dx < 4) — 0–3 %. Отсюда field_resolved_cells = [4, 8]: ниже 4 клеток решатель пузыря не даёт, с 8 — частота насыщается; минимум u_прод/U_H поля в окнах −0,1…−0,43 (Menke: −0,22 ± 0,08).

Против базы AM-00 — наблюдение, не приёмка (решение пользователя: приёмка по физике, σ_w = 0,14·ΔU, Menke 2019; turb_probe.gd --only=lee, tools/research/air_turb/out/am08v/): σ_w с полем Синюха / Бияготы / Аушкуль 0,86 / 0,77 / 0,44 м/с (база 1,14 / 1,08 / 1,27, до AM-08в 2,05 / 2,04 / 1,66), рывков выше 3σ 5,0 / 2,3 / 1,0 в минуту (база 18 / 7,3 / 26; до AM-08в 15 / 6,3 / 7,7); среднее w без термиков −0,07 / −0,18 / −0,15 при w_mech поля + фон 0,03 / −0,23 / −0,14. Слабее базы на 25–65 %: ΔU от ветра на уровне гребня (у Аушкуля гребень над точкой низкий — U_H мал) вместо U_out.

Цена: air_velocity_at с полем (Каянча, 75 м, способ AM-00) — 48–50 мкс против 15 без поля (до AM-08в на той же машине 48–50, tools/bench/air_velocity_bench.gd --kayancha; по сетке 6400 точек с полем 51–52 против 49 мкс, +4–7 %: в зоне отрыва — одна выборка поля на уровне гребня и sample_dx); спектр 8 октав ~11 мкс, turb_at ~7 мкс. Без поля — побитно прежняя (38 400 значений, 0 отличий). Пилот (3 точки × 120 Гц) — ~2 % одного ядра.

Чего не умеет

  • Шум переносится одним ветром (300 м над рельефом аналитики), не местным ветром поля: в тихом следе за гребнем вихри «пролетают» быстрее, чем в жизни (≈10 против 3–5 м/с) — в неподвижной точке частота порывов завышена; в полёте (12 м/с своей скорости) это слабее. У земли (ниже ~10 м) поправлено растяжением масштабов (AS-2, «Перенос вихрей у земли»); выше, в следе за гребнем — нет.
  • Шум не несёт волн короче 14 м: у земли (L_w ≈ z = 1,5–3 м) вся σ_w приходится на вихри 14–30 м — крупнее, чем в жизни, и крыло (размах ~10 м, ветер берётся в одной точке) получает её целиком; настоящий порыв масштаба z крыло осредняет по размаху (σ_w на крыле ≈ 0,6 σ_w в точке при L_w = 2–3 м, размахе 10 м). Это граница модели полёта, не воздуха (AS-2, отчёт).
  • Средний w у земли — w_mech поля, отмасштабированный лог-профилем от первой клетки: на склоне старта 0–0,5 от U·∇h настоящего рельефа (у земли поток идёт вдоль склона, w = U·∇h); сетка 50 м склон 15–20° передаёт не полностью.
  • σ у земли — гауссов шум: при σ_u/U ≈ 0,35 (1,5 м, z0 = 0,1 м — подобие) обратный ветер на миг бывает (~0,4 % времени, событие > 2,7σ) — на 20-с отрезке редко.
  • z0 = 0,1 м (луг) для всего поля: над лесом механическая болтанка занижена (u* ∝ 1/ln(z/z0)).
  • Признак отрыва и слой смешения — по среднему полю RANS-подобного решателя. Возвратное течение решатель даёт сам, если пузырь отрыва покрыт ≳ 8 клетками (окна 50/100 м за гребнями выше ~70/140 м: у старта Каянчи при 9 м/с минимум продольной −0,5 м/с, у седловины −2,2 м/с ≈ 0,2 U_H, как в Perdigão); мельче (область 400 м, низкие бугры в окнах) — эвристика 0,22·U_H одной формой (e^(−agl/0,4r)), без длины и формы пузыря. Переход 4–8 клеток — smoothstep, не замер решателя на каждом гребне.
  • Пузырь отрыва — по данным одной гряды (Perdigão: две параллельные гряды ~250 м, лес, нейтраль); на пологом подветренном склоне, где реального отрыва нет, эвристика (на грубой сетке) всё равно даёт обратный поток, если поле показывает след (lee_f > 0).
  • Механическая болтанка в слое сдвига над следом (l·|∂U/∂z|) больше болтанки слоя смешения: на 50–100 м за гребнем σ_w/ΔU 0,4–0,6 вместо 0,14 (Онгудай 9 м/с, «Числа»); это замыкание длины перемешивания, не эвристика зоны — не подгонялось.
  • Рывки — детерминированный шум заданной формы (не LES); их хвост распределения не выведен из физики, только амплитуда (пик 3σ_w = 0,42 ΔU), нулевое среднее и доля в σ_w.
  • Аналитика (без поля, вне поля, off, полоса края) — прежняя эвристика: опускание 0,7·u·lee и рывки 1,0·u·lee (danger_sink_per_wind, burst_per_wind) — от ощущения пилота, не из физики, в 3–5 раз сильнее слоя смешения (за гребнем при 9 м/с до −1,7·u = −10…−17 м/с); решение пользователя — не менять (AM-08в меняет только путь с полем).
  • Горизонтальные масштабы > 2,5 км шум не несёт отдельно — их доля дисперсии в верхних октавах.