Hypre solver eliminate boundaries (New API) - #3460
Conversation
This reverts commit 81befc2.
The vector should be assembled once after importing values. Assembling twice with elimBE over-corrects the RHS.
row_indexes is free'd using HypreFree, so should be allocated using HypreMalloc.
The row elimination is now an implementation detail that doesn't require changes to the HypreMatrix and HypreVector interface. The elimination is applied to temporary working vectors when assembling the HypreVector, so the input values are not mutated. New tests check the boundary elimination.
Fixes the integrated Laplace-Hypre3D tests. GPT says: The boundary-elimination logic in [src/sys/hypre_interface.cxx (line 48)](/Users/dudson2/code/BOUT-next/src/sys/hypre_interface.cxx:48) was only removing each boundary variable from one coupled row; in the hypre3d stencil, the same Y-boundary value can appear in multiple nearby rows, especially in the SOL case. I changed BoundaryElimination to collect all representable row couplings for each boundary equation, store them with per-boundary offsets, and apply them during matrix reduction, RHS reduction, and matvec reconstruction. The interface metadata was updated in [include/bout/hypre_interface.hxx (line 103)](/Users/dudson2/code/BOUT-next/include/bout/hypre_interface.hxx:103), and I added multi-coupling unit coverage in [tests/unit/include/bout/test_hypre_interface.cxx (line 618)](/Users/dudson2/code/BOUT-next/tests/unit/include/bout/test_hypre_interface.cxx:618).
ZedThree
left a comment
There was a problem hiding this comment.
I can't judge the accuracy of the numerics, but I have a few comments on the code.
| HYPRE_Int nb; | ||
| HYPRE_Int* binum_array; | ||
| HYPRE_Int* bjnum_array; | ||
| HYPRE_Int* bdep_array; | ||
| HYPRE_Complex* bii_array; | ||
| HYPRE_Complex* bij_array; | ||
| HYPRE_Int na; | ||
| HYPRE_Int* aoffset_array; | ||
| HYPRE_Int* aknum_array; | ||
| HYPRE_Complex* aki_array; | ||
| std::vector<HYPRE_Int> reduction_order; | ||
| std::vector<HYPRE_Int> expansion_order; |
There was a problem hiding this comment.
What's the reason some of these are raw pointers, and some are std::vector? Is this a device/host distinction?
There was a problem hiding this comment.
Mostly yes. std::vector can't be used on host and arrays that are passed into Hypre should use HypreMalloc.
| std::vector<std::vector<BoundaryOccurrence>> occurrences_by_boundary(nb); | ||
| for (HYPRE_Int rownum = 0; rownum < nrows; rownum++) { | ||
| const HYPRE_Int row_start = row_indexes[rownum]; | ||
| for (HYPRE_Int m = 0; m < ncols[rownum]; m++) { | ||
| const auto boundary_position = boundary_number_for_row.find(cols[row_start + m]); | ||
| if (boundary_position != boundary_number_for_row.end()) { | ||
| occurrences_by_boundary[boundary_position->second].push_back({rownum, m}); |
There was a problem hiding this comment.
I'm not sure what this is supposed to be doing, but should occurrences_by_boundary just be std::set?
I think this block could use a comment explaining what it's doing
| HYPRE_BigInt* cols, HYPRE_Complex* values, | ||
| HYPRE_Int nb, HYPRE_Int* bi_array) | ||
| : nb(nb) { | ||
| HYPRE_Int* row_indexes; |
There was a problem hiding this comment.
I think this constructor should be executed on the host, so we can use vector here?
| HYPRE_Int* row_indexes; | |
| std::vector<HYPRE_Int> row_indexes; | |
| row_indexes.reserve(nrows); |
There was a problem hiding this comment.
The lifetime of this is somewhat complicated: row_indexes is allocated but then assigned to the "out" parameter row_indexes_ptr. Very C-like behavior. This could perhaps be tidied up in future.
| BoundaryElimination::BoundaryElimination(HYPRE_Int nrows, HYPRE_Int* ncols, | ||
| HYPRE_BigInt* rows, HYPRE_Int** row_indexes_ptr, | ||
| HYPRE_BigInt* cols, HYPRE_Complex* values, | ||
| HYPRE_Int nb, HYPRE_Int* bi_array) |
There was a problem hiding this comment.
I think most of these should at least be const*, but I don't think there's any need for pointers here? Most of them seem to be copied elementwise?
Corner equations are likely not handled correctly, but it probably doesn't matter.
Calculation of boundary rows can just use the size of the container.
More descriptive flag. The API member function `setElimBE` is also renamed to `setUseBoundaryElimination`
Many small things. Replaced some raw pointers with shared_ptr. Added `modernize-use-ranges` to `.clangd`
Replaces #3439, using a new API that doesn't require sharing boundary information between matrices, rhs and solution vectors. Avoids hidden mutations that could be hard to reason about.
Includes additional tests and some changes to the boundary row elimination code.
Written almost entirely by Codex/gpt-5.4 with review and iterations.