lightstack 0.1.1__tar.gz → 0.1.2__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.1
3
+ Version: 0.1.2
4
4
  Summary: Tools for building and processing multi-filter astrophysical datacubes
5
5
  Author: Andressa Wille, Thallis Pessi
6
6
  License: MIT License
@@ -35,6 +35,8 @@ Requires-Dist: regions
35
35
  Requires-Dist: scipy
36
36
  Requires-Dist: photutils
37
37
 
38
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.19393439.svg)](https://doi.org/10.5281/zenodo.19393439)
39
+
38
40
  <img src="https://raw.githubusercontent.com/AndressaWille/lightstack/master/images/logo.png" width="120">
39
41
 
40
42
  # Lightstack
@@ -1,3 +1,5 @@
1
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.19393439.svg)](https://doi.org/10.5281/zenodo.19393439)
2
+
1
3
  <img src="https://raw.githubusercontent.com/AndressaWille/lightstack/master/images/logo.png" width="120">
2
4
 
3
5
  # Lightstack
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "lightstack"
3
- version = "0.1.1"
3
+ version = "0.1.2"
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"}]
@@ -6,7 +6,7 @@ from astropy.wcs import WCS
6
6
 
7
7
  from reproject import reproject_interp, reproject_exact
8
8
 
9
- from .utils import find_ext, infer_filter
9
+ from .utils import find_ext, infer_filter, get_pixel_scale_wcs
10
10
 
11
11
 
12
12
  def align_reproject_fits(fits_list, ref_file, method="interp", crop=1):
@@ -24,7 +24,7 @@ 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, conserves flux --> 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 with resolutions below ~0.05 arcsec, so the results may not be accurate.
28
28
 
29
29
  crop : int, optional
30
30
  Number of pixels to remove from each border after reprojection
@@ -36,6 +36,7 @@ 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
+
39
40
  # Choose reprojection method
40
41
  if method == "exact":
41
42
  reproj_func = reproject_exact
@@ -54,6 +55,9 @@ def align_reproject_fits(fits_list, ref_file, method="interp", crop=1):
54
55
  ref_wcs = WCS(ref_header)
55
56
  shape_out = hdul_ref[ext_ref].data.shape
56
57
 
58
+ # Pixel scale of reference
59
+ scale_out = get_pixel_scale_from_wcs(ref_wcs)
60
+
57
61
  aligned_list = []
58
62
 
59
63
  # Loop over all FITS
@@ -65,20 +69,30 @@ def align_reproject_fits(fits_list, ref_file, method="interp", crop=1):
65
69
  print(f"No data extension found in {fpath}. Skipping.")
66
70
  continue
67
71
 
68
- data = hdul[ext].data
72
+ data = hdul[ext].data.astype(float)
69
73
  header = hdul[ext].header
70
74
  wcs_in = WCS(header)
71
75
 
72
76
  unit = header.get('BUNIT', 'unknown')
73
77
  print(f"Filter {filt}: unit = {unit}")
74
78
 
79
+ # Pixel scale of input
80
+ scale_in = get_pixel_scale_from_wcs(wcs_in)
81
+
75
82
  # Reproject
76
83
  data_aligned, footprint = reproj_func(
77
84
  (data, wcs_in),
78
85
  ref_wcs,
79
86
  shape_out=shape_out)
80
87
 
81
- # Crop border (interp method)
88
+ # Area correction
89
+ area_ratio = (scale_out / scale_in)**2
90
+ data_aligned *= area_ratio
91
+
92
+ print(f"Pixel scale in/out: {scale_in:.4f} -> {scale_out:.4f}")
93
+ print(f"Applied area correction: {area_ratio:.4f}")
94
+
95
+ # Crop border
82
96
  if crop > 0:
83
97
  data_aligned = data_aligned[crop:-crop, crop:-crop]
84
98
 
@@ -90,10 +104,13 @@ def align_reproject_fits(fits_list, ref_file, method="interp", crop=1):
90
104
 
91
105
  # Header
92
106
  header_aligned = wcs_out.to_header()
107
+ header_aligned['BUNIT'] = unit
93
108
 
94
109
  ref_filter = infer_filter(ref_file)
95
110
  header_aligned.add_history(
96
111
  f"Reprojected to {ref_filter} using {method} method (reproject package)")
112
+ header_aligned.add_history(
113
+ "Flux corrected by pixel area ratio")
97
114
 
98
115
  if crop > 0:
99
116
  header_aligned.add_history(f"Cropped {crop} pixels from each border")
@@ -105,10 +122,16 @@ def align_reproject_fits(fits_list, ref_file, method="interp", crop=1):
105
122
  fits.PrimaryHDU(data_aligned, header=header_aligned).writeto(
106
123
  out_name, overwrite=True)
107
124
 
125
+ # Flux sanity check
126
+ sum_in = np.nansum(data)
127
+ sum_out = np.nansum(data_aligned)
128
+ print(f"Flux ratio (out/in): {sum_out/sum_in:.4f}")
129
+
108
130
  aligned_list.append((out_name, filt))
109
131
  print(f"Saved: {out_name}")
110
132
 
111
133
  return aligned_list
134
+
112
135
 
113
136
 
114
137
  def build_datacube(aligned_fits_files, reference_file, output_path):
@@ -125,8 +125,14 @@ def resample_psf(
125
125
  raise ValueError("Provide either zoom_factor OR both psf_pixel_scale and target_pixel_scale")
126
126
  zoom_factor = psf_pixel_scale / target_pixel_scale
127
127
 
128
+ if zoom_factor > 1:
129
+ raise ValueError("Upsampling not recommended.")
130
+
128
131
  # Resample
129
132
  psf_resampled = zoom(psf, zoom_factor, order=order)
133
+
134
+ # Area correction
135
+ psf_resampled /= zoom_factor**2
130
136
 
131
137
  # Ensure odd shape
132
138
  if make_odd_shape:
@@ -143,6 +149,7 @@ def resample_psf(
143
149
  header["HISTORY"] = "PSF resampled using scipy.ndimage.zoom"
144
150
  if target_pixel_scale is not None:
145
151
  header["CDELT1"] = (target_pixel_scale / 3600, "deg/pix")
152
+ header["CDELT2"] = (target_pixel_scale / 3600, "deg/pix")
146
153
 
147
154
  # Save
148
155
  fits.PrimaryHDU(psf_resampled, header=header).writeto(
@@ -210,7 +210,7 @@ def MJy_sr_to_jy(aligned_list):
210
210
 
211
211
  def get_pixel_scale(fits_path):
212
212
  """
213
- Compute pixel scale in arcsec/pixel using WCS.
213
+ Compute pixel scale in arcsec/pixel using WCS. Assumes square pixels and no significant distortion.
214
214
  """
215
215
  with fits.open(fits_path) as hdul:
216
216
  ext = find_ext(hdul)
@@ -221,3 +221,11 @@ def get_pixel_scale(fits_path):
221
221
 
222
222
  pixscale = proj_plane_pixel_scales(wcs)[0] * 3600.0
223
223
  return pixscale
224
+
225
+
226
+ def get_pixel_scale_from_wcs(wcs):
227
+ """
228
+ Compute pixel scale in arcsec/pixel from a WCS object.
229
+ Assumes square pixels and no significant distortion.
230
+ """
231
+ return proj_plane_pixel_scales(wcs)[0] * 3600.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lightstack
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: Tools for building and processing multi-filter astrophysical datacubes
5
5
  Author: Andressa Wille, Thallis Pessi
6
6
  License: MIT License
@@ -35,6 +35,8 @@ Requires-Dist: regions
35
35
  Requires-Dist: scipy
36
36
  Requires-Dist: photutils
37
37
 
38
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.19393439.svg)](https://doi.org/10.5281/zenodo.19393439)
39
+
38
40
  <img src="https://raw.githubusercontent.com/AndressaWille/lightstack/master/images/logo.png" width="120">
39
41
 
40
42
  # Lightstack
File without changes
File without changes