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
negative Jacobian tests in 2D #10229
Conversation
Is LIBMESH_DIM=2 or LIBMESH_DIM=3 for this 2D input file? |
Job Documentation on cf2d540 wanted to post the following: View the site here This comment will be updated on new commits. |
Thanks for setting this test up, @WilkAndy! I'd love to get to the bottom of this. |
Your question about about mixed dimensions got me thinking though. When computing the Jacobian determinant for a 2D element (potentially) living in 3D space, we are talking about the determinant of a non-square matrix, which of course does not exist. So what we do, on line 797 of fe_map.C, is compute the determinant of the square matrix
This determinant can of course still be zero, which is consistent with what @WilkAndy reported above. I suspect this is why we never see negative Jacobians for 2D problems no matter how badly we invert elements, but I don't really know what the fix is, short of somehow detecting that the last row of |
BuildBot actually used to test --enable-2D-only and even --enable-1D-only regularly; they might not have succumbed to bitrot since then yet...
Even that wouldn't be a valid fix, I'm afraid, because that would be a false positive in a supported use case! We deliberately allow users to wrap 2D manifolds around in 3D space. If one of @pbauman's stretchy membrane simulations has a parachute where one element ends up flat-but-exactly-inverted (which seems improbable a priori but might happen frequently in symmetric initial conditions) then we want that calculation to chug on, not error out.
If this is for sanity checking then we don't need to put the check in every single mapping computation; you could do a single sanity-check-only sweep over elements before assembly even begins, and then not bother to re-test at every point in every residual evaluation. And if you know a priori that you're not in an "element legitimately flipped" use case then you can just run the 2D jac calculation in user space and test for non-positive. |
Ideally we'd like to be able to detect "negative Jacobian" (whatever this means for non-square Jacobian matrices) elements in the middle of e.g. solid mechanics computations with a displaced mesh, and throw an exception that would then allow us to cut the time step and retry the most recent solve. We could get part of the way there by the pre- or post-step check of the elements that you suggest, backing up an entire time step if an inverted element was detected. But even this would require a reliable algorithm for detecting negative Jacobian, non-planar manifold elements... which I am not sure how to even define at the moment. |
@WilkAndy I have added a quick and dirty "2D_in_3D" branch in my libmesh fork that detects when an Elem is xy-planar (even though it "lives" in 3D) and instead uses the standard 2D formula for computing the element Jacobian determinants. When I uncomment your new test and run
which I believe is close to (the value is off by a factor of 2) the expected result? Running the rest of the test suite with this version of libmesh also results in quite a few other failures:
that would have to be addressed on a case by case basis. So I'm not sure where we go from here, but this is at least a start. |
@gardnerru was also interested in this thread. |
Oops, there was an extra
I also updated the link above so that no one grabs the wrong commit by mistake... |
Thanks for looking into this everyone. Yes, the above Where can we see the test results (6 moose failures, 8 modules failures)? If i have to update libmesh i will be compiling all day (literally) on my little MacBookAir. I'll leave it up to you guys to decide what to do here. I'm not just being lazy or passing-the-buck - this is a pretty fundamental issue and i don't know what's best from a MOOSE perspective. Do we want MOOSE to fail in 2D simulations when elements get inverted (i think so, but will leave it up to you)? My initial reason for this Issue was that i noticed MOOSE suddenly wasn't failing (in 3D) with use_displaced_mesh=false and that was VERY GOOD for me :-) So i certainly don't want to go back to the old days of MOOSE failing for inverted use_displaced_mesh=false elements. |
I just ran them locally, didn't push a PR or anything for testing. Other than your new test (which only failed due to a slightly different expected error message), the remaining MOOSE tests that failed for me were all mortar related:
I haven't looked into it in detail, but I think those tests may be using a mesh with an inverted element somehow, and we just never caught it because of the problem with the 2D Jacobian calculation. |
And were the modules failures in tensor mechanics? |
A couple of them were, yeah:
The
The |
Wow, i have no idea what that |
Just to chime in on this point, it definitely can happen. When we first started playing with those meshes, we actually had to do some cleanup because some elements didn't have a consistent outward normal (some pointed inside the parachute, some outside). That required some reorienting of some elements. |
My current thinking on this issue is: for a 2D element living in 3D it is not possible to reliably detect an inverted/bad element (via Jacobian determinant sign) without some kind of external information, i.e. a "reference normal", being provided by the user. We can't use the element's own surface normal, because a twisted element will have a correspondingly inverted normal and its area would be self-consistent with this. Therefore, we might as well go with the current implementation which works in the general case, but which we now know is too permissive in that it allows all kinds of tangled and badly-shaped elements. We would then additionally provide a sanity check like Not sure if this will fix the case that @WilkAndy was originally trying to address since you won't be able to throw an exception immediately (i.e. in the middle of |
Well, a "twisted" quad would have a normal at some quadrature points which was opposite the normal at others, but I would expect tensor mechanics simulations on quad meshes to risk wholly-inverted elements, not just twisted elements, and on TRI3 meshes you would only ever get wholly-inverted elements. So a sanity check that could only detect twisted quad/TRI6 elements would be little more than false confidence.
So, a third option just now occurred to me: should MOOSE (or libMesh, for that matter) provide applications a way to save the current normals for all top-level elements, and then reuse those saved normals (looking them up via top_parent() in the case of AMR) for sanity checking later? When the user reads in their mesh, whether it's something generated a priori or a restart mesh from a previous (sanity-checked!) run, they ought to know that that mesh has no inverted or twisted elements. And if it has no inverted or twisted elements initially, then they can rely on those initial normal vectors to determine whether each element or any of its descendants has been inverted or twisted subsequently. This would only work if you are solving meshes with 2D manifolds where the element movement is constrained to remain within the manifold (because if not then e.g. one of @pbauman's parachutes can "fold" around a stiffener and that's not non-physical) and where the manifold doesn't curve too much (because if an element can "slide" around a 90-degree bend then |
You're right, bad terminology by me. I was thinking primarily of the wholly inverted case while writing this.
This is an interesting idea. It would allow initial meshes which were "bad" for whatever reason (this sometimes happens when people build meshes by hand or try to implement a reader for their own mesh format) but I suspect that to be a small percentage of use cases. Storing the extra information in a user-friendely, parallel, elegant, way consistent with the rest of the library sounds like it might be a bit of a chore though. |
Maybe, maybe not: libMesh/libmesh#1439 |
Currently, at least for mechanics simulations, all of the 2D problems that we solve in MOOSE have 2D elements in a 2D domain. These models also are currently always defined in the xy plane, although I imagine they could be defined in other planes. This is the case that's really important for us to have an inversion check for currently. Detecting inversion of a 2D element in 3D space is a far more exotic problem, and probably far less likely to occur because elements can distort out of plane when they are pushed in ways that tend to make them invert. We also don't even have shell elements in MOOSE yet, so it's not even relevant to us yet (although we do have plans to add such a capability in the near future). I just looked at some of our 2D meshes, and I think we usually (maybe always) have 'num_dim=2' set in the exodus mesh. Couldn't we just have some way to either detect that the mesh is living in 2D (by looking at num_dim in the case of exodus meshes) or have an API to tell libMesh that the mesh is in 2D, and then modify the inversion check to take that into account? I envision some kind of enum that stores the dimensionality of the mesh, as well as information about the plane (or axis for the 1D case) it lives in. This could be used for inversion checks of 1D elements in 1D space as well. It could have values like 1DX, 1DY, 1DZ, 2DXY, 2DXZ, 2DYZ, 3D. With that information available, I imagine that we would be able to do efficient inversion checks within fe.reinit() for all these cases. |
@jwpeterson - What's the final resolution on this, or is there one? Do we need to talk about this some more. This is nearly two months old now. |
I think the best solution is to not change the current behavior of Instead, we could add a sanity check based on either a specified reference normal, as described in my previous comment, or something more heavy-handed (like described in Roy's followup) that would alert users when an element's Jacobian goes bad. This sanity check could eventually be used to cut back timesteps or in some other way allow user's to recover from previous solutions once elements become inverted. |
It's late on Friday afternoon (=beer) and i just noticed this negative-Jacobian thing again. Thanks for all your contemplating, everyone. I hope you find it fun to fix, when you eventually get round to it :-) |
Closing due to inactivity but we need to fix this soon! |
:-( |
These tests ensure that in 2D: - when use_displaced_mesh=false, and when an element is inverted, MOOSE will not throw an error; - when use_displaced_mesh=false, and when an element is distorted to have exactly zero element-Jacobian, MOOSE will not throw an error; - when use_displaced_mesh=true, and when an element is distorted to have exactly zero element-Jacobian, MOOSE correctly will throw an error The tests do NOT test in 2D: - when use_displaced_mesh=true, and when an element is inverted to produce a negative element-Jacobian, MOOSE correctly throws an error. See lengthy discussion at idaholab#10229 Refs idaholab#9740
Refs #9740
I need help with this. @jwpeterson ?
This PR contains a single 2D input file that illustrates:
(1) MOOSE does not fail if an element is inverted, if the kernels use_displaced_mesh=false
(2) MOOSE does fail (correctly) if an element is deformed such that Jacobian=0, if the kernels use_displaced_mesh=true
But, i can't get MOOSE to fail if:
(3) an element is deformed such that Jacobian < 0, if the kernels use_displaced_mesh=true
There is already a 3D test (in the same directory) that illustrate (1), (2) and (3) work in 3D.
The question is: why is Libmesh not producing an error in 2D when Jacobian < 0. From line 736 of
libmesh/src/fe/fe_map.C
i believe that it should.