Files
CFDManager/docs/theory/2d_solver/bench/run_campaign.ps1
T
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

109 lines
5.1 KiB
PowerShell
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Драйвер валидационной кампании для Windows. Делает ровно то же, что run_campaign.sh, и
# читает тот же scenarios.json — целевая машина Linux, а этот вариант нужен для локальной
# отладки обвязки.
#
# .\run_campaign.ps1 -DryRun
# .\run_campaign.ps1 -Calibrate
# .\run_campaign.ps1 -Smoke
# .\run_campaign.ps1 -Resume -Group A,B
# .\run_campaign.ps1 -Only cyl_re150 -BudgetHours 6
[CmdletBinding()]
param(
[switch]$DryRun,
[switch]$Resume,
[switch]$Calibrate,
[switch]$Smoke,
[string[]]$Group,
[string]$Only,
[double]$BudgetHours = 0,
[string]$Bin = "..\target\release\kbc2d.exe",
[string]$Scenarios = "scenarios.json",
[string]$Out = "out",
[double]$GpuMlups = 1200,
[double]$CpuMlups = 22
)
Set-Location $PSScriptRoot
if (-not (Test-Path $Bin)) { throw "не найден бинарь решателя: $Bin (собери cargo build --release)" }
if (-not (Test-Path $Scenarios)) { throw "не найден список сценариев: $Scenarios" }
if ($Calibrate) {
"Калибровка на этой машине (три коротких прогона)…"
foreach ($c in @(@(240,120,'gpu'), @(960,480,'gpu'), @(480,240,'cpu'))) {
$o = & $Bin --nx $c[0] --ny $c[1] --size 16 --refine 1 --steps 3000 `
--report-every 3000 --verbose full --backend $c[2] 2>$null
$m = ($o | Select-String 'MLUPS' | Select-Object -Last 1)
" $($c[0])x$($c[1]) на $($c[2]): $(if ($m) { ($m -replace '.*?([0-9.]+) MLUPS.*','$1') } else { 'не измерено' })"
}
""
"Подставь замеренное: .\run_campaign.ps1 -DryRun -GpuMlups <число> -CpuMlups <число>"
return
}
$runs = (Get-Content $Scenarios -Raw -Encoding UTF8 | ConvertFrom-Json).runs
if ($Group) { $g = $Group | ForEach-Object { $_.ToUpper() }; $runs = $runs | Where-Object { $g -contains $_.group.ToUpper() } }
if ($Only) { $runs = $runs | Where-Object { $_.id -like "*$Only*" } }
if ($Smoke) { $runs = $runs | Sort-Object cost | Select-Object -First 3 }
if (-not $runs) { "под выборку не попал ни один прогон"; return }
$totalCost = ($runs | Measure-Object -Property cost -Sum).Sum
$estH = $totalCost / ($GpuMlups * 1e6) / 3600
"прогонов: $($runs.Count) обновлений узлов: {0:e3} ≈ {1:F1} ч при $GpuMlups MLUPS" -f $totalCost, $estH
"результаты: $Out\<id>\"
""
if ($DryRun) {
"{0,-28} {1,-3} {2,-4} {3,12} {4,8} {5}" -f 'id','гр','бэк','шагов','часов','название'
foreach ($r in $runs) {
$m = if ($r.backend -eq 'cpu') { $CpuMlups } else { $GpuMlups }
"{0,-28} {1,-3} {2,-4} {3,12} {4,8:F2} {5}" -f $r.id, $r.group, $r.backend, $r.steps, ($r.cost/($m*1e6)/3600), $r.title
}
return
}
New-Item -ItemType Directory -Force $Out | Out-Null
$summary = Join-Path $Out 'summary.csv'
$log = Join-Path $Out 'campaign.log'
if (-not (Test-Path $summary)) { 'id,group,backend,status,seconds,steps,cost,title' | Out-File $summary -Encoding utf8 }
$started = Get-Date
foreach ($r in $runs) {
$dir = Join-Path $Out $r.id
if ($Resume -and (Test-Path (Join-Path $dir 'summary.json'))) { "· $($r.id) — уже посчитан, пропускаю"; continue }
if ($BudgetHours -gt 0 -and ((Get-Date) - $started).TotalHours -ge $BudgetHours) {
"бюджет $BudgetHours ч исчерпан, останавливаюсь"; break
}
New-Item -ItemType Directory -Force $dir | Out-Null
# относительные имена файлов из сценария кладём внутрь папки прогона
$argv = @()
for ($i = 0; $i -lt $r.args.Count; $i++) {
$v = $r.args[$i]
if ($i -gt 0 -and @('--gif','--xt','--case-csv') -contains $r.args[$i-1]) { $v = Join-Path $dir $v }
$argv += $v
}
$argv += @('--summary', (Join-Path $dir 'summary.json'),
'--csv', (Join-Path $dir 'series.csv'),
'--verbose','full','--report-every','5000')
"$Bin $($argv -join ' ')" | Out-File (Join-Path $dir 'cmd.txt') -Encoding utf8
"▶ $($r.id) ($($r.group), $($r.backend)) $($r.title)"
$t0 = Get-Date
& $Bin @argv 2>&1 | Out-File (Join-Path $dir 'log.txt') -Encoding utf8
$st = if ($LASTEXITCODE -eq 0) { 'ok' } else { 'fail' }
$dt = [int]((Get-Date) - $t0).TotalSeconds
if (Select-String -Path (Join-Path $dir 'log.txt') -Pattern 'РАЗВАЛИЛСЯ' -Quiet) { $st = 'blewup' }
$rep = Get-Content (Join-Path $dir 'log.txt') -Encoding utf8
$k = ($rep | Select-String 'ИТОГОВЫЙ ОТЧЁТ' | Select-Object -First 1).LineNumber
if ($k) { $rep[($k-1)..($rep.Count-1)] | Out-File (Join-Path $dir 'report.txt') -Encoding utf8 }
'{0},{1},{2},{3},{4},{5},{6},"{7}"' -f $r.id,$r.group,$r.backend,$st,$dt,$r.steps,$r.cost,$r.title |
Out-File $summary -Append -Encoding utf8
"$(Get-Date -Format o) $($r.id) $st ${dt}s" | Out-File $log -Append -Encoding utf8
" → $st за $dt с"
}
""
"готово. сводная таблица: $summary"