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
Conversation
29cedfe
to
b49c4eb
Compare
b49c4eb
to
6e44ebf
Compare
""" | ||
detectors = ListField[int]( | ||
doc="A list containing the detectors IDs.", | ||
default=None, |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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", |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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): |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
det_id : int | |
det_id : `int` |
det_id : int | ||
The detector ID. | ||
|
||
detectors_focal_plane_regions : dict |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 " |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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): |
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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, | ||
) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
6e44ebf
to
281496d
Compare
from lsst.pipe.base.connectionTypes import Input, Output | ||
from lsst.pipe.tasks.coaddBase import subBBoxIter |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 | ||
|
There was a problem hiding this comment.
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?
281496d
to
8616186
Compare
8616186
to
fb1e298
Compare
No description provided.