physicskit 0.1.0__py3-none-any.whl

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 (484) hide show
  1. physicskit/__init__.py +51 -0
  2. physicskit/astro/__init__.py +66 -0
  3. physicskit/astro/cosmic_web.py +395 -0
  4. physicskit/astro/galactic_dynamics.py +137 -0
  5. physicskit/astro/nbody.py +208 -0
  6. physicskit/astro/orbital_mechanics.py +220 -0
  7. physicskit/astro/stellar_dynamo.py +628 -0
  8. physicskit/astro/stellar_structure.py +198 -0
  9. physicskit/astro/tests/__init__.py +0 -0
  10. physicskit/astro/tests/conftest.py +7 -0
  11. physicskit/astro/tests/test_cosmic_web.py +159 -0
  12. physicskit/astro/tests/test_galactic_dynamics.py +46 -0
  13. physicskit/astro/tests/test_nbody.py +81 -0
  14. physicskit/astro/tests/test_orbital_mechanics.py +73 -0
  15. physicskit/astro/tests/test_stellar_dynamo.py +198 -0
  16. physicskit/astro/tests/test_stellar_structure.py +52 -0
  17. physicskit/astro/tests/test_visualizers.py +159 -0
  18. physicskit/astro/visualizers.py +628 -0
  19. physicskit/chaos/__init__.py +55 -0
  20. physicskit/chaos/core/__init__.py +3 -0
  21. physicskit/chaos/core/base_system.py +342 -0
  22. physicskit/chaos/core/integrators.py +40 -0
  23. physicskit/chaos/exceptions.py +24 -0
  24. physicskit/chaos/quantum/__init__.py +38 -0
  25. physicskit/chaos/quantum/billiards.py +274 -0
  26. physicskit/chaos/quantum/husimi.py +85 -0
  27. physicskit/chaos/quantum/maps.py +366 -0
  28. physicskit/chaos/systems/__init__.py +38 -0
  29. physicskit/chaos/systems/billiards.py +1131 -0
  30. physicskit/chaos/systems/continuous.py +1276 -0
  31. physicskit/chaos/systems/maps.py +557 -0
  32. physicskit/chaos/tests/__init__.py +0 -0
  33. physicskit/chaos/tests/test_basins.py +76 -0
  34. physicskit/chaos/tests/test_bifurcation.py +132 -0
  35. physicskit/chaos/tests/test_billiards.py +115 -0
  36. physicskit/chaos/tests/test_continuous.py +224 -0
  37. physicskit/chaos/tests/test_dimension.py +69 -0
  38. physicskit/chaos/tests/test_divergence.py +90 -0
  39. physicskit/chaos/tests/test_dynamic_plots.py +269 -0
  40. physicskit/chaos/tests/test_integrators.py +96 -0
  41. physicskit/chaos/tests/test_io.py +41 -0
  42. physicskit/chaos/tests/test_maps.py +110 -0
  43. physicskit/chaos/tests/test_metrics.py +115 -0
  44. physicskit/chaos/tests/test_phase_space.py +73 -0
  45. physicskit/chaos/tests/test_properties.py +116 -0
  46. physicskit/chaos/tests/test_quantum_billiards.py +94 -0
  47. physicskit/chaos/tests/test_quantum_maps.py +144 -0
  48. physicskit/chaos/tests/test_recurrence.py +51 -0
  49. physicskit/chaos/tests/test_repr_and_api_consistency.py +134 -0
  50. physicskit/chaos/tests/test_section.py +49 -0
  51. physicskit/chaos/tests/test_spectral.py +48 -0
  52. physicskit/chaos/tests/test_streamlit_app.py +45 -0
  53. physicskit/chaos/tests/test_timeseries.py +131 -0
  54. physicskit/chaos/tests/test_viewer3d.py +41 -0
  55. physicskit/chaos/utils/__init__.py +46 -0
  56. physicskit/chaos/utils/dimension.py +139 -0
  57. physicskit/chaos/utils/io.py +95 -0
  58. physicskit/chaos/utils/metrics.py +304 -0
  59. physicskit/chaos/utils/recurrence.py +122 -0
  60. physicskit/chaos/utils/spectral.py +69 -0
  61. physicskit/chaos/utils/timeseries.py +303 -0
  62. physicskit/chaos/visualizers/__init__.py +85 -0
  63. physicskit/chaos/visualizers/basins.py +185 -0
  64. physicskit/chaos/visualizers/bifurcation.py +273 -0
  65. physicskit/chaos/visualizers/divergence.py +374 -0
  66. physicskit/chaos/visualizers/dynamic_plots.py +1372 -0
  67. physicskit/chaos/visualizers/phase_space.py +133 -0
  68. physicskit/chaos/visualizers/quantum.py +299 -0
  69. physicskit/chaos/visualizers/recurrence.py +91 -0
  70. physicskit/chaos/visualizers/section.py +161 -0
  71. physicskit/chaos/visualizers/theme.py +31 -0
  72. physicskit/chaos/visualizers/viewer3d.py +82 -0
  73. physicskit/classical/__init__.py +55 -0
  74. physicskit/classical/core/__init__.py +17 -0
  75. physicskit/classical/core/base_system.py +510 -0
  76. physicskit/classical/core/integrators.py +397 -0
  77. physicskit/classical/systems/__init__.py +32 -0
  78. physicskit/classical/systems/chains.py +334 -0
  79. physicskit/classical/systems/hamiltonian.py +215 -0
  80. physicskit/classical/systems/lagrangian.py +308 -0
  81. physicskit/classical/systems/newtonian.py +536 -0
  82. physicskit/classical/systems/rotations.py +614 -0
  83. physicskit/classical/tests/__init__.py +0 -0
  84. physicskit/classical/tests/conftest.py +7 -0
  85. physicskit/classical/tests/test_base_system.py +113 -0
  86. physicskit/classical/tests/test_chains.py +84 -0
  87. physicskit/classical/tests/test_conservation.py +244 -0
  88. physicskit/classical/tests/test_conservation_utils.py +47 -0
  89. physicskit/classical/tests/test_hamiltonian.py +44 -0
  90. physicskit/classical/tests/test_integrators.py +123 -0
  91. physicskit/classical/tests/test_lagrangian.py +69 -0
  92. physicskit/classical/tests/test_newtonian.py +45 -0
  93. physicskit/classical/tests/test_rotations.py +160 -0
  94. physicskit/classical/tests/test_stepsize.py +149 -0
  95. physicskit/classical/tests/test_symbolic.py +125 -0
  96. physicskit/classical/tests/test_visualizers.py +172 -0
  97. physicskit/classical/utils/__init__.py +20 -0
  98. physicskit/classical/utils/conservation.py +126 -0
  99. physicskit/classical/utils/stepsize.py +187 -0
  100. physicskit/classical/utils/symbolic.py +235 -0
  101. physicskit/classical/visualizers/__init__.py +25 -0
  102. physicskit/classical/visualizers/animations.py +508 -0
  103. physicskit/classical/visualizers/interactive.py +113 -0
  104. physicskit/classical/visualizers/modal_analysis.py +97 -0
  105. physicskit/classical/visualizers/phase_space.py +214 -0
  106. physicskit/condensed/__init__.py +122 -0
  107. physicskit/condensed/anderson_localization.py +155 -0
  108. physicskit/condensed/correlated.py +284 -0
  109. physicskit/condensed/ginzburg_landau.py +220 -0
  110. physicskit/condensed/landau_levels.py +246 -0
  111. physicskit/condensed/laughlin.py +229 -0
  112. physicskit/condensed/models.py +627 -0
  113. physicskit/condensed/tests/__init__.py +0 -0
  114. physicskit/condensed/tests/test_condensed.py +518 -0
  115. physicskit/condensed/tests/test_visualizers.py +87 -0
  116. physicskit/condensed/tight_binding.py +445 -0
  117. physicskit/condensed/topological_insulator_3d.py +178 -0
  118. physicskit/condensed/topology.py +217 -0
  119. physicskit/condensed/visualizers.py +296 -0
  120. physicskit/condensed/weyl.py +180 -0
  121. physicskit/constants.py +184 -0
  122. physicskit/fields/__init__.py +111 -0
  123. physicskit/fields/electrodynamics.py +671 -0
  124. physicskit/fields/quantum_fields.py +510 -0
  125. physicskit/fields/solitons.py +527 -0
  126. physicskit/fields/tests/__init__.py +0 -0
  127. physicskit/fields/tests/conftest.py +7 -0
  128. physicskit/fields/tests/test_field_animations.py +154 -0
  129. physicskit/fields/tests/test_fields.py +439 -0
  130. physicskit/fields/tests/test_visualizers.py +101 -0
  131. physicskit/fields/visualizers.py +503 -0
  132. physicskit/fluids/__init__.py +134 -0
  133. physicskit/fluids/core/__init__.py +23 -0
  134. physicskit/fluids/core/grid.py +165 -0
  135. physicskit/fluids/core/timestepping.py +231 -0
  136. physicskit/fluids/exceptions.py +26 -0
  137. physicskit/fluids/systems/__init__.py +70 -0
  138. physicskit/fluids/systems/compressible_flow.py +281 -0
  139. physicskit/fluids/systems/instabilities.py +343 -0
  140. physicskit/fluids/systems/navier_stokes.py +221 -0
  141. physicskit/fluids/systems/potential_flow.py +440 -0
  142. physicskit/fluids/systems/viscous_flow.py +425 -0
  143. physicskit/fluids/systems/vortex_dynamics.py +304 -0
  144. physicskit/fluids/tests/__init__.py +0 -0
  145. physicskit/fluids/tests/conftest.py +7 -0
  146. physicskit/fluids/tests/test_compressible_flow.py +66 -0
  147. physicskit/fluids/tests/test_instabilities.py +115 -0
  148. physicskit/fluids/tests/test_navier_stokes.py +67 -0
  149. physicskit/fluids/tests/test_potential_flow.py +92 -0
  150. physicskit/fluids/tests/test_static_visualizers.py +91 -0
  151. physicskit/fluids/tests/test_utils.py +100 -0
  152. physicskit/fluids/tests/test_viscous_flow.py +110 -0
  153. physicskit/fluids/tests/test_visualizers.py +34 -0
  154. physicskit/fluids/tests/test_vortex_dynamics.py +88 -0
  155. physicskit/fluids/utils/__init__.py +18 -0
  156. physicskit/fluids/utils/dimensionless.py +239 -0
  157. physicskit/fluids/utils/spectral_analysis.py +142 -0
  158. physicskit/fluids/visualizers/__init__.py +14 -0
  159. physicskit/fluids/visualizers/compressible.py +39 -0
  160. physicskit/fluids/visualizers/flow_fields.py +252 -0
  161. physicskit/fluids/visualizers/potential_flow.py +51 -0
  162. physicskit/fluids/visualizers/spectra.py +62 -0
  163. physicskit/fluids/visualizers/theme.py +38 -0
  164. physicskit/integrators/__init__.py +52 -0
  165. physicskit/integrators/adaptive.py +199 -0
  166. physicskit/integrators/fixed_step.py +360 -0
  167. physicskit/integrators/tests/__init__.py +0 -0
  168. physicskit/integrators/tests/test_integrators.py +101 -0
  169. physicskit/optics/__init__.py +124 -0
  170. physicskit/optics/_compat.py +5 -0
  171. physicskit/optics/gaussian.py +382 -0
  172. physicskit/optics/quantum_optics.py +428 -0
  173. physicskit/optics/ray.py +469 -0
  174. physicskit/optics/tests/__init__.py +0 -0
  175. physicskit/optics/tests/test_diffraction_animation.py +38 -0
  176. physicskit/optics/tests/test_gaussian.py +170 -0
  177. physicskit/optics/tests/test_quantum_optics.py +147 -0
  178. physicskit/optics/tests/test_ray.py +177 -0
  179. physicskit/optics/tests/test_visualizers.py +58 -0
  180. physicskit/optics/tests/test_wave.py +146 -0
  181. physicskit/optics/visualizers.py +264 -0
  182. physicskit/optics/wave.py +365 -0
  183. physicskit/particle/__init__.py +152 -0
  184. physicskit/particle/collider.py +382 -0
  185. physicskit/particle/confinement.py +125 -0
  186. physicskit/particle/decays.py +357 -0
  187. physicskit/particle/electroweak.py +300 -0
  188. physicskit/particle/kinematics.py +271 -0
  189. physicskit/particle/neutrinos.py +84 -0
  190. physicskit/particle/nuclear.py +100 -0
  191. physicskit/particle/scattering.py +159 -0
  192. physicskit/particle/tests/__init__.py +0 -0
  193. physicskit/particle/tests/conftest.py +7 -0
  194. physicskit/particle/tests/test_collider.py +119 -0
  195. physicskit/particle/tests/test_confinement.py +42 -0
  196. physicskit/particle/tests/test_decays.py +112 -0
  197. physicskit/particle/tests/test_electroweak.py +97 -0
  198. physicskit/particle/tests/test_kinematics.py +138 -0
  199. physicskit/particle/tests/test_neutrinos.py +36 -0
  200. physicskit/particle/tests/test_nuclear.py +45 -0
  201. physicskit/particle/tests/test_scattering.py +64 -0
  202. physicskit/particle/tests/test_visualizers.py +214 -0
  203. physicskit/particle/visualizers/__init__.py +38 -0
  204. physicskit/particle/visualizers/animations.py +705 -0
  205. physicskit/particle/visualizers/static.py +101 -0
  206. physicskit/plasma/__init__.py +176 -0
  207. physicskit/plasma/acceleration.py +152 -0
  208. physicskit/plasma/instabilities.py +417 -0
  209. physicskit/plasma/kinetic.py +588 -0
  210. physicskit/plasma/mhd.py +463 -0
  211. physicskit/plasma/single_particle.py +562 -0
  212. physicskit/plasma/tests/__init__.py +0 -0
  213. physicskit/plasma/tests/conftest.py +7 -0
  214. physicskit/plasma/tests/test_new_time_domain_features.py +222 -0
  215. physicskit/plasma/tests/test_plasma.py +232 -0
  216. physicskit/plasma/tests/test_visualizers.py +79 -0
  217. physicskit/plasma/turbulence.py +219 -0
  218. physicskit/plasma/visualizers.py +868 -0
  219. physicskit/plasma/waves.py +549 -0
  220. physicskit/quantum/__init__.py +180 -0
  221. physicskit/quantum/_compat.py +20 -0
  222. physicskit/quantum/chapters/__init__.py +0 -0
  223. physicskit/quantum/chapters/entanglement.py +489 -0
  224. physicskit/quantum/chapters/harmonic_spin.py +517 -0
  225. physicskit/quantum/chapters/hydrogen_am.py +292 -0
  226. physicskit/quantum/chapters/perturbation.py +350 -0
  227. physicskit/quantum/chapters/potentials.py +997 -0
  228. physicskit/quantum/chapters/spin.py +306 -0
  229. physicskit/quantum/chapters/wave_packets.py +541 -0
  230. physicskit/quantum/core/__init__.py +0 -0
  231. physicskit/quantum/core/eigensolvers.py +311 -0
  232. physicskit/quantum/core/operators.py +296 -0
  233. physicskit/quantum/core/solvers.py +271 -0
  234. physicskit/quantum/tests/__init__.py +0 -0
  235. physicskit/quantum/tests/conftest.py +7 -0
  236. physicskit/quantum/tests/test_animations.py +225 -0
  237. physicskit/quantum/tests/test_compat.py +43 -0
  238. physicskit/quantum/tests/test_eigensolvers.py +24 -0
  239. physicskit/quantum/tests/test_entanglement.py +127 -0
  240. physicskit/quantum/tests/test_harmonic_spin.py +140 -0
  241. physicskit/quantum/tests/test_hydrogen_am.py +48 -0
  242. physicskit/quantum/tests/test_measure.py +110 -0
  243. physicskit/quantum/tests/test_operators.py +105 -0
  244. physicskit/quantum/tests/test_perturbation.py +112 -0
  245. physicskit/quantum/tests/test_physics_checks.py +131 -0
  246. physicskit/quantum/tests/test_potentials.py +191 -0
  247. physicskit/quantum/tests/test_solvers.py +70 -0
  248. physicskit/quantum/tests/test_spin.py +22 -0
  249. physicskit/quantum/tests/test_unitarity.py +84 -0
  250. physicskit/quantum/tests/test_wave_packets.py +53 -0
  251. physicskit/quantum/utils/__init__.py +0 -0
  252. physicskit/quantum/utils/measure.py +253 -0
  253. physicskit/quantum/visualizers/__init__.py +0 -0
  254. physicskit/quantum/visualizers/bloch_sphere.py +205 -0
  255. physicskit/quantum/visualizers/entanglement.py +58 -0
  256. physicskit/quantum/visualizers/orbitals.py +187 -0
  257. physicskit/quantum/visualizers/phase_space.py +179 -0
  258. physicskit/quantum/visualizers/wavefunctions.py +230 -0
  259. physicskit/relativity/__init__.py +61 -0
  260. physicskit/relativity/chapters/__init__.py +33 -0
  261. physicskit/relativity/chapters/cosmology.py +190 -0
  262. physicskit/relativity/chapters/gw_merger.py +399 -0
  263. physicskit/relativity/chapters/kerr.py +371 -0
  264. physicskit/relativity/chapters/lensing.py +196 -0
  265. physicskit/relativity/chapters/neutron_star.py +149 -0
  266. physicskit/relativity/chapters/schwarzschild.py +412 -0
  267. physicskit/relativity/chapters/timekeeping.py +133 -0
  268. physicskit/relativity/core/__init__.py +12 -0
  269. physicskit/relativity/core/geodesics.py +216 -0
  270. physicskit/relativity/core/kerr_raytracer.py +259 -0
  271. physicskit/relativity/core/raytracer.py +241 -0
  272. physicskit/relativity/core/tensors.py +468 -0
  273. physicskit/relativity/tests/__init__.py +0 -0
  274. physicskit/relativity/tests/test_conservations.py +54 -0
  275. physicskit/relativity/tests/test_constants.py +34 -0
  276. physicskit/relativity/tests/test_cosmology.py +61 -0
  277. physicskit/relativity/tests/test_gw_merger.py +117 -0
  278. physicskit/relativity/tests/test_kerr.py +116 -0
  279. physicskit/relativity/tests/test_kerr_raytracer.py +103 -0
  280. physicskit/relativity/tests/test_lensing.py +75 -0
  281. physicskit/relativity/tests/test_neutron_star.py +48 -0
  282. physicskit/relativity/tests/test_raytracer.py +67 -0
  283. physicskit/relativity/tests/test_schwarzschild.py +126 -0
  284. physicskit/relativity/tests/test_shadow_render.py +81 -0
  285. physicskit/relativity/tests/test_tensors.py +86 -0
  286. physicskit/relativity/tests/test_timekeeping.py +51 -0
  287. physicskit/relativity/tests/test_visualizers_misc.py +115 -0
  288. physicskit/relativity/utils/__init__.py +1 -0
  289. physicskit/relativity/utils/constants.py +232 -0
  290. physicskit/relativity/visualizers/__init__.py +46 -0
  291. physicskit/relativity/visualizers/interactive.py +127 -0
  292. physicskit/relativity/visualizers/shadow_render.py +367 -0
  293. physicskit/relativity/visualizers/spacetime_3d.py +92 -0
  294. physicskit/relativity/visualizers/spacetime_diagrams.py +198 -0
  295. physicskit/relativity/visualizers/wave_plots.py +185 -0
  296. physicskit/rmt/__init__.py +8 -0
  297. physicskit/rmt/ensembles/__init__.py +108 -0
  298. physicskit/rmt/ensembles/banded.py +114 -0
  299. physicskit/rmt/ensembles/base.py +135 -0
  300. physicskit/rmt/ensembles/bdg.py +218 -0
  301. physicskit/rmt/ensembles/chiral.py +308 -0
  302. physicskit/rmt/ensembles/circular.py +104 -0
  303. physicskit/rmt/ensembles/crossover.py +82 -0
  304. physicskit/rmt/ensembles/density_matrix.py +148 -0
  305. physicskit/rmt/ensembles/effective_hamiltonian.py +230 -0
  306. physicskit/rmt/ensembles/embedded.py +207 -0
  307. physicskit/rmt/ensembles/gaussian.py +153 -0
  308. physicskit/rmt/ensembles/ginibre.py +110 -0
  309. physicskit/rmt/ensembles/girko.py +190 -0
  310. physicskit/rmt/ensembles/graph_laplacian.py +92 -0
  311. physicskit/rmt/ensembles/haar.py +57 -0
  312. physicskit/rmt/ensembles/jacobi.py +182 -0
  313. physicskit/rmt/ensembles/poisson.py +77 -0
  314. physicskit/rmt/ensembles/polynomial.py +139 -0
  315. physicskit/rmt/ensembles/pt_symmetric.py +120 -0
  316. physicskit/rmt/ensembles/single_ring.py +170 -0
  317. physicskit/rmt/ensembles/sparse.py +95 -0
  318. physicskit/rmt/ensembles/syk.py +170 -0
  319. physicskit/rmt/ensembles/truncated_unitary.py +80 -0
  320. physicskit/rmt/ensembles/universality.py +92 -0
  321. physicskit/rmt/ensembles/wishart.py +88 -0
  322. physicskit/rmt/py.typed +0 -0
  323. physicskit/rmt/spectrum.py +82 -0
  324. physicskit/rmt/stats/__init__.py +160 -0
  325. physicskit/rmt/stats/characteristic_polynomial.py +77 -0
  326. physicskit/rmt/stats/circular_law.py +54 -0
  327. physicskit/rmt/stats/correlations.py +162 -0
  328. physicskit/rmt/stats/density.py +70 -0
  329. physicskit/rmt/stats/entanglement.py +36 -0
  330. physicskit/rmt/stats/free_probability.py +202 -0
  331. physicskit/rmt/stats/localization.py +338 -0
  332. physicskit/rmt/stats/marchenko_pastur.py +55 -0
  333. physicskit/rmt/stats/pt_symmetric.py +265 -0
  334. physicskit/rmt/stats/ratios.py +110 -0
  335. physicskit/rmt/stats/real_ginibre.py +134 -0
  336. physicskit/rmt/stats/rigidity.py +272 -0
  337. physicskit/rmt/stats/single_ring.py +95 -0
  338. physicskit/rmt/stats/spacing.py +150 -0
  339. physicskit/rmt/stats/tracy_widom.py +175 -0
  340. physicskit/rmt/stats/truncated_unitary.py +40 -0
  341. physicskit/rmt/stats/unfolding.py +37 -0
  342. physicskit/rmt/stats/wachter.py +67 -0
  343. physicskit/rmt/tests/__init__.py +0 -0
  344. physicskit/rmt/tests/cache_utils.py +107 -0
  345. physicskit/rmt/tests/conftest.py +8 -0
  346. physicskit/rmt/tests/test_banded.py +86 -0
  347. physicskit/rmt/tests/test_base.py +29 -0
  348. physicskit/rmt/tests/test_bdg.py +133 -0
  349. physicskit/rmt/tests/test_characteristic_polynomial.py +50 -0
  350. physicskit/rmt/tests/test_chiral.py +273 -0
  351. physicskit/rmt/tests/test_circular.py +225 -0
  352. physicskit/rmt/tests/test_crossover.py +72 -0
  353. physicskit/rmt/tests/test_density.py +23 -0
  354. physicskit/rmt/tests/test_density_matrix.py +181 -0
  355. physicskit/rmt/tests/test_effective_hamiltonian.py +140 -0
  356. physicskit/rmt/tests/test_embedded.py +169 -0
  357. physicskit/rmt/tests/test_free_probability.py +81 -0
  358. physicskit/rmt/tests/test_gaussian_ensembles.py +22 -0
  359. physicskit/rmt/tests/test_gaussian_semicircle.py +127 -0
  360. physicskit/rmt/tests/test_ginibre_circular_law.py +131 -0
  361. physicskit/rmt/tests/test_girko.py +178 -0
  362. physicskit/rmt/tests/test_graph_laplacian.py +102 -0
  363. physicskit/rmt/tests/test_haar.py +118 -0
  364. physicskit/rmt/tests/test_jacobi_wachter.py +136 -0
  365. physicskit/rmt/tests/test_localization.py +240 -0
  366. physicskit/rmt/tests/test_poisson.py +149 -0
  367. physicskit/rmt/tests/test_polynomial.py +107 -0
  368. physicskit/rmt/tests/test_pt_symmetric.py +178 -0
  369. physicskit/rmt/tests/test_random_state.py +19 -0
  370. physicskit/rmt/tests/test_real_ginibre.py +112 -0
  371. physicskit/rmt/tests/test_rigidity_universality.py +167 -0
  372. physicskit/rmt/tests/test_single_ring.py +132 -0
  373. physicskit/rmt/tests/test_spacing_and_ratios.py +122 -0
  374. physicskit/rmt/tests/test_sparse.py +71 -0
  375. physicskit/rmt/tests/test_spectrum.py +27 -0
  376. physicskit/rmt/tests/test_syk.py +136 -0
  377. physicskit/rmt/tests/test_tracy_widom.py +113 -0
  378. physicskit/rmt/tests/test_tridiagonal.py +41 -0
  379. physicskit/rmt/tests/test_truncated_unitary.py +80 -0
  380. physicskit/rmt/tests/test_validation_base.py +25 -0
  381. physicskit/rmt/tests/test_validation_misc.py +67 -0
  382. physicskit/rmt/tests/test_wishart_marchenko_pastur.py +120 -0
  383. physicskit/rmt/utils/__init__.py +0 -0
  384. physicskit/rmt/utils/haar.py +128 -0
  385. physicskit/rmt/utils/random_state.py +24 -0
  386. physicskit/rmt/utils/tridiagonal.py +167 -0
  387. physicskit/rmt/validation/__init__.py +36 -0
  388. physicskit/rmt/validation/base.py +126 -0
  389. physicskit/rmt/validation/circular_law.py +80 -0
  390. physicskit/rmt/validation/marchenko_pastur.py +53 -0
  391. physicskit/rmt/validation/ratio_distribution.py +53 -0
  392. physicskit/rmt/validation/real_ginibre.py +60 -0
  393. physicskit/rmt/validation/sine_kernel.py +57 -0
  394. physicskit/rmt/validation/single_ring.py +68 -0
  395. physicskit/rmt/validation/tracy_widom.py +89 -0
  396. physicskit/rmt/validation/universality.py +100 -0
  397. physicskit/rmt/validation/wachter.py +52 -0
  398. physicskit/rmt/validation/wigner_semicircle.py +40 -0
  399. physicskit/rmt/validation/wigner_surmise.py +58 -0
  400. physicskit/semiclassical/__init__.py +94 -0
  401. physicskit/semiclassical/_compat.py +5 -0
  402. physicskit/semiclassical/core/__init__.py +41 -0
  403. physicskit/semiclassical/core/gutzwiller.py +215 -0
  404. physicskit/semiclassical/core/path_integral.py +594 -0
  405. physicskit/semiclassical/core/propagators.py +579 -0
  406. physicskit/semiclassical/core/wkb.py +308 -0
  407. physicskit/semiclassical/systems/__init__.py +13 -0
  408. physicskit/semiclassical/systems/scarring.py +278 -0
  409. physicskit/semiclassical/tests/__init__.py +0 -0
  410. physicskit/semiclassical/tests/test_gutzwiller.py +35 -0
  411. physicskit/semiclassical/tests/test_path_integral.py +194 -0
  412. physicskit/semiclassical/tests/test_propagators.py +82 -0
  413. physicskit/semiclassical/tests/test_scarring.py +43 -0
  414. physicskit/semiclassical/tests/test_visualizers.py +89 -0
  415. physicskit/semiclassical/tests/test_wkb.py +30 -0
  416. physicskit/semiclassical/visualizers/__init__.py +17 -0
  417. physicskit/semiclassical/visualizers/gutzwiller.py +55 -0
  418. physicskit/semiclassical/visualizers/path_integral.py +195 -0
  419. physicskit/semiclassical/visualizers/propagators.py +89 -0
  420. physicskit/semiclassical/visualizers/scarring.py +146 -0
  421. physicskit/semiclassical/visualizers/wkb.py +55 -0
  422. physicskit/statphys/__init__.py +80 -0
  423. physicskit/statphys/chapters/__init__.py +43 -0
  424. physicskit/statphys/chapters/ehrenfest_urn.py +112 -0
  425. physicskit/statphys/chapters/ising_lattice.py +431 -0
  426. physicskit/statphys/chapters/kpz_growth.py +129 -0
  427. physicskit/statphys/chapters/molecular_dynamics.py +258 -0
  428. physicskit/statphys/chapters/nonequilibrium_work.py +146 -0
  429. physicskit/statphys/chapters/percolation.py +305 -0
  430. physicskit/statphys/chapters/random_walk.py +142 -0
  431. physicskit/statphys/chapters/renormalization.py +148 -0
  432. physicskit/statphys/chapters/sandpile.py +109 -0
  433. physicskit/statphys/chapters/spin_glass.py +275 -0
  434. physicskit/statphys/core/__init__.py +10 -0
  435. physicskit/statphys/core/kpz_engine.py +53 -0
  436. physicskit/statphys/core/md_engine.py +157 -0
  437. physicskit/statphys/core/monte_carlo.py +462 -0
  438. physicskit/statphys/core/random_walk_engine.py +68 -0
  439. physicskit/statphys/core/sandpile_engine.py +52 -0
  440. physicskit/statphys/tests/__init__.py +0 -0
  441. physicskit/statphys/tests/test_dynamics.py +66 -0
  442. physicskit/statphys/tests/test_ehrenfest_urn.py +44 -0
  443. physicskit/statphys/tests/test_finite_size_scaling.py +62 -0
  444. physicskit/statphys/tests/test_interactive.py +33 -0
  445. physicskit/statphys/tests/test_ising.py +68 -0
  446. physicskit/statphys/tests/test_kpz.py +55 -0
  447. physicskit/statphys/tests/test_landau_theory.py +49 -0
  448. physicskit/statphys/tests/test_md_engine.py +31 -0
  449. physicskit/statphys/tests/test_molecular_dynamics.py +72 -0
  450. physicskit/statphys/tests/test_nonequilibrium_work.py +33 -0
  451. physicskit/statphys/tests/test_partition_function.py +26 -0
  452. physicskit/statphys/tests/test_percolation.py +116 -0
  453. physicskit/statphys/tests/test_potts_model.py +46 -0
  454. physicskit/statphys/tests/test_random_walk.py +50 -0
  455. physicskit/statphys/tests/test_renormalization.py +44 -0
  456. physicskit/statphys/tests/test_sandpile.py +44 -0
  457. physicskit/statphys/tests/test_spin_glass.py +73 -0
  458. physicskit/statphys/tests/test_thermodynamics.py +121 -0
  459. physicskit/statphys/tests/test_xy_model.py +45 -0
  460. physicskit/statphys/utils/__init__.py +1 -0
  461. physicskit/statphys/utils/dynamics.py +100 -0
  462. physicskit/statphys/utils/finite_size_scaling.py +171 -0
  463. physicskit/statphys/utils/landau_theory.py +151 -0
  464. physicskit/statphys/utils/partition_function.py +109 -0
  465. physicskit/statphys/utils/thermodynamics.py +356 -0
  466. physicskit/statphys/visualizers/__init__.py +87 -0
  467. physicskit/statphys/visualizers/interactive.py +161 -0
  468. physicskit/statphys/visualizers/jarzynski_render.py +44 -0
  469. physicskit/statphys/visualizers/kpz_render.py +65 -0
  470. physicskit/statphys/visualizers/lattice_render.py +212 -0
  471. physicskit/statphys/visualizers/particle_render.py +121 -0
  472. physicskit/statphys/visualizers/random_walk_render.py +106 -0
  473. physicskit/statphys/visualizers/rg_render.py +45 -0
  474. physicskit/statphys/visualizers/sandpile_render.py +65 -0
  475. physicskit/statphys/visualizers/spin_glass_render.py +40 -0
  476. physicskit/statphys/visualizers/urn_render.py +38 -0
  477. physicskit/statphys/visualizers/vortex_render.py +84 -0
  478. physicskit/tests/__init__.py +0 -0
  479. physicskit/tests/test_constants.py +38 -0
  480. physicskit-0.1.0.dist-info/METADATA +196 -0
  481. physicskit-0.1.0.dist-info/RECORD +484 -0
  482. physicskit-0.1.0.dist-info/WHEEL +5 -0
  483. physicskit-0.1.0.dist-info/licenses/LICENSE +21 -0
  484. physicskit-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,562 @@
