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-40763: Modify region definition config in extended_psf #837

Merged
merged 1 commit into from Oct 24, 2023

Conversation

bazkiaei
Copy link
Contributor

No description provided.

@bazkiaei bazkiaei force-pushed the tickets/DM-40763 branch 2 times, most recently from 29cedfe to b49c4eb Compare October 8, 2023 06:24
@bazkiaei bazkiaei marked this pull request as ready for review October 8, 2023 06:30
"""
detectors = ListField[int](
doc="A list containing the detectors IDs.",
default=None,
Copy link
Member

Choose a reason for hiding this comment

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

If the default value is None, then you should also set optional=True so that the default value is a valid one.

Copy link
Member

Choose a reason for hiding this comment

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

Is there a reason for this to be an instance of a Config?

Copy link
Member

Choose a reason for hiding this comment

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

Ah, is that because you want to specify this as the itemtype in the ConfigDictField?

Copy link
Contributor

Choose a reason for hiding this comment

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

Added optional=True as suggested. You're correct on your second point here.

Copy link
Member

Choose a reason for hiding this comment

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

Wait, in find_region_for_detector, you check for membership without any checks (I can't tell yet if it has been checked previously before it gets there). May be this should be an empty list by default instead of None (in which case, you can remove the optional=True.

Copy link
Contributor

Choose a reason for hiding this comment

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

That's a nice idea. Modified as you suggest.

@@ -280,7 +325,7 @@ class StackBrightStarsConfig(Config):
)
stacking_statistic = ChoiceField[str](
doc="Type of statistic to use for stacking.",
default="MEANCLIP",
default="MEDIAN",
Copy link
Member

Choose a reason for hiding this comment

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

This change seems out of scope for this ticket. It should remain the same in this ticket.

Copy link
Contributor

Choose a reason for hiding this comment

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

It was during testing of this ticket that we discovered that MEDIAN produces superior results. As it's only a 1-line change, I thought it best to put it here. An alternative is to move it to a separate commit. We can also remove it to put this 1-liner on another ticket if you think it optimal.

Copy link
Member

Choose a reason for hiding this comment

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

Definitely a separate commit please. If you could put on Jira what you mean by superior results (maybe you already have), that would be good.

Choose a reason for hiding this comment

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

Definitely more details, please! Stacking can be subtle, and medians are deceptively tempting

Copy link
Member

Choose a reason for hiding this comment

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

My preference for a different ticket also comes when we look at the CHANGELOG between versions, it wouldn't be obvious at all that a (perhaps significant) algorithmic change was made here looking at the title of the ticket.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'll go with the median opinion on this thread and relegate this change to a subsequent ticket.

from lsst.pipe.base import PipelineTaskConfig, PipelineTaskConnections, Struct, Task
from lsst.pipe.tasks.coaddBase import subBBoxIter
from lsst.pipe.base.connectionTypes import Input, Output


def find_region_for_detector(det_id, detectors_focal_plane_regions):
Copy link
Member

Choose a reason for hiding this comment

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

This could become a static method of ExtendedPsf unless you plan on using it more generally as a utility function (which I don't think is the case)

Copy link
Contributor

Choose a reason for hiding this comment

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

It's also called inside MeasureExtendedPsfTask. I think we'd imagined this may be a somewhat convenient utility script allowing for a trivial lookup between detector ID and region ID. Perhaps that's not required?

Copy link
Member

Choose a reason for hiding this comment

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

Oh, I see it now. I guess it's fine as it is.


Parameters
----------
det_id : int
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
det_id : int
det_id : `int`

det_id : int
The detector ID.

detectors_focal_plane_regions : dict
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
detectors_focal_plane_regions : dict
detectors_focal_plane_regions : `dict` [`str`, `int`]

List of detector IDs that define the focal plane region over which this
extended PSF model has been built (and can be used).
"""

extended_psf_image: MaskedImageF
detector_list: List[int]
region_detectors: DetectorsInRegion
Copy link
Member

Choose a reason for hiding this comment

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

Doesn't affect the code in runtime, but shouldn't this be ListField as well, based on the above. It's inconsistent with the docstring above.

Copy link
Contributor

Choose a reason for hiding this comment

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

Actually, I think the docstring is incorrect here. Modifying here (and elsewhere) accordingly to lsst.pipe.tasks.extended_psf.DetectorsInRegion.

