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-10299: Apply crosstalk correction in decam #50

Merged
merged 3 commits into from Mar 26, 2018
Merged

Conversation

mrawls
Copy link
Contributor

@mrawls mrawls commented Mar 13, 2018

No description provided.

@@ -465,6 +466,9 @@ def run(self, ccdExposure, bias=None, linearizer=None, dark=None, flat=None, def

@return a pipeBase.Struct with field:
- exposure
otherDataRef : `lsst.daf.persistence.butlerSubset.ButlerDataRef`
optional dataRef, e.g., needed for DECam crosstalk correction
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't it possible to derive the exposure-based dataRef from the CCD-based dataRef?
And I believe the run method is forbidden from receiving a dataRef.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're correct that run methods generally shouldn't receive a dataRef, as per RFC-352. I've added this special case because I need to extract data from other CCDs within DecamCrosstalkTask. I use the dataRef to get a Butler and retrieve one or more different exposures with different ccdnums as needed. It's not clear to me how I could move this into a runDataRef method as suggested by RFC-352, but I'm open to suggestions.

I also thought I'd preserved old-style docstrings here for consistency (this was before the AHM hack day doc-venture), so I'm impressed you found this briefly-existing numpydoc version :)

Copy link
Contributor

@PaulPrice PaulPrice Mar 14, 2018

Choose a reason for hiding this comment

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

I think this will work:

def getVisitSensorRefs(sensorRef):
    """Return a data reference for all sensors in the visit

    Parameters
    ----------
    sensorRef : `lsst.daf.persistence.ButlerDataRef`
        Data reference for a single sensor.

    Returns
    -------
    visitSensorRefs : `list` of `lsst.daf.persistence.ButlerDataRef`
        Data references for all sensors in the visit.
    """
    butler = sensorRef.getButler()
    visitKeys = butler.getKeys("raw", "visit")
    dataId = sensorRef.dataId.copy()
    for kk in set(dataId.keys()).difference(set(visitKeys)):
        dataId.pop(kk)
    visitRef = butler.dataRef("raw", "visit", dataId)
    return [rr for rr in visitRef.subItems("ccd")]

Copy link
Contributor

Choose a reason for hiding this comment

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

The established pattern is that runDataRef loads everything that's required and passes that to the run method. I am concerned that it's naive (because loading the entire focal plane at once for every CCD is not good), but them's the rules. You'll need to get @RobertLuptonTheGood to sign off on your design.

Copy link
Contributor

Choose a reason for hiding this comment

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

Note that the decision on RFC-352 was "We will permit exceptions to that, by means of RFC — anybody who is (re-)designing or implementing a task, and who doesn't feel that run is appropriate, can file an RFC to explain why and get an exemption." We haven't fixed up our existing cases either.

Your suggested getVisitSensorRefs() seems to have its own problems of butler dataId manipulation.

Choose a reason for hiding this comment

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

What's the conclusion here? I agree with John that there are cases when we might need to pass objects that look a bit like dataRefs around (reference catalogues are one), but I didn't see an RFC to permit it here.

Why is this in DecamCrosstalkTask rather than generalising the cross-talk correction code?

Copy link
Contributor

Choose a reason for hiding this comment

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

Hey folks — just to be clear, the aim behind this PR was for @mrawls to iterate with @SimonKrughoff on the approach she should take here. As of now, that discussion is still ongoing; let's see how it converges. Should any RFCs or other special measures be needed, rest assured that everything will be done by the book.

Copy link
Contributor

Choose a reason for hiding this comment

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

I believe the conclusion is to add a (noop) method called prepCrosstalk to the default CrosstalkTask. Then they will override that in the DECam version to assemble need information. That's a generic solution that other crosstalk implementations can use.

Copy link
Contributor

@SimonKrughoff SimonKrughoff left a comment

Choose a reason for hiding this comment

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

There may be another argument to the run method, but I don't think it has to be a data ref.

@@ -439,7 +439,8 @@ def readIsrData(self, dataRef, rawExposure):
def run(self, ccdExposure, bias=None, linearizer=None, dark=None, flat=None, defects=None,
fringes=None, bfKernel=None, camera=None,
opticsTransmission=None, filterTransmission=None,
sensorTransmission=None, atmosphereTransmission=None):
sensorTransmission=None, atmosphereTransmission=None,
otherDataRef=None):
Copy link
Contributor

Choose a reason for hiding this comment

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

The way you've implemented the decam crosstalk correction, I don't think that modifications here are necessary. I think this should be reverted except to add a prepCrosstalk method to the CrosstalkTask base class.

Copy link
Contributor

Choose a reason for hiding this comment

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

That method would be called in the RunDataRef method on IsrTask. You would still have to pass through the source data object, but then it wouldn't be opaque.

# immediate=True required for functors and linearizers are functors; see ticket DM-6515
linearizer = dataRef.get("linearizer", immediate=True) if self.doLinearize(ccd) else None
darkExposure = self.getIsrExposure(dataRef, "dark") if self.config.doDark else None
flatExposure = self.getIsrExposure(dataRef, "flat") if self.config.doFlat else None
flatExposure = self.getIsrExposure(dataRef, self.config.flatDataProductName) \
if self.config.doFlat else None
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm confused why this is in a ticket for adding crosstalk. It seems to be complicating the issue.

Also, it seems odd to only generalize two of them. Why not also generalize dark?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This came up when I was writing a test which calls DecamCpIsrTask, which had its own copy-pasted readIsrData method for the specially-named cpBias and cpFlat issue alone. It was slowly diverging from ip_isr's readIsrData which is what I wanted to use anyway, so instead of perpetuating the copy-paste cycle I thought it best to just add this fix. (There aren't such a thing as cpDarks so I let it be.)

Copy link
Contributor

Choose a reason for hiding this comment

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

I see. Please add the generalization for dark. I think it would be pretty confusing to someone who uses this to have access to only two of the three.

self.crosstalk.run(ccdExposure, otherDataRef)
if crosstalkSources is not None:
# crosstalkSources is used by DECam
self.crosstalk.run(ccdExposure, crosstalkSources)
else:
self.crosstalk.run(ccdExposure)
Copy link
Contributor

Choose a reason for hiding this comment

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

I think you should add the crosstalkSources to the default CrosstalkTask. It can default to None in the base class.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Think I've got this now.

Copy link
Contributor

@SimonKrughoff SimonKrughoff left a comment

Choose a reason for hiding this comment

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

I'm happy with this if the one simplification I mention is implemented.

# crosstalkSources is used by DECam
self.crosstalk.run(ccdExposure, crosstalkSources)
else:
self.crosstalk.run(ccdExposure)
Copy link
Contributor

Choose a reason for hiding this comment

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

This can now be collapsed to:

if self.config.doCrosstalk:
    self.crosstalk.run(ccdExposure, crosstalkSources)

Note that only obs_decam presently has a prepCrosstalk method
which returns crosstalkSources that are not None.

Correspondingly, CrosstalkTask's run now accepts crosstalkSources
but expects them to be None by default.

Includes some flake8 cleanups and numpydoc docstrings.
This is useful for using community pipeline bias and flats
with obs_decam, which are called cpBias and cpFlat. For
completeness, we include darkDataProductName too.
@mrawls mrawls merged commit e427493 into master Mar 26, 2018
@ktlim ktlim deleted the tickets/DM-10299 branch August 25, 2018 06:44
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.

None yet

6 participants