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,126 @@
1
+ import numpy as np
2
+ import pytest
3
+
4
+ from physicskit.relativity.chapters.schwarzschild import SchwarzschildBlackHole
5
+
6
+
7
+ def test_key_radii():
8
+ bh = SchwarzschildBlackHole(M=2.0)
9
+ assert bh.horizon_radius == pytest.approx(4.0)
10
+ assert bh.photon_sphere_radius == pytest.approx(6.0)
11
+ assert bh.isco_radius == pytest.approx(12.0)
12
+ assert bh.critical_impact_parameter == pytest.approx(2.0 * 3.0 * np.sqrt(3.0))
13
+
14
+
15
+ def test_circular_orbit_angular_velocity_is_keplerian():
16
+ bh = SchwarzschildBlackHole(M=1.0)
17
+ r = 100.0
18
+ assert bh.circular_orbit_angular_velocity(r) == pytest.approx(np.sqrt(1.0 / r**3))
19
+
20
+
21
+ def test_circular_orbit_stays_at_constant_radius():
22
+ bh = SchwarzschildBlackHole(M=1.0)
23
+ y0 = bh.circular_orbit_initial_state(r0=10.0)
24
+ traj = bh.integrate_geodesic(y0, dtau=0.02, n_steps=2000)
25
+ assert np.allclose(traj["r"], 10.0, atol=1e-6)
26
+
27
+
28
+ def test_light_deflection_converges_to_weak_field_formula():
29
+ bh = SchwarzschildBlackHole(M=1.0)
30
+ b = 50.0
31
+ y0 = bh.null_geodesic_initial_state(r0=2.0e5, impact_parameter=b, ingoing=True)
32
+ traj = bh.integrate_geodesic(y0, dtau=2.0, n_steps=300000)
33
+ measured = (traj["phi"][-1] - traj["phi"][0]) - np.pi
34
+ assert measured == pytest.approx(bh.light_deflection_angle(b), rel=0.1)
35
+
36
+
37
+ def test_light_capture_near_photon_sphere():
38
+ bh = SchwarzschildBlackHole(M=1.0)
39
+ # impact parameter well below the critical value: photon should plunge in
40
+ y0 = bh.null_geodesic_initial_state(r0=100.0, impact_parameter=3.0, ingoing=True)
41
+ traj = bh.integrate_geodesic(y0, dtau=0.05, n_steps=20000)
42
+ assert traj["r"].min() < bh.horizon_radius * 1.05
43
+
44
+
45
+ def test_light_escape_above_critical_impact_parameter():
46
+ bh = SchwarzschildBlackHole(M=1.0)
47
+ y0 = bh.null_geodesic_initial_state(r0=1000.0, impact_parameter=30.0, ingoing=True)
48
+ traj = bh.integrate_geodesic(y0, dtau=0.5, n_steps=20000)
49
+ assert traj["r"].min() > bh.photon_sphere_radius
50
+ assert traj["r"][-1] > traj["r"].min()
51
+
52
+
53
+ def test_perihelion_precession_is_empty_for_too_short_a_trajectory():
54
+ bh = SchwarzschildBlackHole(M=1.0)
55
+ y0 = bh.eccentric_orbit_initial_state(r0=100.0, eccentricity_boost=0.03)
56
+ traj = bh.integrate_geodesic(y0, dtau=0.05, n_steps=50) # fewer than 2 periapsis passages
57
+ prec = bh.perihelion_precession(traj)
58
+ assert prec.shape == (0,)
59
+
60
+
61
+ def test_weak_field_precession_converges_for_wide_low_eccentricity_orbit():
62
+ bh = SchwarzschildBlackHole(M=1.0)
63
+ y0 = bh.eccentric_orbit_initial_state(r0=100.0, eccentricity_boost=0.03)
64
+ traj = bh.integrate_geodesic(y0, dtau=0.05, n_steps=300000)
65
+ prec = bh.perihelion_precession(traj)
66
+ assert len(prec) > 0
67
+ e_est = (traj["r"].max() - traj["r"].min()) / (traj["r"].max() + traj["r"].min())
68
+ a_semi = (traj["r"].max() + traj["r"].min()) / 2.0
69
+ weak_field = bh.weak_field_precession_per_orbit(a_semi, e_est)
70
+ assert np.mean(prec) == pytest.approx(weak_field, rel=0.15)
71
+ assert np.mean(prec) > 0.0 # perihelion precesses forward, as GR predicts
72
+
73
+
74
+ def test_shapiro_delay_positive_and_increases_with_smaller_impact_parameter():
75
+ bh = SchwarzschildBlackHole(M=1.0)
76
+ delay_far = bh.shapiro_delay(r1=1000.0, r2=1000.0, impact_parameter=100.0)
77
+ delay_close = bh.shapiro_delay(r1=1000.0, r2=1000.0, impact_parameter=10.0)
78
+ assert delay_far > 0.0
79
+ assert delay_close > delay_far
80
+
81
+
82
+ def test_effective_potential_has_stable_and_unstable_extrema_above_isco_momentum():
83
+ bh = SchwarzschildBlackHole(M=1.0)
84
+ # for L modestly above the ISCO value L^2=12M^2, V_eff should show both an
85
+ # unstable maximum (between the photon sphere and the ISCO) and a stable
86
+ # minimum (outside the ISCO) -- the two circular-orbit branches merging
87
+ # into the single ISCO inflection as L -> sqrt(12)*M from above.
88
+ L = np.sqrt(12.0) * 1.05
89
+ r = np.linspace(3.5, 20.0, 2000)
90
+ V = bh.effective_potential(r, L)
91
+ idx_max = np.argmax(V[r < 6.0])
92
+ idx_min = np.argmin(V[r > 6.0])
93
+ r_unstable = r[r < 6.0][idx_max]
94
+ r_stable = r[r > 6.0][idx_min]
95
+ assert bh.photon_sphere_radius < r_unstable < bh.isco_radius
96
+ assert r_stable > bh.isco_radius
97
+
98
+
99
+ def test_disk_redshift_factor_doppler_asymmetry():
100
+ bh = SchwarzschildBlackHole(M=1.0)
101
+ r = 10.0
102
+ g_receding = bh.disk_redshift_factor(r, impact_parameter=-5.0)
103
+ g_none = bh.disk_redshift_factor(r, impact_parameter=0.0)
104
+ g_approaching = bh.disk_redshift_factor(r, impact_parameter=5.0)
105
+ assert g_receding < g_none < g_approaching
106
+ assert g_none < 1.0 # b=0: purely gravitational + orbital time dilation, no Doppler boost
107
+
108
+
109
+ def test_disk_redshift_factor_approaches_one_far_from_the_black_hole():
110
+ bh = SchwarzschildBlackHole(M=1.0)
111
+ assert bh.disk_redshift_factor(1.0e6, impact_parameter=0.0) == pytest.approx(1.0, abs=1.0e-4)
112
+
113
+
114
+ def test_tidal_acceleration_stretches_radially_compresses_transversely():
115
+ bh = SchwarzschildBlackHole(M=1.0)
116
+ radial, transverse = bh.tidal_acceleration(r=6.0, proper_separation=1.0)
117
+ assert radial > 0.0
118
+ assert transverse < 0.0
119
+ assert radial == pytest.approx(-2.0 * transverse) # 2M/r^3 vs -M/r^3: exactly a factor of 2
120
+
121
+
122
+ def test_tidal_acceleration_grows_closer_to_the_black_hole():
123
+ bh = SchwarzschildBlackHole(M=1.0)
124
+ radial_far, _ = bh.tidal_acceleration(r=20.0, proper_separation=1.0)
125
+ radial_near, _ = bh.tidal_acceleration(r=4.0, proper_separation=1.0)
126
+ assert radial_near > radial_far
@@ -0,0 +1,81 @@
1
+ import numpy as np
2
+ import pytest
3
+
4
+ from physicskit.relativity.core.raytracer import OUTCOME_CAPTURED, OUTCOME_DISK
5
+ from physicskit.relativity.visualizers.shadow_render import (
6
+ _redshift_factor,
7
+ plot_black_hole_shadow,
8
+ render_black_hole_image,
9
+ )
10
+
11
+
12
+ def test_a_zero_uses_schwarzschild_backend_and_a_positive_uses_kerr():
13
+ result_s = render_black_hole_image(M=1.0, a=0.0, ny=40, nx=40)
14
+ result_k = render_black_hole_image(M=1.0, a=0.5, ny=40, nx=40)
15
+ assert result_s["a"] == 0.0
16
+ assert result_k["a"] == 0.5
17
+ assert result_s["outcomes"].shape == (40, 40)
18
+ assert result_k["outcomes"].shape == (40, 40)
19
+
20
+
21
+ def test_default_disk_inner_radius_is_isco():
22
+ from physicskit.relativity.chapters.kerr import KerrBlackHole
23
+
24
+ result_s = render_black_hole_image(M=1.0, a=0.0, ny=20, nx=20)
25
+ assert result_s["r_disk_inner"] == pytest.approx(6.0)
26
+
27
+ result_k = render_black_hole_image(M=1.0, a=0.9, ny=20, nx=20)
28
+ expected = KerrBlackHole(M=1.0, a=0.9).isco_radius(prograde=True)
29
+ assert result_k["r_disk_inner"] == pytest.approx(expected)
30
+
31
+
32
+ def test_L_phi_grid_is_linear_in_alpha_and_antisymmetric():
33
+ # At theta_o=pi/2 (edge-on), L_phi = z-component of (ray_origin x view_dir)
34
+ # is exactly proportional to alpha (verified: coefficient +1 for the
35
+ # Schwarzschild camera_basis convention, which need not match the sign
36
+ # convention of the independent Kerr (alpha,beta)->(L,Q) parametrization
37
+ # -- each is internally self-consistent, but the two are not required to
38
+ # agree in handedness).
39
+ result = render_black_hole_image(M=1.0, a=0.0, ny=21, nx=21, inclination=np.pi / 2.0, screen_half_width=10.0)
40
+ alpha_row = np.linspace(-10.0, 10.0, 21)
41
+ L_phi_row = result["L_phi"][10, :]
42
+ assert np.allclose(L_phi_row, alpha_row, atol=1.0e-8) or np.allclose(L_phi_row, -alpha_row, atol=1.0e-8)
43
+ assert L_phi_row[0] == pytest.approx(-L_phi_row[-1])
44
+
45
+
46
+ def test_redshift_factor_is_finite_and_asymmetric():
47
+ result = render_black_hole_image(M=1.0, a=0.0, ny=80, nx=80, r_disk_inner=6.0, r_disk_outer=20.0)
48
+ disk_mask = result["outcomes"] == OUTCOME_DISK
49
+ assert np.any(disk_mask)
50
+ g = _redshift_factor(result, disk_mask)
51
+ assert np.all(np.isfinite(g[disk_mask]))
52
+ assert np.all(g[disk_mask] > 0.0)
53
+
54
+ nx = result["outcomes"].shape[1]
55
+ left_mask = disk_mask.copy()
56
+ left_mask[:, nx // 2 :] = False
57
+ right_mask = disk_mask.copy()
58
+ right_mask[:, : nx // 2] = False
59
+ assert g[left_mask].mean() != pytest.approx(g[right_mask].mean(), rel=1.0e-3)
60
+
61
+
62
+ def test_plot_black_hole_shadow_runs_with_and_without_redshift():
63
+ import matplotlib
64
+
65
+ matplotlib.use("Agg")
66
+ result = render_black_hole_image(M=1.0, a=0.0, ny=30, nx=30)
67
+ ax1 = plot_black_hole_shadow(result, redshift=False)
68
+ ax2 = plot_black_hole_shadow(result, redshift=True)
69
+ assert ax1 is not None
70
+ assert ax2 is not None
71
+
72
+
73
+ def test_captured_pixels_are_black_regardless_of_redshift_option():
74
+ import matplotlib
75
+
76
+ matplotlib.use("Agg")
77
+ result = render_black_hole_image(M=1.0, a=0.0, ny=40, nx=40, r_disk_inner=1.0, r_disk_outer=0.0)
78
+ ax = plot_black_hole_shadow(result, redshift=True)
79
+ image = ax.images[0].get_array()
80
+ captured_mask = result["outcomes"] == OUTCOME_CAPTURED
81
+ assert np.allclose(np.asarray(image)[captured_mask][:, :3], 0.0)
@@ -0,0 +1,86 @@
1
+ import numpy as np
2
+ import pytest
3
+
4
+ from physicskit.relativity.core.tensors import (
5
+ alcubierre_metric,
6
+ christoffel_symbols,
7
+ einstein_tensor,
8
+ flrw_metric,
9
+ kerr_metric_bl,
10
+ reissner_nordstrom_metric,
11
+ ricci_scalar,
12
+ schwarzschild_metric,
13
+ )
14
+
15
+
16
+ def test_schwarzschild_metric_signature_and_symmetry():
17
+ coords = np.array([0.0, 10.0, np.pi / 2.0, 0.3])
18
+ g = schwarzschild_metric(coords, M=1.0)
19
+ assert g.shape == (4, 4)
20
+ assert np.allclose(g, g.T)
21
+ assert g[0, 0] < 0.0 # timelike direction
22
+ assert g[1, 1] > 0.0 and g[2, 2] > 0.0 and g[3, 3] > 0.0
23
+
24
+
25
+ def test_kerr_reduces_to_schwarzschild_at_zero_spin():
26
+ coords = np.array([0.0, 10.0, 1.1, 0.3])
27
+ g_kerr = kerr_metric_bl(coords, M=1.0, a=0.0)
28
+ g_schw = schwarzschild_metric(coords, M=1.0)
29
+ assert np.allclose(g_kerr, g_schw, atol=1e-10)
30
+
31
+
32
+ def test_kerr_metric_has_frame_dragging_cross_term():
33
+ coords = np.array([0.0, 10.0, np.pi / 2.0, 0.0])
34
+ g = kerr_metric_bl(coords, M=1.0, a=0.9)
35
+ assert g[0, 3] != 0.0
36
+ assert g[0, 3] == pytest.approx(g[3, 0])
37
+
38
+
39
+ def test_reissner_nordstrom_reduces_to_schwarzschild_at_zero_charge():
40
+ coords = np.array([0.0, 10.0, np.pi / 2.0, 0.3])
41
+ g_rn = reissner_nordstrom_metric(coords, M=1.0, Q=0.0)
42
+ g_schw = schwarzschild_metric(coords, M=1.0)
43
+ assert np.allclose(g_rn, g_schw)
44
+
45
+
46
+ def test_flrw_metric_flat_matter_dominated():
47
+ a_func = lambda t: (1.0 + t) ** (2.0 / 3.0)
48
+ coords = np.array([0.0, 1.0, np.pi / 2.0, 0.0])
49
+ g = flrw_metric(coords, a_func, k=0.0)
50
+ assert g[0, 0] == pytest.approx(-1.0)
51
+ assert g[1, 1] == pytest.approx(a_func(0.0) ** 2)
52
+
53
+
54
+ def test_flrw_metric_closed_and_open_use_sin_and_sinh_of_chi():
55
+ a_func = lambda t: 1.0
56
+ coords = np.array([0.0, 0.5, np.pi / 2.0, 0.0])
57
+ g_closed = flrw_metric(coords, a_func, k=1.0)
58
+ g_open = flrw_metric(coords, a_func, k=-1.0)
59
+ assert g_closed[2, 2] == pytest.approx(a_func(0.0) ** 2 * np.sin(0.5) ** 2)
60
+ assert g_open[2, 2] == pytest.approx(a_func(0.0) ** 2 * np.sinh(0.5) ** 2)
61
+
62
+
63
+ def test_alcubierre_metric_is_flat_far_from_the_bubble():
64
+ coords = np.array([0.0, 1000.0, 1000.0, 1000.0])
65
+ g = alcubierre_metric(coords, v_s=2.0, sigma=8.0, R=1.0)
66
+ minkowski = np.diag([-1.0, 1.0, 1.0, 1.0])
67
+ assert np.allclose(g, minkowski, atol=1e-6)
68
+
69
+
70
+ def test_christoffel_symbols_are_symmetric_in_lower_indices():
71
+ coords = np.array([0.0, 10.0, np.pi / 2.0, 0.0])
72
+ Gamma = christoffel_symbols(schwarzschild_metric, coords, {"M": 1.0})
73
+ assert Gamma.shape == (4, 4, 4)
74
+ assert np.allclose(Gamma, np.transpose(Gamma, (0, 2, 1)), atol=1e-8)
75
+
76
+
77
+ def test_schwarzschild_vacuum_einstein_tensor_is_zero():
78
+ coords = np.array([0.0, 10.0, np.pi / 2.0, 0.3])
79
+ G = einstein_tensor(schwarzschild_metric, coords, {"M": 1.0})
80
+ assert np.max(np.abs(G)) < 1e-2
81
+
82
+
83
+ def test_schwarzschild_ricci_scalar_is_zero():
84
+ coords = np.array([0.0, 8.0, 1.2, 0.0])
85
+ R = ricci_scalar(schwarzschild_metric, coords, {"M": 1.0})
86
+ assert abs(R) < 1e-2
@@ -0,0 +1,51 @@
1
+ import pytest
2
+
3
+ from physicskit.relativity.chapters.timekeeping import (
4
+ EARTH_RADIUS_M,
5
+ GPS_ORBITAL_RADIUS_M,
6
+ clock_rate_factor,
7
+ earth_mass_geometrized,
8
+ gps_relativistic_offset_per_day,
9
+ )
10
+
11
+
12
+ def test_earth_mass_geometrized_is_a_few_millimeters():
13
+ # GM_earth/c^2 is about 4.4 mm
14
+ M = earth_mass_geometrized()
15
+ assert pytest.approx(0.004435, abs=0.0002) == M
16
+
17
+
18
+ def test_clock_rate_factor_defaults_m_to_earth_mass_geometrized():
19
+ rate_default = clock_rate_factor(EARTH_RADIUS_M, angular_velocity_si=0.0)
20
+ rate_explicit = clock_rate_factor(EARTH_RADIUS_M, angular_velocity_si=0.0, M=earth_mass_geometrized())
21
+ assert rate_default == pytest.approx(rate_explicit)
22
+
23
+
24
+ def test_clock_rate_factor_static_is_pure_gravitational_redshift():
25
+ M = earth_mass_geometrized()
26
+ rate = clock_rate_factor(EARTH_RADIUS_M, angular_velocity_si=0.0, M=M)
27
+ expected = (1.0 - 2.0 * M / EARTH_RADIUS_M) ** 0.5
28
+ assert rate == pytest.approx(expected)
29
+
30
+
31
+ def test_clock_rate_factor_less_than_one_and_decreases_with_rotation():
32
+ M = earth_mass_geometrized()
33
+ rate_static = clock_rate_factor(GPS_ORBITAL_RADIUS_M, 0.0, M=M)
34
+ rate_rotating = clock_rate_factor(GPS_ORBITAL_RADIUS_M, angular_velocity_si=1.0e-4, M=M)
35
+ assert rate_static < 1.0
36
+ assert rate_rotating < rate_static
37
+
38
+
39
+ def test_gps_offset_matches_the_well_known_38_microseconds_per_day():
40
+ offset = gps_relativistic_offset_per_day()
41
+ assert offset == pytest.approx(38.6, abs=1.0)
42
+
43
+
44
+ def test_gps_offset_is_positive_satellite_runs_fast():
45
+ assert gps_relativistic_offset_per_day() > 0.0
46
+
47
+
48
+ def test_higher_orbit_increases_the_gravitational_advantage():
49
+ offset_low = gps_relativistic_offset_per_day(r_satellite=1.0e7)
50
+ offset_high = gps_relativistic_offset_per_day(r_satellite=4.0e7)
51
+ assert offset_high > offset_low
@@ -0,0 +1,115 @@
1
+ """Coverage for previously entirely untested plotting/interactive modules in
2
+ physicskit.relativity.visualizers: spacetime_3d, spacetime_diagrams,
3
+ wave_plots, interactive.interactive_shadow_image, and
4
+ shadow_render.animate_shadow_spin_sweep. All built/drawn directly without
5
+ GIF-saving, so fast except where noted @pytest.mark.slow (ray tracing)."""
6
+
7
+ from __future__ import annotations
8
+
9
+ import matplotlib.pyplot as plt
10
+ import numpy as np
11
+ import pytest
12
+ from matplotlib.axes import Axes
13
+
14
+ from physicskit.relativity.chapters.gw_merger import BinaryMerger
15
+ from physicskit.relativity.visualizers.interactive import interactive_shadow_image
16
+ from physicskit.relativity.visualizers.shadow_render import animate_shadow_spin_sweep, render_black_hole_image
17
+ from physicskit.relativity.visualizers.spacetime_3d import flamm_paraboloid, plot_flamm_paraboloid
18
+ from physicskit.relativity.visualizers.spacetime_diagrams import (
19
+ kruskal_coordinates,
20
+ penrose_carter_coordinates,
21
+ plot_kruskal_diagram,
22
+ plot_penrose_diagram,
23
+ )
24
+ from physicskit.relativity.visualizers.wave_plots import animate_wave_ripple, plot_strain_waveform, plot_wave_ripple
25
+
26
+
27
+ def test_flamm_paraboloid_and_plot_given_and_default_ax():
28
+ M = 1.0
29
+ X, Y, Z = flamm_paraboloid(M, n_r=10, n_phi=10)
30
+ assert X.shape == Y.shape == Z.shape == (10, 10)
31
+ assert np.all(Z >= 0.0)
32
+
33
+ ax0 = plot_flamm_paraboloid(M)
34
+ assert isinstance(ax0, Axes)
35
+
36
+ fig, ax = plt.subplots(subplot_kw={"projection": "3d"})
37
+ ax_given = plot_flamm_paraboloid(M, ax=ax)
38
+ assert ax_given is ax
39
+
40
+
41
+ def test_kruskal_and_penrose_coordinates_map_horizon_to_light_cone():
42
+ M = 1.0
43
+ X, T = kruskal_coordinates(t=0.0, r=2.0 * M, M=M)
44
+ assert X == pytest.approx(0.0, abs=1e-8)
45
+ assert T == pytest.approx(0.0, abs=1e-8)
46
+
47
+ space, time = penrose_carter_coordinates(t=0.0, r=2.0 * M, M=M)
48
+ assert space == pytest.approx(0.0, abs=1e-8)
49
+ assert time == pytest.approx(0.0, abs=1e-8)
50
+
51
+
52
+ def test_plot_kruskal_diagram_default_and_given_ax():
53
+ ax0 = plot_kruskal_diagram(M=1.0)
54
+ assert isinstance(ax0, Axes)
55
+
56
+ fig, ax = plt.subplots()
57
+ ax_given = plot_kruskal_diagram(M=1.0, ax=ax)
58
+ assert ax_given is ax
59
+
60
+
61
+ def test_plot_penrose_diagram_default_and_given_ax():
62
+ ax0 = plot_penrose_diagram(M=1.0)
63
+ assert isinstance(ax0, Axes)
64
+
65
+ fig, ax = plt.subplots()
66
+ ax_given = plot_penrose_diagram(M=1.0, ax=ax)
67
+ assert ax_given is ax
68
+
69
+
70
+ def test_plot_strain_waveform_default_ax_with_cross_polarization_and_merger_marker():
71
+ t = np.linspace(-10, 10, 50)
72
+ h_plus = np.sin(t)
73
+ h_cross = np.cos(t)
74
+ ax = plot_strain_waveform(t, h_plus, h_cross=h_cross, t_merger=0.0)
75
+ assert isinstance(ax, Axes)
76
+ assert len(ax.lines) == 3 # h_plus, h_cross, merger marker
77
+
78
+
79
+ def test_plot_wave_ripple_default_and_given_ax():
80
+ merger = BinaryMerger(m1=30.0, m2=30.0, distance=1000.0)
81
+ ax0 = plot_wave_ripple(merger, t=0.0, t_merger=0.0, grid_size=20)
82
+ assert isinstance(ax0, Axes)
83
+
84
+ fig, ax = plt.subplots()
85
+ ax_given = plot_wave_ripple(merger, t=0.0, t_merger=0.0, ax=ax, grid_size=20)
86
+ assert ax_given is ax
87
+
88
+
89
+ def test_animate_wave_ripple_builds_and_draws_a_frame():
90
+ merger = BinaryMerger(m1=30.0, m2=30.0, distance=1000.0)
91
+ t_values = np.linspace(-5.0, 5.0, 3)
92
+ anim = animate_wave_ripple(merger, t_merger=0.0, t_values=t_values, grid_size=15)
93
+ anim._draw_frame(1)
94
+ assert anim._fig.axes[0].get_title() == f"t={t_values[1]:.1f}"
95
+
96
+
97
+ def test_interactive_shadow_image_returns_heatmap_figure():
98
+ result = render_black_hole_image(M=1.0, ny=10, nx=10)
99
+ fig = interactive_shadow_image(result)
100
+ assert fig.data[0].type == "heatmap"
101
+
102
+
103
+ @pytest.mark.slow
104
+ def test_animate_shadow_spin_sweep_builds_and_draws_a_frame():
105
+ a_values = np.array([0.0, 0.3])
106
+ anim = animate_shadow_spin_sweep(M=1.0, a_values=a_values, ny=10, nx=10)
107
+ anim._draw_frame(1)
108
+ assert f"a = {a_values[1]:.2f}" in anim._fig.axes[0].get_title()
109
+
110
+
111
+ @pytest.mark.slow
112
+ def test_animate_shadow_spin_sweep_default_a_values():
113
+ anim = animate_shadow_spin_sweep(M=1.0, ny=6, nx=6)
114
+ anim._draw_frame(0)
115
+ assert anim._fig.axes[0].get_title().startswith("a = 0.00")
@@ -0,0 +1 @@
1
+ """Physical constants and the geometrized (G=c=1) unit system."""
@@ -0,0 +1,232 @@
1
+ """Physical constants and the geometrized unit system used throughout physicskit.relativity.
2
+
3
+ Every calculation in this package is done in **geometrized units**
4
+ (:math:`G = c = 1`), the standard convention in numerical relativity. In
5
+ these units, mass, length, and time all share the same dimension: a mass
6
+ :math:`M` (in kilograms) corresponds to a length
7
+ :math:`GM/c^2` and a time :math:`GM/c^3`. Concretely, a Schwarzschild black
8
+ hole of mass :math:`M` (in geometrized length units) has its horizon at
9
+ :math:`r = 2M`, its photon sphere at :math:`r = 3M`, and its innermost
10
+ stable circular orbit at :math:`r = 6M` -- clean numbers that would carry
11
+ awkward factors of :math:`G` and :math:`c` in SI units.
12
+
13
+ Use :func:`solar_masses_to_geometrized` to convert a physical mass (in solar
14
+ masses) into this geometrized length, and :func:`geometrized_to_seconds` /
15
+ :func:`geometrized_to_meters` to convert results back to physical units for
16
+ reporting.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import warnings
22
+
23
+ import numpy as np
24
+
25
+ from physicskit import constants as _const
26
+
27
+ __all__ = [
28
+ "C_SI",
29
+ "G_SI",
30
+ "PARSEC_M",
31
+ "SOLAR_MASS_KG",
32
+ "check_geometrized_distance",
33
+ "check_geometrized_mass",
34
+ "geometrized_to_meters",
35
+ "geometrized_to_seconds",
36
+ "geometrized_to_solar_masses",
37
+ "seconds_to_geometrized",
38
+ "solar_masses_to_geometrized",
39
+ ]
40
+
41
+ #: Newton's gravitational constant, in m^3 kg^-1 s^-2.
42
+ G_SI = _const.G
43
+
44
+ #: Speed of light in vacuum, in m/s.
45
+ C_SI = _const.C
46
+
47
+ #: Solar mass, in kg.
48
+ SOLAR_MASS_KG = _const.SOLAR_MASS_KG
49
+
50
+ #: One parsec, in meters.
51
+ PARSEC_M = _const.PARSEC_M
52
+
53
+
54
+ def solar_masses_to_geometrized(mass_solar):
55
+ """Convert a mass in solar masses to geometrized length units (meters).
56
+
57
+ .. math::
58
+
59
+ M_{\\text{geom}} = \\frac{G M_\\odot}{c^2} \\times \\left(\\frac{M}{M_\\odot}\\right)
60
+
61
+ Parameters
62
+ ----------
63
+ mass_solar : float or array_like
64
+ Mass in units of the Sun's mass.
65
+
66
+ Returns
67
+ -------
68
+ float or ndarray
69
+ Mass expressed as a geometrized length, in meters. For example,
70
+ this is the horizon radius (up to the factor of 2) of a
71
+ Schwarzschild black hole of that mass.
72
+
73
+ Examples
74
+ --------
75
+ >>> round(float(solar_masses_to_geometrized(1.0)), 2)
76
+ 1476.67
77
+ """
78
+ return np.asarray(mass_solar) * G_SI * SOLAR_MASS_KG / C_SI**2
79
+
80
+
81
+ def geometrized_to_meters(length_geom):
82
+ """Identity conversion: geometrized lengths are already in meters.
83
+
84
+ Provided for symmetry and readability at call sites (e.g.
85
+ ``geometrized_to_meters(r_isco)``); geometrized *length* units coincide
86
+ with SI meters by construction (:math:`G = c = 1`).
87
+ """
88
+ return np.asarray(length_geom)
89
+
90
+
91
+ def geometrized_to_seconds(length_geom):
92
+ """Convert a geometrized length (or time expressed as a length, :math:`ct`) to seconds.
93
+
94
+ Parameters
95
+ ----------
96
+ length_geom : float or array_like
97
+ A geometrized quantity with dimensions of length, e.g. :math:`c
98
+ t` for a geometrized time.
99
+
100
+ Returns
101
+ -------
102
+ float or ndarray
103
+ Time in seconds.
104
+ """
105
+ return np.asarray(length_geom) / C_SI
106
+
107
+
108
+ def seconds_to_geometrized(time_s):
109
+ """Convert a time in seconds to a geometrized length (:math:`ct`, in meters)."""
110
+ return np.asarray(time_s) * C_SI
111
+
112
+
113
+ def geometrized_to_solar_masses(mass_geom):
114
+ """Convert a geometrized mass (length, in meters) back to solar masses.
115
+
116
+ Parameters
117
+ ----------
118
+ mass_geom : float or array_like
119
+ Mass expressed as a geometrized length (meters).
120
+
121
+ Returns
122
+ -------
123
+ float or ndarray
124
+ Mass in units of the Sun's mass.
125
+
126
+ Examples
127
+ --------
128
+ >>> round(float(geometrized_to_solar_masses(solar_masses_to_geometrized(30.0))), 6)
129
+ 30.0
130
+ """
131
+ return np.asarray(mass_geom) * C_SI**2 / (G_SI * SOLAR_MASS_KG)
132
+
133
+
134
+ #: Below this, a mass argument is almost certainly an unconverted physical
135
+ #: value rather than a geometrized length: it's lighter than any known or
136
+ #: theorized neutron star (~1 solar mass ~ 1477 m), let alone a black hole.
137
+ _MIN_PLAUSIBLE_COMPACT_OBJECT_MASS_M = 100.0
138
+
139
+ #: Below this, a distance argument is almost certainly an unconverted
140
+ #: physical value: it's many orders of magnitude short of even 1 AU
141
+ #: (~1.5e11 m), let alone an astrophysical source distance.
142
+ _MIN_PLAUSIBLE_ASTROPHYSICAL_DISTANCE_M = 1.0e9
143
+
144
+
145
+ def check_geometrized_mass(value, name="mass"):
146
+ """Warn if `value` looks like an unconverted physical mass rather than a geometrized length.
147
+
148
+ Intended for classes that require an *absolute* geometrized mass with
149
+ no normalized (``M=1``) convention available -- :class:`~physicskit.relativity.chapters.gw_merger.BinaryMerger`
150
+ is the motivating case: its radiated strain amplitude depends on the
151
+ true physical scale, so silently passing a raw solar-mass count (e.g.
152
+ ``36.0`` instead of ``solar_masses_to_geometrized(36.0)``) doesn't
153
+ raise an error -- it just produces a "chirp" that never actually
154
+ accumulates any phase, an easy mistake to miss since nothing crashes.
155
+
156
+ This is a heuristic plausibility check, not a unit system: it only
157
+ catches the common case of a compact-object mass that's wildly too
158
+ small to be a geometrized length. It does not (and cannot, without
159
+ real unit tracking) catch every possible mis-conversion.
160
+
161
+ Parameters
162
+ ----------
163
+ value : float
164
+ The mass argument to check, in whatever units the caller passed.
165
+ name : str, default="mass"
166
+ Parameter name to mention in the warning message.
167
+
168
+ Examples
169
+ --------
170
+ >>> import warnings
171
+ >>> with warnings.catch_warnings(record=True) as w:
172
+ ... warnings.simplefilter("always")
173
+ ... check_geometrized_mass(36.0, name="m1") # raw solar masses -- wrong
174
+ ... len(w)
175
+ 1
176
+ >>> with warnings.catch_warnings(record=True) as w:
177
+ ... warnings.simplefilter("always")
178
+ ... check_geometrized_mass(solar_masses_to_geometrized(36.0), name="m1") # correct
179
+ ... len(w)
180
+ 0
181
+ """
182
+ if 0.0 < value < _MIN_PLAUSIBLE_COMPACT_OBJECT_MASS_M:
183
+ warnings.warn(
184
+ f"{name}={value!r} is implausibly small for a geometrized-unit mass "
185
+ f"(1 solar mass is ~{solar_masses_to_geometrized(1.0):.1f} m; even the "
186
+ f"lightest known neutron star is a large fraction of that). Did you "
187
+ f"forget to convert with solar_masses_to_geometrized()? If this value "
188
+ f"is intentional (e.g. a sub-solar-mass primordial black hole), ignore "
189
+ f"this warning.",
190
+ UserWarning,
191
+ stacklevel=3,
192
+ )
193
+
194
+
195
+ def check_geometrized_distance(value, name="distance"):
196
+ """Warn if `value` looks like an unconverted physical distance rather than a geometrized length.
197
+
198
+ Same rationale as :func:`check_geometrized_mass`, for distance
199
+ arguments: a raw parsec/Mpc count (e.g. ``410.0`` instead of
200
+ ``410.0 * PARSEC_M * 1e6``) is many orders of magnitude too small to
201
+ be a real geometrized distance, but doesn't raise an error on its own.
202
+
203
+ Parameters
204
+ ----------
205
+ value : float
206
+ The distance argument to check, in whatever units the caller passed.
207
+ name : str, default="distance"
208
+ Parameter name to mention in the warning message.
209
+
210
+ Examples
211
+ --------
212
+ >>> import warnings
213
+ >>> with warnings.catch_warnings(record=True) as w:
214
+ ... warnings.simplefilter("always")
215
+ ... check_geometrized_distance(410.0, name="distance") # raw Mpc -- wrong
216
+ ... len(w)
217
+ 1
218
+ >>> with warnings.catch_warnings(record=True) as w:
219
+ ... warnings.simplefilter("always")
220
+ ... check_geometrized_distance(410.0e6 * PARSEC_M, name="distance") # correct
221
+ ... len(w)
222
+ 0
223
+ """
224
+ if 0.0 < value < _MIN_PLAUSIBLE_ASTROPHYSICAL_DISTANCE_M:
225
+ warnings.warn(
226
+ f"{name}={value!r} is implausibly small for a geometrized-unit distance "
227
+ f"(1 parsec is ~{PARSEC_M:.3e} m). Did you forget to convert, e.g. "
228
+ f"distance_mpc * 1e6 * PARSEC_M? If this value is intentional (e.g. a "
229
+ f"laboratory- or Earth-orbit-scale distance), ignore this warning.",
230
+ UserWarning,
231
+ stacklevel=3,
232
+ )