API Reference

This section provides detailed documentation for the StellCoilBench Python API. The API is organized into several modules, each handling a specific aspect of the benchmarking framework.

Module Overview

  • ``stellcoilbench.cli``: Command-line interface implementation

  • ``stellcoilbench.coil_optimization``: Core coil optimization logic

  • ``stellcoilbench.config_scheme``: Configuration data structures

  • ``stellcoilbench.evaluate``: Data structure for aggregated results (SubmissionResults)

  • ``stellcoilbench.submission_packaging``: Builds submission dirs, zips, and results.json from optimization output

  • ``stellcoilbench.update_db``: Scans submissions, aggregates metrics, generates leaderboards (RST/MD/JSON)

  • ``stellcoilbench.validate_config``: Configuration validation

  • ``stellcoilbench.post_processing``: VMEC, Poincaré, QFM, Boozer plots, SIMPLE, finite-build VTK

  • ``stellcoilbench.sensitivity``: Coil sensitivity analysis via stochastic perturbation

  • ``stellcoilbench.path_utils``: Path resolution, surface/case lookup, YAML load/dump

  • ``stellcoilbench.finite_build``: Finite-build coil geometry (rectangular cross-section swept along centerline)

  • ``stellcoilbench.structural_analysis``: FEM structural analysis (DOLFINx / scikit-fem)

Configuration Module

Configuration dataclasses for StellCoilBench case definitions.

This module defines the schema for case YAML files and submission metadata, providing typed structures for validation and runtime use.

class stellcoilbench.config_scheme.CaseConfig(description: str, surface_params: SurfaceParams, coils_params: CoilsParams, optimizer_params: OptimizerParams, scoring: dict[str, Any] | None = None, coil_objective_terms: dict[str, Any] | None = None, fourier_continuation: dict[str, Any] | None = None, post_processing_params: dict[str, Any] | None = None)

Bases: object

Configuration for a single benchmark case, usually parsed from case.yaml.

description

Human-readable description of the benchmark case.

Type:

str

surface_params

Plasma surface parameters (e.g., surface filename, range, virtual_casing).

Type:

SurfaceParams

coils_params

Coil configuration (ncoils, order, coil_type, etc.).

Type:

CoilsParams

optimizer_params

Optimizer settings (max_iterations, algorithm, tolerances).

Type:

OptimizerParams

coil_objective_terms

Optional coil regularization terms (length, curvature, distances).

Type:

dict[str, Any] | None

fourier_continuation

Optional Fourier continuation orders for progressive refinement.

Type:

dict[str, Any] | None

post_processing_params

Optional post-processing options (VMEC, Poincaré, etc.).

Type:

dict[str, Any] | None

coil_objective_terms: dict[str, Any] | None = None
coils_params: CoilsParams
description: str
fourier_continuation: dict[str, Any] | None = None
classmethod from_dict(data: dict[str, Any]) CaseConfig

Construct a CaseConfig from a parsed YAML/JSON dictionary.

Parameters:

data (dict[str, Any]) – Raw configuration dict (e.g., from yaml.safe_load).

Returns:

Validated configuration instance.

Return type:

CaseConfig

optimizer_params: OptimizerParams
post_processing_params: dict[str, Any] | None = None
scoring: dict[str, Any] | None = None
surface_params: SurfaceParams
class stellcoilbench.config_scheme.CoilsParams

Bases: TypedDict

Typed dictionary for coil parameter configuration.

coil_type: str
coil_width: float
inboard_radius: float
ncoils: int
order: int
target_B: float
vv_extension: float
class stellcoilbench.config_scheme.OptimizerParams

Bases: TypedDict

Typed dictionary for optimizer parameter configuration.

algorithm: str
algorithm_options: dict
max_iter_subopt: int
max_iterations: int
verbose: bool
class stellcoilbench.config_scheme.PostProcessingConfig(run_vmec: bool = False, helicity_m: int = 1, helicity_n: int = 0, ns: int = 50, plot_boozer: bool = True, plot_poincare: bool = True, nfieldlines: int = 20, run_simple: bool = False, simple_executable_path: Path | None = None, run_vmec_original: bool = False, plot_finite_build: bool = False, finite_build_width: float | None = None, finite_build_height: float | None = None, run_structural: bool = False, export_structural_full_coil_set: bool = False, structural_E: float | None = None, structural_nu: float | None = None, compute_shape_gradient: bool = False)

Bases: object

Configuration for the post-processing pipeline.

Groups the parameters accepted by run_post_processing into a structured object for cleaner function signatures and easier forwarding from higher-level orchestration code.

run_vmec

Whether to run VMEC equilibrium reconstruction.

Type:

bool

helicity_m

Poloidal helicity index for quasisymmetry evaluation.

Type:

int

helicity_n

Toroidal helicity index for quasisymmetry evaluation.

Type:

int

ns

Number of VMEC radial surfaces.

Type:

int

plot_boozer

Whether to generate Boozer-surface plots.

Type:

bool

plot_poincare

Whether to generate Poincaré section plots.

Type:

bool

nfieldlines

Number of field lines for Poincaré tracing.

Type:

int

run_simple

Whether to run SIMPLE particle tracing.

Type:

bool

simple_executable_path

Path to the SIMPLE executable (None uses system default).

Type:

Optional[Path]

run_vmec_original

Whether to run VMEC on the original (pre-optimization) coils.

Type:

bool

plot_finite_build

Whether to generate finite-build cross-section plots.

Type:

bool

finite_build_width

Winding-pack width for finite-build visualization (metres).

Type:

Optional[float]

finite_build_height

Winding-pack height for finite-build visualization (metres).

Type:

Optional[float]

run_structural

Whether to run FEM structural (linear-elasticity) analysis on finite-build coil geometry. Requires DOLFINx and a tetrahedral mesh (Gmsh).

Type:

bool

export_structural_full_coil_set

When True and run_structural is True, also export structural_results_full.vtk with the full coil set (unique coils plus toroidal/stellarator symmetry copies).

Type:

bool

structural_E

Young’s modulus [Pa] for the winding-pack material. None uses the default from constants.WP_YOUNGS_MODULUS_PA.

Type:

Optional[float]

structural_nu

Poisson ratio for the winding-pack material. None uses the default from constants.WP_POISSON_RATIO.

Type:

Optional[float]

compute_shape_gradient

Whether to compute per-coil shape gradients and save to VTK.

Type:

bool

compute_shape_gradient: bool = False
export_structural_full_coil_set: bool = False
finite_build_height: float | None = None
finite_build_width: float | None = None
classmethod from_case_config(case_cfg: CaseConfig, **overrides: Any) PostProcessingConfig

Create from a CaseConfig’s post_processing_params, with optional overrides.

Parameters:
  • case_cfg (CaseConfig) – The case configuration whose post_processing_params dict supplies default values.

  • **overrides – Any keyword argument accepted by PostProcessingConfig; these take precedence over values in post_processing_params.

Returns:

A fully-resolved configuration instance.

Return type:

PostProcessingConfig

classmethod from_cli_options(**kwargs: Any) PostProcessingConfig

Create from raw Typer CLI option values.

Accepts keyword arguments matching PostProcessingConfig field names; unknown keys are ignored. Use after apply_all_post_processing_flags when building config from submit-case or post-process CLI.

Parameters:

**kwargs – CLI option values (e.g. run_vmec, run_simple, plot_poincare, finite_build_width, structural_E). Only keys that match PostProcessingConfig fields are used.

Returns:

A configuration instance with provided values; defaults for omitted keys.

Return type:

PostProcessingConfig

helicity_m: int = 1
helicity_n: int = 0
nfieldlines: int = 20
ns: int = 50
plot_boozer: bool = True
plot_finite_build: bool = False
plot_poincare: bool = True
run_simple: bool = False
run_structural: bool = False
run_vmec: bool = False
run_vmec_original: bool = False
simple_executable_path: Path | None = None
structural_E: float | None = None
structural_nu: float | None = None
to_run_post_processing_kwargs(*, helicity_n: int | None = None, ns: int | None = None, **extra: Any) dict[str, Any]

Build kwargs dict for run_post_processing.

Parameters:
  • helicity_n (int | None) – Override helicity_n (e.g. from case surface detection). Uses self.helicity_n if None.

  • ns (int | None) – Override VMEC radial surfaces. Uses self.ns if None.

  • **extra (Any) – Additional kwargs (coils_json_path, output_dir, case_yaml_path, plasma_surfaces_dir, mpi) to merge into the result.

Returns:

Kwargs suitable for run_post_processing.

Return type:

dict[str, Any]

class stellcoilbench.config_scheme.SubmissionMetadata(method_version: str, contact: str, hardware: str)

Bases: object

Descriptive information about a submission or method implementation.

method_version

Version string for reproducibility.

Type:

str

contact

Contact identifier (e.g., GitHub username, email).

Type:

str

hardware

Hardware description (e.g., CPU model, GPU type).

Type:

str

contact: str
hardware: str
method_version: str
class stellcoilbench.config_scheme.SurfaceParams

Bases: TypedDict

Typed dictionary for surface parameter configuration.

range: str
surface: str
virtual_casing: bool

The config_scheme module defines data structures for case configurations and submission metadata.

CaseConfig

Represents a complete case configuration loaded from a YAML file. Contains:

  • description: Case description

  • surface_params: Plasma surface configuration

  • coils_params: Coil geometry parameters

  • optimizer_params: Optimization algorithm settings

  • coil_objective_terms: Objective function terms

Methods:

  • from_dict(data: Dict[str, Any]) -> CaseConfig: Create from dictionary

SubmissionMetadata

