flow 1.3.0
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
vof_scriven Namespace Reference

Functions

 _scriven_integral (beta, rr, lo=0.0)
 
 scriven_beta (ja, rr)
 
 scriven_T (r, R, beta, rr, dT)
 
 initial_temperature (n, ctr, R, beta, rr, dT, mode="similarity", sub=4)
 
 sphere_colour_chunked (n, ctr, R, sub)
 
 sphere_colour (n, cx, cy, cz, R, sub=4)
 
 apply_p3g (s, order=1, op_mdot=None, gfm_order=None, curv_dist=None, carry=None, deposit=None)
 
 run (n, ja, ratio, r0, r1, cfl=0.2, alpha_l=1.0, sweeps=200, plane=True, consistent=True, quad=True, muscl=False, init="similarity", sub=4, area_mode=None, verbose=True, budget=0, carry=False, fitkap=False, p3g=None)
 
 plane_colour (n, nrm, sub=8, shift=0.37)
 
 plane_colour_periodic (n, nrm, sub=8, shift=0.37)
 
 cylinder_colour_chunked (n, ctr, R, sub)
 
 solenoidal_faces (n, amp, drift)
 
 area_advect (n=128, R=16.0, sub=16, steps=100, cfl=0.2, amp=0.0, drift=(0.5, 0.25, 0.125), modes=(0, 3, 4, 6, 7))
 
 _mc_area (c)
 
 area_probe (n, radii, ratio=100.0, sub=4, mode=None, shape="sphere")
 
 _sph_moments (w, ax, ctr, lmax=4)
 
 _cubic_bins (n, ctr, nb=4)
 
 regress_probe (n=128, radii=(16.0,), deltas=(0.05, 0.1, 0.2), sub=16, modes=(0, 6), ratio=100.0, advect_steps=0, drift=(0.5, 0.25, 0.125), cfl=0.2, nbin=4)
 
 _mdot_scene (n, R, ja, ratio, sub, alpha_l, area_mode, quad, plane, geom, dt, prof, kap=False, p3g=None)
 
 mdot_probe (n, radii, ja=0.5, ratio=100.0, sub=16, alpha_l=1.0, area_mode=None, quad=True, plane=True, dt=None, geoms=("sphere", "plane"), profs=("scriven",), kap=False, p3g=None)
 
 carry_probe (n, steps, ratio=100.0, carry=False, sub=16, grad=0.05, mdot=2.0e-3, p3g=None)
 
 main ()
 

Variables

tuple AREA_MODES
 
 _GL = np.polynomial.legendre.leggauss(400)
 

Detailed Description

WO-P23 rung P3 — SCRIVEN bubble growth (Scriven, Chem. Eng. Sci. 10:1, 1959).

A spherical vapour bubble growing in uniformly superheated liquid, thermally controlled:

    R(t) = 2 beta sqrt(alpha_l t) ,
    Ja   = rho_l c_pl (T_inf - T_sat) / (rho_v h_lv)
         = 2 beta^2 int_0^1 exp( -beta^2 [ (1-x)^-2 - 2(1 - rho_v/rho_l) x - 1 ] ) dx      (Scriven)
    T(r,t) = T_inf - (rho_v h_lv)/(rho_l c_pl) * 2 beta^2
             int_{1-R/r}^{1} exp( -beta^2 [ (1-x)^-2 - 2(1 - rho_v/rho_l) x - 1 ] ) dx .

The integral is tabulated numerically here (Gauss-Legendre on a substitution that resolves the
essential singularity at x -> 1); `beta` follows by bisection.

WHAT THE RUN MEASURES.  R(t) from the LIQUID VOLUME DEFICIT, R = (3 sum(1-C) / 4pi)^(1/3) — a
conserved-quantity read-out, not a contour, so it does not depend on any interface post-processing.
The gate is |R_num - R_exact| / R_exact over the last half of the run.

