From 0e0cd0a22d059159a8799906f57d12cec99c1a89 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian Date: Mon, 13 Sep 2021 01:25:36 -0300 Subject: [PATCH 01/66] Added hooks for NCrystal --- CMakeLists.txt | 16 +++++++++ include/openmc/material.h | 8 +++++ include/openmc/particle_data.h | 3 ++ include/openmc/physics.h | 27 ++++++++++++++++ include/openmc/settings.h | 3 ++ openmc/material.py | 59 ++++++++++++++++++++++++++++++++++ src/material.cpp | 36 ++++++++++++++++++++- src/physics.cpp | 21 +++++++++++- src/settings.cpp | 4 +++ 9 files changed, 175 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 521b46cfc..7be54dc3e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,7 @@ option(OPENMC_ENABLE_COVERAGE "Compile with coverage analysis flags" option(OPENMC_USE_DAGMC "Enable support for DAGMC (CAD) geometry" OFF) option(OPENMC_USE_LIBMESH "Enable support for libMesh unstructured mesh tallies" OFF) option(OPENMC_USE_MPI "Enable MPI" OFF) +option(OPENMC_USE_NCRYSTAL "Enable support for NCrystal scattering" OFF) # Warnings for deprecated options foreach(OLD_OPT IN ITEMS "openmp" "profile" "coverage" "dagmc" "libmesh") @@ -98,6 +99,14 @@ macro(find_package_write_status pkg) endif() endmacro() +#=============================================================================== +# NCrystal Scattering Support +#=============================================================================== + +if(OPENMC_USE_NCRYSTAL) + find_package(NCrystal REQUIRED PATH_SUFFIXES ./)#fixme +endif() + #=============================================================================== # DAGMC Geometry Support - need DAGMC/MOAB #=============================================================================== @@ -482,6 +491,13 @@ if (OPENMC_USE_MPI) target_link_libraries(libopenmc MPI::MPI_CXX) endif() +if(OPENMC_USE_NCRYSTAL) + target_compile_definitions(libopenmc PRIVATE NCRYSTAL) + target_link_libraries(libopenmc NCrystal::NCrystal) +endif() + + + #=============================================================================== # openmc executable #=============================================================================== diff --git a/include/openmc/material.h b/include/openmc/material.h index b251a3ca8..55f8ae20d 100644 --- a/include/openmc/material.h +++ b/include/openmc/material.h @@ -15,6 +15,10 @@ #include "openmc/particle.h" #include "openmc/vector.h" +#ifdef NCRYSTAL +#include "NCrystal/NCrystal.hh" +#endif + namespace openmc { //============================================================================== @@ -157,6 +161,10 @@ public: std::string name_; //!< Name of material vector nuclide_; //!< Indices in nuclides vector vector element_; //!< Indices in elements vector +#ifdef NCRYSTAL + std::string cfg_; //!< NCrystal configuration string + std::shared_ptr m_NCrystal_mat_; +#endif xt::xtensor atom_density_; //!< Nuclide atom density in [atom/b-cm] double density_; //!< Total atom density in [atom/b-cm] double density_gpcc_; //!< Total atom density in [g/cm^3] diff --git a/include/openmc/particle_data.h b/include/openmc/particle_data.h index d3d00571a..06ca5990a 100644 --- a/include/openmc/particle_data.h +++ b/include/openmc/particle_data.h @@ -162,6 +162,9 @@ struct MacroXS { double fission; //!< macroscopic fission xs double nu_fission; //!< macroscopic production xs double photon_prod; //!< macroscopic photon production xs +#ifdef NCRYSTAL + double NCrystal_XS; //!< macroscopic cross section of processes handled by NCrystal +#endif // Photon cross sections double coherent; //!< macroscopic coherent xs diff --git a/include/openmc/physics.h b/include/openmc/physics.h index 262b3a884..a1db07291 100644 --- a/include/openmc/physics.h +++ b/include/openmc/physics.h @@ -8,6 +8,10 @@ #include "openmc/reaction.h" #include "openmc/vector.h" +#ifdef NCRYSTAL +#include "NCrystal/NCRNG.hh" +#endif + namespace openmc { //============================================================================== @@ -101,6 +105,29 @@ void sample_secondary_photons(Particle& p, int i_nuclide); //! \param[in] p, particle to be split or rouletted with the weight window. void split_particle(Particle& p); +#ifdef NCRYSTAL +//============================================================================== +// NCrystal wrapper class for the OpenMC random number generator +//============================================================================== + +class NcrystalRNG_Wrapper : public NCrystal::RNGStream { + uint64_t* openmc_seed_; +public: + constexpr NcrystalRNG_Wrapper(uint64_t* s) noexcept : openmc_seed_(s) {} + //Can be cheaply created on the stack just before being used in calls to + //ProcImpl::Scatter objects, like: + // + // RNG_Wrapper rng(seed); + // +protected: + //double actualGenerate() override { return prn(openmc_seed_); } + double actualGenerate() override { + return std::max( std::numeric_limits::min(), prn(openmc_seed_) ); + } + +}; +#endif + } // namespace openmc #endif // OPENMC_PHYSICS_H diff --git a/include/openmc/settings.h b/include/openmc/settings.h index 1e061b235..ba1a30e30 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -120,6 +120,9 @@ extern int trigger_batch_interval; //!< Batch interval for triggers extern "C" int verbosity; //!< How verbose to make output extern double weight_cutoff; //!< Weight cutoff for Russian roulette extern double weight_survive; //!< Survival weight after Russian roulette +#ifdef NCRYSTAL +extern double ncrystal_max_energy; // Energy in eV to switch between NCrystal and ENDF +#endif } // namespace settings //============================================================================== diff --git a/openmc/material.py b/openmc/material.py index e99eec52c..1fe62961d 100644 --- a/openmc/material.py +++ b/openmc/material.py @@ -118,6 +118,7 @@ class Material(IDManagerMixin): self._volume = None self._atoms = {} self._isotropic = [] + self._NCrystal_cfg = None # A list of tuples (nuclide, percent, percent type) self._nuclides = [] @@ -140,6 +141,8 @@ class Material(IDManagerMixin): string += '{: <16}\n'.format('\tS(a,b) Tables') + string += '{: <16}=\t{}\n'.format('\tNCrystal conf', self._NCrystal_cfg) + for sab in self._sab: string += '{: <16}=\t{}\n'.format('\tS(a,b)', sab) @@ -331,6 +334,48 @@ class Material(IDManagerMixin): return material + @classmethod + def from_NCrystal(cls, cfg): + """Create material from NCrystal configuration string + Density is set from the NCrystal value, + and material temperature from the configuration string. + + Parameters + ---------- + cfg : str + NCrystal configuration string + + Returns + ------- + openmc.Material + Material instance + + """ + + try: + import NCrystal + except ImportError: + raise SystemExit("ERROR: NCrystal Python module is required.") + + NC_mat = NCrystal.createInfo(cfg) + NC_comp = NC_mat.getComposition() + + # Create the Material + material = cls() + + for frac, atom in NC_comp: + if (atom.A() == 0): + material.add_element(atom.displayLabel(), frac, 'ao') + else: + material.add_nuclide(atom.displayLabel(), frac, 'ao') + + material._NCrystal_cfg = cfg + material._density_units = "g/cm3" + material._density = NC_mat.getDensity() + material.temperature = NC_mat.getTemperature() + + return material + def add_volume_information(self, volume_calc): """Add volume information to a material. @@ -405,6 +450,9 @@ class Material(IDManagerMixin): 'macroscopic data-set has already been added'.format(self._id) raise ValueError(msg) + if self._NCrystal_cfg is not None: + raise ValueError("Cannot add nuclides to NCrystal material") + # If nuclide name doesn't look valid, give a warning try: Z, _, _ = openmc.data.zam(nuclide) @@ -609,6 +657,9 @@ class Material(IDManagerMixin): raise ValueError("Element name should be given by the " "element's symbol or name, e.g., 'Zr', 'zirconium'") + if self._NCrystal_cfg is not None: + raise ValueError("Cannot add elements to NCrystal material") + # Allow for element identifier to be given as a symbol or name if len(element) > 2: el = element.lower() @@ -1135,6 +1186,14 @@ class Material(IDManagerMixin): if self._volume: element.set("volume", str(self._volume)) + if self._NCrystal_cfg: + if self._sab: + raise ValueError("NCrystal materials are not compatible with S(a,b).") + if self._macroscopic is not None: + raise ValueError("NCrystal materials are not compatible macroscopic cross sections.") + + element.set("cfg", str(self._NCrystal_cfg)) + # Create temperature XML subelement if self.temperature is not None: element.set("temperature", str(self.temperature)) diff --git a/src/material.cpp b/src/material.cpp index 30dfa5ed5..d7fae8361 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -60,6 +60,14 @@ Material::Material(pugi::xml_node node) name_ = get_node_value(node, "name"); } +#ifdef NCRYSTAL + if (check_for_node(node, "cfg")) { + cfg_ = get_node_value(node, "cfg"); + write_message(5, "NCrystal config string: >>{}<< ", cfg_); + m_NCrystal_mat_ = NCrystal::FactImpl::createScatter(cfg_); + } +#endif + if (check_for_node(node, "depletable")) { depletable_ = get_node_value_bool(node, "depletable"); } @@ -792,6 +800,17 @@ void Material::calculate_neutron_xs(Particle& p) const // Initialize position in i_sab_nuclides int j = 0; +#ifdef NCRYSTAL + double ncrystal_xs = -1; + + if (m_NCrystal_mat_ != nullptr && p.E() < settings::ncrystal_max_energy){ + // Calculate scattering XS per atom with NCrystal, only once per material + NCrystal::CachePtr dummyCache; + auto nc_energy = NCrystal::NeutronEnergy{p.E()}; + ncrystal_xs = m_NCrystal_mat_->crossSection( dummyCache, nc_energy, {p.u().x, p.u().y, p.u().z}).get(); + } +#endif + // Add contribution from each nuclide in material for (int i = 0; i < nuclide_.size(); ++i) { // ====================================================================== @@ -830,10 +849,25 @@ void Material::calculate_neutron_xs(Particle& p) const int i_nuclide = nuclide_[i]; // Calculate microscopic cross section for this nuclide - const auto& micro {p.neutron_xs(i_nuclide)}; +#ifndef NCRYSTAL + const +#endif + auto& micro {p.neutron_xs(i_nuclide)}; if (p.E() != micro.last_E || p.sqrtkT() != micro.last_sqrtkT || i_sab != micro.index_sab || sab_frac != micro.sab_frac) { data::nuclides[i_nuclide]->calculate_xs(i_sab, i_grid, sab_frac, p); +#ifdef NCRYSTAL + if (ncrystal_xs >= 0.0){ + if ( micro.thermal > 0 || micro.thermal_elastic > 0) { + fatal_error("S(a,b) treatment and NCrystal are not compatible."); + } + data::nuclides[i_nuclide]->calculate_elastic_xs(p); + // remove free atom cross section + // and replace it by scattering cross section per atom from NCrystal + micro.total = micro.total - micro.elastic + ncrystal_xs; + micro.elastic = ncrystal_xs; + } +#endif } // ====================================================================== diff --git a/src/physics.cpp b/src/physics.cpp index bcdcf7ecf..9eff4aed7 100644 --- a/src/physics.cpp +++ b/src/physics.cpp @@ -140,8 +140,27 @@ void sample_neutron_reaction(Particle& p) // Sample a scattering reaction and determine the secondary energy of the // exiting neutron - scatter(p, i_nuclide); +#ifdef NCRYSTAL + if (model::materials[p.material_]->m_NCrystal_mat_ != nullptr && p.E() < settings::ncrystal_max_energy){ + + NcrystalRNG_Wrapper rng(p.current_seed()); // Initialize RNG + //create a cache pointer for multi thread physics + NCrystal::CachePtr dummyCache;//fixme: avoid recreating here (triggers malloc) + auto nc_energy = NCrystal::NeutronEnergy{p.E()}; + auto outcome = model::materials[p.material_]->m_NCrystal_mat_->sampleScatter( dummyCache, rng , nc_energy, {p.u().x, p.u().y, p.u().z}); + p.E_last() = p.E(); + p.E() = outcome.ekin.get(); + Direction u_old {p.u()}; + p.u() = Direction(outcome.direction[0], outcome.direction[1], outcome.direction[2]); + p.mu_ = u_old.dot(p.u()); + p.event_mt_ = ELASTIC;//fixme: define a new label for NCrystal? + } else { + scatter(p, i_nuclide); + } +#else + scatter(p, i_nuclide); +#endif // Advance URR seed stream 'N' times after energy changes if (p.E() != p.E_last()) { p.stream() = STREAM_URR_PTABLE; diff --git a/src/settings.cpp b/src/settings.cpp index eb0d4936c..3f3e97054 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -118,6 +118,10 @@ int verbosity {7}; double weight_cutoff {0.25}; double weight_survive {1.0}; +#ifdef NCRYSTAL +double ncrystal_max_energy{5.0}; +#endif + } // namespace settings //============================================================================== From 0f33e0b11c6add029f354eb3e5c89b002eec7500 Mon Sep 17 00:00:00 2001 From: Thomas Kittelmann Date: Tue, 13 Sep 2022 14:22:04 +0200 Subject: [PATCH 02/66] Add temporary requirement of natural elements in NCrystal materials --- openmc/material.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/openmc/material.py b/openmc/material.py index 1fe62961d..0a293eb0f 100644 --- a/openmc/material.py +++ b/openmc/material.py @@ -364,10 +364,9 @@ class Material(IDManagerMixin): material = cls() for frac, atom in NC_comp: - if (atom.A() == 0): - material.add_element(atom.displayLabel(), frac, 'ao') - else: - material.add_nuclide(atom.displayLabel(), frac, 'ao') + if not atom.isNaturalElement(): + raise ValueError('NCrystal-OpenMC interface only works with natural elements for now.') + material.add_element(atom.elementName(), frac, 'ao') material._NCrystal_cfg = cfg material._density_units = "g/cm3" From 9c54ac0b6b53336f834b5f868aa85e3e140239f8 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian Date: Tue, 13 Sep 2022 16:07:50 +0200 Subject: [PATCH 03/66] Fix access to private attributes --- include/openmc/material.h | 6 ++++++ src/physics.cpp | 9 ++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/include/openmc/material.h b/include/openmc/material.h index 55f8ae20d..eef557088 100644 --- a/include/openmc/material.h +++ b/include/openmc/material.h @@ -155,6 +155,12 @@ public: //! \return Temperature in [K] double temperature() const; +#ifdef NCRYSTAL + //! Gwet pointer to NCrystal material object + //! \return Pointer to NCrystal material object + std::shared_ptr m_NCrystal_mat() const {return m_NCrystal_mat_}; +#endif + //---------------------------------------------------------------------------- // Data int32_t id_ {C_NONE}; //!< Unique ID diff --git a/src/physics.cpp b/src/physics.cpp index 9eff4aed7..948307521 100644 --- a/src/physics.cpp +++ b/src/physics.cpp @@ -142,19 +142,18 @@ void sample_neutron_reaction(Particle& p) // exiting neutron #ifdef NCRYSTAL - if (model::materials[p.material_]->m_NCrystal_mat_ != nullptr && p.E() < settings::ncrystal_max_energy){ - + if (model::materials[p.material()]->m_NCrystal_mat() != nullptr && p.E() < settings::ncrystal_max_energy){ NcrystalRNG_Wrapper rng(p.current_seed()); // Initialize RNG //create a cache pointer for multi thread physics NCrystal::CachePtr dummyCache;//fixme: avoid recreating here (triggers malloc) auto nc_energy = NCrystal::NeutronEnergy{p.E()}; - auto outcome = model::materials[p.material_]->m_NCrystal_mat_->sampleScatter( dummyCache, rng , nc_energy, {p.u().x, p.u().y, p.u().z}); + auto outcome = model::materials[p.material()]->m_NCrystal_mat()->sampleScatter( dummyCache, rng , nc_energy, {p.u().x, p.u().y, p.u().z}); p.E_last() = p.E(); p.E() = outcome.ekin.get(); Direction u_old {p.u()}; p.u() = Direction(outcome.direction[0], outcome.direction[1], outcome.direction[2]); - p.mu_ = u_old.dot(p.u()); - p.event_mt_ = ELASTIC;//fixme: define a new label for NCrystal? + p.mu() = u_old.dot(p.u()); + p.event_mt() = ELASTIC;//fixme: define a new label for NCrystal? } else { scatter(p, i_nuclide); } From 70d5c76ebd70cf43349423f17028c3cb1dfe1daa Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian Date: Tue, 13 Sep 2022 16:10:05 +0200 Subject: [PATCH 04/66] Fixed material.h --- include/openmc/material.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/openmc/material.h b/include/openmc/material.h index eef557088..10e64773e 100644 --- a/include/openmc/material.h +++ b/include/openmc/material.h @@ -158,7 +158,7 @@ public: #ifdef NCRYSTAL //! Gwet pointer to NCrystal material object //! \return Pointer to NCrystal material object - std::shared_ptr m_NCrystal_mat() const {return m_NCrystal_mat_}; + std::shared_ptr m_NCrystal_mat() const {return m_NCrystal_mat_; }; #endif //---------------------------------------------------------------------------- From 29a10eafd3db601ffa9a7e7bf882431c150ee1b2 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian Date: Fri, 16 Sep 2022 09:03:09 -0300 Subject: [PATCH 05/66] Removed MacroNCrystalXS This was left from a previous implementation. Now it is not needed because NCrystal just takes over all neutron scattering events below a given energy threshold. --- include/openmc/particle_data.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/include/openmc/particle_data.h b/include/openmc/particle_data.h index 06ca5990a..d3d00571a 100644 --- a/include/openmc/particle_data.h +++ b/include/openmc/particle_data.h @@ -162,9 +162,6 @@ struct MacroXS { double fission; //!< macroscopic fission xs double nu_fission; //!< macroscopic production xs double photon_prod; //!< macroscopic photon production xs -#ifdef NCRYSTAL - double NCrystal_XS; //!< macroscopic cross section of processes handled by NCrystal -#endif // Photon cross sections double coherent; //!< macroscopic coherent xs From 2e19260dea9176f03f2d5d80be19c8c819d339d3 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian Date: Mon, 26 Sep 2022 11:19:54 +0200 Subject: [PATCH 06/66] Implement recommended changes --- CMakeLists.txt | 2 +- include/openmc/material.h | 8 ++++---- include/openmc/physics.h | 10 ++-------- openmc/material.py | 31 ++++++++++++++----------------- src/material.cpp | 16 +++++++++------- src/physics.cpp | 10 +++++----- 6 files changed, 35 insertions(+), 42 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7be54dc3e..7080accfd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -104,7 +104,7 @@ endmacro() #=============================================================================== if(OPENMC_USE_NCRYSTAL) - find_package(NCrystal REQUIRED PATH_SUFFIXES ./)#fixme + find_package(NCrystal REQUIRED) endif() #=============================================================================== diff --git a/include/openmc/material.h b/include/openmc/material.h index 10e64773e..30d8ffb7d 100644 --- a/include/openmc/material.h +++ b/include/openmc/material.h @@ -156,9 +156,9 @@ public: double temperature() const; #ifdef NCRYSTAL - //! Gwet pointer to NCrystal material object + //! Get pointer to NCrystal material object //! \return Pointer to NCrystal material object - std::shared_ptr m_NCrystal_mat() const {return m_NCrystal_mat_; }; + std::shared_ptr ncrystal_mat() const {return ncrystal_mat_; }; #endif //---------------------------------------------------------------------------- @@ -168,8 +168,8 @@ public: vector nuclide_; //!< Indices in nuclides vector vector element_; //!< Indices in elements vector #ifdef NCRYSTAL - std::string cfg_; //!< NCrystal configuration string - std::shared_ptr m_NCrystal_mat_; + std::string ncrystal_cfg_; //!< NCrystal configuration string + std::shared_ptr ncrystal_mat_; #endif xt::xtensor atom_density_; //!< Nuclide atom density in [atom/b-cm] double density_; //!< Total atom density in [atom/b-cm] diff --git a/include/openmc/physics.h b/include/openmc/physics.h index a1db07291..109f7caff 100644 --- a/include/openmc/physics.h +++ b/include/openmc/physics.h @@ -110,17 +110,11 @@ void split_particle(Particle& p); // NCrystal wrapper class for the OpenMC random number generator //============================================================================== -class NcrystalRNG_Wrapper : public NCrystal::RNGStream { +class NCrystalRNGWrapper : public NCrystal::RNGStream { uint64_t* openmc_seed_; public: - constexpr NcrystalRNG_Wrapper(uint64_t* s) noexcept : openmc_seed_(s) {} - //Can be cheaply created on the stack just before being used in calls to - //ProcImpl::Scatter objects, like: - // - // RNG_Wrapper rng(seed); - // + constexpr NCrystalRNGWrapper(uint64_t* seed) noexcept : openmc_seed_(seed) {} protected: - //double actualGenerate() override { return prn(openmc_seed_); } double actualGenerate() override { return std::max( std::numeric_limits::min(), prn(openmc_seed_) ); } diff --git a/openmc/material.py b/openmc/material.py index 0a293eb0f..15d1f8ac9 100644 --- a/openmc/material.py +++ b/openmc/material.py @@ -118,7 +118,7 @@ class Material(IDManagerMixin): self._volume = None self._atoms = {} self._isotropic = [] - self._NCrystal_cfg = None + self._ncrystal_cfg = None # A list of tuples (nuclide, percent, percent type) self._nuclides = [] @@ -141,7 +141,7 @@ class Material(IDManagerMixin): string += '{: <16}\n'.format('\tS(a,b) Tables') - string += '{: <16}=\t{}\n'.format('\tNCrystal conf', self._NCrystal_cfg) + string += '{: <16}=\t{}\n'.format('\tNCrystal conf', self._ncrystal_cfg) for sab in self._sab: string += '{: <16}=\t{}\n'.format('\tS(a,b)', sab) @@ -335,7 +335,7 @@ class Material(IDManagerMixin): return material @classmethod - def from_NCrystal(cls, cfg): + def from_ncrystal(cls, cfg): """Create material from NCrystal configuration string Density is set from the NCrystal value, and material temperature from the configuration string. @@ -352,26 +352,23 @@ class Material(IDManagerMixin): """ - try: - import NCrystal - except ImportError: - raise SystemExit("ERROR: NCrystal Python module is required.") + import NCrystal - NC_mat = NCrystal.createInfo(cfg) - NC_comp = NC_mat.getComposition() + nc_mat = NCrystal.createInfo(cfg) + nc_comp = nc_mat.getComposition() # Create the Material material = cls() - for frac, atom in NC_comp: + for frac, atom in nc_comp: if not atom.isNaturalElement(): raise ValueError('NCrystal-OpenMC interface only works with natural elements for now.') material.add_element(atom.elementName(), frac, 'ao') - material._NCrystal_cfg = cfg + material._ncrystal_cfg = cfg material._density_units = "g/cm3" - material._density = NC_mat.getDensity() - material.temperature = NC_mat.getTemperature() + material._density = nc_mat.getDensity() + material.temperature = nc_mat.getTemperature() return material @@ -449,7 +446,7 @@ class Material(IDManagerMixin): 'macroscopic data-set has already been added'.format(self._id) raise ValueError(msg) - if self._NCrystal_cfg is not None: + if self._ncrystal_cfg is not None: raise ValueError("Cannot add nuclides to NCrystal material") # If nuclide name doesn't look valid, give a warning @@ -656,7 +653,7 @@ class Material(IDManagerMixin): raise ValueError("Element name should be given by the " "element's symbol or name, e.g., 'Zr', 'zirconium'") - if self._NCrystal_cfg is not None: + if self._ncrystal_cfg is not None: raise ValueError("Cannot add elements to NCrystal material") # Allow for element identifier to be given as a symbol or name @@ -1185,13 +1182,13 @@ class Material(IDManagerMixin): if self._volume: element.set("volume", str(self._volume)) - if self._NCrystal_cfg: + if self._ncrystal_cfg: if self._sab: raise ValueError("NCrystal materials are not compatible with S(a,b).") if self._macroscopic is not None: raise ValueError("NCrystal materials are not compatible macroscopic cross sections.") - element.set("cfg", str(self._NCrystal_cfg)) + element.set("cfg", str(self._ncrystal_cfg)) # Create temperature XML subelement if self.temperature is not None: diff --git a/src/material.cpp b/src/material.cpp index d7fae8361..13acbba1f 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -63,8 +63,8 @@ Material::Material(pugi::xml_node node) #ifdef NCRYSTAL if (check_for_node(node, "cfg")) { cfg_ = get_node_value(node, "cfg"); - write_message(5, "NCrystal config string: >>{}<< ", cfg_); - m_NCrystal_mat_ = NCrystal::FactImpl::createScatter(cfg_); + write_message(5, "NCrystal config string: '{}'", ncrystal_cfg_); + ncrystal_mat_ = NCrystal::FactImpl::createScatter(ncrystal_cfg_); } #endif @@ -803,11 +803,11 @@ void Material::calculate_neutron_xs(Particle& p) const #ifdef NCRYSTAL double ncrystal_xs = -1; - if (m_NCrystal_mat_ != nullptr && p.E() < settings::ncrystal_max_energy){ + if (ncrystal_mat_ && p.E() < settings::ncrystal_max_energy){ // Calculate scattering XS per atom with NCrystal, only once per material - NCrystal::CachePtr dummyCache; + NCrystal::CachePtr dummy_cache; auto nc_energy = NCrystal::NeutronEnergy{p.E()}; - ncrystal_xs = m_NCrystal_mat_->crossSection( dummyCache, nc_energy, {p.u().x, p.u().y, p.u().z}).get(); + ncrystal_xs = ncrystal_mat_->crossSection( dummy_cache, nc_energy, {p.u().x, p.u().y, p.u().z}).get(); } #endif @@ -849,8 +849,10 @@ void Material::calculate_neutron_xs(Particle& p) const int i_nuclide = nuclide_[i]; // Calculate microscopic cross section for this nuclide -#ifndef NCRYSTAL - const +#ifdef NCRYSTAL + auto& micro {p.neutron_xs(i_nuclide)}; +#else + const auto& micro {p.neutron_xs(i_nuclide)}; #endif auto& micro {p.neutron_xs(i_nuclide)}; if (p.E() != micro.last_E || p.sqrtkT() != micro.last_sqrtkT || diff --git a/src/physics.cpp b/src/physics.cpp index 948307521..bad499772 100644 --- a/src/physics.cpp +++ b/src/physics.cpp @@ -142,18 +142,18 @@ void sample_neutron_reaction(Particle& p) // exiting neutron #ifdef NCRYSTAL - if (model::materials[p.material()]->m_NCrystal_mat() != nullptr && p.E() < settings::ncrystal_max_energy){ - NcrystalRNG_Wrapper rng(p.current_seed()); // Initialize RNG + if (model::materials[p.material()]->ncrystal_mat() && p.E() < settings::ncrystal_max_energy){ + NCrystalRNGWrapper rng(p.current_seed()); // Initialize RNG //create a cache pointer for multi thread physics - NCrystal::CachePtr dummyCache;//fixme: avoid recreating here (triggers malloc) + NCrystal::CachePtr dummy_cache; auto nc_energy = NCrystal::NeutronEnergy{p.E()}; - auto outcome = model::materials[p.material()]->m_NCrystal_mat()->sampleScatter( dummyCache, rng , nc_energy, {p.u().x, p.u().y, p.u().z}); + auto outcome = model::materials[p.material()]->ncrystal_mat()->sampleScatter( dummy_cache, rng , nc_energy, {p.u().x, p.u().y, p.u().z}); p.E_last() = p.E(); p.E() = outcome.ekin.get(); Direction u_old {p.u()}; p.u() = Direction(outcome.direction[0], outcome.direction[1], outcome.direction[2]); p.mu() = u_old.dot(p.u()); - p.event_mt() = ELASTIC;//fixme: define a new label for NCrystal? + p.event_mt() = ELASTIC; } else { scatter(p, i_nuclide); } From 5afcc9e49b60ec1c5e2290d4725bac7a96e1692e Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian Date: Mon, 26 Sep 2022 11:30:39 +0200 Subject: [PATCH 07/66] Apply clang-format --- include/openmc/material.h | 13 ++++++++----- include/openmc/physics.h | 17 ++++++++++------- include/openmc/settings.h | 3 ++- src/material.cpp | 33 ++++++++++++++++++--------------- src/physics.cpp | 18 +++++++++++------- src/settings.cpp | 2 +- 6 files changed, 50 insertions(+), 36 deletions(-) diff --git a/include/openmc/material.h b/include/openmc/material.h index 30d8ffb7d..47e3e6a16 100644 --- a/include/openmc/material.h +++ b/include/openmc/material.h @@ -158,15 +158,18 @@ public: #ifdef NCRYSTAL //! Get pointer to NCrystal material object //! \return Pointer to NCrystal material object - std::shared_ptr ncrystal_mat() const {return ncrystal_mat_; }; + std::shared_ptr ncrystal_mat() const + { + return ncrystal_mat_; + }; #endif //---------------------------------------------------------------------------- // Data - int32_t id_ {C_NONE}; //!< Unique ID - std::string name_; //!< Name of material - vector nuclide_; //!< Indices in nuclides vector - vector element_; //!< Indices in elements vector + int32_t id_ {C_NONE}; //!< Unique ID + std::string name_; //!< Name of material + vector nuclide_; //!< Indices in nuclides vector + vector element_; //!< Indices in elements vector #ifdef NCRYSTAL std::string ncrystal_cfg_; //!< NCrystal configuration string std::shared_ptr ncrystal_mat_; diff --git a/include/openmc/physics.h b/include/openmc/physics.h index 109f7caff..23995e7af 100644 --- a/include/openmc/physics.h +++ b/include/openmc/physics.h @@ -111,14 +111,17 @@ void split_particle(Particle& p); //============================================================================== class NCrystalRNGWrapper : public NCrystal::RNGStream { - uint64_t* openmc_seed_; -public: - constexpr NCrystalRNGWrapper(uint64_t* seed) noexcept : openmc_seed_(seed) {} -protected: - double actualGenerate() override { - return std::max( std::numeric_limits::min(), prn(openmc_seed_) ); - } + uint64_t* openmc_seed_; +public: + constexpr NCrystalRNGWrapper(uint64_t* seed) noexcept : openmc_seed_(seed) {} + +protected: + double actualGenerate() override + { + return std::max( + std::numeric_limits::min(), prn(openmc_seed_)); + } }; #endif diff --git a/include/openmc/settings.h b/include/openmc/settings.h index ba1a30e30..9bb447005 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -121,7 +121,8 @@ extern "C" int verbosity; //!< How verbose to make output extern double weight_cutoff; //!< Weight cutoff for Russian roulette extern double weight_survive; //!< Survival weight after Russian roulette #ifdef NCRYSTAL -extern double ncrystal_max_energy; // Energy in eV to switch between NCrystal and ENDF +extern double + ncrystal_max_energy; //!< Energy in eV to switch between NCrystal and ENDF #endif } // namespace settings diff --git a/src/material.cpp b/src/material.cpp index 13acbba1f..e894084e2 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -64,7 +64,7 @@ Material::Material(pugi::xml_node node) if (check_for_node(node, "cfg")) { cfg_ = get_node_value(node, "cfg"); write_message(5, "NCrystal config string: '{}'", ncrystal_cfg_); - ncrystal_mat_ = NCrystal::FactImpl::createScatter(ncrystal_cfg_); + ncrystal_mat_ = NCrystal::FactImpl::createScatter(ncrystal_cfg_); } #endif @@ -375,8 +375,8 @@ void Material::finalize() this->init_thermal(); } -// Normalize density -this->normalize_density(); + // Normalize density + this->normalize_density(); } void Material::normalize_density() @@ -803,11 +803,14 @@ void Material::calculate_neutron_xs(Particle& p) const #ifdef NCRYSTAL double ncrystal_xs = -1; - if (ncrystal_mat_ && p.E() < settings::ncrystal_max_energy){ + if (ncrystal_mat_ && p.E() < settings::ncrystal_max_energy) { // Calculate scattering XS per atom with NCrystal, only once per material NCrystal::CachePtr dummy_cache; - auto nc_energy = NCrystal::NeutronEnergy{p.E()}; - ncrystal_xs = ncrystal_mat_->crossSection( dummy_cache, nc_energy, {p.u().x, p.u().y, p.u().z}).get(); + auto nc_energy = NCrystal::NeutronEnergy {p.E()}; + ncrystal_xs = + ncrystal_mat_ + ->crossSection(dummy_cache, nc_energy, {p.u().x, p.u().y, p.u().z}) + .get(); } #endif @@ -859,15 +862,15 @@ void Material::calculate_neutron_xs(Particle& p) const i_sab != micro.index_sab || sab_frac != micro.sab_frac) { data::nuclides[i_nuclide]->calculate_xs(i_sab, i_grid, sab_frac, p); #ifdef NCRYSTAL - if (ncrystal_xs >= 0.0){ - if ( micro.thermal > 0 || micro.thermal_elastic > 0) { - fatal_error("S(a,b) treatment and NCrystal are not compatible."); - } - data::nuclides[i_nuclide]->calculate_elastic_xs(p); - // remove free atom cross section - // and replace it by scattering cross section per atom from NCrystal - micro.total = micro.total - micro.elastic + ncrystal_xs; - micro.elastic = ncrystal_xs; + if (ncrystal_xs >= 0.0) { + if (micro.thermal > 0 || micro.thermal_elastic > 0) { + fatal_error("S(a,b) treatment and NCrystal are not compatible."); + } + data::nuclides[i_nuclide]->calculate_elastic_xs(p); + // remove free atom cross section + // and replace it by scattering cross section per atom from NCrystal + micro.total = micro.total - micro.elastic + ncrystal_xs; + micro.elastic = ncrystal_xs; } #endif } diff --git a/src/physics.cpp b/src/physics.cpp index bad499772..e19640314 100644 --- a/src/physics.cpp +++ b/src/physics.cpp @@ -138,20 +138,24 @@ void sample_neutron_reaction(Particle& p) if (!p.alive()) return; - // Sample a scattering reaction and determine the secondary energy of the - // exiting neutron + // Sample a scattering reaction and determine the secondary energy of the + // exiting neutron #ifdef NCRYSTAL - if (model::materials[p.material()]->ncrystal_mat() && p.E() < settings::ncrystal_max_energy){ + if (model::materials[p.material()]->ncrystal_mat() && + p.E() < settings::ncrystal_max_energy) { NCrystalRNGWrapper rng(p.current_seed()); // Initialize RNG - //create a cache pointer for multi thread physics + // create a cache pointer for multi thread physics NCrystal::CachePtr dummy_cache; - auto nc_energy = NCrystal::NeutronEnergy{p.E()}; - auto outcome = model::materials[p.material()]->ncrystal_mat()->sampleScatter( dummy_cache, rng , nc_energy, {p.u().x, p.u().y, p.u().z}); + auto nc_energy = NCrystal::NeutronEnergy {p.E()}; + auto outcome = + model::materials[p.material()]->ncrystal_mat()->sampleScatter( + dummy_cache, rng, nc_energy, {p.u().x, p.u().y, p.u().z}); p.E_last() = p.E(); p.E() = outcome.ekin.get(); Direction u_old {p.u()}; - p.u() = Direction(outcome.direction[0], outcome.direction[1], outcome.direction[2]); + p.u() = Direction( + outcome.direction[0], outcome.direction[1], outcome.direction[2]); p.mu() = u_old.dot(p.u()); p.event_mt() = ELASTIC; } else { diff --git a/src/settings.cpp b/src/settings.cpp index 3f3e97054..87c24eedd 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -119,7 +119,7 @@ double weight_cutoff {0.25}; double weight_survive {1.0}; #ifdef NCRYSTAL -double ncrystal_max_energy{5.0}; +double ncrystal_max_energy {5.0}; #endif } // namespace settings From fdf763e801b1439da96009fd5dadeadeb93e36f7 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian Date: Mon, 26 Sep 2022 12:35:12 +0200 Subject: [PATCH 08/66] Couple of fixes --- src/material.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/material.cpp b/src/material.cpp index e894084e2..9531e9f43 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -62,7 +62,7 @@ Material::Material(pugi::xml_node node) #ifdef NCRYSTAL if (check_for_node(node, "cfg")) { - cfg_ = get_node_value(node, "cfg"); + ncrystal_cfg_ = get_node_value(node, "cfg"); write_message(5, "NCrystal config string: '{}'", ncrystal_cfg_); ncrystal_mat_ = NCrystal::FactImpl::createScatter(ncrystal_cfg_); } @@ -857,7 +857,6 @@ void Material::calculate_neutron_xs(Particle& p) const #else const auto& micro {p.neutron_xs(i_nuclide)}; #endif - auto& micro {p.neutron_xs(i_nuclide)}; if (p.E() != micro.last_E || p.sqrtkT() != micro.last_sqrtkT || i_sab != micro.index_sab || sab_frac != micro.sab_frac) { data::nuclides[i_nuclide]->calculate_xs(i_sab, i_grid, sab_frac, p); From 52ee2c37ced8fce859d2ec6a06c6909dd0d04b44 Mon Sep 17 00:00:00 2001 From: Thomas Kittelmann Date: Thu, 20 Oct 2022 11:36:22 +0200 Subject: [PATCH 09/66] Print where NCrystal was found as suggested by @paulromano Co-authored-by: Paul Romano --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7080accfd..2815230d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -105,6 +105,7 @@ endmacro() if(OPENMC_USE_NCRYSTAL) find_package(NCrystal REQUIRED) + message(STATUS "Found NCrystal: ${NCrystal_DIR} (version ${NCrystal_VERSION})") endif() #=============================================================================== From a5b811ec899470f9b0bb3e387f0f672ae8490321 Mon Sep 17 00:00:00 2001 From: Thomas Kittelmann Date: Thu, 20 Oct 2022 13:25:05 +0200 Subject: [PATCH 10/66] Use new NCrystal getFlattenedComposition method to better support more complicated materials --- openmc/material.py | 63 +++++++++++++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 17 deletions(-) diff --git a/openmc/material.py b/openmc/material.py index 15d1f8ac9..de4a22cb8 100644 --- a/openmc/material.py +++ b/openmc/material.py @@ -335,15 +335,23 @@ class Material(IDManagerMixin): return material @classmethod - def from_ncrystal(cls, cfg): - """Create material from NCrystal configuration string - Density is set from the NCrystal value, - and material temperature from the configuration string. - + def from_ncrystal(cls, cfg, material_id=None, name=''): + """Create material from NCrystal configuration string. Density, + temperature, and material composition, and (ultimately) thermal neutron + scattering, will be automatically be provided by NCrystal based on this + string. The name and material_id parameters are simply passed on to the + Material constructor. + Parameters ---------- cfg : str NCrystal configuration string + material_id : int, optional + Unique identifier for the material. If not specified, an identifier will + automatically be assigned. + name : str, optional + Name of the material. If not specified, the name will be the empty + string. Returns ------- @@ -353,22 +361,43 @@ class Material(IDManagerMixin): """ import NCrystal - nc_mat = NCrystal.createInfo(cfg) - nc_comp = nc_mat.getComposition() + + def openmc_natabund( Z ): + #nc_mat.getFlattenedComposition might need natural abundancies. + #This call-back function is used so NCrystal can flatten composition + #using OpenMC's natural abundancies. In practice this function will + #only get invoked in the unlikely case where a material is specified + #by referring both to natural elements and specific isotopes of the + #same element. + elem_name = openmc.data.ATOMIC_SYMBOL.get( Z, None ) + if not elem_name: + raise ValueError( f'Element with Z={Z} is not known' ) + l = [] + for iso_name,abund in openmc.data.isotopes( elem_name ): + l.append( ( int(iso_name[ len(elem_name) : ]), abund ) ) + return l + + flat_compos = nc_mat.getFlattenedComposition( preferNaturalElements = True, + naturalAbundProvider = openmc_natabund ) # Create the Material - material = cls() + material = cls( material_id = material_id, + name = name, + temperature = nc_mat.getTemperature() ) - for frac, atom in nc_comp: - if not atom.isNaturalElement(): - raise ValueError('NCrystal-OpenMC interface only works with natural elements for now.') - material.add_element(atom.elementName(), frac, 'ao') + for Z, A_vals in flat_compos: + elemname = openmc.data.ATOMIC_SYMBOL.get(Z,None) + if not elemname: + raise ValueError(f'Element with Z={Z} is not known') + for A, frac in A_vals: + if A: + material.add_nuclide( elemname + str(A), frac, 'ao' ) + else: + material.add_element( elemname, frac, 'ao' ) - material._ncrystal_cfg = cfg - material._density_units = "g/cm3" - material._density = nc_mat.getDensity() - material.temperature = nc_mat.getTemperature() + material.set_density( 'g/cm3', nc_mat.getDensity() ) + material._ncrystal_cfg = NCrystal.normaliseCfg( cfg ) return material @@ -1186,7 +1215,7 @@ class Material(IDManagerMixin): if self._sab: raise ValueError("NCrystal materials are not compatible with S(a,b).") if self._macroscopic is not None: - raise ValueError("NCrystal materials are not compatible macroscopic cross sections.") + raise ValueError("NCrystal materials are not compatible with macroscopic cross sections.") element.set("cfg", str(self._ncrystal_cfg)) From f2e807e942ed421cd03fc87823c579e47dfcce3c Mon Sep 17 00:00:00 2001 From: Thomas Kittelmann Date: Thu, 20 Oct 2022 13:26:45 +0200 Subject: [PATCH 11/66] Move NCrystalRNGWrapper data member into explicit private section as requested --- include/openmc/physics.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/openmc/physics.h b/include/openmc/physics.h index 23995e7af..806297c4e 100644 --- a/include/openmc/physics.h +++ b/include/openmc/physics.h @@ -111,8 +111,6 @@ void split_particle(Particle& p); //============================================================================== class NCrystalRNGWrapper : public NCrystal::RNGStream { - uint64_t* openmc_seed_; - public: constexpr NCrystalRNGWrapper(uint64_t* seed) noexcept : openmc_seed_(seed) {} @@ -122,6 +120,8 @@ protected: return std::max( std::numeric_limits::min(), prn(openmc_seed_)); } +private: + uint64_t* openmc_seed_; }; #endif From aca4fcb42bbfb45bd3ded0f9f1bbf8b7107aa632 Mon Sep 17 00:00:00 2001 From: Thomas Kittelmann Date: Fri, 21 Oct 2022 10:30:05 +0200 Subject: [PATCH 12/66] NCrystal cfgstr printout now mentions material ID --- src/material.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/material.cpp b/src/material.cpp index 9531e9f43..32f5f2e86 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -63,7 +63,7 @@ Material::Material(pugi::xml_node node) #ifdef NCRYSTAL if (check_for_node(node, "cfg")) { ncrystal_cfg_ = get_node_value(node, "cfg"); - write_message(5, "NCrystal config string: '{}'", ncrystal_cfg_); + write_message(5, "NCrystal config string for material #{}: '{}'", this->id(), ncrystal_cfg_); ncrystal_mat_ = NCrystal::FactImpl::createScatter(ncrystal_cfg_); } #endif From a3dd55d8bc4b28323b73e9a36c52f950db6ea1fb Mon Sep 17 00:00:00 2001 From: Thomas Kittelmann Date: Fri, 21 Oct 2022 15:04:57 +0200 Subject: [PATCH 13/66] first stab at gha-install-ncrystal.sh --- tools/ci/gha-install-ncrystal.sh | 62 ++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100755 tools/ci/gha-install-ncrystal.sh diff --git a/tools/ci/gha-install-ncrystal.sh b/tools/ci/gha-install-ncrystal.sh new file mode 100755 index 000000000..c18087f23 --- /dev/null +++ b/tools/ci/gha-install-ncrystal.sh @@ -0,0 +1,62 @@ +#!/bin/bash +set -ex +cd $HOME + +#Use the NCrystal develop branch (in the near future we can move this to master): +git clone https://github.com/mctools/ncrystal --branch develop --single-branch --depth 1 ncrystal_src + +SRC_DIR="$PWD/ncrystal_src" +BLD_DIR="$PWD/ncrystal_bld" +INST_DIR="$PWD/ncrystal_inst" +PYTHON=$(which python3) + +CPU_COUNT=1 + +mkdir "$BLD_DIR" +cd ncrystal_bld + +#cmake -Dstatic=on .. && make 2>/dev/null && sudo make install + +cmake \ + "${SRC_DIR}" \ + -DBUILD_SHARED_LIBS=ON \ + -DNCRYSTAL_NOTOUCH_CMAKE_BUILD_TYPE=ON \ + -DMODIFY_RPATH=OFF \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_EXAMPLES=OFF \ + -DINSTALL_SETUPSH=OFF \ + -DEMBED_DATA=ON \ + -DINSTALL_DATA=OFF \ + -DNO_DIRECT_PYMODINST=ON \ + -DPython3_EXECUTABLE="$PYTHON" + +make -j${CPU_COUNT:-1} +make install + +#Note: There is no "make test" or "make ctest" functionality for NCrystal +# yet. If it appears in the future, we should add it here. + + +#The next stuff is not pretty, but it is needed as a temporary +#workaround until NCrystal add better support for system-wide +#installations in CMake: + +cat < ./findncrystallib.py +import pathlib +libname = pathlib.Path('./cfg_ncrystal_libname.txt').read_text().strip() +libs = list(pathlib.Path("/usr").glob("**/lib*/**/%s"%libname)) +assert len(libs)==1 +pathlib.Path('ncrystal_liblocation.txt').write_text(str(libs[0])) +EOF + +python3 ./findncrystallib.py + +TMPNCRYSTALLIBLOCATION=$(cat ./ncrystal_liblocation.txt) + +cat < ./ncrystal_pypkg/NCrystal/_nclibpath.py +#File autogenerated for working with systemwide install of NCrystal: +import pathlib +liblocation = pathlib.Path('${TMPNCRYSTALLIBLOCATION}'.strip()) +EOF + +$PYTHON -m pip install ./ncrystal_pypkg/ -vv From e64bf6a7ffb1b33f732e804bf10ff376d2cba700 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian <22483345+marquezj@users.noreply.github.com> Date: Thu, 10 Nov 2022 12:31:45 -0300 Subject: [PATCH 14/66] Add installation instructions for NCrystal --- docs/source/usersguide/install.rst | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index 74be9ba0b..9e5ba1ffd 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -271,6 +271,16 @@ Prerequisites cmake -DOPENMC_USE_DAGMC=on -DCMAKE_PREFIX_PATH=/path/to/dagmc/installation .. + * NCrystal_ library for defining materials with enhanced thermal neutron transport + + Adding this option allows the creation of materials from NCrystal, which + replaces the scattering kernel treatment of ACE files with a modular, on-the-fly approach. + To use it `install `_ and + `initialize `_ + NCrystal and turn on the option in the CMake configuration step: + + cmake -DOPENMC_USE_NCRYSTAL=on .. + * libMesh_ mesh library framework for numerical simulations of partial differential equations This optional dependency enables support for unstructured mesh tally @@ -294,6 +304,7 @@ Prerequisites .. _MOAB: https://bitbucket.org/fathomteam/moab .. _libMesh: https://libmesh.github.io/ .. _libpng: http://www.libpng.org/pub/png/libpng.html +.. _NCrystal: https://github.com/mctools/ncrystal Obtaining the Source -------------------- @@ -362,6 +373,12 @@ OPENMC_USE_DAGMC should also be defined as `DAGMC_ROOT` in the CMake configuration command. (Default: off) +OPENMC_USE_NCRYSTAL + Turns on support for NCrystal materials. NCrystal must be + `installed `_ and + `initialized `_. + (Default: off) + OPENMC_USE_LIBMESH Enables the use of unstructured mesh tallies with libMesh_. (Default: off) From 1f3c5ed6bc4b29259d300a0d9b0fac0decf0eff6 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian <22483345+marquezj@users.noreply.github.com> Date: Thu, 10 Nov 2022 13:04:47 -0300 Subject: [PATCH 15/66] Add use instructions and examples for NCrystal --- docs/source/usersguide/materials.rst | 37 ++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/docs/source/usersguide/materials.rst b/docs/source/usersguide/materials.rst index 8b43f1a2a..ab5a8c5af 100644 --- a/docs/source/usersguide/materials.rst +++ b/docs/source/usersguide/materials.rst @@ -101,6 +101,42 @@ you would need to add hydrogen and oxygen to a material and then assign the .. _usersguide_naming: +------------------ +Adding NCrystal materials +------------------ + +Additional support for thermal scattering can be added by using NCrystal_. +The :meth:`Material.from_ncrystal` class method generates a material object from +an `NCrystal configuration string `_. +Temperature, material composition and density are passed from the configuration string +and the `NCMAT file `_ +that define the material, e.g: + +:: + + mat = openmc.Material.from_ncrystal('Al_sg225.ncmat') + +defines a material containing polycrystalline alumnium, + +:: + + mat = openmc.Material.from_ncrystal(""""Ge_sg227.ncmat;dcutoff=0.5;mos=40arcsec; + dir1=@crys_hkl:5,1,1@lab:0,0,1; + dir2=@crys_hkl:0,-1,1@lab:0,1,0""") + +defines an oriented germanium single crystal with 40 arcsec mosaicity. + +NCrystal only handles low energy neutron interactions. Other interaction are +provided by standard ACE files. NCrystal_ comes with a `predefined library +https://github.com/mctools/ncrystal/wiki/Data-library`_ but more materials can +be added by creating NCMAT files or on-the-fly in the configuration string. + +.. warning:: Currently, NCrystal_ materials cannot be modified after created. + Density, temperature and composition should be defined in the + configuration string or the NCMAT file. + +.. _NCrystal: https://github.com/mctools/ncrystal + ------------------ Naming Conventions ------------------ @@ -222,3 +258,4 @@ been generated, you can tell OpenMC to use this file either by setting materials.cross_sections = '/path/to/cross_sections.xml' .. _MCNP: https://mcnp.lanl.gov/ + From cf96ddf8bbedac4d72e1730e313d5b7a9cd94284 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian <22483345+marquezj@users.noreply.github.com> Date: Thu, 17 Nov 2022 13:47:03 +0100 Subject: [PATCH 16/66] Basic introduction to NCrystal Included references to the main papers and documentation --- docs/source/methods/cross_sections.rst | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/docs/source/methods/cross_sections.rst b/docs/source/methods/cross_sections.rst index 3396d7f25..c700b3f2d 100644 --- a/docs/source/methods/cross_sections.rst +++ b/docs/source/methods/cross_sections.rst @@ -178,6 +178,24 @@ been selected. There are three methods available: section data is loaded for a single temperature and is used in the unresolved resonance and fast energy ranges. +------------------ +NCrystal materials +------------------ + +As an alternative of the standard thermal scattering treatment using :math:`S(\alpha,\beta)` +tables, OpenMC allows to create materials using NCrystal_. In addition to the regular thermal elastic, +and thermal inelastic processes, NCrystal allows the generation of models for materials that +cannot currently included in ACE files such as oriented single crystals (see the `NCrystal paper`_), +and further extend the physics `using plugins`_. Thermal scattering kernels are generated on +the fly from dynamic and structural data, or loaded from :math:`S(\alpha,\beta)` tables converted +from ENDF6 evaluations. These kernels are sampled in a direct way using a fast `rejection algorithm`_ +that does not require previous processing. A `large library` of materials is already included in +the NCrystal distribution, and new materials can be easily defined from scratch in the `NCMAT format` +or `combining existing files`. + +The compositions of the materials defined in NCrystal are passed on to OpenMC all other reactions +except for thermal neutron scattering are handled by continuous energy ACE libraries. + ---------------- Multi-Group Data ---------------- @@ -279,3 +297,10 @@ or even isotropic scattering. .. _ENDF/B data: https://www.nndc.bnl.gov/endf-b8.0/ .. _Leppanen: https://doi.org/10.1016/j.anucene.2009.03.019 .. _algorithms: http://ab-initio.mit.edu/wiki/index.php/Faddeeva_Package +.. _NCrystal: https://github.com/mctools/ncrystal +.. _NCrystal paper: https://doi.org/10.1016/j.cpc.2019.07.015 +.. _using plugins: https://doi.org/10.1016/j.cpc.2021.108082 +.. _rejection algorithm: https://doi.org/10.1016/j.jcp.2018.11.043 +.. _large library: https://github.com/mctools/ncrystal/wiki/Data-library +.. _NCMAT format: https://github.com/mctools/ncrystal/wiki/NCMAT-format +.. _combining existing files: https://github.com/mctools/ncrystal/wiki/Announcement-Release3.0.0#2-multiphase-materials From 24572a71f22b588eb628e855618a4a4a27a63e4c Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian Date: Thu, 17 Nov 2022 16:33:48 +0100 Subject: [PATCH 17/66] Added simple NCrystal test --- tests/regression_tests/ncrystal/__init__.py | 0 .../regression_tests/ncrystal/inputs_true.dat | 45 +++++ .../ncrystal/results_true.dat | 181 ++++++++++++++++++ tests/regression_tests/ncrystal/test.py | 70 +++++++ 4 files changed, 296 insertions(+) create mode 100644 tests/regression_tests/ncrystal/__init__.py create mode 100644 tests/regression_tests/ncrystal/inputs_true.dat create mode 100644 tests/regression_tests/ncrystal/results_true.dat create mode 100644 tests/regression_tests/ncrystal/test.py diff --git a/tests/regression_tests/ncrystal/__init__.py b/tests/regression_tests/ncrystal/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/regression_tests/ncrystal/inputs_true.dat b/tests/regression_tests/ncrystal/inputs_true.dat new file mode 100644 index 000000000..7a3f20eb3 --- /dev/null +++ b/tests/regression_tests/ncrystal/inputs_true.dat @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + fixed source + 100000 + 10 + + + 0 0 -20 + + + + 0.012 1.0 + + + + + + + 1 + + + 0.0 0.017453292519943295 0.03490658503988659 0.05235987755982989 0.06981317007977318 0.08726646259971647 0.10471975511965978 0.12217304763960307 0.13962634015954636 0.15707963267948966 0.17453292519943295 0.19198621771937624 0.20943951023931956 0.22689280275926285 0.24434609527920614 0.2617993877991494 0.2792526803190927 0.29670597283903605 0.3141592653589793 0.33161255787892263 0.3490658503988659 0.3665191429188092 0.3839724354387525 0.4014257279586958 0.4188790204786391 0.4363323129985824 0.4537856055185257 0.47123889803846897 0.4886921905584123 0.5061454830783556 0.5235987755982988 0.5410520681182421 0.5585053606381855 0.5759586531581288 0.5934119456780721 0.6108652381980153 0.6283185307179586 0.6457718232379019 0.6632251157578453 0.6806784082777885 0.6981317007977318 0.7155849933176751 0.7330382858376184 0.7504915783575618 0.767944870877505 0.7853981633974483 0.8028514559173916 0.8203047484373349 0.8377580409572782 0.8552113334772214 0.8726646259971648 0.8901179185171081 0.9075712110370514 0.9250245035569946 0.9424777960769379 0.9599310885968813 0.9773843811168246 0.9948376736367679 1.0122909661567112 1.0297442586766545 1.0471975511965976 1.064650843716541 1.0821041362364843 1.0995574287564276 1.117010721276371 1.1344640137963142 1.1519173063162575 1.1693705988362009 1.1868238913561442 1.2042771838760873 1.2217304763960306 1.239183768915974 1.2566370614359172 1.2740903539558606 1.2915436464758039 1.3089969389957472 1.3264502315156905 1.3439035240356338 1.361356816555577 1.3788101090755203 1.3962634015954636 1.413716694115407 1.4311699866353502 1.4486232791552935 1.4660765716752369 1.4835298641951802 1.5009831567151235 1.5184364492350666 1.53588974175501 1.5533430342749532 1.5707963267948966 1.5882496193148399 1.6057029118347832 1.6231562043547265 1.6406094968746698 1.6580627893946132 1.6755160819145565 1.6929693744344996 1.710422666954443 1.7278759594743862 1.7453292519943295 1.7627825445142729 1.7802358370342162 1.7976891295541595 1.8151424220741028 1.8325957145940461 1.8500490071139892 1.8675022996339325 1.8849555921538759 1.9024088846738192 1.9198621771937625 1.9373154697137058 1.9547687622336491 1.9722220547535925 1.9896753472735358 2.007128639793479 2.0245819323134224 2.0420352248333655 2.059488517353309 2.076941809873252 2.0943951023931953 2.111848394913139 2.129301687433082 2.1467549799530254 2.1642082724729685 2.181661564992912 2.199114857512855 2.2165681500327987 2.234021442552742 2.251474735072685 2.2689280275926285 2.2863813201125716 2.303834612632515 2.321287905152458 2.3387411976724017 2.356194490192345 2.3736477827122884 2.3911010752322315 2.4085543677521746 2.426007660272118 2.443460952792061 2.4609142453120048 2.478367537831948 2.4958208303518914 2.5132741228718345 2.530727415391778 2.548180707911721 2.5656340004316642 2.5830872929516078 2.600540585471551 2.6179938779914944 2.6354471705114375 2.652900463031381 2.670353755551324 2.6878070480712677 2.705260340591211 2.722713633111154 2.7401669256310974 2.7576202181510405 2.775073510670984 2.792526803190927 2.8099800957108707 2.827433388230814 2.8448866807507573 2.8623399732707004 2.8797932657906435 2.897246558310587 2.91469985083053 2.9321531433504737 2.949606435870417 2.9670597283903604 2.9845130209103035 3.001966313430247 3.01941960595019 3.036872898470133 3.0543261909900767 3.07177948351002 3.0892327760299634 3.1066860685499065 3.12413936106985 3.141592653589793 + + + 1 + + + 1 2 3 + current + + diff --git a/tests/regression_tests/ncrystal/results_true.dat b/tests/regression_tests/ncrystal/results_true.dat new file mode 100644 index 000000000..3ef0306fd --- /dev/null +++ b/tests/regression_tests/ncrystal/results_true.dat @@ -0,0 +1,181 @@ + surface polar low [rad] polar high [rad] cellfrom nuclide score mean std. dev. +0 1 0.00e+00 1.75e-02 1 total current 9.82e-01 1.43e-04 +1 1 1.75e-02 3.49e-02 1 total current 0.00e+00 0.00e+00 +2 1 3.49e-02 5.24e-02 1 total current 1.00e-06 1.00e-06 +3 1 5.24e-02 6.98e-02 1 total current 0.00e+00 0.00e+00 +4 1 6.98e-02 8.73e-02 1 total current 0.00e+00 0.00e+00 +5 1 8.73e-02 1.05e-01 1 total current 1.00e-06 1.00e-06 +6 1 1.05e-01 1.22e-01 1 total current 0.00e+00 0.00e+00 +7 1 1.22e-01 1.40e-01 1 total current 1.00e-06 1.00e-06 +8 1 1.40e-01 1.57e-01 1 total current 1.00e-06 1.00e-06 +9 1 1.57e-01 1.75e-01 1 total current 1.00e-06 1.00e-06 +10 1 1.75e-01 1.92e-01 1 total current 0.00e+00 0.00e+00 +11 1 1.92e-01 2.09e-01 1 total current 2.00e-06 1.33e-06 +12 1 2.09e-01 2.27e-01 1 total current 0.00e+00 0.00e+00 +13 1 2.27e-01 2.44e-01 1 total current 1.00e-06 1.00e-06 +14 1 2.44e-01 2.62e-01 1 total current 1.00e-06 1.00e-06 +15 1 2.62e-01 2.79e-01 1 total current 2.00e-06 1.33e-06 +16 1 2.79e-01 2.97e-01 1 total current 0.00e+00 0.00e+00 +17 1 2.97e-01 3.14e-01 1 total current 2.00e-06 2.00e-06 +18 1 3.14e-01 3.32e-01 1 total current 1.00e-06 1.00e-06 +19 1 3.32e-01 3.49e-01 1 total current 1.00e-06 1.00e-06 +20 1 3.49e-01 3.67e-01 1 total current 2.00e-06 1.33e-06 +21 1 3.67e-01 3.84e-01 1 total current 0.00e+00 0.00e+00 +22 1 3.84e-01 4.01e-01 1 total current 2.00e-06 1.33e-06 +23 1 4.01e-01 4.19e-01 1 total current 2.00e-06 1.33e-06 +24 1 4.19e-01 4.36e-01 1 total current 1.00e-06 1.00e-06 +25 1 4.36e-01 4.54e-01 1 total current 2.00e-06 2.00e-06 +26 1 4.54e-01 4.71e-01 1 total current 5.00e-06 2.24e-06 +27 1 4.71e-01 4.89e-01 1 total current 4.00e-06 1.63e-06 +28 1 4.89e-01 5.06e-01 1 total current 3.00e-06 1.53e-06 +29 1 5.06e-01 5.24e-01 1 total current 3.00e-06 1.53e-06 +30 1 5.24e-01 5.41e-01 1 total current 3.00e-06 1.53e-06 +31 1 5.41e-01 5.59e-01 1 total current 7.00e-06 2.13e-06 +32 1 5.59e-01 5.76e-01 1 total current 3.00e-06 1.53e-06 +33 1 5.76e-01 5.93e-01 1 total current 3.00e-06 1.53e-06 +34 1 5.93e-01 6.11e-01 1 total current 2.00e-06 1.33e-06 +35 1 6.11e-01 6.28e-01 1 total current 2.00e-06 1.33e-06 +36 1 6.28e-01 6.46e-01 1 total current 3.00e-06 2.13e-06 +37 1 6.46e-01 6.63e-01 1 total current 3.00e-06 1.53e-06 +38 1 6.63e-01 6.81e-01 1 total current 2.00e-06 1.33e-06 +39 1 6.81e-01 6.98e-01 1 total current 3.00e-06 1.53e-06 +40 1 6.98e-01 7.16e-01 1 total current 1.00e-06 1.00e-06 +41 1 7.16e-01 7.33e-01 1 total current 6.00e-06 2.67e-06 +42 1 7.33e-01 7.50e-01 1 total current 6.00e-06 2.21e-06 +43 1 7.50e-01 7.68e-01 1 total current 7.00e-06 3.35e-06 +44 1 7.68e-01 7.85e-01 1 total current 6.00e-06 2.67e-06 +45 1 7.85e-01 8.03e-01 1 total current 6.00e-06 2.21e-06 +46 1 8.03e-01 8.20e-01 1 total current 7.00e-06 2.13e-06 +47 1 8.20e-01 8.38e-01 1 total current 5.00e-06 2.24e-06 +48 1 8.38e-01 8.55e-01 1 total current 3.00e-06 1.53e-06 +49 1 8.55e-01 8.73e-01 1 total current 8.00e-06 3.27e-06 +50 1 8.73e-01 8.90e-01 1 total current 7.00e-06 2.13e-06 +51 1 8.90e-01 9.08e-01 1 total current 6.00e-06 2.21e-06 +52 1 9.08e-01 9.25e-01 1 total current 4.00e-06 2.21e-06 +53 1 9.25e-01 9.42e-01 1 total current 1.20e-05 3.27e-06 +54 1 9.42e-01 9.60e-01 1 total current 8.00e-06 3.89e-06 +55 1 9.60e-01 9.77e-01 1 total current 1.20e-05 4.42e-06 +56 1 9.77e-01 9.95e-01 1 total current 7.00e-06 3.67e-06 +57 1 9.95e-01 1.01e+00 1 total current 1.20e-05 3.89e-06 +58 1 1.01e+00 1.03e+00 1 total current 9.00e-06 2.33e-06 +59 1 1.03e+00 1.05e+00 1 total current 6.00e-06 2.67e-06 +60 1 1.05e+00 1.06e+00 1 total current 7.00e-06 2.13e-06 +61 1 1.06e+00 1.08e+00 1 total current 5.00e-06 2.24e-06 +62 1 1.08e+00 1.10e+00 1 total current 1.20e-05 2.91e-06 +63 1 1.10e+00 1.12e+00 1 total current 1.30e-05 3.35e-06 +64 1 1.12e+00 1.13e+00 1 total current 9.00e-06 3.14e-06 +65 1 1.13e+00 1.15e+00 1 total current 1.20e-05 3.89e-06 +66 1 1.15e+00 1.17e+00 1 total current 6.00e-06 2.21e-06 +67 1 1.17e+00 1.19e+00 1 total current 5.11e-03 4.20e-05 +68 1 1.19e+00 1.20e+00 1 total current 8.00e-06 3.27e-06 +69 1 1.20e+00 1.22e+00 1 total current 1.30e-05 4.48e-06 +70 1 1.22e+00 1.24e+00 1 total current 1.20e-05 3.89e-06 +71 1 1.24e+00 1.26e+00 1 total current 1.50e-05 4.28e-06 +72 1 1.26e+00 1.27e+00 1 total current 7.00e-06 3.00e-06 +73 1 1.27e+00 1.29e+00 1 total current 1.60e-05 3.71e-06 +74 1 1.29e+00 1.31e+00 1 total current 9.00e-06 3.79e-06 +75 1 1.31e+00 1.33e+00 1 total current 1.30e-05 2.60e-06 +76 1 1.33e+00 1.34e+00 1 total current 1.60e-05 3.06e-06 +77 1 1.34e+00 1.36e+00 1 total current 1.40e-05 2.67e-06 +78 1 1.36e+00 1.38e+00 1 total current 1.40e-05 6.86e-06 +79 1 1.38e+00 1.40e+00 1 total current 1.50e-05 4.28e-06 +80 1 1.40e+00 1.41e+00 1 total current 3.26e-03 6.93e-05 +81 1 1.41e+00 1.43e+00 1 total current 1.40e-05 3.71e-06 +82 1 1.43e+00 1.45e+00 1 total current 1.30e-05 3.00e-06 +83 1 1.45e+00 1.47e+00 1 total current 1.30e-05 3.67e-06 +84 1 1.47e+00 1.48e+00 1 total current 1.70e-05 5.59e-06 +85 1 1.48e+00 1.50e+00 1 total current 1.70e-05 3.67e-06 +86 1 1.50e+00 1.52e+00 1 total current 1.40e-05 3.40e-06 +87 1 1.52e+00 1.54e+00 1 total current 2.50e-05 4.53e-06 +88 1 1.54e+00 1.55e+00 1 total current 1.30e-05 5.39e-06 +89 1 1.55e+00 1.57e+00 1 total current 1.80e-05 4.67e-06 +90 1 1.57e+00 1.59e+00 1 total current 1.40e-05 4.52e-06 +91 1 1.59e+00 1.61e+00 1 total current 1.70e-05 4.23e-06 +92 1 1.61e+00 1.62e+00 1 total current 1.40e-05 3.71e-06 +93 1 1.62e+00 1.64e+00 1 total current 1.00e-05 2.11e-06 +94 1 1.64e+00 1.66e+00 1 total current 2.00e-05 4.22e-06 +95 1 1.66e+00 1.68e+00 1 total current 2.30e-05 5.59e-06 +96 1 1.68e+00 1.69e+00 1 total current 1.70e-05 6.51e-06 +97 1 1.69e+00 1.71e+00 1 total current 1.30e-05 3.00e-06 +98 1 1.71e+00 1.73e+00 1 total current 1.50e-05 4.01e-06 +99 1 1.73e+00 1.75e+00 1 total current 1.70e-05 3.96e-06 +100 1 1.75e+00 1.76e+00 1 total current 1.80e-05 5.12e-06 +101 1 1.76e+00 1.78e+00 1 total current 2.50e-05 6.54e-06 +102 1 1.78e+00 1.80e+00 1 total current 1.80e-05 3.59e-06 +103 1 1.80e+00 1.82e+00 1 total current 1.50e-05 4.01e-06 +104 1 1.82e+00 1.83e+00 1 total current 1.10e-05 4.07e-06 +105 1 1.83e+00 1.85e+00 1 total current 1.50e-05 4.01e-06 +106 1 1.85e+00 1.87e+00 1 total current 1.90e-05 4.82e-06 +107 1 1.87e+00 1.88e+00 1 total current 2.20e-05 3.89e-06 +108 1 1.88e+00 1.90e+00 1 total current 2.10e-05 3.79e-06 +109 1 1.90e+00 1.92e+00 1 total current 1.50e-05 3.42e-06 +110 1 1.92e+00 1.94e+00 1 total current 2.20e-05 5.12e-06 +111 1 1.94e+00 1.95e+00 1 total current 2.10e-05 5.86e-06 +112 1 1.95e+00 1.97e+00 1 total current 2.60e-05 4.27e-06 +113 1 1.97e+00 1.99e+00 1 total current 2.20e-05 4.16e-06 +114 1 1.99e+00 2.01e+00 1 total current 2.40e-05 5.42e-06 +115 1 2.01e+00 2.02e+00 1 total current 1.60e-05 4.52e-06 +116 1 2.02e+00 2.04e+00 1 total current 1.30e-05 3.35e-06 +117 1 2.04e+00 2.06e+00 1 total current 1.90e-05 4.07e-06 +118 1 2.06e+00 2.08e+00 1 total current 1.30e-05 3.00e-06 +119 1 2.08e+00 2.09e+00 1 total current 1.40e-05 4.00e-06 +120 1 2.09e+00 2.11e+00 1 total current 3.10e-05 5.47e-06 +121 1 2.11e+00 2.13e+00 1 total current 2.30e-05 5.39e-06 +122 1 2.13e+00 2.15e+00 1 total current 2.20e-05 4.67e-06 +123 1 2.15e+00 2.16e+00 1 total current 1.70e-05 4.48e-06 +124 1 2.16e+00 2.18e+00 1 total current 1.60e-05 4.00e-06 +125 1 2.18e+00 2.20e+00 1 total current 1.80e-05 4.16e-06 +126 1 2.20e+00 2.22e+00 1 total current 1.80e-05 4.42e-06 +127 1 2.22e+00 2.23e+00 1 total current 1.80e-05 5.54e-06 +128 1 2.23e+00 2.25e+00 1 total current 1.90e-05 4.33e-06 +129 1 2.25e+00 2.27e+00 1 total current 1.00e-05 3.33e-06 +130 1 2.27e+00 2.29e+00 1 total current 1.80e-05 4.42e-06 +131 1 2.29e+00 2.30e+00 1 total current 4.15e-03 5.40e-05 +132 1 2.30e+00 2.32e+00 1 total current 1.90e-05 2.33e-06 +133 1 2.32e+00 2.34e+00 1 total current 2.30e-05 3.96e-06 +134 1 2.34e+00 2.36e+00 1 total current 1.90e-05 3.48e-06 +135 1 2.36e+00 2.37e+00 1 total current 1.60e-05 3.71e-06 +136 1 2.37e+00 2.39e+00 1 total current 1.60e-05 4.27e-06 +137 1 2.39e+00 2.41e+00 1 total current 2.30e-05 4.48e-06 +138 1 2.41e+00 2.43e+00 1 total current 2.10e-05 3.48e-06 +139 1 2.43e+00 2.44e+00 1 total current 1.60e-05 2.21e-06 +140 1 2.44e+00 2.46e+00 1 total current 9.00e-06 2.77e-06 +141 1 2.46e+00 2.48e+00 1 total current 1.30e-05 3.00e-06 +142 1 2.48e+00 2.50e+00 1 total current 2.70e-05 3.67e-06 +143 1 2.50e+00 2.51e+00 1 total current 1.90e-05 4.07e-06 +144 1 2.51e+00 2.53e+00 1 total current 1.20e-05 4.16e-06 +145 1 2.53e+00 2.55e+00 1 total current 1.30e-05 2.13e-06 +146 1 2.55e+00 2.57e+00 1 total current 1.10e-05 2.33e-06 +147 1 2.57e+00 2.58e+00 1 total current 1.50e-05 3.07e-06 +148 1 2.58e+00 2.60e+00 1 total current 1.20e-05 2.49e-06 +149 1 2.60e+00 2.62e+00 1 total current 1.80e-05 5.54e-06 +150 1 2.62e+00 2.64e+00 1 total current 1.30e-05 3.67e-06 +151 1 2.64e+00 2.65e+00 1 total current 1.60e-05 3.40e-06 +152 1 2.65e+00 2.67e+00 1 total current 7.00e-06 3.35e-06 +153 1 2.67e+00 2.69e+00 1 total current 1.00e-05 2.98e-06 +154 1 2.69e+00 2.71e+00 1 total current 7.00e-06 3.35e-06 +155 1 2.71e+00 2.72e+00 1 total current 1.20e-05 2.91e-06 +156 1 2.72e+00 2.74e+00 1 total current 9.00e-06 2.33e-06 +157 1 2.74e+00 2.76e+00 1 total current 1.00e-05 3.33e-06 +158 1 2.76e+00 2.78e+00 1 total current 1.10e-05 3.14e-06 +159 1 2.78e+00 2.79e+00 1 total current 1.00e-05 3.33e-06 +160 1 2.79e+00 2.81e+00 1 total current 1.40e-05 4.76e-06 +161 1 2.81e+00 2.83e+00 1 total current 8.00e-06 2.91e-06 +162 1 2.83e+00 2.84e+00 1 total current 5.00e-06 2.69e-06 +163 1 2.84e+00 2.86e+00 1 total current 6.00e-06 2.21e-06 +164 1 2.86e+00 2.88e+00 1 total current 5.00e-06 1.67e-06 +165 1 2.88e+00 2.90e+00 1 total current 4.00e-06 2.21e-06 +166 1 2.90e+00 2.91e+00 1 total current 7.00e-06 2.13e-06 +167 1 2.91e+00 2.93e+00 1 total current 6.00e-06 2.67e-06 +168 1 2.93e+00 2.95e+00 1 total current 7.00e-06 2.13e-06 +169 1 2.95e+00 2.97e+00 1 total current 5.00e-06 1.67e-06 +170 1 2.97e+00 2.98e+00 1 total current 3.00e-06 1.53e-06 +171 1 2.98e+00 3.00e+00 1 total current 6.00e-06 2.21e-06 +172 1 3.00e+00 3.02e+00 1 total current 3.00e-06 1.53e-06 +173 1 3.02e+00 3.04e+00 1 total current 1.00e-05 2.98e-06 +174 1 3.04e+00 3.05e+00 1 total current 2.00e-06 1.33e-06 +175 1 3.05e+00 3.07e+00 1 total current 1.00e-06 1.00e-06 +176 1 3.07e+00 3.09e+00 1 total current 2.00e-06 1.33e-06 +177 1 3.09e+00 3.11e+00 1 total current 0.00e+00 0.00e+00 +178 1 3.11e+00 3.12e+00 1 total current 1.00e-06 1.00e-06 +179 1 3.12e+00 3.14e+00 1 total current 0.00e+00 0.00e+00 \ No newline at end of file diff --git a/tests/regression_tests/ncrystal/test.py b/tests/regression_tests/ncrystal/test.py new file mode 100644 index 000000000..51a0f07b7 --- /dev/null +++ b/tests/regression_tests/ncrystal/test.py @@ -0,0 +1,70 @@ +import numpy as np +import openmc +from tests.testing_harness import PyAPITestHarness + +def compute_angular_distribution(cfg, E0, N): + """Return a openmc.model.Model() object for a monoenergetic pencil + beam hitting a 1 mm sphere filled with the material defined by + the cfg string, and compute the angular distribution""" + + # Material definition + + m1 = openmc.Material.from_ncrystal(cfg) + materials = openmc.Materials([m1]) + + # Geometry definition + + sample_sphere = openmc.Sphere(r=0.1) + outer_sphere = openmc.Sphere(r=100, boundary_type="vacuum") + cell1 = openmc.Cell(region= -sample_sphere, fill=m1) + cell2_region= +sample_sphere&-outer_sphere + cell2 = openmc.Cell(region= cell2_region, fill=None) + uni1 = openmc.Universe(cells=[cell1, cell2]) + geometry = openmc.Geometry(uni1) + + # Source definition + + source = openmc.Source() + source.space = openmc.stats.Point(xyz = (0,0,-20)) + source.angle = openmc.stats.Monodirectional(reference_uvw = (0,0,1)) + source.energy = openmc.stats.Discrete([E0], [1.0]) + + # Execution settings + + settings = openmc.Settings() + settings.source = source + settings.run_mode = "fixed source" + settings.batches = 10 + settings.particles = N + + # Tally definition + + tally1 = openmc.Tally(name="angular distribution") + tally1.scores = ["current"] + filter1 = openmc.filter.SurfaceFilter(sample_sphere) + filter2 = openmc.filter.PolarFilter(np.linspace(0,np.pi,180+1)) + filter3 = openmc.filter.CellFromFilter(cell1) + tally1.filters = [filter1, filter2, filter3] + tallies = openmc.Tallies([tally1]) + + return openmc.model.Model(geometry, materials, settings, tallies) + + +class NCrystalTest(PyAPITestHarness): + def _get_results(self): + """Digest info in the statepoint and return as a string.""" + + # Read the statepoint file. + sp = openmc.StatePoint(self._sp_name) + tal = sp.get_tally(name='angular distribution') + df = tal.get_pandas_dataframe() + return df.to_string() + +def test_ncrystal(): + NParticles = 100000 + T = 293.6 # K + E0 = 0.012 # eV + cfg = 'Al_sg225.ncmat' + test = compute_angular_distribution(cfg, E0, NParticles) + harness = NCrystalTest('statepoint.10.h5', model=test) + harness.main() From 54745f507641489bc82acbfb4303a728c7bf4fca Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian <22483345+marquezj@users.noreply.github.com> Date: Fri, 18 Nov 2022 11:11:37 -0300 Subject: [PATCH 18/66] Changed example NCrystal cfg strings --- docs/source/usersguide/materials.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/usersguide/materials.rst b/docs/source/usersguide/materials.rst index ab5a8c5af..1886a772b 100644 --- a/docs/source/usersguide/materials.rst +++ b/docs/source/usersguide/materials.rst @@ -114,13 +114,13 @@ that define the material, e.g: :: - mat = openmc.Material.from_ncrystal('Al_sg225.ncmat') + mat = openmc.Material.from_ncrystal('Al_sg225.ncmat;temp=300K') defines a material containing polycrystalline alumnium, :: - mat = openmc.Material.from_ncrystal(""""Ge_sg227.ncmat;dcutoff=0.5;mos=40arcsec; + mat = openmc.Material.from_ncrystal("""Ge_sg227.ncmat;dcutoff=0.5;mos=40arcsec; dir1=@crys_hkl:5,1,1@lab:0,0,1; dir2=@crys_hkl:0,-1,1@lab:0,1,0""") From 91f939a391032db72220e4be715b888d0f8db287 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian Date: Fri, 18 Nov 2022 18:26:31 +0100 Subject: [PATCH 19/66] Add flag for NCrystal --- include/openmc/settings.h | 2 ++ openmc/lib/__init__.py | 3 +++ src/settings.cpp | 7 +++++++ tests/regression_tests/ncrystal/test.py | 7 +++++++ 4 files changed, 19 insertions(+) diff --git a/include/openmc/settings.h b/include/openmc/settings.h index 9bb447005..b01c75d45 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -20,6 +20,8 @@ namespace openmc { // Global variable declarations //============================================================================== +extern "C" const bool NCRYSTAL_ENABLED; + namespace settings { // Boolean flags diff --git a/openmc/lib/__init__.py b/openmc/lib/__init__.py index c14b0d9c2..55e2ec11b 100644 --- a/openmc/lib/__init__.py +++ b/openmc/lib/__init__.py @@ -42,6 +42,9 @@ else: def _dagmc_enabled(): return c_bool.in_dll(_dll, "DAGMC_ENABLED").value +def _ncrystal_enabled(): + return c_bool.in_dll(_dll, "NCRYSTAL_ENABLED").value + def _coord_levels(): return c_int.in_dll(_dll, "n_coord_levels").value diff --git a/src/settings.cpp b/src/settings.cpp index 87c24eedd..be7228d68 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -36,6 +36,13 @@ namespace openmc { // Global variables //============================================================================== +#ifdef NCRYSTAL +const bool NCRYSTAL_ENABLED = true; +#else +const bool NCRYSTAL_ENABLED = false; +#endif + + namespace settings { // Default values for boolean flags diff --git a/tests/regression_tests/ncrystal/test.py b/tests/regression_tests/ncrystal/test.py index 51a0f07b7..0a47d634d 100644 --- a/tests/regression_tests/ncrystal/test.py +++ b/tests/regression_tests/ncrystal/test.py @@ -1,7 +1,14 @@ import numpy as np import openmc +import openmc.lib +import pytest + from tests.testing_harness import PyAPITestHarness +pytestmark = pytest.mark.skipif( + not openmc.lib._ncrystal_enabled(), + reason="NCrystal materials are not enabled.") + def compute_angular_distribution(cfg, E0, N): """Return a openmc.model.Model() object for a monoenergetic pencil beam hitting a 1 mm sphere filled with the material defined by From 5f8e64b463aa2e0509d2b9955dc7de6686d5497b Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian <22483345+marquezj@users.noreply.github.com> Date: Fri, 18 Nov 2022 17:47:21 -0300 Subject: [PATCH 20/66] Add NCrystal vairables to ci.yml --- .github/workflows/ci.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e267c0d8e..321f18874 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,6 +29,7 @@ jobs: mpi: [n, y] omp: [n, y] dagmc: [n] + ncrystal: [n] libmesh: [n] event: [n] vectfit: [n] @@ -47,6 +48,10 @@ jobs: python-version: '3.10' mpi: y omp: y + - ncrystal: y + python-version: '3.10' + mpi: n + omp: n - libmesh: y python-version: '3.10' mpi: y @@ -64,7 +69,7 @@ jobs: omp: n mpi: y name: "Python ${{ matrix.python-version }} (omp=${{ matrix.omp }}, - mpi=${{ matrix.mpi }}, dagmc=${{ matrix.dagmc }}, + mpi=${{ matrix.mpi }}, dagmc=${{ matrix.dagmc }}, ncrystal=${{ matrix.ncrystal }}, libmesh=${{ matrix.libmesh }}, event=${{ matrix.event }} vectfit=${{ matrix.vectfit }})" @@ -73,6 +78,7 @@ jobs: PHDF5: ${{ matrix.mpi }} OMP: ${{ matrix.omp }} DAGMC: ${{ matrix.dagmc }} + NCRYSTAL: ${{ matrix.ncrystal }} EVENT: ${{ matrix.event }} VECTFIT: ${{ matrix.vectfit }} LIBMESH: ${{ matrix.libmesh }} From 87ed26babee2c7b69c229529e146d335a90fd952 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian <22483345+marquezj@users.noreply.github.com> Date: Fri, 18 Nov 2022 17:48:19 -0300 Subject: [PATCH 21/66] Add option to install NCrystal --- tools/ci/gha-install.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/ci/gha-install.sh b/tools/ci/gha-install.sh index aa40eb90b..e5390be1e 100755 --- a/tools/ci/gha-install.sh +++ b/tools/ci/gha-install.sh @@ -17,6 +17,11 @@ if [[ $DAGMC = 'y' ]]; then ./tools/ci/gha-install-dagmc.sh fi +# Install NCrystal if needed +if [[ $NCRYSTAL = 'y' ]]; then + ./tools/ci/gha-install-ncrystal.sh +fi + # Install vectfit for WMP generation if needed if [[ $VECTFIT = 'y' ]]; then ./tools/ci/gha-install-vectfit.sh From 8cf9dbe320baf4ff98eb57979f6e249e69ee0424 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian Date: Fri, 18 Nov 2022 22:28:04 +0100 Subject: [PATCH 22/66] Fixed in NCrystal docs --- docs/source/methods/cross_sections.rst | 4 ++-- docs/source/usersguide/materials.rst | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/source/methods/cross_sections.rst b/docs/source/methods/cross_sections.rst index c700b3f2d..64e4fac3d 100644 --- a/docs/source/methods/cross_sections.rst +++ b/docs/source/methods/cross_sections.rst @@ -190,8 +190,8 @@ and further extend the physics `using plugins`_. Thermal scattering kernels are the fly from dynamic and structural data, or loaded from :math:`S(\alpha,\beta)` tables converted from ENDF6 evaluations. These kernels are sampled in a direct way using a fast `rejection algorithm`_ that does not require previous processing. A `large library` of materials is already included in -the NCrystal distribution, and new materials can be easily defined from scratch in the `NCMAT format` -or `combining existing files`. +the NCrystal distribution, and new materials can be easily defined from scratch in the `NCMAT format`_ +or `combining existing files`_. The compositions of the materials defined in NCrystal are passed on to OpenMC all other reactions except for thermal neutron scattering are handled by continuous energy ACE libraries. diff --git a/docs/source/usersguide/materials.rst b/docs/source/usersguide/materials.rst index 1886a772b..1eaae846c 100644 --- a/docs/source/usersguide/materials.rst +++ b/docs/source/usersguide/materials.rst @@ -127,8 +127,8 @@ defines a material containing polycrystalline alumnium, defines an oriented germanium single crystal with 40 arcsec mosaicity. NCrystal only handles low energy neutron interactions. Other interaction are -provided by standard ACE files. NCrystal_ comes with a `predefined library -https://github.com/mctools/ncrystal/wiki/Data-library`_ but more materials can +provided by standard ACE files. NCrystal_ comes with a `predefined library +`_ but more materials can be added by creating NCMAT files or on-the-fly in the configuration string. .. warning:: Currently, NCrystal_ materials cannot be modified after created. From b9e8f1324e72dc4730185b5f1f350eea62ab6757 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian Date: Fri, 18 Nov 2022 22:40:46 +0100 Subject: [PATCH 23/66] Attempt to fix NCrystal installation script --- tools/ci/gha-install-ncrystal.sh | 4 ++++ tools/ci/gha-install.py | 8 ++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/tools/ci/gha-install-ncrystal.sh b/tools/ci/gha-install-ncrystal.sh index c18087f23..5f577b8da 100755 --- a/tools/ci/gha-install-ncrystal.sh +++ b/tools/ci/gha-install-ncrystal.sh @@ -28,6 +28,7 @@ cmake \ -DEMBED_DATA=ON \ -DINSTALL_DATA=OFF \ -DNO_DIRECT_PYMODINST=ON \ + -DCMAKE_INSTALL_PREFIX="${INST_DIR}" \ -DPython3_EXECUTABLE="$PYTHON" make -j${CPU_COUNT:-1} @@ -60,3 +61,6 @@ liblocation = pathlib.Path('${TMPNCRYSTALLIBLOCATION}'.strip()) EOF $PYTHON -m pip install ./ncrystal_pypkg/ -vv + +setup ${INST_DIR}/setup.sh + diff --git a/tools/ci/gha-install.py b/tools/ci/gha-install.py index 4c69ba70b..f3a29273f 100644 --- a/tools/ci/gha-install.py +++ b/tools/ci/gha-install.py @@ -19,7 +19,7 @@ def which(program): return None -def install(omp=False, mpi=False, phdf5=False, dagmc=False, libmesh=False): +def install(omp=False, mpi=False, phdf5=False, dagmc=False, libmesh=False, ncrystal=False): # Create build directory and change to it shutil.rmtree('build', ignore_errors=True) os.mkdir('build') @@ -54,6 +54,9 @@ def install(omp=False, mpi=False, phdf5=False, dagmc=False, libmesh=False): libmesh_path = os.environ.get('HOME') + '/LIBMESH' cmake_cmd.append('-DCMAKE_PREFIX_PATH=' + libmesh_path) + if ncrystal: + cmake_cmd.append('-DOPENMC_USE_NCRYSTAL=ON') + # Build in coverage mode for coverage testing cmake_cmd.append('-DOPENMC_ENABLE_COVERAGE=on') @@ -70,10 +73,11 @@ def main(): mpi = (os.environ.get('MPI') == 'y') phdf5 = (os.environ.get('PHDF5') == 'y') dagmc = (os.environ.get('DAGMC') == 'y') + ncrystal = (os.environ.get('NCRYSTAL') == 'y') libmesh = (os.environ.get('LIBMESH') == 'y') # Build and install - install(omp, mpi, phdf5, dagmc, libmesh) + install(omp, mpi, phdf5, dagmc, libmesh, ncrystal) if __name__ == '__main__': main() From d7ab491f473e87f5865e365bd5f9fbcfcd24943e Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian <22483345+marquezj@users.noreply.github.com> Date: Sat, 19 Nov 2022 08:57:00 +0100 Subject: [PATCH 24/66] Try fix of NCrystal install --- tools/ci/gha-install-ncrystal.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/ci/gha-install-ncrystal.sh b/tools/ci/gha-install-ncrystal.sh index 5f577b8da..78a1a44f2 100755 --- a/tools/ci/gha-install-ncrystal.sh +++ b/tools/ci/gha-install-ncrystal.sh @@ -45,7 +45,7 @@ make install cat < ./findncrystallib.py import pathlib libname = pathlib.Path('./cfg_ncrystal_libname.txt').read_text().strip() -libs = list(pathlib.Path("/usr").glob("**/lib*/**/%s"%libname)) +libs = list(pathlib.Path("${INST_DIR}").glob("**/lib*/**/%s"%libname)) assert len(libs)==1 pathlib.Path('ncrystal_liblocation.txt').write_text(str(libs[0])) EOF From b199db9cb9c6de05117c2749c21ce5ea3d474f7d Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian <22483345+marquezj@users.noreply.github.com> Date: Sat, 19 Nov 2022 09:06:42 +0100 Subject: [PATCH 25/66] Update gha-install-ncrystal.sh --- tools/ci/gha-install-ncrystal.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/ci/gha-install-ncrystal.sh b/tools/ci/gha-install-ncrystal.sh index 78a1a44f2..715e4da94 100755 --- a/tools/ci/gha-install-ncrystal.sh +++ b/tools/ci/gha-install-ncrystal.sh @@ -62,5 +62,5 @@ EOF $PYTHON -m pip install ./ncrystal_pypkg/ -vv -setup ${INST_DIR}/setup.sh +source ${INST_DIR}/setup.sh From 39d0c5cd61224b2a80b8c6ee75597588e132842b Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian <22483345+marquezj@users.noreply.github.com> Date: Sat, 19 Nov 2022 09:38:34 +0100 Subject: [PATCH 26/66] Try to initialize with setup.sh --- tools/ci/gha-install-ncrystal.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/ci/gha-install-ncrystal.sh b/tools/ci/gha-install-ncrystal.sh index 715e4da94..8e75cc60a 100755 --- a/tools/ci/gha-install-ncrystal.sh +++ b/tools/ci/gha-install-ncrystal.sh @@ -24,7 +24,7 @@ cmake \ -DMODIFY_RPATH=OFF \ -DCMAKE_BUILD_TYPE=Release \ -DBUILD_EXAMPLES=OFF \ - -DINSTALL_SETUPSH=OFF \ + -DINSTALL_SETUPSH=ON \ -DEMBED_DATA=ON \ -DINSTALL_DATA=OFF \ -DNO_DIRECT_PYMODINST=ON \ From 9e1abb328429072d9349fd5c01ae6249c438d187 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian <22483345+marquezj@users.noreply.github.com> Date: Sat, 19 Nov 2022 10:47:26 +0100 Subject: [PATCH 27/66] Update gha-install-ncrystal.sh --- tools/ci/gha-install-ncrystal.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/ci/gha-install-ncrystal.sh b/tools/ci/gha-install-ncrystal.sh index 8e75cc60a..c080c16d8 100755 --- a/tools/ci/gha-install-ncrystal.sh +++ b/tools/ci/gha-install-ncrystal.sh @@ -24,7 +24,7 @@ cmake \ -DMODIFY_RPATH=OFF \ -DCMAKE_BUILD_TYPE=Release \ -DBUILD_EXAMPLES=OFF \ - -DINSTALL_SETUPSH=ON \ + -DINSTALL_SETUPSH=OFF \ -DEMBED_DATA=ON \ -DINSTALL_DATA=OFF \ -DNO_DIRECT_PYMODINST=ON \ @@ -62,5 +62,5 @@ EOF $PYTHON -m pip install ./ncrystal_pypkg/ -vv -source ${INST_DIR}/setup.sh +# source ${INST_DIR}/setup.sh From 3e6bb9d5f669d2729fa96acdb4894ed1d338f2c5 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian <22483345+marquezj@users.noreply.github.com> Date: Mon, 28 Nov 2022 08:51:23 -0300 Subject: [PATCH 28/66] Upgrade installation script to NCrystal 3.5.0 --- tools/ci/gha-install-ncrystal.sh | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/tools/ci/gha-install-ncrystal.sh b/tools/ci/gha-install-ncrystal.sh index c080c16d8..18472dfa6 100755 --- a/tools/ci/gha-install-ncrystal.sh +++ b/tools/ci/gha-install-ncrystal.sh @@ -21,13 +21,12 @@ cmake \ "${SRC_DIR}" \ -DBUILD_SHARED_LIBS=ON \ -DNCRYSTAL_NOTOUCH_CMAKE_BUILD_TYPE=ON \ - -DMODIFY_RPATH=OFF \ + -DNCRYSTAL_MODIFY_RPATH=OFF \ -DCMAKE_BUILD_TYPE=Release \ - -DBUILD_EXAMPLES=OFF \ - -DINSTALL_SETUPSH=OFF \ - -DEMBED_DATA=ON \ - -DINSTALL_DATA=OFF \ - -DNO_DIRECT_PYMODINST=ON \ + -DNCRYSTAL_ENABLE_EXAMPLES=OFF \ + -DNCRYSTAL_ENABLE_SETUPSH=OFF \ + -DNCRYSTAL_ENABLE_DATA=OFF \ + -DNCRYSTAL_SKIP_PYMODINST=ON \ -DCMAKE_INSTALL_PREFIX="${INST_DIR}" \ -DPython3_EXECUTABLE="$PYTHON" @@ -62,5 +61,5 @@ EOF $PYTHON -m pip install ./ncrystal_pypkg/ -vv -# source ${INST_DIR}/setup.sh +eval $( "${INST_DIR}/bin/ncrystal-config --setup" ) From be330a11b6798850ddaa54635167c89030bfb511 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian <22483345+marquezj@users.noreply.github.com> Date: Mon, 28 Nov 2022 09:15:45 -0300 Subject: [PATCH 29/66] Update gha-install-ncrystal.sh --- tools/ci/gha-install-ncrystal.sh | 31 ++++--------------------------- 1 file changed, 4 insertions(+), 27 deletions(-) diff --git a/tools/ci/gha-install-ncrystal.sh b/tools/ci/gha-install-ncrystal.sh index 18472dfa6..1bcc89627 100755 --- a/tools/ci/gha-install-ncrystal.sh +++ b/tools/ci/gha-install-ncrystal.sh @@ -25,8 +25,7 @@ cmake \ -DCMAKE_BUILD_TYPE=Release \ -DNCRYSTAL_ENABLE_EXAMPLES=OFF \ -DNCRYSTAL_ENABLE_SETUPSH=OFF \ - -DNCRYSTAL_ENABLE_DATA=OFF \ - -DNCRYSTAL_SKIP_PYMODINST=ON \ + -DNCRYSTAL_ENABLE_DATA=EMBED \ -DCMAKE_INSTALL_PREFIX="${INST_DIR}" \ -DPython3_EXECUTABLE="$PYTHON" @@ -36,30 +35,8 @@ make install #Note: There is no "make test" or "make ctest" functionality for NCrystal # yet. If it appears in the future, we should add it here. - -#The next stuff is not pretty, but it is needed as a temporary -#workaround until NCrystal add better support for system-wide -#installations in CMake: - -cat < ./findncrystallib.py -import pathlib -libname = pathlib.Path('./cfg_ncrystal_libname.txt').read_text().strip() -libs = list(pathlib.Path("${INST_DIR}").glob("**/lib*/**/%s"%libname)) -assert len(libs)==1 -pathlib.Path('ncrystal_liblocation.txt').write_text(str(libs[0])) -EOF - -python3 ./findncrystallib.py - -TMPNCRYSTALLIBLOCATION=$(cat ./ncrystal_liblocation.txt) - -cat < ./ncrystal_pypkg/NCrystal/_nclibpath.py -#File autogenerated for working with systemwide install of NCrystal: -import pathlib -liblocation = pathlib.Path('${TMPNCRYSTALLIBLOCATION}'.strip()) -EOF - -$PYTHON -m pip install ./ncrystal_pypkg/ -vv - eval $( "${INST_DIR}/bin/ncrystal-config --setup" ) +# Check installation worked + +ncrystal-config --setup From fff545ac79d39b6aad0ee4dd055ac1da38cc344a Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian <22483345+marquezj@users.noreply.github.com> Date: Mon, 28 Nov 2022 09:27:12 -0300 Subject: [PATCH 30/66] Update gha-install-ncrystal.sh --- tools/ci/gha-install-ncrystal.sh | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tools/ci/gha-install-ncrystal.sh b/tools/ci/gha-install-ncrystal.sh index 1bcc89627..390b7dba5 100755 --- a/tools/ci/gha-install-ncrystal.sh +++ b/tools/ci/gha-install-ncrystal.sh @@ -35,8 +35,14 @@ make install #Note: There is no "make test" or "make ctest" functionality for NCrystal # yet. If it appears in the future, we should add it here. -eval $( "${INST_DIR}/bin/ncrystal-config --setup" ) +# Output the configuration to the log + +"${INST_DIR}/bin/ncrystal-config" --setup + +# Change environmental variables + +eval $( "${INST_DIR}/bin/ncrystal-config" --setup ) # Check installation worked -ncrystal-config --setup +nctool --test From 8598ff7e4311c80c3dc7993a34eb6fc40976d7ed Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian <22483345+marquezj@users.noreply.github.com> Date: Mon, 28 Nov 2022 09:45:27 -0300 Subject: [PATCH 31/66] Pass CMAKE_PREFIX_PATH from NCrystall installation --- tools/ci/gha-install.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/ci/gha-install.py b/tools/ci/gha-install.py index f3a29273f..95fe70abe 100644 --- a/tools/ci/gha-install.py +++ b/tools/ci/gha-install.py @@ -56,6 +56,8 @@ def install(omp=False, mpi=False, phdf5=False, dagmc=False, libmesh=False, ncrys if ncrystal: cmake_cmd.append('-DOPENMC_USE_NCRYSTAL=ON') + ncrystal_path = os.environ.get('CMAKE_PREFIX_PATH') + cmake_cmd.append('-DCMAKE_PREFIX_PATH=' + ncrystal_path) # Build in coverage mode for coverage testing cmake_cmd.append('-DOPENMC_ENABLE_COVERAGE=on') From c98a12171010e00fb78572c72f7327b0de740718 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian <22483345+marquezj@users.noreply.github.com> Date: Mon, 28 Nov 2022 09:56:10 -0300 Subject: [PATCH 32/66] Update gha-install.py --- tools/ci/gha-install.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/ci/gha-install.py b/tools/ci/gha-install.py index 95fe70abe..f5779c935 100644 --- a/tools/ci/gha-install.py +++ b/tools/ci/gha-install.py @@ -56,8 +56,8 @@ def install(omp=False, mpi=False, phdf5=False, dagmc=False, libmesh=False, ncrys if ncrystal: cmake_cmd.append('-DOPENMC_USE_NCRYSTAL=ON') - ncrystal_path = os.environ.get('CMAKE_PREFIX_PATH') - cmake_cmd.append('-DCMAKE_PREFIX_PATH=' + ncrystal_path) + ncrystal_cmake_path = os.environ.get('HOME')+'/ncrystal_inst/lib/cmake' + cmake_cmd.append('-DCMAKE_PREFIX_PATH=' + ncrystal_cmake_path) # Build in coverage mode for coverage testing cmake_cmd.append('-DOPENMC_ENABLE_COVERAGE=on') From 19bf624b34806efcffc52a8f18ad915624b8811b Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian <22483345+marquezj@users.noreply.github.com> Date: Mon, 28 Nov 2022 11:03:41 -0300 Subject: [PATCH 33/66] Add NCrystal installation test --- tools/ci/gha-script.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/ci/gha-script.sh b/tools/ci/gha-script.sh index c791167e9..4c125b529 100755 --- a/tools/ci/gha-script.sh +++ b/tools/ci/gha-script.sh @@ -14,5 +14,10 @@ if [[ $EVENT == 'y' ]]; then args="${args} --event " fi +# Check NCrystal installation +if [[ $NCRYSTAL = 'y' ]]; then + nctool --test +fi + # Run regression and unit tests pytest --cov=openmc -v $args tests From 4391d601fde4f74f91d594238e98564632c695b8 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian <22483345+marquezj@users.noreply.github.com> Date: Mon, 28 Nov 2022 11:06:06 -0300 Subject: [PATCH 34/66] find NCrystal --- cmake/OpenMCConfig.cmake.in | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cmake/OpenMCConfig.cmake.in b/cmake/OpenMCConfig.cmake.in index d0e2beb82..162e3aad7 100644 --- a/cmake/OpenMCConfig.cmake.in +++ b/cmake/OpenMCConfig.cmake.in @@ -9,6 +9,11 @@ if(@OPENMC_USE_DAGMC@) find_package(DAGMC REQUIRED HINTS @DAGMC_DIR@) endif() +if(@OPENMC_USE_NCRYSTAL@) + find_package(NCrystal REQUIRED) + message(STATUS "Found NCrystal: ${NCrystal_DIR} (version ${NCrystal_VERSION})") +endif() + if(@OPENMC_USE_LIBMESH@) include(FindPkgConfig) list(APPEND CMAKE_PREFIX_PATH @LIBMESH_PREFIX@) From e626447a4e8dbce5ff8211b0ada661c329e3cd3d Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian <22483345+marquezj@users.noreply.github.com> Date: Mon, 28 Nov 2022 11:18:32 -0300 Subject: [PATCH 35/66] Update gha-script.sh --- tools/ci/gha-script.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/ci/gha-script.sh b/tools/ci/gha-script.sh index 4c125b529..85fdef49f 100755 --- a/tools/ci/gha-script.sh +++ b/tools/ci/gha-script.sh @@ -16,6 +16,8 @@ fi # Check NCrystal installation if [[ $NCRYSTAL = 'y' ]]; then + # Change environmental variables + eval $( "${HOME}/ncrystal_inst/bin/ncrystal-config" --setup ) nctool --test fi From e7b4285bd5214d0028700c07897fe60919ef8689 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian Date: Tue, 29 Nov 2022 11:21:04 +0100 Subject: [PATCH 36/66] Cleanup --- docs/source/methods/cross_sections.rst | 2 +- docs/source/usersguide/materials.rst | 2 +- tools/ci/gha-install-ncrystal.sh | 2 -- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/source/methods/cross_sections.rst b/docs/source/methods/cross_sections.rst index 64e4fac3d..0b87913e5 100644 --- a/docs/source/methods/cross_sections.rst +++ b/docs/source/methods/cross_sections.rst @@ -189,7 +189,7 @@ cannot currently included in ACE files such as oriented single crystals (see the and further extend the physics `using plugins`_. Thermal scattering kernels are generated on the fly from dynamic and structural data, or loaded from :math:`S(\alpha,\beta)` tables converted from ENDF6 evaluations. These kernels are sampled in a direct way using a fast `rejection algorithm`_ -that does not require previous processing. A `large library` of materials is already included in +that does not require previous processing. A `large library`_ of materials is already included in the NCrystal distribution, and new materials can be easily defined from scratch in the `NCMAT format`_ or `combining existing files`_. diff --git a/docs/source/usersguide/materials.rst b/docs/source/usersguide/materials.rst index 1eaae846c..a3c05f421 100644 --- a/docs/source/usersguide/materials.rst +++ b/docs/source/usersguide/materials.rst @@ -106,7 +106,7 @@ Adding NCrystal materials ------------------ Additional support for thermal scattering can be added by using NCrystal_. -The :meth:`Material.from_ncrystal` class method generates a material object from +The :meth:`Material.from_ncrystal` class method generates a :class:`openmc.Material` object from an `NCrystal configuration string `_. Temperature, material composition and density are passed from the configuration string and the `NCMAT file `_ diff --git a/tools/ci/gha-install-ncrystal.sh b/tools/ci/gha-install-ncrystal.sh index 390b7dba5..16f77e13e 100755 --- a/tools/ci/gha-install-ncrystal.sh +++ b/tools/ci/gha-install-ncrystal.sh @@ -15,8 +15,6 @@ CPU_COUNT=1 mkdir "$BLD_DIR" cd ncrystal_bld -#cmake -Dstatic=on .. && make 2>/dev/null && sudo make install - cmake \ "${SRC_DIR}" \ -DBUILD_SHARED_LIBS=ON \ From f2c28706a6eaf2206da5ff73867c279946b55f4b Mon Sep 17 00:00:00 2001 From: Thomas Kittelmann Date: Wed, 7 Dec 2022 10:12:23 +0100 Subject: [PATCH 37/66] Apply suggestions from code review Co-authored-by: Paul Romano --- CMakeLists.txt | 2 -- docs/source/usersguide/materials.rst | 14 ++++---- openmc/material.py | 43 +++++++++++++------------ tests/regression_tests/ncrystal/test.py | 29 ++++++++--------- tools/ci/gha-install.py | 4 +-- tools/ci/gha-script.sh | 6 ++-- 6 files changed, 47 insertions(+), 51 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2815230d4..5c4d489e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -497,8 +497,6 @@ if(OPENMC_USE_NCRYSTAL) target_link_libraries(libopenmc NCrystal::NCrystal) endif() - - #=============================================================================== # openmc executable #=============================================================================== diff --git a/docs/source/usersguide/materials.rst b/docs/source/usersguide/materials.rst index a3c05f421..fbf9effd8 100644 --- a/docs/source/usersguide/materials.rst +++ b/docs/source/usersguide/materials.rst @@ -101,19 +101,17 @@ you would need to add hydrogen and oxygen to a material and then assign the .. _usersguide_naming: ------------------- +------------------------- Adding NCrystal materials ------------------- +------------------------- Additional support for thermal scattering can be added by using NCrystal_. The :meth:`Material.from_ncrystal` class method generates a :class:`openmc.Material` object from an `NCrystal configuration string `_. -Temperature, material composition and density are passed from the configuration string +Temperature, material composition, and density are passed from the configuration string and the `NCMAT file `_ -that define the material, e.g: +that define the material, e.g.:: -:: - mat = openmc.Material.from_ncrystal('Al_sg225.ncmat;temp=300K') defines a material containing polycrystalline alumnium, @@ -126,12 +124,12 @@ defines a material containing polycrystalline alumnium, defines an oriented germanium single crystal with 40 arcsec mosaicity. -NCrystal only handles low energy neutron interactions. Other interaction are +NCrystal only handles low energy neutron interactions. Other interactions are provided by standard ACE files. NCrystal_ comes with a `predefined library `_ but more materials can be added by creating NCMAT files or on-the-fly in the configuration string. -.. warning:: Currently, NCrystal_ materials cannot be modified after created. +.. warning:: Currently, NCrystal_ materials cannot be modified after they are created. Density, temperature and composition should be defined in the configuration string or the NCMAT file. diff --git a/openmc/material.py b/openmc/material.py index de4a22cb8..298731c92 100644 --- a/openmc/material.py +++ b/openmc/material.py @@ -336,11 +336,12 @@ class Material(IDManagerMixin): @classmethod def from_ncrystal(cls, cfg, material_id=None, name=''): - """Create material from NCrystal configuration string. Density, - temperature, and material composition, and (ultimately) thermal neutron - scattering, will be automatically be provided by NCrystal based on this - string. The name and material_id parameters are simply passed on to the - Material constructor. + """Create material from NCrystal configuration string. + + Density, temperature, and material composition, and (ultimately) thermal + neutron scattering will be automatically be provided by NCrystal based + on this string. The name and material_id parameters are simply passed on + to the Material constructor. Parameters ---------- @@ -363,41 +364,41 @@ class Material(IDManagerMixin): import NCrystal nc_mat = NCrystal.createInfo(cfg) - def openmc_natabund( Z ): + def openmc_natabund(Z): #nc_mat.getFlattenedComposition might need natural abundancies. #This call-back function is used so NCrystal can flatten composition #using OpenMC's natural abundancies. In practice this function will #only get invoked in the unlikely case where a material is specified #by referring both to natural elements and specific isotopes of the #same element. - elem_name = openmc.data.ATOMIC_SYMBOL.get( Z, None ) + elem_name = openmc.data.ATOMIC_SYMBOL.get(Z) if not elem_name: raise ValueError( f'Element with Z={Z} is not known' ) - l = [] - for iso_name,abund in openmc.data.isotopes( elem_name ): - l.append( ( int(iso_name[ len(elem_name) : ]), abund ) ) - return l + return [ + (int(iso_name[len(elem_name):]), abund) + for iso_name, abund in openmc.data.isotopes(elem_name) + ] - flat_compos = nc_mat.getFlattenedComposition( preferNaturalElements = True, - naturalAbundProvider = openmc_natabund ) + flat_compos = nc_mat.getFlattenedComposition(preferNaturalElements = True, + naturalAbundProvider=openmc_natabund) # Create the Material - material = cls( material_id = material_id, - name = name, - temperature = nc_mat.getTemperature() ) + material = cls(material_id=material_id, + name=name, + temperature=nc_mat.getTemperature()) for Z, A_vals in flat_compos: - elemname = openmc.data.ATOMIC_SYMBOL.get(Z,None) + elemname = openmc.data.ATOMIC_SYMBOL.get(Z) if not elemname: raise ValueError(f'Element with Z={Z} is not known') for A, frac in A_vals: if A: - material.add_nuclide( elemname + str(A), frac, 'ao' ) + material.add_nuclide(f'{elemname}{A}', frac) else: - material.add_element( elemname, frac, 'ao' ) + material.add_element(elemname, frac) - material.set_density( 'g/cm3', nc_mat.getDensity() ) - material._ncrystal_cfg = NCrystal.normaliseCfg( cfg ) + material.set_density('g/cm3', nc_mat.getDensity()) + material._ncrystal_cfg = NCrystal.normaliseCfg(cfg) return material diff --git a/tests/regression_tests/ncrystal/test.py b/tests/regression_tests/ncrystal/test.py index 0a47d634d..3137515d4 100644 --- a/tests/regression_tests/ncrystal/test.py +++ b/tests/regression_tests/ncrystal/test.py @@ -10,7 +10,7 @@ pytestmark = pytest.mark.skipif( reason="NCrystal materials are not enabled.") def compute_angular_distribution(cfg, E0, N): - """Return a openmc.model.Model() object for a monoenergetic pencil + """Return an openmc.Model() object for a monoenergetic pencil beam hitting a 1 mm sphere filled with the material defined by the cfg string, and compute the angular distribution""" @@ -23,17 +23,16 @@ def compute_angular_distribution(cfg, E0, N): sample_sphere = openmc.Sphere(r=0.1) outer_sphere = openmc.Sphere(r=100, boundary_type="vacuum") - cell1 = openmc.Cell(region= -sample_sphere, fill=m1) - cell2_region= +sample_sphere&-outer_sphere - cell2 = openmc.Cell(region= cell2_region, fill=None) - uni1 = openmc.Universe(cells=[cell1, cell2]) - geometry = openmc.Geometry(uni1) + cell1 = openmc.Cell(region=-sample_sphere, fill=m1) + cell2_region= +sample_sphere & -outer_sphere + cell2 = openmc.Cell(region=cell2_region, fill=None) + geometry = openmc.Geometry([cell1, cell2]) # Source definition source = openmc.Source() - source.space = openmc.stats.Point(xyz = (0,0,-20)) - source.angle = openmc.stats.Monodirectional(reference_uvw = (0,0,1)) + source.space = openmc.stats.Point((0, 0, -20)) + source.angle = openmc.stats.Monodirectional(reference_uvw=(0, 0, 1)) source.energy = openmc.stats.Discrete([E0], [1.0]) # Execution settings @@ -48,13 +47,13 @@ def compute_angular_distribution(cfg, E0, N): tally1 = openmc.Tally(name="angular distribution") tally1.scores = ["current"] - filter1 = openmc.filter.SurfaceFilter(sample_sphere) - filter2 = openmc.filter.PolarFilter(np.linspace(0,np.pi,180+1)) - filter3 = openmc.filter.CellFromFilter(cell1) + filter1 = openmc.SurfaceFilter(sample_sphere) + filter2 = openmc.PolarFilter(np.linspace(0, np.pi, 180+1)) + filter3 = openmc.CellFromFilter(cell1) tally1.filters = [filter1, filter2, filter3] tallies = openmc.Tallies([tally1]) - return openmc.model.Model(geometry, materials, settings, tallies) + return openmc.Model(geometry, materials, settings, tallies) class NCrystalTest(PyAPITestHarness): @@ -62,9 +61,9 @@ class NCrystalTest(PyAPITestHarness): """Digest info in the statepoint and return as a string.""" # Read the statepoint file. - sp = openmc.StatePoint(self._sp_name) - tal = sp.get_tally(name='angular distribution') - df = tal.get_pandas_dataframe() + with openmc.StatePoint(self._sp_name) as sp: + tal = sp.get_tally(name='angular distribution') + df = tal.get_pandas_dataframe() return df.to_string() def test_ncrystal(): diff --git a/tools/ci/gha-install.py b/tools/ci/gha-install.py index f5779c935..ac10bb3f6 100644 --- a/tools/ci/gha-install.py +++ b/tools/ci/gha-install.py @@ -56,8 +56,8 @@ def install(omp=False, mpi=False, phdf5=False, dagmc=False, libmesh=False, ncrys if ncrystal: cmake_cmd.append('-DOPENMC_USE_NCRYSTAL=ON') - ncrystal_cmake_path = os.environ.get('HOME')+'/ncrystal_inst/lib/cmake' - cmake_cmd.append('-DCMAKE_PREFIX_PATH=' + ncrystal_cmake_path) + ncrystal_cmake_path = os.environ.get('HOME') + '/ncrystal_inst/lib/cmake' + cmake_cmd.append(f'-DCMAKE_PREFIX_PATH={ncrystal_cmake_path}') # Build in coverage mode for coverage testing cmake_cmd.append('-DOPENMC_ENABLE_COVERAGE=on') diff --git a/tools/ci/gha-script.sh b/tools/ci/gha-script.sh index 85fdef49f..1f1c3a1eb 100755 --- a/tools/ci/gha-script.sh +++ b/tools/ci/gha-script.sh @@ -16,9 +16,9 @@ fi # Check NCrystal installation if [[ $NCRYSTAL = 'y' ]]; then - # Change environmental variables - eval $( "${HOME}/ncrystal_inst/bin/ncrystal-config" --setup ) - nctool --test + # Change environmental variables + eval $( "${HOME}/ncrystal_inst/bin/ncrystal-config" --setup ) + nctool --test fi # Run regression and unit tests From a86e74f59a27236a4705ab0b4dc8c79736cd2c56 Mon Sep 17 00:00:00 2001 From: Thomas Kittelmann Date: Wed, 7 Dec 2022 12:51:31 +0100 Subject: [PATCH 38/66] Rename compute_angular_distribution to pencil_beam_model --- tests/regression_tests/ncrystal/test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/regression_tests/ncrystal/test.py b/tests/regression_tests/ncrystal/test.py index 3137515d4..2a789dac7 100644 --- a/tests/regression_tests/ncrystal/test.py +++ b/tests/regression_tests/ncrystal/test.py @@ -9,7 +9,7 @@ pytestmark = pytest.mark.skipif( not openmc.lib._ncrystal_enabled(), reason="NCrystal materials are not enabled.") -def compute_angular_distribution(cfg, E0, N): +def pencil_beam_model(cfg, E0, N): """Return an openmc.Model() object for a monoenergetic pencil beam hitting a 1 mm sphere filled with the material defined by the cfg string, and compute the angular distribution""" @@ -71,6 +71,6 @@ def test_ncrystal(): T = 293.6 # K E0 = 0.012 # eV cfg = 'Al_sg225.ncmat' - test = compute_angular_distribution(cfg, E0, NParticles) + test = pencil_beam_model(cfg, E0, NParticles) harness = NCrystalTest('statepoint.10.h5', model=test) harness.main() From ecfa94e0ff0c50a83c8db61444e5cad888b16c7a Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 8 Sep 2022 14:25:22 -0500 Subject: [PATCH 39/66] Rename gnd_name to gnds_name. Change GND to GNDS in comments --- docs/source/pythonapi/data.rst | 2 +- docs/source/usersguide/scripts.rst | 4 ++-- openmc/data/ace.py | 4 ++-- openmc/data/data.py | 19 +++++++++++-------- openmc/data/decay.py | 2 +- openmc/data/endf.py | 12 ++++++------ openmc/data/multipole.py | 4 ++-- openmc/data/neutron.py | 4 ++-- openmc/data/reaction.py | 2 +- openmc/data/thermal.py | 10 +++++----- openmc/deplete/chain.py | 16 ++++++++-------- openmc/deplete/nuclide.py | 2 +- openmc/nuclide.py | 2 +- scripts/openmc-update-inputs | 5 ++--- tests/unit_tests/test_data_misc.py | 12 ++++++------ 15 files changed, 51 insertions(+), 49 deletions(-) diff --git a/docs/source/pythonapi/data.rst b/docs/source/pythonapi/data.rst index 960abec2f..1eaf90c97 100644 --- a/docs/source/pythonapi/data.rst +++ b/docs/source/pythonapi/data.rst @@ -66,7 +66,7 @@ Core Functions decay_energy decay_photon_energy dose_coefficients - gnd_name + gnds_name half_life isotopes kalbach_slope diff --git a/docs/source/usersguide/scripts.rst b/docs/source/usersguide/scripts.rst index c433ffe2c..78ee6f775 100644 --- a/docs/source/usersguide/scripts.rst +++ b/docs/source/usersguide/scripts.rst @@ -157,9 +157,9 @@ geometry.xml added. Any 'surfaces' attributes/elements on a cell will be renamed 'region'. materials.xml - Nuclide names will be changed from ACE aliases (e.g., Am-242m) to HDF5/GND + Nuclide names will be changed from ACE aliases (e.g., Am-242m) to HDF5/GNDS names (e.g., Am242_m1). Thermal scattering table names will be changed from - ACE aliases (e.g., HH2O) to HDF5/GND names (e.g., c_H_in_H2O). + ACE aliases (e.g., HH2O) to HDF5/GNDS names (e.g., c_H_in_H2O). ---------------------- ``openmc-update-mgxs`` diff --git a/openmc/data/ace.py b/openmc/data/ace.py index 06c581b42..91cbe4196 100644 --- a/openmc/data/ace.py +++ b/openmc/data/ace.py @@ -24,7 +24,7 @@ import numpy as np import openmc.checkvalue as cv from openmc.mixin import EqualityMixin -from .data import ATOMIC_SYMBOL, gnd_name, EV_PER_MEV, K_BOLTZMANN +from .data import ATOMIC_SYMBOL, gnds_name, EV_PER_MEV, K_BOLTZMANN from .endf import ENDF_FLOAT_RE @@ -88,7 +88,7 @@ def get_metadata(zaid, metastable_scheme='nndc'): # Determine name element = ATOMIC_SYMBOL[Z] - name = gnd_name(Z, mass_number, metastable) + name = gnds_name(Z, mass_number, metastable) return (name, element, Z, mass_number, metastable) diff --git a/openmc/data/data.py b/openmc/data/data.py index 55bfb4f09..d5521d998 100644 --- a/openmc/data/data.py +++ b/openmc/data/data.py @@ -197,8 +197,8 @@ NEUTRON_MASS = 1.00866491595 # Used in atomic_mass function as a cache _ATOMIC_MASS = {} -# Regex for GND nuclide names (used in zam function) -_GND_NAME_RE = re.compile(r'([A-Zn][a-z]*)(\d+)((?:_[em]\d+)?)') +# Regex for GNDS nuclide names (used in zam function) +_GNDS_NAME_RE = re.compile(r'([A-Zn][a-z]*)(\d+)((?:_[em]\d+)?)') # Used in half_life function as a cache _HALF_LIFE = {} @@ -436,8 +436,11 @@ def water_density(temperature, pressure=0.1013): return coeff / pi / gamma1_pi -def gnd_name(Z, A, m=0): - """Return nuclide name using GND convention +def gnds_name(Z, A, m=0): + """Return nuclide name using GNDS convention + + .. versionchanged:: 0.14.0 + Function name changed from ``gnd_name`` to ``gnds_name`` Parameters ---------- @@ -451,7 +454,7 @@ def gnd_name(Z, A, m=0): Returns ------- str - Nuclide name in GND convention, e.g., 'Am242_m1' + Nuclide name in GNDS convention, e.g., 'Am242_m1' """ if m > 0: @@ -502,7 +505,7 @@ def zam(name): Parameters ---------- name : str - Name of nuclide using GND convention, e.g., 'Am242_m1' + Name of nuclide using GNDS convention, e.g., 'Am242_m1' Returns ------- @@ -511,10 +514,10 @@ def zam(name): """ try: - symbol, A, state = _GND_NAME_RE.match(name).groups() + symbol, A, state = _GNDS_NAME_RE.match(name).groups() except AttributeError: raise ValueError(f"'{name}' does not appear to be a nuclide name in " - "GND format") + "GNDS format") if symbol not in ATOMIC_NUMBER: raise ValueError(f"'{symbol}' is not a recognized element symbol") diff --git a/openmc/data/decay.py b/openmc/data/decay.py index 0db842201..d00242438 100644 --- a/openmc/data/decay.py +++ b/openmc/data/decay.py @@ -144,7 +144,7 @@ class FissionProductYields(EqualityMixin): # Assign basic nuclide properties self.nuclide = { - 'name': ev.gnd_name, + 'name': ev.gnds_name, 'atomic_number': ev.target['atomic_number'], 'mass_number': ev.target['mass_number'], 'isomeric_state': ev.target['isomeric_state'] diff --git a/openmc/data/endf.py b/openmc/data/endf.py index f9b9d0694..d526bc53f 100644 --- a/openmc/data/endf.py +++ b/openmc/data/endf.py @@ -12,7 +12,7 @@ import re import numpy as np -from .data import gnd_name +from .data import gnds_name from .function import Tabulated1D try: from ._endf import float_endf @@ -520,10 +520,10 @@ class Evaluation: self.reaction_list.append((mf, mt, nc, mod)) @property - def gnd_name(self): - return gnd_name(self.target['atomic_number'], - self.target['mass_number'], - self.target['isomeric_state']) + def gnds_name(self): + return gnds_name(self.target['atomic_number'], + self.target['mass_number'], + self.target['isomeric_state']) class Tabulated2D: @@ -531,7 +531,7 @@ class Tabulated2D: This is a dummy class that is not really used other than to store the interpolation information for a two-dimensional function. Once we refactor - to adopt GND-like data containers, this will probably be removed or + to adopt GNDS-like data containers, this will probably be removed or extended. Parameters diff --git a/openmc/data/multipole.py b/openmc/data/multipole.py index 508c76ac4..0be70cdba 100644 --- a/openmc/data/multipole.py +++ b/openmc/data/multipole.py @@ -748,12 +748,12 @@ class WindowedMultipole(EqualityMixin): Parameters ---------- name : str - Name of the nuclide using the GND naming convention + Name of the nuclide using the GNDS naming convention Attributes ---------- name : str - Name of the nuclide using the GND naming convention + Name of the nuclide using the GNDS naming convention spacing : float The width of each window in sqrt(E)-space. For example, the frst window will end at (sqrt(E_min) + spacing)**2 and the second window at diff --git a/openmc/data/neutron.py b/openmc/data/neutron.py index e0574d76d..2d4c13963 100644 --- a/openmc/data/neutron.py +++ b/openmc/data/neutron.py @@ -44,7 +44,7 @@ class IncidentNeutron(EqualityMixin): Parameters ---------- name : str - Name of the nuclide using the GND naming convention + Name of the nuclide using the GNDS naming convention atomic_number : int Number of protons in the target nucleus mass_number : int @@ -75,7 +75,7 @@ class IncidentNeutron(EqualityMixin): Metastable state of the target nucleus. A value of zero indicates ground state. name : str - Name of the nuclide using the GND naming convention + Name of the nuclide using the GNDS naming convention reactions : collections.OrderedDict Contains the cross sections, secondary angle and energy distributions, and other associated data for each reaction. The keys are the MT values diff --git a/openmc/data/reaction.py b/openmc/data/reaction.py index a2431cf1c..ac9d7f14e 100644 --- a/openmc/data/reaction.py +++ b/openmc/data/reaction.py @@ -555,7 +555,7 @@ def _get_activation_products(ev, rx): Z, A = divmod(items[2], 1000) excited_state = items[3] - # Get GND name for product + # Get GNDS name for product symbol = ATOMIC_SYMBOL[Z] if excited_state > 0: name = '{}{}_e{}'.format(symbol, A, excited_state) diff --git a/openmc/data/thermal.py b/openmc/data/thermal.py index e6213e5f0..48f6bfc9b 100644 --- a/openmc/data/thermal.py +++ b/openmc/data/thermal.py @@ -104,7 +104,7 @@ def get_thermal_name(name): Returns ------- str - GND-format thermal scattering name + GNDS-format thermal scattering name """ if name in _THERMAL_NAMES: @@ -396,7 +396,7 @@ class ThermalScattering(EqualityMixin): Parameters ---------- name : str - Name of the material using GND convention, e.g. c_H_in_H2O + Name of the material using GNDS convention, e.g. c_H_in_H2O atomic_weight_ratio : float Atomic mass ratio of the target nuclide. kTs : Iterable of float @@ -415,7 +415,7 @@ class ThermalScattering(EqualityMixin): Inelastic scattering cross section derived in the incoherent approximation name : str - Name of the material using GND convention, e.g. c_H_in_H2O + Name of the material using GNDS convention, e.g. c_H_in_H2O temperatures : Iterable of str List of string representations the temperatures of the target nuclide in the data set. The temperatures are strings of the temperature, @@ -491,7 +491,7 @@ class ThermalScattering(EqualityMixin): ACE table to read from. If given as a string, it is assumed to be the filename for the ACE file. name : str - GND-conforming name of the material, e.g. c_H_in_H2O. If none is + GNDS-conforming name of the material, e.g. c_H_in_H2O. If none is passed, the appropriate name is guessed based on the name of the ACE table. @@ -596,7 +596,7 @@ class ThermalScattering(EqualityMixin): ACE table to read from. If given as a string, it is assumed to be the filename for the ACE file. name : str - GND-conforming name of the material, e.g. c_H_in_H2O. If none is + GNDS-conforming name of the material, e.g. c_H_in_H2O. If none is passed, the appropriate name is guessed based on the name of the ACE table. diff --git a/openmc/deplete/chain.py b/openmc/deplete/chain.py index f439cedc3..372ed3510 100644 --- a/openmc/deplete/chain.py +++ b/openmc/deplete/chain.py @@ -15,7 +15,7 @@ from numbers import Real, Integral from warnings import warn from openmc.checkvalue import check_type, check_greater_than -from openmc.data import gnd_name, zam, DataLibrary +from openmc.data import gnds_name, zam, DataLibrary from openmc.exceptions import DataError from .nuclide import FissionYieldDistribution @@ -135,14 +135,14 @@ def replace_missing(product, decay_data): Parameters ---------- product : str - Name of product in GND format, e.g. 'Y86_m1'. + Name of product in GNDS format, e.g. 'Y86_m1'. decay_data : dict Dictionary of decay data Returns ------- product : str - Replacement for missing product in GND format. + Replacement for missing product in GNDS format. """ # Determine atomic number, mass number, and metastable state @@ -213,7 +213,7 @@ def replace_missing_fpy(actinide, fpy_data, decay_data): # Check if metastable state has data (e.g., Am242m) Z, A, m = zam(actinide) if m == 0: - metastable = gnd_name(Z, A, 1) + metastable = gnds_name(Z, A, 1) if metastable in fpy_data: return metastable @@ -222,7 +222,7 @@ def replace_missing_fpy(actinide, fpy_data, decay_data): while isotone in decay_data: Z += 1 A += 1 - isotone = gnd_name(Z, A, 0) + isotone = gnds_name(Z, A, 0) if isotone in fpy_data: return isotone @@ -231,7 +231,7 @@ def replace_missing_fpy(actinide, fpy_data, decay_data): while isotone in decay_data: Z -= 1 A -= 1 - isotone = gnd_name(Z, A, 0) + isotone = gnds_name(Z, A, 0) if isotone in fpy_data: return isotone @@ -357,7 +357,7 @@ class Chain: reactions = {} for f in neutron_files: evaluation = openmc.data.endf.Evaluation(f) - name = evaluation.gnd_name + name = evaluation.gnds_name reactions[name] = {} for mf, mt, nc, mod in evaluation.reaction_list: if mf == 3: @@ -904,7 +904,7 @@ class Chain: ground_target = grounds.get(parent_name) if ground_target is None: pz, pa, pm = zam(parent_name) - ground_target = gnd_name(pz, pa + 1, 0) + ground_target = gnds_name(pz, pa + 1, 0) new_ratios[ground_target] = ground_br parent.add_reaction(reaction, ground_target, rxn_Q, ground_br) diff --git a/openmc/deplete/nuclide.py b/openmc/deplete/nuclide.py index b84b91a7f..2e9673d5f 100644 --- a/openmc/deplete/nuclide.py +++ b/openmc/deplete/nuclide.py @@ -82,7 +82,7 @@ class Nuclide: Parameters ---------- name : str, optional - GND name of this nuclide, e.g. ``"He4"``, ``"Am242_m1"`` + GNDS name of this nuclide, e.g. ``"He4"``, ``"Am242_m1"`` Attributes ---------- diff --git a/openmc/nuclide.py b/openmc/nuclide.py index 196c8d770..d5ae4bddb 100644 --- a/openmc/nuclide.py +++ b/openmc/nuclide.py @@ -26,7 +26,7 @@ class Nuclide(str): if name.endswith('m'): name = name[:-1] + '_m1' - msg = ('OpenMC nuclides follow the GND naming convention. ' + msg = ('OpenMC nuclides follow the GNDS naming convention. ' f'Nuclide "{orig_name}" is being renamed as "{name}".') warnings.warn(msg) diff --git a/scripts/openmc-update-inputs b/scripts/openmc-update-inputs index c47f888c8..2d49c0626 100755 --- a/scripts/openmc-update-inputs +++ b/scripts/openmc-update-inputs @@ -4,7 +4,6 @@ """ import argparse -from difflib import get_close_matches from itertools import chain from random import randint from shutil import move @@ -27,8 +26,8 @@ geometry.xml: Lattices containing 'outside' attributes/tags will be replaced will be renamed 'region'. materials.xml: Nuclide names will be changed from ACE aliases (e.g., Am-242m) to - HDF5/GND names (e.g., Am242_m1). Thermal scattering table names will be - changed from ACE aliases (e.g., HH2O) to HDF5/GND names (e.g., c_H_in_H2O). + HDF5/GNDS names (e.g., Am242_m1). Thermal scattering table names will be + changed from ACE aliases (e.g., HH2O) to HDF5/GNDS names (e.g., c_H_in_H2O). """ diff --git a/tests/unit_tests/test_data_misc.py b/tests/unit_tests/test_data_misc.py index 37e024fe2..f88be2602 100644 --- a/tests/unit_tests/test_data_misc.py +++ b/tests/unit_tests/test_data_misc.py @@ -101,12 +101,12 @@ def test_water_density(): assert dens(500.0, 3.0) == pytest.approx(1e-3/0.120241800e-2, 1e-6) -def test_gnd_name(): - assert openmc.data.gnd_name(1, 1) == 'H1' - assert openmc.data.gnd_name(40, 90) == ('Zr90') - assert openmc.data.gnd_name(95, 242, 0) == ('Am242') - assert openmc.data.gnd_name(95, 242, 1) == ('Am242_m1') - assert openmc.data.gnd_name(95, 242, 10) == ('Am242_m10') +def test_gnds_name(): + assert openmc.data.gnds_name(1, 1) == 'H1' + assert openmc.data.gnds_name(40, 90) == ('Zr90') + assert openmc.data.gnds_name(95, 242, 0) == ('Am242') + assert openmc.data.gnds_name(95, 242, 1) == ('Am242_m1') + assert openmc.data.gnds_name(95, 242, 10) == ('Am242_m10') def test_isotopes(): From a5bdbd3adbc18ec429647c4b0ae9cee7098d7025 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 23 Dec 2022 00:04:45 -0600 Subject: [PATCH 40/66] Address a few warnings from tests --- openmc/mgxs/mgxs.py | 4 ++-- tests/regression_tests/diff_tally/test.py | 5 ++--- tests/regression_tests/surface_tally/test.py | 5 ++--- tests/unit_tests/test_mesh_to_vtk.py | 4 ++-- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/openmc/mgxs/mgxs.py b/openmc/mgxs/mgxs.py index 15a1128f8..c93b42b88 100644 --- a/openmc/mgxs/mgxs.py +++ b/openmc/mgxs/mgxs.py @@ -925,7 +925,7 @@ class MGXS: # Tabulate the atomic number densities for all nuclides elif nuclides == 'all': nuclides = self.get_nuclides() - densities = np.zeros(self.num_nuclides, dtype=np.float) + densities = np.zeros(self.num_nuclides, dtype=float) for i, nuclide in enumerate(nuclides): densities[i] += self.get_nuclide_density(nuclide) @@ -3019,7 +3019,7 @@ class DiffusionCoefficient(TransportXS): new_filt = openmc.EnergyFilter(old_filt.values) p1_tally.filters[-2] = new_filt - p1_tally = p1_tally.get_slice(filters=[openmc.LegendreFilter], + p1_tally = p1_tally.get_slice(filters=[openmc.LegendreFilter], filter_bins=[('P1',)],squeeze=True) p1_tally._scores = ['scatter-1'] total_xs = self.tallies['total'] / self.tallies['flux (tracklength)'] diff --git a/tests/regression_tests/diff_tally/test.py b/tests/regression_tests/diff_tally/test.py index b688e6d23..18c501372 100644 --- a/tests/regression_tests/diff_tally/test.py +++ b/tests/regression_tests/diff_tally/test.py @@ -103,9 +103,8 @@ class DiffTallyTestHarness(PyAPITestHarness): sp = openmc.StatePoint(statepoint) # Extract the tally data as a Pandas DataFrame. - df = pd.DataFrame() - for t in sp.tallies.values(): - df = df.append(t.get_pandas_dataframe(), ignore_index=True) + tally_dfs = [t.get_pandas_dataframe() for t in sp.tallies.values()] + df = pd.concat(tally_dfs, ignore_index=True) # Extract the relevant data as a CSV string. cols = ('d_material', 'd_nuclide', 'd_variable', 'score', 'mean', diff --git a/tests/regression_tests/surface_tally/test.py b/tests/regression_tests/surface_tally/test.py index 96199ec2a..d21f361e6 100644 --- a/tests/regression_tests/surface_tally/test.py +++ b/tests/regression_tests/surface_tally/test.py @@ -164,9 +164,8 @@ class SurfaceTallyTestHarness(PyAPITestHarness): sp = openmc.StatePoint(self._sp_name) # Extract the tally data as a Pandas DataFrame. - df = pd.DataFrame() - for t in sp.tallies.values(): - df = df.append(t.get_pandas_dataframe(), ignore_index=True) + tally_dfs = [t.get_pandas_dataframe() for t in sp.tallies.values()] + df = pd.concat(tally_dfs, ignore_index=True) # Extract the relevant data as a CSV string. cols = ('mean', 'std. dev.') diff --git a/tests/unit_tests/test_mesh_to_vtk.py b/tests/unit_tests/test_mesh_to_vtk.py index d7d5907a2..f90eab134 100644 --- a/tests/unit_tests/test_mesh_to_vtk.py +++ b/tests/unit_tests/test_mesh_to_vtk.py @@ -77,8 +77,8 @@ def test_write_data_to_vtk_size_mismatch(mesh): # by regex. These are needed to make the test string match the error message # string when using the match argument as that uses regular expression expected_error_msg = ( - f"The size of the dataset 'label' \({len(data)}\) should be equal to " - f"the number of mesh cells \({mesh.num_mesh_cells}\)" + f"The size of the dataset 'label' ({len(data)}) should be equal to " + f"the number of mesh cells ({mesh.num_mesh_cells})" ) with pytest.raises(ValueError, match=expected_error_msg): mesh.write_data_to_vtk(filename="out.vtk", datasets={"label": data}) From f9c6765eca0026291bf760cb8f33866fe367034b Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 12 Dec 2022 21:39:39 -0600 Subject: [PATCH 41/66] Update intersphinx URLs --- docs/source/conf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index f08333279..ae329dd7e 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -247,7 +247,7 @@ napoleon_use_ivar = True intersphinx_mapping = { 'python': ('https://docs.python.org/3', None), 'numpy': ('https://numpy.org/doc/stable/', None), - 'scipy': ('https://docs.scipy.org/doc/scipy/reference', None), + 'scipy': ('https://docs.scipy.org/doc/scipy/', None), 'pandas': ('https://pandas.pydata.org/pandas-docs/stable/', None), - 'matplotlib': ('https://matplotlib.org/', None) + 'matplotlib': ('https://matplotlib.org/stable/', None) } From a178f5f85a95d3eb519da91b9e462ae5d906be0c Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 23 Dec 2022 10:55:58 -0600 Subject: [PATCH 42/66] Use raw f-string in test_mesh_to_vtk.py --- tests/unit_tests/test_mesh_to_vtk.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit_tests/test_mesh_to_vtk.py b/tests/unit_tests/test_mesh_to_vtk.py index f90eab134..bc3633c8c 100644 --- a/tests/unit_tests/test_mesh_to_vtk.py +++ b/tests/unit_tests/test_mesh_to_vtk.py @@ -77,8 +77,8 @@ def test_write_data_to_vtk_size_mismatch(mesh): # by regex. These are needed to make the test string match the error message # string when using the match argument as that uses regular expression expected_error_msg = ( - f"The size of the dataset 'label' ({len(data)}) should be equal to " - f"the number of mesh cells ({mesh.num_mesh_cells})" + fr"The size of the dataset 'label' \({len(data)}\) should be equal to " + fr"the number of mesh cells \({mesh.num_mesh_cells}\)" ) with pytest.raises(ValueError, match=expected_error_msg): mesh.write_data_to_vtk(filename="out.vtk", datasets={"label": data}) From 577157b846ac6885251ade59456967e4102d6b24 Mon Sep 17 00:00:00 2001 From: shimwell Date: Sat, 24 Dec 2022 22:40:34 +0000 Subject: [PATCH 43/66] allowing xml file for materials --- openmc/geometry.py | 29 +++++++++++++++++++---------- tests/unit_tests/test_geometry.py | 19 ++++++++++++++++++- 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/openmc/geometry.py b/openmc/geometry.py index a43899daa..eb7fa8200 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -1,3 +1,5 @@ +import os +import typing from collections import OrderedDict, defaultdict from collections.abc import Iterable from copy import deepcopy @@ -7,7 +9,7 @@ import warnings import openmc import openmc._xml as xml -from .checkvalue import check_type, check_less_than, check_greater_than +from .checkvalue import check_type, check_less_than, check_greater_than, PathLike class Geometry: @@ -254,16 +256,20 @@ class Geometry: raise ValueError('Error determining root universe.') @classmethod - def from_xml(cls, path='geometry.xml', materials=None): + def from_xml( + cls, + path: PathLike = 'geometry.xml', + materials: typing.Optional[typing.Union[PathLike, 'openmc.Materials']] = 'materials.xml' + ): """Generate geometry from XML file Parameters ---------- - path : str, optional + path : PathLike, optional Path to geometry XML file - materials : openmc.Materials or None - Materials used to assign to cells. If None, an attempt is made to - generate it from the materials.xml file. + materials : openmc.Materials or or PathLike + Materials used to assign to cells. If PathLike, an attempt is made + to generate materials from the provided xml file. Returns ------- @@ -271,10 +277,13 @@ class Geometry: Geometry object """ - # Create dictionary to easily look up materials - if materials is None: - filename = Path(path).parent / 'materials.xml' - materials = openmc.Materials.from_xml(str(filename)) + + # Using str and os.Pathlike here to avoid error when using just the imported PathLike + # TypeError: Subscripted generics cannot be used with class and instance checks + check_type('materials', materials, (str, os.PathLike, openmc.Materials)) + + if isinstance(materials, (str, os.PathLike)): + materials = openmc.Materials.from_xml(materials) tree = ET.parse(path) root = tree.getroot() diff --git a/tests/unit_tests/test_geometry.py b/tests/unit_tests/test_geometry.py index 9db112fd2..843c01def 100644 --- a/tests/unit_tests/test_geometry.py +++ b/tests/unit_tests/test_geometry.py @@ -1,4 +1,5 @@ import xml.etree.ElementTree as ET +from pathlib import Path import numpy as np import openmc @@ -274,7 +275,23 @@ def test_from_xml(run_in_tmpdir, mixed_lattice_model): # Export model mixed_lattice_model.export_to_xml() - # Import geometry + mats_from_xml = openmc.Materials.from_xml('materials.xml') + # checking string a Path are both acceptable + for path in ['geometry.xml', Path('geometry.xml')]: + for materials in [mats_from_xml, 'materials.xml']: + # Import geometry from file + geom = openmc.Geometry.from_xml(path=path, materials=materials) + assert isinstance(geom, openmc.Geometry) + ll, ur = geom.bounding_box + assert ll == pytest.approx((-6.0, -6.0, -np.inf)) + assert ur == pytest.approx((6.0, 6.0, np.inf)) + + with pytest.raises(TypeError) as excinfo: + geom = openmc.Geometry.from_xml(path='geometry.xml', materials=None) + assert 'Unable to set "materials" to "None"' in str(excinfo.value) + + + # checking that the default args also work geom = openmc.Geometry.from_xml() assert isinstance(geom, openmc.Geometry) ll, ur = geom.bounding_box From 80dad0f2403176c8fe5a501ec7a29ef0963ddf03 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Sat, 31 Dec 2022 11:32:39 +0700 Subject: [PATCH 44/66] Small fix in plot_xs when S(a,b) tables are present --- openmc/plotter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmc/plotter.py b/openmc/plotter.py index 16760868e..e2d18d085 100644 --- a/openmc/plotter.py +++ b/openmc/plotter.py @@ -563,7 +563,7 @@ def _calculate_cexs_elem_mat(this, types, temperature=294., for nuclide in nuclides.items(): sabs[nuclide[0]] = None if isinstance(this, openmc.Material): - for sab_name in this._sab: + for sab_name, _ in this._sab: sab = openmc.data.ThermalScattering.from_hdf5( library.get_by_material(sab_name, data_type='thermal')['path']) for nuc in sab.nuclides: From ac2ba93b3a573353a6e2fd051a1a35581f32c257 Mon Sep 17 00:00:00 2001 From: josh Date: Wed, 4 Jan 2023 03:31:07 +0000 Subject: [PATCH 45/66] remove cell deepcopying for creating a fresh instance --- openmc/cell.py | 11 ++++++++--- tests/unit_tests/test_cell.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/openmc/cell.py b/openmc/cell.py index 2aa538012..8169ca4d2 100644 --- a/openmc/cell.py +++ b/openmc/cell.py @@ -1,6 +1,5 @@ from collections import OrderedDict from collections.abc import Iterable -from copy import deepcopy from math import cos, sin, pi from numbers import Real from xml.etree import ElementTree as ET @@ -519,8 +518,14 @@ class Cell(IDManagerMixin): paths = self._paths self._paths = None - clone = deepcopy(self) - clone.id = None + clone = openmc.Cell() + clone.name = self.name + clone.temperature = self.temperature + clone.volume = self.volume + if self.translation is not None: + clone.translation = self.translation + if self.rotation is not None: + clone.rotation = self.rotation clone._num_instances = None # Restore paths on original instance diff --git a/tests/unit_tests/test_cell.py b/tests/unit_tests/test_cell.py index 1c2e1b70e..eebe0f895 100644 --- a/tests/unit_tests/test_cell.py +++ b/tests/unit_tests/test_cell.py @@ -58,24 +58,54 @@ def test_clone(): cyl = openmc.ZCylinder() c = openmc.Cell(fill=m, region=-cyl) c.temperature = 650. + c.translation = (1,2,3) + c.rotation = (4,5,6) + c.volume = 100 c2 = c.clone() assert c2.id != c.id assert c2.fill != c.fill assert c2.region != c.region assert c2.temperature == c.temperature + assert all(c2.translation == c.translation) + assert all(c2.rotation == c.rotation) + assert c2.volume == c.volume c3 = c.clone(clone_materials=False) assert c3.id != c.id assert c3.fill == c.fill assert c3.region != c.region assert c3.temperature == c.temperature + assert all(c2.translation == c.translation) + assert all(c2.rotation == c.rotation) c4 = c.clone(clone_regions=False) assert c4.id != c.id assert c4.fill != c.fill assert c4.region == c.region assert c4.temperature == c.temperature + assert all(c2.translation == c.translation) + assert all(c2.rotation == c.rotation) + + c5 = c.clone(clone_materials=False, clone_regions=False) + assert c5.id != c.id + assert c5.fill == c.fill + assert c5.region == c.region + assert c5.temperature == c.temperature + assert all(c2.translation == c.translation) + assert all(c2.rotation == c.rotation) + + # Mutate the original to ensure the changes are not seen in the clones + c.fill = openmc.Material() + c.region = +openmc.ZCylinder() + c.translation = [-1,-2,-3] + c.rotation = [-4,-5,-6] + c.temperature = 1 + assert c5.fill != c.fill + assert c5.region != c.region + assert c5.temperature != c.temperature + assert all(c2.translation != c.translation) + assert all(c2.rotation != c.rotation) def test_temperature(cell_with_lattice): From affc62f09cdb66bd2fea9ffdf6b44099b1e0afde Mon Sep 17 00:00:00 2001 From: josh Date: Wed, 4 Jan 2023 22:53:55 +0000 Subject: [PATCH 46/66] only reassign temps when not None --- openmc/cell.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openmc/cell.py b/openmc/cell.py index 8169ca4d2..cea362ed8 100644 --- a/openmc/cell.py +++ b/openmc/cell.py @@ -520,8 +520,9 @@ class Cell(IDManagerMixin): clone = openmc.Cell() clone.name = self.name - clone.temperature = self.temperature clone.volume = self.volume + if self.temperature is not None: + clone.temperature = self.temperature if self.translation is not None: clone.translation = self.translation if self.rotation is not None: From 60f9a4271172d61c2c36dd57ebbe8bc4a02fe587 Mon Sep 17 00:00:00 2001 From: josh Date: Wed, 4 Jan 2023 22:54:26 +0000 Subject: [PATCH 47/66] simplify cell cloning test changes --- tests/unit_tests/test_cell.py | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/tests/unit_tests/test_cell.py b/tests/unit_tests/test_cell.py index eebe0f895..4185e9a44 100644 --- a/tests/unit_tests/test_cell.py +++ b/tests/unit_tests/test_cell.py @@ -57,43 +57,37 @@ def test_clone(): m = openmc.Material() cyl = openmc.ZCylinder() c = openmc.Cell(fill=m, region=-cyl) - c.temperature = 650. - c.translation = (1,2,3) - c.rotation = (4,5,6) - c.volume = 100 + # Check cloning with all optional params as the defaults c2 = c.clone() assert c2.id != c.id assert c2.fill != c.fill assert c2.region != c.region - assert c2.temperature == c.temperature - assert all(c2.translation == c.translation) - assert all(c2.rotation == c.rotation) - assert c2.volume == c.volume c3 = c.clone(clone_materials=False) assert c3.id != c.id assert c3.fill == c.fill assert c3.region != c.region - assert c3.temperature == c.temperature - assert all(c2.translation == c.translation) - assert all(c2.rotation == c.rotation) c4 = c.clone(clone_regions=False) assert c4.id != c.id assert c4.fill != c.fill assert c4.region == c.region - assert c4.temperature == c.temperature - assert all(c2.translation == c.translation) - assert all(c2.rotation == c.rotation) + + # Add optional properties to the original cell to ensure they're cloned successfully + c.temperature = 650. + c.translation = [1,2,3] + c.rotation = [4,5,6] + c.volume = 100 c5 = c.clone(clone_materials=False, clone_regions=False) assert c5.id != c.id assert c5.fill == c.fill assert c5.region == c.region assert c5.temperature == c.temperature - assert all(c2.translation == c.translation) - assert all(c2.rotation == c.rotation) + assert c5.volume == c.volume + assert all(c5.translation == c.translation) + assert all(c5.rotation == c.rotation) # Mutate the original to ensure the changes are not seen in the clones c.fill = openmc.Material() @@ -101,11 +95,13 @@ def test_clone(): c.translation = [-1,-2,-3] c.rotation = [-4,-5,-6] c.temperature = 1 + c.volume = 1 assert c5.fill != c.fill assert c5.region != c.region assert c5.temperature != c.temperature - assert all(c2.translation != c.translation) - assert all(c2.rotation != c.rotation) + assert c5.volume != c.volume + assert all(c5.translation != c.translation) + assert all(c5.rotation != c.rotation) def test_temperature(cell_with_lattice): From 82429cbb19d4ba10aa771a5493a092cd35711b4f Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Thu, 5 Jan 2023 11:16:16 +0000 Subject: [PATCH 48/66] added test for sab xs appears in xs calc --- tests/unit_tests/test_plotter.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 tests/unit_tests/test_plotter.py diff --git a/tests/unit_tests/test_plotter.py b/tests/unit_tests/test_plotter.py new file mode 100644 index 000000000..a3f1ff666 --- /dev/null +++ b/tests/unit_tests/test_plotter.py @@ -0,0 +1,27 @@ +import openmc +import numpy as np + + +def test_calculate_cexs_elem_mat_sab(): + """Checks that sab cross sections are included in the + _calculate_cexs_elem_mat method and have the correct shape""" + + mat_1 = openmc.Material() + mat_1.add_element("H", 4.0, "ao") + mat_1.add_element("O", 4.0, "ao") + mat_1.add_element("C", 4.0, "ao") + + mat_1.add_s_alpha_beta("c_C6H6") + mat_1.set_density("g/cm3", 0.865) + + energy_grid, data = openmc.plotter._calculate_cexs_elem_mat( + mat_1, + ["inelastic"], + sab_name="c_C6H6", + ) + + assert isinstance(energy_grid, np.ndarray) + assert isinstance(data, np.ndarray) + assert len(energy_grid) > 1 + assert len(data) == 1 + assert len(data[0]) == len(energy_grid) From 9fac1bb4d25d7399d7c942e8605c7dc01cd4824a Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Thu, 5 Jan 2023 12:13:55 -0600 Subject: [PATCH 49/66] Updating note on photon transport limitations --- docs/source/usersguide/settings.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/source/usersguide/settings.rst b/docs/source/usersguide/settings.rst index 336982ac6..760a08e90 100644 --- a/docs/source/usersguide/settings.rst +++ b/docs/source/usersguide/settings.rst @@ -474,7 +474,6 @@ selected:: Some features related to photon transport are not currently implemented, including: - * Tallying photon energy deposition. * Generating a photon source from a neutron calculation that can be used for a later fixed source photon calculation. * Photoneutron reactions. From 9615b9f5da9a1bccfd0a693f74c9c0437ec1329a Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Thu, 5 Jan 2023 16:04:33 -0600 Subject: [PATCH 50/66] Adding tests for tally triggers. --- tests/unit_tests/test_triggers.py | 75 +++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 tests/unit_tests/test_triggers.py diff --git a/tests/unit_tests/test_triggers.py b/tests/unit_tests/test_triggers.py new file mode 100644 index 000000000..4fe6e044a --- /dev/null +++ b/tests/unit_tests/test_triggers.py @@ -0,0 +1,75 @@ + +import openmc + +def test_tally_trigger(run_in_tmpdir): + pincell = openmc.examples.pwr_pin_cell() + + # create a tally filter on the materials + mat_filter = openmc.MaterialFilter(pincell.materials) + + # create a tally with triggers applied + tally = openmc.Tally() + tally.filters = [mat_filter] + tally.scores = ['scatter'] + + trigger = openmc.Trigger('rel_err', 0.05) + trigger.scores = ['scatter'] + + tally.triggers = [trigger] + + pincell.tallies = [tally] + + pincell.settings.trigger_active = True + pincell.settings.trigger_max_batches = 100 + pincell.settings.trigger_batch_interval = 5 + + sp_file = pincell.run() + with openmc.StatePoint(sp_file) as sp: + expected_realizations = sp.n_realizations + + # adding other scores to the tally should not change the + # number of batches required to satisfy the trigger + tally.scores = ['total', 'absorption', 'scatter'] + + sp_file = pincell.run() + + with openmc.StatePoint(sp_file) as sp: + realizations = sp.n_realizations + + assert realizations == expected_realizations + + +def test_tally_trigger_null_score(run_in_tmpdir): + pincell = openmc.examples.pwr_pin_cell() + + # create a tally filter on the materials + mat_filter = openmc.MaterialFilter(pincell.materials) + + # apply a tally with a score that be tallied in this model + tally = openmc.Tally() + tally.filters = [mat_filter] + tally.scores = ['pair-production'] + + trigger = openmc.Trigger('rel_err', 0.05) + trigger.scores = ['pair-production'] + + tally.triggers = [trigger] + + pincell.tallies = [tally] + + pincell.settings.trigger_active = True + pincell.settings.trigger_max_batches = 50 + pincell.settings.trigger_batch_interval = 5 + + sp_file = pincell.run() + + with openmc.StatePoint(sp_file) as sp: + # verify that the tally mean is zero + tally_out = sp.get_tally(id=tally.id) + assert all(tally_out.mean == 0.0) + + # we expect that this simulation will run + # up to the max allowed batches + total_batches = sp.n_realizations + sp.n_inactive + assert total_batches == pincell.settings.trigger_max_batches + From bfcbecdbaa002954c5a25edae670d108ea95369a Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Thu, 5 Jan 2023 16:25:15 -0600 Subject: [PATCH 51/66] Fixing tally triggers. Refs #2342 and #2343 --- src/tallies/trigger.cpp | 87 ++++++++++++++++++++++++----------------- 1 file changed, 51 insertions(+), 36 deletions(-) diff --git a/src/tallies/trigger.cpp b/src/tallies/trigger.cpp index 2c155980e..79af65879 100644 --- a/src/tallies/trigger.cpp +++ b/src/tallies/trigger.cpp @@ -37,6 +37,10 @@ std::pair get_tally_uncertainty( int n = tally->n_realizations_; auto mean = sum / n; + + // if the result has no contributions, return an invalid pair + if (mean == 0) return {-1 , -1}; + double std_dev = std::sqrt((sum_sq / n - mean * mean) / (n - 1)); double rel_err = (mean != 0.) ? std_dev / std::abs(mean) : 0.; @@ -68,42 +72,49 @@ void check_tally_triggers(double& ratio, int& tally_id, int& score) const auto& results = t.results_; for (auto filter_index = 0; filter_index < results.shape()[0]; ++filter_index) { - for (auto score_index = 0; score_index < results.shape()[1]; - ++score_index) { - // Compute the tally uncertainty metrics. - auto uncert_pair = - get_tally_uncertainty(i_tally, score_index, filter_index); - double std_dev = uncert_pair.first; - double rel_err = uncert_pair.second; + // Compute the tally uncertainty metrics. + auto uncert_pair = + get_tally_uncertainty(i_tally, trigger.score_index, filter_index); - // Pick out the relevant uncertainty metric for this trigger. - double uncertainty; - switch (trigger.metric) { - case TriggerMetric::variance: - uncertainty = std_dev * std_dev; - break; - case TriggerMetric::standard_deviation: - uncertainty = std_dev; - break; - case TriggerMetric::relative_error: - uncertainty = rel_err; - break; - case TriggerMetric::not_active: - UNREACHABLE(); - } + // if there is a score without contributions, set ratio to inf and + // exit early + if (uncert_pair.first == -1) { + ratio = INFINITY; + score = t.scores_[trigger.score_index]; + tally_id = t.id_; + return; + } - // Compute the uncertainty / threshold ratio. - double this_ratio = uncertainty / trigger.threshold; - if (trigger.metric == TriggerMetric::variance) { - this_ratio = std::sqrt(ratio); - } + double std_dev = uncert_pair.first; + double rel_err = uncert_pair.second; - // If this is the most uncertain value, set the output variables. - if (this_ratio > ratio) { - ratio = this_ratio; - score = t.scores_[trigger.score_index]; - tally_id = t.id_; - } + // Pick out the relevant uncertainty metric for this trigger. + double uncertainty; + switch (trigger.metric) { + case TriggerMetric::variance: + uncertainty = std_dev * std_dev; + break; + case TriggerMetric::standard_deviation: + uncertainty = std_dev; + break; + case TriggerMetric::relative_error: + uncertainty = rel_err; + break; + case TriggerMetric::not_active: + UNREACHABLE(); + } + + // Compute the uncertainty / threshold ratio. + double this_ratio = uncertainty / trigger.threshold; + if (trigger.metric == TriggerMetric::variance) { + this_ratio = std::sqrt(ratio); + } + + // If this is the most uncertain value, set the output variables. + if (this_ratio > ratio) { + ratio = this_ratio; + score = t.scores_[trigger.score_index]; + tally_id = t.id_; } } } @@ -181,9 +192,13 @@ void check_triggers() "eigenvalue", keff_ratio); } else { - msg = fmt::format( - "Triggers unsatisfied, max unc./thresh. is {} for {} in tally {}", - tally_ratio, reaction_name(score), tally_id); + if (tally_ratio == INFINITY) { + msg = fmt::format("Triggers unsatisfied, no result tallied for score {} in tally {}", reaction_name(score), tally_id); + } else{ + msg = fmt::format( + "Triggers unsatisfied, max unc./thresh. is {} for {} in tally {}", + tally_ratio, reaction_name(score), tally_id); + } } write_message(msg, 7); From 62af77310897b6de1ef43906a781da0c9fcfe15f Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 9 Jan 2023 18:16:45 +0700 Subject: [PATCH 52/66] Refactor NCrystal interface code to ncrystal_interface.h/cpp --- CMakeLists.txt | 1 + include/openmc/material.h | 25 ++----- include/openmc/ncrystal_interface.h | 91 +++++++++++++++++++++++ include/openmc/physics.h | 28 +------- include/openmc/settings.h | 8 +-- src/material.cpp | 39 +++------- src/ncrystal_interface.cpp | 108 ++++++++++++++++++++++++++++ src/physics.cpp | 30 ++------ src/settings.cpp | 12 +--- 9 files changed, 229 insertions(+), 113 deletions(-) create mode 100644 include/openmc/ncrystal_interface.h create mode 100644 src/ncrystal_interface.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 1dee3f90e..4737ff47b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -348,6 +348,7 @@ list(APPEND libopenmc_SOURCES src/message_passing.cpp src/mgxs.cpp src/mgxs_interface.cpp + src/ncrystal_interface.cpp src/nuclide.cpp src/output.cpp src/particle.cpp diff --git a/include/openmc/material.h b/include/openmc/material.h index 5326ad6de..8db9bb628 100644 --- a/include/openmc/material.h +++ b/include/openmc/material.h @@ -12,13 +12,10 @@ #include "openmc/bremsstrahlung.h" #include "openmc/constants.h" #include "openmc/memory.h" // for unique_ptr +#include "openmc/ncrystal_interface.h" #include "openmc/particle.h" #include "openmc/vector.h" -#ifdef NCRYSTAL -#include "NCrystal/NCrystal.hh" -#endif - namespace openmc { //============================================================================== @@ -155,25 +152,17 @@ public: //! \return Temperature in [K] double temperature() const; -#ifdef NCRYSTAL //! Get pointer to NCrystal material object //! \return Pointer to NCrystal material object - std::shared_ptr ncrystal_mat() const - { - return ncrystal_mat_; - }; -#endif + const NCrystalMat& ncrystal_mat() const { return ncrystal_mat_; }; //---------------------------------------------------------------------------- // Data - int32_t id_ {C_NONE}; //!< Unique ID - std::string name_; //!< Name of material - vector nuclide_; //!< Indices in nuclides vector - vector element_; //!< Indices in elements vector -#ifdef NCRYSTAL - std::string ncrystal_cfg_; //!< NCrystal configuration string - std::shared_ptr ncrystal_mat_; -#endif + int32_t id_ {C_NONE}; //!< Unique ID + std::string name_; //!< Name of material + vector nuclide_; //!< Indices in nuclides vector + vector element_; //!< Indices in elements vector + NCrystalMat ncrystal_mat_; //!< NCrystal material object xt::xtensor atom_density_; //!< Nuclide atom density in [atom/b-cm] double density_; //!< Total atom density in [atom/b-cm] double density_gpcc_; //!< Total atom density in [g/cm^3] diff --git a/include/openmc/ncrystal_interface.h b/include/openmc/ncrystal_interface.h new file mode 100644 index 000000000..36038d50c --- /dev/null +++ b/include/openmc/ncrystal_interface.h @@ -0,0 +1,91 @@ +#ifndef OPENMC_NCRYSTAL_INTERFACE_H +#define OPENMC_NCRYSTAL_INTERFACE_H + +#ifdef NCRYSTAL +#include "NCrystal/NCRNG.hh" +#include "NCrystal/NCrystal.hh" +#endif + +#include "openmc/particle.h" + +#include // for uint64_t +#include // for numeric_limits +#include + +namespace openmc { + +//============================================================================== +// Constants +//============================================================================== + +extern "C" const bool NCRYSTAL_ENABLED; + +//============================================================================== +// Wrapper class an NCrystal material +//============================================================================== + +class NCrystalMat { +public: + //---------------------------------------------------------------------------- + // Constructors + NCrystalMat() = default; + explicit NCrystalMat(const std::string& cfg); + + //---------------------------------------------------------------------------- + // Methods + +#ifdef NCRYSTAL + //! Return configuration string + std::string cfg() const; + + //! Get cross section from NCrystal material + // + //! \param[in] p Particle object + //! \return Cross section in [b] + double xs(const Particle& p) const; + + // Process scattering event + // + //! \param[in] p Particle object + void scatter(Particle& p) const; + + //! Whether the object holds a valid NCrystal material + operator bool() const; +#else + + //---------------------------------------------------------------------------- + // Trivial methods when compiling without NCRYSTAL + std::string cfg() const + { + return ""; + } + double xs(const Particle& p) const + { + return -1.0; + } + void scatter(Particle& p) const {} + operator bool() const + { + return false; + } +#endif + +private: + //---------------------------------------------------------------------------- + // Data members (only present when compiling with NCrystal support) +#ifdef NCRYSTAL + std::string cfg_; //!< NCrystal configuration string + std::shared_ptr + ptr_; //!< Pointer to NCrystal material object +#endif +}; + +//============================================================================== +// Functions +//============================================================================== + +void ncrystal_update_micro(double xs, NuclideMicroXS& micro); + +} // namespace openmc + +#endif // OPENMC_NCRYSTAL_INTERFACE_H diff --git a/include/openmc/physics.h b/include/openmc/physics.h index 806297c4e..382f0f0dd 100644 --- a/include/openmc/physics.h +++ b/include/openmc/physics.h @@ -8,10 +8,6 @@ #include "openmc/reaction.h" #include "openmc/vector.h" -#ifdef NCRYSTAL -#include "NCrystal/NCRNG.hh" -#endif - namespace openmc { //============================================================================== @@ -100,31 +96,11 @@ void inelastic_scatter(const Nuclide& nuc, const Reaction& rx, Particle& p); void sample_secondary_photons(Particle& p, int i_nuclide); -//!Split or Roulette particles based their weight and the lower weight window -// bound. +// !Split or Roulette particles based their weight and the lower weight window +// bound. //! \param[in] p, particle to be split or rouletted with the weight window. void split_particle(Particle& p); -#ifdef NCRYSTAL -//============================================================================== -// NCrystal wrapper class for the OpenMC random number generator -//============================================================================== - -class NCrystalRNGWrapper : public NCrystal::RNGStream { -public: - constexpr NCrystalRNGWrapper(uint64_t* seed) noexcept : openmc_seed_(seed) {} - -protected: - double actualGenerate() override - { - return std::max( - std::numeric_limits::min(), prn(openmc_seed_)); - } -private: - uint64_t* openmc_seed_; -}; -#endif - } // namespace openmc #endif // OPENMC_PHYSICS_H diff --git a/include/openmc/settings.h b/include/openmc/settings.h index fb5f8387a..9cd430deb 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -20,8 +20,6 @@ namespace openmc { // Global variable declarations //============================================================================== -extern "C" const bool NCRYSTAL_ENABLED; - namespace settings { // Boolean flags @@ -93,6 +91,8 @@ extern int n_log_bins; //!< number of bins for logarithmic energy grid extern int n_batches; //!< number of (inactive+active) batches extern int n_max_batches; //!< Maximum number of batches extern int max_tracks; //!< Maximum number of particle tracks written to file +extern double + ncrystal_max_energy; //!< Energy in eV to switch between NCrystal and ENDF extern ResScatMethod res_scat_method; //!< resonance upscattering method extern double res_scat_energy_min; //!< Min energy in [eV] for res. upscattering extern double res_scat_energy_max; //!< Max energy in [eV] for res. upscattering @@ -124,10 +124,6 @@ extern int trigger_batch_interval; //!< Batch interval for triggers extern "C" int verbosity; //!< How verbose to make output extern double weight_cutoff; //!< Weight cutoff for Russian roulette extern double weight_survive; //!< Survival weight after Russian roulette -#ifdef NCRYSTAL -extern double - ncrystal_max_energy; //!< Energy in eV to switch between NCrystal and ENDF -#endif } // namespace settings //============================================================================== diff --git a/src/material.cpp b/src/material.cpp index 25014c448..2d55d6a04 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -60,13 +60,12 @@ Material::Material(pugi::xml_node node) name_ = get_node_value(node, "name"); } -#ifdef NCRYSTAL if (check_for_node(node, "cfg")) { - ncrystal_cfg_ = get_node_value(node, "cfg"); - write_message(5, "NCrystal config string for material #{}: '{}'", this->id(), ncrystal_cfg_); - ncrystal_mat_ = NCrystal::FactImpl::createScatter(ncrystal_cfg_); + auto cfg = get_node_value(node, "cfg"); + write_message( + 5, "NCrystal config string for material #{}: '{}'", this->id(), cfg); + ncrystal_mat_ = NCrystalMat(cfg); } -#endif if (check_for_node(node, "depletable")) { depletable_ = get_node_value_bool(node, "depletable"); @@ -800,19 +799,11 @@ void Material::calculate_neutron_xs(Particle& p) const // Initialize position in i_sab_nuclides int j = 0; -#ifdef NCRYSTAL - double ncrystal_xs = -1; - + // Calculate NCrystal cross section + double ncrystal_xs = -1.0; if (ncrystal_mat_ && p.E() < settings::ncrystal_max_energy) { - // Calculate scattering XS per atom with NCrystal, only once per material - NCrystal::CachePtr dummy_cache; - auto nc_energy = NCrystal::NeutronEnergy {p.E()}; - ncrystal_xs = - ncrystal_mat_ - ->crossSection(dummy_cache, nc_energy, {p.u().x, p.u().y, p.u().z}) - .get(); + ncrystal_xs = ncrystal_mat_.xs(p); } -#endif // Add contribution from each nuclide in material for (int i = 0; i < nuclide_.size(); ++i) { @@ -852,26 +843,16 @@ void Material::calculate_neutron_xs(Particle& p) const int i_nuclide = nuclide_[i]; // Calculate microscopic cross section for this nuclide -#ifdef NCRYSTAL auto& micro {p.neutron_xs(i_nuclide)}; -#else - const auto& micro {p.neutron_xs(i_nuclide)}; -#endif if (p.E() != micro.last_E || p.sqrtkT() != micro.last_sqrtkT || i_sab != micro.index_sab || sab_frac != micro.sab_frac) { data::nuclides[i_nuclide]->calculate_xs(i_sab, i_grid, sab_frac, p); -#ifdef NCRYSTAL + + // If NCrystal is being used, update micro cross section cache if (ncrystal_xs >= 0.0) { - if (micro.thermal > 0 || micro.thermal_elastic > 0) { - fatal_error("S(a,b) treatment and NCrystal are not compatible."); - } data::nuclides[i_nuclide]->calculate_elastic_xs(p); - // remove free atom cross section - // and replace it by scattering cross section per atom from NCrystal - micro.total = micro.total - micro.elastic + ncrystal_xs; - micro.elastic = ncrystal_xs; + ncrystal_update_micro(ncrystal_xs, micro); } -#endif } // ====================================================================== diff --git a/src/ncrystal_interface.cpp b/src/ncrystal_interface.cpp new file mode 100644 index 000000000..b39f62d90 --- /dev/null +++ b/src/ncrystal_interface.cpp @@ -0,0 +1,108 @@ +#include "openmc/ncrystal_interface.h" + +#include "openmc/error.h" +#include "openmc/material.h" +#include "openmc/random_lcg.h" + +namespace openmc { + +//============================================================================== +// Constants +//============================================================================== + +#ifdef NCRYSTAL +const bool NCRYSTAL_ENABLED = true; +#else +const bool NCRYSTAL_ENABLED = false; +#endif + +//============================================================================== +// NCrystal wrapper class for the OpenMC random number generator +//============================================================================== + +#ifdef NCRYSTAL +class NCrystalRNGWrapper : public NCrystal::RNGStream { +public: + constexpr NCrystalRNGWrapper(uint64_t* seed) noexcept : openmc_seed_(seed) {} + +protected: + double actualGenerate() override + { + return std::max( + std::numeric_limits::min(), prn(openmc_seed_)); + } + +private: + uint64_t* openmc_seed_; +}; +#endif + +//============================================================================== +// NCrystal implementation +//============================================================================== + +NCrystalMat::NCrystalMat(const std::string& cfg) +{ +#ifdef NCRYSTAL + cfg_ = cfg; + ptr_ = NCrystal::FactImpl::createScatter(cfg); +#else + fatal_error("Your build of OpenMC does not support NCrystal materials."); +#endif +} + +#ifdef NCRYSTAL +std::string NCrystalMat::cfg() const +{ + return cfg_; +} + +double NCrystalMat::xs(const Particle& p) const +{ + // Calculate scattering XS per atom with NCrystal, only once per material + NCrystal::CachePtr dummy_cache; + auto nc_energy = NCrystal::NeutronEnergy {p.E()}; + return ptr_->crossSection(dummy_cache, nc_energy, {p.u().x, p.u().y, p.u().z}) + .get(); +} + +void NCrystalMat::scatter(Particle& p) const +{ + NCrystalRNGWrapper rng(p.current_seed()); // Initialize RNG + // create a cache pointer for multi thread physics + NCrystal::CachePtr dummy_cache; + auto nc_energy = NCrystal::NeutronEnergy {p.E()}; + auto outcome = ptr_->sampleScatter( + dummy_cache, rng, nc_energy, {p.u().x, p.u().y, p.u().z}); + + // Modify attributes of particle + p.E() = outcome.ekin.get(); + Direction u_old {p.u()}; + p.u() = + Direction(outcome.direction[0], outcome.direction[1], outcome.direction[2]); + p.mu() = u_old.dot(p.u()); + p.event_mt() = ELASTIC; +} + +NCrystalMat::operator bool() const +{ + return ptr_.get(); +} +#endif + +//============================================================================== +// Functions +//============================================================================== + +void ncrystal_update_micro(double xs, NuclideMicroXS& micro) +{ + if (micro.thermal > 0 || micro.thermal_elastic > 0) { + fatal_error("S(a,b) treatment and NCrystal are not compatible."); + } + // remove free atom cross section + // and replace it by scattering cross section per atom from NCrystal + micro.total = micro.total - micro.elastic + xs; + micro.elastic = xs; +} + +} // namespace openmc diff --git a/src/physics.cpp b/src/physics.cpp index c1ef282f7..46bf37aa9 100644 --- a/src/physics.cpp +++ b/src/physics.cpp @@ -10,6 +10,7 @@ #include "openmc/material.h" #include "openmc/math_functions.h" #include "openmc/message_passing.h" +#include "openmc/ncrystal_interface.h" #include "openmc/nuclide.h" #include "openmc/photon.h" #include "openmc/physics_common.h" @@ -138,32 +139,15 @@ void sample_neutron_reaction(Particle& p) if (!p.alive()) return; - // Sample a scattering reaction and determine the secondary energy of the - // exiting neutron - -#ifdef NCRYSTAL - if (model::materials[p.material()]->ncrystal_mat() && - p.E() < settings::ncrystal_max_energy) { - NCrystalRNGWrapper rng(p.current_seed()); // Initialize RNG - // create a cache pointer for multi thread physics - NCrystal::CachePtr dummy_cache; - auto nc_energy = NCrystal::NeutronEnergy {p.E()}; - auto outcome = - model::materials[p.material()]->ncrystal_mat()->sampleScatter( - dummy_cache, rng, nc_energy, {p.u().x, p.u().y, p.u().z}); - p.E_last() = p.E(); - p.E() = outcome.ekin.get(); - Direction u_old {p.u()}; - p.u() = Direction( - outcome.direction[0], outcome.direction[1], outcome.direction[2]); - p.mu() = u_old.dot(p.u()); - p.event_mt() = ELASTIC; + // Sample a scattering reaction and determine the secondary energy of the + // exiting neutron + const auto& ncrystal_mat = model::materials[p.material()]->ncrystal_mat(); + if (ncrystal_mat && p.E() < settings::ncrystal_max_energy) { + ncrystal_mat.scatter(p); } else { scatter(p, i_nuclide); } -#else - scatter(p, i_nuclide); -#endif + // Advance URR seed stream 'N' times after energy changes if (p.E() != p.E_last()) { p.stream() = STREAM_URR_PTABLE; diff --git a/src/settings.cpp b/src/settings.cpp index 6f6e4e090..9174d4114 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -37,13 +37,6 @@ namespace openmc { // Global variables //============================================================================== -#ifdef NCRYSTAL -const bool NCRYSTAL_ENABLED = true; -#else -const bool NCRYSTAL_ENABLED = false; -#endif - - namespace settings { // Default values for boolean flags @@ -106,6 +99,7 @@ int n_batches; int n_max_batches; int max_splits {1000}; int max_tracks {1000}; +double ncrystal_max_energy {5.0}; ResScatMethod res_scat_method {ResScatMethod::rvs}; double res_scat_energy_min {0.01}; double res_scat_energy_max {1000.0}; @@ -128,10 +122,6 @@ int verbosity {7}; double weight_cutoff {0.25}; double weight_survive {1.0}; -#ifdef NCRYSTAL -double ncrystal_max_energy {5.0}; -#endif - } // namespace settings //============================================================================== From 4eda693133a3a0f6c55eea084ca7ddeb3ab0769d Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 10 Jan 2023 10:39:22 +0700 Subject: [PATCH 53/66] Style and spacing changes --- docs/source/methods/cross_sections.rst | 27 ++++++++++++++----------- docs/source/usersguide/install.rst | 11 +++++----- docs/source/usersguide/materials.rst | 20 +++++++++--------- include/openmc/physics.h | 5 +++-- tests/regression_tests/ncrystal/test.py | 16 +++++++++------ 5 files changed, 45 insertions(+), 34 deletions(-) diff --git a/docs/source/methods/cross_sections.rst b/docs/source/methods/cross_sections.rst index 0b87913e5..2cafa8691 100644 --- a/docs/source/methods/cross_sections.rst +++ b/docs/source/methods/cross_sections.rst @@ -182,19 +182,22 @@ been selected. There are three methods available: NCrystal materials ------------------ -As an alternative of the standard thermal scattering treatment using :math:`S(\alpha,\beta)` -tables, OpenMC allows to create materials using NCrystal_. In addition to the regular thermal elastic, -and thermal inelastic processes, NCrystal allows the generation of models for materials that -cannot currently included in ACE files such as oriented single crystals (see the `NCrystal paper`_), -and further extend the physics `using plugins`_. Thermal scattering kernels are generated on -the fly from dynamic and structural data, or loaded from :math:`S(\alpha,\beta)` tables converted -from ENDF6 evaluations. These kernels are sampled in a direct way using a fast `rejection algorithm`_ -that does not require previous processing. A `large library`_ of materials is already included in -the NCrystal distribution, and new materials can be easily defined from scratch in the `NCMAT format`_ -or `combining existing files`_. +As an alternative of the standard thermal scattering treatment using +:math:`S(\alpha,\beta)` tables, OpenMC allows to create materials using +NCrystal_. In addition to the regular thermal elastic, and thermal inelastic +processes, NCrystal allows the generation of models for materials that cannot +currently included in ACE files such as oriented single crystals (see the +`NCrystal paper`_), and further extend the physics `using plugins`_. Thermal +scattering kernels are generated on the fly from dynamic and structural data, or +loaded from :math:`S(\alpha,\beta)` tables converted from ENDF6 evaluations. +These kernels are sampled in a direct way using a fast `rejection algorithm`_ +that does not require previous processing. A `large library`_ of materials is +already included in the NCrystal distribution, and new materials can be easily +defined from scratch in the `NCMAT format`_ or `combining existing files`_. -The compositions of the materials defined in NCrystal are passed on to OpenMC all other reactions -except for thermal neutron scattering are handled by continuous energy ACE libraries. +The compositions of the materials defined in NCrystal are passed on to OpenMC +all other reactions except for thermal neutron scattering are handled by +continuous energy ACE libraries. ---------------- Multi-Group Data diff --git a/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index 9e5ba1ffd..537dda824 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -274,9 +274,10 @@ Prerequisites * NCrystal_ library for defining materials with enhanced thermal neutron transport Adding this option allows the creation of materials from NCrystal, which - replaces the scattering kernel treatment of ACE files with a modular, on-the-fly approach. - To use it `install `_ and - `initialize `_ + replaces the scattering kernel treatment of ACE files with a modular, + on-the-fly approach. To use it `install + `_ and `initialize + `_ NCrystal and turn on the option in the CMake configuration step: cmake -DOPENMC_USE_NCRYSTAL=on .. @@ -374,8 +375,8 @@ OPENMC_USE_DAGMC (Default: off) OPENMC_USE_NCRYSTAL - Turns on support for NCrystal materials. NCrystal must be - `installed `_ and + Turns on support for NCrystal materials. NCrystal must be + `installed `_ and `initialized `_. (Default: off) diff --git a/docs/source/usersguide/materials.rst b/docs/source/usersguide/materials.rst index fbf9effd8..83af55805 100644 --- a/docs/source/usersguide/materials.rst +++ b/docs/source/usersguide/materials.rst @@ -105,12 +105,14 @@ you would need to add hydrogen and oxygen to a material and then assign the Adding NCrystal materials ------------------------- -Additional support for thermal scattering can be added by using NCrystal_. -The :meth:`Material.from_ncrystal` class method generates a :class:`openmc.Material` object from -an `NCrystal configuration string `_. -Temperature, material composition, and density are passed from the configuration string -and the `NCMAT file `_ -that define the material, e.g.:: +Additional support for thermal scattering can be added by using NCrystal_. The +:meth:`Material.from_ncrystal` class method generates a :class:`openmc.Material` +object from an `NCrystal configuration string +`_. +Temperature, material composition, and density are passed from the configuration +string and the `NCMAT file +`_ that define the +material, e.g.:: mat = openmc.Material.from_ncrystal('Al_sg225.ncmat;temp=300K') @@ -125,12 +127,12 @@ defines a material containing polycrystalline alumnium, defines an oriented germanium single crystal with 40 arcsec mosaicity. NCrystal only handles low energy neutron interactions. Other interactions are -provided by standard ACE files. NCrystal_ comes with a `predefined library +provided by standard ACE files. NCrystal_ comes with a `predefined library `_ but more materials can be added by creating NCMAT files or on-the-fly in the configuration string. -.. warning:: Currently, NCrystal_ materials cannot be modified after they are created. - Density, temperature and composition should be defined in the +.. warning:: Currently, NCrystal_ materials cannot be modified after they are created. + Density, temperature and composition should be defined in the configuration string or the NCMAT file. .. _NCrystal: https://github.com/mctools/ncrystal diff --git a/include/openmc/physics.h b/include/openmc/physics.h index 382f0f0dd..6e7327d38 100644 --- a/include/openmc/physics.h +++ b/include/openmc/physics.h @@ -96,8 +96,9 @@ void inelastic_scatter(const Nuclide& nuc, const Reaction& rx, Particle& p); void sample_secondary_photons(Particle& p, int i_nuclide); -// !Split or Roulette particles based their weight and the lower weight window -// bound. +//! Split or Roulette particles based their weight and the lower weight window +//! bound. +// //! \param[in] p, particle to be split or rouletted with the weight window. void split_particle(Particle& p); diff --git a/tests/regression_tests/ncrystal/test.py b/tests/regression_tests/ncrystal/test.py index 2a789dac7..cb6421b03 100644 --- a/tests/regression_tests/ncrystal/test.py +++ b/tests/regression_tests/ncrystal/test.py @@ -1,3 +1,5 @@ +from math import pi + import numpy as np import openmc import openmc.lib @@ -9,6 +11,7 @@ pytestmark = pytest.mark.skipif( not openmc.lib._ncrystal_enabled(), reason="NCrystal materials are not enabled.") + def pencil_beam_model(cfg, E0, N): """Return an openmc.Model() object for a monoenergetic pencil beam hitting a 1 mm sphere filled with the material defined by @@ -24,7 +27,7 @@ def pencil_beam_model(cfg, E0, N): sample_sphere = openmc.Sphere(r=0.1) outer_sphere = openmc.Sphere(r=100, boundary_type="vacuum") cell1 = openmc.Cell(region=-sample_sphere, fill=m1) - cell2_region= +sample_sphere & -outer_sphere + cell2_region = +sample_sphere & -outer_sphere cell2 = openmc.Cell(region=cell2_region, fill=None) geometry = openmc.Geometry([cell1, cell2]) @@ -48,7 +51,7 @@ def pencil_beam_model(cfg, E0, N): tally1 = openmc.Tally(name="angular distribution") tally1.scores = ["current"] filter1 = openmc.SurfaceFilter(sample_sphere) - filter2 = openmc.PolarFilter(np.linspace(0, np.pi, 180+1)) + filter2 = openmc.PolarFilter(np.linspace(0, pi, 180+1)) filter3 = openmc.CellFromFilter(cell1) tally1.filters = [filter1, filter2, filter3] tallies = openmc.Tallies([tally1]) @@ -66,11 +69,12 @@ class NCrystalTest(PyAPITestHarness): df = tal.get_pandas_dataframe() return df.to_string() + def test_ncrystal(): - NParticles = 100000 - T = 293.6 # K - E0 = 0.012 # eV + n_particles = 100000 + T = 293.6 # K + E0 = 0.012 # eV cfg = 'Al_sg225.ncmat' - test = pencil_beam_model(cfg, E0, NParticles) + test = pencil_beam_model(cfg, E0, n_particles) harness = NCrystalTest('statepoint.10.h5', model=test) harness.main() From c4227f7dc1b30879dd7492730e8dbb02b14006d5 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 10 Jan 2023 10:39:45 +0700 Subject: [PATCH 54/66] Change Material.from_ncrystal to accept kwargs --- openmc/material.py | 51 +++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/openmc/material.py b/openmc/material.py index 68ec199c3..7844ec131 100644 --- a/openmc/material.py +++ b/openmc/material.py @@ -99,6 +99,10 @@ class Material(IDManagerMixin): [decay/sec]. .. versionadded:: 0.13.2 + ncrystal_cfg : str + NCrystal configuration string + + .. versionadded:: 0.13.3 """ @@ -141,7 +145,8 @@ class Material(IDManagerMixin): string += '{: <16}\n'.format('\tS(a,b) Tables') - string += '{: <16}=\t{}\n'.format('\tNCrystal conf', self._ncrystal_cfg) + if self._ncrystal_cfg: + string += '{: <16}=\t{}\n'.format('\tNCrystal conf', self._ncrystal_cfg) for sab in self._sab: string += '{: <16}=\t{}\n'.format('\tS(a,b)', sab) @@ -222,6 +227,10 @@ class Material(IDManagerMixin): def volume(self): return self._volume + @property + def ncrystal_cfg(self): + return self._ncrystal_cfg + @name.setter def name(self, name: Optional[str]): if name is not None: @@ -335,9 +344,9 @@ class Material(IDManagerMixin): return material @classmethod - def from_ncrystal(cls, cfg, material_id=None, name=''): + def from_ncrystal(cls, cfg, **kwargs): """Create material from NCrystal configuration string. - + Density, temperature, and material composition, and (ultimately) thermal neutron scattering will be automatically be provided by NCrystal based on this string. The name and material_id parameters are simply passed on @@ -347,12 +356,8 @@ class Material(IDManagerMixin): ---------- cfg : str NCrystal configuration string - material_id : int, optional - Unique identifier for the material. If not specified, an identifier will - automatically be assigned. - name : str, optional - Name of the material. If not specified, the name will be the empty - string. + **kwargs + Keyword arguments passed to :class:`openmc.Material` Returns ------- @@ -371,26 +376,20 @@ class Material(IDManagerMixin): #only get invoked in the unlikely case where a material is specified #by referring both to natural elements and specific isotopes of the #same element. - elem_name = openmc.data.ATOMIC_SYMBOL.get(Z) - if not elem_name: - raise ValueError( f'Element with Z={Z} is not known' ) + elem_name = openmc.data.ATOMIC_SYMBOL[Z] return [ (int(iso_name[len(elem_name):]), abund) for iso_name, abund in openmc.data.isotopes(elem_name) ] - flat_compos = nc_mat.getFlattenedComposition(preferNaturalElements = True, - naturalAbundProvider=openmc_natabund) + flat_compos = nc_mat.getFlattenedComposition( + preferNaturalElements=True, naturalAbundProvider=openmc_natabund) # Create the Material - material = cls(material_id=material_id, - name=name, - temperature=nc_mat.getTemperature()) + material = cls(temperature=nc_mat.getTemperature(), **kwargs) for Z, A_vals in flat_compos: - elemname = openmc.data.ATOMIC_SYMBOL.get(Z) - if not elemname: - raise ValueError(f'Element with Z={Z} is not known') + elemname = openmc.data.ATOMIC_SYMBOL[Z] for A, frac in A_vals: if A: material.add_nuclide(f'{elemname}{A}', frac) @@ -1067,7 +1066,7 @@ class Material(IDManagerMixin): activity[nuclide] = inv_seconds * 1e24 * atoms_per_bcm * multiplier return activity if by_nuclide else sum(activity.values()) - + def get_decay_heat(self, units: str = 'W', by_nuclide: bool = False): """Returns the decay heat of the material or for each nuclide in the material in units of [W], [W/g] or [W/cm3]. @@ -1101,16 +1100,16 @@ class Material(IDManagerMixin): multiplier = 1 elif units == 'W/g': multiplier = 1.0 / self.get_mass_density() - - decayheat = {} + + decayheat = {} for nuclide, atoms_per_bcm in self.get_nuclide_atom_densities().items(): decay_erg = openmc.data.decay_energy(nuclide) inv_seconds = openmc.data.decay_constant(nuclide) decay_erg *= openmc.data.JOULE_PER_EV decayheat[nuclide] = inv_seconds * decay_erg * 1e24 * atoms_per_bcm * multiplier - return decayheat if by_nuclide else sum(decayheat.values()) - + return decayheat if by_nuclide else sum(decayheat.values()) + def get_nuclide_atoms(self): """Return number of atoms of each nuclide in the material @@ -1651,4 +1650,4 @@ class Materials(cv.CheckedList): tree = ET.parse(path) root = tree.getroot() - return cls.from_xml_element(root) \ No newline at end of file + return cls.from_xml_element(root) From e83ced88dc58d0a9fcf83f04390a5284b30d06b9 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 11 Jan 2023 01:17:53 +0700 Subject: [PATCH 55/66] Change ncrystal_max_energy to be a global constant --- include/openmc/ncrystal_interface.h | 3 +++ include/openmc/settings.h | 2 -- src/material.cpp | 2 +- src/physics.cpp | 2 +- src/settings.cpp | 1 - 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/openmc/ncrystal_interface.h b/include/openmc/ncrystal_interface.h index 36038d50c..5a3882df9 100644 --- a/include/openmc/ncrystal_interface.h +++ b/include/openmc/ncrystal_interface.h @@ -20,6 +20,9 @@ namespace openmc { extern "C" const bool NCRYSTAL_ENABLED; +//! Energy in [eV] to switch between NCrystal and ENDF +constexpr double NCRYSTAL_MAX_ENERGY {5.0}; + //============================================================================== // Wrapper class an NCrystal material //============================================================================== diff --git a/include/openmc/settings.h b/include/openmc/settings.h index 9cd430deb..90f1e8c29 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -91,8 +91,6 @@ extern int n_log_bins; //!< number of bins for logarithmic energy grid extern int n_batches; //!< number of (inactive+active) batches extern int n_max_batches; //!< Maximum number of batches extern int max_tracks; //!< Maximum number of particle tracks written to file -extern double - ncrystal_max_energy; //!< Energy in eV to switch between NCrystal and ENDF extern ResScatMethod res_scat_method; //!< resonance upscattering method extern double res_scat_energy_min; //!< Min energy in [eV] for res. upscattering extern double res_scat_energy_max; //!< Max energy in [eV] for res. upscattering diff --git a/src/material.cpp b/src/material.cpp index 2d55d6a04..74a8d7e35 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -801,7 +801,7 @@ void Material::calculate_neutron_xs(Particle& p) const // Calculate NCrystal cross section double ncrystal_xs = -1.0; - if (ncrystal_mat_ && p.E() < settings::ncrystal_max_energy) { + if (ncrystal_mat_ && p.E() < NCRYSTAL_MAX_ENERGY) { ncrystal_xs = ncrystal_mat_.xs(p); } diff --git a/src/physics.cpp b/src/physics.cpp index 46bf37aa9..7cb8040f6 100644 --- a/src/physics.cpp +++ b/src/physics.cpp @@ -142,7 +142,7 @@ void sample_neutron_reaction(Particle& p) // Sample a scattering reaction and determine the secondary energy of the // exiting neutron const auto& ncrystal_mat = model::materials[p.material()]->ncrystal_mat(); - if (ncrystal_mat && p.E() < settings::ncrystal_max_energy) { + if (ncrystal_mat && p.E() < NCRYSTAL_MAX_ENERGY) { ncrystal_mat.scatter(p); } else { scatter(p, i_nuclide); diff --git a/src/settings.cpp b/src/settings.cpp index 9174d4114..2fda352ce 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -99,7 +99,6 @@ int n_batches; int n_max_batches; int max_splits {1000}; int max_tracks {1000}; -double ncrystal_max_energy {5.0}; ResScatMethod res_scat_method {ResScatMethod::rvs}; double res_scat_energy_min {0.01}; double res_scat_energy_max {1000.0}; From a0afa5de441bbd584721bf7f6c029e966675291f Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Tue, 10 Jan 2023 19:04:12 +0000 Subject: [PATCH 56/66] added get_surfaces_by_name method --- openmc/geometry.py | 21 +++++++++++++++++++++ tests/unit_tests/test_geometry.py | 16 ++++++++++++---- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/openmc/geometry.py b/openmc/geometry.py index a43899daa..f9aecca9c 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -525,6 +525,27 @@ class Geometry: """ return self._get_domains_by_name(name, case_sensitive, matching, 'cell') + def get_surfaces_by_name(self, name, case_sensitive=False, matching=False): + """Return a list of surfaces with matching names. + + Parameters + ---------- + name : str + The name to search match + case_sensitive : bool + Whether to distinguish upper and lower case letters in each + surface's name (default is False) + matching : bool + Whether the names must match completely (default is False) + + Returns + ------- + list of openmc.Surface + Surfaces matching the queried name + + """ + return self._get_domains_by_name(name, case_sensitive, matching, 'surface') + def get_cells_by_fill_name(self, name, case_sensitive=False, matching=False): """Return a list of cells with fills with matching names. diff --git a/tests/unit_tests/test_geometry.py b/tests/unit_tests/test_geometry.py index 9db112fd2..f8c523071 100644 --- a/tests/unit_tests/test_geometry.py +++ b/tests/unit_tests/test_geometry.py @@ -159,12 +159,13 @@ def test_get_by_name(): m2 = openmc.Material(name='Zirconium') m2.add_element('Zr', 1.0) - c1 = openmc.Cell(fill=m1, name='cell1') + s1 = openmc.Sphere(name='surface1') + c1 = openmc.Cell(fill=m1, region=-s1, name='cell1') u1 = openmc.Universe(name='Zircaloy universe', cells=[c1]) - cyl = openmc.ZCylinder() - c2 = openmc.Cell(fill=u1, region=-cyl, name='cell2') - c3 = openmc.Cell(fill=m2, region=+cyl, name='Cell3') + s2 = openmc.ZCylinder(name='surface2') + c2 = openmc.Cell(fill=u1, region=-s2, name='cell2') + c3 = openmc.Cell(fill=m2, region=+s2, name='Cell3') root = openmc.Universe(name='root Universe', cells=[c2, c3]) geom = openmc.Geometry(root) @@ -177,6 +178,13 @@ def test_get_by_name(): mats = geom.get_materials_by_name('zirconium', True, True) assert not mats + surfaces = set(geom.get_surfaces_by_name('surface')) + assert not surfaces ^ {s1, s2} + surfaces = set(geom.get_surfaces_by_name('Surface2', False, True)) + assert not surfaces ^ {s2} + surfaces = geom.get_surfaces_by_name('Surface2', True, True) + assert not surfaces + cells = set(geom.get_cells_by_name('cell')) assert not cells ^ {c1, c2, c3} cells = set(geom.get_cells_by_name('cell', True)) From 2c9df1b77e5828ed410dbd00250856265ec211ca Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Wed, 11 Jan 2023 09:32:02 +0000 Subject: [PATCH 57/66] Apply typo fixes identified incode review by @paulromano Co-authored-by: Paul Romano --- openmc/geometry.py | 2 +- tests/unit_tests/test_geometry.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/openmc/geometry.py b/openmc/geometry.py index eb7fa8200..a9817fbbb 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -267,7 +267,7 @@ class Geometry: ---------- path : PathLike, optional Path to geometry XML file - materials : openmc.Materials or or PathLike + materials : openmc.Materials or PathLike Materials used to assign to cells. If PathLike, an attempt is made to generate materials from the provided xml file. diff --git a/tests/unit_tests/test_geometry.py b/tests/unit_tests/test_geometry.py index 843c01def..0e952ab0f 100644 --- a/tests/unit_tests/test_geometry.py +++ b/tests/unit_tests/test_geometry.py @@ -290,7 +290,6 @@ def test_from_xml(run_in_tmpdir, mixed_lattice_model): geom = openmc.Geometry.from_xml(path='geometry.xml', materials=None) assert 'Unable to set "materials" to "None"' in str(excinfo.value) - # checking that the default args also work geom = openmc.Geometry.from_xml() assert isinstance(geom, openmc.Geometry) From 50892c61b7865a92b27e289be88299ad2667343b Mon Sep 17 00:00:00 2001 From: Christopher Fichtlscherer Date: Wed, 11 Jan 2023 13:56:54 -0500 Subject: [PATCH 58/66] unit test and description --- tests/unit_tests/test_settings.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/unit_tests/test_settings.py b/tests/unit_tests/test_settings.py index 1ea66d1f6..15e86ca61 100644 --- a/tests/unit_tests/test_settings.py +++ b/tests/unit_tests/test_settings.py @@ -49,6 +49,7 @@ def test_export_to_xml(run_in_tmpdir): domains=[openmc.Cell()], samples=1000, lower_left=(-10., -10., -10.), upper_right = (10., 10., 10.)) s.create_fission_neutrons = True + s.create_delayed_neutrons = False s.log_grid_bins = 2000 s.photon_transport = False s.electron_treatment = 'led' @@ -107,6 +108,7 @@ def test_export_to_xml(run_in_tmpdir): 'energy_min': 1.0, 'energy_max': 1000.0, 'nuclides': ['U235', 'U238', 'Pu239']} assert s.create_fission_neutrons + assert not s.create_delayed_neutrons assert s.log_grid_bins == 2000 assert not s.photon_transport assert s.electron_treatment == 'led' From 9d86274fcc790e7ba05d030234ec099ef8c09d15 Mon Sep 17 00:00:00 2001 From: Christopher Fichtlscherer Date: Wed, 11 Jan 2023 14:17:29 -0500 Subject: [PATCH 59/66] merge conflict --- openmc/settings.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openmc/settings.py b/openmc/settings.py index 6f3ef5ab4..6271e05dc 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -224,6 +224,10 @@ class Settings: weight_windows : WeightWindows iterable of WeightWindows Weight windows to use for variance reduction + .. versionadded:: 0.13.3 + create_delayed_neutrons : bool + Whether delayed neutrons are created in fission. + .. versionadded:: 0.13 weight_windows_on : bool Whether weight windows are enabled From e266bb15bfcbbdbaa77a0a30c2c9909f60612bb8 Mon Sep 17 00:00:00 2001 From: Christopher Fichtlscherer Date: Wed, 11 Jan 2023 14:50:06 -0500 Subject: [PATCH 60/66] old changes for delayed neutron feature --- docs/source/io_formats/settings.rst | 11 +++++++++++ include/openmc/settings.h | 1 + openmc/settings.py | 23 +++++++++++++++++++++++ src/finalize.cpp | 1 + src/nuclide.cpp | 4 ++-- src/settings.cpp | 7 +++++++ 6 files changed, 45 insertions(+), 2 deletions(-) diff --git a/docs/source/io_formats/settings.rst b/docs/source/io_formats/settings.rst index 71ca61d54..6dff76838 100644 --- a/docs/source/io_formats/settings.rst +++ b/docs/source/io_formats/settings.rst @@ -32,6 +32,17 @@ standard deviation. *Default*: false +------------------------------------- +```` Element +------------------------------------- + +The ```` element indicates whether delayed neutrons +are created in fission. If this element is set to "true", delayed neutrons +will be created in fission events; otherwise only prompt neutrons will be +created. + + *Default*: true + ------------------------------------- ```` Element ------------------------------------- diff --git a/include/openmc/settings.h b/include/openmc/settings.h index 90f1e8c29..806288efe 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -28,6 +28,7 @@ extern bool check_overlaps; //!< check overlaps in geometry? extern bool confidence_intervals; //!< use confidence intervals for results? extern bool create_fission_neutrons; //!< create fission neutrons (fixed source)? +extern bool create_delayed_neutrons; //!< create delayed fission neutrons? extern "C" bool cmfd_run; //!< is a CMFD run? extern bool delayed_photon_scaling; //!< Scale fission photon yield to include delayed diff --git a/openmc/settings.py b/openmc/settings.py index 6271e05dc..3f5873c0a 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -300,6 +300,7 @@ class Settings: VolumeCalculation, 'volume calculations') self._create_fission_neutrons = None + self._create_delayed_neutrons = None self._delayed_photon_scaling = None self._material_cell_offsets = None self._log_grid_bins = None @@ -463,6 +464,10 @@ class Settings: def create_fission_neutrons(self) -> bool: return self._create_fission_neutrons + @property + def create_delayed_neutrons(self) -> bool: + return self._create_delayed_neutrons + @property def delayed_photon_scaling(self) -> bool: return self._delayed_photon_scaling @@ -870,6 +875,12 @@ class Settings: create_fission_neutrons, bool) self._create_fission_neutrons = create_fission_neutrons + @create_delayed_neutrons.setter + def create_delayed_neutrons(self, create_delayed_neutrons: bool): + cv.check_type('Whether create only prompt neutrons', + create_delayed_neutrons, bool) + self._create_delayed_neutrons = create_delayed_neutrons + @delayed_photon_scaling.setter def delayed_photon_scaling(self, value: bool): cv.check_type('delayed photon scaling', value, bool) @@ -1211,6 +1222,11 @@ class Settings: elem = ET.SubElement(root, "create_fission_neutrons") elem.text = str(self._create_fission_neutrons).lower() + def _create_create_delayed_neutrons_subelement(self, root): + if self._create_delayed_neutrons is not None: + elem = ET.SubElement(root, "create_delayed_neutrons") + elem.text = str(self._create_delayed_neutrons).lower() + def _create_delayed_photon_scaling_subelement(self, root): if self._delayed_photon_scaling is not None: elem = ET.SubElement(root, "delayed_photon_scaling") @@ -1536,6 +1552,11 @@ class Settings: if text is not None: self.create_fission_neutrons = text in ('true', '1') + def _create_delayed_neutrons_from_xml_element(self, root): + text = get_text(root, 'create_delayed_neutrons') + if text is not None: + self.create_delayed_neutrons = text in ('true', '1') + def _delayed_photon_scaling_from_xml_element(self, root): text = get_text(root, 'delayed_photon_scaling') if text is not None: @@ -1634,6 +1655,7 @@ class Settings: self._create_resonance_scattering_subelement(element) self._create_volume_calcs_subelement(element) self._create_create_fission_neutrons_subelement(element) + self._create_create_delayed_neutrons_subelement(root_element) self._create_delayed_photon_scaling_subelement(element) self._create_event_based_subelement(element) self._create_max_particles_in_flight_subelement(element) @@ -1726,6 +1748,7 @@ class Settings: settings._ufs_mesh_from_xml_element(elem, meshes) settings._resonance_scattering_from_xml_element(elem) settings._create_fission_neutrons_from_xml_element(elem) + settings._create_delayed_neutrons_from_xml_element(root) settings._delayed_photon_scaling_from_xml_element(elem) settings._event_based_from_xml_element(elem) settings._max_particles_in_flight_from_xml_element(elem) diff --git a/src/finalize.cpp b/src/finalize.cpp index 0c2c62310..59294b28c 100644 --- a/src/finalize.cpp +++ b/src/finalize.cpp @@ -74,6 +74,7 @@ int openmc_finalize() settings::check_overlaps = false; settings::confidence_intervals = false; settings::create_fission_neutrons = true; + settings::create_delayed_neutrons = true; settings::electron_treatment = ElectronTreatment::LED; settings::delayed_photon_scaling = true; settings::energy_cutoff = {0.0, 1000.0, 0.0, 0.0}; diff --git a/src/nuclide.cpp b/src/nuclide.cpp index 134e2d6b9..99ed44b11 100644 --- a/src/nuclide.cpp +++ b/src/nuclide.cpp @@ -519,7 +519,7 @@ double Nuclide::nu(double E, EmissionMode mode, int group) const case EmissionMode::prompt: return (*fission_rx_[0]->products_[0].yield_)(E); case EmissionMode::delayed: - if (n_precursor_ > 0) { + if (n_precursor_ > 0 && settings::create_delayed_neutrons) { auto rx = fission_rx_[0]; if (group >= 1 && group < rx->products_.size()) { // If delayed group specified, determine yield immediately @@ -544,7 +544,7 @@ double Nuclide::nu(double E, EmissionMode mode, int group) const return 0.0; } case EmissionMode::total: - if (total_nu_) { + if (total_nu_ && settings::create_delayed_neutrons) { return (*total_nu_)(E); } else { return (*fission_rx_[0]->products_[0].yield_)(E); diff --git a/src/settings.cpp b/src/settings.cpp index 6386ce52b..1b822f3b9 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -44,6 +44,7 @@ bool assume_separate {false}; bool check_overlaps {false}; bool cmfd_run {false}; bool confidence_intervals {false}; +bool create_delayed_neutrons {true}; bool create_fission_neutrons {true}; bool delayed_photon_scaling {true}; bool entropy_on {false}; @@ -872,6 +873,12 @@ void read_settings_xml(pugi::xml_node root) } } + // Check whether create delayed neutrons in fission + if (check_for_node(root, "create_delayed_neutrons")) { + create_delayed_neutrons = + get_node_value_bool(root, "create_delayed_neutrons"); + } + // Check whether create fission sites if (run_mode == RunMode::FIXED_SOURCE) { if (check_for_node(root, "create_fission_neutrons")) { From 8b1ecaad09546901231dc0890e55fc57622b77bf Mon Sep 17 00:00:00 2001 From: Christopher Fichtlscherer Date: Wed, 11 Jan 2023 15:38:16 -0500 Subject: [PATCH 61/66] deleted white spaces --- openmc/settings.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openmc/settings.py b/openmc/settings.py index 3f5873c0a..2210b23a2 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -465,7 +465,7 @@ class Settings: return self._create_fission_neutrons @property - def create_delayed_neutrons(self) -> bool: + def create_delayed_neutrons(self) -> bool: return self._create_delayed_neutrons @property @@ -875,7 +875,7 @@ class Settings: create_fission_neutrons, bool) self._create_fission_neutrons = create_fission_neutrons - @create_delayed_neutrons.setter + @create_delayed_neutrons.setter def create_delayed_neutrons(self, create_delayed_neutrons: bool): cv.check_type('Whether create only prompt neutrons', create_delayed_neutrons, bool) @@ -1222,7 +1222,7 @@ class Settings: elem = ET.SubElement(root, "create_fission_neutrons") elem.text = str(self._create_fission_neutrons).lower() - def _create_create_delayed_neutrons_subelement(self, root): + def _create_create_delayed_neutrons_subelement(self, root): if self._create_delayed_neutrons is not None: elem = ET.SubElement(root, "create_delayed_neutrons") elem.text = str(self._create_delayed_neutrons).lower() @@ -1552,7 +1552,7 @@ class Settings: if text is not None: self.create_fission_neutrons = text in ('true', '1') - def _create_delayed_neutrons_from_xml_element(self, root): + def _create_delayed_neutrons_from_xml_element(self, root): text = get_text(root, 'create_delayed_neutrons') if text is not None: self.create_delayed_neutrons = text in ('true', '1') From 3ba117fcd95afa1f8aba745dd368db705a3a5102 Mon Sep 17 00:00:00 2001 From: Christopher Fichtlscherer Date: Wed, 11 Jan 2023 15:41:31 -0500 Subject: [PATCH 62/66] indentation level change --- openmc/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmc/settings.py b/openmc/settings.py index 2210b23a2..79d28c44b 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -1222,7 +1222,7 @@ class Settings: elem = ET.SubElement(root, "create_fission_neutrons") elem.text = str(self._create_fission_neutrons).lower() - def _create_create_delayed_neutrons_subelement(self, root): + def _create_create_delayed_neutrons_subelement(self, root): if self._create_delayed_neutrons is not None: elem = ET.SubElement(root, "create_delayed_neutrons") elem.text = str(self._create_delayed_neutrons).lower() From afa490f6a6c98e41062c180eb74fae20ddffdad4 Mon Sep 17 00:00:00 2001 From: Josh May Date: Wed, 11 Jan 2023 13:27:06 -0800 Subject: [PATCH 63/66] Apply suggestions from code review Co-authored-by: Paul Romano --- openmc/cell.py | 3 +-- tests/unit_tests/test_cell.py | 8 ++++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/openmc/cell.py b/openmc/cell.py index cea362ed8..a8e1178f4 100644 --- a/openmc/cell.py +++ b/openmc/cell.py @@ -518,8 +518,7 @@ class Cell(IDManagerMixin): paths = self._paths self._paths = None - clone = openmc.Cell() - clone.name = self.name + clone = openmc.Cell(name=self.name) clone.volume = self.volume if self.temperature is not None: clone.temperature = self.temperature diff --git a/tests/unit_tests/test_cell.py b/tests/unit_tests/test_cell.py index 4185e9a44..cef77f160 100644 --- a/tests/unit_tests/test_cell.py +++ b/tests/unit_tests/test_cell.py @@ -76,8 +76,8 @@ def test_clone(): # Add optional properties to the original cell to ensure they're cloned successfully c.temperature = 650. - c.translation = [1,2,3] - c.rotation = [4,5,6] + c.translation = (1., 2., 3.) + c.rotation = (4., 5., 6.) c.volume = 100 c5 = c.clone(clone_materials=False, clone_regions=False) @@ -92,8 +92,8 @@ def test_clone(): # Mutate the original to ensure the changes are not seen in the clones c.fill = openmc.Material() c.region = +openmc.ZCylinder() - c.translation = [-1,-2,-3] - c.rotation = [-4,-5,-6] + c.translation = (-1., -2., -3.) + c.rotation = (-4., -5., -6.) c.temperature = 1 c.volume = 1 assert c5.fill != c.fill From f7c1a57b15ba1fe5bb697db077bf4cd29d595f79 Mon Sep 17 00:00:00 2001 From: Christopher Fichtlscherer Date: Wed, 11 Jan 2023 16:55:27 -0500 Subject: [PATCH 64/66] settings change element --- openmc/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmc/settings.py b/openmc/settings.py index 79d28c44b..911131387 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -1655,7 +1655,7 @@ class Settings: self._create_resonance_scattering_subelement(element) self._create_volume_calcs_subelement(element) self._create_create_fission_neutrons_subelement(element) - self._create_create_delayed_neutrons_subelement(root_element) + self._create_create_delayed_neutrons_subelement(element) self._create_delayed_photon_scaling_subelement(element) self._create_event_based_subelement(element) self._create_max_particles_in_flight_subelement(element) From 36249d3742dabfe95ee50e2aac822caf72f1e1a9 Mon Sep 17 00:00:00 2001 From: Christopher Fichtlscherer <29277544+cfichtlscherer@users.noreply.github.com> Date: Thu, 12 Jan 2023 06:25:52 -0500 Subject: [PATCH 65/66] Update openmc/settings.py yes Co-authored-by: Paul Romano --- openmc/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmc/settings.py b/openmc/settings.py index 911131387..9330d99b6 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -1748,7 +1748,7 @@ class Settings: settings._ufs_mesh_from_xml_element(elem, meshes) settings._resonance_scattering_from_xml_element(elem) settings._create_fission_neutrons_from_xml_element(elem) - settings._create_delayed_neutrons_from_xml_element(root) + settings._create_delayed_neutrons_from_xml_element(elem) settings._delayed_photon_scaling_from_xml_element(elem) settings._event_based_from_xml_element(elem) settings._max_particles_in_flight_from_xml_element(elem) From a66d4da53f1ce87f6a14902636ab12eb765c895b Mon Sep 17 00:00:00 2001 From: Christopher Fichtlscherer <29277544+cfichtlscherer@users.noreply.github.com> Date: Thu, 12 Jan 2023 06:26:07 -0500 Subject: [PATCH 66/66] Update openmc/settings.py version number Co-authored-by: Paul Romano --- openmc/settings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openmc/settings.py b/openmc/settings.py index 9330d99b6..22a83509f 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -224,11 +224,11 @@ class Settings: weight_windows : WeightWindows iterable of WeightWindows Weight windows to use for variance reduction - .. versionadded:: 0.13.3 + .. versionadded:: 0.13 create_delayed_neutrons : bool Whether delayed neutrons are created in fission. - .. versionadded:: 0.13 + .. versionadded:: 0.13.3 weight_windows_on : bool Whether weight windows are enabled