py-lfkit 0.1.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lfkit/__init__.py +19 -0
- lfkit/_version.py +24 -0
- lfkit/api/__init__.py +0 -0
- lfkit/api/corrections.py +308 -0
- lfkit/api/lumfunc.py +914 -0
- lfkit/corrections/__init__.py +0 -0
- lfkit/corrections/color_anchors.py +176 -0
- lfkit/corrections/filters.py +185 -0
- lfkit/corrections/kcorrect_backend.py +149 -0
- lfkit/corrections/kcorrect_from_color.py +111 -0
- lfkit/corrections/kcorrect_grids.py +242 -0
- lfkit/corrections/poggianti1997.py +386 -0
- lfkit/corrections/responses.py +183 -0
- lfkit/cosmo/__init__.py +0 -0
- lfkit/cosmo/cosmology.py +211 -0
- lfkit/data/demo_catalogs/fake_magnitude_limited_catalog.csv +201 -0
- lfkit/data/kcorrect/grids/kcorrect__bessell__z0.0000_4.0__Nz801__bsnone.npz +0 -0
- lfkit/data/kcorrect/grids/kcorrect__decam__z0.0000_4.0__Nz801__bsnone.npz +0 -0
- lfkit/data/kcorrect/grids/kcorrect__sdss__z0.0000_4.0__Nz801__bsnone.npz +0 -0
- lfkit/data/kcorrect/grids/kcorrect__subaru_suprimecam__z0.0000_4.0__Nz801__bsnone.npz +0 -0
- lfkit/data/poggianti1997/__init__.py +0 -0
- lfkit/data/poggianti1997/ecorr.csv +603 -0
- lfkit/data/poggianti1997/filters.csv +516 -0
- lfkit/data/poggianti1997/kcorr.csv +490 -0
- lfkit/data/poggianti1997/kcorrv.csv +37 -0
- lfkit/data/poggianti1997/sed.csv +295 -0
- lfkit/photometry/__init__.py +0 -0
- lfkit/photometry/catalog_completeness.py +381 -0
- lfkit/photometry/lf_integrals.py +500 -0
- lfkit/photometry/lf_parameter_models.py +386 -0
- lfkit/photometry/lf_redshift_density.py +238 -0
- lfkit/photometry/luminosities.py +426 -0
- lfkit/photometry/luminosity_function.py +707 -0
- lfkit/photometry/magnitudes.py +214 -0
- lfkit/utils/__init__.py +0 -0
- lfkit/utils/download_poggianti97_data.py +70 -0
- lfkit/utils/evaluators.py +104 -0
- lfkit/utils/interpolation.py +216 -0
- lfkit/utils/io.py +240 -0
- lfkit/utils/types.py +27 -0
- lfkit/utils/units.py +160 -0
- lfkit/utils/validators.py +63 -0
- py_lfkit-0.1.4.dist-info/METADATA +94 -0
- py_lfkit-0.1.4.dist-info/RECORD +47 -0
- py_lfkit-0.1.4.dist-info/WHEEL +5 -0
- py_lfkit-0.1.4.dist-info/licenses/LICENSE +21 -0
- py_lfkit-0.1.4.dist-info/top_level.txt +1 -0
lfkit/__init__.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Top-level package for LFKit."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from lfkit.api.corrections import Corrections
|
|
6
|
+
from lfkit.api.lumfunc import LuminosityFunction
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
from lfkit._version import version as __version__
|
|
10
|
+
except ImportError:
|
|
11
|
+
__version__ = "unknown"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"Corrections",
|
|
16
|
+
"LuminosityFunction",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
__author__ = """Niko Sarcevic and collaborators."""
|
lfkit/_version.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# file generated by vcs-versioning
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"__version__",
|
|
7
|
+
"__version_tuple__",
|
|
8
|
+
"version",
|
|
9
|
+
"version_tuple",
|
|
10
|
+
"__commit_id__",
|
|
11
|
+
"commit_id",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
version: str
|
|
15
|
+
__version__: str
|
|
16
|
+
__version_tuple__: tuple[int | str, ...]
|
|
17
|
+
version_tuple: tuple[int | str, ...]
|
|
18
|
+
commit_id: str | None
|
|
19
|
+
__commit_id__: str | None
|
|
20
|
+
|
|
21
|
+
__version__ = version = '0.1.4'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 1, 4)
|
|
23
|
+
|
|
24
|
+
__commit_id__ = commit_id = None
|
lfkit/api/__init__.py
ADDED
|
File without changes
|
lfkit/api/corrections.py
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Unified k- and e-correction interface.
|
|
3
|
+
|
|
4
|
+
This module is the user-facing entry point for LFKit photometric corrections.
|
|
5
|
+
It provides a small, stable API for evaluating:
|
|
6
|
+
|
|
7
|
+
k(z) bandpass (k-) correction
|
|
8
|
+
e(z) luminosity evolution (e-) correction
|
|
9
|
+
ke(z) combined correction, ke(z) = k(z) + e(z)
|
|
10
|
+
|
|
11
|
+
Design goals
|
|
12
|
+
------------
|
|
13
|
+
- Keep the runtime API minimal and stable: k(z), e(z), ke(z)
|
|
14
|
+
- Make backend choices explicit and composable:
|
|
15
|
+
- k(z) backend: "poggianti" or "kcorrect"
|
|
16
|
+
- e(z) backend: "none" or "poggianti"
|
|
17
|
+
- Use astronomy-friendly inputs at the public boundary:
|
|
18
|
+
- filterset: "sdss", "hsc", "decam", "bessell", ...
|
|
19
|
+
- band: "r", "i", "V", ...
|
|
20
|
+
- Poggianti uses a typed table key (gal_type)
|
|
21
|
+
- kcorrect uses an SED mixture specified by a color anchor
|
|
22
|
+
|
|
23
|
+
Reality check about kcorrect
|
|
24
|
+
----------------------------
|
|
25
|
+
kcorrect returns k(z) for a chosen SED mixture and filter response. It does not
|
|
26
|
+
encode physical luminosity evolution by itself. If you want e(z), you supply a
|
|
27
|
+
separate evolution model (for example from Poggianti tabulations), and LFKit
|
|
28
|
+
combines them consistently.
|
|
29
|
+
|
|
30
|
+
Sign convention
|
|
31
|
+
---------------
|
|
32
|
+
Absolute magnitude is defined as:
|
|
33
|
+
|
|
34
|
+
M = m - DM(z) - k(z) + e(z)
|
|
35
|
+
|
|
36
|
+
With this convention, if galaxies were brighter in the past, then e(z) is
|
|
37
|
+
typically negative for z > z_piv in pivoted evolution models.
|
|
38
|
+
|
|
39
|
+
Notes on filter names and kcorrect responses
|
|
40
|
+
--------------------------------------------
|
|
41
|
+
In LFKit the public choice of a band is expressed as (filterset, band). kcorrect
|
|
42
|
+
internally uses response curve identifiers (file stems). LFKit maps:
|
|
43
|
+
|
|
44
|
+
(filterset, band) -> response_name
|
|
45
|
+
|
|
46
|
+
You can override or extend this mapping via ``response_map`` when working with
|
|
47
|
+
custom surveys or custom filter curves.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
from __future__ import annotations
|
|
51
|
+
|
|
52
|
+
from pathlib import Path
|
|
53
|
+
from typing import Callable
|
|
54
|
+
|
|
55
|
+
import numpy as np
|
|
56
|
+
|
|
57
|
+
from lfkit.utils.interpolation import build_1d_interpolator, as_1d_finite_grid
|
|
58
|
+
import lfkit.corrections.poggianti1997 as pogg
|
|
59
|
+
from lfkit.corrections.filters import DEFAULT_RESPONSE_MAP
|
|
60
|
+
|
|
61
|
+
ArrayLike = object # anything np.asarray can handle
|
|
62
|
+
|
|
63
|
+
# Default mapping: (filterset, band) -> kcorrect response curve name.
|
|
64
|
+
DEFAULT_KCORRECT_RESPONSE_MAP = DEFAULT_RESPONSE_MAP
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class Corrections:
|
|
68
|
+
"""Evaluator for k(z), e(z), and ke(z).
|
|
69
|
+
|
|
70
|
+
This object wraps callables representing the k-correction and the
|
|
71
|
+
luminosity evolution correction and provides a consistent interface
|
|
72
|
+
for evaluating:
|
|
73
|
+
|
|
74
|
+
k(z)
|
|
75
|
+
e(z)
|
|
76
|
+
ke(z) = k(z) + e(z)
|
|
77
|
+
|
|
78
|
+
Instances are typically created through the class constructors
|
|
79
|
+
``Corrections.poggianti`` or ``Corrections.kcorrect``.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
k_func: Callable returning k(z).
|
|
83
|
+
e_func: Optional callable returning e(z). If None, e(z)=0.
|
|
84
|
+
meta: Optional metadata dictionary describing the backend
|
|
85
|
+
configuration.
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
def __init__(
|
|
89
|
+
self,
|
|
90
|
+
k_func: Callable[[ArrayLike], ArrayLike],
|
|
91
|
+
e_func: Callable[[ArrayLike], ArrayLike] | None = None,
|
|
92
|
+
*,
|
|
93
|
+
meta: dict[str, object] | None = None,
|
|
94
|
+
) -> None:
|
|
95
|
+
self._k = k_func
|
|
96
|
+
self._e = e_func
|
|
97
|
+
self.meta: dict[str, object] = {} if meta is None else dict(meta)
|
|
98
|
+
|
|
99
|
+
def k(self, z: ArrayLike) -> np.ndarray:
|
|
100
|
+
"""Evaluate the k-correction.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
z: Scalar or array-like redshift.
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
NumPy array containing k(z).
|
|
107
|
+
"""
|
|
108
|
+
return np.asarray(self._k(z), float)
|
|
109
|
+
|
|
110
|
+
def e(self, z: ArrayLike) -> np.ndarray:
|
|
111
|
+
"""Evaluate the luminosity evolution correction.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
z: Scalar or array-like redshift.
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
NumPy array containing e(z). If no evolution model is attached,
|
|
118
|
+
zeros are returned.
|
|
119
|
+
"""
|
|
120
|
+
if self._e is None:
|
|
121
|
+
return np.zeros_like(np.asarray(z, float))
|
|
122
|
+
return np.asarray(self._e(z), float)
|
|
123
|
+
|
|
124
|
+
def ke(self, z: ArrayLike) -> np.ndarray:
|
|
125
|
+
"""Evaluate the combined correction ke(z).
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
z: Scalar or array-like redshift.
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
NumPy array containing ke(z) = k(z) + e(z).
|
|
132
|
+
"""
|
|
133
|
+
z_arr = np.asarray(z, float)
|
|
134
|
+
return self.k(z_arr) + self.e(z_arr)
|
|
135
|
+
|
|
136
|
+
@classmethod
|
|
137
|
+
def poggianti(
|
|
138
|
+
cls,
|
|
139
|
+
*,
|
|
140
|
+
band: str,
|
|
141
|
+
gal_type: str,
|
|
142
|
+
cosmo=None,
|
|
143
|
+
original_z_for_e: bool = True,
|
|
144
|
+
method: str = "pchip",
|
|
145
|
+
extrapolate: bool = True,
|
|
146
|
+
e_model: str = "poggianti",
|
|
147
|
+
) -> "Corrections":
|
|
148
|
+
"""Construct corrections from Poggianti (1997) tabulated models.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
band: Poggianti band identifier (e.g. "V", "B").
|
|
152
|
+
gal_type: Galaxy spectral type in the Poggianti tables
|
|
153
|
+
(e.g. "E", "Sc").
|
|
154
|
+
cosmo: Optional cosmology object used for lookback-time
|
|
155
|
+
calculations in the evolution correction.
|
|
156
|
+
original_z_for_e: Whether the evolution correction is evaluated
|
|
157
|
+
on the original Poggianti redshift grid.
|
|
158
|
+
method: Interpolation method for the tabulated curves.
|
|
159
|
+
extrapolate: Whether to allow extrapolation outside the
|
|
160
|
+
tabulated redshift range.
|
|
161
|
+
e_model: Evolution backend ("poggianti" or "none").
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
Corrections instance capable of evaluating k(z), e(z), and ke(z).
|
|
165
|
+
|
|
166
|
+
Raises:
|
|
167
|
+
ValueError: If an unsupported evolution model is requested.
|
|
168
|
+
"""
|
|
169
|
+
z_k, kcorr, z_e, ecorr = pogg.load_poggianti1997_tables(band=band, sed=gal_type)
|
|
170
|
+
|
|
171
|
+
k_func = pogg.make_kcorr_interpolator(
|
|
172
|
+
z_k, kcorr, method=method, extrapolate=extrapolate
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
e_func: Callable[[ArrayLike], np.ndarray] | None
|
|
176
|
+
if str(e_model).lower() == "none":
|
|
177
|
+
e_func = None
|
|
178
|
+
elif str(e_model).lower() == "poggianti":
|
|
179
|
+
e_func = pogg.make_ecorr_interpolator(
|
|
180
|
+
z_e,
|
|
181
|
+
ecorr,
|
|
182
|
+
original_z=original_z_for_e,
|
|
183
|
+
cosmo=cosmo,
|
|
184
|
+
method=method,
|
|
185
|
+
extrapolate=extrapolate,
|
|
186
|
+
)
|
|
187
|
+
else:
|
|
188
|
+
raise ValueError(
|
|
189
|
+
"e_model must be 'none' or 'poggianti' for Corrections.poggianti()."
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
out = cls(k_func=k_func, e_func=e_func)
|
|
193
|
+
out.meta.update(
|
|
194
|
+
{
|
|
195
|
+
"k_backend": "poggianti1997",
|
|
196
|
+
"e_backend": str(e_model).lower(),
|
|
197
|
+
"band": str(band),
|
|
198
|
+
"gal_type": str(gal_type),
|
|
199
|
+
}
|
|
200
|
+
)
|
|
201
|
+
return out
|
|
202
|
+
|
|
203
|
+
@classmethod
|
|
204
|
+
def kcorrect(
|
|
205
|
+
cls,
|
|
206
|
+
*,
|
|
207
|
+
z_grid: ArrayLike | None = None,
|
|
208
|
+
response_out: str,
|
|
209
|
+
color: tuple[str, str],
|
|
210
|
+
color_value: float,
|
|
211
|
+
z_phot: float = 0.0,
|
|
212
|
+
anchor_band: str | None = None,
|
|
213
|
+
anchor_mag: float = 22.0,
|
|
214
|
+
band_shift: float | None = None,
|
|
215
|
+
response_dir: str | Path | None = None,
|
|
216
|
+
redshift_range: tuple[float, float] = (0.0, 2.0),
|
|
217
|
+
nredshift: int = 4000,
|
|
218
|
+
ivar_level: float = 1e10,
|
|
219
|
+
anchor_z0: bool = True,
|
|
220
|
+
method: str = "pchip",
|
|
221
|
+
extrapolate: bool = True,
|
|
222
|
+
) -> "Corrections":
|
|
223
|
+
"""Construct k(z) using the kcorrect SED template model.
|
|
224
|
+
|
|
225
|
+
The spectral energy distribution (SED) is constrained by a two-band
|
|
226
|
+
color. The color fixes a flux ratio between two bands, which
|
|
227
|
+
determines a mixture of kcorrect templates consistent with
|
|
228
|
+
that constraint. The resulting template mixture is then used
|
|
229
|
+
to evaluate k(z) for the requested output response.
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
z_grid: Redshift grid used to compute the k(z) curve. If None,
|
|
233
|
+
a default grid spanning ``redshift_range`` is used.
|
|
234
|
+
response_out: kcorrect response name for which k(z) is evaluated.
|
|
235
|
+
color: Two-band color constraint (band_a, band_b).
|
|
236
|
+
color_value: Target color value in magnitudes.
|
|
237
|
+
z_phot: Redshift at which the color constraint is applied.
|
|
238
|
+
anchor_band: Band used to set the arbitrary flux normalization.
|
|
239
|
+
anchor_mag: Magnitude used to set the arbitrary flux normalization.
|
|
240
|
+
band_shift: Optional kcorrect band shift parameter.
|
|
241
|
+
response_dir: Directory containing custom response curves.
|
|
242
|
+
redshift_range: Internal redshift range used by kcorrect.
|
|
243
|
+
nredshift: Internal redshift grid size used by kcorrect.
|
|
244
|
+
ivar_level: Weight assigned to constrained photometric bands.
|
|
245
|
+
anchor_z0: Whether to normalize so that k(z=0)=0.
|
|
246
|
+
method: Interpolation method used to construct k(z).
|
|
247
|
+
extrapolate: Whether to allow extrapolation outside the
|
|
248
|
+
computed redshift range.
|
|
249
|
+
|
|
250
|
+
Returns:
|
|
251
|
+
Corrections instance evaluating k(z). In this configuration
|
|
252
|
+
e(z) = 0.
|
|
253
|
+
"""
|
|
254
|
+
from lfkit.corrections.kcorrect_from_color import kcorrect_from_bandcolor
|
|
255
|
+
|
|
256
|
+
if z_grid is None:
|
|
257
|
+
z_arr = np.linspace(redshift_range[0], redshift_range[1], 4001)
|
|
258
|
+
else:
|
|
259
|
+
z_arr = as_1d_finite_grid(z_grid, name="z_grid")
|
|
260
|
+
|
|
261
|
+
z_ok, k_ok = kcorrect_from_bandcolor(
|
|
262
|
+
z=np.asarray(z_arr, float),
|
|
263
|
+
response_out=str(response_out),
|
|
264
|
+
color=(str(color[0]), str(color[1])),
|
|
265
|
+
color_value=float(color_value),
|
|
266
|
+
z_phot=float(z_phot),
|
|
267
|
+
anchor_band=None if anchor_band is None else str(anchor_band),
|
|
268
|
+
anchor_mag=float(anchor_mag),
|
|
269
|
+
band_shift=band_shift,
|
|
270
|
+
response_dir=response_dir,
|
|
271
|
+
redshift_range=(float(redshift_range[0]), float(redshift_range[1])),
|
|
272
|
+
nredshift=int(nredshift),
|
|
273
|
+
ivar_level=float(ivar_level),
|
|
274
|
+
anchor_z0=bool(anchor_z0),
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
k_func = build_1d_interpolator(
|
|
278
|
+
np.asarray(z_ok, float),
|
|
279
|
+
np.asarray(k_ok, float),
|
|
280
|
+
method=str(method),
|
|
281
|
+
extrapolate=bool(extrapolate),
|
|
282
|
+
extrap_mode="linear_tail",
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
out = cls(k_func=k_func, e_func=None)
|
|
286
|
+
out.meta.update(
|
|
287
|
+
{
|
|
288
|
+
"k_backend": "kcorrect_bandcolor",
|
|
289
|
+
"response_out": str(response_out),
|
|
290
|
+
"color": (str(color[0]), str(color[1])),
|
|
291
|
+
"color_value": float(color_value),
|
|
292
|
+
"z_phot": float(z_phot),
|
|
293
|
+
"anchor_band": None if anchor_band is None else str(anchor_band),
|
|
294
|
+
"anchor_mag": float(anchor_mag),
|
|
295
|
+
"band_shift": band_shift,
|
|
296
|
+
"response_dir": str(response_dir) if response_dir is not None else None,
|
|
297
|
+
"redshift_range": (float(redshift_range[0]), float(redshift_range[1])),
|
|
298
|
+
"nredshift": int(nredshift),
|
|
299
|
+
"ivar_level": float(ivar_level),
|
|
300
|
+
"anchor_z0": bool(anchor_z0),
|
|
301
|
+
"method": str(method),
|
|
302
|
+
"extrapolate": bool(extrapolate),
|
|
303
|
+
"z_valid_min": float(np.asarray(z_ok, float)[0]),
|
|
304
|
+
"z_valid_max": float(np.asarray(z_ok, float)[-1]),
|
|
305
|
+
"n_finite": int(np.asarray(z_ok).size),
|
|
306
|
+
}
|
|
307
|
+
)
|
|
308
|
+
return out
|