Metadata for submissions, including method information, contact details, hardware information, and timestamps.

Case Loader Module

Case loading and path resolution for StellCoilBench.

Provides a single entry point for loading, validating, and resolving case.yaml configurations across the codebase.

stellcoilbench.case_loader.load_case(path: Path | str, *, validate: bool = True) CaseConfig

Load a case configuration from a file path or directory.

Accepts either a path to case.yaml or a directory containing case.yaml. Uses path_utils.load_yaml, validate_config.validate_case_config, and CaseConfig.from_dict as a single entry point for load+validate+CaseConfig.

Parameters:
  • path (Path | str) – Path to case.yaml file, or directory containing case.yaml.

  • validate (bool, default=True) – If True, run validate_case_config before constructing CaseConfig. If False, skip validation (not recommended for untrusted input).

Returns:

Parsed and validated case configuration.

Return type:

CaseConfig

Raises:
  • FileNotFoundError – If case.yaml is not found at the expected location.

  • ValueError – If validation fails (when validate=True).

Use stellcoilbench.case_loader.load_case() to load validated case configurations from a YAML file or directory containing case.yaml.

load_case(path: Path | str, *, validate: bool = True) -> CaseConfig

Load and validate a case configuration.

Parameters:

  • path: Path to case.yaml file or directory containing case.yaml

  • validate: If True (default), run validation before constructing CaseConfig

Returns:

  • CaseConfig: Validated configuration

Raises:

  • FileNotFoundError: If case.yaml not found (includes searched paths and suggested next steps)

  • ValueError: If configuration validation fails

Evaluation Module

Evaluation utilities for StellCoilBench.

This module provides dataclasses for evaluation results. Case loading is handled by stellcoilbench.case_loader.

class stellcoilbench.evaluate.SubmissionResults(metadata: SubmissionMetadata, metrics: Dict[str, Any])

Bases: object

Aggregated results for a single submission.

metadata

Method name, version, contact, hardware.

Type:

SubmissionMetadata

metrics

Evaluation metrics (scores, coil metrics, reactor-scale quantities).

Type:

dict[str, Any]

metadata: SubmissionMetadata
metrics: Dict[str, Any]

The evaluate module provides SubmissionResults for aggregated evaluation metrics. Use stellcoilbench.case_loader.load_case() for case loading.

Coil Optimization Module

The coil_optimization module contains the core optimization logic. Key functions: optimize_coils (main entry point), initialize_coils_loop, optimize_coils_loop, optimize_coils_with_fourier_continuation. LinearPenalty provides threshold-based penalty terms. See the automodule output above for full signatures and docstrings.

save_coils_config(coils: List, config_path: Path) -> None

Save coil configuration to JSON file.

Parameters:

  • coils: List of coil objects

  • config_path: Path to save coils.json

coils_to_vtk(coils: List, filename: Path) -> None

Export coils to VTK format for visualization.

Parameters:

  • coils: List of coil objects

  • filename: Output VTK file path

plot_bn_error_3d(surface, bs, coils, out_dir: Path, filename: str = “bn_error_3d_plot.pdf”, title: str = “B_N/|B| Error on Plasma Surface with Optimized Coils”, plot_upsample: int = 3) -> None

Create 3D visualization of B_N error on plasma surface.

Generates a high-resolution PDF plot showing:

  • Plasma surface colored by \(B_N/|B|\) error magnitude

  • Coils colored by current magnitude

  • Colorbars for both

Parameters:

  • surface: Plasma surface object

  • bs: BiotSavart field calculator

  • coils: List of coil objects

  • out_dir: Output directory

  • filename: Output PDF filename

  • title: Plot title

  • plot_upsample: Surface upsampling factor for higher resolution

Update Database Module

Leaderboard generation and database update for StellCoilBench.

Scans submissions/ for zip files containing results.json, aggregates metrics, and generates per-surface leaderboards in RST, Markdown, and JSON under docs/leaderboards/. Handles reactor-scale constraints, composite scoring, and submission formatting.

stellcoilbench.update_db.build_leaderboard_json(methods: Dict[str, Any]) Dict[str, Any]

Build a leaderboard summary from methods.json-style data.

Ranking uses the composite score (higher is better). Entries that fail hard reactor-scale constraints receive composite_score=0 and are moved to excluded_entries. Entries without any usable score are filtered out.

Parameters:

methods (dict[str, Any]) – Per-method data from build_methods_json().

Returns:

Keys: entries (ranked list), excluded_entries (failed constraints). Each entry has rank, composite_score, metrics, etc.

Return type:

dict[str, Any]

stellcoilbench.update_db.build_methods_json(submissions_root: Path, repo_root: Path) Dict[str, Any]

Build the per-method summary dictionary from submissions.

Scans submissions, normalizes metrics, recomputes coils_linked_to_surface, checks reactor-scale constraints, and computes composite scores.

Parameters:
  • submissions_root (Path) – Root directory containing submission results.json files.

  • repo_root (Path) – Repository root for resolving plasma surfaces and relative paths.

Returns:

Keys are "contact:surface:user:version"; values hold metadata, metrics, reactor_scale_metrics, composite_score, passes_constraints.

Return type:

dict[str, Any]

stellcoilbench.update_db.build_surface_leaderboards(leaderboard: Dict[str, Any], submissions_root: Path, plasma_surfaces_dir: Path) Dict[str, Dict[str, Any]]

Group entries by plasma surface extracted from case.yaml or path structure.

Expects path structure: submissions/surface/user/timestamp/results.json or all_files.zip. Entries within each surface are ranked by composite_score.

Parameters:
  • leaderboard (dict) – Leaderboard with entries list.

  • submissions_root (Path) – Root of submissions directory (for path parsing).

  • plasma_surfaces_dir (Path) – Unused; kept for API compatibility.

Returns:

Mapping surface_name -> {"entries": [...]} with ranked entries.

Return type:

dict[str, dict]

stellcoilbench.update_db.check_reactor_constraints(metrics: Dict[str, Any], reactor_scale_metrics: Dict[str, Any]) Tuple[bool, List[Dict[str, Any]]]

Check whether a submission meets all reactor-scale engineering constraints.

Evaluates each constraint in REACTOR_SCALE_CONSTRAINTS against the provided metrics. Hard violations indicate infeasibility; soft violations are recorded but do not affect the pass/fail flag.

Parameters:
  • metrics (dict) – Device-scale metrics (e.g. coils_linked_to_surface, final_linking_number).

  • reactor_scale_metrics (dict) – Reactor-scale metrics (e.g. separations, length, curvature, turns).

Returns:

  • passes_hard (bool) – True if no hard constraints are violated.

  • violations (list of dict) – All violated constraints, each with label, metric, value, bound, direction, units, and hard.

stellcoilbench.update_db.compute_composite_score(metrics: Dict[str, Any], reactor_scale_metrics: Dict[str, Any]) Tuple[Any, Dict[str, Any]]

Compute a composite feasibility/quality score.

Combines soft constraints via a geometric mean of exponential margin factors. Hard constraint violations return 0.0 immediately.

Score interpretation: - 0.0: Hard infeasibility (e.g. coils delinked, interlinked). - < 1: One or more soft constraints violated on average. - 1.0: All constraints met exactly. - > 1: Constraints met with engineering margin.

Parameters:
  • metrics (dict) – Device-scale metrics.

  • reactor_scale_metrics (dict) – Reactor-scale metrics.

Returns:

  • score (float | None) – Composite score; 0.0 for infeasible, > 0 for feasible, None when no soft-constraint metrics are available.

  • details (dict) – Diagnostic info: factors (per-constraint margins), infeasible, reason, n_factors, mean_margin.

stellcoilbench.update_db.normalize_submission_metrics(data: Dict[str, Any]) Dict[str, Any]

Normalize submission data to canonical structure.

Ensures consistent layout for leaderboard processing: - Maps final_normalized_squared_fluxfinal_squared_flux. - Promotes top-level metric keys into a metrics dict when missing. - Preserves metadata and ensures it exists.

Parameters:

data (dict) – Raw submission data (from results.json or zip contents).

Returns:

Copy of data with normalized metrics and metadata keys.

Return type:

dict

stellcoilbench.update_db.parse_submission_path(path: Path, submissions_root: Path) Dict[str, Any]

Parse a submission path into surface, user, timestamp, and version.

Extracts structured metadata from paths under the submissions directory. Supports both directory-based layouts (results.json in timestamp dir) and zip files (all_files.zip or timestamp-named zips).

Parameters:
  • path (Path) – Path to a submission file, e.g. submissions/surface/user/timestamp/results.json or submissions/surface/user/12-01-2025_01-51.zip.

  • submissions_root (Path) – Root directory containing submissions (e.g. repo_root / "submissions").

Returns:

Keys: surface, user, timestamp, version, parts. Uses "unknown" for surface/user when path structure is incomplete. parts is the list of path components after stripping submissions/.

Return type:

dict

Examples

>>> parse_submission_path(Path("submissions/QA/user1/run1/results.json"), Path("submissions"))
{'surface': 'QA', 'user': 'user1', 'timestamp': 'run1', 'version': 'run1', ...}
stellcoilbench.update_db.update_database(repo_root: Path, submissions_root: Path | None = None, docs_dir: Path | None = None, cases_root: Path | None = None, plasma_surfaces_dir: Path | None = None, *, use_local_viz_links: bool = False) dict

High-level entry point to rebuild the leaderboard.

It does several things:
  1. Scans submissions_root for results.json files

  2. Aggregates data from submissions (in-memory)

  3. Writes docs/leaderboards/ (per-surface leaderboards)

  4. Writes docs/leaderboard.json for reference

Parameters:
  • repo_root – Root of the git repo (e.g. Path.cwd() when called from repo root).

  • submissions_root – Directory containing per-method submissions. Defaults to repo_root / “submissions”.

  • docs_dir – Directory where docs/leaderboards/ leaderboards and leaderboard.json are written. Defaults to repo_root / “docs”.

  • cases_root – Directory containing case.yaml files. Defaults to repo_root / “cases”.

  • plasma_surfaces_dir – Directory containing plasma surface files. Defaults to repo_root / “plasma_surfaces”.

  • use_local_viz_links (bool, optional) – If True, leaderboard PDF links use relative paths (e.g. ../../../../submissions/…/file.pdf) instead of jsDelivr CDN URLs. Use for local docs so PDFs open from disk and avoid the CDN 50 MB limit.

Returns:

Summary with submissions_count, surfaces_updated, and errors.

Return type:

dict

Raises:

RuntimeError – If leaderboard.json could not be written.

stellcoilbench.update_db.write_markdown_leaderboard(leaderboard: Dict[str, Any], out_md: Path) None

Write a markdown leaderboard table to out_md.

Parameters:
  • leaderboard (dict) – Leaderboard with entries list (each entry has rank, metrics, etc.).

  • out_md (Path) – Output path for the Markdown file.

stellcoilbench.update_db.write_reactor_scale_leaderboard(leaderboard: Dict[str, Any], surface_leaderboards: Dict[str, Dict[str, Any]], out_rst: Path, repo_root: Path | None = None, *, use_local_links: bool = False) None

Write a reactor-scale leaderboard RST file with per-surface tables.

Shows reactor-scale engineering metrics (MN/m forces, curvatures in 1/m) alongside composite score and constraint status. Violated constraints are highlighted (red=hard, orange=soft).

Parameters:
  • leaderboard (dict) – Leaderboard (unused; surface_leaderboards carries entries).

  • surface_leaderboards (dict[str, dict]) – Per-surface data; includes excluded entries for diagnostics.

  • out_rst (Path) – Output path for reactor_scale.rst.

  • repo_root (Path, optional) – Repository root for visualization links; inferred from out_rst if None.

stellcoilbench.update_db.write_rst_leaderboard(leaderboard: Dict[str, Any], out_rst: Path, surface_leaderboards: Dict[str, Dict[str, Any]]) None

Write a ReadTheDocs-friendly RST leaderboard with metric definitions and per-surface tables.

Writes leaderboard.rst, leaderboard/metric_definitions.rst, and leaderboard/surface_specific.rst.

Parameters:
  • leaderboard (dict) – Leaderboard with entries list.

  • out_rst (Path) – Path for main leaderboard.rst file.

  • surface_leaderboards (dict[str, dict]) – Per-surface data from build_surface_leaderboards().

stellcoilbench.update_db.write_surface_leaderboards(surface_leaderboards: Dict[str, Dict[str, Any]], docs_dir: Path, repo_root: Path) list[str]

Write per-surface leaderboard Markdown files.

Each surface gets a file in docs_dir/leaderboards/ with sortable HTML tables and metric legend.

Parameters:
  • surface_leaderboards (dict[str, dict]) – Per-surface data from build_surface_leaderboards().

  • docs_dir (Path) – Docs root (e.g. repo_root / “docs”).

  • repo_root (Path) – Repository root (unused; kept for API compatibility).

Returns:

Sorted list of surface names written.

Return type:

list[str]

The update_db module handles leaderboard generation and management.

update_database(repo_root: Path, submissions_root: Path | None = None, docs_dir: Path | None = None, cases_root: Path | None = None, plasma_surfaces_dir: Path | None = None, *, use_local_viz_links: bool = False) -> dict

Main function to update leaderboards from submissions.

Scans submissions directory, loads results, computes rankings, and generates leaderboard files.

Parameters:

  • repo_root: Repository root directory

  • submissions_root: Submissions directory (default: repo_root / “submissions”)

  • docs_dir: Documentation directory (default: repo_root / “docs”)

  • cases_root: Cases directory (default: repo_root / “cases”)

  • plasma_surfaces_dir: Plasma surfaces directory (default: repo_root / “plasma_surfaces”)

  • use_local_viz_links: If True, use relative paths for PDF links instead of CDN

Returns:

  • dict: Summary with submissions_count, surfaces_updated, errors

build_surface_leaderboards(leaderboard: Dict[str, Any], submissions_root: Path, plasma_surfaces_dir: Path) -> Dict[str, Dict[str, Any]]

Group leaderboard entries by plasma surface.

Parameters:

  • leaderboard: Overall leaderboard dictionary

  • submissions_root: Submissions directory

  • plasma_surfaces_dir: Plasma surfaces directory

Returns:

  • Dict[str, Dict[str, Any]]: Dictionary mapping surface names to leaderboard entries

write_rst_leaderboard(leaderboard: Dict[str, Any], out_rst: Path, surface_leaderboards: Dict[str, Dict[str, Any]]) -> None

Write ReadTheDocs-formatted leaderboard.

Generates a comprehensive RST file with embedded tables for all surfaces.

Parameters:

  • leaderboard: Overall leaderboard dictionary

  • out_rst: Output RST file path

  • surface_leaderboards: Per-surface leaderboards

write_markdown_leaderboard(leaderboard: Dict[str, Any], out_md: Path) -> None

Write markdown-formatted leaderboard.

Parameters:

  • leaderboard: Leaderboard dictionary

  • out_md: Output markdown file path

write_surface_leaderboards(surface_leaderboards: Dict[str, Dict[str, Any]], docs_dir: Path, repo_root: Path) -> list[str]

Write per-surface markdown leaderboards.

Parameters:

  • surface_leaderboards: Per-surface leaderboards

  • docs_dir: Documentation directory

  • repo_root: Repository root

Returns:

  • list[str]: List of generated surface names

load_submissions(submissions_root: Path) -> Iterable[Tuple[str, Path, Dict[str, Any]]]

Load all submissions from directory.

Handles both regular directories and zip files. Extracts results.json from zips as needed.

Parameters:

  • submissions_root: Submissions directory

Yields:

  • (method_key, path, data): Method key, submission path, and results data

metric_shorthand(metric_name: str) -> str

Convert metric names to compact shorthand for display.

Parameters:

  • metric_name: Full metric name

Returns:

  • Shorthand/acronym (e.g., “f_B” for “final_normalized_squared_flux”)

metric_definition(metric_name: str) -> str

Get detailed mathematical definition for a metric.

Parameters:

  • metric_name: Metric name

Returns:

  • LaTeX-formatted mathematical definition

Post-Processing Module

The post_processing module handles post-optimization analysis including VMEC equilibrium calculations, Poincaré plots, quasisymmetry analysis, and Boozer surface plots.

run_post_processing(coils_json_path: Path, output_dir: Path, case_yaml_path: Optional[Path] = None, plasma_surfaces_dir: Optional[Path] = None, run_vmec: bool = True, helicity_m: int = 1, helicity_n: int = 0, ns: int = 50, plot_boozer: bool = True, plot_poincare: bool = True, nfieldlines: int = 20, mpi: Optional[Any] = None) -> Dict[str, Any]

Run complete post-processing pipeline.

This function:

  1. Loads coils and plasma surface

  2. Generates Poincaré plot (if requested)

  3. Computes QFM surface

  4. Optionally runs VMEC equilibrium

  5. Computes quasisymmetry metrics

  6. Generates VMEC-dependent plots (Boozer, iota, quasisymmetry)

Parameters:

  • coils_json_path: Path to coils JSON file

  • output_dir: Directory where output files will be saved

  • case_yaml_path: Path to case.yaml file (optional)

  • plasma_surfaces_dir: Directory containing plasma surface files (optional)

  • run_vmec: Whether to run VMEC equilibrium calculation (default: True)

  • helicity_m: Poloidal mode number for quasisymmetry (default: 1)

  • helicity_n: Toroidal mode number for quasisymmetry (default: 0)

  • ns: Number of radial surfaces for quasisymmetry evaluation (default: 50)

  • plot_boozer: Whether to generate Boozer surface plot (default: True)

  • plot_poincare: Whether to generate Poincaré plot (default: True)

  • nfieldlines: Number of fieldlines to trace for Poincaré plot (default: 20)

  • mpi: MPI partition for parallel execution (optional)

Returns:

  • Dict[str, Any]: Dictionary containing post-processing results: - qfm_surface: QFM surface object - quasisymmetry_average: Average quasisymmetry error - quasisymmetry_profile: Radial quasisymmetry profile - vmec: VMEC equilibrium object (if run_vmec=True)

Sensitivity Module

Coil sensitivity analysis via stochastic perturbation.

Uses simsopt’s CurvePerturbed / GaussianSampler to quantify how robust optimized coil solutions are to geometric perturbations (e.g. manufacturing tolerances, positioning errors). The principal output is sigma* – the perturbation amplitude (standard deviation in metres) at which the squared-flux metric f_B degrades by no more than a user-chosen factor (default 2x) at the chosen percentile (default 95th).

Typical usage

>>> from stellcoilbench.sensitivity import run_sensitivity_analysis
>>> results = run_sensitivity_analysis(
...     coils_json_path=Path("coils.json"),
...     case_yaml_path=Path("case.yaml"),
... )
>>> print(f"Critical sigma*: {results['critical_sigma_m'] * 1e3:.2f} mm")
class stellcoilbench.sensitivity.BisectionStep(sigma: float, percentile_ratio: float, n_samples: int)

Bases: object

Record of a single bisection iteration.

