Skip to content

Fix get_index_in_direction for regular meshes#3948

Open
JoffreyDorville wants to merge 12 commits into
openmc-dev:developfrom
JoffreyDorville:fix_get_index_in_direction_regular_meshes
Open

Fix get_index_in_direction for regular meshes#3948
JoffreyDorville wants to merge 12 commits into
openmc-dev:developfrom
JoffreyDorville:fix_get_index_in_direction_regular_meshes

Conversation

@JoffreyDorville

@JoffreyDorville JoffreyDorville commented May 28, 2026

Copy link
Copy Markdown
Contributor

Description

For regular meshes, the function get_index_in_direction is returning 0 instead of 1 when the tested coordinate is coincident with the lower boundary. This results in points being incorrectly seen outside the mesh when they lie on surfaces corresponding to the lower boundaries of the mesh.

For rectilinear meshes, the function get_index_in_direction works as expected.

I added a cpp unit test to illustrate the problem and to show that this PR fixes it.

This fix also corrected the results of the weightwindows/survival_biasing regression tests which have been updated. I cross-checked that using an equivalent rectilinear mesh instead of the regular mesh in tests/regression_tests/weightwindows/survival_biasing/test.py produces the same results as the updated ones with the fix.

Rectilinear mesh used:

mesh = openmc.RectilinearMesh()
mesh.x_grid = np.linspace(0.0, 160.0, 21)
mesh.y_grid = np.linspace(0.0, 160.0, 21)
mesh.z_grid = np.linspace(0.0, 160.0, 2)

Checklist

  • I have performed a self-review of my own code
  • I have run clang-format (version 18) on any C++ source files (if applicable)
  • I have followed the style guidelines for Python source files (if applicable)
  • I have made corresponding changes to the documentation (if applicable)
  • I have added tests that prove my fix is effective or that my feature works (if applicable)

Comment thread src/mesh.cpp Outdated

@GuySten GuySten left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks good to me.

@GuySten GuySten added the Merging Soon PR will be merged in < 24 hrs if no further comments are made. label May 29, 2026

@paulromano paulromano left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm not sure I agree with the change. When the position being tested is on the boundary between two mesh elements, it is ambiguous as to whether it should be considered as being in the mesh element to the left or to the right. To me it seems more consistent to always pick one direction.

We have other ways of handling this ambiguity elsewhere in the code such as 1) pushing the position forward by TINY_BIT to move it off the boundary, or 2) Looking at the dot product of u and the surface normal to decide which one to choose.

@GuySten

GuySten commented May 30, 2026

Copy link
Copy Markdown
Contributor

I think using the particle direction is a good solution.
In any case, equivalent regular and rectilinear meshes should have the same behavior.
Currently they behave differently.

@GuySten GuySten removed the Merging Soon PR will be merged in < 24 hrs if no further comments are made. label May 30, 2026
@JoffreyDorville

Copy link
Copy Markdown
Contributor Author

Thanks for the feedback @paulromano!

I just added the direction check for regular and rectilinear meshes. The solution is even simpler than pushing the particle or using the surface normal.

For cylindrical and spherical meshes, the task is a bit more involved because of specific cases (i.e., non-convex shapes). I have some ideas on how to handle these cases but it can be on a different PR as it is not required for the current state of the temperature field PR.

I have not looked into unstructured meshes for the moment.

I changed the signature of several functions that needed the new direction parameter, which also had the drawback of changing the signature of functions that did not need it. Let me know what you prefer for the Mesh interface.

@paulromano paulromano left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I made some comments below regarding the current implementation. However, I think the bigger issue is that this seems like an overly complicated solution for a relatively minor consistency issue. Sorry to go back and forth here, but I would prefer either:

  1. Reverting to the original fix, if (r == lower_left_[i]) return 1, or
  2. Change the implementation of RectilinearMesh::get_index_in_direction to use std::lower_bound directly (std::lower_bound(grid_[i].begin(), grid_[i].end(), r) - grid_[i].begin()). The reason it currently behaves differently than RegularMesh is because lower_bound_index has a special case for the first value in an array.

I lean toward solution 2 because it would treat all mesh cells equivalently with respect to behavior on the boundary (each mesh cells owns its upper boundary but not its lower).

Comment thread src/mesh.cpp
Comment on lines +1476 to +1483
int idx = std::ceil((r - lower_left_[i]) / width_[i]);

// If on upper boundary with positive direction, use next index
if (r == lower_left_[i] + width_[i] * idx) {
if (u > 0.0) {
idx++;
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This doesn't really match mentally what I was expecting. I would have thought that a particle that is coincident with any mesh boundary would account for the direction, not just the upper boundary. Additionally, the check for the upper boundary is done using floating-point equality, which seems like it may fail even when the particle is exactly on the boundary because of rounding/truncation.

Comment thread src/mesh.cpp
int idx = lower_bound_index(grid_[i].begin(), grid_[i].end(), r) + 1;

// If on lower boundary with negative direction, use previous index
if (r == grid_[i][idx - 1]) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If r is below the first grid point, we would have that idx == 0, which means that grid_[i][idx - 1] would be out of bounds.

Comment thread src/mesh.cpp
}

// If on upper boundary with positive direction, use next index
if (r == grid_[i][idx]) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same consideration here: if r is above the last grid point, this will be out of bounds.

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants