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,868 @@
1
+ """Plotting helpers for particle orbits, MHD equilibria, wave maps, and kinetic phase space.
2
+
3
+ Every Matplotlib function returns its figure and axes rather than calling
4
+ ``show()``; :func:`plot_phase_space_interactive` returns a Plotly figure
5
+ for pan/zoom exploration of a PIC phase-space snapshot. The ``animate_*``
6
+ functions each return a :class:`matplotlib.animation.FuncAnimation` built
7
+ from a sequence of snapshots taken while repeatedly re-invoking the
8
+ corresponding time-domain simulation function -- save with, e.g.,
9
+ ``anim.save(path, writer=PillowWriter(fps=10))``.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import matplotlib.pyplot as plt
15
+ import numpy as np
16
+ import plotly.graph_objects as go
17
+ from matplotlib.animation import FuncAnimation
18
+
19
+ from physicskit.plasma.acceleration import simulate_wakefield_acceleration, wakefield_e_field
20
+ from physicskit.plasma.instabilities import simulate_reconnection, simulate_weibel_filamentation
21
+ from physicskit.plasma.kinetic import deposit_number_density, pic_simulate
22
+ from physicskit.plasma.turbulence import simulate_hasegawa_mima
23
+ from physicskit.plasma.waves import ion_acoustic_soliton_evolve, simulate_alfven_wave
24
+
25
+ __all__ = [
26
+ "plot_particle_orbit_3d",
27
+ "plot_drift_trajectory",
28
+ "plot_flux_surfaces",
29
+ "plot_q_profile",
30
+ "plot_phase_space",
31
+ "plot_field_energy_history",
32
+ "plot_cma_diagram",
33
+ "plot_phase_space_interactive",
34
+ "animate_reconnection",
35
+ "animate_weibel_filamentation",
36
+ "animate_two_stream_phase_space",
37
+ "animate_langmuir_wave",
38
+ "animate_ion_acoustic_soliton",
39
+ "animate_drift_wave_turbulence",
40
+ "animate_alfven_wave",
41
+ "animate_wakefield_acceleration",
42
+ ]
43
+
44
+
45
+ def plot_particle_orbit_3d(pos_hist: np.ndarray, ax=None):
46
+ """Plot a charged particle's 3D trajectory (e.g. Boris-pusher gyro-orbit).
47
+
48
+ Parameters
49
+ ----------
50
+ pos_hist : ndarray, shape (steps, 3)
51
+ Position history, as from :func:`physicskit.plasma.single_particle.boris_integrate`.
52
+ ax : matplotlib.axes.Axes3D, optional
53
+ 3D axes to draw into; a new figure is created if omitted.
54
+
55
+ Returns
56
+ -------
57
+ fig : matplotlib.figure.Figure
58
+ ax : matplotlib.axes.Axes3D
59
+
60
+ Examples
61
+ --------
62
+ >>> import numpy as np
63
+ >>> from physicskit.plasma.single_particle import boris_integrate, QE, MP
64
+ >>> pos_hist, vel_hist = boris_integrate(
65
+ ... 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=200
66
+ ... )
67
+ >>> fig, ax = plot_particle_orbit_3d(pos_hist)
68
+ >>> isinstance(fig, plt.Figure)
69
+ True
70
+ """
71
+ if ax is None:
72
+ fig = plt.figure()
73
+ ax = fig.add_subplot(projection="3d")
74
+ else:
75
+ fig = ax.figure
76
+ ax.plot(pos_hist[:, 0], pos_hist[:, 1], pos_hist[:, 2])
77
+ ax.set_xlabel("x (m)")
78
+ ax.set_ylabel("y (m)")
79
+ ax.set_zlabel("z (m)")
80
+ return fig, ax
81
+
82
+
83
+ def plot_drift_trajectory(pos_hist: np.ndarray, ax=None):
84
+ """Plot the guiding-center drift path as seen from above (the x-y plane).
85
+
86
+ Parameters
87
+ ----------
88
+ pos_hist : ndarray, shape (steps, 3) or (steps, 2)
89
+ Position history; only the first two components are used.
90
+ ax : matplotlib.axes.Axes, optional
91
+ Axes to draw into; a new figure is created if omitted.
92
+
93
+ Returns
94
+ -------
95
+ fig : matplotlib.figure.Figure
96
+ ax : matplotlib.axes.Axes
97
+
98
+ See Also
99
+ --------
100
+ plot_particle_orbit_3d : The full 3D gyro-orbit this drift path averages over.
101
+
102
+ Examples
103
+ --------
104
+ >>> import numpy as np
105
+ >>> from physicskit.plasma.single_particle import boris_integrate, QE, MP
106
+ >>> E = np.array([0.0, 1e3, 0.0])
107
+ >>> B = np.array([0.0, 0.0, 1.0])
108
+ >>> pos_hist, vel_hist = boris_integrate(np.zeros(3), np.zeros(3), QE, MP, E, B, 1e-10, steps=500)
109
+ >>> fig, ax = plot_drift_trajectory(pos_hist)
110
+ >>> isinstance(fig, plt.Figure)
111
+ True
112
+ """
113
+ if ax is None:
114
+ fig, ax = plt.subplots()
115
+ else:
116
+ fig = ax.figure
117
+ ax.plot(pos_hist[:, 0], pos_hist[:, 1])
118
+ ax.set_xlabel("x (m)")
119
+ ax.set_ylabel("y (m)")
120
+ ax.set_aspect("equal")
121
+ return fig, ax
122
+
123
+
124
+ def plot_flux_surfaces(R: np.ndarray, Z: np.ndarray, psi: np.ndarray, ax=None, levels: int = 20):
125
+ """Contour-plot poloidal flux surfaces :math:`\\psi(R, Z)` of a toroidal equilibrium.
126
+
127
+ Parameters
128
+ ----------
129
+ R, Z : ndarray, shape (nr,), (nz,)
130
+ Cylindrical coordinate grids.
131
+ psi : ndarray, shape (nr, nz)
132
+ Poloidal flux, as from :func:`physicskit.plasma.mhd.solve_grad_shafranov`.
133
+ ax : matplotlib.axes.Axes, optional
134
+ Axes to draw into; a new figure is created if omitted.
135
+ levels : int, default=20
136
+ Number of contour levels.
137
+
138
+ Returns
139
+ -------
140
+ fig : matplotlib.figure.Figure
141
+ ax : matplotlib.axes.Axes
142
+
143
+ Examples
144
+ --------
145
+ >>> import numpy as np
146
+ >>> from physicskit.plasma.mhd import solve_grad_shafranov
147
+ >>> R = np.linspace(0.5, 1.5, 41)
148
+ >>> Z = np.linspace(-0.5, 0.5, 41)
149
+ >>> psi = solve_grad_shafranov(R, Z, c1=1.0, c2=-2.0)
150
+ >>> fig, ax = plot_flux_surfaces(R, Z, psi)
151
+ >>> isinstance(fig, plt.Figure)
152
+ True
153
+ """
154
+ if ax is None:
155
+ fig, ax = plt.subplots()
156
+ else:
157
+ fig = ax.figure
158
+ RR, ZZ = np.meshgrid(R, Z, indexing="ij")
159
+ cs = ax.contour(RR, ZZ, psi, levels=levels)
160
+ fig.colorbar(cs, ax=ax, label=r"$\psi$")
161
+ ax.set_xlabel("R (m)")
162
+ ax.set_ylabel("Z (m)")
163
+ ax.set_aspect("equal")
164
+ return fig, ax
165
+
166
+
167
+ def plot_q_profile(r: np.ndarray, q: np.ndarray, ax=None):
168
+ """Plot the tokamak safety factor :math:`q(r)` against minor radius.
169
+
170
+ Parameters
171
+ ----------
172
+ r : ndarray
173
+ Minor-radius coordinate.
174
+ q : ndarray
175
+ Safety factor, e.g. from
176
+ :func:`physicskit.plasma.mhd.safety_factor_large_aspect_ratio`
177
+ evaluated at each ``r``.
178
+ ax : matplotlib.axes.Axes, optional
179
+ Axes to draw into; a new figure is created if omitted.
180
+
181
+ Returns
182
+ -------
183
+ fig : matplotlib.figure.Figure
184
+ ax : matplotlib.axes.Axes
185
+
186
+ Examples
187
+ --------
188
+ >>> import numpy as np
189
+ >>> from physicskit.plasma.mhd import safety_factor_large_aspect_ratio
190
+ >>> r = np.linspace(0.05, 0.5, 20)
191
+ >>> q = np.array([safety_factor_large_aspect_ratio(ri, R0=1.0, Bt=2.0, Bp=0.2) for ri in r])
192
+ >>> fig, ax = plot_q_profile(r, q)
193
+ >>> isinstance(fig, plt.Figure)
194
+ True
195
+ """
196
+ if ax is None:
197
+ fig, ax = plt.subplots()
198
+ else:
199
+ fig = ax.figure
200
+ ax.plot(r, q)
201
+ ax.axhline(1.0, color="gray", linestyle="--", linewidth=0.8, label="q=1 (sawtooth)")
202
+ ax.set_xlabel("minor radius r (m)")
203
+ ax.set_ylabel("safety factor q")
204
+ ax.legend()
205
+ return fig, ax
206
+
207
+
208
+ def plot_phase_space(x: np.ndarray, v: np.ndarray, ax=None, bins: int = 64):
209
+ """Heatmap the particle-in-cell phase-space density :math:`f(x, v)` from a particle snapshot.
210
+
211
+ Parameters
212
+ ----------
213
+ x, v : ndarray, shape (n_particles,)
214
+ Particle positions and velocities, e.g. from
215
+ :func:`physicskit.plasma.kinetic.pic_simulate`.
216
+ ax : matplotlib.axes.Axes, optional
217
+ Axes to draw into; a new figure is created if omitted.
218
+ bins : int, default=64
219
+ Number of histogram bins along each axis.
220
+
221
+ Returns
222
+ -------
223
+ fig : matplotlib.figure.Figure
224
+ ax : matplotlib.axes.Axes
225
+
226
+ See Also
227
+ --------
228
+ plot_phase_space_interactive : An interactive Plotly scatter of the same data.
229
+
230
+ Examples
231
+ --------
232
+ >>> import numpy as np
233
+ >>> from physicskit.plasma.kinetic import landau_damping_ic
234
+ >>> x, v = landau_damping_ic(2000, L=4 * np.pi, k_mode=0.5, alpha=0.1, v_th=1.0, seed=0)
235
+ >>> fig, ax = plot_phase_space(x, v)
236
+ >>> isinstance(fig, plt.Figure)
237
+ True
238
+ """
239
+ if ax is None:
240
+ fig, ax = plt.subplots()
241
+ else:
242
+ fig = ax.figure
243
+ h = ax.hist2d(x, v, bins=bins, cmap="inferno")
244
+ fig.colorbar(h[3], ax=ax, label="particle count")
245
+ ax.set_xlabel("x")
246
+ ax.set_ylabel("v")
247
+ return fig, ax
248
+
249
+
250
+ def plot_field_energy_history(t: np.ndarray, field_energy: np.ndarray, ax=None):
251
+ """Semilog plot of electrostatic field energy vs. time, showing Landau damping (or two-stream growth).
252
+
253
+ Parameters
254
+ ----------
255
+ t : ndarray
256
+ Time array, as from :func:`physicskit.plasma.kinetic.pic_simulate`.
257
+ field_energy : ndarray
258
+ Field energy at each time, same shape as ``t``.
259
+ ax : matplotlib.axes.Axes, optional
260
+ Axes to draw into; a new figure is created if omitted.
261
+
262
+ Returns
263
+ -------
264
+ fig : matplotlib.figure.Figure
265
+ ax : matplotlib.axes.Axes
266
+
267
+ Examples
268
+ --------
269
+ >>> import numpy as np
270
+ >>> from physicskit.plasma.kinetic import landau_damping_ic, pic_simulate
271
+ >>> x0, v0 = landau_damping_ic(2000, L=4 * np.pi, k_mode=0.5, alpha=0.05, v_th=1.0, seed=0)
272
+ >>> result = pic_simulate(x0, v0, L=4 * np.pi, ng=32, dt=0.1, steps=30)
273
+ >>> fig, ax = plot_field_energy_history(result["t"], result["field_energy"])
274
+ >>> isinstance(fig, plt.Figure)
275
+ True
276
+ """
277
+ if ax is None:
278
+ fig, ax = plt.subplots()
279
+ else:
280
+ fig = ax.figure
281
+ ax.semilogy(t, field_energy)
282
+ ax.set_xlabel("t (1/$\\omega_{pe}$)")
283
+ ax.set_ylabel("field energy")
284
+ return fig, ax
285
+
286
+
287
+ def plot_cma_diagram(X: np.ndarray, Y: np.ndarray, ax=None):
288
+ """Scatter a set of plasma states on log-log Clemmow-Mullaly-Allis (CMA) diagram axes.
289
+
290
+ Parameters
291
+ ----------
292
+ X, Y : ndarray
293
+ CMA coordinates, as from :func:`physicskit.plasma.waves.cma_coordinates`.
294
+ ax : matplotlib.axes.Axes, optional
295
+ Axes to draw into; a new figure is created if omitted.
296
+
297
+ Returns
298
+ -------
299
+ fig : matplotlib.figure.Figure
300
+ ax : matplotlib.axes.Axes
301
+
302
+ Examples
303
+ --------
304
+ >>> import numpy as np
305
+ >>> from physicskit.plasma.waves import cma_coordinates
306
+ >>> omega = np.linspace(0.1, 5.0, 30)
307
+ >>> X, Y = cma_coordinates(omega, wpe=2.0, wce=3.0)
308
+ >>> fig, ax = plot_cma_diagram(X, Y)
309
+ >>> isinstance(fig, plt.Figure)
310
+ True
311
+ """
312
+ if ax is None:
313
+ fig, ax = plt.subplots()
314
+ else:
315
+ fig = ax.figure
316
+ ax.loglog(X, Y, "o-")
317
+ ax.axvline(1.0, color="gray", linestyle="--", linewidth=0.8)
318
+ ax.axhline(1.0, color="gray", linestyle="--", linewidth=0.8)
319
+ ax.set_xlabel(r"$X = \omega_{pe}^2/\omega^2$")
320
+ ax.set_ylabel(r"$Y = \omega_{ce}/\omega$")
321
+ return fig, ax
322
+
323
+
324
+ def plot_phase_space_interactive(x: np.ndarray, v: np.ndarray):
325
+ """Interactive Plotly scatter of a particle-in-cell phase-space snapshot.
326
+
327
+ Unlike :func:`plot_phase_space`, points remain individually
328
+ identifiable under pan and zoom -- useful for inspecting fine
329
+ structure like phase-space vortices in a developed two-stream
330
+ instability.
331
+
332
+ Parameters
333
+ ----------
334
+ x, v : ndarray, shape (n_particles,)
335
+ Particle positions and velocities.
336
+
337
+ Returns
338
+ -------
339
+ plotly.graph_objects.Figure
340
+
341
+ See Also
342
+ --------
343
+ plot_phase_space : The static Matplotlib heatmap equivalent.
344
+
345
+ Examples
346
+ --------
347
+ >>> import numpy as np
348
+ >>> from physicskit.plasma.kinetic import two_stream_ic
349
+ >>> x, v = two_stream_ic(500, L=10.0, v_drift=3.0, v_th=0.5, seed=0)
350
+ >>> fig = plot_phase_space_interactive(x, v)
351
+ >>> isinstance(fig, go.Figure)
352
+ True
353
+ """
354
+ fig = go.Figure(data=go.Scattergl(x=x, y=v, mode="markers", marker={"size": 3, "opacity": 0.5}))
355
+ fig.update_layout(xaxis_title="x", yaxis_title="v")
356
+ return fig
357
+
358
+
359
+ def _animate_1d_snapshots(x: np.ndarray, snapshots: list, xlabel: str, ylabel: str, title: str | None = None, interval: int = 60) -> FuncAnimation:
360
+ """Shared line-plot animator: redraws a single ``y(x)`` curve over a sequence of snapshots."""
361
+ fig, ax = plt.subplots()
362
+ (line,) = ax.plot(x, snapshots[0])
363
+ ax.set_xlabel(xlabel)
364
+ ax.set_ylabel(ylabel)
365
+ if title:
366
+ ax.set_title(title)
367
+ stacked = np.stack(snapshots)
368
+ lo, hi = stacked.min(), stacked.max()
369
+ pad = 0.05 * (hi - lo) if hi > lo else 1.0
370
+ ax.set_ylim(lo - pad, hi + pad)
371
+
372
+ def update(frame):
373
+ line.set_ydata(snapshots[frame])
374
+ return (line,)
375
+
376
+ return FuncAnimation(fig, update, frames=len(snapshots), interval=interval, blit=True)
377
+
378
+
379
+ def _animate_2d_snapshots(
380
+ snapshots: list, extent: tuple, xlabel: str = "x", ylabel: str = "y", cmap: str = "RdBu_r", title: str | None = None, interval: int = 60
381
+ ) -> FuncAnimation:
382
+ """Shared imshow animator: redraws a single 2D scalar field over a sequence of snapshots."""
383
+ fig, ax = plt.subplots()
384
+ vmax = max(np.max(np.abs(s)) for s in snapshots) or 1.0
385
+ im = ax.imshow(snapshots[0].T, origin="lower", extent=extent, cmap=cmap, vmin=-vmax, vmax=vmax, aspect="auto")
386
+ fig.colorbar(im, ax=ax)
387
+ ax.set_xlabel(xlabel)
388
+ ax.set_ylabel(ylabel)
389
+ if title:
390
+ ax.set_title(title)
391
+
392
+ def update(frame):
393
+ im.set_data(snapshots[frame].T)
394
+ return (im,)
395
+
396
+ return FuncAnimation(fig, update, frames=len(snapshots), interval=interval, blit=True)
397
+
398
+
399
+ def animate_reconnection(
400
+ psi0: np.ndarray, eta: float, v0: float, dt: float, steps_per_frame: int, n_frames: int, Lx: float, Ly: float, interval: int = 60
401
+ ) -> FuncAnimation:
402
+ """Animate the flux function :math:`\\psi(x,y,t)` reconnecting at the X-point of a resistive current sheet.
403
+
404
+ Repeatedly calls :func:`physicskit.plasma.instabilities.simulate_reconnection`
405
+ for ``steps_per_frame`` steps at a time, using each call's final state as
406
+ the next call's initial condition, and shows the accumulated flux
407
+ snapshots as an imshow animation -- the antiparallel field lines above
408
+ and below the sheet visibly merge into a single reconnected topology at
409
+ the X-point, and the squeezed-out reconnected flux forms the outflow
410
+ "jets" along the sheet.
411
+
412
+ Each snapshot has its :math:`x`-mean subtracted, :math:`\\psi(x,y,t) -
413
+ \\langle\\psi\\rangle_x(y,t)`, before display. The unperturbed Harris
414
+ profile :math:`-L\\ln\\cosh(y/L)` grows without bound away from the
415
+ sheet, so on a fixed color scale it dwarfs the localized island/X-point
416
+ structure that is actually reconnecting -- left in, the animation reads
417
+ as visually static even though the underlying field is evolving.
418
+ Removing the (x-independent) background isolates exactly the
419
+ x-varying perturbation that breaks and reconnects, which is what makes
420
+ the merging visible frame to frame.
421
+
422
+ Parameters
423
+ ----------
424
+ psi0 : ndarray, shape (nx, ny)
425
+ Initial flux function, e.g. from
426
+ :func:`physicskit.plasma.instabilities.reconnection_harris_ic`.
427
+ eta : float
428
+ Resistivity.
429
+ v0 : float
430
+ Inflow speed.
431
+ dt : float
432
+ Time step per :func:`~physicskit.plasma.instabilities.simulate_reconnection` sub-step.
433
+ steps_per_frame : int
434
+ Number of sub-steps advanced between animation frames.
435
+ n_frames : int
436
+ Number of animation frames.
437
+ Lx, Ly : float
438
+ Domain size.
439
+ interval : int, default=60
440
+ Delay between frames in milliseconds.
441
+
442
+ Returns
443
+ -------
444
+ matplotlib.animation.FuncAnimation
445
+
446
+ Examples
447
+ --------
448
+ >>> from physicskit.plasma.instabilities import reconnection_harris_ic
449
+ >>> psi0 = reconnection_harris_ic(32, 32, Lx=20.0, Ly=20.0, sheet_width=1.0, perturbation_amplitude=0.2)
450
+ >>> anim = animate_reconnection(psi0, eta=0.02, v0=0.05, dt=0.02, steps_per_frame=5, n_frames=4, Lx=20.0, Ly=20.0)
451
+ >>> len(list(anim.new_frame_seq()))
452
+ 4
453
+ """
454
+ psi = psi0
455
+ snapshots = [psi0]
456
+ for _ in range(n_frames - 1):
457
+ result = simulate_reconnection(psi, eta, v0, dt, steps_per_frame, Lx, Ly)
458
+ psi = result["psi"]
459
+ snapshots.append(psi)
460
+ deviations = [s - s.mean(axis=0, keepdims=True) for s in snapshots]
461
+ return _animate_2d_snapshots(
462
+ deviations,
463
+ extent=(0.0, Lx, -Ly / 2.0, Ly / 2.0),
464
+ xlabel="x",
465
+ ylabel="y",
466
+ title=r"Reconnection: perturbed flux $\psi-\langle\psi\rangle_x$",
467
+ interval=interval,
468
+ )
469
+
470
+
471
+ def animate_weibel_filamentation(
472
+ x: np.ndarray, t: np.ndarray, wpe: float, temperature_anisotropy: float, n_modes: int = 12, seed: int = 0, interval: int = 60
473
+ ) -> FuncAnimation:
474
+ """Animate the transverse current filaments growing under the reduced quasi-linear Weibel model.
475
+
476
+ Parameters
477
+ ----------
478
+ x : ndarray, shape (nx,)
479
+ Spatial grid.
480
+ t : ndarray, shape (nt,)
481
+ Animation frame times.
482
+ wpe : float
483
+ Electron plasma frequency.
484
+ temperature_anisotropy : float
485
+ The ratio :math:`T_\\perp/T_\\parallel`; see
486
+ :func:`physicskit.plasma.instabilities.weibel_growth_rate`.
487
+ n_modes : int, default=12
488
+ Number of seeded Fourier modes.
489
+ seed : int, default=0
490
+ Random seed.
491
+ interval : int, default=60
492
+ Delay between frames in milliseconds.
493
+
494
+ Returns
495
+ -------
496
+ matplotlib.animation.FuncAnimation
497
+
498
+ See Also
499
+ --------
500
+ physicskit.plasma.instabilities.simulate_weibel_filamentation : Supplies the current-density snapshots animated here.
501
+
502
+ Examples
503
+ --------
504
+ >>> import numpy as np
505
+ >>> x = np.linspace(0, 20.0, 128, endpoint=False)
506
+ >>> t = np.linspace(0, 5.0, 6)
507
+ >>> anim = animate_weibel_filamentation(x, t, wpe=1.0, temperature_anisotropy=4.0, n_modes=6)
508
+ >>> len(list(anim.new_frame_seq()))
509
+ 6
510
+ """
511
+ J = simulate_weibel_filamentation(x, t, wpe, temperature_anisotropy, n_modes=n_modes, seed=seed)
512
+ snapshots = [J[i] for i in range(J.shape[0])]
513
+ return _animate_1d_snapshots(
514
+ x, snapshots, xlabel="x", ylabel=r"current density $J_y$ (normalized)", title="Weibel/filamentation instability", interval=interval
515
+ )
516
+
517
+
518
+ def animate_two_stream_phase_space(
519
+ x0: np.ndarray, v0: np.ndarray, L: float, ng: int, dt: float, steps_per_frame: int, n_frames: int, interval: int = 60
520
+ ) -> FuncAnimation:
521
+ """Animate two-stream-instability phase space :math:`(x, v)` developing its characteristic vortex.
522
+
523
+ Repeatedly calls :func:`physicskit.plasma.kinetic.pic_simulate` for
524
+ ``steps_per_frame`` leapfrog steps at a time, redrawing a scatter of
525
+ every particle's position and velocity each frame -- the two initially
526
+ separate beams of :func:`physicskit.plasma.kinetic.two_stream_ic`
527
+ visibly wrap around each other into a single phase-space "hole" as the
528
+ instability saturates.
529
+
530
+ Parameters
531
+ ----------
532
+ x0, v0 : ndarray, shape (n_particles,)
533
+ Initial particle positions and velocities, e.g. from
534
+ :func:`physicskit.plasma.kinetic.two_stream_ic`.
535
+ L : float
536
+ Domain length.
537
+ ng : int
538
+ Number of grid points for the PIC field solve.
539
+ dt : float
540
+ Time step per leapfrog sub-step.
541
+ steps_per_frame : int
542
+ Number of PIC steps advanced between animation frames.
543
+ n_frames : int
544
+ Number of animation frames.
545
+ interval : int, default=60
546
+ Delay between frames in milliseconds.
547
+
548
+ Returns
549
+ -------
550
+ matplotlib.animation.FuncAnimation
551
+
552
+ Examples
553
+ --------
554
+ >>> from physicskit.plasma.kinetic import two_stream_ic
555
+ >>> x0, v0 = two_stream_ic(2000, L=10.0, v_drift=3.0, v_th=0.5, seed=0)
556
+ >>> anim = animate_two_stream_phase_space(x0, v0, L=10.0, ng=32, dt=0.05, steps_per_frame=5, n_frames=4)
557
+ >>> len(list(anim.new_frame_seq()))
558
+ 4
559
+ """
560
+ x, v = np.asarray(x0, dtype=float), np.asarray(v0, dtype=float)
561
+ x_snaps, v_snaps = [x.copy()], [v.copy()]
562
+ for _ in range(n_frames - 1):
563
+ result = pic_simulate(x, v, L, ng, dt, steps_per_frame)
564
+ x, v = result["x"], result["v"]
565
+ x_snaps.append(x.copy())
566
+ v_snaps.append(v.copy())
567
+
568
+ fig, ax = plt.subplots()
569
+ scatter = ax.scatter(x_snaps[0], v_snaps[0], s=2, alpha=0.5)
570
+ ax.set_xlim(0, L)
571
+ all_v = np.concatenate(v_snaps)
572
+ vpad = 0.1 * (all_v.max() - all_v.min())
573
+ ax.set_ylim(all_v.min() - vpad, all_v.max() + vpad)
574
+ ax.set_xlabel("x")
575
+ ax.set_ylabel("v")
576
+ ax.set_title("Two-stream instability phase space")
577
+
578
+ def update(frame):
579
+ scatter.set_offsets(np.column_stack([x_snaps[frame], v_snaps[frame]]))
580
+ return (scatter,)
581
+
582
+ return FuncAnimation(fig, update, frames=len(x_snaps), interval=interval, blit=True)
583
+
584
+
585
+ def animate_langmuir_wave(
586
+ x0: np.ndarray, v0: np.ndarray, L: float, ng: int, dt: float, steps_per_frame: int, n_frames: int, interval: int = 60
587
+ ) -> FuncAnimation:
588
+ """Animate a Langmuir wave's electron density oscillating in place at (approximately) the plasma frequency.
589
+
590
+ Repeatedly calls :func:`physicskit.plasma.kinetic.pic_simulate` for
591
+ ``steps_per_frame`` steps at a time and, each frame, deposits the
592
+ current particle positions onto the grid with
593
+ :func:`physicskit.plasma.kinetic.deposit_number_density` -- reusing the
594
+ exact charge-assignment kernel the PIC field solve itself uses, so the
595
+ density shown is precisely what
596
+ :func:`physicskit.plasma.kinetic.pic_step` sees when it solves Poisson's
597
+ equation for the field driving the next sub-step.
598
+
599
+ Parameters
600
+ ----------
601
+ x0, v0 : ndarray, shape (n_particles,)
602
+ Initial particle positions and velocities, e.g. from
603
+ :func:`physicskit.plasma.kinetic.langmuir_wave_ic`.
604
+ L : float
605
+ Domain length.
606
+ ng : int
607
+ Number of grid points.
608
+ dt : float
609
+ Time step per leapfrog sub-step.
610
+ steps_per_frame : int
611
+ Number of PIC steps advanced between animation frames.
612
+ n_frames : int
613
+ Number of animation frames.
614
+ interval : int, default=60
615
+ Delay between frames in milliseconds.
616
+
617
+ Returns
618
+ -------
619
+ matplotlib.animation.FuncAnimation
620
+
621
+ Examples
622
+ --------
623
+ >>> import numpy as np
624
+ >>> from physicskit.plasma.kinetic import langmuir_wave_ic
625
+ >>> k = 2 * np.pi / 4.0
626
+ >>> x0, v0 = langmuir_wave_ic(4000, L=4.0, k_mode=k, alpha=0.05, v_th=0.05, seed=0)
627
+ >>> anim = animate_langmuir_wave(x0, v0, L=4.0, ng=32, dt=0.05, steps_per_frame=4, n_frames=4)
628
+ >>> len(list(anim.new_frame_seq()))
629
+ 4
630
+ """
631
+ x, v = np.asarray(x0, dtype=float), np.asarray(v0, dtype=float)
632
+ x_grid = np.linspace(0.0, L, ng, endpoint=False)
633
+ snapshots = [deposit_number_density(x, L, ng)]
634
+ for _ in range(n_frames - 1):
635
+ result = pic_simulate(x, v, L, ng, dt, steps_per_frame)
636
+ x, v = result["x"], result["v"]
637
+ snapshots.append(deposit_number_density(x, L, ng))
638
+ return _animate_1d_snapshots(x_grid, snapshots, xlabel="x", ylabel="electron density", title="Langmuir wave", interval=interval)
639
+
640
+
641
+ def animate_ion_acoustic_soliton(u0: np.ndarray, x: np.ndarray, dt: float, steps_per_frame: int, n_frames: int, interval: int = 60) -> FuncAnimation:
642
+ """Animate an ion-acoustic soliton propagating without change of shape.
643
+
644
+ Repeatedly calls :func:`physicskit.plasma.waves.ion_acoustic_soliton_evolve`
645
+ for ``steps_per_frame`` steps at a time and animates the resulting
646
+ density-pulse snapshots.
647
+
648
+ Parameters
649
+ ----------
650
+ u0 : ndarray
651
+ Initial soliton profile, e.g. from
652
+ :func:`physicskit.plasma.waves.ion_acoustic_soliton_profile`.
653
+ x : ndarray
654
+ Uniformly spaced periodic spatial grid.
655
+ dt : float
656
+ Time step per sub-step.
657
+ steps_per_frame : int
658
+ Number of steps advanced between animation frames.
659
+ n_frames : int
660
+ Number of animation frames.
661
+ interval : int, default=60
662
+ Delay between frames in milliseconds.
663
+
664
+ Returns
665
+ -------
666
+ matplotlib.animation.FuncAnimation
667
+
668
+ Examples
669
+ --------
670
+ >>> import numpy as np
671
+ >>> from physicskit.plasma.waves import ion_acoustic_soliton_profile
672
+ >>> x = np.linspace(-30, 30, 256, endpoint=False)
673
+ >>> u0 = ion_acoustic_soliton_profile(x, speed=4.0, x0=-15.0)
674
+ >>> anim = animate_ion_acoustic_soliton(u0, x, dt=0.0005, steps_per_frame=200, n_frames=4)
675
+ >>> len(list(anim.new_frame_seq()))
676
+ 4
677
+ """
678
+ u = np.asarray(u0, dtype=float)
679
+ snapshots = [u.copy()]
680
+ for _ in range(n_frames - 1):
681
+ u = ion_acoustic_soliton_evolve(u, x, dt, steps_per_frame)
682
+ snapshots.append(u.copy())
683
+ return _animate_1d_snapshots(x, snapshots, xlabel="x", ylabel="density perturbation", title="Ion-acoustic soliton", interval=interval)
684
+
685
+
686
+ def animate_drift_wave_turbulence(
687
+ phi0: np.ndarray, dt: float, steps_per_frame: int, n_frames: int, length: float, nu: float = 0.03, interval: int = 60
688
+ ) -> FuncAnimation:
689
+ """Animate a Hasegawa-Mima potential field developing turbulent structure from small-amplitude noise.
690
+
691
+ Repeatedly calls
692
+ :func:`physicskit.plasma.turbulence.simulate_hasegawa_mima` for
693
+ ``steps_per_frame`` steps at a time and animates the resulting
694
+ potential-field snapshots.
695
+
696
+ Parameters
697
+ ----------
698
+ phi0 : ndarray, shape (n, n)
699
+ Initial potential field, e.g. from
700
+ :func:`physicskit.plasma.turbulence.drift_wave_noise_ic`.
701
+ dt : float
702
+ Time step per sub-step.
703
+ steps_per_frame : int
704
+ Number of RK4 steps advanced between animation frames.
705
+ n_frames : int
706
+ Number of animation frames.
707
+ length : float
708
+ Physical domain size.
709
+ nu : float, default=0.03
710
+ Dissipation coefficient (see :func:`physicskit.plasma.turbulence.simulate_hasegawa_mima`).
711
+ interval : int, default=60
712
+ Delay between frames in milliseconds.
713
+
714
+ Returns
715
+ -------
716
+ matplotlib.animation.FuncAnimation
717
+
718
+ Examples
719
+ --------
720
+ >>> import numpy as np
721
+ >>> from physicskit.plasma.turbulence import drift_wave_noise_ic
722
+ >>> phi0 = drift_wave_noise_ic(48, 2 * np.pi, amplitude=0.05, seed=0)
723
+ >>> anim = animate_drift_wave_turbulence(phi0, dt=0.02, steps_per_frame=10, n_frames=4, length=2 * np.pi)
724
+ >>> len(list(anim.new_frame_seq()))
725
+ 4
726
+ """
727
+ phi = np.asarray(phi0, dtype=float)
728
+ snapshots = [phi.copy()]
729
+ for _ in range(n_frames - 1):
730
+ result = simulate_hasegawa_mima(phi, dt, steps_per_frame, length, nu=nu)
731
+ phi = result["phi"]
732
+ snapshots.append(phi.copy())
733
+ return _animate_2d_snapshots(snapshots, extent=(0.0, length, 0.0, length), title="Drift-wave turbulence (Hasegawa-Mima)", interval=interval)
734
+
735
+
736
+ def animate_alfven_wave(
737
+ By0: np.ndarray,
738
+ vy0: np.ndarray,
739
+ x: np.ndarray,
740
+ dt: float,
741
+ steps_per_frame: int,
742
+ n_frames: int,
743
+ B0: float,
744
+ rho0: float,
745
+ mu0: float = 1.0,
746
+ interval: int = 60,
747
+ ) -> FuncAnimation:
748
+ """Animate a transverse Alfven-wave pulse propagating (and splitting) along the background field.
749
+
750
+ Repeatedly calls :func:`physicskit.plasma.waves.simulate_alfven_wave`
751
+ for ``steps_per_frame`` steps at a time and animates the resulting
752
+ transverse-field snapshots.
753
+
754
+ Parameters
755
+ ----------
756
+ By0, vy0 : ndarray
757
+ Initial transverse field and velocity perturbations, e.g. from
758
+ :func:`physicskit.plasma.waves.alfven_wave_pulse_ic`.
759
+ x : ndarray
760
+ Uniformly spaced periodic spatial grid.
761
+ dt : float
762
+ Time step per sub-step.
763
+ steps_per_frame : int
764
+ Number of RK4 steps advanced between animation frames.
765
+ n_frames : int
766
+ Number of animation frames.
767
+ B0 : float
768
+ Background field magnitude.
769
+ rho0 : float
770
+ Background mass density.
771
+ mu0 : float, default=1.0
772
+ Vacuum permeability (normalized units by default; see
773
+ :func:`physicskit.plasma.waves.simulate_alfven_wave`).
774
+ interval : int, default=60
775
+ Delay between frames in milliseconds.
776
+
777
+ Returns
778
+ -------
779
+ matplotlib.animation.FuncAnimation
780
+
781
+ Examples
782
+ --------
783
+ >>> import numpy as np
784
+ >>> from physicskit.plasma.waves import alfven_wave_pulse_ic
785
+ >>> x = np.linspace(-20, 20, 256, endpoint=False)
786
+ >>> By0, vy0 = alfven_wave_pulse_ic(x, x0=0.0, width=1.0, amplitude=0.1)
787
+ >>> anim = animate_alfven_wave(By0, vy0, x, dt=0.002, steps_per_frame=200, n_frames=4, B0=1.0, rho0=1.0)
788
+ >>> len(list(anim.new_frame_seq()))
789
+ 4
790
+ """
791
+ By, vy = np.asarray(By0, dtype=float), np.asarray(vy0, dtype=float)
792
+ snapshots = [By.copy()]
793
+ for _ in range(n_frames - 1):
794
+ result = simulate_alfven_wave(By, vy, x, dt, steps_per_frame, B0, rho0, mu0=mu0)
795
+ By, vy = result["By"], result["vy"]
796
+ snapshots.append(By.copy())
797
+ return _animate_1d_snapshots(x, snapshots, xlabel="x", ylabel=r"$B_y$ perturbation", title="Alfven wave", interval=interval)
798
+
799
+
800
+ def animate_wakefield_acceleration(
801
+ x0: float, v0: float, q: float, m: float, E0: float, k: float, v_phase: float, dt: float, steps: int, frame_stride: int = 20, interval: int = 60
802
+ ) -> FuncAnimation:
803
+ """Animate a test charge surfing a prescribed traveling wakefield, with an energy-gain trace inset.
804
+
805
+ Runs :func:`physicskit.plasma.acceleration.simulate_wakefield_acceleration`
806
+ once for the full trajectory, then animates a snapshot of the wakefield
807
+ :math:`E_z(x, t)` with the particle's position marked on it, alongside
808
+ a running plot of its kinetic energy -- showing both the spatial
809
+ "surfing" picture and the resulting energy gain simultaneously.
810
+
811
+ Parameters
812
+ ----------
813
+ x0, v0 : float
814
+ Initial particle position and velocity.
815
+ q, m : float
816
+ Particle charge and mass.
817
+ E0, k, v_phase : float
818
+ Wakefield amplitude, wavenumber, and phase velocity, as in
819
+ :func:`physicskit.plasma.acceleration.wakefield_e_field`.
820
+ dt : float
821
+ Time step.
822
+ steps : int
823
+ Total number of Boris-pusher steps to integrate.
824
+ frame_stride : int, default=20
825
+ Number of integration steps between animation frames.
826
+ interval : int, default=60
827
+ Delay between frames in milliseconds.
828
+
829
+ Returns
830
+ -------
831
+ matplotlib.animation.FuncAnimation
832
+
833
+ Examples
834
+ --------
835
+ >>> anim = animate_wakefield_acceleration(
836
+ ... 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
837
+ ... )
838
+ >>> len(list(anim.new_frame_seq()))
839
+ 11
840
+ """
841
+ result = simulate_wakefield_acceleration(x0, v0, q, m, E0, k, v_phase, dt, steps)
842
+ t_hist, x_hist, kinetic_energy = result["t"], result["x"], result["kinetic_energy"]
843
+ frame_idx = np.arange(0, len(t_hist), frame_stride)
844
+
845
+ x_span = max(abs(x_hist.max() - x_hist.min()), 1.0)
846
+ x_field = np.linspace(x_hist.min() - 0.2 * x_span, x_hist.max() + 0.2 * x_span, 400)
847
+
848
+ fig, (ax_field, ax_energy) = plt.subplots(2, 1, figsize=(6, 6))
849
+ (field_line,) = ax_field.plot(x_field, wakefield_e_field(x_field, t_hist[0], E0, k, v_phase))
850
+ (particle_marker,) = ax_field.plot([x_hist[0]], [wakefield_e_field(x_hist[0], t_hist[0], E0, k, v_phase)], "o", color="red", markersize=8)
851
+ ax_field.set_xlabel("x")
852
+ ax_field.set_ylabel(r"$E_z$")
853
+ ax_field.set_title("Wakefield acceleration")
854
+
855
+ (energy_line,) = ax_energy.plot([], [])
856
+ ax_energy.set_xlim(t_hist[0], t_hist[-1])
857
+ ax_energy.set_ylim(0.0, 1.1 * kinetic_energy.max())
858
+ ax_energy.set_xlabel("t")
859
+ ax_energy.set_ylabel("kinetic energy")
860
+
861
+ def update(i):
862
+ idx = frame_idx[i]
863
+ field_line.set_ydata(wakefield_e_field(x_field, t_hist[idx], E0, k, v_phase))
864
+ particle_marker.set_data([x_hist[idx]], [wakefield_e_field(x_hist[idx], t_hist[idx], E0, k, v_phase)])
865
+ energy_line.set_data(t_hist[: idx + 1], kinetic_energy[: idx + 1])
866
+ return field_line, particle_marker, energy_line
867
+
868
+ return FuncAnimation(fig, update, frames=len(frame_idx), interval=interval, blit=True)