From 58d751bfaea969b56cc5cb8067ea91301a54ed28 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Thu, 13 Jun 2019 09:15:21 +0100 Subject: [PATCH 01/90] Added internal infrastructure for dlopen based shared object sources --- include/openmc/settings.h | 1 + include/openmc/source.h | 4 +++ src/settings.cpp | 1 + src/source.cpp | 54 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 59 insertions(+), 1 deletion(-) diff --git a/include/openmc/settings.h b/include/openmc/settings.h index cbc0c488b9..689e222830 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -60,6 +60,7 @@ extern std::string path_input; //!< directory where main .xml files r extern std::string path_output; //!< directory where output files are written extern std::string path_particle_restart; //!< path to a particle restart file extern std::string path_source; +extern std::string path_source_library; //!< path to the source shared object extern std::string path_sourcepoint; //!< path to a source file extern "C" std::string path_statepoint; //!< path to a statepoint file diff --git a/include/openmc/source.h b/include/openmc/source.h index a177995ea8..0a73260fd1 100644 --- a/include/openmc/source.h +++ b/include/openmc/source.h @@ -59,6 +59,10 @@ private: //! Initialize source bank from file/distribution extern "C" void initialize_source(); +// as yet uncreated function to sample a source from a shared object +// +// extern "C" Particle::Bank sample_source(); + //! Sample a site from all external source distributions in proportion to their //! source strength //! \param[inout] seed Pseudorandom seed pointer diff --git a/src/settings.cpp b/src/settings.cpp index 5bd53de259..6419e46910 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -74,6 +74,7 @@ std::string path_input; std::string path_output; std::string path_particle_restart; std::string path_source; +std::string path_source_library; std::string path_sourcepoint; std::string path_statepoint; diff --git a/src/source.cpp b/src/source.cpp index b15a15d23a..4666783d4a 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -1,6 +1,8 @@ #include "openmc/source.h" #include // for move +#include // for stringstream +#include // for dlopen #include #include "xtensor/xadapt.hpp" @@ -71,7 +73,14 @@ SourceDistribution::SourceDistribution(pugi::xml_node node) fatal_error(fmt::format("Source file '{}' does not exist.", settings::path_source)); } - + } else if (check_for_node(node, "library")) { + settings::path_source_library = get_node_value(node, "library", false, true); + // check if it exists + if (!file_exists(settings::path_source_library)) { + std::stringstream msg; + msg << "Library file " << settings::path_source_library << "' does not exist."; + fatal_error(msg); + } } else { // Spatial distribution for external source @@ -261,7 +270,50 @@ void initialize_source() // Close file file_close(file_id); + } else if ( settings::path_source_library != "" ) { + // Get the source from a library object + std::stringstream msg; + msg << "Sampling from library source " << settings::path_source << "..."; + write_message(msg, 6); + + // Open the library + void* source_library = dlopen(settings::path_source_library.c_str(),RTLD_LAZY); + if(!source_library) { + std::stringstream msg("Couldnt open source library " + settings::path_source_library); + fatal_error(msg); + } + + // load the symbol + typedef Particle::Bank (*sample_t)(); + + // reset errors + dlerror(); + + // get the function from the library + sample_t sample_source = (sample_t) dlsym(source_library, "sample_source"); + const char *dlsym_error = dlerror(); + + if (dlsym_error) { + dlclose(source_library); + fatal_error(dlsym_error); + } + + // Generation source sites from specified distribution in the + // library source + for (int64_t i = 0; i < simulation::work_per_rank; ++i) { + // initialize random number seed + int64_t id = simulation::total_gen*settings::n_particles + + simulation::work_index[mpi::rank] + i + 1; + set_particle_seed(id); + + // sample external source distribution + simulation::source_bank[i] = sample_source(); + } + + // release the library + dlclose(source_library); + } else { // Generation source sites from specified distribution in user input for (int64_t i = 0; i < simulation::work_per_rank; ++i) { From 325a24bdbcd50f3ae65fc0b8e64c26d66d251968 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Thu, 13 Jun 2019 14:40:15 +0100 Subject: [PATCH 02/90] Added missing fixed source subroutine --- src/source.cpp | 50 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/src/source.cpp b/src/source.cpp index 4666783d4a..b856a8d2ad 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -294,9 +294,11 @@ void initialize_source() sample_t sample_source = (sample_t) dlsym(source_library, "sample_source"); const char *dlsym_error = dlerror(); + // check for any dlsym errors if (dlsym_error) { + std::cout << dlsym_error << std::endl; dlclose(source_library); - fatal_error(dlsym_error); + fatal_error("Couldnt open the sample_source symbol"); } // Generation source sites from specified distribution in the @@ -313,7 +315,7 @@ void initialize_source() // release the library dlclose(source_library); - + } else { // Generation source sites from specified distribution in user input for (int64_t i = 0; i < simulation::work_per_rank; ++i) { @@ -375,8 +377,7 @@ void free_memory_source() void fill_source_bank_fixedsource() { - if (settings::path_source.empty()) { - #pragma omp parallel for + if (settings::path_source.empty() && settings::path_source_library.empty()) { for (int64_t i = 0; i < simulation::work_per_rank; ++i) { // initialize random number seed int64_t id = (simulation::total_gen + overall_generation()) * @@ -386,6 +387,47 @@ void fill_source_bank_fixedsource() // sample external source distribution simulation::source_bank[i] = sample_external_source(&seed); } + } else if (settings::path_source.empty() && !settings::path_source.empty()) { + std::stringstream msg; + + // Open the library + void* source_library = dlopen(settings::path_source_library.c_str(),RTLD_LAZY); + if(!source_library) { + std::stringstream msg("Couldnt open source library " + settings::path_source_library); + fatal_error(msg); + } + + // load the symbol + typedef Particle::Bank (*sample_t)(); + + // reset errors + dlerror(); + + // get the function from the library + sample_t sample_source = (sample_t) dlsym(source_library, "sample_source"); + const char *dlsym_error = dlerror(); + + // check for any dlsym errors + if (dlsym_error) { + std::cout << dlsym_error << std::endl; + dlclose(source_library); + fatal_error("Couldnt open the sample_source symbol"); + } + + // Generation source sites from specified distribution in the + // library source + for (int64_t i = 0; i < simulation::work_per_rank; ++i) { + // initialize random number seed + int64_t id = simulation::total_gen*settings::n_particles + + simulation::work_index[mpi::rank] + i + 1; + set_particle_seed(id); + + // sample external source distribution + simulation::source_bank[i] = sample_source(); + } + + // release the library + dlclose(source_library); } } From 915f7dd1456ee9fb7653d5e4acbfb77155dff42c Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Thu, 13 Jun 2019 17:49:29 +0100 Subject: [PATCH 03/90] Added example on how to use xml based custom source --- examples/xml/custom_source/geometry.xml | 15 +++++++++++++ examples/xml/custom_source/materials.xml | 16 ++++++++++++++ examples/xml/custom_source/settings.xml | 15 +++++++++++++ examples/xml/custom_source/source_ring.cpp | 25 ++++++++++++++++++++++ examples/xml/custom_source/tallies.xml | 17 +++++++++++++++ 5 files changed, 88 insertions(+) create mode 100644 examples/xml/custom_source/geometry.xml create mode 100644 examples/xml/custom_source/materials.xml create mode 100644 examples/xml/custom_source/settings.xml create mode 100644 examples/xml/custom_source/source_ring.cpp create mode 100644 examples/xml/custom_source/tallies.xml diff --git a/examples/xml/custom_source/geometry.xml b/examples/xml/custom_source/geometry.xml new file mode 100644 index 0000000000..b30884f8ca --- /dev/null +++ b/examples/xml/custom_source/geometry.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/examples/xml/custom_source/materials.xml b/examples/xml/custom_source/materials.xml new file mode 100644 index 0000000000..606c676df8 --- /dev/null +++ b/examples/xml/custom_source/materials.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/examples/xml/custom_source/settings.xml b/examples/xml/custom_source/settings.xml new file mode 100644 index 0000000000..f8d497459c --- /dev/null +++ b/examples/xml/custom_source/settings.xml @@ -0,0 +1,15 @@ + + + + fixed source + 10 + 0 + 100000 + + + + + ./source_ring.so + + + diff --git a/examples/xml/custom_source/source_ring.cpp b/examples/xml/custom_source/source_ring.cpp new file mode 100644 index 0000000000..ef2784ea36 --- /dev/null +++ b/examples/xml/custom_source/source_ring.cpp @@ -0,0 +1,25 @@ +#include +#include "openmc/random_lcg.h" +#include "openmc/source.h" +#include "openmc/particle.h" + +// you must have external C linkage here otherwise +// dlopen will not find the file +extern "C" openmc::Particle::Bank sample_source() { + openmc::Particle::Bank particle; + // wgt + particle.particle = openmc::Particle::Type::neutron; + particle.wgt = 1.0; + // position + + double angle = 2.*M_PI*openmc::prn(); + double radius = 3.0; + particle.r.x = radius*std::cos(angle); + particle.r.y = radius*std::sin(angle); + particle.r.z = 0.; + // angle + particle.u = {1.,0,0}; + particle.E = 14.08e6; + particle.delayed_group = 0; + return particle; +} \ No newline at end of file diff --git a/examples/xml/custom_source/tallies.xml b/examples/xml/custom_source/tallies.xml new file mode 100644 index 0000000000..7f6f299261 --- /dev/null +++ b/examples/xml/custom_source/tallies.xml @@ -0,0 +1,17 @@ + + + + + 100 + + + + 0 20.0e6 + + + + 1 2 + flux + + + From 1d998984b288310fcc7f4363af0ee4b7010a3c98 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Fri, 14 Jun 2019 08:33:44 +0100 Subject: [PATCH 04/90] Added python bindngs for library based source --- openmc/source.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/openmc/source.py b/openmc/source.py index 88c2f86119..e0fd2b6c40 100644 --- a/openmc/source.py +++ b/openmc/source.py @@ -44,11 +44,12 @@ class Source(object): """ def __init__(self, space=None, angle=None, energy=None, filename=None, - strength=1.0, particle='neutron'): + library=None, strength=1.0, particle='neutron'): self._space = None self._angle = None self._energy = None self._file = None + self._source_library = None if space is not None: self.space = space @@ -58,6 +59,8 @@ class Source(object): self.energy = energy if filename is not None: self.file = filename + if library is not None: + self.source_library = library self.strength = strength self.particle = particle @@ -65,6 +68,10 @@ class Source(object): def file(self): return self._file + @property + def library(self): + return self._source_library + @property def space(self): return self._space @@ -90,6 +97,11 @@ class Source(object): cv.check_type('source file', filename, str) self._file = filename + @library.setter + def library(self, library_name): + cv.check_type('library', library_name, str) + self._source_library = library_name + @space.setter def space(self, space): cv.check_type('spatial distribution', space, Spatial) @@ -131,6 +143,8 @@ class Source(object): element.set("particle", self.particle) if self.file is not None: element.set("file", self.file) + if self.source_library is not None: + element.set("library", self.source_library) if self.space is not None: element.append(self.space.to_xml_element()) if self.angle is not None: @@ -168,6 +182,10 @@ class Source(object): if filename is not None: source.file = filename + library = get_text(elem, 'library') + if library is not None: + source.source_library = library + space = elem.find('space') if space is not None: source.space = Spatial.from_xml_element(space) From c2ed0ddc75ec10a3e6c7352ec60658d0194837b6 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Fri, 14 Jun 2019 10:52:55 +0100 Subject: [PATCH 05/90] Fix the python part --- openmc/source.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openmc/source.py b/openmc/source.py index e0fd2b6c40..f8ea8ea936 100644 --- a/openmc/source.py +++ b/openmc/source.py @@ -60,7 +60,7 @@ class Source(object): if filename is not None: self.file = filename if library is not None: - self.source_library = library + self.library = library self.strength = strength self.particle = particle @@ -143,8 +143,8 @@ class Source(object): element.set("particle", self.particle) if self.file is not None: element.set("file", self.file) - if self.source_library is not None: - element.set("library", self.source_library) + if self.library is not None: + element.set("library", self.library) if self.space is not None: element.append(self.space.to_xml_element()) if self.angle is not None: @@ -184,7 +184,7 @@ class Source(object): library = get_text(elem, 'library') if library is not None: - source.source_library = library + source.library = library space = elem.find('space') if space is not None: From 4fc83ce67005519a64101330359181b69c5f3c7f Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Thu, 13 Jun 2019 09:15:21 +0100 Subject: [PATCH 06/90] Added internal infrastructure for dlopen based shared object sources --- src/source.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/source.cpp b/src/source.cpp index b856a8d2ad..089dbddef7 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -312,10 +312,9 @@ void initialize_source() // sample external source distribution simulation::source_bank[i] = sample_source(); } - // release the library dlclose(source_library); - + } else { // Generation source sites from specified distribution in user input for (int64_t i = 0; i < simulation::work_per_rank; ++i) { From eb028827f8e06227293d739fdf7a5edfc273b6d7 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Thu, 13 Jun 2019 14:40:15 +0100 Subject: [PATCH 07/90] Added missing fixed source subroutine --- src/source.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/source.cpp b/src/source.cpp index 089dbddef7..b7d1e24990 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -314,7 +314,7 @@ void initialize_source() } // release the library dlclose(source_library); - + } else { // Generation source sites from specified distribution in user input for (int64_t i = 0; i < simulation::work_per_rank; ++i) { From 9ec9fc000f2b5281456bdb463bd3c1b4a4a882f6 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Fri, 14 Jun 2019 08:33:44 +0100 Subject: [PATCH 08/90] Added python bindngs for library based source --- openmc/source.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openmc/source.py b/openmc/source.py index f8ea8ea936..e0fd2b6c40 100644 --- a/openmc/source.py +++ b/openmc/source.py @@ -60,7 +60,7 @@ class Source(object): if filename is not None: self.file = filename if library is not None: - self.library = library + self.source_library = library self.strength = strength self.particle = particle @@ -143,8 +143,8 @@ class Source(object): element.set("particle", self.particle) if self.file is not None: element.set("file", self.file) - if self.library is not None: - element.set("library", self.library) + if self.source_library is not None: + element.set("library", self.source_library) if self.space is not None: element.append(self.space.to_xml_element()) if self.angle is not None: @@ -184,7 +184,7 @@ class Source(object): library = get_text(elem, 'library') if library is not None: - source.library = library + source.source_library = library space = elem.find('space') if space is not None: From 5e98373036e6cecc3973e8022b23fb5f99b4b0ea Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Fri, 14 Jun 2019 10:52:55 +0100 Subject: [PATCH 09/90] Fix the python part --- openmc/source.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openmc/source.py b/openmc/source.py index e0fd2b6c40..f8ea8ea936 100644 --- a/openmc/source.py +++ b/openmc/source.py @@ -60,7 +60,7 @@ class Source(object): if filename is not None: self.file = filename if library is not None: - self.source_library = library + self.library = library self.strength = strength self.particle = particle @@ -143,8 +143,8 @@ class Source(object): element.set("particle", self.particle) if self.file is not None: element.set("file", self.file) - if self.source_library is not None: - element.set("library", self.source_library) + if self.library is not None: + element.set("library", self.library) if self.space is not None: element.append(self.space.to_xml_element()) if self.angle is not None: @@ -184,7 +184,7 @@ class Source(object): library = get_text(elem, 'library') if library is not None: - source.source_library = library + source.library = library space = elem.find('space') if space is not None: From f78d3708a7f2187eaf9c421e5bd9462e9ff46a54 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Mon, 5 Aug 2019 22:44:21 +0100 Subject: [PATCH 10/90] Now writes a Makefile for users to build a source --- CMakeLists.txt | 58 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ec6b500b45..6cc9fceaf5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -406,6 +406,58 @@ install(FILES man/man1/openmc.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) install(FILES LICENSE DESTINATION "${CMAKE_INSTALL_DOCDIR}" RENAME copyright) install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) -# Copy headers for vendored dependencies (note that all except faddeeva are handled -# separately since they are managed by CMake) -install(DIRECTORY vendor/faddeeva DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +#============================= +# write the dl_open Makefile +#============================= +file(WRITE share/Makefile" +# Makefile to build dynamic sources for OpenMC +# this assumes that your source sampling filename is +# source_sampling.cpp +# +# you can add fortran, c and cpp dependencies to this source +# adding them in FC_DEPS, C_DEPS, and CXX_DEPS accordingly + +ifeq ($(FC), f77) +FC = gfortran +endif + +default: all + +ALL = source_sampling +# add your fortran depencies here +FC_DEPS = +# add your c dependencies here +C_DEPS = +# add your cpp dependencies here +CPP_DEPS = +DEPS = $(FC_DEPS) $(C_DEPS) $(CPP_DEPS) +OPT_LEVEL = -O3 +FFLAGS = $(OPT_LEVEL) -fPIC +C_FLAGS = -fPIC +CXX_FLAGS = -fPIC + +OPENMC_DIR = ${CMAKE_INSTALL_PREFIX} +OPENMC_INC_DIR = $(OPENMC_DIR)/include +OPENMC_LIB_DIR = $(OPENMC_DIR)/lib +# setting the so name is important +LINK_FLAGS = $(OPT_LEVEL) -Wall -Wl,-soname,source_sampling.so +# setting shared is important +LINK_FLAGS += -L$(OPENMC_LIB_DIR) -lopenmc -lgfortran -fPIC -shared +OPENMC_INCLUDES = -I$(OPENMC_INC_DIR) -I$(OPENMC_DIR)/vendor/pugixml + +all: $(ALL) + +source_sampling: $(DEPS) + $(CXX) source_sampling.cpp $(DEPS) $(OPENMC_INCLUDES) $(LINK_FLAGS) -o $@.so +# make any fortran objects +%.o : %.F90 + $(FC) -c $(FFLAGS) $*.F90 -o $@ +# make any c objects +%.o : %.c + $(CC) -c $(FFLAGS) $*.c -o $@ +#make any cpp objects +%.o : %.cpp + $(CXX) -c $(FFLAGS) $*.cpp -o $@ +clean: + rm -rf *.o *.mod +") From 8036903b056332988f393d401cff9a97be723f7c Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Thu, 13 Jun 2019 09:15:21 +0100 Subject: [PATCH 11/90] Added internal infrastructure for dlopen based shared object sources --- src/source.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/source.cpp b/src/source.cpp index b7d1e24990..51d86fcaa5 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -296,9 +296,8 @@ void initialize_source() // check for any dlsym errors if (dlsym_error) { - std::cout << dlsym_error << std::endl; dlclose(source_library); - fatal_error("Couldnt open the sample_source symbol"); + fatal_error(dlsym_error); } // Generation source sites from specified distribution in the @@ -314,7 +313,7 @@ void initialize_source() } // release the library dlclose(source_library); - + } else { // Generation source sites from specified distribution in user input for (int64_t i = 0; i < simulation::work_per_rank; ++i) { From 21bf7243eb945fd7dae637b3649978235b978910 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Thu, 13 Jun 2019 14:40:15 +0100 Subject: [PATCH 12/90] Added missing fixed source subroutine --- src/source.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/source.cpp b/src/source.cpp index 51d86fcaa5..b7d1e24990 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -296,8 +296,9 @@ void initialize_source() // check for any dlsym errors if (dlsym_error) { + std::cout << dlsym_error << std::endl; dlclose(source_library); - fatal_error(dlsym_error); + fatal_error("Couldnt open the sample_source symbol"); } // Generation source sites from specified distribution in the @@ -313,7 +314,7 @@ void initialize_source() } // release the library dlclose(source_library); - + } else { // Generation source sites from specified distribution in user input for (int64_t i = 0; i < simulation::work_per_rank; ++i) { From 0545c46fa3344bbd4e62803886c22bf2cf0b3251 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Fri, 14 Jun 2019 08:33:44 +0100 Subject: [PATCH 13/90] Added python bindngs for library based source --- openmc/source.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openmc/source.py b/openmc/source.py index f8ea8ea936..e0fd2b6c40 100644 --- a/openmc/source.py +++ b/openmc/source.py @@ -60,7 +60,7 @@ class Source(object): if filename is not None: self.file = filename if library is not None: - self.library = library + self.source_library = library self.strength = strength self.particle = particle @@ -143,8 +143,8 @@ class Source(object): element.set("particle", self.particle) if self.file is not None: element.set("file", self.file) - if self.library is not None: - element.set("library", self.library) + if self.source_library is not None: + element.set("library", self.source_library) if self.space is not None: element.append(self.space.to_xml_element()) if self.angle is not None: @@ -184,7 +184,7 @@ class Source(object): library = get_text(elem, 'library') if library is not None: - source.library = library + source.source_library = library space = elem.find('space') if space is not None: From a38a543fe3a808092ca7ae22d79d211c40b07e9b Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Fri, 14 Jun 2019 10:52:55 +0100 Subject: [PATCH 14/90] Fix the python part --- openmc/source.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openmc/source.py b/openmc/source.py index e0fd2b6c40..f8ea8ea936 100644 --- a/openmc/source.py +++ b/openmc/source.py @@ -60,7 +60,7 @@ class Source(object): if filename is not None: self.file = filename if library is not None: - self.source_library = library + self.library = library self.strength = strength self.particle = particle @@ -143,8 +143,8 @@ class Source(object): element.set("particle", self.particle) if self.file is not None: element.set("file", self.file) - if self.source_library is not None: - element.set("library", self.source_library) + if self.library is not None: + element.set("library", self.library) if self.space is not None: element.append(self.space.to_xml_element()) if self.angle is not None: @@ -184,7 +184,7 @@ class Source(object): library = get_text(elem, 'library') if library is not None: - source.source_library = library + source.library = library space = elem.find('space') if space is not None: From 510e43127958adab7af9c078ae5e85f173ea0d09 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 7 Aug 2019 08:02:17 +0100 Subject: [PATCH 15/90] missing space --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6cc9fceaf5..605ba0b612 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -409,7 +409,7 @@ install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) #============================= # write the dl_open Makefile #============================= -file(WRITE share/Makefile" +file(WRITE share/Makefile " # Makefile to build dynamic sources for OpenMC # this assumes that your source sampling filename is # source_sampling.cpp From a4f99cb99c3adcda1fb5403c2a70b041d61fa456 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 7 Aug 2019 08:02:38 +0100 Subject: [PATCH 16/90] Adding unit test for dlopen source --- .../dlopen_source/source_sampling.cpp | 23 ++++++++ .../dlopen_source/test_dlopen_source.py | 57 +++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 tests/unit_tests/dlopen_source/source_sampling.cpp create mode 100644 tests/unit_tests/dlopen_source/test_dlopen_source.py diff --git a/tests/unit_tests/dlopen_source/source_sampling.cpp b/tests/unit_tests/dlopen_source/source_sampling.cpp new file mode 100644 index 0000000000..4b13c7db92 --- /dev/null +++ b/tests/unit_tests/dlopen_source/source_sampling.cpp @@ -0,0 +1,23 @@ +#include +#include "openmc/random_lcg.h" +#include "openmc/source.h" +#include "openmc/particle.h" + +// you must have external C linkage here otherwise +// dlopen will not find the file +extern "C" openmc::Particle::Bank sample_source() { + openmc::Particle::Bank particle; + // wgt + particle.particle = openmc::Particle::Type::neutron; + particle.wgt = 1.0; + // position + + particle.r.x = 0.; + particle.r.y = 0.; + particle.r.z = 0.; + // angle + particle.u = {1.,0,0}; + particle.E = 14.08e6; + particle.delayed_group = 0; + return particle; +} diff --git a/tests/unit_tests/dlopen_source/test_dlopen_source.py b/tests/unit_tests/dlopen_source/test_dlopen_source.py new file mode 100644 index 0000000000..917f3fd4fe --- /dev/null +++ b/tests/unit_tests/dlopen_source/test_dlopen_source.py @@ -0,0 +1,57 @@ +import openmc +import pytest +import os + +# compile the external source +def compile_source(): + # needs a more robust way to know where the + # Makefile is + status = os.system('cp /usr/local/share/Makefile .') + assert os.WEXITSTATUS(status) == 0 + status = os.system('make') + assert os.WEXITSTATUS(status) == 0 + + return + +# build the test geometry +def make_geometry(): + mats = openmc.Materials() + + natural_lead = openmc.Material(1, "natural_lead") + natural_lead.add_element('Pb', 1,'ao') + natural_lead.set_density('g/cm3', 11.34) + mats.append(natural_lead) + + # surfaces + surface_sph1 = openmc.Sphere(r=100) + volume_sph1 = -surface_sph1 + + # cell + cell_1 = openmc.Cell(region=volume_sph1) + cell_1.fill = natural_lead #assigning a material to a cell + universe = openmc.Universe(cells=[cell_1]) #hint, this list will need to include the new cell + geom = openmc.Geometry(universe) + + # settings + sett = openmc.Settings() + batches = 10 + sett.batches = batches + sett.inactive = 0 + sett.particles = 1000 + sett.particle = "neutron" + sett.run_mode = 'fixed source' + + #source + source = openmc.Source() + source.library = PATH+'source_sampling.so' + sett.source = source + + # run + model = openmc.model.Model(geom,mats,sett) + return model + +def test_dlopen_source(): + compile_source() + model = make_geometry() + model.run() + From 3b630abc0f97dc642cc612794a5abe5c9729b5c1 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Tue, 15 Oct 2019 20:41:10 +0100 Subject: [PATCH 17/90] make a cmakefile instead of a makefile for cross platform compatibility --- CMakeLists.txt | 59 +++++++------------------------------------------- 1 file changed, 8 insertions(+), 51 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 605ba0b612..18de941218 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -409,55 +409,12 @@ install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) #============================= # write the dl_open Makefile #============================= -file(WRITE share/Makefile " -# Makefile to build dynamic sources for OpenMC -# this assumes that your source sampling filename is -# source_sampling.cpp -# -# you can add fortran, c and cpp dependencies to this source -# adding them in FC_DEPS, C_DEPS, and CXX_DEPS accordingly - -ifeq ($(FC), f77) -FC = gfortran -endif - -default: all - -ALL = source_sampling -# add your fortran depencies here -FC_DEPS = -# add your c dependencies here -C_DEPS = -# add your cpp dependencies here -CPP_DEPS = -DEPS = $(FC_DEPS) $(C_DEPS) $(CPP_DEPS) -OPT_LEVEL = -O3 -FFLAGS = $(OPT_LEVEL) -fPIC -C_FLAGS = -fPIC -CXX_FLAGS = -fPIC - -OPENMC_DIR = ${CMAKE_INSTALL_PREFIX} -OPENMC_INC_DIR = $(OPENMC_DIR)/include -OPENMC_LIB_DIR = $(OPENMC_DIR)/lib -# setting the so name is important -LINK_FLAGS = $(OPT_LEVEL) -Wall -Wl,-soname,source_sampling.so -# setting shared is important -LINK_FLAGS += -L$(OPENMC_LIB_DIR) -lopenmc -lgfortran -fPIC -shared -OPENMC_INCLUDES = -I$(OPENMC_INC_DIR) -I$(OPENMC_DIR)/vendor/pugixml - -all: $(ALL) - -source_sampling: $(DEPS) - $(CXX) source_sampling.cpp $(DEPS) $(OPENMC_INCLUDES) $(LINK_FLAGS) -o $@.so -# make any fortran objects -%.o : %.F90 - $(FC) -c $(FFLAGS) $*.F90 -o $@ -# make any c objects -%.o : %.c - $(CC) -c $(FFLAGS) $*.c -o $@ -#make any cpp objects -%.o : %.cpp - $(CXX) -c $(FFLAGS) $*.cpp -o $@ -clean: - rm -rf *.o *.mod +file(WRITE ${CMAKE_INSTALL_PREFIX}/share/CMakeLists.txt " +cmake_minimum_required(VERSION 3.3 FATAL_ERROR) +project(openmc_sources C CXX) +add_library(source SHARED \$\{SOURCE_FILES\}) +target_include_directories(source PUBLIC ${PROJECT_SOURCE_DIR}/include + ${PROJECT_SOURCE_DIR}/vendor/pugixml ${HDF5_INCLUDE_DIRS}) +target_link_libraries(source ${ldflags} ${HDF5_LIBRARIES} ${HDF5_HL_LIBRARIES}) ") + From e4b0b4f276f5acbc3c5d466ce433db11962c7349 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Tue, 15 Oct 2019 22:00:07 +0100 Subject: [PATCH 18/90] Added regression tests for dlopen source --- .../source_dlopen/inputs_true.dat | 23 ++++++ .../source_dlopen/results_true.dat | 0 .../source_dlopen}/source_sampling.cpp | 0 tests/regression_tests/source_dlopen/test.py | 70 +++++++++++++++++++ .../dlopen_source/test_dlopen_source.py | 57 --------------- 5 files changed, 93 insertions(+), 57 deletions(-) create mode 100644 tests/regression_tests/source_dlopen/inputs_true.dat create mode 100644 tests/regression_tests/source_dlopen/results_true.dat rename tests/{unit_tests/dlopen_source => regression_tests/source_dlopen}/source_sampling.cpp (100%) create mode 100644 tests/regression_tests/source_dlopen/test.py delete mode 100644 tests/unit_tests/dlopen_source/test_dlopen_source.py diff --git a/tests/regression_tests/source_dlopen/inputs_true.dat b/tests/regression_tests/source_dlopen/inputs_true.dat new file mode 100644 index 0000000000..23f3d84384 --- /dev/null +++ b/tests/regression_tests/source_dlopen/inputs_true.dat @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + fixed source + 1000 + 10 + 0 + + diff --git a/tests/regression_tests/source_dlopen/results_true.dat b/tests/regression_tests/source_dlopen/results_true.dat new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/unit_tests/dlopen_source/source_sampling.cpp b/tests/regression_tests/source_dlopen/source_sampling.cpp similarity index 100% rename from tests/unit_tests/dlopen_source/source_sampling.cpp rename to tests/regression_tests/source_dlopen/source_sampling.cpp diff --git a/tests/regression_tests/source_dlopen/test.py b/tests/regression_tests/source_dlopen/test.py new file mode 100644 index 0000000000..ac63ca8045 --- /dev/null +++ b/tests/regression_tests/source_dlopen/test.py @@ -0,0 +1,70 @@ +import openmc +import pytest +import os +import glob + +from tests.testing_harness import PyAPITestHarness + +# compile the external source +def compile_source(): + # first one should be CMakelists in share/ + files = glob.glob('../../../*/CMakeLists.txt') + assert len(files) != 0 + + # copy the cmakefile + status = os.system('rm -rf build') + assert os.WEXITSTATUS(status) == 0 + status = os.system('mkdir build ; cd build ; cp ../'+files[0]+' .') + assert os.WEXITSTATUS(status) == 0 + status = os.system('cd build ; cmake -DSOURCE_FILES=../source_sampling.cpp ; make') + assert os.WEXITSTATUS(status) == 0 + status = os.system('cp build/libsource.so .') + assert os.WEXITSTATUS(status) == 0 + status = os.system('rm -rf build') + assert os.WEXITSTATUS(status) == 0 + + return 0 + +class SourceTestHarness(PyAPITestHarness): + # build the test geometry + def _build_inputs(self): + mats = openmc.Materials() + + natural_lead = openmc.Material(1, "natural_lead") + natural_lead.add_element('Pb', 1,'ao') + natural_lead.set_density('g/cm3', 11.34) + mats.append(natural_lead) + + # surfaces + surface_sph1 = openmc.Sphere(r=100,boundary_type='vacuum') + volume_sph1 = -surface_sph1 + + # cell + cell_1 = openmc.Cell(region=volume_sph1) + cell_1.fill = natural_lead #assigning a material to a cell + universe = openmc.Universe(cells=[cell_1]) #hint, this list will need to include the new cell + geom = openmc.Geometry(universe) + + # settings + sett = openmc.Settings() + batches = 10 + sett.batches = batches + sett.inactive = 0 + sett.particles = 1000 + sett.particle = "neutron" + sett.run_mode = 'fixed source' + + #source + source = openmc.Source() + source.library = './libsource.so' + sett.source = source + + # run + model = openmc.model.Model(geom,mats,sett) + model.export_to_xml() + return + +def test_dlopen_source(): + compile_source() + harness = SourceTestHarness('statepoint.10.h5') + harness.main() diff --git a/tests/unit_tests/dlopen_source/test_dlopen_source.py b/tests/unit_tests/dlopen_source/test_dlopen_source.py deleted file mode 100644 index 917f3fd4fe..0000000000 --- a/tests/unit_tests/dlopen_source/test_dlopen_source.py +++ /dev/null @@ -1,57 +0,0 @@ -import openmc -import pytest -import os - -# compile the external source -def compile_source(): - # needs a more robust way to know where the - # Makefile is - status = os.system('cp /usr/local/share/Makefile .') - assert os.WEXITSTATUS(status) == 0 - status = os.system('make') - assert os.WEXITSTATUS(status) == 0 - - return - -# build the test geometry -def make_geometry(): - mats = openmc.Materials() - - natural_lead = openmc.Material(1, "natural_lead") - natural_lead.add_element('Pb', 1,'ao') - natural_lead.set_density('g/cm3', 11.34) - mats.append(natural_lead) - - # surfaces - surface_sph1 = openmc.Sphere(r=100) - volume_sph1 = -surface_sph1 - - # cell - cell_1 = openmc.Cell(region=volume_sph1) - cell_1.fill = natural_lead #assigning a material to a cell - universe = openmc.Universe(cells=[cell_1]) #hint, this list will need to include the new cell - geom = openmc.Geometry(universe) - - # settings - sett = openmc.Settings() - batches = 10 - sett.batches = batches - sett.inactive = 0 - sett.particles = 1000 - sett.particle = "neutron" - sett.run_mode = 'fixed source' - - #source - source = openmc.Source() - source.library = PATH+'source_sampling.so' - sett.source = source - - # run - model = openmc.model.Model(geom,mats,sett) - return model - -def test_dlopen_source(): - compile_source() - model = make_geometry() - model.run() - From 7adc619c880f81bcded31c2af591b76d61f59b00 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Tue, 15 Oct 2019 22:01:43 +0100 Subject: [PATCH 19/90] Stylistic changes --- src/source.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/source.cpp b/src/source.cpp index b7d1e24990..464a39948f 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -77,9 +77,9 @@ SourceDistribution::SourceDistribution(pugi::xml_node node) settings::path_source_library = get_node_value(node, "library", false, true); // check if it exists if (!file_exists(settings::path_source_library)) { - std::stringstream msg; - msg << "Library file " << settings::path_source_library << "' does not exist."; - fatal_error(msg); + std::stringstream msg; + msg << "Library file " << settings::path_source_library << "' does not exist."; + fatal_error(msg); } } else { From 991e49585f33a1367b2aa58176d5d18e09ffa0b4 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Tue, 15 Oct 2019 22:10:33 +0100 Subject: [PATCH 20/90] added unit test for dlopen source, and changed library variable name --- openmc/source.py | 6 +++--- tests/unit_tests/test_source.py | 8 ++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/openmc/source.py b/openmc/source.py index f8ea8ea936..339d41723e 100644 --- a/openmc/source.py +++ b/openmc/source.py @@ -49,7 +49,7 @@ class Source(object): self._angle = None self._energy = None self._file = None - self._source_library = None + self._library = None if space is not None: self.space = space @@ -70,7 +70,7 @@ class Source(object): @property def library(self): - return self._source_library + return self._library @property def space(self): @@ -100,7 +100,7 @@ class Source(object): @library.setter def library(self, library_name): cv.check_type('library', library_name, str) - self._source_library = library_name + self._library = library_name @space.setter def space(self, space): diff --git a/tests/unit_tests/test_source.py b/tests/unit_tests/test_source.py index 1c70e159d2..d4d17a3dab 100644 --- a/tests/unit_tests/test_source.py +++ b/tests/unit_tests/test_source.py @@ -34,3 +34,11 @@ def test_source_file(): elem = src.to_xml_element() assert 'strength' in elem.attrib assert 'file' in elem.attrib + +def test_source_dlopen(): + library = './libsource.so' + src = openmc.Source(library=library) + assert src.library == library + + elem = src.to_xml_element() + assert 'library' in elem.attrib From bc7e03e708d541c76cf8097a05f842d141e67451 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Tue, 15 Oct 2019 22:58:36 +0100 Subject: [PATCH 21/90] Added supporting documentation --- docs/source/io_formats/settings.rst | 69 +++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/docs/source/io_formats/settings.rst b/docs/source/io_formats/settings.rst index 2e2648237b..1d35c18aaf 100644 --- a/docs/source/io_formats/settings.rst +++ b/docs/source/io_formats/settings.rst @@ -459,6 +459,15 @@ attributes/sub-elements: *Default*: None + :library: + If this attribute is given, it indicates that the source is to be instanciated + from an externally compiled source function. This source can be as complex as + is required to define the source for your problem. The only requirement that + is made upon this source, is that there is a function called sample_source() + more documentation on how to build sources can be found in :ref:`custom_source` + + *Deafult*: None + :space: An element specifying the spatial distribution of source sites. This element has the following attributes: @@ -591,6 +600,66 @@ attributes/sub-elements: *Default*: false +.. _custom_source: + +Custom Sources +++++++++++++++++++++++++++++++++++++ + +It is often the case that one may wish to simulate a complex source distribution, +which may include physics not present within OpenMC or to be phase space complex. It +is possible to define complex source with an externally defined source function +and loaded at runtime. A simple example source is shown below. + +.. code-block:: c++ + + #include + #include "openmc/random_lcg.h" + #include "openmc/source.h" + #include "openmc/particle.h" + + // you must have external C linkage here + extern "C" openmc::Particle::Bank sample_source() { + openmc::Particle::Bank particle; + // wgt + particle.particle = openmc::Particle::Type::neutron; + particle.wgt = 1.0; + // position + double angle = 2.*M_PI*openmc::prn(); + double radius = 3.0; + particle.r.x = radius*std::cos(angle); + particle.r.y = radius*std::sin(angle); + particle.r.z = 0.; + // angle + particle.u = {1.,0,0}; + particle.E = 14.08e6; + particle.delayed_group = 0; + return particle; + } + +The above source, creates 14.08 MeV neutrons, with an istropic direction +vector but distributed in a ring of radius three cm. This routine is +not particular complex, but should serve as an example upon which to build +more complicated sources. + + .. note:: The function signature must be declared to be extern. + + .. note:: You should only use the openmc::prn() random number generator + +In order to build your external source you need the CMakeLists.txt file that +was created during the OpenMC installation process (found in the share subdirectory) +and your source file(s). + +.. code-block:: bash + + cp $HOME/openmc/share/CMakeLists.txt. + mkdir bld + cd bld + cmake .. -DSOURCE_FILES=source_sampling.cpp + make + +You will now have a libsouce.so file in this directory, now point the library +attribute of source to this file and you will be able to sample particles. + .. _univariate: Univariate Probability Distributions From 8f515b504acccf9b2461fa4a36014277a7ae4343 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 16 Oct 2019 07:41:14 +0100 Subject: [PATCH 22/90] Wrapped the first instance trying to use dlopen in a posix safety ifdef --- src/source.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/source.cpp b/src/source.cpp index 464a39948f..066e3a52cc 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -2,7 +2,10 @@ #include // for move #include // for stringstream + +#if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__)) #include // for dlopen +#endif #include #include "xtensor/xadapt.hpp" @@ -277,12 +280,17 @@ void initialize_source() msg << "Sampling from library source " << settings::path_source << "..."; write_message(msg, 6); + #if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__)) // Open the library void* source_library = dlopen(settings::path_source_library.c_str(),RTLD_LAZY); if(!source_library) { std::stringstream msg("Couldnt open source library " + settings::path_source_library); fatal_error(msg); } + #else + std::stringstream msg("This feature has not yet been implemented for non POSIX systems"); + fatal_error(msg); + #endif // load the symbol typedef Particle::Bank (*sample_t)(); From e58f8926930d2f7ef397a72247f0b355c70571f8 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 16 Oct 2019 14:21:27 +0100 Subject: [PATCH 23/90] made the building of the cmake more sensible --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 18de941218..d61477f190 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -409,7 +409,7 @@ install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) #============================= # write the dl_open Makefile #============================= -file(WRITE ${CMAKE_INSTALL_PREFIX}/share/CMakeLists.txt " +file(WRITE ${CMAKE_BINARY_DIR}/CMakeLists.txt " cmake_minimum_required(VERSION 3.3 FATAL_ERROR) project(openmc_sources C CXX) add_library(source SHARED \$\{SOURCE_FILES\}) @@ -417,4 +417,4 @@ target_include_directories(source PUBLIC ${PROJECT_SOURCE_DIR}/include ${PROJECT_SOURCE_DIR}/vendor/pugixml ${HDF5_INCLUDE_DIRS}) target_link_libraries(source ${ldflags} ${HDF5_LIBRARIES} ${HDF5_HL_LIBRARIES}) ") - +install(FILES ${CMAKE_BINARY_DIR}/CMakeLists.txt DESTINATION share) From 7f1f675c872700959c61def0c25f3f0e6cb3ef15 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 16 Oct 2019 20:27:39 +0100 Subject: [PATCH 24/90] try adding an init py file --- tests/regression_tests/source_dlopen/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/regression_tests/source_dlopen/__init__.py diff --git a/tests/regression_tests/source_dlopen/__init__.py b/tests/regression_tests/source_dlopen/__init__.py new file mode 100644 index 0000000000..e69de29bb2 From 7a51552c3e67a95986cf63753f962617a1ef75e1 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 16 Oct 2019 21:07:31 +0100 Subject: [PATCH 25/90] cxx standards for the source compile --- CMakeLists.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d61477f190..ef67da4e50 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -412,7 +412,12 @@ install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) file(WRITE ${CMAKE_BINARY_DIR}/CMakeLists.txt " cmake_minimum_required(VERSION 3.3 FATAL_ERROR) project(openmc_sources C CXX) -add_library(source SHARED \$\{SOURCE_FILES\}) +add_library(source SHARED \$\{SOURCE_FILES\})") +get_target_property(cxx_std openmc CXX_STANDARD) +get_target_property(cxx_ext openmc CXX_EXTENSIONS) +file(APPEND ${CMAKE_BINARY_DIR}/CMakeLists.txt " +set_target_properties(source PROPERTIES CXX_STANDARD ${cxx_std} + CXX_EXTENSIONS ${cxx_ext}) target_include_directories(source PUBLIC ${PROJECT_SOURCE_DIR}/include ${PROJECT_SOURCE_DIR}/vendor/pugixml ${HDF5_INCLUDE_DIRS}) target_link_libraries(source ${ldflags} ${HDF5_LIBRARIES} ${HDF5_HL_LIBRARIES}) From 55f85b1924172fb1296036a9ea2a9d4fb0e1053e Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Tue, 11 Feb 2020 22:07:53 +0000 Subject: [PATCH 26/90] updated signatures according to new changes in how seed is set --- src/source.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/source.cpp b/src/source.cpp index 066e3a52cc..b1fd8c71e9 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -293,7 +293,7 @@ void initialize_source() #endif // load the symbol - typedef Particle::Bank (*sample_t)(); + typedef Particle::Bank (*sample_t)(uint64_t *seed); // reset errors dlerror(); @@ -315,10 +315,10 @@ void initialize_source() // initialize random number seed int64_t id = simulation::total_gen*settings::n_particles + simulation::work_index[mpi::rank] + i + 1; - set_particle_seed(id); + uint64_t seed = init_seed(id, STREAM_SOURCE); // sample external source distribution - simulation::source_bank[i] = sample_source(); + simulation::source_bank[i] = sample_source(&seed); } // release the library dlclose(source_library); @@ -405,7 +405,7 @@ void fill_source_bank_fixedsource() } // load the symbol - typedef Particle::Bank (*sample_t)(); + typedef Particle::Bank (*sample_t)(uint64_t *seed); // reset errors dlerror(); @@ -427,10 +427,10 @@ void fill_source_bank_fixedsource() // initialize random number seed int64_t id = simulation::total_gen*settings::n_particles + simulation::work_index[mpi::rank] + i + 1; - set_particle_seed(id); - + uint64_t seed = init_seed(id, STREAM_SOURCE); + // sample external source distribution - simulation::source_bank[i] = sample_source(); + simulation::source_bank[i] = sample_source(&seed); } // release the library From d4835f49ec39139d1442a164add4212be5635e3e Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 12 Feb 2020 09:29:02 +0000 Subject: [PATCH 27/90] Updated the source routines according to passing seed to prn --- src/source.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/source.cpp b/src/source.cpp index b1fd8c71e9..465c2aac14 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -293,7 +293,7 @@ void initialize_source() #endif // load the symbol - typedef Particle::Bank (*sample_t)(uint64_t *seed); + typedef Particle::Bank (*sample_t)(uint64_t seed); // reset errors dlerror(); @@ -318,7 +318,7 @@ void initialize_source() uint64_t seed = init_seed(id, STREAM_SOURCE); // sample external source distribution - simulation::source_bank[i] = sample_source(&seed); + simulation::source_bank[i] = sample_source(seed); } // release the library dlclose(source_library); @@ -405,7 +405,7 @@ void fill_source_bank_fixedsource() } // load the symbol - typedef Particle::Bank (*sample_t)(uint64_t *seed); + typedef Particle::Bank (*sample_t)(uint64_t seed); // reset errors dlerror(); @@ -430,7 +430,7 @@ void fill_source_bank_fixedsource() uint64_t seed = init_seed(id, STREAM_SOURCE); // sample external source distribution - simulation::source_bank[i] = sample_source(&seed); + simulation::source_bank[i] = sample_source(seed); } // release the library From 84421dd95522176b9272f66ec08c015a499d4f65 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 12 Feb 2020 09:43:35 +0000 Subject: [PATCH 28/90] Updated documentation to reflect changes --- docs/source/io_formats/settings.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/io_formats/settings.rst b/docs/source/io_formats/settings.rst index 1d35c18aaf..fde374b8d1 100644 --- a/docs/source/io_formats/settings.rst +++ b/docs/source/io_formats/settings.rst @@ -618,7 +618,7 @@ and loaded at runtime. A simple example source is shown below. #include "openmc/particle.h" // you must have external C linkage here - extern "C" openmc::Particle::Bank sample_source() { + extern "C" openmc::Particle::Bank sample_source(const int64_t seed) { openmc::Particle::Bank particle; // wgt particle.particle = openmc::Particle::Type::neutron; From 7b57ef10c8a5166b3a1c8b6afdd57b07adcdc7c2 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 12 Feb 2020 12:24:29 +0000 Subject: [PATCH 29/90] Added statements for additional cmake targets --- CMakeLists.txt | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ef67da4e50..0d4c371f02 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -207,7 +207,6 @@ endif() add_library(faddeeva STATIC vendor/faddeeva/Faddeeva.cc) target_include_directories(faddeeva PUBLIC - $ $ ) target_compile_options(faddeeva PRIVATE ${cxxflags}) @@ -389,7 +388,7 @@ add_custom_command(TARGET libopenmc POST_BUILD #=============================================================================== set(INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake/OpenMC) -install(TARGETS openmc libopenmc faddeeva +install(TARGETS openmc libopenmc faddeeva pugixml gsl-lite EXPORT openmc-targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} @@ -406,20 +405,3 @@ install(FILES man/man1/openmc.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) install(FILES LICENSE DESTINATION "${CMAKE_INSTALL_DOCDIR}" RENAME copyright) install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) -#============================= -# write the dl_open Makefile -#============================= -file(WRITE ${CMAKE_BINARY_DIR}/CMakeLists.txt " -cmake_minimum_required(VERSION 3.3 FATAL_ERROR) -project(openmc_sources C CXX) -add_library(source SHARED \$\{SOURCE_FILES\})") -get_target_property(cxx_std openmc CXX_STANDARD) -get_target_property(cxx_ext openmc CXX_EXTENSIONS) -file(APPEND ${CMAKE_BINARY_DIR}/CMakeLists.txt " -set_target_properties(source PROPERTIES CXX_STANDARD ${cxx_std} - CXX_EXTENSIONS ${cxx_ext}) -target_include_directories(source PUBLIC ${PROJECT_SOURCE_DIR}/include - ${PROJECT_SOURCE_DIR}/vendor/pugixml ${HDF5_INCLUDE_DIRS}) -target_link_libraries(source ${ldflags} ${HDF5_LIBRARIES} ${HDF5_HL_LIBRARIES}) -") -install(FILES ${CMAKE_BINARY_DIR}/CMakeLists.txt DESTINATION share) From c6949e5e53d8ca3061ab0b998b5a5fef3eb92fe6 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 12 Feb 2020 12:44:46 +0000 Subject: [PATCH 30/90] Updated spelling mistakes --- src/source.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/source.cpp b/src/source.cpp index 465c2aac14..f910b384c9 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -284,7 +284,7 @@ void initialize_source() // Open the library void* source_library = dlopen(settings::path_source_library.c_str(),RTLD_LAZY); if(!source_library) { - std::stringstream msg("Couldnt open source library " + settings::path_source_library); + std::stringstream msg("Couldn't open source library " + settings::path_source_library); fatal_error(msg); } #else @@ -306,7 +306,7 @@ void initialize_source() if (dlsym_error) { std::cout << dlsym_error << std::endl; dlclose(source_library); - fatal_error("Couldnt open the sample_source symbol"); + fatal_error("Couldn't open the sample_source symbol"); } // Generation source sites from specified distribution in the @@ -400,7 +400,7 @@ void fill_source_bank_fixedsource() // Open the library void* source_library = dlopen(settings::path_source_library.c_str(),RTLD_LAZY); if(!source_library) { - std::stringstream msg("Couldnt open source library " + settings::path_source_library); + std::stringstream msg("Couldn't open source library " + settings::path_source_library); fatal_error(msg); } @@ -418,7 +418,7 @@ void fill_source_bank_fixedsource() if (dlsym_error) { std::cout << dlsym_error << std::endl; dlclose(source_library); - fatal_error("Couldnt open the sample_source symbol"); + fatal_error("Couldn't open the sample_source symbol"); } // Generation source sites from specified distribution in the From 7937e7670c43febd4b80285415ff55647b679d89 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 12 Feb 2020 12:47:39 +0000 Subject: [PATCH 31/90] Corrected style according to review --- examples/xml/custom_source/source_ring.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/xml/custom_source/source_ring.cpp b/examples/xml/custom_source/source_ring.cpp index ef2784ea36..fb361d1aa9 100644 --- a/examples/xml/custom_source/source_ring.cpp +++ b/examples/xml/custom_source/source_ring.cpp @@ -12,14 +12,14 @@ extern "C" openmc::Particle::Bank sample_source() { particle.wgt = 1.0; // position - double angle = 2.*M_PI*openmc::prn(); + double angle = 2. * M_PI * openmc::prn(); double radius = 3.0; - particle.r.x = radius*std::cos(angle); - particle.r.y = radius*std::sin(angle); - particle.r.z = 0.; + particle.r.x = radius * std::cos(angle); + particle.r.y = radius * std::sin(angle); + particle.r.z = 0.0; // angle - particle.u = {1.,0,0}; + particle.u = {1.0, 0.0, 0.0}; particle.E = 14.08e6; particle.delayed_group = 0; return particle; -} \ No newline at end of file +} From acce6941c3499deba180c52b6103e0f8fb5472ba Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 12 Feb 2020 12:48:54 +0000 Subject: [PATCH 32/90] line removal from review --- src/source.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/source.cpp b/src/source.cpp index f910b384c9..e7a476746e 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -275,7 +275,6 @@ void initialize_source() file_close(file_id); } else if ( settings::path_source_library != "" ) { // Get the source from a library object - std::stringstream msg; msg << "Sampling from library source " << settings::path_source << "..."; write_message(msg, 6); From 675631028dcca27fe6947b49953345f891e52dc3 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 12 Feb 2020 12:50:53 +0000 Subject: [PATCH 33/90] further changes from review --- docs/source/io_formats/settings.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/source/io_formats/settings.rst b/docs/source/io_formats/settings.rst index fde374b8d1..f224df265f 100644 --- a/docs/source/io_formats/settings.rst +++ b/docs/source/io_formats/settings.rst @@ -658,7 +658,8 @@ and your source file(s). make You will now have a libsouce.so file in this directory, now point the library -attribute of source to this file and you will be able to sample particles. +attribute of the source XML element to this file and you will be able to sample +particles. .. _univariate: From 4460a120a365d633a2fbe4a2bb6a67d37a2ff6fe Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 12 Feb 2020 12:55:21 +0000 Subject: [PATCH 34/90] further review comments regarding style --- docs/source/io_formats/settings.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/source/io_formats/settings.rst b/docs/source/io_formats/settings.rst index f224df265f..5392efef5f 100644 --- a/docs/source/io_formats/settings.rst +++ b/docs/source/io_formats/settings.rst @@ -603,12 +603,12 @@ attributes/sub-elements: .. _custom_source: Custom Sources -++++++++++++++++++++++++++++++++++++ +++++++++++++++ It is often the case that one may wish to simulate a complex source distribution, which may include physics not present within OpenMC or to be phase space complex. It -is possible to define complex source with an externally defined source function -and loaded at runtime. A simple example source is shown below. +is possible to define a complex source with an externally defined source function +that is loaded at runtime. A simple example source is shown below. .. code-block:: c++ @@ -637,7 +637,7 @@ and loaded at runtime. A simple example source is shown below. } The above source, creates 14.08 MeV neutrons, with an istropic direction -vector but distributed in a ring of radius three cm. This routine is +vector but distributed in a ring with a 3 cm radius. This routine is not particular complex, but should serve as an example upon which to build more complicated sources. From 441c284c52eca883ed804e0410af7947c311cb61 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 12 Feb 2020 13:06:30 +0000 Subject: [PATCH 35/90] Refactor into function as suggested by review --- include/openmc/source.h | 3 ++ src/source.cpp | 110 +++++++++++++++++----------------------- 2 files changed, 49 insertions(+), 64 deletions(-) diff --git a/include/openmc/source.h b/include/openmc/source.h index 0a73260fd1..e980f2a8c8 100644 --- a/include/openmc/source.h +++ b/include/openmc/source.h @@ -72,6 +72,9 @@ Particle::Bank sample_external_source(uint64_t* seed); //! Fill source bank at end of generation for fixed source simulations void fill_source_bank_fixedsource(); +//! Fill source bank at the end of a generation for dlopen based source simulation +void fill_source_bank_dlopen_source(); + void free_memory_source(); } // namespace openmc diff --git a/src/source.cpp b/src/source.cpp index e7a476746e..2cda76a5c0 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -297,30 +297,7 @@ void initialize_source() // reset errors dlerror(); - // get the function from the library - sample_t sample_source = (sample_t) dlsym(source_library, "sample_source"); - const char *dlsym_error = dlerror(); - - // check for any dlsym errors - if (dlsym_error) { - std::cout << dlsym_error << std::endl; - dlclose(source_library); - fatal_error("Couldn't open the sample_source symbol"); - } - - // Generation source sites from specified distribution in the - // library source - for (int64_t i = 0; i < simulation::work_per_rank; ++i) { - // initialize random number seed - int64_t id = simulation::total_gen*settings::n_particles + - simulation::work_index[mpi::rank] + i + 1; - uint64_t seed = init_seed(id, STREAM_SOURCE); - - // sample external source distribution - simulation::source_bank[i] = sample_source(seed); - } - // release the library - dlclose(source_library); + fill_source_bank_dlopen_source(); } else { // Generation source sites from specified distribution in user input @@ -381,6 +358,50 @@ void free_memory_source() model::external_sources.clear(); } +// fill the source bank from the external source +void fill_source_bank_dlopen_source() +{ + std::stringstream msg; + + // Open the library + void* source_library = dlopen(settings::path_source_library.c_str(),RTLD_LAZY); + if(!source_library) { + std::stringstream msg("Couldn't open source library " + settings::path_source_library); + fatal_error(msg); + } + + // load the symbol + typedef Particle::Bank (*sample_t)(uint64_t seed); + + // reset errors + dlerror(); + + // get the function from the library + sample_t sample_source = (sample_t) dlsym(source_library, "sample_source"); + const char *dlsym_error = dlerror(); + + // check for any dlsym errors + if (dlsym_error) { + std::cout << dlsym_error << std::endl; + dlclose(source_library); + fatal_error("Couldn't open the sample_source symbol"); + } + + // Generation source sites from specified distribution in the + // library source + for (int64_t i = 0; i < simulation::work_per_rank; ++i) { + // initialize random number seed + int64_t id = simulation::total_gen*settings::n_particles + + simulation::work_index[mpi::rank] + i + 1; + uint64_t seed = init_seed(id, STREAM_SOURCE); + // sample external source distribution + simulation::source_bank[i] = sample_source(seed); + } + + // release the library + dlclose(source_library); +} + void fill_source_bank_fixedsource() { if (settings::path_source.empty() && settings::path_source_library.empty()) { @@ -394,46 +415,7 @@ void fill_source_bank_fixedsource() simulation::source_bank[i] = sample_external_source(&seed); } } else if (settings::path_source.empty() && !settings::path_source.empty()) { - std::stringstream msg; - - // Open the library - void* source_library = dlopen(settings::path_source_library.c_str(),RTLD_LAZY); - if(!source_library) { - std::stringstream msg("Couldn't open source library " + settings::path_source_library); - fatal_error(msg); - } - - // load the symbol - typedef Particle::Bank (*sample_t)(uint64_t seed); - - // reset errors - dlerror(); - - // get the function from the library - sample_t sample_source = (sample_t) dlsym(source_library, "sample_source"); - const char *dlsym_error = dlerror(); - - // check for any dlsym errors - if (dlsym_error) { - std::cout << dlsym_error << std::endl; - dlclose(source_library); - fatal_error("Couldn't open the sample_source symbol"); - } - - // Generation source sites from specified distribution in the - // library source - for (int64_t i = 0; i < simulation::work_per_rank; ++i) { - // initialize random number seed - int64_t id = simulation::total_gen*settings::n_particles + - simulation::work_index[mpi::rank] + i + 1; - uint64_t seed = init_seed(id, STREAM_SOURCE); - - // sample external source distribution - simulation::source_bank[i] = sample_source(seed); - } - - // release the library - dlclose(source_library); + fill_source_bank_dlopen_source(); } } From 513a4b7b6590b04290521a347e9fef9b943c3c6e Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 12 Feb 2020 13:11:22 +0000 Subject: [PATCH 36/90] updated for new cmake instructions --- docs/source/io_formats/settings.rst | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/docs/source/io_formats/settings.rst b/docs/source/io_formats/settings.rst index 5392efef5f..6f898f911b 100644 --- a/docs/source/io_formats/settings.rst +++ b/docs/source/io_formats/settings.rst @@ -645,19 +645,17 @@ more complicated sources. .. note:: You should only use the openmc::prn() random number generator -In order to build your external source you need the CMakeLists.txt file that -was created during the OpenMC installation process (found in the share subdirectory) -and your source file(s). +In order to build your external source you need the following CMakeLists.txt file -.. code-block:: bash +.. code-block:: cmake - cp $HOME/openmc/share/CMakeLists.txt. - mkdir bld - cd bld - cmake .. -DSOURCE_FILES=source_sampling.cpp - make + cmake_minimum_required(VERSION 3.3 FATAL_ERROR) + project(openmc_sources CXX) + add_library(source SHARED source_ring.cpp) + find_package(OpenMC REQUIRED HINTS ) + target_link_libraries(source OpenMC::libopenmc) -You will now have a libsouce.so file in this directory, now point the library +You will now have a libsouce.so (or .dylib) file in this directory, now point the library attribute of the source XML element to this file and you will be able to sample particles. From 8dd963f8e28fad45e24954035527137cf7c3b1e1 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 12 Feb 2020 13:11:53 +0000 Subject: [PATCH 37/90] updated the example source according to style --- examples/xml/custom_source/source_ring.cpp | 33 +++++++++++----------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/examples/xml/custom_source/source_ring.cpp b/examples/xml/custom_source/source_ring.cpp index fb361d1aa9..babb2f591c 100644 --- a/examples/xml/custom_source/source_ring.cpp +++ b/examples/xml/custom_source/source_ring.cpp @@ -5,21 +5,22 @@ // you must have external C linkage here otherwise // dlopen will not find the file -extern "C" openmc::Particle::Bank sample_source() { - openmc::Particle::Bank particle; - // wgt - particle.particle = openmc::Particle::Type::neutron; - particle.wgt = 1.0; - // position +extern "C" openmc::Particle::Bank sample_source() +{ + openmc::Particle::Bank particle; + // wgt + particle.particle = openmc::Particle::Type::neutron; + particle.wgt = 1.0; + // position - double angle = 2. * M_PI * openmc::prn(); - double radius = 3.0; - particle.r.x = radius * std::cos(angle); - particle.r.y = radius * std::sin(angle); - particle.r.z = 0.0; - // angle - particle.u = {1.0, 0.0, 0.0}; - particle.E = 14.08e6; - particle.delayed_group = 0; - return particle; + double angle = 2. * M_PI * openmc::prn(); + double radius = 3.0; + particle.r.x = radius * std::cos(angle); + particle.r.y = radius * std::sin(angle); + particle.r.z = 0.0; + // angle + particle.u = {1.0, 0.0, 0.0}; + particle.E = 14.08e6; + particle.delayed_group = 0; + return particle; } From 39672e79b0fcf7e7cbbd63d7a9dad14f7251b459 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Thu, 13 Jun 2019 09:15:21 +0100 Subject: [PATCH 38/90] Added internal infrastructure for dlopen based shared object sources --- include/openmc/settings.h | 1 + include/openmc/source.h | 4 +++ src/settings.cpp | 1 + src/source.cpp | 54 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 59 insertions(+), 1 deletion(-) diff --git a/include/openmc/settings.h b/include/openmc/settings.h index 2b4203646f..494cfd1a20 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -60,6 +60,7 @@ extern std::string path_input; //!< directory where main .xml files r extern std::string path_output; //!< directory where output files are written extern std::string path_particle_restart; //!< path to a particle restart file extern std::string path_source; +extern std::string path_source_library; //!< path to the source shared object extern std::string path_sourcepoint; //!< path to a source file extern "C" std::string path_statepoint; //!< path to a statepoint file diff --git a/include/openmc/source.h b/include/openmc/source.h index a177995ea8..0a73260fd1 100644 --- a/include/openmc/source.h +++ b/include/openmc/source.h @@ -59,6 +59,10 @@ private: //! Initialize source bank from file/distribution extern "C" void initialize_source(); +// as yet uncreated function to sample a source from a shared object +// +// extern "C" Particle::Bank sample_source(); + //! Sample a site from all external source distributions in proportion to their //! source strength //! \param[inout] seed Pseudorandom seed pointer diff --git a/src/settings.cpp b/src/settings.cpp index 7808a9d0f6..614dd8f5b4 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -74,6 +74,7 @@ std::string path_input; std::string path_output; std::string path_particle_restart; std::string path_source; +std::string path_source_library; std::string path_sourcepoint; std::string path_statepoint; diff --git a/src/source.cpp b/src/source.cpp index b15a15d23a..4666783d4a 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -1,6 +1,8 @@ #include "openmc/source.h" #include // for move +#include // for stringstream +#include // for dlopen #include #include "xtensor/xadapt.hpp" @@ -71,7 +73,14 @@ SourceDistribution::SourceDistribution(pugi::xml_node node) fatal_error(fmt::format("Source file '{}' does not exist.", settings::path_source)); } - + } else if (check_for_node(node, "library")) { + settings::path_source_library = get_node_value(node, "library", false, true); + // check if it exists + if (!file_exists(settings::path_source_library)) { + std::stringstream msg; + msg << "Library file " << settings::path_source_library << "' does not exist."; + fatal_error(msg); + } } else { // Spatial distribution for external source @@ -261,7 +270,50 @@ void initialize_source() // Close file file_close(file_id); + } else if ( settings::path_source_library != "" ) { + // Get the source from a library object + std::stringstream msg; + msg << "Sampling from library source " << settings::path_source << "..."; + write_message(msg, 6); + + // Open the library + void* source_library = dlopen(settings::path_source_library.c_str(),RTLD_LAZY); + if(!source_library) { + std::stringstream msg("Couldnt open source library " + settings::path_source_library); + fatal_error(msg); + } + + // load the symbol + typedef Particle::Bank (*sample_t)(); + + // reset errors + dlerror(); + + // get the function from the library + sample_t sample_source = (sample_t) dlsym(source_library, "sample_source"); + const char *dlsym_error = dlerror(); + + if (dlsym_error) { + dlclose(source_library); + fatal_error(dlsym_error); + } + + // Generation source sites from specified distribution in the + // library source + for (int64_t i = 0; i < simulation::work_per_rank; ++i) { + // initialize random number seed + int64_t id = simulation::total_gen*settings::n_particles + + simulation::work_index[mpi::rank] + i + 1; + set_particle_seed(id); + + // sample external source distribution + simulation::source_bank[i] = sample_source(); + } + + // release the library + dlclose(source_library); + } else { // Generation source sites from specified distribution in user input for (int64_t i = 0; i < simulation::work_per_rank; ++i) { From e1baa804a639527654e4e2bf226014abb7e2a816 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Thu, 13 Jun 2019 14:40:15 +0100 Subject: [PATCH 39/90] Added missing fixed source subroutine --- src/source.cpp | 50 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/src/source.cpp b/src/source.cpp index 4666783d4a..b856a8d2ad 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -294,9 +294,11 @@ void initialize_source() sample_t sample_source = (sample_t) dlsym(source_library, "sample_source"); const char *dlsym_error = dlerror(); + // check for any dlsym errors if (dlsym_error) { + std::cout << dlsym_error << std::endl; dlclose(source_library); - fatal_error(dlsym_error); + fatal_error("Couldnt open the sample_source symbol"); } // Generation source sites from specified distribution in the @@ -313,7 +315,7 @@ void initialize_source() // release the library dlclose(source_library); - + } else { // Generation source sites from specified distribution in user input for (int64_t i = 0; i < simulation::work_per_rank; ++i) { @@ -375,8 +377,7 @@ void free_memory_source() void fill_source_bank_fixedsource() { - if (settings::path_source.empty()) { - #pragma omp parallel for + if (settings::path_source.empty() && settings::path_source_library.empty()) { for (int64_t i = 0; i < simulation::work_per_rank; ++i) { // initialize random number seed int64_t id = (simulation::total_gen + overall_generation()) * @@ -386,6 +387,47 @@ void fill_source_bank_fixedsource() // sample external source distribution simulation::source_bank[i] = sample_external_source(&seed); } + } else if (settings::path_source.empty() && !settings::path_source.empty()) { + std::stringstream msg; + + // Open the library + void* source_library = dlopen(settings::path_source_library.c_str(),RTLD_LAZY); + if(!source_library) { + std::stringstream msg("Couldnt open source library " + settings::path_source_library); + fatal_error(msg); + } + + // load the symbol + typedef Particle::Bank (*sample_t)(); + + // reset errors + dlerror(); + + // get the function from the library + sample_t sample_source = (sample_t) dlsym(source_library, "sample_source"); + const char *dlsym_error = dlerror(); + + // check for any dlsym errors + if (dlsym_error) { + std::cout << dlsym_error << std::endl; + dlclose(source_library); + fatal_error("Couldnt open the sample_source symbol"); + } + + // Generation source sites from specified distribution in the + // library source + for (int64_t i = 0; i < simulation::work_per_rank; ++i) { + // initialize random number seed + int64_t id = simulation::total_gen*settings::n_particles + + simulation::work_index[mpi::rank] + i + 1; + set_particle_seed(id); + + // sample external source distribution + simulation::source_bank[i] = sample_source(); + } + + // release the library + dlclose(source_library); } } From 995a920854a56ae273fcadad3f20b3b25cc86728 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Thu, 13 Jun 2019 17:49:29 +0100 Subject: [PATCH 40/90] Added example on how to use xml based custom source --- examples/xml/custom_source/geometry.xml | 15 +++++++++++++ examples/xml/custom_source/materials.xml | 16 ++++++++++++++ examples/xml/custom_source/settings.xml | 15 +++++++++++++ examples/xml/custom_source/source_ring.cpp | 25 ++++++++++++++++++++++ examples/xml/custom_source/tallies.xml | 17 +++++++++++++++ 5 files changed, 88 insertions(+) create mode 100644 examples/xml/custom_source/geometry.xml create mode 100644 examples/xml/custom_source/materials.xml create mode 100644 examples/xml/custom_source/settings.xml create mode 100644 examples/xml/custom_source/source_ring.cpp create mode 100644 examples/xml/custom_source/tallies.xml diff --git a/examples/xml/custom_source/geometry.xml b/examples/xml/custom_source/geometry.xml new file mode 100644 index 0000000000..b30884f8ca --- /dev/null +++ b/examples/xml/custom_source/geometry.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/examples/xml/custom_source/materials.xml b/examples/xml/custom_source/materials.xml new file mode 100644 index 0000000000..606c676df8 --- /dev/null +++ b/examples/xml/custom_source/materials.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/examples/xml/custom_source/settings.xml b/examples/xml/custom_source/settings.xml new file mode 100644 index 0000000000..f8d497459c --- /dev/null +++ b/examples/xml/custom_source/settings.xml @@ -0,0 +1,15 @@ + + + + fixed source + 10 + 0 + 100000 + + + + + ./source_ring.so + + + diff --git a/examples/xml/custom_source/source_ring.cpp b/examples/xml/custom_source/source_ring.cpp new file mode 100644 index 0000000000..ef2784ea36 --- /dev/null +++ b/examples/xml/custom_source/source_ring.cpp @@ -0,0 +1,25 @@ +#include +#include "openmc/random_lcg.h" +#include "openmc/source.h" +#include "openmc/particle.h" + +// you must have external C linkage here otherwise +// dlopen will not find the file +extern "C" openmc::Particle::Bank sample_source() { + openmc::Particle::Bank particle; + // wgt + particle.particle = openmc::Particle::Type::neutron; + particle.wgt = 1.0; + // position + + double angle = 2.*M_PI*openmc::prn(); + double radius = 3.0; + particle.r.x = radius*std::cos(angle); + particle.r.y = radius*std::sin(angle); + particle.r.z = 0.; + // angle + particle.u = {1.,0,0}; + particle.E = 14.08e6; + particle.delayed_group = 0; + return particle; +} \ No newline at end of file diff --git a/examples/xml/custom_source/tallies.xml b/examples/xml/custom_source/tallies.xml new file mode 100644 index 0000000000..7f6f299261 --- /dev/null +++ b/examples/xml/custom_source/tallies.xml @@ -0,0 +1,17 @@ + + + + + 100 + + + + 0 20.0e6 + + + + 1 2 + flux + + + From c3fb48bc62ff880b654a5fabef7c808f301943ef Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Fri, 14 Jun 2019 08:33:44 +0100 Subject: [PATCH 41/90] Added python bindngs for library based source --- openmc/source.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/openmc/source.py b/openmc/source.py index 88c2f86119..e0fd2b6c40 100644 --- a/openmc/source.py +++ b/openmc/source.py @@ -44,11 +44,12 @@ class Source(object): """ def __init__(self, space=None, angle=None, energy=None, filename=None, - strength=1.0, particle='neutron'): + library=None, strength=1.0, particle='neutron'): self._space = None self._angle = None self._energy = None self._file = None + self._source_library = None if space is not None: self.space = space @@ -58,6 +59,8 @@ class Source(object): self.energy = energy if filename is not None: self.file = filename + if library is not None: + self.source_library = library self.strength = strength self.particle = particle @@ -65,6 +68,10 @@ class Source(object): def file(self): return self._file + @property + def library(self): + return self._source_library + @property def space(self): return self._space @@ -90,6 +97,11 @@ class Source(object): cv.check_type('source file', filename, str) self._file = filename + @library.setter + def library(self, library_name): + cv.check_type('library', library_name, str) + self._source_library = library_name + @space.setter def space(self, space): cv.check_type('spatial distribution', space, Spatial) @@ -131,6 +143,8 @@ class Source(object): element.set("particle", self.particle) if self.file is not None: element.set("file", self.file) + if self.source_library is not None: + element.set("library", self.source_library) if self.space is not None: element.append(self.space.to_xml_element()) if self.angle is not None: @@ -168,6 +182,10 @@ class Source(object): if filename is not None: source.file = filename + library = get_text(elem, 'library') + if library is not None: + source.source_library = library + space = elem.find('space') if space is not None: source.space = Spatial.from_xml_element(space) From d140b9e49978cdcbcdf3dfa110a76c48c28859a9 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Fri, 14 Jun 2019 10:52:55 +0100 Subject: [PATCH 42/90] Fix the python part --- openmc/source.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openmc/source.py b/openmc/source.py index e0fd2b6c40..f8ea8ea936 100644 --- a/openmc/source.py +++ b/openmc/source.py @@ -60,7 +60,7 @@ class Source(object): if filename is not None: self.file = filename if library is not None: - self.source_library = library + self.library = library self.strength = strength self.particle = particle @@ -143,8 +143,8 @@ class Source(object): element.set("particle", self.particle) if self.file is not None: element.set("file", self.file) - if self.source_library is not None: - element.set("library", self.source_library) + if self.library is not None: + element.set("library", self.library) if self.space is not None: element.append(self.space.to_xml_element()) if self.angle is not None: @@ -184,7 +184,7 @@ class Source(object): library = get_text(elem, 'library') if library is not None: - source.source_library = library + source.library = library space = elem.find('space') if space is not None: From 0324200b61d986103f23b6107e59aa0220234b88 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Thu, 13 Jun 2019 09:15:21 +0100 Subject: [PATCH 43/90] Added internal infrastructure for dlopen based shared object sources --- src/source.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/source.cpp b/src/source.cpp index b856a8d2ad..089dbddef7 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -312,10 +312,9 @@ void initialize_source() // sample external source distribution simulation::source_bank[i] = sample_source(); } - // release the library dlclose(source_library); - + } else { // Generation source sites from specified distribution in user input for (int64_t i = 0; i < simulation::work_per_rank; ++i) { From 89f86883fba7c5a02ad8dcb3ac22286204cf305a Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Thu, 13 Jun 2019 14:40:15 +0100 Subject: [PATCH 44/90] Added missing fixed source subroutine --- src/source.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/source.cpp b/src/source.cpp index 089dbddef7..b7d1e24990 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -314,7 +314,7 @@ void initialize_source() } // release the library dlclose(source_library); - + } else { // Generation source sites from specified distribution in user input for (int64_t i = 0; i < simulation::work_per_rank; ++i) { From 7781b9249574acaa770eba61d057844951e2426b Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Fri, 14 Jun 2019 08:33:44 +0100 Subject: [PATCH 45/90] Added python bindngs for library based source --- openmc/source.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openmc/source.py b/openmc/source.py index f8ea8ea936..e0fd2b6c40 100644 --- a/openmc/source.py +++ b/openmc/source.py @@ -60,7 +60,7 @@ class Source(object): if filename is not None: self.file = filename if library is not None: - self.library = library + self.source_library = library self.strength = strength self.particle = particle @@ -143,8 +143,8 @@ class Source(object): element.set("particle", self.particle) if self.file is not None: element.set("file", self.file) - if self.library is not None: - element.set("library", self.library) + if self.source_library is not None: + element.set("library", self.source_library) if self.space is not None: element.append(self.space.to_xml_element()) if self.angle is not None: @@ -184,7 +184,7 @@ class Source(object): library = get_text(elem, 'library') if library is not None: - source.library = library + source.source_library = library space = elem.find('space') if space is not None: From 8c3d47a4e02fabd4ec2c9cc455f45983e3dca1d0 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Fri, 14 Jun 2019 10:52:55 +0100 Subject: [PATCH 46/90] Fix the python part --- openmc/source.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openmc/source.py b/openmc/source.py index e0fd2b6c40..f8ea8ea936 100644 --- a/openmc/source.py +++ b/openmc/source.py @@ -60,7 +60,7 @@ class Source(object): if filename is not None: self.file = filename if library is not None: - self.source_library = library + self.library = library self.strength = strength self.particle = particle @@ -143,8 +143,8 @@ class Source(object): element.set("particle", self.particle) if self.file is not None: element.set("file", self.file) - if self.source_library is not None: - element.set("library", self.source_library) + if self.library is not None: + element.set("library", self.library) if self.space is not None: element.append(self.space.to_xml_element()) if self.angle is not None: @@ -184,7 +184,7 @@ class Source(object): library = get_text(elem, 'library') if library is not None: - source.source_library = library + source.library = library space = elem.find('space') if space is not None: From 98fc03bda13314ee539ada19c9917d014ed79151 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Mon, 5 Aug 2019 22:44:21 +0100 Subject: [PATCH 47/90] Now writes a Makefile for users to build a source --- CMakeLists.txt | 58 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ec6b500b45..6cc9fceaf5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -406,6 +406,58 @@ install(FILES man/man1/openmc.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) install(FILES LICENSE DESTINATION "${CMAKE_INSTALL_DOCDIR}" RENAME copyright) install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) -# Copy headers for vendored dependencies (note that all except faddeeva are handled -# separately since they are managed by CMake) -install(DIRECTORY vendor/faddeeva DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +#============================= +# write the dl_open Makefile +#============================= +file(WRITE share/Makefile" +# Makefile to build dynamic sources for OpenMC +# this assumes that your source sampling filename is +# source_sampling.cpp +# +# you can add fortran, c and cpp dependencies to this source +# adding them in FC_DEPS, C_DEPS, and CXX_DEPS accordingly + +ifeq ($(FC), f77) +FC = gfortran +endif + +default: all + +ALL = source_sampling +# add your fortran depencies here +FC_DEPS = +# add your c dependencies here +C_DEPS = +# add your cpp dependencies here +CPP_DEPS = +DEPS = $(FC_DEPS) $(C_DEPS) $(CPP_DEPS) +OPT_LEVEL = -O3 +FFLAGS = $(OPT_LEVEL) -fPIC +C_FLAGS = -fPIC +CXX_FLAGS = -fPIC + +OPENMC_DIR = ${CMAKE_INSTALL_PREFIX} +OPENMC_INC_DIR = $(OPENMC_DIR)/include +OPENMC_LIB_DIR = $(OPENMC_DIR)/lib +# setting the so name is important +LINK_FLAGS = $(OPT_LEVEL) -Wall -Wl,-soname,source_sampling.so +# setting shared is important +LINK_FLAGS += -L$(OPENMC_LIB_DIR) -lopenmc -lgfortran -fPIC -shared +OPENMC_INCLUDES = -I$(OPENMC_INC_DIR) -I$(OPENMC_DIR)/vendor/pugixml + +all: $(ALL) + +source_sampling: $(DEPS) + $(CXX) source_sampling.cpp $(DEPS) $(OPENMC_INCLUDES) $(LINK_FLAGS) -o $@.so +# make any fortran objects +%.o : %.F90 + $(FC) -c $(FFLAGS) $*.F90 -o $@ +# make any c objects +%.o : %.c + $(CC) -c $(FFLAGS) $*.c -o $@ +#make any cpp objects +%.o : %.cpp + $(CXX) -c $(FFLAGS) $*.cpp -o $@ +clean: + rm -rf *.o *.mod +") From 0bb36da36bb1410a3341c0c3f94f5bef9046bf1a Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Thu, 13 Jun 2019 09:15:21 +0100 Subject: [PATCH 48/90] Added internal infrastructure for dlopen based shared object sources --- src/source.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/source.cpp b/src/source.cpp index b7d1e24990..51d86fcaa5 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -296,9 +296,8 @@ void initialize_source() // check for any dlsym errors if (dlsym_error) { - std::cout << dlsym_error << std::endl; dlclose(source_library); - fatal_error("Couldnt open the sample_source symbol"); + fatal_error(dlsym_error); } // Generation source sites from specified distribution in the @@ -314,7 +313,7 @@ void initialize_source() } // release the library dlclose(source_library); - + } else { // Generation source sites from specified distribution in user input for (int64_t i = 0; i < simulation::work_per_rank; ++i) { From da3c0e83f0f266a2908b3739c440b099ed777de0 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Thu, 13 Jun 2019 14:40:15 +0100 Subject: [PATCH 49/90] Added missing fixed source subroutine --- src/source.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/source.cpp b/src/source.cpp index 51d86fcaa5..b7d1e24990 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -296,8 +296,9 @@ void initialize_source() // check for any dlsym errors if (dlsym_error) { + std::cout << dlsym_error << std::endl; dlclose(source_library); - fatal_error(dlsym_error); + fatal_error("Couldnt open the sample_source symbol"); } // Generation source sites from specified distribution in the @@ -313,7 +314,7 @@ void initialize_source() } // release the library dlclose(source_library); - + } else { // Generation source sites from specified distribution in user input for (int64_t i = 0; i < simulation::work_per_rank; ++i) { From 590e659079c76b08902699e782d0cf89a68f93bd Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Fri, 14 Jun 2019 08:33:44 +0100 Subject: [PATCH 50/90] Added python bindngs for library based source --- openmc/source.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openmc/source.py b/openmc/source.py index f8ea8ea936..e0fd2b6c40 100644 --- a/openmc/source.py +++ b/openmc/source.py @@ -60,7 +60,7 @@ class Source(object): if filename is not None: self.file = filename if library is not None: - self.library = library + self.source_library = library self.strength = strength self.particle = particle @@ -143,8 +143,8 @@ class Source(object): element.set("particle", self.particle) if self.file is not None: element.set("file", self.file) - if self.library is not None: - element.set("library", self.library) + if self.source_library is not None: + element.set("library", self.source_library) if self.space is not None: element.append(self.space.to_xml_element()) if self.angle is not None: @@ -184,7 +184,7 @@ class Source(object): library = get_text(elem, 'library') if library is not None: - source.library = library + source.source_library = library space = elem.find('space') if space is not None: From 50c55041b3309d8496c57832b585db9a3a1289d9 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Fri, 14 Jun 2019 10:52:55 +0100 Subject: [PATCH 51/90] Fix the python part --- openmc/source.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openmc/source.py b/openmc/source.py index e0fd2b6c40..f8ea8ea936 100644 --- a/openmc/source.py +++ b/openmc/source.py @@ -60,7 +60,7 @@ class Source(object): if filename is not None: self.file = filename if library is not None: - self.source_library = library + self.library = library self.strength = strength self.particle = particle @@ -143,8 +143,8 @@ class Source(object): element.set("particle", self.particle) if self.file is not None: element.set("file", self.file) - if self.source_library is not None: - element.set("library", self.source_library) + if self.library is not None: + element.set("library", self.library) if self.space is not None: element.append(self.space.to_xml_element()) if self.angle is not None: @@ -184,7 +184,7 @@ class Source(object): library = get_text(elem, 'library') if library is not None: - source.source_library = library + source.library = library space = elem.find('space') if space is not None: From ceb88c2d17eb078bdc6587b8202e67c4943e0dc8 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 7 Aug 2019 08:02:17 +0100 Subject: [PATCH 52/90] missing space --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6cc9fceaf5..605ba0b612 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -409,7 +409,7 @@ install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) #============================= # write the dl_open Makefile #============================= -file(WRITE share/Makefile" +file(WRITE share/Makefile " # Makefile to build dynamic sources for OpenMC # this assumes that your source sampling filename is # source_sampling.cpp From 46c79c0889f1b94a27909e376d46e80102efd718 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 7 Aug 2019 08:02:38 +0100 Subject: [PATCH 53/90] Adding unit test for dlopen source --- .../dlopen_source/source_sampling.cpp | 23 ++++++++ .../dlopen_source/test_dlopen_source.py | 57 +++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 tests/unit_tests/dlopen_source/source_sampling.cpp create mode 100644 tests/unit_tests/dlopen_source/test_dlopen_source.py diff --git a/tests/unit_tests/dlopen_source/source_sampling.cpp b/tests/unit_tests/dlopen_source/source_sampling.cpp new file mode 100644 index 0000000000..4b13c7db92 --- /dev/null +++ b/tests/unit_tests/dlopen_source/source_sampling.cpp @@ -0,0 +1,23 @@ +#include +#include "openmc/random_lcg.h" +#include "openmc/source.h" +#include "openmc/particle.h" + +// you must have external C linkage here otherwise +// dlopen will not find the file +extern "C" openmc::Particle::Bank sample_source() { + openmc::Particle::Bank particle; + // wgt + particle.particle = openmc::Particle::Type::neutron; + particle.wgt = 1.0; + // position + + particle.r.x = 0.; + particle.r.y = 0.; + particle.r.z = 0.; + // angle + particle.u = {1.,0,0}; + particle.E = 14.08e6; + particle.delayed_group = 0; + return particle; +} diff --git a/tests/unit_tests/dlopen_source/test_dlopen_source.py b/tests/unit_tests/dlopen_source/test_dlopen_source.py new file mode 100644 index 0000000000..917f3fd4fe --- /dev/null +++ b/tests/unit_tests/dlopen_source/test_dlopen_source.py @@ -0,0 +1,57 @@ +import openmc +import pytest +import os + +# compile the external source +def compile_source(): + # needs a more robust way to know where the + # Makefile is + status = os.system('cp /usr/local/share/Makefile .') + assert os.WEXITSTATUS(status) == 0 + status = os.system('make') + assert os.WEXITSTATUS(status) == 0 + + return + +# build the test geometry +def make_geometry(): + mats = openmc.Materials() + + natural_lead = openmc.Material(1, "natural_lead") + natural_lead.add_element('Pb', 1,'ao') + natural_lead.set_density('g/cm3', 11.34) + mats.append(natural_lead) + + # surfaces + surface_sph1 = openmc.Sphere(r=100) + volume_sph1 = -surface_sph1 + + # cell + cell_1 = openmc.Cell(region=volume_sph1) + cell_1.fill = natural_lead #assigning a material to a cell + universe = openmc.Universe(cells=[cell_1]) #hint, this list will need to include the new cell + geom = openmc.Geometry(universe) + + # settings + sett = openmc.Settings() + batches = 10 + sett.batches = batches + sett.inactive = 0 + sett.particles = 1000 + sett.particle = "neutron" + sett.run_mode = 'fixed source' + + #source + source = openmc.Source() + source.library = PATH+'source_sampling.so' + sett.source = source + + # run + model = openmc.model.Model(geom,mats,sett) + return model + +def test_dlopen_source(): + compile_source() + model = make_geometry() + model.run() + From f387d2cba9437e41affbd8bbe5ea8a9799b34745 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Tue, 15 Oct 2019 20:41:10 +0100 Subject: [PATCH 54/90] make a cmakefile instead of a makefile for cross platform compatibility --- CMakeLists.txt | 59 +++++++------------------------------------------- 1 file changed, 8 insertions(+), 51 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 605ba0b612..18de941218 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -409,55 +409,12 @@ install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) #============================= # write the dl_open Makefile #============================= -file(WRITE share/Makefile " -# Makefile to build dynamic sources for OpenMC -# this assumes that your source sampling filename is -# source_sampling.cpp -# -# you can add fortran, c and cpp dependencies to this source -# adding them in FC_DEPS, C_DEPS, and CXX_DEPS accordingly - -ifeq ($(FC), f77) -FC = gfortran -endif - -default: all - -ALL = source_sampling -# add your fortran depencies here -FC_DEPS = -# add your c dependencies here -C_DEPS = -# add your cpp dependencies here -CPP_DEPS = -DEPS = $(FC_DEPS) $(C_DEPS) $(CPP_DEPS) -OPT_LEVEL = -O3 -FFLAGS = $(OPT_LEVEL) -fPIC -C_FLAGS = -fPIC -CXX_FLAGS = -fPIC - -OPENMC_DIR = ${CMAKE_INSTALL_PREFIX} -OPENMC_INC_DIR = $(OPENMC_DIR)/include -OPENMC_LIB_DIR = $(OPENMC_DIR)/lib -# setting the so name is important -LINK_FLAGS = $(OPT_LEVEL) -Wall -Wl,-soname,source_sampling.so -# setting shared is important -LINK_FLAGS += -L$(OPENMC_LIB_DIR) -lopenmc -lgfortran -fPIC -shared -OPENMC_INCLUDES = -I$(OPENMC_INC_DIR) -I$(OPENMC_DIR)/vendor/pugixml - -all: $(ALL) - -source_sampling: $(DEPS) - $(CXX) source_sampling.cpp $(DEPS) $(OPENMC_INCLUDES) $(LINK_FLAGS) -o $@.so -# make any fortran objects -%.o : %.F90 - $(FC) -c $(FFLAGS) $*.F90 -o $@ -# make any c objects -%.o : %.c - $(CC) -c $(FFLAGS) $*.c -o $@ -#make any cpp objects -%.o : %.cpp - $(CXX) -c $(FFLAGS) $*.cpp -o $@ -clean: - rm -rf *.o *.mod +file(WRITE ${CMAKE_INSTALL_PREFIX}/share/CMakeLists.txt " +cmake_minimum_required(VERSION 3.3 FATAL_ERROR) +project(openmc_sources C CXX) +add_library(source SHARED \$\{SOURCE_FILES\}) +target_include_directories(source PUBLIC ${PROJECT_SOURCE_DIR}/include + ${PROJECT_SOURCE_DIR}/vendor/pugixml ${HDF5_INCLUDE_DIRS}) +target_link_libraries(source ${ldflags} ${HDF5_LIBRARIES} ${HDF5_HL_LIBRARIES}) ") + From cba7e9bc87e1757d228aba9e29b271f8808d0f6c Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Tue, 15 Oct 2019 22:00:07 +0100 Subject: [PATCH 55/90] Added regression tests for dlopen source --- .../source_dlopen/inputs_true.dat | 23 ++++++ .../source_dlopen/results_true.dat | 0 .../source_dlopen}/source_sampling.cpp | 0 tests/regression_tests/source_dlopen/test.py | 70 +++++++++++++++++++ .../dlopen_source/test_dlopen_source.py | 57 --------------- 5 files changed, 93 insertions(+), 57 deletions(-) create mode 100644 tests/regression_tests/source_dlopen/inputs_true.dat create mode 100644 tests/regression_tests/source_dlopen/results_true.dat rename tests/{unit_tests/dlopen_source => regression_tests/source_dlopen}/source_sampling.cpp (100%) create mode 100644 tests/regression_tests/source_dlopen/test.py delete mode 100644 tests/unit_tests/dlopen_source/test_dlopen_source.py diff --git a/tests/regression_tests/source_dlopen/inputs_true.dat b/tests/regression_tests/source_dlopen/inputs_true.dat new file mode 100644 index 0000000000..23f3d84384 --- /dev/null +++ b/tests/regression_tests/source_dlopen/inputs_true.dat @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + fixed source + 1000 + 10 + 0 + + diff --git a/tests/regression_tests/source_dlopen/results_true.dat b/tests/regression_tests/source_dlopen/results_true.dat new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/unit_tests/dlopen_source/source_sampling.cpp b/tests/regression_tests/source_dlopen/source_sampling.cpp similarity index 100% rename from tests/unit_tests/dlopen_source/source_sampling.cpp rename to tests/regression_tests/source_dlopen/source_sampling.cpp diff --git a/tests/regression_tests/source_dlopen/test.py b/tests/regression_tests/source_dlopen/test.py new file mode 100644 index 0000000000..ac63ca8045 --- /dev/null +++ b/tests/regression_tests/source_dlopen/test.py @@ -0,0 +1,70 @@ +import openmc +import pytest +import os +import glob + +from tests.testing_harness import PyAPITestHarness + +# compile the external source +def compile_source(): + # first one should be CMakelists in share/ + files = glob.glob('../../../*/CMakeLists.txt') + assert len(files) != 0 + + # copy the cmakefile + status = os.system('rm -rf build') + assert os.WEXITSTATUS(status) == 0 + status = os.system('mkdir build ; cd build ; cp ../'+files[0]+' .') + assert os.WEXITSTATUS(status) == 0 + status = os.system('cd build ; cmake -DSOURCE_FILES=../source_sampling.cpp ; make') + assert os.WEXITSTATUS(status) == 0 + status = os.system('cp build/libsource.so .') + assert os.WEXITSTATUS(status) == 0 + status = os.system('rm -rf build') + assert os.WEXITSTATUS(status) == 0 + + return 0 + +class SourceTestHarness(PyAPITestHarness): + # build the test geometry + def _build_inputs(self): + mats = openmc.Materials() + + natural_lead = openmc.Material(1, "natural_lead") + natural_lead.add_element('Pb', 1,'ao') + natural_lead.set_density('g/cm3', 11.34) + mats.append(natural_lead) + + # surfaces + surface_sph1 = openmc.Sphere(r=100,boundary_type='vacuum') + volume_sph1 = -surface_sph1 + + # cell + cell_1 = openmc.Cell(region=volume_sph1) + cell_1.fill = natural_lead #assigning a material to a cell + universe = openmc.Universe(cells=[cell_1]) #hint, this list will need to include the new cell + geom = openmc.Geometry(universe) + + # settings + sett = openmc.Settings() + batches = 10 + sett.batches = batches + sett.inactive = 0 + sett.particles = 1000 + sett.particle = "neutron" + sett.run_mode = 'fixed source' + + #source + source = openmc.Source() + source.library = './libsource.so' + sett.source = source + + # run + model = openmc.model.Model(geom,mats,sett) + model.export_to_xml() + return + +def test_dlopen_source(): + compile_source() + harness = SourceTestHarness('statepoint.10.h5') + harness.main() diff --git a/tests/unit_tests/dlopen_source/test_dlopen_source.py b/tests/unit_tests/dlopen_source/test_dlopen_source.py deleted file mode 100644 index 917f3fd4fe..0000000000 --- a/tests/unit_tests/dlopen_source/test_dlopen_source.py +++ /dev/null @@ -1,57 +0,0 @@ -import openmc -import pytest -import os - -# compile the external source -def compile_source(): - # needs a more robust way to know where the - # Makefile is - status = os.system('cp /usr/local/share/Makefile .') - assert os.WEXITSTATUS(status) == 0 - status = os.system('make') - assert os.WEXITSTATUS(status) == 0 - - return - -# build the test geometry -def make_geometry(): - mats = openmc.Materials() - - natural_lead = openmc.Material(1, "natural_lead") - natural_lead.add_element('Pb', 1,'ao') - natural_lead.set_density('g/cm3', 11.34) - mats.append(natural_lead) - - # surfaces - surface_sph1 = openmc.Sphere(r=100) - volume_sph1 = -surface_sph1 - - # cell - cell_1 = openmc.Cell(region=volume_sph1) - cell_1.fill = natural_lead #assigning a material to a cell - universe = openmc.Universe(cells=[cell_1]) #hint, this list will need to include the new cell - geom = openmc.Geometry(universe) - - # settings - sett = openmc.Settings() - batches = 10 - sett.batches = batches - sett.inactive = 0 - sett.particles = 1000 - sett.particle = "neutron" - sett.run_mode = 'fixed source' - - #source - source = openmc.Source() - source.library = PATH+'source_sampling.so' - sett.source = source - - # run - model = openmc.model.Model(geom,mats,sett) - return model - -def test_dlopen_source(): - compile_source() - model = make_geometry() - model.run() - From 8784fc5d86723969ca232cc792bd21e5a66febf5 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Tue, 15 Oct 2019 22:01:43 +0100 Subject: [PATCH 56/90] Stylistic changes --- src/source.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/source.cpp b/src/source.cpp index b7d1e24990..464a39948f 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -77,9 +77,9 @@ SourceDistribution::SourceDistribution(pugi::xml_node node) settings::path_source_library = get_node_value(node, "library", false, true); // check if it exists if (!file_exists(settings::path_source_library)) { - std::stringstream msg; - msg << "Library file " << settings::path_source_library << "' does not exist."; - fatal_error(msg); + std::stringstream msg; + msg << "Library file " << settings::path_source_library << "' does not exist."; + fatal_error(msg); } } else { From 8c432a2e5471cbb4d8d139ffa0b100e3629a2d5b Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Tue, 15 Oct 2019 22:10:33 +0100 Subject: [PATCH 57/90] added unit test for dlopen source, and changed library variable name --- openmc/source.py | 6 +++--- tests/unit_tests/test_source.py | 8 ++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/openmc/source.py b/openmc/source.py index f8ea8ea936..339d41723e 100644 --- a/openmc/source.py +++ b/openmc/source.py @@ -49,7 +49,7 @@ class Source(object): self._angle = None self._energy = None self._file = None - self._source_library = None + self._library = None if space is not None: self.space = space @@ -70,7 +70,7 @@ class Source(object): @property def library(self): - return self._source_library + return self._library @property def space(self): @@ -100,7 +100,7 @@ class Source(object): @library.setter def library(self, library_name): cv.check_type('library', library_name, str) - self._source_library = library_name + self._library = library_name @space.setter def space(self, space): diff --git a/tests/unit_tests/test_source.py b/tests/unit_tests/test_source.py index 1c70e159d2..d4d17a3dab 100644 --- a/tests/unit_tests/test_source.py +++ b/tests/unit_tests/test_source.py @@ -34,3 +34,11 @@ def test_source_file(): elem = src.to_xml_element() assert 'strength' in elem.attrib assert 'file' in elem.attrib + +def test_source_dlopen(): + library = './libsource.so' + src = openmc.Source(library=library) + assert src.library == library + + elem = src.to_xml_element() + assert 'library' in elem.attrib From 7d87979880b7143c7435b61a9f0006964828c55b Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Tue, 15 Oct 2019 22:58:36 +0100 Subject: [PATCH 58/90] Added supporting documentation --- docs/source/io_formats/settings.rst | 69 +++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/docs/source/io_formats/settings.rst b/docs/source/io_formats/settings.rst index 2e2648237b..1d35c18aaf 100644 --- a/docs/source/io_formats/settings.rst +++ b/docs/source/io_formats/settings.rst @@ -459,6 +459,15 @@ attributes/sub-elements: *Default*: None + :library: + If this attribute is given, it indicates that the source is to be instanciated + from an externally compiled source function. This source can be as complex as + is required to define the source for your problem. The only requirement that + is made upon this source, is that there is a function called sample_source() + more documentation on how to build sources can be found in :ref:`custom_source` + + *Deafult*: None + :space: An element specifying the spatial distribution of source sites. This element has the following attributes: @@ -591,6 +600,66 @@ attributes/sub-elements: *Default*: false +.. _custom_source: + +Custom Sources +++++++++++++++++++++++++++++++++++++ + +It is often the case that one may wish to simulate a complex source distribution, +which may include physics not present within OpenMC or to be phase space complex. It +is possible to define complex source with an externally defined source function +and loaded at runtime. A simple example source is shown below. + +.. code-block:: c++ + + #include + #include "openmc/random_lcg.h" + #include "openmc/source.h" + #include "openmc/particle.h" + + // you must have external C linkage here + extern "C" openmc::Particle::Bank sample_source() { + openmc::Particle::Bank particle; + // wgt + particle.particle = openmc::Particle::Type::neutron; + particle.wgt = 1.0; + // position + double angle = 2.*M_PI*openmc::prn(); + double radius = 3.0; + particle.r.x = radius*std::cos(angle); + particle.r.y = radius*std::sin(angle); + particle.r.z = 0.; + // angle + particle.u = {1.,0,0}; + particle.E = 14.08e6; + particle.delayed_group = 0; + return particle; + } + +The above source, creates 14.08 MeV neutrons, with an istropic direction +vector but distributed in a ring of radius three cm. This routine is +not particular complex, but should serve as an example upon which to build +more complicated sources. + + .. note:: The function signature must be declared to be extern. + + .. note:: You should only use the openmc::prn() random number generator + +In order to build your external source you need the CMakeLists.txt file that +was created during the OpenMC installation process (found in the share subdirectory) +and your source file(s). + +.. code-block:: bash + + cp $HOME/openmc/share/CMakeLists.txt. + mkdir bld + cd bld + cmake .. -DSOURCE_FILES=source_sampling.cpp + make + +You will now have a libsouce.so file in this directory, now point the library +attribute of source to this file and you will be able to sample particles. + .. _univariate: Univariate Probability Distributions From 94285c39ee29dfdafa49ed5c453f9f95e77126d5 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 16 Oct 2019 07:41:14 +0100 Subject: [PATCH 59/90] Wrapped the first instance trying to use dlopen in a posix safety ifdef --- src/source.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/source.cpp b/src/source.cpp index 464a39948f..066e3a52cc 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -2,7 +2,10 @@ #include // for move #include // for stringstream + +#if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__)) #include // for dlopen +#endif #include #include "xtensor/xadapt.hpp" @@ -277,12 +280,17 @@ void initialize_source() msg << "Sampling from library source " << settings::path_source << "..."; write_message(msg, 6); + #if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__)) // Open the library void* source_library = dlopen(settings::path_source_library.c_str(),RTLD_LAZY); if(!source_library) { std::stringstream msg("Couldnt open source library " + settings::path_source_library); fatal_error(msg); } + #else + std::stringstream msg("This feature has not yet been implemented for non POSIX systems"); + fatal_error(msg); + #endif // load the symbol typedef Particle::Bank (*sample_t)(); From 649a6336000cbe821007d97a7aa5ba155a7b63f3 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 16 Oct 2019 14:21:27 +0100 Subject: [PATCH 60/90] made the building of the cmake more sensible --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 18de941218..d61477f190 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -409,7 +409,7 @@ install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) #============================= # write the dl_open Makefile #============================= -file(WRITE ${CMAKE_INSTALL_PREFIX}/share/CMakeLists.txt " +file(WRITE ${CMAKE_BINARY_DIR}/CMakeLists.txt " cmake_minimum_required(VERSION 3.3 FATAL_ERROR) project(openmc_sources C CXX) add_library(source SHARED \$\{SOURCE_FILES\}) @@ -417,4 +417,4 @@ target_include_directories(source PUBLIC ${PROJECT_SOURCE_DIR}/include ${PROJECT_SOURCE_DIR}/vendor/pugixml ${HDF5_INCLUDE_DIRS}) target_link_libraries(source ${ldflags} ${HDF5_LIBRARIES} ${HDF5_HL_LIBRARIES}) ") - +install(FILES ${CMAKE_BINARY_DIR}/CMakeLists.txt DESTINATION share) From d81bf9a2817084dc96aa93d226db4261731a2c3d Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 16 Oct 2019 20:27:39 +0100 Subject: [PATCH 61/90] try adding an init py file --- tests/regression_tests/source_dlopen/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/regression_tests/source_dlopen/__init__.py diff --git a/tests/regression_tests/source_dlopen/__init__.py b/tests/regression_tests/source_dlopen/__init__.py new file mode 100644 index 0000000000..e69de29bb2 From b39cbe404d610b7626f270bb5de0b5e1634c889a Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 16 Oct 2019 21:07:31 +0100 Subject: [PATCH 62/90] cxx standards for the source compile --- CMakeLists.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d61477f190..ef67da4e50 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -412,7 +412,12 @@ install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) file(WRITE ${CMAKE_BINARY_DIR}/CMakeLists.txt " cmake_minimum_required(VERSION 3.3 FATAL_ERROR) project(openmc_sources C CXX) -add_library(source SHARED \$\{SOURCE_FILES\}) +add_library(source SHARED \$\{SOURCE_FILES\})") +get_target_property(cxx_std openmc CXX_STANDARD) +get_target_property(cxx_ext openmc CXX_EXTENSIONS) +file(APPEND ${CMAKE_BINARY_DIR}/CMakeLists.txt " +set_target_properties(source PROPERTIES CXX_STANDARD ${cxx_std} + CXX_EXTENSIONS ${cxx_ext}) target_include_directories(source PUBLIC ${PROJECT_SOURCE_DIR}/include ${PROJECT_SOURCE_DIR}/vendor/pugixml ${HDF5_INCLUDE_DIRS}) target_link_libraries(source ${ldflags} ${HDF5_LIBRARIES} ${HDF5_HL_LIBRARIES}) From f1d8565ef4909013e2c1ffda282d3035852ee7e0 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Tue, 11 Feb 2020 22:07:53 +0000 Subject: [PATCH 63/90] updated signatures according to new changes in how seed is set --- src/source.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/source.cpp b/src/source.cpp index 066e3a52cc..b1fd8c71e9 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -293,7 +293,7 @@ void initialize_source() #endif // load the symbol - typedef Particle::Bank (*sample_t)(); + typedef Particle::Bank (*sample_t)(uint64_t *seed); // reset errors dlerror(); @@ -315,10 +315,10 @@ void initialize_source() // initialize random number seed int64_t id = simulation::total_gen*settings::n_particles + simulation::work_index[mpi::rank] + i + 1; - set_particle_seed(id); + uint64_t seed = init_seed(id, STREAM_SOURCE); // sample external source distribution - simulation::source_bank[i] = sample_source(); + simulation::source_bank[i] = sample_source(&seed); } // release the library dlclose(source_library); @@ -405,7 +405,7 @@ void fill_source_bank_fixedsource() } // load the symbol - typedef Particle::Bank (*sample_t)(); + typedef Particle::Bank (*sample_t)(uint64_t *seed); // reset errors dlerror(); @@ -427,10 +427,10 @@ void fill_source_bank_fixedsource() // initialize random number seed int64_t id = simulation::total_gen*settings::n_particles + simulation::work_index[mpi::rank] + i + 1; - set_particle_seed(id); - + uint64_t seed = init_seed(id, STREAM_SOURCE); + // sample external source distribution - simulation::source_bank[i] = sample_source(); + simulation::source_bank[i] = sample_source(&seed); } // release the library From e98d7fb28e1aea224c824342922c60cb9d363c17 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 12 Feb 2020 09:29:02 +0000 Subject: [PATCH 64/90] Updated the source routines according to passing seed to prn --- src/source.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/source.cpp b/src/source.cpp index b1fd8c71e9..465c2aac14 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -293,7 +293,7 @@ void initialize_source() #endif // load the symbol - typedef Particle::Bank (*sample_t)(uint64_t *seed); + typedef Particle::Bank (*sample_t)(uint64_t seed); // reset errors dlerror(); @@ -318,7 +318,7 @@ void initialize_source() uint64_t seed = init_seed(id, STREAM_SOURCE); // sample external source distribution - simulation::source_bank[i] = sample_source(&seed); + simulation::source_bank[i] = sample_source(seed); } // release the library dlclose(source_library); @@ -405,7 +405,7 @@ void fill_source_bank_fixedsource() } // load the symbol - typedef Particle::Bank (*sample_t)(uint64_t *seed); + typedef Particle::Bank (*sample_t)(uint64_t seed); // reset errors dlerror(); @@ -430,7 +430,7 @@ void fill_source_bank_fixedsource() uint64_t seed = init_seed(id, STREAM_SOURCE); // sample external source distribution - simulation::source_bank[i] = sample_source(&seed); + simulation::source_bank[i] = sample_source(seed); } // release the library From 2823dd07e9db9397c657da2edf4b9e496b5cc3d3 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 12 Feb 2020 09:43:35 +0000 Subject: [PATCH 65/90] Updated documentation to reflect changes --- docs/source/io_formats/settings.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/io_formats/settings.rst b/docs/source/io_formats/settings.rst index 1d35c18aaf..fde374b8d1 100644 --- a/docs/source/io_formats/settings.rst +++ b/docs/source/io_formats/settings.rst @@ -618,7 +618,7 @@ and loaded at runtime. A simple example source is shown below. #include "openmc/particle.h" // you must have external C linkage here - extern "C" openmc::Particle::Bank sample_source() { + extern "C" openmc::Particle::Bank sample_source(const int64_t seed) { openmc::Particle::Bank particle; // wgt particle.particle = openmc::Particle::Type::neutron; From 8f4b8535a6e9e24a686896b76d3ec9c1eab764c5 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 12 Feb 2020 12:24:29 +0000 Subject: [PATCH 66/90] Added statements for additional cmake targets --- CMakeLists.txt | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ef67da4e50..0d4c371f02 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -207,7 +207,6 @@ endif() add_library(faddeeva STATIC vendor/faddeeva/Faddeeva.cc) target_include_directories(faddeeva PUBLIC - $ $ ) target_compile_options(faddeeva PRIVATE ${cxxflags}) @@ -389,7 +388,7 @@ add_custom_command(TARGET libopenmc POST_BUILD #=============================================================================== set(INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake/OpenMC) -install(TARGETS openmc libopenmc faddeeva +install(TARGETS openmc libopenmc faddeeva pugixml gsl-lite EXPORT openmc-targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} @@ -406,20 +405,3 @@ install(FILES man/man1/openmc.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) install(FILES LICENSE DESTINATION "${CMAKE_INSTALL_DOCDIR}" RENAME copyright) install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) -#============================= -# write the dl_open Makefile -#============================= -file(WRITE ${CMAKE_BINARY_DIR}/CMakeLists.txt " -cmake_minimum_required(VERSION 3.3 FATAL_ERROR) -project(openmc_sources C CXX) -add_library(source SHARED \$\{SOURCE_FILES\})") -get_target_property(cxx_std openmc CXX_STANDARD) -get_target_property(cxx_ext openmc CXX_EXTENSIONS) -file(APPEND ${CMAKE_BINARY_DIR}/CMakeLists.txt " -set_target_properties(source PROPERTIES CXX_STANDARD ${cxx_std} - CXX_EXTENSIONS ${cxx_ext}) -target_include_directories(source PUBLIC ${PROJECT_SOURCE_DIR}/include - ${PROJECT_SOURCE_DIR}/vendor/pugixml ${HDF5_INCLUDE_DIRS}) -target_link_libraries(source ${ldflags} ${HDF5_LIBRARIES} ${HDF5_HL_LIBRARIES}) -") -install(FILES ${CMAKE_BINARY_DIR}/CMakeLists.txt DESTINATION share) From 59b00b2171281a39a21e9a1fe534b0376e4e8daf Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 12 Feb 2020 12:44:46 +0000 Subject: [PATCH 67/90] Updated spelling mistakes --- src/source.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/source.cpp b/src/source.cpp index 465c2aac14..f910b384c9 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -284,7 +284,7 @@ void initialize_source() // Open the library void* source_library = dlopen(settings::path_source_library.c_str(),RTLD_LAZY); if(!source_library) { - std::stringstream msg("Couldnt open source library " + settings::path_source_library); + std::stringstream msg("Couldn't open source library " + settings::path_source_library); fatal_error(msg); } #else @@ -306,7 +306,7 @@ void initialize_source() if (dlsym_error) { std::cout << dlsym_error << std::endl; dlclose(source_library); - fatal_error("Couldnt open the sample_source symbol"); + fatal_error("Couldn't open the sample_source symbol"); } // Generation source sites from specified distribution in the @@ -400,7 +400,7 @@ void fill_source_bank_fixedsource() // Open the library void* source_library = dlopen(settings::path_source_library.c_str(),RTLD_LAZY); if(!source_library) { - std::stringstream msg("Couldnt open source library " + settings::path_source_library); + std::stringstream msg("Couldn't open source library " + settings::path_source_library); fatal_error(msg); } @@ -418,7 +418,7 @@ void fill_source_bank_fixedsource() if (dlsym_error) { std::cout << dlsym_error << std::endl; dlclose(source_library); - fatal_error("Couldnt open the sample_source symbol"); + fatal_error("Couldn't open the sample_source symbol"); } // Generation source sites from specified distribution in the From 13133b87e535319c2986427d2969042e77c30efd Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 12 Feb 2020 12:47:39 +0000 Subject: [PATCH 68/90] Corrected style according to review --- examples/xml/custom_source/source_ring.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/xml/custom_source/source_ring.cpp b/examples/xml/custom_source/source_ring.cpp index ef2784ea36..fb361d1aa9 100644 --- a/examples/xml/custom_source/source_ring.cpp +++ b/examples/xml/custom_source/source_ring.cpp @@ -12,14 +12,14 @@ extern "C" openmc::Particle::Bank sample_source() { particle.wgt = 1.0; // position - double angle = 2.*M_PI*openmc::prn(); + double angle = 2. * M_PI * openmc::prn(); double radius = 3.0; - particle.r.x = radius*std::cos(angle); - particle.r.y = radius*std::sin(angle); - particle.r.z = 0.; + particle.r.x = radius * std::cos(angle); + particle.r.y = radius * std::sin(angle); + particle.r.z = 0.0; // angle - particle.u = {1.,0,0}; + particle.u = {1.0, 0.0, 0.0}; particle.E = 14.08e6; particle.delayed_group = 0; return particle; -} \ No newline at end of file +} From a392353a36fb83547647dbb76eeaa0794c6d8f8b Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 12 Feb 2020 12:48:54 +0000 Subject: [PATCH 69/90] line removal from review --- src/source.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/source.cpp b/src/source.cpp index f910b384c9..e7a476746e 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -275,7 +275,6 @@ void initialize_source() file_close(file_id); } else if ( settings::path_source_library != "" ) { // Get the source from a library object - std::stringstream msg; msg << "Sampling from library source " << settings::path_source << "..."; write_message(msg, 6); From 1fd6e003e803c10febdde8dde58aaf84f1ee7665 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 12 Feb 2020 12:50:53 +0000 Subject: [PATCH 70/90] further changes from review --- docs/source/io_formats/settings.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/source/io_formats/settings.rst b/docs/source/io_formats/settings.rst index fde374b8d1..f224df265f 100644 --- a/docs/source/io_formats/settings.rst +++ b/docs/source/io_formats/settings.rst @@ -658,7 +658,8 @@ and your source file(s). make You will now have a libsouce.so file in this directory, now point the library -attribute of source to this file and you will be able to sample particles. +attribute of the source XML element to this file and you will be able to sample +particles. .. _univariate: From 728969c5f2c43e429be5bd0ba55cefa64ed85e86 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 12 Feb 2020 12:55:21 +0000 Subject: [PATCH 71/90] further review comments regarding style --- docs/source/io_formats/settings.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/source/io_formats/settings.rst b/docs/source/io_formats/settings.rst index f224df265f..5392efef5f 100644 --- a/docs/source/io_formats/settings.rst +++ b/docs/source/io_formats/settings.rst @@ -603,12 +603,12 @@ attributes/sub-elements: .. _custom_source: Custom Sources -++++++++++++++++++++++++++++++++++++ +++++++++++++++ It is often the case that one may wish to simulate a complex source distribution, which may include physics not present within OpenMC or to be phase space complex. It -is possible to define complex source with an externally defined source function -and loaded at runtime. A simple example source is shown below. +is possible to define a complex source with an externally defined source function +that is loaded at runtime. A simple example source is shown below. .. code-block:: c++ @@ -637,7 +637,7 @@ and loaded at runtime. A simple example source is shown below. } The above source, creates 14.08 MeV neutrons, with an istropic direction -vector but distributed in a ring of radius three cm. This routine is +vector but distributed in a ring with a 3 cm radius. This routine is not particular complex, but should serve as an example upon which to build more complicated sources. From 7c2b695c982ff2f312789dc9557de080dced4e9e Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 12 Feb 2020 13:06:30 +0000 Subject: [PATCH 72/90] Refactor into function as suggested by review --- include/openmc/source.h | 3 ++ src/source.cpp | 110 +++++++++++++++++----------------------- 2 files changed, 49 insertions(+), 64 deletions(-) diff --git a/include/openmc/source.h b/include/openmc/source.h index 0a73260fd1..e980f2a8c8 100644 --- a/include/openmc/source.h +++ b/include/openmc/source.h @@ -72,6 +72,9 @@ Particle::Bank sample_external_source(uint64_t* seed); //! Fill source bank at end of generation for fixed source simulations void fill_source_bank_fixedsource(); +//! Fill source bank at the end of a generation for dlopen based source simulation +void fill_source_bank_dlopen_source(); + void free_memory_source(); } // namespace openmc diff --git a/src/source.cpp b/src/source.cpp index e7a476746e..2cda76a5c0 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -297,30 +297,7 @@ void initialize_source() // reset errors dlerror(); - // get the function from the library - sample_t sample_source = (sample_t) dlsym(source_library, "sample_source"); - const char *dlsym_error = dlerror(); - - // check for any dlsym errors - if (dlsym_error) { - std::cout << dlsym_error << std::endl; - dlclose(source_library); - fatal_error("Couldn't open the sample_source symbol"); - } - - // Generation source sites from specified distribution in the - // library source - for (int64_t i = 0; i < simulation::work_per_rank; ++i) { - // initialize random number seed - int64_t id = simulation::total_gen*settings::n_particles + - simulation::work_index[mpi::rank] + i + 1; - uint64_t seed = init_seed(id, STREAM_SOURCE); - - // sample external source distribution - simulation::source_bank[i] = sample_source(seed); - } - // release the library - dlclose(source_library); + fill_source_bank_dlopen_source(); } else { // Generation source sites from specified distribution in user input @@ -381,6 +358,50 @@ void free_memory_source() model::external_sources.clear(); } +// fill the source bank from the external source +void fill_source_bank_dlopen_source() +{ + std::stringstream msg; + + // Open the library + void* source_library = dlopen(settings::path_source_library.c_str(),RTLD_LAZY); + if(!source_library) { + std::stringstream msg("Couldn't open source library " + settings::path_source_library); + fatal_error(msg); + } + + // load the symbol + typedef Particle::Bank (*sample_t)(uint64_t seed); + + // reset errors + dlerror(); + + // get the function from the library + sample_t sample_source = (sample_t) dlsym(source_library, "sample_source"); + const char *dlsym_error = dlerror(); + + // check for any dlsym errors + if (dlsym_error) { + std::cout << dlsym_error << std::endl; + dlclose(source_library); + fatal_error("Couldn't open the sample_source symbol"); + } + + // Generation source sites from specified distribution in the + // library source + for (int64_t i = 0; i < simulation::work_per_rank; ++i) { + // initialize random number seed + int64_t id = simulation::total_gen*settings::n_particles + + simulation::work_index[mpi::rank] + i + 1; + uint64_t seed = init_seed(id, STREAM_SOURCE); + // sample external source distribution + simulation::source_bank[i] = sample_source(seed); + } + + // release the library + dlclose(source_library); +} + void fill_source_bank_fixedsource() { if (settings::path_source.empty() && settings::path_source_library.empty()) { @@ -394,46 +415,7 @@ void fill_source_bank_fixedsource() simulation::source_bank[i] = sample_external_source(&seed); } } else if (settings::path_source.empty() && !settings::path_source.empty()) { - std::stringstream msg; - - // Open the library - void* source_library = dlopen(settings::path_source_library.c_str(),RTLD_LAZY); - if(!source_library) { - std::stringstream msg("Couldn't open source library " + settings::path_source_library); - fatal_error(msg); - } - - // load the symbol - typedef Particle::Bank (*sample_t)(uint64_t seed); - - // reset errors - dlerror(); - - // get the function from the library - sample_t sample_source = (sample_t) dlsym(source_library, "sample_source"); - const char *dlsym_error = dlerror(); - - // check for any dlsym errors - if (dlsym_error) { - std::cout << dlsym_error << std::endl; - dlclose(source_library); - fatal_error("Couldn't open the sample_source symbol"); - } - - // Generation source sites from specified distribution in the - // library source - for (int64_t i = 0; i < simulation::work_per_rank; ++i) { - // initialize random number seed - int64_t id = simulation::total_gen*settings::n_particles + - simulation::work_index[mpi::rank] + i + 1; - uint64_t seed = init_seed(id, STREAM_SOURCE); - - // sample external source distribution - simulation::source_bank[i] = sample_source(seed); - } - - // release the library - dlclose(source_library); + fill_source_bank_dlopen_source(); } } From 09581ebf3816b234127dc2b4bee4b8004f8666e9 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 12 Feb 2020 13:11:22 +0000 Subject: [PATCH 73/90] updated for new cmake instructions --- docs/source/io_formats/settings.rst | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/docs/source/io_formats/settings.rst b/docs/source/io_formats/settings.rst index 5392efef5f..6f898f911b 100644 --- a/docs/source/io_formats/settings.rst +++ b/docs/source/io_formats/settings.rst @@ -645,19 +645,17 @@ more complicated sources. .. note:: You should only use the openmc::prn() random number generator -In order to build your external source you need the CMakeLists.txt file that -was created during the OpenMC installation process (found in the share subdirectory) -and your source file(s). +In order to build your external source you need the following CMakeLists.txt file -.. code-block:: bash +.. code-block:: cmake - cp $HOME/openmc/share/CMakeLists.txt. - mkdir bld - cd bld - cmake .. -DSOURCE_FILES=source_sampling.cpp - make + cmake_minimum_required(VERSION 3.3 FATAL_ERROR) + project(openmc_sources CXX) + add_library(source SHARED source_ring.cpp) + find_package(OpenMC REQUIRED HINTS ) + target_link_libraries(source OpenMC::libopenmc) -You will now have a libsouce.so file in this directory, now point the library +You will now have a libsouce.so (or .dylib) file in this directory, now point the library attribute of the source XML element to this file and you will be able to sample particles. From fba8f6a8e0a932d90a1c09b9a02d8f60fcaadbd7 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 12 Feb 2020 13:11:53 +0000 Subject: [PATCH 74/90] updated the example source according to style --- examples/xml/custom_source/source_ring.cpp | 33 +++++++++++----------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/examples/xml/custom_source/source_ring.cpp b/examples/xml/custom_source/source_ring.cpp index fb361d1aa9..babb2f591c 100644 --- a/examples/xml/custom_source/source_ring.cpp +++ b/examples/xml/custom_source/source_ring.cpp @@ -5,21 +5,22 @@ // you must have external C linkage here otherwise // dlopen will not find the file -extern "C" openmc::Particle::Bank sample_source() { - openmc::Particle::Bank particle; - // wgt - particle.particle = openmc::Particle::Type::neutron; - particle.wgt = 1.0; - // position +extern "C" openmc::Particle::Bank sample_source() +{ + openmc::Particle::Bank particle; + // wgt + particle.particle = openmc::Particle::Type::neutron; + particle.wgt = 1.0; + // position - double angle = 2. * M_PI * openmc::prn(); - double radius = 3.0; - particle.r.x = radius * std::cos(angle); - particle.r.y = radius * std::sin(angle); - particle.r.z = 0.0; - // angle - particle.u = {1.0, 0.0, 0.0}; - particle.E = 14.08e6; - particle.delayed_group = 0; - return particle; + double angle = 2. * M_PI * openmc::prn(); + double radius = 3.0; + particle.r.x = radius * std::cos(angle); + particle.r.y = radius * std::sin(angle); + particle.r.z = 0.0; + // angle + particle.u = {1.0, 0.0, 0.0}; + particle.E = 14.08e6; + particle.delayed_group = 0; + return particle; } From 5457326d378f34b97ba83052b1dff8c44bb2a787 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Mon, 17 Feb 2020 16:18:33 +0000 Subject: [PATCH 75/90] revert previous attempts to fix --- CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d4c371f02..d628cd2a6e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -388,7 +388,7 @@ add_custom_command(TARGET libopenmc POST_BUILD #=============================================================================== set(INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake/OpenMC) -install(TARGETS openmc libopenmc faddeeva pugixml gsl-lite +install(TARGETS openmc libopenmc faddeeva EXPORT openmc-targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} @@ -399,6 +399,9 @@ install(EXPORT openmc-targets NAMESPACE OpenMC:: DESTINATION ${INSTALL_CONFIGDIR}) +# Copy headers for vendored dependencies (note that all except faddeeva are handled +# separately since they are managed by CMake) +install(DIRECTORY vendor/faddeeva DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) install(DIRECTORY src/relaxng DESTINATION ${CMAKE_INSTALL_DATADIR}/openmc) install(FILES cmake/OpenMCConfig.cmake DESTINATION ${INSTALL_CONFIGDIR}) install(FILES man/man1/openmc.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) From c5dbff356a32ab89814db19d4eecc6ba2a534a92 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 14 Feb 2020 10:02:36 -0600 Subject: [PATCH 76/90] Fixes to custom source feature --- docs/source/io_formats/settings.rst | 63 +++++++------- examples/xml/custom_source/CMakeLists.txt | 8 ++ examples/xml/custom_source/settings.xml | 3 +- examples/xml/custom_source/source_ring.cpp | 11 ++- include/openmc/source.h | 6 +- src/source.cpp | 96 ++++++++++------------ 6 files changed, 92 insertions(+), 95 deletions(-) create mode 100644 examples/xml/custom_source/CMakeLists.txt diff --git a/docs/source/io_formats/settings.rst b/docs/source/io_formats/settings.rst index 6f898f911b..28754e7163 100644 --- a/docs/source/io_formats/settings.rst +++ b/docs/source/io_formats/settings.rst @@ -142,7 +142,7 @@ materials in the problem and is specified using a :ref:`mesh_element`. ---------------------------- Determines whether to use event-based parallelism instead of the default -history-based parallelism. +history-based parallelism. *Default*: false @@ -467,7 +467,7 @@ attributes/sub-elements: more documentation on how to build sources can be found in :ref:`custom_source` *Deafult*: None - + :space: An element specifying the spatial distribution of source sites. This element has the following attributes: @@ -605,59 +605,60 @@ attributes/sub-elements: Custom Sources ++++++++++++++ -It is often the case that one may wish to simulate a complex source distribution, -which may include physics not present within OpenMC or to be phase space complex. It -is possible to define a complex source with an externally defined source function -that is loaded at runtime. A simple example source is shown below. +It is often the case that one may wish to simulate a complex source +distribution, which may include physics not present within OpenMC or to be phase +space complex. It is possible to define a complex source with an externally +defined source function that is loaded at runtime. A simple example source is +shown below. .. code-block:: c++ - #include #include "openmc/random_lcg.h" #include "openmc/source.h" #include "openmc/particle.h" // you must have external C linkage here - extern "C" openmc::Particle::Bank sample_source(const int64_t seed) { + extern "C" openmc::Particle::Bank sample_source(uint64_t* seed) { openmc::Particle::Bank particle; - // wgt - particle.particle = openmc::Particle::Type::neutron; - particle.wgt = 1.0; - // position - double angle = 2.*M_PI*openmc::prn(); - double radius = 3.0; - particle.r.x = radius*std::cos(angle); - particle.r.y = radius*std::sin(angle); - particle.r.z = 0.; - // angle - particle.u = {1.,0,0}; - particle.E = 14.08e6; - particle.delayed_group = 0; - return particle; + // weight + particle.particle = openmc::Particle::Type::neutron; + particle.wgt = 1.0; + // position + double angle = 2.0 * M_PI * openmc::prn(seed); + double radius = 3.0; + particle.r.x = radius * std::cos(angle); + particle.r.y = radius * std::sin(angle); + particle.r.z = 0.0; + // angle + particle.u = {1.0, 0.0, 0.0}; + particle.E = 14.08e6; + particle.delayed_group = 0; + return particle; } The above source, creates 14.08 MeV neutrons, with an istropic direction vector but distributed in a ring with a 3 cm radius. This routine is -not particular complex, but should serve as an example upon which to build -more complicated sources. +not particularly complex, but should serve as an example upon which to build +more complicated sources. + + .. note:: The function signature must be declared to be extern "C". - .. note:: The function signature must be declared to be extern. - .. note:: You should only use the openmc::prn() random number generator -In order to build your external source you need the following CMakeLists.txt file +In order to build your external source you need the following CMakeLists.txt +file .. code-block:: cmake cmake_minimum_required(VERSION 3.3 FATAL_ERROR) - project(openmc_sources CXX) + project(openmc_sources CXX) add_library(source SHARED source_ring.cpp) find_package(OpenMC REQUIRED HINTS ) target_link_libraries(source OpenMC::libopenmc) -You will now have a libsouce.so (or .dylib) file in this directory, now point the library -attribute of the source XML element to this file and you will be able to sample -particles. +You will now have a libsource.so (or .dylib) file in this directory, now point +the library attribute of the source XML element to this file and you will be +able to sample particles. .. _univariate: diff --git a/examples/xml/custom_source/CMakeLists.txt b/examples/xml/custom_source/CMakeLists.txt new file mode 100644 index 0000000000..9498176944 --- /dev/null +++ b/examples/xml/custom_source/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.3 FATAL_ERROR) +project(openmc_sources CXX) +add_library(source SHARED source_ring.cpp) +find_package(OpenMC REQUIRED) +if (OpenMC_FOUND) + message(STATUS "Found OpenMC: ${OpenMC_DIR}") +endif() +target_link_libraries(source OpenMC::libopenmc) diff --git a/examples/xml/custom_source/settings.xml b/examples/xml/custom_source/settings.xml index f8d497459c..f935ed685e 100644 --- a/examples/xml/custom_source/settings.xml +++ b/examples/xml/custom_source/settings.xml @@ -8,8 +8,7 @@ - - ./source_ring.so + build/libsource.so diff --git a/examples/xml/custom_source/source_ring.cpp b/examples/xml/custom_source/source_ring.cpp index babb2f591c..0feb287022 100644 --- a/examples/xml/custom_source/source_ring.cpp +++ b/examples/xml/custom_source/source_ring.cpp @@ -3,17 +3,16 @@ #include "openmc/source.h" #include "openmc/particle.h" -// you must have external C linkage here otherwise +// you must have external C linkage here otherwise // dlopen will not find the file -extern "C" openmc::Particle::Bank sample_source() +extern "C" openmc::Particle::Bank sample_source(uint64_t* seed) { openmc::Particle::Bank particle; // wgt particle.particle = openmc::Particle::Type::neutron; - particle.wgt = 1.0; + particle.wgt = 1.0; // position - - double angle = 2. * M_PI * openmc::prn(); + double angle = 2. * M_PI * openmc::prn(seed); double radius = 3.0; particle.r.x = radius * std::cos(angle); particle.r.y = radius * std::sin(angle); @@ -22,5 +21,5 @@ extern "C" openmc::Particle::Bank sample_source() particle.u = {1.0, 0.0, 0.0}; particle.E = 14.08e6; particle.delayed_group = 0; - return particle; + return particle; } diff --git a/include/openmc/source.h b/include/openmc/source.h index e980f2a8c8..4db17f241a 100644 --- a/include/openmc/source.h +++ b/include/openmc/source.h @@ -59,10 +59,6 @@ private: //! Initialize source bank from file/distribution extern "C" void initialize_source(); -// as yet uncreated function to sample a source from a shared object -// -// extern "C" Particle::Bank sample_source(); - //! Sample a site from all external source distributions in proportion to their //! source strength //! \param[inout] seed Pseudorandom seed pointer @@ -73,7 +69,7 @@ Particle::Bank sample_external_source(uint64_t* seed); void fill_source_bank_fixedsource(); //! Fill source bank at the end of a generation for dlopen based source simulation -void fill_source_bank_dlopen_source(); +void fill_source_bank_custom_source(); void free_memory_source(); diff --git a/src/source.cpp b/src/source.cpp index 2cda76a5c0..96175804ce 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -1,10 +1,13 @@ #include "openmc/source.h" -#include // for move -#include // for stringstream - #if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__)) -#include // for dlopen +#define HAS_DYNAMIC_LINKING +#endif + +#include // for move + +#ifdef HAS_DYNAMIC_LINKING +#include // for dlopen, dlsym, dlclose, dlerror #endif #include @@ -77,13 +80,11 @@ SourceDistribution::SourceDistribution(pugi::xml_node node) settings::path_source)); } } else if (check_for_node(node, "library")) { - settings::path_source_library = get_node_value(node, "library", false, true); - // check if it exists - if (!file_exists(settings::path_source_library)) { - std::stringstream msg; - msg << "Library file " << settings::path_source_library << "' does not exist."; - fatal_error(msg); - } + settings::path_source_library = get_node_value(node, "library", false, true); + if (!file_exists(settings::path_source_library)) { + fatal_error(fmt::format("Source library '{}' does not exist.", + settings::path_source_library)); + } } else { // Spatial distribution for external source @@ -249,7 +250,7 @@ void initialize_source() { write_message("Initializing source particles...", 5); - if (settings::path_source != "") { + if (!settings::path_source.empty()) { // Read the source from a binary file instead of sampling from some // assumed source distribution @@ -273,31 +274,27 @@ void initialize_source() // Close file file_close(file_id); - } else if ( settings::path_source_library != "" ) { - // Get the source from a library object - std::stringstream msg; - msg << "Sampling from library source " << settings::path_source << "..."; - write_message(msg, 6); + } else if (!settings::path_source_library.empty()) { + + #ifdef HAS_DYNAMIC_LINKING + // Get the source from a library object + write_message(fmt::format("Sampling from library source {}...", + settings::path_source), 6); - #if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__)) // Open the library - void* source_library = dlopen(settings::path_source_library.c_str(),RTLD_LAZY); - if(!source_library) { - std::stringstream msg("Couldn't open source library " + settings::path_source_library); - fatal_error(msg); + auto source_library = dlopen(settings::path_source_library.c_str(), RTLD_LAZY); + if (!source_library) { + fatal_error("Couldn't open source library " + settings::path_source_library); } - #else - std::stringstream msg("This feature has not yet been implemented for non POSIX systems"); - fatal_error(msg); - #endif - - // load the symbol - typedef Particle::Bank (*sample_t)(uint64_t seed); // reset errors dlerror(); - fill_source_bank_dlopen_source(); + fill_source_bank_custom_source(); + #else + fatal_error("Custom source libraries have not yet been implemented for " + "non-POSIX systems"); + #endif } else { // Generation source sites from specified distribution in user input @@ -359,47 +356,44 @@ void free_memory_source() } // fill the source bank from the external source -void fill_source_bank_dlopen_source() +void fill_source_bank_custom_source() { - std::stringstream msg; - +#ifdef HAS_DYNAMIC_LINKING // Open the library - void* source_library = dlopen(settings::path_source_library.c_str(),RTLD_LAZY); - if(!source_library) { - std::stringstream msg("Couldn't open source library " + settings::path_source_library); - fatal_error(msg); + auto source_library = dlopen(settings::path_source_library.c_str(), RTLD_LAZY); + if (!source_library) { + fatal_error("Couldn't open source library " + settings::path_source_library); } - // load the symbol - typedef Particle::Bank (*sample_t)(uint64_t seed); - // reset errors dlerror(); // get the function from the library - sample_t sample_source = (sample_t) dlsym(source_library, "sample_source"); - const char *dlsym_error = dlerror(); - + using sample_t = Particle::Bank (*)(uint64_t* seed); + auto sample_source = reinterpret_cast(dlsym(source_library, "sample_source")); + // check for any dlsym errors + auto dlsym_error = dlerror(); if (dlsym_error) { - std::cout << dlsym_error << std::endl; dlclose(source_library); - fatal_error("Couldn't open the sample_source symbol"); + fatal_error(fmt::format("Couldn't open the sample_source symbol: {}", dlsym_error)); } // Generation source sites from specified distribution in the - // library source + // library source for (int64_t i = 0; i < simulation::work_per_rank; ++i) { // initialize random number seed - int64_t id = simulation::total_gen*settings::n_particles + - simulation::work_index[mpi::rank] + i + 1; + int64_t id = (simulation::total_gen + overall_generation()) * + settings::n_particles + simulation::work_index[mpi::rank] + i + 1; uint64_t seed = init_seed(id, STREAM_SOURCE); + // sample external source distribution - simulation::source_bank[i] = sample_source(seed); + simulation::source_bank[i] = sample_source(&seed); } // release the library dlclose(source_library); +#endif } void fill_source_bank_fixedsource() @@ -414,8 +408,8 @@ void fill_source_bank_fixedsource() // sample external source distribution simulation::source_bank[i] = sample_external_source(&seed); } - } else if (settings::path_source.empty() && !settings::path_source.empty()) { - fill_source_bank_dlopen_source(); + } else if (settings::path_source.empty() && !settings::path_source_library.empty()) { + fill_source_bank_custom_source(); } } From f3dbc24964439974a01994511af2d4184e95b287 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Tue, 18 Feb 2020 09:22:09 +0000 Subject: [PATCH 77/90] updated test signature --- tests/regression_tests/source_dlopen/source_sampling.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/regression_tests/source_dlopen/source_sampling.cpp b/tests/regression_tests/source_dlopen/source_sampling.cpp index 4b13c7db92..eaf8b74d92 100644 --- a/tests/regression_tests/source_dlopen/source_sampling.cpp +++ b/tests/regression_tests/source_dlopen/source_sampling.cpp @@ -5,7 +5,7 @@ // you must have external C linkage here otherwise // dlopen will not find the file -extern "C" openmc::Particle::Bank sample_source() { +extern "C" openmc::Particle::Bank sample_source(uint64_t *seed) { openmc::Particle::Bank particle; // wgt particle.particle = openmc::Particle::Type::neutron; @@ -16,7 +16,7 @@ extern "C" openmc::Particle::Bank sample_source() { particle.r.y = 0.; particle.r.z = 0.; // angle - particle.u = {1.,0,0}; + particle.u = {1.0, 0.0, 0.0}; particle.E = 14.08e6; particle.delayed_group = 0; return particle; From faf454c3442b1e0961f268cc6de7197759b5196e Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 19 Feb 2020 07:48:21 +0000 Subject: [PATCH 78/90] updated test to use new build method --- .../source_dlopen/clear_and_build.sh | 7 +++++ tests/regression_tests/source_dlopen/test.py | 31 +++++++++++-------- 2 files changed, 25 insertions(+), 13 deletions(-) create mode 100644 tests/regression_tests/source_dlopen/clear_and_build.sh diff --git a/tests/regression_tests/source_dlopen/clear_and_build.sh b/tests/regression_tests/source_dlopen/clear_and_build.sh new file mode 100644 index 0000000000..3f61b2cd8f --- /dev/null +++ b/tests/regression_tests/source_dlopen/clear_and_build.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +rm -rf build +mkdir build +cd build +cmake .. +make diff --git a/tests/regression_tests/source_dlopen/test.py b/tests/regression_tests/source_dlopen/test.py index ac63ca8045..1c174b775b 100644 --- a/tests/regression_tests/source_dlopen/test.py +++ b/tests/regression_tests/source_dlopen/test.py @@ -5,22 +5,27 @@ import glob from tests.testing_harness import PyAPITestHarness +def __write_cmake_file(openmc_dir, build_dir): + cmake_string = "cmake_minimum_required(VERSION 3.3 FATAL_ERROR)\n" + + "project(openmc_sources CXX)\n" + + "add_library(source SHARED source_ring.cpp)\n" + + "find_package(OpenMC REQUIRED HINTS {})\n" + + "target_link_libraries(source OpenMC::libopenmc)" + + f = open('CMakeLists.txt','w') + f.write(cmake_string.format(openmc_dir)) + f.close() + + return + # compile the external source def compile_source(): - # first one should be CMakelists in share/ - files = glob.glob('../../../*/CMakeLists.txt') - assert len(files) != 0 + + openmc_home_dir = os.environ['OPENMC_INSTALL_DIR'] + __write_cmake_file(openmc_home_dir,'build') # copy the cmakefile - status = os.system('rm -rf build') - assert os.WEXITSTATUS(status) == 0 - status = os.system('mkdir build ; cd build ; cp ../'+files[0]+' .') - assert os.WEXITSTATUS(status) == 0 - status = os.system('cd build ; cmake -DSOURCE_FILES=../source_sampling.cpp ; make') - assert os.WEXITSTATUS(status) == 0 - status = os.system('cp build/libsource.so .') - assert os.WEXITSTATUS(status) == 0 - status = os.system('rm -rf build') + status = os.system('sh clear_and_build.sh') assert os.WEXITSTATUS(status) == 0 return 0 @@ -56,7 +61,7 @@ class SourceTestHarness(PyAPITestHarness): #source source = openmc.Source() - source.library = './libsource.so' + source.library = 'build/libsource.so' sett.source = source # run From 030a5873257d926eb3e02dd51ebe1ce062eac7a0 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 19 Feb 2020 12:48:11 -0600 Subject: [PATCH 79/90] Get the source_dlopen test working --- .../source_dlopen/clear_and_build.sh | 7 -- .../source_dlopen/inputs_true.dat | 6 +- tests/regression_tests/source_dlopen/test.py | 110 +++++++++--------- 3 files changed, 57 insertions(+), 66 deletions(-) delete mode 100644 tests/regression_tests/source_dlopen/clear_and_build.sh diff --git a/tests/regression_tests/source_dlopen/clear_and_build.sh b/tests/regression_tests/source_dlopen/clear_and_build.sh deleted file mode 100644 index 3f61b2cd8f..0000000000 --- a/tests/regression_tests/source_dlopen/clear_and_build.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -rm -rf build -mkdir build -cd build -cmake .. -make diff --git a/tests/regression_tests/source_dlopen/inputs_true.dat b/tests/regression_tests/source_dlopen/inputs_true.dat index 23f3d84384..23878ac207 100644 --- a/tests/regression_tests/source_dlopen/inputs_true.dat +++ b/tests/regression_tests/source_dlopen/inputs_true.dat @@ -1,7 +1,7 @@ - - + + @@ -19,5 +19,5 @@ 1000 10 0 - + diff --git a/tests/regression_tests/source_dlopen/test.py b/tests/regression_tests/source_dlopen/test.py index 1c174b775b..12969cf29d 100644 --- a/tests/regression_tests/source_dlopen/test.py +++ b/tests/regression_tests/source_dlopen/test.py @@ -1,75 +1,73 @@ +from pathlib import Path +import os +import shutil +import subprocess +import textwrap + import openmc import pytest -import os -import glob from tests.testing_harness import PyAPITestHarness -def __write_cmake_file(openmc_dir, build_dir): - cmake_string = "cmake_minimum_required(VERSION 3.3 FATAL_ERROR)\n" + - "project(openmc_sources CXX)\n" + - "add_library(source SHARED source_ring.cpp)\n" + - "find_package(OpenMC REQUIRED HINTS {})\n" + - "target_link_libraries(source OpenMC::libopenmc)" - f = open('CMakeLists.txt','w') - f.write(cmake_string.format(openmc_dir)) - f.close() +@pytest.fixture +def compile_source(request): + """Compile the external source""" - return + # Get build directory and write CMakeLists.txt file + openmc_dir = Path(str(request.config.rootdir)) / 'build' + with open('CMakeLists.txt', 'w') as f: + f.write(textwrap.dedent(""" + cmake_minimum_required(VERSION 3.3 FATAL_ERROR) + project(openmc_sources CXX) + add_library(source SHARED source_sampling.cpp) + find_package(OpenMC REQUIRED HINTS {}) + target_link_libraries(source OpenMC::libopenmc) + """.format(openmc_dir))) -# compile the external source -def compile_source(): + # Create temporary build directory and change to there + local_builddir = Path('build') + local_builddir.mkdir(exist_ok=True) + os.chdir(str(local_builddir)) - openmc_home_dir = os.environ['OPENMC_INSTALL_DIR'] - __write_cmake_file(openmc_home_dir,'build') + # Run cmake/make to build the shared libary + subprocess.run(['cmake', os.path.pardir], check=True) + subprocess.run(['make'], check=True) + os.chdir(os.path.pardir) - # copy the cmakefile - status = os.system('sh clear_and_build.sh') - assert os.WEXITSTATUS(status) == 0 + yield - return 0 + # Remove local build directory when test is complete + shutil.rmtree('build') -class SourceTestHarness(PyAPITestHarness): - # build the test geometry - def _build_inputs(self): - mats = openmc.Materials() - natural_lead = openmc.Material(1, "natural_lead") - natural_lead.add_element('Pb', 1,'ao') - natural_lead.set_density('g/cm3', 11.34) - mats.append(natural_lead) +@pytest.fixture +def model(): + model = openmc.model.Model() + natural_lead = openmc.Material(name="natural_lead") + natural_lead.add_element('Pb', 1.0) + natural_lead.set_density('g/cm3', 11.34) + model.materials.append(natural_lead) - # surfaces - surface_sph1 = openmc.Sphere(r=100,boundary_type='vacuum') - volume_sph1 = -surface_sph1 - - # cell - cell_1 = openmc.Cell(region=volume_sph1) - cell_1.fill = natural_lead #assigning a material to a cell - universe = openmc.Universe(cells=[cell_1]) #hint, this list will need to include the new cell - geom = openmc.Geometry(universe) + # geometry + surface_sph1 = openmc.Sphere(r=100, boundary_type='vacuum') + cell_1 = openmc.Cell(fill=natural_lead, region=-surface_sph1) + model.geometry = openmc.Geometry([cell_1]) - # settings - sett = openmc.Settings() - batches = 10 - sett.batches = batches - sett.inactive = 0 - sett.particles = 1000 - sett.particle = "neutron" - sett.run_mode = 'fixed source' + # settings + model.settings.batches = 10 + model.settings.inactive = 0 + model.settings.particles = 1000 + model.settings.run_mode = 'fixed source' - #source - source = openmc.Source() - source.library = 'build/libsource.so' - sett.source = source + # custom source from shared library + source = openmc.Source() + source.library = 'build/libsource.so' + model.settings.source = source - # run - model = openmc.model.Model(geom,mats,sett) - model.export_to_xml() - return + return model -def test_dlopen_source(): - compile_source() - harness = SourceTestHarness('statepoint.10.h5') + +def test_dlopen_source(compile_source, model): + harness = PyAPITestHarness('statepoint.10.h5', model) harness.main() From 6bea6fe081d53ca4c0270bc3a2382d21cb6b59e2 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 24 Feb 2020 19:38:34 -0600 Subject: [PATCH 80/90] Revert changes in CMakeLists.txt --- CMakeLists.txt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5e74ce96dd..29eed4fb04 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -212,6 +212,7 @@ endif() add_library(faddeeva STATIC vendor/faddeeva/Faddeeva.cc) target_include_directories(faddeeva PUBLIC + $ $ ) target_compile_options(faddeeva PRIVATE ${cxxflags}) @@ -394,7 +395,7 @@ add_custom_command(TARGET libopenmc POST_BUILD configure_file(cmake/OpenMCConfig.cmake.in "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/OpenMCConfig.cmake" @ONLY) set(INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake/OpenMC) -install(TARGETS openmc libopenmc faddeeva +install(TARGETS openmc libopenmc faddeeva EXPORT openmc-targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} @@ -405,12 +406,12 @@ install(EXPORT openmc-targets NAMESPACE OpenMC:: DESTINATION ${INSTALL_CONFIGDIR}) -# Copy headers for vendored dependencies (note that all except faddeeva are handled -# separately since they are managed by CMake) -install(DIRECTORY vendor/faddeeva DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) install(DIRECTORY src/relaxng DESTINATION ${CMAKE_INSTALL_DATADIR}/openmc) install(FILES "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/OpenMCConfig.cmake" DESTINATION ${INSTALL_CONFIGDIR}) install(FILES man/man1/openmc.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) install(FILES LICENSE DESTINATION "${CMAKE_INSTALL_DOCDIR}" RENAME copyright) install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +# Copy headers for vendored dependencies (note that all except faddeeva are handled +# separately since they are managed by CMake) +install(DIRECTORY vendor/faddeeva DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) From 8e8d5e0478efd90ee21115c45d78874208ddc0f7 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 24 Feb 2020 19:53:46 -0600 Subject: [PATCH 81/90] Documentation updates --- docs/source/io_formats/settings.rst | 23 +++++++++++----------- examples/xml/custom_source/source_ring.cpp | 3 ++- openmc/source.py | 18 ++++++++++------- 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/docs/source/io_formats/settings.rst b/docs/source/io_formats/settings.rst index 28754e7163..83aa0691f4 100644 --- a/docs/source/io_formats/settings.rst +++ b/docs/source/io_formats/settings.rst @@ -460,13 +460,13 @@ attributes/sub-elements: *Default*: None :library: - If this attribute is given, it indicates that the source is to be instanciated - from an externally compiled source function. This source can be as complex as - is required to define the source for your problem. The only requirement that - is made upon this source, is that there is a function called sample_source() - more documentation on how to build sources can be found in :ref:`custom_source` + If this attribute is given, it indicates that the source is to be + instantiated from an externally compiled source function. This source can be + as complex as is required to define the source for your problem. The only + requirement is that there is a function called ``sample_source()``. More + documentation on how to build sources can be found in :ref:`custom_source`. - *Deafult*: None + *Default*: None :space: An element specifying the spatial distribution of source sites. This element @@ -645,8 +645,8 @@ more complicated sources. .. note:: You should only use the openmc::prn() random number generator -In order to build your external source you need the following CMakeLists.txt -file +In order to build your external source, you will need to link it against the +OpenMC shared library. This can be done by writing a CMakeLists.txt file: .. code-block:: cmake @@ -656,9 +656,10 @@ file find_package(OpenMC REQUIRED HINTS ) target_link_libraries(source OpenMC::libopenmc) -You will now have a libsource.so (or .dylib) file in this directory, now point -the library attribute of the source XML element to this file and you will be -able to sample particles. +After running ``cmake`` and ``make``, you will have a libsource.so (or .dylib) +file in your build directory. Setting the :attr:`openmc.Source.library` +attribute to the path of this shared library will indicate that it should be +used for sampling source particles at runtime. .. _univariate: diff --git a/examples/xml/custom_source/source_ring.cpp b/examples/xml/custom_source/source_ring.cpp index 0feb287022..d68122dd75 100644 --- a/examples/xml/custom_source/source_ring.cpp +++ b/examples/xml/custom_source/source_ring.cpp @@ -1,4 +1,5 @@ -#include +#include // for M_PI + #include "openmc/random_lcg.h" #include "openmc/source.h" #include "openmc/particle.h" diff --git a/openmc/source.py b/openmc/source.py index 339d41723e..7b709321f1 100644 --- a/openmc/source.py +++ b/openmc/source.py @@ -8,20 +8,22 @@ from openmc.stats.multivariate import UnitSphere, Spatial import openmc.checkvalue as cv -class Source(object): +class Source: """Distribution of phase space coordinates for source sites. Parameters ---------- - space : openmc.stats.Spatial, optional + space : openmc.stats.Spatial Spatial distribution of source sites - angle : openmc.stats.UnitSphere, optional + angle : openmc.stats.UnitSphere Angular distribution of source sites - energy : openmc.stats.Univariate, optional + energy : openmc.stats.Univariate Energy distribution of source sites - filename : str, optional + filename : str Source file from which sites should be sampled - strength : Real + library : str + Path to a custom source library + strength : float Strength of the source particle : {'neutron', 'photon'} Source particle type @@ -36,7 +38,9 @@ class Source(object): Energy distribution of source sites file : str or None Source file from which sites should be sampled - strength : Real + library : str or None + Path to a custom source library + strength : float Strength of the source particle : {'neutron', 'photon'} Source particle type From 2870da61e63b7dfa4d9c425d94f2736a7cdf8a54 Mon Sep 17 00:00:00 2001 From: Ethan Peterson Date: Fri, 21 Feb 2020 20:49:06 -0500 Subject: [PATCH 82/90] Refactoring improvements --- openmc/surface.py | 333 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 246 insertions(+), 87 deletions(-) diff --git a/openmc/surface.py b/openmc/surface.py index 92c828d65f..0c1513a527 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -24,6 +24,8 @@ _WARNING_KWARGS = """\ will not accept positional parameters for superclass arguments.\ """ +_SURFACE_CLASSES = {} + class Surface(IDManagerMixin, metaclass=ABCMeta): """An implicit surface with an associated boundary condition. @@ -77,6 +79,10 @@ class Surface(IDManagerMixin, metaclass=ABCMeta): # Value - coefficient value self._coefficients = {} + def __init_subclass__(cls, **kwargs): + super().__init_subclass__(**kwargs) + _SURFACE_CLASSES[cls._type] = cls + def __neg__(self): return Halfspace(self, '-') @@ -361,6 +367,36 @@ class PlaneMixin(metaclass=ABCMeta): self._periodic_surface = periodic_surface periodic_surface._periodic_surface = self + def _get_base_coeffs(self): + return (self.a, self.b, self.c, self.d) + + def _get_normal(self): + a, b, c = self._get_base_coeffs()[0:3] + return np.array((a, b, c)) / np.sqrt(a*a + b*b + c*c) + + def bounding_box(self, side): + # Compute the bounding box based on the normal vector to the plane + nhat = self._get_normal() + lb = np.array([-np.inf, -np.inf, -np.inf]) + ub = np.array([np.inf, np.inf, np.inf]) + # If the plane is axis aligned, find the proper bounding box + if np.any(np.isclose(np.abs(nhat), 1., rtol=0., atol=self._atol)): + sign = np.dot(np.array([1., 1., 1.]), nhat) + a, b, c, d = self._get_base_coeffs() + vals = tuple(d/i if round(i) != 0 else np.nan for i in (a, b, c)) + if side == '-': + if sign > 0: + ub = np.array([v if ~np.isnan(v) else np.inf for v in vals]) + else: + lb = np.array([v if ~np.isnan(v) else -np.inf for v in vals]) + elif side == '+': + if sign > 0: + lb = np.array([v if ~np.isnan(v) else -np.inf for v in vals]) + else: + ub = np.array([v if ~np.isnan(v) else np.inf for v in vals]) + + return (lb, ub) + def evaluate(self, point): """Evaluate the surface equation at a given point. @@ -509,6 +545,12 @@ class Plane(PlaneMixin, Surface): FutureWarning) setattr(self, k.lower(), val) + @classmethod + def __subclasshook__(cls, c): + if cls is Plane and c in (XPlane, YPlane, ZPlane): + return True + return NotImplemented + @property def a(self): return self.coefficients['a'] @@ -545,9 +587,6 @@ class Plane(PlaneMixin, Surface): check_type('D coefficient', d, Real) self._coefficients['d'] = d - def _get_base_coeffs(self): - return (self.a, self.b, self.c, self.d) - @classmethod def from_points(cls, p1, p2, p3, **kwargs): """Return a plane given three points that pass through it. @@ -638,29 +677,31 @@ class XPlane(PlaneMixin, Surface): def x0(self): return self.coefficients['x0'] + @property + def a(self): + return 1. + + @property + def b(self): + return 0. + + @property + def c(self): + return 0. + + @property + def d(self): + return self.x0 + @x0.setter def x0(self, x0): check_type('x0 coefficient', x0, Real) self._coefficients['x0'] = x0 - def _get_base_coeffs(self): - return (1., 0., 0., self.x0) - - def bounding_box(self, side): - if side == '-': - return (np.array([-np.inf, -np.inf, -np.inf]), - np.array([self.x0, np.inf, np.inf])) - elif side == '+': - return (np.array([self.x0, -np.inf, -np.inf]), - np.array([np.inf, np.inf, np.inf])) - def evaluate(self, point): return point[0] - self.x0 -Plane.register(XPlane) - - class YPlane(PlaneMixin, Surface): """A plane perpendicular to the y axis of the form :math:`y - y_0 = 0` @@ -718,29 +759,31 @@ class YPlane(PlaneMixin, Surface): def y0(self): return self.coefficients['y0'] + @property + def a(self): + return 0. + + @property + def b(self): + return 1. + + @property + def c(self): + return 0. + + @property + def d(self): + return self.y0 + @y0.setter def y0(self, y0): check_type('y0 coefficient', y0, Real) self._coefficients['y0'] = y0 - def _get_base_coeffs(self): - return (0., 1., 0., self.y0) - - def bounding_box(self, side): - if side == '-': - return (np.array([-np.inf, -np.inf, -np.inf]), - np.array([np.inf, self.y0, np.inf])) - elif side == '+': - return (np.array([-np.inf, self.y0, -np.inf]), - np.array([np.inf, np.inf, np.inf])) - def evaluate(self, point): return point[1] - self.y0 -Plane.register(YPlane) - - class ZPlane(PlaneMixin, Surface): """A plane perpendicular to the z axis of the form :math:`z - z_0 = 0` @@ -798,29 +841,31 @@ class ZPlane(PlaneMixin, Surface): def z0(self): return self.coefficients['z0'] + @property + def a(self): + return 0. + + @property + def b(self): + return 0. + + @property + def c(self): + return 1. + + @property + def d(self): + return self.z0 + @z0.setter def z0(self, z0): check_type('z0 coefficient', z0, Real) self._coefficients['z0'] = z0 - def _get_base_coeffs(self): - return (0., 0., 1., self.z0) - - def bounding_box(self, side): - if side == '-': - return (np.array([-np.inf, -np.inf, -np.inf]), - np.array([np.inf, np.inf, self.z0])) - elif side == '+': - return (np.array([-np.inf, -np.inf, self.z0]), - np.array([np.inf, np.inf, np.inf])) - def evaluate(self, point): return point[2] - self.z0 -Plane.register(ZPlane) - - class QuadricMixin(metaclass=ABCMeta): """A Mixin class implementing common functionality for quadric surfaces""" @@ -884,7 +929,7 @@ class QuadricMixin(metaclass=ABCMeta): """ x = np.asarray(point) A, b, c = self.get_Abc() - return np.matmul(x.T, np.matmul(A, x)) + np.matmul(b.T, x) + c + return x.T @ A @ x + b.T @ x + c def translate(self, vector, inplace=False): """Translate surface in given direction @@ -907,17 +952,20 @@ class QuadricMixin(metaclass=ABCMeta): surf = self if inplace else self.clone() - if set(('x0', 'y0', 'z0')).intersection(set(surf._coeff_keys)): + if any((isinstance(self, cls) for cls in (Cylinder, Sphere, Cone))): for vi, xi in zip(vector, ('x0', 'y0', 'z0')): - val = getattr(surf, xi, None) - if val is not None: + val = getattr(surf, xi) + try: setattr(surf, xi, val + vi) - else: + except AttributeError: + # That attribute is read only i.e x0 for XCylinder + pass + + elif isinstance(self, Quadric): A, bvec, cnst = self.get_Abc() - g, h, j = bvec - 2*np.matmul(vector.T, A) - k = cnst + np.matmul(vector.T, np.matmul(A, vector)) \ - - np.matmul(bvec.T, vector) + g, h, j = bvec - 2*vector.T @ A + k = cnst + vector.T @ A @ vector - bvec.T @ vector for key, val in zip(('g', 'h', 'j', 'k'), (g, h, j, k)): setattr(surf, key, val) @@ -993,16 +1041,17 @@ class Cylinder(QuadricMixin, Surface): _coeff_keys = ('x0', 'y0', 'z0', 'r', 'dx', 'dy', 'dz') def __init__(self, x0=0., y0=0., z0=0., r=1., dx=0., dy=0., dz=1., **kwargs): - raise NotImplementedError('There is no C++ implementation for general ' - 'Cylinders yet, please use ' - 'openmc.model.funcs.cylinder_from_points to ' - 'return a Quadric instance instead for now') - super().__init__(**kwargs) for key, val in zip(self._coeff_keys, (x0, y0, z0, r, dx, dy, dz)): setattr(self, key, val) + @classmethod + def __subclasshook__(cls, c): + if cls is Cylinder and c in (XCylinder, YCylinder, ZCylinder): + return True + return NotImplemented + @property def x0(self): return self.coefficients['x0'] @@ -1118,10 +1167,6 @@ class Cylinder(QuadricMixin, Surface): radius r. """ - raise NotImplementedError('There is no C++ implementation for general ' - 'Cylinders yet, please use ' - 'openmc.model.funcs.cylinder_from_points to ' - 'return a Quadric instance instead for now') # Convert to numpy arrays p1 = np.asarray(p1) p2 = np.asarray(p2) @@ -1130,6 +1175,30 @@ class Cylinder(QuadricMixin, Surface): return cls(x0=x0, y0=y0, z0=z0, r=r, dx=dx, dy=dy, dz=dz, **kwargs) + def to_xml_element(self): + """Return XML representation of the surface + + Returns + ------- + element : xml.etree.ElementTree.Element + XML element containing source data + + """ + # This method overrides Surface.to_xml_element to generate a Quadric + # since the C++ layer doesn't support Cylinders right now + element = ET.Element("surface") + element.set("id", str(self._id)) + + if len(self._name) > 0: + element.set("name", str(self._name)) + + element.set("type", 'quadric') + if self.boundary_type != 'transmission': + element.set("boundary", self.boundary_type) + element.set("coeffs", ' '.join([str(c) for c in self._get_base_coeffs()])) + + return element + class XCylinder(QuadricMixin, Surface): """An infinite cylinder whose length is parallel to the x-axis of the form @@ -1202,6 +1271,22 @@ class XCylinder(QuadricMixin, Surface): def r(self): return self.coefficients['r'] + @property + def x0(self): + return 0. + + @property + def dx(self): + return 1. + + @property + def dy(self): + return 0. + + @property + def dz(self): + return 0. + @y0.setter def y0(self, y0): check_type('y0 coefficient', y0, Real) @@ -1240,9 +1325,6 @@ class XCylinder(QuadricMixin, Surface): return y*y + z*z - self.r**2 -Cylinder.register(XCylinder) - - class YCylinder(QuadricMixin, Surface): """An infinite cylinder whose length is parallel to the y-axis of the form :math:`(x - x_0)^2 + (z - z_0)^2 = r^2`. @@ -1314,6 +1396,22 @@ class YCylinder(QuadricMixin, Surface): def r(self): return self.coefficients['r'] + @property + def y0(self): + return 0. + + @property + def dx(self): + return 0. + + @property + def dy(self): + return 1. + + @property + def dz(self): + return 0. + @x0.setter def x0(self, x0): check_type('x0 coefficient', x0, Real) @@ -1352,9 +1450,6 @@ class YCylinder(QuadricMixin, Surface): return x*x + z*z - self.r**2 -Cylinder.register(YCylinder) - - class ZCylinder(QuadricMixin, Surface): """An infinite cylinder whose length is parallel to the z-axis of the form :math:`(x - x_0)^2 + (y - y_0)^2 = r^2`. @@ -1426,6 +1521,22 @@ class ZCylinder(QuadricMixin, Surface): def r(self): return self.coefficients['r'] + @property + def z0(self): + return 0. + + @property + def dx(self): + return 0. + + @property + def dy(self): + return 0. + + @property + def dz(self): + return 1. + @x0.setter def x0(self, x0): check_type('x0 coefficient', x0, Real) @@ -1464,9 +1575,6 @@ class ZCylinder(QuadricMixin, Surface): return x*x + y*y - self.r**2 -Cylinder.register(ZCylinder) - - class Sphere(QuadricMixin, Surface): """A sphere of the form :math:`(x - x_0)^2 + (y - y_0)^2 + (z - z_0)^2 = r^2`. @@ -1656,9 +1764,6 @@ class Cone(QuadricMixin, Surface): _coeff_keys = ('x0', 'y0', 'z0', 'r2', 'dx', 'dy', 'dz') def __init__(self, x0=0., y0=0., z0=0., r2=1., dx=0., dy=0., dz=1., **kwargs): - raise NotImplementedError('There is no C++ implementation for general ' - 'Cones yet, this functionality should be ' - 'added soon.') R2 = kwargs.pop('R2', None) if R2 is not None: warn(_WARNING_UPPER.format(type(self).__name__, 'r2', 'R2'), @@ -1669,6 +1774,12 @@ class Cone(QuadricMixin, Surface): for key, val in zip(self._coeff_keys, (x0, y0, z0, r2, dx, dy, dz)): setattr(self, key, val) + @classmethod + def __subclasshook__(cls, c): + if cls is Cone and c in (XCone, YCone, ZCone): + return True + return NotImplemented + @property def x0(self): return self.coefficients['x0'] @@ -1761,11 +1872,35 @@ class Cone(QuadricMixin, Surface): f = 2*dx*dz g = -2*(dx*dx*x0 + dx*dy*y0 + dx*dz*z0 - cos2) h = -2*(dy*dy*y0 + dx*dy*x0 + dy*dz*z0 - cos2) - j = -2*(dz*dz*y0 + dx*dz*x0 + dy*dz*y0 - cos2) + j = -2*(dz*dz*z0 + dx*dz*x0 + dy*dz*y0 - cos2) k = (dx*x0 + dy*y0 + dz*z0)**2 - cos2*(x0*x0 + y0*y0 + z0*z0) return (a, b, c, d, e, f, g, h, j, k) + def to_xml_element(self): + """Return XML representation of the surface + + Returns + ------- + element : xml.etree.ElementTree.Element + XML element containing source data + + """ + # This method overrides Surface.to_xml_element to generate a Quadric + # since the C++ layer doesn't support Cylinders right now + element = ET.Element("surface") + element.set("id", str(self._id)) + + if len(self._name) > 0: + element.set("name", str(self._name)) + + element.set("type", 'quadric') + if self.boundary_type != 'transmission': + element.set("boundary", self.boundary_type) + element.set("coeffs", ' '.join([str(c) for c in self._get_base_coeffs()])) + + return element + class XCone(QuadricMixin, Surface): """A cone parallel to the x-axis of the form :math:`(y - y_0)^2 + (z - z_0)^2 = @@ -1845,6 +1980,18 @@ class XCone(QuadricMixin, Surface): def r2(self): return self.coefficients['r2'] + @property + def dx(self): + return 1. + + @property + def dy(self): + return 0. + + @property + def dz(self): + return 0. + @x0.setter def x0(self, x0): check_type('x0 coefficient', x0, Real) @@ -1883,9 +2030,6 @@ class XCone(QuadricMixin, Surface): return y*y + z*z - self.r2*x*x -Cone.register(XCone) - - class YCone(QuadricMixin, Surface): """A cone parallel to the y-axis of the form :math:`(x - x_0)^2 + (z - z_0)^2 = r^2 (y - y_0)^2`. @@ -1964,6 +2108,18 @@ class YCone(QuadricMixin, Surface): def r2(self): return self.coefficients['r2'] + @property + def dx(self): + return 0. + + @property + def dy(self): + return 1. + + @property + def dz(self): + return 0. + @x0.setter def x0(self, x0): check_type('x0 coefficient', x0, Real) @@ -2002,9 +2158,6 @@ class YCone(QuadricMixin, Surface): return x*x + z*z - self.r2*y*y -Cone.register(YCone) - - class ZCone(QuadricMixin, Surface): """A cone parallel to the x-axis of the form :math:`(x - x_0)^2 + (y - y_0)^2 = r^2 (z - z_0)^2`. @@ -2083,6 +2236,18 @@ class ZCone(QuadricMixin, Surface): def r2(self): return self.coefficients['r2'] + @property + def dx(self): + return 0. + + @property + def dy(self): + return 0. + + @property + def dz(self): + return 1. + @x0.setter def x0(self, x0): check_type('x0 coefficient', x0, Real) @@ -2121,9 +2286,6 @@ class ZCone(QuadricMixin, Surface): return x*x + y*y - self.r2*z*z -Cone.register(ZCone) - - class Quadric(QuadricMixin, Surface): """A surface of the form :math:`Ax^2 + By^2 + Cz^2 + Dxy + Eyz + Fxz + Gx + Hy + Jz + K = 0`. @@ -2451,6 +2613,3 @@ class Halfspace(Region): # Return translated surface return type(self)(memo[key], self.side) - - -_SURFACE_CLASSES = {cls._type: cls for cls in Surface.__subclasses__()} From 089b6d04ef5609c5f0c205f773472d0f3afbc722 Mon Sep 17 00:00:00 2001 From: Ethan Peterson Date: Sat, 22 Feb 2020 08:03:35 -0500 Subject: [PATCH 83/90] removed __init_subclass__ since it's not in python 3.5 --- openmc/surface.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/openmc/surface.py b/openmc/surface.py index 0c1513a527..62ea83456c 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -79,10 +79,6 @@ class Surface(IDManagerMixin, metaclass=ABCMeta): # Value - coefficient value self._coefficients = {} - def __init_subclass__(cls, **kwargs): - super().__init_subclass__(**kwargs) - _SURFACE_CLASSES[cls._type] = cls - def __neg__(self): return Halfspace(self, '-') @@ -2613,3 +2609,5 @@ class Halfspace(Region): # Return translated surface return type(self)(memo[key], self.side) + +_SURFACE_CLASSES = {cls._type: cls for cls in Surface.__subclasses__()} From ac5bb4047d4dc4de9fe71877c05b3d26c123b386 Mon Sep 17 00:00:00 2001 From: Ethan Peterson Date: Sun, 23 Feb 2020 10:42:27 -0500 Subject: [PATCH 84/90] removed extraneous definition --- openmc/surface.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/openmc/surface.py b/openmc/surface.py index 62ea83456c..a4c8b8c88d 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -24,8 +24,6 @@ _WARNING_KWARGS = """\ will not accept positional parameters for superclass arguments.\ """ -_SURFACE_CLASSES = {} - class Surface(IDManagerMixin, metaclass=ABCMeta): """An implicit surface with an associated boundary condition. From 378856d434a1778d79bc3d3d34077914b3cbf88b Mon Sep 17 00:00:00 2001 From: Ethan Peterson Date: Mon, 24 Feb 2020 11:27:29 -0500 Subject: [PATCH 85/90] Apply suggestions from code review Co-Authored-By: Paul Romano --- openmc/surface.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/openmc/surface.py b/openmc/surface.py index a4c8b8c88d..a1696b08c4 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -366,7 +366,7 @@ class PlaneMixin(metaclass=ABCMeta): def _get_normal(self): a, b, c = self._get_base_coeffs()[0:3] - return np.array((a, b, c)) / np.sqrt(a*a + b*b + c*c) + return np.array((a, b, c)) / math.sqrt(a*a + b*b + c*c) def bounding_box(self, side): # Compute the bounding box based on the normal vector to the plane @@ -375,12 +375,12 @@ class PlaneMixin(metaclass=ABCMeta): ub = np.array([np.inf, np.inf, np.inf]) # If the plane is axis aligned, find the proper bounding box if np.any(np.isclose(np.abs(nhat), 1., rtol=0., atol=self._atol)): - sign = np.dot(np.array([1., 1., 1.]), nhat) + sign = nhat.sum() a, b, c, d = self._get_base_coeffs() - vals = tuple(d/i if round(i) != 0 else np.nan for i in (a, b, c)) + vals = [d/val if round(val) != 0 else np.nan for val in (a, b, c)] if side == '-': if sign > 0: - ub = np.array([v if ~np.isnan(v) else np.inf for v in vals]) + ub = np.array([v if not np.isnan(v) else np.inf for v in vals]) else: lb = np.array([v if ~np.isnan(v) else -np.inf for v in vals]) elif side == '+': @@ -946,7 +946,7 @@ class QuadricMixin(metaclass=ABCMeta): surf = self if inplace else self.clone() - if any((isinstance(self, cls) for cls in (Cylinder, Sphere, Cone))): + if hasattr(self, 'x0'): for vi, xi in zip(vector, ('x0', 'y0', 'z0')): val = getattr(surf, xi) try: @@ -955,7 +955,7 @@ class QuadricMixin(metaclass=ABCMeta): # That attribute is read only i.e x0 for XCylinder pass - elif isinstance(self, Quadric): + else: A, bvec, cnst = self.get_Abc() g, h, j = bvec - 2*vector.T @ A From 3d3a27f841b03c765196a84c3a5dfd0e9b76cc70 Mon Sep 17 00:00:00 2001 From: Ethan Peterson Date: Mon, 24 Feb 2020 11:55:42 -0500 Subject: [PATCH 86/90] applied suggestions from code review --- docs/source/usersguide/install.rst | 2 +- openmc/model/funcs.py | 47 +----------------- openmc/surface.py | 79 +++++++++++++++++------------- setup.py | 3 +- tests/unit_tests/test_surface.py | 27 +++++----- 5 files changed, 63 insertions(+), 95 deletions(-) diff --git a/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index 66440a3c37..ec10fea4fa 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -404,7 +404,7 @@ to install the Python package in :ref:`"editable" mode `. Prerequisites ------------- -The Python API works with Python 3.4+. In addition to Python itself, the API +The Python API works with Python 3.5+. In addition to Python itself, the API relies on a number of third-party packages. All prerequisites can be installed using Conda_ (recommended), pip_, or through the package manager in most Linux distributions. To run simulations in parallel using MPI, it is recommended to diff --git a/openmc/model/funcs.py b/openmc/model/funcs.py index 3ee5052402..9999c383ff 100644 --- a/openmc/model/funcs.py +++ b/openmc/model/funcs.py @@ -373,52 +373,7 @@ def get_hexagonal_prism(*args, **kwargs): return hexagonal_prism(*args, **kwargs) -def cylinder_from_points(p1, p2, r, **kwargs): - """Return cylinder defined by two points passing through its center. - - Parameters - ---------- - p1, p2 : 3-tuples - Coordinates of two points that pass through the center of the cylinder - r : float - Radius of the cylinder - kwargs : dict - Keyword arguments passed to the :class:`openmc.Quadric` constructor - - Returns - ------- - openmc.Quadric - Quadric surface representing the cylinder. - - """ - # Get x, y, z coordinates of two points - x1, y1, z1 = p1 - x2, y2, z2 = p2 - - # Define intermediate terms - dx = x2 - x1 - dy = y2 - y1 - dz = z2 - z1 - cx = y1*z2 - y2*z1 - cy = x2*z1 - x1*z2 - cz = x1*y2 - x2*y1 - - # Given p=(x,y,z), p1=(x1, y1, z1), p2=(x2, y2, z2), the equation for the - # cylinder can be derived as r = |(p - p1) ⨯ (p - p2)| / |p2 - p1|. - # Expanding out all terms and grouping according to what Quadric expects - # gives the following coefficients. - kwargs['a'] = dy*dy + dz*dz - kwargs['b'] = dx*dx + dz*dz - kwargs['c'] = dx*dx + dy*dy - kwargs['d'] = -2*dx*dy - kwargs['e'] = -2*dy*dz - kwargs['f'] = -2*dx*dz - kwargs['g'] = 2*(cy*dz - cz*dy) - kwargs['h'] = 2*(cz*dx - cx*dz) - kwargs['j'] = 2*(cx*dy - cy*dx) - kwargs['k'] = cx*cx + cy*cy + cz*cz - (dx*dx + dy*dy + dz*dz)*r*r - - return Quadric(**kwargs) +cylinder_from_points = Cylinder.from_points def subdivide(surfaces): diff --git a/openmc/surface.py b/openmc/surface.py index a1696b08c4..4d0434a4b0 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -3,13 +3,14 @@ from collections import OrderedDict from copy import deepcopy from numbers import Real from xml.etree import ElementTree as ET -from warnings import warn +from warnings import warn, catch_warnings, simplefilter +import math import numpy as np from openmc.checkvalue import check_type, check_value from openmc.region import Region, Intersection, Union -from openmc.mixin import IDManagerMixin +from openmc.mixin import IDManagerMixin, IDWarning _BOUNDARY_TYPES = ['transmission', 'vacuum', 'reflective', 'periodic', 'white'] @@ -369,10 +370,32 @@ class PlaneMixin(metaclass=ABCMeta): return np.array((a, b, c)) / math.sqrt(a*a + b*b + c*c) def bounding_box(self, side): + """Determine an axis-aligned bounding box. + + An axis-aligned bounding box for Plane half-spaces is represented by + its lower-left and upper-right coordinates. If the half-space is + unbounded in a particular direction, numpy.inf is used to represent + infinity. + + Parameters + ---------- + side : {'+', '-'} + Indicates the negative or positive half-space + + Returns + ------- + numpy.ndarray + Lower-left coordinates of the axis-aligned bounding box for the + desired half-space + numpy.ndarray + Upper-right coordinates of the axis-aligned bounding box for the + desired half-space + + """ # Compute the bounding box based on the normal vector to the plane nhat = self._get_normal() - lb = np.array([-np.inf, -np.inf, -np.inf]) - ub = np.array([np.inf, np.inf, np.inf]) + ll = np.array([-np.inf, -np.inf, -np.inf]) + ur = np.array([np.inf, np.inf, np.inf]) # If the plane is axis aligned, find the proper bounding box if np.any(np.isclose(np.abs(nhat), 1., rtol=0., atol=self._atol)): sign = nhat.sum() @@ -380,16 +403,16 @@ class PlaneMixin(metaclass=ABCMeta): vals = [d/val if round(val) != 0 else np.nan for val in (a, b, c)] if side == '-': if sign > 0: - ub = np.array([v if not np.isnan(v) else np.inf for v in vals]) + ur = np.array([v if not np.isnan(v) else np.inf for v in vals]) else: - lb = np.array([v if ~np.isnan(v) else -np.inf for v in vals]) + ll = np.array([v if not np.isnan(v) else -np.inf for v in vals]) elif side == '+': if sign > 0: - lb = np.array([v if ~np.isnan(v) else -np.inf for v in vals]) + ll = np.array([v if not np.isnan(v) else -np.inf for v in vals]) else: - ub = np.array([v if ~np.isnan(v) else np.inf for v in vals]) + ur = np.array([v if not np.isnan(v) else np.inf for v in vals]) - return (lb, ub) + return (ll, ur) def evaluate(self, point): """Evaluate the surface equation at a given point. @@ -1180,18 +1203,12 @@ class Cylinder(QuadricMixin, Surface): """ # This method overrides Surface.to_xml_element to generate a Quadric # since the C++ layer doesn't support Cylinders right now - element = ET.Element("surface") - element.set("id", str(self._id)) - - if len(self._name) > 0: - element.set("name", str(self._name)) - - element.set("type", 'quadric') - if self.boundary_type != 'transmission': - element.set("boundary", self.boundary_type) - element.set("coeffs", ' '.join([str(c) for c in self._get_base_coeffs()])) - - return element + with catch_warnings(): + simplefilter('ignore', IDWarning) + kwargs = {'boundary_type': self.boundary_type, 'name': self.name, + 'surface_id': self.id} + quad_rep = Quadric(*self._get_base_coeffs(), **kwargs) + return quad_rep.to_xml_element() class XCylinder(QuadricMixin, Surface): @@ -1881,19 +1898,13 @@ class Cone(QuadricMixin, Surface): """ # This method overrides Surface.to_xml_element to generate a Quadric - # since the C++ layer doesn't support Cylinders right now - element = ET.Element("surface") - element.set("id", str(self._id)) - - if len(self._name) > 0: - element.set("name", str(self._name)) - - element.set("type", 'quadric') - if self.boundary_type != 'transmission': - element.set("boundary", self.boundary_type) - element.set("coeffs", ' '.join([str(c) for c in self._get_base_coeffs()])) - - return element + # since the C++ layer doesn't support Cones right now + with catch_warnings(): + simplefilter('ignore', IDWarning) + kwargs = {'boundary_type': self.boundary_type, 'name': self.name, + 'surface_id': self.id} + quad_rep = Quadric(*self._get_base_coeffs(), **kwargs) + return quad_rep.to_xml_element() class XCone(QuadricMixin, Surface): diff --git a/setup.py b/setup.py index 99db831484..c193645c35 100755 --- a/setup.py +++ b/setup.py @@ -56,14 +56,13 @@ kwargs = { 'Topic :: Scientific/Engineering' 'Programming Language :: C++', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', ], # Dependencies - 'python_requires': '>=3.4', + 'python_requires': '>=3.5', 'install_requires': [ 'numpy>=1.9', 'h5py', 'scipy', 'ipython', 'matplotlib', 'pandas', 'lxml', 'uncertainties' diff --git a/tests/unit_tests/test_surface.py b/tests/unit_tests/test_surface.py index 703010a059..86e01b6af3 100644 --- a/tests/unit_tests/test_surface.py +++ b/tests/unit_tests/test_surface.py @@ -370,23 +370,26 @@ def test_cylinder_from_points_axis(): # (x - 3)^2 + (y - 4)^2 = 2^2 # x^2 + y^2 - 6x - 8y + 21 = 0 s = openmc.model.cylinder_from_points((3., 4., 0.), (3., 4., 1.), 2.) - assert (s.a, s.b, s.c) == pytest.approx((1., 1., 0.)) - assert (s.d, s.e, s.f) == pytest.approx((0., 0., 0.)) - assert (s.g, s.h, s.j) == pytest.approx((-6., -8., 0.)) - assert s.k == pytest.approx(21.) + a, b, c, d, e, f, g, h, j, k = s._get_base_coeffs() + assert (a, b, c) == pytest.approx((1., 1., 0.)) + assert (d, e, f) == pytest.approx((0., 0., 0.)) + assert (g, h, j) == pytest.approx((-6., -8., 0.)) + assert k == pytest.approx(21.) # (y + 7)^2 + (z - 1)^2 = 3^2 # y^2 + z^2 + 14y - 2z + 41 = 0 s = openmc.model.cylinder_from_points((0., -7, 1.), (1., -7., 1.), 3.) - assert (s.a, s.b, s.c) == pytest.approx((0., 1., 1.)) - assert (s.d, s.e, s.f) == pytest.approx((0., 0., 0.)) - assert (s.g, s.h, s.j) == pytest.approx((0., 14., -2.)) - assert s.k == 41. + a, b, c, d, e, f, g, h, j, k = s._get_base_coeffs() + assert (a, b, c) == pytest.approx((0., 1., 1.)) + assert (d, e, f) == pytest.approx((0., 0., 0.)) + assert (g, h, j) == pytest.approx((0., 14., -2.)) + assert k == 41. # (x - 2)^2 + (z - 5)^2 = 4^2 # x^2 + z^2 - 4x - 10z + 13 = 0 s = openmc.model.cylinder_from_points((2., 0., 5.), (2., 1., 5.), 4.) - assert (s.a, s.b, s.c) == pytest.approx((1., 0., 1.)) - assert (s.d, s.e, s.f) == pytest.approx((0., 0., 0.)) - assert (s.g, s.h, s.j) == pytest.approx((-4., 0., -10.)) - assert s.k == pytest.approx(13.) + a, b, c, d, e, f, g, h, j, k = s._get_base_coeffs() + assert (a, b, c) == pytest.approx((1., 0., 1.)) + assert (d, e, f) == pytest.approx((0., 0., 0.)) + assert (g, h, j) == pytest.approx((-4., 0., -10.)) + assert k == pytest.approx(13.) From 1e5a8e56ff8b2195fdeb09161f945ac128f3913d Mon Sep 17 00:00:00 2001 From: Ethan Peterson Date: Mon, 24 Feb 2020 12:06:12 -0500 Subject: [PATCH 87/90] removed use of round in favor of isclose --- openmc/surface.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openmc/surface.py b/openmc/surface.py index 4d0434a4b0..cc92a810b3 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -400,7 +400,8 @@ class PlaneMixin(metaclass=ABCMeta): if np.any(np.isclose(np.abs(nhat), 1., rtol=0., atol=self._atol)): sign = nhat.sum() a, b, c, d = self._get_base_coeffs() - vals = [d/val if round(val) != 0 else np.nan for val in (a, b, c)] + vals = [d/val if not np.isclose(val, 0., rtol=0., atol=self._atol) + else np.nan for val in (a, b, c)] if side == '-': if sign > 0: ur = np.array([v if not np.isnan(v) else np.inf for v in vals]) From e3af035095bc7783b127d97df956f06edef21f70 Mon Sep 17 00:00:00 2001 From: Ethan Peterson Date: Mon, 24 Feb 2020 15:30:28 -0500 Subject: [PATCH 88/90] finished unit tests and fixed wrong math --- openmc/surface.py | 44 ++++++----- tests/unit_tests/test_surface.py | 121 +++++++++++++++++++++++++++++++ 2 files changed, 146 insertions(+), 19 deletions(-) diff --git a/openmc/surface.py b/openmc/surface.py index cc92a810b3..e4c11692a1 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -887,6 +887,15 @@ class ZPlane(PlaneMixin, Surface): class QuadricMixin(metaclass=ABCMeta): """A Mixin class implementing common functionality for quadric surfaces""" + @property + def _origin(self): + return np.array((self.x0, self.y0, self.z0)) + + @property + def _axis(self): + axis = np.array((self.dx, self.dy, self.dz)) + return axis / np.linalg.norm(axis) + def get_Abc(self, coeffs=None): """Compute matrix, vector, and scalar coefficients for this surface or for a specified set of coefficients. @@ -1135,8 +1144,8 @@ class Cylinder(QuadricMixin, Surface): def _get_base_coeffs(self): # Get x, y, z coordinates of two points - x1, y1, z1 = self.x0, self.y0, self.z0 - x2, y2, z2 = x1 + self.dx, y1 + self.dy, z1 + self.dz + x1, y1, z1 = self._origin + x2, y2, z2 = self._origin + self._axis r = self.r # Define intermediate terms @@ -1868,24 +1877,21 @@ class Cone(QuadricMixin, Surface): # The argument r2 for cones is actually tan^2(theta) so that # cos^2(theta) = 1 / (1 + r2) - x0, y0, z0, r2 = self.x0, self.y0, self.z0, self.r2 - dx, dy, dz = self.dx, self.dy, self.dz - dnorm = dx*dx + dy*dy + dz*dz - dx /= dnorm - dy /= dnorm - dz /= dnorm - cos2 = 1 / (1 + r2) + x0, y0, z0 = self._origin + dx, dy, dz = self._axis + cos2 = 1 / (1 + self.r2) - a = dx*dx - cos2 - b = dy*dy - cos2 - c = dz*dz - cos2 - d = 2*dx*dy - e = 2*dy*dz - f = 2*dx*dz - g = -2*(dx*dx*x0 + dx*dy*y0 + dx*dz*z0 - cos2) - h = -2*(dy*dy*y0 + dx*dy*x0 + dy*dz*z0 - cos2) - j = -2*(dz*dz*z0 + dx*dz*x0 + dy*dz*y0 - cos2) - k = (dx*x0 + dy*y0 + dz*z0)**2 - cos2*(x0*x0 + y0*y0 + z0*z0) + a = cos2 - dx*dx + b = cos2 - dy*dy + c = cos2 - dz*dz + d = -2*dx*dy + e = -2*dy*dz + f = -2*dx*dz + g = 2*(dx*(dy*y0 + dz*z0) - a*x0) + h = 2*(dy*(dx*x0 + dz*z0) - b*y0) + j = 2*(dz*(dx*x0 + dy*y0) - c*z0) + k = a*x0*x0 + b*y0*y0 + c*z0*z0 - 2*(dx*dy*x0*y0 + dy*dz*y0*z0 + + dx*dz*x0*z0) return (a, b, c, d, e, f, g, h, j, k) diff --git a/tests/unit_tests/test_surface.py b/tests/unit_tests/test_surface.py index 86e01b6af3..b6c7398778 100644 --- a/tests/unit_tests/test_surface.py +++ b/tests/unit_tests/test_surface.py @@ -2,6 +2,7 @@ from functools import partial from random import uniform, seed import numpy as np +import math import openmc import pytest @@ -139,6 +140,63 @@ def test_zplane(): repr(s) +def test_cylinder(): + x0, y0, z0, r = 2, 3, 4, 2 + dx, dy, dz = 1, -1, 1 + s = openmc.Cylinder(x0=x0, y0=y0, z0=z0, dx=dx, dy=dy, dz=dz, r=r) + assert s.x0 == 2 + assert s.y0 == 3 + assert s.z0 == 4 + assert s.dx == 1 + assert s.dy == -1 + assert s.dz == 1 + assert s.r == 2 + + # Check bounding box + assert_infinite_bb(s) + + # evaluate method + # |(p - p1) ⨯ (p - p2)|^2 / |p2 - p1|^2 - r^2 + # point inside + p = s._origin + 5*s._axis + p1 = s._origin + p2 = p1 + s._axis + c1 = np.linalg.norm(np.cross(p - p1, p - p2)) / np.linalg.norm(p2 - p1) + val = c1*c1 - s.r*s.r + assert s.evaluate(p) < 0 + assert s.evaluate(p) == pytest.approx(val) + # point outside + p = np.array((4., 0., 2.5)) + p1 = s._origin + p2 = p1 + s._axis + c1 = np.linalg.norm(np.cross(p - p1, p - p2)) / np.linalg.norm(p2 - p1) + val = c1*c1 - s.r*s.r + assert s.evaluate(p) > 0 + assert s.evaluate(p) == pytest.approx(val) + # point on cylinder + perp = np.array((1, -2, 1))*(1 / s._axis) + p = s._origin + s.r*perp / np.linalg.norm(perp) + p1 = s._origin + p2 = p1 + s._axis + c1 = np.linalg.norm(np.cross(p - p1, p - p2)) / np.linalg.norm(p2 - p1) + val = c1*c1 - s.r*s.r + assert 0 == pytest.approx(s.evaluate(p)) + assert val == pytest.approx(s.evaluate(p)) + + # translate method + st = s.translate((1.0, 1.0, 1.0)) + assert st.x0 == s.x0 + 1 + assert st.y0 == s.y0 + 1 + assert st.z0 == s.z0 + 1 + assert st.dx == s.dx + assert st.dy == s.dy + assert st.dz == s.dz + assert st.r == s.r + + # Make sure repr works + repr(s) + + def test_xcylinder(): y, z, r = 3, 5, 2 s = openmc.XCylinder(y0=y, z0=z, r=r) @@ -284,6 +342,69 @@ def cone_common(apex, r2, cls): repr(s) +def test_cone(): + x0, y0, z0, r2 = 2, 3, 4, 4 + dx, dy, dz = 1, -1, 1 + s = openmc.Cone(x0=x0, y0=y0, z0=z0, dx=dx, dy=dy, dz=dz, r2=r2) + assert s.x0 == 2 + assert s.y0 == 3 + assert s.z0 == 4 + assert s.dx == 1 + assert s.dy == -1 + assert s.dz == 1 + assert s.r2 == 4 + + # Check bounding box + assert_infinite_bb(s) + + # evaluate method + # cos^2(theta) * ((p - p1))**2 - (d @ (p - p1))^2 + # The argument r2 for cones is actually tan^2(theta) so that + # cos^2(theta) = 1 / (1 + r2) + # + # This makes the evaluation equation shown below where p is the evaluation + # point (x, y, z) p1 is the apex (origin) of the cone and r2 is related to + # the aperature of the cone as described above + # (p - p1) @ (p - p1) / (1 + r2) - (d @ (p - p1))^2 + # point inside + p1 = s._origin + d = s._axis + p = p1 + 5*d + val = np.sum((p - p1)**2) / (1 + s.r2) - np.sum((d @ (p - p1))**2) + assert s.evaluate(p) < 0 + assert s.evaluate(p) == pytest.approx(val) + # point outside + p1 = s._origin + d = s._axis + perp = np.array((1, -2, 1))*(1 / d) + p = p1 + 3.2*perp + val = np.sum((p - p1)**2) / (1 + s.r2) - np.sum((d @ (p - p1))**2) + assert s.evaluate(p) > 0 + assert s.evaluate(p) == pytest.approx(val) + # point on cone + p1 = s._origin + d = s._axis + perp = np.array((1, -2, 1))*(1 / d) + perp /= np.linalg.norm(perp) + p = p1 + 3.2*d + 3.2*math.sqrt(s.r2)*perp + val = np.sum((p - p1)**2) / (1 + s.r2) - np.sum((d @ (p - p1))**2) + assert 0 == pytest.approx(s.evaluate(p)) + assert val == pytest.approx(s.evaluate(p)) + + # translate method + st = s.translate((1.0, 1.0, 1.0)) + assert st.x0 == s.x0 + 1 + assert st.y0 == s.y0 + 1 + assert st.z0 == s.z0 + 1 + assert st.dx == s.dx + assert st.dy == s.dy + assert st.dz == s.dz + assert st.r2 == s.r2 + + # Make sure repr works + repr(s) + + def test_xcone(): apex = (10, 0, 0) r2 = 4 From e6cf49275ab18b6cef7845d570b0fab408b2f906 Mon Sep 17 00:00:00 2001 From: Ethan Peterson Date: Tue, 25 Feb 2020 08:40:58 -0500 Subject: [PATCH 89/90] applied suggestions from code review --- openmc/surface.py | 2 +- tests/unit_tests/test_surface.py | 67 +++++++++++--------------------- 2 files changed, 23 insertions(+), 46 deletions(-) diff --git a/openmc/surface.py b/openmc/surface.py index e4c11692a1..567968b2c8 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -366,7 +366,7 @@ class PlaneMixin(metaclass=ABCMeta): return (self.a, self.b, self.c, self.d) def _get_normal(self): - a, b, c = self._get_base_coeffs()[0:3] + a, b, c = self._get_base_coeffs()[:3] return np.array((a, b, c)) / math.sqrt(a*a + b*b + c*c) def bounding_box(self, side): diff --git a/tests/unit_tests/test_surface.py b/tests/unit_tests/test_surface.py index b6c7398778..749ae96212 100644 --- a/tests/unit_tests/test_surface.py +++ b/tests/unit_tests/test_surface.py @@ -157,31 +157,19 @@ def test_cylinder(): # evaluate method # |(p - p1) ⨯ (p - p2)|^2 / |p2 - p1|^2 - r^2 - # point inside - p = s._origin + 5*s._axis p1 = s._origin p2 = p1 + s._axis - c1 = np.linalg.norm(np.cross(p - p1, p - p2)) / np.linalg.norm(p2 - p1) - val = c1*c1 - s.r*s.r - assert s.evaluate(p) < 0 - assert s.evaluate(p) == pytest.approx(val) - # point outside - p = np.array((4., 0., 2.5)) - p1 = s._origin - p2 = p1 + s._axis - c1 = np.linalg.norm(np.cross(p - p1, p - p2)) / np.linalg.norm(p2 - p1) - val = c1*c1 - s.r*s.r - assert s.evaluate(p) > 0 - assert s.evaluate(p) == pytest.approx(val) - # point on cylinder perp = np.array((1, -2, 1))*(1 / s._axis) - p = s._origin + s.r*perp / np.linalg.norm(perp) - p1 = s._origin - p2 = p1 + s._axis - c1 = np.linalg.norm(np.cross(p - p1, p - p2)) / np.linalg.norm(p2 - p1) - val = c1*c1 - s.r*s.r - assert 0 == pytest.approx(s.evaluate(p)) - assert val == pytest.approx(s.evaluate(p)) + divisor = np.linalg.norm(p2 - p1) + pin = p1 + 5*s._axis # point inside cylinder + pout = np.array((4., 0., 2.5)) # point outside the cylinder + pon = p1 + s.r*perp / np.linalg.norm(perp) # point on cylinder + for p, fn in zip((pin, pout, pon), (np.less, np.greater, np.isclose)): + c1 = np.linalg.norm(np.cross(p - p1, p - p2)) / divisor + val = c1*c1 - s.r*s.r + p_eval = s.evaluate(p) + assert fn(p_eval, 0.) + assert p_eval == pytest.approx(val) # translate method st = s.translate((1.0, 1.0, 1.0)) @@ -369,27 +357,16 @@ def test_cone(): # point inside p1 = s._origin d = s._axis - p = p1 + 5*d - val = np.sum((p - p1)**2) / (1 + s.r2) - np.sum((d @ (p - p1))**2) - assert s.evaluate(p) < 0 - assert s.evaluate(p) == pytest.approx(val) - # point outside - p1 = s._origin - d = s._axis - perp = np.array((1, -2, 1))*(1 / d) - p = p1 + 3.2*perp - val = np.sum((p - p1)**2) / (1 + s.r2) - np.sum((d @ (p - p1))**2) - assert s.evaluate(p) > 0 - assert s.evaluate(p) == pytest.approx(val) - # point on cone - p1 = s._origin - d = s._axis perp = np.array((1, -2, 1))*(1 / d) perp /= np.linalg.norm(perp) - p = p1 + 3.2*d + 3.2*math.sqrt(s.r2)*perp - val = np.sum((p - p1)**2) / (1 + s.r2) - np.sum((d @ (p - p1))**2) - assert 0 == pytest.approx(s.evaluate(p)) - assert val == pytest.approx(s.evaluate(p)) + pin = p1 + 5*d # point inside cone + pout = p1 + 3.2*perp # point outside cone + pon = p1 + 3.2*d + 3.2*math.sqrt(s.r2)*perp # point on cone + for p, fn in zip((pin, pout, pon), (np.less, np.greater, np.isclose)): + val = np.sum((p - p1)**2) / (1 + s.r2) - np.sum((d @ (p - p1))**2) + p_eval = s.evaluate(p) + assert fn(p_eval, 0.) + assert p_eval == pytest.approx(val) # translate method st = s.translate((1.0, 1.0, 1.0)) @@ -460,7 +437,7 @@ def test_cylinder_from_points(): p1 = np.array([xi(), xi(), xi()]) p2 = np.array([xi(), xi(), xi()]) r = uniform(1.0, 100.0) - s = openmc.model.cylinder_from_points(p1, p2, r) + s = openmc.Cylinder.from_points(p1, p2, r) # Points p1 and p2 need to be inside cylinder assert p1 in -s @@ -490,7 +467,7 @@ def test_cylinder_from_points_axis(): # (x - 3)^2 + (y - 4)^2 = 2^2 # x^2 + y^2 - 6x - 8y + 21 = 0 - s = openmc.model.cylinder_from_points((3., 4., 0.), (3., 4., 1.), 2.) + s = openmc.Cylinder.from_points((3., 4., 0.), (3., 4., 1.), 2.) a, b, c, d, e, f, g, h, j, k = s._get_base_coeffs() assert (a, b, c) == pytest.approx((1., 1., 0.)) assert (d, e, f) == pytest.approx((0., 0., 0.)) @@ -499,7 +476,7 @@ def test_cylinder_from_points_axis(): # (y + 7)^2 + (z - 1)^2 = 3^2 # y^2 + z^2 + 14y - 2z + 41 = 0 - s = openmc.model.cylinder_from_points((0., -7, 1.), (1., -7., 1.), 3.) + s = openmc.Cylinder.from_points((0., -7, 1.), (1., -7., 1.), 3.) a, b, c, d, e, f, g, h, j, k = s._get_base_coeffs() assert (a, b, c) == pytest.approx((0., 1., 1.)) assert (d, e, f) == pytest.approx((0., 0., 0.)) @@ -508,7 +485,7 @@ def test_cylinder_from_points_axis(): # (x - 2)^2 + (z - 5)^2 = 4^2 # x^2 + z^2 - 4x - 10z + 13 = 0 - s = openmc.model.cylinder_from_points((2., 0., 5.), (2., 1., 5.), 4.) + s = openmc.Cylinder.from_points((2., 0., 5.), (2., 1., 5.), 4.) a, b, c, d, e, f, g, h, j, k = s._get_base_coeffs() assert (a, b, c) == pytest.approx((1., 0., 1.)) assert (d, e, f) == pytest.approx((0., 0., 0.)) From 2854b3a3a0e1806db23c04bdc89606f2c4af2b69 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 25 Feb 2020 09:57:40 -0500 Subject: [PATCH 90/90] Disallow numpy ufuncs being applied directly to FissionYields Set ``__array_ufunc__`` to ``None`` to ensure that numpy floats respect __rmul__ and __radd__ for fission yields. Allows weights to be left or right multiplied as numpy floats, which can be computed during fission yield weighting. A unit test has been added to ensure this behavior is preserved Closes #1492 --- openmc/deplete/nuclide.py | 8 ++++++++ tests/unit_tests/test_deplete_nuclide.py | 8 +++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/openmc/deplete/nuclide.py b/openmc/deplete/nuclide.py index 7e352fbf07..0aae5adb72 100644 --- a/openmc/deplete/nuclide.py +++ b/openmc/deplete/nuclide.py @@ -533,6 +533,7 @@ class FissionYield(Mapping): return zip(self.products, self.yields) def __add__(self, other): + """Add one set of fission yields to this set, return new yields""" if not isinstance(other, FissionYield): return NotImplemented new = FissionYield(self.products, self.yields.copy()) @@ -550,12 +551,14 @@ class FissionYield(Mapping): return self + other def __imul__(self, scalar): + """Scale these fission yields by a real scalar""" if not isinstance(scalar, Real): return NotImplemented self.yields *= scalar return self def __mul__(self, scalar): + """Return a new set of yields scaled by a real scalar""" if not isinstance(scalar, Real): return NotImplemented new = FissionYield(self.products, self.yields.copy()) @@ -568,3 +571,8 @@ class FissionYield(Mapping): def __repr__(self): return "<{} containing {} products and yields>".format( self.__class__.__name__, len(self)) + + # Avoid greedy numpy operations like np.float64 * fission_yield + # converting this to an array on the fly. Force __rmul__ and + # __radd__. See issue #1492 + __array_ufunc__ = None diff --git a/tests/unit_tests/test_deplete_nuclide.py b/tests/unit_tests/test_deplete_nuclide.py index 7d68b3a3d8..b8a258df00 100644 --- a/tests/unit_tests/test_deplete_nuclide.py +++ b/tests/unit_tests/test_deplete_nuclide.py @@ -149,12 +149,18 @@ def test_fission_yield_distribution(): # __getitem__ return yields as a view into yield matrix assert orig_yields.yields.base is yield_dist.yield_matrix - # Fission yield feature uses scaled and incremented + # Scale and increment fission yields mod_yields = orig_yields * 2 assert numpy.array_equal(orig_yields.yields * 2, mod_yields.yields) mod_yields += orig_yields assert numpy.array_equal(orig_yields.yields * 3, mod_yields.yields) + mod_yields = 2.0 * orig_yields + assert numpy.array_equal(orig_yields.yields * 2, mod_yields.yields) + + mod_yields = numpy.float64(2.0) * orig_yields + assert numpy.array_equal(orig_yields.yields * 2, mod_yields.yields) + # Failure modes for adding, multiplying yields similar = numpy.empty_like(orig_yields.yields) with pytest.raises(TypeError):