mirror of
https://github.com/openmc-dev/openmc.git
synced 2026-07-21 06:25:30 -04:00
Some checks are pending
Tests and Coverage / filter-changes (push) Waiting to run
Tests and Coverage / Python 3.13 (omp=n, mpi=n, dagmc=, libmesh=, event= (push) Blocked by required conditions
Tests and Coverage / Python 3.14 (omp=n, mpi=n, dagmc=, libmesh=, event= (push) Blocked by required conditions
Tests and Coverage / Python 3.14t (omp=n, mpi=n, dagmc=, libmesh=, event= (push) Blocked by required conditions
Tests and Coverage / Python 3.12 (omp=n, mpi=n, dagmc=n, libmesh=n, event=n (push) Blocked by required conditions
Tests and Coverage / Python 3.12 (omp=y, mpi=n, dagmc=n, libmesh=n, event=n (push) Blocked by required conditions
Tests and Coverage / Python 3.12 (omp=n, mpi=y, dagmc=n, libmesh=n, event=n (push) Blocked by required conditions
Tests and Coverage / Python 3.12 (omp=y, mpi=y, dagmc=n, libmesh=n, event=n (push) Blocked by required conditions
Tests and Coverage / Python 3.12 (omp=y, mpi=n, dagmc=, libmesh=y, event= (push) Blocked by required conditions
Tests and Coverage / Python 3.12 (omp=y, mpi=n, dagmc=, libmesh=, event=y (push) Blocked by required conditions
Tests and Coverage / Python 3.12 (omp=y, mpi=y, dagmc=y, libmesh=, event= (push) Blocked by required conditions
Tests and Coverage / Python 3.12 (omp=y, mpi=y, dagmc=, libmesh=y, event= (push) Blocked by required conditions
Tests and Coverage / coverage (push) Blocked by required conditions
Tests and Coverage / Check CI status (push) Blocked by required conditions
dockerhub-publish-develop / main (push) Waiting to run
dockerhub-publish-develop-dagmc-libmesh / main (push) Waiting to run
dockerhub-publish-develop-dagmc / main (push) Waiting to run
dockerhub-publish-develop-libmesh / main (push) Waiting to run
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
661 lines
23 KiB
Python
661 lines
23 KiB
Python
from difflib import unified_diff
|
|
import filecmp
|
|
import glob
|
|
import h5py
|
|
import hashlib
|
|
import os
|
|
import shutil
|
|
|
|
import numpy as np
|
|
import openmc
|
|
from openmc.examples import pwr_core
|
|
from colorama import Fore, init
|
|
|
|
from tests.regression_tests import config
|
|
|
|
init()
|
|
|
|
|
|
def colorize(diff):
|
|
"""Produce colored diff for test results"""
|
|
for line in diff:
|
|
if line.startswith('+'):
|
|
yield Fore.RED + line + Fore.RESET
|
|
elif line.startswith('-'):
|
|
yield Fore.GREEN + line + Fore.RESET
|
|
elif line.startswith('^'):
|
|
yield Fore.BLUE + line + Fore.RESET
|
|
else:
|
|
yield line
|
|
|
|
|
|
class TestHarness:
|
|
"""General class for running OpenMC regression tests."""
|
|
|
|
def __init__(self, statepoint_name):
|
|
self._sp_name = statepoint_name
|
|
|
|
def main(self):
|
|
"""Accept commandline arguments and either run or update tests."""
|
|
if config['update']:
|
|
self.update_results()
|
|
else:
|
|
self.execute_test()
|
|
|
|
def execute_test(self):
|
|
"""Run OpenMC with the appropriate arguments and check the outputs."""
|
|
try:
|
|
self._run_openmc()
|
|
self._test_output_created()
|
|
results = self._get_results()
|
|
self._write_results(results)
|
|
self._compare_results()
|
|
finally:
|
|
self._cleanup()
|
|
|
|
def update_results(self):
|
|
"""Update the results_true using the current version of OpenMC."""
|
|
try:
|
|
self._run_openmc()
|
|
self._test_output_created()
|
|
results = self._get_results()
|
|
self._write_results(results)
|
|
self._overwrite_results()
|
|
finally:
|
|
self._cleanup()
|
|
|
|
def _run_openmc(self):
|
|
if config['mpi']:
|
|
mpi_args = [config['mpiexec'], '-n', config['mpi_np']]
|
|
openmc.run(openmc_exec=config['exe'], mpi_args=mpi_args,
|
|
event_based=config['event'])
|
|
else:
|
|
openmc.run(openmc_exec=config['exe'], event_based=config['event'])
|
|
|
|
def _test_output_created(self):
|
|
"""Make sure statepoint.* and tallies.out have been created."""
|
|
statepoint = glob.glob(self._sp_name)
|
|
assert len(statepoint) == 1, 'Either multiple or no statepoint files' \
|
|
' exist.'
|
|
assert statepoint[0].endswith('h5'), \
|
|
'Statepoint file is not a HDF5 file.'
|
|
if os.path.exists('tallies.xml'):
|
|
assert os.path.exists('tallies.out'), \
|
|
'Tally output file does not exist.'
|
|
|
|
def _get_results(self, hash_output=False):
|
|
"""Digest info in the statepoint and return as a string."""
|
|
# Read the statepoint file.
|
|
statepoint = glob.glob(self._sp_name)[0]
|
|
with openmc.StatePoint(statepoint) as sp:
|
|
outstr = ''
|
|
if sp.run_mode == 'eigenvalue':
|
|
# Write out k-combined.
|
|
outstr += 'k-combined:\n'
|
|
form = '{0:12.6E} {1:12.6E}\n'
|
|
outstr += form.format(sp.keff.n, sp.keff.s)
|
|
|
|
# Write out tally data.
|
|
for i, tally_ind in enumerate(sp.tallies):
|
|
tally = sp.tallies[tally_ind]
|
|
results = np.zeros((tally.sum.size * 2, ))
|
|
results[0::2] = tally.sum.ravel()
|
|
results[1::2] = tally.sum_sq.ravel()
|
|
results = ['{0:12.6E}'.format(x) for x in results]
|
|
|
|
outstr += 'tally {}:\n'.format(i + 1)
|
|
outstr += '\n'.join(results) + '\n'
|
|
|
|
# Hash the results if necessary.
|
|
if hash_output:
|
|
sha512 = hashlib.sha512()
|
|
sha512.update(outstr.encode('utf-8'))
|
|
outstr = sha512.hexdigest()
|
|
|
|
return outstr
|
|
|
|
@property
|
|
def statepoint_name(self):
|
|
return self._sp_name
|
|
|
|
def _write_results(self, results_string):
|
|
"""Write the results to an ASCII file."""
|
|
with open('results_test.dat', 'w') as fh:
|
|
fh.write(results_string)
|
|
|
|
def _overwrite_results(self):
|
|
"""Overwrite the results_true with the results_test."""
|
|
shutil.copyfile('results_test.dat', 'results_true.dat')
|
|
|
|
def _compare_results(self):
|
|
"""Make sure the current results agree with the reference."""
|
|
compare = filecmp.cmp('results_test.dat', 'results_true.dat')
|
|
if not compare:
|
|
expected = open('results_true.dat').readlines()
|
|
actual = open('results_test.dat').readlines()
|
|
diff = unified_diff(expected, actual, 'results_true.dat',
|
|
'results_test.dat')
|
|
print('Result differences:')
|
|
print(''.join(colorize(diff)))
|
|
os.rename('results_test.dat', 'results_error.dat')
|
|
assert compare, 'Results do not agree'
|
|
|
|
def _cleanup(self):
|
|
"""Delete statepoints, tally, and test files."""
|
|
output = glob.glob('statepoint.*.h5')
|
|
output += ['tallies.out', 'tallies.forward.out']
|
|
output += ['results_test.dat', 'summary.h5']
|
|
output += glob.glob('volume_*.h5')
|
|
for f in output:
|
|
if os.path.exists(f):
|
|
os.remove(f)
|
|
|
|
|
|
class HashedTestHarness(TestHarness):
|
|
"""Specialized TestHarness that hashes the results."""
|
|
|
|
def _get_results(self):
|
|
"""Digest info in the statepoint and return as a string."""
|
|
return super()._get_results(True)
|
|
|
|
|
|
class CMFDTestHarness(TestHarness):
|
|
"""Specialized TestHarness for running OpenMC CMFD tests."""
|
|
|
|
def __init__(self, statepoint_name, cmfd_run):
|
|
super().__init__(statepoint_name)
|
|
self._create_cmfd_result_str(cmfd_run)
|
|
|
|
def _create_cmfd_result_str(self, cmfd_run):
|
|
"""Create CMFD result string from variables of CMFDRun instance"""
|
|
outstr = 'cmfd indices\n'
|
|
outstr += '\n'.join(['{:.6E}'.format(x) for x in cmfd_run.indices])
|
|
outstr += '\nk cmfd\n'
|
|
outstr += '\n'.join(['{:.6E}'.format(x) for x in cmfd_run.k_cmfd])
|
|
outstr += '\ncmfd entropy\n'
|
|
outstr += '\n'.join(['{:.6E}'.format(x) for x in cmfd_run.entropy])
|
|
outstr += '\ncmfd balance\n'
|
|
outstr += '\n'.join(['{:.5E}'.format(x) for x in cmfd_run.balance])
|
|
outstr += '\ncmfd dominance ratio\n'
|
|
outstr += '\n'.join(['{:.3E}'.format(x) for x in cmfd_run.dom])
|
|
outstr += '\ncmfd openmc source comparison\n'
|
|
outstr += '\n'.join(['{:.6E}'.format(x) for x in cmfd_run.src_cmp])
|
|
outstr += '\ncmfd source\n'
|
|
cmfdsrc = np.reshape(cmfd_run.cmfd_src, np.prod(cmfd_run.indices),
|
|
order='F')
|
|
outstr += '\n'.join(['{:.6E}'.format(x) for x in cmfdsrc])
|
|
outstr += '\n'
|
|
self._cmfdrun_results = outstr
|
|
|
|
def execute_test(self):
|
|
"""Don't call _run_openmc as OpenMC will be called through C API for
|
|
CMFD tests, and write CMFD results that were passsed as argument
|
|
|
|
"""
|
|
try:
|
|
self._test_output_created()
|
|
results = self._get_results()
|
|
results += self._cmfdrun_results
|
|
self._write_results(results)
|
|
self._compare_results()
|
|
finally:
|
|
self._cleanup()
|
|
|
|
def update_results(self):
|
|
"""Don't call _run_openmc as OpenMC will be called through C API for
|
|
CMFD tests, and write CMFD results that were passsed as argument
|
|
|
|
"""
|
|
try:
|
|
self._test_output_created()
|
|
results = self._get_results()
|
|
results += self._cmfdrun_results
|
|
self._write_results(results)
|
|
self._overwrite_results()
|
|
finally:
|
|
self._cleanup()
|
|
|
|
def _cleanup(self):
|
|
"""Delete output files for numpy matrices and flux vectors."""
|
|
super()._cleanup()
|
|
output = ['loss.npz', 'loss.dat', 'prod.npz', 'prod.dat',
|
|
'fluxvec.npy', 'fluxvec.dat']
|
|
for f in output:
|
|
if os.path.exists(f):
|
|
os.remove(f)
|
|
|
|
|
|
class ParticleRestartTestHarness(TestHarness):
|
|
"""Specialized TestHarness for running OpenMC particle restart tests."""
|
|
|
|
def _run_openmc(self):
|
|
# Set arguments
|
|
args = {'openmc_exec': config['exe']}
|
|
if config['mpi']:
|
|
args['mpi_args'] = [config['mpiexec'], '-n', config['mpi_np']]
|
|
|
|
# Initial run
|
|
openmc.run(**args)
|
|
|
|
# Run particle restart
|
|
args.update({'restart_file': self._sp_name})
|
|
openmc.run(**args)
|
|
|
|
def _test_output_created(self):
|
|
"""Make sure the restart file has been created."""
|
|
particle = glob.glob(self._sp_name)
|
|
assert len(particle) == 1, 'Either multiple or no particle restart ' \
|
|
'files exist.'
|
|
assert particle[0].endswith('h5'), \
|
|
'Particle restart file is not a HDF5 file.'
|
|
|
|
def _get_results(self):
|
|
"""Digest info in the statepoint and return as a string."""
|
|
# Read the particle restart file.
|
|
particle = glob.glob(self._sp_name)[0]
|
|
p = openmc.Particle(particle)
|
|
|
|
# Write out the properties.
|
|
outstr = ''
|
|
outstr += 'current batch:\n'
|
|
outstr += "{0:12.6E}\n".format(p.current_batch)
|
|
outstr += 'current generation:\n'
|
|
outstr += "{0:12.6E}\n".format(p.current_generation)
|
|
outstr += 'particle id:\n'
|
|
outstr += "{0:12.6E}\n".format(p.id)
|
|
outstr += 'run mode:\n'
|
|
outstr += "{0}\n".format(p.run_mode)
|
|
outstr += 'particle weight:\n'
|
|
outstr += "{0:12.6E}\n".format(p.weight)
|
|
outstr += 'particle energy:\n'
|
|
outstr += "{0:12.6E}\n".format(p.energy)
|
|
outstr += 'particle xyz:\n'
|
|
outstr += "{0:12.6E} {1:12.6E} {2:12.6E}\n".format(p.xyz[0], p.xyz[1],
|
|
p.xyz[2])
|
|
outstr += 'particle uvw:\n'
|
|
outstr += "{0:12.6E} {1:12.6E} {2:12.6E}\n".format(p.uvw[0], p.uvw[1],
|
|
p.uvw[2])
|
|
|
|
return outstr
|
|
|
|
def _cleanup(self):
|
|
"""Delete particle restart files."""
|
|
super()._cleanup()
|
|
output = glob.glob('particle*.h5')
|
|
for f in output:
|
|
os.remove(f)
|
|
|
|
|
|
class PyAPITestHarness(TestHarness):
|
|
def __init__(self, statepoint_name, model=None, inputs_true=None):
|
|
super().__init__(statepoint_name)
|
|
if model is None:
|
|
self._model = pwr_core()
|
|
else:
|
|
self._model = model
|
|
self._model.plots = []
|
|
|
|
self.inputs_true = "inputs_true.dat" if not inputs_true else inputs_true
|
|
|
|
def main(self):
|
|
"""Accept commandline arguments and either run or update tests."""
|
|
if config['build_inputs']:
|
|
self._build_inputs()
|
|
elif config['update']:
|
|
self.update_results()
|
|
else:
|
|
self.execute_test()
|
|
|
|
def execute_test(self, change_dir=False):
|
|
"""Build input XMLs, run OpenMC, and verify correct results."""
|
|
base_dir = os.getcwd() if change_dir else None
|
|
try:
|
|
if change_dir:
|
|
os.chdir(self.workdir)
|
|
self._build_inputs()
|
|
inputs = self._get_inputs()
|
|
self._write_inputs(inputs)
|
|
self._compare_inputs()
|
|
self._run_openmc()
|
|
self._test_output_created()
|
|
results = self._get_results()
|
|
self._write_results(results)
|
|
self._compare_results()
|
|
finally:
|
|
self._cleanup()
|
|
if base_dir:
|
|
os.chdir(base_dir)
|
|
|
|
def update_results(self, change_dir=False):
|
|
"""Update results_true.dat and inputs_true.dat"""
|
|
base_dir = os.getcwd() if change_dir else None
|
|
try:
|
|
if change_dir:
|
|
os.chdir(self.workdir)
|
|
self._build_inputs()
|
|
inputs = self._get_inputs()
|
|
self._write_inputs(inputs)
|
|
self._overwrite_inputs()
|
|
self._run_openmc()
|
|
self._test_output_created()
|
|
results = self._get_results()
|
|
self._write_results(results)
|
|
self._overwrite_results()
|
|
finally:
|
|
self._cleanup()
|
|
if base_dir:
|
|
os.chdir(base_dir)
|
|
|
|
def _build_inputs(self):
|
|
"""Write input XML files."""
|
|
self._model.export_to_model_xml()
|
|
|
|
def _get_inputs(self):
|
|
"""Return a hash digest of the input XML files."""
|
|
xmls = ['model.xml', 'plots.xml']
|
|
return ''.join([open(fname).read() for fname in xmls
|
|
if os.path.exists(fname)])
|
|
|
|
def _write_inputs(self, input_digest):
|
|
"""Write the digest of the input XMLs to an ASCII file."""
|
|
with open('inputs_test.dat', 'w') as fh:
|
|
fh.write(input_digest)
|
|
|
|
def _overwrite_inputs(self):
|
|
"""Overwrite inputs_true.dat with inputs_test.dat"""
|
|
shutil.copyfile('inputs_test.dat', self.inputs_true)
|
|
|
|
def _compare_inputs(self):
|
|
"""Make sure the current inputs agree with the _true standard."""
|
|
compare = filecmp.cmp('inputs_test.dat', self.inputs_true)
|
|
if not compare:
|
|
expected = open(self.inputs_true, 'r').readlines()
|
|
actual = open('inputs_test.dat', 'r').readlines()
|
|
diff = unified_diff(expected, actual, self.inputs_true,
|
|
'inputs_test.dat')
|
|
print('Input differences:')
|
|
print(''.join(colorize(diff)))
|
|
os.rename('inputs_test.dat', 'inputs_error.dat')
|
|
assert compare, 'Input files are broken.'
|
|
|
|
def _cleanup(self):
|
|
"""Delete XMLs, statepoints, tally, and test files."""
|
|
super()._cleanup()
|
|
output = ['materials.xml', 'geometry.xml', 'settings.xml',
|
|
'tallies.xml', 'plots.xml', 'inputs_test.dat', 'model.xml',
|
|
'collision_track.h5', 'collision_track.mcpl']
|
|
for f in output:
|
|
if os.path.exists(f):
|
|
os.remove(f)
|
|
|
|
|
|
class HashedPyAPITestHarness(PyAPITestHarness):
|
|
def _get_results(self):
|
|
"""Digest info in the statepoint and return as a string."""
|
|
return super()._get_results(True)
|
|
|
|
|
|
class TolerantPyAPITestHarness(PyAPITestHarness):
|
|
"""Specialized harness for running tests that involve significant levels
|
|
of floating point non-associativity when using shared memory parallelism
|
|
due to single precision usage (e.g., as in the random ray solver).
|
|
|
|
"""
|
|
|
|
def _are_files_equal(self, actual_path, expected_path, tolerance):
|
|
def isfloat(value):
|
|
try:
|
|
float(value)
|
|
return True
|
|
except ValueError:
|
|
return False
|
|
|
|
def tokenize(line):
|
|
return line.strip().split()
|
|
|
|
def compare_tokens(token1, token2):
|
|
if isfloat(token1) and isfloat(token2):
|
|
float1, float2 = float(token1), float(token2)
|
|
return abs(float1 - float2) <= tolerance * max(abs(float1), abs(float2))
|
|
else:
|
|
return token1 == token2
|
|
|
|
expected = open(expected_path).readlines()
|
|
actual = open(actual_path).readlines()
|
|
|
|
if len(expected) != len(actual):
|
|
return False
|
|
|
|
for line1, line2 in zip(expected, actual):
|
|
tokens1 = tokenize(line1)
|
|
tokens2 = tokenize(line2)
|
|
|
|
if len(tokens1) != len(tokens2):
|
|
return False
|
|
|
|
for token1, token2 in zip(tokens1, tokens2):
|
|
if not compare_tokens(token1, token2):
|
|
return False
|
|
|
|
return True
|
|
|
|
def _compare_results(self):
|
|
"""Make sure the current results agree with the reference."""
|
|
compare = self._are_files_equal(
|
|
'results_test.dat', 'results_true.dat', 1e-6)
|
|
if not compare:
|
|
expected = open('results_true.dat').readlines()
|
|
actual = open('results_test.dat').readlines()
|
|
diff = unified_diff(expected, actual, 'results_true.dat',
|
|
'results_test.dat')
|
|
print('Result differences:')
|
|
print(''.join(colorize(diff)))
|
|
os.rename('results_test.dat', 'results_error.dat')
|
|
assert compare, 'Results do not agree'
|
|
|
|
|
|
class WeightWindowPyAPITestHarness(PyAPITestHarness):
|
|
def _get_results(self):
|
|
"""Digest info in the weight window file and return as a string."""
|
|
ww = openmc.WeightWindowsList.from_hdf5()[0]
|
|
|
|
# Access the weight window bounds
|
|
lower_bound = ww.lower_ww_bounds
|
|
upper_bound = ww.upper_ww_bounds
|
|
|
|
# Flatten both arrays
|
|
flattened_lower_bound = lower_bound.flatten()
|
|
flattened_upper_bound = upper_bound.flatten()
|
|
|
|
# Convert each element to a string in scientific notation with 2 decimal places
|
|
formatted_lower_bound = [f'{x:.2e}' for x in flattened_lower_bound]
|
|
formatted_upper_bound = [f'{x:.2e}' for x in flattened_upper_bound]
|
|
|
|
# Concatenate the formatted arrays
|
|
concatenated_strings = ["Lower Bounds"] + formatted_lower_bound + \
|
|
["Upper Bounds"] + formatted_upper_bound
|
|
|
|
# Join the concatenated strings into a single string with newline characters
|
|
final_string = '\n'.join(concatenated_strings)
|
|
|
|
# Prepend the mesh text description and return final string
|
|
return str(ww.mesh) + final_string
|
|
|
|
def _cleanup(self):
|
|
super()._cleanup()
|
|
f = 'weight_windows.h5'
|
|
if os.path.exists(f):
|
|
os.remove(f)
|
|
|
|
|
|
class PlotTestHarness(TestHarness):
|
|
"""Specialized TestHarness for running OpenMC plotting tests."""
|
|
|
|
def __init__(self, plot_names, voxel_convert_checks=[]):
|
|
super().__init__(None)
|
|
self._plot_names = plot_names
|
|
self._voxel_convert_checks = voxel_convert_checks
|
|
|
|
def _run_openmc(self):
|
|
openmc.plot_geometry(openmc_exec=config['exe'])
|
|
|
|
# Check that voxel h5 can be converted to vtk
|
|
for voxel_h5_filename in self._voxel_convert_checks:
|
|
openmc.voxel_to_vtk(voxel_h5_filename)
|
|
|
|
def _test_output_created(self):
|
|
"""Make sure *.png has been created."""
|
|
for fname in self._plot_names:
|
|
assert os.path.exists(fname), 'Plot output file does not exist.'
|
|
|
|
def _cleanup(self):
|
|
super()._cleanup()
|
|
for fname in self._plot_names:
|
|
if os.path.exists(fname):
|
|
os.remove(fname)
|
|
|
|
def _get_results(self):
|
|
"""Return a string hash of the plot files."""
|
|
outstr = bytes()
|
|
|
|
for fname in self._plot_names:
|
|
if fname.endswith('.png'):
|
|
# Add PNG output to results
|
|
with open(fname, 'rb') as fh:
|
|
outstr += fh.read()
|
|
elif fname.endswith('.h5'):
|
|
# Add voxel data to results
|
|
with h5py.File(fname, 'r') as fh:
|
|
outstr += fh.attrs['filetype']
|
|
outstr += fh.attrs['num_voxels'].tobytes()
|
|
outstr += fh.attrs['lower_left'].tobytes()
|
|
outstr += fh.attrs['voxel_width'].tobytes()
|
|
outstr += fh['data'][()].tobytes()
|
|
|
|
# Hash the information and return.
|
|
sha512 = hashlib.sha512()
|
|
sha512.update(outstr)
|
|
outstr = sha512.hexdigest()
|
|
|
|
return outstr
|
|
|
|
|
|
class CollisionTrackTestHarness(PyAPITestHarness):
|
|
def __init__(self, statepoint_name, model=None, inputs_true=None, workdir=None):
|
|
super().__init__(statepoint_name, model, inputs_true)
|
|
self.workdir = workdir
|
|
|
|
def _test_output_created(self):
|
|
"""Make sure collision_track.h5 has also been created."""
|
|
if self._model.settings.collision_track:
|
|
assert os.path.exists(
|
|
"collision_track.h5"
|
|
), "collision_track file has not been created."
|
|
|
|
def _compare_results(self):
|
|
"""Compare collision_track.h5 files."""
|
|
if self._model.settings.collision_track:
|
|
collision_track_true = self._return_collision_track_data(
|
|
"collision_track_true.h5")
|
|
collision_track_test = self._return_collision_track_data(
|
|
"collision_track.h5")
|
|
assert collision_track_true.shape == collision_track_test.shape
|
|
np.testing.assert_allclose(
|
|
collision_track_true, collision_track_test, rtol=1e-07)
|
|
|
|
def main(self):
|
|
"""Accept commandline arguments and either run or update tests."""
|
|
if config["build_inputs"]:
|
|
self.build_inputs()
|
|
elif config["update"]:
|
|
self.update_results(change_dir=True)
|
|
else:
|
|
self.execute_test(change_dir=True)
|
|
|
|
def build_inputs(self):
|
|
"""Build inputs."""
|
|
base_dir = os.getcwd()
|
|
try:
|
|
os.chdir(self.workdir)
|
|
self._build_inputs()
|
|
finally:
|
|
os.chdir(base_dir)
|
|
|
|
def _overwrite_results(self):
|
|
"""Also add the 'collision_track.h5' file during overwriting."""
|
|
if os.path.exists("collision_track.h5"):
|
|
shutil.copyfile("collision_track.h5", "collision_track_true.h5")
|
|
|
|
def _write_results(self, results_string):
|
|
# The result file for this test are written by the OpenMC executable itself
|
|
pass
|
|
|
|
@staticmethod
|
|
def _return_collision_track_data(filepath):
|
|
"""
|
|
Read a collision_track file and return a sorted array composed of
|
|
flattened collision records.
|
|
|
|
Parameters
|
|
----------
|
|
filepath : str
|
|
Path to the collision_track file
|
|
|
|
Returns
|
|
-------
|
|
data : np.array
|
|
Sorted array composed of flattened collision-track records.
|
|
"""
|
|
source = openmc.read_collision_track_file(filepath)
|
|
columns = [
|
|
source['r']['x'],
|
|
source['r']['y'],
|
|
source['r']['z'],
|
|
source['u']['x'],
|
|
source['u']['y'],
|
|
source['u']['z'],
|
|
source['E'],
|
|
source['dE'],
|
|
source['time'],
|
|
source['wgt'],
|
|
source['event_mt'],
|
|
source['delayed_group'],
|
|
source['cell_id'],
|
|
source['nuclide_id'],
|
|
source['material_id'],
|
|
source['universe_id'],
|
|
source['n_collision'],
|
|
source['particle'],
|
|
source['parent_id'],
|
|
source['progeny_id'],
|
|
]
|
|
data = np.column_stack(columns)
|
|
|
|
# Sort by the complete record, prioritizing stable integer identifiers
|
|
# before floating-point fields. This removes dependence on the order in
|
|
# which threads append otherwise reproducible collision records.
|
|
sort_columns = [
|
|
source['parent_id'],
|
|
source['progeny_id'],
|
|
source['n_collision'],
|
|
source['particle'],
|
|
source['cell_id'],
|
|
source['material_id'],
|
|
source['universe_id'],
|
|
source['nuclide_id'],
|
|
source['event_mt'],
|
|
source['delayed_group'],
|
|
source['r']['x'],
|
|
source['r']['y'],
|
|
source['r']['z'],
|
|
source['u']['x'],
|
|
source['u']['y'],
|
|
source['u']['z'],
|
|
source['E'],
|
|
source['dE'],
|
|
source['time'],
|
|
source['wgt'],
|
|
]
|
|
sorted_idx = np.lexsort(tuple(reversed(sort_columns)))
|
|
|
|
return data[sorted_idx]
|