gsfit 0.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.
Files changed (166) hide show
  1. gsfit-0.0.0/PKG-INFO +287 -0
  2. gsfit-0.0.0/README.md +255 -0
  3. gsfit-0.0.0/pyproject.toml +80 -0
  4. gsfit-0.0.0/python/diagnostic_and_simulation_base/README.md +268 -0
  5. gsfit-0.0.0/python/diagnostic_and_simulation_base/__init__.py +24 -0
  6. gsfit-0.0.0/python/diagnostic_and_simulation_base/diagnostic_and_simulation_base.py +238 -0
  7. gsfit-0.0.0/python/diagnostic_and_simulation_base/nested_dictionary.py +256 -0
  8. gsfit-0.0.0/python/diagnostic_and_simulation_base/py.typed +0 -0
  9. gsfit-0.0.0/python/diagnostic_and_simulation_base/utility/get_python_library.py +46 -0
  10. gsfit-0.0.0/python/diagnostic_and_simulation_base/utility/get_version.py +44 -0
  11. gsfit-0.0.0/python/diagnostic_and_simulation_base/utility/logging.py +78 -0
  12. gsfit-0.0.0/python/diagnostic_and_simulation_base/utility/make_settings_json.py +65 -0
  13. gsfit-0.0.0/python/diagnostic_and_simulation_base/version_storage.py +15 -0
  14. gsfit-0.0.0/python/gsfit/__init__.py +26 -0
  15. gsfit-0.0.0/python/gsfit/database_readers/__init__.py +30 -0
  16. gsfit-0.0.0/python/gsfit/database_readers/freegs/__init__.py +59 -0
  17. gsfit-0.0.0/python/gsfit/database_readers/freegs/setup_bp_probes.py +97 -0
  18. gsfit-0.0.0/python/gsfit/database_readers/freegs/setup_coils.py +79 -0
  19. gsfit-0.0.0/python/gsfit/database_readers/freegs/setup_dialoop.py +38 -0
  20. gsfit-0.0.0/python/gsfit/database_readers/freegs/setup_flux_loops.py +88 -0
  21. gsfit-0.0.0/python/gsfit/database_readers/freegs/setup_isoflux_boundary_sensors.py +38 -0
  22. gsfit-0.0.0/python/gsfit/database_readers/freegs/setup_isoflux_sensors.py +40 -0
  23. gsfit-0.0.0/python/gsfit/database_readers/freegs/setup_passives.py +128 -0
  24. gsfit-0.0.0/python/gsfit/database_readers/freegs/setup_plasma.py +88 -0
  25. gsfit-0.0.0/python/gsfit/database_readers/freegs/setup_pressure_sensors.py +38 -0
  26. gsfit-0.0.0/python/gsfit/database_readers/freegs/setup_rogowski_coils.py +62 -0
  27. gsfit-0.0.0/python/gsfit/database_readers/freegsnke/__init__.py +59 -0
  28. gsfit-0.0.0/python/gsfit/database_readers/freegsnke/setup_bp_probes.py +95 -0
  29. gsfit-0.0.0/python/gsfit/database_readers/freegsnke/setup_coils.py +112 -0
  30. gsfit-0.0.0/python/gsfit/database_readers/freegsnke/setup_dialoop.py +38 -0
  31. gsfit-0.0.0/python/gsfit/database_readers/freegsnke/setup_flux_loops.py +84 -0
  32. gsfit-0.0.0/python/gsfit/database_readers/freegsnke/setup_isoflux_boundary_sensors.py +38 -0
  33. gsfit-0.0.0/python/gsfit/database_readers/freegsnke/setup_isoflux_sensors.py +40 -0
  34. gsfit-0.0.0/python/gsfit/database_readers/freegsnke/setup_passives.py +38 -0
  35. gsfit-0.0.0/python/gsfit/database_readers/freegsnke/setup_plasma.py +113 -0
  36. gsfit-0.0.0/python/gsfit/database_readers/freegsnke/setup_pressure_sensors.py +38 -0
  37. gsfit-0.0.0/python/gsfit/database_readers/freegsnke/setup_rogowski_coils.py +79 -0
  38. gsfit-0.0.0/python/gsfit/database_readers/interface.py +414 -0
  39. gsfit-0.0.0/python/gsfit/database_readers/st40_astra_mdsplus/__init__.py +22 -0
  40. gsfit-0.0.0/python/gsfit/database_readers/st40_astra_mdsplus/setup_bp_probes.py +89 -0
  41. gsfit-0.0.0/python/gsfit/database_readers/st40_astra_mdsplus/setup_coils.py +269 -0
  42. gsfit-0.0.0/python/gsfit/database_readers/st40_astra_mdsplus/setup_coils_CORRECT.py +251 -0
  43. gsfit-0.0.0/python/gsfit/database_readers/st40_astra_mdsplus/setup_dialoop.py +33 -0
  44. gsfit-0.0.0/python/gsfit/database_readers/st40_astra_mdsplus/setup_flux_loops.py +85 -0
  45. gsfit-0.0.0/python/gsfit/database_readers/st40_astra_mdsplus/setup_isoflux_boundary_sensors.py +33 -0
  46. gsfit-0.0.0/python/gsfit/database_readers/st40_astra_mdsplus/setup_isoflux_sensors.py +34 -0
  47. gsfit-0.0.0/python/gsfit/database_readers/st40_astra_mdsplus/setup_passives.py +124 -0
  48. gsfit-0.0.0/python/gsfit/database_readers/st40_astra_mdsplus/setup_plasma.py +100 -0
  49. gsfit-0.0.0/python/gsfit/database_readers/st40_astra_mdsplus/setup_pressure_sensors.py +32 -0
  50. gsfit-0.0.0/python/gsfit/database_readers/st40_astra_mdsplus/setup_rogowski_coils.py +107 -0
  51. gsfit-0.0.0/python/gsfit/database_readers/st40_mdsplus/__init__.py +59 -0
  52. gsfit-0.0.0/python/gsfit/database_readers/st40_mdsplus/setup_bp_probes.py +76 -0
  53. gsfit-0.0.0/python/gsfit/database_readers/st40_mdsplus/setup_coils.py +85 -0
  54. gsfit-0.0.0/python/gsfit/database_readers/st40_mdsplus/setup_dialoop.py +43 -0
  55. gsfit-0.0.0/python/gsfit/database_readers/st40_mdsplus/setup_flux_loops.py +73 -0
  56. gsfit-0.0.0/python/gsfit/database_readers/st40_mdsplus/setup_isoflux_boundary_sensors.py +33 -0
  57. gsfit-0.0.0/python/gsfit/database_readers/st40_mdsplus/setup_isoflux_sensors.py +116 -0
  58. gsfit-0.0.0/python/gsfit/database_readers/st40_mdsplus/setup_passives.py +123 -0
  59. gsfit-0.0.0/python/gsfit/database_readers/st40_mdsplus/setup_plasma.py +116 -0
  60. gsfit-0.0.0/python/gsfit/database_readers/st40_mdsplus/setup_pressure_sensors.py +32 -0
  61. gsfit-0.0.0/python/gsfit/database_readers/st40_mdsplus/setup_rogowski_coils.py +107 -0
  62. gsfit-0.0.0/python/gsfit/database_writers/__init__.py +28 -0
  63. gsfit-0.0.0/python/gsfit/database_writers/interface.py +14 -0
  64. gsfit-0.0.0/python/gsfit/database_writers/rtgsfit_mdsplus/__init__.py +6 -0
  65. gsfit-0.0.0/python/gsfit/database_writers/rtgsfit_mdsplus/greens_with_boundary_points.py +129 -0
  66. gsfit-0.0.0/python/gsfit/database_writers/rtgsfit_mdsplus/map_results_to_database.py +406 -0
  67. gsfit-0.0.0/python/gsfit/database_writers/rtgsfit_mdsplus/poisson_matrix.py +146 -0
  68. gsfit-0.0.0/python/gsfit/database_writers/tokamak_energy_mdsplus/__init__.py +12 -0
  69. gsfit-0.0.0/python/gsfit/database_writers/tokamak_energy_mdsplus/map_results_to_database.py +174 -0
  70. gsfit-0.0.0/python/gsfit/gsfit.py +282 -0
  71. gsfit-0.0.0/python/gsfit/py.typed +0 -0
  72. gsfit-0.0.0/python/gsfit/settings/default/GSFIT_code_settings.json +101 -0
  73. gsfit-0.0.0/python/gsfit/settings/default/passive_dof_regularisation.json +80 -0
  74. gsfit-0.0.0/python/gsfit/settings/default/sensor_weights_bp_probe.json +274 -0
  75. gsfit-0.0.0/python/gsfit/settings/default/sensor_weights_dialoop.json +3 -0
  76. gsfit-0.0.0/python/gsfit/settings/default/sensor_weights_flux_loops.json +314 -0
  77. gsfit-0.0.0/python/gsfit/settings/default/sensor_weights_isoflux.json +2 -0
  78. gsfit-0.0.0/python/gsfit/settings/default/sensor_weights_pressure.json +18 -0
  79. gsfit-0.0.0/python/gsfit/settings/default/sensor_weights_rogowski_coils.json +106 -0
  80. gsfit-0.0.0/python/gsfit/settings/default/source_function_ff_prime.json +18 -0
  81. gsfit-0.0.0/python/gsfit/settings/default/source_function_p_prime.json +18 -0
  82. gsfit-0.0.0/python/gsfit/settings/mastu_with_synthetic_data_from_freegsnke/GSFIT_code_settings.json +103 -0
  83. gsfit-0.0.0/python/gsfit/settings/mastu_with_synthetic_data_from_freegsnke/passive_dof_regularisation.json +80 -0
  84. gsfit-0.0.0/python/gsfit/settings/mastu_with_synthetic_data_from_freegsnke/sensor_weights_bp_probe.json +1730 -0
  85. gsfit-0.0.0/python/gsfit/settings/mastu_with_synthetic_data_from_freegsnke/sensor_weights_dialoop.json +3 -0
  86. gsfit-0.0.0/python/gsfit/settings/mastu_with_synthetic_data_from_freegsnke/sensor_weights_flux_loops.json +818 -0
  87. gsfit-0.0.0/python/gsfit/settings/mastu_with_synthetic_data_from_freegsnke/sensor_weights_isoflux.json +2 -0
  88. gsfit-0.0.0/python/gsfit/settings/mastu_with_synthetic_data_from_freegsnke/sensor_weights_pressure.json +18 -0
  89. gsfit-0.0.0/python/gsfit/settings/mastu_with_synthetic_data_from_freegsnke/sensor_weights_rogowski_coils.json +10 -0
  90. gsfit-0.0.0/python/gsfit/settings/mastu_with_synthetic_data_from_freegsnke/source_function_ff_prime.json +18 -0
  91. gsfit-0.0.0/python/gsfit/settings/mastu_with_synthetic_data_from_freegsnke/source_function_p_prime.json +18 -0
  92. gsfit-0.0.0/python/gsfit/settings/st40_all_magnetic_sensors_working/GSFIT_code_settings.json +101 -0
  93. gsfit-0.0.0/python/gsfit/settings/st40_all_magnetic_sensors_working/passive_dof_regularisation.json +80 -0
  94. gsfit-0.0.0/python/gsfit/settings/st40_all_magnetic_sensors_working/sensor_weights_bp_probe.json +274 -0
  95. gsfit-0.0.0/python/gsfit/settings/st40_all_magnetic_sensors_working/sensor_weights_dialoop.json +3 -0
  96. gsfit-0.0.0/python/gsfit/settings/st40_all_magnetic_sensors_working/sensor_weights_flux_loops.json +314 -0
  97. gsfit-0.0.0/python/gsfit/settings/st40_all_magnetic_sensors_working/sensor_weights_isoflux.json +2 -0
  98. gsfit-0.0.0/python/gsfit/settings/st40_all_magnetic_sensors_working/sensor_weights_pressure.json +18 -0
  99. gsfit-0.0.0/python/gsfit/settings/st40_all_magnetic_sensors_working/sensor_weights_rogowski_coils.json +106 -0
  100. gsfit-0.0.0/python/gsfit/settings/st40_all_magnetic_sensors_working/source_function_ff_prime.json +18 -0
  101. gsfit-0.0.0/python/gsfit/settings/st40_all_magnetic_sensors_working/source_function_p_prime.json +18 -0
  102. gsfit-0.0.0/python/gsfit/settings/st40_setup_for_rtgsfit/GSFIT_code_settings.json +97 -0
  103. gsfit-0.0.0/python/gsfit/settings/st40_setup_for_rtgsfit/RTGSFIT_code_settings.json +7 -0
  104. gsfit-0.0.0/python/gsfit/settings/st40_setup_for_rtgsfit/passive_dof_regularisation.json +80 -0
  105. gsfit-0.0.0/python/gsfit/settings/st40_setup_for_rtgsfit/rtgsfit_initial_conditions.json +5 -0
  106. gsfit-0.0.0/python/gsfit/settings/st40_setup_for_rtgsfit/sensor_weights_bp_probe.json +274 -0
  107. gsfit-0.0.0/python/gsfit/settings/st40_setup_for_rtgsfit/sensor_weights_dialoop.json +3 -0
  108. gsfit-0.0.0/python/gsfit/settings/st40_setup_for_rtgsfit/sensor_weights_flux_loops.json +314 -0
  109. gsfit-0.0.0/python/gsfit/settings/st40_setup_for_rtgsfit/sensor_weights_isoflux.json +2 -0
  110. gsfit-0.0.0/python/gsfit/settings/st40_setup_for_rtgsfit/sensor_weights_pressure.json +18 -0
  111. gsfit-0.0.0/python/gsfit/settings/st40_setup_for_rtgsfit/sensor_weights_rogowski_coils.json +106 -0
  112. gsfit-0.0.0/python/gsfit/settings/st40_setup_for_rtgsfit/source_function_ff_prime.json +18 -0
  113. gsfit-0.0.0/python/gsfit/settings/st40_setup_for_rtgsfit/source_function_p_prime.json +18 -0
  114. gsfit-0.0.0/python/gsfit/settings/st40_with_synthetic_data_from_astra/GSFIT_code_settings.json +87 -0
  115. gsfit-0.0.0/python/gsfit/settings/st40_with_synthetic_data_from_astra/passive_dof_regularisation.json +80 -0
  116. gsfit-0.0.0/python/gsfit/settings/st40_with_synthetic_data_from_astra/sensor_weights_bp_probe.json +274 -0
  117. gsfit-0.0.0/python/gsfit/settings/st40_with_synthetic_data_from_astra/sensor_weights_dialoop.json +3 -0
  118. gsfit-0.0.0/python/gsfit/settings/st40_with_synthetic_data_from_astra/sensor_weights_flux_loops.json +314 -0
  119. gsfit-0.0.0/python/gsfit/settings/st40_with_synthetic_data_from_astra/sensor_weights_isoflux.json +2 -0
  120. gsfit-0.0.0/python/gsfit/settings/st40_with_synthetic_data_from_astra/sensor_weights_pressure.json +18 -0
  121. gsfit-0.0.0/python/gsfit/settings/st40_with_synthetic_data_from_astra/sensor_weights_rogowski_coils.json +106 -0
  122. gsfit-0.0.0/python/gsfit/settings/st40_with_synthetic_data_from_astra/source_function_ff_prime.json +18 -0
  123. gsfit-0.0.0/python/gsfit/settings/st40_with_synthetic_data_from_astra/source_function_p_prime.json +18 -0
  124. gsfit-0.0.0/python/gsfit_rs/__init__.py +6 -0
  125. gsfit-0.0.0/python/gsfit_rs/gsfit_rs.pyi +644 -0
  126. gsfit-0.0.0/python/gsfit_rs/py.typed +0 -0
  127. gsfit-0.0.0/rust/Cargo.lock +1899 -0
  128. gsfit-0.0.0/rust/Cargo.toml +42 -0
  129. gsfit-0.0.0/rust/rustfmt.toml +2 -0
  130. gsfit-0.0.0/rust/src/bicubic_interpolator.rs +99 -0
  131. gsfit-0.0.0/rust/src/circuit_equations/mod.rs +8 -0
  132. gsfit-0.0.0/rust/src/circuit_equations/solver.rs +135 -0
  133. gsfit-0.0.0/rust/src/circuit_equations/solver_example.rs +78 -0
  134. gsfit-0.0.0/rust/src/circuit_equations/test_rk4.rs +103 -0
  135. gsfit-0.0.0/rust/src/coils.rs +356 -0
  136. gsfit-0.0.0/rust/src/grad_shafranov/gs_solution.rs +1318 -0
  137. gsfit-0.0.0/rust/src/grad_shafranov/mod.rs +5 -0
  138. gsfit-0.0.0/rust/src/greens/d_greens_magnetic_field_dz.rs +91 -0
  139. gsfit-0.0.0/rust/src/greens/greens.rs +145 -0
  140. gsfit-0.0.0/rust/src/greens/greens_magnetic_field.rs +157 -0
  141. gsfit-0.0.0/rust/src/greens/mod.rs +12 -0
  142. gsfit-0.0.0/rust/src/greens/mutual_inductance_finite_size_to_finite_size.rs +170 -0
  143. gsfit-0.0.0/rust/src/greens/mutual_inductance_finite_size_to_point_source.rs +0 -0
  144. gsfit-0.0.0/rust/src/lib.rs +417 -0
  145. gsfit-0.0.0/rust/src/nested_dict.rs +965 -0
  146. gsfit-0.0.0/rust/src/passives.rs +465 -0
  147. gsfit-0.0.0/rust/src/plasma.rs +2127 -0
  148. gsfit-0.0.0/rust/src/plasma_geometry/boundary_contour.rs +816 -0
  149. gsfit-0.0.0/rust/src/plasma_geometry/find_boundary.rs +227 -0
  150. gsfit-0.0.0/rust/src/plasma_geometry/find_magnetic_axis.rs +204 -0
  151. gsfit-0.0.0/rust/src/plasma_geometry/find_viable_limit_point.rs +235 -0
  152. gsfit-0.0.0/rust/src/plasma_geometry/find_viable_xpt.rs +604 -0
  153. gsfit-0.0.0/rust/src/plasma_geometry/mod.rs +11 -0
  154. gsfit-0.0.0/rust/src/sensors/bp_probes.rs +660 -0
  155. gsfit-0.0.0/rust/src/sensors/dialoop.rs +559 -0
  156. gsfit-0.0.0/rust/src/sensors/flux_loops.rs +603 -0
  157. gsfit-0.0.0/rust/src/sensors/isoflux.rs +843 -0
  158. gsfit-0.0.0/rust/src/sensors/isoflux_boundary.rs +690 -0
  159. gsfit-0.0.0/rust/src/sensors/mod.rs +20 -0
  160. gsfit-0.0.0/rust/src/sensors/pressure.rs +565 -0
  161. gsfit-0.0.0/rust/src/sensors/rogowski_coils.rs +921 -0
  162. gsfit-0.0.0/rust/src/sensors/static_and_dynamic_data_types.rs +16 -0
  163. gsfit-0.0.0/rust/src/source_functions/efit_polynomial.rs +105 -0
  164. gsfit-0.0.0/rust/src/source_functions/liuqe_polynomial.rs +81 -0
  165. gsfit-0.0.0/rust/src/source_functions/mod.rs +9 -0
  166. gsfit-0.0.0/rust/src/source_functions/source_function_generics.rs +12 -0
