pianistpy 0.9.0__tar.gz

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 (178) hide show
  1. pianistpy-0.9.0/AUTHORS.md +7 -0
  2. pianistpy-0.9.0/LICENSE +29 -0
  3. pianistpy-0.9.0/PKG-INFO +224 -0
  4. pianistpy-0.9.0/README.md +169 -0
  5. pianistpy-0.9.0/pianistpy/__init__.py +77 -0
  6. pianistpy-0.9.0/pianistpy/_version.py +5 -0
  7. pianistpy-0.9.0/pianistpy/anisotropy/__init__.py +5 -0
  8. pianistpy-0.9.0/pianistpy/anisotropy/core.py +178 -0
  9. pianistpy-0.9.0/pianistpy/anisotropy/tests/__init__.py +0 -0
  10. pianistpy-0.9.0/pianistpy/anisotropy/tests/test_core.py +32 -0
  11. pianistpy-0.9.0/pianistpy/anisotropy/tests/test_factor.py +61 -0
  12. pianistpy-0.9.0/pianistpy/df/__init__.py +15 -0
  13. pianistpy-0.9.0/pianistpy/df/analytic.py +257 -0
  14. pianistpy-0.9.0/pianistpy/df/assembly.py +1767 -0
  15. pianistpy-0.9.0/pianistpy/df/asymptotics.py +293 -0
  16. pianistpy-0.9.0/pianistpy/df/center.py +498 -0
  17. pianistpy-0.9.0/pianistpy/df/central.py +366 -0
  18. pianistpy-0.9.0/pianistpy/df/checks.py +548 -0
  19. pianistpy-0.9.0/pianistpy/df/core.py +265 -0
  20. pianistpy-0.9.0/pianistpy/df/derivatives.py +444 -0
  21. pianistpy-0.9.0/pianistpy/df/edge.py +171 -0
  22. pianistpy-0.9.0/pianistpy/df/edge_law.py +299 -0
  23. pianistpy-0.9.0/pianistpy/df/fill.py +772 -0
  24. pianistpy-0.9.0/pianistpy/df/form.py +199 -0
  25. pianistpy-0.9.0/pianistpy/df/gates.py +150 -0
  26. pianistpy-0.9.0/pianistpy/df/grid.py +341 -0
  27. pianistpy-0.9.0/pianistpy/df/inputs.py +229 -0
  28. pianistpy-0.9.0/pianistpy/df/inversion.py +220 -0
  29. pianistpy-0.9.0/pianistpy/df/invert.py +466 -0
  30. pianistpy-0.9.0/pianistpy/df/lookup.py +578 -0
  31. pianistpy-0.9.0/pianistpy/df/pair.py +71 -0
  32. pianistpy-0.9.0/pianistpy/df/poisson.py +777 -0
  33. pianistpy-0.9.0/pianistpy/df/profile_checks.py +339 -0
  34. pianistpy-0.9.0/pianistpy/df/report.py +281 -0
  35. pianistpy-0.9.0/pianistpy/df/results.py +658 -0
  36. pianistpy-0.9.0/pianistpy/df/rows.py +1015 -0
  37. pianistpy-0.9.0/pianistpy/df/stages.py +698 -0
  38. pianistpy-0.9.0/pianistpy/df/tabulated.py +1282 -0
  39. pianistpy-0.9.0/pianistpy/df/target.py +414 -0
  40. pianistpy-0.9.0/pianistpy/df/tests/__init__.py +0 -0
  41. pianistpy-0.9.0/pianistpy/df/tests/test_analytic.py +125 -0
  42. pianistpy-0.9.0/pianistpy/df/tests/test_central.py +15 -0
  43. pianistpy-0.9.0/pianistpy/df/tests/test_checks.py +58 -0
  44. pianistpy-0.9.0/pianistpy/df/tests/test_core.py +104 -0
  45. pianistpy-0.9.0/pianistpy/df/tests/test_derivatives.py +66 -0
  46. pianistpy-0.9.0/pianistpy/df/tests/test_edge.py +91 -0
  47. pianistpy-0.9.0/pianistpy/df/tests/test_edge_law.py +176 -0
  48. pianistpy-0.9.0/pianistpy/df/tests/test_edge_row.py +55 -0
  49. pianistpy-0.9.0/pianistpy/df/tests/test_form.py +39 -0
  50. pianistpy-0.9.0/pianistpy/df/tests/test_gates.py +53 -0
  51. pianistpy-0.9.0/pianistpy/df/tests/test_gradients.py +73 -0
  52. pianistpy-0.9.0/pianistpy/df/tests/test_grid.py +17 -0
  53. pianistpy-0.9.0/pianistpy/df/tests/test_inputs.py +132 -0
  54. pianistpy-0.9.0/pianistpy/df/tests/test_invert.py +45 -0
  55. pianistpy-0.9.0/pianistpy/df/tests/test_leaves.py +95 -0
  56. pianistpy-0.9.0/pianistpy/df/tests/test_lookup.py +285 -0
  57. pianistpy-0.9.0/pianistpy/df/tests/test_report.py +25 -0
  58. pianistpy-0.9.0/pianistpy/df/tests/test_results.py +290 -0
  59. pianistpy-0.9.0/pianistpy/df/tests/test_row_bins.py +59 -0
  60. pianistpy-0.9.0/pianistpy/df/tests/test_top.py +56 -0
  61. pianistpy-0.9.0/pianistpy/df/tests/test_truncate.py +136 -0
  62. pianistpy-0.9.0/pianistpy/df/tests/test_truncation.py +61 -0
  63. pianistpy-0.9.0/pianistpy/df/tests/test_write_text.py +77 -0
  64. pianistpy-0.9.0/pianistpy/df/top.py +339 -0
  65. pianistpy-0.9.0/pianistpy/df/truncated.py +1059 -0
  66. pianistpy-0.9.0/pianistpy/df/truncation.py +669 -0
  67. pianistpy-0.9.0/pianistpy/df/validate.py +288 -0
  68. pianistpy-0.9.0/pianistpy/exceptions.py +79 -0
  69. pianistpy-0.9.0/pianistpy/identity.py +77 -0
  70. pianistpy-0.9.0/pianistpy/inference/__init__.py +5 -0
  71. pianistpy-0.9.0/pianistpy/inference/core.py +522 -0
  72. pianistpy-0.9.0/pianistpy/inference/tests/__init__.py +0 -0
  73. pianistpy-0.9.0/pianistpy/inference/tests/test_core.py +190 -0
  74. pianistpy-0.9.0/pianistpy/inference/tests/test_moments_scale.py +93 -0
  75. pianistpy-0.9.0/pianistpy/inference/tests/test_superposed.py +213 -0
  76. pianistpy-0.9.0/pianistpy/io/__init__.py +25 -0
  77. pianistpy-0.9.0/pianistpy/io/core.py +334 -0
  78. pianistpy-0.9.0/pianistpy/io/tests/__init__.py +0 -0
  79. pianistpy-0.9.0/pianistpy/io/tests/test_core.py +79 -0
  80. pianistpy-0.9.0/pianistpy/io/tests/test_read_guard.py +41 -0
  81. pianistpy-0.9.0/pianistpy/io/tests/test_text.py +90 -0
  82. pianistpy-0.9.0/pianistpy/io/text.py +117 -0
  83. pianistpy-0.9.0/pianistpy/numerics/__init__.py +14 -0
  84. pianistpy-0.9.0/pianistpy/numerics/chain_rule.py +103 -0
  85. pianistpy-0.9.0/pianistpy/numerics/hermite.py +141 -0
  86. pianistpy-0.9.0/pianistpy/numerics/pchip.py +131 -0
  87. pianistpy-0.9.0/pianistpy/numerics/pytrees.py +75 -0
  88. pianistpy-0.9.0/pianistpy/numerics/quadrature.py +67 -0
  89. pianistpy-0.9.0/pianistpy/numerics/tests/__init__.py +0 -0
  90. pianistpy-0.9.0/pianistpy/numerics/tests/test_chain_rule.py +35 -0
  91. pianistpy-0.9.0/pianistpy/numerics/tests/test_gradient_integrals.py +62 -0
  92. pianistpy-0.9.0/pianistpy/numerics/tests/test_gradient_tables.py +129 -0
  93. pianistpy-0.9.0/pianistpy/numerics/tests/test_hermite.py +61 -0
  94. pianistpy-0.9.0/pianistpy/numerics/tests/test_pchip.py +120 -0
  95. pianistpy-0.9.0/pianistpy/observables/__init__.py +6 -0
  96. pianistpy-0.9.0/pianistpy/observables/_angle.py +226 -0
  97. pianistpy-0.9.0/pianistpy/observables/_angle_rule.py +138 -0
  98. pianistpy-0.9.0/pianistpy/observables/_aperture.py +397 -0
  99. pianistpy-0.9.0/pianistpy/observables/_aperture_losvd.py +475 -0
  100. pianistpy-0.9.0/pianistpy/observables/_dispersion.py +396 -0
  101. pianistpy-0.9.0/pianistpy/observables/_ellipse.py +1502 -0
  102. pianistpy-0.9.0/pianistpy/observables/_ellipse_df.py +182 -0
  103. pianistpy-0.9.0/pianistpy/observables/_ellipse_model.py +466 -0
  104. pianistpy-0.9.0/pianistpy/observables/_ellipse_rules.py +321 -0
  105. pianistpy-0.9.0/pianistpy/observables/_kept.py +205 -0
  106. pianistpy-0.9.0/pianistpy/observables/_layout.py +294 -0
  107. pianistpy-0.9.0/pianistpy/observables/_losvd_stages.py +286 -0
  108. pianistpy-0.9.0/pianistpy/observables/_losvd_table.py +652 -0
  109. pianistpy-0.9.0/pianistpy/observables/_panel_table.py +616 -0
  110. pianistpy-0.9.0/pianistpy/observables/_projection.py +522 -0
  111. pianistpy-0.9.0/pianistpy/observables/_tables.py +397 -0
  112. pianistpy-0.9.0/pianistpy/observables/aperture.py +260 -0
  113. pianistpy-0.9.0/pianistpy/observables/core.py +416 -0
  114. pianistpy-0.9.0/pianistpy/observables/stages.py +1125 -0
  115. pianistpy-0.9.0/pianistpy/observables/tests/__init__.py +0 -0
  116. pianistpy-0.9.0/pianistpy/observables/tests/test_angle_rule.py +121 -0
  117. pianistpy-0.9.0/pianistpy/observables/tests/test_aperture.py +52 -0
  118. pianistpy-0.9.0/pianistpy/observables/tests/test_aperture_geometry.py +105 -0
  119. pianistpy-0.9.0/pianistpy/observables/tests/test_aperture_integrals.py +93 -0
  120. pianistpy-0.9.0/pianistpy/observables/tests/test_core.py +141 -0
  121. pianistpy-0.9.0/pianistpy/observables/tests/test_dispersion.py +72 -0
  122. pianistpy-0.9.0/pianistpy/observables/tests/test_ellipse_leaves.py +140 -0
  123. pianistpy-0.9.0/pianistpy/observables/tests/test_losvd_stages.py +405 -0
  124. pianistpy-0.9.0/pianistpy/observables/tests/test_projection.py +66 -0
  125. pianistpy-0.9.0/pianistpy/observables/tests/test_quick.py +49 -0
  126. pianistpy-0.9.0/pianistpy/observables/tests/test_stages.py +78 -0
  127. pianistpy-0.9.0/pianistpy/observables/tests/test_superposed.py +211 -0
  128. pianistpy-0.9.0/pianistpy/particles/__init__.py +22 -0
  129. pianistpy-0.9.0/pianistpy/particles/core.py +584 -0
  130. pianistpy-0.9.0/pianistpy/particles/speed.py +1501 -0
  131. pianistpy-0.9.0/pianistpy/particles/stages.py +1036 -0
  132. pianistpy-0.9.0/pianistpy/particles/tests/__init__.py +0 -0
  133. pianistpy-0.9.0/pianistpy/particles/tests/test_core.py +105 -0
  134. pianistpy-0.9.0/pianistpy/particles/tests/test_quick.py +25 -0
  135. pianistpy-0.9.0/pianistpy/particles/tests/test_stages.py +558 -0
  136. pianistpy-0.9.0/pianistpy/particles/tests/test_writers.py +179 -0
  137. pianistpy-0.9.0/pianistpy/particles/writers.py +343 -0
  138. pianistpy-0.9.0/pianistpy/profiles/__init__.py +6 -0
  139. pianistpy-0.9.0/pianistpy/profiles/core.py +570 -0
  140. pianistpy-0.9.0/pianistpy/profiles/series.py +226 -0
  141. pianistpy-0.9.0/pianistpy/profiles/tests/__init__.py +0 -0
  142. pianistpy-0.9.0/pianistpy/profiles/tests/test_core.py +135 -0
  143. pianistpy-0.9.0/pianistpy/profiles/tests/test_log_slope.py +32 -0
  144. pianistpy-0.9.0/pianistpy/profiles/tests/test_series.py +48 -0
  145. pianistpy-0.9.0/pianistpy/profiles/tests/test_truncated.py +212 -0
  146. pianistpy-0.9.0/pianistpy/profiles/truncated.py +477 -0
  147. pianistpy-0.9.0/pianistpy/runtime.py +629 -0
  148. pianistpy-0.9.0/pianistpy/settings.py +587 -0
  149. pianistpy-0.9.0/pianistpy/superposition/__init__.py +5 -0
  150. pianistpy-0.9.0/pianistpy/superposition/core.py +376 -0
  151. pianistpy-0.9.0/pianistpy/superposition/tests/__init__.py +0 -0
  152. pianistpy-0.9.0/pianistpy/superposition/tests/test_core.py +150 -0
  153. pianistpy-0.9.0/pianistpy/superposition/tests/test_files.py +73 -0
  154. pianistpy-0.9.0/pianistpy/superposition/tests/test_models_only.py +76 -0
  155. pianistpy-0.9.0/pianistpy/superposition/tests/test_sampler.py +62 -0
  156. pianistpy-0.9.0/pianistpy/tests/__init__.py +0 -0
  157. pianistpy-0.9.0/pianistpy/tests/test_backend.py +73 -0
  158. pianistpy-0.9.0/pianistpy/tests/test_dependencies.py +47 -0
  159. pianistpy-0.9.0/pianistpy/tests/test_layers.py +193 -0
  160. pianistpy-0.9.0/pianistpy/tests/test_pytrees.py +122 -0
  161. pianistpy-0.9.0/pianistpy/tests/test_runtime.py +160 -0
  162. pianistpy-0.9.0/pianistpy/tests/test_settings.py +77 -0
  163. pianistpy-0.9.0/pianistpy/tests/test_validation.py +112 -0
  164. pianistpy-0.9.0/pianistpy/typing.py +10 -0
  165. pianistpy-0.9.0/pianistpy/units/__init__.py +6 -0
  166. pianistpy-0.9.0/pianistpy/units/core.py +152 -0
  167. pianistpy-0.9.0/pianistpy/units/system.py +198 -0
  168. pianistpy-0.9.0/pianistpy/units/tests/__init__.py +0 -0
  169. pianistpy-0.9.0/pianistpy/units/tests/test_core.py +62 -0
  170. pianistpy-0.9.0/pianistpy/units/tests/test_system.py +102 -0
  171. pianistpy-0.9.0/pianistpy/validation.py +211 -0
  172. pianistpy-0.9.0/pianistpy.egg-info/PKG-INFO +224 -0
  173. pianistpy-0.9.0/pianistpy.egg-info/SOURCES.txt +176 -0
  174. pianistpy-0.9.0/pianistpy.egg-info/dependency_links.txt +1 -0
  175. pianistpy-0.9.0/pianistpy.egg-info/requires.txt +10 -0
  176. pianistpy-0.9.0/pianistpy.egg-info/top_level.txt +1 -0
  177. pianistpy-0.9.0/pyproject.toml +75 -0
  178. pianistpy-0.9.0/setup.cfg +4 -0
