diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 68c9442106..06bf52a7e5 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -1,9 +1,10 @@ name: Benchmarks on: - pull_request: + pull_request_target: branches: - master + types: [opened, synchronize] workflow_dispatch: inputs: debug_enabled: @@ -73,6 +74,7 @@ jobs: source .venv-${{ matrix.python-version }}/bin/activate python -m pip install --upgrade pip pip install -r devtools/dev-requirements.txt + pip install matplotlib==3.9.2 - name: Benchmark with pytest-benchmark (PR) if: env.has_changes == 'true' diff --git a/.github/workflows/cache_dependencies.yml b/.github/workflows/cache_dependencies.yml index 661cec4df1..dd648d78f4 100644 --- a/.github/workflows/cache_dependencies.yml +++ b/.github/workflows/cache_dependencies.yml @@ -41,7 +41,7 @@ jobs: source .venv-${{ matrix.python-version }}/bin/activate python -m pip install --upgrade pip pip install -r devtools/dev-requirements.txt - pip install matplotlib==3.7.2 + pip install matplotlib==3.9.2 - name: Cache Python environment id: cache-env diff --git a/.github/workflows/changelog_update.yml b/.github/workflows/changelog_update.yml new file mode 100644 index 0000000000..78ca014c42 --- /dev/null +++ b/.github/workflows/changelog_update.yml @@ -0,0 +1,35 @@ +name: Check changelog updated + +on: + pull_request: + branches: + - master + types: [opened, synchronize, labeled, unlabeled] + +jobs: + check_changelog_updated: + runs-on: ubuntu-latest + steps: + - name: Filter changes + id: changes + uses: dorny/paths-filter@v3 + with: + filters: | + has_changes: + - 'desc/**' + - 'requirements.txt' + - 'requirements_conda.yml' + - '.github/workflows/changelog_update.yml' + + - name: Check for relevant changes + id: check_changes + run: echo "has_changes=${{ steps.changes.outputs.has_changes }}" >> $GITHUB_ENV + + - uses: actions/checkout@v4 + + - if: ${{ !contains(github.event.pull_request.labels.*.name, 'skip_changelog') && env.has_changes == 'true'}} + uses: danieljimeneznz/ensure-files-changed@v4.1.1 + with: + require-changes-to: | + CHANGELOG.md + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/jax_tests.yml b/.github/workflows/jax_tests.yml index 8e6c0da5a8..9749dc42a8 100644 --- a/.github/workflows/jax_tests.yml +++ b/.github/workflows/jax_tests.yml @@ -35,7 +35,6 @@ jobs: sed -i '1i\jax[cpu] == ${{ matrix.jax-version }}' ./requirements.txt cat ./requirements.txt pip install -r ./devtools/dev-requirements.txt - pip install matplotlib==3.7.2 - name: Verify dependencies run: | python --version diff --git a/.github/workflows/notebook_tests.yml b/.github/workflows/notebook_tests.yml index 1ceccb8ac1..78cd39f905 100644 --- a/.github/workflows/notebook_tests.yml +++ b/.github/workflows/notebook_tests.yml @@ -67,7 +67,7 @@ jobs: source .venv-${{ matrix.python-version }}/bin/activate python -m pip install --upgrade pip pip install -r devtools/dev-requirements.txt - pip install matplotlib==3.7.2 + pip install matplotlib==3.9.2 - name: Test notebooks with pytest and nbmake if: env.has_changes == 'true' diff --git a/.github/workflows/regression_tests.yml b/.github/workflows/regression_tests.yml index ed13617be4..540fc2ab48 100644 --- a/.github/workflows/regression_tests.yml +++ b/.github/workflows/regression_tests.yml @@ -66,7 +66,7 @@ jobs: source .venv-${{ matrix.python-version }}/bin/activate python -m pip install --upgrade pip pip install -r devtools/dev-requirements.txt - pip install matplotlib==3.7.2 + pip install matplotlib==3.9.2 - name: Set Swap Space if: env.has_changes == 'true' diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 54075f0892..1ce8b55c7a 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -72,7 +72,7 @@ jobs: source .venv-${{ matrix.combos.python_version }}/bin/activate python -m pip install --upgrade pip pip install -r devtools/dev-requirements.txt - pip install matplotlib==3.7.2 + pip install matplotlib==3.9.2 - name: Set Swap Space if: env.has_changes == 'true' diff --git a/.github/workflows/weekly_tests.yml b/.github/workflows/weekly_tests.yml index 2fb309bd8a..b1ac1e5614 100644 --- a/.github/workflows/weekly_tests.yml +++ b/.github/workflows/weekly_tests.yml @@ -26,6 +26,7 @@ jobs: run: | python -m pip install --upgrade pip pip install -r devtools/dev-requirements.txt + pip install matplotlib==3.9.2 - name: Set Swap Space uses: pierotofy/set-swap-space@master with: diff --git a/CHANGELOG.md b/CHANGELOG.md index 931e5621d4..6743861fbc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ New Features Bug Fixes - Fixes bug that occurs when taking the gradient of ``root`` and ``root_scalar`` with newer versions of JAX (>=0.4.34) and unpins the JAX version - +- Changes ``FixLambdaGauge`` constraint to now enforce zero flux surface average for lambda, instead of enforcing lambda(rho,0,0)=0 as it was incorrectly doing before. v0.12.3 ------- diff --git a/desc/basis.py b/desc/basis.py index 6dc88c5e66..0f01555cec 100644 --- a/desc/basis.py +++ b/desc/basis.py @@ -1440,7 +1440,9 @@ def zernike_radial_coeffs(l, m, exact=True): # hence they are all integers. So, we can use exact arithmetic with integer # division instead of floating point division. # [1]https://en.wikipedia.org/wiki/Zernike_polynomials#Other_representations - coeffs[ii, s] = ((-1) ** ((ll - s) // 2) * factorial((ll + s) // 2)) // ( + coeffs[ii, s] = ( + int((-1) ** ((ll - s) // 2)) * factorial((ll + s) // 2) + ) // ( factorial((ll - s) // 2) * factorial((s + mm) // 2) * factorial((s - mm) // 2) diff --git a/desc/magnetic_fields/_core.py b/desc/magnetic_fields/_core.py index bc1d7bad87..a8603351a0 100644 --- a/desc/magnetic_fields/_core.py +++ b/desc/magnetic_fields/_core.py @@ -1816,10 +1816,10 @@ def from_mgrid(cls, mgrid_file, extcur=None, method="cubic", extrap=False): ir = int(mgrid["ir"][()]) # number of grid points in the R coordinate jz = int(mgrid["jz"][()]) # number of grid points in the Z coordinate kp = int(mgrid["kp"][()]) # number of grid points in the phi coordinate - Rmin = mgrid["rmin"][()] # Minimum R coordinate (m) - Rmax = mgrid["rmax"][()] # Maximum R coordinate (m) - Zmin = mgrid["zmin"][()] # Minimum Z coordinate (m) - Zmax = mgrid["zmax"][()] # Maximum Z coordinate (m) + Rmin = mgrid["rmin"][()].filled() # Minimum R coordinate (m) + Rmax = mgrid["rmax"][()].filled() # Maximum R coordinate (m) + Zmin = mgrid["zmin"][()].filled() # Minimum Z coordinate (m) + Zmax = mgrid["zmax"][()].filled() # Maximum Z coordinate (m) nfp = int(mgrid["nfp"][()]) # Number of field periods Rgrid = np.linspace(Rmin, Rmax, ir) Zgrid = np.linspace(Zmin, Zmax, jz) @@ -1831,9 +1831,15 @@ def from_mgrid(cls, mgrid_file, extcur=None, method="cubic", extrap=False): bz = np.zeros([kp, jz, ir, nextcur]) for i in range(nextcur): coil_id = "%03d" % (i + 1,) - br[:, :, :, i] += mgrid["br_" + coil_id][()] # B_R radial magnetic field - bp[:, :, :, i] += mgrid["bp_" + coil_id][()] # B_phi toroidal field (T) - bz[:, :, :, i] += mgrid["bz_" + coil_id][()] # B_Z vertical magnetic field + br[:, :, :, i] += mgrid["br_" + coil_id][ + () + ].filled() # B_R radial magnetic field + bp[:, :, :, i] += mgrid["bp_" + coil_id][ + () + ].filled() # B_phi toroidal field (T) + bz[:, :, :, i] += mgrid["bz_" + coil_id][ + () + ].filled() # B_Z vertical magnetic field # shift axes to correct order br = np.moveaxis(br, (0, 1, 2), (1, 2, 0)) @@ -1849,13 +1855,13 @@ def from_mgrid(cls, mgrid_file, extcur=None, method="cubic", extrap=False): coil_id = "%03d" % (i + 1,) ar[:, :, :, i] += mgrid["ar_" + coil_id][ () - ] # A_R radial mag. vec. potential + ].filled() # A_R radial mag. vec. potential ap[:, :, :, i] += mgrid["ap_" + coil_id][ () - ] # A_phi toroidal mag. vec. potential + ].filled() # A_phi toroidal mag. vec. potential az[:, :, :, i] += mgrid["az_" + coil_id][ () - ] # A_Z vertical mag. vec. potential + ].filled() # A_Z vertical mag. vec. potential # shift axes to correct order ar = np.moveaxis(ar, (0, 1, 2), (1, 2, 0)) diff --git a/desc/objectives/linear_objectives.py b/desc/objectives/linear_objectives.py index 6f302f2df0..3e6275d0c9 100644 --- a/desc/objectives/linear_objectives.py +++ b/desc/objectives/linear_objectives.py @@ -12,7 +12,7 @@ from termcolor import colored from desc.backend import execute_on_cpu, jnp, tree_leaves, tree_map, tree_structure -from desc.basis import zernike_radial, zernike_radial_coeffs +from desc.basis import zernike_radial from desc.geometry import FourierRZCurve from desc.utils import broadcast_tree, errorif, setdefault @@ -774,8 +774,8 @@ def build(self, use_jit=False, verbose=1): super().build(use_jit=use_jit, verbose=verbose) -class FixLambdaGauge(_Objective): - """Fixes gauge freedom for lambda: lambda(theta=0,zeta=0)=0. +class FixLambdaGauge(FixParameters): + """Fixes gauge freedom for lambda, which sets the flux surface avg of lambda to 0. Note: this constraint is automatically applied when needed, and does not need to be included by the user. @@ -793,9 +793,6 @@ class FixLambdaGauge(_Objective): """ - _scalar = False - _linear = True - _fixed = False # not "diagonal", since it is fixing a sum _units = "(rad)" _print_value_fmt = "lambda gauge error: " @@ -806,75 +803,21 @@ def __init__( normalize_target=True, name="lambda gauge", ): + if eq.sym: + indices = False + else: + indices = np.where( + np.logical_and(eq.L_basis.modes[:, 1] == 0, eq.L_basis.modes[:, 2] == 0) + )[0] super().__init__( - things=eq, + thing=eq, + params={"L_lmn": indices}, target=0, - bounds=None, - weight=1, normalize=normalize, normalize_target=normalize_target, name=name, ) - def build(self, use_jit=False, verbose=1): - """Build constant arrays. - - Parameters - ---------- - use_jit : bool, optional - Whether to just-in-time compile the objective and derivatives. - verbose : int, optional - Level of output. - - """ - eq = self.things[0] - L_basis = eq.L_basis - - if L_basis.sym: - self._A = np.zeros((0, L_basis.num_modes)) - else: - # l(rho,0,0) = 0 - # at theta=zeta=0, basis for lambda reduces to just a polynomial in rho - # what this constraint does is make all the coefficients of each power - # of rho equal to zero - # i.e. if lambda = (L_200 + 2*L_310) rho**2 + (L_100 + 2*L_210)*rho - # this constraint will make - # L_200 + 2*L_310 = 0 - # L_100 + 2*L_210 = 0 - L_modes = L_basis.modes - mnpos = np.where((L_modes[:, 1:] >= [0, 0]).all(axis=1))[0] - l_lmn = L_modes[mnpos, :] - if len(l_lmn) > 0: - c = zernike_radial_coeffs(l_lmn[:, 0], l_lmn[:, 1]) - else: - c = np.zeros((0, 0)) - - A = np.zeros((c.shape[1], L_basis.num_modes)) - A[:, mnpos] = c.T - self._A = A - - self._dim_f = self._A.shape[0] - super().build(use_jit=use_jit, verbose=verbose) - - def compute(self, params, constants=None): - """Compute lambda gauge freedom errors. - - Parameters - ---------- - params : dict - Dictionary of equilibrium degrees of freedom, eg Equilibrium.params_dict - constants : dict - Dictionary of constant data, eg transforms, profiles etc. Defaults to - self.constants - - Returns - ------- - f : ndarray - gauge freedom errors. - - """ - return jnp.dot(self._A, params["L_lmn"]) - class FixThetaSFL(FixParameters): """Fixes lambda=0 so that poloidal angle is the SFL poloidal angle. diff --git a/desc/optimize/optimizer.py b/desc/optimize/optimizer.py index 48e016b67c..99a57c926d 100644 --- a/desc/optimize/optimizer.py +++ b/desc/optimize/optimizer.py @@ -209,9 +209,11 @@ def optimize( # noqa: C901 - FIXME: simplify this objective, nonlinear_constraints = _maybe_wrap_nonlinear_constraints( eq, objective, nonlinear_constraints, self.method, options ) - if not isinstance(objective, ProximalProjection): - for t in things: - linear_constraints = maybe_add_self_consistency(t, linear_constraints) + is_prox = isinstance(objective, ProximalProjection) + for t in things: + if isinstance(t, Equilibrium) and is_prox: + continue # don't add Equilibrium self-consistency if proximal is used + linear_constraints = maybe_add_self_consistency(t, linear_constraints) linear_constraint = _combine_constraints(linear_constraints) nonlinear_constraint = _combine_constraints(nonlinear_constraints) diff --git a/desc/plotting.py b/desc/plotting.py index 3a6dcdd76a..def22fa14d 100644 --- a/desc/plotting.py +++ b/desc/plotting.py @@ -1354,6 +1354,8 @@ def plot_section( * ``phi``: float, int or array-like. Toroidal angles to plot. If an integer, plot that number equally spaced in [0,2pi/NFP). Default 1 for axisymmetry and 6 for non-axisymmetry + * ``fill`` : bool, Whether the contours are filled, i.e. whether to use + `contourf` or `contour`. Default to ``fill=True`` Returns ------- @@ -1442,7 +1444,7 @@ def plot_section( R = coords["R"].reshape((nt, nr, nz), order="F") Z = coords["Z"].reshape((nt, nr, nz), order="F") data = data.reshape((nt, nr, nz), order="F") - + op = "contour" + ("f" if kwargs.pop("fill", True) else "") contourf_kwargs = {} if log: data = np.abs(data) # ensure data is positive for log plot @@ -1474,7 +1476,9 @@ def plot_section( for i in range(nphi): divider = make_axes_locatable(ax[i]) - cntr = ax[i].contourf(R[:, :, i], Z[:, :, i], data[:, :, i], **contourf_kwargs) + cntr = getattr(ax[i], op)( + R[:, :, i], Z[:, :, i], data[:, :, i], **contourf_kwargs + ) cax = divider.append_axes("right", **cax_kwargs) cbar = fig.colorbar(cntr, cax=cax) cbar.update_ticks() diff --git a/devtools/dev-requirements_conda.yml b/devtools/dev-requirements_conda.yml index 0fb6f88030..45b1260c6a 100644 --- a/devtools/dev-requirements_conda.yml +++ b/devtools/dev-requirements_conda.yml @@ -7,9 +7,9 @@ dependencies: - matplotlib >= 3.5.0, < 4.0.0 - mpmath >= 1.0.0, < 2.0 - netcdf4 >= 1.5.4, < 2.0 - - numpy >= 1.20.0, < 2.0 + - numpy >= 1.20.0 - psutil - - scipy >= 1.7.0, < 2.0 + - scipy >= 1.7.0 - termcolor - pip - pip: diff --git a/requirements.txt b/requirements.txt index c00e7674e2..84983d76ed 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,11 +6,11 @@ interpax >= 0.3.3 matplotlib >= 3.5.0, < 4.0.0 mpmath >= 1.0.0, < 2.0 netcdf4 >= 1.5.4, < 2.0 -numpy >= 1.20.0, < 2.0.0 +numpy >= 1.20.0 nvgpu orthax plotly >= 5.16, < 6.0 psutil pylatexenc >= 2.0, < 3.0 -scipy >= 1.7.0, < 2.0.0 +scipy >= 1.7.0 termcolor diff --git a/requirements_conda.yml b/requirements_conda.yml index 40db48f1cb..4c5af7f356 100644 --- a/requirements_conda.yml +++ b/requirements_conda.yml @@ -7,9 +7,9 @@ dependencies: - matplotlib >= 3.5.0, < 4.0.0 - mpmath >= 1.0.0, < 2.0 - netcdf4 >= 1.5.4, < 2.0 - - numpy >= 1.20.0, < 2.0 + - numpy >= 1.20.0 - psutil - - scipy >= 1.7.0, < 2.0 + - scipy >= 1.7.0 - termcolor - pip - pip: diff --git a/tests/baseline/test_1d_elongation.png b/tests/baseline/test_1d_elongation.png index ccf04b533c..70baf117c1 100644 Binary files a/tests/baseline/test_1d_elongation.png and b/tests/baseline/test_1d_elongation.png differ diff --git a/tests/baseline/test_1d_iota.png b/tests/baseline/test_1d_iota.png index 7d40e1c4e1..2bcfeadc01 100644 Binary files a/tests/baseline/test_1d_iota.png and b/tests/baseline/test_1d_iota.png differ diff --git a/tests/baseline/test_1d_iota_radial.png b/tests/baseline/test_1d_iota_radial.png index c910e0cba1..3058287751 100644 Binary files a/tests/baseline/test_1d_iota_radial.png and b/tests/baseline/test_1d_iota_radial.png differ diff --git a/tests/baseline/test_1d_logpsi.png b/tests/baseline/test_1d_logpsi.png index 562f4895b6..cebc3c930a 100644 Binary files a/tests/baseline/test_1d_logpsi.png and b/tests/baseline/test_1d_logpsi.png differ diff --git a/tests/baseline/test_1d_p.png b/tests/baseline/test_1d_p.png index 36db721bfc..25f7587240 100644 Binary files a/tests/baseline/test_1d_p.png and b/tests/baseline/test_1d_p.png differ diff --git a/tests/baseline/test_2d_g_rz.png b/tests/baseline/test_2d_g_rz.png index a8c3609ef6..7e04f9fcd3 100644 Binary files a/tests/baseline/test_2d_g_rz.png and b/tests/baseline/test_2d_g_rz.png differ diff --git a/tests/baseline/test_2d_g_tz.png b/tests/baseline/test_2d_g_tz.png index e58a3fb603..2a0d6e09fd 100644 Binary files a/tests/baseline/test_2d_g_tz.png and b/tests/baseline/test_2d_g_tz.png differ diff --git a/tests/baseline/test_2d_logF.png b/tests/baseline/test_2d_logF.png index 12bf243827..7ccacb699a 100644 Binary files a/tests/baseline/test_2d_logF.png and b/tests/baseline/test_2d_logF.png differ diff --git a/tests/baseline/test_2d_plot_Bn.png b/tests/baseline/test_2d_plot_Bn.png index 7e3ad74c80..f456f818fa 100644 Binary files a/tests/baseline/test_2d_plot_Bn.png and b/tests/baseline/test_2d_plot_Bn.png differ diff --git a/tests/baseline/test_Redl_figures_2_3.png b/tests/baseline/test_Redl_figures_2_3.png index 525d569f43..0ef0db71ad 100644 Binary files a/tests/baseline/test_Redl_figures_2_3.png and b/tests/baseline/test_Redl_figures_2_3.png differ diff --git a/tests/baseline/test_Redl_figures_4_5.png b/tests/baseline/test_Redl_figures_4_5.png index 52fbbbc549..053497ad68 100644 Binary files a/tests/baseline/test_Redl_figures_4_5.png and b/tests/baseline/test_Redl_figures_4_5.png differ diff --git a/tests/baseline/test_Redl_sfincs_QA.png b/tests/baseline/test_Redl_sfincs_QA.png index 4b2559cc7d..f50a41da45 100644 Binary files a/tests/baseline/test_Redl_sfincs_QA.png and b/tests/baseline/test_Redl_sfincs_QA.png differ diff --git a/tests/baseline/test_Redl_sfincs_QH.png b/tests/baseline/test_Redl_sfincs_QH.png index 15798d91d9..4a533bd4c9 100644 Binary files a/tests/baseline/test_Redl_sfincs_QH.png and b/tests/baseline/test_Redl_sfincs_QH.png differ diff --git a/tests/baseline/test_Redl_sfincs_tokamak_benchmark.png b/tests/baseline/test_Redl_sfincs_tokamak_benchmark.png index 3184aea467..f39f934f26 100644 Binary files a/tests/baseline/test_Redl_sfincs_tokamak_benchmark.png and b/tests/baseline/test_Redl_sfincs_tokamak_benchmark.png differ diff --git a/tests/baseline/test_binormal_drift_bounce1d.png b/tests/baseline/test_binormal_drift_bounce1d.png index dc27441b91..b90d94d804 100644 Binary files a/tests/baseline/test_binormal_drift_bounce1d.png and b/tests/baseline/test_binormal_drift_bounce1d.png differ diff --git a/tests/baseline/test_bounce1d_checks.png b/tests/baseline/test_bounce1d_checks.png index f3927bec61..23538254be 100644 Binary files a/tests/baseline/test_bounce1d_checks.png and b/tests/baseline/test_bounce1d_checks.png differ diff --git a/tests/baseline/test_fsa_F_normalized.png b/tests/baseline/test_fsa_F_normalized.png index 170263d9e1..023bb31f84 100644 Binary files a/tests/baseline/test_fsa_F_normalized.png and b/tests/baseline/test_fsa_F_normalized.png differ diff --git a/tests/baseline/test_fsa_I.png b/tests/baseline/test_fsa_I.png index 13401edc96..940416d05f 100644 Binary files a/tests/baseline/test_fsa_I.png and b/tests/baseline/test_fsa_I.png differ diff --git a/tests/baseline/test_plot_1d_curve.png b/tests/baseline/test_plot_1d_curve.png index 48e8e469fd..a0b92aa626 100644 Binary files a/tests/baseline/test_plot_1d_curve.png and b/tests/baseline/test_plot_1d_curve.png differ diff --git a/tests/baseline/test_plot_1d_surface.png b/tests/baseline/test_plot_1d_surface.png index 524767094d..722a538ac1 100644 Binary files a/tests/baseline/test_plot_1d_surface.png and b/tests/baseline/test_plot_1d_surface.png differ diff --git a/tests/baseline/test_plot_2d_surface.png b/tests/baseline/test_plot_2d_surface.png index 4d386b3eb1..2659f0cbe4 100644 Binary files a/tests/baseline/test_plot_2d_surface.png and b/tests/baseline/test_plot_2d_surface.png differ diff --git a/tests/baseline/test_plot_b_mag.png b/tests/baseline/test_plot_b_mag.png index c81f89f037..3bd4a9fc9a 100644 Binary files a/tests/baseline/test_plot_b_mag.png and b/tests/baseline/test_plot_b_mag.png differ diff --git a/tests/baseline/test_plot_basis_doublefourierseries.png b/tests/baseline/test_plot_basis_doublefourierseries.png index 8f08a4768b..9af4d0fe03 100644 Binary files a/tests/baseline/test_plot_basis_doublefourierseries.png and b/tests/baseline/test_plot_basis_doublefourierseries.png differ diff --git a/tests/baseline/test_plot_basis_fourierseries.png b/tests/baseline/test_plot_basis_fourierseries.png index c43503a1c0..514a41bfd7 100644 Binary files a/tests/baseline/test_plot_basis_fourierseries.png and b/tests/baseline/test_plot_basis_fourierseries.png differ diff --git a/tests/baseline/test_plot_basis_fourierzernike.png b/tests/baseline/test_plot_basis_fourierzernike.png index 495b1fe973..05a323cbda 100644 Binary files a/tests/baseline/test_plot_basis_fourierzernike.png and b/tests/baseline/test_plot_basis_fourierzernike.png differ diff --git a/tests/baseline/test_plot_basis_powerseries.png b/tests/baseline/test_plot_basis_powerseries.png index a2d1c9294c..cb60ec942c 100644 Binary files a/tests/baseline/test_plot_basis_powerseries.png and b/tests/baseline/test_plot_basis_powerseries.png differ diff --git a/tests/baseline/test_plot_boozer_modes.png b/tests/baseline/test_plot_boozer_modes.png index a2d1c6e374..4d75242af5 100644 Binary files a/tests/baseline/test_plot_boozer_modes.png and b/tests/baseline/test_plot_boozer_modes.png differ diff --git a/tests/baseline/test_plot_boozer_modes_breaking_only.png b/tests/baseline/test_plot_boozer_modes_breaking_only.png index 06f04654f1..54ec2378fb 100644 Binary files a/tests/baseline/test_plot_boozer_modes_breaking_only.png and b/tests/baseline/test_plot_boozer_modes_breaking_only.png differ diff --git a/tests/baseline/test_plot_boozer_modes_max.png b/tests/baseline/test_plot_boozer_modes_max.png index b3d52bbe2d..7d4f91b1b4 100644 Binary files a/tests/baseline/test_plot_boozer_modes_max.png and b/tests/baseline/test_plot_boozer_modes_max.png differ diff --git a/tests/baseline/test_plot_boozer_modes_no_norm.png b/tests/baseline/test_plot_boozer_modes_no_norm.png index a5ccc0e320..d43fac6cbd 100644 Binary files a/tests/baseline/test_plot_boozer_modes_no_norm.png and b/tests/baseline/test_plot_boozer_modes_no_norm.png differ diff --git a/tests/baseline/test_plot_boozer_surface.png b/tests/baseline/test_plot_boozer_surface.png index 4e13a758b3..7e60398064 100644 Binary files a/tests/baseline/test_plot_boozer_surface.png and b/tests/baseline/test_plot_boozer_surface.png differ diff --git a/tests/baseline/test_plot_boundaries.png b/tests/baseline/test_plot_boundaries.png index 9e730b85a6..734cec8051 100644 Binary files a/tests/baseline/test_plot_boundaries.png and b/tests/baseline/test_plot_boundaries.png differ diff --git a/tests/baseline/test_plot_boundary.png b/tests/baseline/test_plot_boundary.png index ab4e97d73b..be986fd4fc 100644 Binary files a/tests/baseline/test_plot_boundary.png and b/tests/baseline/test_plot_boundary.png differ diff --git a/tests/baseline/test_plot_boundary_surface.png b/tests/baseline/test_plot_boundary_surface.png index 1e4f60d91b..5d0f5c2f56 100644 Binary files a/tests/baseline/test_plot_boundary_surface.png and b/tests/baseline/test_plot_boundary_surface.png differ diff --git a/tests/baseline/test_plot_coefficients.png b/tests/baseline/test_plot_coefficients.png index 7852b9fbfd..020f6988d0 100644 Binary files a/tests/baseline/test_plot_coefficients.png and b/tests/baseline/test_plot_coefficients.png differ diff --git a/tests/baseline/test_plot_comparison.png b/tests/baseline/test_plot_comparison.png index 9f7a7f85d5..ed32ff6ce2 100644 Binary files a/tests/baseline/test_plot_comparison.png and b/tests/baseline/test_plot_comparison.png differ diff --git a/tests/baseline/test_plot_comparison_different_NFPs.png b/tests/baseline/test_plot_comparison_different_NFPs.png index 96a140648f..13faf3470f 100644 Binary files a/tests/baseline/test_plot_comparison_different_NFPs.png and b/tests/baseline/test_plot_comparison_different_NFPs.png differ diff --git a/tests/baseline/test_plot_comparison_no_theta.png b/tests/baseline/test_plot_comparison_no_theta.png index 2d1cd7a7ae..57c8634687 100644 Binary files a/tests/baseline/test_plot_comparison_no_theta.png and b/tests/baseline/test_plot_comparison_no_theta.png differ diff --git a/tests/baseline/test_plot_con_basis.png b/tests/baseline/test_plot_con_basis.png index 565721d5b5..2d87bf7c1d 100644 Binary files a/tests/baseline/test_plot_con_basis.png and b/tests/baseline/test_plot_con_basis.png differ diff --git a/tests/baseline/test_plot_cov_basis.png b/tests/baseline/test_plot_cov_basis.png index 68ed3a5906..bd94b586c7 100644 Binary files a/tests/baseline/test_plot_cov_basis.png and b/tests/baseline/test_plot_cov_basis.png differ diff --git a/tests/baseline/test_plot_grid_cheb1.png b/tests/baseline/test_plot_grid_cheb1.png index eae7af457f..eb93899cb8 100644 Binary files a/tests/baseline/test_plot_grid_cheb1.png and b/tests/baseline/test_plot_grid_cheb1.png differ diff --git a/tests/baseline/test_plot_grid_cheb2.png b/tests/baseline/test_plot_grid_cheb2.png index f1a3c5fb9b..bd3696f22c 100644 Binary files a/tests/baseline/test_plot_grid_cheb2.png and b/tests/baseline/test_plot_grid_cheb2.png differ diff --git a/tests/baseline/test_plot_grid_jacobi.png b/tests/baseline/test_plot_grid_jacobi.png index 5f120588ac..07bc558923 100644 Binary files a/tests/baseline/test_plot_grid_jacobi.png and b/tests/baseline/test_plot_grid_jacobi.png differ diff --git a/tests/baseline/test_plot_grid_linear.png b/tests/baseline/test_plot_grid_linear.png index 3b72a1a015..0aec12f3cc 100644 Binary files a/tests/baseline/test_plot_grid_linear.png and b/tests/baseline/test_plot_grid_linear.png differ diff --git a/tests/baseline/test_plot_grid_ocs.png b/tests/baseline/test_plot_grid_ocs.png index a165c3fb42..2438f913a5 100644 Binary files a/tests/baseline/test_plot_grid_ocs.png and b/tests/baseline/test_plot_grid_ocs.png differ diff --git a/tests/baseline/test_plot_grid_quad.png b/tests/baseline/test_plot_grid_quad.png index a20bc963a0..80512e0313 100644 Binary files a/tests/baseline/test_plot_grid_quad.png and b/tests/baseline/test_plot_grid_quad.png differ diff --git a/tests/baseline/test_plot_logo.png b/tests/baseline/test_plot_logo.png index c290d29474..ebe9e60d04 100644 Binary files a/tests/baseline/test_plot_logo.png and b/tests/baseline/test_plot_logo.png differ diff --git a/tests/baseline/test_plot_normF_2d.png b/tests/baseline/test_plot_normF_2d.png index 9552700b53..bc868055b2 100644 Binary files a/tests/baseline/test_plot_normF_2d.png and b/tests/baseline/test_plot_normF_2d.png differ diff --git a/tests/baseline/test_plot_normF_section.png b/tests/baseline/test_plot_normF_section.png index ead4cf980f..ef67c4444b 100644 Binary files a/tests/baseline/test_plot_normF_section.png and b/tests/baseline/test_plot_normF_section.png differ diff --git a/tests/baseline/test_plot_omnigenous_field.png b/tests/baseline/test_plot_omnigenous_field.png index a4d36172c4..9ab9663eef 100644 Binary files a/tests/baseline/test_plot_omnigenous_field.png and b/tests/baseline/test_plot_omnigenous_field.png differ diff --git a/tests/baseline/test_plot_poincare.png b/tests/baseline/test_plot_poincare.png index 30e7127448..66a5bad617 100644 Binary files a/tests/baseline/test_plot_poincare.png and b/tests/baseline/test_plot_poincare.png differ diff --git a/tests/baseline/test_plot_qs_error.png b/tests/baseline/test_plot_qs_error.png index 10485feb2d..df7496e482 100644 Binary files a/tests/baseline/test_plot_qs_error.png and b/tests/baseline/test_plot_qs_error.png differ diff --git a/tests/baseline/test_plot_surfaces.png b/tests/baseline/test_plot_surfaces.png index bdae244b07..b4000ed2c5 100644 Binary files a/tests/baseline/test_plot_surfaces.png and b/tests/baseline/test_plot_surfaces.png differ diff --git a/tests/baseline/test_plot_surfaces_HELIOTRON.png b/tests/baseline/test_plot_surfaces_HELIOTRON.png index 1b2c8c9dbd..fb5a8c91a1 100644 Binary files a/tests/baseline/test_plot_surfaces_HELIOTRON.png and b/tests/baseline/test_plot_surfaces_HELIOTRON.png differ diff --git a/tests/baseline/test_plot_surfaces_no_theta.png b/tests/baseline/test_plot_surfaces_no_theta.png index f2f3e9c6bd..0306256616 100644 Binary files a/tests/baseline/test_plot_surfaces_no_theta.png and b/tests/baseline/test_plot_surfaces_no_theta.png differ diff --git a/tests/baseline/test_plot_vmec_comparison.png b/tests/baseline/test_plot_vmec_comparison.png index 72d07abf30..5a0db9f755 100644 Binary files a/tests/baseline/test_plot_vmec_comparison.png and b/tests/baseline/test_plot_vmec_comparison.png differ diff --git a/tests/baseline/test_qh_optimization3.png b/tests/baseline/test_qh_optimization3.png deleted file mode 100644 index b8231c27ba..0000000000 Binary files a/tests/baseline/test_qh_optimization3.png and /dev/null differ diff --git a/tests/baseline/test_section_F.png b/tests/baseline/test_section_F.png index 573206b0b4..fb3f691ba6 100644 Binary files a/tests/baseline/test_section_F.png and b/tests/baseline/test_section_F.png differ diff --git a/tests/baseline/test_section_J.png b/tests/baseline/test_section_J.png index afc8cfce37..1e3a7ef226 100644 Binary files a/tests/baseline/test_section_J.png and b/tests/baseline/test_section_J.png differ diff --git a/tests/baseline/test_section_chi_contour.png b/tests/baseline/test_section_chi_contour.png new file mode 100644 index 0000000000..ac8c342c18 Binary files /dev/null and b/tests/baseline/test_section_chi_contour.png differ diff --git a/tests/baseline/test_section_logF.png b/tests/baseline/test_section_logF.png index 02456ea0a9..59cf418feb 100644 Binary files a/tests/baseline/test_section_logF.png and b/tests/baseline/test_section_logF.png differ diff --git a/tests/baseline/test_trapped_fraction_Kim.png b/tests/baseline/test_trapped_fraction_Kim.png index faa395ef87..10f1b90c12 100644 Binary files a/tests/baseline/test_trapped_fraction_Kim.png and b/tests/baseline/test_trapped_fraction_Kim.png differ diff --git a/tests/conftest.py b/tests/conftest.py index ccab0e07a6..54e60554d6 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -284,13 +284,13 @@ def DummyMixedCoilSet(tmpdir_factory): vf_coil, displacement=[0, 0, 2], n=3, endpoint=True ) xyz_coil = FourierXYZCoil(current=2) - phi = 2 * np.pi * np.linspace(0, 1, 20, endpoint=True) ** 2 + phi = 2 * np.pi * np.linspace(0, 1, 20, endpoint=True) spline_coil = SplineXYZCoil( current=1, X=np.cos(phi), Y=np.sin(phi), Z=np.zeros_like(phi), - knots=np.linspace(0, 2 * np.pi, len(phi)), + knots=phi, ) full_coilset = MixedCoilSet( (tf_coilset, vf_coilset, xyz_coil, spline_coil), check_intersection=False diff --git a/tests/inputs/HELIO_asym.h5 b/tests/inputs/HELIO_asym.h5 index c66a6cb100..3d57653cb6 100644 Binary files a/tests/inputs/HELIO_asym.h5 and b/tests/inputs/HELIO_asym.h5 differ diff --git a/tests/test_examples.py b/tests/test_examples.py index ffeeb7d791..d18af82ffb 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -1401,7 +1401,20 @@ def test_second_stage_optimization_CoilSet(): @pytest.mark.slow @pytest.mark.unit -def test_optimize_with_all_coil_types(DummyCoilSet, DummyMixedCoilSet): +@pytest.mark.parametrize( + "coil_type", + [ + "FourierPlanarCoil", + "FourierRZCoil", + "FourierXYZCoil", + "SplineXYZCoil", + "CoilSet sym", + "CoilSet asym", + "MixedCoilSet", + "nested CoilSet", + ], +) +def test_optimize_with_all_coil_types(DummyCoilSet, DummyMixedCoilSet, coil_type): """Test optimizing for every type of coil and dummy coil sets.""" sym_coils = load(load_from=str(DummyCoilSet["output_path_sym"]), file_format="hdf5") asym_coils = load( @@ -1417,66 +1430,63 @@ def test_optimize_with_all_coil_types(DummyCoilSet, DummyMixedCoilSet): quad_eval_grid = LinearGrid(M=2, sym=True) quad_field_grid = LinearGrid(N=2) - def test(c, method): - target = 11 - rtol = 1e-3 - # first just check that quad flux works for a couple iterations - # as this is an expensive objective to compute - obj = ObjectiveFunction( - QuadraticFlux( - eq=eq, - field=c, - vacuum=True, - weight=1e-4, - eval_grid=quad_eval_grid, - field_grid=quad_field_grid, - ) - ) - optimizer = Optimizer(method) - (c,), _ = optimizer.optimize(c, obj, maxiter=2, ftol=0, xtol=1e-15) - - # now check with optimizing geometry and actually check result - objs = [ - CoilLength(c, target=target), - ] - extra_msg = "" - if isinstance(c, MixedCoilSet): - # just to check they work without error - objs.extend( - [ - CoilCurvature(c, target=0.5, weight=1e-2), - CoilTorsion(c, target=0, weight=1e-2), - ] - ) - rtol = 3e-2 - extra_msg = " with curvature and torsion obj" - - obj = ObjectiveFunction(objs) - - (c,), _ = optimizer.optimize(c, obj, maxiter=25, ftol=5e-3, xtol=1e-15) - flattened_coils = tree_leaves( - c, is_leaf=lambda x: isinstance(x, _Coil) and not isinstance(x, CoilSet) - ) - lengths = [coil.compute("length")["length"] for coil in flattened_coils] - np.testing.assert_allclose( - lengths, target, rtol=rtol, err_msg=f"lengths {c}" + extra_msg - ) - spline_coil = mixed_coils.coils[-1].copy() - # single coil - test(FourierPlanarCoil(), "fmintr") - test(FourierRZCoil(), "fmintr") - test(FourierXYZCoil(), "fmintr") - test(spline_coil, "fmintr") + types = { + "FourierPlanarCoil": (FourierPlanarCoil(), "fmintr"), + "FourierRZCoil": (FourierRZCoil(), "fmintr"), + "FourierXYZCoil": (FourierXYZCoil(), "fmintr"), + "SplineXYZCoil": (spline_coil, "fmintr"), + "CoilSet sym": (sym_coils, "lsq-exact"), + "CoilSet asym": (asym_coils, "lsq-exact"), + "MixedCoilSet": (mixed_coils, "lsq-exact"), + "nested CoilSet": (nested_coils, "lsq-exact"), + } + c, method = types[coil_type] + + target = 11 + rtol = 1e-3 + # first just check that quad flux works for a couple iterations + # as this is an expensive objective to compute + obj = ObjectiveFunction( + QuadraticFlux( + eq=eq, + field=c, + vacuum=True, + weight=1e-4, + eval_grid=quad_eval_grid, + field_grid=quad_field_grid, + ) + ) + optimizer = Optimizer(method) + (cc,), _ = optimizer.optimize(c, obj, maxiter=2, ftol=0, xtol=1e-8, copy=True) + + # now check with optimizing geometry and actually check result + objs = [ + CoilLength(c, target=target), + ] + extra_msg = "" + if isinstance(c, MixedCoilSet): + # just to check they work without error + objs.extend( + [ + CoilCurvature(c, target=0.5, weight=1e-2), + CoilTorsion(c, target=0, weight=1e-2), + ] + ) + rtol = 3e-2 + extra_msg = " with curvature and torsion obj" - # CoilSet - test(sym_coils, "lsq-exact") - test(asym_coils, "lsq-exact") + obj = ObjectiveFunction(objs) - # MixedCoilSet - test(mixed_coils, "lsq-exact") - test(nested_coils, "lsq-exact") + (c,), _ = optimizer.optimize(c, obj, maxiter=25, ftol=5e-3, xtol=1e-8) + flattened_coils = tree_leaves( + c, is_leaf=lambda x: isinstance(x, _Coil) and not isinstance(x, CoilSet) + ) + lengths = [coil.compute("length")["length"] for coil in flattened_coils] + np.testing.assert_allclose( + lengths, target, rtol=rtol, err_msg=f"lengths {c}" + extra_msg + ) @pytest.mark.unit diff --git a/tests/test_linear_objectives.py b/tests/test_linear_objectives.py index d0c17f8e39..83f9a32936 100644 --- a/tests/test_linear_objectives.py +++ b/tests/test_linear_objectives.py @@ -2,14 +2,12 @@ import numpy as np import pytest -import scipy.linalg from qsc import Qsc import desc.examples from desc.backend import jnp from desc.equilibrium import Equilibrium from desc.geometry import FourierRZToroidalSurface -from desc.grid import LinearGrid from desc.io import load from desc.magnetic_fields import OmnigenousField from desc.objectives import ( @@ -69,10 +67,10 @@ def test_LambdaGauge_sym(DummyStellarator): eq = load(load_from=str(DummyStellarator["output_path"]), file_format="hdf5") with pytest.warns(UserWarning, match="Reducing radial"): eq.change_resolution(L=2, M=1, N=1) - correct_constraint_matrix = np.zeros((0, 5)) lam_con = FixLambdaGauge(eq) lam_con.build() - np.testing.assert_array_equal(lam_con._A, correct_constraint_matrix) + # should have no indices to fix + assert lam_con._params["L_lmn"].size == 0 @pytest.mark.unit @@ -105,13 +103,10 @@ def test_LambdaGauge_asym(): lam_con = FixLambdaGauge(eq) lam_con.build() - # make sure that any lambda in the null space gives lambda==0 at theta=zeta=0 - Z = scipy.linalg.null_space(lam_con._A) - grid = LinearGrid(L=10, theta=[0], zeta=[0]) - for z in Z.T: - eq.L_lmn = z - lam = eq.compute("lambda", grid=grid)["lambda"] - np.testing.assert_allclose(lam, 0, atol=1e-15) + indices = np.where( + np.logical_and(eq.L_basis.modes[:, 1] == 0, eq.L_basis.modes[:, 2] == 0) + )[0] + np.testing.assert_allclose(indices, lam_con._params["L_lmn"]) @pytest.mark.regression diff --git a/tests/test_plotting.py b/tests/test_plotting.py index 8488c91a87..44a3c5c2da 100644 --- a/tests/test_plotting.py +++ b/tests/test_plotting.py @@ -429,6 +429,14 @@ def test_section_J(self): return fig + @pytest.mark.unit + @pytest.mark.mpl_image_compare(remove_text=True, tolerance=tol_2d) + def test_section_chi_contour(self): + """Test plotting poincare section of poloidal flux, with fill=False.""" + eq = get("DSHAPE_CURRENT") + fig, ax = plot_section(eq, "chi", fill=False, levels=20) + return fig + @pytest.mark.unit @pytest.mark.mpl_image_compare(remove_text=True, tolerance=tol_2d) def test_section_F(self): diff --git a/tests/test_stability_funs.py b/tests/test_stability_funs.py index 72b4819b38..72bee4fea4 100644 --- a/tests/test_stability_funs.py +++ b/tests/test_stability_funs.py @@ -64,7 +64,8 @@ def get_vmec_data(path, quantity): """ f = Dataset(path) - rho = np.sqrt(f.variables["phi"] / np.array(f.variables["phi"])[-1]) + phi = np.array(f.variables["phi"]) + rho = np.sqrt(phi / phi[-1]) q = np.array(f.variables[quantity]) f.close() return rho, q diff --git a/tests/test_vmec.py b/tests/test_vmec.py index 0fef594b3c..91b34667aa 100644 --- a/tests/test_vmec.py +++ b/tests/test_vmec.py @@ -1022,7 +1022,7 @@ def test_vmec_save_asym(VMEC_save_asym): vmec.variables["jcurv"][20:100], desc.variables["jcurv"][20:100], rtol=2 ) np.testing.assert_allclose( - vmec.variables["DShear"][20:100], desc.variables["DShear"][20:100], rtol=3e-2 + vmec.variables["DShear"][20:100], desc.variables["DShear"][20:100], rtol=6e-2 ) np.testing.assert_allclose( vmec.variables["DCurr"][20:100], @@ -1074,13 +1074,22 @@ def test_vmec_save_asym(VMEC_save_asym): # Next, calculate some quantities and compare # the DESC wout -> DESC (should be very close) # and the DESC wout -> VMEC wout (should be approximately close) + surfs = desc.variables["ns"][:] + s_full = np.linspace(0, 1, surfs) + s_half = s_full[0:-1] + 0.5 / (surfs - 1) + r_full = np.sqrt(s_full) + r_half = np.sqrt(s_half) + vol_grid = LinearGrid( - rho=np.sqrt( - abs( - vmec.variables["phi"][:].filled() - / np.max(np.abs(vmec.variables["phi"][:].filled())) - ) - )[10::10], + rho=r_full[10::10], + M=15, + N=15, + NFP=eq.NFP, + axis=False, + sym=False, + ) + vol_half_grid = LinearGrid( + rho=r_half[10::10], M=15, N=15, NFP=eq.NFP, @@ -1100,12 +1109,13 @@ def test( atol_vmec_desc_wout=1e-5, rtol_vmec_desc_wout=1e-2, grid=vol_grid, + is_half_grid=False, ): """Helper fxn to evaluate Fourier series from wout and compare to DESC.""" xm = desc.variables["xm_nyq"][:] if use_nyq else desc.variables["xm"][:] xn = desc.variables["xn_nyq"][:] if use_nyq else desc.variables["xn"][:] - si = abs(vmec.variables["phi"][:] / np.max(np.abs(vmec.variables["phi"][:]))) + si = np.insert(s_half, 0, 0) if is_half_grid else s_full rho = grid.nodes[:, 0] s = rho**2 # some quantities must be negated before comparison bc @@ -1166,18 +1176,26 @@ def test( rtol=rtol_vmec_desc_wout, ) - # R & Z & lambda + # R & Z test("rmn", "R", use_nyq=False) - test("zmn", "Z", use_nyq=False, atol_vmec_desc_wout=4e-2) - + test("zmn", "Z", use_nyq=False, atol_vmec_desc_wout=1e-2) + test( + "lmn", + "lambda", + use_nyq=False, + atol_vmec_desc_wout=1e-2, + negate_DESC_quant=True, + grid=vol_half_grid, + is_half_grid=True, + ) # |B| test("bmn", "|B|", rtol_desc_desc_wout=7e-4) # B^zeta - test("bsupvmn", "B^zeta") # ,rtol_desc_desc_wout=6e-5) + test("bsupvmn", "B^zeta") # B_zeta - test("bsubvmn", "B_zeta", rtol_desc_desc_wout=3e-4) + test("bsubvmn", "B_zeta", grid=vol_half_grid, is_half_grid=True) # hard to compare to VMEC for the currents, since # VMEC F error is worse and equilibria are not exactly similar @@ -1185,30 +1203,22 @@ def test( test("currumn", "J^theta", atol_vmec_desc_wout=1e4) test("currvmn", "J^zeta", negate_DESC_quant=True, atol_vmec_desc_wout=1e5) - # can only compare lambda, sqrt(g) B_psi B^theta and B_theta at bdry - test( - "lmn", - "lambda", - use_nyq=False, - negate_DESC_quant=True, - grid=bdry_grid, - atol_desc_desc_wout=4e-4, - atol_vmec_desc_wout=5e-2, - ) test( "gmn", "sqrt(g)", convert_sqrt_g_or_B_rho=True, negate_DESC_quant=True, - grid=bdry_grid, - rtol_desc_desc_wout=5e-4, - rtol_vmec_desc_wout=4e-2, + grid=vol_half_grid, + is_half_grid=True, ) + + # Compare B_psi B^theta and B_theta at bdry only test( "bsupumn", "B^theta", negate_DESC_quant=True, grid=bdry_grid, + is_half_grid=True, atol_vmec_desc_wout=6e-4, ) test( @@ -1216,16 +1226,14 @@ def test( "B_theta", negate_DESC_quant=True, grid=bdry_grid, - atol_desc_desc_wout=1e-4, - atol_vmec_desc_wout=4e-4, + is_half_grid=True, ) test( "bsubsmn", "B_rho", - grid=bdry_grid, convert_sqrt_g_or_B_rho=True, - rtol_vmec_desc_wout=6e-2, - atol_vmec_desc_wout=9e-3, + grid=bdry_grid, + atol_vmec_desc_wout=2e-3, )