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,281 @@
|
|
|
1
|
+
"""1D compressible flow: the Euler equations, normal shocks, and the Sod shock tube.
|
|
2
|
+
|
|
3
|
+
Where potential and viscous flow assume incompressibility, this module
|
|
4
|
+
drops that assumption entirely: the 1D Euler equations for an inviscid,
|
|
5
|
+
compressible, ideal gas support genuinely discontinuous solutions (shocks
|
|
6
|
+
and contact discontinuities) that no continuous velocity field can produce.
|
|
7
|
+
:func:`rankine_hugoniot_jump_conditions` and :func:`normal_shock_relations`
|
|
8
|
+
give the exact algebraic jump a shock must satisfy; :func:`sod_shock_tube`
|
|
9
|
+
resolves that jump -- alongside a contact discontinuity and an expansion
|
|
10
|
+
fan -- dynamically, by integrating the full nonlinear equations through a
|
|
11
|
+
classic Riemann problem.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import numpy as np
|
|
17
|
+
from numba import njit
|
|
18
|
+
from numpy.typing import NDArray
|
|
19
|
+
|
|
20
|
+
from physicskit.fluids.exceptions import InvalidParameterError
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
"rankine_hugoniot_jump_conditions",
|
|
24
|
+
"normal_shock_relations",
|
|
25
|
+
"sod_shock_tube",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def rankine_hugoniot_jump_conditions(rho1: float, u1: float, p1: float, rho2: float, u2: float, p2: float) -> dict[str, float]:
|
|
30
|
+
"""Residuals of the Rankine-Hugoniot jump conditions across a stationary discontinuity.
|
|
31
|
+
|
|
32
|
+
A steady discontinuity in a 1D inviscid compressible flow (states 1
|
|
33
|
+
upstream, 2 downstream, in the frame where the discontinuity itself is
|
|
34
|
+
at rest) must conserve mass, momentum, and energy flux exactly:
|
|
35
|
+
|
|
36
|
+
.. math::
|
|
37
|
+
|
|
38
|
+
\\rho_1 u_1 = \\rho_2 u_2, \\qquad
|
|
39
|
+
p_1 + \\rho_1 u_1^2 = p_2 + \\rho_2 u_2^2, \\qquad
|
|
40
|
+
h_1 + \\tfrac{1}{2}u_1^2 = h_2 + \\tfrac{1}{2}u_2^2,
|
|
41
|
+
|
|
42
|
+
where :math:`h=\\gamma p/((\\gamma-1)\\rho)` is specific enthalpy (an
|
|
43
|
+
ideal gas is assumed only for the energy residual). Rankine (1870) first
|
|
44
|
+
wrote the mass and momentum conditions; Hugoniot (1887, 1889) added the
|
|
45
|
+
energy condition and the resulting relation between the shock's pressure
|
|
46
|
+
and density ratios now called the Hugoniot curve. This function returns
|
|
47
|
+
each condition's residual, primarily to *verify* a candidate jump (e.g.
|
|
48
|
+
the output of :func:`normal_shock_relations`) rather than to solve for
|
|
49
|
+
one directly.
|
|
50
|
+
|
|
51
|
+
Parameters
|
|
52
|
+
----------
|
|
53
|
+
rho1, u1, p1 : float
|
|
54
|
+
Upstream density, velocity, and pressure.
|
|
55
|
+
rho2, u2, p2 : float
|
|
56
|
+
Downstream density, velocity, and pressure.
|
|
57
|
+
|
|
58
|
+
Returns
|
|
59
|
+
-------
|
|
60
|
+
dict
|
|
61
|
+
``{"mass": ..., "momentum": ..., "energy": ...}`` residuals; all
|
|
62
|
+
zero for an exactly satisfied jump.
|
|
63
|
+
|
|
64
|
+
Examples
|
|
65
|
+
--------
|
|
66
|
+
>>> jump = normal_shock_relations(M1=2.0)
|
|
67
|
+
>>> residuals = rankine_hugoniot_jump_conditions(1.0, 2.0, 1.0 / 1.4, jump["rho2_rho1"], 2.0 / jump["rho2_rho1"], jump["p2_p1"] / 1.4)
|
|
68
|
+
>>> bool(max(abs(v) for v in residuals.values()) < 1e-10)
|
|
69
|
+
True
|
|
70
|
+
"""
|
|
71
|
+
gamma = 1.4
|
|
72
|
+
mass = rho1 * u1 - rho2 * u2
|
|
73
|
+
momentum = (p1 + rho1 * u1**2) - (p2 + rho2 * u2**2)
|
|
74
|
+
h1 = gamma * p1 / ((gamma - 1.0) * rho1)
|
|
75
|
+
h2 = gamma * p2 / ((gamma - 1.0) * rho2)
|
|
76
|
+
energy = (h1 + 0.5 * u1**2) - (h2 + 0.5 * u2**2)
|
|
77
|
+
return {"mass": mass, "momentum": momentum, "energy": energy}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def normal_shock_relations(M1: float, gamma: float = 1.4) -> dict[str, float]:
|
|
81
|
+
"""Ideal-gas normal shock relations as a function of upstream Mach number.
|
|
82
|
+
|
|
83
|
+
Solving the Rankine-Hugoniot conditions
|
|
84
|
+
(:func:`rankine_hugoniot_jump_conditions`) for an ideal gas gives the
|
|
85
|
+
downstream state entirely in terms of the upstream Mach number
|
|
86
|
+
:math:`M_1 = u_1/c_1`:
|
|
87
|
+
|
|
88
|
+
.. math::
|
|
89
|
+
|
|
90
|
+
\\frac{p_2}{p_1} = 1 + \\frac{2\\gamma}{\\gamma+1}(M_1^2-1), \\qquad
|
|
91
|
+
\\frac{\\rho_2}{\\rho_1} = \\frac{(\\gamma+1)M_1^2}{(\\gamma-1)M_1^2+2}, \\qquad
|
|
92
|
+
M_2^2 = \\frac{1+\\tfrac{\\gamma-1}{2}M_1^2}{\\gamma M_1^2-\\tfrac{\\gamma-1}{2}}.
|
|
93
|
+
|
|
94
|
+
Only :math:`M_1 \\geq 1` gives a physically admissible (entropy-increasing)
|
|
95
|
+
shock; a supersonic upstream flow needs a mechanism, such as this jump,
|
|
96
|
+
to return to subsonic downstream, and no analogous jump exists in reverse.
|
|
97
|
+
|
|
98
|
+
Parameters
|
|
99
|
+
----------
|
|
100
|
+
M1 : float
|
|
101
|
+
Upstream Mach number; must be at least 1.
|
|
102
|
+
gamma : float, default 1.4
|
|
103
|
+
Ratio of specific heats (1.4 for a diatomic ideal gas, e.g. air).
|
|
104
|
+
|
|
105
|
+
Returns
|
|
106
|
+
-------
|
|
107
|
+
dict
|
|
108
|
+
``{"p2_p1": ..., "rho2_rho1": ..., "T2_T1": ..., "M2": ...}``.
|
|
109
|
+
|
|
110
|
+
Raises
|
|
111
|
+
------
|
|
112
|
+
InvalidParameterError
|
|
113
|
+
If `M1` is less than 1.
|
|
114
|
+
|
|
115
|
+
Examples
|
|
116
|
+
--------
|
|
117
|
+
>>> jump = normal_shock_relations(M1=1.0)
|
|
118
|
+
>>> [round(v, 6) for v in (jump["p2_p1"], jump["rho2_rho1"], jump["M2"])]
|
|
119
|
+
[1.0, 1.0, 1.0]
|
|
120
|
+
>>> jump = normal_shock_relations(M1=2.0)
|
|
121
|
+
>>> round(jump["p2_p1"], 3), round(jump["rho2_rho1"], 3), round(jump["M2"], 3)
|
|
122
|
+
(4.5, 2.667, 0.577)
|
|
123
|
+
"""
|
|
124
|
+
if M1 < 1.0:
|
|
125
|
+
raise InvalidParameterError(f"M1 must be >= 1 for an admissible (entropy-increasing) shock, got {M1}")
|
|
126
|
+
p2_p1 = 1.0 + (2.0 * gamma / (gamma + 1.0)) * (M1**2 - 1.0)
|
|
127
|
+
rho2_rho1 = ((gamma + 1.0) * M1**2) / ((gamma - 1.0) * M1**2 + 2.0)
|
|
128
|
+
T2_T1 = p2_p1 / rho2_rho1
|
|
129
|
+
M2 = np.sqrt((1.0 + 0.5 * (gamma - 1.0) * M1**2) / (gamma * M1**2 - 0.5 * (gamma - 1.0)))
|
|
130
|
+
return {"p2_p1": float(p2_p1), "rho2_rho1": float(rho2_rho1), "T2_T1": float(T2_T1), "M2": float(M2)}
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
@njit(cache=True)
|
|
134
|
+
def _euler_flux(U: NDArray[np.float64], gamma: float) -> NDArray[np.float64]:
|
|
135
|
+
"""Physical flux of the 1D Euler equations in conservative variables.
|
|
136
|
+
|
|
137
|
+
Parameters
|
|
138
|
+
----------
|
|
139
|
+
U : ndarray of float, shape (3,)
|
|
140
|
+
Conservative state ``(rho, rho*u, E)``.
|
|
141
|
+
gamma : float
|
|
142
|
+
Ratio of specific heats.
|
|
143
|
+
|
|
144
|
+
Returns
|
|
145
|
+
-------
|
|
146
|
+
ndarray of float, shape (3,)
|
|
147
|
+
Flux ``(rho*u, rho*u^2 + p, u*(E + p))``.
|
|
148
|
+
"""
|
|
149
|
+
rho, mom, E = U[0], U[1], U[2]
|
|
150
|
+
u = mom / rho
|
|
151
|
+
p = (gamma - 1.0) * (E - 0.5 * rho * u * u)
|
|
152
|
+
flux = np.empty(3)
|
|
153
|
+
flux[0] = mom
|
|
154
|
+
flux[1] = mom * u + p
|
|
155
|
+
flux[2] = u * (E + p)
|
|
156
|
+
return flux
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
@njit(cache=True)
|
|
160
|
+
def _lax_friedrichs_evolve(U0: NDArray[np.float64], dx: float, dt: float, gamma: float, steps: int) -> NDArray[np.float64]:
|
|
161
|
+
"""Evolve the 1D Euler equations with the Lax-Friedrichs finite-volume scheme.
|
|
162
|
+
|
|
163
|
+
Parameters
|
|
164
|
+
----------
|
|
165
|
+
U0 : ndarray of float, shape (nx, 3)
|
|
166
|
+
Initial conservative state at each cell.
|
|
167
|
+
dx : float
|
|
168
|
+
Cell width.
|
|
169
|
+
dt : float
|
|
170
|
+
Time step (assumed to already satisfy the CFL condition).
|
|
171
|
+
gamma : float
|
|
172
|
+
Ratio of specific heats.
|
|
173
|
+
steps : int
|
|
174
|
+
Number of time steps to advance.
|
|
175
|
+
|
|
176
|
+
Returns
|
|
177
|
+
-------
|
|
178
|
+
ndarray of float, shape (nx, 3)
|
|
179
|
+
Conservative state after `steps` steps. The two boundary cells are
|
|
180
|
+
held fixed at their initial (far-field) values, approximating an
|
|
181
|
+
open/transmissive boundary over the short times these simulations run.
|
|
182
|
+
"""
|
|
183
|
+
nx = U0.shape[0]
|
|
184
|
+
U = U0.copy()
|
|
185
|
+
lam = dt / dx
|
|
186
|
+
for _ in range(steps):
|
|
187
|
+
U_new = U.copy()
|
|
188
|
+
for i in range(1, nx - 1):
|
|
189
|
+
fL = _euler_flux(U[i - 1], gamma)
|
|
190
|
+
fR = _euler_flux(U[i + 1], gamma)
|
|
191
|
+
U_new[i] = 0.5 * (U[i - 1] + U[i + 1]) - 0.5 * lam * (fR - fL)
|
|
192
|
+
U = U_new
|
|
193
|
+
return U
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def sod_shock_tube(nx: int = 400, x0: float = 0.5, t_final: float = 0.2, gamma: float = 1.4, cfl: float = 0.5) -> dict[str, NDArray[np.float64]]:
|
|
197
|
+
"""Solve the classic Sod shock-tube problem with a Lax-Friedrichs finite-volume scheme.
|
|
198
|
+
|
|
199
|
+
The Sod (1978) problem is the standard test Riemann problem for a
|
|
200
|
+
compressible-flow solver: a diaphragm at `x0` initially separates high
|
|
201
|
+
pressure/density gas (left, at rest) from low pressure/density gas
|
|
202
|
+
(right, at rest); removing the diaphragm at :math:`t=0` produces, for
|
|
203
|
+
:math:`t>0`, exactly three simple waves fanning out from `x0` -- a
|
|
204
|
+
left-running rarefaction (expansion) fan, a right-running contact
|
|
205
|
+
discontinuity (a density jump with continuous pressure and velocity),
|
|
206
|
+
and a right-running shock satisfying
|
|
207
|
+
:func:`rankine_hugoniot_jump_conditions` -- against which any
|
|
208
|
+
finite-volume scheme's numerical diffusion and shock-capturing behavior
|
|
209
|
+
can be judged. The domain is ``[0, 1]``; the classic Sod initial
|
|
210
|
+
condition is ``(rho, u, p) = (1, 0, 1)`` for ``x < x0`` and
|
|
211
|
+
``(0.125, 0, 0.1)`` for ``x >= x0``.
|
|
212
|
+
|
|
213
|
+
Parameters
|
|
214
|
+
----------
|
|
215
|
+
nx : int, default 400
|
|
216
|
+
Number of finite-volume cells.
|
|
217
|
+
x0 : float, default 0.5
|
|
218
|
+
Initial diaphragm location.
|
|
219
|
+
t_final : float, default 0.2
|
|
220
|
+
Time to integrate to.
|
|
221
|
+
gamma : float, default 1.4
|
|
222
|
+
Ratio of specific heats.
|
|
223
|
+
cfl : float, default 0.5
|
|
224
|
+
Courant number; the time step is chosen adaptively each macro-chunk
|
|
225
|
+
of the integration as ``cfl * dx / max(|u| + c)``, and must satisfy
|
|
226
|
+
``0 < cfl <= 1`` for the explicit scheme to be stable.
|
|
227
|
+
|
|
228
|
+
Returns
|
|
229
|
+
-------
|
|
230
|
+
dict
|
|
231
|
+
``{"x": ..., "rho": ..., "u": ..., "p": ...}`` at `t_final`, each an
|
|
232
|
+
array of length `nx`.
|
|
233
|
+
|
|
234
|
+
Raises
|
|
235
|
+
------
|
|
236
|
+
InvalidParameterError
|
|
237
|
+
If `nx` is smaller than 4, `t_final` is not positive, or `cfl` is
|
|
238
|
+
not in ``(0, 1]``.
|
|
239
|
+
|
|
240
|
+
Examples
|
|
241
|
+
--------
|
|
242
|
+
>>> result = sod_shock_tube(nx=200, t_final=0.15)
|
|
243
|
+
>>> result["rho"].shape
|
|
244
|
+
(200,)
|
|
245
|
+
>>> bool(result["rho"][0] > result["rho"][-1] > 0) # left state denser than right
|
|
246
|
+
True
|
|
247
|
+
"""
|
|
248
|
+
if nx < 4:
|
|
249
|
+
raise InvalidParameterError(f"nx must be at least 4, got {nx}")
|
|
250
|
+
if t_final <= 0:
|
|
251
|
+
raise InvalidParameterError(f"t_final must be positive, got {t_final}")
|
|
252
|
+
if not 0.0 < cfl <= 1.0:
|
|
253
|
+
raise InvalidParameterError(f"cfl must be in (0, 1], got {cfl}")
|
|
254
|
+
|
|
255
|
+
dx = 1.0 / nx
|
|
256
|
+
x = (np.arange(nx) + 0.5) * dx
|
|
257
|
+
rho = np.where(x < x0, 1.0, 0.125)
|
|
258
|
+
u = np.zeros(nx)
|
|
259
|
+
p = np.where(x < x0, 1.0, 0.1)
|
|
260
|
+
|
|
261
|
+
U = np.empty((nx, 3))
|
|
262
|
+
U[:, 0] = rho
|
|
263
|
+
U[:, 1] = rho * u
|
|
264
|
+
U[:, 2] = p / (gamma - 1.0) + 0.5 * rho * u**2
|
|
265
|
+
|
|
266
|
+
t = 0.0
|
|
267
|
+
while t < t_final:
|
|
268
|
+
rho_now = U[:, 0]
|
|
269
|
+
u_now = U[:, 1] / rho_now
|
|
270
|
+
p_now = (gamma - 1.0) * (U[:, 2] - 0.5 * rho_now * u_now**2)
|
|
271
|
+
c_now = np.sqrt(gamma * p_now / rho_now)
|
|
272
|
+
dt = cfl * dx / np.max(np.abs(u_now) + c_now)
|
|
273
|
+
dt = min(dt, t_final - t)
|
|
274
|
+
chunk_steps = 1
|
|
275
|
+
U = _lax_friedrichs_evolve(U, dx, dt, gamma, chunk_steps)
|
|
276
|
+
t += dt
|
|
277
|
+
|
|
278
|
+
rho = U[:, 0]
|
|
279
|
+
u = U[:, 1] / rho
|
|
280
|
+
p = (gamma - 1.0) * (U[:, 2] - 0.5 * rho * u**2)
|
|
281
|
+
return {"x": x, "rho": rho, "u": u, "p": p}
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
"""Shear and buoyancy-driven instabilities: Kelvin-Helmholtz and Rayleigh-Taylor.
|
|
2
|
+
|
|
3
|
+
Both instabilities turn a smooth base state (a shear layer, a stratified
|
|
4
|
+
interface) into vigorous mixing given only an infinitesimal perturbation --
|
|
5
|
+
the base state is *linearly unstable*, not just occasionally disturbed into
|
|
6
|
+
instability. This module pairs each initial condition with the linear
|
|
7
|
+
growth-rate law that predicts how fast its seeding ripple should grow, and
|
|
8
|
+
with a full nonlinear simulation (built on
|
|
9
|
+
:mod:`physicskit.fluids.systems.navier_stokes`) that grows it well past the
|
|
10
|
+
point where the linear theory stops applying, into the rolled-up vortices
|
|
11
|
+
(Kelvin-Helmholtz) or mushroom-shaped plumes (Rayleigh-Taylor) these
|
|
12
|
+
instabilities are named for.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import numpy as np
|
|
18
|
+
from numpy.typing import NDArray
|
|
19
|
+
|
|
20
|
+
from physicskit.fluids.core.timestepping import integrate_boussinesq, integrate_vorticity_streamfunction
|
|
21
|
+
from physicskit.fluids.exceptions import InvalidParameterError
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"kelvin_helmholtz_ic",
|
|
25
|
+
"kelvin_helmholtz_growth_rate",
|
|
26
|
+
"simulate_kelvin_helmholtz",
|
|
27
|
+
"rayleigh_taylor_ic",
|
|
28
|
+
"rayleigh_taylor_growth_rate",
|
|
29
|
+
"simulate_rayleigh_taylor",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def kelvin_helmholtz_ic(n: int, length: float, shear_width: float = 0.1, perturbation_amplitude: float = 0.05) -> NDArray[np.float64]:
|
|
34
|
+
"""Vorticity initial condition for the Kelvin-Helmholtz shear-layer instability.
|
|
35
|
+
|
|
36
|
+
A thin vortex sheet at mid-domain, with a small sinusoidal ripple that
|
|
37
|
+
seeds the instability: under
|
|
38
|
+
:class:`physicskit.fluids.systems.navier_stokes.NavierStokes2D`, the
|
|
39
|
+
ripple's amplitude grows -- initially at the rate predicted by
|
|
40
|
+
:func:`kelvin_helmholtz_growth_rate` -- as the shear layer rolls up into
|
|
41
|
+
a row of discrete "cat's eye" vortices.
|
|
42
|
+
|
|
43
|
+
Parameters
|
|
44
|
+
----------
|
|
45
|
+
n : int
|
|
46
|
+
Number of grid points along each axis.
|
|
47
|
+
length : float
|
|
48
|
+
Physical domain size.
|
|
49
|
+
shear_width : float, default 0.1
|
|
50
|
+
Thickness of the vortex sheet.
|
|
51
|
+
perturbation_amplitude : float, default 0.05
|
|
52
|
+
Amplitude of the seeding sinusoidal ripple.
|
|
53
|
+
|
|
54
|
+
Returns
|
|
55
|
+
-------
|
|
56
|
+
ndarray of float, shape (n, n)
|
|
57
|
+
Vorticity field.
|
|
58
|
+
|
|
59
|
+
Examples
|
|
60
|
+
--------
|
|
61
|
+
>>> omega0 = kelvin_helmholtz_ic(64, 2 * 3.141592653589793)
|
|
62
|
+
>>> omega0.shape
|
|
63
|
+
(64, 64)
|
|
64
|
+
>>> bool(omega0.max() > 0)
|
|
65
|
+
True
|
|
66
|
+
"""
|
|
67
|
+
x = np.linspace(0, length, n, endpoint=False)
|
|
68
|
+
X, Y = np.meshgrid(x, x, indexing="ij")
|
|
69
|
+
y0 = length / 2
|
|
70
|
+
base = (1.0 / shear_width) / np.cosh((Y - y0) / shear_width) ** 2
|
|
71
|
+
ripple = perturbation_amplitude * np.sin(2 * np.pi * X / length) * np.exp(-(((Y - y0) / (length / 4)) ** 2))
|
|
72
|
+
return base + ripple
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def kelvin_helmholtz_growth_rate(k: float, delta_u: float) -> float:
|
|
76
|
+
"""Inviscid linear growth rate of a Kelvin-Helmholtz-unstable vortex sheet.
|
|
77
|
+
|
|
78
|
+
For a vortex sheet (an infinitesimally thin shear layer with a velocity
|
|
79
|
+
jump `delta_u` across it), linear stability analysis of the Euler
|
|
80
|
+
equations gives a perturbation of wavenumber `k` growing as
|
|
81
|
+
:math:`e^{\\sigma t}` with
|
|
82
|
+
|
|
83
|
+
.. math::
|
|
84
|
+
|
|
85
|
+
\\sigma(k) = \\frac{k\\,\\Delta u}{2},
|
|
86
|
+
|
|
87
|
+
unstable at *every* wavenumber with no threshold velocity -- unlike,
|
|
88
|
+
say, the Rayleigh-Taylor instability below, which is stabilized at short
|
|
89
|
+
wavelength by surface tension or diffusion. A shear layer of finite
|
|
90
|
+
thickness `shear_width` (as built by :func:`kelvin_helmholtz_ic`)
|
|
91
|
+
instead has a fastest-growing wavelength comparable to the layer
|
|
92
|
+
thickness, with growth cut off entirely for :math:`k\\,(\\text{shear
|
|
93
|
+
width})` large (Michalke, 1964); this vortex-sheet limit is the
|
|
94
|
+
thin-layer, small-``k`` approximation to that fuller theory, and is what
|
|
95
|
+
the shear-layer roll-up in :func:`kelvin_helmholtz_ic` grows at
|
|
96
|
+
initially, before finite-thickness and nonlinear effects take over.
|
|
97
|
+
|
|
98
|
+
Parameters
|
|
99
|
+
----------
|
|
100
|
+
k : float
|
|
101
|
+
Perturbation wavenumber (angular, :math:`2\\pi/\\lambda`).
|
|
102
|
+
delta_u : float
|
|
103
|
+
Velocity jump across the shear layer.
|
|
104
|
+
|
|
105
|
+
Returns
|
|
106
|
+
-------
|
|
107
|
+
float
|
|
108
|
+
Growth rate :math:`\\sigma`, in units of inverse time.
|
|
109
|
+
|
|
110
|
+
Examples
|
|
111
|
+
--------
|
|
112
|
+
>>> round(kelvin_helmholtz_growth_rate(k=1.0, delta_u=2.0), 6)
|
|
113
|
+
1.0
|
|
114
|
+
"""
|
|
115
|
+
return 0.5 * k * delta_u
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def simulate_kelvin_helmholtz(omega0: NDArray[np.float64], nu: float, dt: float, steps: int, length: float) -> dict[str, NDArray[np.float64]]:
|
|
119
|
+
"""Time-step the 2D incompressible Kelvin-Helmholtz shear layer with RK4.
|
|
120
|
+
|
|
121
|
+
A thin wrapper around
|
|
122
|
+
:func:`physicskit.fluids.core.timestepping.integrate_vorticity_streamfunction`
|
|
123
|
+
-- the same pseudo-spectral vorticity-streamfunction Navier-Stokes
|
|
124
|
+
engine :func:`simulate_rayleigh_taylor` builds its Boussinesq extension
|
|
125
|
+
on -- evolving :func:`kelvin_helmholtz_ic` through the roll-up of its
|
|
126
|
+
seeded ripple into the characteristic "cat's-eye" vortex row, well past
|
|
127
|
+
the point :func:`kelvin_helmholtz_growth_rate`'s linear-theory
|
|
128
|
+
exponential growth stops applying.
|
|
129
|
+
|
|
130
|
+
Parameters
|
|
131
|
+
----------
|
|
132
|
+
omega0 : ndarray of float, shape (n, n)
|
|
133
|
+
Initial vorticity field, as from :func:`kelvin_helmholtz_ic`.
|
|
134
|
+
nu : float
|
|
135
|
+
Kinematic viscosity; must be positive.
|
|
136
|
+
dt : float
|
|
137
|
+
Time step.
|
|
138
|
+
steps : int
|
|
139
|
+
Number of RK4 steps to advance.
|
|
140
|
+
length : float
|
|
141
|
+
Physical domain size.
|
|
142
|
+
|
|
143
|
+
Returns
|
|
144
|
+
-------
|
|
145
|
+
dict
|
|
146
|
+
``{"omega": ..., "psi": ..., "u": ..., "v": ...}`` at the final time.
|
|
147
|
+
|
|
148
|
+
Raises
|
|
149
|
+
------
|
|
150
|
+
InvalidParameterError
|
|
151
|
+
If `nu` is not positive.
|
|
152
|
+
|
|
153
|
+
See Also
|
|
154
|
+
--------
|
|
155
|
+
kelvin_helmholtz_ic : Builds the initial condition consumed here.
|
|
156
|
+
kelvin_helmholtz_growth_rate : The linear-theory prediction this simulation exceeds nonlinearly.
|
|
157
|
+
simulate_rayleigh_taylor : The companion buoyancy-driven instability, built on the same engine.
|
|
158
|
+
|
|
159
|
+
Examples
|
|
160
|
+
--------
|
|
161
|
+
>>> import numpy as np
|
|
162
|
+
>>> n, length = 96, 2 * np.pi
|
|
163
|
+
>>> omega0 = kelvin_helmholtz_ic(n, length, shear_width=0.1, perturbation_amplitude=0.05)
|
|
164
|
+
>>> result = simulate_kelvin_helmholtz(omega0, nu=0.001, dt=0.0025, steps=50, length=length)
|
|
165
|
+
>>> result["omega"].shape
|
|
166
|
+
(96, 96)
|
|
167
|
+
"""
|
|
168
|
+
if nu <= 0:
|
|
169
|
+
raise InvalidParameterError(f"nu (kinematic viscosity) must be positive, got {nu}")
|
|
170
|
+
return integrate_vorticity_streamfunction(omega0, nu, dt, steps, length)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def rayleigh_taylor_ic(
|
|
174
|
+
n: int, length: float, atwood_number: float, perturbation_amplitude: float = 0.02, interface_position: float | None = None
|
|
175
|
+
) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
|
|
176
|
+
"""Vorticity and buoyancy initial condition for the Rayleigh-Taylor instability.
|
|
177
|
+
|
|
178
|
+
A denser fluid layer sits atop a lighter one (heavy-on-top is exactly
|
|
179
|
+
the unstable arrangement), separated by an interface at mid-domain with
|
|
180
|
+
a small sinusoidal ripple, evolved through
|
|
181
|
+
:func:`simulate_rayleigh_taylor`. Vorticity starts at exactly zero: in
|
|
182
|
+
the Boussinesq system, all vorticity generation comes from the
|
|
183
|
+
baroclinic torque acting on the rippled density interface once gravity
|
|
184
|
+
is switched on (see :func:`physicskit.fluids.core.timestepping.buoyant_vorticity_rhs`).
|
|
185
|
+
|
|
186
|
+
Parameters
|
|
187
|
+
----------
|
|
188
|
+
n : int
|
|
189
|
+
Number of grid points along each axis.
|
|
190
|
+
length : float
|
|
191
|
+
Physical domain size.
|
|
192
|
+
atwood_number : float
|
|
193
|
+
The Atwood number :math:`A=(\\rho_{heavy}-\\rho_{light})/(\\rho_{heavy}+\\rho_{light})`,
|
|
194
|
+
:math:`0 < A < 1`, setting the buoyancy jump across the interface.
|
|
195
|
+
perturbation_amplitude : float, default 0.02
|
|
196
|
+
Amplitude of the seeding sinusoidal ripple on the interface.
|
|
197
|
+
interface_position : float, optional
|
|
198
|
+
Vertical position of the unperturbed interface; defaults to
|
|
199
|
+
``length / 2``.
|
|
200
|
+
|
|
201
|
+
Returns
|
|
202
|
+
-------
|
|
203
|
+
omega0 : ndarray of float, shape (n, n)
|
|
204
|
+
Initial vorticity field (identically zero).
|
|
205
|
+
buoyancy0 : ndarray of float, shape (n, n)
|
|
206
|
+
Initial buoyancy field: :math:`+A` below the interface (light fluid),
|
|
207
|
+
:math:`-A` above it (heavy fluid), smoothed over one grid cell so the
|
|
208
|
+
spectral derivatives it feeds resolve without ringing.
|
|
209
|
+
|
|
210
|
+
Raises
|
|
211
|
+
------
|
|
212
|
+
InvalidParameterError
|
|
213
|
+
If `atwood_number` is not in ``(0, 1)``.
|
|
214
|
+
|
|
215
|
+
Examples
|
|
216
|
+
--------
|
|
217
|
+
>>> omega0, buoyancy0 = rayleigh_taylor_ic(64, 2 * 3.141592653589793, atwood_number=0.3)
|
|
218
|
+
>>> bool(np.all(omega0 == 0.0))
|
|
219
|
+
True
|
|
220
|
+
>>> bool(buoyancy0.max() > 0 > buoyancy0.min())
|
|
221
|
+
True
|
|
222
|
+
"""
|
|
223
|
+
if not 0.0 < atwood_number < 1.0:
|
|
224
|
+
raise InvalidParameterError(f"atwood_number must be in (0, 1), got {atwood_number}")
|
|
225
|
+
x = np.linspace(0, length, n, endpoint=False)
|
|
226
|
+
X, Y = np.meshgrid(x, x, indexing="ij")
|
|
227
|
+
y0 = length / 2 if interface_position is None else interface_position
|
|
228
|
+
smoothing = length / n
|
|
229
|
+
ripple = perturbation_amplitude * np.cos(2 * np.pi * X / length)
|
|
230
|
+
buoyancy0 = -atwood_number * np.tanh((Y - y0 - ripple) / smoothing)
|
|
231
|
+
omega0 = np.zeros_like(X)
|
|
232
|
+
return omega0, buoyancy0
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def rayleigh_taylor_growth_rate(k: float, atwood_number: float, g: float = 1.0) -> float:
|
|
236
|
+
"""Linear growth rate of the Rayleigh-Taylor instability.
|
|
237
|
+
|
|
238
|
+
For two inviscid, semi-infinite fluid layers (heavy density
|
|
239
|
+
:math:`\\rho_2` on top of light density :math:`\\rho_1`, gravity `g`
|
|
240
|
+
pointing from the heavy into the light layer), a perturbation of
|
|
241
|
+
wavenumber `k` on the interface grows as :math:`e^{\\sigma t}` with
|
|
242
|
+
|
|
243
|
+
.. math::
|
|
244
|
+
|
|
245
|
+
\\sigma(k) = \\sqrt{A\\,g\\,k}, \\qquad
|
|
246
|
+
A = \\frac{\\rho_2-\\rho_1}{\\rho_2+\\rho_1}
|
|
247
|
+
|
|
248
|
+
(Rayleigh, 1883; Taylor, 1950), unstable at every wavenumber in the
|
|
249
|
+
idealized inviscid limit -- real fluids are cut off at short wavelength
|
|
250
|
+
by viscosity and surface tension, which :func:`simulate_rayleigh_taylor`
|
|
251
|
+
supplies dynamically via its buoyancy diffusivity.
|
|
252
|
+
|
|
253
|
+
Parameters
|
|
254
|
+
----------
|
|
255
|
+
k : float
|
|
256
|
+
Perturbation wavenumber (angular, :math:`2\\pi/\\lambda`).
|
|
257
|
+
atwood_number : float
|
|
258
|
+
The Atwood number :math:`A`, :math:`0 < A < 1`.
|
|
259
|
+
g : float, default 1.0
|
|
260
|
+
Gravitational acceleration, pointing from the heavy layer into the light one.
|
|
261
|
+
|
|
262
|
+
Returns
|
|
263
|
+
-------
|
|
264
|
+
float
|
|
265
|
+
Growth rate :math:`\\sigma`, in units of inverse time.
|
|
266
|
+
|
|
267
|
+
Raises
|
|
268
|
+
------
|
|
269
|
+
InvalidParameterError
|
|
270
|
+
If `atwood_number` is not in ``(0, 1)`` or `k` is negative.
|
|
271
|
+
|
|
272
|
+
Examples
|
|
273
|
+
--------
|
|
274
|
+
>>> round(rayleigh_taylor_growth_rate(k=2.0, atwood_number=0.5, g=1.0), 6)
|
|
275
|
+
1.0
|
|
276
|
+
"""
|
|
277
|
+
if not 0.0 < atwood_number < 1.0:
|
|
278
|
+
raise InvalidParameterError(f"atwood_number must be in (0, 1), got {atwood_number}")
|
|
279
|
+
if k < 0:
|
|
280
|
+
raise InvalidParameterError(f"k must be non-negative, got {k}")
|
|
281
|
+
return float(np.sqrt(atwood_number * g * k))
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def simulate_rayleigh_taylor(
|
|
285
|
+
omega0: NDArray[np.float64],
|
|
286
|
+
buoyancy0: NDArray[np.float64],
|
|
287
|
+
nu: float,
|
|
288
|
+
kappa: float,
|
|
289
|
+
g: float,
|
|
290
|
+
dt: float,
|
|
291
|
+
steps: int,
|
|
292
|
+
length: float,
|
|
293
|
+
) -> dict[str, NDArray[np.float64]]:
|
|
294
|
+
"""Time-step the Boussinesq Rayleigh-Taylor system with RK4.
|
|
295
|
+
|
|
296
|
+
Evolves the coupled vorticity-buoyancy equations of
|
|
297
|
+
:func:`physicskit.fluids.core.timestepping.buoyant_vorticity_rhs`
|
|
298
|
+
(scaled by the gravitational acceleration `g`) starting from
|
|
299
|
+
:func:`rayleigh_taylor_ic`, through the roll-up of the rippled interface
|
|
300
|
+
into the characteristic Rayleigh-Taylor mushroom plumes.
|
|
301
|
+
|
|
302
|
+
Parameters
|
|
303
|
+
----------
|
|
304
|
+
omega0 : ndarray of float, shape (n, n)
|
|
305
|
+
Initial vorticity field, as from :func:`rayleigh_taylor_ic`.
|
|
306
|
+
buoyancy0 : ndarray of float, shape (n, n)
|
|
307
|
+
Initial buoyancy field, as from :func:`rayleigh_taylor_ic`.
|
|
308
|
+
nu : float
|
|
309
|
+
Kinematic viscosity; must be positive.
|
|
310
|
+
kappa : float
|
|
311
|
+
Buoyancy (density) diffusivity; must be positive.
|
|
312
|
+
g : float
|
|
313
|
+
Gravitational acceleration; must be positive.
|
|
314
|
+
dt : float
|
|
315
|
+
Time step.
|
|
316
|
+
steps : int
|
|
317
|
+
Number of RK4 steps to advance.
|
|
318
|
+
length : float
|
|
319
|
+
Physical domain size.
|
|
320
|
+
|
|
321
|
+
Returns
|
|
322
|
+
-------
|
|
323
|
+
dict
|
|
324
|
+
``{"omega": ..., "buoyancy": ..., "psi": ..., "u": ..., "v": ...}`` at
|
|
325
|
+
the final time.
|
|
326
|
+
|
|
327
|
+
Raises
|
|
328
|
+
------
|
|
329
|
+
InvalidParameterError
|
|
330
|
+
If `nu`, `kappa`, or `g` is not positive.
|
|
331
|
+
|
|
332
|
+
See Also
|
|
333
|
+
--------
|
|
334
|
+
rayleigh_taylor_ic : Builds the initial condition consumed here.
|
|
335
|
+
rayleigh_taylor_growth_rate : The linear-theory prediction this simulation exceeds nonlinearly.
|
|
336
|
+
"""
|
|
337
|
+
if nu <= 0:
|
|
338
|
+
raise InvalidParameterError(f"nu (kinematic viscosity) must be positive, got {nu}")
|
|
339
|
+
if kappa <= 0:
|
|
340
|
+
raise InvalidParameterError(f"kappa (buoyancy diffusivity) must be positive, got {kappa}")
|
|
341
|
+
if g <= 0:
|
|
342
|
+
raise InvalidParameterError(f"g (gravitational acceleration) must be positive, got {g}")
|
|
343
|
+
return integrate_boussinesq(omega0, buoyancy0, nu, kappa, g, dt, steps, length)
|