lightstack 0.1.5__tar.gz → 0.1.6__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.
- {lightstack-0.1.5/src/lightstack.egg-info → lightstack-0.1.6}/PKG-INFO +1 -1
- {lightstack-0.1.5 → lightstack-0.1.6}/pyproject.toml +1 -1
- {lightstack-0.1.5 → lightstack-0.1.6}/src/lightstack/datacube.py +51 -33
- {lightstack-0.1.5 → lightstack-0.1.6}/src/lightstack/psf.py +1 -1
- {lightstack-0.1.5 → lightstack-0.1.6}/src/lightstack/utils.py +3 -3
- {lightstack-0.1.5 → lightstack-0.1.6/src/lightstack.egg-info}/PKG-INFO +1 -1
- {lightstack-0.1.5 → lightstack-0.1.6}/LICENSE +0 -0
- {lightstack-0.1.5 → lightstack-0.1.6}/README.md +0 -0
- {lightstack-0.1.5 → lightstack-0.1.6}/setup.cfg +0 -0
- {lightstack-0.1.5 → lightstack-0.1.6}/src/lightstack/__init__.py +0 -0
- {lightstack-0.1.5 → lightstack-0.1.6}/src/lightstack/crop.py +0 -0
- {lightstack-0.1.5 → lightstack-0.1.6}/src/lightstack/plot.py +0 -0
- {lightstack-0.1.5 → lightstack-0.1.6}/src/lightstack.egg-info/SOURCES.txt +0 -0
- {lightstack-0.1.5 → lightstack-0.1.6}/src/lightstack.egg-info/dependency_links.txt +0 -0
- {lightstack-0.1.5 → lightstack-0.1.6}/src/lightstack.egg-info/requires.txt +0 -0
- {lightstack-0.1.5 → lightstack-0.1.6}/src/lightstack.egg-info/top_level.txt +0 -0
|
@@ -24,11 +24,11 @@ def align_reproject_fits(fits_list, ref_file, method="interp", crop=1):
|
|
|
24
24
|
method : str, optional
|
|
25
25
|
Reprojection method:
|
|
26
26
|
- "interp" (default): faster, interpolates values
|
|
27
|
-
- "exact": slower
|
|
27
|
+
- "exact": slower --> but reproject_exact has precision issues
|
|
28
|
+
with resolutions below ~0.05 arcsec.
|
|
28
29
|
|
|
29
30
|
crop : int, optional
|
|
30
|
-
Number of pixels to remove from each border after reprojection
|
|
31
|
-
(helps remove edge artifacts from interpolation).
|
|
31
|
+
Number of pixels to remove from each border after reprojection.
|
|
32
32
|
|
|
33
33
|
Returns
|
|
34
34
|
-------
|
|
@@ -36,7 +36,6 @@ def align_reproject_fits(fits_list, ref_file, method="interp", crop=1):
|
|
|
36
36
|
List in the form [(aligned_fits_path, filter_name), ...].
|
|
37
37
|
"""
|
|
38
38
|
|
|
39
|
-
|
|
40
39
|
# Choose reprojection method
|
|
41
40
|
if method == "exact":
|
|
42
41
|
reproj_func = reproject_exact
|
|
@@ -49,7 +48,9 @@ def align_reproject_fits(fits_list, ref_file, method="interp", crop=1):
|
|
|
49
48
|
with fits.open(ref_file) as hdul_ref:
|
|
50
49
|
ext_ref = find_ext(hdul_ref)
|
|
51
50
|
if ext_ref is None:
|
|
52
|
-
raise ValueError(
|
|
51
|
+
raise ValueError(
|
|
52
|
+
f"No valid image data in reference file '{ref_file}'."
|
|
53
|
+
)
|
|
53
54
|
|
|
54
55
|
ref_header = hdul_ref[ext_ref].header
|
|
55
56
|
ref_wcs = WCS(ref_header)
|
|
@@ -83,14 +84,20 @@ def align_reproject_fits(fits_list, ref_file, method="interp", crop=1):
|
|
|
83
84
|
data_aligned, footprint = reproj_func(
|
|
84
85
|
(data, wcs_in),
|
|
85
86
|
ref_wcs,
|
|
86
|
-
shape_out=shape_out
|
|
87
|
-
|
|
88
|
-
# Area correction
|
|
89
|
-
area_ratio = (scale_out / scale_in)**2
|
|
90
|
-
data_aligned *= area_ratio
|
|
87
|
+
shape_out=shape_out
|
|
88
|
+
)
|
|
91
89
|
|
|
92
90
|
print(f"Pixel scale in/out: {scale_in:.4f} -> {scale_out:.4f}")
|
|
93
|
-
|
|
91
|
+
|
|
92
|
+
# Apply area correction only if scales differ
|
|
93
|
+
if not np.isclose(scale_in, scale_out, rtol=1e-6):
|
|
94
|
+
area_ratio = (scale_out / scale_in) ** 2
|
|
95
|
+
data_aligned *= area_ratio
|
|
96
|
+
print(f"Applied area correction: {area_ratio:.4f}")
|
|
97
|
+
area_corrected = True
|
|
98
|
+
else:
|
|
99
|
+
print("Pixel scales identical: no area correction applied.")
|
|
100
|
+
area_corrected = False
|
|
94
101
|
|
|
95
102
|
# Crop border
|
|
96
103
|
if crop > 0:
|
|
@@ -108,19 +115,33 @@ def align_reproject_fits(fits_list, ref_file, method="interp", crop=1):
|
|
|
108
115
|
|
|
109
116
|
ref_filter = infer_filter(ref_file)
|
|
110
117
|
header_aligned.add_history(
|
|
111
|
-
f"Reprojected to {ref_filter} using {method} method
|
|
112
|
-
|
|
113
|
-
|
|
118
|
+
f"Reprojected to {ref_filter} using {method} method "
|
|
119
|
+
"(reproject package)"
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
if area_corrected:
|
|
123
|
+
header_aligned.add_history(
|
|
124
|
+
"Flux corrected by pixel area ratio"
|
|
125
|
+
)
|
|
126
|
+
else:
|
|
127
|
+
header_aligned.add_history(
|
|
128
|
+
"No area correction applied "
|
|
129
|
+
"(identical pixel scale)"
|
|
130
|
+
)
|
|
114
131
|
|
|
115
132
|
if crop > 0:
|
|
116
|
-
header_aligned.add_history(
|
|
133
|
+
header_aligned.add_history(
|
|
134
|
+
f"Cropped {crop} pixels from each border"
|
|
135
|
+
)
|
|
117
136
|
|
|
118
137
|
# Save
|
|
119
138
|
suffix = f"_aligned_{method}"
|
|
120
139
|
out_name = os.path.splitext(fpath)[0] + f"{suffix}.fits"
|
|
121
140
|
|
|
122
|
-
fits.PrimaryHDU(
|
|
123
|
-
|
|
141
|
+
fits.PrimaryHDU(
|
|
142
|
+
data_aligned,
|
|
143
|
+
header=header_aligned
|
|
144
|
+
).writeto(out_name, overwrite=True)
|
|
124
145
|
|
|
125
146
|
# Flux sanity check
|
|
126
147
|
sum_in = np.nansum(data)
|
|
@@ -131,7 +152,6 @@ def align_reproject_fits(fits_list, ref_file, method="interp", crop=1):
|
|
|
131
152
|
print(f"Saved: {out_name}")
|
|
132
153
|
|
|
133
154
|
return aligned_list
|
|
134
|
-
|
|
135
155
|
|
|
136
156
|
|
|
137
157
|
def build_datacube(aligned_fits_files, reference_file, output_path):
|
|
@@ -255,45 +275,43 @@ def cut_region_datacube(cube_fits_file, x_start, x_end, y_start, y_end, output_p
|
|
|
255
275
|
None
|
|
256
276
|
Saves the cut datacube.
|
|
257
277
|
"""
|
|
258
|
-
|
|
278
|
+
|
|
259
279
|
with fits.open(cube_fits_file) as hdul:
|
|
260
280
|
ext = find_ext(hdul)
|
|
261
281
|
if ext is None:
|
|
262
282
|
raise ValueError(f"No image data found in {cube_fits_file}")
|
|
263
283
|
|
|
264
284
|
cube_data = hdul[ext].data
|
|
265
|
-
cube_header = hdul[ext].header
|
|
285
|
+
cube_header = hdul[ext].header.copy()
|
|
266
286
|
|
|
267
|
-
# Cut
|
|
287
|
+
# Cut data
|
|
268
288
|
cut_data = cube_data[:, y_start:y_end, x_start:x_end]
|
|
269
289
|
|
|
270
|
-
#
|
|
290
|
+
# Update WCS
|
|
271
291
|
wcs_3d = WCS(cube_header)
|
|
272
292
|
wcs_3d.wcs.crpix[0] -= x_start
|
|
273
293
|
wcs_3d.wcs.crpix[1] -= y_start
|
|
274
294
|
|
|
275
|
-
#
|
|
276
|
-
new_header =
|
|
277
|
-
new_header
|
|
295
|
+
# New header
|
|
296
|
+
new_header = cube_header.copy()
|
|
297
|
+
new_header.update(wcs_3d.to_header())
|
|
298
|
+
|
|
278
299
|
new_header['NAXIS1'] = x_end - x_start
|
|
279
300
|
new_header['NAXIS2'] = y_end - y_start
|
|
280
301
|
new_header['NAXIS3'] = cube_data.shape[0]
|
|
281
302
|
|
|
282
|
-
#
|
|
283
|
-
for key in cube_header:
|
|
284
|
-
if key.startswith('FILTER'):
|
|
285
|
-
new_header[key] = cube_header[key]
|
|
286
|
-
|
|
287
|
-
# Crop window
|
|
303
|
+
# Crop window
|
|
288
304
|
new_header['XMINPIX'] = x_start
|
|
289
305
|
new_header['XMAXPIX'] = x_end
|
|
290
306
|
new_header['YMINPIX'] = y_start
|
|
291
307
|
new_header['YMAXPIX'] = y_end
|
|
292
308
|
|
|
293
|
-
# Save
|
|
309
|
+
# Save
|
|
294
310
|
fits.PrimaryHDU(cut_data, header=new_header).writeto(output_path, overwrite=True)
|
|
311
|
+
|
|
295
312
|
print(f"Cut datacube saved to '{output_path}'")
|
|
296
|
-
|
|
313
|
+
|
|
314
|
+
|
|
297
315
|
def build_valid_datacube(cube_fits_file, output_cube, threshold=0.0, frac_valid=0.01):
|
|
298
316
|
"""
|
|
299
317
|
Remove empty filters in a datacube, saves the new datacube and returns the valid filter names.
|
|
@@ -33,7 +33,7 @@ def pick_folder(filt):
|
|
|
33
33
|
|
|
34
34
|
def get_filter(fname):
|
|
35
35
|
"""
|
|
36
|
-
Extracts the filter name from a FITS filename using a regular expression.
|
|
36
|
+
Extracts the filter name from a FITS filename using a regular expression. Function for HST and JWST filters.
|
|
37
37
|
|
|
38
38
|
Parameters
|
|
39
39
|
----------
|
|
@@ -58,7 +58,7 @@ def get_filter(fname):
|
|
|
58
58
|
|
|
59
59
|
def infer_filter(fname):
|
|
60
60
|
"""
|
|
61
|
-
Infers the filter name from a FITS filename.
|
|
61
|
+
Infers the filter name from a FITS filename. Function for HST and JWST filters.
|
|
62
62
|
|
|
63
63
|
Parameters
|
|
64
64
|
----------
|
|
@@ -79,7 +79,7 @@ def infer_filter(fname):
|
|
|
79
79
|
|
|
80
80
|
def filter_id(filt):
|
|
81
81
|
"""
|
|
82
|
-
Extracts the numeric part of a filter name for sorting.
|
|
82
|
+
Extracts the numeric part of a filter name for sorting. Function for HST and JWST filters.
|
|
83
83
|
|
|
84
84
|
Parameters
|
|
85
85
|
----------
|
|
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
|