Клеточный автомат тепла и массы — общий итог (2D-разрез хребта)
Прототип по карточке docs/archive/plan/heat-ca-prototype.md. Код, запуск и смысл картинок — README.md.
Хребет 500 м, крутой склон слева (до ~33°), пологий справа (до ~16°); разрез 6,4 × 3,2 км;
фон почти безразличный (θ растёт на 1 К/км, т. е. T падает на ~8,8 К/км); нагрев склонов до 250 Вт/м².
Счёт — GPU (RTX 4070 SUPER, CuPy + CUDA Graph, float32); в игре это будут вычислительные шейдеры
Vulkan (должно работать на AMD), CuPy — только для прототипа.
Правила автомата (простыми словами)
- Земля греет нижнюю клетку столбца (по солнцу на склон).
- Поток вверх через грань ускоряется, если клетки теплее фона на своей высоте; поднятый воздух несёт свою температуру — на инверсии быстро становится холоднее соседей, подъём гаснет сам.
- Потоки переносят сами себя, вязкость и трение сглаживают; губка у потолка.
- Недостача массы в клетке «подсасывает» соседей (давление) — многосеточно (
mg) или чисто локально (acoustic, «медленный звук», без глобального решателя). - Масса и тепло переходят через грани парными потоками (что ушло из одной — пришло в другую) → сохранение точное; плюс теплопроводность, плюс слабое выхолаживание к фону (τ = 2 ч, явный сток).
Это конвекция Буссинеска, записанная локально: п. 2 — плавучесть относительно фона, п. 4 — уравнение Пуассона для давления (итерации «клетка смотрит на соседей» = Якоби).
Сценарии: результат против условий приёмки (клетка 50 м)
| Сценарий | Макс. подъём | Приток у подножия (к склону +) | Опускание | Высота подъёма | Установление | Приёмка |
|---|---|---|---|---|---|---|
| 1. Солнце на крутой склон | 1,65 м/с над верхней третью солнечного склона (x = 2,48 км, 475 м над землёй) | +0,49 м/с; вверх по склону +0,53 м/с | в долине слева в среднем −0,08 (до −0,23) м/с; над теневым склоном −0,02 м/с | 1400 м над подножием | 5754 шага (210 мин модели), 3,1 с GPU | да (п. 1, 2, 6) |
| 2. Оба склона | 2,47 м/с над гребнем (x = 3,33 км, чуть за гребнем — склоны разные) | +0,48 / +0,44 м/с; вверх по склонам +0,65 / +0,81 | долина −0,10 м/с, по бокам столба до −0,75 м/с | 1800 м | 6302 шага, 3,4 с | да (п. 1, 3, 6) |
| 2б. Симметричный хребет | 2,08 м/с строго над гребнем | +0,48 / +0,48 м/с | −0,08 м/с | 1600 м | 6302 шага, 3,4 с | симметрия до 6·10⁻⁶ м/с |
| 3. Ветер 3 м/с слева | полное поле 1,40 м/с (наветренный склон); тепловая добавка 0,35 м/с | тепловая добавка к притоку +0,26 м/с | за гребнем — нисходящая часть горной волны до −1,1 м/с | тепловая добавка до 1900 м | 5480 шагов, 3,1 с | да (п. 1, 5, 6) |
| 4. Инверсия 1500–1800 м | 2,33 м/с | +0,49 / +0,44 м/с | −0,09 м/с | 1550 м (без инверсии 1800), тёплый воздух до 1800 | 5206 шагов, 2,8 с | да (п. 1, 3, 4, 6) |
- Сохранение (п. 1): невязка массы ~10⁻¹¹ от массы области, тепла ~10⁻⁵ от суммарного нагрева
(float32); допуск 0,1 % выполнен с запасом на порядки. Недовыравненная масса в клетке ~2·10⁻⁸
(многосеточно) или 0,1–2 % (локальный режим, ∝ 1/c²). Графики —
mass_balance.png. - П. 2 (сценарий 1): подъём над прогретым склоном и гребнем, приток у подножия к склону, возвратный
поток на 0,8–1,3 км и опускание над долиной —
s1_sun_one_slope/flow.png,evolution.mp4. - П. 3: схождение и столб подъёма над гребнем; на несимметричном хребте столб сдвинут на ~0,5 км к
пологому склону (с него приходит больше тёплого воздуха); на симметричном (
s2_sym) — строго над гребнем, картина зеркальная до 10⁻⁵. - П. 4 (инверсия): подъём гаснет у нижней кромки инверсии (1550 м против 1800 без неё), под
инверсией — растекание в стороны до 1,6 м/с и холодная «шапка» перебега; выше инверсии |w| ≤ 0,03 м/с,
|θ′| ≤ 0,06 К —
s4_inversion/flow.png,temp.png. - П. 5 (ветер): картина не разваливается; полное поле — это прежде всего обтекание хребта
устойчивым воздухом (горная волна: подъём на наветренном склоне и над долиной перед ним, сильное
опускание за гребнем). Тепловая часть (с нагревом минус без) — наклонённый по ветру подъём от
наветренного склона через гребень (dx/dz ≈ +0,5), тёплый слой сдувается за хребет —
s3_wind/flow_thermal.png,flow_no_heating.png. При 4 м/с тепловую часть почти не видно — её забивает волна (честно: при ветре 4+ м/с и такой устойчивости тепло склона — поправка ~0,3 м/с). - П. 6: «пилы» нет, шаг устойчивый (перенос: dt ≤ 0,35·клетка/8 м/с; теплопроводность: ≤ 0,2·клетка²/K), установление за ~200 мин модельного времени — 1,2–15 тыс. шагов по клетке.
Похоже ли на слова пилота. Да по всем трём пунктам карточки: подъём у прогретого склона и над гребнем (максимум — над верхней частью склона, 0,4–0,7 км над землёй), приток снизу к подножию (~0,5 м/с, вверх по склону ~0,5–0,8 м/с — склоновый ветер), потолок на инверсии (столб останавливается у её нижней кромки и растекается). Опускание над долиной слабое и широкое (−0,1…−0,2 м/с), рядом со столбом — сильнее (до −0,75 м/с). Числа порядка настоящих склоновых циркуляций; сила зависит от вязкости/теплопроводности (30 м²/с — грубое «перемешивание»).
Сходимость по клетке и время (сценарий 1)
Сходимость по клетке и время на GPU (NVIDIA GeForce RTX 4070 SUPER, CuPy + CUDA Graph, float32; сценарий 1; выравнивание массы — многосеточный Пуассон, 2 V-цикла на шаг)
| Клетка | Сетка (воздушных) | dt, с | Шагов до установления | Время до установления, с | мс/шаг | Память GPU, МБ | Макс. подъём, м/с (где) | Приток у подножия, м/с | Вверх по склону, м/с | Опускание в долине (ср.), м/с | Высота подъёма, м |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 200 м | 32×16 (489) | 8.75 | 1242 (181 мин) | 0.61 | 0.491 | 0.2 | 1.21 (2.30 км, 500 м) | +0.71 | +0.02 | -0.091 | 1400 |
| 100 м | 64×32 (1948) | 4.38 | 2740 (200 мин) | 1.37 | 0.500 | 0.7 | 1.42 (2.45 км, 450 м) | +0.55 | +0.47 | -0.082 | 1400 |
| 50 м | 128×64 (7792) | 2.19 | 5754 (210 мин) | 3.06 | 0.532 | 2.9 | 1.65 (2.48 км, 475 м) | +0.49 | +0.53 | -0.075 | 1400 |
| 25 м | 256×128 (31168) | 1.09 | 12078 (220 мин) | 7.05 | 0.583 | 13.3 | 1.81 (2.51 км, 488 м) | +0.44 | +0.61 | -0.072 | 1425 |
| 12.5 м | 512×256 (124672) | 0.55 | 24134 (220 мин) | 18.22 | 0.755 | 53.9 | 1.90 (2.53 км, 481 м) | +0.41 | +0.65 | -0.071 | 1425 |
| 6.25 м | 1024×512 (498688) | 0.26 | 50688 (220 мин) | 72.41 | 1.428 | 216.1 | 1.95 (2.55 км, 472 м) | +0.40 | +0.67 | -0.070 | 1425 |
| 3.125 м (только замер скорости) | 2048×1024 | 6.034 | 864 |
Оценка для 3D (квадрат 40×40 км × 32 уровня, та же физика, тот же GPU): мс/шаг = max(0.49 мс — пол накладных расходов, 2.88 нс на клетку·шаг по самой большой 2D-сетке × 1.5 за 3D); шагов — как в 2D при той же клетке; память — 432 байт на клетку × 1,3.
| Клетка | Ячеек 3D | мс/шаг (оценка) | Шагов | До установления, с (оценка) | Память как в прототипе, МБ | Память компактно (~64 байт/клетка), МБ |
|---|---|---|---|---|---|---|
| 200 м | 1.28 млн | 5.5 | 1242 | 7 | 686 | 78 |
| 100 м | 5.12 млн | 22.1 | 2740 | 61 | 2744 | 312 |
| 50 м | 20.48 млн | 88.4 | 5754 | 509 | 10974 | 1250 |
| 25 м | 81.92 млн | 353.5 | 12078 | 4270 | 43898 | 5000 |
Прототип держит в пуле CuPy все временные массивы шага (CUDA Graph) — отсюда ~430 байт на клетку; в вычислительном шейдере нужно ~12–16 чисел float32 на клетку (u, v, w, m, H, p, правая часть, невязка, уровни многосеточного) ≈ 64 байт.
Сходимость по клетке и время на GPU (NVIDIA GeForce RTX 4070 SUPER, CuPy + CUDA Graph, float32; сценарий 1; выравнивание массы — чисто локально («медленный звук» 60 м/с))
| Клетка | Сетка (воздушных) | dt, с | Шагов до установления | Время до установления, с | мс/шаг | Память GPU, МБ | Макс. подъём, м/с (где) | Приток у подножия, м/с | Вверх по склону, м/с | Опускание в долине (ср.), м/с | Высота подъёма, м |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 200 м | 32×16 (489) | 8.75 | 1242 (181 мин) | 0.45 | 0.364 | 0.2 | 1.17 (2.30 км, 500 м) | +0.74 | +0.01 | -0.121 | 1400 |
| 100 м | 64×32 (1948) | 4.38 | 2603 (190 мин) | 0.91 | 0.350 | 0.7 | 1.39 (2.45 км, 450 м) | +0.57 | +0.50 | -0.098 | 1400 |
| 50 м | 128×64 (7792) | 2.19 | 5754 (210 мин) | 2.06 | 0.358 | 2.9 | 1.64 (2.48 км, 475 м) | +0.51 | +0.54 | -0.084 | 1400 |
| 25 м | 256×128 (31168) | 1.09 | 11529 (210 мин) | 4.45 | 0.386 | 13.3 | 1.80 (2.51 км, 488 м) | +0.45 | +0.62 | -0.076 | 1425 |
| 12.5 м | 512×256 (124672) | 0.55 | 24134 (220 мин) | 13.50 | 0.559 | 53.9 | 1.90 (2.53 км, 481 м) | +0.42 | +0.65 | -0.073 | 1425 |
| 6.25 м | 1024×512 (498688) | 0.26 | 50688 (220 мин) | 63.51 | 1.253 | 216.1 | 1.95 (2.55 км, 472 м) | +0.40 | +0.67 | -0.071 | 1431 |
| 3.125 м (только замер скорости) | 2048×1024 | 5.735 | 864 |
Оценка для 3D (квадрат 40×40 км × 32 уровня, та же физика, тот же GPU): мс/шаг = max(0.35 мс — пол накладных расходов, 2.73 нс на клетку·шаг по самой большой 2D-сетке × 1.5 за 3D); шагов — как в 2D при той же клетке; память — 432 байт на клетку × 1,3.
| Клетка | Ячеек 3D | мс/шаг (оценка) | Шагов | До установления, с (оценка) | Память как в прототипе, МБ | Память компактно (~64 байт/клетка), МБ |
|---|---|---|---|---|---|---|
| 200 м | 1.28 млн | 5.3 | 1242 | 7 | 686 | 78 |
| 100 м | 5.12 млн | 21.0 | 2603 | 55 | 2744 | 312 |
| 50 м | 20.48 млн | 84.0 | 5754 | 483 | 10974 | 1250 |
| 25 м | 81.92 млн | 336.0 | 11529 | 3874 | 43898 | 5000 |
Прототип держит в пуле CuPy все временные массивы шага (CUDA Graph) — отсюда ~430 байт на клетку; в вычислительном шейдере нужно ~12–16 чисел float32 на клетку (u, v, w, m, H, p, правая часть, невязка, уровни многосеточного) ≈ 64 байт.
Сходимость по клетке (out/convergence/conv_w_profile.png, conv_flow.png): картина (где подъём,
приток, возвратный поток, опускание, высота подъёма 1400 м) одинакова от 200 до 6,25 м. Меняются:
- макс. подъём растёт с измельчением (1,21 → 1,42 → 1,65 → 1,81 → 1,90 → 1,95 м/с) — сходится как первый порядок, 50 м даёт ~85 % от предела;
- склоновый ветер вверх по склону: на 200 м его нет (+0,02 м/с — склон 2 клетками), на 100 м уже +0,47 (предел ~0,67);
- приток у подножия на грубых сетках завышен (0,71 на 200 м против ~0,40). Рекомендация: самая грубая клетка с правильной картиной — 100 м (есть склоновый ветер, подъём в нужном месте, ~75 % силы); 200 м годится только как фон (циркуляция долины без склонового слоя). Для 3D при загрузке — 200 м по всей области и 50–100 м в окне у пилота (как WF-14).
Время. Число шагов до установления ∝ 1/клетка (шаг по времени ∝ клетке, а устанавливается ~3 часа модельного времени — медленный прогрев долины). На малых сетках GPU ждёт запуски (пол ~0,4–0,5 мс/шаг даже с CUDA Graph), на 3,125 м (2 млн клеток) — 2,9 нс на клетку·шаг. Оценка 3D 40×40 км × 32 уровня: 200 м — ~7 с, 100 м — ~1 мин, 50 м — ~8 мин, 25 м — ~1 ч (тот же GPU; на слабой AMD — в 3–10 раз дольше). Память в компактной реализации ~64 байт/клетку: 78 МБ (200 м) … 1,25 ГБ (50 м).
Выравнивание массы: сколько итераций нужно
Выравнивание массы: сколько итераций нужно (сценарий 1, клетка 50 м, GPU)
| Способ | Итераций (подшагов) на шаг | Макс. |m−1| в клетке (недовыравнено) | Шагов до установления | мс/шаг | Макс. подъём, м/с | Приток у подножия, м/с | Высота подъёма, м | |—|—|—|—|—|—|—|—| | многосеточный V-цикл (Пуассон) | 1 | 5.9e-08 | 5754 | 0.392 | 1.65 | +0.49 | 1400 | | многосеточный V-цикл (Пуассон) | 2 | 2.2e-08 | 5754 | 0.530 | 1.65 | +0.49 | 1400 | | многосеточный V-цикл (Пуассон) | 4 | 2.0e-08 | 5754 | 0.812 | 1.65 | +0.49 | 1400 | | Якоби, локальные итерации давления, снимать накопленный избыток массы | 50 | расходимость: |v|max=nan на шаге 274 | | | | | | | Якоби, локальные итерации давления, без возврата накопленной массы | 50 | 3.0e-03 | 5754 | 0.297 | 1.65 | +0.49 | 1400 | | Якоби, локальные итерации давления, без возврата накопленной массы | 500 | 8.0e-04 | 5754 | 0.715 | 1.65 | +0.49 | 1400 | | локально: «медленный звук» c = 30 м/с | 3 | 2.3e-02 | 5754 | 0.288 | 1.65 | +0.50 | 1400 | | локально: «медленный звук» c = 60 м/с | 6 | 5.6e-03 | 5754 | 0.355 | 1.64 | +0.51 | 1400 | | локально: «медленный звук» c = 120 м/с | 11 | 1.3e-03 | 5754 | 0.468 | 1.63 | +0.50 | 1400 |
- Многосеточному хватает 1 V-цикла на шаг с тёплым стартом (недостача массы 6·10⁻⁸).
- Буквальная «клетка смотрит на соседей» (Якоби), если каждый шаг пытаться вернуть накопленную недостачу массы, разваливается (двойной счёт недостачи → раскачка); без возврата работает, но масса в клетках «плывёт» (0,1–0,3 %), и на больших сетках Якоби сходится всё медленнее (∝ N²).
- Лучший локальный вариант — «медленный звук» (
--pressure acoustic): недостача массы сама даёт давление, 3–11 подшагов по соседям на шаг, без глобального решателя; картина та же (подъём 1,63–1,65 м/с), быстрее многосеточного (0,29–0,47 против 0,39–0,53 мс/шаг), масса в клетке 0,1–2 %. Для вычислительного шейдера это самое удобное: только соседи, никаких уровней и редукций; на AMD переносимо.
Сравнение с решателем Пуассона из плана поля ветра
Выравнивание массы здесь — это то же уравнение Пуассона, что в docs/archive/plan/wind-field.md (∇·(∇λ) =
−∇·u₀, те же проводимости граней, многосеточный метод). Разница в том, что поле ветра решает его
один раз (диагностическое поле без инерции и плавучести: 15–30 итераций PCG), а автомат — на
каждом шаге (1 V-цикл × тысячи шагов) плюс перенос тепла и импульса. То есть автомат = решатель
поля ветра + плавучесть + время. Отсюда цена: при 200 м — секунды против долей секунды; при 50 м —
минуты против секунды. Зато автомат даёт то, чего поле по массе не даёт в принципе (§11.2
исследования): приток к прогретому склону, столб над гребнем, возвратный поток и опускание,
потолок на инверсии, наклон по ветру.
Вывод для игры
- Идея рабочая и физически честная: локальные правила дают правильную качественную картину и правильные порядки величин; сохранение точное; счёт устойчивый.
- Статичный прогон до установления при загрузке/смене часа реален только на грубой сетке: 3D 200 м — ~5–10 с на 4070 SUPER (на слабой AMD — до минуты), 100 м — ~1 мин (слишком долго для загрузки, можно фоном). 50 м по всей области — нет; 50 м в окне 8×8 км у пилота — ~30 с фоном.
- Ускорители: локальный «медленный звук» вместо многосеточного (проще шейдер, быстрее); старт не с покоя, а с прошлого часа (установление не с нуля, а на ~30–60 мин модельного времени — в 3–5 раз меньше шагов); грубая сетка для фона + мелкая в окне (WF-14).
- Что это даст пилоту поверх поля ветра по массе: склоновый ветер и приток снизу (трава/пыль тянутся к склону), широкое опускание над долиной, высоту «потолка» по инверсии, снос тепла ветром. Термики как пузыри и «провалы» — по-прежнему отдельная модель (автомат на 50–200 м их не разрешает).
- Рекомендация: не заменять план поля ветра, а рассматривать автомат как этап 2–3 связанной системы (§11.3): тот же решатель + плавучесть от солнца на склонах, 200 м по области, раз в игровой час, фоном; начать с 2D→3D прототипа на Vulkan с локальным режимом давления.
Слои как матрицы: итог опытов 1–6
Главный критерий — физическая корректность в заявленных границах модели. Дальше — точность против точной неподвижной точки и цена в 3D на GPU.
| Подход | Физика в ответе | Точность | 2D, 50 м | 3D 200 м / 100 м (4070 SUPER) | Где кончается применимость |
|---|---|---|---|---|---|
| Шаги по времени (автомат; эталон) | масса и тепло точно, полный импульс, нелокальное давление; есть переходные процессы | — | 3 с (до 1 % — 7 с) | 7–18 с / 1–2,5 мин | единственный с динамикой; дорог на мелкой клетке |
| 2. Пикар (Озеен + SIMPLEC + тепло) | то же, что у автомата; подгонки нет | ≤ 0,5 % по u, w; ~1 % по θ′ | 0,1–0,3 с | 2–5 с / 7–17 с | только установившееся среднее поле (перемешанный воздух) |
| 5. ADI-прогонки | то же | до 1 % за 100–190 раундов | 0,3–0,4 с | 6–16 с / 15–40 с | то же; ветер не реализован; итерация пока дорогая |
| 4. Матрицы перехода (гармоники + Андерсон) | то же | 1–2 % (ветер — до 14 % по w) | 0,1–0,5 с; повторов не больше при измельчении | 1–12 с / 5–48 с | память Андерсона 2,5 КБ/клетку (по области — только 200 м) |
| 6. Ядро струи | масса точно, тепло глобально; импульс только по оси; давление — проекция | ключевые числа 5–30 %, поле 0,6–0,75 | 1–2 мс | ~20 мс / ~80 мс | одна струя, без взаимодействий; с ветром грубо |
| 1 и 3. Линейные ядра и свёртка | линеаризованный Буссинеск | подъём втрое слабее | 0,03 мс | — | только подъём меньше 5 см/с |
Недостаток базы. Критерий установления абсолютный (0,02 м/с и 0,03 К за 10 мин), поэтому эталоны out/refs недоустановились: от точной неподвижной точки они отличаются на 3–8 % по u, w и на 10–23 % по θ′. Точные эталоны — exp2_picard/out/truth, exp4_transfer_sweeps/out/refs_long, exp5_adi/out/fixed. Критерий стоит сделать относительным или считать 8–10 ч.
Рекомендация для игры (Vulkan compute, AMD, клипмап WF-14):
- Единая модель «воздуха» — те же уравнения Буссинеска. «Поле на час» считать Пикаром (опыт 2): 200 м по всей области за ~2–5 с, окна 50–100 м вокруг пилота фоном, с тёплым стартом от грубого поля и от прошлого часа.
- Строительные блоки простые: поэлементные ядра, прогонки в разделяемой памяти, V-цикл. На AMD переносимо, но для линий длиннее 1024 точек нужна многопроходная прогонка.
- Шаги по времени (локальный режим «медленный звук») — только для переходов между часами.
- Ядро струи — начальное приближение и мгновенная оценка.
- Матрицы перехода — запасной вариант для окон, если упрёмся в сходимость Пикара.
- Все подходы дают только среднюю циркуляцию. Термики-пузыри и «провалы» остаются отдельной моделью.
- Следующий шаг физики — влага как ещё один переносимый скаляр: из неё база облаков, дальше диагностика гроз (CAPE).
Как воспроизвести:
cd tools/research/heat_ca
.venv/bin/python run.py all
.venv/bin/python study.py cells [--pressure acoustic]
.venv/bin/python study.py solvers
.venv/bin/python refs.pyКоманды для опытов — в README.md каждой папки exp*/. Все замеры времени — под flock /tmp/heat_ca_gpu.lock.
Картинки для статьи:
- база:
out/s1_sun_one_slope/flow.pngиevolution.mp4,out/s4_inversion/flow.pngиtemp.png,out/s3_wind/flow_thermal.png,out/convergence/conv_w_profile.png,out/solvers/solvers_w300.png; - опыт 1:
exp1_3_6_kernels/out/exp1_linearity_vs_strength.png; - опыт 6:
exp1_3_6_kernels/out/exp6_jet_anatomy.png; - опыт 2:
exp2_picard/out/fig_convergence.png; - опыт 4:
exp4_transfer_sweeps/out/passes_explained.png,streams_s4_inversion.png; - опыт 5:
exp5_adi/out/rounds_lines_adi.png.
Данные вне git: снимки повторов опыта 4 для 25 и 12,5 м (~140 МБ) — локально в exp4_transfer_sweeps/out/snaps/.