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
physicskit/optics/ray.py
ADDED
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
"""Geometric ray optics: paraxial ray transfer (ABCD) matrices and optical systems.
|
|
2
|
+
|
|
3
|
+
In the paraxial approximation, a light ray at a given plane along the
|
|
4
|
+
optical axis is fully described by two numbers -- its height above the
|
|
5
|
+
axis, :math:`y`, and the angle it makes with the axis, :math:`\\theta`
|
|
6
|
+
(in radians, small-angle/paraxial regime) -- collected into a state vector
|
|
7
|
+
:math:`(y, \\theta)^T`. Every simple optical element (propagation through
|
|
8
|
+
free space, refraction at an interface, a thin or thick lens, a curved
|
|
9
|
+
mirror, a graded-index medium, ...) acts on this vector as a linear map, a
|
|
10
|
+
:math:`2\\times 2` "ABCD" matrix:
|
|
11
|
+
|
|
12
|
+
.. math::
|
|
13
|
+
|
|
14
|
+
\\begin{pmatrix} y_{\\text{out}} \\\\ \\theta_{\\text{out}} \\end{pmatrix}
|
|
15
|
+
= \\begin{pmatrix} A & B \\\\ C & D \\end{pmatrix}
|
|
16
|
+
\\begin{pmatrix} y_{\\text{in}} \\\\ \\theta_{\\text{in}} \\end{pmatrix}.
|
|
17
|
+
|
|
18
|
+
This formalism -- developed piecemeal through the 19th century (Gauss's
|
|
19
|
+
theory of optical systems) and formalized for laser resonator design by
|
|
20
|
+
Kogelnik and Li in the 1960s -- reduces the analysis of an arbitrarily long
|
|
21
|
+
chain of lenses, mirrors, and gaps to ordinary matrix multiplication: the
|
|
22
|
+
matrix of a compound system is just the product of the matrices of its
|
|
23
|
+
elements, applied in the order light encounters them (rightmost first).
|
|
24
|
+
The same matrices reappear in :mod:`physicskit.optics.gaussian` to
|
|
25
|
+
propagate the complex beam parameter of a Gaussian laser beam, and their
|
|
26
|
+
trace controls whether a laser cavity is a stable resonator.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
from __future__ import annotations
|
|
30
|
+
|
|
31
|
+
import numpy as np
|
|
32
|
+
|
|
33
|
+
__all__ = [
|
|
34
|
+
"free_space",
|
|
35
|
+
"thin_lens",
|
|
36
|
+
"flat_interface",
|
|
37
|
+
"curved_interface",
|
|
38
|
+
"thick_lens",
|
|
39
|
+
"spherical_mirror",
|
|
40
|
+
"grin_medium",
|
|
41
|
+
"OpticalElement",
|
|
42
|
+
"OpticalSystem",
|
|
43
|
+
"cavity_round_trip_matrix",
|
|
44
|
+
"cavity_stability",
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def free_space(d):
|
|
49
|
+
"""ABCD matrix for propagation through a distance ``d`` of free space (or any homogeneous medium).
|
|
50
|
+
|
|
51
|
+
.. math::
|
|
52
|
+
|
|
53
|
+
M = \\begin{pmatrix} 1 & d \\\\ 0 & 1 \\end{pmatrix}
|
|
54
|
+
|
|
55
|
+
A ray's height changes in proportion to its angle and the distance
|
|
56
|
+
traveled; its angle is unchanged.
|
|
57
|
+
|
|
58
|
+
Parameters
|
|
59
|
+
----------
|
|
60
|
+
d : float
|
|
61
|
+
Propagation distance.
|
|
62
|
+
|
|
63
|
+
Returns
|
|
64
|
+
-------
|
|
65
|
+
ndarray of shape (2, 2)
|
|
66
|
+
|
|
67
|
+
Examples
|
|
68
|
+
--------
|
|
69
|
+
>>> free_space(2.0)
|
|
70
|
+
array([[1., 2.],
|
|
71
|
+
[0., 1.]])
|
|
72
|
+
"""
|
|
73
|
+
return np.array([[1.0, float(d)], [0.0, 1.0]])
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def thin_lens(f):
|
|
77
|
+
"""ABCD matrix for a thin lens of focal length ``f``.
|
|
78
|
+
|
|
79
|
+
.. math::
|
|
80
|
+
|
|
81
|
+
M = \\begin{pmatrix} 1 & 0 \\\\ -1/f & 1 \\end{pmatrix}
|
|
82
|
+
|
|
83
|
+
Parameters
|
|
84
|
+
----------
|
|
85
|
+
f : float
|
|
86
|
+
Focal length (positive for a converging lens, negative for
|
|
87
|
+
diverging).
|
|
88
|
+
|
|
89
|
+
Returns
|
|
90
|
+
-------
|
|
91
|
+
ndarray of shape (2, 2)
|
|
92
|
+
|
|
93
|
+
Examples
|
|
94
|
+
--------
|
|
95
|
+
>>> thin_lens(0.5)
|
|
96
|
+
array([[ 1., 0.],
|
|
97
|
+
[-2., 1.]])
|
|
98
|
+
"""
|
|
99
|
+
return np.array([[1.0, 0.0], [-1.0 / f, 1.0]])
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def flat_interface(n1, n2):
|
|
103
|
+
"""ABCD matrix for refraction at a flat interface, index ``n1`` to ``n2``.
|
|
104
|
+
|
|
105
|
+
.. math::
|
|
106
|
+
|
|
107
|
+
M = \\begin{pmatrix} 1 & 0 \\\\ 0 & n_1/n_2 \\end{pmatrix}
|
|
108
|
+
|
|
109
|
+
Parameters
|
|
110
|
+
----------
|
|
111
|
+
n1 : float
|
|
112
|
+
Refractive index of the incident medium.
|
|
113
|
+
n2 : float
|
|
114
|
+
Refractive index of the transmitted medium.
|
|
115
|
+
|
|
116
|
+
Returns
|
|
117
|
+
-------
|
|
118
|
+
ndarray of shape (2, 2)
|
|
119
|
+
|
|
120
|
+
Examples
|
|
121
|
+
--------
|
|
122
|
+
>>> flat_interface(1.0, 1.5)
|
|
123
|
+
array([[1. , 0. ],
|
|
124
|
+
[0. , 0.66666667]])
|
|
125
|
+
"""
|
|
126
|
+
return np.array([[1.0, 0.0], [0.0, n1 / n2]])
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def curved_interface(R, n1, n2):
|
|
130
|
+
"""ABCD matrix for refraction at a spherical interface of radius ``R``, index ``n1`` to ``n2``.
|
|
131
|
+
|
|
132
|
+
.. math::
|
|
133
|
+
|
|
134
|
+
M = \\begin{pmatrix} 1 & 0 \\\\ (n_1-n_2)/(R n_2) & n_1/n_2 \\end{pmatrix}
|
|
135
|
+
|
|
136
|
+
Parameters
|
|
137
|
+
----------
|
|
138
|
+
R : float
|
|
139
|
+
Radius of curvature of the interface, positive if the center of
|
|
140
|
+
curvature lies on the outgoing (transmitted) side of the surface.
|
|
141
|
+
n1 : float
|
|
142
|
+
Refractive index of the incident medium.
|
|
143
|
+
n2 : float
|
|
144
|
+
Refractive index of the transmitted medium.
|
|
145
|
+
|
|
146
|
+
Returns
|
|
147
|
+
-------
|
|
148
|
+
ndarray of shape (2, 2)
|
|
149
|
+
|
|
150
|
+
See Also
|
|
151
|
+
--------
|
|
152
|
+
flat_interface : The :math:`R \\to \\infty` limit.
|
|
153
|
+
|
|
154
|
+
Examples
|
|
155
|
+
--------
|
|
156
|
+
>>> np.allclose(curved_interface(1.0e12, 1.0, 1.5), flat_interface(1.0, 1.5), atol=1e-6)
|
|
157
|
+
True
|
|
158
|
+
"""
|
|
159
|
+
return np.array([[1.0, 0.0], [(n1 - n2) / (R * n2), n1 / n2]])
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def thick_lens(R1, R2, t, n, n_ext=1.0):
|
|
163
|
+
"""ABCD matrix for a thick lens: two curved interfaces separated by thickness ``t``.
|
|
164
|
+
|
|
165
|
+
Composes, in the order light passes through them, refraction into the
|
|
166
|
+
lens at the first surface, propagation across the lens body, and
|
|
167
|
+
refraction back out at the second surface:
|
|
168
|
+
|
|
169
|
+
.. math::
|
|
170
|
+
|
|
171
|
+
M = M_{R_2}\\,M_t\\,M_{R_1}, \\qquad
|
|
172
|
+
M_{R_1} = \\text{curved\\_interface}(R_1, n_{\\text{ext}}, n), \\\\
|
|
173
|
+
M_t = \\text{free\\_space}(t) \\ \\text{(in medium } n\\text{)}, \\qquad
|
|
174
|
+
M_{R_2} = \\text{curved\\_interface}(R_2, n, n_{\\text{ext}}).
|
|
175
|
+
|
|
176
|
+
Parameters
|
|
177
|
+
----------
|
|
178
|
+
R1 : float
|
|
179
|
+
Radius of curvature of the first (entrance) surface.
|
|
180
|
+
R2 : float
|
|
181
|
+
Radius of curvature of the second (exit) surface.
|
|
182
|
+
t : float
|
|
183
|
+
Center thickness of the lens.
|
|
184
|
+
n : float
|
|
185
|
+
Refractive index of the lens material.
|
|
186
|
+
n_ext : float, default=1.0
|
|
187
|
+
Refractive index of the surrounding medium (air, by default).
|
|
188
|
+
|
|
189
|
+
Returns
|
|
190
|
+
-------
|
|
191
|
+
ndarray of shape (2, 2)
|
|
192
|
+
|
|
193
|
+
Examples
|
|
194
|
+
--------
|
|
195
|
+
A symmetric biconvex lens in air; the resulting matrix has unit
|
|
196
|
+
determinant, as any lossless ABCD system must:
|
|
197
|
+
|
|
198
|
+
>>> M = thick_lens(R1=0.1, R2=-0.1, t=0.01, n=1.5)
|
|
199
|
+
>>> round(float(np.linalg.det(M)), 8)
|
|
200
|
+
1.0
|
|
201
|
+
"""
|
|
202
|
+
m1 = curved_interface(R1, n_ext, n)
|
|
203
|
+
m2 = free_space(t)
|
|
204
|
+
m3 = curved_interface(R2, n, n_ext)
|
|
205
|
+
return m3 @ m2 @ m1
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def spherical_mirror(R):
|
|
209
|
+
"""ABCD matrix for reflection from a spherical mirror of radius of curvature ``R``.
|
|
210
|
+
|
|
211
|
+
.. math::
|
|
212
|
+
|
|
213
|
+
M = \\begin{pmatrix} 1 & 0 \\\\ -2/R & 1 \\end{pmatrix}
|
|
214
|
+
|
|
215
|
+
Parameters
|
|
216
|
+
----------
|
|
217
|
+
R : float
|
|
218
|
+
Radius of curvature, positive for a concave mirror as seen by the
|
|
219
|
+
incoming ray (i.e. a focusing mirror).
|
|
220
|
+
|
|
221
|
+
Returns
|
|
222
|
+
-------
|
|
223
|
+
ndarray of shape (2, 2)
|
|
224
|
+
|
|
225
|
+
Examples
|
|
226
|
+
--------
|
|
227
|
+
>>> spherical_mirror(2.0)
|
|
228
|
+
array([[ 1., 0.],
|
|
229
|
+
[-1., 1.]])
|
|
230
|
+
"""
|
|
231
|
+
return np.array([[1.0, 0.0], [-2.0 / R, 1.0]])
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def grin_medium(n0, n2_coeff, d):
|
|
235
|
+
"""ABCD matrix for a graded-index (GRIN) medium of length ``d``.
|
|
236
|
+
|
|
237
|
+
For the standard quadratic radial index profile
|
|
238
|
+
|
|
239
|
+
.. math::
|
|
240
|
+
|
|
241
|
+
n(r) = n_0\\left(1 - \\frac{n_2 r^2}{2}\\right),
|
|
242
|
+
|
|
243
|
+
paraxial rays oscillate sinusoidally about the axis, giving
|
|
244
|
+
|
|
245
|
+
.. math::
|
|
246
|
+
|
|
247
|
+
A = D = \\cos(\\sqrt{n_2}\\,d), \\qquad
|
|
248
|
+
B = \\frac{\\sin(\\sqrt{n_2}\\,d)}{n_0\\sqrt{n_2}}, \\qquad
|
|
249
|
+
C = -n_0\\sqrt{n_2}\\,\\sin(\\sqrt{n_2}\\,d).
|
|
250
|
+
|
|
251
|
+
As ``n2_coeff`` :math:`\\to 0` (a homogeneous medium of index
|
|
252
|
+
:math:`n_0`), :math:`A=D\\to 1`, :math:`C\\to 0`, and
|
|
253
|
+
:math:`B \\to d/n_0` -- matching :func:`free_space` exactly only for
|
|
254
|
+
:math:`n_0 = 1`. This ``d/n_0`` scaling (rather than plain ``d``) for
|
|
255
|
+
:math:`n_0 \\neq 1` is inherent to the standard GRIN formula quoted
|
|
256
|
+
above; callers comparing against :func:`free_space` in this limit
|
|
257
|
+
should do so at :math:`n_0 = 1`.
|
|
258
|
+
|
|
259
|
+
Parameters
|
|
260
|
+
----------
|
|
261
|
+
n0 : float
|
|
262
|
+
On-axis refractive index.
|
|
263
|
+
n2_coeff : float
|
|
264
|
+
Quadratic index-gradient coefficient :math:`n_2` (units of
|
|
265
|
+
1/length^2). Must be non-negative; ``0`` gives a homogeneous
|
|
266
|
+
medium.
|
|
267
|
+
d : float
|
|
268
|
+
Length of the GRIN medium.
|
|
269
|
+
|
|
270
|
+
Returns
|
|
271
|
+
-------
|
|
272
|
+
ndarray of shape (2, 2)
|
|
273
|
+
|
|
274
|
+
Examples
|
|
275
|
+
--------
|
|
276
|
+
A tiny gradient barely distinguishable from a homogeneous medium of
|
|
277
|
+
index 1 reduces to plain free-space propagation:
|
|
278
|
+
|
|
279
|
+
>>> np.allclose(grin_medium(n0=1.0, n2_coeff=1e-8, d=2.0), free_space(2.0), atol=1e-4)
|
|
280
|
+
True
|
|
281
|
+
"""
|
|
282
|
+
if n2_coeff <= 0.0:
|
|
283
|
+
return np.array([[1.0, float(d) / n0], [0.0, 1.0]])
|
|
284
|
+
sqrt_n2 = np.sqrt(n2_coeff)
|
|
285
|
+
arg = sqrt_n2 * d
|
|
286
|
+
A = np.cos(arg)
|
|
287
|
+
B = np.sin(arg) / (n0 * sqrt_n2)
|
|
288
|
+
C = -n0 * sqrt_n2 * np.sin(arg)
|
|
289
|
+
return np.array([[A, B], [C, A]])
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
class OpticalElement:
|
|
293
|
+
"""A single named optical element wrapping one ABCD matrix.
|
|
294
|
+
|
|
295
|
+
Parameters
|
|
296
|
+
----------
|
|
297
|
+
matrix : array_like, shape (2, 2)
|
|
298
|
+
The element's ray transfer matrix, e.g. from :func:`thin_lens` or
|
|
299
|
+
:func:`free_space`.
|
|
300
|
+
name : str, default=""
|
|
301
|
+
Human-readable label (e.g. ``"f=50mm lens"``).
|
|
302
|
+
length : float, default=0.0
|
|
303
|
+
Physical length occupied by this element along the optical axis
|
|
304
|
+
(zero for a "thin" element such as a lens or mirror).
|
|
305
|
+
|
|
306
|
+
Examples
|
|
307
|
+
--------
|
|
308
|
+
>>> elem = OpticalElement(thin_lens(0.05), name="focusing lens", length=0.0)
|
|
309
|
+
>>> elem.name
|
|
310
|
+
'focusing lens'
|
|
311
|
+
"""
|
|
312
|
+
|
|
313
|
+
def __init__(self, matrix, name="", length=0.0):
|
|
314
|
+
self.matrix = np.asarray(matrix, dtype=float)
|
|
315
|
+
self.name = name
|
|
316
|
+
self.length = length
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
class OpticalSystem:
|
|
320
|
+
"""An ordered sequence of :class:`OpticalElement` forming a compound optical system.
|
|
321
|
+
|
|
322
|
+
Parameters
|
|
323
|
+
----------
|
|
324
|
+
elements : list of OpticalElement
|
|
325
|
+
The elements in the order light passes through them: ``elements[0]``
|
|
326
|
+
is hit first.
|
|
327
|
+
|
|
328
|
+
Examples
|
|
329
|
+
--------
|
|
330
|
+
A single thin lens followed by propagation over its focal length
|
|
331
|
+
focuses any parallel ray bundle back to the axis:
|
|
332
|
+
|
|
333
|
+
>>> f = 0.1
|
|
334
|
+
>>> sys = OpticalSystem([
|
|
335
|
+
... OpticalElement(thin_lens(f), name="lens"),
|
|
336
|
+
... OpticalElement(free_space(f), name="propagate to focus"),
|
|
337
|
+
... ])
|
|
338
|
+
>>> trajectory = sys.trace_ray(y0=0.01, theta0=0.0)
|
|
339
|
+
>>> abs(float(trajectory[-1, 0])) < 1e-12
|
|
340
|
+
True
|
|
341
|
+
"""
|
|
342
|
+
|
|
343
|
+
def __init__(self, elements):
|
|
344
|
+
self.elements = list(elements)
|
|
345
|
+
|
|
346
|
+
def system_matrix(self):
|
|
347
|
+
"""Total ABCD matrix of the system, :math:`M = M_n \\cdots M_2 M_1`.
|
|
348
|
+
|
|
349
|
+
``elements[0]`` is applied first (it is the rightmost factor), so
|
|
350
|
+
it acts on the incoming ray state before any later element.
|
|
351
|
+
|
|
352
|
+
Returns
|
|
353
|
+
-------
|
|
354
|
+
ndarray of shape (2, 2)
|
|
355
|
+
"""
|
|
356
|
+
M = np.eye(2)
|
|
357
|
+
for element in self.elements:
|
|
358
|
+
M = element.matrix @ M
|
|
359
|
+
return M
|
|
360
|
+
|
|
361
|
+
def trace_ray(self, y0, theta0):
|
|
362
|
+
"""Trace a single ray through every element, recording its state at each step.
|
|
363
|
+
|
|
364
|
+
Parameters
|
|
365
|
+
----------
|
|
366
|
+
y0 : float
|
|
367
|
+
Initial height.
|
|
368
|
+
theta0 : float
|
|
369
|
+
Initial angle, in radians.
|
|
370
|
+
|
|
371
|
+
Returns
|
|
372
|
+
-------
|
|
373
|
+
ndarray of shape (n_elements + 1, 2)
|
|
374
|
+
Row 0 is the input state ``[y0, theta0]``; row ``i`` (for
|
|
375
|
+
``i >= 1``) is the state after passing through
|
|
376
|
+
``elements[0], ..., elements[i-1]``.
|
|
377
|
+
"""
|
|
378
|
+
n = len(self.elements)
|
|
379
|
+
states = np.empty((n + 1, 2))
|
|
380
|
+
state = np.array([float(y0), float(theta0)])
|
|
381
|
+
states[0] = state
|
|
382
|
+
for i, element in enumerate(self.elements):
|
|
383
|
+
state = element.matrix @ state
|
|
384
|
+
states[i + 1] = state
|
|
385
|
+
return states
|
|
386
|
+
|
|
387
|
+
@property
|
|
388
|
+
def stability_parameter(self):
|
|
389
|
+
"""Resonator stability parameter :math:`(A+D)/2` of the system matrix.
|
|
390
|
+
|
|
391
|
+
Returns
|
|
392
|
+
-------
|
|
393
|
+
float
|
|
394
|
+
"""
|
|
395
|
+
M = self.system_matrix()
|
|
396
|
+
return (M[0, 0] + M[1, 1]) / 2.0
|
|
397
|
+
|
|
398
|
+
def is_stable(self):
|
|
399
|
+
"""Whether the system satisfies the resonator stability condition :math:`|A+D| \\le 2`.
|
|
400
|
+
|
|
401
|
+
Returns
|
|
402
|
+
-------
|
|
403
|
+
bool
|
|
404
|
+
"""
|
|
405
|
+
return cavity_stability(self.system_matrix())
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
def cavity_round_trip_matrix(elements):
|
|
409
|
+
"""Round-trip ABCD matrix of a laser cavity, given its elements in traversal order.
|
|
410
|
+
|
|
411
|
+
Equivalent to ``OpticalSystem(elements).system_matrix()``; provided as
|
|
412
|
+
a standalone function for cavities analyzed without constructing a full
|
|
413
|
+
:class:`OpticalSystem`.
|
|
414
|
+
|
|
415
|
+
Parameters
|
|
416
|
+
----------
|
|
417
|
+
elements : list of OpticalElement
|
|
418
|
+
The elements encountered over one full round trip, in order.
|
|
419
|
+
|
|
420
|
+
Returns
|
|
421
|
+
-------
|
|
422
|
+
ndarray of shape (2, 2)
|
|
423
|
+
|
|
424
|
+
See Also
|
|
425
|
+
--------
|
|
426
|
+
cavity_stability : Test the resulting matrix for resonator stability.
|
|
427
|
+
|
|
428
|
+
Examples
|
|
429
|
+
--------
|
|
430
|
+
>>> M = cavity_round_trip_matrix([
|
|
431
|
+
... OpticalElement(spherical_mirror(2.0), name="M1"),
|
|
432
|
+
... OpticalElement(free_space(1.0), name="gap"),
|
|
433
|
+
... OpticalElement(spherical_mirror(2.0), name="M2"),
|
|
434
|
+
... OpticalElement(free_space(1.0), name="gap"),
|
|
435
|
+
... ])
|
|
436
|
+
>>> cavity_stability(M)
|
|
437
|
+
True
|
|
438
|
+
"""
|
|
439
|
+
M = np.eye(2)
|
|
440
|
+
for element in elements:
|
|
441
|
+
M = element.matrix @ M
|
|
442
|
+
return M
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
def cavity_stability(M):
|
|
446
|
+
"""Resonator stability test :math:`|A+D| \\le 2` (equivalently :math:`|\\operatorname{tr} M| \\le 2`).
|
|
447
|
+
|
|
448
|
+
A laser cavity with round-trip matrix ``M`` supports stable,
|
|
449
|
+
non-diverging paraxial ray bundles if and only if this holds.
|
|
450
|
+
|
|
451
|
+
Parameters
|
|
452
|
+
----------
|
|
453
|
+
M : array_like, shape (2, 2)
|
|
454
|
+
A round-trip ABCD matrix, e.g. from :func:`cavity_round_trip_matrix`
|
|
455
|
+
or :meth:`OpticalSystem.system_matrix`.
|
|
456
|
+
|
|
457
|
+
Returns
|
|
458
|
+
-------
|
|
459
|
+
bool
|
|
460
|
+
|
|
461
|
+
Examples
|
|
462
|
+
--------
|
|
463
|
+
>>> cavity_stability(np.array([[1.0, 0.0], [0.0, 1.0]]))
|
|
464
|
+
True
|
|
465
|
+
>>> cavity_stability(np.array([[3.0, 0.0], [0.0, 3.0]]))
|
|
466
|
+
False
|
|
467
|
+
"""
|
|
468
|
+
M = np.asarray(M, dtype=float)
|
|
469
|
+
return bool(abs(np.trace(M)) <= 2.0)
|
|
File without changes
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Animation smoke + sanity tests for physicskit.optics.visualizers.animate_diffraction_propagation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
import pytest
|
|
7
|
+
from matplotlib.animation import PillowWriter
|
|
8
|
+
|
|
9
|
+
from physicskit.optics.visualizers import animate_diffraction_propagation
|
|
10
|
+
from physicskit.optics.wave import angular_spectrum_propagate, double_slit_aperture, intensity
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@pytest.mark.slow
|
|
14
|
+
def test_diffraction_propagation_animation(tmp_path):
|
|
15
|
+
dx = 1e-3
|
|
16
|
+
ap = double_slit_aperture((64, 64), dx=dx, width=2e-3, separation=1e-2).astype(complex)
|
|
17
|
+
z_values = np.linspace(0.01, 2.0, 6)
|
|
18
|
+
anim = animate_diffraction_propagation(ap, wavelength=0.5e-3, z_values=z_values, dx=dx)
|
|
19
|
+
out = tmp_path / "diffraction.gif"
|
|
20
|
+
anim.save(out, writer=PillowWriter(fps=10))
|
|
21
|
+
assert out.exists() and out.stat().st_size > 0
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_pattern_widens_as_z_increases_matching_angular_spectrum_directly():
|
|
25
|
+
"""The animation just steps angular_spectrum_propagate over z -- sanity-check that the
|
|
26
|
+
interference pattern's spatial extent (its second moment) actually grows with z, as
|
|
27
|
+
expected of a diverging diffraction pattern developing from the near to the far field."""
|
|
28
|
+
dx = 1e-3
|
|
29
|
+
ap = double_slit_aperture((64, 64), dx=dx, width=2e-3, separation=1e-2).astype(complex)
|
|
30
|
+
z_small, z_large = 0.05, 3.0
|
|
31
|
+
x = (np.arange(64) - 32) * dx
|
|
32
|
+
X, _ = np.meshgrid(x, x)
|
|
33
|
+
|
|
34
|
+
def spread(z):
|
|
35
|
+
I = intensity(angular_spectrum_propagate(ap, wavelength=0.5e-3, z=z, dx=dx))
|
|
36
|
+
return np.sqrt(np.sum(I * X**2) / np.sum(I))
|
|
37
|
+
|
|
38
|
+
assert spread(z_large) > spread(z_small)
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pytest
|
|
3
|
+
|
|
4
|
+
from physicskit.optics.gaussian import (
|
|
5
|
+
GaussianBeam,
|
|
6
|
+
hermite_gaussian_mode,
|
|
7
|
+
laguerre_gaussian_mode,
|
|
8
|
+
m2_beam_waist,
|
|
9
|
+
propagate_q,
|
|
10
|
+
q_to_beam_params,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _free_space(d):
|
|
15
|
+
return np.array([[1.0, d], [0.0, 1.0]])
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _thin_lens(f):
|
|
19
|
+
return np.array([[1.0, 0.0], [-1.0 / f, 1.0]])
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_propagate_q_free_space_shifts_real_part():
|
|
23
|
+
q0 = 1j * 2.0
|
|
24
|
+
M = _free_space(3.0)
|
|
25
|
+
q1 = propagate_q(q0, M)
|
|
26
|
+
assert q1 == pytest.approx(3.0 + 2.0j)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_propagate_q_thin_lens_leaves_flat_wavefront_flat():
|
|
30
|
+
# A collimated (plane-wave, R=inf) beam is represented by q real & huge;
|
|
31
|
+
# instead check a lens leaves an on-axis waist's q consistent with 1/q - 1/f rule.
|
|
32
|
+
q0 = 1j * 5.0
|
|
33
|
+
f = 10.0
|
|
34
|
+
q1 = propagate_q(q0, _thin_lens(f))
|
|
35
|
+
assert 1.0 / q1 == pytest.approx(1.0 / q0 - 1.0 / f)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_q_to_beam_params_at_waist_gives_infinite_radius_of_curvature():
|
|
39
|
+
zR = 4.0
|
|
40
|
+
wavelength = 1.0e-3
|
|
41
|
+
w0 = np.sqrt(wavelength * zR / np.pi)
|
|
42
|
+
w, R = q_to_beam_params(1j * zR, wavelength)
|
|
43
|
+
assert R == np.inf
|
|
44
|
+
assert w == pytest.approx(w0)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_symmetric_two_lens_imaging_system_reproduces_object_waist():
|
|
48
|
+
# Symmetric imaging system built directly from the ABCD matrices given
|
|
49
|
+
# in the house convention: free_space(f) -> lens(f) -> free_space(f) ->
|
|
50
|
+
# lens(f) -> free_space(f), starting from a beam waist at the object
|
|
51
|
+
# plane. This should reproduce the object-plane waist size at the image
|
|
52
|
+
# plane (unit magnification, by symmetry).
|
|
53
|
+
wavelength = 0.5e-3
|
|
54
|
+
w0 = 0.1
|
|
55
|
+
f = 50.0
|
|
56
|
+
|
|
57
|
+
beam = GaussianBeam(wavelength=wavelength, w0=w0, z0=0.0)
|
|
58
|
+
q0 = beam.q_parameter(0.0)
|
|
59
|
+
|
|
60
|
+
M = _free_space(f) @ _thin_lens(f) @ _free_space(f) @ _thin_lens(f) @ _free_space(f)
|
|
61
|
+
q_image = propagate_q(q0, M)
|
|
62
|
+
w_image, _ = q_to_beam_params(q_image, wavelength)
|
|
63
|
+
|
|
64
|
+
assert w_image == pytest.approx(w0, rel=1e-6)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def test_waist_at_z0_equals_w0_exactly():
|
|
68
|
+
beam = GaussianBeam(wavelength=1.0e-3, w0=0.2, z0=1.5)
|
|
69
|
+
assert beam.waist(beam.z0) == pytest.approx(0.2)
|
|
70
|
+
assert beam.waist(beam.z0) == 0.2
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def test_radius_of_curvature_is_infinite_at_the_waist():
|
|
74
|
+
beam = GaussianBeam(wavelength=1.0e-3, w0=0.2, z0=1.5)
|
|
75
|
+
assert beam.radius_of_curvature(beam.z0) == np.inf
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def test_radius_of_curvature_far_from_waist_approaches_z_minus_z0():
|
|
79
|
+
beam = GaussianBeam(wavelength=1.0e-3, w0=0.05, z0=0.0)
|
|
80
|
+
z = 1.0e6 # z >> zR, so R(z) -> z - z0
|
|
81
|
+
assert beam.radius_of_curvature(z) == pytest.approx(z, rel=1e-3)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_radius_of_curvature_accepts_an_array_of_positions():
|
|
85
|
+
beam = GaussianBeam(wavelength=1.0e-3, w0=0.2, z0=1.5)
|
|
86
|
+
z = np.array([1.5, 1.0e6])
|
|
87
|
+
R = beam.radius_of_curvature(z)
|
|
88
|
+
assert isinstance(R, np.ndarray)
|
|
89
|
+
assert R[0] == np.inf
|
|
90
|
+
assert R[1] == pytest.approx(z[1], rel=1e-3)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def test_divergence_angle_matches_far_field_waist_slope():
|
|
94
|
+
beam = GaussianBeam(wavelength=1.0e-3, w0=0.02, z0=0.0)
|
|
95
|
+
z_far = 1000.0 * beam.rayleigh_range
|
|
96
|
+
slope = beam.waist(z_far) / (z_far - beam.z0)
|
|
97
|
+
assert slope == pytest.approx(beam.divergence_angle, rel=1e-3)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def test_gouy_phase_is_zero_at_waist_and_approaches_pi_over_2_far_away():
|
|
101
|
+
beam = GaussianBeam(wavelength=1.0e-3, w0=0.05, z0=0.0)
|
|
102
|
+
assert beam.gouy_phase(0.0) == pytest.approx(0.0)
|
|
103
|
+
assert beam.gouy_phase(1.0e6) == pytest.approx(np.pi / 2.0, abs=1e-3)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def test_hermite_gaussian_mode_shape_and_fundamental_matches_gaussian_beam():
|
|
107
|
+
beam = GaussianBeam(wavelength=1.0, w0=1.0, z0=0.0)
|
|
108
|
+
x = np.linspace(-2, 2, 9)
|
|
109
|
+
y = np.linspace(-2, 2, 9)
|
|
110
|
+
X, Y = np.meshgrid(x, y)
|
|
111
|
+
field = hermite_gaussian_mode(X, Y, 0.0, beam, 0, 0)
|
|
112
|
+
assert field.shape == X.shape
|
|
113
|
+
assert np.iscomplexobj(field)
|
|
114
|
+
expected_intensity = np.exp(-2.0 * (X**2 + Y**2) / beam.waist(0.0) ** 2)
|
|
115
|
+
assert np.allclose(np.abs(field) ** 2, expected_intensity)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def test_hermite_gaussian_mode_gouy_phase_scales_with_mode_order():
|
|
119
|
+
# Use x=1 (rather than the axis) so H_0(sqrt2 x/w) and H_1(sqrt2 x/w)
|
|
120
|
+
# are both strictly positive real numbers, isolating the Gouy phase
|
|
121
|
+
# contribution (m+n+1)*zeta(z) from any sign flip of the real
|
|
122
|
+
# Hermite-polynomial amplitude factor.
|
|
123
|
+
beam = GaussianBeam(wavelength=1.0, w0=1.0, z0=0.0)
|
|
124
|
+
z = 3.0 * beam.rayleigh_range
|
|
125
|
+
u00 = hermite_gaussian_mode(1.0, 0.0, z, beam, 0, 0)
|
|
126
|
+
u10 = hermite_gaussian_mode(1.0, 0.0, z, beam, 1, 0)
|
|
127
|
+
phase_diff = np.angle(u10) - np.angle(u00)
|
|
128
|
+
expected = beam.gouy_phase(z) # (1+0+1)*zeta - (0+0+1)*zeta = zeta
|
|
129
|
+
assert phase_diff == pytest.approx(expected, abs=1e-8)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def test_laguerre_gaussian_mode_shape_and_ring_structure():
|
|
133
|
+
beam = GaussianBeam(wavelength=1.0, w0=1.0, z0=0.0)
|
|
134
|
+
r = np.linspace(0, 3, 50)
|
|
135
|
+
phi = np.zeros_like(r)
|
|
136
|
+
field = laguerre_gaussian_mode(r, phi, 0.0, beam, l=1, p=0)
|
|
137
|
+
assert field.shape == r.shape
|
|
138
|
+
# LG_0^1 has a phase singularity (zero amplitude) on-axis
|
|
139
|
+
assert abs(field[0]) == pytest.approx(0.0, abs=1e-12)
|
|
140
|
+
assert np.max(np.abs(field)) > 0.0
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def test_laguerre_gaussian_mode_orbital_phase_winds_by_2pi_l():
|
|
144
|
+
beam = GaussianBeam(wavelength=1.0, w0=1.0, z0=0.0)
|
|
145
|
+
r = np.full(9, 0.5)
|
|
146
|
+
phi = np.linspace(0.0, 2 * np.pi, 9, endpoint=False)
|
|
147
|
+
field = laguerre_gaussian_mode(r, phi, 0.0, beam, l=2, p=0)
|
|
148
|
+
phase = np.unwrap(np.angle(field))
|
|
149
|
+
total_winding = phase[-1] + (phase[-1] - phase[-2]) - phase[0]
|
|
150
|
+
assert np.round(total_winding / (2 * np.pi)) == pytest.approx(2.0, abs=0.2)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def test_m2_beam_waist_reduces_to_ideal_at_M2_equals_1():
|
|
154
|
+
wavelength = 1.0e-3
|
|
155
|
+
w0 = 0.1
|
|
156
|
+
z = np.linspace(-5, 5, 11)
|
|
157
|
+
beam = GaussianBeam(wavelength=wavelength, w0=w0, z0=0.0)
|
|
158
|
+
ideal = beam.waist(z)
|
|
159
|
+
m2 = m2_beam_waist(z, wavelength, w0, M2=1.0)
|
|
160
|
+
assert np.allclose(ideal, m2)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def test_m2_beam_waist_diverges_faster_for_larger_M2():
|
|
164
|
+
wavelength = 1.0e-3
|
|
165
|
+
w0 = 0.1
|
|
166
|
+
z = 10.0
|
|
167
|
+
w_ideal = m2_beam_waist(z, wavelength, w0, M2=1.0)
|
|
168
|
+
w_poor = m2_beam_waist(z, wavelength, w0, M2=3.0)
|
|
169
|
+
assert w_poor > w_ideal
|
|
170
|
+
assert m2_beam_waist(0.0, wavelength, w0, M2=3.0) == pytest.approx(w0)
|