How it works

Building a simulation

A Struphy run is one Simulation object. You assemble it from a model and the pieces that say where it lives, how it is discretized and how it steps, then call run(). Nothing is hidden in a configuration format: the arguments below are the whole setup, and each one is a Python object you can build, print and reuse.

Anatomy of a run

Every argument Simulation takes, grouped by the question it answers. The ones carrying a browse link have a catalogue on this site — 36 models, 44 propagators, and the domains, equilibria, perturbations and backgrounds they are configured with.

What is solved

The physics: a model, which brings its own species, variables and propagators.

modelStruphyModelrequired

Physics model that provides species, propagators and variables.

Browse Models →

Where it lives

The mapped geometry, the background it is initialized from, and the element grid covering it.

domainDomain= Cuboid()

Computational domain description.

Browse Domains →
equilFluidEquilibrium= None

Initial fluid equilibrium (may be None).

Browse Equilibria →
gridgrids.TensorProductGrid= TensorProductGrid()

Spatial grid used for FEEC variables.

Browse FEEC basics →

num_elements · mpi_dims_mask

How it is discretized

Spline degrees and boundary conditions for the de Rham complex the fields live in.

derham_optsDerhamOptions= DerhamOptions()

Options for discrete differential operators.

Browse FEEC basics →

degree · bcs · nquads · nquads_proj · polar_splines · local_projectors

How it steps

Step size, end time, and how the model's propagators are composed over a step.

time_optsTime= Time()

Time-stepping options (dt, Tend, split algorithm, ...).

Browse Time integration →

dt · Tend · split_algo

How it runs

Output folders, restarts, MPI, and what the profiler records — everything around the physics.

envEnvironmentOptions= EnvironmentOptions()

Runtime and output environment options.

out_folders · sim_folder · sim_label · restart · max_runtime · save_step · save_restart · sort_step · num_clones

profiling_optsProfilingOptions= ProfilingOptions()

Options passed to scope-profiler when profiling is active.

file_path · label · use_likwid · perf_events · use_line_profiler · use_memray · memory_profile_path · memray_native_traces · memray_trace_python_allocators · memray_follow_fork · deactivate_profiling · use_nvtx … 29 in total

commMPI.Intracomm= None

MPI communicator for parallel execution. If None, uses MPI.COMM_WORLD.

logging_levelint | None= None

Logging level (e.g., logging.INFO, logging.DEBUG). If None, uses default.

How it is labelled

Carried into the run's metadata, so a saved run says what it was.

namestr= ''

Name of the simulation.

descriptionstr= ''

Description of the simulation.

params_pathstr= None

Path to a Python parameter file to save alongside outputs.

In code

The same pieces, in the order you usually build them. This is the shape every gallery example has, with its analysis and plotting removed.

from struphy import DerhamOptions, EnvironmentOptions, Simulation, Time
from struphy import domains, grids, perturbations
from struphy.models import Maxwell

# 1. The model brings its own species, variables and propagators.
model = Maxwell()
model.propagators.maxwell.options = model.propagators.maxwell.Options(algo="implicit")

# 2. Where it lives: a mapped domain and the elements covering it.
domain = domains.Cuboid(r3=20.0)
grid = grids.TensorProductGrid(num_elements=(1, 1, 128))

# 3. How the fields are discretized on that grid.
derham_opts = DerhamOptions(degree=(1, 1, 3))

# 4. Initial conditions, added to the model's variables.
model.em_fields.e_field.add_perturbation(perturbations.Noise(amp=0.1, comp=0, seed=123))

# 5. How it steps.
time_opts = Time(dt=0.05, Tend=50.0, split_algo="LieTrotter")

# 6. Everything above, assembled into one object.
sim = Simulation(
    model=model,
    name="Maxwell light-wave dispersion",
    env=EnvironmentOptions(out_folders="runs", sim_folder="maxwell_light_wave"),
    domain=domain,
    grid=grid,
    derham_opts=derham_opts,
    time_opts=time_opts,
)

sim.run(profiling_activated=True)   # allocate, initialize, step, save
sim.pproc()                         # evaluate the FEEC fields onto a grid
sim.load_plotting_data()            # read the result back for analysis

What run() does

Setup happens once — all of it inside one setup: total region — and then the time loop advances the model and saves at intervals. Struphy instruments each phase, so these names are exactly the regions you can read off the performance charts on an example page.

  1. setup: run metadata
  2. setup: data storage
  3. setup: geometry vtk
  4. setup: plasma params
  5. setup: restart
  6. setup: initial diagnostics
  7. setup: hdf5 datasets
  8. model.integrate(dt, split_algo)once per step, until Tend — each step composing the model's propagators

How that last line composes the propagators is the subject of time integration; what the propagators themselves solve is on the propagator pages.

After the run

A run writes into out_folders/sim_folder: the field and marker data, the geometry, the parameters it ran with, and — when profiling is on — the timing file behind those performance charts.sim.pproc() evaluates the FEEC coefficients onto a grid and sim.load_plotting_data() reads them back, so analysis happens against the same objects the run used rather than a re-parsed output format.