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,12 @@
1
+ """Low-level differential geometry and Numba-accelerated integration.
2
+
3
+ :mod:`physicskit.relativity.core.tensors` computes Christoffel symbols and curvature
4
+ tensors numerically for arbitrary metrics. :mod:`physicskit.relativity.core.geodesics`
5
+ integrates Schwarzschild and equatorial Kerr geodesics with fixed-step RK4.
6
+ :mod:`physicskit.relativity.core.raytracer` backward ray-traces Schwarzschild null
7
+ geodesics to render gravitationally lensed images; :mod:`physicskit.relativity.core.kerr_raytracer`
8
+ does the same for Kerr, using the full Carter-formalism equations of motion.
9
+
10
+ These functions operate on plain NumPy arrays and carry no state; the
11
+ stateful, user-facing model classes live in :mod:`physicskit.relativity.chapters`.
12
+ """
@@ -0,0 +1,216 @@
1
+ """Numba-accelerated geodesic integrators for Schwarzschild and Kerr spacetimes.
2
+
3
+ A geodesic satisfies :math:`\\frac{d^2 x^\\mu}{d\\lambda^2} + \\Gamma^\\mu_{\\alpha\\beta}
4
+ \\frac{dx^\\alpha}{d\\lambda}\\frac{dx^\\beta}{d\\lambda} = 0`, where :math:`\\lambda` is
5
+ proper time :math:`\\tau` for a timelike (massive-particle) geodesic or an
6
+ affine parameter for a null (photon) geodesic. This module hand-codes the
7
+ analytic Christoffel symbols for two spacetimes -- rather than the generic
8
+ finite-difference engine in :mod:`physicskit.relativity.core.tensors` -- so that repeated
9
+ RK4 evaluation inside a JIT-compiled loop is fast enough for interactive use
10
+ and for tracing thousands of rays in :mod:`physicskit.relativity.core.raytracer`.
11
+
12
+ - :func:`integrate_schwarzschild_geodesic` integrates the full 4D
13
+ coordinate geodesic equation with the exact Schwarzschild Christoffel
14
+ symbols, supporting arbitrary (non-equatorial) timelike or null geodesics.
15
+ - :func:`integrate_kerr_equatorial_geodesic` integrates equatorial
16
+ (:math:`\\theta = \\pi/2`) Kerr geodesics using the reduced
17
+ Boyer-Lindquist equations of motion in terms of the conserved energy
18
+ :math:`E` and angular momentum :math:`L` (Bardeen, Press & Teukolsky
19
+ 1972), which is both simpler and more numerically robust than the full
20
+ Kerr Christoffel symbols for the equatorial-plane physics (ISCO, frame
21
+ dragging, the Penrose process) this package focuses on.
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ import numpy as np
27
+ from numba import njit
28
+
29
+ __all__ = [
30
+ "integrate_kerr_equatorial_geodesic",
31
+ "integrate_schwarzschild_geodesic",
32
+ ]
33
+
34
+
35
+ @njit(cache=True)
36
+ def _schwarzschild_rhs(y, M):
37
+ """RHS of the first-order system equivalent to the Schwarzschild geodesic equation."""
38
+ _t, r, th, _ph, ut, ur, uth, uphi = y
39
+
40
+ two_m = 2.0 * M
41
+ Gt_tr = M / (r * (r - two_m))
42
+ Gr_tt = M * (r - two_m) / r**3
43
+ Gr_rr = -M / (r * (r - two_m))
44
+ Gr_thth = -(r - two_m)
45
+ Gr_phph = -(r - two_m) * np.sin(th) ** 2
46
+ Gth_rth = 1.0 / r
47
+ Gth_phph = -np.sin(th) * np.cos(th)
48
+ Gph_rph = 1.0 / r
49
+ Gph_thph = np.cos(th) / np.sin(th)
50
+
51
+ dut = -2.0 * Gt_tr * ut * ur
52
+ dur = -(Gr_tt * ut * ut + Gr_rr * ur * ur + Gr_thth * uth * uth + Gr_phph * uphi * uphi)
53
+ duth = -(2.0 * Gth_rth * ur * uth + Gth_phph * uphi * uphi)
54
+ duphi = -(2.0 * Gph_rph * ur * uphi + 2.0 * Gph_thph * uth * uphi)
55
+
56
+ out = np.empty(8)
57
+ out[0] = ut
58
+ out[1] = ur
59
+ out[2] = uth
60
+ out[3] = uphi
61
+ out[4] = dut
62
+ out[5] = dur
63
+ out[6] = duth
64
+ out[7] = duphi
65
+ return out
66
+
67
+
68
+ @njit(cache=True)
69
+ def integrate_schwarzschild_geodesic(y0, M, dlambda, n_steps, r_horizon_factor=1.001, r_max=1.0e6):
70
+ """Integrate a Schwarzschild geodesic with fixed-step RK4.
71
+
72
+ Works for both timelike and null geodesics: the geodesic equation
73
+ itself does not depend on the normalization of the initial 4-velocity,
74
+ only the initial condition does (``y0[4:8]`` should satisfy
75
+ :math:`g_{\\mu\\nu} u^\\mu u^\\nu = -1` for a timelike geodesic or
76
+ :math:`=0` for a null one).
77
+
78
+ Parameters
79
+ ----------
80
+ y0 : ndarray of shape (8,)
81
+ Initial state :math:`(t, r, \\theta, \\phi, u^t, u^r, u^\\theta, u^\\phi)`.
82
+ M : float
83
+ Black hole mass, in geometrized units.
84
+ dlambda : float
85
+ Affine-parameter step size.
86
+ n_steps : int
87
+ Maximum number of steps to take.
88
+ r_horizon_factor : float, default=1.001
89
+ Integration stops once :math:`r` falls below
90
+ ``r_horizon_factor * 2M`` (the particle has fallen in).
91
+ r_max : float, default=1e6
92
+ Integration stops once :math:`r` exceeds this value (the particle
93
+ has escaped).
94
+
95
+ Returns
96
+ -------
97
+ trajectory : ndarray of shape (n_valid + 1, 8)
98
+ The integrated state at every step actually taken.
99
+ n_valid : int
100
+ Number of successful steps (so ``trajectory[: n_valid + 1]`` is the
101
+ full valid trajectory; the array is already trimmed to this size).
102
+ """
103
+ y = y0.copy()
104
+ trajectory = np.empty((n_steps + 1, 8))
105
+ trajectory[0] = y
106
+ r_stop = r_horizon_factor * 2.0 * M
107
+ n_valid = 0
108
+
109
+ for i in range(n_steps):
110
+ r = y[1]
111
+ if r <= r_stop or r >= r_max:
112
+ break
113
+ k1 = _schwarzschild_rhs(y, M)
114
+ k2 = _schwarzschild_rhs(y + 0.5 * dlambda * k1, M)
115
+ k3 = _schwarzschild_rhs(y + 0.5 * dlambda * k2, M)
116
+ k4 = _schwarzschild_rhs(y + dlambda * k3, M)
117
+ y = y + (dlambda / 6.0) * (k1 + 2.0 * k2 + 2.0 * k3 + k4)
118
+ trajectory[i + 1] = y
119
+ n_valid = i + 1
120
+
121
+ return trajectory[: n_valid + 1], n_valid
122
+
123
+
124
+ @njit(cache=True)
125
+ def _kerr_eq_radial_potential(r, E, L, a, M, mu2):
126
+ """Carter's radial potential R(r)/Sigma^2 = (dr/dtau)^2 for equatorial Kerr orbits."""
127
+ Delta = r * r - 2.0 * M * r + a * a
128
+ A = E * (r * r + a * a) - L * a
129
+ R = A * A - Delta * (mu2 * r * r + (L - a * E) ** 2)
130
+ return R / r**4
131
+
132
+
133
+ @njit(cache=True)
134
+ def _kerr_eq_rhs(y, E, L, a, M, mu2, h):
135
+ """RHS of the reduced equatorial Kerr equations of motion, state (t, r, phi, dr/dtau)."""
136
+ _t, r, _phi, ur = y
137
+ Delta = r * r - 2.0 * M * r + a * a
138
+ A = E * (r * r + a * a) - L * a
139
+
140
+ dt = (-a * (a * E - L) + (r * r + a * a) * A / Delta) / (r * r)
141
+ dphi = (-(a * E - L) + a * A / Delta) / (r * r)
142
+
143
+ F_plus = _kerr_eq_radial_potential(r + h, E, L, a, M, mu2)
144
+ F_minus = _kerr_eq_radial_potential(r - h, E, L, a, M, mu2)
145
+ d2r = 0.5 * (F_plus - F_minus) / (2.0 * h)
146
+
147
+ out = np.empty(4)
148
+ out[0] = dt
149
+ out[1] = ur
150
+ out[2] = dphi
151
+ out[3] = d2r
152
+ return out
153
+
154
+
155
+ @njit(cache=True)
156
+ def integrate_kerr_equatorial_geodesic(y0, E, L, a, M, mu2, dlambda, n_steps, r_horizon_factor=1.001, r_max=1.0e6):
157
+ """Integrate an equatorial-plane Kerr geodesic with fixed-step RK4.
158
+
159
+ Uses the reduced Boyer-Lindquist equations of motion for a particle of
160
+ conserved specific energy :math:`E` and angular momentum :math:`L`
161
+ (Carter's constant :math:`Q = 0` for equatorial orbits), rather than the
162
+ full 4D Kerr Christoffel symbols.
163
+
164
+ Parameters
165
+ ----------
166
+ y0 : ndarray of shape (4,)
167
+ Initial state :math:`(t, r, \\phi, dr/d\\tau)`.
168
+ E : float
169
+ Conserved specific energy :math:`E = -u_t`.
170
+ L : float
171
+ Conserved specific angular momentum :math:`L = u_\\phi`.
172
+ a : float
173
+ Kerr spin parameter, :math:`0 \\le a < M`.
174
+ M : float
175
+ Black hole mass, in geometrized units.
176
+ mu2 : float
177
+ ``1.0`` for a timelike geodesic, ``0.0`` for a null (photon) geodesic.
178
+ dlambda : float
179
+ Affine-parameter (or proper-time) step size.
180
+ n_steps : int
181
+ Maximum number of steps to take.
182
+ r_horizon_factor : float, default=1.001
183
+ Integration stops once :math:`r` falls below
184
+ ``r_horizon_factor * r_+``, where :math:`r_+ = M + \\sqrt{M^2 - a^2}`.
185
+ r_max : float, default=1e6
186
+ Integration stops once :math:`r` exceeds this value.
187
+
188
+ Returns
189
+ -------
190
+ trajectory : ndarray of shape (n_valid + 1, 4)
191
+ The integrated state at every step actually taken.
192
+ n_valid : int
193
+ Number of successful steps.
194
+ """
195
+ r_plus = M + np.sqrt(M * M - a * a)
196
+ r_stop = r_horizon_factor * r_plus
197
+ h = 1.0e-6 * max(1.0, y0[1])
198
+
199
+ y = y0.copy()
200
+ trajectory = np.empty((n_steps + 1, 4))
201
+ trajectory[0] = y
202
+ n_valid = 0
203
+
204
+ for i in range(n_steps):
205
+ r = y[1]
206
+ if r <= r_stop or r >= r_max:
207
+ break
208
+ k1 = _kerr_eq_rhs(y, E, L, a, M, mu2, h)
209
+ k2 = _kerr_eq_rhs(y + 0.5 * dlambda * k1, E, L, a, M, mu2, h)
210
+ k3 = _kerr_eq_rhs(y + 0.5 * dlambda * k2, E, L, a, M, mu2, h)
211
+ k4 = _kerr_eq_rhs(y + dlambda * k3, E, L, a, M, mu2, h)
212
+ y = y + (dlambda / 6.0) * (k1 + 2.0 * k2 + 2.0 * k3 + k4)
213
+ trajectory[i + 1] = y
214
+ n_valid = i + 1
215
+
216
+ return trajectory[: n_valid + 1], n_valid
@@ -0,0 +1,259 @@
1
+ """A Numba-accelerated backward photon ray-tracer for Kerr gravitational lensing.
2
+
3
+ Unlike Schwarzschild, a spinning black hole's null geodesics are not
4
+ confined to a single plane, so :mod:`physicskit.relativity.core.raytracer`'s 2D orbit
5
+ equation no longer applies. This module integrates the full Carter (1968)
6
+ separated equations of motion in Mino time :math:`\\lambda` (where
7
+ :math:`d\\lambda = d\\tau / \\Sigma`), which decouple the radial and polar
8
+ motion into two independent effective-potential problems:
9
+
10
+ .. math::
11
+
12
+ \\left(\\frac{dr}{d\\lambda}\\right)^2 = R(r), \\qquad
13
+ \\left(\\frac{d\\theta}{d\\lambda}\\right)^2 = \\Theta(\\theta)
14
+
15
+ Differentiating once more removes the sign ambiguity at radial and polar
16
+ turning points entirely (the same trick used for equatorial Kerr orbits in
17
+ :mod:`physicskit.relativity.core.geodesics`, extended here to both :math:`r` and
18
+ :math:`\\theta`):
19
+
20
+ .. math::
21
+
22
+ \\frac{d^2 r}{d\\lambda^2} = \\frac{1}{2}\\frac{dR}{dr}, \\qquad
23
+ \\frac{d^2 \\theta}{d\\lambda^2} = \\frac{1}{2}\\frac{d\\Theta}{d\\theta}
24
+
25
+ Camera rays are parametrized by Bardeen's (1973) impact parameters
26
+ :math:`(\\alpha, \\beta)`, which fix the photon's conserved energy
27
+ (:math:`E=1`), angular momentum :math:`L = -\\alpha \\sin\\theta_o`, and
28
+ Carter constant :math:`Q = (\\alpha^2 - a^2)\\cos^2\\theta_o + \\beta^2`
29
+ given the observer's inclination :math:`\\theta_o`.
30
+ """
31
+
32
+ from __future__ import annotations
33
+
34
+ import numpy as np
35
+ from numba import njit
36
+
37
+ __all__ = ["render_kerr_shadow_image"]
38
+
39
+ OUTCOME_ESCAPED = 0
40
+ OUTCOME_CAPTURED = 1
41
+ OUTCOME_UNRESOLVED = 2
42
+ OUTCOME_DISK = 3
43
+
44
+
45
+ @njit(cache=True)
46
+ def _kerr_R(r, E, L, Q, a, M):
47
+ Delta = r * r - 2.0 * M * r + a * a
48
+ A = E * (r * r + a * a) - L * a
49
+ return A * A - Delta * (Q + (L - a * E) ** 2)
50
+
51
+
52
+ @njit(cache=True)
53
+ def _kerr_Theta(theta, E, L, Q, a):
54
+ cos2 = np.cos(theta) ** 2
55
+ sin2 = np.sin(theta) ** 2
56
+ return Q + cos2 * (a * a * E * E - L * L / sin2)
57
+
58
+
59
+ @njit(cache=True)
60
+ def _kerr_full_rhs(y, E, L, Q, a, M, h):
61
+ """RHS of the 6D first-order system: state (r, dr/dlambda, theta, dtheta/dlambda, t, phi)."""
62
+ r, pr, theta, ptheta, _t, _phi = y
63
+ Delta = r * r - 2.0 * M * r + a * a
64
+ A = E * (r * r + a * a) - L * a
65
+ sin2 = np.sin(theta) ** 2
66
+
67
+ dt = -a * a * E * sin2 + a * L + (r * r + a * a) * A / Delta
68
+ dphi = -a * E + L / sin2 + a * A / Delta
69
+
70
+ R_plus = _kerr_R(r + h, E, L, Q, a, M)
71
+ R_minus = _kerr_R(r - h, E, L, Q, a, M)
72
+ d2r = 0.5 * (R_plus - R_minus) / (2.0 * h)
73
+
74
+ Theta_plus = _kerr_Theta(theta + h, E, L, Q, a)
75
+ Theta_minus = _kerr_Theta(theta - h, E, L, Q, a)
76
+ d2theta = 0.5 * (Theta_plus - Theta_minus) / (2.0 * h)
77
+
78
+ out = np.empty(6)
79
+ out[0] = pr
80
+ out[1] = d2r
81
+ out[2] = ptheta
82
+ out[3] = d2theta
83
+ out[4] = dt
84
+ out[5] = dphi
85
+ return out
86
+
87
+
88
+ @njit(cache=True)
89
+ def _trace_pixel_kerr(
90
+ alpha,
91
+ beta,
92
+ r_o,
93
+ theta_o,
94
+ a,
95
+ M,
96
+ r_disk_inner,
97
+ r_disk_outer,
98
+ dlambda_max,
99
+ n_steps,
100
+ r_horizon_factor,
101
+ ):
102
+ """Trace one Kerr camera ray; returns (outcome_code, hit_radius).
103
+
104
+ Uses an adaptive Mino-time step: in Mino time, ``dr/dlambda`` grows like
105
+ ``r**2`` at large ``r`` (since Mino time absorbs a factor of
106
+ :math:`\\Sigma \\sim r^2`), so a fixed step size that resolves the
107
+ near-horizon dynamics would take absurdly large jumps far from the
108
+ black hole. Scaling the step to the local radial "speed" keeps every
109
+ step's fractional change in ``r`` bounded, automatically compressing
110
+ the (physically boring, nearly straight-line) far-field approach into
111
+ few steps while still resolving the near-horizon region finely.
112
+ """
113
+ E = 1.0
114
+ L = -alpha * np.sin(theta_o)
115
+ Q = (alpha * alpha - a * a) * np.cos(theta_o) ** 2 + beta * beta
116
+
117
+ R0 = _kerr_R(r_o, E, L, Q, a, M)
118
+ pr0 = -np.sqrt(max(R0, 0.0))
119
+ ptheta0 = beta # Theta(theta_o) = beta^2 exactly, by construction of Q
120
+
121
+ y = np.array([r_o, pr0, theta_o, ptheta0, 0.0, 0.0])
122
+ h = 1.0e-6 * max(1.0, r_o)
123
+
124
+ r_plus = M + np.sqrt(M * M - a * a)
125
+ r_stop = r_horizon_factor * r_plus
126
+ r_max = 2.0 * r_o
127
+
128
+ outcome = OUTCOME_UNRESOLVED
129
+ hit_r = -1.0
130
+
131
+ for _i in range(n_steps):
132
+ r = y[0]
133
+ theta = y[2]
134
+ pr = y[1]
135
+ if r <= r_stop:
136
+ outcome = OUTCOME_CAPTURED
137
+ break
138
+ if r >= r_max:
139
+ outcome = OUTCOME_ESCAPED
140
+ break
141
+
142
+ dlambda = min(dlambda_max, 0.02 * max(r, 1.0) / (abs(pr) + 1.0e-6))
143
+ k1 = _kerr_full_rhs(y, E, L, Q, a, M, h)
144
+ k2 = _kerr_full_rhs(y + 0.5 * dlambda * k1, E, L, Q, a, M, h)
145
+ k3 = _kerr_full_rhs(y + 0.5 * dlambda * k2, E, L, Q, a, M, h)
146
+ k4 = _kerr_full_rhs(y + dlambda * k3, E, L, Q, a, M, h)
147
+ y_new = y + (dlambda / 6.0) * (k1 + 2.0 * k2 + 2.0 * k3 + k4)
148
+
149
+ # Reproject the momenta onto the exact constraint surfaces
150
+ # (dr/dlambda)^2=R(r), (dtheta/dlambda)^2=Theta(theta), keeping whatever
151
+ # sign the raw RK4 step produced. Without this, small integration errors
152
+ # in pr/ptheta feed back into the adaptive step-size estimate and can
153
+ # snowball into a spurious sign flip (the ray appearing to "bounce").
154
+ r_new = y_new[0]
155
+ theta_new = y_new[2]
156
+ R_new = _kerr_R(r_new, E, L, Q, a, M)
157
+ sign_pr = 1.0 if y_new[1] >= 0.0 else -1.0
158
+ y_new[1] = sign_pr * np.sqrt(max(R_new, 0.0))
159
+ Theta_new = _kerr_Theta(theta_new, E, L, Q, a)
160
+ sign_ptheta = 1.0 if y_new[3] >= 0.0 else -1.0
161
+ y_new[3] = sign_ptheta * np.sqrt(max(Theta_new, 0.0))
162
+ if (theta - np.pi / 2.0) * (theta_new - np.pi / 2.0) < 0.0:
163
+ r_cross = 0.5 * (r + r_new)
164
+ if r_disk_inner <= r_cross <= r_disk_outer:
165
+ outcome = OUTCOME_DISK
166
+ hit_r = r_cross
167
+ y = y_new
168
+ break
169
+ y = y_new
170
+
171
+ return outcome, hit_r
172
+
173
+
174
+ @njit(cache=True)
175
+ def render_kerr_shadow_image(
176
+ ny,
177
+ nx,
178
+ screen_half_width,
179
+ screen_half_height,
180
+ r_observer,
181
+ theta_observer,
182
+ a,
183
+ M,
184
+ r_disk_inner,
185
+ r_disk_outer,
186
+ dlambda_max=0.02,
187
+ n_steps=4000,
188
+ r_horizon_factor=1.001,
189
+ ):
190
+ """Render a Kerr black hole shadow / accretion disk image by backward ray-tracing.
191
+
192
+ Parameters
193
+ ----------
194
+ ny, nx : int
195
+ Image height and width, in pixels.
196
+ screen_half_width, screen_half_height : float
197
+ Half-extent of the camera screen (Bardeen impact-parameter space),
198
+ in geometrized length units.
199
+ r_observer : float
200
+ Observer's radial (Boyer-Lindquist) coordinate; should be
201
+ :math:`\\gg M` for the flat, far-away camera approximation to hold.
202
+ theta_observer : float
203
+ Observer's polar angle (inclination from the spin axis).
204
+ a : float
205
+ Kerr spin parameter, :math:`0 \\le a < M`.
206
+ M : float
207
+ Black hole mass, in geometrized units.
208
+ r_disk_inner, r_disk_outer : float
209
+ Inner and outer radius of a thin equatorial accretion disk (set
210
+ ``r_disk_inner > r_disk_outer`` to omit the disk).
211
+ dlambda_max : float, default=0.02
212
+ Maximum Mino-time step size (the actual step is adaptively reduced
213
+ wherever the radial "speed" ``|dr/dlambda|`` is large; see
214
+ :func:`_trace_pixel_kerr`).
215
+ n_steps : int, default=4000
216
+ Maximum steps per ray.
217
+ r_horizon_factor : float, default=1.001
218
+ A ray is marked captured once :math:`r` drops below this factor
219
+ times the outer horizon radius :math:`r_+`.
220
+
221
+ Returns
222
+ -------
223
+ outcomes : ndarray of shape (ny, nx), dtype int64
224
+ Per-pixel outcome code (0=escaped, 1=captured, 2=unresolved, 3=disk).
225
+ hit_radii : ndarray of shape (ny, nx)
226
+ Disk-crossing radius for disk-hit pixels, ``-1`` elsewhere.
227
+
228
+ Examples
229
+ --------
230
+ >>> outcomes, hit_radii = render_kerr_shadow_image(
231
+ ... ny=20, nx=20, screen_half_width=15.0, screen_half_height=15.0,
232
+ ... r_observer=200.0, theta_observer=1.3, a=0.9, M=1.0,
233
+ ... r_disk_inner=1.0, r_disk_outer=0.0,
234
+ ... )
235
+ >>> outcomes.shape
236
+ (20, 20)
237
+ """
238
+ outcomes = np.zeros((ny, nx), dtype=np.int64)
239
+ hit_radii = np.full((ny, nx), -1.0)
240
+ for iy in range(ny):
241
+ beta = screen_half_height * (1.0 - 2.0 * iy / (ny - 1))
242
+ for ix in range(nx):
243
+ alpha = screen_half_width * (2.0 * ix / (nx - 1) - 1.0)
244
+ outcome, hit_r = _trace_pixel_kerr(
245
+ alpha,
246
+ beta,
247
+ r_observer,
248
+ theta_observer,
249
+ a,
250
+ M,
251
+ r_disk_inner,
252
+ r_disk_outer,
253
+ dlambda_max,
254
+ n_steps,
255
+ r_horizon_factor,
256
+ )
257
+ outcomes[iy, ix] = outcome
258
+ hit_radii[iy, ix] = hit_r
259
+ return outcomes, hit_radii