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.
Files changed (484) hide show
  1. physicskit/__init__.py +51 -0
  2. physicskit/astro/__init__.py +66 -0
  3. physicskit/astro/cosmic_web.py +395 -0
  4. physicskit/astro/galactic_dynamics.py +137 -0
  5. physicskit/astro/nbody.py +208 -0
  6. physicskit/astro/orbital_mechanics.py +220 -0
  7. physicskit/astro/stellar_dynamo.py +628 -0
  8. physicskit/astro/stellar_structure.py +198 -0
  9. physicskit/astro/tests/__init__.py +0 -0
  10. physicskit/astro/tests/conftest.py +7 -0
  11. physicskit/astro/tests/test_cosmic_web.py +159 -0
  12. physicskit/astro/tests/test_galactic_dynamics.py +46 -0
  13. physicskit/astro/tests/test_nbody.py +81 -0
  14. physicskit/astro/tests/test_orbital_mechanics.py +73 -0
  15. physicskit/astro/tests/test_stellar_dynamo.py +198 -0
  16. physicskit/astro/tests/test_stellar_structure.py +52 -0
  17. physicskit/astro/tests/test_visualizers.py +159 -0
  18. physicskit/astro/visualizers.py +628 -0
  19. physicskit/chaos/__init__.py +55 -0
  20. physicskit/chaos/core/__init__.py +3 -0
  21. physicskit/chaos/core/base_system.py +342 -0
  22. physicskit/chaos/core/integrators.py +40 -0
  23. physicskit/chaos/exceptions.py +24 -0
  24. physicskit/chaos/quantum/__init__.py +38 -0
  25. physicskit/chaos/quantum/billiards.py +274 -0
  26. physicskit/chaos/quantum/husimi.py +85 -0
  27. physicskit/chaos/quantum/maps.py +366 -0
  28. physicskit/chaos/systems/__init__.py +38 -0
  29. physicskit/chaos/systems/billiards.py +1131 -0
  30. physicskit/chaos/systems/continuous.py +1276 -0
  31. physicskit/chaos/systems/maps.py +557 -0
  32. physicskit/chaos/tests/__init__.py +0 -0
  33. physicskit/chaos/tests/test_basins.py +76 -0
  34. physicskit/chaos/tests/test_bifurcation.py +132 -0
  35. physicskit/chaos/tests/test_billiards.py +115 -0
  36. physicskit/chaos/tests/test_continuous.py +224 -0
  37. physicskit/chaos/tests/test_dimension.py +69 -0
  38. physicskit/chaos/tests/test_divergence.py +90 -0
  39. physicskit/chaos/tests/test_dynamic_plots.py +269 -0
  40. physicskit/chaos/tests/test_integrators.py +96 -0
  41. physicskit/chaos/tests/test_io.py +41 -0
  42. physicskit/chaos/tests/test_maps.py +110 -0
  43. physicskit/chaos/tests/test_metrics.py +115 -0
  44. physicskit/chaos/tests/test_phase_space.py +73 -0
  45. physicskit/chaos/tests/test_properties.py +116 -0
  46. physicskit/chaos/tests/test_quantum_billiards.py +94 -0
  47. physicskit/chaos/tests/test_quantum_maps.py +144 -0
  48. physicskit/chaos/tests/test_recurrence.py +51 -0
  49. physicskit/chaos/tests/test_repr_and_api_consistency.py +134 -0
  50. physicskit/chaos/tests/test_section.py +49 -0
  51. physicskit/chaos/tests/test_spectral.py +48 -0
  52. physicskit/chaos/tests/test_streamlit_app.py +45 -0
  53. physicskit/chaos/tests/test_timeseries.py +131 -0
  54. physicskit/chaos/tests/test_viewer3d.py +41 -0
  55. physicskit/chaos/utils/__init__.py +46 -0
  56. physicskit/chaos/utils/dimension.py +139 -0
  57. physicskit/chaos/utils/io.py +95 -0
  58. physicskit/chaos/utils/metrics.py +304 -0
  59. physicskit/chaos/utils/recurrence.py +122 -0
  60. physicskit/chaos/utils/spectral.py +69 -0
  61. physicskit/chaos/utils/timeseries.py +303 -0
  62. physicskit/chaos/visualizers/__init__.py +85 -0
  63. physicskit/chaos/visualizers/basins.py +185 -0
  64. physicskit/chaos/visualizers/bifurcation.py +273 -0
  65. physicskit/chaos/visualizers/divergence.py +374 -0
  66. physicskit/chaos/visualizers/dynamic_plots.py +1372 -0
  67. physicskit/chaos/visualizers/phase_space.py +133 -0
  68. physicskit/chaos/visualizers/quantum.py +299 -0
  69. physicskit/chaos/visualizers/recurrence.py +91 -0
  70. physicskit/chaos/visualizers/section.py +161 -0
  71. physicskit/chaos/visualizers/theme.py +31 -0
  72. physicskit/chaos/visualizers/viewer3d.py +82 -0
  73. physicskit/classical/__init__.py +55 -0
  74. physicskit/classical/core/__init__.py +17 -0
  75. physicskit/classical/core/base_system.py +510 -0
  76. physicskit/classical/core/integrators.py +397 -0
  77. physicskit/classical/systems/__init__.py +32 -0
  78. physicskit/classical/systems/chains.py +334 -0
  79. physicskit/classical/systems/hamiltonian.py +215 -0
  80. physicskit/classical/systems/lagrangian.py +308 -0
  81. physicskit/classical/systems/newtonian.py +536 -0
  82. physicskit/classical/systems/rotations.py +614 -0
  83. physicskit/classical/tests/__init__.py +0 -0
  84. physicskit/classical/tests/conftest.py +7 -0
  85. physicskit/classical/tests/test_base_system.py +113 -0
  86. physicskit/classical/tests/test_chains.py +84 -0
  87. physicskit/classical/tests/test_conservation.py +244 -0
  88. physicskit/classical/tests/test_conservation_utils.py +47 -0
  89. physicskit/classical/tests/test_hamiltonian.py +44 -0
  90. physicskit/classical/tests/test_integrators.py +123 -0
  91. physicskit/classical/tests/test_lagrangian.py +69 -0
  92. physicskit/classical/tests/test_newtonian.py +45 -0
  93. physicskit/classical/tests/test_rotations.py +160 -0
  94. physicskit/classical/tests/test_stepsize.py +149 -0
  95. physicskit/classical/tests/test_symbolic.py +125 -0
  96. physicskit/classical/tests/test_visualizers.py +172 -0
  97. physicskit/classical/utils/__init__.py +20 -0
  98. physicskit/classical/utils/conservation.py +126 -0
  99. physicskit/classical/utils/stepsize.py +187 -0
  100. physicskit/classical/utils/symbolic.py +235 -0
  101. physicskit/classical/visualizers/__init__.py +25 -0
  102. physicskit/classical/visualizers/animations.py +508 -0
  103. physicskit/classical/visualizers/interactive.py +113 -0
  104. physicskit/classical/visualizers/modal_analysis.py +97 -0
  105. physicskit/classical/visualizers/phase_space.py +214 -0
  106. physicskit/condensed/__init__.py +122 -0
  107. physicskit/condensed/anderson_localization.py +155 -0
  108. physicskit/condensed/correlated.py +284 -0
  109. physicskit/condensed/ginzburg_landau.py +220 -0
  110. physicskit/condensed/landau_levels.py +246 -0
  111. physicskit/condensed/laughlin.py +229 -0
  112. physicskit/condensed/models.py +627 -0
  113. physicskit/condensed/tests/__init__.py +0 -0
  114. physicskit/condensed/tests/test_condensed.py +518 -0
  115. physicskit/condensed/tests/test_visualizers.py +87 -0
  116. physicskit/condensed/tight_binding.py +445 -0
  117. physicskit/condensed/topological_insulator_3d.py +178 -0
  118. physicskit/condensed/topology.py +217 -0
  119. physicskit/condensed/visualizers.py +296 -0
  120. physicskit/condensed/weyl.py +180 -0
  121. physicskit/constants.py +184 -0
  122. physicskit/fields/__init__.py +111 -0
  123. physicskit/fields/electrodynamics.py +671 -0
  124. physicskit/fields/quantum_fields.py +510 -0
  125. physicskit/fields/solitons.py +527 -0
  126. physicskit/fields/tests/__init__.py +0 -0
  127. physicskit/fields/tests/conftest.py +7 -0
  128. physicskit/fields/tests/test_field_animations.py +154 -0
  129. physicskit/fields/tests/test_fields.py +439 -0
  130. physicskit/fields/tests/test_visualizers.py +101 -0
  131. physicskit/fields/visualizers.py +503 -0
  132. physicskit/fluids/__init__.py +134 -0
  133. physicskit/fluids/core/__init__.py +23 -0
  134. physicskit/fluids/core/grid.py +165 -0
  135. physicskit/fluids/core/timestepping.py +231 -0
  136. physicskit/fluids/exceptions.py +26 -0
  137. physicskit/fluids/systems/__init__.py +70 -0
  138. physicskit/fluids/systems/compressible_flow.py +281 -0
  139. physicskit/fluids/systems/instabilities.py +343 -0
  140. physicskit/fluids/systems/navier_stokes.py +221 -0
  141. physicskit/fluids/systems/potential_flow.py +440 -0
  142. physicskit/fluids/systems/viscous_flow.py +425 -0
  143. physicskit/fluids/systems/vortex_dynamics.py +304 -0
  144. physicskit/fluids/tests/__init__.py +0 -0
  145. physicskit/fluids/tests/conftest.py +7 -0
  146. physicskit/fluids/tests/test_compressible_flow.py +66 -0
  147. physicskit/fluids/tests/test_instabilities.py +115 -0
  148. physicskit/fluids/tests/test_navier_stokes.py +67 -0
  149. physicskit/fluids/tests/test_potential_flow.py +92 -0
  150. physicskit/fluids/tests/test_static_visualizers.py +91 -0
  151. physicskit/fluids/tests/test_utils.py +100 -0
  152. physicskit/fluids/tests/test_viscous_flow.py +110 -0
  153. physicskit/fluids/tests/test_visualizers.py +34 -0
  154. physicskit/fluids/tests/test_vortex_dynamics.py +88 -0
  155. physicskit/fluids/utils/__init__.py +18 -0
  156. physicskit/fluids/utils/dimensionless.py +239 -0
  157. physicskit/fluids/utils/spectral_analysis.py +142 -0
  158. physicskit/fluids/visualizers/__init__.py +14 -0
  159. physicskit/fluids/visualizers/compressible.py +39 -0
  160. physicskit/fluids/visualizers/flow_fields.py +252 -0
  161. physicskit/fluids/visualizers/potential_flow.py +51 -0
  162. physicskit/fluids/visualizers/spectra.py +62 -0
  163. physicskit/fluids/visualizers/theme.py +38 -0
  164. physicskit/integrators/__init__.py +52 -0
  165. physicskit/integrators/adaptive.py +199 -0
  166. physicskit/integrators/fixed_step.py +360 -0
  167. physicskit/integrators/tests/__init__.py +0 -0
  168. physicskit/integrators/tests/test_integrators.py +101 -0
  169. physicskit/optics/__init__.py +124 -0
  170. physicskit/optics/_compat.py +5 -0
  171. physicskit/optics/gaussian.py +382 -0
  172. physicskit/optics/quantum_optics.py +428 -0
  173. physicskit/optics/ray.py +469 -0
  174. physicskit/optics/tests/__init__.py +0 -0
  175. physicskit/optics/tests/test_diffraction_animation.py +38 -0
  176. physicskit/optics/tests/test_gaussian.py +170 -0
  177. physicskit/optics/tests/test_quantum_optics.py +147 -0
  178. physicskit/optics/tests/test_ray.py +177 -0
  179. physicskit/optics/tests/test_visualizers.py +58 -0
  180. physicskit/optics/tests/test_wave.py +146 -0
  181. physicskit/optics/visualizers.py +264 -0
  182. physicskit/optics/wave.py +365 -0
  183. physicskit/particle/__init__.py +152 -0
  184. physicskit/particle/collider.py +382 -0
  185. physicskit/particle/confinement.py +125 -0
  186. physicskit/particle/decays.py +357 -0
  187. physicskit/particle/electroweak.py +300 -0
  188. physicskit/particle/kinematics.py +271 -0
  189. physicskit/particle/neutrinos.py +84 -0
  190. physicskit/particle/nuclear.py +100 -0
  191. physicskit/particle/scattering.py +159 -0
  192. physicskit/particle/tests/__init__.py +0 -0
  193. physicskit/particle/tests/conftest.py +7 -0
  194. physicskit/particle/tests/test_collider.py +119 -0
  195. physicskit/particle/tests/test_confinement.py +42 -0
  196. physicskit/particle/tests/test_decays.py +112 -0
  197. physicskit/particle/tests/test_electroweak.py +97 -0
  198. physicskit/particle/tests/test_kinematics.py +138 -0
  199. physicskit/particle/tests/test_neutrinos.py +36 -0
  200. physicskit/particle/tests/test_nuclear.py +45 -0
  201. physicskit/particle/tests/test_scattering.py +64 -0
  202. physicskit/particle/tests/test_visualizers.py +214 -0
  203. physicskit/particle/visualizers/__init__.py +38 -0
  204. physicskit/particle/visualizers/animations.py +705 -0
  205. physicskit/particle/visualizers/static.py +101 -0
  206. physicskit/plasma/__init__.py +176 -0
  207. physicskit/plasma/acceleration.py +152 -0
  208. physicskit/plasma/instabilities.py +417 -0
  209. physicskit/plasma/kinetic.py +588 -0
  210. physicskit/plasma/mhd.py +463 -0
  211. physicskit/plasma/single_particle.py +562 -0
  212. physicskit/plasma/tests/__init__.py +0 -0
  213. physicskit/plasma/tests/conftest.py +7 -0
  214. physicskit/plasma/tests/test_new_time_domain_features.py +222 -0
  215. physicskit/plasma/tests/test_plasma.py +232 -0
  216. physicskit/plasma/tests/test_visualizers.py +79 -0
  217. physicskit/plasma/turbulence.py +219 -0
  218. physicskit/plasma/visualizers.py +868 -0
  219. physicskit/plasma/waves.py +549 -0
  220. physicskit/quantum/__init__.py +180 -0
  221. physicskit/quantum/_compat.py +20 -0
  222. physicskit/quantum/chapters/__init__.py +0 -0
  223. physicskit/quantum/chapters/entanglement.py +489 -0
  224. physicskit/quantum/chapters/harmonic_spin.py +517 -0
  225. physicskit/quantum/chapters/hydrogen_am.py +292 -0
  226. physicskit/quantum/chapters/perturbation.py +350 -0
  227. physicskit/quantum/chapters/potentials.py +997 -0
  228. physicskit/quantum/chapters/spin.py +306 -0
  229. physicskit/quantum/chapters/wave_packets.py +541 -0
  230. physicskit/quantum/core/__init__.py +0 -0
  231. physicskit/quantum/core/eigensolvers.py +311 -0
  232. physicskit/quantum/core/operators.py +296 -0
  233. physicskit/quantum/core/solvers.py +271 -0
  234. physicskit/quantum/tests/__init__.py +0 -0
  235. physicskit/quantum/tests/conftest.py +7 -0
  236. physicskit/quantum/tests/test_animations.py +225 -0
  237. physicskit/quantum/tests/test_compat.py +43 -0
  238. physicskit/quantum/tests/test_eigensolvers.py +24 -0
  239. physicskit/quantum/tests/test_entanglement.py +127 -0
  240. physicskit/quantum/tests/test_harmonic_spin.py +140 -0
  241. physicskit/quantum/tests/test_hydrogen_am.py +48 -0
  242. physicskit/quantum/tests/test_measure.py +110 -0
  243. physicskit/quantum/tests/test_operators.py +105 -0
  244. physicskit/quantum/tests/test_perturbation.py +112 -0
  245. physicskit/quantum/tests/test_physics_checks.py +131 -0
  246. physicskit/quantum/tests/test_potentials.py +191 -0
  247. physicskit/quantum/tests/test_solvers.py +70 -0
  248. physicskit/quantum/tests/test_spin.py +22 -0
  249. physicskit/quantum/tests/test_unitarity.py +84 -0
  250. physicskit/quantum/tests/test_wave_packets.py +53 -0
  251. physicskit/quantum/utils/__init__.py +0 -0
  252. physicskit/quantum/utils/measure.py +253 -0
  253. physicskit/quantum/visualizers/__init__.py +0 -0
  254. physicskit/quantum/visualizers/bloch_sphere.py +205 -0
  255. physicskit/quantum/visualizers/entanglement.py +58 -0
  256. physicskit/quantum/visualizers/orbitals.py +187 -0
  257. physicskit/quantum/visualizers/phase_space.py +179 -0
  258. physicskit/quantum/visualizers/wavefunctions.py +230 -0
  259. physicskit/relativity/__init__.py +61 -0
  260. physicskit/relativity/chapters/__init__.py +33 -0
  261. physicskit/relativity/chapters/cosmology.py +190 -0
  262. physicskit/relativity/chapters/gw_merger.py +399 -0
  263. physicskit/relativity/chapters/kerr.py +371 -0
  264. physicskit/relativity/chapters/lensing.py +196 -0
  265. physicskit/relativity/chapters/neutron_star.py +149 -0
  266. physicskit/relativity/chapters/schwarzschild.py +412 -0
  267. physicskit/relativity/chapters/timekeeping.py +133 -0
  268. physicskit/relativity/core/__init__.py +12 -0
  269. physicskit/relativity/core/geodesics.py +216 -0
  270. physicskit/relativity/core/kerr_raytracer.py +259 -0
  271. physicskit/relativity/core/raytracer.py +241 -0
  272. physicskit/relativity/core/tensors.py +468 -0
  273. physicskit/relativity/tests/__init__.py +0 -0
  274. physicskit/relativity/tests/test_conservations.py +54 -0
  275. physicskit/relativity/tests/test_constants.py +34 -0
  276. physicskit/relativity/tests/test_cosmology.py +61 -0
  277. physicskit/relativity/tests/test_gw_merger.py +117 -0
  278. physicskit/relativity/tests/test_kerr.py +116 -0
  279. physicskit/relativity/tests/test_kerr_raytracer.py +103 -0
  280. physicskit/relativity/tests/test_lensing.py +75 -0
  281. physicskit/relativity/tests/test_neutron_star.py +48 -0
  282. physicskit/relativity/tests/test_raytracer.py +67 -0
  283. physicskit/relativity/tests/test_schwarzschild.py +126 -0
  284. physicskit/relativity/tests/test_shadow_render.py +81 -0
  285. physicskit/relativity/tests/test_tensors.py +86 -0
  286. physicskit/relativity/tests/test_timekeeping.py +51 -0
  287. physicskit/relativity/tests/test_visualizers_misc.py +115 -0
  288. physicskit/relativity/utils/__init__.py +1 -0
  289. physicskit/relativity/utils/constants.py +232 -0
  290. physicskit/relativity/visualizers/__init__.py +46 -0
  291. physicskit/relativity/visualizers/interactive.py +127 -0
  292. physicskit/relativity/visualizers/shadow_render.py +367 -0
  293. physicskit/relativity/visualizers/spacetime_3d.py +92 -0
  294. physicskit/relativity/visualizers/spacetime_diagrams.py +198 -0
  295. physicskit/relativity/visualizers/wave_plots.py +185 -0
  296. physicskit/rmt/__init__.py +8 -0
  297. physicskit/rmt/ensembles/__init__.py +108 -0
  298. physicskit/rmt/ensembles/banded.py +114 -0
  299. physicskit/rmt/ensembles/base.py +135 -0
  300. physicskit/rmt/ensembles/bdg.py +218 -0
  301. physicskit/rmt/ensembles/chiral.py +308 -0
  302. physicskit/rmt/ensembles/circular.py +104 -0
  303. physicskit/rmt/ensembles/crossover.py +82 -0
  304. physicskit/rmt/ensembles/density_matrix.py +148 -0
  305. physicskit/rmt/ensembles/effective_hamiltonian.py +230 -0
  306. physicskit/rmt/ensembles/embedded.py +207 -0
  307. physicskit/rmt/ensembles/gaussian.py +153 -0
  308. physicskit/rmt/ensembles/ginibre.py +110 -0
  309. physicskit/rmt/ensembles/girko.py +190 -0
  310. physicskit/rmt/ensembles/graph_laplacian.py +92 -0
  311. physicskit/rmt/ensembles/haar.py +57 -0
  312. physicskit/rmt/ensembles/jacobi.py +182 -0
  313. physicskit/rmt/ensembles/poisson.py +77 -0
  314. physicskit/rmt/ensembles/polynomial.py +139 -0
  315. physicskit/rmt/ensembles/pt_symmetric.py +120 -0
  316. physicskit/rmt/ensembles/single_ring.py +170 -0
  317. physicskit/rmt/ensembles/sparse.py +95 -0
  318. physicskit/rmt/ensembles/syk.py +170 -0
  319. physicskit/rmt/ensembles/truncated_unitary.py +80 -0
  320. physicskit/rmt/ensembles/universality.py +92 -0
  321. physicskit/rmt/ensembles/wishart.py +88 -0
  322. physicskit/rmt/py.typed +0 -0
  323. physicskit/rmt/spectrum.py +82 -0
  324. physicskit/rmt/stats/__init__.py +160 -0
  325. physicskit/rmt/stats/characteristic_polynomial.py +77 -0
  326. physicskit/rmt/stats/circular_law.py +54 -0
  327. physicskit/rmt/stats/correlations.py +162 -0
  328. physicskit/rmt/stats/density.py +70 -0
  329. physicskit/rmt/stats/entanglement.py +36 -0
  330. physicskit/rmt/stats/free_probability.py +202 -0
  331. physicskit/rmt/stats/localization.py +338 -0
  332. physicskit/rmt/stats/marchenko_pastur.py +55 -0
  333. physicskit/rmt/stats/pt_symmetric.py +265 -0
  334. physicskit/rmt/stats/ratios.py +110 -0
  335. physicskit/rmt/stats/real_ginibre.py +134 -0
  336. physicskit/rmt/stats/rigidity.py +272 -0
  337. physicskit/rmt/stats/single_ring.py +95 -0
  338. physicskit/rmt/stats/spacing.py +150 -0
  339. physicskit/rmt/stats/tracy_widom.py +175 -0
  340. physicskit/rmt/stats/truncated_unitary.py +40 -0
  341. physicskit/rmt/stats/unfolding.py +37 -0
  342. physicskit/rmt/stats/wachter.py +67 -0
  343. physicskit/rmt/tests/__init__.py +0 -0
  344. physicskit/rmt/tests/cache_utils.py +107 -0
  345. physicskit/rmt/tests/conftest.py +8 -0
  346. physicskit/rmt/tests/test_banded.py +86 -0
  347. physicskit/rmt/tests/test_base.py +29 -0
  348. physicskit/rmt/tests/test_bdg.py +133 -0
  349. physicskit/rmt/tests/test_characteristic_polynomial.py +50 -0
  350. physicskit/rmt/tests/test_chiral.py +273 -0
  351. physicskit/rmt/tests/test_circular.py +225 -0
  352. physicskit/rmt/tests/test_crossover.py +72 -0
  353. physicskit/rmt/tests/test_density.py +23 -0
  354. physicskit/rmt/tests/test_density_matrix.py +181 -0
  355. physicskit/rmt/tests/test_effective_hamiltonian.py +140 -0
  356. physicskit/rmt/tests/test_embedded.py +169 -0
  357. physicskit/rmt/tests/test_free_probability.py +81 -0
  358. physicskit/rmt/tests/test_gaussian_ensembles.py +22 -0
  359. physicskit/rmt/tests/test_gaussian_semicircle.py +127 -0
  360. physicskit/rmt/tests/test_ginibre_circular_law.py +131 -0
  361. physicskit/rmt/tests/test_girko.py +178 -0
  362. physicskit/rmt/tests/test_graph_laplacian.py +102 -0
  363. physicskit/rmt/tests/test_haar.py +118 -0
  364. physicskit/rmt/tests/test_jacobi_wachter.py +136 -0
  365. physicskit/rmt/tests/test_localization.py +240 -0
  366. physicskit/rmt/tests/test_poisson.py +149 -0
  367. physicskit/rmt/tests/test_polynomial.py +107 -0
  368. physicskit/rmt/tests/test_pt_symmetric.py +178 -0
  369. physicskit/rmt/tests/test_random_state.py +19 -0
  370. physicskit/rmt/tests/test_real_ginibre.py +112 -0
  371. physicskit/rmt/tests/test_rigidity_universality.py +167 -0
  372. physicskit/rmt/tests/test_single_ring.py +132 -0
  373. physicskit/rmt/tests/test_spacing_and_ratios.py +122 -0
  374. physicskit/rmt/tests/test_sparse.py +71 -0
  375. physicskit/rmt/tests/test_spectrum.py +27 -0
  376. physicskit/rmt/tests/test_syk.py +136 -0
  377. physicskit/rmt/tests/test_tracy_widom.py +113 -0
  378. physicskit/rmt/tests/test_tridiagonal.py +41 -0
  379. physicskit/rmt/tests/test_truncated_unitary.py +80 -0
  380. physicskit/rmt/tests/test_validation_base.py +25 -0
  381. physicskit/rmt/tests/test_validation_misc.py +67 -0
  382. physicskit/rmt/tests/test_wishart_marchenko_pastur.py +120 -0
  383. physicskit/rmt/utils/__init__.py +0 -0
  384. physicskit/rmt/utils/haar.py +128 -0
  385. physicskit/rmt/utils/random_state.py +24 -0
  386. physicskit/rmt/utils/tridiagonal.py +167 -0
  387. physicskit/rmt/validation/__init__.py +36 -0
  388. physicskit/rmt/validation/base.py +126 -0
  389. physicskit/rmt/validation/circular_law.py +80 -0
  390. physicskit/rmt/validation/marchenko_pastur.py +53 -0
  391. physicskit/rmt/validation/ratio_distribution.py +53 -0
  392. physicskit/rmt/validation/real_ginibre.py +60 -0
  393. physicskit/rmt/validation/sine_kernel.py +57 -0
  394. physicskit/rmt/validation/single_ring.py +68 -0
  395. physicskit/rmt/validation/tracy_widom.py +89 -0
  396. physicskit/rmt/validation/universality.py +100 -0
  397. physicskit/rmt/validation/wachter.py +52 -0
  398. physicskit/rmt/validation/wigner_semicircle.py +40 -0
  399. physicskit/rmt/validation/wigner_surmise.py +58 -0
  400. physicskit/semiclassical/__init__.py +94 -0
  401. physicskit/semiclassical/_compat.py +5 -0
  402. physicskit/semiclassical/core/__init__.py +41 -0
  403. physicskit/semiclassical/core/gutzwiller.py +215 -0
  404. physicskit/semiclassical/core/path_integral.py +594 -0
  405. physicskit/semiclassical/core/propagators.py +579 -0
  406. physicskit/semiclassical/core/wkb.py +308 -0
  407. physicskit/semiclassical/systems/__init__.py +13 -0
  408. physicskit/semiclassical/systems/scarring.py +278 -0
  409. physicskit/semiclassical/tests/__init__.py +0 -0
  410. physicskit/semiclassical/tests/test_gutzwiller.py +35 -0
  411. physicskit/semiclassical/tests/test_path_integral.py +194 -0
  412. physicskit/semiclassical/tests/test_propagators.py +82 -0
  413. physicskit/semiclassical/tests/test_scarring.py +43 -0
  414. physicskit/semiclassical/tests/test_visualizers.py +89 -0
  415. physicskit/semiclassical/tests/test_wkb.py +30 -0
  416. physicskit/semiclassical/visualizers/__init__.py +17 -0
  417. physicskit/semiclassical/visualizers/gutzwiller.py +55 -0
  418. physicskit/semiclassical/visualizers/path_integral.py +195 -0
  419. physicskit/semiclassical/visualizers/propagators.py +89 -0
  420. physicskit/semiclassical/visualizers/scarring.py +146 -0
  421. physicskit/semiclassical/visualizers/wkb.py +55 -0
  422. physicskit/statphys/__init__.py +80 -0
  423. physicskit/statphys/chapters/__init__.py +43 -0
  424. physicskit/statphys/chapters/ehrenfest_urn.py +112 -0
  425. physicskit/statphys/chapters/ising_lattice.py +431 -0
  426. physicskit/statphys/chapters/kpz_growth.py +129 -0
  427. physicskit/statphys/chapters/molecular_dynamics.py +258 -0
  428. physicskit/statphys/chapters/nonequilibrium_work.py +146 -0
  429. physicskit/statphys/chapters/percolation.py +305 -0
  430. physicskit/statphys/chapters/random_walk.py +142 -0
  431. physicskit/statphys/chapters/renormalization.py +148 -0
  432. physicskit/statphys/chapters/sandpile.py +109 -0
  433. physicskit/statphys/chapters/spin_glass.py +275 -0
  434. physicskit/statphys/core/__init__.py +10 -0
  435. physicskit/statphys/core/kpz_engine.py +53 -0
  436. physicskit/statphys/core/md_engine.py +157 -0
  437. physicskit/statphys/core/monte_carlo.py +462 -0
  438. physicskit/statphys/core/random_walk_engine.py +68 -0
  439. physicskit/statphys/core/sandpile_engine.py +52 -0
  440. physicskit/statphys/tests/__init__.py +0 -0
  441. physicskit/statphys/tests/test_dynamics.py +66 -0
  442. physicskit/statphys/tests/test_ehrenfest_urn.py +44 -0
  443. physicskit/statphys/tests/test_finite_size_scaling.py +62 -0
  444. physicskit/statphys/tests/test_interactive.py +33 -0
  445. physicskit/statphys/tests/test_ising.py +68 -0
  446. physicskit/statphys/tests/test_kpz.py +55 -0
  447. physicskit/statphys/tests/test_landau_theory.py +49 -0
  448. physicskit/statphys/tests/test_md_engine.py +31 -0
  449. physicskit/statphys/tests/test_molecular_dynamics.py +72 -0
  450. physicskit/statphys/tests/test_nonequilibrium_work.py +33 -0
  451. physicskit/statphys/tests/test_partition_function.py +26 -0
  452. physicskit/statphys/tests/test_percolation.py +116 -0
  453. physicskit/statphys/tests/test_potts_model.py +46 -0
  454. physicskit/statphys/tests/test_random_walk.py +50 -0
  455. physicskit/statphys/tests/test_renormalization.py +44 -0
  456. physicskit/statphys/tests/test_sandpile.py +44 -0
  457. physicskit/statphys/tests/test_spin_glass.py +73 -0
  458. physicskit/statphys/tests/test_thermodynamics.py +121 -0
  459. physicskit/statphys/tests/test_xy_model.py +45 -0
  460. physicskit/statphys/utils/__init__.py +1 -0
  461. physicskit/statphys/utils/dynamics.py +100 -0
  462. physicskit/statphys/utils/finite_size_scaling.py +171 -0
  463. physicskit/statphys/utils/landau_theory.py +151 -0
  464. physicskit/statphys/utils/partition_function.py +109 -0
  465. physicskit/statphys/utils/thermodynamics.py +356 -0
  466. physicskit/statphys/visualizers/__init__.py +87 -0
  467. physicskit/statphys/visualizers/interactive.py +161 -0
  468. physicskit/statphys/visualizers/jarzynski_render.py +44 -0
  469. physicskit/statphys/visualizers/kpz_render.py +65 -0
  470. physicskit/statphys/visualizers/lattice_render.py +212 -0
  471. physicskit/statphys/visualizers/particle_render.py +121 -0
  472. physicskit/statphys/visualizers/random_walk_render.py +106 -0
  473. physicskit/statphys/visualizers/rg_render.py +45 -0
  474. physicskit/statphys/visualizers/sandpile_render.py +65 -0
  475. physicskit/statphys/visualizers/spin_glass_render.py +40 -0
  476. physicskit/statphys/visualizers/urn_render.py +38 -0
  477. physicskit/statphys/visualizers/vortex_render.py +84 -0
  478. physicskit/tests/__init__.py +0 -0
  479. physicskit/tests/test_constants.py +38 -0
  480. physicskit-0.1.0.dist-info/METADATA +196 -0
  481. physicskit-0.1.0.dist-info/RECORD +484 -0
  482. physicskit-0.1.0.dist-info/WHEEL +5 -0
  483. physicskit-0.1.0.dist-info/licenses/LICENSE +21 -0
  484. 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