Skip to content

DM-54625: Update preconvolution with recent diffim improvements#453

Merged
isullivan merged 22 commits intomainfrom
tickets/DM-54625
May 6, 2026
Merged

DM-54625: Update preconvolution with recent diffim improvements#453
isullivan merged 22 commits intomainfrom
tickets/DM-54625

Conversation

@isullivan
Copy link
Copy Markdown
Contributor

No description provided.

@isullivan isullivan changed the title DM-54625: Support partial outputs for detection on the preconvolved diffim DM-54625: Update preconvolution with recent diffim improvements Apr 22, 2026
@isullivan isullivan force-pushed the tickets/DM-54625 branch 4 times, most recently from 8a2e2ab to cd5766b Compare April 27, 2026 23:54
Copy link
Copy Markdown
Member

@kfindeisen kfindeisen left a comment

Choose a reason for hiding this comment

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

As forewarned, lots of comments. Some of the factoring I'm asking for is pre-existing and should probably be deferred to a later work item, but I suggest at least streamlining the "when to do CR removal vs. background removal" logic. And I really don't understand how the Diff_scoreExp vs. Diff_scoreTempExp swap doesn't break things.

Comment thread python/lsst/ip/diffim/detectAndMeasure.py
Comment thread python/lsst/ip/diffim/makeKernel.py Outdated
Comment thread python/lsst/ip/diffim/subtractImages.py Outdated
Comment thread python/lsst/ip/diffim/subtractImages.py Outdated
Comment thread python/lsst/ip/diffim/subtractImages.py Outdated
Comment thread python/lsst/ip/diffim/detectAndMeasure.py Outdated
Comment thread python/lsst/ip/diffim/detectAndMeasure.py Outdated
Comment thread python/lsst/ip/diffim/detectAndMeasure.py Outdated
x0 = 123
y0 = 456
kernelSize = 31
templateBorderSize = kernelSize//2
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Again, if you're going to have a comment for one copy of the code, you probably need it everywhere. (Can the copies be factored out?)

Comment thread tests/test_subtractTask.py
@isullivan
Copy link
Copy Markdown
Contributor Author

This is ready for another round of review. The commits that start "fixup" are intended to be squashed into the referenced commit. I left them pre-rebasing so that the changes are visible.

isullivan added 2 commits May 4, 2026 12:21
Also make sure to use a consistent PSF size for preconvolution and decorrelation.
@isullivan isullivan force-pushed the tickets/DM-54625 branch from 069011b to 930510f Compare May 4, 2026 19:24
Copy link
Copy Markdown
Member

@kfindeisen kfindeisen left a comment

Choose a reason for hiding this comment

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

Mostly minor comments, but I'm still concerned about the change in role for *Diff_scoreExp -- something you said was a bug earlier? If it is, it would affect lsst/ap_pipe#269 as well.

Comment thread python/lsst/ip/diffim/makeKernel.py Outdated
# the size was auto-computed. An explicit ``scienceFwhmPix`` is
# assumed already to reflect the preconvolved effective width.
if preconvolved:
scienceFwhmPix *= np.sqrt(2)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This code runs if scienceFwhmPix was passed in but templateFwhmPix was None. Was that the intent? Same with the indented block on lines 213-214 and the version moved into _checkPsfWidths.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, that was intentional. Both scienceFwhmPix and templateFwhmPix have to be specified or else both are recalculated, and in that case the preconvolved check needs to be made.

Comment on lines +1411 to +1417
# Background is measured on the difference image, and then
# subtracted from both the difference and score images.
# The preconvolution kernel is normalized to 1, so the
# same background level applies to both.
background = self.subtractInitialBackground.run(difference.clone()).background
detectionScoreExposure = scoreExposure.clone()
detectionScoreExposure.image -= background.getImage()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I still think it's confusing to talk about subtracting the background from the difference image when you're going out of your way to not do that. Maybe just have the last two lines (with "both difference and score images")?

_detection_wrapper(positive=True)
_detection_wrapper(positive=False)

def test_mask_cosmic_rays(self):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Redefinition of method from line 417.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It is not obvious, but this is a different version than the first one. I'll update the docstring to mention the difference.

backgroundModel.setParameters([-p for p in modelParams])
invertedBackground = backgroundModel.clone()
invertedBackground.setParameters([-p for p in backgroundModel.getParameters()])
backgroundModel = invertedBackground
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Could you expand the commit message to mention that this is for legibility? Right now it just says "Clone the background model before inverting", which implies that there's some obvious(?) reason why you have to do that (e.g., a bug fix).

maglim = np.nan
return np.nan
self.log.info("Unable to evaluate PSF, using fallback FWHM %f", fallbackPsfSize)
psf_area = np.pi*(fallbackPsfSize/2)**2
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Could you mention the return-from-finally in the commit message, so that it's clearer what's going on?

self._prepareInputs(template, science, visitSummary=visitSummary)

