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,222 @@
1
+ """Physics-correctness and animation-smoke tests for the newly added time-domain
2
+ plasma simulations: magnetic reconnection and Weibel filamentation
3
+ (:mod:`physicskit.plasma.instabilities`), Langmuir waves
4
+ (:mod:`physicskit.plasma.kinetic`), ion-acoustic solitons and Alfven waves
5
+ (:mod:`physicskit.plasma.waves`), drift-wave turbulence
6
+ (:mod:`physicskit.plasma.turbulence`), wakefield particle acceleration
7
+ (:mod:`physicskit.plasma.acceleration`), and the two-stream phase-space
8
+ animation (:mod:`physicskit.plasma.visualizers`).
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import numpy as np
14
+ import pytest
15
+ from matplotlib.animation import PillowWriter
16
+ from scipy.signal import find_peaks
17
+
18
+ from physicskit.plasma.acceleration import simulate_wakefield_acceleration
19
+ from physicskit.plasma.instabilities import (
20
+ reconnection_field_from_flux,
21
+ reconnection_harris_ic,
22
+ simulate_reconnection,
23
+ simulate_weibel_filamentation,
24
+ weibel_fastest_growing_mode,
25
+ weibel_growth_rate,
26
+ )
27
+ from physicskit.plasma.kinetic import langmuir_wave_ic, pic_simulate, two_stream_ic
28
+ from physicskit.plasma.turbulence import drift_wave_noise_ic, simulate_hasegawa_mima
29
+ from physicskit.plasma.visualizers import (
30
+ animate_alfven_wave,
31
+ animate_drift_wave_turbulence,
32
+ animate_ion_acoustic_soliton,
33
+ animate_langmuir_wave,
34
+ animate_reconnection,
35
+ animate_two_stream_phase_space,
36
+ animate_wakefield_acceleration,
37
+ animate_weibel_filamentation,
38
+ )
39
+ from physicskit.plasma.waves import alfven_wave_pulse_ic, ion_acoustic_soliton_profile, simulate_alfven_wave
40
+
41
+
42
+ class TestReconnection:
43
+ def test_resistivity_dissipates_magnetic_energy_faster_at_higher_eta(self):
44
+ nx, ny, Lx, Ly = 48, 48, 20.0, 20.0
45
+ dx, dy = Lx / nx, Ly / ny
46
+ psi0 = reconnection_harris_ic(nx, ny, Lx, Ly, sheet_width=1.0, perturbation_amplitude=0.2)
47
+
48
+ def magnetic_energy(psi):
49
+ Bx, By = reconnection_field_from_flux(psi, dx, dy)
50
+ return np.sum(Bx**2 + By**2) * dx * dy
51
+
52
+ e0 = magnetic_energy(psi0)
53
+ r_low = simulate_reconnection(psi0, eta=0.01, v0=0.05, dt=0.01, steps=200, Lx=Lx, Ly=Ly)
54
+ r_high = simulate_reconnection(psi0, eta=0.05, v0=0.05, dt=0.01, steps=200, Lx=Lx, Ly=Ly)
55
+ e_low = magnetic_energy(r_low["psi"])
56
+ e_high = magnetic_energy(r_high["psi"])
57
+ assert e_high < e_low < e0
58
+
59
+ @pytest.mark.slow
60
+ def test_animation_saves_to_gif(self, tmp_path):
61
+ psi0 = reconnection_harris_ic(32, 32, Lx=20.0, Ly=20.0, sheet_width=1.0, perturbation_amplitude=0.2)
62
+ anim = animate_reconnection(psi0, eta=0.02, v0=0.05, dt=0.02, steps_per_frame=5, n_frames=5, Lx=20.0, Ly=20.0)
63
+ out = tmp_path / "reconnection.gif"
64
+ anim.save(out, writer=PillowWriter(fps=10))
65
+ assert out.exists() and out.stat().st_size > 0
66
+
67
+
68
+ class TestWeibelFilamentation:
69
+ def test_growth_rate_matches_zero_k_limit(self):
70
+ wpe, aniso = 1.0, 4.0
71
+ gamma0 = weibel_growth_rate(0.0, wpe, aniso)
72
+ assert gamma0 == pytest.approx(wpe * np.sqrt(aniso - 1.0))
73
+
74
+ def test_growth_rate_vanishes_above_cutoff(self):
75
+ wpe, aniso, c = 1.0, 4.0, 3e8
76
+ k_max = (wpe / c) * np.sqrt(aniso - 1.0)
77
+ assert weibel_growth_rate(2.0 * k_max, wpe, aniso, c=c) == 0.0
78
+
79
+ def test_fastest_growing_mode_matches_zero_k_formula(self):
80
+ k0, gamma_max = weibel_fastest_growing_mode(wpe=1.0, temperature_anisotropy=4.0)
81
+ assert k0 == 0.0
82
+ assert gamma_max == pytest.approx(np.sqrt(3.0))
83
+
84
+ def test_fastest_growing_mode_is_zero_for_isotropic_temperature(self):
85
+ assert weibel_fastest_growing_mode(wpe=1.0, temperature_anisotropy=1.0) == (0.0, 0.0)
86
+
87
+ def test_current_filaments_grow_in_time(self):
88
+ x = np.linspace(0, 20.0, 256, endpoint=False)
89
+ t = np.array([0.0, 5.0])
90
+ J = simulate_weibel_filamentation(x, t, wpe=1.0, temperature_anisotropy=4.0, n_modes=8, seed=0)
91
+ assert np.std(J[1]) > 100 * np.std(J[0])
92
+
93
+ @pytest.mark.slow
94
+ def test_animation_saves_to_gif(self, tmp_path):
95
+ x = np.linspace(0, 20.0, 128, endpoint=False)
96
+ t = np.linspace(0, 5.0, 6)
97
+ anim = animate_weibel_filamentation(x, t, wpe=1.0, temperature_anisotropy=4.0, n_modes=6)
98
+ out = tmp_path / "weibel.gif"
99
+ anim.save(out, writer=PillowWriter(fps=10))
100
+ assert out.exists() and out.stat().st_size > 0
101
+
102
+
103
+ class TestLangmuirWave:
104
+ def test_field_energy_oscillates_without_substantial_decay(self):
105
+ k, L = 2 * np.pi / 4.0, 4.0
106
+ x0, v0 = langmuir_wave_ic(20000, L=L, k_mode=k, alpha=0.05, v_th=0.05, seed=1)
107
+ result = pic_simulate(x0, v0, L=L, ng=64, dt=0.05, steps=400)
108
+ field_energy = result["field_energy"]
109
+ peaks, _ = find_peaks(field_energy)
110
+ assert len(peaks) >= 4
111
+ peak_heights = field_energy[peaks]
112
+ assert peak_heights[-1] > 0.5 * peak_heights[0]
113
+
114
+ @pytest.mark.slow
115
+ def test_animation_saves_to_gif(self, tmp_path):
116
+ k, L = 2 * np.pi / 4.0, 4.0
117
+ x0, v0 = langmuir_wave_ic(4000, L=L, k_mode=k, alpha=0.05, v_th=0.05, seed=0)
118
+ anim = animate_langmuir_wave(x0, v0, L=L, ng=32, dt=0.05, steps_per_frame=4, n_frames=5)
119
+ out = tmp_path / "langmuir.gif"
120
+ anim.save(out, writer=PillowWriter(fps=10))
121
+ assert out.exists() and out.stat().st_size > 0
122
+
123
+
124
+ class TestTwoStreamAnimation:
125
+ @pytest.mark.slow
126
+ def test_animation_saves_to_gif(self, tmp_path):
127
+ x0, v0 = two_stream_ic(2000, L=10.0, v_drift=3.0, v_th=0.5, seed=0)
128
+ anim = animate_two_stream_phase_space(x0, v0, L=10.0, ng=32, dt=0.05, steps_per_frame=5, n_frames=5)
129
+ out = tmp_path / "two_stream.gif"
130
+ anim.save(out, writer=PillowWriter(fps=10))
131
+ assert out.exists() and out.stat().st_size > 0
132
+
133
+
134
+ class TestIonAcousticSoliton:
135
+ def test_soliton_propagates_at_its_speed_and_keeps_amplitude(self):
136
+ N, L = 512, 60.0
137
+ x = np.linspace(-L / 2, L / 2, N, endpoint=False)
138
+ u0 = ion_acoustic_soliton_profile(x, speed=4.0, x0=-15.0)
139
+ from physicskit.plasma.waves import ion_acoustic_soliton_evolve
140
+
141
+ dt, steps = 0.0005, 4000
142
+ u = ion_acoustic_soliton_evolve(u0, x, dt, steps)
143
+ shift = x[np.argmax(u)] - x[np.argmax(u0)]
144
+ assert shift == pytest.approx(4.0 * steps * dt, abs=0.5)
145
+ assert u.max() == pytest.approx(u0.max(), abs=0.05)
146
+
147
+ @pytest.mark.slow
148
+ def test_animation_saves_to_gif(self, tmp_path):
149
+ x = np.linspace(-30, 30, 256, endpoint=False)
150
+ u0 = ion_acoustic_soliton_profile(x, speed=4.0, x0=-15.0)
151
+ anim = animate_ion_acoustic_soliton(u0, x, dt=0.0005, steps_per_frame=200, n_frames=5)
152
+ out = tmp_path / "ion_acoustic.gif"
153
+ anim.save(out, writer=PillowWriter(fps=10))
154
+ assert out.exists() and out.stat().st_size > 0
155
+
156
+
157
+ class TestAlfvenWave:
158
+ def test_pulse_splits_and_propagates_at_alfven_speed(self):
159
+ x = np.linspace(-20, 20, 256, endpoint=False)
160
+ By0, vy0 = alfven_wave_pulse_ic(x, x0=0.0, width=1.0, amplitude=0.1)
161
+ B0, rho0, mu0 = 1.0, 1.0, 1.0
162
+ vA = B0 / np.sqrt(mu0 * rho0)
163
+ dt, steps = 0.002, 2000
164
+ result = simulate_alfven_wave(By0, vy0, x, dt, steps, B0, rho0, mu0=mu0)
165
+ By = result["By"]
166
+ right_half = By[x > 0]
167
+ left_half = By[x < 0]
168
+ peak_right = x[x > 0][np.argmax(right_half)]
169
+ peak_left = x[x < 0][np.argmax(left_half)]
170
+ expected_shift = vA * steps * dt
171
+ assert peak_right == pytest.approx(expected_shift, abs=1.0)
172
+ assert peak_left == pytest.approx(-expected_shift, abs=1.0)
173
+
174
+ @pytest.mark.slow
175
+ def test_animation_saves_to_gif(self, tmp_path):
176
+ x = np.linspace(-20, 20, 128, endpoint=False)
177
+ By0, vy0 = alfven_wave_pulse_ic(x, x0=0.0, width=1.0, amplitude=0.1)
178
+ anim = animate_alfven_wave(By0, vy0, x, dt=0.002, steps_per_frame=100, n_frames=5, B0=1.0, rho0=1.0)
179
+ out = tmp_path / "alfven.gif"
180
+ anim.save(out, writer=PillowWriter(fps=10))
181
+ assert out.exists() and out.stat().st_size > 0
182
+
183
+
184
+ class TestDriftWaveTurbulence:
185
+ def test_higher_dissipation_decays_faster(self):
186
+ n, length = 48, 2 * np.pi
187
+ phi0 = drift_wave_noise_ic(n, length, amplitude=0.05, seed=2)
188
+ dt, steps = 0.02, 300
189
+ r_low = simulate_hasegawa_mima(phi0, dt, steps, length, nu=0.02)
190
+ r_high = simulate_hasegawa_mima(phi0, dt, steps, length, nu=0.06)
191
+ rms_low = np.sqrt(np.mean(r_low["phi"] ** 2))
192
+ rms_high = np.sqrt(np.mean(r_high["phi"] ** 2))
193
+ assert np.isfinite(rms_low) and np.isfinite(rms_high)
194
+ assert rms_high < rms_low
195
+
196
+ @pytest.mark.slow
197
+ def test_animation_saves_to_gif(self, tmp_path):
198
+ phi0 = drift_wave_noise_ic(48, 2 * np.pi, amplitude=0.05, seed=0)
199
+ anim = animate_drift_wave_turbulence(phi0, dt=0.02, steps_per_frame=10, n_frames=5, length=2 * np.pi)
200
+ out = tmp_path / "drift_wave.gif"
201
+ anim.save(out, writer=PillowWriter(fps=10))
202
+ assert out.exists() and out.stat().st_size > 0
203
+
204
+
205
+ class TestWakefieldAcceleration:
206
+ def test_phase_locked_particle_gains_energy_matching_work_energy_theorem(self):
207
+ result = simulate_wakefield_acceleration(x0=0.0, v0=0.9, q=1.0, m=1.0, E0=0.05, k=1.0, v_phase=1.0, dt=0.001, steps=20000)
208
+ ke = result["kinetic_energy"]
209
+ assert ke[-1] > ke[0]
210
+ v, t, x = result["v"], result["t"], result["x"]
211
+ dt = t[1] - t[0]
212
+ from physicskit.plasma.acceleration import wakefield_e_field
213
+
214
+ work = np.sum(wakefield_e_field(x[:-1], t[:-1], 0.05, 1.0, 1.0) * v[:-1] * dt)
215
+ assert (ke[-1] - ke[0]) == pytest.approx(work, rel=0.1)
216
+
217
+ @pytest.mark.slow
218
+ def test_animation_saves_to_gif(self, tmp_path):
219
+ anim = animate_wakefield_acceleration(x0=0.0, v0=0.9, q=1.0, m=1.0, E0=0.05, k=1.0, v_phase=1.0, dt=0.01, steps=400, frame_stride=40)
220
+ out = tmp_path / "wakefield.gif"
221
+ anim.save(out, writer=PillowWriter(fps=10))
222
+ assert out.exists() and out.stat().st_size > 0
@@ -0,0 +1,232 @@
1
+ """Physics-correctness tests for physicskit.plasma."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import numpy as np
6
+ import pytest
7
+ from scipy.signal import find_peaks
8
+
9
+ from physicskit.plasma.kinetic import (
10
+ deposit_number_density,
11
+ landau_damping_ic,
12
+ landau_damping_rate,
13
+ pic_simulate,
14
+ solve_poisson_1d,
15
+ two_stream_ic,
16
+ )
17
+ from physicskit.plasma.mhd import (
18
+ alfven_speed,
19
+ grad_shafranov_rhs_solovev,
20
+ lundquist_number,
21
+ magnetosonic_speeds,
22
+ petschek_rate,
23
+ resistive_diffusion_time,
24
+ solovev_particular_solution,
25
+ solve_grad_shafranov,
26
+ sound_speed,
27
+ sweet_parker_layer_width,
28
+ sweet_parker_rate,
29
+ )
30
+ from physicskit.plasma.single_particle import (
31
+ ME,
32
+ MP,
33
+ QE,
34
+ boris_integrate,
35
+ curvature_drift,
36
+ cyclotron_frequency,
37
+ exb_drift,
38
+ grad_b_drift,
39
+ larmor_radius,
40
+ magnetic_mirror_bounce,
41
+ magnetic_moment,
42
+ )
43
+ from physicskit.plasma.waves import EPS0, cold_plasma_dispersion, plasma_frequency, rl_parameters, stix_parameters, whistler_dispersion
44
+
45
+
46
+ class TestBorisPusher:
47
+ def test_conserves_kinetic_energy_in_pure_b_field(self):
48
+ B = np.array([0.0, 0.0, 1.0])
49
+ E = np.zeros(3)
50
+ omega_c = cyclotron_frequency(QE, MP, 1.0)
51
+ dt = (2 * np.pi / omega_c) / 500
52
+ pos_hist, vel_hist = boris_integrate(np.zeros(3), np.array([1e5, 0.0, 0.0]), QE, MP, E, B, dt, steps=5000)
53
+ speeds = np.linalg.norm(vel_hist, axis=1)
54
+ assert speeds == pytest.approx(1e5, rel=1e-9)
55
+
56
+ def test_gyroradius_matches_larmor_formula(self):
57
+ B = np.array([0.0, 0.0, 1.0])
58
+ E = np.zeros(3)
59
+ v0 = 1e5
60
+ omega_c = cyclotron_frequency(QE, MP, 1.0)
61
+ dt = (2 * np.pi / omega_c) / 2000
62
+ pos_hist, _ = boris_integrate(np.zeros(3), np.array([v0, 0.0, 0.0]), QE, MP, E, B, dt, steps=2000)
63
+ # the particle starts on its own gyro-circle, so its farthest excursion
64
+ # from the starting point is the orbit *diameter*, 2 * r_L
65
+ diameter_from_orbit = np.max(np.linalg.norm(pos_hist, axis=1))
66
+ assert diameter_from_orbit == pytest.approx(2 * larmor_radius(v0, QE, MP, 1.0), rel=1e-3)
67
+
68
+ def test_exb_drift_matches_analytic_average_velocity(self):
69
+ E = np.array([0.0, 1e3, 0.0])
70
+ B = np.array([0.0, 0.0, 1.0])
71
+ omega_c = cyclotron_frequency(QE, MP, 1.0)
72
+ dt = (2 * np.pi / omega_c) / 1000
73
+ pos_hist, vel_hist = boris_integrate(np.zeros(3), np.zeros(3), QE, MP, E, B, dt, steps=10000)
74
+ drift = exb_drift(E, B)
75
+ measured = np.mean(vel_hist[-1000:], axis=0)
76
+ assert measured == pytest.approx(drift, abs=5.0)
77
+
78
+ def test_magnetic_mirror_reflects_confined_particle(self):
79
+ B_func = lambda z: 1.0 + 4.0 * (z / 0.05) ** 2
80
+ z_hist, v_par_hist = magnetic_mirror_bounce(z0=0.0, v_par0=2e4, v_perp0=8e4, m=MP, B_func=B_func, steps=6000)
81
+ assert v_par_hist[0] > 0
82
+ assert v_par_hist[-1] < 0
83
+
84
+
85
+ class TestGuidingCenterDrifts:
86
+ def test_grad_b_and_curvature_drift_are_perpendicular_to_b(self):
87
+ B = np.array([0.0, 0.0, 1.0])
88
+ grad_B = np.array([0.1, 0.0, 0.0])
89
+ drift = grad_b_drift(1e5, QE, MP, B, grad_B)
90
+ assert drift @ B == pytest.approx(0.0, abs=1e-12)
91
+
92
+ R_c = np.array([1.0, 0.0, 0.0])
93
+ drift_c = curvature_drift(1e5, QE, MP, B, R_c)
94
+ assert drift_c @ B == pytest.approx(0.0, abs=1e-12)
95
+
96
+ def test_electron_and_ion_grad_b_drifts_are_antiparallel(self):
97
+ B = np.array([0.0, 0.0, 1.0])
98
+ grad_B = np.array([0.1, 0.0, 0.0])
99
+ drift_ion = grad_b_drift(1e5, QE, MP, B, grad_B)
100
+ drift_electron = grad_b_drift(1e5, -QE, ME, B, grad_B)
101
+ assert drift_ion @ drift_electron < 0
102
+
103
+ def test_magnetic_moment_matches_definition(self):
104
+ assert magnetic_moment(v_perp=2e5, m=MP, B=2.0) == pytest.approx(0.5 * MP * 2e5**2 / 2.0)
105
+
106
+
107
+ class TestMHDWaveSpeeds:
108
+ def test_alfven_speed_matches_formula(self):
109
+ B, rho = 1.5, 2e-6
110
+ mu0 = 4 * np.pi * 1e-7
111
+ assert alfven_speed(B, rho) == pytest.approx(B / np.sqrt(mu0 * rho))
112
+
113
+ def test_magnetosonic_parallel_limits_reduce_to_max_and_min(self):
114
+ vA, cs = 8e5, 1e5
115
+ v_fast, v_slow = magnetosonic_speeds(vA, cs, theta=0.0)
116
+ assert v_fast == pytest.approx(max(vA, cs))
117
+ assert v_slow == pytest.approx(min(vA, cs))
118
+
119
+ def test_magnetosonic_perpendicular_limit(self):
120
+ vA, cs = 8e5, 1e5
121
+ v_fast, v_slow = magnetosonic_speeds(vA, cs, theta=np.pi / 2)
122
+ assert v_fast == pytest.approx(np.sqrt(vA**2 + cs**2))
123
+ assert v_slow == pytest.approx(0.0, abs=1e-6)
124
+
125
+
126
+ class TestGradShafranov:
127
+ def test_sor_solution_matches_solovev_analytic(self):
128
+ R = np.linspace(0.5, 1.5, 61)
129
+ Z = np.linspace(-0.5, 0.5, 61)
130
+ c1, c2 = 1.0, -2.0
131
+ psi = solve_grad_shafranov(R, Z, c1, c2)
132
+ RR, ZZ = np.meshgrid(R, Z, indexing="ij")
133
+ psi_exact = solovev_particular_solution(RR, ZZ, c1, c2)
134
+ assert np.max(np.abs(psi - psi_exact)) < 1e-3
135
+
136
+
137
+ class TestReconnection:
138
+ def test_petschek_faster_than_sweet_parker_at_high_lundquist_number(self):
139
+ S = 1e10
140
+ assert petschek_rate(S) > sweet_parker_rate(S)
141
+
142
+
143
+ class TestColdPlasmaWaves:
144
+ def test_parallel_propagation_gives_r_and_l_waves(self):
145
+ electrons = (1e19, -QE, ME)
146
+ ions = (1e19, QE, MP)
147
+ S, D, P = stix_parameters(omega=2e9, B=1.0, species=[electrons, ions])
148
+ R, L = rl_parameters(S, D)
149
+ n2_plus, n2_minus = cold_plasma_dispersion(theta=0.0, S=S, D=D, P=P)
150
+ assert sorted([n2_plus, n2_minus]) == pytest.approx(sorted([R, L]))
151
+
152
+ def test_perpendicular_propagation_gives_o_and_x_modes(self):
153
+ electrons = (1e19, -QE, ME)
154
+ ions = (1e19, QE, MP)
155
+ S, D, P = stix_parameters(omega=2e9, B=1.0, species=[electrons, ions])
156
+ R, L = rl_parameters(S, D)
157
+ n2_plus, n2_minus = cold_plasma_dispersion(theta=np.pi / 2, S=S, D=D, P=P)
158
+ assert sorted([n2_plus, n2_minus]) == pytest.approx(sorted([P, R * L / S]))
159
+
160
+ def test_plasma_frequency_matches_formula(self):
161
+ n = 1e19
162
+ assert plasma_frequency(n, q=QE, m=ME) == pytest.approx(np.sqrt(n * QE**2 / (EPS0 * ME)))
163
+
164
+ def test_whistler_dispersion_matches_electron_only_r_wave_root(self):
165
+ """See the docstring's own worked example: for omega << wce, the
166
+ whistler approximation should closely match the exact electron-only
167
+ R-wave root of cold_plasma_dispersion."""
168
+ wpe, wce = 1.784e11, 1.759e11
169
+ omega = 0.001 * wce
170
+ S, D, P = stix_parameters(omega, B=1.0, species=[(wpe**2 * EPS0 * ME / QE**2, -QE, ME)])
171
+ R, _ = rl_parameters(S, D)
172
+ n2_approx = whistler_dispersion(omega, wpe, wce)
173
+ assert n2_approx == pytest.approx(R, rel=0.01)
174
+
175
+
176
+ class TestKineticPIC:
177
+ def test_cic_deposit_conserves_total_charge(self):
178
+ x = np.array([0.1, 2.4, 4.9, 7.7, 9.99])
179
+ rho = deposit_number_density(x, L=10.0, ng=20, n0=3.0)
180
+ assert np.sum(rho) * (10.0 / 20) == pytest.approx(3.0 * 10.0)
181
+
182
+ def test_poisson_solver_matches_analytic_sinusoid(self):
183
+ ng = 64
184
+ L = 2 * np.pi
185
+ x_grid = np.linspace(0, L, ng, endpoint=False)
186
+ rho = np.sin(x_grid)
187
+ E = solve_poisson_1d(rho, L)
188
+ assert E == pytest.approx(-np.cos(x_grid), abs=1e-10)
189
+
190
+ def test_two_stream_ic_shapes_and_symmetric_mean_velocity(self):
191
+ x, v = two_stream_ic(2000, L=10.0, v_drift=3.0, v_th=0.5, seed=0)
192
+ assert x.shape == (2000,)
193
+ assert v.shape == (2000,)
194
+ assert np.mean(v) == pytest.approx(0.0, abs=0.5)
195
+
196
+ def test_landau_damping_matches_analytic_decay_rate(self):
197
+ k, v_th = 0.5, 1.0
198
+ L = 2 * np.pi / k
199
+ x0, v0 = landau_damping_ic(100000, L=L, k_mode=k, alpha=0.05, v_th=v_th, seed=1)
200
+ result = pic_simulate(x0, v0, L=L, ng=64, dt=0.05, steps=250)
201
+ field_energy, t = result["field_energy"], result["t"]
202
+
203
+ peak_idx, _ = find_peaks(field_energy)
204
+ t_peaks, e_peaks = t[peak_idx], field_energy[peak_idx]
205
+ above_noise_floor = e_peaks > 1e-4
206
+ t_peaks, e_peaks = t_peaks[above_noise_floor][:6], e_peaks[above_noise_floor][:6]
207
+
208
+ gamma_fit = 0.5 * np.polyfit(t_peaks, np.log(e_peaks), 1)[0]
209
+ gamma_analytic = landau_damping_rate(k, v_th)
210
+ assert gamma_fit == pytest.approx(gamma_analytic, rel=0.5)
211
+
212
+
213
+ def test_sound_speed_matches_formula():
214
+ assert sound_speed(gamma=5 / 3, p=1.0, rho=1e-6) == pytest.approx(np.sqrt(5 / 3 * 1.0 / 1e-6))
215
+
216
+
217
+ def test_grad_shafranov_rhs_solovev_matches_formula():
218
+ R = np.array([1.0, 2.0])
219
+ np.testing.assert_allclose(grad_shafranov_rhs_solovev(R, c1=1.0, c2=-2.0), 1.0 * R**2 - 2.0)
220
+
221
+
222
+ def test_lundquist_number_matches_formula():
223
+ assert lundquist_number(L=1.0, vA=1e6, eta=1.0) == pytest.approx(1e6)
224
+
225
+
226
+ def test_resistive_diffusion_time_matches_formula():
227
+ assert resistive_diffusion_time(L=1.0, eta=1.0) == pytest.approx(1.0)
228
+ assert resistive_diffusion_time(L=2.0, eta=1.0) == pytest.approx(4.0)
229
+
230
+
231
+ def test_sweet_parker_layer_width_matches_formula():
232
+ assert sweet_parker_layer_width(L=1e7, S=1e6) == pytest.approx(1e7 / np.sqrt(1e6))
@@ -0,0 +1,79 @@
1
+ """Coverage for physicskit.plasma.visualizers's static plotting functions
2
+ (previously untested): the ax=None/ax=<given> branch of each. All pure
3
+ matplotlib, small inputs -- fast."""
4
+
5
+ from __future__ import annotations
6
+
7
+ import matplotlib.pyplot as plt
8
+ import numpy as np
9
+
10
+ from physicskit.plasma.kinetic import landau_damping_ic
11
+ from physicskit.plasma.mhd import safety_factor_large_aspect_ratio, solve_grad_shafranov
12
+ from physicskit.plasma.single_particle import MP, QE, boris_integrate
13
+ from physicskit.plasma.visualizers import (
14
+ plot_cma_diagram,
15
+ plot_drift_trajectory,
16
+ plot_field_energy_history,
17
+ plot_flux_surfaces,
18
+ plot_particle_orbit_3d,
19
+ plot_phase_space,
20
+ plot_q_profile,
21
+ )
22
+ from physicskit.plasma.waves import cma_coordinates
23
+
24
+
25
+ def test_plot_particle_orbit_3d_given_ax():
26
+ pos_hist, _ = boris_integrate(np.zeros(3), np.array([1e5, 0.0, 0.0]), QE, MP, np.zeros(3), np.array([0.0, 0.0, 1.0]), 1e-10, steps=20)
27
+ fig, ax = plt.subplots(subplot_kw={"projection": "3d"})
28
+ _, ax_given = plot_particle_orbit_3d(pos_hist, ax=ax)
29
+ assert ax_given is ax
30
+ assert fig is ax.figure
31
+
32
+
33
+ def test_plot_drift_trajectory_given_ax():
34
+ E = np.array([0.0, 1e3, 0.0])
35
+ B = np.array([0.0, 0.0, 1.0])
36
+ pos_hist, _ = boris_integrate(np.zeros(3), np.zeros(3), QE, MP, E, B, 1e-10, steps=20)
37
+ fig, ax = plt.subplots()
38
+ _, ax_given = plot_drift_trajectory(pos_hist, ax=ax)
39
+ assert ax_given is ax
40
+
41
+
42
+ def test_plot_flux_surfaces_given_ax():
43
+ R = np.linspace(0.5, 1.5, 11)
44
+ Z = np.linspace(-0.5, 0.5, 11)
45
+ psi = solve_grad_shafranov(R, Z, c1=1.0, c2=-2.0)
46
+ fig, ax = plt.subplots()
47
+ _, ax_given = plot_flux_surfaces(R, Z, psi, ax=ax)
48
+ assert ax_given is ax
49
+
50
+
51
+ def test_plot_q_profile_given_ax():
52
+ r = np.linspace(0.05, 0.5, 10)
53
+ q = np.array([safety_factor_large_aspect_ratio(ri, R0=1.0, Bt=2.0, Bp=0.2) for ri in r])
54
+ fig, ax = plt.subplots()
55
+ _, ax_given = plot_q_profile(r, q, ax=ax)
56
+ assert ax_given is ax
57
+
58
+
59
+ def test_plot_phase_space_given_ax():
60
+ x, v = landau_damping_ic(200, L=4 * np.pi, k_mode=0.5, alpha=0.1, v_th=1.0, seed=0)
61
+ fig, ax = plt.subplots()
62
+ _, ax_given = plot_phase_space(x, v, ax=ax)
63
+ assert ax_given is ax
64
+
65
+
66
+ def test_plot_field_energy_history_given_ax():
67
+ t = np.linspace(0, 10, 20)
68
+ field_energy = np.exp(-0.1 * t)
69
+ fig, ax = plt.subplots()
70
+ _, ax_given = plot_field_energy_history(t, field_energy, ax=ax)
71
+ assert ax_given is ax
72
+
73
+
74
+ def test_plot_cma_diagram_given_ax():
75
+ omega = np.linspace(0.1, 5.0, 10)
76
+ X, Y = cma_coordinates(omega, wpe=2.0, wce=3.0)
77
+ fig, ax = plt.subplots()
78
+ _, ax_given = plot_cma_diagram(X, Y, ax=ax)
79
+ assert ax_given is ax