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,536 @@
1
+ """Newtonian vector-dynamics systems.
2
+
3
+ :class:`ProjectileMotion` is the classic "cannonball" problem -- free
4
+ fall under constant gravity with an oblique (angled) initial velocity,
5
+ the trajectory-is-a-parabola result at the historical root of
6
+ Newtonian dynamics -- with an optional quadratic air-drag term.
7
+
8
+ :class:`KeplerSystem` integrates a reduced 2-body problem in the
9
+ center-of-mass frame as a planar, separable Hamiltonian system in
10
+ Cartesian coordinates q = (x, y), p = (px, py), with::
11
+
12
+ H(q, p) = |p|^2 / (2 mu) + V(r), r = |q|
13
+ V(r) = -k / r + c_eps / r**(1 + eps) + c_pn / r**3
14
+
15
+ The ``c_eps`` term is an adjustable power-law perturbation to the pure
16
+ 1/r potential; ``c_pn`` is an effective post-Newtonian correction that
17
+ reproduces the qualitative apsidal (perihelion) precession General
18
+ Relativity predicts for Mercury's orbit. Both terms are still purely
19
+ radial (velocity-independent), so the system remains conservative and
20
+ separable -- exactly integrable with the symplectic Verlet/Yoshida4
21
+ backends -- while the Laplace-Runge-Lenz vector, exactly conserved for
22
+ the pure Kepler problem, slowly precesses once either perturbation is
23
+ turned on.
24
+
25
+ :class:`FoucaultPendulum` is a small-oscillation pendulum viewed from
26
+ the rotating Earth: Newton's second law in a non-inertial frame picks
27
+ up a velocity-dependent Coriolis term that precesses the swing plane
28
+ without doing any work, so energy stays exactly conserved even as the
29
+ plane of swing rotates -- the effect Leon Foucault used in 1851 to
30
+ demonstrate Earth's rotation mechanically.
31
+ """
32
+
33
+ from __future__ import annotations
34
+
35
+ from functools import lru_cache
36
+
37
+ import numpy as np
38
+ from numba import njit
39
+
40
+ from physicskit.classical.core.base_system import HamiltonianSystem, ODESystem
41
+
42
+ __all__ = ["ProjectileMotion", "KeplerSystem", "FoucaultPendulum"]
43
+
44
+
45
+ @lru_cache(maxsize=64)
46
+ def _make_projectile_force(m: float, g: float):
47
+ @njit(cache=True)
48
+ def force(q, t):
49
+ out = np.empty(2)
50
+ out[0] = 0.0
51
+ out[1] = -m * g
52
+ return out
53
+
54
+ return force
55
+
56
+
57
+ @lru_cache(maxsize=64)
58
+ def _make_projectile_deriv_with_drag(m: float, g: float, drag_coeff: float):
59
+ """Full (non-conservative) [dq, dp] derivative including quadratic
60
+ air drag, ``F_drag = -drag_coeff * |v| * v``, used by the 'rk4' and
61
+ 'implicit_midpoint' fallback methods when drag_coeff != 0 (drag
62
+ depends on velocity/momentum, so it cannot enter a separable
63
+ force(q, t) and is invisible to the Verlet/Yoshida4 backends)."""
64
+
65
+ @njit(cache=False)
66
+ def deriv(t, y):
67
+ px, py = y[2], y[3]
68
+ vx, vy = px / m, py / m
69
+ speed = np.sqrt(vx * vx + vy * vy)
70
+ out = np.empty(4)
71
+ out[0] = vx
72
+ out[1] = vy
73
+ out[2] = -drag_coeff * speed * vx
74
+ out[3] = -m * g - drag_coeff * speed * vy
75
+ return out
76
+
77
+ return deriv
78
+
79
+
80
+ class ProjectileMotion(HamiltonianSystem):
81
+ """Free fall under constant gravity with an oblique initial velocity
82
+ -- the "cannonball" problem -- with optional quadratic air drag.
83
+
84
+ Without drag this is the separable Hamiltonian
85
+ ``H(q, p) = |p|^2/(2m) + m*g*y``, q = (x, y), whose exact solution is
86
+ Galileo's parabolic trajectory (see :meth:`analytic_trajectory`);
87
+ because the force is constant, the symplectic Verlet/Yoshida4
88
+ backends reproduce it to machine precision at any step size.
89
+
90
+ With ``drag_coeff > 0`` a quadratic air-resistance force
91
+ ``F = -drag_coeff * |v| * v`` is added. This makes the system
92
+ genuinely dissipative (energy decreases monotonically, there is no
93
+ closed-form trajectory), so it is only available through the
94
+ ``rk4``/``implicit_midpoint`` methods -- ``verlet``/``yoshida4``
95
+ would silently ignore it (their force callback only sees the
96
+ conservative gravity term) and are therefore refused outright.
97
+
98
+ Parameters
99
+ ----------
100
+ q0, p0 : array-like, shape (2,)
101
+ Initial position and momentum, q = (x, y), p = (px, py).
102
+ m, g : float
103
+ Projectile mass and gravitational acceleration.
104
+ drag_coeff : float
105
+ Quadratic air-drag coefficient (0 for the vacuum/exact case).
106
+ """
107
+
108
+ separable = True
109
+
110
+ def __init__(self, q0, p0, m: float = 1.0, g: float = 9.81, drag_coeff: float = 0.0):
111
+ self.m, self.g, self.drag_coeff = m, g, drag_coeff
112
+ self.mass_inv = 1.0 / m
113
+ self._force_njit = _make_projectile_force(m, g)
114
+ if drag_coeff:
115
+ self._deriv_njit = _make_projectile_deriv_with_drag(m, g, drag_coeff)
116
+ super().__init__(q0, p0)
117
+
118
+ def integrate(self, t_span, dt: float, method: str = "yoshida4"):
119
+ if self.drag_coeff and method in ("verlet", "yoshida4"):
120
+ raise ValueError(
121
+ f"method='{method}' only sees the conservative force and would silently ignore drag_coeff != 0; use method='rk4' or 'implicit_midpoint' instead"
122
+ )
123
+ return super().integrate(t_span, dt, method=method)
124
+
125
+ def kinetic_energy(self, p: np.ndarray) -> float:
126
+ return float(np.dot(p, p)) / (2.0 * self.m)
127
+
128
+ def potential_energy(self, q: np.ndarray) -> float:
129
+ return self.m * self.g * q[1]
130
+
131
+ @classmethod
132
+ def from_launch(cls, speed: float, angle_deg: float, m: float = 1.0, g: float = 9.81, drag_coeff: float = 0.0, height: float = 0.0):
133
+ """Construct a system launched from ``(0, height)`` -- the natural "aim the cannon" entry point.
134
+
135
+ Parameters
136
+ ----------
137
+ speed : float
138
+ Launch speed.
139
+ angle_deg : float
140
+ Launch angle above the horizontal, in degrees.
141
+ m, g : float
142
+ Projectile mass and gravitational acceleration.
143
+ drag_coeff : float
144
+ Quadratic air-drag coefficient (0 for the vacuum/exact case).
145
+ height : float
146
+ Initial height.
147
+
148
+ Returns
149
+ -------
150
+ ProjectileMotion
151
+ """
152
+ theta = np.deg2rad(angle_deg)
153
+ q0 = np.array([0.0, height])
154
+ p0 = m * speed * np.array([np.cos(theta), np.sin(theta)])
155
+ return cls(q0, p0, m=m, g=g, drag_coeff=drag_coeff)
156
+
157
+ @staticmethod
158
+ def analytic_trajectory(speed: float, angle_deg: float, g: float, t, height: float = 0.0):
159
+ """Exact vacuum trajectory x(t), y(t) -- Galileo's parabola.
160
+
161
+ Used to validate the numerical integration (``drag_coeff=0`` only).
162
+
163
+ Parameters
164
+ ----------
165
+ speed, angle_deg : float
166
+ Launch speed and angle above the horizontal, in degrees.
167
+ g : float
168
+ Gravitational acceleration.
169
+ t : array-like
170
+ Times at which to evaluate the trajectory.
171
+ height : float
172
+ Initial height.
173
+
174
+ Returns
175
+ -------
176
+ x, y : ndarray
177
+ """
178
+ theta = np.deg2rad(angle_deg)
179
+ t = np.asarray(t, dtype=np.float64)
180
+ vx0, vy0 = speed * np.cos(theta), speed * np.sin(theta)
181
+ x = vx0 * t
182
+ y = height + vy0 * t - 0.5 * g * t**2
183
+ return x, y
184
+
185
+ @staticmethod
186
+ def range_and_max_height(speed: float, angle_deg: float, g: float, height: float = 0.0):
187
+ """Closed-form (vacuum) horizontal range and maximum height.
188
+
189
+ Parameters
190
+ ----------
191
+ speed, angle_deg : float
192
+ Launch speed and angle above the horizontal, in degrees.
193
+ g : float
194
+ Gravitational acceleration.
195
+ height : float
196
+ Initial height.
197
+
198
+ Returns
199
+ -------
200
+ range, max_height : float
201
+ """
202
+ theta = np.deg2rad(angle_deg)
203
+ vx0, vy0 = speed * np.cos(theta), speed * np.sin(theta)
204
+ t_apex = vy0 / g
205
+ max_height = height + vy0 * t_apex - 0.5 * g * t_apex**2
206
+ t_flight = (vy0 + np.sqrt(vy0**2 + 2.0 * g * height)) / g
207
+ rng = vx0 * t_flight
208
+ return float(rng), float(max_height)
209
+
210
+
211
+ @lru_cache(maxsize=64)
212
+ def _make_force_njit(k: float, eps: float, c_eps: float, c_pn: float, softening: float):
213
+ @njit(cache=False)
214
+ def force(q, t):
215
+ x, y = q[0], q[1]
216
+ r2 = x * x + y * y + softening * softening
217
+ r = np.sqrt(r2)
218
+ # dV/dr for V(r) = -k/r + c_eps * r**(-(1+eps)) + c_pn * r**(-3)
219
+ dVdr = k / r2 - c_eps * (1.0 + eps) * r ** (-(2.0 + eps)) - 3.0 * c_pn * r ** (-4.0)
220
+ # force = -dV/dq = -dVdr * (q / r)
221
+ fmag = -dVdr / r
222
+ out = np.empty(2)
223
+ out[0] = fmag * x
224
+ out[1] = fmag * y
225
+ return out
226
+
227
+ return force
228
+
229
+
230
+ class KeplerSystem(HamiltonianSystem):
231
+ """Planar 2-body Kepler problem with optional non-Newtonian perturbations.
232
+
233
+ Parameters
234
+ ----------
235
+ q0, p0 : array-like, shape (2,)
236
+ Initial position and (reduced-mass) momentum.
237
+ k : float
238
+ Gravitational coupling ``G * M * mu`` (standard Kepler strength).
239
+ mu : float
240
+ Reduced mass (sets kinetic energy ``T = |p|^2 / (2 mu)``).
241
+ eps, c_eps : float
242
+ Exponent and coefficient of an additional ``c_eps / r**(1+eps)``
243
+ perturbing potential (eps=0 reduces to an extra inverse-square term).
244
+ c_pn : float
245
+ Coefficient of the effective post-Newtonian ``c_pn / r**3``
246
+ correction driving perihelion precession.
247
+ softening : float
248
+ Small Plummer-style softening length to avoid a singular force
249
+ at r=0 (0 for the exact point-mass limit).
250
+ """
251
+
252
+ separable = True
253
+
254
+ def __init__(self, q0, p0, k=1.0, mu=1.0, eps=0.0, c_eps=0.0, c_pn=0.0, softening=0.0):
255
+ self.k = k
256
+ self.mu = mu
257
+ self.eps = eps
258
+ self.c_eps = c_eps
259
+ self.c_pn = c_pn
260
+ self.softening = softening
261
+ self.mass_inv = 1.0 / mu
262
+ self._force_njit = _make_force_njit(k, eps, c_eps, c_pn, softening)
263
+ super().__init__(q0, p0)
264
+
265
+ def kinetic_energy(self, p: np.ndarray) -> float:
266
+ return float(np.dot(p, p)) / (2.0 * self.mu)
267
+
268
+ def potential_energy(self, q: np.ndarray) -> float:
269
+ r = np.sqrt(q[0] ** 2 + q[1] ** 2 + self.softening**2)
270
+ return -self.k / r + self.c_eps * r ** (-(1.0 + self.eps)) + self.c_pn * r ** (-3.0)
271
+
272
+ def angular_momentum(self, q: np.ndarray = None, p: np.ndarray = None) -> float:
273
+ """Out-of-plane angular momentum ``L = x*py - y*px``.
274
+
275
+ Parameters
276
+ ----------
277
+ q, p : ndarray, optional
278
+ State to evaluate; defaults to the current state.
279
+
280
+ Returns
281
+ -------
282
+ float
283
+ """
284
+ q = self.q if q is None else q
285
+ p = self.p if p is None else p
286
+ return float(q[0] * p[1] - q[1] * p[0])
287
+
288
+ def lrl_vector(self, q: np.ndarray = None, p: np.ndarray = None) -> np.ndarray:
289
+ """Laplace-Runge-Lenz vector ``A = p x L - mu*k*r_hat`` (planar form).
290
+
291
+ Exactly conserved (a fixed vector pointing at perihelion) for the
292
+ unperturbed 1/r potential; its slow rotation once ``c_eps`` or
293
+ ``c_pn`` is nonzero is the precession signature (e.g. Mercury's
294
+ perihelion advance).
295
+
296
+ Parameters
297
+ ----------
298
+ q, p : ndarray, optional
299
+ State to evaluate; defaults to the current state.
300
+
301
+ Returns
302
+ -------
303
+ ndarray, shape (2,)
304
+ """
305
+ q = self.q if q is None else q
306
+ p = self.p if p is None else p
307
+ L = self.angular_momentum(q, p)
308
+ r = np.linalg.norm(q)
309
+ # p x L in 2D (L is the out-of-plane scalar): (py*L, -px*L)
310
+ px_L = np.array([p[1] * L, -p[0] * L])
311
+ return px_L - self.mu * self.k * q / r
312
+
313
+ @classmethod
314
+ def from_orbital_elements(cls, a: float, e: float, k: float = 1.0, mu: float = 1.0, **kwargs):
315
+ """Construct a system starting at perihelion (pure two-body reference orbit).
316
+
317
+ Parameters
318
+ ----------
319
+ a : float
320
+ Semi-major axis.
321
+ e : float
322
+ Eccentricity.
323
+ k, mu : float
324
+ Gravitational coupling and reduced mass, as in ``__init__``.
325
+ **kwargs
326
+ Forwarded to ``__init__`` (e.g. ``eps``, ``c_eps``, ``c_pn``, ``softening``).
327
+
328
+ Returns
329
+ -------
330
+ KeplerSystem
331
+ """
332
+ r_peri = a * (1.0 - e)
333
+ v_peri = np.sqrt(k / mu * (2.0 / r_peri - 1.0 / a))
334
+ q0 = np.array([r_peri, 0.0])
335
+ p0 = np.array([0.0, mu * v_peri])
336
+ return cls(q0, p0, k=k, mu=mu, **kwargs)
337
+
338
+
339
+ _SIDEREAL_DAY = 86164.0905
340
+ """float: Earth's rotation period relative to the stars, in seconds."""
341
+
342
+
343
+ @lru_cache(maxsize=64)
344
+ def _make_foucault_deriv(omega0: float, omega_z: float):
345
+ @njit(cache=True)
346
+ def deriv(t, state):
347
+ x, y, vx, vy = state[0], state[1], state[2], state[3]
348
+ out = np.empty(4)
349
+ out[0] = vx
350
+ out[1] = vy
351
+ out[2] = -omega0 * omega0 * x + 2.0 * omega_z * vy
352
+ out[3] = -omega0 * omega0 * y - 2.0 * omega_z * vx
353
+ return out
354
+
355
+ return deriv
356
+
357
+
358
+ class FoucaultPendulum(ODESystem):
359
+ """Small-oscillation pendulum viewed from Earth's rotating frame.
360
+
361
+ Swinging near a fixed vertical, a pendulum's horizontal displacement
362
+ q = (x, y) (x = East, y = North) obeys the isotropic harmonic
363
+ equation ``qddot = -omega0**2 * q`` in a non-rotating (inertial)
364
+ frame. Viewed instead from the surface of the rotating Earth, that
365
+ frame itself turns about the local vertical at
366
+ ``omega_z = omega_earth * sin(latitude)`` -- the projection of
367
+ Earth's angular velocity onto the local vertical -- which adds a
368
+ velocity-dependent Coriolis term::
369
+
370
+ xddot = -omega0**2 * x + 2 * omega_z * ydot
371
+ yddot = -omega0**2 * y - 2 * omega_z * xdot
372
+
373
+ Because the Coriolis force is always perpendicular to the velocity
374
+ it does no work, so the (non-rotating-frame) mechanical energy
375
+ ``T + V = |v|**2 / 2 + omega0**2 * |q|**2 / 2`` stays exactly
376
+ conserved even as the plane of swing itself steadily precesses --
377
+ exactly the effect Leon Foucault used in 1851 to demonstrate
378
+ Earth's rotation mechanically, without reference to the sky.
379
+
380
+ Parameters
381
+ ----------
382
+ q0, v0 : array-like, shape (2,)
383
+ Initial horizontal displacement (x, y) and velocity (vx, vy) of
384
+ the bob relative to the vertical through the pivot.
385
+ length, g : float
386
+ Pendulum length and gravitational acceleration, setting the
387
+ non-rotating swing frequency ``omega0 = sqrt(g / length)``.
388
+ latitude_deg : float
389
+ Geographic latitude in degrees (default 48.85, the Paris
390
+ Pantheon's latitude for Foucault's original 1851 demonstration).
391
+ omega_earth : float
392
+ Earth's sidereal rotation rate, ``2*pi / 86164.0905`` rad/s by default.
393
+ """
394
+
395
+ def __init__(
396
+ self,
397
+ q0,
398
+ v0,
399
+ length: float = 67.0,
400
+ g: float = 9.81,
401
+ latitude_deg: float = 48.85,
402
+ omega_earth: float = 2.0 * np.pi / _SIDEREAL_DAY,
403
+ ):
404
+ self.length, self.g = length, g
405
+ self.omega0 = float(np.sqrt(g / length))
406
+ self.latitude_deg = latitude_deg
407
+ self.omega_earth = omega_earth
408
+ self.omega_z = float(omega_earth * np.sin(np.deg2rad(latitude_deg)))
409
+ self._deriv_njit = _make_foucault_deriv(self.omega0, self.omega_z)
410
+ state0 = np.concatenate([np.asarray(q0, dtype=np.float64), np.asarray(v0, dtype=np.float64)])
411
+ super().__init__(state0)
412
+
413
+ @property
414
+ def position(self) -> np.ndarray:
415
+ """ndarray, shape (2,): Current horizontal displacement (x, y)."""
416
+ return self.state[:2]
417
+
418
+ @property
419
+ def velocity(self) -> np.ndarray:
420
+ """ndarray, shape (2,): Current horizontal velocity (vx, vy)."""
421
+ return self.state[2:]
422
+
423
+ def energy(self, state: np.ndarray = None) -> float:
424
+ state = self.state if state is None else state
425
+ x, y, vx, vy = state[0], state[1], state[2], state[3]
426
+ return 0.5 * (vx * vx + vy * vy) + 0.5 * self.omega0**2 * (x * x + y * y)
427
+
428
+ @classmethod
429
+ def from_deflection(
430
+ cls,
431
+ amplitude: float,
432
+ angle_deg: float = 0.0,
433
+ length: float = 67.0,
434
+ g: float = 9.81,
435
+ latitude_deg: float = 48.85,
436
+ omega_earth: float = 2.0 * np.pi / _SIDEREAL_DAY,
437
+ ):
438
+ """Construct a system released from rest at a fixed deflection.
439
+
440
+ This is how a real Foucault pendulum is started: pulled aside
441
+ and tied off, then released from rest.
442
+
443
+ Parameters
444
+ ----------
445
+ amplitude : float
446
+ Initial horizontal displacement magnitude.
447
+ angle_deg : float
448
+ Direction of the initial displacement, in degrees
449
+ counterclockwise from East.
450
+ length, g, latitude_deg, omega_earth : float
451
+ As in ``__init__``.
452
+
453
+ Returns
454
+ -------
455
+ FoucaultPendulum
456
+ """
457
+ theta = np.deg2rad(angle_deg)
458
+ q0 = amplitude * np.array([np.cos(theta), np.sin(theta)])
459
+ v0 = np.zeros(2)
460
+ return cls(q0, v0, length=length, g=g, latitude_deg=latitude_deg, omega_earth=omega_earth)
461
+
462
+ def precession_period(self) -> float:
463
+ """Time for the swing plane to complete one full precession.
464
+
465
+ Equal to the sidereal day divided by ``sin(latitude)`` -- a
466
+ "Foucault day" -- e.g. about 32 hours at Paris's latitude.
467
+
468
+ Returns
469
+ -------
470
+ float
471
+ """
472
+ return 2.0 * np.pi / abs(self.omega_z)
473
+
474
+ @staticmethod
475
+ def analytic_solution(q0, v0, omega0: float, omega_z: float, t):
476
+ """Exact horizontal trajectory x(t), y(t) in the small-oscillation approximation.
477
+
478
+ The complex displacement ``w = x + i*y`` satisfies the linear
479
+ equation ``wddot + 2i*omega_z*wdot + omega0**2*w = 0``.
480
+ Substituting ``w = exp(-i*omega_z*t) * u(t)`` reduces this to
481
+ plain simple harmonic motion for ``u`` at frequency
482
+ ``Omega = sqrt(omega0**2 + omega_z**2)``: the solution is an
483
+ ordinary (non-rotating) harmonic swing pattern ``u(t)``, rotated
484
+ rigidly as a whole at the constant rate ``omega_z`` -- exactly
485
+ the precession Foucault observed.
486
+
487
+ Parameters
488
+ ----------
489
+ q0, v0 : array-like, shape (2,)
490
+ Initial displacement and velocity.
491
+ omega0, omega_z : float
492
+ Non-rotating swing frequency and the local-vertical
493
+ component of Earth's angular velocity.
494
+ t : array-like
495
+ Times at which to evaluate the trajectory.
496
+
497
+ Returns
498
+ -------
499
+ x, y : ndarray
500
+ """
501
+ t = np.asarray(t, dtype=np.float64)
502
+ Omega = np.sqrt(omega0**2 + omega_z**2)
503
+ w0 = complex(q0[0], q0[1])
504
+ wdot0 = complex(v0[0], v0[1])
505
+ u0 = w0
506
+ udot0 = wdot0 + 1j * omega_z * w0
507
+ u = u0 * np.cos(Omega * t) + (udot0 / Omega) * np.sin(Omega * t)
508
+ w = np.exp(-1j * omega_z * t) * u
509
+ return w.real, w.imag
510
+
511
+ @staticmethod
512
+ def to_corotating_frame(x, y, t, omega_z: float):
513
+ """Rotate (x, y) by ``+omega_z * t``, undoing the precession.
514
+
515
+ In this frame the pendulum swings in a single fixed plane, just
516
+ like an ordinary non-rotating pendulum -- the frame in which
517
+ Foucault's precession is "unwound".
518
+
519
+ Parameters
520
+ ----------
521
+ x, y : array-like
522
+ Trajectory in the lab (ground) frame.
523
+ t : array-like
524
+ Corresponding times.
525
+ omega_z : float
526
+ Local-vertical component of Earth's angular velocity.
527
+
528
+ Returns
529
+ -------
530
+ x_rot, y_rot : ndarray
531
+ """
532
+ x, y, t = np.asarray(x), np.asarray(y), np.asarray(t)
533
+ c, s = np.cos(omega_z * t), np.sin(omega_z * t)
534
+ x_rot = c * x - s * y
535
+ y_rot = s * x + c * y
536
+ return x_rot, y_rot