diff --git a/include/openmc/settings.h b/include/openmc/settings.h index 66825867d..5b755784e 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -47,6 +47,7 @@ extern "C" bool run_CE; //!< run with continuous-energy data? extern bool source_latest; //!< write latest source at each batch? extern bool source_separate; //!< write source to separate file? extern bool source_write; //!< write source in HDF5 files? +extern bool source_mcpl_write; //!< write source in mcpl files? extern bool surf_source_write; //!< write surface source file? extern bool surf_mcpl_write; //!< write surface mcpl file? extern bool surf_source_read; //!< read surface source file? diff --git a/openmc/data/decay.py b/openmc/data/decay.py index d7ededf33..801ef4410 100644 --- a/openmc/data/decay.py +++ b/openmc/data/decay.py @@ -1,10 +1,9 @@ from collections.abc import Iterable from io import StringIO from math import log -from pathlib import Path import re +from typing import Optional from warnings import warn -from xml.etree import ElementTree as ET import numpy as np from uncertainties import ufloat, UFloat @@ -579,7 +578,7 @@ class Decay(EqualityMixin): _DECAY_PHOTON_ENERGY = {} -def decay_photon_energy(nuclide: str) -> Univariate: +def decay_photon_energy(nuclide: str) -> Optional[Univariate]: """Get photon energy distribution resulting from the decay of a nuclide This function relies on data stored in a depletion chain. Before calling it @@ -595,9 +594,10 @@ def decay_photon_energy(nuclide: str) -> Univariate: Returns ------- - openmc.stats.Univariate - Distribution of energies in [eV] of photons emitted from decay. Note - that the probabilities represent intensities, given as [decay/sec]. + openmc.stats.Univariate or None + Distribution of energies in [eV] of photons emitted from decay, or None + if no photon source exists. Note that the probabilities represent + intensities, given as [decay/sec]. """ if not _DECAY_PHOTON_ENERGY: chain_file = openmc.config.get('chain_file') diff --git a/openmc/material.py b/openmc/material.py index 16b5a3048..e99eec52c 100644 --- a/openmc/material.py +++ b/openmc/material.py @@ -92,10 +92,11 @@ class Material(IDManagerMixin): fissionable_mass : float Mass of fissionable nuclides in the material in [g]. Requires that the :attr:`volume` attribute is set. - decay_photon_energy : openmc.stats.Univariate + decay_photon_energy : openmc.stats.Univariate or None Energy distribution of photons emitted from decay of unstable nuclides - within the material. The integral of this distribution is the total - intensity of the photon source in [decay/sec]. + within the material, or None if no photon source exists. The integral of + this distribution is the total intensity of the photon source in + [decay/sec]. .. versionadded:: 0.14.0 @@ -264,7 +265,7 @@ class Material(IDManagerMixin): return density*self.volume @property - def decay_photon_energy(self) -> Univariate: + def decay_photon_energy(self) -> Optional[Univariate]: atoms = self.get_nuclide_atoms() dists = [] probs = [] @@ -273,7 +274,7 @@ class Material(IDManagerMixin): if source_per_atom is not None: dists.append(source_per_atom) probs.append(num_atoms) - return openmc.data.combine_distributions(dists, probs) + return openmc.data.combine_distributions(dists, probs) if dists else None @classmethod def from_hdf5(cls, group: h5py.Group): diff --git a/setup.py b/setup.py index 2a7e6a58d..a4b77e68b 100755 --- a/setup.py +++ b/setup.py @@ -5,11 +5,7 @@ import sys import numpy as np from setuptools import setup, find_packages -try: - from Cython.Build import cythonize - have_cython = True -except ImportError: - have_cython = False +from Cython.Build import cythonize # Determine shared library suffix @@ -76,13 +72,9 @@ kwargs = { 'test': ['pytest', 'pytest-cov', 'colorama'], 'vtk': ['vtk'], }, + # Cython is used to add resonance reconstruction and fast float_endf + 'ext_modules': cythonize('openmc/data/*.pyx'), + 'include_dirs': [np.get_include()] } -# If Cython is present, add resonance reconstruction and fast float_endf -if have_cython: - kwargs.update({ - 'ext_modules': cythonize('openmc/data/*.pyx'), - 'include_dirs': [np.get_include()] - }) - setup(**kwargs) diff --git a/src/settings.cpp b/src/settings.cpp index d1762596c..9098eb620 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -60,6 +60,7 @@ bool run_CE {true}; bool source_latest {false}; bool source_separate {false}; bool source_write {true}; +bool source_mcpl_write {true}; bool surf_source_write {false}; bool surf_mcpl_write {false}; bool surf_source_read {false}; @@ -653,6 +654,9 @@ void read_settings_xml() if (check_for_node(node_sp, "write")) { source_write = get_node_value_bool(node_sp, "write"); } + if (check_for_node(node_sp, "mcpl")) { + source_write = get_node_value_bool(node_sp, "mcpl"); + } if (check_for_node(node_sp, "overwrite_latest")) { source_latest = get_node_value_bool(node_sp, "overwrite_latest"); source_separate = source_latest; diff --git a/src/simulation.cpp b/src/simulation.cpp index d90c33ffd..d7cc2237d 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -389,16 +389,33 @@ void finalize_batch() if (settings::run_mode == RunMode::EIGENVALUE) { // Write out a separate source point if it's been specified for this batch - if (contains(settings::sourcepoint_batch, simulation::current_batch) && - settings::source_write && settings::source_separate) { - write_source_point(nullptr); - } +#ifdef OPENMC_MCPL + if(! settings::source_mcpl_write) { +#endif + if (contains(settings::sourcepoint_batch, simulation::current_batch) && + settings::source_write && settings::source_separate) { + write_source_point(nullptr); + } - // Write a continously-overwritten source point if requested. - if (settings::source_latest) { - auto filename = settings::path_output + "source.h5"; - write_source_point(filename.c_str()); + // Write a continously-overwritten source point if requested. + if (settings::source_latest) { + auto filename = settings::path_output + "source.h5"; + write_source_point(filename.c_str()); + } +#ifdef OPENMC_MCPL + } else { + if (contains(settings::sourcepoint_batch, simulation::current_batch) && + settings::source_mcpl_write && settings::source_separate) { + write_mcpl_source_point(nullptr); + } + + // Write a continously-overwritten source point if requested. + if (settings::source_latest && settings::source_mcpl_write) { + auto filename = settings::path_output + "source.mcpl"; + write_mcpl_source_point(filename.c_str()); + } } +#endif } // Write out surface source if requested. diff --git a/src/state_point.cpp b/src/state_point.cpp index 983147c62..d0fe92bdf 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -637,7 +637,7 @@ void write_mcpl_source_point(const char *filename, bool surf_source_bank) if (mpi::master) { //change this - this is h5 specific - mcpl_closeandgzip_outfile(file_id); + mcpl_close_outfile(file_id); } } diff --git a/tests/regression_tests/source_mcpl_file/results_true.dat b/tests/regression_tests/source_mcpl_file/results_true.dat index f2209006a..19460623f 100644 --- a/tests/regression_tests/source_mcpl_file/results_true.dat +++ b/tests/regression_tests/source_mcpl_file/results_true.dat @@ -1,2 +1,2 @@ k-combined: -3.017557E-01 3.398770E-03 +3.039964E-01 3.654869E-04 diff --git a/tests/regression_tests/source_mcpl_file/settings.xml b/tests/regression_tests/source_mcpl_file/settings.xml index 8b1510e0c..47b010bff 100644 --- a/tests/regression_tests/source_mcpl_file/settings.xml +++ b/tests/regression_tests/source_mcpl_file/settings.xml @@ -1,11 +1,15 @@ + + 10 5 1000 - source.10.mcpl + + -4 -4 -4 4 4 4 + diff --git a/tests/regression_tests/source_mcpl_file/test.py b/tests/regression_tests/source_mcpl_file/test.py index f55a76904..a005fc2d8 100644 --- a/tests/regression_tests/source_mcpl_file/test.py +++ b/tests/regression_tests/source_mcpl_file/test.py @@ -1,65 +1,101 @@ -import os -import subprocess +#!/usr/bin/env python + import glob +import os -from tests.testing_harness import TestHarness +from tests.testing_harness import * -class SourceMCPLFileTestHarness(TestHarness): - def execute_test(self): - """Run OpenMC with the appropriate arguments and check the outputs.""" - try: - self._create_input() - self._test_input_created() - self._run_openmc() - self._test_output_created() - results = self._get_results() - self._write_results(results) - self._compare_results() - finally: - self._cleanup() +settings1=""" + + + + + 10 + 5 + 1000 + + + + -4 -4 -4 4 4 4 + + + +""" - def _create_input(self): - compiled=subprocess.run(['gcc','-o','gen_dummy_mcpl.out','gen_dummy_mcpl.c','-lm','-lmcpl']) - assert compiled==0, 'Could not compile mcpl-file generator code' - subprocess.run(['./gen_dummy_mcpl.out']) +settings2 = """ + + + 10 + 5 + 1000 + + + source.10.{0} + + +""" - def update_results(self): - """Update the results_true using the current version of OpenMC.""" - try: - self._create_input() - self._test_input_created() - self._run_openmc() - self._test_output_created() - results = self._get_results() - self._write_results(results) - self._overwrite_results() - finally: - self._cleanup() - def _test_input_created(self): - """Check that the input mcpl.file was generated as it should""" - mcplfile=glob.glob(os.path.join(os.getcwd(),'source.10.mcpl')) - assert len(mcplfile) == 1, 'Either multiple or no mcpl files ' \ - 'exist.' - assert mcplfile[0].endswith('mcpl'), \ - 'output file does not end with mcpl.' +class SourceFileTestHarness(TestHarness): + def execute_test(self): + """Run OpenMC with the appropriate arguments and check the outputs.""" + try: + self._run_openmc() + self._test_output_created() + self._run_openmc_restart() + results = self._get_results() + self._write_results(results) + self._compare_results() + finally: + self._cleanup() - def _test_output_created(self): - """Check that the output files were created""" - statepoint = glob.glob(os.path.join(os.getcwd(), self._sp_name)) - assert len(statepoint) == 1, 'Either multiple or no statepoint files ' \ - 'exist.' - assert statepoint[0].endswith('h5'), \ - 'statepoint file does not end with h5.' + def update_results(self): + """Update the results_true using the current version of OpenMC.""" + try: + self._run_openmc() + self._test_output_created() + self._run_openmc_restart() + results = self._get_results() + self._write_results(results) + self._overwrite_results() + finally: + self._cleanup() - def _cleanup(self): - super()._cleanup() - source_mcpl=glob.glob(os.path.join(os.getcwd(),'source*.mcpl')) - for f in source_mcpl: - if (os.path.exists(f)): - os.remove(f) + def _test_output_created(self): + """Make sure statepoint and source files have been created.""" + statepoint = glob.glob(os.path.join(os.getcwd(), 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.' -def test_mcpl_source_file(): - harness = SourceMCPLFileTestHarness('source.10.mcpl') - harness.main() + source = glob.glob(os.path.join(os.getcwd(), 'source.10.mcpl*')) + assert len(source) == 1, 'Either multiple or no source files exist.' + assert source[0].endswith('mcpl') or source[0].endswith('mcpl.gz'), \ + 'Source file is not a MCPL file.' + + def _run_openmc_restart(self): + # Get the name of the source file. + source = glob.glob(os.path.join(os.getcwd(), 'source.10.*')) + + # Write the new settings.xml file. + with open('settings.xml','w') as fh: + fh.write(settings2.format(source[0].split('.')[-1])) + + # Run OpenMC. + self._run_openmc() + + def _cleanup(self): + TestHarness._cleanup(self) + output = glob.glob(os.path.join(os.getcwd(), 'source.*')) + #for f in output: + # if os.path.exists(f): + # os.remove(f) + with open('settings.xml','w') as fh: + fh.write(settings1) + + +def test_source_file(): + harness = SourceFileTestHarness('statepoint.10.h5') + harness.main() diff --git a/tests/unit_tests/test_material.py b/tests/unit_tests/test_material.py index 58df246dd..80935d68d 100644 --- a/tests/unit_tests/test_material.py +++ b/tests/unit_tests/test_material.py @@ -573,3 +573,9 @@ def test_decay_photon_energy(): assert src.integral() == pytest.approx(sum( intensity(decay_photon_energy(nuc)) for nuc in m.get_nuclides() )) + + # A material with no unstable nuclides should have no decay photon source + stable = openmc.Material() + stable.add_nuclide('Gd156', 1.0) + stable.volume = 1.0 + assert stable.decay_photon_energy is None