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,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
|
+
)
|