OpenMC/tests/regression_tests/collision_track/test.py
Patrick Shriwise ea6ba328c9
Fix collision track feature for photon transport (#3946)
Co-authored-by: Paul Romano <paul.k.romano@gmail.com>
2026-06-08 23:37:34 +00:00

222 lines
7.7 KiB
Python

"""Test the 'collision_track' setting.
Results
-------
All results are generated using only 1 MPI process.
All results are generated using 1 thread except for "test_consistency_low_realization_number".
This specific test verifies that when the number of realization (i.e., point being candidate
to be stored) is lower than the capacity, results are reproducible even with multiple
threads (i.e., there is no potential thread competition that would produce different
results in that case).
All results are generated using the history-based mode except for cases e01 to e03.
All results are visually verified using the '_visualize.py' script in the regression test folder.
OpenMC models
-------------
Four OpenMC models with CSG-only geometries are used to cover the transmission, vacuum,
reflective and periodic Boundary Conditions (BC):
- model_1: cylindrical core in 2 boxes (vacuum and transmission BC),
# Test cases for simulation parameters using CSG-only geometries
# ============================================================
# Each test case is defined by a combination of folder name, model name, and specific parameters.
# Below is a summary of the parameters used in the test cases:
#
# - max_collisions: Maximum number of particles to track in the simulation.
# - reactions: List of MT numbers (reaction types- 2 for scattering, 18 for fission, 101 for absorbtion).
# - cell_ids: IDs of specific cells in the model.
# - mat_ids: Material IDs for filtering particles.
# - nuclides: Nuclides for filtering particles.
# - univ_ids: Universe IDs for filtering particles.
# - E_threshold: Energy threshold for filtering particles (optional).
#
# The test cases are designed to validate the behavior of the simulation under various configurations.
*: BC stands for Boundary Conditions, T for Transmission, R for Reflective, and V for Vacuum.
An additional case, called 'case-a01', is used to check that the results are comparable when
the number of threads is set to 2 if the number of realization is lower than the capacity.
*: BC stands for Boundary Conditions, T for Transmission, and V for Vacuum.
Notes:
- The test cases list is non-exhaustive compared to the number of possible combinations.
Test cases have been selected based on use and internal code logic.
TODO:
- Test with a lattice.
"""
import openmc
import pytest
from tests.testing_harness import CollisionTrackTestHarness
from tests.regression_tests import config
@pytest.fixture(scope="module")
def model_1():
"""Cylindrical core contained in a first box which is contained in a larger box.
A lower universe is used to describe the interior of the first box which
contains the core and its surrounding space.
"""
openmc.reset_auto_ids()
model = openmc.Model()
# =============================================================================
# Materials
# =============================================================================
fuel = openmc.Material(material_id=1)
fuel.add_nuclide("U234", 0.0004524)
fuel.add_nuclide("U235", 0.0506068)
fuel.add_nuclide("U238", 0.9487090)
fuel.add_nuclide("U236", 0.0002318)
fuel.add_nuclide("O16", 2.0)
fuel.set_density("g/cm3", 11.0)
water = openmc.Material(material_id=11)
water.add_nuclide("H1", 2.0)
water.add_nuclide("O16", 1.0)
water.set_density("g/cm3", 1.0)
# =============================================================================
# Geometry
# =============================================================================
# -----------------------------------------------------------------------------
# Cylindrical core
# -----------------------------------------------------------------------------
# Parameters
core_radius = 2.0
core_height = 4.0
# Surfaces
core_cylinder = openmc.ZCylinder(r=core_radius)
core_lower_plane = openmc.ZPlane(-core_height / 2.0)
core_upper_plane = openmc.ZPlane(core_height / 2.0)
# Region
core_region = -core_cylinder & +core_lower_plane & -core_upper_plane
# Cells
core = openmc.Cell(fill=fuel, region=core_region, cell_id=22)
outside_core_region = +core_cylinder | -core_lower_plane | +core_upper_plane
outside_core = openmc.Cell(
fill=water, region=outside_core_region, cell_id=33)
# Universe
inside_box1_universe = openmc.Universe(
cells=[core, outside_core], universe_id=77)
# -----------------------------------------------------------------------------
# Box 1
# -----------------------------------------------------------------------------
# Parameters
box1_size = 6.0
# Surfaces
box1_rpp = openmc.model.RectangularParallelepiped(
-box1_size / 2.0, box1_size / 2.0,
-box1_size / 2.0, box1_size / 2.0,
-box1_size / 2.0, box1_size / 2.0,
)
# Cell
box1 = openmc.Cell(fill=inside_box1_universe, region=-box1_rpp, cell_id=5)
# -----------------------------------------------------------------------------
# Box 2
# -----------------------------------------------------------------------------
# Parameters
box2_size = 8
# Surfaces
box2_rpp = openmc.model.RectangularParallelepiped(
-box2_size / 2.0, box2_size / 2.0,
-box2_size / 2.0, box2_size / 2.0,
-box2_size / 2.0, box2_size / 2.0,
boundary_type="vacuum"
)
# Cell
box2 = openmc.Cell(fill=water, region=-box2_rpp & +box1_rpp, cell_id=8)
# Register geometry
model.geometry = openmc.Geometry([box1, box2])
# =============================================================================
# Settings
# =============================================================================
model.settings = openmc.Settings()
model.settings.particles = 80
model.settings.batches = 5
model.settings.inactive = 4
model.settings.seed = 1
bounds = [
-core_radius,
-core_radius,
-core_height / 2.0,
core_radius,
core_radius,
core_height / 2.0,
]
distribution = openmc.stats.Box(bounds[:3], bounds[3:])
model.settings.source = openmc.IndependentSource(
space=distribution, constraints={'fissionable': True})
return model
@pytest.mark.parametrize(
"folder, model_name, parameter",
[("case_1_Reactions", "model_1", {"max_collisions": 100, "reactions": ["(n,fission)", 101]}),
("case_2_Cell_ID", "model_1", {
"max_collisions": 100, "cell_ids": [22]}),
("case_3_Material_ID", "model_1", {
"max_collisions": 100, "material_ids": [1]}),
("case_4_Nuclide_ID", "model_1", {
"max_collisions": 100, "nuclides": ["O16", "U235"]}),
("case_5_Universe_ID", "model_1", {
"max_collisions": 100, "cell_ids": [22], "universe_ids": [77]}),
("case_6_deposited_energy_threshold", "model_1", {
"max_collisions": 100, "deposited_E_threshold": 5.5e5}),
("case_7_all_parameters_used_together", "model_1", {
"max_collisions": 100,
"reactions": ["elastic", 18, "(n,disappear)"],
"material_ids": [1, 11],
"universe_ids": [77],
"nuclides": ["U238", "U235", "H1", "U234"],
"cell_ids": [22, 33],
"deposited_E_threshold": 1e5})
],
)
def test_collision_track_several_cases(
folder, model_name, parameter, request
):
# Since for these tests the actual number of collisions recorded is < max_collisions,
# we can run them with 1 or 2 threads, and in history or event mode.
model = request.getfixturevalue(model_name)
model.settings.collision_track = parameter
harness = CollisionTrackTestHarness(
"statepoint.5.h5", model=model, workdir=folder
)
harness.main()