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,31 @@
|
|
|
1
|
+
"""Shared color constants for physicskit.chaos's Matplotlib and Plotly visualizers.
|
|
2
|
+
|
|
3
|
+
Every visualizer still accepts its own color/colormap keyword arguments to
|
|
4
|
+
override these, so nothing here is load-bearing -- it exists so that "the
|
|
5
|
+
trajectory color" or "the boundary color" is defined once, consistently,
|
|
6
|
+
rather than each visualizer module picking its own ad hoc literal string.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
#: Primary data color: trajectories, orbits, scatter points, histograms.
|
|
12
|
+
PRIMARY: str = "steelblue"
|
|
13
|
+
|
|
14
|
+
#: Accent/highlight color: a second trajectory, a moving point, a fit line, a mean marker.
|
|
15
|
+
ACCENT: str = "crimson"
|
|
16
|
+
|
|
17
|
+
#: Structural elements: billiard boundaries, walls, reference geometry.
|
|
18
|
+
STRUCTURE: str = "black"
|
|
19
|
+
|
|
20
|
+
#: Muted/reference color: guide lines, surrogate distributions, secondary annotations.
|
|
21
|
+
MUTED: str = "gray"
|
|
22
|
+
|
|
23
|
+
#: Qualitative colormap name for discretely-labeled data (e.g. basins of attraction).
|
|
24
|
+
QUALITATIVE_CMAP: str = "tab10"
|
|
25
|
+
|
|
26
|
+
#: Sequential colormap name for scalar-valued data (e.g. a trajectory colored by time or speed).
|
|
27
|
+
SEQUENTIAL_CMAP: str = "viridis"
|
|
28
|
+
|
|
29
|
+
#: Diverging colormap name for signed scalar data centered at zero (e.g. a wavefunction's
|
|
30
|
+
#: amplitude, where sign as well as magnitude matters).
|
|
31
|
+
DIVERGING_CMAP: str = "RdBu_r"
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""Interactive 3D trajectory viewing via PyVista (the ``physicskit.chaos[viz3d]``
|
|
2
|
+
optional extra).
|
|
3
|
+
|
|
4
|
+
PyVista renders in a real, camera-navigable 3D scene, unlike Matplotlib's
|
|
5
|
+
projected-2D-with-fake-depth ``mplot3d`` axes -- most useful for genuinely
|
|
6
|
+
3D structures like the Lorenz/Rossler attractors, a double pendulum's
|
|
7
|
+
configuration-space trajectory, or a restricted three-body orbit.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import numpy as np
|
|
13
|
+
from numpy.typing import ArrayLike
|
|
14
|
+
|
|
15
|
+
from physicskit.chaos.visualizers import theme
|
|
16
|
+
|
|
17
|
+
try:
|
|
18
|
+
import pyvista as pv
|
|
19
|
+
except ImportError as exc: # pragma: no cover - exercised only without the extra installed
|
|
20
|
+
raise ImportError("physicskit.chaos.visualizers.viewer3d requires PyVista; install it with `pip install physicskit.chaos[viz3d]`.") from exc
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def pyvista_trajectory(
|
|
24
|
+
states: ArrayLike,
|
|
25
|
+
components: tuple[int, int, int] = (0, 1, 2),
|
|
26
|
+
cmap: str = theme.SEQUENTIAL_CMAP,
|
|
27
|
+
line_width: float = 2.0,
|
|
28
|
+
color_by: str = "index",
|
|
29
|
+
off_screen: bool = False,
|
|
30
|
+
) -> pv.Plotter:
|
|
31
|
+
"""Render a 3D trajectory as an interactive PyVista scene.
|
|
32
|
+
|
|
33
|
+
Parameters
|
|
34
|
+
----------
|
|
35
|
+
states : array_like of float, shape (n, dim), dim >= 3
|
|
36
|
+
Trajectory samples; three of each state's components are used as
|
|
37
|
+
``(x, y, z)``.
|
|
38
|
+
components : tuple of int, default (0, 1, 2)
|
|
39
|
+
Indices into each state's columns to use as ``(x, y, z)`` -- e.g.
|
|
40
|
+
``(0, 1, 2)`` selects the trajectory itself, but any three state
|
|
41
|
+
components (or, for a system with more than three, any subset) can
|
|
42
|
+
be chosen.
|
|
43
|
+
cmap : str, default "viridis" (:data:`physicskit.chaos.visualizers.theme.SEQUENTIAL_CMAP`)
|
|
44
|
+
Colormap the trajectory is colored by, per `color_by`.
|
|
45
|
+
line_width : float, default 2.0
|
|
46
|
+
Rendered line width, in screen pixels.
|
|
47
|
+
color_by : {"index", "speed"}, default "index"
|
|
48
|
+
Whether to color the trajectory by sample index (i.e. time) or by
|
|
49
|
+
local speed (the Euclidean distance between consecutive selected
|
|
50
|
+
points) -- the latter highlights where the trajectory moves fastest.
|
|
51
|
+
off_screen : bool, default False
|
|
52
|
+
Render off-screen (e.g. to save a screenshot from a script or in CI)
|
|
53
|
+
instead of opening an interactive window.
|
|
54
|
+
|
|
55
|
+
Returns
|
|
56
|
+
-------
|
|
57
|
+
pyvista.Plotter
|
|
58
|
+
Call :meth:`~pyvista.Plotter.show` to display interactively, or
|
|
59
|
+
:meth:`~pyvista.Plotter.screenshot` when `off_screen=True`.
|
|
60
|
+
"""
|
|
61
|
+
states = np.asarray(states, dtype=np.float64)
|
|
62
|
+
points = states[:, components]
|
|
63
|
+
|
|
64
|
+
if color_by == "speed":
|
|
65
|
+
deltas = np.diff(points, axis=0)
|
|
66
|
+
scalars = np.concatenate([[0.0], np.linalg.norm(deltas, axis=1)])
|
|
67
|
+
else:
|
|
68
|
+
scalars = np.arange(points.shape[0], dtype=np.float64)
|
|
69
|
+
|
|
70
|
+
line = pv.lines_from_points(points)
|
|
71
|
+
line["scalars"] = scalars
|
|
72
|
+
|
|
73
|
+
plotter = pv.Plotter(off_screen=off_screen)
|
|
74
|
+
plotter.add_mesh(
|
|
75
|
+
line,
|
|
76
|
+
scalars="scalars",
|
|
77
|
+
cmap=cmap, # type: ignore[arg-type] # pyvista stubs: cmap accepts any registered name
|
|
78
|
+
line_width=line_width,
|
|
79
|
+
show_scalar_bar=False,
|
|
80
|
+
)
|
|
81
|
+
plotter.show_grid() # type: ignore[call-arg] # pyvista stubs mistype this bound method
|
|
82
|
+
return plotter
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""physicskit.classical: classical_mechanics_kit.
|
|
2
|
+
|
|
3
|
+
Visual and computational demonstrations of classical mechanics -- from
|
|
4
|
+
Newtonian vector dynamics to Lagrangian variational principles,
|
|
5
|
+
Hamiltonian phase spaces, coupled lattice chains, and rigid body
|
|
6
|
+
rotations -- built on Numba-accelerated symplectic integrators.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
__version__ = "0.1.0"
|
|
10
|
+
|
|
11
|
+
from physicskit.classical.core.base_system import (
|
|
12
|
+
DynamicalSystem,
|
|
13
|
+
HamiltonianSystem,
|
|
14
|
+
LagrangianSystem,
|
|
15
|
+
ODESystem,
|
|
16
|
+
SimulationResult,
|
|
17
|
+
)
|
|
18
|
+
from physicskit.classical.systems.chains import FPUTChain, HarmonicChain, SineGordonChain
|
|
19
|
+
from physicskit.classical.systems.hamiltonian import HenonHeilesSystem, PendulumSwarm, pendulum_action_angle
|
|
20
|
+
from physicskit.classical.systems.lagrangian import BeadOnRotatingHoop, CoupledOscillators, DoublePendulum
|
|
21
|
+
from physicskit.classical.systems.newtonian import KeplerSystem, ProjectileMotion
|
|
22
|
+
from physicskit.classical.systems.rotations import (
|
|
23
|
+
EulerTop,
|
|
24
|
+
HeavySymmetricTop,
|
|
25
|
+
effective_potential_symmetric_top,
|
|
26
|
+
find_theta_equilibrium,
|
|
27
|
+
nutation_frequency,
|
|
28
|
+
precession_frequency,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
__all__ = [
|
|
32
|
+
"__version__",
|
|
33
|
+
"DynamicalSystem",
|
|
34
|
+
"ODESystem",
|
|
35
|
+
"HamiltonianSystem",
|
|
36
|
+
"LagrangianSystem",
|
|
37
|
+
"SimulationResult",
|
|
38
|
+
"ProjectileMotion",
|
|
39
|
+
"KeplerSystem",
|
|
40
|
+
"DoublePendulum",
|
|
41
|
+
"BeadOnRotatingHoop",
|
|
42
|
+
"CoupledOscillators",
|
|
43
|
+
"HenonHeilesSystem",
|
|
44
|
+
"PendulumSwarm",
|
|
45
|
+
"pendulum_action_angle",
|
|
46
|
+
"HarmonicChain",
|
|
47
|
+
"FPUTChain",
|
|
48
|
+
"SineGordonChain",
|
|
49
|
+
"EulerTop",
|
|
50
|
+
"HeavySymmetricTop",
|
|
51
|
+
"effective_potential_symmetric_top",
|
|
52
|
+
"find_theta_equilibrium",
|
|
53
|
+
"nutation_frequency",
|
|
54
|
+
"precession_frequency",
|
|
55
|
+
]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from physicskit.classical.core import integrators
|
|
2
|
+
from physicskit.classical.core.base_system import (
|
|
3
|
+
DynamicalSystem,
|
|
4
|
+
HamiltonianSystem,
|
|
5
|
+
LagrangianSystem,
|
|
6
|
+
ODESystem,
|
|
7
|
+
SimulationResult,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"DynamicalSystem",
|
|
12
|
+
"ODESystem",
|
|
13
|
+
"HamiltonianSystem",
|
|
14
|
+
"LagrangianSystem",
|
|
15
|
+
"SimulationResult",
|
|
16
|
+
"integrators",
|
|
17
|
+
]
|
|
@@ -0,0 +1,510 @@
|
|
|
1
|
+
"""Abstract base classes for ODE, Lagrangian, and Hamiltonian systems.
|
|
2
|
+
|
|
3
|
+
These classes define the common interface that every physical system in
|
|
4
|
+
:mod:`physicskit.classical.systems` implements, and wire each system up to the
|
|
5
|
+
integrators in :mod:`physicskit.classical.core.integrators`. A :class:`SimulationResult`
|
|
6
|
+
is returned by every ``integrate`` call and is what the visualizers in
|
|
7
|
+
:mod:`physicskit.classical.visualizers` consume.
|
|
8
|
+
|
|
9
|
+
Notes
|
|
10
|
+
-----
|
|
11
|
+
The step/loop functions in :mod:`physicskit.classical.core.integrators` are themselves
|
|
12
|
+
``@njit``-compiled and call the supplied ``force_func``/``deriv_func``
|
|
13
|
+
*from inside nopython code*. Numba can only do this when that callback
|
|
14
|
+
is itself a genuine ``@njit`` dispatcher (numba's "first-class function"
|
|
15
|
+
support) -- a plain bound Python method (e.g. ``self.derivatives``)
|
|
16
|
+
cannot be typed and would fail. Every concrete system therefore builds
|
|
17
|
+
its own standalone njit callbacks (conventionally stored as
|
|
18
|
+
``self._force_njit`` and/or ``self._deriv_njit``, produced by a
|
|
19
|
+
module-level factory function that closes over the system's numeric
|
|
20
|
+
parameters) and the base classes below always integrate using *those*
|
|
21
|
+
attributes, never a bound method.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
from abc import ABC, abstractmethod
|
|
27
|
+
from dataclasses import dataclass, field
|
|
28
|
+
from typing import Optional
|
|
29
|
+
|
|
30
|
+
import numpy as np
|
|
31
|
+
|
|
32
|
+
from physicskit.classical.core import integrators as intg
|
|
33
|
+
|
|
34
|
+
__all__ = [
|
|
35
|
+
"SimulationResult",
|
|
36
|
+
"DynamicalSystem",
|
|
37
|
+
"ODESystem",
|
|
38
|
+
"HamiltonianSystem",
|
|
39
|
+
"LagrangianSystem",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass
|
|
44
|
+
class SimulationResult:
|
|
45
|
+
"""Container for the output of a system's ``integrate`` call."""
|
|
46
|
+
|
|
47
|
+
t: np.ndarray
|
|
48
|
+
"""ndarray, shape (n_steps + 1,): Time samples."""
|
|
49
|
+
|
|
50
|
+
y: np.ndarray
|
|
51
|
+
"""ndarray, shape (n_steps + 1, state_dim): The raw stacked-state
|
|
52
|
+
trajectory, in whatever layout the producing system uses internally."""
|
|
53
|
+
|
|
54
|
+
q: Optional[np.ndarray] = None
|
|
55
|
+
"""ndarray, shape (n_steps + 1, ndof), optional: Generalized positions,
|
|
56
|
+
for Hamiltonian/Lagrangian systems."""
|
|
57
|
+
|
|
58
|
+
p: Optional[np.ndarray] = None
|
|
59
|
+
"""ndarray, shape (n_steps + 1, ndof), optional: Canonical momenta for
|
|
60
|
+
Hamiltonian systems, or qdot for Lagrangian systems."""
|
|
61
|
+
|
|
62
|
+
energy: Optional[np.ndarray] = None
|
|
63
|
+
"""ndarray, shape (n_steps + 1,), optional: Total mechanical energy
|
|
64
|
+
H(t) (or T+V) at each sample."""
|
|
65
|
+
|
|
66
|
+
method: str = ""
|
|
67
|
+
"""str: Name of the integrator used (e.g. ``"yoshida4"``)."""
|
|
68
|
+
|
|
69
|
+
extra: dict = field(default_factory=dict)
|
|
70
|
+
"""dict: Free-form slot for any additional diagnostics a system
|
|
71
|
+
chooses to attach."""
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class DynamicalSystem(ABC):
|
|
75
|
+
"""Common base for any system with a state vector evolving in time."""
|
|
76
|
+
|
|
77
|
+
def __init__(self, state0):
|
|
78
|
+
self.state = np.asarray(state0, dtype=np.float64)
|
|
79
|
+
self.t = 0.0
|
|
80
|
+
|
|
81
|
+
@abstractmethod
|
|
82
|
+
def derivatives(self, t: float, state: np.ndarray) -> np.ndarray:
|
|
83
|
+
"""Return dstate/dt at ``(t, state)``.
|
|
84
|
+
|
|
85
|
+
Parameters
|
|
86
|
+
----------
|
|
87
|
+
t : float
|
|
88
|
+
Current time.
|
|
89
|
+
state : ndarray
|
|
90
|
+
Current state vector.
|
|
91
|
+
|
|
92
|
+
Returns
|
|
93
|
+
-------
|
|
94
|
+
ndarray
|
|
95
|
+
Time derivative of ``state``.
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
@abstractmethod
|
|
99
|
+
def energy(self, state: Optional[np.ndarray] = None) -> float:
|
|
100
|
+
"""Return the total mechanical energy of ``state``.
|
|
101
|
+
|
|
102
|
+
Parameters
|
|
103
|
+
----------
|
|
104
|
+
state : ndarray, optional
|
|
105
|
+
State to evaluate; defaults to ``self.state``.
|
|
106
|
+
|
|
107
|
+
Returns
|
|
108
|
+
-------
|
|
109
|
+
float
|
|
110
|
+
Total mechanical energy.
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
def reset(self, state0=None, t0: float = 0.0):
|
|
114
|
+
"""Reset the system's state and clock.
|
|
115
|
+
|
|
116
|
+
Parameters
|
|
117
|
+
----------
|
|
118
|
+
state0 : array-like, optional
|
|
119
|
+
New state; if omitted, the current state is kept.
|
|
120
|
+
t0 : float, default 0.0
|
|
121
|
+
New time.
|
|
122
|
+
|
|
123
|
+
Returns
|
|
124
|
+
-------
|
|
125
|
+
ndarray
|
|
126
|
+
The (possibly updated) current state.
|
|
127
|
+
"""
|
|
128
|
+
if state0 is not None:
|
|
129
|
+
self.state = np.asarray(state0, dtype=np.float64)
|
|
130
|
+
self.t = t0
|
|
131
|
+
return self.state
|
|
132
|
+
|
|
133
|
+
@abstractmethod
|
|
134
|
+
def integrate(self, t_span, dt: float, method: str = "rk4") -> SimulationResult:
|
|
135
|
+
"""Integrate the system forward in time.
|
|
136
|
+
|
|
137
|
+
Parameters
|
|
138
|
+
----------
|
|
139
|
+
t_span : tuple of float
|
|
140
|
+
``(t0, t1)``, start and end time.
|
|
141
|
+
dt : float
|
|
142
|
+
Fixed step size.
|
|
143
|
+
method : str
|
|
144
|
+
Integrator to use; valid values depend on the subclass.
|
|
145
|
+
|
|
146
|
+
Returns
|
|
147
|
+
-------
|
|
148
|
+
SimulationResult
|
|
149
|
+
The full trajectory and diagnostics.
|
|
150
|
+
"""
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class ODESystem(DynamicalSystem):
|
|
154
|
+
"""A general (possibly non-conservative) first-order ODE system.
|
|
155
|
+
|
|
156
|
+
Concrete subclasses must set ``self._deriv_njit`` (an ``@njit``
|
|
157
|
+
dispatcher with signature ``(t, state) -> dstate``) in ``__init__``.
|
|
158
|
+
Suitable for systems that are not naturally Hamiltonian/symplectic,
|
|
159
|
+
e.g. central-force problems with drag, or rigid-body Euler equations
|
|
160
|
+
(whose implicit-midpoint flow exactly conserves the quadratic energy
|
|
161
|
+
and angular-momentum-squared invariants).
|
|
162
|
+
"""
|
|
163
|
+
|
|
164
|
+
_deriv_njit = None
|
|
165
|
+
|
|
166
|
+
def derivatives(self, t: float, state: np.ndarray) -> np.ndarray:
|
|
167
|
+
return self._deriv_njit(t, state)
|
|
168
|
+
|
|
169
|
+
def integrate(self, t_span, dt: float, method: str = "implicit_midpoint") -> SimulationResult:
|
|
170
|
+
"""Integrate the system forward in time.
|
|
171
|
+
|
|
172
|
+
Parameters
|
|
173
|
+
----------
|
|
174
|
+
t_span : tuple of float
|
|
175
|
+
``(t0, t1)``, start and end time.
|
|
176
|
+
dt : float
|
|
177
|
+
Fixed step size.
|
|
178
|
+
method : {"implicit_midpoint", "rk4"}
|
|
179
|
+
Integrator to use.
|
|
180
|
+
|
|
181
|
+
Returns
|
|
182
|
+
-------
|
|
183
|
+
SimulationResult
|
|
184
|
+
The full trajectory and energy diagnostics.
|
|
185
|
+
"""
|
|
186
|
+
t0, t1 = t_span
|
|
187
|
+
n_steps = int(round((t1 - t0) / dt))
|
|
188
|
+
if method == "rk4":
|
|
189
|
+
ts, ys = intg.rk4_integrate(self._deriv_njit, self.state, t0, n_steps, dt)
|
|
190
|
+
elif method == "implicit_midpoint":
|
|
191
|
+
ts, ys = intg.implicit_midpoint_integrate(self._deriv_njit, self.state, t0, n_steps, dt)
|
|
192
|
+
else:
|
|
193
|
+
raise ValueError(f"Unknown method '{method}' for ODESystem")
|
|
194
|
+
energies = np.array([self.energy(y) for y in ys])
|
|
195
|
+
self.state = ys[-1]
|
|
196
|
+
self.t = ts[-1]
|
|
197
|
+
return SimulationResult(t=ts, y=ys, energy=energies, method=method)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
class HamiltonianSystem(DynamicalSystem):
|
|
201
|
+
"""Base class for canonical Hamiltonian systems H(q, p, t).
|
|
202
|
+
|
|
203
|
+
Subclasses set ``ndof``, ``separable``, and ``mass_inv``, and build
|
|
204
|
+
``self._force_njit(q, t) -> array`` (required when ``separable`` is
|
|
205
|
+
True, giving -dV/dq for H = T(p) + V(q)) and/or
|
|
206
|
+
``self._deriv_njit(t, y) -> dy`` for the full stacked state (required
|
|
207
|
+
for non-separable systems, and optional otherwise -- if omitted it
|
|
208
|
+
is built automatically from ``_force_njit``).
|
|
209
|
+
|
|
210
|
+
Parameters
|
|
211
|
+
----------
|
|
212
|
+
q0, p0 : array-like, shape (ndof,)
|
|
213
|
+
Initial position and momentum.
|
|
214
|
+
|
|
215
|
+
Notes
|
|
216
|
+
-----
|
|
217
|
+
``state`` is stored flattened as ``[q_1..q_n, p_1..p_n]``.
|
|
218
|
+
"""
|
|
219
|
+
|
|
220
|
+
ndof: int = 0
|
|
221
|
+
"""int: Number of degrees of freedom, inferred from ``q0``."""
|
|
222
|
+
|
|
223
|
+
separable: bool = True
|
|
224
|
+
"""bool: Whether ``H = T(p) + V(q)``, enabling the Verlet/Yoshida4 integrators."""
|
|
225
|
+
|
|
226
|
+
mass_inv = 1.0
|
|
227
|
+
"""float or ndarray: 1/m per coordinate; ``dq/dt = mass_inv * p``."""
|
|
228
|
+
|
|
229
|
+
_force_njit = None
|
|
230
|
+
_deriv_njit = None
|
|
231
|
+
|
|
232
|
+
def __init__(self, q0, p0):
|
|
233
|
+
q0 = np.asarray(q0, dtype=np.float64)
|
|
234
|
+
p0 = np.asarray(p0, dtype=np.float64)
|
|
235
|
+
self.ndof = q0.shape[0]
|
|
236
|
+
super().__init__(np.concatenate([q0, p0]))
|
|
237
|
+
|
|
238
|
+
@property
|
|
239
|
+
def q(self) -> np.ndarray:
|
|
240
|
+
"""ndarray: Generalized positions (the first ``ndof`` entries of ``state``)."""
|
|
241
|
+
return self.state[: self.ndof]
|
|
242
|
+
|
|
243
|
+
@property
|
|
244
|
+
def p(self) -> np.ndarray:
|
|
245
|
+
"""ndarray: Canonical momenta (the last ``ndof`` entries of ``state``)."""
|
|
246
|
+
return self.state[self.ndof :]
|
|
247
|
+
|
|
248
|
+
@staticmethod
|
|
249
|
+
def split(state: np.ndarray, ndof: int):
|
|
250
|
+
"""Split a stacked ``[q, p]`` state vector into its two halves.
|
|
251
|
+
|
|
252
|
+
Parameters
|
|
253
|
+
----------
|
|
254
|
+
state : ndarray, shape (2 * ndof,)
|
|
255
|
+
ndof : int
|
|
256
|
+
|
|
257
|
+
Returns
|
|
258
|
+
-------
|
|
259
|
+
q, p : ndarray, shape (ndof,)
|
|
260
|
+
"""
|
|
261
|
+
return state[:ndof], state[ndof:]
|
|
262
|
+
|
|
263
|
+
@abstractmethod
|
|
264
|
+
def kinetic_energy(self, p: np.ndarray) -> float:
|
|
265
|
+
"""Return T(p).
|
|
266
|
+
|
|
267
|
+
Parameters
|
|
268
|
+
----------
|
|
269
|
+
p : ndarray
|
|
270
|
+
Canonical momenta.
|
|
271
|
+
|
|
272
|
+
Returns
|
|
273
|
+
-------
|
|
274
|
+
float
|
|
275
|
+
"""
|
|
276
|
+
...
|
|
277
|
+
|
|
278
|
+
@abstractmethod
|
|
279
|
+
def potential_energy(self, q: np.ndarray) -> float:
|
|
280
|
+
"""Return V(q).
|
|
281
|
+
|
|
282
|
+
Parameters
|
|
283
|
+
----------
|
|
284
|
+
q : ndarray
|
|
285
|
+
Generalized positions.
|
|
286
|
+
|
|
287
|
+
Returns
|
|
288
|
+
-------
|
|
289
|
+
float
|
|
290
|
+
"""
|
|
291
|
+
...
|
|
292
|
+
|
|
293
|
+
def force(self, q: np.ndarray, t: float = 0.0) -> np.ndarray:
|
|
294
|
+
"""Return -dV/dq (only defined for separable systems).
|
|
295
|
+
|
|
296
|
+
Parameters
|
|
297
|
+
----------
|
|
298
|
+
q : ndarray
|
|
299
|
+
Generalized positions.
|
|
300
|
+
t : float, default 0.0
|
|
301
|
+
Current time.
|
|
302
|
+
|
|
303
|
+
Returns
|
|
304
|
+
-------
|
|
305
|
+
ndarray
|
|
306
|
+
|
|
307
|
+
Raises
|
|
308
|
+
------
|
|
309
|
+
NotImplementedError
|
|
310
|
+
If the system is non-separable (no ``_force_njit`` set).
|
|
311
|
+
"""
|
|
312
|
+
if self._force_njit is None:
|
|
313
|
+
raise NotImplementedError("This system has no separable force() (non-separable Hamiltonian)")
|
|
314
|
+
return self._force_njit(q, t)
|
|
315
|
+
|
|
316
|
+
def _ensure_deriv_njit(self):
|
|
317
|
+
if self._deriv_njit is None:
|
|
318
|
+
if not self.separable:
|
|
319
|
+
raise NotImplementedError("Non-separable systems must provide self._deriv_njit")
|
|
320
|
+
self._deriv_njit = intg.make_separable_derivatives(self._force_njit, self.mass_inv, self.ndof)
|
|
321
|
+
return self._deriv_njit
|
|
322
|
+
|
|
323
|
+
def derivatives(self, t: float, state: np.ndarray) -> np.ndarray:
|
|
324
|
+
return self._ensure_deriv_njit()(t, state)
|
|
325
|
+
|
|
326
|
+
def energy(self, state: Optional[np.ndarray] = None) -> float:
|
|
327
|
+
state = self.state if state is None else state
|
|
328
|
+
q, p = self.split(state, self.ndof)
|
|
329
|
+
return self.kinetic_energy(p) + self.potential_energy(q)
|
|
330
|
+
|
|
331
|
+
def integrate(self, t_span, dt: float, method: str = "yoshida4") -> SimulationResult:
|
|
332
|
+
"""Integrate the system forward in time.
|
|
333
|
+
|
|
334
|
+
Parameters
|
|
335
|
+
----------
|
|
336
|
+
t_span : tuple of float
|
|
337
|
+
``(t0, t1)``, start and end time.
|
|
338
|
+
dt : float
|
|
339
|
+
Fixed step size.
|
|
340
|
+
method : {"yoshida4", "verlet", "implicit_midpoint", "rk4"}
|
|
341
|
+
Integrator to use. ``"yoshida4"``/``"verlet"`` require
|
|
342
|
+
``self.separable``.
|
|
343
|
+
|
|
344
|
+
Returns
|
|
345
|
+
-------
|
|
346
|
+
SimulationResult
|
|
347
|
+
The full ``(q, p)`` trajectory and energy diagnostics.
|
|
348
|
+
"""
|
|
349
|
+
t0, t1 = t_span
|
|
350
|
+
n_steps = int(round((t1 - t0) / dt))
|
|
351
|
+
q0, p0 = self.q.copy(), self.p.copy()
|
|
352
|
+
if method in ("verlet", "yoshida4"):
|
|
353
|
+
if not self.separable:
|
|
354
|
+
raise ValueError(f"method='{method}' requires a separable Hamiltonian")
|
|
355
|
+
integ = intg.velocity_verlet_integrate if method == "verlet" else intg.yoshida4_integrate
|
|
356
|
+
ts, qs, ps = integ(self._force_njit, self.mass_inv, q0, p0, t0, n_steps, dt)
|
|
357
|
+
elif method in ("implicit_midpoint", "rk4"):
|
|
358
|
+
y0 = np.concatenate([q0, p0])
|
|
359
|
+
deriv = self._ensure_deriv_njit()
|
|
360
|
+
integ = intg.implicit_midpoint_integrate if method == "implicit_midpoint" else intg.rk4_integrate
|
|
361
|
+
ts, ys = integ(deriv, y0, t0, n_steps, dt)
|
|
362
|
+
qs, ps = ys[:, : self.ndof], ys[:, self.ndof :]
|
|
363
|
+
else:
|
|
364
|
+
raise ValueError(f"Unknown method '{method}' for HamiltonianSystem")
|
|
365
|
+
energies = np.array([self.kinetic_energy(p) + self.potential_energy(q) for q, p in zip(qs, ps)])
|
|
366
|
+
self.state = np.concatenate([qs[-1], ps[-1]])
|
|
367
|
+
self.t = ts[-1]
|
|
368
|
+
return SimulationResult(t=ts, y=np.concatenate([qs, ps], axis=1), q=qs, p=ps, energy=energies, method=method)
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
class LagrangianSystem(DynamicalSystem):
|
|
372
|
+
"""Base class for systems defined via a Lagrangian L(q, qdot, t).
|
|
373
|
+
|
|
374
|
+
Subclasses typically obtain their equations of motion from
|
|
375
|
+
:class:`physicskit.classical.utils.symbolic.LagrangianEngine`, which derives both a
|
|
376
|
+
direct acceleration function (for reporting/plotting in (q, qdot)
|
|
377
|
+
coordinates, via ``self._accel_njit``) and a canonical Hamiltonian
|
|
378
|
+
form (``self._canonical_deriv_njit``, used for symplectic
|
|
379
|
+
integration via ``implicit_midpoint`` -- valid even when the mass
|
|
380
|
+
matrix M(q) is configuration-dependent and the system is therefore
|
|
381
|
+
non-separable, e.g. the double pendulum).
|
|
382
|
+
|
|
383
|
+
Parameters
|
|
384
|
+
----------
|
|
385
|
+
q0, qdot0 : array-like, shape (ndof,)
|
|
386
|
+
Initial generalized position and velocity.
|
|
387
|
+
|
|
388
|
+
Notes
|
|
389
|
+
-----
|
|
390
|
+
``state`` is stored flattened as ``[q_1..q_n, qdot_1..qdot_n]``.
|
|
391
|
+
"""
|
|
392
|
+
|
|
393
|
+
ndof: int = 0
|
|
394
|
+
"""int: Number of degrees of freedom, inferred from ``q0``."""
|
|
395
|
+
|
|
396
|
+
_accel_njit = None
|
|
397
|
+
_momentum_njit = None
|
|
398
|
+
_canonical_deriv_njit = None # (t, [q,p]) -> [dq, dp] = [dH/dp, -dH/dq]
|
|
399
|
+
_velocity_njit = None # (q, p) -> qdot = dH/dp, used to report qdot after integration
|
|
400
|
+
_deriv_njit = None # (t, [q,qdot]) -> [qdot, qddot], used only by method='rk4'
|
|
401
|
+
|
|
402
|
+
def __init__(self, q0, qdot0):
|
|
403
|
+
q0 = np.asarray(q0, dtype=np.float64)
|
|
404
|
+
qdot0 = np.asarray(qdot0, dtype=np.float64)
|
|
405
|
+
self.ndof = q0.shape[0]
|
|
406
|
+
super().__init__(np.concatenate([q0, qdot0]))
|
|
407
|
+
|
|
408
|
+
@property
|
|
409
|
+
def q(self) -> np.ndarray:
|
|
410
|
+
"""ndarray: Generalized positions (the first ``ndof`` entries of ``state``)."""
|
|
411
|
+
return self.state[: self.ndof]
|
|
412
|
+
|
|
413
|
+
@property
|
|
414
|
+
def qdot(self) -> np.ndarray:
|
|
415
|
+
"""ndarray: Generalized velocities (the last ``ndof`` entries of ``state``)."""
|
|
416
|
+
return self.state[self.ndof :]
|
|
417
|
+
|
|
418
|
+
@staticmethod
|
|
419
|
+
def split(state: np.ndarray, ndof: int):
|
|
420
|
+
"""Split a stacked ``[q, qdot]`` state vector into its two halves.
|
|
421
|
+
|
|
422
|
+
Parameters
|
|
423
|
+
----------
|
|
424
|
+
state : ndarray, shape (2 * ndof,)
|
|
425
|
+
ndof : int
|
|
426
|
+
|
|
427
|
+
Returns
|
|
428
|
+
-------
|
|
429
|
+
q, qdot : ndarray, shape (ndof,)
|
|
430
|
+
"""
|
|
431
|
+
return state[:ndof], state[ndof:]
|
|
432
|
+
|
|
433
|
+
def acceleration(self, q: np.ndarray, qdot: np.ndarray, t: float) -> np.ndarray:
|
|
434
|
+
"""Return qddot at ``(q, qdot, t)`` via the Euler-Lagrange equations.
|
|
435
|
+
|
|
436
|
+
Parameters
|
|
437
|
+
----------
|
|
438
|
+
q, qdot : ndarray
|
|
439
|
+
t : float
|
|
440
|
+
|
|
441
|
+
Returns
|
|
442
|
+
-------
|
|
443
|
+
ndarray
|
|
444
|
+
"""
|
|
445
|
+
return self._accel_njit(q, qdot, t)
|
|
446
|
+
|
|
447
|
+
def momentum(self, q: np.ndarray, qdot: np.ndarray) -> np.ndarray:
|
|
448
|
+
"""Return the canonical momentum p = dL/dqdot at ``(q, qdot)``.
|
|
449
|
+
|
|
450
|
+
Parameters
|
|
451
|
+
----------
|
|
452
|
+
q, qdot : ndarray
|
|
453
|
+
|
|
454
|
+
Returns
|
|
455
|
+
-------
|
|
456
|
+
ndarray
|
|
457
|
+
"""
|
|
458
|
+
return self._momentum_njit(q, qdot)
|
|
459
|
+
|
|
460
|
+
@abstractmethod
|
|
461
|
+
def energy(self, state: Optional[np.ndarray] = None) -> float: ...
|
|
462
|
+
|
|
463
|
+
def derivatives(self, t: float, state: np.ndarray) -> np.ndarray:
|
|
464
|
+
q, qdot = self.split(state, self.ndof)
|
|
465
|
+
return np.concatenate([qdot, self.acceleration(q, qdot, t)])
|
|
466
|
+
|
|
467
|
+
def integrate(self, t_span, dt: float, method: str = "implicit_midpoint") -> SimulationResult:
|
|
468
|
+
"""Integrate the system forward in time.
|
|
469
|
+
|
|
470
|
+
Parameters
|
|
471
|
+
----------
|
|
472
|
+
t_span : tuple of float
|
|
473
|
+
``(t0, t1)``, start and end time.
|
|
474
|
+
dt : float
|
|
475
|
+
Fixed step size.
|
|
476
|
+
method : {"implicit_midpoint", "rk4"}
|
|
477
|
+
Integrator to use. ``"implicit_midpoint"`` (the default) is
|
|
478
|
+
symplectic even for non-separable systems, via the
|
|
479
|
+
Legendre-transformed canonical form; ``"rk4"`` integrates
|
|
480
|
+
the direct ``(q, qdot)`` acceleration form and is not
|
|
481
|
+
symplectic.
|
|
482
|
+
|
|
483
|
+
Returns
|
|
484
|
+
-------
|
|
485
|
+
SimulationResult
|
|
486
|
+
The full ``(q, qdot)`` trajectory and energy diagnostics
|
|
487
|
+
(with ``result.p`` holding qdot, not a canonical momentum).
|
|
488
|
+
"""
|
|
489
|
+
t0, t1 = t_span
|
|
490
|
+
n_steps = int(round((t1 - t0) / dt))
|
|
491
|
+
q0, qdot0 = self.q.copy(), self.qdot.copy()
|
|
492
|
+
if method == "implicit_midpoint":
|
|
493
|
+
p0 = self.momentum(q0, qdot0)
|
|
494
|
+
y0 = np.concatenate([q0, p0])
|
|
495
|
+
ts, ys = intg.implicit_midpoint_integrate(self._canonical_deriv_njit, y0, t0, n_steps, dt)
|
|
496
|
+
qs = ys[:, : self.ndof]
|
|
497
|
+
ps = ys[:, self.ndof :]
|
|
498
|
+
qdots = np.array([self._velocity_njit(q, p) for q, p in zip(qs, ps)])
|
|
499
|
+
ys_out = np.concatenate([qs, qdots], axis=1)
|
|
500
|
+
elif method == "rk4":
|
|
501
|
+
y0 = np.concatenate([q0, qdot0])
|
|
502
|
+
ts, ys_out = intg.rk4_integrate(self._deriv_njit, y0, t0, n_steps, dt)
|
|
503
|
+
qs = ys_out[:, : self.ndof]
|
|
504
|
+
qdots = ys_out[:, self.ndof :]
|
|
505
|
+
else:
|
|
506
|
+
raise ValueError(f"Unknown method '{method}' for LagrangianSystem")
|
|
507
|
+
energies = np.array([self.energy(np.concatenate([q, qd])) for q, qd in zip(qs, qdots)])
|
|
508
|
+
self.state = ys_out[-1]
|
|
509
|
+
self.t = ts[-1]
|
|
510
|
+
return SimulationResult(t=ts, y=ys_out, q=qs, p=qdots, energy=energies, method=method)
|