From c20227258f3162dd94d2708b382d09b9fad52f06 Mon Sep 17 00:00:00 2001 From: maxlin Date: Wed, 29 Jul 2026 15:09:23 +0200 Subject: [PATCH] Added claude skill --- .claude/skills/setup-simulation/SKILL.md | 158 +++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 .claude/skills/setup-simulation/SKILL.md diff --git a/.claude/skills/setup-simulation/SKILL.md b/.claude/skills/setup-simulation/SKILL.md new file mode 100644 index 000000000..0bc5eb8fe --- /dev/null +++ b/.claude/skills/setup-simulation/SKILL.md @@ -0,0 +1,158 @@ +--- +name: setup-simulation +description: 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 running the simulation and post-processing results. Triggers on requests like "set up a simulation", "create a params file for ", "configure a run of ", or "how do I run struphy". +--- + +# Setting up a Struphy simulation + +Struphy simulations are configured as plain Python scripts (`params_.py`) that +build a `Simulation` object from the Struphy API, then call `sim.run()`. There is no +YAML/JSON config — the params file *is* the config, so it can use real Python +(loops, conditionals, computed values) to derive parameters. + +## Workflow + +1. **Confirm Struphy is installed and compiled** (`struphy compile`) if this is a fresh + checkout — see `doc/sections/install.rst`. Skip 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 under `examples///params_*.py` + (list with `ls examples/`) and adapt it. This repo's examples 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 ` — writes `params_.py` in the cwd with + every option pre-filled to defaults and commented. + - List available models with `struphy params --help` (shows all model names grouped + as Fluid/Kinetic/Hybrid) or `ls src/struphy/models/*.py`. + - Read the model's docstring (`src/struphy/models/.py`) for its species, + variables, and propagators before wiring options — don't guess propagator names. + +3. **Edit the params file** following the structure below. + +4. **Run it:** + - `python params_.py` + - `mpirun -n python params_.py` for multiple MPI processes (all Struphy + data structures are MPI-aware, so this works unchanged). + +5. **Post-process** with a sibling `pproc_.py` script (see pattern below) if the + user wants plots or derived quantities beyond what's dumped live. + +## Anatomy of a params file + +Based on `examples/VlasovAmpereOneSpecies/two_stream/params_two_stream.py` and +`doc/sections/quickstart.rst`. Sections, in order: + +```python +# 1. Free-text description (printed at run start, keeps runs traceable) +description = """...""" + +# 2. Imports from the top-level struphy API +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 + +# 3. Model instance (constructor kwargs are model-specific, e.g. alpha/epsilon/with_B0) +model = (...) +model..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(sim_folder="sim_data") # output folder, restart, save_step, ... +time_opts = Time(dt=0.1, Tend=50.0, split_algo="LieTrotter") +domain = domains.Cuboid(r1=31.42) # see src/struphy/geometry/domains.py +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..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 the model source or an example. +model.propagators..options = model.propagators..Options(...) + +# 8. Backgrounds, perturbations, initial conditions +# - Fields: model...add_background(FieldsBackground()) +# model...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..var.add_background(maxwellian) +perturbation = perturbations.ModesCos(amps=(0.001,), ls=(1,)) +init = maxwellians.Maxwellian3D(n=(0.5, perturbation), u1=(3.0, None)) +model..var.add_initial_condition(init) + +# 9. Run — guard with __main__ so pproc scripts can `import params_ as params` +# without triggering a run. +if __name__ == "__main__": + sim.run() +``` + +Key building blocks and where to look them up: + +| Piece | Source | +|---|---| +| Domains (`Cuboid`, `HollowTorus`, `Tokamak`, ...) | `src/struphy/geometry/domains.py` | +| Grid | `src/struphy/topology/grids.py` (`TensorProductGrid`) | +| Fluid equilibria | `src/struphy/fields_background/equils.py` | +| Perturbations | `src/struphy/initial/perturbations.py` | +| Kinetic backgrounds | `src/struphy/kinetic_background/maxwellians.py` | +| Options dataclasses (`Time`, `BaseUnits`, `DerhamOptions`, `EnvironmentOptions`, `FieldsBackground`) | `src/struphy/io/options.py` (full docstrings with every field) | +| Models and their propagators/species | `src/struphy/models/.py` | + +## Post-processing pattern + +```python +import params_ as params # importing does NOT re-run the sim (guarded above) +from struphy import PostProcessor, PlottingData + +pp = PostProcessor(sim=params.sim) +pp.process() + +pdata = PlottingData(sim=params.sim) +pdata.load() +# pdata.f...f_binned / grid_e1 / grid_v1, etc. +``` + +For scalar diagnostics (energies, ...) saved every step, read directly from +`/data/data_proc0.hdf5` (`scalar/`, `time/value`) — no +post-processing needed, as shown in `examples/VlasovAmpereOneSpecies/two_stream/pproc_two_stream.py`. + +## Common pitfalls + +- Don't invent propagator or option names — every model has a different propagator + set; read the model file or copy from a matching example instead of guessing. +- 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. +- `sim.run()` must stay behind `if __name__ == "__main__":` so the params file can be + safely imported for post-processing. +- Use `struphy params --check-file .yml` to validate parameters if + the user is working from a legacy YAML-style params file rather than the current + Python API.