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.
- dkx-2.4.0/PKG-INFO +181 -0
- dkx-2.4.0/README.md +150 -0
- {dkx-2.3.0 → dkx-2.4.0}/pyproject.toml +37 -15
- dkx-2.4.0/src/dkx/__init__.py +184 -0
- dkx-2.4.0/src/dkx/_version.py +3 -0
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/api.py +100 -15
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/batch.py +252 -91
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/bounce_averaged.py +5 -0
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/cli.py +1132 -35
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/coarse_precond.py +506 -162
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/collisions.py +193 -6
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/collocation.py +5 -0
- dkx-2.4.0/src/dkx/config.py +1651 -0
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/drift_kinetic.py +66 -24
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/er.py +295 -106
- dkx-2.4.0/src/dkx/execution.py +1413 -0
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/impurity.py +5 -0
- dkx-2.4.0/src/dkx/input_compat.py +1522 -0
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/inputs.py +188 -1
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/magnetic_geometry.py +148 -16
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/moments.py +206 -37
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/momentum_correction.py +5 -22
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/monoenergetic.py +5 -0
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/multigrid.py +21 -15
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/namelist.py +8 -1
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/paths.py +26 -0
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/phase_space.py +126 -20
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/phi1.py +10 -5
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/plotting.py +305 -3
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/representative.py +625 -141
- dkx-2.4.0/src/dkx/result.py +483 -0
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/run.py +27 -12
- dkx-2.4.0/src/dkx/runtime.py +355 -0
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/sensitivity.py +67 -0
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/solve.py +1079 -353
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/sparse_precond.py +27 -5
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/species.py +5 -0
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/units.py +71 -0
- dkx-2.4.0/src/dkx/validation/__init__.py +17 -0
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/validity.py +69 -0
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/variational.py +5 -0
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/workflows/__init__.py +3 -1
- dkx-2.4.0/src/dkx/workflows/ambipolar_native.py +1174 -0
- dkx-2.4.0/src/dkx/workflows/converge.py +515 -0
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/workflows/geometry_adapters.py +5 -0
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/workflows/optimization.py +13 -0
- dkx-2.4.0/src/dkx/workflows/scan.py +252 -0
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/workflows/scans.py +287 -4
- dkx-2.4.0/src/dkx.egg-info/PKG-INFO +181 -0
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx.egg-info/SOURCES.txt +100 -66
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx.egg-info/requires.txt +4 -5
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_api_contracts.py +79 -40
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_audit_suite_output_keys.py +1 -1
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_audit_suite_runtime_drift.py +1 -1
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_batch.py +284 -103
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_benchmark_artifact_policy.py +1 -1
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_benchmark_doc_claims.py +20 -15
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_bounce_averaged.py +1 -1
- dkx-2.4.0/tests/test_case_config.py +480 -0
- dkx-2.4.0/tests/test_cli_case.py +240 -0
- dkx-2.4.0/tests/test_cli_compare.py +151 -0
- dkx-2.4.0/tests/test_cli_compat_group.py +128 -0
- dkx-2.4.0/tests/test_cli_convert.py +847 -0
- dkx-2.4.0/tests/test_cli_doctor.py +100 -0
- dkx-2.4.0/tests/test_cli_plot.py +340 -0
- dkx-2.4.0/tests/test_cli_roots.py +145 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_cli_solve_mode.py +5 -3
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_cli_validation_io_fast_coverage.py +2 -2
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_coarse_precond_constants.py +1 -1
- dkx-2.4.0/tests/test_coarse_ragged_chains.py +217 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_collision_physics_gates.py +15 -6
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_compilation_cache_is_not_size_capped.py +9 -4
- dkx-2.4.0/tests/test_converge_workflow.py +568 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_create_production_benchmark_inputs.py +1 -1
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_domain_package_import_contracts.py +9 -2
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_drift_kinetic.py +45 -5
- dkx-2.4.0/tests/test_dropped_couplings.py +212 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_environment_guard.py +18 -3
- dkx-2.4.0/tests/test_er.py +800 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_example_docstrings_stay_short.py +8 -8
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_example_qa_bootstrap.py +8 -9
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_examples_optimization.py +2 -2
- dkx-2.4.0/tests/test_examples_pedagogic.py +311 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_examples_tree_contract.py +92 -22
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_examples_workflow_browser.py +7 -2
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_figure_provenance.py +1 -1
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_float64_single_owner.py +61 -9
- dkx-2.4.0/tests/test_fokker_planck_phi1_reduces_to_no_phi1.py +253 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_generate_readme_fast_branch_audit.py +1 -1
- dkx-2.4.0/tests/test_lazy_transposed_precond.py +113 -0
- dkx-2.4.0/tests/test_legendre_tail_diagnostic.py +146 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_magnetic_drift_diagonal.py +1 -1
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_magnetic_geometry.py +51 -1
- dkx-2.4.0/tests/test_math.py +192 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_monoenergetic_database.py +1 -1
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_multigrid.py +3 -3
- dkx-2.4.0/tests/test_namelist_guards.py +361 -0
- dkx-2.4.0/tests/test_native_ambipolar.py +709 -0
- dkx-2.4.0/tests/test_native_execution_result.py +1044 -0
- dkx-2.4.0/tests/test_normalized_radial_electric_field.py +87 -0
- dkx-2.4.0/tests/test_numerics.py +288 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_output_h5_magdrift_schemes_parity.py +2 -2
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_output_h5_scheme13_parity.py +1 -1
- dkx-2.4.0/tests/test_package_metadata.py +40 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_paper_benchmark_monoenergetic.py +1 -1
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_phase_space.py +179 -34
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_phase_timing.py +1 -1
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_phi1.py +1 -1
- dkx-2.4.0/tests/test_physics.py +261 -0
- dkx-2.4.0/tests/test_planning_evidence.py +68 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_profile_write_output_trace.py +1 -1
- dkx-2.4.0/tests/test_readme_budget.py +97 -0
- dkx-2.4.0/tests/test_readme_quickstart_runs.py +110 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_release_gate_metadata.py +2 -2
- dkx-2.4.0/tests/test_release_size_contracts.py +68 -0
- dkx-2.4.0/tests/test_release_workflow_contracts.py +93 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_remaining_public_contracts.py +1 -1
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_repo_size_policy.py +1 -1
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_representative.py +554 -41
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_representative_run_works_when_installed.py +37 -1
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_research_lane_policy.py +2 -2
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_rosenbluth_quadrature.py +81 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_run_rhsmode1.py +7 -7
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_runtime_helper_coverage.py +3 -3
- dkx-2.4.0/tests/test_scan_survives_a_failing_point.py +487 -0
- dkx-2.4.0/tests/test_scan_workflow.py +387 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_scans_progress_and_recycle.py +63 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_sensitivity.py +68 -0
- dkx-2.4.0/tests/test_sfincs_matrix_threshold.py +76 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_shaing_callen.py +1 -1
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_solve.py +563 -78
- dkx-2.4.0/tests/test_source_tree_consolidation.py +314 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_sparse_precond.py +10 -3
- dkx-2.4.0/tests/test_speed_triangle_precond.py +187 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_tier1_refinement.py +3 -3
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_tier2_memory_guard.py +3 -3
- dkx-2.4.0/tests/test_tier2_stall_escalates_instead_of_crashing.py +165 -0
- dkx-2.4.0/tests/test_transport_limits.py +752 -0
- dkx-2.4.0/tests/test_validation.py +2248 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_validation_artifacts.py +74 -2
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_validation_deferred_lane_gates.py +1 -2
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_validation_figures.py +4 -4
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_validation_manifest_schema.py +0 -1
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_validation_math.py +1 -1
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_validation_policy_coverage.py +1 -1
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_variational_bounds.py +1 -1
- dkx-2.4.0/tests/test_wheel_ships_every_package_file.py +155 -0
- dkx-2.3.0/PKG-INFO +0 -374
- dkx-2.3.0/README.md +0 -343
- dkx-2.3.0/dkx/__init__.py +0 -396
- dkx-2.3.0/dkx/input_compat.py +0 -460
- dkx-2.3.0/dkx/validation/__init__.py +0 -5
- dkx-2.3.0/dkx/validation/artifacts.py +0 -837
- dkx-2.3.0/dkx/validation/lanes.py +0 -265
- dkx-2.3.0/dkx/validation/panels.py +0 -941
- dkx-2.3.0/dkx/validation/policy.py +0 -781
- dkx-2.3.0/dkx/validation/release.py +0 -2347
- dkx-2.3.0/dkx/validation/series.py +0 -938
- dkx-2.3.0/dkx.egg-info/PKG-INFO +0 -374
- dkx-2.3.0/tests/test_er.py +0 -267
- dkx-2.3.0/tests/test_examples_pedagogic.py +0 -110
- dkx-2.3.0/tests/test_fokker_planck_phi1_reduces_to_no_phi1.py +0 -74
- dkx-2.3.0/tests/test_package_metadata.py +0 -13
- dkx-2.3.0/tests/test_readme_quickstart_runs.py +0 -53
- dkx-2.3.0/tests/test_source_tree_consolidation.py +0 -715
- {dkx-2.3.0 → dkx-2.4.0}/LICENSE +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/setup.cfg +0 -0
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/__main__.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/ambipolar.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/bootstrap.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/compare.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/console.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/constants.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/io.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/profiling.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/shaing_callen.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/solver_trace.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/validation/data_fetch.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/validation/equilibria_manifest.json +0 -0
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/validation/fortran.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/vmec_ascii.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/writer.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx/xgrid.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx.egg-info/dependency_links.txt +0 -0
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx.egg-info/entry_points.txt +0 -0
- {dkx-2.3.0 → dkx-2.4.0/src}/dkx.egg-info/top_level.txt +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_bootstrap_objective.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_classical_transport_physics_gates.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_collisionality_artifact.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_collisions_improved_sugama.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_collocation.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_compare_reference_corruption.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_data_fetch.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_er_scan_and_ambipolar.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_er_trajectory_sweep.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_er_trajectory_sweep_artifact.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_examples_bootstrap_dkx.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_examples_tutorials.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_export_f_canonical_parity.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_finite_beta_vmec_example.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_fortran_mpi_wrapper.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_generate_autodiff_sensitivity_validation.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_generate_fortran_suite_benchmark_summary.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_generate_sfincs_paper_figs.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_generate_validation_dashboard.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_generate_w7x_high_nu_performance.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_generated_deck_coverage.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_geometry_scheme11_parity.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_getting_started_examples.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_gpu_solver_path_artifacts.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_h5_parity.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_helper_module_coverage.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_impurity.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_input_compat.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_input_namelist_writer.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_inputs_console.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_jax_geometry_adapters.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_kinetic_operator_fortran_parity.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_magnetic_drifts_parity.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_momentum_correction.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_namelist.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_optimization_comparison.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_optimization_evidence.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_optimization_ladder.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_optimization_neoclassical_objectives.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_optimization_promotion.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_optimization_public_scripts_cli.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_optimization_real_artifacts.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_optimization_wave3_coverage.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_optimization_workflow.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_output_coordinate_physics_gates.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_output_formats.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_output_h5_constraintscheme34_parity.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_output_h5_nonstelsym_vmec_scheme5_parity.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_output_h5_scheme11_parity.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_output_h5_scheme1_parity.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_output_h5_scheme2_parity.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_output_h5_scheme4_parity.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_output_h5_scheme4_quick2species_parity.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_output_h5_scheme5_parity.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_output_h5_xgrid_schemes_parity.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_output_key_coverage_regressions.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_output_npz_canonical_parity.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_paper_benchmark_bootstrap_loop.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_paper_benchmark_electron_root.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_paper_benchmark_w7x_ambipolar.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_plot_pdf_paging_is_documented_correctly.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_plotting.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_preconditioner_xi_semantics.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_profiling.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_profiling_helpers.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_public_docs_wording_contract.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_public_facades_and_paths.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_reference_goldens_lightweight.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_rhsmode1_current_closure.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_rhsmode1_phi1_write_output_end_to_end.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_rhsmode1_write_output_end_to_end.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_run_function_is_not_shadowed_by_run_module.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_run_transport.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_solver_path_artifacts.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_solver_trace.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_solver_trace_canonical_parity.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_species_constants.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_transport_matrix_write_output_end_to_end.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_units.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_upstream_scanplot2_smoke.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_utils_smoke.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_validation_petsc_and_upstream_helpers.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_validity.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_vmec_wout_ascii.py +0 -0
- {dkx-2.3.0 → dkx-2.4.0}/tests/test_vmec_wout_conventions.py +0 -0
- {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
|
+
[](https://pypi.org/project/dkx/)
|
|
35
|
+
[](https://github.com/uwplasma/DKX/actions/workflows/ci.yml)
|
|
36
|
+
[](https://sfincs-jax.readthedocs.io/en/latest/)
|
|
37
|
+
[](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
|
+

|
|
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
|
+

|
|
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
|
+

|
|
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
|
+

|
|
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
|
+
[](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
|
+
|
|
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
|
+

|
|
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
|
+

|
|
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
|
+

|
|
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
|
+

|
|
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
|
-
|
|
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
|
+
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
|
-
#
|
|
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.
|
|
28
|
-
# the
|
|
29
|
-
#
|
|
30
|
-
|
|
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
|
-
#
|
|
35
|
-
#
|
|
36
|
-
|
|
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
|
-
"
|
|
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
|
+
]
|