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,221 @@
1
+ """Incompressible 2D Navier-Stokes via the vorticity-streamfunction formulation.
2
+
3
+ Solves :math:`\\partial_t \\omega + (\\mathbf{u}\\cdot\\nabla)\\omega = \\nu \\nabla^2 \\omega`
4
+ on a doubly periodic domain, with the incompressible velocity field
5
+ recovered from the streamfunction :math:`\\psi` (:math:`\\nabla^2\\psi=-\\omega`,
6
+ :math:`\\mathbf{u}=(\\partial_y\\psi,\\,-\\partial_x\\psi)`) via an exact spectral
7
+ Poisson solve at every step -- the standard pseudo-spectral vorticity
8
+ formulation used throughout this package for 2D turbulence and shear-flow
9
+ instabilities. Working in vorticity rather than velocity-pressure form
10
+ eliminates the pressure entirely (it never appears once the curl of the
11
+ momentum equation is taken) at the cost of being specific to two dimensions,
12
+ where vorticity is a scalar.
13
+
14
+ This module is the general-purpose incompressible-flow workhorse of
15
+ :mod:`physicskit.fluids`: :mod:`physicskit.fluids.systems.instabilities` grows
16
+ its Kelvin-Helmholtz and Rayleigh-Taylor initial conditions with it (or with
17
+ its Boussinesq extension), and :mod:`physicskit.fluids.utils.spectral_analysis`
18
+ measures the turbulent energy spectrum of the velocity fields it produces.
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import numpy as np
24
+ from numpy.typing import NDArray
25
+
26
+ from physicskit.fluids.core.grid import poisson_solve_streamfunction, spectral_grid, velocity_from_streamfunction
27
+ from physicskit.fluids.core.timestepping import integrate_vorticity_streamfunction, vorticity_rhs
28
+ from physicskit.fluids.exceptions import InvalidParameterError
29
+
30
+ __all__ = ["NavierStokes2D", "simulate_vorticity_streamfunction"]
31
+
32
+
33
+ class NavierStokes2D:
34
+ """A doubly periodic 2D incompressible Navier-Stokes solver, in vorticity-streamfunction form.
35
+
36
+ Builds the pseudo-spectral grid once at construction and exposes
37
+ :meth:`rhs`, :meth:`step`, and :meth:`simulate` against it, so that
38
+ repeated calls (e.g. one per animation frame) do not re-derive the
39
+ wavenumber grid on every call the way the module-level
40
+ :func:`simulate_vorticity_streamfunction` convenience function must.
41
+
42
+ Parameters
43
+ ----------
44
+ n : int, default 64
45
+ Number of grid points along each axis.
46
+ length : float, default ``2*pi``
47
+ Physical domain size (the domain is ``[0, length) x [0, length)``).
48
+ nu : float, default 0.01
49
+ Kinematic viscosity. Must be positive; a truly inviscid flow is
50
+ ill-posed on this grid (nothing controls the cascade of enstrophy to
51
+ the grid scale), so ``nu=0`` is rejected.
52
+
53
+ Attributes
54
+ ----------
55
+ n : int
56
+ Grid resolution.
57
+ length : float
58
+ Domain size.
59
+ nu : float
60
+ Kinematic viscosity.
61
+ X, Y : ndarray of float, shape (n, n)
62
+ Real-space coordinate grids.
63
+ KX, KY, K2 : ndarray of float, shape (n, n)
64
+ Wavenumber grids.
65
+
66
+ Raises
67
+ ------
68
+ InvalidParameterError
69
+ If `nu` is not positive (see :func:`physicskit.fluids.core.grid.spectral_grid`
70
+ for the conditions on `n` and `length`).
71
+
72
+ Examples
73
+ --------
74
+ >>> import numpy as np
75
+ >>> solver = NavierStokes2D(n=48, length=2 * np.pi, nu=0.05)
76
+ >>> omega0 = np.sin(solver.X) * np.sin(solver.Y)
77
+ >>> result = solver.simulate(omega0, dt=0.01, steps=20)
78
+ >>> result["omega"].shape
79
+ (48, 48)
80
+ >>> bool(np.max(np.abs(result["omega"])) < np.max(np.abs(omega0)))
81
+ True
82
+ """
83
+
84
+ def __init__(self, n: int = 64, length: float = 2.0 * np.pi, nu: float = 0.01):
85
+ if nu <= 0:
86
+ raise InvalidParameterError(f"nu (kinematic viscosity) must be positive, got {nu}")
87
+ self.n = int(n)
88
+ self.length = float(length)
89
+ self.nu = float(nu)
90
+ self.X, self.Y, self.KX, self.KY, self.K2 = spectral_grid(self.n, self.length)
91
+
92
+ def rhs(self, omega: NDArray[np.float64]) -> NDArray[np.float64]:
93
+ """Evaluate the vorticity-transport right-hand side at `omega`.
94
+
95
+ Parameters
96
+ ----------
97
+ omega : ndarray of float, shape (n, n)
98
+ Vorticity field.
99
+
100
+ Returns
101
+ -------
102
+ ndarray of float, shape (n, n)
103
+ :math:`-(\\mathbf{u}\\cdot\\nabla)\\omega + \\nu\\nabla^2\\omega`.
104
+ """
105
+ return vorticity_rhs(omega, self.KX, self.KY, self.K2, self.nu)
106
+
107
+ def velocity(self, omega: NDArray[np.float64]) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
108
+ """Recover the velocity field associated with a vorticity field.
109
+
110
+ Parameters
111
+ ----------
112
+ omega : ndarray of float, shape (n, n)
113
+ Vorticity field.
114
+
115
+ Returns
116
+ -------
117
+ u, v : ndarray of float, shape (n, n)
118
+ Velocity components.
119
+ """
120
+ psi = poisson_solve_streamfunction(omega, self.K2)
121
+ return velocity_from_streamfunction(psi, self.KX, self.KY)
122
+
123
+ def step(self, omega: NDArray[np.float64], dt: float) -> NDArray[np.float64]:
124
+ """Advance the vorticity field by one RK4 step of size `dt`.
125
+
126
+ Parameters
127
+ ----------
128
+ omega : ndarray of float, shape (n, n)
129
+ Current vorticity field.
130
+ dt : float
131
+ Step size.
132
+
133
+ Returns
134
+ -------
135
+ ndarray of float, shape (n, n)
136
+ Vorticity field advanced by one step.
137
+ """
138
+ k1 = self.rhs(omega)
139
+ k2 = self.rhs(omega + dt / 2 * k1)
140
+ k3 = self.rhs(omega + dt / 2 * k2)
141
+ k4 = self.rhs(omega + dt * k3)
142
+ return omega + dt / 6 * (k1 + 2 * k2 + 2 * k3 + k4)
143
+
144
+ def simulate(self, omega0: NDArray[np.float64], dt: float, steps: int) -> dict[str, NDArray[np.float64]]:
145
+ """Time-step an initial vorticity field forward by ``steps`` RK4 steps.
146
+
147
+ Parameters
148
+ ----------
149
+ omega0 : ndarray of float, shape (n, n)
150
+ Initial vorticity field.
151
+ dt : float
152
+ Time step.
153
+ steps : int
154
+ Number of RK4 steps to advance.
155
+
156
+ Returns
157
+ -------
158
+ dict
159
+ ``{"omega": final vorticity, "psi": final streamfunction, "u": ..., "v": ...}``.
160
+ """
161
+ omega = np.array(omega0, dtype=float, copy=True)
162
+ for _ in range(steps):
163
+ omega = self.step(omega, dt)
164
+ psi = poisson_solve_streamfunction(omega, self.K2)
165
+ u, v = velocity_from_streamfunction(psi, self.KX, self.KY)
166
+ return {"omega": omega, "psi": psi, "u": u, "v": v}
167
+
168
+ def __repr__(self) -> str:
169
+ return f"NavierStokes2D(n={self.n}, length={self.length}, nu={self.nu})"
170
+
171
+
172
+ def simulate_vorticity_streamfunction(omega0: NDArray[np.float64], nu: float, dt: float, steps: int, length: float) -> dict[str, NDArray[np.float64]]:
173
+ """Time-step 2D incompressible vorticity-streamfunction Navier-Stokes with RK4.
174
+
175
+ A one-shot functional convenience wrapper around :class:`NavierStokes2D`,
176
+ for scripts that only need a single run rather than repeated stepping
177
+ against the same grid.
178
+
179
+ Parameters
180
+ ----------
181
+ omega0 : ndarray of float, shape (n, n)
182
+ Initial vorticity field on a doubly periodic ``[0, length)^2`` domain.
183
+ nu : float
184
+ Kinematic viscosity; must be positive.
185
+ dt : float
186
+ Time step.
187
+ steps : int
188
+ Number of RK4 steps to advance.
189
+ length : float
190
+ Physical domain size.
191
+
192
+ Returns
193
+ -------
194
+ dict
195
+ ``{"omega": final vorticity, "psi": final streamfunction, "u": ..., "v": ...}``.
196
+
197
+ Raises
198
+ ------
199
+ InvalidParameterError
200
+ If `nu` is not positive.
201
+
202
+ See Also
203
+ --------
204
+ NavierStokes2D : Reusable object form, for repeated stepping on one grid.
205
+ physicskit.fluids.systems.instabilities.kelvin_helmholtz_ic :
206
+ Shear-layer initial condition that grows via this solver.
207
+
208
+ Examples
209
+ --------
210
+ >>> import numpy as np
211
+ >>> n = 48
212
+ >>> from physicskit.fluids.core.grid import spectral_grid
213
+ >>> X, Y, KX, KY, K2 = spectral_grid(n, 2 * np.pi)
214
+ >>> omega0 = np.sin(X) * np.sin(Y)
215
+ >>> result = simulate_vorticity_streamfunction(omega0, nu=0.05, dt=0.01, steps=20, length=2 * np.pi)
216
+ >>> result["omega"].shape
217
+ (48, 48)
218
+ """
219
+ if nu <= 0:
220
+ raise InvalidParameterError(f"nu (kinematic viscosity) must be positive, got {nu}")
221
+ return integrate_vorticity_streamfunction(omega0, nu, dt, steps, length)
@@ -0,0 +1,440 @@
1
+ """Inviscid, irrotational 2D potential flow: superposition of elementary solutions.
2
+
3
+ Where the flow has no vorticity (:math:`\\nabla\\times\\mathbf{u}=0`) the
4
+ velocity is the gradient of a scalar velocity potential,
5
+ :math:`\\mathbf{u}=\\nabla\\phi`, and incompressibility
6
+ (:math:`\\nabla\\cdot\\mathbf{u}=0`) then makes :math:`\\phi` harmonic:
7
+ :math:`\\nabla^2\\phi=0`. Laplace's equation is linear, so any sum of
8
+ solutions is itself a solution -- the entire content of this module is that
9
+ observation, applied to four elementary flows (a uniform stream, a
10
+ source/sink, a doublet, and a point vortex) whose superpositions reproduce
11
+ the classic textbook flow fields, most famously flow past a circular
12
+ cylinder with lift.
13
+
14
+ Every element is expressed through its complex potential
15
+ :math:`W(z) = \\phi + i\\psi`, :math:`z = x + iy`, since for these
16
+ particular elementary flows the complex-analytic form is both the most
17
+ compact way to write the solution and, via the Cauchy-Riemann equations, an
18
+ automatic guarantee that :math:`\\phi` and its harmonic conjugate
19
+ (streamfunction) :math:`\\psi` are consistent: :math:`u - iv = dW/dz`.
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ import numpy as np
25
+ from numpy.typing import ArrayLike, NDArray
26
+
27
+ from physicskit.fluids.exceptions import InvalidParameterError
28
+
29
+ __all__ = [
30
+ "uniform_flow_potential",
31
+ "source_potential",
32
+ "doublet_potential",
33
+ "point_vortex_potential",
34
+ "PotentialFlow",
35
+ "flow_past_cylinder",
36
+ "pressure_coefficient",
37
+ "kutta_joukowski_lift",
38
+ ]
39
+
40
+
41
+ def _complex_z(X: ArrayLike, Y: ArrayLike) -> NDArray[np.complex128]:
42
+ return np.asarray(X, dtype=np.float64) + 1j * np.asarray(Y, dtype=np.float64)
43
+
44
+
45
+ def uniform_flow_potential(X: ArrayLike, Y: ArrayLike, U_inf: float, alpha: float = 0.0) -> NDArray[np.complex128]:
46
+ """Complex potential of a uniform stream.
47
+
48
+ Parameters
49
+ ----------
50
+ X, Y : array_like of float
51
+ Field points.
52
+ U_inf : float
53
+ Free-stream speed.
54
+ alpha : float, default 0.0
55
+ Angle of attack (radians), measured from the ``+x`` axis.
56
+
57
+ Returns
58
+ -------
59
+ ndarray of complex
60
+ Complex potential :math:`W(z) = U_\\infty e^{-i\\alpha} z`.
61
+ """
62
+ z = _complex_z(X, Y)
63
+ return U_inf * np.exp(-1j * alpha) * z
64
+
65
+
66
+ def source_potential(X: ArrayLike, Y: ArrayLike, strength: float, x0: float = 0.0, y0: float = 0.0) -> NDArray[np.complex128]:
67
+ """Complex potential of a point source (or, for negative `strength`, a sink).
68
+
69
+ Parameters
70
+ ----------
71
+ X, Y : array_like of float
72
+ Field points.
73
+ strength : float
74
+ Source strength (volume flow rate per unit depth); negative for a sink.
75
+ x0, y0 : float, default 0.0
76
+ Source location.
77
+
78
+ Returns
79
+ -------
80
+ ndarray of complex
81
+ Complex potential :math:`W(z) = \\frac{m}{2\\pi}\\ln(z - z_0)`.
82
+ """
83
+ z = _complex_z(X, Y) - (x0 + 1j * y0)
84
+ with np.errstate(divide="ignore"):
85
+ return (strength / (2.0 * np.pi)) * np.log(z)
86
+
87
+
88
+ def doublet_potential(X: ArrayLike, Y: ArrayLike, strength: float, x0: float = 0.0, y0: float = 0.0, alpha: float = 0.0) -> NDArray[np.complex128]:
89
+ """Complex potential of a doublet (a source-sink pair in the zero-separation limit).
90
+
91
+ Parameters
92
+ ----------
93
+ X, Y : array_like of float
94
+ Field points.
95
+ strength : float
96
+ Doublet strength :math:`\\kappa` (the limit of ``source_strength *
97
+ separation`` as the separation shrinks to zero).
98
+ x0, y0 : float, default 0.0
99
+ Doublet location.
100
+ alpha : float, default 0.0
101
+ Orientation angle (radians) from sink-to-source.
102
+
103
+ Returns
104
+ -------
105
+ ndarray of complex
106
+ Complex potential :math:`W(z) = \\frac{\\kappa e^{-i\\alpha}}{2\\pi (z-z_0)}`.
107
+ """
108
+ z = _complex_z(X, Y) - (x0 + 1j * y0)
109
+ with np.errstate(divide="ignore"):
110
+ return (strength * np.exp(-1j * alpha)) / (2.0 * np.pi * z)
111
+
112
+
113
+ def point_vortex_potential(X: ArrayLike, Y: ArrayLike, circulation: float, x0: float = 0.0, y0: float = 0.0) -> NDArray[np.complex128]:
114
+ """Complex potential of an isolated point vortex.
115
+
116
+ Parameters
117
+ ----------
118
+ X, Y : array_like of float
119
+ Field points.
120
+ circulation : float
121
+ Circulation :math:`\\Gamma` (positive is counterclockwise).
122
+ x0, y0 : float, default 0.0
123
+ Vortex location.
124
+
125
+ Returns
126
+ -------
127
+ ndarray of complex
128
+ Complex potential :math:`W(z) = \\frac{-i\\Gamma}{2\\pi}\\ln(z - z_0)`.
129
+ """
130
+ z = _complex_z(X, Y) - (x0 + 1j * y0)
131
+ with np.errstate(divide="ignore"):
132
+ return (-1j * circulation / (2.0 * np.pi)) * np.log(z)
133
+
134
+
135
+ class PotentialFlow:
136
+ """A superposition of elementary potential-flow solutions.
137
+
138
+ Because Laplace's equation is linear, the complex potential of any
139
+ combination of uniform flow, sources/sinks, doublets, and point vortices
140
+ is simply the sum of their individual complex potentials -- exactly what
141
+ this class accumulates. Call :meth:`add_source`, :meth:`add_doublet`, or
142
+ :meth:`add_vortex` to build up a flow on top of the base uniform stream
143
+ ``(U_inf, alpha)``, then
144
+ :meth:`velocity`, :meth:`streamfunction`, or
145
+ :meth:`pressure_coefficient` to evaluate it on a grid.
146
+
147
+ Parameters
148
+ ----------
149
+ U_inf : float, default 1.0
150
+ Free-stream speed used both as the base uniform flow and as the
151
+ reference speed for :meth:`pressure_coefficient`.
152
+ alpha : float, default 0.0
153
+ Free-stream angle of attack (radians).
154
+
155
+ Attributes
156
+ ----------
157
+ U_inf, alpha : float
158
+ As above.
159
+
160
+ Examples
161
+ --------
162
+ >>> flow = PotentialFlow(U_inf=1.0)
163
+ >>> flow.add_doublet(strength=2 * np.pi * 1.0**2) # uniform flow + doublet = cylinder
164
+ >>> X, Y = np.meshgrid(np.linspace(-3, 3, 5), np.linspace(-3, 3, 5))
165
+ >>> u, v = flow.velocity(X, Y)
166
+ >>> u.shape
167
+ (5, 5)
168
+ """
169
+
170
+ def __init__(self, U_inf: float = 1.0, alpha: float = 0.0):
171
+ self.U_inf = float(U_inf)
172
+ self.alpha = float(alpha)
173
+ self._elements: list = []
174
+
175
+ def add_source(self, strength: float, x0: float = 0.0, y0: float = 0.0) -> None:
176
+ """Add a point source (or sink, for negative `strength`).
177
+
178
+ Parameters
179
+ ----------
180
+ strength : float
181
+ Source strength; negative for a sink.
182
+ x0, y0 : float, default 0.0
183
+ Source location.
184
+ """
185
+ self._elements.append(("source", (strength, x0, y0)))
186
+
187
+ def add_doublet(self, strength: float, x0: float = 0.0, y0: float = 0.0, alpha: float = 0.0) -> None:
188
+ """Add a doublet.
189
+
190
+ Parameters
191
+ ----------
192
+ strength : float
193
+ Doublet strength.
194
+ x0, y0 : float, default 0.0
195
+ Doublet location.
196
+ alpha : float, default 0.0
197
+ Orientation angle (radians).
198
+ """
199
+ self._elements.append(("doublet", (strength, x0, y0, alpha)))
200
+
201
+ def add_vortex(self, circulation: float, x0: float = 0.0, y0: float = 0.0) -> None:
202
+ """Add a point vortex.
203
+
204
+ Parameters
205
+ ----------
206
+ circulation : float
207
+ Circulation :math:`\\Gamma`; positive is counterclockwise.
208
+ x0, y0 : float, default 0.0
209
+ Vortex location.
210
+ """
211
+ self._elements.append(("vortex", (circulation, x0, y0)))
212
+
213
+ def complex_potential(self, X: ArrayLike, Y: ArrayLike) -> NDArray[np.complex128]:
214
+ """Evaluate the total complex potential :math:`W(z)=\\phi+i\\psi`.
215
+
216
+ Parameters
217
+ ----------
218
+ X, Y : array_like of float
219
+ Field points.
220
+
221
+ Returns
222
+ -------
223
+ ndarray of complex
224
+ Complex potential at each point.
225
+ """
226
+ X = np.asarray(X, dtype=np.float64)
227
+ W = uniform_flow_potential(X, Y, self.U_inf, self.alpha)
228
+ for kind, args in self._elements:
229
+ if kind == "source":
230
+ W = W + source_potential(X, Y, *args)
231
+ elif kind == "doublet":
232
+ W = W + doublet_potential(X, Y, *args)
233
+ elif kind == "vortex":
234
+ W = W + point_vortex_potential(X, Y, *args)
235
+ return W
236
+
237
+ def streamfunction(self, X: ArrayLike, Y: ArrayLike) -> NDArray[np.float64]:
238
+ """Evaluate the streamfunction :math:`\\psi = \\mathrm{Im}(W)`.
239
+
240
+ Parameters
241
+ ----------
242
+ X, Y : array_like of float
243
+ Field points.
244
+
245
+ Returns
246
+ -------
247
+ ndarray of float
248
+ Streamfunction values.
249
+ """
250
+ return np.imag(self.complex_potential(X, Y))
251
+
252
+ def velocity(self, X: ArrayLike, Y: ArrayLike, delta: float = 1e-6) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
253
+ """Evaluate the velocity field :math:`u - iv = dW/dz`.
254
+
255
+ Differentiates the complex potential with a central difference in
256
+ `X` (equivalent, by the Cauchy-Riemann equations, to differentiating
257
+ in either direction), which sidesteps deriving and summing a
258
+ separate closed-form velocity expression for every element type.
259
+
260
+ Parameters
261
+ ----------
262
+ X, Y : array_like of float
263
+ Field points.
264
+ delta : float, default 1e-6
265
+ Finite-difference step used to differentiate the complex potential.
266
+
267
+ Returns
268
+ -------
269
+ u, v : ndarray of float
270
+ Velocity components.
271
+ """
272
+ X = np.asarray(X, dtype=np.float64)
273
+ dW = (self.complex_potential(X + delta, Y) - self.complex_potential(X - delta, Y)) / (2.0 * delta)
274
+ return np.real(dW), -np.imag(dW)
275
+
276
+ def pressure_coefficient(self, X: ArrayLike, Y: ArrayLike) -> NDArray[np.float64]:
277
+ """Evaluate the Bernoulli pressure coefficient :math:`C_p = 1 - (u^2+v^2)/U_\\infty^2`.
278
+
279
+ Parameters
280
+ ----------
281
+ X, Y : array_like of float
282
+ Field points.
283
+
284
+ Returns
285
+ -------
286
+ ndarray of float
287
+ Pressure coefficient at each point.
288
+ """
289
+ return pressure_coefficient(*self.velocity(X, Y), self.U_inf)
290
+
291
+
292
+ def pressure_coefficient(u: ArrayLike, v: ArrayLike, U_inf: float) -> NDArray[np.float64]:
293
+ """Bernoulli's pressure coefficient for steady, incompressible potential flow.
294
+
295
+ Along a streamline of a steady, incompressible, inviscid flow, Bernoulli's
296
+ equation :math:`p + \\tfrac{1}{2}\\rho|\\mathbf{u}|^2 = \\mathrm{const}`
297
+ gives the dimensionless pressure coefficient
298
+
299
+ .. math::
300
+
301
+ C_p = \\frac{p - p_\\infty}{\\tfrac{1}{2}\\rho U_\\infty^2}
302
+ = 1 - \\frac{u^2+v^2}{U_\\infty^2}.
303
+
304
+ Parameters
305
+ ----------
306
+ u, v : array_like of float
307
+ Velocity components.
308
+ U_inf : float
309
+ Free-stream speed.
310
+
311
+ Returns
312
+ -------
313
+ ndarray of float
314
+ Pressure coefficient.
315
+
316
+ Raises
317
+ ------
318
+ InvalidParameterError
319
+ If `U_inf` is not positive.
320
+
321
+ Examples
322
+ --------
323
+ >>> import numpy as np
324
+ >>> bool(np.isclose(pressure_coefficient(u=1.0, v=0.0, U_inf=1.0), 0.0))
325
+ True
326
+ """
327
+ if U_inf <= 0:
328
+ raise InvalidParameterError(f"U_inf must be positive, got {U_inf}")
329
+ u = np.asarray(u, dtype=np.float64)
330
+ v = np.asarray(v, dtype=np.float64)
331
+ return 1.0 - (u**2 + v**2) / U_inf**2
332
+
333
+
334
+ def flow_past_cylinder(U_inf: float, radius: float, circulation: float = 0.0) -> PotentialFlow:
335
+ """Build the classic potential flow past a circular cylinder, with optional lift.
336
+
337
+ Superposing a uniform stream with a doublet of strength
338
+ :math:`\\kappa = 2\\pi U_\\infty R^2` places a circular streamline of
339
+ radius `radius` exactly at the origin -- since :math:`\\psi=0` on that
340
+ circle can be shown to be exactly the streamfunction of this
341
+ combination, the circle itself can be interpreted as a solid boundary
342
+ (potential flow does not enforce a no-slip condition, only no
343
+ penetration). Adding a point vortex at the same location keeps the
344
+ circular streamline intact (a vortex centered on the circle induces
345
+ purely tangential velocity on it) while breaking front-back symmetry,
346
+ which is exactly what a real cylinder's boundary layer does when it
347
+ separates asymmetrically -- e.g. from a spinning cylinder (the Magnus
348
+ effect).
349
+
350
+ Parameters
351
+ ----------
352
+ U_inf : float
353
+ Free-stream speed.
354
+ radius : float
355
+ Cylinder radius.
356
+ circulation : float, default 0.0
357
+ Circulation :math:`\\Gamma` added at the cylinder center; `0.0` gives
358
+ the symmetric, lift-free flow.
359
+
360
+ Returns
361
+ -------
362
+ PotentialFlow
363
+ The assembled flow: uniform stream + doublet (+ vortex if `circulation`
364
+ is nonzero).
365
+
366
+ Raises
367
+ ------
368
+ InvalidParameterError
369
+ If `U_inf` or `radius` is not positive.
370
+
371
+ See Also
372
+ --------
373
+ kutta_joukowski_lift : The lift force generated by `circulation`.
374
+
375
+ Examples
376
+ --------
377
+ >>> flow = flow_past_cylinder(U_inf=1.0, radius=1.0, circulation=4 * np.pi)
378
+ >>> theta = np.linspace(0, 2 * np.pi, 9)
379
+ >>> X, Y = 1.0 * np.cos(theta), 1.0 * np.sin(theta)
380
+ >>> psi = flow.streamfunction(X, Y)
381
+ >>> bool(np.max(np.abs(psi - psi[0])) < 1e-8) # the cylinder surface is one streamline
382
+ True
383
+ """
384
+ if U_inf <= 0:
385
+ raise InvalidParameterError(f"U_inf must be positive, got {U_inf}")
386
+ if radius <= 0:
387
+ raise InvalidParameterError(f"radius must be positive, got {radius}")
388
+ flow = PotentialFlow(U_inf=U_inf)
389
+ flow.add_doublet(strength=2.0 * np.pi * U_inf * radius**2)
390
+ if circulation != 0.0:
391
+ flow.add_vortex(circulation=circulation)
392
+ return flow
393
+
394
+
395
+ def kutta_joukowski_lift(rho: float, U_inf: float, circulation: float) -> float:
396
+ """The Kutta-Joukowski lift theorem: lift per unit span from bound circulation.
397
+
398
+ .. math::
399
+
400
+ L' = \\rho\\,U_\\infty\\,\\Gamma
401
+
402
+ Any 2D body generating a net circulation :math:`\\Gamma` around itself in
403
+ a stream of speed :math:`U_\\infty` experiences a lift force per unit
404
+ span of exactly this magnitude, directed perpendicular to the free
405
+ stream -- true regardless of the body's shape, a remarkable consequence
406
+ of potential theory (Kutta 1902, Zhukovsky 1906) that underlies all of
407
+ classical airfoil theory. Real airfoils select `circulation` via the
408
+ Kutta condition (smooth flow off a sharp trailing edge); the cylinder
409
+ with an added point vortex built by :func:`flow_past_cylinder` sets it
410
+ directly.
411
+
412
+ Parameters
413
+ ----------
414
+ rho : float
415
+ Fluid density.
416
+ U_inf : float
417
+ Free-stream speed.
418
+ circulation : float
419
+ Circulation :math:`\\Gamma` (positive counterclockwise) bound to the body.
420
+
421
+ Returns
422
+ -------
423
+ float
424
+ Lift force per unit span, :math:`L'`.
425
+
426
+ Raises
427
+ ------
428
+ InvalidParameterError
429
+ If `rho` or `U_inf` is not positive.
430
+
431
+ Examples
432
+ --------
433
+ >>> round(kutta_joukowski_lift(rho=1.2, U_inf=10.0, circulation=5.0), 1)
434
+ 60.0
435
+ """
436
+ if rho <= 0:
437
+ raise InvalidParameterError(f"rho must be positive, got {rho}")
438
+ if U_inf <= 0:
439
+ raise InvalidParameterError(f"U_inf must be positive, got {U_inf}")
440
+ return rho * U_inf * circulation