INITIALISATION (`--init`, WO-P3b).  Scriven's solution carries a thermal boundary layer of
thickness ~R0/(2 beta^2) at t0, and the classic trap in this benchmark is to start from a UNIFORM
superheat with a sharp bubble, so that the first steps evaporate the superheat sitting AT the
interface and the radius keeps a permanent offset.  `--init similarity` (the DEFAULT, and what
this driver has always done) samples T(r, t0) from the profile above at the cell centres, with
R0 = 2 beta sqrt(alpha_l t0) by construction (t0 is derived from R0);  `--init cellavg` is the
same profile CELL-AVERAGED by 4^3 subsampling;  `--init uniform` is the trap itself, kept as the
CONTROL that shows what it costs.

BOUNDARIES.  Outflow on all six faces (the net vapour production is a real volume flux and has to
leave; with pure liquid at every face and the reference density set to rho_l the boundary
coefficient `applyBoundaryOpenness` re-imposes IS the variable-density one, so WO-R2 item 1 is
side-stepped by construction) and T = T_inf Dirichlet on all six.

Usage:
    PYTHONPATH=$PWD/build_cuda python tests/study/vof_scriven.py [--n 128] [--ja 0.5,2,10]

Function Documentation

◆ _scriven_integral()

vof_scriven._scriven_integral (   beta,
  rr,
  lo = 0.0 
)
protected
int_lo^1 exp(-beta^2[(1-x)^-2 - 2(1-rr) x - 1]) dx, by the substitution x = 1 - 1/u
(u in [1/(1-lo), inf)) folded onto a finite interval with u = 1/v.

Definition at line 52 of file vof_scriven.py.

Referenced by scriven_beta(), and scriven_T().

◆ scriven_beta()

vof_scriven.scriven_beta (   ja,
  rr 
)

Definition at line 65 of file vof_scriven.py.

References _scriven_integral().

Referenced by _mdot_scene(), mdot_probe(), and run().

◆ scriven_T()

vof_scriven.scriven_T (   r,
  R,
  beta,
  rr,
  dT 
)
T_inf - dT * (2 beta^2 / Ja) * int_{1-R/r}^1 (...)  == the profile above, normalised so that
T(R) = T_inf - dT.

Definition at line 76 of file vof_scriven.py.

References _scriven_integral().

Referenced by _mdot_scene(), initial_temperature(), and run().

◆ initial_temperature()

vof_scriven.initial_temperature (   n,
  ctr,
  R,
  beta,
  rr,
  dT,
  mode = "similarity",
  sub = 4 
)
T(r, t0) - T_sat on the cell centres, for the three initialisations of `--init`.

'similarity' : Scriven's similarity profile sampled AT the cell centre.  This is what the
               driver has always done and what Sato & Niceno (JCP 2013) and Malan et al.
               (2021) do; it is the shipped default and this branch is byte-identical to the
               pre-WO-P3b code.
'cellavg'    : the same profile, CELL-AVERAGED by sub^3 subsampling (a finite-volume initial
               state rather than a point sample -- the O(h^2 T'') half of the same question).
'uniform'    : uniform superheat T_inf in the liquid, T_sat inside the bubble, i.e. NO thermal
               boundary layer at t0.  The classic trap, kept as the control.

Definition at line 86 of file vof_scriven.py.

References mean(), and scriven_T().

Referenced by run().

◆ sphere_colour_chunked()

vof_scriven.sphere_colour_chunked (   n,
  ctr,
  R,
  sub 
)
Liquid fraction of a sphere by sub^3 subsampling, one cell-plane at a time.

WO-P3c: the AREA probe needs a colour field whose SLIVER cells survive.  `sub^3` subsampling
quantizes C to multiples of 1/sub^3, so every cell whose true liquid fraction is below
1/(2 sub^3) is rounded to exactly 0 or 1 and DROPS OUT of the interface entirely — at sub = 4
that is a quarter of the interfacial cells of a sphere and 5-9 % of its area, while the VOLUME
(and hence R0) moves by 1e-4 %.  That is the whole of WO-P3b's "the PLIC area of a sphere is
5-9 % low": see the WO-P3c findings.  The chunked loop is what makes sub = 32 affordable
(sub = 32 on R = 28 would otherwise materialise a 2000^3 sample cloud).

