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-8439: Add wrapper on astshim to take point lists #182
Conversation
846abd0
to
117fb96
Compare
include/lsst/afw/geom/SpherePoint.h
Outdated
* | ||
* @exceptsafe Provides strong exception guarantee. | ||
*/ | ||
SpherePoint(); |
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'd still prefer not to let SpherePoint
be default-constructible if it can be avoided; the stated default, while better than any of the alternatives, is not as obvious as it could be because the default point is (0, 0)
in lon-lat representation but (1, 0, 0)
in vector representation.
Do you use default constructibility anywhere? I tried looking for explicit variable declarations but didn't find any.
If you do keep the default constructor, consider declaring it noexcept
.
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 class designs seem reasonable to me. I would suggest having another pybind11 master take a look, since I don't really know what to look for in that code.
Some clarification questions, several things about the uses of asserts.
Also, you may want to add self.longMessage=True
to the python test setUp()
, so that the msg=
kwargs don't overwrite the default messages. See here: https://docs.python.org/2/library/unittest.html#unittest.TestCase.longMessage
- Point3Endpoint is used for Point3D data | ||
- SpherePointEndpoint for SpherePoint data | ||
- GenericEndpoint is used when no other form will do; its LSST data type | ||
is identical to the type used for ast::Mapping.tran. |
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 identical" -> "must be identical"?
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 prefer my wording. I consider it an explanation of the design and a suggestion of a place to look for more information.
template <typename PointT, typename ArrayT> | ||
class BaseEndpoint { | ||
public: | ||
using Point = PointT; |
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.
Out of curiosity, why do you use these "using"s here, instead of either just using the T
names, or calling the types the name without the T
in the first place?
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 makes PointT
available to users as BaseEndpoint::Point
. I suspect the template type name and the class attribute can have the same name, but I worry that using Point = Point
looks too strange. @kfindeisen may have an opinion about 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.
Ah, ok. That makes sense as a technical reason why to do it this way.
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 actually know whether using Point = Point
would compile (though it probably would). @parejkoj, the LSST style guide recommends the PointT
name (and disambiguating that we don't mean afw::geom::Point
is certainly relevant here).
|
||
Do not obother to check the number of axes because that is done elsewhere. | ||
|
||
The base implementation does nothing. |
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 line redundant, since it's virtual?
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 a default behavior that derived classes inherit and can choose to override. As such it might do something useful, but in this case it does nothing.
return data.size(); | ||
} | ||
|
||
int _getNPoints(ndarray::Array<double, 2, 2> const & data) const { |
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 also need a _getNPoints for <double, 1, 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.
No because <double, 1, 1>
is used to represent a single point. (Also these private methods are simply used as convenience methods for other code; the fact that the code compiles and passes unit tests is a fairly strong indication that it is not missing such methods).
include/lsst/afw/geom/Endpoint.h
Outdated
*/ | ||
explicit BaseEndpoint(int nAxes) : _nAxes(nAxes) {} | ||
|
||
virtual ~BaseEndpoint() {}; |
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 need to explicitly implement the default/delete copy and move constructors here, per RFC-209?
@@ -15,6 +15,7 @@ setupRequired(astropy) | |||
setupRequired(python_future) | |||
setupRequired(log) | |||
setupRequired(pybind11) | |||
setupRequired(astshim) |
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.
And they never thought there'd be a day when afw depended on AST!
tests/test_transform.py
Outdated
# check invalid numbers of output for the given toName | ||
for badNout in getBadNAxes(toName): | ||
badPolyMap = makePolyMap(nIn, badNout) | ||
with self.assertRaises(Exception): |
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.
Which Exception?
tests/test_transform.py
Outdated
inPoint = fromEndpoint.pointFromData(rawInPoint) | ||
outPoint = transform.tranForward(inPoint) | ||
rawOutPoint = toEndpoint.dataFromPoint(outPoint) | ||
self.assertTrue(np.allclose(rawOutPoint, polyMap.tranForward(rawInPoint))) |
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.
self.assertFloatsAlmostEqual
and msg=
here, and the other asserts below.
tests/test_transform.py
Outdated
self.assertTrue(np.allclose(rawInversePoint, frameSet.tranInverse(rawOutPoint))) | ||
|
||
# forward transformation of an array of points | ||
nPoints = 7 # arbitrary |
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 comment.
tests/test_transform.py
Outdated
for badNin in getBadNAxes(fromName): | ||
for nIn in getNaxes(toName) + getBadNAxes(toName): | ||
badPolyMap = makePolyMap(badNin, nOut) | ||
with self.assertRaises(Exception): |
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.
Which Exception?
36a5be1
to
24a8200
Compare
aa0f4e1
to
5d8f7d7
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.
Thanks for adding the output operator. That'll be helpful when debugging in python.
I think you still need to convert all the |
And I guess the remaining |
2cd8185
to
5f4149e
Compare
e61236b
to
a335cbb
Compare
53f479c
to
0967862
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 have a few requests for more documentation and a few suggestions for code cleanup, but I don't see any major issues in the pybind11 wrappers.
python/lsst/afw/geom/endpoint.cc
Outdated
cls.def("__str__", [](Class const& self) { | ||
std::ostringstream os; | ||
os << self; | ||
return os.str(); |
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.
Personally I prefer buffer
as more informative than os
, but we can worry about when we standardize post-RFC-298.
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'd be surprised and disappointed if we felt we had to dictate the variable name in our standards.
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.
Not that kind of standard. Pim expressed an interest in reducing the code duplication in our current __str__
and __repr__
implementations, presumably by creating a single centralized function to replace these lambdas.
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.
Got it. I think that will work out to not wrapping __repr__
at all (which I am violating to provide a slightly nicer class name) and making __str__
= operator<< (which it is, so it can trivially modified to call the standard function when we have one).
python/lsst/afw/geom/endpoint.cc
Outdated
using Class = typename PyClass::type; // C++ class associated with pybind11 wrapper class | ||
cls.def("__repr__", [](Class const& self) { | ||
std::ostringstream os; | ||
os << "lsst.afw.geom." << self; |
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 only correct if the output of operator<<
begins with the name of the class; perhaps document that as a requirement of <<
?
cls.def("makeFrame", [](Class const & self) { | ||
auto frame = self.makeFrame(); | ||
return frame->copy(); | ||
}); |
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.
Comment on why you don't wrap getNPoints
, *From*
?
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 assume we don't ever plan to create python-only subclasses, so we don't need the protected methods?
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 originally didn't wrap getNPoints
and etc. because they were not implemented in this base class. But I think it's considered better to wrap them, so I will.
I don't anticipate us making Python-only subclasses, and the protected methods aren't essential to doing so.
python/lsst/afw/geom/endpoint.cc
Outdated
py::class_<Class, std::shared_ptr<Class>, BaseEndpoint<Point, Array>> cls( | ||
mod, ("_BaseVectorEndpoint" + suffix).c_str()); | ||
|
||
cls.def(py::init<int>(), "nAxes"_a); |
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.
Isn't BaseVectorEndpoint
a base class? Why do you need the constructor?
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 remove it. Also, I'll make the constructor for BaseEndpoint
and BaseVectorEndpoint
protected, since neither is intended to be used directly.
python/lsst/afw/geom/endpoint.cc
Outdated
using Point = typename Class::Point; | ||
std::stringstream os; | ||
os << "Point" << N; | ||
std::string pointNumStr = os.str(); |
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.
An iostream is overkill for this. Use std::to_string
instead.
cls.def("makeFrame", [](Class const & self) { | ||
auto frame = self.makeFrame(); | ||
return frame->copy(); | ||
}); |
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.
Code duplication with declareBaseEndpoint
; factor into a common function.
os << pyClassName; | ||
auto const frameSet = self.getFrameSet(); | ||
os << "[" << frameSet->getNin() << "->" << frameSet->getNout() << "]"; | ||
return os.str(); |
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 might want to add a comment noting that all the distinguishing information is in pyClassName
; at first I thought the result would be something like "Transform[2->2]"
, which is not particularly helpful.
python/lsst/afw/geom/transform.cc
Outdated
cls.def("tranForward", (ToPoint (Class::*)(FromPoint const &) const) &Class::tranForward, "point"_a); | ||
cls.def("tranInverse", (FromArray (Class::*)(ToArray const &) const) &Class::tranInverse, "array"_a); | ||
cls.def("tranInverse", (FromPoint (Class::*)(ToPoint const &) const) &Class::tranInverse, "point"_a); | ||
/// repr format is lsst.afw.geom.<ClassName>[<nIn>-><nOut>] |
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.
Doxygen comments should not be used inside function bodies. Did you mean //
? [Same for __str__
]
7d60ddd
to
c83d116
Compare
Add SpherePoint(double const lonLatRad[2]) for efficient construction from raw data.
In Python these classes are named Transform_fromPrefix_To_toPrefix_ where _fromPrefix_ and _toPrefix_ are endpoint prefixes such as "Generic", "Point3" or "SpherePoint".
No description provided.