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,281 @@
1
+ """1D compressible flow: the Euler equations, normal shocks, and the Sod shock tube.
2
+
3
+ Where potential and viscous flow assume incompressibility, this module
4
+ drops that assumption entirely: the 1D Euler equations for an inviscid,
5
+ compressible, ideal gas support genuinely discontinuous solutions (shocks
6
+ and contact discontinuities) that no continuous velocity field can produce.
7
+ :func:`rankine_hugoniot_jump_conditions` and :func:`normal_shock_relations`
8
+ give the exact algebraic jump a shock must satisfy; :func:`sod_shock_tube`
9
+ resolves that jump -- alongside a contact discontinuity and an expansion
10
+ fan -- dynamically, by integrating the full nonlinear equations through a
11
+ classic Riemann problem.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import numpy as np
17
+ from numba import njit
18
+ from numpy.typing import NDArray
19
+
20
+ from physicskit.fluids.exceptions import InvalidParameterError
21
+
22
+ __all__ = [
23
+ "rankine_hugoniot_jump_conditions",
24
+ "normal_shock_relations",
25
+ "sod_shock_tube",
26
+ ]
27
+
28
+
29
+ def rankine_hugoniot_jump_conditions(rho1: float, u1: float, p1: float, rho2: float, u2: float, p2: float) -> dict[str, float]:
30
+ """Residuals of the Rankine-Hugoniot jump conditions across a stationary discontinuity.
31
+
32
+ A steady discontinuity in a 1D inviscid compressible flow (states 1
33
+ upstream, 2 downstream, in the frame where the discontinuity itself is
34
+ at rest) must conserve mass, momentum, and energy flux exactly:
35
+
36
+ .. math::
37
+
38
+ \\rho_1 u_1 = \\rho_2 u_2, \\qquad
39
+ p_1 + \\rho_1 u_1^2 = p_2 + \\rho_2 u_2^2, \\qquad
40
+ h_1 + \\tfrac{1}{2}u_1^2 = h_2 + \\tfrac{1}{2}u_2^2,
41
+
42
+ where :math:`h=\\gamma p/((\\gamma-1)\\rho)` is specific enthalpy (an
43
+ ideal gas is assumed only for the energy residual). Rankine (1870) first
44
+ wrote the mass and momentum conditions; Hugoniot (1887, 1889) added the
45
+ energy condition and the resulting relation between the shock's pressure
46
+ and density ratios now called the Hugoniot curve. This function returns
47
+ each condition's residual, primarily to *verify* a candidate jump (e.g.
48
+ the output of :func:`normal_shock_relations`) rather than to solve for
49
+ one directly.
50
+
51
+ Parameters
52
+ ----------
53
+ rho1, u1, p1 : float
54
+ Upstream density, velocity, and pressure.
55
+ rho2, u2, p2 : float
56
+ Downstream density, velocity, and pressure.
57
+
58
+ Returns
59
+ -------
60
+ dict
61
+ ``{"mass": ..., "momentum": ..., "energy": ...}`` residuals; all
62
+ zero for an exactly satisfied jump.
63
+
64
+ Examples
65
+ --------
66
+ >>> jump = normal_shock_relations(M1=2.0)
67
+ >>> residuals = rankine_hugoniot_jump_conditions(1.0, 2.0, 1.0 / 1.4, jump["rho2_rho1"], 2.0 / jump["rho2_rho1"], jump["p2_p1"] / 1.4)
68
+ >>> bool(max(abs(v) for v in residuals.values()) < 1e-10)
69
+ True
70
+ """
71
+ gamma = 1.4
72
+ mass = rho1 * u1 - rho2 * u2
73
+ momentum = (p1 + rho1 * u1**2) - (p2 + rho2 * u2**2)
74
+ h1 = gamma * p1 / ((gamma - 1.0) * rho1)
75
+ h2 = gamma * p2 / ((gamma - 1.0) * rho2)
76
+ energy = (h1 + 0.5 * u1**2) - (h2 + 0.5 * u2**2)
77
+ return {"mass": mass, "momentum": momentum, "energy": energy}
78
+
79
+
80
+ def normal_shock_relations(M1: float, gamma: float = 1.4) -> dict[str, float]:
81
+ """Ideal-gas normal shock relations as a function of upstream Mach number.
82
+
83
+ Solving the Rankine-Hugoniot conditions
84
+ (:func:`rankine_hugoniot_jump_conditions`) for an ideal gas gives the
85
+ downstream state entirely in terms of the upstream Mach number
86
+ :math:`M_1 = u_1/c_1`:
87
+
88
+ .. math::
89
+
90
+ \\frac{p_2}{p_1} = 1 + \\frac{2\\gamma}{\\gamma+1}(M_1^2-1), \\qquad
91
+ \\frac{\\rho_2}{\\rho_1} = \\frac{(\\gamma+1)M_1^2}{(\\gamma-1)M_1^2+2}, \\qquad
92
+ M_2^2 = \\frac{1+\\tfrac{\\gamma-1}{2}M_1^2}{\\gamma M_1^2-\\tfrac{\\gamma-1}{2}}.
93
+
94
+ Only :math:`M_1 \\geq 1` gives a physically admissible (entropy-increasing)
95
+ shock; a supersonic upstream flow needs a mechanism, such as this jump,
96
+ to return to subsonic downstream, and no analogous jump exists in reverse.
97
+
98
+ Parameters
99
+ ----------
100
+ M1 : float
101
+ Upstream Mach number; must be at least 1.
102
+ gamma : float, default 1.4
103
+ Ratio of specific heats (1.4 for a diatomic ideal gas, e.g. air).
104
+
105
+ Returns
106
+ -------
107
+ dict
108
+ ``{"p2_p1": ..., "rho2_rho1": ..., "T2_T1": ..., "M2": ...}``.
109
+
110
+ Raises
111
+ ------
112
+ InvalidParameterError
113
+ If `M1` is less than 1.
114
+
115
+ Examples
116
+ --------
117
+ >>> jump = normal_shock_relations(M1=1.0)
118
+ >>> [round(v, 6) for v in (jump["p2_p1"], jump["rho2_rho1"], jump["M2"])]
119
+ [1.0, 1.0, 1.0]
120
+ >>> jump = normal_shock_relations(M1=2.0)
121
+ >>> round(jump["p2_p1"], 3), round(jump["rho2_rho1"], 3), round(jump["M2"], 3)
122
+ (4.5, 2.667, 0.577)
123
+ """
124
+ if M1 < 1.0:
125
+ raise InvalidParameterError(f"M1 must be >= 1 for an admissible (entropy-increasing) shock, got {M1}")
126
+ p2_p1 = 1.0 + (2.0 * gamma / (gamma + 1.0)) * (M1**2 - 1.0)
127
+ rho2_rho1 = ((gamma + 1.0) * M1**2) / ((gamma - 1.0) * M1**2 + 2.0)
128
+ T2_T1 = p2_p1 / rho2_rho1
129
+ M2 = np.sqrt((1.0 + 0.5 * (gamma - 1.0) * M1**2) / (gamma * M1**2 - 0.5 * (gamma - 1.0)))
130
+ return {"p2_p1": float(p2_p1), "rho2_rho1": float(rho2_rho1), "T2_T1": float(T2_T1), "M2": float(M2)}
131
+
132
+
133
+ @njit(cache=True)
134
+ def _euler_flux(U: NDArray[np.float64], gamma: float) -> NDArray[np.float64]:
135
+ """Physical flux of the 1D Euler equations in conservative variables.
136
+
137
+ Parameters
138
+ ----------
139
+ U : ndarray of float, shape (3,)
140
+ Conservative state ``(rho, rho*u, E)``.
141
+ gamma : float
142
+ Ratio of specific heats.
143
+
144
+ Returns
145
+ -------
146
+ ndarray of float, shape (3,)
147
+ Flux ``(rho*u, rho*u^2 + p, u*(E + p))``.
148
+ """
149
+ rho, mom, E = U[0], U[1], U[2]
150
+ u = mom / rho
151
+ p = (gamma - 1.0) * (E - 0.5 * rho * u * u)
152
+ flux = np.empty(3)
153
+ flux[0] = mom
154
+ flux[1] = mom * u + p
155
+ flux[2] = u * (E + p)
156
+ return flux
157
+
158
+
159
+ @njit(cache=True)
160
+ def _lax_friedrichs_evolve(U0: NDArray[np.float64], dx: float, dt: float, gamma: float, steps: int) -> NDArray[np.float64]:
161
+ """Evolve the 1D Euler equations with the Lax-Friedrichs finite-volume scheme.
162
+
163
+ Parameters
164
+ ----------
165
+ U0 : ndarray of float, shape (nx, 3)
166
+ Initial conservative state at each cell.
167
+ dx : float
168
+ Cell width.
169
+ dt : float
170
+ Time step (assumed to already satisfy the CFL condition).
171
+ gamma : float
172
+ Ratio of specific heats.
173
+ steps : int
174
+ Number of time steps to advance.
175
+
176
+ Returns
177
+ -------
178
+ ndarray of float, shape (nx, 3)
179
+ Conservative state after `steps` steps. The two boundary cells are
180
+ held fixed at their initial (far-field) values, approximating an
181
+ open/transmissive boundary over the short times these simulations run.
182
+ """
183
+ nx = U0.shape[0]
184
+ U = U0.copy()
185
+ lam = dt / dx
186
+ for _ in range(steps):
187
+ U_new = U.copy()
188
+ for i in range(1, nx - 1):
189
+ fL = _euler_flux(U[i - 1], gamma)
190
+ fR = _euler_flux(U[i + 1], gamma)
191
+ U_new[i] = 0.5 * (U[i - 1] + U[i + 1]) - 0.5 * lam * (fR - fL)
192
+ U = U_new
193
+ return U
194
+
195
+
196
+ def sod_shock_tube(nx: int = 400, x0: float = 0.5, t_final: float = 0.2, gamma: float = 1.4, cfl: float = 0.5) -> dict[str, NDArray[np.float64]]:
197
+ """Solve the classic Sod shock-tube problem with a Lax-Friedrichs finite-volume scheme.
198
+
199
+ The Sod (1978) problem is the standard test Riemann problem for a
200
+ compressible-flow solver: a diaphragm at `x0` initially separates high
201
+ pressure/density gas (left, at rest) from low pressure/density gas
202
+ (right, at rest); removing the diaphragm at :math:`t=0` produces, for
203
+ :math:`t>0`, exactly three simple waves fanning out from `x0` -- a
204
+ left-running rarefaction (expansion) fan, a right-running contact
205
+ discontinuity (a density jump with continuous pressure and velocity),
206
+ and a right-running shock satisfying
207
+ :func:`rankine_hugoniot_jump_conditions` -- against which any
208
+ finite-volume scheme's numerical diffusion and shock-capturing behavior
209
+ can be judged. The domain is ``[0, 1]``; the classic Sod initial
210
+ condition is ``(rho, u, p) = (1, 0, 1)`` for ``x < x0`` and
211
+ ``(0.125, 0, 0.1)`` for ``x >= x0``.
212
+
213
+ Parameters
214
+ ----------
215
+ nx : int, default 400
216
+ Number of finite-volume cells.
217
+ x0 : float, default 0.5
218
+ Initial diaphragm location.
219
+ t_final : float, default 0.2
220
+ Time to integrate to.
221
+ gamma : float, default 1.4
222
+ Ratio of specific heats.
223
+ cfl : float, default 0.5
224
+ Courant number; the time step is chosen adaptively each macro-chunk
225
+ of the integration as ``cfl * dx / max(|u| + c)``, and must satisfy
226
+ ``0 < cfl <= 1`` for the explicit scheme to be stable.
227
+
228
+ Returns
229
+ -------
230
+ dict
231
+ ``{"x": ..., "rho": ..., "u": ..., "p": ...}`` at `t_final`, each an
232
+ array of length `nx`.
233
+
234
+ Raises
235
+ ------
236
+ InvalidParameterError
237
+ If `nx` is smaller than 4, `t_final` is not positive, or `cfl` is
238
+ not in ``(0, 1]``.
239
+
240
+ Examples
241
+ --------
242
+ >>> result = sod_shock_tube(nx=200, t_final=0.15)
243
+ >>> result["rho"].shape
244
+ (200,)
245
+ >>> bool(result["rho"][0] > result["rho"][-1] > 0) # left state denser than right
246
+ True
247
+ """
248
+ if nx < 4:
249
+ raise InvalidParameterError(f"nx must be at least 4, got {nx}")
250
+ if t_final <= 0:
251
+ raise InvalidParameterError(f"t_final must be positive, got {t_final}")
252
+ if not 0.0 < cfl <= 1.0:
253
+ raise InvalidParameterError(f"cfl must be in (0, 1], got {cfl}")
254
+
255
+ dx = 1.0 / nx
256
+ x = (np.arange(nx) + 0.5) * dx
257
+ rho = np.where(x < x0, 1.0, 0.125)
258
+ u = np.zeros(nx)
259
+ p = np.where(x < x0, 1.0, 0.1)
260
+
261
+ U = np.empty((nx, 3))
262
+ U[:, 0] = rho
263
+ U[:, 1] = rho * u
264
+ U[:, 2] = p / (gamma - 1.0) + 0.5 * rho * u**2
265
+
266
+ t = 0.0
267
+ while t < t_final:
268
+ rho_now = U[:, 0]
269
+ u_now = U[:, 1] / rho_now
270
+ p_now = (gamma - 1.0) * (U[:, 2] - 0.5 * rho_now * u_now**2)
271
+ c_now = np.sqrt(gamma * p_now / rho_now)
272
+ dt = cfl * dx / np.max(np.abs(u_now) + c_now)
273
+ dt = min(dt, t_final - t)
274
+ chunk_steps = 1
275
+ U = _lax_friedrichs_evolve(U, dx, dt, gamma, chunk_steps)
276
+ t += dt
277
+
278
+ rho = U[:, 0]
279
+ u = U[:, 1] / rho
280
+ p = (gamma - 1.0) * (U[:, 2] - 0.5 * rho * u**2)
281
+ return {"x": x, "rho": rho, "u": u, "p": p}
@@ -0,0 +1,343 @@
1
+ """Shear and buoyancy-driven instabilities: Kelvin-Helmholtz and Rayleigh-Taylor.
2
+
3
+ Both instabilities turn a smooth base state (a shear layer, a stratified
4
+ interface) into vigorous mixing given only an infinitesimal perturbation --
5
+ the base state is *linearly unstable*, not just occasionally disturbed into
6
+ instability. This module pairs each initial condition with the linear
7
+ growth-rate law that predicts how fast its seeding ripple should grow, and
8
+ with a full nonlinear simulation (built on
9
+ :mod:`physicskit.fluids.systems.navier_stokes`) that grows it well past the
10
+ point where the linear theory stops applying, into the rolled-up vortices
11
+ (Kelvin-Helmholtz) or mushroom-shaped plumes (Rayleigh-Taylor) these
12
+ instabilities are named for.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import numpy as np
18
+ from numpy.typing import NDArray
19
+
20
+ from physicskit.fluids.core.timestepping import integrate_boussinesq, integrate_vorticity_streamfunction
21
+ from physicskit.fluids.exceptions import InvalidParameterError
22
+
23
+ __all__ = [
24
+ "kelvin_helmholtz_ic",
25
+ "kelvin_helmholtz_growth_rate",
26
+ "simulate_kelvin_helmholtz",
27
+ "rayleigh_taylor_ic",
28
+ "rayleigh_taylor_growth_rate",
29
+ "simulate_rayleigh_taylor",
30
+ ]
31
+
32
+
33
+ def kelvin_helmholtz_ic(n: int, length: float, shear_width: float = 0.1, perturbation_amplitude: float = 0.05) -> NDArray[np.float64]:
34
+ """Vorticity initial condition for the Kelvin-Helmholtz shear-layer instability.
35
+
36
+ A thin vortex sheet at mid-domain, with a small sinusoidal ripple that
37
+ seeds the instability: under
38
+ :class:`physicskit.fluids.systems.navier_stokes.NavierStokes2D`, the
39
+ ripple's amplitude grows -- initially at the rate predicted by
40
+ :func:`kelvin_helmholtz_growth_rate` -- as the shear layer rolls up into
41
+ a row of discrete "cat's eye" vortices.
42
+
43
+ Parameters
44
+ ----------
45
+ n : int
46
+ Number of grid points along each axis.
47
+ length : float
48
+ Physical domain size.
49
+ shear_width : float, default 0.1
50
+ Thickness of the vortex sheet.
51
+ perturbation_amplitude : float, default 0.05
52
+ Amplitude of the seeding sinusoidal ripple.
53
+
54
+ Returns
55
+ -------
56
+ ndarray of float, shape (n, n)
57
+ Vorticity field.
58
+
59
+ Examples
60
+ --------
61
+ >>> omega0 = kelvin_helmholtz_ic(64, 2 * 3.141592653589793)
62
+ >>> omega0.shape
63
+ (64, 64)
64
+ >>> bool(omega0.max() > 0)
65
+ True
66
+ """
67
+ x = np.linspace(0, length, n, endpoint=False)
68
+ X, Y = np.meshgrid(x, x, indexing="ij")
69
+ y0 = length / 2
70
+ base = (1.0 / shear_width) / np.cosh((Y - y0) / shear_width) ** 2
71
+ ripple = perturbation_amplitude * np.sin(2 * np.pi * X / length) * np.exp(-(((Y - y0) / (length / 4)) ** 2))
72
+ return base + ripple
73
+
74
+
75
+ def kelvin_helmholtz_growth_rate(k: float, delta_u: float) -> float:
76
+ """Inviscid linear growth rate of a Kelvin-Helmholtz-unstable vortex sheet.
77
+
78
+ For a vortex sheet (an infinitesimally thin shear layer with a velocity
79
+ jump `delta_u` across it), linear stability analysis of the Euler
80
+ equations gives a perturbation of wavenumber `k` growing as
81
+ :math:`e^{\\sigma t}` with
82
+
83
+ .. math::
84
+
85
+ \\sigma(k) = \\frac{k\\,\\Delta u}{2},
86
+
87
+ unstable at *every* wavenumber with no threshold velocity -- unlike,
88
+ say, the Rayleigh-Taylor instability below, which is stabilized at short
89
+ wavelength by surface tension or diffusion. A shear layer of finite
90
+ thickness `shear_width` (as built by :func:`kelvin_helmholtz_ic`)
91
+ instead has a fastest-growing wavelength comparable to the layer
92
+ thickness, with growth cut off entirely for :math:`k\\,(\\text{shear
93
+ width})` large (Michalke, 1964); this vortex-sheet limit is the
94
+ thin-layer, small-``k`` approximation to that fuller theory, and is what
95
+ the shear-layer roll-up in :func:`kelvin_helmholtz_ic` grows at
96
+ initially, before finite-thickness and nonlinear effects take over.
97
+
98
+ Parameters
99
+ ----------
100
+ k : float
101
+ Perturbation wavenumber (angular, :math:`2\\pi/\\lambda`).
102
+ delta_u : float
103
+ Velocity jump across the shear layer.
104
+
105
+ Returns
106
+ -------
107
+ float
108
+ Growth rate :math:`\\sigma`, in units of inverse time.
109
+
110
+ Examples
111
+ --------
112
+ >>> round(kelvin_helmholtz_growth_rate(k=1.0, delta_u=2.0), 6)
113
+ 1.0
114
+ """
115
+ return 0.5 * k * delta_u
116
+
117
+
118
+ def simulate_kelvin_helmholtz(omega0: NDArray[np.float64], nu: float, dt: float, steps: int, length: float) -> dict[str, NDArray[np.float64]]:
119
+ """Time-step the 2D incompressible Kelvin-Helmholtz shear layer with RK4.
120
+
121
+ A thin wrapper around
122
+ :func:`physicskit.fluids.core.timestepping.integrate_vorticity_streamfunction`
123
+ -- the same pseudo-spectral vorticity-streamfunction Navier-Stokes
124
+ engine :func:`simulate_rayleigh_taylor` builds its Boussinesq extension
125
+ on -- evolving :func:`kelvin_helmholtz_ic` through the roll-up of its
126
+ seeded ripple into the characteristic "cat's-eye" vortex row, well past
127
+ the point :func:`kelvin_helmholtz_growth_rate`'s linear-theory
128
+ exponential growth stops applying.
129
+
130
+ Parameters
131
+ ----------
132
+ omega0 : ndarray of float, shape (n, n)
133
+ Initial vorticity field, as from :func:`kelvin_helmholtz_ic`.
134
+ nu : float
135
+ Kinematic viscosity; must be positive.
136
+ dt : float
137
+ Time step.
138
+ steps : int
139
+ Number of RK4 steps to advance.
140
+ length : float
141
+ Physical domain size.
142
+
143
+ Returns
144
+ -------
145
+ dict
146
+ ``{"omega": ..., "psi": ..., "u": ..., "v": ...}`` at the final time.
147
+
148
+ Raises
149
+ ------
150
+ InvalidParameterError
151
+ If `nu` is not positive.
152
+
153
+ See Also
154
+ --------
155
+ kelvin_helmholtz_ic : Builds the initial condition consumed here.
156
+ kelvin_helmholtz_growth_rate : The linear-theory prediction this simulation exceeds nonlinearly.
157
+ simulate_rayleigh_taylor : The companion buoyancy-driven instability, built on the same engine.
158
+
159
+ Examples
160
+ --------
161
+ >>> import numpy as np
162
+ >>> n, length = 96, 2 * np.pi
163
+ >>> omega0 = kelvin_helmholtz_ic(n, length, shear_width=0.1, perturbation_amplitude=0.05)
164
+ >>> result = simulate_kelvin_helmholtz(omega0, nu=0.001, dt=0.0025, steps=50, length=length)
165
+ >>> result["omega"].shape
166
+ (96, 96)
167
+ """
168
+ if nu <= 0:
169
+ raise InvalidParameterError(f"nu (kinematic viscosity) must be positive, got {nu}")
170
+ return integrate_vorticity_streamfunction(omega0, nu, dt, steps, length)
171
+
172
+
173
+ def rayleigh_taylor_ic(
174
+ n: int, length: float, atwood_number: float, perturbation_amplitude: float = 0.02, interface_position: float | None = None
175
+ ) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
176
+ """Vorticity and buoyancy initial condition for the Rayleigh-Taylor instability.
177
+
178
+ A denser fluid layer sits atop a lighter one (heavy-on-top is exactly
179
+ the unstable arrangement), separated by an interface at mid-domain with
180
+ a small sinusoidal ripple, evolved through
181
+ :func:`simulate_rayleigh_taylor`. Vorticity starts at exactly zero: in
182
+ the Boussinesq system, all vorticity generation comes from the
183
+ baroclinic torque acting on the rippled density interface once gravity
184
+ is switched on (see :func:`physicskit.fluids.core.timestepping.buoyant_vorticity_rhs`).
185
+
186
+ Parameters
187
+ ----------
188
+ n : int
189
+ Number of grid points along each axis.
190
+ length : float
191
+ Physical domain size.
192
+ atwood_number : float
193
+ The Atwood number :math:`A=(\\rho_{heavy}-\\rho_{light})/(\\rho_{heavy}+\\rho_{light})`,
194
+ :math:`0 < A < 1`, setting the buoyancy jump across the interface.
195
+ perturbation_amplitude : float, default 0.02
196
+ Amplitude of the seeding sinusoidal ripple on the interface.
197
+ interface_position : float, optional
198
+ Vertical position of the unperturbed interface; defaults to
199
+ ``length / 2``.
200
+
201
+ Returns
202
+ -------
203
+ omega0 : ndarray of float, shape (n, n)
204
+ Initial vorticity field (identically zero).
205
+ buoyancy0 : ndarray of float, shape (n, n)
206
+ Initial buoyancy field: :math:`+A` below the interface (light fluid),
207
+ :math:`-A` above it (heavy fluid), smoothed over one grid cell so the
208
+ spectral derivatives it feeds resolve without ringing.
209
+
210
+ Raises
211
+ ------
212
+ InvalidParameterError
213
+ If `atwood_number` is not in ``(0, 1)``.
214
+
215
+ Examples
216
+ --------
217
+ >>> omega0, buoyancy0 = rayleigh_taylor_ic(64, 2 * 3.141592653589793, atwood_number=0.3)
218
+ >>> bool(np.all(omega0 == 0.0))
219
+ True
220
+ >>> bool(buoyancy0.max() > 0 > buoyancy0.min())
221
+ True
222
+ """
223
+ if not 0.0 < atwood_number < 1.0:
224
+ raise InvalidParameterError(f"atwood_number must be in (0, 1), got {atwood_number}")
225
+ x = np.linspace(0, length, n, endpoint=False)
226
+ X, Y = np.meshgrid(x, x, indexing="ij")
227
+ y0 = length / 2 if interface_position is None else interface_position
228
+ smoothing = length / n
229
+ ripple = perturbation_amplitude * np.cos(2 * np.pi * X / length)
230
+ buoyancy0 = -atwood_number * np.tanh((Y - y0 - ripple) / smoothing)
231
+ omega0 = np.zeros_like(X)
232
+ return omega0, buoyancy0
233
+
234
+
235
+ def rayleigh_taylor_growth_rate(k: float, atwood_number: float, g: float = 1.0) -> float:
236
+ """Linear growth rate of the Rayleigh-Taylor instability.
237
+
238
+ For two inviscid, semi-infinite fluid layers (heavy density
239
+ :math:`\\rho_2` on top of light density :math:`\\rho_1`, gravity `g`
240
+ pointing from the heavy into the light layer), a perturbation of
241
+ wavenumber `k` on the interface grows as :math:`e^{\\sigma t}` with
242
+
243
+ .. math::
244
+
245
+ \\sigma(k) = \\sqrt{A\\,g\\,k}, \\qquad
246
+ A = \\frac{\\rho_2-\\rho_1}{\\rho_2+\\rho_1}
247
+
248
+ (Rayleigh, 1883; Taylor, 1950), unstable at every wavenumber in the
249
+ idealized inviscid limit -- real fluids are cut off at short wavelength
250
+ by viscosity and surface tension, which :func:`simulate_rayleigh_taylor`
251
+ supplies dynamically via its buoyancy diffusivity.
252
+
253
+ Parameters
254
+ ----------
255
+ k : float
256
+ Perturbation wavenumber (angular, :math:`2\\pi/\\lambda`).
257
+ atwood_number : float
258
+ The Atwood number :math:`A`, :math:`0 < A < 1`.
259
+ g : float, default 1.0
260
+ Gravitational acceleration, pointing from the heavy layer into the light one.
261
+
262
+ Returns
263
+ -------
264
+ float
265
+ Growth rate :math:`\\sigma`, in units of inverse time.
266
+
267
+ Raises
268
+ ------
269
+ InvalidParameterError
270
+ If `atwood_number` is not in ``(0, 1)`` or `k` is negative.
271
+
272
+ Examples
273
+ --------
274
+ >>> round(rayleigh_taylor_growth_rate(k=2.0, atwood_number=0.5, g=1.0), 6)
275
+ 1.0
276
+ """
277
+ if not 0.0 < atwood_number < 1.0:
278
+ raise InvalidParameterError(f"atwood_number must be in (0, 1), got {atwood_number}")
279
+ if k < 0:
280
+ raise InvalidParameterError(f"k must be non-negative, got {k}")
281
+ return float(np.sqrt(atwood_number * g * k))
282
+
283
+
284
+ def simulate_rayleigh_taylor(
285
+ omega0: NDArray[np.float64],
286
+ buoyancy0: NDArray[np.float64],
287
+ nu: float,
288
+ kappa: float,
289
+ g: float,
290
+ dt: float,
291
+ steps: int,
292
+ length: float,
293
+ ) -> dict[str, NDArray[np.float64]]:
294
+ """Time-step the Boussinesq Rayleigh-Taylor system with RK4.
295
+
296
+ Evolves the coupled vorticity-buoyancy equations of
297
+ :func:`physicskit.fluids.core.timestepping.buoyant_vorticity_rhs`
298
+ (scaled by the gravitational acceleration `g`) starting from
299
+ :func:`rayleigh_taylor_ic`, through the roll-up of the rippled interface
300
+ into the characteristic Rayleigh-Taylor mushroom plumes.
301
+
302
+ Parameters
303
+ ----------
304
+ omega0 : ndarray of float, shape (n, n)
305
+ Initial vorticity field, as from :func:`rayleigh_taylor_ic`.
306
+ buoyancy0 : ndarray of float, shape (n, n)
307
+ Initial buoyancy field, as from :func:`rayleigh_taylor_ic`.
308
+ nu : float
309
+ Kinematic viscosity; must be positive.
310
+ kappa : float
311
+ Buoyancy (density) diffusivity; must be positive.
312
+ g : float
313
+ Gravitational acceleration; must be positive.
314
+ dt : float
315
+ Time step.
316
+ steps : int
317
+ Number of RK4 steps to advance.
318
+ length : float
319
+ Physical domain size.
320
+
321
+ Returns
322
+ -------
323
+ dict
324
+ ``{"omega": ..., "buoyancy": ..., "psi": ..., "u": ..., "v": ...}`` at
325
+ the final time.
326
+
327
+ Raises
328
+ ------
329
+ InvalidParameterError
330
+ If `nu`, `kappa`, or `g` is not positive.
331
+
332
+ See Also
333
+ --------
334
+ rayleigh_taylor_ic : Builds the initial condition consumed here.
335
+ rayleigh_taylor_growth_rate : The linear-theory prediction this simulation exceeds nonlinearly.
336
+ """
337
+ if nu <= 0:
338
+ raise InvalidParameterError(f"nu (kinematic viscosity) must be positive, got {nu}")
339
+ if kappa <= 0:
340
+ raise InvalidParameterError(f"kappa (buoyancy diffusivity) must be positive, got {kappa}")
341
+ if g <= 0:
342
+ raise InvalidParameterError(f"g (gravitational acceleration) must be positive, got {g}")
343
+ return integrate_boussinesq(omega0, buoyancy0, nu, kappa, g, dt, steps, length)