-
Notifications
You must be signed in to change notification settings - Fork 298
VariationalMeshSmoother: Support for higher order elements #4218
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
Conversation
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
// 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. |
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.
Won't any face-centered node on the "boundary" between two subdomains belong to two elements?
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.
But you've already instantiated the HEX27 tests!? I'd have expected that to be the iconic example of the problem.
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.
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.
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.
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.
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 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?
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()); |
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.
…_tri6 Conflicts: src/systems/variational_smoother_system.C
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, |
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.
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(); |
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.
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.
for (const auto &local_node_id : nodes_on_side) | ||
if (elem->node_id(local_node_id) == node.id()) | ||
{ | ||
found_node = true; | ||
break; | ||
} |
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.
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.
auto node_ptr = std::make_unique<Node>( | ||
equilateral_points[node_id] * side_length, node_id); |
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.
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.); |
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.
Minor comment but I don't think we use this type of initialization for Real
s elsewhere (we typically use boring Real foo = 0.;
) so it looks a bit out of place IMO.
tests/mesh/mesh_smoother_test.C
Outdated
@@ -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; |
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.
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.
// 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); |
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.
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.
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.
Created #4220 for this.
This pull request extends the
VariationalMeshSmoother
to handle higher-order elements, as part of the work on issue #4082.Improved Test Coverage:
VariationalMeshSmoother
tester to better handle symmetric configurations. Nodes previously unaffected by theDistortHypercube
due to lying on planes of symmetry now receive a 5% forced distortion.distortion_is
logic.Extended Element Support:
distortion_is
were made to correctly map midpoint nodes to integer values usingfe_order
.Ref #4082.