Skip to content

Execution and Development Workflow

The project is centered on the Fortran runtime. Python handles post-processing, visualization, and utility workflows. For normal users, the recommended path is to install beach-bem and run the beach command.

Terminal window
make --version
gfortran --version
fpm --version
python --version
Terminal window
python -m pip install -U pip setuptools wheel
python -m pip install beach-bem

During pip install, make install installs both the Python CLI and the Fortran runtime binary. Pip builds use INSTALL_PROFILE=auto by default and fall back to generic when needed. Set BEACH_PIP_FALLBACK_GENERIC=0 to disable that fallback.

Terminal window
export PATH="$HOME/.local/bin:$PATH"

To try the development version directly from Git:

Terminal window
python -m pip install "git+https://github.com/Nkzono99/BEACH.git"
Terminal window
python -m pip install -U pip setuptools wheel
python -m pip install -e . --no-build-isolation
Terminal window
make check
make run CONFIG=examples/beach.toml

make check is the standard development build check. It uses BEACH_VERSION_MODE=dev to pass a stable version string such as 1.4.0-dev to the Fortran side, so changes to the git hash do not invalidate fpm’s compile-flag hash and incremental builds stay reusable.

make build and make install embed a git-hash version by default. Override the version mode when needed.

Terminal window
make build VERSION_MODE=dev
make build VERSION_MODE=plain
make build VERSION_MODE=git

Install profiles can also be selected explicitly.

Terminal window
make install-generic
make install-camphor
Terminal window
fpm run --profile release --flag "-fopenmp" -- examples/beach.toml

For normal development, prefer make run / make check through build.sh. The wrapper passes __BEACH_VERSION__ and __BEACH_VERSION_MODE__ consistently.

Terminal window
make test-l0 # L0: static/schema/build check
make test # L1: normal development loop
make test-l2 # L2: contract/integration
make test-l3 # L3: heavy/release gate
make test-heavy # heavy Fortran targets only
make test-fortran-far-correction # explicit oracle far-correction diagnostics
make test-full # unfiltered fpm test

The test suite is tiered for the development loop.

  • L0: git diff --check, JSON schema parse check, make check
  • L1: L0 + Python tests + lightweight Fortran test targets (make test / make test-l1)
  • L2: L1 + contract/integration targets such as the C field-kernel contract
  • L3: L2 + heavy FMM targets for release gates, nightly checks, and integration before main

make test-fortran aliases the lightweight Fortran targets. Heavy FMM targets such as test_dynamics_fmm and test_coulomb_fmm_core_basic are excluded from normal make test and must be run with make test-l3, make test-heavy, make test-fortran-heavy, or make test-full. The m2l_root_oracle far-correction diagnostics are even heavier and require make test-fortran-far-correction or unfiltered make test-full.

Run a single target with:

Terminal window
FPM_ACTION=test ./build.sh --target test_version

On KUDPC login nodes, do not run make test*, fpm test, or equivalent build/test payloads directly. Submit them to compute nodes with tssrun or sbatch.

Usually, edit beach.toml and pass that file directly to beach. See beachx config / High-Level Notation Guide for the high-level notation layer.

  1. Prepare beach.toml.
  2. Check it with beachx lint beach.toml.
  3. Run the simulation with beach beach.toml.
  4. Inspect files under output.dir.
  5. Visualize with Python CLI commands or the Beach API.

See Input Parameters Reference for the beach.toml specification.

Terminal window
mkdir run_periodic2
cd run_periodic2
beachx config init
beachx lint beach.toml
beach beach.toml
  1. Prepare beach.toml (see Input Parameters Reference).
  2. Use high-level notation if useful; the Fortran parser resolves it while loading.
  3. Run the simulation with beach beach.toml.
  4. Inspect output.dir.
  5. Visualize with a Python CLI command or the Beach API.
Terminal window
beach beach.toml

Without arguments, beach reads beach.toml from the current directory.

Terminal window
OMP_NUM_THREADS=8 beach beach.toml

After installing an MPI build, launch beach through the MPI runner.

Terminal window
mpirun -n 4 beach examples/beach.toml

Enable coarse phase profiling with:

Terminal window
BEACH_PROFILE=1 OMP_NUM_THREADS=8 beach examples/beach.toml

For scaling comparisons, use the rank_max_s value on the simulation_total row in performance_profile.csv.

Visualization example:

Terminal window
beachx profile outputs/latest/performance_profile.csv \
--save outputs/latest/performance_profile.png

Primary outputs:

  • summary.txt
  • charges.csv
  • mesh_potential.csv when write_mesh_potential = true
  • mesh_triangles.csv
  • mesh_sources.csv
  • charge_history.csv when history_stride > 0
  • potential_history.csv when write_potential_history = true and history_stride > 0
  • performance_profile.csv when BEACH_PROFILE=1
  • rng_state.txt
  • macro_residuals.csv

mesh_triangles.csv includes mesh_id for each element. mesh_sources.csv maps each mesh_id to the source template kind, surface model, epsilon_r, and element count. conductor is relaxed as a floating conductor only with field_bc_mode = "free". dielectric is metadata-only in the current implementation; summary.txt also prints a note when it appears. Enabling mesh_potential.csv stores centroid potential [V] with the same element ordering.

MPI runs (world_size > 1) write rank-local RNG state and residual files.

  • rng_state_rank00000.txt, rng_state_rank00001.txt, …
  • macro_residuals_rank00000.csv, macro_residuals_rank00001.csv, …

For reservoir_face / photo_raycast, particle counts per batch are dynamic, so estimating the workload first is recommended.

Terminal window
beachx workload examples/beach.toml --threads 8

Rank-local estimate with residuals:

