PyCosmoMMF 0.3.0__tar.gz → 0.5.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 (68) hide show
  1. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/PKG-INFO +3 -2
  2. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/src/pycosmommf/_version.py +2 -2
  3. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/src/pycosmommf/signatures.py +1 -1
  4. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/src/pycosmommf/tagging.py +55 -38
  5. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/tests/test_integration.py +16 -1
  6. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/.copier-answers.yml +0 -0
  7. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/.git_archival.txt +0 -0
  8. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/.gitattributes +0 -0
  9. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/.github/CONTRIBUTING.md +0 -0
  10. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/.github/dependabot.yml +0 -0
  11. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/.github/release.yml +0 -0
  12. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/.github/workflows/cd.yml +0 -0
  13. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/.github/workflows/ci.yml +0 -0
  14. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/.gitignore +0 -0
  15. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/.pre-commit-config.yaml +0 -0
  16. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/.readthedocs.yaml +0 -0
  17. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/LICENSE +0 -0
  18. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/README.md +0 -0
  19. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/docs/_static/CosmoMMF_Dark.png +0 -0
  20. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/docs/_static/CosmoMMF_light.png +0 -0
  21. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/docs/_static/basic.css +0 -0
  22. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/docs/_static/check-solid.svg +0 -0
  23. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/docs/_static/clipboard.min.js +0 -0
  24. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/docs/_static/copy-button.svg +0 -0
  25. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/docs/_static/copybutton.css +0 -0
  26. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/docs/_static/copybutton.js +0 -0
  27. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/docs/_static/copybutton_funcs.js +0 -0
  28. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/docs/_static/debug.css +0 -0
  29. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/docs/_static/doctools.js +0 -0
  30. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/docs/_static/documentation_options.js +0 -0
  31. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/docs/_static/file.png +0 -0
  32. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/docs/_static/language_data.js +0 -0
  33. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/docs/_static/minus.png +0 -0
  34. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/docs/_static/plus.png +0 -0
  35. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/docs/_static/pygments.css +0 -0
  36. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/docs/_static/scripts/furo-extensions.js +0 -0
  37. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/docs/_static/scripts/furo.js +0 -0
  38. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/docs/_static/scripts/furo.js.LICENSE.txt +0 -0
  39. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/docs/_static/scripts/furo.js.map +0 -0
  40. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/docs/_static/searchtools.js +0 -0
  41. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/docs/_static/skeleton.css +0 -0
  42. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/docs/_static/sphinx_highlight.js +0 -0
  43. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/docs/_static/styles/furo-extensions.css +0 -0
  44. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/docs/_static/styles/furo-extensions.css.map +0 -0
  45. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/docs/_static/styles/furo.css +0 -0
  46. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/docs/_static/styles/furo.css.map +0 -0
  47. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/docs/api/pycosmommf.rst +0 -0
  48. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/docs/api_tab.md +0 -0
  49. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/docs/basic_usage.md +0 -0
  50. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/docs/conf.py +0 -0
  51. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/docs/images/CosmoMMF_Dark.png +0 -0
  52. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/docs/images/CosmoMMF_light.png +0 -0
  53. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/docs/images/final_NEXUSPLUS_Signatures_hydro.png +0 -0
  54. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/docs/images/final_NEXUSPLUS_Signatures_hydro_dark.png +0 -0
  55. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/docs/images/final_tagging_figure.png +0 -0
  56. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/docs/images/final_tagging_figure_dark.png +0 -0
  57. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/docs/index.md +0 -0
  58. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/noxfile.py +0 -0
  59. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/pyproject.toml +0 -0
  60. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/src/pycosmommf/__init__.py +0 -0
  61. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/src/pycosmommf/_version.pyi +0 -0
  62. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/src/pycosmommf/filter.py +0 -0
  63. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/src/pycosmommf/hessian.py +0 -0
  64. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/src/pycosmommf/py.typed +0 -0
  65. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/src/pycosmommf/utils.py +0 -0
  66. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/tests/__init__.py +0 -0
  67. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/tests/test_package.py +0 -0
  68. {pycosmommf-0.3.0 → pycosmommf-0.5.0}/tests/test_unit.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: pycosmommf
3
- Version: 0.3.0
3
+ Version: 0.5.0
4
4
  Summary: A package for identifying structures in the cosmic web.
5
5
  Project-URL: Homepage, https://github.com/James11222/pycosmommf
6
6
  Project-URL: Bug Tracker, https://github.com/James11222/pycosmommf/issues
@@ -26,6 +26,7 @@ License: Copyright 2024 James Sunseri
26
26
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
27
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
28
  SOFTWARE.
29
+ License-File: LICENSE
29
30
  Classifier: Development Status :: 1 - Planning
