Files
CFDManager/docs/theory/2d_solver/bench
NotBigGhostandClaude Opus 5 8d17bc8014 Валидационная кампания на 115 прогонов и Docker-образ для сервера
bench/ — список сценариев (порождается gen_scenarios.py, а не правится
руками), драйверы под Linux и Windows, предполётная проверка и описание.
Девять групп: эталоны первоисточников, цилиндр против литературы, модели
стенки и субсеточность, профили крыла, сложная и множественная геометрия,
границы домена, старт и время жизни, внутренние инварианты, сверхмелкие
сетки до 4096×2048. Стоимость планируется в обновлениях узлов —
единственной переносимой между машинами мере; в часы драйвер переводит её
по фактическим MLUPS, которые замеряет на месте (--calibrate). Итого
3.76e14 обновлений, ≈90 часов на 4070 Ti.

Гифки пишутся на всю длительность прогона в реальном времени, 10 кадр/с.
Число кадров этим задано жёстко, поэтому размер регулируется только кадром:
замерено 0.103 байта на пиксель после LZW, отсюда бюджет кадры×пиксели с
нижней границей по ширине. Итог 4.4 ГБ, самый тяжёлый файл 226 МБ.

preflight.py гоняет каждый сценарий на два шага. Окупился сразу: поймал,
что вся группа сверхмелких сеток падала на пределе GPU, а девять прогонов
группы F передавали --body-x дважды. Оба отказа проявились бы только на
сервере, часов через двадцать после старта кампании.

Docker: двухстадийная сборка, в образе libvulkan1 и vulkan-tools.
NVIDIA Container Toolkit подкладывает Vulkan-ICD только при graphics в
NVIDIA_DRIVER_CAPABILITIES — без него wgpu не увидит карту, поэтому
capabilities прописаны в образе, а vulkaninfo лежит внутрь для проверки.
ENTRYPOINT — драйвер кампании, CMD по умолчанию --dry-run, чтобы случайный
docker run не запустил сточасовую задачу. Проверено локально: образ
собирается, смоук проходит с монтированием результатов на хост, физика
совпадает с хостовой до последней цифры, --backend gpu без карты отказывает
явно. GPU-путь в контейнере проверяется только на машине с картой.

В README поправлено разделение вкладов в продольную акустику: измерением
2×2 показано, что 74.9% → 1.1% даёт ОДНОРОДНЫЙ СТАРТ, причём при полностью
выключенной губке, а губка снимает лишь остаток (1.12% → 0.83% при
утроении длины). При старте из покоя губка не помогает совсем — у стоячей
четвертьволновой моды там узел давления, где вязкость её не трогает.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 17:36:40 +03:00
..

Валидационная кампания

115 прогонов, ≈90 часов на RTX 4070 Ti. Проверяет решатель по трём независимым линиям: эталонам из статей авторов метода, литературе по обтеканию тел и внутренним инвариантам самой схемы. Каждый прогон кладёт логи, ряды, машиночитаемую сводку и гифку в собственную папку.

Быстрый старт на сервере

docker build -t kbc2d docs/theory/2d_solver
docker run --rm --gpus all --entrypoint python3 kbc2d preflight.py   # сценарии стартуют
docker run --rm --gpus all kbc2d --calibrate                          # GPU виден
docker run -d --gpus all -v "$PWD/out:/work/bench/out" --name kbc2d kbc2d --resume
docker logs -f kbc2d

Первым делом стоит убедиться, что Vulkan внутри контейнера действительно видит карту:

docker run --rm --gpus all --entrypoint vulkaninfo kbc2d --summary | head -20

Если адаптеров ноль — почти наверняка дело в NVIDIA_DRIVER_CAPABILITIES: Container Toolkit подкладывает Vulkan-ICD только при наличии graphics в списке. В образе это прописано, но может быть переопределено снаружи.

Без Docker

cargo build --release                 # из docs/theory/2d_solver
cd bench
python preflight.py                   # каждый сценарий стартует на два шага
./run_campaign.sh --calibrate         # замерить MLUPS этой машины
KBC2D_GPU_MLUPS=1400 ./run_campaign.sh --dry-run
./run_campaign.sh --resume

Под Windows то же самое делает run_campaign.ps1 (тот же scenarios.json); он нужен для локальной отладки обвязки, целевая машина — Linux.

Ключи драйвера

