Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DM-35135: cpCombine partial reads interferes with amplifier-level scaling #136

Merged
merged 1 commit into from
Jun 9, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 13 additions & 6 deletions python/lsst/cp/pipe/cpCombine.py
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ def getSize(self, dimList):
raise RuntimeError("Inconsistent dimensions: %s" % dim)
return dim.pop()

def applyScale(self, exposure, scale=None):
def applyScale(self, exposure, bbox=None, scale=None):
"""Apply scale to input exposure.

This implementation applies a flux scaling: the input exposure is
Expand All @@ -404,17 +404,24 @@ def applyScale(self, exposure, scale=None):
----------
exposure : `lsst.afw.image.Exposure`
Exposure to scale.
bbox : `lsst.geom.Box2I`
BBox matching the segment of the exposure passed in.
scale : `float` or `list` [`float`], optional
Constant scale to divide the exposure by.
"""
if scale is not None:
mi = exposure.getMaskedImage()
if isinstance(scale, list):
# Create a realization of the per-amp scales as an
# image we can take a subset of. This may be slightly
# slower than only populating the region we care
# about, but this avoids needing to do arbitrary
# numbers of offsets, etc.
scaleExp = afwImage.MaskedImageF(exposure.getDetector().getBBox())
for amp, ampScale in zip(exposure.getDetector(), scale):
ampIm = mi[amp.getBBox()]
ampIm /= ampScale
else:
mi /= scale
scaleExp.image[amp.getBBox()] = ampScale
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this creates a fake full exposure and fills it with the amp scale values?

And I guess the performance penalty is that it has to do this every time we call applyScale? Will the scale_list be different for every input exposure, or is there some caching that could be done? (And feel free to tell me that this would be premature optimization anyway...)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's correct. I thought of trying to be clever, but that seems like I'm just making more places to get geometry wrong.
We do instantiate a new full-size image for each applyScale call, which means we make (Nrows / NsubRows) copies of the same image for each input. This isn't optimal, but pulling it out of the loop will cause the same memory issues the partial reads fixed. The scales are different for each input exposure, so I'm not sure there's any way to optimize there.

scale = scaleExp[bbox]
mi /= scale

@staticmethod
def _subBBoxIter(bbox, subregionSize):
Expand Down Expand Up @@ -472,7 +479,7 @@ def combine(self, target, expHandleList, expScaleList, stats):
images = []
for expHandle, expScale in zip(expHandleList, expScaleList):
inputExp = expHandle.get(parameters={"bbox": subBbox})
self.applyScale(inputExp, expScale)
self.applyScale(inputExp, subBbox, expScale)
images.append(inputExp.getMaskedImage())

combinedSubregion = afwMath.statisticsStack(images, combineType, stats)
Expand Down