dkx 2.0.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.
- dkx-2.0.0/LICENSE +21 -0
- dkx-2.0.0/PKG-INFO +293 -0
- dkx-2.0.0/README.md +263 -0
- dkx-2.0.0/dkx/__init__.py +216 -0
- dkx-2.0.0/dkx/__main__.py +8 -0
- dkx-2.0.0/dkx/ambipolar.py +334 -0
- dkx-2.0.0/dkx/api.py +601 -0
- dkx-2.0.0/dkx/batch.py +509 -0
- dkx-2.0.0/dkx/bounce_averaged.py +523 -0
- dkx-2.0.0/dkx/cli.py +1362 -0
- dkx-2.0.0/dkx/collisions.py +1451 -0
- dkx-2.0.0/dkx/compare.py +871 -0
- dkx-2.0.0/dkx/console.py +355 -0
- dkx-2.0.0/dkx/constants.py +185 -0
- dkx-2.0.0/dkx/drift_kinetic.py +2473 -0
- dkx-2.0.0/dkx/er.py +881 -0
- dkx-2.0.0/dkx/impurity.py +606 -0
- dkx-2.0.0/dkx/input_compat.py +460 -0
- dkx-2.0.0/dkx/inputs.py +621 -0
- dkx-2.0.0/dkx/io.py +337 -0
- dkx-2.0.0/dkx/magnetic_geometry.py +1946 -0
- dkx-2.0.0/dkx/moments.py +1328 -0
- dkx-2.0.0/dkx/momentum_correction.py +529 -0
- dkx-2.0.0/dkx/monoenergetic.py +980 -0
- dkx-2.0.0/dkx/namelist.py +207 -0
- dkx-2.0.0/dkx/paths.py +102 -0
- dkx-2.0.0/dkx/phase_space.py +1532 -0
- dkx-2.0.0/dkx/phi1.py +479 -0
- dkx-2.0.0/dkx/plotting.py +266 -0
- dkx-2.0.0/dkx/profiling.py +177 -0
- dkx-2.0.0/dkx/run.py +817 -0
- dkx-2.0.0/dkx/sensitivity.py +801 -0
- dkx-2.0.0/dkx/shaing_callen.py +286 -0
- dkx-2.0.0/dkx/solve.py +1798 -0
- dkx-2.0.0/dkx/solver_trace.py +161 -0
- dkx-2.0.0/dkx/species.py +261 -0
- dkx-2.0.0/dkx/validation/__init__.py +5 -0
- dkx-2.0.0/dkx/validation/artifacts.py +3346 -0
- dkx-2.0.0/dkx/validation/data_fetch.py +193 -0
- dkx-2.0.0/dkx/validation/equilibria_manifest.json +39 -0
- dkx-2.0.0/dkx/validation/fortran.py +345 -0
- dkx-2.0.0/dkx/validation/release.py +2347 -0
- dkx-2.0.0/dkx/variational.py +283 -0
- dkx-2.0.0/dkx/workflows/__init__.py +23 -0
- dkx-2.0.0/dkx/workflows/geometry_adapters.py +963 -0
- dkx-2.0.0/dkx/workflows/optimization.py +2021 -0
- dkx-2.0.0/dkx/workflows/scans.py +392 -0
- dkx-2.0.0/dkx/writer.py +1595 -0
- dkx-2.0.0/dkx/xgrid.py +271 -0
- dkx-2.0.0/dkx.egg-info/PKG-INFO +293 -0
- dkx-2.0.0/dkx.egg-info/SOURCES.txt +179 -0
- dkx-2.0.0/dkx.egg-info/dependency_links.txt +1 -0
- dkx-2.0.0/dkx.egg-info/entry_points.txt +2 -0
- dkx-2.0.0/dkx.egg-info/requires.txt +20 -0
- dkx-2.0.0/dkx.egg-info/top_level.txt +1 -0
- dkx-2.0.0/pyproject.toml +63 -0
- dkx-2.0.0/setup.cfg +4 -0
- dkx-2.0.0/tests/test_api_contracts.py +348 -0
- dkx-2.0.0/tests/test_audit_suite_output_keys.py +84 -0
- dkx-2.0.0/tests/test_audit_suite_runtime_drift.py +80 -0
- dkx-2.0.0/tests/test_batch.py +423 -0
- dkx-2.0.0/tests/test_benchmark_artifact_policy.py +586 -0
- dkx-2.0.0/tests/test_benchmark_doc_claims.py +274 -0
- dkx-2.0.0/tests/test_bounce_averaged.py +276 -0
- dkx-2.0.0/tests/test_classical_transport_physics_gates.py +76 -0
- dkx-2.0.0/tests/test_cli_solve_mode.py +1170 -0
- dkx-2.0.0/tests/test_cli_validation_io_fast_coverage.py +369 -0
- dkx-2.0.0/tests/test_collision_physics_gates.py +372 -0
- dkx-2.0.0/tests/test_collisionality_artifact.py +118 -0
- dkx-2.0.0/tests/test_collisions_improved_sugama.py +306 -0
- dkx-2.0.0/tests/test_compare_reference_corruption.py +929 -0
- dkx-2.0.0/tests/test_create_production_benchmark_inputs.py +372 -0
- dkx-2.0.0/tests/test_data_fetch.py +411 -0
- dkx-2.0.0/tests/test_domain_package_import_contracts.py +151 -0
- dkx-2.0.0/tests/test_drift_kinetic.py +501 -0
- dkx-2.0.0/tests/test_er.py +267 -0
- dkx-2.0.0/tests/test_er_scan_and_ambipolar.py +313 -0
- dkx-2.0.0/tests/test_er_trajectory_sweep.py +179 -0
- dkx-2.0.0/tests/test_er_trajectory_sweep_artifact.py +101 -0
- dkx-2.0.0/tests/test_example_qa_bootstrap.py +248 -0
- dkx-2.0.0/tests/test_examples_optimization.py +236 -0
- dkx-2.0.0/tests/test_examples_pedagogic.py +101 -0
- dkx-2.0.0/tests/test_examples_tree_contract.py +459 -0
- dkx-2.0.0/tests/test_examples_tutorials.py +65 -0
- dkx-2.0.0/tests/test_examples_workflow_browser.py +80 -0
- dkx-2.0.0/tests/test_export_f_canonical_parity.py +97 -0
- dkx-2.0.0/tests/test_finite_beta_vmec_example.py +855 -0
- dkx-2.0.0/tests/test_fokker_planck_phi1_reduces_to_no_phi1.py +74 -0
- dkx-2.0.0/tests/test_fortran_mpi_wrapper.py +101 -0
- dkx-2.0.0/tests/test_generate_autodiff_sensitivity_validation.py +57 -0
- dkx-2.0.0/tests/test_generate_fortran_suite_benchmark_summary.py +420 -0
- dkx-2.0.0/tests/test_generate_readme_fast_branch_audit.py +124 -0
- dkx-2.0.0/tests/test_generate_sfincs_paper_figs.py +706 -0
- dkx-2.0.0/tests/test_generate_validation_dashboard.py +46 -0
- dkx-2.0.0/tests/test_generate_w7x_high_nu_performance.py +109 -0
- dkx-2.0.0/tests/test_geometry_scheme11_parity.py +57 -0
- dkx-2.0.0/tests/test_getting_started_examples.py +157 -0
- dkx-2.0.0/tests/test_gpu_solver_path_artifacts.py +76 -0
- dkx-2.0.0/tests/test_h5_parity.py +199 -0
- dkx-2.0.0/tests/test_helper_module_coverage.py +376 -0
- dkx-2.0.0/tests/test_impurity.py +261 -0
- dkx-2.0.0/tests/test_input_compat.py +471 -0
- dkx-2.0.0/tests/test_inputs_console.py +351 -0
- dkx-2.0.0/tests/test_jax_geometry_adapters.py +790 -0
- dkx-2.0.0/tests/test_kinetic_operator_fortran_parity.py +232 -0
- dkx-2.0.0/tests/test_magnetic_drifts_parity.py +237 -0
- dkx-2.0.0/tests/test_magnetic_geometry.py +303 -0
- dkx-2.0.0/tests/test_momentum_correction.py +372 -0
- dkx-2.0.0/tests/test_monoenergetic_database.py +410 -0
- dkx-2.0.0/tests/test_namelist.py +124 -0
- dkx-2.0.0/tests/test_optimization_comparison.py +193 -0
- dkx-2.0.0/tests/test_optimization_evidence.py +80 -0
- dkx-2.0.0/tests/test_optimization_ladder.py +135 -0
- dkx-2.0.0/tests/test_optimization_neoclassical_objectives.py +183 -0
- dkx-2.0.0/tests/test_optimization_promotion.py +119 -0
- dkx-2.0.0/tests/test_optimization_public_scripts_cli.py +338 -0
- dkx-2.0.0/tests/test_optimization_real_artifacts.py +207 -0
- dkx-2.0.0/tests/test_optimization_wave3_coverage.py +134 -0
- dkx-2.0.0/tests/test_optimization_workflow.py +216 -0
- dkx-2.0.0/tests/test_output_coordinate_physics_gates.py +84 -0
- dkx-2.0.0/tests/test_output_formats.py +139 -0
- dkx-2.0.0/tests/test_output_h5_constraintscheme34_parity.py +101 -0
- dkx-2.0.0/tests/test_output_h5_magdrift_schemes_parity.py +100 -0
- dkx-2.0.0/tests/test_output_h5_nonstelsym_vmec_scheme5_parity.py +97 -0
- dkx-2.0.0/tests/test_output_h5_scheme11_parity.py +153 -0
- dkx-2.0.0/tests/test_output_h5_scheme13_parity.py +148 -0
- dkx-2.0.0/tests/test_output_h5_scheme1_parity.py +129 -0
- dkx-2.0.0/tests/test_output_h5_scheme2_parity.py +83 -0
- dkx-2.0.0/tests/test_output_h5_scheme4_parity.py +182 -0
- dkx-2.0.0/tests/test_output_h5_scheme4_quick2species_parity.py +140 -0
- dkx-2.0.0/tests/test_output_h5_scheme5_parity.py +150 -0
- dkx-2.0.0/tests/test_output_h5_xgrid_schemes_parity.py +97 -0
- dkx-2.0.0/tests/test_output_key_coverage_regressions.py +61 -0
- dkx-2.0.0/tests/test_output_npz_canonical_parity.py +113 -0
- dkx-2.0.0/tests/test_package_metadata.py +13 -0
- dkx-2.0.0/tests/test_paper_benchmark_bootstrap_loop.py +128 -0
- dkx-2.0.0/tests/test_paper_benchmark_electron_root.py +134 -0
- dkx-2.0.0/tests/test_paper_benchmark_monoenergetic.py +366 -0
- dkx-2.0.0/tests/test_paper_benchmark_w7x_ambipolar.py +119 -0
- dkx-2.0.0/tests/test_phase_space.py +394 -0
- dkx-2.0.0/tests/test_phase_timing.py +25 -0
- dkx-2.0.0/tests/test_phi1.py +549 -0
- dkx-2.0.0/tests/test_plotting.py +36 -0
- dkx-2.0.0/tests/test_profile_write_output_trace.py +322 -0
- dkx-2.0.0/tests/test_profiling.py +160 -0
- dkx-2.0.0/tests/test_profiling_helpers.py +177 -0
- dkx-2.0.0/tests/test_public_docs_wording_contract.py +92 -0
- dkx-2.0.0/tests/test_public_facades_and_paths.py +81 -0
- dkx-2.0.0/tests/test_reference_goldens_lightweight.py +74 -0
- dkx-2.0.0/tests/test_release_gate_metadata.py +102 -0
- dkx-2.0.0/tests/test_remaining_public_contracts.py +279 -0
- dkx-2.0.0/tests/test_repo_size_policy.py +23 -0
- dkx-2.0.0/tests/test_research_lane_policy.py +229 -0
- dkx-2.0.0/tests/test_rhsmode1_current_closure.py +62 -0
- dkx-2.0.0/tests/test_rhsmode1_phi1_write_output_end_to_end.py +126 -0
- dkx-2.0.0/tests/test_rhsmode1_write_output_end_to_end.py +132 -0
- dkx-2.0.0/tests/test_run_rhsmode1.py +315 -0
- dkx-2.0.0/tests/test_run_transport.py +201 -0
- dkx-2.0.0/tests/test_runtime_helper_coverage.py +64 -0
- dkx-2.0.0/tests/test_scans_progress_and_recycle.py +332 -0
- dkx-2.0.0/tests/test_sensitivity.py +748 -0
- dkx-2.0.0/tests/test_shaing_callen.py +243 -0
- dkx-2.0.0/tests/test_solve.py +878 -0
- dkx-2.0.0/tests/test_solver_path_artifacts.py +399 -0
- dkx-2.0.0/tests/test_solver_trace.py +120 -0
- dkx-2.0.0/tests/test_solver_trace_canonical_parity.py +76 -0
- dkx-2.0.0/tests/test_source_tree_consolidation.py +715 -0
- dkx-2.0.0/tests/test_species_constants.py +301 -0
- dkx-2.0.0/tests/test_transport_matrix_write_output_end_to_end.py +181 -0
- dkx-2.0.0/tests/test_upstream_scanplot2_smoke.py +58 -0
- dkx-2.0.0/tests/test_utils_smoke.py +79 -0
- dkx-2.0.0/tests/test_validation_artifacts.py +761 -0
- dkx-2.0.0/tests/test_validation_deferred_lane_gates.py +103 -0
- dkx-2.0.0/tests/test_validation_figures.py +721 -0
- dkx-2.0.0/tests/test_validation_manifest_schema.py +108 -0
- dkx-2.0.0/tests/test_validation_math.py +199 -0
- dkx-2.0.0/tests/test_validation_petsc_and_upstream_helpers.py +438 -0
- dkx-2.0.0/tests/test_validation_policy_coverage.py +209 -0
- dkx-2.0.0/tests/test_variational_bounds.py +141 -0
- dkx-2.0.0/tests/test_vmec_wout_conventions.py +321 -0
- dkx-2.0.0/tests/test_vmex_workflow.py +166 -0
dkx-2.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 UW Plasma
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
dkx-2.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dkx
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: Neoclassical transport solver with CPU/GPU and differentiable JAX workflows
|
|
5
|
+
Project-URL: Homepage, https://github.com/uwplasma/DKX
|
|
6
|
+
Project-URL: Documentation, https://sfincs-jax.readthedocs.io/
|
|
7
|
+
Project-URL: Repository, https://github.com/uwplasma/DKX
|
|
8
|
+
Project-URL: Issues, https://github.com/uwplasma/DKX/issues
|
|
9
|
+
Requires-Python: >=3.10
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Requires-Dist: jax
|
|
13
|
+
Requires-Dist: numpy
|
|
14
|
+
Requires-Dist: scipy
|
|
15
|
+
Requires-Dist: h5py
|
|
16
|
+
Requires-Dist: netCDF4
|
|
17
|
+
Requires-Dist: matplotlib
|
|
18
|
+
Requires-Dist: solvax
|
|
19
|
+
Provides-Extra: structured
|
|
20
|
+
Requires-Dist: solvax; extra == "structured"
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: pytest; extra == "dev"
|
|
23
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
24
|
+
Requires-Dist: pytest-xdist; extra == "dev"
|
|
25
|
+
Requires-Dist: pytest-split; extra == "dev"
|
|
26
|
+
Provides-Extra: docs
|
|
27
|
+
Requires-Dist: sphinx; extra == "docs"
|
|
28
|
+
Requires-Dist: sphinx-rtd-theme; extra == "docs"
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
|
|
31
|
+
# DKX
|
|
32
|
+
|
|
33
|
+
[](https://pypi.org/project/dkx/)
|
|
34
|
+
[](https://github.com/uwplasma/DKX/actions/workflows/ci.yml)
|
|
35
|
+
[](https://sfincs-jax.readthedocs.io/en/latest/)
|
|
36
|
+
[](LICENSE)
|
|
37
|
+
[](https://www.python.org/downloads/)
|
|
38
|
+
|
|
39
|
+
**DKX** solves the radially local, linearized drift-kinetic equation on a flux
|
|
40
|
+
surface — the same physics as [SFINCS Fortran v3](https://github.com/landreman/sfincs) —
|
|
41
|
+
in pure JAX. One `input.namelist` plus one geometry file gives neoclassical
|
|
42
|
+
particle/heat fluxes, parallel flows, bootstrap current, and transport matrices
|
|
43
|
+
for stellarators and tokamaks, on CPU or GPU. Every output is pinned
|
|
44
|
+
field-by-field against SFINCS Fortran v3, and the whole solve is differentiable:
|
|
45
|
+
`jax.grad` of any output with respect to any input, by implicit differentiation.
|
|
46
|
+
|
|
47
|
+

|
|
48
|
+
|
|
49
|
+
*Every canonical module admitted against the reference implementation at pinned
|
|
50
|
+
tolerances that run in CI: fluxes, flows, bootstrap current, transport matrices,
|
|
51
|
+
collisions, geometry, and console prints all match SFINCS Fortran v3 to the
|
|
52
|
+
envelopes shown.*
|
|
53
|
+
|
|
54
|
+
## Install
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pip install dkx
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The solver tiers (block-tridiagonal Legendre elimination, recycled GCROT,
|
|
61
|
+
implicit differentiation) live in the external
|
|
62
|
+
[`solvax`](https://pypi.org/project/solvax/) library, which installs
|
|
63
|
+
automatically as a core dependency. For GPU, add the matching CUDA build of JAX,
|
|
64
|
+
e.g. `pip install -U "jax[cuda12]"`. Large public equilibria (W7-X, HSX) are
|
|
65
|
+
fetched from a GitHub release on first use and cached under `~/.cache/dkx/data`
|
|
66
|
+
(prefetch with `python -m dkx.validation.data_fetch`; see the
|
|
67
|
+
[installation docs](docs/installation.rst) for offline options).
|
|
68
|
+
|
|
69
|
+
## Quickstart
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
dkx input.namelist --out sfincsOutput.h5 # solve, write SFINCS-keyed HDF5/NetCDF
|
|
73
|
+
dkx --plot sfincsOutput.h5 # PDF diagnostics panel
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The same solve from Python (mirrors
|
|
77
|
+
[`examples/getting_started/run_tokamak.py`](examples/getting_started/run_tokamak.py),
|
|
78
|
+
which also builds the namelist from dicts and plots the result):
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
from pathlib import Path
|
|
82
|
+
from dkx.run import run_profile
|
|
83
|
+
|
|
84
|
+
deck = Path("input.namelist")
|
|
85
|
+
deck.write_text("""\
|
|
86
|
+
&geometryParameters
|
|
87
|
+
geometryScheme = 1 ! circular tokamak: BHat = 1 + 0.1 cos(theta)
|
|
88
|
+
inputRadialCoordinate = 3
|
|
89
|
+
rN_wish = 0.3
|
|
90
|
+
B0OverBBar = 1.0 GHat = 1.0 IHat = 0.0 iota = 1.31
|
|
91
|
+
epsilon_t = 0.1 epsilon_h = 0.0 psiAHat = 0.045 aHat = 0.1
|
|
92
|
+
/
|
|
93
|
+
&speciesParameters
|
|
94
|
+
Zs = 1 mHats = 1.0 nHats = 1.0 THats = 0.5
|
|
95
|
+
dNHatdrHats = -6.0 dTHatdrHats = -3.0
|
|
96
|
+
/
|
|
97
|
+
&physicsParameters
|
|
98
|
+
Delta = 4.5694d-3 alpha = 1.0 nu_n = 8.4774d-3
|
|
99
|
+
Er = 0.0 collisionOperator = 1 ! pitch-angle scattering
|
|
100
|
+
/
|
|
101
|
+
&resolutionParameters
|
|
102
|
+
Ntheta = 15 Nzeta = 1 Nxi = 8 NL = 4 Nx = 6
|
|
103
|
+
solverTolerance = 1d-10
|
|
104
|
+
/
|
|
105
|
+
""")
|
|
106
|
+
|
|
107
|
+
run = run_profile(deck, solve_method="auto", out_path=Path("sfincsOutput.h5"))
|
|
108
|
+
print("particle flux:", float(run.moments["particleFlux_vm_psiHat"][0]))
|
|
109
|
+
print("bootstrap current <j.B>:", float(run.moments["FSABjHat"]))
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
`run_profile` prints the Fortran-parity console flow, writes `sfincsOutput.h5`/`.nc`
|
|
113
|
+
keyed by the SFINCS output names, and returns the state, solver statistics, and
|
|
114
|
+
all moments in memory.
|
|
115
|
+
|
|
116
|
+
## Parity with SFINCS Fortran v3
|
|
117
|
+
|
|
118
|
+
Every canonical module is admitted against the reference implementation —
|
|
119
|
+
Fortran golden outputs, tiny-grid PETSc matrix dumps, or the retained legacy
|
|
120
|
+
path — at pinned tolerances that run in CI (the envelope figure at the top of
|
|
121
|
+
this page). Outputs, per-species result tables, and console prints match SFINCS
|
|
122
|
+
Fortran v3 field-by-field. The scheme-1 monoenergetic `transportMatrix[0,1]`
|
|
123
|
+
element is pinned to upstream's expected value because that element is
|
|
124
|
+
tolerance-unstable in the Fortran build itself; the DKX direct solve reproduces
|
|
125
|
+
the expected value to 4.2e-6 by construction.
|
|
126
|
+
|
|
127
|
+
| Capability | dkx | SFINCS Fortran v3 |
|
|
128
|
+
| --- | :---: | :---: |
|
|
129
|
+
| RHSMode 1/2/3 (fluxes, flows, bootstrap current, transport matrices) | ✅ | ✅ |
|
|
130
|
+
| Pitch-angle + full Fokker-Planck (Rosenbluth) collisions | ✅ | ✅ |
|
|
131
|
+
| Geometry: analytic 1-4, VMEC 5, Boozer `.bc` 11/12, namelist spectrum 13; non-symmetric (`lasym`) | ✅ | ✅ |
|
|
132
|
+
| `Phi1`/quasineutrality; Tangential magnetic drifts; `export_f` output | ✅ | ✅ |
|
|
133
|
+
| Ambipolar radial-electric-field root solve | ✅ | ✅ |
|
|
134
|
+
| Exact gradients of any output w.r.t. any input (`jax.grad`, implicit differentiation) | ✅ | ❌ |
|
|
135
|
+
| GPU execution; warm starts + Krylov recycling across scans | ✅ | ❌ |
|
|
136
|
+
| Variational upper/lower transport bounds (convergence certificates) | ✅ | ❌ |
|
|
137
|
+
| MPI multi-node execution | ❌ (single-node multicore + GPU) | ✅ |
|
|
138
|
+
|
|
139
|
+
The full matrix — including the JAX-only research capabilities (momentum-conserving
|
|
140
|
+
flow corrections, an extended-collisionality Sugama operator, monoenergetic
|
|
141
|
+
database mode, batched GPU scans, a bounce-averaged 1/ν surrogate) — lives in
|
|
142
|
+
[docs/feature_matrix.rst](docs/feature_matrix.rst).
|
|
143
|
+
|
|
144
|
+
*Reproduce with the drivers in [`examples/parity/`](examples/parity/).*
|
|
145
|
+
|
|
146
|
+
## Fast on CPU and GPU
|
|
147
|
+
|
|
148
|
+

|
|
149
|
+
|
|
150
|
+
Measured head-to-head on the same machine (MacBook, Apple M4, 24 GB) and the
|
|
151
|
+
same deck: `HSX_PASCollisions_DKESTrajectories`, RHSMode=1, at
|
|
152
|
+
`Ntheta=25, Nzeta=51, Nxi=100, Nx=5` — **744,610 unknowns**. The Fortran
|
|
153
|
+
reference is the conda PETSc 3.23 + MUMPS 5.8.2 build of SFINCS v3.
|
|
154
|
+
|
|
155
|
+
- With the matched `Nxi`-for-`x` ramp discretization, DKX solves in
|
|
156
|
+
**27.2 s at 0.93 GB** — 17x faster than 1-rank Fortran (463.6 s, 3.98 GB) and
|
|
157
|
+
8.4x faster than Fortran's best measured parallel floor (229.5 s / 2.86 GB at
|
|
158
|
+
2 ranks), at roughly 30% of the memory. With uniform `Nxi` it takes 44.3 s at
|
|
159
|
+
1.16 GB; an RTX A4000 GPU takes 45.0 s (the Legendre scan is serial and A4000
|
|
160
|
+
FP64 is 1/32 rate).
|
|
161
|
+
- A cross-machine sweep on the two-species production variant (1,275,010
|
|
162
|
+
unknowns) repeats the shape: one DKX process beats every measured MPI
|
|
163
|
+
configuration — 3.1x the laptop's best on CPU, 13.6x the workstation's best on
|
|
164
|
+
its GPU. At the full production resolution (2.5 M unknowns) neither code fits a
|
|
165
|
+
global sparse factorization in 24 GB, and the truncated Legendre elimination is
|
|
166
|
+
the locally viable direct path (~0.3 GB vs ~91 GB for the full-band factor).
|
|
167
|
+
- The direct solve is more converged than the Fortran reference: Fortran's own
|
|
168
|
+
electron `FSABFlow` scatters 51% across its 1/2/4/8-rank runs (Krylov solver
|
|
169
|
+
noise), while DKX matches the closest Fortran run to 2e-10.
|
|
170
|
+
|
|
171
|
+
Scope: this is **one measured 744k-unknown HSX PAS case**; further cases are
|
|
172
|
+
promoted here as each vertical slice lands with its own evidence. Full tables,
|
|
173
|
+
provenance, and known issues: [docs/performance.rst](docs/performance.rst);
|
|
174
|
+
regenerate with `python tools/benchmarks/tier1_hsx_head_to_head.py` and
|
|
175
|
+
`python tools/benchmarks/readme_figures.py`.
|
|
176
|
+
|
|
177
|
+
## Differentiable optimization
|
|
178
|
+
|
|
179
|
+

|
|
180
|
+
|
|
181
|
+
One `jax.value_and_grad` differentiates the whole physics chain — boundary
|
|
182
|
+
Fourier modes through the fixed-boundary MHD equilibrium (implicit adjoint), the
|
|
183
|
+
differentiable Boozer transform, and the drift-kinetic solve — to the bootstrap
|
|
184
|
+
current, with no finite differences. The flagship run shapes a genuine
|
|
185
|
+
quasi-axisymmetric stellarator and then lowers its bootstrap current at held
|
|
186
|
+
quasisymmetry, warm-starting the kinetic Krylov solve across optimizer
|
|
187
|
+
iterations so each evaluation is a few seconds.
|
|
188
|
+
|
|
189
|
+
*Reproduce with `python examples/optimization/optimize_QA_bootstrap.py` (needs
|
|
190
|
+
the optional `vmex` + `booz_xform_jax` companions).*
|
|
191
|
+
|
|
192
|
+
## Monoenergetic (ICNTS) benchmarks
|
|
193
|
+
|
|
194
|
+

|
|
195
|
+
|
|
196
|
+
ICNTS-style monoenergetic coefficients (`D11*`, `D31*` versus collisionality at
|
|
197
|
+
several `EStar`) on the W7-X, TJ-II, and HSX standard configurations, each with
|
|
198
|
+
matched-deck SFINCS Fortran v3 cross-check points at solver precision. The
|
|
199
|
+
quasi-helically symmetric HSX case shows the suppressed 1/ν branch that W7-X and
|
|
200
|
+
TJ-II retain.
|
|
201
|
+
|
|
202
|
+
*Reproduce with `python examples/paper_benchmarks/monoenergetic_icnts_w7x.py`
|
|
203
|
+
(and the `_tjii` / `_hsx` companions).*
|
|
204
|
+
|
|
205
|
+
## Low-collisionality bootstrap convergence
|
|
206
|
+
|
|
207
|
+

|
|
208
|
+
|
|
209
|
+
The hard low-collisionality test: the bootstrap coefficient `D31*` on the W7-X
|
|
210
|
+
standard configuration scanned to `nuPrime = 3e-4`, approaching the collisionless
|
|
211
|
+
Shaing-Callen asymptote. At `EStar = 0` the coefficient keeps deepening past the
|
|
212
|
+
asymptote — the 1/ν-regime offset does not decay without orbit precession — while
|
|
213
|
+
a small finite `EStar` detaches below `nuPrime ~ 1e-3` and flattens back toward
|
|
214
|
+
the asymptote, the E×B-precession dip.
|
|
215
|
+
|
|
216
|
+
*Reproduce with `python examples/paper_benchmarks/shaing_callen_convergence.py`.*
|
|
217
|
+
|
|
218
|
+
## Ambipolar Er and electron roots
|
|
219
|
+
|
|
220
|
+

|
|
221
|
+
|
|
222
|
+
Validated against a real published W7-X core-electron-root-confinement discharge
|
|
223
|
+
[Pablant et al., *Phys. Plasmas* 25, 022508 (2018)]: DKX resolves every ambipolar
|
|
224
|
+
root of `J_r(E_r)`, classifies each as ion / unstable / electron by the `dJr/dEr`
|
|
225
|
+
sign, and follows the physical branch by radial continuity to reproduce the
|
|
226
|
+
electron-root → ion-root crossover near `ρ ~ 0.6`, matching the reference `E_r`
|
|
227
|
+
within the digitization uncertainty (~1.5 kV/m mean difference).
|
|
228
|
+
|
|
229
|
+
*Reproduce with `python examples/paper_benchmarks/w7x_ambipolar_er.py` and
|
|
230
|
+
`python examples/paper_benchmarks/electron_root_optimization.py`.*
|
|
231
|
+
|
|
232
|
+
## Impurity transport
|
|
233
|
+
|
|
234
|
+

|
|
235
|
+
|
|
236
|
+
Classical and neoclassical transport of a high-Z trace impurity in a hydrogenic
|
|
237
|
+
bulk, anchored by a Fortran-parity check on the committed carbon two-species deck
|
|
238
|
+
(neoclassical impurity flux to 1.5e-6 relative). The temperature-screening
|
|
239
|
+
diagnostic recovers the exact `-Z` density-peaking coefficient and the classical
|
|
240
|
+
1/2 collisional screening coefficient, with an autodiff ion-temperature-gradient
|
|
241
|
+
derivative verified against finite differences.
|
|
242
|
+
|
|
243
|
+
*Reproduce with `python examples/paper_benchmarks/impurity_transport.py`.*
|
|
244
|
+
|
|
245
|
+
## Kinetic-in-the-loop bootstrap
|
|
246
|
+
|
|
247
|
+

|
|
248
|
+
|
|
249
|
+
A self-consistent finite-β precise-QA equilibrium with the full drift-kinetic
|
|
250
|
+
solve inside the Picard loop, in place of the Redl analytic proxy, converging in
|
|
251
|
+
7 damped iterations. At the converged state the analytic proxy over-predicts the
|
|
252
|
+
kinetic bootstrap current by a few percent across the interior profile — the
|
|
253
|
+
error the kinetic-in-the-loop iteration removes by construction — and one
|
|
254
|
+
`jax.value_and_grad` differentiates the total bootstrap current through the
|
|
255
|
+
equilibrium → Boozer → kinetic chain.
|
|
256
|
+
|
|
257
|
+
*Reproduce with `python examples/paper_benchmarks/bootstrap_consistency_kinetic_loop.py`
|
|
258
|
+
(needs the optional `vmex` + `booz_xform_jax` companions).*
|
|
259
|
+
|
|
260
|
+
## Examples
|
|
261
|
+
|
|
262
|
+
Six pedagogic scripts on the canonical API sit at the top of
|
|
263
|
+
[`examples/`](examples/) — parameters at the top, printed progress, a plot, and
|
|
264
|
+
output files written and read back. The wider tree (tutorial notebooks,
|
|
265
|
+
parity/benchmark drivers, upstream SFINCS decks) is mapped in the navigable
|
|
266
|
+
[`examples/README.md`](examples/README.md).
|
|
267
|
+
|
|
268
|
+
## Documentation
|
|
269
|
+
|
|
270
|
+
Full documentation — installation, quickstart, the equations solved, namelist
|
|
271
|
+
and output references, API, and measured performance/validation notes — at
|
|
272
|
+
[sfincs-jax.readthedocs.io](https://sfincs-jax.readthedocs.io/).
|
|
273
|
+
|
|
274
|
+
## Known issues
|
|
275
|
+
|
|
276
|
+
- `Nxi_for_x` ramps embed the truncated degrees of freedom as identity-pinned
|
|
277
|
+
rows in the matrix-free operator (the Fortran code packs them out of its
|
|
278
|
+
matrix). The direct tier solves each `(species, x)` subsystem with its own
|
|
279
|
+
packed Legendre count — the exact Fortran discretization — and gradients
|
|
280
|
+
through the ramped route match finite differences to 1e-6 relative in the
|
|
281
|
+
regression tests; every solve raises at execution time if a forward or adjoint
|
|
282
|
+
solve fails to converge.
|
|
283
|
+
- The scheme-1 monoenergetic `transportMatrix[0,1]` element is ill-conditioned
|
|
284
|
+
in the upstream configuration itself, so parity for it is pinned to upstream's
|
|
285
|
+
expected value. Near-singular structured eliminations (for example a
|
|
286
|
+
collisionless `nu_n = 0` deck) fall back automatically from the direct tier to
|
|
287
|
+
the preconditioned Krylov tier.
|
|
288
|
+
|
|
289
|
+
## License
|
|
290
|
+
|
|
291
|
+
MIT. See [LICENSE](LICENSE). If you use DKX in published work, please cite this
|
|
292
|
+
repository and the SFINCS drift-kinetic formulation
|
|
293
|
+
[Landreman et al., *Phys. Plasmas* 21, 042503 (2014)].
|
dkx-2.0.0/README.md
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
# DKX
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/dkx/)
|
|
4
|
+
[](https://github.com/uwplasma/DKX/actions/workflows/ci.yml)
|
|
5
|
+
[](https://sfincs-jax.readthedocs.io/en/latest/)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](https://www.python.org/downloads/)
|
|
8
|
+
|
|
9
|
+
**DKX** solves the radially local, linearized drift-kinetic equation on a flux
|
|
10
|
+
surface — the same physics as [SFINCS Fortran v3](https://github.com/landreman/sfincs) —
|
|
11
|
+
in pure JAX. One `input.namelist` plus one geometry file gives neoclassical
|
|
12
|
+
particle/heat fluxes, parallel flows, bootstrap current, and transport matrices
|
|
13
|
+
for stellarators and tokamaks, on CPU or GPU. Every output is pinned
|
|
14
|
+
field-by-field against SFINCS Fortran v3, and the whole solve is differentiable:
|
|
15
|
+
`jax.grad` of any output with respect to any input, by implicit differentiation.
|
|
16
|
+
|
|
17
|
+

|
|
18
|
+
|
|
19
|
+
*Every canonical module admitted against the reference implementation at pinned
|
|
20
|
+
tolerances that run in CI: fluxes, flows, bootstrap current, transport matrices,
|
|
21
|
+
collisions, geometry, and console prints all match SFINCS Fortran v3 to the
|
|
22
|
+
envelopes shown.*
|
|
23
|
+
|
|
24
|
+
## Install
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pip install dkx
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The solver tiers (block-tridiagonal Legendre elimination, recycled GCROT,
|
|
31
|
+
implicit differentiation) live in the external
|
|
32
|
+
[`solvax`](https://pypi.org/project/solvax/) library, which installs
|
|
33
|
+
automatically as a core dependency. For GPU, add the matching CUDA build of JAX,
|
|
34
|
+
e.g. `pip install -U "jax[cuda12]"`. Large public equilibria (W7-X, HSX) are
|
|
35
|
+
fetched from a GitHub release on first use and cached under `~/.cache/dkx/data`
|
|
36
|
+
(prefetch with `python -m dkx.validation.data_fetch`; see the
|
|
37
|
+
[installation docs](docs/installation.rst) for offline options).
|
|
38
|
+
|
|
39
|
+
## Quickstart
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
dkx input.namelist --out sfincsOutput.h5 # solve, write SFINCS-keyed HDF5/NetCDF
|
|
43
|
+
dkx --plot sfincsOutput.h5 # PDF diagnostics panel
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The same solve from Python (mirrors
|
|
47
|
+
[`examples/getting_started/run_tokamak.py`](examples/getting_started/run_tokamak.py),
|
|
48
|
+
which also builds the namelist from dicts and plots the result):
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
from pathlib import Path
|
|
52
|
+
from dkx.run import run_profile
|
|
53
|
+
|
|
54
|
+
deck = Path("input.namelist")
|
|
55
|
+
deck.write_text("""\
|
|
56
|
+
&geometryParameters
|
|
57
|
+
geometryScheme = 1 ! circular tokamak: BHat = 1 + 0.1 cos(theta)
|
|
58
|
+
inputRadialCoordinate = 3
|
|
59
|
+
rN_wish = 0.3
|
|
60
|
+
B0OverBBar = 1.0 GHat = 1.0 IHat = 0.0 iota = 1.31
|
|
61
|
+
epsilon_t = 0.1 epsilon_h = 0.0 psiAHat = 0.045 aHat = 0.1
|
|
62
|
+
/
|
|
63
|
+
&speciesParameters
|
|
64
|
+
Zs = 1 mHats = 1.0 nHats = 1.0 THats = 0.5
|
|
65
|
+
dNHatdrHats = -6.0 dTHatdrHats = -3.0
|
|
66
|
+
/
|
|
67
|
+
&physicsParameters
|
|
68
|
+
Delta = 4.5694d-3 alpha = 1.0 nu_n = 8.4774d-3
|
|
69
|
+
Er = 0.0 collisionOperator = 1 ! pitch-angle scattering
|
|
70
|
+
/
|
|
71
|
+
&resolutionParameters
|
|
72
|
+
Ntheta = 15 Nzeta = 1 Nxi = 8 NL = 4 Nx = 6
|
|
73
|
+
solverTolerance = 1d-10
|
|
74
|
+
/
|
|
75
|
+
""")
|
|
76
|
+
|
|
77
|
+
run = run_profile(deck, solve_method="auto", out_path=Path("sfincsOutput.h5"))
|
|
78
|
+
print("particle flux:", float(run.moments["particleFlux_vm_psiHat"][0]))
|
|
79
|
+
print("bootstrap current <j.B>:", float(run.moments["FSABjHat"]))
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
`run_profile` prints the Fortran-parity console flow, writes `sfincsOutput.h5`/`.nc`
|
|
83
|
+
keyed by the SFINCS output names, and returns the state, solver statistics, and
|
|
84
|
+
all moments in memory.
|
|
85
|
+
|
|
86
|
+
## Parity with SFINCS Fortran v3
|
|
87
|
+
|
|
88
|
+
Every canonical module is admitted against the reference implementation —
|
|
89
|
+
Fortran golden outputs, tiny-grid PETSc matrix dumps, or the retained legacy
|
|
90
|
+
path — at pinned tolerances that run in CI (the envelope figure at the top of
|
|
91
|
+
this page). Outputs, per-species result tables, and console prints match SFINCS
|
|
92
|
+
Fortran v3 field-by-field. The scheme-1 monoenergetic `transportMatrix[0,1]`
|
|
93
|
+
element is pinned to upstream's expected value because that element is
|
|
94
|
+
tolerance-unstable in the Fortran build itself; the DKX direct solve reproduces
|
|
95
|
+
the expected value to 4.2e-6 by construction.
|
|
96
|
+
|
|
97
|
+
| Capability | dkx | SFINCS Fortran v3 |
|
|
98
|
+
| --- | :---: | :---: |
|
|
99
|
+
| RHSMode 1/2/3 (fluxes, flows, bootstrap current, transport matrices) | ✅ | ✅ |
|
|
100
|
+
| Pitch-angle + full Fokker-Planck (Rosenbluth) collisions | ✅ | ✅ |
|
|
101
|
+
| Geometry: analytic 1-4, VMEC 5, Boozer `.bc` 11/12, namelist spectrum 13; non-symmetric (`lasym`) | ✅ | ✅ |
|
|
102
|
+
| `Phi1`/quasineutrality; Tangential magnetic drifts; `export_f` output | ✅ | ✅ |
|
|
103
|
+
| Ambipolar radial-electric-field root solve | ✅ | ✅ |
|
|
104
|
+
| Exact gradients of any output w.r.t. any input (`jax.grad`, implicit differentiation) | ✅ | ❌ |
|
|
105
|
+
| GPU execution; warm starts + Krylov recycling across scans | ✅ | ❌ |
|
|
106
|
+
| Variational upper/lower transport bounds (convergence certificates) | ✅ | ❌ |
|
|
107
|
+
| MPI multi-node execution | ❌ (single-node multicore + GPU) | ✅ |
|
|
108
|
+
|
|
109
|
+
The full matrix — including the JAX-only research capabilities (momentum-conserving
|
|
110
|
+
flow corrections, an extended-collisionality Sugama operator, monoenergetic
|
|
111
|
+
database mode, batched GPU scans, a bounce-averaged 1/ν surrogate) — lives in
|
|
112
|
+
[docs/feature_matrix.rst](docs/feature_matrix.rst).
|
|
113
|
+
|
|
114
|
+
*Reproduce with the drivers in [`examples/parity/`](examples/parity/).*
|
|
115
|
+
|
|
116
|
+
## Fast on CPU and GPU
|
|
117
|
+
|
|
118
|
+

|
|
119
|
+
|
|
120
|
+
Measured head-to-head on the same machine (MacBook, Apple M4, 24 GB) and the
|
|
121
|
+
same deck: `HSX_PASCollisions_DKESTrajectories`, RHSMode=1, at
|
|
122
|
+
`Ntheta=25, Nzeta=51, Nxi=100, Nx=5` — **744,610 unknowns**. The Fortran
|
|
123
|
+
reference is the conda PETSc 3.23 + MUMPS 5.8.2 build of SFINCS v3.
|
|
124
|
+
|
|
125
|
+
- With the matched `Nxi`-for-`x` ramp discretization, DKX solves in
|
|
126
|
+
**27.2 s at 0.93 GB** — 17x faster than 1-rank Fortran (463.6 s, 3.98 GB) and
|
|
127
|
+
8.4x faster than Fortran's best measured parallel floor (229.5 s / 2.86 GB at
|
|
128
|
+
2 ranks), at roughly 30% of the memory. With uniform `Nxi` it takes 44.3 s at
|
|
129
|
+
1.16 GB; an RTX A4000 GPU takes 45.0 s (the Legendre scan is serial and A4000
|
|
130
|
+
FP64 is 1/32 rate).
|
|
131
|
+
- A cross-machine sweep on the two-species production variant (1,275,010
|
|
132
|
+
unknowns) repeats the shape: one DKX process beats every measured MPI
|
|
133
|
+
configuration — 3.1x the laptop's best on CPU, 13.6x the workstation's best on
|
|
134
|
+
its GPU. At the full production resolution (2.5 M unknowns) neither code fits a
|
|
135
|
+
global sparse factorization in 24 GB, and the truncated Legendre elimination is
|
|
136
|
+
the locally viable direct path (~0.3 GB vs ~91 GB for the full-band factor).
|
|
137
|
+
- The direct solve is more converged than the Fortran reference: Fortran's own
|
|
138
|
+
electron `FSABFlow` scatters 51% across its 1/2/4/8-rank runs (Krylov solver
|
|
139
|
+
noise), while DKX matches the closest Fortran run to 2e-10.
|
|
140
|
+
|
|
141
|
+
Scope: this is **one measured 744k-unknown HSX PAS case**; further cases are
|
|
142
|
+
promoted here as each vertical slice lands with its own evidence. Full tables,
|
|
143
|
+
provenance, and known issues: [docs/performance.rst](docs/performance.rst);
|
|
144
|
+
regenerate with `python tools/benchmarks/tier1_hsx_head_to_head.py` and
|
|
145
|
+
`python tools/benchmarks/readme_figures.py`.
|
|
146
|
+
|
|
147
|
+
## Differentiable optimization
|
|
148
|
+
|
|
149
|
+

|
|
150
|
+
|
|
151
|
+
One `jax.value_and_grad` differentiates the whole physics chain — boundary
|
|
152
|
+
Fourier modes through the fixed-boundary MHD equilibrium (implicit adjoint), the
|
|
153
|
+
differentiable Boozer transform, and the drift-kinetic solve — to the bootstrap
|
|
154
|
+
current, with no finite differences. The flagship run shapes a genuine
|
|
155
|
+
quasi-axisymmetric stellarator and then lowers its bootstrap current at held
|
|
156
|
+
quasisymmetry, warm-starting the kinetic Krylov solve across optimizer
|
|
157
|
+
iterations so each evaluation is a few seconds.
|
|
158
|
+
|
|
159
|
+
*Reproduce with `python examples/optimization/optimize_QA_bootstrap.py` (needs
|
|
160
|
+
the optional `vmex` + `booz_xform_jax` companions).*
|
|
161
|
+
|
|
162
|
+
## Monoenergetic (ICNTS) benchmarks
|
|
163
|
+
|
|
164
|
+

|
|
165
|
+
|
|
166
|
+
ICNTS-style monoenergetic coefficients (`D11*`, `D31*` versus collisionality at
|
|
167
|
+
several `EStar`) on the W7-X, TJ-II, and HSX standard configurations, each with
|
|
168
|
+
matched-deck SFINCS Fortran v3 cross-check points at solver precision. The
|
|
169
|
+
quasi-helically symmetric HSX case shows the suppressed 1/ν branch that W7-X and
|
|
170
|
+
TJ-II retain.
|
|
171
|
+
|
|
172
|
+
*Reproduce with `python examples/paper_benchmarks/monoenergetic_icnts_w7x.py`
|
|
173
|
+
(and the `_tjii` / `_hsx` companions).*
|
|
174
|
+
|
|
175
|
+
## Low-collisionality bootstrap convergence
|
|
176
|
+
|
|
177
|
+

|
|
178
|
+
|
|
179
|
+
The hard low-collisionality test: the bootstrap coefficient `D31*` on the W7-X
|
|
180
|
+
standard configuration scanned to `nuPrime = 3e-4`, approaching the collisionless
|
|
181
|
+
Shaing-Callen asymptote. At `EStar = 0` the coefficient keeps deepening past the
|
|
182
|
+
asymptote — the 1/ν-regime offset does not decay without orbit precession — while
|
|
183
|
+
a small finite `EStar` detaches below `nuPrime ~ 1e-3` and flattens back toward
|
|
184
|
+
the asymptote, the E×B-precession dip.
|
|
185
|
+
|
|
186
|
+
*Reproduce with `python examples/paper_benchmarks/shaing_callen_convergence.py`.*
|
|
187
|
+
|
|
188
|
+
## Ambipolar Er and electron roots
|
|
189
|
+
|
|
190
|
+

|
|
191
|
+
|
|
192
|
+
Validated against a real published W7-X core-electron-root-confinement discharge
|
|
193
|
+
[Pablant et al., *Phys. Plasmas* 25, 022508 (2018)]: DKX resolves every ambipolar
|
|
194
|
+
root of `J_r(E_r)`, classifies each as ion / unstable / electron by the `dJr/dEr`
|
|
195
|
+
sign, and follows the physical branch by radial continuity to reproduce the
|
|
196
|
+
electron-root → ion-root crossover near `ρ ~ 0.6`, matching the reference `E_r`
|
|
197
|
+
within the digitization uncertainty (~1.5 kV/m mean difference).
|
|
198
|
+
|
|
199
|
+
*Reproduce with `python examples/paper_benchmarks/w7x_ambipolar_er.py` and
|
|
200
|
+
`python examples/paper_benchmarks/electron_root_optimization.py`.*
|
|
201
|
+
|
|
202
|
+
## Impurity transport
|
|
203
|
+
|
|
204
|
+

|
|
205
|
+
|
|
206
|
+
Classical and neoclassical transport of a high-Z trace impurity in a hydrogenic
|
|
207
|
+
bulk, anchored by a Fortran-parity check on the committed carbon two-species deck
|
|
208
|
+
(neoclassical impurity flux to 1.5e-6 relative). The temperature-screening
|
|
209
|
+
diagnostic recovers the exact `-Z` density-peaking coefficient and the classical
|
|
210
|
+
1/2 collisional screening coefficient, with an autodiff ion-temperature-gradient
|
|
211
|
+
derivative verified against finite differences.
|
|
212
|
+
|
|
213
|
+
*Reproduce with `python examples/paper_benchmarks/impurity_transport.py`.*
|
|
214
|
+
|
|
215
|
+
## Kinetic-in-the-loop bootstrap
|
|
216
|
+
|
|
217
|
+

|
|
218
|
+
|
|
219
|
+
A self-consistent finite-β precise-QA equilibrium with the full drift-kinetic
|
|
220
|
+
solve inside the Picard loop, in place of the Redl analytic proxy, converging in
|
|
221
|
+
7 damped iterations. At the converged state the analytic proxy over-predicts the
|
|
222
|
+
kinetic bootstrap current by a few percent across the interior profile — the
|
|
223
|
+
error the kinetic-in-the-loop iteration removes by construction — and one
|
|
224
|
+
`jax.value_and_grad` differentiates the total bootstrap current through the
|
|
225
|
+
equilibrium → Boozer → kinetic chain.
|
|
226
|
+
|
|
227
|
+
*Reproduce with `python examples/paper_benchmarks/bootstrap_consistency_kinetic_loop.py`
|
|
228
|
+
(needs the optional `vmex` + `booz_xform_jax` companions).*
|
|
229
|
+
|
|
230
|
+
## Examples
|
|
231
|
+
|
|
232
|
+
Six pedagogic scripts on the canonical API sit at the top of
|
|
233
|
+
[`examples/`](examples/) — parameters at the top, printed progress, a plot, and
|
|
234
|
+
output files written and read back. The wider tree (tutorial notebooks,
|
|
235
|
+
parity/benchmark drivers, upstream SFINCS decks) is mapped in the navigable
|
|
236
|
+
[`examples/README.md`](examples/README.md).
|
|
237
|
+
|
|
238
|
+
## Documentation
|
|
239
|
+
|
|
240
|
+
Full documentation — installation, quickstart, the equations solved, namelist
|
|
241
|
+
and output references, API, and measured performance/validation notes — at
|
|
242
|
+
[sfincs-jax.readthedocs.io](https://sfincs-jax.readthedocs.io/).
|
|
243
|
+
|
|
244
|
+
## Known issues
|
|
245
|
+
|
|
246
|
+
- `Nxi_for_x` ramps embed the truncated degrees of freedom as identity-pinned
|
|
247
|
+
rows in the matrix-free operator (the Fortran code packs them out of its
|
|
248
|
+
matrix). The direct tier solves each `(species, x)` subsystem with its own
|
|
249
|
+
packed Legendre count — the exact Fortran discretization — and gradients
|
|
250
|
+
through the ramped route match finite differences to 1e-6 relative in the
|
|
251
|
+
regression tests; every solve raises at execution time if a forward or adjoint
|
|
252
|
+
solve fails to converge.
|
|
253
|
+
- The scheme-1 monoenergetic `transportMatrix[0,1]` element is ill-conditioned
|
|
254
|
+
in the upstream configuration itself, so parity for it is pinned to upstream's
|
|
255
|
+
expected value. Near-singular structured eliminations (for example a
|
|
256
|
+
collisionless `nu_n = 0` deck) fall back automatically from the direct tier to
|
|
257
|
+
the preconditioned Krylov tier.
|
|
258
|
+
|
|
259
|
+
## License
|
|
260
|
+
|
|
261
|
+
MIT. See [LICENSE](LICENSE). If you use DKX in published work, please cite this
|
|
262
|
+
repository and the SFINCS drift-kinetic formulation
|
|
263
|
+
[Landreman et al., *Phys. Plasmas* 21, 042503 (2014)].
|