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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lightstack
3
- Version: 0.1.5
3
+ Version: 0.1.6
4
4
  Summary: Tools for building and processing multi-filter astrophysical datacubes
5
5
  Author: Andressa Wille, Thallis Pessi
6
6
  License: MIT License
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "lightstack"
3
- version = "0.1.5"
3
+ version = "0.1.6"
4
4
  description = "Tools for building and processing multi-filter astrophysical datacubes"
5
5
  readme = "README.md"
6
6
  authors = [{name = "Andressa Wille"}, {name="Thallis Pessi"}]
@@ -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 --> but reproject_exact has precision issues with resolutions below ~0.05 arcsec, so the results may not be accurate.
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(f"No valid image data in reference file '{ref_file}'.")
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
- print(f"Applied area correction: {area_ratio:.4f}")
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 (reproject package)")
112
- header_aligned.add_history(
113
- "Flux corrected by pixel area ratio")
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(f"Cropped {crop} pixels from each border")
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(data_aligned, header=header_aligned).writeto(
123
- out_name, overwrite=True)
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
- # Open datacube
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 the data array
287
+ # Cut data
268
288
  cut_data = cube_data[:, y_start:y_end, x_start:x_end]
269
289
 
270
- # Get and update WCS
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
- # Create new header with updated size and WCS
276
- new_header = wcs_3d.to_header()
277
- new_header['NAXIS'] = 3
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
- # Filter info
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 cut datacube
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.
@@ -133,7 +133,7 @@ def resample_psf(
133
133
  psf_resampled = zoom(psf, zoom_factor, order=order)
134
134
 
135
135
  # Area correction
136
- psf_resampled /= zoom_factor**2
136
+ psf_resampled /= zoom_factor
137
137
 
138
138
  # Ensure odd shape
139
139
  if make_odd_shape:
@@ -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
  ----------
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lightstack
3
- Version: 0.1.5
3
+ Version: 0.1.6
4
4
  Summary: Tools for building and processing multi-filter astrophysical datacubes
5
5
  Author: Andressa Wille, Thallis Pessi
6
6
  License: MIT License
File without changes
File without changes
File without changes