setup-simulation

Use when creating or editing a Struphy simulation parameter file (params_*.py) - choosing a model, configuring domain/grid/time-stepping, species/backgrounds/perturbations and propagator options, then

SKILL.md

GitHub

Setting up a Struphy simulation

Struphy simulations are configured as plain Python scripts (params_<name>.py) that build a Simulation object from the Struphy API, then call sim.run(). The params file can use real Python (loops, conditionals, computed values) to derive parameters. Each run writes run_metadata.json, containing a configuration snapshot and runtime facts such as the MPI rank count. Post-processing reads this saved metadata directly.

This skill ships with the installed Struphy package, so it matches the installed version. struphy agents prints the general usage guide and the examples URL for that version.

Workflow

  1. Confirm Struphy is installed and compiled if this is a fresh environment: struphy compile --status must report all kernels compiled; otherwise run struphy compile. Install help: https://struphy-hub.github.io/struphy/sections/install.html. Skip this if the user is clearly iterating on an existing working setup.

  2. Pick a starting point, in this order of preference:

    • Find the closest existing example and adapt it. Examples live in <ModelName>/<case>/params_*.py of https://github.com/struphy-hub/struphy-examples (not in the pip package; struphy agents prints the URL of the tag for the installed version, clone it with git clone --branch vX.Y.Z ...). They are the most reliable reference because model-specific options (which propagators exist, what each species needs) vary per model and are easiest to get right by copying a working case.
    • If no example matches, scaffold a default with the CLI: struphy params <ModelName> writes params_<ModelName>.py in the cwd with every option pre-filled to defaults and commented (-y skips the prompt).
    • List available models with struphy params --help (all model names), or struphy --fluid, struphy --kinetic, struphy --hybrid for one group. Model descriptions: https://struphy-hub.github.io/struphy/sections/models.html.
    • Read the model's docstring for its species, variables and propagators before wiring options, e.g. python -c "from struphy.models import Maxwell; help(Maxwell)". Don't guess propagator names.
  3. Edit the params file following the structure below.

  4. Check it with struphy params <ModelName> --check-file params_<name>.py.

  5. Run it:

    • python params_<name>.py
    • mpirun -n <N> python params_<name>.py for multiple MPI processes (all Struphy data structures are MPI-aware, so this works unchanged; requires pip install mpi4py).
  6. Post-process with a sibling pproc_<name>.py script if the user wants plots or derived quantities beyond what's dumped live. See the postprocess-simulation skill.

Anatomy of a params file

