Thank you for your interest in contributing to SCPN Fusion Core -- a neuro-symbolic tokamak simulation and control framework. Contributions from the fusion plasma physics, scientific computing, and open-source communities are welcome and valued.
Please read this guide carefully before submitting your first pull request.
- Code of Conduct
- Getting Started
- Development Environment
- Code Style
- Testing Requirements
- Branch Naming Conventions
- Commit Message Format
- Pull Request Process
- Reporting Issues
- Architecture Overview
- Priority Areas for Contribution
- License Implications
This project follows the Contributor Covenant v2.1. By participating, you agree to uphold a welcoming, inclusive, and harassment-free environment for everyone. Violations can be reported to protoscience@anulum.li.
- Fork the repository on GitHub.
- Clone your fork:
git clone https://github.com/<your-username>/scpn-fusion-core.git cd scpn-fusion-core
- Create a feature branch from
main:git checkout -b feature/your-feature-name
- Install in development mode:
pip install -e ".[dev]" - Verify your setup by running the test suite:
pytest tests/ -v
Required: Python 3.9 or later.
# Create a virtual environment (recommended)
python -m venv .venv
source .venv/bin/activate # Linux/macOS
.venv\Scripts\activate # Windows
# Install with development dependencies
pip install -e ".[dev]"
# Verify installation
python -c "import scpn_fusion; print(scpn_fusion.__version__)"
pytest tests/ -vModel weights and reference .npz/.npy artifacts are tracked with Git LFS.
Set this up once per machine:
git lfs install
git lfs pullBefore pushing, run:
python tools/check_lfs_hygiene.pyThis is also enforced by local pre-commit/pre-push guards.
The Rust workspace provides high-performance kernels. All functionality works without Rust via NumPy/SciPy fallbacks. If you want to contribute to the Rust codebase:
Required: Rust stable toolchain (1.75+), installed via rustup.
cd scpn-fusion-rs
# Build
cargo build --release
# Test
cargo test --all-features
# Format check
cargo fmt --all -- --check
# Lint (zero warnings policy)
cargo clippy --all-targets --all-features -- -D warnings
# Build Python bindings (requires maturin)
pip install maturin
cd crates/fusion-python
maturin develop --releaseFor a reproducible environment without local toolchain setup:
docker build --build-arg INSTALL_DEV=1 -t scpn-fusion-core:dev .
docker run scpn-fusion-core:dev pytest tests/ -v| Tool | Purpose | Configuration |
|---|---|---|
| black | Formatter | Default settings (line length 88) |
| ruff | Linter | Default rule set |
# Format
black src/ tests/
# Lint
ruff check src/ tests/# Optional: install pre-commit hooks for local parity with CI style gates
pip install pre-commit
pre-commit install
pre-commit run --all-files
# Required: enable repository-native git hooks (commit + push guards)
git config core.hooksPath .githooks
# Ensure hook scripts are executable (Linux/macOS/Git Bash)
chmod +x .githooks/pre-commit .githooks/pre-pushAdditional conventions:
- Type hints: Use on all public API functions and class methods.
- Docstrings: NumPy or Google style. All public functions, classes, and modules must have docstrings.
- Imports: Standard library, then third-party, then local -- separated by blank lines. Use absolute imports within the package.
| Tool | Purpose | Policy |
|---|---|---|
rustfmt |
Formatter | Default settings; run cargo fmt before every commit |
clippy |
Linter | Zero warnings (-D warnings); no #[allow] without justification |
cargo fmt --all
cargo clippy --all-targets --all-features -- -D warningsAdditional conventions:
- All
pubitems must have///doc comments. - Avoid
unwrap()andexpect()in library code. UseFusionResult<T>for fallible operations. - Use
thiserrorfor error type definitions.
Every new source file must include a copyright header at the top. Use the following template:
Python:
# SCPN Fusion Core
# Copyright concepts (c) 1996-2026 Miroslav Sotek. All rights reserved.
# Copyright code (c) 2023-2026 Miroslav Sotek. All rights reserved.
# ORCID: https://orcid.org/0009-0009-3560-0851
# Contact: www.anulum.li | protoscience@anulum.li
# License: GNU AGPL v3 | Commercial licensing availableRust:
// SCPN Fusion Core
// Copyright concepts (c) 1996-2026 Miroslav Sotek. All rights reserved.
// Copyright code (c) 2023-2026 Miroslav Sotek. All rights reserved.
// ORCID: https://orcid.org/0009-0009-3560-0851
// Contact: www.anulum.li | protoscience@anulum.li
// License: GNU AGPL v3 | Commercial licensing availableAll tests must pass before a pull request will be reviewed.
# Run the full test suite
pytest tests/ -v
# Run strict typing with CI-parity settings
python tools/run_mypy_strict.py
# Run a specific test file
pytest tests/test_physics.py -v
# Run with coverage reporting
pytest tests/ -v --cov=scpn_fusion --cov-report=term-missing
# Run local CI-parity gate before every push (recommended default)
python tools/run_python_preflight.py --gate release
# Optional stricter local gate (release + research lane)
python tools/run_python_preflight.py --gate all- Never push without a green local preflight gate (
releaseat minimum). - Never use
--no-verifyto bypass hooks except for incident recovery; if used, document reason indocs/CI_FAILURE_LEDGER.md. - Treat
drift detectedandmanifest is staleas local process failures; regenerate artifacts and re-run preflight before push. - Treat infrastructure-only errors (e.g., upstream HTTP 500 from GitHub services) as transient and re-run CI before changing code.
- Keep
core.hooksPath=.githooksconfigured in every local clone.
cd scpn-fusion-rs
cargo test --all-features
# Run benchmarks (optional, for performance-related changes)
cargo bench- New features: Add unit tests that cover the happy path, edge cases, and expected error conditions.
- Bug fixes: Add a regression test that fails without the fix and passes with it.
- Physics modules: Include at least one numerical invariant test (e.g., conservation law, symmetry property, known analytical solution).
- Property-based tests: Use Hypothesis (Python) or proptest (Rust) for numerical routines where applicable.
The CI pipeline runs on every push and pull request. It checks:
black --checkandruff check(Python formatting/linting)python tools/run_mypy_strict.py(strict Python typing)cargo fmt --checkandcargo clippy(Rust formatting/linting)pytest tests/(Python test suite)cargo test(Rust test suite)cargo audit(Rust dependency vulnerability scan)
Your PR will not be merged until all CI checks pass.
Operational handover notes under .handovers/ are strictly local-only working
artifacts and must not be committed or pushed to public remotes.
If you generate local handover notes, keep them under .handovers/ and verify
they are excluded from git status before committing.
Use descriptive branch names with the following prefixes:
| Prefix | Use Case | Example |
|---|---|---|
feature/ |
New functionality | feature/multigrid-v-cycle |
fix/ |
Bug fixes | fix/sor-convergence-check |
docs/ |
Documentation only | docs/solver-tuning-examples |
refactor/ |
Code restructuring (no behavior change) | refactor/transport-module-split |
test/ |
Test additions or improvements | test/property-based-inverse |
bench/ |
Benchmarking additions | bench/criterion-fft-kernel |
ci/ |
CI/CD pipeline changes | ci/add-coverage-reporting |
Always branch from main. Keep branches focused on a single concern.
Use clear, descriptive commit messages following this pattern:
<type>(<scope>): <short summary>
<optional body explaining WHY, not WHAT>
<optional footer with references>
Types: feat, fix, docs, test, refactor, bench, ci, chore
Scope: The module or area affected (e.g., kernel, transport, scpn,
fusion-core, ci).
Examples:
feat(transport): add neoclassical bootstrap current model
Implements the Sauter et al. (1999) bootstrap current formula
for use in the integrated transport solver. Validated against
Table 2 of the original paper.
Refs: Sauter et al., Phys. Plasmas 6, 2834 (1999)
fix(inverse): prevent singular Jacobian in Levenberg-Marquardt
The LM solver could encounter a singular Jacobian when coil
currents were near zero. Added a Tikhonov regularization floor
of 1e-8 to prevent division by zero.
Fixes #42
test(stability): add proptest for Mercier criterion symmetry
- Rebase on latest
mainto avoid merge conflicts. - Run the full test suite locally (
pytest tests/ -vandcargo test). - Format and lint your code (
black,ruff,cargo fmt,cargo clippy). - Update documentation if you changed any public API.
- Review your own diff -- remove debugging artifacts, unrelated changes, and commented-out code.
- Focused scope: One feature, one fix, or one refactor per PR. Do not mix unrelated changes.
- Tests: Include tests that fail without your change and pass with it.
- Documentation: Update docstrings, Sphinx docs, or rustdoc for any public API changes.
- No regressions: All existing tests must continue to pass.
- Descriptive title and body: Summarize the change, explain why it is needed, and link any related issues.
- At least one maintainer approval is required before merge.
- Reviewers may request changes. Please address all comments or explain why you disagree.
- Once approved, a maintainer will merge using squash-merge to keep the commit history clean.
- The CI pipeline must be fully green before merge.
- Focused scope (one logical change).
- Tests that demonstrate the change works.
- No unrelated formatting or whitespace changes.
- Clear commit messages that explain why, not just what.
- Links to relevant issues, papers, or external references.
Use our issue templates:
- Bug Report: Include reproduction steps, environment details, and full error output.
- Feature Request: Describe the problem being solved and the proposed approach, including any relevant physics or engineering context.
- Security Vulnerability: Do not open a public issue. See SECURITY.md for responsible disclosure instructions.
Understanding the project structure will help you contribute effectively:
src/scpn_fusion/-- Python package (src layout), the primary interface.scpn-fusion-rs/-- Rust workspace mirroring the Python structure for performance-critical paths.scpn-fusion-rs/crates/fusion-python/-- PyO3 bindings connecting Rust to Python.scpn/-- Neuro-symbolic compiler (Petri nets to stochastic neurons).validation/-- Reference data from real tokamaks (SPARC, ITPA, ITER).tests/-- Python test suite.docs/-- Technical documentation, solver guides, benchmark reports.examples/-- Jupyter notebooks and standalone scripts.
For a detailed architecture diagram, see the README.
We especially welcome contributions in these areas:
| Area | Examples | Skill Level |
|---|---|---|
| Benchmarks | Criterion.rs benchmarks, Python timing comparisons | Intermediate |
| Validation | Cross-checking against published tokamak data (JET, DIII-D, ITER) | Advanced |
| Solver improvements | SOR/multigrid upgrades, adaptive mesh refinement | Advanced |
| Documentation | Tutorials, Jupyter notebooks, docstring improvements | Beginner |
| Testing | Increasing coverage, property-based testing (Hypothesis/proptest) | Beginner-Intermediate |
| GPU acceleration | CUDA/OpenCL offload for SOR, FFT, transport solvers | Advanced |
| Data integration | IMAS IDS import/export, MDSplus readers, CHEASE format | Intermediate |
Look for issues tagged good first issue
on our issue tracker. Some ideas:
- Add GEQDSK test cases from publicly available tokamak data (MAST-U, W7-X).
- Add Hypothesis property tests for numerical routines in
core/. - Write a new tutorial notebook demonstrating a specific physics module.
- Reduce P0/P1 entries in
UNDERDEVELOPED_REGISTER.md(see Executive Summary for current counts). - Contribute real experimental validation data (equilibria, confinement profiles).
SCPN Fusion Core is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0-or-later). By submitting a pull request, you agree to the following:
- Your contributions will be licensed under the AGPL-3.0-or-later, the same license as the rest of the project. You retain copyright on your contributions.
- You certify that you have the right to submit the contribution under this license (i.e., it is your original work, or you have permission from the copyright holder).
- Derivative works that incorporate your contribution must also be released under the AGPL-3.0-or-later when distributed or made available over a network.
- Commercial licensing is available separately from the project maintainer for organizations that cannot comply with AGPL requirements. Contact protoscience@anulum.li for details.
If you have questions about licensing, please ask before submitting your contribution.
- Check the documentation and README first.
- Ask in GitHub Discussions (Q&A category).
- Open a bug report or feature request.
- Email the maintainer at protoscience@anulum.li.
Thank you for helping improve fusion plasma simulation and control.
The Python source tree (src/scpn_fusion/) is organised into subsystems.
When contributing, target the appropriate subsystem and follow existing patterns
within that directory.
| Subsystem | Files | Scope |
|---|---|---|
core/ |
118 | GS equilibrium, transport, GK three-path, MHD stability, disruptions, edge physics, neural surrogates, JAX solvers, scenario simulation, 3D geometry, UQ |
control/ |
54 | PID, H-infinity, mu-synthesis, NMPC, SNN, RL, free-boundary tracking, disruption mitigation, burn control, flight sim, HiL |
phase/ |
10 | Kuramoto UPDE solver, adaptive K_nm, GK-to-UPDE bridge, plasma coupling, Lyapunov guard |
scpn/ |
12 | Petri net compiler, formal contracts, safety interlocks, artifact management |
io/ |
15 | IMAS connector/adapter, GEQDSK I/O, tokamak archive, logging |
diagnostics/ |
5 | Synthetic diagnostic forward models, sensors, tomography |
engineering/ |
4 | Balance of plant, CAD raytrace, thermal hydraulics |
nuclear/ |
5 | Blanket neutronics, PWI erosion, tritium breeding |
hpc/ |
2 | C++ solver bridge, HPC types |
ui/ |
4 | Streamlit dashboard, generator, launcher |
Rust crates (scpn-fusion-rs/crates/): 11 crates mirroring the Python
structure. See ARCHITECTURE.md for the full crate map.
Total: 234 Python source files, 62,570 lines, 11 Rust crates, 2859 tests.
All session logs and handover documents for this project are stored permanently in the monorepo's canonical location:
- Session logs:
.coordination/sessions/SCPN-Fusion-Core/ - Handovers:
.coordination/handovers/SCPN-Fusion-Core/
Do not place session logs or handovers inside this project directory. They will be moved during consolidation.
These files are permanent records and must never be deleted, even if outdated.
See .coordination/SESSION_AND_HANDOVER_POLICY.md for naming conventions, templates, and the full policy.