Definition at line 122 of file vof_scriven.py.

References mean().

Referenced by _mdot_scene(), area_advect(), area_probe(), regress_probe(), and run().

◆ sphere_colour()

vof_scriven.sphere_colour (   n,
  cx,
  cy,
  cz,
  R,
  sub = 4 
)
Liquid fraction (C = 1 liquid, 0 vapour) of a sphere of radius R, by sub^3 subsampling.

Definition at line 147 of file vof_scriven.py.

Referenced by regress_probe().

◆ apply_p3g()

vof_scriven.apply_p3g (   s,
  order = 1,
  op_mdot = None,
  gfm_order = None,
  curv_dist = None,
  carry = None,
  deposit = None 
)
WO-P3g: the second-order interfacial energy operator, as a package and as an ablation.

`order = 2` turns on all four pieces (operator-flux mdot, the Gibou-Fedkiw three-point row,
curvature-consistent distances from the V3 cascade, and WO-P3f's enthalpy-conserving Dirichlet
overwrite); the four keyword overrides then switch individual pieces back off or on, which is
how the ablation rows of the findings are produced.  `order = 1` with all overrides None is the
shipped scheme, bitwise.

Definition at line 166 of file vof_scriven.py.

Referenced by _mdot_scene(), carry_probe(), and run().

◆ run()

vof_scriven.run (   n,
  ja,
  ratio,
  r0,
  r1,
  cfl = 0.2,
  alpha_l = 1.0,
  sweeps = 200,
  plane = True,
  consistent = True,
  quad = True,
  muscl = False,
  init = "similarity",
  sub = 4,
  area_mode = None,
  verbose = True,
  budget = 0,
  carry = False,
  fitkap = False,
  p3g = None 
)

Definition at line 188 of file vof_scriven.py.

References apply_p3g(), initial_temperature(), scriven_beta(), scriven_T(), and sphere_colour_chunked().

Referenced by main().

◆ plane_colour()

vof_scriven.plane_colour (   n,
  nrm,
  sub = 8,
  shift = 0.37 
)
Liquid fraction (C = 1 liquid) of the half space n.(x - centre) < shift, by sub^3
subsampling.  The sub-cell shift matters: a grid-aligned plane placed exactly on a cell face
has NO mixed cell at all (CLAUDE.md, the VoF dt-limiter note), so the probe would read zero
interfacial cells and no area.

Definition at line 473 of file vof_scriven.py.

References mean().

Referenced by area_probe().

◆ plane_colour_periodic()

vof_scriven.plane_colour_periodic (   n,
  nrm,
  sub = 8,
  shift = 0.37 
)
Liquid fraction of the PERIODIC tilted-plane family `0 <= (nrm . x) mod n < n/2`.

WO-P3d.  A single half-space in a PERIODIC box is not a single plane: the wrap turns the
domain faces into a second interface, and a JOINED surface reconstruction reports it (measured:
the (1,1,0) row of `--area-probe=-2` comes out +5 % against the one-plane reference purely
because of the seam, while a per-cell area misses it since the seam has no mixed cell).  The
repair is a scene that is periodic BY CONSTRUCTION: with an INTEGER normal `nrm`, the level
sets of `f = nrm . x  (mod n)` are closed flat surfaces of the torus, and the co-area formula
gives their total area in closed form,

    integral |grad f| dV = |nrm|_2 n^3 = n * A(one level)   =>   A(one level) = |nrm|_2 n^2 ,

so the two level sets bounding `f < n/2` carry EXACTLY `2 |nrm|_2 n^2`.  No edge convention, no
seam, no reference uncertainty — for (0,0,1) it is 2 n^2, for (1,1,0) it is 2 sqrt(2) n^2.

