Skip to content

Feature: add neighbor subface helper#2216

Open
dutkalex wants to merge 8 commits intoDLR-AMR:mainfrom
dutkalex:add-neighbor-subface-helper
Open

Feature: add neighbor subface helper#2216
dutkalex wants to merge 8 commits intoDLR-AMR:mainfrom
dutkalex:add-neighbor-subface-helper

Conversation

@dutkalex
Copy link
Copy Markdown
Contributor

@dutkalex dutkalex commented Mar 6, 2026

Describe your changes here:
Implements #2215

All these boxes must be checked by the AUTHOR before requesting review:

  • The PR is small enough to be reviewed easily. If not, consider splitting up the changes in multiple PRs.
  • The title starts with one of the following prefixes: Documentation:, Bugfix:, Feature:, Improvement: or Other:.
  • If the PR is related to an issue, make sure to link it.
  • The author made sure that, as a reviewer, he/she would check all boxes below.

All these boxes must be checked by the REVIEWERS before merging the pull request:

As a reviewer please read through all the code lines and make sure that the code is fully understood, bug free, well-documented and well-structured.

General

  • The reviewer executed the new code features at least once and checked the results manually.
  • The code follows the t8code coding guidelines.
  • New source/header files are properly added to the CMake files.
  • The code is well documented. In particular, all function declarations, structs/classes and their members have a proper doxygen documentation. Make sure to add a file documentation for each file!
  • All new algorithms and data structures are sufficiently optimal in terms of memory and runtime (If this should be merged, but there is still potential for optimization, create a new issue).

Tests

  • The code is covered in an existing or new test case using Google Test.
  • The code coverage of the project (reported in the CI) should not decrease. If coverage is decreased, make sure that this is reasonable and acceptable.
  • Valgrind doesn't find any bugs in the new code. This script can be used to check for errors; see also this wiki article.

If the Pull request introduces code that is not covered by the github action (for example coupling with a new library):

  • Should this use case be added to the github action?
  • If not, does the specific use case compile and all tests pass (check manually).

Scripts and Wiki

  • If a new directory with source files is added, it must be covered by the scripts/internal/find_all_source_files.sh to check the indentation of these files.
  • If this PR introduces a new feature, it must be covered in an example or tutorial and a Wiki article.

License

  • The author added a BSD statement to doc/ (or already has one).

@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 6, 2026

Codecov Report

❌ Patch coverage is 60.00000% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.52%. Comparing base (f878a8e) to head (a7f955d).
⚠️ Report is 539 commits behind head on main.

Files with missing lines Patch % Lines
src/t8_forest/t8_forest.cxx 60.00% 6 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2216      +/-   ##
==========================================
+ Coverage   78.27%   82.52%   +4.24%     
==========================================
  Files         114      115       +1     
  Lines       19101    18561     -540     
==========================================
+ Hits        14952    15318     +366     
+ Misses       4149     3243     -906     

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

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@holke holke self-assigned this Mar 12, 2026
@holke holke added enhancement Enhances already existing code priority:medium Should be solved within half a year workload:low Would take half a day or less labels Mar 12, 2026
@holke holke assigned spenke91 and benegee and unassigned holke Apr 17, 2026
@spenke91 spenke91 self-requested a review April 29, 2026 07:06
Copy link
Copy Markdown
Collaborator

@spenke91 spenke91 left a comment

Choose a reason for hiding this comment

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

Thank you very much @dutkalex for another great contribution! :-) I am sorry it took us so long to reply...
I mostly have suggestions regarding variable names and code comments (which, I know, not everybody loves as much as I do 🤓 ). Aside from that, I only have a question regarding the hard-coded 4.

Comment thread src/t8_forest/t8_forest_general.h
Comment on lines +1894 to +1898
t8_element_t *target = nullptr;
scheme->element_new (neighbor_tree_class, 1, &target);

int dummy; // Can't pass a nullptr to t8_forest_element_face_neighbor below (see #2214)
t8_forest_element_face_neighbor (forest, ltreeid, leaf, target, neighbor_tree_class, face, &dummy);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Maybe we can find a more expressive name than target? e.g.

Suggested change
t8_element_t *target = nullptr;
scheme->element_new (neighbor_tree_class, 1, &target);
int dummy; // Can't pass a nullptr to t8_forest_element_face_neighbor below (see #2214)
t8_forest_element_face_neighbor (forest, ltreeid, leaf, target, neighbor_tree_class, face, &dummy);
// Determine the (virtual) face neighbor
// Note: For this function to work properly, this face neighbor has to be a direct child of neighbor_leaf
t8_element_t *virtual_face_neighbor= nullptr;
scheme->element_new (neighbor_tree_class, 1, &virtual_face_neighbor);
int dummy; // Can't pass a nullptr to t8_forest_element_face_neighbor below (see #2214)
t8_forest_element_face_neighbor (forest, ltreeid, leaf, virtual_face_neighbor, neighbor_tree_class, face, &dummy);

Or maybe we can even find something better? sub_neighbor?

Copy link
Copy Markdown
Contributor Author

@dutkalex dutkalex May 9, 2026

Choose a reason for hiding this comment

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

"There are only two hard things in computer science: cache invalidation and naming things." 😉

