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,439 @@
1
+ """Physics-correctness tests for physicskit.fields."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import numpy as np
6
+ import pytest
7
+ from scipy.signal import find_peaks
8
+
9
+ from physicskit.fields.electrodynamics import (
10
+ C0,
11
+ courant_limit_1d,
12
+ courant_limit_2d,
13
+ dielectric_slab,
14
+ fdtd_1d,
15
+ fdtd_2d_tmz,
16
+ fdtd_2d_tmz_evolve,
17
+ flux_tube_field_1d,
18
+ oscillating_dipole_source,
19
+ pml_conductivity_profile_2d,
20
+ poynting_vector_tmz,
21
+ tmz_cavity_mode,
22
+ )
23
+ from physicskit.fields.quantum_fields import (
24
+ casimir_energy_1d,
25
+ casimir_mode_frequencies,
26
+ count_vortices,
27
+ gpe_energy,
28
+ gpe_evolve,
29
+ gpe_imprint_vortex,
30
+ gpe_relax,
31
+ harmonic_trap_grid,
32
+ )
33
+ from physicskit.fields.solitons import (
34
+ kdv_evolve,
35
+ kdv_evolve_frames,
36
+ kdv_soliton,
37
+ nls_bright_soliton,
38
+ nls_dark_soliton,
39
+ nls_evolve,
40
+ nls_evolve_frames,
41
+ sine_gordon_evolve,
42
+ sine_gordon_evolve_frames,
43
+ sine_gordon_kink,
44
+ )
45
+
46
+
47
+ class TestFDTDWaveSpeed:
48
+ def test_1d_pulse_propagates_at_c(self):
49
+ N, dx = 800, 1e-3
50
+ dt = 0.99 * courant_limit_1d(dx)
51
+ eta0 = np.sqrt(4 * np.pi * 1e-7 / 8.8541878128e-12)
52
+ x0, sigma_pulse = 100, 25
53
+ Ez0 = np.exp(-((np.arange(N) - x0) ** 2) / (2 * sigma_pulse**2))
54
+ xh = np.arange(N - 1) + 0.5
55
+ Hy0 = np.exp(-((xh - x0) ** 2) / (2 * sigma_pulse**2)) / eta0
56
+ eps_r, mu_r = np.ones(N), np.ones(N)
57
+ steps = 400
58
+ Ez, _ = fdtd_1d(Ez0, Hy0, eps_r, mu_r, steps=steps, dt=dt, dx=dx)
59
+ peak = np.argmax(Ez)
60
+ measured_speed = (peak - x0) * dx / (steps * dt)
61
+ assert measured_speed == pytest.approx(C0, rel=0.01)
62
+
63
+ def test_1d_lossy_medium_damps_the_pulse_more_than_lossless(self):
64
+ N, dx = 200, 1e-3
65
+ dt = 0.99 * courant_limit_1d(dx)
66
+ x0, sigma_pulse = 60, 15
67
+ Ez0 = np.exp(-((np.arange(N) - x0) ** 2) / (2 * sigma_pulse**2))
68
+ Hy0 = np.zeros(N - 1)
69
+ eps_r, mu_r = np.ones(N), np.ones(N)
70
+ steps = 100
71
+ Ez_lossless, _ = fdtd_1d(Ez0, Hy0, eps_r, mu_r, steps=steps, dt=dt, dx=dx)
72
+ Ez_lossy, _ = fdtd_1d(Ez0, Hy0, eps_r, mu_r, steps=steps, dt=dt, dx=dx, sigma=np.full(N, 5.0))
73
+ assert np.max(np.abs(Ez_lossy)) < np.max(np.abs(Ez_lossless))
74
+
75
+ def test_2d_tmz_runs_and_conserves_shape(self):
76
+ Nx, Ny = 40, 40
77
+ Ez0 = np.zeros((Nx, Ny))
78
+ Ez0[Nx // 2, Ny // 2] = 1.0
79
+ Hx0, Hy0 = np.zeros((Nx, Ny)), np.zeros((Nx, Ny))
80
+ eps_r, mu_r = np.ones((Nx, Ny)), np.ones((Nx, Ny))
81
+ dx = dy = 1e-3
82
+ dt = 0.5 * courant_limit_2d(dx, dy)
83
+ Ez, _, _ = fdtd_2d_tmz(Ez0, Hx0, Hy0, eps_r, mu_r, steps=15, dt=dt, dx=dx, dy=dy)
84
+ assert Ez.shape == (Nx, Ny)
85
+ assert np.all(np.isfinite(Ez))
86
+
87
+
88
+ class TestKdVSolitonCollision:
89
+ def test_two_soliton_collision_is_elastic(self):
90
+ N, L = 512, 60.0
91
+ x = np.linspace(-L / 2, L / 2, N, endpoint=False)
92
+ c_fast, c_slow = 9.0, 4.0
93
+ u0 = kdv_soliton(x, c_fast, x0=-20) + kdv_soliton(x, c_slow, x0=-8)
94
+ u = kdv_evolve(u0, x, dt=0.0005, steps=6000)
95
+ peaks, _ = find_peaks(u, height=1.0)
96
+ heights = sorted(u[peaks])
97
+ assert len(heights) == 2
98
+ assert heights[0] == pytest.approx(c_slow / 2, abs=0.02)
99
+ assert heights[1] == pytest.approx(c_fast / 2, abs=0.02)
100
+
101
+ def test_mass_is_conserved(self):
102
+ N, L = 512, 60.0
103
+ x = np.linspace(-L / 2, L / 2, N, endpoint=False)
104
+ dx = x[1] - x[0]
105
+ u0 = kdv_soliton(x, 9.0, x0=-20) + kdv_soliton(x, 4.0, x0=-8)
106
+ u = kdv_evolve(u0, x, dt=0.0005, steps=6000)
107
+ assert np.sum(u) * dx == pytest.approx(np.sum(u0) * dx, rel=1e-6)
108
+
109
+ def test_single_soliton_shape_preserved(self):
110
+ N, L = 512, 60.0
111
+ x = np.linspace(-L / 2, L / 2, N, endpoint=False)
112
+ c = 4.0
113
+ u0 = kdv_soliton(x, c, x0=-15)
114
+ steps, dt = 2000, 0.001
115
+ u = kdv_evolve(u0, x, dt=dt, steps=steps)
116
+ expected = kdv_soliton(x, c, x0=-15 + c * dt * steps)
117
+ assert np.max(np.abs(u - expected)) < 1e-3
118
+
119
+
120
+ class TestGPEVortexLattice:
121
+ def test_imaginary_time_energy_decreases_monotonically(self):
122
+ n, length, g = 48, 12.0, 4.0
123
+ X, Y, _, _, K2 = harmonic_trap_grid(n, length)
124
+ V = 0.5 * (X**2 + Y**2)
125
+ psi = np.exp(-0.5 * (X**2 + Y**2)).astype(complex)
126
+ energies = []
127
+ for _ in range(8):
128
+ psi = gpe_relax(psi, V, g, dtau=5e-4, steps=250, X=X, Y=Y, K2=K2)
129
+ energies.append(gpe_energy(psi, V, g, X, Y, K2)["total"])
130
+ assert all(energies[i + 1] <= energies[i] + 1e-9 for i in range(len(energies) - 1))
131
+
132
+ def test_vortex_state_is_a_stationary_solution_with_quantized_circulation(self):
133
+ # Seeded slightly off a grid vertex: a core sitting exactly on a lattice
134
+ # site makes the discrete plaquette circulation ambiguous (it's shared
135
+ # between four adjacent plaquettes), which is a detection subtlety, not
136
+ # a physical effect.
137
+ n, length, g = 48, 10.0, 4.0
138
+ X, Y, _, _, K2 = harmonic_trap_grid(n, length)
139
+ V = 0.5 * (X**2 + Y**2)
140
+ psi0 = gpe_imprint_vortex(np.exp(-0.5 * (X**2 + Y**2)).astype(complex), X, Y, [(0.03, 0.02)])
141
+ psi = gpe_relax(psi0, V, g, dtau=5e-4, steps=4000, X=X, Y=Y, K2=K2)
142
+ winding = count_vortices(psi)
143
+ assert np.sum(np.abs(winding)) == 1
144
+ # a nonzero winding number topologically requires a density zero somewhere.
145
+ assert np.min(np.abs(psi) ** 2) < 0.01 * np.max(np.abs(psi) ** 2)
146
+
147
+ def test_rotation_favors_the_vortex_state_above_critical_frequency(self):
148
+ n, length, g = 64, 12.0, 4.0
149
+ X, Y, _, _, K2 = harmonic_trap_grid(n, length)
150
+ V = 0.5 * (X**2 + Y**2)
151
+ psi_vf = gpe_relax(np.exp(-0.5 * (X**2 + Y**2)).astype(complex), V, g, dtau=5e-4, steps=4000, X=X, Y=Y, K2=K2)
152
+ psi0_v = gpe_imprint_vortex(np.exp(-0.5 * (X**2 + Y**2)).astype(complex), X, Y, [(0.0, 0.0)])
153
+ psi_v = gpe_relax(psi0_v, V, g, dtau=5e-4, steps=4000, X=X, Y=Y, K2=K2)
154
+ E0 = gpe_energy(psi_vf, V, g, X, Y, K2)
155
+ E1 = gpe_energy(psi_v, V, g, X, Y, K2)
156
+ omega_c = (E1["total"] - E0["total"]) / (E1["angular_momentum"] - E0["angular_momentum"])
157
+ assert 0.0 < omega_c < 1.0
158
+ # below Omega_c the vortex-free state has lower rotating-frame energy; above, the vortex does.
159
+ below = E0["total"] - 0.5 * omega_c * E0["angular_momentum"] < E1["total"] - 0.5 * omega_c * E1["angular_momentum"]
160
+ above = E1["total"] - 1.5 * omega_c * E1["angular_momentum"] < E0["total"] - 1.5 * omega_c * E0["angular_momentum"]
161
+ assert below and above
162
+
163
+ def test_relax_with_nonzero_omega_stays_normalized(self):
164
+ n, length, g = 24, 10.0, 1.0
165
+ X, Y, _, _, K2 = harmonic_trap_grid(n, length)
166
+ V = 0.5 * (X**2 + Y**2)
167
+ psi0 = np.exp(-0.5 * (X**2 + Y**2)).astype(complex)
168
+ psi = gpe_relax(psi0, V, g, dtau=5e-4, steps=5, X=X, Y=Y, K2=K2, Omega=0.5)
169
+ dx = X[1, 0] - X[0, 0]
170
+ norm = np.sum(np.abs(psi) ** 2) * dx * dx
171
+ assert norm == pytest.approx(1.0, rel=1e-6)
172
+
173
+
174
+ class TestNLSSolitons:
175
+ def test_bright_soliton_does_not_disperse(self):
176
+ N, L = 1024, 80.0
177
+ x = np.linspace(-L / 2, L / 2, N, endpoint=False)
178
+ psi0 = nls_bright_soliton(x, t=0.0, A=1.0)
179
+ psi = nls_evolve(psi0, x, dt=0.001, steps=2000, g=1.0)
180
+ assert np.max(np.abs(np.abs(psi) - np.abs(psi0))) < 1e-3
181
+
182
+ def test_norm_conserved(self):
183
+ N, L = 1024, 80.0
184
+ x = np.linspace(-L / 2, L / 2, N, endpoint=False)
185
+ dx = x[1] - x[0]
186
+ psi0 = nls_bright_soliton(x, t=0.0, A=1.0)
187
+ psi = nls_evolve(psi0, x, dt=0.001, steps=2000, g=1.0)
188
+ assert np.sum(np.abs(psi) ** 2) * dx == pytest.approx(np.sum(np.abs(psi0) ** 2) * dx, rel=1e-9)
189
+
190
+
191
+ def test_poynting_vector_tmz_matches_cross_product_formula():
192
+ # S = E x H with E=(0,0,Ez), H=(Hx,Hy,0) gives (-Ez*Hy, Ez*Hx, 0).
193
+ Ez = np.array([[2.0]])
194
+ Hx = np.array([[3.0]])
195
+ Hy = np.array([[5.0]])
196
+ Sx, Sy = poynting_vector_tmz(Ez, Hx, Hy)
197
+ np.testing.assert_allclose(Sx, [[-10.0]])
198
+ np.testing.assert_allclose(Sy, [[6.0]])
199
+
200
+
201
+ class TestNLSDarkSoliton:
202
+ def test_density_vanishes_at_center(self):
203
+ x = np.array([0.0])
204
+ assert abs(nls_dark_soliton(x, t=0.0, rho0=1.0))[0] == pytest.approx(0.0, abs=1e-12)
205
+
206
+ def test_density_approaches_background_far_from_center(self):
207
+ rho0 = 2.0
208
+ x = np.array([50.0, -50.0])
209
+ np.testing.assert_allclose(np.abs(nls_dark_soliton(x, t=0.0, rho0=rho0)), np.sqrt(rho0), atol=1e-6)
210
+
211
+
212
+ class TestSineGordonKink:
213
+ def test_kink_propagates_at_prescribed_velocity(self):
214
+ N, L, v, x0 = 4000, 200.0, 0.5, -50.0
215
+ x = np.linspace(-L / 2, L / 2, N)
216
+ dx = x[1] - x[0]
217
+ dt = 0.4 * dx
218
+ u_prev = sine_gordon_kink(x, -dt, v, x0)
219
+ u0 = sine_gordon_kink(x, 0.0, v, x0)
220
+ steps = 1000
221
+ u, _ = sine_gordon_evolve(u0, u_prev, x, dt, steps)
222
+ expected = sine_gordon_kink(x, steps * dt, v, x0)
223
+ assert np.max(np.abs(u - expected)) < 0.01
224
+
225
+
226
+ class TestEvolveFramesChaining:
227
+ """The *_evolve_frames wrappers must reproduce plain repeated evolve() calls exactly:
228
+ they are pure chunking/bookkeeping around the existing, already-tested physics."""
229
+
230
+ def test_kdv_frames_match_direct_evolve(self):
231
+ N, L, c = 256, 40.0, 4.0
232
+ x = np.linspace(-L / 2, L / 2, N, endpoint=False)
233
+ u0 = kdv_soliton(x, c, x0=-10)
234
+ dt, steps_per_frame, n_frames = 0.001, 100, 4
235
+ frames, times = kdv_evolve_frames(u0, x, dt, steps_per_frame, n_frames)
236
+ assert frames.shape == (n_frames + 1, N)
237
+ direct = kdv_evolve(u0, x, dt, steps_per_frame * n_frames)
238
+ assert np.max(np.abs(frames[-1] - direct)) < 1e-10
239
+ assert times[-1] == pytest.approx(steps_per_frame * n_frames * dt)
240
+
241
+ def test_nls_frames_match_direct_evolve(self):
242
+ N, L = 256, 40.0
243
+ x = np.linspace(-L / 2, L / 2, N, endpoint=False)
244
+ psi0 = nls_bright_soliton(x, t=0.0, A=1.0)
245
+ dt, steps_per_frame, n_frames = 0.001, 100, 4
246
+ frames, times = nls_evolve_frames(psi0, x, dt, steps_per_frame, n_frames, g=1.0)
247
+ assert frames.shape == (n_frames + 1, N)
248
+ direct = nls_evolve(psi0, x, dt, steps_per_frame * n_frames, g=1.0)
249
+ assert np.max(np.abs(frames[-1] - direct)) < 1e-10
250
+
251
+ def test_sine_gordon_frames_match_direct_evolve(self):
252
+ N, L, v, x0 = 800, 100.0, 0.4, -20.0
253
+ x = np.linspace(-L / 2, L / 2, N)
254
+ dt = 0.4 * (x[1] - x[0])
255
+ u_prev = sine_gordon_kink(x, -dt, v, x0)
256
+ u0 = sine_gordon_kink(x, 0.0, v, x0)
257
+ steps_per_frame, n_frames = 50, 4
258
+ frames, times = sine_gordon_evolve_frames(u0, u_prev, x, dt, steps_per_frame, n_frames)
259
+ assert frames.shape == (n_frames + 1, N)
260
+ direct, _ = sine_gordon_evolve(u0, u_prev, x, dt, steps_per_frame * n_frames)
261
+ assert np.max(np.abs(frames[-1] - direct)) < 1e-10
262
+
263
+
264
+ class TestDipoleRadiationCausality:
265
+ def test_wavefront_respects_causality(self):
266
+ """An oscillating dipole source can't be felt anywhere before the wavefront,
267
+ traveling at c0, has had time to arrive -- the basic physical signature of radiation."""
268
+ N = 120
269
+ dx = dy = 2e-3
270
+ dt = 0.5 * courant_limit_2d(dx, dy)
271
+ eps_r = mu_r = np.ones((N, N))
272
+ Ez0 = Hx0 = Hy0 = np.zeros((N, N))
273
+ sigma = pml_conductivity_profile_2d((N, N), pml_width=15, dx=dx, dy=dy)
274
+ source = oscillating_dipole_source(N // 2, N // 2, amplitude=1.0, freq=3e10)
275
+ r_near, r_far = 10, 40
276
+ snaps, times = fdtd_2d_tmz_evolve(Ez0, Hx0, Hy0, eps_r, mu_r, steps=4000, dt=dt, dx=dx, dy=dy, sigma=sigma, source=source, snapshot_stride=20)
277
+ near_vals = snaps[:, N // 2 + r_near, N // 2]
278
+ far_vals = snaps[:, N // 2 + r_far, N // 2]
279
+ t_near, t_far = r_near * dx / C0, r_far * dx / C0
280
+ idx_near = np.searchsorted(times, t_near * 0.5)
281
+ idx_far = np.searchsorted(times, t_far * 0.5)
282
+ assert idx_near > 0 and idx_far > idx_near # sanity: the test windows are non-trivial
283
+ assert np.max(np.abs(near_vals[:idx_near])) == 0.0
284
+ assert np.max(np.abs(far_vals[:idx_far])) == 0.0
285
+ # after enough time for the wavefront to pass both probes, the field is excited at both
286
+ assert np.max(np.abs(near_vals)) > 0.0
287
+ assert np.max(np.abs(far_vals)) > 0.0
288
+
289
+
290
+ class TestDielectricSlabReflection:
291
+ def test_normal_incidence_reflection_matches_fresnel(self):
292
+ """Uses the existing (already-tested) fdtd_1d, which already supports a spatially
293
+ varying eps_r -- the same physics fdtd_2d_tmz uses for a dielectric-slab demo,
294
+ checked here against the analytic Fresnel reflection coefficient."""
295
+ N, dx = 3000, 1e-3
296
+ dt = 0.99 * courant_limit_1d(dx)
297
+ eta0 = np.sqrt(4 * np.pi * 1e-7 / 8.8541878128e-12)
298
+ x0, sigma_pulse = 500, 20
299
+ xs = np.arange(N)
300
+ Ez0 = np.exp(-((xs - x0) ** 2) / (2 * sigma_pulse**2))
301
+ xh = np.arange(N - 1) + 0.5
302
+ Hy0 = np.exp(-((xh - x0) ** 2) / (2 * sigma_pulse**2)) / eta0
303
+ eps_r_slab = 4.0
304
+ eps_r = np.ones(N)
305
+ eps_r[1500:] = eps_r_slab
306
+ mu_r = np.ones(N)
307
+ Ez, _ = fdtd_1d(Ez0, Hy0, eps_r, mu_r, steps=2400, dt=dt, dx=dx)
308
+ R_measured = np.max(np.abs(Ez[:400]))
309
+ n1, n2 = 1.0, np.sqrt(eps_r_slab)
310
+ R_analytic = abs((n1 - n2) / (n1 + n2))
311
+ assert R_measured == pytest.approx(R_analytic, rel=0.02)
312
+
313
+ def test_dielectric_slab_map_values(self):
314
+ eps_r = dielectric_slab((40, 20), i_start=15, i_end=25, eps_r_slab=4.0)
315
+ assert eps_r[10, 5] == pytest.approx(1.0)
316
+ assert eps_r[20, 5] == pytest.approx(4.0)
317
+ assert eps_r[30, 5] == pytest.approx(1.0)
318
+
319
+
320
+ class TestCavityStandingWaveMode:
321
+ def test_seeded_mode_oscillates_at_analytic_frequency_with_pec_walls(self):
322
+ N = 41
323
+ dx = dy = 1e-3
324
+ dt = 0.5 * courant_limit_2d(dx, dy)
325
+ Ez0, omega = tmz_cavity_mode((N, N), dx, dy, m=1, n=1)
326
+ Hx0 = Hy0 = np.zeros((N, N))
327
+ eps_r = mu_r = np.ones((N, N))
328
+ period = 2 * np.pi / omega
329
+ steps = int(3 * period / dt)
330
+ snaps, times = fdtd_2d_tmz_evolve(Ez0, Hx0, Hy0, eps_r, mu_r, steps=steps, dt=dt, dx=dx, dy=dy, snapshot_stride=1)
331
+ assert np.max(np.abs(snaps[:, 0, :])) == 0.0 # PEC walls: exactly zero, not just small
332
+ assert np.max(np.abs(snaps[:, -1, :])) == 0.0
333
+ probe = snaps[:, N // 2, N // 2]
334
+ spectrum = np.abs(np.fft.rfft(probe - probe.mean()))
335
+ freqs = np.fft.rfftfreq(len(probe), d=dt)
336
+ peak_freq = freqs[np.argmax(spectrum[1:]) + 1]
337
+ measured_omega = 2 * np.pi * peak_freq
338
+ assert measured_omega == pytest.approx(omega, rel=0.02)
339
+
340
+
341
+ class TestFluxTubeConfinement:
342
+ def test_energy_grows_linearly_with_separation(self):
343
+ """The defining signature of confinement (vs. an ordinary field that spreads and
344
+ whose energy is roughly separation-independent): total field energy in the
345
+ confined tube should scale linearly with charge separation."""
346
+ x = np.linspace(-30, 30, 4000)
347
+ dx = x[1] - x[0]
348
+ separations = np.array([6.0, 10.0, 14.0, 18.0, 22.0])
349
+ energies = np.array([np.sum(0.5 * flux_tube_field_1d(x, s) ** 2) * dx for s in separations])
350
+ slope, intercept = np.polyfit(separations, energies, 1)
351
+ predicted = slope * separations + intercept
352
+ assert np.max(np.abs(energies - predicted)) < 1e-6
353
+ assert slope > 0
354
+
355
+ def test_field_plateaus_between_charges_and_vanishes_outside(self):
356
+ x = np.linspace(-20, 20, 2000)
357
+ field = flux_tube_field_1d(x, separation=10.0, flux_quantum=1.0)
358
+ assert abs(field[np.argmin(np.abs(x))] - 1.0) < 0.05
359
+ assert abs(field[np.argmin(np.abs(x - 15))]) < 0.05
360
+
361
+
362
+ class TestCasimirEffect:
363
+ def test_mode_frequencies_are_evenly_spaced(self):
364
+ omega = casimir_mode_frequencies(d=2.0, c=1.0, n_max=10)
365
+ spacing = np.diff(omega)
366
+ assert np.allclose(spacing, np.pi / 2.0)
367
+
368
+ def test_regularized_energy_converges_to_known_closed_form(self):
369
+ d = 3.0
370
+ exact = -np.pi / (24 * d)
371
+ coarse_error = abs(casimir_energy_1d(d, cutoff=3e-3 * d) - exact)
372
+ fine_error = abs(casimir_energy_1d(d, cutoff=7e-4 * d) - exact)
373
+ assert coarse_error < 1e-6
374
+ assert fine_error < coarse_error
375
+
376
+ def test_energy_magnitude_shrinks_as_plates_separate(self):
377
+ # weaker (less negative) zero-point energy at larger separation --
378
+ # the discrete spectrum crowds toward the continuum as d grows.
379
+ assert casimir_energy_1d(1.0) < casimir_energy_1d(2.0) < casimir_energy_1d(4.0) < 0.0
380
+
381
+
382
+ class TestGPERealTimeEvolution:
383
+ def test_norm_is_conserved_under_real_time_propagation(self):
384
+ n, length, g = 40, 12.0, 2.0
385
+ X, Y, _, _, K2 = harmonic_trap_grid(n, length)
386
+ dx = X[1, 0] - X[0, 0]
387
+ V = 0.5 * (X**2 + Y**2)
388
+ psi0 = np.exp(-0.5 * (X**2 + Y**2)).astype(complex)
389
+ psi0 *= 1.0 / np.sqrt(np.sum(np.abs(psi0) ** 2) * dx * dx)
390
+ snaps, _ = gpe_evolve(psi0, V, g, dt=1e-3, steps=400, K2=K2, snapshot_stride=50)
391
+ norms = np.sum(np.abs(snaps) ** 2, axis=(1, 2)) * dx * dx
392
+ assert np.max(np.abs(norms - norms[0])) < 1e-9
393
+
394
+ def test_off_center_vortex_precesses_around_the_trap(self):
395
+ """A vortex imprinted off-center on a relaxed (vortex-free) ground state is not a
396
+ stationary GPE solution -- unlike an imaginary-time relaxation (which would just
397
+ expel it), real-time propagation makes it precess around the trap center at
398
+ roughly constant radius, the standard single-vortex precession phenomenon."""
399
+ n, length, g = 64, 14.0, 4.0
400
+ X, Y, _, _, K2 = harmonic_trap_grid(n, length)
401
+ V = 0.5 * (X**2 + Y**2)
402
+ psi_gs = gpe_relax(np.exp(-0.5 * (X**2 + Y**2)).astype(complex), V, g, dtau=5e-4, steps=4000, X=X, Y=Y, K2=K2)
403
+ psi_v = gpe_imprint_vortex(psi_gs, X, Y, [(1.2, 0.03)])
404
+ snaps, times = gpe_evolve(psi_v, V, g, dt=2e-3, steps=6000, K2=K2, snapshot_stride=300)
405
+
406
+ def core_position(psi, margin=8, threshold=0.0005):
407
+ winding = count_vortices(psi, density_threshold=threshold)
408
+ winding[:margin, :] = 0
409
+ winding[-margin:, :] = 0
410
+ winding[:, :margin] = 0
411
+ winding[:, -margin:] = 0
412
+ idxs = np.argwhere(np.abs(winding) > 0)
413
+ return (float(X[idxs[0, 0], idxs[0, 1]]), float(Y[idxs[0, 0], idxs[0, 1]])) if len(idxs) else None
414
+
415
+ positions = [core_position(snap) for snap in snaps]
416
+ found = [p for p in positions if p is not None]
417
+ assert len(found) >= len(positions) - 3 # the detector should find the core in almost every frame
418
+ angles = np.unwrap([np.arctan2(y, x) for x, y in found])
419
+ radii = [np.hypot(x, y) for x, y in found]
420
+ assert abs(angles[-1] - angles[0]) > np.deg2rad(90) # it has visibly gone around part of an orbit
421
+ assert all(0.3 < r < 3.0 for r in radii) # stays in a bounded annulus -- neither collapsed nor ejected
422
+
423
+ def test_self_focusing_nls_concentrates_before_grid_breaks_down(self):
424
+ """Attractive interactions (g<0 in this module's +g|psi|^2 convention) with a tall,
425
+ narrow packet cause self-focusing: peak density grows and the packet narrows over
426
+ time. True collapse is a singularity this finite grid cannot resolve, so the test
427
+ (like the function's documented usage) stops well before that breakdown."""
428
+ n, length = 128, 20.0
429
+ X, Y, _, _, K2 = harmonic_trap_grid(n, length)
430
+ dx = X[1, 0] - X[0, 0]
431
+ V = np.zeros((n, n))
432
+ psi0 = 3.0 * np.exp(-0.5 * (X**2 + Y**2)).astype(complex)
433
+ norm0 = np.sum(np.abs(psi0) ** 2) * dx * dx
434
+ snaps, _ = gpe_evolve(psi0, V, g=-1.0, dt=2e-4, steps=1400, K2=K2, snapshot_stride=200)
435
+ norms = np.sum(np.abs(snaps) ** 2, axis=(1, 2)) * dx * dx
436
+ peaks = np.max(np.abs(snaps) ** 2, axis=(1, 2))
437
+ assert np.max(np.abs(norms - norm0)) / norm0 < 1e-6
438
+ assert np.all(np.diff(peaks) > 0) # monotonically concentrating
439
+ assert peaks[-1] > 10 * peaks[0] # substantial concentration within the simulated window
@@ -0,0 +1,101 @@
1
+ """Fast (no anim.save(), no file I/O) coverage of physicskit.fields.visualizers:
2
+ static plots and the ax=None/ax=<given> branch of every function, plus
3
+ building each animation and drawing a frame without ever writing a GIF
4
+ (see physicskit/fields/tests/test_field_animations.py for the real
5
+ save()-to-disk smoke tests, marked slow)."""
6
+
7
+ from __future__ import annotations
8
+
9
+ import matplotlib.pyplot as plt
10
+ import numpy as np
11
+
12
+ from physicskit.fields.quantum_fields import harmonic_trap_grid
13
+ from physicskit.fields.visualizers import (
14
+ animate_density_2d,
15
+ animate_field_1d,
16
+ animate_field_2d,
17
+ animate_flux_tube,
18
+ plot_bec_density,
19
+ plot_bec_phase,
20
+ plot_field_1d,
21
+ plot_poynting_field,
22
+ )
23
+
24
+
25
+ def test_plot_field_1d_with_and_without_label_and_given_ax():
26
+ x = np.linspace(-10, 10, 50)
27
+ u = np.sin(x)
28
+
29
+ fig, ax = plot_field_1d(x, u)
30
+ assert isinstance(fig, plt.Figure)
31
+ assert not ax.get_legend()
32
+
33
+ fig2, ax2 = plot_field_1d(x, u, label="u(x)")
34
+ assert ax2.get_legend() is not None
35
+
36
+ _, ax_given = plot_field_1d(x, u, ax=ax)
37
+ assert ax_given is ax
38
+
39
+
40
+ def test_plot_poynting_field_given_ax():
41
+ X, Y = np.meshgrid(np.linspace(-1, 1, 10), np.linspace(-1, 1, 10))
42
+ Sx, Sy = -Y, X
43
+ fig, ax = plot_poynting_field(X, Y, Sx, Sy)
44
+ assert isinstance(fig, plt.Figure)
45
+
46
+ _, ax_given = plot_poynting_field(X, Y, Sx, Sy, ax=ax)
47
+ assert ax_given is ax
48
+
49
+
50
+ def test_plot_bec_density_and_phase_given_ax():
51
+ X, Y, _, _, _ = harmonic_trap_grid(16, 10.0)
52
+ psi = np.exp(-0.5 * (X**2 + Y**2)).astype(complex)
53
+
54
+ fig, ax = plot_bec_density(X, Y, psi)
55
+ assert isinstance(fig, plt.Figure)
56
+ _, ax_given = plot_bec_density(X, Y, psi, ax=ax)
57
+ assert ax_given is ax
58
+
59
+ fig2, ax2 = plot_bec_phase(X, Y, psi)
60
+ assert isinstance(fig2, plt.Figure)
61
+ _, ax2_given = plot_bec_phase(X, Y, psi, ax=ax2)
62
+ assert ax2_given is ax2
63
+
64
+
65
+ def test_animate_field_1d_builds_and_draws_a_frame_given_ax():
66
+ x = np.linspace(-10, 10, 30)
67
+ frames = np.array([np.sin(x + phase) for phase in np.linspace(0, 1, 4)])
68
+ times = np.linspace(0, 1, 4)
69
+
70
+ fig, ax = plt.subplots()
71
+ anim = animate_field_1d(x, frames, times, ax=ax)
72
+ assert anim._fig is fig
73
+ anim._draw_frame(1)
74
+ assert ax.get_title() == f"t = {times[1]:.4g}"
75
+
76
+
77
+ def test_animate_field_2d_builds_and_draws_a_frame_given_ax():
78
+ X, Y = np.meshgrid(np.arange(8), np.arange(8), indexing="ij")
79
+ frames = np.random.default_rng(0).normal(size=(3, 8, 8))
80
+
81
+ fig, ax = plt.subplots()
82
+ anim = animate_field_2d(X, Y, frames, ax=ax)
83
+ assert anim._fig is fig
84
+ anim._draw_frame(1)
85
+ assert ax.get_title() == "frame 1" # times=None branch
86
+
87
+
88
+ def test_animate_density_2d_builds_and_draws_a_frame_given_ax():
89
+ frames = np.random.default_rng(0).random(size=(3, 8, 8))
90
+
91
+ fig, ax = plt.subplots()
92
+ anim = animate_density_2d(frames, ax=ax)
93
+ assert anim._fig is fig
94
+ anim._draw_frame(1)
95
+
96
+
97
+ def test_animate_flux_tube_builds_and_draws_a_frame_given_ax():
98
+ fig, ax = plt.subplots()
99
+ anim = animate_flux_tube((20, 10), dx=0.25, dy=0.25, separations=np.linspace(4.0, 8.0, 3), ax=ax)
100
+ assert anim._fig is fig
101
+ anim._draw_frame(1)