convolveTemplate = self.chooseConvolutionMethod(template, science)
self.matchedPsfSize = self.sciencePsfSize if convolveTemplate else self.templatePsfSize
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should the commit message read "exactly once" instead of "only once"?

Comment thread python/lsst/ip/diffim/subtractImages.py Outdated
Sources from the input catalog that were used to construct the
PSF-matching kernel.
"""
self.usePreconvolution = False
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Something seems fishy here. The only references that set usePreconvolution are:

  • AlardLuptonSubtractTask.run sets self.usePreconvolution = False
  • AlardLuptonPreconvolveSubtractTask.run sets self.usePreconvolution = True
  • SimplifiedSubtractTask.run sets self.usePreconvolution = False

If the goal is to define usePreconvolution as a function of the implementation class instead of the arguments, then it would be better to set as a class constant, where at least it's clear that there won't be any side effects or weird interactions (e.g., what if AlardLuptonPreconvolveSubtractTask.run called super().run(), unlikely as it is?). It also avoids any potential problems from usePreconvolution being referenced before run is called.

(TBH, making this a class variable/constant still feels wrong, but I can't come up with a better idea at the moment.)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I've moved usePreconvolution to a class variable, and checked that it is used instead of other flags where possible.

Comment thread python/lsst/ip/diffim/subtractImages.py Outdated
"""

if self.usePreconvolution:
raise RuntimeError("Incorrect matching kernel calculation configured.")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Be more specific about how it's incorrect?

self.preconvolvedSciencePsfSize = self.sciencePsfSize*np.sqrt(2)
self.log.info("Preconvolved science PSF FWHM: %f pixels", self.preconvolvedSciencePsfSize)
self.metadata["preconvolvedSciencePsfSize"] = self.preconvolvedSciencePsfSize
self.matchedPsfSize = self.sciencePsfSize*np.sqrt(2)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Duplicate of line 1228 (though I think this spot is better, since it's immediately before first use).

Comment on lines +667 to +676
if self.config.doSubtractBackground:
# Run background subtraction before clearing the mask planes
detectionExposure = difference.clone()
background = self.subtractInitialBackground.run(detectionExposure).background
else:
detectionExposure = difference
background = afwMath.BackgroundList()
# Run background subtraction (if configured) before clearing the mask
# planes.
detectionExposure, background = self._subtractInitialBackground(differenceExposure=difference)

self._prepareInputs(detectionExposure)

if self.config.doFindCosmicRays and not self.config.doSubtractBackground:
# Detect and interpolate over any remaining cosmic rays
# This only needs to run once, right before the final detection.
self.findAndMaskCosmicRays(detectionExposure)
# Detect and interpolate over any remaining cosmic rays. When
# background subtraction is enabled, this is deferred to the second
# pass so it runs only once on a properly background-subtracted image.
self._findInitialCosmicRays(differenceExposure=difference)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This isn't what I had in mind; I thought it would be simpler to have something like:

# Preprocess or skip
if doSubtractBackground:
    detectionExposure = copy
    background = rough background
    self._prepareInputs(detectionExposure)  # Only run on first pass, but not second?
    results = detection.run(detectionExposure)
    background = final background
else:
    background = empty
    self._prepareInputs(difference)  # Run on only pass

# Final processing
self.findAndMaskCosmicRays(difference)  # current code aliases as detectionExposure if not doSubtractBackground
results = detection.run(difference)

What you're proposing here obfuscates the multiple doSubtractBackground checks instead of eliminating them.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I've pushed a new version that refactors background subtraction and cosmic ray masking.

Copy link
Copy Markdown
Member

@kfindeisen kfindeisen left a comment

Choose a reason for hiding this comment

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

With the connections question resolved, I have no major objections.

isullivan added 12 commits May 5, 2026 13:54
Otherwise the results are unstable when the science image has a psfSize of 2.0 in the unit tests and the template has a psfSize of 2.3 to 2.4.  This change makes the basis a smooth transition instead of a sudden jump.
Mostly for readability, but this does prevent modifying the original background model if the function is called in a notebook environment.
The return statement inside the finally block is bad practice and suppressed error messages.
@isullivan isullivan force-pushed the tickets/DM-54625 branch 2 times, most recently from 0faf38c to 229d253 Compare May 6, 2026 18:32
This does not suffer from the same bug as setting the image plane to 0 for masked pixels, since the peak is removed after detection.
isullivan added 6 commits May 6, 2026 14:49
Consolidate setting common values, but there should be no behavior changes.
Also remove the redundant calculation of the kernel source list in subtractImages - makeCandidateList was effectively called twice.
Use common code for both standard and Score versions.
@isullivan isullivan force-pushed the tickets/DM-54625 branch from 229d253 to 610e70c Compare May 6, 2026 21:52
@isullivan isullivan merged commit 3dd0db4 into main May 6, 2026
3 of 4 checks passed
@isullivan isullivan deleted the tickets/DM-54625 branch May 6, 2026 22:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants