Skip to content

Commit 9339518

Browse files
committed
fix virtual surface crossing
1 parent 368ea06 commit 9339518

3 files changed

Lines changed: 95 additions & 1 deletion

File tree

include/openmc/cell.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,20 @@ class Region {
112112

113113
//! Determine if a particle is inside the cell for a complex cell.
114114
//!
115-
//! Uses the comobination of half-spaces and binary operators to determine
115+
//! Uses the combination of half-spaces and binary operators to determine
116116
//! if short circuiting can be used. Short cicuiting uses the relative and
117117
//! absolute depth of parentheses in the expression.
118118
bool contains_complex(Position r, Direction u, int32_t on_surface) const;
119119

120+
//! Find the oncoming boundary of this cell for a simple cell (only
121+
//! intersection operators)
122+
std::pair<double, int32_t> distance_simple(
123+
Position r, Direction u, int32_t on_surface) const;
124+
125+
//! Find the oncoming boundary of this cell for a complex cell.
126+
std::pair<double, int32_t> distance_complex(
127+
Position r, Direction u, int32_t on_surface) const;
128+
120129
//! BoundingBox if the paritcle is in a simple cell.
121130
BoundingBox bounding_box_simple() const;
122131

src/cell.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -919,6 +919,18 @@ std::string Region::str() const
919919

920920
std::pair<double, int32_t> Region::distance(
921921
Position r, Direction u, int32_t on_surface) const
922+
{
923+
if (simple_) {
924+
return distance_simple(r, u, on_surface);
925+
} else {
926+
return distance_complex(r, u, on_surface);
927+
}
928+
}
929+
930+
//==============================================================================
931+
932+
std::pair<double, int32_t> Region::distance_simple(
933+
Position r, Direction u, int32_t on_surface) const
922934
{
923935
double min_dist {INFTY};
924936
int32_t i_surf {std::numeric_limits<int32_t>::max()};
@@ -947,6 +959,37 @@ std::pair<double, int32_t> Region::distance(
947959

948960
//==============================================================================
949961

962+
std::pair<double, int32_t> Region::distance_complex(
963+
Position r, Direction u, int32_t on_surface) const
964+
{
965+
double min_dist {INFTY};
966+
int32_t i_surf {std::numeric_limits<int32_t>::max()};
967+
968+
for (int32_t token : expression_) {
969+
// Ignore this token if it corresponds to an operator rather than a region.
970+
if (token >= OP_UNION)
971+
continue;
972+
973+
// Calculate the distance to this surface.
974+
// Note the off-by-one indexing
975+
bool coincident {std::abs(token) == std::abs(on_surface)};
976+
double d {model::surfaces[abs(token) - 1]->distance(r, u, coincident)};
977+
978+
// Check if this distance is the new minimum.
979+
if (d < min_dist) {
980+
if (min_dist - d >= FP_PRECISION * min_dist) {
981+
if (!contains_complex(r + (d + TINY_BIT) * u, u, on_surface)) {
982+
min_dist = d;
983+
i_surf = -token;
984+
}
985+
}
986+
}
987+
}
988+
return {min_dist, i_surf};
989+
}
990+
991+
//==============================================================================
992+
950993
bool Region::contains(Position r, Direction u, int32_t on_surface) const
951994
{
952995
if (simple_) {

tests/unit_tests/test_surface_flux.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Tests for surface flux tallying via flux score + SurfaceFilter."""
22

33
import math
4+
import numpy as np
45
import pytest
56

67
import openmc
@@ -118,3 +119,44 @@ def test_cellfrom_filter_flux_directional(two_cell_model, run_in_tmpdir):
118119
assert mean_from1 == pytest.approx(1.0)
119120
# No particles cross xmid from cell2 → flux = 0
120121
assert mean_from2 == pytest.approx(0.0)
122+
123+
124+
def test_surface_filter_do_not_tally_virtual_surface_crossing(run_in_tmpdir):
125+
openmc.reset_auto_ids()
126+
model = openmc.Model()
127+
zmin = openmc.ZPlane(z0 = -1.0, surface_id=1, name="plane 1")
128+
zmax = openmc.ZPlane(z0 = 1.0, surface_id=2, name="plane 2")
129+
ymin = openmc.YPlane(y0 = -1.0, surface_id=3, name="plane 3")
130+
ymax = openmc.YPlane(y0 = 1.0, surface_id=4, name="plane 4")
131+
xmin = openmc.XPlane(x0 = -1.0, surface_id=5, name="plane 5")
132+
xmax = openmc.XPlane(x0 = 1.0, surface_id=6, name="plane 6")
133+
sph = openmc.Sphere(r=100.0, boundary_type='vacuum')
134+
135+
cube = (+zmin & -zmax & +ymin & -ymax & +xmin & -xmax )
136+
sphere = -sph&~(cube)
137+
138+
cube_cell = openmc.Cell(region=cube)
139+
sphere_cell = openmc.Cell(region=sphere)
140+
141+
univ = openmc.Universe(cells=[cube_cell, sphere_cell])
142+
model.geometry = openmc.Geometry(univ)
143+
144+
src = openmc.IndependentSource()
145+
src.space = openmc.stats.Point((0.0, 0.0, 0.0))
146+
147+
model.settings.run_mode = 'fixed source'
148+
model.settings.batches = 1
149+
model.settings.particles = 100
150+
model.settings.source = src
151+
152+
surface_filter = openmc.SurfaceFilter([1,2,3,4,5,6])
153+
tally = openmc.Tally()
154+
tally.scores = ["current"]
155+
tally.filters = [surface_filter]
156+
model.tallies = [tally]
157+
158+
model.run(apply_tally_results=True)
159+
current_mean = np.abs(tally.mean.flatten())
160+
161+
# Every particle crosses exit the cube with weight 1, so current = 1.0
162+
assert current_mean.sum() == pytest.approx(1.0, rel=1e-8)

0 commit comments

Comments
 (0)