Skip to content

Conversation

pbehne
Copy link
Contributor

@pbehne pbehne commented Jul 28, 2025

This pull request extends the VariationalMeshSmoother to handle higher-order elements, as part of the work on issue #4082.

  • Improved Test Coverage:

    • Updated the VariationalMeshSmoother tester to better handle symmetric configurations. Nodes previously unaffected by the DistortHypercube due to lying on planes of symmetry now receive a 5% forced distortion.
    • The above forced distortion removed the need to skip distortion checks on nodes with coordinate value 0.5, simplifying the distortion_is logic.
  • Extended Element Support:

    • EDGE3 and HEX20: Added tests verifying smoother functionality on these higher-order elements. Minor adjustments to distortion_is were made to correctly map midpoint nodes to integer values using fe_order.
    • TRI6: Introduced a target equilateral quadratic triangle to support smoothing for TRI6 elements, along with corresponding tests.
    • HEX27: Implemented logic to determine face neighbors for face-center nodes when no nodal neighbors are available, enabling full support and test coverage for HEX27 elements.

Ref #4082.

pbehne added 6 commits July 28, 2025 15:04
Previously, some nodes lay on a plane of symmetry and were not distorted
by the DistortHypercube class. A check has been added for this and if a
node will not be distorted due to symmetry, a 5% distortion is forced.
Because of this, we no longer have to skip checks in distortion_is on
nodes with a coordinate value of 0.5.
Smoothing for these higher-order elements worked out-of-the box and only
required a small tweak to the `distortion_is` function to map midpoint
nodes to integer values.

This also required tightening the tolerance in the constraint variants.
This required adding a target equilateral quadratic triangle to the
VariationalSmootherSystem for TRI6 elements.
This involved implementing a utility function to return face neighbors
when no nodal neighbors are found for a face's central node.

Ref libMesh#4082
Comment on lines 539 to 541
// It doesn't make sense for a node having no edge neighbors to belong to
// multiple elements, right? Otherwise, we will figure out those cases as
// they occur.
Copy link
Member

Choose a reason for hiding this comment

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

Won't any face-centered node on the "boundary" between two subdomains belong to two elements?

Copy link
Member

Choose a reason for hiding this comment

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

But you've already instantiated the HEX27 tests!? I'd have expected that to be the iconic example of the problem.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, so these high order hex tests take a bit to run in dbg mode, so I always ran them in opt and didn't see the assertion hit in dbg until I put this PR up. I don't know what I was thinking when I wrote the assertion. I took it out.

Copy link
Member

Choose a reason for hiding this comment

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

How'd we pass tests in CI? I could have sworn the 32-bit recipe was running with unit tests enabled and in dbg mode.

@moosebuild
Copy link

moosebuild commented Jul 29, 2025

Job Coverage, step Generate coverage on ead09fc wanted to post the following:

Coverage

d1acc0 #4218 ead09f
Total Total +/- New
Rate 64.45% 64.54% +0.08% 97.37%
Hits 75713 75828 +115 74
Misses 41759 41671 -88 2

Diff coverage report

Full coverage report

This comment will be updated on new commits.

Copy link
Member

@roystgnr roystgnr left a comment

Choose a reason for hiding this comment

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

This looks fine to me just reading the code, but before we merge:

I want to give @jwpeterson a day to look on it or pass on looking on it

I want to kick those Fetch failures and let CI finish

I want to figure out why exactly we weren't tripping that bogus assert before! Even if the library code is right, maybe there's something screwed up in the test coverage?

@pbehne
Copy link
Contributor Author

pbehne commented Jul 29, 2025

I want to figure out why exactly we weren't tripping that bogus assert before! Even if the library code is right, maybe there's something screwed up in the test coverage?

Is this something you want to check, or want me to look into it?

@roystgnr
Copy link
Member

Is this something you want to check, or want me to look into it?

Between errands this morning and tomorrow I'm running stupidly behind, so ... wait a minute. Emphasis on "stupidly". That commit was hitting that assertion failure left and right! E.g. https://civet.inl.gov/job/3073780/

I could swear I saw it pass but maybe that was only after the assertion-removal commit.

Okay, we'll merge tomorrow morning unless John screams.

