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,439 @@
|
|
|
1
|
+
"""Physics-correctness tests for physicskit.fields."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
import pytest
|
|
7
|
+
from scipy.signal import find_peaks
|
|
8
|
+
|
|
9
|
+
from physicskit.fields.electrodynamics import (
|
|
10
|
+
C0,
|
|
11
|
+
courant_limit_1d,
|
|
12
|
+
courant_limit_2d,
|
|
13
|
+
dielectric_slab,
|
|
14
|
+
fdtd_1d,
|
|
15
|
+
fdtd_2d_tmz,
|
|
16
|
+
fdtd_2d_tmz_evolve,
|
|
17
|
+
flux_tube_field_1d,
|
|
18
|
+
oscillating_dipole_source,
|
|
19
|
+
pml_conductivity_profile_2d,
|
|
20
|
+
poynting_vector_tmz,
|
|
21
|
+
tmz_cavity_mode,
|
|
22
|
+
)
|
|
23
|
+
from physicskit.fields.quantum_fields import (
|
|
24
|
+
casimir_energy_1d,
|
|
25
|
+
casimir_mode_frequencies,
|
|
26
|
+
count_vortices,
|
|
27
|
+
gpe_energy,
|
|
28
|
+
gpe_evolve,
|
|
29
|
+
gpe_imprint_vortex,
|
|
30
|
+
gpe_relax,
|
|
31
|
+
harmonic_trap_grid,
|
|
32
|
+
)
|
|
33
|
+
from physicskit.fields.solitons import (
|
|
34
|
+
kdv_evolve,
|
|
35
|
+
kdv_evolve_frames,
|
|
36
|
+
kdv_soliton,
|
|
37
|
+
nls_bright_soliton,
|
|
38
|
+
nls_dark_soliton,
|
|
39
|
+
nls_evolve,
|
|
40
|
+
nls_evolve_frames,
|
|
41
|
+
sine_gordon_evolve,
|
|
42
|
+
sine_gordon_evolve_frames,
|
|
43
|
+
sine_gordon_kink,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class TestFDTDWaveSpeed:
|
|
48
|
+
def test_1d_pulse_propagates_at_c(self):
|
|
49
|
+
N, dx = 800, 1e-3
|
|
50
|
+
dt = 0.99 * courant_limit_1d(dx)
|
|
51
|
+
eta0 = np.sqrt(4 * np.pi * 1e-7 / 8.8541878128e-12)
|
|
52
|
+
x0, sigma_pulse = 100, 25
|
|
53
|
+
Ez0 = np.exp(-((np.arange(N) - x0) ** 2) / (2 * sigma_pulse**2))
|
|
54
|
+
xh = np.arange(N - 1) + 0.5
|
|
55
|
+
Hy0 = np.exp(-((xh - x0) ** 2) / (2 * sigma_pulse**2)) / eta0
|
|
56
|
+
eps_r, mu_r = np.ones(N), np.ones(N)
|
|
57
|
+
steps = 400
|
|
58
|
+
Ez, _ = fdtd_1d(Ez0, Hy0, eps_r, mu_r, steps=steps, dt=dt, dx=dx)
|
|
59
|
+
peak = np.argmax(Ez)
|
|
60
|
+
measured_speed = (peak - x0) * dx / (steps * dt)
|
|
61
|
+
assert measured_speed == pytest.approx(C0, rel=0.01)
|
|
62
|
+
|
|
63
|
+
def test_1d_lossy_medium_damps_the_pulse_more_than_lossless(self):
|
|
64
|
+
N, dx = 200, 1e-3
|
|
65
|
+
dt = 0.99 * courant_limit_1d(dx)
|
|
66
|
+
x0, sigma_pulse = 60, 15
|
|
67
|
+
Ez0 = np.exp(-((np.arange(N) - x0) ** 2) / (2 * sigma_pulse**2))
|
|
68
|
+
Hy0 = np.zeros(N - 1)
|
|
69
|
+
eps_r, mu_r = np.ones(N), np.ones(N)
|
|
70
|
+
steps = 100
|
|
71
|
+
Ez_lossless, _ = fdtd_1d(Ez0, Hy0, eps_r, mu_r, steps=steps, dt=dt, dx=dx)
|
|
72
|
+
Ez_lossy, _ = fdtd_1d(Ez0, Hy0, eps_r, mu_r, steps=steps, dt=dt, dx=dx, sigma=np.full(N, 5.0))
|
|
73
|
+
assert np.max(np.abs(Ez_lossy)) < np.max(np.abs(Ez_lossless))
|
|
74
|
+
|
|
75
|
+
def test_2d_tmz_runs_and_conserves_shape(self):
|
|
76
|
+
Nx, Ny = 40, 40
|
|
77
|
+
Ez0 = np.zeros((Nx, Ny))
|
|
78
|
+
Ez0[Nx // 2, Ny // 2] = 1.0
|
|
79
|
+
Hx0, Hy0 = np.zeros((Nx, Ny)), np.zeros((Nx, Ny))
|
|
80
|
+
eps_r, mu_r = np.ones((Nx, Ny)), np.ones((Nx, Ny))
|
|
81
|
+
dx = dy = 1e-3
|
|
82
|
+
dt = 0.5 * courant_limit_2d(dx, dy)
|
|
83
|
+
Ez, _, _ = fdtd_2d_tmz(Ez0, Hx0, Hy0, eps_r, mu_r, steps=15, dt=dt, dx=dx, dy=dy)
|
|
84
|
+
assert Ez.shape == (Nx, Ny)
|
|
85
|
+
assert np.all(np.isfinite(Ez))
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class TestKdVSolitonCollision:
|
|
89
|
+
def test_two_soliton_collision_is_elastic(self):
|
|
90
|
+
N, L = 512, 60.0
|
|
91
|
+
x = np.linspace(-L / 2, L / 2, N, endpoint=False)
|
|
92
|
+
c_fast, c_slow = 9.0, 4.0
|
|
93
|
+
u0 = kdv_soliton(x, c_fast, x0=-20) + kdv_soliton(x, c_slow, x0=-8)
|
|
94
|
+
u = kdv_evolve(u0, x, dt=0.0005, steps=6000)
|
|
95
|
+
peaks, _ = find_peaks(u, height=1.0)
|
|
96
|
+
heights = sorted(u[peaks])
|
|
97
|
+
assert len(heights) == 2
|
|
98
|
+
assert heights[0] == pytest.approx(c_slow / 2, abs=0.02)
|
|
99
|
+
assert heights[1] == pytest.approx(c_fast / 2, abs=0.02)
|
|
100
|
+
|
|
101
|
+
def test_mass_is_conserved(self):
|
|
102
|
+
N, L = 512, 60.0
|
|
103
|
+
x = np.linspace(-L / 2, L / 2, N, endpoint=False)
|
|
104
|
+
dx = x[1] - x[0]
|
|
105
|
+
u0 = kdv_soliton(x, 9.0, x0=-20) + kdv_soliton(x, 4.0, x0=-8)
|
|
106
|
+
u = kdv_evolve(u0, x, dt=0.0005, steps=6000)
|
|
107
|
+
assert np.sum(u) * dx == pytest.approx(np.sum(u0) * dx, rel=1e-6)
|
|
108
|
+
|
|
109
|
+
def test_single_soliton_shape_preserved(self):
|
|
110
|
+
N, L = 512, 60.0
|
|
111
|
+
x = np.linspace(-L / 2, L / 2, N, endpoint=False)
|
|
112
|
+
c = 4.0
|
|
113
|
+
u0 = kdv_soliton(x, c, x0=-15)
|
|
114
|
+
steps, dt = 2000, 0.001
|
|
115
|
+
u = kdv_evolve(u0, x, dt=dt, steps=steps)
|
|
116
|
+
expected = kdv_soliton(x, c, x0=-15 + c * dt * steps)
|
|
117
|
+
assert np.max(np.abs(u - expected)) < 1e-3
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class TestGPEVortexLattice:
|
|
121
|
+
def test_imaginary_time_energy_decreases_monotonically(self):
|
|
122
|
+
n, length, g = 48, 12.0, 4.0
|
|
123
|
+
X, Y, _, _, K2 = harmonic_trap_grid(n, length)
|
|
124
|
+
V = 0.5 * (X**2 + Y**2)
|
|
125
|
+
psi = np.exp(-0.5 * (X**2 + Y**2)).astype(complex)
|
|
126
|
+
energies = []
|
|
127
|
+
for _ in range(8):
|
|
128
|
+
psi = gpe_relax(psi, V, g, dtau=5e-4, steps=250, X=X, Y=Y, K2=K2)
|
|
129
|
+
energies.append(gpe_energy(psi, V, g, X, Y, K2)["total"])
|
|
130
|
+
assert all(energies[i + 1] <= energies[i] + 1e-9 for i in range(len(energies) - 1))
|
|
131
|
+
|
|
132
|
+
def test_vortex_state_is_a_stationary_solution_with_quantized_circulation(self):
|
|
133
|
+
# Seeded slightly off a grid vertex: a core sitting exactly on a lattice
|
|
134
|
+
# site makes the discrete plaquette circulation ambiguous (it's shared
|
|
135
|
+
# between four adjacent plaquettes), which is a detection subtlety, not
|
|
136
|
+
# a physical effect.
|
|
137
|
+
n, length, g = 48, 10.0, 4.0
|
|
138
|
+
X, Y, _, _, K2 = harmonic_trap_grid(n, length)
|
|
139
|
+
V = 0.5 * (X**2 + Y**2)
|
|
140
|
+
psi0 = gpe_imprint_vortex(np.exp(-0.5 * (X**2 + Y**2)).astype(complex), X, Y, [(0.03, 0.02)])
|
|
141
|
+
psi = gpe_relax(psi0, V, g, dtau=5e-4, steps=4000, X=X, Y=Y, K2=K2)
|
|
142
|
+
winding = count_vortices(psi)
|
|
143
|
+
assert np.sum(np.abs(winding)) == 1
|
|
144
|
+
# a nonzero winding number topologically requires a density zero somewhere.
|
|
145
|
+
assert np.min(np.abs(psi) ** 2) < 0.01 * np.max(np.abs(psi) ** 2)
|
|
146
|
+
|
|
147
|
+
def test_rotation_favors_the_vortex_state_above_critical_frequency(self):
|
|
148
|
+
n, length, g = 64, 12.0, 4.0
|
|
149
|
+
X, Y, _, _, K2 = harmonic_trap_grid(n, length)
|
|
150
|
+
V = 0.5 * (X**2 + Y**2)
|
|
151
|
+
psi_vf = gpe_relax(np.exp(-0.5 * (X**2 + Y**2)).astype(complex), V, g, dtau=5e-4, steps=4000, X=X, Y=Y, K2=K2)
|
|
152
|
+
psi0_v = gpe_imprint_vortex(np.exp(-0.5 * (X**2 + Y**2)).astype(complex), X, Y, [(0.0, 0.0)])
|
|
153
|
+
psi_v = gpe_relax(psi0_v, V, g, dtau=5e-4, steps=4000, X=X, Y=Y, K2=K2)
|
|
154
|
+
E0 = gpe_energy(psi_vf, V, g, X, Y, K2)
|
|
155
|
+
E1 = gpe_energy(psi_v, V, g, X, Y, K2)
|
|
156
|
+
omega_c = (E1["total"] - E0["total"]) / (E1["angular_momentum"] - E0["angular_momentum"])
|
|
157
|
+
assert 0.0 < omega_c < 1.0
|
|
158
|
+
# below Omega_c the vortex-free state has lower rotating-frame energy; above, the vortex does.
|
|
159
|
+
below = E0["total"] - 0.5 * omega_c * E0["angular_momentum"] < E1["total"] - 0.5 * omega_c * E1["angular_momentum"]
|
|
160
|
+
above = E1["total"] - 1.5 * omega_c * E1["angular_momentum"] < E0["total"] - 1.5 * omega_c * E0["angular_momentum"]
|
|
161
|
+
assert below and above
|
|
162
|
+
|
|
163
|
+
def test_relax_with_nonzero_omega_stays_normalized(self):
|
|
164
|
+
n, length, g = 24, 10.0, 1.0
|
|
165
|
+
X, Y, _, _, K2 = harmonic_trap_grid(n, length)
|
|
166
|
+
V = 0.5 * (X**2 + Y**2)
|
|
167
|
+
psi0 = np.exp(-0.5 * (X**2 + Y**2)).astype(complex)
|
|
168
|
+
psi = gpe_relax(psi0, V, g, dtau=5e-4, steps=5, X=X, Y=Y, K2=K2, Omega=0.5)
|
|
169
|
+
dx = X[1, 0] - X[0, 0]
|
|
170
|
+
norm = np.sum(np.abs(psi) ** 2) * dx * dx
|
|
171
|
+
assert norm == pytest.approx(1.0, rel=1e-6)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
class TestNLSSolitons:
|
|
175
|
+
def test_bright_soliton_does_not_disperse(self):
|
|
176
|
+
N, L = 1024, 80.0
|
|
177
|
+
x = np.linspace(-L / 2, L / 2, N, endpoint=False)
|
|
178
|
+
psi0 = nls_bright_soliton(x, t=0.0, A=1.0)
|
|
179
|
+
psi = nls_evolve(psi0, x, dt=0.001, steps=2000, g=1.0)
|
|
180
|
+
assert np.max(np.abs(np.abs(psi) - np.abs(psi0))) < 1e-3
|
|
181
|
+
|
|
182
|
+
def test_norm_conserved(self):
|
|
183
|
+
N, L = 1024, 80.0
|
|
184
|
+
x = np.linspace(-L / 2, L / 2, N, endpoint=False)
|
|
185
|
+
dx = x[1] - x[0]
|
|
186
|
+
psi0 = nls_bright_soliton(x, t=0.0, A=1.0)
|
|
187
|
+
psi = nls_evolve(psi0, x, dt=0.001, steps=2000, g=1.0)
|
|
188
|
+
assert np.sum(np.abs(psi) ** 2) * dx == pytest.approx(np.sum(np.abs(psi0) ** 2) * dx, rel=1e-9)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def test_poynting_vector_tmz_matches_cross_product_formula():
|
|
192
|
+
# S = E x H with E=(0,0,Ez), H=(Hx,Hy,0) gives (-Ez*Hy, Ez*Hx, 0).
|
|
193
|
+
Ez = np.array([[2.0]])
|
|
194
|
+
Hx = np.array([[3.0]])
|
|
195
|
+
Hy = np.array([[5.0]])
|
|
196
|
+
Sx, Sy = poynting_vector_tmz(Ez, Hx, Hy)
|
|
197
|
+
np.testing.assert_allclose(Sx, [[-10.0]])
|
|
198
|
+
np.testing.assert_allclose(Sy, [[6.0]])
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
class TestNLSDarkSoliton:
|
|
202
|
+
def test_density_vanishes_at_center(self):
|
|
203
|
+
x = np.array([0.0])
|
|
204
|
+
assert abs(nls_dark_soliton(x, t=0.0, rho0=1.0))[0] == pytest.approx(0.0, abs=1e-12)
|
|
205
|
+
|
|
206
|
+
def test_density_approaches_background_far_from_center(self):
|
|
207
|
+
rho0 = 2.0
|
|
208
|
+
x = np.array([50.0, -50.0])
|
|
209
|
+
np.testing.assert_allclose(np.abs(nls_dark_soliton(x, t=0.0, rho0=rho0)), np.sqrt(rho0), atol=1e-6)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
class TestSineGordonKink:
|
|
213
|
+
def test_kink_propagates_at_prescribed_velocity(self):
|
|
214
|
+
N, L, v, x0 = 4000, 200.0, 0.5, -50.0
|
|
215
|
+
x = np.linspace(-L / 2, L / 2, N)
|
|
216
|
+
dx = x[1] - x[0]
|
|
217
|
+
dt = 0.4 * dx
|
|
218
|
+
u_prev = sine_gordon_kink(x, -dt, v, x0)
|
|
219
|
+
u0 = sine_gordon_kink(x, 0.0, v, x0)
|
|
220
|
+
steps = 1000
|
|
221
|
+
u, _ = sine_gordon_evolve(u0, u_prev, x, dt, steps)
|
|
222
|
+
expected = sine_gordon_kink(x, steps * dt, v, x0)
|
|
223
|
+
assert np.max(np.abs(u - expected)) < 0.01
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
class TestEvolveFramesChaining:
|
|
227
|
+
"""The *_evolve_frames wrappers must reproduce plain repeated evolve() calls exactly:
|
|
228
|
+
they are pure chunking/bookkeeping around the existing, already-tested physics."""
|
|
229
|
+
|
|
230
|
+
def test_kdv_frames_match_direct_evolve(self):
|
|
231
|
+
N, L, c = 256, 40.0, 4.0
|
|
232
|
+
x = np.linspace(-L / 2, L / 2, N, endpoint=False)
|
|
233
|
+
u0 = kdv_soliton(x, c, x0=-10)
|
|
234
|
+
dt, steps_per_frame, n_frames = 0.001, 100, 4
|
|
235
|
+
frames, times = kdv_evolve_frames(u0, x, dt, steps_per_frame, n_frames)
|
|
236
|
+
assert frames.shape == (n_frames + 1, N)
|
|
237
|
+
direct = kdv_evolve(u0, x, dt, steps_per_frame * n_frames)
|
|
238
|
+
assert np.max(np.abs(frames[-1] - direct)) < 1e-10
|
|
239
|
+
assert times[-1] == pytest.approx(steps_per_frame * n_frames * dt)
|
|
240
|
+
|
|
241
|
+
def test_nls_frames_match_direct_evolve(self):
|
|
242
|
+
N, L = 256, 40.0
|
|
243
|
+
x = np.linspace(-L / 2, L / 2, N, endpoint=False)
|
|
244
|
+
psi0 = nls_bright_soliton(x, t=0.0, A=1.0)
|
|
245
|
+
dt, steps_per_frame, n_frames = 0.001, 100, 4
|
|
246
|
+
frames, times = nls_evolve_frames(psi0, x, dt, steps_per_frame, n_frames, g=1.0)
|
|
247
|
+
assert frames.shape == (n_frames + 1, N)
|
|
248
|
+
direct = nls_evolve(psi0, x, dt, steps_per_frame * n_frames, g=1.0)
|
|
249
|
+
assert np.max(np.abs(frames[-1] - direct)) < 1e-10
|
|
250
|
+
|
|
251
|
+
def test_sine_gordon_frames_match_direct_evolve(self):
|
|
252
|
+
N, L, v, x0 = 800, 100.0, 0.4, -20.0
|
|
253
|
+
x = np.linspace(-L / 2, L / 2, N)
|
|
254
|
+
dt = 0.4 * (x[1] - x[0])
|
|
255
|
+
u_prev = sine_gordon_kink(x, -dt, v, x0)
|
|
256
|
+
u0 = sine_gordon_kink(x, 0.0, v, x0)
|
|
257
|
+
steps_per_frame, n_frames = 50, 4
|
|
258
|
+
frames, times = sine_gordon_evolve_frames(u0, u_prev, x, dt, steps_per_frame, n_frames)
|
|
259
|
+
assert frames.shape == (n_frames + 1, N)
|
|
260
|
+
direct, _ = sine_gordon_evolve(u0, u_prev, x, dt, steps_per_frame * n_frames)
|
|
261
|
+
assert np.max(np.abs(frames[-1] - direct)) < 1e-10
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
class TestDipoleRadiationCausality:
|
|
265
|
+
def test_wavefront_respects_causality(self):
|
|
266
|
+
"""An oscillating dipole source can't be felt anywhere before the wavefront,
|
|
267
|
+
traveling at c0, has had time to arrive -- the basic physical signature of radiation."""
|
|
268
|
+
N = 120
|
|
269
|
+
dx = dy = 2e-3
|
|
270
|
+
dt = 0.5 * courant_limit_2d(dx, dy)
|
|
271
|
+
eps_r = mu_r = np.ones((N, N))
|
|
272
|
+
Ez0 = Hx0 = Hy0 = np.zeros((N, N))
|
|
273
|
+
sigma = pml_conductivity_profile_2d((N, N), pml_width=15, dx=dx, dy=dy)
|
|
274
|
+
source = oscillating_dipole_source(N // 2, N // 2, amplitude=1.0, freq=3e10)
|
|
275
|
+
r_near, r_far = 10, 40
|
|
276
|
+
snaps, times = fdtd_2d_tmz_evolve(Ez0, Hx0, Hy0, eps_r, mu_r, steps=4000, dt=dt, dx=dx, dy=dy, sigma=sigma, source=source, snapshot_stride=20)
|
|
277
|
+
near_vals = snaps[:, N // 2 + r_near, N // 2]
|
|
278
|
+
far_vals = snaps[:, N // 2 + r_far, N // 2]
|
|
279
|
+
t_near, t_far = r_near * dx / C0, r_far * dx / C0
|
|
280
|
+
idx_near = np.searchsorted(times, t_near * 0.5)
|
|
281
|
+
idx_far = np.searchsorted(times, t_far * 0.5)
|
|
282
|
+
assert idx_near > 0 and idx_far > idx_near # sanity: the test windows are non-trivial
|
|
283
|
+
assert np.max(np.abs(near_vals[:idx_near])) == 0.0
|
|
284
|
+
assert np.max(np.abs(far_vals[:idx_far])) == 0.0
|
|
285
|
+
# after enough time for the wavefront to pass both probes, the field is excited at both
|
|
286
|
+
assert np.max(np.abs(near_vals)) > 0.0
|
|
287
|
+
assert np.max(np.abs(far_vals)) > 0.0
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
class TestDielectricSlabReflection:
|
|
291
|
+
def test_normal_incidence_reflection_matches_fresnel(self):
|
|
292
|
+
"""Uses the existing (already-tested) fdtd_1d, which already supports a spatially
|
|
293
|
+
varying eps_r -- the same physics fdtd_2d_tmz uses for a dielectric-slab demo,
|
|
294
|
+
checked here against the analytic Fresnel reflection coefficient."""
|
|
295
|
+
N, dx = 3000, 1e-3
|
|
296
|
+
dt = 0.99 * courant_limit_1d(dx)
|
|
297
|
+
eta0 = np.sqrt(4 * np.pi * 1e-7 / 8.8541878128e-12)
|
|
298
|
+
x0, sigma_pulse = 500, 20
|
|
299
|
+
xs = np.arange(N)
|
|
300
|
+
Ez0 = np.exp(-((xs - x0) ** 2) / (2 * sigma_pulse**2))
|
|
301
|
+
xh = np.arange(N - 1) + 0.5
|
|
302
|
+
Hy0 = np.exp(-((xh - x0) ** 2) / (2 * sigma_pulse**2)) / eta0
|
|
303
|
+
eps_r_slab = 4.0
|
|
304
|
+
eps_r = np.ones(N)
|
|
305
|
+
eps_r[1500:] = eps_r_slab
|
|
306
|
+
mu_r = np.ones(N)
|
|
307
|
+
Ez, _ = fdtd_1d(Ez0, Hy0, eps_r, mu_r, steps=2400, dt=dt, dx=dx)
|
|
308
|
+
R_measured = np.max(np.abs(Ez[:400]))
|
|
309
|
+
n1, n2 = 1.0, np.sqrt(eps_r_slab)
|
|
310
|
+
R_analytic = abs((n1 - n2) / (n1 + n2))
|
|
311
|
+
assert R_measured == pytest.approx(R_analytic, rel=0.02)
|
|
312
|
+
|
|
313
|
+
def test_dielectric_slab_map_values(self):
|
|
314
|
+
eps_r = dielectric_slab((40, 20), i_start=15, i_end=25, eps_r_slab=4.0)
|
|
315
|
+
assert eps_r[10, 5] == pytest.approx(1.0)
|
|
316
|
+
assert eps_r[20, 5] == pytest.approx(4.0)
|
|
317
|
+
assert eps_r[30, 5] == pytest.approx(1.0)
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
class TestCavityStandingWaveMode:
|
|
321
|
+
def test_seeded_mode_oscillates_at_analytic_frequency_with_pec_walls(self):
|
|
322
|
+
N = 41
|
|
323
|
+
dx = dy = 1e-3
|
|
324
|
+
dt = 0.5 * courant_limit_2d(dx, dy)
|
|
325
|
+
Ez0, omega = tmz_cavity_mode((N, N), dx, dy, m=1, n=1)
|
|
326
|
+
Hx0 = Hy0 = np.zeros((N, N))
|
|
327
|
+
eps_r = mu_r = np.ones((N, N))
|
|
328
|
+
period = 2 * np.pi / omega
|
|
329
|
+
steps = int(3 * period / dt)
|
|
330
|
+
snaps, times = fdtd_2d_tmz_evolve(Ez0, Hx0, Hy0, eps_r, mu_r, steps=steps, dt=dt, dx=dx, dy=dy, snapshot_stride=1)
|
|
331
|
+
assert np.max(np.abs(snaps[:, 0, :])) == 0.0 # PEC walls: exactly zero, not just small
|
|
332
|
+
assert np.max(np.abs(snaps[:, -1, :])) == 0.0
|
|
333
|
+
probe = snaps[:, N // 2, N // 2]
|
|
334
|
+
spectrum = np.abs(np.fft.rfft(probe - probe.mean()))
|
|
335
|
+
freqs = np.fft.rfftfreq(len(probe), d=dt)
|
|
336
|
+
peak_freq = freqs[np.argmax(spectrum[1:]) + 1]
|
|
337
|
+
measured_omega = 2 * np.pi * peak_freq
|
|
338
|
+
assert measured_omega == pytest.approx(omega, rel=0.02)
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
class TestFluxTubeConfinement:
|
|
342
|
+
def test_energy_grows_linearly_with_separation(self):
|
|
343
|
+
"""The defining signature of confinement (vs. an ordinary field that spreads and
|
|
344
|
+
whose energy is roughly separation-independent): total field energy in the
|
|
345
|
+
confined tube should scale linearly with charge separation."""
|
|
346
|
+
x = np.linspace(-30, 30, 4000)
|
|
347
|
+
dx = x[1] - x[0]
|
|
348
|
+
separations = np.array([6.0, 10.0, 14.0, 18.0, 22.0])
|
|
349
|
+
energies = np.array([np.sum(0.5 * flux_tube_field_1d(x, s) ** 2) * dx for s in separations])
|
|
350
|
+
slope, intercept = np.polyfit(separations, energies, 1)
|
|
351
|
+
predicted = slope * separations + intercept
|
|
352
|
+
assert np.max(np.abs(energies - predicted)) < 1e-6
|
|
353
|
+
assert slope > 0
|
|
354
|
+
|
|
355
|
+
def test_field_plateaus_between_charges_and_vanishes_outside(self):
|
|
356
|
+
x = np.linspace(-20, 20, 2000)
|
|
357
|
+
field = flux_tube_field_1d(x, separation=10.0, flux_quantum=1.0)
|
|
358
|
+
assert abs(field[np.argmin(np.abs(x))] - 1.0) < 0.05
|
|
359
|
+
assert abs(field[np.argmin(np.abs(x - 15))]) < 0.05
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
class TestCasimirEffect:
|
|
363
|
+
def test_mode_frequencies_are_evenly_spaced(self):
|
|
364
|
+
omega = casimir_mode_frequencies(d=2.0, c=1.0, n_max=10)
|
|
365
|
+
spacing = np.diff(omega)
|
|
366
|
+
assert np.allclose(spacing, np.pi / 2.0)
|
|
367
|
+
|
|
368
|
+
def test_regularized_energy_converges_to_known_closed_form(self):
|
|
369
|
+
d = 3.0
|
|
370
|
+
exact = -np.pi / (24 * d)
|
|
371
|
+
coarse_error = abs(casimir_energy_1d(d, cutoff=3e-3 * d) - exact)
|
|
372
|
+
fine_error = abs(casimir_energy_1d(d, cutoff=7e-4 * d) - exact)
|
|
373
|
+
assert coarse_error < 1e-6
|
|
374
|
+
assert fine_error < coarse_error
|
|
375
|
+
|
|
376
|
+
def test_energy_magnitude_shrinks_as_plates_separate(self):
|
|
377
|
+
# weaker (less negative) zero-point energy at larger separation --
|
|
378
|
+
# the discrete spectrum crowds toward the continuum as d grows.
|
|
379
|
+
assert casimir_energy_1d(1.0) < casimir_energy_1d(2.0) < casimir_energy_1d(4.0) < 0.0
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
class TestGPERealTimeEvolution:
|
|
383
|
+
def test_norm_is_conserved_under_real_time_propagation(self):
|
|
384
|
+
n, length, g = 40, 12.0, 2.0
|
|
385
|
+
X, Y, _, _, K2 = harmonic_trap_grid(n, length)
|
|
386
|
+
dx = X[1, 0] - X[0, 0]
|
|
387
|
+
V = 0.5 * (X**2 + Y**2)
|
|
388
|
+
psi0 = np.exp(-0.5 * (X**2 + Y**2)).astype(complex)
|
|
389
|
+
psi0 *= 1.0 / np.sqrt(np.sum(np.abs(psi0) ** 2) * dx * dx)
|
|
390
|
+
snaps, _ = gpe_evolve(psi0, V, g, dt=1e-3, steps=400, K2=K2, snapshot_stride=50)
|
|
391
|
+
norms = np.sum(np.abs(snaps) ** 2, axis=(1, 2)) * dx * dx
|
|
392
|
+
assert np.max(np.abs(norms - norms[0])) < 1e-9
|
|
393
|
+
|
|
394
|
+
def test_off_center_vortex_precesses_around_the_trap(self):
|
|
395
|
+
"""A vortex imprinted off-center on a relaxed (vortex-free) ground state is not a
|
|
396
|
+
stationary GPE solution -- unlike an imaginary-time relaxation (which would just
|
|
397
|
+
expel it), real-time propagation makes it precess around the trap center at
|
|
398
|
+
roughly constant radius, the standard single-vortex precession phenomenon."""
|
|
399
|
+
n, length, g = 64, 14.0, 4.0
|
|
400
|
+
X, Y, _, _, K2 = harmonic_trap_grid(n, length)
|
|
401
|
+
V = 0.5 * (X**2 + Y**2)
|
|
402
|
+
psi_gs = gpe_relax(np.exp(-0.5 * (X**2 + Y**2)).astype(complex), V, g, dtau=5e-4, steps=4000, X=X, Y=Y, K2=K2)
|
|
403
|
+
psi_v = gpe_imprint_vortex(psi_gs, X, Y, [(1.2, 0.03)])
|
|
404
|
+
snaps, times = gpe_evolve(psi_v, V, g, dt=2e-3, steps=6000, K2=K2, snapshot_stride=300)
|
|
405
|
+
|
|
406
|
+
def core_position(psi, margin=8, threshold=0.0005):
|
|
407
|
+
winding = count_vortices(psi, density_threshold=threshold)
|
|
408
|
+
winding[:margin, :] = 0
|
|
409
|
+
winding[-margin:, :] = 0
|
|
410
|
+
winding[:, :margin] = 0
|
|
411
|
+
winding[:, -margin:] = 0
|
|
412
|
+
idxs = np.argwhere(np.abs(winding) > 0)
|
|
413
|
+
return (float(X[idxs[0, 0], idxs[0, 1]]), float(Y[idxs[0, 0], idxs[0, 1]])) if len(idxs) else None
|
|
414
|
+
|
|
415
|
+
positions = [core_position(snap) for snap in snaps]
|
|
416
|
+
found = [p for p in positions if p is not None]
|
|
417
|
+
assert len(found) >= len(positions) - 3 # the detector should find the core in almost every frame
|
|
418
|
+
angles = np.unwrap([np.arctan2(y, x) for x, y in found])
|
|
419
|
+
radii = [np.hypot(x, y) for x, y in found]
|
|
420
|
+
assert abs(angles[-1] - angles[0]) > np.deg2rad(90) # it has visibly gone around part of an orbit
|
|
421
|
+
assert all(0.3 < r < 3.0 for r in radii) # stays in a bounded annulus -- neither collapsed nor ejected
|
|
422
|
+
|
|
423
|
+
def test_self_focusing_nls_concentrates_before_grid_breaks_down(self):
|
|
424
|
+
"""Attractive interactions (g<0 in this module's +g|psi|^2 convention) with a tall,
|
|
425
|
+
narrow packet cause self-focusing: peak density grows and the packet narrows over
|
|
426
|
+
time. True collapse is a singularity this finite grid cannot resolve, so the test
|
|
427
|
+
(like the function's documented usage) stops well before that breakdown."""
|
|
428
|
+
n, length = 128, 20.0
|
|
429
|
+
X, Y, _, _, K2 = harmonic_trap_grid(n, length)
|
|
430
|
+
dx = X[1, 0] - X[0, 0]
|
|
431
|
+
V = np.zeros((n, n))
|
|
432
|
+
psi0 = 3.0 * np.exp(-0.5 * (X**2 + Y**2)).astype(complex)
|
|
433
|
+
norm0 = np.sum(np.abs(psi0) ** 2) * dx * dx
|
|
434
|
+
snaps, _ = gpe_evolve(psi0, V, g=-1.0, dt=2e-4, steps=1400, K2=K2, snapshot_stride=200)
|
|
435
|
+
norms = np.sum(np.abs(snaps) ** 2, axis=(1, 2)) * dx * dx
|
|
436
|
+
peaks = np.max(np.abs(snaps) ** 2, axis=(1, 2))
|
|
437
|
+
assert np.max(np.abs(norms - norm0)) / norm0 < 1e-6
|
|
438
|
+
assert np.all(np.diff(peaks) > 0) # monotonically concentrating
|
|
439
|
+
assert peaks[-1] > 10 * peaks[0] # substantial concentration within the simulated window
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""Fast (no anim.save(), no file I/O) coverage of physicskit.fields.visualizers:
|
|
2
|
+
static plots and the ax=None/ax=<given> branch of every function, plus
|
|
3
|
+
building each animation and drawing a frame without ever writing a GIF
|
|
4
|
+
(see physicskit/fields/tests/test_field_animations.py for the real
|
|
5
|
+
save()-to-disk smoke tests, marked slow)."""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import matplotlib.pyplot as plt
|
|
10
|
+
import numpy as np
|
|
11
|
+
|
|
12
|
+
from physicskit.fields.quantum_fields import harmonic_trap_grid
|
|
13
|
+
from physicskit.fields.visualizers import (
|
|
14
|
+
animate_density_2d,
|
|
15
|
+
animate_field_1d,
|
|
16
|
+
animate_field_2d,
|
|
17
|
+
animate_flux_tube,
|
|
18
|
+
plot_bec_density,
|
|
19
|
+
plot_bec_phase,
|
|
20
|
+
plot_field_1d,
|
|
21
|
+
plot_poynting_field,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_plot_field_1d_with_and_without_label_and_given_ax():
|
|
26
|
+
x = np.linspace(-10, 10, 50)
|
|
27
|
+
u = np.sin(x)
|
|
28
|
+
|
|
29
|
+
fig, ax = plot_field_1d(x, u)
|
|
30
|
+
assert isinstance(fig, plt.Figure)
|
|
31
|
+
assert not ax.get_legend()
|
|
32
|
+
|
|
33
|
+
fig2, ax2 = plot_field_1d(x, u, label="u(x)")
|
|
34
|
+
assert ax2.get_legend() is not None
|
|
35
|
+
|
|
36
|
+
_, ax_given = plot_field_1d(x, u, ax=ax)
|
|
37
|
+
assert ax_given is ax
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_plot_poynting_field_given_ax():
|
|
41
|
+
X, Y = np.meshgrid(np.linspace(-1, 1, 10), np.linspace(-1, 1, 10))
|
|
42
|
+
Sx, Sy = -Y, X
|
|
43
|
+
fig, ax = plot_poynting_field(X, Y, Sx, Sy)
|
|
44
|
+
assert isinstance(fig, plt.Figure)
|
|
45
|
+
|
|
46
|
+
_, ax_given = plot_poynting_field(X, Y, Sx, Sy, ax=ax)
|
|
47
|
+
assert ax_given is ax
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_plot_bec_density_and_phase_given_ax():
|
|
51
|
+
X, Y, _, _, _ = harmonic_trap_grid(16, 10.0)
|
|
52
|
+
psi = np.exp(-0.5 * (X**2 + Y**2)).astype(complex)
|
|
53
|
+
|
|
54
|
+
fig, ax = plot_bec_density(X, Y, psi)
|
|
55
|
+
assert isinstance(fig, plt.Figure)
|
|
56
|
+
_, ax_given = plot_bec_density(X, Y, psi, ax=ax)
|
|
57
|
+
assert ax_given is ax
|
|
58
|
+
|
|
59
|
+
fig2, ax2 = plot_bec_phase(X, Y, psi)
|
|
60
|
+
assert isinstance(fig2, plt.Figure)
|
|
61
|
+
_, ax2_given = plot_bec_phase(X, Y, psi, ax=ax2)
|
|
62
|
+
assert ax2_given is ax2
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def test_animate_field_1d_builds_and_draws_a_frame_given_ax():
|
|
66
|
+
x = np.linspace(-10, 10, 30)
|
|
67
|
+
frames = np.array([np.sin(x + phase) for phase in np.linspace(0, 1, 4)])
|
|
68
|
+
times = np.linspace(0, 1, 4)
|
|
69
|
+
|
|
70
|
+
fig, ax = plt.subplots()
|
|
71
|
+
anim = animate_field_1d(x, frames, times, ax=ax)
|
|
72
|
+
assert anim._fig is fig
|
|
73
|
+
anim._draw_frame(1)
|
|
74
|
+
assert ax.get_title() == f"t = {times[1]:.4g}"
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_animate_field_2d_builds_and_draws_a_frame_given_ax():
|
|
78
|
+
X, Y = np.meshgrid(np.arange(8), np.arange(8), indexing="ij")
|
|
79
|
+
frames = np.random.default_rng(0).normal(size=(3, 8, 8))
|
|
80
|
+
|
|
81
|
+
fig, ax = plt.subplots()
|
|
82
|
+
anim = animate_field_2d(X, Y, frames, ax=ax)
|
|
83
|
+
assert anim._fig is fig
|
|
84
|
+
anim._draw_frame(1)
|
|
85
|
+
assert ax.get_title() == "frame 1" # times=None branch
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def test_animate_density_2d_builds_and_draws_a_frame_given_ax():
|
|
89
|
+
frames = np.random.default_rng(0).random(size=(3, 8, 8))
|
|
90
|
+
|
|
91
|
+
fig, ax = plt.subplots()
|
|
92
|
+
anim = animate_density_2d(frames, ax=ax)
|
|
93
|
+
assert anim._fig is fig
|
|
94
|
+
anim._draw_frame(1)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def test_animate_flux_tube_builds_and_draws_a_frame_given_ax():
|
|
98
|
+
fig, ax = plt.subplots()
|
|
99
|
+
anim = animate_flux_tube((20, 10), dx=0.25, dy=0.25, separations=np.linspace(4.0, 8.0, 3), ax=ax)
|
|
100
|
+
assert anim._fig is fig
|
|
101
|
+
anim._draw_frame(1)
|