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

Parallel transport generic vector interpolation method. #163

Merged
merged 31 commits into from
Dec 15, 2023

Conversation

odlomax
Copy link
Contributor

@odlomax odlomax commented Nov 17, 2023

Hi @wdeconinck,

I'm experimenting with a new method I've cooked up to perform geometry-agnostic vector interpolation. Essentially, it works out the angular difference between North and a great-circle arc connecting a source and a target point, then rotates the source field vector to match the target field vector.

The upshot of this is that we require four interpolation matrices instead of the usual one complex interpolation weights, but the results look promising. I've experimented below, interpolating from a CS-LFR-48 to an O48 (the interpolation method knows nothing about the cubed-sphere).

Here are some figures showing the results so far:

test_field_eastwards
Fig 1: Eastward component of test field on CS-LFR-48 grid

test_field_northwards
Fig 2: Northward component of test field on CS-LFR-48 grid

error_scalar_interp
Fig 3: Interpolation error when mapped on to O48 gird. Vector field components treated as independent scalars.

error_parallel_transport
Fig 4: Interpolation error when mapped on to O48 gird. Source vectors rotated to match target vectors.

tl;dr I've made the error of interpolating winds over the poles go away, and I believe it will work for all matrix-based interpolation methods.

I'll try and get it finished up over the next week or so.

@codecov-commenter
Copy link

codecov-commenter commented Nov 30, 2023

Codecov Report

Attention: 21 lines in your changes are missing coverage. Please review.

Comparison is base (24d175a) 79.92% compared to head (56badc7) 80.02%.

Files Patch % Lines
...polation/method/sphericalvector/SphericalVector.cc 86.71% 17 Missing ⚠️
...rpolation/method/sphericalvector/SphericalVector.h 33.33% 2 Missing ⚠️
src/atlas/util/Geometry.h 60.00% 2 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop     #163      +/-   ##
===========================================
+ Coverage    79.92%   80.02%   +0.10%     
===========================================
  Files          853      857       +4     
  Lines        63199    63498     +299     
===========================================
+ Hits         50511    50817     +306     
+ Misses       12688    12681       -7     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@wdeconinck
Copy link
Member

wdeconinck commented Nov 30, 2023

Your updates to exclusively rely on Eigen is potentially problematic.
Two ways...

  1. Revert the change, which ensures that it will always work.
  2. Check if compilation without Eigen works now (cmake : -DENABLE_EIGEN=OFF), and
    add some #if ATLAS_HAVE_EIGEN in SphericalVector.h and SphericalVector.cc to hide Eigen includes and types and then throw exceptions in the SphericalVector.cc when trying to use it. Also the compilation of SphericalVector should not be optional. The #if ATLAS_HAVE_EIGEN in code should take care of it.

@odlomax odlomax marked this pull request as ready for review November 30, 2023 11:48
@odlomax
Copy link
Contributor Author

odlomax commented Nov 30, 2023

Your updates to exclusively rely on Eigen is potentially problematic. Two ways...

  1. Revert the change, which ensures that it will always work.
  2. Check if compilation without Eigen works now (cmake : -DENABLE_EIGEN=OFF), and
    add some #if ATLAS_HAVE_EIGEN in SphericalVector.h and SphericalVector.cc to hide Eigen includes and types and then throw exceptions in the SphericalVector.cc when trying to use it. Also the compilation of SphericalVector should not be optional. The #if ATLAS_HAVE_EIGEN in code should take care of it.

Thanks, @wdeconinck, I've replaced the optional compilation with pre-processor macros in relevant files. It builds and runs properly on my system with ENABLE_EIGEN set to both ON and OFF.

I did some digging in the current eckit sparse matrix. It does indeed prune out zero-valued triples (see here). This means all the real, imaginary and magnitude matrices can have different sparsity. The simplest in-code solution was to use Eigen directly. Minimal code changes would be required if the current eckit SparseMatrix is generalised to SparseMatrixT.

I'm going to open an issue which details what I think are the to-do items (hopefully) for follow-up PRs.

@wdeconinck
Copy link
Member

I did some digging in the current eckit sparse matrix. It does indeed prune out zero-valued triples

That does seem like a sensible optimisation, but prevents us from fusing the two loops indeed, and then Eigen matrix solution is appropriate.

@odlomax
Copy link
Contributor Author

odlomax commented Nov 30, 2023

I did some digging in the current eckit sparse matrix. It does indeed prune out zero-valued triples

That does seem like a sensible optimisation, but prevents us from fusing the two loops indeed, and then Eigen matrix solution is appropriate.

Its probably worth pointing out, I could fuse the the horizontal and vertical component loops, at the expense of adding some more complicated code.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was still good to have it in this file, but not guarded by #if ATLAS_HAVE_EIGEN if that makes sense.

Copy link
Contributor Author

@odlomax odlomax Nov 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see that it adds unnecessary marcro mess. I'm just marvelling at how the factories can register at run time instead of compile time!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I meant is what I added to your branch with a96057e

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah! That looks far safer!

… Write test for 3-vector field (note: this works when hacking current 2-vector test).
added tests for 3-vector and 2 vector fields.
@wdeconinck
Copy link
Member

wdeconinck commented Dec 8, 2023

Nice cleanup.
Unfortunately still the same compile error with intel/2021.4
We could also disable OpenMP for that compiler and version as that thread you suggested says it could be fixed with 2022.2

