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,54 @@
1
+ import numpy as np
2
+
3
+ from physicskit.relativity.chapters.kerr import KerrBlackHole
4
+ from physicskit.relativity.chapters.schwarzschild import SchwarzschildBlackHole
5
+
6
+
7
+ def test_circular_orbit_energy_and_angular_momentum_conserved():
8
+ bh = SchwarzschildBlackHole(M=1.0)
9
+ y0 = bh.circular_orbit_initial_state(r0=10.0)
10
+ traj = bh.integrate_geodesic(y0, dtau=0.02, n_steps=3000)
11
+ E0, L0 = traj["energy"][0], traj["angular_momentum"][0]
12
+ assert np.allclose(traj["energy"], E0, rtol=1e-5, atol=1e-5)
13
+ assert np.allclose(traj["angular_momentum"], L0, rtol=1e-5, atol=1e-5)
14
+
15
+
16
+ def test_eccentric_orbit_energy_and_angular_momentum_conserved():
17
+ bh = SchwarzschildBlackHole(M=1.0)
18
+ y0 = bh.eccentric_orbit_initial_state(r0=20.0, eccentricity_boost=0.15)
19
+ traj = bh.integrate_geodesic(y0, dtau=0.02, n_steps=20000)
20
+ E0, L0 = traj["energy"][0], traj["angular_momentum"][0]
21
+ assert np.allclose(traj["energy"], E0, rtol=1e-5, atol=1e-5)
22
+ assert np.allclose(traj["angular_momentum"], L0, rtol=1e-5, atol=1e-5)
23
+ # a genuinely eccentric orbit should show radial oscillation
24
+ assert traj["r"].max() - traj["r"].min() > 1.0
25
+
26
+
27
+ def test_null_geodesic_energy_and_angular_momentum_conserved():
28
+ bh = SchwarzschildBlackHole(M=1.0)
29
+ y0 = bh.null_geodesic_initial_state(r0=500.0, impact_parameter=20.0, ingoing=True)
30
+ traj = bh.integrate_geodesic(y0, dtau=0.5, n_steps=5000)
31
+ E0, L0 = traj["energy"][0], traj["angular_momentum"][0]
32
+ assert np.allclose(traj["energy"], E0, rtol=1e-5, atol=1e-5)
33
+ assert np.allclose(traj["angular_momentum"], L0, rtol=1e-5, atol=1e-5)
34
+
35
+
36
+ def test_kerr_equatorial_circular_orbit_four_velocity_normalized():
37
+ bh = KerrBlackHole(M=1.0, a=0.7)
38
+ E, L = bh.circular_orbit_conserved_quantities(r0=10.0, prograde=True)
39
+ result = bh.integrate_equatorial_geodesic(r0=10.0, E=E, L=L, mu2=1.0, dtau=0.02, n_steps=3000)
40
+ assert np.allclose(result["norm"], -1.0, atol=1e-5)
41
+
42
+
43
+ def test_kerr_equatorial_eccentric_orbit_four_velocity_normalized():
44
+ bh = KerrBlackHole(M=1.0, a=0.7)
45
+ E, L = bh.circular_orbit_conserved_quantities(r0=10.0, prograde=True)
46
+ result = bh.integrate_equatorial_geodesic(r0=10.0, E=E, L=L * 0.98, mu2=1.0, dtau=0.02, n_steps=6000)
47
+ assert np.allclose(result["norm"], -1.0, atol=1e-4)
48
+ assert result["r"].max() - result["r"].min() > 0.5
49
+
50
+
51
+ def test_kerr_equatorial_null_geodesic_four_velocity_normalized():
52
+ bh = KerrBlackHole(M=1.0, a=0.5)
53
+ result = bh.integrate_equatorial_geodesic(r0=500.0, E=1.0, L=15.0, mu2=0.0, dtau=0.5, n_steps=5000)
54
+ assert np.allclose(result["norm"], 0.0, atol=1e-4)
@@ -0,0 +1,34 @@
1
+ """Coverage for physicskit.relativity.utils.constants's simple geometrized-unit
2
+ conversion functions (previously untested)."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import numpy as np
7
+ import pytest
8
+
9
+ from physicskit.relativity.utils.constants import (
10
+ C_SI,
11
+ geometrized_to_meters,
12
+ geometrized_to_seconds,
13
+ geometrized_to_solar_masses,
14
+ seconds_to_geometrized,
15
+ solar_masses_to_geometrized,
16
+ )
17
+
18
+
19
+ def test_geometrized_to_meters_is_the_identity():
20
+ assert geometrized_to_meters(5.0) == pytest.approx(5.0)
21
+
22
+
23
+ def test_geometrized_to_seconds_divides_by_c():
24
+ assert geometrized_to_seconds(C_SI) == pytest.approx(1.0)
25
+
26
+
27
+ def test_seconds_to_geometrized_is_the_inverse_of_geometrized_to_seconds():
28
+ t_s = 2.5
29
+ assert geometrized_to_seconds(seconds_to_geometrized(t_s)) == pytest.approx(t_s)
30
+ assert np.asarray(seconds_to_geometrized(t_s)).shape == ()
31
+
32
+
33
+ def test_geometrized_to_solar_masses_is_the_inverse_of_solar_masses_to_geometrized():
34
+ assert geometrized_to_solar_masses(solar_masses_to_geometrized(30.0)) == pytest.approx(30.0, rel=1e-6)
@@ -0,0 +1,61 @@
1
+ import numpy as np
2
+ import pytest
3
+
4
+ from physicskit.relativity.chapters.cosmology import FLRWCosmology
5
+
6
+
7
+ def test_flat_universe_omega_k_zero():
8
+ cosmo = FLRWCosmology(H0=70.0, Omega_m=0.3, Omega_r=0.0, Omega_Lambda=0.7)
9
+ assert cosmo.Omega_k == pytest.approx(0.0)
10
+
11
+
12
+ def test_hubble_parameter_today_equals_H0():
13
+ cosmo = FLRWCosmology(H0=67.4, Omega_m=0.315, Omega_r=0.0, Omega_Lambda=0.685)
14
+ assert cosmo.hubble_parameter(1.0) == pytest.approx(67.4, rel=1e-6)
15
+
16
+
17
+ def test_expansion_rate_decreases_going_back_from_matter_domination():
18
+ cosmo = FLRWCosmology(H0=70.0, Omega_m=0.3, Omega_r=0.0, Omega_Lambda=0.7)
19
+ # E(a) should be larger in the past (smaller a) for an expanding universe
20
+ assert cosmo.E(0.5) > cosmo.E(1.0)
21
+
22
+
23
+ def test_redshift_scale_factor_roundtrip():
24
+ cosmo = FLRWCosmology()
25
+ z = 2.5
26
+ a = cosmo.redshift_to_scale_factor(z)
27
+ assert cosmo.scale_factor_to_redshift(a) == pytest.approx(z)
28
+
29
+
30
+ def test_age_of_universe_is_physically_reasonable():
31
+ cosmo = FLRWCosmology(H0=70.0, Omega_m=0.3, Omega_r=0.0, Omega_Lambda=0.7)
32
+ age = cosmo.age_gyr()
33
+ assert 12.0 < age < 15.0
34
+
35
+
36
+ def test_age_increases_with_scale_factor():
37
+ cosmo = FLRWCosmology(H0=70.0, Omega_m=0.3, Omega_r=0.0, Omega_Lambda=0.7)
38
+ assert cosmo.age_gyr(0.5) < cosmo.age_gyr(1.0) < cosmo.age_gyr(1.5)
39
+
40
+
41
+ def test_comoving_distance_increases_with_redshift():
42
+ cosmo = FLRWCosmology(H0=70.0, Omega_m=0.3, Omega_r=0.0, Omega_Lambda=0.7)
43
+ d1 = cosmo.comoving_distance_mpc(0.5)
44
+ d2 = cosmo.comoving_distance_mpc(1.0)
45
+ d3 = cosmo.comoving_distance_mpc(2.0)
46
+ assert 0.0 < d1 < d2 < d3
47
+
48
+
49
+ def test_luminosity_distance_exceeds_comoving_distance_at_nonzero_redshift():
50
+ cosmo = FLRWCosmology(H0=70.0, Omega_m=0.3, Omega_r=0.0, Omega_Lambda=0.7)
51
+ z = 1.0
52
+ assert cosmo.luminosity_distance_mpc(z) == pytest.approx((1.0 + z) * cosmo.comoving_distance_mpc(z))
53
+ assert cosmo.luminosity_distance_mpc(z) > cosmo.comoving_distance_mpc(z)
54
+
55
+
56
+ def test_scale_factor_history_shape():
57
+ cosmo = FLRWCosmology(H0=70.0, Omega_m=0.3, Omega_r=0.0, Omega_Lambda=0.7)
58
+ t_gyr, a = cosmo.scale_factor_history(n_points=50)
59
+ assert t_gyr.shape == (50,)
60
+ assert a.shape == (50,)
61
+ assert np.all(np.diff(t_gyr) > 0.0) # age monotonically increases with scale factor
@@ -0,0 +1,117 @@
1
+ import numpy as np
2
+ import pytest
3
+
4
+ from physicskit.relativity.chapters.gw_merger import BinaryMerger
5
+
6
+
7
+ def test_chirp_mass_equal_mass_case():
8
+ merger = BinaryMerger(m1=30.0, m2=30.0, distance=1000.0)
9
+ # for equal masses, chirp mass = m * 2^(-1/5)
10
+ assert merger.chirp_mass == pytest.approx(30.0 * 2.0 ** (-1.0 / 5.0), rel=1e-10)
11
+
12
+
13
+ def test_symmetric_mass_ratio_bounds():
14
+ equal = BinaryMerger(m1=20.0, m2=20.0, distance=1.0)
15
+ unequal = BinaryMerger(m1=50.0, m2=5.0, distance=1.0)
16
+ assert equal.symmetric_mass_ratio == pytest.approx(0.25)
17
+ assert unequal.symmetric_mass_ratio < 0.25
18
+
19
+
20
+ def test_inspiral_frequency_increases_toward_merger():
21
+ merger = BinaryMerger(m1=30.0, m2=25.0, distance=1000.0)
22
+ t = np.linspace(-2.0, -0.01, 200)
23
+ f = merger.inspiral_frequency(t, t_merger=0.0)
24
+ assert np.all(np.diff(f) > 0.0)
25
+
26
+
27
+ def test_inspiral_phase_matches_frequency_derivative():
28
+ merger = BinaryMerger(m1=30.0, m2=25.0, distance=1000.0)
29
+ t = np.linspace(-2.0, -0.5, 1000)
30
+ dt = t[1] - t[0]
31
+ phase = merger.inspiral_phase(t, t_merger=0.0)
32
+ f_from_phase = np.gradient(phase, dt) / (2.0 * np.pi)
33
+ f_formula = merger.inspiral_frequency(t, t_merger=0.0)
34
+ assert np.allclose(f_from_phase, f_formula, rtol=1e-3)
35
+
36
+
37
+ def test_inspiral_strain_shapes_and_finite():
38
+ merger = BinaryMerger(m1=30.0, m2=25.0, distance=1000.0, inclination=0.5)
39
+ t = np.linspace(-2.0, -0.1, 300)
40
+ hp, hc = merger.inspiral_strain(t, t_merger=0.0)
41
+ assert hp.shape == t.shape
42
+ assert hc.shape == t.shape
43
+ assert np.all(np.isfinite(hp))
44
+ assert np.all(np.isfinite(hc))
45
+
46
+
47
+ def test_face_on_gives_constant_envelope_circular_polarization():
48
+ # face-on (inclination=0): h_plus and h_cross combine into a constant
49
+ # envelope (cos^2(phase) + sin^2(phase) = 1) equal to the raw amplitude.
50
+ merger = BinaryMerger(m1=30.0, m2=25.0, distance=1000.0, inclination=0.0)
51
+ t = np.array([-0.5])
52
+ hp, hc = merger.inspiral_strain(t, t_merger=0.0)
53
+ amp = (4.0 / merger.distance) * merger.chirp_mass ** (5.0 / 3.0) * (np.pi * merger.inspiral_frequency(t, 0.0)) ** (2.0 / 3.0)
54
+ assert np.hypot(hp, hc)[0] == pytest.approx(amp[0], rel=1e-6)
55
+
56
+
57
+ def test_qnm_frequency_and_damping_positive():
58
+ merger = BinaryMerger(m1=30.0, m2=25.0, distance=1000.0)
59
+ f_qnm, tau_damp = merger.qnm_frequency_damping()
60
+ assert f_qnm > 0.0
61
+ assert tau_damp > 0.0
62
+
63
+
64
+ def test_ringdown_strain_decays():
65
+ merger = BinaryMerger(m1=30.0, m2=25.0, distance=1000.0)
66
+ t = np.linspace(0.0, 500.0, 500)
67
+ hp, hc = merger.ringdown_strain(t, t_merger=0.0, amplitude=1.0)
68
+ envelope = np.hypot(hp, hc)
69
+ assert envelope[0] >= envelope[-1]
70
+ assert np.all(np.isfinite(envelope))
71
+
72
+
73
+ def test_full_waveform_continuous_and_finite():
74
+ merger = BinaryMerger(m1=30.0, m2=25.0, distance=1000.0)
75
+ t = np.linspace(-1.0, 0.05, 500)
76
+ hp, hc = merger.full_waveform(t, t_merger=0.0)
77
+ assert np.all(np.isfinite(hp))
78
+ assert np.all(np.isfinite(hc))
79
+ assert hp.shape == t.shape
80
+
81
+
82
+ def test_remnant_mass_less_than_total_mass():
83
+ merger = BinaryMerger(m1=30.0, m2=25.0, distance=1000.0)
84
+ M_f, a_f = merger.remnant_estimate()
85
+ assert 0.0 < M_f < merger.total_mass
86
+ assert 0.0 <= a_f < M_f
87
+
88
+
89
+ def test_orbital_period_matches_keplers_third_law():
90
+ merger = BinaryMerger(m1=1.4, m2=1.4, distance=1.0)
91
+ a = 1.0e6
92
+ P = merger.orbital_period(a)
93
+ assert pytest.approx(2.0 * np.pi * np.sqrt(a**3 / merger.total_mass)) == P
94
+
95
+
96
+ def test_semi_major_axis_decay_rate_is_negative_and_grows_with_eccentricity():
97
+ merger = BinaryMerger(m1=1.4, m2=1.4, distance=1.0)
98
+ a = 1.0e6
99
+ rate_circular = merger.semi_major_axis_decay_rate(a, eccentricity=0.0)
100
+ rate_eccentric = merger.semi_major_axis_decay_rate(a, eccentricity=0.6)
101
+ assert rate_circular < 0.0
102
+ assert abs(rate_eccentric) > abs(rate_circular)
103
+
104
+
105
+ def test_hulse_taylor_pulsar_period_decay_matches_observation():
106
+ # PSR B1913+16: the discovery that won Hulse and Taylor the 1993 Nobel
107
+ # Prize. Predicted GR period decay should match the observed
108
+ # -2.4056e-12 (dimensionless, s/s) to within a few percent.
109
+ import physicskit.relativity.utils.constants as const
110
+
111
+ m1 = const.solar_masses_to_geometrized(1.4398)
112
+ m2 = const.solar_masses_to_geometrized(1.3886)
113
+ psr = BinaryMerger(m1=m1, m2=m2, distance=1.0)
114
+ period_s = 27906.98
115
+ a = (psr.total_mass * (period_s * const.C_SI) ** 2 / (4.0 * np.pi**2)) ** (1.0 / 3.0)
116
+ dPdt = psr.period_decay_rate(a, eccentricity=0.6171338)
117
+ assert dPdt == pytest.approx(-2.4056e-12, rel=0.05)
@@ -0,0 +1,116 @@
1
+ import numpy as np
2
+ import pytest
3
+
4
+ from physicskit.relativity.chapters.kerr import KerrBlackHole
5
+
6
+
7
+ def test_schwarzschild_limit_a_zero():
8
+ bh = KerrBlackHole(M=2.0, a=0.0)
9
+ assert bh.outer_horizon_radius == pytest.approx(4.0)
10
+ assert bh.inner_horizon_radius == pytest.approx(0.0)
11
+ assert bh.ergosphere_radius(np.pi / 2.0) == pytest.approx(4.0)
12
+ assert bh.isco_radius(prograde=True) == pytest.approx(12.0)
13
+ assert bh.isco_radius(prograde=False) == pytest.approx(12.0)
14
+
15
+
16
+ def test_invalid_spin_raises():
17
+ with pytest.raises(ValueError):
18
+ KerrBlackHole(M=1.0, a=1.0)
19
+ with pytest.raises(ValueError):
20
+ KerrBlackHole(M=1.0, a=-0.1)
21
+
22
+
23
+ def test_prograde_isco_smaller_than_retrograde():
24
+ bh = KerrBlackHole(M=1.0, a=0.9)
25
+ assert bh.isco_radius(prograde=True) < 6.0
26
+ assert bh.isco_radius(prograde=False) > 6.0
27
+ assert bh.isco_radius(prograde=True) < bh.isco_radius(prograde=False)
28
+
29
+
30
+ def test_isco_approaches_horizon_for_extremal_spin():
31
+ # convergence to r_ISCO -> M is slow (a power law in 1-a/M) near extremality,
32
+ # so this only checks the qualitative trend, not tight numerical convergence.
33
+ moderate = KerrBlackHole(M=1.0, a=0.9)
34
+ near_extremal = KerrBlackHole(M=1.0, a=0.9999)
35
+ assert moderate.isco_radius(prograde=True) > near_extremal.isco_radius(prograde=True) > 1.0
36
+ assert near_extremal.isco_radius(prograde=True) == pytest.approx(1.0, abs=0.15)
37
+
38
+
39
+ def test_ergosphere_contains_horizon_and_widest_at_equator():
40
+ bh = KerrBlackHole(M=1.0, a=0.8)
41
+ assert bh.ergosphere_radius(np.pi / 2.0) == pytest.approx(2.0)
42
+ assert bh.ergosphere_radius(0.0) == pytest.approx(bh.outer_horizon_radius)
43
+ assert bh.ergosphere_radius(np.pi / 2.0) > bh.outer_horizon_radius
44
+
45
+
46
+ def test_is_inside_ergosphere():
47
+ bh = KerrBlackHole(M=1.0, a=0.8)
48
+ r_mid = (bh.outer_horizon_radius + bh.ergosphere_radius(np.pi / 2.0)) / 2.0
49
+ assert bh.is_inside_ergosphere(r_mid, np.pi / 2.0)
50
+ assert not bh.is_inside_ergosphere(bh.ergosphere_radius(np.pi / 2.0) + 1.0, np.pi / 2.0)
51
+
52
+
53
+ def test_frame_dragging_positive_and_decays_at_large_r():
54
+ bh = KerrBlackHole(M=1.0, a=0.9)
55
+ r = np.array([3.0, 10.0, 100.0, 1000.0])
56
+ omega = bh.frame_dragging_angular_velocity(r)
57
+ assert np.all(omega > 0.0)
58
+ assert np.all(np.diff(omega) < 0.0) # monotonically decreasing with r
59
+ # asymptotic Lense-Thirring rate omega ~ 2Ma/r^3
60
+ assert omega[-1] == pytest.approx(2.0 * bh.M * bh.a / r[-1] ** 3, rel=1e-3)
61
+
62
+
63
+ def test_max_penrose_efficiency_bounds():
64
+ schwarzschild_limit = KerrBlackHole(M=1.0, a=0.0)
65
+ assert schwarzschild_limit.max_penrose_efficiency() == pytest.approx(0.0)
66
+
67
+ extremal = KerrBlackHole(M=1.0, a=0.999999)
68
+ assert extremal.max_penrose_efficiency() == pytest.approx(1.0 - 1.0 / np.sqrt(2.0), abs=1e-3)
69
+
70
+
71
+ def test_penrose_energy_gain():
72
+ bh = KerrBlackHole(M=1.0, a=0.9)
73
+ e_out = bh.penrose_energy_gain(initial_energy=1.0, fragment_energy_infalling=-0.1)
74
+ assert e_out == pytest.approx(1.1)
75
+ with pytest.raises(ValueError):
76
+ bh.penrose_energy_gain(initial_energy=1.0, fragment_energy_infalling=0.1)
77
+
78
+
79
+ def test_circular_orbit_conserved_quantities_reduce_to_schwarzschild():
80
+ bh = KerrBlackHole(M=1.0, a=1e-8)
81
+ E, L = bh.circular_orbit_conserved_quantities(r0=10.0, prograde=True)
82
+ r = 10.0
83
+ # Schwarzschild circular-orbit specific energy and angular momentum
84
+ E_schw = (1.0 - 2.0 / r) / np.sqrt(1.0 - 3.0 / r)
85
+ L_schw = np.sqrt(r) / np.sqrt(1.0 - 3.0 / r)
86
+ assert pytest.approx(E_schw, rel=1e-5) == E
87
+ assert pytest.approx(L_schw, rel=1e-5) == L
88
+
89
+
90
+ def test_keplerian_angular_velocity_reduces_to_schwarzschild_at_zero_spin():
91
+ bh = KerrBlackHole(M=1.0, a=0.0)
92
+ r = 10.0
93
+ assert bh.keplerian_angular_velocity(r, prograde=True) == pytest.approx(np.sqrt(1.0 / r**3))
94
+
95
+
96
+ def test_keplerian_angular_velocity_retrograde_requires_larger_magnitude():
97
+ # Frame dragging effectively supplies "free" angular velocity to a
98
+ # prograde orbiter, so it needs a smaller coordinate Omega to stay on a
99
+ # circular geodesic at the same r; a retrograde orbiter fights the
100
+ # dragging and needs a larger |Omega|.
101
+ bh = KerrBlackHole(M=1.0, a=0.9)
102
+ omega_pro = bh.keplerian_angular_velocity(10.0, prograde=True)
103
+ omega_retro = bh.keplerian_angular_velocity(10.0, prograde=False)
104
+ assert omega_pro > 0.0
105
+ assert omega_retro < 0.0
106
+ assert abs(omega_retro) > omega_pro
107
+
108
+
109
+ def test_kerr_disk_redshift_factor_doppler_asymmetry():
110
+ bh = KerrBlackHole(M=1.0, a=0.7)
111
+ r = 10.0
112
+ g_receding = bh.disk_redshift_factor(r, impact_parameter=-5.0)
113
+ g_none = bh.disk_redshift_factor(r, impact_parameter=0.0)
114
+ g_approaching = bh.disk_redshift_factor(r, impact_parameter=5.0)
115
+ assert g_receding < g_none < g_approaching
116
+ assert g_none < 1.0
@@ -0,0 +1,103 @@
1
+ import numpy as np
2
+ import pytest
3
+
4
+ from physicskit.relativity.core.kerr_raytracer import render_kerr_shadow_image
5
+
6
+
7
+ def test_zero_spin_shadow_matches_schwarzschild_theory():
8
+ outcomes, _hit_radii = render_kerr_shadow_image(
9
+ ny=90,
10
+ nx=90,
11
+ screen_half_width=15.0,
12
+ screen_half_height=15.0,
13
+ r_observer=200.0,
14
+ theta_observer=1.3,
15
+ a=0.0,
16
+ M=1.0,
17
+ r_disk_inner=1.0,
18
+ r_disk_outer=0.0,
19
+ )
20
+ captured_frac = np.mean(outcomes == 1)
21
+ expected = np.pi * (3.0 * np.sqrt(3.0)) ** 2 / (30.0**2)
22
+ assert captured_frac == pytest.approx(expected, rel=0.15)
23
+
24
+
25
+ def test_central_ray_is_captured():
26
+ # a purely radial (alpha=beta=0) ray must plunge straight through the horizon;
27
+ # a 3x3 grid puts the exact center pixel (index [1, 1]) at alpha=beta=0.
28
+ outcomes, _ = render_kerr_shadow_image(
29
+ ny=3,
30
+ nx=3,
31
+ screen_half_width=1.0,
32
+ screen_half_height=1.0,
33
+ r_observer=200.0,
34
+ theta_observer=1.3,
35
+ a=0.7,
36
+ M=1.0,
37
+ r_disk_inner=1.0,
38
+ r_disk_outer=0.0,
39
+ )
40
+ assert outcomes[1, 1] == 1
41
+
42
+
43
+ @pytest.mark.slow
44
+ def test_shadow_area_shrinks_and_shifts_with_spin():
45
+ fractions = []
46
+ shifts = []
47
+ for a in [0.0, 0.5, 0.9]:
48
+ outcomes, _ = render_kerr_shadow_image(
49
+ ny=70,
50
+ nx=70,
51
+ screen_half_width=15.0,
52
+ screen_half_height=15.0,
53
+ r_observer=200.0,
54
+ theta_observer=1.3,
55
+ a=a,
56
+ M=1.0,
57
+ r_disk_inner=1.0,
58
+ r_disk_outer=0.0,
59
+ )
60
+ captured = outcomes == 1
61
+ fractions.append(np.mean(captured))
62
+ cols = np.where(captured.any(axis=0))[0]
63
+ shifts.append((cols.min() + cols.max()) / 2.0 - (outcomes.shape[1] - 1) / 2.0)
64
+
65
+ # known qualitative Kerr shadow behavior: shrinks and shifts toward
66
+ # positive alpha (the frame-dragging direction) as spin increases
67
+ assert fractions[0] > fractions[1] > fractions[2]
68
+ assert shifts[0] < shifts[1] < shifts[2]
69
+
70
+
71
+ def test_disk_hits_stay_within_disk_bounds():
72
+ outcomes, hit_radii = render_kerr_shadow_image(
73
+ ny=90,
74
+ nx=90,
75
+ screen_half_width=20.0,
76
+ screen_half_height=20.0,
77
+ r_observer=200.0,
78
+ theta_observer=1.3,
79
+ a=0.9,
80
+ M=1.0,
81
+ r_disk_inner=2.32,
82
+ r_disk_outer=15.0,
83
+ )
84
+ disk_mask = outcomes == 3
85
+ assert np.any(disk_mask)
86
+ assert np.all(hit_radii[disk_mask] >= 2.32 - 1.0e-6)
87
+ assert np.all(hit_radii[disk_mask] <= 15.0 + 1.0e-6)
88
+
89
+
90
+ def test_all_outcomes_are_valid_codes():
91
+ outcomes, _ = render_kerr_shadow_image(
92
+ ny=30,
93
+ nx=30,
94
+ screen_half_width=15.0,
95
+ screen_half_height=15.0,
96
+ r_observer=200.0,
97
+ theta_observer=1.0,
98
+ a=0.5,
99
+ M=1.0,
100
+ r_disk_inner=1.0,
101
+ r_disk_outer=0.0,
102
+ )
103
+ assert np.all((outcomes >= 0) & (outcomes <= 3))
@@ -0,0 +1,75 @@
1
+ import numpy as np
2
+ import pytest
3
+
4
+ from physicskit.relativity.chapters.lensing import PointMassLens, exact_deflection_angle
5
+ from physicskit.relativity.chapters.schwarzschild import SchwarzschildBlackHole
6
+
7
+
8
+ def test_einstein_ring_at_perfect_alignment():
9
+ lens = PointMassLens(M=1.0, D_L=1000.0, D_S=2000.0)
10
+ theta_plus, theta_minus = lens.image_angles(beta=0.0)
11
+ assert theta_plus == pytest.approx(lens.einstein_angle())
12
+ assert theta_minus == pytest.approx(-lens.einstein_angle())
13
+
14
+
15
+ def test_einstein_angle_formula():
16
+ lens = PointMassLens(M=1.0, D_L=1000.0, D_S=2000.0)
17
+ expected = np.sqrt(4.0 * 1.0 * 1000.0 / (1000.0 * 2000.0))
18
+ assert lens.einstein_angle() == pytest.approx(expected)
19
+
20
+
21
+ def test_images_straddle_the_lens_for_any_offset():
22
+ lens = PointMassLens(M=1.0, D_L=1000.0, D_S=2000.0)
23
+ theta_plus, theta_minus = lens.image_angles(beta=0.01)
24
+ assert theta_plus > 0.0
25
+ assert theta_minus < 0.0
26
+
27
+
28
+ def test_primary_image_approaches_source_position_far_from_lens():
29
+ lens = PointMassLens(M=1.0, D_L=1000.0, D_S=2000.0)
30
+ beta = 10.0 * lens.einstein_angle()
31
+ theta_plus, _ = lens.image_angles(beta)
32
+ assert theta_plus == pytest.approx(beta, rel=0.05)
33
+
34
+
35
+ def test_magnification_diverges_toward_perfect_alignment():
36
+ lens = PointMassLens(M=1.0, D_L=1000.0, D_S=2000.0)
37
+ theta_E = lens.einstein_angle()
38
+ _, _, total_far = lens.magnification(1.0 * theta_E)
39
+ _, _, total_near = lens.magnification(0.01 * theta_E)
40
+ assert total_near > total_far > 1.0
41
+
42
+
43
+ def test_magnification_canonical_value_at_u_equals_one():
44
+ lens = PointMassLens(M=1.0, D_L=1000.0, D_S=2000.0)
45
+ theta_E = lens.einstein_angle()
46
+ _, _, total = lens.magnification(theta_E)
47
+ assert total == pytest.approx(1.34164, abs=1.0e-4)
48
+
49
+
50
+ def test_invalid_distances_raise():
51
+ with pytest.raises(ValueError):
52
+ PointMassLens(M=1.0, D_L=2000.0, D_S=1000.0)
53
+
54
+
55
+ def test_exact_deflection_matches_weak_field_for_large_impact_parameter():
56
+ # A finite r_far leaves a small residual bias (the flat-space "straight
57
+ # line" total angle swept between two finite radii isn't exactly pi;
58
+ # see the light-bending example script), so allow a generous tolerance
59
+ # rather than an unrealistically tight one.
60
+ bh = SchwarzschildBlackHole(M=1.0)
61
+ b = 50.0
62
+ exact = exact_deflection_angle(bh, b)
63
+ weak = bh.light_deflection_angle(b)
64
+ assert exact == pytest.approx(weak, rel=0.1)
65
+
66
+
67
+ def test_exact_deflection_exceeds_weak_field_closer_to_the_photon_sphere():
68
+ # n_steps is increased (at fixed default r_far) so the ray has enough
69
+ # affine-parameter budget to complete the round trip out to r_far; see
70
+ # exact_deflection_angle's default dtau = 3*r_far/n_steps.
71
+ bh = SchwarzschildBlackHole(M=1.0)
72
+ exact_far = exact_deflection_angle(bh, 200.0, n_steps=600000)
73
+ exact_near = exact_deflection_angle(bh, 15.0, n_steps=600000)
74
+ assert exact_near > bh.light_deflection_angle(15.0)
75
+ assert exact_near > exact_far
@@ -0,0 +1,48 @@
1
+ import numpy as np
2
+ import pytest
3
+
4
+ from physicskit.relativity.chapters.neutron_star import NeutronStar
5
+
6
+
7
+ def test_solve_gives_positive_mass_and_radius():
8
+ star = NeutronStar(K=100.0, Gamma=2.0)
9
+ M, R, sol = star.solve(central_density=1.28e-3)
10
+ assert M > 0.0
11
+ assert R > 0.0
12
+ assert sol.success
13
+
14
+
15
+ def test_known_k100_gamma2_benchmark():
16
+ # Standard numerical-relativity textbook benchmark (e.g. Baumgarte &
17
+ # Shapiro): a K=100, Gamma=2 polytrope has a maximum mass of
18
+ # approximately 1.6-1.7 (geometrized units matching this convention),
19
+ # near a central density of a few x 1e-3.
20
+ star = NeutronStar(K=100.0, Gamma=2.0)
21
+ M, R, _ = star.solve(central_density=1.28e-3)
22
+ assert pytest.approx(1.4, abs=0.1) == M
23
+ assert pytest.approx(9.6, abs=1.0) == R
24
+
25
+
26
+ def test_mass_radius_curve_has_a_maximum_mass_turning_point():
27
+ star = NeutronStar(K=100.0, Gamma=2.0)
28
+ central_densities = np.logspace(-3.3, -2.2, 14)
29
+ masses, radii = star.mass_radius_curve(central_densities)
30
+ assert np.all(masses > 0.0)
31
+ assert np.all(radii > 0.0)
32
+ # radius should decrease monotonically as central density increases
33
+ # (denser, more compact stars)
34
+ assert np.all(np.diff(radii) < 0.0)
35
+ # a genuine maximum mass exists strictly inside the scanned range
36
+ assert 0 < np.argmax(masses) < len(masses) - 1
37
+
38
+
39
+ def test_higher_central_density_gives_smaller_radius():
40
+ star = NeutronStar(K=100.0, Gamma=2.0)
41
+ _M_low, R_low, _ = star.solve(central_density=5.0e-4)
42
+ _M_high, R_high, _ = star.solve(central_density=1.0e-3)
43
+ assert R_high < R_low
44
+
45
+
46
+ def test_energy_density_reduces_to_rest_mass_at_zero_pressure():
47
+ star = NeutronStar(K=100.0, Gamma=2.0)
48
+ assert star.energy_density(0.0) == pytest.approx(0.0)
@@ -0,0 +1,67 @@
1
+ import numpy as np
2
+ import pytest
3
+
4
+ from physicskit.relativity.core.raytracer import OUTCOME_CAPTURED, OUTCOME_DISK, OUTCOME_ESCAPED, camera_basis
5
+ from physicskit.relativity.visualizers.shadow_render import render_black_hole_image
6
+
7
+
8
+ def test_camera_basis_is_orthonormal():
9
+ view_dir, right, up = camera_basis(inclination=1.2)
10
+ for v in (view_dir, right, up):
11
+ assert np.linalg.norm(v) == pytest.approx(1.0)
12
+ assert np.dot(view_dir, right) == pytest.approx(0.0, abs=1e-10)
13
+ assert np.dot(view_dir, up) == pytest.approx(0.0, abs=1e-10)
14
+ assert np.dot(right, up) == pytest.approx(0.0, abs=1e-10)
15
+
16
+
17
+ def test_shadow_fraction_matches_critical_impact_parameter_without_disk():
18
+ M = 1.0
19
+ screen_half_width = 15.0
20
+ result = render_black_hole_image(
21
+ M=M,
22
+ ny=120,
23
+ nx=120,
24
+ screen_half_width=screen_half_width,
25
+ r_disk_inner=1.0,
26
+ r_disk_outer=0.0,
27
+ n_steps=2000,
28
+ )
29
+ outcomes = result["outcomes"]
30
+ captured_fraction = np.mean(outcomes == OUTCOME_CAPTURED)
31
+ b_crit = 3.0 * np.sqrt(3.0) * M
32
+ expected_fraction = np.pi * b_crit**2 / (2.0 * screen_half_width) ** 2
33
+ assert captured_fraction == pytest.approx(expected_fraction, rel=0.15)
34
+
35
+
36
+ def test_all_pixels_produce_a_valid_outcome():
37
+ result = render_black_hole_image(M=1.0, ny=30, nx=30, n_steps=1500)
38
+ outcomes = result["outcomes"]
39
+ assert np.all(outcomes >= 0)
40
+ assert np.all(outcomes <= 3)
41
+
42
+
43
+ def test_disk_pixels_report_radius_within_disk_bounds():
44
+ result = render_black_hole_image(M=1.0, ny=80, nx=80, r_disk_inner=6.0, r_disk_outer=20.0, n_steps=2000)
45
+ outcomes = result["outcomes"]
46
+ hit_radii = result["hit_radii"]
47
+ disk_mask = outcomes == OUTCOME_DISK
48
+ assert np.any(disk_mask)
49
+ assert np.all(hit_radii[disk_mask] >= 6.0 - 1e-6)
50
+ assert np.all(hit_radii[disk_mask] <= 20.0 + 1e-6)
51
+
52
+
53
+ def test_face_on_view_is_rotationally_symmetric_without_disk():
54
+ # at inclination 0, the (diskless) shadow is a circle around the image center
55
+ result = render_black_hole_image(M=1.0, ny=61, nx=61, inclination=1e-6, r_disk_inner=1.0, r_disk_outer=0.0, n_steps=1500)
56
+ outcomes = result["outcomes"]
57
+ captured = outcomes == OUTCOME_CAPTURED
58
+ # symmetric under a vertical flip for a face-on, non-tilted view
59
+ assert np.array_equal(captured, captured[::-1, :]) or np.mean(captured == captured[::-1, :]) > 0.9
60
+
61
+
62
+ def test_escaped_pixels_are_far_from_center_pixels_dominated_by_capture():
63
+ result = render_black_hole_image(M=1.0, ny=41, nx=41, r_disk_inner=1.0, r_disk_outer=0.0, n_steps=1500)
64
+ outcomes = result["outcomes"]
65
+ center = outcomes.shape[0] // 2
66
+ assert outcomes[center, center] == OUTCOME_CAPTURED
67
+ assert outcomes[0, 0] == OUTCOME_ESCAPED