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,198 @@
1
+ r"""Polytropic stellar models via the Lane-Emden equation.
2
+
3
+ Uses **gravitational units with** :math:`G=1` by default -- every function
4
+ that needs it accepts ``G`` as a keyword argument, the same convention
5
+ :mod:`physicskit.relativity` uses for its own geometrized units.
6
+
7
+ - :func:`lane_emden` -- the dimensionless hydrostatic-equilibrium profile
8
+ of a self-gravitating polytropic sphere.
9
+ - :class:`PolytropicStar` -- a physical star (radius, mass) built from one
10
+ Lane-Emden solution.
11
+ - :func:`chandrasekhar_mass` -- the white-dwarf mass limit.
12
+ - :func:`main_sequence_luminosity` -- the empirical mass-luminosity relation.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import numpy as np
18
+ from scipy.integrate import solve_ivp
19
+
20
+ __all__ = ["lane_emden", "PolytropicStar", "chandrasekhar_mass", "main_sequence_luminosity"]
21
+
22
+
23
+ def lane_emden(n, xi_max=10.0, n_points=2000):
24
+ r"""Integrate the Lane-Emden equation for a polytrope of index ``n``.
25
+
26
+ .. math::
27
+
28
+ \frac{1}{\xi^2}\frac{d}{d\xi}\left(\xi^2\frac{d\theta}{d\xi}\right)
29
+ + \theta^n = 0, \qquad \theta(0)=1,\ \theta'(0)=0
30
+
31
+ the dimensionless hydrostatic-equilibrium profile of a self-gravitating
32
+ sphere with equation of state :math:`P=K\rho^{1+1/n}`.
33
+
34
+ Parameters
35
+ ----------
36
+ n : float
37
+ Polytropic index.
38
+ xi_max : float, default=10.0
39
+ Upper integration bound, used only as a fallback for indices
40
+ (e.g. ``n=5``) where :math:`\theta` never reaches zero.
41
+ n_points : int, default=2000
42
+ Number of points to sample the returned arrays at.
43
+
44
+ Returns
45
+ -------
46
+ xi : ndarray
47
+ Dimensionless radius, from just above 0 to the surface (the first
48
+ zero of :math:`\theta`) or to ``xi_max``.
49
+ theta : ndarray
50
+ The Lane-Emden function :math:`\theta(\xi)`.
51
+
52
+ Examples
53
+ --------
54
+ >>> xi, theta = lane_emden(0.0)
55
+ >>> round(float(xi[-1]), 3) # exact analytic surface is sqrt(6)
56
+ 2.449
57
+ """
58
+ xi0 = 1e-6
59
+ theta0 = 1.0 - xi0**2 / 6.0
60
+ dtheta0 = -xi0 / 3.0
61
+
62
+ def rhs(xi, y):
63
+ theta, dtheta = y
64
+ theta_pos = max(theta, 0.0)
65
+ d2theta = -(theta_pos**n) - (2.0 / xi) * dtheta
66
+ return [dtheta, d2theta]
67
+
68
+ def surface_event(xi, y):
69
+ return y[0]
70
+
71
+ surface_event.terminal = True
72
+ surface_event.direction = -1
73
+
74
+ sol = solve_ivp(
75
+ rhs,
76
+ [xi0, xi_max],
77
+ [theta0, dtheta0],
78
+ events=surface_event,
79
+ dense_output=True,
80
+ rtol=1e-9,
81
+ atol=1e-11,
82
+ max_step=xi_max / 2000,
83
+ )
84
+ xi_surface = sol.t[-1]
85
+ xi = np.linspace(xi0, xi_surface, n_points)
86
+ theta = sol.sol(xi)[0]
87
+ theta = np.clip(theta, 0.0, None)
88
+ return xi, theta
89
+
90
+
91
+ class PolytropicStar:
92
+ r"""A physical polytropic star built from one Lane-Emden solution.
93
+
94
+ Parameters
95
+ ----------
96
+ n : float
97
+ Polytropic index, :math:`P=K\rho^{1+1/n}`.
98
+ K : float
99
+ Polytropic constant.
100
+ rho_c : float
101
+ Central density.
102
+ G : float, default=1.0
103
+ Gravitational constant.
104
+
105
+ Examples
106
+ --------
107
+ >>> star = PolytropicStar(n=0.0, K=1.0, rho_c=1.0)
108
+ >>> round(star.mass / (4 / 3 * np.pi * star.radius**3 * star.rho_c), 3)
109
+ 1.0
110
+ """
111
+
112
+ def __init__(self, n, K, rho_c, G=1.0):
113
+ self.n = n
114
+ self.K = K
115
+ self.rho_c = rho_c
116
+ self.G = G
117
+ self._xi, self._theta = lane_emden(n)
118
+
119
+ @property
120
+ def xi1(self):
121
+ """The dimensionless surface radius, the last point of the Lane-Emden solution."""
122
+ return float(self._xi[-1])
123
+
124
+ @property
125
+ def alpha(self):
126
+ r"""Physical length scale, :math:`\alpha=\sqrt{(n+1)K\rho_c^{1/n-1}/(4\pi G)}`."""
127
+ if self.n == 0:
128
+ exponent = -1.0
129
+ else:
130
+ exponent = 1.0 / self.n - 1.0
131
+ return float(np.sqrt((self.n + 1) * self.K * self.rho_c**exponent / (4.0 * np.pi * self.G)))
132
+
133
+ @property
134
+ def radius(self):
135
+ r"""Physical stellar radius, :math:`R=\alpha\xi_1`."""
136
+ return self.alpha * self.xi1
137
+
138
+ @property
139
+ def mass(self):
140
+ r"""Physical stellar mass, :math:`M=4\pi\alpha^3\rho_c\left[-\xi_1^2\theta'(\xi_1)\right]`."""
141
+ dtheta_surface = (self._theta[-1] - self._theta[-2]) / (self._xi[-1] - self._xi[-2])
142
+ return float(4.0 * np.pi * self.alpha**3 * self.rho_c * (-(self.xi1**2) * dtheta_surface))
143
+
144
+
145
+ def chandrasekhar_mass(mu_e=2.0):
146
+ r"""The Chandrasekhar mass limit, in solar masses.
147
+
148
+ .. math::
149
+
150
+ M_{\rm Ch} \approx \frac{5.83}{\mu_e^2}\ M_\odot
151
+
152
+ (the standard numerical prefactor from the n=3 polytrope /
153
+ relativistic-electron-degeneracy treatment; see e.g. Kippenhahn,
154
+ Weigert & Weiss, *Stellar Structure and Evolution*).
155
+
156
+ Parameters
157
+ ----------
158
+ mu_e : float, default=2.0
159
+ Mean molecular weight per electron (2.0 for carbon/oxygen white
160
+ dwarfs).
161
+
162
+ Returns
163
+ -------
164
+ float
165
+ Mass limit, in solar masses.
166
+
167
+ Examples
168
+ --------
169
+ >>> M = chandrasekhar_mass(2.0)
170
+ >>> 1.2 < M < 1.5
171
+ True
172
+ """
173
+ return 5.83 / mu_e**2
174
+
175
+
176
+ def main_sequence_luminosity(mass_solar):
177
+ r"""Empirical main-sequence mass-luminosity relation, :math:`L/L_\odot\approx(M/M_\odot)^{3.5}`.
178
+
179
+ Valid roughly for main-sequence stars in the 0.5-10 solar mass range;
180
+ this is a pure empirical power law, not derived from first-principles
181
+ stellar structure.
182
+
183
+ Parameters
184
+ ----------
185
+ mass_solar : float
186
+ Mass, in solar masses.
187
+
188
+ Returns
189
+ -------
190
+ float
191
+ Luminosity, in solar luminosities.
192
+
193
+ Examples
194
+ --------
195
+ >>> round(main_sequence_luminosity(1.0), 6)
196
+ 1.0
197
+ """
198
+ return mass_solar**3.5
File without changes
@@ -0,0 +1,7 @@
1
+ """Test-session setup: force Matplotlib's non-interactive Agg backend
2
+ before anything imports pyplot, so the suite (and CI, which has no
3
+ display) can exercise the plotting/animation code headlessly."""
4
+
5
+ import matplotlib
6
+
7
+ matplotlib.use("Agg")
@@ -0,0 +1,159 @@
1
+ import numpy as np
2
+ import pytest
3
+
4
+ from physicskit.astro.cosmic_web import (
5
+ first_caustic_time,
6
+ lagrangian_grid,
7
+ random_displacement_potential,
8
+ zeldovich_displacement,
9
+ zeldovich_hessian_eigenvalues,
10
+ zeldovich_position,
11
+ )
12
+
13
+ # For a single plane-wave potential Phi(q) = A*cos(kx*x + ky*y + phase), the
14
+ # Hessian is H = -A*cos(theta)*[[kx^2, kx*ky], [kx*ky, ky^2]] at every q,
15
+ # i.e. a rank-1 (outer-product) matrix scaled by c = -A*cos(theta). Its two
16
+ # eigenvalues are exactly {0, c*|k|^2}, so the *largest* eigenvalue at any
17
+ # given q is max(0, c*|k|^2) -- and this is maximized over q (giving the
18
+ # analytic first-caustic time 1/(A*|k|^2)) exactly where cos(theta) = -1,
19
+ # i.e. where kx*x + ky*y + phase = pi (mod 2*pi).
20
+
21
+
22
+ def test_single_mode_hessian_eigenvalues_exact_at_compression_maximum():
23
+ """At the specific q where the mode's phase argument equals pi (its
24
+ point of maximal compression), the Hessian eigenvalues are exactly
25
+ (0, A*|k|^2) -- a fully deterministic, hand-derivable check."""
26
+ kx, ky, A = 2 * np.pi, 0.0, 1.5
27
+ k_vectors = np.array([[kx, ky]])
28
+ amplitudes = np.array([A])
29
+ phases = np.array([np.pi]) # theta = kx*0 + ky*0 + pi = pi at q=(0,0)
30
+
31
+ q_star = np.array([[0.0, 0.0]])
32
+ eigs = zeldovich_hessian_eigenvalues(q_star, k_vectors, amplitudes, phases)
33
+
34
+ k_sq = kx**2 + ky**2
35
+ assert eigs.shape == (1, 2)
36
+ assert eigs[0, 0] == pytest.approx(0.0, abs=1e-12)
37
+ assert eigs[0, 1] == pytest.approx(A * k_sq, rel=1e-10)
38
+
39
+
40
+ def test_first_caustic_time_matches_single_mode_analytic_value_exactly():
41
+ """first_caustic_time at that same single deterministic point should
42
+ equal the closed-form D_collapse = 1/(A*|k|^2) exactly."""
43
+ kx, ky, A = 2 * np.pi, 0.0, 1.5
44
+ k_vectors = np.array([[kx, ky]])
45
+ amplitudes = np.array([A])
46
+ phases = np.array([np.pi])
47
+
48
+ q_star = np.array([[0.0, 0.0]])
49
+ D_collapse = first_caustic_time(q_star, k_vectors, amplitudes, phases)
50
+ analytic = 1.0 / (A * (kx**2 + ky**2))
51
+ assert D_collapse == pytest.approx(analytic, rel=1e-10)
52
+
53
+
54
+ def test_first_caustic_time_converges_with_grid_resolution():
55
+ """On a *generic* grid (the compression maximum does not sit exactly on
56
+ a grid point), first_caustic_time only finds the nearest sampled point
57
+ to the true maximum, so it overestimates D_collapse -- but the error
58
+ should shrink as the grid is refined and the sampling gets closer to
59
+ the true maximizing point."""
60
+ L = 1.0
61
+ kx, ky, A = 2 * np.pi / L, 0.0, 1.0
62
+ k_vectors = np.array([[kx, ky]])
63
+ amplitudes = np.array([A])
64
+ phases = np.array([0.7]) # arbitrary offset, not aligned with any grid spacing
65
+ analytic = 1.0 / (A * (kx**2 + ky**2))
66
+
67
+ q_coarse = lagrangian_grid(20, L)
68
+ q_fine = lagrangian_grid(400, L)
69
+
70
+ D_coarse = first_caustic_time(q_coarse, k_vectors, amplitudes, phases)
71
+ D_fine = first_caustic_time(q_fine, k_vectors, amplitudes, phases)
72
+
73
+ error_coarse = D_coarse - analytic
74
+ error_fine = D_fine - analytic
75
+
76
+ # The grid-based estimate can only ever undershoot the true max
77
+ # eigenvalue, so it can only ever overshoot D_collapse.
78
+ assert error_coarse >= 0.0
79
+ assert error_fine >= 0.0
80
+ assert error_fine < error_coarse
81
+ assert error_fine < 1e-5
82
+
83
+
84
+ def test_zeldovich_position_at_zero_growth_returns_lagrangian_position():
85
+ k_vectors, amplitudes, phases = random_displacement_potential(6, k_min=1.0, k_max=4.0, amplitude_scale=0.2, seed=3)
86
+ q = lagrangian_grid(12, 2.0)
87
+ x0 = zeldovich_position(q, 0.0, k_vectors, amplitudes, phases)
88
+ assert x0 == pytest.approx(q, abs=1e-12)
89
+
90
+
91
+ def test_zeldovich_displacement_matches_finite_difference_gradient():
92
+ """Sanity check independent of the eigenvalue-formula correctness:
93
+ the analytic gradient should match a numerical finite-difference
94
+ gradient of Phi evaluated by hand from the same plane-wave sum."""
95
+ rng = np.random.default_rng(42)
96
+ n_modes = 4
97
+ k_vectors = rng.uniform(-3.0, 3.0, size=(n_modes, 2))
98
+ amplitudes = rng.uniform(0.1, 1.0, size=n_modes)
99
+ phases = rng.uniform(0.0, 2 * np.pi, size=n_modes)
100
+
101
+ def phi(point):
102
+ theta = k_vectors @ point + phases
103
+ return float(np.sum(amplitudes * np.cos(theta)))
104
+
105
+ q_points = rng.uniform(-2.0, 2.0, size=(5, 2))
106
+ analytic_grad = zeldovich_displacement(q_points, k_vectors, amplitudes, phases)
107
+
108
+ h = 1e-6
109
+ for i, point in enumerate(q_points):
110
+ numeric_grad = np.zeros(2)
111
+ for d in range(2):
112
+ step = np.zeros(2)
113
+ step[d] = h
114
+ numeric_grad[d] = (phi(point + step) - phi(point - step)) / (2 * h)
115
+ assert analytic_grad[i] == pytest.approx(numeric_grad, abs=1e-5)
116
+
117
+
118
+ def test_shapes_and_no_nans():
119
+ k_vectors, amplitudes, phases = random_displacement_potential(5, k_min=1.0, k_max=3.0, amplitude_scale=0.1, seed=7)
120
+ q = lagrangian_grid(10, 2.0)
121
+ n = q.shape[0]
122
+
123
+ assert q.shape == (100, 2)
124
+ assert k_vectors.shape == (5, 2)
125
+ assert amplitudes.shape == (5,)
126
+ assert phases.shape == (5,)
127
+
128
+ grad = zeldovich_displacement(q, k_vectors, amplitudes, phases)
129
+ x = zeldovich_position(q, 0.5, k_vectors, amplitudes, phases)
130
+ eigs = zeldovich_hessian_eigenvalues(q, k_vectors, amplitudes, phases)
131
+ D_collapse = first_caustic_time(q, k_vectors, amplitudes, phases)
132
+
133
+ assert grad.shape == (n, 2)
134
+ assert x.shape == (n, 2)
135
+ assert eigs.shape == (n, 2)
136
+ assert np.all(eigs[:, 0] <= eigs[:, 1])
137
+ assert isinstance(D_collapse, float)
138
+ assert D_collapse > 0.0
139
+
140
+ assert not np.any(np.isnan(grad))
141
+ assert not np.any(np.isnan(x))
142
+ assert not np.any(np.isnan(eigs))
143
+ assert not np.isnan(D_collapse)
144
+
145
+
146
+ def test_random_displacement_potential_invalid_arguments_raise():
147
+ with pytest.raises(ValueError):
148
+ random_displacement_potential(4, k_min=1.0, k_max=2.0, amplitude_scale=0.1, ndim=3)
149
+ with pytest.raises(ValueError):
150
+ random_displacement_potential(0, k_min=1.0, k_max=2.0, amplitude_scale=0.1)
151
+ with pytest.raises(ValueError):
152
+ random_displacement_potential(4, k_min=2.0, k_max=1.0, amplitude_scale=0.1)
153
+
154
+
155
+ def test_lagrangian_grid_shape_and_bounds():
156
+ q = lagrangian_grid(7, 3.0)
157
+ assert q.shape == (49, 2)
158
+ assert np.all(q >= 0.0)
159
+ assert np.all(q < 3.0)
@@ -0,0 +1,46 @@
1
+ import numpy as np
2
+ import pytest
3
+ from scipy.integrate import quad
4
+
5
+ from physicskit.astro.galactic_dynamics import (
6
+ circular_velocity,
7
+ nfw_density,
8
+ nfw_enclosed_mass,
9
+ nfw_potential,
10
+ )
11
+
12
+
13
+ def test_nfw_enclosed_mass_matches_numerical_integral_of_density():
14
+ rho_s, r_s = 2.0, 3.0
15
+ for r in [1.0, 3.0, 10.0]:
16
+ expected, _ = quad(lambda rp: 4 * np.pi * rp**2 * nfw_density(rp, rho_s, r_s), 0, r)
17
+ assert nfw_enclosed_mass(r, rho_s, r_s) == pytest.approx(expected, rel=1e-4)
18
+
19
+
20
+ def test_nfw_enclosed_mass_monotonically_increasing():
21
+ rho_s, r_s = 1.0, 1.0
22
+ r = np.linspace(0.1, 20, 50)
23
+ M = nfw_enclosed_mass(r, rho_s, r_s)
24
+ assert np.all(np.diff(M) > 0)
25
+
26
+
27
+ def test_nfw_potential_derivative_matches_enclosed_mass():
28
+ rho_s, r_s, G = 1.5, 2.0, 1.0
29
+ r0 = 4.0
30
+ dr = 1e-5
31
+ dPhi_dr = (nfw_potential(r0 + dr, rho_s, r_s, G) - nfw_potential(r0 - dr, rho_s, r_s, G)) / (2 * dr)
32
+ expected = G * nfw_enclosed_mass(r0, rho_s, r_s) / r0**2
33
+ assert dPhi_dr == pytest.approx(expected, rel=1e-4)
34
+
35
+
36
+ def test_circular_velocity_point_mass_is_keplerian():
37
+ M0, G, r = 5.0, 1.0, 3.0
38
+ v = circular_velocity(r, lambda rr: M0, G=G)
39
+ assert v == pytest.approx(np.sqrt(G * M0 / r))
40
+
41
+
42
+ def test_nfw_rotation_curve_is_roughly_flat_at_large_radius():
43
+ rho_s, r_s = 1.0, 1.0
44
+ r = np.linspace(5 * r_s, 30 * r_s, 50)
45
+ v = circular_velocity(r, lambda rr: nfw_enclosed_mass(rr, rho_s, r_s))
46
+ assert np.std(v) / np.mean(v) < 0.2
@@ -0,0 +1,81 @@
1
+ import numpy as np
2
+ import pytest
3
+
4
+ from physicskit.astro.nbody import NBodySystem, figure_eight_initial_conditions, gravitational_acceleration, leapfrog_step
5
+
6
+
7
+ def test_gravitational_acceleration_two_body():
8
+ positions = np.array([[0.0, 0.0, 0.0], [1.0, 0.0, 0.0]])
9
+ masses = np.array([1.0, 1.0])
10
+ acc = gravitational_acceleration(positions, masses, G=1.0)
11
+ assert acc[0] == pytest.approx([1.0, 0.0, 0.0], abs=1e-10)
12
+ assert acc[1] == pytest.approx([-1.0, 0.0, 0.0], abs=1e-10)
13
+
14
+
15
+ def test_leapfrog_step_shapes():
16
+ positions = np.random.default_rng(0).normal(size=(4, 3))
17
+ velocities = np.zeros((4, 3))
18
+ masses = np.ones(4)
19
+ new_pos, new_vel = leapfrog_step(positions, velocities, masses, dt=0.01)
20
+ assert new_pos.shape == (4, 3)
21
+ assert new_vel.shape == (4, 3)
22
+
23
+
24
+ def _circular_two_body_system(G=1.0, m=1.0, d=2.0):
25
+ v = np.sqrt(G * m / (2 * d))
26
+ positions = np.array([[d / 2, 0.0, 0.0], [-d / 2, 0.0, 0.0]])
27
+ velocities = np.array([[0.0, v, 0.0], [0.0, -v, 0.0]])
28
+ masses = np.array([m, m])
29
+ return NBodySystem(positions, velocities, masses, G=G), v, d
30
+
31
+
32
+ def test_two_body_circular_orbit_conserves_energy_and_angular_momentum():
33
+ system, v, d = _circular_two_body_system()
34
+ E0 = system.total_energy()
35
+ L0 = system.total_angular_momentum()
36
+
37
+ period = 2 * np.pi * (d / 2) / v
38
+ n_steps = 2000
39
+ dt = period * 5 / n_steps # five orbits
40
+ system.simulate(dt, n_steps)
41
+
42
+ E1 = system.total_energy()
43
+ L1 = system.total_angular_momentum()
44
+ assert E1 == pytest.approx(E0, rel=1e-3)
45
+ assert L1 == pytest.approx(L0, rel=1e-3)
46
+
47
+
48
+ def test_two_body_orbit_returns_near_starting_position_after_one_period():
49
+ system, v, d = _circular_two_body_system()
50
+ period = 2 * np.pi * (d / 2) / v
51
+ n_steps = 500
52
+ dt = period / n_steps
53
+ history = system.simulate(dt, n_steps)
54
+ assert history[-1] == pytest.approx(history[0], abs=1e-2)
55
+
56
+
57
+ def test_figure_eight_initial_conditions_has_zero_net_momentum():
58
+ """The choreography's whole premise is that the three equal masses
59
+ share a common center of mass at rest; a nonzero net momentum would
60
+ mean the figure drifts rather than retracing itself in place."""
61
+ positions, velocities, masses = figure_eight_initial_conditions()
62
+ momentum = np.sum(masses[:, None] * velocities, axis=0)
63
+ assert momentum == pytest.approx([0.0, 0.0, 0.0], abs=1e-8)
64
+
65
+
66
+ def test_figure_eight_initial_conditions_is_periodic_and_conserves_energy():
67
+ """Integrating for one full period (T ~ 6.32591398, the well-known
68
+ value for this choreography) should return each body close to its
69
+ starting position, with energy conserved by the symplectic leapfrog
70
+ integrator."""
71
+ positions, velocities, masses = figure_eight_initial_conditions()
72
+ system = NBodySystem(positions, velocities, masses)
73
+ E0 = system.total_energy()
74
+
75
+ period = 6.32591398
76
+ n_steps = 20000
77
+ dt = period / n_steps
78
+ system.simulate(dt, n_steps)
79
+
80
+ assert system.total_energy() == pytest.approx(E0, rel=1e-6)
81
+ assert system.positions == pytest.approx(positions, abs=1e-2)
@@ -0,0 +1,73 @@
1
+ import numpy as np
2
+ import pytest
3
+
4
+ from physicskit.astro.orbital_mechanics import (
5
+ hohmann_transfer,
6
+ orbital_elements_from_state,
7
+ orbital_period,
8
+ state_from_orbital_elements,
9
+ vis_viva_speed,
10
+ )
11
+
12
+
13
+ def test_orbital_period_matches_kepler_third_law():
14
+ mu = 1.0
15
+ a = 2.0
16
+ T = orbital_period(a, mu)
17
+ assert T == pytest.approx(2 * np.pi * np.sqrt(a**3 / mu))
18
+
19
+
20
+ def test_vis_viva_parabolic_limit():
21
+ v = vis_viva_speed(r=1.0, a=np.inf, mu=1.0)
22
+ assert v == pytest.approx(np.sqrt(2.0))
23
+
24
+
25
+ def test_vis_viva_circular_orbit_matches_period():
26
+ mu = 1.0
27
+ r = 3.0
28
+ v = vis_viva_speed(r, a=r, mu=mu)
29
+ period_from_v = 2 * np.pi * r / v
30
+ assert period_from_v == pytest.approx(orbital_period(r, mu))
31
+
32
+
33
+ ELEMENT_CASES = [
34
+ (1.0, 0.3, 0.5, 1.0, 0.7, 1.2),
35
+ (2.0, 0.1, 1.2, 2.5, 0.3, 4.0),
36
+ (1.5, 0.5, 0.9, 0.2, 2.1, 0.5),
37
+ (1.5, 0.4, 0.8, 4.0, 4.0, 1.0), # n_vec[1]<0 and e_vec[2]<0 branches of raan/argp
38
+ (1.5, 0.4, 0.0, 0.0, 0.0, 1.0), # equatorial (n<=1e-12): raan, argp forced to 0
39
+ (1.5, 0.0, 0.8, 1.0, 0.0, 0.0), # circular (e<=1e-12): argp, nu forced to 0
40
+ ]
41
+
42
+
43
+ @pytest.mark.parametrize("a,e,i,raan,argp,nu", ELEMENT_CASES)
44
+ def test_orbital_elements_roundtrip(a, e, i, raan, argp, nu):
45
+ mu = 1.0
46
+ r_vec, v_vec = state_from_orbital_elements(a, e, i, raan, argp, nu, mu)
47
+ a2, e2, i2, raan2, argp2, nu2 = orbital_elements_from_state(r_vec, v_vec, mu)
48
+ assert a2 == pytest.approx(a, rel=1e-8)
49
+ assert e2 == pytest.approx(e, rel=1e-8)
50
+ assert i2 == pytest.approx(i, rel=1e-8)
51
+ assert raan2 == pytest.approx(raan % (2 * np.pi), rel=1e-8)
52
+ assert argp2 == pytest.approx(argp % (2 * np.pi), rel=1e-8)
53
+ assert nu2 == pytest.approx(nu % (2 * np.pi), rel=1e-8)
54
+
55
+
56
+ def test_hohmann_transfer_outward_deltav_matches_speed_differences():
57
+ mu, r1, r2 = 1.0, 1.0, 4.0
58
+ a_t = (r1 + r2) / 2.0
59
+ dv1, dv2, t = hohmann_transfer(r1, r2, mu)
60
+ expected_dv1 = vis_viva_speed(r1, a_t, mu) - vis_viva_speed(r1, r1, mu)
61
+ expected_dv2 = vis_viva_speed(r2, r2, mu) - vis_viva_speed(r2, a_t, mu)
62
+ assert dv1 == pytest.approx(abs(expected_dv1))
63
+ assert dv2 == pytest.approx(abs(expected_dv2))
64
+ assert dv1 > 0
65
+ assert dv2 > 0
66
+ assert t == pytest.approx(orbital_period(a_t, mu) / 2.0)
67
+
68
+
69
+ def test_hohmann_transfer_inward_is_positive_magnitudes():
70
+ dv1, dv2, t = hohmann_transfer(4.0, 1.0, mu=1.0)
71
+ assert dv1 > 0
72
+ assert dv2 > 0
73
+ assert t > 0