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,198 @@
|
|
|
1
|
+
r"""Polytropic stellar models via the Lane-Emden equation.
|
|
2
|
+
|
|
3
|
+
Uses **gravitational units with** :math:`G=1` by default -- every function
|
|
4
|
+
that needs it accepts ``G`` as a keyword argument, the same convention
|
|
5
|
+
:mod:`physicskit.relativity` uses for its own geometrized units.
|
|
6
|
+
|
|
7
|
+
- :func:`lane_emden` -- the dimensionless hydrostatic-equilibrium profile
|
|
8
|
+
of a self-gravitating polytropic sphere.
|
|
9
|
+
- :class:`PolytropicStar` -- a physical star (radius, mass) built from one
|
|
10
|
+
Lane-Emden solution.
|
|
11
|
+
- :func:`chandrasekhar_mass` -- the white-dwarf mass limit.
|
|
12
|
+
- :func:`main_sequence_luminosity` -- the empirical mass-luminosity relation.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import numpy as np
|
|
18
|
+
from scipy.integrate import solve_ivp
|
|
19
|
+
|
|
20
|
+
__all__ = ["lane_emden", "PolytropicStar", "chandrasekhar_mass", "main_sequence_luminosity"]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def lane_emden(n, xi_max=10.0, n_points=2000):
|
|
24
|
+
r"""Integrate the Lane-Emden equation for a polytrope of index ``n``.
|
|
25
|
+
|
|
26
|
+
.. math::
|
|
27
|
+
|
|
28
|
+
\frac{1}{\xi^2}\frac{d}{d\xi}\left(\xi^2\frac{d\theta}{d\xi}\right)
|
|
29
|
+
+ \theta^n = 0, \qquad \theta(0)=1,\ \theta'(0)=0
|
|
30
|
+
|
|
31
|
+
the dimensionless hydrostatic-equilibrium profile of a self-gravitating
|
|
32
|
+
sphere with equation of state :math:`P=K\rho^{1+1/n}`.
|
|
33
|
+
|
|
34
|
+
Parameters
|
|
35
|
+
----------
|
|
36
|
+
n : float
|
|
37
|
+
Polytropic index.
|
|
38
|
+
xi_max : float, default=10.0
|
|
39
|
+
Upper integration bound, used only as a fallback for indices
|
|
40
|
+
(e.g. ``n=5``) where :math:`\theta` never reaches zero.
|
|
41
|
+
n_points : int, default=2000
|
|
42
|
+
Number of points to sample the returned arrays at.
|
|
43
|
+
|
|
44
|
+
Returns
|
|
45
|
+
-------
|
|
46
|
+
xi : ndarray
|
|
47
|
+
Dimensionless radius, from just above 0 to the surface (the first
|
|
48
|
+
zero of :math:`\theta`) or to ``xi_max``.
|
|
49
|
+
theta : ndarray
|
|
50
|
+
The Lane-Emden function :math:`\theta(\xi)`.
|
|
51
|
+
|
|
52
|
+
Examples
|
|
53
|
+
--------
|
|
54
|
+
>>> xi, theta = lane_emden(0.0)
|
|
55
|
+
>>> round(float(xi[-1]), 3) # exact analytic surface is sqrt(6)
|
|
56
|
+
2.449
|
|
57
|
+
"""
|
|
58
|
+
xi0 = 1e-6
|
|
59
|
+
theta0 = 1.0 - xi0**2 / 6.0
|
|
60
|
+
dtheta0 = -xi0 / 3.0
|
|
61
|
+
|
|
62
|
+
def rhs(xi, y):
|
|
63
|
+
theta, dtheta = y
|
|
64
|
+
theta_pos = max(theta, 0.0)
|
|
65
|
+
d2theta = -(theta_pos**n) - (2.0 / xi) * dtheta
|
|
66
|
+
return [dtheta, d2theta]
|
|
67
|
+
|
|
68
|
+
def surface_event(xi, y):
|
|
69
|
+
return y[0]
|
|
70
|
+
|
|
71
|
+
surface_event.terminal = True
|
|
72
|
+
surface_event.direction = -1
|
|
73
|
+
|
|
74
|
+
sol = solve_ivp(
|
|
75
|
+
rhs,
|
|
76
|
+
[xi0, xi_max],
|
|
77
|
+
[theta0, dtheta0],
|
|
78
|
+
events=surface_event,
|
|
79
|
+
dense_output=True,
|
|
80
|
+
rtol=1e-9,
|
|
81
|
+
atol=1e-11,
|
|
82
|
+
max_step=xi_max / 2000,
|
|
83
|
+
)
|
|
84
|
+
xi_surface = sol.t[-1]
|
|
85
|
+
xi = np.linspace(xi0, xi_surface, n_points)
|
|
86
|
+
theta = sol.sol(xi)[0]
|
|
87
|
+
theta = np.clip(theta, 0.0, None)
|
|
88
|
+
return xi, theta
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class PolytropicStar:
|
|
92
|
+
r"""A physical polytropic star built from one Lane-Emden solution.
|
|
93
|
+
|
|
94
|
+
Parameters
|
|
95
|
+
----------
|
|
96
|
+
n : float
|
|
97
|
+
Polytropic index, :math:`P=K\rho^{1+1/n}`.
|
|
98
|
+
K : float
|
|
99
|
+
Polytropic constant.
|
|
100
|
+
rho_c : float
|
|
101
|
+
Central density.
|
|
102
|
+
G : float, default=1.0
|
|
103
|
+
Gravitational constant.
|
|
104
|
+
|
|
105
|
+
Examples
|
|
106
|
+
--------
|
|
107
|
+
>>> star = PolytropicStar(n=0.0, K=1.0, rho_c=1.0)
|
|
108
|
+
>>> round(star.mass / (4 / 3 * np.pi * star.radius**3 * star.rho_c), 3)
|
|
109
|
+
1.0
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
def __init__(self, n, K, rho_c, G=1.0):
|
|
113
|
+
self.n = n
|
|
114
|
+
self.K = K
|
|
115
|
+
self.rho_c = rho_c
|
|
116
|
+
self.G = G
|
|
117
|
+
self._xi, self._theta = lane_emden(n)
|
|
118
|
+
|
|
119
|
+
@property
|
|
120
|
+
def xi1(self):
|
|
121
|
+
"""The dimensionless surface radius, the last point of the Lane-Emden solution."""
|
|
122
|
+
return float(self._xi[-1])
|
|
123
|
+
|
|
124
|
+
@property
|
|
125
|
+
def alpha(self):
|
|
126
|
+
r"""Physical length scale, :math:`\alpha=\sqrt{(n+1)K\rho_c^{1/n-1}/(4\pi G)}`."""
|
|
127
|
+
if self.n == 0:
|
|
128
|
+
exponent = -1.0
|
|
129
|
+
else:
|
|
130
|
+
exponent = 1.0 / self.n - 1.0
|
|
131
|
+
return float(np.sqrt((self.n + 1) * self.K * self.rho_c**exponent / (4.0 * np.pi * self.G)))
|
|
132
|
+
|
|
133
|
+
@property
|
|
134
|
+
def radius(self):
|
|
135
|
+
r"""Physical stellar radius, :math:`R=\alpha\xi_1`."""
|
|
136
|
+
return self.alpha * self.xi1
|
|
137
|
+
|
|
138
|
+
@property
|
|
139
|
+
def mass(self):
|
|
140
|
+
r"""Physical stellar mass, :math:`M=4\pi\alpha^3\rho_c\left[-\xi_1^2\theta'(\xi_1)\right]`."""
|
|
141
|
+
dtheta_surface = (self._theta[-1] - self._theta[-2]) / (self._xi[-1] - self._xi[-2])
|
|
142
|
+
return float(4.0 * np.pi * self.alpha**3 * self.rho_c * (-(self.xi1**2) * dtheta_surface))
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def chandrasekhar_mass(mu_e=2.0):
|
|
146
|
+
r"""The Chandrasekhar mass limit, in solar masses.
|
|
147
|
+
|
|
148
|
+
.. math::
|
|
149
|
+
|
|
150
|
+
M_{\rm Ch} \approx \frac{5.83}{\mu_e^2}\ M_\odot
|
|
151
|
+
|
|
152
|
+
(the standard numerical prefactor from the n=3 polytrope /
|
|
153
|
+
relativistic-electron-degeneracy treatment; see e.g. Kippenhahn,
|
|
154
|
+
Weigert & Weiss, *Stellar Structure and Evolution*).
|
|
155
|
+
|
|
156
|
+
Parameters
|
|
157
|
+
----------
|
|
158
|
+
mu_e : float, default=2.0
|
|
159
|
+
Mean molecular weight per electron (2.0 for carbon/oxygen white
|
|
160
|
+
dwarfs).
|
|
161
|
+
|
|
162
|
+
Returns
|
|
163
|
+
-------
|
|
164
|
+
float
|
|
165
|
+
Mass limit, in solar masses.
|
|
166
|
+
|
|
167
|
+
Examples
|
|
168
|
+
--------
|
|
169
|
+
>>> M = chandrasekhar_mass(2.0)
|
|
170
|
+
>>> 1.2 < M < 1.5
|
|
171
|
+
True
|
|
172
|
+
"""
|
|
173
|
+
return 5.83 / mu_e**2
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def main_sequence_luminosity(mass_solar):
|
|
177
|
+
r"""Empirical main-sequence mass-luminosity relation, :math:`L/L_\odot\approx(M/M_\odot)^{3.5}`.
|
|
178
|
+
|
|
179
|
+
Valid roughly for main-sequence stars in the 0.5-10 solar mass range;
|
|
180
|
+
this is a pure empirical power law, not derived from first-principles
|
|
181
|
+
stellar structure.
|
|
182
|
+
|
|
183
|
+
Parameters
|
|
184
|
+
----------
|
|
185
|
+
mass_solar : float
|
|
186
|
+
Mass, in solar masses.
|
|
187
|
+
|
|
188
|
+
Returns
|
|
189
|
+
-------
|
|
190
|
+
float
|
|
191
|
+
Luminosity, in solar luminosities.
|
|
192
|
+
|
|
193
|
+
Examples
|
|
194
|
+
--------
|
|
195
|
+
>>> round(main_sequence_luminosity(1.0), 6)
|
|
196
|
+
1.0
|
|
197
|
+
"""
|
|
198
|
+
return mass_solar**3.5
|
|
File without changes
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pytest
|
|
3
|
+
|
|
4
|
+
from physicskit.astro.cosmic_web import (
|
|
5
|
+
first_caustic_time,
|
|
6
|
+
lagrangian_grid,
|
|
7
|
+
random_displacement_potential,
|
|
8
|
+
zeldovich_displacement,
|
|
9
|
+
zeldovich_hessian_eigenvalues,
|
|
10
|
+
zeldovich_position,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
# For a single plane-wave potential Phi(q) = A*cos(kx*x + ky*y + phase), the
|
|
14
|
+
# Hessian is H = -A*cos(theta)*[[kx^2, kx*ky], [kx*ky, ky^2]] at every q,
|
|
15
|
+
# i.e. a rank-1 (outer-product) matrix scaled by c = -A*cos(theta). Its two
|
|
16
|
+
# eigenvalues are exactly {0, c*|k|^2}, so the *largest* eigenvalue at any
|
|
17
|
+
# given q is max(0, c*|k|^2) -- and this is maximized over q (giving the
|
|
18
|
+
# analytic first-caustic time 1/(A*|k|^2)) exactly where cos(theta) = -1,
|
|
19
|
+
# i.e. where kx*x + ky*y + phase = pi (mod 2*pi).
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_single_mode_hessian_eigenvalues_exact_at_compression_maximum():
|
|
23
|
+
"""At the specific q where the mode's phase argument equals pi (its
|
|
24
|
+
point of maximal compression), the Hessian eigenvalues are exactly
|
|
25
|
+
(0, A*|k|^2) -- a fully deterministic, hand-derivable check."""
|
|
26
|
+
kx, ky, A = 2 * np.pi, 0.0, 1.5
|
|
27
|
+
k_vectors = np.array([[kx, ky]])
|
|
28
|
+
amplitudes = np.array([A])
|
|
29
|
+
phases = np.array([np.pi]) # theta = kx*0 + ky*0 + pi = pi at q=(0,0)
|
|
30
|
+
|
|
31
|
+
q_star = np.array([[0.0, 0.0]])
|
|
32
|
+
eigs = zeldovich_hessian_eigenvalues(q_star, k_vectors, amplitudes, phases)
|
|
33
|
+
|
|
34
|
+
k_sq = kx**2 + ky**2
|
|
35
|
+
assert eigs.shape == (1, 2)
|
|
36
|
+
assert eigs[0, 0] == pytest.approx(0.0, abs=1e-12)
|
|
37
|
+
assert eigs[0, 1] == pytest.approx(A * k_sq, rel=1e-10)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_first_caustic_time_matches_single_mode_analytic_value_exactly():
|
|
41
|
+
"""first_caustic_time at that same single deterministic point should
|
|
42
|
+
equal the closed-form D_collapse = 1/(A*|k|^2) exactly."""
|
|
43
|
+
kx, ky, A = 2 * np.pi, 0.0, 1.5
|
|
44
|
+
k_vectors = np.array([[kx, ky]])
|
|
45
|
+
amplitudes = np.array([A])
|
|
46
|
+
phases = np.array([np.pi])
|
|
47
|
+
|
|
48
|
+
q_star = np.array([[0.0, 0.0]])
|
|
49
|
+
D_collapse = first_caustic_time(q_star, k_vectors, amplitudes, phases)
|
|
50
|
+
analytic = 1.0 / (A * (kx**2 + ky**2))
|
|
51
|
+
assert D_collapse == pytest.approx(analytic, rel=1e-10)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def test_first_caustic_time_converges_with_grid_resolution():
|
|
55
|
+
"""On a *generic* grid (the compression maximum does not sit exactly on
|
|
56
|
+
a grid point), first_caustic_time only finds the nearest sampled point
|
|
57
|
+
to the true maximum, so it overestimates D_collapse -- but the error
|
|
58
|
+
should shrink as the grid is refined and the sampling gets closer to
|
|
59
|
+
the true maximizing point."""
|
|
60
|
+
L = 1.0
|
|
61
|
+
kx, ky, A = 2 * np.pi / L, 0.0, 1.0
|
|
62
|
+
k_vectors = np.array([[kx, ky]])
|
|
63
|
+
amplitudes = np.array([A])
|
|
64
|
+
phases = np.array([0.7]) # arbitrary offset, not aligned with any grid spacing
|
|
65
|
+
analytic = 1.0 / (A * (kx**2 + ky**2))
|
|
66
|
+
|
|
67
|
+
q_coarse = lagrangian_grid(20, L)
|
|
68
|
+
q_fine = lagrangian_grid(400, L)
|
|
69
|
+
|
|
70
|
+
D_coarse = first_caustic_time(q_coarse, k_vectors, amplitudes, phases)
|
|
71
|
+
D_fine = first_caustic_time(q_fine, k_vectors, amplitudes, phases)
|
|
72
|
+
|
|
73
|
+
error_coarse = D_coarse - analytic
|
|
74
|
+
error_fine = D_fine - analytic
|
|
75
|
+
|
|
76
|
+
# The grid-based estimate can only ever undershoot the true max
|
|
77
|
+
# eigenvalue, so it can only ever overshoot D_collapse.
|
|
78
|
+
assert error_coarse >= 0.0
|
|
79
|
+
assert error_fine >= 0.0
|
|
80
|
+
assert error_fine < error_coarse
|
|
81
|
+
assert error_fine < 1e-5
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_zeldovich_position_at_zero_growth_returns_lagrangian_position():
|
|
85
|
+
k_vectors, amplitudes, phases = random_displacement_potential(6, k_min=1.0, k_max=4.0, amplitude_scale=0.2, seed=3)
|
|
86
|
+
q = lagrangian_grid(12, 2.0)
|
|
87
|
+
x0 = zeldovich_position(q, 0.0, k_vectors, amplitudes, phases)
|
|
88
|
+
assert x0 == pytest.approx(q, abs=1e-12)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def test_zeldovich_displacement_matches_finite_difference_gradient():
|
|
92
|
+
"""Sanity check independent of the eigenvalue-formula correctness:
|
|
93
|
+
the analytic gradient should match a numerical finite-difference
|
|
94
|
+
gradient of Phi evaluated by hand from the same plane-wave sum."""
|
|
95
|
+
rng = np.random.default_rng(42)
|
|
96
|
+
n_modes = 4
|
|
97
|
+
k_vectors = rng.uniform(-3.0, 3.0, size=(n_modes, 2))
|
|
98
|
+
amplitudes = rng.uniform(0.1, 1.0, size=n_modes)
|
|
99
|
+
phases = rng.uniform(0.0, 2 * np.pi, size=n_modes)
|
|
100
|
+
|
|
101
|
+
def phi(point):
|
|
102
|
+
theta = k_vectors @ point + phases
|
|
103
|
+
return float(np.sum(amplitudes * np.cos(theta)))
|
|
104
|
+
|
|
105
|
+
q_points = rng.uniform(-2.0, 2.0, size=(5, 2))
|
|
106
|
+
analytic_grad = zeldovich_displacement(q_points, k_vectors, amplitudes, phases)
|
|
107
|
+
|
|
108
|
+
h = 1e-6
|
|
109
|
+
for i, point in enumerate(q_points):
|
|
110
|
+
numeric_grad = np.zeros(2)
|
|
111
|
+
for d in range(2):
|
|
112
|
+
step = np.zeros(2)
|
|
113
|
+
step[d] = h
|
|
114
|
+
numeric_grad[d] = (phi(point + step) - phi(point - step)) / (2 * h)
|
|
115
|
+
assert analytic_grad[i] == pytest.approx(numeric_grad, abs=1e-5)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def test_shapes_and_no_nans():
|
|
119
|
+
k_vectors, amplitudes, phases = random_displacement_potential(5, k_min=1.0, k_max=3.0, amplitude_scale=0.1, seed=7)
|
|
120
|
+
q = lagrangian_grid(10, 2.0)
|
|
121
|
+
n = q.shape[0]
|
|
122
|
+
|
|
123
|
+
assert q.shape == (100, 2)
|
|
124
|
+
assert k_vectors.shape == (5, 2)
|
|
125
|
+
assert amplitudes.shape == (5,)
|
|
126
|
+
assert phases.shape == (5,)
|
|
127
|
+
|
|
128
|
+
grad = zeldovich_displacement(q, k_vectors, amplitudes, phases)
|
|
129
|
+
x = zeldovich_position(q, 0.5, k_vectors, amplitudes, phases)
|
|
130
|
+
eigs = zeldovich_hessian_eigenvalues(q, k_vectors, amplitudes, phases)
|
|
131
|
+
D_collapse = first_caustic_time(q, k_vectors, amplitudes, phases)
|
|
132
|
+
|
|
133
|
+
assert grad.shape == (n, 2)
|
|
134
|
+
assert x.shape == (n, 2)
|
|
135
|
+
assert eigs.shape == (n, 2)
|
|
136
|
+
assert np.all(eigs[:, 0] <= eigs[:, 1])
|
|
137
|
+
assert isinstance(D_collapse, float)
|
|
138
|
+
assert D_collapse > 0.0
|
|
139
|
+
|
|
140
|
+
assert not np.any(np.isnan(grad))
|
|
141
|
+
assert not np.any(np.isnan(x))
|
|
142
|
+
assert not np.any(np.isnan(eigs))
|
|
143
|
+
assert not np.isnan(D_collapse)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def test_random_displacement_potential_invalid_arguments_raise():
|
|
147
|
+
with pytest.raises(ValueError):
|
|
148
|
+
random_displacement_potential(4, k_min=1.0, k_max=2.0, amplitude_scale=0.1, ndim=3)
|
|
149
|
+
with pytest.raises(ValueError):
|
|
150
|
+
random_displacement_potential(0, k_min=1.0, k_max=2.0, amplitude_scale=0.1)
|
|
151
|
+
with pytest.raises(ValueError):
|
|
152
|
+
random_displacement_potential(4, k_min=2.0, k_max=1.0, amplitude_scale=0.1)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def test_lagrangian_grid_shape_and_bounds():
|
|
156
|
+
q = lagrangian_grid(7, 3.0)
|
|
157
|
+
assert q.shape == (49, 2)
|
|
158
|
+
assert np.all(q >= 0.0)
|
|
159
|
+
assert np.all(q < 3.0)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pytest
|
|
3
|
+
from scipy.integrate import quad
|
|
4
|
+
|
|
5
|
+
from physicskit.astro.galactic_dynamics import (
|
|
6
|
+
circular_velocity,
|
|
7
|
+
nfw_density,
|
|
8
|
+
nfw_enclosed_mass,
|
|
9
|
+
nfw_potential,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_nfw_enclosed_mass_matches_numerical_integral_of_density():
|
|
14
|
+
rho_s, r_s = 2.0, 3.0
|
|
15
|
+
for r in [1.0, 3.0, 10.0]:
|
|
16
|
+
expected, _ = quad(lambda rp: 4 * np.pi * rp**2 * nfw_density(rp, rho_s, r_s), 0, r)
|
|
17
|
+
assert nfw_enclosed_mass(r, rho_s, r_s) == pytest.approx(expected, rel=1e-4)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_nfw_enclosed_mass_monotonically_increasing():
|
|
21
|
+
rho_s, r_s = 1.0, 1.0
|
|
22
|
+
r = np.linspace(0.1, 20, 50)
|
|
23
|
+
M = nfw_enclosed_mass(r, rho_s, r_s)
|
|
24
|
+
assert np.all(np.diff(M) > 0)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_nfw_potential_derivative_matches_enclosed_mass():
|
|
28
|
+
rho_s, r_s, G = 1.5, 2.0, 1.0
|
|
29
|
+
r0 = 4.0
|
|
30
|
+
dr = 1e-5
|
|
31
|
+
dPhi_dr = (nfw_potential(r0 + dr, rho_s, r_s, G) - nfw_potential(r0 - dr, rho_s, r_s, G)) / (2 * dr)
|
|
32
|
+
expected = G * nfw_enclosed_mass(r0, rho_s, r_s) / r0**2
|
|
33
|
+
assert dPhi_dr == pytest.approx(expected, rel=1e-4)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_circular_velocity_point_mass_is_keplerian():
|
|
37
|
+
M0, G, r = 5.0, 1.0, 3.0
|
|
38
|
+
v = circular_velocity(r, lambda rr: M0, G=G)
|
|
39
|
+
assert v == pytest.approx(np.sqrt(G * M0 / r))
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_nfw_rotation_curve_is_roughly_flat_at_large_radius():
|
|
43
|
+
rho_s, r_s = 1.0, 1.0
|
|
44
|
+
r = np.linspace(5 * r_s, 30 * r_s, 50)
|
|
45
|
+
v = circular_velocity(r, lambda rr: nfw_enclosed_mass(rr, rho_s, r_s))
|
|
46
|
+
assert np.std(v) / np.mean(v) < 0.2
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pytest
|
|
3
|
+
|
|
4
|
+
from physicskit.astro.nbody import NBodySystem, figure_eight_initial_conditions, gravitational_acceleration, leapfrog_step
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_gravitational_acceleration_two_body():
|
|
8
|
+
positions = np.array([[0.0, 0.0, 0.0], [1.0, 0.0, 0.0]])
|
|
9
|
+
masses = np.array([1.0, 1.0])
|
|
10
|
+
acc = gravitational_acceleration(positions, masses, G=1.0)
|
|
11
|
+
assert acc[0] == pytest.approx([1.0, 0.0, 0.0], abs=1e-10)
|
|
12
|
+
assert acc[1] == pytest.approx([-1.0, 0.0, 0.0], abs=1e-10)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_leapfrog_step_shapes():
|
|
16
|
+
positions = np.random.default_rng(0).normal(size=(4, 3))
|
|
17
|
+
velocities = np.zeros((4, 3))
|
|
18
|
+
masses = np.ones(4)
|
|
19
|
+
new_pos, new_vel = leapfrog_step(positions, velocities, masses, dt=0.01)
|
|
20
|
+
assert new_pos.shape == (4, 3)
|
|
21
|
+
assert new_vel.shape == (4, 3)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _circular_two_body_system(G=1.0, m=1.0, d=2.0):
|
|
25
|
+
v = np.sqrt(G * m / (2 * d))
|
|
26
|
+
positions = np.array([[d / 2, 0.0, 0.0], [-d / 2, 0.0, 0.0]])
|
|
27
|
+
velocities = np.array([[0.0, v, 0.0], [0.0, -v, 0.0]])
|
|
28
|
+
masses = np.array([m, m])
|
|
29
|
+
return NBodySystem(positions, velocities, masses, G=G), v, d
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_two_body_circular_orbit_conserves_energy_and_angular_momentum():
|
|
33
|
+
system, v, d = _circular_two_body_system()
|
|
34
|
+
E0 = system.total_energy()
|
|
35
|
+
L0 = system.total_angular_momentum()
|
|
36
|
+
|
|
37
|
+
period = 2 * np.pi * (d / 2) / v
|
|
38
|
+
n_steps = 2000
|
|
39
|
+
dt = period * 5 / n_steps # five orbits
|
|
40
|
+
system.simulate(dt, n_steps)
|
|
41
|
+
|
|
42
|
+
E1 = system.total_energy()
|
|
43
|
+
L1 = system.total_angular_momentum()
|
|
44
|
+
assert E1 == pytest.approx(E0, rel=1e-3)
|
|
45
|
+
assert L1 == pytest.approx(L0, rel=1e-3)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_two_body_orbit_returns_near_starting_position_after_one_period():
|
|
49
|
+
system, v, d = _circular_two_body_system()
|
|
50
|
+
period = 2 * np.pi * (d / 2) / v
|
|
51
|
+
n_steps = 500
|
|
52
|
+
dt = period / n_steps
|
|
53
|
+
history = system.simulate(dt, n_steps)
|
|
54
|
+
assert history[-1] == pytest.approx(history[0], abs=1e-2)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_figure_eight_initial_conditions_has_zero_net_momentum():
|
|
58
|
+
"""The choreography's whole premise is that the three equal masses
|
|
59
|
+
share a common center of mass at rest; a nonzero net momentum would
|
|
60
|
+
mean the figure drifts rather than retracing itself in place."""
|
|
61
|
+
positions, velocities, masses = figure_eight_initial_conditions()
|
|
62
|
+
momentum = np.sum(masses[:, None] * velocities, axis=0)
|
|
63
|
+
assert momentum == pytest.approx([0.0, 0.0, 0.0], abs=1e-8)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_figure_eight_initial_conditions_is_periodic_and_conserves_energy():
|
|
67
|
+
"""Integrating for one full period (T ~ 6.32591398, the well-known
|
|
68
|
+
value for this choreography) should return each body close to its
|
|
69
|
+
starting position, with energy conserved by the symplectic leapfrog
|
|
70
|
+
integrator."""
|
|
71
|
+
positions, velocities, masses = figure_eight_initial_conditions()
|
|
72
|
+
system = NBodySystem(positions, velocities, masses)
|
|
73
|
+
E0 = system.total_energy()
|
|
74
|
+
|
|
75
|
+
period = 6.32591398
|
|
76
|
+
n_steps = 20000
|
|
77
|
+
dt = period / n_steps
|
|
78
|
+
system.simulate(dt, n_steps)
|
|
79
|
+
|
|
80
|
+
assert system.total_energy() == pytest.approx(E0, rel=1e-6)
|
|
81
|
+
assert system.positions == pytest.approx(positions, abs=1e-2)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pytest
|
|
3
|
+
|
|
4
|
+
from physicskit.astro.orbital_mechanics import (
|
|
5
|
+
hohmann_transfer,
|
|
6
|
+
orbital_elements_from_state,
|
|
7
|
+
orbital_period,
|
|
8
|
+
state_from_orbital_elements,
|
|
9
|
+
vis_viva_speed,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_orbital_period_matches_kepler_third_law():
|
|
14
|
+
mu = 1.0
|
|
15
|
+
a = 2.0
|
|
16
|
+
T = orbital_period(a, mu)
|
|
17
|
+
assert T == pytest.approx(2 * np.pi * np.sqrt(a**3 / mu))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_vis_viva_parabolic_limit():
|
|
21
|
+
v = vis_viva_speed(r=1.0, a=np.inf, mu=1.0)
|
|
22
|
+
assert v == pytest.approx(np.sqrt(2.0))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_vis_viva_circular_orbit_matches_period():
|
|
26
|
+
mu = 1.0
|
|
27
|
+
r = 3.0
|
|
28
|
+
v = vis_viva_speed(r, a=r, mu=mu)
|
|
29
|
+
period_from_v = 2 * np.pi * r / v
|
|
30
|
+
assert period_from_v == pytest.approx(orbital_period(r, mu))
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
ELEMENT_CASES = [
|
|
34
|
+
(1.0, 0.3, 0.5, 1.0, 0.7, 1.2),
|
|
35
|
+
(2.0, 0.1, 1.2, 2.5, 0.3, 4.0),
|
|
36
|
+
(1.5, 0.5, 0.9, 0.2, 2.1, 0.5),
|
|
37
|
+
(1.5, 0.4, 0.8, 4.0, 4.0, 1.0), # n_vec[1]<0 and e_vec[2]<0 branches of raan/argp
|
|
38
|
+
(1.5, 0.4, 0.0, 0.0, 0.0, 1.0), # equatorial (n<=1e-12): raan, argp forced to 0
|
|
39
|
+
(1.5, 0.0, 0.8, 1.0, 0.0, 0.0), # circular (e<=1e-12): argp, nu forced to 0
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@pytest.mark.parametrize("a,e,i,raan,argp,nu", ELEMENT_CASES)
|
|
44
|
+
def test_orbital_elements_roundtrip(a, e, i, raan, argp, nu):
|
|
45
|
+
mu = 1.0
|
|
46
|
+
r_vec, v_vec = state_from_orbital_elements(a, e, i, raan, argp, nu, mu)
|
|
47
|
+
a2, e2, i2, raan2, argp2, nu2 = orbital_elements_from_state(r_vec, v_vec, mu)
|
|
48
|
+
assert a2 == pytest.approx(a, rel=1e-8)
|
|
49
|
+
assert e2 == pytest.approx(e, rel=1e-8)
|
|
50
|
+
assert i2 == pytest.approx(i, rel=1e-8)
|
|
51
|
+
assert raan2 == pytest.approx(raan % (2 * np.pi), rel=1e-8)
|
|
52
|
+
assert argp2 == pytest.approx(argp % (2 * np.pi), rel=1e-8)
|
|
53
|
+
assert nu2 == pytest.approx(nu % (2 * np.pi), rel=1e-8)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_hohmann_transfer_outward_deltav_matches_speed_differences():
|
|
57
|
+
mu, r1, r2 = 1.0, 1.0, 4.0
|
|
58
|
+
a_t = (r1 + r2) / 2.0
|
|
59
|
+
dv1, dv2, t = hohmann_transfer(r1, r2, mu)
|
|
60
|
+
expected_dv1 = vis_viva_speed(r1, a_t, mu) - vis_viva_speed(r1, r1, mu)
|
|
61
|
+
expected_dv2 = vis_viva_speed(r2, r2, mu) - vis_viva_speed(r2, a_t, mu)
|
|
62
|
+
assert dv1 == pytest.approx(abs(expected_dv1))
|
|
63
|
+
assert dv2 == pytest.approx(abs(expected_dv2))
|
|
64
|
+
assert dv1 > 0
|
|
65
|
+
assert dv2 > 0
|
|
66
|
+
assert t == pytest.approx(orbital_period(a_t, mu) / 2.0)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def test_hohmann_transfer_inward_is_positive_magnitudes():
|
|
70
|
+
dv1, dv2, t = hohmann_transfer(4.0, 1.0, mu=1.0)
|
|
71
|
+
assert dv1 > 0
|
|
72
|
+
assert dv2 > 0
|
|
73
|
+
assert t > 0
|