|
| 1 | +# Copilot Instructions for OpenFUSIONToolkit |
| 2 | + |
| 3 | +## Project Overview |
| 4 | + |
| 5 | +The Open FUSION Toolkit (OFT) is a scientific computing suite for plasma and fusion research. It provides finite element methods on unstructured 2D/3D meshes for MHD equilibrium, stability, and time-dependent simulations. |
| 6 | + |
| 7 | +The codebase is a hybrid Fortran/C/C++/Python project: |
| 8 | +- **Core computational code**: Fortran 90 (`.F90` files) with C/C++ bridge files |
| 9 | +- **Python interface**: `ctypes`-based wrappers calling into compiled Fortran shared libraries |
| 10 | +- **Build system**: CMake (requires ≥ 3.27), orchestrated by `src/utilities/build_libs.py` |
| 11 | + |
| 12 | +### Component Tools |
| 13 | + |
| 14 | +| Tool | Purpose | Key Fortran Source | Python Module | |
| 15 | +|---|---|---|---| |
| 16 | +| **TokaMaker** | Axisymmetric Grad-Shafranov MHD equilibria | `src/physics/grad_shaf*.F90` | `OpenFUSIONToolkit.TokaMaker` | |
| 17 | +| **ThinCurr** | Thin-wall eddy current modeling | `src/physics/thin_wall*.F90` | `OpenFUSIONToolkit.ThinCurr` | |
| 18 | +| **Marklin** | 3D force-free ideal MHD equilibria | `src/physics/taylor.F90` | `OpenFUSIONToolkit.Marklin` | |
| 19 | +| **MUG** | Time-dependent extended MHD | `src/physics/xmhd*.F90` | *(Fortran executables only)* | |
| 20 | + |
| 21 | +## Repository Layout |
| 22 | + |
| 23 | +``` |
| 24 | +src/ |
| 25 | +├── base/ # Core runtime (I/O, XML, sorting, stitching) |
| 26 | +├── grid/ # Mesh types, readers (Cubit, Gmsh, native, T3D), multigrid |
| 27 | +├── fem/ # Finite element basis functions and operators (H1, Hcurl, Lagrange) |
| 28 | +├── lin_alg/ # Linear algebra (native solvers, PETSc, ARPACK, SuperLU, UMFPACK) |
| 29 | +├── physics/ # Physics modules (Grad-Shafranov, thin-wall, Taylor, xMHD, tracing) |
| 30 | +├── bin/ # Standalone Fortran executables |
| 31 | +├── python/ |
| 32 | +│ ├── OpenFUSIONToolkit/ # Python package (ctypes wrappers) |
| 33 | +│ │ ├── TokaMaker/ # TokaMaker Python API |
| 34 | +│ │ ├── ThinCurr/ # ThinCurr Python API |
| 35 | +│ │ └── Marklin/ # Marklin Python API |
| 36 | +│ └── wrappers/ # Fortran-side C-interop wrapper subroutines |
| 37 | +├── ext_libs/ # Bundled 3rd-party sources (triangle, minpack, bvls, dlsode) |
| 38 | +├── tests/ # Regression tests (pytest-driven, Fortran + Python) |
| 39 | +├── examples/ # Jupyter notebook examples per tool |
| 40 | +├── utilities/ # Build scripts, code generators, helper tools |
| 41 | +├── docs/ # Doxygen documentation sources |
| 42 | +├── cmake/ # CMake find-modules for external dependencies |
| 43 | +├── include/ # C/Fortran header files |
| 44 | +└── CMakeLists.txt # Top-level CMake configuration |
| 45 | +``` |
| 46 | + |
| 47 | +## Build System |
| 48 | + |
| 49 | +OFT uses a two-stage build process: |
| 50 | + |
| 51 | +### Stage 1: Build external libraries |
| 52 | + |
| 53 | +```bash |
| 54 | +mkdir builds && cd builds |
| 55 | +source ../setup_env.sh # activates Python venv |
| 56 | +python ../src/utilities/build_libs.py \ |
| 57 | + --nthread=4 --build_umfpack=1 --build_superlu=1 \ |
| 58 | + --build_arpack=1 --oft_build_tests=1 --build_mpich=1 |
| 59 | +``` |
| 60 | + |
| 61 | +This downloads and compiles dependencies (OpenBLAS, HDF5, METIS, etc.) and generates `config_cmake.sh` — a shell script containing the full CMake invocation with all paths. |
| 62 | + |
| 63 | +### Stage 2: Configure, build, and install OFT |
| 64 | + |
| 65 | +```bash |
| 66 | +# Still in builds/ |
| 67 | +bash config_cmake.sh # runs cmake, creates builds/build_release/ |
| 68 | +cd build_release |
| 69 | +make # compile |
| 70 | +make install # install to builds/install_release/ |
| 71 | +``` |
| 72 | + |
| 73 | +### Key CMake options (set via build_libs.py flags) |
| 74 | + |
| 75 | +- `OFT_BUILD_TESTS` — build test executables (enable with `--oft_build_tests=1`) |
| 76 | +- `OFT_BUILD_PYTHON` — build Python wrappers (default ON) |
| 77 | +- `OFT_USE_MPI` — enable MPI parallelism (set by `--build_mpich=1` or `--build_openmpi=1`) |
| 78 | +- `OFT_BUILD_DOCS` — build Doxygen documentation |
| 79 | +- `OFT_BUILD_EXAMPLES` — build example programs |
| 80 | + |
| 81 | +### Important Environment Notes |
| 82 | + |
| 83 | +- The project uses a Python virtual environment at `oft_venv/`. **Always** `source setup_env.sh` before running builds or tests. |
| 84 | +- The `copilot-setup-steps.yml` workflow mirrors the Ubuntu 24.04 GCC 14 + OpenMP CI configuration and pre-builds external libraries in a cached `builds/` directory. After this workflow runs, the agent environment has: |
| 85 | + - Compilers: `gcc-14`, `g++-14`, `gfortran-14` |
| 86 | + - Python venv with: `pytest`, `numpy`, `scipy`, `h5py`, `matplotlib`, `xarray` |
| 87 | + - Pre-built external libraries in `builds/` |
| 88 | + - OFT compiled and installed in `builds/install_release/` |
| 89 | + |
| 90 | +## Testing |
| 91 | + |
| 92 | +Tests use **pytest** and are organized under `src/tests/` in subdirectories matching the source layout: `base/`, `grid/`, `lin_alg/`, `fem/`, `physics/`. |
| 93 | + |
| 94 | +### Test structure |
| 95 | + |
| 96 | +Each test subdirectory has: |
| 97 | +- `test_*.py` — pytest test files |
| 98 | +- `test_*.F90` — corresponding Fortran test programs (compiled during build) |
| 99 | +- Various data files (`.h5`, `.g`, `.inp`, etc.) |
| 100 | + |
| 101 | +### Running tests |
| 102 | + |
| 103 | +From `builds/build_release/`: |
| 104 | + |
| 105 | +```bash |
| 106 | +source ../../setup_env.sh |
| 107 | +make test # runs: pytest -m "not slow" base grid lin_alg fem physics |
| 108 | +make test_full # runs all tests including slow ones |
| 109 | +make test_examples # runs example notebook tests |
| 110 | +``` |
| 111 | + |
| 112 | +Or run individual test files: |
| 113 | + |
| 114 | +```bash |
| 115 | +cd builds/build_release/tests |
| 116 | +../../run_test.sh physics/test_TokaMaker.py -k "test_name" |
| 117 | +../../run_test.sh physics/test_TokaMaker.py --collect-only |
| 118 | +``` |
| 119 | + |
| 120 | +### Test conventions |
| 121 | + |
| 122 | +- Tests marked `@pytest.mark.slow` are excluded from default CI runs |
| 123 | +- Tests marked `@pytest.mark.mpi` require MPI (`OFT_HAVE_MPI=1`) |
| 124 | +- Tests marked `@pytest.mark.coverage` are for code coverage runs |
| 125 | +- `oft_testing.py` provides `run_OFT()` helper for running Fortran executables with timeout |
| 126 | +- Physics Python tests (TokaMaker, ThinCurr) use `multiprocessing.Process` to isolate OFT runtime (only one `OFT_env` instance per process) |
| 127 | +- Test timeout is multiplied by 4× when `OFT_DEBUG_TEST=1` |
| 128 | + |
| 129 | +## Linting |
| 130 | + |
| 131 | +### Python linting |
| 132 | + |
| 133 | +Python code is linted with **ruff**. Configuration is in `src/python/pyproject.toml`: |
| 134 | + |
| 135 | +```bash |
| 136 | +cd src/python && ruff check |
| 137 | +``` |
| 138 | + |
| 139 | +Rules: Pyflakes (`F`) + pycodestyle (`E`) with ignores for `E722`, `F403`, `F405`. Target: Python 3.7. |
| 140 | + |
| 141 | +### Fortran stack checking |
| 142 | + |
| 143 | +A custom lint checks Fortran debug stack entries: |
| 144 | + |
| 145 | +```bash |
| 146 | +cd src && python utilities/generate_stack.py -l |
| 147 | +``` |
| 148 | + |
| 149 | +This validates that all `SUBROUTINE`/`FUNCTION` entries have matching debug stack annotations. Run from the `src/` directory. |
| 150 | + |
| 151 | +## CI Workflows |
| 152 | + |
| 153 | +| Workflow | File | Trigger | Purpose | |
| 154 | +|---|---|---|---| |
| 155 | +| **CI Build** | `ci_build.yaml` | push to main, PRs | Full matrix build (GCC, Intel, macOS) × (OpenMP, MPICH, OpenMPI) | |
| 156 | +| **Lint** | `lint.yaml` | push to main, PRs | ruff check + Fortran stack check | |
| 157 | +| **Coverage** | `cov_build.yaml` | push to main | Build with `--coverage`, upload to Codecov | |
| 158 | +| **CD Nightly** | `cd_nightly.yaml` | schedule | Nightly package builds | |
| 159 | +| **Copilot Setup** | `copilot-setup-steps.yml` | manual/PR | Agent environment setup | |
| 160 | + |
| 161 | +### CI configuration used in copilot-setup-steps |
| 162 | + |
| 163 | +- **OS**: Ubuntu 24.04 |
| 164 | +- **Compilers**: `gcc-14` / `g++-14` / `gfortran-14` |
| 165 | +- **Parallel**: OpenMP only (no MPI) |
| 166 | +- **Libraries**: OpenBLAS, UMFPACK, SuperLU, ARPACK, HDF5, METIS |
| 167 | + |
| 168 | +## Coding Conventions |
| 169 | + |
| 170 | +### Fortran |
| 171 | + |
| 172 | +- Free-form Fortran 90+ (`.F90` extension, preprocessed) |
| 173 | +- SPDX license header: `! SPDX-License-Identifier: LGPL-3.0-only` |
| 174 | +- Doxygen-style comments with `!>` and `!!` markers |
| 175 | +- Module names typically prefixed with `oft_` (e.g., `oft_gs`, `oft_la_base`) |
| 176 | +- Line length: unlimited (`-ffree-line-length-none`), but keep reasonable |
| 177 | +- New functions/subroutines must have Doxygen documentation |
| 178 | + |
| 179 | +### Python |
| 180 | + |
| 181 | +- SPDX license header comment block at top of each file |
| 182 | +- Doxygen-style docstrings with `@param`, `@result`, `@authors`, `@date` |
| 183 | +- Target Python 3.7+ (for OMFIT compatibility) |
| 184 | +- Use `ctypes` for Fortran interop; wrapper patterns in `_interface.py` files |
| 185 | +- All Python wrappers live under `src/python/OpenFUSIONToolkit/` |
| 186 | + |
| 187 | +### Pull Requests |
| 188 | + |
| 189 | +- PRs for a specific tool should be titled: `ToolName: description` (e.g., `TokaMaker: Fix boundary condition`) |
| 190 | +- Keep changes focused on a single feature; secondary changes should be minimal |
| 191 | +- Comment on whether APIs or input files change |
| 192 | +- Run regression tests before submitting |
| 193 | + |
| 194 | +## Common Pitfalls |
| 195 | + |
| 196 | +1. **Single OFT_env instance**: The Python `OFT_env` class enforces a singleton. Tests use `multiprocessing.Process` to work around this. Never create two `OFT_env` instances in the same process. |
| 197 | + |
| 198 | +2. **Source setup_env.sh**: Always source this before any build/test commands. It activates the Python venv. In CI, this file is generated during prerequisites setup. |
| 199 | + |
| 200 | +3. **Build from `builds/` directory**: The `build_libs.py` script must run from the `builds/` directory. It creates `config_cmake.sh` there. CMake then creates `build_release/` and `install_release/` inside `builds/`. |
| 201 | + |
| 202 | +4. **Tests run from build tree**: Tests must be run from `builds/build_release/tests/` (or via `make test` from `builds/build_release/`), not from the source tree, because compiled Fortran test executables are in the build tree. |
| 203 | + |
| 204 | +5. **ext_libs/ is bundled third-party code**: Do not modify files in `src/ext_libs/`. These are upstream sources (triangle, minpack, bvls, dlsode). |
| 205 | + |
| 206 | +6. **Fortran/Python interop**: The Python package calls compiled Fortran via `ctypes`. The Fortran-side wrappers are in `src/python/wrappers/` and use `ISO_C_BINDING`. Changes to Fortran function signatures require corresponding updates to both `wrappers/*_f.F90` and `python/OpenFUSIONToolkit/*/_interface.py`. |
| 207 | + |
| 208 | +7. **CMake template files**: Some files use `@VARIABLE@` CMake substitution (e.g., `__init__.py`, `pyproject.toml.in`, `run_test.sh.in`). Edit the `.in` template, not the generated file. |
0 commit comments