I see your point, and I guess since I could not come up with a better name at the time and went for the rather undescriptive but concise option. I feel like virtual_face_neighbor is more descriptive than sub_neighbor, but it looses the "it's what we're looking for" idea. It seems hard to find a (reasonably sized) name which encodes unambiguously all this information. Maybe this means a small comment is necessary to clarify exactly what this is... What do you think?


scheme->element_get_children_at_face (neighbor_tree_class, neighbor_leaf, neighbor_face, children.begin (),
num_children, nullptr);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
Iterate over the neighbor's children and compare with virtual face neighbor to find the sub-face ID.

Copy link
Copy Markdown
Contributor Author

@dutkalex dutkalex May 9, 2026

Choose a reason for hiding this comment

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

I guess this is meant to be a comment. However, after re-reading with fresh eyes this piece of code, I think this is just a std::find_if and that refactoring the code to use this is the proper way to make the intent explicit here

result = i_child;
}
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// Free memory and return sub-face ID.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think the "free memory" part is quite self evident here, but the "return sub-face ID" part is indeed not clear with the current state of the code. However maybe this just means that result is a bad name ? 😅

int dummy; // Can't pass a nullptr to t8_forest_element_face_neighbor below (see #2214)
t8_forest_element_face_neighbor (forest, ltreeid, leaf, target, neighbor_tree_class, face, &dummy);

int const num_children = scheme->element_get_num_face_children (neighbor_tree_class, neighbor_leaf, neighbor_face);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
int const num_children = scheme->element_get_num_face_children (neighbor_tree_class, neighbor_leaf, neighbor_face);
// Get the number of the neighbor's children touching the neighbor_face or, equivalenlty, the face's number of children.
int const num_neighbor_face_children = scheme->element_get_num_face_children (neighbor_tree_class, neighbor_leaf, neighbor_face);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I can see why you'd want the variable name to be a little bit more explicit, but isn't the comment just paraphrasing what element_get_num_face_children's documentation already says?
I tend to think that comments should be used to explain why something is done a certain way, but not what the code does (that should be clear by reading the code, and if you think it's not the case maybe I should change the code then).

Comment on lines +1902 to +1906
std::array<t8_element_t *, 4> children; // assumes a 2:1 balanced forest
scheme->element_new (neighbor_tree_class, 4, children.begin ());

scheme->element_get_children_at_face (neighbor_tree_class, neighbor_leaf, neighbor_face, children.begin (),
num_children, nullptr);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

It may well be that I missed something here, but shouldn't we use num_face_children rather than 4?

Suggested change
std::array<t8_element_t *, 4> children; // assumes a 2:1 balanced forest
scheme->element_new (neighbor_tree_class, 4, children.begin ());
scheme->element_get_children_at_face (neighbor_tree_class, neighbor_leaf, neighbor_face, children.begin (),
num_children, nullptr);
// Create an array containing the neighbor's children at neighbor_face.
std::array<t8_element_t *, num_neighbor_face_children> neighbor_children_at_face;
scheme->element_new (neighbor_tree_class, num_neighbor_face_children, neighbor_children_at_face.begin ());
scheme->element_get_children_at_face (neighbor_tree_class, neighbor_leaf, neighbor_face, neighbor_children_at_face.begin (), num_neighbor_face_children, nullptr);

Copy link
Copy Markdown
Contributor Author

@dutkalex dutkalex May 9, 2026

Choose a reason for hiding this comment

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

There are two reasons for why we can't use num_face_children/num_neighbor_face_children here:

  1. because we need an integral constant expression here (i.e. a constexpr int-like thing)
  2. because what we need here is the upper-bound of the number of face neighbors (this happens to be 4 for a 2:1 balanced interface) so that this piece of code works for any element type

Maybe there is a properly named global constant which could be used instead of this magic number?

Comment on lines +1909 to +1910
for (int i_child = 0; i_child < num_children; ++i_child) {
if (scheme->element_compare (neighbor_tree_class, target, children[i_child]) == 0) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
for (int i_child = 0; i_child < num_children; ++i_child) {
if (scheme->element_compare (neighbor_tree_class, target, children[i_child]) == 0) {
for (int i_child = 0; i_child < num_neighbor_face_children; ++i_child) {
if (scheme->element_compare (neighbor_tree_class, virtual_face_neighbor, neighbor_children_at_face[i_child]) == 0) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'll make the adequate changes here when the previous comments are settled.

Comment thread src/t8_forest/t8_forest.cxx
dutkalex and others added 2 commits May 9, 2026 23:04
Co-authored-by: spenke91 <thomas.spenke@dlr.de>
Add assertion to ensure the face was found before destruction.
@dutkalex
Copy link
Copy Markdown
Contributor Author

dutkalex commented May 9, 2026

Thank you very much @dutkalex for another great contribution! :-) I am sorry it took us so long to reply...

No worries @spenke91. Thank you for taking the time, I know you guys have a lot on your plate.

I mostly have suggestions regarding variable names and code comments (which, I know, not everybody loves as much as I do 🤓 ). Aside from that, I only have a question regarding the hard-coded 4.

I get it, I often say that getting the compiler to understand the code is the easy part and that the real challenge it to write code that humans can comprehend 😉. I have either committed your suggestions directly, or answered your comments/questions to get your opinion. Let me know what you think and I'll revise the code accordingly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement Enhances already existing code priority:medium Should be solved within half a year workload:low Would take half a day or less

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants