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,199 @@
|
|
|
1
|
+
"""Adaptive-step-size integration: embedded Dormand-Prince RK5(4).
|
|
2
|
+
|
|
3
|
+
This is deliberately offered only for the non-symplectic RK4-style path
|
|
4
|
+
(``rhs(state, t, params) -> ndarray``), not for the symplectic
|
|
5
|
+
:func:`~physicskit.integrators.fixed_step.leapfrog_step` /
|
|
6
|
+
:func:`~physicskit.integrators.fixed_step.yoshida4_step` family. Those
|
|
7
|
+
integrators conserve energy over long integrations *because* they take a
|
|
8
|
+
fixed step; naively varying ``dt`` from an embedded error estimate breaks
|
|
9
|
+
that guarantee; a genuinely adaptive symplectic scheme needs a reversible
|
|
10
|
+
step-size controller (see Hairer, Lubich & Wanner, *Geometric Numerical
|
|
11
|
+
Integration*), which is a different algorithm, not a drop-in extension of
|
|
12
|
+
this one. Use :func:`dopri5_integrate` for non-conservative, driven, or
|
|
13
|
+
stiff-ish systems where accuracy control matters more than exact energy
|
|
14
|
+
conservation; use the fixed-step symplectic integrators for long-time
|
|
15
|
+
Hamiltonian evolution.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import numpy as np
|
|
21
|
+
from numba import njit
|
|
22
|
+
from numpy.typing import NDArray
|
|
23
|
+
|
|
24
|
+
from physicskit.integrators.fixed_step import RHSFunc
|
|
25
|
+
|
|
26
|
+
__all__ = ["dopri5_step", "dopri5_integrate"]
|
|
27
|
+
|
|
28
|
+
# Dormand & Prince (1980) 5(4) coefficients (the classic "ode45" tableau).
|
|
29
|
+
_C2, _C3, _C4, _C5 = 1.0 / 5.0, 3.0 / 10.0, 4.0 / 5.0, 8.0 / 9.0
|
|
30
|
+
|
|
31
|
+
_A21 = 1.0 / 5.0
|
|
32
|
+
_A31, _A32 = 3.0 / 40.0, 9.0 / 40.0
|
|
33
|
+
_A41, _A42, _A43 = 44.0 / 45.0, -56.0 / 15.0, 32.0 / 9.0
|
|
34
|
+
_A51, _A52, _A53, _A54 = 19372.0 / 6561.0, -25360.0 / 2187.0, 64448.0 / 6561.0, -212.0 / 729.0
|
|
35
|
+
_A61, _A62, _A63, _A64, _A65 = 9017.0 / 3168.0, -355.0 / 33.0, 46732.0 / 5247.0, 49.0 / 176.0, -5103.0 / 18656.0
|
|
36
|
+
|
|
37
|
+
# 5th-order solution weights (= 7th-stage row, i.e. this method is FSAL).
|
|
38
|
+
_B1, _B3, _B4, _B5, _B6 = 35.0 / 384.0, 500.0 / 1113.0, 125.0 / 192.0, -2187.0 / 6784.0, 11.0 / 84.0
|
|
39
|
+
# Embedded 4th-order weights, for the error estimate y5 - y4.
|
|
40
|
+
_B1S, _B3S, _B4S, _B5S, _B6S, _B7S = (
|
|
41
|
+
5179.0 / 57600.0,
|
|
42
|
+
7571.0 / 16695.0,
|
|
43
|
+
393.0 / 640.0,
|
|
44
|
+
-92097.0 / 339200.0,
|
|
45
|
+
187.0 / 2100.0,
|
|
46
|
+
1.0 / 40.0,
|
|
47
|
+
)
|
|
48
|
+
_E1 = _B1 - _B1S
|
|
49
|
+
_E3 = _B3 - _B3S
|
|
50
|
+
_E4 = _B4 - _B4S
|
|
51
|
+
_E5 = _B5 - _B5S
|
|
52
|
+
_E6 = _B6 - _B6S
|
|
53
|
+
_E7 = -_B7S
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@njit(cache=True)
|
|
57
|
+
def dopri5_step(
|
|
58
|
+
rhs: RHSFunc,
|
|
59
|
+
state: NDArray[np.float64],
|
|
60
|
+
t: float,
|
|
61
|
+
dt: float,
|
|
62
|
+
params: NDArray[np.float64],
|
|
63
|
+
) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
|
|
64
|
+
"""Single embedded Dormand-Prince RK5(4) step, with an error estimate.
|
|
65
|
+
|
|
66
|
+
Parameters
|
|
67
|
+
----------
|
|
68
|
+
rhs : callable
|
|
69
|
+
Numba-jitted right-hand-side function ``rhs(state, t, params) ->
|
|
70
|
+
ndarray``.
|
|
71
|
+
state : ndarray of float, shape (dim,)
|
|
72
|
+
Current state vector.
|
|
73
|
+
t : float
|
|
74
|
+
Current time.
|
|
75
|
+
dt : float
|
|
76
|
+
Step size to attempt.
|
|
77
|
+
params : ndarray of float
|
|
78
|
+
Parameter vector passed through to `rhs`.
|
|
79
|
+
|
|
80
|
+
Returns
|
|
81
|
+
-------
|
|
82
|
+
state_new : ndarray of float, shape (dim,)
|
|
83
|
+
The 5th-order state estimate at ``t + dt``.
|
|
84
|
+
error : ndarray of float, shape (dim,)
|
|
85
|
+
Difference between the 5th- and embedded 4th-order estimates, for
|
|
86
|
+
step-size control (see :func:`dopri5_integrate`).
|
|
87
|
+
"""
|
|
88
|
+
k1 = rhs(state, t, params)
|
|
89
|
+
k2 = rhs(state + dt * (_A21 * k1), t + _C2 * dt, params)
|
|
90
|
+
k3 = rhs(state + dt * (_A31 * k1 + _A32 * k2), t + _C3 * dt, params)
|
|
91
|
+
k4 = rhs(state + dt * (_A41 * k1 + _A42 * k2 + _A43 * k3), t + _C4 * dt, params)
|
|
92
|
+
k5 = rhs(state + dt * (_A51 * k1 + _A52 * k2 + _A53 * k3 + _A54 * k4), t + _C5 * dt, params)
|
|
93
|
+
k6 = rhs(state + dt * (_A61 * k1 + _A62 * k2 + _A63 * k3 + _A64 * k4 + _A65 * k5), t + dt, params)
|
|
94
|
+
state_new = state + dt * (_B1 * k1 + _B3 * k3 + _B4 * k4 + _B5 * k5 + _B6 * k6)
|
|
95
|
+
k7 = rhs(state_new, t + dt, params) # FSAL: also the first stage of the next step
|
|
96
|
+
error = dt * (_E1 * k1 + _E3 * k3 + _E4 * k4 + _E5 * k5 + _E6 * k6 + _E7 * k7)
|
|
97
|
+
return state_new, error
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@njit(cache=True)
|
|
101
|
+
def dopri5_integrate(
|
|
102
|
+
rhs: RHSFunc,
|
|
103
|
+
state0: NDArray[np.float64],
|
|
104
|
+
t0: float,
|
|
105
|
+
t_end: float,
|
|
106
|
+
dt0: float,
|
|
107
|
+
params: NDArray[np.float64],
|
|
108
|
+
rtol: float = 1e-6,
|
|
109
|
+
atol: float = 1e-9,
|
|
110
|
+
dt_min: float = 1e-12,
|
|
111
|
+
dt_max: float = 1e6,
|
|
112
|
+
safety: float = 0.9,
|
|
113
|
+
max_steps: int = 100_000,
|
|
114
|
+
) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
|
|
115
|
+
"""Integrate forward from ``t0`` to ``t_end`` with adaptive step-size control.
|
|
116
|
+
|
|
117
|
+
Steps are accepted or rejected from the embedded RK5(4) error estimate
|
|
118
|
+
against an ``atol + rtol * |state|`` tolerance (the same convention as
|
|
119
|
+
:func:`scipy.integrate.solve_ivp`), with the step size adjusted after
|
|
120
|
+
every attempt. Only forward integration (``t_end > t0``, ``dt0 > 0``) is
|
|
121
|
+
supported.
|
|
122
|
+
|
|
123
|
+
Parameters
|
|
124
|
+
----------
|
|
125
|
+
rhs : callable
|
|
126
|
+
Numba-jitted right-hand-side function ``rhs(state, t, params) ->
|
|
127
|
+
ndarray``.
|
|
128
|
+
state0 : ndarray of float, shape (dim,)
|
|
129
|
+
Initial state vector.
|
|
130
|
+
t0, t_end : float
|
|
131
|
+
Integration interval, with ``t_end > t0``.
|
|
132
|
+
dt0 : float
|
|
133
|
+
Initial step size to attempt.
|
|
134
|
+
params : ndarray of float
|
|
135
|
+
Parameter vector passed through to `rhs`.
|
|
136
|
+
rtol, atol : float
|
|
137
|
+
Relative and absolute error tolerances.
|
|
138
|
+
dt_min, dt_max : float
|
|
139
|
+
Step-size bounds; a step is accepted once ``dt`` shrinks to
|
|
140
|
+
`dt_min` regardless of its error estimate, to guarantee progress.
|
|
141
|
+
safety : float, default=0.9
|
|
142
|
+
Safety factor applied to the step-size update.
|
|
143
|
+
max_steps : int
|
|
144
|
+
Upper bound on the number of attempted steps (accepted or
|
|
145
|
+
rejected), to guarantee termination.
|
|
146
|
+
|
|
147
|
+
Returns
|
|
148
|
+
-------
|
|
149
|
+
times : ndarray of float, shape (n_accepted + 1,)
|
|
150
|
+
Times of the accepted steps, starting at `t0` and ending at
|
|
151
|
+
`t_end`.
|
|
152
|
+
states : ndarray of float, shape (n_accepted + 1, dim)
|
|
153
|
+
State at each accepted step.
|
|
154
|
+
"""
|
|
155
|
+
dim = state0.shape[0]
|
|
156
|
+
cap = 1024
|
|
157
|
+
times = np.empty(cap)
|
|
158
|
+
states = np.empty((cap, dim))
|
|
159
|
+
times[0] = t0
|
|
160
|
+
states[0] = state0
|
|
161
|
+
|
|
162
|
+
t = t0
|
|
163
|
+
state = state0.copy()
|
|
164
|
+
dt = dt0
|
|
165
|
+
count = 0
|
|
166
|
+
for _ in range(max_steps):
|
|
167
|
+
if t >= t_end:
|
|
168
|
+
break
|
|
169
|
+
if t + dt > t_end:
|
|
170
|
+
dt = t_end - t
|
|
171
|
+
|
|
172
|
+
state_new, error = dopri5_step(rhs, state, t, dt, params)
|
|
173
|
+
scale = atol + rtol * np.maximum(np.abs(state), np.abs(state_new))
|
|
174
|
+
err_norm = np.sqrt(np.mean((error / scale) ** 2))
|
|
175
|
+
|
|
176
|
+
if err_norm <= 1.0 or dt <= dt_min:
|
|
177
|
+
t = t + dt
|
|
178
|
+
state = state_new
|
|
179
|
+
count += 1
|
|
180
|
+
if count >= cap:
|
|
181
|
+
new_cap = cap * 2
|
|
182
|
+
new_times = np.empty(new_cap)
|
|
183
|
+
new_states = np.empty((new_cap, dim))
|
|
184
|
+
new_times[:cap] = times
|
|
185
|
+
new_states[:cap] = states
|
|
186
|
+
times = new_times
|
|
187
|
+
states = new_states
|
|
188
|
+
cap = new_cap
|
|
189
|
+
times[count] = t
|
|
190
|
+
states[count] = state
|
|
191
|
+
|
|
192
|
+
if err_norm == 0.0:
|
|
193
|
+
factor = 5.0
|
|
194
|
+
else:
|
|
195
|
+
factor = safety * err_norm ** (-0.2)
|
|
196
|
+
factor = min(max(factor, 0.2), 5.0)
|
|
197
|
+
dt = min(max(dt * factor, dt_min), dt_max)
|
|
198
|
+
|
|
199
|
+
return times[: count + 1], states[: count + 1]
|
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
"""Numba-accelerated fixed-step time integrators, shared across subpackages.
|
|
2
|
+
|
|
3
|
+
Right-hand-side functions passed to these integrators must be module-level
|
|
4
|
+
``@njit`` functions with the signature ``rhs(state, t, params) -> ndarray``
|
|
5
|
+
(for :func:`rk4_step` / :func:`rk4_integrate`) or ``force(pos, t, params) ->
|
|
6
|
+
ndarray`` (for :func:`leapfrog_step` / :func:`leapfrog_integrate` /
|
|
7
|
+
:func:`yoshida4_step` / :func:`yoshida4_integrate`). Keeping ``params`` as an
|
|
8
|
+
explicit ``float64`` array (rather than closing over Python scalars) lets
|
|
9
|
+
Numba compile these integrators once and reuse them, as first-class
|
|
10
|
+
functions, across many differently-parameterized systems -- e.g. sweeping a
|
|
11
|
+
grid of parameter values without recompiling per value.
|
|
12
|
+
|
|
13
|
+
The two symplectic integrators (:func:`leapfrog_step`, 2nd order, and
|
|
14
|
+
:func:`yoshida4_step`, 4th order) only apply to *separable* Hamiltonian
|
|
15
|
+
systems of the form ``pos'' = force(pos, t)`` (the force may not depend on
|
|
16
|
+
velocity); RK4 is required for non-separable systems (e.g. anything with a
|
|
17
|
+
Coriolis-like velocity-dependent force).
|
|
18
|
+
|
|
19
|
+
Callers that need a closure-based calling convention instead (a fixed
|
|
20
|
+
``force_func(q, t)`` plus a per-instance ``mass_inv``, as used by
|
|
21
|
+
:mod:`physicskit.classical.core.integrators` for momentum-parameterized
|
|
22
|
+
Hamiltonian systems) are not a good fit for the ``params``-array convention
|
|
23
|
+
here -- baking ``mass_inv`` into a per-step closure only pays off when the
|
|
24
|
+
closure is built once per system instance, which conflicts with a runtime
|
|
25
|
+
``params`` array meant to be swept without recompilation. That subpackage
|
|
26
|
+
keeps its own small, independently-tested step implementations rather than
|
|
27
|
+
adapting to this one.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from __future__ import annotations
|
|
31
|
+
|
|
32
|
+
from collections.abc import Callable
|
|
33
|
+
|
|
34
|
+
import numpy as np
|
|
35
|
+
from numba import njit
|
|
36
|
+
from numpy.typing import NDArray
|
|
37
|
+
|
|
38
|
+
__all__ = [
|
|
39
|
+
"RHSFunc",
|
|
40
|
+
"rk4_step",
|
|
41
|
+
"rk4_integrate",
|
|
42
|
+
"leapfrog_step",
|
|
43
|
+
"leapfrog_integrate",
|
|
44
|
+
"velocity_verlet_step",
|
|
45
|
+
"velocity_verlet_integrate",
|
|
46
|
+
"yoshida4_step",
|
|
47
|
+
"yoshida4_integrate",
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
#: Signature shared by every right-hand-side / force function accepted by
|
|
51
|
+
#: the integrators below: ``f(state, t, params) -> derivative``.
|
|
52
|
+
RHSFunc = Callable[[NDArray[np.float64], float, NDArray[np.float64]], NDArray[np.float64]]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@njit(cache=True)
|
|
56
|
+
def rk4_step(
|
|
57
|
+
rhs: RHSFunc,
|
|
58
|
+
state: NDArray[np.float64],
|
|
59
|
+
t: float,
|
|
60
|
+
dt: float,
|
|
61
|
+
params: NDArray[np.float64],
|
|
62
|
+
) -> NDArray[np.float64]:
|
|
63
|
+
"""Single classical 4th-order Runge-Kutta step.
|
|
64
|
+
|
|
65
|
+
Parameters
|
|
66
|
+
----------
|
|
67
|
+
rhs : callable
|
|
68
|
+
Numba-jitted right-hand-side function ``rhs(state, t, params) ->
|
|
69
|
+
ndarray``.
|
|
70
|
+
state : ndarray of float, shape (dim,)
|
|
71
|
+
Current state vector.
|
|
72
|
+
t : float
|
|
73
|
+
Current time.
|
|
74
|
+
dt : float
|
|
75
|
+
Step size.
|
|
76
|
+
params : ndarray of float
|
|
77
|
+
Parameter vector passed through to `rhs`.
|
|
78
|
+
|
|
79
|
+
Returns
|
|
80
|
+
-------
|
|
81
|
+
ndarray of float, shape (dim,)
|
|
82
|
+
The state advanced by one step of size `dt`.
|
|
83
|
+
"""
|
|
84
|
+
k1 = rhs(state, t, params)
|
|
85
|
+
k2 = rhs(state + 0.5 * dt * k1, t + 0.5 * dt, params)
|
|
86
|
+
k3 = rhs(state + 0.5 * dt * k2, t + 0.5 * dt, params)
|
|
87
|
+
k4 = rhs(state + dt * k3, t + dt, params)
|
|
88
|
+
return state + (dt / 6.0) * (k1 + 2.0 * k2 + 2.0 * k3 + k4)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@njit(cache=True)
|
|
92
|
+
def rk4_integrate(
|
|
93
|
+
rhs: RHSFunc,
|
|
94
|
+
state0: NDArray[np.float64],
|
|
95
|
+
t0: float,
|
|
96
|
+
dt: float,
|
|
97
|
+
n_steps: int,
|
|
98
|
+
params: NDArray[np.float64],
|
|
99
|
+
) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
|
|
100
|
+
"""Integrate ``n_steps`` of RK4 starting from ``state0``.
|
|
101
|
+
|
|
102
|
+
Parameters
|
|
103
|
+
----------
|
|
104
|
+
rhs : callable
|
|
105
|
+
Numba-jitted right-hand-side function ``rhs(state, t, params) ->
|
|
106
|
+
ndarray``.
|
|
107
|
+
state0 : ndarray of float, shape (dim,)
|
|
108
|
+
Initial state vector.
|
|
109
|
+
t0 : float
|
|
110
|
+
Initial time.
|
|
111
|
+
dt : float
|
|
112
|
+
Step size.
|
|
113
|
+
n_steps : int
|
|
114
|
+
Number of integration steps.
|
|
115
|
+
params : ndarray of float
|
|
116
|
+
Parameter vector passed through to `rhs`.
|
|
117
|
+
|
|
118
|
+
Returns
|
|
119
|
+
-------
|
|
120
|
+
times : ndarray of float, shape (n_steps + 1,)
|
|
121
|
+
Time at each step, starting at `t0`.
|
|
122
|
+
states : ndarray of float, shape (n_steps + 1, dim)
|
|
123
|
+
State at each step, starting at `state0`.
|
|
124
|
+
"""
|
|
125
|
+
dim = state0.shape[0]
|
|
126
|
+
states = np.empty((n_steps + 1, dim))
|
|
127
|
+
times = np.empty(n_steps + 1)
|
|
128
|
+
states[0] = state0
|
|
129
|
+
times[0] = t0
|
|
130
|
+
state = state0.copy()
|
|
131
|
+
t = t0
|
|
132
|
+
for i in range(n_steps):
|
|
133
|
+
state = rk4_step(rhs, state, t, dt, params)
|
|
134
|
+
t = t0 + (i + 1) * dt
|
|
135
|
+
states[i + 1] = state
|
|
136
|
+
times[i + 1] = t
|
|
137
|
+
return times, states
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
@njit(cache=True)
|
|
141
|
+
def leapfrog_step(
|
|
142
|
+
force: RHSFunc,
|
|
143
|
+
pos: NDArray[np.float64],
|
|
144
|
+
vel: NDArray[np.float64],
|
|
145
|
+
t: float,
|
|
146
|
+
dt: float,
|
|
147
|
+
params: NDArray[np.float64],
|
|
148
|
+
) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
|
|
149
|
+
"""Single velocity-Verlet (symplectic) step for ``pos'' = force(pos, t)``.
|
|
150
|
+
|
|
151
|
+
Parameters
|
|
152
|
+
----------
|
|
153
|
+
force : callable
|
|
154
|
+
Numba-jitted acceleration function ``force(pos, t, params) ->
|
|
155
|
+
ndarray``.
|
|
156
|
+
pos : ndarray of float, shape (dim,)
|
|
157
|
+
Current position vector.
|
|
158
|
+
vel : ndarray of float, shape (dim,)
|
|
159
|
+
Current velocity vector.
|
|
160
|
+
t : float
|
|
161
|
+
Current time.
|
|
162
|
+
dt : float
|
|
163
|
+
Step size.
|
|
164
|
+
params : ndarray of float
|
|
165
|
+
Parameter vector passed through to `force`.
|
|
166
|
+
|
|
167
|
+
Returns
|
|
168
|
+
-------
|
|
169
|
+
pos_new : ndarray of float, shape (dim,)
|
|
170
|
+
Position advanced by one step of size `dt`.
|
|
171
|
+
vel_new : ndarray of float, shape (dim,)
|
|
172
|
+
Velocity advanced by one step of size `dt`.
|
|
173
|
+
"""
|
|
174
|
+
a0 = force(pos, t, params)
|
|
175
|
+
pos_new = pos + vel * dt + 0.5 * a0 * dt * dt
|
|
176
|
+
a1 = force(pos_new, t + dt, params)
|
|
177
|
+
vel_new = vel + 0.5 * (a0 + a1) * dt
|
|
178
|
+
return pos_new, vel_new
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
@njit(cache=True)
|
|
182
|
+
def leapfrog_integrate(
|
|
183
|
+
force: RHSFunc,
|
|
184
|
+
pos0: NDArray[np.float64],
|
|
185
|
+
vel0: NDArray[np.float64],
|
|
186
|
+
t0: float,
|
|
187
|
+
dt: float,
|
|
188
|
+
n_steps: int,
|
|
189
|
+
params: NDArray[np.float64],
|
|
190
|
+
) -> tuple[NDArray[np.float64], NDArray[np.float64], NDArray[np.float64]]:
|
|
191
|
+
"""Integrate ``n_steps`` of the symplectic leapfrog scheme.
|
|
192
|
+
|
|
193
|
+
Parameters
|
|
194
|
+
----------
|
|
195
|
+
force : callable
|
|
196
|
+
Numba-jitted acceleration function ``force(pos, t, params) ->
|
|
197
|
+
ndarray``.
|
|
198
|
+
pos0 : ndarray of float, shape (dim,)
|
|
199
|
+
Initial position vector.
|
|
200
|
+
vel0 : ndarray of float, shape (dim,)
|
|
201
|
+
Initial velocity vector.
|
|
202
|
+
t0 : float
|
|
203
|
+
Initial time.
|
|
204
|
+
dt : float
|
|
205
|
+
Step size.
|
|
206
|
+
n_steps : int
|
|
207
|
+
Number of integration steps.
|
|
208
|
+
params : ndarray of float
|
|
209
|
+
Parameter vector passed through to `force`.
|
|
210
|
+
|
|
211
|
+
Returns
|
|
212
|
+
-------
|
|
213
|
+
times : ndarray of float, shape (n_steps + 1,)
|
|
214
|
+
Time at each step, starting at `t0`.
|
|
215
|
+
positions : ndarray of float, shape (n_steps + 1, dim)
|
|
216
|
+
Position at each step, starting at `pos0`.
|
|
217
|
+
velocities : ndarray of float, shape (n_steps + 1, dim)
|
|
218
|
+
Velocity at each step, starting at `vel0`.
|
|
219
|
+
"""
|
|
220
|
+
dim = pos0.shape[0]
|
|
221
|
+
positions = np.empty((n_steps + 1, dim))
|
|
222
|
+
velocities = np.empty((n_steps + 1, dim))
|
|
223
|
+
times = np.empty(n_steps + 1)
|
|
224
|
+
positions[0] = pos0
|
|
225
|
+
velocities[0] = vel0
|
|
226
|
+
times[0] = t0
|
|
227
|
+
pos = pos0.copy()
|
|
228
|
+
vel = vel0.copy()
|
|
229
|
+
t = t0
|
|
230
|
+
for i in range(n_steps):
|
|
231
|
+
pos, vel = leapfrog_step(force, pos, vel, t, dt, params)
|
|
232
|
+
t = t0 + (i + 1) * dt
|
|
233
|
+
positions[i + 1] = pos
|
|
234
|
+
velocities[i + 1] = vel
|
|
235
|
+
times[i + 1] = t
|
|
236
|
+
return times, positions, velocities
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
#: ``leapfrog_step``/``leapfrog_integrate`` under the name used by
|
|
240
|
+
#: gravitational-dynamics and molecular-dynamics literature, where this same
|
|
241
|
+
#: kick-drift-kick scheme is usually called "velocity Verlet".
|
|
242
|
+
velocity_verlet_step = leapfrog_step
|
|
243
|
+
velocity_verlet_integrate = leapfrog_integrate
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
#: Yoshida (1990) 4th-order composition coefficients: the 4th-order step is
|
|
247
|
+
#: three 2nd-order (leapfrog) sub-steps of sizes ``w1*dt``, ``w0*dt``,
|
|
248
|
+
#: ``w1*dt`` (which sum to ``dt``, since ``2*w1 + w0 == 1``).
|
|
249
|
+
_YOSHIDA_CBRT2 = 2.0 ** (1.0 / 3.0)
|
|
250
|
+
_YOSHIDA_W0 = -_YOSHIDA_CBRT2 / (2.0 - _YOSHIDA_CBRT2)
|
|
251
|
+
_YOSHIDA_W1 = 1.0 / (2.0 - _YOSHIDA_CBRT2)
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
@njit(cache=True)
|
|
255
|
+
def yoshida4_step(
|
|
256
|
+
force: RHSFunc,
|
|
257
|
+
pos: NDArray[np.float64],
|
|
258
|
+
vel: NDArray[np.float64],
|
|
259
|
+
t: float,
|
|
260
|
+
dt: float,
|
|
261
|
+
params: NDArray[np.float64],
|
|
262
|
+
) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
|
|
263
|
+
"""Single 4th-order symplectic (Yoshida) step for ``pos'' = force(pos, t)``.
|
|
264
|
+
|
|
265
|
+
Composes three :func:`leapfrog_step` sub-steps with Yoshida's (1990)
|
|
266
|
+
coefficients to raise the (still symplectic) accuracy from 2nd to 4th
|
|
267
|
+
order, at roughly 3x the cost per step of plain leapfrog. Because it
|
|
268
|
+
remains symplectic, it -- like leapfrog -- conserves energy far better
|
|
269
|
+
than RK4 over long integrations, now with much smaller local truncation
|
|
270
|
+
error too.
|
|
271
|
+
|
|
272
|
+
Parameters
|
|
273
|
+
----------
|
|
274
|
+
force : callable
|
|
275
|
+
Numba-jitted acceleration function ``force(pos, t, params) ->
|
|
276
|
+
ndarray``.
|
|
277
|
+
pos : ndarray of float, shape (dim,)
|
|
278
|
+
Current position vector.
|
|
279
|
+
vel : ndarray of float, shape (dim,)
|
|
280
|
+
Current velocity vector.
|
|
281
|
+
t : float
|
|
282
|
+
Current time.
|
|
283
|
+
dt : float
|
|
284
|
+
Step size.
|
|
285
|
+
params : ndarray of float
|
|
286
|
+
Parameter vector passed through to `force`.
|
|
287
|
+
|
|
288
|
+
Returns
|
|
289
|
+
-------
|
|
290
|
+
pos_new : ndarray of float, shape (dim,)
|
|
291
|
+
Position advanced by one step of size `dt`.
|
|
292
|
+
vel_new : ndarray of float, shape (dim,)
|
|
293
|
+
Velocity advanced by one step of size `dt`.
|
|
294
|
+
"""
|
|
295
|
+
h1 = _YOSHIDA_W1 * dt
|
|
296
|
+
h0 = _YOSHIDA_W0 * dt
|
|
297
|
+
pos, vel = leapfrog_step(force, pos, vel, t, h1, params)
|
|
298
|
+
t = t + h1
|
|
299
|
+
pos, vel = leapfrog_step(force, pos, vel, t, h0, params)
|
|
300
|
+
t = t + h0
|
|
301
|
+
pos, vel = leapfrog_step(force, pos, vel, t, h1, params)
|
|
302
|
+
return pos, vel
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
@njit(cache=True)
|
|
306
|
+
def yoshida4_integrate(
|
|
307
|
+
force: RHSFunc,
|
|
308
|
+
pos0: NDArray[np.float64],
|
|
309
|
+
vel0: NDArray[np.float64],
|
|
310
|
+
t0: float,
|
|
311
|
+
dt: float,
|
|
312
|
+
n_steps: int,
|
|
313
|
+
params: NDArray[np.float64],
|
|
314
|
+
) -> tuple[NDArray[np.float64], NDArray[np.float64], NDArray[np.float64]]:
|
|
315
|
+
"""Integrate ``n_steps`` of the 4th-order symplectic Yoshida scheme.
|
|
316
|
+
|
|
317
|
+
Parameters
|
|
318
|
+
----------
|
|
319
|
+
force : callable
|
|
320
|
+
Numba-jitted acceleration function ``force(pos, t, params) ->
|
|
321
|
+
ndarray``.
|
|
322
|
+
pos0 : ndarray of float, shape (dim,)
|
|
323
|
+
Initial position vector.
|
|
324
|
+
vel0 : ndarray of float, shape (dim,)
|
|
325
|
+
Initial velocity vector.
|
|
326
|
+
t0 : float
|
|
327
|
+
Initial time.
|
|
328
|
+
dt : float
|
|
329
|
+
Step size.
|
|
330
|
+
n_steps : int
|
|
331
|
+
Number of integration steps.
|
|
332
|
+
params : ndarray of float
|
|
333
|
+
Parameter vector passed through to `force`.
|
|
334
|
+
|
|
335
|
+
Returns
|
|
336
|
+
-------
|
|
337
|
+
times : ndarray of float, shape (n_steps + 1,)
|
|
338
|
+
Time at each step, starting at `t0`.
|
|
339
|
+
positions : ndarray of float, shape (n_steps + 1, dim)
|
|
340
|
+
Position at each step, starting at `pos0`.
|
|
341
|
+
velocities : ndarray of float, shape (n_steps + 1, dim)
|
|
342
|
+
Velocity at each step, starting at `vel0`.
|
|
343
|
+
"""
|
|
344
|
+
dim = pos0.shape[0]
|
|
345
|
+
positions = np.empty((n_steps + 1, dim))
|
|
346
|
+
velocities = np.empty((n_steps + 1, dim))
|
|
347
|
+
times = np.empty(n_steps + 1)
|
|
348
|
+
positions[0] = pos0
|
|
349
|
+
velocities[0] = vel0
|
|
350
|
+
times[0] = t0
|
|
351
|
+
pos = pos0.copy()
|
|
352
|
+
vel = vel0.copy()
|
|
353
|
+
t = t0
|
|
354
|
+
for i in range(n_steps):
|
|
355
|
+
pos, vel = yoshida4_step(force, pos, vel, t, dt, params)
|
|
356
|
+
t = t0 + (i + 1) * dt
|
|
357
|
+
positions[i + 1] = pos
|
|
358
|
+
velocities[i + 1] = vel
|
|
359
|
+
times[i + 1] = t
|
|
360
|
+
return times, positions, velocities
|
|
File without changes
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""Tests for physicskit.integrators itself.
|
|
2
|
+
|
|
3
|
+
physicskit.chaos.tests.test_integrators already exercises rk4/leapfrog/
|
|
4
|
+
yoshida4 correctness (via physicskit.chaos.core.integrators, which now
|
|
5
|
+
re-exports these same functions) -- it is not repeated here. This file
|
|
6
|
+
covers only what's specific to this package: that it's usable on its own
|
|
7
|
+
(without going through chaos), the velocity_verlet aliasing, and the new
|
|
8
|
+
adaptive Dormand-Prince integrator.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import numpy as np
|
|
12
|
+
import pytest
|
|
13
|
+
from numba import njit
|
|
14
|
+
|
|
15
|
+
from physicskit.integrators import (
|
|
16
|
+
dopri5_integrate,
|
|
17
|
+
leapfrog_step,
|
|
18
|
+
rk4_integrate,
|
|
19
|
+
velocity_verlet_step,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@njit
|
|
24
|
+
def _harmonic_force(pos, t, params):
|
|
25
|
+
omega = params[0]
|
|
26
|
+
return -(omega**2) * pos
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@njit
|
|
30
|
+
def _harmonic_rhs(state, t, params):
|
|
31
|
+
omega = params[0]
|
|
32
|
+
out = np.empty(2)
|
|
33
|
+
out[0] = state[1]
|
|
34
|
+
out[1] = -(omega**2) * state[0]
|
|
35
|
+
return out
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _harmonic_energy(pos, vel, omega):
|
|
39
|
+
return 0.5 * vel**2 + 0.5 * omega**2 * pos**2
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_velocity_verlet_is_an_alias_for_leapfrog():
|
|
43
|
+
"""The two names refer to the same kick-drift-kick scheme -- 'leapfrog'
|
|
44
|
+
in the celestial-mechanics literature, 'velocity Verlet' in molecular
|
|
45
|
+
dynamics -- so they must be the identical function object, not merely
|
|
46
|
+
numerically equivalent copies."""
|
|
47
|
+
assert velocity_verlet_step is leapfrog_step
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@pytest.mark.slow
|
|
51
|
+
def test_dopri5_matches_analytic_harmonic_oscillator():
|
|
52
|
+
omega = 2.0
|
|
53
|
+
params = np.array([omega])
|
|
54
|
+
state0 = np.array([1.0, 0.0])
|
|
55
|
+
t_end = 10.0
|
|
56
|
+
|
|
57
|
+
ts, ys = dopri5_integrate(_harmonic_rhs, state0, 0.0, t_end, 0.01, params, rtol=1e-10, atol=1e-12)
|
|
58
|
+
|
|
59
|
+
assert ts[0] == 0.0
|
|
60
|
+
assert ts[-1] == t_end
|
|
61
|
+
expected = np.cos(omega * ts)
|
|
62
|
+
np.testing.assert_allclose(ys[:, 0], expected, atol=1e-6)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def test_dopri5_energy_drift_shrinks_with_tolerance():
|
|
66
|
+
omega = 2.0
|
|
67
|
+
params = np.array([omega])
|
|
68
|
+
state0 = np.array([1.0, 0.0])
|
|
69
|
+
t_end = 20.0
|
|
70
|
+
|
|
71
|
+
drifts = []
|
|
72
|
+
for rtol in (1e-4, 1e-9):
|
|
73
|
+
_, ys = dopri5_integrate(_harmonic_rhs, state0, 0.0, t_end, 0.01, params, rtol=rtol, atol=rtol * 1e-3)
|
|
74
|
+
energy = _harmonic_energy(ys[:, 0], ys[:, 1], omega)
|
|
75
|
+
drifts.append(np.max(np.abs(energy - energy[0]) / energy[0]))
|
|
76
|
+
|
|
77
|
+
assert drifts[1] < drifts[0]
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def test_dopri5_takes_fewer_steps_for_looser_tolerance():
|
|
81
|
+
omega = 2.0
|
|
82
|
+
params = np.array([omega])
|
|
83
|
+
state0 = np.array([1.0, 0.0])
|
|
84
|
+
t_end = 20.0
|
|
85
|
+
|
|
86
|
+
ts_tight, _ = dopri5_integrate(_harmonic_rhs, state0, 0.0, t_end, 0.01, params, rtol=1e-10, atol=1e-12)
|
|
87
|
+
ts_loose, _ = dopri5_integrate(_harmonic_rhs, state0, 0.0, t_end, 0.01, params, rtol=1e-3, atol=1e-6)
|
|
88
|
+
|
|
89
|
+
assert len(ts_loose) < len(ts_tight)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def test_dopri5_agrees_with_fixed_step_rk4_at_tight_tolerance():
|
|
93
|
+
omega = 2.0
|
|
94
|
+
params = np.array([omega])
|
|
95
|
+
state0 = np.array([1.0, 0.0])
|
|
96
|
+
t_end = 5.0
|
|
97
|
+
|
|
98
|
+
ts, ys = dopri5_integrate(_harmonic_rhs, state0, 0.0, t_end, 0.01, params, rtol=1e-10, atol=1e-12)
|
|
99
|
+
_, ys_rk4 = rk4_integrate(_harmonic_rhs, state0, 0.0, 1e-4, 50000, params)
|
|
100
|
+
|
|
101
|
+
np.testing.assert_allclose(ys[-1], ys_rk4[-1], atol=1e-6)
|