sigma

Perturbation amplitude [m] at this step.

Type:

float

percentile_ratio

Ratio \(f_B^{\mathrm{pert}}/f_B^{\mathrm{nom}}\) at the target percentile.

Type:

float

n_samples

Monte-Carlo sample count used.

Type:

int

n_samples: int
percentile_ratio: float
sigma: float
class stellcoilbench.sensitivity.SensitivityResult(critical_sigma_m: float, nominal_fb: float, factor: float, percentile: float, correlation_length_m: float, n_samples: int, seed: int, bisection_history: list[BisectionStep] = <factory>, sweep_sigmas: list[float] = <factory>, sweep_p50_ratios: list[float] = <factory>, sweep_p95_ratios: list[float] = <factory>, sweep_mean_ratios: list[float] = <factory>)

Bases: object

Full output of a sensitivity analysis run.

critical_sigma_m

Critical perturbation amplitude \(\sigma^*\) [m].

Type:

float

nominal_fb

Squared-flux \(f_B\) of the unperturbed coils.

Type:

float

factor

Maximum tolerated degradation factor (e.g. 2.0).

Type:

float

percentile

Percentile used for bisection (e.g. 95).

Type:

float

correlation_length_m

GP correlation length [m].

Type:

float

n_samples

Monte-Carlo samples per evaluation.

Type:

int

seed

Random seed.

Type:

int

bisection_history

Log of bisection iterations.

Type:

list

sweep_sigmas, sweep_p50_ratios, sweep_p95_ratios, sweep_mean_ratios

Sweep data for plotting.

Type:

list

bisection_history: list[BisectionStep]
correlation_length_m: float
critical_sigma_m: float
factor: float
n_samples: int
nominal_fb: float
percentile: float
seed: int
sweep_mean_ratios: list[float]
sweep_p50_ratios: list[float]
sweep_p95_ratios: list[float]
sweep_sigmas: list[float]
to_dict() dict[str, Any]

Serialize to a JSON-friendly dictionary.

stellcoilbench.sensitivity.compute_fb_perturbed(bs: BiotSavart, surface: SurfaceRZFourier, sigma: float, correlation_length_m: float, n_samples: int, seed: int = 42, flux_threshold: float = 0.0, samplers: list | None = None) np.ndarray

Evaluate \(f_B\) over n_samples stochastic coil perturbations.

