PyCosmoMMF 0.6.0__tar.gz → 1.0.1__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.6.0 → pycosmommf-1.0.1}/PKG-INFO +2 -1
  2. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/README.md +1 -0
  3. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/docs/basic_usage.md +16 -19
  4. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/pyproject.toml +1 -0
  5. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/src/pycosmommf/_version.py +9 -4
  6. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/src/pycosmommf/filter.py +59 -74
  7. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/src/pycosmommf/hessian.py +11 -13
  8. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/src/pycosmommf/signatures.py +33 -34
  9. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/src/pycosmommf/tagging.py +94 -64
  10. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/src/pycosmommf/utils.py +40 -6
  11. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/tests/test_integration.py +21 -7
  12. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/.copier-answers.yml +0 -0
  13. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/.git_archival.txt +0 -0
  14. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/.gitattributes +0 -0
  15. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/.github/CONTRIBUTING.md +0 -0
  16. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/.github/dependabot.yml +0 -0
  17. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/.github/release.yml +0 -0
  18. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/.github/workflows/cd.yml +0 -0
  19. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/.github/workflows/ci.yml +0 -0
  20. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/.gitignore +0 -0
  21. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/.pre-commit-config.yaml +0 -0
  22. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/.readthedocs.yaml +0 -0
  23. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/LICENSE +0 -0
  24. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/docs/_static/CosmoMMF_Dark.png +0 -0
  25. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/docs/_static/CosmoMMF_light.png +0 -0
  26. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/docs/_static/basic.css +0 -0
  27. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/docs/_static/check-solid.svg +0 -0
  28. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/docs/_static/clipboard.min.js +0 -0
  29. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/docs/_static/copy-button.svg +0 -0
  30. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/docs/_static/copybutton.css +0 -0
  31. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/docs/_static/copybutton.js +0 -0
  32. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/docs/_static/copybutton_funcs.js +0 -0
  33. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/docs/_static/debug.css +0 -0
  34. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/docs/_static/doctools.js +0 -0
  35. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/docs/_static/documentation_options.js +0 -0
  36. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/docs/_static/file.png +0 -0
  37. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/docs/_static/language_data.js +0 -0
  38. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/docs/_static/minus.png +0 -0
  39. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/docs/_static/plus.png +0 -0
  40. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/docs/_static/pygments.css +0 -0
  41. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/docs/_static/scripts/furo-extensions.js +0 -0
  42. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/docs/_static/scripts/furo.js +0 -0
  43. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/docs/_static/scripts/furo.js.LICENSE.txt +0 -0
  44. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/docs/_static/scripts/furo.js.map +0 -0
  45. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/docs/_static/searchtools.js +0 -0
  46. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/docs/_static/skeleton.css +0 -0
  47. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/docs/_static/sphinx_highlight.js +0 -0
  48. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/docs/_static/styles/furo-extensions.css +0 -0
  49. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/docs/_static/styles/furo-extensions.css.map +0 -0
  50. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/docs/_static/styles/furo.css +0 -0
  51. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/docs/_static/styles/furo.css.map +0 -0
  52. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/docs/api/pycosmommf.rst +6 -6
  53. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/docs/api_tab.md +0 -0
  54. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/docs/conf.py +0 -0
  55. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/docs/images/CosmoMMF_Dark.png +0 -0
  56. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/docs/images/CosmoMMF_light.png +0 -0
  57. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/docs/images/final_NEXUSPLUS_Signatures_hydro.png +0 -0
  58. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/docs/images/final_NEXUSPLUS_Signatures_hydro_dark.png +0 -0
  59. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/docs/images/final_tagging_figure.png +0 -0
  60. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/docs/images/final_tagging_figure_dark.png +0 -0
  61. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/docs/index.md +0 -0
  62. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/noxfile.py +0 -0
  63. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/src/pycosmommf/__init__.py +0 -0
  64. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/src/pycosmommf/_version.pyi +0 -0
  65. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/src/pycosmommf/py.typed +0 -0
  66. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/tests/__init__.py +0 -0
  67. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/tests/test_package.py +0 -0
  68. {pycosmommf-0.6.0 → pycosmommf-1.0.1}/tests/test_unit.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pycosmommf
3
- Version: 0.6.0
3
+ Version: 1.0.1
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
@@ -64,6 +64,7 @@ Description-Content-Type: text/markdown
64
64
 
65
65
  [![Actions Status][actions-badge]][actions-link]
66
66
  [![Documentation Status][rtd-badge]][rtd-link]
