-
Notifications
You must be signed in to change notification settings - Fork 46
Polygon implementation for modern (spherical mean value) interpolation. #329
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
base: develop
Are you sure you want to change the base?
Polygon implementation for modern (spherical mean value) interpolation. #329
Conversation
|
Thanks @lewisn-met for this contribution. I am wondering in how far there is overlap with the ConvexSphericalPolygon which is used in the "ConservativeSphericalPolygon" interpolation, and if missing functionality can be merged perhaps. @sbrdar could you also have a look please? |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## develop #329 +/- ##
===========================================
+ Coverage 79.27% 79.78% +0.50%
===========================================
Files 909 909
Lines 61597 70950 +9353
===========================================
+ Hits 48832 56605 +7773
- Misses 12765 14345 +1580 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
Thanks for taking a look at this and for the kind words @wdeconinck. From a brief read of the ConvexSphericalPolygon class, the method used to check for the left of a great circle is precisely what I used to check if a point was inside SphericalPolygon3D. As for the intersection method, I will need to look a bit further, but they seem to be distinct. |
|
On a slightly unrelated note, I notice that the linux intel-classic ci and gnu-14 ci are are currently failing for this PR. These failures seem to be related to the pluto-benchmark and either the SphericalVector or Cubic2D classes. Is this something you'd like me to take a look at? |
My hunch is that it is due to an updated Eigen version 5.0!!! that happened under the hood without us realising: Any help to fix this is always welcome of course. |
|
I have followed up on my hunch, and reverting the CI to use "eigen@3" seems to fix the compilation. This is not a proper fix for compatibility Eigen 5.0.0 but perhaps these are infancy issues that will be sorted upstream in Eigen in due course. At least we can work without failing CI now. |
|
I have now merged the fix from develop into this PR. Thanks for following your hunch so promptly - fingers crossed that any issues with Eigen 5. get sorted out soon! I also noticed that the validate method included in your ConvexSphericalPolygon class nicely verifies the polygon is on the sphere and points are not too close. I could not see anywhere in the FiniteElement class that does a similar check, so it may be worth sharing this functionality between the methods. |
|
Hi @lewisn-met. Many thanks for this interesting development. It seems to me that |
|
Hi @sbrdar, thank you for taking a look at this. Indeed, we can assume With regards to merging functionality, incorporating the methods of Hopefully we can discuss this soon. |
|
Hi @lewisn-met I am thinking that the Floater's barycentre could be used for the 2nd order cons. remapping in Atlas, so I cherry-picked your implementation with the unit test on SphericalVector3D, and added it to ConvexSphericalPolygon, see the usage here. Maybe something to discuss soon. |
|
Would incorporating this method into the cons. remapping be in addition or as an alternative to using Floater's interpolation in the finite-element class? Our original motivation for the algorithm was as a drop-in alternative to the Quad3D and Triag3D classes used for "continuous" interpolation, see the implementation here. However, I can see how Floater's ideas could be useful in both settings and I don't expect the use of ConvexSphericalPolygon in FiniteElement.cc to be too different from the branch I have linked. |
…l-mean-value-polygon
…l-mean-value-polygon
|
@sbrdar I see there is an issue with the preview-publish/deploy action - can you confirm that this is an issue on your end and not with this PR itself? It appears to be related to the python authenticator being called by the job runner. On a separate note, would you like me to merge from develop, since I see there are several commits which have not yet made their way onto this branch? |
|
@lewisn-met Thanks very much for the merger with ConvexSphericalPolygon - great work! The tests pass correctly. The failing unrelated static_analyzer problem we can ignore for now and continue with the merge. Can you in the very last step merge from the latest develop? |
…cal-mean-value-polygon
|
@sbrdar many thanks for your kind words, I'm glad you like the work! The latest ECMWF/develop has now been merged into this branch and we are good to go. I have a few small improvements for the spherical mean-value interpolator in mind, but will raise those in a separate PR when I get the chance to work on it. Thanks again for all of your help! |
sbrdar
left a 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.
All good for the merge. Very nice results and code implementation. Many thanks!
| config.get("type", interpType); | ||
| config.get("normalisation", normalisationMode); | ||
|
|
||
| SECTION("using " + interpType + " " + normalisationMode) { |
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.
A tiny comment here. This output can return, for instance, "using spherical-mean-value true" or "using spherical-mean-value false", but 'false'/'true' refers to normalisation. Can we have normalisationMode replaced with (normalisationMode ? "w/ normalisation" : "")?
| config.get("type", interpType); | ||
| config.get("normalisation", normalisationMode); | ||
|
|
||
| SECTION("using " + interpType + " " + normalisationMode) { |
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.
A similar comment as before also here. This output can return, for instance, "using spherical-mean-value true" or "using spherical-mean-value false", but 'false'/'true' refers to normalisation. Can we have normalisationMode replaced with (normalisationMode ? "w/ normalisation" : "")?
| } | ||
|
|
||
| gmshOutput("cubedsphere_source.msh", FieldSet(sourceField)); | ||
| gmshOutput("cubedsphere_source_" + interpType + normalisationMode + ".msh", FieldSet(sourceField)); |
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.
A tiny suggestion. Perhaps one can replace normalisationMode with (normalisationMode ? "_normalised" : "")
| targetFields.add(targetField); | ||
| targetFields.add(errorField); | ||
| targetFields.add(partField); | ||
| gmshOutput("cubedsphere_target_" + interpType + "_" + normalisationMode + ".msh", targetFields); |
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.
Here as well. A tiny suggestion. Perhaps one can replace normalisationMode with (normalisationMode ? "_normalised" : "")
| SECTION("test interpolation outputs") { | ||
| Field field_source = fs.createField<double>(option::name("source")); | ||
| Field field_target("target", array::make_datatype<double>(), array::make_shape(pointcloud.size())); | ||
| SECTION("using " + interpType + " " + normalisationMode) { |
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.
A tiny suggestion. Perhaps one can replace normalisationMode with (normalisationMode ? "_normalised" : "")
wdeconinck
left a 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.
Hello @lewisn-met I think this is looks a good merge of your previous polygon and this one.
I have some questions and suggestions.
| } | ||
|
|
||
| // cf. M. Floater, “Generalized barycentric coordinates and applications” Acta Numerica, p. 001, 2016. | ||
| std::optional<std::vector<double>> ConvexSphericalPolygon::compute_vertex_weights( |
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.
Why optional?
Since it is a dynamically sized vector, would it not be same when just passing an empty vector in the "not present" case?
And then in the client code check for vertex_weights.empty()
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 ignore this, and see below for other suggested API, which I added as a review comment later.
| ATLAS_ASSERT(size_ > 2, "Polygon must have at least 3 points"); | ||
| switch (mode) { | ||
| case SMV: | ||
| validateAndComputeNormals(); |
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.
validateAndComputeNormals() cannot be split into the following, reusing the already available validate?
validate();
compute_edge_normals();|
|
||
| template <class Points, typename std::enable_if<contains_PointLonLat<Points>::value, void>::type* = nullptr> | ||
| ConvexSphericalPolygon(const Points& points): ConvexSphericalPolygon(points.data(), points.size()) {} | ||
| ConvexSphericalPolygon(const Points& points, const interpolationMode mode = interpolationMode::GRID): |
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 interpolationMode to me seems fishy.
What is fundamentally different depending on the mode?
The computed area or centroid do not seem different, so why the mode? I would really like to avoid this.
|
|
||
| size_t size() const { return size_; } | ||
|
|
||
| std::vector<PointXYZ> edge_normals() const { return edge_normals_; } |
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 can be computed lazily on demand, just like the area.
Perhaps that could avoid the use of mode.
|
|
||
| int previous(const int index) const { return (index == 0) ? size_ - 1 : index - 1; }; | ||
|
|
||
| std::optional<std::vector<double>> compute_vertex_weights(const PointXYZ& candidatePoint) 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.
Another API may be desired and more performant, avoiding dynamic allocations:
int compute_vertex_weights(const PointXYZ& point, double vertex_weights[], size_t vertex_weights_size) {
ATLAS_ASSERT(vertex_weights_size == size());
...
return return_code; // instead of optional
}The intended use would then be compatible with many data container types where the control of allocation is then on the client side.
std::vector<double> vertex_weights;
vertex_weights.reserve(max_size); // allocate outside of a loop
for (auto polygon : polygons ) {
vertex_weights.resize( polygon.size() ); // no reallocation happening
if (polygon.compute_vertex_weights( point, vertex_weights.data(), vertex_weights.size() ) == OK){
// success, use vertex_weights
}
}or example with std::span (C++20):
std::array<double,1000> buffer;
for (auto polygon : polygons ) {
std::span<double> vertex_weights(buffer.data(), polygon.size());
if (polygon.compute_vertex_weights( point, vertex_weights.data(), vertex_weights.size() ) == OK) {
// success, use vertex_weights
}
}
Description
This PR relates to issue #328 and adds:
A SphericalPolygon3D class as an alternative interpolation method to the Triag3D and Quad3D classes currently used in the finite-element interpolation method (
FiniteElement.h). This computes weights using the spherical mean value formula rather than the bilinear/barycentric formulae currently implemented.A test suite for the new class, in
test_SphericalPolygon3D.cc, which validates that the interpolation method correctly computes the areas, normals, and weights of an example triangle, planar quadrilateral, and non-planar quadrilateral on the surface of the unit sphere.(Draft) PR integrating this class into the existing set of interpolation methods in atlas to follow, with the exact implementation to be fixed after further discussion with myself and @odlomax.
Contributor Declaration
By opening this pull request, I affirm the following: