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.
Files changed (106) hide show
  1. drbx/__init__.py +20 -0
  2. drbx/__main__.py +5 -0
  3. drbx/cli.py +586 -0
  4. drbx/config/__init__.py +20 -0
  5. drbx/config/boutinp.py +556 -0
  6. drbx/config/model.py +34 -0
  7. drbx/config/normalization.py +66 -0
  8. drbx/data/atomic_rates/__init__.py +1 -0
  9. drbx/data/atomic_rates/iz_AMJUEL_H.x_2.1.5.json +213 -0
  10. drbx/data/atomic_rates/iz_AMJUEL_H.x_2.3.9a.json +213 -0
  11. drbx/data/atomic_rates/rec_AMJUEL_H.x_2.1.8.json +213 -0
  12. drbx/data/atomic_rates/rec_AMJUEL_H.x_2.3.13a.json +213 -0
  13. drbx/geometry/__init__.py +207 -0
  14. drbx/geometry/embedding.py +56 -0
  15. drbx/geometry/essos_import.py +1385 -0
  16. drbx/geometry/fci_geometry.py +4622 -0
  17. drbx/geometry/fci_maps.py +85 -0
  18. drbx/geometry/island_divertor.py +291 -0
  19. drbx/geometry/metric_tensor.py +99 -0
  20. drbx/geometry/open_slab.py +150 -0
  21. drbx/geometry/rotating_ellipse.py +253 -0
  22. drbx/geometry/shifted_torus.py +225 -0
  23. drbx/geometry/stellarator.py +287 -0
  24. drbx/geometry/vmec_extender_import.py +499 -0
  25. drbx/geometry/vmec_jax_import.py +306 -0
  26. drbx/linear/__init__.py +37 -0
  27. drbx/linear/dispersion.py +138 -0
  28. drbx/linear/eigen.py +91 -0
  29. drbx/native/__init__.py +224 -0
  30. drbx/native/array_backend.py +64 -0
  31. drbx/native/deck_runner.py +779 -0
  32. drbx/native/electromagnetic.py +250 -0
  33. drbx/native/expression.py +173 -0
  34. drbx/native/fci.py +295 -0
  35. drbx/native/fci_2_field_rhs.py +182 -0
  36. drbx/native/fci_4_field_rhs.py +1267 -0
  37. drbx/native/fci_boundaries.py +2494 -0
  38. drbx/native/fci_differentiable_case.py +304 -0
  39. drbx/native/fci_drb_EB_rhs.py +1243 -0
  40. drbx/native/fci_drb_rhs.py +190 -0
  41. drbx/native/fci_halo.py +1575 -0
  42. drbx/native/fci_helpers.py +350 -0
  43. drbx/native/fci_model.py +294 -0
  44. drbx/native/fci_neutral.py +139 -0
  45. drbx/native/fci_operators.py +4081 -0
  46. drbx/native/fci_sharding.py +597 -0
  47. drbx/native/fci_sheath_recycling.py +206 -0
  48. drbx/native/fci_time_integrator.py +96 -0
  49. drbx/native/fci_vorticity.py +198 -0
  50. drbx/native/fluid_1d.py +330 -0
  51. drbx/native/hasegawa_wakatani.py +196 -0
  52. drbx/native/limiters.py +57 -0
  53. drbx/native/mesh.py +238 -0
  54. drbx/native/metrics.py +234 -0
  55. drbx/native/neutrals/__init__.py +58 -0
  56. drbx/native/neutrals/atomic_rates.py +134 -0
  57. drbx/native/neutrals/detachment_sol_model.py +221 -0
  58. drbx/native/neutrals/reactions.py +164 -0
  59. drbx/native/neutrals/recycling_sol_model.py +197 -0
  60. drbx/native/sol_flux_tube.py +133 -0
  61. drbx/native/stellarator_turbulence.py +343 -0
  62. drbx/native/transport.py +134 -0
  63. drbx/native/units.py +32 -0
  64. drbx/native/vorticity.py +252 -0
  65. drbx/runtime/__init__.py +53 -0
  66. drbx/runtime/artifacts.py +161 -0
  67. drbx/runtime/memory.py +144 -0
  68. drbx/runtime/output.py +374 -0
  69. drbx/runtime/paths.py +9 -0
  70. drbx/runtime/performance.py +161 -0
  71. drbx/runtime/run_config.py +184 -0
  72. drbx/runtime/scheduler.py +99 -0
  73. drbx/runtime/state.py +40 -0
  74. drbx/validation/__init__.py +424 -0
  75. drbx/validation/autodiff_diffusion.py +329 -0
  76. drbx/validation/autodiff_diffusion_uncertainty.py +235 -0
  77. drbx/validation/diverted_tokamak_movie.py +558 -0
  78. drbx/validation/essos_fieldline_import_campaign.py +181 -0
  79. drbx/validation/essos_imported_artifact_audit.py +535 -0
  80. drbx/validation/essos_imported_drb_movie_campaign.py +2826 -0
  81. drbx/validation/essos_imported_fci_campaign.py +5241 -0
  82. drbx/validation/essos_imported_pytree_campaign.py +406 -0
  83. drbx/validation/essos_vmec_closed_field_campaign.py +314 -0
  84. drbx/validation/essos_vmec_closed_field_transient_campaign.py +629 -0
  85. drbx/validation/essos_vmec_fieldline_surface_campaign.py +620 -0
  86. drbx/validation/fluid_1d_mms_convergence.py +250 -0
  87. drbx/validation/geometry_lineouts.py +136 -0
  88. drbx/validation/geometry_slices.py +178 -0
  89. drbx/validation/publication_plotting.py +91 -0
  90. drbx/validation/stellarator_drb_pytree_campaign.py +621 -0
  91. drbx/validation/stellarator_fci_geometry_campaign.py +200 -0
  92. drbx/validation/stellarator_fci_operator_campaign.py +304 -0
  93. drbx/validation/stellarator_fci_suite_campaign.py +264 -0
  94. drbx/validation/stellarator_metric_mms_campaign.py +289 -0
  95. drbx/validation/stellarator_neutral_physics_campaign.py +255 -0
  96. drbx/validation/stellarator_sheath_recycling_campaign.py +331 -0
  97. drbx/validation/stellarator_sol_showcase.py +628 -0
  98. drbx/validation/stellarator_vorticity_campaign.py +304 -0
  99. drbx/validation/vmec_extender_edge_field_campaign.py +260 -0
  100. drbx/validation/vmec_extender_sol_smoke_campaign.py +365 -0
  101. drbx-2.0.0.dev0.dist-info/METADATA +380 -0
  102. drbx-2.0.0.dev0.dist-info/RECORD +106 -0
  103. drbx-2.0.0.dev0.dist-info/WHEEL +5 -0
  104. drbx-2.0.0.dev0.dist-info/entry_points.txt +2 -0
  105. drbx-2.0.0.dev0.dist-info/licenses/LICENSE +21 -0
  106. drbx-2.0.0.dev0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,304 @@
