Repository navigation
Added
- Cut-cell scalar transport with solids (opt-in:
add_scalar(name, D, cutcell=True)). A
transported scalar sees the immersed bodies as cut cells with a probe-flux wall condition, second
order for Neumann, Dirichlet and Robin walls (set_scalar_wall), conjugate transport into the
solid (set_scalar_solid,get_scalar_solid), sources (set_scalar_source), a mean-gradient
closure for periodic beds (set_scalar_mean_gradient,diagnostics.scalar_mean_flux), and a
steady solve (solve_scalar_steady) beside the transient advance;set_scalar_residual_tolerance
(the velocity solver's criterion, one spelling),diagnostics.set_scalar_max_iterations(max_iter);
per-body wall fluxesdiagnostics.scalar_wall_flux, budgets and
solver statisticsdiagnostics.scalar_budget/scalar_census, the cut-cell record
diagnostics.scalar_geometry/scalar_facets. Conservation is exact in the discretization; the
solver residual is reported, never fixed up. Staggered grids and the collocated cut-cell
projection; refused by name: the collocated'ghost'scheme, collocated open faces, porous
continuity, moving scenes. Legacy scalars (cutcell=False, the default) are unchanged. diagnostics.set_pressure_vcycle_precision('fp64' | 'fp32' | 'auto')and
diagnostics.pressure_vcycle_precision— an FP32 V-cycle below the exact FP64 PCG/FCG, with an
FP64 restart (latched for the run) on a cap or breakdown; single rank and distributed.diagnostics.set_pressure_initial_guess('zero' | 'projected')(opt-in; default'zero'):
A-norm projection onto the last one or two pressure solutions.diagnostics.set_pressure_prefactor_hoist(bool),diagnostics.num_pressure_prefactors,
diagnostics.num_pressure_chebyshev_restarts(developer tier).- With np > 1,
init_mpiprints one line on rank 0 naming ranks whose OpenMP threads straddle L3
(CCD) domains (no result effect).initMpi(dec)refuses, on every rank, a decomposition the ranks
do not share.
Changed (named numerics changes)
- The GPU pressure V-cycle defaults to
'auto': FP32 where eligible on device backends, at
every np, with the FP64 restart; host'auto'is'fp64', so host results are bitwise unchanged.
Device fields move in the last digits (~1e-13 relative); 66 probe configurations 0 violations,
2000 steps 0 restarts. ~5 % per step on one H100 (16.9 → 16.1 ms), −28 % projection on an
RTX 5080, never slower at np > 1.'fp64'restores the previous solve. - The default decomposition (
set_decomposition(0)) is the AUTO policy: the legacy aligned ORB
where it is exactly balanced (bit-identical there), otherwise a balance-aware coarse-first
partition (coreSplitAxis::Balanced), otherwise the better of legacy and the balance-aware
aligned ORB; it never cuts an axis the legacy ORB leaves whole. np > 1 partitions change where
the legacy split is inexact (bubble column np 3/12/24): fields within 5.9e-14 of the legacy
partition, iterations identical; the bubble column at 24×1 was 2:1 imbalanced under legacy.
set_decomposition(1)is the legacy ORB; explicit levels ≥ 2 are unchanged (2394/2394 published
partitions byte-identical). The policy keeps legacy's set of split axes unless legacy's max/mean
load is ≥ 5/4; this guard reverts 14 census configurations to the legacy partition (128×96×64
at np 12 among them). The box-shell launch of the auto 12×2 column no longer runs serially on one
thread (6.2 ms/step on genoa); bitwise. - Host backends use the
'direct'pressure bottom where eligible (single rank: FP32
block-tridiagonal factor + FP64 FCG, replacing GraphAMG; distributed: solved redundantly on every
rank from the allgathered bottom). 50-step difference 3.3e-14 single rank, 2.3–3.6e-14 at np > 1,
pressure iterations identical;set_pressure_bottom_solver('algebraic')restores GraphAMG.
Workstation 8×3: 61.4 → 53.6 ms/step. - VoF tier-3 curvature fallback (core 1.5.0's moment kernels): a recorded round-off change, 50-step
max relative 2.7e-14 host / 2.5e-14 CUDA, iterations identical; tier 3 3.55 → 0.58 ms on an
RTX 5080. - Host pressure-MG sum reductions run in pencil order; the Chebyshev driver re-estimates its spectral
bounds from the previous estimate after a variable-density rebuild (cold re-estimate on a guard,
counted); VoF block statistics are reduced in one launch per step (device: fixed 512-cell tiles) —
diagnostics order only. - Scalar and phase-change energy inputs are physical under an armed extent (a fix of the
physical-units contract):add_scalar's diffusivity, mass fluxes, latent heat, conductivity,
ρc_p and the closures on ρ/μ/forces are converted at the boundary; the getters report physical.
Scripts that passed cell-unit values to a solver built withextent=change result. - VoF blocks under a physical domain (
Solver(cells, extent=…)withenable_vof_blocks): the
step handed its internal dt to the physical-unit block advector, which converted it a second
time (the first step hit the Weymouth–Yue cap), and the block CSF kept a σ from before the first
set_dt(1/t_ref² too strong). Both fixed; cell-unit runs are bitwise unchanged (ctest
vof_blocks_physical_units). - Open boundaries keep their high-side face plane. Ghost fills of the MAC face fields wrapped
every axis, so a non-periodic high-side face (outflow or inflow) carried the low boundary's
face; the collocated projection and the collocated VoF bridge exposed it, and legacy scalars
read the wrapped plane. Periodic axes are untouched (ctestopen_face_plane, np 1–3). - Scalar inflow. A scalar's ghosts are filled before the old state is frozen, so the first
step afterset_field(or a coupling or VoF write) no longer upwinds from stale ghosts; at a
Dirichlet face that is also a velocity inflow the advection upwinds the prescribed value, not the
Dirichlet reflection (a plug's inventory was U t + 0.5 cells; now U t to 6e-14, ctest
scalar_inflow). Results change for scalars written before the first step (state hash
scalar). Trade: on a smooth inflow profile the first interior face carries an O(h) error
(gh/6 coded limiter, gh/4 second-order upwind).
Changed (performance, bitwise)
- Distributed host step: the pressure-MG stencil exchanges are faces-only with rank-local periodic
axes read in-kernel; core 1.6.0's halo transport (persistent requests, one pack launch, on-node
shared memory: 10.9–12.5 → 1.1–1.7 µs per exchange); fused host Krylov reductions; host VoF
kernels and momentum ghost fills in fewer launches. - Device step: no global synchronization inside
step()(ctestdevice_sync_budget), GPU-resident
coarse tail with the bottom factor on a side execution instance.
Known limitations
- Cut-cell scalars: particles in contact do not converge at R/h 8–32 (no contact model yet);
collocated open faces are refused. - Distributed host runs: threads per rank must divide the cores of an L3 domain; 8×3-type layouts
on 8-core CCDs are slow (+17.6 ms kernel time on genoa), whatever the partition.
Part of the peclet family 1.5.0 (pins core v1.7.0).