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

Refactor image/stamp workflow #424

Open
wants to merge 44 commits into
base: main
Choose a base branch
from

Conversation

g-braeunlich
Copy link
Contributor

No description provided.

@g-braeunlich g-braeunlich self-assigned this Sep 28, 2023
@g-braeunlich g-braeunlich force-pushed the u/g-braeunlich/refactor-image branch 2 times, most recently from e58012d to c145f0c Compare October 10, 2023 12:34
@g-braeunlich g-braeunlich force-pushed the u/g-braeunlich/refactor-image branch from c145f0c to 1d7fefc Compare October 30, 2023 09:16
@g-braeunlich g-braeunlich force-pushed the u/g-braeunlich/refactor-image branch 2 times, most recently from acbac83 to 9f49df7 Compare November 30, 2023 12:45
@welucas2
Copy link
Collaborator

The latest commit aligns photon objects in the photon pooling pipeline with their positions in the original pipeline.

@welucas2 welucas2 self-assigned this Apr 29, 2024
@welucas2
Copy link
Collaborator

Almost there, but blocked by GalSim-developers/GalSim#1284.

@welucas2 welucas2 force-pushed the u/g-braeunlich/refactor-image branch from bdaadae to b786cb0 Compare June 5, 2024 13:10
@welucas2 welucas2 marked this pull request as ready for review June 5, 2024 13:32
@welucas2
Copy link
Collaborator

welucas2 commented Jun 5, 2024

This is finally open for review -- all comments welcome.

@welucas2 welucas2 force-pushed the u/g-braeunlich/refactor-image branch from 46461fb to eeb67dc Compare November 20, 2024 16:54
@welucas2
Copy link
Collaborator

I think this is ready for re-review now, though CI is failing with an error in test_stamp_bandpass_airmass from numpy coming through GalSim. I saw the same error a couple of weeks ago on an ARM system when using GalSim 2.6 which introduces its own utilities.least_squares(). I wasn't able to investigate further before the system went down and only came back up yesterday.

@welucas2
Copy link
Collaborator

I think there is actually one more commit incoming. I've realised that in edge cases where nominally bright photon objects' phot_flux falls below nbatch, we can end up with batches containing objects with 0 flux. I think I have a fix, by having these objects be treated as faint objects for batching purposes. I'll test this and if all is well push it tomorrow.

@welucas2
Copy link
Collaborator

OK, that should be it -- code open for review!

@welucas2 welucas2 requested a review from rmjarvis November 22, 2024 17:05
Copy link
Contributor

@rmjarvis rmjarvis left a comment

Choose a reason for hiding this comment

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

Thanks William. I think we're close.

I'd still like to have a separate PR that just changes the tests of the expected centers. I think the way to do that is to change the test to mostly your new code, but when calling (the old) XyToV, use sky_pos = img_wcs.toWorld(image_pos) and local_wcs = img_wcs.local(image_pos). I'm not sure what image_pos is exactly, but hopefully you can follow Geri's code to see what the implicit image_pos is there.

