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-32402: Add TractBuilders and sub-patch cell capability. #56

Merged
merged 6 commits into from Nov 8, 2021

Conversation

erykoff
Copy link
Contributor

@erykoff erykoff commented Oct 28, 2021

No description provided.

"""
return self._sequentialIndex

sequentialIndex = property(getSequentialIndex, None)
Copy link
Member

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.

Copy link
Contributor Author

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.

Copy link
Member

@TallJimbo TallJimbo left a 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.

"""

def __init__(self, index, innerBBox, outerBBox):
def __init__(self, index, innerBBox, outerBBox,
cellInnerDimensions=Extent2I(0, 0), cellBorder=0,
Copy link
Member

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).

self._cellInnerDimensions = cellInnerDimensions
self._cellBorder = cellBorder
if numCellsPerPatchInner == 0:
self._numCells = Extent2I(0, 0)
Copy link
Member

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).

Copy link
Contributor Author

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.

dtype=int,
default=100,
tractBuilder = tractBuilderRegistry.makeField(
"Tract building algorithm",
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
"Tract building algorithm",
"Algorithm for creating the patches within a tract.",

from .detail import makeSkyPolygonFromBBox


class CellInfo():
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
class CellInfo():
class CellInfo:


nx, ny = self.getNumCells()
x = sequentialIndex % nx
y = (sequentialIndex - x) // nx
Copy link
Member

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?

Copy link
Contributor Author

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.

from .detail import Index2D

__all__ = ["tractBuilderRegistry", "BaseTractBuilder",
"LegacyTractBuilder", "CellTractBuilder"]
Copy link
Member

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.



class BaseTractBuilder(metaclass=abc.ABCMeta):
"""Base class for tract builders.
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
"""Base class for tract builders.
"""Base class for algorithms that define the patches within a tract.

# 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)
Copy link
Member

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
Copy link
Member

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?

Copy link
Contributor Author

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.

Copy link
Member

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.

Copy link
Contributor Author

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.

Copy link
Member

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.

Copy link
Contributor Author

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.

Copy link
Member

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.

Copy link
Member

@arunkannawadi arunkannawadi left a 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):
Copy link
Member

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.

IndexError
If index is out of range.
"""
if self._numCells[0] == 0 or self._numCells[1] == 0:
Copy link
Member

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.

_index = self.getCellIndexPair(index)
else:
_index = Index2D(*index)
if (not 0 <= _index.x < self._numCells[0]) \
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
if (not 0 <= _index.x < self._numCells[0]) \
if (not 0 <= _index.x < self._numCells.x) \

else:
_index = Index2D(*index)
if (not 0 <= _index.x < self._numCells[0]) \
or (not 0 <= _index.y < self._numCells[1]):
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
or (not 0 <= _index.y < self._numCells[1]):
or (not 0 <= _index.y < self._numCells.y):

)

def validate(self):
if len(self.patchInnerDimensions) != 2:
Copy link
Member

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.

Copy link
Contributor Author

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.

Copy link
Member

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

Copy link
Contributor Author

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`
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
tractWcs : `lsst.afw.geom.SkyWcs`
tractWcs : `lsst.afw.geom.SkyWcs`, optional

self._innerBBox = innerBBox
self._outerBBox = outerBBox
self._wcs = tractWcs
Copy link
Member

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.

Copy link
Contributor Author

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?

Copy link
Member

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.

Copy link
Contributor Author

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?

Copy link
Member

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.

else:
if not isinstance(index, Iterable):
raise ValueError("Input index is not an iterable.")
if len(index) != 2:
Copy link
Member

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.


def getPatchInfo(self, index, tractWcs):
# This should always be initialized
assert self._initialized
Copy link
Member

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.


def getPatchInfo(self, index, tractWcs):
# This should always be initialized
assert self._initialized
Copy link
Member

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.

@erykoff erykoff force-pushed the tickets/DM-32402 branch 2 times, most recently from 38a79f5 to eb4b260 Compare November 5, 2021 21:52
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

3 participants