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,588 @@
1
+ """Electrostatic particle-in-cell (PIC) solution of the 1D1V Vlasov-Poisson system.
2
+
3
+ Rather than discretizing the distribution function :math:`f(x, v, t)` on a
4
+ phase-space grid, a PIC code samples it with a finite set of
5
+ computational "super-particles" and lets them stream along exact
6
+ single-particle orbits, recovering the self-consistent field by depositing
7
+ their charge onto a spatial grid and solving Poisson's equation there each
8
+ step. This is Vlasov's collisionless equation
9
+ :math:`\\partial_t f + v\\,\\partial_x f - (e/m)E\\,\\partial_v f = 0` solved
10
+ by the method of characteristics: each particle *is* one characteristic.
11
+ The whole pipeline -- :func:`deposit_number_density`,
12
+ :func:`solve_poisson_1d`, :func:`interpolate_field`, :func:`pic_step` --
13
+ reproduces collisionless (Landau) damping and the two-stream instability
14
+ without ever assuming a collision operator, exactly as the underlying
15
+ kinetic theory predicts.
16
+
17
+ Units throughout are the standard PIC-normalized units: electron charge
18
+ :math:`e=1`, mass :math:`m_e=1`, vacuum permittivity
19
+ :math:`\\varepsilon_0=1`, and equilibrium density :math:`n_0=1`, so that
20
+ the electron plasma frequency :math:`\\omega_{pe}=1` and velocities are in
21
+ units of the thermal speed :math:`v_{th}`. Ions are a fixed, uniform,
22
+ charge-neutralizing background (infinite mass limit).
23
+
24
+ The charge-deposit and field-interpolation kernels -- called once per
25
+ particle per step -- are compiled with Numba, since they dominate the
26
+ cost of every PIC time step.
27
+ """
28
+
29
+ from __future__ import annotations
30
+
31
+ import numpy as np
32
+ from numba import njit
33
+
34
+ __all__ = [
35
+ "maxwellian_velocities",
36
+ "landau_damping_ic",
37
+ "langmuir_wave_ic",
38
+ "two_stream_ic",
39
+ "deposit_number_density",
40
+ "interpolate_field",
41
+ "solve_poisson_1d",
42
+ "pic_step",
43
+ "pic_simulate",
44
+ "landau_damping_rate",
45
+ ]
46
+
47
+
48
+ def maxwellian_velocities(n_particles: int, v_th: float, seed: int = 0) -> np.ndarray:
49
+ """Sample particle velocities from a Maxwellian (Gaussian) distribution.
50
+
51
+ Parameters
52
+ ----------
53
+ n_particles : int
54
+ Number of particles to sample.
55
+ v_th : float
56
+ Thermal speed (standard deviation of the Gaussian), in
57
+ normalized velocity units.
58
+ seed : int, default=0
59
+ Seed for the pseudo-random number generator, for reproducibility.
60
+
61
+ Returns
62
+ -------
63
+ ndarray, shape (n_particles,)
64
+ Sampled velocities.
65
+
66
+ Examples
67
+ --------
68
+ >>> import numpy as np
69
+ >>> v = maxwellian_velocities(4, v_th=1.0, seed=0)
70
+ >>> bool(np.allclose(v, [0.12573022, -0.13210486, 0.64042265, 0.10490012]))
71
+ True
72
+ """
73
+ rng = np.random.default_rng(seed)
74
+ return rng.normal(0.0, v_th, n_particles)
75
+
76
+
77
+ def landau_damping_ic(n_particles: int, L: float, k_mode: float, alpha: float, v_th: float, seed: int = 0) -> tuple:
78
+ """Quiet-start initial condition seeding a single-mode density perturbation for a Landau damping test.
79
+
80
+ Displaces an otherwise uniform particle load by
81
+ :math:`\\delta x = (\\alpha/k)\\sin(kx_0)`, which -- since particle
82
+ number is conserved through the displacement's Jacobian -- produces
83
+ exactly the density perturbation :math:`n(x) \\approx n_0(1 -
84
+ \\alpha\\cos(kx))` used in the classic linear Landau damping test
85
+ problem, without the sampling noise a random density draw would add
86
+ on top of the intended signal.
87
+
88
+ Parameters
89
+ ----------
90
+ n_particles : int
91
+ Number of particles.
92
+ L : float
93
+ Domain length (periodic), in normalized length units. Choosing
94
+ :math:`L = 2\\pi/k_{mode}` fits exactly one wavelength.
95
+ k_mode : float
96
+ Wavenumber of the seeded perturbation.
97
+ alpha : float
98
+ Perturbation amplitude (:math:`\\alpha \\ll 1` for the linear regime).
99
+ v_th : float
100
+ Thermal speed of the background Maxwellian.
101
+ seed : int, default=0
102
+ Random seed for the velocity sampling.
103
+
104
+ Returns
105
+ -------
106
+ x, v : ndarray, shape (n_particles,)
107
+ Particle positions (in :math:`[0, L)`) and velocities.
108
+
109
+ See Also
110
+ --------
111
+ pic_simulate : Evolve this initial condition forward in time.
112
+ landau_damping_rate : The analytic decay rate this test is checked against.
113
+
114
+ Examples
115
+ --------
116
+ >>> import numpy as np
117
+ >>> x, v = landau_damping_ic(4, L=4.0, k_mode=2 * 3.141592653589793 / 4.0, alpha=0.1, v_th=1.0, seed=0)
118
+ >>> bool(np.all((x >= 0) & (x < 4.0)))
119
+ True
120
+ >>> x.shape, v.shape
121
+ ((4,), (4,))
122
+ """
123
+ x0 = np.linspace(0.0, L, n_particles, endpoint=False)
124
+ x = np.mod(x0 + (alpha / k_mode) * np.sin(k_mode * x0), L)
125
+ v = maxwellian_velocities(n_particles, v_th, seed)
126
+ return x, v
127
+
128
+
129
+ def langmuir_wave_ic(n_particles: int, L: float, k_mode: float, alpha: float, v_th: float, seed: int = 0) -> tuple:
130
+ """Small-amplitude single-mode initial condition for a driven Langmuir (electron plasma) wave.
131
+
132
+ Identical in construction to :func:`landau_damping_ic` -- the same
133
+ quiet-start displacement :math:`\\delta x=(\\alpha/k)\\sin(kx_0)` seeds
134
+ the same density perturbation :math:`n_0(1-\\alpha\\cos(kx))` -- but
135
+ additionally imparts each particle the small coherent velocity
136
+ perturbation :math:`\\delta v = (\\alpha\\,\\omega_{pe}/k)\\sin(kx_0)`
137
+ predicted by the linearized cold-fluid continuity equation for a wave
138
+ oscillating at the electron plasma frequency (:math:`\\omega\\approx\\omega_{pe}=1`
139
+ in these normalized units). Giving both the density *and* velocity
140
+ perturbation their linear-theory phase relationship launches a wave
141
+ that is (for :math:`k\\lambda_D \\ll 1`, i.e. `v_th` small enough that
142
+ Landau damping in :func:`landau_damping_rate` is negligible) very
143
+ weakly damped, so :func:`pic_simulate` shows the density oscillating
144
+ in place at :math:`\\omega_{pe}` for many periods rather than decaying
145
+ within a few, as the otherwise-identical :func:`landau_damping_ic`
146
+ (fluid velocity perturbation omitted) would for the same `alpha`.
147
+
148
+ Parameters
149
+ ----------
150
+ n_particles : int
151
+ Number of particles.
152
+ L : float
153
+ Domain length (periodic); :math:`L=2\\pi/k_{mode}` fits one wavelength.
154
+ k_mode : float
155
+ Wavenumber of the seeded standing wave.
156
+ alpha : float
157
+ Perturbation amplitude (:math:`\\alpha \\ll 1`).
158
+ v_th : float
159
+ Thermal speed of the background Maxwellian; keep
160
+ :math:`k_{mode}\\,v_{th} \\ll \\omega_{pe}=1` for the wave to be
161
+ only weakly Landau-damped.
162
+ seed : int, default=0
163
+ Random seed for the thermal velocity sampling.
164
+
165
+ Returns
166
+ -------
167
+ x, v : ndarray, shape (n_particles,)
168
+ Particle positions and velocities.
169
+
170
+ See Also
171
+ --------
172
+ landau_damping_ic : The companion density-only perturbation (no
173
+ coherent velocity kick), which damps rather than sustains its
174
+ oscillation.
175
+ pic_simulate : Evolve this initial condition forward in time.
176
+
177
+ Examples
178
+ --------
179
+ >>> import numpy as np
180
+ >>> k = 2 * np.pi / 4.0
181
+ >>> x, v = langmuir_wave_ic(4, L=4.0, k_mode=k, alpha=0.05, v_th=0.05, seed=0)
182
+ >>> bool(np.all((x >= 0) & (x < 4.0)))
183
+ True
184
+ >>> x.shape, v.shape
185
+ ((4,), (4,))
186
+ """
187
+ x0 = np.linspace(0.0, L, n_particles, endpoint=False)
188
+ x = np.mod(x0 + (alpha / k_mode) * np.sin(k_mode * x0), L)
189
+ omega_pe = 1.0
190
+ dv = (alpha * omega_pe / k_mode) * np.sin(k_mode * x0)
191
+ v = maxwellian_velocities(n_particles, v_th, seed) + dv
192
+ return x, v
193
+
194
+
195
+ def two_stream_ic(n_particles: int, L: float, v_drift: float, v_th: float, seed: int = 0) -> tuple:
196
+ """Initial condition for the two-stream instability: two counter-streaming Maxwellian beams.
197
+
198
+ Splits the particles into two equal populations drifting at
199
+ :math:`\\pm v_{drift}`, each with thermal spread :math:`v_{th}`, and
200
+ seeds the fastest-growing long-wavelength mode with a small
201
+ density ripple. When :math:`v_{drift}` exceeds the thermal spread by
202
+ enough to make the combined velocity distribution doubly-peaked, the
203
+ positive-slope region between the two peaks violates the (kinetic)
204
+ Penrose stability criterion and the ripple grows exponentially,
205
+ eventually rolling the two beams up into a single phase-space vortex.
206
+
207
+ Parameters
208
+ ----------
209
+ n_particles : int
210
+ Number of particles (split evenly between the two beams).
211
+ L : float
212
+ Domain length (periodic).
213
+ v_drift : float
214
+ Drift speed of each beam (beams move at :math:`+v_{drift}` and :math:`-v_{drift}`).
215
+ v_th : float
216
+ Thermal spread of each beam.
217
+ seed : int, default=0
218
+ Random seed.
219
+
220
+ Returns
221
+ -------
222
+ x, v : ndarray, shape (n_particles,)
223
+ Particle positions and velocities.
224
+
225
+ See Also
226
+ --------
227
+ landau_damping_ic : The companion (stable) single-beam initial condition.
228
+
229
+ Examples
230
+ --------
231
+ >>> import numpy as np
232
+ >>> x, v = two_stream_ic(1000, L=10.0, v_drift=3.0, v_th=0.5, seed=0)
233
+ >>> x.shape, v.shape
234
+ ((1000,), (1000,))
235
+ >>> bool(np.mean(v) < 0.5)
236
+ True
237
+ """
238
+ rng = np.random.default_rng(seed)
239
+ half = n_particles // 2
240
+ x0 = np.linspace(0.0, L, n_particles, endpoint=False)
241
+ k_seed = 2.0 * np.pi / L
242
+ x = np.mod(x0 + 0.01 * np.sin(k_seed * x0), L)
243
+ v = np.empty(n_particles)
244
+ v[:half] = rng.normal(v_drift, v_th, half)
245
+ v[half:] = rng.normal(-v_drift, v_th, n_particles - half)
246
+ return x, v
247
+
248
+
249
+ @njit(cache=True)
250
+ def _deposit_cic_kernel(x, weight, L, ng):
251
+ dx = L / ng
252
+ rho = np.zeros(ng)
253
+ for p in range(x.shape[0]):
254
+ xi = x[p] / dx
255
+ i0 = int(np.floor(xi)) % ng
256
+ frac = xi - np.floor(xi)
257
+ i1 = (i0 + 1) % ng
258
+ rho[i0] += weight * (1.0 - frac) / dx
259
+ rho[i1] += weight * frac / dx
260
+ return rho
261
+
262
+
263
+ @njit(cache=True)
264
+ def _interpolate_cic_kernel(x, field, L, ng):
265
+ dx = L / ng
266
+ out = np.empty(x.shape[0])
267
+ for p in range(x.shape[0]):
268
+ xi = x[p] / dx
269
+ i0 = int(np.floor(xi)) % ng
270
+ frac = xi - np.floor(xi)
271
+ i1 = (i0 + 1) % ng
272
+ out[p] = field[i0] * (1.0 - frac) + field[i1] * frac
273
+ return out
274
+
275
+
276
+ @njit(cache=True)
277
+ def _push_kernel(x, v, E_at_particles, qm, dt, L):
278
+ n = x.shape[0]
279
+ for p in range(n):
280
+ v[p] += qm * E_at_particles[p] * dt
281
+ x[p] = (x[p] + v[p] * dt) % L
282
+ return x, v
283
+
284
+
285
+ def deposit_number_density(x: np.ndarray, L: float, ng: int, n0: float = 1.0) -> np.ndarray:
286
+ """Deposit particle positions onto a grid as a number density, via cloud-in-cell (CIC) weighting.
287
+
288
+ Each particle represents a "cloud" of physical charge spanning one
289
+ grid cell, split linearly between its two nearest grid points -- the
290
+ standard first-order PIC weighting scheme, chosen because it is exact
291
+ for a uniform density and (unlike nearest-grid-point deposit)
292
+ produces a smooth, differentiable force with no self-force
293
+ discontinuities as particles cross cell boundaries.
294
+
295
+ Parameters
296
+ ----------
297
+ x : ndarray, shape (n_particles,)
298
+ Particle positions in :math:`[0, L)`.
299
+ L : float
300
+ Domain length (periodic).
301
+ ng : int
302
+ Number of grid points.
303
+ n0 : float, default=1.0
304
+ Equilibrium number density (sets each particle's statistical weight,
305
+ :math:`n_0 L / n_{particles}`).
306
+
307
+ Returns
308
+ -------
309
+ ndarray, shape (ng,)
310
+ Number density on the grid.
311
+
312
+ See Also
313
+ --------
314
+ interpolate_field : The companion gather operation.
315
+
316
+ Examples
317
+ --------
318
+ Total deposited charge exactly equals the physical charge represented,
319
+ regardless of how the particles are distributed:
320
+
321
+ >>> import numpy as np
322
+ >>> x = np.array([0.1, 2.4, 4.9, 7.7])
323
+ >>> rho = deposit_number_density(x, L=10.0, ng=20, n0=2.0)
324
+ >>> dx = 10.0 / 20
325
+ >>> round(float(np.sum(rho) * dx), 8)
326
+ 20.0
327
+ """
328
+ n_particles = x.shape[0]
329
+ weight = n0 * L / n_particles
330
+ return _deposit_cic_kernel(np.asarray(x, dtype=float), float(weight), float(L), int(ng))
331
+
332
+
333
+ def interpolate_field(x: np.ndarray, field_grid: np.ndarray, L: float) -> np.ndarray:
334
+ """Interpolate a grid-defined field to particle positions, via cloud-in-cell (CIC) weighting.
335
+
336
+ The gather step dual to :func:`deposit_number_density`: using the
337
+ same linear weights for both deposit and gather is what makes the
338
+ PIC method momentum-conserving (no self-force on an isolated particle).
339
+
340
+ Parameters
341
+ ----------
342
+ x : ndarray, shape (n_particles,)
343
+ Particle positions in :math:`[0, L)`.
344
+ field_grid : ndarray, shape (ng,)
345
+ Field values on the grid, e.g. from :func:`solve_poisson_1d`.
346
+ L : float
347
+ Domain length (periodic).
348
+
349
+ Returns
350
+ -------
351
+ ndarray, shape (n_particles,)
352
+ Field value at each particle's position.
353
+
354
+ Examples
355
+ --------
356
+ A particle sitting exactly on a grid node picks up that node's value:
357
+
358
+ >>> import numpy as np
359
+ >>> ng = 8
360
+ >>> field_grid = np.array([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0])
361
+ >>> x = np.array([2 * (10.0 / ng)])
362
+ >>> interpolate_field(x, field_grid, L=10.0)
363
+ array([3.])
364
+ """
365
+ ng = field_grid.shape[0]
366
+ return _interpolate_cic_kernel(np.asarray(x, dtype=float), np.asarray(field_grid, dtype=float), float(L), int(ng))
367
+
368
+
369
+ def solve_poisson_1d(rho: np.ndarray, L: float) -> np.ndarray:
370
+ """Solve the 1D periodic Poisson equation :math:`dE/dx = \\rho` (normalized :math:`\\varepsilon_0=1`) via FFT.
371
+
372
+ Parameters
373
+ ----------
374
+ rho : ndarray, shape (ng,)
375
+ Net charge density on the grid (e.g. ion background minus deposited
376
+ electron density). Its mean is discarded, since a uniform charge
377
+ density has no periodic solution and physically should integrate
378
+ to zero net charge in the box.
379
+ L : float
380
+ Domain length (periodic).
381
+
382
+ Returns
383
+ -------
384
+ ndarray, shape (ng,)
385
+ Electric field on the grid.
386
+
387
+ See Also
388
+ --------
389
+ deposit_number_density : Supplies the density this solves for.
390
+ interpolate_field : Gathers this field back onto the particles.
391
+
392
+ Examples
393
+ --------
394
+ >>> import numpy as np
395
+ >>> ng = 64
396
+ >>> L = 2 * np.pi
397
+ >>> x_grid = np.linspace(0, L, ng, endpoint=False)
398
+ >>> rho = np.sin(x_grid)
399
+ >>> E = solve_poisson_1d(rho, L)
400
+ >>> E_exact = -np.cos(x_grid)
401
+ >>> bool(np.max(np.abs(E - E_exact)) < 1e-10)
402
+ True
403
+ """
404
+ ng = rho.shape[0]
405
+ k = 2.0 * np.pi * np.fft.fftfreq(ng, d=L / ng)
406
+ rho_hat = np.fft.fft(rho - np.mean(rho))
407
+ E_hat = np.zeros_like(rho_hat)
408
+ nonzero = k != 0
409
+ E_hat[nonzero] = -1j * rho_hat[nonzero] / k[nonzero]
410
+ return np.real(np.fft.ifft(E_hat))
411
+
412
+
413
+ def pic_step(x: np.ndarray, v: np.ndarray, L: float, ng: int, dt: float, qm: float = -1.0, n0: float = 1.0) -> tuple:
414
+ """Advance the electrostatic PIC system one leapfrog step.
415
+
416
+ Deposits the electron density, solves for the self-consistent field
417
+ against a uniform neutralizing ion background, gathers the field back
418
+ onto the particles, and kicks/drifts them -- one full cycle of the
419
+ deposit-solve-gather-push loop at the heart of every PIC code.
420
+ Velocities are staggered a half step behind positions (standard
421
+ leapfrog); see :func:`pic_simulate` for a driver that initializes
422
+ that offset correctly.
423
+
424
+ Parameters
425
+ ----------
426
+ x : ndarray, shape (n_particles,)
427
+ Particle positions in :math:`[0, L)`.
428
+ v : ndarray, shape (n_particles,)
429
+ Particle velocities, staggered a half step behind ``x``.
430
+ L : float
431
+ Domain length (periodic).
432
+ ng : int
433
+ Number of grid points.
434
+ dt : float
435
+ Time step.
436
+ qm : float, default=-1.0
437
+ Charge-to-mass ratio in normalized units (``-1.0`` for electrons
438
+ with a fixed, uniform ion background of density ``n0``).
439
+ n0 : float, default=1.0
440
+ Equilibrium number density.
441
+
442
+ Returns
443
+ -------
444
+ x_new, v_new : ndarray
445
+ Updated positions and velocities.
446
+ field_energy : float
447
+ :math:`\\int E^2/2\\,dx`, evaluated at the field used for this step's kick.
448
+
449
+ See Also
450
+ --------
451
+ pic_simulate : Repeated application of this step with correct leapfrog initialization.
452
+
453
+ Examples
454
+ --------
455
+ >>> import numpy as np
456
+ >>> x = np.linspace(0, 10, 50, endpoint=False)
457
+ >>> v = np.zeros(50)
458
+ >>> x_new, v_new, fe = pic_step(x, v, L=10.0, ng=32, dt=0.1)
459
+ >>> x_new.shape, v_new.shape
460
+ ((50,), (50,))
461
+ >>> bool(np.isfinite(fe))
462
+ True
463
+ """
464
+ n_e = deposit_number_density(x, L, ng, n0)
465
+ rho = n0 - n_e
466
+ E = solve_poisson_1d(rho, L)
467
+ field_energy = 0.5 * np.sum(E**2) * (L / ng)
468
+ E_at_particles = interpolate_field(x, E, L)
469
+ x_new, v_new = _push_kernel(np.asarray(x, dtype=float).copy(), np.asarray(v, dtype=float).copy(), E_at_particles, float(qm), float(dt), float(L))
470
+ return x_new, v_new, float(field_energy)
471
+
472
+
473
+ def pic_simulate(x0: np.ndarray, v0: np.ndarray, L: float, ng: int, dt: float, steps: int, qm: float = -1.0, n0: float = 1.0) -> dict:
474
+ """Run an electrostatic PIC simulation forward in time, recording the field-energy history.
475
+
476
+ Correctly initializes the leapfrog velocity offset (staggering
477
+ ``v0`` back by half a step using the field at ``x0``) before
478
+ repeatedly applying :func:`pic_step`.
479
+
480
+ Parameters
481
+ ----------
482
+ x0 : ndarray, shape (n_particles,)
483
+ Initial particle positions, e.g. from :func:`landau_damping_ic` or :func:`two_stream_ic`.
484
+ v0 : ndarray, shape (n_particles,)
485
+ Initial particle velocities.
486
+ L : float
487
+ Domain length (periodic).
488
+ ng : int
489
+ Number of grid points.
490
+ dt : float
491
+ Time step.
492
+ steps : int
493
+ Number of steps to advance.
494
+ qm : float, default=-1.0
495
+ Charge-to-mass ratio in normalized units.
496
+ n0 : float, default=1.0
497
+ Equilibrium number density.
498
+
499
+ Returns
500
+ -------
501
+ dict
502
+ ``{"t": ndarray of shape (steps,), "field_energy": ndarray of shape (steps,),
503
+ "x": final positions, "v": final velocities}``.
504
+
505
+ See Also
506
+ --------
507
+ pic_step : The single-step update repeated here.
508
+ landau_damping_rate : Analytic decay rate to compare the field-energy history against.
509
+
510
+ Examples
511
+ --------
512
+ >>> import numpy as np
513
+ >>> x0, v0 = landau_damping_ic(2000, L=4 * np.pi, k_mode=0.5, alpha=0.01, v_th=1.0, seed=0)
514
+ >>> result = pic_simulate(x0, v0, L=4 * np.pi, ng=32, dt=0.1, steps=20)
515
+ >>> result["field_energy"].shape
516
+ (20,)
517
+ >>> bool(np.all(np.isfinite(result["field_energy"])))
518
+ True
519
+ """
520
+ x = np.asarray(x0, dtype=float).copy()
521
+ v = np.asarray(v0, dtype=float).copy()
522
+ n_e = deposit_number_density(x, L, ng, n0)
523
+ E0 = solve_poisson_1d(n0 - n_e, L)
524
+ E0_at_particles = interpolate_field(x, E0, L)
525
+ v = v - 0.5 * qm * E0_at_particles * dt
526
+
527
+ field_energy = np.empty(steps)
528
+ for s in range(steps):
529
+ x, v, fe = pic_step(x, v, L, ng, dt, qm, n0)
530
+ field_energy[s] = fe
531
+ t = np.arange(steps) * dt
532
+ return {"t": t, "field_energy": field_energy, "x": x, "v": v}
533
+
534
+
535
+ def landau_damping_rate(k: float, v_th: float, omega_pe: float = 1.0) -> float:
536
+ """Analytic linear Landau damping rate for a Maxwellian electron plasma.
537
+
538
+ The classic weak-damping result (Landau, 1946) for a wave of
539
+ wavenumber :math:`k` on a Maxwellian of thermal speed :math:`v_{th}`,
540
+ with Debye length :math:`\\lambda_D = v_{th}/\\omega_{pe}`:
541
+
542
+ .. math::
543
+
544
+ \\gamma = -\\omega_{pe}\\sqrt{\\frac{\\pi}{8}}\\,
545
+ \\frac{1}{(k\\lambda_D)^3}\\,
546
+ \\exp\\!\\left(-\\frac{1}{2(k\\lambda_D)^2} - \\frac{3}{2}\\right).
547
+
548
+ Electrons resonant with the wave's phase velocity
549
+ (:math:`v = \\omega/k`) surf it, extracting energy from the field on
550
+ net because the Maxwellian has slightly more slower particles being
551
+ accelerated than faster particles being decelerated -- a purely
552
+ collisionless damping mechanism with no dissipation at the particle
553
+ level, reproduced here by :func:`pic_simulate` without any explicit
554
+ damping term in the equations of motion.
555
+
556
+ Parameters
557
+ ----------
558
+ k : float
559
+ Wavenumber, in units of inverse Debye length times
560
+ :math:`k\\lambda_D` conventions -- concretely, pass the physical
561
+ wavenumber and set ``v_th``/``omega_pe`` consistently.
562
+ v_th : float
563
+ Electron thermal speed.
564
+ omega_pe : float, default=1.0
565
+ Electron plasma frequency (``1.0`` in the normalized units used
566
+ throughout this module).
567
+
568
+ Returns
569
+ -------
570
+ float
571
+ Damping rate :math:`\\gamma` (negative, since the wave decays).
572
+ The formula is only accurate for weak damping,
573
+ :math:`k\\lambda_D \\lesssim 0.5`.
574
+
575
+ See Also
576
+ --------
577
+ pic_simulate : Numerically reproduces this decay from first principles.
578
+
579
+ Examples
580
+ --------
581
+ The standard textbook benchmark case, :math:`k\\lambda_D = 0.5`:
582
+
583
+ >>> round(float(landau_damping_rate(k=0.5, v_th=1.0)), 4)
584
+ -0.1514
585
+ """
586
+ lambda_D = v_th / omega_pe
587
+ klD = k * lambda_D
588
+ return -omega_pe * np.sqrt(np.pi / 8.0) * (1.0 / klD**3) * np.exp(-1.0 / (2.0 * klD**2) - 1.5)