├── .clang-format
├── .clang-tidy
├── .github
└── workflows
│ ├── clang_tidy.yaml
│ ├── docker.yaml
│ ├── docs.yaml
│ ├── pypi_publish.yml
│ ├── pyright.yaml
│ ├── test_bazel.yaml
│ ├── test_conda_env.yaml
│ └── tests.yaml
├── .gitignore
├── .gitmodules
├── .pre-commit-config.yaml
├── AGENTS.md
├── CITATION.cff
├── CMakeLists.txt
├── LICENSE.txt
├── README.md
├── VMECPP_NAMING_GUIDE.md
├── docker
├── Dockerfile
└── README.md
├── docs
├── .gitattributes
├── conf.py
├── index.md
├── proxima_logo_dark_mode.png
├── proxima_logo_light_mode.png
└── the_numerics_of_vmecpp.pdf
├── environment.yml
├── examples
├── compare_vmecpp_to_parvmec.py
├── data
│ ├── .gitignore
│ ├── README.md
│ ├── cth_like_fixed_bdy.json
│ ├── input.cth_like_fixed_bdy
│ ├── input.nfp4_QH_warm_start
│ ├── input.solovev
│ ├── input.w7x
│ ├── solovev.json
│ ├── w7x.json
│ └── wout_cth_like_fixed_bdy.nc
├── freeboundary_run_and_hot_restart.py
├── hot_restart_scaling.py
├── mpi_finite_difference.py
├── normal_run_and_hot_restart.py
├── plot_plasma_boundary.py
├── python_api.py
├── python_api_input.py
├── sample_hot_restarts_with_random_perturbations.py
├── simsopt_integration.py
└── simsopt_qh_fixed_resolution.py
├── pyproject.toml
├── src
├── CMakeLists.txt
└── vmecpp
│ ├── CMakeLists.txt
│ ├── __init__.py
│ ├── __main__.py
│ ├── _free_boundary.py
│ ├── _pydantic_numpy.py
│ ├── _util.py
│ ├── cpp
│ ├── .bazelrc
│ ├── .bazelversion
│ ├── .gitattributes
│ ├── CMakeLists.txt
│ ├── MODULE.bazel
│ ├── MODULE.bazel.lock
│ ├── WORKSPACE.bazel
│ ├── third_party
│ │ ├── hdf5
│ │ │ └── BUILD.bazel
│ │ └── netcdf4
│ │ │ └── BUILD.bazel
│ ├── util
│ │ ├── CMakeLists.txt
│ │ ├── file_io
│ │ │ ├── BUILD.bazel
│ │ │ ├── CMakeLists.txt
│ │ │ ├── file_io.cc
│ │ │ ├── file_io.h
│ │ │ └── file_io_test.cc
│ │ ├── hdf5_io
│ │ │ ├── BUILD.bazel
│ │ │ ├── CMakeLists.txt
│ │ │ ├── hdf5_io.cc
│ │ │ └── hdf5_io.h
│ │ ├── json_io
│ │ │ ├── BUILD.bazel
│ │ │ ├── CMakeLists.txt
│ │ │ ├── json_io.cc
│ │ │ ├── json_io.h
│ │ │ └── json_io_test.cc
│ │ ├── netcdf_io
│ │ │ ├── BUILD.bazel
│ │ │ ├── CMakeLists.txt
│ │ │ ├── example_netcdf.nc
│ │ │ ├── netcdf_io.cc
│ │ │ ├── netcdf_io.h
│ │ │ └── netcdf_io_test.cc
│ │ ├── test_data
│ │ │ ├── BUILD.bazel
│ │ │ ├── empty.txt
│ │ │ └── lorem.txt
│ │ └── testing
│ │ │ ├── BUILD.bazel
│ │ │ ├── CMakeLists.txt
│ │ │ ├── numerical_comparison_lib.cc
│ │ │ ├── numerical_comparison_lib.h
│ │ │ └── numerical_comparison_test.cc
│ └── vmecpp
│ │ ├── CMakeLists.txt
│ │ ├── common
│ │ ├── CMakeLists.txt
│ │ ├── composed_types_definition
│ │ │ ├── BUILD.bazel
│ │ │ ├── CMakeLists.txt
│ │ │ └── composed_types.h
│ │ ├── composed_types_lib
│ │ │ ├── BUILD.bazel
│ │ │ ├── CMakeLists.txt
│ │ │ ├── composed_types_lib.cc
│ │ │ ├── composed_types_lib.h
│ │ │ └── composed_types_lib_test.cc
│ │ ├── flow_control
│ │ │ ├── BUILD.bazel
│ │ │ ├── CMakeLists.txt
│ │ │ ├── flow_control.cc
│ │ │ └── flow_control.h
│ │ ├── fourier_basis_fast_poloidal
│ │ │ ├── BUILD.bazel
│ │ │ ├── CMakeLists.txt
│ │ │ ├── fourier_basis_fast_poloidal.cc
│ │ │ ├── fourier_basis_fast_poloidal.h
│ │ │ └── fourier_basis_fast_poloidal_test.cc
│ │ ├── fourier_basis_fast_toroidal
│ │ │ ├── BUILD.bazel
│ │ │ ├── CMakeLists.txt
│ │ │ ├── fourier_basis_fast_toroidal.cc
│ │ │ ├── fourier_basis_fast_toroidal.h
│ │ │ └── fourier_basis_fast_toroidal_test.cc
│ │ ├── magnetic_configuration_definition
│ │ │ ├── BUILD.bazel
│ │ │ ├── CMakeLists.txt
│ │ │ └── magnetic_configuration.h
│ │ ├── magnetic_configuration_lib
│ │ │ ├── BUILD.bazel
│ │ │ ├── CMakeLists.txt
│ │ │ ├── magnetic_configuration_lib.cc
│ │ │ ├── magnetic_configuration_lib.h
│ │ │ └── magnetic_configuration_lib_test.cc
│ │ ├── magnetic_field_provider
│ │ │ ├── BUILD.bazel
│ │ │ ├── CMakeLists.txt
│ │ │ ├── magnetic_field_provider_lib.cc
│ │ │ ├── magnetic_field_provider_lib.h
│ │ │ └── magnetic_field_provider_lib_test.cc
│ │ ├── makegrid_lib
│ │ │ ├── BUILD.bazel
│ │ │ ├── CMakeLists.txt
│ │ │ ├── create_reference_mgrid.sh
│ │ │ ├── makegrid_lib.cc
│ │ │ ├── makegrid_lib.h
│ │ │ ├── makegrid_lib_test.cc
│ │ │ └── test_data
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── coils.test_non_symmetric
│ │ │ │ ├── coils.test_symmetric_even
│ │ │ │ ├── coils.test_symmetric_odd
│ │ │ │ ├── extcur.test_non_symmetric
│ │ │ │ ├── extcur.test_symmetric_even
│ │ │ │ ├── extcur.test_symmetric_odd
│ │ │ │ ├── mgrid_test_non_symmetric.nc
│ │ │ │ ├── mgrid_test_symmetric_even.nc
│ │ │ │ └── mgrid_test_symmetric_odd.nc
│ │ ├── sizes
│ │ │ ├── BUILD.bazel
│ │ │ ├── CMakeLists.txt
│ │ │ ├── sizes.cc
│ │ │ └── sizes.h
│ │ ├── util
│ │ │ ├── BUILD.bazel
│ │ │ ├── CMakeLists.txt
│ │ │ ├── util.cc
│ │ │ ├── util.h
│ │ │ └── util_test.cc
│ │ └── vmec_indata
│ │ │ ├── BUILD.bazel
│ │ │ ├── CMakeLists.txt
│ │ │ ├── boundary_from_json.cc
│ │ │ ├── boundary_from_json.h
│ │ │ ├── vmec_indata.cc
│ │ │ ├── vmec_indata.h
│ │ │ └── vmec_indata_test.cc
│ │ ├── free_boundary
│ │ ├── CMakeLists.txt
│ │ ├── external_magnetic_field
│ │ │ ├── BUILD.bazel
│ │ │ ├── CMakeLists.txt
│ │ │ ├── external_magnetic_field.cc
│ │ │ └── external_magnetic_field.h
│ │ ├── free_boundary_base
│ │ │ ├── BUILD.bazel
│ │ │ ├── CMakeLists.txt
│ │ │ └── free_boundary_base.h
│ │ ├── laplace_solver
│ │ │ ├── BUILD.bazel
│ │ │ ├── CMakeLists.txt
│ │ │ ├── laplace_solver.cc
│ │ │ └── laplace_solver.h
│ │ ├── mgrid_provider
│ │ │ ├── BUILD.bazel
│ │ │ ├── CMakeLists.txt
│ │ │ ├── mgrid_provider.cc
│ │ │ └── mgrid_provider.h
│ │ ├── nestor
│ │ │ ├── BUILD.bazel
│ │ │ ├── CMakeLists.txt
│ │ │ ├── nestor.cc
│ │ │ └── nestor.h
│ │ ├── regularized_integrals
│ │ │ ├── BUILD.bazel
│ │ │ ├── CMakeLists.txt
│ │ │ ├── regularized_integrals.cc
│ │ │ └── regularized_integrals.h
│ │ ├── singular_integrals
│ │ │ ├── BUILD.bazel
│ │ │ ├── CMakeLists.txt
│ │ │ ├── singular_integrals.cc
│ │ │ ├── singular_integrals.h
│ │ │ └── singular_integrals_test.cc
│ │ ├── surface_geometry
│ │ │ ├── BUILD.bazel
│ │ │ ├── CMakeLists.txt
│ │ │ ├── surface_geometry.cc
│ │ │ ├── surface_geometry.h
│ │ │ └── surface_geometry_test.cc
│ │ ├── surface_geometry_mockup
│ │ │ ├── BUILD.bazel
│ │ │ ├── lcfs.SurfaceRZFourier.csv
│ │ │ ├── surface_geometry_mockup.cc
│ │ │ └── surface_geometry_mockup.h
│ │ └── tangential_partitioning
│ │ │ ├── BUILD.bazel
│ │ │ ├── CMakeLists.txt
│ │ │ ├── tangential_partitioning.cc
│ │ │ └── tangential_partitioning.h
│ │ ├── simsopt_compat
│ │ ├── README.md
│ │ ├── __init__.py
│ │ └── _indata_to_surfacerzfourier.py
│ │ ├── test_data
│ │ ├── BUILD.bazel
│ │ ├── circular_tokamak.json
│ │ ├── cma.json
│ │ ├── coils.cth_like
│ │ ├── cth_like_fixed_bdy.json
│ │ ├── cth_like_fixed_bdy_nzeta_37.json
│ │ ├── cth_like_free_bdy.json
│ │ ├── input.cma
│ │ ├── input.cth_like_fixed_bdy
│ │ ├── input.cth_like_fixed_bdy_nzeta_37
│ │ ├── input.cth_like_free_bdy
│ │ ├── input.li383_low_res
│ │ ├── input.solovev
│ │ ├── input.solovev_analytical
│ │ ├── input.solovev_no_axis
│ │ ├── jxbout_cma.nc
│ │ ├── li383_low_res.json
│ │ ├── makegrid_parameters_cth_like.json
│ │ ├── mgrid_cth_like.nc
│ │ ├── solovev.json
│ │ ├── solovev_analytical.json
│ │ ├── solovev_no_axis.json
│ │ ├── wout_LandremanSengupta2019_section5.4_B2_A80_reference.nc
│ │ ├── wout_circular_tokamak_reference.nc
│ │ ├── wout_cma.nc
│ │ ├── wout_cth_like_fixed_bdy.nc
│ │ ├── wout_cth_like_fixed_bdy_nzeta_37.nc
│ │ ├── wout_cth_like_free_bdy.nc
│ │ ├── wout_li383_low_res_reference.nc
│ │ ├── wout_solovev.nc
│ │ ├── wout_solovev_analytical.nc
│ │ └── wout_solovev_no_axis.nc
│ │ └── vmec
│ │ ├── CMakeLists.txt
│ │ ├── boundaries
│ │ ├── BUILD.bazel
│ │ ├── CMakeLists.txt
│ │ ├── boundaries.cc
│ │ ├── boundaries.h
│ │ ├── guess_magnetic_axis.cc
│ │ └── guess_magnetic_axis.h
│ │ ├── fourier_coefficients
│ │ ├── BUILD.bazel
│ │ ├── CMakeLists.txt
│ │ ├── fourier_coefficients.cc
│ │ └── fourier_coefficients.h
│ │ ├── fourier_forces
│ │ ├── BUILD.bazel
│ │ ├── CMakeLists.txt
│ │ ├── fourier_forces.cc
│ │ └── fourier_forces.h
│ │ ├── fourier_geometry
│ │ ├── BUILD.bazel
│ │ ├── CMakeLists.txt
│ │ ├── fourier_geometry.cc
│ │ └── fourier_geometry.h
│ │ ├── fourier_velocity
│ │ ├── BUILD.bazel
│ │ ├── CMakeLists.txt
│ │ ├── fourier_velocity.cc
│ │ └── fourier_velocity.h
│ │ ├── handover_storage
│ │ ├── BUILD.bazel
│ │ ├── CMakeLists.txt
│ │ ├── handover_storage.cc
│ │ └── handover_storage.h
│ │ ├── ideal_mhd_model
│ │ ├── BUILD.bazel
│ │ ├── CMakeLists.txt
│ │ ├── dft_data.h
│ │ ├── ideal_mhd_model.cc
│ │ └── ideal_mhd_model.h
│ │ ├── output_quantities
│ │ ├── BUILD.bazel
│ │ ├── CMakeLists.txt
│ │ ├── output_quantities.cc
│ │ ├── output_quantities.h
│ │ ├── output_quantities_io_test.cc
│ │ ├── output_quantities_test.cc
│ │ └── test_helpers.h
│ │ ├── profile_parameterization_data
│ │ ├── BUILD.bazel
│ │ ├── CMakeLists.txt
│ │ ├── profile_parameterization_data.cc
│ │ └── profile_parameterization_data.h
│ │ ├── pybind11
│ │ ├── .gitignore
│ │ ├── BUILD.bazel
│ │ ├── pybind_vmec.cc
│ │ ├── vmec_indata_pywrapper.cc
│ │ ├── vmec_indata_pywrapper.h
│ │ └── vmec_indata_pywrapper_test.cc
│ │ ├── radial_partitioning
│ │ ├── BUILD.bazel
│ │ ├── CMakeLists.txt
│ │ ├── radial_partitioning.cc
│ │ ├── radial_partitioning.h
│ │ └── radial_partitioning_test.cc
│ │ ├── radial_profiles
│ │ ├── BUILD.bazel
│ │ ├── CMakeLists.txt
│ │ ├── radial_profiles.cc
│ │ └── radial_profiles.h
│ │ ├── thread_local_storage
│ │ ├── BUILD.bazel
│ │ ├── CMakeLists.txt
│ │ ├── thread_local_storage.cc
│ │ └── thread_local_storage.h
│ │ ├── vmec
│ │ ├── BUILD.bazel
│ │ ├── CMakeLists.txt
│ │ ├── vmec.cc
│ │ ├── vmec.h
│ │ ├── vmec_in_memory_mgrid_test.cc
│ │ └── vmec_test.cc
│ │ ├── vmec_constants
│ │ ├── BUILD.bazel
│ │ ├── CMakeLists.txt
│ │ ├── vmec_algorithm_constants.h
│ │ ├── vmec_constants.cc
│ │ └── vmec_constants.h
│ │ └── vmec_standalone
│ │ ├── BUILD.bazel
│ │ └── vmec_standalone.cc
│ └── simsopt_compat.py
└── tests
├── cpp
└── vmecpp
│ ├── simsopt_compat
│ └── test_indata_to_surfacerzfourier.py
│ └── vmec
│ └── pybind11
│ └── test_pybind_vmec.py
├── test_free_boundary.py
├── test_init.py
├── test_pydantic_numpy.py
├── test_simsopt_compat.py
├── test_simsopt_vmec_ported.py
└── test_util.py
/.clang-format:
--------------------------------------------------------------------------------
1 | BasedOnStyle: Google
2 |
--------------------------------------------------------------------------------
/.github/workflows/clang_tidy.yaml:
--------------------------------------------------------------------------------
1 | name: clang-tidy-review
2 |
3 | on:
4 | # clang-tidy-review only works on PRs, not direct commits to main.
5 | pull_request:
6 | paths:
7 | - 'src/vmecpp/cpp/vmecpp/**/*.h'
8 | - 'src/vmecpp/cpp/vmecpp/**/*.cc'
9 |
10 | # Cancel currently running job if a new one comes along for the same branch or tag.
11 | # From https://stackoverflow.com/a/72408109.
12 | concurrency:
13 | group: ${{ github.workflow }}-${{ github.ref || github.run_id }}
14 | cancel-in-progress: ${{ github.ref_name != 'main' }}
15 |
16 | jobs:
17 | clang-tidy:
18 | permissions:
19 | checks: write
20 | pull-requests: write
21 | packages: read
22 | contents: read
23 | runs-on: ubuntu-22.04
24 | steps:
25 | - uses: actions/checkout@v4
26 | - uses: ZedThree/clang-tidy-review@v0.20.1
27 | name: Run clang-tidy review
28 | id: review
29 | with:
30 | config_file: ".clang-tidy"
31 | apt_packages: libhdf5-dev, liblapack-dev, libnetcdf-dev, gfortran, python3-dev, libomp-dev
32 | build_dir: build
33 | cmake_command: cmake -B build && cmake -DCMAKE_EXPORT_COMPILE_COMMANDS:STRING=ON build
34 | exclude: "./src/vmecpp/cpp/bazel-*,./src/vmecpp/cpp/external,./src/vmecpp/cpp/third_party"
35 | # turn off "LGTM" comments: we only want pings about warnings/errors
36 | lgtm_comment_body: ""
37 | # If there are any comments, fail the check
38 | - if: steps.review.outputs.total_comments > 0
39 | run: exit 1
40 |
--------------------------------------------------------------------------------
/.github/workflows/docker.yaml:
--------------------------------------------------------------------------------
1 | name: Build and Push Docker Container
2 |
3 | on:
4 | pull_request:
5 | paths:
6 | - 'docker/**'
7 | - '.github/workflows/docker.yaml'
8 |
9 | # allows running workflows manually
10 | workflow_dispatch:
11 | release:
12 | types: [published]
13 |
14 | jobs:
15 | docker:
16 | name: Build and push Docker image
17 | runs-on: ubuntu-latest
18 | permissions:
19 | contents: read
20 | packages: write
21 |
22 | steps:
23 | - name: Checkout repository
24 | uses: actions/checkout@v4
25 |
26 | - name: Set up Docker Buildx
27 | uses: docker/setup-buildx-action@v3
28 |
29 | - name: Log in to GitHub Container Registry
30 | # Always test if the login action works, even if we don't push
31 | uses: docker/login-action@v3
32 | with:
33 | registry: ghcr.io
34 | username: ${{ github.actor }}
35 | password: ${{ secrets.GITHUB_TOKEN }}
36 |
37 | - name: Extract Docker metadata
38 | id: meta
39 | uses: docker/metadata-action@v5
40 | with:
41 | images: ghcr.io/${{ github.repository }}
42 | tags: |
43 | type=semver,pattern={{version}}
44 | type=semver,pattern={{major}}.{{minor}}
45 | type=raw,value=latest
46 |
47 | - name: Build and push Docker image
48 | uses: docker/build-push-action@v5
49 | with:
50 | context: ./docker
51 | push: ${{ github.event_name != 'pull_request' }}
52 | tags: ${{ steps.meta.outputs.tags }}
53 | labels: ${{ steps.meta.outputs.labels }}
54 |
--------------------------------------------------------------------------------
/.github/workflows/docs.yaml:
--------------------------------------------------------------------------------
1 | name: Build and deploy docs
2 |
3 | on:
4 | push:
5 | branches: ["main"]
6 | workflow_dispatch:
7 |
8 | permissions:
9 | contents: read
10 | pages: write
11 | id-token: write
12 |
13 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
14 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
15 | concurrency:
16 | group: "pages"
17 | cancel-in-progress: false
18 |
19 | jobs:
20 | build-docs:
21 | environment:
22 | name: github-pages
23 | url: ${{ steps.deployment.outputs.page_url }}
24 | runs-on: ubuntu-22.04
25 | steps:
26 | - name: Checkout
27 | uses: actions/checkout@v4
28 | - uses: actions/setup-python@v5
29 | with:
30 | python-version: "3.10"
31 | - name: Build docs
32 | run: |
33 | sudo apt-get install -y build-essential cmake libnetcdf-dev liblapack-dev libomp-dev libhdf5-dev
34 | # must actually install the package otherwise sphinx can't autogenerate the API reference
35 | python -m pip install -vvv .[docs]
36 | sphinx-apidoc --module-first --no-toc --force --separate -o docs/api src/vmecpp/ && sphinx-build docs html_docs
37 | - name: Upload artifacts
38 | uses: actions/upload-pages-artifact@v3
39 | with:
40 | path: 'html_docs'
41 | - name: Deploy to GitHub Pages
42 | id: deployment
43 | uses: actions/deploy-pages@v4
44 |
--------------------------------------------------------------------------------
/.github/workflows/pyright.yaml:
--------------------------------------------------------------------------------
1 | name: Python Type Checking
2 |
3 | on:
4 | pull_request:
5 | push:
6 | branches:
7 | - main
8 |
9 | # Cancel currently running job if a new one comes along for the same branch or tag.
10 | # From https://stackoverflow.com/a/72408109.
11 | concurrency:
12 | group: ${{ github.workflow }}-${{ github.ref || github.run_id }}
13 | cancel-in-progress: ${{ github.ref_name != 'main' }}
14 |
15 | jobs:
16 | tests:
17 | name: Pyright type check
18 | runs-on: ubuntu-24.04
19 | steps:
20 | - uses: actions/checkout@v4
21 | - uses: actions/setup-python@v5
22 | with:
23 | python-version: '3.10'
24 | - name: Install dependencies
25 | run: sudo apt-get install build-essential cmake libnetcdf-dev liblapack-dev python3-dev
26 | # Parse the dependencies from the pyproject.toml file and install them,
27 | # without installing and building the entire vmecpp project
28 | - run: |
29 | python3 -m venv .venv
30 | source .venv/bin/activate
31 | pip install .
32 |
33 | - run: echo "$PWD/.venv/bin" >> $GITHUB_PATH
34 | - uses: jakebailey/pyright-action@v2
35 | with:
36 | version: 1.1.400
37 |
--------------------------------------------------------------------------------
/.github/workflows/test_bazel.yaml:
--------------------------------------------------------------------------------
1 | name: C++ core tests
2 |
3 | on:
4 | pull_request:
5 | push:
6 | branches:
7 | - main
8 |
9 | # Cancel currently running job if a new one comes along for the same branch or tag.
10 | # From https://stackoverflow.com/a/72408109.
11 | concurrency:
12 | group: ${{ github.workflow }}-${{ github.ref || github.run_id }}
13 | cancel-in-progress: ${{ github.ref_name != 'main' }}
14 |
15 | jobs:
16 | tests:
17 | name: Build and run ${{ matrix.config }} tests
18 | runs-on: ubuntu-22.04
19 | strategy:
20 | matrix:
21 | config: [opt, asan, ubsan]
22 | steps:
23 | - uses: actions/checkout@v4
24 | with:
25 | lfs: true # for the mgrid test data
26 | - name: Install required system packages for Ubuntu
27 | run: |
28 | sudo apt-get update && sudo apt-get install -y build-essential cmake liblapack-dev libomp-dev python3-dev python3-pip python-is-python3
29 | - name: Mount bazel cache
30 | uses: actions/cache@v4
31 | with:
32 | path: "~/.cache/bazel"
33 | key: bazel-${{ matrix.config }}
34 | - name: Build VMEC++ via bazel
35 | run: |
36 | cd src/vmecpp/cpp
37 | bazel build --config=${{ matrix.config }} -- //...
38 | - uses: actions/checkout@v4
39 | with:
40 | repository: proximafusion/vmecpp_large_cpp_tests
41 | path: src/vmecpp/cpp/vmecpp_large_cpp_tests
42 | lfs: true
43 | - name: Build VMEC++ C++ tests via bazel
44 | run: |
45 | cd src/vmecpp/cpp
46 | bazel build --config=${{ matrix.config }} -- //vmecpp_large_cpp_tests/...
47 | - name: Run full C++ test suite
48 | run: |
49 | cd src/vmecpp/cpp
50 | bazel test --config=${{ matrix.config }} --jobs=1 --test_output=errors -- //...
51 |
--------------------------------------------------------------------------------
/.github/workflows/test_conda_env.yaml:
--------------------------------------------------------------------------------
1 | name: Test conda forge env creation
2 |
3 | on:
4 | pull_request:
5 | push:
6 | branches:
7 | - main
8 |
9 | # Cancel currently running job if a new one comes along for the same branch or tag.
10 | # From https://stackoverflow.com/a/72408109.
11 | concurrency:
12 | group: ${{ github.workflow }}-${{ github.ref || github.run_id }}
13 | cancel-in-progress: ${{ github.ref_name != 'main' }}
14 |
15 | jobs:
16 | test-conda-env:
17 | name: Test conda env
18 | runs-on: ubuntu-22.04
19 | container:
20 | image: continuumio/miniconda3
21 | steps:
22 | - uses: actions/checkout@v4
23 | # This container does not have git-lfs installed
24 | - uses: actions/setup-python@v5
25 | with:
26 | python-version: "3.10"
27 | - name: Create conda env
28 | run: |
29 | conda env create --yes --quiet --file environment.yml
30 | - name: Test conda env
31 | run: |
32 | . "/opt/conda/etc/profile.d/conda.sh"
33 | conda activate vmecpp
34 | conda install --yes --quiet pytest
35 | # exclude test that requires VMEC2000 to be installed:
36 | # it's not available as a conda package, and skip test
37 | # that requires mgrid file from git-lfs
38 | # (LFS not available in this container image)
39 | python -m pytest -k 'not test_ensure_vmec2000_input_from_vmecpp_input and not test_against_reference_wout' tests/
40 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | **/compile_commands.json
2 | venv/
3 | .vscode
4 | build/
5 |
6 | # python build outputs
7 | dist/
8 | wheelhouse/
9 | __pycache__
10 | src/vmecpp/__about__.py
11 |
12 | # bazel output directories
13 | bazel-*
14 | src/vmecpp/cpp/_vmecpp.so
15 |
16 | # developers will clone this in order to have the full test suite available
17 | src/vmecpp/cpp/vmecpp_large_cpp_tests/
18 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "pybind11"]
2 | path = pybind11
3 | url = https://github.com/pybind/pybind11.git
4 | [submodule "abscab-cpp"]
5 | path = abscab-cpp
6 | url = https://github.com/jonathanschilling/abscab-cpp.git
7 | [submodule "abseil-cpp"]
8 | path = abseil-cpp
9 | url = https://github.com/abseil/abseil-cpp.git
10 | [submodule "indata2json"]
11 | path = indata2json
12 | url = https://github.com/jonathanschilling/indata2json.git
13 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | repos:
2 | - repo: https://github.com/astral-sh/ruff-pre-commit
3 | rev: "v0.7.1"
4 | hooks:
5 | - id: ruff-format
6 | - id: ruff
7 | args: ["--fix", "--show-fixes"]
8 | - repo: https://github.com/adamchainz/blacken-docs
9 | rev: "1.19.1"
10 | hooks:
11 | - id: blacken-docs
12 | additional_dependencies:
13 | - black==22.12.0
14 | - repo: https://github.com/PyCQA/docformatter
15 | rev: eb1df34
16 | hooks:
17 | - id: docformatter
18 | additional_dependencies: [tomli]
19 | args: ["--in-place", "--config", "./pyproject.toml"]
20 | - repo: https://github.com/pre-commit/pre-commit-hooks
21 | rev: "v5.0.0"
22 | hooks:
23 | - id: check-added-large-files
24 | - id: check-case-conflict
25 | - id: check-merge-conflict
26 | - id: check-symlinks
27 | - id: check-yaml
28 | - id: debug-statements
29 | - id: end-of-file-fixer
30 | - id: mixed-line-ending
31 | - id: name-tests-test
32 | args: ["--pytest-test-first"]
33 | - id: requirements-txt-fixer
34 | - id: trailing-whitespace
35 | - repo: https://github.com/pre-commit/mirrors-clang-format
36 | rev: v20.1.0
37 | hooks:
38 | - id: clang-format
39 | types_or: [c++, c]
40 | - repo: https://github.com/RobertCraigie/pyright-python
41 | rev: v1.1.400
42 | hooks:
43 | - id: pyright
44 |
--------------------------------------------------------------------------------
/CITATION.cff:
--------------------------------------------------------------------------------
1 | abstract:
From-scratch C++ and Python reimplementation of the Variational Moments
2 | Equilibrium Code (VMEC).
3 | authors:
4 | - name: Proxima Fusion GmbH, Munich, Germany
5 | - affiliation: Proxima Fusion GmbH, Munich, Germany
6 | family-names: Schilling
7 | given-names: Jonathan
8 | - affiliation: Proxima Fusion GmbH, Munich, Germany
9 | family-names: Guiraud
10 | given-names: Enrico
11 | - affiliation: Proxima Fusion GmbH, Munich, Germany
12 | family-names: Siska
13 | given-names: Veronika
14 | cff-version: 1.2.0
15 | date-released: '2025-02-04'
16 | doi: 10.5281/zenodo.14800158
17 | license:
18 | - mit
19 | title: VMEC++
20 | type: software
21 | version: 0.1.0
22 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024-present Proxima Fusion GmbH
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/docker/Dockerfile:
--------------------------------------------------------------------------------
1 | # run as:
2 | # docker build -t ghcr.io/proximafusion/vmecpp:latest .
3 | FROM ubuntu:22.04
4 |
5 | RUN apt-get -q update && \
6 | apt-get -q -y install \
7 | build-essential \
8 | cmake \
9 | libnetcdf-dev \
10 | liblapack-dev \
11 | libomp-dev \
12 | libhdf5-dev \
13 | python3-pip \
14 | python-is-python3 \
15 | git \
16 | wget \
17 | ninja-build \
18 | gfortran
19 |
20 | RUN wget https://github.com/bazelbuild/bazelisk/releases/download/v1.24.1/bazelisk-linux-amd64 \
21 | && mv bazelisk-linux-amd64 /usr/local/bin/bazel \
22 | && chmod u+x /usr/local/bin/bazel
23 |
24 | RUN git clone https://github.com/proximafusion/vmecpp.git
25 | WORKDIR /vmecpp
26 | RUN python -m pip install .
27 |
--------------------------------------------------------------------------------
/docker/README.md:
--------------------------------------------------------------------------------
1 | An Ubuntu 22.04 image with VMEC++ installed as a global Python package.
2 |
3 | ## Get the image
4 |
5 | ```console
6 | docker pull ghcr.io/proximafusion/vmecpp:latest
7 | ```
8 |
9 | ## Use the image
10 |
11 | Simple test: run SIMSOPT's ["QH fixed resolution" example](/examples/simsopt_qh_fixed_resolution.py) with VMEC++:
12 |
13 | ```shell
14 | docker run -it --rm ghcr.io/proximafusion/vmecpp:latest
15 | # now inside the docker container (by default we'll be inside the vmecpp repo sources):
16 | python examples/simsopt_qh_fixed_resolution.py
17 | ```
18 |
19 | To run VMEC++ on configurations you have on your host system, e.g. in a directory `data_dir`,
20 | you could mount that directory onto the docker container and use VMEC++'s CLI API:
21 |
22 | ```shell
23 | docker run -it --rm -v/absolute/path/to/data_dir:/data_dir ghcr.io/proximafusion/vmecpp:latest
24 | # now inside the docker container we can run the VMEC++ CLI:
25 | python -m vmecpp /data_dir/input.xyz
26 | ```
27 |
28 |
29 | ## For developers: manually pushing a new image
30 |
31 | 1. create a GitHub token like in [Get the image](#get-the-image), but check the `write:packages` and `repo` permissions
32 | 2. log into the GitHub container registry, e.g. with `echo YOUR_TOKEN | docker login ghcr.io -u YOUR_USERNAME --password-stdin`
33 | 3. build the docker image
34 | - `docker build --tag=ghcr.io/proximafusion/vmecpp:latest .`
35 | - on systems with newer docker, you might need `docker buildx build` instead of just `docker build`
36 | 4. push the docker image
37 | - `docker push ghcr.io/proximafusion/vmecpp:latest`
38 |
--------------------------------------------------------------------------------
/docs/.gitattributes:
--------------------------------------------------------------------------------
1 | *.pdf filter=lfs diff=lfs merge=lfs -text
2 |
--------------------------------------------------------------------------------
/docs/conf.py:
--------------------------------------------------------------------------------
1 | # from https://learn.scientific-python.org/development/guides/docs
2 | from typing import Any
3 |
4 | project = "VMEC++"
5 | copyright = "2024, Proxima Fusion GmbH"
6 | author = "Proxima Fusion GmbH"
7 |
8 | extensions = [
9 | "myst_parser",
10 | "sphinx.ext.autodoc",
11 | "sphinx.ext.intersphinx",
12 | "sphinx.ext.autosummary",
13 | "sphinx.ext.mathjax",
14 | "sphinx.ext.napoleon",
15 | # must come after napoleon,
16 | # see https://pypi.org/project/sphinx-autodoc-typehints
17 | "sphinx_autodoc_typehints",
18 | "sphinx_copybutton",
19 | ]
20 |
21 | source_suffix = [".rst", ".md"]
22 | exclude_patterns = [
23 | "build",
24 | ".venv",
25 | ]
26 |
27 | html_theme = "furo"
28 |
29 | html_theme_options: dict[str, Any] = {
30 | "footer_icons": [
31 | {
32 | "name": "GitHub",
33 | "url": "https://github.com/proximafusion/vmecpp",
34 | "html": """
35 |
38 | """,
39 | "class": "",
40 | },
41 | ],
42 | "source_repository": "https://github.com/proximafusion/vmecpp",
43 | "source_branch": "main",
44 | "source_directory": "docs/",
45 | }
46 |
47 | intersphinx_mapping = {
48 | "python": ("https://docs.python.org/3", None),
49 | }
50 |
51 | nitpick_ignore = [
52 | ("py:class", "_io.StringIO"),
53 | ("py:class", "_io.BytesIO"),
54 | ]
55 |
56 | always_document_param_types = True
57 |
58 | myst_enable_extensions = ["html_image"]
59 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | # VMEC++
5 |
6 | ```{toctree}
7 | :maxdepth: 2
8 | :hidden:
9 |
10 | Reference
11 | ```
12 |
13 | 
14 | 
15 | [](https://doi.org/10.5281/zenodo.14800158)
16 |
17 | VMEC++ is a Python-friendly, from-scratch reimplementation in C++ of the Variational Moments Equilibrium Code (VMEC),
18 | a free-boundary ideal-MHD equilibrium solver for stellarators and tokamaks.
19 |
20 | The original version was written by Steven P. Hirshman and colleagues in the 1980s and 1990s.
21 | The latest version of the original code is called `PARVMEC` and is available [here](https://github.com/ORNL-Fusion/PARVMEC).
22 |
23 | Compared to its Fortran predecessors, VMEC++:
24 | - has a zero-crash policy and reports issues via standard Python exceptions
25 | - allows hot-restarting a run from a previous converged state (see [Hot restart](#hot-restart))
26 | - supports inputs in the classic INDATA format as well as simpler-to-parse JSON files; it is also simple to construct input objects programmatically in Python
27 | - typically runs just as fast or faster
28 | - comes with [substantial documentation of its internal numerics](https://github.com/proximafusion/vmecpp/blob/main/docs/the_numerics_of_vmecpp.pdf)
29 |
30 | VMEC++ can run on a laptop, but it is a suitable component for large-scale stellarator optimization pipelines.
31 |
32 | On the other hand, some features of the original Fortran VMEC are not available in VMEC++.
33 | See [below](#differences-with-respect-to-parvmec-vmec2000) for more details.
34 |
35 | ```{include} ../README.md
36 | :start-after:
37 | ```
38 |
39 | ## Indices and tables
40 |
41 | - {ref}`genindex`
42 | - {ref}`modindex`
43 | - {ref}`search`
44 |
--------------------------------------------------------------------------------
/docs/proxima_logo_dark_mode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/proximafusion/vmecpp/ef96e726f1cc9bda8bb2a97ab32add94891adb36/docs/proxima_logo_dark_mode.png
--------------------------------------------------------------------------------
/docs/proxima_logo_light_mode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/proximafusion/vmecpp/ef96e726f1cc9bda8bb2a97ab32add94891adb36/docs/proxima_logo_light_mode.png
--------------------------------------------------------------------------------
/docs/the_numerics_of_vmecpp.pdf:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:16926b7798b58d527cbbf6423710914b1b14c81b04535d18515acf6f428ad5fa
3 | size 2353641
4 |
--------------------------------------------------------------------------------
/environment.yml:
--------------------------------------------------------------------------------
1 | # Create the env with:
2 | # conda env create -f environment.yml
3 | #
4 | # N.B. We use a relative path to pip-install
5 | # the vmecpp repo, so you must execute the
6 | # command from the root of the vmecpp repo.
7 |
8 | name: vmecpp
9 | channels:
10 | - conda-forge
11 | dependencies:
12 | - _libgcc_mutex=0.1=conda_forge
13 | - _openmp_mutex=4.5=2_gnu
14 | - binutils_impl_linux-64=2.43=h4bf12b8_2
15 | - blas=2.123=blis
16 | - blas-devel=3.9.0=23_linux64_blis
17 | - blis=0.9.0=h4ab18f5_2
18 | - blosc=1.21.6=he440d0b_1
19 | - bzip2=1.0.8=h4bc722e_7
20 | - c-ares=1.34.4=hb9d3cd8_0
21 | - ca-certificates=2024.12.14=hbcca054_0
22 | - certifi=2024.12.14=pyhd8ed1ab_0
23 | - cftime=1.6.4=py310hf462985_1
24 | - eigen=3.4.0=h00ab1b0_0
25 | - gcc=14.2.0=h96c4ede_1
26 | - gcc_impl_linux-64=14.2.0=h6b349bd_1
27 | - gfortran=14.2.0=h96c4ede_1
28 | - gfortran_impl_linux-64=14.2.0=hc73f493_1
29 | - gxx=14.2.0=h96c4ede_1
30 | - gxx_impl_linux-64=14.2.0=h2c03514_1
31 | - hdf4=4.2.15=h2a13503_7
32 | - hdf5=1.14.3=nompi_hdf9ad27_105
33 | - kernel-headers_linux-64=5.14.0=h8bc681e_0
34 | - keyutils=1.6.1=h166bdaf_0
35 | - krb5=1.21.3=h659f571_0
36 | - ld_impl_linux-64=2.43=h712a8e2_2
37 | - libaec=1.1.3=h59595ed_0
38 | - libblas=3.9.0=23_linux64_blis
39 | - libcblas=3.9.0=23_linux64_blis
40 | - libcurl=8.11.1=h332b0f4_0
41 | - libedit=3.1.20240808=pl5321h7949ede_0
42 | - libev=4.33=hd590300_2
43 | - libffi=3.4.2=h7f98852_5
44 | - libgcc=14.2.0=h77fa898_1
45 | - libgcc-devel_linux-64=14.2.0=h41c2201_101
46 | - libgcc-ng=14.2.0=h69a702a_1
47 | - libgfortran-ng=7.5.0=h14aa051_20
48 | - libgfortran4=7.5.0=h14aa051_20
49 | - libgfortran5=14.2.0=hd5240d6_1
50 | - libgomp=14.2.0=h77fa898_1
51 | - libiconv=1.17=hd590300_2
52 | - libjpeg-turbo=3.0.0=hd590300_1
53 | - liblapack=3.9.0=6_ha36c22a_netlib
54 | - liblzma=5.6.3=hb9d3cd8_1
55 | - libnetcdf=4.9.2=nompi_h00e09a9_116
56 | - libnghttp2=1.64.0=h161d5f1_0
57 | - libnsl=2.0.1=hd590300_0
58 | - libsanitizer=14.2.0=h2a3dede_1
59 | - libsqlite=3.47.2=hee588c1_0
60 | - libssh2=1.11.1=hf672d98_0
61 | - libstdcxx=14.2.0=hc0a3c3a_1
62 | - libstdcxx-devel_linux-64=14.2.0=h41c2201_101
63 | - libstdcxx-ng=14.2.0=h4852527_1
64 | - libuuid=2.38.1=h0b41bf4_0
65 | - libxcrypt=4.4.36=hd590300_1
66 | - libxml2=2.13.5=h0d44e9d_1
67 | - libzip=1.11.2=h6991a6a_0
68 | - libzlib=1.3.1=hb9d3cd8_2
69 | - lz4-c=1.10.0=h5888daf_1
70 | - ncurses=6.5=h2d0b736_2
71 | - netcdf-fortran=4.6.1=nompi_h228c76a_104
72 | - netcdf4=1.7.2=nompi_py310h9f0ad05_101
73 | - numpy=1.26.4=py310hb13e2d6_0
74 | - openssl=3.4.0=h7b32b05_1
75 | - pip=24.3.1=pyh8b19718_2
76 | - python=3.10.16=he725a3c_1_cpython
77 | - python_abi=3.10=5_cp310
78 | - readline=8.2=h8228510_1
79 | - setuptools=75.8.0=pyhff2d567_0
80 | - snappy=1.2.1=h8bd8927_1
81 | - sysroot_linux-64=2.34=h8bc681e_0
82 | - tk=8.6.13=noxft_h4845f30_101
83 | - tzdata=2024b=hc8b5060_0
84 | - wheel=0.45.1=pyhd8ed1ab_1
85 | - zlib=1.3.1=hb9d3cd8_2
86 | - zstd=1.5.6=ha6fb4c9_0
87 | - binutils
88 | - nlohmann_json
89 | - pip:
90 | - ./.
91 |
--------------------------------------------------------------------------------
/examples/compare_vmecpp_to_parvmec.py:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | #
3 | # SPDX-License-Identifier: MIT
4 | """Comparison of VMEC++ with PARVMEC."""
5 |
6 | from pathlib import Path
7 |
8 | import matplotlib.pyplot as plt
9 | import netCDF4
10 | import numpy as np
11 |
12 | import vmecpp
13 |
14 | examples = Path("examples")
15 |
16 | # reference produced using ORNL-Fusion/PARVMEC via
17 | # `mpirun -np 4 xvmec input.w7x` in `examples/data`
18 |
19 | # read PARVMEC reference output
20 | with netCDF4.Dataset(examples / "data" / "wout_w7x.nc", "r") as reference_wout:
21 | reference_wout.set_always_mask(False)
22 | ref_nfp = reference_wout["nfp"][()]
23 | ref_ns = reference_wout["ns"][()]
24 | ref_mnmax = reference_wout["mnmax"][()]
25 | ref_xm = reference_wout["xm"][()]
26 | ref_xn = reference_wout["xn"][()]
27 | ref_rmnc = reference_wout["rmnc"][()]
28 | ref_zmns = reference_wout["zmns"][()]
29 | ref_iotas = reference_wout["iotas"][()][1:]
30 |
31 | # read input file and run VMEC++
32 | input_file = examples / "data" / "input.w7x"
33 | vmecpp_input = vmecpp.VmecInput.from_file(input_file)
34 | vmecpp_output = vmecpp.run(input=vmecpp_input)
35 |
36 | # make sure runs are broadly consistent
37 | assert ref_nfp == vmecpp_output.wout.nfp
38 | assert ref_ns == vmecpp_output.wout.ns
39 | assert ref_mnmax == vmecpp_output.wout.mnmax
40 |
41 | # evaluate flux surface geometry from both runs and plot them
42 | ntheta = 101
43 | theta = np.linspace(0.0, 2.0 * np.pi, ntheta)
44 | for phi_degrees in [0, 18, 36]:
45 | phi = np.deg2rad(phi_degrees)
46 | kernel = np.outer(ref_xm, theta) - np.outer(ref_xn, phi)
47 | cos_kernel = np.cos(kernel)
48 | sin_kernel = np.sin(kernel)
49 |
50 | plt.figure()
51 | for j in [0, 2**2, 4**2, 6**2, 8**2, 98]:
52 | ref_r = np.dot(ref_rmnc[j, :], cos_kernel)
53 | ref_z = np.dot(ref_zmns[j, :], sin_kernel)
54 | if j == 0:
55 | plt.plot(ref_r, ref_z, "ro", label="PARVMEC")
56 | else:
57 | plt.plot(ref_r, ref_z, "r-", lw=2)
58 |
59 | vmecpp_r = np.dot(vmecpp_output.wout.rmnc[:, j], cos_kernel)
60 | vmecpp_z = np.dot(vmecpp_output.wout.zmns[:, j], sin_kernel)
61 | if j == 0:
62 | plt.plot(vmecpp_r, vmecpp_z, "bx", label="VMEC++")
63 | else:
64 | plt.plot(vmecpp_r, vmecpp_z, "b--", lw=2)
65 |
66 | plt.axis("equal")
67 | plt.xlabel("R / m")
68 | plt.ylabel("Z / m")
69 | plt.grid(True)
70 | plt.legend(loc="upper right")
71 |
72 | plt.title(f"$\\varphi = {phi_degrees}$ deg")
73 |
74 | # plot iota profile comparison
75 | s_half = (0.5 + np.arange(ref_ns - 1)) / (ref_ns - 1.0)
76 | plt.figure()
77 | plt.plot(s_half, ref_iotas, "ro-", label="PARVMEC")
78 | plt.plot(s_half, vmecpp_output.wout.iotas[1:], "bx--", label="VMEC++")
79 | plt.xlabel("s / 1")
80 | plt.ylabel("rotational transform / 1")
81 | plt.grid(True)
82 | plt.legend(loc="upper left")
83 |
84 | plt.show()
85 |
--------------------------------------------------------------------------------
/examples/data/.gitignore:
--------------------------------------------------------------------------------
1 | # PARVMEC outputs
2 | jxbout_*.nc
3 | mercier.*
4 | parvmecinfo.txt
5 | threed1.*
6 | timings.txt
7 | wout_*.nc
8 |
--------------------------------------------------------------------------------
/examples/data/README.md:
--------------------------------------------------------------------------------
1 | # Example input and output files for VMEC++
2 |
3 | A few cases are available for testing VMCE++ and experimenting with it:
4 |
5 | 1. `cth_like_fixed_bdy.json` - Stellarator case, similar to the Compact Toroidal Hybrid ([CTH](https://www.auburn.edu/cosam/departments/physics/research/plasma_physics/compact_toroidal_hybrid/index.htm)) device
6 | 1. `input.cth_like_fixed_bdy` - Fortran namelist input file to be used with Fortran VMEC
7 | 1. `cth_like_fixed_bdy.json` - JSON input file for VMEC++, derived from `input.cth_like_fixed_bdy` using [`indata2json`](https://github.com/jonathanschilling/indata2json)
8 | 1. `wout_cth_like_fixed_bdy.nc` - NetCDF output file, produced using [`PARVMEC`](https://github.com/ORNL-Fusion/PARVMEC) from `input.cth_like_fixed_bdy`, for testing the loading of a `wout` file using VMEC++'s tooling
9 |
10 | 1. `input.nfp4_QH_warm_start` - quasi-helically example for use with SIMSOPT
11 |
12 | 1. `solovev` - axisymmetric Tokamak case, similar to the Solov'ev equilibrium used in the [1983 Hirshman & Whitson article](https://doi.org/10.1063/1.864116)
13 | 1. `input.solovev` - Fortran namelist input file for use with Fortran VMEC
14 | 1. `solovev.json` - JSON input file for VMEC++, derived from `input.solovev` using [`indata2json`](https://github.com/jonathanschilling/indata2json)
15 |
16 | 1. `w7x` - Wendelstein 7-X ([W7-X](https://www.ipp.mpg.de/w7x)) Stellarator
17 | 1. `input.w7x` - Fortran namelist input file for use with Fortran VMEC
18 | 1. `w7x.json` - JSON input file for VMEC++, derived from `input.w7x` using [`indata2json`](https://github.com/jonathanschilling/indata2json)
19 |
--------------------------------------------------------------------------------
/examples/data/input.nfp4_QH_warm_start:
--------------------------------------------------------------------------------
1 | &INDATA
2 | !----- Runtime Parameters -----
3 | DELT = 9.00E-01
4 | NITER = 10000
5 | NSTEP = 200
6 | TCON0 = 2.00E+00
7 | NS_ARRAY = 16 50
8 | NITER_ARRAY = 600 2000
9 | FTOL_ARRAY = 1.0E-16 1.0E-11
10 | PRECON_TYPE = 'none'
11 | PREC2D_THRESHOLD = 1.000000E-19
12 | !----- Grid Parameters -----
13 | LASYM = F
14 | NFP = 0004
15 | MPOL = 0005
16 | NTOR = 0005
17 | PHIEDGE = 0.048
18 | !----- Free Boundary Parameters -----
19 | LFREEB = F
20 | NVACSKIP = 6
21 | !----- Pressure Parameters -----
22 | GAMMA = 0.000000000000E+000
23 | BLOAT = 1.000000000000E+000
24 | SPRES_PED = 1.000000000000E+000
25 | PRES_SCALE = 1.000000000000E+000
26 | PMASS_TYPE = 'power_series'
27 | AM = 000000000E+00
28 | !----- Current/Iota Parameters -----
29 | CURTOR = 0
30 | NCURR = 1
31 | PIOTA_TYPE = 'power_series'
32 | PCURR_TYPE = 'power_series'
33 | !----- Boundary Parameters -----
34 | ! n comes before m!
35 | RBC( 0, 0) = 1.000000000000000e+00, ZBS( 0, 0) = 0.000000000000000e+00
36 | RBC( 1, 0) = 1.344559674021724e-01, ZBS( 1, 0) = 1.328736337280527e-01
37 | RBC( -1, 1) = -7.611873649087421e-02, ZBS( -1, 1) = 1.016005813066520e-01
38 | RBC( 0, 1) = 1.663500817350330e-01, ZBS( 0, 1) = 1.669633931562144e-01
39 | RBC( 1, 1) = -1.276344690455198e-02, ZBS( 1, 1) = -1.827419625823130e-02
40 | /
41 |
--------------------------------------------------------------------------------
/examples/data/input.solovev:
--------------------------------------------------------------------------------
1 | &INDATA
2 | DELT = 0.9
3 | LASYM = F
4 | NFP = 1
5 | NCURR = 0
6 | NSTEP = 250
7 | GAMMA = 0
8 | PHIEDGE = 1
9 | BLOAT = 1
10 | CURTOR = 0
11 | SPRES_PED = 1
12 | PRES_SCALE = 1
13 | PMASS_TYPE = "power_series"
14 | MPOL = 6
15 | NTOR = 0
16 | NS_ARRAY = 5 11 55
17 | NITER_ARRAY = 1000 2000 2000
18 | FTOL_ARRAY = 1E-12 1E-12 1E-12
19 | AM = 0.125 -0.125
20 | AI = 1
21 | RAXIS_CC = 4
22 | ZAXIS_CS = 0
23 | RBC(0,0) = 3.999 ZBS(0,0) = 0.000
24 | RBC(0,1) = 1.026 ZBS(0,1) = 1.580
25 | RBC(0,2) = -0.068 ZBS(0,2) = 0.010
26 | /
27 |
--------------------------------------------------------------------------------
/examples/data/solovev.json:
--------------------------------------------------------------------------------
1 | {
2 | "mpol" : 6,
3 | "ntor" : 0,
4 | "delt" : 0.9,
5 | "ncurr" : 0,
6 | "nstep" : 250,
7 | "ns_array" : [5, 11, 55 ],
8 | "niter_array" : [1000, 2000, 2000 ],
9 | "ftol_array" : [1.0e-16, 1.0e-16, 1.0e-16],
10 | "am" : [ 0.125, -0.125 ],
11 | "ai" : [ 1.0 ],
12 | "raxis_c" : [ 4.0 ],
13 | "zaxis_s" : [ 0.0 ],
14 | "rbc" : [ {"n" : 0, "m" : 0, "value" : 3.999 },
15 | {"n" : 0, "m" : 1, "value" : 1.026 },
16 | {"n" : 0, "m" : 2, "value" : -0.068 } ],
17 | "zbs" : [ {"n" : 0, "m" : 0, "value" : 0.000 },
18 | {"n" : 0, "m" : 1, "value" : 1.580 },
19 | {"n" : 0, "m" : 2, "value" : 0.010 } ]
20 | }
21 |
--------------------------------------------------------------------------------
/examples/data/wout_cth_like_fixed_bdy.nc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/proximafusion/vmecpp/ef96e726f1cc9bda8bb2a97ab32add94891adb36/examples/data/wout_cth_like_fixed_bdy.nc
--------------------------------------------------------------------------------
/examples/freeboundary_run_and_hot_restart.py:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | #
3 | # SPDX-License-Identifier: MIT
4 | """Hot-restart from a converged equilibrium."""
5 |
6 | from pathlib import Path
7 |
8 | import vmecpp
9 |
10 | # Load the VMEC++ JSON indata file.
11 | # Its keys have 1:1 correspondence with those in a classic Fortran INDATA file.
12 | TEST_DATA_DIR = Path("src") / "vmecpp" / "cpp" / "vmecpp" / "test_data"
13 | vmec_input_filename = TEST_DATA_DIR / "cth_like_free_bdy.json"
14 | coils_fname = TEST_DATA_DIR / "coils.cth_like"
15 | makegrid_params_fname = TEST_DATA_DIR / "makegrid_parameters_cth_like.json"
16 |
17 | vmec_input = vmecpp.VmecInput.from_file(vmec_input_filename)
18 | # We don't need an mgrid file, because we are passing the magnetic field as an object in memory
19 | vmec_input.mgrid_file = ""
20 |
21 | mgrid_params = vmecpp.MakegridParameters.from_file(makegrid_params_fname)
22 | magnetic_response_table = vmecpp.MagneticFieldResponseTable.from_coils_file(
23 | coils_fname, mgrid_params
24 | )
25 | # Let's run VMEC++.
26 | # In case of errors or non-convergence, a RuntimeError is raised.
27 | # The OutputQuantities object returned has attributes corresponding
28 | # to the usual outputs: wout, jxbout, mercier, ...
29 | vmec_output = vmecpp.run(vmec_input, magnetic_field=magnetic_response_table)
30 |
31 | # Now let's perturb the magnetic field...
32 | magnetic_response_table.b_p *= 1.01
33 | magnetic_response_table.b_r *= 1.01
34 | magnetic_response_table.b_z *= 1.01
35 | print("Increased the magnetic field strength by 1%")
36 |
37 | # ...and run VMEC++ again, but using its "hot restart" feature:
38 | # passing the previously obtained output_quantities ensures that
39 | # the run starts already close to the equilibrium, so it will take
40 | # fewer iterations to converge this time.
41 | perturbed_output = vmecpp.run(
42 | vmec_input, magnetic_field=magnetic_response_table, restart_from=vmec_output
43 | )
44 | print("First run took", vmec_output.wout.itfsq, "iterations")
45 | print("Second run took", perturbed_output.wout.itfsq, "iterations")
46 |
--------------------------------------------------------------------------------
/examples/hot_restart_scaling.py:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | #
3 | # SPDX-License-Identifier: MIT
4 | """This example demonstrates how the size of the perturbation on the plasma boundary
5 | geometry in a fixed-boundary equilibrium computation affects the number of iterations
6 | required for a hot-restarted equilibrium to converge again."""
7 |
8 | from pathlib import Path
9 |
10 | import matplotlib.pyplot as plt
11 | import numpy as np
12 |
13 | # Import the VMEC++ Python module.
14 | import vmecpp
15 |
16 | # Load the VMEC++ JSON indata file.
17 | # Its keys have 1:1 correspondence with those in a classic Fortran INDATA file.
18 | vmec_input_filename = Path("examples") / "data" / "cth_like_fixed_bdy.json"
19 | vmec_input = vmecpp.VmecInput.from_file(vmec_input_filename)
20 |
21 | vmec_input.ftol_array[0] = 1.0e-13
22 |
23 | # make initial run to restart from
24 | output_quantities = vmecpp.run(vmec_input)
25 |
26 | niter_initial = output_quantities.wout.niter
27 |
28 | # pertubation amplitudes
29 | rbc_01_scale = [
30 | 1.0 + 1.0e-15,
31 | 1.0 + 1.0e-14,
32 | 1.0 + 1.0e-13,
33 | 1.0 + 1.0e-12,
34 | 1.0 + 1.0e-11,
35 | 1.0 + 1.0e-10,
36 | 1.0 + 1.0e-9,
37 | 1.0 + 1.0e-8,
38 | 1.0 + 1.0e-7,
39 | 1.0 + 1.0e-6,
40 | 1.0 + 1.0e-5,
41 | 1.0 + 1.0e-4,
42 | 1.0 + 1.0e-3,
43 | 1.0 + 1.0e-2,
44 | # 1.0 + 1.0e-1 does not converge at all anymore
45 | ]
46 | niter = []
47 |
48 | for s in rbc_01_scale:
49 | # copy over un-perturbed inputs to reset previous rbc pertubation
50 | perturbed_indata = vmec_input.model_copy()
51 |
52 | # apply pertubation; indices are [m, ntor + n] (to allow for negative n)
53 | perturbed_indata.rbc[1, vmec_input.ntor + 0] *= s
54 |
55 | restarted_wout = vmecpp.run(perturbed_indata, restart_from=output_quantities)
56 |
57 | niter.append(restarted_wout.wout.niter)
58 |
59 | plt.figure()
60 |
61 | plot_relative = False
62 | if plot_relative:
63 | # plot new number of iterations relative to original ones
64 | plt.semilogx(np.subtract(rbc_01_scale, 1.0), np.divide(niter, niter_initial), "o-")
65 | plt.axhline(y=1, linestyle="--")
66 | else:
67 | # plot absolute number of new iterations
68 | plt.semilogx(np.subtract(rbc_01_scale, 1.0), niter, "o-")
69 | plt.axhline(y=niter_initial, linestyle="--")
70 |
71 | plt.xlabel("perturbation of RBC(n=0,m=1)")
72 | plt.ylabel("iterations for re-convergence")
73 | plt.grid(True)
74 |
75 | plt.show()
76 |
--------------------------------------------------------------------------------
/examples/mpi_finite_difference.py:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | #
3 | # SPDX-License-Identifier: MIT
4 | """Example of how to utilize the hot restart functionality, to evaluate a finite
5 | difference estimate of the local Jacobian efficiently.
6 |
7 | We parallelize the evaluation using MPI and distribute the workload across a number of
8 | ranks. In this example, we compute the derivatives of the volume in respect to all
9 | Fourier components of the geometry.
10 | """
11 |
12 | from pathlib import Path
13 |
14 | import mpi4py
15 | import numpy as np
16 |
17 | import vmecpp
18 |
19 | # Notice that the OpenMP parallelism in VMEC++ allows us to use a simple MPI
20 | # communicator, without the need to split into sub-groups.
21 | comm = mpi4py.MPI.COMM_WORLD
22 | rank = comm.Get_rank()
23 |
24 | # In a real application, only root would probably read the input file and broadcast it.
25 | filename = Path(__file__).parent / "data" / "cth_like_fixed_bdy.json"
26 | initial_input = vmecpp.VmecInput.from_file(filename)
27 |
28 | # All Fourier components of the geometry R, Z are degrees of freedom
29 | n_dofs = np.prod(initial_input.rbc.shape) + np.prod(initial_input.zbs.shape)
30 | m_outputs = 1 # Only interested in the volume
31 | # One process per DOF. Root can also do a finite difference evaluation
32 | assert n_dofs % comm.Get_size() == 0, f"Number of degrees of freedom: {n_dofs}"
33 | n_dofs_per_proc = n_dofs // comm.Get_size()
34 |
35 | # Base evaluation
36 | initial_output = None
37 | if rank == 0:
38 | initial_output = vmecpp.run(initial_input)
39 | initial_output = comm.bcast(initial_output)
40 |
41 | # ...and fix up the multigrid steps: hot-restarted runs only allow a single step
42 | initial_input.ns_array = initial_input.ns_array[-1:]
43 | initial_input.ftol_array = initial_input.ftol_array[-1:]
44 | initial_input.niter_array = initial_input.niter_array[-1:]
45 |
46 | eps = 1e-8
47 | my_jacobian = np.zeros((n_dofs_per_proc, m_outputs))
48 | # Start the finite difference evaluation
49 | for i in range(n_dofs_per_proc):
50 | perturbed_input = initial_input.model_copy(deep=True)
51 | dof_idx = i + rank * n_dofs_per_proc
52 | if dof_idx < n_dofs // 2:
53 | perturbed_input.rbc.flat[dof_idx] += eps
54 | else:
55 | perturbed_input.zbs.flat[dof_idx - n_dofs // 2] += eps
56 |
57 | # We can now run a finite difference evaluation with hot restart:
58 | hot_restarted_output = vmecpp.run(
59 | perturbed_input, restart_from=initial_output, max_threads=1
60 | )
61 | dVdx = (hot_restarted_output.wout.volume - initial_output.wout.volume) / eps
62 | print(f"{dof_idx:3d} dVdx: {dVdx}")
63 | my_jacobian[i, :] = dVdx
64 |
65 | # Gather Jacobians on root process
66 | jacobian = comm.gather(my_jacobian, root=0)
67 | if rank == 0:
68 | jacobian = np.vstack(jacobian)
69 | print("Final Jacobian matrix:\n", jacobian)
70 |
--------------------------------------------------------------------------------
/examples/normal_run_and_hot_restart.py:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | #
3 | # SPDX-License-Identifier: MIT
4 | """Hot-restart from a converged equilibrium."""
5 |
6 | from pathlib import Path
7 |
8 | import vmecpp
9 |
10 | # Load the VMEC++ JSON indata file.
11 | # Its keys have 1:1 correspondence with those in a classic Fortran INDATA file.
12 | vmec_input_filename = Path("examples") / "data" / "cth_like_fixed_bdy.json"
13 | vmec_input = vmecpp.VmecInput.from_file(vmec_input_filename)
14 |
15 | # Let's run VMEC++.
16 | # In case of errors or non-convergence, a RuntimeError is raised.
17 | # The OutputQuantities object returned has attributes corresponding
18 | # to the usual outputs: wout, jxbout, mercier, ...
19 | vmec_output = vmecpp.run(vmec_input)
20 | print(" initial volume:", vmec_output.wout.volume_p)
21 |
22 | # Now let's perturb the plasma boundary a little bit...
23 | vmec_input.rbc[0, 0] *= 0.8
24 | vmec_input.rbc[1, 0] *= 1.2
25 |
26 | # ...and run VMEC++ again, but using its "hot restart" feature:
27 | # passing the previously obtained output_quantities ensures that
28 | # the run starts already close to the equilibrium, so it will take
29 | # very few iterations to converge this time.
30 | perturbed_output = vmecpp.run(vmec_input, restart_from=vmec_output, verbose=False)
31 | print("perturbed volume:", perturbed_output.wout.volume_p)
32 |
--------------------------------------------------------------------------------
/examples/plot_plasma_boundary.py:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | #
3 | # SPDX-License-Identifier: MIT
4 | """Showcases how to plot the geometry of the plasma boundary computed using VMEC++.
5 |
6 | Note, that this script requires matplotlib as an additional dependency.
7 | """
8 |
9 | from pathlib import Path
10 |
11 | import matplotlib.pyplot as plt
12 | import numpy as np
13 |
14 | import vmecpp
15 |
16 |
17 | def plot_plasma_boundary():
18 | # We need a VmecInput, a Python object that corresponds
19 | # to the classic "input.*" files.
20 | # We can construct it from such a classic VMEC input file
21 | # (Fortran namelist called INDATA):
22 | input_file = Path(__file__).parent / "data" / "input.w7x"
23 | vmec_input = vmecpp.VmecInput.from_file(input_file)
24 |
25 | # Now we can run VMEC++:
26 | vmec_output = vmecpp.run(vmec_input)
27 |
28 | # The output object contains the Fourier coefficients of the geometry in R and Z
29 | # as a function of the poloidal (theta) and toroidal (phi) angle-like coordinates
30 | # for a number of discrete radial locations.
31 |
32 | # number of flux surfaces, i.e., final radial resolution
33 | ns = vmec_output.wout.ns
34 |
35 | # poloidal mode numbers: m
36 | xm = vmec_output.wout.xm
37 |
38 | # toroidal mode numbers: n * nfp
39 | xn = vmec_output.wout.xn
40 |
41 | # stellarator-symmetric Fourier coefficients of flux surface geometry R ~ cos(m * theta - n * nfp * phi)
42 | rmnc = vmec_output.wout.rmnc
43 |
44 | # stellarator-symmetric Fourier coefficients of flux surface geometry Z ~ sin(m * theta - n * nfp * phi)
45 | zmns = vmec_output.wout.zmns
46 |
47 | # plot the outermost (last) flux surface, which is the plasma boundary
48 | j = ns - 1
49 |
50 | # resolution over the flux surface
51 | num_theta = 101
52 | num_phi = 181
53 |
54 | # grid in theta and phi along the flux surface
55 | grid_theta = np.linspace(0.0, 2.0 * np.pi, num_theta, endpoint=True)
56 | grid_phi = np.linspace(0.0, 2.0 * np.pi, num_phi, endpoint=True)
57 |
58 | # compute Cartesian coordinates of flux surface geometry
59 | x = np.zeros([num_theta, num_phi])
60 | y = np.zeros([num_theta, num_phi])
61 | z = np.zeros([num_theta, num_phi])
62 | for idx_theta, theta in enumerate(grid_theta):
63 | for idx_phi, phi in enumerate(grid_phi):
64 | kernel = xm * theta - xn * phi
65 | r = np.dot(rmnc[:, j], np.cos(kernel))
66 | x[idx_theta, idx_phi] = r * np.cos(phi)
67 | y[idx_theta, idx_phi] = r * np.sin(phi)
68 | z[idx_theta, idx_phi] = np.dot(zmns[:, j], np.sin(kernel))
69 |
70 | # actually make the 3D plot
71 | fig = plt.figure()
72 | ax = fig.add_subplot(projection="3d")
73 |
74 | # Plot the surface
75 | ax.plot_surface(x, y, z)
76 |
77 | # Set an equal aspect ratio
78 | ax.set_aspect("equal")
79 |
80 | plt.show()
81 |
82 |
83 | if __name__ == "__main__":
84 | plot_plasma_boundary()
85 |
--------------------------------------------------------------------------------
/examples/python_api.py:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | #
3 | # SPDX-License-Identifier: MIT
4 | """How to run VMEC++ via the Python API."""
5 |
6 | from pathlib import Path
7 |
8 | import vmecpp
9 |
10 |
11 | def run_vmecpp():
12 | # We need a VmecInput, a Python object that corresponds
13 | # to the classic "input.*" files.
14 | # We can construct it from such a classic VMEC input file
15 | # (Fortran namelist called INDATA):
16 | input_file = Path(__file__).parent / "data" / "input.solovev"
17 | input = vmecpp.VmecInput.from_file(input_file)
18 |
19 | # Now we can run VMEC++:
20 | output = vmecpp.run(input)
21 | # An optional parameter max_threads=N controls
22 | # the level of parallelism in VMEC++.
23 | # By default, VMEC++ runs with max_threads equal
24 | # to the number of logical cores on the machine.
25 | # Note that the actual level of parallelism is
26 | # limited so that each thread operates on at
27 | # least two flux surfaces, so VMEC++ might use
28 | # less threads than max_threads if there are
29 | # few flux surfaces.
30 |
31 | # We can save the output wout as a classic NetCDF
32 | # wout file if needed:
33 | output.wout.save("wout_solovev.nc")
34 | # Text files like threed1 are no longer supporeted in
35 | # the same way, but the data is available:
36 | print("threed1_volumetrics", output.threed1_volumetrics.model_dump_json(indent=2))
37 | # Free-boundary runs work just the same, in which
38 | # case VmecInput will also include a path to an
39 | # "mgrid_*.nc" file produced by MAKEGRID.
40 |
41 |
42 | if __name__ == "__main__":
43 | run_vmecpp()
44 |
--------------------------------------------------------------------------------
/examples/python_api_input.py:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | #
3 | # SPDX-License-Identifier: MIT
4 | """How to create a VmecInput object via the Python API."""
5 |
6 | import numpy as np
7 |
8 | import vmecpp
9 |
10 | # We can construct a VmecInput object completely from Python.
11 | # As a starting point, we can use the VMEC2000 defaults.
12 | vmec_input = vmecpp.VmecInput.default()
13 | print("default = \n", vmec_input.to_json(indent=2))
14 |
15 | # Let's set a pressure profile that decays linearly from 0.125 to zero.
16 | vmec_input.am = np.array([0.125, -0.125])
17 | # ...a constant rotational transform of 1.0
18 | vmec_input.ai = np.array([1.0])
19 | # ...and increase the number of iterations so it has time to converge.
20 | vmec_input.niter_array = np.array([200])
21 |
22 | # Finally we construct boundary geometry that is approximately the Solovev
23 | # equilibrium. There are helper methods for explicitly resizing coefficient
24 | # arrays to the correct shape by padding them with zeros left and right.
25 | vmec_input.rbc = vmecpp.VmecInput.resize_2d_coeff(
26 | np.array([[4.0], [1.0], [-0.068]]),
27 | vmec_input.mpol,
28 | vmec_input.ntor,
29 | )
30 | vmec_input.zbs = vmecpp.VmecInput.resize_2d_coeff(
31 | np.array([[0], [1.58], [0.01]]),
32 | vmec_input.mpol,
33 | vmec_input.ntor,
34 | )
35 |
36 | # Now it is time to run the equilibrium solver and inspect the results.
37 | output = vmecpp.run(vmec_input)
38 | # Once the residual force is small enough, the equilibrium is converged.
39 | print(
40 | "Residual force: ",
41 | output.wout.fsqr,
42 | "is below the threshold of",
43 | vmec_input.ftol_array,
44 | )
45 |
--------------------------------------------------------------------------------
/examples/simsopt_integration.py:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | #
3 | # SPDX-License-Identifier: MIT
4 | """How to run VMEC++ within SIMSOPT."""
5 |
6 | from pathlib import Path
7 |
8 | import vmecpp.simsopt_compat
9 |
10 |
11 | def run_vmecpp_with_simsopt():
12 | input_file = Path(__file__).parent / "data" / "input.solovev"
13 | vmec = vmecpp.simsopt_compat.Vmec(input_file)
14 | print(f"Computed plasma volume: {vmec.volume()}")
15 |
16 |
17 | if __name__ == "__main__":
18 | run_vmecpp_with_simsopt()
19 |
--------------------------------------------------------------------------------
/examples/simsopt_qh_fixed_resolution.py:
--------------------------------------------------------------------------------
1 | """
2 | This example reproduces https://github.com/hiddenSymmetries/simsopt/blob/master/examples/2_Intermediate/QH_fixed_resolution.py
3 | using VMEC++ instead of VMEC2000.
4 |
5 | It shows how to optimize a VMEC equilibrium for quasi-helical symmetry (M=1, N=1) throughout the volume.
6 | """
7 |
8 | from pathlib import Path
9 |
10 | import numpy as np
11 | from simsopt.mhd import QuasisymmetryRatioResidual
12 | from simsopt.objectives import LeastSquaresProblem
13 | from simsopt.solve import least_squares_mpi_solve
14 | from simsopt.util import MpiPartition, proc0_print
15 |
16 | from vmecpp.simsopt_compat import Vmec
17 |
18 | # This problem has 24 degrees of freedom, so we can use 24 + 1 = 25
19 | # concurrent function evaluations for 1-sided finite difference
20 | # gradients.
21 | proc0_print("Running 2_Intermediate/QH_fixed_resolution.py")
22 | proc0_print("=============================================")
23 |
24 | mpi = MpiPartition(25)
25 |
26 | filename = Path(__file__).parent / "data" / "input.nfp4_QH_warm_start"
27 | vmec = Vmec(str(filename))
28 |
29 | # Define parameter space:
30 | surf = vmec.boundary
31 | surf.fix_all()
32 | max_mode = 2
33 | surf.fixed_range(mmin=0, mmax=max_mode, nmin=-max_mode, nmax=max_mode, fixed=False)
34 | surf.fix("rc(0,0)") # Major radius
35 |
36 | proc0_print("Parameter space:", surf.dof_names)
37 |
38 | # Configure quasisymmetry objective:
39 | qs = QuasisymmetryRatioResidual(
40 | vmec,
41 | np.arange(0, 1.01, 0.1), # Radii to target
42 | helicity_m=1,
43 | helicity_n=-1,
44 | ) # (M, N) you want in |B|
45 |
46 | # Define objective function
47 | prob = LeastSquaresProblem.from_tuples([(vmec.aspect, 7, 1), (qs.residuals, 0, 1)])
48 |
49 | # Make sure all procs participate in computing the objective:
50 | prob.objective()
51 |
52 | proc0_print("Quasisymmetry objective before optimization:", qs.total())
53 | proc0_print("Total objective before optimization:", prob.objective())
54 |
55 | # To keep this example fast, we stop after the first function
56 | # evaluation. For a "real" optimization, remove the max_nfev
57 | # parameter.
58 | least_squares_mpi_solve(prob, mpi, grad=True, rel_step=1e-5, abs_step=1e-8, max_nfev=1)
59 |
60 | # Make sure all procs participate in computing the objective:
61 | prob.objective()
62 |
63 | proc0_print("Final aspect ratio:", vmec.aspect())
64 | proc0_print("Quasisymmetry objective after optimization:", qs.total())
65 | proc0_print("Total objective after optimization:", prob.objective())
66 |
67 | proc0_print("End of 2_Intermediate/QH_fixed_resolution.py")
68 | proc0_print("============================================")
69 |
--------------------------------------------------------------------------------
/src/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_subdirectory(vmecpp)
2 | set (vmecpp_sources "${vmecpp_sources}" PARENT_SCOPE)
3 |
--------------------------------------------------------------------------------
/src/vmecpp/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_subdirectory(cpp)
2 | set (vmecpp_sources "${vmecpp_sources}" PARENT_SCOPE)
3 |
--------------------------------------------------------------------------------
/src/vmecpp/__main__.py:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | #
3 | # SPDX-License-Identifier: MIT
4 | import argparse
5 | import importlib.metadata
6 | from pathlib import Path
7 |
8 | import vmecpp
9 |
10 |
11 | def parse_arguments() -> argparse.Namespace:
12 | p = argparse.ArgumentParser(
13 | description="VMEC++ is a free-boundary ideal-MHD equilibrium solver for stellarators and tokamaks."
14 | )
15 | p.add_argument(
16 | "input_file",
17 | help="A VMEC input file either in the classic Fortran 'indata' format or in VMEC++'s JSON format.",
18 | type=Path,
19 | )
20 | p.add_argument(
21 | "-t",
22 | "--max-threads",
23 | help="Maximum number of threads that VMEC++ should spawn. The actual number might still be lower that this in case there are too few flux surfaces to keep these many threads busy.",
24 | type=int,
25 | )
26 | p.add_argument(
27 | "-q",
28 | "--quiet",
29 | help="If present, silences the printing of VMEC++ logs to standard output.",
30 | action="store_true",
31 | )
32 | p.add_argument(
33 | "-v",
34 | "--version",
35 | help="Print VMEC++ version information and exit.",
36 | action="version",
37 | version=f"vmecpp v{importlib.metadata.version('vmecpp')}",
38 | )
39 | return p.parse_args()
40 |
41 |
42 | args = parse_arguments()
43 |
44 | input = vmecpp.VmecInput.from_file(args.input_file)
45 | output = vmecpp.run(input, max_threads=args.max_threads, verbose=not args.quiet)
46 |
47 | configuration_name = vmecpp._util.get_vmec_configuration_name(args.input_file)
48 | wout_file = Path(f"wout_{configuration_name}.nc")
49 | output.wout.save(wout_file)
50 |
51 | print(f"\nOutput written to {wout_file}") # noqa: T201
52 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/.bazelrc:
--------------------------------------------------------------------------------
1 | # general compilation options
2 | build --cxxopt="-std=c++20" --copt="-fdiagnostics-color=always"
3 | # If compiling with clang or intel compilers, you will need to adjust the linked OpenMP library from libgomp to libomp
4 | build --copt="-fopenmp" --linkopt="-lgomp"
5 |
6 | # workaround for a Bazel 7 issue with rules_foreign_cc: https://github.com/bazelbuild/rules_foreign_cc/issues/1129
7 | build --sandbox_add_mount_pair=/tmp
8 |
9 | # -fno-math-errno yields a few % of performance improvements and we don't check errno anyways
10 | build:opt -c opt --copt="-O3" --copt="-fno-math-errno"
11 | build:perf -c opt --copt="-O3" --copt="-fno-math-errno" --copt="-g" --strip=never --copt="-fno-omit-frame-pointer"
12 | build:dbg -c dbg --copt="-O0"
13 | build:asan -c dbg --features=asan --strip=never
14 | # Undefined behavior sanitization is quite slow, to compensate we compile in opt mode
15 | build:ubsan -c opt --features=ubsan --strip=never
16 |
17 | # We observe CPU overloads when having too many OpenMP threads in parallel on the CI machines.
18 | # So far, it looks similar to this issue: https://stackoverflow.com/q/70126350
19 | # As of now, we use the solution to run the tests one after another,
20 | # as suggested here: https://stackoverflow.com/a/70084246.
21 | test --local_test_jobs=1
22 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/.bazelversion:
--------------------------------------------------------------------------------
1 | 7.4.1
2 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/.gitattributes:
--------------------------------------------------------------------------------
1 | vmecpp/test_data/mgrid_cth_like.nc filter=lfs diff=lfs merge=lfs -text
2 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_subdirectory(util)
2 | add_subdirectory(vmecpp)
3 | set (vmecpp_sources "${vmecpp_sources}" PARENT_SCOPE)
4 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/MODULE.bazel:
--------------------------------------------------------------------------------
1 | # Find available modules at https://registry.bazel.build
2 |
3 | # NOTE: if updating the version, must also update src/vmecpp/__about__.py
4 | module(name = "vmecpp", version = "0.4.2")
5 |
6 | bazel_dep(name = "abseil-cpp", version = "20230802.0.bcr.1")
7 | bazel_dep(name = "eigen", version = "3.4.0.bcr.3")
8 | bazel_dep(name = "google_benchmark", version = "1.8.2")
9 | bazel_dep(name = "googletest", version = "1.14.0")
10 |
11 | bazel_dep(name = "nlohmann_json", version = "3.11.3")
12 |
13 | # Pybind11: see https://github.com/pybind/pybind11_bazel#bzlmod
14 | bazel_dep(name = "pybind11_bazel", version = "2.13.6")
15 |
16 | python_configure = use_extension("@pybind11_bazel//:python_configure.bzl", "extension")
17 | use_repo(python_configure, "local_config_python", "pybind11")
18 |
19 | bazel_dep(name = "rules_cc", version = "0.0.9")
20 | bazel_dep(name = "rules_foreign_cc", version = "0.12.0")
21 |
22 |
23 | bazel_dep(name = "rules_python", version = "0.34.0")
24 | python = use_extension("@rules_python//python/extensions:python.bzl", "python")
25 | python.toolchain(
26 | configure_coverage_tool = True,
27 | # Only set when you have multiple toolchain versions.
28 | is_default = True,
29 | python_version = "3.10",
30 | ignore_root_user_error = True,
31 | )
32 |
33 | # zlib is needed for NetCDF4's version of HDF5
34 | bazel_dep(name = "zlib", version = "1.3.1.bcr.5")
35 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/WORKSPACE.bazel:
--------------------------------------------------------------------------------
1 | # NOTE: prefer adding dependencies to MODULE.bazel instead if possible.
2 |
3 | workspace(name = "vmecpp")
4 |
5 | load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
6 |
7 | # Accurate Biot-Savart routines with Correct Asymptotic Behaviour (C++)
8 | # https://github.com/jonathanschilling/abscab
9 | http_archive(
10 | name = "abscab_cpp",
11 | urls = [ "https://github.com/jonathanschilling/abscab-cpp/archive/refs/tags/v1.0.3.tar.gz" ],
12 | strip_prefix = "abscab-cpp-1.0.3",
13 | sha256 = "d7d4d8060117ac047ca7a3c5824f79cc7d8f42c538e542946650b188c7d2e145",
14 | )
15 |
16 | # HDF5
17 | # The following only downloads the archive, the actual build file is in third_party/hdf5.
18 | # See also https://bazelbuild.github.io/rules_foreign_cc/main/cmake.html.
19 | # Switch this to a bazel module when avaiable: https://github.com/bazelbuild/bazel-central-registry/issues/1327
20 | _ALL_CONTENT = """\
21 | filegroup(
22 | name = "all_srcs",
23 | srcs = glob(["**"]),
24 | visibility = ["//visibility:public"],
25 | )
26 | """
27 |
28 | http_archive(
29 | name = "hdf5",
30 | build_file_content = _ALL_CONTENT,
31 | sha256 = "df5ee33c74d5efb59738075ef96f4201588e1f1eeb233f047ac7fd1072dee1f6",
32 | urls = [ "https://github.com/HDFGroup/hdf5/archive/refs/tags/hdf5-1_14_3.tar.gz" ],
33 | strip_prefix = "hdf5-hdf5-1_14_3",
34 | )
35 |
36 | http_archive(
37 | name = "netcdf4",
38 | build_file_content = _ALL_CONTENT,
39 | integrity = "sha256-mQ9G1JUl1qtdxCSfhoTG3ur1Teb+xjoYfp+zgswP/f8=",
40 | urls = [ "https://github.com/Unidata/netcdf-c/archive/refs/tags/v4.9.3.tar.gz" ],
41 | strip_prefix = "netcdf-c-4.9.3",
42 | )
43 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/third_party/hdf5/BUILD.bazel:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | #
3 | # SPDX-License-Identifier: MIT
4 | load("@rules_foreign_cc//foreign_cc:defs.bzl", "cmake")
5 |
6 | cmake(
7 | name = "hdf5",
8 | lib_source = "@hdf5//:all_srcs",
9 | # to automatically get a multi-core build
10 | generate_args = ["-GNinja"],
11 | cache_entries = {
12 | # enable C++ API (slightly more friendly wrapper around the C API)
13 | "HDF5_BUILD_CPP_LIB": "ON",
14 | # enable ZLIB support, since this is a hard requirement by NetCDF4
15 | "HDF5_ENABLE_Z_LIB_SUPPORT" : "ON",
16 | # disable SZIP support, since this might not be available on all systems
17 | # and is not needed for our use case
18 | "HDF5_ENABLE_SZIP_SUPPORT" : "OFF",
19 | "HDF5_ENABLE_SZIP_ENCODING" : "OFF",
20 | "SZIP_INCLUDE_DIR": "IGNORE_THIS_PATH",
21 | "SZIP_LIBRARY": "IGNORE_THIS_PATH",
22 | "BUILD_TESTING": "OFF",
23 | "HDF5_BUILD_TOOLS": "OFF",
24 | },
25 | # When building in dbg mode the artifact is called libhdf5_debug.a.
26 | # This is controlled by the CMAKE_DEBUG_POSTFIX cmake variable but
27 | # unfortunately setting it to the empty string counts as leaving it
28 | # unset, so I don't know how to stop cmake from appending the postfix.
29 | # Instead we change the expected static lib name here in Bazel.
30 | out_static_libs = select({
31 | "//conditions:default": ["libhdf5_cpp.a", "libhdf5_hl.a", "libhdf5.a"],
32 | ":debug": ["libhdf5_cpp_debug.a", "libhdf5_hl_debug.a", "libhdf5_debug.a"],
33 | }),
34 | visibility = [ "//visibility:public" ],
35 | deps=[
36 | "@zlib",
37 | ],
38 | )
39 |
40 | config_setting(
41 | name = "debug",
42 | values = {"compilation_mode": "dbg"}
43 | )
44 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/third_party/netcdf4/BUILD.bazel:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | #
3 | # SPDX-License-Identifier: MIT
4 | load("@rules_foreign_cc//foreign_cc:defs.bzl", "cmake")
5 |
6 | cmake(
7 | name = "netcdf4",
8 | lib_source = "@netcdf4//:all_srcs",
9 | # to automatically get a multi-core build
10 | generate_args = ["-GNinja"],
11 | cache_entries = {
12 | "BUILD_TESTING": "OFF",
13 | "BUILD_SHARED_LIBS": "OFF",
14 | "NETCDF_ENABLE_DAP": "OFF",
15 | "NETCDF_ENABLE_DAP2": "OFF",
16 | "NETCDF_ENABLE_DAP4": "OFF",
17 | "NETCDF_ENABLE_NCZARR": "OFF",
18 | "NETCDF_ENABLE_NCZARR_ZIP": "OFF",
19 | },
20 | copts = [
21 | "-fPIC",
22 | ],
23 | out_static_libs = ["libnetcdf.a"],
24 | visibility = [ "//visibility:public" ],
25 | deps = [
26 | "//third_party/hdf5",
27 | ]
28 | )
29 |
30 | config_setting(
31 | name = "debug",
32 | values = {"compilation_mode": "dbg"}
33 | )
34 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/util/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_subdirectory(file_io)
2 | add_subdirectory(hdf5_io)
3 | add_subdirectory(json_io)
4 | add_subdirectory(netcdf_io)
5 | add_subdirectory(testing)
6 | set (vmecpp_sources "${vmecpp_sources}" PARENT_SCOPE)
7 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/util/file_io/BUILD.bazel:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | #
3 | # SPDX-License-Identifier: MIT
4 | cc_library(
5 | name = "file_io",
6 | srcs = ["file_io.cc"],
7 | hdrs = ["file_io.h"],
8 | visibility = ["//visibility:public"],
9 | deps = [
10 | "@abseil-cpp//absl/status:status",
11 | "@abseil-cpp//absl/status:statusor",
12 | ],
13 | )
14 |
15 | cc_test(
16 | name = "file_io_test",
17 | srcs = ["file_io_test.cc"],
18 | deps = [
19 | ":file_io",
20 | "@googletest//:gtest_main",
21 | ],
22 | data = [
23 | "//util/test_data:test_files"
24 | ],
25 | )
26 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/util/file_io/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | list (APPEND vmecpp_sources
2 | ${CMAKE_CURRENT_SOURCE_DIR}/file_io.cc
3 | ${CMAKE_CURRENT_SOURCE_DIR}/file_io.h
4 | )
5 | set (vmecpp_sources "${vmecpp_sources}" PARENT_SCOPE)
6 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/util/file_io/file_io.cc:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | //
3 | //
4 | // SPDX-License-Identifier: MIT
5 | #include "util/file_io/file_io.h"
6 |
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | #include "absl/status/status.h"
13 | #include "absl/status/statusor.h"
14 |
15 | namespace fs = std::filesystem;
16 |
17 | absl::StatusOr file_io::ReadFile(const fs::path& filename) {
18 | if (!fs::exists(filename)) {
19 | return absl::NotFoundError("File " + filename.string() + " not found.");
20 | }
21 |
22 | std::ifstream ifs(filename.c_str());
23 | if (!ifs.is_open()) {
24 | return absl::PermissionDeniedError("File " + filename.string() +
25 | " could not be opened for reading.");
26 | }
27 |
28 | std::string contents(std::istreambuf_iterator{ifs}, {});
29 | return contents;
30 | }
31 |
32 | absl::Status file_io::WriteFile(const std::filesystem::path& filename,
33 | const std::string& contents) {
34 | std::ofstream file_stream(filename.string());
35 | if (!file_stream.is_open()) {
36 | return absl::PermissionDeniedError("File " + filename.string() +
37 | " could not be opened for writing.");
38 | }
39 |
40 | file_stream << contents;
41 |
42 | file_stream.flush();
43 | file_stream.close();
44 |
45 | return absl::OkStatus();
46 | }
47 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/util/file_io/file_io.h:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | //
3 | //
4 | // SPDX-License-Identifier: MIT
5 | #ifndef UTIL_FILE_IO_FILE_IO_H_
6 | #define UTIL_FILE_IO_FILE_IO_H_
7 |
8 | #include
9 | #include
10 |
11 | #include "absl/status/statusor.h"
12 |
13 | namespace file_io {
14 |
15 | // read a file into a string
16 | absl::StatusOr ReadFile(const std::filesystem::path& filename);
17 |
18 | // write a (text) file from a string
19 | absl::Status WriteFile(const std::filesystem::path& filename,
20 | const std::string& contents);
21 |
22 | } // namespace file_io
23 |
24 | #endif // UTIL_FILE_IO_FILE_IO_H_
25 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/util/file_io/file_io_test.cc:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | //
3 | //
4 | // SPDX-License-Identifier: MIT
5 | #include "util/file_io/file_io.h"
6 |
7 | #include
8 | #include
9 |
10 | #include "gtest/gtest.h"
11 |
12 | namespace file_io {
13 |
14 | TEST(TestFileIO, CheckReadFile) {
15 | // test that reading non-existent file fails
16 | std::string nonexistent_filename = "this/file/does/likely/not/exist";
17 |
18 | // make sure that the assumed-nonexisting file actually does not exist
19 | std::ifstream ifs(nonexistent_filename);
20 | ASSERT_FALSE(ifs.is_open());
21 |
22 | // now check that reading it actually returns an empty string
23 | absl::StatusOr no_contents = ReadFile(nonexistent_filename);
24 | EXPECT_FALSE(no_contents.ok());
25 |
26 | // read empty file
27 | absl::StatusOr empty = ReadFile("util/test_data/empty.txt");
28 | ASSERT_TRUE(empty.ok()) << empty.status().message();
29 | EXPECT_EQ(*empty, "");
30 |
31 | // read simple text file
32 | absl::StatusOr lorem = ReadFile("util/test_data/lorem.txt");
33 | ASSERT_TRUE(lorem.ok()) << lorem.status().message();
34 | EXPECT_EQ(*lorem, "lorem ipsum\n");
35 | } // CheckReadFile
36 |
37 | // rely on file_io::ReadFile for checking if test was successful
38 | TEST(TestFileIO, CheckWriteFile) {
39 | // create a temporary file name
40 | namespace fs = std::filesystem;
41 | const fs::path temp_file =
42 | fs::temp_directory_path() /
43 | ("file_io_test_checkwritefile_" + std::to_string(getpid()));
44 |
45 | std::string contents = "Testing file_io::WriteFile...\n";
46 |
47 | absl::Status status = WriteFile(temp_file, contents);
48 | ASSERT_TRUE(status.ok()) << status.message();
49 |
50 | // now check that the file actually contains the desired contents
51 | absl::StatusOr contents_reread = ReadFile(temp_file);
52 | ASSERT_TRUE(contents_reread.ok()) << contents_reread.status().message();
53 | EXPECT_EQ(*contents_reread, contents);
54 |
55 | // clean up the temporary file after use
56 | std::remove(temp_file.c_str());
57 | } // CheckWriteFile
58 |
59 | } // namespace file_io
60 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/util/hdf5_io/BUILD.bazel:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | #
3 | # SPDX-License-Identifier: MIT
4 | cc_library(
5 | name = "hdf5_io",
6 | srcs = ["hdf5_io.cc"],
7 | hdrs = ["hdf5_io.h"],
8 | visibility = ["//visibility:public"],
9 | deps = [
10 | "@eigen",
11 | "//third_party/hdf5",
12 | ],
13 | )
14 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/util/hdf5_io/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | list (APPEND vmecpp_sources
2 | ${CMAKE_CURRENT_SOURCE_DIR}/hdf5_io.cc
3 | ${CMAKE_CURRENT_SOURCE_DIR}/hdf5_io.h
4 | )
5 | set (vmecpp_sources "${vmecpp_sources}" PARENT_SCOPE)
6 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/util/json_io/BUILD.bazel:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | #
3 | # SPDX-License-Identifier: MIT
4 | cc_library(
5 | name = "json_io",
6 | srcs = ["json_io.cc"],
7 | hdrs = ["json_io.h"],
8 | visibility = ["//visibility:public"],
9 | deps = [
10 | "@abseil-cpp//absl/status:statusor",
11 | "@abseil-cpp//absl/strings:str_format",
12 | "@nlohmann_json//:json",
13 | ],
14 | )
15 |
16 | cc_test(
17 | name = "json_io_test",
18 | srcs = ["json_io_test.cc"],
19 | deps = [
20 | ":json_io",
21 | "@googletest//:gtest_main",
22 | ]
23 | )
24 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/util/json_io/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | list (APPEND vmecpp_sources
2 | ${CMAKE_CURRENT_SOURCE_DIR}/json_io.cc
3 | ${CMAKE_CURRENT_SOURCE_DIR}/json_io.h
4 | )
5 | set (vmecpp_sources "${vmecpp_sources}" PARENT_SCOPE)
6 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/util/json_io/json_io.h:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | //
3 | //
4 | // SPDX-License-Identifier: MIT
5 | #ifndef UTIL_JSON_IO_JSON_IO_H_
6 | #define UTIL_JSON_IO_JSON_IO_H_
7 |
8 | #include
9 | #include
10 | #include
11 |
12 | #include "absl/status/statusor.h"
13 | #include "nlohmann/json.hpp"
14 |
15 | namespace json_io {
16 |
17 | // Try to read a bool from the given JSON data.
18 | // If the variable is not present in the given JSON object, status will be ok
19 | // and optional will be not populated. If the variable was found, but was not
20 | // the correct type, the status will be not ok.
21 | absl::StatusOr > JsonReadBool(const nlohmann::json& j,
22 | const std::string& name);
23 |
24 | // Try to read an int from the given JSON data.
25 | // If the variable is not present in the given JSON object, status will be ok
26 | // and optional will be not populated. If the variable was found, but was not
27 | // the correct type, the status will be not ok.
28 | absl::StatusOr > JsonReadInt(const nlohmann::json& j,
29 | const std::string& name);
30 |
31 | // Try to read a double from the given JSON data.
32 | // If the variable is not present in the given JSON object, status will be ok
33 | // and optional will be not populated. If the variable was found, but was not
34 | // the correct type, the status will be not ok.
35 | absl::StatusOr > JsonReadDouble(const nlohmann::json& j,
36 | const std::string& name);
37 |
38 | // Try to read a string from the given JSON data.
39 | // If the variable is not present in the given JSON object, status will be ok
40 | // and optional will be not populated. If the variable was found, but was not
41 | // the correct type, the status will be not ok.
42 | absl::StatusOr > JsonReadString(
43 | const nlohmann::json& j, const std::string& name);
44 |
45 | // Try to read a vector of integers from the given JSON data.
46 | // If the variable is not present in the given JSON object, status will be ok
47 | // and optional will be not populated. If the variable was found, but was not
48 | // the correct type, the status will be not ok.
49 | absl::StatusOr > > JsonReadVectorInt(
50 | const nlohmann::json& j, const std::string& name);
51 |
52 | // Try to read a vector of doubles from the given JSON data.
53 | // If the variable is not present in the given JSON object, status will be ok
54 | // and optional will be not populated. If the variable was found, but was not
55 | // the correct type, the status will be not ok.
56 | absl::StatusOr > > JsonReadVectorDouble(
57 | const nlohmann::json& j, const std::string& name);
58 |
59 | } // namespace json_io
60 |
61 | #endif // UTIL_JSON_IO_JSON_IO_H_
62 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/util/netcdf_io/BUILD.bazel:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | #
3 | # SPDX-License-Identifier: MIT
4 | cc_library(
5 | name = "netcdf_io",
6 | srcs = ["netcdf_io.cc"],
7 | hdrs = ["netcdf_io.h"],
8 | visibility = ["//visibility:public"],
9 | deps = [
10 | "@abseil-cpp//absl/log:check",
11 | "@abseil-cpp//absl/log:log",
12 | "@abseil-cpp//absl/strings",
13 | "@abseil-cpp//absl/strings:str_format",
14 | "//third_party/netcdf4",
15 | ],
16 | )
17 |
18 | filegroup(
19 | name = "example_netcdf_files",
20 | visibility = ["//util/netcdf_io:__subpackages__"],
21 | srcs = [
22 | "example_netcdf.nc",
23 | ],
24 | )
25 |
26 | cc_test(
27 | name = "netcdf_io_test",
28 | srcs = ["netcdf_io_test.cc"],
29 | data = [
30 | ":example_netcdf_files",
31 | ],
32 | deps = [
33 | ":netcdf_io",
34 | "@googletest//:gtest_main",
35 | ],
36 | )
37 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/util/netcdf_io/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | list (APPEND vmecpp_sources
2 | ${CMAKE_CURRENT_SOURCE_DIR}/netcdf_io.cc
3 | ${CMAKE_CURRENT_SOURCE_DIR}/netcdf_io.h
4 | )
5 | set (vmecpp_sources "${vmecpp_sources}" PARENT_SCOPE)
6 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/util/netcdf_io/example_netcdf.nc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/proximafusion/vmecpp/ef96e726f1cc9bda8bb2a97ab32add94891adb36/src/vmecpp/cpp/util/netcdf_io/example_netcdf.nc
--------------------------------------------------------------------------------
/src/vmecpp/cpp/util/netcdf_io/netcdf_io.h:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | //
3 | //
4 | // SPDX-License-Identifier: MIT
5 | #ifndef UTIL_NETCDF_IO_NETCDF_IO_H_
6 | #define UTIL_NETCDF_IO_NETCDF_IO_H_
7 |
8 | #include
9 | #include
10 |
11 | namespace netcdf_io {
12 |
13 | // Read a scalar `bool` variable in Fortran VMEC style from the (opened) NetCDF
14 | // file identified by `ncid`. It is expected that the value is stored in a
15 | // scalar `int` variable named `__logical__`.
16 | bool NetcdfReadBool(int ncid, const std::string& variable_name);
17 |
18 | // Read a scalar `char` variable from the (opened) NetCDF file identified by
19 | // `ncid`. It is expected that the value is stored in a length-1 `char` array.
20 | char NetcdfReadChar(int ncid, const std::string& variable_name);
21 |
22 | // Read a scalar `int` variable from the (opened) NetCDF file identified by
23 | // `ncid`.
24 | int NetcdfReadInt(int ncid, const std::string& variable_name);
25 |
26 | // Read a scalar `double` variable from the (opened) NetCDF file identified by
27 | // `ncid`.
28 | double NetcdfReadDouble(int ncid, const std::string& variable_name);
29 |
30 | // Read a string from the (opened) NetCDF file identified by `ncid`.
31 | // It is expected that the data is stored as a rank-1 `char` array.
32 | // Whitespace at the start and end of the `char` array is stripped.
33 | std::string NetcdfReadString(int ncid, const std::string& variable_name);
34 |
35 | // Read a rank-1 `double` array from the (opened) NetCDF file identified by
36 | // `ncid`.
37 | std::vector NetcdfReadArray1D(int ncid,
38 | const std::string& variable_name);
39 |
40 | // Read a rank-2 `double` array from the (opened) NetCDF file identified by
41 | // `ncid`.
42 | std::vector > NetcdfReadArray2D(
43 | int ncid, const std::string& variable_name);
44 |
45 | // Read a rank-3 `double` array from the (opened) NetCDF file identified by
46 | // `ncid`.
47 | std::vector > > NetcdfReadArray3D(
48 | int ncid, const std::string& variable_name);
49 |
50 | } // namespace netcdf_io
51 |
52 | #endif // UTIL_NETCDF_IO_NETCDF_IO_H_
53 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/util/test_data/BUILD.bazel:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | #
3 | # SPDX-License-Identifier: MIT
4 | filegroup(
5 | name = "test_files",
6 | visibility = ["//visibility:public"],
7 | srcs = [
8 | "empty.txt",
9 | "lorem.txt",
10 | ],
11 | )
12 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/util/test_data/empty.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/proximafusion/vmecpp/ef96e726f1cc9bda8bb2a97ab32add94891adb36/src/vmecpp/cpp/util/test_data/empty.txt
--------------------------------------------------------------------------------
/src/vmecpp/cpp/util/test_data/lorem.txt:
--------------------------------------------------------------------------------
1 | lorem ipsum
2 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/util/testing/BUILD.bazel:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | #
3 | # SPDX-License-Identifier: MIT
4 | cc_library(
5 | name = "numerical_comparison_lib",
6 | hdrs = ["numerical_comparison_lib.h"],
7 | srcs = ["numerical_comparison_lib.cc"],
8 | visibility = ["//visibility:public"],
9 | deps = [
10 | "@abseil-cpp//absl/strings:str_format",
11 | "@eigen",
12 | ]
13 | )
14 |
15 | cc_test(
16 | name = "numerical_comparison_test",
17 | srcs = ["numerical_comparison_test.cc"],
18 | deps = [
19 | ":numerical_comparison_lib",
20 | "@googletest//:gtest_main",
21 | ]
22 | )
23 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/util/testing/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | list (APPEND vmecpp_sources
2 | ${CMAKE_CURRENT_SOURCE_DIR}/numerical_comparison_lib.cc
3 | ${CMAKE_CURRENT_SOURCE_DIR}/numerical_comparison_lib.h
4 | )
5 | set (vmecpp_sources "${vmecpp_sources}" PARENT_SCOPE)
6 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/util/testing/numerical_comparison_lib.cc:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | //
3 | //
4 | // SPDX-License-Identifier: MIT
5 | #include "util/testing/numerical_comparison_lib.h"
6 |
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | #include "absl/strings/str_format.h"
13 |
14 | bool testing::IsCloseRelAbs(double expected, double actual, double tolerance) {
15 | const double rel_abs_error = (actual - expected) / (1.0 + std::abs(expected));
16 | if (std::abs(rel_abs_error) > tolerance ||
17 | (std::isnan(expected) != std::isnan(actual))) {
18 | std::cerr << absl::StrFormat(
19 | "out-of-tolerance: |% .3e| > % .3e\n expected = % .20e\n "
20 | "actual = % .20e\n",
21 | rel_abs_error, tolerance, expected, actual);
22 | return false;
23 | }
24 | return true;
25 | }
26 |
27 | bool testing::IsVectorCloseRelAbs(const std::vector& expected,
28 | const std::vector& actual,
29 | double tolerance) {
30 | const auto& expected_eigen = Eigen::Map(
31 | expected.data(), static_cast(expected.size()));
32 | const auto& actual_eigen = Eigen::Map(
33 | actual.data(), static_cast(actual.size()));
34 |
35 | return IsVectorCloseRelAbs(expected_eigen, actual_eigen, tolerance);
36 | }
37 |
38 | bool testing::IsVectorCloseRelAbs(const Eigen::VectorXd& expected,
39 | const Eigen::VectorXd& actual,
40 | double tolerance) {
41 | if (expected.size() != actual.size()) {
42 | return false;
43 | }
44 |
45 | for (int i = 0; i < expected.size(); ++i) {
46 | if (!IsCloseRelAbs(expected[i], actual[i], tolerance)) {
47 | return false;
48 | }
49 | }
50 |
51 | return true;
52 | }
53 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/util/testing/numerical_comparison_lib.h:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | //
3 | //
4 | // SPDX-License-Identifier: MIT
5 | #ifndef UTIL_TESTING_NUMERICAL_COMPARISON_LIB_H_
6 | #define UTIL_TESTING_NUMERICAL_COMPARISON_LIB_H_
7 |
8 | #include
9 | #include
10 |
11 | namespace testing {
12 |
13 | /// Check if two values are approximately equal within a prescribed tolerance.
14 | ///
15 | /// For values much smaller than 1, this is similar to a comparison of the
16 | /// absolute values. For values much greater than 1, this is similar to a
17 | /// comparison of the relative values. This method is described in Gill, Murray
18 | /// & Wright, "Practical Optimization" (1984).
19 | ///
20 | /// If the expected value is NaN, the actual value is checked to also be NaN.
21 | /// If the expected value is not NaN, the actual value is checked to also not be
22 | /// NaN.
23 | ///
24 | /// @param expected expected result
25 | /// @param actual actual result
26 | /// @param tolerance relative or absolute tolerance on the mismatch between the
27 | /// expected and the actual values
28 | /// @return true if the values match within the prescribed tolerance; false
29 | /// otherwise
30 | bool IsCloseRelAbs(double expected, double actual, double tolerance);
31 |
32 | /// Check that two STL vectors have same size and their elements pass the
33 | /// IsCloseRelAbs check pairwise.
34 | bool IsVectorCloseRelAbs(const std::vector& expected,
35 | const std::vector& actual, double tolerance);
36 |
37 | /// Check that two Eigen vectors have same size and their elements pass the
38 | /// IsCloseRelAbs check pairwise.
39 | bool IsVectorCloseRelAbs(const Eigen::VectorXd& expected,
40 | const Eigen::VectorXd& actual, double tolerance);
41 | } // namespace testing
42 |
43 | #endif // UTIL_TESTING_NUMERICAL_COMPARISON_LIB_H_
44 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/util/testing/numerical_comparison_test.cc:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | //
3 | //
4 | // SPDX-License-Identifier: MIT
5 | #include
6 |
7 | #include "gtest/gtest.h"
8 | #include "util/testing/numerical_comparison_lib.h"
9 |
10 | namespace testing {
11 |
12 | TEST(TestNumericalComparison, CheckIsCloseRelAbs) {
13 | EXPECT_TRUE(IsCloseRelAbs(1.0, 1.0, 0.0));
14 | EXPECT_TRUE(IsCloseRelAbs(1.0, 1.0, 1.0e-15));
15 |
16 | // like absolute error for small numbers
17 | EXPECT_TRUE(IsCloseRelAbs(1.0e-15, 2.0e-15, 1.0e-14));
18 |
19 | // like a relative error for relatively-small numbers
20 | EXPECT_TRUE(IsCloseRelAbs(1.0, 1.0 + 1.0e-15, 1.0e-14));
21 |
22 | // like a relative error for large numbers
23 | EXPECT_TRUE(IsCloseRelAbs(1.0e15, 1.0e15 + 1, 1.0e-14));
24 |
25 | EXPECT_FALSE(IsCloseRelAbs(1.0, 2.0, 1.0e-3));
26 |
27 | double nan = std::numeric_limits::quiet_NaN();
28 |
29 | EXPECT_FALSE(IsCloseRelAbs(1.0, nan, 1.0e-3));
30 | EXPECT_FALSE(IsCloseRelAbs(nan, 1.0, 1.0e-3));
31 |
32 | EXPECT_TRUE(IsCloseRelAbs(nan, nan, 1.0e-3));
33 | } // CheckIsCloseRelAbs
34 |
35 | } // namespace testing
36 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_subdirectory(common)
2 | add_subdirectory(free_boundary)
3 | add_subdirectory(vmec)
4 | set (vmecpp_sources "${vmecpp_sources}" PARENT_SCOPE)
5 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_subdirectory(composed_types_definition)
2 | add_subdirectory(composed_types_lib)
3 | add_subdirectory(flow_control)
4 | add_subdirectory(fourier_basis_fast_poloidal)
5 | add_subdirectory(fourier_basis_fast_toroidal)
6 | add_subdirectory(magnetic_configuration_definition)
7 | add_subdirectory(magnetic_configuration_lib)
8 | add_subdirectory(magnetic_field_provider)
9 | add_subdirectory(makegrid_lib)
10 | add_subdirectory(sizes)
11 | add_subdirectory(util)
12 | add_subdirectory(vmec_indata)
13 | set (vmecpp_sources "${vmecpp_sources}" PARENT_SCOPE)
14 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/composed_types_definition/BUILD.bazel:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | #
3 | # SPDX-License-Identifier: MIT
4 | cc_library(
5 | name="composed_types",
6 | visibility = ["//visibility:public"],
7 | hdrs=["composed_types.h"],
8 | )
9 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/composed_types_definition/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | list (APPEND vmecpp_sources
2 | ${CMAKE_CURRENT_SOURCE_DIR}/composed_types.h
3 | )
4 | set (vmecpp_sources "${vmecpp_sources}" PARENT_SCOPE)
5 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/composed_types_lib/BUILD.bazel:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | #
3 | # SPDX-License-Identifier: MIT
4 | cc_library(
5 | name = "composed_types_lib",
6 | srcs = ["composed_types_lib.cc"],
7 | hdrs = ["composed_types_lib.h"],
8 | visibility = ["//visibility:public"],
9 | deps = [
10 | "//vmecpp/common/composed_types_definition:composed_types",
11 | "@abseil-cpp//absl/strings:strings",
12 | "@abseil-cpp//absl/status:status",
13 | "@abseil-cpp//absl/status:statusor",
14 | "@abseil-cpp//absl/log:check",
15 | ]
16 | )
17 |
18 | cc_test(
19 | name = "composed_types_lib_test",
20 | srcs = ["composed_types_lib_test.cc"],
21 | deps = [
22 | ":composed_types_lib",
23 | "@googletest//:gtest_main",
24 | "//util/testing:numerical_comparison_lib",
25 | ]
26 | )
27 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/composed_types_lib/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | list (APPEND vmecpp_sources
2 | ${CMAKE_CURRENT_SOURCE_DIR}/composed_types_lib.cc
3 | ${CMAKE_CURRENT_SOURCE_DIR}/composed_types_lib.h
4 | )
5 | set (vmecpp_sources "${vmecpp_sources}" PARENT_SCOPE)
6 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/flow_control/BUILD.bazel:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | #
3 | # SPDX-License-Identifier: MIT
4 | cc_library(
5 | name = "flow_control",
6 | srcs = ["flow_control.cc"],
7 | hdrs = ["flow_control.h"],
8 | visibility = ["//visibility:public"],
9 | deps = [
10 | "//vmecpp/common/util:util",
11 | "@abseil-cpp//absl/log:check",
12 | ]
13 | )
14 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/flow_control/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | list (APPEND vmecpp_sources
2 | ${CMAKE_CURRENT_SOURCE_DIR}/flow_control.cc
3 | ${CMAKE_CURRENT_SOURCE_DIR}/flow_control.h
4 | )
5 | set (vmecpp_sources "${vmecpp_sources}" PARENT_SCOPE)
6 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/flow_control/flow_control.cc:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | //
3 | //
4 | // SPDX-License-Identifier: MIT
5 | #include "vmecpp/common/flow_control/flow_control.h"
6 |
7 | #include "absl/log/check.h"
8 | #include "absl/log/log.h"
9 |
10 | #ifdef _OPENMP
11 | #include
12 | #endif // _OPENMP
13 |
14 | namespace vmecpp {
15 |
16 | RestartReason RestartReasonFromInt(int restart_reason) {
17 | switch (restart_reason) {
18 | case 1:
19 | return RestartReason::NO_RESTART;
20 | case 2:
21 | return RestartReason::BAD_JACOBIAN;
22 | case 3:
23 | return RestartReason::BAD_PROGRESS;
24 | case 4:
25 | return RestartReason::HUGE_INITIAL_FORCES;
26 | default:
27 | LOG(FATAL) << "Invalid restart_reason value: " << restart_reason;
28 | }
29 | }
30 |
31 | int get_max_threads(std::optional max_threads) {
32 | if (max_threads == std::nullopt) {
33 | return omp_get_max_threads();
34 | }
35 | CHECK_GT(max_threads.value(), 0)
36 | << "The number of threads must be >=1. "
37 | "To automatically use all available threads, pass std::nullopt";
38 | return max_threads.value();
39 | }
40 |
41 | FlowControl::FlowControl(bool lfreeb, double delt, int num_grids,
42 | std::optional max_threads)
43 | : lfreeb(lfreeb), max_threads_(get_max_threads(max_threads)) {
44 | fsq = 1.0;
45 |
46 | // INITIALIZE PARAMETERS
47 | fsqr = 1.0;
48 | fsqz = 1.0;
49 | ftolv = fsqr;
50 | ijacob = 0;
51 | restart_reason = RestartReason::NO_RESTART;
52 | res0 = -1;
53 | delt0r = delt;
54 | multi_ns_grid = num_grids;
55 | neqs_old = 0;
56 |
57 | fResInvar.resize(3, 0.0);
58 | fResPrecd.resize(3, 0.0);
59 |
60 | ns_old = 0;
61 | }
62 |
63 | int FlowControl::max_threads() const { return max_threads_; }
64 |
65 | } // namespace vmecpp
66 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/flow_control/flow_control.h:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | //
3 | //
4 | // SPDX-License-Identifier: MIT
5 | #ifndef VMECPP_COMMON_FLOW_CONTROL_FLOW_CONTROL_H_
6 | #define VMECPP_COMMON_FLOW_CONTROL_FLOW_CONTROL_H_
7 |
8 | #include
9 | #include
10 | #include
11 |
12 | namespace vmecpp {
13 |
14 | // enumerates values of `restart_reason`
15 | // was `irst` = 1, 2, 3, 4 in Fortran VMEC
16 | enum class RestartReason : std::uint8_t {
17 | // irst == 1, no restart required, instead make backup of current state vector
18 | // when calling Vmec::RestartIteration
19 | NO_RESTART,
20 |
21 | // irst == 2, bad Jacobian, flux surfaces are overlapping
22 | BAD_JACOBIAN,
23 |
24 | // irst == 3, bad progress, residuals not decaying as expected
25 | BAD_PROGRESS,
26 |
27 | // irst == 4, huge initial forces, flux surfaces are too close to each other
28 | // (but not overlapping yet)
29 | HUGE_INITIAL_FORCES
30 | };
31 |
32 | RestartReason RestartReasonFromInt(int restart_reason);
33 |
34 | class FlowControl {
35 | public:
36 | // ns4: number of iterations between update of radial preconditioner matrix
37 | static constexpr int kPreconditionerUpdateInterval = 25;
38 |
39 | FlowControl(bool lfreeb, double delt, int num_grids,
40 | std::optional max_threads = std::nullopt);
41 |
42 | int max_threads() const;
43 |
44 | const bool lfreeb;
45 |
46 | // was called `irst` in Fortran VMEC
47 | RestartReason restart_reason;
48 |
49 | // current ns in algorithm
50 | int ns;
51 |
52 | int neqs;
53 | int neqs_old;
54 |
55 | bool haveToFlipTheta;
56 |
57 | int ijacob;
58 |
59 | int multi_ns_grid;
60 |
61 | // ------ current multi-grid step settings
62 |
63 | // radial resolution of current multi-grid step
64 | int nsval;
65 |
66 | // radial grid spacing of flux surfaces: 1.0 / (ns - 1.0)
67 | double deltaS;
68 |
69 | // current force tolerance
70 | double ftolv;
71 |
72 | // current maximum number of iterations
73 | int niterv;
74 |
75 | // --------- end: current multi-grid step settings
76 |
77 | int num_surfaces_to_distribute;
78 |
79 | // for initialize_radial and interp
80 | int ns_min;
81 | int ns_old;
82 |
83 | double delt0r;
84 |
85 | double fsqr, fsqz, fsql;
86 | double fsqr1, fsqz1, fsql1;
87 | double fsq;
88 |
89 | std::vector fsqt;
90 | std::vector mhd_energy;
91 |
92 | double res0;
93 |
94 | std::vector fResInvar;
95 | std::vector fResPrecd;
96 |
97 | private:
98 | const int max_threads_;
99 | };
100 |
101 | } // namespace vmecpp
102 |
103 | #endif // VMECPP_COMMON_FLOW_CONTROL_FLOW_CONTROL_H_
104 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/fourier_basis_fast_poloidal/BUILD.bazel:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | #
3 | # SPDX-License-Identifier: MIT
4 | cc_library(
5 | name = "fourier_basis_fast_poloidal",
6 | srcs = ["fourier_basis_fast_poloidal.cc"],
7 | hdrs = ["fourier_basis_fast_poloidal.h"],
8 | visibility = ["//visibility:public"],
9 | deps = [
10 | "@abseil-cpp//absl/algorithm:container",
11 | "@abseil-cpp//absl/log:check",
12 | "@abseil-cpp//absl/strings:str_format",
13 | "//vmecpp/common/util:util",
14 | "//vmecpp/common/sizes:sizes",
15 | ],
16 | )
17 |
18 | cc_test(
19 | name = "fourier_basis_fast_poloidal_test",
20 | srcs = ["fourier_basis_fast_poloidal_test.cc"],
21 | deps = [
22 | ":fourier_basis_fast_poloidal",
23 | "@googletest//:gtest_main",
24 | "@nlohmann_json//:json",
25 | "//util/file_io:file_io",
26 | "//util/testing:numerical_comparison_lib",
27 | "//vmecpp/common/util:util",
28 | "//vmecpp/common/sizes:sizes",
29 | "@abseil-cpp//absl/algorithm:container",
30 | ],
31 | size = "small",
32 | )
33 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/fourier_basis_fast_poloidal/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | list (APPEND vmecpp_sources
2 | ${CMAKE_CURRENT_SOURCE_DIR}/fourier_basis_fast_poloidal.cc
3 | ${CMAKE_CURRENT_SOURCE_DIR}/fourier_basis_fast_poloidal.h
4 | )
5 | set (vmecpp_sources "${vmecpp_sources}" PARENT_SCOPE)
6 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/fourier_basis_fast_poloidal/fourier_basis_fast_poloidal.h:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | //
3 | //
4 | // SPDX-License-Identifier: MIT
5 | #ifndef VMECPP_COMMON_FOURIER_BASIS_FAST_POLOIDAL_FOURIER_BASIS_FAST_POLOIDAL_H_
6 | #define VMECPP_COMMON_FOURIER_BASIS_FAST_POLOIDAL_FOURIER_BASIS_FAST_POLOIDAL_H_
7 |
8 | #include
9 | #include
10 |
11 | #include "vmecpp/common/sizes/sizes.h"
12 |
13 | namespace vmecpp {
14 |
15 | // An implementation of FourierBasis that stores its data so that
16 | // the poloidal coordinate is the fast loop index.
17 | // NOTE: Nestor has its own implementation of this class because we want to be
18 | // able to use different data layouts between VMEC++ and Nestor.
19 | class FourierBasisFastPoloidal {
20 | public:
21 | explicit FourierBasisFastPoloidal(const Sizes* s);
22 |
23 | std::vector mscale;
24 | std::vector nscale;
25 |
26 | std::vector cosmu;
27 | std::vector sinmu;
28 | std::vector cosmum;
29 | std::vector sinmum;
30 |
31 | std::vector cosmui;
32 | std::vector sinmui;
33 | std::vector cosmumi;
34 | std::vector sinmumi;
35 |
36 | std::vector cosnv;
37 | std::vector sinnv;
38 | std::vector cosnvn;
39 | std::vector sinnvn;
40 |
41 | // ---------------
42 |
43 | int cos_to_cc_ss(const std::span fcCos,
44 | std::span m_fcCC, std::span m_fcSS,
45 | int n_size, int m_size) const;
46 | int sin_to_sc_cs(const std::span fcSin,
47 | std::span m_fcSC, std::span m_fcCS,
48 | int n_size, int m_size) const;
49 |
50 | int cc_ss_to_cos(const std::span fcCC,
51 | const std::span fcSS,
52 | std::span m_fcCos, int n_size, int m_size) const;
53 | int sc_cs_to_sin(const std::span fcSC,
54 | const std::span fcCS,
55 | std::span m_fcSin, int n_size, int m_size) const;
56 |
57 | int mnIdx(int m, int n) const;
58 | int mnMax(int m_size, int n_size) const;
59 | void computeConversionIndices(std::vector& m_xm, std::vector& m_xn,
60 | int n_size, int m_size, int nfp) const;
61 |
62 | std::vector xm;
63 | std::vector xn;
64 |
65 | std::vector xm_nyq;
66 | std::vector xn_nyq;
67 |
68 | private:
69 | const Sizes& s_;
70 |
71 | void computeFourierBasisFastPoloidal(int nfp);
72 | };
73 |
74 | } // namespace vmecpp
75 |
76 | #endif // VMECPP_COMMON_FOURIER_BASIS_FAST_POLOIDAL_FOURIER_BASIS_FAST_POLOIDAL_H_
77 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/fourier_basis_fast_toroidal/BUILD.bazel:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | #
3 | # SPDX-License-Identifier: MIT
4 | cc_library(
5 | name = "fourier_basis_fast_toroidal",
6 | srcs = ["fourier_basis_fast_toroidal.cc"],
7 | hdrs = ["fourier_basis_fast_toroidal.h"],
8 | visibility = ["//visibility:public"],
9 | deps = [
10 | "@abseil-cpp//absl/algorithm:container",
11 | "@abseil-cpp//absl/log:check",
12 | "@abseil-cpp//absl/strings:str_format",
13 | "//vmecpp/common/util:util",
14 | "//vmecpp/common/sizes:sizes",
15 | ],
16 | )
17 |
18 | cc_test(
19 | name = "fourier_basis_fast_toroidal_test",
20 | srcs = ["fourier_basis_fast_toroidal_test.cc"],
21 | deps = [
22 | ":fourier_basis_fast_toroidal",
23 | "@googletest//:gtest_main",
24 | "@nlohmann_json//:json",
25 | "//util/file_io:file_io",
26 | "//util/testing:numerical_comparison_lib",
27 | "//vmecpp/common/util:util",
28 | "//vmecpp/common/sizes:sizes",
29 | "@abseil-cpp//absl/algorithm:container",
30 | ],
31 | size = "small",
32 | )
33 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/fourier_basis_fast_toroidal/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | list (APPEND vmecpp_sources
2 | ${CMAKE_CURRENT_SOURCE_DIR}/fourier_basis_fast_toroidal.cc
3 | ${CMAKE_CURRENT_SOURCE_DIR}/fourier_basis_fast_toroidal.h
4 | )
5 | set (vmecpp_sources "${vmecpp_sources}" PARENT_SCOPE)
6 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/fourier_basis_fast_toroidal/fourier_basis_fast_toroidal.h:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | //
3 | //
4 | // SPDX-License-Identifier: MIT
5 | #ifndef VMECPP_COMMON_FOURIER_BASIS_FAST_TOROIDAL_FOURIER_BASIS_FAST_TOROIDAL_H_
6 | #define VMECPP_COMMON_FOURIER_BASIS_FAST_TOROIDAL_FOURIER_BASIS_FAST_TOROIDAL_H_
7 |
8 | #include
9 |
10 | #include "vmecpp/common/sizes/sizes.h"
11 |
12 | namespace vmecpp {
13 |
14 | // An implementation of FourierBasis that stores its data so that the toroidal
15 | // coordinate is the fast loop index.
16 | // NOTE: Nestor has its own implementation of this class because we want to be
17 | // able to use different data layouts between VMEC++ and Nestor.
18 | // TODO(eguiraud) reduce overall code duplication
19 | class FourierBasisFastToroidal {
20 | public:
21 | explicit FourierBasisFastToroidal(const Sizes* s);
22 |
23 | std::vector mscale;
24 | std::vector nscale;
25 |
26 | std::vector cosmu;
27 | std::vector sinmu;
28 | std::vector cosmum;
29 | std::vector sinmum;
30 |
31 | std::vector cosmui;
32 | std::vector sinmui;
33 | std::vector cosmumi;
34 | std::vector sinmumi;
35 |
36 | std::vector cosnv;
37 | std::vector sinnv;
38 | std::vector cosnvn;
39 | std::vector sinnvn;
40 |
41 | // ---------------
42 |
43 | int cos_to_cc_ss(const std::vector& fcCos,
44 | std::vector& m_fcCC, std::vector& m_fcSS,
45 | int n_size, int m_size) const;
46 | int sin_to_sc_cs(const std::vector& fcSin,
47 | std::vector& m_fcSC, std::vector& m_fcCS,
48 | int n_size, int m_size) const;
49 |
50 | int cc_ss_to_cos(const std::vector& fcCC,
51 | const std::vector& fcSS,
52 | std::vector& m_fcCos, int n_size, int m_size) const;
53 | int sc_cs_to_sin(const std::vector& fcSC,
54 | const std::vector& fcCS,
55 | std::vector& m_fcSin, int n_size, int m_size) const;
56 |
57 | int mnIdx(int m, int n) const;
58 | int mnMax(int m_size, int n_size) const;
59 | void computeConversionIndices(std::vector& m_xm, std::vector& m_xn,
60 | int n_size, int m_size, int nfp) const;
61 |
62 | std::vector xm;
63 | std::vector xn;
64 |
65 | std::vector xm_nyq;
66 | std::vector xn_nyq;
67 |
68 | private:
69 | const Sizes& s_;
70 |
71 | void computeFourierBasisFastToroidal(int nfp);
72 | };
73 |
74 | } // namespace vmecpp
75 |
76 | #endif // VMECPP_COMMON_FOURIER_BASIS_FAST_TOROIDAL_FOURIER_BASIS_FAST_TOROIDAL_H_
77 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/magnetic_configuration_definition/BUILD.bazel:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | #
3 | # SPDX-License-Identifier: MIT
4 | cc_library(
5 | name="magnetic_configuration",
6 | visibility = ["//visibility:public"],
7 | hdrs=["magnetic_configuration.h"],
8 | deps = [
9 | "//vmecpp/common/composed_types_definition:composed_types",
10 | ],
11 | )
12 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/magnetic_configuration_definition/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | list (APPEND vmecpp_sources
2 | ${CMAKE_CURRENT_SOURCE_DIR}/magnetic_configuration.h
3 | )
4 | set (vmecpp_sources "${vmecpp_sources}" PARENT_SCOPE)
5 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/magnetic_configuration_lib/BUILD.bazel:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | #
3 | # SPDX-License-Identifier: MIT
4 | cc_library(
5 | name = "magnetic_configuration_lib",
6 | visibility = ["//visibility:public"],
7 | srcs = ["magnetic_configuration_lib.cc"],
8 | hdrs = ["magnetic_configuration_lib.h"],
9 | deps = [
10 | "//vmecpp/common/magnetic_configuration_definition:magnetic_configuration",
11 | "//vmecpp/common/composed_types_lib:composed_types_lib",
12 | "//util/file_io",
13 | "@abseil-cpp//absl/algorithm:container",
14 | "@abseil-cpp//absl/log",
15 | "@abseil-cpp//absl/log:check",
16 | "@abseil-cpp//absl/strings:strings",
17 | "@abseil-cpp//absl/status:status",
18 | "@abseil-cpp//absl/status:statusor",
19 | ],
20 | )
21 |
22 | cc_test(
23 | name = "magnetic_configuration_lib_test",
24 | srcs = ["magnetic_configuration_lib_test.cc"],
25 | deps = [
26 | ":magnetic_configuration_lib",
27 | "@googletest//:gtest_main",
28 | "//util/testing:numerical_comparison_lib",
29 | ],
30 | )
31 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/magnetic_configuration_lib/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | list (APPEND vmecpp_sources
2 | ${CMAKE_CURRENT_SOURCE_DIR}/magnetic_configuration_lib.cc
3 | ${CMAKE_CURRENT_SOURCE_DIR}/magnetic_configuration_lib.h
4 | )
5 | set (vmecpp_sources "${vmecpp_sources}" PARENT_SCOPE)
6 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/magnetic_field_provider/BUILD.bazel:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | #
3 | # SPDX-License-Identifier: MIT
4 | cc_library(
5 | name = "magnetic_field_provider_lib",
6 | visibility = ["//visibility:public"],
7 | srcs = ["magnetic_field_provider_lib.cc"],
8 | hdrs = ["magnetic_field_provider_lib.h"],
9 | deps = [
10 | "//vmecpp/common/composed_types_definition:composed_types",
11 | "//vmecpp/common/composed_types_lib:composed_types_lib",
12 | "//vmecpp/common/magnetic_configuration_definition:magnetic_configuration",
13 | "//vmecpp/common/magnetic_configuration_lib:magnetic_configuration_lib",
14 | "@abscab_cpp//abscab:abscab",
15 | "@abseil-cpp//absl/status",
16 | "@abseil-cpp//absl/status:statusor",
17 | "@abseil-cpp//absl/log:log",
18 | "@abseil-cpp//absl/log:check",
19 | ],
20 | )
21 |
22 | cc_test(
23 | name = "magnetic_field_provider_lib_test",
24 | srcs = ["magnetic_field_provider_lib_test.cc"],
25 | data = [
26 | # we don't copy these files in the repo
27 | # "//vmec_validation/coils",
28 | ],
29 | deps = [
30 | ":magnetic_field_provider_lib",
31 | "@googletest//:gtest_main",
32 | "//util/testing:numerical_comparison_lib",
33 | ],
34 | )
35 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/magnetic_field_provider/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | list (APPEND vmecpp_sources
2 | ${CMAKE_CURRENT_SOURCE_DIR}/magnetic_field_provider_lib.cc
3 | ${CMAKE_CURRENT_SOURCE_DIR}/magnetic_field_provider_lib.h
4 | )
5 | set (vmecpp_sources "${vmecpp_sources}" PARENT_SCOPE)
6 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/makegrid_lib/BUILD.bazel:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | #
3 | # SPDX-License-Identifier: MIT
4 | cc_library(
5 | name = "makegrid_lib",
6 | srcs = ["makegrid_lib.cc"],
7 | hdrs = ["makegrid_lib.h"],
8 | visibility = ["//visibility:public"],
9 | deps = [
10 | "@abseil-cpp//absl/strings:str_format",
11 | "@nlohmann_json//:json",
12 | "//third_party/netcdf4",
13 | "//util/json_io",
14 | "//util/file_io",
15 | "//vmecpp/common/composed_types_lib",
16 | "//vmecpp/common/magnetic_configuration_lib",
17 | "//vmecpp/common/magnetic_field_provider:magnetic_field_provider_lib",
18 | "//vmecpp/common/util:util",
19 | ],
20 | )
21 |
22 | cc_test(
23 | name = "makegrid_lib_test",
24 | srcs = ["makegrid_lib_test.cc"],
25 | data = [
26 | "//vmecpp/common/makegrid_lib/test_data:mgrid_non_symmetric",
27 | "//vmecpp/common/makegrid_lib/test_data:mgrid_symmetric_even",
28 | "//vmecpp/common/makegrid_lib/test_data:mgrid_symmetric_odd",
29 | ],
30 | deps = [
31 | ":makegrid_lib",
32 | "@googletest//:gtest_main",
33 | "//util/file_io",
34 | "//util/netcdf_io",
35 | "//util/testing:numerical_comparison_lib",
36 | ],
37 | )
38 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/makegrid_lib/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | list (APPEND vmecpp_sources
2 | ${CMAKE_CURRENT_SOURCE_DIR}/makegrid_lib.cc
3 | ${CMAKE_CURRENT_SOURCE_DIR}/makegrid_lib.h
4 | )
5 | set (vmecpp_sources "${vmecpp_sources}" PARENT_SCOPE)
6 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/makegrid_lib/create_reference_mgrid.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # This needs the official Fortran MAKEGRID executable `mgrid` on $PATH.
4 | # The actual code is hosted here: https://github.com/ORNL-Fusion/MAKEGRID
5 | # but keep in mind that you need to go throught the `Stellarator-Tools` repo to get the build system:
6 | # https://github.com/ORNL-Fusion/Stellarator-Tools
7 |
8 | # delete previous outputs
9 | rm -fi extcur.test_* mgrid_test_*
10 |
11 | OMP_NUM_THREADS=1 mgrid coils.test_non_symmetric
12 | OMP_NUM_THREADS=1 mgrid coils.test_symmetric_even
13 | OMP_NUM_THREADS=1 mgrid coils.test_symmetric_odd
14 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/makegrid_lib/test_data/BUILD.bazel:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | #
3 | # SPDX-License-Identifier: MIT
4 | filegroup(
5 | name = "mgrid_non_symmetric",
6 | visibility = ["//visibility:public"],
7 | srcs = [
8 | "coils.test_non_symmetric",
9 | "extcur.test_non_symmetric",
10 | "mgrid_test_non_symmetric.nc",
11 | ],
12 | )
13 |
14 | filegroup(
15 | name = "mgrid_symmetric_even",
16 | visibility = ["//visibility:public"],
17 | srcs = [
18 | "coils.test_symmetric_even",
19 | "extcur.test_symmetric_even",
20 | "mgrid_test_symmetric_even.nc",
21 | ],
22 | )
23 |
24 | filegroup(
25 | name = "mgrid_symmetric_odd",
26 | visibility = ["//visibility:public"],
27 | srcs = [
28 | "coils.test_symmetric_odd",
29 | "extcur.test_symmetric_odd",
30 | "mgrid_test_symmetric_odd.nc",
31 | ],
32 | )
33 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/makegrid_lib/test_data/coils.test_non_symmetric:
--------------------------------------------------------------------------------
1 | ! Simplified Coil model based on CTH.
2 |
3 | &MGRID_NLI
4 | MGRID_EXT = 'test_non_symmetric'
5 | MGRID_MODE = 'R'
6 | LSTELL_SYM = .FALSE.
7 | RMIN = 1.0
8 | RMAX = 2.0
9 | ZMIN = -0.6
10 | ZMAX = 0.6
11 | IR = 11
12 | JZ = 13
13 | KP = 18
14 | /
15 |
16 | ** coils_dot_starts_below **
17 | periods 5
18 | begin filament
19 | mirror NUL
20 | 1.13366 0. 0. 96
21 | 1.05079 0.1379 0.225085 96
22 | 0.827436 0.263483 0.364319 96
23 | 0.554917 0.301189 0.365058 96
24 | 0.312606 0.311488 0.224283 96
25 | 0.114531 0.35249 5.0307E-17 96
26 | -0.0698152 0.435744 -0.224283 96
27 | -0.271902 0.569839 -0.365058 96
28 | -0.514538 0.699517 -0.364319 96
29 | -0.769054 0.729204 -0.225085 96
30 | -0.917147 0.666346 -9.54098E-17 96
31 | -0.931165 0.506077 0.225085 96
32 | -0.824281 0.273192 0.364319 96
33 | -0.625972 0.0825046 0.365058 96
34 | -0.435991 -0.0682541 0.224283 96
35 | -0.299846 -0.217851 1.50054E-16 96
36 | -0.199642 -0.393561 -0.224283 96
37 | -0.114969 -0.62083 -0.365058 96
38 | 0.00510449 -0.868359 -0.364319 96
39 | 0.193562 -1.04198 -0.225085 96
40 | 0.350319 -1.07817 -1.89952E-16 96
41 | 0.455864 -0.95675 0.225085 96
42 | 0.506279 -0.705518 0.364319 96
43 | 0.457927 -0.434685 0.365058 96
44 | 0.392843 -0.20105 0.224283 96
45 | 0.37063 -4.94396E-17 2.498E-16 96
46 | 0.392843 0.20105 -0.224283 96
47 | 0.457927 0.434685 -0.365058 96
48 | 0.506279 0.705518 -0.364319 96
49 | 0.455864 0.95675 -0.225085 96
50 | 0.350319 1.07817 -2.7929E-16 96
51 | 0.193562 1.04198 0.225085 96
52 | 0.00510449 0.868359 0.364319 96
53 | -0.114969 0.62083 0.365058 96
54 | -0.199642 0.393561 0.224283 96
55 | -0.299846 0.217851 3.50414E-16 96
56 | -0.435991 0.0682541 -0.224283 96
57 | -0.625972 -0.0825046 -0.365058 96
58 | -0.824281 -0.273192 -0.364319 96
59 | -0.931165 -0.506077 -0.225085 96
60 | -0.917147 -0.666346 -3.75568E-16 96
61 | -0.769054 -0.729204 0.225085 96
62 | -0.514538 -0.699517 0.364319 96
63 | -0.271902 -0.569839 0.365058 96
64 | -0.0698152 -0.435744 0.224283 96
65 | 0.114531 -0.35249 3.99854E-16 96
66 | 0.312606 -0.311488 -0.224283 96
67 | 0.554917 -0.301189 -0.365058 96
68 | 0.827436 -0.263483 -0.364319 96
69 | 1.05079 -0.1379 -0.225085 96
70 | 1.13366 0. 0. 0 1 HF-OVF
71 | 1.266 0.0 -0.523 -16 1 HF-OVF
72 | 1.266 0.0 0.523 -16 1 HF-OVF
73 | 1.268 0.0 0.573 -36 2 TVF
74 | 1.268 0.0 -0.573 -36 2 TVF
75 | end
76 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/makegrid_lib/test_data/coils.test_symmetric_even:
--------------------------------------------------------------------------------
1 | ! Simplified Coil model based on CTH.
2 |
3 | &MGRID_NLI
4 | MGRID_EXT = 'test_symmetric_even'
5 | MGRID_MODE = 'R'
6 | LSTELL_SYM = .TRUE.
7 | RMIN = 1.0
8 | RMAX = 2.0
9 | ZMIN = -0.6
10 | ZMAX = 0.6
11 | IR = 11
12 | JZ = 13
13 | KP = 18
14 | /
15 |
16 | ** coils_dot_starts_below **
17 | periods 5
18 | begin filament
19 | mirror NUL
20 | 1.13366 0. 0. 96
21 | 1.05079 0.1379 0.225085 96
22 | 0.827436 0.263483 0.364319 96
23 | 0.554917 0.301189 0.365058 96
24 | 0.312606 0.311488 0.224283 96
25 | 0.114531 0.35249 5.0307E-17 96
26 | -0.0698152 0.435744 -0.224283 96
27 | -0.271902 0.569839 -0.365058 96
28 | -0.514538 0.699517 -0.364319 96
29 | -0.769054 0.729204 -0.225085 96
30 | -0.917147 0.666346 -9.54098E-17 96
31 | -0.931165 0.506077 0.225085 96
32 | -0.824281 0.273192 0.364319 96
33 | -0.625972 0.0825046 0.365058 96
34 | -0.435991 -0.0682541 0.224283 96
35 | -0.299846 -0.217851 1.50054E-16 96
36 | -0.199642 -0.393561 -0.224283 96
37 | -0.114969 -0.62083 -0.365058 96
38 | 0.00510449 -0.868359 -0.364319 96
39 | 0.193562 -1.04198 -0.225085 96
40 | 0.350319 -1.07817 -1.89952E-16 96
41 | 0.455864 -0.95675 0.225085 96
42 | 0.506279 -0.705518 0.364319 96
43 | 0.457927 -0.434685 0.365058 96
44 | 0.392843 -0.20105 0.224283 96
45 | 0.37063 -4.94396E-17 2.498E-16 96
46 | 0.392843 0.20105 -0.224283 96
47 | 0.457927 0.434685 -0.365058 96
48 | 0.506279 0.705518 -0.364319 96
49 | 0.455864 0.95675 -0.225085 96
50 | 0.350319 1.07817 -2.7929E-16 96
51 | 0.193562 1.04198 0.225085 96
52 | 0.00510449 0.868359 0.364319 96
53 | -0.114969 0.62083 0.365058 96
54 | -0.199642 0.393561 0.224283 96
55 | -0.299846 0.217851 3.50414E-16 96
56 | -0.435991 0.0682541 -0.224283 96
57 | -0.625972 -0.0825046 -0.365058 96
58 | -0.824281 -0.273192 -0.364319 96
59 | -0.931165 -0.506077 -0.225085 96
60 | -0.917147 -0.666346 -3.75568E-16 96
61 | -0.769054 -0.729204 0.225085 96
62 | -0.514538 -0.699517 0.364319 96
63 | -0.271902 -0.569839 0.365058 96
64 | -0.0698152 -0.435744 0.224283 96
65 | 0.114531 -0.35249 3.99854E-16 96
66 | 0.312606 -0.311488 -0.224283 96
67 | 0.554917 -0.301189 -0.365058 96
68 | 0.827436 -0.263483 -0.364319 96
69 | 1.05079 -0.1379 -0.225085 96
70 | 1.13366 0. 0. 0 1 HF-OVF
71 | 1.266 0.0 -0.523 -16 1 HF-OVF
72 | 1.266 0.0 0.523 -16 1 HF-OVF
73 | 1.268 0.0 0.573 -36 2 TVF
74 | 1.268 0.0 -0.573 -36 2 TVF
75 | end
76 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/makegrid_lib/test_data/coils.test_symmetric_odd:
--------------------------------------------------------------------------------
1 | ! Simplified Coil model based on CTH.
2 |
3 | &MGRID_NLI
4 | MGRID_EXT = 'test_symmetric_odd'
5 | MGRID_MODE = 'R'
6 | LSTELL_SYM = .TRUE.
7 | RMIN = 1.0
8 | RMAX = 2.0
9 | ZMIN = -0.6
10 | ZMAX = 0.6
11 | IR = 11
12 | JZ = 13
13 | KP = 19
14 | /
15 |
16 | ** coils_dot_starts_below **
17 | periods 5
18 | begin filament
19 | mirror NUL
20 | 1.13366 0. 0. 96
21 | 1.05079 0.1379 0.225085 96
22 | 0.827436 0.263483 0.364319 96
23 | 0.554917 0.301189 0.365058 96
24 | 0.312606 0.311488 0.224283 96
25 | 0.114531 0.35249 5.0307E-17 96
26 | -0.0698152 0.435744 -0.224283 96
27 | -0.271902 0.569839 -0.365058 96
28 | -0.514538 0.699517 -0.364319 96
29 | -0.769054 0.729204 -0.225085 96
30 | -0.917147 0.666346 -9.54098E-17 96
31 | -0.931165 0.506077 0.225085 96
32 | -0.824281 0.273192 0.364319 96
33 | -0.625972 0.0825046 0.365058 96
34 | -0.435991 -0.0682541 0.224283 96
35 | -0.299846 -0.217851 1.50054E-16 96
36 | -0.199642 -0.393561 -0.224283 96
37 | -0.114969 -0.62083 -0.365058 96
38 | 0.00510449 -0.868359 -0.364319 96
39 | 0.193562 -1.04198 -0.225085 96
40 | 0.350319 -1.07817 -1.89952E-16 96
41 | 0.455864 -0.95675 0.225085 96
42 | 0.506279 -0.705518 0.364319 96
43 | 0.457927 -0.434685 0.365058 96
44 | 0.392843 -0.20105 0.224283 96
45 | 0.37063 -4.94396E-17 2.498E-16 96
46 | 0.392843 0.20105 -0.224283 96
47 | 0.457927 0.434685 -0.365058 96
48 | 0.506279 0.705518 -0.364319 96
49 | 0.455864 0.95675 -0.225085 96
50 | 0.350319 1.07817 -2.7929E-16 96
51 | 0.193562 1.04198 0.225085 96
52 | 0.00510449 0.868359 0.364319 96
53 | -0.114969 0.62083 0.365058 96
54 | -0.199642 0.393561 0.224283 96
55 | -0.299846 0.217851 3.50414E-16 96
56 | -0.435991 0.0682541 -0.224283 96
57 | -0.625972 -0.0825046 -0.365058 96
58 | -0.824281 -0.273192 -0.364319 96
59 | -0.931165 -0.506077 -0.225085 96
60 | -0.917147 -0.666346 -3.75568E-16 96
61 | -0.769054 -0.729204 0.225085 96
62 | -0.514538 -0.699517 0.364319 96
63 | -0.271902 -0.569839 0.365058 96
64 | -0.0698152 -0.435744 0.224283 96
65 | 0.114531 -0.35249 3.99854E-16 96
66 | 0.312606 -0.311488 -0.224283 96
67 | 0.554917 -0.301189 -0.365058 96
68 | 0.827436 -0.263483 -0.364319 96
69 | 1.05079 -0.1379 -0.225085 96
70 | 1.13366 0. 0. 0 1 HF-OVF
71 | 1.266 0.0 -0.523 -16 1 HF-OVF
72 | 1.266 0.0 0.523 -16 1 HF-OVF
73 | 1.268 0.0 0.573 -36 2 TVF
74 | 1.268 0.0 -0.573 -36 2 TVF
75 | end
76 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/makegrid_lib/test_data/extcur.test_non_symmetric:
--------------------------------------------------------------------------------
1 | EXTCUR(1) = 9.60000000000000E+01/ 9.60000000000000E+01
2 | EXTCUR(2) = -3.60000000000000E+01/ -3.60000000000000E+01
3 |
4 | THE BFIELDS HAVE BEEN STORED IN THE MGRID FILE IN RAW
5 | MODE. THE USER MUST PROVIDE THE EXTCUR ARRAY VALUES FOR THE VMEC INPUT (INDATA) FILE.
6 | CHOOSING EXTCUR=1 CORRESPONDS TO THE CURRENTS PRESENT IN THE COILS FILE.
7 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/makegrid_lib/test_data/extcur.test_symmetric_even:
--------------------------------------------------------------------------------
1 | EXTCUR(1) = 9.60000000000000E+01/ 9.60000000000000E+01
2 | EXTCUR(2) = -3.60000000000000E+01/ -3.60000000000000E+01
3 |
4 | THE BFIELDS HAVE BEEN STORED IN THE MGRID FILE IN RAW
5 | MODE. THE USER MUST PROVIDE THE EXTCUR ARRAY VALUES FOR THE VMEC INPUT (INDATA) FILE.
6 | CHOOSING EXTCUR=1 CORRESPONDS TO THE CURRENTS PRESENT IN THE COILS FILE.
7 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/makegrid_lib/test_data/extcur.test_symmetric_odd:
--------------------------------------------------------------------------------
1 | EXTCUR(1) = 9.60000000000000E+01/ 9.60000000000000E+01
2 | EXTCUR(2) = -3.60000000000000E+01/ -3.60000000000000E+01
3 |
4 | THE BFIELDS HAVE BEEN STORED IN THE MGRID FILE IN RAW
5 | MODE. THE USER MUST PROVIDE THE EXTCUR ARRAY VALUES FOR THE VMEC INPUT (INDATA) FILE.
6 | CHOOSING EXTCUR=1 CORRESPONDS TO THE CURRENTS PRESENT IN THE COILS FILE.
7 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/makegrid_lib/test_data/mgrid_test_non_symmetric.nc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/proximafusion/vmecpp/ef96e726f1cc9bda8bb2a97ab32add94891adb36/src/vmecpp/cpp/vmecpp/common/makegrid_lib/test_data/mgrid_test_non_symmetric.nc
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/makegrid_lib/test_data/mgrid_test_symmetric_even.nc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/proximafusion/vmecpp/ef96e726f1cc9bda8bb2a97ab32add94891adb36/src/vmecpp/cpp/vmecpp/common/makegrid_lib/test_data/mgrid_test_symmetric_even.nc
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/makegrid_lib/test_data/mgrid_test_symmetric_odd.nc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/proximafusion/vmecpp/ef96e726f1cc9bda8bb2a97ab32add94891adb36/src/vmecpp/cpp/vmecpp/common/makegrid_lib/test_data/mgrid_test_symmetric_odd.nc
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/sizes/BUILD.bazel:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | #
3 | # SPDX-License-Identifier: MIT
4 | cc_library(
5 | name = "sizes",
6 | srcs = ["sizes.cc"],
7 | hdrs = ["sizes.h"],
8 | visibility = ["//visibility:public"],
9 | deps = [
10 | "@abseil-cpp//absl/log:check",
11 | "@abseil-cpp//absl/strings:str_format",
12 | "//vmecpp/common/util:util",
13 | "//vmecpp/common/vmec_indata:vmec_indata",
14 | ],
15 | )
16 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/sizes/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | list (APPEND vmecpp_sources
2 | ${CMAKE_CURRENT_SOURCE_DIR}/sizes.cc
3 | ${CMAKE_CURRENT_SOURCE_DIR}/sizes.h
4 | )
5 | set (vmecpp_sources "${vmecpp_sources}" PARENT_SCOPE)
6 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/sizes/sizes.h:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | //
3 | //
4 | // SPDX-License-Identifier: MIT
5 | #ifndef VMECPP_COMMON_SIZES_SIZES_H_
6 | #define VMECPP_COMMON_SIZES_SIZES_H_
7 |
8 | #include
9 |
10 | #include "vmecpp/common/util/util.h"
11 | #include "vmecpp/common/vmec_indata/vmec_indata.h"
12 |
13 | namespace vmecpp {
14 |
15 | class Sizes {
16 | public:
17 | // The array sizes etc. in VMEC are derived from a few key parameters
18 | // specified in the input file. These parameters are set here from the INDATA
19 | // object.
20 | explicit Sizes(const VmecINDATA& id);
21 |
22 | Sizes(bool lasym, int nfp, int mpol, int ntor, int ntheta, int nzeta);
23 |
24 | // inputs from INDATA
25 |
26 | // flag to indicate non-symmetric case
27 | bool lasym;
28 |
29 | // number of toroidal field periods
30 | int nfp;
31 |
32 | // number of poloidal Fourier harmoncis
33 | int mpol;
34 |
35 | // number of toroidal Fourier harmoncis
36 | int ntor;
37 |
38 | // number of poloidal grid points
39 | int ntheta;
40 |
41 | // number of toroidal grid points
42 | int nZeta;
43 |
44 | // derived
45 |
46 | // flag to indicate 3D case
47 | bool lthreed;
48 |
49 | // number of Fourier basis components
50 | int num_basis;
51 |
52 | // number of poloidal grid points
53 | int nThetaEven;
54 | int nThetaReduced;
55 | int nThetaEff;
56 |
57 | // number of grid points on the surface
58 | int nZnT;
59 |
60 | // [nThetaEff] poloidal integration weights
61 | std::vector wInt;
62 |
63 | // number of Fourier coefficients in one of the 2D arrays (cc, ss, ...)
64 | int mnsize;
65 |
66 | // number of Fourier coefficients in linearly-indexed array (xm, xn)
67 | int mnmax;
68 |
69 | // --------- Nyquist sizes
70 |
71 | // max poloidal mode number to hold full information on realspace grid
72 | // (nThetaEven) for computing quantities in FourierBasisFastPoloidal
73 | int mnyq2;
74 |
75 | // max toroidal mode number to hold full information on realspace grid (nZeta)
76 | // for computing quantities in FourierBasisFastPoloidal
77 | int nnyq2;
78 |
79 | // max poloidal mode number to hold full information on realspace grid
80 | // (nThetaEven)
81 | int mnyq;
82 |
83 | // max toroidal mode number to hold full information on realspace grid (nZeta)
84 | int nnyq;
85 |
86 | // number of Fourier coefficients in linearly-indexed array (xm_nyq, xn_nyq)
87 | int mnmax_nyq;
88 |
89 | private:
90 | void computeDerivedSizes();
91 | };
92 |
93 | } // namespace vmecpp
94 |
95 | #endif // VMECPP_COMMON_SIZES_SIZES_H_
96 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/util/BUILD.bazel:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | #
3 | # SPDX-License-Identifier: MIT
4 | cc_library(
5 | name = "util",
6 | srcs = ["util.cc"],
7 | hdrs = ["util.h"],
8 | visibility = ["//visibility:public"],
9 | deps = [
10 | "@abseil-cpp//absl/log:check",
11 | "@abseil-cpp//absl/log:log",
12 | "@abseil-cpp//absl/strings",
13 | "@abseil-cpp//absl/strings:str_format",
14 | "@eigen",
15 | "@nlohmann_json//:json",
16 | "//vmecpp/vmec/vmec_constants:vmec_constants",
17 | ],
18 | )
19 |
20 | cc_test(
21 | name = "util_test",
22 | srcs = ["util_test.cc"],
23 | deps = [
24 | ":util",
25 | "@googletest//:gtest_main",
26 | "//util/testing:numerical_comparison_lib"
27 | ],
28 | size = "small",
29 | )
30 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/util/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | list (APPEND vmecpp_sources
2 | ${CMAKE_CURRENT_SOURCE_DIR}/util.cc
3 | ${CMAKE_CURRENT_SOURCE_DIR}/util.h
4 | )
5 | set (vmecpp_sources "${vmecpp_sources}" PARENT_SCOPE)
6 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/vmec_indata/BUILD.bazel:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | #
3 | # SPDX-License-Identifier: MIT
4 | cc_library(
5 | name = "boundary_from_json",
6 | srcs = ["boundary_from_json.cc"],
7 | hdrs = ["boundary_from_json.h"],
8 | visibility = ["//visibility:public"],
9 | deps = [
10 | "//util/json_io:json_io",
11 | "@abseil-cpp//absl/log",
12 | "@abseil-cpp//absl/log:check",
13 | "@abseil-cpp//absl/strings:str_format",
14 | "@abseil-cpp//absl/status:statusor",
15 | ],
16 | )
17 |
18 | cc_library(
19 | name = "vmec_indata",
20 | srcs = ["vmec_indata.cc"],
21 | hdrs = ["vmec_indata.h"],
22 | visibility = ["//visibility:public"],
23 | deps = [
24 | ":boundary_from_json",
25 | "//vmecpp/common/util:util",
26 | "//util/hdf5_io",
27 | "@abseil-cpp//absl/algorithm:container",
28 | "@abseil-cpp//absl/log:check",
29 | "@abseil-cpp//absl/log:log",
30 | "@abseil-cpp//absl/status:status",
31 | "@abseil-cpp//absl/status:statusor",
32 | "@abseil-cpp//absl/strings:strings",
33 |
34 | ],
35 | )
36 |
37 | cc_test(
38 | name = "vmec_indata_test",
39 | srcs = ["vmec_indata_test.cc"],
40 | data = [
41 | "//vmecpp/test_data:solovev",
42 | "//vmecpp/test_data:solovev_analytical",
43 | "//vmecpp/test_data:solovev_no_axis",
44 | "//vmecpp/test_data:cth_like_fixed_bdy",
45 | "//vmecpp/test_data:cth_like_fixed_bdy_nzeta_37",
46 | "//vmecpp/test_data:cma",
47 | "//vmecpp/test_data:cth_like_free_bdy",
48 | ],
49 | deps = [
50 | ":vmec_indata",
51 | ":boundary_from_json",
52 | "@googletest//:gtest_main",
53 | "@bazel_tools//tools/cpp/runfiles",
54 | "//util/file_io:file_io",
55 | "//vmecpp/common/composed_types_lib:composed_types_lib",
56 | ],
57 | size = "small",
58 | )
59 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/vmec_indata/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | list (APPEND vmecpp_sources
2 | ${CMAKE_CURRENT_SOURCE_DIR}/boundary_from_json.cc
3 | ${CMAKE_CURRENT_SOURCE_DIR}/boundary_from_json.h
4 | ${CMAKE_CURRENT_SOURCE_DIR}/vmec_indata.cc
5 | ${CMAKE_CURRENT_SOURCE_DIR}/vmec_indata.h
6 | )
7 | set (vmecpp_sources "${vmecpp_sources}" PARENT_SCOPE)
8 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/common/vmec_indata/boundary_from_json.h:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | //
3 | //
4 | // SPDX-License-Identifier: MIT
5 | #ifndef VMECPP_COMMON_VMEC_INDATA_BOUNDARY_FROM_JSON_H_
6 | #define VMECPP_COMMON_VMEC_INDATA_BOUNDARY_FROM_JSON_H_
7 |
8 | #include
9 | #include
10 | #include
11 |
12 | #include "absl/status/statusor.h"
13 | #include "util/json_io/json_io.h"
14 |
15 | namespace vmecpp {
16 |
17 | // A single Fourier coefficient describing, e.g., the plasma boundary in a VMEC
18 | // input file. This is expected in JSON as {"n": 4, "m": 5, "value": 1.23}.
19 | struct BoundaryCoefficient {
20 | // poloidal mode number
21 | int m;
22 |
23 | // toroidal mode number
24 | int n;
25 |
26 | // Fourier coefficient of boundary
27 | double value;
28 |
29 | // Try to read a VMEC boundary description from the given JSON data.
30 | // If the variable is not present in the given JSON object, status will be ok
31 | // and optional will be not populated. If the variable was found, but was not
32 | // the correct type, the status will be not ok.
33 | static absl::StatusOr > >
34 | FromJson(const nlohmann::json& j, const std::string& name);
35 | }; // BoundaryCoefficient
36 |
37 | // Read the desired boundary coefficient array (`key` = `rbc`,
38 | // `zbs`, `rbs`, `zbc`)
39 | // from the given JSON obect and put the populated coefficients
40 | // in fast-toroidal order into a vector.
41 | // The indexing is `m * (2 * ntor + 1) + (ntor + n)`
42 | // and the size of the returned vector is `mpol * (2 * ntor + 1)`.
43 | std::vector BoundaryFromJson(const nlohmann::json& json,
44 | const std::string& key, int mpol,
45 | int ntor);
46 |
47 | } // namespace vmecpp
48 |
49 | #endif // VMECPP_COMMON_VMEC_INDATA_BOUNDARY_FROM_JSON_H_
50 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/free_boundary/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_subdirectory(external_magnetic_field)
2 | add_subdirectory(free_boundary_base)
3 | add_subdirectory(laplace_solver)
4 | add_subdirectory(mgrid_provider)
5 | add_subdirectory(nestor)
6 | add_subdirectory(regularized_integrals)
7 | add_subdirectory(singular_integrals)
8 | add_subdirectory(surface_geometry)
9 | add_subdirectory(tangential_partitioning)
10 | set (vmecpp_sources "${vmecpp_sources}" PARENT_SCOPE)
11 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/free_boundary/external_magnetic_field/BUILD.bazel:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | #
3 | # SPDX-License-Identifier: MIT
4 | cc_library(
5 | name = "external_magnetic_field",
6 | srcs = ["external_magnetic_field.cc"],
7 | hdrs = ["external_magnetic_field.h"],
8 | visibility = ["//visibility:public"],
9 | deps = [
10 | "//vmecpp/common/util:util",
11 | "//vmecpp/common/sizes:sizes",
12 | "//vmecpp/free_boundary/surface_geometry:surface_geometry",
13 | "//vmecpp/free_boundary/mgrid_provider:mgrid_provider",
14 | "//vmecpp/free_boundary/tangential_partitioning:tangential_partitioning",
15 | "@abscab_cpp//abscab:abscab",
16 | "@abseil-cpp//absl/algorithm:container",
17 | ],
18 | )
19 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/free_boundary/external_magnetic_field/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | list (APPEND vmecpp_sources
2 | ${CMAKE_CURRENT_SOURCE_DIR}/external_magnetic_field.cc
3 | ${CMAKE_CURRENT_SOURCE_DIR}/external_magnetic_field.h
4 | )
5 | set (vmecpp_sources "${vmecpp_sources}" PARENT_SCOPE)
6 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/free_boundary/free_boundary_base/BUILD.bazel:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | #
3 | # SPDX-License-Identifier: MIT
4 | cc_library(
5 | name = "free_boundary_base",
6 | hdrs = ["free_boundary_base.h"],
7 | visibility = ["//visibility:public"],
8 | deps = [
9 | "//vmecpp/common/fourier_basis_fast_toroidal:fourier_basis_fast_toroidal",
10 | "//vmecpp/common/sizes:sizes",
11 | "//vmecpp/common/util:util",
12 | "//vmecpp/free_boundary/tangential_partitioning:tangential_partitioning",
13 | "//vmecpp/free_boundary/external_magnetic_field:external_magnetic_field",
14 | ],
15 | )
16 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/free_boundary/free_boundary_base/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | list (APPEND vmecpp_sources
2 | ${CMAKE_CURRENT_SOURCE_DIR}/free_boundary_base.h
3 | )
4 | set (vmecpp_sources "${vmecpp_sources}" PARENT_SCOPE)
5 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/free_boundary/laplace_solver/BUILD.bazel:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | #
3 | # SPDX-License-Identifier: MIT
4 | cc_library(
5 | name = "laplace_solver",
6 | srcs = ["laplace_solver.cc"],
7 | hdrs = ["laplace_solver.h"],
8 | visibility = ["//visibility:public"],
9 | deps = [
10 | "@abseil-cpp//absl/log:check",
11 | "//vmecpp/common/util:util",
12 | "//vmecpp/common/sizes:sizes",
13 | "//vmecpp/common/fourier_basis_fast_toroidal",
14 | "//vmecpp/free_boundary/tangential_partitioning:tangential_partitioning",
15 | ],
16 | linkopts = [
17 | "-llapack",
18 | ],
19 | )
20 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/free_boundary/laplace_solver/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | list (APPEND vmecpp_sources
2 | ${CMAKE_CURRENT_SOURCE_DIR}/laplace_solver.cc
3 | ${CMAKE_CURRENT_SOURCE_DIR}/laplace_solver.h
4 | )
5 | set (vmecpp_sources "${vmecpp_sources}" PARENT_SCOPE)
6 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/free_boundary/laplace_solver/laplace_solver.h:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | //
3 | //
4 | // SPDX-License-Identifier: MIT
5 | #ifndef VMECPP_FREE_BOUNDARY_LAPLACE_SOLVER_LAPLACE_SOLVER_H_
6 | #define VMECPP_FREE_BOUNDARY_LAPLACE_SOLVER_LAPLACE_SOLVER_H_
7 |
8 | #include
9 |
10 | #include "vmecpp/common/fourier_basis_fast_toroidal/fourier_basis_fast_toroidal.h"
11 | #include "vmecpp/common/sizes/sizes.h"
12 | #include "vmecpp/common/util/util.h"
13 | #include "vmecpp/free_boundary/tangential_partitioning/tangential_partitioning.h"
14 |
15 | namespace vmecpp {
16 |
17 | class LaplaceSolver {
18 | public:
19 | LaplaceSolver(const Sizes* s, const FourierBasisFastToroidal* fb,
20 | const TangentialPartitioning* tp, int nf, int mf,
21 | std::span matrixShare, std::span iPiv,
22 | std::span bvecShare);
23 |
24 | void TransformGreensFunctionDerivative(const std::vector& greenp);
25 | void SymmetriseSourceTerm(const std::vector& gstore);
26 | void AccumulateFullGrpmn(const std::vector& grpmn_sin_singular);
27 | void PerformToroidalFourierTransforms();
28 | void PerformPoloidalFourierTransforms();
29 |
30 | void BuildMatrix();
31 | void DecomposeMatrix();
32 | void SolveForPotential(const std::vector& bvec_sin_singular);
33 |
34 | // Green's function derivative Fourier transform, non-singular part,
35 | // stellarator-symmetric
36 | std::vector grpmn_sin;
37 |
38 | // Green's function derivative Fourier transform, non-singular part,
39 | // non-stellarator-symmetric
40 | std::vector grpmn_cos;
41 |
42 | // symmetrized source term, stellarator-symmetric
43 | std::vector gstore_symm;
44 |
45 | std::vector bcos;
46 | std::vector bsin;
47 |
48 | std::vector actemp;
49 | std::vector astemp;
50 |
51 | // linear system to be solved
52 | std::vector bvec_sin;
53 | std::vector amat_sin_sin;
54 |
55 | private:
56 | const Sizes& s_;
57 | const FourierBasisFastToroidal& fb_;
58 | const TangentialPartitioning& tp_;
59 |
60 | int nf;
61 | int mf;
62 |
63 | // needed for LAPACK's dgetrf
64 | // non-owning pointers
65 | std::span matrixShare;
66 | std::span iPiv;
67 | std::span bvecShare;
68 |
69 | // ----------------
70 |
71 | int numLocal;
72 |
73 | std::vector grpOdd;
74 | std::vector grpEvn;
75 | };
76 |
77 | } // namespace vmecpp
78 |
79 | #endif // VMECPP_FREE_BOUNDARY_LAPLACE_SOLVER_LAPLACE_SOLVER_H_
80 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/free_boundary/mgrid_provider/BUILD.bazel:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | #
3 | # SPDX-License-Identifier: MIT
4 | cc_library(
5 | name = "mgrid_provider",
6 | srcs = ["mgrid_provider.cc"],
7 | hdrs = ["mgrid_provider.h"],
8 | visibility = ["//visibility:public"],
9 | deps = [
10 | "@abseil-cpp//absl/log:check",
11 | "@abseil-cpp//absl/strings:str_format",
12 | "//util/netcdf_io:netcdf_io",
13 | "//vmecpp/common/util:util",
14 | "//vmecpp/common/sizes:sizes",
15 | "//vmecpp/common/fourier_basis_fast_toroidal",
16 | "//vmecpp/common/makegrid_lib",
17 | "//vmecpp/free_boundary/tangential_partitioning:tangential_partitioning",
18 | ],
19 | )
20 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/free_boundary/mgrid_provider/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | list (APPEND vmecpp_sources
2 | ${CMAKE_CURRENT_SOURCE_DIR}/mgrid_provider.cc
3 | ${CMAKE_CURRENT_SOURCE_DIR}/mgrid_provider.h
4 | )
5 | set (vmecpp_sources "${vmecpp_sources}" PARENT_SCOPE)
6 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/free_boundary/mgrid_provider/mgrid_provider.h:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | //
3 | //
4 | // SPDX-License-Identifier: MIT
5 | #ifndef VMECPP_FREE_BOUNDARY_MGRID_PROVIDER_MGRID_PROVIDER_H_
6 | #define VMECPP_FREE_BOUNDARY_MGRID_PROVIDER_MGRID_PROVIDER_H_
7 |
8 | #include
9 | #include
10 |
11 | #include "vmecpp/common/makegrid_lib/makegrid_lib.h"
12 | #include "vmecpp/common/sizes/sizes.h"
13 |
14 | namespace vmecpp {
15 |
16 | class MGridProvider {
17 | public:
18 | MGridProvider();
19 |
20 | absl::Status LoadFile(const std::filesystem::path& filename,
21 | const std::vector& coil_currents);
22 |
23 | // May return an error status, when the response table resolution doesn't
24 | // match mgrid_params or coil_currents.size()
25 | absl::Status LoadFields(
26 | const makegrid::MakegridParameters& mgrid_params,
27 | const makegrid::MagneticFieldResponseTable& magnetic_response_table,
28 | const std::vector& coil_currents);
29 |
30 | void SetFixedMagneticField(const std::vector& fixed_br,
31 | const std::vector& fixed_bp,
32 | const std::vector& fixed_bz);
33 |
34 | void interpolate(int ztMin, int ztMax, int nZeta,
35 | const std::vector& r, const std::vector& z,
36 | std::vector& m_interpBr,
37 | std::vector& m_interpBp,
38 | std::vector& m_interpBz) const;
39 |
40 | // mgrid internals below
41 |
42 | std::vector bR;
43 | std::vector bP;
44 | std::vector bZ;
45 |
46 | int nfp;
47 |
48 | int numR;
49 | double minR;
50 | double maxR;
51 | double deltaR;
52 |
53 | int numZ;
54 | double minZ;
55 | double maxZ;
56 | double deltaZ;
57 |
58 | int numPhi;
59 |
60 | int nextcur;
61 |
62 | std::string mgrid_mode;
63 |
64 | bool IsLoaded() const { return has_mgrid_loaded_; }
65 |
66 | private:
67 | bool has_mgrid_loaded_;
68 | bool has_fixed_field_;
69 |
70 | std::vector fixed_br_;
71 | std::vector fixed_bp_;
72 | std::vector fixed_bz_;
73 | };
74 |
75 | } // namespace vmecpp
76 |
77 | #endif // VMECPP_FREE_BOUNDARY_MGRID_PROVIDER_MGRID_PROVIDER_H_
78 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/free_boundary/nestor/BUILD.bazel:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | #
3 | # SPDX-License-Identifier: MIT
4 | cc_library(
5 | name = "nestor",
6 | srcs = ["nestor.cc"],
7 | hdrs = ["nestor.h"],
8 | visibility = ["//visibility:public"],
9 | deps = [
10 | "//vmecpp/common/util:util",
11 | "//vmecpp/common/sizes:sizes",
12 | "//vmecpp/common/fourier_basis_fast_toroidal",
13 | "//vmecpp/free_boundary/tangential_partitioning:tangential_partitioning",
14 | "//vmecpp/free_boundary/external_magnetic_field:external_magnetic_field",
15 | "//vmecpp/free_boundary/singular_integrals:singular_integrals",
16 | "//vmecpp/free_boundary/regularized_integrals:regularized_integrals",
17 | "//vmecpp/free_boundary/laplace_solver:laplace_solver",
18 | "//vmecpp/free_boundary/free_boundary_base:free_boundary_base",
19 | ],
20 | )
21 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/free_boundary/nestor/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | list (APPEND vmecpp_sources
2 | ${CMAKE_CURRENT_SOURCE_DIR}/nestor.cc
3 | ${CMAKE_CURRENT_SOURCE_DIR}/nestor.h
4 | )
5 | set (vmecpp_sources "${vmecpp_sources}" PARENT_SCOPE)
6 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/free_boundary/nestor/nestor.h:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | //
3 | //
4 | // SPDX-License-Identifier: MIT
5 | #ifndef VMECPP_FREE_BOUNDARY_NESTOR_NESTOR_H_
6 | #define VMECPP_FREE_BOUNDARY_NESTOR_NESTOR_H_
7 |
8 | #include
9 | #include
10 |
11 | #include "vmecpp/common/fourier_basis_fast_toroidal/fourier_basis_fast_toroidal.h"
12 | #include "vmecpp/common/sizes/sizes.h"
13 | #include "vmecpp/common/util/util.h"
14 | #include "vmecpp/free_boundary/external_magnetic_field/external_magnetic_field.h"
15 | #include "vmecpp/free_boundary/free_boundary_base/free_boundary_base.h"
16 | #include "vmecpp/free_boundary/laplace_solver/laplace_solver.h"
17 | #include "vmecpp/free_boundary/mgrid_provider/mgrid_provider.h"
18 | #include "vmecpp/free_boundary/regularized_integrals/regularized_integrals.h"
19 | #include "vmecpp/free_boundary/singular_integrals/singular_integrals.h"
20 | #include "vmecpp/free_boundary/tangential_partitioning/tangential_partitioning.h"
21 |
22 | namespace vmecpp {
23 |
24 | class Nestor : public FreeBoundaryBase {
25 | public:
26 | Nestor(const Sizes* s, const TangentialPartitioning* tp,
27 | const MGridProvider* mgrid, std::span matrixShare,
28 | std::span bvecShare, std::span bSqVacShare,
29 | std::span iPiv, std::span vacuum_b_r_share,
30 | std::span vacuum_b_phi_share,
31 | std::span vacuum_b_z_share);
32 |
33 | bool update(
34 | const std::span rCC, const std::span rSS,
35 | const std::span rSC, const std::span rCS,
36 | const std::span zSC, const std::span zCS,
37 | const std::span zCC, const std::span zSS,
38 | int signOfJacobian, const std::span rAxis,
39 | const std::span zAxis, double* bSubUVac, double* bSubVVac,
40 | double netToroidalCurrent, int ivacskip,
41 | const VmecCheckpoint& vmec_checkpoint = VmecCheckpoint::NONE,
42 | bool at_checkpoint_iteration = false) final;
43 |
44 | const SingularIntegrals& GetSingularIntegrals() const;
45 | const RegularizedIntegrals& GetRegularizedIntegrals() const;
46 | const LaplaceSolver& GetLaplaceSolver() const;
47 |
48 | // tangential derivatives of scalar magnetic potential
49 | std::vector potU;
50 | std::vector potV;
51 |
52 | // covariant magnetic field components on surface
53 | std::vector bSubU;
54 | std::vector bSubV;
55 |
56 | private:
57 | // tangential Fourier resolution
58 | // 0 : ntor
59 | const int nf;
60 | // 0 : (mpol + 1)
61 | const int mf;
62 |
63 | SingularIntegrals si_;
64 | RegularizedIntegrals ri_;
65 | LaplaceSolver ls_;
66 |
67 | std::span bvecShare;
68 | };
69 |
70 | } // namespace vmecpp
71 |
72 | #endif // VMECPP_FREE_BOUNDARY_NESTOR_NESTOR_H_
73 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/free_boundary/regularized_integrals/BUILD.bazel:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | #
3 | # SPDX-License-Identifier: MIT
4 | cc_library(
5 | name = "regularized_integrals",
6 | srcs = ["regularized_integrals.cc"],
7 | hdrs = ["regularized_integrals.h"],
8 | visibility = ["//visibility:public"],
9 | deps = [
10 | "//vmecpp/common/util:util",
11 | "//vmecpp/common/sizes:sizes",
12 | "//vmecpp/common/fourier_basis_fast_toroidal",
13 | "//vmecpp/free_boundary/surface_geometry:surface_geometry",
14 | "//vmecpp/free_boundary/tangential_partitioning:tangential_partitioning",
15 | ],
16 | )
17 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/free_boundary/regularized_integrals/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | list (APPEND vmecpp_sources
2 | ${CMAKE_CURRENT_SOURCE_DIR}/regularized_integrals.cc
3 | ${CMAKE_CURRENT_SOURCE_DIR}/regularized_integrals.h
4 | )
5 | set (vmecpp_sources "${vmecpp_sources}" PARENT_SCOPE)
6 |
--------------------------------------------------------------------------------
/src/vmecpp/cpp/vmecpp/free_boundary/regularized_integrals/regularized_integrals.h:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: 2024-present Proxima Fusion GmbH
2 | //
3 | //
4 | // SPDX-License-Identifier: MIT
5 | #ifndef VMECPP_FREE_BOUNDARY_REGULARIZED_INTEGRALS_REGULARIZED_INTEGRALS_H_
6 | #define VMECPP_FREE_BOUNDARY_REGULARIZED_INTEGRALS_REGULARIZED_INTEGRALS_H_
7 |
8 | #include
9 |
10 | #include "vmecpp/common/fourier_basis_fast_toroidal/fourier_basis_fast_toroidal.h"
11 | #include "vmecpp/common/sizes/sizes.h"
12 | #include "vmecpp/common/util/util.h"
13 | #include "vmecpp/free_boundary/surface_geometry/surface_geometry.h"
14 | #include "vmecpp/free_boundary/tangential_partitioning/tangential_partitioning.h"
15 |
16 | namespace vmecpp {
17 |
18 | class RegularizedIntegrals {
19 | public:
20 | RegularizedIntegrals(const Sizes* s, const TangentialPartitioning* tp,
21 | const SurfaceGeometry* sg);
22 |
23 | void update(const std::vector