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