ключ что делает
--dry-run печатает смету: что, сколько шагов, сколько часов. Ничего не считает
--calibrate три коротких прогона, замер фактических MLUPS этой машины
--smoke три самых дешёвых прогона: проверить обвязку, а не физику
--resume пропускает прогоны, у которых уже есть summary.json
--group A,B только выбранные группы
--only cyl_re150 по подстроке идентификатора
--budget-hours 24 остановиться, когда время выйдет

Прогон, который упал или развалился, помечается в сводке и не останавливает кампанию: группы E и G специально ищут предел устойчивости, там развал — ожидаемый результат.

Что где лежит

out/summary.csv          сводная таблица: id, группа, статус, секунды, стоимость
out/campaign.log         журнал с отметками времени
out/<id>/cmd.txt         точная команда, которой прогон был запущен
out/<id>/log.txt         полный вывод
out/<id>/report.txt      только итоговый отчёт
out/<id>/series.csv      временные ряды по шагам
out/<id>/summary.json    ключевые метрики машиночитаемо — с этого удобно начинать разбор
out/<id>/*.gif           анимация
out/<id>/*_case.csv      энергия, энстрофия, палинстрофия (эталонные течения)
out/<id>/xt_*.csv        x–t диаграммы (группа G)

Группы

прогонов что проверяется
A 12 эталоны первоисточников: Тейлор–Грин (второй порядок сходимости; при фиксированном u₀ — полка O(Ma²)), сдвиговый слой Re=3·10⁴, затухающая турбулентность
B 16 цилиндр против литературы: стационар Re=20/40, дорожка Re=100…300, разрешение D=16…128, блокировка Ny/D=6…32 с экстраполяцией к бесконечной среде
C 20 модели стенки и субсеточность: hrr / grad / bouzidi / staircase при Re=150 и 2000, плюс сдвиг тела внутри клетки на 0 / ¼ / ½ для всех четырёх
D 14 профили крыла: поляра NACA 0012, Re-серия, изгиб 4412, сходимость по хорде
E 12 сложная и множественная геометрия: тандем, решётка, перфорация, многоэлементный профиль, сверхтонкая пластина, клин, зазубренная кромка
F 9 поле влияния и границы домена: отступы до входа и выхода, вложенный патч
G 14 старт, акустика, время жизни: x–t диаграммы, губки, предел по Re, прогон на 10⁷ шагов
H 9 инварианты: симметрия, зеркальность, зависимость от числа Маха, расхождение f32 против f64
I 9 сверхмелкие сетки 4096×2048 по всем формам и композиции из трёх тел

Стоимость и время

Стоимость каждого прогона хранится в обновлениях узлов (nodes_per_step × steps) — это единственная мера, переносимая между машинами. Часы драйвер получает, поделив её на MLUPS. Оценки по умолчанию исходят из 1200 MLUPS на GPU и 22 на CPU; --calibrate обязателен, потому что эти числа взяты с другой машины.

Пересобрать список с другой длительностью:

python gen_scenarios.py --scale 3      # все прогоны в полтора раза длиннее (≈135 ч)

Что известно заранее

  • Точность f32. Замерено на Тейлоре–Грине: GPU совпадает с CPU/f64, пока истинная ошибка выше ~10⁻³, и промахивается в 40 раз, когда она ниже. Поэтому исследования сходимости из группы A идут на CPU — это указано в самих сценариях.
  • Развалы ожидаемы в группе G (предел по Re) и у прогона A09_shear_n512_lbgk: LBGK при Re=3·10⁴ обязан развалиться там, где KBC доживает — это и есть проверяемое утверждение.
  • Гифки пишутся на всю длительность прогона, в реальном времени, 10 кадр/с. Число кадров этим задано жёстко (у самого длинного прогона их 16 666), поэтому единственный рычаг — размер кадра. Замерено: 0.103 байта на пиксель после LZW; отсюда бюджет кадры×пиксели ≤ 1.5·10⁹ на гифку, но ширина не опускается ниже 480 пикселей. Итог по кампании: ≈4.4 ГБ, самый тяжёлый файл 226 МБ.
  • Перед запуском имеет смысл прогнать предполётную проверку: каждый сценарий стартует на два шага, что ловит опечатки в ключах и несовместимые сочетания до того, как кампания уйдёт считать на девяносто часов. Именно она поймала, что вся группа I падала на пределе GPU в 65535 рабочих групп на измерение.