Simplify rectangular lattice crossing and correctly handle corner checks (#3703)

Co-authored-by: Paul Romano <paul.k.romano@gmail.com>
This commit is contained in:
GuySten 2026-01-06 11:29:46 +02:00 committed by GitHub
parent 60ddafa9b3
commit 818fd11b18
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 358 additions and 38 deletions

View file

@ -260,46 +260,24 @@ std::pair<double, array<int, 3>> RectLattice::distance(
// Determine the oncoming edge.
double x0 {copysign(0.5 * pitch_[0], u.x)};
double y0 {copysign(0.5 * pitch_[1], u.y)};
double z0;
// Left and right sides
double d {INFTY};
array<int, 3> lattice_trans;
if ((std::abs(x - x0) > FP_PRECISION) && u.x != 0) {
d = (x0 - x) / u.x;
if (u.x > 0) {
lattice_trans = {1, 0, 0};
} else {
lattice_trans = {-1, 0, 0};
}
}
// Front and back sides
if ((std::abs(y - y0) > FP_PRECISION) && u.y != 0) {
double this_d = (y0 - y) / u.y;
if (this_d < d) {
d = this_d;
if (u.y > 0) {
lattice_trans = {0, 1, 0};
} else {
lattice_trans = {0, -1, 0};
}
}
}
// Top and bottom sides
double d = std::min(
u.x != 0.0 ? (x0 - x) / u.x : INFTY, u.y != 0.0 ? (y0 - y) / u.y : INFTY);
if (is_3d_) {
double z0 {copysign(0.5 * pitch_[2], u.z)};
if ((std::abs(z - z0) > FP_PRECISION) && u.z != 0) {
double this_d = (z0 - z) / u.z;
if (this_d < d) {
d = this_d;
if (u.z > 0) {
lattice_trans = {0, 0, 1};
} else {
lattice_trans = {0, 0, -1};
}
}
}
z0 = copysign(0.5 * pitch_[2], u.z);
d = std::min(d, u.z != 0.0 ? (z0 - z) / u.z : INFTY);
}
// Determine which lattice boundaries are being crossed
array<int, 3> lattice_trans = {0, 0, 0};
if (u.x != 0.0 && std::abs(x + u.x * d - x0) < FP_PRECISION)
lattice_trans[0] = copysign(1, u.x);
if (u.y != 0.0 && std::abs(y + u.y * d - y0) < FP_PRECISION)
lattice_trans[1] = copysign(1, u.y);
if (is_3d_) {
if (u.z != 0.0 && std::abs(z + u.z * d - z0) < FP_PRECISION)
lattice_trans[2] = copysign(1, u.z);
}
return {d, lattice_trans};

View file

@ -0,0 +1,58 @@
<?xml version='1.0' encoding='utf-8'?>
<model>
<materials>
<material id="1">
<density value="0.001" units="g/cm3"/>
<nuclide name="N14" ao="1.0"/>
</material>
<material id="2">
<density value="7.0" units="g/cm3"/>
<nuclide name="Fe56" ao="1.0"/>
</material>
</materials>
<geometry>
<cell id="1" material="2" universe="1"/>
<cell id="2" material="1" universe="2"/>
<cell id="3" material="1" region="-5 (-1 | 2 | -3 | 4)" universe="4"/>
<cell id="4" fill="3" region="1 -2 3 -4" universe="4"/>
<lattice id="3">
<pitch>10.0 10.0</pitch>
<dimension>2 2</dimension>
<lower_left>-10.0 -10.0</lower_left>
<universes>
1 2
2 1 </universes>
</lattice>
<surface id="1" name="minimum x" type="x-plane" coeffs="-10.0"/>
<surface id="2" name="maximum x" type="x-plane" coeffs="10.0"/>
<surface id="3" name="minimum y" type="y-plane" coeffs="-10.0"/>
<surface id="4" name="maximum y" type="y-plane" coeffs="10.0"/>
<surface id="5" type="z-cylinder" boundary="vacuum" coeffs="0.0 0.0 20.0"/>
</geometry>
<settings>
<run_mode>fixed source</run_mode>
<particles>1000</particles>
<batches>10</batches>
<source type="independent" strength="1.0" particle="neutron">
<space type="point">
<parameters>-0.7071067811865476 -0.7071067811865475 0.0</parameters>
</space>
<angle type="monodirectional" reference_uvw="0.7071067811865476 0.7071067811865475 0.0"/>
</source>
</settings>
<tallies>
<mesh id="1">
<dimension>10 10</dimension>
<lower_left>-20.0 -20.0</lower_left>
<upper_right>20.0 20.0</upper_right>
</mesh>
<filter id="1" type="mesh">
<bins>1</bins>
</filter>
<tally id="1">
<filters>1</filters>
<scores>flux</scores>
<estimator>tracklength</estimator>
</tally>
</tallies>
</model>

View file

@ -0,0 +1,201 @@
tally 1:
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
2.648775E-03
7.016009E-06
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
2.981847E-03
8.891414E-06
3.331677E-03
1.110007E-05
6.742237E-03
4.545776E-05
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
4.388206E-03
1.925636E-05
2.032104E-03
4.129446E-06
3.014946E-03
9.089898E-06
7.058968E-03
4.982903E-05
9.013188E-04
8.123755E-07
7.501461E-04
5.627191E-07
0.000000E+00
0.000000E+00
5.134898E-03
2.636718E-05
1.207302E-03
1.457579E-06
3.045925E-03
9.277657E-06
4.132399E-03
1.707672E-05
1.055271E-02
5.829711E-05
4.014909E-03
1.611949E-05
2.698215E-03
7.280363E-06
1.751215E-02
1.073504E-04
1.356908E-02
9.589157E-05
5.451466E-03
2.971848E-05
3.165676E-04
1.002151E-07
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
3.724938E-03
1.387516E-05
2.812863E-03
7.278837E-06
1.000700E+01
1.001402E+01
2.345904E-02
2.127641E-04
2.451653E-02
1.750725E-04
8.695904E-03
5.553981E-05
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
4.947565E-03
2.447840E-05
1.725721E-02
8.878803E-05
5.656444E+01
3.199538E+02
2.159961E-02
1.034364E-04
3.091063E-02
5.543317E-04
4.232209E-03
1.791159E-05
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
4.716170E-03
2.224226E-05
1.321855E-02
5.288265E-05
5.309576E-02
7.984865E-04
5.656179E+01
3.199238E+02
2.399311E-02
2.936670E-04
4.680753E-02
9.526509E-04
1.074699E-02
1.154978E-04
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
4.526425E-03
2.048853E-05
3.983003E-03
9.553596E-06
1.726789E-02
9.579848E-05
3.355605E-02
3.331438E-04
3.578422E-02
3.560242E-04
5.649209E+01
3.191358E+02
2.591495E-02
4.169753E-04
9.484744E-03
8.065715E-05
0.000000E+00
0.000000E+00
4.232587E-03
9.113263E-06
5.787378E-03
2.194940E-05
6.193626E-03
2.357423E-05
4.552787E-03
6.987360E-06
9.641308E-03
3.506339E-05
1.303892E-02
4.378408E-05
1.831744E-02
8.836683E-05
3.024725E+01
9.148969E+01
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
1.488204E-03
2.214752E-06
5.067487E-03
1.520987E-05
7.550927E-03
2.993991E-05
3.215559E-03
1.033982E-05
4.258525E-03
1.205064E-05
2.115487E-03
4.475286E-06
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00

View file

@ -0,0 +1,83 @@
"""
This test is designed to ensure that we account for potential corner crossings
in floating point precision.
"""
from math import pi, cos, sin
import openmc
import pytest
from tests.testing_harness import PyAPITestHarness
@pytest.fixture
def model():
model = openmc.Model()
# Length of lattice on each side
lat_size = 20.0
# Angle that we're crossing the corner at
phi = pi/4.0
air = openmc.Material()
air.set_density('g/cm3', 0.001)
air.add_nuclide('N14', 1.0)
metal = openmc.Material()
metal.set_density('g/cm3', 7.0)
metal.add_nuclide('Fe56', 1.0)
metal_cell = openmc.Cell(fill=metal)
metal_uni = openmc.Universe(cells=[metal_cell])
air_cell = openmc.Cell(fill=air)
air_uni = openmc.Universe(cells=[air_cell])
# Define a checkerboard lattice
lattice = openmc.RectLattice()
lattice.lower_left = (-lat_size/2.0, -lat_size/2.0)
lattice.pitch = (lat_size/2, lat_size/2)
lattice.universes = [
[metal_uni, air_uni],
[air_uni, metal_uni]
]
box = openmc.model.RectangularPrism(lat_size, lat_size)
cyl = openmc.ZCylinder(r=lat_size, boundary_type='vacuum')
outside_lattice = openmc.Cell(region=-cyl & +box, fill=air)
inside_lattice = openmc.Cell(region=-box, fill=lattice)
model.geometry = openmc.Geometry([outside_lattice, inside_lattice])
# Set all runtime parameters
model.settings.run_mode = 'fixed source'
model.settings.batches = 10
model.settings.particles = 1000
# Define a source located outside the lattice and pointing straight into its
# corner at 45 degrees
model.settings.source = openmc.IndependentSource(
space=openmc.stats.Point((-cos(phi), -sin(phi), 0.0)),
angle=openmc.stats.Monodirectional((cos(phi), sin(phi), 0.0))
)
# Create a mesh tally
mesh = openmc.RegularMesh()
mesh.dimension = (10, 10)
mesh.lower_left = (-lat_size, -lat_size)
mesh.upper_right = (lat_size, lat_size)
mesh_filter = openmc.MeshFilter(mesh)
tally = openmc.Tally(tally_id=1)
tally.filters = [mesh_filter]
tally.scores = ['flux']
tally.estimator = 'tracklength'
model.tallies = [tally]
return model
def test_lattice_corner_crossing(model):
harness = PyAPITestHarness('statepoint.10.h5', model)
harness.main()