From efe0bb4ed9e7aa8afb150644bae672cfa65a21fb Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 28 Sep 2026 23:37:59 +0200 Subject: [PATCH 01/16] Added TAE examples --- docs/src/examples/README.md | 1 + docs/src/examples/itpa-tae-linear-mhd.py | 190 +++++++++++++++++ docs/src/examples/itpa-tae-shear-alfven.py | 192 ++++++++++++++++++ .../pages/examples/itpa-tae-linear-mhd.py.ts | 10 + .../examples/itpa-tae-shear-alfven.py.ts | 10 + 5 files changed, 403 insertions(+) create mode 100644 docs/src/examples/itpa-tae-linear-mhd.py create mode 100644 docs/src/examples/itpa-tae-shear-alfven.py create mode 100644 docs/src/pages/examples/itpa-tae-linear-mhd.py.ts create mode 100644 docs/src/pages/examples/itpa-tae-shear-alfven.py.ts diff --git a/docs/src/examples/README.md b/docs/src/examples/README.md index 7b02b45..00fd92e 100644 --- a/docs/src/examples/README.md +++ b/docs/src/examples/README.md @@ -17,6 +17,7 @@ scripts exist today — use whichever is closest to your model and diagnostic as - `resistive-x-point.py` — nonlinear visco-resistive MHD at a driven magnetic null, with current-density/flux animation, reconnection rate and conservation diagnostics - `mhd-slab-waves.py` — the shear Alfvén and the slow and fast magnetosonic waves of `LinearMHD`, from the (k, ω) spectra of the velocity and the pressure, with fitted against exact speeds - `toroidal-shear-alfven.py` — a small `LinearMHD` tokamak run with the m=10,11 perturbations, animated physical velocity components on a poloidal slice, ring histories, radial profiles, radius–time RMS maps, poloidal and temporal FFT spectra, frequency–radius maps, dominant-band reconstruction, and perturbation energies. Defaults to 8 × 48 × 4 cells, degree (3,3,2), dt=0.5 and t=20; edit the constants at the top for longer, finer runs. The short default record has only 21 samples and Δω≈0.299; this is an exploratory preview, not a converged TAE frequency measurement. Fourier diagnostics use plasma-plots' `out.analysis.fft`, `out.analysis.time_fft` and `out.analysis.filter_time`. +- `itpa-tae-linear-mhd.py`, `itpa-tae-shear-alfven.py` — the ITPA toroidal Alfvén eigenmode benchmark (m=10,11, n=−6 in a sixth of a tokamak), with `LinearMHD` and with the reduced `ShearAlfven` model: radial power against the shear-Alfvén continua, the power spectrum against ω_TAE, the eigenfunction of each harmonic, mode amplitudes, a matrix-pencil fit and the poloidal-plane animation, all drawn by plasma-plots. Defaults to 8 × 48 × 4 cells and t=150 (about two TAE periods); the benchmark resolution is noted at the top of each script. - `zeldovich-caustic.py` — pressureless SPH collapse to a caustic and multi-stream flow, with the exact density from the Lagrangian map (animated density and phase space) - `diffusion-methods.py` — the random-walk and the deterministic particle methods for the diffusion equation, compared with the exact decay (two simulations in one script) - `incompressible-shear-relaxation.py` — incompressible SPH between no-slip walls: the pressure projection removes a compressive wave, and the shear mode decays at the exact viscous rate diff --git a/docs/src/examples/itpa-tae-linear-mhd.py b/docs/src/examples/itpa-tae-linear-mhd.py new file mode 100644 index 0000000..d5b3886 --- /dev/null +++ b/docs/src/examples/itpa-tae-linear-mhd.py @@ -0,0 +1,190 @@ +"""The ITPA toroidal Alfvén eigenmode (TAE) benchmark in ideal LinearMHD. + +The setup of Struphy's `examples/LinearMHD/itpa_tae_benchmark`: an m = 10, 11 perturbation of +the velocity in one sixth of a circular tokamak, whose coupled harmonics ring at the frequency +of the toroidal Alfvén gap. The frequency is read off the power spectrum, located between the +shear-Alfvén continua of the two harmonics, and its radial eigenfunction reconstructed. + +Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly +(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +directory (`--show` shows them first). + +The default is a short, coarse run (t = 150, about two TAE periods, on 8 x 48 x 4 cells). The +benchmark itself uses NUM_ELEMENTS = (24, 96, 16), DEGREE = (3, 3, 3), END_TIME = 500. +""" + +import argparse + +import numpy as np + +from struphy import ( + BaseUnits, + DerhamOptions, + EnvironmentOptions, + Simulation, + Time, + domains, + equils, + grids, + perturbations, +) +from struphy.linear_algebra.solver import SolverParameters +from struphy.models import LinearMHD +from plasma_plots import save_figure +from plasma_plots.theory.waves import alfven_continuum, tae_frequency + +STEM = "itpa-tae-linear-mhd" +NUM_ELEMENTS = (8, 48, 4) +DEGREE = (3, 3, 2) +END_TIME = 150.0 +DT = 0.2 +SAVE_STEP = 2 + +# The torus is one sixth of a full one: the sector mode n = -1 is the full-torus mode n = -6. +TOR_PERIOD = 6 +MODES = (10, 11) +SECTOR_MODE = -1 +AMPLITUDE = 1e-3 + + +def minor_radius(eta1): + """The minor radius of the hollow torus, 0.1 <= r <= 1.""" + return 0.1 + 0.9 * eta1 + + +def create_simulation() -> Simulation: + model = LinearMHD(base_units=BaseUnits()) + # A relative tolerance of 1e-6 halves the cost of the implicit steps, with no visible change. + solver_params = SolverParameters(tol=1e-6) + model.propagators.shear_alf.options = model.propagators.shear_alf.Options(solver_params=solver_params) + model.propagators.mag_sonic.options = model.propagators.mag_sonic.Options(solver_params=solver_params) + for field in (model.em_fields.b_field, model.mhd.density, model.mhd.velocity, model.mhd.pressure): + field.save_data = True + + domain = domains.HollowTorus(a1=0.1, a2=1.0, R0=10.0, sfl=False, pol_period=1, tor_period=TOR_PERIOD) + equil = equils.AdhocTorus( + a=1.0, R0=10.0, B0=3.0, q_kind=0, p_kind=1, + q0=1.71, q1=1.87, p1=0.95, p2=0.05, beta=0.0018, + ) + grid = grids.TensorProductGrid(num_elements=NUM_ELEMENTS) + derham_opts = DerhamOptions(degree=DEGREE, bcs=(("dirichlet", "dirichlet"), None, None)) + + # Radial (sin) and poloidal (cos) logical components of the velocity 2-form, the poloidal + # one from the radial derivative of the Gaussian, so that the perturbation is nearly + # divergence-free. + model.mhd.velocity.add_perturbation(perturbations.TorusModesSin( + ms=MODES, ns=(SECTOR_MODE,) * 2, amps=(AMPLITUDE,) * 2, + pfuns=("exp", "exp"), pfun_params=([0.5, 0.1], [0.5, 0.1]), + comp=0, given_in_basis="2", + )) + model.mhd.velocity.add_perturbation(perturbations.TorusModesCos( + ms=MODES, ns=(SECTOR_MODE,) * 2, amps=tuple(AMPLITUDE / (2 * np.pi * m) for m in MODES), + pfuns=("d_exp", "d_exp"), pfun_params=([0.5, 0.1], [0.5, 0.1]), + comp=1, given_in_basis="2", + )) + + env = EnvironmentOptions(out_folders="struphy_gallery_runs", sim_folder="itpa_tae_linear_mhd", save_step=SAVE_STEP) + sim = Simulation( + model=model, + name="ITPA TAE benchmark (LinearMHD)", + description=( + "The ideal-MHD part of the ITPA toroidal Alfvén eigenmode benchmark: a global Alfvén " + "wave in the gap that toroidicity opens between the shear-Alfvén continua of two " + "neighbouring poloidal harmonics. " + r"A hollow torus :math:`0.1\le r\le 1`, :math:`R_0=10`, one sixth of the full torus, " + r"carries the AdhocTorus equilibrium with :math:`B_0=3`, " + r":math:`q(r)=1.71+0.16\,r^2` and :math:`\beta=0.0018`. " + r"The continua of :math:`m=10` and :math:`m=11` at :math:`n=-6` cross where " + r":math:`q=10.5/6=1.75`, at :math:`r=0.5`, near " + r"$$\omega_\mathrm{TAE}=\frac{v_A}{2qR_0}\approx 0.096,\qquad v_A=\frac{B_0}{\sqrt{n(r)}}.$$ " + "The velocity is seeded with these two harmonics, Gaussian in radius around r = 0.55. " + "LinearMHD evolves the shear-Alfvén and the magnetosonic parts of the linearized MHD " + "equations. The default is a short, coarse run of about two TAE periods; " + "the benchmark resolution is in the script." + ), + params_path=__file__, env=env, time_opts=Time(dt=DT, Tend=END_TIME), + domain=domain, equil=equil, grid=grid, derham_opts=derham_opts, + ) + return sim + + +def pproc(sim: Simulation, show: bool = False): + """Save (and show) the figures from an existing simulation.""" + output = sim.output + equil = sim.equil + R0 = sim.domain.params["R0"] + + # The physical velocity, rotated to the radial, poloidal and toroidal directions of the torus. + output.pproc(physical=True, celldivide=(2, 2, 2), create_vtk=False) + velocity = output.evaluate("mhd/velocity_xyz").plasma.analysis.toroidal_components(R0=R0) + times = velocity.t.values + if not np.isclose(times[-1], END_TIME): + raise RuntimeError(f"Run stopped at t={times[-1]}, before requested t={END_TIME}.") + if not np.isfinite(velocity.values).all(): + raise RuntimeError("Non-finite velocity: refusing to export the example.") + u_r = velocity.sel(component="radial", drop=True).rename("u_r") + u_r.attrs.update(label="$u_r$") + + # Theory: the shear-Alfvén continua of the seeded harmonics, and the gap frequency. + n_full = SECTOR_MODE * TOR_PERIOD + v_A = lambda r: equil.params["B0"] / np.sqrt(equil.n_r(r)) + q_gap = (MODES[0] + 0.5) / abs(n_full) + r_gap = float(np.sqrt((q_gap - equil.params["q0"]) / (equil.params["q1"] - equil.params["q0"]))) + omega_tae = float(tae_frequency(q_gap, major_radius=R0, alfven_speed=v_A(r_gap))) + continuum = lambda r, m, n: {"shear Alfvén": alfven_continuum(r, m, n, equil.q_r, major_radius=R0, alfven_speed=v_A).real} + print(f"TAE gap: q = {q_gap:.3f} at r = {r_gap:.3f}, omega_TAE = {omega_tae:.4f}") + + # The measured frequency: the strongest peak of the radial velocity, refined between bins. + peaks = u_r.plasma.analysis.spectral_peaks(n_peaks=2, window="hann") + omega = float(peaks.omega_refined[0]) + probe = u_r.sel(eta1=0.45, eta2=0.0, eta3=0.0, method="nearest") + pencil = probe.plasma.analysis.matrix_pencil(n_modes=1) + print(f"Measured frequency: {omega:.4f} (FFT peak), {float(pencil.omega[0]):.4f} (matrix pencil), " + f"damping {float(pencil.gamma[0]):.2e}; omega_TAE = {omega_tae:.4f}") + + # Main figure: where each frequency lives in radius, against the continua. + radial = u_r.plasma.plot.radial_power( + window="hann", x_of=minor_radius, continuum=(continuum, [(m, n_full) for m in MODES]), + omega_max=0.5, backend="plotly", + ) + save_figure(radial, STEM, show=show) + + spectrum = u_r.plasma.plot.power_spectrum( + window="hann", peaks=2, omega_max=0.5, + frequencies={"ω_TAE (gap centre)": omega_tae}, title="Power spectrum of u_r", backend="plotly", + ) + save_figure(spectrum, f"{STEM}-spectrum", show=show) + + profiles = u_r.plasma.plot.mode_profiles(omega, x_of=minor_radius, top=4, backend="plotly") + save_figure(profiles, f"{STEM}-eigenfunction", show=show) + + amplitudes = u_r.plasma.plot.mode_amplitudes(top=4, backend="plotly") + save_figure(amplitudes, f"{STEM}-mode-amplitudes", show=show) + + fit = probe.plasma.plot.pencil_fit(n_modes=1, backend="plotly") + save_figure(fit, f"{STEM}-pencil", show=show) + + movie = u_r.plasma.plot.animation( + coords="physical", plane="RZ", eta3=0, symmetric=True, cmap="RdBu_r", max_frames=40, + title="u_r on the poloidal plane φ = 0", backend="plotly", + ) + save_figure(movie, f"{STEM}-poloidal", frame=20, show=show) + + energies = output.plot.energies(backend="plotly") + save_figure(energies, f"{STEM}-energy", show=show) + + +if __name__ == "__main__": + argparser = argparse.ArgumentParser(description="Run the ITPA TAE benchmark with LinearMHD.") + argparser.add_argument( + "--pproc-only", + action="store_true", + help="Run post-processing on an existing simulation instead of running a new one.", + ) + argparser.add_argument("--show", action="store_true", help="Show the figures before saving them.") + args = argparser.parse_args() + + simulation = create_simulation() + if not args.pproc_only: + simulation.run() + pproc(simulation, show=args.show) diff --git a/docs/src/examples/itpa-tae-shear-alfven.py b/docs/src/examples/itpa-tae-shear-alfven.py new file mode 100644 index 0000000..2f23c05 --- /dev/null +++ b/docs/src/examples/itpa-tae-shear-alfven.py @@ -0,0 +1,192 @@ +"""The ITPA toroidal Alfvén eigenmode (TAE) benchmark with the reduced ShearAlfven model. + +The setup of Struphy's `examples/ShearAlfven/itpa_tae_benchmark`: an m = 10, 11 perturbation of +the velocity in one sixth of a circular tokamak, whose coupled harmonics ring at the frequency +of the toroidal Alfvén gap. ShearAlfven keeps only the shear-Alfvén part of LinearMHD (no +magnetosonic propagator and no density or pressure), so this is the same benchmark without +compressional coupling; compare with itpa-tae-linear-mhd.py. The frequency is read off the power spectrum, located between the +shear-Alfvén continua of the two harmonics, and its radial eigenfunction reconstructed. + +Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly +(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +directory (`--show` shows them first). + +The default is a short, coarse run (t = 150, about two TAE periods, on 8 x 48 x 4 cells). The +benchmark itself uses NUM_ELEMENTS = (24, 96, 16), DEGREE = (3, 3, 3), END_TIME = 500. +""" + +import argparse + +import numpy as np + +from struphy import ( + BaseUnits, + DerhamOptions, + EnvironmentOptions, + Simulation, + Time, + domains, + equils, + grids, + perturbations, +) +from struphy.linear_algebra.solver import SolverParameters +from struphy.models import ShearAlfven +from plasma_plots import save_figure +from plasma_plots.theory.waves import alfven_continuum, tae_frequency + +STEM = "itpa-tae-shear-alfven" +NUM_ELEMENTS = (8, 48, 4) +DEGREE = (3, 3, 2) +END_TIME = 150.0 +DT = 0.2 +SAVE_STEP = 2 + +# The torus is one sixth of a full one: the sector mode n = -1 is the full-torus mode n = -6. +TOR_PERIOD = 6 +MODES = (10, 11) +SECTOR_MODE = -1 +AMPLITUDE = 1e-3 + + +def minor_radius(eta1): + """The minor radius of the hollow torus, 0.1 <= r <= 1.""" + return 0.1 + 0.9 * eta1 + + +def create_simulation() -> Simulation: + model = ShearAlfven(base_units=BaseUnits()) + # A relative tolerance of 1e-6 halves the cost of the implicit steps, with no visible change. + solver_params = SolverParameters(tol=1e-6) + model.propagators.shear_alf.options = model.propagators.shear_alf.Options(solver_params=solver_params) + for field in (model.em_fields.b_field, model.mhd.velocity): + field.save_data = True + + domain = domains.HollowTorus(a1=0.1, a2=1.0, R0=10.0, sfl=False, pol_period=1, tor_period=TOR_PERIOD) + equil = equils.AdhocTorus( + a=1.0, R0=10.0, B0=3.0, q_kind=0, p_kind=1, + q0=1.71, q1=1.87, p1=0.95, p2=0.05, beta=0.0018, + ) + grid = grids.TensorProductGrid(num_elements=NUM_ELEMENTS) + derham_opts = DerhamOptions(degree=DEGREE, bcs=(("dirichlet", "dirichlet"), None, None)) + + # Radial (sin) and poloidal (cos) logical components of the velocity 2-form, the poloidal + # one from the radial derivative of the Gaussian, so that the perturbation is nearly + # divergence-free. + model.mhd.velocity.add_perturbation(perturbations.TorusModesSin( + ms=MODES, ns=(SECTOR_MODE,) * 2, amps=(AMPLITUDE,) * 2, + pfuns=("exp", "exp"), pfun_params=([0.5, 0.1], [0.5, 0.1]), + comp=0, given_in_basis="2", + )) + model.mhd.velocity.add_perturbation(perturbations.TorusModesCos( + ms=MODES, ns=(SECTOR_MODE,) * 2, amps=tuple(AMPLITUDE / (2 * np.pi * m) for m in MODES), + pfuns=("d_exp", "d_exp"), pfun_params=([0.5, 0.1], [0.5, 0.1]), + comp=1, given_in_basis="2", + )) + + env = EnvironmentOptions(out_folders="struphy_gallery_runs", sim_folder="itpa_tae_shear_alfven", save_step=SAVE_STEP) + sim = Simulation( + model=model, + name="ITPA TAE benchmark (ShearAlfven)", + description=( + "The ideal-MHD part of the ITPA toroidal Alfvén eigenmode benchmark: a global Alfvén " + "wave in the gap that toroidicity opens between the shear-Alfvén continua of two " + "neighbouring poloidal harmonics. " + r"A hollow torus :math:`0.1\le r\le 1`, :math:`R_0=10`, one sixth of the full torus, " + r"carries the AdhocTorus equilibrium with :math:`B_0=3`, " + r":math:`q(r)=1.71+0.16\,r^2` and :math:`\beta=0.0018`. " + r"The continua of :math:`m=10` and :math:`m=11` at :math:`n=-6` cross where " + r":math:`q=10.5/6=1.75`, at :math:`r=0.5`, near " + r"$$\omega_\mathrm{TAE}=\frac{v_A}{2qR_0}\approx 0.096,\qquad v_A=\frac{B_0}{\sqrt{n(r)}}.$$ " + "The velocity is seeded with these two harmonics, Gaussian in radius around r = 0.55. " + "ShearAlfven evolves only the shear-Alfvén part of the linearized MHD equations: " + "velocity and magnetic field, with no magnetosonic coupling, density or pressure " + "perturbation. The default is a short, coarse run of about two TAE periods; " + "the benchmark resolution is in the script." + ), + params_path=__file__, env=env, time_opts=Time(dt=DT, Tend=END_TIME), + domain=domain, equil=equil, grid=grid, derham_opts=derham_opts, + ) + return sim + + +def pproc(sim: Simulation, show: bool = False): + """Save (and show) the figures from an existing simulation.""" + output = sim.output + equil = sim.equil + R0 = sim.domain.params["R0"] + + # The physical velocity, rotated to the radial, poloidal and toroidal directions of the torus. + output.pproc(physical=True, celldivide=(2, 2, 2), create_vtk=False) + velocity = output.evaluate("mhd/velocity_xyz").plasma.analysis.toroidal_components(R0=R0) + times = velocity.t.values + if not np.isclose(times[-1], END_TIME): + raise RuntimeError(f"Run stopped at t={times[-1]}, before requested t={END_TIME}.") + if not np.isfinite(velocity.values).all(): + raise RuntimeError("Non-finite velocity: refusing to export the example.") + u_r = velocity.sel(component="radial", drop=True).rename("u_r") + u_r.attrs.update(label="$u_r$") + + # Theory: the shear-Alfvén continua of the seeded harmonics, and the gap frequency. + n_full = SECTOR_MODE * TOR_PERIOD + v_A = lambda r: equil.params["B0"] / np.sqrt(equil.n_r(r)) + q_gap = (MODES[0] + 0.5) / abs(n_full) + r_gap = float(np.sqrt((q_gap - equil.params["q0"]) / (equil.params["q1"] - equil.params["q0"]))) + omega_tae = float(tae_frequency(q_gap, major_radius=R0, alfven_speed=v_A(r_gap))) + continuum = lambda r, m, n: {"shear Alfvén": alfven_continuum(r, m, n, equil.q_r, major_radius=R0, alfven_speed=v_A).real} + print(f"TAE gap: q = {q_gap:.3f} at r = {r_gap:.3f}, omega_TAE = {omega_tae:.4f}") + + # The measured frequency: the strongest peak of the radial velocity, refined between bins. + peaks = u_r.plasma.analysis.spectral_peaks(n_peaks=2, window="hann") + omega = float(peaks.omega_refined[0]) + probe = u_r.sel(eta1=0.45, eta2=0.0, eta3=0.0, method="nearest") + pencil = probe.plasma.analysis.matrix_pencil(n_modes=1) + print(f"Measured frequency: {omega:.4f} (FFT peak), {float(pencil.omega[0]):.4f} (matrix pencil), " + f"damping {float(pencil.gamma[0]):.2e}; omega_TAE = {omega_tae:.4f}") + + # Main figure: where each frequency lives in radius, against the continua. + radial = u_r.plasma.plot.radial_power( + window="hann", x_of=minor_radius, continuum=(continuum, [(m, n_full) for m in MODES]), + omega_max=0.5, backend="plotly", + ) + save_figure(radial, STEM, show=show) + + spectrum = u_r.plasma.plot.power_spectrum( + window="hann", peaks=2, omega_max=0.5, + frequencies={"ω_TAE (gap centre)": omega_tae}, title="Power spectrum of u_r", backend="plotly", + ) + save_figure(spectrum, f"{STEM}-spectrum", show=show) + + profiles = u_r.plasma.plot.mode_profiles(omega, x_of=minor_radius, top=4, backend="plotly") + save_figure(profiles, f"{STEM}-eigenfunction", show=show) + + amplitudes = u_r.plasma.plot.mode_amplitudes(top=4, backend="plotly") + save_figure(amplitudes, f"{STEM}-mode-amplitudes", show=show) + + fit = probe.plasma.plot.pencil_fit(n_modes=1, backend="plotly") + save_figure(fit, f"{STEM}-pencil", show=show) + + movie = u_r.plasma.plot.animation( + coords="physical", plane="RZ", eta3=0, symmetric=True, cmap="RdBu_r", max_frames=40, + title="u_r on the poloidal plane φ = 0", backend="plotly", + ) + save_figure(movie, f"{STEM}-poloidal", frame=20, show=show) + + energies = output.plot.energies(backend="plotly") + save_figure(energies, f"{STEM}-energy", show=show) + + +if __name__ == "__main__": + argparser = argparse.ArgumentParser(description="Run the ITPA TAE benchmark with ShearAlfven.") + argparser.add_argument( + "--pproc-only", + action="store_true", + help="Run post-processing on an existing simulation instead of running a new one.", + ) + argparser.add_argument("--show", action="store_true", help="Show the figures before saving them.") + args = argparser.parse_args() + + simulation = create_simulation() + if not args.pproc_only: + simulation.run() + pproc(simulation, show=args.show) diff --git a/docs/src/pages/examples/itpa-tae-linear-mhd.py.ts b/docs/src/pages/examples/itpa-tae-linear-mhd.py.ts new file mode 100644 index 0000000..9400732 --- /dev/null +++ b/docs/src/pages/examples/itpa-tae-linear-mhd.py.ts @@ -0,0 +1,10 @@ +import scriptSource from '../../examples/itpa-tae-linear-mhd.py?raw'; + +export function GET() { + return new Response(scriptSource, { + headers: { + 'Content-Type': 'text/x-python; charset=utf-8', + 'Content-Disposition': 'attachment; filename="itpa-tae-linear-mhd.py"', + }, + }); +} diff --git a/docs/src/pages/examples/itpa-tae-shear-alfven.py.ts b/docs/src/pages/examples/itpa-tae-shear-alfven.py.ts new file mode 100644 index 0000000..4eccff5 --- /dev/null +++ b/docs/src/pages/examples/itpa-tae-shear-alfven.py.ts @@ -0,0 +1,10 @@ +import scriptSource from '../../examples/itpa-tae-shear-alfven.py?raw'; + +export function GET() { + return new Response(scriptSource, { + headers: { + 'Content-Type': 'text/x-python; charset=utf-8', + 'Content-Disposition': 'attachment; filename="itpa-tae-shear-alfven.py"', + }, + }); +} From 119ab419ebebff1058c6c5f35a8c1f9c772afac3 Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 29 Sep 2026 08:58:03 +0200 Subject: [PATCH 02/16] Run TAE on Pitagora --- .github/precomputed-examples.json | 23 ++++++ .github/scripts/example-cache.cjs | 1 + .github/scripts/run-pitagora-example.py | 64 +++------------ .gitignore | 6 ++ docs/src/data/example-config.ts | 50 ++++++++++++ docs/src/examples/README.md | 21 ++++- docs/src/examples/itpa-tae-linear-mhd.py | 61 +++++++------- docs/src/examples/itpa-tae-shear-alfven.py | 60 +++++++------- run_example.py | 73 ++++++++++++++++- scripts/pack_precomputed_run.py | 70 ++++++++++++++++ scripts/slurm_jobs.py | 93 +++++++++++++++++++++ scripts/submit_precomputed_run.py | 95 ++++++++++++++++++++++ 12 files changed, 503 insertions(+), 114 deletions(-) create mode 100644 .github/precomputed-examples.json create mode 100644 scripts/pack_precomputed_run.py create mode 100644 scripts/slurm_jobs.py create mode 100644 scripts/submit_precomputed_run.py diff --git a/.github/precomputed-examples.json b/.github/precomputed-examples.json new file mode 100644 index 0000000..9b61746 --- /dev/null +++ b/.github/precomputed-examples.json @@ -0,0 +1,23 @@ +{ + "_comment": "Examples too expensive to simulate in CI. run_example.py downloads each archived run (a tar.gz of docs/public/examples/struphy_gallery_runs/, made by scripts/pack_precomputed_run.py), checks its sha256, unpacks it and only post-processes it. Fill in url and sha256 after uploading the archive to a release. Each job is the Slurm allocation scripts/submit_precomputed_run.py requests for the simulation (estimates; adjust after the first run).", + "itpa-tae-linear-mhd": { + "sim_folder": "itpa_tae_linear_mhd", + "url": null, + "sha256": null, + "job": { + "mpi_ranks": 32, + "time": "08:00:00", + "mem": null + } + }, + "itpa-tae-shear-alfven": { + "sim_folder": "itpa_tae_shear_alfven", + "url": null, + "sha256": null, + "job": { + "mpi_ranks": 32, + "time": "24:00:00", + "mem": null + } + } +} diff --git a/.github/scripts/example-cache.cjs b/.github/scripts/example-cache.cjs index c82f6f7..d10c777 100644 --- a/.github/scripts/example-cache.cjs +++ b/.github/scripts/example-cache.cjs @@ -6,6 +6,7 @@ async function exampleCache(example, prefix, suffix, glob) { 'catalogue_docs.py', 'generate_examples.py', 'run_example.py', + '.github/precomputed-examples.json', 'generate_example_domain.py', 'generate_domains.py', 'submodules/plasma-plots/struphy/src/struphy/**/*.py', diff --git a/.github/scripts/run-pitagora-example.py b/.github/scripts/run-pitagora-example.py index e0ba796..a0c62a6 100644 --- a/.github/scripts/run-pitagora-example.py +++ b/.github/scripts/run-pitagora-example.py @@ -6,19 +6,11 @@ import argparse import os import shlex +import sys from pathlib import Path -from slurm_script_generator.slurm_script import SlurmScript -from slurm_script_generator.squeue import SQueue - -MODULES = [ - "gcc/12.3.0", - "python/3.11.7", - "hdf5/1.14.3--gcc--12.3.0", - "cmake/3.27.9", - "netcdf-fortran/4.6.1--gcc--12.3.0", - "netlib-scalapack/2.2.0--openmpi--4.1.6--gcc--12.3.0-ucx1.20", -] +sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "scripts")) +from slurm_jobs import environment_commands, example_job, submit_and_wait # noqa: E402 # Orszag--Tang writes 401 field snapshots and post-processing loads them all # before evaluating the output grid. It exceeds the debug partition's default @@ -27,15 +19,6 @@ MPI_RANKS_BY_EXAMPLE = {"orszag-tang-vortex": 4} -def tail_log(path: Path, lines: int = 200) -> None: - """Print a bounded batch log in a collapsible GitHub Actions group.""" - if not path.is_file(): - return - print(f"::group::{path.name}") - print("\n".join(path.read_text(errors="replace").splitlines()[-lines:])) - print("::endgroup::") - - def main() -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("example", help="Gallery example stem") @@ -49,48 +32,21 @@ def main() -> int: run_id = os.environ["GITHUB_RUN_ID"] run_attempt = os.environ["GITHUB_RUN_ATTEMPT"] job_name = f"struphy-{args.example}-{run_id}-{run_attempt}" - script_path = runner_temp / f"{job_name}.sbatch" - stdout = workspace / f"slurm-{job_name}-%j.out" - stderr = workspace / f"slurm-{job_name}-%j.err" mpi_ranks = MPI_RANKS_BY_EXAMPLE.get(args.example, 1) - commands = [ - "set -euo pipefail", - f"source {shlex.quote(str(virtual_env / 'bin' / 'activate'))}", - ] - if mpi_ranks == 1: - # A one-task Slurm allocation is not an MPI launch. - commands.append("export STRUPHY_MPI=0") - else: - # mpi4py must see the Open MPI environment that `cli.py --mpi` creates. - commands.append("unset STRUPHY_MPI") + commands = environment_commands(virtual_env, mpi_ranks) commands.append(f"python cli.py run {shlex.quote(args.example)} --mpi {mpi_ranks}") - script = SlurmScript( + script = example_job( job_name=job_name, account=args.account, partition=args.partition, - nodes=1, - ntasks=mpi_ranks, - cpus_per_task=1, - time="00:30:00", + workspace=workspace, + log_dir=workspace, + commands=commands, + mpi_ranks=mpi_ranks, mem=MEMORY_BY_EXAMPLE.get(args.example), - chdir=str(workspace), - output=str(stdout), - error=str(stderr), - modules=MODULES, - custom_commands=commands, ) - job_id = script.submit_job(path=str(script_path), verbose=True) - print(f"Submitted {job_name} as Slurm job {job_id}") - - try: - state = SQueue().wait_until_done(job_id=job_id, poll_interval=15, check=True)[ - job_id - ] - print(f"Slurm job {job_id} finished with state {state or 'unknown'}.") - finally: - tail_log(workspace / f"slurm-{job_name}-{job_id}.out") - tail_log(workspace / f"slurm-{job_name}-{job_id}.err") + submit_and_wait(script, job_name, runner_temp / f"{job_name}.sbatch", workspace) return 0 diff --git a/.gitignore b/.gitignore index 0acf1ec..3d9456f 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,9 @@ docs/public/examples/struphy.log.* .idea/ .vscode/ .DS_Store + +# Cluster runs of precomputed examples (scripts/submit_precomputed_run.py) +/struphy-precomputed-*.sbatch +/slurm-*.out +/slurm-*.err +/*-run.tar.gz diff --git a/docs/src/data/example-config.ts b/docs/src/data/example-config.ts index 7e88c4e..9098a43 100644 --- a/docs/src/data/example-config.ts +++ b/docs/src/data/example-config.ts @@ -76,6 +76,56 @@ export const exampleConfig: Record = { }, }, }, + 'itpa-tae-linear-mhd': { + category: 'MHD waves', setupTitle: 'The ITPA TAE benchmark in ideal MHD', plotTitle: 'Radial power of u_r against the shear-Alfvén continua', plotAlt: 'Temporal power of the minor-radial velocity over minor radius and frequency, with the m=10 and m=11 shear-Alfvén continua at n=-6', + figures: { + 'spectrum': { + alt: 'Power spectrum of the minor-radial velocity with its two strongest peaks and the TAE gap-centre frequency', + caption: 'Power per frequency bin of the physical minor-radial velocity u_r, averaged over the sampled torus sector (Hann window, mean removed). Triangles mark the two strongest peaks, refined between bins; the dotted line is the gap-centre estimate ω_TAE = v_A/(2qR₀) at q = 1.75, r = 0.5. The record to t = 500 resolves Δω = 2π/500 ≈ 0.013.', + }, + 'eigenfunction': { + alt: 'Radial amplitude and phase of the four strongest poloidal harmonics of u_r at the strongest measured frequency', + caption: 'The complex amplitude of u_r at the strongest measured frequency, transformed over poloidal and sector toroidal angle (n = −1 in the sector is n = −6 on the full torus). Top: the amplitude of each harmonic against minor radius; bottom: its phase, drawn where the harmonic has at least 5% of its peak. The coupled harmonics of a global eigenmode have flat, locked phases.', + }, + 'mode-amplitudes': { + alt: 'Amplitudes of the four strongest poloidal and toroidal harmonics of u_r over time', + caption: 'The largest amplitude over radius of each of the four strongest (m, n) harmonics of u_r, n counted in the sector. The seeded harmonics are m = 10 and 11; toroidal coupling drives their neighbours.', + }, + 'poloidal': { + alt: 'Animated minor-radial velocity on the poloidal plane of the torus', + caption: 'Physical minor-radial velocity on the poloidal plane φ = 0, with one symmetric color range for all frames; the slider steps through 40 of the saved times.', + }, + 'energy': { + alt: 'Kinetic, magnetic and thermal perturbation energies, and the relative change of their sum', + caption: 'The perturbation energies of LinearMHD (left) and the relative change of their sum (right), which the implicit propagators conserve up to the linear-solver tolerance.', + }, + }, + }, + 'itpa-tae-shear-alfven': { + category: 'MHD waves', setupTitle: 'The ITPA TAE benchmark without compressional coupling', plotTitle: 'Radial power of u_r against the shear-Alfvén continua', plotAlt: 'Temporal power of the minor-radial velocity over minor radius and frequency in the ShearAlfven model, with the m=10 and m=11 shear-Alfvén continua at n=-6', + figures: { + 'spectrum': { + alt: 'Power spectrum of the minor-radial velocity in the ShearAlfven model, with the TAE gap-centre frequency', + caption: 'Power per frequency bin of the physical minor-radial velocity u_r, averaged over the sampled torus sector (Hann window, mean removed). Triangles mark the two strongest peaks, refined between bins; the dotted line is the gap-centre estimate ω_TAE = v_A/(2qR₀) at q = 1.75, r = 0.5. Compare with the LinearMHD run of the same setup.', + }, + 'eigenfunction': { + alt: 'Radial amplitude and phase of the four strongest poloidal harmonics of u_r at the strongest measured frequency', + caption: 'The complex amplitude of u_r at the strongest measured frequency, transformed over poloidal and sector toroidal angle (n = −1 in the sector is n = −6 on the full torus): amplitude of each harmonic against minor radius (top) and its phase (bottom, drawn where the harmonic has at least 5% of its peak).', + }, + 'mode-amplitudes': { + alt: 'Amplitudes of the four strongest poloidal and toroidal harmonics of u_r over time in the ShearAlfven model', + caption: 'The largest amplitude over radius of each of the four strongest (m, n) harmonics of u_r, n counted in the sector.', + }, + 'poloidal': { + alt: 'Animated minor-radial velocity on the poloidal plane of the torus in the ShearAlfven model', + caption: 'Physical minor-radial velocity on the poloidal plane φ = 0, with one symmetric color range for all frames; the slider steps through 40 of the saved times.', + }, + 'energy': { + alt: 'Kinetic and magnetic perturbation energies of the ShearAlfven model, and the relative change of their sum', + caption: 'The kinetic and magnetic perturbation energies (left) and the relative change of their sum (right). ShearAlfven has no pressure or density perturbation, so there is no thermal energy.', + }, + }, + }, 'ordinary-mode-dispersion': { category: 'Plasma waves', setupTitle: 'Electromagnetic waves above the plasma cutoff', plotTitle: 'Ordinary-mode frequencies and the plasma cutoff', plotAlt: 'Four measured ordinary-wave frequencies on the cold-plasma dispersion curve, with their electric-field oscillations', figures: { diff --git a/docs/src/examples/README.md b/docs/src/examples/README.md index 00fd92e..a7ee19f 100644 --- a/docs/src/examples/README.md +++ b/docs/src/examples/README.md @@ -17,7 +17,7 @@ scripts exist today — use whichever is closest to your model and diagnostic as - `resistive-x-point.py` — nonlinear visco-resistive MHD at a driven magnetic null, with current-density/flux animation, reconnection rate and conservation diagnostics - `mhd-slab-waves.py` — the shear Alfvén and the slow and fast magnetosonic waves of `LinearMHD`, from the (k, ω) spectra of the velocity and the pressure, with fitted against exact speeds - `toroidal-shear-alfven.py` — a small `LinearMHD` tokamak run with the m=10,11 perturbations, animated physical velocity components on a poloidal slice, ring histories, radial profiles, radius–time RMS maps, poloidal and temporal FFT spectra, frequency–radius maps, dominant-band reconstruction, and perturbation energies. Defaults to 8 × 48 × 4 cells, degree (3,3,2), dt=0.5 and t=20; edit the constants at the top for longer, finer runs. The short default record has only 21 samples and Δω≈0.299; this is an exploratory preview, not a converged TAE frequency measurement. Fourier diagnostics use plasma-plots' `out.analysis.fft`, `out.analysis.time_fft` and `out.analysis.filter_time`. -- `itpa-tae-linear-mhd.py`, `itpa-tae-shear-alfven.py` — the ITPA toroidal Alfvén eigenmode benchmark (m=10,11, n=−6 in a sixth of a tokamak), with `LinearMHD` and with the reduced `ShearAlfven` model: radial power against the shear-Alfvén continua, the power spectrum against ω_TAE, the eigenfunction of each harmonic, mode amplitudes, a matrix-pencil fit and the poloidal-plane animation, all drawn by plasma-plots. Defaults to 8 × 48 × 4 cells and t=150 (about two TAE periods); the benchmark resolution is noted at the top of each script. +- `itpa-tae-linear-mhd.py`, `itpa-tae-shear-alfven.py` — the ITPA toroidal Alfvén eigenmode benchmark (m=10,11, n=−6 in a sixth of a tokamak), with `LinearMHD` and with the reduced `ShearAlfven` model: radial power against the shear-Alfvén continua, the power spectrum against ω_TAE, the eigenfunction of each harmonic, mode amplitudes, a matrix-pencil fit and the poloidal-plane animation, all drawn by plasma-plots. They run at the benchmark resolution (24 × 96 × 16 cells, t=500), which takes hours on a cluster, so CI only post-processes their archived runs: see [Precomputed examples](#precomputed-examples). - `zeldovich-caustic.py` — pressureless SPH collapse to a caustic and multi-stream flow, with the exact density from the Lagrangian map (animated density and phase space) - `diffusion-methods.py` — the random-walk and the deterministic particle methods for the diffusion equation, compared with the exact decay (two simulations in one script) - `incompressible-shear-relaxation.py` — incompressible SPH between no-slip walls: the pressure projection removes a compressive wave, and the shear mode decays at the exact viscous rate @@ -141,6 +141,25 @@ every script from scratch before each site build. A fresh clone therefore has no the pages to build (Struphy needed, compiled kernels not), and `python cli.py run ` also makes that example's figures. +### Precomputed examples + +Examples listed in `.github/precomputed-examples.json` are too expensive for a CI runner. `run_example.py` +(and so `python cli.py run` and CI) downloads their archived run from the listed `url`, checks its `sha256`, +unpacks it into `docs/public/examples/struphy_gallery_runs/` and only post-processes it. To make or renew an +archive, submit its simulation as a Slurm job from the repository root on a cluster, in the virtual environment +with Struphy. The job is generated with slurm-script-generator, as the Pitagora CI jobs are (both use +`scripts/slurm_jobs.py`), with the ranks and time limit of the registry entry's `job`: + +```sh +python scripts/submit_precomputed_run.py itpa-tae-linear-mhd --account --partition +``` + +It simulates with `run_example.py --simulate` and packs the run with `scripts/pack_precomputed_run.py`, which +prints the `gh release upload` command and the registry entry to paste at the end of the job's log (`--dry-run` +only writes the batch script, `--no-wait` only submits it, `--module` replaces Pitagora's module stack). A changed entry +changes the example's CI cache key, so the figures are rebuilt from the new archive. The simulation must +use the script's current parameters: `--pproc-only` rebuilds the `Simulation` from the script. + ### Running on several MPI ranks GitHub-hosted runners execute each example with `run_example.py` in a single process diff --git a/docs/src/examples/itpa-tae-linear-mhd.py b/docs/src/examples/itpa-tae-linear-mhd.py index d5b3886..0068f07 100644 --- a/docs/src/examples/itpa-tae-linear-mhd.py +++ b/docs/src/examples/itpa-tae-linear-mhd.py @@ -1,16 +1,18 @@ """The ITPA toroidal Alfvén eigenmode (TAE) benchmark in ideal LinearMHD. The setup of Struphy's `examples/LinearMHD/itpa_tae_benchmark`: an m = 10, 11 perturbation of -the velocity in one sixth of a circular tokamak, whose coupled harmonics ring at the frequency -of the toroidal Alfvén gap. The frequency is read off the power spectrum, located between the -shear-Alfvén continua of the two harmonics, and its radial eigenfunction reconstructed. +the velocity in one sixth of a circular tokamak, meant to excite the toroidal Alfvén eigenmode in the gap +between the shear-Alfvén continua of the two harmonics. The measured frequencies are drawn against those +continua and the gap-centre estimate, with the radial eigenfunction of each harmonic. Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). -The default is a short, coarse run (t = 150, about two TAE periods, on 8 x 48 x 4 cells). The -benchmark itself uses NUM_ELEMENTS = (24, 96, 16), DEGREE = (3, 3, 3), END_TIME = 500. +The parameters are those of the benchmark: 24 x 96 x 16 cells of degree 3 to t = 500, hours on a +cluster (`python scripts/submit_precomputed_run.py itpa-tae-linear-mhd --account ... --partition ...` from the +repository root submits it as a Slurm job, profiles it and packs its output). The website does not rerun it: its CI downloads the archived run listed in +`.github/precomputed-examples.json` and only post-processes it. """ import argparse @@ -28,16 +30,15 @@ grids, perturbations, ) -from struphy.linear_algebra.solver import SolverParameters from struphy.models import LinearMHD from plasma_plots import save_figure from plasma_plots.theory.waves import alfven_continuum, tae_frequency STEM = "itpa-tae-linear-mhd" -NUM_ELEMENTS = (8, 48, 4) -DEGREE = (3, 3, 2) -END_TIME = 150.0 -DT = 0.2 +NUM_ELEMENTS = (24, 96, 16) +DEGREE = (3, 3, 3) +END_TIME = 500.0 +DT = 0.5 SAVE_STEP = 2 # The torus is one sixth of a full one: the sector mode n = -1 is the full-torus mode n = -6. @@ -54,11 +55,10 @@ def minor_radius(eta1): def create_simulation() -> Simulation: model = LinearMHD(base_units=BaseUnits()) - # A relative tolerance of 1e-6 halves the cost of the implicit steps, with no visible change. - solver_params = SolverParameters(tol=1e-6) - model.propagators.shear_alf.options = model.propagators.shear_alf.Options(solver_params=solver_params) - model.propagators.mag_sonic.options = model.propagators.mag_sonic.Options(solver_params=solver_params) - for field in (model.em_fields.b_field, model.mhd.density, model.mhd.velocity, model.mhd.pressure): + model.propagators.shear_alf.options = model.propagators.shear_alf.Options() + model.propagators.mag_sonic.options = model.propagators.mag_sonic.Options() + # Only the velocity and the field are saved, to keep the archived run small; the energies are scalars. + for field in (model.em_fields.b_field, model.mhd.velocity): field.save_data = True domain = domains.HollowTorus(a1=0.1, a2=1.0, R0=10.0, sfl=False, pol_period=1, tor_period=TOR_PERIOD) @@ -90,17 +90,18 @@ def create_simulation() -> Simulation: description=( "The ideal-MHD part of the ITPA toroidal Alfvén eigenmode benchmark: a global Alfvén " "wave in the gap that toroidicity opens between the shear-Alfvén continua of two " - "neighbouring poloidal harmonics. " + "neighbouring poloidal harmonics, set up here for exploration. " r"A hollow torus :math:`0.1\le r\le 1`, :math:`R_0=10`, one sixth of the full torus, " r"carries the AdhocTorus equilibrium with :math:`B_0=3`, " r":math:`q(r)=1.71+0.16\,r^2` and :math:`\beta=0.0018`. " r"The continua of :math:`m=10` and :math:`m=11` at :math:`n=-6` cross where " r":math:`q=10.5/6=1.75`, at :math:`r=0.5`, near " r"$$\omega_\mathrm{TAE}=\frac{v_A}{2qR_0}\approx 0.096,\qquad v_A=\frac{B_0}{\sqrt{n(r)}}.$$ " - "The velocity is seeded with these two harmonics, Gaussian in radius around r = 0.55. " + "The velocity is seeded with these two harmonics, Gaussian in radius around r = 0.55, " + "on 24 × 96 × 16 cells of degree 3, to t = 500 (about eight TAE periods). " "LinearMHD evolves the shear-Alfvén and the magnetosonic parts of the linearized MHD " - "equations. The default is a short, coarse run of about two TAE periods; " - "the benchmark resolution is in the script." + "equations. The figures post-process an archived run of this script; " + "it takes hours on a cluster." ), params_path=__file__, env=env, time_opts=Time(dt=DT, Tend=END_TIME), domain=domain, equil=equil, grid=grid, derham_opts=derham_opts, @@ -115,7 +116,7 @@ def pproc(sim: Simulation, show: bool = False): R0 = sim.domain.params["R0"] # The physical velocity, rotated to the radial, poloidal and toroidal directions of the torus. - output.pproc(physical=True, celldivide=(2, 2, 2), create_vtk=False) + output.pproc(physical=True, celldivide=(1, 1, 1), create_vtk=False) velocity = output.evaluate("mhd/velocity_xyz").plasma.analysis.toroidal_components(R0=R0) times = velocity.t.values if not np.isclose(times[-1], END_TIME): @@ -127,20 +128,25 @@ def pproc(sim: Simulation, show: bool = False): # Theory: the shear-Alfvén continua of the seeded harmonics, and the gap frequency. n_full = SECTOR_MODE * TOR_PERIOD - v_A = lambda r: equil.params["B0"] / np.sqrt(equil.n_r(r)) + + def v_A(r): + return equil.params["B0"] / np.sqrt(equil.n_r(r)) + + def continuum(r, m, n): + omega = alfven_continuum(r, m, n, equil.q_r, major_radius=R0, alfven_speed=v_A) + return {"shear Alfvén": omega.real} + q_gap = (MODES[0] + 0.5) / abs(n_full) r_gap = float(np.sqrt((q_gap - equil.params["q0"]) / (equil.params["q1"] - equil.params["q0"]))) omega_tae = float(tae_frequency(q_gap, major_radius=R0, alfven_speed=v_A(r_gap))) - continuum = lambda r, m, n: {"shear Alfvén": alfven_continuum(r, m, n, equil.q_r, major_radius=R0, alfven_speed=v_A).real} print(f"TAE gap: q = {q_gap:.3f} at r = {r_gap:.3f}, omega_TAE = {omega_tae:.4f}") # The measured frequency: the strongest peak of the radial velocity, refined between bins. peaks = u_r.plasma.analysis.spectral_peaks(n_peaks=2, window="hann") omega = float(peaks.omega_refined[0]) - probe = u_r.sel(eta1=0.45, eta2=0.0, eta3=0.0, method="nearest") - pencil = probe.plasma.analysis.matrix_pencil(n_modes=1) - print(f"Measured frequency: {omega:.4f} (FFT peak), {float(pencil.omega[0]):.4f} (matrix pencil), " - f"damping {float(pencil.gamma[0]):.2e}; omega_TAE = {omega_tae:.4f}") + measured = ", ".join(f"{w:.4f}" for w in peaks.omega_refined.values) + print(f"Strongest frequencies of u_r: {measured} (resolution {2 * np.pi / (times[-1] - times[0]):.4f}); " + f"omega_TAE = {omega_tae:.4f}") # Main figure: where each frequency lives in radius, against the continua. radial = u_r.plasma.plot.radial_power( @@ -161,9 +167,6 @@ def pproc(sim: Simulation, show: bool = False): amplitudes = u_r.plasma.plot.mode_amplitudes(top=4, backend="plotly") save_figure(amplitudes, f"{STEM}-mode-amplitudes", show=show) - fit = probe.plasma.plot.pencil_fit(n_modes=1, backend="plotly") - save_figure(fit, f"{STEM}-pencil", show=show) - movie = u_r.plasma.plot.animation( coords="physical", plane="RZ", eta3=0, symmetric=True, cmap="RdBu_r", max_frames=40, title="u_r on the poloidal plane φ = 0", backend="plotly", diff --git a/docs/src/examples/itpa-tae-shear-alfven.py b/docs/src/examples/itpa-tae-shear-alfven.py index 2f23c05..2f6a3d2 100644 --- a/docs/src/examples/itpa-tae-shear-alfven.py +++ b/docs/src/examples/itpa-tae-shear-alfven.py @@ -1,18 +1,20 @@ """The ITPA toroidal Alfvén eigenmode (TAE) benchmark with the reduced ShearAlfven model. The setup of Struphy's `examples/ShearAlfven/itpa_tae_benchmark`: an m = 10, 11 perturbation of -the velocity in one sixth of a circular tokamak, whose coupled harmonics ring at the frequency -of the toroidal Alfvén gap. ShearAlfven keeps only the shear-Alfvén part of LinearMHD (no +the velocity in one sixth of a circular tokamak, meant to excite the toroidal Alfvén eigenmode in the gap +between the shear-Alfvén continua of the two harmonics. ShearAlfven keeps only the shear-Alfvén part of LinearMHD (no magnetosonic propagator and no density or pressure), so this is the same benchmark without -compressional coupling; compare with itpa-tae-linear-mhd.py. The frequency is read off the power spectrum, located between the -shear-Alfvén continua of the two harmonics, and its radial eigenfunction reconstructed. +compressional coupling; compare with itpa-tae-linear-mhd.py. The measured frequencies are drawn against those +continua and the gap-centre estimate, with the radial eigenfunction of each harmonic. Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). -The default is a short, coarse run (t = 150, about two TAE periods, on 8 x 48 x 4 cells). The -benchmark itself uses NUM_ELEMENTS = (24, 96, 16), DEGREE = (3, 3, 3), END_TIME = 500. +The parameters are those of the benchmark: 24 x 96 x 16 cells of degree 3 to t = 500, hours on a +cluster (`python scripts/submit_precomputed_run.py itpa-tae-shear-alfven --account ... --partition ...` from the +repository root submits it as a Slurm job, profiles it and packs its output). The website does not rerun it: its CI downloads the archived run listed in +`.github/precomputed-examples.json` and only post-processes it. """ import argparse @@ -30,17 +32,16 @@ grids, perturbations, ) -from struphy.linear_algebra.solver import SolverParameters from struphy.models import ShearAlfven from plasma_plots import save_figure from plasma_plots.theory.waves import alfven_continuum, tae_frequency STEM = "itpa-tae-shear-alfven" -NUM_ELEMENTS = (8, 48, 4) -DEGREE = (3, 3, 2) -END_TIME = 150.0 -DT = 0.2 -SAVE_STEP = 2 +NUM_ELEMENTS = (24, 96, 16) +DEGREE = (3, 3, 3) +END_TIME = 500.0 +DT = 0.1 +SAVE_STEP = 10 # The torus is one sixth of a full one: the sector mode n = -1 is the full-torus mode n = -6. TOR_PERIOD = 6 @@ -56,9 +57,7 @@ def minor_radius(eta1): def create_simulation() -> Simulation: model = ShearAlfven(base_units=BaseUnits()) - # A relative tolerance of 1e-6 halves the cost of the implicit steps, with no visible change. - solver_params = SolverParameters(tol=1e-6) - model.propagators.shear_alf.options = model.propagators.shear_alf.Options(solver_params=solver_params) + model.propagators.shear_alf.options = model.propagators.shear_alf.Options() for field in (model.em_fields.b_field, model.mhd.velocity): field.save_data = True @@ -91,18 +90,19 @@ def create_simulation() -> Simulation: description=( "The ideal-MHD part of the ITPA toroidal Alfvén eigenmode benchmark: a global Alfvén " "wave in the gap that toroidicity opens between the shear-Alfvén continua of two " - "neighbouring poloidal harmonics. " + "neighbouring poloidal harmonics, set up here for exploration. " r"A hollow torus :math:`0.1\le r\le 1`, :math:`R_0=10`, one sixth of the full torus, " r"carries the AdhocTorus equilibrium with :math:`B_0=3`, " r":math:`q(r)=1.71+0.16\,r^2` and :math:`\beta=0.0018`. " r"The continua of :math:`m=10` and :math:`m=11` at :math:`n=-6` cross where " r":math:`q=10.5/6=1.75`, at :math:`r=0.5`, near " r"$$\omega_\mathrm{TAE}=\frac{v_A}{2qR_0}\approx 0.096,\qquad v_A=\frac{B_0}{\sqrt{n(r)}}.$$ " - "The velocity is seeded with these two harmonics, Gaussian in radius around r = 0.55. " + "The velocity is seeded with these two harmonics, Gaussian in radius around r = 0.55, " + "on 24 × 96 × 16 cells of degree 3, to t = 500 (about eight TAE periods). " "ShearAlfven evolves only the shear-Alfvén part of the linearized MHD equations: " "velocity and magnetic field, with no magnetosonic coupling, density or pressure " - "perturbation. The default is a short, coarse run of about two TAE periods; " - "the benchmark resolution is in the script." + "perturbation. The figures post-process an archived run of this script; " + "it takes hours on a cluster." ), params_path=__file__, env=env, time_opts=Time(dt=DT, Tend=END_TIME), domain=domain, equil=equil, grid=grid, derham_opts=derham_opts, @@ -117,7 +117,7 @@ def pproc(sim: Simulation, show: bool = False): R0 = sim.domain.params["R0"] # The physical velocity, rotated to the radial, poloidal and toroidal directions of the torus. - output.pproc(physical=True, celldivide=(2, 2, 2), create_vtk=False) + output.pproc(physical=True, celldivide=(1, 1, 1), create_vtk=False) velocity = output.evaluate("mhd/velocity_xyz").plasma.analysis.toroidal_components(R0=R0) times = velocity.t.values if not np.isclose(times[-1], END_TIME): @@ -129,20 +129,25 @@ def pproc(sim: Simulation, show: bool = False): # Theory: the shear-Alfvén continua of the seeded harmonics, and the gap frequency. n_full = SECTOR_MODE * TOR_PERIOD - v_A = lambda r: equil.params["B0"] / np.sqrt(equil.n_r(r)) + + def v_A(r): + return equil.params["B0"] / np.sqrt(equil.n_r(r)) + + def continuum(r, m, n): + omega = alfven_continuum(r, m, n, equil.q_r, major_radius=R0, alfven_speed=v_A) + return {"shear Alfvén": omega.real} + q_gap = (MODES[0] + 0.5) / abs(n_full) r_gap = float(np.sqrt((q_gap - equil.params["q0"]) / (equil.params["q1"] - equil.params["q0"]))) omega_tae = float(tae_frequency(q_gap, major_radius=R0, alfven_speed=v_A(r_gap))) - continuum = lambda r, m, n: {"shear Alfvén": alfven_continuum(r, m, n, equil.q_r, major_radius=R0, alfven_speed=v_A).real} print(f"TAE gap: q = {q_gap:.3f} at r = {r_gap:.3f}, omega_TAE = {omega_tae:.4f}") # The measured frequency: the strongest peak of the radial velocity, refined between bins. peaks = u_r.plasma.analysis.spectral_peaks(n_peaks=2, window="hann") omega = float(peaks.omega_refined[0]) - probe = u_r.sel(eta1=0.45, eta2=0.0, eta3=0.0, method="nearest") - pencil = probe.plasma.analysis.matrix_pencil(n_modes=1) - print(f"Measured frequency: {omega:.4f} (FFT peak), {float(pencil.omega[0]):.4f} (matrix pencil), " - f"damping {float(pencil.gamma[0]):.2e}; omega_TAE = {omega_tae:.4f}") + measured = ", ".join(f"{w:.4f}" for w in peaks.omega_refined.values) + print(f"Strongest frequencies of u_r: {measured} (resolution {2 * np.pi / (times[-1] - times[0]):.4f}); " + f"omega_TAE = {omega_tae:.4f}") # Main figure: where each frequency lives in radius, against the continua. radial = u_r.plasma.plot.radial_power( @@ -163,9 +168,6 @@ def pproc(sim: Simulation, show: bool = False): amplitudes = u_r.plasma.plot.mode_amplitudes(top=4, backend="plotly") save_figure(amplitudes, f"{STEM}-mode-amplitudes", show=show) - fit = probe.plasma.plot.pencil_fit(n_modes=1, backend="plotly") - save_figure(fit, f"{STEM}-pencil", show=show) - movie = u_r.plasma.plot.animation( coords="physical", plane="RZ", eta3=0, symmetric=True, cmap="RdBu_r", max_frames=40, title="u_r on the poloidal plane φ = 0", backend="plotly", diff --git a/run_example.py b/run_example.py index d882b72..6f7ab69 100644 --- a/run_example.py +++ b/run_example.py @@ -13,25 +13,85 @@ Scripts that still publish by themselves (they import `plasma_plots.gallery`) are run as they are. +Examples listed in `.github/precomputed-examples.json` are too expensive to simulate here: their +archived run is downloaded (into `.cache/precomputed-examples/`), checked against its sha256 and +unpacked into `docs/public/examples/struphy_gallery_runs/`, and the script only post-processes it. +`--simulate` runs such an example's simulation anyway, e.g. on a cluster to make the archive +(`scripts/pack_precomputed_run.py` packs it). + Usage (under MPI, prefix `mpirun -n 4`; `python cli.py run ` calls this): python run_example.py maxwell-wave # simulate, post-process, publish python run_example.py maxwell-wave --pproc-only # post-process an earlier run again + mpirun -n 32 python run_example.py itpa-tae-linear-mhd --simulate # scripts/submit_precomputed_run.py's job """ from __future__ import annotations import argparse +import hashlib +import json import os import runpy import shutil import sys +import tarfile +import urllib.request from pathlib import Path ROOT = Path(__file__).resolve().parent EXAMPLES_DIR = ROOT / "docs" / "src" / "examples" OUTPUT_DIR = ROOT / "docs" / "public" / "examples" IMAGES_DIR = ROOT / "docs" / "public" / "images" / "examples" +PRECOMPUTED = ROOT / ".github" / "precomputed-examples.json" +REGISTRY = PRECOMPUTED.relative_to(ROOT) # as named in messages +DOWNLOADS = ROOT / ".cache" / "precomputed-examples" +RUNS_DIR = OUTPUT_DIR / "struphy_gallery_runs" # the scripts' EnvironmentOptions(out_folders=...) + + +def precomputed_run(stem: str) -> dict | None: + """The archived run of this example from `.github/precomputed-examples.json`, if it has one.""" + if not PRECOMPUTED.is_file(): + return None + return json.loads(PRECOMPUTED.read_text()).get(stem) + + +def sha256(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as file: + for block in iter(lambda: file.read(1 << 20), b""): + digest.update(block) + return digest.hexdigest() + + +def fetch_precomputed_run(stem: str, entry: dict) -> None: + """Download, check and unpack the archived run into RUNS_DIR, unless it is there already.""" + run = RUNS_DIR / entry["sim_folder"] + if (run / "data").is_dir(): + print(f"{stem}: using the existing run {os.path.relpath(run, ROOT)}") + return + if not entry.get("url") or not entry.get("sha256"): + raise SystemExit( + f"{stem}: no archived run published yet; set its url and sha256 in " + f"{REGISTRY}, or simulate it with --simulate" + ) + DOWNLOADS.mkdir(parents=True, exist_ok=True) + archive = DOWNLOADS / f"{stem}-{entry['sha256'][:12]}.tar.gz" + if not archive.is_file() or sha256(archive) != entry["sha256"]: + print(f"{stem}: downloading {entry['url']}") + partial = archive.with_suffix(".part") + urllib.request.urlretrieve(entry["url"], partial) + if sha256(partial) != entry["sha256"]: + partial.unlink() + raise SystemExit(f"{stem}: the download does not match the sha256 in {REGISTRY}") + partial.replace(archive) + RUNS_DIR.mkdir(parents=True, exist_ok=True) + with tarfile.open(archive) as tar: + tops = {Path(member.name).parts[0] for member in tar.getmembers()} + if tops != {entry["sim_folder"]}: + raise SystemExit(f"{stem}: the archive must hold only {entry['sim_folder']}/, not {sorted(tops)}") + tar.extractall(RUNS_DIR, filter="data") + print(f"{stem}: unpacked {archive.name} into {os.path.relpath(run, ROOT)}") def publishes_by_itself(script: Path) -> bool: @@ -66,6 +126,9 @@ def main(argv: list[str] | None = None) -> None: parser = argparse.ArgumentParser(description="Run one gallery example and publish its results.") parser.add_argument("example", help="the script stem, e.g. maxwell-wave") parser.add_argument("--pproc-only", action="store_true", help="post-process an earlier run instead of simulating") + parser.add_argument( + "--simulate", action="store_true", help="simulate even an example whose archived run is downloaded otherwise" + ) args = parser.parse_args(argv) stem = args.example @@ -75,7 +138,15 @@ def main(argv: list[str] | None = None) -> None: OUTPUT_DIR.mkdir(parents=True, exist_ok=True) os.chdir(OUTPUT_DIR) # the scripts save their figures and scratch runs in the current directory - sys.argv = [str(script), *(["--pproc-only"] if args.pproc_only else [])] + pproc_only = args.pproc_only + entry = precomputed_run(stem) + if entry is not None and not args.simulate: + if any(int(os.environ.get(name, "1")) > 1 for name in ("OMPI_COMM_WORLD_SIZE", "PMI_SIZE")): + raise SystemExit(f"{stem}: post-process its archived run on one rank (or pass --simulate)") + fetch_precomputed_run(stem, entry) + pproc_only = True + + sys.argv = [str(script), *(["--pproc-only"] if pproc_only else [])] if publishes_by_itself(script): runpy.run_path(str(script), run_name="__main__") return diff --git a/scripts/pack_precomputed_run.py b/scripts/pack_precomputed_run.py new file mode 100644 index 0000000..400a0b5 --- /dev/null +++ b/scripts/pack_precomputed_run.py @@ -0,0 +1,70 @@ +"""Pack the run of a precomputed gallery example for upload, and print its registry entry. + +After simulating it (from the repository root, on a cluster): + + mpirun -n 32 python run_example.py itpa-tae-linear-mhd --simulate + python scripts/pack_precomputed_run.py itpa-tae-linear-mhd + +This writes `-run.tar.gz` of `docs/public/examples/struphy_gallery_runs//` +(without its `post_processing/`, which the website regenerates), and prints its sha256 and the +commands to upload it to a release and fill in `.github/precomputed-examples.json`. It uploads +nothing itself. Standard library only. +""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import tarfile +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +PRECOMPUTED = ROOT / ".github" / "precomputed-examples.json" +RUNS_DIR = ROOT / "docs" / "public" / "examples" / "struphy_gallery_runs" +REPOSITORY = "struphy-hub/struphy-hub.github.io" + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) + parser.add_argument("example", help="the script stem, e.g. itpa-tae-linear-mhd") + parser.add_argument("--tag", default="precomputed-examples-v1", help="the release to upload to") + parser.add_argument("--output", type=Path, default=Path.cwd(), help="where to write the archive") + args = parser.parse_args() + + entry = json.loads(PRECOMPUTED.read_text()).get(args.example) + if entry is None: + raise SystemExit(f"{args.example} is not listed in {PRECOMPUTED.relative_to(ROOT)}") + run = RUNS_DIR / entry["sim_folder"] + if not (run / "data").is_dir(): + raise SystemExit(f"no simulation output in {run}; run it first with run_example.py --simulate") + + archive = args.output / f"{args.example}-run.tar.gz" + + def keep(info: tarfile.TarInfo) -> tarfile.TarInfo | None: + parts = Path(info.name).parts + return None if len(parts) > 1 and parts[1] == "post_processing" else info + + with tarfile.open(archive, "w:gz") as tar: + tar.add(run, arcname=entry["sim_folder"], filter=keep) + + digest = hashlib.sha256() + with archive.open("rb") as file: + for block in iter(lambda: file.read(1 << 20), b""): + digest.update(block) + size = archive.stat().st_size + url = f"https://github.com/{REPOSITORY}/releases/download/{args.tag}/{archive.name}" + print(f"Wrote {archive} ({size / 1e6:.0f} MB)") + if size > 2e9: + print("warning: GitHub release assets are limited to 2 GB; save fewer fields or snapshots") + print("\nUpload it (creates the release the first time):") + print(f" gh release view {args.tag} -R {REPOSITORY} || gh release create {args.tag} -R {REPOSITORY} " + f"--title 'Precomputed example runs' --notes 'Archived simulation output of gallery examples too expensive for CI.'") + print(f" gh release upload {args.tag} {archive} -R {REPOSITORY} --clobber") + print(f"\nThen set in {PRECOMPUTED.relative_to(ROOT)}:") + print(json.dumps({args.example: {**entry, "url": url, "sha256": digest.hexdigest()}}, indent=2)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/slurm_jobs.py b/scripts/slurm_jobs.py new file mode 100644 index 0000000..d4bb907 --- /dev/null +++ b/scripts/slurm_jobs.py @@ -0,0 +1,93 @@ +"""Slurm jobs of gallery examples, generated and watched with slurm-script-generator. + +Shared by `.github/scripts/run-pitagora-example.py` (CI on the self-hosted Pitagora runner) and +`scripts/submit_precomputed_run.py` (the long runs whose archives CI downloads). +""" + +from __future__ import annotations + +import shlex +from pathlib import Path + +from slurm_script_generator.slurm_script import SlurmScript +from slurm_script_generator.squeue import SQueue + +# The module stack of Pitagora's GitHub runner. +MODULES = [ + "gcc/12.3.0", + "python/3.11.7", + "hdf5/1.14.3--gcc--12.3.0", + "cmake/3.27.9", + "netcdf-fortran/4.6.1--gcc--12.3.0", + "netlib-scalapack/2.2.0--openmpi--4.1.6--gcc--12.3.0-ucx1.20", +] + + +def environment_commands(virtual_env: Path, mpi_ranks: int) -> list[str]: + """Strict bash, the virtual environment, and Struphy's MPI switch for this many ranks.""" + commands = [ + "set -euo pipefail", + f"source {shlex.quote(str(virtual_env / 'bin' / 'activate'))}", + ] + if mpi_ranks == 1: + # A one-task Slurm allocation is not an MPI launch. + commands.append("export STRUPHY_MPI=0") + else: + # mpi4py must see the Open MPI environment of the launch. + commands.append("unset STRUPHY_MPI") + return commands + + +def example_job( + *, + job_name: str, + account: str, + partition: str, + workspace: Path, + log_dir: Path, + commands: list[str], + mpi_ranks: int = 1, + time: str = "00:30:00", + mem: str | None = None, + modules: list[str] | None = None, +) -> SlurmScript: + """One single-node job in ``workspace``, logging to ``log_dir/slurm--.out/.err``.""" + return SlurmScript( + job_name=job_name, + account=account, + partition=partition, + nodes=1, + ntasks=mpi_ranks, + cpus_per_task=1, + time=time, + mem=mem, + chdir=str(workspace), + output=str(log_dir / f"slurm-{job_name}-%j.out"), + error=str(log_dir / f"slurm-{job_name}-%j.err"), + modules=MODULES if modules is None else modules, + custom_commands=commands, + ) + + +def tail_log(path: Path, lines: int = 200) -> None: + """Print a bounded batch log in a collapsible GitHub Actions group.""" + if not path.is_file(): + return + print(f"::group::{path.name}") + print("\n".join(path.read_text(errors="replace").splitlines()[-lines:])) + print("::endgroup::") + + +def submit_and_wait( + script: SlurmScript, job_name: str, script_path: Path, log_dir: Path, poll_interval: float = 15 +) -> str | None: + """Submit the job, wait for it (raising unless it completed) and print its logs; return its state.""" + job_id = script.submit_job(path=str(script_path), verbose=True) + print(f"Submitted {job_name} as Slurm job {job_id}") + try: + state = SQueue().wait_until_done(job_id=job_id, poll_interval=poll_interval, check=True)[job_id] + print(f"Slurm job {job_id} finished with state {state or 'unknown'}.") + finally: + tail_log(log_dir / f"slurm-{job_name}-{job_id}.out") + tail_log(log_dir / f"slurm-{job_name}-{job_id}.err") + return state diff --git a/scripts/submit_precomputed_run.py b/scripts/submit_precomputed_run.py new file mode 100644 index 0000000..2c5a92b --- /dev/null +++ b/scripts/submit_precomputed_run.py @@ -0,0 +1,95 @@ +"""Simulate a precomputed gallery example as a Slurm job, and pack its run for upload. + +The website's CI only post-processes these examples (see `.github/precomputed-examples.json`). This +makes their archived run on a cluster, with the same slurm-script-generator jobs as the Pitagora +CI (`scripts/slurm_jobs.py`): from the repository root, in the virtual environment with Struphy, + + python scripts/submit_precomputed_run.py itpa-tae-linear-mhd --account --partition + +The job generates the example's metadata, runs `run_example.py --simulate` on the registry +entry's `job.mpi_ranks` ranks (which also profiles it) and packs the run with +`scripts/pack_precomputed_run.py`, whose printed upload commands and registry entry end the job's +log. This waits for the job and prints its logs; `--no-wait` only submits it, and `--dry-run` only +writes the batch script. It uploads nothing. +""" + +from __future__ import annotations + +import argparse +import json +import os +import shlex +import sys +import time +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +PRECOMPUTED = ROOT / ".github" / "precomputed-examples.json" +sys.path.insert(0, str(Path(__file__).resolve().parent)) +from slurm_jobs import MODULES, environment_commands, example_job, submit_and_wait # noqa: E402 + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) + parser.add_argument("example", help="the script stem, e.g. itpa-tae-linear-mhd") + parser.add_argument("--account", required=True) + parser.add_argument("--partition", required=True) + parser.add_argument("--time", help="override the registry's job.time, e.g. 12:00:00") + parser.add_argument("--mpi-ranks", type=int, help="override the registry's job.mpi_ranks") + parser.add_argument( + "--module", action="append", dest="modules", + help=f"a module to load, instead of Pitagora's (repeat it); default: {' '.join(MODULES)}", + ) + parser.add_argument( + "--venv", type=Path, default=os.environ.get("VIRTUAL_ENV"), + help="the virtual environment with Struphy (default: the active one)", + ) + parser.add_argument("--no-wait", action="store_true", help="submit the job and return") + parser.add_argument("--dry-run", action="store_true", help="only write the batch script") + args = parser.parse_args() + + entry = json.loads(PRECOMPUTED.read_text()).get(args.example) + if entry is None: + raise SystemExit(f"{args.example} is not listed in {PRECOMPUTED.relative_to(ROOT)}") + if args.venv is None: + raise SystemExit("activate the virtual environment with Struphy, or pass --venv") + job = entry.get("job", {}) + mpi_ranks = args.mpi_ranks or job.get("mpi_ranks", 1) + stem = shlex.quote(args.example) + + commands = environment_commands(Path(args.venv).resolve(), mpi_ranks) + commands += [ + "export OMP_NUM_THREADS=1 PYTHONUNBUFFERED=1", + f"python generate_examples.py {stem}", + # mpirun takes the ranks and hosts from the Slurm allocation. + f"{f'mpirun -n {mpi_ranks} ' if mpi_ranks > 1 else ''}python run_example.py {stem} --simulate", + f"python scripts/pack_precomputed_run.py {stem} --output {shlex.quote(str(ROOT))}", + ] + job_name = f"struphy-precomputed-{args.example}-{time.strftime('%Y%m%d-%H%M%S')}" + script = example_job( + job_name=job_name, + account=args.account, + partition=args.partition, + workspace=ROOT, + log_dir=ROOT, + commands=commands, + mpi_ranks=mpi_ranks, + time=args.time or job.get("time", "01:00:00"), + mem=job.get("mem"), + modules=args.modules, + ) + script_path = ROOT / f"{job_name}.sbatch" + if args.dry_run: + script.save(script_path) + print(f"Wrote {script_path.relative_to(ROOT)}; submit it with sbatch.") + return 0 + if args.no_wait: + job_id = script.submit_job(path=str(script_path), verbose=True) + print(f"Submitted {job_name} as Slurm job {job_id}; its log is slurm-{job_name}-{job_id}.out") + return 0 + submit_and_wait(script, job_name, script_path, ROOT, poll_interval=60) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From 69c2f0f982cf7a40d526fd6cb2423c2e632fcf1a Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 30 Sep 2026 11:06:00 +0200 Subject: [PATCH 03/16] updated struphy submodule --- submodules/plasma-plots | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/plasma-plots b/submodules/plasma-plots index 43dbd03..b25a241 160000 --- a/submodules/plasma-plots +++ b/submodules/plasma-plots @@ -1 +1 @@ -Subproject commit 43dbd032cf53be6649e87eda5a66478f197e045c +Subproject commit b25a2413ce7119f46ed0299aab75945d1ddbe3fa From 78748c809c8057c74b6728c7efcea503a05d402a Mon Sep 17 00:00:00 2001 From: Max Lindqvist Date: Wed, 30 Sep 2026 14:22:43 +0200 Subject: [PATCH 04/16] Updated struphy to latest struphy-plots-integration --- submodules/struphy | 1 + 1 file changed, 1 insertion(+) create mode 160000 submodules/struphy diff --git a/submodules/struphy b/submodules/struphy new file mode 160000 index 0000000..139b8b9 --- /dev/null +++ b/submodules/struphy @@ -0,0 +1 @@ +Subproject commit 139b8b9c013cc7913314f68f0e1868ef081ca84c From 46bef93ffa2f425ddc70c38a5c47cc9c8fd1242c Mon Sep 17 00:00:00 2001 From: Max Lindqvist Date: Wed, 30 Sep 2026 14:25:00 +0200 Subject: [PATCH 05/16] removed submodules/struphy --- submodules/struphy | 1 - 1 file changed, 1 deletion(-) delete mode 160000 submodules/struphy diff --git a/submodules/struphy b/submodules/struphy deleted file mode 160000 index 139b8b9..0000000 --- a/submodules/struphy +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 139b8b9c013cc7913314f68f0e1868ef081ca84c From 942f33db930dec90bd0c09289d2ee437c58df2ac Mon Sep 17 00:00:00 2001 From: Max Lindqvist Date: Wed, 30 Sep 2026 14:29:28 +0200 Subject: [PATCH 06/16] Updated plasma-plots --- submodules/plasma-plots | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/plasma-plots b/submodules/plasma-plots index b25a241..65a5bcd 160000 --- a/submodules/plasma-plots +++ b/submodules/plasma-plots @@ -1 +1 @@ -Subproject commit b25a2413ce7119f46ed0299aab75945d1ddbe3fa +Subproject commit 65a5bcd41920041cec023f46c37ccf0fbdca5136 From 77c01ad961445503445e32d74feac81330734d60 Mon Sep 17 00:00:00 2001 From: Max Lindqvist Date: Thu, 1 Oct 2026 15:23:06 +0200 Subject: [PATCH 07/16] Updated submodules --- submodules/plasma-plots | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/plasma-plots b/submodules/plasma-plots index 65a5bcd..e03bb85 160000 --- a/submodules/plasma-plots +++ b/submodules/plasma-plots @@ -1 +1 @@ -Subproject commit 65a5bcd41920041cec023f46c37ccf0fbdca5136 +Subproject commit e03bb85704948c9136a3754eb15903436398a10c From 02a3f03c286da4b0923ab078c0ba0a22d1829684 Mon Sep 17 00:00:00 2001 From: Max Lindqvist Date: Fri, 2 Oct 2026 11:22:36 +0200 Subject: [PATCH 08/16] update plasma plots --- submodules/plasma-plots | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/plasma-plots b/submodules/plasma-plots index e03bb85..a2afc66 160000 --- a/submodules/plasma-plots +++ b/submodules/plasma-plots @@ -1 +1 @@ -Subproject commit e03bb85704948c9136a3754eb15903436398a10c +Subproject commit a2afc6695e402ba7ca2ece9585bfb1f514d6f965 From f5b65b0884fef9ba0d13637a8eb0e79fdaac1679 Mon Sep 17 00:00:00 2001 From: Max Lindqvist Date: Fri, 2 Oct 2026 11:24:06 +0200 Subject: [PATCH 09/16] remove the long examples --- .github/scripts/list-examples.cjs | 4 +++- .github/scripts/list-pitagora-examples.cjs | 5 +++++ docs/scripts/generate-examples-index.mjs | 5 +++++ scripts/check_examples.py | 7 +++++-- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/.github/scripts/list-examples.cjs b/.github/scripts/list-examples.cjs index fc1eb9d..e10d4bd 100644 --- a/.github/scripts/list-examples.cjs +++ b/.github/scripts/list-examples.cjs @@ -1,8 +1,10 @@ const fs = require('node:fs/promises'); const { exampleCache } = require('./example-cache.cjs'); +const CI_EXCLUDED = ['itpa-tae-linear-mhd', 'itpa-tae-shear-alfven']; + module.exports = async function listExamples({ cache, glob, core, exclude = ['poisson-source'] }) { - const excluded = new Set(exclude); + const excluded = new Set([...CI_EXCLUDED, ...exclude]); const examples = (await fs.readdir('docs/src/examples')) .filter(name => name.endsWith('.py') && !name.startsWith('_') && !excluded.has(name.slice(0, -3))) .map(name => name.slice(0, -3)).sort(); diff --git a/.github/scripts/list-pitagora-examples.cjs b/.github/scripts/list-pitagora-examples.cjs index af5a360..aef4295 100644 --- a/.github/scripts/list-pitagora-examples.cjs +++ b/.github/scripts/list-pitagora-examples.cjs @@ -2,6 +2,7 @@ const fs = require('node:fs/promises'); const { exampleCache } = require('./example-cache.cjs'); const selectionPath = '.github/pitagora-examples.txt'; +const CI_EXCLUDED = new Set(['itpa-tae-linear-mhd', 'itpa-tae-shear-alfven']); async function selectedPitagoraExamples() { const text = await fs.readFile(selectionPath, 'utf8'); @@ -10,6 +11,10 @@ async function selectedPitagoraExamples() { .map(line => line.split('#', 1)[0].trim()) .filter(Boolean); if (!examples.length) throw new Error(`${selectionPath} contains no examples`); + const excluded = examples.filter(example => CI_EXCLUDED.has(example)); + if (excluded.length) { + throw new Error(`${selectionPath} includes examples disabled in CI: ${excluded.join(', ')}`); + } if (new Set(examples).size !== examples.length) { throw new Error(`${selectionPath} contains duplicate examples`); } diff --git a/docs/scripts/generate-examples-index.mjs b/docs/scripts/generate-examples-index.mjs index e9ba46d..3efd857 100644 --- a/docs/scripts/generate-examples-index.mjs +++ b/docs/scripts/generate-examples-index.mjs @@ -16,6 +16,10 @@ const exampleDomainsDir = join(root, 'public', 'example-domains'); const scriptsDir = join(root, 'src', 'examples'); const outFile = join(root, 'src', 'data', 'examples-index.json'); +// Keep long-running examples available in the source and generated artifacts, +// but omit them from the site until their results are ready for regular browsing. +const hiddenSlugs = new Set(['itpa-tae-linear-mhd', 'itpa-tae-shear-alfven']); + function toSlug(value) { return value .replace(/([a-z0-9])([A-Z])/g, '$1-$2') @@ -52,6 +56,7 @@ const files = (await readdir(examplesDir)).filter((name) => name.endsWith('.meta const examples = []; for (const filename of files) { const slug = filename.replace(/\.metadata\.json$/, ''); + if (hiddenSlugs.has(slug)) continue; const data = JSON.parse(await readFile(join(examplesDir, filename), 'utf8')); const statsPath = join(examplesDir, `${slug}-region-stats.json`); const profile = (await exists(statsPath)) diff --git a/scripts/check_examples.py b/scripts/check_examples.py index 5bea6b7..f62bb43 100644 --- a/scripts/check_examples.py +++ b/scripts/check_examples.py @@ -25,6 +25,9 @@ # What generate_examples.py writes for every example, and the example pages read without a guard. REQUIRED = ("name", "description", "model", "equationsMarkdown", "domain", "steps") +# These scripts remain available in the site source, but their expensive runs are +# intentionally kept out of CI. Their metadata and download routes are still checked. +CI_EXCLUDED = {"itpa-tae-linear-mhd", "itpa-tae-shear-alfven"} def _reject_constant(name: str): @@ -76,11 +79,11 @@ def main() -> int: problems.append( f"{stem}: no download route {route.relative_to(ROOT)} (its 'Download .py' link would be a 404)" ) - if args.require_figures and not (OUTPUT_DIR / f"{stem}.plotly.json").is_file(): + if args.require_figures and stem not in CI_EXCLUDED and not (OUTPUT_DIR / f"{stem}.plotly.json").is_file(): problems.append( f"{stem}: no {stem}.plotly.json in {OUTPUT_DIR.relative_to(ROOT)}; the example did not produce its figure" ) - if args.require_figures and not (OUTPUT_DIR / f"{stem}.html").is_file(): + if args.require_figures and stem not in CI_EXCLUDED and not (OUTPUT_DIR / f"{stem}.html").is_file(): problems.append( f"{stem}: no {stem}.html in {OUTPUT_DIR.relative_to(ROOT)}; the example did not produce its standalone figure" ) From 795bfd44357923e201b2f1cc1db426571005402b Mon Sep 17 00:00:00 2001 From: Max Lindqvist Date: Fri, 2 Oct 2026 11:29:30 +0200 Subject: [PATCH 10/16] Removed plasma-plots submodule --- .github/scripts/example-cache.cjs | 5 +++-- .github/scripts/list-examples.test.cjs | 5 +++-- .github/workflows/build-site.yml | 2 +- .github/workflows/pitagora-examples.yml | 5 +++-- .github/workflows/run-example.yml | 14 +++++++------- .gitmodules | 6 +++--- README.md | 12 ++++++------ cli.py | 8 ++++---- docs/src/examples/README.md | 4 ++-- docs/src/examples/acoustic-pulse.py | 2 +- docs/src/examples/alfven-standing-wave.py | 2 +- docs/src/examples/beltrami-sph.py | 2 +- docs/src/examples/bump-on-tail.py | 2 +- docs/src/examples/coaxial-waveguide.py | 2 +- docs/src/examples/cold-plasma-oscillation.py | 2 +- docs/src/examples/cold-plasma-wave-packet.py | 2 +- docs/src/examples/cold-plasma-waves.py | 2 +- docs/src/examples/dam-break.py | 2 +- docs/src/examples/damped-alfven-wave.py | 2 +- docs/src/examples/diffusion-methods.py | 2 +- docs/src/examples/diocotron-instability.py | 2 +- docs/src/examples/faraday-rotation.py | 2 +- docs/src/examples/gas-expansion.py | 2 +- docs/src/examples/grad-b-drift.py | 2 +- docs/src/examples/guiding-center-orbits.py | 2 +- docs/src/examples/gvec-equilibrium.py | 2 +- docs/src/examples/gyromotion.py | 2 +- docs/src/examples/hall-mhd-waves.py | 2 +- docs/src/examples/hasegawa-wakatani.py | 2 +- docs/src/examples/hybrid-alfven-ion-coupling.py | 2 +- docs/src/examples/hybrid-current-coupling.py | 2 +- .../examples/incompressible-shear-relaxation.py | 2 +- docs/src/examples/itg-drift-wave.py | 2 +- docs/src/examples/itpa-tae-linear-mhd.py | 2 +- docs/src/examples/itpa-tae-shear-alfven.py | 2 +- docs/src/examples/langmuir-wave-dispersion.py | 2 +- .../src/examples/linear-dissipative-alfven-wave.py | 2 +- docs/src/examples/maxwell-cavity-resonances.py | 2 +- docs/src/examples/maxwell-curved-mesh.py | 2 +- .../src/examples/maxwell-structure-preservation.py | 2 +- docs/src/examples/maxwell-wave.py | 2 +- docs/src/examples/mhd-slab-waves.py | 2 +- docs/src/examples/ordinary-mode-dispersion.py | 2 +- docs/src/examples/orszag-tang-vortex.py | 2 +- docs/src/examples/poisson-convergence.py | 2 +- docs/src/examples/poisson-source.py | 2 +- docs/src/examples/pressureless-transport.py | 2 +- docs/src/examples/resistive-diffusion.py | 2 +- docs/src/examples/resistive-x-point.py | 2 +- docs/src/examples/shear-alfven-wave.py | 2 +- docs/src/examples/sph-velocity-diffusion.py | 2 +- docs/src/examples/strong-landau-damping.py | 2 +- docs/src/examples/toroidal-shear-alfven.py | 2 +- docs/src/examples/two-stream-instability.py | 2 +- docs/src/examples/vlasov-tokamak.py | 2 +- docs/src/examples/vortex-merger.py | 2 +- docs/src/examples/weak-landau-damping.py | 2 +- docs/src/examples/weibel-instability.py | 2 +- docs/src/examples/zeldovich-caustic.py | 2 +- requirements-examples.txt | 3 +++ requirements.txt | 2 ++ scripts/generate_hero_stellarator.py | 2 +- submodules/plasma-plots | 1 - submodules/struphy | 1 + 64 files changed, 89 insertions(+), 81 deletions(-) create mode 100644 requirements-examples.txt create mode 100644 requirements.txt delete mode 160000 submodules/plasma-plots create mode 160000 submodules/struphy diff --git a/.github/scripts/example-cache.cjs b/.github/scripts/example-cache.cjs index d10c777..c14e7d0 100644 --- a/.github/scripts/example-cache.cjs +++ b/.github/scripts/example-cache.cjs @@ -2,14 +2,15 @@ async function exampleCache(example, prefix, suffix, glob) { const patterns = [ `docs/src/examples/${example}.py`, - 'submodules/plasma-plots/src/plasma_plots/**/*.py', + 'requirements.txt', + 'requirements-examples.txt', 'catalogue_docs.py', 'generate_examples.py', 'run_example.py', '.github/precomputed-examples.json', 'generate_example_domain.py', 'generate_domains.py', - 'submodules/plasma-plots/struphy/src/struphy/**/*.py', + 'submodules/struphy/src/struphy/**/*.py', ]; return { key: `${prefix}${suffix}-${example}-${await glob.hashFiles(patterns.join('\n'))}`, diff --git a/.github/scripts/list-examples.test.cjs b/.github/scripts/list-examples.test.cjs index 5fd1843..88944c4 100644 --- a/.github/scripts/list-examples.test.cjs +++ b/.github/scripts/list-examples.test.cjs @@ -80,6 +80,7 @@ test('shared keys retain prefix, suffix, example and source hash', async () => { }); assert.equal(result.key, 'pitagora-example-v1-custom-alpha-hash'); assert.equal(patterns[0], 'docs/src/examples/alpha.py'); - assert.ok(patterns.includes('submodules/plasma-plots/struphy/src/struphy/**/*.py')); - assert.ok(patterns.includes('submodules/plasma-plots/src/plasma_plots/**/*.py')); + assert.ok(patterns.includes('submodules/struphy/src/struphy/**/*.py')); + assert.ok(patterns.includes('requirements.txt')); + assert.ok(patterns.includes('requirements-examples.txt')); }); diff --git a/.github/workflows/build-site.yml b/.github/workflows/build-site.yml index f1d97f6..5a1c61d 100644 --- a/.github/workflows/build-site.yml +++ b/.github/workflows/build-site.yml @@ -97,7 +97,7 @@ jobs: python-version: '3.12' - name: Install Struphy - run: python -m pip install --upgrade pip && python -m pip install ./submodules/plasma-plots/struphy ./submodules/plasma-plots + run: python -m pip install --upgrade pip && python -m pip install ./submodules/struphy -r requirements.txt - name: Generate equilibrium slices run: python generate_equilibrium_slices.py diff --git a/.github/workflows/pitagora-examples.yml b/.github/workflows/pitagora-examples.yml index b6c9bbc..ef842f4 100644 --- a/.github/workflows/pitagora-examples.yml +++ b/.github/workflows/pitagora-examples.yml @@ -53,8 +53,9 @@ jobs: export CXX="$(command -v g++)" source /pitagora_scratch/userexternal/mlindqvi/github_runner_builds/.venv/bin/activate - git -C submodules/plasma-plots/struphy rev-parse HEAD - python -m pip install './submodules/plasma-plots/struphy[phys,mpi]' './submodules/plasma-plots[gallery]' + git -C submodules/struphy rev-parse HEAD + python -m pip install './submodules/struphy[phys,mpi]' + python -m pip install -r requirements-examples.txt python -m pip install 'scope-profiler[pproc]>=0.6.1,<=0.7.0' kaleido 'slurm-script-generator==0.4.0' # Install Chrome into Kaleido's user-accessible location for PNG export. kaleido_get_chrome diff --git a/.github/workflows/run-example.yml b/.github/workflows/run-example.yml index f4e3d5a..8051d81 100644 --- a/.github/workflows/run-example.yml +++ b/.github/workflows/run-example.yml @@ -52,7 +52,7 @@ jobs: - name: Install Struphy from this repo's pinned submodule commit # Same approach as struphy's own CI - # (submodules/plasma-plots/struphy/.github/actions/install/struphy_in_container/action.yml): + # (submodules/struphy/.github/actions/install/struphy_in_container/action.yml): # the image bakes in whatever struphy commit was HEAD when it was last built, so # move /struphy_fortran_'s own checkout to the exact commit our submodule pins # (rather than reinstalling editable from a second copy of the source) and @@ -66,13 +66,13 @@ jobs: # # plasma-plots registers the .plasma accessors that the example scripts use, and its # gallery extra brings plasma_plots.gallery, the helpers that write every example's - # figures, profiling and metadata. It has no compiled parts, so it is installed straight - # from our pinned submodule. + # figures, profiling and metadata. Install the pinned PyPI release using the + # gallery requirements file. if: steps.cache-example.outputs.cache-hit != 'true' shell: bash run: | - STRUPHY_PLOTS="$PWD/submodules/plasma-plots" - STRUPHY_SHA=$(git -C submodules/plasma-plots/struphy rev-parse HEAD) + SITE_REQUIREMENTS="$PWD/requirements-examples.txt" + STRUPHY_SHA=$(git -C submodules/struphy rev-parse HEAD) cd /struphy_fortran_ git fetch origin --tags git checkout "$STRUPHY_SHA" @@ -83,7 +83,7 @@ jobs: # exists from 0.6.1 onward -- --upgrade forces past struphy's conservative pin # to the version this was actually developed and tested against. pip install --upgrade "scope-profiler[pproc]>=0.6.1" kaleido - pip install "$STRUPHY_PLOTS[gallery]" + pip install -r "$SITE_REQUIREMENTS" - name: Rebuild GVEC for this runner's CPU # The published gvec wheel is built with -march=native. GitHub-hosted @@ -110,7 +110,7 @@ jobs: path: | /struphy_fortran_/src/struphy/**/__pyccel__ /struphy_fortran_/src/struphy/**/*.so - key: struphy-kernels-${{ hashFiles('submodules/plasma-plots/struphy/src/struphy/**/*.py') }} + key: struphy-kernels-${{ hashFiles('submodules/struphy/src/struphy/**/*.py') }} # Chrome doesn't depend on the struphy commit or example scripts, so cache it under # its own static key instead of the cache-example key above -- otherwise every diff --git a/.gitmodules b/.gitmodules index 307f723..1a0e6e2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ -[submodule "submodules/struphy-plots"] - path = submodules/plasma-plots - url = https://github.com/struphy-hub/plasma-plots.git +[submodule "struphy"] + path = submodules/struphy + url = https://github.com/struphy-hub/struphy.git diff --git a/README.md b/README.md index d66c00e..01d441f 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,8 @@ # struphy-hub.github.io The Struphy documentation site: an [Astro](https://astro.build) site in `docs/`, backed by data -generated from the [Struphy](https://github.com/struphy-hub/struphy) Python package (checked out -here as a nested git submodule of [plasma-plots](https://github.com/struphy-hub/plasma-plots), at -`submodules/plasma-plots/struphy`). +generated from the [Struphy](https://github.com/struphy-hub/struphy) Python package, pinned as a +git submodule at `submodules/struphy`. Plotting uses the PyPI release `plasma-plots==0.1.1`. ## Building the site locally @@ -20,14 +19,14 @@ If you already cloned without `--recurse-submodules`: git submodule update --init --recursive ``` -### 2. Install Struphy and plasma-plots from the submodules +### 2. Install Struphy and the pinned plasma-plots release Requires Python 3.12. From the repo root: ```sh python -m venv .venv source .venv/bin/activate -pip install ./submodules/plasma-plots/struphy './submodules/plasma-plots[gallery]' +pip install ./submodules/struphy -r requirements.txt ``` ### 3. Generate the site's data @@ -74,7 +73,8 @@ The full pipeline (including the CI-specific steps) is defined in `docs/src/examples/*.py` are full, runnable Struphy simulations shown on the `/examples/` pages, separate from the data pipeline above. They need `struphy compile` and a heavier install -(`pip install './submodules/plasma-plots/struphy[mpi]'`, or whatever extras the example needs) and aren't +(`pip install './submodules/struphy[phys,mpi]'`, followed by +`pip install -r requirements-examples.txt`) and aren't required to build the site. `generate_examples.py` (repo root) generates their page metadata (name, description, equations, config summary) by importing each script without running its simulation — see `docs/src/examples/README.md` for the full step-by-step guide to adding a diff --git a/cli.py b/cli.py index 3a8f1bd..7585efe 100644 --- a/cli.py +++ b/cli.py @@ -15,8 +15,8 @@ python cli.py clean --all # remove everything a run generated Only the standard library is used, so ``list``, ``show`` and ``clean`` work without Struphy. -``metadata`` and ``run`` need Struphy (``pip install ./submodules/plasma-plots/struphy``) and ``run`` also -its compiled kernels (``struphy compile``) and plasma-plots (``pip install './submodules/plasma-plots[gallery]'``). +``metadata`` and ``run`` need Struphy (``pip install ./submodules/struphy``) and ``run`` also +its compiled kernels (``struphy compile``) and plasma-plots (``pip install -r requirements-examples.txt``). """ from __future__ import annotations @@ -220,14 +220,14 @@ def python_env_problem(need_kernels: bool) -> str | None: return ( "Struphy is not importable in this Python. From the repository root:\n" " python -m venv .venv && source .venv/bin/activate\n" - " pip install ./submodules/plasma-plots/struphy" + " pip install ./submodules/struphy" + ("\n struphy compile" if need_kernels else "") ) if need_kernels and importlib.util.find_spec("plasma_plots") is None: return ( "plasma-plots is not importable in this Python (the examples use its accessors and\n" "plasma_plots.gallery). From the repository root:\n" - " pip install './submodules/plasma-plots[gallery]'" + " pip install -r requirements-examples.txt" ) for module in ("plotly", "kaleido"): if need_kernels and importlib.util.find_spec(module) is None: diff --git a/docs/src/examples/README.md b/docs/src/examples/README.md index a7ee19f..a663e5e 100644 --- a/docs/src/examples/README.md +++ b/docs/src/examples/README.md @@ -102,7 +102,7 @@ filename. ## 2. Generate the structural metadata -From the repo root, with Struphy installed (`pip install ./submodules/plasma-plots/struphy` — no +From the repo root, with Struphy installed (`pip install ./submodules/struphy` — no compiled kernels needed for this step): ```sh @@ -230,7 +230,7 @@ in its metadata. No list to edit by hand. Use the Struphy revision pinned by this repository, including local submodule changes while developing. The full Orszag–Tang run needs the fix in `struphy/feec/mass.py` that preserves geometric weights between density-weighted matrix assemblies. Commit that fix and its regression test in Struphy, -then bump the `struphy` pointer in plasma-plots and the website's `submodules/plasma-plots` pointer before publishing. A website-only commit cannot +then bump the website's `submodules/struphy` pointer before publishing. A website-only commit cannot reproduce this example in CI. The Orszag–Tang script checks for non-finite diagnostics, non-positive density and incomplete diff --git a/docs/src/examples/acoustic-pulse.py b/docs/src/examples/acoustic-pulse.py index 1c461b9..9ce3569 100644 --- a/docs/src/examples/acoustic-pulse.py +++ b/docs/src/examples/acoustic-pulse.py @@ -8,7 +8,7 @@ starts as pure thermodynamic energy, which the running pulses share with kinetic energy. Requires Struphy 3.3 with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/alfven-standing-wave.py b/docs/src/examples/alfven-standing-wave.py index cce52be..fef5015 100644 --- a/docs/src/examples/alfven-standing-wave.py +++ b/docs/src/examples/alfven-standing-wave.py @@ -11,7 +11,7 @@ propagator is a Crank-Nicolson step, which conserves that sum up to the tolerance of the linear solver. Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/beltrami-sph.py b/docs/src/examples/beltrami-sph.py index dc902eb..1a8cf3a 100644 --- a/docs/src/examples/beltrami-sph.py +++ b/docs/src/examples/beltrami-sph.py @@ -14,7 +14,7 @@ Adapted from Struphy's ``tutorial_beltrami_sph.ipynb`` tutorial. Requires Struphy 3.3 with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/bump-on-tail.py b/docs/src/examples/bump-on-tail.py index d09f571..2ff34e8 100644 --- a/docs/src/examples/bump-on-tail.py +++ b/docs/src/examples/bump-on-tail.py @@ -8,7 +8,7 @@ Adapted from Struphy's maintained example (examples/VlasovAmpereOneSpecies/bump_on). Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/coaxial-waveguide.py b/docs/src/examples/coaxial-waveguide.py index 9f18da0..6a2f03e 100644 --- a/docs/src/examples/coaxial-waveguide.py +++ b/docs/src/examples/coaxial-waveguide.py @@ -8,7 +8,7 @@ Adapted from Struphy's tutorial (tutorials/tutorial_maxwell.ipynb) and its verification test. Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/cold-plasma-oscillation.py b/docs/src/examples/cold-plasma-oscillation.py index d2d4b16..fd37dbf 100644 --- a/docs/src/examples/cold-plasma-oscillation.py +++ b/docs/src/examples/cold-plasma-oscillation.py @@ -7,7 +7,7 @@ sin^2(omega_p t). The plasma frequency scales as sqrt(n0), which a short scan over the density confirms. Requires Struphy 3.3 with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/cold-plasma-wave-packet.py b/docs/src/examples/cold-plasma-wave-packet.py index 8e9dbe3..87ec0ad 100644 --- a/docs/src/examples/cold-plasma-wave-packet.py +++ b/docs/src/examples/cold-plasma-wave-packet.py @@ -9,7 +9,7 @@ each packet is compared with the analytic group velocity d omega / d k of the cold-plasma dispersion relation. Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/cold-plasma-waves.py b/docs/src/examples/cold-plasma-waves.py index 79c0ad9..1c7b661 100644 --- a/docs/src/examples/cold-plasma-waves.py +++ b/docs/src/examples/cold-plasma-waves.py @@ -7,7 +7,7 @@ E_x shows them, and is compared with the analytic cold-plasma dispersion relation. Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/dam-break.py b/docs/src/examples/dam-break.py index 79ff0ab..463c6c2 100644 --- a/docs/src/examples/dam-break.py +++ b/docs/src/examples/dam-break.py @@ -9,7 +9,7 @@ Adapted from Struphy's tutorial (tutorials/tutorial_dam_break_sph.ipynb). Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/damped-alfven-wave.py b/docs/src/examples/damped-alfven-wave.py index 96f3398..38bf830 100644 --- a/docs/src/examples/damped-alfven-wave.py +++ b/docs/src/examples/damped-alfven-wave.py @@ -7,7 +7,7 @@ configuration.) A scan over three resistivities compares the decay with the exact rate. Requires Struphy 3.3 with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/diffusion-methods.py b/docs/src/examples/diffusion-methods.py index f902a1c..ea9a20b 100644 --- a/docs/src/examples/diffusion-methods.py +++ b/docs/src/examples/diffusion-methods.py @@ -13,7 +13,7 @@ Both start from the same markers and are compared with the exact decay. Requires Struphy 3.3 with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/diocotron-instability.py b/docs/src/examples/diocotron-instability.py index e30b116..3b023ba 100644 --- a/docs/src/examples/diocotron-instability.py +++ b/docs/src/examples/diocotron-instability.py @@ -12,7 +12,7 @@ Mehrenberger & Vecil (2014), https://doi.org/10.1140/epjd/e2014-50180-9. Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/faraday-rotation.py b/docs/src/examples/faraday-rotation.py index 63c87fa..bd4a2cc 100644 --- a/docs/src/examples/faraday-rotation.py +++ b/docs/src/examples/faraday-rotation.py @@ -8,7 +8,7 @@ Reference: https://farside.ph.utexas.edu/teaching/315/Waveshtml/node76.html Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/gas-expansion.py b/docs/src/examples/gas-expansion.py index b3d30fa..60abefa 100644 --- a/docs/src/examples/gas-expansion.py +++ b/docs/src/examples/gas-expansion.py @@ -9,7 +9,7 @@ so that an exact solution exists. Requires Struphy 3.2 with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/grad-b-drift.py b/docs/src/examples/grad-b-drift.py index a3bf501..337eeb5 100644 --- a/docs/src/examples/grad-b-drift.py +++ b/docs/src/examples/grad-b-drift.py @@ -10,7 +10,7 @@ Reference: https://farside.ph.utexas.edu/teaching/plasma/lectures/node19.html Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/guiding-center-orbits.py b/docs/src/examples/guiding-center-orbits.py index 980509d..803581f 100644 --- a/docs/src/examples/guiding-center-orbits.py +++ b/docs/src/examples/guiding-center-orbits.py @@ -10,7 +10,7 @@ Adapted from Struphy's particle-tracing tutorial (tutorials/tutorial_particle_tracing.ipynb). Requires Struphy 3.2 with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/gvec-equilibrium.py b/docs/src/examples/gvec-equilibrium.py index 4c005a0..d75f12b 100644 --- a/docs/src/examples/gvec-equilibrium.py +++ b/docs/src/examples/gvec-equilibrium.py @@ -13,7 +13,7 @@ in them, the faster ones circulate. Requires Struphy with compiled kernels (`struphy compile`) and its optional physics dependencies -(`pip install -e ".[phys]"`), and plasma-plots with Plotly (`pip install "plasma-plots[plotly]"`). +(`pip install -e ".[phys]"`), and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/gyromotion.py b/docs/src/examples/gyromotion.py index 411fb21..80343f2 100644 --- a/docs/src/examples/gyromotion.py +++ b/docs/src/examples/gyromotion.py @@ -7,7 +7,7 @@ helices: the gyroperiod is the same for all of them, the Larmor radius grows in proportion to v_perp. Requires Struphy 3.3 with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/hall-mhd-waves.py b/docs/src/examples/hall-mhd-waves.py index 6773f25..cc29ab7 100644 --- a/docs/src/examples/hall-mhd-waves.py +++ b/docs/src/examples/hall-mhd-waves.py @@ -9,7 +9,7 @@ spectrum with the exact ones. Requires Struphy 3.3 with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/hasegawa-wakatani.py b/docs/src/examples/hasegawa-wakatani.py index 17fd7a9..b18f169 100644 --- a/docs/src/examples/hasegawa-wakatani.py +++ b/docs/src/examples/hasegawa-wakatani.py @@ -10,7 +10,7 @@ eddies and the slow reorganization of their energy. Requires Struphy with compiled kernels (``struphy compile``) and plasma-plots with Plotly -(``pip install "plasma-plots[plotly]"``). Run as a script, it saves its figures in the current +(``pip install "plasma-plots[plotly]==0.1.1"``). Run as a script, it saves its figures in the current directory (``--show`` shows them first). """ diff --git a/docs/src/examples/hybrid-alfven-ion-coupling.py b/docs/src/examples/hybrid-alfven-ion-coupling.py index d00eb45..901d5aa 100644 --- a/docs/src/examples/hybrid-alfven-ion-coupling.py +++ b/docs/src/examples/hybrid-alfven-ion-coupling.py @@ -8,7 +8,7 @@ scheme. Requires Struphy 3.2 with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/hybrid-current-coupling.py b/docs/src/examples/hybrid-current-coupling.py index 1ce6009..ef95fcd 100644 --- a/docs/src/examples/hybrid-current-coupling.py +++ b/docs/src/examples/hybrid-current-coupling.py @@ -12,7 +12,7 @@ conservation demonstration, not a measurement of a kinetic damping rate. Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/incompressible-shear-relaxation.py b/docs/src/examples/incompressible-shear-relaxation.py index e65b72a..a3b8bcb 100644 --- a/docs/src/examples/incompressible-shear-relaxation.py +++ b/docs/src/examples/incompressible-shear-relaxation.py @@ -11,7 +11,7 @@ (models/tests/verification/test_verif_IncompressibleNavierStokesSPH.py). Requires Struphy 3.3 with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/itg-drift-wave.py b/docs/src/examples/itg-drift-wave.py index f044e74..297b2f4 100644 --- a/docs/src/examples/itg-drift-wave.py +++ b/docs/src/examples/itg-drift-wave.py @@ -12,7 +12,7 @@ resolution and run length to keep it a quick gallery run. Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/itpa-tae-linear-mhd.py b/docs/src/examples/itpa-tae-linear-mhd.py index 0068f07..dd8dbed 100644 --- a/docs/src/examples/itpa-tae-linear-mhd.py +++ b/docs/src/examples/itpa-tae-linear-mhd.py @@ -6,7 +6,7 @@ continua and the gap-centre estimate, with the radial eigenfunction of each harmonic. Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). The parameters are those of the benchmark: 24 x 96 x 16 cells of degree 3 to t = 500, hours on a diff --git a/docs/src/examples/itpa-tae-shear-alfven.py b/docs/src/examples/itpa-tae-shear-alfven.py index 2f6a3d2..f1b1e91 100644 --- a/docs/src/examples/itpa-tae-shear-alfven.py +++ b/docs/src/examples/itpa-tae-shear-alfven.py @@ -8,7 +8,7 @@ continua and the gap-centre estimate, with the radial eigenfunction of each harmonic. Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). The parameters are those of the benchmark: 24 x 96 x 16 cells of degree 3 to t = 500, hours on a diff --git a/docs/src/examples/langmuir-wave-dispersion.py b/docs/src/examples/langmuir-wave-dispersion.py index 7c239e0..d92916b 100644 --- a/docs/src/examples/langmuir-wave-dispersion.py +++ b/docs/src/examples/langmuir-wave-dispersion.py @@ -11,7 +11,7 @@ shown for reference: it ignores the kinetic effects and misses the frequency by several per cent already at k = 0.5. Requires Struphy 3.3 with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/linear-dissipative-alfven-wave.py b/docs/src/examples/linear-dissipative-alfven-wave.py index 03ca4d9..957be95 100644 --- a/docs/src/examples/linear-dissipative-alfven-wave.py +++ b/docs/src/examples/linear-dissipative-alfven-wave.py @@ -7,7 +7,7 @@ and is not part of this linear perturbation system. Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/maxwell-cavity-resonances.py b/docs/src/examples/maxwell-cavity-resonances.py index 69e1318..fcb107a 100644 --- a/docs/src/examples/maxwell-cavity-resonances.py +++ b/docs/src/examples/maxwell-cavity-resonances.py @@ -10,7 +10,7 @@ the box is not a square, the resonances that would coincide in a square box separate. Requires Struphy 3.3 with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/maxwell-curved-mesh.py b/docs/src/examples/maxwell-curved-mesh.py index 9493883..14f7f4e 100644 --- a/docs/src/examples/maxwell-curved-mesh.py +++ b/docs/src/examples/maxwell-curved-mesh.py @@ -8,7 +8,7 @@ energy, which the structure-preserving scheme conserves for any mesh, is followed as well. Requires Struphy 3.3 with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/maxwell-structure-preservation.py b/docs/src/examples/maxwell-structure-preservation.py index e0c637f..6c662a1 100644 --- a/docs/src/examples/maxwell-structure-preservation.py +++ b/docs/src/examples/maxwell-structure-preservation.py @@ -13,7 +13,7 @@ condition does not save it: round-off seeds the unstable modes and they take over. Requires Struphy 3.3 with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/maxwell-wave.py b/docs/src/examples/maxwell-wave.py index b76e321..16fc437 100644 --- a/docs/src/examples/maxwell-wave.py +++ b/docs/src/examples/maxwell-wave.py @@ -5,7 +5,7 @@ FEEC, and plots the numerical dispersion relation against omega = c k. Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/mhd-slab-waves.py b/docs/src/examples/mhd-slab-waves.py index 56731fd..f7d4eff 100644 --- a/docs/src/examples/mhd-slab-waves.py +++ b/docs/src/examples/mhd-slab-waves.py @@ -8,7 +8,7 @@ Adapted from Struphy's tutorial (tutorials/tutorial_linear_mhd_slab_waves_1d.ipynb). Requires Struphy 3.3 with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/ordinary-mode-dispersion.py b/docs/src/examples/ordinary-mode-dispersion.py index d5b4f06..e20c8b6 100644 --- a/docs/src/examples/ordinary-mode-dispersion.py +++ b/docs/src/examples/ordinary-mode-dispersion.py @@ -8,7 +8,7 @@ Reference: https://farside.ph.utexas.edu/teaching/315/Waveshtml/node75.html Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/orszag-tang-vortex.py b/docs/src/examples/orszag-tang-vortex.py index a0fb201..fdf0f3d 100644 --- a/docs/src/examples/orszag-tang-vortex.py +++ b/docs/src/examples/orszag-tang-vortex.py @@ -5,7 +5,7 @@ energy and discrete magnetic-divergence diagnostics. Requires Struphy 3.3 with compiled kernels (``struphy compile``) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/poisson-convergence.py b/docs/src/examples/poisson-convergence.py index d296485..dc4b354 100644 --- a/docs/src/examples/poisson-convergence.py +++ b/docs/src/examples/poisson-convergence.py @@ -8,7 +8,7 @@ whose coarse meshes are not yet in the asymptotic range, they are steeper. Requires Struphy 3.3 with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/poisson-source.py b/docs/src/examples/poisson-source.py index 3f611a7..cb994c5 100644 --- a/docs/src/examples/poisson-source.py +++ b/docs/src/examples/poisson-source.py @@ -6,7 +6,7 @@ solution. Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/pressureless-transport.py b/docs/src/examples/pressureless-transport.py index 910b1f3..639f9cd 100644 --- a/docs/src/examples/pressureless-transport.py +++ b/docs/src/examples/pressureless-transport.py @@ -5,7 +5,7 @@ This is an exact nonlinear solution, without characteristic crossing. Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/resistive-diffusion.py b/docs/src/examples/resistive-diffusion.py index 7e886c5..d537d73 100644 --- a/docs/src/examples/resistive-diffusion.py +++ b/docs/src/examples/resistive-diffusion.py @@ -7,7 +7,7 @@ the pressure gradient it produces sets only a negligible flow. A scan over three resistivities confirms the rate. Requires Struphy 3.3 with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/resistive-x-point.py b/docs/src/examples/resistive-x-point.py index a923956..2b610e7 100644 --- a/docs/src/examples/resistive-x-point.py +++ b/docs/src/examples/resistive-x-point.py @@ -6,7 +6,7 @@ resistivity permits reconnection. Requires Struphy with compiled kernels (``struphy compile``) and plasma-plots with Plotly -(``pip install "plasma-plots[plotly]"``). Run as a script, it saves its figures in the current +(``pip install "plasma-plots[plotly]==0.1.1"``). Run as a script, it saves its figures in the current directory (``--show`` shows them first). """ diff --git a/docs/src/examples/shear-alfven-wave.py b/docs/src/examples/shear-alfven-wave.py index 91ccfb6..08b1164 100644 --- a/docs/src/examples/shear-alfven-wave.py +++ b/docs/src/examples/shear-alfven-wave.py @@ -7,7 +7,7 @@ v_A = B0 / sqrt(n0) = 1. Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/sph-velocity-diffusion.py b/docs/src/examples/sph-velocity-diffusion.py index 86fac07..f1b2bfb 100644 --- a/docs/src/examples/sph-velocity-diffusion.py +++ b/docs/src/examples/sph-velocity-diffusion.py @@ -6,7 +6,7 @@ makes a small, fast verification case with an analytic answer. Requires Struphy 3.3 with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/strong-landau-damping.py b/docs/src/examples/strong-landau-damping.py index afd17d9..d95239b 100644 --- a/docs/src/examples/strong-landau-damping.py +++ b/docs/src/examples/strong-landau-damping.py @@ -9,7 +9,7 @@ Adapted from Struphy's maintained example (examples/VlasovAmpereOneSpecies/strong_Landau_damping). Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/toroidal-shear-alfven.py b/docs/src/examples/toroidal-shear-alfven.py index 0225386..ad480c1 100644 --- a/docs/src/examples/toroidal-shear-alfven.py +++ b/docs/src/examples/toroidal-shear-alfven.py @@ -4,7 +4,7 @@ .venv/bin/python cli.py run toroidal-shear-alfven Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). The default is an exploratory local run, not a converged ITPA TAE benchmark. Increase NUM_ELEMENTS to (24, 96, 16), DEGREE to (3, 3, 3), and END_TIME diff --git a/docs/src/examples/two-stream-instability.py b/docs/src/examples/two-stream-instability.py index 01e224c..8addfa7 100644 --- a/docs/src/examples/two-stream-instability.py +++ b/docs/src/examples/two-stream-instability.py @@ -8,7 +8,7 @@ Adapted from Struphy's maintained example (examples/VlasovAmpereOneSpecies/two_stream). Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/vlasov-tokamak.py b/docs/src/examples/vlasov-tokamak.py index ceac5a3..07ab386 100644 --- a/docs/src/examples/vlasov-tokamak.py +++ b/docs/src/examples/vlasov-tokamak.py @@ -8,7 +8,7 @@ (or bounces) through the torus, conserving its speed exactly. Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/vortex-merger.py b/docs/src/examples/vortex-merger.py index 10d47f2..464844a 100644 --- a/docs/src/examples/vortex-merger.py +++ b/docs/src/examples/vortex-merger.py @@ -9,7 +9,7 @@ an annulus with grounded walls, a uniform background field, and the ToyDrift model. Requires Struphy 3.2 with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/weak-landau-damping.py b/docs/src/examples/weak-landau-damping.py index 6e2b4ab..1c1fa38 100644 --- a/docs/src/examples/weak-landau-damping.py +++ b/docs/src/examples/weak-landau-damping.py @@ -8,7 +8,7 @@ Adapted from Struphy's maintained example (examples/VlasovAmpereOneSpecies/weak_Landau_damping). Requires Struphy 3.2 with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/weibel-instability.py b/docs/src/examples/weibel-instability.py index 504e743..c0e34cc 100644 --- a/docs/src/examples/weibel-instability.py +++ b/docs/src/examples/weibel-instability.py @@ -10,7 +10,7 @@ count and run length to keep it a quick gallery run. Requires Struphy 3.2 with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/zeldovich-caustic.py b/docs/src/examples/zeldovich-caustic.py index 025dbdc..207f4d7 100644 --- a/docs/src/examples/zeldovich-caustic.py +++ b/docs/src/examples/zeldovich-caustic.py @@ -11,7 +11,7 @@ first stage of the Zel'dovich approximation for structure formation. Requires Struphy 3.3 with compiled kernels (`struphy compile`) and plasma-plots with Plotly -(`pip install "plasma-plots[plotly]"`). Run as a script, it saves its figures in the current +(`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/requirements-examples.txt b/requirements-examples.txt new file mode 100644 index 0000000..57ccd9a --- /dev/null +++ b/requirements-examples.txt @@ -0,0 +1,3 @@ +# Gallery rendering and profiling dependencies for running examples. +-r requirements.txt +plasma-plots[gallery]==0.1.1 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..445e55f --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +# Plotting dependency for metadata generation and site data. +plasma-plots==0.1.1 diff --git a/scripts/generate_hero_stellarator.py b/scripts/generate_hero_stellarator.py index 5e7b425..0877d81 100644 --- a/scripts/generate_hero_stellarator.py +++ b/scripts/generate_hero_stellarator.py @@ -16,7 +16,7 @@ It also renders ``docs/public/images/stellarator-hero.webp``, the still shown until the viewer has loaded. All are small and committed, so the site build does not need GVEC. Rerun this script only when the hero -should change. It needs compiled Struphy kernels and ``pip install "./submodules/plasma-plots/struphy[phys]" pyvista``. +should change. It needs compiled Struphy kernels and ``pip install "./submodules/struphy[phys]" pyvista``. python scripts/generate_hero_stellarator.py """ diff --git a/submodules/plasma-plots b/submodules/plasma-plots deleted file mode 160000 index a2afc66..0000000 --- a/submodules/plasma-plots +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a2afc6695e402ba7ca2ece9585bfb1f514d6f965 diff --git a/submodules/struphy b/submodules/struphy new file mode 160000 index 0000000..e69fdef --- /dev/null +++ b/submodules/struphy @@ -0,0 +1 @@ +Subproject commit e69fdefe2d00a7a4eaf4a31ce7ea94775cab588f From 61272a78a5a967c3f9ca8cdc846622817beec192 Mon Sep 17 00:00:00 2001 From: Max Lindqvist Date: Fri, 2 Oct 2026 11:35:55 +0200 Subject: [PATCH 11/16] Removed struphy submodule --- .github/scripts/example-cache.cjs | 1 - .github/scripts/list-examples.test.cjs | 1 - .github/workflows/build-site.yml | 6 +- .github/workflows/pitagora-examples.yml | 11 +-- .github/workflows/run-example.yml | 73 ++++++------------- .gitmodules | 3 - README.md | 19 ++--- cli.py | 4 +- docs/src/examples/README.md | 11 ++- docs/src/examples/acoustic-pulse.py | 2 +- docs/src/examples/alfven-standing-wave.py | 2 +- docs/src/examples/beltrami-sph.py | 2 +- docs/src/examples/bump-on-tail.py | 2 +- docs/src/examples/coaxial-waveguide.py | 2 +- docs/src/examples/cold-plasma-oscillation.py | 2 +- docs/src/examples/cold-plasma-wave-packet.py | 2 +- docs/src/examples/cold-plasma-waves.py | 2 +- docs/src/examples/dam-break.py | 2 +- docs/src/examples/damped-alfven-wave.py | 2 +- docs/src/examples/diffusion-methods.py | 2 +- docs/src/examples/diocotron-instability.py | 2 +- docs/src/examples/faraday-rotation.py | 2 +- docs/src/examples/gas-expansion.py | 2 +- docs/src/examples/grad-b-drift.py | 2 +- docs/src/examples/guiding-center-orbits.py | 2 +- docs/src/examples/gvec-equilibrium.py | 4 +- docs/src/examples/gyromotion.py | 2 +- docs/src/examples/hall-mhd-waves.py | 2 +- docs/src/examples/hasegawa-wakatani.py | 2 +- .../examples/hybrid-alfven-ion-coupling.py | 2 +- docs/src/examples/hybrid-current-coupling.py | 2 +- .../incompressible-shear-relaxation.py | 2 +- docs/src/examples/itg-drift-wave.py | 2 +- docs/src/examples/itpa-tae-linear-mhd.py | 2 +- docs/src/examples/itpa-tae-shear-alfven.py | 2 +- docs/src/examples/langmuir-wave-dispersion.py | 2 +- .../linear-dissipative-alfven-wave.py | 2 +- .../src/examples/maxwell-cavity-resonances.py | 2 +- docs/src/examples/maxwell-curved-mesh.py | 2 +- .../maxwell-structure-preservation.py | 2 +- docs/src/examples/maxwell-wave.py | 2 +- docs/src/examples/mhd-slab-waves.py | 2 +- docs/src/examples/ordinary-mode-dispersion.py | 2 +- docs/src/examples/orszag-tang-vortex.py | 2 +- docs/src/examples/poisson-convergence.py | 2 +- docs/src/examples/poisson-source.py | 2 +- docs/src/examples/pressureless-transport.py | 2 +- docs/src/examples/resistive-diffusion.py | 2 +- docs/src/examples/resistive-x-point.py | 2 +- docs/src/examples/shear-alfven-wave.py | 2 +- docs/src/examples/sph-velocity-diffusion.py | 2 +- docs/src/examples/strong-landau-damping.py | 2 +- docs/src/examples/toroidal-shear-alfven.py | 2 +- docs/src/examples/two-stream-instability.py | 2 +- docs/src/examples/vlasov-tokamak.py | 2 +- docs/src/examples/vortex-merger.py | 2 +- docs/src/examples/weak-landau-damping.py | 2 +- docs/src/examples/weibel-instability.py | 2 +- docs/src/examples/zeldovich-caustic.py | 2 +- pyproject.toml | 5 +- requirements-examples.txt | 3 +- requirements.txt | 3 +- scripts/generate_hero_stellarator.py | 2 +- submodules/struphy | 1 - 64 files changed, 100 insertions(+), 145 deletions(-) delete mode 100644 .gitmodules delete mode 160000 submodules/struphy diff --git a/.github/scripts/example-cache.cjs b/.github/scripts/example-cache.cjs index c14e7d0..6204dd6 100644 --- a/.github/scripts/example-cache.cjs +++ b/.github/scripts/example-cache.cjs @@ -10,7 +10,6 @@ async function exampleCache(example, prefix, suffix, glob) { '.github/precomputed-examples.json', 'generate_example_domain.py', 'generate_domains.py', - 'submodules/struphy/src/struphy/**/*.py', ]; return { key: `${prefix}${suffix}-${example}-${await glob.hashFiles(patterns.join('\n'))}`, diff --git a/.github/scripts/list-examples.test.cjs b/.github/scripts/list-examples.test.cjs index 88944c4..211c38e 100644 --- a/.github/scripts/list-examples.test.cjs +++ b/.github/scripts/list-examples.test.cjs @@ -80,7 +80,6 @@ test('shared keys retain prefix, suffix, example and source hash', async () => { }); assert.equal(result.key, 'pitagora-example-v1-custom-alpha-hash'); assert.equal(patterns[0], 'docs/src/examples/alpha.py'); - assert.ok(patterns.includes('submodules/struphy/src/struphy/**/*.py')); assert.ok(patterns.includes('requirements.txt')); assert.ok(patterns.includes('requirements-examples.txt')); }); diff --git a/.github/workflows/build-site.yml b/.github/workflows/build-site.yml index 5a1c61d..c369c56 100644 --- a/.github/workflows/build-site.yml +++ b/.github/workflows/build-site.yml @@ -27,8 +27,6 @@ jobs: steps: - name: Check out repository uses: actions/checkout@v4 - with: - submodules: recursive - name: Install cache client run: npm install --prefix "$RUNNER_TEMP/example-cache" --no-audit --no-fund --ignore-scripts @actions/cache@4.0.5 @@ -88,8 +86,6 @@ jobs: steps: - name: Check out repository uses: actions/checkout@v4 - with: - submodules: recursive - name: Set up Python uses: actions/setup-python@v5 @@ -97,7 +93,7 @@ jobs: python-version: '3.12' - name: Install Struphy - run: python -m pip install --upgrade pip && python -m pip install ./submodules/struphy -r requirements.txt + run: python -m pip install --upgrade pip && python -m pip install -r requirements.txt - name: Generate equilibrium slices run: python generate_equilibrium_slices.py diff --git a/.github/workflows/pitagora-examples.yml b/.github/workflows/pitagora-examples.yml index ef842f4..efc24b7 100644 --- a/.github/workflows/pitagora-examples.yml +++ b/.github/workflows/pitagora-examples.yml @@ -27,10 +27,8 @@ jobs: SLURM_PARTITION: dcgp_fua_dbg EXAMPLES: ${{ inputs.examples }} steps: - - name: Check out repository and Struphy + - name: Check out repository uses: actions/checkout@v4 - with: - submodules: recursive - name: Install Struphy and run example # A login shell initializes the cluster's module command. Keep setup and @@ -53,14 +51,13 @@ jobs: export CXX="$(command -v g++)" source /pitagora_scratch/userexternal/mlindqvi/github_runner_builds/.venv/bin/activate - git -C submodules/struphy rev-parse HEAD - python -m pip install './submodules/struphy[phys,mpi]' - python -m pip install -r requirements-examples.txt + python -m pip uninstall -y struphy + python -m pip install --upgrade -r requirements-examples.txt python -m pip install 'scope-profiler[pproc]>=0.6.1,<=0.7.0' kaleido 'slurm-script-generator==0.4.0' # Install Chrome into Kaleido's user-accessible location for PNG export. kaleido_get_chrome struphy compile --status - # struphy compile -y --language fortran + struphy compile -y --language fortran # The Python API generates a self-contained serial batch script, # submits it, waits for its exact job ID, verifies its final Slurm # state, and prints its batch logs. Submit exactly one job at a time diff --git a/.github/workflows/run-example.yml b/.github/workflows/run-example.yml index 8051d81..5ccd9b7 100644 --- a/.github/workflows/run-example.yml +++ b/.github/workflows/run-example.yml @@ -29,17 +29,9 @@ jobs: steps: - name: Check out repository uses: actions/checkout@v4 - with: - submodules: recursive - - - name: Trust the checked-out worktree - # Actions containers run as root; git refuses to touch a worktree owned by another - # UID ("detected dubious ownership") unless told to trust it -- struphy's own - # editable install below shells out to git (setuptools-scm) and would hit this. - run: git config --global --add safe.directory '*' # Each run is deterministic (fixed seeds), so cache on this example script's and - # Struphy's own source, and skip the expensive rebuild when neither changed. Every + # Python requirements, and skip the expensive rebuild when neither changed. Every # file an example writes is named after its script stem (the metadata JSON, the # figures, their thumbnails); no example's stem is a prefix of another's, and the # glob also covers two-stream-instability's extra "-phasespace" pair. @@ -50,40 +42,26 @@ jobs: example: ${{ inputs.example }} cache-prefix: struphy-example-serial-v1 - - name: Install Struphy from this repo's pinned submodule commit - # Same approach as struphy's own CI - # (submodules/struphy/.github/actions/install/struphy_in_container/action.yml): - # the image bakes in whatever struphy commit was HEAD when it was last built, so - # move /struphy_fortran_'s own checkout to the exact commit our submodule pins - # (rather than reinstalling editable from a second copy of the source) and - # reinstall there, reusing the image's already-installed venv and already-built - # gvec/desc-opt. - # - # scope-profiler[pproc] and kaleido aren't pulled in by the phys/mpi extras (only by - # struphy's own dev/doc extras) but the example scripts need both: scope-profiler's - # pproc submodule for the durations/gantt/flame plot-data export, kaleido for - # figure.write_image(...)'s static PNG export. - # - # plasma-plots registers the .plasma accessors that the example scripts use, and its - # gallery extra brings plasma_plots.gallery, the helpers that write every example's - # figures, profiling and metadata. Install the pinned PyPI release using the - # gallery requirements file. + - name: Install Python dependencies from PyPI + id: install-python + # Replace the image's editable Struphy install with a released wheel. + # The gallery extra provides Plotly exports and profiling helpers. if: steps.cache-example.outputs.cache-hit != 'true' shell: bash run: | - SITE_REQUIREMENTS="$PWD/requirements-examples.txt" - STRUPHY_SHA=$(git -C submodules/struphy rev-parse HEAD) - cd /struphy_fortran_ - git fetch origin --tags - git checkout "$STRUPHY_SHA" - source env_fortran_/bin/activate - pip install -U --upgrade-strategy eager -e ".[phys,mpi]" - # Struphy pins scope-profiler <=0.5.0 as a hard dependency, but the example - # scripts use plot_durations(metrics=(...)), a plural-metrics API that only - # exists from 0.6.1 onward -- --upgrade forces past struphy's conservative pin - # to the version this was actually developed and tested against. - pip install --upgrade "scope-profiler[pproc]>=0.6.1" kaleido - pip install -r "$SITE_REQUIREMENTS" + source /struphy_fortran_/env_fortran_/bin/activate + python -m pip uninstall -y struphy + python -m pip install --upgrade -r requirements-examples.txt + python - <<'PY' + import os + import sysconfig + from importlib.metadata import distribution, version + with open(os.environ["GITHUB_OUTPUT"], "a") as output: + print(f"struphy-path={distribution('struphy').locate_file('struphy')}", file=output) + print(f"struphy-version={version('struphy')}", file=output) + print(f"python-abi={sysconfig.get_config_var('SOABI')}", file=output) + print(f"pyccel-version={version('pyccel')}", file=output) + PY - name: Rebuild GVEC for this runner's CPU # The published gvec wheel is built with -march=native. GitHub-hosted @@ -97,22 +75,19 @@ jobs: lscpu | grep -E 'Model name|Flags' python -m pip install --no-binary=gvec --no-cache-dir --no-deps --force-reinstall 'gvec==1.5.0' - # Compiled kernels depend only on struphy's own source, not on which example this - # shard runs -- cache them under their own key (shared across all 10 shards) so that - # when only an example script changes, the 1-2 min recompile happens once instead of - # once per shard. Keyed independently of cache-example above; checked out to the - # right commit by the (fast, still-unconditional) install step regardless of hit/miss. + # Cache kernels in the installed package, using the resolved release and + # Python/compiler versions so an upgraded wheel gets a fresh compilation. - name: Cache compiled Struphy kernels id: cache-kernels if: steps.cache-example.outputs.cache-hit != 'true' uses: actions/cache@v4 with: path: | - /struphy_fortran_/src/struphy/**/__pyccel__ - /struphy_fortran_/src/struphy/**/*.so - key: struphy-kernels-${{ hashFiles('submodules/struphy/src/struphy/**/*.py') }} + ${{ steps.install-python.outputs.struphy-path }}/**/__pyccel__ + ${{ steps.install-python.outputs.struphy-path }}/**/*.so + key: struphy-kernels-pypi-${{ runner.os }}-${{ runner.arch }}-${{ steps.install-python.outputs.struphy-version }}-${{ steps.install-python.outputs.python-abi }}-${{ steps.install-python.outputs.pyccel-version }}-${{ hashFiles('requirements*.txt') }} - # Chrome doesn't depend on the struphy commit or example scripts, so cache it under + # Chrome doesn't depend on the Struphy release or example scripts, so cache it under # its own static key instead of the cache-example key above -- otherwise every # cache-miss shard redundantly re-downloads ~150-200MB from Google plus its apt # dependencies for no reason. diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 1a0e6e2..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "struphy"] - path = submodules/struphy - url = https://github.com/struphy-hub/struphy.git diff --git a/README.md b/README.md index 01d441f..a3516a8 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,18 @@ # struphy-hub.github.io The Struphy documentation site: an [Astro](https://astro.build) site in `docs/`, backed by data -generated from the [Struphy](https://github.com/struphy-hub/struphy) Python package, pinned as a -git submodule at `submodules/struphy`. Plotting uses the PyPI release `plasma-plots==0.1.1`. +generated from the [Struphy](https://github.com/struphy-hub/struphy) Python package. Both Python +packages come from PyPI: `struphy>=3.4.0` and `plasma-plots==0.1.1`. ## Building the site locally -### 1. Get the code, including the Struphy submodule +### 1. Get the code ```sh -git clone --recurse-submodules git@github.com:struphy-hub/struphy-hub.github.io.git +git clone git@github.com:struphy-hub/struphy-hub.github.io.git cd struphy-hub.github.io ``` -If you already cloned without `--recurse-submodules`: - -```sh -git submodule update --init --recursive -``` - ### 2. Install Struphy and the pinned plasma-plots release Requires Python 3.12. From the repo root: @@ -26,7 +20,7 @@ Requires Python 3.12. From the repo root: ```sh python -m venv .venv source .venv/bin/activate -pip install ./submodules/struphy -r requirements.txt +pip install -r requirements.txt ``` ### 3. Generate the site's data @@ -73,8 +67,7 @@ The full pipeline (including the CI-specific steps) is defined in `docs/src/examples/*.py` are full, runnable Struphy simulations shown on the `/examples/` pages, separate from the data pipeline above. They need `struphy compile` and a heavier install -(`pip install './submodules/struphy[phys,mpi]'`, followed by -`pip install -r requirements-examples.txt`) and aren't +(`pip install -r requirements-examples.txt`) and aren't required to build the site. `generate_examples.py` (repo root) generates their page metadata (name, description, equations, config summary) by importing each script without running its simulation — see `docs/src/examples/README.md` for the full step-by-step guide to adding a diff --git a/cli.py b/cli.py index 7585efe..59c8ded 100644 --- a/cli.py +++ b/cli.py @@ -15,7 +15,7 @@ python cli.py clean --all # remove everything a run generated Only the standard library is used, so ``list``, ``show`` and ``clean`` work without Struphy. -``metadata`` and ``run`` need Struphy (``pip install ./submodules/struphy``) and ``run`` also +``metadata`` and ``run`` need Struphy (``pip install -r requirements.txt``) and ``run`` also its compiled kernels (``struphy compile``) and plasma-plots (``pip install -r requirements-examples.txt``). """ @@ -220,7 +220,7 @@ def python_env_problem(need_kernels: bool) -> str | None: return ( "Struphy is not importable in this Python. From the repository root:\n" " python -m venv .venv && source .venv/bin/activate\n" - " pip install ./submodules/struphy" + " pip install -r " + ("requirements-examples.txt" if need_kernels else "requirements.txt") + ("\n struphy compile" if need_kernels else "") ) if need_kernels and importlib.util.find_spec("plasma_plots") is None: diff --git a/docs/src/examples/README.md b/docs/src/examples/README.md index a663e5e..d1f639b 100644 --- a/docs/src/examples/README.md +++ b/docs/src/examples/README.md @@ -102,7 +102,7 @@ filename. ## 2. Generate the structural metadata -From the repo root, with Struphy installed (`pip install ./submodules/struphy` — no +From the repo root, with Struphy installed (`pip install -r requirements.txt` — no compiled kernels needed for this step): ```sh @@ -227,11 +227,10 @@ in its metadata. No list to edit by hand. ## Reproducing the completed examples -Use the Struphy revision pinned by this repository, including local submodule changes while developing. -The full Orszag–Tang run needs the fix in `struphy/feec/mass.py` that preserves geometric weights -between density-weighted matrix assemblies. Commit that fix and its regression test in Struphy, -then bump the website's `submodules/struphy` pointer before publishing. A website-only commit cannot -reproduce this example in CI. +Install the released dependencies with `pip install -r requirements-examples.txt`: +Struphy >=3.4.0 and plasma-plots 0.1.1. Runtime fixes needed by an example must be +published in Struphy first; update the minimum Struphy version in the requirements +files when an example depends on a newer release. The Orszag–Tang script checks for non-finite diagnostics, non-positive density and incomplete evolution before publishing figures. The plotted current uses differences of sampled physical diff --git a/docs/src/examples/acoustic-pulse.py b/docs/src/examples/acoustic-pulse.py index 9ce3569..6da3124 100644 --- a/docs/src/examples/acoustic-pulse.py +++ b/docs/src/examples/acoustic-pulse.py @@ -7,7 +7,7 @@ d'Alembert's, rho - 1 = (f(x - c t) + f(x + c t)) / 2. Struphy's variational discretization conserves the total energy: the pulse starts as pure thermodynamic energy, which the running pulses share with kinetic energy. -Requires Struphy 3.3 with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/alfven-standing-wave.py b/docs/src/examples/alfven-standing-wave.py index fef5015..25faf1a 100644 --- a/docs/src/examples/alfven-standing-wave.py +++ b/docs/src/examples/alfven-standing-wave.py @@ -10,7 +10,7 @@ so each channel oscillates at twice the wave frequency while their sum stays constant. Struphy's propagator is a Crank-Nicolson step, which conserves that sum up to the tolerance of the linear solver. -Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/beltrami-sph.py b/docs/src/examples/beltrami-sph.py index 1a8cf3a..fd4de64 100644 --- a/docs/src/examples/beltrami-sph.py +++ b/docs/src/examples/beltrami-sph.py @@ -13,7 +13,7 @@ Adapted from Struphy's ``tutorial_beltrami_sph.ipynb`` tutorial. -Requires Struphy 3.3 with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/bump-on-tail.py b/docs/src/examples/bump-on-tail.py index 2ff34e8..61123ab 100644 --- a/docs/src/examples/bump-on-tail.py +++ b/docs/src/examples/bump-on-tail.py @@ -7,7 +7,7 @@ Adapted from Struphy's maintained example (examples/VlasovAmpereOneSpecies/bump_on). -Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/coaxial-waveguide.py b/docs/src/examples/coaxial-waveguide.py index 6a2f03e..87eead9 100644 --- a/docs/src/examples/coaxial-waveguide.py +++ b/docs/src/examples/coaxial-waveguide.py @@ -7,7 +7,7 @@ Adapted from Struphy's tutorial (tutorials/tutorial_maxwell.ipynb) and its verification test. -Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/cold-plasma-oscillation.py b/docs/src/examples/cold-plasma-oscillation.py index fd37dbf..5946f0e 100644 --- a/docs/src/examples/cold-plasma-oscillation.py +++ b/docs/src/examples/cold-plasma-oscillation.py @@ -6,7 +6,7 @@ Its energy passes back and forth between the electric field and the electron flow, as cos^2(omega_p t) and sin^2(omega_p t). The plasma frequency scales as sqrt(n0), which a short scan over the density confirms. -Requires Struphy 3.3 with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/cold-plasma-wave-packet.py b/docs/src/examples/cold-plasma-wave-packet.py index 87ec0ad..ad65601 100644 --- a/docs/src/examples/cold-plasma-wave-packet.py +++ b/docs/src/examples/cold-plasma-wave-packet.py @@ -8,7 +8,7 @@ other way: the L packet is fast, the R packet slow and spreading, since the whistler frequency depends strongly on k. The measured speed of each packet is compared with the analytic group velocity d omega / d k of the cold-plasma dispersion relation. -Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/cold-plasma-waves.py b/docs/src/examples/cold-plasma-waves.py index 1c7b661..c879df9 100644 --- a/docs/src/examples/cold-plasma-waves.py +++ b/docs/src/examples/cold-plasma-waves.py @@ -6,7 +6,7 @@ noise in the transverse electric field excites all branches at once; the (k, omega) power spectrum of E_x shows them, and is compared with the analytic cold-plasma dispersion relation. -Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/dam-break.py b/docs/src/examples/dam-break.py index 463c6c2..b386003 100644 --- a/docs/src/examples/dam-break.py +++ b/docs/src/examples/dam-break.py @@ -8,7 +8,7 @@ Adapted from Struphy's tutorial (tutorials/tutorial_dam_break_sph.ipynb). -Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/damped-alfven-wave.py b/docs/src/examples/damped-alfven-wave.py index 38bf830..f9b957c 100644 --- a/docs/src/examples/damped-alfven-wave.py +++ b/docs/src/examples/damped-alfven-wave.py @@ -6,7 +6,7 @@ gamma = eta k^2 / 2. (Viscosity would add nu k^2 / 2; it is left out because Struphy's viscosity propagator does not run in this configuration.) A scan over three resistivities compares the decay with the exact rate. -Requires Struphy 3.3 with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/diffusion-methods.py b/docs/src/examples/diffusion-methods.py index ea9a20b..36236d8 100644 --- a/docs/src/examples/diffusion-methods.py +++ b/docs/src/examples/diffusion-methods.py @@ -12,7 +12,7 @@ Both start from the same markers and are compared with the exact decay. -Requires Struphy 3.3 with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/diocotron-instability.py b/docs/src/examples/diocotron-instability.py index 3b023ba..7c8abef 100644 --- a/docs/src/examples/diocotron-instability.py +++ b/docs/src/examples/diocotron-instability.py @@ -11,7 +11,7 @@ (examples/ToyGyrokinetic/diocotron_instability). Parameters follow Crouseilles, Mehrenberger & Vecil (2014), https://doi.org/10.1140/epjd/e2014-50180-9. -Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/faraday-rotation.py b/docs/src/examples/faraday-rotation.py index bd4a2cc..c8f51e2 100644 --- a/docs/src/examples/faraday-rotation.py +++ b/docs/src/examples/faraday-rotation.py @@ -7,7 +7,7 @@ This initializes an established wave train, not a pulse injected at a boundary. Reference: https://farside.ph.utexas.edu/teaching/315/Waveshtml/node76.html -Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/gas-expansion.py b/docs/src/examples/gas-expansion.py index 60abefa..8c617dd 100644 --- a/docs/src/examples/gas-expansion.py +++ b/docs/src/examples/gas-expansion.py @@ -8,7 +8,7 @@ Adapted from Struphy's tutorial (tutorials/tutorial_gas_expansion_sph.ipynb), reduced to one dimension so that an exact solution exists. -Requires Struphy 3.2 with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/grad-b-drift.py b/docs/src/examples/grad-b-drift.py index 337eeb5..c6c31a9 100644 --- a/docs/src/examples/grad-b-drift.py +++ b/docs/src/examples/grad-b-drift.py @@ -9,7 +9,7 @@ because this example needs the complete trajectories of individually tracked markers. Reference: https://farside.ph.utexas.edu/teaching/plasma/lectures/node19.html -Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/guiding-center-orbits.py b/docs/src/examples/guiding-center-orbits.py index 803581f..1c8906a 100644 --- a/docs/src/examples/guiding-center-orbits.py +++ b/docs/src/examples/guiding-center-orbits.py @@ -9,7 +9,7 @@ Adapted from Struphy's particle-tracing tutorial (tutorials/tutorial_particle_tracing.ipynb). -Requires Struphy 3.2 with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/gvec-equilibrium.py b/docs/src/examples/gvec-equilibrium.py index d75f12b..8a7a5b1 100644 --- a/docs/src/examples/gvec-equilibrium.py +++ b/docs/src/examples/gvec-equilibrium.py @@ -12,8 +12,8 @@ magnetic wells: markers launched with a small parallel velocity are mirror-trapped in them, the faster ones circulate. -Requires Struphy with compiled kernels (`struphy compile`) and its optional physics dependencies -(`pip install -e ".[phys]"`), and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and its optional physics dependencies +(`pip install "struphy[phys]>=3.4.0"`), and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/gyromotion.py b/docs/src/examples/gyromotion.py index 80343f2..c1cb236 100644 --- a/docs/src/examples/gyromotion.py +++ b/docs/src/examples/gyromotion.py @@ -6,7 +6,7 @@ constants of the motion. Four markers with different perpendicular speeds are followed and compared with the exact helices: the gyroperiod is the same for all of them, the Larmor radius grows in proportion to v_perp. -Requires Struphy 3.3 with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/hall-mhd-waves.py b/docs/src/examples/hall-mhd-waves.py index cc29ab7..ca129be 100644 --- a/docs/src/examples/hall-mhd-waves.py +++ b/docs/src/examples/hall-mhd-waves.py @@ -8,7 +8,7 @@ the pressure are compared with the analytic Hall-MHD branches, and the phase velocities read off the spectrum with the exact ones. -Requires Struphy 3.3 with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/hasegawa-wakatani.py b/docs/src/examples/hasegawa-wakatani.py index b18f169..b4c5f59 100644 --- a/docs/src/examples/hasegawa-wakatani.py +++ b/docs/src/examples/hasegawa-wakatani.py @@ -9,7 +9,7 @@ it remains a manageable gallery calculation while retaining the turbulent eddies and the slow reorganization of their energy. -Requires Struphy with compiled kernels (``struphy compile``) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (``struphy compile``) and plasma-plots with Plotly (``pip install "plasma-plots[plotly]==0.1.1"``). Run as a script, it saves its figures in the current directory (``--show`` shows them first). """ diff --git a/docs/src/examples/hybrid-alfven-ion-coupling.py b/docs/src/examples/hybrid-alfven-ion-coupling.py index 901d5aa..59e1c99 100644 --- a/docs/src/examples/hybrid-alfven-ion-coupling.py +++ b/docs/src/examples/hybrid-alfven-ion-coupling.py @@ -7,7 +7,7 @@ conserves the total -- the defining property of a structure-preserving hybrid scheme. -Requires Struphy 3.2 with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/hybrid-current-coupling.py b/docs/src/examples/hybrid-current-coupling.py index ef95fcd..8c3c31a 100644 --- a/docs/src/examples/hybrid-current-coupling.py +++ b/docs/src/examples/hybrid-current-coupling.py @@ -11,7 +11,7 @@ channel. Finite marker sampling introduces noise; this is a coupling and conservation demonstration, not a measurement of a kinetic damping rate. -Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/incompressible-shear-relaxation.py b/docs/src/examples/incompressible-shear-relaxation.py index a3b8bcb..d295641 100644 --- a/docs/src/examples/incompressible-shear-relaxation.py +++ b/docs/src/examples/incompressible-shear-relaxation.py @@ -10,7 +10,7 @@ Based on the verification test of the model in Struphy (models/tests/verification/test_verif_IncompressibleNavierStokesSPH.py). -Requires Struphy 3.3 with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/itg-drift-wave.py b/docs/src/examples/itg-drift-wave.py index 297b2f4..cdb78f9 100644 --- a/docs/src/examples/itg-drift-wave.py +++ b/docs/src/examples/itg-drift-wave.py @@ -11,7 +11,7 @@ (examples/DriftKineticElectrostaticAdiabatic/itg_cylindre), at reduced resolution and run length to keep it a quick gallery run. -Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/itpa-tae-linear-mhd.py b/docs/src/examples/itpa-tae-linear-mhd.py index dd8dbed..fe0c44c 100644 --- a/docs/src/examples/itpa-tae-linear-mhd.py +++ b/docs/src/examples/itpa-tae-linear-mhd.py @@ -5,7 +5,7 @@ between the shear-Alfvén continua of the two harmonics. The measured frequencies are drawn against those continua and the gap-centre estimate, with the radial eigenfunction of each harmonic. -Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). diff --git a/docs/src/examples/itpa-tae-shear-alfven.py b/docs/src/examples/itpa-tae-shear-alfven.py index f1b1e91..97bdfd2 100644 --- a/docs/src/examples/itpa-tae-shear-alfven.py +++ b/docs/src/examples/itpa-tae-shear-alfven.py @@ -7,7 +7,7 @@ compressional coupling; compare with itpa-tae-linear-mhd.py. The measured frequencies are drawn against those continua and the gap-centre estimate, with the radial eigenfunction of each harmonic. -Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). diff --git a/docs/src/examples/langmuir-wave-dispersion.py b/docs/src/examples/langmuir-wave-dispersion.py index d92916b..2de9c95 100644 --- a/docs/src/examples/langmuir-wave-dispersion.py +++ b/docs/src/examples/langmuir-wave-dispersion.py @@ -10,7 +10,7 @@ frequency and the damping rate of the electric field, and compare them with this root. The fluid estimate omega^2 = 1 + 3 k^2 (Bohm-Gross) is shown for reference: it ignores the kinetic effects and misses the frequency by several per cent already at k = 0.5. -Requires Struphy 3.3 with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/linear-dissipative-alfven-wave.py b/docs/src/examples/linear-dissipative-alfven-wave.py index 957be95..7b612fe 100644 --- a/docs/src/examples/linear-dissipative-alfven-wave.py +++ b/docs/src/examples/linear-dissipative-alfven-wave.py @@ -6,7 +6,7 @@ The linear model dissipates the quadratic wave energy; heating is second order and is not part of this linear perturbation system. -Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/maxwell-cavity-resonances.py b/docs/src/examples/maxwell-cavity-resonances.py index fcb107a..38218f4 100644 --- a/docs/src/examples/maxwell-cavity-resonances.py +++ b/docs/src/examples/maxwell-cavity-resonances.py @@ -9,7 +9,7 @@ averaged over the box, then has one peak per resonance, and each peak is compared with the exact frequency. Because the box is not a square, the resonances that would coincide in a square box separate. -Requires Struphy 3.3 with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/maxwell-curved-mesh.py b/docs/src/examples/maxwell-curved-mesh.py index 14f7f4e..d4011cc 100644 --- a/docs/src/examples/maxwell-curved-mesh.py +++ b/docs/src/examples/maxwell-curved-mesh.py @@ -7,7 +7,7 @@ each oscillating at omega = c |k|, and it can be compared with the numerical field at every point of the distorted mesh. The energy, which the structure-preserving scheme conserves for any mesh, is followed as well. -Requires Struphy 3.3 with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/maxwell-structure-preservation.py b/docs/src/examples/maxwell-structure-preservation.py index 6c662a1..258b9fc 100644 --- a/docs/src/examples/maxwell-structure-preservation.py +++ b/docs/src/examples/maxwell-structure-preservation.py @@ -12,7 +12,7 @@ is set by the largest eigenvalue the grid supports, not by the wave being modelled, so a smooth initial condition does not save it: round-off seeds the unstable modes and they take over. -Requires Struphy 3.3 with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/maxwell-wave.py b/docs/src/examples/maxwell-wave.py index 16fc437..5fd49b0 100644 --- a/docs/src/examples/maxwell-wave.py +++ b/docs/src/examples/maxwell-wave.py @@ -4,7 +4,7 @@ test. It excites a broadband electric field, evolves Maxwell's equations with FEEC, and plots the numerical dispersion relation against omega = c k. -Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/mhd-slab-waves.py b/docs/src/examples/mhd-slab-waves.py index f7d4eff..e8e86da 100644 --- a/docs/src/examples/mhd-slab-waves.py +++ b/docs/src/examples/mhd-slab-waves.py @@ -7,7 +7,7 @@ Adapted from Struphy's tutorial (tutorials/tutorial_linear_mhd_slab_waves_1d.ipynb). -Requires Struphy 3.3 with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/ordinary-mode-dispersion.py b/docs/src/examples/ordinary-mode-dispersion.py index e20c8b6..4c8fc08 100644 --- a/docs/src/examples/ordinary-mode-dispersion.py +++ b/docs/src/examples/ordinary-mode-dispersion.py @@ -7,7 +7,7 @@ from an interface. The cutoff is the k -> 0 limit of the dispersion relation. Reference: https://farside.ph.utexas.edu/teaching/315/Waveshtml/node75.html -Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/orszag-tang-vortex.py b/docs/src/examples/orszag-tang-vortex.py index fdf0f3d..d681fcb 100644 --- a/docs/src/examples/orszag-tang-vortex.py +++ b/docs/src/examples/orszag-tang-vortex.py @@ -4,7 +4,7 @@ The example shows nonlinear compression and field deformation, with energy and discrete magnetic-divergence diagnostics. -Requires Struphy 3.3 with compiled kernels (``struphy compile``) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (``struphy compile``) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/poisson-convergence.py b/docs/src/examples/poisson-convergence.py index dc4b354..ec702ff 100644 --- a/docs/src/examples/poisson-convergence.py +++ b/docs/src/examples/poisson-convergence.py @@ -7,7 +7,7 @@ the curved one. The slopes are measured from the runs and compared with p + 1: on the straight mesh they are close to it, and on the curved mesh, whose coarse meshes are not yet in the asymptotic range, they are steeper. -Requires Struphy 3.3 with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/poisson-source.py b/docs/src/examples/poisson-source.py index cb994c5..7929923 100644 --- a/docs/src/examples/poisson-source.py +++ b/docs/src/examples/poisson-source.py @@ -5,7 +5,7 @@ recovers the potential at every step, compared here against the closed-form solution. -Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/pressureless-transport.py b/docs/src/examples/pressureless-transport.py index 639f9cd..4c79c11 100644 --- a/docs/src/examples/pressureless-transport.py +++ b/docs/src/examples/pressureless-transport.py @@ -4,7 +4,7 @@ unchanged. Here rho = 1 + A cos(x - U t) makes one circuit of a periodic box. This is an exact nonlinear solution, without characteristic crossing. -Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/resistive-diffusion.py b/docs/src/examples/resistive-diffusion.py index d537d73..fc4afdb 100644 --- a/docs/src/examples/resistive-diffusion.py +++ b/docs/src/examples/resistive-diffusion.py @@ -6,7 +6,7 @@ thermal energy (Ohmic heating) and keeps the total energy constant to the accuracy of its nonlinear solver. The field is weak, so that the pressure gradient it produces sets only a negligible flow. A scan over three resistivities confirms the rate. -Requires Struphy 3.3 with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/resistive-x-point.py b/docs/src/examples/resistive-x-point.py index 2b610e7..3b11a31 100644 --- a/docs/src/examples/resistive-x-point.py +++ b/docs/src/examples/resistive-x-point.py @@ -5,7 +5,7 @@ X-point. A small incompressible strain drives flux towards it and finite resistivity permits reconnection. -Requires Struphy with compiled kernels (``struphy compile``) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (``struphy compile``) and plasma-plots with Plotly (``pip install "plasma-plots[plotly]==0.1.1"``). Run as a script, it saves its figures in the current directory (``--show`` shows them first). """ diff --git a/docs/src/examples/shear-alfven-wave.py b/docs/src/examples/shear-alfven-wave.py index 08b1164..4164186 100644 --- a/docs/src/examples/shear-alfven-wave.py +++ b/docs/src/examples/shear-alfven-wave.py @@ -6,7 +6,7 @@ dispersion relation is compared against the exact Alfvén speed v_A = B0 / sqrt(n0) = 1. -Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/sph-velocity-diffusion.py b/docs/src/examples/sph-velocity-diffusion.py index f1b2bfb..dddedd3 100644 --- a/docs/src/examples/sph-velocity-diffusion.py +++ b/docs/src/examples/sph-velocity-diffusion.py @@ -5,7 +5,7 @@ then damps that mode at 4 mu k^2 / 3, with k = 2 pi / L. The binned SPH current makes a small, fast verification case with an analytic answer. -Requires Struphy 3.3 with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/strong-landau-damping.py b/docs/src/examples/strong-landau-damping.py index d95239b..65d0f7e 100644 --- a/docs/src/examples/strong-landau-damping.py +++ b/docs/src/examples/strong-landau-damping.py @@ -8,7 +8,7 @@ Adapted from Struphy's maintained example (examples/VlasovAmpereOneSpecies/strong_Landau_damping). -Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/toroidal-shear-alfven.py b/docs/src/examples/toroidal-shear-alfven.py index ad480c1..b156a5b 100644 --- a/docs/src/examples/toroidal-shear-alfven.py +++ b/docs/src/examples/toroidal-shear-alfven.py @@ -3,7 +3,7 @@ Run from the repository root with: .venv/bin/python cli.py run toroidal-shear-alfven -Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). The default is an exploratory local run, not a converged ITPA TAE benchmark. diff --git a/docs/src/examples/two-stream-instability.py b/docs/src/examples/two-stream-instability.py index 8addfa7..56b26c8 100644 --- a/docs/src/examples/two-stream-instability.py +++ b/docs/src/examples/two-stream-instability.py @@ -7,7 +7,7 @@ Adapted from Struphy's maintained example (examples/VlasovAmpereOneSpecies/two_stream). -Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/vlasov-tokamak.py b/docs/src/examples/vlasov-tokamak.py index 07ab386..4f8553b 100644 --- a/docs/src/examples/vlasov-tokamak.py +++ b/docs/src/examples/vlasov-tokamak.py @@ -7,7 +7,7 @@ field, so each particle should gyrate around a field line while it circulates (or bounces) through the torus, conserving its speed exactly. -Requires Struphy with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/vortex-merger.py b/docs/src/examples/vortex-merger.py index 464844a..8d3b40f 100644 --- a/docs/src/examples/vortex-merger.py +++ b/docs/src/examples/vortex-merger.py @@ -8,7 +8,7 @@ Follows the setup of Struphy's diocotron example (examples/ToyGyrokinetic/diocotron_instability): an annulus with grounded walls, a uniform background field, and the ToyDrift model. -Requires Struphy 3.2 with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/weak-landau-damping.py b/docs/src/examples/weak-landau-damping.py index 1c1fa38..ab49b0a 100644 --- a/docs/src/examples/weak-landau-damping.py +++ b/docs/src/examples/weak-landau-damping.py @@ -7,7 +7,7 @@ Adapted from Struphy's maintained example (examples/VlasovAmpereOneSpecies/weak_Landau_damping). -Requires Struphy 3.2 with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/weibel-instability.py b/docs/src/examples/weibel-instability.py index c0e34cc..d7265f0 100644 --- a/docs/src/examples/weibel-instability.py +++ b/docs/src/examples/weibel-instability.py @@ -9,7 +9,7 @@ (examples/VlasovMaxwellOneSpecies/weibel_instability), at reduced particle count and run length to keep it a quick gallery run. -Requires Struphy 3.2 with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/docs/src/examples/zeldovich-caustic.py b/docs/src/examples/zeldovich-caustic.py index 207f4d7..372bedd 100644 --- a/docs/src/examples/zeldovich-caustic.py +++ b/docs/src/examples/zeldovich-caustic.py @@ -10,7 +10,7 @@ The SPH density estimate is compared with it. This is the classic test of pressureless SPH, and the first stage of the Zel'dovich approximation for structure formation. -Requires Struphy 3.3 with compiled kernels (`struphy compile`) and plasma-plots with Plotly +Requires Struphy >=3.4.0 with compiled kernels (`struphy compile`) and plasma-plots with Plotly (`pip install "plasma-plots[plotly]==0.1.1"`). Run as a script, it saves its figures in the current directory (`--show` shows them first). """ diff --git a/pyproject.toml b/pyproject.toml index 7dee2bd..71c92ec 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,11 +1,10 @@ [tool.black] extend-exclude = ''' ( - ^/submodules/ - | ^/\.venv/ + ^/\.venv/ | ^/docs/ ) ''' [tool.isort] -extend_skip_glob = ["submodules/*", ".venv/*", "docs/*"] +extend_skip_glob = [".venv/*", "docs/*"] diff --git a/requirements-examples.txt b/requirements-examples.txt index 57ccd9a..e32e16c 100644 --- a/requirements-examples.txt +++ b/requirements-examples.txt @@ -1,3 +1,4 @@ -# Gallery rendering and profiling dependencies for running examples. +# Simulation, gallery rendering and profiling dependencies. -r requirements.txt +struphy[phys,mpi]>=3.4.0 plasma-plots[gallery]==0.1.1 diff --git a/requirements.txt b/requirements.txt index 445e55f..202949b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ -# Plotting dependency for metadata generation and site data. +# Python dependencies for metadata generation and site data. +struphy>=3.4.0 plasma-plots==0.1.1 diff --git a/scripts/generate_hero_stellarator.py b/scripts/generate_hero_stellarator.py index 0877d81..e598fd8 100644 --- a/scripts/generate_hero_stellarator.py +++ b/scripts/generate_hero_stellarator.py @@ -16,7 +16,7 @@ It also renders ``docs/public/images/stellarator-hero.webp``, the still shown until the viewer has loaded. All are small and committed, so the site build does not need GVEC. Rerun this script only when the hero -should change. It needs compiled Struphy kernels and ``pip install "./submodules/struphy[phys]" pyvista``. +should change. It needs compiled Struphy kernels and ``pip install "struphy[phys]>=3.4.0" pyvista``. python scripts/generate_hero_stellarator.py """ diff --git a/submodules/struphy b/submodules/struphy deleted file mode 160000 index e69fdef..0000000 --- a/submodules/struphy +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e69fdefe2d00a7a4eaf4a31ce7ea94775cab588f From 0b71e32e91f2f335ffca61172460a3bfd5d1e03d Mon Sep 17 00:00:00 2001 From: Max Lindqvist Date: Fri, 2 Oct 2026 11:51:20 +0200 Subject: [PATCH 12/16] Updated instructions --- docs/src/components/ExamplePage.astro | 51 +++++++++++++++++-- docs/src/examples/acoustic-pulse.py | 18 +++++-- docs/src/examples/alfven-standing-wave.py | 18 +++++-- docs/src/examples/beltrami-sph.py | 18 +++++-- docs/src/examples/bump-on-tail.py | 18 +++++-- docs/src/examples/coaxial-waveguide.py | 18 +++++-- docs/src/examples/cold-plasma-oscillation.py | 18 +++++-- docs/src/examples/cold-plasma-wave-packet.py | 18 +++++-- docs/src/examples/cold-plasma-waves.py | 18 +++++-- docs/src/examples/dam-break.py | 18 +++++-- docs/src/examples/damped-alfven-wave.py | 18 +++++-- docs/src/examples/diffusion-methods.py | 18 +++++-- docs/src/examples/diocotron-instability.py | 18 +++++-- docs/src/examples/faraday-rotation.py | 18 +++++-- docs/src/examples/gas-expansion.py | 18 +++++-- docs/src/examples/grad-b-drift.py | 18 +++++-- docs/src/examples/guiding-center-orbits.py | 18 +++++-- docs/src/examples/gvec-equilibrium.py | 19 +++++-- docs/src/examples/gyromotion.py | 18 +++++-- docs/src/examples/hall-mhd-waves.py | 18 +++++-- docs/src/examples/hasegawa-wakatani.py | 18 +++++-- .../examples/hybrid-alfven-ion-coupling.py | 18 +++++-- docs/src/examples/hybrid-current-coupling.py | 18 +++++-- .../incompressible-shear-relaxation.py | 18 +++++-- docs/src/examples/itg-drift-wave.py | 18 +++++-- docs/src/examples/itpa-tae-linear-mhd.py | 26 +++++++--- docs/src/examples/itpa-tae-shear-alfven.py | 26 +++++++--- docs/src/examples/langmuir-wave-dispersion.py | 18 +++++-- .../linear-dissipative-alfven-wave.py | 18 +++++-- .../src/examples/maxwell-cavity-resonances.py | 18 +++++-- docs/src/examples/maxwell-curved-mesh.py | 18 +++++-- .../maxwell-structure-preservation.py | 18 +++++-- docs/src/examples/maxwell-wave.py | 18 +++++-- docs/src/examples/mhd-slab-waves.py | 18 +++++-- docs/src/examples/ordinary-mode-dispersion.py | 18 +++++-- docs/src/examples/orszag-tang-vortex.py | 18 +++++-- docs/src/examples/poisson-convergence.py | 18 +++++-- docs/src/examples/poisson-source.py | 18 +++++-- docs/src/examples/pressureless-transport.py | 18 +++++-- docs/src/examples/resistive-diffusion.py | 18 +++++-- docs/src/examples/resistive-x-point.py | 18 +++++-- docs/src/examples/shear-alfven-wave.py | 18 +++++-- docs/src/examples/sph-velocity-diffusion.py | 18 +++++-- docs/src/examples/strong-landau-damping.py | 18 +++++-- docs/src/examples/toroidal-shear-alfven.py | 19 +++++-- docs/src/examples/two-stream-instability.py | 18 +++++-- docs/src/examples/vlasov-tokamak.py | 18 +++++-- docs/src/examples/vortex-merger.py | 18 +++++-- docs/src/examples/weak-landau-damping.py | 18 +++++-- docs/src/examples/weibel-instability.py | 18 +++++-- docs/src/examples/zeldovich-caustic.py | 18 +++++-- docs/src/pages/examples/[slug]/index.astro | 7 ++- 52 files changed, 809 insertions(+), 167 deletions(-) diff --git a/docs/src/components/ExamplePage.astro b/docs/src/components/ExamplePage.astro index b1e5f7b..506260c 100644 --- a/docs/src/components/ExamplePage.astro +++ b/docs/src/components/ExamplePage.astro @@ -6,7 +6,7 @@ import PlotlyFigure from './PlotlyFigure.astro'; import VtkDomainViewer from './VtkDomainViewer.astro'; import { toSlug } from '../lib/catalogue'; -const { slug, data, config, highlightedSource, equationsHtml, nextExample, exampleDomain, allExamples = [] } = Astro.props; +const { slug, data, config, highlightedSource, highlightedInstallation, equationsHtml, nextExample, exampleDomain, allExamples = [] } = Astro.props; const model = typeof data.model === 'string' ? data.model : data.model?.model || 'Struphy'; const domainName = typeof data.domain === 'string' ? data.domain.split(' ')[0] : ''; const hasProfiling = Boolean(data.profilingRegionStats); @@ -83,7 +83,33 @@ const hasProfiling = Boolean(data.profilingRegionStats);

Region statistics

RegionCountAvg (s)Min (s)Max (s)Total (s)
} -

Complete source

Simulation script

Download .py

Requires Struphy with compiled kernels and Plotly. Run struphy compile once, then execute python {slug}.py.

See the post-processing guide for more ways to inspect the result.

+
+
+

Complete source

Simulation script

+
+ + Download .py +
+
+
+
+

Run it locally

1. Install and compile

+
+
+

Use Python 3.10 or newer with Struphy's system prerequisites installed. Run these commands in your Python environment:

+
+

PNG exports need Chrome or Chromium. If Chrome is not installed, run kaleido_get_chrome.

+
+

2. Run the script

+
+
+

Download the .py file above, open a terminal in its directory, then run:

+
{`python ${slug}.py`}
+

Figures are saved in the current directory. Add --show to display them before saving.

+
+
+

See the post-processing guide for more ways to inspect the result.

+