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-32402: Add TractBuilders and sub-patch cell capability. #56
Conversation
c80cdd5
to
3cdb16a
Compare
python/lsst/skymap/cellInfo.py
Outdated
""" | ||
return self._sequentialIndex | ||
|
||
sequentialIndex = property(getSequentialIndex, 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.
I have not yet looked at the PR in detail, but I wanted to make one point up-front based on what I knew was coming: I think we should make all of these new properties snake_case. They're brand new, so there's no breakage from doing so, and there are no existing camelCase properties and relatively few camelCase public attributes, so the internal-to-this-package inconsistency wouldn't actually be too. The new cell_coadds
package that will make the most use of these will be snake_case throughout, so it'll be good to have more consistency with that, and of course it'd be much harder to change these anytime later.
One more side note: I think property(getSequentialIndex)
should work too, and seems a bit better than the explicit 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.
I was wondering about this, and now it is done.
3cdb16a
to
cf96cfa
Compare
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've left a few minor comments. I haven't looked super closely at everything - I gather a lot is copied from existing code in the package, and I saw some things move around, and I know Arun is reviewing, too.
python/lsst/skymap/patchInfo.py
Outdated
""" | ||
|
||
def __init__(self, index, innerBBox, outerBBox): | ||
def __init__(self, index, innerBBox, outerBBox, | ||
cellInnerDimensions=Extent2I(0, 0), cellBorder=0, |
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.
Extent2I
is actually mutable, so it shouldn't be used as a default argument (maybe someday I'll get around to DM-21148 and fix that, but don't hold your breath).
python/lsst/skymap/patchInfo.py
Outdated
self._cellInnerDimensions = cellInnerDimensions | ||
self._cellBorder = cellBorder | ||
if numCellsPerPatchInner == 0: | ||
self._numCells = Extent2I(0, 0) |
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.
Using an Extent2I
for a number of cells rather than something with dimensions of pixels doesn't quite feel right; if it's not a big inconvenience for arithmetic, it might be better to use a tuple or separate x, y attributes here (or that new named tuple).
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 numPatches was always Extent2I
(written by Russell Owen 10 years ago) it just wasn't documented. But as far as I can tell it was only used by anybody getting this property as a tuple except for one of the tests. So I'll make it the Index2D
named tuple.
python/lsst/skymap/baseSkyMap.py
Outdated
dtype=int, | ||
default=100, | ||
tractBuilder = tractBuilderRegistry.makeField( | ||
"Tract building algorithm", |
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.
"Tract building algorithm", | |
"Algorithm for creating the patches within a tract.", |
python/lsst/skymap/cellInfo.py
Outdated
from .detail import makeSkyPolygonFromBBox | ||
|
||
|
||
class CellInfo(): |
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.
class CellInfo(): | |
class CellInfo: |
python/lsst/skymap/patchInfo.py
Outdated
|
||
nx, ny = self.getNumCells() | ||
x = sequentialIndex % nx | ||
y = (sequentialIndex - x) // nx |
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 would have written this as just
y = sequentialIndex // nx
I'm not sure that's better, but they are the same, right, because x < nx
?
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'm just copying what was there before! But you are correct.
python/lsst/skymap/tractBuilder.py
Outdated
from .detail import Index2D | ||
|
||
__all__ = ["tractBuilderRegistry", "BaseTractBuilder", | ||
"LegacyTractBuilder", "CellTractBuilder"] |
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.
__all__
should go above all of the import
statements.
python/lsst/skymap/tractBuilder.py
Outdated
|
||
|
||
class BaseTractBuilder(metaclass=abc.ABCMeta): | ||
"""Base class for tract builders. |
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.
"""Base class for tract builders. | |
"""Base class for algorithms that define the patches within a tract. |
python/lsst/skymap/tractBuilder.py
Outdated
# This should probably be the same as above because we only | ||
# care about the INNER dimensions. | ||
patchInd = tuple(int(pixelInd[i]/self._patchInnerDimensions[i]) for i in range(2)) | ||
return self.getPatchInfo(patchInd, wcs) |
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.
Do you expect derived classes to ever need to override findPatch
and/or findPatchList
? If not, maybe they should go back in TractInfo
instead.
for yInd in range(llPatchInd[1], urPatchInd[1]+1)) | ||
|
||
def getPatchBorder(self): | ||
return self._patchBorder |
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 this relevant for all tractBuilders?
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.
At the moment, yes. Both legacy and cells have a border region, it's just one is an arbitrary number of pixels, the other is an arbitrary number of cells.
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.
Hmm. If it means different things for different builders, I would be inclined to get rid of it if we possibly can.
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 is always pixels that are returned here. It's just how it's specified in the config. You can specify the number of pixels directly (legacy) or the number of cells (cells), and since each cell has an explicit size you know how big the border is.
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.
Ok, that's not too bad. Still, if nothing calls it, I'd vote for dropping 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.
It's used in how to define the patches that make the tract. Dropping it would require some other big changes.
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.
Ok, I'd hoped because the patches have different geometry on the edges, it might not have been needed, but if it is, fine.
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 took a more detailed approach, mainly focus on what I thought I would need for my code. A number of suggestions, but all extremely minor.
return lsst.sphgeom.ConvexPolygon([sp.getVector() for sp in skyPoints]) | ||
|
||
|
||
class Index2D(NamedTuple): |
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 needs a docstring, mainly to provide context on its use cases.
python/lsst/skymap/patchInfo.py
Outdated
IndexError | ||
If index is out of range. | ||
""" | ||
if self._numCells[0] == 0 or self._numCells[1] == 0: |
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.
Given that _numCells
are named tuples, it's best to address the components with x
and y
.
python/lsst/skymap/patchInfo.py
Outdated
_index = self.getCellIndexPair(index) | ||
else: | ||
_index = Index2D(*index) | ||
if (not 0 <= _index.x < self._numCells[0]) \ |
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 (not 0 <= _index.x < self._numCells[0]) \ | |
if (not 0 <= _index.x < self._numCells.x) \ |
python/lsst/skymap/patchInfo.py
Outdated
else: | ||
_index = Index2D(*index) | ||
if (not 0 <= _index.x < self._numCells[0]) \ | ||
or (not 0 <= _index.y < self._numCells[1]): |
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.
or (not 0 <= _index.y < self._numCells[1]): | |
or (not 0 <= _index.y < self._numCells.y): |
python/lsst/skymap/tractBuilder.py
Outdated
) | ||
|
||
def validate(self): | ||
if len(self.patchInnerDimensions) != 2: |
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 is already enforced by setting length=2
in patchInnerDimensions
and is therefore redundant.
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 my testing, that did not appear to be 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.
Ah, ListField
's documentation was somewhat confusing. Calling config.validate()
will raise an Exception if length is not two without explicitly defining it here.
https://github.com/lsst/pex_config/blob/ae7742993c3fa92bf5ac1d9ab0c298c95e81930a/python/lsst/pex/config/listField.py#L343..L345
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.
Huh. I thought I tested this and it didn't work as I expected, but you're right. So I've deleted that redundant check. Thanks!
x,y index of patch (a pair of ints) | ||
innerBBox : `lsst.geom.Box2I` | ||
inner bounding box | ||
outerBBox : `lsst.geom.Box2I` | ||
inner bounding box | ||
sequentialIndex : `int` | ||
Patch sequential index | ||
tractWcs : `lsst.afw.geom.SkyWcs` |
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.
tractWcs : `lsst.afw.geom.SkyWcs` | |
tractWcs : `lsst.afw.geom.SkyWcs`, optional |
self._innerBBox = innerBBox | ||
self._outerBBox = outerBBox | ||
self._wcs = tractWcs |
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.
Either tractWCS
should not be optional (not ideal) or self._wcs
should take the tract's WCS.
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'm not sure what the comment is 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.
Let me try to rephrase my comment.
Since PatchInfo
can be instantiated with tractWCS
set to None
(default), self._wcs
will be set to None
. I don't see a way where _wcs
can be set later after instantiation. This would cause callinggetInnerSkyPolygon
and getOuterSkyPolygon
with tractWCS=None
fail (default again). So we should not allow a PatchInfo
object to be constructed without a tractWCS
.
I recollect discussing about overriding the WCS attached to the object when calling those methods, but don't recollect discussing about needing to create PatchInfo
without a WCS. If you see a compelling need, then please add to the docstring that if initialized without a WCS, they must necessarily be passed in methods such as getInnerSkyPolygon
.
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 your point. I think that there's no way for anything legitimate (not monkey-patched or otherwise hacked) to get a PatchInfo
without a wcs. So I could make this required at instantiation, or at least Raise if it doesn't have a valid wcs. Which would you prefer?
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 would prefer making it required instead of raising an exception. I could imagine trying to create a PatchInfo directly for testing/playing around and would like to know that I need to provide a WCS to begin with.
python/lsst/skymap/patchInfo.py
Outdated
else: | ||
if not isinstance(index, Iterable): | ||
raise ValueError("Input index is not an iterable.") | ||
if len(index) != 2: |
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 Index2D
constructor (or more accurately the NamedTuple
constructor) takes care of this internally that I don't think this check needs to be in here.
python/lsst/skymap/tractBuilder.py
Outdated
|
||
def getPatchInfo(self, index, tractWcs): | ||
# This should always be initialized | ||
assert self._initialized |
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.
Should this raise an AssertionError wihout any helpful message, or should we explicitly raise an exception? I understand assert
statement are removed from the production builds, but during the development stage I don't think this is going to help and shouldn't be a significant overhead in production either.
python/lsst/skymap/tractBuilder.py
Outdated
|
||
def getPatchInfo(self, index, tractWcs): | ||
# This should always be initialized | ||
assert self._initialized |
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.
Similar comment about asserting as an earlier comment.
38a79f5
to
eb4b260
Compare
eb4b260
to
1ead253
Compare
No description provided.