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,1243 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+
5
+ import jax
6
+ import jax.numpy as jnp
7
+
8
+ from ..geometry import (
9
+ ConservativeStencilBuilder,
10
+ FciGeometry3D,
11
+ LocalStencilBuilder,
12
+ build_conservative_stencil_from_field,
13
+ build_local_stencil_from_field,
14
+ )
15
+ from .fci_model import FciModelState
16
+ from .fci_boundaries import (
17
+ BoundaryFaceBC3D,
18
+ ConservativeStencil3D,
19
+ CutWallBC3D,
20
+ CutWallGeometry3D,
21
+ LocalStencil1D,
22
+ LocalStencil3D,
23
+ )
24
+ from .fci_operators import (
25
+ curvature_op,
26
+ grad_parallel_op_direct,
27
+ parallel_laplacian_direct_op,
28
+ perp_laplacian_conservative_op,
29
+ poisson_bracket_op,
30
+ )
31
+
32
+
33
+ @jax.tree_util.register_pytree_node_class
34
+ @dataclass(frozen=True)
35
+ class FciDrbEBState(FciModelState):
36
+ """State for the electrostatic Boussinesq drift-reduced Braginskii scaffold."""
37
+
38
+ density: jax.Array
39
+ phi: jax.Array
40
+ Te: jax.Array
41
+ Ti: jax.Array
42
+ Vi: jax.Array
43
+ Ve: jax.Array
44
+ vorticity: jax.Array
45
+
46
+
47
+ @jax.tree_util.register_pytree_node_class
48
+ @dataclass(frozen=True)
49
+ class FciDrbEBBoundaryConditions:
50
+ """Per-field boundary payload for the electrostatic Boussinesq DRB model."""
51
+
52
+ density_face_bc: BoundaryFaceBC3D
53
+ density_cut_wall_bc: CutWallBC3D
54
+ potential_face_bc: BoundaryFaceBC3D
55
+ potential_cut_wall_bc: CutWallBC3D
56
+ vorticity_face_bc: BoundaryFaceBC3D
57
+ vorticity_cut_wall_bc: CutWallBC3D
58
+ Te_face_bc: BoundaryFaceBC3D
59
+ Te_cut_wall_bc: CutWallBC3D
60
+ Ti_face_bc: BoundaryFaceBC3D
61
+ Ti_cut_wall_bc: CutWallBC3D
62
+ Vi_face_bc: BoundaryFaceBC3D
63
+ Vi_cut_wall_bc: CutWallBC3D
64
+ Ve_face_bc: BoundaryFaceBC3D
65
+ Ve_cut_wall_bc: CutWallBC3D
66
+
67
+ def tree_flatten(self):
68
+ return (
69
+ (
70
+ self.density_face_bc,
71
+ self.density_cut_wall_bc,
72
+ self.potential_face_bc,
73
+ self.potential_cut_wall_bc,
74
+ self.vorticity_face_bc,
75
+ self.vorticity_cut_wall_bc,
76
+ self.Te_face_bc,
77
+ self.Te_cut_wall_bc,
78
+ self.Ti_face_bc,
79
+ self.Ti_cut_wall_bc,
80
+ self.Vi_face_bc,
81
+ self.Vi_cut_wall_bc,
82
+ self.Ve_face_bc,
83
+ self.Ve_cut_wall_bc,
84
+ ),
85
+ None,
86
+ )
87
+
88
+ @classmethod
89
+ def tree_unflatten(cls, _aux_data, children):
90
+ return cls(*children)
91
+
92
+
93
+ @jax.tree_util.register_pytree_node_class
94
+ @dataclass(frozen=True)
95
+ class FciDrbEBRhsParameters:
96
+ """Physical normalization constants for the electrostatic Boussinesq DRB scaffold."""
97
+
98
+ n0: float = 1.0
99
+ Te0: float = 1.0
100
+ Ti0: float = 1.0
101
+ cs_0: float = 1.0
102
+ rhos_s0: float = 1.0
103
+ tau: float = 1.0
104
+ mi_over_me: float = 1836.0
105
+ rho_star: float = 1.0
106
+ phi_inversion_iterations: int = 80
107
+ phi_inversion_regularization: float = 1.0e-9
108
+ density_D_perp: float = 0.0
109
+ density_D_parallel: float = 0.0
110
+ electron_temperature_chi_parallel: float = 0.0
111
+ electron_temperature_D_perp: float = 0.0
112
+ ion_temperature_chi_parallel: float = 0.0
113
+ ion_temperature_D_perp: float = 0.0
114
+ Ve_nu: float = 0.0
115
+ Ve_D_perp: float = 0.0
116
+ Ve_D_parallel: float = 0.0
117
+ Vi_D_perp: float = 0.0
118
+ Vi_D_parallel: float = 0.0
119
+ vorticity_D_perp: float = 0.0
120
+ vorticity_D_parallel: float = 0.0
121
+
122
+ def tree_flatten(self):
123
+ return (
124
+ (
125
+ self.n0,
126
+ self.Te0,
127
+ self.Ti0,
128
+ self.cs_0,
129
+ self.rhos_s0,
130
+ self.tau,
131
+ self.mi_over_me,
132
+ self.rho_star,
133
+ self.phi_inversion_iterations,
134
+ self.phi_inversion_regularization,
135
+ self.density_D_perp,
136
+ self.density_D_parallel,
137
+ self.electron_temperature_chi_parallel,
138
+ self.electron_temperature_D_perp,
139
+ self.ion_temperature_chi_parallel,
140
+ self.ion_temperature_D_perp,
141
+ self.Ve_nu,
142
+ self.Ve_D_perp,
143
+ self.Ve_D_parallel,
144
+ self.Vi_D_perp,
145
+ self.Vi_D_parallel,
146
+ self.vorticity_D_perp,
147
+ self.vorticity_D_parallel,
148
+ ),
149
+ None,
150
+ )
151
+
152
+ @classmethod
153
+ def tree_unflatten(cls, _aux_data, children):
154
+ (
155
+ n0,
156
+ Te0,
157
+ Ti0,
158
+ cs_0,
159
+ rhos_s0,
160
+ tau,
161
+ mi_over_me,
162
+ rho_star,
163
+ phi_inversion_iterations,
164
+ phi_inversion_regularization,
165
+ density_D_perp,
166
+ density_D_parallel,
167
+ electron_temperature_chi_parallel,
168
+ electron_temperature_D_perp,
169
+ ion_temperature_chi_parallel,
170
+ ion_temperature_D_perp,
171
+ Ve_nu,
172
+ Ve_D_perp,
173
+ Ve_D_parallel,
174
+ Vi_D_perp,
175
+ Vi_D_parallel,
176
+ vorticity_D_perp,
177
+ vorticity_D_parallel,
178
+ ) = children
179
+ return cls(
180
+ n0=n0,
181
+ Te0=Te0,
182
+ Ti0=Ti0,
183
+ cs_0=cs_0,
184
+ rhos_s0=rhos_s0,
185
+ tau=tau,
186
+ mi_over_me=mi_over_me,
187
+ rho_star=rho_star,
188
+ phi_inversion_iterations=phi_inversion_iterations,
189
+ phi_inversion_regularization=phi_inversion_regularization,
190
+ density_D_perp=density_D_perp,
191
+ density_D_parallel=density_D_parallel,
192
+ electron_temperature_chi_parallel=electron_temperature_chi_parallel,
193
+ electron_temperature_D_perp=electron_temperature_D_perp,
194
+ ion_temperature_chi_parallel=ion_temperature_chi_parallel,
195
+ ion_temperature_D_perp=ion_temperature_D_perp,
196
+ Ve_nu=Ve_nu,
197
+ Ve_D_perp=Ve_D_perp,
198
+ Ve_D_parallel=Ve_D_parallel,
199
+ Vi_D_perp=Vi_D_perp,
200
+ Vi_D_parallel=Vi_D_parallel,
201
+ vorticity_D_perp=vorticity_D_perp,
202
+ vorticity_D_parallel=vorticity_D_parallel,
203
+ )
204
+
205
+
206
+ @jax.tree_util.register_pytree_node_class
207
+ @dataclass(frozen=True)
208
+ class FciDrbEBRhsResult:
209
+ rhs: FciDrbEBState
210
+ potential: jax.Array
211
+ potential_residual_l2: jax.Array
212
+
213
+ def tree_flatten(self):
214
+ return ((self.rhs, self.potential, self.potential_residual_l2), None)
215
+
216
+ @classmethod
217
+ def tree_unflatten(cls, _aux_data, children):
218
+ rhs, potential, potential_residual_l2 = children
219
+ return cls(rhs=rhs, potential=potential, potential_residual_l2=potential_residual_l2)
220
+
221
+
222
+ def _multiply_local_stencils(left: LocalStencil3D, right: LocalStencil3D) -> LocalStencil3D:
223
+ """Multiply two boundary-complete local stencils pointwise."""
224
+
225
+ def _multiply_axis(left_axis: LocalStencil1D, right_axis: LocalStencil1D) -> LocalStencil1D:
226
+ return left_axis.replace(
227
+ center=left_axis.center * right_axis.center,
228
+ minus=left_axis.minus * right_axis.minus,
229
+ plus=left_axis.plus * right_axis.plus,
230
+ derivative_minus_weight=left_axis.derivative_minus_weight,
231
+ derivative_center_weight=left_axis.derivative_center_weight,
232
+ derivative_plus_weight=left_axis.derivative_plus_weight,
233
+ )
234
+
235
+ return LocalStencil3D(
236
+ x=_multiply_axis(left.x, right.x),
237
+ y=_multiply_axis(left.y, right.y),
238
+ z=_multiply_axis(left.z, right.z),
239
+ )
240
+
241
+
242
+ def _density_rhs(
243
+ *,
244
+ density: jnp.ndarray,
245
+ geometry: FciGeometry3D,
246
+ parameters: FciDrbEBRhsParameters,
247
+ curvature_coefficients: jnp.ndarray,
248
+ density_stencil: LocalStencil3D,
249
+ potential_stencil: LocalStencil3D,
250
+ Pe_stencil: LocalStencil3D,
251
+ Ve_stencil: LocalStencil3D,
252
+ density_conservative_stencil: ConservativeStencil3D | None,
253
+ density_face_bc: BoundaryFaceBC3D,
254
+ density_cut_wall_geometry: CutWallGeometry3D | None,
255
+ density_cut_wall_bc: CutWallBC3D | None,
256
+ periodic_axes: tuple[bool, bool, bool],
257
+ b_floor: float = 1.0e-30,
258
+ jacobian_floor: float = 1.0e-30,
259
+ ) -> jnp.ndarray:
260
+ """Assemble the density RHS with prebuilt stencils and geometry inputs."""
261
+
262
+ density = jnp.asarray(density, dtype=jnp.float64)
263
+ rho_star = jnp.asarray(parameters.rho_star, dtype=jnp.float64)
264
+ density_D_perp = jnp.asarray(parameters.density_D_perp, dtype=jnp.float64)
265
+ density_D_parallel = jnp.asarray(parameters.density_D_parallel, dtype=jnp.float64)
266
+ bmag = jnp.maximum(jnp.asarray(geometry.cell_bfield.Bmag, dtype=jnp.float64), float(b_floor))
267
+
268
+ poisson_bracket_density = poisson_bracket_op(potential_stencil, density_stencil, geometry)
269
+ particle_flux_parallel_stencil = _multiply_local_stencils(density_stencil, Ve_stencil)
270
+ parallel_density_flux = grad_parallel_op_direct(particle_flux_parallel_stencil, geometry)
271
+ curvature_Pe = curvature_op(
272
+ Pe_stencil,
273
+ geometry,
274
+ curvature_coefficients=curvature_coefficients,
275
+ )
276
+ curvature_potential = curvature_op(
277
+ potential_stencil,
278
+ geometry,
279
+ curvature_coefficients=curvature_coefficients,
280
+ )
281
+ perp_diffusion_density = jnp.zeros_like(density)
282
+ if float(density_D_perp) != 0.0:
283
+ if density_conservative_stencil is None:
284
+ raise ValueError("density_conservative_stencil is required when density_D_perp is nonzero")
285
+ perp_diffusion_density = density_D_perp * perp_laplacian_conservative_op(
286
+ density_conservative_stencil,
287
+ geometry,
288
+ face_bc=density_face_bc,
289
+ cut_wall_geometry=density_cut_wall_geometry,
290
+ cut_wall_bc=density_cut_wall_bc,
291
+ periodic_axes=periodic_axes,
292
+ b_floor=b_floor,
293
+ jacobian_floor=jacobian_floor,
294
+ )
295
+ parallel_diffusion_density = jnp.zeros_like(density)
296
+ if float(density_D_parallel) != 0.0:
297
+ parallel_diffusion_density = density_D_parallel * parallel_laplacian_direct_op(
298
+ density,
299
+ geometry,
300
+ face_bc=density_face_bc,
301
+ periodic_axes=periodic_axes,
302
+ )
303
+
304
+ return (
305
+ -(poisson_bracket_density / (rho_star * bmag))
306
+ - parallel_density_flux
307
+ + (2.0 / bmag) * (curvature_Pe - density * curvature_potential)
308
+ + perp_diffusion_density
309
+ + parallel_diffusion_density
310
+ )
311
+
312
+
313
+ def _Te_rhs(
314
+ *,
315
+ Te: jnp.ndarray,
316
+ density: jnp.ndarray,
317
+ Vi: jnp.ndarray,
318
+ Ve: jnp.ndarray,
319
+ geometry: FciGeometry3D,
320
+ parameters: FciDrbEBRhsParameters,
321
+ curvature_coefficients: jnp.ndarray,
322
+ temperature_stencil: LocalStencil3D,
323
+ density_stencil: LocalStencil3D,
324
+ potential_stencil: LocalStencil3D,
325
+ Pe_stencil: LocalStencil3D,
326
+ current_density_stencil: LocalStencil3D,
327
+ Ve_stencil: LocalStencil3D,
328
+ temperature_conservative_stencil: ConservativeStencil3D | None,
329
+ temperature_face_bc: BoundaryFaceBC3D,
330
+ temperature_cut_wall_geometry: CutWallGeometry3D | None,
331
+ temperature_cut_wall_bc: CutWallBC3D | None,
332
+ periodic_axes: tuple[bool, bool, bool],
333
+ b_floor: float = 1.0e-30,
334
+ jacobian_floor: float = 1.0e-30,
335
+ ) -> jnp.ndarray:
336
+ """Assemble the electron temperature RHS with prebuilt stencils and geometry inputs."""
337
+
338
+ Te = jnp.asarray(Te, dtype=jnp.float64)
339
+ density = jnp.asarray(density, dtype=jnp.float64)
340
+ Vi = jnp.asarray(Vi, dtype=jnp.float64)
341
+ Ve = jnp.asarray(Ve, dtype=jnp.float64)
342
+ rho_star = jnp.asarray(parameters.rho_star, dtype=jnp.float64)
343
+ chi_parallel = jnp.asarray(parameters.electron_temperature_chi_parallel, dtype=jnp.float64)
344
+ D_perp = jnp.asarray(parameters.electron_temperature_D_perp, dtype=jnp.float64)
345
+ bmag = jnp.maximum(jnp.asarray(geometry.cell_bfield.Bmag, dtype=jnp.float64), float(b_floor))
346
+
347
+ poisson_bracket_te = poisson_bracket_op(potential_stencil, temperature_stencil, geometry)
348
+ parallel_advection_te = Ve * grad_parallel_op_direct(temperature_stencil, geometry)
349
+ curvature_Pe = curvature_op(
350
+ Pe_stencil,
351
+ geometry,
352
+ curvature_coefficients=curvature_coefficients,
353
+ )
354
+ curvature_temperature = curvature_op(
355
+ temperature_stencil,
356
+ geometry,
357
+ curvature_coefficients=curvature_coefficients,
358
+ )
359
+ curvature_potential = curvature_op(
360
+ potential_stencil,
361
+ geometry,
362
+ curvature_coefficients=curvature_coefficients,
363
+ )
364
+ parallel_current_density = grad_parallel_op_direct(current_density_stencil, geometry)
365
+ parallel_Ve = grad_parallel_op_direct(Ve_stencil, geometry)
366
+ perp_diffusion_te = jnp.zeros_like(Te)
367
+ if float(D_perp) != 0.0:
368
+ if temperature_conservative_stencil is None:
369
+ raise ValueError("temperature_conservative_stencil is required when electron_temperature_D_perp is nonzero")
370
+ perp_diffusion_te = D_perp * perp_laplacian_conservative_op(
371
+ temperature_conservative_stencil,
372
+ geometry,
373
+ face_bc=temperature_face_bc,
374
+ cut_wall_geometry=temperature_cut_wall_geometry,
375
+ cut_wall_bc=temperature_cut_wall_bc,
376
+ periodic_axes=periodic_axes,
377
+ b_floor=b_floor,
378
+ jacobian_floor=jacobian_floor,
379
+ )
380
+ parallel_diffusion_te = jnp.zeros_like(Te)
381
+ if float(chi_parallel) != 0.0:
382
+ parallel_diffusion_te = chi_parallel * parallel_laplacian_direct_op(
383
+ Te,
384
+ geometry,
385
+ face_bc=temperature_face_bc,
386
+ periodic_axes=periodic_axes,
387
+ )
388
+
389
+ return (
390
+ -(poisson_bracket_te / (rho_star * bmag))
391
+ - parallel_advection_te
392
+ + (4.0 * Te / (3.0 * bmag))
393
+ * (
394
+ curvature_Pe / density
395
+ + 2.5 * curvature_temperature
396
+ - curvature_potential
397
+ )
398
+ + (2.0 * Te / (3.0 * density))
399
+ * (
400
+ 0.71 * parallel_current_density - density * parallel_Ve
401
+ )
402
+ + parallel_diffusion_te
403
+ + perp_diffusion_te
404
+ )
405
+
406
+
407
+ def _Ti_rhs(
408
+ *,
409
+ Ti: jnp.ndarray,
410
+ density: jnp.ndarray,
411
+ Vi: jnp.ndarray,
412
+ Ve: jnp.ndarray,
413
+ geometry: FciGeometry3D,
414
+ parameters: FciDrbEBRhsParameters,
415
+ curvature_coefficients: jnp.ndarray,
416
+ Ti_stencil: LocalStencil3D,
417
+ density_stencil: LocalStencil3D,
418
+ potential_stencil: LocalStencil3D,
419
+ Pe_stencil: LocalStencil3D,
420
+ current_density_stencil: LocalStencil3D,
421
+ Vi_stencil: LocalStencil3D,
422
+ Ti_conservative_stencil: ConservativeStencil3D | None,
423
+ Ti_face_bc: BoundaryFaceBC3D,
424
+ Ti_cut_wall_geometry: CutWallGeometry3D | None,
425
+ Ti_cut_wall_bc: CutWallBC3D | None,
426
+ periodic_axes: tuple[bool, bool, bool],
427
+ b_floor: float = 1.0e-30,
428
+ jacobian_floor: float = 1.0e-30,
429
+ ) -> jnp.ndarray:
430
+ """Assemble the ion temperature RHS with prebuilt stencils and geometry inputs."""
431
+
432
+ Ti = jnp.asarray(Ti, dtype=jnp.float64)
433
+ density = jnp.asarray(density, dtype=jnp.float64)
434
+ Vi = jnp.asarray(Vi, dtype=jnp.float64)
435
+ Ve = jnp.asarray(Ve, dtype=jnp.float64)
436
+ rho_star = jnp.asarray(parameters.rho_star, dtype=jnp.float64)
437
+ tau = jnp.asarray(parameters.tau, dtype=jnp.float64)
438
+ chi_parallel = jnp.asarray(parameters.ion_temperature_chi_parallel, dtype=jnp.float64)
439
+ D_perp = jnp.asarray(parameters.ion_temperature_D_perp, dtype=jnp.float64)
440
+ bmag = jnp.maximum(jnp.asarray(geometry.cell_bfield.Bmag, dtype=jnp.float64), float(b_floor))
441
+
442
+ poisson_bracket_Ti = poisson_bracket_op(potential_stencil, Ti_stencil, geometry)
443
+ parallel_advection_Ti = Vi * grad_parallel_op_direct(Ti_stencil, geometry)
444
+ curvature_Pe = curvature_op(
445
+ Pe_stencil,
446
+ geometry,
447
+ curvature_coefficients=curvature_coefficients,
448
+ )
449
+ curvature_Ti = curvature_op(
450
+ Ti_stencil,
451
+ geometry,
452
+ curvature_coefficients=curvature_coefficients,
453
+ )
454
+ curvature_potential = curvature_op(
455
+ potential_stencil,
456
+ geometry,
457
+ curvature_coefficients=curvature_coefficients,
458
+ )
459
+ parallel_current_density = grad_parallel_op_direct(current_density_stencil, geometry)
460
+ parallel_Vi = grad_parallel_op_direct(Vi_stencil, geometry)
461
+ perp_diffusion_Ti = jnp.zeros_like(Ti)
462
+ if float(D_perp) != 0.0:
463
+ if Ti_conservative_stencil is None:
464
+ raise ValueError("Ti_conservative_stencil is required when ion_temperature_D_perp is nonzero")
465
+ perp_diffusion_Ti = D_perp * perp_laplacian_conservative_op(
466
+ Ti_conservative_stencil,
467
+ geometry,
468
+ face_bc=Ti_face_bc,
469
+ cut_wall_geometry=Ti_cut_wall_geometry,
470
+ cut_wall_bc=Ti_cut_wall_bc,
471
+ periodic_axes=periodic_axes,
472
+ b_floor=b_floor,
473
+ jacobian_floor=jacobian_floor,
474
+ )
475
+ parallel_diffusion_Ti = jnp.zeros_like(Ti)
476
+ if float(chi_parallel) != 0.0:
477
+ parallel_diffusion_Ti = chi_parallel * parallel_laplacian_direct_op(
478
+ Ti,
479
+ geometry,
480
+ face_bc=Ti_face_bc,
481
+ periodic_axes=periodic_axes,
482
+ )
483
+
484
+ return (
485
+ -(poisson_bracket_Ti / (rho_star * bmag))
486
+ - parallel_advection_Ti
487
+ + (4.0 * Ti / (3.0 * bmag))
488
+ * (
489
+ curvature_Pe / density
490
+ - 2.5 * tau * curvature_Ti
491
+ - curvature_potential
492
+ )
493
+ + (2.0 * Ti / (3.0 * density))
494
+ * (
495
+ parallel_current_density - density * parallel_Vi
496
+ )
497
+ + parallel_diffusion_Ti
498
+ + perp_diffusion_Ti
499
+ )
500
+
501
+
502
+ def _Ve_rhs(
503
+ *,
504
+ Ve: jnp.ndarray,
505
+ density: jnp.ndarray,
506
+ Te: jnp.ndarray,
507
+ geometry: FciGeometry3D,
508
+ parameters: FciDrbEBRhsParameters,
509
+ curvature_coefficients: jnp.ndarray,
510
+ Ve_stencil: LocalStencil3D,
511
+ density_stencil: LocalStencil3D,
512
+ potential_stencil: LocalStencil3D,
513
+ Pe_stencil: LocalStencil3D,
514
+ Te_stencil: LocalStencil3D,
515
+ current_density: jnp.ndarray,
516
+ Ve_conservative_stencil: ConservativeStencil3D | None,
517
+ Ve_face_bc: BoundaryFaceBC3D,
518
+ Ve_cut_wall_geometry: CutWallGeometry3D | None,
519
+ Ve_cut_wall_bc: CutWallBC3D | None,
520
+ periodic_axes: tuple[bool, bool, bool],
521
+ b_floor: float = 1.0e-30,
522
+ jacobian_floor: float = 1.0e-30,
523
+ ) -> jnp.ndarray:
524
+ """Assemble the electron velocity RHS with prebuilt stencils and geometry inputs."""
525
+
526
+ Ve = jnp.asarray(Ve, dtype=jnp.float64)
527
+ density = jnp.asarray(density, dtype=jnp.float64)
528
+ Te = jnp.asarray(Te, dtype=jnp.float64)
529
+ current_density = jnp.asarray(current_density, dtype=jnp.float64)
530
+ rho_star = jnp.asarray(parameters.rho_star, dtype=jnp.float64)
531
+ mi_over_me = jnp.asarray(parameters.mi_over_me, dtype=jnp.float64)
532
+ Ve_nu = jnp.asarray(parameters.Ve_nu, dtype=jnp.float64)
533
+ Ve_D_perp = jnp.asarray(parameters.Ve_D_perp, dtype=jnp.float64)
534
+ Ve_D_parallel = jnp.asarray(parameters.Ve_D_parallel, dtype=jnp.float64)
535
+ bmag = jnp.maximum(jnp.asarray(geometry.cell_bfield.Bmag, dtype=jnp.float64), float(b_floor))
536
+
537
+ poisson_bracket_Ve = poisson_bracket_op(potential_stencil, Ve_stencil, geometry)
538
+ parallel_advection_Ve = Ve * grad_parallel_op_direct(Ve_stencil, geometry)
539
+ parallel_phi = grad_parallel_op_direct(potential_stencil, geometry)
540
+ parallel_Pe = grad_parallel_op_direct(Pe_stencil, geometry)
541
+ parallel_Te = grad_parallel_op_direct(Te_stencil, geometry)
542
+ perp_diffusion_Ve = jnp.zeros_like(Ve)
543
+ if float(Ve_D_perp) != 0.0:
544
+ if Ve_conservative_stencil is None:
545
+ raise ValueError("Ve_conservative_stencil is required when Ve_D_perp is nonzero")
546
+ perp_diffusion_Ve = Ve_D_perp * perp_laplacian_conservative_op(
547
+ Ve_conservative_stencil,
548
+ geometry,
549
+ face_bc=Ve_face_bc,
550
+ cut_wall_geometry=Ve_cut_wall_geometry,
551
+ cut_wall_bc=Ve_cut_wall_bc,
552
+ periodic_axes=periodic_axes,
553
+ b_floor=b_floor,
554
+ jacobian_floor=jacobian_floor,
555
+ )
556
+ parallel_diffusion_Ve = jnp.zeros_like(Ve)
557
+ if float(Ve_D_parallel) != 0.0:
558
+ parallel_diffusion_Ve = Ve_D_parallel * parallel_laplacian_direct_op(
559
+ Ve,
560
+ geometry,
561
+ face_bc=Ve_face_bc,
562
+ periodic_axes=periodic_axes,
563
+ )
564
+
565
+ return (
566
+ -(poisson_bracket_Ve / (rho_star * bmag))
567
+ - parallel_advection_Ve
568
+ + mi_over_me
569
+ * (
570
+ Ve_nu * current_density
571
+ + parallel_phi
572
+ - parallel_Pe / jnp.maximum(density, 1.0e-30)
573
+ - 0.71 * parallel_Te
574
+ )
575
+ + parallel_diffusion_Ve
576
+ + perp_diffusion_Ve
577
+ )
578
+
579
+
580
+ def _Vi_rhs(
581
+ *,
582
+ Vi: jnp.ndarray,
583
+ density: jnp.ndarray,
584
+ geometry: FciGeometry3D,
585
+ parameters: FciDrbEBRhsParameters,
586
+ curvature_coefficients: jnp.ndarray,
587
+ Vi_stencil: LocalStencil3D,
588
+ density_stencil: LocalStencil3D,
589
+ potential_stencil: LocalStencil3D,
590
+ pressure_stencil: LocalStencil3D,
591
+ Vi_conservative_stencil: ConservativeStencil3D | None,
592
+ Vi_face_bc: BoundaryFaceBC3D,
593
+ Vi_cut_wall_geometry: CutWallGeometry3D | None,
594
+ Vi_cut_wall_bc: CutWallBC3D | None,
595
+ periodic_axes: tuple[bool, bool, bool],
596
+ b_floor: float = 1.0e-30,
597
+ jacobian_floor: float = 1.0e-30,
598
+ ) -> jnp.ndarray:
599
+ """Assemble the ion velocity RHS with prebuilt stencils and geometry inputs."""
600
+
601
+ Vi = jnp.asarray(Vi, dtype=jnp.float64)
602
+ density = jnp.asarray(density, dtype=jnp.float64)
603
+ rho_star = jnp.asarray(parameters.rho_star, dtype=jnp.float64)
604
+ Vi_D_perp = jnp.asarray(parameters.Vi_D_perp, dtype=jnp.float64)
605
+ Vi_D_parallel = jnp.asarray(parameters.Vi_D_parallel, dtype=jnp.float64)
606
+ bmag = jnp.maximum(jnp.asarray(geometry.cell_bfield.Bmag, dtype=jnp.float64), float(b_floor))
607
+
608
+ poisson_bracket_Vi = poisson_bracket_op(potential_stencil, Vi_stencil, geometry)
609
+ parallel_advection_Vi = Vi * grad_parallel_op_direct(Vi_stencil, geometry)
610
+ parallel_pressure = grad_parallel_op_direct(pressure_stencil, geometry)
611
+ perp_diffusion_Vi = jnp.zeros_like(Vi)
612
+ if float(Vi_D_perp) != 0.0:
613
+ if Vi_conservative_stencil is None:
614
+ raise ValueError("Vi_conservative_stencil is required when Vi_D_perp is nonzero")
615
+ perp_diffusion_Vi = Vi_D_perp * perp_laplacian_conservative_op(
616
+ Vi_conservative_stencil,
617
+ geometry,
618
+ face_bc=Vi_face_bc,
619
+ cut_wall_geometry=Vi_cut_wall_geometry,
620
+ cut_wall_bc=Vi_cut_wall_bc,
621
+ periodic_axes=periodic_axes,
622
+ b_floor=b_floor,
623
+ jacobian_floor=jacobian_floor,
624
+ )
625
+ parallel_diffusion_Vi = jnp.zeros_like(Vi)
626
+ if float(Vi_D_parallel) != 0.0:
627
+ parallel_diffusion_Vi = Vi_D_parallel * parallel_laplacian_direct_op(
628
+ Vi,
629
+ geometry,
630
+ face_bc=Vi_face_bc,
631
+ periodic_axes=periodic_axes,
632
+ )
633
+
634
+ return (
635
+ -(poisson_bracket_Vi / (rho_star * bmag))
636
+ - parallel_advection_Vi
637
+ - parallel_pressure / jnp.maximum(density, 1.0e-30)
638
+ + parallel_diffusion_Vi
639
+ + perp_diffusion_Vi
640
+ )
641
+
642
+
643
+ def _vorticity_rhs(
644
+ *,
645
+ vorticity: jnp.ndarray,
646
+ density: jnp.ndarray,
647
+ Vi: jnp.ndarray,
648
+ geometry: FciGeometry3D,
649
+ parameters: FciDrbEBRhsParameters,
650
+ curvature_coefficients: jnp.ndarray,
651
+ vorticity_stencil: LocalStencil3D,
652
+ density_stencil: LocalStencil3D,
653
+ potential_stencil: LocalStencil3D,
654
+ pressure_stencil: LocalStencil3D,
655
+ current_density_stencil: LocalStencil3D,
656
+ vorticity_conservative_stencil: ConservativeStencil3D | None,
657
+ vorticity_face_bc: BoundaryFaceBC3D,
658
+ vorticity_cut_wall_geometry: CutWallGeometry3D | None,
659
+ vorticity_cut_wall_bc: CutWallBC3D | None,
660
+ periodic_axes: tuple[bool, bool, bool],
661
+ b_floor: float = 1.0e-30,
662
+ jacobian_floor: float = 1.0e-30,
663
+ ) -> jnp.ndarray:
664
+ """Assemble the vorticity RHS with prebuilt stencils and geometry inputs."""
665
+
666
+ vorticity = jnp.asarray(vorticity, dtype=jnp.float64)
667
+ density = jnp.asarray(density, dtype=jnp.float64)
668
+ Vi = jnp.asarray(Vi, dtype=jnp.float64)
669
+ rho_star = jnp.asarray(parameters.rho_star, dtype=jnp.float64)
670
+ vorticity_D_perp = jnp.asarray(parameters.vorticity_D_perp, dtype=jnp.float64)
671
+ vorticity_D_parallel = jnp.asarray(parameters.vorticity_D_parallel, dtype=jnp.float64)
672
+ bmag = jnp.maximum(jnp.asarray(geometry.cell_bfield.Bmag, dtype=jnp.float64), float(b_floor))
673
+
674
+ poisson_bracket_vorticity = poisson_bracket_op(potential_stencil, vorticity_stencil, geometry)
675
+ parallel_advection_vorticity = Vi * grad_parallel_op_direct(vorticity_stencil, geometry)
676
+ parallel_current_density = grad_parallel_op_direct(current_density_stencil, geometry)
677
+ curvature_pressure = curvature_op(
678
+ pressure_stencil,
679
+ geometry,
680
+ curvature_coefficients=curvature_coefficients,
681
+ )
682
+ perp_diffusion_vorticity = jnp.zeros_like(vorticity)
683
+ if float(vorticity_D_perp) != 0.0:
684
+ if vorticity_conservative_stencil is None:
685
+ raise ValueError("vorticity_conservative_stencil is required when vorticity_D_perp is nonzero")
686
+ perp_diffusion_vorticity = vorticity_D_perp * perp_laplacian_conservative_op(
687
+ vorticity_conservative_stencil,
688
+ geometry,
689
+ face_bc=vorticity_face_bc,
690
+ cut_wall_geometry=vorticity_cut_wall_geometry,
691
+ cut_wall_bc=vorticity_cut_wall_bc,
692
+ periodic_axes=periodic_axes,
693
+ b_floor=b_floor,
694
+ jacobian_floor=jacobian_floor,
695
+ )
696
+ parallel_diffusion_vorticity = jnp.zeros_like(vorticity)
697
+ if float(vorticity_D_parallel) != 0.0:
698
+ parallel_diffusion_vorticity = vorticity_D_parallel * parallel_laplacian_direct_op(
699
+ vorticity,
700
+ geometry,
701
+ face_bc=vorticity_face_bc,
702
+ periodic_axes=periodic_axes,
703
+ )
704
+
705
+ return (
706
+ -(poisson_bracket_vorticity / (rho_star * bmag))
707
+ - parallel_advection_vorticity
708
+ + (bmag * bmag / jnp.maximum(density, 1.0e-30)) * parallel_current_density
709
+ + (2.0 * bmag / jnp.maximum(density, 1.0e-30)) * curvature_pressure
710
+ + parallel_diffusion_vorticity
711
+ + perp_diffusion_vorticity
712
+ )
713
+
714
+
715
+ def _diffusion_only_rhs(
716
+ *,
717
+ density: jnp.ndarray,
718
+ Te: jnp.ndarray,
719
+ Ti: jnp.ndarray,
720
+ Vi: jnp.ndarray,
721
+ Ve: jnp.ndarray,
722
+ vorticity: jnp.ndarray,
723
+ geometry: FciGeometry3D,
724
+ conservative_stencil_builder: ConservativeStencilBuilder,
725
+ parameters: FciDrbEBRhsParameters,
726
+ density_face_bc: BoundaryFaceBC3D,
727
+ vorticity_face_bc: BoundaryFaceBC3D,
728
+ electron_temperature_face_bc: BoundaryFaceBC3D,
729
+ ion_temperature_face_bc: BoundaryFaceBC3D,
730
+ electron_velocity_parallel_face_bc: BoundaryFaceBC3D,
731
+ ion_velocity_parallel_face_bc: BoundaryFaceBC3D,
732
+ density_cut_wall_geometry: CutWallGeometry3D | None,
733
+ density_cut_wall_bc: CutWallBC3D | None,
734
+ vorticity_cut_wall_geometry: CutWallGeometry3D | None,
735
+ vorticity_cut_wall_bc: CutWallBC3D | None,
736
+ electron_temperature_cut_wall_geometry: CutWallGeometry3D | None,
737
+ electron_temperature_cut_wall_bc: CutWallBC3D | None,
738
+ ion_temperature_cut_wall_geometry: CutWallGeometry3D | None,
739
+ ion_temperature_cut_wall_bc: CutWallBC3D | None,
740
+ electron_velocity_parallel_cut_wall_geometry: CutWallGeometry3D | None,
741
+ electron_velocity_parallel_cut_wall_bc: CutWallBC3D | None,
742
+ ion_velocity_parallel_cut_wall_geometry: CutWallGeometry3D | None,
743
+ ion_velocity_parallel_cut_wall_bc: CutWallBC3D | None,
744
+ periodic_axes: tuple[bool, bool, bool],
745
+ b_floor: float = 1.0e-30,
746
+ jacobian_floor: float = 1.0e-30,
747
+ ) -> FciDrbEBState:
748
+ """Assemble only the explicit field diffusion terms for EB sanity checks."""
749
+
750
+ density = jnp.asarray(density, dtype=jnp.float64)
751
+ Te = jnp.asarray(Te, dtype=jnp.float64)
752
+ Ti = jnp.asarray(Ti, dtype=jnp.float64)
753
+ Vi = jnp.asarray(Vi, dtype=jnp.float64)
754
+ Ve = jnp.asarray(Ve, dtype=jnp.float64)
755
+ vorticity = jnp.asarray(vorticity, dtype=jnp.float64)
756
+
757
+ def _field_diffusion(
758
+ field: jnp.ndarray,
759
+ *,
760
+ perpendicular_coefficient: float,
761
+ parallel_coefficient: float,
762
+ face_bc: BoundaryFaceBC3D,
763
+ cut_wall_geometry: CutWallGeometry3D | None,
764
+ cut_wall_bc: CutWallBC3D | None,
765
+ ) -> jnp.ndarray:
766
+ rhs = jnp.zeros_like(field, dtype=jnp.float64)
767
+ if float(perpendicular_coefficient) != 0.0:
768
+ conservative_stencil = conservative_stencil_builder(
769
+ field,
770
+ geometry,
771
+ periodic_axes=periodic_axes,
772
+ face_bc=face_bc,
773
+ )
774
+ rhs = rhs + jnp.asarray(perpendicular_coefficient, dtype=jnp.float64) * perp_laplacian_conservative_op(
775
+ conservative_stencil,
776
+ geometry,
777
+ face_bc=face_bc,
778
+ cut_wall_geometry=cut_wall_geometry,
779
+ cut_wall_bc=cut_wall_bc,
780
+ periodic_axes=periodic_axes,
781
+ b_floor=b_floor,
782
+ jacobian_floor=jacobian_floor,
783
+ )
784
+ if float(parallel_coefficient) != 0.0:
785
+ rhs = rhs + jnp.asarray(parallel_coefficient, dtype=jnp.float64) * parallel_laplacian_direct_op(
786
+ field,
787
+ geometry,
788
+ face_bc=face_bc,
789
+ periodic_axes=periodic_axes,
790
+ )
791
+ return rhs
792
+
793
+ return FciDrbEBState(
794
+ density=_field_diffusion(
795
+ density,
796
+ perpendicular_coefficient=parameters.density_D_perp,
797
+ parallel_coefficient=parameters.density_D_parallel,
798
+ face_bc=density_face_bc,
799
+ cut_wall_geometry=density_cut_wall_geometry,
800
+ cut_wall_bc=density_cut_wall_bc,
801
+ ),
802
+ phi=jnp.zeros_like(density, dtype=jnp.float64),
803
+ Te=_field_diffusion(
804
+ Te,
805
+ perpendicular_coefficient=parameters.electron_temperature_D_perp,
806
+ parallel_coefficient=parameters.electron_temperature_chi_parallel,
807
+ face_bc=electron_temperature_face_bc,
808
+ cut_wall_geometry=electron_temperature_cut_wall_geometry,
809
+ cut_wall_bc=electron_temperature_cut_wall_bc,
810
+ ),
811
+ Ti=_field_diffusion(
812
+ Ti,
813
+ perpendicular_coefficient=parameters.ion_temperature_D_perp,
814
+ parallel_coefficient=parameters.ion_temperature_chi_parallel,
815
+ face_bc=ion_temperature_face_bc,
816
+ cut_wall_geometry=ion_temperature_cut_wall_geometry,
817
+ cut_wall_bc=ion_temperature_cut_wall_bc,
818
+ ),
819
+ Vi=_field_diffusion(
820
+ Vi,
821
+ perpendicular_coefficient=parameters.Vi_D_perp,
822
+ parallel_coefficient=parameters.Vi_D_parallel,
823
+ face_bc=ion_velocity_parallel_face_bc,
824
+ cut_wall_geometry=ion_velocity_parallel_cut_wall_geometry,
825
+ cut_wall_bc=ion_velocity_parallel_cut_wall_bc,
826
+ ),
827
+ Ve=_field_diffusion(
828
+ Ve,
829
+ perpendicular_coefficient=parameters.Ve_D_perp,
830
+ parallel_coefficient=parameters.Ve_D_parallel,
831
+ face_bc=electron_velocity_parallel_face_bc,
832
+ cut_wall_geometry=electron_velocity_parallel_cut_wall_geometry,
833
+ cut_wall_bc=electron_velocity_parallel_cut_wall_bc,
834
+ ),
835
+ vorticity=_field_diffusion(
836
+ vorticity,
837
+ perpendicular_coefficient=parameters.vorticity_D_perp,
838
+ parallel_coefficient=parameters.vorticity_D_parallel,
839
+ face_bc=vorticity_face_bc,
840
+ cut_wall_geometry=vorticity_cut_wall_geometry,
841
+ cut_wall_bc=vorticity_cut_wall_bc,
842
+ ),
843
+ )
844
+
845
+
846
+ def compute_fci_drb_eb_rhs(
847
+ state: FciDrbEBState,
848
+ *,
849
+ geometry: FciGeometry3D,
850
+ stencil_builder: LocalStencilBuilder = build_local_stencil_from_field,
851
+ conservative_stencil_builder: ConservativeStencilBuilder = build_conservative_stencil_from_field,
852
+ parameters: FciDrbEBRhsParameters = FciDrbEBRhsParameters(),
853
+ curvature_coefficients: jnp.ndarray,
854
+ density_face_bc: BoundaryFaceBC3D,
855
+ potential_face_bc: BoundaryFaceBC3D,
856
+ vorticity_face_bc: BoundaryFaceBC3D,
857
+ electron_temperature_face_bc: BoundaryFaceBC3D,
858
+ electron_velocity_parallel_face_bc: BoundaryFaceBC3D,
859
+ ion_temperature_face_bc: BoundaryFaceBC3D | None = None,
860
+ ion_velocity_parallel_face_bc: BoundaryFaceBC3D | None = None,
861
+ density_cut_wall_geometry: CutWallGeometry3D | None = None,
862
+ density_cut_wall_bc: CutWallBC3D | None = None,
863
+ potential_cut_wall_geometry: CutWallGeometry3D | None = None,
864
+ potential_cut_wall_bc: CutWallBC3D | None = None,
865
+ vorticity_cut_wall_geometry: CutWallGeometry3D | None = None,
866
+ vorticity_cut_wall_bc: CutWallBC3D | None = None,
867
+ electron_temperature_cut_wall_geometry: CutWallGeometry3D | None = None,
868
+ electron_temperature_cut_wall_bc: CutWallBC3D | None = None,
869
+ ion_temperature_cut_wall_geometry: CutWallGeometry3D | None = None,
870
+ ion_temperature_cut_wall_bc: CutWallBC3D | None = None,
871
+ electron_velocity_parallel_cut_wall_geometry: CutWallGeometry3D | None = None,
872
+ electron_velocity_parallel_cut_wall_bc: CutWallBC3D | None = None,
873
+ ion_velocity_parallel_cut_wall_geometry: CutWallGeometry3D | None = None,
874
+ ion_velocity_parallel_cut_wall_bc: CutWallBC3D | None = None,
875
+ periodic_axes: tuple[bool, bool, bool] = (False, True, True),
876
+ diffusion_only: bool = False,
877
+ density_source: jax.Array | None = None,
878
+ electron_temperature_source: jax.Array | None = None,
879
+ ) -> FciDrbEBRhsResult:
880
+ """Assemble the electrostatic Boussinesq DRB RHS.
881
+
882
+ Optional density and electron-temperature source terms are added only when
883
+ ``diffusion_only`` is false.
884
+ """
885
+
886
+ density = jnp.asarray(state.density, dtype=jnp.float64)
887
+ phi_state = jnp.asarray(state.phi, dtype=jnp.float64)
888
+ vorticity = jnp.asarray(state.vorticity, dtype=jnp.float64)
889
+ Te = jnp.asarray(state.Te, dtype=jnp.float64)
890
+ Ti = jnp.asarray(state.Ti, dtype=jnp.float64)
891
+ Vi = jnp.asarray(state.Vi, dtype=jnp.float64)
892
+ Ve = jnp.asarray(state.Ve, dtype=jnp.float64)
893
+ rho_star = jnp.asarray(parameters.rho_star, dtype=jnp.float64)
894
+ ion_temperature_face_bc = (
895
+ electron_temperature_face_bc
896
+ if ion_temperature_face_bc is None
897
+ else ion_temperature_face_bc
898
+ )
899
+ ion_temperature_cut_wall_geometry = (
900
+ electron_temperature_cut_wall_geometry
901
+ if ion_temperature_cut_wall_geometry is None
902
+ else ion_temperature_cut_wall_geometry
903
+ )
904
+ ion_temperature_cut_wall_bc = (
905
+ electron_temperature_cut_wall_bc
906
+ if ion_temperature_cut_wall_bc is None
907
+ else ion_temperature_cut_wall_bc
908
+ )
909
+ ion_velocity_parallel_face_bc = (
910
+ electron_velocity_parallel_face_bc
911
+ if ion_velocity_parallel_face_bc is None
912
+ else ion_velocity_parallel_face_bc
913
+ )
914
+ ion_velocity_parallel_cut_wall_geometry = (
915
+ electron_velocity_parallel_cut_wall_geometry
916
+ if ion_velocity_parallel_cut_wall_geometry is None
917
+ else ion_velocity_parallel_cut_wall_geometry
918
+ )
919
+ ion_velocity_parallel_cut_wall_bc = (
920
+ electron_velocity_parallel_cut_wall_bc
921
+ if ion_velocity_parallel_cut_wall_bc is None
922
+ else ion_velocity_parallel_cut_wall_bc
923
+ )
924
+ # These coefficients are Python scalars in the current simulation path, so we can
925
+ # skip zero-diffusion branches eagerly instead of building Laplacians that multiply away.
926
+ density_has_perp_diffusion = float(parameters.density_D_perp) != 0.0
927
+ temperature_has_perp_diffusion = float(parameters.electron_temperature_D_perp) != 0.0
928
+ ti_has_perp_diffusion = float(parameters.ion_temperature_D_perp) != 0.0
929
+ ve_has_perp_diffusion = float(parameters.Ve_D_perp) != 0.0
930
+ vi_has_perp_diffusion = float(parameters.Vi_D_perp) != 0.0
931
+ vorticity_has_perp_diffusion = float(parameters.vorticity_D_perp) != 0.0
932
+
933
+ if bool(diffusion_only):
934
+ rhs = _diffusion_only_rhs(
935
+ density=density,
936
+ Te=Te,
937
+ Ti=Ti,
938
+ Vi=Vi,
939
+ Ve=Ve,
940
+ vorticity=vorticity,
941
+ geometry=geometry,
942
+ conservative_stencil_builder=conservative_stencil_builder,
943
+ parameters=parameters,
944
+ density_face_bc=density_face_bc,
945
+ vorticity_face_bc=vorticity_face_bc,
946
+ electron_temperature_face_bc=electron_temperature_face_bc,
947
+ ion_temperature_face_bc=ion_temperature_face_bc,
948
+ electron_velocity_parallel_face_bc=electron_velocity_parallel_face_bc,
949
+ ion_velocity_parallel_face_bc=ion_velocity_parallel_face_bc,
950
+ density_cut_wall_geometry=density_cut_wall_geometry,
951
+ density_cut_wall_bc=density_cut_wall_bc,
952
+ vorticity_cut_wall_geometry=vorticity_cut_wall_geometry,
953
+ vorticity_cut_wall_bc=vorticity_cut_wall_bc,
954
+ electron_temperature_cut_wall_geometry=electron_temperature_cut_wall_geometry,
955
+ electron_temperature_cut_wall_bc=electron_temperature_cut_wall_bc,
956
+ ion_temperature_cut_wall_geometry=ion_temperature_cut_wall_geometry,
957
+ ion_temperature_cut_wall_bc=ion_temperature_cut_wall_bc,
958
+ electron_velocity_parallel_cut_wall_geometry=electron_velocity_parallel_cut_wall_geometry,
959
+ electron_velocity_parallel_cut_wall_bc=electron_velocity_parallel_cut_wall_bc,
960
+ ion_velocity_parallel_cut_wall_geometry=ion_velocity_parallel_cut_wall_geometry,
961
+ ion_velocity_parallel_cut_wall_bc=ion_velocity_parallel_cut_wall_bc,
962
+ periodic_axes=periodic_axes,
963
+ )
964
+ return FciDrbEBRhsResult(
965
+ rhs=rhs,
966
+ potential=phi_state,
967
+ potential_residual_l2=jnp.asarray(0.0, dtype=jnp.float64),
968
+ )
969
+
970
+ density_stencil = stencil_builder(
971
+ density,
972
+ geometry,
973
+ periodic_axes=periodic_axes,
974
+ face_bc=density_face_bc,
975
+ cut_wall_geometry=density_cut_wall_geometry,
976
+ cut_wall_bc=density_cut_wall_bc,
977
+ )
978
+ Pe = density * Te
979
+ Pe_stencil = stencil_builder(
980
+ Pe,
981
+ geometry,
982
+ periodic_axes=periodic_axes,
983
+ face_bc=electron_temperature_face_bc,
984
+ cut_wall_geometry=electron_temperature_cut_wall_geometry,
985
+ cut_wall_bc=electron_temperature_cut_wall_bc,
986
+ )
987
+ Ve_stencil = stencil_builder(
988
+ Ve,
989
+ geometry,
990
+ periodic_axes=periodic_axes,
991
+ face_bc=electron_velocity_parallel_face_bc,
992
+ cut_wall_geometry=electron_velocity_parallel_cut_wall_geometry,
993
+ cut_wall_bc=electron_velocity_parallel_cut_wall_bc,
994
+ )
995
+ Vi_stencil = stencil_builder(
996
+ Vi,
997
+ geometry,
998
+ periodic_axes=periodic_axes,
999
+ face_bc=ion_velocity_parallel_face_bc,
1000
+ cut_wall_geometry=ion_velocity_parallel_cut_wall_geometry,
1001
+ cut_wall_bc=ion_velocity_parallel_cut_wall_bc,
1002
+ )
1003
+ Vi_conservative_stencil = (
1004
+ conservative_stencil_builder(
1005
+ Vi,
1006
+ geometry,
1007
+ periodic_axes=periodic_axes,
1008
+ face_bc=ion_velocity_parallel_face_bc,
1009
+ )
1010
+ if vi_has_perp_diffusion
1011
+ else None
1012
+ )
1013
+ temperature_stencil = stencil_builder(
1014
+ Te,
1015
+ geometry,
1016
+ periodic_axes=periodic_axes,
1017
+ face_bc=electron_temperature_face_bc,
1018
+ cut_wall_geometry=electron_temperature_cut_wall_geometry,
1019
+ cut_wall_bc=electron_temperature_cut_wall_bc,
1020
+ )
1021
+ current_density = density * (Vi - Ve)
1022
+ current_density_stencil = stencil_builder(
1023
+ current_density,
1024
+ geometry,
1025
+ periodic_axes=periodic_axes,
1026
+ face_bc=electron_velocity_parallel_face_bc,
1027
+ cut_wall_geometry=electron_velocity_parallel_cut_wall_geometry,
1028
+ cut_wall_bc=electron_velocity_parallel_cut_wall_bc,
1029
+ )
1030
+ temperature_conservative_stencil = (
1031
+ conservative_stencil_builder(
1032
+ Te,
1033
+ geometry,
1034
+ periodic_axes=periodic_axes,
1035
+ face_bc=electron_temperature_face_bc,
1036
+ )
1037
+ if temperature_has_perp_diffusion
1038
+ else None
1039
+ )
1040
+ Ti_stencil = stencil_builder(
1041
+ Ti,
1042
+ geometry,
1043
+ periodic_axes=periodic_axes,
1044
+ face_bc=ion_temperature_face_bc,
1045
+ cut_wall_geometry=ion_temperature_cut_wall_geometry,
1046
+ cut_wall_bc=ion_temperature_cut_wall_bc,
1047
+ )
1048
+ pressure = Pe + parameters.tau * (density * Ti)
1049
+ pressure_stencil = stencil_builder(
1050
+ pressure,
1051
+ geometry,
1052
+ periodic_axes=periodic_axes,
1053
+ face_bc=electron_temperature_face_bc,
1054
+ cut_wall_geometry=electron_temperature_cut_wall_geometry,
1055
+ cut_wall_bc=electron_temperature_cut_wall_bc,
1056
+ )
1057
+ Ti_conservative_stencil = (
1058
+ conservative_stencil_builder(
1059
+ Ti,
1060
+ geometry,
1061
+ periodic_axes=periodic_axes,
1062
+ face_bc=ion_temperature_face_bc,
1063
+ )
1064
+ if ti_has_perp_diffusion
1065
+ else None
1066
+ )
1067
+ potential = phi_state
1068
+ potential_residual_l2 = jnp.asarray(0.0, dtype=jnp.float64)
1069
+ potential_stencil = stencil_builder(
1070
+ potential,
1071
+ geometry,
1072
+ periodic_axes=periodic_axes,
1073
+ face_bc=potential_face_bc,
1074
+ cut_wall_geometry=potential_cut_wall_geometry,
1075
+ cut_wall_bc=potential_cut_wall_bc,
1076
+ )
1077
+ vorticity_stencil = stencil_builder(
1078
+ vorticity,
1079
+ geometry,
1080
+ periodic_axes=periodic_axes,
1081
+ face_bc=vorticity_face_bc,
1082
+ cut_wall_geometry=vorticity_cut_wall_geometry,
1083
+ cut_wall_bc=vorticity_cut_wall_bc,
1084
+ )
1085
+ Ve_conservative_stencil = (
1086
+ conservative_stencil_builder(
1087
+ Ve,
1088
+ geometry,
1089
+ periodic_axes=periodic_axes,
1090
+ face_bc=electron_velocity_parallel_face_bc,
1091
+ )
1092
+ if ve_has_perp_diffusion
1093
+ else None
1094
+ )
1095
+ density_conservative_stencil = (
1096
+ conservative_stencil_builder(
1097
+ density,
1098
+ geometry,
1099
+ periodic_axes=periodic_axes,
1100
+ face_bc=density_face_bc,
1101
+ )
1102
+ if density_has_perp_diffusion
1103
+ else None
1104
+ )
1105
+ vorticity_conservative_stencil = (
1106
+ conservative_stencil_builder(
1107
+ vorticity,
1108
+ geometry,
1109
+ periodic_axes=periodic_axes,
1110
+ face_bc=vorticity_face_bc,
1111
+ )
1112
+ if vorticity_has_perp_diffusion
1113
+ else None
1114
+ )
1115
+
1116
+ density_rhs = _density_rhs(
1117
+ density=density,
1118
+ geometry=geometry,
1119
+ parameters=parameters,
1120
+ curvature_coefficients=curvature_coefficients,
1121
+ density_stencil=density_stencil,
1122
+ potential_stencil=potential_stencil,
1123
+ Pe_stencil=Pe_stencil,
1124
+ Ve_stencil=Ve_stencil,
1125
+ density_conservative_stencil=density_conservative_stencil,
1126
+ density_face_bc=density_face_bc,
1127
+ density_cut_wall_geometry=density_cut_wall_geometry,
1128
+ density_cut_wall_bc=density_cut_wall_bc,
1129
+ periodic_axes=periodic_axes,
1130
+ )
1131
+ if density_source is not None:
1132
+ density_rhs = density_rhs + jnp.asarray(density_source, dtype=jnp.float64)
1133
+ Te_rhs = _Te_rhs(
1134
+ Te=Te,
1135
+ density=density,
1136
+ Vi=Vi,
1137
+ Ve=Ve,
1138
+ geometry=geometry,
1139
+ parameters=parameters,
1140
+ curvature_coefficients=curvature_coefficients,
1141
+ temperature_stencil=temperature_stencil,
1142
+ density_stencil=density_stencil,
1143
+ potential_stencil=potential_stencil,
1144
+ Pe_stencil=Pe_stencil,
1145
+ current_density_stencil=current_density_stencil,
1146
+ Ve_stencil=Ve_stencil,
1147
+ temperature_conservative_stencil=temperature_conservative_stencil,
1148
+ temperature_face_bc=electron_temperature_face_bc,
1149
+ temperature_cut_wall_geometry=electron_temperature_cut_wall_geometry,
1150
+ temperature_cut_wall_bc=electron_temperature_cut_wall_bc,
1151
+ periodic_axes=periodic_axes,
1152
+ )
1153
+ if electron_temperature_source is not None:
1154
+ Te_rhs = Te_rhs + jnp.asarray(electron_temperature_source, dtype=jnp.float64)
1155
+ Ti_rhs = _Ti_rhs(
1156
+ Ti=Ti,
1157
+ density=density,
1158
+ Vi=Vi,
1159
+ Ve=Ve,
1160
+ geometry=geometry,
1161
+ parameters=parameters,
1162
+ curvature_coefficients=curvature_coefficients,
1163
+ Ti_stencil=Ti_stencil,
1164
+ density_stencil=density_stencil,
1165
+ potential_stencil=potential_stencil,
1166
+ Pe_stencil=Pe_stencil,
1167
+ current_density_stencil=current_density_stencil,
1168
+ Vi_stencil=Vi_stencil,
1169
+ Ti_conservative_stencil=Ti_conservative_stencil,
1170
+ Ti_face_bc=ion_temperature_face_bc,
1171
+ Ti_cut_wall_geometry=ion_temperature_cut_wall_geometry,
1172
+ Ti_cut_wall_bc=ion_temperature_cut_wall_bc,
1173
+ periodic_axes=periodic_axes,
1174
+ )
1175
+ Vi_rhs = _Vi_rhs(
1176
+ Vi=Vi,
1177
+ density=density,
1178
+ geometry=geometry,
1179
+ parameters=parameters,
1180
+ curvature_coefficients=curvature_coefficients,
1181
+ Vi_stencil=Vi_stencil,
1182
+ density_stencil=density_stencil,
1183
+ potential_stencil=potential_stencil,
1184
+ pressure_stencil=pressure_stencil,
1185
+ Vi_conservative_stencil=Vi_conservative_stencil,
1186
+ Vi_face_bc=ion_velocity_parallel_face_bc,
1187
+ Vi_cut_wall_geometry=ion_velocity_parallel_cut_wall_geometry,
1188
+ Vi_cut_wall_bc=ion_velocity_parallel_cut_wall_bc,
1189
+ periodic_axes=periodic_axes,
1190
+ )
1191
+ vorticity_rhs = _vorticity_rhs(
1192
+ vorticity=vorticity,
1193
+ density=density,
1194
+ Vi=Vi,
1195
+ geometry=geometry,
1196
+ parameters=parameters,
1197
+ curvature_coefficients=curvature_coefficients,
1198
+ vorticity_stencil=vorticity_stencil,
1199
+ density_stencil=density_stencil,
1200
+ potential_stencil=potential_stencil,
1201
+ pressure_stencil=pressure_stencil,
1202
+ current_density_stencil=current_density_stencil,
1203
+ vorticity_conservative_stencil=vorticity_conservative_stencil,
1204
+ vorticity_face_bc=vorticity_face_bc,
1205
+ vorticity_cut_wall_geometry=vorticity_cut_wall_geometry,
1206
+ vorticity_cut_wall_bc=vorticity_cut_wall_bc,
1207
+ periodic_axes=periodic_axes,
1208
+ )
1209
+ Ve_rhs = _Ve_rhs(
1210
+ Ve=Ve,
1211
+ density=density,
1212
+ Te=Te,
1213
+ geometry=geometry,
1214
+ parameters=parameters,
1215
+ curvature_coefficients=curvature_coefficients,
1216
+ Ve_stencil=Ve_stencil,
1217
+ density_stencil=density_stencil,
1218
+ potential_stencil=potential_stencil,
1219
+ Pe_stencil=Pe_stencil,
1220
+ Te_stencil=temperature_stencil,
1221
+ current_density=current_density,
1222
+ Ve_conservative_stencil=Ve_conservative_stencil,
1223
+ Ve_face_bc=electron_velocity_parallel_face_bc,
1224
+ Ve_cut_wall_geometry=electron_velocity_parallel_cut_wall_geometry,
1225
+ Ve_cut_wall_bc=electron_velocity_parallel_cut_wall_bc,
1226
+ periodic_axes=periodic_axes,
1227
+ )
1228
+
1229
+ rhs = FciDrbEBState(
1230
+ density=density_rhs,
1231
+ phi=phi_state,
1232
+ Te=Te_rhs,
1233
+ Ti=Ti_rhs,
1234
+ Vi=Vi_rhs,
1235
+ Ve=Ve_rhs,
1236
+ vorticity=vorticity_rhs,
1237
+ )
1238
+
1239
+ return FciDrbEBRhsResult(
1240
+ rhs=rhs,
1241
+ potential=potential,
1242
+ potential_residual_l2=potential_residual_l2,
1243
+ )