dkx 2.3.0__tar.gz → 2.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (272) hide show
  1. dkx-2.4.0/PKG-INFO +181 -0
  2. dkx-2.4.0/README.md +150 -0
  3. {dkx-2.3.0 → dkx-2.4.0}/pyproject.toml +37 -15
  4. dkx-2.4.0/src/dkx/__init__.py +184 -0
  5. dkx-2.4.0/src/dkx/_version.py +3 -0
  6. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/api.py +100 -15
  7. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/batch.py +252 -91
  8. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/bounce_averaged.py +5 -0
  9. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/cli.py +1132 -35
  10. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/coarse_precond.py +506 -162
  11. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/collisions.py +193 -6
  12. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/collocation.py +5 -0
  13. dkx-2.4.0/src/dkx/config.py +1651 -0
  14. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/drift_kinetic.py +66 -24
  15. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/er.py +295 -106
  16. dkx-2.4.0/src/dkx/execution.py +1413 -0
  17. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/impurity.py +5 -0
  18. dkx-2.4.0/src/dkx/input_compat.py +1522 -0
  19. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/inputs.py +188 -1
  20. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/magnetic_geometry.py +148 -16
  21. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/moments.py +206 -37
  22. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/momentum_correction.py +5 -22
  23. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/monoenergetic.py +5 -0
  24. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/multigrid.py +21 -15
  25. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/namelist.py +8 -1
  26. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/paths.py +26 -0
  27. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/phase_space.py +126 -20
  28. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/phi1.py +10 -5
  29. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/plotting.py +305 -3
  30. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/representative.py +625 -141
  31. dkx-2.4.0/src/dkx/result.py +483 -0
  32. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/run.py +27 -12
  33. dkx-2.4.0/src/dkx/runtime.py +355 -0
  34. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/sensitivity.py +67 -0
  35. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/solve.py +1079 -353
  36. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/sparse_precond.py +27 -5
  37. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/species.py +5 -0
  38. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/units.py +71 -0
  39. dkx-2.4.0/src/dkx/validation/__init__.py +17 -0
  40. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/validity.py +69 -0
  41. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/variational.py +5 -0
  42. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/workflows/__init__.py +3 -1
  43. dkx-2.4.0/src/dkx/workflows/ambipolar_native.py +1174 -0
  44. dkx-2.4.0/src/dkx/workflows/converge.py +515 -0
  45. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/workflows/geometry_adapters.py +5 -0
  46. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/workflows/optimization.py +13 -0
  47. dkx-2.4.0/src/dkx/workflows/scan.py +252 -0
  48. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/workflows/scans.py +287 -4
  49. dkx-2.4.0/src/dkx.egg-info/PKG-INFO +181 -0
  50. {dkx-2.3.0 → dkx-2.4.0/src}/dkx.egg-info/SOURCES.txt +100 -66
  51. {dkx-2.3.0 → dkx-2.4.0/src}/dkx.egg-info/requires.txt +4 -5
  52. {dkx-2.3.0 → dkx-2.4.0}/tests/test_api_contracts.py +79 -40
  53. {dkx-2.3.0 → dkx-2.4.0}/tests/test_audit_suite_output_keys.py +1 -1
  54. {dkx-2.3.0 → dkx-2.4.0}/tests/test_audit_suite_runtime_drift.py +1 -1
  55. {dkx-2.3.0 → dkx-2.4.0}/tests/test_batch.py +284 -103
  56. {dkx-2.3.0 → dkx-2.4.0}/tests/test_benchmark_artifact_policy.py +1 -1
  57. {dkx-2.3.0 → dkx-2.4.0}/tests/test_benchmark_doc_claims.py +20 -15
  58. {dkx-2.3.0 → dkx-2.4.0}/tests/test_bounce_averaged.py +1 -1
  59. dkx-2.4.0/tests/test_case_config.py +480 -0
  60. dkx-2.4.0/tests/test_cli_case.py +240 -0
  61. dkx-2.4.0/tests/test_cli_compare.py +151 -0
  62. dkx-2.4.0/tests/test_cli_compat_group.py +128 -0
  63. dkx-2.4.0/tests/test_cli_convert.py +847 -0
  64. dkx-2.4.0/tests/test_cli_doctor.py +100 -0
  65. dkx-2.4.0/tests/test_cli_plot.py +340 -0
  66. dkx-2.4.0/tests/test_cli_roots.py +145 -0
  67. {dkx-2.3.0 → dkx-2.4.0}/tests/test_cli_solve_mode.py +5 -3
  68. {dkx-2.3.0 → dkx-2.4.0}/tests/test_cli_validation_io_fast_coverage.py +2 -2
  69. {dkx-2.3.0 → dkx-2.4.0}/tests/test_coarse_precond_constants.py +1 -1
  70. dkx-2.4.0/tests/test_coarse_ragged_chains.py +217 -0
  71. {dkx-2.3.0 → dkx-2.4.0}/tests/test_collision_physics_gates.py +15 -6
  72. {dkx-2.3.0 → dkx-2.4.0}/tests/test_compilation_cache_is_not_size_capped.py +9 -4
  73. dkx-2.4.0/tests/test_converge_workflow.py +568 -0
  74. {dkx-2.3.0 → dkx-2.4.0}/tests/test_create_production_benchmark_inputs.py +1 -1
  75. {dkx-2.3.0 → dkx-2.4.0}/tests/test_domain_package_import_contracts.py +9 -2
  76. {dkx-2.3.0 → dkx-2.4.0}/tests/test_drift_kinetic.py +45 -5
  77. dkx-2.4.0/tests/test_dropped_couplings.py +212 -0
  78. {dkx-2.3.0 → dkx-2.4.0}/tests/test_environment_guard.py +18 -3
  79. dkx-2.4.0/tests/test_er.py +800 -0
  80. {dkx-2.3.0 → dkx-2.4.0}/tests/test_example_docstrings_stay_short.py +8 -8
  81. {dkx-2.3.0 → dkx-2.4.0}/tests/test_example_qa_bootstrap.py +8 -9
  82. {dkx-2.3.0 → dkx-2.4.0}/tests/test_examples_optimization.py +2 -2
  83. dkx-2.4.0/tests/test_examples_pedagogic.py +311 -0
  84. {dkx-2.3.0 → dkx-2.4.0}/tests/test_examples_tree_contract.py +92 -22
  85. {dkx-2.3.0 → dkx-2.4.0}/tests/test_examples_workflow_browser.py +7 -2
  86. {dkx-2.3.0 → dkx-2.4.0}/tests/test_figure_provenance.py +1 -1
  87. {dkx-2.3.0 → dkx-2.4.0}/tests/test_float64_single_owner.py +61 -9
  88. dkx-2.4.0/tests/test_fokker_planck_phi1_reduces_to_no_phi1.py +253 -0
  89. {dkx-2.3.0 → dkx-2.4.0}/tests/test_generate_readme_fast_branch_audit.py +1 -1
  90. dkx-2.4.0/tests/test_lazy_transposed_precond.py +113 -0
  91. dkx-2.4.0/tests/test_legendre_tail_diagnostic.py +146 -0
  92. {dkx-2.3.0 → dkx-2.4.0}/tests/test_magnetic_drift_diagonal.py +1 -1
  93. {dkx-2.3.0 → dkx-2.4.0}/tests/test_magnetic_geometry.py +51 -1
  94. dkx-2.4.0/tests/test_math.py +192 -0
  95. {dkx-2.3.0 → dkx-2.4.0}/tests/test_monoenergetic_database.py +1 -1
  96. {dkx-2.3.0 → dkx-2.4.0}/tests/test_multigrid.py +3 -3
  97. dkx-2.4.0/tests/test_namelist_guards.py +361 -0
  98. dkx-2.4.0/tests/test_native_ambipolar.py +709 -0
  99. dkx-2.4.0/tests/test_native_execution_result.py +1044 -0
  100. dkx-2.4.0/tests/test_normalized_radial_electric_field.py +87 -0
  101. dkx-2.4.0/tests/test_numerics.py +288 -0
  102. {dkx-2.3.0 → dkx-2.4.0}/tests/test_output_h5_magdrift_schemes_parity.py +2 -2
  103. {dkx-2.3.0 → dkx-2.4.0}/tests/test_output_h5_scheme13_parity.py +1 -1
  104. dkx-2.4.0/tests/test_package_metadata.py +40 -0
  105. {dkx-2.3.0 → dkx-2.4.0}/tests/test_paper_benchmark_monoenergetic.py +1 -1
  106. {dkx-2.3.0 → dkx-2.4.0}/tests/test_phase_space.py +179 -34
  107. {dkx-2.3.0 → dkx-2.4.0}/tests/test_phase_timing.py +1 -1
  108. {dkx-2.3.0 → dkx-2.4.0}/tests/test_phi1.py +1 -1
  109. dkx-2.4.0/tests/test_physics.py +261 -0
  110. dkx-2.4.0/tests/test_planning_evidence.py +68 -0
  111. {dkx-2.3.0 → dkx-2.4.0}/tests/test_profile_write_output_trace.py +1 -1
  112. dkx-2.4.0/tests/test_readme_budget.py +97 -0
  113. dkx-2.4.0/tests/test_readme_quickstart_runs.py +110 -0
  114. {dkx-2.3.0 → dkx-2.4.0}/tests/test_release_gate_metadata.py +2 -2
  115. dkx-2.4.0/tests/test_release_size_contracts.py +68 -0
  116. dkx-2.4.0/tests/test_release_workflow_contracts.py +93 -0
  117. {dkx-2.3.0 → dkx-2.4.0}/tests/test_remaining_public_contracts.py +1 -1
  118. {dkx-2.3.0 → dkx-2.4.0}/tests/test_repo_size_policy.py +1 -1
  119. {dkx-2.3.0 → dkx-2.4.0}/tests/test_representative.py +554 -41
  120. {dkx-2.3.0 → dkx-2.4.0}/tests/test_representative_run_works_when_installed.py +37 -1
  121. {dkx-2.3.0 → dkx-2.4.0}/tests/test_research_lane_policy.py +2 -2
  122. {dkx-2.3.0 → dkx-2.4.0}/tests/test_rosenbluth_quadrature.py +81 -0
  123. {dkx-2.3.0 → dkx-2.4.0}/tests/test_run_rhsmode1.py +7 -7
  124. {dkx-2.3.0 → dkx-2.4.0}/tests/test_runtime_helper_coverage.py +3 -3
  125. dkx-2.4.0/tests/test_scan_survives_a_failing_point.py +487 -0
  126. dkx-2.4.0/tests/test_scan_workflow.py +387 -0
  127. {dkx-2.3.0 → dkx-2.4.0}/tests/test_scans_progress_and_recycle.py +63 -0
  128. {dkx-2.3.0 → dkx-2.4.0}/tests/test_sensitivity.py +68 -0
  129. dkx-2.4.0/tests/test_sfincs_matrix_threshold.py +76 -0
  130. {dkx-2.3.0 → dkx-2.4.0}/tests/test_shaing_callen.py +1 -1
  131. {dkx-2.3.0 → dkx-2.4.0}/tests/test_solve.py +563 -78
  132. dkx-2.4.0/tests/test_source_tree_consolidation.py +314 -0
  133. {dkx-2.3.0 → dkx-2.4.0}/tests/test_sparse_precond.py +10 -3
  134. dkx-2.4.0/tests/test_speed_triangle_precond.py +187 -0
  135. {dkx-2.3.0 → dkx-2.4.0}/tests/test_tier1_refinement.py +3 -3
  136. {dkx-2.3.0 → dkx-2.4.0}/tests/test_tier2_memory_guard.py +3 -3
  137. dkx-2.4.0/tests/test_tier2_stall_escalates_instead_of_crashing.py +165 -0
  138. dkx-2.4.0/tests/test_transport_limits.py +752 -0
  139. dkx-2.4.0/tests/test_validation.py +2248 -0
  140. {dkx-2.3.0 → dkx-2.4.0}/tests/test_validation_artifacts.py +74 -2
  141. {dkx-2.3.0 → dkx-2.4.0}/tests/test_validation_deferred_lane_gates.py +1 -2
  142. {dkx-2.3.0 → dkx-2.4.0}/tests/test_validation_figures.py +4 -4
  143. {dkx-2.3.0 → dkx-2.4.0}/tests/test_validation_manifest_schema.py +0 -1
  144. {dkx-2.3.0 → dkx-2.4.0}/tests/test_validation_math.py +1 -1
  145. {dkx-2.3.0 → dkx-2.4.0}/tests/test_validation_policy_coverage.py +1 -1
  146. {dkx-2.3.0 → dkx-2.4.0}/tests/test_variational_bounds.py +1 -1
  147. dkx-2.4.0/tests/test_wheel_ships_every_package_file.py +155 -0
  148. dkx-2.3.0/PKG-INFO +0 -374
  149. dkx-2.3.0/README.md +0 -343
  150. dkx-2.3.0/dkx/__init__.py +0 -396
  151. dkx-2.3.0/dkx/input_compat.py +0 -460
  152. dkx-2.3.0/dkx/validation/__init__.py +0 -5
  153. dkx-2.3.0/dkx/validation/artifacts.py +0 -837
  154. dkx-2.3.0/dkx/validation/lanes.py +0 -265
  155. dkx-2.3.0/dkx/validation/panels.py +0 -941
  156. dkx-2.3.0/dkx/validation/policy.py +0 -781
  157. dkx-2.3.0/dkx/validation/release.py +0 -2347
  158. dkx-2.3.0/dkx/validation/series.py +0 -938
  159. dkx-2.3.0/dkx.egg-info/PKG-INFO +0 -374
  160. dkx-2.3.0/tests/test_er.py +0 -267
  161. dkx-2.3.0/tests/test_examples_pedagogic.py +0 -110
  162. dkx-2.3.0/tests/test_fokker_planck_phi1_reduces_to_no_phi1.py +0 -74
  163. dkx-2.3.0/tests/test_package_metadata.py +0 -13
  164. dkx-2.3.0/tests/test_readme_quickstart_runs.py +0 -53
  165. dkx-2.3.0/tests/test_source_tree_consolidation.py +0 -715
  166. {dkx-2.3.0 → dkx-2.4.0}/LICENSE +0 -0
  167. {dkx-2.3.0 → dkx-2.4.0}/setup.cfg +0 -0
  168. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/__main__.py +0 -0
  169. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/ambipolar.py +0 -0
  170. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/bootstrap.py +0 -0
  171. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/compare.py +0 -0
  172. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/console.py +0 -0
  173. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/constants.py +0 -0
  174. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/io.py +0 -0
  175. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/profiling.py +0 -0
  176. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/shaing_callen.py +0 -0
  177. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/solver_trace.py +0 -0
  178. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/validation/data_fetch.py +0 -0
  179. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/validation/equilibria_manifest.json +0 -0
  180. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/validation/fortran.py +0 -0
  181. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/vmec_ascii.py +0 -0
  182. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/writer.py +0 -0
  183. {dkx-2.3.0 → dkx-2.4.0/src}/dkx/xgrid.py +0 -0
  184. {dkx-2.3.0 → dkx-2.4.0/src}/dkx.egg-info/dependency_links.txt +0 -0
  185. {dkx-2.3.0 → dkx-2.4.0/src}/dkx.egg-info/entry_points.txt +0 -0
  186. {dkx-2.3.0 → dkx-2.4.0/src}/dkx.egg-info/top_level.txt +0 -0
  187. {dkx-2.3.0 → dkx-2.4.0}/tests/test_bootstrap_objective.py +0 -0
  188. {dkx-2.3.0 → dkx-2.4.0}/tests/test_classical_transport_physics_gates.py +0 -0
  189. {dkx-2.3.0 → dkx-2.4.0}/tests/test_collisionality_artifact.py +0 -0
  190. {dkx-2.3.0 → dkx-2.4.0}/tests/test_collisions_improved_sugama.py +0 -0
  191. {dkx-2.3.0 → dkx-2.4.0}/tests/test_collocation.py +0 -0
  192. {dkx-2.3.0 → dkx-2.4.0}/tests/test_compare_reference_corruption.py +0 -0
  193. {dkx-2.3.0 → dkx-2.4.0}/tests/test_data_fetch.py +0 -0
  194. {dkx-2.3.0 → dkx-2.4.0}/tests/test_er_scan_and_ambipolar.py +0 -0
  195. {dkx-2.3.0 → dkx-2.4.0}/tests/test_er_trajectory_sweep.py +0 -0
  196. {dkx-2.3.0 → dkx-2.4.0}/tests/test_er_trajectory_sweep_artifact.py +0 -0
  197. {dkx-2.3.0 → dkx-2.4.0}/tests/test_examples_bootstrap_dkx.py +0 -0
  198. {dkx-2.3.0 → dkx-2.4.0}/tests/test_examples_tutorials.py +0 -0
  199. {dkx-2.3.0 → dkx-2.4.0}/tests/test_export_f_canonical_parity.py +0 -0
  200. {dkx-2.3.0 → dkx-2.4.0}/tests/test_finite_beta_vmec_example.py +0 -0
  201. {dkx-2.3.0 → dkx-2.4.0}/tests/test_fortran_mpi_wrapper.py +0 -0
  202. {dkx-2.3.0 → dkx-2.4.0}/tests/test_generate_autodiff_sensitivity_validation.py +0 -0
  203. {dkx-2.3.0 → dkx-2.4.0}/tests/test_generate_fortran_suite_benchmark_summary.py +0 -0
  204. {dkx-2.3.0 → dkx-2.4.0}/tests/test_generate_sfincs_paper_figs.py +0 -0
  205. {dkx-2.3.0 → dkx-2.4.0}/tests/test_generate_validation_dashboard.py +0 -0
  206. {dkx-2.3.0 → dkx-2.4.0}/tests/test_generate_w7x_high_nu_performance.py +0 -0
  207. {dkx-2.3.0 → dkx-2.4.0}/tests/test_generated_deck_coverage.py +0 -0
  208. {dkx-2.3.0 → dkx-2.4.0}/tests/test_geometry_scheme11_parity.py +0 -0
  209. {dkx-2.3.0 → dkx-2.4.0}/tests/test_getting_started_examples.py +0 -0
  210. {dkx-2.3.0 → dkx-2.4.0}/tests/test_gpu_solver_path_artifacts.py +0 -0
  211. {dkx-2.3.0 → dkx-2.4.0}/tests/test_h5_parity.py +0 -0
  212. {dkx-2.3.0 → dkx-2.4.0}/tests/test_helper_module_coverage.py +0 -0
  213. {dkx-2.3.0 → dkx-2.4.0}/tests/test_impurity.py +0 -0
  214. {dkx-2.3.0 → dkx-2.4.0}/tests/test_input_compat.py +0 -0
  215. {dkx-2.3.0 → dkx-2.4.0}/tests/test_input_namelist_writer.py +0 -0
  216. {dkx-2.3.0 → dkx-2.4.0}/tests/test_inputs_console.py +0 -0
  217. {dkx-2.3.0 → dkx-2.4.0}/tests/test_jax_geometry_adapters.py +0 -0
  218. {dkx-2.3.0 → dkx-2.4.0}/tests/test_kinetic_operator_fortran_parity.py +0 -0
  219. {dkx-2.3.0 → dkx-2.4.0}/tests/test_magnetic_drifts_parity.py +0 -0
  220. {dkx-2.3.0 → dkx-2.4.0}/tests/test_momentum_correction.py +0 -0
  221. {dkx-2.3.0 → dkx-2.4.0}/tests/test_namelist.py +0 -0
  222. {dkx-2.3.0 → dkx-2.4.0}/tests/test_optimization_comparison.py +0 -0
  223. {dkx-2.3.0 → dkx-2.4.0}/tests/test_optimization_evidence.py +0 -0
  224. {dkx-2.3.0 → dkx-2.4.0}/tests/test_optimization_ladder.py +0 -0
  225. {dkx-2.3.0 → dkx-2.4.0}/tests/test_optimization_neoclassical_objectives.py +0 -0
  226. {dkx-2.3.0 → dkx-2.4.0}/tests/test_optimization_promotion.py +0 -0
  227. {dkx-2.3.0 → dkx-2.4.0}/tests/test_optimization_public_scripts_cli.py +0 -0
  228. {dkx-2.3.0 → dkx-2.4.0}/tests/test_optimization_real_artifacts.py +0 -0
  229. {dkx-2.3.0 → dkx-2.4.0}/tests/test_optimization_wave3_coverage.py +0 -0
  230. {dkx-2.3.0 → dkx-2.4.0}/tests/test_optimization_workflow.py +0 -0
  231. {dkx-2.3.0 → dkx-2.4.0}/tests/test_output_coordinate_physics_gates.py +0 -0
  232. {dkx-2.3.0 → dkx-2.4.0}/tests/test_output_formats.py +0 -0
  233. {dkx-2.3.0 → dkx-2.4.0}/tests/test_output_h5_constraintscheme34_parity.py +0 -0
  234. {dkx-2.3.0 → dkx-2.4.0}/tests/test_output_h5_nonstelsym_vmec_scheme5_parity.py +0 -0
  235. {dkx-2.3.0 → dkx-2.4.0}/tests/test_output_h5_scheme11_parity.py +0 -0
  236. {dkx-2.3.0 → dkx-2.4.0}/tests/test_output_h5_scheme1_parity.py +0 -0
  237. {dkx-2.3.0 → dkx-2.4.0}/tests/test_output_h5_scheme2_parity.py +0 -0
  238. {dkx-2.3.0 → dkx-2.4.0}/tests/test_output_h5_scheme4_parity.py +0 -0
  239. {dkx-2.3.0 → dkx-2.4.0}/tests/test_output_h5_scheme4_quick2species_parity.py +0 -0
  240. {dkx-2.3.0 → dkx-2.4.0}/tests/test_output_h5_scheme5_parity.py +0 -0
  241. {dkx-2.3.0 → dkx-2.4.0}/tests/test_output_h5_xgrid_schemes_parity.py +0 -0
  242. {dkx-2.3.0 → dkx-2.4.0}/tests/test_output_key_coverage_regressions.py +0 -0
  243. {dkx-2.3.0 → dkx-2.4.0}/tests/test_output_npz_canonical_parity.py +0 -0
  244. {dkx-2.3.0 → dkx-2.4.0}/tests/test_paper_benchmark_bootstrap_loop.py +0 -0
  245. {dkx-2.3.0 → dkx-2.4.0}/tests/test_paper_benchmark_electron_root.py +0 -0
  246. {dkx-2.3.0 → dkx-2.4.0}/tests/test_paper_benchmark_w7x_ambipolar.py +0 -0
  247. {dkx-2.3.0 → dkx-2.4.0}/tests/test_plot_pdf_paging_is_documented_correctly.py +0 -0
  248. {dkx-2.3.0 → dkx-2.4.0}/tests/test_plotting.py +0 -0
  249. {dkx-2.3.0 → dkx-2.4.0}/tests/test_preconditioner_xi_semantics.py +0 -0
  250. {dkx-2.3.0 → dkx-2.4.0}/tests/test_profiling.py +0 -0
  251. {dkx-2.3.0 → dkx-2.4.0}/tests/test_profiling_helpers.py +0 -0
  252. {dkx-2.3.0 → dkx-2.4.0}/tests/test_public_docs_wording_contract.py +0 -0
  253. {dkx-2.3.0 → dkx-2.4.0}/tests/test_public_facades_and_paths.py +0 -0
  254. {dkx-2.3.0 → dkx-2.4.0}/tests/test_reference_goldens_lightweight.py +0 -0
  255. {dkx-2.3.0 → dkx-2.4.0}/tests/test_rhsmode1_current_closure.py +0 -0
  256. {dkx-2.3.0 → dkx-2.4.0}/tests/test_rhsmode1_phi1_write_output_end_to_end.py +0 -0
  257. {dkx-2.3.0 → dkx-2.4.0}/tests/test_rhsmode1_write_output_end_to_end.py +0 -0
  258. {dkx-2.3.0 → dkx-2.4.0}/tests/test_run_function_is_not_shadowed_by_run_module.py +0 -0
  259. {dkx-2.3.0 → dkx-2.4.0}/tests/test_run_transport.py +0 -0
  260. {dkx-2.3.0 → dkx-2.4.0}/tests/test_solver_path_artifacts.py +0 -0
  261. {dkx-2.3.0 → dkx-2.4.0}/tests/test_solver_trace.py +0 -0
  262. {dkx-2.3.0 → dkx-2.4.0}/tests/test_solver_trace_canonical_parity.py +0 -0
  263. {dkx-2.3.0 → dkx-2.4.0}/tests/test_species_constants.py +0 -0
  264. {dkx-2.3.0 → dkx-2.4.0}/tests/test_transport_matrix_write_output_end_to_end.py +0 -0
  265. {dkx-2.3.0 → dkx-2.4.0}/tests/test_units.py +0 -0
  266. {dkx-2.3.0 → dkx-2.4.0}/tests/test_upstream_scanplot2_smoke.py +0 -0
  267. {dkx-2.3.0 → dkx-2.4.0}/tests/test_utils_smoke.py +0 -0
  268. {dkx-2.3.0 → dkx-2.4.0}/tests/test_validation_petsc_and_upstream_helpers.py +0 -0
  269. {dkx-2.3.0 → dkx-2.4.0}/tests/test_validity.py +0 -0
  270. {dkx-2.3.0 → dkx-2.4.0}/tests/test_vmec_wout_ascii.py +0 -0
  271. {dkx-2.3.0 → dkx-2.4.0}/tests/test_vmec_wout_conventions.py +0 -0
  272. {dkx-2.3.0 → dkx-2.4.0}/tests/test_vmex_workflow.py +0 -0
