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,264 @@
|
|
|
1
|
+
"""Plotting helpers: ray traces, Gaussian beam envelopes, diffraction patterns, Wigner surfaces.
|
|
2
|
+
|
|
3
|
+
Matplotlib is used for 2D plots (ray traces through an optical system, beam
|
|
4
|
+
waist envelopes, diffraction-pattern heatmaps), and Plotly for the
|
|
5
|
+
interactive 3D Wigner phase-space surface. Every function returns its figure
|
|
6
|
+
object rather than calling ``show()``, so it composes cleanly into larger
|
|
7
|
+
figures or headless pipelines.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import matplotlib.pyplot as plt
|
|
13
|
+
import numpy as np
|
|
14
|
+
import plotly.graph_objects as go
|
|
15
|
+
from matplotlib.animation import FuncAnimation
|
|
16
|
+
|
|
17
|
+
from physicskit.optics.wave import angular_spectrum_propagate, intensity
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"plot_ray_trace",
|
|
21
|
+
"plot_beam_envelope",
|
|
22
|
+
"plot_diffraction_pattern",
|
|
23
|
+
"interactive_wigner_surface",
|
|
24
|
+
"animate_diffraction_propagation",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def plot_ray_trace(system, y0, theta0, ax=None):
|
|
29
|
+
"""Plot a ray's height through each element of an :class:`~physicskit.optics.ray.OpticalSystem`.
|
|
30
|
+
|
|
31
|
+
Parameters
|
|
32
|
+
----------
|
|
33
|
+
system : physicskit.optics.ray.OpticalSystem
|
|
34
|
+
The optical system to trace the ray through.
|
|
35
|
+
y0 : float
|
|
36
|
+
Initial ray height.
|
|
37
|
+
theta0 : float
|
|
38
|
+
Initial ray angle.
|
|
39
|
+
ax : matplotlib.axes.Axes, optional
|
|
40
|
+
Axes to draw into; a new figure is created if omitted.
|
|
41
|
+
|
|
42
|
+
Returns
|
|
43
|
+
-------
|
|
44
|
+
fig : matplotlib.figure.Figure
|
|
45
|
+
ax : matplotlib.axes.Axes
|
|
46
|
+
|
|
47
|
+
Examples
|
|
48
|
+
--------
|
|
49
|
+
>>> from physicskit.optics.ray import OpticalElement, OpticalSystem, free_space, thin_lens
|
|
50
|
+
>>> system = OpticalSystem([
|
|
51
|
+
... OpticalElement(free_space(1.0), name="d1", length=1.0),
|
|
52
|
+
... OpticalElement(thin_lens(1.0), name="lens"),
|
|
53
|
+
... OpticalElement(free_space(1.0), name="d2", length=1.0),
|
|
54
|
+
... ])
|
|
55
|
+
>>> fig, ax = plot_ray_trace(system, y0=0.5, theta0=0.0)
|
|
56
|
+
>>> isinstance(fig, plt.Figure)
|
|
57
|
+
True
|
|
58
|
+
>>> len(ax.lines[0].get_xdata())
|
|
59
|
+
4
|
|
60
|
+
"""
|
|
61
|
+
trace = system.trace_ray(y0, theta0)
|
|
62
|
+
positions = np.concatenate(([0.0], np.cumsum([el.length for el in system.elements])))
|
|
63
|
+
if ax is None:
|
|
64
|
+
fig, ax = plt.subplots()
|
|
65
|
+
else:
|
|
66
|
+
fig = ax.figure
|
|
67
|
+
ax.plot(positions, trace[:, 0], marker="o")
|
|
68
|
+
ax.set_xlabel("position")
|
|
69
|
+
ax.set_ylabel("ray height y")
|
|
70
|
+
ax.axhline(0.0, color="k", linewidth=0.5)
|
|
71
|
+
return fig, ax
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def plot_beam_envelope(beam, z_range, n_points=200, ax=None):
|
|
75
|
+
r"""Plot a Gaussian beam's :math:`\pm w(z)` waist envelope over a propagation range.
|
|
76
|
+
|
|
77
|
+
Parameters
|
|
78
|
+
----------
|
|
79
|
+
beam : physicskit.optics.gaussian.GaussianBeam
|
|
80
|
+
The beam to plot.
|
|
81
|
+
z_range : tuple(float, float)
|
|
82
|
+
``(z_min, z_max)`` propagation-axis range to plot over.
|
|
83
|
+
n_points : int, default=200
|
|
84
|
+
Number of sampled points.
|
|
85
|
+
ax : matplotlib.axes.Axes, optional
|
|
86
|
+
Axes to draw into; a new figure is created if omitted.
|
|
87
|
+
|
|
88
|
+
Returns
|
|
89
|
+
-------
|
|
90
|
+
fig : matplotlib.figure.Figure
|
|
91
|
+
ax : matplotlib.axes.Axes
|
|
92
|
+
|
|
93
|
+
Examples
|
|
94
|
+
--------
|
|
95
|
+
>>> from physicskit.optics.gaussian import GaussianBeam
|
|
96
|
+
>>> beam = GaussianBeam(wavelength=0.5e-3, w0=0.1, z0=0.0)
|
|
97
|
+
>>> fig, ax = plot_beam_envelope(beam, z_range=(-10, 10))
|
|
98
|
+
>>> isinstance(fig, plt.Figure)
|
|
99
|
+
True
|
|
100
|
+
>>> bool(ax.lines[0].get_ydata().min() >= 0)
|
|
101
|
+
True
|
|
102
|
+
"""
|
|
103
|
+
z = np.linspace(z_range[0], z_range[1], n_points)
|
|
104
|
+
w = beam.waist(z)
|
|
105
|
+
if ax is None:
|
|
106
|
+
fig, ax = plt.subplots()
|
|
107
|
+
else:
|
|
108
|
+
fig = ax.figure
|
|
109
|
+
ax.plot(z, w, color="C0")
|
|
110
|
+
ax.plot(z, -w, color="C0")
|
|
111
|
+
ax.fill_between(z, -w, w, alpha=0.15, color="C0")
|
|
112
|
+
ax.axvline(beam.z0, color="k", linewidth=0.5, linestyle="--")
|
|
113
|
+
ax.set_xlabel("z")
|
|
114
|
+
ax.set_ylabel("beam radius w(z)")
|
|
115
|
+
return fig, ax
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def plot_diffraction_pattern(U, dx, ax=None, log_scale=False):
|
|
119
|
+
"""Plot the intensity pattern of a complex diffracted field as a 2D heatmap.
|
|
120
|
+
|
|
121
|
+
Parameters
|
|
122
|
+
----------
|
|
123
|
+
U : ndarray of shape (Ny, Nx)
|
|
124
|
+
Complex field amplitude (e.g. from
|
|
125
|
+
:func:`physicskit.optics.wave.angular_spectrum_propagate`).
|
|
126
|
+
dx : float
|
|
127
|
+
Grid spacing, used to set physical axis extents.
|
|
128
|
+
ax : matplotlib.axes.Axes, optional
|
|
129
|
+
Axes to draw into; a new figure is created if omitted.
|
|
130
|
+
log_scale : bool, default=False
|
|
131
|
+
If True, plot ``log10(intensity + eps)`` to reveal faint diffraction
|
|
132
|
+
fringes alongside the bright central peak.
|
|
133
|
+
|
|
134
|
+
Returns
|
|
135
|
+
-------
|
|
136
|
+
fig : matplotlib.figure.Figure
|
|
137
|
+
ax : matplotlib.axes.Axes
|
|
138
|
+
|
|
139
|
+
Examples
|
|
140
|
+
--------
|
|
141
|
+
>>> from physicskit.optics.wave import circular_aperture
|
|
142
|
+
>>> U = circular_aperture((64, 64), dx=1e-3, radius=5e-3).astype(complex)
|
|
143
|
+
>>> fig, ax = plot_diffraction_pattern(U, dx=1e-3)
|
|
144
|
+
>>> isinstance(fig, plt.Figure)
|
|
145
|
+
True
|
|
146
|
+
"""
|
|
147
|
+
I = intensity(U)
|
|
148
|
+
if log_scale:
|
|
149
|
+
I = np.log10(I + 1e-12 * I.max())
|
|
150
|
+
ny, nx = I.shape
|
|
151
|
+
extent = (-nx / 2 * dx, nx / 2 * dx, -ny / 2 * dx, ny / 2 * dx)
|
|
152
|
+
if ax is None:
|
|
153
|
+
fig, ax = plt.subplots()
|
|
154
|
+
else:
|
|
155
|
+
fig = ax.figure
|
|
156
|
+
im = ax.imshow(I, extent=extent, origin="lower", cmap="inferno")
|
|
157
|
+
fig.colorbar(im, ax=ax, label="log10 intensity" if log_scale else "intensity")
|
|
158
|
+
ax.set_xlabel("x")
|
|
159
|
+
ax.set_ylabel("y")
|
|
160
|
+
return fig, ax
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def animate_diffraction_propagation(aperture, wavelength, z_values, dx, log_scale=False, interval=100, ax=None):
|
|
164
|
+
"""Animate the diffraction pattern developing as propagation distance ``z`` increases.
|
|
165
|
+
|
|
166
|
+
Steps :func:`physicskit.optics.wave.angular_spectrum_propagate` over
|
|
167
|
+
each distance in ``z_values`` (treating ``z`` as the animation's "time"
|
|
168
|
+
axis -- the standard way to visualize the Fresnel-to-Fraunhofer
|
|
169
|
+
development of a diffraction pattern), and animates the resulting
|
|
170
|
+
intensity as an imshow heatmap: a double-slit aperture's near-field
|
|
171
|
+
wavefronts visibly evolve into the far-field interference fringes as
|
|
172
|
+
``z`` grows.
|
|
173
|
+
|
|
174
|
+
Parameters
|
|
175
|
+
----------
|
|
176
|
+
aperture : ndarray of shape (Ny, Nx)
|
|
177
|
+
Input field immediately after the aperture (e.g. from
|
|
178
|
+
:func:`physicskit.optics.wave.double_slit_aperture`).
|
|
179
|
+
wavelength : float
|
|
180
|
+
Wavelength.
|
|
181
|
+
z_values : ndarray
|
|
182
|
+
Sequence of propagation distances to sweep over.
|
|
183
|
+
dx : float
|
|
184
|
+
Grid spacing (same for input and output, since
|
|
185
|
+
:func:`~physicskit.optics.wave.angular_spectrum_propagate` stays on the same grid).
|
|
186
|
+
log_scale : bool, default=False
|
|
187
|
+
If True, plot ``log10(intensity + eps)`` to reveal faint fringes.
|
|
188
|
+
interval : int, default=100
|
|
189
|
+
Delay between frames, in milliseconds.
|
|
190
|
+
ax : matplotlib.axes.Axes, optional
|
|
191
|
+
Axes to draw into; a new figure is created if omitted.
|
|
192
|
+
|
|
193
|
+
Returns
|
|
194
|
+
-------
|
|
195
|
+
matplotlib.animation.FuncAnimation
|
|
196
|
+
|
|
197
|
+
Examples
|
|
198
|
+
--------
|
|
199
|
+
>>> import numpy as np
|
|
200
|
+
>>> from physicskit.optics.wave import double_slit_aperture
|
|
201
|
+
>>> ap = double_slit_aperture((64, 64), dx=1e-3, width=2e-3, separation=1e-2).astype(complex)
|
|
202
|
+
>>> z_values = np.linspace(0.01, 2.0, 5)
|
|
203
|
+
>>> anim = animate_diffraction_propagation(ap, wavelength=0.5e-3, z_values=z_values, dx=1e-3)
|
|
204
|
+
>>> isinstance(anim, FuncAnimation)
|
|
205
|
+
True
|
|
206
|
+
"""
|
|
207
|
+
frames = np.stack([intensity(angular_spectrum_propagate(aperture, wavelength, z, dx)) for z in z_values])
|
|
208
|
+
if log_scale:
|
|
209
|
+
frames = np.log10(frames + 1e-12 * frames.max())
|
|
210
|
+
ny, nx = frames.shape[1:]
|
|
211
|
+
extent = (-nx / 2 * dx, nx / 2 * dx, -ny / 2 * dx, ny / 2 * dx)
|
|
212
|
+
if ax is None:
|
|
213
|
+
fig, ax = plt.subplots()
|
|
214
|
+
else:
|
|
215
|
+
fig = ax.figure
|
|
216
|
+
im = ax.imshow(frames[0], extent=extent, origin="lower", cmap="inferno", vmin=frames.min(), vmax=frames.max())
|
|
217
|
+
fig.colorbar(im, ax=ax, label="log10 intensity" if log_scale else "intensity")
|
|
218
|
+
ax.set_xlabel("x")
|
|
219
|
+
ax.set_ylabel("y")
|
|
220
|
+
|
|
221
|
+
def update(i):
|
|
222
|
+
im.set_data(frames[i])
|
|
223
|
+
ax.set_title(f"z = {z_values[i]:.4g}")
|
|
224
|
+
return (im,)
|
|
225
|
+
|
|
226
|
+
return FuncAnimation(fig, update, frames=len(z_values), interval=interval, blit=False)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def interactive_wigner_surface(W, x_grid, p_grid, title=None):
|
|
230
|
+
"""An interactive 3D Plotly surface plot of a Wigner quasi-probability distribution.
|
|
231
|
+
|
|
232
|
+
Parameters
|
|
233
|
+
----------
|
|
234
|
+
W : ndarray of shape (Nx, Np)
|
|
235
|
+
Wigner function, e.g. from
|
|
236
|
+
:func:`physicskit.optics.quantum_optics.compute_wigner_function`.
|
|
237
|
+
x_grid : ndarray of shape (Nx,)
|
|
238
|
+
Grid of x quadrature values.
|
|
239
|
+
p_grid : ndarray of shape (Np,)
|
|
240
|
+
Grid of p quadrature values.
|
|
241
|
+
title : str, optional
|
|
242
|
+
Plot title.
|
|
243
|
+
|
|
244
|
+
Returns
|
|
245
|
+
-------
|
|
246
|
+
plotly.graph_objects.Figure
|
|
247
|
+
|
|
248
|
+
Examples
|
|
249
|
+
--------
|
|
250
|
+
>>> import numpy as np
|
|
251
|
+
>>> from physicskit.optics.quantum_optics import coherent_state, compute_wigner_function
|
|
252
|
+
>>> x = np.linspace(-4, 4, 40)
|
|
253
|
+
>>> p = np.linspace(-4, 4, 40)
|
|
254
|
+
>>> W = compute_wigner_function(coherent_state(1.0, 20), x, p)
|
|
255
|
+
>>> fig = interactive_wigner_surface(W, x, p)
|
|
256
|
+
>>> isinstance(fig, go.Figure)
|
|
257
|
+
True
|
|
258
|
+
"""
|
|
259
|
+
fig = go.Figure(data=[go.Surface(x=x_grid, y=p_grid, z=W.T, colorscale="RdBu", cmid=0.0)])
|
|
260
|
+
fig.update_layout(
|
|
261
|
+
title=title,
|
|
262
|
+
scene={"xaxis_title": "x", "yaxis_title": "p", "zaxis_title": "W(x,p)"},
|
|
263
|
+
)
|
|
264
|
+
return fig
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
"""Scalar wave optics: FFT-based Fresnel, Fraunhofer, and angular-spectrum diffraction.
|
|
2
|
+
|
|
3
|
+
Treats light as a scalar complex field :math:`U(x,y)` obeying the
|
|
4
|
+
Helmholtz equation, and propagates it between parallel planes using the
|
|
5
|
+
three workhorse methods of Fourier optics (Goodman, *Introduction to
|
|
6
|
+
Fourier Optics*):
|
|
7
|
+
|
|
8
|
+
- :func:`angular_spectrum_propagate` -- the exact scalar-diffraction
|
|
9
|
+
solution, decomposing the field into plane waves (its 2D Fourier
|
|
10
|
+
transform), advancing each by its own propagation phase
|
|
11
|
+
:math:`e^{ik_z z}`, and re-synthesizing. Valid at any distance,
|
|
12
|
+
including deep into the near field, as long as the grid resolves the
|
|
13
|
+
field's spatial frequencies.
|
|
14
|
+
- :func:`fresnel_diffraction` -- the paraxial (parabolic-wave) near-field
|
|
15
|
+
approximation, computable as a single Fourier transform of the aperture
|
|
16
|
+
times a quadratic phase.
|
|
17
|
+
- :func:`fraunhofer_diffraction` -- the further far-field approximation
|
|
18
|
+
valid once :math:`z` is large enough that the quadratic phase across the
|
|
19
|
+
aperture itself is negligible; the diffraction pattern becomes simply
|
|
20
|
+
the (scaled, phase-prefactored) Fourier transform of the aperture, the
|
|
21
|
+
basis for classic single- and double-slit interference patterns.
|
|
22
|
+
|
|
23
|
+
All propagation routines expect a field sampled on a uniform square-pixel
|
|
24
|
+
grid of spacing ``dx`` (same length units as ``wavelength``), and treat
|
|
25
|
+
array index ``(Ny//2, Nx//2)`` as the on-axis origin.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
import numpy as np
|
|
31
|
+
|
|
32
|
+
__all__ = [
|
|
33
|
+
"circular_aperture",
|
|
34
|
+
"single_slit_aperture",
|
|
35
|
+
"double_slit_aperture",
|
|
36
|
+
"fraunhofer_diffraction",
|
|
37
|
+
"fresnel_diffraction",
|
|
38
|
+
"angular_spectrum_propagate",
|
|
39
|
+
"intensity",
|
|
40
|
+
"field_grid",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def field_grid(shape, dx):
|
|
45
|
+
"""Centered physical-coordinate grid for an array of the given ``shape``.
|
|
46
|
+
|
|
47
|
+
Parameters
|
|
48
|
+
----------
|
|
49
|
+
shape : tuple of int
|
|
50
|
+
``(Ny, Nx)`` array shape.
|
|
51
|
+
dx : float
|
|
52
|
+
Grid spacing (same units as ``wavelength`` elsewhere in this
|
|
53
|
+
module).
|
|
54
|
+
|
|
55
|
+
Returns
|
|
56
|
+
-------
|
|
57
|
+
X, Y : ndarray of shape (Ny, Nx)
|
|
58
|
+
Cartesian coordinates, with the origin at index ``(Ny//2, Nx//2)``.
|
|
59
|
+
|
|
60
|
+
Examples
|
|
61
|
+
--------
|
|
62
|
+
>>> X, Y = field_grid((4, 4), dx=1.0)
|
|
63
|
+
>>> X[0].tolist()
|
|
64
|
+
[-2.0, -1.0, 0.0, 1.0]
|
|
65
|
+
"""
|
|
66
|
+
Ny, Nx = shape
|
|
67
|
+
x = (np.arange(Nx) - Nx // 2) * dx
|
|
68
|
+
y = (np.arange(Ny) - Ny // 2) * dx
|
|
69
|
+
X, Y = np.meshgrid(x, y)
|
|
70
|
+
return X, Y
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def circular_aperture(shape, dx, radius):
|
|
74
|
+
"""A circular (disk) aperture, transmittance 1 inside, 0 outside.
|
|
75
|
+
|
|
76
|
+
Parameters
|
|
77
|
+
----------
|
|
78
|
+
shape : tuple of int
|
|
79
|
+
``(Ny, Nx)`` array shape.
|
|
80
|
+
dx : float
|
|
81
|
+
Grid spacing.
|
|
82
|
+
radius : float
|
|
83
|
+
Aperture radius.
|
|
84
|
+
|
|
85
|
+
Returns
|
|
86
|
+
-------
|
|
87
|
+
ndarray of shape (Ny, Nx)
|
|
88
|
+
Real-valued array of 1.0 (open) and 0.0 (blocked).
|
|
89
|
+
|
|
90
|
+
Examples
|
|
91
|
+
--------
|
|
92
|
+
>>> ap = circular_aperture((5, 5), dx=1.0, radius=1.5)
|
|
93
|
+
>>> float(ap[2, 2])
|
|
94
|
+
1.0
|
|
95
|
+
>>> float(ap[0, 0])
|
|
96
|
+
0.0
|
|
97
|
+
"""
|
|
98
|
+
X, Y = field_grid(shape, dx)
|
|
99
|
+
return np.where(X**2 + Y**2 <= radius**2, 1.0, 0.0)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def single_slit_aperture(shape, dx, width):
|
|
103
|
+
"""A single slit of the given ``width`` along x, open along the full height in y.
|
|
104
|
+
|
|
105
|
+
Parameters
|
|
106
|
+
----------
|
|
107
|
+
shape : tuple of int
|
|
108
|
+
``(Ny, Nx)`` array shape.
|
|
109
|
+
dx : float
|
|
110
|
+
Grid spacing.
|
|
111
|
+
width : float
|
|
112
|
+
Full width of the slit opening (in the x direction).
|
|
113
|
+
|
|
114
|
+
Returns
|
|
115
|
+
-------
|
|
116
|
+
ndarray of shape (Ny, Nx)
|
|
117
|
+
Real-valued array of 1.0 (open) and 0.0 (blocked).
|
|
118
|
+
|
|
119
|
+
Examples
|
|
120
|
+
--------
|
|
121
|
+
>>> ap = single_slit_aperture((3, 7), dx=1.0, width=2.5)
|
|
122
|
+
>>> ap[0].tolist()
|
|
123
|
+
[0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0]
|
|
124
|
+
"""
|
|
125
|
+
X, _ = field_grid(shape, dx)
|
|
126
|
+
return np.where(np.abs(X) <= width / 2.0, 1.0, 0.0)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def double_slit_aperture(shape, dx, width, separation):
|
|
130
|
+
"""Two parallel slits of the given ``width``, centers separated by ``separation``.
|
|
131
|
+
|
|
132
|
+
Parameters
|
|
133
|
+
----------
|
|
134
|
+
shape : tuple of int
|
|
135
|
+
``(Ny, Nx)`` array shape.
|
|
136
|
+
dx : float
|
|
137
|
+
Grid spacing.
|
|
138
|
+
width : float
|
|
139
|
+
Full width of each slit opening.
|
|
140
|
+
separation : float
|
|
141
|
+
Center-to-center distance between the two slits.
|
|
142
|
+
|
|
143
|
+
Returns
|
|
144
|
+
-------
|
|
145
|
+
ndarray of shape (Ny, Nx)
|
|
146
|
+
Real-valued array of 1.0 (open) and 0.0 (blocked).
|
|
147
|
+
|
|
148
|
+
Examples
|
|
149
|
+
--------
|
|
150
|
+
>>> ap = double_slit_aperture((3, 11), dx=1.0, width=1.5, separation=6.0)
|
|
151
|
+
>>> ap[0].tolist()
|
|
152
|
+
[0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0]
|
|
153
|
+
"""
|
|
154
|
+
X, _ = field_grid(shape, dx)
|
|
155
|
+
left = np.abs(X + separation / 2.0) <= width / 2.0
|
|
156
|
+
right = np.abs(X - separation / 2.0) <= width / 2.0
|
|
157
|
+
return np.where(left | right, 1.0, 0.0)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def fraunhofer_diffraction(aperture, wavelength, z, dx):
|
|
161
|
+
"""Far-field (Fraunhofer) diffraction pattern of ``aperture`` at distance ``z``.
|
|
162
|
+
|
|
163
|
+
Uses the standard single-Fourier-transform Fraunhofer formula (Goodman
|
|
164
|
+
convention),
|
|
165
|
+
|
|
166
|
+
.. math::
|
|
167
|
+
|
|
168
|
+
U(x',y') = \\frac{e^{ikz}}{i\\lambda z}
|
|
169
|
+
e^{i\\frac{k}{2z}(x'^2+y'^2)}
|
|
170
|
+
\\iint U_0(x,y)\\,
|
|
171
|
+
e^{-i\\frac{2\\pi}{\\lambda z}(x x' + y y')}\\,dx\\,dy,
|
|
172
|
+
|
|
173
|
+
with :math:`k = 2\\pi/\\lambda`. The double integral is exactly a 2D
|
|
174
|
+
Fourier transform of ``aperture`` evaluated at spatial frequency
|
|
175
|
+
:math:`(f_x, f_y) = (x'/(\\lambda z), y'/(\\lambda z))`, computed here
|
|
176
|
+
with a single FFT (the discrete sum is converted to a continuum
|
|
177
|
+
integral by the ``dx**2`` pixel-area factor). The output array lives
|
|
178
|
+
on its own natural grid of spacing :math:`\\lambda z / (N\\,dx)`, not
|
|
179
|
+
the input grid.
|
|
180
|
+
|
|
181
|
+
Parameters
|
|
182
|
+
----------
|
|
183
|
+
aperture : ndarray of shape (Ny, Nx)
|
|
184
|
+
Input field (real or complex) immediately after the aperture.
|
|
185
|
+
wavelength : float
|
|
186
|
+
Wavelength :math:`\\lambda`, in the same length units as ``dx``.
|
|
187
|
+
z : float
|
|
188
|
+
Propagation distance to the observation plane.
|
|
189
|
+
dx : float
|
|
190
|
+
Input grid spacing.
|
|
191
|
+
|
|
192
|
+
Returns
|
|
193
|
+
-------
|
|
194
|
+
ndarray of shape (Ny, Nx), complex
|
|
195
|
+
The far-field complex amplitude.
|
|
196
|
+
|
|
197
|
+
See Also
|
|
198
|
+
--------
|
|
199
|
+
fresnel_diffraction : The near-field approximation this is a further limit of.
|
|
200
|
+
|
|
201
|
+
Examples
|
|
202
|
+
--------
|
|
203
|
+
>>> ap = circular_aperture((64, 64), dx=0.01, radius=0.05)
|
|
204
|
+
>>> U = fraunhofer_diffraction(ap, wavelength=0.5e-3, z=2.0, dx=0.01)
|
|
205
|
+
>>> U.shape
|
|
206
|
+
(64, 64)
|
|
207
|
+
>>> bool(intensity(U)[32, 32] == intensity(U).max())
|
|
208
|
+
True
|
|
209
|
+
"""
|
|
210
|
+
aperture = np.asarray(aperture)
|
|
211
|
+
k = 2.0 * np.pi / wavelength
|
|
212
|
+
Ny, Nx = aperture.shape
|
|
213
|
+
FT = np.fft.fftshift(np.fft.fft2(np.fft.ifftshift(aperture))) * dx * dx
|
|
214
|
+
fx = np.fft.fftshift(np.fft.fftfreq(Nx, d=dx))
|
|
215
|
+
fy = np.fft.fftshift(np.fft.fftfreq(Ny, d=dx))
|
|
216
|
+
Xp, Yp = np.meshgrid(wavelength * z * fx, wavelength * z * fy)
|
|
217
|
+
prefactor = (np.exp(1j * k * z) / (1j * wavelength * z)) * np.exp(1j * k / (2.0 * z) * (Xp**2 + Yp**2))
|
|
218
|
+
return prefactor * FT
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def fresnel_diffraction(aperture, wavelength, z, dx):
|
|
222
|
+
"""Near-field (Fresnel) diffraction pattern of ``aperture`` at distance ``z``.
|
|
223
|
+
|
|
224
|
+
The single-Fourier-transform ("one-step") Fresnel propagation method:
|
|
225
|
+
multiply the input field by the Fresnel quadratic phase, Fourier
|
|
226
|
+
transform, then multiply by the corresponding output quadratic phase
|
|
227
|
+
and prefactor,
|
|
228
|
+
|
|
229
|
+
.. math::
|
|
230
|
+
|
|
231
|
+
U(x',y') = \\frac{e^{ikz}}{i\\lambda z}
|
|
232
|
+
e^{i\\frac{k}{2z}(x'^2+y'^2)}\\,
|
|
233
|
+
\\mathcal{F}\\!\\left[U_0(x,y)\\,
|
|
234
|
+
e^{i\\frac{k}{2z}(x^2+y^2)}\\right]_{f_x=x'/(\\lambda z),\\ f_y=y'/(\\lambda z)},
|
|
235
|
+
|
|
236
|
+
the paraxial approximation to exact scalar diffraction. As with
|
|
237
|
+
:func:`fraunhofer_diffraction`, the output lives on its own natural
|
|
238
|
+
grid of spacing :math:`\\lambda z/(N\\,dx)`.
|
|
239
|
+
|
|
240
|
+
Parameters
|
|
241
|
+
----------
|
|
242
|
+
aperture : ndarray of shape (Ny, Nx)
|
|
243
|
+
Input field (real or complex).
|
|
244
|
+
wavelength : float
|
|
245
|
+
Wavelength :math:`\\lambda`.
|
|
246
|
+
z : float
|
|
247
|
+
Propagation distance.
|
|
248
|
+
dx : float
|
|
249
|
+
Input grid spacing.
|
|
250
|
+
|
|
251
|
+
Returns
|
|
252
|
+
-------
|
|
253
|
+
ndarray of shape (Ny, Nx), complex
|
|
254
|
+
The propagated complex amplitude.
|
|
255
|
+
|
|
256
|
+
See Also
|
|
257
|
+
--------
|
|
258
|
+
angular_spectrum_propagate : The exact (non-paraxial) alternative, on the same input grid.
|
|
259
|
+
|
|
260
|
+
Examples
|
|
261
|
+
--------
|
|
262
|
+
>>> ap = circular_aperture((64, 64), dx=0.01, radius=0.05)
|
|
263
|
+
>>> U = fresnel_diffraction(ap, wavelength=0.5e-3, z=5.0, dx=0.01)
|
|
264
|
+
>>> U.shape
|
|
265
|
+
(64, 64)
|
|
266
|
+
"""
|
|
267
|
+
aperture = np.asarray(aperture)
|
|
268
|
+
k = 2.0 * np.pi / wavelength
|
|
269
|
+
Ny, Nx = aperture.shape
|
|
270
|
+
X, Y = field_grid((Ny, Nx), dx)
|
|
271
|
+
integrand = aperture * np.exp(1j * k / (2.0 * z) * (X**2 + Y**2))
|
|
272
|
+
FT = np.fft.fftshift(np.fft.fft2(np.fft.ifftshift(integrand))) * dx * dx
|
|
273
|
+
fx = np.fft.fftshift(np.fft.fftfreq(Nx, d=dx))
|
|
274
|
+
fy = np.fft.fftshift(np.fft.fftfreq(Ny, d=dx))
|
|
275
|
+
Xp, Yp = np.meshgrid(wavelength * z * fx, wavelength * z * fy)
|
|
276
|
+
prefactor = (np.exp(1j * k * z) / (1j * wavelength * z)) * np.exp(1j * k / (2.0 * z) * (Xp**2 + Yp**2))
|
|
277
|
+
return prefactor * FT
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def angular_spectrum_propagate(U0, wavelength, z, dx):
|
|
281
|
+
"""Exact scalar diffraction propagation via the angular spectrum method.
|
|
282
|
+
|
|
283
|
+
Decomposes the input field into plane-wave components (its 2D Fourier
|
|
284
|
+
transform), advances each by its own longitudinal propagation phase,
|
|
285
|
+
and re-synthesizes:
|
|
286
|
+
|
|
287
|
+
.. math::
|
|
288
|
+
|
|
289
|
+
U(x,y;z) = \\mathcal{F}^{-1}\\!\\left[
|
|
290
|
+
\\mathcal{F}[U_0](f_x,f_y)\\; e^{ik_z z}
|
|
291
|
+
\\right], \\qquad
|
|
292
|
+
k_z = \\begin{cases}
|
|
293
|
+
\\sqrt{k^2 - k_x^2 - k_y^2} & k_x^2+k_y^2 \\le k^2 \\quad \\text{(propagating)}\\\\
|
|
294
|
+
i\\sqrt{k_x^2+k_y^2 - k^2} & k_x^2+k_y^2 > k^2 \\quad \\text{(evanescent)}
|
|
295
|
+
\\end{cases}
|
|
296
|
+
|
|
297
|
+
with :math:`k=2\\pi/\\lambda`, :math:`k_x = 2\\pi f_x`,
|
|
298
|
+
:math:`k_y = 2\\pi f_y`. Evanescent orders get a purely imaginary
|
|
299
|
+
:math:`k_z`, so :math:`e^{ik_z z}` decays exponentially rather than
|
|
300
|
+
producing ``nan``/``inf``. Unlike :func:`fresnel_diffraction` and
|
|
301
|
+
:func:`fraunhofer_diffraction`, this is not a paraxial approximation
|
|
302
|
+
and the output remains on the *same* grid (shape and spacing ``dx``)
|
|
303
|
+
as the input.
|
|
304
|
+
|
|
305
|
+
Parameters
|
|
306
|
+
----------
|
|
307
|
+
U0 : ndarray of shape (Ny, Nx)
|
|
308
|
+
Input complex (or real) field.
|
|
309
|
+
wavelength : float
|
|
310
|
+
Wavelength :math:`\\lambda`.
|
|
311
|
+
z : float
|
|
312
|
+
Propagation distance.
|
|
313
|
+
dx : float
|
|
314
|
+
Grid spacing (both input and output).
|
|
315
|
+
|
|
316
|
+
Returns
|
|
317
|
+
-------
|
|
318
|
+
ndarray of shape (Ny, Nx), complex
|
|
319
|
+
The propagated field, on the same grid as the input.
|
|
320
|
+
|
|
321
|
+
Examples
|
|
322
|
+
--------
|
|
323
|
+
>>> ap = circular_aperture((64, 64), dx=0.01, radius=0.05)
|
|
324
|
+
>>> U = angular_spectrum_propagate(ap, wavelength=0.5e-3, z=0.05, dx=0.01)
|
|
325
|
+
>>> U.shape
|
|
326
|
+
(64, 64)
|
|
327
|
+
>>> bool(np.isclose(intensity(U).sum(), intensity(ap).sum(), rtol=0.02))
|
|
328
|
+
True
|
|
329
|
+
"""
|
|
330
|
+
U0 = np.asarray(U0)
|
|
331
|
+
Ny, Nx = U0.shape
|
|
332
|
+
k = 2.0 * np.pi / wavelength
|
|
333
|
+
fx = np.fft.fftfreq(Nx, d=dx)
|
|
334
|
+
fy = np.fft.fftfreq(Ny, d=dx)
|
|
335
|
+
FX, FY = np.meshgrid(fx, fy)
|
|
336
|
+
kx = 2.0 * np.pi * FX
|
|
337
|
+
ky = 2.0 * np.pi * FY
|
|
338
|
+
kz_sq = k**2 - kx**2 - ky**2
|
|
339
|
+
propagating = kz_sq >= 0.0
|
|
340
|
+
kz = np.empty_like(kz_sq, dtype=complex)
|
|
341
|
+
kz[propagating] = np.sqrt(kz_sq[propagating])
|
|
342
|
+
kz[~propagating] = 1j * np.sqrt(-kz_sq[~propagating])
|
|
343
|
+
H = np.exp(1j * kz * z)
|
|
344
|
+
return np.fft.ifft2(np.fft.fft2(U0) * H)
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
def intensity(U):
|
|
348
|
+
"""Optical intensity :math:`|U|^2` of a complex field.
|
|
349
|
+
|
|
350
|
+
Parameters
|
|
351
|
+
----------
|
|
352
|
+
U : ndarray
|
|
353
|
+
Complex (or real) field amplitude.
|
|
354
|
+
|
|
355
|
+
Returns
|
|
356
|
+
-------
|
|
357
|
+
ndarray
|
|
358
|
+
Real-valued intensity, same shape as ``U``.
|
|
359
|
+
|
|
360
|
+
Examples
|
|
361
|
+
--------
|
|
362
|
+
>>> intensity(np.array([3.0 + 4.0j]))
|
|
363
|
+
array([25.])
|
|
364
|
+
"""
|
|
365
|
+
return np.abs(U) ** 2
|