1
+ """Differentiable drift-reduced two-field FCI rollout on the shifted torus.
2
+
3
+ Reusable pieces behind the Phase 6 non-axisymmetric differentiability flagship
4
+ (``examples/stellarator/fci_differentiable.py``) and its gate
5
+ (``tests/test_fci_differentiable.py``):
6
+
7
+ - :func:`build_context` -- shifted-torus geometry plus the fixed-wall FCI
8
+ operator scaffold, bundled as a :class:`DemoContext`;
9
+ - :func:`seeded_initial_state` -- smooth multi-mode seed whose fluctuation
10
+ vanishes at the radial walls; the amplitude is the differentiation knob;
11
+ - :func:`single_rhs` -- one drift-reduced two-field FCI RHS evaluation;
12
+ - :func:`rollout` / :func:`evolved_density_variance` -- the differentiable
13
+ multi-step RK4 rollout (via ``jax.lax.scan``) and the scalar objective;
14
+ - :func:`differentiability_report` -- ``jax.grad`` of the evolved density
15
+ variance vs a central finite difference, with finiteness diagnostics;
16
+ - :func:`single_rhs_grad_and_fd` -- the cheaper single-RHS gradient witness.
17
+
18
+ Everything is pure JAX and ``jit``/``grad``-transparent; no files are written
19
+ here. Plotting and JSON summaries live in the example script.
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ from functools import partial
25
+ from typing import NamedTuple
26
+
27
+ import jax
28
+ import jax.numpy as jnp
29
+ import numpy as np
30
+
31
+ from ..geometry import (
32
+ RegularFaceGeometry3D,
33
+ build_curvature_coefficients,
34
+ build_shifted_torus_geometry,
35
+ logical_grid_from_axis_vectors,
36
+ )
37
+ from .fci_2_field_rhs import (
38
+ Fci2FieldRhsParameters,
39
+ Fci2FieldState,
40
+ compute_2field_rhs,
41
+ )
42
+ from .fci_boundaries import (
43
+ BC_DIRICHLET,
44
+ BoundaryFaceBC3D,
45
+ CutWallBC3D,
46
+ CutWallGeometry3D,
47
+ )
48
+ from .fci_time_integrator import rk4_step
49
+
50
+ __all__ = [
51
+ "DemoContext",
52
+ "build_context",
53
+ "density_variance",
54
+ "differentiability_report",
55
+ "evolved_density_variance",
56
+ "rollout",
57
+ "seeded_initial_state",
58
+ "single_rhs",
59
+ "single_rhs_grad_and_fd",
60
+ ]
61
+
62
+
63
+ class DemoContext(NamedTuple):
64
+ """Everything the differentiable rollout needs, built once per resolution."""
65
+
66
+ geometry: object
67
+ curvature_coefficients: jax.Array
68
+ density_face_bc: BoundaryFaceBC3D
69
+ phi_face_bc: BoundaryFaceBC3D
70
+ v_parallel_face_bc: BoundaryFaceBC3D
71
+ parameters: Fci2FieldRhsParameters
72
+ x_min: float
73
+ x_max: float
74
+
75
+
76
+ def _fixed_radial_dirichlet_face_bc(geometry, boundary_value: float) -> BoundaryFaceBC3D:
77
+ """A fixed Dirichlet face BC at the two radial (x) walls; periodic elsewhere.
78
+
79
+ ``boundary_value`` is held constant in time, so this is a simple fixed wall,
80
+ not an exact-solution boundary. For the density the wall value is the
81
+ background (1.0); for the fluctuation quantities phi and v_parallel it is 0
82
+ (zero-Dirichlet on the fluctuation).
83
+ """
84
+
85
+ face = RegularFaceGeometry3D.unit(geometry)
86
+ return BoundaryFaceBC3D(
87
+ kind_x=jnp.zeros_like(face.x_area, dtype=jnp.int32).at[0].set(BC_DIRICHLET).at[-1].set(BC_DIRICHLET),
88
+ kind_y=jnp.zeros_like(face.y_area, dtype=jnp.int32),
89
+ kind_z=jnp.zeros_like(face.z_area, dtype=jnp.int32),
90
+ value_x=jnp.full_like(face.x_area, 0.0).at[0].set(boundary_value).at[-1].set(boundary_value),
91
+ value_y=jnp.zeros_like(face.y_area, dtype=jnp.float64),
92
+ value_z=jnp.zeros_like(face.z_area, dtype=jnp.float64),
93
+ mask_x=jnp.zeros_like(face.x_open_mask, dtype=bool).at[0].set(True).at[-1].set(True),
94
+ mask_y=jnp.zeros_like(face.y_open_mask, dtype=bool),
95
+ mask_z=jnp.zeros_like(face.z_open_mask, dtype=bool),
96
+ )
97
+
98
+
99
+ def build_context(
100
+ shape: tuple[int, int, int] = (16, 16, 8),
101
+ *,
102
+ sigma: float = 0.6,
103
+ rho_star: float = 1.0,
104
+ x_min: float = 0.15,
105
+ x_max: float = 1.0,
106
+ r0: float = 3.0,
107
+ alpha_value: float = 0.25,
108
+ iota: float = 1.1,
109
+ c_phi: float = 3.0,
110
+ ) -> DemoContext:
111
+ """Build the shifted-torus geometry and the fixed-wall FCI operator scaffold."""
112
+
113
+ geometry = build_shifted_torus_geometry(
114
+ shape,
115
+ x_min=x_min,
116
+ x_max=x_max,
117
+ r0=r0,
118
+ alpha_value=alpha_value,
119
+ iota=iota,
120
+ c_phi=c_phi,
121
+ sigma=sigma,
122
+ construct_fci_maps=False,
123
+ )
124
+ curvature_coefficients = build_curvature_coefficients(geometry, periodic_axes=(False, True, True))
125
+ return DemoContext(
126
+ geometry=geometry,
127
+ curvature_coefficients=curvature_coefficients,
128
+ density_face_bc=_fixed_radial_dirichlet_face_bc(geometry, 1.0),
129
+ phi_face_bc=_fixed_radial_dirichlet_face_bc(geometry, 0.0),
130
+ v_parallel_face_bc=_fixed_radial_dirichlet_face_bc(geometry, 0.0),
131
+ parameters=Fci2FieldRhsParameters(rho_star=rho_star),
132
+ x_min=float(x_min),
133
+ x_max=float(x_max),
134
+ )
135
+
136
+
137
+ def seeded_initial_state(
138
+ ctx: DemoContext,
139
+ amp: jax.Array,
140
+ *,
141
+ m1: int = 2,
142
+ n1: int = 1,
143
+ m2: int = 3,
144
+ n2: int = 2,
145
+ ) -> Fci2FieldState:
146
+ """Smooth seeded initial state whose fluctuation vanishes at the radial walls.
147
+
148
+ ``density = background * exp(amp * perturbation)`` keeps the density strictly
149
+ positive (the model derives ``phi = log(density / background)`` internally),
150
+ and ``perturbation`` carries a radial envelope ``sin(pi * x_norm)`` so the
151
+ fluctuation is zero at both radial boundaries, consistent with the fixed
152
+ zero-Dirichlet fluctuation wall. ``(m1, n1)`` and ``(m2, n2)`` are the seeded
153
+ (poloidal, toroidal) mode numbers.
154
+ """
155
+
156
+ logical_grid = logical_grid_from_axis_vectors(*ctx.geometry.grid.logical_axis_vectors)
157
+ x = logical_grid[..., 0]
158
+ theta = logical_grid[..., 1]
159
+ zeta = logical_grid[..., 2]
160
+ x_norm = (x - ctx.x_min) / (ctx.x_max - ctx.x_min)
161
+ envelope = jnp.sin(jnp.pi * x_norm)
162
+ perturbation = envelope * (
163
+ jnp.cos(m1 * theta) * jnp.sin(n1 * zeta)
164
+ + 0.5 * jnp.sin(m2 * theta) * jnp.cos(n2 * zeta)
165
+ )
166
+ background = jnp.ones(ctx.geometry.shape, dtype=jnp.float64)
167
+ density = background * jnp.exp(amp * perturbation)
168
+ v_parallel = amp * envelope * jnp.sin(m1 * theta) * jnp.cos(n1 * zeta)
169
+ return Fci2FieldState(density=density, v_parallel=v_parallel, density_background=background)
170
+
171
+
172
+ def _clamp_radial_boundaries(ctx: DemoContext, state: Fci2FieldState) -> Fci2FieldState:
173
+ """Hold the radial boundary rows at the fixed wall values (density=bg, v=0)."""
174
+
175
+ del ctx
176
+ density = state.density.at[0, :, :].set(1.0).at[-1, :, :].set(1.0)
177
+ v_parallel = state.v_parallel.at[0, :, :].set(0.0).at[-1, :, :].set(0.0)
178
+ return Fci2FieldState(
179
+ density=density,
180
+ v_parallel=v_parallel,
181
+ density_background=state.density_background,
182
+ )
183
+
184
+
185
+ def single_rhs(ctx: DemoContext, state: Fci2FieldState) -> Fci2FieldState:
186
+ """One drift-reduced two-field FCI RHS evaluation on the shifted torus."""
187
+
188
+ state = _clamp_radial_boundaries(ctx, state)
189
+ result, _timings = compute_2field_rhs(
190
+ state,
191
+ geometry=ctx.geometry,
192
+ parameters=ctx.parameters,
193
+ curvature_coefficients=ctx.curvature_coefficients,
194
+ periodic_axes=(False, True, True),
195
+ density_face_bc=ctx.density_face_bc,
196
+ phi_face_bc=ctx.phi_face_bc,
197
+ v_parallel_face_bc=ctx.v_parallel_face_bc,
198
+ density_cut_wall_geometry=CutWallGeometry3D.empty(),
199
+ density_cut_wall_bc=CutWallBC3D.empty(),
200
+ phi_cut_wall_geometry=CutWallGeometry3D.empty(),
201
+ phi_cut_wall_bc=CutWallBC3D.empty(),
202
+ v_parallel_cut_wall_geometry=CutWallGeometry3D.empty(),
203
+ v_parallel_cut_wall_bc=CutWallBC3D.empty(),
204
+ )
205
+ return result.rhs
206
+
207
+
208
+ def rollout(ctx: DemoContext, amp: jax.Array, *, n_steps: int, dt: float) -> Fci2FieldState:
209
+ """Advance the seeded free state ``n_steps`` RK4 steps (differentiable, via scan)."""
210
+
211
+ initial_state = _clamp_radial_boundaries(ctx, seeded_initial_state(ctx, amp))
212
+
213
+ def _rhs_fn(current_state, _stage_time, _carry):
214
+ return single_rhs(ctx, current_state), None, jnp.asarray(0.0)
215
+
216
+ def _body(state, _):
217
+ step = rk4_step(state, time=0.0, timestep=dt, rhs_fn=_rhs_fn, carry=None)
218
+ return _clamp_radial_boundaries(ctx, step.state), None
219
+
220
+ final_state, _ = jax.lax.scan(_body, initial_state, None, length=int(n_steps))
221
+ return final_state
222
+
223
+
224
+ def density_variance(state: Fci2FieldState) -> jax.Array:
225
+ """Scalar diagnostic: variance of the density over interior (non-wall) cells."""
226
+
227
+ interior = state.density[1:-1, :, :]
228
+ return jnp.mean((interior - jnp.mean(interior)) ** 2)
229
+
230
+
231
+ def evolved_density_variance(ctx: DemoContext, amp: jax.Array, *, n_steps: int, dt: float) -> jax.Array:
232
+ """The scalar objective we differentiate: variance of the evolved density."""
233
+
234
+ return density_variance(rollout(ctx, amp, n_steps=n_steps, dt=dt))
235
+
236
+
237
+ def differentiability_report(
238
+ ctx: DemoContext,
239
+ *,
240
+ amp0: float = 0.1,
241
+ n_steps: int = 24,
242
+ dt: float = 1.0e-3,
243
+ fd_step: float = 1.0e-5,
244
+ ) -> dict:
245
+ """Roll out the free run and compare autodiff grad to a central FD (wrt amp0).
246
+
247
+ Returns the evolved state and the gradient diagnostics. The objective is
248
+ JIT-compiled once so the finite-difference samples reuse the compiled rollout.
249
+ """
250
+
251
+ objective = partial(evolved_density_variance, ctx, n_steps=n_steps, dt=dt)
252
+ objective_jit = jax.jit(objective)
253
+ grad_jit = jax.jit(jax.grad(objective))
254
+
255
+ amp0 = float(amp0)
256
+ grad_value = float(grad_jit(amp0))
257
+ plus = float(objective_jit(amp0 + fd_step))
258
+ minus = float(objective_jit(amp0 - fd_step))
259
+ fd_value = (plus - minus) / (2.0 * fd_step)
260
+ rel_error = abs(grad_value - fd_value) / max(abs(fd_value), 1.0e-30)
261
+
262
+ final_state = rollout(ctx, amp0, n_steps=n_steps, dt=dt)
263
+ density = np.asarray(final_state.density, dtype=np.float64)
264
+ v_parallel = np.asarray(final_state.v_parallel, dtype=np.float64)
265
+ finite = bool(np.all(np.isfinite(density)) and np.all(np.isfinite(v_parallel)))
266
+
267
+ return {
268
+ "grad": grad_value,
269
+ "fd": fd_value,
270
+ "rel_error": rel_error,
271
+ "fd_step": float(fd_step),
272
+ "amp0": amp0,
273
+ "n_steps": int(n_steps),
274
+ "dt": float(dt),
275
+ "objective_value": float(objective_jit(amp0)),
276
+ "finite": finite,
277
+ "density_max": float(np.max(np.abs(density))),
278
+ "v_parallel_max": float(np.max(np.abs(v_parallel))),
279
+ "final_state": final_state,
280
+ }
281
+
282
+
283
+ def single_rhs_grad_and_fd(
284
+ ctx: DemoContext,
285
+ *,
286
+ amp0: float = 0.1,
287
+ fd_step: float = 1.0e-5,
288
+ ) -> dict:
289
+ """Secondary cheaper witness: differentiate a SINGLE RHS evaluation wrt amp.
290
+
291
+ A fast cross-check that the FCI RHS itself is differentiable, independent of
292
+ the RK4 rollout.
293
+ """
294
+
295
+ def objective(amp: jax.Array) -> jax.Array:
296
+ return density_variance(single_rhs(ctx, seeded_initial_state(ctx, amp)))
297
+
298
+ objective_jit = jax.jit(objective)
299
+ grad_value = float(jax.jit(jax.grad(objective))(float(amp0)))
300
+ plus = float(objective_jit(float(amp0) + fd_step))
301
+ minus = float(objective_jit(float(amp0) - fd_step))
302
+ fd_value = (plus - minus) / (2.0 * fd_step)
303
+ rel_error = abs(grad_value - fd_value) / max(abs(fd_value), 1.0e-30)
304
+ return {"grad": grad_value, "fd": fd_value, "rel_error": rel_error}