Based on the example VlasovAmpereOneSpecies/two_stream/params_two_stream.py and the quickstart (https://struphy-hub.github.io/struphy/sections/quickstart.html). Sections, in order:

# 1. Free-text description (printed at run start, keeps runs traceable)
description = """..."""

# 2. Imports from the top-level struphy API
from pathlib import Path

from struphy import (
    BaseUnits, DerhamOptions, EnvironmentOptions, FieldsBackground,
    Simulation, Time, domains, equils, grids, perturbations,
)
# Kinetic species only:
from struphy import (
    BinningPlot, BoundaryParameters, KernelDensityPlot, LoadingParameters,
    SavingParameters, SortingParameters, WeightsParameters, maxwellians,
)
from struphy.models import <ModelName>

# 3. Model instance (constructor kwargs are model-specific, e.g. alpha/epsilon/with_B0)
model = <ModelName>(...)
model.<species>.<variable>.save_data = True   # opt in/out of saving each variable

# 4. Simulation-level config (all optional, sensible defaults exist)
base_units = BaseUnits()                                   # x, B, n, kBT -> derived units
env = EnvironmentOptions(
    out_folders=str(Path(__file__).resolve().parent),
    sim_folder="sim_data",
)  # output beside the script, independent of the working directory
time_opts = Time(dt=0.1, Tend=50.0, split_algo="LieTrotter")
domain = domains.Cuboid(r1=31.42)                           # see help(domains.Cuboid)
equil = None                                                # or e.g. equils.HomogenSlab()
grid = grids.TensorProductGrid(num_elements=(32, 1, 1))
derham_opts = DerhamOptions(degree=(3, 1, 1))               # spline degree, bcs, quadrature

# 5. Build the Simulation (this is what sim.run() acts on)
sim = Simulation(
    model=model, params_path=__file__, env=env, time_opts=time_opts,
    domain=domain, equil=equil, grid=grid, derham_opts=derham_opts,
)

# 6. Kinetic species only: markers (loading/weights/boundary/sorting/saving)
loading_params = LoadingParameters(ppc=1000, moments=(...))
weights_params = WeightsParameters(control_variate=True)
boundary_params = BoundaryParameters()
sorting_params = SortingParameters(boxes_per_dim=(16, 1, 1), do_sort=True)
saving_params = SavingParameters(binning_plots=(BinningPlot(...),))
model.<kinetic_species>.set_markers(
    loading_params=loading_params, weights_params=weights_params,
    boundary_params=boundary_params, sorting_params=sorting_params,
    saving_params=saving_params,
)

# 7. Propagator options — wire each propagator listed in the model to its variables.
#    Names/required args are model-specific; check help(<ModelName>), `struphy params <ModelName>` or an example.
model.propagators.<name>.options = model.propagators.<name>.Options(...)

# 8. Backgrounds, perturbations, initial conditions
#    - Fields: model.<species>.<var>.add_background(FieldsBackground())
#              model.<species>.<var>.add_perturbation(perturbations.ModesCos(...))
#    - Kinetic: background is mandatory; if add_initial_condition() is never called,
#      the background doubles as the initial condition. Perturbations for kinetic
#      species are added to distribution moments, not the variable directly:
maxwellian = maxwellians.Maxwellian3D(n=(0.5, None), u1=(3.0, None))
model.<kinetic_species>.var.add_background(maxwellian)
perturbation = perturbations.ModesCos(amps=(0.001,), ls=(1,))
init = maxwellians.Maxwellian3D(n=(0.5, perturbation), u1=(3.0, None))
model.<kinetic_species>.var.add_initial_condition(init)

# 9. Run only when the parameter script is executed directly.
if __name__ == "__main__":
    sim.run()

Key building blocks and where to look them up. Every class has a full docstring (help(...) in Python); the module's file is printed by e.g. python -c "import struphy.geometry.domains as m; print(m.__file__)".

Piece Module
Domains (Cuboid, HollowTorus, Tokamak, ...) struphy.domains (struphy.geometry.domains)
Grid (TensorProductGrid) struphy.grids (struphy.topology.grids)
Fluid equilibria struphy.equils (struphy.fields_background.equils)
Perturbations struphy.perturbations (struphy.initial.perturbations)
Kinetic backgrounds struphy.maxwellians (struphy.kinetic_background.maxwellians)
Options dataclasses (Time, BaseUnits, DerhamOptions, EnvironmentOptions, FieldsBackground) struphy.io.options (docstrings list every field)
Models and their propagators/species struphy.models.<Model>

Lists of what is available, with parameters, under https://struphy-hub.github.io/struphy/sections/: domains.html, fluid-equils.html, perturbations.html, kinetic-equils.html and models.html (models and their propagators).

Common pitfalls

  • Don't invent propagator or option names — every model has a different propagator set; read help(<ModelName>), generate struphy params <ModelName> or copy from a matching example instead of guessing.
  • The output folder is EnvironmentOptions.sim_folder (default sim_1) inside out_folders; the post-processing script must open the same folder.
  • For kinetic species, a background is required even if only using perturbations; perturbations attach to distribution moments (via maxwellians.*), not directly to the species variable like field perturbations do.
  • Keep sim.run() behind if __name__ == "__main__": to avoid starting a run on import. Post-processing scripts should open the saved output folder directly.