feat: add matched SRT channel closure for blocked AMR root
This commit is contained in:
1 parent
20ae677092
commit
5b4610441c
9 files changed
+504
-14
No files matched your search
@@ -0,0 +1,84 @@
|
||||
// Actual production step kernels under a CPU launch shim. No CUDA claim.
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#define __device__
|
||||
#define __constant__
|
||||
#define __global__
|
||||
#define __forceinline__ inline
|
||||
#define __launch_bounds__(...)
|
||||
#define LATTICE_D2Q9 0
|
||||
#define LATTICE_D3Q19 1
|
||||
#define LATTICE_MODEL 0
|
||||
#define NQ 9
|
||||
#define DIM 2
|
||||
#define NZ 1
|
||||
#define NT 1
|
||||
#define COLLISION_MODEL 0
|
||||
#define USE_LES 0
|
||||
#define RHO 1.f
|
||||
#define U0 .025f
|
||||
#define OMEGA_COLLISION_MIN .01f
|
||||
#define OMEGA_COLLISION_MAX 1.99f
|
||||
#define Y_WALL_BC 0
|
||||
#define OUTLET_MODE 0
|
||||
struct Dim {unsigned int x=0,y=0,z=0;} threadIdx,blockIdx,blockDim;
|
||||
inline float __fdiv_rn(float a,float b){return a/b;}
|
||||
#include "core/precision.cuh"
|
||||
#include "core/descriptors.cuh"
|
||||
#include "core/layout.cuh"
|
||||
#include "core/params.cuh"
|
||||
#include "core/flags.cuh"
|
||||
#include "operators/macro.cuh"
|
||||
#include "operators/collision_srt.cuh"
|
||||
#include "streaming/pull_double_buffer.cuh"
|
||||
#if UNIFORM
|
||||
#include "operators/forcing_guo.cuh"
|
||||
#include "operators/helpers.cuh"
|
||||
#include "boundary/bounce_back.cuh"
|
||||
#include "boundary/inlet_outlet.cuh"
|
||||
#include "step/one_step_double.cu"
|
||||
#else
|
||||
constexpr unsigned char KIND_OUTER=2;
|
||||
#include "step/block_channel.cu"
|
||||
#endif
|
||||
int main(){
|
||||
constexpr int gx=24,gy=20,n=gx*gy;
|
||||
std::vector<float> state(9*n),next(9*n);
|
||||
for(int q=0;q<9;q++)for(int y=0;y<gy;y++)for(int x=0;x<gx;x++)
|
||||
state[q*n+y*gx+x]=d_w[q]*(1.f+.001f*std::sin(float(x+3*y+q)))-USE_DDF_SHIFTING*d_w[q];
|
||||
d_params={1.25f,0,0,0,0};
|
||||
#if UNIFORM
|
||||
std::vector<uint16_t> flags(n,FLAG_FLUID);
|
||||
for(int y=0;y<gy;y++)for(int x=0;x<gx;x++){
|
||||
if(y==0||y==gy-1) flags[y*gx+x]=FLAG_SOLID|FLAG_BC_WALL;
|
||||
else if(x==0) flags[y*gx+x]=FLAG_SOLID|FLAG_BC_INLET;
|
||||
else if(x==gx-1) flags[y*gx+x]=FLAG_SOLID|FLAG_BC_OUTLET;
|
||||
}
|
||||
#else
|
||||
constexpr int bx=NX-2,by=NY-2,nbx=gx/bx,nby=gy/by;
|
||||
std::vector<float> input(nbx*nby*9*TOTAL_CELLS),output(input.size());
|
||||
d_block_channel={gx,gy,U0};
|
||||
#endif
|
||||
for(int step=0;step<5;step++){
|
||||
#if UNIFORM
|
||||
for(int y=0;y<gy;y++)for(int x=0;x<gx;x++){
|
||||
blockIdx={unsigned(x),unsigned(y),0};OneStep(flags.data(),state.data(),next.data());
|
||||
}
|
||||
#else
|
||||
// Populate cores and every wrapped halo from the same dense source.
|
||||
for(int b=0;b<nbx*nby;b++)for(int y=0;y<NY;y++)for(int x=0;x<NX;x++)for(int q=0;q<9;q++){
|
||||
int xx=((b%nbx)*bx+x-1+gx)%gx, yy=((b/nbx)*by+y-1+gy)%gy;
|
||||
input[b*9*TOTAL_CELLS+q*TOTAL_CELLS+y*NX+x]=state[q*n+yy*gx+xx];
|
||||
}
|
||||
for(int b=0;b<nbx*nby;b++)for(int y=1;y<NY-1;y++)for(int x=1;x<NX-1;x++){
|
||||
blockIdx={unsigned(x),unsigned(y),unsigned(b)};
|
||||
BlockChannelStep(input.data(),output.data(),1,nullptr,nullptr);
|
||||
for(int q=0;q<9;q++)next[q*n+((b/nbx)*by+y-1)*gx+(b%nbx)*bx+x-1]=output[b*9*TOTAL_CELLS+q*TOTAL_CELLS+y*NX+x];
|
||||
}
|
||||
#endif
|
||||
state.swap(next);
|
||||
}
|
||||
for(float f:state)printf("%.9g\n",f);
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
"""Root-channel host contract and actual-header CPU parity (no CUDA evidence)."""
|
||||
from dataclasses import replace
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from CelerisLab.config import LBMConfig
|
||||
from CelerisLab.lbm.blocks import BlockLayout2D
|
||||
from CelerisLab.lbm.channel import channel_masks, validate_channel_config, validate_channel_transfers
|
||||
from CelerisLab.lbm.refinement import FixedPatch2D
|
||||
from CelerisLab.lbm.refinement_tables import blocked_base_tables
|
||||
from .test_numerical_operators import compile_run
|
||||
|
||||
|
||||
@pytest.mark.parametrize("shift", [0, 1])
|
||||
@pytest.mark.parametrize("profile", [0, 1])
|
||||
def test_actual_kernel_uniform_parity(tmp_path, shift, profile):
|
||||
"""Nonuniform ghosts, corners, top wall, global profile and outlet halo donor."""
|
||||
defines = dict(USE_DDF_SHIFTING=shift, INLET_PROFILE=profile)
|
||||
uniform = np.array(compile_run(tmp_path, "channel_root.cpp", dict(
|
||||
defines, UNIFORM=1, NX=24, NY=20)), np.float32)
|
||||
assert np.isfinite(uniform).all()
|
||||
for by, bx in ((20, 24), (5, 6), (4, 1)):
|
||||
blocked = np.array(compile_run(tmp_path, "channel_root.cpp", dict(
|
||||
defines, UNIFORM=0, NX=bx+2, NY=by+2)), np.float32)
|
||||
np.testing.assert_array_equal(blocked, uniform)
|
||||
|
||||
|
||||
def transfer(bounds, block=(4, 6)):
|
||||
cfg = LBMConfig(nx=24, ny=20, viscosity=.1)
|
||||
pair = FixedPatch2D.from_config(cfg, bounds)
|
||||
layout = BlockLayout2D.build(20, 24, *block)
|
||||
targets = blocked_base_tables(pair, layout, np.argsort(pair.coalesce_coarse))[1]
|
||||
return pair, layout, targets
|
||||
|
||||
|
||||
def test_masks_and_minimal_footprint():
|
||||
fluid, bulk = channel_masks((20, 24))
|
||||
assert fluid.sum() == 18*22 and bulk.sum() == 16*22
|
||||
assert fluid[1, 1] and not bulk[1, 1]
|
||||
assert bulk[2, 1] and not fluid[0, 0]
|
||||
# No tile alignment or artificial large padding: closest legal x donor is x=1.
|
||||
validate_channel_transfers(*transfer((3, 4, 19, 16)))
|
||||
for bounds in ((2, 4, 19, 16), (3, 3, 19, 16), (3, 4, 22, 16), (3, 4, 19, 17)):
|
||||
with pytest.raises(ValueError, match="bulk"):
|
||||
validate_channel_transfers(*transfer(bounds))
|
||||
|
||||
|
||||
def test_actual_closure_read_conflicts():
|
||||
pair, layout, targets = transfer((6, 6, 18, 14))
|
||||
validate_channel_transfers(pair, layout, targets)
|
||||
for target in (layout.slot(layout.owner[5, -1]-1, 2),
|
||||
layout.slot(layout.owner[1, 4], 4),
|
||||
layout.slot(layout.owner[-2, 4], 3)):
|
||||
with pytest.raises(ValueError, match="immutable"):
|
||||
validate_channel_transfers(pair, layout, np.append(targets, target))
|
||||
|
||||
|
||||
def test_channel_scope():
|
||||
cfg = LBMConfig(nx=24, ny=20, viscosity=.1)
|
||||
validate_channel_config(cfg, [(4, 6), (4, 4)])
|
||||
with pytest.raises(ValueError, match="all-blocked"):
|
||||
validate_channel_config(cfg, [None, (4, 4)])
|
||||
for change in (dict(inlet_scheme="equilibrium"), dict(outlet_mode="zero_gradient"),
|
||||
dict(y_wall_bc="free_slip"), dict(wall_slot_width=2)):
|
||||
with pytest.raises(ValueError, match="channel requires"):
|
||||
validate_channel_config(replace(cfg, **change), [(4, 6), (4, 4)])
|
||||
|
||||
|
||||
def test_evolved_ghost_snapshot_and_physical_download():
|
||||
from contextlib import nullcontext
|
||||
from unittest.mock import Mock
|
||||
from .test_refinement_three_level_multipatch import host_module
|
||||
host = host_module()
|
||||
storage = {}
|
||||
host.compiler.load_module.return_value.get_global.side_effect = lambda name: (name, 12 if name == "d_block_channel" else 20)
|
||||
host.cuda.mem_alloc.side_effect = lambda size: Mock()
|
||||
host.cuda.memcpy_htod.side_effect = lambda ptr, a: storage.__setitem__(id(ptr), np.array(a, copy=True))
|
||||
host.cuda.memcpy_dtoh.side_effect = lambda a, ptr: np.copyto(a, storage[id(ptr)])
|
||||
context = Mock(activate=nullcontext, sm_arch="sm_70")
|
||||
cfg = LBMConfig(nx=24, ny=20, viscosity=.1, velocity=.025)
|
||||
backend = host.FixedPatchStepper(cfg, (6, 6, 18, 14), context=context,
|
||||
coarse_boundary="channel", base_block=(4, 6), fine_block=(4, 4))
|
||||
try:
|
||||
weights = np.array([4/9]+[1/9]*4+[1/36]*4)[:, None, None]
|
||||
arrays = [np.broadcast_to(weights, level.shape).copy() for level in backend._levels]
|
||||
arrays[0][:, 0, 0] *= 1.001
|
||||
backend.initialize(*arrays)
|
||||
snapshot = backend._download_raw()
|
||||
assert np.isfinite(snapshot.values[0][:, 0, :]).all()
|
||||
assert np.isnan(backend.download()[0][:, 0, :]).all()
|
||||
backend._initialize_raw(snapshot)
|
||||
for a, b in zip(snapshot.values, backend._download_raw().values):
|
||||
np.testing.assert_array_equal(a, b)
|
||||
arrays[0][0, 0, 0] = np.nan
|
||||
with pytest.raises(ValueError, match="finite"):
|
||||
backend.initialize(*arrays)
|
||||
assert backend._levels[0].kernel == host.compiler.load_module.return_value.get_function("BlockChannelStep")
|
||||
finally:
|
||||
backend.close()
|
||||
Reference in new issue
Block a user