Skip to content

Commit 7af3435

Browse files
committed
Reverting the sorting parts in filter and mesh python files
1 parent 357bf62 commit 7af3435

4 files changed

Lines changed: 101 additions & 56 deletions

File tree

openmc/filter.py

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1112,20 +1112,12 @@ def from_volumes(cls, mesh: openmc.MeshBase, volumes: openmc.MeshMaterialVolumes
11121112
A new MeshMaterialFilter instance
11131113
11141114
"""
1115-
# Build bins sorted by volume ascending (mat ID ascending as tiebreaker)
1116-
# so the bin ordering is deterministic regardless of the ray-traversal
1117-
# order stored in the raw _materials array.
1118-
bins = []
1119-
for i in range(volumes.num_elements):
1120-
entries = volumes.by_element(i)
1121-
# sort by (volume asc, mat_id asc), excluding void (None)
1122-
entries = sorted(
1123-
((mat_id, vol)
1124-
for mat_id, vol in entries if mat_id is not None),
1125-
key=lambda t: (t[1], t[0])
1126-
)
1127-
for mat_id, _ in entries:
1128-
bins.append((i, mat_id))
1115+
# Get flat arrays of material IDs and element indices
1116+
mat_ids = volumes._materials[volumes._materials > -1]
1117+
elems, _ = np.where(volumes._materials > -1)
1118+
1119+
# Stack them into a 2D array of (element, material) pairs
1120+
bins = np.column_stack((elems, mat_ids))
11291121
return cls(mesh, bins)
11301122

11311123
def __hash__(self):

openmc/mesh.py

Lines changed: 2 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -506,33 +506,7 @@ def material_volumes(
506506

507507
# Restore original tallies
508508
model.tallies = original_tallies
509-
510-
# Sort each element's materials by material ID (ascending), with
511-
# None (-1) after positive IDs and empty slots (-2) last. This
512-
# gives a deterministic ordering that is independent of the ray
513-
# traversal order used by the C library.
514-
mat_arr = volumes._materials.copy()
515-
vol_arr = volumes._volumes.copy()
516-
bbox_arr = volumes._bboxes.copy() if volumes._bboxes is not None else None
517-
n_elements, table_size = mat_arr.shape
518-
519-
def _sort_key(m):
520-
m = int(m)
521-
if m == -2:
522-
return (2, 0) # empty slot – always last
523-
if m == -1:
524-
return (1, 0) # vacuum / None – just before empty
525-
return (0, m) # real material – ascending ID
526-
527-
for i in range(n_elements):
528-
indices = sorted(range(table_size),
529-
key=lambda j: _sort_key(mat_arr[i, j]))
530-
mat_arr[i] = mat_arr[i, indices]
531-
vol_arr[i] = vol_arr[i, indices]
532-
if bbox_arr is not None:
533-
bbox_arr[i] = bbox_arr[i, indices]
534-
535-
return MeshMaterialVolumes(mat_arr, vol_arr, bbox_arr)
509+
return volumes
536510

537511

538512
class StructuredMesh(MeshBase):
@@ -3366,6 +3340,7 @@ def append_dataset(dset, array):
33663340
)
33673341

33683342
with h5py.File(filename, "w") as f:
3343+
33693344
root = f.create_group("VTKHDF")
33703345
vtk_file_format_version = (2, 1)
33713346
root.attrs["Version"] = vtk_file_format_version

tests/unit_tests/conftest.py

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,9 @@
1-
import os
2-
31
import openmc
42
import pytest
53

64
from tests.regression_tests import config
75

86

9-
@pytest.fixture
10-
def run_in_tmpdir(tmp_path):
11-
"""Run test in a temporary directory, restoring cwd afterwards."""
12-
orig = os.getcwd()
13-
os.chdir(tmp_path)
14-
yield tmp_path
15-
os.chdir(orig)
16-
17-
187
@pytest.fixture(scope='module')
198
def mpi_intracomm():
209
if config['mpi']:

tests/unit_tests/test_mesh.py

Lines changed: 93 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from math import pi
22
from tempfile import TemporaryDirectory
33
from pathlib import Path
4+
import itertools
5+
import random
46

57
import h5py
68
import numpy as np
@@ -642,6 +644,7 @@ def test_mesh_get_homogenized_materials():
642644

643645
@pytest.fixture
644646
def sphere_model():
647+
openmc.reset_auto_ids()
645648
# Model with three materials separated by planes x=0 and z=0
646649
mats = []
647650
for i in range(3):
@@ -768,6 +771,49 @@ def test_mesh_material_volumes_serialize_with_bboxes():
768771
np.testing.assert_array_equal(second.upper_right, (5.0, 6.0, 7.0))
769772

770773

774+
def test_mesh_material_volumes_serialize_with_bboxes():
775+
materials = np.array([
776+
[1, -1, -2],
777+
[-1, -2, -2],
778+
[2, 1, -2],
779+
[2, -2, -2]
780+
])
781+
volumes = np.array([
782+
[0.5, 0.5, 0.0],
783+
[1.0, 0.0, 0.0],
784+
[0.5, 0.5, 0.0],
785+
[1.0, 0.0, 0.0]
786+
])
787+
788+
# (xmin, ymin, zmin, xmax, ymax, zmax)
789+
bboxes = np.empty((4, 3, 6))
790+
bboxes[..., 0:3] = np.inf
791+
bboxes[..., 3:6] = -np.inf
792+
bboxes[0, 0] = [-1.0, -2.0, -3.0, 1.0, 2.0, 3.0] # material 1
793+
bboxes[0, 1] = [-5.0, -6.0, -7.0, 5.0, 6.0, 7.0] # void
794+
bboxes[1, 0] = [0.0, 0.0, 0.0, 10.0, 1.0, 2.0] # void
795+
bboxes[2, 0] = [-1.0, -1.0, -1.0, 0.0, 0.0, 0.0] # material 2
796+
bboxes[2, 1] = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0] # material 1
797+
bboxes[3, 0] = [-2.0, -2.0, -2.0, 2.0, 2.0, 2.0] # material 2
798+
799+
mmv = openmc.MeshMaterialVolumes(materials, volumes, bboxes)
800+
with TemporaryDirectory() as tmpdir:
801+
path = f'{tmpdir}/volumes_bboxes.npz'
802+
mmv.save(path)
803+
loaded = openmc.MeshMaterialVolumes.from_npz(path)
804+
805+
assert loaded.has_bounding_boxes
806+
first = loaded.by_element(0, include_bboxes=True)[0][2]
807+
assert isinstance(first, openmc.BoundingBox)
808+
np.testing.assert_array_equal(first.lower_left, (-1.0, -2.0, -3.0))
809+
np.testing.assert_array_equal(first.upper_right, (1.0, 2.0, 3.0))
810+
811+
second = loaded.by_element(0, include_bboxes=True)[1][2]
812+
assert isinstance(second, openmc.BoundingBox)
813+
np.testing.assert_array_equal(second.lower_left, (-5.0, -6.0, -7.0))
814+
np.testing.assert_array_equal(second.upper_right, (5.0, 6.0, 7.0))
815+
816+
771817
def test_mesh_material_volumes_boundary_conditions(sphere_model):
772818
"""Test the material volumes method using a regular mesh
773819
that overlaps with a vacuum boundary condition."""
@@ -841,6 +887,53 @@ def test_mesh_material_volumes_bounding_boxes():
841887
np.testing.assert_allclose(bbox.lower_left, cell_bbox.lower_left, atol=tol)
842888
np.testing.assert_allclose(bbox.upper_right, cell_bbox.upper_right, atol=tol)
843889

890+
def test_mesh_material_volumes_bounding_boxes():
891+
# Create a model with 8 spherical cells at known locations with random radii
892+
box = openmc.model.RectangularParallelepiped(
893+
-10, 10, -10, 10, -10, 10, boundary_type='vacuum')
894+
895+
mat = openmc.Material()
896+
mat.add_nuclide('H1', 1.0)
897+
898+
sph_cells = []
899+
for x, y, z in itertools.product((-5., 5.), repeat=3):
900+
mat_i = mat.clone()
901+
sph = openmc.Sphere(x, y, z, r=random.uniform(0.5, 1.5))
902+
sph_cells.append(openmc.Cell(region=-sph, fill=mat_i))
903+
background = openmc.Cell(region=-box & openmc.Intersection([~c.region for c in sph_cells]))
904+
905+
model = openmc.Model()
906+
model.geometry = openmc.Geometry(sph_cells + [background])
907+
model.settings.particles = 1000
908+
model.settings.batches = 10
909+
910+
# Create a one-element mesh that encompasses the entire geometry
911+
mesh = openmc.RegularMesh()
912+
mesh.lower_left = (-10., -10., -10.)
913+
mesh.upper_right = (10., 10., 10.)
914+
mesh.dimension = (1, 1, 1)
915+
916+
# Run material volume calculation with bounding boxes
917+
n_samples = (400, 400, 400)
918+
mmv = mesh.material_volumes(model, n_samples, max_materials=10, bounding_boxes=True)
919+
assert mmv.has_bounding_boxes
920+
921+
# Create a mapping of material ID to bounding box
922+
bbox_by_mat = {
923+
mat_id: bbox
924+
for mat_id, vol, bbox in mmv.by_element(0, include_bboxes=True)
925+
if mat_id is not None and vol > 0.0
926+
}
927+
928+
# Match the mesh ray spacing used for the bounding box estimator.
929+
tol = 0.5 * mesh.bounding_box.width[0] / n_samples[0]
930+
for cell in sph_cells:
931+
bbox = bbox_by_mat[cell.fill.id]
932+
cell_bbox = cell.bounding_box
933+
np.testing.assert_allclose(bbox.lower_left, cell_bbox.lower_left, atol=tol)
934+
np.testing.assert_allclose(bbox.upper_right, cell_bbox.upper_right, atol=tol)
935+
936+
844937
def test_raytrace_mesh_infinite_loop(run_in_tmpdir):
845938
# Create a model with one large spherical cell
846939
sphere = openmc.Sphere(r=100, boundary_type='vacuum')
@@ -953,10 +1046,6 @@ def test_filter_time_mesh(run_in_tmpdir):
9531046
)
9541047

9551048

956-
# =============================================================================
957-
# VTKHDF Format Tests for StructuredMeshes
958-
# =============================================================================
959-
9601049
def test_regular_mesh_get_indices_at_coords():
9611050
"""Test get_indices_at_coords method for RegularMesh"""
9621051
# Create a 10x10x10 mesh from (0,0,0) to (1,1,1)

0 commit comments

Comments
 (0)