From 481361ae799ae45aae63730530f85f58153db12a Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 8 Jul 2019 14:49:06 -0500 Subject: [PATCH 1/4] Use strings in representation of particle filter --- openmc/filter.py | 34 ++++++++++++------- src/tallies/filter_particle.cpp | 29 +++++++++++++--- .../photon_production/inputs_true.dat | 2 +- .../photon_source/inputs_true.dat | 2 +- 4 files changed, 49 insertions(+), 18 deletions(-) diff --git a/openmc/filter.py b/openmc/filter.py index dbdaf2e19e..52ff8638ef 100644 --- a/openmc/filter.py +++ b/openmc/filter.py @@ -32,7 +32,8 @@ _CURRENT_NAMES = ( 'z-min out', 'z-min in', 'z-max out', 'z-max in' ) -_PARTICLE_IDS = {'neutron': 1, 'photon': 2, 'electron': 3, 'positron': 4} +_PARTICLES = {'neutron', 'photon', 'electron', 'positron'} + class FilterMeta(ABCMeta): def __new__(cls, name, bases, namespace, **kwargs): @@ -547,10 +548,9 @@ class ParticleFilter(Filter): Parameters ---------- - bins : str, int, or iterable of Integral - The Particles to tally. Either str with particle type or their - ID numbers can be used ('neutron' = 1, 'photon' = 2, 'electron' = 3, - 'positron' = 4). + bins : str, or iterable of str + The particles to tally represented as strings ('neutron', 'photon', + 'electron', 'positron'). filter_id : int Unique identifier for the filter @@ -571,16 +571,26 @@ class ParticleFilter(Filter): @bins.setter def bins(self, bins): bins = np.atleast_1d(bins) - cv.check_iterable_type('filter bins', bins, (Integral, str)) + cv.check_iterable_type('filter bins', bins, str) for edge in bins: - if isinstance(edge, Integral): - cv.check_value('filter bin', edge, _PARTICLE_IDS.values()) - else: - cv.check_value('filter bin', edge, _PARTICLE_IDS.keys()) - bins = np.atleast_1d([b if isinstance(b, Integral) else _PARTICLE_IDS[b] - for b in bins]) + cv.check_value('filter bin', edge, _PARTICLES) self._bins = bins + @classmethod + def from_hdf5(cls, group, **kwargs): + if group['type'][()].decode() != cls.short_name.lower(): + raise ValueError("Expected HDF5 data for filter type '" + + cls.short_name.lower() + "' but got '" + + group['type'][()].decode() + " instead") + + if 'meshes' not in kwargs: + raise ValueError(cls.__name__ + " requires a 'meshes' keyword " + "argument.") + + particles = [b.decode() for b in group['bins'][()]] + filter_id = int(group.name.split('/')[-1].lstrip('filter ')) + return cls(particles, filter_id=filter_id) + class MeshFilter(Filter): """Bins tally event locations onto a regular, rectangular mesh. diff --git a/src/tallies/filter_particle.cpp b/src/tallies/filter_particle.cpp index de142489ff..eb419fec9a 100644 --- a/src/tallies/filter_particle.cpp +++ b/src/tallies/filter_particle.cpp @@ -7,12 +7,20 @@ namespace openmc { void ParticleFilter::from_xml(pugi::xml_node node) { - auto particles = get_node_array(node, "bins"); + auto particles = get_node_array(node, "bins"); // Convert to vector of Particle::Type std::vector types; for (auto& p : particles) { - types.push_back(static_cast(p - 1)); + if (p == "neutron") { + types.push_back(Particle::Type::neutron); + } else if (p == "photon") { + types.push_back(Particle::Type::photon); + } else if (p == "electron") { + types.push_back(Particle::Type::electron); + } else if (p == "positron") { + types.push_back(Particle::Type::positron); + } } this->set_particles(types); } @@ -47,9 +55,22 @@ void ParticleFilter::to_statepoint(hid_t filter_group) const { Filter::to_statepoint(filter_group); - std::vector particles; + std::vector particles; for (auto p : particles_) { - particles.push_back(static_cast(p) + 1); + switch (p) { + case Particle::Type::neutron: + particles.push_back("neutron"); + break; + case Particle::Type::photon: + particles.push_back("photon"); + break; + case Particle::Type::electron: + particles.push_back("electron"); + break; + case Particle::Type::positron: + particles.push_back("positron"); + break; + } } write_dataset(filter_group, "bins", particles); } diff --git a/tests/regression_tests/photon_production/inputs_true.dat b/tests/regression_tests/photon_production/inputs_true.dat index f82b4a6a78..1821bea92f 100644 --- a/tests/regression_tests/photon_production/inputs_true.dat +++ b/tests/regression_tests/photon_production/inputs_true.dat @@ -41,7 +41,7 @@ 9 - 2 + photon 1 2 diff --git a/tests/regression_tests/photon_source/inputs_true.dat b/tests/regression_tests/photon_source/inputs_true.dat index 425738a479..89f4de0e0c 100644 --- a/tests/regression_tests/photon_source/inputs_true.dat +++ b/tests/regression_tests/photon_source/inputs_true.dat @@ -36,7 +36,7 @@ - 2 + photon 1 From 26166553259ae846ebaf92b97769111e149d6f50 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 18 Jul 2019 16:24:37 -0500 Subject: [PATCH 2/4] Fix use of ENDF_FLOAT_RE in ace.py --- openmc/data/ace.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmc/data/ace.py b/openmc/data/ace.py index b93de0d24c..ee194560b4 100644 --- a/openmc/data/ace.py +++ b/openmc/data/ace.py @@ -411,7 +411,7 @@ class Library(EqualityMixin): # after it). If it's too short, then we apply the ENDF float regular # expression. We don't do this by default because it's expensive! if xss.size != nxs[1] + 1: - datastr = ENDF_FLOAT_RE.sub(r'\1e\2', datastr) + datastr = ENDF_FLOAT_RE.sub(r'\1e\2\3', datastr) xss = np.fromstring(datastr, sep=' ') assert xss.size == nxs[1] + 1 From 09be1f9293c3517212fa2c31e1d7740b7a0a8467 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 19 Jul 2019 07:38:24 -0500 Subject: [PATCH 3/4] Update devguide test suite section --- docs/source/devguide/tests.rst | 56 ++++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 20 deletions(-) diff --git a/docs/source/devguide/tests.rst b/docs/source/devguide/tests.rst index 475d0f69b3..b26f5c762f 100644 --- a/docs/source/devguide/tests.rst +++ b/docs/source/devguide/tests.rst @@ -4,9 +4,6 @@ Test Suite ========== -Running Tests -------------- - The OpenMC test suite consists of two parts, a regression test suite and a unit test suite. The regression test suite is based on regression or integrated testing where different types of input files are configured and the full OpenMC @@ -14,27 +11,35 @@ code is executed. Results from simulations are compared with expected results. The unit tests are primarily intended to test individual functions/classes in the OpenMC Python API. -The test suite relies on the third-party `pytest `_ -package. To run either or both the regression and unit test suites, it is -assumed that you have OpenMC fully installed, i.e., the :ref:`scripts_openmc` -executable is available on your :envvar:`PATH` and the :mod:`openmc` Python -module is importable. In development where it would be onerous to continually -install OpenMC every time a small change is made, it is recommended to install -OpenMC in development/editable mode. With setuptools, this is accomplished by -running:: +Prerequisites +------------- - python setup.py develop +- The test suite relies on the third-party `pytest `_ + package. To run either or both the regression and unit test suites, it is + assumed that you have OpenMC fully installed, i.e., the :ref:`scripts_openmc` + executable is available on your :envvar:`PATH` and the :mod:`openmc` Python + module is importable. In development where it would be onerous to continually + install OpenMC every time a small change is made, it is recommended to install + OpenMC in development/editable mode. With setuptools, this is accomplished by + running:: -or using pip (recommended):: + python setup.py develop - pip install -e .[test] + or using pip (recommended):: -It is also assumed that you have cross section data available that is pointed to -by the :envvar:`OPENMC_CROSS_SECTIONS` environment variables. Furthermore, to -run unit tests for the :mod:`openmc.data` module, it is necessary to have -ENDF/B-VII.1 data available and pointed to by the :envvar:`OPENMC_ENDF_DATA` -environment variable. All data sources can be obtained using the -``tools/ci/travis-before-script.sh`` script. + pip install -e .[test] + +- The test suite requires a specific set of cross section data in order for + tests to pass. A download URL for the data that OpenMC expects can be found + within ``tools/ci/download-xs.sh``. +- In addition to the HDF5 data, some tests rely on ENDF files. A download URL + for those can also be found in ``tools/ci/download-xs.sh``. +- Some tests require `NJOY `_ to preprocess + cross section data. The test suite assumes that you have an ``njoy`` + executable available on your :envvar:`PATH`. + +Running Tests +------------- To execute the test suite, go to the ``tests/`` directory and run:: @@ -46,6 +51,17 @@ installed and run:: pytest --cov=../openmc --cov-report=html +Generating XML Inputs +--------------------- + +Many of the regression tests rely on the Python API to build an appropriate +model. However, it can sometimes be desirable to work directly with the XML +input files rather than having to run a script in order to run the problem/test. +To build the input files for a test without actually running the test, you can +run:: + + pytest --build-inputs + Adding Tests to the Regression Suite ------------------------------------ From 91fdc10d46415401e658c5ffbbddc6ebba3a49d8 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 22 Jul 2019 07:12:23 -0500 Subject: [PATCH 4/4] Get rid of erroneous check in ParticleFilter.from_hdf5 --- openmc/filter.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/openmc/filter.py b/openmc/filter.py index 52ff8638ef..8527ca9b90 100644 --- a/openmc/filter.py +++ b/openmc/filter.py @@ -583,10 +583,6 @@ class ParticleFilter(Filter): + cls.short_name.lower() + "' but got '" + group['type'][()].decode() + " instead") - if 'meshes' not in kwargs: - raise ValueError(cls.__name__ + " requires a 'meshes' keyword " - "argument.") - particles = [b.decode() for b in group['bins'][()]] filter_id = int(group.name.split('/')[-1].lstrip('filter ')) return cls(particles, filter_id=filter_id)