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-46720: Add uncalibrateImage method to afw.image.PhotoCalib #748

Merged
merged 6 commits into from
Oct 31, 2024

Conversation

parejkoj
Copy link
Contributor

No description provided.

@parejkoj parejkoj force-pushed the tickets/DM-46720 branch 3 times, most recently from 729c6ff to 0aeacf3 Compare October 18, 2024 00:23
include/lsst/afw/image/PhotoCalib.h Outdated Show resolved Hide resolved
auto eigenFluxVar = ndarray::asEigen<Eigen::ArrayXpr>(fluxVar);
auto eigenInstFlux = ndarray::asEigen<Eigen::ArrayXpr>(instFlux);
auto eigenOut = ndarray::asEigen<Eigen::ArrayXpr>(out);
eigenOut = (eigenFluxVar / eigenFlux.square() - (scaleErr / eigenFlux * eigenInstFlux).square()) *
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
eigenOut = (eigenFluxVar / eigenFlux.square() - (scaleErr / eigenFlux * eigenInstFlux).square()) *
eigenOut = (eigenFluxVar / eigenFlux.square() - (scaleErr / (eigenFlux * eigenInstFlux).square())) *

I don't remember the C++ precedence rules well enough to know if what you had was already correct, but this ensures it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Uh oh: the above is the exact expression used in the ADU->nJy version:

    eigenOut = eigenFlux.square() *
               (eigenInstFluxVar / eigenInstFlux.square() + (scaleErr / eigenFlux * eigenInstFlux).square());

Have we been doing that incorrectly the whole time? Because your change here does switch the precedence. I've never been sure whether that calibration error is a variance or not. In jointcal just copied it over from PhotoCalTask: https://github.com/lsst/pipe_tasks/blob/main/python/lsst/pipe/tasks/photoCal.py#L418

Copy link
Member

@TallJimbo TallJimbo Oct 18, 2024

Choose a reason for hiding this comment

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

Oh, wait, I read that incorrectly. Your original version was correct (but not obviously so), and I was trying to write it as

    eigenOut = (eigenFluxVar / eigenFlux.square() - (scaleErr / (eigenFlux / eigenInstFlux)).square()) *

(note the divide rather than multiply inside the new parentheses).

So we've got a strange test tolerance to explain.

Copy link
Member

@TallJimbo TallJimbo Oct 18, 2024

Choose a reason for hiding this comment

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

Oh, and I apparently put the closing parentheses in the wrong place in my original suggestion, too (now fixed in the post above). Here's a fully corrected suggestion:

Suggested change
eigenOut = (eigenFluxVar / eigenFlux.square() - (scaleErr / eigenFlux * eigenInstFlux).square()) *
eigenOut = (eigenFluxVar / eigenFlux.square() - (scaleErr / (eigenFlux / eigenInstFlux)).square()) *

Copy link
Contributor Author

@parejkoj parejkoj Oct 18, 2024

Choose a reason for hiding this comment

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

Hmmm... I believe the original expression evaluates to (scaleErr / eigenFlux) * eigenInstFlux, because / and * have the same precedence, and are evaluated left-to-right, not the version that you wrote above.

EDIT: Ugh, sorry, yes, I think what you wrote is the same, dang it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

But unfortunately, changing to make that flux/instFlux division explicit didn't fix the numerical problem.

Copy link
Member

@TallJimbo TallJimbo Oct 18, 2024

Choose a reason for hiding this comment

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

The way I think about it - which may be worth writing out more explicitly via another temporary variable for scale - is

scale = flux / instFlux
instFluxVar = instFlux**2 * ((fluxVar / flux**2) - (scaleVar / scale**2))

Copy link
Member

Choose a reason for hiding this comment

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

Would it be possible to modify the numbers going into the test make sure we're not adding/subtracting a big number to/from a small number here?

Copy link
Contributor Author

@parejkoj parejkoj Oct 18, 2024

Choose a reason for hiding this comment

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

As we discussed on slack, I think we both now agree that the PhotoCalib.calibrationErr is supposed to be sigma (that's what comes out of PhotoCalTask), although my docstring is not really specific enough, sadly.

http://doxygen.lsst.codes/stack/doxygen/x_mainDoxyDoc/classlsst_1_1afw_1_1image_1_1_photo_calib.html#adb7c4cc78845e8d64666b3751cf5a51b

uncalibrated = photoCalib.uncalibrateImage(result)
# High rtol because our specific choice of calib values results in a
# very-close-to-but-not-1 value, which results in poor round-tripping.
self.assertMaskedImagesAlmostEqual(maskedImage, uncalibrated, rtol=4e-2)
Copy link
Member

Choose a reason for hiding this comment

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

I'm hoping those parentheses in the calculation were important and you can shrink this after you add them, because that is indeed an extremely high tolerance.

This test should have always used the subimage, not the full image.
This doesn't change the math, just makes it more obvious what the
calculation is.
The old values caused high numerical error in some image-related
calculations; these reduce that error, which I think is inherent to the
math (small - big number and float uncertainty squared problems).
@parejkoj
Copy link
Contributor Author

@TallJimbo : please have another look. I changed the test values and that lets the various calculations match within typical uncertainty. There's a "small number minus big number" problem that will always be there for some input values.

@TallJimbo
Copy link
Member

Looks good!

@parejkoj parejkoj merged commit 9e67688 into main Oct 31, 2024
2 checks passed
@parejkoj parejkoj deleted the tickets/DM-46720 branch October 31, 2024 17:30
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