Repository navigation
Releases: computational-chemical-engineering/peclet-flow
Release list
peclet-flow 1.4.0
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).
peclet-flow 1.3.0
peclet-flow 1.3.0
Added
peclet.flow.march_to_steady(solver, monitor, ...)marches a solver to its steady state and
returns aMarchResult(converged,steps,accelerated_steps,reason,num_restarts,
monitor). By default the march is Anderson-accelerated (type II, on the device, through core's
AndersonCore);accelerate=Falseis the plain march with the same stop test. On a dense
staggered bed (φ 0.6, N = 64) it certifies in 93 steps instead of 325, 3.4–3.9× less wall time.
StaggeredSolver, andSolverColocatedwith the'ghost'scheme; configurations the
accelerator cannot take (VoF, variable ρ/μ, moving scenes, cell forces, …) raise with the reason
and point ataccelerate=False.diagnostics.set_pressure_bottom_solver('auto' | 'direct' | 'algebraic')(developer tier)
selects the engine of the agglomerated pressure bottom.
Changed (named numerics changes)
- On GPU backends the pressure multigrid's bottom is solved on the device (
'auto', the
default, selects'direct'where eligible: single rank, a bottom of at most 8192 cells with at
most 64 fluid components, solids allowed): an FP64 flexible CG to 1e-5, preconditioned by an
FP32 block-tridiagonal direct factor refactored once per operator change, with no host transfer.
It replaces the host GraphAMG bottom there. Bubble column on an RTX 5080: 42.6 → 36.1–37.4 ms
per step; over 50 steps the fields differ from the GraphAMG bottom by at most 2.7e-14 (relative)
with identical outer iterations on every step. Host backends and the distributed path keep
GraphAMG and are bitwise unchanged;'algebraic'restores the previous engine on a GPU.
Changed
- A faster VoF step with identical results (bitwise on CUDA and host): the parabolic-fit
curvature fallback runs one GPU team per target cell (core 1.3.2'spvFitTerm/pvFitAccum
split), and the batched marker-block container's last tier runs on persistent warp teams
(14.8 → 7.4 ms per call on the bubble column).
Deprecated
Solver.hydro_force_torque()is deprecated and warns: it is not the force on the bodies.
It returns the reconstructed surface-traction integral, which under-reads the drag by ~30 % at
every resolution (traction / reaction 0.685–0.730 over φ 0.008–0.45 and N 24–128; the central
difference spans the wall, so the deficit is in the viscous part) and a spinning sphere's torque
by 28–41 %. The force and torque on the bodies ishydro_force_torque_reaction()— the
discrete momentum reaction, exactly conservative; its torque reads 1.019–1.027 of the exact
periodic-array value at 6–12 cells per radius. The traction, with its pressure/viscous split,
isdiagnostics.hydro_force_torque_traction(). The old name returns the same array as before
and raises aDeprecationWarningnaming both; it goes at the next major release. A consistent
traction (from the momentum operator's small-cell-robust wall reconstruction) is planned.
Fixed
hydro_force_torque()(the traction) applied the index → physical conversion twice under a
physical domain: 0.019× the reaction atextent1, 3e-8× at 7.3. Cell units were unaffected.enable_vof_momentumtogether with the VoF marker-block container now raises (in either
order). Momentum-consistent transport skips the step that advects the markers, so with both on
every marker stayed frozen while the momentum moved.- Pinned to core v1.4.0.
Known limitations
march_to_steady'sconverged=Truecertifies stationarity at the given dt: the state passed
the stop test on consecutive plain steps. It does not certify that a plain march from the initial
state would reach it. Without advection (Stokes) the steady state is unique. With advection,
above a flow instability (a wake past its Hopf point) Anderson can converge to an unstable
steady branch that a time march would never show; check such cases withaccelerate=Falseor by
stepping on from the result. The stop test assumes no mode decays slower thanslow_rateper
step; where one does it under-reads the remainder (dense bed at rtol 1e-10: ~1.4e-8 from the
steady state), so treat certificates below ~1e-8 on such geometries as approximate.- The pressure in sealed fluid pockets is arbitrary. Fluid cells that no open face connects to
the rest of the flow are out of reach of the pressure projection, so their pressure level is
undetermined (a dense sphere bed, φ 0.6 at N 64, has 126 such cells in 115 pockets). The force on
each body and the permeability, fromhydro_force_torque_reaction(), are unaffected: they move
by at most 1e-7. The surface-traction diagnosticdiagnostics.hydro_force_torque_traction()is
affected: per-body forces by about 2e-4 of the force, and on the collocated grid the bed-total
traction is biased as well. Averages ofget_p()that include pocket cells shift by 1e-3 to 4e-3
of the pressure range for each pressure range by which the pockets are offset. A pocket mask and
a corrected traction are planned after this release. - In cell units,
cell_centers()and an analytic scene disagree by half a cell. Without an
extent,cell_centers()returnsi + 1/2(origin 0, spacing 1), whileset_sceneplaces cell
(i, j, k)'s centre at (i, j, k). An SDF sampled oncell_centers()forset_solidand the same
body given toset_sceneare therefore offset by half a cell. With a physical domain (extent=)
both useorigin + (i + 1/2) hand agree. The docstrings now say so; unifying the cell-unit
convention changes results and is deferred to 2.0.0.
peclet-flow 1.2.0
Changed (named numerics changes)
- Volumetric forces are applied where the velocity lives. A per-cell body force
(enable_cell_force: the CFD-DEM feedback, Boussinesq buoyancy) and the drag coefficient
(enable_drag) are now evaluated at the velocity unknown's position by one rule for every
equation: on the staggered grid the mean of the two cells sharing the face, on the collocated grid
the cell value. Before, the constant-density path applied the cell value half a cell off the face
(first order), and on the variable-density, non-porous path the drag's right-hand side and its
implicit diagonal used different values. Surface forces (pressure, viscous stress, surface
tension) are unchanged: they are integrals over the control volume's faces, not interpolated
cell values. - Variable viscosity on the staggered grid is taken on the velocity control volume's own faces.
Each velocity component used to see the viscosity half a cell shifted. A mirror-symmetric pair of
runs now stays symmetric to round-off (asymmetry 1e-2…3e-2 → 1e-14); in the bubble column the
bubbles had collected at one wall. - Collocated variable density and surface tension. The pressure, surface-tension and body
forces enter the implicit momentum predictor instead of a face-acceleration kick after it, and a
balanced-force pre-projection (set_balanced_force_projection) is on by default in that
configuration (off, with a one-time notice, when the domain has an inflow or outflow face): a
fluid at rest under gravity with a density jump stays at rest to ~1e-12. - Collocated advection uses the projected, divergence-free face velocity (the
Almgren–Bell–Colella prescription) instead of the average of the two cell velocities, so the
advective flux is conservative. This was the last uniform-grid difference frompeclet.amr.
Changed (API)
diagnostics.rebalance_by_weights(w)builds a weighted partition whose cuts sit on multiples of
2^a— so the pressure multigrid can coarsenalevels in place before any redistribution —
and returns that alignment. A code that co-decomposes with flow must build its partition from
the same weights and this alignment (dem'smigrate_to_weights(w, align=)).predict_hierarchy(..., weights=)forecasts the multigrid ladder after such a rebalance and then
returns(rows, align); withoutweightsit returns the rows alone, exactly as before.init_mpimust precede the geometry, and now raises otherwise. A geometry built first left
each rank with a pressure hierarchy for its own block as if it were a periodic domain: every solve
converged, but the projected velocity kept each block's divergence (max |div u| 7.5e-2 at np 2).- A variable viscosity with an explicitly requested velocity multigrid raises
ValueError(the
V-cycle takes a constant viscosity and used to ignore the closure silently). set_superficial_velocity(enabled, axis, velocity)holds the mixture superficial velocity along
a periodic axis.
Added
- Coarse-level multigrid stages from core 1.2.0: the telescope's stage policy and field movement
are core's, and a weighted level-0 partition takes a Repartition stage instead of collapsing onto
few ranks. - VoF marker blocks: a curvature admissibility clip (|κ| ≤ 1/Δ_min), removal of sub-grid debris
with its volume returned exactly to the interface, and a capillary time-step bound that
accounts for overlapping (gas–gas) interfaces (diagnostics.set_vof_phantom_capillary_bound,
default on). The overlap failure of colliding bubbles traced to debris curvature. - A faster VoF step with identical results: face-form pressure-operator storage (32 instead of 56
bytes per cell per read), fused periodic wraps, device-resident Krylov scalars, and batched
kernels across all local marker blocks.
Fixed
- ε^n survives a rebalance. A redistribution whose blocks changed size reset the previous
porosity to zero before the migrated field arrived, so the first porous projection after the move
saw dε/dt = ε/dt (pressure off by 2e2 in coupling's rebalance test). - VoF markers touching a low no-slip wall no longer gain or lose volume through it (per-marker
volume drift 3.9e-3 → 7.7e-15 over 3000 steps). - The pressure solve no longer discards its correction on a uniform periodic bed. When the
right-hand side is round-off on a constant (the null space of the periodic pressure operator), the
bottom-level CG divided 0 by 0; the NaN made the multigrid preconditioner report a non-finite
value, and the solver zeroed the whole correction and marked the step CAPPED. The bottom CG now
stops when p·Ap = 0, as core's GraphAMG PCG already did. Paths with p·Ap > 0 are bitwise
unchanged; the porous fixed-bed Ergun case goes from 63 CAPPED warnings to none. Double operator
storage had been credited with curing this; it did not. - Pinned to core v1.3.0.
peclet-flow 1.1.0
peclet-flow 1.1.0 — part of the peclet 1.1.0 family.
Changed
-
The implicit momentum solve now takes the velocity multigrid V-cycle wherever the operator
warrants it, and there it is roughly 2x faster. Solver selection is'auto'by default and keys
on the operator's condition number (next bullet): atkappa >= 13— the diffusion-dominated
regime dense beds and large timesteps live in — it takes the V-cycle. Before, it took red-black
Gauss–Seidel unless the block was below 65536 cells per rank andnp > 1, a rule whose
premise had the sign of the effect backwards. At lowkappared-black is still the right tool and
is still what you get, so this is not "multigrid everywhere". Measured on the 1.0.0 scaling
benchmark (384³ cut-cell bed, Snellius,D = 6i.e.kappa = 73), the V-cycle is faster at every
rung of both ladders and its margin is largest at the biggest blocks:configuration cells/rank red-black V-cycle speed-up Genoa, 24 cores 2.36 M 70890 ms 36176 ms 1.96x Genoa, 384 cores 147 k 8130 ms 3646 ms 2.23x Genoa, 768 cores 74 k 2904 ms 1745 ms 1.66x H100, 1 GPU 56.6 M 4025 ms 1840 ms 2.19x H100, 4 GPUs 14.1 M 1203 ms 664 ms 1.81x Red-black was not merely slower: it ran at its sweep cap (200 per component) on every rung of
both ladders, so it was not converging. The V-cycle takes 9.7 cycles, rank-independent.
Accuracy is unchanged — that was checked rather than assumed.If you benchmarked 1.0.x, note that the momentum default moved underneath you: published
1.0.0 scaling figures were taken at the 1.0.0 default, not this one. -
The momentum solver is chosen by the operator's condition number, not by whether geometry is
present. The previous rule keyed on "a solid is present", so an IBM-sculpted body and a plain
domain-BC box at the samedtgot different solvers. The criterion is now
kappa = 1 + 4 dt mu (w_x + w_y + w_z) / rho(= 1 + 12 Disotropic): V-cycle atkappa >= 13
(D >~ 1), red-black below. The crossover was measured on a 64³/96³ channel with domain BCs and
no solid — the case that had no evidence behind it before. -
The Chebyshev momentum solvers are bound on
solver.diagnostics, not on the publicSolver.
Under docs/RELEASE.md §4.1 and docs/NAMING.md's alias ladder,
removing public API later costs a major bump; ablation knobs belong on the diagnostics tier
(QUALITY_PLAN D2), besideset_velocity_multigrid_autoand dem's existing
diagnostics.set_velocity_solver.
Added
-
One string selector per choice instead of a boolean per solver, extensible without breaking
the API:solver.diagnostics.set_velocity_solver('auto' | 'gauss_seidel' | 'multigrid' | 'chebyshev') solver.diagnostics.set_velocity_mg_smoother('gauss_seidel' | 'chebyshev') solver.diagnostics.velocity_solver() # readbacks solver.diagnostics.velocity_mg_smoother()
Two axes, kept apart because they are orthogonal: which solver runs the momentum equation, and —
only when that is'multigrid'— which smoother runs inside the V-cycle.'auto'is reachable as
a value for the first time: it clears an explicit choice and re-decides at the nextstep(),
wheredtandmuare final. Spelled aspeclet.demalready spells it (NAMING.md's whole
point). -
Chebyshev semi-iteration as an alternative momentum solver
(diagnostics.set_velocity_chebyshev). The implicit momentum solve is a screened Helmholtz whose
condition number is a property of the diffusion numberD = mu dt / (rho h²)and not of the mesh,
so red-black needsO(kappa)sweeps where Chebyshev needsO(sqrt(kappa))— and one Chebyshev
step costs one residual and one halo exchange against a red-black sweep's two of each, with every
lane active instead of half. The spectral interval is Gershgorin arithmetic on the stored stencil,
reduced over the communicator so every rank iterates with the same polynomial; a degenerate
interval falls back to red-black rather than diverging. It delivers the predictedsqrt(kappa)
factor (107 iterations/component against 147, predicted 111) but loses to the V-cycle except at
the smallest per-rank blocks, and its docstring says so. -
Chebyshev as the velocity multigrid's smoother
(diagnostics.set_velocity_mg_chebyshev), independent of the above. Measured slower, and the
docstring says that too: at degree 4 / ratio 6 it is the stronger smoother per cycle (8.0 V-cycles
against 9.3) and still loses on wall clock. Shipped because the measurement is worth keeping, not
because it wins. -
CPython 3.14 CUDA wheels.
peclet-flow-cu13,peclet-pnm-cu13,peclet-dem-cu13and
peclet-voro-cu13now build one job per interpreter across cp310–cp314, matching the CPU wheels;
cp314 users were falling through to a source build.
Fixed
-
A solid cutting an inflow or outflow face no longer produces an unsolvable pressure system.
Three defects hid in the same gap — nothing anywhere had put an immersed solid on an open
domain face — and none could be fixed alone, because each masked the next.The SDF ghost band outside a non-periodic face was filled by periodic wrap, so the geometry an
inlet or outlet face saw was teleported from the opposite side of the domain. That decides the
boundary-face aperture: a solid cell against the inlet (sdf = -0.73) was handed the far side's
fluid and its inflow face came out fully open, so prescribed inflow was counted into a cell
whose pressure row is entirely closed.0*p = Uis an inconsistent row, and no Krylov driver and
no multigrid depth solves an inconsistent system — which is precisely why the field evidence
showed FCG capping, Chebyshev NaN-ing and divergence atMGLEVELS <= 2, and why the agglomerated
bottom was (correctly) exonerated. The Dirichlet outlet row separately carried the literal
openness1.0instead of the face aperture, i.e. mass leaving through solid.This closes docs/SCALING_ISSUES.md #3, and the MPI-only defect it
surfaced as #8 — the halo wrapped a non-periodic face's high boundary plane. Both halves
(openness and the outflow velocity plane) had to land together, since fixing either alone breaks
vof_bc_mpi's composed conservation budget.Gated by
test_openbc_solid{,_mpi}— the first tests anywhere to combineset_domain_bcwith
set_solid— now run on both the staggered and collocated grids, plus acolo-jetcase
givingSolverColocatedits first open-boundary conservation gate of any kind. The collocated
path needed no code change: every part of the fix is in the geometry, and geometry is
grid-independent. What was missing there was the gate, not the fix. -
flow's height-function curvature is selected as often as it was before 1.0.0 again, and the
droplet damping with it. The cascade and the advector disagreed about what a PURE cell is.
enable_vofruns Weymouth–Yue atwispEps = 1e-8(a cell that close to 0 or 1 is treated as a
pure phase and fluxed algebraically, which is what stops a DRAINED open domain from taking the
MYC normal of round-off residue and going to NaN in three steps). Such a cell is never
reconstructed back onto exactly 1.0, so the colour field legitimately carries bulk liquid at
1 - O(1e-9)— while the height-function column walk judged purity at its own hard-coded
1e-10, two orders tighter, found no pure end to the column, and rejected it.The result degraded progressively and silently: a fallback is a valid answer, so nothing
failed and no test went red — the curvature just got worse the longer a run went on. On the
capillary-oscillations mode-2 droplet the HF tier fell from 790 interface cells to 134
(37 % → 89 % PLIC fallback) over 2.5 periods, tracking 425 bulk cells drifting into the band
between the two tolerances, and the fitted damping rate came out 1.029e-3 against 1.460e-3.Every VoF consumer is now TOLD what a pure cell is instead of deciding for itself — the same
contract phase change was given in633a144when it hit this mechanism first.core's
hfColumnHeighttakes apureEps;Solver::computeVofCurvaturesets it from the advector's
wispEpsat the point of use. With the fix the same run reads 784 HF cells / 37.0 % fallback
and a damping rate of 1.407e-3, against 790 / 37.2 % / 1.460e-3 for the pre-regression
configuration.test_vof_curvaturegate H pins the contract: a bulk deficit inside the
advector's tolerance must not cost the HF tier (it cost all of it — 1968 cells → 0 — before).Runs at
wispEps = 0(the standalone advector's default, andenable_phase_change) are
bit-identical. The drained-domain guard is untouched.This entry was briefly filed under 1.0.1 by mistake. Both halves of the fix (core
e6a612d,
flow28d3224) landed the day after the 1.0.1 tag, so 1.0.1 never carried them; it ships
here.
Tested
Measured on the release host (48-core Genoa + RTX 5080, sm120) on 2026-09-16, OMP_NUM_THREADS
bounded, every suite built fresh against extern/install/{host-openmp,nvidia-cuda}:
| suite | result |
|---|---|
core plain (serial + MPI halo, migration, load balancing) |
54/54, np 1–8 |
core Kokkos (halo + AMR + solver layer), host and CUDA |
69/69 each |
core Python bindings |
6/6, np 1–8 |
flow kernel units, host and CUDA |
46/46 each |
flow distributed tests/kokkos_mpi |
109/109, np 1, 2, 4 |
flow accuracy/efficiency regression |
PASS — K_inf, convergence order, pressure-iteration counts and step counts all +0.00 % against baseline on all three cases |
flow... |
peclet-flow 1.0.0
peclet-flow 1.0.0 — THE flow solver of the family: an incompressible cut-cell immersed-boundary Navier-Stokes solver on a staggered MAC grid, with a collocated variant sharing the same Python API, geometric VoF two-phase flow, analytic moving geometry, scalar transport and a validated distributed solve that is bit-exact to single-rank. This release brings the physical-units API: the solver takes a physical domain and physical properties, and the discretization is derived rather than supplied. Anisotropic cells work on both grids.
Operator storage is now double by default. Float storage breaks the singular row-sum identity A·1 = 0 at high multigrid contrast, and it breaks it silently: on a dense bed the residual floors and then rebounds, so the run is invalid rather than merely less accurate. Measured cost of being wrong on a random close packing at rtol 1e-8: float took 24/33/capped iterations at max|div| 4.5e-06, against 14/14/28 and 9.5e-12 in double. A float build is one flag away and now warns. Your numbers change in the last digits wherever the operator is involved; the accuracy and efficiency baselines do not move, and max|div| improves by about two orders of magnitude.
Build time, for anyone working on the source: the solver template is now compiled once per grid rather than once per consumer, so a full rebuild costs a quarter of the CPU it did and editing one of the twelve domain headers rebuilds four objects instead of forty-nine. Proved bit-exact.
Tested: 155/155 on the OpenMP backend — 49 single-rank plus 106 distributed at np 1, 2 and 4 — 49/49 single-rank on CUDA, and the accuracy and efficiency regression passing with every gated metric unchanged.
peclet 1.0.0 — the clean break.
This is the family's first major release. Every package moves to 1.0.0 together, because the
release removes every non-canonical Python name outright instead of deprecating it: there were no
external users, so the names were fixed once rather than carried forever. From 1.0.0 on the
deprecation ladder in docs/NAMING.md is binding — a rename is additive and deprecated for two
releases before removal, and a break costs a major.
The headline feature is the physical-units API: solvers take a physical domain and physical
properties, and the spatial discretization is derived rather than supplied. Anisotropic cells work
on both grids, single-phase and VoF.
One deliberate numerics change. peclet.flow now stores its operators in double precision by
default. Float storage breaks the singular row-sum identity A·1 = 0 at high multigrid contrast,
and it breaks it silently — on a dense bed the residual floors and then rebounds, so the run is
invalid rather than merely less accurate. A float build is one flag away and now warns. Results from
the default build change in the last digits wherever the operator is involved; the accuracy and
efficiency baselines do not move, and max|div| improves by about two orders of magnitude.
Full changelog, including every removed name with its replacement and the known limitations:
https://github.com/computational-chemical-engineering/peclet/blob/main/CHANGELOG.md
peclet-flow 0.4.0
BREAKING: the pnm pore-network-extraction module moved to its own package, peclet-pnm (import peclet.pnm; was peclet.flow.pnm).
New: directional ghost-cell IBM projection (staggered + collocated, resolves 2nd-order collocated drag at curved boundaries; multi-rank), throat-safe collocated scheme (face_interp mode 9), get_ox_proj/get_oy_proj/get_oz_proj (the openness the projection conserves — for flux bookkeeping e.g. peclet.pnm), eps-conservative porous momentum/projection pair, distributed multiphysics validation, MG smoother/halo overlap, GraphAMG bottom solve.
peclet-flow v0.2.0
Part of the Peclet suite v0.2.0. See the suite changelog: https://github.com/computational-chemical-engineering/peclet/blob/main/CHANGELOG.md