drbx 2.0.0.dev0__py3-none-any.whl
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.
- drbx/__init__.py +20 -0
- drbx/__main__.py +5 -0
- drbx/cli.py +586 -0
- drbx/config/__init__.py +20 -0
- drbx/config/boutinp.py +556 -0
- drbx/config/model.py +34 -0
- drbx/config/normalization.py +66 -0
- drbx/data/atomic_rates/__init__.py +1 -0
- drbx/data/atomic_rates/iz_AMJUEL_H.x_2.1.5.json +213 -0
- drbx/data/atomic_rates/iz_AMJUEL_H.x_2.3.9a.json +213 -0
- drbx/data/atomic_rates/rec_AMJUEL_H.x_2.1.8.json +213 -0
- drbx/data/atomic_rates/rec_AMJUEL_H.x_2.3.13a.json +213 -0
- drbx/geometry/__init__.py +207 -0
- drbx/geometry/embedding.py +56 -0
- drbx/geometry/essos_import.py +1385 -0
- drbx/geometry/fci_geometry.py +4622 -0
- drbx/geometry/fci_maps.py +85 -0
- drbx/geometry/island_divertor.py +291 -0
- drbx/geometry/metric_tensor.py +99 -0
- drbx/geometry/open_slab.py +150 -0
- drbx/geometry/rotating_ellipse.py +253 -0
- drbx/geometry/shifted_torus.py +225 -0
- drbx/geometry/stellarator.py +287 -0
- drbx/geometry/vmec_extender_import.py +499 -0
- drbx/geometry/vmec_jax_import.py +306 -0
- drbx/linear/__init__.py +37 -0
- drbx/linear/dispersion.py +138 -0
- drbx/linear/eigen.py +91 -0
- drbx/native/__init__.py +224 -0
- drbx/native/array_backend.py +64 -0
- drbx/native/deck_runner.py +779 -0
- drbx/native/electromagnetic.py +250 -0
- drbx/native/expression.py +173 -0
- drbx/native/fci.py +295 -0
- drbx/native/fci_2_field_rhs.py +182 -0
- drbx/native/fci_4_field_rhs.py +1267 -0
- drbx/native/fci_boundaries.py +2494 -0
- drbx/native/fci_differentiable_case.py +304 -0
- drbx/native/fci_drb_EB_rhs.py +1243 -0
- drbx/native/fci_drb_rhs.py +190 -0
- drbx/native/fci_halo.py +1575 -0
- drbx/native/fci_helpers.py +350 -0
- drbx/native/fci_model.py +294 -0
- drbx/native/fci_neutral.py +139 -0
- drbx/native/fci_operators.py +4081 -0
- drbx/native/fci_sharding.py +597 -0
- drbx/native/fci_sheath_recycling.py +206 -0
- drbx/native/fci_time_integrator.py +96 -0
- drbx/native/fci_vorticity.py +198 -0
- drbx/native/fluid_1d.py +330 -0
- drbx/native/hasegawa_wakatani.py +196 -0
- drbx/native/limiters.py +57 -0
- drbx/native/mesh.py +238 -0
- drbx/native/metrics.py +234 -0
- drbx/native/neutrals/__init__.py +58 -0
- drbx/native/neutrals/atomic_rates.py +134 -0
- drbx/native/neutrals/detachment_sol_model.py +221 -0
- drbx/native/neutrals/reactions.py +164 -0
- drbx/native/neutrals/recycling_sol_model.py +197 -0
- drbx/native/sol_flux_tube.py +133 -0
- drbx/native/stellarator_turbulence.py +343 -0
- drbx/native/transport.py +134 -0
- drbx/native/units.py +32 -0
- drbx/native/vorticity.py +252 -0
- drbx/runtime/__init__.py +53 -0
- drbx/runtime/artifacts.py +161 -0
- drbx/runtime/memory.py +144 -0
- drbx/runtime/output.py +374 -0
- drbx/runtime/paths.py +9 -0
- drbx/runtime/performance.py +161 -0
- drbx/runtime/run_config.py +184 -0
- drbx/runtime/scheduler.py +99 -0
- drbx/runtime/state.py +40 -0
- drbx/validation/__init__.py +424 -0
- drbx/validation/autodiff_diffusion.py +329 -0
- drbx/validation/autodiff_diffusion_uncertainty.py +235 -0
- drbx/validation/diverted_tokamak_movie.py +558 -0
- drbx/validation/essos_fieldline_import_campaign.py +181 -0
- drbx/validation/essos_imported_artifact_audit.py +535 -0
- drbx/validation/essos_imported_drb_movie_campaign.py +2826 -0
- drbx/validation/essos_imported_fci_campaign.py +5241 -0
- drbx/validation/essos_imported_pytree_campaign.py +406 -0
- drbx/validation/essos_vmec_closed_field_campaign.py +314 -0
- drbx/validation/essos_vmec_closed_field_transient_campaign.py +629 -0
- drbx/validation/essos_vmec_fieldline_surface_campaign.py +620 -0
- drbx/validation/fluid_1d_mms_convergence.py +250 -0
- drbx/validation/geometry_lineouts.py +136 -0
- drbx/validation/geometry_slices.py +178 -0
- drbx/validation/publication_plotting.py +91 -0
- drbx/validation/stellarator_drb_pytree_campaign.py +621 -0
- drbx/validation/stellarator_fci_geometry_campaign.py +200 -0
- drbx/validation/stellarator_fci_operator_campaign.py +304 -0
- drbx/validation/stellarator_fci_suite_campaign.py +264 -0
- drbx/validation/stellarator_metric_mms_campaign.py +289 -0
- drbx/validation/stellarator_neutral_physics_campaign.py +255 -0
- drbx/validation/stellarator_sheath_recycling_campaign.py +331 -0
- drbx/validation/stellarator_sol_showcase.py +628 -0
- drbx/validation/stellarator_vorticity_campaign.py +304 -0
- drbx/validation/vmec_extender_edge_field_campaign.py +260 -0
- drbx/validation/vmec_extender_sol_smoke_campaign.py +365 -0
- drbx-2.0.0.dev0.dist-info/METADATA +380 -0
- drbx-2.0.0.dev0.dist-info/RECORD +106 -0
- drbx-2.0.0.dev0.dist-info/WHEEL +5 -0
- drbx-2.0.0.dev0.dist-info/entry_points.txt +2 -0
- drbx-2.0.0.dev0.dist-info/licenses/LICENSE +21 -0
- drbx-2.0.0.dev0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Any, Mapping
|
|
5
|
+
|
|
6
|
+
from ..config.boutinp import BoutConfig, NumericResolver, ROOT_SECTION
|
|
7
|
+
from ..config.model import has_model_section, locate_model_section
|
|
8
|
+
from ..config.normalization import ModelNormalization
|
|
9
|
+
from .scheduler import ComponentRequest, expand_component_requests
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass(frozen=True)
|
|
13
|
+
class TimeConfig:
|
|
14
|
+
nout: int
|
|
15
|
+
timestep: float
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass(frozen=True)
|
|
19
|
+
class ParallelTransformConfig:
|
|
20
|
+
type: str
|
|
21
|
+
options: Mapping[str, bool | int | float | str | tuple[str, ...]]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass(frozen=True)
|
|
25
|
+
class MeshScalarConfig:
|
|
26
|
+
nx: int | None
|
|
27
|
+
ny: int | None
|
|
28
|
+
nz: int | None
|
|
29
|
+
mxg: int
|
|
30
|
+
myg: int
|
|
31
|
+
mz: int | None
|
|
32
|
+
zperiod: float | None
|
|
33
|
+
file: str | None
|
|
34
|
+
extrapolate_y: bool | None
|
|
35
|
+
parallel_transform: ParallelTransformConfig
|
|
36
|
+
resolved_scalars: Mapping[str, float]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass(frozen=True)
|
|
40
|
+
class SolverConfig:
|
|
41
|
+
type: str | None
|
|
42
|
+
mxstep: int | None
|
|
43
|
+
rtol: float | None
|
|
44
|
+
atol: float | None
|
|
45
|
+
use_precon: bool | None
|
|
46
|
+
cvode_max_order: int | None
|
|
47
|
+
mms: bool | None
|
|
48
|
+
raw_options: Mapping[str, bool | int | float | str | tuple[str, ...]]
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@dataclass(frozen=True)
|
|
52
|
+
class RunConfiguration:
|
|
53
|
+
time: TimeConfig
|
|
54
|
+
mesh: MeshScalarConfig
|
|
55
|
+
solver: SolverConfig
|
|
56
|
+
normalization: ModelNormalization | None
|
|
57
|
+
components: tuple[ComponentRequest, ...]
|
|
58
|
+
root_scalars: Mapping[str, float]
|
|
59
|
+
model_scalars: Mapping[str, float]
|
|
60
|
+
|
|
61
|
+
@classmethod
|
|
62
|
+
def from_config(cls, config: BoutConfig) -> "RunConfiguration":
|
|
63
|
+
resolver = NumericResolver(config)
|
|
64
|
+
time = TimeConfig(
|
|
65
|
+
nout=int(round(_resolve_required_scalar(config, resolver, ROOT_SECTION, "nout"))),
|
|
66
|
+
timestep=_resolve_required_scalar(config, resolver, ROOT_SECTION, "timestep"),
|
|
67
|
+
)
|
|
68
|
+
mesh = _build_mesh_config(config, resolver)
|
|
69
|
+
solver = _build_solver_config(config, resolver)
|
|
70
|
+
normalization = ModelNormalization.from_config(config) if _has_normalization(config) else None
|
|
71
|
+
model_section = locate_model_section(config) if has_model_section(config) else None
|
|
72
|
+
return cls(
|
|
73
|
+
time=time,
|
|
74
|
+
mesh=mesh,
|
|
75
|
+
solver=solver,
|
|
76
|
+
normalization=normalization,
|
|
77
|
+
components=expand_component_requests(config) if model_section is not None else (),
|
|
78
|
+
root_scalars=_resolved_scalars(config, resolver, ROOT_SECTION),
|
|
79
|
+
model_scalars=_resolved_scalars(config, resolver, model_section) if model_section is not None else {},
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _build_mesh_config(config: BoutConfig, resolver: NumericResolver) -> MeshScalarConfig:
|
|
84
|
+
mesh_scalars = _resolved_scalars(config, resolver, "mesh") if config.has_section("mesh") else {}
|
|
85
|
+
parallel_section = "mesh:paralleltransform"
|
|
86
|
+
parallel_options = dict(_resolved_parsed_options(config, parallel_section)) if config.has_section(parallel_section) else {}
|
|
87
|
+
parallel_type = str(parallel_options.get("type", "identity"))
|
|
88
|
+
file_value = config.parsed("mesh", "file") if config.has_option("mesh", "file") else None
|
|
89
|
+
extrapolate_y = bool(config.parsed("mesh", "extrapolate_y")) if config.has_option("mesh", "extrapolate_y") else None
|
|
90
|
+
return MeshScalarConfig(
|
|
91
|
+
nx=_optional_int(mesh_scalars.get("nx")),
|
|
92
|
+
ny=_optional_int(mesh_scalars.get("ny")),
|
|
93
|
+
nz=_optional_int(mesh_scalars.get("nz")),
|
|
94
|
+
mxg=_with_default(_optional_int(_optional_root_scalar(config, resolver, "MXG")), 2),
|
|
95
|
+
myg=_with_default(_optional_int(_optional_root_scalar(config, resolver, "MYG")), 2),
|
|
96
|
+
mz=_optional_int(_optional_root_scalar(config, resolver, "MZ")),
|
|
97
|
+
zperiod=_optional_root_scalar(config, resolver, "zperiod"),
|
|
98
|
+
file=file_value if isinstance(file_value, str) else None,
|
|
99
|
+
extrapolate_y=extrapolate_y,
|
|
100
|
+
parallel_transform=ParallelTransformConfig(type=parallel_type, options=parallel_options),
|
|
101
|
+
resolved_scalars=mesh_scalars,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _build_solver_config(config: BoutConfig, resolver: NumericResolver) -> SolverConfig:
|
|
106
|
+
raw_options = dict(_resolved_parsed_options(config, "solver")) if config.has_section("solver") else {}
|
|
107
|
+
return SolverConfig(
|
|
108
|
+
type=str(raw_options["type"]) if "type" in raw_options else None,
|
|
109
|
+
mxstep=_optional_int(raw_options.get("mxstep")),
|
|
110
|
+
rtol=_optional_float(raw_options.get("rtol")),
|
|
111
|
+
atol=_optional_float(raw_options.get("atol")),
|
|
112
|
+
use_precon=_optional_bool(raw_options.get("use_precon")),
|
|
113
|
+
cvode_max_order=_optional_int(raw_options.get("cvode_max_order")),
|
|
114
|
+
mms=_optional_bool(raw_options.get("mms")),
|
|
115
|
+
raw_options={**raw_options, **_resolved_scalars(config, resolver, "solver")} if config.has_section("solver") else {},
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _resolved_parsed_options(config: BoutConfig, section: str) -> Mapping[str, bool | int | float | str | tuple[str, ...]]:
|
|
120
|
+
if not config.has_section(section):
|
|
121
|
+
return {}
|
|
122
|
+
options: dict[str, bool | int | float | str | tuple[str, ...]] = {}
|
|
123
|
+
for key, entry in config.section(section).items():
|
|
124
|
+
options[key] = entry.value.raw if entry.value.kind == "expression" else entry.value.parsed
|
|
125
|
+
return options
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _resolved_scalars(config: BoutConfig, resolver: NumericResolver, section: str) -> Mapping[str, float]:
|
|
129
|
+
if not config.has_section(section):
|
|
130
|
+
return {}
|
|
131
|
+
scalars: dict[str, float] = {}
|
|
132
|
+
for key, entry in config.section(section).items():
|
|
133
|
+
if entry.value.kind in {"string", "list"}:
|
|
134
|
+
continue
|
|
135
|
+
try:
|
|
136
|
+
scalars[key] = resolver.resolve(section, key)
|
|
137
|
+
except (KeyError, TypeError, ValueError, SyntaxError):
|
|
138
|
+
continue
|
|
139
|
+
return scalars
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _resolve_required_scalar(config: BoutConfig, resolver: NumericResolver, section: str, key: str) -> float:
|
|
143
|
+
if not config.has_option(section, key):
|
|
144
|
+
raise KeyError(f"Missing required scalar option {section}:{key}")
|
|
145
|
+
return resolver.resolve(section, key)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _optional_root_scalar(config: BoutConfig, resolver: NumericResolver, key: str) -> float | None:
|
|
149
|
+
if not config.has_option(ROOT_SECTION, key):
|
|
150
|
+
return None
|
|
151
|
+
return resolver.resolve(ROOT_SECTION, key)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _optional_int(value: Any) -> int | None:
|
|
155
|
+
if value is None:
|
|
156
|
+
return None
|
|
157
|
+
return int(round(float(value)))
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _optional_float(value: Any) -> float | None:
|
|
161
|
+
if value is None:
|
|
162
|
+
return None
|
|
163
|
+
return float(value)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def _optional_bool(value: Any) -> bool | None:
|
|
167
|
+
if value is None:
|
|
168
|
+
return None
|
|
169
|
+
if isinstance(value, bool):
|
|
170
|
+
return value
|
|
171
|
+
return bool(value)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def _with_default(value: Any, default: Any) -> Any:
|
|
175
|
+
if value is None:
|
|
176
|
+
return default
|
|
177
|
+
return value
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def _has_normalization(config: BoutConfig) -> bool:
|
|
181
|
+
if not has_model_section(config):
|
|
182
|
+
return False
|
|
183
|
+
model_section = locate_model_section(config)
|
|
184
|
+
return all(config.has_option(model_section, key) for key in ("Nnorm", "Tnorm", "Bnorm"))
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
import re
|
|
5
|
+
from typing import Any, Protocol
|
|
6
|
+
|
|
7
|
+
from ..config.boutinp import BoutConfig
|
|
8
|
+
from ..config.model import locate_model_section
|
|
9
|
+
|
|
10
|
+
_COMPONENT_TYPE_PATTERN = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass(frozen=True)
|
|
14
|
+
class ComponentRequest:
|
|
15
|
+
label: str
|
|
16
|
+
section: str
|
|
17
|
+
implementation: str
|
|
18
|
+
source_component: str
|
|
19
|
+
typed: bool
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class SupportsSchedulerHooks(Protocol):
|
|
23
|
+
name: str
|
|
24
|
+
|
|
25
|
+
def transform(self, state: Any, time: float) -> Any:
|
|
26
|
+
...
|
|
27
|
+
|
|
28
|
+
def finalize(self, state: Any, time: float) -> Any:
|
|
29
|
+
...
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass(frozen=True)
|
|
33
|
+
class Scheduler:
|
|
34
|
+
components: tuple[SupportsSchedulerHooks, ...]
|
|
35
|
+
|
|
36
|
+
def execute_cycle(self, state: Any, time: float) -> Any:
|
|
37
|
+
current = state
|
|
38
|
+
for component in self.components:
|
|
39
|
+
current = component.transform(current, time)
|
|
40
|
+
for component in self.components:
|
|
41
|
+
current = component.finalize(current, time)
|
|
42
|
+
return current
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def expand_component_requests(config: BoutConfig) -> tuple[ComponentRequest, ...]:
|
|
46
|
+
model_section = locate_model_section(config)
|
|
47
|
+
component_value = config.get(model_section, "components")
|
|
48
|
+
component_names = _as_tuple(component_value.parsed)
|
|
49
|
+
requests: list[ComponentRequest] = []
|
|
50
|
+
|
|
51
|
+
for name in component_names:
|
|
52
|
+
if config.has_section(name) and config.has_option(name, "type") and _is_species_component(config, name):
|
|
53
|
+
implementations = list(_as_tuple(config.parsed(name, "type")))
|
|
54
|
+
if _has_anomalous_diffusion_component(config, name) and "anomalous_diffusion" not in implementations:
|
|
55
|
+
implementations.append("anomalous_diffusion")
|
|
56
|
+
for implementation in implementations:
|
|
57
|
+
requests.append(
|
|
58
|
+
ComponentRequest(
|
|
59
|
+
label=f"{name}:{implementation}",
|
|
60
|
+
section=name,
|
|
61
|
+
implementation=implementation,
|
|
62
|
+
source_component=name,
|
|
63
|
+
typed=True,
|
|
64
|
+
)
|
|
65
|
+
)
|
|
66
|
+
continue
|
|
67
|
+
|
|
68
|
+
requests.append(
|
|
69
|
+
ComponentRequest(
|
|
70
|
+
label=name,
|
|
71
|
+
section=name,
|
|
72
|
+
implementation=name,
|
|
73
|
+
source_component=name,
|
|
74
|
+
typed=False,
|
|
75
|
+
)
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
return tuple(requests)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _as_tuple(value: bool | int | float | str | tuple[str, ...]) -> tuple[str, ...]:
|
|
82
|
+
if isinstance(value, tuple):
|
|
83
|
+
return value
|
|
84
|
+
return (str(value),)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _is_species_component(config: BoutConfig, section: str) -> bool:
|
|
88
|
+
type_value = config.parsed(section, "type")
|
|
89
|
+
return all(_COMPONENT_TYPE_PATTERN.fullmatch(item) for item in _as_tuple(type_value))
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _has_anomalous_diffusion_component(config: BoutConfig, section: str) -> bool:
|
|
93
|
+
for option_name in ("anomalous_D", "anomalous_chi", "anomalous_nu"):
|
|
94
|
+
if not config.has_option(section, option_name):
|
|
95
|
+
continue
|
|
96
|
+
raw_value = str(config.get(section, option_name).raw).strip()
|
|
97
|
+
if raw_value not in {"0", "0.0", "0.", "false", "False"}:
|
|
98
|
+
return True
|
|
99
|
+
return False
|
drbx/runtime/state.py
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from typing import Any, Mapping
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass(frozen=True)
|
|
8
|
+
class SimulationState:
|
|
9
|
+
time: float = 0.0
|
|
10
|
+
fields: Mapping[str, Any] = field(default_factory=dict)
|
|
11
|
+
diagnostics: Mapping[str, Any] = field(default_factory=dict)
|
|
12
|
+
metadata: Mapping[str, Any] = field(default_factory=dict)
|
|
13
|
+
|
|
14
|
+
def with_field(self, name: str, value: Any) -> "SimulationState":
|
|
15
|
+
updated_fields = dict(self.fields)
|
|
16
|
+
updated_fields[name] = value
|
|
17
|
+
return SimulationState(
|
|
18
|
+
time=self.time,
|
|
19
|
+
fields=updated_fields,
|
|
20
|
+
diagnostics=self.diagnostics,
|
|
21
|
+
metadata=self.metadata,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
def with_diagnostic(self, name: str, value: Any) -> "SimulationState":
|
|
25
|
+
updated_diagnostics = dict(self.diagnostics)
|
|
26
|
+
updated_diagnostics[name] = value
|
|
27
|
+
return SimulationState(
|
|
28
|
+
time=self.time,
|
|
29
|
+
fields=self.fields,
|
|
30
|
+
diagnostics=updated_diagnostics,
|
|
31
|
+
metadata=self.metadata,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
def advance_time(self, dt: float) -> "SimulationState":
|
|
35
|
+
return SimulationState(
|
|
36
|
+
time=self.time + dt,
|
|
37
|
+
fields=self.fields,
|
|
38
|
+
diagnostics=self.diagnostics,
|
|
39
|
+
metadata=self.metadata,
|
|
40
|
+
)
|