Skip to content

Commit ea58dda

Browse files
authored
Merge pull request #173 from rchwn/my-changes
adjust lv3 blotting functionality
2 parents bc009bd + cbd9b7e commit ea58dda

File tree

2 files changed

+77
-85
lines changed

2 files changed

+77
-85
lines changed

docs/steps/lv3.rst

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,38 @@ For more details, see the `official documentation <https://jwst-pipeline.readthe
88
If you have background observations, you should ensure these are being correctly picked up by using ``bgr_check_type``
99
and ``bgr_background_name``.
1010

11-
After the main pipeline run, individual exposures can optionally be drizzled onto the
12-
mosaic WCS (boolean ``do_drizzle`` flag) and then blotted back to a reference detector frame
13-
(boolean ``do_blot`` flag). This is useful for per-exposure comparisons in pixel space. Resample
14-
parameters are read from ``jwst_parameters["resample"]``; blotting behaviour is
15-
controlled by ``blot_ref_index`` and ``blot_fillval``. In your config file:
11+
After the main pipeline run, two optional post-processing steps are available.
12+
These are independent of each other and can be enabled separately:
13+
14+
- ``do_drizzle``: Drizzle individual exposures onto the mosaic WCS, producing
15+
one ``*_i2d_single.fits`` file per exposure. Resample parameters are read
16+
from ``jwst_parameters["resample"]``.
17+
- ``do_blot``: Blot the final i2d mosaic back to each exposure's detector
18+
frame, producing one ``*_i2d_blot.fits`` file per exposure. The fill value
19+
for pixels outside the blotted footprint is controlled by ``blot_fillval``.
20+
21+
**Example 1** -- Drizzle individual exposures onto the mosaic WCS only:
1622

17-
Example 1: Just drizzle the individual exposures onto the mosaic WCS. No blotting.
1823
.. code-block:: toml
1924
2025
[parameters.lv3]
2126
do_drizzle = true
22-
do_blot = false
2327
24-
Example 2: Drizzle the individual exposures onto the mosaic WCS and then blot back to the reference detector frame
28+
**Example 2** -- Blot the mosaic to every exposure's detector frame only:
29+
30+
.. code-block:: toml
31+
32+
[parameters.lv3]
33+
do_blot = true
34+
blot_fillval = "nan"
35+
36+
**Example 3** -- Both drizzle and blot:
37+
2538
.. code-block:: toml
2639
2740
[parameters.lv3]
28-
# You can also set do_drizzle = true here, but not necessary.
29-
# You can not set do_blot = true while setting do_drizzle = false (you'll get an error)
41+
do_drizzle = true
3042
do_blot = true
31-
blot_ref_index = 0
32-
blot_fillval = np.nan
3343
3444
---
3545
API

pjpipe/lv3/lv3_step.py

Lines changed: 55 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,8 @@ def __init__(
6969
bgr_background_name="off",
7070
process_bgr_like_science=False,
7171
jwst_parameters=None,
72-
do_drizzle=None,
72+
do_drizzle=False,
7373
do_blot=False,
74-
blot_ref_index=0,
7574
blot_fillval=np.nan,
7675
overwrite=False,
7776
):
@@ -123,17 +122,12 @@ def __init__(
123122
do_drizzle: If True, drizzle individual frames
124123
to the i2d mosaic WCS after the main
125124
pipeline run. Note: creates a lot of files.
126-
If not set (None) and do_blot is True,
127-
do_drizzle is automatically enabled. If
128-
explicitly set to False while do_blot is
129-
True, a ValueError is raised. Defaults to
130-
None.
131-
do_blot: If True, blot each resampled exposure
132-
back to a reference detector frame (requires
133-
drizzling). Defaults to False.
134-
blot_ref_index: Index into the alphabetically sorted CRF file list of
135-
the reference exposure whose detector frame
136-
is used for blotting. Defaults to 0.
125+
Defaults to False.
126+
do_blot: If True, blot the final i2d mosaic to
127+
the detector frame of each exposure,
128+
producing one ``*_i2d_blot.fits`` file per
129+
exposure. Independent of do_drizzle.
130+
Defaults to False.
137131
blot_fillval: Fill value for pixels outside the
138132
blotted footprint. Defaults to np.nan
139133
overwrite: Whether to overwrite or not. Defaults
@@ -175,18 +169,9 @@ def __init__(
175169
self.bgr_background_name = bgr_background_name
176170
self.process_bgr_like_science = process_bgr_like_science
177171
self.jwst_parameters = jwst_parameters
178-
if do_drizzle is False and do_blot:
179-
raise ValueError(
180-
"do_drizzle was explicitly set to False, but do_blot "
181-
"was set to True. Either set (do_drizzle, do_blot) to "
182-
"(True, True) or (True, False), or (None, True)."
183-
)
184-
if do_drizzle is None:
185-
do_drizzle = bool(do_blot)
186172
self.do_drizzle = do_drizzle
187173
self.do_blot = do_blot
188-
self.blot_ref_index = blot_ref_index
189-
self.blot_fillval = blot_fillval
174+
self.blot_fillval = float(blot_fillval)
190175
self.overwrite = overwrite
191176

192177
self.band_type = get_band_type(self.band)
@@ -696,88 +681,85 @@ def run_step(
696681
# - Can't have suffix ending in _i2d.fits, messes with anchoring pattern matching.
697682
for f in glob.glob(os.path.join(self.out_dir, "*_outlier_resamplestep.fits")):
698683
os.rename(f, f.replace("_outlier_resamplestep.fits", "_i2d_single.fits"))
699-
700-
if self.do_blot:
701-
self._blot_to_detector_frame(
702-
crf_files=crf_files,
703-
ref_index=self.blot_ref_index,
704-
)
705684
else:
706685
log.warning("do_drizzle is set but no i2d/crf files found")
707686

687+
if self.do_blot:
688+
i2d_files = glob.glob(os.path.join(self.out_dir, "*_i2d.fits"))
689+
crf_files = sorted(glob.glob(os.path.join(self.out_dir, "*_crf.fits")))
690+
if i2d_files and crf_files:
691+
self._blot_to_detector_frame(crf_files=crf_files)
692+
else:
693+
log.warning("do_blot is set but no i2d/crf files found")
694+
708695
return True
709696

710697
def _blot_to_detector_frame(
711698
self,
712699
crf_files,
713-
ref_index=0,
714700
):
715-
"""Blot resampled single-exposure images back to a detector frame.
701+
"""Blot the final i2d mosaic to each exposure's detector frame.
716702
717-
Each ``*_i2d_single.fits`` file (one per exposure, on the common
718-
i2d WCS grid) is blotted onto the detector frame of the chosen
719-
reference exposure. The output files are written as
720-
``*_blot_det.fits`` alongside the other lv3 products.
703+
The ``*_i2d.fits`` mosaic is blotted onto the detector frame of
704+
every CRF exposure, producing one ``*_i2d_blot.fits`` file per
705+
exposure alongside the other lv3 products.
721706
722707
Args:
723708
crf_files: Sorted list of CRF file paths (one per exposure).
724-
Used to obtain the detector-frame GWCS and shape.
725-
ref_index: Index into *crf_files* of the reference exposure
726-
whose detector frame will be adopted. Defaults to 0
727-
(the first exposure).
709+
Each CRF provides the detector-frame GWCS and shape that
710+
the mosaic is blotted onto.
728711
"""
729712

730-
single_files = sorted(
731-
glob.glob(os.path.join(self.out_dir, "*_i2d_single.fits"))
732-
)
733-
if not single_files:
734-
log.warning("_blot_to_detector_frame: no *_i2d_single.fits files found")
713+
i2d_files = glob.glob(os.path.join(self.out_dir, "*_i2d.fits"))
714+
if not i2d_files:
715+
log.warning("_blot_to_detector_frame: no *_i2d.fits mosaic found")
735716
return
736717

737-
if ref_index >= len(crf_files):
718+
if len(i2d_files) > 1:
738719
log.warning(
739-
"_blot_to_detector_frame: ref_index %d out of range (%d exposures)",
740-
ref_index, len(crf_files),
720+
"_blot_to_detector_frame: found %d i2d files, using %s",
721+
len(i2d_files), os.path.basename(i2d_files[0]),
741722
)
742-
return
743723

744724
log.info(
745-
"Blotting %d resampled exposures to detector frame of %s",
746-
len(single_files), os.path.basename(crf_files[ref_index]),
725+
"Blotting i2d mosaic to %d exposure detector frames",
726+
len(crf_files),
747727
)
748728

749-
with datamodels.open(crf_files[ref_index]) as ref_model:
750-
blot_wcs = ref_model.meta.wcs
751-
blot_shape = ref_model.data.shape
729+
with datamodels.open(i2d_files[0]) as i2d_model:
730+
mosaic_data = i2d_model.data.astype(np.float32)
731+
mosaic_wcs = i2d_model.meta.wcs
752732

753-
ref_pixflux_area = ref_model.meta.photometry.pixelarea_steradians
754-
blot_wcs.array_shape = blot_shape
755-
ref_pixel_area = compute_image_pixel_area(blot_wcs)
756-
pix_ratio = np.sqrt(ref_pixflux_area / ref_pixel_area)
733+
for crf_file in crf_files:
734+
with datamodels.open(crf_file) as crf_model:
735+
blot_wcs = crf_model.meta.wcs
736+
blot_shape = crf_model.data.shape
737+
738+
pixflux_area = crf_model.meta.photometry.pixelarea_steradians
739+
blot_wcs.array_shape = blot_shape
740+
pixel_area = compute_image_pixel_area(blot_wcs)
741+
pix_ratio = np.sqrt(pixflux_area / pixel_area)
757742

758-
for single_file in single_files:
759-
with datamodels.open(single_file) as single_model:
760743
blotted = gwcs_blot(
761-
median_data=single_model.data.astype(np.float32),
762-
median_wcs=single_model.meta.wcs,
744+
median_data=mosaic_data,
745+
median_wcs=mosaic_wcs,
763746
blot_shape=blot_shape,
764747
blot_wcs=blot_wcs,
765748
pix_ratio=pix_ratio,
766749
fillval=self.blot_fillval,
767750
)
768751

769-
out_name = single_file.replace("_i2d_single.fits", "_blot_det.fits")
770-
blot_model = datamodels.ImageModel(data=blotted)
771-
blot_model.update(ref_model)
772-
blot_model.meta.wcs = copy.deepcopy(blot_wcs)
773-
save_file(blot_model, out_name=out_name, dr_version=self.dr_version)
774-
blot_model.close()
775-
776-
log.info(
777-
" %s -> %s",
778-
os.path.basename(single_file),
779-
os.path.basename(out_name),
780-
)
752+
out_name = crf_file.replace("_crf.fits", "_i2d_blot.fits")
753+
blot_model = datamodels.ImageModel(data=blotted)
754+
blot_model.update(crf_model)
755+
blot_model.meta.wcs = copy.deepcopy(blot_wcs)
756+
save_file(blot_model, out_name=out_name, dr_version=self.dr_version)
757+
blot_model.close()
758+
759+
log.info(
760+
" mosaic -> %s",
761+
os.path.basename(out_name),
762+
)
781763

782764
gc.collect()
783765

0 commit comments

Comments
 (0)