dkx-2.4.0/PKG-INFO ADDED
@@ -0,0 +1,181 @@
1
+ Metadata-Version: 2.4
2
+ Name: dkx
3
+ Version: 2.4.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.11
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Requires-Dist: jax
13
+ Requires-Dist: numpy>=2.1
14
+ Requires-Dist: scipy
15
+ Requires-Dist: h5py
16
+ Requires-Dist: netCDF4
17
+ Requires-Dist: matplotlib
18
+ Requires-Dist: rich>=13.0
19
+ Requires-Dist: solvax>=0.21.0
20
+ Provides-Extra: dev
21
+ Requires-Dist: pytest; extra == "dev"
22
+ Requires-Dist: pytest-cov; extra == "dev"
23
+ Requires-Dist: pytest-xdist; extra == "dev"
24
+ Requires-Dist: pytest-split; extra == "dev"
25
+ Requires-Dist: pypdf; extra == "dev"
26
+ Provides-Extra: docs
27
+ Requires-Dist: sphinx; extra == "docs"
28
+ Requires-Dist: myst-parser; extra == "docs"
29
+ Requires-Dist: furo; extra == "docs"
30
+ Dynamic: license-file
31
+
32
+ # DKX
33
+
34
+ [![PyPI](https://img.shields.io/pypi/v/dkx)](https://pypi.org/project/dkx/)
35
+ [![CI](https://img.shields.io/github/actions/workflow/status/uwplasma/DKX/ci.yml?branch=main&label=ci)](https://github.com/uwplasma/DKX/actions/workflows/ci.yml)
36
+ [![Docs](https://img.shields.io/readthedocs/sfincs-jax?label=docs)](https://sfincs-jax.readthedocs.io/en/latest/)
37
+ [![License](https://img.shields.io/github/license/uwplasma/DKX)](LICENSE)
38
+
39
+ **Differentiable neoclassical transport for stellarators and tokamaks, in JAX.**
40
+
41
+ DKX solves the radially local, linearized drift-kinetic equation on a flux surface
42
+ and returns particle and heat fluxes, parallel flows, bootstrap current, transport
43
+ matrices and ambipolar radial electric fields. It implements the SFINCS Fortran v3
44
+ model, including full Fokker–Planck collisions and Phi1, reads and writes SFINCS decks,
45
+ runs on CPU or GPU, and every output is differentiable in every input.
46
+
47
+ ![W7-X standard configuration: |B| and parallel current density on the boundary, bootstrap current profile, ambipolar Er against Pablant et al. 2018](docs/_static/figures/readme/w7x_showcase.png)
48
+
49
+ ## Install
50
+
51
+ ```bash
52
+ pip install dkx # CPU
53
+ pip install -U "jax[cuda12]" # add for NVIDIA GPUs
54
+ ```
55
+
56
+ ## Run
57
+
58
+ ```python
59
+ import dkx
60
+
61
+ case = dkx.Case.from_mapping({ # analytic tokamak, teaching grid: seconds, not converged
62
+ "schema": 1, "name": "tokamak", "run": {"workflow": "profile", "progress": False},
63
+ "geometry": {"format": "analytic", "file": "tokamak", "surfaces": [0.16, 0.25, 0.36]},
64
+ "species": [{"name": "deuterium", "charge": 1, "mass_amu": 2.014,
65
+ "density_m3": [8.0e19, 7.0e19, 6.0e19], "temperature_keV": [1.0, 0.8, 0.6]}],
66
+ "physics": {"model": "full_local", "collisions": "pitch_angle_scattering", "magnetic_drifts": "dkes", "phi1": "off"},
67
+ "electric_field": {"mode": "prescribed", "value_kV_m": 0.0},
68
+ "resolution": {"theta": 9, "zeta": 1, "pitch": 8, "speed": 4}, "solver": {"method": "auto", "relative_tolerance": 1e-8},
69
+ })
70
+ result = dkx.run(case)
71
+ print("solver route:", result.metadata["solver_route"])
72
+ print("particle flux:", float(result.arrays["particle_flux_m2_s"][1, 0]))
73
+ ```
74
+
75
+ From the shell: `dkx run case.toml`, `dkx converge case.toml` (check the resolution before
76
+ trusting a number), `dkx wout_w7x.nc`, or `dkx input.namelist` for a SFINCS deck.
77
+
78
+ ## Gradients
79
+
80
+ ```python
81
+ import jax, jax.numpy as jnp, dkx
82
+
83
+ case = dkx.Case.from_file("examples/05_ambipolar_profile/case.toml")
84
+ problem = dkx.prepare_er_scan(case, surface_index=1) # geometry, grids, collisions once
85
+
86
+ def bootstrap_current(er_kv_m):
87
+ scan = dkx.batched_er_scan(problem, er_kv_m, differentiable=True, retain_full_state=True)
88
+ return jnp.sum(scan.moments["FSABjHat"])
89
+
90
+ j, dj_der = jax.jit(jax.value_and_grad(bootstrap_current))(jnp.array([-0.2, 0.0, 0.2]))
91
+ ```
92
+
93
+ The derivative passes through the linear solve by the implicit function theorem, and
94
+ every returned state has satisfied the original kinetic equation. Profiles and geometry
95
+ differentiate the same way: [differentiability](docs/differentiability.rst).
96
+
97
+ ## Choose a workflow
98
+
99
+ | Calculation | Start from |
100
+ | --- | --- |
101
+ | Tokamak profile with prescribed `E_r` | `examples/01_tokamak_profile` |
102
+ | Stellarator from VMEC or Boozer files | `examples/02_vmec_stellarator`, `examples/03_boozer_stellarator` |
103
+ | Monoenergetic transport scan | `examples/04_monoenergetic_scan` |
104
+ | Ambipolar roots and branch selection | `examples/05_ambipolar_profile` |
105
+ | Resolution study, gradient checks | `examples/06_convergence_certificate`, `examples/07_gradients` |
106
+ | Geometry sensitivity and descent (analytic proxy) | `examples/08_vmex_optimization` |
107
+ | Phi1 and impurities (expert path) | `examples/09_phi1_and_impurities` |
108
+
109
+ ## Why DKX
110
+
111
+ | | DKX | SFINCS v3 | MONKES | yancc |
112
+ | --- | :---: | :---: | :---: | :---: |
113
+ | Full linearized Fokker–Planck, multispecies | ✅ | ✅ | ❌ | ✅ |
114
+ | Analytic, VMEC, Boozer and `lasym` geometry | ✅ | ✅ | ✅ | ✅ |
115
+ | Phi1 quasineutrality; Tangential magnetic drifts; `export_f` | ✅ | ✅ | ❌ | ❌ |
116
+ | Ambipolar `E_r` root with retained branch evidence | ✅ | ✅ | ❌ | ❌ |
117
+ | Transport matrices (RHSMode 2/3) and SFINCS deck/HDF5 I/O | ✅ | ✅ | ❌ | ❌ |
118
+ | GPU, JIT-compiled scans, Krylov recycling | ✅ | ❌ | ❌ | ✅ |
119
+ | Exact gradients of any output w.r.t. any input | ✅ | adjoint branches | ❌ | claimed |
120
+ | Gradients verified against finite differences | ✅ | | | |
121
+
122
+ ## Verified
123
+
124
+ ![DKX against SFINCS Fortran v3, MONKES and YANCC: scaled differences on matched full Fokker-Planck decks, and Beidler-normalized monoenergetic coefficients](docs/_static/figures/readme/cross_code_validation.png)
125
+
126
+ Against SFINCS Fortran v3 on 38 upstream decks with the same discretization, DKX
127
+ agrees to solver tolerance: median 4e-6, full Fokker–Planck decks to 1e-8. Against the
128
+ independent codes MONKES and YANCC, the four Beidler-normalized monoenergetic coefficients
129
+ agree within 6 percent and `D33` within 0.1 percent on three configurations. Gradients
130
+ agree with central finite differences over a step window on every shipped derivative
131
+ example. Details, tolerances and scope: [validation matrix](docs/validation_matrix.rst).
132
+
133
+ ## Fast
134
+
135
+ ![Runtime and peak memory, DKX against SFINCS Fortran v3, on the 744k-unknown HSX PAS case](docs/_static/figures/readme/tier1_hsx_runtime_memory.png)
136
+
137
+ `HSX_PASCollisions_DKESTrajectories`, RHSMode=1, **744,610 unknowns**, one machine, against the
138
+ PETSc 3.23 / MUMPS 5.8.2 build of SFINCS v3. Warm is the second solve in a process, after XLA has compiled.
139
+
140
+ | Configuration | Warm solve | Peak RSS |
141
+ | --- | ---: | ---: |
142
+ | DKX, `Nxi`-for-`x` ramp | **27.2 s** | **0.93 GB** |
143
+ | DKX, uniform `Nxi` | 44.3 s | 1.16 GB |
144
+ | DKX, RTX A4000 GPU | 45.0 s | — |
145
+ | SFINCS Fortran v3, 1 rank | 463.6 s | 3.98 GB |
146
+ | SFINCS Fortran v3, 2 ranks (its best) | 229.5 s | 2.86 GB |
147
+
148
+ | Cold versus warm, M3 Max CPU | Unknowns | Cold | Warm |
149
+ | --- | ---: | ---: | ---: |
150
+ | HSX PAS reduced | 40,584 | 1.72 s | 0.12 s |
151
+ | HSX PAS, `25x51x100x5` | 744,610 | 23.6 s | 20.0 s |
152
+
153
+ That is **one measured 744k-unknown HSX PAS case**, chosen because DKX has an exact structured
154
+ solver for it. Across all 38 upstream decks: structured route faster on 9 of 9; Krylov route faster
155
+ on 7 of 23, six not completed. Every deck, hardware string and method: [performance](docs/performance.rst).
156
+
157
+ ![Measured parity envelopes of DKX against SFINCS Fortran v3](docs/_static/figures/readme/canonical_parity.png)
158
+
159
+ ## Documentation
160
+
161
+ [Tutorial: first W7-X result](docs/usage.rst) ·
162
+ [How-to: resolution, wout files, SFINCS decks](docs/case_files.rst) ·
163
+ [Reference: schema, CLI, API](docs/api.rst) ·
164
+ [Explanation: physics models, solver routes, limitations](docs/physics_models.rst)
165
+
166
+ ## Cite
167
+
168
+ ```bibtex
169
+ @software{dkx,
170
+ author = {Jorge, Rogerio and contributors},
171
+ title = {DKX: differentiable drift-kinetic neoclassical transport in JAX},
172
+ url = {https://github.com/uwplasma/DKX},
173
+ year = {2026}
174
+ }
175
+ ```
176
+
177
+ Please also cite SFINCS (Landreman, Smith, Mollén & Helander, Phys. Plasmas 21, 042503,
178
+ 2014) when you use its decks or model. Metadata: [CITATION.cff](CITATION.cff).
179
+
180
+ [Examples](examples/) · [Research plan](plan.md) · [Contributing](docs/contributing.rst) ·
181
+ [SOLVAX](https://github.com/uwplasma/SOLVAX) owns the reusable solver algorithms · [LICENSE](LICENSE)
dkx-2.4.0/README.md ADDED
@@ -0,0 +1,150 @@
1
+ # DKX
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/dkx)](https://pypi.org/project/dkx/)
4
+ [![CI](https://img.shields.io/github/actions/workflow/status/uwplasma/DKX/ci.yml?branch=main&label=ci)](https://github.com/uwplasma/DKX/actions/workflows/ci.yml)
5
+ [![Docs](https://img.shields.io/readthedocs/sfincs-jax?label=docs)](https://sfincs-jax.readthedocs.io/en/latest/)
6
+ [![License](https://img.shields.io/github/license/uwplasma/DKX)](LICENSE)
7
+
8
+ **Differentiable neoclassical transport for stellarators and tokamaks, in JAX.**
9
+
10
+ DKX solves the radially local, linearized drift-kinetic equation on a flux surface
11
+ and returns particle and heat fluxes, parallel flows, bootstrap current, transport
12
+ matrices and ambipolar radial electric fields. It implements the SFINCS Fortran v3
13
+ model, including full Fokker–Planck collisions and Phi1, reads and writes SFINCS decks,
14
+ runs on CPU or GPU, and every output is differentiable in every input.
15
+
16
+ ![W7-X standard configuration: |B| and parallel current density on the boundary, bootstrap current profile, ambipolar Er against Pablant et al. 2018](docs/_static/figures/readme/w7x_showcase.png)
17
+
18
+ ## Install
19
+
20
+ ```bash
21
+ pip install dkx # CPU
22
+ pip install -U "jax[cuda12]" # add for NVIDIA GPUs
23
+ ```
24
+
25
+ ## Run
26
+
27
+ ```python
28
+ import dkx
29
+
30
+ case = dkx.Case.from_mapping({ # analytic tokamak, teaching grid: seconds, not converged
31
+ "schema": 1, "name": "tokamak", "run": {"workflow": "profile", "progress": False},
32
+ "geometry": {"format": "analytic", "file": "tokamak", "surfaces": [0.16, 0.25, 0.36]},
33
+ "species": [{"name": "deuterium", "charge": 1, "mass_amu": 2.014,
34
+ "density_m3": [8.0e19, 7.0e19, 6.0e19], "temperature_keV": [1.0, 0.8, 0.6]}],
35
+ "physics": {"model": "full_local", "collisions": "pitch_angle_scattering", "magnetic_drifts": "dkes", "phi1": "off"},
36
+ "electric_field": {"mode": "prescribed", "value_kV_m": 0.0},
37
+ "resolution": {"theta": 9, "zeta": 1, "pitch": 8, "speed": 4}, "solver": {"method": "auto", "relative_tolerance": 1e-8},
38
+ })
39
+ result = dkx.run(case)
40
+ print("solver route:", result.metadata["solver_route"])
41
+ print("particle flux:", float(result.arrays["particle_flux_m2_s"][1, 0]))
42
+ ```
43
+
44
+ From the shell: `dkx run case.toml`, `dkx converge case.toml` (check the resolution before
45
+ trusting a number), `dkx wout_w7x.nc`, or `dkx input.namelist` for a SFINCS deck.
46
+
47
+ ## Gradients
48
+
49
+ ```python
50
+ import jax, jax.numpy as jnp, dkx
51
+
52
+ case = dkx.Case.from_file("examples/05_ambipolar_profile/case.toml")
53
+ problem = dkx.prepare_er_scan(case, surface_index=1) # geometry, grids, collisions once
54
+
55
+ def bootstrap_current(er_kv_m):
56
+ scan = dkx.batched_er_scan(problem, er_kv_m, differentiable=True, retain_full_state=True)
57
+ return jnp.sum(scan.moments["FSABjHat"])
58
+
59
+ j, dj_der = jax.jit(jax.value_and_grad(bootstrap_current))(jnp.array([-0.2, 0.0, 0.2]))
60
+ ```
61
+
62
+ The derivative passes through the linear solve by the implicit function theorem, and
63
+ every returned state has satisfied the original kinetic equation. Profiles and geometry
64
+ differentiate the same way: [differentiability](docs/differentiability.rst).
65
+
66
+ ## Choose a workflow
67
+
68
+ | Calculation | Start from |
69
+ | --- | --- |
70
+ | Tokamak profile with prescribed `E_r` | `examples/01_tokamak_profile` |
71
+ | Stellarator from VMEC or Boozer files | `examples/02_vmec_stellarator`, `examples/03_boozer_stellarator` |
72
+ | Monoenergetic transport scan | `examples/04_monoenergetic_scan` |
73
+ | Ambipolar roots and branch selection | `examples/05_ambipolar_profile` |
74
+ | Resolution study, gradient checks | `examples/06_convergence_certificate`, `examples/07_gradients` |
75
+ | Geometry sensitivity and descent (analytic proxy) | `examples/08_vmex_optimization` |
76
+ | Phi1 and impurities (expert path) | `examples/09_phi1_and_impurities` |
77
+
78
+ ## Why DKX
79
+
80
+ | | DKX | SFINCS v3 | MONKES | yancc |
81
+ | --- | :---: | :---: | :---: | :---: |
82
+ | Full linearized Fokker–Planck, multispecies | ✅ | ✅ | ❌ | ✅ |
83
+ | Analytic, VMEC, Boozer and `lasym` geometry | ✅ | ✅ | ✅ | ✅ |
84
+ | Phi1 quasineutrality; Tangential magnetic drifts; `export_f` | ✅ | ✅ | ❌ | ❌ |
85
+ | Ambipolar `E_r` root with retained branch evidence | ✅ | ✅ | ❌ | ❌ |
86
+ | Transport matrices (RHSMode 2/3) and SFINCS deck/HDF5 I/O | ✅ | ✅ | ❌ | ❌ |
87
+ | GPU, JIT-compiled scans, Krylov recycling | ✅ | ❌ | ❌ | ✅ |
88
+ | Exact gradients of any output w.r.t. any input | ✅ | adjoint branches | ❌ | claimed |
89
+ | Gradients verified against finite differences | ✅ | | | |
90
+
91
+ ## Verified
92
+
93
+ ![DKX against SFINCS Fortran v3, MONKES and YANCC: scaled differences on matched full Fokker-Planck decks, and Beidler-normalized monoenergetic coefficients](docs/_static/figures/readme/cross_code_validation.png)
94
+
95
+ Against SFINCS Fortran v3 on 38 upstream decks with the same discretization, DKX
96
+ agrees to solver tolerance: median 4e-6, full Fokker–Planck decks to 1e-8. Against the
97
+ independent codes MONKES and YANCC, the four Beidler-normalized monoenergetic coefficients
98
+ agree within 6 percent and `D33` within 0.1 percent on three configurations. Gradients
99
+ agree with central finite differences over a step window on every shipped derivative
100
+ example. Details, tolerances and scope: [validation matrix](docs/validation_matrix.rst).
101
+
102
+ ## Fast
103
+
104
+ ![Runtime and peak memory, DKX against SFINCS Fortran v3, on the 744k-unknown HSX PAS case](docs/_static/figures/readme/tier1_hsx_runtime_memory.png)
105
+
106
+ `HSX_PASCollisions_DKESTrajectories`, RHSMode=1, **744,610 unknowns**, one machine, against the
107
+ PETSc 3.23 / MUMPS 5.8.2 build of SFINCS v3. Warm is the second solve in a process, after XLA has compiled.
108
+
109
+ | Configuration | Warm solve | Peak RSS |
110
+ | --- | ---: | ---: |
111
+ | DKX, `Nxi`-for-`x` ramp | **27.2 s** | **0.93 GB** |
112
+ | DKX, uniform `Nxi` | 44.3 s | 1.16 GB |
113
+ | DKX, RTX A4000 GPU | 45.0 s | — |
114
+ | SFINCS Fortran v3, 1 rank | 463.6 s | 3.98 GB |
115
+ | SFINCS Fortran v3, 2 ranks (its best) | 229.5 s | 2.86 GB |
116
+
117
+ | Cold versus warm, M3 Max CPU | Unknowns | Cold | Warm |
118
+ | --- | ---: | ---: | ---: |
119
+ | HSX PAS reduced | 40,584 | 1.72 s | 0.12 s |
120
+ | HSX PAS, `25x51x100x5` | 744,610 | 23.6 s | 20.0 s |
121
+
122
+ That is **one measured 744k-unknown HSX PAS case**, chosen because DKX has an exact structured
123
+ solver for it. Across all 38 upstream decks: structured route faster on 9 of 9; Krylov route faster
124
+ on 7 of 23, six not completed. Every deck, hardware string and method: [performance](docs/performance.rst).
125
+
126
+ ![Measured parity envelopes of DKX against SFINCS Fortran v3](docs/_static/figures/readme/canonical_parity.png)
127
+
128
+ ## Documentation
129
+
130
+ [Tutorial: first W7-X result](docs/usage.rst) ·
131
+ [How-to: resolution, wout files, SFINCS decks](docs/case_files.rst) ·
132
+ [Reference: schema, CLI, API](docs/api.rst) ·
133
+ [Explanation: physics models, solver routes, limitations](docs/physics_models.rst)
134
+
135
+ ## Cite
136
+
137
+ ```bibtex
138
+ @software{dkx,
139
+ author = {Jorge, Rogerio and contributors},
140
+ title = {DKX: differentiable drift-kinetic neoclassical transport in JAX},
141
+ url = {https://github.com/uwplasma/DKX},
142
+ year = {2026}
143
+ }
144
+ ```
145
+
146
+ Please also cite SFINCS (Landreman, Smith, Mollén & Helander, Phys. Plasmas 21, 042503,
147
+ 2014) when you use its decks or model. Metadata: [CITATION.cff](CITATION.cff).
148
+
149
+ [Examples](examples/) · [Research plan](plan.md) · [Contributing](docs/contributing.rst) ·
150
+ [SOLVAX](https://github.com/uwplasma/SOLVAX) owns the reusable solver algorithms · [LICENSE](LICENSE)
@@ -4,10 +4,10 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "dkx"
7
- version = "2.3.0"
7
+ dynamic = ["version"]
8
8
  description = "Neoclassical transport solver with CPU/GPU and differentiable JAX workflows"
9
9
  readme = "README.md"
10
- requires-python = ">=3.10"
10
+ requires-python = ">=3.11"
11
11
  dependencies = [
12
12
  # numpy>=2.1 is not a new constraint -- it is the one jax already carries,
13
13
  # made explicit so the resolver says so. Left implicit, `pip install dkx`
@@ -22,20 +22,25 @@ dependencies = [
22
22
  "h5py",
23
23
  "netCDF4",
24
24
  "matplotlib",
25
- # solvax (PyPI) owns the structured linear-algebra solver tiers (block-Thomas
25
+ # Rich owns every user-facing table, progress bar, and error panel the CLI
26
+ # prints. It is a core dependency, not an extra: `dkx run` without it would
27
+ # have to carry a second, plainer output path, and two formatters drift.
28
+ "rich>=13.0",
29
+ # solvax (PyPI) owns the structured linear-algebra solver routes (block-Thomas
26
30
  # elimination, recycled GCROT, implicit differentiation) that every canonical
27
- # solve uses; it is a core dependency, not optional. 0.13.0 is the floor:
28
- # the tier-2 coarse preconditioner's reusable Schur-LU-only route needs
29
- # block_thomas_factor_fn(..., store_offdiagonals=False), which is new there.
30
- "solvax>=0.13.0",
31
+ # solve uses; it is a core dependency, not optional. 0.21.0 is the floor:
32
+ # its bordered preconditioner applies the coarse inverse once per Krylov
33
+ # iteration instead of twice (SOLVAX #101), and every published DKX Krylov
34
+ # timing since 2026-09-12 was measured with that behaviour. 0.19.0 added the
35
+ # selected-tail generated block solve and exact selected-head reuse used by the
36
+ # opt-in bounded Legendre-tail evidence.
37
+ "solvax>=0.21.0",
31
38
  ]
32
39
 
33
40
  [project.optional-dependencies]
34
- # Retained as a no-op alias so existing `pip install dkx[structured]`
35
- # invocations keep working; solvax is now a core dependency (see above).
36
- structured = [
37
- "solvax>=0.13.0",
38
- ]
41
+ # `structured` is gone. It was an alias for a dependency that has been core
42
+ # since solvax moved into the required list, so installing it changed nothing;
43
+ # pip warns about the unknown extra and installs the same environment.
39
44
  dev = [
40
45
  "pytest",
41
46
  "pytest-cov",
@@ -47,7 +52,8 @@ dev = [
47
52
  ]
48
53
  docs = [
49
54
  "sphinx",
50
- "sphinx-rtd-theme",
55
+ "myst-parser",
56
+ "furo",
51
57
  ]
52
58
 
53
59
  [project.urls]
@@ -60,16 +66,32 @@ Issues = "https://github.com/uwplasma/DKX/issues"
60
66
  dkx = "dkx.cli:main"
61
67
 
62
68
  [tool.setuptools.packages.find]
63
- where = ["."]
69
+ where = ["src"]
64
70
  include = ["dkx*"]
65
71
 
72
+ [tool.setuptools.package-dir]
73
+ "" = "src"
74
+
66
75
  [tool.setuptools.package-data]
67
76
  dkx = ["validation/*.json"]
68
77
 
78
+ [tool.setuptools.dynamic]
79
+ version = {attr = "dkx._version.__version__"}
80
+
81
+ # The coverage shards install the package non-editable, so they record paths
82
+ # under site-packages, while the report job only checks the repository out.
83
+ # Without this mapping `coverage combine` keeps the site-packages paths and the
84
+ # report fails with "No source for code". Listing both tells coverage they are
85
+ # the same code and rewrites the shard data onto the checkout.
86
+ [tool.coverage.paths]
87
+ source = [
88
+ "src/dkx",
89
+ "*/site-packages/dkx",
90
+ ]
91
+
69
92
  [tool.pytest.ini_options]
70
93
  addopts = ["--import-mode=prepend"]
71
94
  testpaths = ["tests"]
72
95
  markers = [
73
96
  "slow: heavy end-to-end example/optimization tests; excluded from the fast coverage shards via -m 'not slow' (still run in the full local suite).",
74
97
  ]
75
-
@@ -0,0 +1,184 @@
1
+ """Differentiable neoclassical transport solvers and SFINCS-style outputs in JAX.
2
+
3
+ The public CLI and Python APIs are maintained as standalone research tools while
4
+ retaining release-gated comparisons against SFINCS Fortran v3 for trust building.
5
+
6
+ Importing this package is inert: it reads no environment, writes none, creates
7
+ no directory, imports no JAX backend, and chooses no thread count. The runtime
8
+ those things configure lives in :mod:`dkx.runtime` and is applied by
9
+ :func:`dkx.runtime.configure`, which the CLI and every solve entry point call
10
+ for you. See plan.md section 6.4.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from ._version import __version__
16
+ from . import runtime
17
+ from .runtime import configure, initialize_distributed_runtime_from_env
18
+
19
+ from .api import ( # noqa: E402
20
+ BenchmarkReport,
21
+ GeometryState,
22
+ GridState,
23
+ OperatorState,
24
+ OutputSchema,
25
+ PreconditionerState,
26
+ SolveInputs,
27
+ SolverOptions,
28
+ SolverResult,
29
+ TransportResult,
30
+ prepare_er_scan,
31
+ batched_er_scan,
32
+ batched_surface_scan,
33
+ read_output,
34
+ run_ambipolar_brent,
35
+ run_monoenergetic_database,
36
+ write_output,
37
+ )
38
+ from .inputs import SfincsInput, load_sfincs_input # noqa: E402
39
+ from .config import ( # noqa: E402
40
+ Case,
41
+ CaseValidationError,
42
+ ConvergenceConfig,
43
+ ElectricFieldConfig,
44
+ GeometryConfig,
45
+ OutputConfig,
46
+ ParallelConfig,
47
+ PhysicsConfig,
48
+ ResolutionConfig,
49
+ RunConfig,
50
+ ScanAxis,
51
+ ScanConfig,
52
+ SolverConfig,
53
+ SpeciesConfig,
54
+ case_json_schema,
55
+ )
56
+ from .result import RESULT_SCHEMA_VERSION, Result # noqa: E402
57
+
58
+ # Heavy flagship entry points (they import the JAX solve stack) are exported
59
+ # lazily via PEP 562 module __getattr__ so `import dkx` stays cheap.
60
+ _LAZY_EXPORTS = {
61
+ "plot": ("dkx.plotting", "plot"),
62
+ "run_profile": ("dkx.run", "run_profile"),
63
+ "run_transport_matrix": ("dkx.run", "run_transport_matrix"),
64
+ "run_from_namelist": ("dkx.run", "run_from_namelist"),
65
+ "batched_solve": ("dkx.batch", "batched_solve"),
66
+ "monoenergetic_database": ("dkx.monoenergetic", "monoenergetic_database"),
67
+ "ambipolar_er": ("dkx.er", "ambipolar_er"),
68
+ "find_ambipolar_er": ("dkx.er", "find_ambipolar_er"),
69
+ "classical_impurity_flux": ("dkx.impurity", "classical_impurity_flux"),
70
+ "build_impurity_plasma": ("dkx.impurity", "build_impurity_plasma"),
71
+ }
72
+
73
+
74
+ def __getattr__(name: str):
75
+ if name == "run":
76
+ return _lazy_run_module()
77
+ try:
78
+ module_name, attr = _LAZY_EXPORTS[name]
79
+ except KeyError:
80
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}") from None
81
+ import importlib # noqa: PLC0415
82
+
83
+ value = getattr(importlib.import_module(module_name), attr)
84
+ globals()[name] = value # cache: subsequent lookups skip __getattr__
85
+ return value
86
+
87
+
88
+ def __dir__() -> list[str]:
89
+ # `annotations` is the __future__ import, not an export. Everything else
90
+ # public is either in __all__ or a submodule the user reached for.
91
+ return sorted((set(globals()) | set(_LAZY_EXPORTS)) - {"annotations"})
92
+
93
+
94
+ # ``dkx/run.py`` is a module and ``dkx.run(case)`` is a call. Rather than pick
95
+ # one -- the function breaks ``dkx.run.run_profile`` and every monkeypatch that
96
+ # targets it by path, the module breaks the call -- ``dkx/run.py`` makes itself
97
+ # callable, so ``dkx.run`` is always the module and always invocable. The name
98
+ # is resolved here rather than listed in _LAZY_EXPORTS because what it yields
99
+ # is the module itself, not an attribute of one.
100
+ def _lazy_run_module():
101
+ import importlib # noqa: PLC0415
102
+
103
+ module = importlib.import_module(f"{__name__}.run")
104
+ globals()["run"] = module
105
+ return module
106
+
107
+
108
+
109
+ def require_float64() -> None:
110
+ """Raise unless JAX is in float64 mode.
111
+
112
+ :func:`dkx.runtime.configure` enables it, and every solve entry point calls
113
+ that; a caller who set ``DKX_NO_X64_SETUP`` or passed ``jax_x64=False`` has
114
+ taken the job on, and this is where they find out if they dropped it. The
115
+ check is a dtype probe rather than a config read because the config can be
116
+ set and then overridden, and what matters is the dtype arrays actually get.
117
+ """
118
+ import jax.numpy as _jnp # noqa: PLC0415
119
+
120
+ if _jnp.zeros(1).dtype != _jnp.float64:
121
+ raise RuntimeError(
122
+ "dkx requires JAX float64: the block eliminations and every parity "
123
+ "fixture depend on it, and single precision changes which results "
124
+ "are trustworthy rather than merely how accurate they are. "
125
+ "Enable it with dkx.runtime.configure() before the first array is "
126
+ "created, or jax.config.update('jax_enable_x64', True), or "
127
+ "JAX_ENABLE_X64=1 in the environment, or unset DKX_NO_X64_SETUP "
128
+ "and let dkx set it."
129
+ )
130
+
131
+ __all__ = [
132
+ "configure",
133
+ "runtime",
134
+ "require_float64",
135
+ "BenchmarkReport",
136
+ "Case",
137
+ "CaseValidationError",
138
+ "ConvergenceConfig",
139
+ "ElectricFieldConfig",
140
+ "GeometryConfig",
141
+ "GeometryState",
142
+ "GridState",
143
+ "OperatorState",
144
+ "OutputSchema",
145
+ "OutputConfig",
146
+ "ParallelConfig",
147
+ "PhysicsConfig",
148
+ "PreconditionerState",
149
+ "RESULT_SCHEMA_VERSION",
150
+ "ResolutionConfig",
151
+ "Result",
152
+ "RunConfig",
153
+ "SfincsInput",
154
+ "SolveInputs",
155
+ "SolverOptions",
156
+ "SolverResult",
157
+ "ScanAxis",
158
+ "ScanConfig",
159
+ "SolverConfig",
160
+ "SpeciesConfig",
161
+ "TransportResult",
162
+ "__version__",
163
+ "ambipolar_er",
164
+ "prepare_er_scan",
165
+ "batched_er_scan",
166
+ "batched_surface_scan",
167
+ "case_json_schema",
168
+ "batched_solve",
169
+ "build_impurity_plasma",
170
+ "classical_impurity_flux",
171
+ "find_ambipolar_er",
172
+ "initialize_distributed_runtime_from_env",
173
+ "load_sfincs_input",
174
+ "monoenergetic_database",
175
+ "read_output",
176
+ "run_ambipolar_brent",
177
+ "run_from_namelist",
178
+ "run_monoenergetic_database",
179
+ "plot",
180
+ "run",
181
+ "run_profile",
182
+ "run_transport_matrix",
183
+ "write_output",
184
+ ]
@@ -0,0 +1,3 @@
1
+ """The single source of truth for the DKX release version."""
2
+
3
+ __version__ = "2.4.0"