Definition at line 487 of file vof_scriven.py.

References mean().

Referenced by area_probe().

◆ cylinder_colour_chunked()

vof_scriven.cylinder_colour_chunked (   n,
  ctr,
  R,
  sub 
)
Liquid fraction of an INFINITE cylinder of radius R along z (a curved interface with ONE
non-zero principal curvature — the control that separates a metric error from a curvature
error), by sub^2 subsampling of the transverse plane. Exact in z by construction, so its
reference area 2 pi R n_z carries no z discretisation at all.

Definition at line 516 of file vof_scriven.py.

References mean().

Referenced by area_probe().

◆ solenoidal_faces()

vof_scriven.solenoidal_faces (   n,
  amp,
  drift 
)
A face velocity field that is EXACTLY discretely divergence-free on the staggered grid.

Two cellular (Taylor-Green) cells plus a uniform drift:

    u = A sin(k x_f) cos(k y_c) ,   v = -A cos(k x_c) sin(k y_f) + A sin(k y_f) cos(k z_c) ,
    w = -A cos(k y_c) sin(k z_f) ,   k = 2 pi / n ,   plus the constant `drift`.

The discrete divergence vanishes to the LAST BIT, not to O(h^2), and that matters: `advect_vof`
refuses a field whose `max_open_divergence_projected()` exceeds 1e-10 because Weymouth-Yue's
exact conservation is conditional on it.  The identity is
`sin(k(x+h)) - sin(k x) = 2 sin(kh/2) cos(k(x + h/2))`, and `x + h/2` of a FACE is exactly the
CELL CENTRE the transverse factor is evaluated at — so the two axes' differences cancel term by
term for each cellular pair.

Definition at line 532 of file vof_scriven.py.

Referenced by area_advect(), and regress_probe().

◆ area_advect()

vof_scriven.area_advect (   n = 128,
  R = 16.0,
  sub = 16,
  steps = 100,
  cfl = 0.2,
  amp = 0.0,
  drift = (0.5, 0.25, 0.125),
  modes = (0, 3, 4, 6, 7) 
)
WO-P3d gate (b): is the JOINED area BOUNDED under Weymouth-Yue advection?

An a-priori probe measures a construction on an EXACT colour field.  A running one never sees
one: WY leaves round-off residue in every cell its sweeps touch (down to 1e-300 -- the residue
the V4 curvature needed `interfaceEps` for and the W0 block container needed `bubbleEps` for),
and the interfacial-cell density climbs from the initialisation's value to ~1.5/h^2 within ten
steps.  A per-cell area sums over those cells, so the question "does the area drift with the
WISP population" is a real one; a joined sheet is built from the C = 1/2 crossing and should be
blind to them.  Both are measured here, on the SAME field, step by step.

`amp = 0` is the pure TRANSLATION control, where the exact answer stays `4 pi R^2` for ever.

Definition at line 560 of file vof_scriven.py.

References solenoidal_faces(), and sphere_colour_chunked().

Referenced by main().

◆ _mc_area()

vof_scriven._mc_area (   c)
protected
Marching-cubes area of the C = 1/2 level set — an INDEPENDENT geometric reference for the
summed PLIC area (it agrees with 4 pi R^2 to 0.5 % on the exact spheres below).

Definition at line 625 of file vof_scriven.py.

Referenced by area_probe().

◆ area_probe()

vof_scriven.area_probe (   n,
  radii,
  ratio = 100.0,
  sub = 4,
  mode = None,
  shape = "sphere" 
)
A-PRIORI probe (WO-P3b): the summed PLIC interface area of an EXACT sphere, against 4 pi R^2.

No time stepping, no energy solve, no velocity — the exact sphere fractions are set, one
`apply_phase_change(0.0)` builds the interface, and `phase_change_diagnostics()['interface_area']`
is read.  This isolates the geometry of `plicArea` + the MYC normals from every other part of
the rung, and it is the quantity the R(t) gate integrates: the bubble grows as int mdot dA.

Definition at line 636 of file vof_scriven.py.

References _mc_area(), cylinder_colour_chunked(), plane_colour(), plane_colour_periodic(), and sphere_colour_chunked().

Referenced by main().

◆ _sph_moments()

vof_scriven._sph_moments (   w,
  ax,
  ctr,
  lmax = 4 
)
protected
Real spherical-harmonic moments of a signed cell weight `w` (a removal density), returned
as the per-l RMS coefficient normalised by |sum w|.

The regression shifts each cell's plane along that cell's OWN normal, so a systematic
under-shift on (say) the octant diagonals would leave the bubble faceted.  A cubic lattice can
only produce CUBIC-harmonic anisotropy, whose leading term is l = 4 (l = 2 vanishes by the
three mirror symmetries), so the l = 2 row is the control and the l = 4 row is the signal.

Definition at line 709 of file vof_scriven.py.

Referenced by regress_probe().

◆ _cubic_bins()

vof_scriven._cubic_bins (   n,
  ctr,
  nb = 4 
)
protected
(ctr is a 3-tuple of cell-index centres.)
Direction classes by the cubic invariant s = u_x^4 + u_y^4 + u_z^4, which runs from 1/3 on
the BODY DIAGONAL (111) through 1/2 on a FACE diagonal (110) to 1 on an AXIS (100).  Binning
the removed volume by s and dividing by the same bins of the EXACT removal is a direct,
reference-free read-out of whether the regression is isotropic.

Definition at line 743 of file vof_scriven.py.

Referenced by regress_probe().

◆ regress_probe()

vof_scriven.regress_probe (   n = 128,
  radii = (16.0,),
  deltas = (0.05, 0.1, 0.2),
  sub = 16,
  modes = (0, 6),
  ratio = 100.0,
  advect_steps = 0,
  drift = (0.5, 0.25, 0.125),
  cfl = 0.2,
  nbin = 4 
)
WO-P3e — the a-priori INTERFACE REGRESSION probe.  No time stepping, no energy solve, no
velocity: an exact sphere goes in, ONE `apply_phase_change(dt)` with a uniform prescribed mdot
runs the plane shift + clip-and-redistribute, and the colour field that comes out is compared
against the analytically known answer.

With `rho_l = 1` and `mdot = 1` the step's normal displacement is exactly `delta = dt`, so the
exact liquid volume removed from a sphere of radius R is the shell

    dV = 4/3 pi ((R+delta)^3 - R^3) = 4 pi R^2 delta (1 + delta/R + delta^2/(3 R^2))

(the bubble is the GAS, so evaporation GROWS it: R -> R + delta), the new radius from the
volume deficit is R + delta exactly, and the new interfacial area is 4 pi (R+delta)^2.  Those
three are the gate.  The fourth read-out is ISOTROPY: the shift acts along each cell's own
normal with the cell's own area, so an under-shift on the octant diagonals would leave the
bubble faceted -- measured both as the ratio of the removed volume to the exact removed volume
in bins of the cubic invariant `u_x^4 + u_y^4 + u_z^4`, and as the l = 2 / l = 4 spherical
harmonic moments of the removal density.

`--regress-advect N` runs the sphere through N Weymouth-Yue steps of the PURE TRANSLATION field
of gate (b) first, so the fractions are the ones a running solver actually carries (WY re-creates
the sliver/wisp population within ten steps) while the exact reference stays a sphere.

Definition at line 760 of file vof_scriven.py.

References _cubic_bins(), _sph_moments(), solenoidal_faces(), sphere_colour(), and sphere_colour_chunked().

Referenced by main().

◆ _mdot_scene()

