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,588 @@
|
|
|
1
|
+
"""Electrostatic particle-in-cell (PIC) solution of the 1D1V Vlasov-Poisson system.
|
|
2
|
+
|
|
3
|
+
Rather than discretizing the distribution function :math:`f(x, v, t)` on a
|
|
4
|
+
phase-space grid, a PIC code samples it with a finite set of
|
|
5
|
+
computational "super-particles" and lets them stream along exact
|
|
6
|
+
single-particle orbits, recovering the self-consistent field by depositing
|
|
7
|
+
their charge onto a spatial grid and solving Poisson's equation there each
|
|
8
|
+
step. This is Vlasov's collisionless equation
|
|
9
|
+
:math:`\\partial_t f + v\\,\\partial_x f - (e/m)E\\,\\partial_v f = 0` solved
|
|
10
|
+
by the method of characteristics: each particle *is* one characteristic.
|
|
11
|
+
The whole pipeline -- :func:`deposit_number_density`,
|
|
12
|
+
:func:`solve_poisson_1d`, :func:`interpolate_field`, :func:`pic_step` --
|
|
13
|
+
reproduces collisionless (Landau) damping and the two-stream instability
|
|
14
|
+
without ever assuming a collision operator, exactly as the underlying
|
|
15
|
+
kinetic theory predicts.
|
|
16
|
+
|
|
17
|
+
Units throughout are the standard PIC-normalized units: electron charge
|
|
18
|
+
:math:`e=1`, mass :math:`m_e=1`, vacuum permittivity
|
|
19
|
+
:math:`\\varepsilon_0=1`, and equilibrium density :math:`n_0=1`, so that
|
|
20
|
+
the electron plasma frequency :math:`\\omega_{pe}=1` and velocities are in
|
|
21
|
+
units of the thermal speed :math:`v_{th}`. Ions are a fixed, uniform,
|
|
22
|
+
charge-neutralizing background (infinite mass limit).
|
|
23
|
+
|
|
24
|
+
The charge-deposit and field-interpolation kernels -- called once per
|
|
25
|
+
particle per step -- are compiled with Numba, since they dominate the
|
|
26
|
+
cost of every PIC time step.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
from __future__ import annotations
|
|
30
|
+
|
|
31
|
+
import numpy as np
|
|
32
|
+
from numba import njit
|
|
33
|
+
|
|
34
|
+
__all__ = [
|
|
35
|
+
"maxwellian_velocities",
|
|
36
|
+
"landau_damping_ic",
|
|
37
|
+
"langmuir_wave_ic",
|
|
38
|
+
"two_stream_ic",
|
|
39
|
+
"deposit_number_density",
|
|
40
|
+
"interpolate_field",
|
|
41
|
+
"solve_poisson_1d",
|
|
42
|
+
"pic_step",
|
|
43
|
+
"pic_simulate",
|
|
44
|
+
"landau_damping_rate",
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def maxwellian_velocities(n_particles: int, v_th: float, seed: int = 0) -> np.ndarray:
|
|
49
|
+
"""Sample particle velocities from a Maxwellian (Gaussian) distribution.
|
|
50
|
+
|
|
51
|
+
Parameters
|
|
52
|
+
----------
|
|
53
|
+
n_particles : int
|
|
54
|
+
Number of particles to sample.
|
|
55
|
+
v_th : float
|
|
56
|
+
Thermal speed (standard deviation of the Gaussian), in
|
|
57
|
+
normalized velocity units.
|
|
58
|
+
seed : int, default=0
|
|
59
|
+
Seed for the pseudo-random number generator, for reproducibility.
|
|
60
|
+
|
|
61
|
+
Returns
|
|
62
|
+
-------
|
|
63
|
+
ndarray, shape (n_particles,)
|
|
64
|
+
Sampled velocities.
|
|
65
|
+
|
|
66
|
+
Examples
|
|
67
|
+
--------
|
|
68
|
+
>>> import numpy as np
|
|
69
|
+
>>> v = maxwellian_velocities(4, v_th=1.0, seed=0)
|
|
70
|
+
>>> bool(np.allclose(v, [0.12573022, -0.13210486, 0.64042265, 0.10490012]))
|
|
71
|
+
True
|
|
72
|
+
"""
|
|
73
|
+
rng = np.random.default_rng(seed)
|
|
74
|
+
return rng.normal(0.0, v_th, n_particles)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def landau_damping_ic(n_particles: int, L: float, k_mode: float, alpha: float, v_th: float, seed: int = 0) -> tuple:
|
|
78
|
+
"""Quiet-start initial condition seeding a single-mode density perturbation for a Landau damping test.
|
|
79
|
+
|
|
80
|
+
Displaces an otherwise uniform particle load by
|
|
81
|
+
:math:`\\delta x = (\\alpha/k)\\sin(kx_0)`, which -- since particle
|
|
82
|
+
number is conserved through the displacement's Jacobian -- produces
|
|
83
|
+
exactly the density perturbation :math:`n(x) \\approx n_0(1 -
|
|
84
|
+
\\alpha\\cos(kx))` used in the classic linear Landau damping test
|
|
85
|
+
problem, without the sampling noise a random density draw would add
|
|
86
|
+
on top of the intended signal.
|
|
87
|
+
|
|
88
|
+
Parameters
|
|
89
|
+
----------
|
|
90
|
+
n_particles : int
|
|
91
|
+
Number of particles.
|
|
92
|
+
L : float
|
|
93
|
+
Domain length (periodic), in normalized length units. Choosing
|
|
94
|
+
:math:`L = 2\\pi/k_{mode}` fits exactly one wavelength.
|
|
95
|
+
k_mode : float
|
|
96
|
+
Wavenumber of the seeded perturbation.
|
|
97
|
+
alpha : float
|
|
98
|
+
Perturbation amplitude (:math:`\\alpha \\ll 1` for the linear regime).
|
|
99
|
+
v_th : float
|
|
100
|
+
Thermal speed of the background Maxwellian.
|
|
101
|
+
seed : int, default=0
|
|
102
|
+
Random seed for the velocity sampling.
|
|
103
|
+
|
|
104
|
+
Returns
|
|
105
|
+
-------
|
|
106
|
+
x, v : ndarray, shape (n_particles,)
|
|
107
|
+
Particle positions (in :math:`[0, L)`) and velocities.
|
|
108
|
+
|
|
109
|
+
See Also
|
|
110
|
+
--------
|
|
111
|
+
pic_simulate : Evolve this initial condition forward in time.
|
|
112
|
+
landau_damping_rate : The analytic decay rate this test is checked against.
|
|
113
|
+
|
|
114
|
+
Examples
|
|
115
|
+
--------
|
|
116
|
+
>>> import numpy as np
|
|
117
|
+
>>> x, v = landau_damping_ic(4, L=4.0, k_mode=2 * 3.141592653589793 / 4.0, alpha=0.1, v_th=1.0, seed=0)
|
|
118
|
+
>>> bool(np.all((x >= 0) & (x < 4.0)))
|
|
119
|
+
True
|
|
120
|
+
>>> x.shape, v.shape
|
|
121
|
+
((4,), (4,))
|
|
122
|
+
"""
|
|
123
|
+
x0 = np.linspace(0.0, L, n_particles, endpoint=False)
|
|
124
|
+
x = np.mod(x0 + (alpha / k_mode) * np.sin(k_mode * x0), L)
|
|
125
|
+
v = maxwellian_velocities(n_particles, v_th, seed)
|
|
126
|
+
return x, v
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def langmuir_wave_ic(n_particles: int, L: float, k_mode: float, alpha: float, v_th: float, seed: int = 0) -> tuple:
|
|
130
|
+
"""Small-amplitude single-mode initial condition for a driven Langmuir (electron plasma) wave.
|
|
131
|
+
|
|
132
|
+
Identical in construction to :func:`landau_damping_ic` -- the same
|
|
133
|
+
quiet-start displacement :math:`\\delta x=(\\alpha/k)\\sin(kx_0)` seeds
|
|
134
|
+
the same density perturbation :math:`n_0(1-\\alpha\\cos(kx))` -- but
|
|
135
|
+
additionally imparts each particle the small coherent velocity
|
|
136
|
+
perturbation :math:`\\delta v = (\\alpha\\,\\omega_{pe}/k)\\sin(kx_0)`
|
|
137
|
+
predicted by the linearized cold-fluid continuity equation for a wave
|
|
138
|
+
oscillating at the electron plasma frequency (:math:`\\omega\\approx\\omega_{pe}=1`
|
|
139
|
+
in these normalized units). Giving both the density *and* velocity
|
|
140
|
+
perturbation their linear-theory phase relationship launches a wave
|
|
141
|
+
that is (for :math:`k\\lambda_D \\ll 1`, i.e. `v_th` small enough that
|
|
142
|
+
Landau damping in :func:`landau_damping_rate` is negligible) very
|
|
143
|
+
weakly damped, so :func:`pic_simulate` shows the density oscillating
|
|
144
|
+
in place at :math:`\\omega_{pe}` for many periods rather than decaying
|
|
145
|
+
within a few, as the otherwise-identical :func:`landau_damping_ic`
|
|
146
|
+
(fluid velocity perturbation omitted) would for the same `alpha`.
|
|
147
|
+
|
|
148
|
+
Parameters
|
|
149
|
+
----------
|
|
150
|
+
n_particles : int
|
|
151
|
+
Number of particles.
|
|
152
|
+
L : float
|
|
153
|
+
Domain length (periodic); :math:`L=2\\pi/k_{mode}` fits one wavelength.
|
|
154
|
+
k_mode : float
|
|
155
|
+
Wavenumber of the seeded standing wave.
|
|
156
|
+
alpha : float
|
|
157
|
+
Perturbation amplitude (:math:`\\alpha \\ll 1`).
|
|
158
|
+
v_th : float
|
|
159
|
+
Thermal speed of the background Maxwellian; keep
|
|
160
|
+
:math:`k_{mode}\\,v_{th} \\ll \\omega_{pe}=1` for the wave to be
|
|
161
|
+
only weakly Landau-damped.
|
|
162
|
+
seed : int, default=0
|
|
163
|
+
Random seed for the thermal velocity sampling.
|
|
164
|
+
|
|
165
|
+
Returns
|
|
166
|
+
-------
|
|
167
|
+
x, v : ndarray, shape (n_particles,)
|
|
168
|
+
Particle positions and velocities.
|
|
169
|
+
|
|
170
|
+
See Also
|
|
171
|
+
--------
|
|
172
|
+
landau_damping_ic : The companion density-only perturbation (no
|
|
173
|
+
coherent velocity kick), which damps rather than sustains its
|
|
174
|
+
oscillation.
|
|
175
|
+
pic_simulate : Evolve this initial condition forward in time.
|
|
176
|
+
|
|
177
|
+
Examples
|
|
178
|
+
--------
|
|
179
|
+
>>> import numpy as np
|
|
180
|
+
>>> k = 2 * np.pi / 4.0
|
|
181
|
+
>>> x, v = langmuir_wave_ic(4, L=4.0, k_mode=k, alpha=0.05, v_th=0.05, seed=0)
|
|
182
|
+
>>> bool(np.all((x >= 0) & (x < 4.0)))
|
|
183
|
+
True
|
|
184
|
+
>>> x.shape, v.shape
|
|
185
|
+
((4,), (4,))
|
|
186
|
+
"""
|
|
187
|
+
x0 = np.linspace(0.0, L, n_particles, endpoint=False)
|
|
188
|
+
x = np.mod(x0 + (alpha / k_mode) * np.sin(k_mode * x0), L)
|
|
189
|
+
omega_pe = 1.0
|
|
190
|
+
dv = (alpha * omega_pe / k_mode) * np.sin(k_mode * x0)
|
|
191
|
+
v = maxwellian_velocities(n_particles, v_th, seed) + dv
|
|
192
|
+
return x, v
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def two_stream_ic(n_particles: int, L: float, v_drift: float, v_th: float, seed: int = 0) -> tuple:
|
|
196
|
+
"""Initial condition for the two-stream instability: two counter-streaming Maxwellian beams.
|
|
197
|
+
|
|
198
|
+
Splits the particles into two equal populations drifting at
|
|
199
|
+
:math:`\\pm v_{drift}`, each with thermal spread :math:`v_{th}`, and
|
|
200
|
+
seeds the fastest-growing long-wavelength mode with a small
|
|
201
|
+
density ripple. When :math:`v_{drift}` exceeds the thermal spread by
|
|
202
|
+
enough to make the combined velocity distribution doubly-peaked, the
|
|
203
|
+
positive-slope region between the two peaks violates the (kinetic)
|
|
204
|
+
Penrose stability criterion and the ripple grows exponentially,
|
|
205
|
+
eventually rolling the two beams up into a single phase-space vortex.
|
|
206
|
+
|
|
207
|
+
Parameters
|
|
208
|
+
----------
|
|
209
|
+
n_particles : int
|
|
210
|
+
Number of particles (split evenly between the two beams).
|
|
211
|
+
L : float
|
|
212
|
+
Domain length (periodic).
|
|
213
|
+
v_drift : float
|
|
214
|
+
Drift speed of each beam (beams move at :math:`+v_{drift}` and :math:`-v_{drift}`).
|
|
215
|
+
v_th : float
|
|
216
|
+
Thermal spread of each beam.
|
|
217
|
+
seed : int, default=0
|
|
218
|
+
Random seed.
|
|
219
|
+
|
|
220
|
+
Returns
|
|
221
|
+
-------
|
|
222
|
+
x, v : ndarray, shape (n_particles,)
|
|
223
|
+
Particle positions and velocities.
|
|
224
|
+
|
|
225
|
+
See Also
|
|
226
|
+
--------
|
|
227
|
+
landau_damping_ic : The companion (stable) single-beam initial condition.
|
|
228
|
+
|
|
229
|
+
Examples
|
|
230
|
+
--------
|
|
231
|
+
>>> import numpy as np
|
|
232
|
+
>>> x, v = two_stream_ic(1000, L=10.0, v_drift=3.0, v_th=0.5, seed=0)
|
|
233
|
+
>>> x.shape, v.shape
|
|
234
|
+
((1000,), (1000,))
|
|
235
|
+
>>> bool(np.mean(v) < 0.5)
|
|
236
|
+
True
|
|
237
|
+
"""
|
|
238
|
+
rng = np.random.default_rng(seed)
|
|
239
|
+
half = n_particles // 2
|
|
240
|
+
x0 = np.linspace(0.0, L, n_particles, endpoint=False)
|
|
241
|
+
k_seed = 2.0 * np.pi / L
|
|
242
|
+
x = np.mod(x0 + 0.01 * np.sin(k_seed * x0), L)
|
|
243
|
+
v = np.empty(n_particles)
|
|
244
|
+
v[:half] = rng.normal(v_drift, v_th, half)
|
|
245
|
+
v[half:] = rng.normal(-v_drift, v_th, n_particles - half)
|
|
246
|
+
return x, v
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
@njit(cache=True)
|
|
250
|
+
def _deposit_cic_kernel(x, weight, L, ng):
|
|
251
|
+
dx = L / ng
|
|
252
|
+
rho = np.zeros(ng)
|
|
253
|
+
for p in range(x.shape[0]):
|
|
254
|
+
xi = x[p] / dx
|
|
255
|
+
i0 = int(np.floor(xi)) % ng
|
|
256
|
+
frac = xi - np.floor(xi)
|
|
257
|
+
i1 = (i0 + 1) % ng
|
|
258
|
+
rho[i0] += weight * (1.0 - frac) / dx
|
|
259
|
+
rho[i1] += weight * frac / dx
|
|
260
|
+
return rho
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
@njit(cache=True)
|
|
264
|
+
def _interpolate_cic_kernel(x, field, L, ng):
|
|
265
|
+
dx = L / ng
|
|
266
|
+
out = np.empty(x.shape[0])
|
|
267
|
+
for p in range(x.shape[0]):
|
|
268
|
+
xi = x[p] / dx
|
|
269
|
+
i0 = int(np.floor(xi)) % ng
|
|
270
|
+
frac = xi - np.floor(xi)
|
|
271
|
+
i1 = (i0 + 1) % ng
|
|
272
|
+
out[p] = field[i0] * (1.0 - frac) + field[i1] * frac
|
|
273
|
+
return out
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
@njit(cache=True)
|
|
277
|
+
def _push_kernel(x, v, E_at_particles, qm, dt, L):
|
|
278
|
+
n = x.shape[0]
|
|
279
|
+
for p in range(n):
|
|
280
|
+
v[p] += qm * E_at_particles[p] * dt
|
|
281
|
+
x[p] = (x[p] + v[p] * dt) % L
|
|
282
|
+
return x, v
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def deposit_number_density(x: np.ndarray, L: float, ng: int, n0: float = 1.0) -> np.ndarray:
|
|
286
|
+
"""Deposit particle positions onto a grid as a number density, via cloud-in-cell (CIC) weighting.
|
|
287
|
+
|
|
288
|
+
Each particle represents a "cloud" of physical charge spanning one
|
|
289
|
+
grid cell, split linearly between its two nearest grid points -- the
|
|
290
|
+
standard first-order PIC weighting scheme, chosen because it is exact
|
|
291
|
+
for a uniform density and (unlike nearest-grid-point deposit)
|
|
292
|
+
produces a smooth, differentiable force with no self-force
|
|
293
|
+
discontinuities as particles cross cell boundaries.
|
|
294
|
+
|
|
295
|
+
Parameters
|
|
296
|
+
----------
|
|
297
|
+
x : ndarray, shape (n_particles,)
|
|
298
|
+
Particle positions in :math:`[0, L)`.
|
|
299
|
+
L : float
|
|
300
|
+
Domain length (periodic).
|
|
301
|
+
ng : int
|
|
302
|
+
Number of grid points.
|
|
303
|
+
n0 : float, default=1.0
|
|
304
|
+
Equilibrium number density (sets each particle's statistical weight,
|
|
305
|
+
:math:`n_0 L / n_{particles}`).
|
|
306
|
+
|
|
307
|
+
Returns
|
|
308
|
+
-------
|
|
309
|
+
ndarray, shape (ng,)
|
|
310
|
+
Number density on the grid.
|
|
311
|
+
|
|
312
|
+
See Also
|
|
313
|
+
--------
|
|
314
|
+
interpolate_field : The companion gather operation.
|
|
315
|
+
|
|
316
|
+
Examples
|
|
317
|
+
--------
|
|
318
|
+
Total deposited charge exactly equals the physical charge represented,
|
|
319
|
+
regardless of how the particles are distributed:
|
|
320
|
+
|
|
321
|
+
>>> import numpy as np
|
|
322
|
+
>>> x = np.array([0.1, 2.4, 4.9, 7.7])
|
|
323
|
+
>>> rho = deposit_number_density(x, L=10.0, ng=20, n0=2.0)
|
|
324
|
+
>>> dx = 10.0 / 20
|
|
325
|
+
>>> round(float(np.sum(rho) * dx), 8)
|
|
326
|
+
20.0
|
|
327
|
+
"""
|
|
328
|
+
n_particles = x.shape[0]
|
|
329
|
+
weight = n0 * L / n_particles
|
|
330
|
+
return _deposit_cic_kernel(np.asarray(x, dtype=float), float(weight), float(L), int(ng))
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def interpolate_field(x: np.ndarray, field_grid: np.ndarray, L: float) -> np.ndarray:
|
|
334
|
+
"""Interpolate a grid-defined field to particle positions, via cloud-in-cell (CIC) weighting.
|
|
335
|
+
|
|
336
|
+
The gather step dual to :func:`deposit_number_density`: using the
|
|
337
|
+
same linear weights for both deposit and gather is what makes the
|
|
338
|
+
PIC method momentum-conserving (no self-force on an isolated particle).
|
|
339
|
+
|
|
340
|
+
Parameters
|
|
341
|
+
----------
|
|
342
|
+
x : ndarray, shape (n_particles,)
|
|
343
|
+
Particle positions in :math:`[0, L)`.
|
|
344
|
+
field_grid : ndarray, shape (ng,)
|
|
345
|
+
Field values on the grid, e.g. from :func:`solve_poisson_1d`.
|
|
346
|
+
L : float
|
|
347
|
+
Domain length (periodic).
|
|
348
|
+
|
|
349
|
+
Returns
|
|
350
|
+
-------
|
|
351
|
+
ndarray, shape (n_particles,)
|
|
352
|
+
Field value at each particle's position.
|
|
353
|
+
|
|
354
|
+
Examples
|
|
355
|
+
--------
|
|
356
|
+
A particle sitting exactly on a grid node picks up that node's value:
|
|
357
|
+
|
|
358
|
+
>>> import numpy as np
|
|
359
|
+
>>> ng = 8
|
|
360
|
+
>>> field_grid = np.array([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0])
|
|
361
|
+
>>> x = np.array([2 * (10.0 / ng)])
|
|
362
|
+
>>> interpolate_field(x, field_grid, L=10.0)
|
|
363
|
+
array([3.])
|
|
364
|
+
"""
|
|
365
|
+
ng = field_grid.shape[0]
|
|
366
|
+
return _interpolate_cic_kernel(np.asarray(x, dtype=float), np.asarray(field_grid, dtype=float), float(L), int(ng))
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def solve_poisson_1d(rho: np.ndarray, L: float) -> np.ndarray:
|
|
370
|
+
"""Solve the 1D periodic Poisson equation :math:`dE/dx = \\rho` (normalized :math:`\\varepsilon_0=1`) via FFT.
|
|
371
|
+
|
|
372
|
+
Parameters
|
|
373
|
+
----------
|
|
374
|
+
rho : ndarray, shape (ng,)
|
|
375
|
+
Net charge density on the grid (e.g. ion background minus deposited
|
|
376
|
+
electron density). Its mean is discarded, since a uniform charge
|
|
377
|
+
density has no periodic solution and physically should integrate
|
|
378
|
+
to zero net charge in the box.
|
|
379
|
+
L : float
|
|
380
|
+
Domain length (periodic).
|
|
381
|
+
|
|
382
|
+
Returns
|
|
383
|
+
-------
|
|
384
|
+
ndarray, shape (ng,)
|
|
385
|
+
Electric field on the grid.
|
|
386
|
+
|
|
387
|
+
See Also
|
|
388
|
+
--------
|
|
389
|
+
deposit_number_density : Supplies the density this solves for.
|
|
390
|
+
interpolate_field : Gathers this field back onto the particles.
|
|
391
|
+
|
|
392
|
+
Examples
|
|
393
|
+
--------
|
|
394
|
+
>>> import numpy as np
|
|
395
|
+
>>> ng = 64
|
|
396
|
+
>>> L = 2 * np.pi
|
|
397
|
+
>>> x_grid = np.linspace(0, L, ng, endpoint=False)
|
|
398
|
+
>>> rho = np.sin(x_grid)
|
|
399
|
+
>>> E = solve_poisson_1d(rho, L)
|
|
400
|
+
>>> E_exact = -np.cos(x_grid)
|
|
401
|
+
>>> bool(np.max(np.abs(E - E_exact)) < 1e-10)
|
|
402
|
+
True
|
|
403
|
+
"""
|
|
404
|
+
ng = rho.shape[0]
|
|
405
|
+
k = 2.0 * np.pi * np.fft.fftfreq(ng, d=L / ng)
|
|
406
|
+
rho_hat = np.fft.fft(rho - np.mean(rho))
|
|
407
|
+
E_hat = np.zeros_like(rho_hat)
|
|
408
|
+
nonzero = k != 0
|
|
409
|
+
E_hat[nonzero] = -1j * rho_hat[nonzero] / k[nonzero]
|
|
410
|
+
return np.real(np.fft.ifft(E_hat))
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
def pic_step(x: np.ndarray, v: np.ndarray, L: float, ng: int, dt: float, qm: float = -1.0, n0: float = 1.0) -> tuple:
|
|
414
|
+
"""Advance the electrostatic PIC system one leapfrog step.
|
|
415
|
+
|
|
416
|
+
Deposits the electron density, solves for the self-consistent field
|
|
417
|
+
against a uniform neutralizing ion background, gathers the field back
|
|
418
|
+
onto the particles, and kicks/drifts them -- one full cycle of the
|
|
419
|
+
deposit-solve-gather-push loop at the heart of every PIC code.
|
|
420
|
+
Velocities are staggered a half step behind positions (standard
|
|
421
|
+
leapfrog); see :func:`pic_simulate` for a driver that initializes
|
|
422
|
+
that offset correctly.
|
|
423
|
+
|
|
424
|
+
Parameters
|
|
425
|
+
----------
|
|
426
|
+
x : ndarray, shape (n_particles,)
|
|
427
|
+
Particle positions in :math:`[0, L)`.
|
|
428
|
+
v : ndarray, shape (n_particles,)
|
|
429
|
+
Particle velocities, staggered a half step behind ``x``.
|
|
430
|
+
L : float
|
|
431
|
+
Domain length (periodic).
|
|
432
|
+
ng : int
|
|
433
|
+
Number of grid points.
|
|
434
|
+
dt : float
|
|
435
|
+
Time step.
|
|
436
|
+
qm : float, default=-1.0
|
|
437
|
+
Charge-to-mass ratio in normalized units (``-1.0`` for electrons
|
|
438
|
+
with a fixed, uniform ion background of density ``n0``).
|
|
439
|
+
n0 : float, default=1.0
|
|
440
|
+
Equilibrium number density.
|
|
441
|
+
|
|
442
|
+
Returns
|
|
443
|
+
-------
|
|
444
|
+
x_new, v_new : ndarray
|
|
445
|
+
Updated positions and velocities.
|
|
446
|
+
field_energy : float
|
|
447
|
+
:math:`\\int E^2/2\\,dx`, evaluated at the field used for this step's kick.
|
|
448
|
+
|
|
449
|
+
See Also
|
|
450
|
+
--------
|
|
451
|
+
pic_simulate : Repeated application of this step with correct leapfrog initialization.
|
|
452
|
+
|
|
453
|
+
Examples
|
|
454
|
+
--------
|
|
455
|
+
>>> import numpy as np
|
|
456
|
+
>>> x = np.linspace(0, 10, 50, endpoint=False)
|
|
457
|
+
>>> v = np.zeros(50)
|
|
458
|
+
>>> x_new, v_new, fe = pic_step(x, v, L=10.0, ng=32, dt=0.1)
|
|
459
|
+
>>> x_new.shape, v_new.shape
|
|
460
|
+
((50,), (50,))
|
|
461
|
+
>>> bool(np.isfinite(fe))
|
|
462
|
+
True
|
|
463
|
+
"""
|
|
464
|
+
n_e = deposit_number_density(x, L, ng, n0)
|
|
465
|
+
rho = n0 - n_e
|
|
466
|
+
E = solve_poisson_1d(rho, L)
|
|
467
|
+
field_energy = 0.5 * np.sum(E**2) * (L / ng)
|
|
468
|
+
E_at_particles = interpolate_field(x, E, L)
|
|
469
|
+
x_new, v_new = _push_kernel(np.asarray(x, dtype=float).copy(), np.asarray(v, dtype=float).copy(), E_at_particles, float(qm), float(dt), float(L))
|
|
470
|
+
return x_new, v_new, float(field_energy)
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
def pic_simulate(x0: np.ndarray, v0: np.ndarray, L: float, ng: int, dt: float, steps: int, qm: float = -1.0, n0: float = 1.0) -> dict:
|
|
474
|
+
"""Run an electrostatic PIC simulation forward in time, recording the field-energy history.
|
|
475
|
+
|
|
476
|
+
Correctly initializes the leapfrog velocity offset (staggering
|
|
477
|
+
``v0`` back by half a step using the field at ``x0``) before
|
|
478
|
+
repeatedly applying :func:`pic_step`.
|
|
479
|
+
|
|
480
|
+
Parameters
|
|
481
|
+
----------
|
|
482
|
+
x0 : ndarray, shape (n_particles,)
|
|
483
|
+
Initial particle positions, e.g. from :func:`landau_damping_ic` or :func:`two_stream_ic`.
|
|
484
|
+
v0 : ndarray, shape (n_particles,)
|
|
485
|
+
Initial particle velocities.
|
|
486
|
+
L : float
|
|
487
|
+
Domain length (periodic).
|
|
488
|
+
ng : int
|
|
489
|
+
Number of grid points.
|
|
490
|
+
dt : float
|
|
491
|
+
Time step.
|
|
492
|
+
steps : int
|
|
493
|
+
Number of steps to advance.
|
|
494
|
+
qm : float, default=-1.0
|
|
495
|
+
Charge-to-mass ratio in normalized units.
|
|
496
|
+
n0 : float, default=1.0
|
|
497
|
+
Equilibrium number density.
|
|
498
|
+
|
|
499
|
+
Returns
|
|
500
|
+
-------
|
|
501
|
+
dict
|
|
502
|
+
``{"t": ndarray of shape (steps,), "field_energy": ndarray of shape (steps,),
|
|
503
|
+
"x": final positions, "v": final velocities}``.
|
|
504
|
+
|
|
505
|
+
See Also
|
|
506
|
+
--------
|
|
507
|
+
pic_step : The single-step update repeated here.
|
|
508
|
+
landau_damping_rate : Analytic decay rate to compare the field-energy history against.
|
|
509
|
+
|
|
510
|
+
Examples
|
|
511
|
+
--------
|
|
512
|
+
>>> import numpy as np
|
|
513
|
+
>>> x0, v0 = landau_damping_ic(2000, L=4 * np.pi, k_mode=0.5, alpha=0.01, v_th=1.0, seed=0)
|
|
514
|
+
>>> result = pic_simulate(x0, v0, L=4 * np.pi, ng=32, dt=0.1, steps=20)
|
|
515
|
+
>>> result["field_energy"].shape
|
|
516
|
+
(20,)
|
|
517
|
+
>>> bool(np.all(np.isfinite(result["field_energy"])))
|
|
518
|
+
True
|
|
519
|
+
"""
|
|
520
|
+
x = np.asarray(x0, dtype=float).copy()
|
|
521
|
+
v = np.asarray(v0, dtype=float).copy()
|
|
522
|
+
n_e = deposit_number_density(x, L, ng, n0)
|
|
523
|
+
E0 = solve_poisson_1d(n0 - n_e, L)
|
|
524
|
+
E0_at_particles = interpolate_field(x, E0, L)
|
|
525
|
+
v = v - 0.5 * qm * E0_at_particles * dt
|
|
526
|
+
|
|
527
|
+
field_energy = np.empty(steps)
|
|
528
|
+
for s in range(steps):
|
|
529
|
+
x, v, fe = pic_step(x, v, L, ng, dt, qm, n0)
|
|
530
|
+
field_energy[s] = fe
|
|
531
|
+
t = np.arange(steps) * dt
|
|
532
|
+
return {"t": t, "field_energy": field_energy, "x": x, "v": v}
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
def landau_damping_rate(k: float, v_th: float, omega_pe: float = 1.0) -> float:
|
|
536
|
+
"""Analytic linear Landau damping rate for a Maxwellian electron plasma.
|
|
537
|
+
|
|
538
|
+
The classic weak-damping result (Landau, 1946) for a wave of
|
|
539
|
+
wavenumber :math:`k` on a Maxwellian of thermal speed :math:`v_{th}`,
|
|
540
|
+
with Debye length :math:`\\lambda_D = v_{th}/\\omega_{pe}`:
|
|
541
|
+
|
|
542
|
+
.. math::
|
|
543
|
+
|
|
544
|
+
\\gamma = -\\omega_{pe}\\sqrt{\\frac{\\pi}{8}}\\,
|
|
545
|
+
\\frac{1}{(k\\lambda_D)^3}\\,
|
|
546
|
+
\\exp\\!\\left(-\\frac{1}{2(k\\lambda_D)^2} - \\frac{3}{2}\\right).
|
|
547
|
+
|
|
548
|
+
Electrons resonant with the wave's phase velocity
|
|
549
|
+
(:math:`v = \\omega/k`) surf it, extracting energy from the field on
|
|
550
|
+
net because the Maxwellian has slightly more slower particles being
|
|
551
|
+
accelerated than faster particles being decelerated -- a purely
|
|
552
|
+
collisionless damping mechanism with no dissipation at the particle
|
|
553
|
+
level, reproduced here by :func:`pic_simulate` without any explicit
|
|
554
|
+
damping term in the equations of motion.
|
|
555
|
+
|
|
556
|
+
Parameters
|
|
557
|
+
----------
|
|
558
|
+
k : float
|
|
559
|
+
Wavenumber, in units of inverse Debye length times
|
|
560
|
+
:math:`k\\lambda_D` conventions -- concretely, pass the physical
|
|
561
|
+
wavenumber and set ``v_th``/``omega_pe`` consistently.
|
|
562
|
+
v_th : float
|
|
563
|
+
Electron thermal speed.
|
|
564
|
+
omega_pe : float, default=1.0
|
|
565
|
+
Electron plasma frequency (``1.0`` in the normalized units used
|
|
566
|
+
throughout this module).
|
|
567
|
+
|
|
568
|
+
Returns
|
|
569
|
+
-------
|
|
570
|
+
float
|
|
571
|
+
Damping rate :math:`\\gamma` (negative, since the wave decays).
|
|
572
|
+
The formula is only accurate for weak damping,
|
|
573
|
+
:math:`k\\lambda_D \\lesssim 0.5`.
|
|
574
|
+
|
|
575
|
+
See Also
|
|
576
|
+
--------
|
|
577
|
+
pic_simulate : Numerically reproduces this decay from first principles.
|
|
578
|
+
|
|
579
|
+
Examples
|
|
580
|
+
--------
|
|
581
|
+
The standard textbook benchmark case, :math:`k\\lambda_D = 0.5`:
|
|
582
|
+
|
|
583
|
+
>>> round(float(landau_damping_rate(k=0.5, v_th=1.0)), 4)
|
|
584
|
+
-0.1514
|
|
585
|
+
"""
|
|
586
|
+
lambda_D = v_th / omega_pe
|
|
587
|
+
klD = k * lambda_D
|
|
588
|
+
return -omega_pe * np.sqrt(np.pi / 8.0) * (1.0 / klD**3) * np.exp(-1.0 / (2.0 * klD**2) - 1.5)
|