67
+ [![DOI](https://zenodo.org/badge/770792459.svg)](https://doi.org/10.5281/zenodo.14876495)
67
68
 
68
69
  [![codecov](https://codecov.io/gh/James11222/PyCosmoMMF/graph/badge.svg?token=UCHHHXEV2D)](https://codecov.io/gh/James11222/PyCosmoMMF)
69
70
  [![PyPI version][pypi-version]][pypi-link]
@@ -2,6 +2,7 @@
2
2
 
3
3
  [![Actions Status][actions-badge]][actions-link]
4
4
  [![Documentation Status][rtd-badge]][rtd-link]
5
+ [![DOI](https://zenodo.org/badge/770792459.svg)](https://doi.org/10.5281/zenodo.14876495)
5
6
 
6
7
  [![codecov](https://codecov.io/gh/James11222/PyCosmoMMF/graph/badge.svg?token=UCHHHXEV2D)](https://codecov.io/gh/James11222/PyCosmoMMF)
7
8
  [![PyPI version][pypi-version]][pypi-link]
@@ -46,7 +46,7 @@ density_field = np.load("path/to/density_field.npy")
46
46
  Rs = [sqrt(2) ** n for n in range(10)] # smoothing scales
47
47
 
48
48
  max_signatures = PyCosmoMMF.maximum_signature(
49
- Rs, density_field, alg="NEXUSPLUS"
49
+ Rs, density_field, algorithm="NEXUSPLUS"
50
50
  ) # compute maximum signatures
51
51
  ```
52
52
 
@@ -67,18 +67,13 @@ output of this can be seen below
67
67
 
68
68
  We then have the option of running the tagging scheme a few different ways. The
69
69
  first important argument in `calc_structure_bools()` besides the `density_field`
70
- and the `max_signatures` arrays is the `verbose_flag`. When
71
-
72
- set to `True` the code gives a lot more information and provides a few plots.
73
- This is best turned on when
74
-
75
- debugging the code. When `verbose_flag` is turned on there are 4 additional
76
- outputs to the `calc_structure_bools()`
77
-
78
- function: `S_fil, dM2_fil, S_wall, dM2_wall` which can be used to make the mass
79
- change curves for filaments and
80
-
81
- walls.
70
+ and the `max_signatures` arrays is the `verbose_flag`. When set to `True` the
71
+ code gives a lot more information and provides a few plots. This is best turned
72
+ on when debugging the code. When `verbose_flag` is turned on there is an
73
+ additional output to the `calc_structure_bools()` function: `summary_data` which
74
+ is a dictionary containing all the relevant extra information one might need. It
75
+ can be used to make the mass change curves for filaments and walls. Consult the
76
+ API Docs tab for a detailed summary of what is inside `summary_data`.
82
77
 
83
78
  **_Verbose Flag On:_**
84
79
 
@@ -86,8 +81,8 @@ walls.
86
81
  verbose_flag = True # or False
87
82
 
88
83
 
89
- clusbool, filbool, wallbool, voidbool, S_fil, dM2_fil, S_wall, dM2_wall = (
90
- PyCosmoMMF.calc_structure_bools(density_field, max_signatures, verbose_flag)
84
+ clusbool, filbool, wallbool, voidbool, summary_data = PyCosmoMMF.calc_structure_bools(
85
+ density_field, max_signatures, verbose_flag
91
86
  ) # tag structures
92
87
  ```
93
88
 
@@ -125,11 +120,13 @@ clusbool, filbool, wallbool, voidbool = PyCosmoMMF.calc_structure_bools(
125
120
  ```
126
121
 
127
122
  Another important optional argument in the `calc_structure_bools()` function is
128
- `Δ`. The default value is `Δ = 370` as used in
123
+ `overdensity_threshold`. The default value is `overdensity_threshold = 370` as
124
+ used in
129
125
  [Cautun et al. 2013](https://academic.oup.com/mnras/article/429/2/1286/1038906)
130
- but other values can be 200 or 500 corresponding to `R_200` or `R_500`. `Δ` is
131
- the overdensity parameter, when clusters achieve a density greater than this
132
- value, they are thought to be virialized/collapsed.
126
+ but other values can be 200 or 500 corresponding to `R_200` or `R_500`.
127
+ `overdensity_threshold` is the overdensity parameter Δ, when clusters achieve an
128
+ average overdensity greater than this value, they are thought to be
129
+ virialized/collapsed.
133
130
 
134
131
  The boolean filters for each structure type produced by `calc_structure_bools()`
135
132
  can be used to tag structures within a density field, the results of this can be
@@ -124,6 +124,7 @@ ignore = [
124
124
  "PLR09", # Too many <...>
125
125
  "PLR2004", # Magic value used in comparison
126
126
  "ISC001", # Conflicts with formatter
127
+ "RUF001", # allow usage of `ρ` symbol in docstrings
127
128
  ]
128
129
  isort.required-imports = ["from __future__ import annotations"]
129
130
 
@@ -1,8 +1,13 @@
1
- # file generated by setuptools_scm
1
+ # file generated by setuptools-scm
2
2
  # don't change, don't track in version control
3
+
4
+ __all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
5
+
3
6
  TYPE_CHECKING = False
4
7
  if TYPE_CHECKING:
5
- from typing import Tuple, Union
8
+ from typing import Tuple
9
+ from typing import Union
10
+
6
11
  VERSION_TUPLE = Tuple[Union[int, str], ...]
7
12
  else:
8
13
  VERSION_TUPLE = object
@@ -12,5 +17,5 @@ __version__: str
12
17
  __version_tuple__: VERSION_TUPLE
13
18
  version_tuple: VERSION_TUPLE
14
19
 
15
- __version__ = version = '0.6.0'
16
- __version_tuple__ = version_tuple = (0, 6, 0)
20
+ __version__ = version = '1.0.1'
21
+ __version_tuple__ = version_tuple = (1, 0, 1)
@@ -8,19 +8,19 @@ jit_compiler = nb.njit(parallel=True, fastmath=True)
8
8
 
9
9
  def wavevectors3D(dims, box_size=(2 * np.pi, 2 * np.pi, 2 * np.pi)):
10
10
  """
11
- Returns the wavevectors for a 3D grid of dimensions `dims` and box size `box_size`.
12
-
13
- Parameters
14
- ----------
15
- dims : tuple
16
- The dimensions of the grid.
17
- box_size : tuple
18
- The size of the box in each dimension.
19
-
20
- Returns
21
- -------
22
- kx, ky, kz : tuple
23
- The wavevectors in each dimension.
11
+ Returns the wavevectors for a 3D grid of dimensions ``dims`` and box size ``box_size``.
12
+
13
+ Args:
14
+ dims (:obj:`tuple`):
15
+ The dimensions of the grid ``(nx, ny, nz)``.
16
+ box_size (:obj:`tuple`, optional):
17
+ The size of the box in each dimension ``(Lx, Ly, Lz)``. Defaults to ``(2π, 2π, 2π)``.
18
+
19
+ Returns:
20
+ (tuple): a tuple containing:
21
+ - **kx** (:obj:`np.ndarray`): The wavevector in the x-direction.
22
+ - **ky** (:obj:`np.ndarray`): The wavevector in the y-direction.
23
+ - **kz** (:obj:`np.ndarray`): The wavevector in the z-direction.
24
24
  """
25
25
  sample_rate = 2 * np.pi * (np.array(dims) / box_size)
26
26
  kx = (np.fft.fftfreq(dims[0], 1 / sample_rate[0]) * (2 * np.pi / dims[0])).astype(
@@ -40,17 +40,14 @@ def kspace_gaussian_filter(R_S, kv): # pragma: no cover
40
40
  """
41
41
  create a Gaussian filter in k-space.
42
42
 
43
- Parameters
44
- ----------
45
- R_S : float
46
- The smoothing scale.
47
- kv : tuple
48
- The wavevectors in each dimension.
49
-
50
- Returns
51
- -------
52
- filter_k : array
53
- The filter in k-space.
43
+ Args:
44
+ R_S (:obj:`float`):
45
+ The smoothing scale.
46
+ kv (:obj:`tuple`):
47
+ The wavevectors in each dimension, each a ``1D float np.ndarray``.
48
+
49
+ Returns:
50
+ (:obj:`3D float np.ndarray`): The filter in k-space.
54
51
  """
55
52
  kx, ky, kz = kv
56
53
  nx, ny, nz = len(kx), len(ky), len(kz)
@@ -69,17 +66,14 @@ def kspace_top_hat_filter(R_S, kv): # pragma: no cover
69
66
  """
70
67
  create a top-hat filter in k-space.
71
68
 
72
- Parameters
73
- ----------
74
- R_S : float
75
- The smoothing scale.
76
- kv : tuple
77
- The wavevectors in each dimension.
78
-
79
- Returns
80
- -------
81
- filter_k : array
82
- The filter in k-space.
69
+ Args:
70
+ R_S (:obj:`float`):
71
+ The smoothing scale.
72
+ kv (:obj:`tuple`):
73
+ The wavevectors in each dimension.
74
+
75
+ Returns:
76
+ (:obj:`3D float np.ndarray`): The filter in k-space.
83
77
  """
84
78
 
85
79
  kx, ky, kz = kv
@@ -106,19 +100,16 @@ def smooth_top_hat(f, R_S, kv):
106
100
  """
107
101
  apply a top-hat filter to a field f.
108
102
 
109
- Parameters
110
- ----------
111
- f : array
112
- The field to be smoothed.
113
- R_S : float
114
- The smoothing scale.
115
- kv : tuple
116
- The wavevectors in each dimension.
117
-
118
- Returns
119
- -------
120
- f_Rn : array
121
- The smoothed field.
103
+ Args:
104
+ f (:obj:`3D float np.ndarray`):
105
+ The field to be smoothed.
106
+ R_S (:obj:`float`):
107
+ The smoothing scale in units of voxels.
108
+ kv (:obj:`tuple`):
109
+ The wavevectors in each dimension.
110
+
111
+ Returns:
112
+ (:obj:`3D float np.ndarray`): The smoothed field.
122
113
  """
123
114
  GF = kspace_top_hat_filter(R_S, kv)
124
115
  f_Rn = np.real(np.fft.ifftn(GF * np.fft.fftn(f)))
@@ -130,19 +121,16 @@ def smooth_gauss(f, R_S, kv):
130
121
  """
131
122
  apply a Gaussian filter to a field f.
132
123
 
133
- Parameters
134
- ----------
135
- f : array
136
- The field to be smoothed.
137
- R_S : float
138
- The smoothing scale.
139
- kv : tuple
140
- The wavevectors in each dimension.
141
-
142
- Returns
143
- -------
144
- f_Rn : array
145
- The smoothed field.
124
+ Args:
125
+ f (:obj:`3D float np.ndarray`):
126
+ The field to be smoothed.
127
+ R_S (:obj:`float`):
128
+ The smoothing scale in units of voxels.
129
+ kv (:obj:`tuple`):
130
+ The wavevectors in each dimension.
131
+
132
+ Returns:
133
+ (:obj:`3D float np.ndarray`): The smoothed field.
146
134
  """
147
135
  GF = kspace_gaussian_filter(R_S, kv)
148
136
  f_Rn = np.real(np.fft.ifftn(GF * np.fft.fftn(f)))
@@ -154,19 +142,16 @@ def smooth_loggauss(f, R_S, kv):
154
142
  """
155
143
  apply a Gaussian filter to the log of a field f.
156
144
 
157
- Parameters
158
- ----------
159
- f : array
160
- The field to be smoothed.
161
- R_S : float
162
- The smoothing scale.
163
- kv : tuple
164
- The wavevectors in each dimension.
165
-
166
- Returns
167
- -------
168
- f_Rn : array
169
- The smoothed field.
145
+ Args:
146
+ f (:obj:`3D float np.ndarray`):
147
+ The field to be smoothed.
148
+ R_S (:obj:`float`):
149
+ The smoothing scale in units of voxels.
150
+ kv (:obj:`tuple`):
151
+ The wavevectors in each dimension.
152
+
153
+ Returns:
154
+ (:obj:`3D float np.ndarray`): The smoothed field.
170
155
  """
171
156
  # Get filter in Fourier space
172
157
  GF = kspace_gaussian_filter(R_S, kv)
@@ -10,19 +10,17 @@ def fast_hessian_from_smoothed(f_Rn, R_S, kv):
10
10
  """
11
11
  Compute the hessian matrix of the smoothed field f_Rn.
12
12
 
13
- Parameters
14
- ----------
15
- f_Rn : 3D array
16
- The smoothed field.
17
- R_S : float
18
- The smoothing scale.
19
- kv : 3D array
20
- The wavevectors.
21
-
22
- Returns
23
- -------
24
- hessian : 3D array
25
- The hessian matrix.
13
+ Args:
14
+ f_Rn (:obj:`3D float np.ndarray`):
15
+ The smoothed field in real space.
16
+ R_S (:obj:`float`):
17
+ The smoothing scale in units of voxels.
18
+ kv (:obj:`tuple`):
19
+ The wavevectors in each dimension.
20
+
21
+ Returns:
22
+ (:obj:`4D float np.ndarray`): The hessian matrix of the smoothed field.
23
+ The shape is ``(nx, ny, nz, 6)``, where ``nx, ny, nz`` are the dimensions of ``f_Rn``.
26
24
  """
27
25
 
28
26
  f_Rn_hat = np.fft.fftn(f_Rn)
@@ -14,15 +14,15 @@ def signatures_from_hessian(hessian): # pragma: no cover
14
14
  """
15
15
  Function to calculate the signatures from a given hessian.
16
16
 
17
- Parameters
18
- ----------
19
- hessian : 4D array
20
- The hessian matrix.
21
-
22
- Returns
23
- -------
24
- sigs : 4D array
25
- The signatures.
17
+ Args:
18
+ hessian (:obj:`4D float np.ndarray`):
19
+ The hessian matrix of the smoothed field, with shape ``(nx, ny, nz, 6)``.
20
+
21
+ Returns:
22
+ (:obj:`4D float np.ndarray`): The signatures array with shape ``(nx, ny, nz, 3)``. Each signature corresponds to a different type of structure:
23
+ - ``sigs[..., 0]``: Cluster signature
24
+ - ``sigs[..., 1]``: Filament signature
25
+ - ``sigs[..., 2]``: Wall signature
26
26
  """
27
27
 
28
28
  hsize = hessian.shape[:3]
@@ -78,35 +78,34 @@ def signatures_from_hessian(hessian): # pragma: no cover
78
78
  return sigs
79
79
 
80
80
 
81
- def maximum_signature(Rs, field, alg="NEXUSPLUS", eps=1e-8):
81
+ def maximum_signature(Rs, density_cube, algorithm="NEXUSPLUS", eps=1e-8):
82
82
  """
83
83
  Compute the maximum signatures across all scales Rs.
84
84
 
85
- Parameters
86
- ----------
87
- Rs : array
88
- The scales.
89
- field : 3D array
90
- The field.
91
- alg : string, optional
92
- The algorithm to use. Can be either 'NEXUS' or 'NEXUSPLUS'.
93
- Default is 'NEXUSPLUS'.
94
- eps : float, optional
95
- A small number to avoid division by zero errors and whatnot. Default is 1e-8.
96
-
97
- Returns
98
- -------
99
- sigmax : 4D array
100
- The maximum signatures.
85
+ Args:
86
+ Rs (:obj:`list` of :obj:`float`):
87
+ List of smoothing scales in units of voxels.
88
+ density_cube (:obj:`3D float np.ndarray`):
89
+ The 3D density field to analyze.
90
+ algorithm (:obj:`str`, optional):
91
+ The algorithm to use for smoothing. Can be either "NEXUS" or "NEXUSPLUS". Defaults to "NEXUSPLUS".
92
+ eps (:obj:`float`, optional):
93
+ Small value to avoid division by zero. Defaults to ``1e-8``.
94
+
95
+ Returns:
96
+ (:obj:`4D float np.ndarray`):
97
+ An array of signatures with shape ``(nx, ny, nz, 3)``, where ``nx, ny, nz`` are the dimensions of the input ``density_cube``.
98
+ The last dimension contains the signatures for clusters, filaments, and walls respectively.
101
99
  """
102
- if alg not in ["NEXUS", "NEXUSPLUS"]: # pragma: no cover
103
- msg = "alg must be either 'NEXUS' or 'NEXUSPLUS'"
100
+
101
+ if algorithm not in ["NEXUS", "NEXUSPLUS"]: # pragma: no cover
102
+ msg = "algorithm must be either 'NEXUS' or 'NEXUSPLUS'"
104
103
  raise ValueError(msg)
105
104
 
106
- nx, ny, nz = field.shape
105
+ nx, ny, nz = density_cube.shape
107
106
 
108
107
  # Make sure the field has no 0 values
109
- field = field + eps
108
+ density_cube = density_cube + eps
110
109
 
111
110
  # Calculate wave vectors for our field
112
111
  wave_vecs = wavevectors3D((nx, ny, nz))
@@ -125,10 +124,10 @@ def maximum_signature(Rs, field, alg="NEXUSPLUS", eps=1e-8):
125
124
  )
126
125
 
127
126
  for R in Rs:
128
- if alg == "NEXUS":
129
- f_Rn = smooth_gauss(field, R, wave_vecs)
130
- elif alg == "NEXUSPLUS":
131
- f_Rn = smooth_loggauss(field, R, wave_vecs)
127
+ if algorithm == "NEXUS":
128
+ f_Rn = smooth_gauss(density_cube, R, wave_vecs)
129
+ elif algorithm == "NEXUSPLUS":
130
+ f_Rn = smooth_loggauss(density_cube, R, wave_vecs)
132
131
 
133
132
  H_Rn = fast_hessian_from_smoothed(f_Rn, R, wave_vecs)
134
133
 
@@ -7,30 +7,30 @@ import skimage.measure
7
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
+ def make_the_clusbool(delta, max_sigs, overdensity_threshold): # pragma: no cover
11
11
  """
12
- make_the_clusbool() - Documentation
13
-
14
- A function that creates a Boolean Filter which selects only
12
+ A function that creates a bool Filter which selects only
15
13
  the clusters in a given cosmological δ field. This function accomplishes
16
14
  this by finding a threshold value for 𝒮_cluster by looking at the
17
15
  change in virialization fraction as a function of 𝒮_cluster.
18
16
 
19
- Arguments:
20
- ----------------
21
- delta - [3D Float Array] - delta refers to the δ field.
22
- max_sigs - [4D Float Array] - the maximum signatures array from CosmoMMF.maximum_signature()
23
- verbose - [Boolean] - a flag to allow the function to be more helpful and verbose.
24
- Δ - [Int] - The overdensity parameter threshold for determining virialization. This
25
- parameter comes from a paper by Gunn & Gott on spherical collapse. Commonly
26
- used values that are physically motivated can be 370, 200, or 500 (for R_200 or R_500).
17
+ Args:
18
+ delta (:obj:`3D float np.ndarray`):
19
+ delta refers to the (δ = ρ / <ρ> - 1) field, this is NOT the (δ + 1 = ρ / <ρ>) field.
20
+ max_sigs (:obj:`4D float np.ndarray`):
21
+ the maximum signatures array from the ``maximum_signature()`` function.
22
+ verbose_flag (:obj:`bool`):
23
+ a flag to allow the function to be more helpful and verbose.
24
+ overdensity_threshold (:obj:`float`):
25
+ The overdensity parameter threshold for determining virialization. Commonly used values
26
+ that are physically motivated can be 370, 200, or 500.
27
27
 
28
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.
29
+ (tuple): a tuple containing:
30
+ - **clusbool** (:obj:`3D bool np.ndarray`): a boolean filter that selects only the clusters in a given cosmological δ field.
31
+ - **S_th** (:obj:`float`): the threshold value for 𝒮_cluster.
32
+ - **signature_thresholds** (:obj:`1D float np.ndarray`): an array of threshold values for 𝒮_cluster.
33
+ - **virialized_fractions** (:obj:`1D float np.ndarray`): an array of virialized fractions as a function of 𝒮_cluster.
34
34
  """
35
35
 
36
36
  signature_thresholds = np.geomspace(0.1, 30, 10) # hard coded for now
@@ -65,9 +65,9 @@ def make_the_clusbool(delta, max_sigs, Δ): # pragma: no cover
65
65
  ) # an array where 0 is background and 1, 2, 3, ... are the clumps
66
66
  averaged_overdensity_per_clump = calc_virialized_fraction(clumps)
67
67
 
68
- virialized_fractions[i] = np.sum(averaged_overdensity_per_clump > Δ) / np.max(
69
- clumps
70
- )
68
+ virialized_fractions[i] = np.sum(
69
+ averaged_overdensity_per_clump > overdensity_threshold
70
+ ) / np.max(clumps)
71
71
 
72
72
  S_th = signature_thresholds[np.abs(virialized_fractions - 0.5).argmin()]
73
73
  clusbool = max_sigs[:, :, :, 0] > S_th
@@ -79,17 +79,20 @@ def calc_mass_change(sig_vec, density_vec, Smin, Smax):
79
79
  """
80
80
  Calculate the mass change curve for a given structure type.
81
81
 
82
- Arguments:
83
- -----------
84
- sig_vec - [1D Float Array] - the signature values for a given structure type
85
- density_vec - [1D Float Array] - the density values for a given structure type
86
- Smin - [Float] - the minimum signature value
87
- Smax - [Float] - the maximum signature value
82
+ Args:
83
+ sig_vec (:obj:`1D float np.ndarray`):
84
+ the signature values for a given structure type
85
+ density_vec (:obj:`1D float np.ndarray`):
86
+ the density values for a given structure type
87
+ Smin (:obj:`float`):
88
+ the minimum signature value
89
+ Smax (:obj:`float`):
90
+ the maximum signature value
88
91
 
89
92
  Returns:
90
- -----------
91
- S - [1D Float Array] - the signature values
92
- ΔM_2 - [1D Float Array] - the mass change curve
93
+ (tuple): a tuple containing:
94
+ - **S** (:obj:`1D float np.ndarray`): the signature values
95
+ - **ΔΜ_2** (:obj:`1D float np.ndarray`): the mass change curve
93
96
  """
94
97
  # Initialize our arrays
95
98
  log10S = np.arange(Smin, Smax + 0.1, 0.1)
@@ -111,48 +114,75 @@ def calc_mass_change(sig_vec, density_vec, Smin, Smax):
111
114
 
112
115
 
113
116
  def calc_structure_bools(
114
- density_cube, max_sigs, verbose, clusbool=None, Smin=-3, Smax=2, Δ=370
117
+ density_cube,
118
+ max_sigs,
119
+ verbose_flag,
120
+ clusbool=None,
121
+ Smin=-3,
122
+ Smax=2,
123
+ overdensity_threshold=370,
115
124
  ):
116
125
  """
117
126
  Calculate the boolean filters for clusters, filaments, walls, and voids.
118
127
 
119
- Arguments:
120
- -----------
121
- density_cube - [3D Float Array] - the δ+1 density_cube
122
- max_sigs - [4D Float Array] - the maximum signatures array from CosmoMMF.maximum_signature()
123
- verbose - [Boolean] - a flag to allow the function to be more helpful and verbose.
124
- clusbool - [3D Boolean Array] - the cluster boolean filter
125
- Smin - [Float] - the minimum signature value
126
- Smax - [Float] - the maximum signature value
127
- Δ - [Int] - the overdensity parameter threshold for determining virialization
128
+ Args:
129
+ density_cube (:obj:`3D float np.ndarray`):
130
+ the (δ+1 = ρ/<ρ>) ``density_cube``, this is NOT (δ = ρ/<ρ> - 1).
131
+ max_sigs (:obj:`4D float np.ndarray`):
132
+ the maximum signatures array from CosmoMMF.maximum_signature()
133
+ verbose_flag (:obj:`bool`) :
134
+ a flag to allow the function to be more helpful and verbose.
135
+ clusbool (:obj:`bool np.ndarray`, optional):
136
+ the cluster boolean filter
137
+ Smin (:obj:`float`, optional):
138
+ the minimum signature value
139
+ Smax (:obj:`float`, optional):
140
+ the maximum signature value
141
+ overdensity_threshold (:obj:`int`, optional):
142
+ the overdensity parameter threshold for determining virialization
128
143
 
129
144
  Returns:
130
- -----------
131
- clusbool - [3D Boolean Array] - the cluster boolean filter
132
- filbool - [3D Boolean Array] - the filament boolean filter
133
- wallbool - [3D Boolean Array] - the wall boolean filter
134
- voidbool - [3D Boolean Array] - the void boolean filter
135
-
136
- VERBOSE ONLY:
137
- summary_data - [Dict] - a dictionary containing the following keys:
138
- S_clus - [1D Float Array] - the signature values for clusters
139
- f_vir_clus - [1D Float Array] - the virialization fraction for clusters
140
- S_fil - [1D Float Array] - the signature values for filaments
141
- dM2_fil - [1D Float Array] - the mass change curve for filaments
142
- S_wall - [1D Float Array] - the signature values for walls
143
- dM2_wall - [1D Float Array] - the mass change curve for walls
144
-
145
+ (tuple): a tuple containing:
146
+ - **clusbool** (:obj:`3D bool np.ndarray`): the cluster boolean filter
147
+ - **filbool** (:obj:`3D bool np.ndarray`): the filament boolean filter
148
+ - **wallbool** (:obj:`3D bool np.ndarray`): the wall boolean filter
149
+ - **voidbool** (:obj:`3D bool np.ndarray`): the void boolean filter
150
+ - **summary_data** (:obj:`dict`): a dictionary returned only when ``verbose_flag = True``, it contains the following keys:
151
+ - **S_clus** (:obj:`1D float np.ndarray`): the signature values for clusters
152
+ - **f_vir_clus** (:obj:`1D float np.ndarray`): the virialization fraction for clusters
153
+ - **S_fil** (:obj:`1D float np.ndarray`): the signature values for filaments
154
+ - **dM2_fil** (:obj:`1D float np.ndarray`): the mass change curve for filaments
155
+ - **S_fil_thresh** (:obj:`float`): the threshold value for filaments
156
+ - **S_wall** (:obj:`1D float np.ndarray`): the signature values for walls
157
+ - **dM2_wall** (:obj:`1D float np.ndarray`): the mass change curve for walls
158
+ - **S_wall_thresh** (:obj:`float`): the threshold value for walls
159
+ - **mass_fractions** (:obj:`1D float np.ndarray`): a list of mass fractions for clusters, filaments, walls, and voids
160
+ - **volume_fractions** (:obj:`1D float np.ndarray`): a list of volume fractions for clusters, filaments, walls, and voids
145
161
  """
146
162
 
147
163
  N_cells = np.prod(density_cube.shape)
148
164
 
165
+ # Check if the input density_cube is actually δ
166
+ if np.isclose(np.abs(np.mean(density_cube)), 0, atol=1e-3):
167
+ msg = "make sure that you are not inputing the overdensity field δ = ρ/<ρ> - 1, but rather δ + 1 = ρ/<ρ>."
168
+ raise ValueError(msg)
169
+
170
+ # Check if the input density_cube is actually δ + 1 = ρ/<ρ> or just ρ
171
+ if np.mean(density_cube) > 1.0:
172
+ # If the mean is greater than 1, we assume it is simply ρ, but we want to transform to δ + 1 = ρ/<ρ>
173
+ if verbose_flag: # pragma: no cover
174
+ print(
175
+ "Input density_cube is in density units, transforming to δ + 1 = ρ/<ρ>."
176
+ )
177
+ density_cube /= np.mean(density_cube) # normalize to mean = 1
178
+
149
179
  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
150
- # Step 1. Create Cluster Boolean Filter
180
+ # Step 1. Create Cluster bool Filter
151
181
  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
152
182
 
153
183
  if clusbool is None: # pragma: no cover
154
184
  clusbool, cluster_thresh, S_clus, f_vir_clus = make_the_clusbool(
155
- density_cube - 1.0, max_sigs, Δ
185
+ density_cube - 1.0, max_sigs, overdensity_threshold
156
186
  )
157
187
  else:
158
188
  cluster_thresh = None
@@ -160,7 +190,7 @@ def calc_structure_bools(
160
190
  f_vir_clus = None
161
191
 
162
192
  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
163
- # Step 2. Create Filament Boolean Filter
193
+ # Step 2. Create Filament bool Filter
164
194
  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
165
195
 
166
196
  # we create 3 N^3 arrays with the signature corresponding to each structure
@@ -181,7 +211,7 @@ def calc_structure_bools(
181
211
  filbool = (max_sigs[:, :, :, 1] > filament_thresh) & (~clusbool)
182
212
 
183
213
  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
184
- # Step 3. Create Wall Boolean Filter
214
+ # Step 3. Create Wall bool Filter
185
215
  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
186
216
 
187
217
  # Isolate Valid Walls (not clusters or filaments)
@@ -196,14 +226,14 @@ def calc_structure_bools(
196
226
  wallbool = (max_sigs[:, :, :, 2] > wall_thresh) & ~(filbool | clusbool)
197
227
 
198
228
  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
199
- # Step 4. Create Void Boolean Filter
229
+ # Step 4. Create Void bool Filter
200
230
  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
201
231
 
202
232
  voidbool = (clusbool + filbool + wallbool) == 0
203
233
 
204
- if verbose:
234
+ if verbose_flag:
205
235
  print("---------------------------------------")
206
- print(" Cluster Boolean Filter Report ")
236
+ print(" Cluster bool Filter Report ")
207
237
  print("---------------------------------------\n")
208
238
 
209
239
  volume_fraction_clus = np.sum(clusbool) / N_cells
@@ -215,7 +245,7 @@ def calc_structure_bools(
215
245
  print("")
216
246
 
217
247
  print("---------------------------------------")
218
- print(" Filament Boolean Filter Report ")
248
+ print(" Filament bool Filter Report ")
219
249
  print("---------------------------------------\n")
220
250
 
221
251
  volume_fraction_fil = np.sum(filbool) / N_cells
@@ -227,7 +257,7 @@ def calc_structure_bools(
227
257
  print("")
228
258
 
229
259
  print("---------------------------------------")
230
- print(" Wall Boolean Filter Report ")
260
+ print(" Wall bool Filter Report ")
231
261
  print("---------------------------------------\n")
232
262
 
233
263
  volume_fraction_wall = np.sum(wallbool) / N_cells
@@ -239,7 +269,7 @@ def calc_structure_bools(
239
269
  print("")
240
270
 
241
271
  print("---------------------------------------")
242
- print(" Void Boolean Filter Report ")
272
+ print(" Void bool Filter Report ")
243
273
  print("---------------------------------------\n")
244
274
 
245
275
  volume_fraction_void = np.sum(voidbool) / N_cells
@@ -6,6 +6,15 @@ import numpy as np
6
6
  def shrink(data, new_size):
7
7
  """
8
8
  A simple function to shrink a 3D array by summing over blocks of new_size.
9
+
10
+ Args:
11
+ data (:obj:`3D float np.ndarray`):
12
+ The input 3D array to be shrunk.
13
+ new_size (:obj:`int`):
14
+ The new size for each dimension after shrinking.
15
+
16
+ Returns:
17
+ (:obj:`3D float np.ndarray`): The shrunk 3D array.
9
18
  """
10
19
  xs = new_size
11
20
  ys = new_size
@@ -23,20 +32,36 @@ def shrink(data, new_size):
23
32
  def wall(n):
24
33
  """
25
34
  Builds a wall out of an nxnxn array. All values in the wall
26
- are set to 1. Other points are set to 0.
35
+ are set to 10. Other points are set to 0.
36
+
37
+ Args:
38
+ n (:obj:`int`):
39
+ The size of the 3D array to create.
40
+
41
+ Returns:
42
+ (:obj:`3D float np.ndarray`): An nxnxn array with a wall in the center.
27
43
  """
28
44
  array = np.zeros((n, n, n))
29
45
  array[:, :, :] = 0.1
30
46
  index = n // 2
31
- array[index, :, :] = 1
47
+ array[index, :, :] = 10.0
32
48
  return array
33
49
 
34
50
 
35
51
  def cylinder(n, r):
36
52
  """
37
53
  Returns an nxnxn array with a cylinder in the center with
38
- radius r. All points within the radius are set to 1, the rest
54
+ radius r. All points within the radius are set to 10, the rest
39
55
  are set to 0.
56
+
57
+ Args:
58
+ n (:obj:`int`):
59
+ The size of the 3D array to create.
60
+ r (:obj:`int`):
61
+ The radius of the cylinder.
62
+
63
+ Returns:
64
+ (:obj:`3D float np.ndarray`): An nxnxn array with a cylinder in the center.
40
65
  """
41
66
  array = np.zeros((n, n, n), dtype=np.float64)
42
67
  array[:, :, :] = 0.1
@@ -44,15 +69,24 @@ def cylinder(n, r):
44
69
  for y in range(n):
45
70
  for z in range(n):
46
71
  if (x - (n // 2)) ** 2 + (y - (n // 2)) ** 2 < r**2:
47
- array[x, y, z] = 1
72
+ array[x, y, z] = 10.0
48
73
  return array
49
74
 
50
75
 
51
76
  def sphere(n, r):
52
77
  """
53
78
  Returns an nxnxn array with a sphere in the center with
54
- radius r. All points within the radius are set to 1, the rest
79
+ radius r. All points within the radius are set to 10, the rest
55
80
  are set to 0.
81
+
82
+ Args:
83
+ n (:obj:`int`):
84
+ The size of the 3D array to create.
85
+ r (:obj:`int`):
86
+ The radius of the sphere.
87
+
88
+ Returns:
89
+ (:obj:`3D float np.ndarray`): An nxnxn array with a sphere in the center.
56
90
  """
57
91
  array = np.zeros((n, n, n), dtype=np.float64)
58
92
  array[:, :, :] = 0.1
@@ -62,5 +96,5 @@ def sphere(n, r):
62
96
  if (x - (n // 2)) ** 2 + (y - (n // 2)) ** 2 + (
63
97
  z - (n // 2)
64
98
  ) ** 2 < r**2:
65
- array[x, y, z] = 1
99
+ array[x, y, z] = 10.0
66
100
  return array
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import numpy as np
4
+ import pytest
4
5
 
5
6
  import pycosmommf as m
6
7
 
@@ -15,28 +16,28 @@ test_field = (
15
16
  def test_maximum_signature():
16
17
  Rs = [np.sqrt(2) ** n for n in range(5)]
17
18
  field = test_field
18
- sigs = m.maximum_signature(Rs, field, alg="NEXUSPLUS")
19
+ sigs = m.maximum_signature(Rs, field, algorithm="NEXUSPLUS")
19
20
  assert sigs.shape == (32, 32, 32, 3)
20
21
 
21
- sigs = m.maximum_signature(Rs, field, alg="NEXUS")
22
+ sigs = m.maximum_signature(Rs, field, algorithm="NEXUS")
22
23
  assert sigs.shape == (32, 32, 32, 3)
23
24
 
24
25
 
25
26
  def test_calc_structure_bools():
26
27
  Rs = [np.sqrt(2) ** n for n in range(5)]
27
28
  field = test_field
28
- sigs = m.maximum_signature(Rs, field, alg="NEXUS")
29
+ sigs = m.maximum_signature(Rs, field, algorithm="NEXUS")
29
30
  clusbool = (
30
31
  np.roll(m.sphere(32, 2), -10, axis=0) + np.roll(m.sphere(32, 2), 10, axis=0)
31
32
  ) >= 1
32
33
  clusbool, filbool, wallbool, voidbool, summary_stats = m.calc_structure_bools(
33
34
  field,
34
35
  max_sigs=sigs,
35
- verbose=True,
36
+ verbose_flag=True,
36
37
  clusbool=clusbool,
37
38
  Smin=-3,
38
39
  Smax=2,
39
- Δ=370,
40
+ overdensity_threshold=370,
40
41
  )
41
42
  assert clusbool.shape == (32, 32, 32)
42
43
  assert filbool.shape == (32, 32, 32)
@@ -47,13 +48,26 @@ def test_calc_structure_bools():
47
48
  clusbool, filbool, wallbool, voidbool = m.calc_structure_bools(
48
49
  field,
49
50
  max_sigs=sigs,
50
- verbose=False,
51
+ verbose_flag=False,
51
52
  clusbool=clusbool,
52
53
  Smin=-3,
53
54
  Smax=2,
54
- Δ=370,
55
+ overdensity_threshold=370,
55
56
  )
56
57
  assert clusbool.shape == (32, 32, 32)
57
58
  assert filbool.shape == (32, 32, 32)
58
59
  assert wallbool.shape == (32, 32, 32)
59
60
  assert voidbool.shape == (32, 32, 32)
61
+
62
+ delta = field / np.mean(field) - 1.0
63
+
64
+ with pytest.raises(ValueError): # noqa: PT011
65
+ clusbool, filbool, wallbool, voidbool = m.calc_structure_bools(
66
+ density_cube=delta,
67
+ max_sigs=sigs,
68
+ verbose_flag=False,
69
+ clusbool=clusbool,
70
+ Smin=-3,
71
+ Smax=2,
72
+ overdensity_threshold=370,
73
+ )
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -3,8 +3,8 @@ pycosmommf package
3
3
 
4
4
  .. automodule:: pycosmommf
5
5
  :members:
6
- :undoc-members:
7
6
  :show-inheritance:
7
+ :undoc-members:
8
8
 
9
9
  Submodules
10
10
  ----------
@@ -14,37 +14,37 @@ pycosmommf.filter module
14
14
 
15
15
  .. automodule:: pycosmommf.filter
16
16
  :members:
17
- :undoc-members:
18
17
  :show-inheritance:
18
+ :undoc-members:
19
19
 
20
20
  pycosmommf.hessian module
21
21
  -------------------------
22
22
 
23
23
  .. automodule:: pycosmommf.hessian
24
24
  :members:
25
- :undoc-members:
26
25
  :show-inheritance:
26
+ :undoc-members:
27
27
 
28
28
  pycosmommf.signatures module
29
29
  ----------------------------
30
30
 
31
31
  .. automodule:: pycosmommf.signatures
32
32
  :members:
33
- :undoc-members:
34
33
  :show-inheritance:
34
+ :undoc-members:
35
35
 
36
36
  pycosmommf.tagging module
37
37
  -------------------------
38
38
 
39
39
  .. automodule:: pycosmommf.tagging
40
40
  :members:
41
- :undoc-members:
42
41
  :show-inheritance:
42
+ :undoc-members:
43
43
 
44
44
  pycosmommf.utils module
45
45
  -----------------------
46
46
 
47
47
  .. automodule:: pycosmommf.utils
48
48
  :members:
49
- :undoc-members:
50
49
  :show-inheritance:
50
+ :undoc-members:
File without changes
File without changes
File without changes
File without changes
File without changes