Terminal window
beachx workload examples/beach.toml \
--threads 8 \
--mpi-ranks 4 \
--mpi-rank 0 \
--macro-residuals outputs/latest/macro_residuals_rank00000.csv
[output]
dir = "outputs/latest"
resume = true

Rerunning beach with the same output.dir reads summary.txt, charges.csv, and RNG state and continues from the checkpoint. Use restart_from when the checkpoint directory and the new output directory differ.

[output]
dir = "outputs/continuation"
resume = true
restart_from = "../parent_run/outputs/latest"

In that case, checkpoint files are read from restart_from, while the new summary.txt, charges.csv, history files, and RNG state are written under dir.

sim.batch_count is the cumulative target batch count. If an existing checkpoint has batches=100 and the new batch_count=150, only 50 additional batches run. If batch_count is smaller than the completed checkpoint batch count, execution stops.

For MPI resume, the mpi_world_size in summary.txt must match the current number of ranks.

Terminal window
beachx inspect outputs/latest \
--save-bar outputs/latest/charges_bar.png \
--save-mesh outputs/latest/charges_mesh.png \
--save-potential-mesh outputs/latest/potential_mesh.png \
--potential-self-term area-equivalent
# Draw a sim.field_bc_mode = "periodic2" mesh wrapped into the periodic cell.
beachx inspect outputs/latest \
--save-mesh outputs/latest/charges_mesh_periodic.png \
--save-potential-mesh outputs/latest/potential_mesh_periodic.png \
--apply-periodic2-mesh
# Tile the periodic mesh by n layers. For 1, this draws 3x3 = 9 copies.
beachx inspect outputs/latest \
--save-mesh outputs/latest/charges_mesh_tiled.png \
--periodic2-repeat 1 \
--apply-periodic2-mesh
beachx animate outputs/latest \
--quantity charge \
--save-gif outputs/latest/charge_history.gif \
--total-frames 200
beachx slices outputs/latest \
--grid-n 200 \
--vmin -20 --vmax 20 \
--save outputs/latest/potential_slices.png
beachx coulomb outputs/latest \
--component z \
--save outputs/latest/coulomb_force_z.png
beachx mobility outputs/latest \
--density-kg-m3 2500 \
--mu-static 0.4 \
--save-csv outputs/latest/mobility_summary.csv
# Use the same field kernel as the Fortran FMM core to output net charge, force, and torque per object.
make build-kernel
beachx kernel-forces outputs/latest \
--save-csv outputs/latest/object_forces_kernel.csv

beachx coulomb reads object kind and order from nearby beach.toml mesh.templates when available, and by default places all objects along the target axis for visualization. Use --target-kinds sphere to restrict the target set. beachx mobility treats plane as the support by default and writes object force/torque plus lift_ratio, slide_ratio, and roll_ratio to CSV. Mass-derived indicators need --density-kg-m3 and geometry from beach.toml. beachx kernel-forces calls the Fortran FMM core through libbeach_field_kernel, using sim.softening, sim.field_bc_mode, periodic2, and tree settings from beach.toml. Build the library with make build-kernel; if it is elsewhere, pass --library or set BEACH_FIELD_KERNEL_LIB. Use --config path/to/beach.toml when no config exists near the output directory.

Legacy aliases such as beach-inspect, beach-animate-history, beach-plot-coulomb-force-matrix, beach-plot-potential-slices, beach-estimate-workload, and beach-plot-performance-profile remain available for now, but are deprecated.

from beach import Beach
beach = Beach("outputs/latest")
print(beach.result.absorbed, beach.result.escaped)
# History is always lazy-loaded.
history_step10 = beach.result.history_at(10)
if beach.result.history is not None:
print(beach.result.history.batch_indices)
beach.plot_bar()
beach.plot_mesh()
beach.plot_potential()
beach.plot_mesh(apply_periodic2_mesh=True)
beach.plot_potential(apply_periodic2_mesh=True)
beach.plot_potential_slices(
box_min=[0.0, 0.0, 0.0],
box_max=[1.0, 1.0, 10.0],
grid_n=200,
vmin=-20.0,
vmax=20.0,
)
beach.animate_mesh("outputs/latest/charge_history.gif", quantity="charge", total_frames=200)
mesh1 = beach.get_mesh(1)
mesh2, mesh3 = beach.get_mesh(2, 3)
mesh1_step10 = beach.get_mesh(1, step=10)
charge_step10 = beach.get_mesh_charge(1, step=10)
interaction = beach.calc_coulomb(target=[mesh1, mesh2], source=[mesh3], step=10)
print(interaction.force_on_a_N, interaction.torque_on_a_Nm)
fig_force, ax_force = beach.plot_coulomb_force_matrix(
component="z",
)
fig_force.savefig("outputs/latest/coulomb_force_z.png", dpi=150)
mobility = beach.analyze_coulomb_mobility(
density_kg_m3=2500.0,
mu_static=0.4,
)
for record in mobility.records:
print(record.label, record.lift_ratio, record.slide_ratio)
Terminal window
FPM_FC=mpiifort \
fpm test --target test_mpi_hybrid \
--flag "-fpp -DUSE_MPI -qopenmp" \
--runner "mpirun -n 2"

10. Easy-to-Misread Implementation Behaviors

Section titled “10. Easy-to-Misread Implementation Behaviors”
  • A normal run advances exactly sim.batch_count batches. A resume run advances from the checkpoint batch count until sim.batch_count is reached.
  • sim.tol_rel is a monitoring value. It is not used as an early-stop condition in the current implementation.
  • The Fortran electric field uses element-centroid point-charge approximation plus sim.softening.

For a camphor MPI job example, see examples/job_scripts/camphor_mpi_hybrid_job.sh.