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,868 @@
|
|
|
1
|
+
"""Plotting helpers for particle orbits, MHD equilibria, wave maps, and kinetic phase space.
|
|
2
|
+
|
|
3
|
+
Every Matplotlib function returns its figure and axes rather than calling
|
|
4
|
+
``show()``; :func:`plot_phase_space_interactive` returns a Plotly figure
|
|
5
|
+
for pan/zoom exploration of a PIC phase-space snapshot. The ``animate_*``
|
|
6
|
+
functions each return a :class:`matplotlib.animation.FuncAnimation` built
|
|
7
|
+
from a sequence of snapshots taken while repeatedly re-invoking the
|
|
8
|
+
corresponding time-domain simulation function -- save with, e.g.,
|
|
9
|
+
``anim.save(path, writer=PillowWriter(fps=10))``.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import matplotlib.pyplot as plt
|
|
15
|
+
import numpy as np
|
|
16
|
+
import plotly.graph_objects as go
|
|
17
|
+
from matplotlib.animation import FuncAnimation
|
|
18
|
+
|
|
19
|
+
from physicskit.plasma.acceleration import simulate_wakefield_acceleration, wakefield_e_field
|
|
20
|
+
from physicskit.plasma.instabilities import simulate_reconnection, simulate_weibel_filamentation
|
|
21
|
+
from physicskit.plasma.kinetic import deposit_number_density, pic_simulate
|
|
22
|
+
from physicskit.plasma.turbulence import simulate_hasegawa_mima
|
|
23
|
+
from physicskit.plasma.waves import ion_acoustic_soliton_evolve, simulate_alfven_wave
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
"plot_particle_orbit_3d",
|
|
27
|
+
"plot_drift_trajectory",
|
|
28
|
+
"plot_flux_surfaces",
|
|
29
|
+
"plot_q_profile",
|
|
30
|
+
"plot_phase_space",
|
|
31
|
+
"plot_field_energy_history",
|
|
32
|
+
"plot_cma_diagram",
|
|
33
|
+
"plot_phase_space_interactive",
|
|
34
|
+
"animate_reconnection",
|
|
35
|
+
"animate_weibel_filamentation",
|
|
36
|
+
"animate_two_stream_phase_space",
|
|
37
|
+
"animate_langmuir_wave",
|
|
38
|
+
"animate_ion_acoustic_soliton",
|
|
39
|
+
"animate_drift_wave_turbulence",
|
|
40
|
+
"animate_alfven_wave",
|
|
41
|
+
"animate_wakefield_acceleration",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def plot_particle_orbit_3d(pos_hist: np.ndarray, ax=None):
|
|
46
|
+
"""Plot a charged particle's 3D trajectory (e.g. Boris-pusher gyro-orbit).
|
|
47
|
+
|
|
48
|
+
Parameters
|
|
49
|
+
----------
|
|
50
|
+
pos_hist : ndarray, shape (steps, 3)
|
|
51
|
+
Position history, as from :func:`physicskit.plasma.single_particle.boris_integrate`.
|
|
52
|
+
ax : matplotlib.axes.Axes3D, optional
|
|
53
|
+
3D axes to draw into; a new figure is created if omitted.
|
|
54
|
+
|
|
55
|
+
Returns
|
|
56
|
+
-------
|
|
57
|
+
fig : matplotlib.figure.Figure
|
|
58
|
+
ax : matplotlib.axes.Axes3D
|
|
59
|
+
|
|
60
|
+
Examples
|
|
61
|
+
--------
|
|
62
|
+
>>> import numpy as np
|
|
63
|
+
>>> from physicskit.plasma.single_particle import boris_integrate, QE, MP
|
|
64
|
+
>>> pos_hist, vel_hist = boris_integrate(
|
|
65
|
+
... np.zeros(3), np.array([1e5, 0.0, 0.0]), QE, MP, np.zeros(3), np.array([0.0, 0.0, 1.0]), 1e-10, steps=200
|
|
66
|
+
... )
|
|
67
|
+
>>> fig, ax = plot_particle_orbit_3d(pos_hist)
|
|
68
|
+
>>> isinstance(fig, plt.Figure)
|
|
69
|
+
True
|
|
70
|
+
"""
|
|
71
|
+
if ax is None:
|
|
72
|
+
fig = plt.figure()
|
|
73
|
+
ax = fig.add_subplot(projection="3d")
|
|
74
|
+
else:
|
|
75
|
+
fig = ax.figure
|
|
76
|
+
ax.plot(pos_hist[:, 0], pos_hist[:, 1], pos_hist[:, 2])
|
|
77
|
+
ax.set_xlabel("x (m)")
|
|
78
|
+
ax.set_ylabel("y (m)")
|
|
79
|
+
ax.set_zlabel("z (m)")
|
|
80
|
+
return fig, ax
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def plot_drift_trajectory(pos_hist: np.ndarray, ax=None):
|
|
84
|
+
"""Plot the guiding-center drift path as seen from above (the x-y plane).
|
|
85
|
+
|
|
86
|
+
Parameters
|
|
87
|
+
----------
|
|
88
|
+
pos_hist : ndarray, shape (steps, 3) or (steps, 2)
|
|
89
|
+
Position history; only the first two components are used.
|
|
90
|
+
ax : matplotlib.axes.Axes, optional
|
|
91
|
+
Axes to draw into; a new figure is created if omitted.
|
|
92
|
+
|
|
93
|
+
Returns
|
|
94
|
+
-------
|
|
95
|
+
fig : matplotlib.figure.Figure
|
|
96
|
+
ax : matplotlib.axes.Axes
|
|
97
|
+
|
|
98
|
+
See Also
|
|
99
|
+
--------
|
|
100
|
+
plot_particle_orbit_3d : The full 3D gyro-orbit this drift path averages over.
|
|
101
|
+
|
|
102
|
+
Examples
|
|
103
|
+
--------
|
|
104
|
+
>>> import numpy as np
|
|
105
|
+
>>> from physicskit.plasma.single_particle import boris_integrate, QE, MP
|
|
106
|
+
>>> E = np.array([0.0, 1e3, 0.0])
|
|
107
|
+
>>> B = np.array([0.0, 0.0, 1.0])
|
|
108
|
+
>>> pos_hist, vel_hist = boris_integrate(np.zeros(3), np.zeros(3), QE, MP, E, B, 1e-10, steps=500)
|
|
109
|
+
>>> fig, ax = plot_drift_trajectory(pos_hist)
|
|
110
|
+
>>> isinstance(fig, plt.Figure)
|
|
111
|
+
True
|
|
112
|
+
"""
|
|
113
|
+
if ax is None:
|
|
114
|
+
fig, ax = plt.subplots()
|
|
115
|
+
else:
|
|
116
|
+
fig = ax.figure
|
|
117
|
+
ax.plot(pos_hist[:, 0], pos_hist[:, 1])
|
|
118
|
+
ax.set_xlabel("x (m)")
|
|
119
|
+
ax.set_ylabel("y (m)")
|
|
120
|
+
ax.set_aspect("equal")
|
|
121
|
+
return fig, ax
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def plot_flux_surfaces(R: np.ndarray, Z: np.ndarray, psi: np.ndarray, ax=None, levels: int = 20):
|
|
125
|
+
"""Contour-plot poloidal flux surfaces :math:`\\psi(R, Z)` of a toroidal equilibrium.
|
|
126
|
+
|
|
127
|
+
Parameters
|
|
128
|
+
----------
|
|
129
|
+
R, Z : ndarray, shape (nr,), (nz,)
|
|
130
|
+
Cylindrical coordinate grids.
|
|
131
|
+
psi : ndarray, shape (nr, nz)
|
|
132
|
+
Poloidal flux, as from :func:`physicskit.plasma.mhd.solve_grad_shafranov`.
|
|
133
|
+
ax : matplotlib.axes.Axes, optional
|
|
134
|
+
Axes to draw into; a new figure is created if omitted.
|
|
135
|
+
levels : int, default=20
|
|
136
|
+
Number of contour levels.
|
|
137
|
+
|
|
138
|
+
Returns
|
|
139
|
+
-------
|
|
140
|
+
fig : matplotlib.figure.Figure
|
|
141
|
+
ax : matplotlib.axes.Axes
|
|
142
|
+
|
|
143
|
+
Examples
|
|
144
|
+
--------
|
|
145
|
+
>>> import numpy as np
|
|
146
|
+
>>> from physicskit.plasma.mhd import solve_grad_shafranov
|
|
147
|
+
>>> R = np.linspace(0.5, 1.5, 41)
|
|
148
|
+
>>> Z = np.linspace(-0.5, 0.5, 41)
|
|
149
|
+
>>> psi = solve_grad_shafranov(R, Z, c1=1.0, c2=-2.0)
|
|
150
|
+
>>> fig, ax = plot_flux_surfaces(R, Z, psi)
|
|
151
|
+
>>> isinstance(fig, plt.Figure)
|
|
152
|
+
True
|
|
153
|
+
"""
|
|
154
|
+
if ax is None:
|
|
155
|
+
fig, ax = plt.subplots()
|
|
156
|
+
else:
|
|
157
|
+
fig = ax.figure
|
|
158
|
+
RR, ZZ = np.meshgrid(R, Z, indexing="ij")
|
|
159
|
+
cs = ax.contour(RR, ZZ, psi, levels=levels)
|
|
160
|
+
fig.colorbar(cs, ax=ax, label=r"$\psi$")
|
|
161
|
+
ax.set_xlabel("R (m)")
|
|
162
|
+
ax.set_ylabel("Z (m)")
|
|
163
|
+
ax.set_aspect("equal")
|
|
164
|
+
return fig, ax
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def plot_q_profile(r: np.ndarray, q: np.ndarray, ax=None):
|
|
168
|
+
"""Plot the tokamak safety factor :math:`q(r)` against minor radius.
|
|
169
|
+
|
|
170
|
+
Parameters
|
|
171
|
+
----------
|
|
172
|
+
r : ndarray
|
|
173
|
+
Minor-radius coordinate.
|
|
174
|
+
q : ndarray
|
|
175
|
+
Safety factor, e.g. from
|
|
176
|
+
:func:`physicskit.plasma.mhd.safety_factor_large_aspect_ratio`
|
|
177
|
+
evaluated at each ``r``.
|
|
178
|
+
ax : matplotlib.axes.Axes, optional
|
|
179
|
+
Axes to draw into; a new figure is created if omitted.
|
|
180
|
+
|
|
181
|
+
Returns
|
|
182
|
+
-------
|
|
183
|
+
fig : matplotlib.figure.Figure
|
|
184
|
+
ax : matplotlib.axes.Axes
|
|
185
|
+
|
|
186
|
+
Examples
|
|
187
|
+
--------
|
|
188
|
+
>>> import numpy as np
|
|
189
|
+
>>> from physicskit.plasma.mhd import safety_factor_large_aspect_ratio
|
|
190
|
+
>>> r = np.linspace(0.05, 0.5, 20)
|
|
191
|
+
>>> q = np.array([safety_factor_large_aspect_ratio(ri, R0=1.0, Bt=2.0, Bp=0.2) for ri in r])
|
|
192
|
+
>>> fig, ax = plot_q_profile(r, q)
|
|
193
|
+
>>> isinstance(fig, plt.Figure)
|
|
194
|
+
True
|
|
195
|
+
"""
|
|
196
|
+
if ax is None:
|
|
197
|
+
fig, ax = plt.subplots()
|
|
198
|
+
else:
|
|
199
|
+
fig = ax.figure
|
|
200
|
+
ax.plot(r, q)
|
|
201
|
+
ax.axhline(1.0, color="gray", linestyle="--", linewidth=0.8, label="q=1 (sawtooth)")
|
|
202
|
+
ax.set_xlabel("minor radius r (m)")
|
|
203
|
+
ax.set_ylabel("safety factor q")
|
|
204
|
+
ax.legend()
|
|
205
|
+
return fig, ax
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def plot_phase_space(x: np.ndarray, v: np.ndarray, ax=None, bins: int = 64):
|
|
209
|
+
"""Heatmap the particle-in-cell phase-space density :math:`f(x, v)` from a particle snapshot.
|
|
210
|
+
|
|
211
|
+
Parameters
|
|
212
|
+
----------
|
|
213
|
+
x, v : ndarray, shape (n_particles,)
|
|
214
|
+
Particle positions and velocities, e.g. from
|
|
215
|
+
:func:`physicskit.plasma.kinetic.pic_simulate`.
|
|
216
|
+
ax : matplotlib.axes.Axes, optional
|
|
217
|
+
Axes to draw into; a new figure is created if omitted.
|
|
218
|
+
bins : int, default=64
|
|
219
|
+
Number of histogram bins along each axis.
|
|
220
|
+
|
|
221
|
+
Returns
|
|
222
|
+
-------
|
|
223
|
+
fig : matplotlib.figure.Figure
|
|
224
|
+
ax : matplotlib.axes.Axes
|
|
225
|
+
|
|
226
|
+
See Also
|
|
227
|
+
--------
|
|
228
|
+
plot_phase_space_interactive : An interactive Plotly scatter of the same data.
|
|
229
|
+
|
|
230
|
+
Examples
|
|
231
|
+
--------
|
|
232
|
+
>>> import numpy as np
|
|
233
|
+
>>> from physicskit.plasma.kinetic import landau_damping_ic
|
|
234
|
+
>>> x, v = landau_damping_ic(2000, L=4 * np.pi, k_mode=0.5, alpha=0.1, v_th=1.0, seed=0)
|
|
235
|
+
>>> fig, ax = plot_phase_space(x, v)
|
|
236
|
+
>>> isinstance(fig, plt.Figure)
|
|
237
|
+
True
|
|
238
|
+
"""
|
|
239
|
+
if ax is None:
|
|
240
|
+
fig, ax = plt.subplots()
|
|
241
|
+
else:
|
|
242
|
+
fig = ax.figure
|
|
243
|
+
h = ax.hist2d(x, v, bins=bins, cmap="inferno")
|
|
244
|
+
fig.colorbar(h[3], ax=ax, label="particle count")
|
|
245
|
+
ax.set_xlabel("x")
|
|
246
|
+
ax.set_ylabel("v")
|
|
247
|
+
return fig, ax
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def plot_field_energy_history(t: np.ndarray, field_energy: np.ndarray, ax=None):
|
|
251
|
+
"""Semilog plot of electrostatic field energy vs. time, showing Landau damping (or two-stream growth).
|
|
252
|
+
|
|
253
|
+
Parameters
|
|
254
|
+
----------
|
|
255
|
+
t : ndarray
|
|
256
|
+
Time array, as from :func:`physicskit.plasma.kinetic.pic_simulate`.
|
|
257
|
+
field_energy : ndarray
|
|
258
|
+
Field energy at each time, same shape as ``t``.
|
|
259
|
+
ax : matplotlib.axes.Axes, optional
|
|
260
|
+
Axes to draw into; a new figure is created if omitted.
|
|
261
|
+
|
|
262
|
+
Returns
|
|
263
|
+
-------
|
|
264
|
+
fig : matplotlib.figure.Figure
|
|
265
|
+
ax : matplotlib.axes.Axes
|
|
266
|
+
|
|
267
|
+
Examples
|
|
268
|
+
--------
|
|
269
|
+
>>> import numpy as np
|
|
270
|
+
>>> from physicskit.plasma.kinetic import landau_damping_ic, pic_simulate
|
|
271
|
+
>>> x0, v0 = landau_damping_ic(2000, L=4 * np.pi, k_mode=0.5, alpha=0.05, v_th=1.0, seed=0)
|
|
272
|
+
>>> result = pic_simulate(x0, v0, L=4 * np.pi, ng=32, dt=0.1, steps=30)
|
|
273
|
+
>>> fig, ax = plot_field_energy_history(result["t"], result["field_energy"])
|
|
274
|
+
>>> isinstance(fig, plt.Figure)
|
|
275
|
+
True
|
|
276
|
+
"""
|
|
277
|
+
if ax is None:
|
|
278
|
+
fig, ax = plt.subplots()
|
|
279
|
+
else:
|
|
280
|
+
fig = ax.figure
|
|
281
|
+
ax.semilogy(t, field_energy)
|
|
282
|
+
ax.set_xlabel("t (1/$\\omega_{pe}$)")
|
|
283
|
+
ax.set_ylabel("field energy")
|
|
284
|
+
return fig, ax
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def plot_cma_diagram(X: np.ndarray, Y: np.ndarray, ax=None):
|
|
288
|
+
"""Scatter a set of plasma states on log-log Clemmow-Mullaly-Allis (CMA) diagram axes.
|
|
289
|
+
|
|
290
|
+
Parameters
|
|
291
|
+
----------
|
|
292
|
+
X, Y : ndarray
|
|
293
|
+
CMA coordinates, as from :func:`physicskit.plasma.waves.cma_coordinates`.
|
|
294
|
+
ax : matplotlib.axes.Axes, optional
|
|
295
|
+
Axes to draw into; a new figure is created if omitted.
|
|
296
|
+
|
|
297
|
+
Returns
|
|
298
|
+
-------
|
|
299
|
+
fig : matplotlib.figure.Figure
|
|
300
|
+
ax : matplotlib.axes.Axes
|
|
301
|
+
|
|
302
|
+
Examples
|
|
303
|
+
--------
|
|
304
|
+
>>> import numpy as np
|
|
305
|
+
>>> from physicskit.plasma.waves import cma_coordinates
|
|
306
|
+
>>> omega = np.linspace(0.1, 5.0, 30)
|
|
307
|
+
>>> X, Y = cma_coordinates(omega, wpe=2.0, wce=3.0)
|
|
308
|
+
>>> fig, ax = plot_cma_diagram(X, Y)
|
|
309
|
+
>>> isinstance(fig, plt.Figure)
|
|
310
|
+
True
|
|
311
|
+
"""
|
|
312
|
+
if ax is None:
|
|
313
|
+
fig, ax = plt.subplots()
|
|
314
|
+
else:
|
|
315
|
+
fig = ax.figure
|
|
316
|
+
ax.loglog(X, Y, "o-")
|
|
317
|
+
ax.axvline(1.0, color="gray", linestyle="--", linewidth=0.8)
|
|
318
|
+
ax.axhline(1.0, color="gray", linestyle="--", linewidth=0.8)
|
|
319
|
+
ax.set_xlabel(r"$X = \omega_{pe}^2/\omega^2$")
|
|
320
|
+
ax.set_ylabel(r"$Y = \omega_{ce}/\omega$")
|
|
321
|
+
return fig, ax
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def plot_phase_space_interactive(x: np.ndarray, v: np.ndarray):
|
|
325
|
+
"""Interactive Plotly scatter of a particle-in-cell phase-space snapshot.
|
|
326
|
+
|
|
327
|
+
Unlike :func:`plot_phase_space`, points remain individually
|
|
328
|
+
identifiable under pan and zoom -- useful for inspecting fine
|
|
329
|
+
structure like phase-space vortices in a developed two-stream
|
|
330
|
+
instability.
|
|
331
|
+
|
|
332
|
+
Parameters
|
|
333
|
+
----------
|
|
334
|
+
x, v : ndarray, shape (n_particles,)
|
|
335
|
+
Particle positions and velocities.
|
|
336
|
+
|
|
337
|
+
Returns
|
|
338
|
+
-------
|
|
339
|
+
plotly.graph_objects.Figure
|
|
340
|
+
|
|
341
|
+
See Also
|
|
342
|
+
--------
|
|
343
|
+
plot_phase_space : The static Matplotlib heatmap equivalent.
|
|
344
|
+
|
|
345
|
+
Examples
|
|
346
|
+
--------
|
|
347
|
+
>>> import numpy as np
|
|
348
|
+
>>> from physicskit.plasma.kinetic import two_stream_ic
|
|
349
|
+
>>> x, v = two_stream_ic(500, L=10.0, v_drift=3.0, v_th=0.5, seed=0)
|
|
350
|
+
>>> fig = plot_phase_space_interactive(x, v)
|
|
351
|
+
>>> isinstance(fig, go.Figure)
|
|
352
|
+
True
|
|
353
|
+
"""
|
|
354
|
+
fig = go.Figure(data=go.Scattergl(x=x, y=v, mode="markers", marker={"size": 3, "opacity": 0.5}))
|
|
355
|
+
fig.update_layout(xaxis_title="x", yaxis_title="v")
|
|
356
|
+
return fig
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
def _animate_1d_snapshots(x: np.ndarray, snapshots: list, xlabel: str, ylabel: str, title: str | None = None, interval: int = 60) -> FuncAnimation:
|
|
360
|
+
"""Shared line-plot animator: redraws a single ``y(x)`` curve over a sequence of snapshots."""
|
|
361
|
+
fig, ax = plt.subplots()
|
|
362
|
+
(line,) = ax.plot(x, snapshots[0])
|
|
363
|
+
ax.set_xlabel(xlabel)
|
|
364
|
+
ax.set_ylabel(ylabel)
|
|
365
|
+
if title:
|
|
366
|
+
ax.set_title(title)
|
|
367
|
+
stacked = np.stack(snapshots)
|
|
368
|
+
lo, hi = stacked.min(), stacked.max()
|
|
369
|
+
pad = 0.05 * (hi - lo) if hi > lo else 1.0
|
|
370
|
+
ax.set_ylim(lo - pad, hi + pad)
|
|
371
|
+
|
|
372
|
+
def update(frame):
|
|
373
|
+
line.set_ydata(snapshots[frame])
|
|
374
|
+
return (line,)
|
|
375
|
+
|
|
376
|
+
return FuncAnimation(fig, update, frames=len(snapshots), interval=interval, blit=True)
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
def _animate_2d_snapshots(
|
|
380
|
+
snapshots: list, extent: tuple, xlabel: str = "x", ylabel: str = "y", cmap: str = "RdBu_r", title: str | None = None, interval: int = 60
|
|
381
|
+
) -> FuncAnimation:
|
|
382
|
+
"""Shared imshow animator: redraws a single 2D scalar field over a sequence of snapshots."""
|
|
383
|
+
fig, ax = plt.subplots()
|
|
384
|
+
vmax = max(np.max(np.abs(s)) for s in snapshots) or 1.0
|
|
385
|
+
im = ax.imshow(snapshots[0].T, origin="lower", extent=extent, cmap=cmap, vmin=-vmax, vmax=vmax, aspect="auto")
|
|
386
|
+
fig.colorbar(im, ax=ax)
|
|
387
|
+
ax.set_xlabel(xlabel)
|
|
388
|
+
ax.set_ylabel(ylabel)
|
|
389
|
+
if title:
|
|
390
|
+
ax.set_title(title)
|
|
391
|
+
|
|
392
|
+
def update(frame):
|
|
393
|
+
im.set_data(snapshots[frame].T)
|
|
394
|
+
return (im,)
|
|
395
|
+
|
|
396
|
+
return FuncAnimation(fig, update, frames=len(snapshots), interval=interval, blit=True)
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
def animate_reconnection(
|
|
400
|
+
psi0: np.ndarray, eta: float, v0: float, dt: float, steps_per_frame: int, n_frames: int, Lx: float, Ly: float, interval: int = 60
|
|
401
|
+
) -> FuncAnimation:
|
|
402
|
+
"""Animate the flux function :math:`\\psi(x,y,t)` reconnecting at the X-point of a resistive current sheet.
|
|
403
|
+
|
|
404
|
+
Repeatedly calls :func:`physicskit.plasma.instabilities.simulate_reconnection`
|
|
405
|
+
for ``steps_per_frame`` steps at a time, using each call's final state as
|
|
406
|
+
the next call's initial condition, and shows the accumulated flux
|
|
407
|
+
snapshots as an imshow animation -- the antiparallel field lines above
|
|
408
|
+
and below the sheet visibly merge into a single reconnected topology at
|
|
409
|
+
the X-point, and the squeezed-out reconnected flux forms the outflow
|
|
410
|
+
"jets" along the sheet.
|
|
411
|
+
|
|
412
|
+
Each snapshot has its :math:`x`-mean subtracted, :math:`\\psi(x,y,t) -
|
|
413
|
+
\\langle\\psi\\rangle_x(y,t)`, before display. The unperturbed Harris
|
|
414
|
+
profile :math:`-L\\ln\\cosh(y/L)` grows without bound away from the
|
|
415
|
+
sheet, so on a fixed color scale it dwarfs the localized island/X-point
|
|
416
|
+
structure that is actually reconnecting -- left in, the animation reads
|
|
417
|
+
as visually static even though the underlying field is evolving.
|
|
418
|
+
Removing the (x-independent) background isolates exactly the
|
|
419
|
+
x-varying perturbation that breaks and reconnects, which is what makes
|
|
420
|
+
the merging visible frame to frame.
|
|
421
|
+
|
|
422
|
+
Parameters
|
|
423
|
+
----------
|
|
424
|
+
psi0 : ndarray, shape (nx, ny)
|
|
425
|
+
Initial flux function, e.g. from
|
|
426
|
+
:func:`physicskit.plasma.instabilities.reconnection_harris_ic`.
|
|
427
|
+
eta : float
|
|
428
|
+
Resistivity.
|
|
429
|
+
v0 : float
|
|
430
|
+
Inflow speed.
|
|
431
|
+
dt : float
|
|
432
|
+
Time step per :func:`~physicskit.plasma.instabilities.simulate_reconnection` sub-step.
|
|
433
|
+
steps_per_frame : int
|
|
434
|
+
Number of sub-steps advanced between animation frames.
|
|
435
|
+
n_frames : int
|
|
436
|
+
Number of animation frames.
|
|
437
|
+
Lx, Ly : float
|
|
438
|
+
Domain size.
|
|
439
|
+
interval : int, default=60
|
|
440
|
+
Delay between frames in milliseconds.
|
|
441
|
+
|
|
442
|
+
Returns
|
|
443
|
+
-------
|
|
444
|
+
matplotlib.animation.FuncAnimation
|
|
445
|
+
|
|
446
|
+
Examples
|
|
447
|
+
--------
|
|
448
|
+
>>> from physicskit.plasma.instabilities import reconnection_harris_ic
|
|
449
|
+
>>> psi0 = reconnection_harris_ic(32, 32, Lx=20.0, Ly=20.0, sheet_width=1.0, perturbation_amplitude=0.2)
|
|
450
|
+
>>> anim = animate_reconnection(psi0, eta=0.02, v0=0.05, dt=0.02, steps_per_frame=5, n_frames=4, Lx=20.0, Ly=20.0)
|
|
451
|
+
>>> len(list(anim.new_frame_seq()))
|
|
452
|
+
4
|
|
453
|
+
"""
|
|
454
|
+
psi = psi0
|
|
455
|
+
snapshots = [psi0]
|
|
456
|
+
for _ in range(n_frames - 1):
|
|
457
|
+
result = simulate_reconnection(psi, eta, v0, dt, steps_per_frame, Lx, Ly)
|
|
458
|
+
psi = result["psi"]
|
|
459
|
+
snapshots.append(psi)
|
|
460
|
+
deviations = [s - s.mean(axis=0, keepdims=True) for s in snapshots]
|
|
461
|
+
return _animate_2d_snapshots(
|
|
462
|
+
deviations,
|
|
463
|
+
extent=(0.0, Lx, -Ly / 2.0, Ly / 2.0),
|
|
464
|
+
xlabel="x",
|
|
465
|
+
ylabel="y",
|
|
466
|
+
title=r"Reconnection: perturbed flux $\psi-\langle\psi\rangle_x$",
|
|
467
|
+
interval=interval,
|
|
468
|
+
)
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
def animate_weibel_filamentation(
|
|
472
|
+
x: np.ndarray, t: np.ndarray, wpe: float, temperature_anisotropy: float, n_modes: int = 12, seed: int = 0, interval: int = 60
|
|
473
|
+
) -> FuncAnimation:
|
|
474
|
+
"""Animate the transverse current filaments growing under the reduced quasi-linear Weibel model.
|
|
475
|
+
|
|
476
|
+
Parameters
|
|
477
|
+
----------
|
|
478
|
+
x : ndarray, shape (nx,)
|
|
479
|
+
Spatial grid.
|
|
480
|
+
t : ndarray, shape (nt,)
|
|
481
|
+
Animation frame times.
|
|
482
|
+
wpe : float
|
|
483
|
+
Electron plasma frequency.
|
|
484
|
+
temperature_anisotropy : float
|
|
485
|
+
The ratio :math:`T_\\perp/T_\\parallel`; see
|
|
486
|
+
:func:`physicskit.plasma.instabilities.weibel_growth_rate`.
|
|
487
|
+
n_modes : int, default=12
|
|
488
|
+
Number of seeded Fourier modes.
|
|
489
|
+
seed : int, default=0
|
|
490
|
+
Random seed.
|
|
491
|
+
interval : int, default=60
|
|
492
|
+
Delay between frames in milliseconds.
|
|
493
|
+
|
|
494
|
+
Returns
|
|
495
|
+
-------
|
|
496
|
+
matplotlib.animation.FuncAnimation
|
|
497
|
+
|
|
498
|
+
See Also
|
|
499
|
+
--------
|
|
500
|
+
physicskit.plasma.instabilities.simulate_weibel_filamentation : Supplies the current-density snapshots animated here.
|
|
501
|
+
|
|
502
|
+
Examples
|
|
503
|
+
--------
|
|
504
|
+
>>> import numpy as np
|
|
505
|
+
>>> x = np.linspace(0, 20.0, 128, endpoint=False)
|
|
506
|
+
>>> t = np.linspace(0, 5.0, 6)
|
|
507
|
+
>>> anim = animate_weibel_filamentation(x, t, wpe=1.0, temperature_anisotropy=4.0, n_modes=6)
|
|
508
|
+
>>> len(list(anim.new_frame_seq()))
|
|
509
|
+
6
|
|
510
|
+
"""
|
|
511
|
+
J = simulate_weibel_filamentation(x, t, wpe, temperature_anisotropy, n_modes=n_modes, seed=seed)
|
|
512
|
+
snapshots = [J[i] for i in range(J.shape[0])]
|
|
513
|
+
return _animate_1d_snapshots(
|
|
514
|
+
x, snapshots, xlabel="x", ylabel=r"current density $J_y$ (normalized)", title="Weibel/filamentation instability", interval=interval
|
|
515
|
+
)
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
def animate_two_stream_phase_space(
|
|
519
|
+
x0: np.ndarray, v0: np.ndarray, L: float, ng: int, dt: float, steps_per_frame: int, n_frames: int, interval: int = 60
|
|
520
|
+
) -> FuncAnimation:
|
|
521
|
+
"""Animate two-stream-instability phase space :math:`(x, v)` developing its characteristic vortex.
|
|
522
|
+
|
|
523
|
+
Repeatedly calls :func:`physicskit.plasma.kinetic.pic_simulate` for
|
|
524
|
+
``steps_per_frame`` leapfrog steps at a time, redrawing a scatter of
|
|
525
|
+
every particle's position and velocity each frame -- the two initially
|
|
526
|
+
separate beams of :func:`physicskit.plasma.kinetic.two_stream_ic`
|
|
527
|
+
visibly wrap around each other into a single phase-space "hole" as the
|
|
528
|
+
instability saturates.
|
|
529
|
+
|
|
530
|
+
Parameters
|
|
531
|
+
----------
|
|
532
|
+
x0, v0 : ndarray, shape (n_particles,)
|
|
533
|
+
Initial particle positions and velocities, e.g. from
|
|
534
|
+
:func:`physicskit.plasma.kinetic.two_stream_ic`.
|
|
535
|
+
L : float
|
|
536
|
+
Domain length.
|
|
537
|
+
ng : int
|
|
538
|
+
Number of grid points for the PIC field solve.
|
|
539
|
+
dt : float
|
|
540
|
+
Time step per leapfrog sub-step.
|
|
541
|
+
steps_per_frame : int
|
|
542
|
+
Number of PIC steps advanced between animation frames.
|
|
543
|
+
n_frames : int
|
|
544
|
+
Number of animation frames.
|
|
545
|
+
interval : int, default=60
|
|
546
|
+
Delay between frames in milliseconds.
|
|
547
|
+
|
|
548
|
+
Returns
|
|
549
|
+
-------
|
|
550
|
+
matplotlib.animation.FuncAnimation
|
|
551
|
+
|
|
552
|
+
Examples
|
|
553
|
+
--------
|
|
554
|
+
>>> from physicskit.plasma.kinetic import two_stream_ic
|
|
555
|
+
>>> x0, v0 = two_stream_ic(2000, L=10.0, v_drift=3.0, v_th=0.5, seed=0)
|
|
556
|
+
>>> anim = animate_two_stream_phase_space(x0, v0, L=10.0, ng=32, dt=0.05, steps_per_frame=5, n_frames=4)
|
|
557
|
+
>>> len(list(anim.new_frame_seq()))
|
|
558
|
+
4
|
|
559
|
+
"""
|
|
560
|
+
x, v = np.asarray(x0, dtype=float), np.asarray(v0, dtype=float)
|
|
561
|
+
x_snaps, v_snaps = [x.copy()], [v.copy()]
|
|
562
|
+
for _ in range(n_frames - 1):
|
|
563
|
+
result = pic_simulate(x, v, L, ng, dt, steps_per_frame)
|
|
564
|
+
x, v = result["x"], result["v"]
|
|
565
|
+
x_snaps.append(x.copy())
|
|
566
|
+
v_snaps.append(v.copy())
|
|
567
|
+
|
|
568
|
+
fig, ax = plt.subplots()
|
|
569
|
+
scatter = ax.scatter(x_snaps[0], v_snaps[0], s=2, alpha=0.5)
|
|
570
|
+
ax.set_xlim(0, L)
|
|
571
|
+
all_v = np.concatenate(v_snaps)
|
|
572
|
+
vpad = 0.1 * (all_v.max() - all_v.min())
|
|
573
|
+
ax.set_ylim(all_v.min() - vpad, all_v.max() + vpad)
|
|
574
|
+
ax.set_xlabel("x")
|
|
575
|
+
ax.set_ylabel("v")
|
|
576
|
+
ax.set_title("Two-stream instability phase space")
|
|
577
|
+
|
|
578
|
+
def update(frame):
|
|
579
|
+
scatter.set_offsets(np.column_stack([x_snaps[frame], v_snaps[frame]]))
|
|
580
|
+
return (scatter,)
|
|
581
|
+
|
|
582
|
+
return FuncAnimation(fig, update, frames=len(x_snaps), interval=interval, blit=True)
|
|
583
|
+
|
|
584
|
+
|
|
585
|
+
def animate_langmuir_wave(
|
|
586
|
+
x0: np.ndarray, v0: np.ndarray, L: float, ng: int, dt: float, steps_per_frame: int, n_frames: int, interval: int = 60
|
|
587
|
+
) -> FuncAnimation:
|
|
588
|
+
"""Animate a Langmuir wave's electron density oscillating in place at (approximately) the plasma frequency.
|
|
589
|
+
|
|
590
|
+
Repeatedly calls :func:`physicskit.plasma.kinetic.pic_simulate` for
|
|
591
|
+
``steps_per_frame`` steps at a time and, each frame, deposits the
|
|
592
|
+
current particle positions onto the grid with
|
|
593
|
+
:func:`physicskit.plasma.kinetic.deposit_number_density` -- reusing the
|
|
594
|
+
exact charge-assignment kernel the PIC field solve itself uses, so the
|
|
595
|
+
density shown is precisely what
|
|
596
|
+
:func:`physicskit.plasma.kinetic.pic_step` sees when it solves Poisson's
|
|
597
|
+
equation for the field driving the next sub-step.
|
|
598
|
+
|
|
599
|
+
Parameters
|
|
600
|
+
----------
|
|
601
|
+
x0, v0 : ndarray, shape (n_particles,)
|
|
602
|
+
Initial particle positions and velocities, e.g. from
|
|
603
|
+
:func:`physicskit.plasma.kinetic.langmuir_wave_ic`.
|
|
604
|
+
L : float
|
|
605
|
+
Domain length.
|
|
606
|
+
ng : int
|
|
607
|
+
Number of grid points.
|
|
608
|
+
dt : float
|
|
609
|
+
Time step per leapfrog sub-step.
|
|
610
|
+
steps_per_frame : int
|
|
611
|
+
Number of PIC steps advanced between animation frames.
|
|
612
|
+
n_frames : int
|
|
613
|
+
Number of animation frames.
|
|
614
|
+
interval : int, default=60
|
|
615
|
+
Delay between frames in milliseconds.
|
|
616
|
+
|
|
617
|
+
Returns
|
|
618
|
+
-------
|
|
619
|
+
matplotlib.animation.FuncAnimation
|
|
620
|
+
|
|
621
|
+
Examples
|
|
622
|
+
--------
|
|
623
|
+
>>> import numpy as np
|
|
624
|
+
>>> from physicskit.plasma.kinetic import langmuir_wave_ic
|
|
625
|
+
>>> k = 2 * np.pi / 4.0
|
|
626
|
+
>>> x0, v0 = langmuir_wave_ic(4000, L=4.0, k_mode=k, alpha=0.05, v_th=0.05, seed=0)
|
|
627
|
+
>>> anim = animate_langmuir_wave(x0, v0, L=4.0, ng=32, dt=0.05, steps_per_frame=4, n_frames=4)
|
|
628
|
+
>>> len(list(anim.new_frame_seq()))
|
|
629
|
+
4
|
|
630
|
+
"""
|
|
631
|
+
x, v = np.asarray(x0, dtype=float), np.asarray(v0, dtype=float)
|
|
632
|
+
x_grid = np.linspace(0.0, L, ng, endpoint=False)
|
|
633
|
+
snapshots = [deposit_number_density(x, L, ng)]
|
|
634
|
+
for _ in range(n_frames - 1):
|
|
635
|
+
result = pic_simulate(x, v, L, ng, dt, steps_per_frame)
|
|
636
|
+
x, v = result["x"], result["v"]
|
|
637
|
+
snapshots.append(deposit_number_density(x, L, ng))
|
|
638
|
+
return _animate_1d_snapshots(x_grid, snapshots, xlabel="x", ylabel="electron density", title="Langmuir wave", interval=interval)
|
|
639
|
+
|
|
640
|
+
|
|
641
|
+
def animate_ion_acoustic_soliton(u0: np.ndarray, x: np.ndarray, dt: float, steps_per_frame: int, n_frames: int, interval: int = 60) -> FuncAnimation:
|
|
642
|
+
"""Animate an ion-acoustic soliton propagating without change of shape.
|
|
643
|
+
|
|
644
|
+
Repeatedly calls :func:`physicskit.plasma.waves.ion_acoustic_soliton_evolve`
|
|
645
|
+
for ``steps_per_frame`` steps at a time and animates the resulting
|
|
646
|
+
density-pulse snapshots.
|
|
647
|
+
|
|
648
|
+
Parameters
|
|
649
|
+
----------
|
|
650
|
+
u0 : ndarray
|
|
651
|
+
Initial soliton profile, e.g. from
|
|
652
|
+
:func:`physicskit.plasma.waves.ion_acoustic_soliton_profile`.
|
|
653
|
+
x : ndarray
|
|
654
|
+
Uniformly spaced periodic spatial grid.
|
|
655
|
+
dt : float
|
|
656
|
+
Time step per sub-step.
|
|
657
|
+
steps_per_frame : int
|
|
658
|
+
Number of steps advanced between animation frames.
|
|
659
|
+
n_frames : int
|
|
660
|
+
Number of animation frames.
|
|
661
|
+
interval : int, default=60
|
|
662
|
+
Delay between frames in milliseconds.
|
|
663
|
+
|
|
664
|
+
Returns
|
|
665
|
+
-------
|
|
666
|
+
matplotlib.animation.FuncAnimation
|
|
667
|
+
|
|
668
|
+
Examples
|
|
669
|
+
--------
|
|
670
|
+
>>> import numpy as np
|
|
671
|
+
>>> from physicskit.plasma.waves import ion_acoustic_soliton_profile
|
|
672
|
+
>>> x = np.linspace(-30, 30, 256, endpoint=False)
|
|
673
|
+
>>> u0 = ion_acoustic_soliton_profile(x, speed=4.0, x0=-15.0)
|
|
674
|
+
>>> anim = animate_ion_acoustic_soliton(u0, x, dt=0.0005, steps_per_frame=200, n_frames=4)
|
|
675
|
+
>>> len(list(anim.new_frame_seq()))
|
|
676
|
+
4
|
|
677
|
+
"""
|
|
678
|
+
u = np.asarray(u0, dtype=float)
|
|
679
|
+
snapshots = [u.copy()]
|
|
680
|
+
for _ in range(n_frames - 1):
|
|
681
|
+
u = ion_acoustic_soliton_evolve(u, x, dt, steps_per_frame)
|
|
682
|
+
snapshots.append(u.copy())
|
|
683
|
+
return _animate_1d_snapshots(x, snapshots, xlabel="x", ylabel="density perturbation", title="Ion-acoustic soliton", interval=interval)
|
|
684
|
+
|
|
685
|
+
|
|
686
|
+
def animate_drift_wave_turbulence(
|
|
687
|
+
phi0: np.ndarray, dt: float, steps_per_frame: int, n_frames: int, length: float, nu: float = 0.03, interval: int = 60
|
|
688
|
+
) -> FuncAnimation:
|
|
689
|
+
"""Animate a Hasegawa-Mima potential field developing turbulent structure from small-amplitude noise.
|
|
690
|
+
|
|
691
|
+
Repeatedly calls
|
|
692
|
+
:func:`physicskit.plasma.turbulence.simulate_hasegawa_mima` for
|
|
693
|
+
``steps_per_frame`` steps at a time and animates the resulting
|
|
694
|
+
potential-field snapshots.
|
|
695
|
+
|
|
696
|
+
Parameters
|
|
697
|
+
----------
|
|
698
|
+
phi0 : ndarray, shape (n, n)
|
|
699
|
+
Initial potential field, e.g. from
|
|
700
|
+
:func:`physicskit.plasma.turbulence.drift_wave_noise_ic`.
|
|
701
|
+
dt : float
|
|
702
|
+
Time step per sub-step.
|
|
703
|
+
steps_per_frame : int
|
|
704
|
+
Number of RK4 steps advanced between animation frames.
|
|
705
|
+
n_frames : int
|
|
706
|
+
Number of animation frames.
|
|
707
|
+
length : float
|
|
708
|
+
Physical domain size.
|
|
709
|
+
nu : float, default=0.03
|
|
710
|
+
Dissipation coefficient (see :func:`physicskit.plasma.turbulence.simulate_hasegawa_mima`).
|
|
711
|
+
interval : int, default=60
|
|
712
|
+
Delay between frames in milliseconds.
|
|
713
|
+
|
|
714
|
+
Returns
|
|
715
|
+
-------
|
|
716
|
+
matplotlib.animation.FuncAnimation
|
|
717
|
+
|
|
718
|
+
Examples
|
|
719
|
+
--------
|
|
720
|
+
>>> import numpy as np
|
|
721
|
+
>>> from physicskit.plasma.turbulence import drift_wave_noise_ic
|
|
722
|
+
>>> phi0 = drift_wave_noise_ic(48, 2 * np.pi, amplitude=0.05, seed=0)
|
|
723
|
+
>>> anim = animate_drift_wave_turbulence(phi0, dt=0.02, steps_per_frame=10, n_frames=4, length=2 * np.pi)
|
|
724
|
+
>>> len(list(anim.new_frame_seq()))
|
|
725
|
+
4
|
|
726
|
+
"""
|
|
727
|
+
phi = np.asarray(phi0, dtype=float)
|
|
728
|
+
snapshots = [phi.copy()]
|
|
729
|
+
for _ in range(n_frames - 1):
|
|
730
|
+
result = simulate_hasegawa_mima(phi, dt, steps_per_frame, length, nu=nu)
|
|
731
|
+
phi = result["phi"]
|
|
732
|
+
snapshots.append(phi.copy())
|
|
733
|
+
return _animate_2d_snapshots(snapshots, extent=(0.0, length, 0.0, length), title="Drift-wave turbulence (Hasegawa-Mima)", interval=interval)
|
|
734
|
+
|
|
735
|
+
|
|
736
|
+
def animate_alfven_wave(
|
|
737
|
+
By0: np.ndarray,
|
|
738
|
+
vy0: np.ndarray,
|
|
739
|
+
x: np.ndarray,
|
|
740
|
+
dt: float,
|
|
741
|
+
steps_per_frame: int,
|
|
742
|
+
n_frames: int,
|
|
743
|
+
B0: float,
|
|
744
|
+
rho0: float,
|
|
745
|
+
mu0: float = 1.0,
|
|
746
|
+
interval: int = 60,
|
|
747
|
+
) -> FuncAnimation:
|
|
748
|
+
"""Animate a transverse Alfven-wave pulse propagating (and splitting) along the background field.
|
|
749
|
+
|
|
750
|
+
Repeatedly calls :func:`physicskit.plasma.waves.simulate_alfven_wave`
|
|
751
|
+
for ``steps_per_frame`` steps at a time and animates the resulting
|
|
752
|
+
transverse-field snapshots.
|
|
753
|
+
|
|
754
|
+
Parameters
|
|
755
|
+
----------
|
|
756
|
+
By0, vy0 : ndarray
|
|
757
|
+
Initial transverse field and velocity perturbations, e.g. from
|
|
758
|
+
:func:`physicskit.plasma.waves.alfven_wave_pulse_ic`.
|
|
759
|
+
x : ndarray
|
|
760
|
+
Uniformly spaced periodic spatial grid.
|
|
761
|
+
dt : float
|
|
762
|
+
Time step per sub-step.
|
|
763
|
+
steps_per_frame : int
|
|
764
|
+
Number of RK4 steps advanced between animation frames.
|
|
765
|
+
n_frames : int
|
|
766
|
+
Number of animation frames.
|
|
767
|
+
B0 : float
|
|
768
|
+
Background field magnitude.
|
|
769
|
+
rho0 : float
|
|
770
|
+
Background mass density.
|
|
771
|
+
mu0 : float, default=1.0
|
|
772
|
+
Vacuum permeability (normalized units by default; see
|
|
773
|
+
:func:`physicskit.plasma.waves.simulate_alfven_wave`).
|
|
774
|
+
interval : int, default=60
|
|
775
|
+
Delay between frames in milliseconds.
|
|
776
|
+
|
|
777
|
+
Returns
|
|
778
|
+
-------
|
|
779
|
+
matplotlib.animation.FuncAnimation
|
|
780
|
+
|
|
781
|
+
Examples
|
|
782
|
+
--------
|
|
783
|
+
>>> import numpy as np
|
|
784
|
+
>>> from physicskit.plasma.waves import alfven_wave_pulse_ic
|
|
785
|
+
>>> x = np.linspace(-20, 20, 256, endpoint=False)
|
|
786
|
+
>>> By0, vy0 = alfven_wave_pulse_ic(x, x0=0.0, width=1.0, amplitude=0.1)
|
|
787
|
+
>>> anim = animate_alfven_wave(By0, vy0, x, dt=0.002, steps_per_frame=200, n_frames=4, B0=1.0, rho0=1.0)
|
|
788
|
+
>>> len(list(anim.new_frame_seq()))
|
|
789
|
+
4
|
|
790
|
+
"""
|
|
791
|
+
By, vy = np.asarray(By0, dtype=float), np.asarray(vy0, dtype=float)
|
|
792
|
+
snapshots = [By.copy()]
|
|
793
|
+
for _ in range(n_frames - 1):
|
|
794
|
+
result = simulate_alfven_wave(By, vy, x, dt, steps_per_frame, B0, rho0, mu0=mu0)
|
|
795
|
+
By, vy = result["By"], result["vy"]
|
|
796
|
+
snapshots.append(By.copy())
|
|
797
|
+
return _animate_1d_snapshots(x, snapshots, xlabel="x", ylabel=r"$B_y$ perturbation", title="Alfven wave", interval=interval)
|
|
798
|
+
|
|
799
|
+
|
|
800
|
+
def animate_wakefield_acceleration(
|
|
801
|
+
x0: float, v0: float, q: float, m: float, E0: float, k: float, v_phase: float, dt: float, steps: int, frame_stride: int = 20, interval: int = 60
|
|
802
|
+
) -> FuncAnimation:
|
|
803
|
+
"""Animate a test charge surfing a prescribed traveling wakefield, with an energy-gain trace inset.
|
|
804
|
+
|
|
805
|
+
Runs :func:`physicskit.plasma.acceleration.simulate_wakefield_acceleration`
|
|
806
|
+
once for the full trajectory, then animates a snapshot of the wakefield
|
|
807
|
+
:math:`E_z(x, t)` with the particle's position marked on it, alongside
|
|
808
|
+
a running plot of its kinetic energy -- showing both the spatial
|
|
809
|
+
"surfing" picture and the resulting energy gain simultaneously.
|
|
810
|
+
|
|
811
|
+
Parameters
|
|
812
|
+
----------
|
|
813
|
+
x0, v0 : float
|
|
814
|
+
Initial particle position and velocity.
|
|
815
|
+
q, m : float
|
|
816
|
+
Particle charge and mass.
|
|
817
|
+
E0, k, v_phase : float
|
|
818
|
+
Wakefield amplitude, wavenumber, and phase velocity, as in
|
|
819
|
+
:func:`physicskit.plasma.acceleration.wakefield_e_field`.
|
|
820
|
+
dt : float
|
|
821
|
+
Time step.
|
|
822
|
+
steps : int
|
|
823
|
+
Total number of Boris-pusher steps to integrate.
|
|
824
|
+
frame_stride : int, default=20
|
|
825
|
+
Number of integration steps between animation frames.
|
|
826
|
+
interval : int, default=60
|
|
827
|
+
Delay between frames in milliseconds.
|
|
828
|
+
|
|
829
|
+
Returns
|
|
830
|
+
-------
|
|
831
|
+
matplotlib.animation.FuncAnimation
|
|
832
|
+
|
|
833
|
+
Examples
|
|
834
|
+
--------
|
|
835
|
+
>>> anim = animate_wakefield_acceleration(
|
|
836
|
+
... x0=0.0, v0=0.9, q=1.0, m=1.0, E0=0.05, k=1.0, v_phase=1.0, dt=0.01, steps=400, frame_stride=40
|
|
837
|
+
... )
|
|
838
|
+
>>> len(list(anim.new_frame_seq()))
|
|
839
|
+
11
|
|
840
|
+
"""
|
|
841
|
+
result = simulate_wakefield_acceleration(x0, v0, q, m, E0, k, v_phase, dt, steps)
|
|
842
|
+
t_hist, x_hist, kinetic_energy = result["t"], result["x"], result["kinetic_energy"]
|
|
843
|
+
frame_idx = np.arange(0, len(t_hist), frame_stride)
|
|
844
|
+
|
|
845
|
+
x_span = max(abs(x_hist.max() - x_hist.min()), 1.0)
|
|
846
|
+
x_field = np.linspace(x_hist.min() - 0.2 * x_span, x_hist.max() + 0.2 * x_span, 400)
|
|
847
|
+
|
|
848
|
+
fig, (ax_field, ax_energy) = plt.subplots(2, 1, figsize=(6, 6))
|
|
849
|
+
(field_line,) = ax_field.plot(x_field, wakefield_e_field(x_field, t_hist[0], E0, k, v_phase))
|
|
850
|
+
(particle_marker,) = ax_field.plot([x_hist[0]], [wakefield_e_field(x_hist[0], t_hist[0], E0, k, v_phase)], "o", color="red", markersize=8)
|
|
851
|
+
ax_field.set_xlabel("x")
|
|
852
|
+
ax_field.set_ylabel(r"$E_z$")
|
|
853
|
+
ax_field.set_title("Wakefield acceleration")
|
|
854
|
+
|
|
855
|
+
(energy_line,) = ax_energy.plot([], [])
|
|
856
|
+
ax_energy.set_xlim(t_hist[0], t_hist[-1])
|
|
857
|
+
ax_energy.set_ylim(0.0, 1.1 * kinetic_energy.max())
|
|
858
|
+
ax_energy.set_xlabel("t")
|
|
859
|
+
ax_energy.set_ylabel("kinetic energy")
|
|
860
|
+
|
|
861
|
+
def update(i):
|
|
862
|
+
idx = frame_idx[i]
|
|
863
|
+
field_line.set_ydata(wakefield_e_field(x_field, t_hist[idx], E0, k, v_phase))
|
|
864
|
+
particle_marker.set_data([x_hist[idx]], [wakefield_e_field(x_hist[idx], t_hist[idx], E0, k, v_phase)])
|
|
865
|
+
energy_line.set_data(t_hist[: idx + 1], kinetic_energy[: idx + 1])
|
|
866
|
+
return field_line, particle_marker, energy_line
|
|
867
|
+
|
|
868
|
+
return FuncAnimation(fig, update, frames=len(frame_idx), interval=interval, blit=True)
|