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
Add imageMapReduce task #46
Conversation
ce87058
to
31b7b1b
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.
The overall design is reasonable. Several comments about docs, new unittests and returning Structs.
I think it would be good for you to run coverage (either via pytest-cov, or coverage.py), to help find where your current tests aren't getting to. I can help you with that, if you like.
Although I have some comments to that effect above, you do need some more docs about what happens when Map doesn't return an Exposure (e.g., moving average as you suggested), both at the file/class level, and for the method args/return values.
I do worry about about implementing MapReduce in the stack in ip_diffim, instead of either taking an off-the-shelf python MR code, or putting it somewhere higher (e.g. lsst.utils
?). The implementation here is somewhat specific to your use case, though, so I don't know how feasible either of those would be. At the very least, you should think about how one would make this parallel via multiprocessing or mpi, since MR lends it self perfectly to that.
import lsst.pex.config as pexConfig | ||
import lsst.pipe.base as pipeBase | ||
|
||
__all__ = ("ImageMapReduceTask", "ImageMapReduceConfig", |
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 probably be at the very top. There was recently discussion of this, with this ticket filed: https://jira.lsstcorp.org/browse/DM-9596
It's good that you're using a tuple for these. Apparently there are "interesting" corner cases involving it being a 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.
Thanks for pointing out, but I'll leave it as-is until there's an implemented RFC.
|
||
class ImageMapperSubtask(with_metaclass(abc.ABCMeta, pipeBase.Task)): | ||
"""Abstract base class for any task that is to be | ||
used as `ImageMapReduceConfig.mapperSubtask` |
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 docstring could use some expansion: what would a non-abstract child class used by mapperSubtask
actually do?
_DefaultName = "ip_diffim_ImageMapperSubtask" | ||
|
||
def run(self, subExp, expandedSubExp, fullBBox, **kwargs): | ||
"""Perform operation on given sub-exposure. |
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.
Perform what operation?
ConfigClass = ImageMapperSubtaskConfig | ||
_DefaultName = "ip_diffim_ImageMapperSubtask" | ||
|
||
def run(self, subExp, expandedSubExp, fullBBox, **kwargs): |
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.
You need @abc.abstractmethod
on here, and any other methods below that must be overridden in order to have a complete non-abstract child class.
subExp : afw.Exposure | ||
the sub-exposure upon which to operate | ||
expandedSubExp : afw.Exposure | ||
the expanded sub-exposure upon which to operate |
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.
What's an "expanded sub-exposure"?
scaleByFwhm = self.config.scaleByFwhm | ||
bbox = exposure.getBBox() | ||
|
||
psfFwhm = (exposure.getPsf().computeShape().getDeterminantRadius() * |
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.
Technically, or coding standards don't like spaces around *
, but I don't really care.
|
||
def rescaleValue(val): | ||
if scaleByFwhm: | ||
return np.rint(val * psfFwhm).astype(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.
What happens if the psf has different dimensions in X vs. Y?
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 uses the average.
nGridX = bbox.getWidth() / gridStepX | ||
# Readjust gridStepX so that it fits perfectly in the image. | ||
gridStepX = float(bbox.getWidth() - gridSizeX) / float(nGridX) | ||
if gridStepX <= 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.
<=0
here, or <=1
? (same below)
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 just to catch a case which shouldn't happen. I'll raise an error instead.
centroidY = self.config.gridCentroidsY[i] | ||
centroid = afwGeom.Point2D(centroidX, centroidY) | ||
bb0 = afwGeom.Box2I(bbox0) | ||
xoff = int(np.floor(centroid.getX())) - bb0.getWidth()//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.
Good use of explicit integer division.
return bb0, bb1 | ||
|
||
xoff = 0 | ||
while(xoff <= bbox.getWidth()): |
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 missed while
loops!
Thanks for the excellent review. I have no issues with nearly all of your comments. I agree with the potential for moving this out of |
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.
Thanks for the updates. The docs are a lot clearer now.
See the one minor comment.
@@ -175,7 +246,8 @@ class ImageMapReduceConfig(pexConfig.Config): | |||
) | |||
|
|||
# Separate gridCentroidsX and gridCentroidsY since pexConfig.ListField accepts limited dtypes | |||
# (i.e., no Point2D) | |||
# (i.e., no Point2D). The resulting set of centroids is the "vertical stack" of | |||
# `gridCentroidsX` and `gridCentroidsY`. |
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.
Please give an example here, showing what you get for, e.g., (1,2 ), (3,4)
2d253ea
to
2f9cf10
Compare
Add unit test
Waited until pybind11 merge. Confirmed build on Jenkins. |
Unit tests are in PR #45.