doc=(
"Mapping from detector IDs to focal plane region names. If empty, a constant extended PSF model "
"Mapping from focal plane region names to detector IDs. If empty, a constant extended PSF model "
Copy link
Member

Choose a reason for hiding this comment

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

There should be a check in a validate method so that the values of the dictionaries don't contain a shared entity. This is mainly to prevent a user error when dealing with 100s of detectors that a detector is initially marked as say "W" and then marked as "SW" without removing it from "W". Or could a single detector be in two regions? With the previous version, such a thing would have not been possible.

Copy link
Member

Choose a reason for hiding this comment

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

Also, I see that the region_name gets homogenized here. So yet another job for the validate method could be to ensure there are no keys like "NW" and "nw". That's a less likely user error compared to what I mentioned in the previous comment.

Copy link
Contributor

Choose a reason for hiding this comment

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

There's actually nothing preventing a detector being used in multiple regions, and this is an interesting point to raise. I actually don't know if allowing or preventing such an action is useful scientifically.

Copy link
Contributor

Choose a reason for hiding this comment

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

I can imagine special cases, for example, a camera with a central detector and the user wishes to use the central detector in all sectors of an equally segmented focal plane. Is this optimal? I'm not sure - but I can imagine some may wish to experiment with it?

Copy link
Member

Choose a reason for hiding this comment

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

I see the advantage, but I can't be sure if that's the right thing to do. It should be documented that a detector could be in more than one region and we just have to assume that that's what the user intended.

Copy link
Contributor

Choose a reason for hiding this comment

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

Added a note on this into the associated docstring.

# corresponding focal plane region, make sure we keep track of
# it (eg to raise a warning only once)
if self.config.detectors_focal_plane_regions:
self.detectors_focal_plane_regions = self.config.detectors_focal_plane_regions
Copy link
Member

Choose a reason for hiding this comment

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

I don't think an if statement is required. It's better to have an empty dict (the default value) than have an AttributeError later.

Copy link
Contributor

Choose a reason for hiding this comment

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

Agreed - removed the if and unindented the config setting.

@@ -482,10 +520,24 @@ def select_detector_refs(self, ref_list):
region_ref_list[region_name].append(dataset_handle)
return region_ref_list

def _find_detectors(self, bss_ref_list):
Copy link
Member

Choose a reason for hiding this comment

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

Looks like this should just be a staticmethod.

Copy link
Contributor

Choose a reason for hiding this comment

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

Added the @staticmethod decorator and removed self parameter. However, I'm not sure we need this method any longer, and it may be a vestigial artefact from our development phase on this ticket. Keen to hear from @bazkiaei on this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, you are right @leeskelvin . This method was created to generate a region including all existing detectors as the default region when the user do not provide regions, but since we changed our approach for that we do not need this anymore and we can remove this.

raise KeyError(
"Detector %d is not included in any focal plane region.",
det_id,
)
Copy link
Member

Choose a reason for hiding this comment

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

I don't know how much it matters in practice, but it might be better invert the mapping once upfront (detector ID as key and region as value, as it was before) insead of looping over a dict and then through a list for every query.

Copy link
Contributor

Choose a reason for hiding this comment

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

In practice it seemingly adds very little extra runtime. But yes, I agree a cleaner way would be to construct an inverted lookup internal to these classes at runtime. We have a future refactor ticket which would be a good place to defer this effort to, so I'll make a note of that there.

from lsst.pipe.base.connectionTypes import Input, Output
from lsst.pipe.tasks.coaddBase import subBBoxIter
Copy link
Member

Choose a reason for hiding this comment

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

The rest of the imports appear to be sorted as isort would sort them, so pushing this down by one line seems unnecessary.

Copy link
Contributor

Choose a reason for hiding this comment

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

This change was made by isort, so I suspect this is the correct location for this import in isort-land.

Copy link
Member

Choose a reason for hiding this comment

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

Oh, my bad. I misread it initially.

"'detectors_focal_plane_regions'",
)
self.regionless_dets.append(det_id)
self.regionless_dets.append(detector_id)
Copy link
Member

Choose a reason for hiding this comment

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

Are we expecting the code to execute this except block frequently, if only a few detectors are assigned to regions? Is the typical use case expected to have all (at least most) detectors assigned to regions, or only for a handful number of detectors?

Copy link
Contributor

Choose a reason for hiding this comment

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

This except will only trigger if a given detector was not specified by the user in any of the specified regions. This should hopefully be a rare case - i.e., if I'm splitting a visit into regions, I should hope to specify all detectors. The obvious warning should also be useful in this case, in case this is an oversight by the end-user.

if detector_id not in detectors:
detectors.append(detector_id)
return detectors

Copy link
Member

Choose a reason for hiding this comment

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

This method is going away, right?

@leeskelvin leeskelvin merged commit 3b70695 into main Oct 24, 2023
2 checks passed
@leeskelvin leeskelvin deleted the tickets/DM-40763 branch October 24, 2023 22:16
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

4 participants