gsfit-0.0.0/PKG-INFO ADDED
@@ -0,0 +1,287 @@
1
+ Metadata-Version: 2.4
2
+ Name: gsfit
3
+ Version: 0.0.0
4
+ Classifier: Programming Language :: Rust
5
+ Classifier: Programming Language :: Python :: Implementation :: CPython
6
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
7
+ Requires-Dist: f90nml
8
+ Requires-Dist: matplotlib
9
+ Requires-Dist: numpy
10
+ Requires-Dist: scipy
11
+ Requires-Dist: setuptools-git-versioning
12
+ Requires-Dist: toml
13
+ Requires-Dist: ipykernel ; extra == 'dev'
14
+ Requires-Dist: ipympl ; extra == 'dev'
15
+ Requires-Dist: maturin[patchelf] ; extra == 'dev'
16
+ Requires-Dist: mypy ; extra == 'dev'
17
+ Requires-Dist: pytest ; extra == 'dev'
18
+ Requires-Dist: pytest-cov ; extra == 'dev'
19
+ Requires-Dist: pytest-mypy ; extra == 'dev'
20
+ Requires-Dist: pytest-xdist ; extra == 'dev'
21
+ Requires-Dist: scipy-stubs ; extra == 'dev'
22
+ Requires-Dist: shapely ; extra == 'dev'
23
+ Requires-Dist: st40-database ; extra == 'with-st40-mdsplus'
24
+ Requires-Dist: standard-utility ; extra == 'with-st40-mdsplus'
25
+ Provides-Extra: dev
26
+ Provides-Extra: with_st40_mdsplus
27
+ License-File: LICENSE
28
+ Summary: GSFit: Equilibrium reconstruction
29
+ Author-email: Peter Buxton <peter.buxton@tokamakenergy.com>
30
+ Requires-Python: >=3.11
31
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
32
+
33
+ # GSFit: Grad-Shafranov Fit
34
+ [![The 3-Clause BSD License](https://img.shields.io/pypi/l/prtg-pyprobe)](LICENSE)
35
+ [![Python 3.11|3.12|3.13](https://img.shields.io/badge/Python-3.11%20%7C%203.12%20%7C%203.13-blue?logo=python&logoColor=white)](https://www.python.org/)
36
+ [![Rust](https://img.shields.io/badge/Rust-1.86-red?logo=rust&logoColor=white)](https://www.rust-lang.org/)
37
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
38
+ [![Checked with mypy](https://www.mypy-lang.org/static/mypy_badge.svg)](http://mypy-lang.org/)
39
+
40
+ GSFit is a modern tokamak equilibrium reconstruction tool for post-shot experimental analysis and scientific interpretation.
41
+ Developed by Tokamak Energy Ltd. for use on ST40, the worlds highest field spherical tokamak.
42
+
43
+ The Grad-Shafranov equation is the is equilibrium solution to the ideal, single fluid, MHD equations, assuming axysymmetry.
44
+ The Grad-Shafranov equation has two free degrees of freedom, often referred to as the source functions: the pressure gradient `p_prime` and `ff_prime` where `f` gives rise to the the poloidal plasma current.
45
+ GSFit uses diagnostics, most importantly the magnetics, to symultaneiously solve the non-linear Grad-Shafranov equation while optimising the `p_prime` and `ff_prime` degrees of freedom to minimise the difference between the experimental "measured" values and the "calculated" values.
46
+ More details about the algorithms used can be found in the excellent paper [J.-M. Moret, *et. al.*, "Tokamak equilibrium reconstruction code LIUQE and its real time implementation", Fusion Eng. Design, 91, 2015](https://doi.org/10.1016/j.fusengdes.2014.09.019).
47
+
48
+ GSFit's numerical solver is written in Rust for speed and robustness, and the user interface / set-up is written in Python for it's convenience.
49
+
50
+ The goal of GSFit is high accuracy, not performance; when something can be calculated accurately, it **should** be calculated accurately regardless of computational cost.
51
+
52
+ GSFit uses the COCOS 13 coordinate system, as described in [O. Sauter and S. Y Medvedev, "Tokamak Coordinate Conventions: COCOS", Comput. Phys. Commun. 184, 2013](https://doi.org/10.1016/j.cpc.2012.09.010).
53
+ Where in summary, flux is measured in weber, and the poloidal angle increase counterclockwise, starting from the outboard mid-plane.
54
+
55
+ **Why Rust and Python?**
56
+
57
+ Rust is a compiled, high-performance language, nearly as fast as C and Fortran.
58
+ It includes modern tooling such as a package manager and centralised registry (cargo and [crates.io](https://crates.io/)), autoformatting ([`rustfmt`](https://github.com/rust-lang/rustfmt)), and has [testing built into the language](https://doc.rust-lang.org/rust-by-example/testing/unit_testing.html).
59
+ Rust enforces strict ownership rules, making programs extremely memory efficient without requiring any extra effort.
60
+ These rules also eliminate entire classes of memory issues.
61
+ Additionally, Rust has zero-cost abstractions, allowing "implementations", whcih are similar to classes in object-oriented programming, to be used without any performance penalty.
62
+ For more information the official [Rust book](https://doc.rust-lang.org/stable/book/) gives a complet introduction to the language with examples.
63
+
64
+ Python is ubiquitous within the fusion industry.
65
+ Using Python allows easy integration into existing workflows, for example in
66
+ [examples/example_02_mastu_with_synthetic_data_from_freegsnke.ipynb](examples/example_02_mastu_with_synthetic_data_from_freegsnke.ipynb), we reconstruct an equilibrium using synthetic "measured" data produced by the open source forward Grad-Shafranov solver [FreeGSNKE](https://github.com/FusionComputingLab/freegsnke).
67
+
68
+ # 1. Instiallation and environment
69
+
70
+ ## 1.1 Python environment
71
+ Presently, GSFit can be run on Python 3.11, 3.12 and 3.13 (see the banner at the top of this page).
72
+
73
+ When installing from PyPi, see [section 1.2](#12-installing-from-pypi), you can use the system Python, but it is best practice to use a virtual environment.
74
+
75
+ There are three main virtual environment providers: [`uv`](https://docs.astral.sh/uv/), [`conda`](https://docs.conda.io/projects/conda/en/latest/index.html), and [`virtualenv`](https://docs.python.org/3/library/venv.html).
76
+
77
+ `uv` is both an environment and package manager and is the **recomended** option.
78
+ This is because `uv` can create an environment with any version of Python, the instillation is done within the user's home directory, and `uv` has been specifically designed for speed.
79
+ This makes it simple and quick to test against multiple Python versions.
80
+
81
+ ```shell
82
+ # Install the `uv` environment and package manager
83
+ python -m pip install uv
84
+
85
+ # Creating and activating a virtual environment, called `venv_gsfit`
86
+ python -m uv venv venv_gsfit --python=3.13
87
+ source venv_gsfit/bin/activate
88
+ ```
89
+
90
+
91
+ ## 1.2 Installing from PyPi
92
+ GSFit is available on the [PyPi package registry](https://pypi.org/project/gsfit/) as a pre-compiled binary.
93
+ The binary is compiled using the "manylinux2014" standard, which typically can be run on any Linux machine newer than 2014.
94
+
95
+ All of the packages GSFit depends on are listed in the [pyproject.toml](pyproject.toml).
96
+ These are divided into essential packages which are always required for any run and optional packages which can be installed for different purposes.
97
+ An example of optional packages are machine specific database readers.
98
+
99
+ ```shell
100
+ # Install GSFit from the PyPi package registry, with only the essential packages
101
+ uv pip install gsfit
102
+ # or install GSFit with the "developer" packages, such as `pytest` and `mypy`
103
+ uv pip install gsfit[dev]
104
+ # or install GSFit with reading/writing to/from ST40's experimental database
105
+ # (this will only work within Tokamak Energy's network)
106
+ uv pip install --reinstall .[with_st40_mdsplus]
107
+ # or any combination
108
+ uv pip install --reinstall .[dev,with_st40_mdsplus]
109
+ ```
110
+
111
+ ## 1.3 Compiling and installin from source code
112
+ While GSFit can be installed into the system Python from PyPi, when compiling from source code a virtual environment is <ins>**required**</ins>.
113
+
114
+ The Rust compiler is easily installed using [`rustup`](https://www.rust-lang.org/tools/install).
115
+
116
+ GSFIt also requires [OpenBLAS](https://github.com/OpenMathLib/OpenBLAS) to perform various LAPACK routines, such as least squares minimisation and eignevalue/eigenvector calculations.
117
+
118
+ ```shell
119
+ # Install the Rust compiler (only needs to be done once)
120
+ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
121
+
122
+ # Clone a copy of GSFit from GitHub
123
+ git clone git@github.com:tokamak-energy/gsfit.git
124
+ cd gsfit
125
+
126
+ # Load OpenBLAS (gsfit is statically linked so this is only required when compiling)
127
+ module avail
128
+ module load OpenBLAS
129
+
130
+ # Install GSFit
131
+ uv pip install --reinstall .
132
+ ```
133
+
134
+ ## 1.4 IDE
135
+ [VS Code](https://code.visualstudio.com/) is the recomended IDE for GSFit.
136
+
137
+ A particularly useful extension is [`rust-analyser`](https://github.com/rust-lang/rust-analyzer), which *"is a part of a larger rls-2.0 effort to create excellent IDE support for Rust."*
138
+
139
+ The [`PyO3`](https://crates.io/crates/pyo3) crate requires Python>=3.7.
140
+ On machines where the `python3` command links to an unsupported Python version, you can change the following file: `~/.vscode-server/data/Machine/settings.json` to point to your desired Python's "standard library files". Which will be <ins>**similar**</ins> to:
141
+ ```json
142
+ {
143
+ "rust-analyzer.server.extraEnv": {
144
+ "PYO3_CROSS_LIB_DIR": "/home/<user.name>/.local/share/uv/python/cpython-3.13.2-linux-x86_64-gnu/lib"
145
+ }
146
+ }
147
+ ```
148
+ and Python's "standard library files" directory can be found by:
149
+ ```shell
150
+ python -c "import sysconfig; print(sysconfig.get_config_var('LIBDIR'))"
151
+ ```
152
+
153
+ # 2. GSFit program layout and flow
154
+ ## 2.1 Initialisation
155
+ GSFit's solver and numerics are programmed in Rust, with the initialisation done within Python.
156
+
157
+ The communication between Python and Rust is handled by the [`PyO3`](https://crates.io/crates/pyo3) crate.
158
+ From the Python side the integration is seemless, and a general user would not know if a fuction / class is written in Python or Rust.
159
+ In a similar way a user interacting with `numpy` does not know if they are calling Python, C or Fortran.
160
+
161
+ GSFit has the following implementations (which are similar to classes):
162
+ * **`coils`** for the PF and TF coils. This object contains the geometry; measured values; and reconstructed values
163
+ * **`passives`** for the toroidally conducting strucutres, such as the vacuum vessel and passive plates. `passives` also contains the degrees of freedom allowed for the passive conductors
164
+ * **`plasma`** contains a $(R, Z)$ grid and the reconstructed equilibrium
165
+ * **`bp_probes`** contains the Mrinov coils: geometry; measured values; which sensors to use in the reconstruction and their weights; and the reconstructed values
166
+ * **`flux_loops`** the toroidal flux loops. Note the units are in weber.
167
+ * **`rogowski_coils`** measuring toroidal currents
168
+ * **`isoflux`** contains two time-dependent $(R, Z)$ coordinates which have equal poloidal flux
169
+ * **`isoflux_boundary`** contains a time-dependent $(R, Z)$ coordinate which has the same poloidal flux as the plasma boundary. This can be used if we have measurements of where the strike point is, for example if there is an IR camera
170
+
171
+ All of these implementations are initialised from Python.
172
+ As a result, all of the database access is contained within Python, and any pre-processing such as filtering or smoothing experimental data can also be done within Python.
173
+
174
+ Below is a reproduction of [`examples/example_01_st40_with_experimental_data.py`](examples/example_01_st40_with_experimental_data.py).
175
+ ```python
176
+ # Note, this example will only run inside Tokamak Energy's network
177
+
178
+ from gsfit import Gsfit
179
+
180
+ # Construct the GSFit object
181
+ gsfit_controller = Gsfit(
182
+ pulseNo=12050,
183
+ run_name="TEST01",
184
+ run_description="Test run",
185
+ write_to_mds=True,
186
+ settings_path="default"
187
+ )
188
+
189
+ # Run (solve the inverse Grad-Shafranov equation, and if requested write to database)
190
+ gsfit_controller.run()
191
+ ```
192
+
193
+ To improve reliability and tracability the number of arguments needed to initialise the `gsfit_controller` object is <ins>deliberately kept to a minimum</ins>.
194
+ The `settings_path="default"` argument tells GSFit to use the settings (JSON files) from this directory [`python/gsfit/settings/default/`](python/gsfit/settings/default/).
195
+ By storing all of the settings needed to run GSFit in JSON files allows changes to be tracked through Git.
196
+
197
+ ## 2.2 Adding a new experimental device or coupling to a new forward Grad-Shafranov code
198
+ The information needed to run GSFit comes from two sources:
199
+
200
+ 1. **[`python/gsfit/settings/`](python/gsfit/settings/)**: Contains parameters in JSON files, such as:
201
+ * the maximum number of iterations,
202
+ * which magnetic sensors to use in the reconstruction,
203
+ * the degrees of freedom given to `p_prime` and `ff_prime`,
204
+ * <ins>**and**</ins> which `database_reader` to use.
205
+ 2. **[`python/gsfit/database_readers/`](python/gsfit/database_readers/)**: Connects to machine specific databases to read experimental results in, such as:
206
+ * geometry and current in poloidal field coils
207
+ * measured signals from magnetic sensors
208
+ * isoflux constraints
209
+
210
+ These two sources of information are combined to initialise the Rust objects needed to run GSFit (as described in [section 2.1](#21-initialisation)).
211
+
212
+ Writing to the database is done through [`python/gsfit/database_writers`](python/gsfit/database_writers), in a similar format to `database_readers`.
213
+
214
+ Included are the settings and readers for ST40's experimental data, and synthetic data produced by [FreeGS](https://github.com/freegs-plasma/freegs) and [FreeGSNKE](https://github.com/FusionComputingLab/freegsnke/) simulations.
215
+ We welcome and are happy to inclue configurations for other devices.
216
+
217
+ ## 2.3 Where are the Green's tables stored?
218
+ We can classify objects as either "current sources" or "sensors".
219
+ * `coils`, `passives`, and `plasma` are "current sources".
220
+ * `bp_probes`, `flux_loops`, `rogowski_coils`, `isoflux`, and `plasma` are "sensors".
221
+
222
+ Note, `plasma` is both a current source and a sensor, this is because it contains the plasma current **and** the 2D (R, Z) grid where we want to take measurement on.
223
+
224
+ Principles about where data should be:
225
+ * "sensors" contain the Greens tables.
226
+ * "current sources" contain the currents.
227
+
228
+ the sensors and currens are linked by names, every magnetic probe and PF coil must have a unique name.
229
+
230
+ <!--
231
+ So the field which `bp_probe` `P101` detects will be:
232
+ ```rust
233
+ field_bp_probe_101_detects = bp_probes["P101"]["greens"]["pf"]["SOL"] * coils["pf"]["SOL"]["current"]["measured"] + ... // PF coils
234
+ + bp_probes["P101"]["greens"]["plasma"] * plasma["two_d"]["j"] // plasma
235
+ + ...
236
+ ```
237
+ -->
238
+
239
+ ## 2.4 Decision on coils vs PSU's
240
+ On many tokamaks, including ST40, several PF coils are connected to a single PSU.
241
+ For ST40 we have the `BVLT` and `BVLB` PF coils, both connected to the `BVL` PSU.
242
+
243
+ Several equilibrium reconstruction codes treat the PSU current as a measurement with an associated degree of freedom.
244
+ To allow for this degree of freedom we would need to tell GSFit which PF coil is connected to which PSU.
245
+ This is quite burdensom and **to keep GSFit simple to initialise, we have decided to only consider "coils"**.
246
+ The work-around, if you want to have the PSU currents as a degree of freedom, is to combine all of the PF coils connected to a single power supply into a single coil.
247
+ On ST40 we could create a PF coil called `BVL_combined` which contains both `BVLT` and `BVLB`.
248
+
249
+ # 3. Planned future development
250
+ * Pressure constrained.
251
+ * Spline `p_prime` and `ff_prime`
252
+ * Interfacing to and from IMAS & OMAS.
253
+ * More kinetic sensors, such as MSE, polarimeters, and interferometers.
254
+
255
+ # 4. Citing GSFit
256
+ We intend on publishing a paper on GSFit.
257
+ While GSFit is unpublished please cite it as "P. F. Buxton, GSFit, https://github.com/tokamak-energy/gsfit, 2025"
258
+
259
+ Please use the **GSFit** nomenclature, <ins>**not**</ins> GS-Fit, GSFIT, or g/s fit.
260
+
261
+
262
+ <!-- # 4 Useful Rust code for debugging
263
+ ```rust
264
+ use std::time::Instant;
265
+ let start = Instant::now();
266
+ // code you want to time
267
+ let duration = start.elapsed();
268
+ println!("Time elapsed = {:?}", duration);
269
+ ```
270
+
271
+ Write to file:
272
+ ```rust
273
+ // Imports
274
+ use std::fs::File;
275
+ use std::io::{BufWriter, Write};
276
+ // write to file
277
+ let file = File::create("psi_2d_coils.csv").expect("can't make file");
278
+ let mut writer = BufWriter::new(file);
279
+ for row in psi_2d_coils.rows() {
280
+ let line: String = row.iter()
281
+ .map(|&value| value.to_string())
282
+ .collect::<Vec<_>>()
283
+ .join(", ");
284
+ writeln!(writer, "{}", line).expect("can't write line");
285
+ }
286
+ writer.flush().expect("can't flush writer");
287
+ ``` -->
gsfit-0.0.0/README.md ADDED
@@ -0,0 +1,255 @@
1
+ # GSFit: Grad-Shafranov Fit
2
+ [![The 3-Clause BSD License](https://img.shields.io/pypi/l/prtg-pyprobe)](LICENSE)
3
+ [![Python 3.11|3.12|3.13](https://img.shields.io/badge/Python-3.11%20%7C%203.12%20%7C%203.13-blue?logo=python&logoColor=white)](https://www.python.org/)
4
+ [![Rust](https://img.shields.io/badge/Rust-1.86-red?logo=rust&logoColor=white)](https://www.rust-lang.org/)
5
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
6
+ [![Checked with mypy](https://www.mypy-lang.org/static/mypy_badge.svg)](http://mypy-lang.org/)
7
+
8
+ GSFit is a modern tokamak equilibrium reconstruction tool for post-shot experimental analysis and scientific interpretation.
9
+ Developed by Tokamak Energy Ltd. for use on ST40, the worlds highest field spherical tokamak.
10
+
11
+ The Grad-Shafranov equation is the is equilibrium solution to the ideal, single fluid, MHD equations, assuming axysymmetry.
12
+ The Grad-Shafranov equation has two free degrees of freedom, often referred to as the source functions: the pressure gradient `p_prime` and `ff_prime` where `f` gives rise to the the poloidal plasma current.
13
+ GSFit uses diagnostics, most importantly the magnetics, to symultaneiously solve the non-linear Grad-Shafranov equation while optimising the `p_prime` and `ff_prime` degrees of freedom to minimise the difference between the experimental "measured" values and the "calculated" values.
14
+ More details about the algorithms used can be found in the excellent paper [J.-M. Moret, *et. al.*, "Tokamak equilibrium reconstruction code LIUQE and its real time implementation", Fusion Eng. Design, 91, 2015](https://doi.org/10.1016/j.fusengdes.2014.09.019).
15
+
16
+ GSFit's numerical solver is written in Rust for speed and robustness, and the user interface / set-up is written in Python for it's convenience.
17
+
18
+ The goal of GSFit is high accuracy, not performance; when something can be calculated accurately, it **should** be calculated accurately regardless of computational cost.
19
+
20
+ GSFit uses the COCOS 13 coordinate system, as described in [O. Sauter and S. Y Medvedev, "Tokamak Coordinate Conventions: COCOS", Comput. Phys. Commun. 184, 2013](https://doi.org/10.1016/j.cpc.2012.09.010).
21
+ Where in summary, flux is measured in weber, and the poloidal angle increase counterclockwise, starting from the outboard mid-plane.
22
+
23
+ **Why Rust and Python?**
24
+
25
+ Rust is a compiled, high-performance language, nearly as fast as C and Fortran.
26
+ It includes modern tooling such as a package manager and centralised registry (cargo and [crates.io](https://crates.io/)), autoformatting ([`rustfmt`](https://github.com/rust-lang/rustfmt)), and has [testing built into the language](https://doc.rust-lang.org/rust-by-example/testing/unit_testing.html).
27
+ Rust enforces strict ownership rules, making programs extremely memory efficient without requiring any extra effort.
28
+ These rules also eliminate entire classes of memory issues.
29
+ Additionally, Rust has zero-cost abstractions, allowing "implementations", whcih are similar to classes in object-oriented programming, to be used without any performance penalty.
30
+ For more information the official [Rust book](https://doc.rust-lang.org/stable/book/) gives a complet introduction to the language with examples.
31
+
32
+ Python is ubiquitous within the fusion industry.
33
+ Using Python allows easy integration into existing workflows, for example in
34
+ [examples/example_02_mastu_with_synthetic_data_from_freegsnke.ipynb](examples/example_02_mastu_with_synthetic_data_from_freegsnke.ipynb), we reconstruct an equilibrium using synthetic "measured" data produced by the open source forward Grad-Shafranov solver [FreeGSNKE](https://github.com/FusionComputingLab/freegsnke).
35
+
36
+ # 1. Instiallation and environment
37
+
38
+ ## 1.1 Python environment
39
+ Presently, GSFit can be run on Python 3.11, 3.12 and 3.13 (see the banner at the top of this page).
40
+
41
+ When installing from PyPi, see [section 1.2](#12-installing-from-pypi), you can use the system Python, but it is best practice to use a virtual environment.
42
+
43
+ There are three main virtual environment providers: [`uv`](https://docs.astral.sh/uv/), [`conda`](https://docs.conda.io/projects/conda/en/latest/index.html), and [`virtualenv`](https://docs.python.org/3/library/venv.html).
44
+
45
+ `uv` is both an environment and package manager and is the **recomended** option.
46
+ This is because `uv` can create an environment with any version of Python, the instillation is done within the user's home directory, and `uv` has been specifically designed for speed.
47
+ This makes it simple and quick to test against multiple Python versions.
48
+
49
+ ```shell
50
+ # Install the `uv` environment and package manager
51
+ python -m pip install uv
52
+
53
+ # Creating and activating a virtual environment, called `venv_gsfit`
54
+ python -m uv venv venv_gsfit --python=3.13
55
+ source venv_gsfit/bin/activate
56
+ ```
57
+
58
+
59
+ ## 1.2 Installing from PyPi
60
+ GSFit is available on the [PyPi package registry](https://pypi.org/project/gsfit/) as a pre-compiled binary.
61
+ The binary is compiled using the "manylinux2014" standard, which typically can be run on any Linux machine newer than 2014.
62
+
63
+ All of the packages GSFit depends on are listed in the [pyproject.toml](pyproject.toml).
64
+ These are divided into essential packages which are always required for any run and optional packages which can be installed for different purposes.
65
+ An example of optional packages are machine specific database readers.
66
+
67
+ ```shell
68
+ # Install GSFit from the PyPi package registry, with only the essential packages
69
+ uv pip install gsfit
70
+ # or install GSFit with the "developer" packages, such as `pytest` and `mypy`
71
+ uv pip install gsfit[dev]
72
+ # or install GSFit with reading/writing to/from ST40's experimental database
73
+ # (this will only work within Tokamak Energy's network)
74
+ uv pip install --reinstall .[with_st40_mdsplus]
75
+ # or any combination
76
+ uv pip install --reinstall .[dev,with_st40_mdsplus]
77
+ ```
78
+
79
+ ## 1.3 Compiling and installin from source code
80
+ While GSFit can be installed into the system Python from PyPi, when compiling from source code a virtual environment is <ins>**required**</ins>.
81
+
82
+ The Rust compiler is easily installed using [`rustup`](https://www.rust-lang.org/tools/install).
83
+
84
+ GSFIt also requires [OpenBLAS](https://github.com/OpenMathLib/OpenBLAS) to perform various LAPACK routines, such as least squares minimisation and eignevalue/eigenvector calculations.
85
+
86
+ ```shell
87
+ # Install the Rust compiler (only needs to be done once)
88
+ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
89
+
90
+ # Clone a copy of GSFit from GitHub
91
+ git clone git@github.com:tokamak-energy/gsfit.git
92
+ cd gsfit
93
+
94
+ # Load OpenBLAS (gsfit is statically linked so this is only required when compiling)
95
+ module avail
96
+ module load OpenBLAS
97
+
98
+ # Install GSFit
99
+ uv pip install --reinstall .
100
+ ```
101
+
102
+ ## 1.4 IDE
103
+ [VS Code](https://code.visualstudio.com/) is the recomended IDE for GSFit.
104
+
105
+ A particularly useful extension is [`rust-analyser`](https://github.com/rust-lang/rust-analyzer), which *"is a part of a larger rls-2.0 effort to create excellent IDE support for Rust."*
106
+
107
+ The [`PyO3`](https://crates.io/crates/pyo3) crate requires Python>=3.7.
108
+ On machines where the `python3` command links to an unsupported Python version, you can change the following file: `~/.vscode-server/data/Machine/settings.json` to point to your desired Python's "standard library files". Which will be <ins>**similar**</ins> to:
109
+ ```json
110
+ {
111
+ "rust-analyzer.server.extraEnv": {
112
+ "PYO3_CROSS_LIB_DIR": "/home/<user.name>/.local/share/uv/python/cpython-3.13.2-linux-x86_64-gnu/lib"
113
+ }
114
+ }
115
+ ```
116
+ and Python's "standard library files" directory can be found by:
117
+ ```shell
118
+ python -c "import sysconfig; print(sysconfig.get_config_var('LIBDIR'))"
119
+ ```
120
+
121
+ # 2. GSFit program layout and flow
122
+ ## 2.1 Initialisation
123
+ GSFit's solver and numerics are programmed in Rust, with the initialisation done within Python.
124
+
125
+ The communication between Python and Rust is handled by the [`PyO3`](https://crates.io/crates/pyo3) crate.
126
+ From the Python side the integration is seemless, and a general user would not know if a fuction / class is written in Python or Rust.
127
+ In a similar way a user interacting with `numpy` does not know if they are calling Python, C or Fortran.
128
+
129
+ GSFit has the following implementations (which are similar to classes):
130
+ * **`coils`** for the PF and TF coils. This object contains the geometry; measured values; and reconstructed values
131
+ * **`passives`** for the toroidally conducting strucutres, such as the vacuum vessel and passive plates. `passives` also contains the degrees of freedom allowed for the passive conductors
132
+ * **`plasma`** contains a $(R, Z)$ grid and the reconstructed equilibrium
133
+ * **`bp_probes`** contains the Mrinov coils: geometry; measured values; which sensors to use in the reconstruction and their weights; and the reconstructed values
134
+ * **`flux_loops`** the toroidal flux loops. Note the units are in weber.
135
+ * **`rogowski_coils`** measuring toroidal currents
136
+ * **`isoflux`** contains two time-dependent $(R, Z)$ coordinates which have equal poloidal flux
137
+ * **`isoflux_boundary`** contains a time-dependent $(R, Z)$ coordinate which has the same poloidal flux as the plasma boundary. This can be used if we have measurements of where the strike point is, for example if there is an IR camera
138
+
139
+ All of these implementations are initialised from Python.
140
+ As a result, all of the database access is contained within Python, and any pre-processing such as filtering or smoothing experimental data can also be done within Python.
141
+
142
+ Below is a reproduction of [`examples/example_01_st40_with_experimental_data.py`](examples/example_01_st40_with_experimental_data.py).
143
+ ```python
144
+ # Note, this example will only run inside Tokamak Energy's network
145
+
146
+ from gsfit import Gsfit
147
+
148
+ # Construct the GSFit object
149
+ gsfit_controller = Gsfit(
150
+ pulseNo=12050,
151
+ run_name="TEST01",
152
+ run_description="Test run",
153
+ write_to_mds=True,
154
+ settings_path="default"
155
+ )
156
+
157
+ # Run (solve the inverse Grad-Shafranov equation, and if requested write to database)
158
+ gsfit_controller.run()
159
+ ```
160
+
161
+ To improve reliability and tracability the number of arguments needed to initialise the `gsfit_controller` object is <ins>deliberately kept to a minimum</ins>.
162
+ The `settings_path="default"` argument tells GSFit to use the settings (JSON files) from this directory [`python/gsfit/settings/default/`](python/gsfit/settings/default/).
163
+ By storing all of the settings needed to run GSFit in JSON files allows changes to be tracked through Git.
164
+
165
+ ## 2.2 Adding a new experimental device or coupling to a new forward Grad-Shafranov code
166
+ The information needed to run GSFit comes from two sources:
167
+
168
+ 1. **[`python/gsfit/settings/`](python/gsfit/settings/)**: Contains parameters in JSON files, such as:
169
+ * the maximum number of iterations,
170
+ * which magnetic sensors to use in the reconstruction,
171
+ * the degrees of freedom given to `p_prime` and `ff_prime`,
172
+ * <ins>**and**</ins> which `database_reader` to use.
173
+ 2. **[`python/gsfit/database_readers/`](python/gsfit/database_readers/)**: Connects to machine specific databases to read experimental results in, such as:
174
+ * geometry and current in poloidal field coils
175
+ * measured signals from magnetic sensors
176
+ * isoflux constraints
177
+
178
+ These two sources of information are combined to initialise the Rust objects needed to run GSFit (as described in [section 2.1](#21-initialisation)).
179
+
180
+ Writing to the database is done through [`python/gsfit/database_writers`](python/gsfit/database_writers), in a similar format to `database_readers`.
181
+
182
+ Included are the settings and readers for ST40's experimental data, and synthetic data produced by [FreeGS](https://github.com/freegs-plasma/freegs) and [FreeGSNKE](https://github.com/FusionComputingLab/freegsnke/) simulations.
183
+ We welcome and are happy to inclue configurations for other devices.
184
+
185
+ ## 2.3 Where are the Green's tables stored?
186
+ We can classify objects as either "current sources" or "sensors".
187
+ * `coils`, `passives`, and `plasma` are "current sources".
188
+ * `bp_probes`, `flux_loops`, `rogowski_coils`, `isoflux`, and `plasma` are "sensors".
189
+
190
+ Note, `plasma` is both a current source and a sensor, this is because it contains the plasma current **and** the 2D (R, Z) grid where we want to take measurement on.
191
+
192
+ Principles about where data should be:
193
+ * "sensors" contain the Greens tables.
194
+ * "current sources" contain the currents.
195
+
196
+ the sensors and currens are linked by names, every magnetic probe and PF coil must have a unique name.
197
+
198
+ <!--
199
+ So the field which `bp_probe` `P101` detects will be:
200
+ ```rust
201
+ field_bp_probe_101_detects = bp_probes["P101"]["greens"]["pf"]["SOL"] * coils["pf"]["SOL"]["current"]["measured"] + ... // PF coils
202
+ + bp_probes["P101"]["greens"]["plasma"] * plasma["two_d"]["j"] // plasma
203
+ + ...
204
+ ```
205
+ -->
206
+
207
+ ## 2.4 Decision on coils vs PSU's
208
+ On many tokamaks, including ST40, several PF coils are connected to a single PSU.
209
+ For ST40 we have the `BVLT` and `BVLB` PF coils, both connected to the `BVL` PSU.
210
+
211
+ Several equilibrium reconstruction codes treat the PSU current as a measurement with an associated degree of freedom.
212
+ To allow for this degree of freedom we would need to tell GSFit which PF coil is connected to which PSU.
213
+ This is quite burdensom and **to keep GSFit simple to initialise, we have decided to only consider "coils"**.
214
+ The work-around, if you want to have the PSU currents as a degree of freedom, is to combine all of the PF coils connected to a single power supply into a single coil.
215
+ On ST40 we could create a PF coil called `BVL_combined` which contains both `BVLT` and `BVLB`.
216
+
217
+ # 3. Planned future development
218
+ * Pressure constrained.
219
+ * Spline `p_prime` and `ff_prime`
220
+ * Interfacing to and from IMAS & OMAS.
221
+ * More kinetic sensors, such as MSE, polarimeters, and interferometers.
222
+
223
+ # 4. Citing GSFit
224
+ We intend on publishing a paper on GSFit.
225
+ While GSFit is unpublished please cite it as "P. F. Buxton, GSFit, https://github.com/tokamak-energy/gsfit, 2025"
226
+
227
+ Please use the **GSFit** nomenclature, <ins>**not**</ins> GS-Fit, GSFIT, or g/s fit.
228
+
229
+
230
+ <!-- # 4 Useful Rust code for debugging
231
+ ```rust
232
+ use std::time::Instant;
233
+ let start = Instant::now();
234
+ // code you want to time
235
+ let duration = start.elapsed();
236
+ println!("Time elapsed = {:?}", duration);
237
+ ```
238
+
239
+ Write to file:
240
+ ```rust
241
+ // Imports
242
+ use std::fs::File;
243
+ use std::io::{BufWriter, Write};
244
+ // write to file
245
+ let file = File::create("psi_2d_coils.csv").expect("can't make file");
246
+ let mut writer = BufWriter::new(file);
247
+ for row in psi_2d_coils.rows() {
248
+ let line: String = row.iter()
249
+ .map(|&value| value.to_string())
250
+ .collect::<Vec<_>>()
251
+ .join(", ");
252
+ writeln!(writer, "{}", line).expect("can't write line");
253
+ }
254
+ writer.flush().expect("can't flush writer");
255
+ ``` -->
@@ -0,0 +1,80 @@
1
+ [build-system]
2
+ build-backend = "maturin"
3
+ requires = ["maturin>=1.7,<2.0"]
4
+
5
+ [project]
6
+ authors = [
7
+ {name = "Peter Buxton", email = "peter.buxton@tokamakenergy.com"},
8
+ ]
9
+ classifiers = [
10
+ "Programming Language :: Rust",
11
+ "Programming Language :: Python :: Implementation :: CPython",
12
+ "Programming Language :: Python :: Implementation :: PyPy",
13
+ ]
14
+ dependencies = [
15
+ "f90nml",
16
+ "matplotlib",
17
+ "numpy",
18
+ "scipy",
19
+ "setuptools_git_versioning",
20
+ "toml",
21
+ ]
22
+ description = "GSFit: Equilibrium reconstruction"
23
+ dynamic = ["version"]
24
+ name = "gsfit"
25
+ readme = "README.md"
26
+ requires-python = ">=3.11"
27
+
28
+ [project.optional-dependencies]
29
+ dev = [
30
+ "ipykernel",
31
+ "ipympl",
32
+ "maturin[patchelf]",
33
+ "mypy",
34
+ "pytest",
35
+ "pytest-cov",
36
+ "pytest-mypy",
37
+ "pytest-xdist",
38
+ "scipy-stubs",
39
+ "shapely",
40
+ ]
41
+ with_st40_mdsplus = [
42
+ "st40_database",
43
+ "standard_utility"
44
+ ]
45
+
46
+ [tool.maturin]
47
+ features = ["pyo3/extension-module"]
48
+ python-source = "python"
49
+ python-packages = ["gsfit", "gsfit_rs", "diagnostic_and_simulation_base"]
50
+ gsfit.data = ["gsfit/settings/"]
51
+
52
+ [tool.mypy]
53
+ ignore_errors = false
54
+ disallow_untyped_calls = true
55
+ disallow_untyped_defs = true
56
+ disallow_incomplete_defs = true
57
+ disallow_subclassing_any = true
58
+ disallow_any_generics = true
59
+ check_untyped_defs = true
60
+ no_implicit_optional = true
61
+ show_error_codes = true
62
+ strict_equality = true
63
+ warn_redundant_casts = true
64
+ warn_return_any = true
65
+ warn_unreachable = true
66
+ warn_unused_configs = true
67
+ no_implicit_reexport = true
68
+
69
+ [tool.black]
70
+ line-length = 160
71
+
72
+ [tool.isort]
73
+ multi_line_output = 3
74
+
75
+ [tool.ruff]
76
+ line-length = 160
77
+
78
+ [tool.ruff.lint.isort]
79
+ force-single-line = true
80
+ single-line-exclusions = ["dash"]