@odlomax
Copy link
Contributor Author

odlomax commented Dec 8, 2023

Nice cleanup. Unfortunately still the same compile error with intel/2021.4

Yeah, I just saw. What's confusing is that all the GitHub Actions pass on the JCSDA-internal fork end.

I guess I've got two options:

  • Rewrite the function differently and hope for the best.
  • Remove the OpenMP pragmas and accept that it's going to be slow for now (the error message is complaining about the pragma on the line above).

What would you suggest?

@odlomax
Copy link
Contributor Author

odlomax commented Dec 8, 2023

Nice cleanup. Unfortunately still the same compile error with intel/2021.4 We could also disable OpenMP for that compiler and version as that thread you suggested says it could be fixed with 2022.2

It's certainly worth a try. Should at least help diagnose the problem! 😊

#define atlas_omp_parallel_for for
#endif
#endif

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, @wdeconinck. Hit it with macros until the problem goes away! ❤

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem indeed went away 😃

const auto sourceVector = Complex(sourceVars(0), sourceVars(1));
const auto targetVector = complexWeight * sourceVector;
targetVars(0) += targetVector.real();
targetVars(1) += targetVector.imag();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without going into the detailed maths, why do you have to
targetView.assign(0)
and then use += for the targetVars ? Can this not be replaced with just = ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never mind, I understand, due to the matrix multiply internally.
But perhaps performance can be gained if the assign(0) could be removed and done inside the matrixMultiply

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 because each target element (named targetVars here) has a contribution from multiple source elements (sourceVars). If I changed += to =, it would set the target element to the last source element on that row of the matrix.

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 could do the assign(0.) within matrixMultiply function, but it would have to be on the first line, and therefore be the equivalent operation as it currently is. The SparseMatrixForEach is the only function that has knowledge of i as a whole row, but it doesn't know what functor is doing (it's used in a couple of different contexts).

I suspect details like this will have to be refactored to remove code duplication when the adjoint methods are added. In that context, you don't assign zero to the array you're writing to.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK it can stay as-is. We should not attempt premature optimisation now.

Copy link
Member

@pmaciel pmaciel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @odlomax,

as discussed this is a great contribution! As you see, I've only commented on technical details, because it looks like everything is well thought of. I admit, I think there is a bit overuse of templates to pass functors (when we really only handle the real and complex cases), but it is certainly not an important concern at the moment.

I've already put the "course" method as part of a revised eckit geometry library. I look forward to have ready the more flexible sparse matrix container and the sparse linear algebra backend too, as we discussed. It will happen in the coming months, and will also simplify much of the workarounds you had to painfuly go through.

I'm already aproving the PR -- this is ready to merge!

return;
}

ATLAS_ASSERT_MSG(sourceField.variables() == 2 || sourceField.variables() == 3,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this assertion come right at the beggining of the method?

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'd also have to check that the field was a vector type. By this point in the function, it definitely is.

@odlomax
Copy link
Contributor Author

odlomax commented Dec 14, 2023

Hi @odlomax,

as discussed this is a great contribution! As you see, I've only commented on technical details, because it looks like everything is well thought of. I admit, I think there is a bit overuse of templates to pass functors (when we really only handle the real and complex cases), but it is certainly not an important concern at the moment.

I've already put the "course" method as part of a revised eckit geometry library. I look forward to have ready the more flexible sparse matrix container and the sparse linear algebra backend too, as we discussed. It will happen in the coming months, and will also simplify much of the workarounds you had to painfuly go through.

I'm already aproving the PR -- this is ready to merge!

Thanks, @pmaciel, @wdeconinck,

I had a lot of fun with this one.

I freely admit I'm a template addict. If only we had C++ 20, I could at least throw a few concepts on them! I need to follow this up with an implementation of the adjoint methods. Shall I push @pmaciel's suggested changes to this PR, or the next one?

@pmaciel
Copy link
Member

pmaciel commented Dec 14, 2023

Thanks, @pmaciel, @wdeconinck,

I had a lot of fun with this one.

I freely admit I'm a template addict. If only we had C++ 20, I could at least throw a few concepts on them! I need to follow this up with an implementation of the adjoint methods. Shall I push @pmaciel's suggested changes to this PR, or the next one?

Since they're only technical would prefer that it's merged with the suggested changes :-)

@odlomax
Copy link
Contributor Author

odlomax commented Dec 14, 2023

Thanks, @pmaciel, @wdeconinck,
I had a lot of fun with this one.
I freely admit I'm a template addict. If only we had C++ 20, I could at least throw a few concepts on them! I need to follow this up with an implementation of the adjoint methods. Shall I push @pmaciel's suggested changes to this PR, or the next one?

Since they're only technical would prefer that it's merged with the suggested changes :-)

It is done! Thanks, @pmaciel 🙂

@wdeconinck
Copy link
Member

Thanks @odlomax for addressing further review. I will merge today when all tests have passed. This will be super useful.

@odlomax
Copy link
Contributor Author

odlomax commented Dec 15, 2023

Thanks @odlomax for addressing further review. I will merge today when all tests have passed. This will be super useful.

Again, thanks @wdeconinck and @pmaciel. Always a pleasure.

@wdeconinck wdeconinck merged commit e734e7a into ecmwf:develop Dec 15, 2023
92 checks passed
@odlomax odlomax deleted the feature/parallel_transport branch January 2, 2024 11:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants