Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .claude/agents/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Agent Team

This repo ships a small team of specialized Claude Code subagents in `.claude/agents/`. They are **stateless reviewers**: each runs in its own context, is handed a specific deliverable, and returns its findings to the main session — they do not talk to each other. **The main session is the integrator.** You drive them; you do not delegate the whole task and walk away.

**Invoke an agent by name** for the matching job — e.g. *"review this with the fortran-physics-reviewer"* or *"run the regression-guardian against develop"*. Do **not** "consult all the agents" reflexively: that burns the token budget and produces noise. Pick the agent whose job matches the change.

## Roster

| Agent | Model | Role — invoke when… |
|---|---|---|
| `fortran-physics-reviewer` | opus | A physics kernel, numerical method, derivative, integral, or quadrature was written/changed. Audits fidelity to the reference papers and the Fortran GPEC source. Carries project memory (correspondence map + per-domain audit checklists for KineticForces and InnerLayer), so its reviews compound — let it record findings. |
| `clean-code-reviewer` | opus | A logical chunk of code is ready and you want readability/maintainability review for a fusion physicist audience (naming, magic numbers, docstrings, structure). |
| `julia-performance-optimizer` | opus | A specific function/hotspot is slow or perf-sensitive (type stability, allocations, hot-loop work in ODE/kinetic/resistive-layer paths). |
| `fast-interpolations-optimizer` | opus | Code uses FastInterpolations.jl and you want allocation-free / optimal-search-type review. |
| `regression-guardian` | sonnet | **Before merging any substantive change** (mandatory per the Regression Harness policy — see `docs/development/regression-harness.md`), or when you need to know whether a tracked numerical quantity moved. Runs the harness, reports the table, flags non-OK rows, proposes new cases. |

## Recommended review pipeline for a substantive change

Run sequentially, reading each agent's findings before launching the next:

1. **`fortran-physics-reviewer`** — physics fidelity first; a fast-but-wrong result is worthless.
2. **`clean-code-reviewer`** — readability and maintainability.
3. **`julia-performance-optimizer`** and/or **`fast-interpolations-optimizer`** — only if the change is performance-relevant.
4. **`regression-guardian`** — always, last, before merge. Confirms the numbers didn't silently move.

Not every change needs all four. A docs-only change needs none; a pure perf refactor still needs the physics reviewer (to confirm no numerical change) and the regression-guardian.

## Budget

Every consultation is bounded — see **Subagent Consultations** in `/CLAUDE.md` (≤30 tool uses, ≤10 min, one concrete deliverable, never re-launch a runaway). The agent bodies now self-enforce this, but state the budget in your prompt anyway and always hand the agent the specific file paths to act on.
615 changes: 15 additions & 600 deletions CLAUDE.md

Large diffs are not rendered by default.

199 changes: 199 additions & 0 deletions docs/development/architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
# Architecture

## Computational Workflow

GPEC follows a three-stage analysis pipeline:

1. **Equilibrium** → Solve Grad-Shafranov equation, compute flux surfaces, safety factor q-profile
2. **Stability Analysis** → Solve ideal MHD eigenvalue problem (DCON-style), identify singular surfaces
3. **Perturbed Equilibrium** → Compute plasma response to external fields, analyze singular coupling and island formation

This workflow is reflected in the modular structure and data flow.

## Module Structure

GPEC consists of **seven main modules** organized in `src/`:

### Foundation Modules

1. **Splines** (`src/Splines/`) - Numerical interpolation library
- `CubicSpline.jl` - 1D cubic spline interpolation
- `BicubicSpline.jl` - 2D bicubic spline interpolation
- `FourierSpline.jl` - Fourier-based spline interpolation
- Status: Mature, pure Julia implementation

2. **Utilities** (`src/Utilities/`) - Shared computational tools
- `FourierTransforms.jl` - Efficient Fourier transform utilities with pre-computed basis functions
- Provides type-stable functor pattern for repeated transforms
- Used by Vacuum and PerturbedEquilibrium modules

### Core Physics Modules

3. **Equilibrium** (`src/Equilibrium/`) - MHD equilibrium solvers
- Main entry point: `setup_equilibrium(path)` or `setup_equilibrium(config)`
- Supports multiple equilibrium types:
- `efit` - EFIT g-file format
- `chease`, `chease2` - CHEASE equilibrium code formats
- `lar` - Large Aspect Ratio analytical model
- `sol` - Solovev analytical equilibrium
- Key files:
- `EquilibriumTypes.jl` - Core data structures
- `ReadEquilibrium.jl` - Parsing equilibrium files
- `DirectEquilibrium.jl` - Direct Grad-Shafranov solver
- `InverseEquilibrium.jl` - Inverse equilibrium solver
- `AnalyticEquilibrium.jl` - Analytical solutions
- Status: Stable and feature-complete

4. **Vacuum** (`src/Vacuum/`) - Vacuum field calculations and Green's functions
- Computes vacuum response matrices for ideal MHD analysis
- Calculates both **interior** (grri) and **exterior** (grre) Green's functions
- Main functions:
- `compute_vacuum_response()` - Pure Julia implementation
- Key files:
- `VacuumStructs.jl` - Data structures
- `VacuumInternals.jl` - Core algorithms
- `VacuumFromEquilibrium.jl` - Integration with equilibrium data
- Status: **Pure Julia implementation complete and available**

5. **ForceFreeStates** (`src/ForceFreeStates/`) - Ideal MHD stability analysis (DCON-style)
- Solves ideal MHD eigenvalue problem with force-free boundary conditions
- Identifies singular surfaces where ξ·∇ψ = 0
- Key files:
- `ForceFreeStatesStructs.jl` - Core data structures
- `Ode.jl` - ODE solver for Euler-Lagrange equations
- `Sing.jl` - Singular point handling and layer analysis
- `Fourfit.jl` - Fourier fitting routines
- `FixedBoundaryStability.jl` - Fixed boundary analysis
- `Free.jl` - Free boundary stability
- `Ballooning.jl` - Local stability scan: Mercier D_I, resistive interchange D_R, and high-n ballooning Δ' (s–α). Replaces the former standalone `Mercier.jl`.
- Status: Stable, core DCON functionality implemented

### Perturbed Equilibrium Modules

6. **ForcingTerms** (`src/ForcingTerms/`) - External field specification
- Handles external magnetic field perturbations (coils, RMP, etc.)
- Supports ASCII and HDF5 forcing data formats
- `ForcingMode` data structure specifies amplitude and phase for each (m,n) component
- Status: Complete and functional

7. **PerturbedEquilibrium** (`src/PerturbedEquilibrium/`) - **GPEC-style plasma response**
- Computes plasma response to external forcing
- Calculates singular coupling metrics at rational surfaces
- Key files:
- `PerturbedEquilibrium.jl` - Main entry point
- `PerturbedEquilibriumStructs.jl` - Data structures
- `ResponseMatrices.jl` - Permeability matrix calculation
- `FieldReconstruction.jl` - Mode-space field reconstruction
- `Response.jl` - Plasma response computation
- `SingularCoupling.jl` - **Singular surface analysis** including:
- Delta prime (Δ') tearing stability parameter
- Resonant flux and currents at rational surfaces
- Island half-widths and Chirikov parameters
- Green's functions at interior flux surfaces
- Surface inductance for singular surfaces
- `Utils.jl` - Helper functions
- Status: Core plasma response and singular coupling calculations implemented; active area of development

## Configuration

**Unified Configuration File**: `gpec.toml`

All GPEC modules are configured via a single TOML file with the following sections:

- `[Equilibrium]` - Equilibrium solver settings
- `[Wall]` - Wall geometry and vacuum region
- `[ForceFreeStates]` - Stability analysis parameters
- `[PerturbedEquilibrium]` - Perturbed equilibrium settings
- `[ForcingTerms]` - External field specification

Key parameters:
- `force_termination` - Set to `true` to exit after equilibrium/stability (skip perturbed equilibrium)
- `output_file` - Output filename (default: `gpec.h5`)

Example configuration files are provided in:
- `examples/Solovev_ideal_example/gpec.toml`
- `examples/DIIID-like_ideal_example/gpec.toml`

**Note**: Legacy configuration files (`equil.toml`, `vac.in`) are deprecated.

## Data Flow

The complete GPEC analysis pipeline:

1. **Equilibrium Setup**:
- `setup_equilibrium(config)` reads configuration from `gpec.toml`
- Parses equilibrium data (EFIT, CHEASE, or analytical)
- Runs Grad-Shafranov solver (direct or inverse)
- Computes global parameters: q-profile, pressure, current density, β
- Creates bicubic splines for (ψ, θ, φ) → (R, Z, Φ) mapping
- Outputs: `PlasmaEquilibrium` object

2. **Vacuum Response**:
- Initialize plasma and wall surfaces from equilibrium
- Compute vacuum response matrices (wv, grri, grre)
- Calculate both interior and exterior Green's functions
- Pure Julia implementation

3. **Stability Analysis** (ForceFreeStates):
- Solve ideal MHD Euler-Lagrange equations via ODE integration
- Identify singular surfaces where q = m/n
- Compute Δ' at each singular surface
- Calculate potential and kinetic energies
- Check Mercier and ballooning stability criteria
- Outputs: Eigenmode structure ξ(ψ,θ)

4. **Perturbed Equilibrium** (GPEC-style):
- Load external forcing data (coil fields, RMP configuration)
- Compute plasma response using permeability matrices
- Reconstruct mode-space fields (ξ_modes, b_modes)
- Calculate singular coupling metrics at rational surfaces:
- Δ' (tearing stability parameter)
- Island half-widths
- Chirikov overlap parameter
- Resonant flux and currents
- Outputs: `PerturbedEquilibriumState` with response fields and diagnostics

5. **Output**:
- All results saved to single HDF5 file (default: `gpec.h5`)
- HDF5 groups: `input/`, `info/`, `equil/`, `splines/`, `locstab/`, `integration/`, `singular/`, `vacuum/`, and perturbed equilibrium data

## Key Data Structures

### Equilibrium
- `PlasmaEquilibrium` - Main equilibrium container with bicubic splines (rzphi), 1D profiles (sq), and global parameters
- `EquilibriumConfig` - Configuration loaded from TOML files

### Vacuum
- `VacuumInput` - Input parameters for vacuum calculations
- `WallShapeSettings` - Wall geometry configuration

### Stability
- `SingType` - Singular surface data including:
- Rational surface location (ψ, q = m/n)
- Δ' (tearing stability parameter)
- Eigenmode structure at singular surface
- Green's functions (grri, grre) at interior singular surfaces
- Surface inductance

### Perturbed Equilibrium
- `PerturbedEquilibriumControl` - User-facing TOML configuration parameters
- `PerturbedEquilibriumInternal` - Internal state with mode arrays
- `PerturbedEquilibriumState` - Results including:
- Response fields (ξ_modes, b_modes) in mode space
- Singular coupling matrices [msing × numpert_total]
- Island diagnostics (half-widths, Chirikov parameters)
- `ForcingMode` - External forcing specification (m, n, amplitude, phase)

## Module Dependencies

```
GeneralizedPerturbedEquilibrium
├── Splines (foundation)
├── Utilities (shared tools)
│ └── FourierTransforms
├── Equilibrium (uses Splines)
├── Vacuum (uses Splines, Equilibrium, Utilities)
├── ForcingTerms (data I/O)
├── ForceFreeStates (uses Equilibrium, Vacuum, Splines)
└── PerturbedEquilibrium (uses ForceFreeStates, Vacuum, ForcingTerms, Utilities)
```
51 changes: 51 additions & 0 deletions docs/development/benchmarking.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Benchmarking

Use the generic benchmarking tool at `benchmarks/benchmark_git_branches.jl` to compare performance between branches or commits.

**Tool usage:**

```bash
# Compare feature branch against develop
julia benchmarks/benchmark_git_branches.jl \
--example examples/DIIID-like_ideal_example \
--branch1 develop \
--branch2 feature-branch

# Compare specific commits
julia benchmarks/benchmark_git_branches.jl \
--example examples/DIIID-like_ideal_example \
--commit1 abc123 \
--commit2 def456

# Compare current develop vs develop from 1 month ago
julia benchmarks/benchmark_git_branches.jl \
--example examples/DIIID-like_ideal_example \
--branch1 develop \
--commit1 HEAD~10 \
--branch2 develop
```

**Default benchmark case:** `examples/DIIID-like_ideal_example`

**Reported metrics:**
1. **Eigenmode energy (`et[1]`)** - First eigenvalue; verifies calculation correctness
2. **Integration steps** - Total ODE solver steps
3. **Runtime (warmed)** - Wall-clock time averaged over multiple warm runs (JIT warmup handled automatically)
4. **Commit hash** - Git commit of code tested

**The tool automatically:**
- Handles JIT warmup (runs example 3 times, averages last 2)
- Switches between branches/commits
- Stashes uncommitted changes if necessary
- Restores original branch when done
- Reports comparison with percentage differences

**Important notes:**
- Working directory should be clean or changes will be stashed during branch switching
- Tool requires HDF5.jl for reading `gpec.h5` output
- Each benchmark run takes several minutes per branch (includes compilation + warm runs)

**Benchmark script conventions:**
- Benchmark scripts must reference input data from `examples/` (e.g., `joinpath(@__DIR__, "..", "examples", "DIIID-like_ideal_example")`). Never duplicate example inputs into `benchmarks/`.
- If a benchmark needs modified TOML settings or a parameter scan, copy inputs to a temporary local directory at runtime — do not commit these copies.
- All outputs (figures, CSVs, HDF5 files) must be saved into `benchmarks/` itself (or a self-described subdirectory within it, e.g., `benchmarks/coil_scan_results/`). Output files are not committed.
75 changes: 75 additions & 0 deletions docs/development/git-workflow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Git Workflow

This project uses GitFlow (http://nvie.com/posts/a-successful-git-branching-model):

- Two permanent branches: `main` and `develop`
- `main` is updated only at release-ready stages via pull request from `develop`
- `develop` is the integration branch — all feature branches merge here

**IMPORTANT**: All development must be done on feature branches. No commits should be made directly to `develop` or `main`. Always create a branch from `develop`, do all work there, and open a pull request back into `develop`.

## Branch Naming

Branches use a typed prefix and a lowercase hyphen-separated description:

| Prefix | Purpose | Branches from | Merges into |
|---|---|---|---|
| `feature/` | New functionality | `develop` | `develop` |
| `bugfix/` | Non-critical bug fixes | `develop` | `develop` |
| `hotfix/` | Critical production fix | `main` | `main` + `develop` |
| `performance/` | Performance improvements | `develop` | `develop` |
| `refactor/` | Refactoring without behavior change | `develop` | `develop` |
| `docs/` | Documentation only | `develop` | `develop` |
| `test/` | Test additions/improvements | `develop` | `develop` |
| `experiment/` | Exploratory work, may not merge | `develop` | — |

Examples: `bugfix/sing-lim-bounds-error`, `feature/kinetic-damping`, `performance/green-function-prefactor`

Author-named branches (e.g. `jmh/`, `nlogan/`) are not used — git history already records authorship on every commit.

## Hotfix Workflow

Hotfixes address critical bugs in production (`main`) that cannot wait for the next release cycle:

1. Branch `hotfix/description` from the current tagged `main` commit
2. Fix the bug with one or more commits
3. Merge into `main` via pull request; tag the merge commit with a new patch version (e.g. `v0.1.1`)
4. Merge the same branch into `develop` so the fix is not lost in the next release

## Versioning

This project uses semantic versioning: `v{major}.{minor}.{patch}`

- **major**: breaking API or file-format changes
- **minor**: new features, backward-compatible
- **patch**: bug fixes (typically via hotfix branches)

Tags are applied to merge commits on `main`.

## Commit Message Format

```
CODE - TAG - Detailed message
```

Where:
- **CODE**: Module name (EQUIL, VAC, VACUUM, ForceFreeStates, PERTURBED EQUILIBRIUM, etc.)
- **TAG**: Type descriptor (WIP, MINOR, IMPROVEMENT, BUG FIX, NEW FEATURE, REFACTOR, CLEANUP, etc.)

Examples:
- `PERTURBED EQUILIBRIUM - NEW FEATURE - Implement singular coupling diagnostics`
- `VAC - IMPROVEMENT - Add dual Green's function computation`
- `EQUIL - BUG FIX - Fixed separatrix finding for high kappa`
- `ForceFreeStates - REFACTOR - Unified singular surface data structure`

This format is used for compiling release notes, so tags should be human-readable and descriptive.

## Merge Conflict Resolution Policy

- When resolving git conflicts, do not simply accept one side.
- Analyze what each side changed and WHY before producing a resolution.
- Produce a merged version incorporating both sets of changes.
- If both sides renamed the same symbol differently, prefer the current (ours) branch convention.
- When a rename on one side conflicts with a logic change on the other, apply the logic change using the renamed symbol.
- If a conflict involves changes to numerical parameters (tolerances, boundary conditions, grid sizes), flag for human review rather than guessing.
- Flag any conflicts where the combination is ambiguous for human review.
21 changes: 21 additions & 0 deletions docs/development/plotting.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Plotting Conventions

## Spectrum plots

Any plot with discrete mode numbers m or n on the x-axis must use `seriestype=:steppre` with a `step_series` helper that pads zeros on both ends. Pattern from `benchmarks/benchmark_coil_ForcingTerms_against_fortran.jl`:

```julia
function step_series(m_vals, amps)
m_ext = [m_vals[1] - 1; m_vals; m_vals[end] + 1]
amp_ext = [0.0; amps; 0.0]
return m_ext, amp_ext
end
# Usage:
m_ext, a_ext = step_series(m_vals, amplitudes)
plot!(p, m_ext, a_ext; seriestype=:steppre, lw=2, label="...")
```

## Figures and plots

- Always print the full absolute path of any figure or plot file you save, so the user can open it directly without searching the filesystem.
- Always check that axis labels are not clipped. In Plots.jl there is no `tight_layout()` equivalent; use explicit margins instead: `left_margin=12Plots.mm`, `bottom_margin=4Plots.mm`, etc. When in doubt, add a generous `left_margin` to prevent y-axis label cutoff.
Loading
Loading