1
+ """Single-particle motion in electromagnetic fields: the Boris pusher and guiding-center drifts.
2
+
3
+ Two complementary pictures of charged-particle motion in a magnetized
4
+ plasma live here. The *full-orbit* picture integrates the exact Lorentz
5
+ force :math:`m\\dot{\\mathbf{v}} = q(\\mathbf{E} + \\mathbf{v}\\times\\mathbf{B})`
6
+ with :func:`boris_push` / :func:`boris_integrate`, Jay Boris's 1970
7
+ energy-conserving leapfrog scheme -- still the workhorse integrator of
8
+ every particle-in-cell (PIC) plasma code. The *guiding-center* picture
9
+ instead averages over the fast gyration and tracks only the slow drift of
10
+ the orbit's center: :func:`exb_drift`, :func:`grad_b_drift`, and
11
+ :func:`curvature_drift` give that drift velocity directly, while
12
+ :func:`magnetic_moment` and :func:`mirror_force` capture the adiabatic
13
+ invariant :math:`\\mu = m v_\\perp^2 / 2B` responsible for magnetic-mirror
14
+ confinement.
15
+
16
+ The Boris kernels are compiled with Numba for particle-in-cell-scale
17
+ performance; every other function here is a closed-form NumPy expression.
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ import numpy as np
23
+ from numba import njit
24
+
25
+ from physicskit import constants as _const
26
+
27
+ __all__ = [
28
+ "QE",
29
+ "ME",
30
+ "MP",
31
+ "cyclotron_frequency",
32
+ "larmor_radius",
33
+ "magnetic_moment",
34
+ "boris_push",
35
+ "boris_integrate",
36
+ "exb_drift",
37
+ "grad_b_drift",
38
+ "curvature_drift",
39
+ "mirror_force",
40
+ "magnetic_mirror_bounce",
41
+ ]
42
+
43
+ QE = _const.ELEMENTARY_CHARGE
44
+ """Elementary charge, in Coulombs."""
45
+
46
+ ME = _const.ELECTRON_MASS
47
+ """Electron mass, in kilograms."""
48
+
49
+ MP = _const.PROTON_MASS
50
+ """Proton mass, in kilograms."""
51
+
52
+
53
+ def cyclotron_frequency(q: float, m: float, B: float) -> float:
54
+ """Angular cyclotron (gyration) frequency :math:`\\omega_c = qB/m`.
55
+
56
+ Parameters
57
+ ----------
58
+ q : float
59
+ Particle charge in Coulombs (signed).
60
+ m : float
61
+ Particle mass in kilograms.
62
+ B : float
63
+ Magnetic field magnitude in Tesla.
64
+
65
+ Returns
66
+ -------
67
+ float
68
+ Signed angular gyrofrequency in rad/s (negative for negatively
69
+ charged particles, reflecting the opposite sense of rotation).
70
+
71
+ See Also
72
+ --------
73
+ larmor_radius : The orbit radius set by this frequency.
74
+
75
+ Examples
76
+ --------
77
+ A proton in a 1 T field gyrates about 15.3 MHz:
78
+
79
+ >>> round(cyclotron_frequency(QE, MP, 1.0) / (2 * 3.141592653589793) / 1e6, 2)
80
+ 15.25
81
+ """
82
+ return q * B / m
83
+
84
+
85
+ def larmor_radius(v_perp: float, q: float, m: float, B: float) -> float:
86
+ """Larmor (gyro) radius :math:`r_L = m v_\\perp / (|q| B)`.
87
+
88
+ Parameters
89
+ ----------
90
+ v_perp : float
91
+ Speed perpendicular to the magnetic field, in m/s.
92
+ q : float
93
+ Particle charge in Coulombs (sign is ignored).
94
+ m : float
95
+ Particle mass in kilograms.
96
+ B : float
97
+ Magnetic field magnitude in Tesla.
98
+
99
+ Returns
100
+ -------
101
+ float
102
+ Gyroradius in meters.
103
+
104
+ Examples
105
+ --------
106
+ >>> round(larmor_radius(1e5, QE, MP, 1.0), 6)
107
+ 0.001044
108
+ """
109
+ return m * v_perp / (abs(q) * B)
110
+
111
+
112
+ def magnetic_moment(v_perp: float, m: float, B: float) -> float:
113
+ """The first adiabatic invariant :math:`\\mu = m v_\\perp^2 / (2B)`.
114
+
115
+ Conserved for a charged particle whose gyration is fast compared to
116
+ any change in the field it sees, :math:`\\mu` acts as a magnetic
117
+ "potential energy per unit field": as the particle drifts into
118
+ stronger :math:`B`, :math:`v_\\perp` must grow to keep :math:`\\mu`
119
+ fixed, converting parallel kinetic energy into perpendicular kinetic
120
+ energy. That conversion is the mechanism behind :func:`mirror_force`.
121
+
122
+ Parameters
123
+ ----------
124
+ v_perp : float
125
+ Speed perpendicular to the magnetic field, in m/s.
126
+ m : float
127
+ Particle mass in kilograms.
128
+ B : float
129
+ Magnetic field magnitude in Tesla.
130
+
131
+ Returns
132
+ -------
133
+ float
134
+ Magnetic moment in Joules/Tesla.
135
+
136
+ See Also
137
+ --------
138
+ mirror_force : The parallel force derived from this invariant.
139
+
140
+ Examples
141
+ --------
142
+ >>> round(magnetic_moment(1e5, MP, 1.0) * 1e18, 4)
143
+ 8.3631
144
+ """
145
+ return 0.5 * m * v_perp**2 / B
146
+
147
+
148
+ @njit(cache=True)
149
+ def _boris_kernel(x, y, z, vx, vy, vz, q, m, Ex, Ey, Ez, Bx, By, Bz, dt):
150
+ qmdt2 = (q / m) * (dt / 2.0)
151
+ vmx = vx + qmdt2 * Ex
152
+ vmy = vy + qmdt2 * Ey
153
+ vmz = vz + qmdt2 * Ez
154
+ tx, ty, tz = qmdt2 * Bx, qmdt2 * By, qmdt2 * Bz
155
+ t2 = tx * tx + ty * ty + tz * tz
156
+ denom = 1.0 + t2
157
+ sx, sy, sz = 2.0 * tx / denom, 2.0 * ty / denom, 2.0 * tz / denom
158
+ vpx = vmx + (vmy * tz - vmz * ty)
159
+ vpy = vmy + (vmz * tx - vmx * tz)
160
+ vpz = vmz + (vmx * ty - vmy * tx)
161
+ vplusx = vmx + (vpy * sz - vpz * sy)
162
+ vplusy = vmy + (vpz * sx - vpx * sz)
163
+ vplusz = vmz + (vpx * sy - vpy * sx)
164
+ vnx = vplusx + qmdt2 * Ex
165
+ vny = vplusy + qmdt2 * Ey
166
+ vnz = vplusz + qmdt2 * Ez
167
+ xn = x + vnx * dt
168
+ yn = y + vny * dt
169
+ zn = z + vnz * dt
170
+ return xn, yn, zn, vnx, vny, vnz
171
+
172
+
173
+ @njit(cache=True)
174
+ def _boris_integrate_kernel(x, y, z, vx, vy, vz, q, m, Ex, Ey, Ez, Bx, By, Bz, dt, steps):
175
+ xs = np.empty(steps + 1)
176
+ ys = np.empty(steps + 1)
177
+ zs = np.empty(steps + 1)
178
+ vxs = np.empty(steps + 1)
179
+ vys = np.empty(steps + 1)
180
+ vzs = np.empty(steps + 1)
181
+ xs[0], ys[0], zs[0] = x, y, z
182
+ vxs[0], vys[0], vzs[0] = vx, vy, vz
183
+ for i in range(steps):
184
+ x, y, z, vx, vy, vz = _boris_kernel(x, y, z, vx, vy, vz, q, m, Ex, Ey, Ez, Bx, By, Bz, dt)
185
+ xs[i + 1], ys[i + 1], zs[i + 1] = x, y, z
186
+ vxs[i + 1], vys[i + 1], vzs[i + 1] = vx, vy, vz
187
+ return xs, ys, zs, vxs, vys, vzs
188
+
189
+
190
+ def boris_push(pos: np.ndarray, vel: np.ndarray, q: float, m: float, E: np.ndarray, B: np.ndarray, dt: float) -> tuple:
191
+ """Advance a charged particle one leapfrog step with the Boris integrator.
192
+
193
+ The Boris (1970) scheme splits each step into an electric
194
+ half-acceleration, an exact rotation about :math:`\\mathbf{B}` (via the
195
+ Boris "E-cross-B" trick, avoiding any explicit trigonometry), and a
196
+ second electric half-acceleration. The rotation step preserves speed
197
+ exactly in a pure magnetic field, which is why the scheme conserves
198
+ kinetic energy over arbitrarily many gyro-orbits where a naive
199
+ forward-Euler or even RK4 update would spiral outward.
200
+
201
+ Parameters
202
+ ----------
203
+ pos : ndarray, shape (3,)
204
+ Particle position :math:`[x, y, z]` in meters.
205
+ vel : ndarray, shape (3,)
206
+ Particle velocity :math:`[v_x, v_y, v_z]` in m/s.
207
+ q : float
208
+ Particle charge in Coulombs.
209
+ m : float
210
+ Particle mass in kilograms.
211
+ E : ndarray, shape (3,)
212
+ Electric field :math:`[E_x, E_y, E_z]` in V/m.
213
+ B : ndarray, shape (3,)
214
+ Magnetic field :math:`[B_x, B_y, B_z]` in Tesla.
215
+ dt : float
216
+ Time step in seconds.
217
+
218
+ Returns
219
+ -------
220
+ pos_new, vel_new : ndarray, shape (3,)
221
+ Position and velocity after one step of size ``dt``.
222
+
223
+ See Also
224
+ --------
225
+ boris_integrate : Repeated application of this step over many time steps.
226
+
227
+ Examples
228
+ --------
229
+ A proton launched perpendicular to a uniform field gyrates without
230
+ gaining or losing speed:
231
+
232
+ >>> import numpy as np
233
+ >>> r0 = np.array([0.0, 0.0, 0.0])
234
+ >>> v0 = np.array([1e5, 0.0, 0.0])
235
+ >>> E = np.zeros(3)
236
+ >>> B = np.array([0.0, 0.0, 1.0])
237
+ >>> r1, v1 = boris_push(r0, v0, q=QE, m=MP, E=E, B=B, dt=1e-9)
238
+ >>> round(float(np.linalg.norm(v1)), 6)
239
+ 100000.0
240
+ """
241
+ xn, yn, zn, vnx, vny, vnz = _boris_kernel(
242
+ float(pos[0]),
243
+ float(pos[1]),
244
+ float(pos[2]),
245
+ float(vel[0]),
246
+ float(vel[1]),
247
+ float(vel[2]),
248
+ float(q),
249
+ float(m),
250
+ float(E[0]),
251
+ float(E[1]),
252
+ float(E[2]),
253
+ float(B[0]),
254
+ float(B[1]),
255
+ float(B[2]),
256
+ float(dt),
257
+ )
258
+ return np.array([xn, yn, zn]), np.array([vnx, vny, vnz])
259
+
260
+
261
+ def boris_integrate(pos0: np.ndarray, vel0: np.ndarray, q: float, m: float, E: np.ndarray, B: np.ndarray, dt: float, steps: int) -> tuple:
262
+ """Integrate a charged particle's trajectory in *uniform* E and B fields with the Boris pusher.
263
+
264
+ Repeatedly applies :func:`boris_push` inside a single Numba-compiled
265
+ loop, avoiding Python-level overhead per step -- the throughput this
266
+ buys is what makes the Boris scheme practical for particle-in-cell
267
+ codes tracking millions of particles.
268
+
269
+ Parameters
270
+ ----------
271
+ pos0 : ndarray, shape (3,)
272
+ Initial position in meters.
273
+ vel0 : ndarray, shape (3,)
274
+ Initial velocity in m/s.
275
+ q : float
276
+ Particle charge in Coulombs.
277
+ m : float
278
+ Particle mass in kilograms.
279
+ E : ndarray, shape (3,)
280
+ Uniform electric field in V/m.
281
+ B : ndarray, shape (3,)
282
+ Uniform magnetic field in Tesla.
283
+ dt : float
284
+ Time step in seconds.
285
+ steps : int
286
+ Number of steps to advance.
287
+
288
+ Returns
289
+ -------
290
+ pos_hist : ndarray, shape (steps + 1, 3)
291
+ Position at every step, including the initial condition.
292
+ vel_hist : ndarray, shape (steps + 1, 3)
293
+ Velocity at every step, including the initial condition.
294
+
295
+ See Also
296
+ --------
297
+ boris_push : The single-step update repeated here.
298
+ exb_drift : The closed-form drift velocity this trajectory averages to
299
+ when a non-zero E is present.
300
+
301
+ Examples
302
+ --------
303
+ Kinetic energy is conserved to machine precision over many gyrations
304
+ in a pure magnetic field:
305
+
306
+ >>> import numpy as np
307
+ >>> B = np.array([0.0, 0.0, 1.0])
308
+ >>> E = np.zeros(3)
309
+ >>> omega_c = cyclotron_frequency(QE, MP, 1.0)
310
+ >>> dt = (2 * np.pi / omega_c) / 200
311
+ >>> pos_hist, vel_hist = boris_integrate(
312
+ ... np.zeros(3), np.array([1e5, 0.0, 0.0]), QE, MP, E, B, dt, steps=2000
313
+ ... )
314
+ >>> speeds = np.linalg.norm(vel_hist, axis=1)
315
+ >>> bool(np.max(np.abs(speeds - 1e5)) < 1e-6)
316
+ True
317
+ """
318
+ xs, ys, zs, vxs, vys, vzs = _boris_integrate_kernel(
319
+ float(pos0[0]),
320
+ float(pos0[1]),
321
+ float(pos0[2]),
322
+ float(vel0[0]),
323
+ float(vel0[1]),
324
+ float(vel0[2]),
325
+ float(q),
326
+ float(m),
327
+ float(E[0]),
328
+ float(E[1]),
329
+ float(E[2]),
330
+ float(B[0]),
331
+ float(B[1]),
332
+ float(B[2]),
333
+ float(dt),
334
+ int(steps),
335
+ )
336
+ pos_hist = np.stack([xs, ys, zs], axis=1)
337
+ vel_hist = np.stack([vxs, vys, vzs], axis=1)
338
+ return pos_hist, vel_hist
339
+
340
+
341
+ def exb_drift(E: np.ndarray, B: np.ndarray) -> np.ndarray:
342
+ """The :math:`\\mathbf{E}\\times\\mathbf{B}` drift velocity :math:`\\mathbf{v}_E = (\\mathbf{E}\\times\\mathbf{B})/B^2`.
343
+
344
+ Unlike every other guiding-center drift, this one is independent of
345
+ charge, mass, and energy: electrons and ions drift together at the
346
+ same velocity, so :math:`\\mathbf{E}\\times\\mathbf{B}` drift carries no
347
+ net current and instead advects the whole plasma as a fluid --
348
+ the drift underlying tokamak radial-electric-field rotation and
349
+ magnetospheric convection alike.
350
+
351
+ Parameters
352
+ ----------
353
+ E : ndarray, shape (3,)
354
+ Electric field in V/m.
355
+ B : ndarray, shape (3,)
356
+ Magnetic field in Tesla.
357
+
358
+ Returns
359
+ -------
360
+ ndarray, shape (3,)
361
+ Drift velocity in m/s.
362
+
363
+ Examples
364
+ --------
365
+ >>> import numpy as np
366
+ >>> E = np.array([0.0, 1e3, 0.0])
367
+ >>> B = np.array([0.0, 0.0, 1.0])
368
+ >>> exb_drift(E, B)
369
+ array([1000., 0., 0.])
370
+ """
371
+ return np.cross(E, B) / np.dot(B, B)
372
+
373
+
374
+ def grad_b_drift(v_perp: float, q: float, m: float, B: np.ndarray, grad_B: np.ndarray) -> np.ndarray:
375
+ """The grad-B drift :math:`\\mathbf{v}_{\\nabla B} = \\dfrac{m v_\\perp^2}{2qB^3}(\\mathbf{B}\\times\\nabla B)`.
376
+
377
+ A particle gyrating in a field whose magnitude varies across the
378
+ orbit sees a tighter turn (smaller Larmor radius) on the strong-field
379
+ side than the weak-field side, so the orbit fails to close and the
380
+ guiding center creeps sideways. Because the drift is inversely
381
+ proportional to charge, electrons and ions drift in *opposite*
382
+ directions, producing a net current -- the origin of the ring current
383
+ in planetary magnetospheres.
384
+
385
+ Parameters
386
+ ----------
387
+ v_perp : float
388
+ Speed perpendicular to the field, in m/s.
389
+ q : float
390
+ Particle charge in Coulombs (signed).
391
+ m : float
392
+ Particle mass in kilograms.
393
+ B : ndarray, shape (3,)
394
+ Local magnetic field vector in Tesla.
395
+ grad_B : ndarray, shape (3,)
396
+ Gradient of the field magnitude, :math:`\\nabla |\\mathbf{B}|`, in Tesla/meter.
397
+
398
+ Returns
399
+ -------
400
+ ndarray, shape (3,)
401
+ Drift velocity in m/s.
402
+
403
+ See Also
404
+ --------
405
+ curvature_drift : The companion drift from field-line curvature,
406
+ which combines with this one in any real toroidal field.
407
+
408
+ Examples
409
+ --------
410
+ >>> import numpy as np
411
+ >>> B = np.array([0.0, 0.0, 1.0])
412
+ >>> grad_B = np.array([0.1, 0.0, 0.0])
413
+ >>> drift = grad_b_drift(1e5, QE, MP, B, grad_B)
414
+ >>> round(float(drift[1]), 4)
415
+ 5.2198
416
+ """
417
+ Bmag = np.linalg.norm(B)
418
+ return (m * v_perp**2) / (2.0 * q * Bmag**3) * np.cross(B, grad_B)
419
+
420
+
421
+ def curvature_drift(v_par: float, q: float, m: float, B: np.ndarray, R_c: np.ndarray) -> np.ndarray:
422
+ """The curvature drift :math:`\\mathbf{v}_R = \\dfrac{m v_\\parallel^2}{q}\\dfrac{\\mathbf{R}_c\\times\\mathbf{B}}{R_c^2 B^2}`.
423
+
424
+ A particle streaming along a curved field line feels a centrifugal
425
+ force in the guiding-center frame, which -- crossed with
426
+ :math:`\\mathbf{B}` -- produces a drift perpendicular to the plane of
427
+ curvature. In a low-beta toroidal equilibrium this combines with
428
+ :func:`grad_b_drift` (curvature and field-strength gradients point the
429
+ same way when :math:`\\nabla\\times\\mathbf{B}=0` in vacuum) into the
430
+ single :math:`\\nabla B` + curvature drift responsible for charge
431
+ separation and the resulting E-cross-B rotation in a tokamak.
432
+
433
+ Parameters
434
+ ----------
435
+ v_par : float
436
+ Speed parallel to the field, in m/s.
437
+ q : float
438
+ Particle charge in Coulombs (signed).
439
+ m : float
440
+ Particle mass in kilograms.
441
+ B : ndarray, shape (3,)
442
+ Local magnetic field vector in Tesla.
443
+ R_c : ndarray, shape (3,)
444
+ Radius-of-curvature vector, pointing from the field line's local
445
+ center of curvature to the particle, magnitude :math:`R_c` in meters.
446
+
447
+ Returns
448
+ -------
449
+ ndarray, shape (3,)
450
+ Drift velocity in m/s.
451
+
452
+ Examples
453
+ --------
454
+ >>> import numpy as np
455
+ >>> B = np.array([0.0, 0.0, 1.0])
456
+ >>> R_c = np.array([1.0, 0.0, 0.0])
457
+ >>> drift = curvature_drift(1e5, QE, MP, B, R_c)
458
+ >>> round(float(drift[1]), 2)
459
+ -104.4
460
+ """
461
+ Bmag = np.linalg.norm(B)
462
+ Rc2 = np.dot(R_c, R_c)
463
+ return (m * v_par**2 / q) * np.cross(R_c, B) / (Rc2 * Bmag**2)
464
+
465
+
466
+ def mirror_force(mu: float, grad_B_parallel: float) -> float:
467
+ """The mirror force :math:`F_\\parallel = -\\mu\\, \\partial B/\\partial \\ell` along a field line.
468
+
469
+ As a particle's guiding center moves into a region of stronger field
470
+ (larger :math:`\\partial B/\\partial\\ell`), the conservation of
471
+ :func:`magnetic_moment` forces :math:`v_\\perp` to grow at the expense
472
+ of :math:`v_\\parallel`; this is the reaction force decelerating the
473
+ parallel motion. If the field is strong enough, :math:`v_\\parallel`
474
+ reaches zero before the particle passes the throat and it reflects --
475
+ magnetic mirror confinement, simulated end to end in
476
+ :func:`magnetic_mirror_bounce`.
477
+
478
+ Parameters
479
+ ----------
480
+ mu : float
481
+ Magnetic moment in J/T, from :func:`magnetic_moment`.
482
+ grad_B_parallel : float
483
+ Gradient of the field magnitude along the field line, in Tesla/meter.
484
+
485
+ Returns
486
+ -------
487
+ float
488
+ Force in Newtons, directed to push the particle toward weaker field.
489
+
490
+ Examples
491
+ --------
492
+ >>> mirror_force(mu=1e-17, grad_B_parallel=2.0)
493
+ -2e-17
494
+ """
495
+ return -mu * grad_B_parallel
496
+
497
+
498
+ def magnetic_mirror_bounce(z0: float, v_par0: float, v_perp0: float, m: float, B_func, dz: float = 1e-6, dt: float = 1e-10, steps: int = 20000) -> tuple:
499
+ """Simulate 1D guiding-center bounce motion between the throats of a magnetic mirror.
500
+
501
+ Integrates :math:`m\\dot{v}_\\parallel = -\\mu\\, dB/dz` with :math:`\\mu`
502
+ fixed at its initial value (the adiabatic invariant), using a
503
+ symmetric leapfrog step. A particle with too little pitch angle to
504
+ reflect before reaching the mirror throat's peak field instead falls
505
+ into the *loss cone* and would be lost from confinement in a real
506
+ device; :func:`mirror_force` supplies the underlying force law.
507
+
508
+ Parameters
509
+ ----------
510
+ z0 : float
511
+ Initial position along the field line, in meters.
512
+ v_par0 : float
513
+ Initial parallel velocity, in m/s.
514
+ v_perp0 : float
515
+ Initial perpendicular velocity, in m/s (sets :math:`\\mu` via :func:`magnetic_moment`).
516
+ m : float
517
+ Particle mass in kilograms.
518
+ B_func : callable
519
+ Field-strength profile ``B_func(z) -> float`` along the field line, in Tesla.
520
+ dz : float, default=1e-6
521
+ Finite-difference step used to evaluate :math:`dB/dz`, in meters.
522
+ dt : float, default=1e-10
523
+ Time step in seconds.
524
+ steps : int, default=20000
525
+ Number of leapfrog steps to advance.
526
+
527
+ Returns
528
+ -------
529
+ z_hist : ndarray, shape (steps + 1,)
530
+ Position along the field line at every step.
531
+ v_par_hist : ndarray, shape (steps + 1,)
532
+ Parallel velocity at every step.
533
+
534
+ Examples
535
+ --------
536
+ A particle launched from the mirror midplane with enough perpendicular
537
+ energy reflects before reaching the throat, reversing the sign of its
538
+ parallel velocity:
539
+
540
+ >>> import numpy as np
541
+ >>> B_func = lambda z: 1.0 + 4.0 * (z / 0.05) ** 2
542
+ >>> z_hist, v_par_hist = magnetic_mirror_bounce(
543
+ ... z0=0.0, v_par0=2e4, v_perp0=8e4, m=MP, B_func=B_func, steps=6000
544
+ ... )
545
+ >>> bool(v_par_hist[0] > 0 and v_par_hist[-1] < 0)
546
+ True
547
+ """
548
+ z = z0
549
+ v_par = v_par0
550
+ B0 = B_func(z0)
551
+ mu = magnetic_moment(v_perp0, m, B0)
552
+ z_hist = np.empty(steps + 1)
553
+ v_par_hist = np.empty(steps + 1)
554
+ z_hist[0], v_par_hist[0] = z, v_par
555
+ for i in range(steps):
556
+ grad_B = (B_func(z + dz) - B_func(z - dz)) / (2.0 * dz)
557
+ a = mirror_force(mu, grad_B) / m
558
+ v_par = v_par + a * dt
559
+ z = z + v_par * dt
560
+ z_hist[i + 1] = z
561
+ v_par_hist[i + 1] = v_par
562
+ return z_hist, v_par_hist
File without changes
@@ -0,0 +1,7 @@
1
+ """Test-session setup: force Matplotlib's non-interactive Agg backend
2
+ before anything imports pyplot, so the suite (and CI, which has no
3
+ display) can exercise the plotting/animation code headlessly."""
4
+
5
+ import matplotlib
6
+
7
+ matplotlib.use("Agg")