30
31
  Classifier: Intended Audience :: Developers
31
32
  Classifier: Intended Audience :: Science/Research
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '0.3.0'
16
- __version_tuple__ = version_tuple = (0, 3, 0)
15
+ __version__ = version = '0.5.0'
16
+ __version_tuple__ = version_tuple = (0, 5, 0)
@@ -99,7 +99,7 @@ def maximum_signature(Rs, field, alg="NEXUSPLUS", eps=1e-8):
99
99
  sigmax : 4D array
100
100
  The maximum signatures.
101
101
  """
102
- if alg not in ["NEXUS", "NEXUSPLUS"]:
102
+ if alg not in ["NEXUS", "NEXUSPLUS"]: # pragma: no cover
103
103
  msg = "alg must be either 'NEXUS' or 'NEXUSPLUS'"
104
104
  raise ValueError(msg)
105
105
 
@@ -1,10 +1,11 @@
1
1
  from __future__ import annotations
2
2
 
3
- import functools
4
-
3
+ import numba as nb
5
4
  import numpy as np
6
5
  import skimage.measure
7
6
 
7
+ jit_compiler = nb.njit(parallel=True, fastmath=True)
8
+
8
9
 
9
10
  def make_the_clusbool(delta, max_sigs, Δ): # pragma: no cover
10
11
  """
@@ -16,43 +17,57 @@ def make_the_clusbool(delta, max_sigs, Δ): # pragma: no cover
16
17
  change in virialization fraction as a function of 𝒮_cluster.
17
18
 
18
19
  Arguments:
19
-
20
- delta - [3D Float Array] - delta refers to the δ delta.
20
+ ----------------
21
+ delta - [3D Float Array] - delta refers to the δ field.
21
22
  max_sigs - [4D Float Array] - the maximum signatures array from CosmoMMF.maximum_signature()
22
23
  verbose - [Boolean] - a flag to allow the function to be more helpful and verbose.
23
24
  Δ - [Int] - The overdensity parameter threshold for determining virialization. This
24
25
  parameter comes from a paper by Gunn & Gott on spherical collapse. Commonly
25
26
  used values that are physically motivated can be 370, 200, or 500 (for R_200 or R_500).
27
+
28
+ Returns:
29
+ ----------------
30
+ clusbool - [3D Boolean Array] - a boolean filter that selects only the clusters in a given cosmological δ field.
31
+ S_th - [Float] - the threshold value for 𝒮_cluster.
32
+ signature_thresholds - [1D Float Array] - an array of threshold values for 𝒮_cluster.
33
+ virialized_fractions - [1D Float Array] - an array of virialized fractions as a function of 𝒮_cluster.
26
34
  """
27
35
 
28
36
  signature_thresholds = np.geomspace(0.1, 30, 10) # hard coded for now
29
37
  virialized_fractions = np.zeros_like(signature_thresholds)
30
38
 
31
- def check_virialized(j, delta, clumps, Δ):
32
- δ_ave = np.mean(delta[clumps == j])
33
- return int(δ_ave > Δ)
39
+ @jit_compiler
40
+ def calc_virialized_fraction(clumps):
41
+ nx, ny, nz = clumps.shape
42
+
43
+ total_clumps_detected = np.max(clumps)
44
+ summed_overdensity_per_clump = np.zeros(total_clumps_detected)
45
+ num_cells_per_clump = np.zeros(total_clumps_detected)
46
+
47
+ for i in nb.prange(nx):
48
+ for j in range(ny):
49
+ for k in range(nz):
50
+ if clumps[i, j, k] != 0:
51
+ clump_id = int(
52
+ clumps[i, j, k]
53
+ ) # clump_id is the clump number ranging from 1 to total_clumps_detected
54
+ summed_overdensity_per_clump[clump_id - 1] += delta[i, j, k]
55
+ num_cells_per_clump[clump_id - 1] += 1
56
+
57
+ return summed_overdensity_per_clump / num_cells_per_clump
34
58
 
35
59
  for i, S_th in enumerate(signature_thresholds):
36
60
  # Identify Clumps with S > S_th
37
61
  _clusbool = max_sigs[:, :, :, 0] > S_th
62
+
38
63
  clumps = skimage.measure.label(
39
- _clusbool
64
+ _clusbool.astype(int)
40
65
  ) # an array where 0 is background and 1, 2, 3, ... are the clumps
41
- total_clumps_detected = np.max(clumps)
42
-
43
- num_clumps_virialized = np.sum(
44
- list(
45
- map(
46
- functools.partial(
47
- check_virialized, delta=delta, clumps=clumps, Δ=Δ
48
- ),
49
- range(1, total_clumps_detected + 1),
50
- )
51
- )
52
- ) # number of clumps that are virialized
66
+ averaged_overdensity_per_clump = calc_virialized_fraction(clumps)
53
67
 
54
- # Calculate the number of virialized clumps
55
- virialized_fractions[i] = num_clumps_virialized / total_clumps_detected
68
+ virialized_fractions[i] = np.sum(averaged_overdensity_per_clump > Δ) / np.max(
69
+ clumps
70
+ )
56
71
 
57
72
  S_th = signature_thresholds[np.abs(virialized_fractions - 0.5).argmin()]
58
73
  clusbool = max_sigs[:, :, :, 0] > S_th
@@ -60,14 +75,14 @@ def make_the_clusbool(delta, max_sigs, Δ): # pragma: no cover
60
75
  return clusbool, S_th, signature_thresholds, virialized_fractions
61
76
 
62
77
 
63
- def calc_mass_change(sig_vec, delta_vec, Smin, Smax):
78
+ def calc_mass_change(sig_vec, density_vec, Smin, Smax):
64
79
  """
