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,334 @@
|
|
|
1
|
+
"""Coupled-oscillator and soliton lattice chains.
|
|
2
|
+
|
|
3
|
+
All three chains are 1D lattices of N particles with fixed
|
|
4
|
+
(wall-anchored) boundary conditions, i.e. q_0 = q_{N+1} = 0. Each is a
|
|
5
|
+
separable Hamiltonian system H = sum_i p_i^2/(2m) + V(q), with the
|
|
6
|
+
force computed by a hand-written ``@njit`` loop over lattice sites (the
|
|
7
|
+
"lattice loops" the project brief calls out for mandatory Numba
|
|
8
|
+
acceleration) rather than through the symbolic engine, since the
|
|
9
|
+
physics is fixed and the loop structure is identical for every N.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from functools import lru_cache
|
|
15
|
+
|
|
16
|
+
import numpy as np
|
|
17
|
+
from numba import njit
|
|
18
|
+
|
|
19
|
+
from physicskit.classical.core.base_system import HamiltonianSystem
|
|
20
|
+
|
|
21
|
+
__all__ = ["HarmonicChain", "FPUTChain", "SineGordonChain"]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# ---------------------------------------------------------------------------
|
|
25
|
+
# Harmonic chain: V = sum_i k/2 (q_{i+1} - q_i)^2, fixed ends
|
|
26
|
+
# ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@njit(cache=True)
|
|
30
|
+
def _harmonic_force(q, k):
|
|
31
|
+
n = q.shape[0]
|
|
32
|
+
out = np.empty(n)
|
|
33
|
+
for i in range(n):
|
|
34
|
+
left = q[i - 1] if i > 0 else 0.0
|
|
35
|
+
right = q[i + 1] if i < n - 1 else 0.0
|
|
36
|
+
out[i] = k * (left - 2.0 * q[i] + right)
|
|
37
|
+
return out
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@lru_cache(maxsize=64)
|
|
41
|
+
def _make_harmonic_force(k: float):
|
|
42
|
+
@njit(cache=True)
|
|
43
|
+
def force(q, t):
|
|
44
|
+
return _harmonic_force(q, k)
|
|
45
|
+
|
|
46
|
+
return force
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class _ChainBase(HamiltonianSystem):
|
|
50
|
+
separable = True
|
|
51
|
+
|
|
52
|
+
def __init__(self, q0, p0, m=1.0, k=1.0):
|
|
53
|
+
self.m, self.k = m, k
|
|
54
|
+
self.mass_inv = 1.0 / m
|
|
55
|
+
super().__init__(q0, p0)
|
|
56
|
+
|
|
57
|
+
def kinetic_energy(self, p: np.ndarray) -> float:
|
|
58
|
+
return 0.5 * float(np.sum(p * p)) / self.m
|
|
59
|
+
|
|
60
|
+
def normal_mode_frequencies(self) -> np.ndarray:
|
|
61
|
+
"""Angular frequencies omega_k of the N linear normal modes of a fixed-fixed harmonic chain.
|
|
62
|
+
|
|
63
|
+
Returns
|
|
64
|
+
-------
|
|
65
|
+
ndarray, shape (n,)
|
|
66
|
+
omega_k for k = 1..N.
|
|
67
|
+
"""
|
|
68
|
+
n = self.ndof
|
|
69
|
+
modes = np.arange(1, n + 1)
|
|
70
|
+
return 2.0 * np.sqrt(self.k / self.m) * np.sin(modes * np.pi / (2.0 * (n + 1)))
|
|
71
|
+
|
|
72
|
+
def normal_mode_coords(self, q: np.ndarray = None, p: np.ndarray = None):
|
|
73
|
+
"""Transform (q, p) to normal-mode coordinates (Q_k, P_k).
|
|
74
|
+
|
|
75
|
+
Via the discrete sine transform appropriate for fixed-fixed
|
|
76
|
+
boundaries: ``Q_k = sqrt(2/(N+1)) * sum_i q_i sin(i*k*pi/(N+1))``,
|
|
77
|
+
``i, k = 1..N``.
|
|
78
|
+
|
|
79
|
+
Parameters
|
|
80
|
+
----------
|
|
81
|
+
q, p : ndarray, optional
|
|
82
|
+
State to transform; defaults to the current state.
|
|
83
|
+
|
|
84
|
+
Returns
|
|
85
|
+
-------
|
|
86
|
+
Q, P : ndarray, shape (n,)
|
|
87
|
+
"""
|
|
88
|
+
q = self.q if q is None else q
|
|
89
|
+
p = self.p if p is None else p
|
|
90
|
+
n = self.ndof
|
|
91
|
+
i = np.arange(1, n + 1)
|
|
92
|
+
k = np.arange(1, n + 1)
|
|
93
|
+
S = np.sqrt(2.0 / (n + 1)) * np.sin(np.outer(i, k) * np.pi / (n + 1))
|
|
94
|
+
Q = S.T @ q
|
|
95
|
+
P = S.T @ p
|
|
96
|
+
return Q, P
|
|
97
|
+
|
|
98
|
+
def modal_energies(self, q: np.ndarray = None, p: np.ndarray = None) -> np.ndarray:
|
|
99
|
+
"""Energy stored in each normal mode.
|
|
100
|
+
|
|
101
|
+
``E_k = 0.5*(P_k^2/m + m*omega_k^2*Q_k^2)`` -- the diagnostic
|
|
102
|
+
used to show non-ergodic recurrence (the FPUT paradox) in
|
|
103
|
+
:class:`FPUTChain`.
|
|
104
|
+
|
|
105
|
+
Parameters
|
|
106
|
+
----------
|
|
107
|
+
q, p : ndarray, optional
|
|
108
|
+
State to evaluate; defaults to the current state.
|
|
109
|
+
|
|
110
|
+
Returns
|
|
111
|
+
-------
|
|
112
|
+
ndarray, shape (n,)
|
|
113
|
+
E_k for k = 1..N.
|
|
114
|
+
"""
|
|
115
|
+
Q, P = self.normal_mode_coords(q, p)
|
|
116
|
+
omega = self.normal_mode_frequencies()
|
|
117
|
+
return 0.5 * (P**2) / self.m + 0.5 * self.m * (omega**2) * (Q**2)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class HarmonicChain(_ChainBase):
|
|
121
|
+
"""Linear chain of N masses coupled by identical springs k, both
|
|
122
|
+
ends fixed to walls -- the exactly-solvable reference lattice whose
|
|
123
|
+
normal modes are the sine waves used to analyze the non-linear
|
|
124
|
+
:class:`FPUTChain` below.
|
|
125
|
+
|
|
126
|
+
Parameters
|
|
127
|
+
----------
|
|
128
|
+
q0, p0 : array-like, shape (n,)
|
|
129
|
+
Initial displacements and momenta.
|
|
130
|
+
m, k : float
|
|
131
|
+
Mass and spring constant (uniform across the chain).
|
|
132
|
+
"""
|
|
133
|
+
|
|
134
|
+
def __init__(self, q0, p0, m=1.0, k=1.0):
|
|
135
|
+
super().__init__(q0, p0, m=m, k=k)
|
|
136
|
+
self._force_njit = _make_harmonic_force(k)
|
|
137
|
+
|
|
138
|
+
def potential_energy(self, q: np.ndarray) -> float:
|
|
139
|
+
n = q.shape[0]
|
|
140
|
+
ext = np.empty(n + 2)
|
|
141
|
+
ext[0] = 0.0
|
|
142
|
+
ext[-1] = 0.0
|
|
143
|
+
ext[1:-1] = q
|
|
144
|
+
return 0.5 * self.k * float(np.sum(np.diff(ext) ** 2))
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
# ---------------------------------------------------------------------------
|
|
148
|
+
# FPUT beta-lattice: V(r) = k/2 r^2 + beta/4 r^4
|
|
149
|
+
# ---------------------------------------------------------------------------
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@njit(cache=True)
|
|
153
|
+
def _fput_force(q, k, beta):
|
|
154
|
+
n = q.shape[0]
|
|
155
|
+
out = np.empty(n)
|
|
156
|
+
for i in range(n):
|
|
157
|
+
left = q[i - 1] if i > 0 else 0.0
|
|
158
|
+
right = q[i + 1] if i < n - 1 else 0.0
|
|
159
|
+
r_right = right - q[i]
|
|
160
|
+
r_left = q[i] - left
|
|
161
|
+
f_right = k * r_right + beta * r_right**3
|
|
162
|
+
f_left = k * r_left + beta * r_left**3
|
|
163
|
+
out[i] = f_right - f_left
|
|
164
|
+
return out
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
@lru_cache(maxsize=64)
|
|
168
|
+
def _make_fput_force(k: float, beta: float):
|
|
169
|
+
@njit(cache=True)
|
|
170
|
+
def force(q, t):
|
|
171
|
+
return _fput_force(q, k, beta)
|
|
172
|
+
|
|
173
|
+
return force
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
class FPUTChain(_ChainBase):
|
|
177
|
+
"""Fermi-Pasta-Ulam-Tsingou beta-lattice: N masses (default 32) with
|
|
178
|
+
the quartic non-linear coupling potential
|
|
179
|
+
V(r) = (k/2) r^2 + (beta/4) r^4, r = q_{i+1} - q_i.
|
|
180
|
+
|
|
181
|
+
Famous for its "paradox": exciting a single low-order normal mode
|
|
182
|
+
does *not* thermalize energy across all modes as naive ergodic
|
|
183
|
+
reasoning predicts; instead the energy in :meth:`modal_energies`
|
|
184
|
+
returns almost exactly to the initially excited mode after a
|
|
185
|
+
characteristic recurrence time (the FPUT recurrence).
|
|
186
|
+
|
|
187
|
+
Parameters
|
|
188
|
+
----------
|
|
189
|
+
n : int
|
|
190
|
+
Number of masses.
|
|
191
|
+
m, k : float
|
|
192
|
+
Mass and (linear) spring constant.
|
|
193
|
+
beta : float
|
|
194
|
+
Quartic non-linearity coefficient.
|
|
195
|
+
mode : int
|
|
196
|
+
Which normal mode to excite initially.
|
|
197
|
+
amplitude : float
|
|
198
|
+
Initial amplitude of that mode.
|
|
199
|
+
"""
|
|
200
|
+
|
|
201
|
+
def __init__(self, n=32, m=1.0, k=1.0, beta=0.0, mode=1, amplitude=1.0):
|
|
202
|
+
self.beta = beta
|
|
203
|
+
q0 = amplitude * np.sin(np.arange(1, n + 1) * mode * np.pi / (n + 1))
|
|
204
|
+
p0 = np.zeros(n)
|
|
205
|
+
super().__init__(q0, p0, m=m, k=k)
|
|
206
|
+
self._force_njit = _make_fput_force(k, beta)
|
|
207
|
+
|
|
208
|
+
def potential_energy(self, q: np.ndarray) -> float:
|
|
209
|
+
n = q.shape[0]
|
|
210
|
+
ext = np.empty(n + 2)
|
|
211
|
+
ext[0] = 0.0
|
|
212
|
+
ext[-1] = 0.0
|
|
213
|
+
ext[1:-1] = q
|
|
214
|
+
r = np.diff(ext)
|
|
215
|
+
return float(np.sum(0.5 * self.k * r**2 + 0.25 * self.beta * r**4))
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
# ---------------------------------------------------------------------------
|
|
219
|
+
# Discrete Sine-Gordon chain (Frenkel-Kontorova): kink/antikink solitons
|
|
220
|
+
# ---------------------------------------------------------------------------
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
@njit(cache=True)
|
|
224
|
+
def _sine_gordon_force(q, k):
|
|
225
|
+
n = q.shape[0]
|
|
226
|
+
out = np.empty(n)
|
|
227
|
+
for i in range(n):
|
|
228
|
+
left = q[i - 1] if i > 0 else q[i]
|
|
229
|
+
right = q[i + 1] if i < n - 1 else q[i]
|
|
230
|
+
out[i] = k * (left - 2.0 * q[i] + right) - np.sin(q[i])
|
|
231
|
+
return out
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
@lru_cache(maxsize=64)
|
|
235
|
+
def _make_sine_gordon_force(k: float):
|
|
236
|
+
@njit(cache=True)
|
|
237
|
+
def force(q, t):
|
|
238
|
+
return _sine_gordon_force(q, k)
|
|
239
|
+
|
|
240
|
+
return force
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
class SineGordonChain(_ChainBase):
|
|
244
|
+
"""Discretized pendulum chain (Frenkel-Kontorova model): N coupled
|
|
245
|
+
pendulums, V = sum_i [k/2 (q_{i+1}-q_i)^2 + (1 - cos q_i)], with
|
|
246
|
+
free (Neumann-like) boundary conditions so a topological kink can
|
|
247
|
+
propagate off either end without an artificial restoring wall.
|
|
248
|
+
|
|
249
|
+
Continuum-limit kink initial data from :meth:`kink`
|
|
250
|
+
(q_i = 4*atan(exp((i - i0)/width)), width=1 for the default m=1,
|
|
251
|
+
k=1) launches a soliton that propagates and scatters off other
|
|
252
|
+
kinks/antikinks -- the discrete analogue of the exact sine-Gordon
|
|
253
|
+
soliton solutions.
|
|
254
|
+
|
|
255
|
+
Parameters
|
|
256
|
+
----------
|
|
257
|
+
n : int
|
|
258
|
+
Number of pendulums.
|
|
259
|
+
m, k : float
|
|
260
|
+
Mass and coupling constant.
|
|
261
|
+
q0, p0 : array-like, shape (n,), optional
|
|
262
|
+
Initial displacements and momenta; defaults to all zeros. Use
|
|
263
|
+
:meth:`kink` to build soliton initial data instead.
|
|
264
|
+
"""
|
|
265
|
+
|
|
266
|
+
def __init__(self, n=200, m=1.0, k=1.0, q0=None, p0=None):
|
|
267
|
+
if q0 is None:
|
|
268
|
+
q0 = np.zeros(n)
|
|
269
|
+
if p0 is None:
|
|
270
|
+
p0 = np.zeros(n)
|
|
271
|
+
super().__init__(q0, p0, m=m, k=k)
|
|
272
|
+
self._force_njit = _make_sine_gordon_force(k)
|
|
273
|
+
|
|
274
|
+
def potential_energy(self, q: np.ndarray) -> float:
|
|
275
|
+
r = np.diff(q) # free boundaries: no coupling to phantom exterior sites
|
|
276
|
+
return float(0.5 * self.k * np.sum(r**2) + np.sum(1.0 - np.cos(q)))
|
|
277
|
+
|
|
278
|
+
@staticmethod
|
|
279
|
+
def kink(n: int, center: float, width: float = 1.0, velocity: float = 0.0, polarity: int = 1) -> tuple:
|
|
280
|
+
"""Continuum-limit single-kink (or antikink, polarity=-1) initial
|
|
281
|
+
condition, optionally boosted to move at ``velocity`` (sites per
|
|
282
|
+
unit time; to the right for positive ``velocity``, assuming the
|
|
283
|
+
default ``m=1, k=1`` -- i.e. a natural wave/"light" speed
|
|
284
|
+
``c = sqrt(k/m) = 1``, the sine-Gordon equation's exact
|
|
285
|
+
Lorentz-invariance scale, so ``|velocity| < 1`` is required).
|
|
286
|
+
|
|
287
|
+
``width`` is *not* a free shape knob: q_xx = sin(q) (the
|
|
288
|
+
continuum equation this chain approximates, with the default
|
|
289
|
+
m=1, k=1) is solved by q=4*atan(exp(x)) only for exactly unit
|
|
290
|
+
width. Passing a different ``width`` here without also scaling
|
|
291
|
+
``k``/``m`` to match (``width = sqrt(k/m)``) gives a profile
|
|
292
|
+
that visibly relaxes/radiates under the true dynamics instead
|
|
293
|
+
of propagating as a clean, stable soliton.
|
|
294
|
+
|
|
295
|
+
The exact traveling-wave solution of the continuum sine-Gordon
|
|
296
|
+
equation is the *Lorentz-contracted* profile
|
|
297
|
+
``q(x, t) = polarity * 4*atan(exp(gamma * (x - center - v*t) / width))``,
|
|
298
|
+
``gamma = 1 / sqrt(1 - v**2)`` -- both the spatial profile at t=0
|
|
299
|
+
and its time derivative must be boosted together (not just the
|
|
300
|
+
momentum on top of the unboosted static profile, which is only
|
|
301
|
+
a small-v approximation and becomes badly wrong as ``|v|`` approaches 1).
|
|
302
|
+
|
|
303
|
+
Parameters
|
|
304
|
+
----------
|
|
305
|
+
n : int
|
|
306
|
+
Chain length.
|
|
307
|
+
center : float
|
|
308
|
+
Lattice site the kink is centered on at t=0.
|
|
309
|
+
width : float
|
|
310
|
+
Soliton width; must equal ``sqrt(k/m)`` (1, for the default
|
|
311
|
+
m=1, k=1) for a genuine soliton solution.
|
|
312
|
+
velocity : float
|
|
313
|
+
Boost velocity, ``|velocity| < 1``.
|
|
314
|
+
polarity : {1, -1}
|
|
315
|
+
+1 for a kink, -1 for an antikink.
|
|
316
|
+
|
|
317
|
+
Returns
|
|
318
|
+
-------
|
|
319
|
+
q0, p0 : ndarray, shape (n,)
|
|
320
|
+
Initial displacements and momenta.
|
|
321
|
+
|
|
322
|
+
Raises
|
|
323
|
+
------
|
|
324
|
+
ValueError
|
|
325
|
+
If ``abs(velocity) >= 1``.
|
|
326
|
+
"""
|
|
327
|
+
if abs(velocity) >= 1.0:
|
|
328
|
+
raise ValueError("|velocity| must be < 1 (the natural wave speed sqrt(k/m), default 1) for a valid moving kink")
|
|
329
|
+
gamma = 1.0 / np.sqrt(1.0 - velocity**2)
|
|
330
|
+
i = np.arange(n)
|
|
331
|
+
u = gamma * (i - center) / width
|
|
332
|
+
q0 = polarity * 4.0 * np.arctan(np.exp(u))
|
|
333
|
+
p0 = -polarity * velocity * (2.0 * gamma / width) / np.cosh(u)
|
|
334
|
+
return q0, p0
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"""Hamiltonian phase-space systems illustrating Liouville mechanics.
|
|
2
|
+
|
|
3
|
+
:class:`HenonHeilesSystem` is the classic 2-DOF non-linear oscillator
|
|
4
|
+
whose Poincare sections show the breakdown of KAM tori as energy
|
|
5
|
+
increases. :class:`PendulumSwarm` evolves an ensemble of independent
|
|
6
|
+
phase points under identical 1-DOF pendulum dynamics to visually
|
|
7
|
+
demonstrate Liouville's theorem (phase-space volume/area is conserved
|
|
8
|
+
by Hamiltonian flow, even though the occupied region shears and
|
|
9
|
+
filaments). :func:`pendulum_action_angle` computes the action J(E) and
|
|
10
|
+
period of a simple pendulum via elliptic integrals, the archetypal
|
|
11
|
+
worked example of action-angle variables.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from functools import lru_cache
|
|
17
|
+
|
|
18
|
+
import numpy as np
|
|
19
|
+
from numba import njit
|
|
20
|
+
from scipy import special
|
|
21
|
+
|
|
22
|
+
from physicskit.classical.core.base_system import HamiltonianSystem
|
|
23
|
+
|
|
24
|
+
__all__ = ["HenonHeilesSystem", "PendulumSwarm", "pendulum_action_angle"]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@njit(cache=True)
|
|
28
|
+
def _henon_heiles_force(q, t):
|
|
29
|
+
x, y = q[0], q[1]
|
|
30
|
+
out = np.empty(2)
|
|
31
|
+
out[0] = -(x + 2.0 * x * y)
|
|
32
|
+
out[1] = -(y + x * x - y * y)
|
|
33
|
+
return out
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class HenonHeilesSystem(HamiltonianSystem):
|
|
37
|
+
"""H = (px^2 + py^2)/2 + (x^2 + y^2)/2 + x^2*y - y^3/3.
|
|
38
|
+
|
|
39
|
+
A non-integrable 2-DOF oscillator (originally a model of stellar
|
|
40
|
+
orbits in an axisymmetric galactic potential). Below E ~ 1/6 the
|
|
41
|
+
Poincare section (x, px) at y=0 is dominated by smooth KAM tori;
|
|
42
|
+
as E approaches and exceeds 1/6 the tori progressively break up
|
|
43
|
+
into chaotic seas.
|
|
44
|
+
|
|
45
|
+
Parameters
|
|
46
|
+
----------
|
|
47
|
+
q0, p0 : array-like, shape (2,)
|
|
48
|
+
Initial position ``(x, y)`` and momentum ``(px, py)``.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
separable = True
|
|
52
|
+
mass_inv = 1.0
|
|
53
|
+
|
|
54
|
+
def __init__(self, q0, p0):
|
|
55
|
+
self._force_njit = _henon_heiles_force
|
|
56
|
+
super().__init__(q0, p0)
|
|
57
|
+
|
|
58
|
+
def kinetic_energy(self, p: np.ndarray) -> float:
|
|
59
|
+
return 0.5 * float(np.dot(p, p))
|
|
60
|
+
|
|
61
|
+
def potential_energy(self, q: np.ndarray) -> float:
|
|
62
|
+
x, y = q[0], q[1]
|
|
63
|
+
return 0.5 * (x * x + y * y) + x * x * y - (y**3) / 3.0
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@lru_cache(maxsize=64)
|
|
67
|
+
def _make_pendulum_force(g_over_l: float):
|
|
68
|
+
@njit(cache=True)
|
|
69
|
+
def force(q, t):
|
|
70
|
+
return -g_over_l * np.sin(q)
|
|
71
|
+
|
|
72
|
+
return force
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class PendulumSwarm(HamiltonianSystem):
|
|
76
|
+
"""An ensemble of N independent, identical simple pendulums,
|
|
77
|
+
H_i = p_i^2 / 2 - (g/l) cos(q_i) for each i = 1..N.
|
|
78
|
+
|
|
79
|
+
The pendulums are mutually decoupled (each is its own 1-DOF
|
|
80
|
+
Hamiltonian system) but are integrated together as one N-DOF
|
|
81
|
+
separable Hamiltonian so a whole ensemble can be evolved with a
|
|
82
|
+
single Yoshida4/Verlet call. Seeding N points inside a small
|
|
83
|
+
(dq, dp) box and watching the occupied phase-space patch shear
|
|
84
|
+
over time -- its area invariant even as its shape stretches into a
|
|
85
|
+
filament -- is the standard visual proof of Liouville's theorem.
|
|
86
|
+
|
|
87
|
+
Parameters
|
|
88
|
+
----------
|
|
89
|
+
q0, p0 : array-like, shape (n,)
|
|
90
|
+
Initial angles and momenta, one pair per pendulum in the ensemble.
|
|
91
|
+
g_over_l : float
|
|
92
|
+
Ratio g/l shared by every pendulum.
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
separable = True
|
|
96
|
+
|
|
97
|
+
def __init__(self, q0, p0, g_over_l: float = 1.0):
|
|
98
|
+
self.g_over_l = g_over_l
|
|
99
|
+
self.mass_inv = 1.0
|
|
100
|
+
self._force_njit = _make_pendulum_force(g_over_l)
|
|
101
|
+
super().__init__(q0, p0)
|
|
102
|
+
|
|
103
|
+
@classmethod
|
|
104
|
+
def from_box(cls, q_center: float, p_center: float, dq: float, dp: float, n: int = 1000, g_over_l: float = 1.0, seed: int = 0):
|
|
105
|
+
"""Seed N phase points uniformly inside a (dq x dp) box centered at (q_center, p_center).
|
|
106
|
+
|
|
107
|
+
Parameters
|
|
108
|
+
----------
|
|
109
|
+
q_center, p_center : float
|
|
110
|
+
Box center.
|
|
111
|
+
dq, dp : float
|
|
112
|
+
Box width and height.
|
|
113
|
+
n : int
|
|
114
|
+
Number of phase points to seed.
|
|
115
|
+
g_over_l : float
|
|
116
|
+
Ratio g/l shared by every pendulum.
|
|
117
|
+
seed : int
|
|
118
|
+
Random seed for reproducibility.
|
|
119
|
+
|
|
120
|
+
Returns
|
|
121
|
+
-------
|
|
122
|
+
PendulumSwarm
|
|
123
|
+
"""
|
|
124
|
+
rng = np.random.default_rng(seed)
|
|
125
|
+
q0 = q_center + (rng.random(n) - 0.5) * dq
|
|
126
|
+
p0 = p_center + (rng.random(n) - 0.5) * dp
|
|
127
|
+
return cls(q0, p0, g_over_l=g_over_l)
|
|
128
|
+
|
|
129
|
+
def kinetic_energy(self, p: np.ndarray) -> float:
|
|
130
|
+
return 0.5 * float(np.sum(p * p))
|
|
131
|
+
|
|
132
|
+
def potential_energy(self, q: np.ndarray) -> float:
|
|
133
|
+
return -self.g_over_l * float(np.sum(np.cos(q)))
|
|
134
|
+
|
|
135
|
+
def per_particle_energy(self, q: np.ndarray = None, p: np.ndarray = None) -> np.ndarray:
|
|
136
|
+
"""Energy of each individual pendulum, used to color/track the swarm.
|
|
137
|
+
|
|
138
|
+
Conserved per-particle since the ensemble is decoupled.
|
|
139
|
+
|
|
140
|
+
Parameters
|
|
141
|
+
----------
|
|
142
|
+
q, p : ndarray, optional
|
|
143
|
+
State to evaluate; defaults to the current state.
|
|
144
|
+
|
|
145
|
+
Returns
|
|
146
|
+
-------
|
|
147
|
+
ndarray, shape (n,)
|
|
148
|
+
"""
|
|
149
|
+
q = self.q if q is None else q
|
|
150
|
+
p = self.p if p is None else p
|
|
151
|
+
return 0.5 * p * p - self.g_over_l * np.cos(q)
|
|
152
|
+
|
|
153
|
+
def phase_space_area(self, q: np.ndarray = None, p: np.ndarray = None) -> float:
|
|
154
|
+
"""Convex-hull area of the current swarm's (q, p) point cloud.
|
|
155
|
+
|
|
156
|
+
A practical proxy for the occupied phase-space volume.
|
|
157
|
+
|
|
158
|
+
Parameters
|
|
159
|
+
----------
|
|
160
|
+
q, p : ndarray, optional
|
|
161
|
+
State to evaluate; defaults to the current state.
|
|
162
|
+
|
|
163
|
+
Returns
|
|
164
|
+
-------
|
|
165
|
+
float
|
|
166
|
+
"""
|
|
167
|
+
from scipy.spatial import ConvexHull
|
|
168
|
+
|
|
169
|
+
q = self.q if q is None else q
|
|
170
|
+
p = self.p if p is None else p
|
|
171
|
+
pts = np.column_stack([q, p])
|
|
172
|
+
return float(ConvexHull(pts).volume) # 'volume' is the 2D area for a 2D hull
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def pendulum_action_angle(E: float, g_over_l: float = 1.0):
|
|
176
|
+
"""Action J(E) and period T(E) for a simple pendulum H = p^2/2 - (g/l)cos(q).
|
|
177
|
+
|
|
178
|
+
For librating orbits (-g/l < E < g/l), uses the standard elliptic-integral
|
|
179
|
+
closed form: with k^2 = (E + g/l) / (2 g/l),
|
|
180
|
+
|
|
181
|
+
J(E) = (8/pi) * sqrt(g/l) * [E_ellip(k) - (1 - k^2) * K_ellip(k)]
|
|
182
|
+
T(E) = 4 * K_ellip(k) / sqrt(g/l)
|
|
183
|
+
|
|
184
|
+
where K, E are the complete elliptic integrals of the first/second
|
|
185
|
+
kind (SciPy's ``special.ellipk``/``ellipe`` take the parameter
|
|
186
|
+
m = k^2, not the modulus k).
|
|
187
|
+
|
|
188
|
+
Parameters
|
|
189
|
+
----------
|
|
190
|
+
E : float
|
|
191
|
+
Energy, with ``-g_over_l < E < g_over_l`` (librating orbits only).
|
|
192
|
+
g_over_l : float
|
|
193
|
+
Ratio g/l.
|
|
194
|
+
|
|
195
|
+
Returns
|
|
196
|
+
-------
|
|
197
|
+
J : float
|
|
198
|
+
Action.
|
|
199
|
+
T : float
|
|
200
|
+
Orbital period.
|
|
201
|
+
|
|
202
|
+
Raises
|
|
203
|
+
------
|
|
204
|
+
ValueError
|
|
205
|
+
If ``E`` is outside the librating range.
|
|
206
|
+
"""
|
|
207
|
+
w0 = np.sqrt(g_over_l)
|
|
208
|
+
if -g_over_l >= E or g_over_l <= E:
|
|
209
|
+
raise ValueError("pendulum_action_angle only supports librating orbits (-g/l < E < g/l)")
|
|
210
|
+
m = (E + g_over_l) / (2.0 * g_over_l) # = k^2
|
|
211
|
+
K = special.ellipk(m)
|
|
212
|
+
Eei = special.ellipe(m)
|
|
213
|
+
J = (8.0 / np.pi) * w0 * (Eei - (1.0 - m) * K)
|
|
214
|
+
T = 4.0 * K / w0
|
|
215
|
+
return float(J), float(T)
|