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.
- physicskit/__init__.py +51 -0
- physicskit/astro/__init__.py +66 -0
- physicskit/astro/cosmic_web.py +395 -0
- physicskit/astro/galactic_dynamics.py +137 -0
- physicskit/astro/nbody.py +208 -0
- physicskit/astro/orbital_mechanics.py +220 -0
- physicskit/astro/stellar_dynamo.py +628 -0
- physicskit/astro/stellar_structure.py +198 -0
- physicskit/astro/tests/__init__.py +0 -0
- physicskit/astro/tests/conftest.py +7 -0
- physicskit/astro/tests/test_cosmic_web.py +159 -0
- physicskit/astro/tests/test_galactic_dynamics.py +46 -0
- physicskit/astro/tests/test_nbody.py +81 -0
- physicskit/astro/tests/test_orbital_mechanics.py +73 -0
- physicskit/astro/tests/test_stellar_dynamo.py +198 -0
- physicskit/astro/tests/test_stellar_structure.py +52 -0
- physicskit/astro/tests/test_visualizers.py +159 -0
- physicskit/astro/visualizers.py +628 -0
- physicskit/chaos/__init__.py +55 -0
- physicskit/chaos/core/__init__.py +3 -0
- physicskit/chaos/core/base_system.py +342 -0
- physicskit/chaos/core/integrators.py +40 -0
- physicskit/chaos/exceptions.py +24 -0
- physicskit/chaos/quantum/__init__.py +38 -0
- physicskit/chaos/quantum/billiards.py +274 -0
- physicskit/chaos/quantum/husimi.py +85 -0
- physicskit/chaos/quantum/maps.py +366 -0
- physicskit/chaos/systems/__init__.py +38 -0
- physicskit/chaos/systems/billiards.py +1131 -0
- physicskit/chaos/systems/continuous.py +1276 -0
- physicskit/chaos/systems/maps.py +557 -0
- physicskit/chaos/tests/__init__.py +0 -0
- physicskit/chaos/tests/test_basins.py +76 -0
- physicskit/chaos/tests/test_bifurcation.py +132 -0
- physicskit/chaos/tests/test_billiards.py +115 -0
- physicskit/chaos/tests/test_continuous.py +224 -0
- physicskit/chaos/tests/test_dimension.py +69 -0
- physicskit/chaos/tests/test_divergence.py +90 -0
- physicskit/chaos/tests/test_dynamic_plots.py +269 -0
- physicskit/chaos/tests/test_integrators.py +96 -0
- physicskit/chaos/tests/test_io.py +41 -0
- physicskit/chaos/tests/test_maps.py +110 -0
- physicskit/chaos/tests/test_metrics.py +115 -0
- physicskit/chaos/tests/test_phase_space.py +73 -0
- physicskit/chaos/tests/test_properties.py +116 -0
- physicskit/chaos/tests/test_quantum_billiards.py +94 -0
- physicskit/chaos/tests/test_quantum_maps.py +144 -0
- physicskit/chaos/tests/test_recurrence.py +51 -0
- physicskit/chaos/tests/test_repr_and_api_consistency.py +134 -0
- physicskit/chaos/tests/test_section.py +49 -0
- physicskit/chaos/tests/test_spectral.py +48 -0
- physicskit/chaos/tests/test_streamlit_app.py +45 -0
- physicskit/chaos/tests/test_timeseries.py +131 -0
- physicskit/chaos/tests/test_viewer3d.py +41 -0
- physicskit/chaos/utils/__init__.py +46 -0
- physicskit/chaos/utils/dimension.py +139 -0
- physicskit/chaos/utils/io.py +95 -0
- physicskit/chaos/utils/metrics.py +304 -0
- physicskit/chaos/utils/recurrence.py +122 -0
- physicskit/chaos/utils/spectral.py +69 -0
- physicskit/chaos/utils/timeseries.py +303 -0
- physicskit/chaos/visualizers/__init__.py +85 -0
- physicskit/chaos/visualizers/basins.py +185 -0
- physicskit/chaos/visualizers/bifurcation.py +273 -0
- physicskit/chaos/visualizers/divergence.py +374 -0
- physicskit/chaos/visualizers/dynamic_plots.py +1372 -0
- physicskit/chaos/visualizers/phase_space.py +133 -0
- physicskit/chaos/visualizers/quantum.py +299 -0
- physicskit/chaos/visualizers/recurrence.py +91 -0
- physicskit/chaos/visualizers/section.py +161 -0
- physicskit/chaos/visualizers/theme.py +31 -0
- physicskit/chaos/visualizers/viewer3d.py +82 -0
- physicskit/classical/__init__.py +55 -0
- physicskit/classical/core/__init__.py +17 -0
- physicskit/classical/core/base_system.py +510 -0
- physicskit/classical/core/integrators.py +397 -0
- physicskit/classical/systems/__init__.py +32 -0
- physicskit/classical/systems/chains.py +334 -0
- physicskit/classical/systems/hamiltonian.py +215 -0
- physicskit/classical/systems/lagrangian.py +308 -0
- physicskit/classical/systems/newtonian.py +536 -0
- physicskit/classical/systems/rotations.py +614 -0
- physicskit/classical/tests/__init__.py +0 -0
- physicskit/classical/tests/conftest.py +7 -0
- physicskit/classical/tests/test_base_system.py +113 -0
- physicskit/classical/tests/test_chains.py +84 -0
- physicskit/classical/tests/test_conservation.py +244 -0
- physicskit/classical/tests/test_conservation_utils.py +47 -0
- physicskit/classical/tests/test_hamiltonian.py +44 -0
- physicskit/classical/tests/test_integrators.py +123 -0
- physicskit/classical/tests/test_lagrangian.py +69 -0
- physicskit/classical/tests/test_newtonian.py +45 -0
- physicskit/classical/tests/test_rotations.py +160 -0
- physicskit/classical/tests/test_stepsize.py +149 -0
- physicskit/classical/tests/test_symbolic.py +125 -0
- physicskit/classical/tests/test_visualizers.py +172 -0
- physicskit/classical/utils/__init__.py +20 -0
- physicskit/classical/utils/conservation.py +126 -0
- physicskit/classical/utils/stepsize.py +187 -0
- physicskit/classical/utils/symbolic.py +235 -0
- physicskit/classical/visualizers/__init__.py +25 -0
- physicskit/classical/visualizers/animations.py +508 -0
- physicskit/classical/visualizers/interactive.py +113 -0
- physicskit/classical/visualizers/modal_analysis.py +97 -0
- physicskit/classical/visualizers/phase_space.py +214 -0
- physicskit/condensed/__init__.py +122 -0
- physicskit/condensed/anderson_localization.py +155 -0
- physicskit/condensed/correlated.py +284 -0
- physicskit/condensed/ginzburg_landau.py +220 -0
- physicskit/condensed/landau_levels.py +246 -0
- physicskit/condensed/laughlin.py +229 -0
- physicskit/condensed/models.py +627 -0
- physicskit/condensed/tests/__init__.py +0 -0
- physicskit/condensed/tests/test_condensed.py +518 -0
- physicskit/condensed/tests/test_visualizers.py +87 -0
- physicskit/condensed/tight_binding.py +445 -0
- physicskit/condensed/topological_insulator_3d.py +178 -0
- physicskit/condensed/topology.py +217 -0
- physicskit/condensed/visualizers.py +296 -0
- physicskit/condensed/weyl.py +180 -0
- physicskit/constants.py +184 -0
- physicskit/fields/__init__.py +111 -0
- physicskit/fields/electrodynamics.py +671 -0
- physicskit/fields/quantum_fields.py +510 -0
- physicskit/fields/solitons.py +527 -0
- physicskit/fields/tests/__init__.py +0 -0
- physicskit/fields/tests/conftest.py +7 -0
- physicskit/fields/tests/test_field_animations.py +154 -0
- physicskit/fields/tests/test_fields.py +439 -0
- physicskit/fields/tests/test_visualizers.py +101 -0
- physicskit/fields/visualizers.py +503 -0
- physicskit/fluids/__init__.py +134 -0
- physicskit/fluids/core/__init__.py +23 -0
- physicskit/fluids/core/grid.py +165 -0
- physicskit/fluids/core/timestepping.py +231 -0
- physicskit/fluids/exceptions.py +26 -0
- physicskit/fluids/systems/__init__.py +70 -0
- physicskit/fluids/systems/compressible_flow.py +281 -0
- physicskit/fluids/systems/instabilities.py +343 -0
- physicskit/fluids/systems/navier_stokes.py +221 -0
- physicskit/fluids/systems/potential_flow.py +440 -0
- physicskit/fluids/systems/viscous_flow.py +425 -0
- physicskit/fluids/systems/vortex_dynamics.py +304 -0
- physicskit/fluids/tests/__init__.py +0 -0
- physicskit/fluids/tests/conftest.py +7 -0
- physicskit/fluids/tests/test_compressible_flow.py +66 -0
- physicskit/fluids/tests/test_instabilities.py +115 -0
- physicskit/fluids/tests/test_navier_stokes.py +67 -0
- physicskit/fluids/tests/test_potential_flow.py +92 -0
- physicskit/fluids/tests/test_static_visualizers.py +91 -0
- physicskit/fluids/tests/test_utils.py +100 -0
- physicskit/fluids/tests/test_viscous_flow.py +110 -0
- physicskit/fluids/tests/test_visualizers.py +34 -0
- physicskit/fluids/tests/test_vortex_dynamics.py +88 -0
- physicskit/fluids/utils/__init__.py +18 -0
- physicskit/fluids/utils/dimensionless.py +239 -0
- physicskit/fluids/utils/spectral_analysis.py +142 -0
- physicskit/fluids/visualizers/__init__.py +14 -0
- physicskit/fluids/visualizers/compressible.py +39 -0
- physicskit/fluids/visualizers/flow_fields.py +252 -0
- physicskit/fluids/visualizers/potential_flow.py +51 -0
- physicskit/fluids/visualizers/spectra.py +62 -0
- physicskit/fluids/visualizers/theme.py +38 -0
- physicskit/integrators/__init__.py +52 -0
- physicskit/integrators/adaptive.py +199 -0
- physicskit/integrators/fixed_step.py +360 -0
- physicskit/integrators/tests/__init__.py +0 -0
- physicskit/integrators/tests/test_integrators.py +101 -0
- physicskit/optics/__init__.py +124 -0
- physicskit/optics/_compat.py +5 -0
- physicskit/optics/gaussian.py +382 -0
- physicskit/optics/quantum_optics.py +428 -0
- physicskit/optics/ray.py +469 -0
- physicskit/optics/tests/__init__.py +0 -0
- physicskit/optics/tests/test_diffraction_animation.py +38 -0
- physicskit/optics/tests/test_gaussian.py +170 -0
- physicskit/optics/tests/test_quantum_optics.py +147 -0
- physicskit/optics/tests/test_ray.py +177 -0
- physicskit/optics/tests/test_visualizers.py +58 -0
- physicskit/optics/tests/test_wave.py +146 -0
- physicskit/optics/visualizers.py +264 -0
- physicskit/optics/wave.py +365 -0
- physicskit/particle/__init__.py +152 -0
- physicskit/particle/collider.py +382 -0
- physicskit/particle/confinement.py +125 -0
- physicskit/particle/decays.py +357 -0
- physicskit/particle/electroweak.py +300 -0
- physicskit/particle/kinematics.py +271 -0
- physicskit/particle/neutrinos.py +84 -0
- physicskit/particle/nuclear.py +100 -0
- physicskit/particle/scattering.py +159 -0
- physicskit/particle/tests/__init__.py +0 -0
- physicskit/particle/tests/conftest.py +7 -0
- physicskit/particle/tests/test_collider.py +119 -0
- physicskit/particle/tests/test_confinement.py +42 -0
- physicskit/particle/tests/test_decays.py +112 -0
- physicskit/particle/tests/test_electroweak.py +97 -0
- physicskit/particle/tests/test_kinematics.py +138 -0
- physicskit/particle/tests/test_neutrinos.py +36 -0
- physicskit/particle/tests/test_nuclear.py +45 -0
- physicskit/particle/tests/test_scattering.py +64 -0
- physicskit/particle/tests/test_visualizers.py +214 -0
- physicskit/particle/visualizers/__init__.py +38 -0
- physicskit/particle/visualizers/animations.py +705 -0
- physicskit/particle/visualizers/static.py +101 -0
- physicskit/plasma/__init__.py +176 -0
- physicskit/plasma/acceleration.py +152 -0
- physicskit/plasma/instabilities.py +417 -0
- physicskit/plasma/kinetic.py +588 -0
- physicskit/plasma/mhd.py +463 -0
- physicskit/plasma/single_particle.py +562 -0
- physicskit/plasma/tests/__init__.py +0 -0
- physicskit/plasma/tests/conftest.py +7 -0
- physicskit/plasma/tests/test_new_time_domain_features.py +222 -0
- physicskit/plasma/tests/test_plasma.py +232 -0
- physicskit/plasma/tests/test_visualizers.py +79 -0
- physicskit/plasma/turbulence.py +219 -0
- physicskit/plasma/visualizers.py +868 -0
- physicskit/plasma/waves.py +549 -0
- physicskit/quantum/__init__.py +180 -0
- physicskit/quantum/_compat.py +20 -0
- physicskit/quantum/chapters/__init__.py +0 -0
- physicskit/quantum/chapters/entanglement.py +489 -0
- physicskit/quantum/chapters/harmonic_spin.py +517 -0
- physicskit/quantum/chapters/hydrogen_am.py +292 -0
- physicskit/quantum/chapters/perturbation.py +350 -0
- physicskit/quantum/chapters/potentials.py +997 -0
- physicskit/quantum/chapters/spin.py +306 -0
- physicskit/quantum/chapters/wave_packets.py +541 -0
- physicskit/quantum/core/__init__.py +0 -0
- physicskit/quantum/core/eigensolvers.py +311 -0
- physicskit/quantum/core/operators.py +296 -0
- physicskit/quantum/core/solvers.py +271 -0
- physicskit/quantum/tests/__init__.py +0 -0
- physicskit/quantum/tests/conftest.py +7 -0
- physicskit/quantum/tests/test_animations.py +225 -0
- physicskit/quantum/tests/test_compat.py +43 -0
- physicskit/quantum/tests/test_eigensolvers.py +24 -0
- physicskit/quantum/tests/test_entanglement.py +127 -0
- physicskit/quantum/tests/test_harmonic_spin.py +140 -0
- physicskit/quantum/tests/test_hydrogen_am.py +48 -0
- physicskit/quantum/tests/test_measure.py +110 -0
- physicskit/quantum/tests/test_operators.py +105 -0
- physicskit/quantum/tests/test_perturbation.py +112 -0
- physicskit/quantum/tests/test_physics_checks.py +131 -0
- physicskit/quantum/tests/test_potentials.py +191 -0
- physicskit/quantum/tests/test_solvers.py +70 -0
- physicskit/quantum/tests/test_spin.py +22 -0
- physicskit/quantum/tests/test_unitarity.py +84 -0
- physicskit/quantum/tests/test_wave_packets.py +53 -0
- physicskit/quantum/utils/__init__.py +0 -0
- physicskit/quantum/utils/measure.py +253 -0
- physicskit/quantum/visualizers/__init__.py +0 -0
- physicskit/quantum/visualizers/bloch_sphere.py +205 -0
- physicskit/quantum/visualizers/entanglement.py +58 -0
- physicskit/quantum/visualizers/orbitals.py +187 -0
- physicskit/quantum/visualizers/phase_space.py +179 -0
- physicskit/quantum/visualizers/wavefunctions.py +230 -0
- physicskit/relativity/__init__.py +61 -0
- physicskit/relativity/chapters/__init__.py +33 -0
- physicskit/relativity/chapters/cosmology.py +190 -0
- physicskit/relativity/chapters/gw_merger.py +399 -0
- physicskit/relativity/chapters/kerr.py +371 -0
- physicskit/relativity/chapters/lensing.py +196 -0
- physicskit/relativity/chapters/neutron_star.py +149 -0
- physicskit/relativity/chapters/schwarzschild.py +412 -0
- physicskit/relativity/chapters/timekeeping.py +133 -0
- physicskit/relativity/core/__init__.py +12 -0
- physicskit/relativity/core/geodesics.py +216 -0
- physicskit/relativity/core/kerr_raytracer.py +259 -0
- physicskit/relativity/core/raytracer.py +241 -0
- physicskit/relativity/core/tensors.py +468 -0
- physicskit/relativity/tests/__init__.py +0 -0
- physicskit/relativity/tests/test_conservations.py +54 -0
- physicskit/relativity/tests/test_constants.py +34 -0
- physicskit/relativity/tests/test_cosmology.py +61 -0
- physicskit/relativity/tests/test_gw_merger.py +117 -0
- physicskit/relativity/tests/test_kerr.py +116 -0
- physicskit/relativity/tests/test_kerr_raytracer.py +103 -0
- physicskit/relativity/tests/test_lensing.py +75 -0
- physicskit/relativity/tests/test_neutron_star.py +48 -0
- physicskit/relativity/tests/test_raytracer.py +67 -0
- physicskit/relativity/tests/test_schwarzschild.py +126 -0
- physicskit/relativity/tests/test_shadow_render.py +81 -0
- physicskit/relativity/tests/test_tensors.py +86 -0
- physicskit/relativity/tests/test_timekeeping.py +51 -0
- physicskit/relativity/tests/test_visualizers_misc.py +115 -0
- physicskit/relativity/utils/__init__.py +1 -0
- physicskit/relativity/utils/constants.py +232 -0
- physicskit/relativity/visualizers/__init__.py +46 -0
- physicskit/relativity/visualizers/interactive.py +127 -0
- physicskit/relativity/visualizers/shadow_render.py +367 -0
- physicskit/relativity/visualizers/spacetime_3d.py +92 -0
- physicskit/relativity/visualizers/spacetime_diagrams.py +198 -0
- physicskit/relativity/visualizers/wave_plots.py +185 -0
- physicskit/rmt/__init__.py +8 -0
- physicskit/rmt/ensembles/__init__.py +108 -0
- physicskit/rmt/ensembles/banded.py +114 -0
- physicskit/rmt/ensembles/base.py +135 -0
- physicskit/rmt/ensembles/bdg.py +218 -0
- physicskit/rmt/ensembles/chiral.py +308 -0
- physicskit/rmt/ensembles/circular.py +104 -0
- physicskit/rmt/ensembles/crossover.py +82 -0
- physicskit/rmt/ensembles/density_matrix.py +148 -0
- physicskit/rmt/ensembles/effective_hamiltonian.py +230 -0
- physicskit/rmt/ensembles/embedded.py +207 -0
- physicskit/rmt/ensembles/gaussian.py +153 -0
- physicskit/rmt/ensembles/ginibre.py +110 -0
- physicskit/rmt/ensembles/girko.py +190 -0
- physicskit/rmt/ensembles/graph_laplacian.py +92 -0
- physicskit/rmt/ensembles/haar.py +57 -0
- physicskit/rmt/ensembles/jacobi.py +182 -0
- physicskit/rmt/ensembles/poisson.py +77 -0
- physicskit/rmt/ensembles/polynomial.py +139 -0
- physicskit/rmt/ensembles/pt_symmetric.py +120 -0
- physicskit/rmt/ensembles/single_ring.py +170 -0
- physicskit/rmt/ensembles/sparse.py +95 -0
- physicskit/rmt/ensembles/syk.py +170 -0
- physicskit/rmt/ensembles/truncated_unitary.py +80 -0
- physicskit/rmt/ensembles/universality.py +92 -0
- physicskit/rmt/ensembles/wishart.py +88 -0
- physicskit/rmt/py.typed +0 -0
- physicskit/rmt/spectrum.py +82 -0
- physicskit/rmt/stats/__init__.py +160 -0
- physicskit/rmt/stats/characteristic_polynomial.py +77 -0
- physicskit/rmt/stats/circular_law.py +54 -0
- physicskit/rmt/stats/correlations.py +162 -0
- physicskit/rmt/stats/density.py +70 -0
- physicskit/rmt/stats/entanglement.py +36 -0
- physicskit/rmt/stats/free_probability.py +202 -0
- physicskit/rmt/stats/localization.py +338 -0
- physicskit/rmt/stats/marchenko_pastur.py +55 -0
- physicskit/rmt/stats/pt_symmetric.py +265 -0
- physicskit/rmt/stats/ratios.py +110 -0
- physicskit/rmt/stats/real_ginibre.py +134 -0
- physicskit/rmt/stats/rigidity.py +272 -0
- physicskit/rmt/stats/single_ring.py +95 -0
- physicskit/rmt/stats/spacing.py +150 -0
- physicskit/rmt/stats/tracy_widom.py +175 -0
- physicskit/rmt/stats/truncated_unitary.py +40 -0
- physicskit/rmt/stats/unfolding.py +37 -0
- physicskit/rmt/stats/wachter.py +67 -0
- physicskit/rmt/tests/__init__.py +0 -0
- physicskit/rmt/tests/cache_utils.py +107 -0
- physicskit/rmt/tests/conftest.py +8 -0
- physicskit/rmt/tests/test_banded.py +86 -0
- physicskit/rmt/tests/test_base.py +29 -0
- physicskit/rmt/tests/test_bdg.py +133 -0
- physicskit/rmt/tests/test_characteristic_polynomial.py +50 -0
- physicskit/rmt/tests/test_chiral.py +273 -0
- physicskit/rmt/tests/test_circular.py +225 -0
- physicskit/rmt/tests/test_crossover.py +72 -0
- physicskit/rmt/tests/test_density.py +23 -0
- physicskit/rmt/tests/test_density_matrix.py +181 -0
- physicskit/rmt/tests/test_effective_hamiltonian.py +140 -0
- physicskit/rmt/tests/test_embedded.py +169 -0
- physicskit/rmt/tests/test_free_probability.py +81 -0
- physicskit/rmt/tests/test_gaussian_ensembles.py +22 -0
- physicskit/rmt/tests/test_gaussian_semicircle.py +127 -0
- physicskit/rmt/tests/test_ginibre_circular_law.py +131 -0
- physicskit/rmt/tests/test_girko.py +178 -0
- physicskit/rmt/tests/test_graph_laplacian.py +102 -0
- physicskit/rmt/tests/test_haar.py +118 -0
- physicskit/rmt/tests/test_jacobi_wachter.py +136 -0
- physicskit/rmt/tests/test_localization.py +240 -0
- physicskit/rmt/tests/test_poisson.py +149 -0
- physicskit/rmt/tests/test_polynomial.py +107 -0
- physicskit/rmt/tests/test_pt_symmetric.py +178 -0
- physicskit/rmt/tests/test_random_state.py +19 -0
- physicskit/rmt/tests/test_real_ginibre.py +112 -0
- physicskit/rmt/tests/test_rigidity_universality.py +167 -0
- physicskit/rmt/tests/test_single_ring.py +132 -0
- physicskit/rmt/tests/test_spacing_and_ratios.py +122 -0
- physicskit/rmt/tests/test_sparse.py +71 -0
- physicskit/rmt/tests/test_spectrum.py +27 -0
- physicskit/rmt/tests/test_syk.py +136 -0
- physicskit/rmt/tests/test_tracy_widom.py +113 -0
- physicskit/rmt/tests/test_tridiagonal.py +41 -0
- physicskit/rmt/tests/test_truncated_unitary.py +80 -0
- physicskit/rmt/tests/test_validation_base.py +25 -0
- physicskit/rmt/tests/test_validation_misc.py +67 -0
- physicskit/rmt/tests/test_wishart_marchenko_pastur.py +120 -0
- physicskit/rmt/utils/__init__.py +0 -0
- physicskit/rmt/utils/haar.py +128 -0
- physicskit/rmt/utils/random_state.py +24 -0
- physicskit/rmt/utils/tridiagonal.py +167 -0
- physicskit/rmt/validation/__init__.py +36 -0
- physicskit/rmt/validation/base.py +126 -0
- physicskit/rmt/validation/circular_law.py +80 -0
- physicskit/rmt/validation/marchenko_pastur.py +53 -0
- physicskit/rmt/validation/ratio_distribution.py +53 -0
- physicskit/rmt/validation/real_ginibre.py +60 -0
- physicskit/rmt/validation/sine_kernel.py +57 -0
- physicskit/rmt/validation/single_ring.py +68 -0
- physicskit/rmt/validation/tracy_widom.py +89 -0
- physicskit/rmt/validation/universality.py +100 -0
- physicskit/rmt/validation/wachter.py +52 -0
- physicskit/rmt/validation/wigner_semicircle.py +40 -0
- physicskit/rmt/validation/wigner_surmise.py +58 -0
- physicskit/semiclassical/__init__.py +94 -0
- physicskit/semiclassical/_compat.py +5 -0
- physicskit/semiclassical/core/__init__.py +41 -0
- physicskit/semiclassical/core/gutzwiller.py +215 -0
- physicskit/semiclassical/core/path_integral.py +594 -0
- physicskit/semiclassical/core/propagators.py +579 -0
- physicskit/semiclassical/core/wkb.py +308 -0
- physicskit/semiclassical/systems/__init__.py +13 -0
- physicskit/semiclassical/systems/scarring.py +278 -0
- physicskit/semiclassical/tests/__init__.py +0 -0
- physicskit/semiclassical/tests/test_gutzwiller.py +35 -0
- physicskit/semiclassical/tests/test_path_integral.py +194 -0
- physicskit/semiclassical/tests/test_propagators.py +82 -0
- physicskit/semiclassical/tests/test_scarring.py +43 -0
- physicskit/semiclassical/tests/test_visualizers.py +89 -0
- physicskit/semiclassical/tests/test_wkb.py +30 -0
- physicskit/semiclassical/visualizers/__init__.py +17 -0
- physicskit/semiclassical/visualizers/gutzwiller.py +55 -0
- physicskit/semiclassical/visualizers/path_integral.py +195 -0
- physicskit/semiclassical/visualizers/propagators.py +89 -0
- physicskit/semiclassical/visualizers/scarring.py +146 -0
- physicskit/semiclassical/visualizers/wkb.py +55 -0
- physicskit/statphys/__init__.py +80 -0
- physicskit/statphys/chapters/__init__.py +43 -0
- physicskit/statphys/chapters/ehrenfest_urn.py +112 -0
- physicskit/statphys/chapters/ising_lattice.py +431 -0
- physicskit/statphys/chapters/kpz_growth.py +129 -0
- physicskit/statphys/chapters/molecular_dynamics.py +258 -0
- physicskit/statphys/chapters/nonequilibrium_work.py +146 -0
- physicskit/statphys/chapters/percolation.py +305 -0
- physicskit/statphys/chapters/random_walk.py +142 -0
- physicskit/statphys/chapters/renormalization.py +148 -0
- physicskit/statphys/chapters/sandpile.py +109 -0
- physicskit/statphys/chapters/spin_glass.py +275 -0
- physicskit/statphys/core/__init__.py +10 -0
- physicskit/statphys/core/kpz_engine.py +53 -0
- physicskit/statphys/core/md_engine.py +157 -0
- physicskit/statphys/core/monte_carlo.py +462 -0
- physicskit/statphys/core/random_walk_engine.py +68 -0
- physicskit/statphys/core/sandpile_engine.py +52 -0
- physicskit/statphys/tests/__init__.py +0 -0
- physicskit/statphys/tests/test_dynamics.py +66 -0
- physicskit/statphys/tests/test_ehrenfest_urn.py +44 -0
- physicskit/statphys/tests/test_finite_size_scaling.py +62 -0
- physicskit/statphys/tests/test_interactive.py +33 -0
- physicskit/statphys/tests/test_ising.py +68 -0
- physicskit/statphys/tests/test_kpz.py +55 -0
- physicskit/statphys/tests/test_landau_theory.py +49 -0
- physicskit/statphys/tests/test_md_engine.py +31 -0
- physicskit/statphys/tests/test_molecular_dynamics.py +72 -0
- physicskit/statphys/tests/test_nonequilibrium_work.py +33 -0
- physicskit/statphys/tests/test_partition_function.py +26 -0
- physicskit/statphys/tests/test_percolation.py +116 -0
- physicskit/statphys/tests/test_potts_model.py +46 -0
- physicskit/statphys/tests/test_random_walk.py +50 -0
- physicskit/statphys/tests/test_renormalization.py +44 -0
- physicskit/statphys/tests/test_sandpile.py +44 -0
- physicskit/statphys/tests/test_spin_glass.py +73 -0
- physicskit/statphys/tests/test_thermodynamics.py +121 -0
- physicskit/statphys/tests/test_xy_model.py +45 -0
- physicskit/statphys/utils/__init__.py +1 -0
- physicskit/statphys/utils/dynamics.py +100 -0
- physicskit/statphys/utils/finite_size_scaling.py +171 -0
- physicskit/statphys/utils/landau_theory.py +151 -0
- physicskit/statphys/utils/partition_function.py +109 -0
- physicskit/statphys/utils/thermodynamics.py +356 -0
- physicskit/statphys/visualizers/__init__.py +87 -0
- physicskit/statphys/visualizers/interactive.py +161 -0
- physicskit/statphys/visualizers/jarzynski_render.py +44 -0
- physicskit/statphys/visualizers/kpz_render.py +65 -0
- physicskit/statphys/visualizers/lattice_render.py +212 -0
- physicskit/statphys/visualizers/particle_render.py +121 -0
- physicskit/statphys/visualizers/random_walk_render.py +106 -0
- physicskit/statphys/visualizers/rg_render.py +45 -0
- physicskit/statphys/visualizers/sandpile_render.py +65 -0
- physicskit/statphys/visualizers/spin_glass_render.py +40 -0
- physicskit/statphys/visualizers/urn_render.py +38 -0
- physicskit/statphys/visualizers/vortex_render.py +84 -0
- physicskit/tests/__init__.py +0 -0
- physicskit/tests/test_constants.py +38 -0
- physicskit-0.1.0.dist-info/METADATA +196 -0
- physicskit-0.1.0.dist-info/RECORD +484 -0
- physicskit-0.1.0.dist-info/WHEEL +5 -0
- physicskit-0.1.0.dist-info/licenses/LICENSE +21 -0
- 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
|