65
80
  Calculate the mass change curve for a given structure type.
66
81
 
67
82
  Arguments:
68
83
  -----------
69
84
  sig_vec - [1D Float Array] - the signature values for a given structure type
70
- delta_vec - [1D Float Array] - the delta values for a given structure type
85
+ density_vec - [1D Float Array] - the density values for a given structure type
71
86
  Smin - [Float] - the minimum signature value
72
87
  Smax - [Float] - the maximum signature value
73
88
 
@@ -83,7 +98,7 @@ def calc_mass_change(sig_vec, delta_vec, Smin, Smax):
83
98
  # Sum up all the mass in a structure type as a function of log(𝒮)
84
99
  for i in range(len(M)):
85
100
  filter = sig_vec > 10 ** log10S[i]
86
- M[i] = np.sum(delta_vec[filter])
101
+ M[i] = np.sum(density_vec[filter])
87
102
 
88
103
  # Compute the derivative |dM^2/dlog(𝒮)|
89
104
  ΔM_2 = np.abs(np.diff(M**2) / np.diff(log10S))
@@ -96,14 +111,14 @@ def calc_mass_change(sig_vec, delta_vec, Smin, Smax):
96
111
 
97
112
 
98
113
  def calc_structure_bools(
99
- delta, max_sigs, verbose, clusbool=None, Smin=-3, Smax=2, Δ=370
114
+ density_cube, max_sigs, verbose, clusbool=None, Smin=-3, Smax=2, Δ=370
100
115
  ):
101
116
  """
102
117
  Calculate the boolean filters for clusters, filaments, walls, and voids.
103
118
 
104
119
  Arguments:
105
120
  -----------
106
- delta - [3D Float Array] - the δ delta
121
+ density_cube - [3D Float Array] - the δ+1 density_cube
107
122
  max_sigs - [4D Float Array] - the maximum signatures array from CosmoMMF.maximum_signature()
108
123
  verbose - [Boolean] - a flag to allow the function to be more helpful and verbose.
109
124
  clusbool - [3D Boolean Array] - the cluster boolean filter
@@ -129,15 +144,15 @@ def calc_structure_bools(
129
144
 
130
145
  """
131
146
 
132
- N_cells = np.prod(delta.shape)
147
+ N_cells = np.prod(density_cube.shape)
133
148
 
134
149
  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
135
150
  # Step 1. Create Cluster Boolean Filter
136
151
  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
137
152
 
138
- if clusbool is None:
153
+ if clusbool is None: # pragma: no cover
139
154
  clusbool, cluster_thresh, S_clus, f_vir_clus = make_the_clusbool(
140
- delta, max_sigs, Δ
155
+ density_cube - 1.0, max_sigs, Δ
141
156
  )
142
157
  else:
143
158
  cluster_thresh = None
