constellaration 0.3.0__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {constellaration-0.3.0 → constellaration-0.4.0}/PKG-INFO +13 -12
- {constellaration-0.3.0 → constellaration-0.4.0}/README.md +12 -11
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/__about__.py +2 -2
- constellaration-0.4.0/constellaration/mhd/desc_utils.py +284 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/mhd/vmec_utils.py +8 -1
- {constellaration-0.3.0 → constellaration-0.4.0}/.gitignore +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/LICENSE +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/__init__.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/boozer/__init__.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/boozer/boozer.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/data_generation/__init__.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/data_generation/desc_optimization.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/data_generation/desc_optimization_settings.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/data_generation/rotational_transform_scaling.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/data_generation/vmec_optimization.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/data_generation/vmec_optimization_settings.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/forward_model.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/generative_model/bootstrap_dataset.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/generative_model/mcmc_algo.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/generative_model/optimize_with_mcmc.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/geometry/__init__.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/geometry/radial_profile.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/geometry/spectral_condensation.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/geometry/surface_rz_fourier.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/geometry/surface_utils.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/geometry/surface_utils_desc.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/initial_guess.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/mhd/__init__.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/mhd/flux_power_series.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/mhd/geometry_utils.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/mhd/ideal_mhd_parameters.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/mhd/ideal_mhd_parameters_utils.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/mhd/magnetics_utils.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/mhd/near_axis_configuration.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/mhd/turbulent_transport.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/mhd/vmec_settings.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/mhd_stable_qi_scoring.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/omnigeneity/__init__.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/omnigeneity/omnigenity_field.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/omnigeneity/omnigenity_field_desc.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/omnigeneity/omnigenity_field_plot.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/omnigeneity/omnigenity_field_sampling.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/omnigeneity/qi.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/optimization/__init__.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/optimization/augmented_lagrangian.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/optimization/augmented_lagrangian_runner.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/optimization/scipy_minimize_runner.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/optimization/settings.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/problems.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/utils/__init__.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/utils/dataset_utils.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/utils/file_exporter.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/utils/pytree.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/utils/seed_util.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/utils/types.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/utils/visualization.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/constellaration/utils/visualization_utils.py +0 -0
- {constellaration-0.3.0 → constellaration-0.4.0}/pyproject.toml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: constellaration
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Code for analyzing and evaluating stellarator plasma shapes
|
|
5
5
|
Project-URL: Documentation, https://github.com/proximafusion/constellaration#readme
|
|
6
6
|
Project-URL: Issues, https://github.com/proximafusion/constellaration/issues
|
|
@@ -69,33 +69,34 @@ To keep results comparable, the benchmark of [Cadena et al., NeurIPS 2025](https
|
|
|
69
69
|
|
|
70
70
|
| Use case | Version |
|
|
71
71
|
| --- | --- |
|
|
72
|
-
| [ConStellaration leaderboard](https://huggingface.co/spaces/proxima-fusion/constellaration-bench) (geometrical, simple-to-build QI, MHD-stable QI problems) | [`v0.
|
|
72
|
+
| [ConStellaration leaderboard](https://huggingface.co/spaces/proxima-fusion/constellaration-bench) (geometrical, simple-to-build QI, MHD-stable QI problems) | [`v0.2.6`](https://github.com/proximafusion/constellaration/releases/tag/v0.2.6) |
|
|
73
73
|
| Original NeurIPS 2025 paper experiments | [`0.2.1`](https://pypi.org/project/constellaration/0.2.1/) |
|
|
74
|
-
| Latest development | `main` |
|
|
74
|
+
| Latest development (not comparable to the leaderboard) | `main` / latest release |
|
|
75
75
|
|
|
76
|
-
If you are working on the challenge or comparing against the leaderboard, install the
|
|
76
|
+
If you are working on the challenge or comparing against the leaderboard, install the benchmark version:
|
|
77
77
|
|
|
78
78
|
```bash
|
|
79
|
-
pip install constellaration==0.
|
|
79
|
+
pip install constellaration==0.2.6
|
|
80
80
|
```
|
|
81
81
|
|
|
82
82
|
or, from source:
|
|
83
83
|
|
|
84
84
|
```bash
|
|
85
|
-
git clone --branch v0.
|
|
85
|
+
git clone --branch v0.2.6 https://github.com/proximafusion/constellaration.git
|
|
86
86
|
cd constellaration
|
|
87
87
|
pip install .
|
|
88
88
|
```
|
|
89
89
|
|
|
90
|
-
The leaderboard evaluates every submission with exactly this version, so scores computed locally with `constellaration==0.
|
|
91
|
-
|
|
90
|
+
The leaderboard evaluates every submission with exactly this version, so scores computed locally with `constellaration==0.2.6` match the leaderboard.
|
|
91
|
+
Newer releases may produce different metrics and scores and are not used by the leaderboard unless announced here.
|
|
92
92
|
The PyPI version always equals the git tag of the release it was built from (`constellaration==X.Y.Z` ⇔ tag `vX.Y.Z`).
|
|
93
93
|
|
|
94
|
-
Differences
|
|
95
|
-
-
|
|
96
|
-
-
|
|
94
|
+
Differences that affect benchmark scores:
|
|
95
|
+
- **`0.2.1` → `0.2.6`:** the rotational transform constraint uses the absolute value of the edge rotational transform, so boundaries with negative iota are no longer penalized. Newer VMEC++ and other numerical dependencies cause only negligible numerical differences.
|
|
96
|
+
- **`0.2.6` → `0.3.0`:** the flux compression in regions of bad curvature (a constraint of the MHD-stable problem) is computed with the field-aligned (PEST) Jacobian instead of the VMEC one ([#109](https://github.com/proximafusion/constellaration/pull/109)). This fixes a bug but changes MHD-stable feasibility, so `0.3.0` and later are not comparable to the leaderboard.
|
|
97
97
|
|
|
98
|
-
|
|
98
|
+
> [!NOTE]
|
|
99
|
+
> From 2026-10-02 to 2026-10-08 the leaderboard was briefly evaluated with `0.3.0`. The affected submissions have been re-evaluated with `0.2.6`.
|
|
99
100
|
|
|
100
101
|
## Installation
|
|
101
102
|
|
|
@@ -17,33 +17,34 @@ To keep results comparable, the benchmark of [Cadena et al., NeurIPS 2025](https
|
|
|
17
17
|
|
|
18
18
|
| Use case | Version |
|
|
19
19
|
| --- | --- |
|
|
20
|
-
| [ConStellaration leaderboard](https://huggingface.co/spaces/proxima-fusion/constellaration-bench) (geometrical, simple-to-build QI, MHD-stable QI problems) | [`v0.
|
|
20
|
+
| [ConStellaration leaderboard](https://huggingface.co/spaces/proxima-fusion/constellaration-bench) (geometrical, simple-to-build QI, MHD-stable QI problems) | [`v0.2.6`](https://github.com/proximafusion/constellaration/releases/tag/v0.2.6) |
|
|
21
21
|
| Original NeurIPS 2025 paper experiments | [`0.2.1`](https://pypi.org/project/constellaration/0.2.1/) |
|
|
22
|
-
| Latest development | `main` |
|
|
22
|
+
| Latest development (not comparable to the leaderboard) | `main` / latest release |
|
|
23
23
|
|
|
24
|
-
If you are working on the challenge or comparing against the leaderboard, install the
|
|
24
|
+
If you are working on the challenge or comparing against the leaderboard, install the benchmark version:
|
|
25
25
|
|
|
26
26
|
```bash
|
|
27
|
-
pip install constellaration==0.
|
|
27
|
+
pip install constellaration==0.2.6
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
or, from source:
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
|
-
git clone --branch v0.
|
|
33
|
+
git clone --branch v0.2.6 https://github.com/proximafusion/constellaration.git
|
|
34
34
|
cd constellaration
|
|
35
35
|
pip install .
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
The leaderboard evaluates every submission with exactly this version, so scores computed locally with `constellaration==0.
|
|
39
|
-
|
|
38
|
+
The leaderboard evaluates every submission with exactly this version, so scores computed locally with `constellaration==0.2.6` match the leaderboard.
|
|
39
|
+
Newer releases may produce different metrics and scores and are not used by the leaderboard unless announced here.
|
|
40
40
|
The PyPI version always equals the git tag of the release it was built from (`constellaration==X.Y.Z` ⇔ tag `vX.Y.Z`).
|
|
41
41
|
|
|
42
|
-
Differences
|
|
43
|
-
-
|
|
44
|
-
-
|
|
42
|
+
Differences that affect benchmark scores:
|
|
43
|
+
- **`0.2.1` → `0.2.6`:** the rotational transform constraint uses the absolute value of the edge rotational transform, so boundaries with negative iota are no longer penalized. Newer VMEC++ and other numerical dependencies cause only negligible numerical differences.
|
|
44
|
+
- **`0.2.6` → `0.3.0`:** the flux compression in regions of bad curvature (a constraint of the MHD-stable problem) is computed with the field-aligned (PEST) Jacobian instead of the VMEC one ([#109](https://github.com/proximafusion/constellaration/pull/109)). This fixes a bug but changes MHD-stable feasibility, so `0.3.0` and later are not comparable to the leaderboard.
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
> [!NOTE]
|
|
47
|
+
> From 2026-10-02 to 2026-10-08 the leaderboard was briefly evaluated with `0.3.0`. The affected submissions have been re-evaluated with `0.2.6`.
|
|
47
48
|
|
|
48
49
|
## Installation
|
|
49
50
|
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.
|
|
22
|
-
__version_tuple__ = version_tuple = (0,
|
|
21
|
+
__version__ = version = '0.4.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 4, 0)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
"""DESC ideal-MHD equilibria: a serializable representation, conversion to and from
|
|
2
|
+
DESC objects, and the pipeline that re-solves a VMEC++ equilibrium with DESC.
|
|
3
|
+
|
|
4
|
+
The `desc_equilibrium` subset of the ConStellaration Hugging Face dataset stores one
|
|
5
|
+
`DescEquilibrium` JSON per plasma configuration, produced by
|
|
6
|
+
`solve_desc_equilibrium_from_vmecpp_wout`.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import pathlib
|
|
10
|
+
import tempfile
|
|
11
|
+
|
|
12
|
+
import jaxtyping as jt
|
|
13
|
+
import numpy as np
|
|
14
|
+
import pydantic
|
|
15
|
+
from desc import equilibrium as desc_equilibrium
|
|
16
|
+
from desc import profiles as desc_profiles
|
|
17
|
+
from desc import vmec as desc_vmec
|
|
18
|
+
from typing_extensions import Self
|
|
19
|
+
from vmecpp import _pydantic_numpy as pydantic_numpy
|
|
20
|
+
|
|
21
|
+
from constellaration.mhd import vmec_utils
|
|
22
|
+
from constellaration.utils import file_exporter
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class DescSplineProfile(pydantic_numpy.BaseModelWithNumpy):
|
|
26
|
+
"""A radial profile represented as a DESC `SplineProfile`."""
|
|
27
|
+
|
|
28
|
+
method: str = "cubic2"
|
|
29
|
+
"""Spline interpolation method, e.g. `"linear"`, `"cubic"` or `"cubic2"`."""
|
|
30
|
+
|
|
31
|
+
name: str = ""
|
|
32
|
+
"""Name of the profile, e.g. `"pressure"` or `"current"`."""
|
|
33
|
+
|
|
34
|
+
knots: jt.Float[np.ndarray, " n_knots"]
|
|
35
|
+
"""Knots of the spline in the normalized effective radius `rho`."""
|
|
36
|
+
|
|
37
|
+
params: jt.Float[np.ndarray, " n_knots"]
|
|
38
|
+
"""Values of the profile at the knots."""
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class DescEquilibrium(pydantic_numpy.BaseModelWithNumpy):
|
|
42
|
+
"""A fixed-boundary DESC equilibrium.
|
|
43
|
+
|
|
44
|
+
The geometry is represented on a Fourier-Zernike basis, see
|
|
45
|
+
https://desc-docs.readthedocs.io/en/stable/notebooks/basis_grid.html.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
r_lmn: jt.Float[np.ndarray, " n_modes_r"]
|
|
49
|
+
"""Fourier-Zernike coefficients of the cylindrical coordinate R."""
|
|
50
|
+
|
|
51
|
+
z_lmn: jt.Float[np.ndarray, " n_modes_z"]
|
|
52
|
+
"""Fourier-Zernike coefficients of the cylindrical coordinate Z."""
|
|
53
|
+
|
|
54
|
+
lambda_lmn: jt.Float[np.ndarray, " n_modes_lambda"]
|
|
55
|
+
"""Fourier-Zernike coefficients of the poloidal stream function lambda."""
|
|
56
|
+
|
|
57
|
+
radial_resolution: pydantic.NonNegativeInt
|
|
58
|
+
"""Radial resolution L of the Fourier-Zernike basis."""
|
|
59
|
+
|
|
60
|
+
poloidal_resolution: pydantic.NonNegativeInt
|
|
61
|
+
"""Poloidal resolution M of the Fourier-Zernike basis."""
|
|
62
|
+
|
|
63
|
+
toroidal_resolution: pydantic.NonNegativeInt
|
|
64
|
+
"""Toroidal resolution N of the Fourier-Zernike basis."""
|
|
65
|
+
|
|
66
|
+
spectral_indexing: str = "ansi"
|
|
67
|
+
"""Indexing convention of the Zernike polynomials, `"ansi"` or `"fringe"`."""
|
|
68
|
+
|
|
69
|
+
n_field_periods: pydantic.PositiveInt
|
|
70
|
+
"""Number of toroidal field periods."""
|
|
71
|
+
|
|
72
|
+
is_stellarator_symmetric: bool = True
|
|
73
|
+
"""Whether the equilibrium is stellarator symmetric."""
|
|
74
|
+
|
|
75
|
+
total_toroidal_flux: float
|
|
76
|
+
"""Toroidal magnetic flux enclosed by the plasma boundary, in Wb."""
|
|
77
|
+
|
|
78
|
+
pressure_profile: DescSplineProfile
|
|
79
|
+
"""Plasma pressure, in Pa."""
|
|
80
|
+
|
|
81
|
+
current_profile: DescSplineProfile | None = None
|
|
82
|
+
"""Enclosed toroidal current, in A.
|
|
83
|
+
|
|
84
|
+
Exactly one of `current_profile` and `iota_profile` is set.
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
iota_profile: DescSplineProfile | None = None
|
|
88
|
+
"""Rotational transform.
|
|
89
|
+
|
|
90
|
+
Exactly one of `current_profile` and `iota_profile` is set.
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
r_axis: jt.Float[np.ndarray, " n_modes_r_axis"] | None = None
|
|
94
|
+
"""Fourier coefficients of the R coordinate of the magnetic axis."""
|
|
95
|
+
|
|
96
|
+
z_axis: jt.Float[np.ndarray, " n_modes_z_axis"] | None = None
|
|
97
|
+
"""Fourier coefficients of the Z coordinate of the magnetic axis."""
|
|
98
|
+
|
|
99
|
+
r_boundary_lmn: jt.Float[np.ndarray, " n_modes_r_boundary"] | None = None
|
|
100
|
+
"""Fourier coefficients of the R coordinate of the plasma boundary."""
|
|
101
|
+
|
|
102
|
+
z_boundary_lmn: jt.Float[np.ndarray, " n_modes_z_boundary"] | None = None
|
|
103
|
+
"""Fourier coefficients of the Z coordinate of the plasma boundary."""
|
|
104
|
+
|
|
105
|
+
@pydantic.model_validator(mode="after")
|
|
106
|
+
def _check_exactly_one_of_current_or_iota(self) -> Self:
|
|
107
|
+
if (self.current_profile is None) == (self.iota_profile is None):
|
|
108
|
+
raise ValueError(
|
|
109
|
+
"Exactly one of current_profile and iota_profile must be provided."
|
|
110
|
+
)
|
|
111
|
+
return self
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class DescEquilibriumSolution(pydantic_numpy.BaseModelWithNumpy):
|
|
115
|
+
"""A DESC equilibrium solved for force balance, with its residual force error."""
|
|
116
|
+
|
|
117
|
+
equilibrium: DescEquilibrium
|
|
118
|
+
|
|
119
|
+
normalized_mean_absolute_force_error: float
|
|
120
|
+
"""Mean absolute force-balance error of `equilibrium`, as reported by DESC's
|
|
121
|
+
`ForceBalance` objective, normalized by its characteristic force."""
|
|
122
|
+
|
|
123
|
+
normalized_max_absolute_force_error: float
|
|
124
|
+
"""Maximum absolute force-balance error of `equilibrium`, as reported by DESC's
|
|
125
|
+
`ForceBalance` objective, normalized by its characteristic force."""
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def solve_desc_equilibrium_from_vmecpp_wout(
|
|
129
|
+
vmecpp_wout: vmec_utils.VmecppWOut,
|
|
130
|
+
) -> DescEquilibriumSolution:
|
|
131
|
+
"""Re-solve a VMEC++ equilibrium with DESC.
|
|
132
|
+
|
|
133
|
+
This is the pipeline that produced the `desc_equilibrium` subset of the
|
|
134
|
+
ConStellaration dataset: DESC is initialized from the VMEC++ equilibrium and
|
|
135
|
+
then solved for force balance with fixed boundary, pressure and toroidal current.
|
|
136
|
+
"""
|
|
137
|
+
return solve_desc_equilibrium(desc_equilibrium_from_vmecpp_wout(vmecpp_wout))
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def desc_equilibrium_from_vmecpp_wout(
|
|
141
|
+
vmecpp_wout: vmec_utils.VmecppWOut,
|
|
142
|
+
) -> DescEquilibrium:
|
|
143
|
+
"""Fit a DESC equilibrium to a VMEC++ equilibrium, without solving it.
|
|
144
|
+
|
|
145
|
+
The Fourier-Zernike resolution follows DESC's defaults for the given VMEC
|
|
146
|
+
resolution, and the toroidal current (not the rotational transform) is taken
|
|
147
|
+
as the profile constraint, as in the VMEC++ run.
|
|
148
|
+
"""
|
|
149
|
+
with tempfile.TemporaryDirectory() as tmp_dir:
|
|
150
|
+
wout_path = pathlib.Path(tmp_dir) / "wout.nc"
|
|
151
|
+
file_exporter.to_vmec2000_wout_file(vmecpp_wout, wout_path)
|
|
152
|
+
equilibrium = desc_vmec.VMECIO.load(wout_path, profile="current")
|
|
153
|
+
return from_desc_equilibrium(equilibrium)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def solve_desc_equilibrium(
|
|
157
|
+
equilibrium: DescEquilibrium,
|
|
158
|
+
verbose: int = 0,
|
|
159
|
+
) -> DescEquilibriumSolution:
|
|
160
|
+
"""Solve a DESC equilibrium for force balance with fixed boundary and profiles."""
|
|
161
|
+
desc_object = to_desc_equilibrium(equilibrium)
|
|
162
|
+
solved, result = desc_object.solve(objective="force", verbose=verbose)
|
|
163
|
+
force_error = result["Objective values"]["Force error: "][0]
|
|
164
|
+
return DescEquilibriumSolution(
|
|
165
|
+
equilibrium=from_desc_equilibrium(solved),
|
|
166
|
+
normalized_mean_absolute_force_error=float(force_error["f_mean_norm"]),
|
|
167
|
+
normalized_max_absolute_force_error=float(force_error["f_max_norm"]),
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def to_desc_equilibrium(
|
|
172
|
+
equilibrium: DescEquilibrium,
|
|
173
|
+
) -> desc_equilibrium.Equilibrium:
|
|
174
|
+
"""Converts a `DescEquilibrium` to a `desc.equilibrium.Equilibrium`."""
|
|
175
|
+
desc_object = desc_equilibrium.Equilibrium(
|
|
176
|
+
R_lmn=equilibrium.r_lmn,
|
|
177
|
+
Z_lmn=equilibrium.z_lmn,
|
|
178
|
+
L_lmn=equilibrium.lambda_lmn,
|
|
179
|
+
L=equilibrium.radial_resolution,
|
|
180
|
+
M=equilibrium.poloidal_resolution,
|
|
181
|
+
N=equilibrium.toroidal_resolution,
|
|
182
|
+
spectral_indexing=equilibrium.spectral_indexing,
|
|
183
|
+
Psi=equilibrium.total_toroidal_flux,
|
|
184
|
+
NFP=equilibrium.n_field_periods,
|
|
185
|
+
sym=equilibrium.is_stellarator_symmetric,
|
|
186
|
+
# The coefficients come from a DESC equilibrium, so they are already
|
|
187
|
+
# right-handed.
|
|
188
|
+
check_orientation=False,
|
|
189
|
+
pressure=_to_desc_spline_profile(equilibrium.pressure_profile),
|
|
190
|
+
current=(
|
|
191
|
+
_to_desc_spline_profile(equilibrium.current_profile)
|
|
192
|
+
if equilibrium.current_profile is not None
|
|
193
|
+
else None
|
|
194
|
+
),
|
|
195
|
+
iota=(
|
|
196
|
+
_to_desc_spline_profile(equilibrium.iota_profile)
|
|
197
|
+
if equilibrium.iota_profile is not None
|
|
198
|
+
else None
|
|
199
|
+
),
|
|
200
|
+
)
|
|
201
|
+
# Without a `surface` argument the constructor installs a default boundary and
|
|
202
|
+
# axis; a fixed-boundary solve would then be constrained to the wrong boundary.
|
|
203
|
+
if equilibrium.r_axis is not None:
|
|
204
|
+
desc_object.Ra_n = equilibrium.r_axis
|
|
205
|
+
if equilibrium.z_axis is not None:
|
|
206
|
+
desc_object.Za_n = equilibrium.z_axis
|
|
207
|
+
if equilibrium.r_boundary_lmn is not None:
|
|
208
|
+
desc_object.Rb_lmn = equilibrium.r_boundary_lmn
|
|
209
|
+
if equilibrium.z_boundary_lmn is not None:
|
|
210
|
+
desc_object.Zb_lmn = equilibrium.z_boundary_lmn
|
|
211
|
+
return desc_object
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def from_desc_equilibrium(
|
|
215
|
+
desc_object: desc_equilibrium.Equilibrium,
|
|
216
|
+
) -> DescEquilibrium:
|
|
217
|
+
"""Converts a `desc.equilibrium.Equilibrium` to a `DescEquilibrium`.
|
|
218
|
+
|
|
219
|
+
Profiles that are not `SplineProfile`s are resampled onto one. If both a current
|
|
220
|
+
and a rotational transform profile are set, only the current is kept.
|
|
221
|
+
"""
|
|
222
|
+
if desc_object.pressure is None:
|
|
223
|
+
raise ValueError("The DESC equilibrium must have a pressure profile.")
|
|
224
|
+
current = (
|
|
225
|
+
_from_desc_profile(desc_object.current)
|
|
226
|
+
if desc_object.current is not None
|
|
227
|
+
else None
|
|
228
|
+
)
|
|
229
|
+
iota = (
|
|
230
|
+
_from_desc_profile(desc_object.iota)
|
|
231
|
+
if desc_object.iota is not None and current is None
|
|
232
|
+
else None
|
|
233
|
+
)
|
|
234
|
+
params = desc_object.params_dict
|
|
235
|
+
# DESC types this as optional, but an Equilibrium always has a basis indexing.
|
|
236
|
+
spectral_indexing = desc_object.spectral_indexing
|
|
237
|
+
assert spectral_indexing is not None
|
|
238
|
+
return DescEquilibrium(
|
|
239
|
+
r_lmn=np.asarray(desc_object.R_lmn),
|
|
240
|
+
z_lmn=np.asarray(desc_object.Z_lmn),
|
|
241
|
+
lambda_lmn=np.asarray(desc_object.L_lmn),
|
|
242
|
+
radial_resolution=desc_object.L,
|
|
243
|
+
poloidal_resolution=desc_object.M,
|
|
244
|
+
toroidal_resolution=desc_object.N,
|
|
245
|
+
spectral_indexing=spectral_indexing,
|
|
246
|
+
n_field_periods=desc_object.NFP,
|
|
247
|
+
is_stellarator_symmetric=bool(desc_object.sym),
|
|
248
|
+
total_toroidal_flux=float(desc_object.Psi),
|
|
249
|
+
pressure_profile=_from_desc_profile(desc_object.pressure),
|
|
250
|
+
current_profile=current,
|
|
251
|
+
iota_profile=iota,
|
|
252
|
+
r_axis=np.asarray(params["Ra_n"]),
|
|
253
|
+
z_axis=np.asarray(params["Za_n"]),
|
|
254
|
+
r_boundary_lmn=np.asarray(params["Rb_lmn"]),
|
|
255
|
+
z_boundary_lmn=np.asarray(params["Zb_lmn"]),
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def _to_desc_spline_profile(
|
|
260
|
+
profile: DescSplineProfile,
|
|
261
|
+
) -> desc_profiles.SplineProfile:
|
|
262
|
+
return desc_profiles.SplineProfile(
|
|
263
|
+
values=profile.params,
|
|
264
|
+
knots=profile.knots,
|
|
265
|
+
method=profile.method,
|
|
266
|
+
name=profile.name,
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def _from_desc_profile(profile: desc_profiles._Profile) -> DescSplineProfile:
|
|
271
|
+
if not isinstance(profile, desc_profiles.SplineProfile):
|
|
272
|
+
knots = np.linspace(0.0, 1.0, 20)
|
|
273
|
+
profile = desc_profiles.SplineProfile(
|
|
274
|
+
values=np.asarray(profile(knots)),
|
|
275
|
+
knots=knots,
|
|
276
|
+
method="cubic2",
|
|
277
|
+
name=profile.name,
|
|
278
|
+
)
|
|
279
|
+
return DescSplineProfile(
|
|
280
|
+
method=profile._method,
|
|
281
|
+
name=profile.name,
|
|
282
|
+
knots=np.asarray(profile.knots),
|
|
283
|
+
params=np.asarray(profile.params),
|
|
284
|
+
)
|
|
@@ -218,6 +218,13 @@ class VmecppWOut(vmecpp.VmecWOut):
|
|
|
218
218
|
assert "iota_half" not in data
|
|
219
219
|
assert "iotas" in data
|
|
220
220
|
|
|
221
|
+
# Old wouts have no current-density coefficients. vmecpp would default
|
|
222
|
+
# them to empty 1D arrays, which it can neither re-validate nor save.
|
|
223
|
+
# They are missing data, not zero, hence NaN.
|
|
224
|
+
for field in ("currumnc", "currvmnc"):
|
|
225
|
+
if field not in data:
|
|
226
|
+
data[field] = np.full((data["mnmax_nyq"], data["ns"]), np.nan)
|
|
227
|
+
|
|
221
228
|
return data
|
|
222
229
|
|
|
223
230
|
@property
|
|
@@ -300,7 +307,7 @@ def build_vmecpp_indata(
|
|
|
300
307
|
if not boundary.is_stellarator_symmetric:
|
|
301
308
|
raise NotImplementedError("Only stellarator symmetric surfaces are supported.")
|
|
302
309
|
|
|
303
|
-
indata = vmecpp.VmecInput
|
|
310
|
+
indata = vmecpp.VmecInput()
|
|
304
311
|
|
|
305
312
|
indata.lasym = not boundary.is_stellarator_symmetric
|
|
306
313
|
indata.nfp = boundary.n_field_periods
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{constellaration-0.3.0 → constellaration-0.4.0}/constellaration/data_generation/desc_optimization.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{constellaration-0.3.0 → constellaration-0.4.0}/constellaration/data_generation/vmec_optimization.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{constellaration-0.3.0 → constellaration-0.4.0}/constellaration/generative_model/mcmc_algo.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{constellaration-0.3.0 → constellaration-0.4.0}/constellaration/geometry/spectral_condensation.py
RENAMED
|
File without changes
|
{constellaration-0.3.0 → constellaration-0.4.0}/constellaration/geometry/surface_rz_fourier.py
RENAMED
|
File without changes
|
|
File without changes
|
{constellaration-0.3.0 → constellaration-0.4.0}/constellaration/geometry/surface_utils_desc.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{constellaration-0.3.0 → constellaration-0.4.0}/constellaration/mhd/ideal_mhd_parameters_utils.py
RENAMED
|
File without changes
|
|
File without changes
|
{constellaration-0.3.0 → constellaration-0.4.0}/constellaration/mhd/near_axis_configuration.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{constellaration-0.3.0 → constellaration-0.4.0}/constellaration/omnigeneity/omnigenity_field.py
RENAMED
|
File without changes
|
{constellaration-0.3.0 → constellaration-0.4.0}/constellaration/omnigeneity/omnigenity_field_desc.py
RENAMED
|
File without changes
|
{constellaration-0.3.0 → constellaration-0.4.0}/constellaration/omnigeneity/omnigenity_field_plot.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{constellaration-0.3.0 → constellaration-0.4.0}/constellaration/optimization/augmented_lagrangian.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{constellaration-0.3.0 → constellaration-0.4.0}/constellaration/utils/visualization_utils.py
RENAMED
|
File without changes
|
|
File without changes
|