@@ -0,0 +1,7 @@
1
+ # Authors
2
+
3
+ PIANISTpy is written and maintained by
4
+
5
+ - Barry T. Chiang (Yale University, 2026), barrytchiang@gmail.com
6
+
7
+ To cite PIANISTpy, cite the paper that describes it (see the citation file).
@@ -0,0 +1,29 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2026, Barry T. Chiang
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ 1. Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
11
+
12
+ 2. Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ 3. Neither the name of the copyright holder nor the names of its
17
+ contributors may be used to endorse or promote products derived from
18
+ this software without specific prior written permission.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,224 @@
1
+ Metadata-Version: 2.4
2
+ Name: pianistpy
3
+ Version: 0.9.0
4
+ Summary: Distribution functions, particles and observables of anisotropic spherical systems.
5
+ Author-email: "Barry T. Chiang" <barrytchiang@gmail.com>
6
+ License: BSD 3-Clause License
7
+
8
+ Copyright (c) 2026, Barry T. Chiang
9
+ All rights reserved.
10
+
11
+ Redistribution and use in source and binary forms, with or without
12
+ modification, are permitted provided that the following conditions are met:
13
+
14
+ 1. Redistributions of source code must retain the above copyright notice, this
15
+ list of conditions and the following disclaimer.
16
+
17
+ 2. Redistributions in binary form must reproduce the above copyright notice,
18
+ this list of conditions and the following disclaimer in the documentation
19
+ and/or other materials provided with the distribution.
20
+
21
+ 3. Neither the name of the copyright holder nor the names of its
22
+ contributors may be used to endorse or promote products derived from
23
+ this software without specific prior written permission.
24
+
25
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
29
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
33
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35
+
36
+ Project-URL: Homepage, https://pianistpy.com
37
+ Project-URL: Documentation, https://pianistpy.com/docs/
38
+ Project-URL: Source, https://github.com/barrytchiang/PIANISTpy
39
+ Project-URL: Issues, https://github.com/barrytchiang/PIANISTpy/issues
40
+ Requires-Python: >=3.10
41
+ Description-Content-Type: text/markdown
42
+ License-File: LICENSE
43
+ License-File: AUTHORS.md
44
+ Requires-Dist: numpy>=2.0.0
45
+ Requires-Dist: scipy>=1.14.0
46
+ Requires-Dist: h5py>=3.11.0
47
+ Requires-Dist: astropy>=7.2.0
48
+ Requires-Dist: mpmath>=0.17
49
+ Requires-Dist: sympy>=1.2
50
+ Requires-Dist: jax>=0.6.1
51
+ Requires-Dist: diffrax>=0.7.0
52
+ Requires-Dist: optimistix>=0.0.11
53
+ Requires-Dist: filelock
54
+ Dynamic: license-file
55
+
56
+ <p align="center">
57
+ <img src="https://raw.githubusercontent.com/barrytchiang/PIANISTpy/main/docs/_static/logo.png" alt="PIANISTpy" width="320">
58
+ </p>
59
+
60
+ # PIANISTpy
61
+
62
+ [![Tests](https://github.com/barrytchiang/PIANISTpy/actions/workflows/tests.yml/badge.svg)](https://github.com/barrytchiang/PIANISTpy/actions/workflows/tests.yml)
63
+ [![Coverage](https://codecov.io/gh/barrytchiang/PIANISTpy/branch/main/graph/badge.svg)](https://codecov.io/gh/barrytchiang/PIANISTpy)
64
+ [![Documentation](https://img.shields.io/badge/docs-pianistpy.com-00356B)](https://pianistpy.com)
65
+ [![License](https://img.shields.io/badge/license-BSD--3--Clause-blue)](LICENSE)
66
+
67
+ **P**article **I**nitial conditions with **ANIS**o**T**ropy: distribution functions, particle initial conditions
68
+ and observables of spherical systems with anisotropic velocities, truncated self-consistently in phase space.
69
+
70
+ PIANISTpy builds equilibrium models of spherical systems (dark matter halos and subhalos, star clusters, dwarf
71
+ galaxies) with a velocity anisotropy you choose. The outer boundary of a model is set in phase space, so the
72
+ truncated model is itself an equilibrium and needs no relaxation run before it is used.
73
+
74
+ > Status: version 0.9.0, a pre-release. Version 1.0.0, released with the paper that describes the method,
75
+ > adds the certified accuracy tiers on both backends, the remaining capabilities and the citation.
76
+
77
+ ## What it does
78
+
79
+ - **Density profiles.** Any double power law in its three slopes, with the Navarro-Frenk-White, Hernquist and
80
+ Plummer profiles as named presets.
81
+ - **Velocity anisotropy.** Isotropic, constant, Osipkov-Merritt, and the generalized Osipkov-Merritt (Cuddeford)
82
+ models, radially biased (Type I) or tangentially biased (Type II).
83
+ - **Distribution functions.** The distribution function (DF) of each model, from the analytic inversion of its
84
+ density, returned whether or not it is physical, with a statement of where it is not.
85
+ - **Truncation in phase space.** Method I truncates a model's own DF at an energy you give, or at the energy that
86
+ yields a target mass or a target radius. Method II gives a new anisotropy to a density that is already
87
+ truncated.
88
+ - **Particles.** Positions and velocities drawn by inversion, with no rejection step, and reproducible particle
89
+ by particle.
90
+ - **Observables.** Intrinsic and line-of-sight velocity dispersions, line-of-sight velocity distributions, and
91
+ their averages over apertures.
92
+ - **Superposition.** Distribution functions inverted from one truncated density (Method II children of one
93
+ parent), combined with positive weights that sum to one into one equilibrium model.
94
+ - **Inference.** The log-likelihood of a model against data, as a JAX function of the components' weights and
95
+ the scales of the units.
96
+
97
+ ## Installation
98
+
99
+ ```
100
+ pip install pianistpy
101
+ ```
102
+
103
+ PIANISTpy needs Python 3.10 or later, NumPy, SciPy, h5py, Astropy, mpmath, SymPy, JAX, Diffrax, Optimistix and filelock.
104
+ The oldest versions the test suite passes with are NumPy 2.0.0, SciPy 1.14.0, h5py 3.11.0, Astropy 7.2.0,
105
+ mpmath 0.17, SymPy 1.2, and together JAX 0.6.1, Diffrax 0.7.0 and Optimistix 0.0.11 (these three are bound as
106
+ one set; older Diffrax or Optimistix releases pass only with a newer JAX); `pip` enforces them. It runs on Linux,
107
+ macOS and Windows.
108
+
109
+ ## Quick start
110
+
111
+ The example of the paper's first figure: an NFW halo with a constant velocity anisotropy of 0.25, truncated in
112
+ phase space at the virial mass of a concentration-10 halo, presented as a Milky Way-mass halo.
113
+
114
+ ```python
115
+ import pianistpy as pp
116
+ import astropy.units as u
117
+
118
+ # an untruncated NFW density profile with a constant velocity anisotropy of 0.25
119
+ halo = pp.Component(pp.DoublePowerLaw.nfw(), pp.Anisotropy(beta_c=0.25))
120
+
121
+ # the untruncated distribution function
122
+ df = pp.compute_df(halo)
123
+
124
+ # Method I: truncate it so that the truncated mass equals the virial mass of the
125
+ # untruncated NFW halo of concentration 10 (the mass inside 10 scale radii)
126
+ model = pp.truncate(df, pp.Truncation(mass_t=1.0, concentration=10))
127
+
128
+ # physical units: a halo of 1e12 solar masses with R_vir = c * r_0 = 200 kpc
129
+ model = model.to("physical", mass_0=1e12 * u.Msun, r_0=20 * u.kpc)
130
+
131
+ # draw ten million equal-mass particles centered at the origin and write them to a file
132
+ particles = pp.Sampler(model).sample(10_000_000, seed=1)
133
+ particles.write("NFW_Const0p25_Mt1e12_PARIC.h5")
134
+
135
+ # the line-of-sight velocity dispersion at three projected radii
136
+ sigma = pp.sigma_los(model, [2, 20, 100] * u.kpc)
137
+ ```
138
+
139
+ `pp.solve(halo, truncation)` runs the two steps at once.
140
+
141
+ ## Documentation
142
+
143
+ The user guide, the notebook tutorials, the technical notes and the API reference are at
144
+ [pianistpy.com](https://pianistpy.com).
145
+
146
+ ## Example notebooks
147
+
148
+ Tutorials for each workflow, and notebooks that reproduce the paper's figures, are in
149
+ [PIANISTpy-notebooks](https://github.com/barrytchiang/PIANISTpy-notebooks), tagged with the package version
150
+ they were run with.
151
+
152
+ ## Results and files
153
+
154
+ Every result (a distribution function, a model, a set of particles) is an immutable object that holds what it
155
+ was computed from: the inputs, the numerical settings and the version of the code. Its `write` method stores
156
+ it in a single HDF5 file, and `pp.read(path)` returns an equal object, whatever kind of result the file holds.
157
+ Nothing is written unless you ask.
158
+
159
+ A `Model` is always a valid equilibrium. A request for which none exists raises `UnphysicalModelError`, and the
160
+ exception carries the distribution function so that you can inspect it.
161
+
162
+ ## Units
163
+
164
+ Everything is computed and returned in internal units, the dimensionless normalization of Widrow (2000) in which
165
+ the scale radius and the central potential of the untruncated profile are 1. A result's `to` method presents it
166
+ in model units (G = M_0 = r_0 = 1) or, through Astropy, in physical units, without recomputing it, and what is
167
+ computed from it is returned in that system.
168
+
169
+ Three inputs of a truncation are given in the units that are natural for them, as their documentation states: the
170
+ truncation energy `k_t` in internal units, a target radius `r_t` in units of the scale radius, and a target mass
171
+ `mass_t` in units of M_0, together with the concentration that defines M_0.
172
+
173
+ ## Accuracy
174
+
175
+ Every result carries a validation report, a short and fixed summary of the checks it passed, with its tier: a
176
+ check within the accuracy goal (a relative error of 1e-6) is silent; above the goal, within the ceiling (1e-4) or
177
+ within the hard limit (1e-2), the result is returned with a warning in the package's log and as an
178
+ `AccuracyWarning` (Python's standard filter silences or escalates it), and the flag stays with the result and with
179
+ everything computed from it; beyond the hard limit the result is refused.
180
+
181
+ The numerical settings come in two tested tiers, both held to these checks by the test suite:
182
+
183
+ ```python
184
+ pp.Settings() # the default tier
185
+ pp.Settings(quick=True) # a faster tier for exploration
186
+ ```
187
+
188
+ ## Session switches
189
+
190
+ None of these changes a number.
191
+
192
+ ```python
193
+ pp.set_verbosity("info") # "quiet" (the default), "info" or "debug"
194
+ pp.set_timing(True) # print the wall time of each stage
195
+ pp.set_workers("half") # "auto" (the default), "full", "half" or a number of cores
196
+ pp.set_compilation_cache(False) # the compilation cache is on by default; a path moves it
197
+ pp.set_platform("cpu") # "auto" (the default), "cpu" (the CPU-only mode) or "gpu"; before the first computation
198
+ ```
199
+
200
+ The CPU-only mode gives the same numbers on every machine; the environment variable `PIANISTPY_PLATFORM=cpu`
201
+ sets it before the package is imported. Each file records the backend that made it.
202
+
203
+ Hardware notes. The workers' switch sets how many cores the package's parallel work uses; `"auto"` uses every
204
+ allowed core inside a batch job and half of them anywhere else, and no value changes a number. A GPU accelerates the
205
+ two batched table builds, the speed table (2.7 to 7 times an 8-core CPU on an RTX 3080 Ti) and the line-of-sight
206
+ velocity table (1.0 to 1.4 times a 32-core node); the distribution function and the potential solve gain nothing from
207
+ a GPU and run at their own speed on the CPU. The CPU-only install is complete and is the right one for a laptop. JAX's
208
+ CUDA builds are for Linux: on Windows the GPU is reached through the Windows Subsystem for Linux with the Linux
209
+ wheels, which pays only for large table builds; a native Windows Python runs the CPU-only mode. A GPU and a CPU give
210
+ results that differ at rounding on the kernels; the accuracy goal holds on both, and the file's provenance says which
211
+ made it. The compilation cache lives in the home folder by default (`~/.cache/pianistpy/jax`) and can fill a small
212
+ one; `set_compilation_cache` with a path moves it.
213
+
214
+ ## Citing
215
+
216
+ If you use PIANISTpy, cite the paper that describes it. The citation entry is added with the 1.0.0 release.
217
+
218
+ ## License
219
+
220
+ BSD 3-Clause. See `LICENSE`.
221
+
222
+ ## Contributing
223
+
224
+ See `CONTRIBUTING.md` and the code conventions in `STYLE_GUIDE.md`.
@@ -0,0 +1,169 @@
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/barrytchiang/PIANISTpy/main/docs/_static/logo.png" alt="PIANISTpy" width="320">
3
+ </p>
4
+
5
+ # PIANISTpy
6
+
7
+ [![Tests](https://github.com/barrytchiang/PIANISTpy/actions/workflows/tests.yml/badge.svg)](https://github.com/barrytchiang/PIANISTpy/actions/workflows/tests.yml)
8
+ [![Coverage](https://codecov.io/gh/barrytchiang/PIANISTpy/branch/main/graph/badge.svg)](https://codecov.io/gh/barrytchiang/PIANISTpy)
9
+ [![Documentation](https://img.shields.io/badge/docs-pianistpy.com-00356B)](https://pianistpy.com)
10
+ [![License](https://img.shields.io/badge/license-BSD--3--Clause-blue)](LICENSE)
11
+
12
+ **P**article **I**nitial conditions with **ANIS**o**T**ropy: distribution functions, particle initial conditions
13
+ and observables of spherical systems with anisotropic velocities, truncated self-consistently in phase space.
14
+
15
+ PIANISTpy builds equilibrium models of spherical systems (dark matter halos and subhalos, star clusters, dwarf
16
+ galaxies) with a velocity anisotropy you choose. The outer boundary of a model is set in phase space, so the
17
+ truncated model is itself an equilibrium and needs no relaxation run before it is used.
18
+
19
+ > Status: version 0.9.0, a pre-release. Version 1.0.0, released with the paper that describes the method,
20
+ > adds the certified accuracy tiers on both backends, the remaining capabilities and the citation.
21
+
22
+ ## What it does
23
+
24
+ - **Density profiles.** Any double power law in its three slopes, with the Navarro-Frenk-White, Hernquist and
25
+ Plummer profiles as named presets.
26
+ - **Velocity anisotropy.** Isotropic, constant, Osipkov-Merritt, and the generalized Osipkov-Merritt (Cuddeford)
27
+ models, radially biased (Type I) or tangentially biased (Type II).
28
+ - **Distribution functions.** The distribution function (DF) of each model, from the analytic inversion of its
29
+ density, returned whether or not it is physical, with a statement of where it is not.
30
+ - **Truncation in phase space.** Method I truncates a model's own DF at an energy you give, or at the energy that
31
+ yields a target mass or a target radius. Method II gives a new anisotropy to a density that is already
32
+ truncated.
33
+ - **Particles.** Positions and velocities drawn by inversion, with no rejection step, and reproducible particle
34
+ by particle.
35
+ - **Observables.** Intrinsic and line-of-sight velocity dispersions, line-of-sight velocity distributions, and
36
+ their averages over apertures.
37
+ - **Superposition.** Distribution functions inverted from one truncated density (Method II children of one
38
+ parent), combined with positive weights that sum to one into one equilibrium model.
39
+ - **Inference.** The log-likelihood of a model against data, as a JAX function of the components' weights and
40
+ the scales of the units.
41
+
42
+ ## Installation
43
+
44
+ ```
45
+ pip install pianistpy
46
+ ```
47
+
48
+ PIANISTpy needs Python 3.10 or later, NumPy, SciPy, h5py, Astropy, mpmath, SymPy, JAX, Diffrax, Optimistix and filelock.
49
+ The oldest versions the test suite passes with are NumPy 2.0.0, SciPy 1.14.0, h5py 3.11.0, Astropy 7.2.0,
50
+ mpmath 0.17, SymPy 1.2, and together JAX 0.6.1, Diffrax 0.7.0 and Optimistix 0.0.11 (these three are bound as
51
+ one set; older Diffrax or Optimistix releases pass only with a newer JAX); `pip` enforces them. It runs on Linux,
52
+ macOS and Windows.
53
+
54
+ ## Quick start
55
+
56
+ The example of the paper's first figure: an NFW halo with a constant velocity anisotropy of 0.25, truncated in
57
+ phase space at the virial mass of a concentration-10 halo, presented as a Milky Way-mass halo.
58
+
59
+ ```python
60
+ import pianistpy as pp
61
+ import astropy.units as u
62
+
63
+ # an untruncated NFW density profile with a constant velocity anisotropy of 0.25
64
+ halo = pp.Component(pp.DoublePowerLaw.nfw(), pp.Anisotropy(beta_c=0.25))
65
+
66
+ # the untruncated distribution function
67
+ df = pp.compute_df(halo)
68
+
69
+ # Method I: truncate it so that the truncated mass equals the virial mass of the
70
+ # untruncated NFW halo of concentration 10 (the mass inside 10 scale radii)
71
+ model = pp.truncate(df, pp.Truncation(mass_t=1.0, concentration=10))
72
+
73
+ # physical units: a halo of 1e12 solar masses with R_vir = c * r_0 = 200 kpc
74
+ model = model.to("physical", mass_0=1e12 * u.Msun, r_0=20 * u.kpc)
75
+
76
+ # draw ten million equal-mass particles centered at the origin and write them to a file
77
+ particles = pp.Sampler(model).sample(10_000_000, seed=1)
78
+ particles.write("NFW_Const0p25_Mt1e12_PARIC.h5")
79
+
80
+ # the line-of-sight velocity dispersion at three projected radii
81
+ sigma = pp.sigma_los(model, [2, 20, 100] * u.kpc)
82
+ ```
83
+
84
+ `pp.solve(halo, truncation)` runs the two steps at once.
85
+
86
+ ## Documentation
87
+
88
+ The user guide, the notebook tutorials, the technical notes and the API reference are at
89
+ [pianistpy.com](https://pianistpy.com).
90
+
91
+ ## Example notebooks
92
+
93
+ Tutorials for each workflow, and notebooks that reproduce the paper's figures, are in
94
+ [PIANISTpy-notebooks](https://github.com/barrytchiang/PIANISTpy-notebooks), tagged with the package version
95
+ they were run with.
96
+
97
+ ## Results and files
98
+
99
+ Every result (a distribution function, a model, a set of particles) is an immutable object that holds what it
100
+ was computed from: the inputs, the numerical settings and the version of the code. Its `write` method stores
101
+ it in a single HDF5 file, and `pp.read(path)` returns an equal object, whatever kind of result the file holds.
102
+ Nothing is written unless you ask.
103
+
104
+ A `Model` is always a valid equilibrium. A request for which none exists raises `UnphysicalModelError`, and the
105
+ exception carries the distribution function so that you can inspect it.
106
+
107
+ ## Units
108
+
109
+ Everything is computed and returned in internal units, the dimensionless normalization of Widrow (2000) in which
110
+ the scale radius and the central potential of the untruncated profile are 1. A result's `to` method presents it
111
+ in model units (G = M_0 = r_0 = 1) or, through Astropy, in physical units, without recomputing it, and what is
112
+ computed from it is returned in that system.
113
+
114
+ Three inputs of a truncation are given in the units that are natural for them, as their documentation states: the
115
+ truncation energy `k_t` in internal units, a target radius `r_t` in units of the scale radius, and a target mass
116
+ `mass_t` in units of M_0, together with the concentration that defines M_0.
117
+
118
+ ## Accuracy
119
+
120
+ Every result carries a validation report, a short and fixed summary of the checks it passed, with its tier: a
121
+ check within the accuracy goal (a relative error of 1e-6) is silent; above the goal, within the ceiling (1e-4) or
122
+ within the hard limit (1e-2), the result is returned with a warning in the package's log and as an
123
+ `AccuracyWarning` (Python's standard filter silences or escalates it), and the flag stays with the result and with
124
+ everything computed from it; beyond the hard limit the result is refused.
125
+
126
+ The numerical settings come in two tested tiers, both held to these checks by the test suite:
127
+
128
+ ```python
129
+ pp.Settings() # the default tier
130
+ pp.Settings(quick=True) # a faster tier for exploration
131
+ ```
132
+
133
+ ## Session switches
134
+
135
+ None of these changes a number.
136
+
137
+ ```python
138
+ pp.set_verbosity("info") # "quiet" (the default), "info" or "debug"
139
+ pp.set_timing(True) # print the wall time of each stage
140
+ pp.set_workers("half") # "auto" (the default), "full", "half" or a number of cores
141
+ pp.set_compilation_cache(False) # the compilation cache is on by default; a path moves it
142
+ pp.set_platform("cpu") # "auto" (the default), "cpu" (the CPU-only mode) or "gpu"; before the first computation
143
+ ```
144
+
145
+ The CPU-only mode gives the same numbers on every machine; the environment variable `PIANISTPY_PLATFORM=cpu`
146
+ sets it before the package is imported. Each file records the backend that made it.
147
+
148
+ Hardware notes. The workers' switch sets how many cores the package's parallel work uses; `"auto"` uses every
149
+ allowed core inside a batch job and half of them anywhere else, and no value changes a number. A GPU accelerates the
150
+ two batched table builds, the speed table (2.7 to 7 times an 8-core CPU on an RTX 3080 Ti) and the line-of-sight
151
+ velocity table (1.0 to 1.4 times a 32-core node); the distribution function and the potential solve gain nothing from
152
+ a GPU and run at their own speed on the CPU. The CPU-only install is complete and is the right one for a laptop. JAX's
153
+ CUDA builds are for Linux: on Windows the GPU is reached through the Windows Subsystem for Linux with the Linux
154
+ wheels, which pays only for large table builds; a native Windows Python runs the CPU-only mode. A GPU and a CPU give
155
+ results that differ at rounding on the kernels; the accuracy goal holds on both, and the file's provenance says which
156
+ made it. The compilation cache lives in the home folder by default (`~/.cache/pianistpy/jax`) and can fill a small
157
+ one; `set_compilation_cache` with a path moves it.
158
+
159
+ ## Citing
160
+
161
+ If you use PIANISTpy, cite the paper that describes it. The citation entry is added with the 1.0.0 release.
162
+
163
+ ## License
164
+
165
+ BSD 3-Clause. See `LICENSE`.
166
+
167
+ ## Contributing
168
+
169
+ See `CONTRIBUTING.md` and the code conventions in `STYLE_GUIDE.md`.
@@ -0,0 +1,77 @@
1
+ """PIANISTpy: equilibrium models of anisotropic spherical systems."""
2
+
3
+ __all__ = [
4
+ "AccuracyError",
5
+ "AccuracyWarning",
6
+ "Anisotropy",
7
+ "Aperture",
8
+ "Component",
9
+ "DistributionFunction",
10
+ "DoublePowerLaw",
11
+ "InputError",
12
+ "LosTable",
13
+ "Model",
14
+ "PianistpyError",
15
+ "PianistpyWarning",
16
+ "ResultError",
17
+ "Sampler",
18
+ "Settings",
19
+ "Truncation",
20
+ "UnphysicalModelError",
21
+ "__version__",
22
+ "compute_df",
23
+ "log",
24
+ "platform",
25
+ "read",
26
+ "set_compilation_cache",
27
+ "set_timing",
28
+ "set_verbosity",
29
+ "set_platform",
30
+ "set_workers",
31
+ "sigma_los",
32
+ "sigma_los_aperture",
33
+ "solve",
34
+ "superpose",
35
+ "truncate",
36
+ ]
37
+
38
+ # The reader of data files registers itself on import.
39
+ from . import inference as inference
40
+ from ._version import __version__
41
+ from .anisotropy import Anisotropy
42
+ from .df import (
43
+ Component,
44
+ DistributionFunction,
45
+ Model,
46
+ Truncation,
47
+ compute_df,
48
+ solve,
49
+ truncate,
50
+ )
51
+ from .exceptions import (
52
+ AccuracyError,
53
+ AccuracyWarning,
54
+ InputError,
55
+ PianistpyError,
56
+ PianistpyWarning,
57
+ ResultError,
58
+ UnphysicalModelError,
59
+ )
60
+ from .io import read
61
+ from .observables import Aperture, LosTable, sigma_los, sigma_los_aperture
62
+ from .particles import Sampler
63
+ from .profiles import DoublePowerLaw
64
+ from .runtime import (
65
+ configure_jax,
66
+ log,
67
+ platform,
68
+ set_compilation_cache,
69
+ set_platform,
70
+ set_timing,
71
+ set_verbosity,
72
+ set_workers,
73
+ )
74
+ from .settings import Settings
75
+ from .superposition import superpose
76
+
77
+ configure_jax()
@@ -0,0 +1,5 @@
1
+ """Version of the package."""
2
+
3
+ __all__ = ["__version__"]
4
+
5
+ __version__ = "0.9.0"
@@ -0,0 +1,5 @@
1
+ """Velocity anisotropy models."""
2
+
3
+ __all__ = ["Anisotropy"]
4
+
5
+ from .core import Anisotropy