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,562 @@
|
|
|
1
|
+
"""Single-particle motion in electromagnetic fields: the Boris pusher and guiding-center drifts.
|
|
2
|
+
|
|
3
|
+
Two complementary pictures of charged-particle motion in a magnetized
|
|
4
|
+
plasma live here. The *full-orbit* picture integrates the exact Lorentz
|
|
5
|
+
force :math:`m\\dot{\\mathbf{v}} = q(\\mathbf{E} + \\mathbf{v}\\times\\mathbf{B})`
|
|
6
|
+
with :func:`boris_push` / :func:`boris_integrate`, Jay Boris's 1970
|
|
7
|
+
energy-conserving leapfrog scheme -- still the workhorse integrator of
|
|
8
|
+
every particle-in-cell (PIC) plasma code. The *guiding-center* picture
|
|
9
|
+
instead averages over the fast gyration and tracks only the slow drift of
|
|
10
|
+
the orbit's center: :func:`exb_drift`, :func:`grad_b_drift`, and
|
|
11
|
+
:func:`curvature_drift` give that drift velocity directly, while
|
|
12
|
+
:func:`magnetic_moment` and :func:`mirror_force` capture the adiabatic
|
|
13
|
+
invariant :math:`\\mu = m v_\\perp^2 / 2B` responsible for magnetic-mirror
|
|
14
|
+
confinement.
|
|
15
|
+
|
|
16
|
+
The Boris kernels are compiled with Numba for particle-in-cell-scale
|
|
17
|
+
performance; every other function here is a closed-form NumPy expression.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import numpy as np
|
|
23
|
+
from numba import njit
|
|
24
|
+
|
|
25
|
+
from physicskit import constants as _const
|
|
26
|
+
|
|
27
|
+
__all__ = [
|
|
28
|
+
"QE",
|
|
29
|
+
"ME",
|
|
30
|
+
"MP",
|
|
31
|
+
"cyclotron_frequency",
|
|
32
|
+
"larmor_radius",
|
|
33
|
+
"magnetic_moment",
|
|
34
|
+
"boris_push",
|
|
35
|
+
"boris_integrate",
|
|
36
|
+
"exb_drift",
|
|
37
|
+
"grad_b_drift",
|
|
38
|
+
"curvature_drift",
|
|
39
|
+
"mirror_force",
|
|
40
|
+
"magnetic_mirror_bounce",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
QE = _const.ELEMENTARY_CHARGE
|
|
44
|
+
"""Elementary charge, in Coulombs."""
|
|
45
|
+
|
|
46
|
+
ME = _const.ELECTRON_MASS
|
|
47
|
+
"""Electron mass, in kilograms."""
|
|
48
|
+
|
|
49
|
+
MP = _const.PROTON_MASS
|
|
50
|
+
"""Proton mass, in kilograms."""
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def cyclotron_frequency(q: float, m: float, B: float) -> float:
|
|
54
|
+
"""Angular cyclotron (gyration) frequency :math:`\\omega_c = qB/m`.
|
|
55
|
+
|
|
56
|
+
Parameters
|
|
57
|
+
----------
|
|
58
|
+
q : float
|
|
59
|
+
Particle charge in Coulombs (signed).
|
|
60
|
+
m : float
|
|
61
|
+
Particle mass in kilograms.
|
|
62
|
+
B : float
|
|
63
|
+
Magnetic field magnitude in Tesla.
|
|
64
|
+
|
|
65
|
+
Returns
|
|
66
|
+
-------
|
|
67
|
+
float
|
|
68
|
+
Signed angular gyrofrequency in rad/s (negative for negatively
|
|
69
|
+
charged particles, reflecting the opposite sense of rotation).
|
|
70
|
+
|
|
71
|
+
See Also
|
|
72
|
+
--------
|
|
73
|
+
larmor_radius : The orbit radius set by this frequency.
|
|
74
|
+
|
|
75
|
+
Examples
|
|
76
|
+
--------
|
|
77
|
+
A proton in a 1 T field gyrates about 15.3 MHz:
|
|
78
|
+
|
|
79
|
+
>>> round(cyclotron_frequency(QE, MP, 1.0) / (2 * 3.141592653589793) / 1e6, 2)
|
|
80
|
+
15.25
|
|
81
|
+
"""
|
|
82
|
+
return q * B / m
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def larmor_radius(v_perp: float, q: float, m: float, B: float) -> float:
|
|
86
|
+
"""Larmor (gyro) radius :math:`r_L = m v_\\perp / (|q| B)`.
|
|
87
|
+
|
|
88
|
+
Parameters
|
|
89
|
+
----------
|
|
90
|
+
v_perp : float
|
|
91
|
+
Speed perpendicular to the magnetic field, in m/s.
|
|
92
|
+
q : float
|
|
93
|
+
Particle charge in Coulombs (sign is ignored).
|
|
94
|
+
m : float
|
|
95
|
+
Particle mass in kilograms.
|
|
96
|
+
B : float
|
|
97
|
+
Magnetic field magnitude in Tesla.
|
|
98
|
+
|
|
99
|
+
Returns
|
|
100
|
+
-------
|
|
101
|
+
float
|
|
102
|
+
Gyroradius in meters.
|
|
103
|
+
|
|
104
|
+
Examples
|
|
105
|
+
--------
|
|
106
|
+
>>> round(larmor_radius(1e5, QE, MP, 1.0), 6)
|
|
107
|
+
0.001044
|
|
108
|
+
"""
|
|
109
|
+
return m * v_perp / (abs(q) * B)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def magnetic_moment(v_perp: float, m: float, B: float) -> float:
|
|
113
|
+
"""The first adiabatic invariant :math:`\\mu = m v_\\perp^2 / (2B)`.
|
|
114
|
+
|
|
115
|
+
Conserved for a charged particle whose gyration is fast compared to
|
|
116
|
+
any change in the field it sees, :math:`\\mu` acts as a magnetic
|
|
117
|
+
"potential energy per unit field": as the particle drifts into
|
|
118
|
+
stronger :math:`B`, :math:`v_\\perp` must grow to keep :math:`\\mu`
|
|
119
|
+
fixed, converting parallel kinetic energy into perpendicular kinetic
|
|
120
|
+
energy. That conversion is the mechanism behind :func:`mirror_force`.
|
|
121
|
+
|
|
122
|
+
Parameters
|
|
123
|
+
----------
|
|
124
|
+
v_perp : float
|
|
125
|
+
Speed perpendicular to the magnetic field, in m/s.
|
|
126
|
+
m : float
|
|
127
|
+
Particle mass in kilograms.
|
|
128
|
+
B : float
|
|
129
|
+
Magnetic field magnitude in Tesla.
|
|
130
|
+
|
|
131
|
+
Returns
|
|
132
|
+
-------
|
|
133
|
+
float
|
|
134
|
+
Magnetic moment in Joules/Tesla.
|
|
135
|
+
|
|
136
|
+
See Also
|
|
137
|
+
--------
|
|
138
|
+
mirror_force : The parallel force derived from this invariant.
|
|
139
|
+
|
|
140
|
+
Examples
|
|
141
|
+
--------
|
|
142
|
+
>>> round(magnetic_moment(1e5, MP, 1.0) * 1e18, 4)
|
|
143
|
+
8.3631
|
|
144
|
+
"""
|
|
145
|
+
return 0.5 * m * v_perp**2 / B
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
@njit(cache=True)
|
|
149
|
+
def _boris_kernel(x, y, z, vx, vy, vz, q, m, Ex, Ey, Ez, Bx, By, Bz, dt):
|
|
150
|
+
qmdt2 = (q / m) * (dt / 2.0)
|
|
151
|
+
vmx = vx + qmdt2 * Ex
|
|
152
|
+
vmy = vy + qmdt2 * Ey
|
|
153
|
+
vmz = vz + qmdt2 * Ez
|
|
154
|
+
tx, ty, tz = qmdt2 * Bx, qmdt2 * By, qmdt2 * Bz
|
|
155
|
+
t2 = tx * tx + ty * ty + tz * tz
|
|
156
|
+
denom = 1.0 + t2
|
|
157
|
+
sx, sy, sz = 2.0 * tx / denom, 2.0 * ty / denom, 2.0 * tz / denom
|
|
158
|
+
vpx = vmx + (vmy * tz - vmz * ty)
|
|
159
|
+
vpy = vmy + (vmz * tx - vmx * tz)
|
|
160
|
+
vpz = vmz + (vmx * ty - vmy * tx)
|
|
161
|
+
vplusx = vmx + (vpy * sz - vpz * sy)
|
|
162
|
+
vplusy = vmy + (vpz * sx - vpx * sz)
|
|
163
|
+
vplusz = vmz + (vpx * sy - vpy * sx)
|
|
164
|
+
vnx = vplusx + qmdt2 * Ex
|
|
165
|
+
vny = vplusy + qmdt2 * Ey
|
|
166
|
+
vnz = vplusz + qmdt2 * Ez
|
|
167
|
+
xn = x + vnx * dt
|
|
168
|
+
yn = y + vny * dt
|
|
169
|
+
zn = z + vnz * dt
|
|
170
|
+
return xn, yn, zn, vnx, vny, vnz
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
@njit(cache=True)
|
|
174
|
+
def _boris_integrate_kernel(x, y, z, vx, vy, vz, q, m, Ex, Ey, Ez, Bx, By, Bz, dt, steps):
|
|
175
|
+
xs = np.empty(steps + 1)
|
|
176
|
+
ys = np.empty(steps + 1)
|
|
177
|
+
zs = np.empty(steps + 1)
|
|
178
|
+
vxs = np.empty(steps + 1)
|
|
179
|
+
vys = np.empty(steps + 1)
|
|
180
|
+
vzs = np.empty(steps + 1)
|
|
181
|
+
xs[0], ys[0], zs[0] = x, y, z
|
|
182
|
+
vxs[0], vys[0], vzs[0] = vx, vy, vz
|
|
183
|
+
for i in range(steps):
|
|
184
|
+
x, y, z, vx, vy, vz = _boris_kernel(x, y, z, vx, vy, vz, q, m, Ex, Ey, Ez, Bx, By, Bz, dt)
|
|
185
|
+
xs[i + 1], ys[i + 1], zs[i + 1] = x, y, z
|
|
186
|
+
vxs[i + 1], vys[i + 1], vzs[i + 1] = vx, vy, vz
|
|
187
|
+
return xs, ys, zs, vxs, vys, vzs
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def boris_push(pos: np.ndarray, vel: np.ndarray, q: float, m: float, E: np.ndarray, B: np.ndarray, dt: float) -> tuple:
|
|
191
|
+
"""Advance a charged particle one leapfrog step with the Boris integrator.
|
|
192
|
+
|
|
193
|
+
The Boris (1970) scheme splits each step into an electric
|
|
194
|
+
half-acceleration, an exact rotation about :math:`\\mathbf{B}` (via the
|
|
195
|
+
Boris "E-cross-B" trick, avoiding any explicit trigonometry), and a
|
|
196
|
+
second electric half-acceleration. The rotation step preserves speed
|
|
197
|
+
exactly in a pure magnetic field, which is why the scheme conserves
|
|
198
|
+
kinetic energy over arbitrarily many gyro-orbits where a naive
|
|
199
|
+
forward-Euler or even RK4 update would spiral outward.
|
|
200
|
+
|
|
201
|
+
Parameters
|
|
202
|
+
----------
|
|
203
|
+
pos : ndarray, shape (3,)
|
|
204
|
+
Particle position :math:`[x, y, z]` in meters.
|
|
205
|
+
vel : ndarray, shape (3,)
|
|
206
|
+
Particle velocity :math:`[v_x, v_y, v_z]` in m/s.
|
|
207
|
+
q : float
|
|
208
|
+
Particle charge in Coulombs.
|
|
209
|
+
m : float
|
|
210
|
+
Particle mass in kilograms.
|
|
211
|
+
E : ndarray, shape (3,)
|
|
212
|
+
Electric field :math:`[E_x, E_y, E_z]` in V/m.
|
|
213
|
+
B : ndarray, shape (3,)
|
|
214
|
+
Magnetic field :math:`[B_x, B_y, B_z]` in Tesla.
|
|
215
|
+
dt : float
|
|
216
|
+
Time step in seconds.
|
|
217
|
+
|
|
218
|
+
Returns
|
|
219
|
+
-------
|
|
220
|
+
pos_new, vel_new : ndarray, shape (3,)
|
|
221
|
+
Position and velocity after one step of size ``dt``.
|
|
222
|
+
|
|
223
|
+
See Also
|
|
224
|
+
--------
|
|
225
|
+
boris_integrate : Repeated application of this step over many time steps.
|
|
226
|
+
|
|
227
|
+
Examples
|
|
228
|
+
--------
|
|
229
|
+
A proton launched perpendicular to a uniform field gyrates without
|
|
230
|
+
gaining or losing speed:
|
|
231
|
+
|
|
232
|
+
>>> import numpy as np
|
|
233
|
+
>>> r0 = np.array([0.0, 0.0, 0.0])
|
|
234
|
+
>>> v0 = np.array([1e5, 0.0, 0.0])
|
|
235
|
+
>>> E = np.zeros(3)
|
|
236
|
+
>>> B = np.array([0.0, 0.0, 1.0])
|
|
237
|
+
>>> r1, v1 = boris_push(r0, v0, q=QE, m=MP, E=E, B=B, dt=1e-9)
|
|
238
|
+
>>> round(float(np.linalg.norm(v1)), 6)
|
|
239
|
+
100000.0
|
|
240
|
+
"""
|
|
241
|
+
xn, yn, zn, vnx, vny, vnz = _boris_kernel(
|
|
242
|
+
float(pos[0]),
|
|
243
|
+
float(pos[1]),
|
|
244
|
+
float(pos[2]),
|
|
245
|
+
float(vel[0]),
|
|
246
|
+
float(vel[1]),
|
|
247
|
+
float(vel[2]),
|
|
248
|
+
float(q),
|
|
249
|
+
float(m),
|
|
250
|
+
float(E[0]),
|
|
251
|
+
float(E[1]),
|
|
252
|
+
float(E[2]),
|
|
253
|
+
float(B[0]),
|
|
254
|
+
float(B[1]),
|
|
255
|
+
float(B[2]),
|
|
256
|
+
float(dt),
|
|
257
|
+
)
|
|
258
|
+
return np.array([xn, yn, zn]), np.array([vnx, vny, vnz])
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def boris_integrate(pos0: np.ndarray, vel0: np.ndarray, q: float, m: float, E: np.ndarray, B: np.ndarray, dt: float, steps: int) -> tuple:
|
|
262
|
+
"""Integrate a charged particle's trajectory in *uniform* E and B fields with the Boris pusher.
|
|
263
|
+
|
|
264
|
+
Repeatedly applies :func:`boris_push` inside a single Numba-compiled
|
|
265
|
+
loop, avoiding Python-level overhead per step -- the throughput this
|
|
266
|
+
buys is what makes the Boris scheme practical for particle-in-cell
|
|
267
|
+
codes tracking millions of particles.
|
|
268
|
+
|
|
269
|
+
Parameters
|
|
270
|
+
----------
|
|
271
|
+
pos0 : ndarray, shape (3,)
|
|
272
|
+
Initial position in meters.
|
|
273
|
+
vel0 : ndarray, shape (3,)
|
|
274
|
+
Initial velocity in m/s.
|
|
275
|
+
q : float
|
|
276
|
+
Particle charge in Coulombs.
|
|
277
|
+
m : float
|
|
278
|
+
Particle mass in kilograms.
|
|
279
|
+
E : ndarray, shape (3,)
|
|
280
|
+
Uniform electric field in V/m.
|
|
281
|
+
B : ndarray, shape (3,)
|
|
282
|
+
Uniform magnetic field in Tesla.
|
|
283
|
+
dt : float
|
|
284
|
+
Time step in seconds.
|
|
285
|
+
steps : int
|
|
286
|
+
Number of steps to advance.
|
|
287
|
+
|
|
288
|
+
Returns
|
|
289
|
+
-------
|
|
290
|
+
pos_hist : ndarray, shape (steps + 1, 3)
|
|
291
|
+
Position at every step, including the initial condition.
|
|
292
|
+
vel_hist : ndarray, shape (steps + 1, 3)
|
|
293
|
+
Velocity at every step, including the initial condition.
|
|
294
|
+
|
|
295
|
+
See Also
|
|
296
|
+
--------
|
|
297
|
+
boris_push : The single-step update repeated here.
|
|
298
|
+
exb_drift : The closed-form drift velocity this trajectory averages to
|
|
299
|
+
when a non-zero E is present.
|
|
300
|
+
|
|
301
|
+
Examples
|
|
302
|
+
--------
|
|
303
|
+
Kinetic energy is conserved to machine precision over many gyrations
|
|
304
|
+
in a pure magnetic field:
|
|
305
|
+
|
|
306
|
+
>>> import numpy as np
|
|
307
|
+
>>> B = np.array([0.0, 0.0, 1.0])
|
|
308
|
+
>>> E = np.zeros(3)
|
|
309
|
+
>>> omega_c = cyclotron_frequency(QE, MP, 1.0)
|
|
310
|
+
>>> dt = (2 * np.pi / omega_c) / 200
|
|
311
|
+
>>> pos_hist, vel_hist = boris_integrate(
|
|
312
|
+
... np.zeros(3), np.array([1e5, 0.0, 0.0]), QE, MP, E, B, dt, steps=2000
|
|
313
|
+
... )
|
|
314
|
+
>>> speeds = np.linalg.norm(vel_hist, axis=1)
|
|
315
|
+
>>> bool(np.max(np.abs(speeds - 1e5)) < 1e-6)
|
|
316
|
+
True
|
|
317
|
+
"""
|
|
318
|
+
xs, ys, zs, vxs, vys, vzs = _boris_integrate_kernel(
|
|
319
|
+
float(pos0[0]),
|
|
320
|
+
float(pos0[1]),
|
|
321
|
+
float(pos0[2]),
|
|
322
|
+
float(vel0[0]),
|
|
323
|
+
float(vel0[1]),
|
|
324
|
+
float(vel0[2]),
|
|
325
|
+
float(q),
|
|
326
|
+
float(m),
|
|
327
|
+
float(E[0]),
|
|
328
|
+
float(E[1]),
|
|
329
|
+
float(E[2]),
|
|
330
|
+
float(B[0]),
|
|
331
|
+
float(B[1]),
|
|
332
|
+
float(B[2]),
|
|
333
|
+
float(dt),
|
|
334
|
+
int(steps),
|
|
335
|
+
)
|
|
336
|
+
pos_hist = np.stack([xs, ys, zs], axis=1)
|
|
337
|
+
vel_hist = np.stack([vxs, vys, vzs], axis=1)
|
|
338
|
+
return pos_hist, vel_hist
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def exb_drift(E: np.ndarray, B: np.ndarray) -> np.ndarray:
|
|
342
|
+
"""The :math:`\\mathbf{E}\\times\\mathbf{B}` drift velocity :math:`\\mathbf{v}_E = (\\mathbf{E}\\times\\mathbf{B})/B^2`.
|
|
343
|
+
|
|
344
|
+
Unlike every other guiding-center drift, this one is independent of
|
|
345
|
+
charge, mass, and energy: electrons and ions drift together at the
|
|
346
|
+
same velocity, so :math:`\\mathbf{E}\\times\\mathbf{B}` drift carries no
|
|
347
|
+
net current and instead advects the whole plasma as a fluid --
|
|
348
|
+
the drift underlying tokamak radial-electric-field rotation and
|
|
349
|
+
magnetospheric convection alike.
|
|
350
|
+
|
|
351
|
+
Parameters
|
|
352
|
+
----------
|
|
353
|
+
E : ndarray, shape (3,)
|
|
354
|
+
Electric field in V/m.
|
|
355
|
+
B : ndarray, shape (3,)
|
|
356
|
+
Magnetic field in Tesla.
|
|
357
|
+
|
|
358
|
+
Returns
|
|
359
|
+
-------
|
|
360
|
+
ndarray, shape (3,)
|
|
361
|
+
Drift velocity in m/s.
|
|
362
|
+
|
|
363
|
+
Examples
|
|
364
|
+
--------
|
|
365
|
+
>>> import numpy as np
|
|
366
|
+
>>> E = np.array([0.0, 1e3, 0.0])
|
|
367
|
+
>>> B = np.array([0.0, 0.0, 1.0])
|
|
368
|
+
>>> exb_drift(E, B)
|
|
369
|
+
array([1000., 0., 0.])
|
|
370
|
+
"""
|
|
371
|
+
return np.cross(E, B) / np.dot(B, B)
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def grad_b_drift(v_perp: float, q: float, m: float, B: np.ndarray, grad_B: np.ndarray) -> np.ndarray:
|
|
375
|
+
"""The grad-B drift :math:`\\mathbf{v}_{\\nabla B} = \\dfrac{m v_\\perp^2}{2qB^3}(\\mathbf{B}\\times\\nabla B)`.
|
|
376
|
+
|
|
377
|
+
A particle gyrating in a field whose magnitude varies across the
|
|
378
|
+
orbit sees a tighter turn (smaller Larmor radius) on the strong-field
|
|
379
|
+
side than the weak-field side, so the orbit fails to close and the
|
|
380
|
+
guiding center creeps sideways. Because the drift is inversely
|
|
381
|
+
proportional to charge, electrons and ions drift in *opposite*
|
|
382
|
+
directions, producing a net current -- the origin of the ring current
|
|
383
|
+
in planetary magnetospheres.
|
|
384
|
+
|
|
385
|
+
Parameters
|
|
386
|
+
----------
|
|
387
|
+
v_perp : float
|
|
388
|
+
Speed perpendicular to the field, in m/s.
|
|
389
|
+
q : float
|
|
390
|
+
Particle charge in Coulombs (signed).
|
|
391
|
+
m : float
|
|
392
|
+
Particle mass in kilograms.
|
|
393
|
+
B : ndarray, shape (3,)
|
|
394
|
+
Local magnetic field vector in Tesla.
|
|
395
|
+
grad_B : ndarray, shape (3,)
|
|
396
|
+
Gradient of the field magnitude, :math:`\\nabla |\\mathbf{B}|`, in Tesla/meter.
|
|
397
|
+
|
|
398
|
+
Returns
|
|
399
|
+
-------
|
|
400
|
+
ndarray, shape (3,)
|
|
401
|
+
Drift velocity in m/s.
|
|
402
|
+
|
|
403
|
+
See Also
|
|
404
|
+
--------
|
|
405
|
+
curvature_drift : The companion drift from field-line curvature,
|
|
406
|
+
which combines with this one in any real toroidal field.
|
|
407
|
+
|
|
408
|
+
Examples
|
|
409
|
+
--------
|
|
410
|
+
>>> import numpy as np
|
|
411
|
+
>>> B = np.array([0.0, 0.0, 1.0])
|
|
412
|
+
>>> grad_B = np.array([0.1, 0.0, 0.0])
|
|
413
|
+
>>> drift = grad_b_drift(1e5, QE, MP, B, grad_B)
|
|
414
|
+
>>> round(float(drift[1]), 4)
|
|
415
|
+
5.2198
|
|
416
|
+
"""
|
|
417
|
+
Bmag = np.linalg.norm(B)
|
|
418
|
+
return (m * v_perp**2) / (2.0 * q * Bmag**3) * np.cross(B, grad_B)
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
def curvature_drift(v_par: float, q: float, m: float, B: np.ndarray, R_c: np.ndarray) -> np.ndarray:
|
|
422
|
+
"""The curvature drift :math:`\\mathbf{v}_R = \\dfrac{m v_\\parallel^2}{q}\\dfrac{\\mathbf{R}_c\\times\\mathbf{B}}{R_c^2 B^2}`.
|
|
423
|
+
|
|
424
|
+
A particle streaming along a curved field line feels a centrifugal
|
|
425
|
+
force in the guiding-center frame, which -- crossed with
|
|
426
|
+
:math:`\\mathbf{B}` -- produces a drift perpendicular to the plane of
|
|
427
|
+
curvature. In a low-beta toroidal equilibrium this combines with
|
|
428
|
+
:func:`grad_b_drift` (curvature and field-strength gradients point the
|
|
429
|
+
same way when :math:`\\nabla\\times\\mathbf{B}=0` in vacuum) into the
|
|
430
|
+
single :math:`\\nabla B` + curvature drift responsible for charge
|
|
431
|
+
separation and the resulting E-cross-B rotation in a tokamak.
|
|
432
|
+
|
|
433
|
+
Parameters
|
|
434
|
+
----------
|
|
435
|
+
v_par : float
|
|
436
|
+
Speed parallel to the field, in m/s.
|
|
437
|
+
q : float
|
|
438
|
+
Particle charge in Coulombs (signed).
|
|
439
|
+
m : float
|
|
440
|
+
Particle mass in kilograms.
|
|
441
|
+
B : ndarray, shape (3,)
|
|
442
|
+
Local magnetic field vector in Tesla.
|
|
443
|
+
R_c : ndarray, shape (3,)
|
|
444
|
+
Radius-of-curvature vector, pointing from the field line's local
|
|
445
|
+
center of curvature to the particle, magnitude :math:`R_c` in meters.
|
|
446
|
+
|
|
447
|
+
Returns
|
|
448
|
+
-------
|
|
449
|
+
ndarray, shape (3,)
|
|
450
|
+
Drift velocity in m/s.
|
|
451
|
+
|
|
452
|
+
Examples
|
|
453
|
+
--------
|
|
454
|
+
>>> import numpy as np
|
|
455
|
+
>>> B = np.array([0.0, 0.0, 1.0])
|
|
456
|
+
>>> R_c = np.array([1.0, 0.0, 0.0])
|
|
457
|
+
>>> drift = curvature_drift(1e5, QE, MP, B, R_c)
|
|
458
|
+
>>> round(float(drift[1]), 2)
|
|
459
|
+
-104.4
|
|
460
|
+
"""
|
|
461
|
+
Bmag = np.linalg.norm(B)
|
|
462
|
+
Rc2 = np.dot(R_c, R_c)
|
|
463
|
+
return (m * v_par**2 / q) * np.cross(R_c, B) / (Rc2 * Bmag**2)
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
def mirror_force(mu: float, grad_B_parallel: float) -> float:
|
|
467
|
+
"""The mirror force :math:`F_\\parallel = -\\mu\\, \\partial B/\\partial \\ell` along a field line.
|
|
468
|
+
|
|
469
|
+
As a particle's guiding center moves into a region of stronger field
|
|
470
|
+
(larger :math:`\\partial B/\\partial\\ell`), the conservation of
|
|
471
|
+
:func:`magnetic_moment` forces :math:`v_\\perp` to grow at the expense
|
|
472
|
+
of :math:`v_\\parallel`; this is the reaction force decelerating the
|
|
473
|
+
parallel motion. If the field is strong enough, :math:`v_\\parallel`
|
|
474
|
+
reaches zero before the particle passes the throat and it reflects --
|
|
475
|
+
magnetic mirror confinement, simulated end to end in
|
|
476
|
+
:func:`magnetic_mirror_bounce`.
|
|
477
|
+
|
|
478
|
+
Parameters
|
|
479
|
+
----------
|
|
480
|
+
mu : float
|
|
481
|
+
Magnetic moment in J/T, from :func:`magnetic_moment`.
|
|
482
|
+
grad_B_parallel : float
|
|
483
|
+
Gradient of the field magnitude along the field line, in Tesla/meter.
|
|
484
|
+
|
|
485
|
+
Returns
|
|
486
|
+
-------
|
|
487
|
+
float
|
|
488
|
+
Force in Newtons, directed to push the particle toward weaker field.
|
|
489
|
+
|
|
490
|
+
Examples
|
|
491
|
+
--------
|
|
492
|
+
>>> mirror_force(mu=1e-17, grad_B_parallel=2.0)
|
|
493
|
+
-2e-17
|
|
494
|
+
"""
|
|
495
|
+
return -mu * grad_B_parallel
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
def magnetic_mirror_bounce(z0: float, v_par0: float, v_perp0: float, m: float, B_func, dz: float = 1e-6, dt: float = 1e-10, steps: int = 20000) -> tuple:
|
|
499
|
+
"""Simulate 1D guiding-center bounce motion between the throats of a magnetic mirror.
|
|
500
|
+
|
|
501
|
+
Integrates :math:`m\\dot{v}_\\parallel = -\\mu\\, dB/dz` with :math:`\\mu`
|
|
502
|
+
fixed at its initial value (the adiabatic invariant), using a
|
|
503
|
+
symmetric leapfrog step. A particle with too little pitch angle to
|
|
504
|
+
reflect before reaching the mirror throat's peak field instead falls
|
|
505
|
+
into the *loss cone* and would be lost from confinement in a real
|
|
506
|
+
device; :func:`mirror_force` supplies the underlying force law.
|
|
507
|
+
|
|
508
|
+
Parameters
|
|
509
|
+
----------
|
|
510
|
+
z0 : float
|
|
511
|
+
Initial position along the field line, in meters.
|
|
512
|
+
v_par0 : float
|
|
513
|
+
Initial parallel velocity, in m/s.
|
|
514
|
+
v_perp0 : float
|
|
515
|
+
Initial perpendicular velocity, in m/s (sets :math:`\\mu` via :func:`magnetic_moment`).
|
|
516
|
+
m : float
|
|
517
|
+
Particle mass in kilograms.
|
|
518
|
+
B_func : callable
|
|
519
|
+
Field-strength profile ``B_func(z) -> float`` along the field line, in Tesla.
|
|
520
|
+
dz : float, default=1e-6
|
|
521
|
+
Finite-difference step used to evaluate :math:`dB/dz`, in meters.
|
|
522
|
+
dt : float, default=1e-10
|
|
523
|
+
Time step in seconds.
|
|
524
|
+
steps : int, default=20000
|
|
525
|
+
Number of leapfrog steps to advance.
|
|
526
|
+
|
|
527
|
+
Returns
|
|
528
|
+
-------
|
|
529
|
+
z_hist : ndarray, shape (steps + 1,)
|
|
530
|
+
Position along the field line at every step.
|
|
531
|
+
v_par_hist : ndarray, shape (steps + 1,)
|
|
532
|
+
Parallel velocity at every step.
|
|
533
|
+
|
|
534
|
+
Examples
|
|
535
|
+
--------
|
|
536
|
+
A particle launched from the mirror midplane with enough perpendicular
|
|
537
|
+
energy reflects before reaching the throat, reversing the sign of its
|
|
538
|
+
parallel velocity:
|
|
539
|
+
|
|
540
|
+
>>> import numpy as np
|
|
541
|
+
>>> B_func = lambda z: 1.0 + 4.0 * (z / 0.05) ** 2
|
|
542
|
+
>>> z_hist, v_par_hist = magnetic_mirror_bounce(
|
|
543
|
+
... z0=0.0, v_par0=2e4, v_perp0=8e4, m=MP, B_func=B_func, steps=6000
|
|
544
|
+
... )
|
|
545
|
+
>>> bool(v_par_hist[0] > 0 and v_par_hist[-1] < 0)
|
|
546
|
+
True
|
|
547
|
+
"""
|
|
548
|
+
z = z0
|
|
549
|
+
v_par = v_par0
|
|
550
|
+
B0 = B_func(z0)
|
|
551
|
+
mu = magnetic_moment(v_perp0, m, B0)
|
|
552
|
+
z_hist = np.empty(steps + 1)
|
|
553
|
+
v_par_hist = np.empty(steps + 1)
|
|
554
|
+
z_hist[0], v_par_hist[0] = z, v_par
|
|
555
|
+
for i in range(steps):
|
|
556
|
+
grad_B = (B_func(z + dz) - B_func(z - dz)) / (2.0 * dz)
|
|
557
|
+
a = mirror_force(mu, grad_B) / m
|
|
558
|
+
v_par = v_par + a * dt
|
|
559
|
+
z = z + v_par * dt
|
|
560
|
+
z_hist[i + 1] = z
|
|
561
|
+
v_par_hist[i + 1] = v_par
|
|
562
|
+
return z_hist, v_par_hist
|
|
File without changes
|