Tungsten PFC application¶
Production tungsten phase-field crystal binary: JSON/TOML → pfc::ui::SpectralETDSession<TungstenPhysics, Stack> (aliases TungstenSession (CPU), TungstenCUDASession, TungstenHIPSession in include/tungsten/tungsten_session.hpp). Model parameters are validated at startup (see parameter validation).
Problem setup¶
The Problem setup block the report contract (#112) asks every application
to carry. Two shipped configurations are described below:
tungsten_moving_bc.json, the directional-solidification case, and
tungsten_single_seed.json, the smallest demonstration. Those files are
not executed by ctest. tungsten_resolved_bcc.json is: it is the numerical
resolution preset. It does not calibrate tungsten.
Item |
Directional solidification ( |
Single seed ( |
|---|---|---|
Use case |
A solidification front sweeping through undercooled tungsten, with a grid of differently oriented seeds ahead of it |
Smallest runnable configuration |
Question |
Where does the front sit, how fast does it advance, and what grain structure does it leave? |
Does the stack run end to end? |
Domain |
|
|
Grid/time |
|
|
Boundary conditions |
Periodic on all three axes (the FFT has no other mode), plus a |
Periodic on all three axes plus a stationary |
Initial condition |
Uniform |
Uniform |
Key physical parameters |
|
Identical parameter block |
Observable |
Tracked front position |
Same checksums; |
Model maturity |
numerical verification: regression – split-run equivalence to |
Same |
Why periodic, and what fixed/moving really are¶
The FFT treats the domain as periodic on all three axes, always. fixed and
moving are not boundary conditions in the usual sense: they are field
modifiers applied every step that overlay a sigmoid density band near one end
of the box, pulling psi towards rho_low outside it and rho_high inside.
Physically that band is a reservoir keeping melt available for the front to
grow into and stopping the growing solid from meeting its own periodic image
head-on. It is a penalty layer inside a periodic box, not a wall. What it
excludes is a real free surface: no vapour interface, no substrate, no
container.
Caveat: the single-seed preset does not show a seed¶
SingleSeed hard-codes its radius at 64 reduced units, larger than the
17.8-unit half-width of the 32^3 box, so the “seed” fills the whole domain
and no melt is left around it. Use the 256^3
tungsten_single_seed_256_cuda.json when
you want an actual nucleus in melt.
Caveat: tungsten_moving_bc_options.json does not load¶
That file’s boundary_conditions entry has initial_position but no xpos.
MovingBC::from_json requires xpos and no code reads initial_position, so
the file throws at startup. It documents a feature that does not exist.
Binaries (after OpenPFC_BUILD_APPS=ON)¶
Target |
When |
|---|---|
|
Always (CPU FFT / HeFFTe) — |
|
Always — alias of |
|
CUDA spectral — |
|
CUDA spectral — alias of |
|
HIP spectral — |
|
HIP spectral — alias of |
|
HIP + MPI device-buffer check |
|
Always — resolution study, see below. Minutes, not a test |
Install path when using cmake --install: <prefix>/bin/.
Single-rank FFT¶
Spectral binaries accept root-level "fft": "local" in JSON, or
fft = "local" before any table in TOML. This selects a direct FFTW (CPU),
cuFFT (CUDA) or rocFFT (HIP) 3-D real/complex plan. The communicator must have exactly one rank, with the
whole domain on that rank. A distributed run rejects this choice at startup.
Set "fft": "heffte" to keep HeFFTe on one rank. Omit fft and CPU or
CUDA stays on HeFFTe; one HIP rank that owns the whole domain then uses
the direct rocFFT plan. More than one rank always uses HeFFTe.
The hardware backend choice is independent of fft; existing
plan_options affect the HeFFTe path only.
Both paths retain the same spectral layout, inverse normalization and
preserved inverse input, but roundoff differences are possible. Startup
prints SPECTRAL_FFT implementation=local backend=cufft ranks=1, with
backend=fftw or backend=rocfft on those platforms, so a run records which
implementation it used. The host implementation
uses aligned scratch to preserve both input vectors. Its planning/destruction
must be serialized with other FFTW planners; it leaves global FFTW thread
settings and cleanup under application control.
Inputs¶
Location |
Format |
|---|---|
JSON (mirrors TOML structure) |
|
|
TOML (same scenarios as JSON; no separate README) |
Start from inputs_json/tungsten_single_seed.json or inputs_toml/tungsten_single_seed.toml. A \(3\times 3\) polycrystal (grains that actually meet) is tungsten_polycrystal.json. Heavy performance cases: tungsten_performance.*.
Optional directional-solidification keys on model.params (same names as aluminum): G_grid, V_grid, x_initial. Default is isothermal (G_grid = 0). The linear operator stays at JSON T; the pointwise cubic uses T + G (x' - x_initial - V t). CPU / CUDA / HIP share tungsten_pointwise.hpp.
Run (from build tree)¶
cd build
mpirun -n 4 ./apps/tungsten/tungsten ../apps/tungsten/inputs_json/tungsten_single_seed.json
Use tungsten_cuda / tungsten_hip when built; pass the same config path. On LUMI-G, see docs/INSTALL.LUMI.md and docs/lumi_slurm/README.md.
Restart a moving front¶
Use a checkpoint bundle to continue both the field and its tracked front.
A raw psi_*.bin dump contains no boundary state.
For a first leg, copy inputs_json/tungsten_moving_bc.json, set
timestepping.t1 to 100, change fields[0].data to
results/tungsten/psi_%04d.bin, and add:
"checkpoint": {"every": 100, "directory": "results/tungsten/checkpoints"}
Create results/tungsten, run that config, then use
inputs_json/tungsten_restart.json to continue
from step 100 to step 200. The TOML restart input describes the same case.
Create results/tungsten/restarted before running the second leg; its field
dumps and new checkpoints use this separate output directory.
Keep the original t0, dt, domain, physics parameters, and the ordered
boundary_conditions configuration. The moving-BC keys are type, target,
rho_low, rho_high, width, alpha, disp, xpos and the optional
mode and threshold. Here xpos is the original configured position;
restart replaces it with the saved front.
The front is the last x column whose statistic exceeds the threshold.
mode selects the statistic: amplitude (default) compares the column
maximum minus the column mean, the crystal’s peak height above the local
melt level, with a fraction (threshold, default 0.5) of the strongest
column’s amplitude; it does not depend on the melt density level, the
reservoir band (a damped crystal) and weak nuclei ahead of the front stay
liquid, and a low-amplitude crystal is still found. mean_field compares
the column mean with the midpoint of rho_high and rho_low, for a model
whose solid and liquid differ in mean density or for a smooth field such as
a concentration; here the melt sits at rho_high, so it does not apply.
cutoff is the historical rule, the column maximum above the absolute
level 0.1. The shipped cases pin "mode": "cutoff": their recorded
trajectories and the restart tests depend on that rule, which also counts
weak nuclei ahead of the front as solid and so advances faster than the
amplitude tracker. threshold overrides the default of any mode.
Do not combine restart_from with simulator.increment or
simulator.result_counter. Initial conditions may be omitted on restart.
The bundle’s metadata.json stores each BC’s configuration and state.
For moving, state consists of unwrapped xpos, scan index idx, and
the first-detection flag first. Restored fronts continue scanning without
repeating initial detection. Missing/invalid front state or a changed BC
configuration is a startup error. Older bundles remain loadable with
stateless boundaries. CPU regression tests compare uninterrupted and split
runs on one and two MPI ranks: local fields agree within 1e-12 and saved
front state agrees exactly.
Checkpoints publish after scheduled field output, so the restored result
counter identifies the next filename, including when a save and checkpoint
fall on the same step.
Code map¶
Area |
Path |
|---|---|
Physics (schema, k-space symbols, |
|
Session aliases + catalog registration |
|
ICs / BCs / writers |
Framework catalogs: |
Device instantiation of the nonlinearity |
|
Grid criteria for dealiasing |
|
Resolution study |
|
|
|
Dealiasing and resolution¶
This section is a numerical resolution qualification. It does not calibrate the model and it does not convert the representative coefficients to SI.
inputs_json/tungsten_resolved_bcc.json is the selected preset. The box is
one BCC cell, side \(a=2\pi\sqrt{2}\), with 16 points along that side. That is
11.31 points per period of \(|k|=1\), inside the cubic 1/2 rule (8 points per
period, dealias_clean_dx). model.dealias is true, so the installed
TungstenSession path applies the Orszag 2/3 mask to the nonlinear update.
At this spacing the cut sits above the second harmonic, so the mask does not
delete the crystal.
The gate is that file run through TungstenSession for \(t\in[0,20]\), \(dt=1\),
against the same model, the same box and the same mask on a \(32^3\) grid.
The density RMS stays within \(10^{-6}\) relative and the density maximum
within \(10^{-4}\) relative. The test additionally compares the full
coarse-grid field with the 32³ field at the same physical nodes, and
the six complex normalized first-shell BCC coefficients; these comparisons
detect phase or structural disagreements that scalar extrema can hide. Turning the mask off on the \(16^3\) preset changes
the maximum by less than \(10^{-4}\), and the power in \(|k|>2\) by less than
\(10^{-4}\). The same mask comparison at the historical spacing (\(8^3\) on this
box, \(\Delta x=1.11072\), 5.66 points per period) moves the maximum by about
one percent and removes most of that high-mode power. That spacing fails
both grid criteria, so it is not selected.
The cell is bulk crystal. It has no solidification front. TungstenPhysics
does not expose a free-energy density, so the gate uses the density maximum,
the density RMS and the \(|k|>2\) power. Cost is secondary. ctest also runs
the mask-off twin and the \(8^3\) pair, all at 20 steps on one CPU rank.
tungsten-resolved-bcc-cli separately executes the actual tungsten
binary with this maintained JSON and checks the finite-field checksum.
To capture the numeric acceptance record in a local or source-pinned CI
log, run build/apps/tungsten/test_tungsten "[preset]"; it prints
TUNGSTEN_BCC with field and Fourier-mode errors.
Limitation: high_mode_power is the final density tail for
|k|>2, not a direct estimate of alias energy folded into retained
low-wavenumber modes of the nonlinear RHS. The declared 2/3 output mask
is not a generic cubic 1/2-rule de-aliaser. This one-cell 20-step gate
must not be generalized to all solidification fronts or parameter sets
without an independent nonlinear-product alias benchmark.
The historical solidification presets stay at \(\Delta x=1.1107\) and omit
model.dealias, so their mask stays off. They are demonstration
configurations. The crystal sits at \(k_0=1\) and carries real content at
\(2k_0\) and \(3k_0\); with that spacing, \(k_{\mathrm{Ny}}=2.828\), so the third
harmonic at 3.0 folds back onto \(k=2.657\), and the 2/3 cut at 1.886 sits
below the second harmonic at 2.0. Switching the mask on there would delete
part of the crystal. Both conditions clear at \(\Delta x\le\pi/3\), six points
per lattice period. Eight points is the cubic 1/2 rule.
An earlier tungsten_dealias_study (same seeded solidification, same IC,
1000 steps, mask off then on, not this CI gate) measured:
points / period |
Grid spacing \(\Delta x\) |
\(\Delta\) power |
Peak density change \(\Delta\max\psi\) |
Peak wavenumber change \(\Delta k_1\) |
|---|---|---|---|---|
5.66 (historical) |
1.1107 |
1.70 % |
1.07 % |
9e-06 |
6.00 |
1.0472 |
1.27 % |
1.04 % |
1.4e-04 |
6.67 |
0.9425 |
0.48 % |
0.29 % |
4.7e-04 |
8.00 |
0.7854 |
0.009 % |
0.018 % |
2.5e-06 |
On that historical spacing the long study saw about 2% in amplitude and spectral power, and essentially none in the selected wavenumber \(k_1\). The \(k_1\) column is scatter between two nearly identical runs, not a trend. The qualified preset above is a different grid and a shorter horizon; its tolerance is the ctest gate, not this table.
Regenerate the data (minutes, single rank):
./apps/tungsten/tungsten_dealias_study \
../out/report/data/tungsten_dealias_resolution.csv 1000
python3 out/report/figures/make_figures.py
See the resolution section of the tungsten chapter in the applications report for the full discussion.
See also¶
docs/app_pipeline.md— JSON → session pipelinedocs/applications.md— all shipped appsdocs/io_results.md— binary result writers fromfields