splusdata 5.49__tar.gz → 5.51__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.
- {splusdata-5.49/splusdata.egg-info → splusdata-5.51}/PKG-INFO +2 -2
- {splusdata-5.49 → splusdata-5.51}/pyproject.toml +2 -2
- splusdata-5.51/splusdata/features/zeropoints/zp_map.py +271 -0
- {splusdata-5.49 → splusdata-5.51/splusdata.egg-info}/PKG-INFO +2 -2
- {splusdata-5.49 → splusdata-5.51}/splusdata.egg-info/requires.txt +1 -1
- splusdata-5.49/splusdata/features/zeropoints/zp_map.py +0 -145
- {splusdata-5.49 → splusdata-5.51}/LICENSE +0 -0
- {splusdata-5.49 → splusdata-5.51}/README.md +0 -0
- {splusdata-5.49 → splusdata-5.51}/setup.cfg +0 -0
- {splusdata-5.49 → splusdata-5.51}/splusdata/__init__.py +0 -0
- {splusdata-5.49 → splusdata-5.51}/splusdata/connect.py +0 -0
- {splusdata-5.49 → splusdata-5.51}/splusdata/core.py +0 -0
- {splusdata-5.49 → splusdata-5.51}/splusdata/features/__init__.py +0 -0
- {splusdata-5.49 → splusdata-5.51}/splusdata/features/extinction.py +0 -0
- {splusdata-5.49 → splusdata-5.51}/splusdata/features/filterbw.py +0 -0
- {splusdata-5.49 → splusdata-5.51}/splusdata/features/find_pointings.py +0 -0
- {splusdata-5.49 → splusdata-5.51}/splusdata/features/hipscat.py +0 -0
- {splusdata-5.49 → splusdata-5.51}/splusdata/features/io.py +0 -0
- {splusdata-5.49 → splusdata-5.51}/splusdata/features/zeropoints/__init__.py +0 -0
- {splusdata-5.49 → splusdata-5.51}/splusdata/features/zeropoints/zp_image.py +0 -0
- {splusdata-5.49 → splusdata-5.51}/splusdata/features/zeropointsdr4.py +0 -0
- {splusdata-5.49 → splusdata-5.51}/splusdata/models/__init__.py +0 -0
- {splusdata-5.49 → splusdata-5.51}/splusdata/models/star_gal_quasar.py +0 -0
- {splusdata-5.49 → splusdata-5.51}/splusdata/readconf.py +0 -0
- {splusdata-5.49 → splusdata-5.51}/splusdata/scripts/args.py +0 -0
- {splusdata-5.49 → splusdata-5.51}/splusdata/scripts/utils.py +0 -0
- {splusdata-5.49 → splusdata-5.51}/splusdata/scubes/__init__.py +0 -0
- {splusdata-5.49 → splusdata-5.51}/splusdata/scubes/core.py +0 -0
- {splusdata-5.49 → splusdata-5.51}/splusdata/scubes/read.py +0 -0
- {splusdata-5.49 → splusdata-5.51}/splusdata/scubes/scripts.py +0 -0
- {splusdata-5.49 → splusdata-5.51}/splusdata/vacs/__init__.py +0 -0
- {splusdata-5.49 → splusdata-5.51}/splusdata/vacs/pdfs.py +0 -0
- {splusdata-5.49 → splusdata-5.51}/splusdata/vacs/sqg.py +0 -0
- {splusdata-5.49 → splusdata-5.51}/splusdata/variability/__init__.py +0 -0
- {splusdata-5.49 → splusdata-5.51}/splusdata/vars.py +0 -0
- {splusdata-5.49 → splusdata-5.51}/splusdata.egg-info/SOURCES.txt +0 -0
- {splusdata-5.49 → splusdata-5.51}/splusdata.egg-info/dependency_links.txt +0 -0
- {splusdata-5.49 → splusdata-5.51}/splusdata.egg-info/entry_points.txt +0 -0
- {splusdata-5.49 → splusdata-5.51}/splusdata.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: splusdata
|
|
3
|
-
Version: 5.
|
|
3
|
+
Version: 5.51
|
|
4
4
|
Summary: Download SPLUS catalogs, FITS and more
|
|
5
5
|
Author-email: Gustavo Schwarz <gustavo.b.schwarz@gmail.com>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -10,7 +10,7 @@ Classifier: License :: OSI Approved :: Apache Software License
|
|
|
10
10
|
Requires-Python: >=3.7
|
|
11
11
|
Description-Content-Type: text/markdown
|
|
12
12
|
License-File: LICENSE
|
|
13
|
-
Requires-Dist: adss>=1.
|
|
13
|
+
Requires-Dist: adss>=1.38
|
|
14
14
|
Requires-Dist: requests
|
|
15
15
|
Requires-Dist: astropy
|
|
16
16
|
Requires-Dist: astroquery
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "splusdata"
|
|
7
|
-
version = "5.
|
|
7
|
+
version = "5.51"
|
|
8
8
|
description = "Download SPLUS catalogs, FITS and more"
|
|
9
9
|
authors = [
|
|
10
10
|
{ name = "Gustavo Schwarz", email = "gustavo.b.schwarz@gmail.com" }
|
|
@@ -18,7 +18,7 @@ classifiers = [
|
|
|
18
18
|
"License :: OSI Approved :: Apache Software License"
|
|
19
19
|
]
|
|
20
20
|
dependencies = [
|
|
21
|
-
"adss>=1.
|
|
21
|
+
"adss>=1.38",
|
|
22
22
|
"requests",
|
|
23
23
|
"astropy",
|
|
24
24
|
"astroquery",
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
from scipy.interpolate import RegularGridInterpolator
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
import warnings
|
|
6
|
+
from scipy.interpolate import RegularGridInterpolator
|
|
7
|
+
|
|
8
|
+
def _reconstruct_centers_from_model(model, axis="ra", grid_len=None):
|
|
9
|
+
"""
|
|
10
|
+
Rebuild centers to match the (possibly padded) grid length using the model's
|
|
11
|
+
ra_min/ra_max/dec_min/dec_max, bins, and padding.
|
|
12
|
+
|
|
13
|
+
The model saved edges with length `bins`, which yields B = bins-1 actual bins
|
|
14
|
+
before padding. After padding, the grid dimension is B + 2*padding.
|
|
15
|
+
Centers are midpoints of each (possibly padded) bin.
|
|
16
|
+
|
|
17
|
+
Parameters
|
|
18
|
+
----------
|
|
19
|
+
model : dict
|
|
20
|
+
axis : str
|
|
21
|
+
"ra" or "dec"
|
|
22
|
+
grid_len : int
|
|
23
|
+
Target number of centers (should equal grid.shape[dim])
|
|
24
|
+
|
|
25
|
+
Returns
|
|
26
|
+
-------
|
|
27
|
+
np.ndarray
|
|
28
|
+
"""
|
|
29
|
+
assert axis in ("ra", "dec")
|
|
30
|
+
amin = model[f"{axis}_min"]
|
|
31
|
+
amax = model[f"{axis}_max"]
|
|
32
|
+
bins = int(model.get("bins", 15))
|
|
33
|
+
padding = int(model.get("padding", 1))
|
|
34
|
+
|
|
35
|
+
# B is the number of *actual* bins before padding (because edges length = bins)
|
|
36
|
+
B = bins - 1
|
|
37
|
+
if B <= 0:
|
|
38
|
+
raise ValueError(f"Invalid bins in model: bins={bins}")
|
|
39
|
+
|
|
40
|
+
# native bin width (pre-padding)
|
|
41
|
+
dA = (amax - amin) / B
|
|
42
|
+
|
|
43
|
+
# original centers (length B)
|
|
44
|
+
centers_core = amin + (np.arange(B) + 0.5) * dA
|
|
45
|
+
|
|
46
|
+
# padded centers: extend by `padding` bins on both sides at same spacing
|
|
47
|
+
if padding > 0:
|
|
48
|
+
left = centers_core[0] - dA * np.arange(padding, 0, -1)
|
|
49
|
+
right = centers_core[-1] + dA * np.arange(1, padding + 1)
|
|
50
|
+
centers = np.concatenate([left, centers_core, right])
|
|
51
|
+
else:
|
|
52
|
+
centers = centers_core
|
|
53
|
+
|
|
54
|
+
if grid_len is not None and len(centers) != grid_len:
|
|
55
|
+
# If still mismatched, resample linearly across the padded span as a last resort.
|
|
56
|
+
warnings.warn(
|
|
57
|
+
f"{axis.upper()} centers length ({len(centers)}) != grid axis length ({grid_len}). "
|
|
58
|
+
"Resampling centers to match grid shape."
|
|
59
|
+
)
|
|
60
|
+
# Rebuild centers uniformly across the total span of the padded grid:
|
|
61
|
+
total_span = (amax - amin) + 2 * padding * dA
|
|
62
|
+
a_min_padded = amin - padding * dA
|
|
63
|
+
centers = a_min_padded + (np.arange(grid_len) + 0.5) * (total_span / grid_len)
|
|
64
|
+
|
|
65
|
+
return centers
|
|
66
|
+
|
|
67
|
+
def zp_at_coord(model, ra, dec, margin=0.1):
|
|
68
|
+
"""
|
|
69
|
+
Get zero-point correction(s) for coordinate(s).
|
|
70
|
+
|
|
71
|
+
Accepts:
|
|
72
|
+
- ra: float or array-like
|
|
73
|
+
- dec: float or array-like
|
|
74
|
+
|
|
75
|
+
Supports broadcasting:
|
|
76
|
+
- scalar ra with array dec
|
|
77
|
+
- array ra with scalar dec
|
|
78
|
+
- array ra with array dec (same shape or broadcastable)
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
- float if both inputs are scalar
|
|
82
|
+
- np.ndarray otherwise
|
|
83
|
+
|
|
84
|
+
Notes:
|
|
85
|
+
- Keeps your original behavior: if ANY point is out of bounds (with margin)
|
|
86
|
+
or interpolation yields NaN, it raises Exception (instead of partial fill).
|
|
87
|
+
"""
|
|
88
|
+
global_median = float(model.get("global_median", 0.0))
|
|
89
|
+
|
|
90
|
+
# If no grid info, fallback
|
|
91
|
+
if not ("grid" in model and "ra_centers" in model and "dec_centers" in model):
|
|
92
|
+
if np.isscalar(ra) and np.isscalar(dec):
|
|
93
|
+
return global_median
|
|
94
|
+
ra_arr = np.asarray(ra, dtype=float)
|
|
95
|
+
dec_arr = np.asarray(dec, dtype=float)
|
|
96
|
+
ra_b, dec_b = np.broadcast_arrays(ra_arr, dec_arr)
|
|
97
|
+
return np.full(ra_b.shape, global_median, dtype=float)
|
|
98
|
+
|
|
99
|
+
# Load saved arrays
|
|
100
|
+
grid = np.asarray(model["grid"], dtype=float)
|
|
101
|
+
ra_centers = np.asarray(model.get("ra_centers", []), dtype=float)
|
|
102
|
+
dec_centers = np.asarray(model.get("dec_centers", []), dtype=float)
|
|
103
|
+
|
|
104
|
+
# Rebuild centers if needed (mismatch / empty)
|
|
105
|
+
need_rebuild = (
|
|
106
|
+
ra_centers.size == 0 or
|
|
107
|
+
dec_centers.size == 0 or
|
|
108
|
+
grid.ndim != 2 or
|
|
109
|
+
grid.shape[0] != ra_centers.size or
|
|
110
|
+
grid.shape[1] != dec_centers.size
|
|
111
|
+
)
|
|
112
|
+
if need_rebuild:
|
|
113
|
+
ra_centers = _reconstruct_centers_from_model(model, "ra", grid_len=grid.shape[0])
|
|
114
|
+
dec_centers = _reconstruct_centers_from_model(model, "dec", grid_len=grid.shape[1])
|
|
115
|
+
|
|
116
|
+
# Normalize inputs + broadcast
|
|
117
|
+
ra_is_scalar = np.isscalar(ra)
|
|
118
|
+
dec_is_scalar = np.isscalar(dec)
|
|
119
|
+
|
|
120
|
+
ra_arr = np.asarray(ra, dtype=float)
|
|
121
|
+
dec_arr = np.asarray(dec, dtype=float)
|
|
122
|
+
ra_b, dec_b = np.broadcast_arrays(ra_arr, dec_arr)
|
|
123
|
+
|
|
124
|
+
# Vectorized bounds check
|
|
125
|
+
ra_min, ra_max = float(np.min(ra_centers)), float(np.max(ra_centers))
|
|
126
|
+
dec_min, dec_max = float(np.min(dec_centers)), float(np.max(dec_centers))
|
|
127
|
+
|
|
128
|
+
in_bounds = (
|
|
129
|
+
(ra_b >= (ra_min - margin)) & (ra_b <= (ra_max + margin)) &
|
|
130
|
+
(dec_b >= (dec_min - margin)) & (dec_b <= (dec_max + margin))
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
if not np.all(in_bounds):
|
|
134
|
+
bad = np.argwhere(~in_bounds)
|
|
135
|
+
i0 = tuple(bad[0]) # first offending index
|
|
136
|
+
warnings.warn(
|
|
137
|
+
f"Some coordinates are outside the grid range RA=[{ra_min:.3f}, {ra_max:.3f}] "
|
|
138
|
+
f"Dec=[{dec_min:.3f}, {dec_max:.3f}] (margin={margin:.3f}). "
|
|
139
|
+
f"Example at index {i0}: (RA={ra_b[i0]:.3f}, Dec={dec_b[i0]:.3f}). "
|
|
140
|
+
"Falling back to global median (raising exception, per original behavior)."
|
|
141
|
+
)
|
|
142
|
+
raise Exception("Some coordinates are outside the grid range.")
|
|
143
|
+
|
|
144
|
+
# Build interpolator once
|
|
145
|
+
interpolator = RegularGridInterpolator(
|
|
146
|
+
(ra_centers, dec_centers),
|
|
147
|
+
grid,
|
|
148
|
+
bounds_error=False,
|
|
149
|
+
fill_value=np.nan,
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
# Interpolate all points
|
|
153
|
+
pts = np.column_stack([ra_b.ravel(), dec_b.ravel()]) # (N, 2)
|
|
154
|
+
zp = interpolator(pts).reshape(ra_b.shape)
|
|
155
|
+
|
|
156
|
+
if np.any(np.isnan(zp)):
|
|
157
|
+
bad = np.argwhere(np.isnan(zp))
|
|
158
|
+
i0 = tuple(bad[0])
|
|
159
|
+
warnings.warn(
|
|
160
|
+
f"Interpolation failed (NaN) for some coordinates. "
|
|
161
|
+
f"Example at index {i0}: (RA={ra_b[i0]:.3f}, Dec={dec_b[i0]:.3f}). "
|
|
162
|
+
"Returning global median (raising exception, per original behavior)."
|
|
163
|
+
)
|
|
164
|
+
raise Exception("Interpolation failed (NaN) for some coordinates.")
|
|
165
|
+
|
|
166
|
+
out = zp + global_median
|
|
167
|
+
|
|
168
|
+
# Return scalar if scalar inputs
|
|
169
|
+
if ra_is_scalar and dec_is_scalar:
|
|
170
|
+
return float(out)
|
|
171
|
+
|
|
172
|
+
return out
|
|
173
|
+
"""
|
|
174
|
+
Vectorized zero-point correction lookup.
|
|
175
|
+
|
|
176
|
+
Accepts:
|
|
177
|
+
- ra: float or array-like
|
|
178
|
+
- dec: float or array-like
|
|
179
|
+
Supports broadcasting:
|
|
180
|
+
- ra scalar + dec array
|
|
181
|
+
- ra array + dec scalar
|
|
182
|
+
- ra array + dec array (same shape or broadcastable)
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
- float if both inputs are scalar (and return_scalar_if_scalar=True)
|
|
186
|
+
- np.ndarray otherwise
|
|
187
|
+
"""
|
|
188
|
+
global_median = float(model.get("global_median", 0.0))
|
|
189
|
+
|
|
190
|
+
# If no grid, always fallback
|
|
191
|
+
if not ("grid" in model and ("ra_centers" in model or True) and ("dec_centers" in model or True)):
|
|
192
|
+
# keep original behavior: return global median only
|
|
193
|
+
if np.isscalar(ra) and np.isscalar(dec) and return_scalar_if_scalar:
|
|
194
|
+
return global_median
|
|
195
|
+
ra_arr = np.asarray(ra, dtype=float)
|
|
196
|
+
dec_arr = np.asarray(dec, dtype=float)
|
|
197
|
+
ra_b, dec_b = np.broadcast_arrays(ra_arr, dec_arr)
|
|
198
|
+
return np.full(ra_b.shape, global_median, dtype=float)
|
|
199
|
+
|
|
200
|
+
# Load arrays
|
|
201
|
+
grid = np.asarray(model["grid"], dtype=float)
|
|
202
|
+
ra_centers = np.asarray(model.get("ra_centers", []), dtype=float)
|
|
203
|
+
dec_centers = np.asarray(model.get("dec_centers", []), dtype=float)
|
|
204
|
+
|
|
205
|
+
# Rebuild centers if needed
|
|
206
|
+
need_rebuild = (
|
|
207
|
+
ra_centers.size == 0 or
|
|
208
|
+
dec_centers.size == 0 or
|
|
209
|
+
grid.ndim != 2 or
|
|
210
|
+
grid.shape[0] != ra_centers.size or
|
|
211
|
+
grid.shape[1] != dec_centers.size
|
|
212
|
+
)
|
|
213
|
+
if need_rebuild:
|
|
214
|
+
ra_centers = _reconstruct_centers_from_model(model, "ra", grid_len=grid.shape[0])
|
|
215
|
+
dec_centers = _reconstruct_centers_from_model(model, "dec", grid_len=grid.shape[1])
|
|
216
|
+
|
|
217
|
+
# Interpolator (build once)
|
|
218
|
+
interpolator = RegularGridInterpolator(
|
|
219
|
+
(ra_centers, dec_centers),
|
|
220
|
+
grid,
|
|
221
|
+
bounds_error=False,
|
|
222
|
+
fill_value=np.nan,
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
# Normalize inputs to arrays + broadcast
|
|
226
|
+
ra_is_scalar = np.isscalar(ra)
|
|
227
|
+
dec_is_scalar = np.isscalar(dec)
|
|
228
|
+
|
|
229
|
+
ra_arr = np.asarray(ra, dtype=float)
|
|
230
|
+
dec_arr = np.asarray(dec, dtype=float)
|
|
231
|
+
ra_b, dec_b = np.broadcast_arrays(ra_arr, dec_arr)
|
|
232
|
+
|
|
233
|
+
# Bounds check (vectorized) with margin
|
|
234
|
+
ra_min, ra_max = float(np.min(ra_centers)), float(np.max(ra_centers))
|
|
235
|
+
dec_min, dec_max = float(np.min(dec_centers)), float(np.max(dec_centers))
|
|
236
|
+
|
|
237
|
+
in_bounds = (
|
|
238
|
+
(ra_b >= (ra_min - margin)) & (ra_b <= (ra_max + margin)) &
|
|
239
|
+
(dec_b >= (dec_min - margin)) & (dec_b <= (dec_max + margin))
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
if not np.all(in_bounds):
|
|
243
|
+
bad = np.argwhere(~in_bounds)
|
|
244
|
+
i0 = tuple(bad[0]) # first offending index
|
|
245
|
+
warnings.warn(
|
|
246
|
+
f"Some coordinates are outside the grid range (margin={margin} deg). "
|
|
247
|
+
f"Example at index {i0}: (RA={ra_b[i0]:.3f}, Dec={dec_b[i0]:.3f}) "
|
|
248
|
+
f"outside RA=[{ra_min:.3f}, {ra_max:.3f}], Dec=[{dec_min:.3f}, {dec_max:.3f}]."
|
|
249
|
+
)
|
|
250
|
+
raise Exception("Some coordinates are outside the grid range.")
|
|
251
|
+
|
|
252
|
+
# Evaluate interpolation for all points
|
|
253
|
+
pts = np.column_stack([ra_b.ravel(), dec_b.ravel()]) # shape (N, 2)
|
|
254
|
+
zp = interpolator(pts).reshape(ra_b.shape)
|
|
255
|
+
|
|
256
|
+
if np.any(np.isnan(zp)):
|
|
257
|
+
bad = np.argwhere(np.isnan(zp))
|
|
258
|
+
i0 = tuple(bad[0])
|
|
259
|
+
warnings.warn(
|
|
260
|
+
f"Interpolation returned NaN for some points. "
|
|
261
|
+
f"Example at index {i0}: (RA={ra_b[i0]:.3f}, Dec={dec_b[i0]:.3f})."
|
|
262
|
+
)
|
|
263
|
+
raise Exception("Interpolation failed (NaN) for some points.")
|
|
264
|
+
|
|
265
|
+
zp = zp + global_median
|
|
266
|
+
|
|
267
|
+
# Return float if scalar inputs
|
|
268
|
+
if return_scalar_if_scalar and ra_is_scalar and dec_is_scalar:
|
|
269
|
+
return float(zp)
|
|
270
|
+
|
|
271
|
+
return zp
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: splusdata
|
|
3
|
-
Version: 5.
|
|
3
|
+
Version: 5.51
|
|
4
4
|
Summary: Download SPLUS catalogs, FITS and more
|
|
5
5
|
Author-email: Gustavo Schwarz <gustavo.b.schwarz@gmail.com>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -10,7 +10,7 @@ Classifier: License :: OSI Approved :: Apache Software License
|
|
|
10
10
|
Requires-Python: >=3.7
|
|
11
11
|
Description-Content-Type: text/markdown
|
|
12
12
|
License-File: LICENSE
|
|
13
|
-
Requires-Dist: adss>=1.
|
|
13
|
+
Requires-Dist: adss>=1.38
|
|
14
14
|
Requires-Dist: requests
|
|
15
15
|
Requires-Dist: astropy
|
|
16
16
|
Requires-Dist: astroquery
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
from scipy.interpolate import RegularGridInterpolator
|
|
2
|
-
import numpy as np
|
|
3
|
-
|
|
4
|
-
import numpy as np
|
|
5
|
-
import warnings
|
|
6
|
-
from scipy.interpolate import RegularGridInterpolator
|
|
7
|
-
|
|
8
|
-
def _reconstruct_centers_from_model(model, axis="ra", grid_len=None):
|
|
9
|
-
"""
|
|
10
|
-
Rebuild centers to match the (possibly padded) grid length using the model's
|
|
11
|
-
ra_min/ra_max/dec_min/dec_max, bins, and padding.
|
|
12
|
-
|
|
13
|
-
The model saved edges with length `bins`, which yields B = bins-1 actual bins
|
|
14
|
-
before padding. After padding, the grid dimension is B + 2*padding.
|
|
15
|
-
Centers are midpoints of each (possibly padded) bin.
|
|
16
|
-
|
|
17
|
-
Parameters
|
|
18
|
-
----------
|
|
19
|
-
model : dict
|
|
20
|
-
axis : str
|
|
21
|
-
"ra" or "dec"
|
|
22
|
-
grid_len : int
|
|
23
|
-
Target number of centers (should equal grid.shape[dim])
|
|
24
|
-
|
|
25
|
-
Returns
|
|
26
|
-
-------
|
|
27
|
-
np.ndarray
|
|
28
|
-
"""
|
|
29
|
-
assert axis in ("ra", "dec")
|
|
30
|
-
amin = model[f"{axis}_min"]
|
|
31
|
-
amax = model[f"{axis}_max"]
|
|
32
|
-
bins = int(model.get("bins", 15))
|
|
33
|
-
padding = int(model.get("padding", 1))
|
|
34
|
-
|
|
35
|
-
# B is the number of *actual* bins before padding (because edges length = bins)
|
|
36
|
-
B = bins - 1
|
|
37
|
-
if B <= 0:
|
|
38
|
-
raise ValueError(f"Invalid bins in model: bins={bins}")
|
|
39
|
-
|
|
40
|
-
# native bin width (pre-padding)
|
|
41
|
-
dA = (amax - amin) / B
|
|
42
|
-
|
|
43
|
-
# original centers (length B)
|
|
44
|
-
centers_core = amin + (np.arange(B) + 0.5) * dA
|
|
45
|
-
|
|
46
|
-
# padded centers: extend by `padding` bins on both sides at same spacing
|
|
47
|
-
if padding > 0:
|
|
48
|
-
left = centers_core[0] - dA * np.arange(padding, 0, -1)
|
|
49
|
-
right = centers_core[-1] + dA * np.arange(1, padding + 1)
|
|
50
|
-
centers = np.concatenate([left, centers_core, right])
|
|
51
|
-
else:
|
|
52
|
-
centers = centers_core
|
|
53
|
-
|
|
54
|
-
if grid_len is not None and len(centers) != grid_len:
|
|
55
|
-
# If still mismatched, resample linearly across the padded span as a last resort.
|
|
56
|
-
warnings.warn(
|
|
57
|
-
f"{axis.upper()} centers length ({len(centers)}) != grid axis length ({grid_len}). "
|
|
58
|
-
"Resampling centers to match grid shape."
|
|
59
|
-
)
|
|
60
|
-
# Rebuild centers uniformly across the total span of the padded grid:
|
|
61
|
-
total_span = (amax - amin) + 2 * padding * dA
|
|
62
|
-
a_min_padded = amin - padding * dA
|
|
63
|
-
centers = a_min_padded + (np.arange(grid_len) + 0.5) * (total_span / grid_len)
|
|
64
|
-
|
|
65
|
-
return centers
|
|
66
|
-
|
|
67
|
-
def zp_at_coord(model, ra, dec, margin=0.1):
|
|
68
|
-
"""
|
|
69
|
-
Get zero-point correction for a given coordinate.
|
|
70
|
-
|
|
71
|
-
Parameters
|
|
72
|
-
----------
|
|
73
|
-
model : dict
|
|
74
|
-
Zero-point calibration model loaded from JSON.
|
|
75
|
-
ra : float
|
|
76
|
-
Right Ascension in degrees.
|
|
77
|
-
dec : float
|
|
78
|
-
Declination in degrees.
|
|
79
|
-
margin : float, optional
|
|
80
|
-
Allowed margin (in degrees) outside the grid before warning.
|
|
81
|
-
Default = 0.1 deg.
|
|
82
|
-
|
|
83
|
-
Returns
|
|
84
|
-
-------
|
|
85
|
-
float
|
|
86
|
-
Zero-point correction value (mag).
|
|
87
|
-
"""
|
|
88
|
-
global_median = model.get("global_median", 0.0)
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
if "grid" in model and "ra_centers" in model and "dec_centers" in model:
|
|
92
|
-
ra_centers = np.array(model["ra_centers"])
|
|
93
|
-
dec_centers = np.array(model["dec_centers"])
|
|
94
|
-
grid = np.array(model["grid"])
|
|
95
|
-
|
|
96
|
-
need_rebuild = (
|
|
97
|
-
ra_centers.size == 0 or
|
|
98
|
-
dec_centers.size == 0 or
|
|
99
|
-
grid.shape[0] != ra_centers.size or
|
|
100
|
-
grid.shape[1] != dec_centers.size
|
|
101
|
-
)
|
|
102
|
-
if need_rebuild:
|
|
103
|
-
ra_centers = _reconstruct_centers_from_model(model, "ra", grid_len=grid.shape[0])
|
|
104
|
-
dec_centers = _reconstruct_centers_from_model(model, "dec", grid_len=grid.shape[1])
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
ra_min, ra_max = ra_centers.min(), ra_centers.max()
|
|
108
|
-
dec_min, dec_max = dec_centers.min(), dec_centers.max()
|
|
109
|
-
|
|
110
|
-
# Check bounds with margin
|
|
111
|
-
if not (ra_min - margin <= ra <= ra_max + margin and
|
|
112
|
-
dec_min - margin <= dec <= dec_max + margin):
|
|
113
|
-
warnings.warn(
|
|
114
|
-
f"Coordinate (RA={ra:.3f}, Dec={dec:.3f}) is outside "
|
|
115
|
-
f"the grid range RA=[{ra_min:.3f}, {ra_max:.3f}], "
|
|
116
|
-
f"Dec=[{dec_min:.3f}, {dec_max:.3f}]. "
|
|
117
|
-
"Falling back to global median."
|
|
118
|
-
)
|
|
119
|
-
raise Exception(
|
|
120
|
-
f"Coordinate (RA={ra}, Dec={dec}) is outside the grid range."
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
# Interpolator
|
|
124
|
-
interpolator = RegularGridInterpolator(
|
|
125
|
-
(ra_centers, dec_centers), grid,
|
|
126
|
-
bounds_error=False,
|
|
127
|
-
fill_value=np.nan
|
|
128
|
-
)
|
|
129
|
-
zp_value = interpolator([[ra, dec]])[0]
|
|
130
|
-
|
|
131
|
-
# If interpolator returns NaN, fallback
|
|
132
|
-
if np.isnan(zp_value):
|
|
133
|
-
warnings.warn(
|
|
134
|
-
f"Interpolation failed at (RA={ra:.3f}, Dec={dec:.3f}). "
|
|
135
|
-
"Returning global median."
|
|
136
|
-
)
|
|
137
|
-
raise Exception(
|
|
138
|
-
f"Interpolation failed at (RA={ra}, Dec={dec}). "
|
|
139
|
-
"Falling back to global median."
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
-
return float(zp_value) + global_median
|
|
143
|
-
|
|
144
|
-
# Fallback if no grid in model
|
|
145
|
-
return global_median
|
|
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
|
|
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
|
|
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
|