@@ -155,10 +170,12 @@ def calc_structure_bools(
155
170
  # Isolate Valid Filaments (not clusters)
156
171
  not_clus_flat = np.reshape(clusbool == False, N_cells) # noqa: E712
157
172
  filament_valid = filament_signature[not_clus_flat]
158
- flat_delta_valid = np.reshape(delta, N_cells)[not_clus_flat]
173
+ flat_density_cube_valid = np.reshape(density_cube, N_cells)[not_clus_flat]
159
174
 
160
175
  # Compute Mass Change Curves and Find Filament Threshold
161
- S_fil, dM2_fil = calc_mass_change(filament_valid, flat_delta_valid, Smin, Smax)
176
+ S_fil, dM2_fil = calc_mass_change(
177
+ filament_valid, flat_density_cube_valid, Smin, Smax
178
+ )
162
179
  ind = np.argmax(dM2_fil)
163
180
  filament_thresh = S_fil[ind]
164
181
  filbool = (max_sigs[:, :, :, 1] > filament_thresh) & (~clusbool)
@@ -170,10 +187,10 @@ def calc_structure_bools(
170
187
  # Isolate Valid Walls (not clusters or filaments)
171
188
  wall_valid_filt = (not_clus_flat) & (filament_signature < filament_thresh)
172
189
  wall_valid = wall_signature[wall_valid_filt]
173
- wall_delta_valid = np.reshape(delta, N_cells)[wall_valid_filt]
190
+ wall_density_cube_valid = np.reshape(density_cube, N_cells)[wall_valid_filt]
174
191
 
175
192
  # Compute Mass Change Curves and Find Wall Threshold
176
- S_wall, dM2_wall = calc_mass_change(wall_valid, wall_delta_valid, Smin, Smax)
193
+ S_wall, dM2_wall = calc_mass_change(wall_valid, wall_density_cube_valid, Smin, Smax)
177
194
  ind = np.argmax(dM2_wall)
178
195
  wall_thresh = S_wall[ind]
179
196
  wallbool = (max_sigs[:, :, :, 2] > wall_thresh) & ~(filbool | clusbool)
@@ -190,7 +207,7 @@ def calc_structure_bools(
190
207
  print("---------------------------------------\n")
191
208
 
192
209
  volume_fraction = np.sum(clusbool) / N_cells
193
- mass_fraction = np.sum(delta[clusbool]) / np.sum(delta)
210
+ mass_fraction = np.sum(density_cube[clusbool]) / np.sum(density_cube)
194
211
 
195
212
  print("Signature Threshold of Clusters:", cluster_thresh)
196
213
  print("Volume Fraction of Clusters:", volume_fraction)
@@ -202,7 +219,7 @@ def calc_structure_bools(
202
219
  print("---------------------------------------\n")
203
220
 
204
221
  volume_fraction = np.sum(filbool) / N_cells
205
- mass_fraction = np.sum(delta[filbool]) / np.sum(delta)
222
+ mass_fraction = np.sum(density_cube[filbool]) / np.sum(density_cube)
206
223
 
207
224
  print("Signature Threshold of Filaments:", filament_thresh)
208
225
  print("Volume Fraction of Filaments:", volume_fraction)
@@ -214,7 +231,7 @@ def calc_structure_bools(
214
231
  print("---------------------------------------\n")
215
232
 
216
233
  volume_fraction = np.sum(wallbool) / N_cells
217
- mass_fraction = np.sum(delta[wallbool]) / np.sum(delta)
234
+ mass_fraction = np.sum(density_cube[wallbool]) / np.sum(density_cube)
218
235
 
219
236
  print("Signature Threshold of Walls:", wall_thresh)
220
237
  print("Volume Fraction of Walls:", volume_fraction)
@@ -226,7 +243,7 @@ def calc_structure_bools(
226
243
  print("---------------------------------------\n")
227
244
 
228
245
  volume_fraction = np.sum(voidbool) / N_cells
229
- mass_fraction = np.sum(delta[voidbool]) / np.sum(delta)
246
+ mass_fraction = np.sum(density_cube[voidbool]) / np.sum(density_cube)
230
247
 
231
248
  print("Volume Fraction of Voids:", volume_fraction)
232
249
  print("Mass Fraction of Voids:", mass_fraction)
@@ -30,7 +30,7 @@ def test_calc_structure_bools():
30
30
  np.roll(m.sphere(32, 2), -10, axis=0) + np.roll(m.sphere(32, 2), 10, axis=0)
31
31
  ) >= 1
32
32
  clusbool, filbool, wallbool, voidbool, summary_stats = m.calc_structure_bools(
33
- delta=field,
33
+ field,
34
34
  max_sigs=sigs,
35
35
  verbose=True,
36
36
  clusbool=clusbool,
@@ -42,3 +42,18 @@ def test_calc_structure_bools():
42
42
  assert filbool.shape == (32, 32, 32)
43
43
  assert wallbool.shape == (32, 32, 32)
44
44
  assert voidbool.shape == (32, 32, 32)
45
+ assert type(summary_stats) is dict
46
+
47
+ clusbool, filbool, wallbool, voidbool = m.calc_structure_bools(
48
+ field,
49
+ max_sigs=sigs,
50
+ verbose=False,
51
+ clusbool=clusbool,
52
+ Smin=-3,
53
+ Smax=2,
54
+ Δ=370,
55
+ )
56
+ assert clusbool.shape == (32, 32, 32)
57
+ assert filbool.shape == (32, 32, 32)
58
+ assert wallbool.shape == (32, 32, 32)
59
+ assert voidbool.shape == (32, 32, 32)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes