torux 0.1.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 (115) hide show
  1. torux-0.1.0/LICENSE +121 -0
  2. torux-0.1.0/NOTICE.md +46 -0
  3. torux-0.1.0/PKG-INFO +185 -0
  4. torux-0.1.0/README.md +154 -0
  5. torux-0.1.0/pyproject.toml +41 -0
  6. torux-0.1.0/setup.cfg +4 -0
  7. torux-0.1.0/src/torux/__init__.py +109 -0
  8. torux-0.1.0/src/torux/analysis.py +2355 -0
  9. torux-0.1.0/src/torux/answer.py +988 -0
  10. torux-0.1.0/src/torux/api.py +281 -0
  11. torux-0.1.0/src/torux/ask.py +760 -0
  12. torux-0.1.0/src/torux/axis.py +315 -0
  13. torux-0.1.0/src/torux/axis_general.py +444 -0
  14. torux-0.1.0/src/torux/axisselect.py +250 -0
  15. torux-0.1.0/src/torux/barrier.py +5103 -0
  16. torux-0.1.0/src/torux/batch.py +798 -0
  17. torux-0.1.0/src/torux/boozer.py +327 -0
  18. torux-0.1.0/src/torux/budget.py +70 -0
  19. torux-0.1.0/src/torux/cardtrace.py +262 -0
  20. torux-0.1.0/src/torux/certify.py +142 -0
  21. torux-0.1.0/src/torux/chainfind.py +246 -0
  22. torux-0.1.0/src/torux/chains.py +203 -0
  23. torux-0.1.0/src/torux/check.py +707 -0
  24. torux-0.1.0/src/torux/cli.py +239 -0
  25. torux-0.1.0/src/torux/design.py +302 -0
  26. torux-0.1.0/src/torux/edgebound.py +2342 -0
  27. torux-0.1.0/src/torux/envfail.py +18 -0
  28. torux-0.1.0/src/torux/escape.py +126 -0
  29. torux-0.1.0/src/torux/exitcurve.py +1508 -0
  30. torux-0.1.0/src/torux/exitcurve_far.py +185 -0
  31. torux-0.1.0/src/torux/exitcurve_nearcoil.py +234 -0
  32. torux-0.1.0/src/torux/family.py +724 -0
  33. torux-0.1.0/src/torux/family_general.py +475 -0
  34. torux-0.1.0/src/torux/fast_design.py +106 -0
  35. torux-0.1.0/src/torux/fast_family.py +133 -0
  36. torux-0.1.0/src/torux/fast_kernels.py +715 -0
  37. torux-0.1.0/src/torux/fast_newton.py +140 -0
  38. torux-0.1.0/src/torux/fast_trace.py +371 -0
  39. torux-0.1.0/src/torux/fasttrace.py +243 -0
  40. torux-0.1.0/src/torux/field/__init__.py +1 -0
  41. torux-0.1.0/src/torux/field/analytic.py +108 -0
  42. torux-0.1.0/src/torux/field/cpu.py +298 -0
  43. torux-0.1.0/src/torux/field/cuda_fil.py +384 -0
  44. torux-0.1.0/src/torux/field/engine.py +760 -0
  45. torux-0.1.0/src/torux/field/grid.py +131 -0
  46. torux-0.1.0/src/torux/field/kernels.py +173 -0
  47. torux-0.1.0/src/torux/gradients.py +385 -0
  48. torux-0.1.0/src/torux/guard.py +89 -0
  49. torux-0.1.0/src/torux/io/__init__.py +1 -0
  50. torux-0.1.0/src/torux/io/_stellcommon.py +530 -0
  51. torux-0.1.0/src/torux/io/analytic.py +215 -0
  52. torux-0.1.0/src/torux/io/axisym.py +551 -0
  53. torux-0.1.0/src/torux/io/focus.py +160 -0
  54. torux-0.1.0/src/torux/io/focus_dipoles.py +200 -0
  55. torux-0.1.0/src/torux/io/fourier_json.py +63 -0
  56. torux-0.1.0/src/torux/io/gpec.py +59 -0
  57. torux-0.1.0/src/torux/io/magnets.py +413 -0
  58. torux-0.1.0/src/torux/io/makegrid.py +204 -0
  59. torux-0.1.0/src/torux/io/simsopt_json.py +683 -0
  60. torux-0.1.0/src/torux/io/surfaces.py +408 -0
  61. torux-0.1.0/src/torux/io/wireframe.py +125 -0
  62. torux-0.1.0/src/torux/islandband.py +3960 -0
  63. torux-0.1.0/src/torux/load.py +240 -0
  64. torux-0.1.0/src/torux/manifolds.py +178 -0
  65. torux-0.1.0/src/torux/nearaxis.py +217 -0
  66. torux-0.1.0/src/torux/noaxis.py +1020 -0
  67. torux-0.1.0/src/torux/nonstell.py +2136 -0
  68. torux-0.1.0/src/torux/nonstell_cert.py +863 -0
  69. torux-0.1.0/src/torux/nonstell_index.py +2695 -0
  70. torux-0.1.0/src/torux/orbits.py +326 -0
  71. torux-0.1.0/src/torux/plot.py +784 -0
  72. torux-0.1.0/src/torux/reference/__init__.py +59 -0
  73. torux-0.1.0/src/torux/reference/carlson.py +274 -0
  74. torux-0.1.0/src/torux/reference/constants.py +12 -0
  75. torux-0.1.0/src/torux/reference/dd.py +366 -0
  76. torux-0.1.0/src/torux/reference/fast64.py +779 -0
  77. torux-0.1.0/src/torux/reference/geometry.py +49 -0
  78. torux-0.1.0/src/torux/reference/precise.py +150 -0
  79. torux-0.1.0/src/torux/reference/precise_axipoly.py +590 -0
  80. torux-0.1.0/src/torux/reference/precise_closed.py +417 -0
  81. torux-0.1.0/src/torux/reference/precise_curves.py +335 -0
  82. torux-0.1.0/src/torux/reference/quadrules.py +89 -0
  83. torux-0.1.0/src/torux/reference/trace.py +276 -0
  84. torux-0.1.0/src/torux/seeds.py +1766 -0
  85. torux-0.1.0/src/torux/source.py +275 -0
  86. torux-0.1.0/src/torux/sources.py +243 -0
  87. torux-0.1.0/src/torux/torus.py +1360 -0
  88. torux-0.1.0/src/torux/torus_cpu.py +111 -0
  89. torux-0.1.0/src/torux/torus_general.py +818 -0
  90. torux-0.1.0/src/torux/trace.py +95 -0
  91. torux-0.1.0/src/torux/verify.py +381 -0
  92. torux-0.1.0/src/torux/verify_general.py +139 -0
  93. torux-0.1.0/src/torux.egg-info/PKG-INFO +185 -0
  94. torux-0.1.0/src/torux.egg-info/SOURCES.txt +113 -0
  95. torux-0.1.0/src/torux.egg-info/dependency_links.txt +1 -0
  96. torux-0.1.0/src/torux.egg-info/entry_points.txt +2 -0
  97. torux-0.1.0/src/torux.egg-info/requires.txt +21 -0
  98. torux-0.1.0/src/torux.egg-info/top_level.txt +1 -0
  99. torux-0.1.0/tests/test_answer.py +396 -0
  100. torux-0.1.0/tests/test_ask.py +372 -0
  101. torux-0.1.0/tests/test_boozer.py +311 -0
  102. torux-0.1.0/tests/test_certificates.py +41 -0
  103. torux-0.1.0/tests/test_check.py +485 -0
  104. torux-0.1.0/tests/test_claims.py +211 -0
  105. torux-0.1.0/tests/test_compare300.py +239 -0
  106. torux-0.1.0/tests/test_field.py +70 -0
  107. torux-0.1.0/tests/test_gradients.py +38 -0
  108. torux-0.1.0/tests/test_handoff.py +91 -0
  109. torux-0.1.0/tests/test_independence.py +35 -0
  110. torux-0.1.0/tests/test_many.py +527 -0
  111. torux-0.1.0/tests/test_plot.py +502 -0
  112. torux-0.1.0/tests/test_sources.py +51 -0
  113. torux-0.1.0/tests/test_sources_live.py +250 -0
  114. torux-0.1.0/tests/test_surface.py +50 -0
  115. torux-0.1.0/tests/test_truthful.py +23 -0
torux-0.1.0/LICENSE ADDED
@@ -0,0 +1,121 @@
1
+ Creative Commons Legal Code
2
+
3
+ CC0 1.0 Universal
4
+
5
+ CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
6
+ LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
7
+ ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
8
+ INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
9
+ REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
10
+ PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
11
+ THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
12
+ HEREUNDER.
13
+
14
+ Statement of Purpose
15
+
16
+ The laws of most jurisdictions throughout the world automatically confer
17
+ exclusive Copyright and Related Rights (defined below) upon the creator
18
+ and subsequent owner(s) (each and all, an "owner") of an original work of
19
+ authorship and/or a database (each, a "Work").
20
+
21
+ Certain owners wish to permanently relinquish those rights to a Work for
22
+ the purpose of contributing to a commons of creative, cultural and
23
+ scientific works ("Commons") that the public can reliably and without fear
24
+ of later claims of infringement build upon, modify, incorporate in other
25
+ works, reuse and redistribute as freely as possible in any form whatsoever
26
+ and for any purposes, including without limitation commercial purposes.
27
+ These owners may contribute to the Commons to promote the ideal of a free
28
+ culture and the further production of creative, cultural and scientific
29
+ works, or to gain reputation or greater distribution for their Work in
30
+ part through the use and efforts of others.
31
+
32
+ For these and/or other purposes and motivations, and without any
33
+ expectation of additional consideration or compensation, the person
34
+ associating CC0 with a Work (the "Affirmer"), to the extent that he or she
35
+ is an owner of Copyright and Related Rights in the Work, voluntarily
36
+ elects to apply CC0 to the Work and publicly distribute the Work under its
37
+ terms, with knowledge of his or her Copyright and Related Rights in the
38
+ Work and the meaning and intended legal effect of CC0 on those rights.
39
+
40
+ 1. Copyright and Related Rights. A Work made available under CC0 may be
41
+ protected by copyright and related or neighboring rights ("Copyright and
42
+ Related Rights"). Copyright and Related Rights include, but are not
43
+ limited to, the following:
44
+
45
+ i. the right to reproduce, adapt, distribute, perform, display,
46
+ communicate, and translate a Work;
47
+ ii. moral rights retained by the original author(s) and/or performer(s);
48
+ iii. publicity and privacy rights pertaining to a person's image or
49
+ likeness depicted in a Work;
50
+ iv. rights protecting against unfair competition in regards to a Work,
51
+ subject to the limitations in paragraph 4(a), below;
52
+ v. rights protecting the extraction, dissemination, use and reuse of data
53
+ in a Work;
54
+ vi. database rights (such as those arising under Directive 96/9/EC of the
55
+ European Parliament and of the Council of 11 March 1996 on the legal
56
+ protection of databases, and under any national implementation
57
+ thereof, including any amended or successor version of such
58
+ directive); and
59
+ vii. other similar, equivalent or corresponding rights throughout the
60
+ world based on applicable law or treaty, and any national
61
+ implementations thereof.
62
+
63
+ 2. Waiver. To the greatest extent permitted by, but not in contravention
64
+ of, applicable law, Affirmer hereby overtly, fully, permanently,
65
+ irrevocably and unconditionally waives, abandons, and surrenders all of
66
+ Affirmer's Copyright and Related Rights and associated claims and causes
67
+ of action, whether now known or unknown (including existing as well as
68
+ future claims and causes of action), in the Work (i) in all territories
69
+ worldwide, (ii) for the maximum duration provided by applicable law or
70
+ treaty (including future time extensions), (iii) in any current or future
71
+ medium and for any number of copies, and (iv) for any purpose whatsoever,
72
+ including without limitation commercial, advertising or promotional
73
+ purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
74
+ member of the public at large and to the detriment of Affirmer's heirs and
75
+ successors, fully intending that such Waiver shall not be subject to
76
+ revocation, rescission, cancellation, termination, or any other legal or
77
+ equitable action to disrupt the quiet enjoyment of the Work by the public
78
+ as contemplated by Affirmer's express Statement of Purpose.
79
+
80
+ 3. Public License Fallback. Should any part of the Waiver for any reason
81
+ be judged legally invalid or ineffective under applicable law, then the
82
+ Waiver shall be preserved to the maximum extent permitted taking into
83
+ account Affirmer's express Statement of Purpose. In addition, to the
84
+ extent the Waiver is so judged Affirmer hereby grants to each affected
85
+ person a royalty-free, non transferable, non sublicensable, non exclusive,
86
+ irrevocable and unconditional license to exercise Affirmer's Copyright and
87
+ Related Rights in the Work (i) in all territories worldwide, (ii) for the
88
+ maximum duration provided by applicable law or treaty (including future
89
+ time extensions), (iii) in any current or future medium and for any number
90
+ of copies, and (iv) for any purpose whatsoever, including without
91
+ limitation commercial, advertising or promotional purposes (the
92
+ "License"). The License shall be deemed effective as of the date CC0 was
93
+ applied by Affirmer to the Work. Should any part of the License for any
94
+ reason be judged legally invalid or ineffective under applicable law, such
95
+ partial invalidity or ineffectiveness shall not invalidate the remainder
96
+ of the License, and in such case Affirmer hereby affirms that he or she
97
+ will not (i) exercise any of his or her remaining Copyright and Related
98
+ Rights in the Work or (ii) assert any associated claims and causes of
99
+ action with respect to the Work, in either case contrary to Affirmer's
100
+ express Statement of Purpose.
101
+
102
+ 4. Limitations and Disclaimers.
103
+
104
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
105
+ surrendered, licensed or otherwise affected by this document.
106
+ b. Affirmer offers the Work as-is and makes no representations or
107
+ warranties of any kind concerning the Work, express, implied,
108
+ statutory or otherwise, including without limitation warranties of
109
+ title, merchantability, fitness for a particular purpose, non
110
+ infringement, or the absence of latent or other defects, accuracy, or
111
+ the present or absence of errors, whether or not discoverable, all to
112
+ the greatest extent permissible under applicable law.
113
+ c. Affirmer disclaims responsibility for clearing rights of other persons
114
+ that may apply to the Work or any use thereof, including without
115
+ limitation any person's Copyright and Related Rights in the Work.
116
+ Further, Affirmer disclaims responsibility for obtaining any necessary
117
+ consents, permissions or other rights required for any use of the
118
+ Work.
119
+ d. Affirmer understands and acknowledges that Creative Commons is not a
120
+ party to this document and has no duty or obligation with respect to
121
+ this CC0 or use of the Work.
torux-0.1.0/NOTICE.md ADDED
@@ -0,0 +1,46 @@
1
+ # Credits
2
+
3
+ torux's own code, results, text and figures are public domain ([CC0 1.0](LICENSE)), all but one picture: the twelve machines ([below](#behind-the-pictures)). What it was run on is other people's work, and keeps their licences.
4
+
5
+ ## Bundled inputs (`examples/`)
6
+
7
+ | file | from | licence |
8
+ |---|---|---|
9
+ | `quasr_0000954.json`, `quasr_0219457.json`, `quasr_1100073.json` | QUASR v4, the QUAsisymmetric Stellarator Repository, Andrew Giuliani, [doi:10.5281/zenodo.13717741](https://doi.org/10.5281/zenodo.13717741) | MIT |
10
+ | `hsx.json`, `w7x.json` | the HSX and Wendelstein 7-X coil sets shipped with [simsopt](https://github.com/hiddenSymmetries/simsopt) 1.11, rewritten as Fourier-filament JSON | MIT |
11
+ | `d3d_icoils_upper.dat` | DIII-D's upper I-coils, from the coil library of [GPEC](https://github.com/OpenFUSIONToolkit/GPEC) | MIT |
12
+
13
+ `results/compare300/inputs/`: 306 coil sets of QUASR v4 (as above, MIT), each rewritten as Fourier-filament JSON with its outermost stored surface, its VMEC namelist and its row of QUASR's table.
14
+
15
+ The MIT licence texts are in [`examples/LICENCES.md`](examples/LICENCES.md).
16
+
17
+ ## Behind the results
18
+
19
+ `results/quasr.parquet`, `results/compare300.csv` and `results/refinement.csv` are measurements on coil sets of QUASR (as above).
20
+
21
+ ## Behind the pictures
22
+
23
+ The pictures are torux's own renderings. The coils and magnets drawn in them are other people's designs and data, under these licences. Checksums and origins of the files are in [`visuals/sources.json`](visuals/sources.json) and [`visuals/one-line.json`](visuals/one-line.json).
24
+
25
+ **The picture of the twelve machines is not CC0.** `visuals/any-coil-light.png` and `visuals/any-coil-dark.png` are excluded from the dedication. Three of the machines in them are drawn from files under copyleft licences (LHD: GPL-3.0, ITER and the dipole: LGPL-3.0) and three from CC BY 4.0 data (CNT, the wireframe, MUSE). Those six renderings stay under the terms of their sources, in that picture and where each machine is drawn alone in `visuals/sources/` (`heliotron`, `tokamak`, `dipole`, `interlinked`, `wireframe`, `magnets`).
26
+
27
+ | in `visuals/any-coil-*.png` and `visuals/sources/` | from | licence |
28
+ |---|---|---|
29
+ | QUASR 0124533 | QUASR v4, the QUAsisymmetric Stellarator Repository, by Andrew Giuliani, [doi:10.5281/zenodo.13717741](https://doi.org/10.5281/zenodo.13717741) | MIT |
30
+ | HSX | HSX coil set from simsopt 1.11 (hiddenSymmetries/simsopt), in torux's Fourier-filament form, [github.com/hiddenSymmetries/simsopt](https://github.com/hiddenSymmetries/simsopt) | MIT |
31
+ | Wendelstein 7-X | Wendelstein 7-X coil set from simsopt 1.11 (hiddenSymmetries/simsopt), in torux's Fourier-filament form, [github.com/hiddenSymmetries/simsopt](https://github.com/hiddenSymmetries/simsopt) | MIT |
32
+ | LHD | LHD coil model (Y. Suzuki, 2017) from the FOCUS code's examples (PrincetonUniversity/FOCUS), [github.com/PrincetonUniversity/FOCUS](https://github.com/PrincetonUniversity/FOCUS) | GPL-3.0 |
33
+ | CNT | CNT coil set from "Update on the design of the Columbia Stellarator eXperiment" by A. Baillod, R. Lopez, A. Veksler, M. Campagna, D. Schmeling, E. Paul and A. Knyazev, [doi:10.5281/zenodo.18115389](https://doi.org/10.5281/zenodo.18115389) | CC BY 4.0 |
34
+ | STAR_Lite-A | STAR_Lite-A coil set (Harrer, Giuliani, Padidar, Davies, Naik and Lowe, 2026) shipped in simsopt's configs, [github.com/hiddenSymmetries/simsopt](https://github.com/hiddenSymmetries/simsopt) | MIT |
35
+ | wireframe (RCLS) | wireframe from "Data and code for A framework for discrete optimization of stellarator coils" by Kenneth Hammond, [doi:10.34770/6pbm-9981](https://doi.org/10.34770/6pbm-9981) | CC BY 4.0 |
36
+ | MUSE | MUSE zot43 magnet and coil model from the "QUADCOIL Quasi-single-stage Dataset" by Lanke Fu, [doi:10.5281/zenodo.19923438](https://doi.org/10.5281/zenodo.19923438) | CC BY 4.0 |
37
+ | US131 (umbilic) | US131 coil set from "Omnigenous umbilic stellarators: data and analysis files" by Rahul Gaur, [doi:10.5281/zenodo.15355215](https://doi.org/10.5281/zenodo.15355215) | MIT |
38
+ | ITER | ITER coil geometry of the TokaMaker examples (OpenFUSIONToolkit), [github.com/OpenFUSIONToolkit/OpenFUSIONToolkit](https://github.com/OpenFUSIONToolkit/OpenFUSIONToolkit) | LGPL-3.0 |
39
+ | DIII-D upper I-coils | DIII-D upper I-coil geometry of the GPEC coil library (OpenFUSIONToolkit/GPEC), [github.com/OpenFUSIONToolkit/GPEC](https://github.com/OpenFUSIONToolkit/GPEC) | MIT |
40
+ | LDX-like dipole | LDX-like dipole of the TokaMaker examples (OpenFUSIONToolkit), [github.com/OpenFUSIONToolkit/OpenFUSIONToolkit](https://github.com/OpenFUSIONToolkit/OpenFUSIONToolkit) | LGPL-3.0 |
41
+
42
+ `visuals/one-line-*`: the coils of Wendelstein 7-X, from the coil set shipped with [simsopt](https://github.com/hiddenSymmetries/simsopt) 1.11 (MIT).
43
+
44
+ `visuals/plot-*`: the coils of QUASR design 954, from QUASR v4 (as above, MIT), as `torux.solve("examples/quasr_0000954.json").plot` draws them.
45
+
46
+ GPL-3.0: [gnu.org/licenses/gpl-3.0](https://www.gnu.org/licenses/gpl-3.0.html). LGPL-3.0: [gnu.org/licenses/lgpl-3.0](https://www.gnu.org/licenses/lgpl-3.0.html). CC BY 4.0: [creativecommons.org/licenses/by/4.0](https://creativecommons.org/licenses/by/4.0/). Every picture is a new rendering of the geometry in the files named. Of those files, only the ones listed under bundled inputs are redistributed here.
torux-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,185 @@
1
+ Metadata-Version: 2.4
2
+ Name: torux
3
+ Version: 0.1.0
4
+ Summary: Vacuum magnetic field and flux surfaces of coils and magnets for toroidal confinement, at the limit of double-precision arithmetic.
5
+ Author: Philipp Bogdan
6
+ License: CC0-1.0
7
+ Project-URL: Repository, https://github.com/philippbogdan/torux
8
+ Keywords: stellarator,plasma physics,magnetic confinement,flux surfaces,fusion
9
+ Requires-Python: >=3.12
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ License-File: NOTICE.md
13
+ Requires-Dist: numpy>=2.4
14
+ Requires-Dist: scipy>=1.18
15
+ Requires-Dist: jax>=0.9
16
+ Requires-Dist: numba>=0.65
17
+ Requires-Dist: mpmath>=1.3
18
+ Requires-Dist: threadpoolctl>=3.1
19
+ Provides-Extra: hdf5
20
+ Requires-Dist: h5py>=3.10; extra == "hdf5"
21
+ Provides-Extra: plot
22
+ Requires-Dist: matplotlib>=3.9; extra == "plot"
23
+ Provides-Extra: all
24
+ Requires-Dist: h5py>=3.10; extra == "all"
25
+ Requires-Dist: matplotlib>=3.9; extra == "all"
26
+ Provides-Extra: test
27
+ Requires-Dist: pytest>=8; extra == "test"
28
+ Requires-Dist: pandas>=2; extra == "test"
29
+ Requires-Dist: pyarrow>=15; extra == "test"
30
+ Dynamic: license-file
31
+
32
+ <p align="center">
33
+ <picture>
34
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/philippbogdan/torux/main/visuals/torux-dark.png">
35
+ <img src="https://raw.githubusercontent.com/philippbogdan/torux/main/visuals/torux-light.png" width="830" alt="TORUX">
36
+ </picture>
37
+ </p>
38
+
39
+ <p align="center">
40
+ <picture>
41
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/philippbogdan/torux/main/visuals/any-coil-dark.png">
42
+ <img src="https://raw.githubusercontent.com/philippbogdan/torux/main/visuals/any-coil-light.png" width="838" alt="Twelve magnet systems for fusion stand in three rows of four, drawn in copper: stellarator coil sets of many shapes, a wireframe, a ring of permanent magnets inside upright coils, the coils of the ITER tokamak, six window-frame coils and a levitated ring. Inside nine of them, teal light woven from field lines fills the vacuum magnetic surface torux verified. The last three, ITER's coils, DIII-D's I-coils and the dipole, hold no light: thin grey field lines loop round their coils instead and never go round the machine.">
43
+ </picture>
44
+ </p>
45
+
46
+ <h1 align="center">Arithmetic limit, extremely fast. Any coil works.</h1>
47
+
48
+ <p align="center"><sub>(The aspiration. torux is in development, and vacuum only for now: no finite pressure.)</sub></p>
49
+
50
+ **Give torux the coils of a stellarator. Three seconds later you have the magnetic surface they make, right to the fifteenth digit: the last one a computer holds.**
51
+
52
+ It has done that for 231,878 of the 371,701 coil sets in [QUASR](https://quasr.flatironinstitute.org), a public database of stellarator designs, in a median of three seconds each. A second evaluator, sharing no code with the solver, checked every one. For 5,206 more it returned a certificate that the surface asked for cannot exist, and for another 846 a weaker one. It never guesses: the other 133,771 it calls undetermined, three in four of them after less than a minute's work.
53
+
54
+ Above: twelve kinds of magnet, one solver. Nine carry the surface torux verified for them, drawn as light. The other three (a tokamak's poloidal coils, its perturbation coils, a levitated dipole) carry none, and that is an answer too: their field lines, the thin grey ones, close round the coils and never go round the machine, so no surface can enclose toroidal flux, and torux proves it. (A tokamak's surfaces need the current in its plasma, and a dipole holds its plasma on the closed lines themselves.) [Each one, close up.](https://github.com/philippbogdan/torux/blob/main/visuals/README.md)
55
+
56
+ ## Fifteen digits
57
+
58
+ <picture>
59
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/philippbogdan/torux/main/visuals/accuracy-dark.svg">
60
+ <img src="https://raw.githubusercontent.com/philippbogdan/torux/main/visuals/accuracy-light.svg" width="838" alt="One row of dots for each tool on a logarithmic axis of the field crossing a surface, from 10 to the minus 15 up to 1. torux: 201 dots between 3 times 10 to the minus 16 and 7 times 10 to the minus 15, median 1 times 10 to the minus 15. SIMSOPT BoozerLS: 283 dots between 2 times 10 to the minus 15 and 4 times 10 to the minus 2, median 2 times 10 to the minus 8. SIMSOPT BoozerExact: 288 dots between 5 times 10 to the minus 15 and 2 times 10 to the minus 1, median 3 times 10 to the minus 8. SIMSOPT QFM: 297 dots between 5 times 10 to the minus 14 and 1, median 3 times 10 to the minus 7. VMEC++: 252 dots between 2 times 10 to the minus 6 and 2 times 10 to the minus 1, median 9 times 10 to the minus 4. DESC: 199 dots between 3 times 10 to the minus 8 and 2 times 10 to the minus 1, median 3 times 10 to the minus 3. QUASR's stored surface: 300 dots between 8 times 10 to the minus 9 and 2 times 10 to the minus 1, median 9 times 10 to the minus 4.">
61
+ </picture>
62
+
63
+ Three hundred coil sets drawn at random from QUASR. Every tool gets the same coils, the same requested flux and an hour on four threads for each. The other five need settings, and we chose them: the route each tool's documentation recommends, pushed wherever that paid on six coil sets outside the 300. torux has no settings to choose. One judge: an evaluator that shares no code with any of them, working in extended precision at 4,000 random points of each surface. A dot is a coil set. Its place is how much of the field leaks through the surface. [How each tool was run, and how to run a row again.](https://github.com/philippbogdan/torux/blob/main/results/compare300/README.md)
64
+
65
+ torux's surfaces leak 1.1 × 10⁻¹⁵, within 2.9 times the least that a surface written in double precision can leak. The worst of its 201 leaks 6.8 × 10⁻¹⁵. On the 199 coil sets both solved, the closest tool, SIMSOPT BoozerExact, leaks 700,000 times more: 5 orders of magnitude. The surfaces the designs are stored with, where every solve starts, leak 8.9 × 10⁻⁴: they are coarse, a few hundred Fourier terms each, where torux's take thousands. [Every dot is a row of `results/compare300.csv`.](https://github.com/philippbogdan/torux/blob/main/results/compare300.csv)
66
+
67
+ | | surfaces returned | median leak | within 10⁻¹² | median seconds |
68
+ |---|---:|---:|---:|---:|
69
+ | **torux** | 201 of 300 | 1.1 × 10⁻¹⁵ | 201 | 4.2 |
70
+ | SIMSOPT BoozerLS | 283 of 300 | 2.4 × 10⁻⁸ | 38 | 824 |
71
+ | SIMSOPT BoozerExact | 288 of 300 | 2.7 × 10⁻⁸ | 51 | 67 |
72
+ | SIMSOPT QFM | 297 of 300 | 3.3 × 10⁻⁷ | 14 | 1,770 |
73
+ | VMEC++ | 252 of 300 | 9.4 × 10⁻⁴ | 0 | 1,525 |
74
+ | DESC | 199 of 300 | 2.6 × 10⁻³ | 0 | 885 |
75
+ | QUASR's stored surface | 300 of 300 | 8.9 × 10⁻⁴ | 0 | |
76
+
77
+ Nobody needs fifteen digits of a flux surface. What you need is never to wonder whether the surface is wrong or the solver. And at this precision "there is no surface" stops being a solve that failed and becomes an answer torux can certify: 22 of these 300, and 16 more by its weaker kind of certificate. The other 61 were still undetermined after their hour.
78
+
79
+ ## Seconds
80
+
81
+ <picture>
82
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/philippbogdan/torux/main/visuals/time-dark.svg">
83
+ <img src="https://raw.githubusercontent.com/philippbogdan/torux/main/visuals/time-light.svg" width="838" alt="One row of dots for each tool on a logarithmic axis of seconds, from a tenth of a second to an hour: the time at which each tool had the surface it returned. torux: median 4.2 seconds. SIMSOPT BoozerExact: median 67 seconds. SIMSOPT BoozerLS: median 824 seconds. DESC: median 885 seconds. VMEC++: median 1,525 seconds. SIMSOPT QFM: median 1,770 seconds.">
84
+ </picture>
85
+
86
+ The same 300 coil sets on one processor (AMD EPYC 7742), four threads each. A dot is a surface a tool returned, placed at the time at which the tool had it, the tool's own checks included. torux: 4.2 s in the median, and then it stops. The others were set to spend their hour on accuracy and go on working after the surface they return, VMEC++ and SIMSOPT QFM nearly always until the hour ends: that later time is not counted here.
87
+
88
+ - SIMSOPT BoozerExact: 67 s. On the 199 coil sets both solved it was faster on 12 and more accurate on 0.
89
+ - SIMSOPT BoozerLS: 824 s. On the 199 coil sets both solved it was faster on 2 and more accurate on 0.
90
+ - DESC: 885 s. On the 141 coil sets both solved it was faster on 0 and more accurate on 0.
91
+ - VMEC++: 1,525 s. On the 177 coil sets both solved it was faster on 1 and more accurate on 0.
92
+ - SIMSOPT QFM: 1,770 s. On the 199 coil sets both solved it was faster on 0 and more accurate on 0.
93
+
94
+ ## How it works
95
+
96
+ A stellarator is a fusion machine. Coils wind a magnetic field into a twisted ring, and the field holds a plasma hotter than the centre of the Sun. Follow one line of that field and it goes round and round the ring. If it never leaves one closed surface, that surface is a wall that holds the plasma in. Whether a set of coils makes such walls, and exactly where, is the first question of every design.
97
+
98
+ <p align="center">
99
+ <picture>
100
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/philippbogdan/torux/main/visuals/one-line-dark.gif">
101
+ <img src="https://raw.githubusercontent.com/philippbogdan/torux/main/visuals/one-line-light.gif" width="838" alt="Animation. The coils of the Wendelstein 7-X stellarator, cut away at the front. One magnetic field line, drawn as a point of green light, runs round inside them lap after lap, faster and faster, and its trail weaves a twisted, ring-shaped surface of fine fibres that lights the coils from inside: the magnetic surface torux computed, which the line never leaves.">
102
+ </picture>
103
+ </p>
104
+
105
+ <p align="center"><sub>One field line of Wendelstein 7-X, followed for 200 laps (7.3 km): it never strays more than 1.1 × 10⁻¹³ m from the surface torux computed. <a href="visuals/README.md#one-field-line">How that was measured.</a></sub></p>
106
+
107
+ torux does not follow the line. It writes the surface as a sum of smooth waves and asks one thing of it: that the field crosses it nowhere. Newton's method finds the waves. Each refinement takes orders of magnitude off the error, until the digits of a 64-bit number run out:
108
+
109
+ <picture>
110
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/philippbogdan/torux/main/visuals/refinement-dark.svg">
111
+ <img src="https://raw.githubusercontent.com/philippbogdan/torux/main/visuals/refinement-light.svg" width="838" alt="For 201 coil sets, the field crossing the surface against the number of coefficients describing it. Every line falls steeply, from between 10 to the minus 6 and 10 to the minus 12, and ends just above the limit of double precision near 10 to the minus 15.">
112
+ </picture>
113
+
114
+ Then a second program, written apart from the first and sharing none of its code, measures the leak at 4,096 random points. Only if both agree is the answer "surface".
115
+
116
+ <picture>
117
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/philippbogdan/torux/main/visuals/answers-dark.svg">
118
+ <img src="https://raw.githubusercontent.com/philippbogdan/torux/main/visuals/answers-light.svg" width="838" alt="Coils and a flux go to a Newton solve for the surface. If it converges, different code checks it again at 4,096 points. If that agrees, the answer is surface, and if it disagrees, undetermined. If the solve fails, torux looks for what forbids a surface, an island chain or the edge of confinement: found and bounded, the answer is no surface, with a certificate, and out of time it is undetermined.">
119
+ </picture>
120
+
121
+ When Newton fails, the surface may not be there. Near a resonance the field lines close on themselves and the surfaces break into a chain of islands. Past the edge of confinement the lines wander off and are lost. torux finds the chain or the edge, fences it with curves that no surface can cross, and bounds the flux they forbid: a certificate. That is a theorem with a measured error budget, not a machine-checked proof. Of QUASR's certificates, the 5,206 island bands rest on an exact symmetry of the field, and the 846 weaker ones are edge barriers, which rest on a weaker error model. With neither a surface nor a certificate inside its time budget, torux says so.
122
+
123
+ [The method, for people who know the field.](https://github.com/philippbogdan/torux/blob/main/docs/method.md) [What is checked, and by what.](https://github.com/philippbogdan/torux/blob/main/docs/verification.md)
124
+
125
+ ## Use it
126
+
127
+ ```sh
128
+ git clone https://github.com/philippbogdan/torux && cd torux
129
+ pip install .
130
+ torux solve examples/quasr_0000954.json
131
+ ```
132
+
133
+ ```
134
+ surface verified: the solver's check and an independent evaluator agree
135
+ toroidal flux 0.069106905814 Wb
136
+ rotational transform -0.0975229741144
137
+ radii major 0.999994679 m, minor 0.150000523 m, aspect ratio 6.66661
138
+ volume 0.444132929 m^3
139
+ Fourier series M, N = 48, 32, with 2 field periods
140
+ field across it 1.1e-15 of |B| (the solver's own check)
141
+ independent check 3.3e-15 of |B| across it, and the same flux to within 8e-16
142
+ time 0.7 s
143
+ ```
144
+
145
+ The first run compiles for up to a minute. After that, seconds.
146
+
147
+ ```python
148
+ import torux
149
+
150
+ s = torux.solve("coils.json", minor_radius=0.15) # or flux=0.07 (Wb), volume=0.44 (m³), largest=True
151
+ print(s) # what it found and how it was checked, or why there is no surface
152
+ s.iota, s.volume, s.aspect_ratio # the numbers
153
+ s.save("input.mine") # a VMEC input: VMEC, VMEC++, DESC and SIMSOPT read it
154
+ s.plot("mine.png") # the picture below
155
+ s.boozer().quasisymmetry # |B| in Boozer angles, and how far it is from quasi-symmetry
156
+
157
+ torux.check("coils.json", "wout_other.nc") # a surface from another code: how much of the field crosses it
158
+ torux.many(files, table="answers.csv") # a folder of designs, one row each
159
+ ```
160
+
161
+ <p align="center">
162
+ <picture>
163
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/philippbogdan/torux/main/visuals/plot-dark.png">
164
+ <img src="https://raw.githubusercontent.com/philippbogdan/torux/main/visuals/plot-light.png" width="760" alt="What s.plot draws for the surface above: on the left the flux surface as a green torus, shaded by the strength of the field on it, threaded through its four copper coils. On the right are its cross-sections at three toroidal angles, an ellipse lying flat, a rounded triangle and an ellipse standing upright, and the scale of the field strength, about 0.9 to 1.1 tesla.">
165
+ </picture>
166
+ </p>
167
+
168
+ Nothing to tune: no resolution, no tolerance, no starting guess. The coils are a file (SIMSOPT and QUASR JSON, MAKEGRID, FOCUS, permanent magnets, wireframes, a tokamak's coils), a QUASR design by its number, or a SIMSOPT or DESC object you already hold. [Usage](https://github.com/philippbogdan/torux/blob/main/docs/usage.md) and [formats](https://github.com/philippbogdan/torux/blob/main/docs/formats.md).
169
+
170
+ ## Credit
171
+
172
+ Used torux? Say so, so the next person finds it:
173
+
174
+ > Flux surfaces computed with [torux](https://github.com/philippbogdan/torux).
175
+
176
+ ```bibtex
177
+ @software{bogdan2026torux,
178
+ author = {Bogdan, Philipp},
179
+ title = {torux: vacuum flux surfaces at the limit of double-precision arithmetic},
180
+ year = {2026},
181
+ url = {https://github.com/philippbogdan/torux}
182
+ }
183
+ ```
184
+
185
+ Public domain, [CC0 1.0](https://github.com/philippbogdan/torux/blob/main/LICENSE), all but the picture of the twelve machines: six of them are drawn from files under GPL, LGPL and CC BY licences, and those renderings stay under their sources' terms. The coil sets torux was run on are other people's work, under their own licences: [credits](https://github.com/philippbogdan/torux/blob/main/NOTICE.md).
torux-0.1.0/README.md ADDED
@@ -0,0 +1,154 @@
1
+ <p align="center">
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/philippbogdan/torux/main/visuals/torux-dark.png">
4
+ <img src="https://raw.githubusercontent.com/philippbogdan/torux/main/visuals/torux-light.png" width="830" alt="TORUX">
5
+ </picture>
6
+ </p>
7
+
8
+ <p align="center">
9
+ <picture>
10
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/philippbogdan/torux/main/visuals/any-coil-dark.png">
11
+ <img src="https://raw.githubusercontent.com/philippbogdan/torux/main/visuals/any-coil-light.png" width="838" alt="Twelve magnet systems for fusion stand in three rows of four, drawn in copper: stellarator coil sets of many shapes, a wireframe, a ring of permanent magnets inside upright coils, the coils of the ITER tokamak, six window-frame coils and a levitated ring. Inside nine of them, teal light woven from field lines fills the vacuum magnetic surface torux verified. The last three, ITER's coils, DIII-D's I-coils and the dipole, hold no light: thin grey field lines loop round their coils instead and never go round the machine.">
12
+ </picture>
13
+ </p>
14
+
15
+ <h1 align="center">Arithmetic limit, extremely fast. Any coil works.</h1>
16
+
17
+ <p align="center"><sub>(The aspiration. torux is in development, and vacuum only for now: no finite pressure.)</sub></p>
18
+
19
+ **Give torux the coils of a stellarator. Three seconds later you have the magnetic surface they make, right to the fifteenth digit: the last one a computer holds.**
20
+
21
+ It has done that for 231,878 of the 371,701 coil sets in [QUASR](https://quasr.flatironinstitute.org), a public database of stellarator designs, in a median of three seconds each. A second evaluator, sharing no code with the solver, checked every one. For 5,206 more it returned a certificate that the surface asked for cannot exist, and for another 846 a weaker one. It never guesses: the other 133,771 it calls undetermined, three in four of them after less than a minute's work.
22
+
23
+ Above: twelve kinds of magnet, one solver. Nine carry the surface torux verified for them, drawn as light. The other three (a tokamak's poloidal coils, its perturbation coils, a levitated dipole) carry none, and that is an answer too: their field lines, the thin grey ones, close round the coils and never go round the machine, so no surface can enclose toroidal flux, and torux proves it. (A tokamak's surfaces need the current in its plasma, and a dipole holds its plasma on the closed lines themselves.) [Each one, close up.](https://github.com/philippbogdan/torux/blob/main/visuals/README.md)
24
+
25
+ ## Fifteen digits
26
+
27
+ <picture>
28
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/philippbogdan/torux/main/visuals/accuracy-dark.svg">
29
+ <img src="https://raw.githubusercontent.com/philippbogdan/torux/main/visuals/accuracy-light.svg" width="838" alt="One row of dots for each tool on a logarithmic axis of the field crossing a surface, from 10 to the minus 15 up to 1. torux: 201 dots between 3 times 10 to the minus 16 and 7 times 10 to the minus 15, median 1 times 10 to the minus 15. SIMSOPT BoozerLS: 283 dots between 2 times 10 to the minus 15 and 4 times 10 to the minus 2, median 2 times 10 to the minus 8. SIMSOPT BoozerExact: 288 dots between 5 times 10 to the minus 15 and 2 times 10 to the minus 1, median 3 times 10 to the minus 8. SIMSOPT QFM: 297 dots between 5 times 10 to the minus 14 and 1, median 3 times 10 to the minus 7. VMEC++: 252 dots between 2 times 10 to the minus 6 and 2 times 10 to the minus 1, median 9 times 10 to the minus 4. DESC: 199 dots between 3 times 10 to the minus 8 and 2 times 10 to the minus 1, median 3 times 10 to the minus 3. QUASR's stored surface: 300 dots between 8 times 10 to the minus 9 and 2 times 10 to the minus 1, median 9 times 10 to the minus 4.">
30
+ </picture>
31
+
32
+ Three hundred coil sets drawn at random from QUASR. Every tool gets the same coils, the same requested flux and an hour on four threads for each. The other five need settings, and we chose them: the route each tool's documentation recommends, pushed wherever that paid on six coil sets outside the 300. torux has no settings to choose. One judge: an evaluator that shares no code with any of them, working in extended precision at 4,000 random points of each surface. A dot is a coil set. Its place is how much of the field leaks through the surface. [How each tool was run, and how to run a row again.](https://github.com/philippbogdan/torux/blob/main/results/compare300/README.md)
33
+
34
+ torux's surfaces leak 1.1 × 10⁻¹⁵, within 2.9 times the least that a surface written in double precision can leak. The worst of its 201 leaks 6.8 × 10⁻¹⁵. On the 199 coil sets both solved, the closest tool, SIMSOPT BoozerExact, leaks 700,000 times more: 5 orders of magnitude. The surfaces the designs are stored with, where every solve starts, leak 8.9 × 10⁻⁴: they are coarse, a few hundred Fourier terms each, where torux's take thousands. [Every dot is a row of `results/compare300.csv`.](https://github.com/philippbogdan/torux/blob/main/results/compare300.csv)
35
+
36
+ | | surfaces returned | median leak | within 10⁻¹² | median seconds |
37
+ |---|---:|---:|---:|---:|
38
+ | **torux** | 201 of 300 | 1.1 × 10⁻¹⁵ | 201 | 4.2 |
39
+ | SIMSOPT BoozerLS | 283 of 300 | 2.4 × 10⁻⁸ | 38 | 824 |
40
+ | SIMSOPT BoozerExact | 288 of 300 | 2.7 × 10⁻⁸ | 51 | 67 |
41
+ | SIMSOPT QFM | 297 of 300 | 3.3 × 10⁻⁷ | 14 | 1,770 |
42
+ | VMEC++ | 252 of 300 | 9.4 × 10⁻⁴ | 0 | 1,525 |
43
+ | DESC | 199 of 300 | 2.6 × 10⁻³ | 0 | 885 |
44
+ | QUASR's stored surface | 300 of 300 | 8.9 × 10⁻⁴ | 0 | |
45
+
46
+ Nobody needs fifteen digits of a flux surface. What you need is never to wonder whether the surface is wrong or the solver. And at this precision "there is no surface" stops being a solve that failed and becomes an answer torux can certify: 22 of these 300, and 16 more by its weaker kind of certificate. The other 61 were still undetermined after their hour.
47
+
48
+ ## Seconds
49
+
50
+ <picture>
51
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/philippbogdan/torux/main/visuals/time-dark.svg">
52
+ <img src="https://raw.githubusercontent.com/philippbogdan/torux/main/visuals/time-light.svg" width="838" alt="One row of dots for each tool on a logarithmic axis of seconds, from a tenth of a second to an hour: the time at which each tool had the surface it returned. torux: median 4.2 seconds. SIMSOPT BoozerExact: median 67 seconds. SIMSOPT BoozerLS: median 824 seconds. DESC: median 885 seconds. VMEC++: median 1,525 seconds. SIMSOPT QFM: median 1,770 seconds.">
53
+ </picture>
54
+
55
+ The same 300 coil sets on one processor (AMD EPYC 7742), four threads each. A dot is a surface a tool returned, placed at the time at which the tool had it, the tool's own checks included. torux: 4.2 s in the median, and then it stops. The others were set to spend their hour on accuracy and go on working after the surface they return, VMEC++ and SIMSOPT QFM nearly always until the hour ends: that later time is not counted here.
56
+
57
+ - SIMSOPT BoozerExact: 67 s. On the 199 coil sets both solved it was faster on 12 and more accurate on 0.
58
+ - SIMSOPT BoozerLS: 824 s. On the 199 coil sets both solved it was faster on 2 and more accurate on 0.
59
+ - DESC: 885 s. On the 141 coil sets both solved it was faster on 0 and more accurate on 0.
60
+ - VMEC++: 1,525 s. On the 177 coil sets both solved it was faster on 1 and more accurate on 0.
61
+ - SIMSOPT QFM: 1,770 s. On the 199 coil sets both solved it was faster on 0 and more accurate on 0.
62
+
63
+ ## How it works
64
+
65
+ A stellarator is a fusion machine. Coils wind a magnetic field into a twisted ring, and the field holds a plasma hotter than the centre of the Sun. Follow one line of that field and it goes round and round the ring. If it never leaves one closed surface, that surface is a wall that holds the plasma in. Whether a set of coils makes such walls, and exactly where, is the first question of every design.
66
+
67
+ <p align="center">
68
+ <picture>
69
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/philippbogdan/torux/main/visuals/one-line-dark.gif">
70
+ <img src="https://raw.githubusercontent.com/philippbogdan/torux/main/visuals/one-line-light.gif" width="838" alt="Animation. The coils of the Wendelstein 7-X stellarator, cut away at the front. One magnetic field line, drawn as a point of green light, runs round inside them lap after lap, faster and faster, and its trail weaves a twisted, ring-shaped surface of fine fibres that lights the coils from inside: the magnetic surface torux computed, which the line never leaves.">
71
+ </picture>
72
+ </p>
73
+
74
+ <p align="center"><sub>One field line of Wendelstein 7-X, followed for 200 laps (7.3 km): it never strays more than 1.1 × 10⁻¹³ m from the surface torux computed. <a href="visuals/README.md#one-field-line">How that was measured.</a></sub></p>
75
+
76
+ torux does not follow the line. It writes the surface as a sum of smooth waves and asks one thing of it: that the field crosses it nowhere. Newton's method finds the waves. Each refinement takes orders of magnitude off the error, until the digits of a 64-bit number run out:
77
+
78
+ <picture>
79
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/philippbogdan/torux/main/visuals/refinement-dark.svg">
80
+ <img src="https://raw.githubusercontent.com/philippbogdan/torux/main/visuals/refinement-light.svg" width="838" alt="For 201 coil sets, the field crossing the surface against the number of coefficients describing it. Every line falls steeply, from between 10 to the minus 6 and 10 to the minus 12, and ends just above the limit of double precision near 10 to the minus 15.">
81
+ </picture>
82
+
83
+ Then a second program, written apart from the first and sharing none of its code, measures the leak at 4,096 random points. Only if both agree is the answer "surface".
84
+
85
+ <picture>
86
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/philippbogdan/torux/main/visuals/answers-dark.svg">
87
+ <img src="https://raw.githubusercontent.com/philippbogdan/torux/main/visuals/answers-light.svg" width="838" alt="Coils and a flux go to a Newton solve for the surface. If it converges, different code checks it again at 4,096 points. If that agrees, the answer is surface, and if it disagrees, undetermined. If the solve fails, torux looks for what forbids a surface, an island chain or the edge of confinement: found and bounded, the answer is no surface, with a certificate, and out of time it is undetermined.">
88
+ </picture>
89
+
90
+ When Newton fails, the surface may not be there. Near a resonance the field lines close on themselves and the surfaces break into a chain of islands. Past the edge of confinement the lines wander off and are lost. torux finds the chain or the edge, fences it with curves that no surface can cross, and bounds the flux they forbid: a certificate. That is a theorem with a measured error budget, not a machine-checked proof. Of QUASR's certificates, the 5,206 island bands rest on an exact symmetry of the field, and the 846 weaker ones are edge barriers, which rest on a weaker error model. With neither a surface nor a certificate inside its time budget, torux says so.
91
+
92
+ [The method, for people who know the field.](https://github.com/philippbogdan/torux/blob/main/docs/method.md) [What is checked, and by what.](https://github.com/philippbogdan/torux/blob/main/docs/verification.md)
93
+
94
+ ## Use it
95
+
96
+ ```sh
97
+ git clone https://github.com/philippbogdan/torux && cd torux
98
+ pip install .
99
+ torux solve examples/quasr_0000954.json
100
+ ```
101
+
102
+ ```
103
+ surface verified: the solver's check and an independent evaluator agree
104
+ toroidal flux 0.069106905814 Wb
105
+ rotational transform -0.0975229741144
106
+ radii major 0.999994679 m, minor 0.150000523 m, aspect ratio 6.66661
107
+ volume 0.444132929 m^3
108
+ Fourier series M, N = 48, 32, with 2 field periods
109
+ field across it 1.1e-15 of |B| (the solver's own check)
110
+ independent check 3.3e-15 of |B| across it, and the same flux to within 8e-16
111
+ time 0.7 s
112
+ ```
113
+
114
+ The first run compiles for up to a minute. After that, seconds.
115
+
116
+ ```python
117
+ import torux
118
+
119
+ s = torux.solve("coils.json", minor_radius=0.15) # or flux=0.07 (Wb), volume=0.44 (m³), largest=True
120
+ print(s) # what it found and how it was checked, or why there is no surface
121
+ s.iota, s.volume, s.aspect_ratio # the numbers
122
+ s.save("input.mine") # a VMEC input: VMEC, VMEC++, DESC and SIMSOPT read it
123
+ s.plot("mine.png") # the picture below
124
+ s.boozer().quasisymmetry # |B| in Boozer angles, and how far it is from quasi-symmetry
125
+
126
+ torux.check("coils.json", "wout_other.nc") # a surface from another code: how much of the field crosses it
127
+ torux.many(files, table="answers.csv") # a folder of designs, one row each
128
+ ```
129
+
130
+ <p align="center">
131
+ <picture>
132
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/philippbogdan/torux/main/visuals/plot-dark.png">
133
+ <img src="https://raw.githubusercontent.com/philippbogdan/torux/main/visuals/plot-light.png" width="760" alt="What s.plot draws for the surface above: on the left the flux surface as a green torus, shaded by the strength of the field on it, threaded through its four copper coils. On the right are its cross-sections at three toroidal angles, an ellipse lying flat, a rounded triangle and an ellipse standing upright, and the scale of the field strength, about 0.9 to 1.1 tesla.">
134
+ </picture>
135
+ </p>
136
+
137
+ Nothing to tune: no resolution, no tolerance, no starting guess. The coils are a file (SIMSOPT and QUASR JSON, MAKEGRID, FOCUS, permanent magnets, wireframes, a tokamak's coils), a QUASR design by its number, or a SIMSOPT or DESC object you already hold. [Usage](https://github.com/philippbogdan/torux/blob/main/docs/usage.md) and [formats](https://github.com/philippbogdan/torux/blob/main/docs/formats.md).
138
+
139
+ ## Credit
140
+
141
+ Used torux? Say so, so the next person finds it:
142
+
143
+ > Flux surfaces computed with [torux](https://github.com/philippbogdan/torux).
144
+
145
+ ```bibtex
146
+ @software{bogdan2026torux,
147
+ author = {Bogdan, Philipp},
148
+ title = {torux: vacuum flux surfaces at the limit of double-precision arithmetic},
149
+ year = {2026},
150
+ url = {https://github.com/philippbogdan/torux}
151
+ }
152
+ ```
153
+
154
+ Public domain, [CC0 1.0](https://github.com/philippbogdan/torux/blob/main/LICENSE), all but the picture of the twelve machines: six of them are drawn from files under GPL, LGPL and CC BY licences, and those renderings stay under their sources' terms. The coil sets torux was run on are other people's work, under their own licences: [credits](https://github.com/philippbogdan/torux/blob/main/NOTICE.md).
@@ -0,0 +1,41 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "torux"
7
+ version = "0.1.0"
8
+ description = "Vacuum magnetic field and flux surfaces of coils and magnets for toroidal confinement, at the limit of double-precision arithmetic."
9
+ readme = "README.md"
10
+ requires-python = ">=3.12"
11
+ license = { text = "CC0-1.0" }
12
+ authors = [{ name = "Philipp Bogdan" }]
13
+ keywords = ["stellarator", "plasma physics", "magnetic confinement", "flux surfaces", "fusion"]
14
+ dependencies = [
15
+ "numpy>=2.4",
16
+ "scipy>=1.18",
17
+ "jax>=0.9",
18
+ "numba>=0.65",
19
+ "mpmath>=1.3",
20
+ "threadpoolctl>=3.1",
21
+ ]
22
+
23
+ [project.optional-dependencies]
24
+ hdf5 = ["h5py>=3.10"]
25
+ plot = ["matplotlib>=3.9"]
26
+ all = ["h5py>=3.10", "matplotlib>=3.9"]
27
+ test = ["pytest>=8", "pandas>=2", "pyarrow>=15"]
28
+
29
+ [project.urls]
30
+ Repository = "https://github.com/philippbogdan/torux"
31
+
32
+ [project.scripts]
33
+ torux = "torux.cli:main"
34
+
35
+ [tool.setuptools.packages.find]
36
+ where = ["src"]
37
+
38
+ [tool.pytest.ini_options]
39
+ testpaths = ["tests"]
40
+ addopts = "-m 'not slow'"
41
+ markers = ["slow: takes minutes (the certificates); run with -m slow"]
torux-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+