imsim/lsst_image.py Outdated Show resolved Hide resolved
offset_adjustment: a PositionD by which to offset each object
"""
for stamp in stamps:
offset = offset_adjustment
Copy link
Contributor

Choose a reason for hiding this comment

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

Probably should just call this parameter offset from the start.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Thanks! Doing this now.

Copy link
Collaborator

Choose a reason for hiding this comment

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

And now obsolete with the big simplification in offsetting the photons by stamp_center. I've removed the two static methods in photon_pooling.py dealing with the offset and it's now dealt with in stamp.py just using stamp_center.

Returns:
images: Tuple of the output stamps. In normal PhotonPooling usage these will actually be
PhotonArrays to be processed into images after they have been pooled following this step.
current_vars: Tuple of variables for each stamp (noise etc).
Copy link
Contributor

Choose a reason for hiding this comment

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

These should be the current noise variance in each image. var=variance, not variable.
It's not really relevant for most imsim usage, but if you draw a RealGalaxy, and especially if you then whiten it, the image gets some noise from that, so this mechanism helps GalSim from adding more noise than requested when adding the rest of the image noise.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Thanks for pointing this out. Is the actual usage of current_vars ok?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes. Just the doc string is wrong.

Comment on lines 313 to 315
batches = [
[dataclasses.replace(obj, phot_flux=np.floor(obj.phot_flux / nbatch)) for obj in phot_objects]
for _ in range(nbatch)]
Copy link
Contributor

Choose a reason for hiding this comment

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

A nifty (simpler?) way to get the total flux to come out right:

Suggested change
batches = [
[dataclasses.replace(obj, phot_flux=np.floor(obj.phot_flux / nbatch)) for obj in phot_objects]
for _ in range(nbatch)]
batches = [
[dataclasses.replace(obj,
phot_flux=(obj.phot_flux*(i+1))//nbatch - (obj.phot_flux*i)//nbatch)
) for obj in phot_objects]
for i in range(nbatch)]

Then you don't need the block below this.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Very nice -- thanks! Putting this in now.

imsim/stamp.py Outdated
def build_obj(stamp_config, base, logger):
"""Precompute all data needed to determine the rendering mode of an
object."""
stamp_type = stamp_config.get('type','LSST_Photons')
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think we want to have a default here. Rather we should make sure the user has correctly specified this type. So if stamp_config.get('type', None) != 'LSST_Photons':... then raise some kind of explanatory errror.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Thanks -- doing this now.

imsim/stamp.py Outdated

max_flux_simple = config.get('max_flux_simple', 100)
faint = self.nominal_flux < max_flux_simple
bandpass = base['bandpass']
Copy link
Contributor

Choose a reason for hiding this comment

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

duplicate

Copy link
Collaborator

Choose a reason for hiding this comment

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

D'oh! Thanks for spotting -- fixed.

imsim/stamp.py Outdated
img_pos = base["image_pos"]
obj_offset = base["stamp_offset"]
image.photons.x = image.photons.x + img_pos.x - obj_offset.x
image.photons.y = image.photons.y + img_pos.y - obj_offset.y
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 all this offset stuff could be simpler. Here you add img_pois - stamp_offset. Then later you add 0.5 for even sized images. So the net adjustment is

img_pos - stamp_offset + (0.5 if size%2==0)

I'm pretty sure this combination is equivalent to base["stamp_center"] based on the calculation of stamp_offset here. So I think you can just add stamp_center here and skip the later adjustments. Which honestly makes sense. These photon arrays are meant to be relative to the image center, which here is just the stamp center.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Very nice call. I've just tested and this gives identical output. Making this change now.

# positions the photons before they are pooled to be processed together.
if self.shift_photons and self.image_pos is not None:
photon_array.x += self.image_pos.x
photon_array.y += self.image_pos.y
Copy link
Contributor

Choose a reason for hiding this comment

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

Per my other comment, these should presumably also shift by stamp_center, not image_pos. Likely not a big difference, but might as well use the correct shift.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Should this also be done at the end of applyTo? e.g.

imSim/imsim/photon_ops.py

Lines 111 to 112 in a4c2d97

photon_array.x -= self.image_pos.x
photon_array.y -= self.image_pos.y
in main as it is now.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes. I think so.

Copy link
Collaborator

Choose a reason for hiding this comment

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

This is the last thing on my list. It means changing the API to RubinOptics and RubinDiffractionOptics. I think normal use is always through photon op registrations via the photon_op_type decorator and deserialize_* functions, which wouldn't need to change, but at least the tests in test_photon_ops.py do directly init the photon operators themselves. Is that ok to change?

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 if we preserve the user-facing code (i.e. how the config file specifies things), then it's ok to make changes to the implementation, even if these are technically API changes. But perhaps @jchiang87 or @cwwalter might want to weigh in about how strictly we want to preserve backwards-compatibility of the Python classes/functions.

help="Similar to -k of pytest / unittest: restrict tests to tests starting with the specified prefix.",
default="test_",
)
args = parser.parse_args()
Copy link
Contributor

Choose a reason for hiding this comment

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

I guess if you find it useful, go ahead and keep it. But if you were just copying from that file, I don't think this is necessary to keep.


The transform takes 2 vectors of shape (n,) and returns
a column major vector of shape (n,3).
"""

def __init__(self, local_wcs, icrf_to_field, sky_pos):
def __init__(self, local_wcs, icrf_to_field, img_wcs):
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 this doesn't need local_wcs anymore. If we're breaking the API (with the change in the last parameter), might as well clean it up and remove local_wcs as well.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Done. This also leaves some of the photon_velocity methods in RubinOptics, RubinDiffraction and RubinDiffractionOptics with unnecessary local_wcs and, now I look at them, also rng. Would you prefer those left alone or removed as well?

Copy link
Contributor

Choose a reason for hiding this comment

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

Might as well clean up all the obsolete API. If any of them seem like user-facing code, we should make this a deprecation rather than completely break old code. But I think most of these functions are internal, not user facing, so it should be ok.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I forgot to mention this in the meeting! Yes, everything looks to be internal to the photon ops themselves. rng can't be removed though, as it turns out, or it breaks RubinDiffractionOptics. Otherwise, tidied up as requested.

@welucas2
Copy link
Collaborator

welucas2 commented Dec 6, 2024

Assuming tests pass, that should be everything above. I've included changes to RubinOptics to use stamp_center rather than image_pos, changing the tests to fit, but let me know if you don't like that and I'll revert it.

I'll move on to seeing if I can make the old photon ops tests match the new ones as suggested.

@welucas2
Copy link
Collaborator

I've just created PR #484 which corrects the photon op tests to be self-consistent. There were actually a couple of inconsistencies in the tests here too, but working through them now means everything should be in alignment pre- and post-photon pooling.

I hope this means that once #484 is merged in, this can be, too.

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.

3 participants