vof_scriven._mdot_scene (   n,
  R,
  ja,
  ratio,
  sub,
  alpha_l,
  area_mode,
  quad,
  plane,
  geom,
  dt,
  prof,
  kap = False,
  p3g = None 
)
protected
One a-priori mdot evaluation: `geom` x `prof`, the 2x2 that separates the two curvatures.

`geom` = 'sphere' (the exact sphere, sub^3 fractions) or 'plane' (a flat interface normal to x
at a sub-cell offset, the same sub^3 fractions).  `prof` = 'scriven' (Scriven's similarity
profile of a bubble of radius R at the matching time, as a function of the signed distance to
the interface -- so the sphere and plane rows carry IDENTICAL T', T'' at the interface) or
'linear' (T = T'(R) d exactly, zero profile curvature).

ONE `apply_phase_change(dt)` evaluates the shipped estimator: no energy solve, no velocity, no
time stepping.  The only things measured are the one-sided gradient fit and the area on that
field.  `plane x linear` is the exactness control (the fit's model is the exact state);
`sphere x linear` isolates the INTERFACE's curvature; `plane x scriven` isolates the PROFILE's
curvature and its resolution; `sphere x scriven` is the run's own configuration.

Definition at line 882 of file vof_scriven.py.

References apply_p3g(), mean(), scriven_beta(), scriven_T(), and sphere_colour_chunked().

Referenced by mdot_probe().

◆ mdot_probe()

vof_scriven.mdot_probe (   n,
  radii,
  ja = 0.5,
  ratio = 100.0,
  sub = 16,
  alpha_l = 1.0,
  area_mode = None,
  quad = True,
  plane = True,
  dt = None,
  geoms = ("sphere", "plane"),
  profs = ("scriven",),
  kap = False,
  p3g = None 
)
WO-P3f gate (b): the a-priori CURVATURE BIAS of the one-sided mdot fit.

For each radius the exact sphere carries Scriven's similarity profile at the matching time and
the shipped estimator is evaluated ONCE.  The area-weighted mdot is compared with the analytic
`mdot = rho_v beta sqrt(alpha/t)`; the flat-interface row with the SAME profile is the control.

Definition at line 984 of file vof_scriven.py.

References _mdot_scene(), and scriven_beta().

Referenced by main().

◆ carry_probe()

vof_scriven.carry_probe (   n,
  steps,
  ratio = 100.0,
  carry = False,
  sub = 16,
  grad = 0.05,
  mdot = 2.0e-3,
  p3g = None 
)
WO-P3f, the a-priori gate of `--carry`: a PLANAR interface swept through cells at a
PRESCRIBED mass flux, with a linear superheat profile in the liquid.

Everything about the scene is chosen so the ONLY enthalpy book-keeping under test is the
interfacial cells' Dirichlet overwrite: `mdot` is prescribed (no gradient fit), the phase
conductivities are ~0 (no diffusion), the interface is flat (no curvature) and the profile is
linear (no profile curvature). Per step the instrument prints the identity the option must
satisfy exactly -- what the deposit hands back plus what it could not place equals what the
overwrite destroys -- and the trajectory of the total enthalpy `sum rho c_p(C) (T - T_sat)`
over the inner cells, which is the quantity the option exists to stop leaking.

Definition at line 1037 of file vof_scriven.py.

References apply_p3g(), and mean().

Referenced by main().

◆ main()

vof_scriven.main ( )

Definition at line 1124 of file vof_scriven.py.

References area_advect(), area_probe(), carry_probe(), main(), mdot_probe(), regress_probe(), and run().

Referenced by main().

Variable Documentation

◆ AREA_MODES

tuple vof_scriven.AREA_MODES
Initial value:
1= ("plic", "cascade-metric", "cascade-normal", "cascade-footprint",
2 "sheet-color-centroid", "sheet-color-split", "sheet-plic-centroid", "sheet-plic-split")

Definition at line 46 of file vof_scriven.py.

◆ _GL

vof_scriven._GL = np.polynomial.legendre.leggauss(400)
protected

Definition at line 49 of file vof_scriven.py.