Uses a Gaussian-process perturbation model with covariance \(k(s,s')\) (squared-exponential in arclength) to draw coil deformations, then evaluates SquaredFlux \(f_B\) on each perturbed coil set.

Parameters:
  • bs (simsopt.field.BiotSavart) – Nominal (unperturbed) BiotSavart field.

  • surface (simsopt.geo.SurfaceRZFourier) – Plasma surface for the SquaredFlux evaluation.

  • sigma (float) – Standard deviation of the Gaussian-process perturbation (metres).

  • correlation_length_m (float) – Correlation length of the perturbation along the coil (metres). Internally converted to the normalised [0, 1] domain used by GaussianSampler by dividing by each coil’s arc length. Ignored when samplers is provided.

  • n_samples (int) – Number of Monte-Carlo realisations to draw.

  • seed (int) – Base random seed for reproducibility.

  • flux_threshold (float) – Threshold passed to SquaredFlux (same semantics as optimisation).

  • samplers (list[GaussianSampler] | None) – Pre-built unit-sigma samplers (one per coil). When provided the drawn samples are scaled by sigma, avoiding the expensive sampler construction on every call. Build with _build_unit_samplers.

Returns:

Array of shape (n_samples,) with the f_B value for each perturbed coil set.

Return type:

np.ndarray

stellcoilbench.sensitivity.export_perturbed_coils_vtk(bs: BiotSavart, surface: SurfaceRZFourier, sigma: float, correlation_length_m: float, output_dir: Path, n_vtk_samples: int = 3, seed: int = 42) list[Path]

Export perturbed coil sets and surface B·n to VTK.

Uses perturbation amplitude \(\sigma\) (typically \(\sigma^*\)) to generate \(n\) perturbed coil sets via the GP sampler. For each sample writes coils_perturbed_XX.vtu and surface_perturbed_XX.vts (full-torus plasma surface coloured by B.n from the perturbed field, matching surface_optimized.vts).

Parameters:
  • bs (simsopt.field.BiotSavart) – Nominal BiotSavart field.

  • surface (simsopt.geo.SurfaceRZFourier) – Plasma surface (may be half-period; a full-torus copy is built internally for VTK output).

  • sigma (float) – Perturbation amplitude (metres) – typically sigma*.

  • correlation_length_m (float) – Correlation length (metres).

  • output_dir (Path) – Directory to write VTK files into.

  • n_vtk_samples (int) – Number of perturbed coil sets to export (default 3).

  • seed (int) – Random seed for reproducibility.

Returns:

Paths to the written VTK files (coils + surfaces interleaved).

Return type:

list[Path]

stellcoilbench.sensitivity.find_critical_sigma(bs: BiotSavart, surface: SurfaceRZFourier, nominal_fb: float, correlation_length_m: float = 1.0, n_samples: int = 100, factor: float = 2.0, percentile: float = 95.0, sigma_min: float = 1e-05, sigma_max: float = 0.05, seed: int = 42, flux_threshold: float = 0.0, bisection_tol: float = 0.05, max_bisection_iter: int = 20, samplers: list | None = None) tuple[float, list[BisectionStep]]

Find the critical perturbation amplitude \(\sigma^*\) via bisection.

\(\sigma^*\) is the largest \(\sigma\) for which the percentile-th percentile of \(f_B^{\mathrm{pert}}/f_B^{\mathrm{nom}}\) is at most factor. Bisection criterion: accept \(\sigma\) if \(P_{\mathrm{percentile}}(f_B^{\mathrm{pert}}/f_B^{\mathrm{nom}}) \leq \mathtt{factor}\).

Parameters:
  • bs (simsopt.field.BiotSavart) – Nominal BiotSavart field.

  • surface (simsopt.geo.SurfaceRZFourier) – Plasma surface.

  • nominal_fb (float) – f_B of the unperturbed coil set.

  • correlation_length_m (float) – Physical correlation length (metres).

  • n_samples (int) – Monte-Carlo samples per sigma evaluation.

  • factor (float) – Maximum tolerated f_B degradation factor (e.g. 2.0).

  • percentile (float) – Percentile at which to enforce the factor (e.g. 95).

  • sigma_min (float) – Bisection bounds in metres.

  • sigma_max (float) – Bisection bounds in metres.

  • seed (int) – Random seed.

  • flux_threshold (float) – Passed to SquaredFlux.

  • bisection_tol (float) – Relative tolerance on sigma for convergence (fraction of interval).

  • max_bisection_iter (int) – Maximum bisection iterations.

  • samplers (list[GaussianSampler] | None) – Pre-built unit-sigma samplers (one per coil). When provided, avoids recreating samplers on every bisection iteration.

Returns:

  • sigma_star (float) – Critical perturbation amplitude (metres).

  • history (list[BisectionStep]) – Bisection iteration log.

stellcoilbench.sensitivity.plot_sensitivity(result: SensitivityResult, output_path: Path) Path

Generate a sensitivity plot (\(f_B\) ratio vs \(\sigma\)) and save.

Plots median, percentile (e.g. 95th), and mean of \(f_B^{\mathrm{pert}}/f_B^{\mathrm{nom}}\) vs perturbation amplitude \(\sigma\), with \(\sigma^*\) and the factor threshold marked.

Parameters:
  • result (SensitivityResult) – Completed sensitivity analysis result containing sweep data.

  • output_path (Path) – Destination file (e.g. sensitivity_plot.pdf).

Returns:

The written file path.

Return type:

Path

stellcoilbench.sensitivity.run_sensitivity_analysis(coils_json_path: Path, case_yaml_path: Path | None = None, plasma_surfaces_dir: Path | None = None, correlation_length_m: float = 1.0, n_samples: int = 20, factor: float = 2.0, percentile: float = 95.0, sigma_min: float = 1e-05, sigma_max: float = 0.05, seed: int = 42, output_dir: Path | None = None, make_plot: bool = True, n_sweep: int = 8, n_vtk_samples: int = 0) SensitivityResult

Run a full coil sensitivity analysis.

Loads coils and surface, computes nominal \(f_B\), bisects to find \(\sigma^*\), optionally runs a sweep for visualisation, exports perturbed coil VTK files, and writes sensitivity_results.json (and an optional plot) to output_dir.

Parameters:
  • coils_json_path (Path) – Optimised coils JSON file.

  • case_yaml_path (Path | None) – Case YAML (auto-detected if None).

  • plasma_surfaces_dir (Path | None) – Plasma-surface directory (default plasma_surfaces/).

  • correlation_length_m (float) – Correlation length of perturbation along the coil (metres). Default 1.0 m – appropriate for reactor-scale coils (20-40 m).

  • n_samples (int) – Monte-Carlo samples per sigma evaluation.

  • factor (float) – Maximum tolerated f_B degradation factor.

  • percentile (float) – Percentile at which the factor is enforced.

  • sigma_min (float) – Bisection bounds (metres).

  • sigma_max (float) – Bisection bounds (metres).

  • seed (int) – Random seed.

  • output_dir (Path | None) – Where to write results. Defaults to the parent of coils_json_path.

  • make_plot (bool) – Whether to produce a PDF plot.

  • n_sweep (int) – Number of sigma values in the optional sweep.

  • n_vtk_samples (int) – Number of perturbed coil sets to export as VTK files (at sigma*) for visual comparison. 0 disables VTK export (default).

Returns:

Dataclass with all outputs.

Return type:

SensitivityResult

Coil sensitivity analysis via stochastic perturbation (CurvePerturbed / GaussianSampler). Quantifies robustness to geometric perturbations. Key entry point: run_sensitivity_analysis(coils_json_path, case_yaml_path, ...).

Path Utils Module

Shared path resolution utilities for StellCoilBench.

Provides helpers for locating plasma_surfaces directories, surface files, and case YAML files across the repository layout.

class stellcoilbench.path_utils.CaseResolutionResult(case_yaml_path: Path | None, surface_path: Path | None, case_data: dict[str, Any])

Bases: object

Result of case and surface path resolution.

Use resolve_case_and_surface() as the canonical resolver. Supports tuple unpacking: case_path, surface_path, case_data = result.

case_data: dict[str, Any]
case_yaml_path: Path | None
surface_path: Path | None
class stellcoilbench.path_utils.ResolvedPaths(case_yaml: Path | None, surface_file: Path | None, coils_json: Path | None, plasma_surfaces_dir: Path | None)

Bases: object

Canonical result of path resolution for post-processing and coil loading.

Single entry point for case YAML, surface file, coils JSON, and plasma surfaces directory. Use resolve_all() to populate.

case_yaml: Path | None
coils_json: Path | None
plasma_surfaces_dir: Path | None
surface_file: Path | None
stellcoilbench.path_utils.coils_json_path_from_dir(out_dir: Path) Path | None

Return path to coils JSON in directory, preferring biot_savart_optimized.json.

Checks for biot_savart_optimized.json first (simsopt optimization output), then coils.json (generic fallback). Returns the first that exists.

Parameters:

out_dir (Path) – Directory containing coil output files.

Returns:

Path to coils JSON file if found; None otherwise.

Return type:

Path | None

stellcoilbench.path_utils.dump_yaml(data: dict[str, Any], path: Path | None = None, **kwargs: Any) str | None

Write a dict to YAML. If path is given, write to file and return None. Otherwise return the YAML string.

Parameters:
  • data (dict) – Data to serialize.

  • path (Path, optional) – If provided, write to this file.

  • **kwargs – Passed to yaml.dump (e.g. default_flow_style, sort_keys).

Returns:

YAML string if path is None; None if written to file.

Return type:

str | None

stellcoilbench.path_utils.find_case_and_surface_path(coils_json_path: Path, case_yaml_path: Path | None, plasma_surfaces_dir: Path | None) tuple[Path, Path]

Locate case YAML and plasma surface path; raise if not found.

Wrapper around resolve_case_and_surface() that raises FileNotFoundError or ValueError when case/surface cannot be resolved.

Parameters:
  • coils_json_path (Path) – Path to coils JSON file.

  • case_yaml_path (Path | None) – Explicit case YAML path, or None to search from coils_json_path.

  • plasma_surfaces_dir (Path | None) – Optional plasma surfaces directory.

Returns:

(resolved_case_yaml_path, surface_path).

Return type:

tuple of (Path, Path)

Raises:
  • FileNotFoundError – If case YAML or surface file cannot be found.

  • ValueError – If case YAML does not specify a surface.

stellcoilbench.path_utils.find_dir_up(start_path: Path, dir_name: str, max_levels: int = 5) Path | None

Walk up from start_path to find a directory with the given name.

Parameters:
  • start_path (Path) – Directory to start searching from.

  • dir_name (str) – Name of the directory to find (e.g., “plasma_surfaces”, “cases”).

  • max_levels (int, default=5) – Maximum number of parent levels to traverse.

Returns:

Path to the directory if found; None otherwise.

Return type:

Path | None

stellcoilbench.path_utils.find_file_up(start_path: Path, file_name: str, max_levels: int = 5) Path | None

Walk up from start_path to find a file with the given name.

Parameters:
  • start_path (Path) – Directory to start searching from.

  • file_name (str) – Name of the file to find (e.g., “case.yaml”).

  • max_levels (int, default=5) – Maximum number of parent levels to traverse.

Returns:

Path to the file if found; None otherwise.

Return type:

Path | None

stellcoilbench.path_utils.find_plasma_surfaces_dir(start_path: Path, max_levels: int = 5) Path | None

Walk up from start_path to find a plasma_surfaces directory.

Parameters:
  • start_path (Path) – Directory to start searching from (e.g., output dir, case dir).

  • max_levels (int, default=5) – Maximum number of parent levels to traverse.

Returns:

Path to plasma_surfaces directory if found; None otherwise.

Return type:

Path | None

stellcoilbench.path_utils.find_repo_root(start_path: Path | None = None, max_levels: int = 15) Path | None

Walk up from start_path to find repository root (.git or pyproject.toml).

Parameters:
  • start_path (Path | None) – Directory to start searching from. Defaults to Path.cwd().

  • max_levels (int, default=15) – Maximum number of parent levels to traverse.

Returns:

Repository root directory if found; None otherwise.

Return type:

Path | None

stellcoilbench.path_utils.get_reference_radii(surface: SurfaceRZFourier) tuple[float, float]

Return (major_radius, minor_radius) from raw file when available, else from surface.

Surfaces loaded via load_surface_with_range have _major_radius_raw and _minor_radius_raw from the raw simsopt load (before quadpoint rebuild). These are stable across resolution and ensure consistent a0 and threshold scaling.

Parameters:

surface (SurfaceRZFourier) – Plasma boundary surface.

Returns:

(major_radius [m], minor_radius [m]).

Return type:

tuple[float, float]

stellcoilbench.path_utils.get_surface_filename(case_data: dict | Any) str

Extract surface filename from case config (dict or CaseConfig).

Parameters:

case_data (dict | CaseConfig) – Case configuration (parsed YAML dict or CaseConfig instance).

Returns:

Surface filename (e.g. input.LandremanPaul2021_QA), or empty string.

Return type:

str

stellcoilbench.path_utils.get_surface_search_base_dirs(case_path: Path | None = None, plasma_surfaces_dir: Path | None = None, coils_json_path: Path | None = None) list[Path]

Build list of directories to search for surface files.

Collects plasma_surfaces directories and case/output directories that may contain surface files. Used with resolve_surface_path for consistent surface resolution across the codebase.

Parameters:
  • case_path (Path | None) – Case directory or case.yaml path.

  • plasma_surfaces_dir (Path | None) – Explicit plasma_surfaces directory (e.g., from config).

  • coils_json_path (Path | None) – Path to coils JSON (used to find plasma_surfaces via find_plasma_surfaces_dir).

Returns:

Directories to search, in priority order.

Return type:

list[Path]

stellcoilbench.path_utils.get_target_B_from_surface(surface_file: str) float

Get target on-axis B-field [T] from surface filename.

Maps known surface names to their design target B-field for coil optimization and reactor-scale scaling.

Parameters:

surface_file (str) – Surface filename (e.g., “input.LandremanPaul2021_QA”, “muse.focus”).

Returns:

Target B-field in Tesla.

Return type:

float

stellcoilbench.path_utils.load_surface_with_range(surface_path: Path | str, surface_range: str = 'full torus', nphi: int = 256, ntheta: int = 256, *, endpoints: bool = True) SurfaceRZFourier

Load a surface from file with a specified range and resolution.

Detects file type from path (input/wout/focus) and uses the appropriate SurfaceRZFourier loader.

Parameters:
  • surface_path (Path | str) – Path to surface file.

  • surface_range (str, default="full torus") – Range for surface loading (“full torus” or “half period”).

  • nphi (int, default=256) – Number of phi quadrature points.

  • ntheta (int, default=256) – Number of theta quadrature points.

  • endpoints (bool, default=True) – If True, quadpoints include 0 and 1 so the surface closes at the boundaries. Use for VTK visualization to avoid holes in ParaView.

Returns:

Loaded surface with specified range.

Return type:

SurfaceRZFourier

stellcoilbench.path_utils.load_yaml(path: Path | None = None, content: str | bytes | None = None) dict[str, Any]

Load YAML into a dict from a file path or string/bytes content.

Parameters:
  • path (Path, optional) – Path to the YAML file. Mutually exclusive with content.

  • content (str | bytes, optional) – YAML content as string or bytes. Mutually exclusive with path.

Returns:

Parsed YAML content.

Return type:

dict

stellcoilbench.path_utils.load_yaml_safe(path: Path | None = None, content: str | bytes | None = None) dict[str, Any] | None

Load YAML into a dict, returning None on failure.

Catches OSError and yaml.YAMLError. Use when callers prefer None over raising.

Parameters:
  • path (Path, optional) – Path to the YAML file. Mutually exclusive with content.

  • content (str | bytes, optional) – YAML content as string or bytes. Mutually exclusive with path.

Returns:

Parsed YAML content, or None if loading fails.

Return type:

dict | None

stellcoilbench.path_utils.normalize_surface_id(name: str, for_filename: bool = False) str

Canonical surface identifier for leaderboards and filenames.

Strips input./wout. prefixes and .focus suffix, then optionally replaces dots and spaces for filesystem-safe identifiers.

Parameters:
  • name (str) – Surface name or filename (e.g., “input.LandremanPaul2021_QA”).

  • for_filename (bool, default=False) – If True, replace “.” and “ “ with “_” for safe filenames.

Returns:

Normalized identifier (e.g., “LandremanPaul2021_QA” or “LandremanPaul2021_QA” with underscores when for_filename=True).

Return type:

str

stellcoilbench.path_utils.resolve_all(out_dir: Path, case_hint: Path | str | None = None, surface_filename: str | None = None, coils_hint: Path | None = None) ResolvedPaths

Resolve case YAML, surface file, coils JSON, and plasma_surfaces dir.

Canonical path resolution entry point. Combines resolve_case_yaml_path, resolve_surface_file_path, coils_json_path_from_dir, and find_plasma_surfaces_dir into a single call.

Parameters:
  • out_dir (Path) – Output directory (e.g., optimization output dir) used as search base.

  • case_hint (Path | str | None, optional) – Explicit case path (file or directory containing case.yaml).

  • surface_filename (str | None, optional) – Surface filename for case.yaml search (e.g., from s.filename).

  • coils_hint (Path | None, optional) – Explicit coils JSON path. When None, probes out_dir for coils.

Returns:

case_yaml, surface_file, coils_json, plasma_surfaces_dir (any may be None).

Return type:

ResolvedPaths

stellcoilbench.path_utils.resolve_case_and_surface(case_hint: Path | str | None, coils_path: Path | None = None, plasma_dir: Path | None = None) CaseResolutionResult

Resolve case YAML and plasma surface paths from hints.

Single source of truth for case/surface resolution. When case_hint is None, searches from coils_path: walk up for case.yaml, guess cases/<stem>/, or scan cases/ for matching surface.

Parameters:
  • case_hint (Path or str or None) – Explicit path to case.yaml or case directory. If None, resolved from coils_path when provided.

  • coils_path (Path, optional) – Path to coils JSON; used as search base when case_hint is None.

  • plasma_dir (Path, optional) – Directory containing plasma surface files.

Returns:

(case_yaml_path, surface_path, raw_case_data). Any may be None/empty if not found. Supports tuple unpacking for backward compatibility.

Return type:

CaseResolutionResult

stellcoilbench.path_utils.resolve_case_yaml_path(out_dir: Path, case_path_hint: Path | str | None = None, surface_filename: str | None = None) Path | None

Resolve case.yaml path from output directory, hints, and surface filename.

Search order: (1) case_path_hint if valid file/dir, (2) out_dir/case.yaml, (3) out_dir.parent/case.yaml, (4) surface-based paths (surface_dir, cases/stem), (5) scan cases/*.yaml for surface match.

Parameters:
  • out_dir (Path) – Output directory (e.g., from optimization).

  • case_path_hint (Path | str | None) – Known case path (file or directory containing case.yaml).

  • surface_filename (str | None) – Surface filename (e.g., from s.filename) for cases/stem/case.yaml search.

Returns:

Path to case.yaml if found; None otherwise.

Return type:

Path | None

stellcoilbench.path_utils.resolve_surface_file_path(case_yaml_path: Path | None = None, surface_filename: str | None = None, plasma_surfaces_dir: Path | None = None, coils_json_path: Path | None = None) Path | None

Resolve a plasma surface file path from case YAML or explicit filename.

Centralises the surface-path resolution logic shared by coil loading, Poincaré plotting, and VMEC input resolution. When case_yaml_path or coils_json_path is provided, delegates to resolve_case_and_surface for unified resolution; otherwise uses get_surface_search_base_dirs + resolve_surface_path for explicit surface_filename lookup.

Parameters:
  • case_yaml_path (Path, optional) – Path to a case.yaml file containing surface_params.surface.

  • surface_filename (str, optional) – Explicit surface filename to resolve (overrides case YAML lookup).

  • plasma_surfaces_dir (Path, optional) – Directory containing plasma surface files.

  • coils_json_path (Path, optional) – Coils JSON path used as an additional base directory for search.

Returns:

Resolved absolute path to the surface file, or None if not found.

Return type:

Path or None

stellcoilbench.path_utils.resolve_surface_path(surface_name: str, base_dirs: list[Path], case_insensitive: bool = True) Path | None

Resolve a surface filename to an existing file path.

Searches in base_dirs (e.g., plasma_surfaces, case dirs). Optionally falls back to case-insensitive match within those directories.

Parameters:
  • surface_name (str) – Surface filename (e.g., “input.LandremanPaul2021_QA”).

  • base_dirs (list[Path]) – Directories to search (e.g., [Path(“plasma_surfaces”), Path.cwd() / “plasma_surfaces”]).

  • case_insensitive (bool, default=True) – If exact match fails, try case-insensitive directory search.

Returns:

Path to surface file if found; None otherwise.

Return type:

Path | None

stellcoilbench.path_utils.surface_stem_from_filename(filename: str) str

Extract a normalized surface stem from a surface filename.

Strips common prefixes (input., wout.) and suffixes (.focus) so that “input.LandremanPaul2021_QA”, “wout.LandremanPaul2021_QA”, and “LandremanPaul2021_QA.focus” all yield “LandremanPaul2021_QA”.

Parameters:

filename (str) – Surface filename (e.g., “input.LandremanPaul2021_QA”, “foo.focus”).

Returns:

Normalized stem for use as a surface identifier.

Return type:

str

Path resolution for plasma surfaces, case YAML, and coils. Key functions: resolve_case_and_surface, resolve_all, find_plasma_surfaces_dir, load_yaml, dump_yaml, get_surface_filename.

Finite Build Module

Finite-build coil geometry generation and VTK export.

Generates 3D coil geometry by sweeping a rectangular cross-section along the coil centerline (filament) using a rotation-minimizing frame. Output is a surface mesh VTK file suitable for visualization. Uses ParaStell when available for tetrahedral mesh; falls back to built-in sweep otherwise.

stellcoilbench.finite_build.finite_build_coils_to_msh(coils: List, msh_path: str | Path, width: float, height: float, mesh_size: float, min_mesh_size: float | None = None, max_mesh_size: float | None = None) Tuple[Path, List[int]] | None

Generate tetrahedral .msh for finite-build coils.

Tries ParaStell first. If ParaStell is unavailable or fails, falls back to sweep-based mesh generation (stub: returns None for now).

Parameters:
  • coils (List) – List of simsopt Coil objects.

  • msh_path (Path or str) – Output .msh file path.

  • width (float) – Cross-section width [m].

  • height (float) – Cross-section height [m].

  • mesh_size (float) – Characteristic element size [m].

  • min_mesh_size (float, optional) – Gmsh minimum element size [m]. If None, uses mesh_size.

  • max_mesh_size (float, optional) – Gmsh maximum element size [m]. If None, uses mesh_size.

Returns:

(msh_path, list of coil indices) if successful; None on failure.

Return type:

tuple[Path, list[int]] or None

stellcoilbench.finite_build.finite_build_coils_to_vtk(coils: List, output_path: str | Path, width: float | None = None, height: float | None = None, width_per_coil: List[float] | None = None, height_per_coil: List[float] | None = None, n_along: int | None = None, use_stellaris_default: bool = True, min_mesh_size: float | None = None, max_mesh_size: float | None = None) Path

Generate finite-build coil geometry and export to VTK.

Sweeps a rectangular cross-section along each coil centerline and writes a combined VTK file. Cross-section dimensions can be: - Uniform (width, height) for all coils - Per-coil (width_per_coil, height_per_coil) - Stellaris-derived: sqrt(N_turns) * 20 mm square when N_turns available - Default: 35 cm × 35 cm (reactor scale).

Parameters:
  • coils (List) – List of simsopt Coil objects (with .curve attribute).

  • output_path (Path or str) – Output file path (e.g. “finite_build_coils” -> finite_build_coils.vtk).

  • width (float, optional) – Uniform cross-section width [m] for all coils.

  • height (float, optional) – Uniform cross-section height [m] for all coils.

  • width_per_coil (List[float], optional) – Per-coil width [m]. Length must match number of coils.

  • height_per_coil (List[float], optional) – Per-coil height [m]. Length must match number of coils.

  • n_along (int, optional) – Number of points along each coil for sampling. If None, uses the curve’s existing quadrature points.

  • use_stellaris_default (bool, default=True) – If no dimensions given, use DEFAULT_CROSS_SECTION_M (35 cm).

  • min_mesh_size (float, optional) – Gmsh minimum element size [m] for ParaStell tetrahedral mesh. Used only when ParaStell succeeds (no per-coil dimensions).

  • max_mesh_size (float, optional) – Gmsh maximum element size [m] for ParaStell tetrahedral mesh. Used only when ParaStell succeeds (no per-coil dimensions).

Returns:

Path to the written VTK file.

Return type:

Path

Raises:

ValueError – If coil list is empty or dimension arrays have wrong length.

stellcoilbench.finite_build.sweep_rectangular_cross_section(gamma: ndarray, gammadash: ndarray, width: float, height: float, n_cross: int = 4) Tuple[ndarray, ndarray]

Sweep a rectangular cross-section along a curve to create a surface mesh.

Parameters:
  • gamma (np.ndarray) – Curve points, shape (n_points, 3).

  • gammadash (np.ndarray) – Curve derivatives (tangents), shape (n_points, 3).

  • width (float) – Cross-section width [m] along the first in-plane direction.

  • height (float) – Cross-section height [m] along the second in-plane direction.

  • n_cross (int) – Number of vertices per cross-section (4 for rectangle corners).

Returns:

  • vertices (np.ndarray) – Mesh vertices, shape (n_vertices, 3).

  • faces (np.ndarray) – Triangle faces as vertex indices, shape (n_faces, 3).

Notes

The first point is appended to gamma/gammadash to close the curve, so the last segment connects back to the start for a fully connected loop.

Finite-build coil geometry: rectangular cross-section swept along centerline. Key function: finite_build_coils_to_vtk. Requires pip install .[structural] for Gmsh.

Structural Analysis Module

FEM-based structural analysis for finite-build stellarator coils.

Solves 3-D linear elasticity on a tetrahedral mesh of the winding pack, using electromagnetic Lorentz-force body loads (J × B) and outputs the displacement field, Cauchy stress tensor, and Von Mises stress as XDMF / VTK files suitable for ParaView.

The primary solver backend is DOLFINx (FEniCSx). A lightweight scikit-fem fallback is provided for environments where DOLFINx is not installed (no MPI parallelism, but pip-installable).

Both backends are optional imports; a clear error is raised when neither is available and the user requests a structural solve.

stellcoilbench.structural_analysis.compute_lorentz_body_force(coils: list[Coil], bs: BiotSavart, mesh: dolfinx.mesh.Mesh | skfem.MeshTet1, cross_section_area: float, *, width: float = 0.05, height: float = 0.05, use_regularized: bool = True, mesh_coils: list[Coil] | None = None, all_coils: list[Coil] | None = None, backend: str | None = None, quadrature_degree: int | None = None) dolfinx.fem.Function | np.ndarray

Compute Lorentz body-force density J × B on the coil mesh.

The current density is assumed uniform across the winding-pack cross-section, directed along the coil tangent at the nearest centerline point:

\[\mathbf{J} = \frac{I}{A}\,\mathbf{t}\]

The body-force density (force per unit volume) is:

\[\mathbf{f} = \mathbf{J} \times \mathbf{B} \quad [\mathrm{N/m^3}]\]

When use_regularized=True (default), the Landreman et al. (2025) regularized internal field model is used for self-field; BiotSavart provides the mutual field from other coils.

Parameters:
  • coils (list[Coil]) – simsopt Coil objects.

  • bs (BiotSavart) – Magnetic field evaluator.

  • mesh (dolfinx.mesh.Mesh | skfem.MeshTet1) – Tetrahedral coil mesh.

  • cross_section_area (float) – Winding-pack cross-section area [m²].

  • width (float, optional) – Cross-section full width [m]. Default 0.05.

  • height (float, optional) – Cross-section full height [m]. Default 0.05.

  • use_regularized (bool, optional) – If True, use regularized internal field for self-field. Default True.

  • mesh_coils (list[Coil] | None, optional) – Coils on which mesh points lie (for multi-coil meshes).

  • all_coils (list[Coil] | None, optional) – Full coil set including symmetry copies for mutual-field B.

Returns:

Body-force density [N/m³] at quadrature points (DOLFINx) or mesh nodes (scikit-fem).

Return type:

dolfinx.fem.Function | np.ndarray

stellcoilbench.structural_analysis.compute_stress_field(mesh: dolfinx.mesh.Mesh | skfem.MeshTet1, displacement: dolfinx.fem.Function | np.ndarray, E: float = 100000000000.0, nu: float = 0.3, *, backend: str | None = None) tuple['dolfinx.fem.Function | None', 'dolfinx.fem.Function | np.ndarray']

Compute the Cauchy stress tensor and Von Mises stress from displacement.

The Cauchy stress follows isotropic Hooke’s law:

\[\sigma = \lambda\mathrm{tr}(\varepsilon)I + 2\mu\varepsilon\]

with strain \(\varepsilon = \tfrac{1}{2}(\nabla\mathbf{u} + \nabla\mathbf{u}^T)\). The Von Mises equivalent stress is:

\[\sigma_{\mathrm{vm}} = \sqrt{\tfrac{3}{2}\,\mathbf{s}:\mathbf{s}} \quad\text{where}\quad \mathbf{s} = \sigma - \tfrac{1}{3}\mathrm{tr} (\sigma)I\]

(deviatoric stress \(\mathbf{s}\)).

Parameters:
  • mesh (dolfinx.mesh.Mesh | skfem.MeshTet1) – Tetrahedral mesh.

  • displacement (dolfinx.fem.Function | np.ndarray) – Displacement field from solve_linear_elasticity().

  • E (float, optional) – Young’s modulus [Pa].

  • nu (float, optional) – Poisson ratio.

Returns:

(sigma_field, von_mises). DOLFINx returns full stress tensor; scikit-fem returns (None, von_mises).

Return type:

tuple

stellcoilbench.structural_analysis.export_results(mesh: dolfinx.mesh.Mesh | skfem.MeshTet1, displacement: dolfinx.fem.Function | np.ndarray, von_mises: dolfinx.fem.Function | np.ndarray, output_dir: Path, *, backend: str | None = None) dict[str, str]

Write displacement and Von Mises stress to backend-specific output files.

DOLFINx writes XDMF (displacement.xdmf, von_mises_stress.xdmf). scikit-fem writes a single VTK via meshio.

Parameters:
  • mesh (dolfinx.mesh.Mesh | skfem.MeshTet1) – Tetrahedral mesh.

  • displacement (dolfinx.fem.Function | np.ndarray) – Displacement field.

  • von_mises (dolfinx.fem.Function | np.ndarray) – Von Mises stress.

  • output_dir (Path) – Directory for output files.

  • backend (str | None) – Override: "dolfinx" or "skfem" when that backend is available.

Returns:

Mapping of result name to file path (e.g. "displacement_xdmf", "von_mises_xdmf", "structural_vtk").

Return type:

dict[str, str]

stellcoilbench.structural_analysis.load_coil_mesh(msh_path: Path, *, backend: str | None = None) dolfinx.mesh.Mesh | skfem.MeshTet1

Load a Gmsh .msh coil mesh using the available backend.

Parameters:
  • msh_path (Path) – Path to the .msh file.

  • backend (str | None) – Override: "dolfinx" or "skfem" when that backend is available.

Returns:

Backend-specific mesh object (dolfinx.mesh.Mesh or skfem.MeshTet1).

Return type:

object

stellcoilbench.structural_analysis.run_structural_analysis(coils: list[Coil], bs: BiotSavart, output_dir: Path, msh_path: Path | None = None, vtk_path: Path | None = None, width: float = 0.05, height: float = 0.05, E: float | None = None, nu: float | None = None, structural_mesh_resolution_coarse: float | None = None, backend: str | None = None, quadrature_degree: int | None = None, polynomial_degree: int | None = None, use_spring_bc: bool = True, export_full_coil_set: bool = False, nfp: int = 1, stellsym: bool = False) dict[str, Any]

FEM structural analysis of coil geometry (J×B body force, linear elasticity, VTK/XDMF export).

Loads mesh, computes Lorentz body force, solves elasticity, exports displacement and Von Mises stress. Uses Landreman et al. regularized internal field for self-field. BC: fixed_supports pins bottom 15% z-range. See _dolfinx/_skfem for implementation details.

Parameters:
  • coils (list[Coil]) – simsopt Coil objects.

  • bs (BiotSavart) – Magnetic field evaluator.

  • output_dir (Path) – Output directory.

  • msh_path (Path, optional) – Path to .msh file. If None, uses finite_build_coils_parastell.msh if present, else generates via sweep fallback (structural_coils.msh).

  • vtk_path (Path, optional) – Unused (API compatibility).

  • width (float) – Winding-pack cross-section [m] (default 0.05 each).

  • height (float) – Winding-pack cross-section [m] (default 0.05 each).

  • E (float, optional) – Young’s modulus [Pa], Poisson ratio. Default WP_YOUNGS_MODULUS_PA, WP_POISSON_RATIO.

  • nu (float, optional) – Young’s modulus [Pa], Poisson ratio. Default WP_YOUNGS_MODULUS_PA, WP_POISSON_RATIO.

  • structural_mesh_resolution_coarse (float, optional) – Element size [m] for tetrahedral mesh. If None, uses default (0.08 m).

  • backend (str, optional) – Override backend: "dolfinx" or "skfem".

  • quadrature_degree (int, optional) – Quadrature degree q for body-force RHS integration. Default 4.

  • polynomial_degree (int, optional) – FEM Lagrange order p (DOLFINx only). Default 1. Ignored by scikit-fem.

  • use_spring_bc (bool, optional) – Use Winkler spring-foundation BC when True (default).

  • export_full_coil_set (bool, optional) – When True and symmetry_factor > 1, write structural_results_full.vtk with the full coil set (unique coils + symmetry copies).

  • nfp (int, optional) – Number of field periods for toroidal symmetry (default 1).

  • stellsym (bool, optional) – Whether stellarator symmetry is used (default False).

Returns:

max_von_mises_stress_Pa, mean_von_mises_stress_Pa, max_displacement_m, backend, structural_vtk, displacement_xdmf, von_mises_xdmf.

Return type:

dict[str, Any]

stellcoilbench.structural_analysis.solve_linear_elasticity(mesh: dolfinx.mesh.Mesh | skfem.MeshTet1, body_force: dolfinx.fem.Function | np.ndarray, E: float = 100000000000.0, nu: float = 0.3, *, coils: list[Coil] | None = None, bs: BiotSavart | None = None, cross_section_area: float | None = None, width: float | None = None, height: float | None = None, mesh_coils: list[Coil] | None = None, all_coils: list[Coil] | None = None, backend: str | None = None, polynomial_degree: int | None = None, use_spring_bc: bool = True) dolfinx.fem.Function | np.ndarray

Solve the 3-D linear-elasticity boundary-value problem.

Solves the weak form for displacement \(\mathbf{u}\):

\[\int_\Omega \sigma(\mathbf{u}) : \varepsilon(\mathbf{v})\,dx = \int_\Omega \mathbf{f} \cdot \mathbf{v}\,dx\]

where \(\sigma = \lambda\mathrm{tr}(\varepsilon)I + 2\mu\varepsilon\) (isotropic Hooke’s law), \(\varepsilon = \tfrac{1}{2}(\nabla\mathbf{u} + \nabla\mathbf{u}^T)\), and \(\mathbf{f}\) is the Lorentz body-force density. With use_spring_bc=True (default), a Winkler spring-foundation Robin BC is applied over the bottom 15% of the z-range, eliminating the clamped-free stress singularity. Set use_spring_bc=False for the legacy hard-Dirichlet path (\(\mathbf{u}=0\)).

Parameters:
  • mesh (dolfinx.mesh.Mesh | skfem.MeshTet1) – Tetrahedral mesh.

  • body_force (dolfinx.fem.Function | np.ndarray) – Body-force density [N/m³] from compute_lorentz_body_force().

  • E (float, optional) – Young’s modulus [Pa]. Default from WP_YOUNGS_MODULUS_PA.

  • nu (float, optional) – Poisson ratio. Default from WP_POISSON_RATIO.

  • coils (optional) – Passed to scikit-fem backend when body_force was precomputed elsewhere.

  • bs (optional) – Passed to scikit-fem backend when body_force was precomputed elsewhere.

  • cross_section_area (optional) – Passed to scikit-fem backend when body_force was precomputed elsewhere.

  • width (optional) – Passed to scikit-fem backend when body_force was precomputed elsewhere.

  • height (optional) – Passed to scikit-fem backend when body_force was precomputed elsewhere.

  • mesh_coils (list[Coil] | None, optional) – Coils for mesh (multi-coil case).

  • all_coils (list[Coil] | None, optional) – Full coil set for mutual B in scikit-fem.

  • use_spring_bc (bool, optional) – When True (default), uses a Winkler spring-foundation Robin BC to avoid the clamped-free stress singularity. False selects the legacy hard-Dirichlet BC for comparison. Dolfinx backend only.

Returns:

Displacement field [m] at mesh nodes.

Return type:

dolfinx.fem.Function | np.ndarray

stellcoilbench.structural_analysis.write_structural_vtk(mesh: dolfinx.mesh.Mesh | skfem.MeshTet1, displacement: dolfinx.fem.Function | np.ndarray, von_mises: dolfinx.fem.Function | np.ndarray, output_path: Path) Path

Write a standalone tetrahedral VTK with displacement and Von Mises stress.

Produces a single VTK file with: - Point data: Displacement (3-vector), DisplacementMagnitude - Cell data: VonMisesStress (scalar per tetrahedron)

Parameters:
  • mesh (dolfinx.mesh.Mesh | skfem.MeshTet1) – Tetrahedral mesh.

  • displacement (dolfinx.fem.Function | np.ndarray) – Displacement field, shape (n_nodes, 3).

  • von_mises (dolfinx.fem.Function | np.ndarray) – Von Mises stress per cell or per node.

  • output_path (Path) – Output file path (e.g. structural_results.vtk).

Returns:

The path written.

Return type:

Path

FEM structural analysis (Von Mises stress, Lorentz force). Backends: DOLFINx or scikit-fem. Key function: run_structural_analysis. Optional: pip install .[structural].

Evaluate and Submission Pipeline (Module Responsibilities)

  • ``evaluate``: Defines SubmissionResults dataclass (metadata + metrics). Lightweight container; used when structuring evaluation output.

  • ``submission_packaging``: Assembles the physical submission: builds dirs, writes results.json, copies case YAML, zips. Used by submit-case.

  • ``update_db``: Consumes submissions (zips/dirs), aggregates metrics, generates leaderboards. Does not create submissions.

Validate Config Module

Validation functions for case.yaml configuration files and CI autopilot case JSON.

Validates case YAML (surface_params, coils_params, optimizer_params, coil_objective_terms, fourier_continuation, and related fields) and CI case JSON (case_id, resource caps, case_config embedding, policy limits).

stellcoilbench.validate_config.validate_case_config(data: dict[str, Any], file_path: Path | None = None, surfaces_dir: Path | None = None) list[str]

Validate a case.yaml configuration dictionary.

Checks required fields (description, surface_params, coils_params, optimizer_params), valid keys for each section, type/value constraints, and that the referenced surface file exists in plasma_surfaces/.

Parameters:
  • data (dict[str, Any]) – Parsed YAML/JSON configuration to validate.

  • file_path (Path, optional) – Path to source file; used for error message prefixes.

  • surfaces_dir (Path, optional) – Directory containing surface files. If None, uses repo-relative plasma_surfaces/.

Returns:

Error messages. Empty list means validation passed.

Return type:

list[str]

stellcoilbench.validate_config.validate_case_yaml_file(file_path: Path, surfaces_dir: Path | None = None) list[str]

Validate a case.yaml file on disk.

Loads the file with load_yaml, then delegates to validate_case_config.

Parameters:
  • file_path (Path) – Path to the case YAML file.

  • surfaces_dir (Path, optional) – Directory containing plasma surface files. If None, uses repo-relative plasma_surfaces/.

Returns:

Error messages. Empty list means validation passed.

Return type:

list[str]

stellcoilbench.validate_config.validate_ci_case(data: dict[str, Any], policy: dict[str, Any] | None = None, file_path: Path | None = None) list[str]

Validate a CI autopilot case JSON dictionary.

The CI case wraps a standard case config inside a case_config key and adds case_id, resource, and optional parent_ids / tags / random_seed fields.

Parameters:
  • data (dict) – Parsed JSON for the CI case.

  • policy (dict, optional) – Proposer policy (from policy/proposer_policy.yaml). If provided, resource caps are taken from policy["resource_caps"].

  • file_path (Path, optional) – Used for error-message prefixes.

Returns:

Error messages. Empty list means validation passed.

Return type:

list[str]

stellcoilbench.validate_config.validate_ci_case_file(file_path: Path, policy: dict[str, Any] | None = None) list[str]

Validate a CI autopilot case JSON file on disk.

Loads the file with json.load, then delegates to validate_ci_case.

Parameters:
  • file_path (Path) – Path to the CI case JSON file.

  • policy (dict[str, Any], optional) – Proposer policy for resource caps.

Returns:

Error messages. Empty list means validation passed.

Return type:

list[str]

The validate_config module provides configuration validation.

validate_case_config(data: Dict[str, Any], file_path: Path | None = None, surfaces_dir: Path | None = None) -> List[str]

Validate a case configuration dictionary.

Checks for:

  • Required fields

  • Valid surface names

  • Valid algorithm names

  • Valid objective term options

  • Type correctness

Parameters:

  • data: Configuration dictionary

  • file_path: Optional file path for error messages

  • surfaces_dir: Optional directory for surface file existence checks

Returns:

  • List[str]: List of error messages (empty if valid)

validate_case_yaml_file(file_path: Path, surfaces_dir: Path | None = None) -> List[str]

Validate a case YAML file on disk. Loads with load_yaml, then delegates to validate_case_config.

CLI Module

Typer CLI package for StellCoilBench.

Provides commands: submit-case, run-case, run-ci-case, update-db, generate-submission, post-process. Subcommand implementations live in sibling modules; this module wires the app and exports the public API.

stellcoilbench.cli.main() None

The cli module implements the command-line interface.

Public vs internal: Commands (submit_case, run_case, etc.) and app are public. Helpers such as _detect_github_username, _zip_submission_directory, NumpyJSONEncoder are re-exported for tests and advanced use but may change.

app

Typer application instance. Commands: validate-config, list-cases, submit-case, run-case, run-ci-case, update-db, generate-submission, post-process, sensitivity.

NumpyJSONEncoder

Custom JSON encoder that handles numpy types and arrays. Used for serializing results to JSON.

validate_config_cmd — CLI command: Validate a case YAML configuration.

list_cases — CLI command: List available benchmark cases from cases/*.yaml.

submit_case — CLI command: Run a case and create a submission.

run_case — CLI command: Run a case without creating a submission.

run_ci_case — CLI command: Run a case in CI mode (optimization only).

generate_submission — CLI command: Create a submission from existing results.

Packages coils.json and metadata.yaml into a results.json submission. Uses a placeholder chi2_Bn: 0.001 for metrics (external results are not recomputed). For full metric computation, run submit-case or post-process first. Expected metadata.yaml keys: method_version, contact, hardware.

post_process — CLI command: Run post-processing on coils (Poincaré, VMEC, etc.).

update_db_cmd — CLI command: Regenerate leaderboards.

sensitivity_cmd — CLI command: Run coil sensitivity analysis.

See Overview for usage details.

Usage Examples

Running Optimization Programmatically

from pathlib import Path
from stellcoilbench.case_loader import load_case
from stellcoilbench.coil_optimization import optimize_coils

# Load case
case_path = Path("cases/basic_LandremanPaulQA.yaml")
case_cfg = load_case(case_path)

# Run optimization (returns metrics directly)
results = optimize_coils(
    case_path=case_path,
    coils_out_path=Path("output/coils.json"),
    case_cfg=case_cfg,
    output_dir=Path("output/")
)

# Results contain metrics from the optimization pipeline
print(f"Results: {results}")

Creating Custom Objective Terms

from stellcoilbench.coil_optimization import LinearPenalty
from simsopt.objectives import Weight

# Create a custom penalty term
penalty = LinearPenalty(
    func=lambda x: compute_something(x),
    threshold=1.0,
    penalty_type="l2_threshold"
)

# Scale with weight
weighted_penalty = Weight(1e-3) * penalty

# Add to objective
objective = flux_term + weighted_penalty

Updating Leaderboards Programmatically

from pathlib import Path
from stellcoilbench.update_db import update_database

# Update leaderboards
update_database(
    repo_root=Path("."),
    submissions_root=Path("submissions/"),
    docs_dir=Path("docs/")
)

Next Steps