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,147 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pytest
|
|
3
|
+
|
|
4
|
+
from physicskit.optics.quantum_optics import (
|
|
5
|
+
JaynesCummingsModel,
|
|
6
|
+
coherent_state,
|
|
7
|
+
compute_wigner_function,
|
|
8
|
+
fock_state,
|
|
9
|
+
squeezed_state,
|
|
10
|
+
wigner_negativity,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_fock_state_is_normalized_and_correctly_placed():
|
|
15
|
+
psi = fock_state(3, 8)
|
|
16
|
+
assert np.linalg.norm(psi) == pytest.approx(1.0)
|
|
17
|
+
assert psi[3] == pytest.approx(1.0)
|
|
18
|
+
assert np.sum(np.abs(psi) ** 2) - np.abs(psi[3]) ** 2 == pytest.approx(0.0)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_fock_state_raises_when_n_exceeds_cutoff():
|
|
22
|
+
with pytest.raises(ValueError):
|
|
23
|
+
fock_state(5, 5)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_coherent_state_is_normalized():
|
|
27
|
+
psi = coherent_state(1.5 + 0.5j, cutoff=40)
|
|
28
|
+
assert np.linalg.norm(psi) == pytest.approx(1.0, rel=1e-6)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_coherent_state_of_zero_amplitude_is_the_vacuum_fock_state():
|
|
32
|
+
psi = coherent_state(0.0, cutoff=8)
|
|
33
|
+
np.testing.assert_allclose(psi, fock_state(0, 8))
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_coherent_state_mean_photon_number_matches_alpha_squared():
|
|
37
|
+
alpha = 1.2
|
|
38
|
+
cutoff = 40
|
|
39
|
+
psi = coherent_state(alpha, cutoff)
|
|
40
|
+
n = np.arange(cutoff)
|
|
41
|
+
mean_n = np.sum(n * np.abs(psi) ** 2)
|
|
42
|
+
assert mean_n == pytest.approx(alpha**2, rel=1e-2)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_squeezed_state_is_normalized():
|
|
46
|
+
psi = squeezed_state(xi=0.5 + 0.2j, alpha=0.3, cutoff=30)
|
|
47
|
+
assert np.linalg.norm(psi) == pytest.approx(1.0, rel=1e-6)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_squeezed_state_reduces_to_vacuum_when_xi_and_alpha_zero():
|
|
51
|
+
psi = squeezed_state(xi=0.0, alpha=0.0, cutoff=10)
|
|
52
|
+
expected = fock_state(0, 10)
|
|
53
|
+
assert np.allclose(psi, expected)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_squeezed_state_reduces_to_coherent_state_when_xi_zero():
|
|
57
|
+
alpha = 0.8 - 0.3j
|
|
58
|
+
cutoff = 30
|
|
59
|
+
psi = squeezed_state(xi=0.0, alpha=alpha, cutoff=cutoff)
|
|
60
|
+
expected = coherent_state(alpha, cutoff)
|
|
61
|
+
assert np.allclose(psi, expected, atol=1e-6)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_wigner_function_integrates_to_one_for_coherent_state():
|
|
65
|
+
cutoff = 25
|
|
66
|
+
psi = coherent_state(1.0 + 0.5j, cutoff)
|
|
67
|
+
xg = np.linspace(-6, 6, 121)
|
|
68
|
+
pg = np.linspace(-6, 6, 121)
|
|
69
|
+
W = compute_wigner_function(psi, xg, pg)
|
|
70
|
+
integral = np.trapezoid(np.trapezoid(W, pg, axis=1), xg)
|
|
71
|
+
assert integral == pytest.approx(1.0, rel=0.03)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def test_wigner_function_integrates_to_one_for_fock_state_one():
|
|
75
|
+
cutoff = 15
|
|
76
|
+
psi = fock_state(1, cutoff)
|
|
77
|
+
xg = np.linspace(-6, 6, 121)
|
|
78
|
+
pg = np.linspace(-6, 6, 121)
|
|
79
|
+
W = compute_wigner_function(psi, xg, pg)
|
|
80
|
+
integral = np.trapezoid(np.trapezoid(W, pg, axis=1), xg)
|
|
81
|
+
assert integral == pytest.approx(1.0, rel=0.03)
|
|
82
|
+
assert W.shape == (len(xg), len(pg))
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def test_wigner_negativity_zero_for_coherent_state_positive_for_fock_one():
|
|
86
|
+
xg = np.linspace(-6, 6, 121)
|
|
87
|
+
pg = np.linspace(-6, 6, 121)
|
|
88
|
+
|
|
89
|
+
psi_coh = coherent_state(1.0, 25)
|
|
90
|
+
W_coh = compute_wigner_function(psi_coh, xg, pg)
|
|
91
|
+
neg_coh = wigner_negativity(W_coh, xg, pg)
|
|
92
|
+
assert neg_coh == pytest.approx(0.0, abs=1e-9)
|
|
93
|
+
|
|
94
|
+
psi_fock1 = fock_state(1, 15)
|
|
95
|
+
W_fock1 = compute_wigner_function(psi_fock1, xg, pg)
|
|
96
|
+
neg_fock1 = wigner_negativity(W_fock1, xg, pg)
|
|
97
|
+
assert neg_fock1 > 0.0
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def test_fock_state_one_wigner_function_is_negative_at_origin():
|
|
101
|
+
cutoff = 10
|
|
102
|
+
psi = fock_state(1, cutoff)
|
|
103
|
+
xg = np.linspace(-4, 4, 81)
|
|
104
|
+
pg = np.linspace(-4, 4, 81)
|
|
105
|
+
W = compute_wigner_function(psi, xg, pg)
|
|
106
|
+
origin = (np.abs(xg).argmin(), np.abs(pg).argmin())
|
|
107
|
+
assert W[origin] == pytest.approx(-1.0 / np.pi, rel=1e-3)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def test_jaynes_cummings_hamiltonian_is_hermitian():
|
|
111
|
+
jc = JaynesCummingsModel(omega_c=1.0, omega_a=1.2, g=0.3, cutoff=6)
|
|
112
|
+
H = jc.hamiltonian()
|
|
113
|
+
assert H.shape == (12, 12)
|
|
114
|
+
assert np.allclose(H, H.conj().T)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def test_resonant_vacuum_rabi_oscillation_matches_cos_squared():
|
|
118
|
+
omega0 = 2.0
|
|
119
|
+
g = 0.4
|
|
120
|
+
cutoff = 12
|
|
121
|
+
jc = JaynesCummingsModel(omega_c=omega0, omega_a=omega0, g=g, cutoff=cutoff)
|
|
122
|
+
|
|
123
|
+
t_array = np.array([0.0, 0.5, 1.0, 1.5, 2.0, 3.0, 5.0])
|
|
124
|
+
Pe = jc.excited_state_population(t_array, n_photons=0)
|
|
125
|
+
expected = np.cos(g * t_array) ** 2
|
|
126
|
+
|
|
127
|
+
assert np.allclose(Pe, expected, atol=1e-3)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def test_excited_state_population_conserves_probability():
|
|
131
|
+
jc = JaynesCummingsModel(omega_c=1.0, omega_a=1.5, g=0.2, cutoff=8)
|
|
132
|
+
psi0 = fock_state(0, 2 * jc.cutoff) # |e, 0>
|
|
133
|
+
t_array = np.linspace(0.0, 20.0, 15)
|
|
134
|
+
psi_t = jc.evolve(psi0, t_array)
|
|
135
|
+
norms = np.sum(np.abs(psi_t) ** 2, axis=1)
|
|
136
|
+
assert np.allclose(norms, 1.0, atol=1e-8)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def test_off_resonant_jaynes_cummings_reduces_rabi_amplitude():
|
|
140
|
+
# Far off resonance, the atom barely exchanges excitation with the
|
|
141
|
+
# cavity: the excited-state population should stay close to 1.
|
|
142
|
+
g = 0.1
|
|
143
|
+
cutoff = 10
|
|
144
|
+
jc = JaynesCummingsModel(omega_c=1.0, omega_a=1.0 + 50.0 * g, g=g, cutoff=cutoff)
|
|
145
|
+
t_array = np.linspace(0.0, 20.0, 50)
|
|
146
|
+
Pe = jc.excited_state_population(t_array, n_photons=0)
|
|
147
|
+
assert Pe.min() > 0.9
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pytest
|
|
3
|
+
|
|
4
|
+
from physicskit.optics.ray import (
|
|
5
|
+
OpticalElement,
|
|
6
|
+
OpticalSystem,
|
|
7
|
+
cavity_round_trip_matrix,
|
|
8
|
+
cavity_stability,
|
|
9
|
+
curved_interface,
|
|
10
|
+
flat_interface,
|
|
11
|
+
free_space,
|
|
12
|
+
grin_medium,
|
|
13
|
+
spherical_mirror,
|
|
14
|
+
thick_lens,
|
|
15
|
+
thin_lens,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_free_space_matrix():
|
|
20
|
+
M = free_space(3.0)
|
|
21
|
+
assert np.allclose(M, [[1.0, 3.0], [0.0, 1.0]])
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_thin_lens_matrix():
|
|
25
|
+
M = thin_lens(0.25)
|
|
26
|
+
assert np.allclose(M, [[1.0, 0.0], [-4.0, 1.0]])
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_flat_interface_matrix():
|
|
30
|
+
M = flat_interface(1.0, 1.5)
|
|
31
|
+
assert np.allclose(M, [[1.0, 0.0], [0.0, 1.0 / 1.5]])
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_curved_interface_reduces_to_flat_interface_for_large_radius():
|
|
35
|
+
M_curved = curved_interface(R=1.0e10, n1=1.0, n2=1.5)
|
|
36
|
+
M_flat = flat_interface(1.0, 1.5)
|
|
37
|
+
assert np.allclose(M_curved, M_flat, atol=1e-8)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_spherical_mirror_matrix():
|
|
41
|
+
M = spherical_mirror(4.0)
|
|
42
|
+
assert np.allclose(M, [[1.0, 0.0], [-0.5, 1.0]])
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_thick_lens_composition_order_and_unit_determinant():
|
|
46
|
+
R1, R2, t, n = 0.1, -0.1, 0.01, 1.5
|
|
47
|
+
M = thick_lens(R1, R2, t, n)
|
|
48
|
+
expected = curved_interface(R2, n, 1.0) @ free_space(t) @ curved_interface(R1, 1.0, n)
|
|
49
|
+
assert np.allclose(M, expected)
|
|
50
|
+
# a lossless system's ABCD matrix always has unit determinant
|
|
51
|
+
assert np.linalg.det(M) == pytest.approx(1.0, abs=1e-10)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def test_thin_lens_focuses_parallel_ray_bundle_to_a_point():
|
|
55
|
+
# A bundle of rays parallel to the axis (theta0=0), of any height y0,
|
|
56
|
+
# converges to the axis after propagating one focal length past the lens.
|
|
57
|
+
f = 0.2
|
|
58
|
+
M = free_space(f) @ thin_lens(f)
|
|
59
|
+
for y0 in (-0.05, -0.01, 0.0, 0.01, 0.05, 1.0):
|
|
60
|
+
y_out, theta_out = M @ np.array([y0, 0.0])
|
|
61
|
+
assert y_out == pytest.approx(0.0, abs=1e-12)
|
|
62
|
+
assert theta_out == pytest.approx(-y0 / f)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def test_grin_medium_reduces_to_free_space_as_n2_coeff_vanishes():
|
|
66
|
+
d = 2.5
|
|
67
|
+
M_grin = grin_medium(n0=1.0, n2_coeff=1e-9, d=d)
|
|
68
|
+
M_free = free_space(d)
|
|
69
|
+
assert np.allclose(M_grin, M_free, atol=1e-6)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def test_grin_medium_zero_n2_coeff_is_exactly_homogeneous_propagation():
|
|
73
|
+
n0, d = 1.3, 2.5
|
|
74
|
+
assert np.array_equal(grin_medium(n0=n0, n2_coeff=0.0, d=d), np.array([[1.0, d / n0], [0.0, 1.0]]))
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_grin_medium_matches_closed_form_for_finite_gradient():
|
|
78
|
+
n0, n2_coeff, d = 1.5, 4.0, 0.3
|
|
79
|
+
M = grin_medium(n0, n2_coeff, d)
|
|
80
|
+
sqrt_n2 = np.sqrt(n2_coeff)
|
|
81
|
+
arg = sqrt_n2 * d
|
|
82
|
+
expected = np.array(
|
|
83
|
+
[
|
|
84
|
+
[np.cos(arg), np.sin(arg) / (n0 * sqrt_n2)],
|
|
85
|
+
[-n0 * sqrt_n2 * np.sin(arg), np.cos(arg)],
|
|
86
|
+
]
|
|
87
|
+
)
|
|
88
|
+
assert np.allclose(M, expected)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def test_optical_element_stores_attributes():
|
|
92
|
+
M = thin_lens(0.5)
|
|
93
|
+
elem = OpticalElement(M, name="my lens", length=0.0)
|
|
94
|
+
assert elem.name == "my lens"
|
|
95
|
+
assert elem.length == 0.0
|
|
96
|
+
assert np.allclose(elem.matrix, M)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def test_optical_system_matrix_multiplication_order():
|
|
100
|
+
# elements[0] is hit first, so it must be the RIGHTMOST factor:
|
|
101
|
+
# M = M2 @ M1, not M1 @ M2 (these differ since matrices don't commute).
|
|
102
|
+
m1 = thin_lens(0.3)
|
|
103
|
+
m2 = free_space(1.0)
|
|
104
|
+
system = OpticalSystem([OpticalElement(m1, name="lens"), OpticalElement(m2, name="gap")])
|
|
105
|
+
expected = m2 @ m1
|
|
106
|
+
assert np.allclose(system.system_matrix(), expected)
|
|
107
|
+
assert not np.allclose(system.system_matrix(), m1 @ m2)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def test_optical_system_trace_ray_is_cumulative():
|
|
111
|
+
f = 0.5
|
|
112
|
+
system = OpticalSystem(
|
|
113
|
+
[
|
|
114
|
+
OpticalElement(thin_lens(f), name="lens"),
|
|
115
|
+
OpticalElement(free_space(f), name="gap"),
|
|
116
|
+
]
|
|
117
|
+
)
|
|
118
|
+
y0, theta0 = 0.02, 0.0
|
|
119
|
+
trajectory = system.trace_ray(y0, theta0)
|
|
120
|
+
assert trajectory.shape == (3, 2)
|
|
121
|
+
assert np.allclose(trajectory[0], [y0, theta0])
|
|
122
|
+
assert np.allclose(trajectory[1], thin_lens(f) @ [y0, theta0])
|
|
123
|
+
assert np.allclose(trajectory[2], free_space(f) @ thin_lens(f) @ [y0, theta0])
|
|
124
|
+
# the whole bundle refocuses to the axis after one focal length
|
|
125
|
+
assert trajectory[-1, 0] == pytest.approx(0.0, abs=1e-12)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def test_stability_agrees_with_cavity_stability_for_stable_cavity():
|
|
129
|
+
# Two R=2.0 mirrors separated by d=1.0: g = 1 - d/R = 0.5, g1*g2 = 0.25,
|
|
130
|
+
# which lies in [0, 1] -- a stable resonator by the standard g-parameter
|
|
131
|
+
# criterion (Kogelnik & Li 1966).
|
|
132
|
+
R, d = 2.0, 1.0
|
|
133
|
+
elements = [
|
|
134
|
+
OpticalElement(spherical_mirror(R), name="M1"),
|
|
135
|
+
OpticalElement(free_space(d), name="gap1"),
|
|
136
|
+
OpticalElement(spherical_mirror(R), name="M2"),
|
|
137
|
+
OpticalElement(free_space(d), name="gap2"),
|
|
138
|
+
]
|
|
139
|
+
system = OpticalSystem(elements)
|
|
140
|
+
M = system.system_matrix()
|
|
141
|
+
assert M is not None
|
|
142
|
+
assert system.stability_parameter == pytest.approx((M[0, 0] + M[1, 1]) / 2.0)
|
|
143
|
+
assert system.is_stable() is True
|
|
144
|
+
assert cavity_stability(cavity_round_trip_matrix(elements)) is True
|
|
145
|
+
assert cavity_stability(system.system_matrix()) == system.is_stable()
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def test_stability_agrees_with_cavity_stability_for_unstable_cavity():
|
|
149
|
+
# Two R=0.5 mirrors separated by d=2.0: g = 1 - d/R = -3, g1*g2 = 9,
|
|
150
|
+
# well outside [0, 1] -- an unstable resonator.
|
|
151
|
+
R, d = 0.5, 2.0
|
|
152
|
+
elements = [
|
|
153
|
+
OpticalElement(spherical_mirror(R), name="M1"),
|
|
154
|
+
OpticalElement(free_space(d), name="gap1"),
|
|
155
|
+
OpticalElement(spherical_mirror(R), name="M2"),
|
|
156
|
+
OpticalElement(free_space(d), name="gap2"),
|
|
157
|
+
]
|
|
158
|
+
system = OpticalSystem(elements)
|
|
159
|
+
assert system.is_stable() is False
|
|
160
|
+
assert cavity_stability(cavity_round_trip_matrix(elements)) is False
|
|
161
|
+
assert cavity_stability(system.system_matrix()) == system.is_stable()
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def test_cavity_round_trip_matrix_matches_optical_system():
|
|
165
|
+
elements = [
|
|
166
|
+
OpticalElement(spherical_mirror(1.0), name="M1"),
|
|
167
|
+
OpticalElement(free_space(0.4), name="gap1"),
|
|
168
|
+
OpticalElement(spherical_mirror(1.5), name="M2"),
|
|
169
|
+
OpticalElement(free_space(0.4), name="gap2"),
|
|
170
|
+
]
|
|
171
|
+
assert np.allclose(cavity_round_trip_matrix(elements), OpticalSystem(elements).system_matrix())
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def test_cavity_stability_boundary_cases():
|
|
175
|
+
assert cavity_stability(np.array([[1.0, 0.0], [0.0, 1.0]])) is True # trace=2
|
|
176
|
+
assert cavity_stability(np.array([[-1.0, 0.0], [0.0, -1.0]])) is True # trace=-2
|
|
177
|
+
assert cavity_stability(np.array([[2.0, 0.0], [0.0, 1.01]])) is False # trace=3.01
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Fast coverage of physicskit.optics.visualizers: the ax=None/ax=<given>
|
|
2
|
+
branch of every function, plus the log_scale=True branches of
|
|
3
|
+
plot_diffraction_pattern and animate_diffraction_propagation. See
|
|
4
|
+
test_diffraction_animation.py for the real anim.save()-to-disk smoke test
|
|
5
|
+
(marked slow)."""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import matplotlib.pyplot as plt
|
|
10
|
+
import numpy as np
|
|
11
|
+
|
|
12
|
+
from physicskit.optics.gaussian import GaussianBeam
|
|
13
|
+
from physicskit.optics.ray import OpticalElement, OpticalSystem, free_space, thin_lens
|
|
14
|
+
from physicskit.optics.visualizers import animate_diffraction_propagation, plot_beam_envelope, plot_diffraction_pattern, plot_ray_trace
|
|
15
|
+
from physicskit.optics.wave import double_slit_aperture
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_plot_ray_trace_given_ax():
|
|
19
|
+
system = OpticalSystem(
|
|
20
|
+
[
|
|
21
|
+
OpticalElement(free_space(1.0), name="d1", length=1.0),
|
|
22
|
+
OpticalElement(thin_lens(1.0), name="lens"),
|
|
23
|
+
]
|
|
24
|
+
)
|
|
25
|
+
fig, ax = plot_ray_trace(system, y0=0.5, theta0=0.0)
|
|
26
|
+
assert isinstance(fig, plt.Figure)
|
|
27
|
+
_, ax_given = plot_ray_trace(system, y0=0.5, theta0=0.0, ax=ax)
|
|
28
|
+
assert ax_given is ax
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_plot_beam_envelope_given_ax():
|
|
32
|
+
beam = GaussianBeam(wavelength=0.5e-3, w0=0.1, z0=0.0)
|
|
33
|
+
fig, ax = plot_beam_envelope(beam, z_range=(-5, 5), n_points=20)
|
|
34
|
+
assert isinstance(fig, plt.Figure)
|
|
35
|
+
_, ax_given = plot_beam_envelope(beam, z_range=(-5, 5), n_points=20, ax=ax)
|
|
36
|
+
assert ax_given is ax
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_plot_diffraction_pattern_log_scale_and_given_ax():
|
|
40
|
+
dx = 1e-3
|
|
41
|
+
U = double_slit_aperture((16, 16), dx=dx, width=2e-3, separation=1e-2).astype(complex)
|
|
42
|
+
fig, ax = plot_diffraction_pattern(U, dx=dx, log_scale=True)
|
|
43
|
+
assert isinstance(fig, plt.Figure)
|
|
44
|
+
assert ax.get_ylabel() == "y"
|
|
45
|
+
_, ax_given = plot_diffraction_pattern(U, dx=dx, ax=ax)
|
|
46
|
+
assert ax_given is ax
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def test_animate_diffraction_propagation_log_scale_and_given_ax_builds_and_draws_a_frame():
|
|
50
|
+
dx = 1e-3
|
|
51
|
+
ap = double_slit_aperture((16, 16), dx=dx, width=2e-3, separation=1e-2).astype(complex)
|
|
52
|
+
z_values = np.linspace(0.01, 1.0, 3)
|
|
53
|
+
|
|
54
|
+
fig, ax = plt.subplots()
|
|
55
|
+
anim = animate_diffraction_propagation(ap, wavelength=0.5e-3, z_values=z_values, dx=dx, log_scale=True, ax=ax)
|
|
56
|
+
assert anim._fig is fig
|
|
57
|
+
anim._draw_frame(1)
|
|
58
|
+
assert ax.get_title() == f"z = {z_values[1]:.4g}"
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pytest
|
|
3
|
+
|
|
4
|
+
from physicskit.optics.wave import (
|
|
5
|
+
angular_spectrum_propagate,
|
|
6
|
+
circular_aperture,
|
|
7
|
+
double_slit_aperture,
|
|
8
|
+
field_grid,
|
|
9
|
+
fraunhofer_diffraction,
|
|
10
|
+
fresnel_diffraction,
|
|
11
|
+
intensity,
|
|
12
|
+
single_slit_aperture,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_field_grid_is_centered():
|
|
17
|
+
X, Y = field_grid((4, 6), dx=0.5)
|
|
18
|
+
assert X.shape == (4, 6)
|
|
19
|
+
assert Y.shape == (4, 6)
|
|
20
|
+
assert X[0, 3] == pytest.approx(0.0)
|
|
21
|
+
assert Y[2, 0] == pytest.approx(0.0)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_circular_aperture_is_open_at_center_and_closed_at_corner():
|
|
25
|
+
ap = circular_aperture((65, 65), dx=1.0, radius=10.0)
|
|
26
|
+
assert ap[32, 32] == pytest.approx(1.0)
|
|
27
|
+
assert ap[0, 0] == pytest.approx(0.0)
|
|
28
|
+
assert set(np.unique(ap)) <= {0.0, 1.0}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_single_slit_aperture_width_and_full_height():
|
|
32
|
+
shape = (10, 101)
|
|
33
|
+
dx = 1.0
|
|
34
|
+
width = 20.0
|
|
35
|
+
ap = single_slit_aperture(shape, dx, width)
|
|
36
|
+
# every row should be identical (open along full height)
|
|
37
|
+
assert np.all(ap == ap[0])
|
|
38
|
+
n_open = int(ap[0].sum())
|
|
39
|
+
assert n_open == pytest.approx(width / dx, abs=1)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_double_slit_aperture_has_two_separated_openings():
|
|
43
|
+
shape = (5, 201)
|
|
44
|
+
dx = 1.0
|
|
45
|
+
width, separation = 8.0, 60.0
|
|
46
|
+
ap = double_slit_aperture(shape, dx, width, separation)
|
|
47
|
+
row = ap[0]
|
|
48
|
+
X, _ = field_grid(shape, dx)
|
|
49
|
+
x = X[0]
|
|
50
|
+
# sample the intensity mask around each expected slit center and the gap between them
|
|
51
|
+
left_open = row[np.argmin(np.abs(x - (-separation / 2)))]
|
|
52
|
+
right_open = row[np.argmin(np.abs(x - (separation / 2)))]
|
|
53
|
+
middle_closed = row[np.argmin(np.abs(x - 0.0))]
|
|
54
|
+
assert left_open == pytest.approx(1.0)
|
|
55
|
+
assert right_open == pytest.approx(1.0)
|
|
56
|
+
assert middle_closed == pytest.approx(0.0)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def test_intensity_is_squared_modulus():
|
|
60
|
+
U = np.array([3.0 + 4.0j, 1.0 + 0.0j, 0.0 + 2.0j])
|
|
61
|
+
assert np.allclose(intensity(U), [25.0, 1.0, 4.0])
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_angular_spectrum_propagate_conserves_power_over_short_distance():
|
|
65
|
+
# A circular aperture propagated a short distance, with fine enough
|
|
66
|
+
# sampling (dx well below wavelength/2, so no aperture spatial
|
|
67
|
+
# frequency content is evanescent) should conserve total power to
|
|
68
|
+
# within a small grid/aliasing tolerance.
|
|
69
|
+
wavelength = 0.5e-3 # e.g. millimeters, so this is 500 nm
|
|
70
|
+
dx = 0.2e-3
|
|
71
|
+
N = 256
|
|
72
|
+
radius = 20 * dx
|
|
73
|
+
z = 2.0
|
|
74
|
+
|
|
75
|
+
aperture = circular_aperture((N, N), dx, radius)
|
|
76
|
+
U_out = angular_spectrum_propagate(aperture, wavelength, z, dx)
|
|
77
|
+
|
|
78
|
+
power_in = intensity(aperture).sum()
|
|
79
|
+
power_out = intensity(U_out).sum()
|
|
80
|
+
assert power_out == pytest.approx(power_in, rel=0.02)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def test_angular_spectrum_propagate_matches_input_grid_shape():
|
|
84
|
+
ap = circular_aperture((32, 48), dx=1.0e-3, radius=5.0e-3)
|
|
85
|
+
U = angular_spectrum_propagate(ap, wavelength=0.6e-3, z=1.0, dx=1.0e-3)
|
|
86
|
+
assert U.shape == ap.shape
|
|
87
|
+
assert np.iscomplexobj(U)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def test_fresnel_diffraction_matches_input_grid_shape():
|
|
91
|
+
ap = circular_aperture((64, 64), dx=0.01, radius=0.05)
|
|
92
|
+
U = fresnel_diffraction(ap, wavelength=0.5e-3, z=5.0, dx=0.01)
|
|
93
|
+
assert U.shape == ap.shape
|
|
94
|
+
assert np.iscomplexobj(U)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def test_fresnel_diffraction_conserves_power_over_short_distance():
|
|
98
|
+
# The single-FFT Fresnel method resamples onto an output grid with a
|
|
99
|
+
# different pixel spacing (dx_out = wavelength*z/(N*dx_in)), so total
|
|
100
|
+
# power must be compared as intensity-sum times each grid's own pixel
|
|
101
|
+
# area, not a bare intensity-sum comparison (which implicitly assumes
|
|
102
|
+
# equal pixel areas, valid for angular_spectrum_propagate but not here).
|
|
103
|
+
wavelength = 0.5e-3
|
|
104
|
+
dx = 0.2e-3
|
|
105
|
+
N = 256
|
|
106
|
+
radius = 20 * dx
|
|
107
|
+
z = 2.0
|
|
108
|
+
|
|
109
|
+
aperture = circular_aperture((N, N), dx, radius)
|
|
110
|
+
U_out = fresnel_diffraction(aperture, wavelength, z, dx)
|
|
111
|
+
dx_out = wavelength * z / (N * dx)
|
|
112
|
+
|
|
113
|
+
power_in = intensity(aperture).sum() * dx**2
|
|
114
|
+
power_out = intensity(U_out).sum() * dx_out**2
|
|
115
|
+
assert power_out == pytest.approx(power_in, rel=0.05)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def test_fraunhofer_single_slit_first_minimum_matches_analytic_formula():
|
|
119
|
+
# Standard single-slit diffraction: the first intensity zero occurs at
|
|
120
|
+
# sin(theta) = wavelength / width. We locate the first minimum of the
|
|
121
|
+
# Fraunhofer pattern's central row and convert its transverse position
|
|
122
|
+
# x' at distance z to an angle via the small-angle relation
|
|
123
|
+
# sin(theta) ~= x' / z (valid here since x' << z in the far field).
|
|
124
|
+
wavelength = 0.5e-3 # mm
|
|
125
|
+
width = 0.05 # mm
|
|
126
|
+
dx = 0.002 # mm
|
|
127
|
+
N = 512
|
|
128
|
+
z = 500.0 # mm; far enough for the Fraunhofer approximation to hold
|
|
129
|
+
|
|
130
|
+
aperture = single_slit_aperture((N, N), dx, width)
|
|
131
|
+
U = fraunhofer_diffraction(aperture, wavelength, z, dx)
|
|
132
|
+
row = intensity(U)[N // 2]
|
|
133
|
+
|
|
134
|
+
fx = np.fft.fftshift(np.fft.fftfreq(N, d=dx))
|
|
135
|
+
x_prime = wavelength * z * fx
|
|
136
|
+
|
|
137
|
+
# walk outward from the central peak until intensity starts rising again
|
|
138
|
+
center = N // 2
|
|
139
|
+
i = center
|
|
140
|
+
while row[i + 1] < row[i]:
|
|
141
|
+
i += 1
|
|
142
|
+
x_min = x_prime[i]
|
|
143
|
+
|
|
144
|
+
sin_theta_measured = x_min / z
|
|
145
|
+
sin_theta_predicted = wavelength / width
|
|
146
|
+
assert sin_theta_measured == pytest.approx(sin_theta_predicted, rel=0.05)
|