Батчинг GPU-чтения, компенсированные суммы и HRR-стенка на обоих бэкендах
БАТЧИНГ. Раньше после каждого шага делался map_async + poll(Wait) ради 48 байт итогов: на сетке 240×120 счёт упирался в 863 шаг/с при том, что сам счёт занимал 0.27 мс из 1.16 — три четверти времени машина стояла. Теперь k_stats2 пишет итоги в слот истории step % 128, а хост читает пачкой. Контракт бэкенда изменён с пошагового step() на advance(&mut out) + flush(): записи дописываются в вектор, синхронизация происходит только там, где дальше нужны актуальные данные (кадр гифки, живая строка отчёта). Замер: 240×120 — 6715 шаг/с против 863 (7.8×); пропускная способность 195 MLUPS против прежних 105. Паритет CPU/GPU сохранён: Cd 1.798 против 1.799, ⟨ρ⟩ 1.00090 против 1.00091. КОМПЕНСИРОВАННОЕ СУММИРОВАНИЕ (Кэхена–Ноймайера) в редукциях статистики и в сумме сил. Именно там теряется основная точность f32: наивная сумма по 10^5…10^7 узлам съедает ~log2(N) бит. Аппаратного f64 на целевом железе нет (в WGSL типа f64 не существует вовсе, а локальная Iris Xe сообщает shaderFloat64 = false), поэтому компенсация — единственный доступный способ. РАЗВАЛ СЧЁТА теперь ловится по уже посчитанным max|u| и ⟨ρ⟩, а не полным проходом по полю: на сетке 4096×2048 такой проход тянет с устройства сотни мегабайт, и делать его регулярно нельзя. Полная проверка осталась одна, в конце. HRR-СТЕНКА (--wall hrr, теперь умолчание). Условие Града — это ряд Эрмита, оборванный на 2-м порядке (ρ, u, Π). HRR продолжает его на третий, вычисляя коэффициенты не из популяций (их на стенке как раз и не хватает), а рекурсивно из уже известных: a₃_xxy = 2·u_x·a₂_xy + u_y·a₂_xx, a₃_xyy = 2·u_y·a₂_xy + u_x·a₂_yy В D2Q9 a₃_xxx и a₃_yyy решёткой не поддерживаются и отбрасываются. Третий порядок не трогает ρ, ρu и Π — соответствующие моменты весов обнуляются по симметрии, — что закреплено тестом. Моментная стенка перенесена на GPU (раньше её там не было вовсе, бэкенд отказывался запускаться): буфер индекса граничных узлов плюс ядро k_moment_wall. Скорость на момент t берётся из пост-столкновительного поля — столкновение сохраняет ρ и ρu, поэтому отдельное хранилище прошлого шага не нужно ни на одном бэкенде. Добавлена проверка лимита storage-биндингов адаптера: моментной стенке нужно 9 против 8 гарантируемых. Паритет на HRR: Cd 1.847 (CPU) против 1.848 (GPU). Три модели стенки на одной постановке дают 1.847 (hrr) / 1.845 (grad) / 1.856 (bouzidi). 33 теста, обе сборки чисты. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -275,7 +275,9 @@ impl Level {
|
||||
}
|
||||
}
|
||||
|
||||
/// ГРАНИЧНОЕ УСЛОВИЕ ГРАДА на теле (Dorschner и др., JFM 801 (2016), прил. B).
|
||||
/// ГРАНИЧНОЕ УСЛОВИЕ НА МОМЕНТАХ (Dorschner и др., JFM 801 (2016), прил. B).
|
||||
///
|
||||
/// `third_order` — продолжать ли ряд Эрмита рекурсивно до третьего порядка (HRR).
|
||||
///
|
||||
/// В отличие от Bouzidi, работающего полинково, здесь условие ставится сразу на весь
|
||||
/// граничный узел и не на популяции, а на моменты:
|
||||
@@ -286,7 +288,7 @@ impl Level {
|
||||
///
|
||||
/// после чего недостающие популяции собираются приближением Града (2.13). Скорости
|
||||
/// соседей и градиенты берутся с прошлого шага — см. `u_prev`.
|
||||
fn apply_grad_wall(&mut self) {
|
||||
fn apply_moment_wall(&mut self, third_order: bool) {
|
||||
let nx = self.nx;
|
||||
// стенка неподвижна; для подвижного тела сюда пойдёт её скорость на линке,
|
||||
// и добавится динамическая часть плотности (B 4)
|
||||
@@ -325,7 +327,17 @@ impl Level {
|
||||
}
|
||||
|
||||
let (dudx, dudy, dvdx, dvdy) = self.grad_u_at_t(node);
|
||||
let g = math::grad_wall(rho, ux, uy, dudx, dudy, dvdx, dvdy, self.beta[node % nx]);
|
||||
let g = math::moment_wall(
|
||||
rho,
|
||||
ux,
|
||||
uy,
|
||||
dudx,
|
||||
dudy,
|
||||
dvdx,
|
||||
dvdy,
|
||||
self.beta[node % nx],
|
||||
third_order,
|
||||
);
|
||||
for i in 0..Q {
|
||||
if missing[i] {
|
||||
self.f[node][i] = g[i];
|
||||
@@ -373,7 +385,8 @@ impl Level {
|
||||
fn apply_wall(&mut self, model: WallModel) {
|
||||
match model {
|
||||
WallModel::Bouzidi => self.apply_bouzidi(),
|
||||
WallModel::Grad => self.apply_grad_wall(),
|
||||
WallModel::Grad => self.apply_moment_wall(false),
|
||||
WallModel::Hrr => self.apply_moment_wall(true),
|
||||
WallModel::Staircase => self.apply_staircase(),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user