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,221 @@
|
|
|
1
|
+
"""Incompressible 2D Navier-Stokes via the vorticity-streamfunction formulation.
|
|
2
|
+
|
|
3
|
+
Solves :math:`\\partial_t \\omega + (\\mathbf{u}\\cdot\\nabla)\\omega = \\nu \\nabla^2 \\omega`
|
|
4
|
+
on a doubly periodic domain, with the incompressible velocity field
|
|
5
|
+
recovered from the streamfunction :math:`\\psi` (:math:`\\nabla^2\\psi=-\\omega`,
|
|
6
|
+
:math:`\\mathbf{u}=(\\partial_y\\psi,\\,-\\partial_x\\psi)`) via an exact spectral
|
|
7
|
+
Poisson solve at every step -- the standard pseudo-spectral vorticity
|
|
8
|
+
formulation used throughout this package for 2D turbulence and shear-flow
|
|
9
|
+
instabilities. Working in vorticity rather than velocity-pressure form
|
|
10
|
+
eliminates the pressure entirely (it never appears once the curl of the
|
|
11
|
+
momentum equation is taken) at the cost of being specific to two dimensions,
|
|
12
|
+
where vorticity is a scalar.
|
|
13
|
+
|
|
14
|
+
This module is the general-purpose incompressible-flow workhorse of
|
|
15
|
+
:mod:`physicskit.fluids`: :mod:`physicskit.fluids.systems.instabilities` grows
|
|
16
|
+
its Kelvin-Helmholtz and Rayleigh-Taylor initial conditions with it (or with
|
|
17
|
+
its Boussinesq extension), and :mod:`physicskit.fluids.utils.spectral_analysis`
|
|
18
|
+
measures the turbulent energy spectrum of the velocity fields it produces.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import numpy as np
|
|
24
|
+
from numpy.typing import NDArray
|
|
25
|
+
|
|
26
|
+
from physicskit.fluids.core.grid import poisson_solve_streamfunction, spectral_grid, velocity_from_streamfunction
|
|
27
|
+
from physicskit.fluids.core.timestepping import integrate_vorticity_streamfunction, vorticity_rhs
|
|
28
|
+
from physicskit.fluids.exceptions import InvalidParameterError
|
|
29
|
+
|
|
30
|
+
__all__ = ["NavierStokes2D", "simulate_vorticity_streamfunction"]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class NavierStokes2D:
|
|
34
|
+
"""A doubly periodic 2D incompressible Navier-Stokes solver, in vorticity-streamfunction form.
|
|
35
|
+
|
|
36
|
+
Builds the pseudo-spectral grid once at construction and exposes
|
|
37
|
+
:meth:`rhs`, :meth:`step`, and :meth:`simulate` against it, so that
|
|
38
|
+
repeated calls (e.g. one per animation frame) do not re-derive the
|
|
39
|
+
wavenumber grid on every call the way the module-level
|
|
40
|
+
:func:`simulate_vorticity_streamfunction` convenience function must.
|
|
41
|
+
|
|
42
|
+
Parameters
|
|
43
|
+
----------
|
|
44
|
+
n : int, default 64
|
|
45
|
+
Number of grid points along each axis.
|
|
46
|
+
length : float, default ``2*pi``
|
|
47
|
+
Physical domain size (the domain is ``[0, length) x [0, length)``).
|
|
48
|
+
nu : float, default 0.01
|
|
49
|
+
Kinematic viscosity. Must be positive; a truly inviscid flow is
|
|
50
|
+
ill-posed on this grid (nothing controls the cascade of enstrophy to
|
|
51
|
+
the grid scale), so ``nu=0`` is rejected.
|
|
52
|
+
|
|
53
|
+
Attributes
|
|
54
|
+
----------
|
|
55
|
+
n : int
|
|
56
|
+
Grid resolution.
|
|
57
|
+
length : float
|
|
58
|
+
Domain size.
|
|
59
|
+
nu : float
|
|
60
|
+
Kinematic viscosity.
|
|
61
|
+
X, Y : ndarray of float, shape (n, n)
|
|
62
|
+
Real-space coordinate grids.
|
|
63
|
+
KX, KY, K2 : ndarray of float, shape (n, n)
|
|
64
|
+
Wavenumber grids.
|
|
65
|
+
|
|
66
|
+
Raises
|
|
67
|
+
------
|
|
68
|
+
InvalidParameterError
|
|
69
|
+
If `nu` is not positive (see :func:`physicskit.fluids.core.grid.spectral_grid`
|
|
70
|
+
for the conditions on `n` and `length`).
|
|
71
|
+
|
|
72
|
+
Examples
|
|
73
|
+
--------
|
|
74
|
+
>>> import numpy as np
|
|
75
|
+
>>> solver = NavierStokes2D(n=48, length=2 * np.pi, nu=0.05)
|
|
76
|
+
>>> omega0 = np.sin(solver.X) * np.sin(solver.Y)
|
|
77
|
+
>>> result = solver.simulate(omega0, dt=0.01, steps=20)
|
|
78
|
+
>>> result["omega"].shape
|
|
79
|
+
(48, 48)
|
|
80
|
+
>>> bool(np.max(np.abs(result["omega"])) < np.max(np.abs(omega0)))
|
|
81
|
+
True
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
def __init__(self, n: int = 64, length: float = 2.0 * np.pi, nu: float = 0.01):
|
|
85
|
+
if nu <= 0:
|
|
86
|
+
raise InvalidParameterError(f"nu (kinematic viscosity) must be positive, got {nu}")
|
|
87
|
+
self.n = int(n)
|
|
88
|
+
self.length = float(length)
|
|
89
|
+
self.nu = float(nu)
|
|
90
|
+
self.X, self.Y, self.KX, self.KY, self.K2 = spectral_grid(self.n, self.length)
|
|
91
|
+
|
|
92
|
+
def rhs(self, omega: NDArray[np.float64]) -> NDArray[np.float64]:
|
|
93
|
+
"""Evaluate the vorticity-transport right-hand side at `omega`.
|
|
94
|
+
|
|
95
|
+
Parameters
|
|
96
|
+
----------
|
|
97
|
+
omega : ndarray of float, shape (n, n)
|
|
98
|
+
Vorticity field.
|
|
99
|
+
|
|
100
|
+
Returns
|
|
101
|
+
-------
|
|
102
|
+
ndarray of float, shape (n, n)
|
|
103
|
+
:math:`-(\\mathbf{u}\\cdot\\nabla)\\omega + \\nu\\nabla^2\\omega`.
|
|
104
|
+
"""
|
|
105
|
+
return vorticity_rhs(omega, self.KX, self.KY, self.K2, self.nu)
|
|
106
|
+
|
|
107
|
+
def velocity(self, omega: NDArray[np.float64]) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
|
|
108
|
+
"""Recover the velocity field associated with a vorticity field.
|
|
109
|
+
|
|
110
|
+
Parameters
|
|
111
|
+
----------
|
|
112
|
+
omega : ndarray of float, shape (n, n)
|
|
113
|
+
Vorticity field.
|
|
114
|
+
|
|
115
|
+
Returns
|
|
116
|
+
-------
|
|
117
|
+
u, v : ndarray of float, shape (n, n)
|
|
118
|
+
Velocity components.
|
|
119
|
+
"""
|
|
120
|
+
psi = poisson_solve_streamfunction(omega, self.K2)
|
|
121
|
+
return velocity_from_streamfunction(psi, self.KX, self.KY)
|
|
122
|
+
|
|
123
|
+
def step(self, omega: NDArray[np.float64], dt: float) -> NDArray[np.float64]:
|
|
124
|
+
"""Advance the vorticity field by one RK4 step of size `dt`.
|
|
125
|
+
|
|
126
|
+
Parameters
|
|
127
|
+
----------
|
|
128
|
+
omega : ndarray of float, shape (n, n)
|
|
129
|
+
Current vorticity field.
|
|
130
|
+
dt : float
|
|
131
|
+
Step size.
|
|
132
|
+
|
|
133
|
+
Returns
|
|
134
|
+
-------
|
|
135
|
+
ndarray of float, shape (n, n)
|
|
136
|
+
Vorticity field advanced by one step.
|
|
137
|
+
"""
|
|
138
|
+
k1 = self.rhs(omega)
|
|
139
|
+
k2 = self.rhs(omega + dt / 2 * k1)
|
|
140
|
+
k3 = self.rhs(omega + dt / 2 * k2)
|
|
141
|
+
k4 = self.rhs(omega + dt * k3)
|
|
142
|
+
return omega + dt / 6 * (k1 + 2 * k2 + 2 * k3 + k4)
|
|
143
|
+
|
|
144
|
+
def simulate(self, omega0: NDArray[np.float64], dt: float, steps: int) -> dict[str, NDArray[np.float64]]:
|
|
145
|
+
"""Time-step an initial vorticity field forward by ``steps`` RK4 steps.
|
|
146
|
+
|
|
147
|
+
Parameters
|
|
148
|
+
----------
|
|
149
|
+
omega0 : ndarray of float, shape (n, n)
|
|
150
|
+
Initial vorticity field.
|
|
151
|
+
dt : float
|
|
152
|
+
Time step.
|
|
153
|
+
steps : int
|
|
154
|
+
Number of RK4 steps to advance.
|
|
155
|
+
|
|
156
|
+
Returns
|
|
157
|
+
-------
|
|
158
|
+
dict
|
|
159
|
+
``{"omega": final vorticity, "psi": final streamfunction, "u": ..., "v": ...}``.
|
|
160
|
+
"""
|
|
161
|
+
omega = np.array(omega0, dtype=float, copy=True)
|
|
162
|
+
for _ in range(steps):
|
|
163
|
+
omega = self.step(omega, dt)
|
|
164
|
+
psi = poisson_solve_streamfunction(omega, self.K2)
|
|
165
|
+
u, v = velocity_from_streamfunction(psi, self.KX, self.KY)
|
|
166
|
+
return {"omega": omega, "psi": psi, "u": u, "v": v}
|
|
167
|
+
|
|
168
|
+
def __repr__(self) -> str:
|
|
169
|
+
return f"NavierStokes2D(n={self.n}, length={self.length}, nu={self.nu})"
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def simulate_vorticity_streamfunction(omega0: NDArray[np.float64], nu: float, dt: float, steps: int, length: float) -> dict[str, NDArray[np.float64]]:
|
|
173
|
+
"""Time-step 2D incompressible vorticity-streamfunction Navier-Stokes with RK4.
|
|
174
|
+
|
|
175
|
+
A one-shot functional convenience wrapper around :class:`NavierStokes2D`,
|
|
176
|
+
for scripts that only need a single run rather than repeated stepping
|
|
177
|
+
against the same grid.
|
|
178
|
+
|
|
179
|
+
Parameters
|
|
180
|
+
----------
|
|
181
|
+
omega0 : ndarray of float, shape (n, n)
|
|
182
|
+
Initial vorticity field on a doubly periodic ``[0, length)^2`` domain.
|
|
183
|
+
nu : float
|
|
184
|
+
Kinematic viscosity; must be positive.
|
|
185
|
+
dt : float
|
|
186
|
+
Time step.
|
|
187
|
+
steps : int
|
|
188
|
+
Number of RK4 steps to advance.
|
|
189
|
+
length : float
|
|
190
|
+
Physical domain size.
|
|
191
|
+
|
|
192
|
+
Returns
|
|
193
|
+
-------
|
|
194
|
+
dict
|
|
195
|
+
``{"omega": final vorticity, "psi": final streamfunction, "u": ..., "v": ...}``.
|
|
196
|
+
|
|
197
|
+
Raises
|
|
198
|
+
------
|
|
199
|
+
InvalidParameterError
|
|
200
|
+
If `nu` is not positive.
|
|
201
|
+
|
|
202
|
+
See Also
|
|
203
|
+
--------
|
|
204
|
+
NavierStokes2D : Reusable object form, for repeated stepping on one grid.
|
|
205
|
+
physicskit.fluids.systems.instabilities.kelvin_helmholtz_ic :
|
|
206
|
+
Shear-layer initial condition that grows via this solver.
|
|
207
|
+
|
|
208
|
+
Examples
|
|
209
|
+
--------
|
|
210
|
+
>>> import numpy as np
|
|
211
|
+
>>> n = 48
|
|
212
|
+
>>> from physicskit.fluids.core.grid import spectral_grid
|
|
213
|
+
>>> X, Y, KX, KY, K2 = spectral_grid(n, 2 * np.pi)
|
|
214
|
+
>>> omega0 = np.sin(X) * np.sin(Y)
|
|
215
|
+
>>> result = simulate_vorticity_streamfunction(omega0, nu=0.05, dt=0.01, steps=20, length=2 * np.pi)
|
|
216
|
+
>>> result["omega"].shape
|
|
217
|
+
(48, 48)
|
|
218
|
+
"""
|
|
219
|
+
if nu <= 0:
|
|
220
|
+
raise InvalidParameterError(f"nu (kinematic viscosity) must be positive, got {nu}")
|
|
221
|
+
return integrate_vorticity_streamfunction(omega0, nu, dt, steps, length)
|
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
"""Inviscid, irrotational 2D potential flow: superposition of elementary solutions.
|
|
2
|
+
|
|
3
|
+
Where the flow has no vorticity (:math:`\\nabla\\times\\mathbf{u}=0`) the
|
|
4
|
+
velocity is the gradient of a scalar velocity potential,
|
|
5
|
+
:math:`\\mathbf{u}=\\nabla\\phi`, and incompressibility
|
|
6
|
+
(:math:`\\nabla\\cdot\\mathbf{u}=0`) then makes :math:`\\phi` harmonic:
|
|
7
|
+
:math:`\\nabla^2\\phi=0`. Laplace's equation is linear, so any sum of
|
|
8
|
+
solutions is itself a solution -- the entire content of this module is that
|
|
9
|
+
observation, applied to four elementary flows (a uniform stream, a
|
|
10
|
+
source/sink, a doublet, and a point vortex) whose superpositions reproduce
|
|
11
|
+
the classic textbook flow fields, most famously flow past a circular
|
|
12
|
+
cylinder with lift.
|
|
13
|
+
|
|
14
|
+
Every element is expressed through its complex potential
|
|
15
|
+
:math:`W(z) = \\phi + i\\psi`, :math:`z = x + iy`, since for these
|
|
16
|
+
particular elementary flows the complex-analytic form is both the most
|
|
17
|
+
compact way to write the solution and, via the Cauchy-Riemann equations, an
|
|
18
|
+
automatic guarantee that :math:`\\phi` and its harmonic conjugate
|
|
19
|
+
(streamfunction) :math:`\\psi` are consistent: :math:`u - iv = dW/dz`.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
import numpy as np
|
|
25
|
+
from numpy.typing import ArrayLike, NDArray
|
|
26
|
+
|
|
27
|
+
from physicskit.fluids.exceptions import InvalidParameterError
|
|
28
|
+
|
|
29
|
+
__all__ = [
|
|
30
|
+
"uniform_flow_potential",
|
|
31
|
+
"source_potential",
|
|
32
|
+
"doublet_potential",
|
|
33
|
+
"point_vortex_potential",
|
|
34
|
+
"PotentialFlow",
|
|
35
|
+
"flow_past_cylinder",
|
|
36
|
+
"pressure_coefficient",
|
|
37
|
+
"kutta_joukowski_lift",
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _complex_z(X: ArrayLike, Y: ArrayLike) -> NDArray[np.complex128]:
|
|
42
|
+
return np.asarray(X, dtype=np.float64) + 1j * np.asarray(Y, dtype=np.float64)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def uniform_flow_potential(X: ArrayLike, Y: ArrayLike, U_inf: float, alpha: float = 0.0) -> NDArray[np.complex128]:
|
|
46
|
+
"""Complex potential of a uniform stream.
|
|
47
|
+
|
|
48
|
+
Parameters
|
|
49
|
+
----------
|
|
50
|
+
X, Y : array_like of float
|
|
51
|
+
Field points.
|
|
52
|
+
U_inf : float
|
|
53
|
+
Free-stream speed.
|
|
54
|
+
alpha : float, default 0.0
|
|
55
|
+
Angle of attack (radians), measured from the ``+x`` axis.
|
|
56
|
+
|
|
57
|
+
Returns
|
|
58
|
+
-------
|
|
59
|
+
ndarray of complex
|
|
60
|
+
Complex potential :math:`W(z) = U_\\infty e^{-i\\alpha} z`.
|
|
61
|
+
"""
|
|
62
|
+
z = _complex_z(X, Y)
|
|
63
|
+
return U_inf * np.exp(-1j * alpha) * z
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def source_potential(X: ArrayLike, Y: ArrayLike, strength: float, x0: float = 0.0, y0: float = 0.0) -> NDArray[np.complex128]:
|
|
67
|
+
"""Complex potential of a point source (or, for negative `strength`, a sink).
|
|
68
|
+
|
|
69
|
+
Parameters
|
|
70
|
+
----------
|
|
71
|
+
X, Y : array_like of float
|
|
72
|
+
Field points.
|
|
73
|
+
strength : float
|
|
74
|
+
Source strength (volume flow rate per unit depth); negative for a sink.
|
|
75
|
+
x0, y0 : float, default 0.0
|
|
76
|
+
Source location.
|
|
77
|
+
|
|
78
|
+
Returns
|
|
79
|
+
-------
|
|
80
|
+
ndarray of complex
|
|
81
|
+
Complex potential :math:`W(z) = \\frac{m}{2\\pi}\\ln(z - z_0)`.
|
|
82
|
+
"""
|
|
83
|
+
z = _complex_z(X, Y) - (x0 + 1j * y0)
|
|
84
|
+
with np.errstate(divide="ignore"):
|
|
85
|
+
return (strength / (2.0 * np.pi)) * np.log(z)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def doublet_potential(X: ArrayLike, Y: ArrayLike, strength: float, x0: float = 0.0, y0: float = 0.0, alpha: float = 0.0) -> NDArray[np.complex128]:
|
|
89
|
+
"""Complex potential of a doublet (a source-sink pair in the zero-separation limit).
|
|
90
|
+
|
|
91
|
+
Parameters
|
|
92
|
+
----------
|
|
93
|
+
X, Y : array_like of float
|
|
94
|
+
Field points.
|
|
95
|
+
strength : float
|
|
96
|
+
Doublet strength :math:`\\kappa` (the limit of ``source_strength *
|
|
97
|
+
separation`` as the separation shrinks to zero).
|
|
98
|
+
x0, y0 : float, default 0.0
|
|
99
|
+
Doublet location.
|
|
100
|
+
alpha : float, default 0.0
|
|
101
|
+
Orientation angle (radians) from sink-to-source.
|
|
102
|
+
|
|
103
|
+
Returns
|
|
104
|
+
-------
|
|
105
|
+
ndarray of complex
|
|
106
|
+
Complex potential :math:`W(z) = \\frac{\\kappa e^{-i\\alpha}}{2\\pi (z-z_0)}`.
|
|
107
|
+
"""
|
|
108
|
+
z = _complex_z(X, Y) - (x0 + 1j * y0)
|
|
109
|
+
with np.errstate(divide="ignore"):
|
|
110
|
+
return (strength * np.exp(-1j * alpha)) / (2.0 * np.pi * z)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def point_vortex_potential(X: ArrayLike, Y: ArrayLike, circulation: float, x0: float = 0.0, y0: float = 0.0) -> NDArray[np.complex128]:
|
|
114
|
+
"""Complex potential of an isolated point vortex.
|
|
115
|
+
|
|
116
|
+
Parameters
|
|
117
|
+
----------
|
|
118
|
+
X, Y : array_like of float
|
|
119
|
+
Field points.
|
|
120
|
+
circulation : float
|
|
121
|
+
Circulation :math:`\\Gamma` (positive is counterclockwise).
|
|
122
|
+
x0, y0 : float, default 0.0
|
|
123
|
+
Vortex location.
|
|
124
|
+
|
|
125
|
+
Returns
|
|
126
|
+
-------
|
|
127
|
+
ndarray of complex
|
|
128
|
+
Complex potential :math:`W(z) = \\frac{-i\\Gamma}{2\\pi}\\ln(z - z_0)`.
|
|
129
|
+
"""
|
|
130
|
+
z = _complex_z(X, Y) - (x0 + 1j * y0)
|
|
131
|
+
with np.errstate(divide="ignore"):
|
|
132
|
+
return (-1j * circulation / (2.0 * np.pi)) * np.log(z)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class PotentialFlow:
|
|
136
|
+
"""A superposition of elementary potential-flow solutions.
|
|
137
|
+
|
|
138
|
+
Because Laplace's equation is linear, the complex potential of any
|
|
139
|
+
combination of uniform flow, sources/sinks, doublets, and point vortices
|
|
140
|
+
is simply the sum of their individual complex potentials -- exactly what
|
|
141
|
+
this class accumulates. Call :meth:`add_source`, :meth:`add_doublet`, or
|
|
142
|
+
:meth:`add_vortex` to build up a flow on top of the base uniform stream
|
|
143
|
+
``(U_inf, alpha)``, then
|
|
144
|
+
:meth:`velocity`, :meth:`streamfunction`, or
|
|
145
|
+
:meth:`pressure_coefficient` to evaluate it on a grid.
|
|
146
|
+
|
|
147
|
+
Parameters
|
|
148
|
+
----------
|
|
149
|
+
U_inf : float, default 1.0
|
|
150
|
+
Free-stream speed used both as the base uniform flow and as the
|
|
151
|
+
reference speed for :meth:`pressure_coefficient`.
|
|
152
|
+
alpha : float, default 0.0
|
|
153
|
+
Free-stream angle of attack (radians).
|
|
154
|
+
|
|
155
|
+
Attributes
|
|
156
|
+
----------
|
|
157
|
+
U_inf, alpha : float
|
|
158
|
+
As above.
|
|
159
|
+
|
|
160
|
+
Examples
|
|
161
|
+
--------
|
|
162
|
+
>>> flow = PotentialFlow(U_inf=1.0)
|
|
163
|
+
>>> flow.add_doublet(strength=2 * np.pi * 1.0**2) # uniform flow + doublet = cylinder
|
|
164
|
+
>>> X, Y = np.meshgrid(np.linspace(-3, 3, 5), np.linspace(-3, 3, 5))
|
|
165
|
+
>>> u, v = flow.velocity(X, Y)
|
|
166
|
+
>>> u.shape
|
|
167
|
+
(5, 5)
|
|
168
|
+
"""
|
|
169
|
+
|
|
170
|
+
def __init__(self, U_inf: float = 1.0, alpha: float = 0.0):
|
|
171
|
+
self.U_inf = float(U_inf)
|
|
172
|
+
self.alpha = float(alpha)
|
|
173
|
+
self._elements: list = []
|
|
174
|
+
|
|
175
|
+
def add_source(self, strength: float, x0: float = 0.0, y0: float = 0.0) -> None:
|
|
176
|
+
"""Add a point source (or sink, for negative `strength`).
|
|
177
|
+
|
|
178
|
+
Parameters
|
|
179
|
+
----------
|
|
180
|
+
strength : float
|
|
181
|
+
Source strength; negative for a sink.
|
|
182
|
+
x0, y0 : float, default 0.0
|
|
183
|
+
Source location.
|
|
184
|
+
"""
|
|
185
|
+
self._elements.append(("source", (strength, x0, y0)))
|
|
186
|
+
|
|
187
|
+
def add_doublet(self, strength: float, x0: float = 0.0, y0: float = 0.0, alpha: float = 0.0) -> None:
|
|
188
|
+
"""Add a doublet.
|
|
189
|
+
|
|
190
|
+
Parameters
|
|
191
|
+
----------
|
|
192
|
+
strength : float
|
|
193
|
+
Doublet strength.
|
|
194
|
+
x0, y0 : float, default 0.0
|
|
195
|
+
Doublet location.
|
|
196
|
+
alpha : float, default 0.0
|
|
197
|
+
Orientation angle (radians).
|
|
198
|
+
"""
|
|
199
|
+
self._elements.append(("doublet", (strength, x0, y0, alpha)))
|
|
200
|
+
|
|
201
|
+
def add_vortex(self, circulation: float, x0: float = 0.0, y0: float = 0.0) -> None:
|
|
202
|
+
"""Add a point vortex.
|
|
203
|
+
|
|
204
|
+
Parameters
|
|
205
|
+
----------
|
|
206
|
+
circulation : float
|
|
207
|
+
Circulation :math:`\\Gamma`; positive is counterclockwise.
|
|
208
|
+
x0, y0 : float, default 0.0
|
|
209
|
+
Vortex location.
|
|
210
|
+
"""
|
|
211
|
+
self._elements.append(("vortex", (circulation, x0, y0)))
|
|
212
|
+
|
|
213
|
+
def complex_potential(self, X: ArrayLike, Y: ArrayLike) -> NDArray[np.complex128]:
|
|
214
|
+
"""Evaluate the total complex potential :math:`W(z)=\\phi+i\\psi`.
|
|
215
|
+
|
|
216
|
+
Parameters
|
|
217
|
+
----------
|
|
218
|
+
X, Y : array_like of float
|
|
219
|
+
Field points.
|
|
220
|
+
|
|
221
|
+
Returns
|
|
222
|
+
-------
|
|
223
|
+
ndarray of complex
|
|
224
|
+
Complex potential at each point.
|
|
225
|
+
"""
|
|
226
|
+
X = np.asarray(X, dtype=np.float64)
|
|
227
|
+
W = uniform_flow_potential(X, Y, self.U_inf, self.alpha)
|
|
228
|
+
for kind, args in self._elements:
|
|
229
|
+
if kind == "source":
|
|
230
|
+
W = W + source_potential(X, Y, *args)
|
|
231
|
+
elif kind == "doublet":
|
|
232
|
+
W = W + doublet_potential(X, Y, *args)
|
|
233
|
+
elif kind == "vortex":
|
|
234
|
+
W = W + point_vortex_potential(X, Y, *args)
|
|
235
|
+
return W
|
|
236
|
+
|
|
237
|
+
def streamfunction(self, X: ArrayLike, Y: ArrayLike) -> NDArray[np.float64]:
|
|
238
|
+
"""Evaluate the streamfunction :math:`\\psi = \\mathrm{Im}(W)`.
|
|
239
|
+
|
|
240
|
+
Parameters
|
|
241
|
+
----------
|
|
242
|
+
X, Y : array_like of float
|
|
243
|
+
Field points.
|
|
244
|
+
|
|
245
|
+
Returns
|
|
246
|
+
-------
|
|
247
|
+
ndarray of float
|
|
248
|
+
Streamfunction values.
|
|
249
|
+
"""
|
|
250
|
+
return np.imag(self.complex_potential(X, Y))
|
|
251
|
+
|
|
252
|
+
def velocity(self, X: ArrayLike, Y: ArrayLike, delta: float = 1e-6) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
|
|
253
|
+
"""Evaluate the velocity field :math:`u - iv = dW/dz`.
|
|
254
|
+
|
|
255
|
+
Differentiates the complex potential with a central difference in
|
|
256
|
+
`X` (equivalent, by the Cauchy-Riemann equations, to differentiating
|
|
257
|
+
in either direction), which sidesteps deriving and summing a
|
|
258
|
+
separate closed-form velocity expression for every element type.
|
|
259
|
+
|
|
260
|
+
Parameters
|
|
261
|
+
----------
|
|
262
|
+
X, Y : array_like of float
|
|
263
|
+
Field points.
|
|
264
|
+
delta : float, default 1e-6
|
|
265
|
+
Finite-difference step used to differentiate the complex potential.
|
|
266
|
+
|
|
267
|
+
Returns
|
|
268
|
+
-------
|
|
269
|
+
u, v : ndarray of float
|
|
270
|
+
Velocity components.
|
|
271
|
+
"""
|
|
272
|
+
X = np.asarray(X, dtype=np.float64)
|
|
273
|
+
dW = (self.complex_potential(X + delta, Y) - self.complex_potential(X - delta, Y)) / (2.0 * delta)
|
|
274
|
+
return np.real(dW), -np.imag(dW)
|
|
275
|
+
|
|
276
|
+
def pressure_coefficient(self, X: ArrayLike, Y: ArrayLike) -> NDArray[np.float64]:
|
|
277
|
+
"""Evaluate the Bernoulli pressure coefficient :math:`C_p = 1 - (u^2+v^2)/U_\\infty^2`.
|
|
278
|
+
|
|
279
|
+
Parameters
|
|
280
|
+
----------
|
|
281
|
+
X, Y : array_like of float
|
|
282
|
+
Field points.
|
|
283
|
+
|
|
284
|
+
Returns
|
|
285
|
+
-------
|
|
286
|
+
ndarray of float
|
|
287
|
+
Pressure coefficient at each point.
|
|
288
|
+
"""
|
|
289
|
+
return pressure_coefficient(*self.velocity(X, Y), self.U_inf)
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def pressure_coefficient(u: ArrayLike, v: ArrayLike, U_inf: float) -> NDArray[np.float64]:
|
|
293
|
+
"""Bernoulli's pressure coefficient for steady, incompressible potential flow.
|
|
294
|
+
|
|
295
|
+
Along a streamline of a steady, incompressible, inviscid flow, Bernoulli's
|
|
296
|
+
equation :math:`p + \\tfrac{1}{2}\\rho|\\mathbf{u}|^2 = \\mathrm{const}`
|
|
297
|
+
gives the dimensionless pressure coefficient
|
|
298
|
+
|
|
299
|
+
.. math::
|
|
300
|
+
|
|
301
|
+
C_p = \\frac{p - p_\\infty}{\\tfrac{1}{2}\\rho U_\\infty^2}
|
|
302
|
+
= 1 - \\frac{u^2+v^2}{U_\\infty^2}.
|
|
303
|
+
|
|
304
|
+
Parameters
|
|
305
|
+
----------
|
|
306
|
+
u, v : array_like of float
|
|
307
|
+
Velocity components.
|
|
308
|
+
U_inf : float
|
|
309
|
+
Free-stream speed.
|
|
310
|
+
|
|
311
|
+
Returns
|
|
312
|
+
-------
|
|
313
|
+
ndarray of float
|
|
314
|
+
Pressure coefficient.
|
|
315
|
+
|
|
316
|
+
Raises
|
|
317
|
+
------
|
|
318
|
+
InvalidParameterError
|
|
319
|
+
If `U_inf` is not positive.
|
|
320
|
+
|
|
321
|
+
Examples
|
|
322
|
+
--------
|
|
323
|
+
>>> import numpy as np
|
|
324
|
+
>>> bool(np.isclose(pressure_coefficient(u=1.0, v=0.0, U_inf=1.0), 0.0))
|
|
325
|
+
True
|
|
326
|
+
"""
|
|
327
|
+
if U_inf <= 0:
|
|
328
|
+
raise InvalidParameterError(f"U_inf must be positive, got {U_inf}")
|
|
329
|
+
u = np.asarray(u, dtype=np.float64)
|
|
330
|
+
v = np.asarray(v, dtype=np.float64)
|
|
331
|
+
return 1.0 - (u**2 + v**2) / U_inf**2
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def flow_past_cylinder(U_inf: float, radius: float, circulation: float = 0.0) -> PotentialFlow:
|
|
335
|
+
"""Build the classic potential flow past a circular cylinder, with optional lift.
|
|
336
|
+
|
|
337
|
+
Superposing a uniform stream with a doublet of strength
|
|
338
|
+
:math:`\\kappa = 2\\pi U_\\infty R^2` places a circular streamline of
|
|
339
|
+
radius `radius` exactly at the origin -- since :math:`\\psi=0` on that
|
|
340
|
+
circle can be shown to be exactly the streamfunction of this
|
|
341
|
+
combination, the circle itself can be interpreted as a solid boundary
|
|
342
|
+
(potential flow does not enforce a no-slip condition, only no
|
|
343
|
+
penetration). Adding a point vortex at the same location keeps the
|
|
344
|
+
circular streamline intact (a vortex centered on the circle induces
|
|
345
|
+
purely tangential velocity on it) while breaking front-back symmetry,
|
|
346
|
+
which is exactly what a real cylinder's boundary layer does when it
|
|
347
|
+
separates asymmetrically -- e.g. from a spinning cylinder (the Magnus
|
|
348
|
+
effect).
|
|
349
|
+
|
|
350
|
+
Parameters
|
|
351
|
+
----------
|
|
352
|
+
U_inf : float
|
|
353
|
+
Free-stream speed.
|
|
354
|
+
radius : float
|
|
355
|
+
Cylinder radius.
|
|
356
|
+
circulation : float, default 0.0
|
|
357
|
+
Circulation :math:`\\Gamma` added at the cylinder center; `0.0` gives
|
|
358
|
+
the symmetric, lift-free flow.
|
|
359
|
+
|
|
360
|
+
Returns
|
|
361
|
+
-------
|
|
362
|
+
PotentialFlow
|
|
363
|
+
The assembled flow: uniform stream + doublet (+ vortex if `circulation`
|
|
364
|
+
is nonzero).
|
|
365
|
+
|
|
366
|
+
Raises
|
|
367
|
+
------
|
|
368
|
+
InvalidParameterError
|
|
369
|
+
If `U_inf` or `radius` is not positive.
|
|
370
|
+
|
|
371
|
+
See Also
|
|
372
|
+
--------
|
|
373
|
+
kutta_joukowski_lift : The lift force generated by `circulation`.
|
|
374
|
+
|
|
375
|
+
Examples
|
|
376
|
+
--------
|
|
377
|
+
>>> flow = flow_past_cylinder(U_inf=1.0, radius=1.0, circulation=4 * np.pi)
|
|
378
|
+
>>> theta = np.linspace(0, 2 * np.pi, 9)
|
|
379
|
+
>>> X, Y = 1.0 * np.cos(theta), 1.0 * np.sin(theta)
|
|
380
|
+
>>> psi = flow.streamfunction(X, Y)
|
|
381
|
+
>>> bool(np.max(np.abs(psi - psi[0])) < 1e-8) # the cylinder surface is one streamline
|
|
382
|
+
True
|
|
383
|
+
"""
|
|
384
|
+
if U_inf <= 0:
|
|
385
|
+
raise InvalidParameterError(f"U_inf must be positive, got {U_inf}")
|
|
386
|
+
if radius <= 0:
|
|
387
|
+
raise InvalidParameterError(f"radius must be positive, got {radius}")
|
|
388
|
+
flow = PotentialFlow(U_inf=U_inf)
|
|
389
|
+
flow.add_doublet(strength=2.0 * np.pi * U_inf * radius**2)
|
|
390
|
+
if circulation != 0.0:
|
|
391
|
+
flow.add_vortex(circulation=circulation)
|
|
392
|
+
return flow
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
def kutta_joukowski_lift(rho: float, U_inf: float, circulation: float) -> float:
|
|
396
|
+
"""The Kutta-Joukowski lift theorem: lift per unit span from bound circulation.
|
|
397
|
+
|
|
398
|
+
.. math::
|
|
399
|
+
|
|
400
|
+
L' = \\rho\\,U_\\infty\\,\\Gamma
|
|
401
|
+
|
|
402
|
+
Any 2D body generating a net circulation :math:`\\Gamma` around itself in
|
|
403
|
+
a stream of speed :math:`U_\\infty` experiences a lift force per unit
|
|
404
|
+
span of exactly this magnitude, directed perpendicular to the free
|
|
405
|
+
stream -- true regardless of the body's shape, a remarkable consequence
|
|
406
|
+
of potential theory (Kutta 1902, Zhukovsky 1906) that underlies all of
|
|
407
|
+
classical airfoil theory. Real airfoils select `circulation` via the
|
|
408
|
+
Kutta condition (smooth flow off a sharp trailing edge); the cylinder
|
|
409
|
+
with an added point vortex built by :func:`flow_past_cylinder` sets it
|
|
410
|
+
directly.
|
|
411
|
+
|
|
412
|
+
Parameters
|
|
413
|
+
----------
|
|
414
|
+
rho : float
|
|
415
|
+
Fluid density.
|
|
416
|
+
U_inf : float
|
|
417
|
+
Free-stream speed.
|
|
418
|
+
circulation : float
|
|
419
|
+
Circulation :math:`\\Gamma` (positive counterclockwise) bound to the body.
|
|
420
|
+
|
|
421
|
+
Returns
|
|
422
|
+
-------
|
|
423
|
+
float
|
|
424
|
+
Lift force per unit span, :math:`L'`.
|
|
425
|
+
|
|
426
|
+
Raises
|
|
427
|
+
------
|
|
428
|
+
InvalidParameterError
|
|
429
|
+
If `rho` or `U_inf` is not positive.
|
|
430
|
+
|
|
431
|
+
Examples
|
|
432
|
+
--------
|
|
433
|
+
>>> round(kutta_joukowski_lift(rho=1.2, U_inf=10.0, circulation=5.0), 1)
|
|
434
|
+
60.0
|
|
435
|
+
"""
|
|
436
|
+
if rho <= 0:
|
|
437
|
+
raise InvalidParameterError(f"rho must be positive, got {rho}")
|
|
438
|
+
if U_inf <= 0:
|
|
439
|
+
raise InvalidParameterError(f"U_inf must be positive, got {U_inf}")
|
|
440
|
+
return rho * U_inf * circulation
|