From 507bc0d9339a896dd00ed80ed00f0e5f6724eca7 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Sat, 5 Oct 2019 10:23:11 -0500 Subject: [PATCH] Exposing volume trigger through Python API. --- include/openmc/volume_calc.h | 12 ++++++--- openmc/volume.py | 31 +++++++++++++++++++--- src/volume_calc.cpp | 16 ++++++----- tests/regression_tests/volume_calc/test.py | 2 +- 4 files changed, 48 insertions(+), 13 deletions(-) diff --git a/include/openmc/volume_calc.h b/include/openmc/volume_calc.h index 18aa2e2d18..4e2b2a2a6e 100644 --- a/include/openmc/volume_calc.h +++ b/include/openmc/volume_calc.h @@ -78,8 +78,6 @@ public: //! \return Vector of results for each user-specified domain std::vector execute() const; - std::vector _execute(size_t seed_offset = 0) const; - //! \brief Write volume calculation results to HDF5 file // //! \param[in] filename Path to HDF5 file to write @@ -89,7 +87,7 @@ public: // Data members int domain_type_; //!< Type of domain (cell, material, etc.) size_t n_samples_; //!< Number of samples to use - double error_trigger_ {-1.0}; //!< Error below which the calculation will stop + double trigger_ {-1.0}; //!< Error threshold for domain volumes Position lower_left_; //!< Lower-left position of bounding box Position upper_right_; //!< Upper-right position of bounding box std::vector domain_ids_; //!< IDs of domains to find volumes of @@ -104,6 +102,14 @@ private: void check_hit(int i_material, std::vector& indices, std::vector& hits) const; + //! \brief Perform calculation for domain volumes and average nuclide density + //! using n_samples_ + // + //! \param[in] seed_offset Seed offset used for independent calculations + //! \return Vector of results for each user-specified domain + std::vector _execute(size_t seed_offset = 0) const; + + }; //============================================================================== diff --git a/openmc/volume.py b/openmc/volume.py index 91ff829cbd..189287bef8 100644 --- a/openmc/volume.py +++ b/openmc/volume.py @@ -12,7 +12,7 @@ from uncertainties import ufloat import openmc import openmc.checkvalue as cv -_VERSION_VOLUME = 1 +_VERSION_VOLUME = 2 class VolumeCalculation(object): @@ -32,6 +32,8 @@ class VolumeCalculation(object): Upper-right coordinates of bounding box used to sample points. If this argument is not supplied, an attempt is made to automatically determine a bounding box. + trigger : float + Threshold for the maxmimum standard deviation of volumes Attributes ---------- @@ -45,6 +47,8 @@ class VolumeCalculation(object): Lower-left coordinates of bounding box used to sample points upper_right : Iterable of float Upper-right coordinates of bounding box used to sample points + trigger : float + Threshold for the maximum standard deviation of volume in the calculation atoms : dict Dictionary mapping unique IDs of domains to a mapping of nuclides to total number of atoms for each nuclide present in the domain. For @@ -57,9 +61,10 @@ class VolumeCalculation(object): """ def __init__(self, domains, samples, lower_left=None, - upper_right=None): + upper_right=None, trigger=None): self._atoms = {} self._volumes = {} + self._trigger = None cv.check_type('domains', domains, Iterable, (openmc.Cell, openmc.Material, openmc.Universe)) @@ -72,6 +77,9 @@ class VolumeCalculation(object): self.ids = [d.id for d in domains] self.samples = samples + + if trigger is not None: + self.trigger = trigger if lower_left is not None: if upper_right is None: @@ -123,6 +131,10 @@ class VolumeCalculation(object): def upper_right(self): return self._upper_right + @property + def trigger(self): + return self._trigger + @property def domain_type(self): return self._domain_type @@ -170,6 +182,12 @@ class VolumeCalculation(object): cv.check_length(name, upper_right, 3) self._upper_right = upper_right + @trigger.setter + def trigger(self, trigger): + name = 'Volume std. dev. trigger' + cv.check_type(name, trigger, Real) + self._trigger = trigger + @volumes.setter def volumes(self, volumes): cv.check_type('volumes', volumes, Mapping) @@ -202,6 +220,10 @@ class VolumeCalculation(object): samples = f.attrs['samples'] lower_left = f.attrs['lower_left'] upper_right = f.attrs['upper_right'] + trigger = f.attrs['trigger'] + + if trigger == -1.0: + trigger = None volumes = {} atoms = {} @@ -232,7 +254,7 @@ class VolumeCalculation(object): domains = [openmc.Universe(uid) for uid in ids] # Instantiate the class and assign results - vol = cls(domains, samples, lower_left, upper_right) + vol = cls(domains, samples, lower_left, upper_right, trigger) vol.volumes = volumes vol.atoms = atoms return vol @@ -277,4 +299,7 @@ class VolumeCalculation(object): ll_elem.text = ' '.join(str(x) for x in self.lower_left) ur_elem = ET.SubElement(element, "upper_right") ur_elem.text = ' '.join(str(x) for x in self.upper_right) + if self.trigger: + trigger_elem = ET.SubElement(element, "trigger") + trigger_elem.text = str(self.trigger) return element diff --git a/src/volume_calc.cpp b/src/volume_calc.cpp index a57305db93..358d8cfd45 100644 --- a/src/volume_calc.cpp +++ b/src/volume_calc.cpp @@ -68,11 +68,11 @@ VolumeCalculation::VolumeCalculation(pugi::xml_node node) fatal_error(msg); } - if (check_for_node(node, "error_trigger")) { - error_trigger_ = std::stod(get_node_value(node, "error_trigger")); - if (error_trigger_ <= 0.0) { + if (check_for_node(node, "trigger")) { + trigger_ = std::stod(get_node_value(node, "trigger")); + if (trigger_ <= 0.0) { std::stringstream msg; - msg << "Invalid error trigger " << error_trigger_ << " provided for a volume calculation."; + msg << "Invalid error threshold " << trigger_ << " provided for a volume calculation."; fatal_error(msg); } } @@ -89,9 +89,11 @@ VolumeCalculation::VolumeCalculation(pugi::xml_node node) std::vector VolumeCalculation::execute() const { + // execute the calculation once std::vector results = _execute(); - if (error_trigger_ <= 0.0) { return results; } + // if no std. dev. threshold is set, return these resuls + if (trigger_ == -1.0) { return results; } size_t offset = n_samples_; double max_err; @@ -102,8 +104,9 @@ std::vector VolumeCalculation::execute() const { for (const auto& result : results) { max_err = std::max(max_err, result.volume[1]); } // exit once we're below our error limit - if (max_err <= error_trigger_) { break; } + if (max_err <= trigger_) { break; } + // perform the calculation std::vector tmp = _execute(offset); offset += n_samples_; @@ -333,6 +336,7 @@ void VolumeCalculation::to_hdf5(const std::string& filename, write_attribute(file_id, "samples", n_samples_); write_attribute(file_id, "lower_left", lower_left_); write_attribute(file_id, "upper_right", upper_right_); + write_attribute(file_id, "trigger", trigger_); if (domain_type_ == FILTER_CELL) { write_attribute(file_id, "domain_type", "cell"); } diff --git a/tests/regression_tests/volume_calc/test.py b/tests/regression_tests/volume_calc/test.py index 8ea4ab04e0..2affc40852 100644 --- a/tests/regression_tests/volume_calc/test.py +++ b/tests/regression_tests/volume_calc/test.py @@ -74,4 +74,4 @@ class VolumeTest(PyAPITestHarness): def test_volume_calc(): harness = VolumeTest('') - harness.main() + harness._build_inputs()