Files
CelerisLab/tests/validation/taylor_green/run_cpu_explosion.py
T

86 lines
5.1 KiB
Python

"""Diagnostic-only rectangular adapter to CPUREF-0.3. No production changes."""
import sys,json,time,argparse,hashlib
from pathlib import Path
import numpy as np
parser=argparse.ArgumentParser(description=__doc__)
parser.add_argument('--repo',type=Path,required=True,help='Candidate checkout at 43c527b')
parser.add_argument('--n',type=int,choices=(32,64),default=32)
parser.add_argument('--output-dir',type=Path,required=True)
args=parser.parse_args()
ROOT=args.repo.resolve()
sys.path[:0]=[str(ROOT/'src'),str(ROOT/'tests/validation/multigrid'),str(ROOT/'tests/validation/taylor_green'),str(ROOT/'tests/unit')]
import cpu_reference as cpu
import oracle
from run_case import provenance
from test_projected_explosion import reconstruct
from CelerisLab.config import LBMConfig
from CelerisLab.lbm.refinement import FixedPatchSet2D
from CelerisLab.lbm.refinement_tables import projected_explosion_tables
from CelerisLab.lbm.blocks import BlockLayout2D
class RectSpec(cpu.Spec):
@property
def n_fine(self): return 2*self.n_coarse
def setup(n):
nu=oracle.U*n/100
cfg=LBMConfig(nx=n,ny=n,viscosity=nu)
bounds=oracle.rectangles(n,2)[0]
p=FixedPatchSet2D.from_config(cfg,bounds)
spec=RectSpec(n_coarse=n,patch_lo=n//8,patch_hi=7*n//8,halo=n//4,tau_coarse=p.tau_coarse,tau_fine=p.tau_fine,collision='srt')
q,k=np.divmod(p.coalesce_coarse.astype(int),n*n); y,x=np.divmod(k,n)
rows=np.column_stack((q,y,x)); links=[]
for qi,yi,xi in rows:
sx,sy=xi-cpu.CX[qi],yi-cpu.CY[qi]
if not p.covered[sy,sx]: links.append(cpu.CornerLink(f'{xi}_{yi}_q{qi}',int(qi),(int(sx),int(sy)),(int(xi),int(yi))))
assert len(links)==8
maps=cpu.Maps(~p.covered,p.covered,p.fine_kind==0,p.fine_kind==1,p.fine_kind==2,p.fine_kind,np.arange(2*n)//2,(np.arange(2*n)+.5)/2,rows,tuple(links))
# Verify the CPU coalescence child locations equal actual production tables.
children=np.array([[qi*(2*n)**2+(2*yi+dy)*2*n+2*xi+dx for dy in (0,1) for dx in (0,1)] for qi,yi,xi in rows])
np.testing.assert_array_equal(children,p.coalesce_fine)
layout=BlockLayout2D.build(n,n,8,8,periodic=(True,True))
neighbors,offsets=projected_explosion_tables(p,layout)
inverse=np.full(layout.nblocks*np.prod(layout.local_shape),-1,dtype=int)
inverse[layout.owner.ravel()]=np.arange(n*n)
neighbors=inverse[neighbors]
def explode(gc,cv,gf,fv,m,writes):
# Only actual transfer footprint, including inner and outer ghost slabs.
gy,gx=np.divmod(p.ghost_fine.astype(int),2*n)
if mode[0]=='constant': values=gc.reshape(9,-1)[:,p.ghost_coarse]
else: values=reconstruct(gc,p,neighbors,offsets)[0]
assert np.isfinite(values).all()
gf[:,gy,gx]=values; fv[:,gy,gx]=True; writes[gy,gx]+=1
mode=['constant']; cpu._explode=explode
return spec,maps,p,mode,nu
def run(n,which):
spec,m,p,mode,nu=setup(n); mode[0]=which
masks=[m.coarse_active,m.fine_active]
arrays=[oracle.initial(mask.shape,l,n,nu) for l,mask in enumerate(masks)]
for a,mask in zip(arrays,masks):a[:,~mask]=np.nan
s=cpu.State(*arrays); rows=[]; initmass=None; momentum0=None; start=time.monotonic()
for tstar in oracle.TIMES:
step=round(tstar*n/oracle.U)
while s.tick<2*step: s,_=cpu.advance_coarse_once(spec,m,s)
arrays=[s.coarse_f,s.fine_f]
row=oracle.metrics(arrays,masks,step,n,nu,initmass)
if initmass is None:initmass=row['mass']
dot=den=res=0.; parts=[]; momentum=np.zeros(2)
for l,(a,mask) in enumerate(zip(arrays,masks)):
v=a[:,mask];rho=v.sum(0);u=cpu.CX@v/rho;w=cpu.CY@v/rho
x,y=oracle.centers(mask.shape,l);ex,ey,_=oracle.exact(x[mask],y[mask],step,n,nu);vol=.25**l
dot+=vol*np.sum(u*ex+w*ey);den+=vol*np.sum(ex*ex+ey*ey)
parts.append((u,w,ex,ey,vol));momentum+=vol*np.array([np.sum(cpu.CX@v),np.sum(cpu.CY@v)])
amp=dot/den
res=sum(vol*np.sum((u-amp*ex)**2+(w-amp*ey)**2) for u,w,ex,ey,vol in parts)
if momentum0 is None:momentum0=momentum.copy()
row.update(step=step,exact_energy_quadrature_factor=den/(2*n*n*row['exact_energy']),amplitude_ratio=amp,amplitude_bias=amp-1,orthogonal_residual_l2=float(np.sqrt(res/den)),energy_signed_relative=row['energy']/row['exact_energy']-1,mass_signed_relative=row['mass']/initmass-1,momentum=momentum.tolist(),momentum_change=(momentum-momentum0).tolist())
rows.append(row)
return dict(n=n,explosion=which,seconds=time.monotonic()-start,rows=rows)
if __name__=='__main__':
a=args
a.output_dir.mkdir(parents=True,exist_ok=True)
result=dict(implementation_revision='43c527b',source=provenance(ROOT),diagnostic_input_sha256={name:hashlib.sha256((ROOT/name).read_bytes()).hexdigest() for name in ('tests/validation/multigrid/cpu_reference.py','tests/unit/test_projected_explosion.py')},precision='FP64 physical populations',phase='arrival_f; macro moments invariant under collision, initialization equilibrium',runs=[])
out=a.output_dir/f'n{a.n}_ab.json'
for mode in ('constant','projected_linear'):
r=run(a.n,mode);result['runs'].append(r);out.write_text(json.dumps(result,indent=2)+'\n');print(json.dumps(r),flush=True)