@@ -195,7 +195,7 @@ void VariationalSmootherSystem::prepare_for_smoothing()
// Scale the nodal positions to conserve area
auto node_ptr = std::make_unique<Node>(
equilateral_points[node_id] * side_length, node_id);
target_elem->set_node(node_id) = node_ptr.get();
target_elem->set_node(node_id, node_ptr.get());
Copy link
Member

@jwpeterson jwpeterson Jul 30, 2025

Choose a reason for hiding this comment

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

I think I'll make a separate PR with just this fix as well, it shouldn't conflict with #4218 and it will give us a working --disable-deprecated build before #4218 just in case that is useful for debugging for some reason...

…_tri6

Conflicts:
	src/systems/variational_smoother_system.C
@jwpeterson
Copy link
Member

I fixed a conflict with devel (that I introduced, sorry!) and updated the branch. I also haven't reviewed this yet, but I'll do so now.

@@ -521,6 +521,47 @@ void VariationalSmootherConstraint::constrain_node_to_line(const Node & node,
}
}

void VariationalSmootherConstraint::find_nodal_or_face_neighbors(
const MeshBase &mesh, const Node &node,
Copy link
Member

Choose a reason for hiding this comment

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

Sorry to continue harping on this, but we put spaces both before and after the & and *, so here it would be:

const MeshBase & mesh, const Node & node,

In addition we usually do a hanging indent when there are multiple lines of parameters rather than indenting a fixed four spaces.

I think the clang-format checked in to the repo handles this automatically (?), but I've never used it.

if (!neighbors.size())
{
// Grab the element containing node
const auto *elem = nodes_to_elem_map.at(node.id()).front();
Copy link
Member

Choose a reason for hiding this comment

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

Always use libmesh_map_find() instead of map::at(). The former gives a nicely formatted error message in the exception when the key is not found, the latter just throws a generic std::exception.

Comment on lines 545 to 550
for (const auto &local_node_id : nodes_on_side)
if (elem->node_id(local_node_id) == node.id())
{
found_node = true;
break;
}
Copy link
Member

Choose a reason for hiding this comment

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

I'd refactor this into a std::find_if(nodes_on_side.begin(), nodes_on_side.end(), [&](auto local_node_id){...}); call rather than hand-coding the linear search.

Comment on lines 196 to 197
auto node_ptr = std::make_unique<Node>(
equilateral_points[node_id] * side_length, node_id);
Copy link
Member

Choose a reason for hiding this comment

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

Node::build() can be used here, and I'd emplace_back() the new Node directly into owned_nodes, catching the reference returned by that to then pass to target_elem->set_node(...). This just saves the step of defining the node_ptr temporary variable.

}// if find == end()

// Reference volume computation
Real elem_integrated_det_J(0.);
Copy link
Member

Choose a reason for hiding this comment

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

Minor comment but I don't think we use this type of initialization for Reals elsewhere (we typically use boring Real foo = 0.;) so it looks a bit out of place IMO.

@@ -101,12 +103,12 @@ private:
modulation *= (pj - 0.5) * (pj - 0.5) * 4.; // quadratic bump centered at 0.5
}
}
output(i) = p(i) + (std::pow(xi, 3) - xi) * modulation;
const auto delta = (std::pow(xi, 3) - xi) * modulation;
Copy link
Member

Choose a reason for hiding this comment

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

Please use libMesh::Utility::pow<N> for all integer powers. I know this isn't a new line of code in this PR, but I forgot to mention it on the earlier one. It is just a template function that compiles down to pure multiplication/no function calls at runtime.

Comment on lines +281 to +284
// Get mesh dimension, determine whether type is triangular
const auto * ref_elem = &(ReferenceElem::get(type));
const auto dim = ref_elem->dim();
const bool type_is_tri = dynamic_cast<const Tri*>(ref_elem);
Copy link
Member

Choose a reason for hiding this comment

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

Not necessary for this PR, but this feels so clunky that we should probably add a helper function that does this by looking only at an ElemType enum. Not sure what the best place would be, but maybe a helper function like enum_to_string() would probably make sense. It would need to be updated as new Tri types are added, but we have a few things like that already so I wouldn't mind one more.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Created #4220 for this.

@roystgnr roystgnr merged commit 231f9e6 into libMesh:devel Jul 31, 2025
20 checks passed
@pbehne pbehne deleted the variational_smoother_tri6 branch July 31, 2025 22:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants