OpenMC/tests/unit_tests/test_surface_source_write.py
Zoe Prieto 9686851e7a
Write surface source files per batch (#3124)
Co-authored-by: Patrick Shriwise <pshriwise@gmail.com>
Co-authored-by: Paul Romano <paul.k.romano@gmail.com>
2024-10-03 22:32:03 +00:00

301 lines
9.8 KiB
Python

"""Test the 'surf_source_write' setting used to store particles that cross
surfaces in a file for a given simulation."""
from pathlib import Path
import openmc
import openmc.lib
import pytest
import h5py
import numpy as np
@pytest.fixture(scope="module")
def geometry():
"""Simple hydrogen sphere geometry"""
openmc.reset_auto_ids()
material = openmc.Material(name="H1")
material.add_element("H", 1.0)
sphere = openmc.Sphere(r=1.0, boundary_type="vacuum")
cell = openmc.Cell(region=-sphere, fill=material)
return openmc.Geometry([cell])
@pytest.mark.parametrize(
"parameter",
[
{"max_particles": 200},
{"max_particles": 200, "cell": 1},
{"max_particles": 200, "cellto": 1},
{"max_particles": 200, "cellfrom": 1},
{"max_particles": 200, "surface_ids": [2]},
{"max_particles": 200, "surface_ids": [2], "cell": 1},
{"max_particles": 200, "surface_ids": [2], "cellto": 1},
{"max_particles": 200, "surface_ids": [2], "cellfrom": 1},
{"max_particles": 200, "surface_ids": [2], "max_source_files": 1},
],
)
def test_xml_serialization(parameter, run_in_tmpdir):
"""Check that the different use cases can be written and read in XML."""
settings = openmc.Settings()
settings.surf_source_write = parameter
settings.export_to_xml()
read_settings = openmc.Settings.from_xml()
assert read_settings.surf_source_write == parameter
@pytest.fixture(scope="module")
def model():
"""Simple hydrogen sphere geometry"""
openmc.reset_auto_ids()
model = openmc.Model()
# Material
h1 = openmc.Material(name="H1")
h1.add_nuclide("H1", 1.0)
h1.set_density('g/cm3', 1e-7)
# Geometry
radius = 1.0
sphere = openmc.Sphere(r=radius, boundary_type="vacuum")
cell = openmc.Cell(region=-sphere, fill=h1)
model.geometry = openmc.Geometry([cell])
# Settings
model.settings = openmc.Settings()
model.settings.run_mode = "fixed source"
model.settings.particles = 100
model.settings.batches = 3
model.settings.seed = 1
distribution = openmc.stats.Point()
model.settings.source = openmc.IndependentSource(space=distribution)
return model
@pytest.mark.parametrize(
"max_particles, max_source_files",
[
(100, 2),
(100, 3),
(100, 1),
],
)
def test_number_surface_source_file_created(max_particles, max_source_files,
run_in_tmpdir, model):
"""Check the number of surface source files written."""
model.settings.surf_source_write = {
"max_particles": max_particles,
"max_source_files": max_source_files
}
model.run()
should_be_numbered = max_source_files > 1
for i in range(1, max_source_files + 1):
if should_be_numbered:
assert Path(f"surface_source.{i}.h5").exists()
if not should_be_numbered:
assert Path("surface_source.h5").exists()
ERROR_MSG_1 = (
"A maximum number of particles needs to be specified "
"using the 'max_particles' parameter to store surface "
"source points."
)
ERROR_MSG_2 = "'cell', 'cellfrom' and 'cellto' cannot be used at the same time."
@pytest.mark.parametrize(
"parameter, error",
[
({"cell": 1}, ERROR_MSG_1),
({"max_particles": 200, "cell": 1, "cellto": 1}, ERROR_MSG_2),
({"max_particles": 200, "cell": 1, "cellfrom": 1}, ERROR_MSG_2),
({"max_particles": 200, "cellto": 1, "cellfrom": 1}, ERROR_MSG_2),
({"max_particles": 200, "cell": 1, "cellto": 1, "cellfrom": 1}, ERROR_MSG_2),
],
)
def test_exceptions(parameter, error, run_in_tmpdir, geometry):
"""Test parameters configuration that should return an error."""
settings = openmc.Settings(run_mode="fixed source", batches=5, particles=100)
settings.surf_source_write = parameter
model = openmc.Model(geometry=geometry, settings=settings)
with pytest.raises(RuntimeError, match=error):
model.run()
@pytest.fixture(scope="module")
def model():
"""Simple hydrogen sphere divided in two hemispheres
by a z-plane to form 2 cells."""
openmc.reset_auto_ids()
model = openmc.Model()
# Material
material = openmc.Material(name="H1")
material.add_element("H", 1.0)
# Geometry
radius = 1.0
sphere = openmc.Sphere(r=radius, boundary_type="reflective")
plane = openmc.ZPlane(0.0)
cell_1 = openmc.Cell(region=-sphere & -plane, fill=material)
cell_2 = openmc.Cell(region=-sphere & +plane, fill=material)
root = openmc.Universe(cells=[cell_1, cell_2])
model.geometry = openmc.Geometry(root)
# Settings
model.settings = openmc.Settings()
model.settings.run_mode = "fixed source"
model.settings.particles = 100
model.settings.batches = 3
model.settings.seed = 1
bounds = [-radius, -radius, -radius, radius, radius, radius]
distribution = openmc.stats.Box(bounds[:3], bounds[3:])
model.settings.source = openmc.IndependentSource(space=distribution)
return model
@pytest.mark.parametrize(
"parameter",
[
{"max_particles": 200, "cellto": 2, "surface_ids": [2]},
{"max_particles": 200, "cellfrom": 2, "surface_ids": [2]},
],
)
def test_particle_direction(parameter, run_in_tmpdir, model):
"""Test the direction of particles with the 'cellfrom' and 'cellto' parameters
on a simple model with only one surface of interest.
Cell 2 is the upper hemisphere and surface 2 is the plane dividing the sphere
into two hemispheres.
"""
model.settings.surf_source_write = parameter
model.run()
with h5py.File("surface_source.h5", "r") as f:
source = f["source_bank"]
assert len(source) == 200
# We want to verify that the dot product of the surface's normal vector
# and the direction of the particle is either positive or negative
# depending on cellfrom or cellto. In this case, it is equivalent
# to just compare the z component of the direction of the particle.
for point in source:
if "cellto" in parameter.keys():
assert point["u"]["z"] > 0.0
elif "cellfrom" in parameter.keys():
assert point["u"]["z"] < 0.0
else:
assert False
@pytest.fixture
def model_dagmc(request):
"""Model based on the mesh file 'dagmc.h5m' available from
tests/regression_tests/dagmc/legacy.
"""
openmc.reset_auto_ids()
model = openmc.Model()
# =============================================================================
# Materials
# =============================================================================
u235 = openmc.Material(name="no-void fuel")
u235.add_nuclide("U235", 1.0, "ao")
u235.set_density("g/cc", 11)
u235.id = 40
water = openmc.Material(name="water")
water.add_nuclide("H1", 2.0, "ao")
water.add_nuclide("O16", 1.0, "ao")
water.set_density("g/cc", 1.0)
water.add_s_alpha_beta("c_H_in_H2O")
water.id = 41
model.materials = openmc.Materials([u235, water])
# =============================================================================
# Geometry
# =============================================================================
dagmc_path = Path(request.fspath).parent / "../regression_tests/dagmc/legacy/dagmc.h5m"
dagmc_univ = openmc.DAGMCUniverse(dagmc_path)
model.geometry = openmc.Geometry(dagmc_univ)
# =============================================================================
# Settings
# =============================================================================
model.settings = openmc.Settings()
model.settings.particles = 300
model.settings.batches = 5
model.settings.inactive = 1
model.settings.seed = 1
source_box = openmc.stats.Box([-4, -4, -20], [4, 4, 20])
model.settings.source = openmc.IndependentSource(space=source_box)
return model
@pytest.mark.skipif(
not openmc.lib._dagmc_enabled(), reason="DAGMC CAD geometry is not enabled."
)
@pytest.mark.parametrize(
"parameter",
[
{"max_particles": 200, "cellto": 1},
{"max_particles": 200, "cellfrom": 1},
],
)
def test_particle_direction_dagmc(parameter, run_in_tmpdir, model_dagmc):
"""Test the direction of particles with the 'cellfrom' and 'cellto' parameters
on a DAGMC model."""
model_dagmc.settings.surf_source_write = parameter
model_dagmc.run()
r = 7.0
h = 20.0
with h5py.File("surface_source.h5", "r") as f:
source = f["source_bank"]
assert len(source) == 200
for point in source:
x, y, z = point["r"]
ux, uy, uz = point["u"]
# If the point is on the upper or lower circle
if np.allclose(abs(z), h):
# If the point is also on the cylindrical surface
if np.allclose(np.sqrt(x**2 + y**2), r):
if "cellfrom" in parameter.keys():
assert (uz * z > 0) or (ux * x + uy * y > 0)
elif "cellto" in parameter.keys():
assert (uz * z < 0) or (ux * x + uy * y < 0)
else:
assert False
# If the point is not on the cylindrical surface
else:
if "cellfrom" in parameter.keys():
assert uz * z > 0
elif "cellto" in parameter.keys():
assert uz * z < 0
else:
assert False
# If the point is not on the upper or lower circle,
# meaning it is on the cylindrical surface
else:
if "cellfrom" in parameter.keys():
assert ux * x + uy * y > 0
elif "cellto" in parameter.keys():
assert ux * x + uy * y < 0
else:
assert False