Raising this on my fork first for discussion before deciding whether it belongs upstream.
Summary
An unstructured tet mesh whose tetrahedra are negatively oriented (inward winding) is loaded by the MOAB backend without any warning. The raw track-length tally is unaffected, but MOAB reports signed element volumes, so anything that normalises by mesh.volumes silently produces negative flux densities and a badly wrong total. UnstructuredMesh.write_data_to_vtk(volume_normalization=True) is the default, and normalising a track-length tally by element volume is the standard route to a flux density, so this is on a well-travelled path.
Reproduction and measurement
Cube of side 10 meshed to 3874 tets, 14.06 MeV point source, He4, 10 x 20000 particles, openmc 0.15.3 with DAGMC/MOAB enabled. The three meshes are the same tet set, identical count and identical sorted-connectivity fingerprint; only the per-tet vertex ordering differs. Zero degenerate tets in all three. Same seed and source throughout, so the particle histories are identical.
|
correctly oriented |
mixed (44.5% negative) |
all inverted |
| MOAB / OpenMC load |
accepted |
accepted, no warning |
accepted, no warning |
| total track-length flux |
6.25559042 +/- 4.65e-3 |
6.25559042 |
6.25559042 |
| z vs oriented |
- |
0.000 |
0.000 |
| bins with abs(z) > 3 |
- |
0 |
0 |
| MOAB total volume (analytic 1000) |
+1000.0000 |
+251.2618 |
-1000.0000 |
| MOAB negative element volumes |
0 |
1724 / 3874 |
3874 / 3874 |
| MOAB sum of abs(volumes) |
1000.0 |
1000.0 |
1000.0 |
| volume-normalised flux density, total |
+14.4495 |
+4.0606 |
-14.4495 |
| elements with negative flux density |
0 |
1724 |
3874 |
So:
- Point location and track-length scoring are orientation-insensitive. All three agree to 1e-13 relative (float summation-order noise) and all three match a CSG cell tally over the same cube to z = -0.000. The mesh geometry is being handled correctly.
- The volumes are not.
abs(volumes) is bit-identical across all three, so it is purely the sign. The mixed mesh reports a total volume 74.9 percent low and the inverted one reports exactly minus the right answer.
- The consequence is silent. No warning is emitted at any stage. The only console output is the unrelated
Output for a MOAB mesh (mesh 1) was requested but will not be written.
Why it matters
A negative element volume is never physical, so this is unambiguously bad input, but nothing tells the user. They get a VTK file with negative flux densities in some elements and a total that is wrong by an amount depending on what fraction of their mesh is inverted. If they sum or average across elements, the negatives partially cancel the positives and the result looks plausible.
Meshers do not all guarantee a winding convention, so mixed orientation is a realistic input rather than a contrived one. For context, the same class of defect in our own code (a neutronics element walk that reads outward face normals off a fixed vertex ordering) produced a tally reading 33 percent low, also silently: fusion-neutronics/yamc#316.
Suggested fix
Detect it at load and say so: when an unstructured mesh contains elements of negative volume, warn (or error) naming the count and an example element, rather than proceeding.
I would specifically argue against silently taking abs(volume), even though it would produce the right number here. That repairs a broken mesh behind the user's back and hides the producing mesher's bug, which is the same anti-pattern that let the yamc case run for so long. We recently went the other way there and made bad orientation a hard error with an actionable message, which surfaced the real problem immediately.
Happy to put a PR together if the approach sounds right. The open question is whether warn or error is the correct severity, given that the raw tally is genuinely unaffected and some users may legitimately only ever consume un-normalised totals.
Raising this on my fork first for discussion before deciding whether it belongs upstream.
Summary
An unstructured tet mesh whose tetrahedra are negatively oriented (inward winding) is loaded by the MOAB backend without any warning. The raw track-length tally is unaffected, but MOAB reports signed element volumes, so anything that normalises by
mesh.volumessilently produces negative flux densities and a badly wrong total.UnstructuredMesh.write_data_to_vtk(volume_normalization=True)is the default, and normalising a track-length tally by element volume is the standard route to a flux density, so this is on a well-travelled path.Reproduction and measurement
Cube of side 10 meshed to 3874 tets, 14.06 MeV point source, He4, 10 x 20000 particles, openmc 0.15.3 with DAGMC/MOAB enabled. The three meshes are the same tet set, identical count and identical sorted-connectivity fingerprint; only the per-tet vertex ordering differs. Zero degenerate tets in all three. Same seed and source throughout, so the particle histories are identical.
So:
abs(volumes)is bit-identical across all three, so it is purely the sign. The mixed mesh reports a total volume 74.9 percent low and the inverted one reports exactly minus the right answer.Output for a MOAB mesh (mesh 1) was requested but will not be written.Why it matters
A negative element volume is never physical, so this is unambiguously bad input, but nothing tells the user. They get a VTK file with negative flux densities in some elements and a total that is wrong by an amount depending on what fraction of their mesh is inverted. If they sum or average across elements, the negatives partially cancel the positives and the result looks plausible.
Meshers do not all guarantee a winding convention, so mixed orientation is a realistic input rather than a contrived one. For context, the same class of defect in our own code (a neutronics element walk that reads outward face normals off a fixed vertex ordering) produced a tally reading 33 percent low, also silently: fusion-neutronics/yamc#316.
Suggested fix
Detect it at load and say so: when an unstructured mesh contains elements of negative volume, warn (or error) naming the count and an example element, rather than proceeding.
I would specifically argue against silently taking
abs(volume), even though it would produce the right number here. That repairs a broken mesh behind the user's back and hides the producing mesher's bug, which is the same anti-pattern that let the yamc case run for so long. We recently went the other way there and made bad orientation a hard error with an actionable message, which surfaced the real problem immediately.Happy to put a PR together if the approach sounds right. The open question is whether warn or error is the correct severity, given that the raw tally is genuinely unaffected and some users may legitimately only ever consume un-normalised totals.