From bc08d8a6b8ddf812433a3ec84a0e0642b4bbf91a Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Thu, 12 Dec 2019 17:24:03 -0500 Subject: [PATCH 001/166] Explicitly name lowlevel HDF5 read/write dataset --- include/openmc/hdf5_interface.h | 99 +++++++++++++++++++-------------- src/hdf5_interface.cpp | 53 +++++++++++------- src/state_point.cpp | 2 +- 3 files changed, 90 insertions(+), 64 deletions(-) diff --git a/include/openmc/hdf5_interface.h b/include/openmc/hdf5_interface.h index ee3419fa2..72eaa3936 100644 --- a/include/openmc/hdf5_interface.h +++ b/include/openmc/hdf5_interface.h @@ -26,13 +26,17 @@ namespace openmc { //============================================================================== void read_attr(hid_t obj_id, const char* name, hid_t mem_type_id, - void* buffer); + void* buffer); + void write_attr(hid_t obj_id, int ndim, const hsize_t* dims, const char* name, - hid_t mem_type_id, const void* buffer); -void read_dataset(hid_t obj_id, const char* name, hid_t mem_type_id, - void* buffer, bool indep); -void write_dataset(hid_t group_id, int ndim, const hsize_t* dims, const char* name, - hid_t mem_type_id, const void* buffer, bool indep); + hid_t mem_type_id, const void* buffer); + +void read_dataset_lowlevel(hid_t obj_id, const char* name, hid_t mem_type_id, + void* buffer, bool indep); + +void write_dataset_lowlevel(hid_t group_id, int ndim, const hsize_t* dims, + const char* name, hid_t mem_type_id, const void* buffer, bool indep); + bool using_mpio_device(hid_t obj_id); //============================================================================== @@ -86,34 +90,32 @@ extern "C" { void read_attr_string(hid_t obj_id, const char* name, size_t slen, char* buffer); void read_complex(hid_t obj_id, const char* name, - std::complex* buffer, bool indep); - void read_double(hid_t obj_id, const char* name, double* buffer, - bool indep); - void read_int(hid_t obj_id, const char* name, int* buffer, - bool indep); + std::complex* buffer, bool indep); + void read_double(hid_t obj_id, const char* name, double* buffer, bool indep); + void read_int(hid_t obj_id, const char* name, int* buffer, bool indep); void read_llong(hid_t obj_id, const char* name, long long* buffer, - bool indep); - void read_string(hid_t obj_id, const char* name, size_t slen, - char* buffer, bool indep); + bool indep); + void read_string(hid_t obj_id, const char* name, size_t slen, char* buffer, + bool indep); - void read_tally_results(hid_t group_id, hsize_t n_filter, - hsize_t n_score, double* results); + void read_tally_results(hid_t group_id, hsize_t n_filter, hsize_t n_score, + double* results); void write_attr_double(hid_t obj_id, int ndim, const hsize_t* dims, - const char* name, const double* buffer); + const char* name, const double* buffer); void write_attr_int(hid_t obj_id, int ndim, const hsize_t* dims, - const char* name, const int* buffer); + const char* name, const int* buffer); void write_attr_string(hid_t obj_id, const char* name, const char* buffer); void write_double(hid_t group_id, int ndim, const hsize_t* dims, - const char* name, const double* buffer, bool indep); + const char* name, const double* buffer, bool indep); void write_int(hid_t group_id, int ndim, const hsize_t* dims, - const char* name, const int* buffer, bool indep); + const char* name, const int* buffer, bool indep); void write_llong(hid_t group_id, int ndim, const hsize_t* dims, - const char* name, const long long* buffer, bool indep); + const char* name, const long long* buffer, bool indep); void write_string(hid_t group_id, int ndim, const hsize_t* dims, size_t slen, - const char* name, char const* buffer, bool indep); + const char* name, char const* buffer, bool indep); void write_tally_results(hid_t group_id, hsize_t n_filter, hsize_t n_score, - const double* results); + const double* results); } // extern "C" //============================================================================== @@ -233,7 +235,7 @@ template inline std::enable_if_t>::value> read_dataset(hid_t obj_id, const char* name, T& buffer, bool indep=false) { - read_dataset(obj_id, name, H5TypeMap::type_id, &buffer, indep); + read_dataset_lowlevel(obj_id, name, H5TypeMap::type_id, &buffer, indep); } // overload for std::string @@ -251,9 +253,11 @@ read_dataset(hid_t obj_id, const char* name, std::string& str, bool indep=false) // array version template inline void -read_dataset(hid_t dset, const char* name, std::array& buffer, bool indep=false) +read_dataset(hid_t dset, const char* name, std::array& buffer, + bool indep=false) { - read_dataset(dset, name, H5TypeMap::type_id, buffer.data(), indep); + read_dataset_lowlevel(dset, name, H5TypeMap::type_id, buffer.data(), + indep); } // vector version @@ -267,11 +271,13 @@ void read_dataset(hid_t dset, std::vector& vec, bool indep=false) vec.resize(shape[0]); // Read data into vector - read_dataset(dset, nullptr, H5TypeMap::type_id, vec.data(), indep); + read_dataset_lowlevel(dset, nullptr, H5TypeMap::type_id, vec.data(), + indep); } template -void read_dataset(hid_t obj_id, const char* name, std::vector& vec, bool indep=false) +void read_dataset(hid_t obj_id, const char* name, std::vector& vec, + bool indep=false) { hid_t dset = open_dataset(obj_id, name); read_dataset(dset, vec, indep); @@ -291,14 +297,17 @@ void read_dataset(hid_t dset, xt::xarray& arr, bool indep=false) arr.resize(shape); // Read data from attribute - read_dataset(dset, nullptr, H5TypeMap::type_id, arr.data(), indep); + read_dataset_lowlevel(dset, nullptr, H5TypeMap::type_id, arr.data(), + indep); } template<> -void read_dataset(hid_t dset, xt::xarray>& arr, bool indep); +void read_dataset(hid_t dset, xt::xarray>& arr, + bool indep); template -void read_dataset(hid_t obj_id, const char* name, xt::xarray& arr, bool indep=false) +void read_dataset(hid_t obj_id, const char* name, xt::xarray& arr, + bool indep=false) { // Open dataset and read array hid_t dset = open_dataset(obj_id, name); @@ -308,7 +317,8 @@ void read_dataset(hid_t obj_id, const char* name, xt::xarray& arr, bool indep template -void read_dataset(hid_t obj_id, const char* name, xt::xtensor& arr, bool indep=false) +void read_dataset(hid_t obj_id, const char* name, xt::xtensor& arr, + bool indep=false) { // Open dataset and read array hid_t dset = open_dataset(obj_id, name); @@ -346,7 +356,7 @@ read_dataset(hid_t obj_id, const char* name, Position& r, bool indep=false) template inline void read_dataset_as_shape(hid_t obj_id, const char* name, - xt::xtensor& arr, bool indep=false) + xt::xtensor& arr, bool indep=false) { hid_t dset = open_dataset(obj_id, name); @@ -357,7 +367,8 @@ inline void read_dataset_as_shape(hid_t obj_id, const char* name, std::vector buffer(size); // Read data from attribute - read_dataset(dset, nullptr, H5TypeMap::type_id, buffer.data(), indep); + read_dataset_lowlevel(dset, nullptr, H5TypeMap::type_id, buffer.data(), + indep); // Adapt into xarray arr = xt::adapt(buffer, arr.shape()); @@ -367,8 +378,8 @@ inline void read_dataset_as_shape(hid_t obj_id, const char* name, template -inline void read_nd_vector(hid_t obj_id, const char* name, xt::xtensor& result, - bool must_have=false) +inline void read_nd_vector(hid_t obj_id, const char* name, + xt::xtensor& result, bool must_have=false) { if (object_exists(obj_id, name)) { read_dataset_as_shape(obj_id, name, result, true); @@ -431,7 +442,8 @@ template inline std::enable_if_t>::value> write_dataset(hid_t obj_id, const char* name, T buffer) { - write_dataset(obj_id, 0, nullptr, name, H5TypeMap::type_id, &buffer, false); + write_dataset_lowlevel(obj_id, 0, nullptr, name, H5TypeMap::type_id, + &buffer, false); } inline void @@ -444,11 +456,13 @@ template inline void write_dataset(hid_t obj_id, const char* name, const std::array& buffer) { hsize_t dims[] {N}; - write_dataset(obj_id, 1, dims, name, H5TypeMap::type_id, buffer.data(), false); + write_dataset_lowlevel(obj_id, 1, dims, name, H5TypeMap::type_id, + buffer.data(), false); } inline void -write_dataset(hid_t obj_id, const char* name, const std::vector& buffer) +write_dataset(hid_t obj_id, const char* name, + const std::vector& buffer) { auto n {buffer.size()}; hsize_t dims[] {n}; @@ -477,7 +491,8 @@ template inline void write_dataset(hid_t obj_id, const char* name, const std::vector& buffer) { hsize_t dims[] {buffer.size()}; - write_dataset(obj_id, 1, dims, name, H5TypeMap::type_id, buffer.data(), false); + write_dataset_lowlevel(obj_id, 1, dims, name, H5TypeMap::type_id, + buffer.data(), false); } // Template for xarray, xtensor, etc. @@ -487,8 +502,8 @@ write_dataset(hid_t obj_id, const char* name, const xt::xcontainer& arr) using T = typename D::value_type; auto s = arr.shape(); std::vector dims {s.cbegin(), s.cend()}; - write_dataset(obj_id, dims.size(), dims.data(), name, H5TypeMap::type_id, - arr.data(), false); + write_dataset_lowlevel(obj_id, dims.size(), dims.data(), name, + H5TypeMap::type_id, arr.data(), false); } inline void diff --git a/src/hdf5_interface.cpp b/src/hdf5_interface.cpp index b1a90cb8f..d4434225a 100644 --- a/src/hdf5_interface.cpp +++ b/src/hdf5_interface.cpp @@ -49,7 +49,8 @@ get_shape(hid_t obj_id, hsize_t* dims) } else if (type == H5I_ATTR) { dspace = H5Aget_space(obj_id); } else { - throw std::runtime_error{"Expected dataset or attribute in call to get_shape."}; + throw std::runtime_error{ + "Expected dataset or attribute in call to get_shape."}; } H5Sget_simple_extent_dims(dspace, dims, nullptr); H5Sclose(dspace); @@ -74,7 +75,8 @@ std::vector object_shape(hid_t obj_id) } else if (type == H5I_ATTR) { dspace = H5Aget_space(obj_id); } else { - throw std::runtime_error{"Expected dataset or attribute in call to object_shape."}; + throw std::runtime_error{ + "Expected dataset or attribute in call to object_shape."}; } int n = H5Sget_simple_extent_ndims(dspace); @@ -471,8 +473,8 @@ read_attr_string(hid_t obj_id, const char* name, size_t slen, char* buffer) void -read_dataset(hid_t obj_id, const char* name, hid_t mem_type_id, - void* buffer, bool indep) +read_dataset_lowlevel(hid_t obj_id, const char* name, hid_t mem_type_id, + void* buffer, bool indep) { hid_t dset = obj_id; if (name) dset = open_dataset(obj_id, name); @@ -520,26 +522,27 @@ void read_dataset(hid_t dset, xt::xarray>& arr, bool indep) void read_double(hid_t obj_id, const char* name, double* buffer, bool indep) { - read_dataset(obj_id, name, H5T_NATIVE_DOUBLE, buffer, indep); + read_dataset_lowlevel(obj_id, name, H5T_NATIVE_DOUBLE, buffer, indep); } void read_int(hid_t obj_id, const char* name, int* buffer, bool indep) { - read_dataset(obj_id, name, H5T_NATIVE_INT, buffer, indep); + read_dataset_lowlevel(obj_id, name, H5T_NATIVE_INT, buffer, indep); } void read_llong(hid_t obj_id, const char* name, long long* buffer, bool indep) { - read_dataset(obj_id, name, H5T_NATIVE_LLONG, buffer, indep); + read_dataset_lowlevel(obj_id, name, H5T_NATIVE_LLONG, buffer, indep); } void -read_string(hid_t obj_id, const char* name, size_t slen, char* buffer, bool indep) +read_string(hid_t obj_id, const char* name, size_t slen, char* buffer, + bool indep) { // Create datatype for a string hid_t datatype = H5Tcopy(H5T_C_S1); @@ -548,7 +551,7 @@ read_string(hid_t obj_id, const char* name, size_t slen, char* buffer, bool inde H5Tset_strpad(datatype, H5T_STR_NULLPAD); // Read data into buffer - read_dataset(obj_id, name, datatype, buffer, indep); + read_dataset_lowlevel(obj_id, name, datatype, buffer, indep); // Free resources H5Tclose(datatype); @@ -556,7 +559,8 @@ read_string(hid_t obj_id, const char* name, size_t slen, char* buffer, bool inde void -read_complex(hid_t obj_id, const char* name, std::complex* buffer, bool indep) +read_complex(hid_t obj_id, const char* name, std::complex* buffer, + bool indep) { // Create compound datatype for complex numbers struct complex_t { @@ -569,7 +573,7 @@ read_complex(hid_t obj_id, const char* name, std::complex* buffer, bool H5Tinsert(complex_id, "i", HOFFSET(complex_t, im), H5T_NATIVE_DOUBLE); // Read data - read_dataset(obj_id, name, complex_id, buffer, indep); + read_dataset_lowlevel(obj_id, name, complex_id, buffer, indep); // Free resources H5Tclose(complex_id); @@ -577,7 +581,8 @@ read_complex(hid_t obj_id, const char* name, std::complex* buffer, bool void -read_tally_results(hid_t group_id, hsize_t n_filter, hsize_t n_score, double* results) +read_tally_results(hid_t group_id, hsize_t n_filter, hsize_t n_score, + double* results) { // Create dataspace for hyperslab in memory hsize_t dims[] {n_filter, n_score, 3}; @@ -654,8 +659,8 @@ write_attr_string(hid_t obj_id, const char* name, const char* buffer) void -write_dataset(hid_t group_id, int ndim, const hsize_t* dims, const char* name, - hid_t mem_type_id, const void* buffer, bool indep) +write_dataset_lowlevel(hid_t group_id, int ndim, const hsize_t* dims, + const char* name, hid_t mem_type_id, const void* buffer, bool indep) { // If array is given, create a simple dataspace. Otherwise, create a scalar // datascape. @@ -696,7 +701,8 @@ void write_double(hid_t group_id, int ndim, const hsize_t* dims, const char* name, const double* buffer, bool indep) { - write_dataset(group_id, ndim, dims, name, H5T_NATIVE_DOUBLE, buffer, indep); + write_dataset_lowlevel(group_id, ndim, dims, name, H5T_NATIVE_DOUBLE, buffer, + indep); } @@ -704,7 +710,8 @@ void write_int(hid_t group_id, int ndim, const hsize_t* dims, const char* name, const int* buffer, bool indep) { - write_dataset(group_id, ndim, dims, name, H5T_NATIVE_INT, buffer, indep); + write_dataset_lowlevel(group_id, ndim, dims, name, H5T_NATIVE_INT, buffer, + indep); } @@ -712,7 +719,8 @@ void write_llong(hid_t group_id, int ndim, const hsize_t* dims, const char* name, const long long* buffer, bool indep) { - write_dataset(group_id, ndim, dims, name, H5T_NATIVE_LLONG, buffer, indep); + write_dataset_lowlevel(group_id, ndim, dims, name, H5T_NATIVE_LLONG, buffer, + indep); } @@ -725,7 +733,7 @@ write_string(hid_t group_id, int ndim, const hsize_t* dims, size_t slen, hid_t datatype = H5Tcopy(H5T_C_S1); H5Tset_size(datatype, slen); - write_dataset(group_id, ndim, dims, name, datatype, buffer, indep); + write_dataset_lowlevel(group_id, ndim, dims, name, datatype, buffer, indep); // Free resources H5Tclose(datatype); @@ -734,14 +742,17 @@ write_string(hid_t group_id, int ndim, const hsize_t* dims, size_t slen, void -write_string(hid_t group_id, const char* name, const std::string& buffer, bool indep) +write_string(hid_t group_id, const char* name, const std::string& buffer, + bool indep) { - write_string(group_id, 0, nullptr, buffer.length(), name, buffer.c_str(), indep); + write_string(group_id, 0, nullptr, buffer.length(), name, buffer.c_str(), + indep); } void -write_tally_results(hid_t group_id, hsize_t n_filter, hsize_t n_score, const double* results) +write_tally_results(hid_t group_id, hsize_t n_filter, hsize_t n_score, + const double* results) { // Set dimensions of sum/sum_sq hyperslab to store hsize_t count[] {n_filter, n_score, 2}; diff --git a/src/state_point.cpp b/src/state_point.cpp index 4c159c167..31454ec4d 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -418,7 +418,7 @@ void load_state_point() if (mpi::master) { #endif // Read global tally data - read_dataset(file_id, "global_tallies", H5T_NATIVE_DOUBLE, + read_dataset_lowlevel(file_id, "global_tallies", H5T_NATIVE_DOUBLE, simulation::global_tallies.data(), false); // Check if tally results are present From 6a2f3ee2efb4b59de49986eac012e7f97a4d9c03 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Fri, 13 Dec 2019 11:51:22 -0500 Subject: [PATCH 002/166] Use collective IO for read/write tally results --- include/openmc/hdf5_interface.h | 32 ++++++++------- src/hdf5_interface.cpp | 72 ++++++++++++++++----------------- src/state_point.cpp | 2 +- 3 files changed, 54 insertions(+), 52 deletions(-) diff --git a/include/openmc/hdf5_interface.h b/include/openmc/hdf5_interface.h index 72eaa3936..ddb2319ae 100644 --- a/include/openmc/hdf5_interface.h +++ b/include/openmc/hdf5_interface.h @@ -32,10 +32,11 @@ void write_attr(hid_t obj_id, int ndim, const hsize_t* dims, const char* name, hid_t mem_type_id, const void* buffer); void read_dataset_lowlevel(hid_t obj_id, const char* name, hid_t mem_type_id, - void* buffer, bool indep); + hid_t mem_space_id, bool indep, void* buffer); void write_dataset_lowlevel(hid_t group_id, int ndim, const hsize_t* dims, - const char* name, hid_t mem_type_id, const void* buffer, bool indep); + const char* name, hid_t mem_type_id, hid_t mem_space_id, bool indep, + const void* buffer); bool using_mpio_device(hid_t obj_id); @@ -235,7 +236,8 @@ template inline std::enable_if_t>::value> read_dataset(hid_t obj_id, const char* name, T& buffer, bool indep=false) { - read_dataset_lowlevel(obj_id, name, H5TypeMap::type_id, &buffer, indep); + read_dataset_lowlevel(obj_id, name, H5TypeMap::type_id, H5S_ALL, indep, + &buffer); } // overload for std::string @@ -256,8 +258,8 @@ template inline void read_dataset(hid_t dset, const char* name, std::array& buffer, bool indep=false) { - read_dataset_lowlevel(dset, name, H5TypeMap::type_id, buffer.data(), - indep); + read_dataset_lowlevel(dset, name, H5TypeMap::type_id, H5S_ALL, indep, + buffer.data()); } // vector version @@ -271,8 +273,8 @@ void read_dataset(hid_t dset, std::vector& vec, bool indep=false) vec.resize(shape[0]); // Read data into vector - read_dataset_lowlevel(dset, nullptr, H5TypeMap::type_id, vec.data(), - indep); + read_dataset_lowlevel(dset, nullptr, H5TypeMap::type_id, H5S_ALL, indep, + vec.data()); } template @@ -297,8 +299,8 @@ void read_dataset(hid_t dset, xt::xarray& arr, bool indep=false) arr.resize(shape); // Read data from attribute - read_dataset_lowlevel(dset, nullptr, H5TypeMap::type_id, arr.data(), - indep); + read_dataset_lowlevel(dset, nullptr, H5TypeMap::type_id, H5S_ALL, indep, + arr.data()); } template<> @@ -367,8 +369,8 @@ inline void read_dataset_as_shape(hid_t obj_id, const char* name, std::vector buffer(size); // Read data from attribute - read_dataset_lowlevel(dset, nullptr, H5TypeMap::type_id, buffer.data(), - indep); + read_dataset_lowlevel(dset, nullptr, H5TypeMap::type_id, H5S_ALL, indep, + buffer.data()); // Adapt into xarray arr = xt::adapt(buffer, arr.shape()); @@ -443,7 +445,7 @@ std::enable_if_t>::value> write_dataset(hid_t obj_id, const char* name, T buffer) { write_dataset_lowlevel(obj_id, 0, nullptr, name, H5TypeMap::type_id, - &buffer, false); + H5S_ALL, false, &buffer); } inline void @@ -457,7 +459,7 @@ write_dataset(hid_t obj_id, const char* name, const std::array& buffer) { hsize_t dims[] {N}; write_dataset_lowlevel(obj_id, 1, dims, name, H5TypeMap::type_id, - buffer.data(), false); + H5S_ALL, false, buffer.data()); } inline void @@ -492,7 +494,7 @@ write_dataset(hid_t obj_id, const char* name, const std::vector& buffer) { hsize_t dims[] {buffer.size()}; write_dataset_lowlevel(obj_id, 1, dims, name, H5TypeMap::type_id, - buffer.data(), false); + H5S_ALL, false, buffer.data()); } // Template for xarray, xtensor, etc. @@ -503,7 +505,7 @@ write_dataset(hid_t obj_id, const char* name, const xt::xcontainer& arr) auto s = arr.shape(); std::vector dims {s.cbegin(), s.cend()}; write_dataset_lowlevel(obj_id, dims.size(), dims.data(), name, - H5TypeMap::type_id, arr.data(), false); + H5TypeMap::type_id, H5S_ALL, false, arr.data()); } inline void diff --git a/src/hdf5_interface.cpp b/src/hdf5_interface.cpp index d4434225a..bd73e05fa 100644 --- a/src/hdf5_interface.cpp +++ b/src/hdf5_interface.cpp @@ -474,7 +474,7 @@ read_attr_string(hid_t obj_id, const char* name, size_t slen, char* buffer) void read_dataset_lowlevel(hid_t obj_id, const char* name, hid_t mem_type_id, - void* buffer, bool indep) + hid_t mem_space_id, bool indep, void* buffer) { hid_t dset = obj_id; if (name) dset = open_dataset(obj_id, name); @@ -489,11 +489,11 @@ read_dataset_lowlevel(hid_t obj_id, const char* name, hid_t mem_type_id, H5Pset_dxpl_mpio(plist, data_xfer_mode); // Read data - H5Dread(dset, mem_type_id, H5S_ALL, H5S_ALL, plist, buffer); + H5Dread(dset, mem_type_id, mem_space_id, H5S_ALL, plist, buffer); H5Pclose(plist); #endif } else { - H5Dread(dset, mem_type_id, H5S_ALL, H5S_ALL, H5P_DEFAULT, buffer); + H5Dread(dset, mem_type_id, mem_space_id, H5S_ALL, H5P_DEFAULT, buffer); } if (name) H5Dclose(dset); @@ -522,21 +522,22 @@ void read_dataset(hid_t dset, xt::xarray>& arr, bool indep) void read_double(hid_t obj_id, const char* name, double* buffer, bool indep) { - read_dataset_lowlevel(obj_id, name, H5T_NATIVE_DOUBLE, buffer, indep); + read_dataset_lowlevel(obj_id, name, H5T_NATIVE_DOUBLE, H5S_ALL, indep, + buffer); } void read_int(hid_t obj_id, const char* name, int* buffer, bool indep) { - read_dataset_lowlevel(obj_id, name, H5T_NATIVE_INT, buffer, indep); + read_dataset_lowlevel(obj_id, name, H5T_NATIVE_INT, H5S_ALL, indep, buffer); } void read_llong(hid_t obj_id, const char* name, long long* buffer, bool indep) { - read_dataset_lowlevel(obj_id, name, H5T_NATIVE_LLONG, buffer, indep); + read_dataset_lowlevel(obj_id, name, H5T_NATIVE_LLONG, H5S_ALL, indep, buffer); } @@ -551,7 +552,7 @@ read_string(hid_t obj_id, const char* name, size_t slen, char* buffer, H5Tset_strpad(datatype, H5T_STR_NULLPAD); // Read data into buffer - read_dataset_lowlevel(obj_id, name, datatype, buffer, indep); + read_dataset_lowlevel(obj_id, name, datatype, H5S_ALL, indep, buffer); // Free resources H5Tclose(datatype); @@ -573,7 +574,7 @@ read_complex(hid_t obj_id, const char* name, std::complex* buffer, H5Tinsert(complex_id, "i", HOFFSET(complex_t, im), H5T_NATIVE_DOUBLE); // Read data - read_dataset_lowlevel(obj_id, name, complex_id, buffer, indep); + read_dataset_lowlevel(obj_id, name, complex_id, H5S_ALL, indep, buffer); // Free resources H5Tclose(complex_id); @@ -585,18 +586,18 @@ read_tally_results(hid_t group_id, hsize_t n_filter, hsize_t n_score, double* results) { // Create dataspace for hyperslab in memory - hsize_t dims[] {n_filter, n_score, 3}; - hsize_t start[] {0, 0, 1}; - hsize_t count[] {n_filter, n_score, 2}; - hid_t memspace = H5Screate_simple(3, dims, nullptr); + constexpr int ndim = 3; + hsize_t dims[ndim] {n_filter, n_score, 3}; + hsize_t start[ndim] {0, 0, 1}; + hsize_t count[ndim] {n_filter, n_score, 2}; + hid_t memspace = H5Screate_simple(ndim, dims, nullptr); H5Sselect_hyperslab(memspace, H5S_SELECT_SET, start, nullptr, count, nullptr); - // Create and write dataset - hid_t dset = H5Dopen(group_id, "results", H5P_DEFAULT); - H5Dread(dset, H5T_NATIVE_DOUBLE, memspace, H5S_ALL, H5P_DEFAULT, results); + // Read the dataset + read_dataset_lowlevel(group_id, "results", H5T_NATIVE_DOUBLE, memspace, + false, results); // Free resources - H5Dclose(dset); H5Sclose(memspace); } @@ -660,7 +661,8 @@ write_attr_string(hid_t obj_id, const char* name, const char* buffer) void write_dataset_lowlevel(hid_t group_id, int ndim, const hsize_t* dims, - const char* name, hid_t mem_type_id, const void* buffer, bool indep) + const char* name, hid_t mem_type_id, hid_t mem_space_id, bool indep, + const void* buffer) { // If array is given, create a simple dataspace. Otherwise, create a scalar // datascape. @@ -684,11 +686,11 @@ write_dataset_lowlevel(hid_t group_id, int ndim, const hsize_t* dims, H5Pset_dxpl_mpio(plist, data_xfer_mode); // Write data - H5Dwrite(dset, mem_type_id, H5S_ALL, H5S_ALL, plist, buffer); + H5Dwrite(dset, mem_type_id, mem_space_id, H5S_ALL, plist, buffer); H5Pclose(plist); #endif } else { - H5Dwrite(dset, mem_type_id, H5S_ALL, H5S_ALL, H5P_DEFAULT, buffer); + H5Dwrite(dset, mem_type_id, mem_space_id, H5S_ALL, H5P_DEFAULT, buffer); } // Free resources @@ -701,8 +703,8 @@ void write_double(hid_t group_id, int ndim, const hsize_t* dims, const char* name, const double* buffer, bool indep) { - write_dataset_lowlevel(group_id, ndim, dims, name, H5T_NATIVE_DOUBLE, buffer, - indep); + write_dataset_lowlevel(group_id, ndim, dims, name, H5T_NATIVE_DOUBLE, H5S_ALL, + indep, buffer); } @@ -710,8 +712,8 @@ void write_int(hid_t group_id, int ndim, const hsize_t* dims, const char* name, const int* buffer, bool indep) { - write_dataset_lowlevel(group_id, ndim, dims, name, H5T_NATIVE_INT, buffer, - indep); + write_dataset_lowlevel(group_id, ndim, dims, name, H5T_NATIVE_INT, H5S_ALL, + indep, buffer); } @@ -719,8 +721,8 @@ void write_llong(hid_t group_id, int ndim, const hsize_t* dims, const char* name, const long long* buffer, bool indep) { - write_dataset_lowlevel(group_id, ndim, dims, name, H5T_NATIVE_LLONG, buffer, - indep); + write_dataset_lowlevel(group_id, ndim, dims, name, H5T_NATIVE_LLONG, H5S_ALL, + indep, buffer); } @@ -733,7 +735,8 @@ write_string(hid_t group_id, int ndim, const hsize_t* dims, size_t slen, hid_t datatype = H5Tcopy(H5T_C_S1); H5Tset_size(datatype, slen); - write_dataset_lowlevel(group_id, ndim, dims, name, datatype, buffer, indep); + write_dataset_lowlevel(group_id, ndim, dims, name, datatype, H5S_ALL, indep, + buffer); // Free resources H5Tclose(datatype); @@ -755,24 +758,21 @@ write_tally_results(hid_t group_id, hsize_t n_filter, hsize_t n_score, const double* results) { // Set dimensions of sum/sum_sq hyperslab to store - hsize_t count[] {n_filter, n_score, 2}; - hid_t dspace = H5Screate_simple(3, count, nullptr); + constexpr int ndim = 3; + hsize_t count[ndim] {n_filter, n_score, 2}; // Set dimensions of results array - hsize_t dims[] {n_filter, n_score, 3}; - hsize_t start[] {0, 0, 1}; - hid_t memspace = H5Screate_simple(3, dims, nullptr); + hsize_t dims[ndim] {n_filter, n_score, 3}; + hsize_t start[ndim] {0, 0, 1}; + hid_t memspace = H5Screate_simple(ndim, dims, nullptr); H5Sselect_hyperslab(memspace, H5S_SELECT_SET, start, nullptr, count, nullptr); // Create and write dataset - hid_t dset = H5Dcreate(group_id, "results", H5T_NATIVE_DOUBLE, dspace, - H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); - H5Dwrite(dset, H5T_NATIVE_DOUBLE, memspace, H5S_ALL, H5P_DEFAULT, results); + write_dataset_lowlevel(group_id, ndim, count, "results", H5T_NATIVE_DOUBLE, + memspace, false, results); // Free resources - H5Dclose(dset); H5Sclose(memspace); - H5Sclose(dspace); } diff --git a/src/state_point.cpp b/src/state_point.cpp index 31454ec4d..fb82bb6d2 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -419,7 +419,7 @@ void load_state_point() #endif // Read global tally data read_dataset_lowlevel(file_id, "global_tallies", H5T_NATIVE_DOUBLE, - simulation::global_tallies.data(), false); + H5S_ALL, false, simulation::global_tallies.data()); // Check if tally results are present bool present; From 5115e8ca85bd6ce2a925100aec6afb36f3b96116 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 27 Jan 2020 21:57:54 -0600 Subject: [PATCH 003/166] Add .clang-format file --- .clang-format | 111 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000..39cea7565 --- /dev/null +++ b/.clang-format @@ -0,0 +1,111 @@ +--- +Language: Cpp +# BasedOnStyle: Mozilla +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Right +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: false +BinPackParameters: false +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: true + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeComma +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeComma +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 90 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 2 +ContinuationIndentWidth: 2 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: false +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + - Regex: '.*' + Priority: 1 +IncludeIsMainRegex: '(Test)?$' +IndentCaseLabels: false +IndentPPDirectives: None +IndentWidth: 2 +IndentWrappedFunctionNames: false +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Left +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: true +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +TabWidth: 8 +UseTab: Never +... From 8b5ed5f12dd0907ae265e489253ef824f48cbae1 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 27 Jan 2020 22:05:20 -0600 Subject: [PATCH 004/166] All note in style guide about using clang-format --- docs/source/devguide/styleguide.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/source/devguide/styleguide.rst b/docs/source/devguide/styleguide.rst index 562b75e89..a46744a3f 100644 --- a/docs/source/devguide/styleguide.rst +++ b/docs/source/devguide/styleguide.rst @@ -12,6 +12,14 @@ adding new code in OpenMC. C++ --- +.. important:: To ensure consistent styling with little effort, this project + uses `clang-format `_. The + repository contains a ``.clang-format`` file that can be used to + automatically apply the style rules that are described below. The easiest + way to use clang-format is through a plugin/extension for your editor/IDE + that automatically runs clang-format using the ``.clang-format`` file + whenever a file is saved. + Indentation ----------- From a7238a59c08c44ec3c4d42442b0341c49428f591 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 3 Feb 2020 07:08:54 -0600 Subject: [PATCH 005/166] Apply @smharper suggestions from code review Co-Authored-By: Sterling Harper --- .clang-format | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.clang-format b/.clang-format index 39cea7565..d7ba659f1 100644 --- a/.clang-format +++ b/.clang-format @@ -2,7 +2,7 @@ Language: Cpp # BasedOnStyle: Mozilla AccessModifierOffset: -2 -AlignAfterOpenBracket: Align +AlignAfterOpenBracket: DontAlign AlignConsecutiveAssignments: false AlignConsecutiveDeclarations: false AlignEscapedNewlines: Right @@ -19,7 +19,7 @@ AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: Yes BinPackArguments: false -BinPackParameters: false +BinPackParameters: true BraceWrapping: AfterClass: false AfterControlStatement: false @@ -39,10 +39,10 @@ BraceWrapping: BreakBeforeBinaryOperators: None BreakBeforeBraces: Custom BreakBeforeInheritanceComma: false -BreakInheritanceList: BeforeComma +BreakInheritanceList: BeforeColon BreakBeforeTernaryOperators: true BreakConstructorInitializersBeforeComma: false -BreakConstructorInitializers: BeforeComma +BreakConstructorInitializers: BeforeColon BreakAfterJavaFieldAnnotations: false BreakStringLiterals: true ColumnLimit: 90 @@ -55,7 +55,7 @@ Cpp11BracedListStyle: true DerivePointerAlignment: false DisableFormat: false ExperimentalAutoDetectBinPacking: false -FixNamespaceComments: false +FixNamespaceComments: true ForEachMacros: - foreach - Q_FOREACH From f3e9a347b42335dad2caca0793abc0dd4314b2b3 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 3 Feb 2020 07:25:58 -0600 Subject: [PATCH 006/166] Address remaining @smharper comments on #1467 --- .clang-format | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.clang-format b/.clang-format index d7ba659f1..153f0e4a3 100644 --- a/.clang-format +++ b/.clang-format @@ -18,7 +18,7 @@ AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: Yes -BinPackArguments: false +BinPackArguments: true BinPackParameters: true BraceWrapping: AfterClass: false @@ -38,14 +38,11 @@ BraceWrapping: SplitEmptyNamespace: true BreakBeforeBinaryOperators: None BreakBeforeBraces: Custom -BreakBeforeInheritanceComma: false BreakInheritanceList: BeforeColon BreakBeforeTernaryOperators: true -BreakConstructorInitializersBeforeComma: false BreakConstructorInitializers: BeforeColon -BreakAfterJavaFieldAnnotations: false BreakStringLiterals: true -ColumnLimit: 90 +ColumnLimit: 80 CommentPragmas: '^ IWYU pragma:' CompactNamespaces: false ConstructorInitializerAllOnOneLineOrOnePerLine: false From 3813b1cdb12532a58f423301f28ef42e31ba89fb Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 3 Feb 2020 13:35:45 -0600 Subject: [PATCH 007/166] Update results for test_mgxs_library_distribcell to match pandas 1.0 --- .../mgxs_library_distribcell/results_true.dat | 182 +++++++++--------- 1 file changed, 91 insertions(+), 91 deletions(-) diff --git a/tests/regression_tests/mgxs_library_distribcell/results_true.dat b/tests/regression_tests/mgxs_library_distribcell/results_true.dat index 9f58621c1..a24cfb7ce 100644 --- a/tests/regression_tests/mgxs_library_distribcell/results_true.dat +++ b/tests/regression_tests/mgxs_library_distribcell/results_true.dat @@ -1,91 +1,91 @@ - sum(distribcell) group in nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 total 0.457353 0.010474 - sum(distribcell) group in nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 total 0.410174 0.011573 - sum(distribcell) group in nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 total 0.410166 0.011577 - sum(distribcell) group in nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 total 0.066556 0.00251 - sum(distribcell) group in nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 total 0.028979 0.002712 - sum(distribcell) group in nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 total 0.037577 0.001487 - sum(distribcell) group in nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 total 0.092377 0.003628 - sum(distribcell) group in nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 total 7.276706e+06 287579.247699 - sum(distribcell) group in nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 total 0.390797 0.008717 - sum(distribcell) group in nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 total 0.387332 0.014241 - sum(distribcell) group in group out legendre nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 P0 total 0.387009 0.014230 -1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 P1 total 0.047179 0.004923 -2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 P2 total 0.015713 0.003654 -3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 P3 total 0.005378 0.003137 - sum(distribcell) group in group out legendre nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 P0 total 0.387332 0.014241 -1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 P1 total 0.047187 0.004933 -2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 P2 total 0.015727 0.003654 -3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 P3 total 0.005387 0.003141 - sum(distribcell) group in group out nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 total 1.000834 0.037242 - sum(distribcell) group in group out nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 total 0.094516 0.0059 - sum(distribcell) group in group out nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 total 1.0 0.037213 - sum(distribcell) group in group out legendre nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 P0 total 0.390797 0.016955 -1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 P1 total 0.047641 0.005091 -2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 P2 total 0.015866 0.003708 -3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 P3 total 0.005430 0.003170 - sum(distribcell) group in group out legendre nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 P0 total 0.391123 0.022356 -1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 P1 total 0.047680 0.005395 -2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 P2 total 0.015880 0.003758 -3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 P3 total 0.005435 0.003179 - sum(distribcell) group out nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 total 1.0 0.080455 - sum(distribcell) group out nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 total 1.0 0.080541 - sum(distribcell) group in nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 total 5.139437e-07 2.133314e-08 - sum(distribcell) group in nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 total 0.091725 0.003604 - sum(distribcell) group in group out nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 total 0.093985 0.005872 - sum(distribcell) delayedgroup group in nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 total 0.000021 8.253906e-07 -1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 2 1 total 0.000112 4.284000e-06 -2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 3 1 total 0.000109 4.105197e-06 -3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 4 1 total 0.000252 9.271419e-06 -4 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 5 1 total 0.000112 3.888624e-06 -5 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 6 1 total 0.000047 1.625563e-06 - sum(distribcell) delayedgroup group out nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 total 0.0 0.000000 -1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 2 1 total 1.0 1.414214 -2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 3 1 total 1.0 1.414214 -3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 4 1 total 0.0 0.000000 -4 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 5 1 total 0.0 0.000000 -5 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 6 1 total 1.0 1.414214 - sum(distribcell) delayedgroup group in nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 total 0.000227 0.000012 -1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 2 1 total 0.001209 0.000061 -2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 3 1 total 0.001177 0.000059 -3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 4 1 total 0.002727 0.000135 -4 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 5 1 total 0.001210 0.000058 -5 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 6 1 total 0.000504 0.000024 - sum(distribcell) delayedgroup group in nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 total 0.013353 0.000686 -1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 2 1 total 0.032613 0.001627 -2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 3 1 total 0.121054 0.005911 -3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 4 1 total 0.305627 0.014428 -4 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 5 1 total 0.860892 0.037879 -5 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 6 1 total 2.891521 0.127879 - sum(distribcell) delayedgroup group in group out nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 1 total 0.000000 0.000000 -1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 2 1 1 total 0.000175 0.000175 -2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 3 1 1 total 0.000178 0.000178 -3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 4 1 1 total 0.000000 0.000000 -4 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 5 1 1 total 0.000000 0.000000 -5 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 6 1 1 total 0.000178 0.000178 + sum(distribcell) group in nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 0.457353 0.010474 + sum(distribcell) group in nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 0.410174 0.011573 + sum(distribcell) group in nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 0.410166 0.011577 + sum(distribcell) group in nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 0.066556 0.00251 + sum(distribcell) group in nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 0.028979 0.002712 + sum(distribcell) group in nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 0.037577 0.001487 + sum(distribcell) group in nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 0.092377 0.003628 + sum(distribcell) group in nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 7.276706e+06 287579.247699 + sum(distribcell) group in nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 0.390797 0.008717 + sum(distribcell) group in nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 0.387332 0.014241 + sum(distribcell) group in group out legendre nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P0 total 0.387009 0.014230 +1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P1 total 0.047179 0.004923 +2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P2 total 0.015713 0.003654 +3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P3 total 0.005378 0.003137 + sum(distribcell) group in group out legendre nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P0 total 0.387332 0.014241 +1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P1 total 0.047187 0.004933 +2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P2 total 0.015727 0.003654 +3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P3 total 0.005387 0.003141 + sum(distribcell) group in group out nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 total 1.000834 0.037242 + sum(distribcell) group in group out nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 total 0.094516 0.0059 + sum(distribcell) group in group out nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 total 1.0 0.037213 + sum(distribcell) group in group out legendre nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P0 total 0.390797 0.016955 +1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P1 total 0.047641 0.005091 +2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P2 total 0.015866 0.003708 +3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P3 total 0.005430 0.003170 + sum(distribcell) group in group out legendre nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P0 total 0.391123 0.022356 +1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P1 total 0.047680 0.005395 +2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P2 total 0.015880 0.003758 +3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P3 total 0.005435 0.003179 + sum(distribcell) group out nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 1.0 0.080455 + sum(distribcell) group out nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 1.0 0.080541 + sum(distribcell) group in nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 5.139437e-07 2.133314e-08 + sum(distribcell) group in nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 0.091725 0.003604 + sum(distribcell) group in group out nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 total 0.093985 0.005872 + sum(distribcell) delayedgroup group in nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 total 0.000021 8.253906e-07 +1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 2 1 total 0.000112 4.284000e-06 +2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 3 1 total 0.000109 4.105197e-06 +3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 4 1 total 0.000252 9.271419e-06 +4 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 5 1 total 0.000112 3.888624e-06 +5 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 6 1 total 0.000047 1.625563e-06 + sum(distribcell) delayedgroup group out nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 total 0.0 0.000000 +1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 2 1 total 1.0 1.414214 +2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 3 1 total 1.0 1.414214 +3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 4 1 total 0.0 0.000000 +4 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 5 1 total 0.0 0.000000 +5 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 6 1 total 1.0 1.414214 + sum(distribcell) delayedgroup group in nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 total 0.000227 0.000012 +1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 2 1 total 0.001209 0.000061 +2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 3 1 total 0.001177 0.000059 +3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 4 1 total 0.002727 0.000135 +4 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 5 1 total 0.001210 0.000058 +5 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 6 1 total 0.000504 0.000024 + sum(distribcell) delayedgroup group in nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 total 0.013353 0.000686 +1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 2 1 total 0.032613 0.001627 +2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 3 1 total 0.121054 0.005911 +3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 4 1 total 0.305627 0.014428 +4 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 5 1 total 0.860892 0.037879 +5 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 6 1 total 2.891521 0.127879 + sum(distribcell) delayedgroup group in group out nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 1 total 0.000000 0.000000 +1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 2 1 1 total 0.000175 0.000175 +2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 3 1 1 total 0.000178 0.000178 +3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 4 1 1 total 0.000000 0.000000 +4 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 5 1 1 total 0.000000 0.000000 +5 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 6 1 1 total 0.000178 0.000178 From efb3beb8ce8db6126ef50eaf2269a8975c3fa409 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 3 Feb 2020 14:43:24 -0600 Subject: [PATCH 008/166] Add expected failure for mgxs_library_distribcell on Python 3.5 --- tests/regression_tests/mgxs_library_distribcell/test.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/regression_tests/mgxs_library_distribcell/test.py b/tests/regression_tests/mgxs_library_distribcell/test.py index 5290d313b..9fe567388 100644 --- a/tests/regression_tests/mgxs_library_distribcell/test.py +++ b/tests/regression_tests/mgxs_library_distribcell/test.py @@ -1,8 +1,10 @@ import hashlib +import sys import openmc import openmc.mgxs from openmc.examples import pwr_assembly +import pytest from tests.testing_harness import PyAPITestHarness @@ -64,6 +66,8 @@ class MGXSTestHarness(PyAPITestHarness): return outstr +@pytest.mark.xfail(sys.version_info < (3, 6), + reason="Pandas 1.0 API changed and requires Python 3.6+") def test_mgxs_library_distribcell(): model = pwr_assembly() harness = MGXSTestHarness('statepoint.10.h5', model) From 79c35d6c914c4476a38191689c7484ac480a357f Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 7 Feb 2020 12:13:12 -0600 Subject: [PATCH 009/166] Remove depletion chain generation scripts --- scripts/casl_chain.py | 280 ----------------------- scripts/openmc-make-depletion-chain | 46 ---- scripts/openmc-make-depletion-chain-casl | 247 -------------------- 3 files changed, 573 deletions(-) delete mode 100755 scripts/casl_chain.py delete mode 100755 scripts/openmc-make-depletion-chain delete mode 100755 scripts/openmc-make-depletion-chain-casl diff --git a/scripts/casl_chain.py b/scripts/casl_chain.py deleted file mode 100755 index 2edc1b6d9..000000000 --- a/scripts/casl_chain.py +++ /dev/null @@ -1,280 +0,0 @@ -# This dictionary contains the 255-nuclides, simplified burnup chain used in -# CASL-ORIGEN, which can be found in Appendix A of Kang Seog Kim, "Specification -# for the VERA Depletion Benchmark Suite", CASL-U-2015-1014-000, Rev. 0, -# ORNL/TM-2016/53, 2016. -# -# Note 32 of the 255 nuclides appear twice as they are both activation -# nuclides (category 1) and fission product nuclides (category 3). - -# Te129 has been added due to its link to I129 production. - -CASL_CHAIN = { - # Nuclide: (Stable, CAT, IFPY, Special yield treatment) - # Stable: True if nuclide has no decay reactions - # CAT: Category of nuclides - # 1-Activation nuclides - # 2-Heavy metal nuclides - # 3-Fission product nuclides - # IFPY: Indicator of fission product yield - # 0-Non FPY - # 1-Direct FPY (-1 indicates (stable+metastable) direct FPY) - # 2-Cumulative FPY - # 3-Special treatment with weight fractions - # Special yield: (nuclide_i/weight_i/IFPY_i) - 'B10': (True, 1, 0, None), - 'B11': (True, 1, 0, None), - 'O16': (True, 1, 0, None), - 'Ag107': (True, 1, 0, None), - 'Ag109': (True, 1, 0, None), # redundant as FP - 'Ag110': (False, 1, 0, None), # redundant as FP - 'Cd110': (True, 1, 0, None), # redundant as FP - 'Cd111': (True, 1, 0, None), # redundant as FP - 'Cd112': (True, 1, 0, None), - 'Cd113': (True, 1, 0, None), # redundant as FP - 'Cd114': (True, 1, 0, None), - 'Cd115': (False, 1, 0, None), - 'In113': (True, 1, 0, None), - 'In115': (True, 1, 0, None), # redundant as FP - 'Sm152': (True, 1, 0, None), # redundant as FP - 'Sm153': (False, 1, 0, None), # redundant as FP - 'Eu151': (True, 1, 0, None), # redundant as FP - 'Eu152': (False, 1, 0, None), - 'Eu152_m1': (False, 1, 0, None), - 'Eu153': (True, 1, 0, None), # redundant as FP - 'Eu154': (False, 1, 0, None), # redundant as FP - 'Eu155': (False, 1, 0, None), # redundant as FP - 'Eu156': (False, 1, 0, None), # redundant as FP - 'Eu157': (False, 1, 0, None), # redundant as FP - 'Gd152': (True, 1, 0, None), - 'Gd154': (True, 1, 0, None), # redundant as FP - 'Gd155': (True, 1, 0, None), # redundant as FP - 'Gd156': (True, 1, 0, None), # redundant as FP - 'Gd157': (True, 1, 0, None), # redundant as FP - 'Gd158': (True, 1, 0, None), # redundant as FP - 'Gd159': (False, 1, 0, None), # redundant as FP - 'Gd160': (True, 1, 0, None), # redundant as FP - 'Gd161': (False, 1, 0, None), # redundant as FP - 'Tb159': (True, 1, 0, None), # redundant as FP - 'Tb160': (False, 1, 0, None), # redundant as FP - 'Tb161': (False, 1, 0, None), # redundant as FP - 'Dy160': (True, 1, 0, None), # redundant as FP - 'Dy161': (True, 1, 0, None), # redundant as FP - 'Dy162': (True, 1, 0, None), # redundant as FP - 'Dy163': (True, 1, 0, None), # redundant as FP - 'Dy164': (True, 1, 0, None), # redundant as FP - 'Dy165': (False, 1, 0, None), # redundant as FP - 'Ho165': (True, 1, 0, None), # redundant as FP - 'Er162': (True, 1, 0, None), - 'Er164': (True, 1, 0, None), - 'Er166': (True, 1, 0, None), - 'Er167': (True, 1, 0, None), - 'Er168': (True, 1, 0, None), - 'Er169': (False, 1, 0, None), - 'Er170': (True, 1, 0, None), - 'Er171': (False, 1, 0, None), - 'Tm169': (True, 1, 0, None), - 'Tm170': (False, 1, 0, None), - 'Tm171': (False, 1, 0, None), - 'Hf174': (True, 1, 0, None), - 'Hf176': (True, 1, 0, None), - 'Hf177': (True, 1, 0, None), - 'Hf178': (True, 1, 0, None), - 'Hf179': (True, 1, 0, None), - 'Hf180': (True, 1, 0, None), - 'Hf181': (False, 1, 0, None), - 'Ta181': (True, 1, 0, None), - 'Ta182': (False, 1, 0, None), - 'Th230': (False, 2, 0, None), - 'Th231': (False, 2, 0, None), - 'Th232': (False, 2, 0, None), - 'Th233': (False, 2, 0, None), - 'Th234': (False, 2, 0, None), - 'Pa231': (False, 2, 0, None), - 'Pa232': (False, 2, 0, None), - 'Pa233': (False, 2, 0, None), - 'Pa234': (False, 2, 0, None), - 'U232': (False, 2, 0, None), - 'U233': (False, 2, 0, None), - 'U234': (False, 2, 0, None), - 'U235': (False, 2, 0, None), - 'U236': (False, 2, 0, None), - 'U237': (False, 2, 0, None), - 'U238': (False, 2, 0, None), - 'U239': (False, 2, 0, None), - 'Np236': (False, 2, 0, None), - 'Np237': (False, 2, 0, None), - 'Np238': (False, 2, 0, None), - 'Np239': (False, 2, 0, None), - 'Np240': (False, 2, 0, None), - 'Np240_m1': (False, 2, 0, None), - 'Pu236': (False, 2, 0, None), - 'Pu237': (False, 2, 0, None), - 'Pu238': (False, 2, 0, None), - 'Pu239': (False, 2, 0, None), - 'Pu240': (False, 2, 0, None), - 'Pu241': (False, 2, 0, None), - 'Pu242': (False, 2, 0, None), - 'Pu243': (False, 2, 0, None), - 'Am241': (False, 2, 0, None), - 'Am242': (False, 2, 0, None), - 'Am242_m1': (False, 2, 0, None), - 'Am243': (False, 2, 0, None), - 'Am244': (False, 2, 0, None), - 'Am244_m1': (False, 2, 0, None), - 'Cm242': (False, 2, 0, None), - 'Cm243': (False, 2, 0, None), - 'Cm244': (False, 2, 0, None), - 'Cm245': (False, 2, 0, None), - 'Cm246': (False, 2, 0, None), - 'Br81': (True, 3, 2, None), - 'Br82': (False, 3, 2, None), - 'Kr82': (True, 3, 3, [('Br82_m1', 0.024, 1), ('Kr82', 1.000, 1)]), - 'Kr83': (True, 3, 2, None), - 'Kr84': (True, 3, 2, None), - 'Kr85': (False, 3, 2, None), - 'Kr86': (True, 3, 2, None), - 'Sr89': (False, 3, 2, None), - 'Sr90': (False, 3, 2, None), - 'Y89': (True, 3, 1, None), - 'Y90': (False, 3, 1, None), - 'Y91': (False, 3, 2, None), - 'Zr91': (True, 3, 1, None), - 'Zr93': (False, 3, 2, None), - 'Zr95': (False, 3, 2, None), - 'Zr96': (True, 3, 2, None), - 'Nb95': (False, 3, 3, [('Nb95',1.000, 1), ('Nb95_m1', 0.944, 1)]), - 'Mo95': (True, 3, 3, [('Nb95_m1',0.056, 1), ('Mo95', 1.000, 1)]), - 'Mo96': (True, 3, 3, [('Nb96',1.000, 1), ('Mo96', 1.000, 1)]), - 'Mo97': (True, 3, 2, None), - 'Mo98': (True, 3, 2, None), - 'Mo99': (False, 3, 2, None), - 'Mo100': (True, 3, 2, None), - 'Tc99': (False, 3, 1, None), - 'Tc99_m1': (False, 3, 1, None), - 'Tc100': (False, 3, 1, None), - 'Ru100': (True, 3, 1, None), - 'Ru101': (True, 3, 2, None), - 'Ru102': (True, 3, 2, None), - 'Ru103': (False, 3, 2, None), - 'Ru104': (True, 3, 2, None), - 'Ru105': (False, 3, 2, None), - 'Ru106': (False, 3, 2, None), - 'Rh102': (False, 3, 1, None), - 'Rh102_m1': (False, 3, 1, None), - 'Rh103': (True, 3, 1, None), - 'Rh103_m1': (False, 3, 1, None), - 'Rh104': (False, 3, 1, None), - 'Rh105': (False, 3, 1, None), - 'Rh105_m1': (False, 3, 1, None), - 'Rh106': (False, 3, 1, None), - 'Rh106_m1': (False, 3, 1, None), - 'Pd104': (True, 3, 1, None), - 'Pd105': (True, 3, 1, None), - 'Pd106': (True, 3, 1, None), - 'Pd107': (False, 3, 2, None), - 'Pd108': (True, 3, 2, None), - 'Pd109': (False, 3, 2, None), - 'Ag109': (True, 3, 1, None), - 'Ag109_m1': (False, 3, 1, None), - 'Ag110': (False, 3, 2, None), - 'Ag110_m1': (False, 3, 2, None), - 'Ag111': (False, 3, 2, None), - 'Cd110': (True, 3, 1, None), - 'Cd111': (True, 3, 3, [('Ag110', -1.000, 2), ('Cd110', 1.000, 2), ('Cd111', 1.000, 1)]), - 'Cd113': (True, 3, 2, None), - 'In115': (True, 3, 2, None), - 'Sb121': (True, 3, 2, None), - 'Sb123': (False, 3, 2, None), - 'Sb125': (False, 3, 2, None), - 'Sb127': (False, 3, 2, None), - 'Te127': (False, 3, -1, None), - 'Te127_m1': (False, 3, -1, None), - 'Te129': (False, 3, 1, None), - 'Te129_m1': (False, 3, 2, None), - 'Te132': (False, 3, 2, None), - 'I127': (True, 3, 1, None), - 'I128': (False, 3, 3, [('I128', 0.931, 2)]), - 'I129': (False, 3, 3, [('I129', 1.000, 2), ('I129', -1.000, 2)]), - 'I130': (False, 3, 2, None), - 'I131': (False, 3, 2, None), - 'I132': (False, 3, 1, None), - 'I135': (False, 3, 2, None), - 'Xe128': (True, 3, 1, None), - 'Xe130': (True, 3, 1, None), - 'Xe131': (True, 3, 1, None), - 'Xe132': (True, 3, 1, None), - 'Xe133': (False, 3, 2, None), - 'Xe134': (True, 3, 2, None), - 'Xe135': (False, 3, 1, None), - 'Xe135_m1': (False, 3, 1, None), - 'Xe136': (True, 3, 2, None), - 'Xe137': (False, 3, 2, None), - 'Cs133': (True, 3, 1, None), - 'Cs134': (False, 3, 1, None), - 'Cs135': (False, 3, 1, None), - 'Cs136': (False, 3, 1, None), - 'Cs137': (False, 3, 1, None), - 'Ba134': (True, 3, 1, None), - 'Ba137': (True, 3, 1, None), - 'Ba140': (False, 3, 2, None), - 'La139': (True, 3, 2, None), - 'La140': (False, 3, 1, None), - 'Ce140': (True, 3, 1, None), - 'Ce141': (False, 3, 2, None), - 'Ce142': (True, 3, 2, None), - 'Ce143': (False, 3, 2, None), - 'Ce144': (False, 3, 2, None), - 'Pr141': (True, 3, 1, None), - 'Pr142': (False, 3, 1, None), - 'Pr143': (False, 3, 1, None), - 'Pr144': (False, 3, 1, None), - 'Nd142': (True, 3, 1, None), - 'Nd143': (True, 3, 1, None), - 'Nd144': (False, 3, 1, None), - 'Nd145': (True, 3, 2, None), - 'Nd146': (True, 3, 2, None), - 'Nd147': (False, 3, 2, None), - 'Nd148': (True, 3, 2, None), - 'Nd149': (False, 3, 2, None), - 'Nd150': (True, 3, 2, None), - 'Nd151': (False, 3, 2, None), - 'Pm147': (False, 3, 1, None), - 'Pm148': (False, 3, -1, None), - 'Pm148_m1': (False, 3, -1, None), - 'Pm149': (False, 3, 1, None), - 'Pm150': (False, 3, 1, None), - 'Pm151': (False, 3, 1, None), - 'Sm147': (False, 3, 1, None), - 'Sm148': (False, 3, 1, None), - 'Sm149': (False, 3, 1, None), - 'Sm150': (True, 3, 1, None), - 'Sm151': (False, 3, 1, None), - 'Sm152': (True, 3, 2, None), - 'Sm153': (False, 3, 2, None), - 'Sm154': (True, 3, 2, None), - 'Sm155': (False, 3, 2, None), - 'Eu151': (True, 3, 1, None), - 'Eu153': (True, 3, 1, None), - 'Eu154': (False, 3, 1, None), - 'Eu155': (False, 3, 1, None), - 'Eu156': (False, 3, 2, None), - 'Eu157': (False, 3, 2, None), - 'Gd154': (True, 3, 1, None), - 'Gd155': (True, 3, 1, None), - 'Gd156': (True, 3, 1, None), - 'Gd157': (True, 3, 1, None), - 'Gd158': (True, 3, 2, None), - 'Gd159': (False, 3, 2, None), - 'Gd160': (True, 3, 2, None), - 'Gd161': (False, 3, 2, None), - 'Tb159': (True, 3, 1, None), - 'Tb160': (False, 3, 1, None), - 'Tb161': (False, 3, 1, None), - 'Dy160': (True, 3, 1, None), - 'Dy161': (True, 3, 1, None), - 'Dy162': (True, 3, 2, None), - 'Dy163': (True, 3, 2, None), - 'Dy164': (True, 3, 2, None), - 'Dy165': (False, 3, 2, None), - 'Ho165': (True, 3, 3, [('Dy165_m1', 0.022, 2), ('Ho165', 1.000, 1)]) -} diff --git a/scripts/openmc-make-depletion-chain b/scripts/openmc-make-depletion-chain deleted file mode 100755 index 01f009a2a..000000000 --- a/scripts/openmc-make-depletion-chain +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env python3 - -import os -from pathlib import Path -from zipfile import ZipFile - -from openmc._utils import download -import openmc.deplete - - -URLS = [ - 'https://www.nndc.bnl.gov/endf/b7.1/zips/ENDF-B-VII.1-neutrons.zip', - 'https://www.nndc.bnl.gov/endf/b7.1/zips/ENDF-B-VII.1-decay.zip', - 'https://www.nndc.bnl.gov/endf/b7.1/zips/ENDF-B-VII.1-nfy.zip' -] - -def main(): - endf_dir = os.environ.get("OPENMC_ENDF_DATA") - if endf_dir is not None: - endf_dir = Path(endf_dir) - elif all(os.path.isdir(lib) for lib in ("neutrons", "decay", "nfy")): - endf_dir = Path(".") - else: - for url in URLS: - basename = download(url) - with ZipFile(basename, 'r') as zf: - print('Extracting {}...'.format(basename)) - zf.extractall() - endf_dir = Path(".") - - decay_files = tuple((endf_dir / "decay").glob("*endf")) - neutron_files = tuple((endf_dir / "neutrons").glob("*endf")) - nfy_files = tuple((endf_dir / "nfy").glob("*endf")) - - # check files exist - for flist, ftype in [(decay_files, "decay"), (neutron_files, "neutron"), - (nfy_files, "neutron fission product yield")]: - if not flist: - raise IOError("No {} endf files found in {}".format(ftype, endf_dir)) - - chain = openmc.deplete.Chain.from_endf(decay_files, nfy_files, neutron_files) - chain.export_to_xml('chain_endfb71.xml') - - -if __name__ == '__main__': - main() diff --git a/scripts/openmc-make-depletion-chain-casl b/scripts/openmc-make-depletion-chain-casl deleted file mode 100755 index 9da3b01c3..000000000 --- a/scripts/openmc-make-depletion-chain-casl +++ /dev/null @@ -1,247 +0,0 @@ -#!/usr/bin/env python3 - -import glob -import os -from zipfile import ZipFile -from collections import OrderedDict, defaultdict -from io import StringIO -from itertools import chain - -try: - import lxml.etree as ET - _have_lxml = True -except ImportError: - import xml.etree.ElementTree as ET - _have_lxml = False - -import openmc.data -import openmc.deplete -from openmc._xml import clean_indentation -from openmc.deplete.chain import _REACTIONS -from openmc.deplete.nuclide import Nuclide, DecayTuple, ReactionTuple, \ - FissionYieldDistribution -from openmc._utils import download - -from casl_chain import CASL_CHAIN - -URLS = [ - 'https://www.nndc.bnl.gov/endf/b7.1/zips/ENDF-B-VII.1-neutrons.zip', - 'https://www.nndc.bnl.gov/endf/b7.1/zips/ENDF-B-VII.1-decay.zip', - 'https://www.nndc.bnl.gov/endf/b7.1/zips/ENDF-B-VII.1-nfy.zip' -] - -def main(): - if os.path.isdir('./decay') and os.path.isdir('./nfy') and os.path.isdir('./neutrons'): - endf_dir = '.' - elif 'OPENMC_ENDF_DATA' in os.environ: - endf_dir = os.environ['OPENMC_ENDF_DATA'] - else: - for url in URLS: - basename = download(url) - with ZipFile(basename, 'r') as zf: - print('Extracting {}...'.format(basename)) - zf.extractall() - endf_dir = '.' - - decay_files = glob.glob(os.path.join(endf_dir, 'decay', '*.endf')) - fpy_files = glob.glob(os.path.join(endf_dir, 'nfy', '*.endf')) - neutron_files = glob.glob(os.path.join(endf_dir, 'neutrons', '*.endf')) - - # Create a Chain - chain = openmc.deplete.Chain() - - print('Reading ENDF nuclear data from "{}"...'.format(os.path.abspath(endf_dir))) - - # Create dictionary mapping target to filename - print('Processing neutron sub-library files...') - reactions = {} - for f in neutron_files: - evaluation = openmc.data.endf.Evaluation(f) - nuc_name = evaluation.gnd_name - if nuc_name in CASL_CHAIN: - reactions[nuc_name] = {} - for mf, mt, nc, mod in evaluation.reaction_list: - # Q value for each reaction is given in MF=3 - if mf == 3: - file_obj = StringIO(evaluation.section[3, mt]) - openmc.data.endf.get_head_record(file_obj) - q_value = openmc.data.endf.get_cont_record(file_obj)[1] - reactions[nuc_name][mt] = q_value - - # Determine what decay and FPY nuclides are available - print('Processing decay sub-library files...') - decay_data = {} - for f in decay_files: - decay_obj = openmc.data.Decay(f) - nuc_name = decay_obj.nuclide['name'] - if nuc_name in CASL_CHAIN: - decay_data[nuc_name] = decay_obj - - for nuc_name in CASL_CHAIN: - if nuc_name not in decay_data: - print('WARNING: {} has no decay data!'.format(nuc_name)) - - print('Processing fission product yield sub-library files...') - fpy_data = {} - for f in fpy_files: - fpy_obj = openmc.data.FissionProductYields(f) - name = fpy_obj.nuclide['name'] - if name in CASL_CHAIN: - fpy_data[name] = fpy_obj - - print('Creating depletion_chain...') - missing_daughter = [] - missing_rx_product = [] - missing_fpy = [] - - for idx, parent in enumerate(sorted(decay_data, key=openmc.data.zam)): - data = decay_data[parent] - - nuclide = Nuclide() - nuclide.name = parent - - chain.nuclides.append(nuclide) - chain.nuclide_dict[parent] = idx - - if not CASL_CHAIN[parent][0] and \ - not data.nuclide['stable'] and data.half_life.nominal_value != 0.0: - nuclide.half_life = data.half_life.nominal_value - nuclide.decay_energy = sum(E.nominal_value for E in - data.average_energies.values()) - sum_br = 0.0 - for i, mode in enumerate(data.modes): - decay_type = ','.join(mode.modes) - if mode.daughter in decay_data: - target = mode.daughter - else: - missing_daughter.append((parent, mode)) - continue - - # Write branching ratio, taking care to ensure sum is unity by - # slightly modifying last value if necessary - br = mode.branching_ratio.nominal_value - sum_br += br - if i == len(data.modes) - 1 and sum_br != 1.0: - br = 1.0 - sum(m.branching_ratio.nominal_value - for m in data.modes[:-1]) - - # Append decay mode - nuclide.decay_modes.append(DecayTuple(decay_type, target, br)) - - # If nuclide has incident neutron data, we need to list what - # transmutation reactions are possible - if parent in reactions: - reactions_available = reactions[parent].keys() - for name, mts, changes in _REACTIONS: - if mts & reactions_available: - delta_A, delta_Z = changes - A = data.nuclide['mass_number'] + delta_A - Z = data.nuclide['atomic_number'] + delta_Z - daughter = '{}{}'.format(openmc.data.ATOMIC_SYMBOL[Z], A) - - if name not in chain.reactions: - chain.reactions.append(name) - - if daughter not in decay_data: - missing_rx_product.append((parent, name, daughter)) - daughter = 'Nothing' - - # Store Q value -- use sorted order so we get summation - # reactions (e.g., MT=103) first - for mt in sorted(mts): - if mt in reactions[parent]: - q_value = reactions[parent][mt] - break - else: - q_value = 0.0 - - nuclide.reactions.append(ReactionTuple( - name, daughter, q_value, 1.0)) - - # Check for fission reactions - if any(mt in reactions_available for mt in [18, 19, 20, 21, 38]): - if parent in fpy_data: - q_value = reactions[parent][18] - nuclide.reactions.append( - ReactionTuple('fission', 0, q_value, 1.0)) - - if 'fission' not in chain.reactions: - chain.reactions.append('fission') - else: - missing_fpy.append(parent) - - if parent in fpy_data: - fpy = fpy_data[parent] - - if fpy.energies is not None: - yield_energies = fpy.energies - else: - yield_energies = [0.0] - - yield_data = {} - for E, table_yd, table_yc in zip(yield_energies, fpy.independent, fpy.cumulative): - yields = defaultdict(float) - for product in table_yd: - if product in decay_data: - # identifier - ifpy = CASL_CHAIN[product][2] - # 1 for independent - if ifpy == 1: - if product not in table_yd: - print('No independent fission yields found for {} in {}'.format(product, parent)) - else: - yields[product] += table_yd[product].nominal_value - # 2 for cumulative - elif ifpy == 2: - if product not in table_yc: - print('No cumulative fission yields found for {} in {}'.format(product, parent)) - else: - yields[product] += table_yc[product].nominal_value - # -1 for independent (stable + metastable) - elif ifpy == -1: - if product not in table_yd: - print('No independent fission yields found for {} in {}'.format(product, parent)) - else: - yields[product] += table_yc[product].nominal_value - product_meta = '{}_m1'.format(product) - if product_meta in table_yd: - yields[product] += table_yc[product_meta].nominal_value - # 3 for special treatment with weight fractions - elif ifpy == 3: - for name_i, weight_i, ifpy_i in CASL_CHAIN[product][3]: - if name_i not in table_yd: - print('No fission yields found for {} in {}'.format(name_i, parent)) - else: - if ifpy_i == 1: - yields[product] += weight_i * table_yd[name_i].nominal_value - elif ifpy_i == 2: - yields[product] += weight_i * table_yc[name_i].nominal_value - - yield_data[E] = yields - - nuclide.yield_data = FissionYieldDistribution(yield_data) - - # Display warnings - if missing_daughter: - print('The following decay modes have daughters with no decay data:') - for parent, mode in missing_daughter: - print(' {} -> {} ({})'.format(parent, mode.daughter, ','.join(mode.modes))) - print('') - - if missing_rx_product: - print('The following reaction products have no decay data:') - for vals in missing_rx_product: - print('{} {} -> {}'.format(*vals)) - print('') - - if missing_fpy: - print('The following fissionable nuclides have no fission product yields:') - for parent in missing_fpy: - print(' ' + parent) - print('') - - chain.export_to_xml('chain_casl.xml') - - -if __name__ == '__main__': - main() From d45583ed1914a40ae699ca5364eba66bc732fd49 Mon Sep 17 00:00:00 2001 From: Gavin Ridley Date: Fri, 7 Feb 2020 15:16:02 -0500 Subject: [PATCH 010/166] add some getters to 1D distributions --- include/openmc/distribution.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/include/openmc/distribution.h b/include/openmc/distribution.h index f9d718cdb..33ff2ffcb 100644 --- a/include/openmc/distribution.h +++ b/include/openmc/distribution.h @@ -62,6 +62,9 @@ public: //! \param seed Pseudorandom number seed pointer //! \return Sampled value double sample(uint64_t* seed) const; + + double a() const { return a_; } + double b() const { return a_; } private: double a_; //!< Lower bound of distribution double b_; //!< Upper bound of distribution @@ -80,6 +83,8 @@ public: //! \param seed Pseudorandom number seed pointer //! \return Sampled value double sample(uint64_t* seed) const; + + double theta() const { return theta_; } private: double theta_; //!< Factor in exponential [eV] }; @@ -97,6 +102,9 @@ public: //! \param seed Pseudorandom number seed pointer //! \return Sampled value double sample(uint64_t* seed) const; + + double a() const { return a_; } + double b() const { return b_; } private: double a_; //!< Factor in exponential [eV] double b_; //!< Factor in square root [1/eV] @@ -115,6 +123,9 @@ public: //! \param seed Pseudorandom number seed pointer //! \return Sampled value double sample(uint64_t* seed) const; + + double mean_value() const { return mean_value_; } + double std_dev() const { return std_dev_; } private: double mean_value_; //!< middle of distribution [eV] double std_dev_; //!< standard deviation [eV] @@ -134,6 +145,10 @@ public: //! \param seed Pseudorandom number seed pointer //! \return Sampled value double sample(uint64_t* seed) const; + + double e0() const { return e0_; } + double m_rat() const { return m_rat_; } + double kt() const { return kt_; } private: // example DT fusion m_rat = 5 (D = 2 + T = 3) // ion temp = 20000 eV @@ -161,6 +176,8 @@ public: // x property std::vector& x() { return x_; } const std::vector& x() const { return x_; } + const std::vector& p() const { return p_; } + Interpolation interp() const { return interp_; } private: std::vector x_; //!< tabulated independent variable std::vector p_; //!< tabulated probability density @@ -188,6 +205,8 @@ public: //! \param seed Pseudorandom number seed pointer //! \return Sampled value double sample(uint64_t* seed) const; + + const std::vector& x() const { return x_; } private: std::vector x_; //! Possible outcomes }; From 9d0649d1dd4b0fffb9a618f16aaf89b9b2e7e500 Mon Sep 17 00:00:00 2001 From: Gavin Ridley Date: Fri, 7 Feb 2020 16:42:10 -0500 Subject: [PATCH 011/166] fix typo --- include/openmc/distribution.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/openmc/distribution.h b/include/openmc/distribution.h index 33ff2ffcb..92c10aed3 100644 --- a/include/openmc/distribution.h +++ b/include/openmc/distribution.h @@ -64,7 +64,7 @@ public: double sample(uint64_t* seed) const; double a() const { return a_; } - double b() const { return a_; } + double b() const { return b_; } private: double a_; //!< Lower bound of distribution double b_; //!< Upper bound of distribution From b534c947a504d854ae4c4e3fe48340bfdae62139 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Sun, 9 Feb 2020 21:57:49 -0600 Subject: [PATCH 012/166] Bug fix for reading output settings in Settings.from_xml --- openmc/settings.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/openmc/settings.py b/openmc/settings.py index 3730d7bd7..eb2f926f3 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -385,11 +385,11 @@ class Settings(object): @property def dagmc(self): return self._dagmc - + @property def event_based(self): return self._event_based - + @property def max_particles_in_flight(self): return self._max_particles_in_flight @@ -721,12 +721,12 @@ class Settings(object): def delayed_photon_scaling(self, value): cv.check_type('delayed photon scaling', value, bool) self._delayed_photon_scaling = value - + @event_based.setter def event_based(self, value): cv.check_type('event based', value, bool) self._event_based = value - + @max_particles_in_flight.setter def max_particles_in_flight(self, value): cv.check_type('max particles in flight', value, Integral) @@ -976,12 +976,12 @@ class Settings(object): if self._delayed_photon_scaling is not None: elem = ET.SubElement(root, "delayed_photon_scaling") elem.text = str(self._delayed_photon_scaling).lower() - + def _create_event_based_subelement(self, root): if self._event_based is not None: elem = ET.SubElement(root, "event_based") elem.text = str(self._event_based).lower() - + def _create_max_particles_in_flight_subelement(self, root): if self._max_particles_in_flight is not None: elem = ET.SubElement(root, "max_particles_in_flight") @@ -1056,7 +1056,7 @@ class Settings(object): if value is not None: if key in ('summary', 'tallies'): value = value in ('true', '1') - self.output[key] = value + self.output[key] = value def _statepoint_from_xml_element(self, root): elem = root.find('state_point') @@ -1227,12 +1227,12 @@ class Settings(object): text = get_text(root, 'delayed_photon_scaling') if text is not None: self.delayed_photon_scaling = text in ('true', '1') - + def _event_based_from_xml_element(self, root): text = get_text(root, 'event_based') if text is not None: self.event_based = text in ('true', '1') - + def _max_particles_in_flight_from_xml_element(self, root): text = get_text(root, 'max_particles_in_flight') if text is not None: From f91d69128842c9d2d3295ac03b4a124a3af6b8e1 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Sun, 12 Jan 2020 23:23:55 -0600 Subject: [PATCH 013/166] Update GSL to latest version --- CMakeLists.txt | 5 +++-- vendor/gsl-lite | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 80909b83c..212e55cda 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -161,7 +161,8 @@ target_link_libraries(xtensor INTERFACE xtl) add_subdirectory(vendor/gsl-lite) # Make sure contract violations throw exceptions -target_compile_definitions(gsl-lite INTERFACE GSL_THROW_ON_CONTRACT_VIOLATION) +target_compile_definitions(gsl-lite-v1 INTERFACE GSL_THROW_ON_CONTRACT_VIOLATION) +target_compile_definitions(gsl-lite-v1 INTERFACE gsl_CONFIG_ALLOWS_NONSTRICT_SPAN_COMPARISON=1) #=============================================================================== # RPATH information @@ -345,7 +346,7 @@ endif() # target_link_libraries treats any arguments starting with - but not -l as # linker flags. Thus, we can pass both linker flags and libraries together. target_link_libraries(libopenmc ${ldflags} ${HDF5_LIBRARIES} ${HDF5_HL_LIBRARIES} - pugixml faddeeva xtensor gsl-lite) + pugixml faddeeva xtensor gsl-lite-v1) if(dagmc) target_compile_definitions(libopenmc PRIVATE DAGMC) diff --git a/vendor/gsl-lite b/vendor/gsl-lite index 93607223a..a5706326e 160000 --- a/vendor/gsl-lite +++ b/vendor/gsl-lite @@ -1 +1 @@ -Subproject commit 93607223a48621dae3cedd6b3335431b38067fae +Subproject commit a5706326ed116c315c0e12b72ed39439aff2222f From 8f242d0b07dd182148400aa76ab645575f13b022 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Sun, 19 Jan 2020 10:28:34 -0600 Subject: [PATCH 014/166] Avoid narrowing warnings on index_ members --- src/material.cpp | 3 ++- src/tallies/filter.cpp | 6 ++++-- src/tallies/tally.cpp | 5 +++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/material.cpp b/src/material.cpp index 570c28bcb..6ee874935 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -47,8 +47,9 @@ std::vector> materials; //============================================================================== Material::Material(pugi::xml_node node) - : index_{model::materials.size()} { + index_ = model::materials.size(); // Avoids warning about narrowing + if (check_for_node(node, "id")) { this->set_id(std::stoi(get_node_value(node, "id"))); } else { diff --git a/src/tallies/filter.cpp b/src/tallies/filter.cpp index 689abde60..afe47fbd0 100644 --- a/src/tallies/filter.cpp +++ b/src/tallies/filter.cpp @@ -57,8 +57,10 @@ extern "C" size_t tally_filters_size() // Filter implementation //============================================================================== -Filter::Filter() : index_{model::tally_filters.size()} -{ } +Filter::Filter() +{ + index_ = model::tally_filters.size(); // Avoids warning about narrowing +} Filter::~Filter() { diff --git a/src/tallies/tally.cpp b/src/tallies/tally.cpp index 49bfd901a..22d0e2e04 100644 --- a/src/tallies/tally.cpp +++ b/src/tallies/tally.cpp @@ -244,15 +244,16 @@ score_str_to_int(std::string score_str) //============================================================================== Tally::Tally(int32_t id) - : index_{model::tallies.size()} { + index_ = model::tallies.size(); // Avoids warning about narrowing this->set_id(id); this->set_filters({}); } Tally::Tally(pugi::xml_node node) - : index_{model::tallies.size()} { + index_ = model::tallies.size(); // Avoids warning about narrowing + // Copy and set tally id if (!check_for_node(node, "id")) { throw std::runtime_error{"Must specify id for tally in tally XML file."}; From da53b433750df43e7015610a54d1021e7ca17405 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 13 Jan 2020 07:02:06 -0600 Subject: [PATCH 015/166] Add {fmt} modern formatting library as dependency --- .gitmodules | 3 +++ CMakeLists.txt | 9 ++++++++- vendor/fmt | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) create mode 160000 vendor/fmt diff --git a/.gitmodules b/.gitmodules index 4e2f34167..ff9120010 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "vendor/xtl"] path = vendor/xtl url = https://github.com/xtensor-stack/xtl.git +[submodule "vendor/fmt"] + path = vendor/fmt + url = https://github.com/fmtlib/fmt.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 212e55cda..ec6b500b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -140,6 +140,13 @@ endif() add_subdirectory(vendor/pugixml) +#=============================================================================== +# {fmt} library +#=============================================================================== + +set(FMT_INSTALL ON CACHE BOOL "Generate the install target.") +add_subdirectory(vendor/fmt) + #=============================================================================== # xtensor header-only library #=============================================================================== @@ -346,7 +353,7 @@ endif() # target_link_libraries treats any arguments starting with - but not -l as # linker flags. Thus, we can pass both linker flags and libraries together. target_link_libraries(libopenmc ${ldflags} ${HDF5_LIBRARIES} ${HDF5_HL_LIBRARIES} - pugixml faddeeva xtensor gsl-lite-v1) + pugixml faddeeva xtensor gsl-lite-v1 fmt::fmt) if(dagmc) target_compile_definitions(libopenmc PRIVATE DAGMC) diff --git a/vendor/fmt b/vendor/fmt new file mode 160000 index 000000000..65ac626c5 --- /dev/null +++ b/vendor/fmt @@ -0,0 +1 @@ +Subproject commit 65ac626c5856f5aad1f1542e79407a6714357043 From 570433b9170f847923a5d512a041558f49a8deb1 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 13 Jan 2020 07:32:16 -0600 Subject: [PATCH 016/166] Start using fmt::format in code --- src/cell.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/cell.cpp b/src/cell.cpp index d56e877d3..9e640ce4e 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -8,7 +8,10 @@ #include #include #include + + #include +#include #include "openmc/capi.h" #include "openmc/constants.h" @@ -83,9 +86,8 @@ tokenize(const std::string region_spec) { i++; } else { - std::stringstream err_msg; - err_msg << "Region specification contains invalid character, \"" - << region_spec[i] << "\""; + auto err_msg = fmt::format( + "Region specification contains invalid character, \"{}\"", region_spec[i]); fatal_error(err_msg); } } From 843c136d36ff8517b8c95fd2d8ca802cea95abc7 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Sun, 19 Jan 2020 16:18:48 -0600 Subject: [PATCH 017/166] Convert most uses of iostreams in output.cpp to fmt::print --- src/output.cpp | 303 ++++++++++++++++++++----------------------------- 1 file changed, 123 insertions(+), 180 deletions(-) diff --git a/src/output.cpp b/src/output.cpp index 4744385c1..f32d4c259 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -11,6 +11,7 @@ #include #include // for pair +#include #ifdef _OPENMP #include #endif @@ -44,53 +45,53 @@ namespace openmc { void title() { - std::cout << - " %%%%%%%%%%%%%%%\n" << - " %%%%%%%%%%%%%%%%%%%%%%%%\n" << - " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n" << - " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n" << - " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n" << - " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n" << - " %%%%%%%%%%%%%%%%%%%%%%%%\n" << - " %%%%%%%%%%%%%%%%%%%%%%%%\n" << - " ############### %%%%%%%%%%%%%%%%%%%%%%%%\n" << - " ################## %%%%%%%%%%%%%%%%%%%%%%%\n" << - " ################### %%%%%%%%%%%%%%%%%%%%%%%\n" << - " #################### %%%%%%%%%%%%%%%%%%%%%%\n" << - " ##################### %%%%%%%%%%%%%%%%%%%%%\n" << - " ###################### %%%%%%%%%%%%%%%%%%%%\n" << - " ####################### %%%%%%%%%%%%%%%%%%\n" << - " ####################### %%%%%%%%%%%%%%%%%\n" << - " ###################### %%%%%%%%%%%%%%%%%\n" << - " #################### %%%%%%%%%%%%%%%%%\n" << - " ################# %%%%%%%%%%%%%%%%%\n" << - " ############### %%%%%%%%%%%%%%%%\n" << - " ############ %%%%%%%%%%%%%%%\n" << - " ######## %%%%%%%%%%%%%%\n" << - " %%%%%%%%%%%\n\n"; + fmt::print( + " %%%%%%%%%%%%%%%\n" + " %%%%%%%%%%%%%%%%%%%%%%%%\n" + " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n" + " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n" + " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n" + " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n" + " %%%%%%%%%%%%%%%%%%%%%%%%\n" + " %%%%%%%%%%%%%%%%%%%%%%%%\n" + " ############### %%%%%%%%%%%%%%%%%%%%%%%%\n" + " ################## %%%%%%%%%%%%%%%%%%%%%%%\n" + " ################### %%%%%%%%%%%%%%%%%%%%%%%\n" + " #################### %%%%%%%%%%%%%%%%%%%%%%\n" + " ##################### %%%%%%%%%%%%%%%%%%%%%\n" + " ###################### %%%%%%%%%%%%%%%%%%%%\n" + " ####################### %%%%%%%%%%%%%%%%%%\n" + " ####################### %%%%%%%%%%%%%%%%%\n" + " ###################### %%%%%%%%%%%%%%%%%\n" + " #################### %%%%%%%%%%%%%%%%%\n" + " ################# %%%%%%%%%%%%%%%%%\n" + " ############### %%%%%%%%%%%%%%%%\n" + " ############ %%%%%%%%%%%%%%%\n" + " ######## %%%%%%%%%%%%%%\n" + " %%%%%%%%%%%\n\n"); // Write version information - std::cout << - " | The OpenMC Monte Carlo Code\n" << - " Copyright | 2011-2020 MIT and OpenMC contributors\n" << - " License | http://openmc.readthedocs.io/en/latest/license.html\n" << - " Version | " << VERSION_MAJOR << '.' << VERSION_MINOR << '.' - << VERSION_RELEASE << (VERSION_DEV ? "-dev" : "") << '\n'; + fmt::print( + " | The OpenMC Monte Carlo Code\n" + " Copyright | 2011-2020 MIT and OpenMC contributors\n" + " License | http://openmc.readthedocs.io/en/latest/license.html\n" + " Version | {}.{}.{}{}\n", VERSION_MAJOR, VERSION_MINOR, + VERSION_RELEASE, VERSION_DEV ? "-dev" : ""); #ifdef GIT_SHA1 - std::cout << " Git SHA1 | " << GIT_SHA1 << '\n'; + fmt::print(" Git SHA1 | {}\n", GIT_SHA1); #endif // Write the date and time - std::cout << " Date/Time | " << time_stamp() << '\n'; + fmt::print(" Date/Time | {}\n", time_stamp()); #ifdef OPENMC_MPI // Write number of processors - std::cout << " MPI Processes | " << mpi::n_procs << '\n'; + fmt::print(" MPI Processes | {}\n", mpi::n_procs); #endif #ifdef _OPENMP // Write number of OpenMP threads - std::cout << " OpenMP Threads | " << omp_get_max_threads() << '\n'; + fmt::print(" OpenMP Threads | {}\n", omp_get_max_threads()); #endif std::cout << std::endl; } @@ -146,64 +147,61 @@ extern "C" void print_particle(Particle* p) // Display particle type and ID. switch (p->type_) { case Particle::Type::neutron: - std::cout << "Neutron "; + fmt::print("Neutron "); break; case Particle::Type::photon: - std::cout << "Photon "; + fmt::print("Photon "); break; case Particle::Type::electron: - std::cout << "Electron "; + fmt::print("Electron "); break; case Particle::Type::positron: - std::cout << "Positron "; + fmt::print("Positron "); break; default: - std::cout << "Unknown Particle "; + fmt::print("Unknown Particle "); } - std::cout << p->id_ << "\n"; + fmt::print("{}\n", p->id_); // Display particle geometry hierarchy. for (auto i = 0; i < p->n_coord_; i++) { - std::cout << " Level " << i << "\n"; + fmt::print(" Level {}\n", i); if (p->coord_[i].cell != C_NONE) { const Cell& c {*model::cells[p->coord_[i].cell]}; - std::cout << " Cell = " << c.id_ << "\n"; + fmt::print(" Cell = {}\n", c.id_); } if (p->coord_[i].universe != C_NONE) { const Universe& u {*model::universes[p->coord_[i].universe]}; - std::cout << " Universe = " << u.id_ << "\n"; + fmt::print(" Universe = {}\n", u.id_); } if (p->coord_[i].lattice != C_NONE) { const Lattice& lat {*model::lattices[p->coord_[i].lattice]}; - std::cout << " Lattice = " << lat.id_ << "\n"; - std::cout << " Lattice position = (" << p->coord_[i].lattice_x - << "," << p->coord_[i].lattice_y << "," - << p->coord_[i].lattice_z << ")\n"; + fmt::print(" Lattice = {}\n", lat.id_); + fmt::print(" Lattice position = ({},{},{})\n", p->coord_[i].lattice_x, + p->coord_[i].lattice_y, p->coord_[i].lattice_z); } - std::cout << " r = (" << p->coord_[i].r.x << ", " - << p->coord_[i].r.y << ", " << p->coord_[i].r.z << ")\n"; - std::cout << " u = (" << p->coord_[i].u.x << ", " - << p->coord_[i].u.y << ", " << p->coord_[i].u.z << ")\n"; + fmt::print(" r = "); + std::cout << p->coord_[i].r << '\n'; + fmt::print(" u = "); + std::cout << p->coord_[i].u << '\n'; } // Display miscellaneous info. if (p->surface_ != 0) { const Surface& surf {*model::surfaces[std::abs(p->surface_)-1]}; - std::cout << " Surface = " << std::copysign(surf.id_, p->surface_) << "\n"; + fmt::print(" Surface = {}\n", std::copysign(surf.id_, p->surface_)); } - std::cout << " Weight = " << p->wgt_ << "\n"; + fmt::print(" Weight = {}\n", p->wgt_); if (settings::run_CE) { - std::cout << " Energy = " << p->E_ << "\n"; + fmt::print(" Energy = {}\n", p->E_); } else { - std::cout << " Energy Group = " << p->g_ << "\n"; + fmt::print(" Energy Group = {}\n", p->g_); } - std::cout << " Delayed Group = " << p->delayed_group_ << "\n"; - - std::cout << "\n"; + fmt::print(" Delayed Group = {}\n\n", p->delayed_group_); } //============================================================================== @@ -215,65 +213,53 @@ void print_plot() for (auto pl : model::plots) { // Plot id - std::cout << "Plot ID: " << pl.id_ << "\n"; + fmt::print("Plot ID: {}\n", pl.id_); // Plot filename - std::cout << "Plot file: " << pl.path_plot_ << "\n"; + fmt::print("Plot file: {}\n", pl.path_plot_); // Plot level - std::cout << "Universe depth: " << pl.level_ << "\n"; + fmt::print("Universe depth: {}\n", pl.level_); // Plot type if (PlotType::slice == pl.type_) { - std::cout << "Plot Type: Slice" << "\n"; + fmt::print("Plot Type: Slice\n"); } else if (PlotType::voxel == pl.type_) { - std::cout << "Plot Type: Voxel" << "\n"; + fmt::print("Plot Type: Voxel\n"); } // Plot parameters - std::cout << "Origin: " << pl.origin_[0] << " " - << pl.origin_[1] << " " - << pl.origin_[2] << "\n"; + fmt::print("Origin: {} {} {}\n", pl.origin_[0], pl.origin_[1], pl.origin_[2]); if (PlotType::slice == pl.type_) { - std::cout << std::setprecision(4) - << "Width: " - << pl.width_[0] << " " - << pl.width_[1] << "\n"; + fmt::print("Width: {:4} {:4}\n", pl.width_[0], pl.width_[1]); } else if (PlotType::voxel == pl.type_) { - std::cout << std::setprecision(4) - << "Width: " - << pl.width_[0] << " " - << pl.width_[1] << " " - << pl.width_[2] << "\n"; + fmt::print("Width: {:4} {:4} {:4}\n", pl.width_[0], pl.width_[1], + pl.width_[2]); } if (PlotColorBy::cells == pl.color_by_) { - std::cout << "Coloring: Cells" << "\n"; + fmt::print("Coloring: Cells\n"); } else if (PlotColorBy::mats == pl.color_by_) { - std::cout << "Coloring: Materials" << "\n"; + fmt::print("Coloring: Materials\n"); } if (PlotType::slice == pl.type_) { switch(pl.basis_) { case PlotBasis::xy: - std::cout << "Basis: XY" << "\n"; + fmt::print("Basis: XY\n"); break; case PlotBasis::xz: - std::cout << "Basis: XZ" << "\n"; + fmt::print("Basis: XZ\n"); break; case PlotBasis::yz: - std::cout << "Basis: YZ" << "\n"; + fmt::print("Basis: YZ\n"); break; } - std::cout << "Pixels: " << pl.pixels_[0] << " " - << pl.pixels_[1] << " " << "\n"; + fmt::print("Pixels: {} {}\n", pl.pixels_[0], pl.pixels_[1]); } else if (PlotType::voxel == pl.type_) { - std::cout << "Voxels: " << pl.pixels_[0] << " " - << pl.pixels_[1] << " " - << pl.pixels_[2] << "\n"; + fmt::print("Voxels: {} {} {}\n", pl.pixels_[0], pl.pixels_[1], pl.pixels_[2]); } - std::cout << "\n"; - + fmt::print("\n"); } } @@ -291,23 +277,22 @@ print_overlap_check() if (mpi::master) { header("cell overlap check summary", 1); - std::cout << " Cell ID No. Overlap Checks\n"; + fmt::print(" Cell ID No. Overlap Checks\n"); std::vector sparse_cell_ids; for (int i = 0; i < model::cells.size(); i++) { - std::cout << " " << std::setw(8) << model::cells[i]->id_ << std::setw(17) - << model::overlap_check_count[i] << "\n"; + fmt::print(" {:8}{:17}\n", model::cells[i]->id_, model::overlap_check_count[i]); if (model::overlap_check_count[i] < 10) { sparse_cell_ids.push_back(model::cells[i]->id_); } } - std::cout << "\n There were " << sparse_cell_ids.size() - << " cells with less than 10 overlap checks\n"; + fmt::print("\n There were {} cells with less than 10 overlap checks\n", + sparse_cell_ids.size()); for (auto id : sparse_cell_ids) { - std::cout << " " << id; + fmt::print(" {}", id); } - std::cout << "\n"; + fmt::print("\n"); } } @@ -316,7 +301,7 @@ print_overlap_check() void print_usage() { if (mpi::master) { - std::cout << + fmt::print( "Usage: openmc [options] [directory]\n\n" "Options:\n" " -c, --volume Run in stochastic volume calculation mode\n" @@ -329,7 +314,7 @@ void print_usage() " -t, --track Write tracks for all particles\n" " -e, --event Run using event-based parallelism\n" " -v, --version Show version information\n" - " -h, --help Show this message\n"; + " -h, --help Show this message\n"); } } @@ -338,14 +323,14 @@ void print_usage() void print_version() { if (mpi::master) { - std::cout << "OpenMC version " << VERSION_MAJOR << '.' << VERSION_MINOR - << '.' << VERSION_RELEASE << '\n'; + fmt::print("OpenMC version {}.{}.{}\n", VERSION_MAJOR, VERSION_MINOR, + VERSION_RELEASE); #ifdef GIT_SHA1 - std::cout << "Git SHA1: " << GIT_SHA1 << '\n'; + fmt::print("Git SHA1: {}\n", GIT_SHA1); #endif - std::cout << "Copyright (c) 2011-2019 Massachusetts Institute of " + fmt::print("Copyright (c) 2011-2019 Massachusetts Institute of " "Technology and OpenMC contributors\nMIT/X license at " - "\n"; + "\n"); } } @@ -354,13 +339,13 @@ void print_version() void print_columns() { if (settings::entropy_on) { - std::cout << + fmt::print( " Bat./Gen. k Entropy Average k \n" - " ========= ======== ======== ====================\n"; + " ========= ======== ======== ====================\n"); } else { - std::cout << + fmt::print( " Bat./Gen. k Average k\n" - " ========= ======== ====================\n"; + " ========= ======== ====================\n"); } } @@ -368,85 +353,63 @@ void print_columns() void print_generation() { - // Save state of cout - auto f {std::cout.flags()}; - // Determine overall generation and number of active generations int i = overall_generation() - 1; int n = simulation::current_batch > settings::n_inactive ? settings::gen_per_batch*simulation::n_realizations + simulation::current_gen : 0; - // Set format for values - std::cout << std::fixed << std::setprecision(5); - // write out information batch and option independent output - std::cout << " " << std::setw(9) << std::to_string(simulation::current_batch) - + "/" + std::to_string(simulation::current_gen) << " " << std::setw(8) - << simulation::k_generation[i]; + auto batch_and_gen = std::to_string(simulation::current_batch) + "/" + + std::to_string(simulation::current_gen); + fmt::print(" {:>9} {:8.5f}", batch_and_gen, simulation::k_generation[i]); // write out entropy info if (settings::entropy_on) { - std::cout << " " << std::setw(8) << simulation::entropy[i]; + fmt::print(" {:8.5f}", simulation::entropy[i]); } if (n > 1) { - std::cout << " " << std::setw(8) << simulation::keff << " +/-" - << std::setw(8) << simulation::keff_std; + fmt::print(" {:8.5f} +/-{:8.5f}", simulation::keff, simulation::keff_std); } - std::cout << '\n'; - - // Restore state of cout - std::cout.flags(f); + std::cout << std::endl; } //============================================================================== void print_batch_keff() { - // Save state of cout - auto f {std::cout.flags()}; - // Determine overall generation and number of active generations int i = simulation::current_batch*settings::gen_per_batch - 1; int n = simulation::n_realizations*settings::gen_per_batch; - // Set format for values - std::cout << std::fixed << std::setprecision(5); - // write out information batch and option independent output - std::cout << " " << std::setw(9) << std::to_string(simulation::current_batch) - + "/" + std::to_string(settings::gen_per_batch) << " " << std::setw(8) - << simulation::k_generation[i]; + auto batch_and_gen = std::to_string(simulation::current_batch) + "/" + + std::to_string(settings::gen_per_batch); + fmt::print(" {:>9} {:8.5f}", batch_and_gen, simulation::k_generation[i]); // write out entropy info if (settings::entropy_on) { - std::cout << " " << std::setw(8) << simulation::entropy[i]; + fmt::print(" {:8.5f}", simulation::entropy[i]); } if (n > 1) { - std::cout << " " << std::setw(8) << simulation::keff << " +/-" - << std::setw(8) << simulation::keff_std; + fmt::print(" {:8.5f} +/-{:8.5f}", simulation::keff, simulation::keff_std); } std::cout << std::endl; - - // Restore state of cout - std::cout.flags(f); } //============================================================================== void show_time(const char* label, double secs, int indent_level=0) { - std::cout << std::string(2*indent_level, ' '); int width = 33 - indent_level*2; - std::cout << " " << std::setw(width) << std::left << label << " = " - << std::setw(10) << std::right << secs << " seconds\n"; + fmt::print("{0:{1}} {2:<{3}} = {4:>10.4e} seconds\n", + "", 2*indent_level, label, width, secs); } void show_rate(const char* label, double particles_per_sec) { - std::cout << " " << std::setw(33) << std::left << label << " = " << - particles_per_sec << " particles/second\n"; + fmt::print(" {:<33} = {:.6} particles/second\n", label, particles_per_sec); } void print_runtime() @@ -457,11 +420,7 @@ void print_runtime() header("Timing Statistics", 6); if (settings::verbosity < 6) return; - // Save state of cout - auto f {std::cout.flags()}; - // display time elapsed for various sections - std::cout << std::scientific << std::setprecision(4); show_time("Total time for initialization", time_initialize.elapsed()); show_time("Reading cross sections", time_read_xs.elapsed(), 1); show_time("Total time in simulation", time_inactive.elapsed() + @@ -490,9 +449,6 @@ void print_runtime() show_time("Total time for finalization", time_finalize.elapsed()); show_time("Total time elapsed", time_total.elapsed()); - // Restore state of cout - std::cout.flags(f); - // Calculate particle rate in active/inactive batches int n_active = simulation::current_batch - settings::n_inactive; double speed_inactive = 0.0; @@ -519,15 +475,11 @@ void print_runtime() } // display calculation rate - std::cout << std::setprecision(6) << std::showpoint; if (!(settings::restart_run && (simulation::restart_batch >= settings::n_inactive)) && settings::n_inactive > 0) { show_rate("Calculation Rate (inactive)", speed_inactive); } show_rate("Calculation Rate (active)", speed_active); - - // Restore state of cout - std::cout.flags(f); } //============================================================================== @@ -545,9 +497,6 @@ mean_stdev(const double* x, int n) void print_results() { - // Save state of cout - auto f {std::cout.flags()}; - // display header block for results header("Results", 4); if (settings::verbosity < 4) return; @@ -564,52 +513,46 @@ void print_results() t_n3 = 1.0; } - // Set formatting for floats - std::cout << std::fixed << std::setprecision(5); - // write global tallies const auto& gt = simulation::global_tallies; double mean, stdev; if (n > 1) { if (settings::run_mode == RunMode::EIGENVALUE) { std::tie(mean, stdev) = mean_stdev(>(GlobalTally::K_COLLISION, 0), n); - std::cout << " k-effective (Collision) = " - << mean << " +/- " << t_n1 * stdev << '\n'; + fmt::print(" k-effective (Collision) = {:.5f} +/- {:.5f}\n", + mean, t_n1 * stdev); std::tie(mean, stdev) = mean_stdev(>(GlobalTally::K_TRACKLENGTH, 0), n); - std::cout << " k-effective (Track-length) = " - << mean << " +/- " << t_n1 * stdev << '\n'; + fmt::print(" k-effective (Track-length) = {:.5f} +/- {:.5f}\n", + mean, t_n1 * stdev); std::tie(mean, stdev) = mean_stdev(>(GlobalTally::K_ABSORPTION, 0), n); - std::cout << " k-effective (Absorption) = " - << mean << " +/- " << t_n1 * stdev << '\n'; + fmt::print(" k-effective (Absorption) = {:.5f} +/- {:.5f}\n", + mean, t_n1 * stdev); if (n > 3) { double k_combined[2]; openmc_get_keff(k_combined); - std::cout << " Combined k-effective = " - << k_combined[0] << " +/- " << t_n3 * k_combined[1] << '\n'; + fmt::print(" Combined k-effective = {:.5f} +/- {:.5f}\n", + k_combined[0], k_combined[1]); } } std::tie(mean, stdev) = mean_stdev(>(GlobalTally::LEAKAGE, 0), n); - std::cout << " Leakage Fraction = " - << mean << " +/- " << t_n1 * stdev << '\n'; + fmt::print(" Leakage Fraction = {:.5f} +/- {:.5f}\n", + mean, t_n1 * stdev); } else { if (mpi::master) warning("Could not compute uncertainties -- only one " "active batch simulated!"); if (settings::run_mode == RunMode::EIGENVALUE) { - std::cout << " k-effective (Collision) = " - << gt(GlobalTally::K_COLLISION, TallyResult::SUM) / n << '\n'; - std::cout << " k-effective (Track-length) = " - << gt(GlobalTally::K_TRACKLENGTH, TallyResult::SUM) / n << '\n'; - std::cout << " k-effective (Absorption) = " - << gt(GlobalTally::K_ABSORPTION, TallyResult::SUM) / n << '\n'; + fmt::print(" k-effective (Collision) = {:.5f}\n", + gt(GlobalTally::K_COLLISION, TallyResult::SUM) / n); + fmt::print(" k-effective (Track-length) = {:.5f}\n", + gt(GlobalTally::K_TRACKLENGTH, TallyResult::SUM) / n); + fmt::print(" k-effective (Absorption) = {:.5f}\n", + gt(GlobalTally::K_ABSORPTION, TallyResult::SUM) / n); } - std::cout << " Leakage Fraction = " - << gt(GlobalTally::LEAKAGE, TallyResult::SUM) / n << '\n'; + fmt::print(" Leakage Fraction = {:.5f}\n", + gt(GlobalTally::LEAKAGE, TallyResult::SUM) / n); } - std::cout << '\n'; - - // Restore state of cout - std::cout.flags(f); + fmt::print("\n"); } //============================================================================== From 7383a52f1fcfccf6cf8260698f79f5a0bdfa7da9 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 20 Jan 2020 10:06:26 -0600 Subject: [PATCH 018/166] Use fmt::print and fmt::format in many places --- src/cell.cpp | 82 +++++--------- src/dagmc.cpp | 35 ++---- src/distribution_spatial.cpp | 8 +- src/geometry.cpp | 53 ++++----- src/geometry_aux.cpp | 44 ++++---- src/hdf5_interface.cpp | 31 ++---- src/initialize.cpp | 9 +- src/lattice.cpp | 40 +++---- src/mgxs.cpp | 15 ++- src/output.cpp | 48 ++++---- src/particle.cpp | 10 +- src/physics.cpp | 25 +++-- src/physics_mg.cpp | 16 ++- src/plot.cpp | 197 ++++++++++----------------------- src/reaction.cpp | 23 ++-- src/secondary_uncorrelated.cpp | 7 +- 16 files changed, 240 insertions(+), 403 deletions(-) diff --git a/src/cell.cpp b/src/cell.cpp index 9e640ce4e..d09cde948 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -9,7 +9,7 @@ #include #include - +#include #include #include @@ -158,10 +158,8 @@ generate_rpn(int32_t cell_id, std::vector infix) // If we run out of operators without finding a left parenthesis, it // means there are mismatched parentheses. if (it == stack.rend()) { - std::stringstream err_msg; - err_msg << "Mismatched parentheses in region specification for cell " - << cell_id; - fatal_error(err_msg); + fatal_error(fmt::format( + "Mismatched parentheses in region specification for cell {}", cell_id)); } rpn.push_back(stack.back()); stack.pop_back(); @@ -177,10 +175,8 @@ generate_rpn(int32_t cell_id, std::vector infix) // If the operator is a parenthesis it is mismatched. if (op >= OP_RIGHT_PAREN) { - std::stringstream err_msg; - err_msg << "Mismatched parentheses in region specification for cell " - << cell_id; - fatal_error(err_msg); + fatal_error(fmt::format( + "Mismatched parentheses in region specification for cell {}", cell_id)); } rpn.push_back(stack.back()); @@ -198,9 +194,7 @@ void Universe::to_hdf5(hid_t universes_group) const { // Create a group for this universe. - std::stringstream group_name; - group_name << "universe " << id_; - auto group = create_group(universes_group, group_name); + auto group = create_group(universes_group, fmt::format("universe {}", id_)); // Write the contained cells. if (cells_.size() > 0) { @@ -301,22 +295,19 @@ CSGCell::CSGCell(pugi::xml_node cell_node) bool fill_present = check_for_node(cell_node, "fill"); bool material_present = check_for_node(cell_node, "material"); if (!(fill_present || material_present)) { - std::stringstream err_msg; - err_msg << "Neither material nor fill was specified for cell " << id_; - fatal_error(err_msg); + fatal_error(fmt::format( + "Neither material nor fill was specified for cell {}", id_)); } if (fill_present && material_present) { - std::stringstream err_msg; - err_msg << "Cell " << id_ << " has both a material and a fill specified; " - << "only one can be specified per cell"; - fatal_error(err_msg); + fatal_error(fmt::format("Cell {} has both a material and a fill specified; " + "only one can be specified per cell", id_)); } if (fill_present) { fill_ = std::stoi(get_node_value(cell_node, "fill")); if (fill_ == universe_) { - fatal_error("Cell " + std::to_string(id_) + - " is filled with the same universe that it is contained in."); + fatal_error(fmt::format("Cell {} is filled with the same universe that" + "it is contained in.", id_)); } } else { fill_ = C_NONE; @@ -338,9 +329,8 @@ CSGCell::CSGCell(pugi::xml_node cell_node) } } } else { - std::stringstream err_msg; - err_msg << "An empty material element was specified for cell " << id_; - fatal_error(err_msg); + fatal_error(fmt::format("An empty material element was specified for cell {}", + id_)); } } @@ -351,20 +341,16 @@ CSGCell::CSGCell(pugi::xml_node cell_node) // Make sure this is a material-filled cell. if (material_.size() == 0) { - std::stringstream err_msg; - err_msg << "Cell " << id_ << " was specified with a temperature but " - "no material. Temperature specification is only valid for cells " - "filled with a material."; - fatal_error(err_msg); + fatal_error(fmt::format( + "Cell {} was specified with a temperature but no material. Temperature" + "specification is only valid for cells filled with a material.", id_)); } // Make sure all temperatures are non-negative. for (auto T : sqrtkT_) { if (T < 0) { - std::stringstream err_msg; - err_msg << "Cell " << id_ - << " was specified with a negative temperature"; - fatal_error(err_msg); + fatal_error(fmt::format( + "Cell {} was specified with a negative temperature", id_)); } } @@ -426,17 +412,14 @@ CSGCell::CSGCell(pugi::xml_node cell_node) // Read the translation vector. if (check_for_node(cell_node, "translation")) { if (fill_ == C_NONE) { - std::stringstream err_msg; - err_msg << "Cannot apply a translation to cell " << id_ - << " because it is not filled with another universe"; - fatal_error(err_msg); + fatal_error(fmt::format("Cannot apply a translation to cell {}" + " because it is not filled with another universe", id_)); } auto xyz {get_node_array(cell_node, "translation")}; if (xyz.size() != 3) { - std::stringstream err_msg; - err_msg << "Non-3D translation vector applied to cell " << id_; - fatal_error(err_msg); + fatal_error(fmt::format( + "Non-3D translation vector applied to cell {}", id_)); } translation_ = xyz; } @@ -444,17 +427,14 @@ CSGCell::CSGCell(pugi::xml_node cell_node) // Read the rotation transform. if (check_for_node(cell_node, "rotation")) { if (fill_ == C_NONE) { - std::stringstream err_msg; - err_msg << "Cannot apply a rotation to cell " << id_ - << " because it is not filled with another universe"; - fatal_error(err_msg); + fatal_error(fmt::format("Cannot apply a rotation to cell {}" + " because it is not filled with another universe", id_)); } auto rot {get_node_array(cell_node, "rotation")}; if (rot.size() != 3 && rot.size() != 9) { - std::stringstream err_msg; - err_msg << "Non-3D rotation vector applied to cell " << id_; - fatal_error(err_msg); + fatal_error(fmt::format( + "Non-3D rotation vector applied to cell {}", id_)); } // Compute and store the rotation matrix. @@ -534,9 +514,7 @@ void CSGCell::to_hdf5(hid_t cell_group) const { // Create a group for this cell. - std::stringstream group_name; - group_name << "cell " << id_; - auto group = create_group(cell_group, group_name); + auto group = create_group(cell_group, fmt::format("cell {}", id_)); if (!name_.empty()) { write_string(group, "name", name_, false); @@ -1013,9 +991,7 @@ void read_cells(pugi::xml_node node) if (search == model::cell_map.end()) { model::cell_map[id] = i; } else { - std::stringstream err_msg; - err_msg << "Two or more cells use the same unique ID: " << id; - fatal_error(err_msg); + fatal_error(fmt::format("Two or more cells use the same unique ID: {}", id)); } } diff --git a/src/dagmc.cpp b/src/dagmc.cpp index e14a6062a..3f16799a5 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -12,11 +12,10 @@ #include "openmc/surface.h" #ifdef DAGMC - #include "uwuw.hpp" #include "dagmcmetadata.hpp" - #endif +#include #include #include @@ -110,11 +109,10 @@ void legacy_assign_material(const std::string& mat_string, DAGCell* c) c->material_.push_back(m->id_); // report error if more than one material is found } else { - std::stringstream err_msg; - err_msg << "More than one material found with name " << mat_string - << ". Please ensure materials have unique names if using this" - << " property to assign materials."; - fatal_error(err_msg); + fatal_error(fmt::format( + "More than one material found with name {}. Please ensure materials " + "have unique names if using this property to assign materials.", + mat_string)); } } } @@ -125,10 +123,8 @@ void legacy_assign_material(const std::string& mat_string, DAGCell* c) auto id = std::stoi(mat_string); c->material_.emplace_back(id); } catch (const std::invalid_argument&) { - std::stringstream err_msg; - err_msg << "No material " << mat_string - << " found for volume (cell) " << c->id_; - fatal_error(err_msg); + fatal_error(fmt::format( + "No material {} found for volume (cell) {}", mat_string, c->id_)); } } @@ -153,7 +149,6 @@ void load_dagmc_geometry() model::DAG = new moab::DagMC(); } - std::string filename = settings::path_input + DAGMC_FILENAME; // --- Materials --- @@ -253,9 +248,7 @@ void load_dagmc_geometry() rval = model::DAG->prop_value(vol_handle, "mat", mat_value); MB_CHK_ERR_CONT(rval); } else { - std::stringstream err_msg; - err_msg << "Volume " << c->id_ << " has no material assignment."; - fatal_error(err_msg.str()); + fatal_error(fmt::format("Volume {} has no material assignment.", c->id_)); } std::string cmp_str = mat_value; @@ -277,10 +270,8 @@ void load_dagmc_geometry() int mat_number = uwuw.material_library[uwuw_mat].metadata["mat_number"].asInt(); c->material_.push_back(mat_number); } else { - std::stringstream err_msg; - err_msg << "Material with value " << mat_value << " not found "; - err_msg << "in the UWUW material library"; - fatal_error(err_msg); + fatal_error(fmt::format("Material with value {} not found in the " + "UWUW material library", mat_value)); } } else { legacy_assign_material(mat_value, c); @@ -348,10 +339,8 @@ void load_dagmc_geometry() } else if (bc_value == "periodic") { fatal_error("Periodic boundary condition not supported in DAGMC."); } else { - std::stringstream err_msg; - err_msg << "Unknown boundary condition \"" << bc_value - << "\" specified on surface " << s->id_; - fatal_error(err_msg); + fatal_error(fmt::format("Unknown boundary condition \"{}\" specified " + "on surface {}", bc_value, s->id_)); } } else { // if no condition is found, set to transmit diff --git a/src/distribution_spatial.cpp b/src/distribution_spatial.cpp index 12432baf7..125d8afa6 100644 --- a/src/distribution_spatial.cpp +++ b/src/distribution_spatial.cpp @@ -96,9 +96,7 @@ CylindricalIndependent::CylindricalIndependent(pugi::xml_node node) if (origin.size() == 3) { origin_ = origin; } else { - std::stringstream err_msg; - err_msg << "Origin for cylindrical source distribution must be length 3"; - fatal_error(err_msg); + fatal_error("Origin for cylindrical source distribution must be length 3"); } } else { // If no coordinates were specified, default to (0, 0, 0) @@ -162,9 +160,7 @@ SphericalIndependent::SphericalIndependent(pugi::xml_node node) if (origin.size() == 3) { origin_ = origin; } else { - std::stringstream err_msg; - err_msg << "Origin for spherical source distribution must be length 3"; - fatal_error(err_msg); + fatal_error("Origin for spherical source distribution must be length 3"); } } else { // If no coordinates were specified, default to (0, 0, 0) diff --git a/src/geometry.cpp b/src/geometry.cpp index 8bbc4d90c..d2012d901 100644 --- a/src/geometry.cpp +++ b/src/geometry.cpp @@ -1,7 +1,9 @@ #include "openmc/geometry.h" #include -#include + +#include +#include #include "openmc/cell.h" #include "openmc/constants.h" @@ -47,11 +49,9 @@ bool check_cell_overlap(Particle* p, bool error) if (c.contains(p->coord_[j].r, p->coord_[j].u, p->surface_)) { if (index_cell != p->coord_[j].cell) { if (error) { - std::stringstream err_msg; - err_msg << "Overlapping cells detected: " << c.id_ << ", " - << model::cells[p->coord_[j].cell]->id_ << " on universe " - << univ.id_; - fatal_error(err_msg); + fatal_error(fmt::format( + "Overlapping cells detected: {}, {} on universe {}", + c.id_, model::cells[p->coord_[j].cell]->id_, univ.id_)); } return true; } @@ -120,8 +120,7 @@ find_cell_inner(Particle* p, const NeighborList* neighbor_list) // Announce the cell that the particle is entering. if (found && (settings::verbosity >= 10 || p->trace_)) { - std::stringstream msg; - msg << " Entering cell " << model::cells[i_cell]->id_; + auto msg = fmt::format(" Entering cell {}", model::cells[i_cell]->id_); write_message(msg, 1); } @@ -229,11 +228,8 @@ find_cell_inner(Particle* p, const NeighborList* neighbor_list) if (lat.outer_ != NO_OUTER_UNIVERSE) { coord.universe = lat.outer_; } else { - std::stringstream err_msg; - err_msg << "Particle " << p->id_ << " is outside lattice " - << lat.id_ << " but the lattice has no defined outer " - "universe."; - warning(err_msg); + warning(fmt::format("Particle {} is outside lattice {} but the " + "lattice has no defined outer universe.", p->id_, lat.id_)); return false; } } @@ -298,11 +294,9 @@ cross_lattice(Particle* p, const BoundaryInfo& boundary) auto& lat {*model::lattices[coord.lattice]}; if (settings::verbosity >= 10 || p->trace_) { - std::stringstream msg; - msg << " Crossing lattice " << lat.id_ << ". Current position (" - << coord.lattice_x << "," << coord.lattice_y << "," - << coord.lattice_z << "). r=" << p->r(); - write_message(msg, 1); + write_message(fmt::format( + " Crossing lattice {}. Current position ({},{},{}). r={}", + lat.id_, coord.lattice_x, coord.lattice_y, coord.lattice_z, p->r()), 1); } // Set the lattice indices. @@ -326,10 +320,8 @@ cross_lattice(Particle* p, const BoundaryInfo& boundary) p->n_coord_ = 1; bool found = find_cell(p, 0); if (!found && p->alive_) { - std::stringstream err_msg; - err_msg << "Could not locate particle " << p->id_ - << " after crossing a lattice boundary"; - p->mark_as_lost(err_msg); + p->mark_as_lost(fmt::format("Could not locate particle {} after " + "crossing a lattice boundary", p->id_)); } } else { @@ -343,10 +335,8 @@ cross_lattice(Particle* p, const BoundaryInfo& boundary) p->n_coord_ = 1; bool found = find_cell(p, 0); if (!found && p->alive_) { - std::stringstream err_msg; - err_msg << "Could not locate particle " << p->id_ - << " after crossing a lattice boundary"; - p->mark_as_lost(err_msg); + p->mark_as_lost(fmt::format("Could not locate particle {} after " + "crossing a lattice boundary", p->id_)); } } } @@ -400,10 +390,8 @@ BoundaryInfo distance_to_boundary(Particle* p) level_lat_trans = lattice_distance.second; if (d_lat < 0) { - std::stringstream err_msg; - err_msg << "Particle " << p->id_ - << " had a negative distance to a lattice boundary"; - p->mark_as_lost(err_msg); + p->mark_as_lost(fmt::format( + "Particle {} had a negative distance to a lattice boundary", p->id_)); } } @@ -462,10 +450,7 @@ openmc_find_cell(const double* xyz, int32_t* index, int32_t* instance) p.u() = {0.0, 0.0, 1.0}; if (!find_cell(&p, false)) { - std::stringstream msg; - msg << "Could not find cell at position (" << p.r().x << ", " << p.r().y - << ", " << p.r().z << ")."; - set_errmsg(msg); + set_errmsg(fmt::format("Could not find cell at position {}.", p.r())); return OPENMC_E_GEOMETRY; } diff --git a/src/geometry_aux.cpp b/src/geometry_aux.cpp index 6ee289949..0df1198b1 100644 --- a/src/geometry_aux.cpp +++ b/src/geometry_aux.cpp @@ -4,7 +4,8 @@ #include #include -#include "pugixml.hpp" +#include +#include #include "openmc/cell.h" #include "openmc/constants.h" @@ -94,10 +95,8 @@ adjust_indices() c->type_ = Fill::LATTICE; c->fill_ = search_lat->second; } else { - std::stringstream err_msg; - err_msg << "Specified fill " << id << " on cell " << c->id_ - << " is neither a universe nor a lattice."; - fatal_error(err_msg); + fatal_error(fmt::format("Specified fill {} on cell {} is neither a " + "universe nor a lattice.", id, c->id_)); } } else { c->type_ = Fill::MATERIAL; @@ -105,10 +104,9 @@ adjust_indices() if (mat_id != MATERIAL_VOID) { auto search = model::material_map.find(mat_id); if (search == model::material_map.end()) { - std::stringstream err_msg; - err_msg << "Could not find material " << mat_id - << " specified on cell " << c->id_; - fatal_error(err_msg); + fatal_error(fmt::format( + "Could not find material {} specified on cell {}", + mat_id, c->id_)); } // Change from ID to index mat_id = search->second; @@ -123,10 +121,8 @@ adjust_indices() if (search != model::universe_map.end()) { c->universe_ = search->second; } else { - std::stringstream err_msg; - err_msg << "Could not find universe " << c->universe_ - << " specified on cell " << c->id_; - fatal_error(err_msg); + fatal_error(fmt::format("Could not find universe {} specified on cell {}", + c->universe_, c->id_)); } } @@ -345,23 +341,21 @@ prepare_distribcell() if (c.material_.size() > 1) { if (c.material_.size() != c.n_instances_) { - std::stringstream err_msg; - err_msg << "Cell " << c.id_ << " was specified with " - << c.material_.size() << " materials but has " << c.n_instances_ - << " distributed instances. The number of materials must equal " - "one or the number of instances."; - fatal_error(err_msg); + fatal_error(fmt::format( + "Cell {} was specified with {} materials but has {} distributed " + "instances. The number of materials must equal one or the number " + "of instances.", c.id_, c.material_.size(), c.n_instances_ + )); } } if (c.sqrtkT_.size() > 1) { if (c.sqrtkT_.size() != c.n_instances_) { - std::stringstream err_msg; - err_msg << "Cell " << c.id_ << " was specified with " - << c.sqrtkT_.size() << " temperatures but has " << c.n_instances_ - << " distributed instances. The number of temperatures must equal " - "one or the number of instances."; - fatal_error(err_msg); + fatal_error(fmt::format( + "Cell {} was specified with {} temperatures but has {} distributed " + "instances. The number of temperatures must equal one or the number " + "of instances.", c.id_, c.sqrtkT_.size(), c.n_instances_ + )); } } } diff --git a/src/hdf5_interface.cpp b/src/hdf5_interface.cpp index bd73e05fa..f207f53b7 100644 --- a/src/hdf5_interface.cpp +++ b/src/hdf5_interface.cpp @@ -2,12 +2,12 @@ #include #include -#include #include #include #include "xtensor/xtensor.hpp" #include "xtensor/xarray.hpp" +#include #include "hdf5.h" #include "hdf5_hl.h" @@ -105,9 +105,7 @@ create_group(hid_t parent_id, char const *name) { hid_t out = H5Gcreate(parent_id, name, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); if (out < 0) { - std::stringstream err_msg; - err_msg << "Failed to create HDF5 group \"" << name << "\""; - fatal_error(err_msg); + fatal_error(fmt::format("Failed to create HDF5 group \"{}\"", name)); } return out; } @@ -161,17 +159,13 @@ ensure_exists(hid_t obj_id, const char* name, bool attribute) { if (attribute) { if (!attribute_exists(obj_id, name)) { - std::stringstream err_msg; - err_msg << "Attribute \"" << name << "\" does not exist in object " - << object_name(obj_id); - fatal_error(err_msg); + fatal_error(fmt::format("Attribute \"{}\" does not exist in object {}", + name, object_name(obj_id))); } } else { if (!object_exists(obj_id, name)) { - std::stringstream err_msg; - err_msg << "Object \"" << name << "\" does not exist in object " - << object_name(obj_id); - fatal_error(err_msg); + fatal_error(fmt::format("Object \"{}\" does not exist in object {}", + name, object_name(obj_id))); } } } @@ -194,9 +188,7 @@ file_open(const char* filename, char mode, bool parallel) flags = (mode == 'x' ? H5F_ACC_EXCL : H5F_ACC_TRUNC); break; default: - std::stringstream err_msg; - err_msg << "Invalid file mode: " << mode; - fatal_error(err_msg); + fatal_error(fmt::format("Invalid file mode: ", mode)); } hid_t plist = H5P_DEFAULT; @@ -216,9 +208,8 @@ file_open(const char* filename, char mode, bool parallel) file_id = H5Fopen(filename, flags, plist); } if (file_id < 0) { - std::stringstream msg; - msg << "Failed to open HDF5 file with mode '" << mode << "': " << filename; - fatal_error(msg); + fatal_error(fmt::format( + "Failed to open HDF5 file with mode '{}': {}", mode, filename)); } #ifdef PHDF5 @@ -394,9 +385,7 @@ object_exists(hid_t object_id, const char* name) { htri_t out = H5LTpath_valid(object_id, name, true); if (out < 0) { - std::stringstream err_msg; - err_msg << "Failed to check if object \"" << name << "\" exists."; - fatal_error(err_msg); + fatal_error(fmt::format("Failed to check if object \"{}\" exists.", name)); } return (out > 0); } diff --git a/src/initialize.cpp b/src/initialize.cpp index 22479ae29..a5b516f68 100644 --- a/src/initialize.cpp +++ b/src/initialize.cpp @@ -3,13 +3,13 @@ #include #include // for getenv #include -#include #include #include #ifdef _OPENMP #include #endif +#include #include "openmc/capi.h" #include "openmc/constants.h" @@ -155,9 +155,8 @@ parse_command_line(int argc, char* argv[]) settings::path_particle_restart = argv[i]; settings::particle_restart_run = true; } else { - std::stringstream msg; - msg << "Unrecognized file after restart flag: " << filetype << "."; - strcpy(openmc_err_msg, msg.str().c_str()); + auto msg = fmt::format("Unrecognized file after restart flag: {}.", filetype); + strcpy(openmc_err_msg, msg.c_str()); return OPENMC_E_INVALID_ARGUMENT; } @@ -224,7 +223,7 @@ parse_command_line(int argc, char* argv[]) settings::write_all_tracks = true; } else { - std::cerr << "Unknown option: " << argv[i] << '\n'; + fmt::print(stderr, "Unknown option: {}\n", argv[i]); print_usage(); return OPENMC_E_UNASSIGNED; } diff --git a/src/lattice.cpp b/src/lattice.cpp index 092ff3d11..a8dd23aa8 100644 --- a/src/lattice.cpp +++ b/src/lattice.cpp @@ -1,9 +1,11 @@ #include "openmc/lattice.h" #include -#include +#include #include +#include + #include "openmc/cell.h" #include "openmc/error.h" #include "openmc/geometry.h" @@ -71,10 +73,8 @@ Lattice::adjust_indices() if (search != model::universe_map.end()) { *it = search->second; } else { - std::stringstream err_msg; - err_msg << "Invalid universe number " << uid << " specified on " - "lattice " << id_; - fatal_error(err_msg); + fatal_error(fmt::format( + "Invalid universe number {} specified on lattice {}", uid, id_)); } } @@ -84,10 +84,8 @@ Lattice::adjust_indices() if (search != model::universe_map.end()) { outer_ = search->second; } else { - std::stringstream err_msg; - err_msg << "Invalid universe number " << outer_ << " specified on " - "lattice " << id_; - fatal_error(err_msg); + fatal_error(fmt::format( + "Invalid universe number {} specified on lattice {}", outer_, id_)); } } } @@ -184,12 +182,9 @@ RectLattice::RectLattice(pugi::xml_node lat_node) std::string univ_str {get_node_value(lat_node, "universes")}; std::vector univ_words {split(univ_str)}; if (univ_words.size() != nx*ny*nz) { - std::stringstream err_msg; - err_msg << "Expected " << nx*ny*nz - << " universes for a rectangular lattice of size " - << nx << "x" << ny << "x" << nz << " but " << univ_words.size() - << " were specified."; - fatal_error(err_msg); + fatal_error(fmt::format( + "Expected {} universes for a rectangular lattice of size {}x{]x{} but {} " + "were specified.", nx*ny*nz, nx, ny, nz, univ_words.size())); } // Parse the universes. @@ -487,12 +482,10 @@ HexLattice::HexLattice(pugi::xml_node lat_node) std::string univ_str {get_node_value(lat_node, "universes")}; std::vector univ_words {split(univ_str)}; if (univ_words.size() != n_univ) { - std::stringstream err_msg; - err_msg << "Expected " << n_univ - << " universes for a hexagonal lattice with " << n_rings_ - << " rings and " << n_axial_ << " axial levels" << " but " - << univ_words.size() << " were specified."; - fatal_error(err_msg); + fatal_error(fmt::format( + "Expected {} universes for a hexagonal lattice with {} rings and {} " + "axial levels but {} were specified.", n_univ, n_rings_, n_axial_, + univ_words.size())); } // Parse the universes. @@ -1069,9 +1062,8 @@ void read_lattices(pugi::xml_node node) if (in_map == model::lattice_map.end()) { model::lattice_map[id] = i_lat; } else { - std::stringstream err_msg; - err_msg << "Two or more lattices use the same unique ID: " << id; - fatal_error(err_msg); + fatal_error(fmt::format( + "Two or more lattices use the same unique ID: {}", id)); } } } diff --git a/src/mgxs.cpp b/src/mgxs.cpp index 55be267bc..094361dcb 100644 --- a/src/mgxs.cpp +++ b/src/mgxs.cpp @@ -9,6 +9,7 @@ #include #endif +#include #include "xtensor/xmath.hpp" #include "xtensor/xsort.hpp" #include "xtensor/xadapt.hpp" @@ -121,10 +122,9 @@ Mgxs::metadata_from_hdf5(hid_t xs_id, const std::vector& temperature, temps_to_read.push_back(std::round(temp_actual)); } } else { - std::stringstream msg; - msg << "MGXS library does not contain cross sections for " - << in_name << " at or near " << std::round(T) << " K."; - fatal_error(msg); + fatal_error(fmt::format( + "MGXS library does not contain cross sections for {} at or near {} K.", + in_name, std::round(T))); } } break; @@ -350,10 +350,9 @@ Mgxs::Mgxs(const std::string& in_name, const std::vector& mat_kTs, auto temp_actual = micros[m]->kTs[micro_t[m]]; if (std::abs(temp_actual - temp_desired) >= K_BOLTZMANN * settings::temperature_tolerance) { - std::stringstream msg; - msg << "MGXS Library does not contain cross section for " << name - << " at or near " << std::round(temp_desired / K_BOLTZMANN) << "K."; - fatal_error(msg); + fatal_error(fmt::format( + "MGXS Library does not contain cross section for {} at or near {} K.", + name, std::round(temp_desired / K_BOLTZMANN))); } } break; diff --git a/src/output.cpp b/src/output.cpp index f32d4c259..ecfdb8a95 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -12,6 +12,7 @@ #include // for pair #include +#include #ifdef _OPENMP #include #endif @@ -184,10 +185,8 @@ extern "C" void print_particle(Particle* p) p->coord_[i].lattice_y, p->coord_[i].lattice_z); } - fmt::print(" r = "); - std::cout << p->coord_[i].r << '\n'; - fmt::print(" u = "); - std::cout << p->coord_[i].u << '\n'; + fmt::print(" r = {}\n", p->coord_[i].r); + fmt::print(" u = {}\n", p->coord_[i].u); } // Display miscellaneous info. @@ -586,7 +585,6 @@ write_tallies() // Open the tallies.out file. std::ofstream tallies_out; tallies_out.open("tallies.out", std::ios::out | std::ios::trunc); - tallies_out << std::setprecision(6); // Loop over each tally. for (auto i_tally = 0; i_tally < model::tallies.size(); ++i_tally) { @@ -595,10 +593,10 @@ write_tallies() // Write header block. std::string tally_header("TALLY " + std::to_string(tally.id_)); if (!tally.name_.empty()) tally_header += ": " + tally.name_; - tallies_out << header(tally_header) << "\n\n"; + fmt::print(tallies_out, "{}\n\n", header(tally_header)); if (!tally.writable_) { - tallies_out << " Internal\n\n"; + fmt::print(tallies_out, " Internal\n\n"); continue; } @@ -614,21 +612,20 @@ write_tallies() const auto& deriv {model::tally_derivs[tally.deriv_]}; switch (deriv.variable) { case DerivativeVariable::DENSITY: - tallies_out << " Density derivative Material " - << std::to_string(deriv.diff_material) << "\n"; + fmt::print(tallies_out, " Density derivative Material {}\n", + deriv.diff_material); break; case DerivativeVariable::NUCLIDE_DENSITY: - tallies_out << " Nuclide density derivative Material " - << std::to_string(deriv.diff_material) << " Nuclide " - << data::nuclides[deriv.diff_nuclide]->name_ << "\n"; + fmt::print(tallies_out, " Nuclide density derivative Material {} Nuclide {}\n", + deriv.diff_material, data::nuclides[deriv.diff_nuclide]->name_); break; case DerivativeVariable::TEMPERATURE: - tallies_out << " Temperature derivative Material " - << std::to_string(deriv.diff_material) << "\n"; + fmt::print(tallies_out, " Temperature derivative Material {}\n", + deriv.diff_material); break; default: - fatal_error("Differential tally dependent variable for tally " - + std::to_string(tally.id_) + " not defined in output.cpp"); + fatal_error(fmt::format("Differential tally dependent variable for " + "tally {} not defined in output.cpp", tally.id_)); } } @@ -651,8 +648,8 @@ write_tallies() auto i_filt = tally.filters(i); const auto& filt {*model::tally_filters[i_filt]}; auto& match {filter_matches[i_filt]}; - tallies_out << std::string(indent+1, ' ') - << filt.text_label(match.i_bin_) << "\n"; + fmt::print(tallies_out, "{0:{1}}{2}\n", "", indent + 1, + filt.text_label(match.i_bin_)); } indent += 2; } @@ -662,14 +659,14 @@ write_tallies() for (auto i_nuclide : tally.nuclides_) { // Write label for this nuclide bin. if (i_nuclide == -1) { - tallies_out << std::string(indent+1, ' ') << "Total Material\n"; + fmt::print(tallies_out, "{0:{1}}Total Material\n", "", indent + 1); } else { if (settings::run_CE) { - tallies_out << std::string(indent+1, ' ') - << data::nuclides[i_nuclide]->name_ << "\n"; + fmt::print(tallies_out, "{0:{1}}{2}\n", "", indent + 1, + data::nuclides[i_nuclide]->name_); } else { - tallies_out << std::string(indent+1, ' ') - << data::mg.nuclides_[i_nuclide].name << "\n"; + fmt::print(tallies_out, "{0:{1}}{2}\n", "", indent + 1, + data::mg.nuclides_[i_nuclide].name); } } @@ -681,9 +678,8 @@ write_tallies() double mean, stdev; std::tie(mean, stdev) = mean_stdev( &tally.results_(filter_index, score_index, 0), tally.n_realizations_); - tallies_out << std::string(indent+1, ' ') << std::left - << std::setw(36) << score_name << " " << mean << " +/- " - << t_value * stdev << "\n"; + fmt::print(tallies_out, "{0:{1}}{2:<36} {3:.6} +/- {4:.6}\n", + "", indent + 1, score_name, mean, t_value * stdev); score_index += 1; } indent -= 2; diff --git a/src/particle.cpp b/src/particle.cpp index bd9a61139..276fc197d 100644 --- a/src/particle.cpp +++ b/src/particle.cpp @@ -2,7 +2,8 @@ #include // copy, min #include // log, abs, copysign -#include + +#include #include "openmc/bank.h" #include "openmc/capi.h" @@ -643,14 +644,13 @@ Particle::write_restart() const if (settings::run_mode == RunMode::PARTICLE) return; // Set up file name - std::stringstream filename; - filename << settings::path_output << "particle_" << simulation::current_batch - << '_' << id_ << ".h5"; + auto filename = fmt::format("{}particle_{}_{}.h5", settings::path_output, + simulation::current_batch, id_); #pragma omp critical (WriteParticleRestart) { // Create file - hid_t file_id = file_open(filename.str(), 'w'); + hid_t file_id = file_open(filename, 'w'); // Write filetype and version info write_attribute(file_id, "filetype", "particle restart"); diff --git a/src/physics.cpp b/src/physics.cpp index 6c46bf429..a0f929125 100644 --- a/src/physics.cpp +++ b/src/physics.cpp @@ -21,9 +21,10 @@ #include "openmc/thermal.h" #include "openmc/tallies/tally.h" +#include + #include // for max, min, max_element #include // for sqrt, exp, log, abs, copysign -#include namespace openmc { @@ -61,17 +62,19 @@ void collision(Particle* p) // Display information about collision if (settings::verbosity >= 10 || p->trace_) { - std::stringstream msg; + std::string msg; if (p->event_ == TallyEvent::KILL) { - msg << " Killed. Energy = " << p->E_ << " eV."; + msg = fmt::format(" Killed. Energy = {} eV.", p->E_); } else if (p->type_ == Particle::Type::neutron) { - msg << " " << reaction_name(p->event_mt_) << " with " << - data::nuclides[p->event_nuclide_]->name_ << ". Energy = " << p->E_ << " eV."; + msg = fmt::format(" {} with {}. Energy = {} eV.", + reaction_name(p->event_mt_), data::nuclides[p->event_nuclide_]->name_, + p->E_); } else if (p->type_ == Particle::Type::photon) { - msg << " " << reaction_name(p->event_mt_) << " with " << - to_element(data::nuclides[p->event_nuclide_]->name_) << ". Energy = " << p->E_ << " eV."; + msg = fmt::format(" {} with {}. Energy = {} eV.", + reaction_name(p->event_mt_), + to_element(data::nuclides[p->event_nuclide_]->name_), p->E_); } else { - msg << " Disappeared. Energy = " << p->E_ << " eV."; + msg = fmt::format(" Disappeared. Energy = {} eV.", p->E_); } write_message(msg, 1); } @@ -189,7 +192,7 @@ create_fission_sites(Particle* p, int i_nuclide, const Reaction* rx) // Sample delayed group and angle/energy for fission reaction sample_fission_neutron(i_nuclide, rx, p->E_, &site, p->current_seed()); - + // Store fission site in bank if (use_fission_bank) { int64_t idx = simulation::fission_bank.thread_safe_append(site); @@ -210,7 +213,7 @@ create_fission_sites(Particle* p, int i_nuclide, const Reaction* rx) if (p->delayed_group_ > 0) { nu_d[p->delayed_group_-1]++; } - + // Write fission particles to nuBank if (use_fission_bank) { p->nu_bank_.emplace_back(); @@ -220,7 +223,7 @@ create_fission_sites(Particle* p, int i_nuclide, const Reaction* rx) nu_bank_entry->delayed_group = site.delayed_group; } } - + // If shared fission bank was full, and no fissions could be added, // set the particle fission flag to false. if (nu == skipped) { diff --git a/src/physics_mg.cpp b/src/physics_mg.cpp index 1ac7c1979..526a21439 100644 --- a/src/physics_mg.cpp +++ b/src/physics_mg.cpp @@ -1,8 +1,8 @@ #include "openmc/physics_mg.h" #include -#include +#include #include "xtensor/xarray.hpp" #include "openmc/bank.h" @@ -31,10 +31,8 @@ collision_mg(Particle* p) sample_reaction(p); // Display information about collision - if ((settings::verbosity >= 10) || (p->trace_)) { - std::stringstream msg; - msg << " Energy Group = " << p->g_; - write_message(msg, 1); + if ((settings::verbosity >= 10) || p->trace_) { + write_message(fmt::format(" Energy Group = {}", p->g_), 1); } } @@ -113,13 +111,13 @@ create_fission_sites(Particle* p) // Initialize the counter of delayed neutrons encountered for each delayed // group. double nu_d[MAX_DELAYED_GROUPS] = {0.}; - + // Clear out particle's nu fission bank p->nu_bank_.clear(); p->fission_ = true; int skipped = 0; - + // Determine whether to place fission sites into the shared fission bank // or the secondary particle bank. bool use_fission_bank = (settings::run_mode == RunMode::EIGENVALUE); @@ -176,7 +174,7 @@ create_fission_sites(Particle* p) if (p->delayed_group_ > 0) { nu_d[dg]++; } - + // Write fission particles to nuBank if (use_fission_bank) { p->nu_bank_.emplace_back(); @@ -186,7 +184,7 @@ create_fission_sites(Particle* p) nu_bank_entry->delayed_group = site.delayed_group; } } - + // If shared fission bank was full, and no fissions could be added, // set the particle fission flag to false. if (nu == skipped) { diff --git a/src/plot.cpp b/src/plot.cpp index 5aa6a61b8..0ff785712 100644 --- a/src/plot.cpp +++ b/src/plot.cpp @@ -4,6 +4,8 @@ #include #include +#include +#include #include "xtensor/xview.hpp" #include "openmc/constants.h" @@ -92,10 +94,8 @@ extern "C" int openmc_plot_geometry() { for (auto pl : model::plots) { - std::stringstream ss; - ss << "Processing plot " << pl.id_ << ": " - << pl.path_plot_ << "..."; - write_message(ss.str(), 5); + write_message(fmt::format("Processing plot {}: {}...", + pl.id_, pl.path_plot_), 5); if (PlotType::slice == pl.type_) { // create 2D image @@ -188,9 +188,7 @@ Plot::set_id(pugi::xml_node plot_node) // Check to make sure 'id' hasn't been used if (model::plot_map.find(id_) != model::plot_map.end()) { - std::stringstream err_msg; - err_msg << "Two or more plots use the same unique ID: " << id_; - fatal_error(err_msg.str()); + fatal_error(fmt::format("Two or more plots use the same unique ID: {}", id_)); } } @@ -210,11 +208,9 @@ Plot::set_type(pugi::xml_node plot_node) else if (type_str == "voxel") { type_ = PlotType::voxel; } else { - // if we're here, something is wrong - std::stringstream err_msg; - err_msg << "Unsupported plot type '" << type_str - << "' in plot " << id_; - fatal_error(err_msg.str()); + // if we're here, something is wrong + fatal_error(fmt::format("Unsupported plot type '{}' in plot {}", + type_str, id_)); } } } @@ -223,24 +219,24 @@ void Plot::set_output_path(pugi::xml_node plot_node) { // Set output file path - std::stringstream filename; + std::string filename; if (check_for_node(plot_node, "filename")) { - filename << get_node_value(plot_node, "filename"); + filename = get_node_value(plot_node, "filename"); } else { - filename << "plot_" << id_; + filename = fmt::format("plot_{}", id_); } // add appropriate file extension to name switch(type_) { case PlotType::slice: - filename << ".ppm"; + filename.append(".ppm"); break; case PlotType::voxel: - filename << ".h5"; + filename.append(".h5"); break; } - path_plot_ = filename.str(); + path_plot_ = filename; // Copy plot pixel size std::vector pxls = get_node_array(plot_node, "pixels"); @@ -249,10 +245,7 @@ Plot::set_output_path(pugi::xml_node plot_node) pixels_[0] = pxls[0]; pixels_[1] = pxls[1]; } else { - std::stringstream err_msg; - err_msg << " must be length 2 in slice plot " - << id_; - fatal_error(err_msg.str()); + fatal_error(fmt::format(" must be length 2 in slice plot {}", id_)); } } else if (PlotType::voxel == type_) { if (pxls.size() == 3) { @@ -260,10 +253,7 @@ Plot::set_output_path(pugi::xml_node plot_node) pixels_[1] = pxls[1]; pixels_[2] = pxls[2]; } else { - std::stringstream err_msg; - err_msg << " must be length 3 in voxel plot " - << id_; - fatal_error(err_msg.str()); + fatal_error(fmt::format(" must be length 3 in voxel plot {}", id_)); } } } @@ -276,19 +266,13 @@ Plot::set_bg_color(pugi::xml_node plot_node) std::vector bg_rgb = get_node_array(plot_node, "background"); if (PlotType::voxel == type_) { if (mpi::master) { - std::stringstream err_msg; - err_msg << "Background color ignored in voxel plot " - << id_; - warning(err_msg.str()); + warning(fmt::format("Background color ignored in voxel plot {}", id_)); } } if (bg_rgb.size() == 3) { not_found_ = bg_rgb; } else { - std::stringstream err_msg; - err_msg << "Bad background RGB in plot " - << id_; - fatal_error(err_msg); + fatal_error(fmt::format("Bad background RGB in plot {}", id_)); } } } @@ -309,10 +293,8 @@ Plot::set_basis(pugi::xml_node plot_node) } else if ("yz" == pl_basis) { basis_ = PlotBasis::yz; } else { - std::stringstream err_msg; - err_msg << "Unsupported plot basis '" << pl_basis - << "' in plot " << id_; - fatal_error(err_msg); + fatal_error(fmt::format("Unsupported plot basis '{}' in plot {}", + pl_basis, id_)); } } } @@ -325,10 +307,7 @@ Plot::set_origin(pugi::xml_node plot_node) if (pl_origin.size() == 3) { origin_ = pl_origin; } else { - std::stringstream err_msg; - err_msg << "Origin must be length 3 in plot " - << id_; - fatal_error(err_msg); + fatal_error(fmt::format("Origin must be length 3 in plot {}", id_)); } } @@ -342,20 +321,14 @@ Plot::set_width(pugi::xml_node plot_node) width_.x = pl_width[0]; width_.y = pl_width[1]; } else { - std::stringstream err_msg; - err_msg << " must be length 2 in slice plot " - << id_; - fatal_error(err_msg); + fatal_error(fmt::format(" must be length 2 in slice plot {}", id_)); } } else if (PlotType::voxel == type_) { if (pl_width.size() == 3) { pl_width = get_node_array(plot_node, "width"); width_ = pl_width; } else { - std::stringstream err_msg; - err_msg << " must be length 3 in voxel plot " - << id_; - fatal_error(err_msg); + fatal_error(fmt::format(" must be length 3 in voxel plot {}", id_)); } } } @@ -367,9 +340,7 @@ Plot::set_universe(pugi::xml_node plot_node) if (check_for_node(plot_node, "level")) { level_ = std::stoi(get_node_value(plot_node, "level")); if (level_ < 0) { - std::stringstream err_msg; - err_msg << "Bad universe level in plot " << id_; - fatal_error(err_msg); + fatal_error(fmt::format("Bad universe level in plot {}", id_)); } } else { level_ = PLOT_LEVEL_LOWEST; @@ -391,10 +362,8 @@ Plot::set_default_colors(pugi::xml_node plot_node) color_by_ = PlotColorBy::mats; colors_.resize(model::materials.size()); } else { - std::stringstream err_msg; - err_msg << "Unsupported plot color type '" << pl_color_by - << "' in plot " << id_; - fatal_error(err_msg); + fatal_error(fmt::format("Unsupported plot color type '{}' in plot {}", + pl_color_by, id_)); } for (auto& c : colors_) { @@ -411,10 +380,7 @@ Plot::set_user_colors(pugi::xml_node plot_node) { if (!plot_node.select_nodes("color").empty() && PlotType::voxel == type_) { if (mpi::master) { - std::stringstream err_msg; - err_msg << "Color specifications ignored in voxel plot " - << id_; - warning(err_msg); + warning(fmt::format("Color specifications ignored in voxel plot {}", id_)); } } @@ -422,19 +388,15 @@ Plot::set_user_colors(pugi::xml_node plot_node) // Make sure 3 values are specified for RGB std::vector user_rgb = get_node_array(cn, "rgb"); if (user_rgb.size() != 3) { - std::stringstream err_msg; - err_msg << "Bad RGB in plot " << id_; - fatal_error(err_msg); + fatal_error(fmt::format("Bad RGB in plot {}", id_)); } // Ensure that there is an id for this color specification int col_id; if (check_for_node(cn, "id")) { col_id = std::stoi(get_node_value(cn, "id")); } else { - std::stringstream err_msg; - err_msg << "Must specify id for color specification in plot " - << id_; - fatal_error(err_msg); + fatal_error(fmt::format( + "Must specify id for color specification in plot {}", id_)); } // Add RGB if (PlotColorBy::cells == color_by_) { @@ -442,20 +404,16 @@ Plot::set_user_colors(pugi::xml_node plot_node) col_id = model::cell_map[col_id]; colors_[col_id] = user_rgb; } else { - std::stringstream err_msg; - err_msg << "Could not find cell " << col_id - << " specified in plot " << id_; - fatal_error(err_msg); + fatal_error(fmt::format("Could not find cell {} specified in plot {}", + col_id, id_)); } } else if (PlotColorBy::mats == color_by_) { if (model::material_map.find(col_id) != model::material_map.end()) { col_id = model::material_map[col_id]; colors_[col_id] = user_rgb; } else { - std::stringstream err_msg; - err_msg << "Could not find material " << col_id - << " specified in plot " << id_; - fatal_error(err_msg); + fatal_error(fmt::format( + "Could not find material {} specified in plot {}", col_id, id_)); } } } // color node loop @@ -469,9 +427,7 @@ Plot::set_meshlines(pugi::xml_node plot_node) if (!mesh_line_nodes.empty()) { if (PlotType::voxel == type_) { - std::stringstream msg; - msg << "Meshlines ignored in voxel plot " << id_; - warning(msg); + warning(fmt::format("Meshlines ignored in voxel plot {}", id_)); } if (mesh_line_nodes.size() == 1) { @@ -483,9 +439,8 @@ Plot::set_meshlines(pugi::xml_node plot_node) if (check_for_node(meshlines_node, "meshtype")) { meshtype = get_node_value(meshlines_node, "meshtype"); } else { - std::stringstream err_msg; - err_msg << "Must specify a meshtype for meshlines specification in plot " << id_; - fatal_error(err_msg); + fatal_error(fmt::format( + "Must specify a meshtype for meshlines specification in plot {}", id_)); } // Ensure that there is a linewidth for this meshlines specification @@ -494,9 +449,8 @@ Plot::set_meshlines(pugi::xml_node plot_node) meshline_width = get_node_value(meshlines_node, "linewidth"); meshlines_width_ = std::stoi(meshline_width); } else { - std::stringstream err_msg; - err_msg << "Must specify a linewidth for meshlines specification in plot " << id_; - fatal_error(err_msg); + fatal_error(fmt::format( + "Must specify a linewidth for meshlines specification in plot {}", id_)); } // Check for color @@ -504,9 +458,7 @@ Plot::set_meshlines(pugi::xml_node plot_node) // Check and make sure 3 values are specified for RGB std::vector ml_rgb = get_node_array(meshlines_node, "color"); if (ml_rgb.size() != 3) { - std::stringstream err_msg; - err_msg << "Bad RGB for meshlines color in plot " << id_; - fatal_error(err_msg); + fatal_error(fmt::format("Bad RGB for meshlines color in plot {}", id_)); } meshlines_color_ = ml_rgb; } @@ -514,9 +466,7 @@ Plot::set_meshlines(pugi::xml_node plot_node) // Set mesh based on type if ("ufs" == meshtype) { if (!simulation::ufs_mesh) { - std::stringstream err_msg; - err_msg << "No UFS mesh for meshlines on plot " << id_; - fatal_error(err_msg); + fatal_error(fmt::format("No UFS mesh for meshlines on plot {}", id_)); } else { for (int i = 0; i < model::meshes.size(); ++i) { if (const auto* m @@ -531,9 +481,7 @@ Plot::set_meshlines(pugi::xml_node plot_node) } } else if ("entropy" == meshtype) { if (!simulation::entropy_mesh) { - std::stringstream err_msg; - err_msg << "No entropy mesh for meshlines on plot " << id_; - fatal_error(err_msg); + fatal_error(fmt::format("No entropy mesh for meshlines on plot {}", id_)); } else { for (int i = 0; i < model::meshes.size(); ++i) { if (const auto* m @@ -553,29 +501,22 @@ Plot::set_meshlines(pugi::xml_node plot_node) tally_mesh_id = std::stoi(get_node_value(meshlines_node, "id")); } else { std::stringstream err_msg; - err_msg << "Must specify a mesh id for meshlines tally " - << "mesh specification in plot " << id_; - fatal_error(err_msg); + fatal_error(fmt::format("Must specify a mesh id for meshlines tally " + "mesh specification in plot {}", id_)); } // find the tally index int idx; int err = openmc_get_mesh_index(tally_mesh_id, &idx); if (err != 0) { - std::stringstream err_msg; - err_msg << "Could not find mesh " << tally_mesh_id - << " specified in meshlines for plot " << id_; - fatal_error(err_msg); + fatal_error(fmt::format("Could not find mesh {} specified in " + "meshlines for plot {}", tally_mesh_id, id_)); } index_meshlines_mesh_ = idx; } else { - std::stringstream err_msg; - err_msg << "Invalid type for meshlines on plot " << id_ ; - fatal_error(err_msg); + fatal_error(fmt::format("Invalid type for meshlines on plot {}", id_ )); } } else { - std::stringstream err_msg; - err_msg << "Mutliple meshlines specified in plot " << id_; - fatal_error(err_msg); + fatal_error(fmt::format("Mutliple meshlines specified in plot {}", id_)); } } } @@ -589,9 +530,7 @@ Plot::set_mask(pugi::xml_node plot_node) if (!mask_nodes.empty()) { if (PlotType::voxel == type_) { if (mpi::master) { - std::stringstream wrn_msg; - wrn_msg << "Mask ignored in voxel plot " << id_; - warning(wrn_msg); + warning(fmt::format("Mask ignored in voxel plot {}", id_)); } } @@ -602,9 +541,7 @@ Plot::set_mask(pugi::xml_node plot_node) // Determine how many components there are and allocate std::vector iarray = get_node_array(mask_node, "components"); if (iarray.size() == 0) { - std::stringstream err_msg; - err_msg << "Missing in mask of plot " << id_; - fatal_error(err_msg); + fatal_error(fmt::format("Missing in mask of plot {}", id_)); } // First we need to change the user-specified identifiers to indices @@ -615,20 +552,16 @@ Plot::set_mask(pugi::xml_node plot_node) col_id = model::cell_map[col_id]; } else { - std::stringstream err_msg; - err_msg << "Could not find cell " << col_id - << " specified in the mask in plot " << id_; - fatal_error(err_msg); + fatal_error(fmt::format("Could not find cell {} specified in the " + "mask in plot {}", col_id, id_)); } } else if (PlotColorBy::mats == color_by_) { if (model::material_map.find(col_id) != model::material_map.end()) { col_id = model::material_map[col_id]; } else { - std::stringstream err_msg; - err_msg << "Could not find material " << col_id - << " specified in the mask in plot " << id_; - fatal_error(err_msg); + fatal_error(fmt::format("Could not find material {} specified in " + "the mask in plot {}", col_id, id_)); } } } @@ -646,9 +579,7 @@ Plot::set_mask(pugi::xml_node plot_node) } } else { - std::stringstream err_msg; - err_msg << "Mutliple masks specified in plot " << id_; - fatal_error(err_msg); + fatal_error(fmt::format("Mutliple masks specified in plot {}", id_)); } } } @@ -660,18 +591,14 @@ void Plot::set_overlap_color(pugi::xml_node plot_node) { // check for custom overlap color if (check_for_node(plot_node, "overlap_color")) { if (!color_overlaps_) { - std::stringstream wrn_msg; - wrn_msg << "Overlap color specified in plot " << id_ - << " but overlaps won't be shown."; - warning(wrn_msg); + warning(fmt::format( + "Overlap color specified in plot {} but overlaps won't be shown.", id_)); } std::vector olap_clr = get_node_array(plot_node, "overlap_color"); if (olap_clr.size() == 3) { overlap_color_ = olap_clr; } else { - std::stringstream err_msg; - err_msg << "Bad overlap RGB in plot " << id_; - fatal_error(err_msg); + fatal_error(fmt::format("Bad overlap RGB in plot {}", id_)); } } } @@ -716,9 +643,9 @@ void output_ppm(Plot pl, const ImageData& data) of.open(fname); // Write header - of << "P6" << "\n"; + of << "P6\n"; of << pl.pixels_[0] << " " << pl.pixels_[1] << "\n"; - of << "255" << "\n"; + of << "255\n"; of.close(); of.open(fname, std::ios::binary | std::ios::app); @@ -729,11 +656,7 @@ void output_ppm(Plot pl, const ImageData& data) of << rgb.red << rgb.green << rgb.blue; } } - - // Close file - // THIS IS HERE TO MATCH FORTRAN VERSION, NOT TECHNICALLY NECESSARY of << "\n"; - of.close(); } //============================================================================== diff --git a/src/reaction.cpp b/src/reaction.cpp index f7f1e87ac..ff22540e1 100644 --- a/src/reaction.cpp +++ b/src/reaction.cpp @@ -3,6 +3,8 @@ #include #include // for move +#include + #include "openmc/constants.h" #include "openmc/hdf5_interface.h" #include "openmc/endf.h" @@ -35,8 +37,7 @@ Reaction::Reaction(hid_t group, const std::vector& temperatures) // Read cross section and threshold_idx data for (auto t : temperatures) { // Get group corresponding to temperature - std::string temp_str {std::to_string(t) + "K"}; - hid_t temp_group = open_group(group, temp_str.c_str()); + hid_t temp_group = open_group(group, fmt::format("{}K", t).c_str()); hid_t dset = open_dataset(temp_group, "xs"); // Get threshold index @@ -178,7 +179,7 @@ std::string reaction_name(int mt) } else if (mt == N_NPA) { return "(n,npa)"; } else if (N_N1 <= mt && mt <= N_N40) { - return "(n,n" + std::to_string(mt-50) + ")"; + return fmt::format("(n,n{})", mt - 50); } else if (mt == N_NC) { return "(n,nc)"; } else if (mt == N_DISAPPEAR) { @@ -244,33 +245,31 @@ std::string reaction_name(int mt) } else if (mt == PHOTOELECTRIC) { return "photoelectric"; } else if (534 <= mt && mt <= 572) { - std::stringstream name; - name << "photoelectric, " << SUBSHELLS[mt - 534] << " subshell"; - return name.str(); + return fmt::format("photoelectric, {} subshell", SUBSHELLS[mt - 534]); } else if (600 <= mt && mt <= 648) { - return "(n,p" + std::to_string(mt-600) + ")"; + return fmt::format("(n,p{})", mt - 600); } else if (mt == 649) { return "(n,pc)"; } else if (650 <= mt && mt <= 698) { - return "(n,d" + std::to_string(mt-650) + ")"; + return fmt::format("(n,d{})", mt - 650); } else if (mt == 699) { return "(n,dc)"; } else if (700 <= mt && mt <= 748) { - return "(n,t" + std::to_string(mt-700) + ")"; + return fmt::format("(n,t{})", mt - 700); } else if (mt == 749) { return "(n,tc)"; } else if (750 <= mt && mt <= 798) { - return "(n,3He" + std::to_string(mt-750) + ")"; + return fmt::format("(n,3He{})", mt - 750); } else if (mt == 799) { return "(n,3Hec)"; } else if (800 <= mt && mt <= 848) { - return "(n,a" + std::to_string(mt-800) + ")"; + return fmt::format("(n,a{})", mt - 800); } else if (mt == 849) { return "(n,ac)"; } else if (mt == HEATING_LOCAL) { return "heating-local"; } else { - return "MT=" + std::to_string(mt); + return fmt::format("MT={}", mt); } } diff --git a/src/secondary_uncorrelated.cpp b/src/secondary_uncorrelated.cpp index 1421fcf83..320761916 100644 --- a/src/secondary_uncorrelated.cpp +++ b/src/secondary_uncorrelated.cpp @@ -1,8 +1,9 @@ #include "openmc/secondary_uncorrelated.h" -#include // for stringstream #include // for string +#include + #include "openmc/error.h" #include "openmc/hdf5_interface.h" #include "openmc/random_lcg.h" @@ -42,9 +43,7 @@ UncorrelatedAngleEnergy::UncorrelatedAngleEnergy(hid_t group) } else if (type == "watt") { energy_ = UPtrEDist{new WattEnergy{energy_group}}; } else { - std::stringstream msg; - msg << "Energy distribution type '" << type << "' not implemented."; - warning(msg); + warning(fmt::format("Energy distribution type '{}' not implemented.", type)); } close_group(energy_group); } From 2a230cd73947d2a449c53f52237339683ca4de6c Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 20 Jan 2020 13:43:02 -0600 Subject: [PATCH 019/166] More use of fmt::print and fmt::format --- include/openmc/plot.h | 5 +- include/openmc/volume_calc.h | 1 + src/settings.cpp | 27 ++++----- src/source.cpp | 22 +++---- src/state_point.cpp | 14 ++--- src/surface.cpp | 62 +++++++------------- src/tallies/derivative.cpp | 24 +++----- src/tallies/filter_azimuthal.cpp | 7 +-- src/tallies/filter_cell.cpp | 10 ++-- src/tallies/filter_cell_instance.cpp | 15 +++-- src/tallies/filter_distribcell.cpp | 8 +-- src/tallies/filter_energy.cpp | 10 ++-- src/tallies/filter_energyfunc.cpp | 13 ++--- src/tallies/filter_material.cpp | 10 ++-- src/tallies/filter_mesh.cpp | 21 ++++--- src/tallies/filter_mu.cpp | 6 +- src/tallies/filter_polar.cpp | 6 +- src/tallies/filter_sph_harm.cpp | 16 ++--- src/tallies/filter_sptl_legendre.cpp | 12 ++-- src/tallies/filter_surface.cpp | 10 ++-- src/tallies/filter_universe.cpp | 10 ++-- src/tallies/filter_zernike.cpp | 10 ++-- src/tallies/tally.cpp | 87 ++++++++++++---------------- src/tallies/trigger.cpp | 20 ++++--- src/thermal.cpp | 16 ++--- src/track_output.cpp | 12 ++-- src/volume_calc.cpp | 30 ++++------ src/wmp.cpp | 16 ++--- src/xml_interface.cpp | 21 +++---- 29 files changed, 219 insertions(+), 302 deletions(-) diff --git a/include/openmc/plot.h b/include/openmc/plot.h index 04db9710a..e05d6f2e2 100644 --- a/include/openmc/plot.h +++ b/include/openmc/plot.h @@ -11,6 +11,7 @@ #include "openmc/position.h" #include "openmc/constants.h" #include "openmc/cell.h" +#include "openmc/error.h" #include "openmc/geometry.h" #include "openmc/particle.h" #include "openmc/xml_interface.h" @@ -154,10 +155,8 @@ T PlotBase::get_map() const { in_i = 1; out_i = 2; break; -#ifdef __GNUC__ default: - __builtin_unreachable(); -#endif + UNREACHABLE(); } // set initial position diff --git a/include/openmc/volume_calc.h b/include/openmc/volume_calc.h index e025ab3b0..3258be9d6 100644 --- a/include/openmc/volume_calc.h +++ b/include/openmc/volume_calc.h @@ -7,6 +7,7 @@ #include "pugixml.hpp" #include "xtensor/xtensor.hpp" +#include #include #include #include diff --git a/src/settings.cpp b/src/settings.cpp index 32a7958d8..9bdfd7756 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -2,9 +2,9 @@ #include // for ceil, pow #include // for numeric_limits -#include #include +#include #ifdef _OPENMP #include #endif @@ -130,7 +130,7 @@ void get_run_parameters(pugi::xml_node node_base) if (n_particles == -1) { n_particles = std::stoll(get_node_value(node_base, "particles")); } - + // Get maximum number of in flight particles for event-based mode if (check_for_node(node_base, "max_particles_in_flight")) { max_particles_in_flight = std::stoll(get_node_value(node_base, @@ -195,13 +195,13 @@ void read_settings_xml() std::string filename = path_input + "settings.xml"; if (!file_exists(filename)) { if (run_mode != RunMode::PLOTTING) { - std::stringstream msg; - msg << "Settings XML file '" << filename << "' does not exist! In order " + fatal_error(fmt::format( + "Settings XML file '{}' does not exist! In order " "to run OpenMC, you first need a set of input files; at a minimum, this " "includes settings.xml, geometry.xml, and materials.xml. Please consult " - "the user's guide at http://openmc.readthedocs.io for further " - "information."; - fatal_error(msg); + "the user's guide at https://docs.openmc.org for further " + "information.", filename + )); } else { // The settings.xml file is optional if we just want to make a plot. return; @@ -494,9 +494,8 @@ void read_settings_xml() if (check_for_node(root, "entropy_mesh")) { int temp = std::stoi(get_node_value(root, "entropy_mesh")); if (model::mesh_map.find(temp) == model::mesh_map.end()) { - std::stringstream msg; - msg << "Mesh " << temp << " specified for Shannon entropy does not exist."; - fatal_error(msg); + fatal_error(fmt::format( + "Mesh {} specified for Shannon entropy does not exist.", temp)); } index_entropy_mesh = model::mesh_map.at(temp); @@ -544,10 +543,8 @@ void read_settings_xml() if (check_for_node(root, "ufs_mesh")) { auto temp = std::stoi(get_node_value(root, "ufs_mesh")); if (model::mesh_map.find(temp) == model::mesh_map.end()) { - std::stringstream msg; - msg << "Mesh " << temp << " specified for uniform fission site method " - "does not exist."; - fatal_error(msg); + fatal_error(fmt::format("Mesh {} specified for uniform fission site " + "method does not exist.", temp)); } i_ufs_mesh = model::mesh_map.at(temp); @@ -792,7 +789,7 @@ void read_settings_xml() if (check_for_node(root, "delayed_photon_scaling")) { delayed_photon_scaling = get_node_value_bool(root, "delayed_photon_scaling"); } - + // Check whether to use event-based parallelism if (check_for_node(root, "event_based")) { event_based = get_node_value_bool(root, "event_based"); diff --git a/src/source.cpp b/src/source.cpp index 6314eb23f..631520854 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -1,8 +1,8 @@ #include "openmc/source.h" #include // for move -#include // for stringstream +#include #include "xtensor/xadapt.hpp" #include "openmc/bank.h" @@ -68,9 +68,8 @@ SourceDistribution::SourceDistribution(pugi::xml_node node) // Check if source file exists if (!file_exists(settings::path_source)) { - std::stringstream msg; - msg << "Source file '" << settings::path_source << "' does not exist."; - fatal_error(msg); + fatal_error(fmt::format("Source file '{}' does not exist.", + settings::path_source)); } } else { @@ -97,9 +96,8 @@ SourceDistribution::SourceDistribution(pugi::xml_node node) } else if (type == "point") { space_ = UPtrSpace{new SpatialPoint(node_space)}; } else { - std::stringstream msg; - msg << "Invalid spatial distribution for external source: " << type; - fatal_error(msg); + fatal_error(fmt::format( + "Invalid spatial distribution for external source: {}", type)); } } else { @@ -123,9 +121,8 @@ SourceDistribution::SourceDistribution(pugi::xml_node node) } else if (type == "mu-phi") { angle_ = UPtrAngle{new PolarAzimuthal(node_angle)}; } else { - std::stringstream msg; - msg << "Invalid angular distribution for external source: " << type; - fatal_error(msg); + fatal_error(fmt::format( + "Invalid angular distribution for external source: {}", type)); } } else { @@ -244,9 +241,8 @@ void initialize_source() // Read the source from a binary file instead of sampling from some // assumed source distribution - std::stringstream msg; - msg << "Reading source file from " << settings::path_source << "..."; - write_message(msg, 6); + write_message(fmt::format("Reading source file from {}...", + settings::path_source), 6); // Open the binary file hid_t file_id = file_open(settings::path_source, 'r', true); diff --git a/src/state_point.cpp b/src/state_point.cpp index 60fd3826d..706eb17ef 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -2,10 +2,10 @@ #include #include // for int64_t -#include // for setfill, setw #include #include +#include #include "xtensor/xbuilder.hpp" // for empty_like #include "xtensor/xview.hpp" @@ -41,10 +41,8 @@ openmc_statepoint_write(const char* filename, bool* write_source) int w = std::to_string(settings::n_max_batches).size(); // Set filename for state point - std::stringstream ss; - ss << settings::path_output << "statepoint." << std::setfill('0') - << std::setw(w) << simulation::current_batch << ".h5"; - filename_ = ss.str(); + filename_ = fmt::format("{0}statepoint.{1:0{2}}.h5", + settings::path_output, simulation::current_batch, w); } // Determine whether or not to write the source bank @@ -523,10 +521,8 @@ write_source_point(const char* filename) // Determine width for zero padding int w = std::to_string(settings::n_max_batches).size(); - std::stringstream s; - s << settings::path_output << "source." << std::setfill('0') - << std::setw(w) << simulation::current_batch << ".h5"; - filename_ = s.str(); + filename_ = fmt::format("{0}source.{1:0{2}}.h5", + settings::path_output, simulation::current_batch, w); } hid_t file_id; diff --git a/src/surface.cpp b/src/surface.cpp index f5399fa6a..b1ba2eb05 100644 --- a/src/surface.cpp +++ b/src/surface.cpp @@ -2,9 +2,10 @@ #include #include -#include #include +#include + #include "openmc/error.h" #include "openmc/dagmc.h" #include "openmc/hdf5_interface.h" @@ -35,10 +36,8 @@ void read_coeffs(pugi::xml_node surf_node, int surf_id, double &c1) std::string coeffs = get_node_value(surf_node, "coeffs"); int n_words = word_count(coeffs); if (n_words != 1) { - std::stringstream err_msg; - err_msg << "Surface " << surf_id << " expects 1 coeff but was given " - << n_words; - fatal_error(err_msg); + fatal_error(fmt::format("Surface {} expects 1 coeff but was given {}", + surf_id, n_words)); } // Parse the coefficients. @@ -55,10 +54,8 @@ void read_coeffs(pugi::xml_node surf_node, int surf_id, double &c1, double &c2, std::string coeffs = get_node_value(surf_node, "coeffs"); int n_words = word_count(coeffs); if (n_words != 3) { - std::stringstream err_msg; - err_msg << "Surface " << surf_id << " expects 3 coeffs but was given " - << n_words; - fatal_error(err_msg); + fatal_error(fmt::format("Surface {} expects 3 coeffs but was given {}", + surf_id, n_words)); } // Parse the coefficients. @@ -75,10 +72,8 @@ void read_coeffs(pugi::xml_node surf_node, int surf_id, double &c1, double &c2, std::string coeffs = get_node_value(surf_node, "coeffs"); int n_words = word_count(coeffs); if (n_words != 4) { - std::stringstream err_msg; - err_msg << "Surface " << surf_id << " expects 4 coeffs but was given " - << n_words; - fatal_error(err_msg); + fatal_error(fmt::format("Surface {} expects 4 coeffs but was given ", + surf_id, n_words)); } // Parse the coefficients. @@ -96,10 +91,8 @@ void read_coeffs(pugi::xml_node surf_node, int surf_id, double &c1, double &c2, std::string coeffs = get_node_value(surf_node, "coeffs"); int n_words = word_count(coeffs); if (n_words != 10) { - std::stringstream err_msg; - err_msg << "Surface " << surf_id << " expects 10 coeffs but was given " - << n_words; - fatal_error(err_msg); + fatal_error(fmt::format("Surface {} expects 10 coeffs but was given {}", + surf_id, n_words)); } // Parse the coefficients. @@ -145,10 +138,8 @@ Surface::Surface(pugi::xml_node surf_node) } else if (surf_bc == "periodic") { bc_ = BoundaryType::PERIODIC; } else { - std::stringstream err_msg; - err_msg << "Unknown boundary condition \"" << surf_bc - << "\" specified on surface " << id_; - fatal_error(err_msg); + fatal_error(fmt::format("Unknown boundary condition \"{}\" specified " + "on surface {}", surf_bc, id_)); } } else { @@ -1170,9 +1161,7 @@ void read_surfaces(pugi::xml_node node) model::surfaces.push_back(std::make_unique(surf_node)); } else { - std::stringstream err_msg; - err_msg << "Invalid surface type, \"" << surf_type << "\""; - fatal_error(err_msg); + fatal_error(fmt::format("Invalid surface type, \"{}\"", surf_type)); } } } @@ -1184,9 +1173,8 @@ void read_surfaces(pugi::xml_node node) if (in_map == model::surface_map.end()) { model::surface_map[id] = i_surf; } else { - std::stringstream err_msg; - err_msg << "Two or more surfaces use the same unique ID: " << id; - fatal_error(err_msg); + fatal_error(fmt::format( + "Two or more surfaces use the same unique ID: {}", id)); } } @@ -1202,11 +1190,9 @@ void read_surfaces(pugi::xml_node node) // Make sure this surface inherits from PeriodicSurface. if (!surf) { - std::stringstream err_msg; - err_msg << "Periodic boundary condition not supported for surface " - << surf_base->id_ - << ". Periodic BCs are only supported for planar surfaces."; - fatal_error(err_msg); + fatal_error(fmt::format( + "Periodic boundary condition not supported for surface {}. Periodic " + "BCs are only supported for planar surfaces.", surf_base->id_)); } // See if this surface makes part of the global bounding box. @@ -1278,10 +1264,8 @@ void read_surfaces(pugi::xml_node node) // This is a SurfacePlane. We won't try to find it's partner if the // user didn't specify one. if (surf->i_periodic_ == C_NONE) { - std::stringstream err_msg; - err_msg << "No matching periodic surface specified for periodic " - "boundary condition on surface " << surf->id_; - fatal_error(err_msg); + fatal_error(fmt::format("No matching periodic surface specified for " + "periodic boundary condition on surface {}", surf->id_)); } else { // Convert the surface id to an index. surf->i_periodic_ = model::surface_map[surf->i_periodic_]; @@ -1290,10 +1274,8 @@ void read_surfaces(pugi::xml_node node) // Make sure the opposite surface is also periodic. if (model::surfaces[surf->i_periodic_]->bc_ != Surface::BoundaryType::PERIODIC) { - std::stringstream err_msg; - err_msg << "Could not find matching surface for periodic boundary " - "condition on surface " << surf->id_; - fatal_error(err_msg); + fatal_error(fmt::format("Could not find matching surface for periodic " + "boundary condition on surface {}", surf->id_)); } } } diff --git a/src/tallies/derivative.cpp b/src/tallies/derivative.cpp index 1d2d2fc99..483008340 100644 --- a/src/tallies/derivative.cpp +++ b/src/tallies/derivative.cpp @@ -7,7 +7,7 @@ #include "openmc/tallies/tally.h" #include "openmc/xml_interface.h" -#include +#include template class std::vector; @@ -55,20 +55,16 @@ TallyDerivative::TallyDerivative(pugi::xml_node node) } } if (!found) { - std::stringstream out; - out << "Could not find the nuclide \"" << nuclide_name - << "\" specified in derivative " << id << " in any material."; - fatal_error(out); + fatal_error(fmt::format("Could not find the nuclide \"{}\" specified in " + "derivative {} in any material.", nuclide_name, id)); } } else if (variable_str == "temperature") { variable = DerivativeVariable::TEMPERATURE; } else { - std::stringstream out; - out << "Unrecognized variable \"" << variable_str - << "\" on derivative " << id; - fatal_error(out); + fatal_error(fmt::format("Unrecognized variable \"{}\" on derivative {}", + variable_str, id)); } diff_material = std::stoi(get_node_value(node, "material")); @@ -568,7 +564,7 @@ score_track_derivative(Particle* p, double distance) // A void material cannot be perturbed so it will not affect flux derivatives. if (p->material_ == MATERIAL_VOID) return; const Material& material {*model::materials[p->material_]}; - + for (auto idx = 0; idx < model::tally_derivs.size(); idx++) { const auto& deriv = model::tally_derivs[idx]; auto& flux_deriv = p->flux_derivs_[idx]; @@ -641,11 +637,9 @@ void score_collision_derivative(Particle* p) if (material.nuclide_[i] == deriv.diff_nuclide) break; // Make sure we found the nuclide. if (material.nuclide_[i] != deriv.diff_nuclide) { - std::stringstream err_msg; - err_msg << "Could not find nuclide " - << data::nuclides[deriv.diff_nuclide]->name_ << " in material " - << material.id_ << " for tally derivative " << deriv.id; - fatal_error(err_msg); + fatal_error(fmt::format( + "Could not find nuclide {} in material {} for tally derivative {}", + data::nuclides[deriv.diff_nuclide]->name_, material.id_, deriv.id)); } // phi is proportional to Sigma_s // (1 / phi) * (d_phi / d_N) = (d_Sigma_s / d_N) / Sigma_s diff --git a/src/tallies/filter_azimuthal.cpp b/src/tallies/filter_azimuthal.cpp index 2cf03a127..4d18e5974 100644 --- a/src/tallies/filter_azimuthal.cpp +++ b/src/tallies/filter_azimuthal.cpp @@ -1,7 +1,8 @@ #include "openmc/tallies/filter_azimuthal.h" #include -#include + +#include #include "openmc/constants.h" #include "openmc/error.h" @@ -77,9 +78,7 @@ AzimuthalFilter::to_statepoint(hid_t filter_group) const std::string AzimuthalFilter::text_label(int bin) const { - std::stringstream out; - out << "Azimuthal Angle [" << bins_[bin] << ", " << bins_[bin+1] << ")"; - return out.str(); + return fmt::format("Azimuthal Angle [{}, {})", bins_[bin], bins_[bin+1]); } } // namespace openmc diff --git a/src/tallies/filter_cell.cpp b/src/tallies/filter_cell.cpp index 3077c42ea..86e78df91 100644 --- a/src/tallies/filter_cell.cpp +++ b/src/tallies/filter_cell.cpp @@ -1,6 +1,6 @@ #include "openmc/tallies/filter_cell.h" -#include +#include #include "openmc/capi.h" #include "openmc/cell.h" @@ -17,10 +17,8 @@ CellFilter::from_xml(pugi::xml_node node) for (auto& c : cells) { auto search = model::cell_map.find(c); if (search == model::cell_map.end()) { - std::stringstream err_msg; - err_msg << "Could not find cell " << c - << " specified on tally filter."; - throw std::runtime_error{err_msg.str()}; + throw std::runtime_error{fmt::format( + "Could not find cell {} specified on tally filter.", c)}; } c = search->second; } @@ -72,7 +70,7 @@ CellFilter::to_statepoint(hid_t filter_group) const std::string CellFilter::text_label(int bin) const { - return "Cell " + std::to_string(model::cells[cells_[bin]]->id_); + return fmt::format("Cell {}", model::cells[cells_[bin]]->id_); } //============================================================================== diff --git a/src/tallies/filter_cell_instance.cpp b/src/tallies/filter_cell_instance.cpp index 3417ca077..cb6381410 100644 --- a/src/tallies/filter_cell_instance.cpp +++ b/src/tallies/filter_cell_instance.cpp @@ -1,8 +1,9 @@ #include "openmc/tallies/filter_cell_instance.h" -#include #include +#include + #include "openmc/capi.h" #include "openmc/cell.h" #include "openmc/error.h" @@ -29,10 +30,8 @@ CellInstanceFilter::from_xml(pugi::xml_node node) gsl::index instance = cells[2*i + 1]; auto search = model::cell_map.find(cell_id); if (search == model::cell_map.end()) { - std::stringstream err_msg; - err_msg << "Could not find cell " << cell_id - << " specified on tally filter."; - throw std::runtime_error{err_msg.str()}; + throw std::runtime_error{fmt::format( + "Could not find cell {} specified on tally filter.", cell_id)}; } gsl::index index = search->second; instances.push_back({index, instance}); @@ -55,9 +54,9 @@ CellInstanceFilter::set_cell_instances(gsl::span instances) Expects(x.index_cell < model::cells.size()); const auto& c {model::cells[x.index_cell]}; if (c->type_ != Fill::MATERIAL) { - throw std::invalid_argument{"Cell " + std::to_string(c->id_) + " is not " - "filled with a material. Only material cells can be used in a cell " - "instance filter."}; + throw std::invalid_argument{fmt::format( + "Cell {} is not filled with a material. Only material cells can be " + "used in a cell instance filter.", c->id_)}; } cell_instances_.push_back(x); map_[x] = cell_instances_.size() - 1; diff --git a/src/tallies/filter_distribcell.cpp b/src/tallies/filter_distribcell.cpp index 9efe31759..7ea9b9b04 100644 --- a/src/tallies/filter_distribcell.cpp +++ b/src/tallies/filter_distribcell.cpp @@ -1,5 +1,7 @@ #include "openmc/tallies/filter_distribcell.h" +#include + #include "openmc/cell.h" #include "openmc/error.h" #include "openmc/geometry_aux.h" // For distribcell_path @@ -19,10 +21,8 @@ DistribcellFilter::from_xml(pugi::xml_node node) // Find index in global cells vector corresponding to cell ID auto search = model::cell_map.find(cells[0]); if (search == model::cell_map.end()) { - std::stringstream err_msg; - err_msg << "Could not find cell " << cell_ - << " specified on tally filter."; - throw std::runtime_error{err_msg.str()}; + throw std::runtime_error{fmt::format( + "Could not find cell {} specified on tally filter.", cell_)}; } this->set_cell(search->second); diff --git a/src/tallies/filter_energy.cpp b/src/tallies/filter_energy.cpp index a2c69c947..1ffea1402 100644 --- a/src/tallies/filter_energy.cpp +++ b/src/tallies/filter_energy.cpp @@ -1,5 +1,7 @@ #include "openmc/tallies/filter_energy.h" +#include + #include "openmc/capi.h" #include "openmc/constants.h" // For F90_NONE #include "openmc/mgxs_interface.h" @@ -90,9 +92,7 @@ EnergyFilter::to_statepoint(hid_t filter_group) const std::string EnergyFilter::text_label(int bin) const { - std::stringstream out; - out << "Incoming Energy [" << bins_[bin] << ", " << bins_[bin+1] << ")"; - return out.str(); + return fmt::format("Incoming Energy [{}, {})", bins_[bin], bins_[bin+1]); } //============================================================================== @@ -119,9 +119,7 @@ EnergyoutFilter::get_all_bins(const Particle* p, TallyEstimator estimator, std::string EnergyoutFilter::text_label(int bin) const { - std::stringstream out; - out << "Outgoing Energy [" << bins_[bin] << ", " << bins_[bin+1] << ")"; - return out.str(); + return fmt::format("Outgoing Energy [{}, {})", bins_[bin], bins_[bin+1]); } //============================================================================== diff --git a/src/tallies/filter_energyfunc.cpp b/src/tallies/filter_energyfunc.cpp index 59cc4fcaa..cecd8306b 100644 --- a/src/tallies/filter_energyfunc.cpp +++ b/src/tallies/filter_energyfunc.cpp @@ -1,8 +1,6 @@ #include "openmc/tallies/filter_energyfunc.h" -#include // for setprecision -#include // for scientific -#include +#include #include "openmc/error.h" #include "openmc/search.h" @@ -82,12 +80,9 @@ EnergyFunctionFilter::to_statepoint(hid_t filter_group) const std::string EnergyFunctionFilter::text_label(int bin) const { - std::stringstream out; - out << std::scientific << std::setprecision(1) - << "Energy Function f" - << "([ " << energy_.front() << ", ..., " << energy_.back() << "]) = " - << "[" << y_.front() << ", ..., " << y_.back() << "]"; - return out.str(); + return fmt::format( + "Energy Function f([{:.1e}, ..., {:.1e}]) = [{:.1e}, ..., {:.1e}]", + energy_.front(), energy_.back(), y_.front(), y_.back()); } //============================================================================== diff --git a/src/tallies/filter_material.cpp b/src/tallies/filter_material.cpp index f0c2bbf8e..2760b028d 100644 --- a/src/tallies/filter_material.cpp +++ b/src/tallies/filter_material.cpp @@ -1,6 +1,6 @@ #include "openmc/tallies/filter_material.h" -#include +#include #include "openmc/capi.h" #include "openmc/material.h" @@ -16,10 +16,8 @@ MaterialFilter::from_xml(pugi::xml_node node) for (auto& m : mats) { auto search = model::material_map.find(m); if (search == model::material_map.end()) { - std::stringstream err_msg; - err_msg << "Could not find material " << m - << " specified on tally filter."; - throw std::runtime_error{err_msg.str()}; + throw std::runtime_error{fmt::format( + "Could not find material {} specified on tally filter.", m)}; } m = search->second; } @@ -69,7 +67,7 @@ MaterialFilter::to_statepoint(hid_t filter_group) const std::string MaterialFilter::text_label(int bin) const { - return "Material " + std::to_string(model::materials[materials_[bin]]->id_); + return fmt::format("Material {}", model::materials[materials_[bin]]->id_); } //============================================================================== diff --git a/src/tallies/filter_mesh.cpp b/src/tallies/filter_mesh.cpp index f80e31dc1..609c3662b 100644 --- a/src/tallies/filter_mesh.cpp +++ b/src/tallies/filter_mesh.cpp @@ -1,6 +1,6 @@ #include "openmc/tallies/filter_mesh.h" -#include +#include #include "openmc/capi.h" #include "openmc/constants.h" @@ -24,9 +24,8 @@ MeshFilter::from_xml(pugi::xml_node node) if (search != model::mesh_map.end()) { set_mesh(search->second); } else{ - std::stringstream err_msg; - err_msg << "Could not find mesh " << id << " specified on tally filter."; - fatal_error(err_msg); + fatal_error(fmt::format( + "Could not find mesh {} specified on tally filter.", id)); } } @@ -61,13 +60,13 @@ MeshFilter::text_label(int bin) const std::vector ijk(n_dim); mesh.get_indices_from_bin(bin, ijk.data()); - std::stringstream out; - out << "Mesh Index (" << ijk[0]; - if (n_dim > 1) out << ", " << ijk[1]; - if (n_dim > 2) out << ", " << ijk[2]; - out << ")"; - - return out.str(); + if (n_dim > 2) { + return fmt::format("Mesh Index ({}, {}, {})", ijk[0], ijk[1], ijk[2]); + } else if (n_dim > 1) { + return fmt::format("Mesh Index ({}, {})", ijk[0], ijk[1]); + } else { + return fmt::format("Mesh Index ({})", ijk[0]) ; + } } void diff --git a/src/tallies/filter_mu.cpp b/src/tallies/filter_mu.cpp index a345fe2bd..2a75089d5 100644 --- a/src/tallies/filter_mu.cpp +++ b/src/tallies/filter_mu.cpp @@ -1,6 +1,6 @@ #include "openmc/tallies/filter_mu.h" -#include +#include #include "openmc/error.h" #include "openmc/search.h" @@ -69,9 +69,7 @@ MuFilter::to_statepoint(hid_t filter_group) const std::string MuFilter::text_label(int bin) const { - std::stringstream out; - out << "Change-in-Angle [" << bins_[bin] << ", " << bins_[bin+1] << ")"; - return out.str(); + return fmt::format("Change-in-Angle [{}, {})", bins_[bin], bins_[bin+1]); } } // namespace openmc diff --git a/src/tallies/filter_polar.cpp b/src/tallies/filter_polar.cpp index f35c1d3cd..367ca738a 100644 --- a/src/tallies/filter_polar.cpp +++ b/src/tallies/filter_polar.cpp @@ -1,6 +1,6 @@ #include "openmc/tallies/filter_polar.h" -#include +#include #include "openmc/constants.h" #include "openmc/error.h" @@ -77,9 +77,7 @@ PolarFilter::to_statepoint(hid_t filter_group) const std::string PolarFilter::text_label(int bin) const { - std::stringstream out; - out << "Polar Angle [" << bins_[bin] << ", " << bins_[bin+1] << ")"; - return out.str(); + return fmt::format("Polar Angle [{}, {})", bins_[bin], bins_[bin+1]); } } // namespace openmc diff --git a/src/tallies/filter_sph_harm.cpp b/src/tallies/filter_sph_harm.cpp index bbeebf5b1..b9852aafd 100644 --- a/src/tallies/filter_sph_harm.cpp +++ b/src/tallies/filter_sph_harm.cpp @@ -2,6 +2,9 @@ #include // For pair +#include +#include + #include "openmc/capi.h" #include "openmc/error.h" #include "openmc/math_functions.h" @@ -36,10 +39,8 @@ SphericalHarmonicsFilter::set_cosine(gsl::cstring_span cosine) } else if (cosine == "particle") { cosine_ = SphericalHarmonicsCosine::particle; } else { - std::stringstream err_msg; - err_msg << "Unrecognized cosine type, \"" << cosine - << "\" in spherical harmonics filter"; - throw std::invalid_argument{err_msg.str()}; + throw std::invalid_argument{fmt::format("Unrecognized cosine type, \"{}\" " + "in spherical harmonics filter", gsl::to_string(cosine))}; } } @@ -88,15 +89,14 @@ SphericalHarmonicsFilter::to_statepoint(hid_t filter_group) const std::string SphericalHarmonicsFilter::text_label(int bin) const { - std::stringstream out; + Expects(bin >= 0 && bin < n_bins_); for (int n = 0; n < order_ + 1; n++) { if (bin < (n + 1) * (n + 1)) { int m = (bin - n*n) - n; - out << "Spherical harmonic expansion, Y" << n << "," << m; - break; + return fmt::format("Spherical harmonic expansion, Y{},{}", n, m); } } - return out.str(); + UNREACHABLE(); } //============================================================================== diff --git a/src/tallies/filter_sptl_legendre.cpp b/src/tallies/filter_sptl_legendre.cpp index 71cc3f399..1b38ec154 100644 --- a/src/tallies/filter_sptl_legendre.cpp +++ b/src/tallies/filter_sptl_legendre.cpp @@ -2,6 +2,8 @@ #include // For pair +#include + #include "openmc/capi.h" #include "openmc/error.h" #include "openmc/math_functions.h" @@ -107,17 +109,13 @@ SpatialLegendreFilter::to_statepoint(hid_t filter_group) const std::string SpatialLegendreFilter::text_label(int bin) const { - std::stringstream out; - out << "Legendre expansion, "; if (axis_ == LegendreAxis::x) { - out << "x"; + return fmt::format("Legendre expansion, x axis, P{}", bin); } else if (axis_ == LegendreAxis::y) { - out << "y"; + return fmt::format("Legendre expansion, y axis, P{}", bin); } else { - out << "z"; + return fmt::format("Legendre expansion, z axis, P{}", bin); } - out << " axis, P" << std::to_string(bin); - return out.str(); } //============================================================================== diff --git a/src/tallies/filter_surface.cpp b/src/tallies/filter_surface.cpp index 5613f039a..203a6a094 100644 --- a/src/tallies/filter_surface.cpp +++ b/src/tallies/filter_surface.cpp @@ -1,6 +1,6 @@ #include "openmc/tallies/filter_surface.h" -#include +#include #include "openmc/error.h" #include "openmc/surface.h" @@ -17,10 +17,8 @@ SurfaceFilter::from_xml(pugi::xml_node node) for (auto& s : surfaces) { auto search = model::surface_map.find(s); if (search == model::surface_map.end()) { - std::stringstream err_msg; - err_msg << "Could not find surface " << s - << " specified on tally filter."; - throw std::runtime_error{err_msg.str()}; + throw std::runtime_error{fmt::format( + "Could not find surface {} specified on tally filter.", s)}; } s = search->second; @@ -75,7 +73,7 @@ SurfaceFilter::to_statepoint(hid_t filter_group) const std::string SurfaceFilter::text_label(int bin) const { - return "Surface " + std::to_string(model::surfaces[surfaces_[bin]]->id_); + return fmt::format("Surface {}", model::surfaces[surfaces_[bin]]->id_); } } // namespace openmc diff --git a/src/tallies/filter_universe.cpp b/src/tallies/filter_universe.cpp index 289e96f56..75c5ebe59 100644 --- a/src/tallies/filter_universe.cpp +++ b/src/tallies/filter_universe.cpp @@ -1,6 +1,6 @@ #include "openmc/tallies/filter_universe.h" -#include +#include #include "openmc/cell.h" #include "openmc/error.h" @@ -16,10 +16,8 @@ UniverseFilter::from_xml(pugi::xml_node node) for (auto& u : universes) { auto search = model::universe_map.find(u); if (search == model::universe_map.end()) { - std::stringstream err_msg; - err_msg << "Could not find universe " << u - << " specified on tally filter."; - throw std::runtime_error{err_msg.str()}; + throw std::runtime_error{fmt::format( + "Could not find universe {} specified on tally filter.", u)}; } u = search->second; } @@ -71,7 +69,7 @@ UniverseFilter::to_statepoint(hid_t filter_group) const std::string UniverseFilter::text_label(int bin) const { - return "Universe " + std::to_string(model::universes[universes_[bin]]->id_); + return fmt::format("Universe {}", model::universes[universes_[bin]]->id_); } } // namespace openmc diff --git a/src/tallies/filter_zernike.cpp b/src/tallies/filter_zernike.cpp index 826d9f508..badd85103 100644 --- a/src/tallies/filter_zernike.cpp +++ b/src/tallies/filter_zernike.cpp @@ -4,6 +4,9 @@ #include #include // For pair +#include +#include + #include "openmc/capi.h" #include "openmc/error.h" #include "openmc/math_functions.h" @@ -58,17 +61,16 @@ ZernikeFilter::to_statepoint(hid_t filter_group) const std::string ZernikeFilter::text_label(int bin) const { - std::stringstream out; + Expects(bin >= 0 && bin < n_bins_); for (int n = 0; n < order_+1; n++) { int last = (n + 1) * (n + 2) / 2; if (bin < last) { int first = last - (n + 1); int m = -n + (bin - first) * 2; - out << "Zernike expansion, Z" << n << "," << m; - break; + return fmt::format("Zernike expansion, Z{},{}", n, m); } } - return out.str(); + UNREACHABLE(); } void diff --git a/src/tallies/tally.cpp b/src/tallies/tally.cpp index 22d0e2e04..b52c09ee3 100644 --- a/src/tallies/tally.cpp +++ b/src/tallies/tally.cpp @@ -28,6 +28,7 @@ #include "openmc/tallies/filter_surface.h" #include "openmc/xml_interface.h" +#include #include "xtensor/xadapt.hpp" #include "xtensor/xbuilder.hpp" // for empty_like #include "xtensor/xview.hpp" @@ -35,7 +36,6 @@ #include // for max #include #include // for size_t -#include #include namespace openmc { @@ -287,8 +287,8 @@ Tally::Tally(pugi::xml_node node) // Determine if filter ID is valid auto it = model::filter_map.find(filter_id); if (it == model::filter_map.end()) { - throw std::runtime_error{"Could not find filter " + std::to_string(filter_id) - + " specified on tally " + std::to_string(id_)}; + throw std::runtime_error{fmt::format( + "Could not find filter {} specified on tally {}", filter_id, id_)}; } // Store the index of the filter @@ -334,8 +334,7 @@ Tally::Tally(pugi::xml_node node) this->set_scores(node); if (!check_for_node(node, "scores")) { - fatal_error("No scores specified on tally " + std::to_string(id_) - + "."); + fatal_error(fmt::format("No scores specified on tally {}.", id_)); } // Check if tally is compatible with particle type @@ -371,9 +370,9 @@ Tally::Tally(pugi::xml_node node) auto pf = dynamic_cast(f); for (auto p : pf->particles()) { if (p != Particle::Type::neutron) { - warning("Particle filter other than NEUTRON used with photon " - "transport turned off. All tallies for particle type " + - std::to_string(static_cast(p)) + " will have no scores"); + warning(fmt::format("Particle filter other than NEUTRON used with " + "photon transport turned off. All tallies for particle type {}" + " will have no scores", static_cast(p))); } } } @@ -386,8 +385,8 @@ Tally::Tally(pugi::xml_node node) // Find the derivative with the given id, and store it's index. auto it = model::tally_deriv_map.find(deriv_id); if (it == model::tally_deriv_map.end()) { - fatal_error("Could not find derivative " + std::to_string(deriv_id) - + " specified on tally " + std::to_string(id_)); + fatal_error(fmt::format( + "Could not find derivative {} specified on tally {}", deriv_id, id_)); } deriv_ = it->second; @@ -403,11 +402,10 @@ Tally::Tally(pugi::xml_node node) || deriv.variable == DerivativeVariable::TEMPERATURE) { for (int i_nuc : nuclides_) { if (has_energyout && i_nuc == -1) { - fatal_error("Error on tally " + std::to_string(id_) - + ": Cannot use a 'nuclide_density' or 'temperature' " - "derivative on a tally with an outgoing energy filter and " - "'total' nuclide rate. Instead, tally each nuclide in the " - "material individually."); + fatal_error(fmt::format("Error on tally {}: Cannot use a " + "'nuclide_density' or 'temperature' derivative on a tally with an " + "outgoing energy filter and 'total' nuclide rate. Instead, tally " + "each nuclide in the material individually.", id_)); // Note that diff tallies with these characteristics would work // correctly if no tally events occur in the perturbed material // (e.g. pertrubing moderator but only tallying fuel), but this @@ -432,11 +430,11 @@ Tally::Tally(pugi::xml_node node) estimator_ = TallyEstimator::ANALOG; } else if (est == "tracklength" || est == "track-length" || est == "pathlength" || est == "path-length") { - // If the estimator was set to an analog/collision estimator, this means - // the tally needs post-collision information + // If the estimator was set to an analog estimator, this means the + // tally needs post-collision information if (estimator_ == TallyEstimator::ANALOG || estimator_ == TallyEstimator::COLLISION) { - throw std::runtime_error{"Cannot use track-length estimator for tally " - + std::to_string(id_)}; + throw std::runtime_error{fmt::format("Cannot use track-length " + "estimator for tally {}", id_)}; } // Set estimator to track-length estimator @@ -446,16 +444,16 @@ Tally::Tally(pugi::xml_node node) // If the estimator was set to an analog estimator, this means the // tally needs post-collision information if (estimator_ == TallyEstimator::ANALOG) { - throw std::runtime_error{"Cannot use collision estimator for tally " + - std::to_string(id_)}; + throw std::runtime_error{fmt::format("Cannot use collision estimator " + "for tally ", id_)}; } // Set estimator to collision estimator estimator_ = TallyEstimator::COLLISION; } else { - throw std::runtime_error{"Invalid estimator '" + est + "' on tally " + - std::to_string(id_)}; + throw std::runtime_error{fmt::format( + "Invalid estimator '{}' on tally {}", est, id_)}; } } } @@ -485,7 +483,7 @@ Tally::set_id(int32_t id) // Make sure no other tally has the same ID if (model::tally_map.find(id) != model::tally_map.end()) { - throw std::runtime_error{"Two tallies have the same ID: " + std::to_string(id)}; + throw std::runtime_error{fmt::format("Two tallies have the same ID: {}", id)}; } // If no ID specified, auto-assign next ID in sequence @@ -542,7 +540,7 @@ void Tally::set_scores(pugi::xml_node node) { if (!check_for_node(node, "scores")) - fatal_error("No scores specified on tally " + std::to_string(id_)); + fatal_error(fmt::format("No scores specified on tally {}", id_)); auto scores = get_node_array(node, "scores"); set_scores(scores); @@ -659,8 +657,9 @@ Tally::set_scores(const std::vector& scores) for (auto it1 = scores_.begin(); it1 != scores_.end(); ++it1) { for (auto it2 = it1 + 1; it2 != scores_.end(); ++it2) { if (*it1 == *it2) - fatal_error("Duplicate score of type \"" + reaction_name(*it1) - + "\" found in tally " + std::to_string(id_)); + fatal_error(fmt::format( + "Duplicate score of type \"{}\" found in tally {}", + reaction_name(*it1), id_)); } } @@ -720,9 +719,8 @@ Tally::set_nuclides(const std::vector& nuclides) } else { auto search = data::nuclide_map.find(nuc); if (search == data::nuclide_map.end()) - fatal_error("Could not find the nuclide " + nuc - + " specified in tally " + std::to_string(id_) - + " in any material"); + fatal_error(fmt::format("Could not find the nuclide {} specified in " + "tally {} in any material", nuc, id_)); nuclides_.push_back(search->second); } } @@ -743,15 +741,12 @@ Tally::init_triggers(pugi::xml_node node) } else if (type_str == "rel_err") { metric = TriggerMetric::relative_error; } else { - std::stringstream msg; - msg << "Unknown trigger type \"" << type_str << "\" in tally " << id_; - fatal_error(msg); + fatal_error(fmt::format("Unknown trigger type \"{}\" in tally {}", + type_str, id_)); } } else { - std::stringstream msg; - msg << "Must specify trigger type for tally " << id_ - << " in tally XML file"; - fatal_error(msg); + fatal_error(fmt::format( + "Must specify trigger type for tally {} in tally XML file", id_)); } // Read the trigger threshold. @@ -759,10 +754,8 @@ Tally::init_triggers(pugi::xml_node node) if (check_for_node(trigger_node, "threshold")) { threshold = std::stod(get_node_value(trigger_node, "threshold")); } else { - std::stringstream msg; - msg << "Must specify trigger threshold for tally " << id_ - << " in tally XML file"; - fatal_error(msg); + fatal_error(fmt::format( + "Must specify trigger threshold for tally {} in tally XML file", id_)); } // Read the trigger scores. @@ -786,10 +779,8 @@ Tally::init_triggers(pugi::xml_node node) if (reaction_name(this->scores_[i_score]) == score_str) break; } if (i_score == this->scores_.size()) { - std::stringstream msg; - msg << "Could not find the score \"" << score_str << "\" in tally " - << id_ << " but it was listed in a trigger on that tally"; - fatal_error(msg); + fatal_error(fmt::format("Could not find the score \"{}\" in tally " + "{} but it was listed in a trigger on that tally", score_str, id_)); } triggers_.push_back({metric, threshold, i_score}); } @@ -1078,7 +1069,7 @@ openmc_get_tally_index(int32_t id, int32_t* index) { auto it = model::tally_map.find(id); if (it == model::tally_map.end()) { - set_errmsg("No tally exists with ID=" + std::to_string(id) + "."); + set_errmsg(fmt::format("No tally exists with ID={}.", id)); return OPENMC_E_INVALID_ID; } @@ -1182,9 +1173,7 @@ openmc_tally_set_type(int32_t index, const char* type) } else if (strcmp(type, "surface") == 0) { model::tallies[index]->type_ = TallyType::SURFACE; } else { - std::stringstream errmsg; - errmsg << "Unknown tally type: " << type; - set_errmsg(errmsg); + set_errmsg(fmt::format("Unknown tally type: {}", type)); return OPENMC_E_INVALID_ARGUMENT; } diff --git a/src/tallies/trigger.cpp b/src/tallies/trigger.cpp index 7fb3475c9..405650b2b 100644 --- a/src/tallies/trigger.cpp +++ b/src/tallies/trigger.cpp @@ -1,9 +1,10 @@ #include "openmc/tallies/trigger.h" #include -#include #include // for std::pair +#include + #include "openmc/capi.h" #include "openmc/constants.h" #include "openmc/error.h" @@ -170,13 +171,14 @@ check_triggers() // At least one trigger is unsatisfied. Let the user know which one. simulation::satisfy_triggers = false; - std::stringstream msg; - msg << "Triggers unsatisfied, max unc./thresh. is "; + std::string msg; if (keff_ratio >= tally_ratio) { - msg << keff_ratio << " for eigenvalue"; + msg = fmt::format("Triggers unsatisfied, max unc./thresh. is {} for " + "eigenvalue", keff_ratio); } else { - msg << tally_ratio << " for " << reaction_name(score) << " in tally " - << tally_id; + msg = fmt::format( + "Triggers unsatisfied, max unc./thresh. is {} for {} in tally {}", + tally_ratio, reaction_name(score), tally_id); } write_message(msg, 7); @@ -189,10 +191,10 @@ check_triggers() auto n_pred_batches = static_cast(n_active * max_ratio * max_ratio) + settings::n_inactive + 1; - std::stringstream msg; - msg << "The estimated number of batches is " << n_pred_batches; + std::string msg = fmt::format("The estimated number of batches is {}", + n_pred_batches); if (n_pred_batches > settings::n_max_batches) { - msg << " --- greater than max batches"; + msg.append(" --- greater than max batches"); warning(msg); } else { write_message(msg, 7); diff --git a/src/thermal.cpp b/src/thermal.cpp index ee06ed8aa..d7bdb150c 100644 --- a/src/thermal.cpp +++ b/src/thermal.cpp @@ -2,8 +2,8 @@ #include // for sort, move, min, max, find #include // for round, sqrt, abs -#include // for stringstream +#include #include "xtensor/xarray.hpp" #include "xtensor/xbuilder.hpp" #include "xtensor/xmath.hpp" @@ -88,10 +88,8 @@ ThermalScattering::ThermalScattering(hid_t group, const std::vector& tem temps_to_read.push_back(std::round(temp_actual)); } } else { - std::stringstream msg; - msg << "Nuclear data library does not contain cross sections for " - << name_ << " at or near " << std::round(T) << " K."; - fatal_error(msg); + fatal_error(fmt::format("Nuclear data library does not contain cross " + "sections for {} at or near {} K.", name_, std::round(T))); } } break; @@ -115,10 +113,8 @@ ThermalScattering::ThermalScattering(hid_t group, const std::vector& tem } } if (!found) { - std::stringstream msg; - msg << "Nuclear data library does not contain cross sections for " - << name_ << " at temperatures that bound " << std::round(T) << " K."; - fatal_error(msg); + fatal_error(fmt::format("Nuclear data library does not contain cross " + "sections for {} at temperatures that bound {} K.", name_, std::round(T))); } } } @@ -132,7 +128,7 @@ ThermalScattering::ThermalScattering(hid_t group, const std::vector& tem for (auto T : temps_to_read) { // Get temperature as a string - std::string temp_str = std::to_string(T) + "K"; + std::string temp_str = fmt::format("{}K", T); // Read exact temperature value double kT; diff --git a/src/track_output.cpp b/src/track_output.cpp index 5e071be01..3b358621b 100644 --- a/src/track_output.cpp +++ b/src/track_output.cpp @@ -6,10 +6,10 @@ #include "openmc/settings.h" #include "openmc/simulation.h" +#include #include "xtensor/xtensor.hpp" #include // for size_t -#include #include #include @@ -35,9 +35,9 @@ void write_particle_track(Particle& p) void finalize_particle_track(Particle& p) { - std::stringstream filename; - filename << settings::path_output << "track_" << simulation::current_batch - << '_' << simulation::current_gen << '_' << p.id_ << ".h5"; + std::string filename = fmt::format("{}track_{}_{}_{}.h5", + settings::path_output, simulation::current_batch, simulation::current_gen, + p.id_); // Determine number of coordinates for each particle std::vector n_coords; @@ -47,7 +47,7 @@ void finalize_particle_track(Particle& p) #pragma omp critical (FinalizeParticleTrack) { - hid_t file_id = file_open(filename.str().c_str(), 'w'); + hid_t file_id = file_open(filename, 'w'); write_attribute(file_id, "filetype", "track"); write_attribute(file_id, "version", VERSION_TRACK); write_attribute(file_id, "n_particles", p.tracks_.size()); @@ -61,7 +61,7 @@ void finalize_particle_track(Particle& p) data(j, 1) = t[j].y; data(j, 2) = t[j].z; } - std::string name = "coordinates_" + std::to_string(i); + std::string name = fmt::format("coordinates_{}", i); write_dataset(file_id, name.c_str(), data); } file_close(file_id); diff --git a/src/volume_calc.cpp b/src/volume_calc.cpp index 10ea6bca4..f2f934833 100644 --- a/src/volume_calc.cpp +++ b/src/volume_calc.cpp @@ -15,6 +15,7 @@ #include "openmc/timer.h" #include "openmc/xml_interface.h" +#include #ifdef _OPENMP #include #endif @@ -23,7 +24,6 @@ #include // for copy #include // for pow, sqrt -#include #include namespace openmc { @@ -66,9 +66,8 @@ VolumeCalculation::VolumeCalculation(pugi::xml_node node) threshold_ = std::stod(get_node_value(threshold_node, "threshold")); if (threshold_ <= 0.0) { - std::stringstream msg; - msg << "Invalid error threshold " << threshold_ << " provided for a volume calculation."; - fatal_error(msg); + fatal_error(fmt::format("Invalid error threshold {} provided for a " + "volume calculation.", threshold_)); } std::string tmp = get_node_value(threshold_node, "type"); @@ -79,9 +78,8 @@ VolumeCalculation::VolumeCalculation(pugi::xml_node node) } else if ( tmp == "rel_err") { trigger_type_ = TriggerMetric::relative_error; } else { - std::stringstream msg; - msg << "Invalid volume calculation trigger type '" << tmp << "' provided."; - fatal_error(msg); + fatal_error(fmt::format( + "Invalid volume calculation trigger type '{}' provided.", tmp)); } } @@ -394,8 +392,7 @@ void VolumeCalculation::to_hdf5(const std::string& filename, for (int i = 0; i < domain_ids_.size(); ++i) { - hid_t group_id = create_group(file_id, "domain_" - + std::to_string(domain_ids_[i])); + hid_t group_id = create_group(file_id, fmt::format("domain_{}", domain_ids_[i])); // Write volume for domain const auto& result {results[i]}; @@ -468,7 +465,7 @@ int openmc_calculate_volumes() { for (int i = 0; i < model::volume_calcs.size(); ++i) { if (mpi::master) { - write_message("Running volume calculation " + std::to_string(i+1) + "...", 4); + write_message(fmt::format("Running volume calculation {}...", i + 1), 4); } // Run volume calculation @@ -487,15 +484,13 @@ int openmc_calculate_volumes() { // Display domain volumes for (int j = 0; j < vol_calc.domain_ids_.size(); j++) { - std::stringstream msg; - msg << domain_type << vol_calc.domain_ids_[j] << ": " << - results[j].volume[0] << " +/- " << results[j].volume[1] << " cm^3"; - write_message(msg, 4); + write_message(fmt::format("{}{}: {} +/- {} cm^3", domain_type, + vol_calc.domain_ids_[j], results[j].volume[0], results[j].volume[1]), 4); } // Write volumes to HDF5 file - std::string filename = settings::path_output + "volume_" - + std::to_string(i+1) + ".h5"; + std::string filename = fmt::format("{}volume_{}.h5", + settings::path_output, i + 1); vol_calc.to_hdf5(filename, results); } @@ -504,8 +499,7 @@ int openmc_calculate_volumes() { // Show elapsed time time_volume.stop(); if (mpi::master) { - write_message("Elapsed time: " + std::to_string(time_volume.elapsed()) - + " s", 6); + write_message(fmt::format("Elapsed time: {} s", time_volume.elapsed()), 6); } return 0; diff --git a/src/wmp.cpp b/src/wmp.cpp index f514b9e83..d441de7db 100644 --- a/src/wmp.cpp +++ b/src/wmp.cpp @@ -6,8 +6,9 @@ #include "openmc/math_functions.h" #include "openmc/nuclide.h" +#include + #include -#include namespace openmc { @@ -201,15 +202,14 @@ void check_wmp_version(hid_t file) std::array version; read_attribute(file, "version", version); if (version[0] != WMP_VERSION[0]) { - std::stringstream msg; - msg << "WMP data format uses version " << version[0] << "." << - version[1] << " whereas your installation of OpenMC expects version " - << WMP_VERSION[0] << ".x data."; - fatal_error(msg); + fatal_error(fmt::format( + "WMP data format uses version {}.{} whereas your installation of " + "OpenMC expects version {}.x data.", + version[0], version[1], WMP_VERSION[0])); } } else { - fatal_error("WMP data does not indicate a version. Your installation of " - "OpenMC expects version " + std::to_string(WMP_VERSION[0]) + ".x data."); + fatal_error(fmt::format("WMP data does not indicate a version. Your " + "installation of OpenMC expects version {}x data.", WMP_VERSION[0])); } } diff --git a/src/xml_interface.cpp b/src/xml_interface.cpp index 60ae7ffca..cab7fd2df 100644 --- a/src/xml_interface.cpp +++ b/src/xml_interface.cpp @@ -1,10 +1,9 @@ #include "openmc/xml_interface.h" -#include // for transform -#include +#include #include "openmc/error.h" - +#include "openmc/string_utils.h" namespace openmc { @@ -19,17 +18,13 @@ get_node_value(pugi::xml_node node, const char* name, bool lowercase, } else if (node.child(name)) { value_char = node.child_value(name); } else { - std::stringstream err_msg; - err_msg << "Node \"" << name << "\" is not a member of the \"" - << node.name() << "\" XML node"; - fatal_error(err_msg); + fatal_error(fmt::format( + "Node \"{}\" is not a member of the \"{}\" XML node", name, node.name())); } std::string value {value_char}; // Convert to lower-case if needed - if (lowercase) { - std::transform(value.begin(), value.end(), value.begin(), ::tolower); - } + if (lowercase) to_lower(value); // Strip leading/trailing whitespace if needed if (strip) { @@ -48,10 +43,8 @@ get_node_value_bool(pugi::xml_node node, const char* name) } else if (node.child(name)) { return node.child(name).text().as_bool(); } else { - std::stringstream err_msg; - err_msg << "Node \"" << name << "\" is not a member of the \"" - << node.name() << "\" XML node"; - fatal_error(err_msg); + fatal_error(fmt::format( + "Node \"{}\" is not a member of the \"{}\" XML node", name, node.name())); } return false; } From cc919d25c5954e2e977099c30a8ac9cbc270db5c Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 7 Feb 2020 14:55:11 -0600 Subject: [PATCH 020/166] Use fmt/core.h header to reduce compile time --- src/cell.cpp | 3 +-- src/dagmc.cpp | 2 +- src/geometry.cpp | 2 +- src/geometry_aux.cpp | 2 +- src/hdf5_interface.cpp | 2 +- src/initialize.cpp | 4 ++-- src/lattice.cpp | 2 +- src/mgxs.cpp | 2 +- src/output.cpp | 2 +- src/particle.cpp | 4 ++-- src/physics.cpp | 2 +- src/physics_mg.cpp | 4 ++-- src/plot.cpp | 2 +- src/reaction.cpp | 2 +- src/secondary_uncorrelated.cpp | 2 +- src/settings.cpp | 2 +- src/source.cpp | 2 +- src/state_point.cpp | 2 +- src/surface.cpp | 2 +- src/tallies/derivative.cpp | 2 +- src/tallies/filter_azimuthal.cpp | 2 +- src/tallies/filter_cell.cpp | 2 +- src/tallies/filter_cell_instance.cpp | 2 +- src/tallies/filter_distribcell.cpp | 2 +- src/tallies/filter_energy.cpp | 2 +- src/tallies/filter_energyfunc.cpp | 2 +- src/tallies/filter_material.cpp | 2 +- src/tallies/filter_mesh.cpp | 2 +- src/tallies/filter_mu.cpp | 2 +- src/tallies/filter_polar.cpp | 2 +- src/tallies/filter_sph_harm.cpp | 2 +- src/tallies/filter_sptl_legendre.cpp | 2 +- src/tallies/filter_surface.cpp | 2 +- src/tallies/filter_universe.cpp | 2 +- src/tallies/filter_zernike.cpp | 2 +- src/tallies/tally.cpp | 2 +- src/tallies/trigger.cpp | 2 +- src/thermal.cpp | 2 +- src/track_output.cpp | 2 +- src/volume_calc.cpp | 2 +- src/wmp.cpp | 2 +- src/xml_interface.cpp | 2 +- 42 files changed, 45 insertions(+), 46 deletions(-) diff --git a/src/cell.cpp b/src/cell.cpp index d09cde948..cd6199b75 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -9,9 +9,8 @@ #include #include -#include +#include #include -#include #include "openmc/capi.h" #include "openmc/constants.h" diff --git a/src/dagmc.cpp b/src/dagmc.cpp index 3f16799a5..59435f245 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -15,7 +15,7 @@ #include "uwuw.hpp" #include "dagmcmetadata.hpp" #endif -#include +#include #include #include diff --git a/src/geometry.cpp b/src/geometry.cpp index d2012d901..055f0ec7f 100644 --- a/src/geometry.cpp +++ b/src/geometry.cpp @@ -2,7 +2,7 @@ #include -#include +#include #include #include "openmc/cell.h" diff --git a/src/geometry_aux.cpp b/src/geometry_aux.cpp index 0df1198b1..ae6d894aa 100644 --- a/src/geometry_aux.cpp +++ b/src/geometry_aux.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include #include "openmc/cell.h" diff --git a/src/hdf5_interface.cpp b/src/hdf5_interface.cpp index f207f53b7..1fcc97f3c 100644 --- a/src/hdf5_interface.cpp +++ b/src/hdf5_interface.cpp @@ -7,7 +7,7 @@ #include "xtensor/xtensor.hpp" #include "xtensor/xarray.hpp" -#include +#include #include "hdf5.h" #include "hdf5_hl.h" diff --git a/src/initialize.cpp b/src/initialize.cpp index a5b516f68..10d86ee80 100644 --- a/src/initialize.cpp +++ b/src/initialize.cpp @@ -9,7 +9,7 @@ #ifdef _OPENMP #include #endif -#include +#include #include "openmc/capi.h" #include "openmc/constants.h" @@ -134,7 +134,7 @@ parse_command_line(int argc, char* argv[]) } else if (arg == "-n" || arg == "--particles") { i += 1; settings::n_particles = std::stoll(argv[i]); - + } else if (arg == "-e" || arg == "--event") { settings::event_based = true; diff --git a/src/lattice.cpp b/src/lattice.cpp index a8dd23aa8..8f88d8c59 100644 --- a/src/lattice.cpp +++ b/src/lattice.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include "openmc/cell.h" #include "openmc/error.h" diff --git a/src/mgxs.cpp b/src/mgxs.cpp index 094361dcb..163954caa 100644 --- a/src/mgxs.cpp +++ b/src/mgxs.cpp @@ -9,7 +9,7 @@ #include #endif -#include +#include #include "xtensor/xmath.hpp" #include "xtensor/xsort.hpp" #include "xtensor/xadapt.hpp" diff --git a/src/output.cpp b/src/output.cpp index ecfdb8a95..d98cfd4a1 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -11,7 +11,7 @@ #include #include // for pair -#include +#include #include #ifdef _OPENMP #include diff --git a/src/particle.cpp b/src/particle.cpp index 276fc197d..24bec111c 100644 --- a/src/particle.cpp +++ b/src/particle.cpp @@ -3,7 +3,7 @@ #include // copy, min #include // log, abs, copysign -#include +#include #include "openmc/bank.h" #include "openmc/capi.h" @@ -236,7 +236,7 @@ Particle::event_advance() score_track_derivative(this, distance); } } - + void Particle::event_cross_surface() { diff --git a/src/physics.cpp b/src/physics.cpp index a0f929125..1065c200b 100644 --- a/src/physics.cpp +++ b/src/physics.cpp @@ -21,7 +21,7 @@ #include "openmc/thermal.h" #include "openmc/tallies/tally.h" -#include +#include #include // for max, min, max_element #include // for sqrt, exp, log, abs, copysign diff --git a/src/physics_mg.cpp b/src/physics_mg.cpp index 526a21439..f63a48d2a 100644 --- a/src/physics_mg.cpp +++ b/src/physics_mg.cpp @@ -2,7 +2,7 @@ #include -#include +#include #include "xtensor/xarray.hpp" #include "openmc/bank.h" @@ -153,7 +153,7 @@ create_fission_sites(Particle* p) // We add 1 to the delayed_group bc in MG, -1 is prompt, but in the rest // of the code, 0 is prompt. site.delayed_group = dg + 1; - + // Store fission site in bank if (use_fission_bank) { int64_t idx = simulation::fission_bank.thread_safe_append(site); diff --git a/src/plot.cpp b/src/plot.cpp index 0ff785712..bc94b3ad6 100644 --- a/src/plot.cpp +++ b/src/plot.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include #include "xtensor/xview.hpp" diff --git a/src/reaction.cpp b/src/reaction.cpp index ff22540e1..ce0090752 100644 --- a/src/reaction.cpp +++ b/src/reaction.cpp @@ -3,7 +3,7 @@ #include #include // for move -#include +#include #include "openmc/constants.h" #include "openmc/hdf5_interface.h" diff --git a/src/secondary_uncorrelated.cpp b/src/secondary_uncorrelated.cpp index 320761916..a1aa8ca0b 100644 --- a/src/secondary_uncorrelated.cpp +++ b/src/secondary_uncorrelated.cpp @@ -2,7 +2,7 @@ #include // for string -#include +#include #include "openmc/error.h" #include "openmc/hdf5_interface.h" diff --git a/src/settings.cpp b/src/settings.cpp index 9bdfd7756..5bd53de25 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -4,7 +4,7 @@ #include // for numeric_limits #include -#include +#include #ifdef _OPENMP #include #endif diff --git a/src/source.cpp b/src/source.cpp index 631520854..b15a15d23 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -2,7 +2,7 @@ #include // for move -#include +#include #include "xtensor/xadapt.hpp" #include "openmc/bank.h" diff --git a/src/state_point.cpp b/src/state_point.cpp index 706eb17ef..503b1354e 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include "xtensor/xbuilder.hpp" // for empty_like #include "xtensor/xview.hpp" diff --git a/src/surface.cpp b/src/surface.cpp index b1ba2eb05..a2c54260a 100644 --- a/src/surface.cpp +++ b/src/surface.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include "openmc/error.h" #include "openmc/dagmc.h" diff --git a/src/tallies/derivative.cpp b/src/tallies/derivative.cpp index 483008340..b749d6906 100644 --- a/src/tallies/derivative.cpp +++ b/src/tallies/derivative.cpp @@ -7,7 +7,7 @@ #include "openmc/tallies/tally.h" #include "openmc/xml_interface.h" -#include +#include template class std::vector; diff --git a/src/tallies/filter_azimuthal.cpp b/src/tallies/filter_azimuthal.cpp index 4d18e5974..a7c29b2db 100644 --- a/src/tallies/filter_azimuthal.cpp +++ b/src/tallies/filter_azimuthal.cpp @@ -2,7 +2,7 @@ #include -#include +#include #include "openmc/constants.h" #include "openmc/error.h" diff --git a/src/tallies/filter_cell.cpp b/src/tallies/filter_cell.cpp index 86e78df91..76a80f0d5 100644 --- a/src/tallies/filter_cell.cpp +++ b/src/tallies/filter_cell.cpp @@ -1,6 +1,6 @@ #include "openmc/tallies/filter_cell.h" -#include +#include #include "openmc/capi.h" #include "openmc/cell.h" diff --git a/src/tallies/filter_cell_instance.cpp b/src/tallies/filter_cell_instance.cpp index cb6381410..e3d7e02b2 100644 --- a/src/tallies/filter_cell_instance.cpp +++ b/src/tallies/filter_cell_instance.cpp @@ -2,7 +2,7 @@ #include -#include +#include #include "openmc/capi.h" #include "openmc/cell.h" diff --git a/src/tallies/filter_distribcell.cpp b/src/tallies/filter_distribcell.cpp index 7ea9b9b04..43e68a8f9 100644 --- a/src/tallies/filter_distribcell.cpp +++ b/src/tallies/filter_distribcell.cpp @@ -1,6 +1,6 @@ #include "openmc/tallies/filter_distribcell.h" -#include +#include #include "openmc/cell.h" #include "openmc/error.h" diff --git a/src/tallies/filter_energy.cpp b/src/tallies/filter_energy.cpp index 1ffea1402..1138dabe2 100644 --- a/src/tallies/filter_energy.cpp +++ b/src/tallies/filter_energy.cpp @@ -1,6 +1,6 @@ #include "openmc/tallies/filter_energy.h" -#include +#include #include "openmc/capi.h" #include "openmc/constants.h" // For F90_NONE diff --git a/src/tallies/filter_energyfunc.cpp b/src/tallies/filter_energyfunc.cpp index cecd8306b..8c4e5609d 100644 --- a/src/tallies/filter_energyfunc.cpp +++ b/src/tallies/filter_energyfunc.cpp @@ -1,6 +1,6 @@ #include "openmc/tallies/filter_energyfunc.h" -#include +#include #include "openmc/error.h" #include "openmc/search.h" diff --git a/src/tallies/filter_material.cpp b/src/tallies/filter_material.cpp index 2760b028d..8311fc73b 100644 --- a/src/tallies/filter_material.cpp +++ b/src/tallies/filter_material.cpp @@ -1,6 +1,6 @@ #include "openmc/tallies/filter_material.h" -#include +#include #include "openmc/capi.h" #include "openmc/material.h" diff --git a/src/tallies/filter_mesh.cpp b/src/tallies/filter_mesh.cpp index 609c3662b..6c2cc8bd1 100644 --- a/src/tallies/filter_mesh.cpp +++ b/src/tallies/filter_mesh.cpp @@ -1,6 +1,6 @@ #include "openmc/tallies/filter_mesh.h" -#include +#include #include "openmc/capi.h" #include "openmc/constants.h" diff --git a/src/tallies/filter_mu.cpp b/src/tallies/filter_mu.cpp index 2a75089d5..f874488b1 100644 --- a/src/tallies/filter_mu.cpp +++ b/src/tallies/filter_mu.cpp @@ -1,6 +1,6 @@ #include "openmc/tallies/filter_mu.h" -#include +#include #include "openmc/error.h" #include "openmc/search.h" diff --git a/src/tallies/filter_polar.cpp b/src/tallies/filter_polar.cpp index 367ca738a..c81410750 100644 --- a/src/tallies/filter_polar.cpp +++ b/src/tallies/filter_polar.cpp @@ -1,6 +1,6 @@ #include "openmc/tallies/filter_polar.h" -#include +#include #include "openmc/constants.h" #include "openmc/error.h" diff --git a/src/tallies/filter_sph_harm.cpp b/src/tallies/filter_sph_harm.cpp index b9852aafd..fe014d0f4 100644 --- a/src/tallies/filter_sph_harm.cpp +++ b/src/tallies/filter_sph_harm.cpp @@ -2,7 +2,7 @@ #include // For pair -#include +#include #include #include "openmc/capi.h" diff --git a/src/tallies/filter_sptl_legendre.cpp b/src/tallies/filter_sptl_legendre.cpp index 1b38ec154..6f060ed44 100644 --- a/src/tallies/filter_sptl_legendre.cpp +++ b/src/tallies/filter_sptl_legendre.cpp @@ -2,7 +2,7 @@ #include // For pair -#include +#include #include "openmc/capi.h" #include "openmc/error.h" diff --git a/src/tallies/filter_surface.cpp b/src/tallies/filter_surface.cpp index 203a6a094..72061becb 100644 --- a/src/tallies/filter_surface.cpp +++ b/src/tallies/filter_surface.cpp @@ -1,6 +1,6 @@ #include "openmc/tallies/filter_surface.h" -#include +#include #include "openmc/error.h" #include "openmc/surface.h" diff --git a/src/tallies/filter_universe.cpp b/src/tallies/filter_universe.cpp index 75c5ebe59..ac3903041 100644 --- a/src/tallies/filter_universe.cpp +++ b/src/tallies/filter_universe.cpp @@ -1,6 +1,6 @@ #include "openmc/tallies/filter_universe.h" -#include +#include #include "openmc/cell.h" #include "openmc/error.h" diff --git a/src/tallies/filter_zernike.cpp b/src/tallies/filter_zernike.cpp index badd85103..eab76cbc8 100644 --- a/src/tallies/filter_zernike.cpp +++ b/src/tallies/filter_zernike.cpp @@ -4,7 +4,7 @@ #include #include // For pair -#include +#include #include #include "openmc/capi.h" diff --git a/src/tallies/tally.cpp b/src/tallies/tally.cpp index b52c09ee3..b63128dc0 100644 --- a/src/tallies/tally.cpp +++ b/src/tallies/tally.cpp @@ -28,7 +28,7 @@ #include "openmc/tallies/filter_surface.h" #include "openmc/xml_interface.h" -#include +#include #include "xtensor/xadapt.hpp" #include "xtensor/xbuilder.hpp" // for empty_like #include "xtensor/xview.hpp" diff --git a/src/tallies/trigger.cpp b/src/tallies/trigger.cpp index 405650b2b..0665828fd 100644 --- a/src/tallies/trigger.cpp +++ b/src/tallies/trigger.cpp @@ -3,7 +3,7 @@ #include #include // for std::pair -#include +#include #include "openmc/capi.h" #include "openmc/constants.h" diff --git a/src/thermal.cpp b/src/thermal.cpp index d7bdb150c..67ca8905f 100644 --- a/src/thermal.cpp +++ b/src/thermal.cpp @@ -3,7 +3,7 @@ #include // for sort, move, min, max, find #include // for round, sqrt, abs -#include +#include #include "xtensor/xarray.hpp" #include "xtensor/xbuilder.hpp" #include "xtensor/xmath.hpp" diff --git a/src/track_output.cpp b/src/track_output.cpp index 3b358621b..314e43351 100644 --- a/src/track_output.cpp +++ b/src/track_output.cpp @@ -6,7 +6,7 @@ #include "openmc/settings.h" #include "openmc/simulation.h" -#include +#include #include "xtensor/xtensor.hpp" #include // for size_t diff --git a/src/volume_calc.cpp b/src/volume_calc.cpp index f2f934833..8cae7743d 100644 --- a/src/volume_calc.cpp +++ b/src/volume_calc.cpp @@ -15,7 +15,7 @@ #include "openmc/timer.h" #include "openmc/xml_interface.h" -#include +#include #ifdef _OPENMP #include #endif diff --git a/src/wmp.cpp b/src/wmp.cpp index d441de7db..96e1d5641 100644 --- a/src/wmp.cpp +++ b/src/wmp.cpp @@ -6,7 +6,7 @@ #include "openmc/math_functions.h" #include "openmc/nuclide.h" -#include +#include #include diff --git a/src/xml_interface.cpp b/src/xml_interface.cpp index cab7fd2df..a715dfae2 100644 --- a/src/xml_interface.cpp +++ b/src/xml_interface.cpp @@ -1,6 +1,6 @@ #include "openmc/xml_interface.h" -#include +#include #include "openmc/error.h" #include "openmc/string_utils.h" From 8854286e66e3cf12abd34ecf8c0341f6b6024391 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Mon, 10 Feb 2020 16:05:42 -0500 Subject: [PATCH 021/166] Raise helpful error for large banks in openmc.lib --- openmc/lib/core.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/openmc/lib/core.py b/openmc/lib/core.py index dcd896fa6..6a2a7cdea 100644 --- a/openmc/lib/core.py +++ b/openmc/lib/core.py @@ -336,9 +336,23 @@ def source_bank(): n = c_int64() _dll.openmc_source_bank(ptr, n) - # Convert to numpy array with appropriate datatype - bank_dtype = np.dtype(_Bank) - return as_array(ptr, (n.value,)).view(bank_dtype) + try: + # Convert to numpy array with appropriate datatype + bank_dtype = np.dtype(_Bank) + return as_array(ptr, (n.value,)).view(bank_dtype) + + except ValueError as err: + # If a known numpy error was raised (github.com/numpy/numpy/issues + # /14214), re-raise with a more helpful error message. + if len(err.args) == 0: + raise err + if err.args[0].startswith('invalid shape in fixed-type tuple'): + raise ValueError('The source bank is too large to access via ' + 'openmc.lib with this version of numpy. Use a different ' + 'version of numpy or reduce the bank size (fewer particles ' + 'per MPI process) so that it is smaller than 2 GB.') from err + else: + raise err def statepoint_write(filename=None, write_source=True): From 32cc4c8d434018d9d14fa90bd09b628bf668701f Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 10 Feb 2020 16:01:30 -0600 Subject: [PATCH 022/166] Support timestep units in Integrator.__init__ --- openmc/deplete/abc.py | 82 ++++++++++++++++++++------- openmc/deplete/integrators.py | 104 ++++++++++++++++++++++++++-------- 2 files changed, 142 insertions(+), 44 deletions(-) diff --git a/openmc/deplete/abc.py b/openmc/deplete/abc.py index 10976dd0e..a9640852c 100644 --- a/openmc/deplete/abc.py +++ b/openmc/deplete/abc.py @@ -31,6 +31,9 @@ __all__ = [ "Integrator", "SIIntegrator", "DepSystemSolver"] +_SECONDS_PER_DAY = 24*60*60 +_SECONDS_PER_YEAR = 365.25*24*60*60 + OperatorResult = namedtuple('OperatorResult', ['k', 'rates']) OperatorResult.__doc__ = """\ Result of applying transport operator @@ -597,9 +600,11 @@ class Integrator(ABC): ---------- operator : openmc.deplete.TransportOperator Operator to perform transport simulations - timesteps : iterable of float - Array of timesteps in units of [s]. Note that values are not - cumulative. + timesteps : iterable of float or iterable of tuple + Array of timesteps. Note that values are not cumulative. The units are + specified by the `timestep_units` argument when `timesteps` is an + iterable of float. Alternatively, units can be specified for each step + by passing an iterable of (value, unit) tuples. power : float or iterable of float, optional Power of the reactor in [W]. A single value indicates that the power is constant over all timesteps. An iterable @@ -612,6 +617,11 @@ class Integrator(ABC): Power density of the reactor in [W/gHM]. It is multiplied by initial heavy metal inventory to get total power if ``power`` is not speficied. + timestep_units : {'s', 'd', 'a', 'MWd/kg'} + Units for values specified in the `timesteps` argument. 's' means + seconds, 'd' means days, 'a' means years, and 'MWd/kg' indicates that + the values are given in burnup (MW-d of energy deposited per kilogram + of heavy metal). Attributes ---------- @@ -625,7 +635,8 @@ class Integrator(ABC): Power of the reactor in [W] for each interval in :attr:`timesteps` """ - def __init__(self, operator, timesteps, power=None, power_density=None): + def __init__(self, operator, timesteps, power=None, power_density=None, + timestep_units='s'): # Check number of stages previously used if operator.prev_res is not None: res = operator.prev_res[-1] @@ -638,27 +649,51 @@ class Integrator(ABC): self._num_stages)) self.operator = operator self.chain = operator.chain - if not isinstance(timesteps, Iterable): - self.timesteps = [timesteps] - else: - self.timesteps = timesteps + + # Determine power and normalize units to W + mass = operator.heavy_metal if power is None: if power_density is None: raise ValueError("Either power or power density must be set") if not isinstance(power_density, Iterable): - power = power_density * operator.heavy_metal + power = power_density * mass else: - power = [p * operator.heavy_metal for p in power_density] - + power = [p*mass for p in power_density] if not isinstance(power, Iterable): # Ensure that power is single value if that is the case - power = [power] * len(self.timesteps) - elif len(power) != len(self.timesteps): + power = [power] * len(timesteps) + + if len(power) != len(timesteps): raise ValueError( "Number of time steps != number of powers. {} vs {}".format( - len(self.timesteps), len(power))) + len(timesteps), len(power))) - self.power = power + # Get list of times / units + if isinstance(timesteps[0], Iterable): + times, units = zip(*timesteps) + else: + times = timesteps + units = [timestep_units] * len(timesteps) + + # Determine number of seconds for each timestep + seconds = [] + for time, unit, watts in zip(times, units, power): + if unit == 's': + seconds.append(time) + elif unit in ('d', 'day'): + seconds.append(time*_SECONDS_PER_DAY) + elif unit in ('a', 'yr', 'year'): + seconds.append(time*_SECONDS_PER_YEAR) + elif unit.lower() == 'mwd/kg': + watt_days_per_kg = 1e6*time + kilograms = 1e-3*mass + days = watt_days_per_kg * kilograms / watts + seconds.append(days*_SECONDS_PER_DAY) + else: + raise ValueError("Invalid timestep unit: {}".format(unit)) + + self.timesteps = asarray(seconds) + self.power = asarray(power) @abstractmethod def __call__(self, conc, rates, dt, power, i): @@ -772,9 +807,11 @@ class SIIntegrator(Integrator): ---------- operator : openmc.deplete.TransportOperator The operator object to simulate on. - timesteps : iterable of float - Array of timesteps in units of [s]. Note that values are not - cumulative. + timesteps : iterable of float or iterable of tuple + Array of timesteps. Note that values are not cumulative. The units are + specified by the `timestep_units` argument when `timesteps` is an + iterable of float. Alternatively, units can be specified for each step + by passing an iterable of (value, unit) tuples. power : float or iterable of float, optional Power of the reactor in [W]. A single value indicates that the power is constant over all timesteps. An iterable @@ -787,6 +824,11 @@ class SIIntegrator(Integrator): Power density of the reactor in [W/gHM]. It is multiplied by initial heavy metal inventory to get total power if ``power`` is not speficied. + timestep_units : {'s', 'd', 'a', 'MWd/kg'} + Units for values specified in the `timesteps` argument. 's' means + seconds, 'd' means days, 'a' means years, and 'MWd/kg' indicates that + the values are given in burnup (MW-d of energy deposited per kilogram + of heavy metal). n_steps : int, optional Number of stochastic iterations per depletion interval. Must be greater than zero. Default : 10 @@ -805,10 +847,10 @@ class SIIntegrator(Integrator): Number of stochastic iterations per depletion interval """ def __init__(self, operator, timesteps, power=None, power_density=None, - n_steps=10): + timestep_units='s', n_steps=10): check_type("n_steps", n_steps, Integral) check_greater_than("n_steps", n_steps, 0) - super().__init__(operator, timesteps, power, power_density) + super().__init__(operator, timesteps, power, power_density, timestep_units) self.n_steps = n_steps def _get_bos_data_from_operator(self, step_index, step_power, bos_conc): diff --git a/openmc/deplete/integrators.py b/openmc/deplete/integrators.py index 67106aa3e..84f838cb7 100644 --- a/openmc/deplete/integrators.py +++ b/openmc/deplete/integrators.py @@ -31,9 +31,11 @@ class PredictorIntegrator(Integrator): ---------- operator : openmc.deplete.TransportOperator Operator to perform transport simulations - timesteps : iterable of float - Array of timesteps in units of [s]. Note that values are not - cumulative. + timesteps : iterable of float or iterable of tuple + Array of timesteps. Note that values are not cumulative. The units are + specified by the `timestep_units` argument when `timesteps` is an + iterable of float. Alternatively, units can be specified for each step + by passing an iterable of (value, unit) tuples. power : float or iterable of float, optional Power of the reactor in [W]. A single value indicates that the power is constant over all timesteps. An iterable @@ -46,6 +48,11 @@ class PredictorIntegrator(Integrator): Power density of the reactor in [W/gHM]. It is multiplied by initial heavy metal inventory to get total power if ``power`` is not speficied. + timestep_units : {'s', 'd', 'a', 'MWd/kg'} + Units for values specified in the `timesteps` argument. 's' means + seconds, 'd' means days, 'a' means years, and 'MWd/kg' indicates that + the values are given in burnup (MW-d of energy deposited per kilogram + of heavy metal). Attributes ---------- @@ -113,9 +120,11 @@ class CECMIntegrator(Integrator): ---------- operator : openmc.deplete.TransportOperator Operator to perform transport simulations - timesteps : iterable of float - Array of timesteps in units of [s]. Note that values are not - cumulative. + timesteps : iterable of float or iterable of tuple + Array of timesteps. Note that values are not cumulative. The units are + specified by the `timestep_units` argument when `timesteps` is an + iterable of float. Alternatively, units can be specified for each step + by passing an iterable of (value, unit) tuples. power : float or iterable of float, optional Power of the reactor in [W]. A single value indicates that the power is constant over all timesteps. An iterable @@ -128,6 +137,11 @@ class CECMIntegrator(Integrator): Power density of the reactor in [W/gHM]. It is multiplied by initial heavy metal inventory to get total power if ``power`` is not speficied. + timestep_units : {'s', 'd', 'a', 'MWd/kg'} + Units for values specified in the `timesteps` argument. 's' means + seconds, 'd' means days, 'a' means years, and 'MWd/kg' indicates that + the values are given in burnup (MW-d of energy deposited per kilogram + of heavy metal). Attributes ---------- @@ -203,9 +217,11 @@ class CF4Integrator(Integrator): ---------- operator : openmc.deplete.TransportOperator Operator to perform transport simulations - timesteps : iterable of float - Array of timesteps in units of [s]. Note that values are not - cumulative. + timesteps : iterable of float or iterable of tuple + Array of timesteps. Note that values are not cumulative. The units are + specified by the `timestep_units` argument when `timesteps` is an + iterable of float. Alternatively, units can be specified for each step + by passing an iterable of (value, unit) tuples. power : float or iterable of float, optional Power of the reactor in [W]. A single value indicates that the power is constant over all timesteps. An iterable @@ -218,6 +234,11 @@ class CF4Integrator(Integrator): Power density of the reactor in [W/gHM]. It is multiplied by initial heavy metal inventory to get total power if ``power`` is not speficied. + timestep_units : {'s', 'd', 'a', 'MWd/kg'} + Units for values specified in the `timesteps` argument. 's' means + seconds, 'd' means days, 'a' means years, and 'MWd/kg' indicates that + the values are given in burnup (MW-d of energy deposited per kilogram + of heavy metal). Attributes ---------- @@ -310,9 +331,11 @@ class CELIIntegrator(Integrator): ---------- operator : openmc.deplete.TransportOperator Operator to perform transport simulations - timesteps : iterable of float - Array of timesteps in units of [s]. Note that values are not - cumulative. + timesteps : iterable of float or iterable of tuple + Array of timesteps. Note that values are not cumulative. The units are + specified by the `timestep_units` argument when `timesteps` is an + iterable of float. Alternatively, units can be specified for each step + by passing an iterable of (value, unit) tuples. power : float or iterable of float, optional Power of the reactor in [W]. A single value indicates that the power is constant over all timesteps. An iterable @@ -325,6 +348,11 @@ class CELIIntegrator(Integrator): Power density of the reactor in [W/gHM]. It is multiplied by initial heavy metal inventory to get total power if ``power`` is not speficied. + timestep_units : {'s', 'd', 'a', 'MWd/kg'} + Units for values specified in the `timesteps` argument. 's' means + seconds, 'd' means days, 'a' means years, and 'MWd/kg' indicates that + the values are given in burnup (MW-d of energy deposited per kilogram + of heavy metal). Attributes ---------- @@ -404,9 +432,11 @@ class EPCRK4Integrator(Integrator): ---------- operator : openmc.deplete.TransportOperator Operator to perform transport simulations - timesteps : iterable of float - Array of timesteps in units of [s]. Note that values are not - cumulative. + timesteps : iterable of float or iterable of tuple + Array of timesteps. Note that values are not cumulative. The units are + specified by the `timestep_units` argument when `timesteps` is an + iterable of float. Alternatively, units can be specified for each step + by passing an iterable of (value, unit) tuples. power : float or iterable of float, optional Power of the reactor in [W]. A single value indicates that the power is constant over all timesteps. An iterable @@ -419,6 +449,11 @@ class EPCRK4Integrator(Integrator): Power density of the reactor in [W/gHM]. It is multiplied by initial heavy metal inventory to get total power if ``power`` is not speficied. + timestep_units : {'s', 'd', 'a', 'MWd/kg'} + Units for values specified in the `timesteps` argument. 's' means + seconds, 'd' means days, 'a' means years, and 'MWd/kg' indicates that + the values are given in burnup (MW-d of energy deposited per kilogram + of heavy metal). Attributes ---------- @@ -518,9 +553,11 @@ class LEQIIntegrator(Integrator): ---------- operator : openmc.deplete.TransportOperator Operator to perform transport simulations - timesteps : iterable of float - Array of timesteps in units of [s]. Note that values are not - cumulative. + timesteps : iterable of float or iterable of tuple + Array of timesteps. Note that values are not cumulative. The units are + specified by the `timestep_units` argument when `timesteps` is an + iterable of float. Alternatively, units can be specified for each step + by passing an iterable of (value, unit) tuples. power : float or iterable of float, optional Power of the reactor in [W]. A single value indicates that the power is constant over all timesteps. An iterable @@ -533,6 +570,11 @@ class LEQIIntegrator(Integrator): Power density of the reactor in [W/gHM]. It is multiplied by initial heavy metal inventory to get total power if ``power`` is not speficied. + timestep_units : {'s', 'd', 'a', 'MWd/kg'} + Units for values specified in the `timesteps` argument. 's' means + seconds, 'd' means days, 'a' means years, and 'MWd/kg' indicates that + the values are given in burnup (MW-d of energy deposited per kilogram + of heavy metal). Attributes ---------- @@ -629,9 +671,11 @@ class SICELIIntegrator(SIIntegrator): ---------- operator : openmc.deplete.TransportOperator The operator object to simulate on. - timesteps : iterable of float - Array of timesteps in units of [s]. Note that values are not - cumulative. + timesteps : iterable of float or iterable of tuple + Array of timesteps. Note that values are not cumulative. The units are + specified by the `timestep_units` argument when `timesteps` is an + iterable of float. Alternatively, units can be specified for each step + by passing an iterable of (value, unit) tuples. power : float or iterable of float, optional Power of the reactor in [W]. A single value indicates that the power is constant over all timesteps. An iterable @@ -644,6 +688,11 @@ class SICELIIntegrator(SIIntegrator): Power density of the reactor in [W/gHM]. It is multiplied by initial heavy metal inventory to get total power if ``power`` is not speficied. + timestep_units : {'s', 'd', 'a', 'MWd/kg'} + Units for values specified in the `timesteps` argument. 's' means + seconds, 'd' means days, 'a' means years, and 'MWd/kg' indicates that + the values are given in burnup (MW-d of energy deposited per kilogram + of heavy metal). n_steps : int, optional Number of stochastic iterations per depletion interval. Must be greater than zero. Default : 10 @@ -730,9 +779,11 @@ class SILEQIIntegrator(SIIntegrator): ---------- operator : openmc.deplete.TransportOperator The operator object to simulate on. - timesteps : iterable of float - Array of timesteps in units of [s]. Note that values are not - cumulative. + timesteps : iterable of float or iterable of tuple + Array of timesteps. Note that values are not cumulative. The units are + specified by the `timestep_units` argument when `timesteps` is an + iterable of float. Alternatively, units can be specified for each step + by passing an iterable of (value, unit) tuples. power : float or iterable of float, optional Power of the reactor in [W]. A single value indicates that the power is constant over all timesteps. An iterable @@ -745,6 +796,11 @@ class SILEQIIntegrator(SIIntegrator): Power density of the reactor in [W/gHM]. It is multiplied by initial heavy metal inventory to get total power if ``power`` is not speficied. + timestep_units : {'s', 'd', 'a', 'MWd/kg'} + Units for values specified in the `timesteps` argument. 's' means + seconds, 'd' means days, 'a' means years, and 'MWd/kg' indicates that + the values are given in burnup (MW-d of energy deposited per kilogram + of heavy metal). n_steps : int, optional Number of stochastic iterations per depletion interval. Must be greater than zero. Default : 10 From 8771987bf40ad036107419acf9ea948fc3a1919a Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 11 Feb 2020 06:44:05 -0600 Subject: [PATCH 023/166] Add unit test checking depletion timestep units for all integrators --- tests/unit_tests/test_deplete_integrator.py | 69 ++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/tests/unit_tests/test_deplete_integrator.py b/tests/unit_tests/test_deplete_integrator.py index 47c9690ae..9d5766fd5 100644 --- a/tests/unit_tests/test_deplete_integrator.py +++ b/tests/unit_tests/test_deplete_integrator.py @@ -7,6 +7,7 @@ will be left unimplemented and testing will be done via regression. """ import copy +from random import uniform from unittest.mock import MagicMock import numpy as np @@ -15,11 +16,24 @@ import pytest from openmc.deplete import ( ReactionRates, Results, ResultsList, comm, OperatorResult, - PredictorIntegrator, SICELIIntegrator) + PredictorIntegrator, CECMIntegrator, CF4Integrator, CELIIntegrator, + EPCRK4Integrator, LEQIIntegrator, SICELIIntegrator, SILEQIIntegrator) from tests import dummy_operator +INTEGRATORS = [ + PredictorIntegrator, + CECMIntegrator, + CF4Integrator, + CELIIntegrator, + EPCRK4Integrator, + LEQIIntegrator, + SICELIIntegrator, + SILEQIIntegrator +] + + def test_results_save(run_in_tmpdir): """Test data save module""" @@ -159,3 +173,56 @@ def test_integrator(run_in_tmpdir, scheme): dep_time = res.get_depletion_time() assert dep_time.shape == (2, ) assert all(dep_time > 0) + + +@pytest.mark.parametrize("integrator", INTEGRATORS) +def test_timesteps(integrator): + # Crate fake operator + op = MagicMock() + op.prev_res = None + op.chain = None + + # Set heavy metal mass and power randomly + op.heavy_metal = uniform(0, 10000) + power = uniform(0, 1e6) + + # Reference timesteps in seconds + day = 86400.0 + ref_timesteps = [1*day, 2*day, 5*day, 10*day] + + # Case 1, timesteps in second + timesteps = ref_timesteps + x = integrator(op, timesteps, power, timestep_units='s') + assert np.allclose(x.timesteps, ref_timesteps) + + # Case 2, timesteps in days + timesteps = [t / day for t in ref_timesteps] + x = integrator(op, timesteps, power, timestep_units='d') + assert np.allclose(x.timesteps, ref_timesteps) + + # Case 3, timesteps in years + year = 365.25*day + timesteps = [t / year for t in ref_timesteps] + x = integrator(op, timesteps, power, timestep_units='a') + assert np.allclose(x.timesteps, ref_timesteps) + + # Case 4, timesteps in MWd/kg + kilograms = op.heavy_metal / 1000.0 + days = [t/day for t in ref_timesteps] + megawatts = power / 1000000.0 + burnup = [t * megawatts / kilograms for t in days] + x = integrator(op, burnup, power, timestep_units='MWd/kg') + assert np.allclose(x.timesteps, ref_timesteps) + + # Case 5, mixed units + burnup_per_day = (1e-6*power) / kilograms + timesteps = [(burnup_per_day, 'MWd/kg'), (2*day, 's'), (5, 'd'), + (10*burnup_per_day, 'MWd/kg')] + x = integrator(op, timesteps, power) + assert np.allclose(x.timesteps, ref_timesteps) + + # Bad units should raise an exception + with pytest.raises(ValueError, match="unit"): + integrator(op, ref_timesteps, power, timestep_units='🐨') + with pytest.raises(ValueError, match="unit"): + integrator(op, [(800.0, 'gorillas')], power) From 85264aa38c60d6846de5ca7005373c7cb3b9a47c Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 11 Feb 2020 07:08:53 -0600 Subject: [PATCH 024/166] Support minutes and hours for depletion timesteps rather than years --- openmc/deplete/abc.py | 29 +++++----- openmc/deplete/integrators.py | 64 ++++++++++----------- tests/unit_tests/test_deplete_integrator.py | 26 +++++---- 3 files changed, 64 insertions(+), 55 deletions(-) diff --git a/openmc/deplete/abc.py b/openmc/deplete/abc.py index a9640852c..d75833a6f 100644 --- a/openmc/deplete/abc.py +++ b/openmc/deplete/abc.py @@ -31,8 +31,9 @@ __all__ = [ "Integrator", "SIIntegrator", "DepSystemSolver"] +_SECONDS_PER_MINUTE = 60 +_SECONDS_PER_HOUR = 60*60 _SECONDS_PER_DAY = 24*60*60 -_SECONDS_PER_YEAR = 365.25*24*60*60 OperatorResult = namedtuple('OperatorResult', ['k', 'rates']) OperatorResult.__doc__ = """\ @@ -617,11 +618,11 @@ class Integrator(ABC): Power density of the reactor in [W/gHM]. It is multiplied by initial heavy metal inventory to get total power if ``power`` is not speficied. - timestep_units : {'s', 'd', 'a', 'MWd/kg'} + timestep_units : {'s', 'min', 'h', 'd', 'MWd/kg'} Units for values specified in the `timesteps` argument. 's' means - seconds, 'd' means days, 'a' means years, and 'MWd/kg' indicates that - the values are given in burnup (MW-d of energy deposited per kilogram - of heavy metal). + seconds, 'min' means minutes, 'h' means hours, and 'MWd/kg' indicates + that the values are given in burnup (MW-d of energy deposited per + kilogram of heavy metal). Attributes ---------- @@ -678,19 +679,21 @@ class Integrator(ABC): # Determine number of seconds for each timestep seconds = [] for time, unit, watts in zip(times, units, power): - if unit == 's': + if unit in ('s', 'sec'): seconds.append(time) + elif unit in ('min', 'minute'): + seconds.append(time*_SECONDS_PER_MINUTE) + elif unit in ('h', 'hr', 'hour'): + seconds.append(time*_SECONDS_PER_HOUR) elif unit in ('d', 'day'): seconds.append(time*_SECONDS_PER_DAY) - elif unit in ('a', 'yr', 'year'): - seconds.append(time*_SECONDS_PER_YEAR) elif unit.lower() == 'mwd/kg': watt_days_per_kg = 1e6*time kilograms = 1e-3*mass days = watt_days_per_kg * kilograms / watts seconds.append(days*_SECONDS_PER_DAY) else: - raise ValueError("Invalid timestep unit: {}".format(unit)) + raise ValueError("Invalid timestep unit '{}'".format(unit)) self.timesteps = asarray(seconds) self.power = asarray(power) @@ -824,11 +827,11 @@ class SIIntegrator(Integrator): Power density of the reactor in [W/gHM]. It is multiplied by initial heavy metal inventory to get total power if ``power`` is not speficied. - timestep_units : {'s', 'd', 'a', 'MWd/kg'} + timestep_units : {'s', 'min', 'h', 'd', 'MWd/kg'} Units for values specified in the `timesteps` argument. 's' means - seconds, 'd' means days, 'a' means years, and 'MWd/kg' indicates that - the values are given in burnup (MW-d of energy deposited per kilogram - of heavy metal). + seconds, 'min' means minutes, 'h' means hours, and 'MWd/kg' indicates + that the values are given in burnup (MW-d of energy deposited per + kilogram of heavy metal). n_steps : int, optional Number of stochastic iterations per depletion interval. Must be greater than zero. Default : 10 diff --git a/openmc/deplete/integrators.py b/openmc/deplete/integrators.py index 84f838cb7..da8509d59 100644 --- a/openmc/deplete/integrators.py +++ b/openmc/deplete/integrators.py @@ -48,11 +48,11 @@ class PredictorIntegrator(Integrator): Power density of the reactor in [W/gHM]. It is multiplied by initial heavy metal inventory to get total power if ``power`` is not speficied. - timestep_units : {'s', 'd', 'a', 'MWd/kg'} + timestep_units : {'s', 'min', 'h', 'd', 'MWd/kg'} Units for values specified in the `timesteps` argument. 's' means - seconds, 'd' means days, 'a' means years, and 'MWd/kg' indicates that - the values are given in burnup (MW-d of energy deposited per kilogram - of heavy metal). + seconds, 'min' means minutes, 'h' means hours, and 'MWd/kg' indicates + that the values are given in burnup (MW-d of energy deposited per + kilogram of heavy metal). Attributes ---------- @@ -137,11 +137,11 @@ class CECMIntegrator(Integrator): Power density of the reactor in [W/gHM]. It is multiplied by initial heavy metal inventory to get total power if ``power`` is not speficied. - timestep_units : {'s', 'd', 'a', 'MWd/kg'} + timestep_units : {'s', 'min', 'h', 'd', 'MWd/kg'} Units for values specified in the `timesteps` argument. 's' means - seconds, 'd' means days, 'a' means years, and 'MWd/kg' indicates that - the values are given in burnup (MW-d of energy deposited per kilogram - of heavy metal). + seconds, 'min' means minutes, 'h' means hours, and 'MWd/kg' indicates + that the values are given in burnup (MW-d of energy deposited per + kilogram of heavy metal). Attributes ---------- @@ -234,11 +234,11 @@ class CF4Integrator(Integrator): Power density of the reactor in [W/gHM]. It is multiplied by initial heavy metal inventory to get total power if ``power`` is not speficied. - timestep_units : {'s', 'd', 'a', 'MWd/kg'} + timestep_units : {'s', 'min', 'h', 'd', 'MWd/kg'} Units for values specified in the `timesteps` argument. 's' means - seconds, 'd' means days, 'a' means years, and 'MWd/kg' indicates that - the values are given in burnup (MW-d of energy deposited per kilogram - of heavy metal). + seconds, 'min' means minutes, 'h' means hours, and 'MWd/kg' indicates + that the values are given in burnup (MW-d of energy deposited per + kilogram of heavy metal). Attributes ---------- @@ -348,11 +348,11 @@ class CELIIntegrator(Integrator): Power density of the reactor in [W/gHM]. It is multiplied by initial heavy metal inventory to get total power if ``power`` is not speficied. - timestep_units : {'s', 'd', 'a', 'MWd/kg'} + timestep_units : {'s', 'min', 'h', 'd', 'MWd/kg'} Units for values specified in the `timesteps` argument. 's' means - seconds, 'd' means days, 'a' means years, and 'MWd/kg' indicates that - the values are given in burnup (MW-d of energy deposited per kilogram - of heavy metal). + seconds, 'min' means minutes, 'h' means hours, and 'MWd/kg' indicates + that the values are given in burnup (MW-d of energy deposited per + kilogram of heavy metal). Attributes ---------- @@ -449,11 +449,11 @@ class EPCRK4Integrator(Integrator): Power density of the reactor in [W/gHM]. It is multiplied by initial heavy metal inventory to get total power if ``power`` is not speficied. - timestep_units : {'s', 'd', 'a', 'MWd/kg'} + timestep_units : {'s', 'min', 'h', 'd', 'MWd/kg'} Units for values specified in the `timesteps` argument. 's' means - seconds, 'd' means days, 'a' means years, and 'MWd/kg' indicates that - the values are given in burnup (MW-d of energy deposited per kilogram - of heavy metal). + seconds, 'min' means minutes, 'h' means hours, and 'MWd/kg' indicates + that the values are given in burnup (MW-d of energy deposited per + kilogram of heavy metal). Attributes ---------- @@ -570,11 +570,11 @@ class LEQIIntegrator(Integrator): Power density of the reactor in [W/gHM]. It is multiplied by initial heavy metal inventory to get total power if ``power`` is not speficied. - timestep_units : {'s', 'd', 'a', 'MWd/kg'} + timestep_units : {'s', 'min', 'h', 'd', 'MWd/kg'} Units for values specified in the `timesteps` argument. 's' means - seconds, 'd' means days, 'a' means years, and 'MWd/kg' indicates that - the values are given in burnup (MW-d of energy deposited per kilogram - of heavy metal). + seconds, 'min' means minutes, 'h' means hours, and 'MWd/kg' indicates + that the values are given in burnup (MW-d of energy deposited per + kilogram of heavy metal). Attributes ---------- @@ -688,11 +688,11 @@ class SICELIIntegrator(SIIntegrator): Power density of the reactor in [W/gHM]. It is multiplied by initial heavy metal inventory to get total power if ``power`` is not speficied. - timestep_units : {'s', 'd', 'a', 'MWd/kg'} + timestep_units : {'s', 'min', 'h', 'd', 'MWd/kg'} Units for values specified in the `timesteps` argument. 's' means - seconds, 'd' means days, 'a' means years, and 'MWd/kg' indicates that - the values are given in burnup (MW-d of energy deposited per kilogram - of heavy metal). + seconds, 'min' means minutes, 'h' means hours, and 'MWd/kg' indicates + that the values are given in burnup (MW-d of energy deposited per + kilogram of heavy metal). n_steps : int, optional Number of stochastic iterations per depletion interval. Must be greater than zero. Default : 10 @@ -796,11 +796,11 @@ class SILEQIIntegrator(SIIntegrator): Power density of the reactor in [W/gHM]. It is multiplied by initial heavy metal inventory to get total power if ``power`` is not speficied. - timestep_units : {'s', 'd', 'a', 'MWd/kg'} + timestep_units : {'s', 'min', 'h', 'd', 'MWd/kg'} Units for values specified in the `timesteps` argument. 's' means - seconds, 'd' means days, 'a' means years, and 'MWd/kg' indicates that - the values are given in burnup (MW-d of energy deposited per kilogram - of heavy metal). + seconds, 'min' means minutes, 'h' means hours, and 'MWd/kg' indicates + that the values are given in burnup (MW-d of energy deposited per + kilogram of heavy metal). n_steps : int, optional Number of stochastic iterations per depletion interval. Must be greater than zero. Default : 10 diff --git a/tests/unit_tests/test_deplete_integrator.py b/tests/unit_tests/test_deplete_integrator.py index 9d5766fd5..110894cda 100644 --- a/tests/unit_tests/test_deplete_integrator.py +++ b/tests/unit_tests/test_deplete_integrator.py @@ -190,23 +190,29 @@ def test_timesteps(integrator): day = 86400.0 ref_timesteps = [1*day, 2*day, 5*day, 10*day] - # Case 1, timesteps in second + # Case 1, timesteps in seconds timesteps = ref_timesteps x = integrator(op, timesteps, power, timestep_units='s') assert np.allclose(x.timesteps, ref_timesteps) - # Case 2, timesteps in days + # Case 2, timesteps in minutes + minute = 60 + timesteps = [t / minute for t in ref_timesteps] + x = integrator(op, timesteps, power, timestep_units='min') + assert np.allclose(x.timesteps, ref_timesteps) + + # Case 3, timesteps in hours + hour = 60*60 + timesteps = [t / hour for t in ref_timesteps] + x = integrator(op, timesteps, power, timestep_units='h') + assert np.allclose(x.timesteps, ref_timesteps) + + # Case 4, timesteps in days timesteps = [t / day for t in ref_timesteps] x = integrator(op, timesteps, power, timestep_units='d') assert np.allclose(x.timesteps, ref_timesteps) - # Case 3, timesteps in years - year = 365.25*day - timesteps = [t / year for t in ref_timesteps] - x = integrator(op, timesteps, power, timestep_units='a') - assert np.allclose(x.timesteps, ref_timesteps) - - # Case 4, timesteps in MWd/kg + # Case 5, timesteps in MWd/kg kilograms = op.heavy_metal / 1000.0 days = [t/day for t in ref_timesteps] megawatts = power / 1000000.0 @@ -214,7 +220,7 @@ def test_timesteps(integrator): x = integrator(op, burnup, power, timestep_units='MWd/kg') assert np.allclose(x.timesteps, ref_timesteps) - # Case 5, mixed units + # Case 6, mixed units burnup_per_day = (1e-6*power) / kilograms timesteps = [(burnup_per_day, 'MWd/kg'), (2*day, 's'), (5, 'd'), (10*burnup_per_day, 'MWd/kg')] From 516ac9e1d372ff2030939dc87fd0699d81bb3c9c Mon Sep 17 00:00:00 2001 From: stevendargaville Date: Tue, 11 Feb 2020 13:37:00 +0000 Subject: [PATCH 025/166] Modified the MAX_LOST_PARTICLES const in particle.h to be a variable stored in settings that can be modified by the c/python/xml layers. --- include/openmc/particle.h | 3 --- include/openmc/settings.h | 9 +++++---- openmc/lib/settings.py | 1 + openmc/settings.py | 26 ++++++++++++++++++++++++++ openmc/statepoint.py | 6 ++++++ src/particle.cpp | 6 +++++- src/settings.cpp | 12 ++++++++++-- 7 files changed, 53 insertions(+), 10 deletions(-) diff --git a/include/openmc/particle.h b/include/openmc/particle.h index 8143ee5a3..ee59d7301 100644 --- a/include/openmc/particle.h +++ b/include/openmc/particle.h @@ -34,9 +34,6 @@ namespace openmc { // use to store the bins for delayed group tallies. constexpr int MAX_DELAYED_GROUPS {8}; -// Maximum number of lost particles -constexpr int MAX_LOST_PARTICLES {10}; - // Maximum number of lost particles, relative to the total number of particles constexpr double REL_MAX_LOST_PARTICLES {1.0e-6}; diff --git a/include/openmc/settings.h b/include/openmc/settings.h index cbc0c488b..1dcdea3cc 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -63,10 +63,11 @@ extern std::string path_source; extern std::string path_sourcepoint; //!< path to a source file extern "C" std::string path_statepoint; //!< path to a statepoint file -extern "C" int32_t n_batches; //!< number of (inactive+active) batches -extern "C" int32_t n_inactive; //!< number of inactive batches -extern "C" int32_t gen_per_batch; //!< number of generations per batch -extern "C" int64_t n_particles; //!< number of particles per generation +extern "C" int32_t n_batches; //!< number of (inactive+active) batches +extern "C" int32_t n_inactive; //!< number of inactive batches +extern "C" int32_t n_max_lost_particles; //!< maximum number of lost particles +extern "C" int32_t gen_per_batch; //!< number of generations per batch +extern "C" int64_t n_particles; //!< number of particles per generation extern int64_t max_particles_in_flight; //!< Max num. event-based particles in flight diff --git a/openmc/lib/settings.py b/openmc/lib/settings.py index 275c73a93..a25ccb962 100644 --- a/openmc/lib/settings.py +++ b/openmc/lib/settings.py @@ -22,6 +22,7 @@ class _Settings(object): entropy_on = _DLLGlobal(c_bool, 'entropy_on') generations_per_batch = _DLLGlobal(c_int32, 'gen_per_batch') inactive = _DLLGlobal(c_int32, 'n_inactive') + max_lost_particles = _DLLGlobal(c_int32, 'n_max_lost_particles') particles = _DLLGlobal(c_int64, 'n_particles') restart_run = _DLLGlobal(c_bool, 'restart_run') run_CE = _DLLGlobal(c_bool, 'run_CE') diff --git a/openmc/settings.py b/openmc/settings.py index eb2f926f3..79e7719a3 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -58,6 +58,8 @@ class Settings(object): history-based parallelism. generations_per_batch : int Number of generations per batch + max_lost_particles : int + Maximum number of lost particles inactive : int Number of inactive batches keff_trigger : dict @@ -176,6 +178,7 @@ class Settings(object): self._batches = None self._generations_per_batch = None self._inactive = None + self._max_lost_particles = None self._particles = None self._keff_trigger = None @@ -254,6 +257,10 @@ class Settings(object): def inactive(self): return self._inactive + @property + def max_lost_particles(self): + return self._max_lost_particles + @property def particles(self): return self._particles @@ -417,6 +424,12 @@ class Settings(object): cv.check_greater_than('inactive batches', inactive, 0, True) self._inactive = inactive + @max_lost_particles.setter + def max_lost_particles(self, max_lost_particles): + cv.check_type('max_lost_particles', max_lost_particles, Integral) + cv.check_greater_than('max_lost_particles', max_lost_particles, 0) + self._max_lost_particles = max_lost_particles + @particles.setter def particles(self, particles): cv.check_type('particles', particles, Integral) @@ -763,6 +776,11 @@ class Settings(object): element = ET.SubElement(root, "inactive") element.text = str(self._inactive) + def _create_max_lost_particles_subelement(self, root): + if self._max_lost_particles is not None: + element = ET.SubElement(root, "max_lost_particles") + element.text = str(self._max_lost_particles) + def _create_particles_subelement(self, root): if self._particles is not None: element = ET.SubElement(root, "particles") @@ -1009,6 +1027,7 @@ class Settings(object): self._particles_from_xml_element(elem) self._batches_from_xml_element(elem) self._inactive_from_xml_element(elem) + self._max_lost_particles_from_xml_element(elem) self._generations_per_batch_from_xml_element(elem) def _run_mode_from_xml_element(self, root): @@ -1031,6 +1050,11 @@ class Settings(object): if text is not None: self.inactive = int(text) + def _max_lost_particles_from_xml_element(self, root): + text = get_text(root, 'max_lost_particles') + if text is not None: + self.max_lost_particles = int(text) + def _generations_per_batch_from_xml_element(self, root): text = get_text(root, 'generations_per_batch') if text is not None: @@ -1270,6 +1294,7 @@ class Settings(object): self._create_particles_subelement(root_element) self._create_batches_subelement(root_element) self._create_inactive_subelement(root_element) + self._create_max_lost_particles_subelement(root_element) self._create_generations_per_batch_subelement(root_element) self._create_keff_trigger_subelement(root_element) self._create_source_subelement(root_element) @@ -1340,6 +1365,7 @@ class Settings(object): settings._particles_from_xml_element(root) settings._batches_from_xml_element(root) settings._inactive_from_xml_element(root) + settings._max_lost_particles_from_xml_element(root) settings._generations_per_batch_from_xml_element(root) settings._keff_trigger_from_xml_element(root) settings._source_from_xml_element(root) diff --git a/openmc/statepoint.py b/openmc/statepoint.py index 5802a74ca..051195ec9 100644 --- a/openmc/statepoint.py +++ b/openmc/statepoint.py @@ -77,6 +77,8 @@ class StatePoint(object): Number of batches n_inactive : int Number of inactive batches + n_max_lost_particles : int + Number of max lost particles n_particles : int Number of particles per generation n_realizations : int @@ -312,6 +314,10 @@ class StatePoint(object): else: return None + @property + def n_max_lost_particles(self): + return self._f['n_max_lost_particles'][()] + @property def n_particles(self): return self._f['n_particles'][()] diff --git a/src/particle.cpp b/src/particle.cpp index bd9a61139..8e6622204 100644 --- a/src/particle.cpp +++ b/src/particle.cpp @@ -164,6 +164,8 @@ Particle::event_calculate_xs() if (cell_born_ == C_NONE) cell_born_ = coord_[n_coord_ - 1].cell; } + std::cout << "settings::n_max_lost_particles " << settings::n_max_lost_particles << std::endl; + // Write particle track. if (write_track_) write_particle_track(*this); @@ -628,9 +630,11 @@ Particle::mark_as_lost(const char* message) auto n = simulation::current_batch * settings::gen_per_batch * simulation::work_per_rank; + std::cout << simulation::n_lost_particles << "HELLLOOOOOOOOOOOO MS DOUBTFIRE" << std::endl; + // Abort the simulation if the maximum number of lost particles has been // reached - if (simulation::n_lost_particles >= MAX_LOST_PARTICLES && + if (simulation::n_lost_particles >= settings::n_max_lost_particles && simulation::n_lost_particles >= REL_MAX_LOST_PARTICLES*n) { fatal_error("Maximum number of lost particles has been reached."); } diff --git a/src/settings.cpp b/src/settings.cpp index 32a7958d8..41a55501c 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -79,6 +79,7 @@ std::string path_statepoint; int32_t n_batches; int32_t n_inactive {0}; +int32_t n_max_lost_particles {10}; int32_t gen_per_batch {1}; int64_t n_particles {-1}; @@ -143,6 +144,11 @@ void get_run_parameters(pugi::xml_node node_base) } if (!trigger_on) n_max_batches = n_batches; + // Get max number of lost particles + if (check_for_node(node_base, "max_lost_particles")) { + n_max_lost_particles = std::stoi(get_node_value(node_base, "max_lost_particles")); + } + // Get number of inactive batches if (run_mode == RunMode::EIGENVALUE) { if (check_for_node(node_base, "inactive")) { @@ -343,14 +349,16 @@ void read_settings_xml() // Read run parameters get_run_parameters(node_mode); - // Check number of active batches, inactive batches, and particles + // Check number of active batches, inactive batches, max lost particles and particles if (n_batches <= n_inactive) { fatal_error("Number of active batches must be greater than zero."); } else if (n_inactive < 0) { fatal_error("Number of inactive batches must be non-negative."); } else if (n_particles <= 0) { fatal_error("Number of particles must be greater than zero."); - } + } else if (n_max_lost_particles <= 0) { + fatal_error("Number of max lost particles must be greater than zero."); + } } // Copy random number seed if specified From 9f64dd1a880a150682ae6140da1a9546b1982a35 Mon Sep 17 00:00:00 2001 From: stevendargaville Date: Tue, 11 Feb 2020 13:37:26 +0000 Subject: [PATCH 026/166] Modified the REL_MAX_LOST_PARTICLES const in particle.h to be a variable stored in settings that can be modified by the c/python/xml layers. --- include/openmc/particle.h | 3 --- include/openmc/settings.h | 11 ++++++----- openmc/lib/settings.py | 1 + openmc/settings.py | 31 +++++++++++++++++++++++++++++-- openmc/statepoint.py | 8 +++++++- src/particle.cpp | 6 +----- src/settings.cpp | 10 +++++++++- 7 files changed, 53 insertions(+), 17 deletions(-) diff --git a/include/openmc/particle.h b/include/openmc/particle.h index ee59d7301..2917b45e4 100644 --- a/include/openmc/particle.h +++ b/include/openmc/particle.h @@ -34,9 +34,6 @@ namespace openmc { // use to store the bins for delayed group tallies. constexpr int MAX_DELAYED_GROUPS {8}; -// Maximum number of lost particles, relative to the total number of particles -constexpr double REL_MAX_LOST_PARTICLES {1.0e-6}; - constexpr double CACHE_INVALID {-1.0}; //============================================================================== diff --git a/include/openmc/settings.h b/include/openmc/settings.h index 1dcdea3cc..3407eadf4 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -63,11 +63,12 @@ extern std::string path_source; extern std::string path_sourcepoint; //!< path to a source file extern "C" std::string path_statepoint; //!< path to a statepoint file -extern "C" int32_t n_batches; //!< number of (inactive+active) batches -extern "C" int32_t n_inactive; //!< number of inactive batches -extern "C" int32_t n_max_lost_particles; //!< maximum number of lost particles -extern "C" int32_t gen_per_batch; //!< number of generations per batch -extern "C" int64_t n_particles; //!< number of particles per generation +extern "C" int32_t n_batches; //!< number of (inactive+active) batches +extern "C" int32_t n_inactive; //!< number of inactive batches +extern "C" int32_t n_max_lost_particles; //!< maximum number of lost particles +extern double relative_max_lost_particles; //!< maximum number of lost particles, relative to the total number of particles +extern "C" int32_t gen_per_batch; //!< number of generations per batch +extern "C" int64_t n_particles; //!< number of particles per generation extern int64_t max_particles_in_flight; //!< Max num. event-based particles in flight diff --git a/openmc/lib/settings.py b/openmc/lib/settings.py index a25ccb962..8345e0088 100644 --- a/openmc/lib/settings.py +++ b/openmc/lib/settings.py @@ -23,6 +23,7 @@ class _Settings(object): generations_per_batch = _DLLGlobal(c_int32, 'gen_per_batch') inactive = _DLLGlobal(c_int32, 'n_inactive') max_lost_particles = _DLLGlobal(c_int32, 'n_max_lost_particles') + rel_max_lost_particles = _DLLGlobal(c_double, 'relative_max_lost_particles') particles = _DLLGlobal(c_int64, 'n_particles') restart_run = _DLLGlobal(c_bool, 'restart_run') run_CE = _DLLGlobal(c_bool, 'run_CE') diff --git a/openmc/settings.py b/openmc/settings.py index 79e7719a3..794c97b61 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -60,6 +60,8 @@ class Settings(object): Number of generations per batch max_lost_particles : int Maximum number of lost particles + rel_max_lost_particles : int + Maximum number of lost particles, relative to the total number of particles inactive : int Number of inactive batches keff_trigger : dict @@ -179,6 +181,7 @@ class Settings(object): self._generations_per_batch = None self._inactive = None self._max_lost_particles = None + self._rel_max_lost_particles = None self._particles = None self._keff_trigger = None @@ -261,6 +264,10 @@ class Settings(object): def max_lost_particles(self): return self._max_lost_particles + @property + def rel_max_lost_particles(self): + return self._rel_max_lost_particles + @property def particles(self): return self._particles @@ -430,6 +437,13 @@ class Settings(object): cv.check_greater_than('max_lost_particles', max_lost_particles, 0) self._max_lost_particles = max_lost_particles + @rel_max_lost_particles.setter + def rel_max_lost_particles(self, rel_max_lost_particles): + cv.check_type('rel_max_lost_particles', rel_max_lost_particles, Real) + cv.check_greater_than('rel_max_lost_particles', rel_max_lost_particles, 0) + cv.check_less_than('rel_max_lost_particles', rel_max_lost_particles, 1) + self._rel_max_lost_particles = rel_max_lost_particles + @particles.setter def particles(self, particles): cv.check_type('particles', particles, Integral) @@ -779,7 +793,12 @@ class Settings(object): def _create_max_lost_particles_subelement(self, root): if self._max_lost_particles is not None: element = ET.SubElement(root, "max_lost_particles") - element.text = str(self._max_lost_particles) + element.text = str(self._max_lost_particles) + + def _create_rel_max_lost_particles_subelement(self, root): + if self._rel_max_lost_particles is not None: + element = ET.SubElement(root, "rel_max_lost_particles") + element.text = str(self._rel_max_lost_particles) def _create_particles_subelement(self, root): if self._particles is not None: @@ -1028,6 +1047,7 @@ class Settings(object): self._batches_from_xml_element(elem) self._inactive_from_xml_element(elem) self._max_lost_particles_from_xml_element(elem) + self._rel_max_lost_particles_from_xml_element(elem) self._generations_per_batch_from_xml_element(elem) def _run_mode_from_xml_element(self, root): @@ -1053,7 +1073,12 @@ class Settings(object): def _max_lost_particles_from_xml_element(self, root): text = get_text(root, 'max_lost_particles') if text is not None: - self.max_lost_particles = int(text) + self.max_lost_particles = int(text) + + def _rel_max_lost_particles_from_xml_element(self, root): + text = get_text(root, 'rel_max_lost_particles') + if text is not None: + self.rel_max_lost_particles = float(text) def _generations_per_batch_from_xml_element(self, root): text = get_text(root, 'generations_per_batch') @@ -1295,6 +1320,7 @@ class Settings(object): self._create_batches_subelement(root_element) self._create_inactive_subelement(root_element) self._create_max_lost_particles_subelement(root_element) + self._create_rel_max_lost_particles_subelement(root_element) self._create_generations_per_batch_subelement(root_element) self._create_keff_trigger_subelement(root_element) self._create_source_subelement(root_element) @@ -1366,6 +1392,7 @@ class Settings(object): settings._batches_from_xml_element(root) settings._inactive_from_xml_element(root) settings._max_lost_particles_from_xml_element(root) + settings._rel_max_lost_particles_from_xml_element(root) settings._generations_per_batch_from_xml_element(root) settings._keff_trigger_from_xml_element(root) settings._source_from_xml_element(root) diff --git a/openmc/statepoint.py b/openmc/statepoint.py index 051195ec9..0d78bf68d 100644 --- a/openmc/statepoint.py +++ b/openmc/statepoint.py @@ -79,6 +79,8 @@ class StatePoint(object): Number of inactive batches n_max_lost_particles : int Number of max lost particles + relative_max_lost_particles : float + Number of max lost particles, relative to the total number of particles n_particles : int Number of particles per generation n_realizations : int @@ -316,7 +318,11 @@ class StatePoint(object): @property def n_max_lost_particles(self): - return self._f['n_max_lost_particles'][()] + return self._f['n_max_lost_particles'][()] + + @property + def relative_max_lost_particles(self): + return self._f['relative_max_lost_particles'][()] @property def n_particles(self): diff --git a/src/particle.cpp b/src/particle.cpp index 8e6622204..79e662577 100644 --- a/src/particle.cpp +++ b/src/particle.cpp @@ -164,8 +164,6 @@ Particle::event_calculate_xs() if (cell_born_ == C_NONE) cell_born_ = coord_[n_coord_ - 1].cell; } - std::cout << "settings::n_max_lost_particles " << settings::n_max_lost_particles << std::endl; - // Write particle track. if (write_track_) write_particle_track(*this); @@ -630,12 +628,10 @@ Particle::mark_as_lost(const char* message) auto n = simulation::current_batch * settings::gen_per_batch * simulation::work_per_rank; - std::cout << simulation::n_lost_particles << "HELLLOOOOOOOOOOOO MS DOUBTFIRE" << std::endl; - // Abort the simulation if the maximum number of lost particles has been // reached if (simulation::n_lost_particles >= settings::n_max_lost_particles && - simulation::n_lost_particles >= REL_MAX_LOST_PARTICLES*n) { + simulation::n_lost_particles >= settings::relative_max_lost_particles*n) { fatal_error("Maximum number of lost particles has been reached."); } } diff --git a/src/settings.cpp b/src/settings.cpp index 41a55501c..d72e5f17f 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -80,6 +80,7 @@ std::string path_statepoint; int32_t n_batches; int32_t n_inactive {0}; int32_t n_max_lost_particles {10}; +double relative_max_lost_particles {1.0e-6}; int32_t gen_per_batch {1}; int64_t n_particles {-1}; @@ -149,6 +150,11 @@ void get_run_parameters(pugi::xml_node node_base) n_max_lost_particles = std::stoi(get_node_value(node_base, "max_lost_particles")); } + // Get relative number of lost particles + if (check_for_node(node_base, "rel_max_lost_particles")) { + relative_max_lost_particles = std::stod(get_node_value(node_base, "rel_max_lost_particles")); + } + // Get number of inactive batches if (run_mode == RunMode::EIGENVALUE) { if (check_for_node(node_base, "inactive")) { @@ -358,7 +364,9 @@ void read_settings_xml() fatal_error("Number of particles must be greater than zero."); } else if (n_max_lost_particles <= 0) { fatal_error("Number of max lost particles must be greater than zero."); - } + } else if (relative_max_lost_particles <= 0.0 || relative_max_lost_particles >= 1.0) { + fatal_error("Relative max lost particles must be between zero and one."); + } } // Copy random number seed if specified From 6a40a535e482aa0ae31949b0836c22f3d6445f48 Mon Sep 17 00:00:00 2001 From: stevendargaville Date: Tue, 11 Feb 2020 13:37:32 +0000 Subject: [PATCH 027/166] Modified the test_settings unit test for new max_lost_particles and rel_max_lost_particles to check they can be assigned/read by the python interface --- tests/unit_tests/test_settings.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/unit_tests/test_settings.py b/tests/unit_tests/test_settings.py index 9c33f6a2d..c3ff72879 100644 --- a/tests/unit_tests/test_settings.py +++ b/tests/unit_tests/test_settings.py @@ -9,6 +9,8 @@ def test_export_to_xml(run_in_tmpdir): s.generations_per_batch = 10 s.inactive = 100 s.particles = 1000000 + s.max_lost_particles = 5 + s.rel_max_lost_particles = 1e-4 s.keff_trigger = {'type': 'std_dev', 'threshold': 0.001} s.energy_mode = 'continuous-energy' s.max_order = 5 @@ -62,6 +64,8 @@ def test_export_to_xml(run_in_tmpdir): assert s.generations_per_batch == 10 assert s.inactive == 100 assert s.particles == 1000000 + assert s.max_lost_particles == 5 + assert s.rel_max_lost_particles == 1e-4 assert s.keff_trigger == {'type': 'std_dev', 'threshold': 0.001} assert s.energy_mode == 'continuous-energy' assert s.max_order == 5 From 9224ca318675fcbb06c8eb97d9569466874dda21 Mon Sep 17 00:00:00 2001 From: stevendargaville Date: Tue, 11 Feb 2020 13:37:37 +0000 Subject: [PATCH 028/166] Modified an existing regression test to include the new max_lost_particles and relative version. This is mainly a test that the python/xml input/output routines work through the comparison the regression test harness does in this test, neither of these trigger the end of the simulation. --- tests/regression_tests/photon_source/inputs_true.dat | 2 ++ tests/regression_tests/photon_source/test.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/tests/regression_tests/photon_source/inputs_true.dat b/tests/regression_tests/photon_source/inputs_true.dat index 89f4de0e0..83c909ac6 100644 --- a/tests/regression_tests/photon_source/inputs_true.dat +++ b/tests/regression_tests/photon_source/inputs_true.dat @@ -18,6 +18,8 @@ fixed source 10000 1 + 5 + 0.1 0 0 0 diff --git a/tests/regression_tests/photon_source/test.py b/tests/regression_tests/photon_source/test.py index 94b028003..09a0b4a2d 100644 --- a/tests/regression_tests/photon_source/test.py +++ b/tests/regression_tests/photon_source/test.py @@ -31,6 +31,8 @@ class SourceTestHarness(PyAPITestHarness): settings = openmc.Settings() settings.particles = 10000 settings.batches = 1 + settings.max_lost_particles = 5 + settings.rel_max_lost_particles = 0.1 settings.photon_transport = True settings.electron_treatment = 'ttb' settings.cutoff = {'energy_photon' : 1000.0} From b6a03949c82aee90824b9daf60d3d74a587b5b19 Mon Sep 17 00:00:00 2001 From: stevendargaville Date: Tue, 11 Feb 2020 13:37:41 +0000 Subject: [PATCH 029/166] Modified the StatePoint output to include the new max_lost_particles and relative version. Further modified an existing regression test to check the StatePoint write-out of these variables --- src/state_point.cpp | 4 ++++ tests/regression_tests/photon_source/results_true.dat | 2 ++ tests/regression_tests/photon_source/test.py | 2 ++ 3 files changed, 8 insertions(+) diff --git a/src/state_point.cpp b/src/state_point.cpp index 60fd3826d..a9213bcb4 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -93,6 +93,8 @@ openmc_statepoint_write(const char* filename, bool* write_source) write_attribute(file_id, "photon_transport", settings::photon_transport); write_dataset(file_id, "n_particles", settings::n_particles); write_dataset(file_id, "n_batches", settings::n_batches); + write_dataset(file_id, "n_max_lost_particles", settings::n_max_lost_particles); + write_dataset(file_id, "relative_max_lost_particles", settings::relative_max_lost_particles); // Write out current batch number write_dataset(file_id, "current_batch", simulation::current_batch); @@ -373,6 +375,8 @@ void load_state_point() read_dataset(file_id, "n_particles", settings::n_particles); int temp; read_dataset(file_id, "n_batches", temp); + read_dataset(file_id, "n_max_lost_particles", settings::n_max_lost_particles); + read_dataset(file_id, "relative_max_lost_particles", settings::relative_max_lost_particles); // Take maximum of statepoint n_batches and input n_batches settings::n_batches = std::max(settings::n_batches, temp); diff --git a/tests/regression_tests/photon_source/results_true.dat b/tests/regression_tests/photon_source/results_true.dat index 1448ad6db..ab6ac9ff8 100644 --- a/tests/regression_tests/photon_source/results_true.dat +++ b/tests/regression_tests/photon_source/results_true.dat @@ -1,3 +1,5 @@ tally 1: sum = 2.275713E+02 sum_sq = 5.178870E+04 +max_lost = 5.000000E+00 +rel_max_lost = 1.000000E-01 diff --git a/tests/regression_tests/photon_source/test.py b/tests/regression_tests/photon_source/test.py index 09a0b4a2d..c5c42d680 100644 --- a/tests/regression_tests/photon_source/test.py +++ b/tests/regression_tests/photon_source/test.py @@ -54,6 +54,8 @@ class SourceTestHarness(PyAPITestHarness): outstr += 'tally {}:\n'.format(t.id) outstr += 'sum = {:12.6E}\n'.format(t.sum[0, 0, 0]) outstr += 'sum_sq = {:12.6E}\n'.format(t.sum_sq[0, 0, 0]) + outstr += 'max_lost = {:12.6E}\n'.format(sp.n_max_lost_particles) + outstr += 'rel_max_lost = {:12.6E}\n'.format(sp.relative_max_lost_particles) return outstr From 70e914d2458f6c35ef46f25b93f221f056dac813 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 11 Feb 2020 10:12:34 -0600 Subject: [PATCH 030/166] Fix failing tests --- openmc/deplete/abc.py | 7 +++---- tests/unit_tests/test_deplete_integrator.py | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/openmc/deplete/abc.py b/openmc/deplete/abc.py index d75833a6f..912c0b1fb 100644 --- a/openmc/deplete/abc.py +++ b/openmc/deplete/abc.py @@ -652,14 +652,13 @@ class Integrator(ABC): self.chain = operator.chain # Determine power and normalize units to W - mass = operator.heavy_metal if power is None: if power_density is None: raise ValueError("Either power or power density must be set") if not isinstance(power_density, Iterable): - power = power_density * mass + power = power_density * operator.heavy_metal else: - power = [p*mass for p in power_density] + power = [p*operator.heavy_metal for p in power_density] if not isinstance(power, Iterable): # Ensure that power is single value if that is the case power = [power] * len(timesteps) @@ -689,7 +688,7 @@ class Integrator(ABC): seconds.append(time*_SECONDS_PER_DAY) elif unit.lower() == 'mwd/kg': watt_days_per_kg = 1e6*time - kilograms = 1e-3*mass + kilograms = 1e-3*operator.heavy_metal days = watt_days_per_kg * kilograms / watts seconds.append(days*_SECONDS_PER_DAY) else: diff --git a/tests/unit_tests/test_deplete_integrator.py b/tests/unit_tests/test_deplete_integrator.py index 110894cda..6c09b3fea 100644 --- a/tests/unit_tests/test_deplete_integrator.py +++ b/tests/unit_tests/test_deplete_integrator.py @@ -119,14 +119,14 @@ def test_results_save(run_in_tmpdir): np.testing.assert_array_equal(res[1].time, t2) -@pytest.mark.parametrize("timesteps", (1, [1])) -def test_bad_integrator_inputs(timesteps): +def test_bad_integrator_inputs(): """Test failure modes for Integrator inputs""" op = MagicMock() op.prev_res = None op.chain = None op.heavy_metal = 1.0 + timesteps = [1] # No power nor power density given with pytest.raises(ValueError, match="Either power or power density"): From 58d751bfaea969b56cc5cb8067ea91301a54ed28 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Thu, 13 Jun 2019 09:15:21 +0100 Subject: [PATCH 031/166] 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 cbc0c488b..689e22283 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 a177995ea..0a73260fd 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 5bd53de25..6419e4691 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 b15a15d23..4666783d4 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 032/166] 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 4666783d4..b856a8d2a 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 033/166] 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 000000000..b30884f8c --- /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 000000000..606c676df --- /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 000000000..f8d497459 --- /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 000000000..ef2784ea3 --- /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 000000000..7f6f29926 --- /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 034/166] 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 88c2f8611..e0fd2b6c4 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 035/166] 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 e0fd2b6c4..f8ea8ea93 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 036/166] 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 b856a8d2a..089dbddef 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 037/166] 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 089dbddef..b7d1e2499 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 038/166] 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 f8ea8ea93..e0fd2b6c4 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 039/166] 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 e0fd2b6c4..f8ea8ea93 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 040/166] 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 ec6b500b4..6cc9fceaf 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 041/166] 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 b7d1e2499..51d86fcaa 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 042/166] 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 51d86fcaa..b7d1e2499 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 043/166] 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 f8ea8ea93..e0fd2b6c4 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 044/166] 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 e0fd2b6c4..f8ea8ea93 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 045/166] missing space --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6cc9fceaf..605ba0b61 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 046/166] 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 000000000..4b13c7db9 --- /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 000000000..917f3fd4f --- /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 047/166] 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 605ba0b61..18de94121 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 048/166] 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 000000000..23f3d8438 --- /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 000000000..e69de29bb 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 000000000..ac63ca804 --- /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 917f3fd4f..000000000 --- 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 049/166] Stylistic changes --- src/source.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/source.cpp b/src/source.cpp index b7d1e2499..464a39948 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 050/166] 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 f8ea8ea93..339d41723 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 1c70e159d..d4d17a3da 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 051/166] 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 2e2648237..1d35c18aa 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 052/166] 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 464a39948..066e3a52c 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 053/166] 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 18de94121..d61477f19 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 054/166] 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 000000000..e69de29bb From 7a51552c3e67a95986cf63753f962617a1ef75e1 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 16 Oct 2019 21:07:31 +0100 Subject: [PATCH 055/166] 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 d61477f19..ef67da4e5 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 056/166] 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 066e3a52c..b1fd8c71e 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 057/166] 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 b1fd8c71e..465c2aac1 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 058/166] 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 1d35c18aa..fde374b8d 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 059/166] 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 ef67da4e5..0d4c371f0 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 060/166] 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 465c2aac1..f910b384c 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 061/166] 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 ef2784ea3..fb361d1aa 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 062/166] line removal from review --- src/source.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/source.cpp b/src/source.cpp index f910b384c..e7a476746 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 063/166] 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 fde374b8d..f224df265 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 064/166] 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 f224df265..5392efef5 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 065/166] 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 0a73260fd..e980f2a8c 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 e7a476746..2cda76a5c 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 066/166] 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 5392efef5..6f898f911 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 067/166] 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 fb361d1aa..babb2f591 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 39a60c361c9111575d9ded988c8459dbc4b82342 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 12 Feb 2020 07:19:31 -0600 Subject: [PATCH 068/166] Clarify that heavy metal is initial Co-Authored-By: Andrew Johnson --- openmc/deplete/abc.py | 4 ++-- openmc/deplete/integrators.py | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/openmc/deplete/abc.py b/openmc/deplete/abc.py index 912c0b1fb..658e93da5 100644 --- a/openmc/deplete/abc.py +++ b/openmc/deplete/abc.py @@ -622,7 +622,7 @@ class Integrator(ABC): Units for values specified in the `timesteps` argument. 's' means seconds, 'min' means minutes, 'h' means hours, and 'MWd/kg' indicates that the values are given in burnup (MW-d of energy deposited per - kilogram of heavy metal). + kilogram of initial heavy metal). Attributes ---------- @@ -830,7 +830,7 @@ class SIIntegrator(Integrator): Units for values specified in the `timesteps` argument. 's' means seconds, 'min' means minutes, 'h' means hours, and 'MWd/kg' indicates that the values are given in burnup (MW-d of energy deposited per - kilogram of heavy metal). + kilogram of initial heavy metal). n_steps : int, optional Number of stochastic iterations per depletion interval. Must be greater than zero. Default : 10 diff --git a/openmc/deplete/integrators.py b/openmc/deplete/integrators.py index da8509d59..38a7c33b1 100644 --- a/openmc/deplete/integrators.py +++ b/openmc/deplete/integrators.py @@ -52,7 +52,7 @@ class PredictorIntegrator(Integrator): Units for values specified in the `timesteps` argument. 's' means seconds, 'min' means minutes, 'h' means hours, and 'MWd/kg' indicates that the values are given in burnup (MW-d of energy deposited per - kilogram of heavy metal). + kilogram of initial heavy metal). Attributes ---------- @@ -141,7 +141,7 @@ class CECMIntegrator(Integrator): Units for values specified in the `timesteps` argument. 's' means seconds, 'min' means minutes, 'h' means hours, and 'MWd/kg' indicates that the values are given in burnup (MW-d of energy deposited per - kilogram of heavy metal). + kilogram of initial heavy metal). Attributes ---------- @@ -238,7 +238,7 @@ class CF4Integrator(Integrator): Units for values specified in the `timesteps` argument. 's' means seconds, 'min' means minutes, 'h' means hours, and 'MWd/kg' indicates that the values are given in burnup (MW-d of energy deposited per - kilogram of heavy metal). + kilogram of initial heavy metal). Attributes ---------- @@ -352,7 +352,7 @@ class CELIIntegrator(Integrator): Units for values specified in the `timesteps` argument. 's' means seconds, 'min' means minutes, 'h' means hours, and 'MWd/kg' indicates that the values are given in burnup (MW-d of energy deposited per - kilogram of heavy metal). + kilogram of initial heavy metal). Attributes ---------- @@ -453,7 +453,7 @@ class EPCRK4Integrator(Integrator): Units for values specified in the `timesteps` argument. 's' means seconds, 'min' means minutes, 'h' means hours, and 'MWd/kg' indicates that the values are given in burnup (MW-d of energy deposited per - kilogram of heavy metal). + kilogram of initial heavy metal). Attributes ---------- @@ -574,7 +574,7 @@ class LEQIIntegrator(Integrator): Units for values specified in the `timesteps` argument. 's' means seconds, 'min' means minutes, 'h' means hours, and 'MWd/kg' indicates that the values are given in burnup (MW-d of energy deposited per - kilogram of heavy metal). + kilogram of initial heavy metal). Attributes ---------- @@ -692,7 +692,7 @@ class SICELIIntegrator(SIIntegrator): Units for values specified in the `timesteps` argument. 's' means seconds, 'min' means minutes, 'h' means hours, and 'MWd/kg' indicates that the values are given in burnup (MW-d of energy deposited per - kilogram of heavy metal). + kilogram of initial heavy metal). n_steps : int, optional Number of stochastic iterations per depletion interval. Must be greater than zero. Default : 10 @@ -800,7 +800,7 @@ class SILEQIIntegrator(SIIntegrator): Units for values specified in the `timesteps` argument. 's' means seconds, 'min' means minutes, 'h' means hours, and 'MWd/kg' indicates that the values are given in burnup (MW-d of energy deposited per - kilogram of heavy metal). + kilogram of initial heavy metal). n_steps : int, optional Number of stochastic iterations per depletion interval. Must be greater than zero. Default : 10 From 3682f7adcbf6420198a39e332207818685a9524f Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 12 Feb 2020 07:36:29 -0600 Subject: [PATCH 069/166] Add type/value checks on timestep, power, and units --- openmc/deplete/abc.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/openmc/deplete/abc.py b/openmc/deplete/abc.py index 658e93da5..95bd3326b 100644 --- a/openmc/deplete/abc.py +++ b/openmc/deplete/abc.py @@ -665,7 +665,7 @@ class Integrator(ABC): if len(power) != len(timesteps): raise ValueError( - "Number of time steps != number of powers. {} vs {}".format( + "Number of time steps ({}) != number of powers ({})".format( len(timesteps), len(power))) # Get list of times / units @@ -678,6 +678,13 @@ class Integrator(ABC): # Determine number of seconds for each timestep seconds = [] for time, unit, watts in zip(times, units, power): + # Make sure values passed make sense + check_type('timestep', time, Real) + check_greater_than('timestep', time, 0.0, True) + check_type('timestep units', unit, str) + check_type('power', watts, Real) + check_greater_than('power', watts, 0.0, True) + if unit in ('s', 'sec'): seconds.append(time) elif unit in ('min', 'minute'): From 4966ff7c5dff243853881c75125994dda6d3a887 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 12 Feb 2020 12:21:25 -0600 Subject: [PATCH 070/166] Ensure timesteps are positive Co-Authored-By: Andrew Johnson --- openmc/deplete/abc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmc/deplete/abc.py b/openmc/deplete/abc.py index 95bd3326b..31479f995 100644 --- a/openmc/deplete/abc.py +++ b/openmc/deplete/abc.py @@ -680,7 +680,7 @@ class Integrator(ABC): for time, unit, watts in zip(times, units, power): # Make sure values passed make sense check_type('timestep', time, Real) - check_greater_than('timestep', time, 0.0, True) + check_greater_than('timestep', time, 0.0, False) check_type('timestep units', unit, str) check_type('power', watts, Real) check_greater_than('power', watts, 0.0, True) From 4200b0dfbf11cf555d3f791285508bf0a18c87fd Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 12 Feb 2020 16:05:58 -0600 Subject: [PATCH 071/166] Update depletion-related documentation --- docs/source/usersguide/depletion.rst | 17 ++- .../pincell_depletion/restart_depletion.py | 50 +++---- .../python/pincell_depletion/run_depletion.py | 122 ++++++------------ 3 files changed, 67 insertions(+), 122 deletions(-) diff --git a/docs/source/usersguide/depletion.rst b/docs/source/usersguide/depletion.rst index 4a4c7b1f8..f6feb6d24 100644 --- a/docs/source/usersguide/depletion.rst +++ b/docs/source/usersguide/depletion.rst @@ -33,12 +33,11 @@ material compositions over time. Each method appears as a different class. For example, :class:`openmc.deplete.CECMIntegrator` runs a depletion calculation using the CE/CM algorithm (deplete over a timestep using the middle-of-step reaction rates). An instance of :class:`openmc.deplete.Operator` is passed to -one of these functions along with the power level and timesteps:: +one of these functions along with the timesteps and power level:: - power = 1200.0e6 - days = 24*60*60 - timesteps = [10.0*days, 10.0*days, 10.0*days] - openmc.deplete.CECMIntegrator(op, power, timesteps).integrate() + power = 1200.0e6 # watts + timesteps = [10.0, 10.0, 10.0] # days + openmc.deplete.CECMIntegrator(op, timesteps, power, timestep_units='d').integrate() The coupled transport-depletion problem is executed, and once it is done a ``depletion_results.h5`` file is written. The results can be analyzed using the @@ -67,7 +66,7 @@ the energy deposited during a transport calculation will be lower than expected. This causes the reaction rates to be over-adjusted to hit the user-specific power, or power density, leading to an over-depletion of burnable materials. -There are some remedies. First, the fission Q values can be directly set in a +There are some remedies. First, the fission Q values can be directly set in a variety of ways. This requires knowing what the total fission energy release should be, including indirect components. Some examples are provided below:: @@ -99,11 +98,11 @@ Local Spectra and Repeated Materials ------------------------------------ It is not uncommon to explicitly create a single burnable material across many locations. -From a pure transport perspective, there is nothing wrong with creating a single +From a pure transport perspective, there is nothing wrong with creating a single 3.5 wt.% enriched fuel ``fuel_3``, and placing that fuel in every fuel pin in an assembly or even full core problem. This certainly expedites the model making process, but can pose -issues with depletion. -Under this setup, :mod:`openmc.deplete` will deplete a single ``fuel_3`` material using +issues with depletion. +Under this setup, :mod:`openmc.deplete` will deplete a single ``fuel_3`` material using a single set of reaction rates, and produce a single new composition for the next time step. This can be problematic if the same ``fuel_3`` is used in very different regions of the problem. diff --git a/examples/python/pincell_depletion/restart_depletion.py b/examples/python/pincell_depletion/restart_depletion.py index 013a2469e..f0387e066 100644 --- a/examples/python/pincell_depletion/restart_depletion.py +++ b/examples/python/pincell_depletion/restart_depletion.py @@ -1,25 +1,7 @@ import openmc import openmc.deplete -import numpy as np import matplotlib.pyplot as plt -############################################################################### -# Simulation Input File Parameters -############################################################################### - -# OpenMC simulation parameters -batches = 100 -inactive = 10 -particles = 1000 - -# Depletion simulation parameters -time_step = 1*24*60*60 # s -final_time = 5*24*60*60 # s -time_steps = np.full(final_time // time_step, time_step) - -chain_file = './chain_simple.xml' -power = 174 # W/cm, for 2D simulations only (use W for 3D) - ############################################################################### # Load previous simulation results ############################################################################### @@ -37,31 +19,34 @@ previous_results = openmc.deplete.ResultsList("depletion_results.h5") ############################################################################### # Instantiate a Settings object, set all runtime parameters -settings_file = openmc.Settings() -settings_file.batches = batches -settings_file.inactive = inactive -settings_file.particles = particles +settings = openmc.Settings() +settings.batches = 100 +settings.inactive = 10 +settings.particles = 10000 # Create an initial uniform spatial source distribution over fissionable zones bounds = [-0.62992, -0.62992, -1, 0.62992, 0.62992, 1] uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:], only_fissionable=True) -settings_file.source = openmc.source.Source(space=uniform_dist) +settings.source = openmc.source.Source(space=uniform_dist) entropy_mesh = openmc.RegularMesh() entropy_mesh.lower_left = [-0.39218, -0.39218, -1.e50] entropy_mesh.upper_right = [0.39218, 0.39218, 1.e50] entropy_mesh.dimension = [10, 10, 1] -settings_file.entropy_mesh = entropy_mesh +settings.entropy_mesh = entropy_mesh ############################################################################### # Initialize and run depletion calculation ############################################################################### -op = openmc.deplete.Operator(geometry, settings_file, chain_file, - previous_results) +# Create depletion "operator" +chain_file = './chain_simple.xml' +op = openmc.deplete.Operator(geometry, settings, chain_file, previous_results) # Perform simulation using the predictor algorithm -integrator = openmc.deplete.PredictorIntegrator(op, time_steps, power) +time_steps = [1.0, 1.0, 1.0, 1.0, 1.0] # days +power = 174 # W/cm, for 2D simulations only (use W for 3D) +integrator = openmc.deplete.PredictorIntegrator(op, time_steps, power, timestep_units='d') integrator.integrate() ############################################################################### @@ -77,27 +62,28 @@ time, keff = results.get_eigenvalue() # Obtain U235 concentration as a function of time time, n_U235 = results.get_atoms('1', 'U235') -# Obtain Xe135 absorption as a function of time -time, Xe_gam = results.get_reaction_rate('1', 'Xe135', '(n,gamma)') +# Obtain Xe135 capture reaction rate as a function of time +time, Xe_capture = results.get_reaction_rate('1', 'Xe135', '(n,gamma)') ############################################################################### # Generate plots ############################################################################### +days = 24*60*60 plt.figure() -plt.plot(time/(24*60*60), keff, label="K-effective") +plt.plot(time/days, keff, label="K-effective") plt.xlabel("Time (days)") plt.ylabel("Keff") plt.show() plt.figure() -plt.plot(time/(24*60*60), n_U235, label="U 235") +plt.plot(time/days, n_U235, label="U 235") plt.xlabel("Time (days)") plt.ylabel("n U5 (-)") plt.show() plt.figure() -plt.plot(time/(24*60*60), Xe_gam, label="Xe135 absorption") +plt.plot(time/days, Xe_capture, label="Xe135 capture") plt.xlabel("Time (days)") plt.ylabel("RR (-)") plt.show() diff --git a/examples/python/pincell_depletion/run_depletion.py b/examples/python/pincell_depletion/run_depletion.py index 9933edd48..ce4b0cf5e 100644 --- a/examples/python/pincell_depletion/run_depletion.py +++ b/examples/python/pincell_depletion/run_depletion.py @@ -1,47 +1,31 @@ +from math import pi + import openmc import openmc.deplete -import numpy as np import matplotlib.pyplot as plt -############################################################################### -# Simulation Input File Parameters -############################################################################### - -# OpenMC simulation parameters -batches = 100 -inactive = 10 -particles = 1000 - -# Depletion simulation parameters -time_step = 1*24*60*60 # s -final_time = 5*24*60*60 # s -time_steps = np.full(final_time // time_step, time_step) -chain_file = './chain_simple.xml' -power = 174 # W/cm, for 2D simulations only (use W for 3D) - ############################################################################### # Define materials ############################################################################### # Instantiate some Materials and register the appropriate Nuclides -uo2 = openmc.Material(material_id=1, name='UO2 fuel at 2.4% wt enrichment') +uo2 = openmc.Material(name='UO2 fuel at 2.4% wt enrichment') uo2.set_density('g/cm3', 10.29769) uo2.add_element('U', 1., enrichment=2.4) uo2.add_element('O', 2.) -uo2.depletable = True -helium = openmc.Material(material_id=2, name='Helium for gap') +helium = openmc.Material(name='Helium for gap') helium.set_density('g/cm3', 0.001598) helium.add_element('He', 2.4044e-4) -zircaloy = openmc.Material(material_id=3, name='Zircaloy 4') +zircaloy = openmc.Material(name='Zircaloy 4') zircaloy.set_density('g/cm3', 6.55) -zircaloy.add_element('Sn', 0.014 , 'wo') +zircaloy.add_element('Sn', 0.014, 'wo') zircaloy.add_element('Fe', 0.00165, 'wo') -zircaloy.add_element('Cr', 0.001 , 'wo') +zircaloy.add_element('Cr', 0.001, 'wo') zircaloy.add_element('Zr', 0.98335, 'wo') -borated_water = openmc.Material(material_id=4, name='Borated water') +borated_water = openmc.Material(name='Borated water') borated_water.set_density('g/cm3', 0.740582) borated_water.add_element('B', 4.0e-5) borated_water.add_element('H', 5.0e-2) @@ -52,87 +36,62 @@ borated_water.add_s_alpha_beta('c_H_in_H2O') # Create geometry ############################################################################### -# Instantiate ZCylinder surfaces -fuel_or = openmc.ZCylinder(surface_id=1, x0=0, y0=0, r=0.39218, name='Fuel OR') -clad_ir = openmc.ZCylinder(surface_id=2, x0=0, y0=0, r=0.40005, name='Clad IR') -clad_or = openmc.ZCylinder(surface_id=3, x0=0, y0=0, r=0.45720, name='Clad OR') -left = openmc.XPlane(surface_id=4, x0=-0.62992, name='left') -right = openmc.XPlane(surface_id=5, x0=0.62992, name='right') -bottom = openmc.YPlane(surface_id=6, y0=-0.62992, name='bottom') -top = openmc.YPlane(surface_id=7, y0=0.62992, name='top') +# Define surfaces +pitch = 1.25984 +fuel_or = openmc.ZCylinder(r=0.39218, name='Fuel OR') +clad_ir = openmc.ZCylinder(r=0.40005, name='Clad IR') +clad_or = openmc.ZCylinder(r=0.45720, name='Clad OR') +box = openmc.model.rectangular_prism(pitch, pitch, boundary_type='reflective') -left.boundary_type = 'reflective' -right.boundary_type = 'reflective' -top.boundary_type = 'reflective' -bottom.boundary_type = 'reflective' +# Define cells +fuel = openmc.Cell(fill=uo2, region=-fuel_or) +gap = openmc.Cell(fill=helium, region=+fuel_or & -clad_ir) +clad = openmc.Cell(fill=zircaloy, region=+clad_ir & -clad_or) +water = openmc.Cell(fill=borated_water, region=+clad_or & box) -# Instantiate Cells -fuel = openmc.Cell(cell_id=1, name='cell 1') -gap = openmc.Cell(cell_id=2, name='cell 2') -clad = openmc.Cell(cell_id=3, name='cell 3') -water = openmc.Cell(cell_id=4, name='cell 4') - -# Use surface half-spaces to define regions -fuel.region = -fuel_or -gap.region = +fuel_or & -clad_ir -clad.region = +clad_ir & -clad_or -water.region = +clad_or & +left & -right & +bottom & -top - -# Register Materials with Cells -fuel.fill = uo2 -gap.fill = helium -clad.fill = zircaloy -water.fill = borated_water - -# Instantiate Universe -root = openmc.Universe(universe_id=0, name='root universe') - -# Register Cells with Universe -root.add_cells([fuel, gap, clad, water]) - -# Instantiate a Geometry, register the root Universe -geometry = openmc.Geometry(root) +# Define overall geometry +geometry = openmc.Geometry([fuel, gap, clad, water]) ############################################################################### # Set volumes of depletable materials ############################################################################### -# Compute cell areas -area = {} -area[fuel] = np.pi * fuel_or.coefficients['r'] ** 2 - -# Set materials volume for depletion. Set to an area for 2D simulations -uo2.volume = area[fuel] +# Set material volume for depletion. For 2D simulations, this should be an area. +uo2.volume = pi * fuel_or.r**2 ############################################################################### # Transport calculation settings ############################################################################### # Instantiate a Settings object, set all runtime parameters, and export to XML -settings_file = openmc.Settings() -settings_file.batches = batches -settings_file.inactive = inactive -settings_file.particles = particles +settings = openmc.Settings() +settings.batches = 100 +settings.inactive = 10 +settings.particles = 1000 # Create an initial uniform spatial source distribution over fissionable zones bounds = [-0.62992, -0.62992, -1, 0.62992, 0.62992, 1] uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:], only_fissionable=True) -settings_file.source = openmc.source.Source(space=uniform_dist) +settings.source = openmc.source.Source(space=uniform_dist) entropy_mesh = openmc.RegularMesh() entropy_mesh.lower_left = [-0.39218, -0.39218, -1.e50] entropy_mesh.upper_right = [0.39218, 0.39218, 1.e50] entropy_mesh.dimension = [10, 10, 1] -settings_file.entropy_mesh = entropy_mesh +settings.entropy_mesh = entropy_mesh ############################################################################### # Initialize and run depletion calculation ############################################################################### -op = openmc.deplete.Operator(geometry, settings_file, chain_file) +# Create depletion "operator" +chain_file = './chain_simple.xml' +op = openmc.deplete.Operator(geometry, settings, chain_file) # Perform simulation using the predictor algorithm -integrator = openmc.deplete.PredictorIntegrator(op, time_steps, power) +time_steps = [1.0, 1.0, 1.0, 1.0, 1.0] # days +power = 174 # W/cm, for 2D simulations only (use W for 3D) +integrator = openmc.deplete.PredictorIntegrator(op, time_steps, power, timestep_units='d') integrator.integrate() ############################################################################### @@ -148,27 +107,28 @@ time, keff = results.get_eigenvalue() # Obtain U235 concentration as a function of time time, n_U235 = results.get_atoms('1', 'U235') -# Obtain Xe135 absorption as a function of time -time, Xe_gam = results.get_reaction_rate('1', 'Xe135', '(n,gamma)') +# Obtain Xe135 capture reaction rate as a function of time +time, Xe_capture = results.get_reaction_rate('1', 'Xe135', '(n,gamma)') ############################################################################### # Generate plots ############################################################################### +days = 24*60*60 plt.figure() -plt.plot(time/(24*60*60), keff, label="K-effective") +plt.plot(time/days, keff, label="K-effective") plt.xlabel("Time (days)") plt.ylabel("Keff") plt.show() plt.figure() -plt.plot(time/(24*60*60), n_U235, label="U 235") +plt.plot(time/days, n_U235, label="U235") plt.xlabel("Time (days)") plt.ylabel("n U5 (-)") plt.show() plt.figure() -plt.plot(time/(24*60*60), Xe_gam, label="Xe135 absorption") +plt.plot(time/days, Xe_capture, label="Xe135 capture") plt.xlabel("Time (days)") plt.ylabel("RR (-)") plt.show() From 061fc82e4374f0d810acf017236216e77eced683 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 12 Feb 2020 12:30:19 +0000 Subject: [PATCH 072/166] These fixes allow the Cmake find_package infrastructure to work correctly --- CMakeLists.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ec6b500b4..3cbd27b4e 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} From aa9acca56ad1d8e43d6774afda37a6d4b8d70a32 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 12 Feb 2020 22:10:41 +0000 Subject: [PATCH 073/166] Revert "These fixes allow the Cmake find_package infrastructure to work correctly" This reverts commit cfee500608a823cdbed751ed4cd7675cef519561. --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3cbd27b4e..ec6b500b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -207,6 +207,7 @@ endif() add_library(faddeeva STATIC vendor/faddeeva/Faddeeva.cc) target_include_directories(faddeeva PUBLIC + $ $ ) target_compile_options(faddeeva PRIVATE ${cxxflags}) @@ -388,7 +389,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} From b9c3d7af5424d241be522bcf8f791c9e1dfa4ce3 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 12 Feb 2020 22:11:58 +0000 Subject: [PATCH 074/166] Fix CMake issues per Paul R fixes --- cmake/OpenMCConfig.cmake | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmake/OpenMCConfig.cmake b/cmake/OpenMCConfig.cmake index 0bc86fa71..29a0e4542 100644 --- a/cmake/OpenMCConfig.cmake +++ b/cmake/OpenMCConfig.cmake @@ -1,5 +1,8 @@ get_filename_component(OpenMC_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" DIRECTORY) +find_package(fmt REQUIRED HINTS ${OpenMC_CMAKE_DIR}/../fmt) +find_package(gsl-lite REQUIRED HINTS ${OpenMC_CMAKE_DIR}/../gsl-lite) +find_package(pugixml REQUIRED HINTS ${OpenMC_CMAKE_DIR}/../pugixml) find_package(xtl REQUIRED HINTS ${OpenMC_CMAKE_DIR}/../xtl) find_package(xtensor REQUIRED HINTS ${OpenMC_CMAKE_DIR}/../xtensor) From 8154eeed13db2a663464e9a204749a6d0bb67c10 Mon Sep 17 00:00:00 2001 From: stevendargaville Date: Thu, 13 Feb 2020 14:41:40 +0000 Subject: [PATCH 075/166] Revert "Modified the StatePoint output to include the new max_lost_particles and relative version. Further modified an existing regression test to check the StatePoint write-out of these variables" This reverts commit b6a03949c82aee90824b9daf60d3d74a587b5b19. --- src/state_point.cpp | 4 ---- tests/regression_tests/photon_source/results_true.dat | 2 -- tests/regression_tests/photon_source/test.py | 2 -- 3 files changed, 8 deletions(-) diff --git a/src/state_point.cpp b/src/state_point.cpp index a9213bcb4..60fd3826d 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -93,8 +93,6 @@ openmc_statepoint_write(const char* filename, bool* write_source) write_attribute(file_id, "photon_transport", settings::photon_transport); write_dataset(file_id, "n_particles", settings::n_particles); write_dataset(file_id, "n_batches", settings::n_batches); - write_dataset(file_id, "n_max_lost_particles", settings::n_max_lost_particles); - write_dataset(file_id, "relative_max_lost_particles", settings::relative_max_lost_particles); // Write out current batch number write_dataset(file_id, "current_batch", simulation::current_batch); @@ -375,8 +373,6 @@ void load_state_point() read_dataset(file_id, "n_particles", settings::n_particles); int temp; read_dataset(file_id, "n_batches", temp); - read_dataset(file_id, "n_max_lost_particles", settings::n_max_lost_particles); - read_dataset(file_id, "relative_max_lost_particles", settings::relative_max_lost_particles); // Take maximum of statepoint n_batches and input n_batches settings::n_batches = std::max(settings::n_batches, temp); diff --git a/tests/regression_tests/photon_source/results_true.dat b/tests/regression_tests/photon_source/results_true.dat index ab6ac9ff8..1448ad6db 100644 --- a/tests/regression_tests/photon_source/results_true.dat +++ b/tests/regression_tests/photon_source/results_true.dat @@ -1,5 +1,3 @@ tally 1: sum = 2.275713E+02 sum_sq = 5.178870E+04 -max_lost = 5.000000E+00 -rel_max_lost = 1.000000E-01 diff --git a/tests/regression_tests/photon_source/test.py b/tests/regression_tests/photon_source/test.py index c5c42d680..09a0b4a2d 100644 --- a/tests/regression_tests/photon_source/test.py +++ b/tests/regression_tests/photon_source/test.py @@ -54,8 +54,6 @@ class SourceTestHarness(PyAPITestHarness): outstr += 'tally {}:\n'.format(t.id) outstr += 'sum = {:12.6E}\n'.format(t.sum[0, 0, 0]) outstr += 'sum_sq = {:12.6E}\n'.format(t.sum_sq[0, 0, 0]) - outstr += 'max_lost = {:12.6E}\n'.format(sp.n_max_lost_particles) - outstr += 'rel_max_lost = {:12.6E}\n'.format(sp.relative_max_lost_particles) return outstr From 68312dd9818cecd8129206b4a1a9d816432169e5 Mon Sep 17 00:00:00 2001 From: stevendargaville Date: Thu, 13 Feb 2020 14:43:31 +0000 Subject: [PATCH 076/166] Revert "Modified an existing regression test to include the new max_lost_particles and relative version. This is mainly a test that the python/xml input/output routines work through the comparison the regression test harness does in this test, neither of these trigger the end of the simulation." This reverts commit 9224ca318675fcbb06c8eb97d9569466874dda21. --- tests/regression_tests/photon_source/inputs_true.dat | 2 -- tests/regression_tests/photon_source/test.py | 2 -- 2 files changed, 4 deletions(-) diff --git a/tests/regression_tests/photon_source/inputs_true.dat b/tests/regression_tests/photon_source/inputs_true.dat index 83c909ac6..89f4de0e0 100644 --- a/tests/regression_tests/photon_source/inputs_true.dat +++ b/tests/regression_tests/photon_source/inputs_true.dat @@ -18,8 +18,6 @@ fixed source 10000 1 - 5 - 0.1 0 0 0 diff --git a/tests/regression_tests/photon_source/test.py b/tests/regression_tests/photon_source/test.py index 09a0b4a2d..94b028003 100644 --- a/tests/regression_tests/photon_source/test.py +++ b/tests/regression_tests/photon_source/test.py @@ -31,8 +31,6 @@ class SourceTestHarness(PyAPITestHarness): settings = openmc.Settings() settings.particles = 10000 settings.batches = 1 - settings.max_lost_particles = 5 - settings.rel_max_lost_particles = 0.1 settings.photon_transport = True settings.electron_treatment = 'ttb' settings.cutoff = {'energy_photon' : 1000.0} From 18b469bfa5cc4b4d1280655e9166c997db267481 Mon Sep 17 00:00:00 2001 From: stevendargaville Date: Thu, 13 Feb 2020 14:44:53 +0000 Subject: [PATCH 077/166] Removed new variables from the statepoint python file as they are no longer read out --- openmc/statepoint.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/openmc/statepoint.py b/openmc/statepoint.py index 0d78bf68d..a8e13efd2 100644 --- a/openmc/statepoint.py +++ b/openmc/statepoint.py @@ -77,10 +77,6 @@ class StatePoint(object): Number of batches n_inactive : int Number of inactive batches - n_max_lost_particles : int - Number of max lost particles - relative_max_lost_particles : float - Number of max lost particles, relative to the total number of particles n_particles : int Number of particles per generation n_realizations : int @@ -316,10 +312,6 @@ class StatePoint(object): else: return None - @property - def n_max_lost_particles(self): - return self._f['n_max_lost_particles'][()] - @property def relative_max_lost_particles(self): return self._f['relative_max_lost_particles'][()] From a9d1e37aba4aeadb683947861bb0a392c364e878 Mon Sep 17 00:00:00 2001 From: stevendargaville Date: Thu, 13 Feb 2020 14:46:34 +0000 Subject: [PATCH 078/166] Changed the name of the max_lost_particles in the C++ layer to match the python side --- include/openmc/settings.h | 2 +- openmc/lib/settings.py | 2 +- src/particle.cpp | 2 +- src/settings.cpp | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/openmc/settings.h b/include/openmc/settings.h index 3407eadf4..8cee3c72a 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -65,7 +65,7 @@ extern "C" std::string path_statepoint; //!< path to a statepoint file extern "C" int32_t n_batches; //!< number of (inactive+active) batches extern "C" int32_t n_inactive; //!< number of inactive batches -extern "C" int32_t n_max_lost_particles; //!< maximum number of lost particles +extern "C" int32_t max_lost_particles; //!< maximum number of lost particles extern double relative_max_lost_particles; //!< maximum number of lost particles, relative to the total number of particles extern "C" int32_t gen_per_batch; //!< number of generations per batch extern "C" int64_t n_particles; //!< number of particles per generation diff --git a/openmc/lib/settings.py b/openmc/lib/settings.py index 8345e0088..fa72e1323 100644 --- a/openmc/lib/settings.py +++ b/openmc/lib/settings.py @@ -22,7 +22,7 @@ class _Settings(object): entropy_on = _DLLGlobal(c_bool, 'entropy_on') generations_per_batch = _DLLGlobal(c_int32, 'gen_per_batch') inactive = _DLLGlobal(c_int32, 'n_inactive') - max_lost_particles = _DLLGlobal(c_int32, 'n_max_lost_particles') + max_lost_particles = _DLLGlobal(c_int32, 'max_lost_particles') rel_max_lost_particles = _DLLGlobal(c_double, 'relative_max_lost_particles') particles = _DLLGlobal(c_int64, 'n_particles') restart_run = _DLLGlobal(c_bool, 'restart_run') diff --git a/src/particle.cpp b/src/particle.cpp index 79e662577..7e98b2ce6 100644 --- a/src/particle.cpp +++ b/src/particle.cpp @@ -630,7 +630,7 @@ Particle::mark_as_lost(const char* message) // Abort the simulation if the maximum number of lost particles has been // reached - if (simulation::n_lost_particles >= settings::n_max_lost_particles && + if (simulation::n_lost_particles >= settings::max_lost_particles && simulation::n_lost_particles >= settings::relative_max_lost_particles*n) { fatal_error("Maximum number of lost particles has been reached."); } diff --git a/src/settings.cpp b/src/settings.cpp index d72e5f17f..933f9a217 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -79,7 +79,7 @@ std::string path_statepoint; int32_t n_batches; int32_t n_inactive {0}; -int32_t n_max_lost_particles {10}; +int32_t max_lost_particles {10}; double relative_max_lost_particles {1.0e-6}; int32_t gen_per_batch {1}; int64_t n_particles {-1}; @@ -147,7 +147,7 @@ void get_run_parameters(pugi::xml_node node_base) // Get max number of lost particles if (check_for_node(node_base, "max_lost_particles")) { - n_max_lost_particles = std::stoi(get_node_value(node_base, "max_lost_particles")); + max_lost_particles = std::stoi(get_node_value(node_base, "max_lost_particles")); } // Get relative number of lost particles @@ -362,7 +362,7 @@ void read_settings_xml() fatal_error("Number of inactive batches must be non-negative."); } else if (n_particles <= 0) { fatal_error("Number of particles must be greater than zero."); - } else if (n_max_lost_particles <= 0) { + } else if (max_lost_particles <= 0) { fatal_error("Number of max lost particles must be greater than zero."); } else if (relative_max_lost_particles <= 0.0 || relative_max_lost_particles >= 1.0) { fatal_error("Relative max lost particles must be between zero and one."); From a9fc400863952502bad61fd73bb80993448afc74 Mon Sep 17 00:00:00 2001 From: stevendargaville Date: Thu, 13 Feb 2020 14:51:32 +0000 Subject: [PATCH 079/166] Also removed relative_max_lost_particles from statepoint python as it is not being written out --- openmc/statepoint.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/openmc/statepoint.py b/openmc/statepoint.py index a8e13efd2..84512d70e 100644 --- a/openmc/statepoint.py +++ b/openmc/statepoint.py @@ -310,11 +310,7 @@ class StatePoint(object): if self.run_mode == 'eigenvalue': return self._f['n_inactive'][()] else: - return None - - @property - def relative_max_lost_particles(self): - return self._f['relative_max_lost_particles'][()] + return None @property def n_particles(self): From d763806c43ea05adb25831b8fd11c828fd40d06a Mon Sep 17 00:00:00 2001 From: stevendargaville Date: Thu, 13 Feb 2020 14:52:44 +0000 Subject: [PATCH 080/166] Also made rel_max_lost_particle name consistent between C++ and python layers --- include/openmc/settings.h | 2 +- openmc/lib/settings.py | 2 +- src/particle.cpp | 2 +- src/settings.cpp | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/openmc/settings.h b/include/openmc/settings.h index 8cee3c72a..2b4203646 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -66,7 +66,7 @@ extern "C" std::string path_statepoint; //!< path to a statepoint file extern "C" int32_t n_batches; //!< number of (inactive+active) batches extern "C" int32_t n_inactive; //!< number of inactive batches extern "C" int32_t max_lost_particles; //!< maximum number of lost particles -extern double relative_max_lost_particles; //!< maximum number of lost particles, relative to the total number of particles +extern double rel_max_lost_particles; //!< maximum number of lost particles, relative to the total number of particles extern "C" int32_t gen_per_batch; //!< number of generations per batch extern "C" int64_t n_particles; //!< number of particles per generation diff --git a/openmc/lib/settings.py b/openmc/lib/settings.py index fa72e1323..68d68c29e 100644 --- a/openmc/lib/settings.py +++ b/openmc/lib/settings.py @@ -23,7 +23,7 @@ class _Settings(object): generations_per_batch = _DLLGlobal(c_int32, 'gen_per_batch') inactive = _DLLGlobal(c_int32, 'n_inactive') max_lost_particles = _DLLGlobal(c_int32, 'max_lost_particles') - rel_max_lost_particles = _DLLGlobal(c_double, 'relative_max_lost_particles') + rel_max_lost_particles = _DLLGlobal(c_double, 'rel_max_lost_particles') particles = _DLLGlobal(c_int64, 'n_particles') restart_run = _DLLGlobal(c_bool, 'restart_run') run_CE = _DLLGlobal(c_bool, 'run_CE') diff --git a/src/particle.cpp b/src/particle.cpp index 7e98b2ce6..8f6dba803 100644 --- a/src/particle.cpp +++ b/src/particle.cpp @@ -631,7 +631,7 @@ Particle::mark_as_lost(const char* message) // Abort the simulation if the maximum number of lost particles has been // reached if (simulation::n_lost_particles >= settings::max_lost_particles && - simulation::n_lost_particles >= settings::relative_max_lost_particles*n) { + simulation::n_lost_particles >= settings::rel_max_lost_particles*n) { fatal_error("Maximum number of lost particles has been reached."); } } diff --git a/src/settings.cpp b/src/settings.cpp index 933f9a217..706a17145 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -80,7 +80,7 @@ std::string path_statepoint; int32_t n_batches; int32_t n_inactive {0}; int32_t max_lost_particles {10}; -double relative_max_lost_particles {1.0e-6}; +double rel_max_lost_particles {1.0e-6}; int32_t gen_per_batch {1}; int64_t n_particles {-1}; @@ -152,7 +152,7 @@ void get_run_parameters(pugi::xml_node node_base) // Get relative number of lost particles if (check_for_node(node_base, "rel_max_lost_particles")) { - relative_max_lost_particles = std::stod(get_node_value(node_base, "rel_max_lost_particles")); + rel_max_lost_particles = std::stod(get_node_value(node_base, "rel_max_lost_particles")); } // Get number of inactive batches @@ -364,7 +364,7 @@ void read_settings_xml() fatal_error("Number of particles must be greater than zero."); } else if (max_lost_particles <= 0) { fatal_error("Number of max lost particles must be greater than zero."); - } else if (relative_max_lost_particles <= 0.0 || relative_max_lost_particles >= 1.0) { + } else if (rel_max_lost_particles <= 0.0 || rel_max_lost_particles >= 1.0) { fatal_error("Relative max lost particles must be between zero and one."); } } From 6d6988b2c8cb2ce611234e1ce546580f1cbacb82 Mon Sep 17 00:00:00 2001 From: Ethan Peterson Date: Mon, 10 Feb 2020 14:15:28 -0500 Subject: [PATCH 081/166] refactoring surface classes --- openmc/surface.py | 715 +++++++++++++++++++++++++++------------------- 1 file changed, 423 insertions(+), 292 deletions(-) diff --git a/openmc/surface.py b/openmc/surface.py index 55e3f459d..d64ca242f 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -1,5 +1,6 @@ from abc import ABCMeta, abstractmethod from collections import OrderedDict +from abc.collections import Iterable from copy import deepcopy from numbers import Real, Integral from xml.etree import ElementTree as ET @@ -7,7 +8,7 @@ from warnings import warn import numpy as np -from openmc.checkvalue import check_type, check_value +from openmc.checkvalue import check_type, check_value, check_length from openmc.region import Region, Intersection, Union from openmc.mixin import IDManagerMixin @@ -187,6 +188,17 @@ class Surface(IDManagerMixin, metaclass=ABCMeta): def translate(self, vector): pass + @classmethod + def get_subclasses(cls): + """Recursively find all subclasses of this class""" + return set(cls.__subclasses__()).union([s for c in cls.__subclasses__() + for s in get_subclasses(c)]) + + @classmethod + def get_subclass_map(cls): + """Generate mapping of class _type attributes to classes""" + return {c._type: c for c in cls.get_subclasses()} + def to_xml_element(self): """Return XML representation of the surface @@ -228,21 +240,7 @@ class Surface(IDManagerMixin, metaclass=ABCMeta): # Determine appropriate class surf_type = elem.get('type') - surface_classes = { - 'plane': Plane, - 'x-plane': XPlane, - 'y-plane': YPlane, - 'z-plane': ZPlane, - 'x-cylinder': XCylinder, - 'y-cylinder': YCylinder, - 'z-cylinder': ZCylinder, - 'sphere': Sphere, - 'x-cone': XCone, - 'y-cone': YCone, - 'z-cone': ZCone, - 'quadric': Quadric, - } - cls = surface_classes[surf_type] + cls = _SURFACE_CLASSES[surf_type] # Determine ID, boundary type, coefficients kwargs = {} @@ -268,62 +266,215 @@ class Surface(IDManagerMixin, metaclass=ABCMeta): Instance of surface subclass """ + surface_id = int(group.name.split('/')[-1].lstrip('surface ')) name = group['name'][()].decode() if 'name' in group else '' surf_type = group['type'][()].decode() bc = group['boundary_type'][()].decode() coeffs = group['coefficients'][...] - # Create the Surface based on its type - if surf_type == 'x-plane': - x0 = coeffs[0] - surface = XPlane(x0, bc, name, surface_id) + cls = _SURFACE_CLASSES[surf_type] - elif surf_type == 'y-plane': - y0 = coeffs[0] - surface = YPlane(y0, bc, name, surface_id) - - elif surf_type == 'z-plane': - z0 = coeffs[0] - surface = ZPlane(z0, bc, name, surface_id) - - elif surf_type == 'plane': - A, B, C, D = coeffs - surface = Plane(A, B, C, D, bc, name, surface_id) - - elif surf_type == 'x-cylinder': - y0, z0, r = coeffs - surface = XCylinder(y0, z0, r, bc, name, surface_id) - - elif surf_type == 'y-cylinder': - x0, z0, r = coeffs - surface = YCylinder(x0, z0, r, bc, name, surface_id) - - elif surf_type == 'z-cylinder': - x0, y0, r = coeffs - surface = ZCylinder(x0, y0, r, bc, name, surface_id) - - elif surf_type == 'sphere': - x0, y0, z0, r = coeffs - surface = Sphere(x0, y0, z0, r, bc, name, surface_id) - - elif surf_type in ['x-cone', 'y-cone', 'z-cone']: - x0, y0, z0, r2 = coeffs - if surf_type == 'x-cone': - surface = XCone(x0, y0, z0, r2, bc, name, surface_id) - elif surf_type == 'y-cone': - surface = YCone(x0, y0, z0, r2, bc, name, surface_id) - elif surf_type == 'z-cone': - surface = ZCone(x0, y0, z0, r2, bc, name, surface_id) - - elif surf_type == 'quadric': - a, b, c, d, e, f, g, h, j, k = coeffs - surface = Quadric(a, b, c, d, e, f, g, h, j, k, bc, name, surface_id) - - return surface + return cls(*coeffs, bc, name, surface_id) -class Plane(Surface): +_SURFACE_CLASSES = Surface.get_subclass_map() + + +class PlaneMeta(metaclass=ABCMeta): + """A Plane Meta class for all operations on order 1 surfaces""" + + def __new__(cls, *args, **kwargs): + """Simplify this plane if possible to an XPlane, YPlane, or ZPlane""" + if cls is Plane: + (a,b,c,d,bt, name, surfidlen(args) + if np.all(np.isclose((self.b, self.c), 0., atol=atol)): + x0 = self.d / self.a + return XPlane(x0=x0, boundary_type=self.boundary_type, + name=self.name, surface_id=self.id) + + elif np.all(np.isclose((self.a, self.c), 0., atol=atol)): + y0 = self.d / self.b + return YPlane(y0=y0, boundary_type=self.boundary_type, + name=self.name, surface_id=self.id) + + elif np.all(np.isclose((self.a, self.b), 0., atol=atol)): + z0 = self.d / self.c + return ZPlane(z0=z0, boundary_type=self.boundary_type, + name=self.name, surface_id=self.id) + + def __init__(self): + pass + + @property + def periodic_surface(self): + return self._periodic_surface + + @periodic_surface.setter + def periodic_surface(self, periodic_surface): + check_type('periodic surface', periodic_surface, Plane) + self._periodic_surface = periodic_surface + periodic_surface._periodic_surface = self + + @abstractmethod + def _get_base_coeffs(self): + pass + + @abstractmethod + def _update_from_base_coeffs(self): + pass + + def _neg_bounds(self): + return (np.array([-np.inf, -np.inf, -np.inf]), + np.array([np.inf, np.inf, np.inf])) + + def _pos_bounds(self): + return (np.array([-np.inf, -np.inf, -np.inf]), + np.array([np.inf, np.inf, np.inf])) + + def evaluate(self, point): + """Evaluate the surface equation at a given point. + + Parameters + ---------- + point : 3-tuple of float + The Cartesian coordinates, :math:`(x',y',z')`, at which the surface + equation should be evaluated. + + Returns + ------- + float + :math:`Ax' + By' + Cz' - D` + + """ + + x, y, z = point + a, b, c, d = self._get_base_coeffs() + return a*x + b*y + c*z - d + + def translate(self, vector, clone=False): + """Translate surface in given direction + + Parameters + ---------- + vector : iterable of float + Direction in which surface should be translated + clone : boolean + Whether or not to return a new instance of a Plane or to modify the + coefficients of this plane. + + Returns + ------- + openmc.Plane + Translated surface + + """ + vx, vy, vz = vector + a, b, c, d = self._get_base_coeffs() + d = d + a*vx + b*vy + c*vz + + if clone: + surf = self.clone() + else: + surf = self + + surf._update_from_base_coeffs(a, b, c, d) + return surf + + def rotate(self, rotation, frame='lab', clone=False): + """Rotate surface by given Tait-Bryan angles + + Parameters + ---------- + rotation : iterable of float + Intrinsic Tait-Bryan angles in degrees used to rotate the surface + frame : str, one of 'lab' or 'body' + clone : boolean + Whether or not to return a new instance of a Plane or to modify the + coefficients of this plane. + + Returns + ------- + openmc.Plane or None + Rotated surface + + """ + + check_type('surface rotation', rotation, Iterable, Real) + check_length('surface rotation', rotation, 3) + + # Calculate rotation matrix Rmat from angles phi, theta, psi + phi, theta, psi = rotation*(-np.pi/180.) + c3, s3 = np.cos(phi), np.sin(phi) + c2, s2 = np.cos(theta), np.sin(theta) + c1, s1 = np.cos(psi), np.sin(psi) + Rmat = np.array([[c1*c2, c1*s2*s3 - c3*s1, s1*s3 + c1*c3*s2], + [c2*s1, c1*c3 + s1*s2*s3, c3*s1*s2 - c1*s3], + [-s2, c2*s3, c2*c3]]) + + a, b, c, d = self._get_base_coeffs() + bvec = np.array([a, b, c]) + + # Compute new rotated coefficients a, b, c + a, b, c = np.dot(bvec.T, Rmat.T) + + if clone: + surf = self.clone() + else: + surf = self + + surf._update_from_base_coeffs(a, b, c, d) + return surf + + def bounding_box(self, side): + """Determine an axis-aligned bounding box. + + An axis-aligned bounding box for surface half-spaces is represented by + its lower-left and upper-right coordinates. For the z-plane surface, the + half-spaces are unbounded in their x- and y- directions. To represent + infinity, numpy.inf is used. + + 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 + + """ + + if side == '-': + return self._neg_bounds() + elif side == '+': + return self._pos_bounds() + + def to_xml_element(self): + """Return XML representation of the surface + + Returns + ------- + element : xml.etree.ElementTree.Element + XML element containing source data + + """ + element = super().to_xml_element() + + # Add periodic surface pair information + if self.boundary_type == 'periodic': + if self.periodic_surface is not None: + element.set("periodic_surface_id", + str(self.periodic_surface.id)) + return element + + +class Plane(PlaneMeta, Surface): """An arbitrary plane of the form :math:`Ax + By + Cz = D`. Parameters @@ -406,10 +557,6 @@ class Plane(Surface): def d(self): return self.coefficients['d'] - @property - def periodic_surface(self): - return self._periodic_surface - @a.setter def a(self, a): check_type('A coefficient', a, Real) @@ -430,68 +577,15 @@ class Plane(Surface): check_type('D coefficient', d, Real) self._coefficients['d'] = d - @periodic_surface.setter - def periodic_surface(self, periodic_surface): - check_type('periodic surface', periodic_surface, Plane) - 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 evaluate(self, point): - """Evaluate the surface equation at a given point. - - Parameters - ---------- - point : 3-tuple of float - The Cartesian coordinates, :math:`(x',y',z')`, at which the surface - equation should be evaluated. - - Returns - ------- - float - :math:`Ax' + By' + Cz' - D` - - """ - - x, y, z = point - return self.a*x + self.b*y + self.c*z - self.d - - def translate(self, vector): - """Translate surface in given direction - - Parameters - ---------- - vector : iterable of float - Direction in which surface should be translated - - Returns - ------- - openmc.Plane - Translated surface - - """ - vx, vy, vz = vector - d = self.d + self.a*vx + self.b*vy + self.c*vz - if d == self.d: - return self - else: - return type(self)(a=self.a, b=self.b, c=self.c, d=d) - - def to_xml_element(self): - """Return XML representation of the surface - - Returns - ------- - element : xml.etree.ElementTree.Element - XML element containing source data - - """ - element = super().to_xml_element() - - # Add periodic surface pair information - if self.boundary_type == 'periodic': - if self.periodic_surface is not None: - element.set("periodic_surface_id", str(self.periodic_surface.id)) - return element + def _update_from_base_coeffs(self, coeffs): + a, b, c, d = coeffs + self.a = a + self.b = b + self.c = c + self.d = d @classmethod def from_points(cls, p1, p2, p3, **kwargs): @@ -526,7 +620,7 @@ class Plane(Surface): return cls(a=a, b=b, c=c, d=d, **kwargs) -class XPlane(Plane): +class XPlane(PlaneMeta, Surface): """A plane perpendicular to the x axis of the form :math:`x - x_0 = 0` Parameters @@ -582,76 +676,25 @@ class XPlane(Plane): check_type('x0 coefficient', x0, Real) self._coefficients['x0'] = x0 - def bounding_box(self, side): - """Determine an axis-aligned bounding box. + def _get_base_coeffs(self): + return (1., 0., 0., self.x0) - An axis-aligned bounding box for surface half-spaces is represented by - its lower-left and upper-right coordinates. For the x-plane surface, the - half-spaces are unbounded in their y- and z- directions. To represent - infinity, numpy.inf is used. + def _update_from_base_coeffs(self, coeffs): + a, b, c, d = coeffs + self.x0 = d - Parameters - ---------- - side : {'+', '-'} - Indicates the negative or positive half-space + def _neg_bounds(self): + """Return the lower and upper bounds of the negative half space""" + return (np.array([-np.inf, -np.inf, -np.inf]), + np.array([self.x0, np.inf, np.inf])) - 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 - - """ - - 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): - """Evaluate the surface equation at a given point. - - Parameters - ---------- - point : 3-tuple of float - The Cartesian coordinates, :math:`(x',y',z')`, at which the surface - equation should be evaluated. - - Returns - ------- - float - :math:`x' - x_0` - - """ - return point[0] - self.x0 - - def translate(self, vector): - """Translate surface in given direction - - Parameters - ---------- - vector : iterable of float - Direction in which surface should be translated - - Returns - ------- - openmc.XPlane - Translated surface - - """ - vx = vector[0] - if vx == 0: - return self - else: - return type(self)(x0=self.x0 + vx) + def _pos_bounds(self): + """Return the lower and upper bounds of the positive half space""" + return (np.array([self.x0, -np.inf, -np.inf]), + np.array([np.inf, np.inf, np.inf])) -class YPlane(Plane): +class YPlane(PlaneMeta, Surface): """A plane perpendicular to the y axis of the form :math:`y - y_0 = 0` Parameters @@ -707,76 +750,26 @@ class YPlane(Plane): check_type('y0 coefficient', y0, Real) self._coefficients['y0'] = y0 - def bounding_box(self, side): - """Determine an axis-aligned bounding box. + def _get_base_coeffs(self): + """Return generalized coefficients for a plane""" + return (0., 1., 0., self.y0) - An axis-aligned bounding box for surface half-spaces is represented by - its lower-left and upper-right coordinates. For the y-plane surface, the - half-spaces are unbounded in their x- and z- directions. To represent - infinity, numpy.inf is used. + def _update_from_base_coeffs(self, coeffs): + a, b, c, d = coeffs + self.y0 = d - Parameters - ---------- - side : {'+', '-'} - Indicates the negative or positive half-space + def _neg_bounds(self): + """Return the lower and upper bounds of the negative half space""" + return (np.array([-np.inf, -np.inf, -np.inf]), + np.array([np.inf, self.y0, np.inf])) - 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 - - """ - - 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): - """Evaluate the surface equation at a given point. - - Parameters - ---------- - point : 3-tuple of float - The Cartesian coordinates, :math:`(x',y',z')`, at which the surface - equation should be evaluated. - - Returns - ------- - float - :math:`y' - y_0` - - """ - return point[1] - self.y0 - - def translate(self, vector): - """Translate surface in given direction - - Parameters - ---------- - vector : iterable of float - Direction in which surface should be translated - - Returns - ------- - openmc.YPlane - Translated surface - - """ - vy = vector[1] - if vy == 0.0: - return self - else: - return type(self)(y0=self.y0 + vy) + def _pos_bounds(self): + """Return the lower and upper bounds of the positive half space""" + return (np.array([-np.inf, self.y0, -np.inf]), + np.array([np.inf, np.inf, np.inf])) -class ZPlane(Plane): +class ZPlane(PlaneMeta, Surface): """A plane perpendicular to the z axis of the form :math:`z - z_0 = 0` Parameters @@ -832,36 +825,166 @@ class ZPlane(Plane): check_type('z0 coefficient', z0, Real) self._coefficients['z0'] = z0 - def bounding_box(self, side): - """Determine an axis-aligned bounding box. + def _get_base_coeffs(self): + """Return generalized coefficients for a plane""" + return (0., 0., 1., self.z0) - An axis-aligned bounding box for surface half-spaces is represented by - its lower-left and upper-right coordinates. For the z-plane surface, the - half-spaces are unbounded in their x- and y- directions. To represent - infinity, numpy.inf is used. + def _update_from_base_coeffs(self, coeffs): + a, b, c, d = coeffs + self.z0 = d - Parameters - ---------- - side : {'+', '-'} - Indicates the negative or positive half-space + def _neg_bounds(self): + """Return the lower and upper bounds of the negative half space""" + return (np.array([-np.inf, -np.inf, -np.inf]), + np.array([np.inf, np.inf, self.z0])) - 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 + def _pos_bounds(self): + """Return the lower and upper bounds of the positive half space""" + return (np.array([-np.inf, -np.inf, self.z0]), + np.array([np.inf, np.inf, np.inf])) - """ +class QuadricMeta(metaclass=ABCMeta): + """A surface of the form :math:`Ax^2 + By^2 + Cz^2 + Dxy + Eyz + Fxz + Gx + Hy + + Jz + K = 0`. - 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])) + Parameters + ---------- + a, b, c, d, e, f, g, h, j, k : float, optional + coefficients for the surface. All default to 0. + boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic', 'white'}, optional + Boundary condition that defines the behavior for particles hitting the + surface. Defaults to transmissive boundary condition where particles + freely pass through the surface. + name : str, optional + Name of the surface. If not specified, the name will be the empty string. + surface_id : int, optional + Unique identifier for the surface. If not specified, an identifier will + automatically be assigned. + + Attributes + ---------- + a, b, c, d, e, f, g, h, j, k : float + coefficients for the surface + boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic', 'white'} + Boundary condition that defines the behavior for particles hitting the + surface. + coefficients : dict + Dictionary of surface coefficients + id : int + Unique identifier for the surface + name : str + Name of the surface + type : str + Type of the surface + + """ + + _type = 'quadric' + _coeff_keys = ('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k') + + def __init__(self, a=0., b=0., c=0., d=0., e=0., f=0., g=0., h=0., j=0., + k=0., boundary_type='transmission', name='', surface_id=None): + super().__init__(surface_id, boundary_type, name=name) + self.a = a + self.b = b + self.c = c + self.d = d + self.e = e + self.f = f + self.g = g + self.h = h + self.j = j + self.k = k + + @property + def a(self): + return self.coefficients['a'] + + @property + def b(self): + return self.coefficients['b'] + + @property + def c(self): + return self.coefficients['c'] + + @property + def d(self): + return self.coefficients['d'] + + @property + def e(self): + return self.coefficients['e'] + + @property + def f(self): + return self.coefficients['f'] + + @property + def g(self): + return self.coefficients['g'] + + @property + def h(self): + return self.coefficients['h'] + + @property + def j(self): + return self.coefficients['j'] + + @property + def k(self): + return self.coefficients['k'] + + @a.setter + def a(self, a): + check_type('a coefficient', a, Real) + self._coefficients['a'] = a + + @b.setter + def b(self, b): + check_type('b coefficient', b, Real) + self._coefficients['b'] = b + + @c.setter + def c(self, c): + check_type('c coefficient', c, Real) + self._coefficients['c'] = c + + @d.setter + def d(self, d): + check_type('d coefficient', d, Real) + self._coefficients['d'] = d + + @e.setter + def e(self, e): + check_type('e coefficient', e, Real) + self._coefficients['e'] = e + + @f.setter + def f(self, f): + check_type('f coefficient', f, Real) + self._coefficients['f'] = f + + @g.setter + def g(self, g): + check_type('g coefficient', g, Real) + self._coefficients['g'] = g + + @h.setter + def h(self, h): + check_type('h coefficient', h, Real) + self._coefficients['h'] = h + + @j.setter + def j(self, j): + check_type('j coefficient', j, Real) + self._coefficients['j'] = j + + @k.setter + def k(self, k): + check_type('k coefficient', k, Real) + self._coefficients['k'] = k def evaluate(self, point): """Evaluate the surface equation at a given point. @@ -875,10 +998,14 @@ class ZPlane(Plane): Returns ------- float - :math:`z' - z_0` + :math:`Ax'^2 + By'^2 + Cz'^2 + Dx'y' + Ey'z' + Fx'z' + Gx' + Hy' + + Jz' + K = 0` """ - return point[2] - self.z0 + x, y, z = point + return x*(self.a*x + self.d*y + self.g) + \ + y*(self.b*y + self.e*z + self.h) + \ + z*(self.c*z + self.f*x + self.j) + self.k def translate(self, vector): """Translate surface in given direction @@ -890,15 +1017,19 @@ class ZPlane(Plane): Returns ------- - openmc.ZPlane + openmc.Quadric Translated surface """ - vz = vector[2] - if vz == 0.0: - return self - else: - return type(self)(z0=self.z0 + vz) + vx, vy, vz = vector + a, b, c, d, e, f, g, h, j, k = (getattr(self, key) for key in + self._coeff_keys) + k = (k + vx*vx + vy*vy + vz*vz + d*vx*vy + e*vy*vz + f*vx*vz + - g*vx - h*vy - j*vz) + g = g - 2*a*vx - d*vy - f*vz + h = h - 2*b*vy - d*vx - e*vz + j = j - 2*c*vz - e*vy - f*vx + return type(self)(a=a, b=b, c=c, d=d, e=e, f=f, g=g, h=h, j=j, k=k) class Cylinder(Surface): From 5af686b4fb49b6a9b24cca787a33f3091d26e5e2 Mon Sep 17 00:00:00 2001 From: Ethan Peterson Date: Mon, 10 Feb 2020 17:36:14 -0500 Subject: [PATCH 082/166] working on quadric refactoring --- openmc/surface.py | 382 ++++++++++++++++++---------------------------- 1 file changed, 151 insertions(+), 231 deletions(-) diff --git a/openmc/surface.py b/openmc/surface.py index d64ca242f..de1ec4cc2 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -1,6 +1,6 @@ from abc import ABCMeta, abstractmethod from collections import OrderedDict -from abc.collections import Iterable +from collections.abc import Iterable from copy import deepcopy from numbers import Real, Integral from xml.etree import ElementTree as ET @@ -283,28 +283,30 @@ _SURFACE_CLASSES = Surface.get_subclass_map() class PlaneMeta(metaclass=ABCMeta): """A Plane Meta class for all operations on order 1 surfaces""" +# def __new__(cls, *args, **kwargs): +# for key in cls._coeff_keys, kwargs.pop(key) to get arguments for class +# """Simplify this plane if possible to an XPlane, YPlane, or ZPlane""" +# pass +# +# if cls is Plane: +# if np.all(np.isclose((self.b, self.c), 0., atol=atol)): +# x0 = self.d / self.a +# return XPlane(x0=x0, boundary_type=self.boundary_type, +# name=self.name, surface_id=self.id) +# +# elif np.all(np.isclose((self.a, self.c), 0., atol=atol)): +# y0 = self.d / self.b +# return YPlane(y0=y0, boundary_type=self.boundary_type, +# name=self.name, surface_id=self.id) +# +# elif np.all(np.isclose((self.a, self.b), 0., atol=atol)): +# z0 = self.d / self.c +# return ZPlane(z0=z0, boundary_type=self.boundary_type, +# name=self.name, surface_id=self.id) - def __new__(cls, *args, **kwargs): - """Simplify this plane if possible to an XPlane, YPlane, or ZPlane""" - if cls is Plane: - (a,b,c,d,bt, name, surfidlen(args) - if np.all(np.isclose((self.b, self.c), 0., atol=atol)): - x0 = self.d / self.a - return XPlane(x0=x0, boundary_type=self.boundary_type, - name=self.name, surface_id=self.id) - - elif np.all(np.isclose((self.a, self.c), 0., atol=atol)): - y0 = self.d / self.b - return YPlane(y0=y0, boundary_type=self.boundary_type, - name=self.name, surface_id=self.id) - - elif np.all(np.isclose((self.a, self.b), 0., atol=atol)): - z0 = self.d / self.c - return ZPlane(z0=z0, boundary_type=self.boundary_type, - name=self.name, surface_id=self.id) - - def __init__(self): - pass + def __init__(self, **kwargs): + super().__init__(**kwargs) + self._periodic_surface = None @property def periodic_surface(self): @@ -381,51 +383,6 @@ class PlaneMeta(metaclass=ABCMeta): surf._update_from_base_coeffs(a, b, c, d) return surf - def rotate(self, rotation, frame='lab', clone=False): - """Rotate surface by given Tait-Bryan angles - - Parameters - ---------- - rotation : iterable of float - Intrinsic Tait-Bryan angles in degrees used to rotate the surface - frame : str, one of 'lab' or 'body' - clone : boolean - Whether or not to return a new instance of a Plane or to modify the - coefficients of this plane. - - Returns - ------- - openmc.Plane or None - Rotated surface - - """ - - check_type('surface rotation', rotation, Iterable, Real) - check_length('surface rotation', rotation, 3) - - # Calculate rotation matrix Rmat from angles phi, theta, psi - phi, theta, psi = rotation*(-np.pi/180.) - c3, s3 = np.cos(phi), np.sin(phi) - c2, s2 = np.cos(theta), np.sin(theta) - c1, s1 = np.cos(psi), np.sin(psi) - Rmat = np.array([[c1*c2, c1*s2*s3 - c3*s1, s1*s3 + c1*c3*s2], - [c2*s1, c1*c3 + s1*s2*s3, c3*s1*s2 - c1*s3], - [-s2, c2*s3, c2*c3]]) - - a, b, c, d = self._get_base_coeffs() - bvec = np.array([a, b, c]) - - # Compute new rotated coefficients a, b, c - a, b, c = np.dot(bvec.T, Rmat.T) - - if clone: - surf = self.clone() - else: - surf = self - - surf._update_from_base_coeffs(a, b, c, d) - return surf - def bounding_box(self, side): """Determine an axis-aligned bounding box. @@ -527,15 +484,18 @@ class Plane(PlaneMeta, Surface): _type = 'plane' _coeff_keys = ('a', 'b', 'c', 'd') - def __init__(self, a=1., b=0., c=0., d=0., boundary_type='transmission', - name='', surface_id=None, **kwargs): - super().__init__(surface_id, boundary_type, name=name) - self._periodic_surface = None + def __init__(self, a=1., b=0., c=0., d=0., **kwargs): + # work around until capital letter kwargs are deprecated + oldkwargs = deepcopy(kwargs) + for k in 'ABCD': + kwargs.pop(k, None) + + super().__init__(**kwargs) self.a = a self.b = b self.c = c self.d = d - for k, v in kwargs.items(): + for k, v in oldkwargs.items(): if k in 'ABCD': warn(_WARNING_UPPER.format(type(self).__name__, k.lower(), k), FutureWarning) @@ -662,9 +622,8 @@ class XPlane(PlaneMeta, Surface): _type = 'x-plane' _coeff_keys = ('x0',) - def __init__(self, x0=0., boundary_type='transmission', - name='', surface_id=None): - super().__init__(surface_id=surface_id, boundary_type=boundary_type, name=name) + def __init__(self, x0=0., **kwargs): + super().__init__(**kwargs) self.x0 = x0 @property @@ -736,9 +695,8 @@ class YPlane(PlaneMeta, Surface): _type = 'y-plane' _coeff_keys = ('y0',) - def __init__(self, y0=0., boundary_type='transmission', - name='', surface_id=None): - super().__init__(surface_id=surface_id, boundary_type=boundary_type, name=name) + def __init__(self, y0=0., **kwargs): + super().__init__(**kwargs) self.y0 = y0 @property @@ -811,9 +769,8 @@ class ZPlane(PlaneMeta, Surface): _type = 'z-plane' _coeff_keys = ('z0',) - def __init__(self, z0=0., boundary_type='transmission', - name='', surface_id=None): - super().__init__(surface_id=surface_id, boundary_type=boundary_type, name=name) + def __init__(self, z0=0., **kwargs): + super().__init__(**kwargs) self.z0 = z0 @property @@ -843,148 +800,28 @@ class ZPlane(PlaneMeta, Surface): return (np.array([-np.inf, -np.inf, self.z0]), np.array([np.inf, np.inf, np.inf])) + class QuadricMeta(metaclass=ABCMeta): - """A surface of the form :math:`Ax^2 + By^2 + Cz^2 + Dxy + Eyz + Fxz + Gx + Hy + - Jz + K = 0`. + """A Meta class implementing common functionality for quadric surfaces""" - Parameters - ---------- - a, b, c, d, e, f, g, h, j, k : float, optional - coefficients for the surface. All default to 0. - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic', 'white'}, optional - Boundary condition that defines the behavior for particles hitting the - surface. Defaults to transmissive boundary condition where particles - freely pass through the surface. - name : str, optional - Name of the surface. If not specified, the name will be the empty string. - surface_id : int, optional - Unique identifier for the surface. If not specified, an identifier will - automatically be assigned. + def __init__(self, **kwargs): + super().__init__(**kwargs) - Attributes - ---------- - a, b, c, d, e, f, g, h, j, k : float - coefficients for the surface - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic', 'white'} - Boundary condition that defines the behavior for particles hitting the - surface. - coefficients : dict - Dictionary of surface coefficients - id : int - Unique identifier for the surface - name : str - Name of the surface - type : str - Type of the surface + @abstractmethod + def _get_base_coeffs(self): + pass - """ + @abstractmethod + def _update_from_base_coeffs(self): + pass - _type = 'quadric' - _coeff_keys = ('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k') + def _neg_bounds(self): + return (np.array([-np.inf, -np.inf, -np.inf]), + np.array([np.inf, np.inf, np.inf])) - def __init__(self, a=0., b=0., c=0., d=0., e=0., f=0., g=0., h=0., j=0., - k=0., boundary_type='transmission', name='', surface_id=None): - super().__init__(surface_id, boundary_type, name=name) - self.a = a - self.b = b - self.c = c - self.d = d - self.e = e - self.f = f - self.g = g - self.h = h - self.j = j - self.k = k - - @property - def a(self): - return self.coefficients['a'] - - @property - def b(self): - return self.coefficients['b'] - - @property - def c(self): - return self.coefficients['c'] - - @property - def d(self): - return self.coefficients['d'] - - @property - def e(self): - return self.coefficients['e'] - - @property - def f(self): - return self.coefficients['f'] - - @property - def g(self): - return self.coefficients['g'] - - @property - def h(self): - return self.coefficients['h'] - - @property - def j(self): - return self.coefficients['j'] - - @property - def k(self): - return self.coefficients['k'] - - @a.setter - def a(self, a): - check_type('a coefficient', a, Real) - self._coefficients['a'] = a - - @b.setter - def b(self, b): - check_type('b coefficient', b, Real) - self._coefficients['b'] = b - - @c.setter - def c(self, c): - check_type('c coefficient', c, Real) - self._coefficients['c'] = c - - @d.setter - def d(self, d): - check_type('d coefficient', d, Real) - self._coefficients['d'] = d - - @e.setter - def e(self, e): - check_type('e coefficient', e, Real) - self._coefficients['e'] = e - - @f.setter - def f(self, f): - check_type('f coefficient', f, Real) - self._coefficients['f'] = f - - @g.setter - def g(self, g): - check_type('g coefficient', g, Real) - self._coefficients['g'] = g - - @h.setter - def h(self, h): - check_type('h coefficient', h, Real) - self._coefficients['h'] = h - - @j.setter - def j(self, j): - check_type('j coefficient', j, Real) - self._coefficients['j'] = j - - @k.setter - def k(self, k): - check_type('k coefficient', k, Real) - self._coefficients['k'] = k + def _pos_bounds(self): + return (np.array([-np.inf, -np.inf, -np.inf]), + np.array([np.inf, np.inf, np.inf])) def evaluate(self, point): """Evaluate the surface equation at a given point. @@ -1003,11 +840,10 @@ class QuadricMeta(metaclass=ABCMeta): """ x, y, z = point - return x*(self.a*x + self.d*y + self.g) + \ - y*(self.b*y + self.e*z + self.h) + \ - z*(self.c*z + self.f*x + self.j) + self.k + a, b, c, d, e, f, g, h, j, k = self._get_base_coeffs() + return x*(a*x + d*y + g) + y*(b*y + e*z + h) + z*(c*z + f*x + j) + k - def translate(self, vector): + def translate(self, vector, clone=False): """Translate surface in given direction Parameters @@ -1022,18 +858,23 @@ class QuadricMeta(metaclass=ABCMeta): """ vx, vy, vz = vector - a, b, c, d, e, f, g, h, j, k = (getattr(self, key) for key in - self._coeff_keys) + a, b, c, d, e, f, g, h, j, k = self._get_base_coeffs() k = (k + vx*vx + vy*vy + vz*vz + d*vx*vy + e*vy*vz + f*vx*vz - g*vx - h*vy - j*vz) g = g - 2*a*vx - d*vy - f*vz h = h - 2*b*vy - d*vx - e*vz j = j - 2*c*vz - e*vy - f*vx - return type(self)(a=a, b=b, c=c, d=d, e=e, f=f, g=g, h=h, j=j, k=k) + if clone: + surf = self.clone() + else: + surf = self + + surf._update_from_base_coeffs((a, b, c, d, e, f, g, h, j, k)) + return surf -class Cylinder(Surface): - """A cylinder whose length is parallel to the x-, y-, or z-axis. +class Cylinder(QuadricMeta, Surface): + """A cylinder Parameters ---------- @@ -1067,22 +908,90 @@ class Cylinder(Surface): Type of the surface """ - def __init__(self, r=1., boundary_type='transmission', - name='', surface_id=None): - super().__init__(surface_id, boundary_type, name=name) + _type = 'cylinder' + _coeff_keys = ('x0', 'y0', 'z0', 'r', 'u', 'v','w') + def __init__(self, x0=0., y0=0., z0=0. r=1., u=0., v=0., w=1., **kwargs): + super().__init__(**kwargs) + self.x0 = x0 + self.y0 = y0 + self.z0 = z0 self.r = r + self.u = u + self.v = v + self.w = w + + @property + def x0(self): + return self.coefficients['x0'] + + @property + def y0(self): + return self.coefficients['y0'] + + @property + def z0(self): + return self.coefficients['z0'] @property def r(self): return self.coefficients['r'] + @property + def u(self): + return self.coefficients['u'] + + @property + def v(self): + return self.coefficients['v'] + + @property + def w(self): + return self.coefficients['w'] + + @x0.setter + def x0(self, x0): + check_type('x0 coefficient', x0, Real) + self._coefficients['x0'] = x0 + + @y0.setter + def y0(self, y0): + check_type('y0 coefficient', y0, Real) + self._coefficients['y0'] = y0 + + @z0.setter + def z0(self, z0): + check_type('z0 coefficient', z0, Real) + self._coefficients['z0'] = z0 + @r.setter def r(self, r): check_type('r coefficient', r, Real) self._coefficients['r'] = r + @u.setter + def u(self, u): + check_type('u coefficient', u, Real) + self._coefficients['u'] = u -class XCylinder(Cylinder): + @v.setter + def v(self, v): + check_type('v coefficient', v, Real) + self._coefficients['v'] = v + + @w.setter + def w(self, w): + check_type('w coefficient', w, Real) + self._coefficients['w'] = w + + def _get_base_coeffs(self): + """Return generalized coefficients for a cylinder""" + return (0., 0., 1., self.z0) + + def _update_from_base_coeffs(self, coeffs): + a, b, c, d, e, f, g, h, j, k = coeffs + + +class XCylinder(QuadricMeta, Surface): """An infinite cylinder whose length is parallel to the x-axis of the form :math:`(y - y_0)^2 + (z - z_0)^2 = r^2`. @@ -1131,12 +1040,17 @@ class XCylinder(Cylinder): def __init__(self, y0=0., z0=0., r=1., boundary_type='transmission', name='', surface_id=None, *, R=None): if R is not None: - warn(_WARNING_UPPER.format(type(self).__name__, 'r', 'R'), FutureWarning) + warn(_WARNING_UPPER.format(type(self).__name__, 'r', 'R'), + FutureWarning) r = R super().__init__(r, boundary_type, name, surface_id) self.y0 = y0 self.z0 = z0 + @property + def x0(self): + return self.coefficients['x0'] + @property def y0(self): return self.coefficients['y0'] @@ -1145,6 +1059,11 @@ class XCylinder(Cylinder): def z0(self): return self.coefficients['z0'] + @x0.setter + def x0(self, x0): + check_type('x0 coefficient', x0, Real) + self._coefficients['x0'] = x0 + @y0.setter def y0(self, y0): check_type('y0 coefficient', y0, Real) @@ -1155,6 +1074,7 @@ class XCylinder(Cylinder): check_type('z0 coefficient', z0, Real) self._coefficients['z0'] = z0 + def bounding_box(self, side): """Determine an axis-aligned bounding box. From 64ee9b177f286c5948ad490bdc1d4d8ebf305501 Mon Sep 17 00:00:00 2001 From: Ethan Peterson Date: Mon, 10 Feb 2020 20:13:49 -0500 Subject: [PATCH 083/166] finished skeleton framework for refactoring surfaces --- openmc/surface.py | 650 +++++++++++++++++++++++----------------------- 1 file changed, 323 insertions(+), 327 deletions(-) diff --git a/openmc/surface.py b/openmc/surface.py index de1ec4cc2..6124494fe 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -272,10 +272,11 @@ class Surface(IDManagerMixin, metaclass=ABCMeta): surf_type = group['type'][()].decode() bc = group['boundary_type'][()].decode() coeffs = group['coefficients'][...] + kwargs = {'boundary_type': bc, 'name': name, 'surface_id': surface_id} cls = _SURFACE_CLASSES[surf_type] - return cls(*coeffs, bc, name, surface_id) + return cls(*coeffs, **kwargs) _SURFACE_CLASSES = Surface.get_subclass_map() @@ -850,10 +851,12 @@ class QuadricMeta(metaclass=ABCMeta): ---------- vector : iterable of float Direction in which surface should be translated + clone : bool + Whether to return a clone of the Surface or the Surface itself Returns ------- - openmc.Quadric + openmc.QuadricMeta Translated surface """ @@ -864,12 +867,14 @@ class QuadricMeta(metaclass=ABCMeta): g = g - 2*a*vx - d*vy - f*vz h = h - 2*b*vy - d*vx - e*vz j = j - 2*c*vz - e*vy - f*vx + if clone: surf = self.clone() else: surf = self surf._update_from_base_coeffs((a, b, c, d, e, f, g, h, j, k)) + return surf @@ -878,8 +883,23 @@ class Cylinder(QuadricMeta, Surface): Parameters ---------- + x0 : float, optional + x-coordinate for the origin of the Cylinder. Defaults to 0 + y0 : float, optional + y-coordinate for the origin of the Cylinder. Defaults to 0 + z0 : float, optional + z-coordinate for the origin of the Cylinder. Defaults to 0 r : float, optional Radius of the cylinder. Defaults to 1. + u : float, optional + x-component of the vector representing the axis of the cylinder. + Defaults to 0. + v : float, optional + y-component of the vector representing the axis of the cylinder. + Defaults to 0. + w : float, optional + z-component of the vector representing the axis of the cylinder. + Defaults to 1. boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles @@ -893,8 +913,20 @@ class Cylinder(QuadricMeta, Surface): Attributes ---------- + x0 : float + x-coordinate for the origin of the Cylinder + y0 : float + y-coordinate for the origin of the Cylinder + z0 : float + z-coordinate for the origin of the Cylinder r : float Radius of the cylinder + u : float + x-component of the vector representing the axis of the cylinder + v : float + y-component of the vector representing the axis of the cylinder + w : float + z-component of the vector representing the axis of the cylinder boundary_type : {'transmission, 'vacuum', 'reflective', 'white'} Boundary condition that defines the behavior for particles hitting the surface. @@ -998,11 +1030,11 @@ class XCylinder(QuadricMeta, Surface): Parameters ---------- y0 : float, optional - y-coordinate of the center of the cylinder. Defaults to 0. + y-coordinate for the origin of the Cylinder. Defaults to 0 z0 : float, optional - z-coordinate of the center of the cylinder. Defaults to 0. + z-coordinate for the origin of the Cylinder. Defaults to 0 r : float, optional - Radius of the cylinder. Defaults to 0. + Radius of the cylinder. Defaults to 1. boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles @@ -1017,9 +1049,11 @@ class XCylinder(QuadricMeta, Surface): Attributes ---------- y0 : float - y-coordinate of the center of the cylinder + y-coordinate for the origin of the Cylinder z0 : float - z-coordinate of the center of the cylinder + z-coordinate for the origin of the Cylinder + r : float + Radius of the cylinder boundary_type : {'transmission, 'vacuum', 'reflective', 'white'} Boundary condition that defines the behavior for particles hitting the surface. @@ -1037,15 +1071,16 @@ class XCylinder(QuadricMeta, Surface): _type = 'x-cylinder' _coeff_keys = ('y0', 'z0', 'r') - def __init__(self, y0=0., z0=0., r=1., boundary_type='transmission', - name='', surface_id=None, *, R=None): + def __init__(self, y0=0., z0=0., r=1., **kwargs): + R = kwargs.pop('R', None) if R is not None: warn(_WARNING_UPPER.format(type(self).__name__, 'r', 'R'), FutureWarning) r = R - super().__init__(r, boundary_type, name, surface_id) + super().__init__(**kwargs) self.y0 = y0 self.z0 = z0 + self.r = r @property def x0(self): @@ -1074,6 +1109,12 @@ class XCylinder(QuadricMeta, Surface): check_type('z0 coefficient', z0, Real) self._coefficients['z0'] = z0 + def _get_base_coeffs(self): + """Return generalized coefficients for a cylinder""" + return (0., 0., 1., self.z0) + + def _update_from_base_coeffs(self, coeffs): + a, b, c, d, e, f, g, h, j, k = coeffs def bounding_box(self, side): """Determine an axis-aligned bounding box. @@ -1107,58 +1148,17 @@ class XCylinder(QuadricMeta, Surface): return (np.array([-np.inf, -np.inf, -np.inf]), np.array([np.inf, np.inf, np.inf])) - def evaluate(self, point): - """Evaluate the surface equation at a given point. - Parameters - ---------- - point : 3-tuple of float - The Cartesian coordinates, :math:`(x',y',z')`, at which the surface - equation should be evaluated. - - Returns - ------- - float - :math:`(y' - y_0)^2 + (z' - z_0)^2 - r^2` - - """ - y = point[1] - self.y0 - z = point[2] - self.z0 - return y**2 + z**2 - self.r**2 - - def translate(self, vector): - """Translate surface in given direction - - Parameters - ---------- - vector : iterable of float - Direction in which surface should be translated - - Returns - ------- - openmc.XCylinder - Translated surface - - """ - vx, vy, vz = vector - if vy == 0.0 and vz == 0.0: - return self - else: - y0 = self.y0 + vy - z0 = self.z0 + vz - return type(self)(y0=y0, z0=z0, r=self.r) - - -class YCylinder(Cylinder): +class YCylinder(QuadricMeta): """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`. Parameters ---------- x0 : float, optional - x-coordinate of the center of the cylinder. Defaults to 0. + x-coordinate for the origin of the Cylinder. Defaults to 0 z0 : float, optional - z-coordinate of the center of the cylinder. Defaults to 0. + z-coordinate for the origin of the Cylinder. Defaults to 0 r : float, optional Radius of the cylinder. Defaults to 1. boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}, optional @@ -1175,9 +1175,11 @@ class YCylinder(Cylinder): Attributes ---------- x0 : float - x-coordinate of the center of the cylinder + x-coordinate for the origin of the Cylinder z0 : float - z-coordinate of the center of the cylinder + z-coordinate for the origin of the Cylinder + r : float + Radius of the cylinder boundary_type : {'transmission, 'vacuum', 'reflective', 'white'} Boundary condition that defines the behavior for particles hitting the surface. @@ -1195,14 +1197,15 @@ class YCylinder(Cylinder): _type = 'y-cylinder' _coeff_keys = ('x0', 'z0', 'r') - def __init__(self, x0=0., z0=0., r=1., boundary_type='transmission', - name='', surface_id=None, *, R=None): + def __init__(self, x0=0., z0=0., r=1., **kwargs): + R = kwargs.pop('R', None) if R is not None: warn(_WARNING_UPPER.format(type(self).__name__, 'r', 'R'), FutureWarning) r = R - super().__init__(r, boundary_type, name, surface_id) + super().__init__(**kwargs) self.x0 = x0 self.z0 = z0 + self.r = r @property def x0(self): @@ -1222,6 +1225,13 @@ class YCylinder(Cylinder): check_type('z0 coefficient', z0, Real) self._coefficients['z0'] = z0 + def _get_base_coeffs(self): + """Return generalized coefficients for a cylinder""" + return (0., 0., 1., self.z0) + + def _update_from_base_coeffs(self, coeffs): + a, b, c, d, e, f, g, h, j, k = coeffs + def bounding_box(self, side): """Determine an axis-aligned bounding box. @@ -1254,77 +1264,38 @@ class YCylinder(Cylinder): return (np.array([-np.inf, -np.inf, -np.inf]), np.array([np.inf, np.inf, np.inf])) - def evaluate(self, point): - """Evaluate the surface equation at a given point. - Parameters - ---------- - point : 3-tuple of float - The Cartesian coordinates, :math:`(x',y',z')`, at which the surface - equation should be evaluated. - - Returns - ------- - float - :math:`(x' - x_0)^2 + (z' - z_0)^2 - r^2` - - """ - x = point[0] - self.x0 - z = point[2] - self.z0 - return x**2 + z**2 - self.r**2 - - def translate(self, vector): - """Translate surface in given direction - - Parameters - ---------- - vector : iterable of float - Direction in which surface should be translated - - Returns - ------- - openmc.YCylinder - Translated surface - - """ - vx, vy, vz = vector - if vx == 0.0 and vz == 0.0: - return self - else: - x0 = self.x0 + vx - z0 = self.z0 + vz - return type(self)(x0=x0, z0=z0, r=self.r) - - -class ZCylinder(Cylinder): +class ZCylinder(QuadricMeta, 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`. Parameters ---------- - surface_id : int, optional - Unique identifier for the surface. If not specified, an identifier will - automatically be assigned. + x0 : float, optional + x-coordinate for the origin of the Cylinder. Defaults to 0 + y0 : float, optional + y-coordinate for the origin of the Cylinder. Defaults to 0 + r : float, optional + Radius of the cylinder. Defaults to 1. boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles freely pass through the surface. - x0 : float, optional - x-coordinate of the center of the cylinder. Defaults to 0. - y0 : float, optional - y-coordinate of the center of the cylinder. Defaults to 0. - r : float, optional - Radius of the cylinder. Defaults to 1. name : str, optional Name of the cylinder. If not specified, the name will be the empty string. + surface_id : int, optional + Unique identifier for the surface. If not specified, an identifier will + automatically be assigned. Attributes ---------- x0 : float - x-coordinate of the center of the cylinder + x-coordinate for the origin of the Cylinder y0 : float - y-coordinate of the center of the cylinder + y-coordinate for the origin of the Cylinder + r : float + Radius of the cylinder boundary_type : {'transmission, 'vacuum', 'reflective', 'white'} Boundary condition that defines the behavior for particles hitting the surface. @@ -1342,14 +1313,15 @@ class ZCylinder(Cylinder): _type = 'z-cylinder' _coeff_keys = ('x0', 'y0', 'r') - def __init__(self, x0=0., y0=0., r=1., boundary_type='transmission', - name='', surface_id=None, *, R=None): + def __init__(self, x0=0., y0=0., r=1., **kwargs): + R = kwargs.pop('R', None) if R is not None: warn(_WARNING_UPPER.format(type(self).__name__, 'r', 'R'), FutureWarning) r = R - super().__init__(r, boundary_type, name, surface_id) + super().__init__(**kwargs) self.x0 = x0 self.y0 = y0 + self.r = r @property def x0(self): @@ -1369,6 +1341,13 @@ class ZCylinder(Cylinder): check_type('y0 coefficient', y0, Real) self._coefficients['y0'] = y0 + def _get_base_coeffs(self): + """Return generalized coefficients for a cylinder""" + return (0., 0., 1., self.z0) + + def _update_from_base_coeffs(self, coeffs): + a, b, c, d, e, f, g, h, j, k = coeffs + def bounding_box(self, side): """Determine an axis-aligned bounding box. @@ -1401,49 +1380,8 @@ class ZCylinder(Cylinder): return (np.array([-np.inf, -np.inf, -np.inf]), np.array([np.inf, np.inf, np.inf])) - def evaluate(self, point): - """Evaluate the surface equation at a given point. - Parameters - ---------- - point : 3-tuple of float - The Cartesian coordinates, :math:`(x',y',z')`, at which the surface - equation should be evaluated. - - Returns - ------- - float - :math:`(x' - x_0)^2 + (y' - y_0)^2 - r^2` - - """ - x = point[0] - self.x0 - y = point[1] - self.y0 - return x**2 + y**2 - self.r**2 - - def translate(self, vector): - """Translate surface in given direction - - Parameters - ---------- - vector : iterable of float - Direction in which surface should be translated - - Returns - ------- - openmc.ZCylinder - Translated surface - - """ - vx, vy, vz = vector - if vx == 0.0 and vy == 0.0: - return self - else: - x0 = self.x0 + vx - y0 = self.y0 + vy - return type(self)(x0=x0, y0=y0, r=self.r) - - -class Sphere(Surface): +class Sphere(QuadricMeta, Surface): """A sphere of the form :math:`(x - x_0)^2 + (y - y_0)^2 + (z - z_0)^2 = r^2`. Parameters @@ -1493,12 +1431,12 @@ class Sphere(Surface): _type = 'sphere' _coeff_keys = ('x0', 'y0', 'z0', 'r') - def __init__(self, x0=0., y0=0., z0=0., r=1., boundary_type='transmission', - name='', surface_id=None, *, R=None): + def __init__(self, x0=0., y0=0., z0=0., r=1., **kwargs): + R = kwargs.pop('R', None) if R is not None: warn(_WARNING_UPPER.format(type(self).__name__, 'r', 'R'), FutureWarning) r = R - super().__init__(surface_id, boundary_type, name=name) + super().__init__(**kwargs) self.x0 = x0 self.y0 = y0 self.z0 = z0 @@ -1540,6 +1478,13 @@ class Sphere(Surface): check_type('r coefficient', r, Real) self._coefficients['r'] = r + def _get_base_coeffs(self): + """Return generalized coefficients for a cylinder""" + return (0., 0., 1., self.z0) + + def _update_from_base_coeffs(self, coeffs): + a, b, c, d, e, f, g, h, j, k = coeffs + def bounding_box(self, side): """Determine an axis-aligned bounding box. @@ -1573,51 +1518,8 @@ class Sphere(Surface): return (np.array([-np.inf, -np.inf, -np.inf]), np.array([np.inf, np.inf, np.inf])) - def evaluate(self, point): - """Evaluate the surface equation at a given point. - Parameters - ---------- - point : 3-tuple of float - The Cartesian coordinates, :math:`(x',y',z')`, at which the surface - equation should be evaluated. - - Returns - ------- - float - :math:`(x' - x_0)^2 + (y' - y_0)^2 + (z' - z_0)^2 - r^2` - - """ - x = point[0] - self.x0 - y = point[1] - self.y0 - z = point[2] - self.z0 - return x**2 + y**2 + z**2 - self.r**2 - - def translate(self, vector): - """Translate surface in given direction - - Parameters - ---------- - vector : iterable of float - Direction in which surface should be translated - - Returns - ------- - openmc.Sphere - Translated surface - - """ - vx, vy, vz = vector - if vx == 0.0 and vy == 0.0 and vz == 0.0: - return self - else: - x0 = self.x0 + vx - y0 = self.y0 + vy - z0 = self.z0 + vz - return type(self)(x0=x0, y0=y0, z0=z0, r=self.r) - - -class Cone(Surface): +class Cone(QuadricMeta, Surface): """A conical surface parallel to the x-, y-, or z-axis. Parameters @@ -1630,6 +1532,15 @@ class Cone(Surface): z-coordinate of the apex. Defaults to 0. r2 : float, optional Parameter related to the aperature. Defaults to 1. + u : float, optional + x-component of the vector representing the axis of the cone. + Defaults to 0. + v : float, optional + y-component of the vector representing the axis of the cone. + Defaults to 0. + w : float, optional + z-component of the vector representing the axis of the cone. + Defaults to 1. surface_id : int, optional Unique identifier for the surface. If not specified, an identifier will automatically be assigned. @@ -1650,6 +1561,12 @@ class Cone(Surface): z-coordinate of the apex r2 : float Parameter related to the aperature + u : float + x-component of the vector representing the axis of the cone. + v : float + y-component of the vector representing the axis of the cone. + w : float + z-component of the vector representing the axis of the cone. boundary_type : {'transmission, 'vacuum', 'reflective', 'white'} Boundary condition that defines the behavior for particles hitting the surface. @@ -1664,18 +1581,23 @@ class Cone(Surface): """ - _coeff_keys = ('x0', 'y0', 'z0', 'r2') + _type = 'cylinder' + _coeff_keys = ('x0', 'y0', 'z0', 'r2', 'u', 'v', 'w') - def __init__(self, x0=0., y0=0., z0=0., r2=1., boundary_type='transmission', - name='', surface_id=None, *, R2=None): + def __init__(self, x0=0., y0=0., z0=0., r2=1., u=0., v=0., w=1., **kwargs): + R2 = kwargs.pop('R2', None) if R2 is not None: - warn(_WARNING_UPPER.format(type(self).__name__, 'r2', 'R2'), FutureWarning) + warn(_WARNING_UPPER.format(type(self).__name__, 'r2', 'R2'), + FutureWarning) r2 = R2 - super().__init__(surface_id, boundary_type, name=name) + super().__init__(**kwargs) self.x0 = x0 self.y0 = y0 self.z0 = z0 self.r2 = r2 + self.u = u + self.v = v + self.w = w @property def x0(self): @@ -1693,6 +1615,18 @@ class Cone(Surface): def r2(self): return self.coefficients['r2'] + @property + def u(self): + return self.coefficients['u'] + + @property + def v(self): + return self.coefficients['v'] + + @property + def w(self): + return self.coefficients['w'] + @x0.setter def x0(self, x0): check_type('x0 coefficient', x0, Real) @@ -1713,31 +1647,30 @@ class Cone(Surface): check_type('r^2 coefficient', r2, Real) self._coefficients['r2'] = r2 - def translate(self, vector): - """Translate surface in given direction + @u.setter + def u(self, u): + check_type('u coefficient', u, Real) + self._coefficients['u'] = u - Parameters - ---------- - vector : iterable of float - Direction in which surface should be translated + @v.setter + def v(self, v): + check_type('v coefficient', v, Real) + self._coefficients['v'] = v - Returns - ------- - openmc.Cone - Translated surface + @w.setter + def w(self, w): + check_type('w coefficient', w, Real) + self._coefficients['w'] = w - """ - vx, vy, vz = vector - if vx == 0.0 and vy == 0.0 and vz == 0.0: - return self - else: - x0 = self.x0 + vx - y0 = self.y0 + vy - z0 = self.z0 + vz - return type(self)(x0=x0, y0=y0, z0=z0, r2=self.r2) + def _get_base_coeffs(self): + """Return generalized coefficients for a cylinder""" + return (0., 0., 1., self.z0) + + def _update_from_base_coeffs(self, coeffs): + a, b, c, d, e, f, g, h, j, k = coeffs -class XCone(Cone): +class XCone(QuadricMeta, Surface): """A cone parallel to the x-axis of the form :math:`(y - y_0)^2 + (z - z_0)^2 = r^2 (x - x_0)^2`. @@ -1786,29 +1719,65 @@ class XCone(Cone): """ _type = 'x-cone' + _coeff_keys = ('x0', 'y0', 'z0', 'r2') - def evaluate(self, point): - """Evaluate the surface equation at a given point. + def __init__(self, x0=0., y0=0., z0=0., r2=1., **kwargs): + R2 = kwargs.pop('R2', None) + if R2 is not None: + warn(_WARNING_UPPER.format(type(self).__name__, 'r2', 'R2'), + FutureWarning) + r2 = R2 + super().__init__(**kwargs) + self.x0 = x0 + self.y0 = y0 + self.z0 = z0 + self.r2 = r2 - Parameters - ---------- - point : 3-tuple of float - The Cartesian coordinates, :math:`(x',y',z')`, at which the surface - equation should be evaluated. + @property + def x0(self): + return self.coefficients['x0'] - Returns - ------- - float - :math:`(y' - y_0)^2 + (z' - z_0)^2 - r^2(x' - x_0)^2` + @property + def y0(self): + return self.coefficients['y0'] - """ - x = point[0] - self.x0 - y = point[1] - self.y0 - z = point[2] - self.z0 - return y**2 + z**2 - self.r2*x**2 + @property + def z0(self): + return self.coefficients['z0'] + + @property + def r2(self): + return self.coefficients['r2'] + + @x0.setter + def x0(self, x0): + check_type('x0 coefficient', x0, Real) + self._coefficients['x0'] = x0 + + @y0.setter + def y0(self, y0): + check_type('y0 coefficient', y0, Real) + self._coefficients['y0'] = y0 + + @z0.setter + def z0(self, z0): + check_type('z0 coefficient', z0, Real) + self._coefficients['z0'] = z0 + + @r2.setter + def r2(self, r2): + check_type('r^2 coefficient', r2, Real) + self._coefficients['r2'] = r2 + + def _get_base_coeffs(self): + """Return generalized coefficients for a cylinder""" + return (0., 0., 1., self.z0) + + def _update_from_base_coeffs(self, coeffs): + a, b, c, d, e, f, g, h, j, k = coeffs -class YCone(Cone): +class YCone(QuadricMeta, 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`. @@ -1857,29 +1826,65 @@ class YCone(Cone): """ _type = 'y-cone' + _coeff_keys = ('x0', 'y0', 'z0', 'r2') - def evaluate(self, point): - """Evaluate the surface equation at a given point. + def __init__(self, x0=0., y0=0., z0=0., r2=1., **kwargs): + R2 = kwargs.pop('R2', None) + if R2 is not None: + warn(_WARNING_UPPER.format(type(self).__name__, 'r2', 'R2'), + FutureWarning) + r2 = R2 + super().__init__(**kwargs) + self.x0 = x0 + self.y0 = y0 + self.z0 = z0 + self.r2 = r2 - Parameters - ---------- - point : 3-tuple of float - The Cartesian coordinates, :math:`(x',y',z')`, at which the surface - equation should be evaluated. + @property + def x0(self): + return self.coefficients['x0'] - Returns - ------- - float - :math:`(x' - x_0)^2 + (z' - z_0)^2 - r^2(y' - y_0)^2` + @property + def y0(self): + return self.coefficients['y0'] - """ - x = point[0] - self.x0 - y = point[1] - self.y0 - z = point[2] - self.z0 - return x**2 + z**2 - self.r2*y**2 + @property + def z0(self): + return self.coefficients['z0'] + + @property + def r2(self): + return self.coefficients['r2'] + + @x0.setter + def x0(self, x0): + check_type('x0 coefficient', x0, Real) + self._coefficients['x0'] = x0 + + @y0.setter + def y0(self, y0): + check_type('y0 coefficient', y0, Real) + self._coefficients['y0'] = y0 + + @z0.setter + def z0(self, z0): + check_type('z0 coefficient', z0, Real) + self._coefficients['z0'] = z0 + + @r2.setter + def r2(self, r2): + check_type('r^2 coefficient', r2, Real) + self._coefficients['r2'] = r2 + + def _get_base_coeffs(self): + """Return generalized coefficients for a cylinder""" + return (0., 0., 1., self.z0) + + def _update_from_base_coeffs(self, coeffs): + a, b, c, d, e, f, g, h, j, k = coeffs -class ZCone(Cone): +class ZCone(QuadricMeta, 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`. @@ -1928,29 +1933,65 @@ class ZCone(Cone): """ _type = 'z-cone' + _coeff_keys = ('x0', 'y0', 'z0', 'r2') - def evaluate(self, point): - """Evaluate the surface equation at a given point. + def __init__(self, x0=0., y0=0., z0=0., r2=1., **kwargs): + R2 = kwargs.pop('R2', None) + if R2 is not None: + warn(_WARNING_UPPER.format(type(self).__name__, 'r2', 'R2'), + FutureWarning) + r2 = R2 + super().__init__(**kwargs) + self.x0 = x0 + self.y0 = y0 + self.z0 = z0 + self.r2 = r2 - Parameters - ---------- - point : 3-tuple of float - The Cartesian coordinates, :math:`(x',y',z')`, at which the surface - equation should be evaluated. + @property + def x0(self): + return self.coefficients['x0'] - Returns - ------- - float - :math:`(x' - x_0)^2 + (y' - y_0)^2 - r^2(z' - z_0)^2` + @property + def y0(self): + return self.coefficients['y0'] - """ - x = point[0] - self.x0 - y = point[1] - self.y0 - z = point[2] - self.z0 - return x**2 + y**2 - self.r2*z**2 + @property + def z0(self): + return self.coefficients['z0'] + + @property + def r2(self): + return self.coefficients['r2'] + + @x0.setter + def x0(self, x0): + check_type('x0 coefficient', x0, Real) + self._coefficients['x0'] = x0 + + @y0.setter + def y0(self, y0): + check_type('y0 coefficient', y0, Real) + self._coefficients['y0'] = y0 + + @z0.setter + def z0(self, z0): + check_type('z0 coefficient', z0, Real) + self._coefficients['z0'] = z0 + + @r2.setter + def r2(self, r2): + check_type('r^2 coefficient', r2, Real) + self._coefficients['r2'] = r2 + + def _get_base_coeffs(self): + """Return generalized coefficients for a cylinder""" + return (0., 0., 1., self.z0) + + def _update_from_base_coeffs(self, coeffs): + a, b, c, d, e, f, g, h, j, k = coeffs -class Quadric(Surface): +class Quadric(QuadricMeta, Surface): """A surface of the form :math:`Ax^2 + By^2 + Cz^2 + Dxy + Eyz + Fxz + Gx + Hy + Jz + K = 0`. @@ -1990,8 +2031,8 @@ class Quadric(Surface): _coeff_keys = ('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k') def __init__(self, a=0., b=0., c=0., d=0., e=0., f=0., g=0., h=0., j=0., - k=0., boundary_type='transmission', name='', surface_id=None): - super().__init__(surface_id, boundary_type, name=name) + k=0., **kwargs): + super().__init__(**kwargs) self.a = a self.b = b self.c = c @@ -2093,51 +2134,6 @@ class Quadric(Surface): check_type('k coefficient', k, Real) self._coefficients['k'] = k - def evaluate(self, point): - """Evaluate the surface equation at a given point. - - Parameters - ---------- - point : 3-tuple of float - The Cartesian coordinates, :math:`(x',y',z')`, at which the surface - equation should be evaluated. - - Returns - ------- - float - :math:`Ax'^2 + By'^2 + Cz'^2 + Dx'y' + Ey'z' + Fx'z' + Gx' + Hy' + - Jz' + K = 0` - - """ - x, y, z = point - return x*(self.a*x + self.d*y + self.g) + \ - y*(self.b*y + self.e*z + self.h) + \ - z*(self.c*z + self.f*x + self.j) + self.k - - def translate(self, vector): - """Translate surface in given direction - - Parameters - ---------- - vector : iterable of float - Direction in which surface should be translated - - Returns - ------- - openmc.Quadric - Translated surface - - """ - vx, vy, vz = vector - a, b, c, d, e, f, g, h, j, k = (getattr(self, key) for key in - self._coeff_keys) - k = (k + vx*vx + vy*vy + vz*vz + d*vx*vy + e*vy*vz + f*vx*vz - - g*vx - h*vy - j*vz) - g = g - 2*a*vx - d*vy - f*vz - h = h - 2*b*vy - d*vx - e*vz - j = j - 2*c*vz - e*vy - f*vx - return type(self)(a=a, b=b, c=c, d=d, e=e, f=f, g=g, h=h, j=j, k=k) - class Halfspace(Region): """A positive or negative half-space region. From 9740f91ffc51f58f6ccb321ca9bd0cf65fe75138 Mon Sep 17 00:00:00 2001 From: Ethan Peterson Date: Mon, 10 Feb 2020 20:59:34 -0500 Subject: [PATCH 084/166] working out some _get_base_coeff methods --- openmc/surface.py | 74 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 69 insertions(+), 5 deletions(-) diff --git a/openmc/surface.py b/openmc/surface.py index 6124494fe..ddee08aad 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -641,7 +641,7 @@ class XPlane(PlaneMeta, Surface): def _update_from_base_coeffs(self, coeffs): a, b, c, d = coeffs - self.x0 = d + self.x0 = d / a def _neg_bounds(self): """Return the lower and upper bounds of the negative half space""" @@ -715,7 +715,7 @@ class YPlane(PlaneMeta, Surface): def _update_from_base_coeffs(self, coeffs): a, b, c, d = coeffs - self.y0 = d + self.y0 = d / b def _neg_bounds(self): """Return the lower and upper bounds of the negative half space""" @@ -789,7 +789,7 @@ class ZPlane(PlaneMeta, Surface): def _update_from_base_coeffs(self, coeffs): a, b, c, d = coeffs - self.z0 = d + self.z0 = d / c def _neg_bounds(self): """Return the lower and upper bounds of the negative half space""" @@ -879,7 +879,9 @@ class QuadricMeta(metaclass=ABCMeta): class Cylinder(QuadricMeta, Surface): - """A cylinder + """A cylinder with radius r, centered on the point (x0, y0, z0) with an + axis specified by the line through points (x0, y0, z0) and (x0+u, y0+v, + z0+w) Parameters ---------- @@ -1017,11 +1019,73 @@ class Cylinder(QuadricMeta, Surface): def _get_base_coeffs(self): """Return generalized coefficients for a cylinder""" - return (0., 0., 1., self.z0) + x0, y0, z0 = self.x0, self.y0, self.z0 + x1, y1, z1 = self.x0 + self.u, self.y0 + self.v, self.z0 + self.w + dx, dy, dz = x1 - x0, y1 - y0, z1 - z0 + + # Set coefficients for Quadric surface that represents a cylinder of + # radius r whose axis is the line defined by p1 and p2 + a = dy**2 + dz**2 + b = dx**2 + dz**2 + c = dx**2 + dy**2 + d = -2*dx*dy + e = -2*dy*dz + f = -2*dx*dz + g = -2*((z1 - z0)*(x0*z1 - x1*z0) + (y1 - y0)*(x0*y1-x1*y0)) + h = 2*((x1 - x0)*(x0*y1 - x1*y0) - (z1 - z0)*(y0*z1 - y1*z0)) + j = 2*((x1 - x0)*(x0*z1 - x1*z0) + (y1 - y0)*(y0*z1 - y1*z0)) + k = (y0*z1 - y1*z0)**2 + (x0*z1 - x1*z0)**2 + (x0*y1 - x1*y0)**2 \ + - r**2*(dx**2 + dy**2 + dz**2) + + return (a, b, c, d, e, f, g, h, j, k) def _update_from_base_coeffs(self, coeffs): a, b, c, d, e, f, g, h, j, k = coeffs + @classmethod + def from_points(cls, p1, p2, r=1., **kwargs): + """Return a cylinder given points that define the axis and a radius. + + Parameters + ---------- + p1, p2 : 3-tuples + Points that pass through the plane + r : float, optional + Radius of the cylinder. Defaults to 1. + kwargs : dict + Keyword arguments passed to the :class:`Quadric` constructor + + Returns + ------- + Cylinder + Cylinder that has an axis through the points p1 and p2 + + """ + # Convert to numpy arrays + p1 = np.asarray(p1) + p2 = np.asarray(p2) + x1, y1, z1 = p1 + x2, y2, z2 = p2 + dx, dy, dz = p2 - p1 + + # Set coefficients for Quadric surface that represents a cylinder of + # radius r whose axis is the line defined by p1 and p2 + a = dy**2 + dz**2 + b = dx**2 + dz**2 + c = dx**2 + dy**2 + d = -2*dx*dy + e = -2*dy*dz + f = -2*dx*dz + g = -2*((z2 - z1)*(x1*z2 - x2*z1) + (y2 - y1)*(x1*y2-x2*y1)) + h = 2*((x2 - x1)*(x1*y2 - x2*y1) - (z2 - z1)*(y1*z2 - y2*z1)) + j = 2*((x2 - x1)*(x1*z2 - x2*z1) + (y2 - y1)*(y1*z2 - y2*z1)) + k = (y1*z2 - y2*z1)**2 + (x1*z2 - x2*z1)**2 + (x1*y2 - x2*y1)**2 \ + - r**2*(dx**2 + dy**2 + dz**2) + + cyl = cls(x0=x1, y0=y1, z0=z1, r=r, **kwargs) + cyl._update_from_base_coeffs((a, b, c, d, e, f, g, h, j, k)) + return cyl + class XCylinder(QuadricMeta, Surface): """An infinite cylinder whose length is parallel to the x-axis of the form From 542643f91aebbc54c01093ce8aca7809d38e2a69 Mon Sep 17 00:00:00 2001 From: Ethan Peterson Date: Mon, 10 Feb 2020 21:03:35 -0500 Subject: [PATCH 085/166] fixed typos in translate and __init__ --- openmc/surface.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openmc/surface.py b/openmc/surface.py index ddee08aad..aacaf45b4 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -381,7 +381,7 @@ class PlaneMeta(metaclass=ABCMeta): else: surf = self - surf._update_from_base_coeffs(a, b, c, d) + surf._update_from_base_coeffs((a, b, c, d)) return surf def bounding_box(self, side): @@ -944,7 +944,7 @@ class Cylinder(QuadricMeta, Surface): """ _type = 'cylinder' _coeff_keys = ('x0', 'y0', 'z0', 'r', 'u', 'v','w') - def __init__(self, x0=0., y0=0., z0=0. r=1., u=0., v=0., w=1., **kwargs): + def __init__(self, x0=0., y0=0., z0=0., r=1., u=0., v=0., w=1., **kwargs): super().__init__(**kwargs) self.x0 = x0 self.y0 = y0 From ab465084b60c9fa3170e534d3c362d66b599b369 Mon Sep 17 00:00:00 2001 From: Ethan Peterson Date: Tue, 11 Feb 2020 11:25:51 -0500 Subject: [PATCH 086/166] still working through coefficient transformations, done with (X,Y,Z)Cylinders and Cones, Planes, Quadric --- openmc/surface.py | 626 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 479 insertions(+), 147 deletions(-) diff --git a/openmc/surface.py b/openmc/surface.py index aacaf45b4..8b05ce8c5 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -282,28 +282,8 @@ class Surface(IDManagerMixin, metaclass=ABCMeta): _SURFACE_CLASSES = Surface.get_subclass_map() -class PlaneMeta(metaclass=ABCMeta): +class PlaneMixin(metaclass=ABCMeta): """A Plane Meta class for all operations on order 1 surfaces""" -# def __new__(cls, *args, **kwargs): -# for key in cls._coeff_keys, kwargs.pop(key) to get arguments for class -# """Simplify this plane if possible to an XPlane, YPlane, or ZPlane""" -# pass -# -# if cls is Plane: -# if np.all(np.isclose((self.b, self.c), 0., atol=atol)): -# x0 = self.d / self.a -# return XPlane(x0=x0, boundary_type=self.boundary_type, -# name=self.name, surface_id=self.id) -# -# elif np.all(np.isclose((self.a, self.c), 0., atol=atol)): -# y0 = self.d / self.b -# return YPlane(y0=y0, boundary_type=self.boundary_type, -# name=self.name, surface_id=self.id) -# -# elif np.all(np.isclose((self.a, self.b), 0., atol=atol)): -# z0 = self.d / self.c -# return ZPlane(z0=z0, boundary_type=self.boundary_type, -# name=self.name, surface_id=self.id) def __init__(self, **kwargs): super().__init__(**kwargs) @@ -321,17 +301,32 @@ class PlaneMeta(metaclass=ABCMeta): @abstractmethod def _get_base_coeffs(self): + """Return coefficients a, b, c, d representing a general plane of the + form :math:`ax + by + cz = d`. + + """ pass @abstractmethod - def _update_from_base_coeffs(self): + def _update_from_base_coeffs(self, coeffs): + """Update the current plane from coefficients representing a general + plane of the form :math:`ax + by + cz = d`. + + Parameters + ---------- + coeffs : tuple + Tuple of general coefficients (a, b, c, d) representing the plane + + """ pass def _neg_bounds(self): + """Return the lower and upper bounds of the negative half space""" return (np.array([-np.inf, -np.inf, -np.inf]), np.array([np.inf, np.inf, np.inf])) def _pos_bounds(self): + """Return the lower and upper bounds of the positive half space""" return (np.array([-np.inf, -np.inf, -np.inf]), np.array([np.inf, np.inf, np.inf])) @@ -432,7 +427,7 @@ class PlaneMeta(metaclass=ABCMeta): return element -class Plane(PlaneMeta, Surface): +class Plane(PlaneMixin, Surface): """An arbitrary plane of the form :math:`Ax + By + Cz = D`. Parameters @@ -539,14 +534,25 @@ class Plane(PlaneMeta, Surface): self._coefficients['d'] = d def _get_base_coeffs(self): + """Return coefficients a, b, c, d representing a general plane of the + form :math:`ax + by + cz = d`. + + """ return (self.a, self.b, self.c, self.d) def _update_from_base_coeffs(self, coeffs): - a, b, c, d = coeffs - self.a = a - self.b = b - self.c = c - self.d = d + """Update the current plane from coefficients representing a general + plane of the form :math:`ax + by + cz = d`. + + Parameters + ---------- + coeffs : tuple + Tuple of general coefficients (a, b, c, d) representing the plane + + """ + + for c, val in zip(self._coeff_keys, coeffs): + setattr(self, c, val) @classmethod def from_points(cls, p1, p2, p3, **kwargs): @@ -581,7 +587,7 @@ class Plane(PlaneMeta, Surface): return cls(a=a, b=b, c=c, d=d, **kwargs) -class XPlane(PlaneMeta, Surface): +class XPlane(PlaneMixin, Surface): """A plane perpendicular to the x axis of the form :math:`x - x_0 = 0` Parameters @@ -637,9 +643,22 @@ class XPlane(PlaneMeta, Surface): self._coefficients['x0'] = x0 def _get_base_coeffs(self): + """Return coefficients a, b, c, d representing a general plane of the + form :math:`ax + by + cz = d`. + + """ return (1., 0., 0., self.x0) def _update_from_base_coeffs(self, coeffs): + """Update the current plane from coefficients representing a general + plane of the form :math:`ax + by + cz = d`. + + Parameters + ---------- + coeffs : tuple + Tuple of general coefficients (a, b, c, d) representing the plane + + """ a, b, c, d = coeffs self.x0 = d / a @@ -654,7 +673,10 @@ class XPlane(PlaneMeta, Surface): np.array([np.inf, np.inf, np.inf])) -class YPlane(PlaneMeta, Surface): +Plane.register(XPlane) + + +class YPlane(PlaneMixin, Surface): """A plane perpendicular to the y axis of the form :math:`y - y_0 = 0` Parameters @@ -710,10 +732,22 @@ class YPlane(PlaneMeta, Surface): self._coefficients['y0'] = y0 def _get_base_coeffs(self): - """Return generalized coefficients for a plane""" + """Return coefficients a, b, c, d representing a general plane of the + form :math:`ax + by + cz = d`. + + """ return (0., 1., 0., self.y0) def _update_from_base_coeffs(self, coeffs): + """Update the current plane from coefficients representing a general + plane of the form :math:`ax + by + cz = d`. + + Parameters + ---------- + coeffs : tuple + Tuple of general coefficients (a, b, c, d) representing the plane + + """ a, b, c, d = coeffs self.y0 = d / b @@ -728,23 +762,26 @@ class YPlane(PlaneMeta, Surface): np.array([np.inf, np.inf, np.inf])) -class ZPlane(PlaneMeta, Surface): +Plane.register(YPlane) + + +class ZPlane(PlaneMixin, Surface): """A plane perpendicular to the z axis of the form :math:`z - z_0 = 0` Parameters ---------- - surface_id : int, optional - Unique identifier for the surface. If not specified, an identifier will - automatically be assigned. + z0 : float, optional + Location of the plane. Defaults to 0. boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic', 'white'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles freely pass through the surface. Only axis-aligned periodicity is supported, i.e., x-planes can only be paired with x-planes. - z0 : float, optional - Location of the plane. Defaults to 0. name : str, optional Name of the plane. If not specified, the name will be the empty string. + surface_id : int, optional + Unique identifier for the surface. If not specified, an identifier will + automatically be assigned. Attributes ---------- @@ -784,10 +821,22 @@ class ZPlane(PlaneMeta, Surface): self._coefficients['z0'] = z0 def _get_base_coeffs(self): - """Return generalized coefficients for a plane""" + """Return coefficients a, b, c, d, e, f, g, h, j, k representing a + general plane form :math:`ax + by + cz = d`. + + """ return (0., 0., 1., self.z0) def _update_from_base_coeffs(self, coeffs): + """Update the current plane from coefficients representing a general + plane of the form :math:`ax + by + cz = d`. + + Parameters + ---------- + coeffs : tuple + Tuple of general coefficients (a, b, c, d) representing the plane + + """ a, b, c, d = coeffs self.z0 = d / c @@ -802,25 +851,46 @@ class ZPlane(PlaneMeta, Surface): np.array([np.inf, np.inf, np.inf])) -class QuadricMeta(metaclass=ABCMeta): - """A Meta class implementing common functionality for quadric surfaces""" +Plane.register(ZPlane) + + +class QuadricMixin(metaclass=ABCMeta): + """A Mixin class implementing common functionality for quadric surfaces""" def __init__(self, **kwargs): super().__init__(**kwargs) @abstractmethod def _get_base_coeffs(self): + """Return coefficients a, b, c, d, e, f, g, h, j, k representing a + general quadric surface of the form: + :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. + + """ pass @abstractmethod - def _update_from_base_coeffs(self): + def _update_from_base_coeffs(self, coeffs): + """Update the current surface from coefficients representing a general + quadric surface of the form: + :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. + + Parameters + ---------- + coeffs : tuple + Tuple of general coefficients (a, b, c, d, e, f, g, h, j, k) + representing the quadric surface + + """ pass def _neg_bounds(self): + """Return the lower and upper bounds of the negative half space""" return (np.array([-np.inf, -np.inf, -np.inf]), np.array([np.inf, np.inf, np.inf])) def _pos_bounds(self): + """Return the lower and upper bounds of the positive half space""" return (np.array([-np.inf, -np.inf, -np.inf]), np.array([np.inf, np.inf, np.inf])) @@ -856,7 +926,7 @@ class QuadricMeta(metaclass=ABCMeta): Returns ------- - openmc.QuadricMeta + openmc.QuadricMixin Translated surface """ @@ -878,7 +948,7 @@ class QuadricMeta(metaclass=ABCMeta): return surf -class Cylinder(QuadricMeta, Surface): +class Cylinder(QuadricMixin, Surface): """A cylinder with radius r, centered on the point (x0, y0, z0) with an axis specified by the line through points (x0, y0, z0) and (x0+u, y0+v, z0+w) @@ -893,13 +963,13 @@ class Cylinder(QuadricMeta, Surface): z-coordinate for the origin of the Cylinder. Defaults to 0 r : float, optional Radius of the cylinder. Defaults to 1. - u : float, optional + dx : float, optional x-component of the vector representing the axis of the cylinder. Defaults to 0. - v : float, optional + dy : float, optional y-component of the vector representing the axis of the cylinder. Defaults to 0. - w : float, optional + dz : float, optional z-component of the vector representing the axis of the cylinder. Defaults to 1. boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}, optional @@ -923,11 +993,11 @@ class Cylinder(QuadricMeta, Surface): z-coordinate for the origin of the Cylinder r : float Radius of the cylinder - u : float + dx : float x-component of the vector representing the axis of the cylinder - v : float + dy : float y-component of the vector representing the axis of the cylinder - w : float + dz : float z-component of the vector representing the axis of the cylinder boundary_type : {'transmission, 'vacuum', 'reflective', 'white'} Boundary condition that defines the behavior for particles hitting the @@ -943,16 +1013,16 @@ class Cylinder(QuadricMeta, Surface): """ _type = 'cylinder' - _coeff_keys = ('x0', 'y0', 'z0', 'r', 'u', 'v','w') - def __init__(self, x0=0., y0=0., z0=0., r=1., u=0., v=0., w=1., **kwargs): + _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): super().__init__(**kwargs) self.x0 = x0 self.y0 = y0 self.z0 = z0 self.r = r - self.u = u - self.v = v - self.w = w + self.dx = dx + self.dy = dy + self.dz = dz @property def x0(self): @@ -971,16 +1041,16 @@ class Cylinder(QuadricMeta, Surface): return self.coefficients['r'] @property - def u(self): - return self.coefficients['u'] + def dx(self): + return self.coefficients['dx'] @property - def v(self): - return self.coefficients['v'] + def dy(self): + return self.coefficients['dy'] @property - def w(self): - return self.coefficients['w'] + def dz(self): + return self.coefficients['dz'] @x0.setter def x0(self, x0): @@ -1002,29 +1072,31 @@ class Cylinder(QuadricMeta, Surface): check_type('r coefficient', r, Real) self._coefficients['r'] = r - @u.setter - def u(self, u): - check_type('u coefficient', u, Real) - self._coefficients['u'] = u + @dx.setter + def dx(self, dx): + check_type('dx coefficient', dx, Real) + self._coefficients['dx'] = dx - @v.setter - def v(self, v): - check_type('v coefficient', v, Real) - self._coefficients['v'] = v + @dy.setter + def dy(self, dy): + check_type('dy coefficient', dy, Real) + self._coefficients['dy'] = dy - @w.setter - def w(self, w): - check_type('w coefficient', w, Real) - self._coefficients['w'] = w + @dz.setter + def dz(self, dz): + check_type('dz coefficient', dz, Real) + self._coefficients['dz'] = dz def _get_base_coeffs(self): - """Return generalized coefficients for a cylinder""" - x0, y0, z0 = self.x0, self.y0, self.z0 - x1, y1, z1 = self.x0 + self.u, self.y0 + self.v, self.z0 + self.w - dx, dy, dz = x1 - x0, y1 - y0, z1 - z0 + """Return coefficients a, b, c, d, e, f, g, h, j, k representing a + general quadric surface of the form: + :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. + + """ + x0, y0, z0, r = self.x0, self.y0, self.z0, self.r + dx, dy, dz = self.dx, self.dy, self.dz + x1, y1, z1 = x0 + dx, y0 + dy, z0 + dz - # Set coefficients for Quadric surface that represents a cylinder of - # radius r whose axis is the line defined by p1 and p2 a = dy**2 + dz**2 b = dx**2 + dz**2 c = dx**2 + dy**2 @@ -1040,6 +1112,17 @@ class Cylinder(QuadricMeta, Surface): return (a, b, c, d, e, f, g, h, j, k) def _update_from_base_coeffs(self, coeffs): + """Update the current surface from coefficients representing a general + quadric surface of the form: + :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. + + Parameters + ---------- + coeffs : tuple + Tuple of general coefficients (a, b, c, d, e, f, g, h, j, k) + representing the quadric surface + + """ a, b, c, d, e, f, g, h, j, k = coeffs @classmethod @@ -1087,7 +1170,7 @@ class Cylinder(QuadricMeta, Surface): return cyl -class XCylinder(QuadricMeta, Surface): +class XCylinder(QuadricMixin, Surface): """An infinite cylinder whose length is parallel to the x-axis of the form :math:`(y - y_0)^2 + (z - z_0)^2 = r^2`. @@ -1146,10 +1229,6 @@ class XCylinder(QuadricMeta, Surface): self.z0 = z0 self.r = r - @property - def x0(self): - return self.coefficients['x0'] - @property def y0(self): return self.coefficients['y0'] @@ -1158,10 +1237,9 @@ class XCylinder(QuadricMeta, Surface): def z0(self): return self.coefficients['z0'] - @x0.setter - def x0(self, x0): - check_type('x0 coefficient', x0, Real) - self._coefficients['x0'] = x0 + @property + def r(self): + return self.coefficients['r'] @y0.setter def y0(self, y0): @@ -1173,12 +1251,43 @@ class XCylinder(QuadricMeta, Surface): check_type('z0 coefficient', z0, Real) self._coefficients['z0'] = z0 + @r.setter + def r(self, r): + check_type('r coefficient', r, Real) + self._coefficients['r'] = r + def _get_base_coeffs(self): - """Return generalized coefficients for a cylinder""" - return (0., 0., 1., self.z0) + """Return coefficients a, b, c, d, e, f, g, h, j, k representing a + general quadric surface of the form: + :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. + + """ + y0, z0, r = self.y0, self.z0, self.r + + a = d = e = f = g = 0. + b = c = 1. + h, j, k = -2*y0, -2*z0, y0**2 + z0**2 - r**2 + + return (a, b, c, d, e, f, g, h, j, k) def _update_from_base_coeffs(self, coeffs): + """Update the current surface from coefficients representing a general + quadric surface of the form: + :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. + + Parameters + ---------- + coeffs : tuple + Tuple of general coefficients (a, b, c, d, e, f, g, h, j, k) + representing the quadric surface + + """ a, b, c, d, e, f, g, h, j, k = coeffs + y0, z0 = -h / 2, -j / 2 + r = np.sqrt(y0**2 + z0**2 - k) + + for c, val in zip(self._coeff_keys, (y0, z0, r)): + setattr(self, c, val) def bounding_box(self, side): """Determine an axis-aligned bounding box. @@ -1213,7 +1322,7 @@ class XCylinder(QuadricMeta, Surface): np.array([np.inf, np.inf, np.inf])) -class YCylinder(QuadricMeta): +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`. @@ -1279,6 +1388,10 @@ class YCylinder(QuadricMeta): def z0(self): return self.coefficients['z0'] + @property + def r(self): + return self.coefficients['r'] + @x0.setter def x0(self, x0): check_type('x0 coefficient', x0, Real) @@ -1289,12 +1402,43 @@ class YCylinder(QuadricMeta): check_type('z0 coefficient', z0, Real) self._coefficients['z0'] = z0 + @r.setter + def r(self, r): + check_type('r coefficient', r, Real) + self._coefficients['r'] = r + def _get_base_coeffs(self): - """Return generalized coefficients for a cylinder""" - return (0., 0., 1., self.z0) + """Return coefficients a, b, c, d, e, f, g, h, j, k representing a + general quadric surface of the form: + :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. + + """ + x0, z0, r = self.x0, self.z0, self.r + + b = d = e = f = h = 0. + a = c = 1. + g, j, k = -2*x0, -2*z0, x0**2 + z0**2 - r**2 + + return (a, b, c, d, e, f, g, h, j, k) def _update_from_base_coeffs(self, coeffs): + """Update the current surface from coefficients representing a general + quadric surface of the form: + :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. + + Parameters + ---------- + coeffs : tuple + Tuple of general coefficients (a, b, c, d, e, f, g, h, j, k) + representing the quadric surface + + """ a, b, c, d, e, f, g, h, j, k = coeffs + x0, z0 = -g / 2, -j / 2 + r = np.sqrt(x0**2 + z0**2 - k) + + for c, val in zip(self._coeff_keys, (x0, z0, r)): + setattr(self, c, val) def bounding_box(self, side): """Determine an axis-aligned bounding box. @@ -1329,7 +1473,7 @@ class YCylinder(QuadricMeta): np.array([np.inf, np.inf, np.inf])) -class ZCylinder(QuadricMeta, Surface): +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`. @@ -1380,7 +1524,8 @@ class ZCylinder(QuadricMeta, Surface): def __init__(self, x0=0., y0=0., r=1., **kwargs): R = kwargs.pop('R', None) if R is not None: - warn(_WARNING_UPPER.format(type(self).__name__, 'r', 'R'), FutureWarning) + warn(_WARNING_UPPER.format(type(self).__name__, 'r', 'R'), + FutureWarning) r = R super().__init__(**kwargs) self.x0 = x0 @@ -1395,6 +1540,10 @@ class ZCylinder(QuadricMeta, Surface): def y0(self): return self.coefficients['y0'] + @property + def r(self): + return self.coefficients['r'] + @x0.setter def x0(self, x0): check_type('x0 coefficient', x0, Real) @@ -1405,12 +1554,43 @@ class ZCylinder(QuadricMeta, Surface): check_type('y0 coefficient', y0, Real) self._coefficients['y0'] = y0 + @r.setter + def r(self, r): + check_type('r coefficient', r, Real) + self._coefficients['r'] = r + def _get_base_coeffs(self): - """Return generalized coefficients for a cylinder""" - return (0., 0., 1., self.z0) + """Return coefficients a, b, c, d, e, f, g, h, j, k representing a + general quadric surface of the form: + :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. + + """ + x0, y0, r = self.x0, self.y0, self.r + + c = d = e = f = j = 0. + a = b = 1. + g, h, k = -2*x0, -2*y0, x0**2 + y0**2 - r**2 + + return (a, b, c, d, e, f, g, h, j, k) def _update_from_base_coeffs(self, coeffs): + """Update the current surface from coefficients representing a general + quadric surface of the form: + :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. + + Parameters + ---------- + coeffs : tuple + Tuple of general coefficients (a, b, c, d, e, f, g, h, j, k) + representing the quadric surface + + """ a, b, c, d, e, f, g, h, j, k = coeffs + x0, y0 = -g / 2, -4 / 2 + r = np.sqrt(x0**2 + y0**2 - k) + + for c, val in zip(self._coeff_keys, (x0, y0, r)): + setattr(self, c, val) def bounding_box(self, side): """Determine an axis-aligned bounding box. @@ -1445,7 +1625,7 @@ class ZCylinder(QuadricMeta, Surface): np.array([np.inf, np.inf, np.inf])) -class Sphere(QuadricMeta, Surface): +class Sphere(QuadricMixin, Surface): """A sphere of the form :math:`(x - x_0)^2 + (y - y_0)^2 + (z - z_0)^2 = r^2`. Parameters @@ -1543,11 +1723,35 @@ class Sphere(QuadricMeta, Surface): self._coefficients['r'] = r def _get_base_coeffs(self): - """Return generalized coefficients for a cylinder""" - return (0., 0., 1., self.z0) + """Return coefficients a, b, c, d, e, f, g, h, j, k representing a + general quadric surface of the form: + :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. + + """ + x0, y0, z0, r = self.x0, self.y0, self.z0, self.r + a = b = c = 1. + d = e = f = 0. + g, h, j = -2*x0, -2*y0, -2*z0 + k = x0**2 + y0**2 + z0**2 - r**2 + return (a, b, c, d, e, f, g, h, j, k) def _update_from_base_coeffs(self, coeffs): + """Update the current surface from coefficients representing a general + quadric surface of the form: + :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. + + Parameters + ---------- + coeffs : tuple + Tuple of general coefficients (a, b, c, d, e, f, g, h, j, k) + representing the quadric surface + + """ a, b, c, d, e, f, g, h, j, k = coeffs + x0, y0, z0 = -g / 2, -h / 2, -j / 2 + r = np.sqrt(x0**2 + y0**2 + z0**2 - k) + for c, val in zip(self._coeff_keys, (x0, y0, z0, r)): + setattr(self, c, val) def bounding_box(self, side): """Determine an axis-aligned bounding box. @@ -1583,7 +1787,7 @@ class Sphere(QuadricMeta, Surface): np.array([np.inf, np.inf, np.inf])) -class Cone(QuadricMeta, Surface): +class Cone(QuadricMixin, Surface): """A conical surface parallel to the x-, y-, or z-axis. Parameters @@ -1625,11 +1829,11 @@ class Cone(QuadricMeta, Surface): z-coordinate of the apex r2 : float Parameter related to the aperature - u : float + dx : float x-component of the vector representing the axis of the cone. - v : float + dy : float y-component of the vector representing the axis of the cone. - w : float + dz : float z-component of the vector representing the axis of the cone. boundary_type : {'transmission, 'vacuum', 'reflective', 'white'} Boundary condition that defines the behavior for particles hitting the @@ -1646,9 +1850,9 @@ class Cone(QuadricMeta, Surface): """ _type = 'cylinder' - _coeff_keys = ('x0', 'y0', 'z0', 'r2', 'u', 'v', 'w') + _coeff_keys = ('x0', 'y0', 'z0', 'r2', 'dx', 'dy', 'dz') - def __init__(self, x0=0., y0=0., z0=0., r2=1., u=0., v=0., w=1., **kwargs): + def __init__(self, x0=0., y0=0., z0=0., r2=1., dx=0., dy=0., dz=1., **kwargs): R2 = kwargs.pop('R2', None) if R2 is not None: warn(_WARNING_UPPER.format(type(self).__name__, 'r2', 'R2'), @@ -1659,9 +1863,9 @@ class Cone(QuadricMeta, Surface): self.y0 = y0 self.z0 = z0 self.r2 = r2 - self.u = u - self.v = v - self.w = w + self.dx = dx + self.dy = dy + self.dz = dz @property def x0(self): @@ -1680,16 +1884,16 @@ class Cone(QuadricMeta, Surface): return self.coefficients['r2'] @property - def u(self): - return self.coefficients['u'] + def dx(self): + return self.coefficients['dx'] @property - def v(self): - return self.coefficients['v'] + def dy(self): + return self.coefficients['dy'] @property - def w(self): - return self.coefficients['w'] + def dz(self): + return self.coefficients['dz'] @x0.setter def x0(self, x0): @@ -1711,30 +1915,45 @@ class Cone(QuadricMeta, Surface): check_type('r^2 coefficient', r2, Real) self._coefficients['r2'] = r2 - @u.setter - def u(self, u): - check_type('u coefficient', u, Real) - self._coefficients['u'] = u + @dx.setter + def dx(self, dx): + check_type('dx coefficient', dx, Real) + self._coefficients['dx'] = dx - @v.setter - def v(self, v): - check_type('v coefficient', v, Real) - self._coefficients['v'] = v + @dy.setter + def dy(self, dy): + check_type('dy coefficient', dy, Real) + self._coefficients['dy'] = dy - @w.setter - def w(self, w): - check_type('w coefficient', w, Real) - self._coefficients['w'] = w + @dz.setter + def dz(self, dz): + check_type('dz coefficient', dz, Real) + self._coefficients['dz'] = dz def _get_base_coeffs(self): - """Return generalized coefficients for a cylinder""" + """Return coefficients a, b, c, d, e, f, g, h, j, k representing a + general quadric surface of the form: + :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. + + """ return (0., 0., 1., self.z0) def _update_from_base_coeffs(self, coeffs): + """Update the current surface from coefficients representing a general + quadric surface of the form: + :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. + + Parameters + ---------- + coeffs : tuple + Tuple of general coefficients (a, b, c, d, e, f, g, h, j, k) + representing the quadric surface + + """ a, b, c, d, e, f, g, h, j, k = coeffs -class XCone(QuadricMeta, Surface): +class XCone(QuadricMixin, Surface): """A cone parallel to the x-axis of the form :math:`(y - y_0)^2 + (z - z_0)^2 = r^2 (x - x_0)^2`. @@ -1834,14 +2053,46 @@ class XCone(QuadricMeta, Surface): self._coefficients['r2'] = r2 def _get_base_coeffs(self): - """Return generalized coefficients for a cylinder""" - return (0., 0., 1., self.z0) + """Return coefficients a, b, c, d, e, f, g, h, j, k representing a + general quadric surface of the form: + :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. + + """ + x0, y0, z0, r2 = self.x0, self.y0, self.z0, self.r2 + + a = -r2 + b = c = 1. + d = e = f = 0. + g, h, j = 2*x0*r2, -2*y0, -2*z0 + k = y0**2 + z0**2 - r2*x0**2 + + return (a, b, c, d, e, f, g, h, j, k) def _update_from_base_coeffs(self, coeffs): + """Update the current surface from coefficients representing a general + quadric surface of the form: + :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. + + Parameters + ---------- + coeffs : tuple + Tuple of general coefficients (a, b, c, d, e, f, g, h, j, k) + representing the quadric surface + + """ a, b, c, d, e, f, g, h, j, k = coeffs + r2 = -a + x0, y0, z0 = g / (2*r2), -h / 2, -j / 2 -class YCone(QuadricMeta, Surface): + for c, val in zip(self._coeff_keys, (x0, y0, z0, r2)): + setattr(self, c, val) + + +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`. @@ -1941,14 +2192,46 @@ class YCone(QuadricMeta, Surface): self._coefficients['r2'] = r2 def _get_base_coeffs(self): - """Return generalized coefficients for a cylinder""" - return (0., 0., 1., self.z0) + """Return coefficients a, b, c, d, e, f, g, h, j, k representing a + general quadric surface of the form: + :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. + + """ + x0, y0, z0, r2 = self.x0, self.y0, self.z0, self.r2 + + b = -r2 + a = c = 1. + d = e = f = 0. + g, h, j = -2*x0, 2*y0*r2, -2*z0 + k = x0**2 + z0**2 - r2*y0**2 + + return (a, b, c, d, e, f, g, h, j, k) def _update_from_base_coeffs(self, coeffs): + """Update the current surface from coefficients representing a general + quadric surface of the form: + :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. + + Parameters + ---------- + coeffs : tuple + Tuple of general coefficients (a, b, c, d, e, f, g, h, j, k) + representing the quadric surface + + """ a, b, c, d, e, f, g, h, j, k = coeffs + r2 = -b + x0, y0, z0 = -g / 2, h / (2*r2), -j / 2 -class ZCone(QuadricMeta, Surface): + for c, val in zip(self._coeff_keys, (x0, y0, z0, r2)): + setattr(self, c, val) + + +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`. @@ -2048,14 +2331,46 @@ class ZCone(QuadricMeta, Surface): self._coefficients['r2'] = r2 def _get_base_coeffs(self): - """Return generalized coefficients for a cylinder""" - return (0., 0., 1., self.z0) + """Return coefficients a, b, c, d, e, f, g, h, j, k representing a + general quadric surface of the form: + :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. + + """ + x0, y0, z0, r2 = self.x0, self.y0, self.z0, self.r2 + + c = -r2 + a = b = 1. + d = e = f = 0. + g, h, j = -2*x0, -2*y0, 2*z0*r2 + k = x0**2 + y0**2 - r2*z0**2 + + return (a, b, c, d, e, f, g, h, j, k) def _update_from_base_coeffs(self, coeffs): + """Update the current surface from coefficients representing a general + quadric surface of the form: + :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. + + Parameters + ---------- + coeffs : tuple + Tuple of general coefficients (a, b, c, d, e, f, g, h, j, k) + representing the quadric surface + + """ a, b, c, d, e, f, g, h, j, k = coeffs + r2 = -c + x0, y0, z0 = -g / 2, -h / 2, j / (2*r2) -class Quadric(QuadricMeta, Surface): + for c, val in zip(self._coeff_keys, (x0, y0, z0, r2)): + setattr(self, c, val) + + +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`. @@ -2096,17 +2411,11 @@ class Quadric(QuadricMeta, Surface): def __init__(self, a=0., b=0., c=0., d=0., e=0., f=0., g=0., h=0., j=0., k=0., **kwargs): + super().__init__(**kwargs) - self.a = a - self.b = b - self.c = c - self.d = d - self.e = e - self.f = f - self.g = g - self.h = h - self.j = j - self.k = k + + for c, val in zip(self._coeff_keys, (a, b, c, d, e, f, g, h, j, k)): + setattr(self, c, val) @property def a(self): @@ -2198,6 +2507,29 @@ class Quadric(QuadricMeta, Surface): check_type('k coefficient', k, Real) self._coefficients['k'] = k + def _get_base_coeffs(self): + """Return coefficients a, b, c, d, e, f, g, h, j, k representing a + general quadric surface of the form: + :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. + + """ + return tuple(getattr(self, c) for c in self._coeff_keys) + + def _update_from_base_coeffs(self, coeffs): + """Update the current surface from coefficients representing a general + quadric surface of the form: + :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. + + Parameters + ---------- + coeffs : tuple + Tuple of general coefficients (a, b, c, d, e, f, g, h, j, k) + representing the quadric surface + + """ + for c, val in zip(self._coeff_keys, coeffs): + setattr(self, c, val) + class Halfspace(Region): """A positive or negative half-space region. From 0ad5dbd446ef4e9dd434fff4b648b6af553fb6cc Mon Sep 17 00:00:00 2001 From: Ethan Peterson Date: Tue, 11 Feb 2020 12:10:17 -0500 Subject: [PATCH 087/166] cleaning up and making code more self-consistent --- openmc/surface.py | 49 +++++++++++++++++++++-------------------------- 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/openmc/surface.py b/openmc/surface.py index 8b05ce8c5..44178f6bc 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -1103,9 +1103,9 @@ class Cylinder(QuadricMixin, Surface): d = -2*dx*dy e = -2*dy*dz f = -2*dx*dz - g = -2*((z1 - z0)*(x0*z1 - x1*z0) + (y1 - y0)*(x0*y1-x1*y0)) - h = 2*((x1 - x0)*(x0*y1 - x1*y0) - (z1 - z0)*(y0*z1 - y1*z0)) - j = 2*((x1 - x0)*(x0*z1 - x1*z0) + (y1 - y0)*(y0*z1 - y1*z0)) + g = -2*(dz*(x0*z1 - x1*z0) + dy*(x0*y1-x1*y0)) + h = 2*(dx*(x0*y1 - x1*y0) - dz*(y0*z1 - y1*z0)) + j = 2*(dx*(x0*z1 - x1*z0) + dy*(y0*z1 - y1*z0)) k = (y0*z1 - y1*z0)**2 + (x0*z1 - x1*z0)**2 + (x0*y1 - x1*y0)**2 \ - r**2*(dx**2 + dy**2 + dz**2) @@ -1132,42 +1132,26 @@ class Cylinder(QuadricMixin, Surface): Parameters ---------- p1, p2 : 3-tuples - Points that pass through the plane + Points that pass through the plane, p1 will be used as (x0, y0, z0) r : float, optional Radius of the cylinder. Defaults to 1. kwargs : dict - Keyword arguments passed to the :class:`Quadric` constructor + Keyword arguments passed to the :class:`Cylinder` constructor Returns ------- Cylinder - Cylinder that has an axis through the points p1 and p2 + Cylinder that has an axis through the points p1 and p2, and a + radius r. """ # Convert to numpy arrays p1 = np.asarray(p1) p2 = np.asarray(p2) - x1, y1, z1 = p1 - x2, y2, z2 = p2 + x0, y0, z0 = p1 dx, dy, dz = p2 - p1 - # Set coefficients for Quadric surface that represents a cylinder of - # radius r whose axis is the line defined by p1 and p2 - a = dy**2 + dz**2 - b = dx**2 + dz**2 - c = dx**2 + dy**2 - d = -2*dx*dy - e = -2*dy*dz - f = -2*dx*dz - g = -2*((z2 - z1)*(x1*z2 - x2*z1) + (y2 - y1)*(x1*y2-x2*y1)) - h = 2*((x2 - x1)*(x1*y2 - x2*y1) - (z2 - z1)*(y1*z2 - y2*z1)) - j = 2*((x2 - x1)*(x1*z2 - x2*z1) + (y2 - y1)*(y1*z2 - y2*z1)) - k = (y1*z2 - y2*z1)**2 + (x1*z2 - x2*z1)**2 + (x1*y2 - x2*y1)**2 \ - - r**2*(dx**2 + dy**2 + dz**2) - - cyl = cls(x0=x1, y0=y1, z0=z1, r=r, **kwargs) - cyl._update_from_base_coeffs((a, b, c, d, e, f, g, h, j, k)) - return cyl + return cls(x0=x0, y0=y0, z0=z0, r=r, dx=dx, dy=dy, dz=dz, **kwargs) class XCylinder(QuadricMixin, Surface): @@ -1322,6 +1306,9 @@ class XCylinder(QuadricMixin, Surface): np.array([np.inf, np.inf, np.inf])) +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`. @@ -1373,7 +1360,8 @@ class YCylinder(QuadricMixin, Surface): def __init__(self, x0=0., z0=0., r=1., **kwargs): R = kwargs.pop('R', None) if R is not None: - warn(_WARNING_UPPER.format(type(self).__name__, 'r', 'R'), FutureWarning) + warn(_WARNING_UPPER.format(type(self).__name__, 'r', 'R'), + FutureWarning) r = R super().__init__(**kwargs) self.x0 = x0 @@ -1473,6 +1461,9 @@ class YCylinder(QuadricMixin, Surface): np.array([np.inf, np.inf, np.inf])) +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`. @@ -1625,6 +1616,9 @@ class ZCylinder(QuadricMixin, Surface): np.array([np.inf, np.inf, np.inf])) +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`. @@ -1678,7 +1672,8 @@ class Sphere(QuadricMixin, Surface): def __init__(self, x0=0., y0=0., z0=0., r=1., **kwargs): R = kwargs.pop('R', None) if R is not None: - warn(_WARNING_UPPER.format(type(self).__name__, 'r', 'R'), FutureWarning) + warn(_WARNING_UPPER.format(type(self).__name__, 'r', 'R'), + FutureWarning) r = R super().__init__(**kwargs) self.x0 = x0 From 6b3a23199427e4e71892b78cce495ab012b9f863 Mon Sep 17 00:00:00 2001 From: Ethan Peterson Date: Tue, 11 Feb 2020 12:34:31 -0500 Subject: [PATCH 088/166] fixed issue related to finding all surface subclasses --- openmc/surface.py | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/openmc/surface.py b/openmc/surface.py index 44178f6bc..24b21cf28 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -188,17 +188,6 @@ class Surface(IDManagerMixin, metaclass=ABCMeta): def translate(self, vector): pass - @classmethod - def get_subclasses(cls): - """Recursively find all subclasses of this class""" - return set(cls.__subclasses__()).union([s for c in cls.__subclasses__() - for s in get_subclasses(c)]) - - @classmethod - def get_subclass_map(cls): - """Generate mapping of class _type attributes to classes""" - return {c._type: c for c in cls.get_subclasses()} - def to_xml_element(self): """Return XML representation of the surface @@ -279,9 +268,6 @@ class Surface(IDManagerMixin, metaclass=ABCMeta): return cls(*coeffs, **kwargs) -_SURFACE_CLASSES = Surface.get_subclass_map() - - class PlaneMixin(metaclass=ABCMeta): """A Plane Meta class for all operations on order 1 surfaces""" @@ -1844,7 +1830,7 @@ class Cone(QuadricMixin, Surface): """ - _type = 'cylinder' + _type = 'cone' _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): @@ -2712,3 +2698,18 @@ class Halfspace(Region): # Return translated surface return type(self)(memo[key], self.side) + +def get_subclasses(cls): + """Recursively find all subclasses of this class""" + return set(cls.__subclasses__()).union([s for c in cls.__subclasses__() + for s in get_subclasses(c)]) + + +def get_subclass_map(cls): + """Generate mapping of class _type attributes to classes""" + return {c._type: c for c in get_subclasses(cls)} + + +_SURFACE_CLASSES = get_subclass_map(Surface) + + From a2f96ffe3438ab635effc28458f47c1ac7a1d406 Mon Sep 17 00:00:00 2001 From: Ethan Peterson Date: Tue, 11 Feb 2020 15:11:49 -0500 Subject: [PATCH 089/166] fixed bug in quadric translate function --- openmc/surface.py | 62 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 51 insertions(+), 11 deletions(-) diff --git a/openmc/surface.py b/openmc/surface.py index 24b21cf28..41befbb41 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -20,6 +20,11 @@ _WARNING_UPPER = """\ will not accept the capitalized version.\ """ +_WARNING_KWARGS = """ +"{}(...) accepts keyword arguments only for '{}'. Future versions of OpenMC \ +will not accept positional parameters for superclass arguments.\ +""" + class Surface(IDManagerMixin, metaclass=ABCMeta): """An implicit surface with an associated boundary condition. @@ -336,7 +341,7 @@ class PlaneMixin(metaclass=ABCMeta): a, b, c, d = self._get_base_coeffs() return a*x + b*y + c*z - d - def translate(self, vector, clone=False): + def translate(self, vector, clone=True): """Translate surface in given direction Parameters @@ -345,7 +350,7 @@ class PlaneMixin(metaclass=ABCMeta): Direction in which surface should be translated clone : boolean Whether or not to return a new instance of a Plane or to modify the - coefficients of this plane. + coefficients of this plane. Defaults to True Returns ------- @@ -466,12 +471,21 @@ class Plane(PlaneMixin, Surface): _type = 'plane' _coeff_keys = ('a', 'b', 'c', 'd') - def __init__(self, a=1., b=0., c=0., d=0., **kwargs): + def __init__(self, a=1., b=0., c=0., d=0., *args, **kwargs): # work around until capital letter kwargs are deprecated oldkwargs = deepcopy(kwargs) + # work around for accepting Surface kwargs as positional parameters + # until they are deprecated + argsdict = {k: v for k, v in zip(('boundary_type', 'name', + 'surface_id'), args)} + for k, v in argsdict.items(): + warn(_WARNING_KWARGS.format(type(self).__name__, k), FutureWarning) + for k in 'ABCD': kwargs.pop(k, None) + kwargs.update(argsdict) + super().__init__(**kwargs) self.a = a self.b = b @@ -615,7 +629,15 @@ class XPlane(PlaneMixin, Surface): _type = 'x-plane' _coeff_keys = ('x0',) - def __init__(self, x0=0., **kwargs): + def __init__(self, x0=0., *args, **kwargs): + # work around for accepting Surface kwargs as positional parameters + # until they are deprecated + argsdict = {k: v for k, v in zip(('boundary_type', 'name', + 'surface_id'), args)} + for k, v in argsdict.items(): + warn(_WARNING_KWARGS.format(type(self).__name__, k), FutureWarning) + kwargs.update(argsdict) + super().__init__(**kwargs) self.x0 = x0 @@ -704,7 +726,15 @@ class YPlane(PlaneMixin, Surface): _type = 'y-plane' _coeff_keys = ('y0',) - def __init__(self, y0=0., **kwargs): + def __init__(self, y0=0., *args, **kwargs): + # work around for accepting Surface kwargs as positional parameters + # until they are deprecated + argsdict = {k: v for k, v in zip(('boundary_type', 'name', + 'surface_id'), args)} + for k, v in argsdict.items(): + warn(_WARNING_KWARGS.format(type(self).__name__, k), FutureWarning) + kwargs.update(argsdict) + super().__init__(**kwargs) self.y0 = y0 @@ -793,7 +823,15 @@ class ZPlane(PlaneMixin, Surface): _type = 'z-plane' _coeff_keys = ('z0',) - def __init__(self, z0=0., **kwargs): + def __init__(self, z0=0., *args, **kwargs): + # work around for accepting Surface kwargs as positional parameters + # until they are deprecated + argsdict = {k: v for k, v in zip(('boundary_type', 'name', + 'surface_id'), args)} + for k, v in argsdict.items(): + warn(_WARNING_KWARGS.format(type(self).__name__, k), FutureWarning) + kwargs.update(argsdict) + super().__init__(**kwargs) self.z0 = z0 @@ -900,7 +938,7 @@ class QuadricMixin(metaclass=ABCMeta): a, b, c, d, e, f, g, h, j, k = self._get_base_coeffs() return x*(a*x + d*y + g) + y*(b*y + e*z + h) + z*(c*z + f*x + j) + k - def translate(self, vector, clone=False): + def translate(self, vector, clone=True): """Translate surface in given direction Parameters @@ -908,17 +946,18 @@ class QuadricMixin(metaclass=ABCMeta): vector : iterable of float Direction in which surface should be translated clone : bool - Whether to return a clone of the Surface or the Surface itself + Whether to return a clone of the Surface or the Surface itself. + Defaults to True Returns ------- - openmc.QuadricMixin + openmc.Surface Translated surface """ vx, vy, vz = vector a, b, c, d, e, f, g, h, j, k = self._get_base_coeffs() - k = (k + vx*vx + vy*vy + vz*vz + d*vx*vy + e*vy*vz + f*vx*vz + k = (k + a*vx**2 + b*vy**2 + c*vz**2 + d*vx*vy + e*vy*vz + f*vx*vz - g*vx - h*vy - j*vz) g = g - 2*a*vx - d*vy - f*vz h = h - 2*b*vy - d*vx - e*vz @@ -1237,6 +1276,7 @@ class XCylinder(QuadricMixin, Surface): a = d = e = f = g = 0. b = c = 1. h, j, k = -2*y0, -2*z0, y0**2 + z0**2 - r**2 + print("to base", (a, b, c, d, e, f, g, h, j, k)) return (a, b, c, d, e, f, g, h, j, k) @@ -1563,7 +1603,7 @@ class ZCylinder(QuadricMixin, Surface): """ a, b, c, d, e, f, g, h, j, k = coeffs - x0, y0 = -g / 2, -4 / 2 + x0, y0 = -g / 2, -h / 2 r = np.sqrt(x0**2 + y0**2 - k) for c, val in zip(self._coeff_keys, (x0, y0, r)): From 2a1c48e9878c463df62465cc3a927448272845fe Mon Sep 17 00:00:00 2001 From: Ethan Peterson Date: Tue, 11 Feb 2020 16:15:32 -0500 Subject: [PATCH 090/166] refactoring bounding_box and cleaning up constructors --- openmc/surface.py | 297 ++++++++++++++++++---------------------------- 1 file changed, 113 insertions(+), 184 deletions(-) diff --git a/openmc/surface.py b/openmc/surface.py index 41befbb41..5f5aecfc7 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -487,10 +487,10 @@ class Plane(PlaneMixin, Surface): kwargs.update(argsdict) super().__init__(**kwargs) - self.a = a - self.b = b - self.c = c - self.d = d + + for key, val in zip(self._coeff_keys, (a, b, c, d)): + setattr(self, key, val) + for k, v in oldkwargs.items(): if k in 'ABCD': warn(_WARNING_UPPER.format(type(self).__name__, k.lower(), k), @@ -551,8 +551,8 @@ class Plane(PlaneMixin, Surface): """ - for c, val in zip(self._coeff_keys, coeffs): - setattr(self, c, val) + for key, val in zip(self._coeff_keys, coeffs): + setattr(self, key, val) @classmethod def from_points(cls, p1, p2, p3, **kwargs): @@ -918,6 +918,34 @@ class QuadricMixin(metaclass=ABCMeta): return (np.array([-np.inf, -np.inf, -np.inf]), np.array([np.inf, np.inf, np.inf])) + def bounding_box(self, side): + """Determine an axis-aligned bounding box. + + An axis-aligned bounding box for surface half-spaces is represented by + its lower-left and upper-right coordinates. To represent infinity, + numpy.inf is used. + + 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 + + """ + + if side == '-': + return self._neg_bounds() + elif side == '+': + return self._pos_bounds() + def evaluate(self, point): """Evaluate the surface equation at a given point. @@ -1041,13 +1069,9 @@ 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): super().__init__(**kwargs) - self.x0 = x0 - self.y0 = y0 - self.z0 = z0 - self.r = r - self.dx = dx - self.dy = dy - self.dz = dz + + for key, val in zip(self._coeff_keys, (x0, y0, z0, r, dx, dy, dz)): + setattr(self, key, val) @property def x0(self): @@ -1234,9 +1258,9 @@ class XCylinder(QuadricMixin, Surface): FutureWarning) r = R super().__init__(**kwargs) - self.y0 = y0 - self.z0 = z0 - self.r = r + + for key, val in zip(self._coeff_keys, (y0, z0, r)): + setattr(self, key, val) @property def y0(self): @@ -1296,40 +1320,18 @@ class XCylinder(QuadricMixin, Surface): y0, z0 = -h / 2, -j / 2 r = np.sqrt(y0**2 + z0**2 - k) - for c, val in zip(self._coeff_keys, (y0, z0, r)): - setattr(self, c, val) + for key, val in zip(self._coeff_keys, (y0, z0, r)): + setattr(self, key, val) - def bounding_box(self, side): - """Determine an axis-aligned bounding box. + def _neg_bounds(self): + """Return the lower and upper bounds of the negative half space""" + return (np.array([-np.inf, self.y0 - self.r, self.z0 - self.r]), + np.array([np.inf, self.y0 + self.r, self.z0 + self.r])) - An axis-aligned bounding box for surface half-spaces is represented by - its lower-left and upper-right coordinates. For the x-cylinder surface, - the negative half-space is unbounded in the x- direction and the - positive half-space is unbounded in all directions. To represent - infinity, numpy.inf is used. - - 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 - - """ - - if side == '-': - return (np.array([-np.inf, self.y0 - self.r, self.z0 - self.r]), - np.array([np.inf, self.y0 + self.r, self.z0 + self.r])) - elif side == '+': - return (np.array([-np.inf, -np.inf, -np.inf]), - np.array([np.inf, np.inf, np.inf])) + def _pos_bounds(self): + """Return the lower and upper bounds of the positive half space""" + return (np.array([-np.inf, -np.inf, -np.inf]), + np.array([np.inf, np.inf, np.inf])) Cylinder.register(XCylinder) @@ -1390,9 +1392,9 @@ class YCylinder(QuadricMixin, Surface): FutureWarning) r = R super().__init__(**kwargs) - self.x0 = x0 - self.z0 = z0 - self.r = r + + for key, val in zip(self._coeff_keys, (x0, z0, r)): + setattr(self, key, val) @property def x0(self): @@ -1451,40 +1453,18 @@ class YCylinder(QuadricMixin, Surface): x0, z0 = -g / 2, -j / 2 r = np.sqrt(x0**2 + z0**2 - k) - for c, val in zip(self._coeff_keys, (x0, z0, r)): - setattr(self, c, val) + for key, val in zip(self._coeff_keys, (x0, z0, r)): + setattr(self, key, val) - def bounding_box(self, side): - """Determine an axis-aligned bounding box. + def _neg_bounds(self): + """Return the lower and upper bounds of the negative half space""" + return (np.array([self.x0 - self.r, -np.inf, self.z0 - self.r]), + np.array([self.x0 + self.r, np.inf, self.z0 + self.r])) - An axis-aligned bounding box for surface half-spaces is represented by - its lower-left and upper-right coordinates. For the y-cylinder surface, - the negative half-space is unbounded in the y- direction and the - positive half-space is unbounded in all directions. To represent - infinity, numpy.inf is used. - - 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 - - """ - - if side == '-': - return (np.array([self.x0 - self.r, -np.inf, self.z0 - self.r]), - np.array([self.x0 + self.r, np.inf, self.z0 + self.r])) - elif side == '+': - return (np.array([-np.inf, -np.inf, -np.inf]), - np.array([np.inf, np.inf, np.inf])) + def _pos_bounds(self): + """Return the lower and upper bounds of the positive half space""" + return (np.array([-np.inf, -np.inf, -np.inf]), + np.array([np.inf, np.inf, np.inf])) Cylinder.register(YCylinder) @@ -1545,9 +1525,9 @@ class ZCylinder(QuadricMixin, Surface): FutureWarning) r = R super().__init__(**kwargs) - self.x0 = x0 - self.y0 = y0 - self.r = r + + for key, val in zip(self._coeff_keys, (x0, y0, r)): + setattr(self, key, val) @property def x0(self): @@ -1606,40 +1586,18 @@ class ZCylinder(QuadricMixin, Surface): x0, y0 = -g / 2, -h / 2 r = np.sqrt(x0**2 + y0**2 - k) - for c, val in zip(self._coeff_keys, (x0, y0, r)): - setattr(self, c, val) + for key, val in zip(self._coeff_keys, (x0, y0, r)): + setattr(self, key, val) - def bounding_box(self, side): - """Determine an axis-aligned bounding box. + def _neg_bounds(self): + """Return the lower and upper bounds of the negative half space""" + return (np.array([self.x0 - self.r, self.y0 - self.r, -np.inf]), + np.array([self.x0 + self.r, self.y0 + self.r, np.inf])) - An axis-aligned bounding box for surface half-spaces is represented by - its lower-left and upper-right coordinates. For the z-cylinder surface, - the negative half-space is unbounded in the z- direction and the - positive half-space is unbounded in all directions. To represent - infinity, numpy.inf is used. - - 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 - - """ - - if side == '-': - return (np.array([self.x0 - self.r, self.y0 - self.r, -np.inf]), - np.array([self.x0 + self.r, self.y0 + self.r, np.inf])) - elif side == '+': - return (np.array([-np.inf, -np.inf, -np.inf]), - np.array([np.inf, np.inf, np.inf])) + def _pos_bounds(self): + """Return the lower and upper bounds of the positive half space""" + return (np.array([-np.inf, -np.inf, -np.inf]), + np.array([np.inf, np.inf, np.inf])) Cylinder.register(ZCylinder) @@ -1702,10 +1660,9 @@ class Sphere(QuadricMixin, Surface): FutureWarning) r = R super().__init__(**kwargs) - self.x0 = x0 - self.y0 = y0 - self.z0 = z0 - self.r = r + + for key, val in zip(self._coeff_keys, (x0, y0, z0, r)): + setattr(self, key, val) @property def x0(self): @@ -1771,41 +1728,20 @@ class Sphere(QuadricMixin, Surface): a, b, c, d, e, f, g, h, j, k = coeffs x0, y0, z0 = -g / 2, -h / 2, -j / 2 r = np.sqrt(x0**2 + y0**2 + z0**2 - k) - for c, val in zip(self._coeff_keys, (x0, y0, z0, r)): - setattr(self, c, val) + for key, val in zip(self._coeff_keys, (x0, y0, z0, r)): + setattr(self, key, val) - def bounding_box(self, side): - """Determine an axis-aligned bounding box. + def _neg_bounds(self): + """Return the lower and upper bounds of the negative half space""" + return (np.array([self.x0 - self.r, self.y0 - self.r, + self.z0 - self.r]), + np.array([self.x0 + self.r, self.y0 + self.r, + self.z0 + self.r])) - An axis-aligned bounding box for surface half-spaces is represented by - its lower-left and upper-right coordinates. The positive half-space of a - sphere is unbounded in all directions. To represent infinity, numpy.inf - is used. - - 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 - - """ - - if side == '-': - return (np.array([self.x0 - self.r, self.y0 - self.r, - self.z0 - self.r]), - np.array([self.x0 + self.r, self.y0 + self.r, - self.z0 + self.r])) - elif side == '+': - return (np.array([-np.inf, -np.inf, -np.inf]), - np.array([np.inf, np.inf, np.inf])) + def _pos_bounds(self): + """Return the lower and upper bounds of the positive half space""" + return (np.array([-np.inf, -np.inf, -np.inf]), + np.array([np.inf, np.inf, np.inf])) class Cone(QuadricMixin, Surface): @@ -1880,13 +1816,9 @@ class Cone(QuadricMixin, Surface): FutureWarning) r2 = R2 super().__init__(**kwargs) - self.x0 = x0 - self.y0 = y0 - self.z0 = z0 - self.r2 = r2 - self.dx = dx - self.dy = dy - self.dz = dz + + for key, val in zip(self._coeff_keys, (x0, y0, z0, r2, dx, dy, dz)): + setattr(self, key, val) @property def x0(self): @@ -2032,10 +1964,9 @@ class XCone(QuadricMixin, Surface): FutureWarning) r2 = R2 super().__init__(**kwargs) - self.x0 = x0 - self.y0 = y0 - self.z0 = z0 - self.r2 = r2 + + for key, val in zip(self._coeff_keys, (x0, y0, z0, r2)): + setattr(self, key, val) @property def x0(self): @@ -2106,8 +2037,8 @@ class XCone(QuadricMixin, Surface): r2 = -a x0, y0, z0 = g / (2*r2), -h / 2, -j / 2 - for c, val in zip(self._coeff_keys, (x0, y0, z0, r2)): - setattr(self, c, val) + for key, val in zip(self._coeff_keys, (x0, y0, z0, r2)): + setattr(self, key, val) Cone.register(XCone) @@ -2171,10 +2102,9 @@ class YCone(QuadricMixin, Surface): FutureWarning) r2 = R2 super().__init__(**kwargs) - self.x0 = x0 - self.y0 = y0 - self.z0 = z0 - self.r2 = r2 + + for key, val in zip(self._coeff_keys, (x0, y0, z0, r2)): + setattr(self, key, val) @property def x0(self): @@ -2245,8 +2175,8 @@ class YCone(QuadricMixin, Surface): r2 = -b x0, y0, z0 = -g / 2, h / (2*r2), -j / 2 - for c, val in zip(self._coeff_keys, (x0, y0, z0, r2)): - setattr(self, c, val) + for key, val in zip(self._coeff_keys, (x0, y0, z0, r2)): + setattr(self, key, val) Cone.register(YCone) @@ -2310,10 +2240,9 @@ class ZCone(QuadricMixin, Surface): FutureWarning) r2 = R2 super().__init__(**kwargs) - self.x0 = x0 - self.y0 = y0 - self.z0 = z0 - self.r2 = r2 + + for key, val in zip(self._coeff_keys, (x0, y0, z0, r2)): + setattr(self, key, val) @property def x0(self): @@ -2384,8 +2313,8 @@ class ZCone(QuadricMixin, Surface): r2 = -c x0, y0, z0 = -g / 2, -h / 2, j / (2*r2) - for c, val in zip(self._coeff_keys, (x0, y0, z0, r2)): - setattr(self, c, val) + for key, val in zip(self._coeff_keys, (x0, y0, z0, r2)): + setattr(self, key, val) Cone.register(ZCone) @@ -2435,8 +2364,8 @@ class Quadric(QuadricMixin, Surface): super().__init__(**kwargs) - for c, val in zip(self._coeff_keys, (a, b, c, d, e, f, g, h, j, k)): - setattr(self, c, val) + for key, val in zip(self._coeff_keys, (a, b, c, d, e, f, g, h, j, k)): + setattr(self, key, val) @property def a(self): @@ -2548,8 +2477,8 @@ class Quadric(QuadricMixin, Surface): representing the quadric surface """ - for c, val in zip(self._coeff_keys, coeffs): - setattr(self, c, val) + for key, val in zip(self._coeff_keys, coeffs): + setattr(self, key, val) class Halfspace(Region): From 29255732caec4ec2ba4a1652b5e29d097dd66080 Mon Sep 17 00:00:00 2001 From: Ethan Peterson Date: Tue, 11 Feb 2020 16:43:49 -0500 Subject: [PATCH 091/166] changed subclass finding methods to private --- openmc/surface.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/openmc/surface.py b/openmc/surface.py index 5f5aecfc7..ca1501b88 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -25,6 +25,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. @@ -275,7 +277,6 @@ class Surface(IDManagerMixin, metaclass=ABCMeta): class PlaneMixin(metaclass=ABCMeta): """A Plane Meta class for all operations on order 1 surfaces""" - def __init__(self, **kwargs): super().__init__(**kwargs) self._periodic_surface = None @@ -880,7 +881,6 @@ Plane.register(ZPlane) class QuadricMixin(metaclass=ABCMeta): """A Mixin class implementing common functionality for quadric surfaces""" - def __init__(self, **kwargs): super().__init__(**kwargs) @@ -1300,7 +1300,6 @@ class XCylinder(QuadricMixin, Surface): a = d = e = f = g = 0. b = c = 1. h, j, k = -2*y0, -2*z0, y0**2 + z0**2 - r**2 - print("to base", (a, b, c, d, e, f, g, h, j, k)) return (a, b, c, d, e, f, g, h, j, k) @@ -2668,17 +2667,18 @@ class Halfspace(Region): # Return translated surface return type(self)(memo[key], self.side) -def get_subclasses(cls): + +def _get_subclasses(cls): """Recursively find all subclasses of this class""" return set(cls.__subclasses__()).union([s for c in cls.__subclasses__() - for s in get_subclasses(c)]) + for s in _get_subclasses(c)]) -def get_subclass_map(cls): +def _get_subclass_map(cls): """Generate mapping of class _type attributes to classes""" - return {c._type: c for c in get_subclasses(cls)} + return {c._type: c for c in _get_subclasses(cls)} -_SURFACE_CLASSES = get_subclass_map(Surface) +_SURFACE_CLASSES = _get_subclass_map(Surface) From caeb4d3ce5b14936a8dc56921929063af03f46f5 Mon Sep 17 00:00:00 2001 From: Ethan Peterson Date: Tue, 11 Feb 2020 16:49:38 -0500 Subject: [PATCH 092/166] removed empty dictionary definition --- openmc/surface.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/openmc/surface.py b/openmc/surface.py index ca1501b88..5dc25103f 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -25,8 +25,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 994bd221badf2fd72e1a262f0a636060b01d5e67 Mon Sep 17 00:00:00 2001 From: Ethan Peterson Date: Tue, 11 Feb 2020 21:51:34 -0500 Subject: [PATCH 093/166] fixed general cylinder _to_base_coeffs method --- openmc/surface.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/openmc/surface.py b/openmc/surface.py index 5dc25103f..badbd630c 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -1137,12 +1137,11 @@ class Cylinder(QuadricMixin, Surface): def _get_base_coeffs(self): """Return coefficients a, b, c, d, e, f, g, h, j, k representing a general quadric surface of the form: - :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. + :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz + k = 0`. """ x0, y0, z0, r = self.x0, self.y0, self.z0, self.r dx, dy, dz = self.dx, self.dy, self.dz - x1, y1, z1 = x0 + dx, y0 + dy, z0 + dz a = dy**2 + dz**2 b = dx**2 + dz**2 @@ -1150,18 +1149,19 @@ class Cylinder(QuadricMixin, Surface): d = -2*dx*dy e = -2*dy*dz f = -2*dx*dz - g = -2*(dz*(x0*z1 - x1*z0) + dy*(x0*y1-x1*y0)) - h = 2*(dx*(x0*y1 - x1*y0) - dz*(y0*z1 - y1*z0)) - j = 2*(dx*(x0*z1 - x1*z0) + dy*(y0*z1 - y1*z0)) - k = (y0*z1 - y1*z0)**2 + (x0*z1 - x1*z0)**2 + (x0*y1 - x1*y0)**2 \ - - r**2*(dx**2 + dy**2 + dz**2) + g = 2*(dx*(z0*dz + y0*dy) - x0*(dy**2 + dz**2)) + h = 2*(dy*(z0*dz + x0*dx) - y0*(dx**2 + dz**2)) + j = 2*(dz*(y0*dy + x0*dx) - z0*(dx**2 + dy**2)) + k = x0**2*(dy**2 + dz**2) + y0**2*(dx**2 + dz**2) \ + + z0**2*(dx**2 + dy**2) - 2*dy*dz*y0*z0 \ + - 2*dx*dz*x0*z0 - 2*dx*dy*x0*y0 - r**2*(dx**2 + dy**2 + dz**2) return (a, b, c, d, e, f, g, h, j, k) def _update_from_base_coeffs(self, coeffs): """Update the current surface from coefficients representing a general quadric surface of the form: - :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. + :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz + k = 0`. Parameters ---------- @@ -1170,6 +1170,10 @@ class Cylinder(QuadricMixin, Surface): representing the quadric surface """ + pre_coeffs = self._get_base_coeffs + # infer transformation based on changes between two matrices + # perform same transformation on x0,y0,z0,dx,dy,dz if A matrix + # unchanged then it was a translation if not it was a rotation a, b, c, d, e, f, g, h, j, k = coeffs @classmethod From 777803c029d45499a4b2f40de8f0208d9830230f Mon Sep 17 00:00:00 2001 From: Ethan Peterson Date: Wed, 12 Feb 2020 14:40:12 -0500 Subject: [PATCH 094/166] implemented helper functions --- openmc/surface.py | 114 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 100 insertions(+), 14 deletions(-) diff --git a/openmc/surface.py b/openmc/surface.py index badbd630c..29ec06ae3 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -66,6 +66,7 @@ class Surface(IDManagerMixin, metaclass=ABCMeta): next_id = 1 used_ids = set() + _atol = 1.e-12 def __init__(self, surface_id=None, boundary_type='transmission', name=''): self.id = surface_id @@ -185,6 +186,30 @@ class Surface(IDManagerMixin, metaclass=ABCMeta): return memo[self] + def is_equal(self, coeffs): + """Determine if this Surface is the same as another to within a certain + tolerance + + Parameters + ---------- + coeffs : tuple + Tuple of surface coefficients to compare to the current Surface + object + + """ + def normalize(coeffs): + """Normalize coefficients by first nonzero value""" + coeffs = np.asarray(coeffs) + nonzeros = ~np.isclose(coeffs, 0., rtol=0., atol=self._atol) + norm_factor = coeffs[nonzeros][0] + return tuple([c/norm_factor for c in coeffs]) + + # get normalized sets of coefficients + coeffs = normalize(coeffs) + bcoeffs = normalize(self._get_base_coeffs()) + + return np.all(np.isclose(bcoeffs, coeffs, rtol=0., atol=self._atol)) + @abstractmethod def evaluate(self, point): pass @@ -480,11 +505,11 @@ class Plane(PlaneMixin, Surface): for k, v in argsdict.items(): warn(_WARNING_KWARGS.format(type(self).__name__, k), FutureWarning) + kwargs.update(argsdict) + for k in 'ABCD': kwargs.pop(k, None) - kwargs.update(argsdict) - super().__init__(**kwargs) for key, val in zip(self._coeff_keys, (a, b, c, d)): @@ -493,7 +518,7 @@ class Plane(PlaneMixin, Surface): for k, v in oldkwargs.items(): if k in 'ABCD': warn(_WARNING_UPPER.format(type(self).__name__, k.lower(), k), - FutureWarning) + FutureWarning) setattr(self, k.lower(), v) @property @@ -916,6 +941,38 @@ class QuadricMixin(metaclass=ABCMeta): return (np.array([-np.inf, -np.inf, -np.inf]), np.array([np.inf, np.inf, np.inf])) + def _A(self, coeffs=None): + """Helper function to generate the quadric matrix""" + if coeffs is None: + a, b, c, d, e, f, g, h, j, k = self._get_base_coeffs() + else: + a, b, c, d, e, f, g, h, j, k = coeffs + + return np.asarray([[a, d/2, f/2], [d/2, b, e/2], [f/2, e/2, c]]) + + def _b(self, coeffs=None): + """Helper function to generate the linear coefficients""" + if coeffs is None: + a, b, c, d, e, f, g, h, j, k = self._get_base_coeffs() + else: + a, b, c, d, e, f, g, h, j, k = coeffs + + return np.asarray([g, h, j]) + + def _c(self, coeffs=None): + """Helper function to generate the constant coefficient""" + if coeffs is None: + return self._get_base_coeffs()[-1] + return coeffs[-1] + + def eigh(self, coeffs=None): + """Wrapper method for returning eigenvalues and eigenvectors of this + quadric surface which is used for transformations. + + """ + A = self._A(coeffs=coeffs) + return np.linalg.eigh(A) + def bounding_box(self, side): """Determine an axis-aligned bounding box. @@ -981,13 +1038,22 @@ class QuadricMixin(metaclass=ABCMeta): Translated surface """ - vx, vy, vz = vector + vector = np.asarray(vector) + A = self._A() + bvec = self._b() + const = self._c() + a, b, c, d, e, f, g, h, j, k = self._get_base_coeffs() - k = (k + a*vx**2 + b*vy**2 + c*vz**2 + d*vx*vy + e*vy*vz + f*vx*vz - - g*vx - h*vy - j*vz) - g = g - 2*a*vx - d*vy - f*vz - h = h - 2*b*vy - d*vx - e*vz - j = j - 2*c*vz - e*vy - f*vx + g, h, j = bvec - 2*(vector.T @ A) + k = const + vector.T @ A @ vector - bvec.T @ vector + + #vx, vy, vz = vector + #a, b, c, d, e, f, g, h, j, k = self._get_base_coeffs() + #k = (k + a*vx**2 + b*vy**2 + c*vz**2 + d*vx*vy + e*vy*vz + f*vx*vz + # - g*vx - h*vy - j*vz) + #g = g - 2*a*vx - d*vy - f*vz + #h = h - 2*b*vy - d*vx - e*vz + #j = j - 2*c*vz - e*vy - f*vx if clone: surf = self.clone() @@ -1065,7 +1131,9 @@ class Cylinder(QuadricMixin, Surface): """ _type = 'cylinder' _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): + super().__init__(**kwargs) for key, val in zip(self._coeff_keys, (x0, y0, z0, r, dx, dy, dz)): @@ -1170,11 +1238,29 @@ class Cylinder(QuadricMixin, Surface): representing the quadric surface """ - pre_coeffs = self._get_base_coeffs - # infer transformation based on changes between two matrices - # perform same transformation on x0,y0,z0,dx,dy,dz if A matrix - # unchanged then it was a translation if not it was a rotation - a, b, c, d, e, f, g, h, j, k = coeffs + if self.is_equal(coeffs): + print("surfaces are identical") + return + + if np.isclose(self.Amat(), self.Amat(coeffs), rtol=0., atol=self._atol): + # transformation was translation + vec = np.asarray([self.x0, self.y0, self.z0]) + A = self.Amat() + bvec = self.bvec() + 2*np.dot(vec.T, A) + + # get eigenvalues and eigenvectors of quadric matrix + w, v = self.eigh() + w, u = self.eigh(coeffs=coeffs) + Rmat = np.dot(u.T, v) + xyz0 = np.asarray([self.x0, self.y0, self.z0]) + dxdydz0 = np.asarray([self.dx, self.dy, self.dz]) + # principal axis for cylinder is first eigenvector which has + # an eigenvalue of 0 + x0, y0, z0 = np.dot(Rmat, xyz0) + dx, dy, dz = np.dot(Rmat, dxdydz0) + r = self.r + for key, val in zip(self._coeff_keys, (x0, y0, z0, r, dx, dy, dz)): + setattr(self, key, val) @classmethod def from_points(cls, p1, p2, r=1., **kwargs): From 90d41aeabefd5df975fea5be1128206d084294eb Mon Sep 17 00:00:00 2001 From: Ethan Peterson Date: Wed, 12 Feb 2020 21:21:47 -0500 Subject: [PATCH 095/166] finished general cone transformation --- openmc/surface.py | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/openmc/surface.py b/openmc/surface.py index 29ec06ae3..71112427e 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -1239,7 +1239,6 @@ class Cylinder(QuadricMixin, Surface): """ if self.is_equal(coeffs): - print("surfaces are identical") return if np.isclose(self.Amat(), self.Amat(coeffs), rtol=0., atol=self._atol): @@ -1844,13 +1843,13 @@ class Cone(QuadricMixin, Surface): z-coordinate of the apex. Defaults to 0. r2 : float, optional Parameter related to the aperature. Defaults to 1. - u : float, optional + dx : float, optional x-component of the vector representing the axis of the cone. Defaults to 0. - v : float, optional + dy : float, optional y-component of the vector representing the axis of the cone. Defaults to 0. - w : float, optional + dz : float, optional z-component of the vector representing the axis of the cone. Defaults to 1. surface_id : int, optional @@ -1976,7 +1975,23 @@ class Cone(QuadricMixin, Surface): :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. """ - return (0., 0., 1., self.z0) + x0, y0, z0, r2 = self.x0, self.y0, self.z0, self.r2 + dx, dy, dz = self.dx, self.dy, self.dz + cos2 = 1 / (1 + r2) + c1 = (dx**2 + dy**2 + dz**2)*cos2 + + a = dx**2 - c1 + b = dy**2 - c1 + c = dz**2 - c1 + d = 2*dx*dy + e = 2*dy*dz + f = 2*dx*dz + g = -2*(dx**2*x0 + dx*dy*y0 + dx*dz*z0 - c1) + h = -2*(dy**2*y0 + dx*dy*x0 + dy*dz*z0 - c1) + j = -2*(dz**2*y0 + dx*dz*x0 + dy*dz*y0 - c1) + k = (dx*x0 + dy*y0 + dz*z0)**2 - c1*(x0**2 + y0**2 + z0**2) + + return (a, b, c, d, e, f, g, h, j, k) def _update_from_base_coeffs(self, coeffs): """Update the current surface from coefficients representing a general From 2919b2791aeb6f96c74f34135d02b9f786f05635 Mon Sep 17 00:00:00 2001 From: Ethan Peterson Date: Thu, 13 Feb 2020 12:06:01 -0500 Subject: [PATCH 096/166] finished implementation --- openmc/surface.py | 223 ++++++++++++++++++++++++++++------------------ 1 file changed, 137 insertions(+), 86 deletions(-) diff --git a/openmc/surface.py b/openmc/surface.py index 71112427e..ba6ec15f7 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -1,8 +1,7 @@ from abc import ABCMeta, abstractmethod from collections import OrderedDict -from collections.abc import Iterable from copy import deepcopy -from numbers import Real, Integral +from numbers import Real from xml.etree import ElementTree as ET from warnings import warn @@ -186,29 +185,38 @@ class Surface(IDManagerMixin, metaclass=ABCMeta): return memo[self] - def is_equal(self, coeffs): - """Determine if this Surface is the same as another to within a certain - tolerance + def normalize(self, coeffs=None): + """Normalize coefficients by first nonzero value + Parameters + ---------- + coeffs : tuple, optional + Tuple of surface coefficients to normalize. If none are supplied, + the coefficients from the current surface will be used. + + Returns + ------- + tuple of normalized coefficients + + """ + coeffs = np.asarray(coeffs) + nonzeros = ~np.isclose(coeffs, 0., rtol=0., atol=self._atol) + norm_factor = coeffs[nonzeros][0] + return tuple([c/norm_factor for c in coeffs]) + + def is_equal(self, other): + """Determine if this Surface is equivalent to another Parameters ---------- - coeffs : tuple - Tuple of surface coefficients to compare to the current Surface - object + other : instance of openmc.Surface + Instance of openmc.Surface that should be compared to the current + surface """ - def normalize(coeffs): - """Normalize coefficients by first nonzero value""" - coeffs = np.asarray(coeffs) - nonzeros = ~np.isclose(coeffs, 0., rtol=0., atol=self._atol) - norm_factor = coeffs[nonzeros][0] - return tuple([c/norm_factor for c in coeffs]) + coeffs1 = normalize(self._get_base_coeffs()) + coeffs2 = normalize(other._get_base_coeffs()) - # get normalized sets of coefficients - coeffs = normalize(coeffs) - bcoeffs = normalize(self._get_base_coeffs()) - - return np.all(np.isclose(bcoeffs, coeffs, rtol=0., atol=self._atol)) + return np.all(np.isclose(coeffs1, coeffs2, rtol=0., atol=self._atol)) @abstractmethod def evaluate(self, point): @@ -299,7 +307,7 @@ class Surface(IDManagerMixin, metaclass=ABCMeta): class PlaneMixin(metaclass=ABCMeta): - """A Plane Meta class for all operations on order 1 surfaces""" + """A Plane mixin class for all operations on order 1 surfaces""" def __init__(self, **kwargs): super().__init__(**kwargs) self._periodic_surface = None @@ -941,37 +949,47 @@ class QuadricMixin(metaclass=ABCMeta): return (np.array([-np.inf, -np.inf, -np.inf]), np.array([np.inf, np.inf, np.inf])) - def _A(self, coeffs=None): - """Helper function to generate the quadric matrix""" + def get_Abc(self, coeffs=None): + """Compute matrix, vector, and scalar coefficients for this surface or + for a specified set of coefficients. + + Parameters + ---------- + coeffs : tuple, optional + Tuple of coefficients from which to compute the quadric elements. + If none are supplied the coefficients of this surface will be used. + """ if coeffs is None: a, b, c, d, e, f, g, h, j, k = self._get_base_coeffs() else: a, b, c, d, e, f, g, h, j, k = coeffs - return np.asarray([[a, d/2, f/2], [d/2, b, e/2], [f/2, e/2, c]]) + A = np.asarray([[a, d/2, f/2], [d/2, b, e/2], [f/2, e/2, c]]) + bvec = np.asarray([g, h, j]) - def _b(self, coeffs=None): - """Helper function to generate the linear coefficients""" - if coeffs is None: - a, b, c, d, e, f, g, h, j, k = self._get_base_coeffs() - else: - a, b, c, d, e, f, g, h, j, k = coeffs - - return np.asarray([g, h, j]) - - def _c(self, coeffs=None): - """Helper function to generate the constant coefficient""" - if coeffs is None: - return self._get_base_coeffs()[-1] - return coeffs[-1] + return A, bvec, k def eigh(self, coeffs=None): """Wrapper method for returning eigenvalues and eigenvectors of this quadric surface which is used for transformations. + Parameters + ---------- + coeffs : tuple, optional + Tuple of coefficients from which to compute the quadric elements. + If none are supplied the coefficients of this surface will be used. + + Returns + ------- + w, v : tuple of numpy arrays with shapes (3,) and (3,3) respectively + Returns the eigenvalues and eigenvectors of the quadric matrix A + that represents the supplied coefficients. The vector w contains + the eigenvalues in ascending order and the matrix v contains the + eigenvectors such that v[:,i] is the eigenvector corresponding to + the eigenvalue w[i]. + """ - A = self._A(coeffs=coeffs) - return np.linalg.eigh(A) + return np.linalg.eigh(self.get_Abc(coeffs=coeffs)[0]) def bounding_box(self, side): """Determine an axis-aligned bounding box. @@ -1017,9 +1035,9 @@ class QuadricMixin(metaclass=ABCMeta): Jz' + K = 0` """ - x, y, z = point - a, b, c, d, e, f, g, h, j, k = self._get_base_coeffs() - return x*(a*x + d*y + g) + y*(b*y + e*z + h) + z*(c*z + f*x + j) + k + 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 def translate(self, vector, clone=True): """Translate surface in given direction @@ -1039,21 +1057,12 @@ class QuadricMixin(metaclass=ABCMeta): """ vector = np.asarray(vector) - A = self._A() - bvec = self._b() - const = self._c() + A, bvec, cnst = self.get_Abc() - a, b, c, d, e, f, g, h, j, k = self._get_base_coeffs() - g, h, j = bvec - 2*(vector.T @ A) - k = const + vector.T @ A @ vector - bvec.T @ vector - - #vx, vy, vz = vector - #a, b, c, d, e, f, g, h, j, k = self._get_base_coeffs() - #k = (k + a*vx**2 + b*vy**2 + c*vz**2 + d*vx*vy + e*vy*vz + f*vx*vz - # - g*vx - h*vy - j*vz) - #g = g - 2*a*vx - d*vy - f*vz - #h = h - 2*b*vy - d*vx - e*vz - #j = j - 2*c*vz - e*vy - f*vx + a, b, c, d, e, f = self._get_base_coeffs()[0:6] + 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) if clone: surf = self.clone() @@ -1210,19 +1219,19 @@ class Cylinder(QuadricMixin, Surface): """ x0, y0, z0, r = self.x0, self.y0, self.z0, self.r dx, dy, dz = self.dx, self.dy, self.dz + dx2, dy2, dz2 = dx**2, dy**2, dz**2 - a = dy**2 + dz**2 - b = dx**2 + dz**2 - c = dx**2 + dy**2 + a = dy2 + dz2 + b = dx2 + dz2 + c = dx2 + dy2 d = -2*dx*dy e = -2*dy*dz f = -2*dx*dz - g = 2*(dx*(z0*dz + y0*dy) - x0*(dy**2 + dz**2)) - h = 2*(dy*(z0*dz + x0*dx) - y0*(dx**2 + dz**2)) - j = 2*(dz*(y0*dy + x0*dx) - z0*(dx**2 + dy**2)) - k = x0**2*(dy**2 + dz**2) + y0**2*(dx**2 + dz**2) \ - + z0**2*(dx**2 + dy**2) - 2*dy*dz*y0*z0 \ - - 2*dx*dz*x0*z0 - 2*dx*dy*x0*y0 - r**2*(dx**2 + dy**2 + dz**2) + g = 2*(dx*(z0*dz + y0*dy) - x0*(dy2 + dz2)) + h = 2*(dy*(z0*dz + x0*dx) - y0*(dx2 + dz2)) + j = 2*(dz*(y0*dy + x0*dx) - z0*(dx2 + dy2)) + k = x0**2*(dy2 + dz2) + y0**2*(dx2 + dz2) + z0**2*(dx2 + dy2) \ + + e*y0*z0 + f*x0*z0 + d*x0*y0 - r**2*(dx2 + dy2 + dz2) return (a, b, c, d, e, f, g, h, j, k) @@ -1238,28 +1247,38 @@ class Cylinder(QuadricMixin, Surface): representing the quadric surface """ - if self.is_equal(coeffs): - return + raise NotImplementedError('_update_from_base_coeffs for {} is ' + 'undefined'.format(self.__cls__.__name__)) - if np.isclose(self.Amat(), self.Amat(coeffs), rtol=0., atol=self._atol): - # transformation was translation - vec = np.asarray([self.x0, self.y0, self.z0]) - A = self.Amat() - bvec = self.bvec() + 2*np.dot(vec.T, A) + def translate(self, vector, clone=True): + """Translate surface in given direction + + Parameters + ---------- + vector : iterable of float + Direction in which surface should be translated + clone : bool + Whether to return a clone of the Surface or the Surface itself. + Defaults to True + + Returns + ------- + openmc.Surface + Translated surface + + """ + + if clone: + surf = self.clone() + else: + surf = self + + surf.x0 += vector[0] + surf.y0 += vector[1] + surf.z0 += vector[2] + + return surf - # get eigenvalues and eigenvectors of quadric matrix - w, v = self.eigh() - w, u = self.eigh(coeffs=coeffs) - Rmat = np.dot(u.T, v) - xyz0 = np.asarray([self.x0, self.y0, self.z0]) - dxdydz0 = np.asarray([self.dx, self.dy, self.dz]) - # principal axis for cylinder is first eigenvector which has - # an eigenvalue of 0 - x0, y0, z0 = np.dot(Rmat, xyz0) - dx, dy, dz = np.dot(Rmat, dxdydz0) - r = self.r - for key, val in zip(self._coeff_keys, (x0, y0, z0, r, dx, dy, dz)): - setattr(self, key, val) @classmethod def from_points(cls, p1, p2, r=1., **kwargs): @@ -1797,6 +1816,7 @@ class Sphere(QuadricMixin, Surface): d = e = f = 0. g, h, j = -2*x0, -2*y0, -2*z0 k = x0**2 + y0**2 + z0**2 - r**2 + return (a, b, c, d, e, f, g, h, j, k) def _update_from_base_coeffs(self, coeffs): @@ -1814,6 +1834,7 @@ class Sphere(QuadricMixin, Surface): a, b, c, d, e, f, g, h, j, k = coeffs x0, y0, z0 = -g / 2, -h / 2, -j / 2 r = np.sqrt(x0**2 + y0**2 + z0**2 - k) + for key, val in zip(self._coeff_keys, (x0, y0, z0, r)): setattr(self, key, val) @@ -2005,7 +2026,37 @@ class Cone(QuadricMixin, Surface): representing the quadric surface """ - a, b, c, d, e, f, g, h, j, k = coeffs + raise NotImplementedError('_update_from_base_coeffs for {} is ' + 'undefined'.format(self.__cls__.__name__)) + + def translate(self, vector, clone=True): + """Translate surface in given direction + + Parameters + ---------- + vector : iterable of float + Direction in which surface should be translated + clone : bool + Whether to return a clone of the Surface or the Surface itself. + Defaults to True + + Returns + ------- + openmc.Surface + Translated surface + + """ + + if clone: + surf = self.clone() + else: + surf = self + + surf.x0 += vector[0] + surf.y0 += vector[1] + surf.z0 += vector[2] + + return surf class XCone(QuadricMixin, Surface): From f1b14c95e2e1968b803765150d59836c0df70c28 Mon Sep 17 00:00:00 2001 From: Ethan Peterson Date: Thu, 13 Feb 2020 13:43:42 -0500 Subject: [PATCH 097/166] implemented individual translate and evaulate methods and removed extraneous docstrings --- openmc/surface.py | 711 +++++------------- .../cmfd_feed_2g/results_true.dat | 2 +- tests/regression_tests/triso/results_true.dat | 2 +- 3 files changed, 202 insertions(+), 513 deletions(-) diff --git a/openmc/surface.py b/openmc/surface.py index ba6ec15f7..064ac7242 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -7,7 +7,7 @@ from warnings import warn import numpy as np -from openmc.checkvalue import check_type, check_value, check_length +from openmc.checkvalue import check_type, check_value from openmc.region import Region, Intersection, Union from openmc.mixin import IDManagerMixin @@ -330,29 +330,6 @@ class PlaneMixin(metaclass=ABCMeta): """ pass - @abstractmethod - def _update_from_base_coeffs(self, coeffs): - """Update the current plane from coefficients representing a general - plane of the form :math:`ax + by + cz = d`. - - Parameters - ---------- - coeffs : tuple - Tuple of general coefficients (a, b, c, d) representing the plane - - """ - pass - - def _neg_bounds(self): - """Return the lower and upper bounds of the negative half space""" - return (np.array([-np.inf, -np.inf, -np.inf]), - np.array([np.inf, np.inf, np.inf])) - - def _pos_bounds(self): - """Return the lower and upper bounds of the positive half space""" - return (np.array([-np.inf, -np.inf, -np.inf]), - np.array([np.inf, np.inf, np.inf])) - def evaluate(self, point): """Evaluate the surface equation at a given point. @@ -373,6 +350,7 @@ class PlaneMixin(metaclass=ABCMeta): a, b, c, d = self._get_base_coeffs() return a*x + b*y + c*z - d + @abstractmethod def translate(self, vector, clone=True): """Translate surface in given direction @@ -390,46 +368,7 @@ class PlaneMixin(metaclass=ABCMeta): Translated surface """ - vx, vy, vz = vector - a, b, c, d = self._get_base_coeffs() - d = d + a*vx + b*vy + c*vz - - if clone: - surf = self.clone() - else: - surf = self - - surf._update_from_base_coeffs((a, b, c, d)) - return surf - - def bounding_box(self, side): - """Determine an axis-aligned bounding box. - - An axis-aligned bounding box for surface half-spaces is represented by - its lower-left and upper-right coordinates. For the z-plane surface, the - half-spaces are unbounded in their x- and y- directions. To represent - infinity, numpy.inf is used. - - 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 - - """ - - if side == '-': - return self._neg_bounds() - elif side == '+': - return self._pos_bounds() + pass def to_xml_element(self): """Return XML representation of the surface @@ -566,25 +505,18 @@ class Plane(PlaneMixin, Surface): self._coefficients['d'] = d def _get_base_coeffs(self): - """Return coefficients a, b, c, d representing a general plane of the - form :math:`ax + by + cz = d`. - - """ return (self.a, self.b, self.c, self.d) - def _update_from_base_coeffs(self, coeffs): - """Update the current plane from coefficients representing a general - plane of the form :math:`ax + by + cz = d`. - - Parameters - ---------- - coeffs : tuple - Tuple of general coefficients (a, b, c, d) representing the plane - - """ - - for key, val in zip(self._coeff_keys, coeffs): - setattr(self, key, val) + def translate(self, vector, clone=True): + vx, vy, vz = vector + a, b, c, d = self._get_base_coeffs() + d = d + a*vx + b*vy + c*vz + if clone: + surf = self.clone() + else: + surf = self + surf.d = d + return surf @classmethod def from_points(cls, p1, p2, p3, **kwargs): @@ -683,34 +615,26 @@ class XPlane(PlaneMixin, Surface): self._coefficients['x0'] = x0 def _get_base_coeffs(self): - """Return coefficients a, b, c, d representing a general plane of the - form :math:`ax + by + cz = d`. - - """ return (1., 0., 0., self.x0) - def _update_from_base_coeffs(self, coeffs): - """Update the current plane from coefficients representing a general - plane of the form :math:`ax + by + cz = d`. + 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])) - Parameters - ---------- - coeffs : tuple - Tuple of general coefficients (a, b, c, d) representing the plane + def evaluate(self, point): + return point[0] - self.x0 - """ - a, b, c, d = coeffs - self.x0 = d / a - - def _neg_bounds(self): - """Return the lower and upper bounds of the negative half space""" - return (np.array([-np.inf, -np.inf, -np.inf]), - np.array([self.x0, np.inf, np.inf])) - - def _pos_bounds(self): - """Return the lower and upper bounds of the positive half space""" - return (np.array([self.x0, -np.inf, -np.inf]), - np.array([np.inf, np.inf, np.inf])) + def translate(self, vector, clone=True): + if clone: + surf = self.clone() + else: + surf = self + surf.x0 += vector[0] + return surf Plane.register(XPlane) @@ -780,34 +704,26 @@ class YPlane(PlaneMixin, Surface): self._coefficients['y0'] = y0 def _get_base_coeffs(self): - """Return coefficients a, b, c, d representing a general plane of the - form :math:`ax + by + cz = d`. - - """ return (0., 1., 0., self.y0) - def _update_from_base_coeffs(self, coeffs): - """Update the current plane from coefficients representing a general - plane of the form :math:`ax + by + cz = d`. + 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])) - Parameters - ---------- - coeffs : tuple - Tuple of general coefficients (a, b, c, d) representing the plane + def evaluate(self, point): + return point[1] - self.y0 - """ - a, b, c, d = coeffs - self.y0 = d / b - - def _neg_bounds(self): - """Return the lower and upper bounds of the negative half space""" - return (np.array([-np.inf, -np.inf, -np.inf]), - np.array([np.inf, self.y0, np.inf])) - - def _pos_bounds(self): - """Return the lower and upper bounds of the positive half space""" - return (np.array([-np.inf, self.y0, -np.inf]), - np.array([np.inf, np.inf, np.inf])) + def translate(self, vector, clone=True): + if clone: + surf = self.clone() + else: + surf = self + surf.y0 += vector[1] + return surf Plane.register(YPlane) @@ -877,34 +793,26 @@ class ZPlane(PlaneMixin, Surface): self._coefficients['z0'] = z0 def _get_base_coeffs(self): - """Return coefficients a, b, c, d, e, f, g, h, j, k representing a - general plane form :math:`ax + by + cz = d`. - - """ return (0., 0., 1., self.z0) - def _update_from_base_coeffs(self, coeffs): - """Update the current plane from coefficients representing a general - plane of the form :math:`ax + by + cz = d`. + 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])) - Parameters - ---------- - coeffs : tuple - Tuple of general coefficients (a, b, c, d) representing the plane + def evaluate(self, point): + return point[2] - self.z0 - """ - a, b, c, d = coeffs - self.z0 = d / c - - def _neg_bounds(self): - """Return the lower and upper bounds of the negative half space""" - return (np.array([-np.inf, -np.inf, -np.inf]), - np.array([np.inf, np.inf, self.z0])) - - def _pos_bounds(self): - """Return the lower and upper bounds of the positive half space""" - return (np.array([-np.inf, -np.inf, self.z0]), - np.array([np.inf, np.inf, np.inf])) + def translate(self, vector, clone=True): + if clone: + surf = self.clone() + else: + surf = self + surf.z0 += vector[2] + return surf Plane.register(ZPlane) @@ -924,31 +832,6 @@ class QuadricMixin(metaclass=ABCMeta): """ pass - @abstractmethod - def _update_from_base_coeffs(self, coeffs): - """Update the current surface from coefficients representing a general - quadric surface of the form: - :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. - - Parameters - ---------- - coeffs : tuple - Tuple of general coefficients (a, b, c, d, e, f, g, h, j, k) - representing the quadric surface - - """ - pass - - def _neg_bounds(self): - """Return the lower and upper bounds of the negative half space""" - return (np.array([-np.inf, -np.inf, -np.inf]), - np.array([np.inf, np.inf, np.inf])) - - def _pos_bounds(self): - """Return the lower and upper bounds of the positive half space""" - return (np.array([-np.inf, -np.inf, -np.inf]), - np.array([np.inf, np.inf, np.inf])) - def get_Abc(self, coeffs=None): """Compute matrix, vector, and scalar coefficients for this surface or for a specified set of coefficients. @@ -991,34 +874,6 @@ class QuadricMixin(metaclass=ABCMeta): """ return np.linalg.eigh(self.get_Abc(coeffs=coeffs)[0]) - def bounding_box(self, side): - """Determine an axis-aligned bounding box. - - An axis-aligned bounding box for surface half-spaces is represented by - its lower-left and upper-right coordinates. To represent infinity, - numpy.inf is used. - - 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 - - """ - - if side == '-': - return self._neg_bounds() - elif side == '+': - return self._pos_bounds() - def evaluate(self, point): """Evaluate the surface equation at a given point. @@ -1039,6 +894,7 @@ class QuadricMixin(metaclass=ABCMeta): A, b, c = self.get_Abc() return np.matmul(x.T, np.matmul(A, x)) + np.matmul(b.T, x) + c + @abstractmethod def translate(self, vector, clone=True): """Translate surface in given direction @@ -1056,22 +912,7 @@ class QuadricMixin(metaclass=ABCMeta): Translated surface """ - vector = np.asarray(vector) - A, bvec, cnst = self.get_Abc() - - a, b, c, d, e, f = self._get_base_coeffs()[0:6] - 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) - - if clone: - surf = self.clone() - else: - surf = self - - surf._update_from_base_coeffs((a, b, c, d, e, f, g, h, j, k)) - - return surf + pass class Cylinder(QuadricMixin, Surface): @@ -1212,11 +1053,6 @@ class Cylinder(QuadricMixin, Surface): self._coefficients['dz'] = dz def _get_base_coeffs(self): - """Return coefficients a, b, c, d, e, f, g, h, j, k representing a - general quadric surface of the form: - :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz + k = 0`. - - """ x0, y0, z0, r = self.x0, self.y0, self.z0, self.r dx, dy, dz = self.dx, self.dy, self.dz dx2, dy2, dz2 = dx**2, dy**2, dz**2 @@ -1235,39 +1071,7 @@ class Cylinder(QuadricMixin, Surface): return (a, b, c, d, e, f, g, h, j, k) - def _update_from_base_coeffs(self, coeffs): - """Update the current surface from coefficients representing a general - quadric surface of the form: - :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz + k = 0`. - - Parameters - ---------- - coeffs : tuple - Tuple of general coefficients (a, b, c, d, e, f, g, h, j, k) - representing the quadric surface - - """ - raise NotImplementedError('_update_from_base_coeffs for {} is ' - 'undefined'.format(self.__cls__.__name__)) - def translate(self, vector, clone=True): - """Translate surface in given direction - - Parameters - ---------- - vector : iterable of float - Direction in which surface should be translated - clone : bool - Whether to return a clone of the Surface or the Surface itself. - Defaults to True - - Returns - ------- - openmc.Surface - Translated surface - - """ - if clone: surf = self.clone() else: @@ -1279,7 +1083,6 @@ class Cylinder(QuadricMixin, Surface): return surf - @classmethod def from_points(cls, p1, p2, r=1., **kwargs): """Return a cylinder given points that define the axis and a radius. @@ -1396,11 +1199,6 @@ class XCylinder(QuadricMixin, Surface): self._coefficients['r'] = r def _get_base_coeffs(self): - """Return coefficients a, b, c, d, e, f, g, h, j, k representing a - general quadric surface of the form: - :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. - - """ y0, z0, r = self.y0, self.z0, self.r a = d = e = f = g = 0. @@ -1409,34 +1207,27 @@ class XCylinder(QuadricMixin, Surface): return (a, b, c, d, e, f, g, h, j, k) - def _update_from_base_coeffs(self, coeffs): - """Update the current surface from coefficients representing a general - quadric surface of the form: - :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. + def bounding_box(self, side): + if side == '-': + return (np.array([-np.inf, self.y0 - self.r, self.z0 - self.r]), + np.array([np.inf, self.y0 + self.r, self.z0 + self.r])) + elif side == '+': + return (np.array([-np.inf, -np.inf, -np.inf]), + np.array([np.inf, np.inf, np.inf])) - Parameters - ---------- - coeffs : tuple - Tuple of general coefficients (a, b, c, d, e, f, g, h, j, k) - representing the quadric surface + def evaluate(self, point): + y = point[1] - self.y0 + z = point[2] - self.z0 + return y**2 + z**2 - self.r**2 - """ - a, b, c, d, e, f, g, h, j, k = coeffs - y0, z0 = -h / 2, -j / 2 - r = np.sqrt(y0**2 + z0**2 - k) - - for key, val in zip(self._coeff_keys, (y0, z0, r)): - setattr(self, key, val) - - def _neg_bounds(self): - """Return the lower and upper bounds of the negative half space""" - return (np.array([-np.inf, self.y0 - self.r, self.z0 - self.r]), - np.array([np.inf, self.y0 + self.r, self.z0 + self.r])) - - def _pos_bounds(self): - """Return the lower and upper bounds of the positive half space""" - return (np.array([-np.inf, -np.inf, -np.inf]), - np.array([np.inf, np.inf, np.inf])) + def translate(self, vector, clone=True): + if clone: + surf = self.clone() + else: + surf = self + surf.y0 += vector[1] + surf.z0 += vector[2] + return surf Cylinder.register(XCylinder) @@ -1529,11 +1320,6 @@ class YCylinder(QuadricMixin, Surface): self._coefficients['r'] = r def _get_base_coeffs(self): - """Return coefficients a, b, c, d, e, f, g, h, j, k representing a - general quadric surface of the form: - :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. - - """ x0, z0, r = self.x0, self.z0, self.r b = d = e = f = h = 0. @@ -1542,34 +1328,27 @@ class YCylinder(QuadricMixin, Surface): return (a, b, c, d, e, f, g, h, j, k) - def _update_from_base_coeffs(self, coeffs): - """Update the current surface from coefficients representing a general - quadric surface of the form: - :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. + def bounding_box(self, side): + if side == '-': + return (np.array([self.x0 - self.r, -np.inf, self.z0 - self.r]), + np.array([self.x0 + self.r, np.inf, self.z0 + self.r])) + elif side == '+': + return (np.array([-np.inf, -np.inf, -np.inf]), + np.array([np.inf, np.inf, np.inf])) - Parameters - ---------- - coeffs : tuple - Tuple of general coefficients (a, b, c, d, e, f, g, h, j, k) - representing the quadric surface + def evaluate(self, point): + x = point[0] - self.x0 + z = point[2] - self.z0 + return x**2 + z**2 - self.r**2 - """ - a, b, c, d, e, f, g, h, j, k = coeffs - x0, z0 = -g / 2, -j / 2 - r = np.sqrt(x0**2 + z0**2 - k) - - for key, val in zip(self._coeff_keys, (x0, z0, r)): - setattr(self, key, val) - - def _neg_bounds(self): - """Return the lower and upper bounds of the negative half space""" - return (np.array([self.x0 - self.r, -np.inf, self.z0 - self.r]), - np.array([self.x0 + self.r, np.inf, self.z0 + self.r])) - - def _pos_bounds(self): - """Return the lower and upper bounds of the positive half space""" - return (np.array([-np.inf, -np.inf, -np.inf]), - np.array([np.inf, np.inf, np.inf])) + def translate(self, vector, clone=True): + if clone: + surf = self.clone() + else: + surf = self + surf.x0 += vector[0] + surf.z0 += vector[2] + return surf Cylinder.register(YCylinder) @@ -1662,11 +1441,6 @@ class ZCylinder(QuadricMixin, Surface): self._coefficients['r'] = r def _get_base_coeffs(self): - """Return coefficients a, b, c, d, e, f, g, h, j, k representing a - general quadric surface of the form: - :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. - - """ x0, y0, r = self.x0, self.y0, self.r c = d = e = f = j = 0. @@ -1675,34 +1449,27 @@ class ZCylinder(QuadricMixin, Surface): return (a, b, c, d, e, f, g, h, j, k) - def _update_from_base_coeffs(self, coeffs): - """Update the current surface from coefficients representing a general - quadric surface of the form: - :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. + def bounding_box(self, side): + if side == '-': + return (np.array([self.x0 - self.r, self.y0 - self.r, -np.inf]), + np.array([self.x0 + self.r, self.y0 + self.r, np.inf])) + elif side == '+': + return (np.array([-np.inf, -np.inf, -np.inf]), + np.array([np.inf, np.inf, np.inf])) - Parameters - ---------- - coeffs : tuple - Tuple of general coefficients (a, b, c, d, e, f, g, h, j, k) - representing the quadric surface + def evaluate(self, point): + x = point[0] - self.x0 + y = point[1] - self.y0 + return x**2 + y**2 - self.r**2 - """ - a, b, c, d, e, f, g, h, j, k = coeffs - x0, y0 = -g / 2, -h / 2 - r = np.sqrt(x0**2 + y0**2 - k) - - for key, val in zip(self._coeff_keys, (x0, y0, r)): - setattr(self, key, val) - - def _neg_bounds(self): - """Return the lower and upper bounds of the negative half space""" - return (np.array([self.x0 - self.r, self.y0 - self.r, -np.inf]), - np.array([self.x0 + self.r, self.y0 + self.r, np.inf])) - - def _pos_bounds(self): - """Return the lower and upper bounds of the positive half space""" - return (np.array([-np.inf, -np.inf, -np.inf]), - np.array([np.inf, np.inf, np.inf])) + def translate(self, vector, clone=True): + if clone: + surf = self.clone() + else: + surf = self + surf.x0 += vector[0] + surf.y0 += vector[1] + return surf Cylinder.register(ZCylinder) @@ -1806,11 +1573,6 @@ class Sphere(QuadricMixin, Surface): self._coefficients['r'] = r def _get_base_coeffs(self): - """Return coefficients a, b, c, d, e, f, g, h, j, k representing a - general quadric surface of the form: - :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. - - """ x0, y0, z0, r = self.x0, self.y0, self.z0, self.r a = b = c = 1. d = e = f = 0. @@ -1819,36 +1581,31 @@ class Sphere(QuadricMixin, Surface): return (a, b, c, d, e, f, g, h, j, k) - def _update_from_base_coeffs(self, coeffs): - """Update the current surface from coefficients representing a general - quadric surface of the form: - :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. + def bounding_box(self, side): + if side == '-': + return (np.array([self.x0 - self.r, self.y0 - self.r, + self.z0 - self.r]), + np.array([self.x0 + self.r, self.y0 + self.r, + self.z0 + self.r])) + elif side == '+': + return (np.array([-np.inf, -np.inf, -np.inf]), + np.array([np.inf, np.inf, np.inf])) - Parameters - ---------- - coeffs : tuple - Tuple of general coefficients (a, b, c, d, e, f, g, h, j, k) - representing the quadric surface + def evaluate(self, point): + x = point[0] - self.x0 + y = point[1] - self.y0 + z = point[2] - self.z0 + return x**2 + y**2 + z**2 - self.r**2 - """ - a, b, c, d, e, f, g, h, j, k = coeffs - x0, y0, z0 = -g / 2, -h / 2, -j / 2 - r = np.sqrt(x0**2 + y0**2 + z0**2 - k) - - for key, val in zip(self._coeff_keys, (x0, y0, z0, r)): - setattr(self, key, val) - - def _neg_bounds(self): - """Return the lower and upper bounds of the negative half space""" - return (np.array([self.x0 - self.r, self.y0 - self.r, - self.z0 - self.r]), - np.array([self.x0 + self.r, self.y0 + self.r, - self.z0 + self.r])) - - def _pos_bounds(self): - """Return the lower and upper bounds of the positive half space""" - return (np.array([-np.inf, -np.inf, -np.inf]), - np.array([np.inf, np.inf, np.inf])) + def translate(self, vector, clone=True): + if clone: + surf = self.clone() + else: + surf = self + surf.x0 += vector[0] + surf.y0 += vector[1] + surf.z0 += vector[2] + return surf class Cone(QuadricMixin, Surface): @@ -1991,11 +1748,6 @@ class Cone(QuadricMixin, Surface): self._coefficients['dz'] = dz def _get_base_coeffs(self): - """Return coefficients a, b, c, d, e, f, g, h, j, k representing a - general quadric surface of the form: - :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. - - """ x0, y0, z0, r2 = self.x0, self.y0, self.z0, self.r2 dx, dy, dz = self.dx, self.dy, self.dz cos2 = 1 / (1 + r2) @@ -2014,48 +1766,14 @@ class Cone(QuadricMixin, Surface): return (a, b, c, d, e, f, g, h, j, k) - def _update_from_base_coeffs(self, coeffs): - """Update the current surface from coefficients representing a general - quadric surface of the form: - :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. - - Parameters - ---------- - coeffs : tuple - Tuple of general coefficients (a, b, c, d, e, f, g, h, j, k) - representing the quadric surface - - """ - raise NotImplementedError('_update_from_base_coeffs for {} is ' - 'undefined'.format(self.__cls__.__name__)) - def translate(self, vector, clone=True): - """Translate surface in given direction - - Parameters - ---------- - vector : iterable of float - Direction in which surface should be translated - clone : bool - Whether to return a clone of the Surface or the Surface itself. - Defaults to True - - Returns - ------- - openmc.Surface - Translated surface - - """ - if clone: surf = self.clone() else: surf = self - surf.x0 += vector[0] surf.y0 += vector[1] surf.z0 += vector[2] - return surf @@ -2158,11 +1876,6 @@ class XCone(QuadricMixin, Surface): self._coefficients['r2'] = r2 def _get_base_coeffs(self): - """Return coefficients a, b, c, d, e, f, g, h, j, k representing a - general quadric surface of the form: - :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. - - """ x0, y0, z0, r2 = self.x0, self.y0, self.z0, self.r2 a = -r2 @@ -2173,25 +1886,21 @@ class XCone(QuadricMixin, Surface): return (a, b, c, d, e, f, g, h, j, k) - def _update_from_base_coeffs(self, coeffs): - """Update the current surface from coefficients representing a general - quadric surface of the form: - :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. + def evaluate(self, point): + x = point[0] - self.x0 + y = point[1] - self.y0 + z = point[2] - self.z0 + return y**2 + z**2 - self.r2*x**2 - Parameters - ---------- - coeffs : tuple - Tuple of general coefficients (a, b, c, d, e, f, g, h, j, k) - representing the quadric surface - - """ - a, b, c, d, e, f, g, h, j, k = coeffs - - r2 = -a - x0, y0, z0 = g / (2*r2), -h / 2, -j / 2 - - for key, val in zip(self._coeff_keys, (x0, y0, z0, r2)): - setattr(self, key, val) + def translate(self, vector, clone=True): + if clone: + surf = self.clone() + else: + surf = self + surf.x0 += vector[0] + surf.y0 += vector[1] + surf.z0 += vector[2] + return surf Cone.register(XCone) @@ -2296,11 +2005,6 @@ class YCone(QuadricMixin, Surface): self._coefficients['r2'] = r2 def _get_base_coeffs(self): - """Return coefficients a, b, c, d, e, f, g, h, j, k representing a - general quadric surface of the form: - :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. - - """ x0, y0, z0, r2 = self.x0, self.y0, self.z0, self.r2 b = -r2 @@ -2311,25 +2015,21 @@ class YCone(QuadricMixin, Surface): return (a, b, c, d, e, f, g, h, j, k) - def _update_from_base_coeffs(self, coeffs): - """Update the current surface from coefficients representing a general - quadric surface of the form: - :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. + def evaluate(self, point): + x = point[0] - self.x0 + y = point[1] - self.y0 + z = point[2] - self.z0 + return x**2 + z**2 - self.r2*y**2 - Parameters - ---------- - coeffs : tuple - Tuple of general coefficients (a, b, c, d, e, f, g, h, j, k) - representing the quadric surface - - """ - a, b, c, d, e, f, g, h, j, k = coeffs - - r2 = -b - x0, y0, z0 = -g / 2, h / (2*r2), -j / 2 - - for key, val in zip(self._coeff_keys, (x0, y0, z0, r2)): - setattr(self, key, val) + def translate(self, vector, clone=True): + if clone: + surf = self.clone() + else: + surf = self + surf.x0 += vector[0] + surf.y0 += vector[1] + surf.z0 += vector[2] + return surf Cone.register(YCone) @@ -2434,11 +2134,6 @@ class ZCone(QuadricMixin, Surface): self._coefficients['r2'] = r2 def _get_base_coeffs(self): - """Return coefficients a, b, c, d, e, f, g, h, j, k representing a - general quadric surface of the form: - :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. - - """ x0, y0, z0, r2 = self.x0, self.y0, self.z0, self.r2 c = -r2 @@ -2449,25 +2144,21 @@ class ZCone(QuadricMixin, Surface): return (a, b, c, d, e, f, g, h, j, k) - def _update_from_base_coeffs(self, coeffs): - """Update the current surface from coefficients representing a general - quadric surface of the form: - :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. + def evaluate(self, point): + x = point[0] - self.x0 + y = point[1] - self.y0 + z = point[2] - self.z0 + return x**2 + y**2 - self.r2*z**2 - Parameters - ---------- - coeffs : tuple - Tuple of general coefficients (a, b, c, d, e, f, g, h, j, k) - representing the quadric surface - - """ - a, b, c, d, e, f, g, h, j, k = coeffs - - r2 = -c - x0, y0, z0 = -g / 2, -h / 2, j / (2*r2) - - for key, val in zip(self._coeff_keys, (x0, y0, z0, r2)): - setattr(self, key, val) + def translate(self, vector, clone=True): + if clone: + surf = self.clone() + else: + surf = self + surf.x0 += vector[0] + surf.y0 += vector[1] + surf.z0 += vector[2] + return surf Cone.register(ZCone) @@ -2611,27 +2302,25 @@ class Quadric(QuadricMixin, Surface): self._coefficients['k'] = k def _get_base_coeffs(self): - """Return coefficients a, b, c, d, e, f, g, h, j, k representing a - general quadric surface of the form: - :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. - - """ return tuple(getattr(self, c) for c in self._coeff_keys) - def _update_from_base_coeffs(self, coeffs): - """Update the current surface from coefficients representing a general - quadric surface of the form: - :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. + def translate(self, vector, clone=True): + vector = np.asarray(vector) + A, bvec, cnst = self.get_Abc() - Parameters - ---------- - coeffs : tuple - Tuple of general coefficients (a, b, c, d, e, f, g, h, j, k) - representing the quadric surface + 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) - """ - for key, val in zip(self._coeff_keys, coeffs): - setattr(self, key, val) + if clone: + surf = self.clone() + else: + surf = self + + for key, val in zip(('g', 'h', 'j', 'k'), (g, h, j, k)): + setattr(surf, key, val) + + return surf class Halfspace(Region): diff --git a/tests/regression_tests/cmfd_feed_2g/results_true.dat b/tests/regression_tests/cmfd_feed_2g/results_true.dat index d928b33e8..dc1751574 100644 --- a/tests/regression_tests/cmfd_feed_2g/results_true.dat +++ b/tests/regression_tests/cmfd_feed_2g/results_true.dat @@ -381,7 +381,7 @@ cmfd balance 2.68486E-04 4.84991E-04 1.08402E-03 -1.09177E-03 +1.09178E-03 5.45977E-04 4.45554E-04 4.01147E-04 diff --git a/tests/regression_tests/triso/results_true.dat b/tests/regression_tests/triso/results_true.dat index c8e683234..0c5186e45 100644 --- a/tests/regression_tests/triso/results_true.dat +++ b/tests/regression_tests/triso/results_true.dat @@ -1,2 +1,2 @@ k-combined: -1.701412E+00 3.180881E-02 +1.701412E+00 3.180877E-02 From bed26993ab1bb0e1225370e4bbcc8bfac82755d7 Mon Sep 17 00:00:00 2001 From: Ethan Peterson Date: Thu, 13 Feb 2020 16:17:30 -0500 Subject: [PATCH 098/166] fixed pytest results after ci failure --- tests/regression_tests/cmfd_feed_2g/results_true.dat | 2 +- tests/regression_tests/triso/results_true.dat | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/regression_tests/cmfd_feed_2g/results_true.dat b/tests/regression_tests/cmfd_feed_2g/results_true.dat index dc1751574..d928b33e8 100644 --- a/tests/regression_tests/cmfd_feed_2g/results_true.dat +++ b/tests/regression_tests/cmfd_feed_2g/results_true.dat @@ -381,7 +381,7 @@ cmfd balance 2.68486E-04 4.84991E-04 1.08402E-03 -1.09178E-03 +1.09177E-03 5.45977E-04 4.45554E-04 4.01147E-04 diff --git a/tests/regression_tests/triso/results_true.dat b/tests/regression_tests/triso/results_true.dat index 0c5186e45..c8e683234 100644 --- a/tests/regression_tests/triso/results_true.dat +++ b/tests/regression_tests/triso/results_true.dat @@ -1,2 +1,2 @@ k-combined: -1.701412E+00 3.180877E-02 +1.701412E+00 3.180881E-02 From 38640840c1107bfd9fcdf94b6541cd6e4d533643 Mon Sep 17 00:00:00 2001 From: Ethan Peterson Date: Fri, 14 Feb 2020 09:16:14 -0500 Subject: [PATCH 099/166] applied some suggestions from code review --- openmc/surface.py | 201 ++++++++++++++++++++++------------------------ 1 file changed, 96 insertions(+), 105 deletions(-) diff --git a/openmc/surface.py b/openmc/surface.py index 064ac7242..0337c7297 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -190,14 +190,17 @@ class Surface(IDManagerMixin, metaclass=ABCMeta): Parameters ---------- coeffs : tuple, optional - Tuple of surface coefficients to normalize. If none are supplied, - the coefficients from the current surface will be used. + Tuple of surface coefficients to normalize. Defaults to None. If no + coefficients are supplied then the coefficients will be taken from + the current Surface. Returns ------- tuple of normalized coefficients """ + if coeffs is None: + coeffs = self._get_base_coeffs() coeffs = np.asarray(coeffs) nonzeros = ~np.isclose(coeffs, 0., rtol=0., atol=self._atol) norm_factor = coeffs[nonzeros][0] @@ -213,8 +216,8 @@ class Surface(IDManagerMixin, metaclass=ABCMeta): surface """ - coeffs1 = normalize(self._get_base_coeffs()) - coeffs2 = normalize(other._get_base_coeffs()) + coeffs1 = self.normalize(self._get_base_coeffs()) + coeffs2 = self.normalize(other._get_base_coeffs()) return np.all(np.isclose(coeffs1, coeffs2, rtol=0., atol=self._atol)) @@ -351,16 +354,16 @@ class PlaneMixin(metaclass=ABCMeta): return a*x + b*y + c*z - d @abstractmethod - def translate(self, vector, clone=True): + def translate(self, vector, inplace=False): """Translate surface in given direction Parameters ---------- vector : iterable of float Direction in which surface should be translated - clone : boolean + inplace : boolean Whether or not to return a new instance of a Plane or to modify the - coefficients of this plane. Defaults to True + coefficients of this plane. Defaults to False Returns ------- @@ -447,8 +450,7 @@ class Plane(PlaneMixin, Surface): oldkwargs = deepcopy(kwargs) # work around for accepting Surface kwargs as positional parameters # until they are deprecated - argsdict = {k: v for k, v in zip(('boundary_type', 'name', - 'surface_id'), args)} + argsdict = dict(zip(('boundary_type', 'name', 'surface_id'), args)) for k, v in argsdict.items(): warn(_WARNING_KWARGS.format(type(self).__name__, k), FutureWarning) @@ -507,14 +509,14 @@ class Plane(PlaneMixin, Surface): def _get_base_coeffs(self): return (self.a, self.b, self.c, self.d) - def translate(self, vector, clone=True): + def translate(self, vector, inplace=False): vx, vy, vz = vector a, b, c, d = self._get_base_coeffs() d = d + a*vx + b*vy + c*vz - if clone: - surf = self.clone() - else: + if inplace: surf = self + else: + surf = self.clone() surf.d = d return surf @@ -628,11 +630,11 @@ class XPlane(PlaneMixin, Surface): def evaluate(self, point): return point[0] - self.x0 - def translate(self, vector, clone=True): - if clone: - surf = self.clone() - else: + def translate(self, vector, inplace=False): + if inplace: surf = self + else: + surf = self.clone() surf.x0 += vector[0] return surf @@ -717,11 +719,11 @@ class YPlane(PlaneMixin, Surface): def evaluate(self, point): return point[1] - self.y0 - def translate(self, vector, clone=True): - if clone: - surf = self.clone() - else: + def translate(self, vector, inplace=False): + if inplace: surf = self + else: + surf = self.clone() surf.y0 += vector[1] return surf @@ -806,11 +808,11 @@ class ZPlane(PlaneMixin, Surface): def evaluate(self, point): return point[2] - self.z0 - def translate(self, vector, clone=True): - if clone: - surf = self.clone() - else: + def translate(self, vector, inplace=False): + if inplace: surf = self + else: + surf = self.clone() surf.z0 += vector[2] return surf @@ -895,16 +897,16 @@ class QuadricMixin(metaclass=ABCMeta): return np.matmul(x.T, np.matmul(A, x)) + np.matmul(b.T, x) + c @abstractmethod - def translate(self, vector, clone=True): + def translate(self, vector, inplace=False): """Translate surface in given direction Parameters ---------- vector : iterable of float Direction in which surface should be translated - clone : bool + inplace : boolean Whether to return a clone of the Surface or the Surface itself. - Defaults to True + Defaults to False Returns ------- @@ -1055,7 +1057,7 @@ class Cylinder(QuadricMixin, Surface): def _get_base_coeffs(self): x0, y0, z0, r = self.x0, self.y0, self.z0, self.r dx, dy, dz = self.dx, self.dy, self.dz - dx2, dy2, dz2 = dx**2, dy**2, dz**2 + dx2, dy2, dz2 = dx*dx, dy*dy, dz*dz a = dy2 + dz2 b = dx2 + dz2 @@ -1066,16 +1068,16 @@ class Cylinder(QuadricMixin, Surface): g = 2*(dx*(z0*dz + y0*dy) - x0*(dy2 + dz2)) h = 2*(dy*(z0*dz + x0*dx) - y0*(dx2 + dz2)) j = 2*(dz*(y0*dy + x0*dx) - z0*(dx2 + dy2)) - k = x0**2*(dy2 + dz2) + y0**2*(dx2 + dz2) + z0**2*(dx2 + dy2) \ - + e*y0*z0 + f*x0*z0 + d*x0*y0 - r**2*(dx2 + dy2 + dz2) + k = x0*x0*(dy2 + dz2) + y0*y0*(dx2 + dz2) + z0*z0*(dx2 + dy2) \ + + e*y0*z0 + f*x0*z0 + d*x0*y0 - r*r*(dx2 + dy2 + dz2) return (a, b, c, d, e, f, g, h, j, k) - def translate(self, vector, clone=True): - if clone: - surf = self.clone() - else: + def translate(self, vector, inplace=False): + if inplace: surf = self + else: + surf = self.clone() surf.x0 += vector[0] surf.y0 += vector[1] @@ -1203,7 +1205,7 @@ class XCylinder(QuadricMixin, Surface): a = d = e = f = g = 0. b = c = 1. - h, j, k = -2*y0, -2*z0, y0**2 + z0**2 - r**2 + h, j, k = -2*y0, -2*z0, y0*y0 + z0*z0 - r*r return (a, b, c, d, e, f, g, h, j, k) @@ -1218,13 +1220,13 @@ class XCylinder(QuadricMixin, Surface): def evaluate(self, point): y = point[1] - self.y0 z = point[2] - self.z0 - return y**2 + z**2 - self.r**2 + return y*y + z*z - self.r**2 - def translate(self, vector, clone=True): - if clone: - surf = self.clone() - else: + def translate(self, vector, inplace=False): + if inplace: surf = self + else: + surf = self.clone() surf.y0 += vector[1] surf.z0 += vector[2] return surf @@ -1324,7 +1326,7 @@ class YCylinder(QuadricMixin, Surface): b = d = e = f = h = 0. a = c = 1. - g, j, k = -2*x0, -2*z0, x0**2 + z0**2 - r**2 + g, j, k = -2*x0, -2*z0, x0*x0 + z0*z0 - r*r return (a, b, c, d, e, f, g, h, j, k) @@ -1339,13 +1341,13 @@ class YCylinder(QuadricMixin, Surface): def evaluate(self, point): x = point[0] - self.x0 z = point[2] - self.z0 - return x**2 + z**2 - self.r**2 + return x*x + z*z - self.r**2 - def translate(self, vector, clone=True): - if clone: - surf = self.clone() - else: + def translate(self, vector, inplace=False): + if inplace: surf = self + else: + surf = self.clone() surf.x0 += vector[0] surf.z0 += vector[2] return surf @@ -1445,7 +1447,7 @@ class ZCylinder(QuadricMixin, Surface): c = d = e = f = j = 0. a = b = 1. - g, h, k = -2*x0, -2*y0, x0**2 + y0**2 - r**2 + g, h, k = -2*x0, -2*y0, x0*x0 + y0*y0 - r*r return (a, b, c, d, e, f, g, h, j, k) @@ -1460,13 +1462,13 @@ class ZCylinder(QuadricMixin, Surface): def evaluate(self, point): x = point[0] - self.x0 y = point[1] - self.y0 - return x**2 + y**2 - self.r**2 + return x*x + y*y - self.r**2 - def translate(self, vector, clone=True): - if clone: - surf = self.clone() - else: + def translate(self, vector, inplace=False): + if inplace: surf = self + else: + surf = self.clone() surf.x0 += vector[0] surf.y0 += vector[1] return surf @@ -1577,7 +1579,7 @@ class Sphere(QuadricMixin, Surface): a = b = c = 1. d = e = f = 0. g, h, j = -2*x0, -2*y0, -2*z0 - k = x0**2 + y0**2 + z0**2 - r**2 + k = x0*x0 + y0*y0 + z0*z0 - r*r return (a, b, c, d, e, f, g, h, j, k) @@ -1595,13 +1597,13 @@ class Sphere(QuadricMixin, Surface): x = point[0] - self.x0 y = point[1] - self.y0 z = point[2] - self.z0 - return x**2 + y**2 + z**2 - self.r**2 + return x*x + y*y + z*z - self.r**2 - def translate(self, vector, clone=True): - if clone: - surf = self.clone() - else: + def translate(self, vector, inplace=False): + if inplace: surf = self + else: + surf = self.clone() surf.x0 += vector[0] surf.y0 += vector[1] surf.z0 += vector[2] @@ -1751,26 +1753,26 @@ class Cone(QuadricMixin, Surface): x0, y0, z0, r2 = self.x0, self.y0, self.z0, self.r2 dx, dy, dz = self.dx, self.dy, self.dz cos2 = 1 / (1 + r2) - c1 = (dx**2 + dy**2 + dz**2)*cos2 + c1 = (dx*dx + dy*dy + dz*dz)*cos2 - a = dx**2 - c1 - b = dy**2 - c1 - c = dz**2 - c1 + a = dx*dx - c1 + b = dy*dy - c1 + c = dz*dz - c1 d = 2*dx*dy e = 2*dy*dz f = 2*dx*dz - g = -2*(dx**2*x0 + dx*dy*y0 + dx*dz*z0 - c1) - h = -2*(dy**2*y0 + dx*dy*x0 + dy*dz*z0 - c1) - j = -2*(dz**2*y0 + dx*dz*x0 + dy*dz*y0 - c1) - k = (dx*x0 + dy*y0 + dz*z0)**2 - c1*(x0**2 + y0**2 + z0**2) + g = -2*(dx*dx*x0 + dx*dy*y0 + dx*dz*z0 - c1) + h = -2*(dy*dy*y0 + dx*dy*x0 + dy*dz*z0 - c1) + j = -2*(dz*dz*y0 + dx*dz*x0 + dy*dz*y0 - c1) + k = (dx*x0 + dy*y0 + dz*z0)**2 - c1*(x0*x0 + y0*y0 + z0*z0) return (a, b, c, d, e, f, g, h, j, k) - def translate(self, vector, clone=True): - if clone: - surf = self.clone() - else: + def translate(self, vector, inplace=False): + if inplace: surf = self + else: + surf = self.clone() surf.x0 += vector[0] surf.y0 += vector[1] surf.z0 += vector[2] @@ -1882,7 +1884,7 @@ class XCone(QuadricMixin, Surface): b = c = 1. d = e = f = 0. g, h, j = 2*x0*r2, -2*y0, -2*z0 - k = y0**2 + z0**2 - r2*x0**2 + k = y0*y0 + z0*z0 - r2*x0*x0 return (a, b, c, d, e, f, g, h, j, k) @@ -1890,13 +1892,13 @@ class XCone(QuadricMixin, Surface): x = point[0] - self.x0 y = point[1] - self.y0 z = point[2] - self.z0 - return y**2 + z**2 - self.r2*x**2 + return y*y + z*z - self.r2*x*x - def translate(self, vector, clone=True): - if clone: - surf = self.clone() - else: + def translate(self, vector, inplace=False): + if inplace: surf = self + else: + surf = self.clone() surf.x0 += vector[0] surf.y0 += vector[1] surf.z0 += vector[2] @@ -2011,7 +2013,7 @@ class YCone(QuadricMixin, Surface): a = c = 1. d = e = f = 0. g, h, j = -2*x0, 2*y0*r2, -2*z0 - k = x0**2 + z0**2 - r2*y0**2 + k = x0*x0 + z0*z0 - r2*y0*y0 return (a, b, c, d, e, f, g, h, j, k) @@ -2019,13 +2021,13 @@ class YCone(QuadricMixin, Surface): x = point[0] - self.x0 y = point[1] - self.y0 z = point[2] - self.z0 - return x**2 + z**2 - self.r2*y**2 + return x*x + z*z - self.r2*y*y - def translate(self, vector, clone=True): - if clone: - surf = self.clone() - else: + def translate(self, vector, inplace=False): + if inplace: surf = self + else: + surf = self.clone() surf.x0 += vector[0] surf.y0 += vector[1] surf.z0 += vector[2] @@ -2140,7 +2142,7 @@ class ZCone(QuadricMixin, Surface): a = b = 1. d = e = f = 0. g, h, j = -2*x0, -2*y0, 2*z0*r2 - k = x0**2 + y0**2 - r2*z0**2 + k = x0*x0 + y0*y0 - r2*z0*z0 return (a, b, c, d, e, f, g, h, j, k) @@ -2148,13 +2150,13 @@ class ZCone(QuadricMixin, Surface): x = point[0] - self.x0 y = point[1] - self.y0 z = point[2] - self.z0 - return x**2 + y**2 - self.r2*z**2 + return x*x + y*y - self.r2*z*z - def translate(self, vector, clone=True): - if clone: - surf = self.clone() - else: + def translate(self, vector, inplace=False): + if inplace: surf = self + else: + surf = self.clone() surf.x0 += vector[0] surf.y0 += vector[1] surf.z0 += vector[2] @@ -2304,7 +2306,7 @@ class Quadric(QuadricMixin, Surface): def _get_base_coeffs(self): return tuple(getattr(self, c) for c in self._coeff_keys) - def translate(self, vector, clone=True): + def translate(self, vector, inplace=False): vector = np.asarray(vector) A, bvec, cnst = self.get_Abc() @@ -2312,10 +2314,10 @@ class Quadric(QuadricMixin, Surface): k = cnst + np.matmul(vector.T, np.matmul(A, vector)) \ - np.matmul(bvec.T, vector) - if clone: - surf = self.clone() - else: + if inplace: surf = self + else: + surf = self.clone() for key, val in zip(('g', 'h', 'j', 'k'), (g, h, j, k)): setattr(surf, key, val) @@ -2511,17 +2513,6 @@ class Halfspace(Region): return type(self)(memo[key], self.side) -def _get_subclasses(cls): - """Recursively find all subclasses of this class""" - return set(cls.__subclasses__()).union([s for c in cls.__subclasses__() - for s in _get_subclasses(c)]) - - -def _get_subclass_map(cls): - """Generate mapping of class _type attributes to classes""" - return {c._type: c for c in _get_subclasses(cls)} - - -_SURFACE_CLASSES = _get_subclass_map(Surface) +_SURFACE_CLASSES = {cls._type: cls for cls in Surface.__subclasses__()} From 33c46c78c87c6b1b8400d5bab5a44b1bf6164048 Mon Sep 17 00:00:00 2001 From: Ethan Peterson Date: Fri, 14 Feb 2020 09:55:57 -0500 Subject: [PATCH 100/166] refactored translate method back out to mixins --- openmc/surface.py | 181 +++++++++------------------------------------- 1 file changed, 35 insertions(+), 146 deletions(-) diff --git a/openmc/surface.py b/openmc/surface.py index 0337c7297..d45c8387f 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -353,7 +353,6 @@ class PlaneMixin(metaclass=ABCMeta): a, b, c, d = self._get_base_coeffs() return a*x + b*y + c*z - d - @abstractmethod def translate(self, vector, inplace=False): """Translate surface in given direction @@ -371,7 +370,18 @@ class PlaneMixin(metaclass=ABCMeta): Translated surface """ - pass + vx, vy, vz = vector + a, b, c, d = self._get_base_coeffs() + d = d + a*vx + b*vy + c*vz + + if inplace: + surf = self + else: + surf = self.clone() + + setattr(surf, self._coeff_keys[-1], d) + + return surf def to_xml_element(self): """Return XML representation of the surface @@ -509,17 +519,6 @@ class Plane(PlaneMixin, Surface): def _get_base_coeffs(self): return (self.a, self.b, self.c, self.d) - def translate(self, vector, inplace=False): - vx, vy, vz = vector - a, b, c, d = self._get_base_coeffs() - d = d + a*vx + b*vy + c*vz - if inplace: - surf = self - else: - surf = self.clone() - surf.d = d - return surf - @classmethod def from_points(cls, p1, p2, p3, **kwargs): """Return a plane given three points that pass through it. @@ -630,14 +629,6 @@ class XPlane(PlaneMixin, Surface): def evaluate(self, point): return point[0] - self.x0 - def translate(self, vector, inplace=False): - if inplace: - surf = self - else: - surf = self.clone() - surf.x0 += vector[0] - return surf - Plane.register(XPlane) @@ -719,14 +710,6 @@ class YPlane(PlaneMixin, Surface): def evaluate(self, point): return point[1] - self.y0 - def translate(self, vector, inplace=False): - if inplace: - surf = self - else: - surf = self.clone() - surf.y0 += vector[1] - return surf - Plane.register(YPlane) @@ -808,14 +791,6 @@ class ZPlane(PlaneMixin, Surface): def evaluate(self, point): return point[2] - self.z0 - def translate(self, vector, inplace=False): - if inplace: - surf = self - else: - surf = self.clone() - surf.z0 += vector[2] - return surf - Plane.register(ZPlane) @@ -896,7 +871,6 @@ class QuadricMixin(metaclass=ABCMeta): A, b, c = self.get_Abc() return np.matmul(x.T, np.matmul(A, x)) + np.matmul(b.T, x) + c - @abstractmethod def translate(self, vector, inplace=False): """Translate surface in given direction @@ -914,7 +888,29 @@ class QuadricMixin(metaclass=ABCMeta): Translated surface """ - pass + vector = np.asarray(vector) + + if inplace: + surf = self + else: + surf = self.clone() + + if set(('x0', 'y0', 'z0')).intersection(set(surf._coeff_keys)): + for vi, xi in zip(vector, ('x0', 'y0', 'z0')): + val = getattr(surf, xi, None) + if val is not None: + setattr(surf, xi, val + vi) + else: + 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) + + for key, val in zip(('g', 'h', 'j', 'k'), (g, h, j, k)): + setattr(surf, key, val) + + return surf class Cylinder(QuadricMixin, Surface): @@ -1073,18 +1069,6 @@ class Cylinder(QuadricMixin, Surface): return (a, b, c, d, e, f, g, h, j, k) - def translate(self, vector, inplace=False): - if inplace: - surf = self - else: - surf = self.clone() - - surf.x0 += vector[0] - surf.y0 += vector[1] - surf.z0 += vector[2] - - return surf - @classmethod def from_points(cls, p1, p2, r=1., **kwargs): """Return a cylinder given points that define the axis and a radius. @@ -1222,15 +1206,6 @@ class XCylinder(QuadricMixin, Surface): z = point[2] - self.z0 return y*y + z*z - self.r**2 - def translate(self, vector, inplace=False): - if inplace: - surf = self - else: - surf = self.clone() - surf.y0 += vector[1] - surf.z0 += vector[2] - return surf - Cylinder.register(XCylinder) @@ -1343,15 +1318,6 @@ class YCylinder(QuadricMixin, Surface): z = point[2] - self.z0 return x*x + z*z - self.r**2 - def translate(self, vector, inplace=False): - if inplace: - surf = self - else: - surf = self.clone() - surf.x0 += vector[0] - surf.z0 += vector[2] - return surf - Cylinder.register(YCylinder) @@ -1464,15 +1430,6 @@ class ZCylinder(QuadricMixin, Surface): y = point[1] - self.y0 return x*x + y*y - self.r**2 - def translate(self, vector, inplace=False): - if inplace: - surf = self - else: - surf = self.clone() - surf.x0 += vector[0] - surf.y0 += vector[1] - return surf - Cylinder.register(ZCylinder) @@ -1599,16 +1556,6 @@ class Sphere(QuadricMixin, Surface): z = point[2] - self.z0 return x*x + y*y + z*z - self.r**2 - def translate(self, vector, inplace=False): - if inplace: - surf = self - else: - surf = self.clone() - surf.x0 += vector[0] - surf.y0 += vector[1] - surf.z0 += vector[2] - return surf - class Cone(QuadricMixin, Surface): """A conical surface parallel to the x-, y-, or z-axis. @@ -1768,16 +1715,6 @@ class Cone(QuadricMixin, Surface): return (a, b, c, d, e, f, g, h, j, k) - def translate(self, vector, inplace=False): - if inplace: - surf = self - else: - surf = self.clone() - surf.x0 += vector[0] - surf.y0 += vector[1] - surf.z0 += vector[2] - return surf - class XCone(QuadricMixin, Surface): """A cone parallel to the x-axis of the form :math:`(y - y_0)^2 + (z - z_0)^2 = @@ -1894,16 +1831,6 @@ class XCone(QuadricMixin, Surface): z = point[2] - self.z0 return y*y + z*z - self.r2*x*x - def translate(self, vector, inplace=False): - if inplace: - surf = self - else: - surf = self.clone() - surf.x0 += vector[0] - surf.y0 += vector[1] - surf.z0 += vector[2] - return surf - Cone.register(XCone) @@ -2023,16 +1950,6 @@ class YCone(QuadricMixin, Surface): z = point[2] - self.z0 return x*x + z*z - self.r2*y*y - def translate(self, vector, inplace=False): - if inplace: - surf = self - else: - surf = self.clone() - surf.x0 += vector[0] - surf.y0 += vector[1] - surf.z0 += vector[2] - return surf - Cone.register(YCone) @@ -2152,16 +2069,6 @@ class ZCone(QuadricMixin, Surface): z = point[2] - self.z0 return x*x + y*y - self.r2*z*z - def translate(self, vector, inplace=False): - if inplace: - surf = self - else: - surf = self.clone() - surf.x0 += vector[0] - surf.y0 += vector[1] - surf.z0 += vector[2] - return surf - Cone.register(ZCone) @@ -2306,24 +2213,6 @@ class Quadric(QuadricMixin, Surface): def _get_base_coeffs(self): return tuple(getattr(self, c) for c in self._coeff_keys) - def translate(self, vector, inplace=False): - vector = np.asarray(vector) - 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) - - if inplace: - surf = self - else: - surf = self.clone() - - for key, val in zip(('g', 'h', 'j', 'k'), (g, h, j, k)): - setattr(surf, key, val) - - return surf - class Halfspace(Region): """A positive or negative half-space region. From 35fb12b3619b8d2614ac007c59c1f6a1584152db Mon Sep 17 00:00:00 2001 From: Ethan Peterson Date: Fri, 14 Feb 2020 11:37:26 -0500 Subject: [PATCH 101/166] implemented Plane.__new__ for returning simplified versions --- openmc/surface.py | 95 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 73 insertions(+), 22 deletions(-) diff --git a/openmc/surface.py b/openmc/surface.py index d45c8387f..2e75945d3 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -379,7 +379,7 @@ class PlaneMixin(metaclass=ABCMeta): else: surf = self.clone() - setattr(surf, self._coeff_keys[-1], d) + setattr(surf, surf._coeff_keys[-1], d) return surf @@ -410,9 +410,9 @@ class Plane(PlaneMixin, Surface): a : float, optional The 'A' parameter for the plane. Defaults to 1. b : float, optional - The 'B' parameter for the plane. Defaults to 0. + The 'B' parameter for the plane. Defaults to 1. c : float, optional - The 'C' parameter for the plane. Defaults to 0. + The 'C' parameter for the plane. Defaults to 1. d : float, optional The 'D' parameter for the plane. Defaults to 0. boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}, optional @@ -455,30 +455,82 @@ class Plane(PlaneMixin, Surface): _type = 'plane' _coeff_keys = ('a', 'b', 'c', 'd') - def __init__(self, a=1., b=0., c=0., d=0., *args, **kwargs): - # work around until capital letter kwargs are deprecated - oldkwargs = deepcopy(kwargs) - # work around for accepting Surface kwargs as positional parameters - # until they are deprecated - argsdict = dict(zip(('boundary_type', 'name', 'surface_id'), args)) - for k, v in argsdict.items(): - warn(_WARNING_KWARGS.format(type(self).__name__, k), FutureWarning) - - kwargs.update(argsdict) - - for k in 'ABCD': - kwargs.pop(k, None) + def __new__(cls, *args, **kwargs): + # Intercept Plane creation to return a simpler form if warranted + new_cls, kwargs = cls._process_args(*args, **kwargs) + obj = super().__new__(new_cls) + if new_cls is not cls: + obj.__init__(**kwargs) + return obj + def __init__(self, a=1., b=1., c=1., d=0., *args, **kwargs): super().__init__(**kwargs) for key, val in zip(self._coeff_keys, (a, b, c, d)): setattr(self, key, val) - for k, v in oldkwargs.items(): - if k in 'ABCD': - warn(_WARNING_UPPER.format(type(self).__name__, k.lower(), k), + @classmethod + def _process_args(cls, *args, **kwargs): + """ Process arguments to Plane class to determine which type of Plane + should be instantiated. + + Returns + ------- + tuple : tuple of type and dict + Returns a tuple where the first element is the class (XPlane, + YPlane, ZPlane, or Plane) that should be instantiated. The second + element is the dictionary of keyword arguments that should be + passed to that object's constructor. + + """ + # *args should ultimately be limited to a, b, c, d as specified in + # __init__, but to preserve the API it is allowed to accept Surface + # parameters for now, but will raise warnings if this is done. + argtup = ('a', 'b', 'c', 'd', 'boundary_type', 'name', 'surface_id') + kwargs.update(dict(zip(argtup, args))) + + # Warn if capital letter arguments are passed + for k in 'ABCD': + val = kwargs.pop(k, None) + if val is not None: + warn(_WARNING_UPPER.format(cls.__name__, k.lower(), k), FutureWarning) - setattr(self, k.lower(), v) + kwargs[k.lower()] = val + + # Warn if Surface parameters are passed by position, not by keyword + for k in ('boundary_type', 'name', 'surface_id'): + val = kwargs.get(k, None) + if val is not None: + warn(_WARNING_KWARGS.format(cls.__name__, k), + FutureWarning) + + # Determine if a simpler version of a Plane should be returned and + # modify kwargs accordingly + a = kwargs.get('a', 1.) + b = kwargs.get('b', 1.) + c = kwargs.get('c', 1.) + + # If two of a, b, or c are zero, a simpler version should be returned + if np.sum(np.isclose((a, b, c), 0., rtol=0., atol=cls._atol)) >= 2: + a = kwargs.pop('a', 1.) + b = kwargs.pop('b', 1.) + c = kwargs.pop('c', 1.) + d = kwargs.pop('d', 0.) + # Return XPlane class and kwargs + if ~np.isclose(a, 0.0, rtol=0., atol=cls._atol): + kwargs['x0'] = d/a + return XPlane, kwargs + # Return YPlane class and kwargs + elif ~np.isclose(b, 0.0, rtol=0., atol=cls._atol): + kwargs['y0'] = d/b + return YPlane, kwargs + # Return ZPlane class and kwargs + elif ~np.isclose(c, 0.0, rtol=0., atol=cls._atol): + kwargs['z0'] = d/c + return ZPlane, kwargs + else: + # Return Plane class and kwargs + return cls, kwargs @property def a(self): @@ -597,8 +649,7 @@ class XPlane(PlaneMixin, Surface): def __init__(self, x0=0., *args, **kwargs): # work around for accepting Surface kwargs as positional parameters # until they are deprecated - argsdict = {k: v for k, v in zip(('boundary_type', 'name', - 'surface_id'), args)} + argsdict = dict(zip(('boundary_type', 'name', 'surface_id'), args)) for k, v in argsdict.items(): warn(_WARNING_KWARGS.format(type(self).__name__, k), FutureWarning) kwargs.update(argsdict) From ae696503b6af3d09505556965e38e86881fa5d9b Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 17 Feb 2020 07:52:52 -0600 Subject: [PATCH 102/166] Avoid copysign in instances where an int return value is needed --- src/cell.cpp | 6 +++--- src/output.cpp | 2 +- src/particle.cpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/cell.cpp b/src/cell.cpp index cd6199b75..1d175f05d 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -377,7 +377,7 @@ CSGCell::CSGCell(pugi::xml_node cell_node) throw std::runtime_error{"Invalid surface ID " + std::to_string(abs(r)) + " specified in region for cell " + std::to_string(id_) + "."}; } - r = copysign(it->second + 1, r); + r = (r > 0) ? it->second + 1 : -(it->second + 1); } } @@ -536,8 +536,8 @@ CSGCell::to_hdf5(hid_t cell_group) const region_spec << " |"; } else { // Note the off-by-one indexing - region_spec << " " - << copysign(model::surfaces[abs(token)-1]->id_, token); + auto surf_id = model::surfaces[abs(token)-1]->id_; + region_spec << " " << ((token > 0) ? surf_id : -surf_id); } } write_string(group, "region", region_spec.str(), false); diff --git a/src/output.cpp b/src/output.cpp index d98cfd4a1..18b94be9b 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -192,7 +192,7 @@ extern "C" void print_particle(Particle* p) // Display miscellaneous info. if (p->surface_ != 0) { const Surface& surf {*model::surfaces[std::abs(p->surface_)-1]}; - fmt::print(" Surface = {}\n", std::copysign(surf.id_, p->surface_)); + fmt::print(" Surface = {}\n", (p->surface_ > 0) ? surf.id_ : -surf.id_); } fmt::print(" Weight = {}\n", p->wgt_); if (settings::run_CE) { diff --git a/src/particle.cpp b/src/particle.cpp index d2d8b2e1b..ea0d03cd0 100644 --- a/src/particle.cpp +++ b/src/particle.cpp @@ -1,7 +1,7 @@ #include "openmc/particle.h" #include // copy, min -#include // log, abs, copysign +#include // log, abs #include @@ -539,7 +539,7 @@ Particle::cross_surface() // TODO: off-by-one surface_ = rotational ? surf_p->i_periodic_ + 1 : - std::copysign(surf_p->i_periodic_ + 1, surface_); + ((surface_ > 0) ? surf_p->i_periodic_ + 1 : -(surf_p->i_periodic_ + 1)); // Figure out what cell particle is in now n_coord_ = 1; From 39672e79b0fcf7e7cbbd63d7a9dad14f7251b459 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Thu, 13 Jun 2019 09:15:21 +0100 Subject: [PATCH 103/166] 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 2b4203646..494cfd1a2 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 a177995ea..0a73260fd 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 7808a9d0f..614dd8f5b 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 b15a15d23..4666783d4 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 104/166] 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 4666783d4..b856a8d2a 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 105/166] 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 000000000..b30884f8c --- /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 000000000..606c676df --- /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 000000000..f8d497459 --- /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 000000000..ef2784ea3 --- /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 000000000..7f6f29926 --- /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 106/166] 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 88c2f8611..e0fd2b6c4 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 107/166] 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 e0fd2b6c4..f8ea8ea93 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 108/166] 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 b856a8d2a..089dbddef 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 109/166] 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 089dbddef..b7d1e2499 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 110/166] 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 f8ea8ea93..e0fd2b6c4 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 111/166] 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 e0fd2b6c4..f8ea8ea93 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 112/166] 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 ec6b500b4..6cc9fceaf 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 113/166] 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 b7d1e2499..51d86fcaa 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 114/166] 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 51d86fcaa..b7d1e2499 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 115/166] 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 f8ea8ea93..e0fd2b6c4 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 116/166] 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 e0fd2b6c4..f8ea8ea93 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 117/166] missing space --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6cc9fceaf..605ba0b61 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 118/166] 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 000000000..4b13c7db9 --- /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 000000000..917f3fd4f --- /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 119/166] 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 605ba0b61..18de94121 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 120/166] 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 000000000..23f3d8438 --- /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 000000000..e69de29bb 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 000000000..ac63ca804 --- /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 917f3fd4f..000000000 --- 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 121/166] Stylistic changes --- src/source.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/source.cpp b/src/source.cpp index b7d1e2499..464a39948 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 122/166] 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 f8ea8ea93..339d41723 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 1c70e159d..d4d17a3da 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 123/166] 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 2e2648237..1d35c18aa 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 124/166] 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 464a39948..066e3a52c 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 125/166] 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 18de94121..d61477f19 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 126/166] 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 000000000..e69de29bb From b39cbe404d610b7626f270bb5de0b5e1634c889a Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 16 Oct 2019 21:07:31 +0100 Subject: [PATCH 127/166] 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 d61477f19..ef67da4e5 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 128/166] 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 066e3a52c..b1fd8c71e 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 129/166] 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 b1fd8c71e..465c2aac1 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 130/166] 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 1d35c18aa..fde374b8d 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 131/166] 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 ef67da4e5..0d4c371f0 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 132/166] 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 465c2aac1..f910b384c 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 133/166] 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 ef2784ea3..fb361d1aa 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 134/166] line removal from review --- src/source.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/source.cpp b/src/source.cpp index f910b384c..e7a476746 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 135/166] 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 fde374b8d..f224df265 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 136/166] 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 f224df265..5392efef5 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 137/166] 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 0a73260fd..e980f2a8c 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 e7a476746..2cda76a5c 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 138/166] 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 5392efef5..6f898f911 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 139/166] 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 fb361d1aa..babb2f591 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 140/166] revert previous attempts to fix --- CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d4c371f0..d628cd2a6 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 141/166] 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 6f898f911..28754e716 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 000000000..949817694 --- /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 f8d497459..f935ed685 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 babb2f591..0feb28702 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 e980f2a8c..4db17f241 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 2cda76a5c..96175804c 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 142/166] 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 4b13c7db9..eaf8b74d9 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 2332a679fadbf2142ae21a1a0fbe46fd90b62f48 Mon Sep 17 00:00:00 2001 From: Ethan Peterson Date: Tue, 18 Feb 2020 10:08:22 -0500 Subject: [PATCH 143/166] reverted simplifying Plane constructor and default args --- openmc/surface.py | 98 +++++-------------- .../deplete/example_geometry.py | 1 + 2 files changed, 25 insertions(+), 74 deletions(-) diff --git a/openmc/surface.py b/openmc/surface.py index 2e75945d3..c73ce6fc8 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -203,7 +203,7 @@ class Surface(IDManagerMixin, metaclass=ABCMeta): coeffs = self._get_base_coeffs() coeffs = np.asarray(coeffs) nonzeros = ~np.isclose(coeffs, 0., rtol=0., atol=self._atol) - norm_factor = coeffs[nonzeros][0] + norm_factor = np.abs(coeffs[nonzeros][0]) return tuple([c/norm_factor for c in coeffs]) def is_equal(self, other): @@ -410,9 +410,9 @@ class Plane(PlaneMixin, Surface): a : float, optional The 'A' parameter for the plane. Defaults to 1. b : float, optional - The 'B' parameter for the plane. Defaults to 1. + The 'B' parameter for the plane. Defaults to 0. c : float, optional - The 'C' parameter for the plane. Defaults to 1. + The 'C' parameter for the plane. Defaults to 0. d : float, optional The 'D' parameter for the plane. Defaults to 0. boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}, optional @@ -455,82 +455,34 @@ class Plane(PlaneMixin, Surface): _type = 'plane' _coeff_keys = ('a', 'b', 'c', 'd') - def __new__(cls, *args, **kwargs): - # Intercept Plane creation to return a simpler form if warranted - new_cls, kwargs = cls._process_args(*args, **kwargs) - obj = super().__new__(new_cls) - if new_cls is not cls: - obj.__init__(**kwargs) - return obj - - def __init__(self, a=1., b=1., c=1., d=0., *args, **kwargs): - super().__init__(**kwargs) - - for key, val in zip(self._coeff_keys, (a, b, c, d)): - setattr(self, key, val) - - @classmethod - def _process_args(cls, *args, **kwargs): - """ Process arguments to Plane class to determine which type of Plane - should be instantiated. - - Returns - ------- - tuple : tuple of type and dict - Returns a tuple where the first element is the class (XPlane, - YPlane, ZPlane, or Plane) that should be instantiated. The second - element is the dictionary of keyword arguments that should be - passed to that object's constructor. - - """ + def __init__(self, a=1., b=0., c=0., d=0., *args, **kwargs): # *args should ultimately be limited to a, b, c, d as specified in # __init__, but to preserve the API it is allowed to accept Surface # parameters for now, but will raise warnings if this is done. argtup = ('a', 'b', 'c', 'd', 'boundary_type', 'name', 'surface_id') kwargs.update(dict(zip(argtup, args))) + # Warn if Surface parameters are passed by position, not by keyword + superkwargs = {} + for k in ('boundary_type', 'name', 'surface_id'): + val = kwargs.get(k, None) + if val is not None: + superkwargs[k] = val + warn(_WARNING_KWARGS.format(type(self), k), + FutureWarning) + + super().__init__(**superkwargs) + + for key, val in zip(self._coeff_keys, (a, b, c, d)): + setattr(self, key, val) + # Warn if capital letter arguments are passed for k in 'ABCD': val = kwargs.pop(k, None) if val is not None: - warn(_WARNING_UPPER.format(cls.__name__, k.lower(), k), + warn(_WARNING_UPPER.format(type(self), k.lower(), k), FutureWarning) - kwargs[k.lower()] = val - - # Warn if Surface parameters are passed by position, not by keyword - for k in ('boundary_type', 'name', 'surface_id'): - val = kwargs.get(k, None) - if val is not None: - warn(_WARNING_KWARGS.format(cls.__name__, k), - FutureWarning) - - # Determine if a simpler version of a Plane should be returned and - # modify kwargs accordingly - a = kwargs.get('a', 1.) - b = kwargs.get('b', 1.) - c = kwargs.get('c', 1.) - - # If two of a, b, or c are zero, a simpler version should be returned - if np.sum(np.isclose((a, b, c), 0., rtol=0., atol=cls._atol)) >= 2: - a = kwargs.pop('a', 1.) - b = kwargs.pop('b', 1.) - c = kwargs.pop('c', 1.) - d = kwargs.pop('d', 0.) - # Return XPlane class and kwargs - if ~np.isclose(a, 0.0, rtol=0., atol=cls._atol): - kwargs['x0'] = d/a - return XPlane, kwargs - # Return YPlane class and kwargs - elif ~np.isclose(b, 0.0, rtol=0., atol=cls._atol): - kwargs['y0'] = d/b - return YPlane, kwargs - # Return ZPlane class and kwargs - elif ~np.isclose(c, 0.0, rtol=0., atol=cls._atol): - kwargs['z0'] = d/c - return ZPlane, kwargs - else: - # Return Plane class and kwargs - return cls, kwargs + setattr(self, k.lower(), val) @property def a(self): @@ -729,9 +681,8 @@ class YPlane(PlaneMixin, Surface): def __init__(self, y0=0., *args, **kwargs): # work around for accepting Surface kwargs as positional parameters # until they are deprecated - argsdict = {k: v for k, v in zip(('boundary_type', 'name', - 'surface_id'), args)} - for k, v in argsdict.items(): + argsdict = dict(zip(('boundary_type', 'name', 'surface_id'), args)) + for k in argsdict.keys(): warn(_WARNING_KWARGS.format(type(self).__name__, k), FutureWarning) kwargs.update(argsdict) @@ -810,9 +761,8 @@ class ZPlane(PlaneMixin, Surface): def __init__(self, z0=0., *args, **kwargs): # work around for accepting Surface kwargs as positional parameters # until they are deprecated - argsdict = {k: v for k, v in zip(('boundary_type', 'name', - 'surface_id'), args)} - for k, v in argsdict.items(): + argsdict = dict(zip(('boundary_type', 'name', 'surface_id'), args)) + for k in argsdict.keys(): warn(_WARNING_KWARGS.format(type(self).__name__, k), FutureWarning) kwargs.update(argsdict) diff --git a/tests/regression_tests/deplete/example_geometry.py b/tests/regression_tests/deplete/example_geometry.py index f79044558..1275e23b2 100644 --- a/tests/regression_tests/deplete/example_geometry.py +++ b/tests/regression_tests/deplete/example_geometry.py @@ -188,6 +188,7 @@ def segment_pin(n_rings, n_wedges, r_fuel, r_gap, r_clad): fuel_wedges = [openmc.Plane(a=math.cos(theta[i]), b=math.sin(theta[i])) for i in range(n_wedges)] + print(fuel_wedges) gap_ring = openmc.ZCylinder(x0=0, y0=0, r=r_gap) clad_ring = openmc.ZCylinder(x0=0, y0=0, r=r_clad) From 03f33943ad46d1a7bfdc17b707f40bb66fd24455 Mon Sep 17 00:00:00 2001 From: Ethan Peterson Date: Tue, 18 Feb 2020 11:54:33 -0500 Subject: [PATCH 144/166] removed debug statement --- tests/regression_tests/deplete/example_geometry.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/regression_tests/deplete/example_geometry.py b/tests/regression_tests/deplete/example_geometry.py index 1275e23b2..f79044558 100644 --- a/tests/regression_tests/deplete/example_geometry.py +++ b/tests/regression_tests/deplete/example_geometry.py @@ -188,7 +188,6 @@ def segment_pin(n_rings, n_wedges, r_fuel, r_gap, r_clad): fuel_wedges = [openmc.Plane(a=math.cos(theta[i]), b=math.sin(theta[i])) for i in range(n_wedges)] - print(fuel_wedges) gap_ring = openmc.ZCylinder(x0=0, y0=0, r=r_gap) clad_ring = openmc.ZCylinder(x0=0, y0=0, r=r_clad) From faf454c3442b1e0961f268cc6de7197759b5196e Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 19 Feb 2020 07:48:21 +0000 Subject: [PATCH 145/166] 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 000000000..3f61b2cd8 --- /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 ac63ca804..1c174b775 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 dd74b2f43f1d2060486de2823ee30058b8971dbd Mon Sep 17 00:00:00 2001 From: Sam Powell-Gill Date: Wed, 19 Feb 2020 09:26:13 +0000 Subject: [PATCH 146/166] Fixed bug which prevents creating photon data with no atomic relax --- openmc/data/photon.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openmc/data/photon.py b/openmc/data/photon.py index 32ed3010a..9cc697b78 100644 --- a/openmc/data/photon.py +++ b/openmc/data/photon.py @@ -784,8 +784,9 @@ class IncidentPhoton(EqualityMixin): sub_group = shell_group.create_group(key) # Write atomic relaxation - if key in self.atomic_relaxation.subshells: - self.atomic_relaxation.to_hdf5(sub_group, key) + if self.atomic_relaxation is not None: + if key in self.atomic_relaxation.subshells: + self.atomic_relaxation.to_hdf5(sub_group, key) else: continue From 6fd08f5aecd835e695b65657598c8a7e7c64e26b Mon Sep 17 00:00:00 2001 From: Sam Powell-Gill Date: Wed, 19 Feb 2020 11:59:35 +0000 Subject: [PATCH 147/166] Unit test for exporting photon data with no atomic relax --- tests/unit_tests/test_data_photon.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/unit_tests/test_data_photon.py b/tests/unit_tests/test_data_photon.py index c767d19e6..02635bcc9 100644 --- a/tests/unit_tests/test_data_photon.py +++ b/tests/unit_tests/test_data_photon.py @@ -144,3 +144,11 @@ def test_export_to_hdf5(tmpdir, element): element2.bremsstrahlung['electron_energy']).all() # Export to hdf5 again element2.export_to_hdf5(filename, 'w') + +def test_photodat_only(tmpdir): + endf_data = os.environ['OPENMC_ENDF_DATA'] + filename = str(tmpdir.join('tmp.h5')) + p_file = 'photoat-{:03}_{}_000.endf'.format(1, 'H') + p_path = os.path.join(endf_data, 'photoat', p_file) + data=openmc.data.IncidentPhoton.from_endf(p_path) + data.export_to_hdf5(filename, 'w') \ No newline at end of file From 9a21eabd55d1353ca8241abbd91578b4ede84502 Mon Sep 17 00:00:00 2001 From: Ethan Peterson Date: Wed, 19 Feb 2020 08:46:50 -0500 Subject: [PATCH 148/166] Apply suggestions from code review Co-Authored-By: Paul Romano --- openmc/surface.py | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/openmc/surface.py b/openmc/surface.py index c73ce6fc8..6d08507d6 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -19,7 +19,7 @@ _WARNING_UPPER = """\ will not accept the capitalized version.\ """ -_WARNING_KWARGS = """ +_WARNING_KWARGS = """\ "{}(...) accepts keyword arguments only for '{}'. Future versions of OpenMC \ will not accept positional parameters for superclass arguments.\ """ @@ -219,7 +219,7 @@ class Surface(IDManagerMixin, metaclass=ABCMeta): coeffs1 = self.normalize(self._get_base_coeffs()) coeffs2 = self.normalize(other._get_base_coeffs()) - return np.all(np.isclose(coeffs1, coeffs2, rtol=0., atol=self._atol)) + return np.allclose(coeffs1, coeffs2, rtol=0., atol=self._atol) @abstractmethod def evaluate(self, point): @@ -599,10 +599,10 @@ class XPlane(PlaneMixin, Surface): _coeff_keys = ('x0',) def __init__(self, x0=0., *args, **kwargs): - # work around for accepting Surface kwargs as positional parameters + # work around for accepting Surface kwargs as positional parameters # until they are deprecated argsdict = dict(zip(('boundary_type', 'name', 'surface_id'), args)) - for k, v in argsdict.items(): + for k in argsdict: warn(_WARNING_KWARGS.format(type(self).__name__, k), FutureWarning) kwargs.update(argsdict) @@ -679,10 +679,10 @@ class YPlane(PlaneMixin, Surface): _coeff_keys = ('y0',) def __init__(self, y0=0., *args, **kwargs): - # work around for accepting Surface kwargs as positional parameters + # work around for accepting Surface kwargs as positional parameters # until they are deprecated argsdict = dict(zip(('boundary_type', 'name', 'surface_id'), args)) - for k in argsdict.keys(): + for k in argsdict: warn(_WARNING_KWARGS.format(type(self).__name__, k), FutureWarning) kwargs.update(argsdict) @@ -759,10 +759,10 @@ class ZPlane(PlaneMixin, Surface): _coeff_keys = ('z0',) def __init__(self, z0=0., *args, **kwargs): - # work around for accepting Surface kwargs as positional parameters + # work around for accepting Surface kwargs as positional parameters # until they are deprecated argsdict = dict(zip(('boundary_type', 'name', 'surface_id'), args)) - for k in argsdict.keys(): + for k in argsdict: warn(_WARNING_KWARGS.format(type(self).__name__, k), FutureWarning) kwargs.update(argsdict) @@ -825,8 +825,8 @@ class QuadricMixin(metaclass=ABCMeta): else: a, b, c, d, e, f, g, h, j, k = coeffs - A = np.asarray([[a, d/2, f/2], [d/2, b, e/2], [f/2, e/2, c]]) - bvec = np.asarray([g, h, j]) + A = np.array([[a, d/2, f/2], [d/2, b, e/2], [f/2, e/2, c]]) + bvec = np.array([g, h, j]) return A, bvec, k @@ -979,7 +979,7 @@ class Cylinder(QuadricMixin, Surface): """ _type = 'cylinder' - _coeff_keys = ('x0', 'y0', 'z0', 'r', 'dx', 'dy','dz') + _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): @@ -2405,4 +2405,3 @@ class Halfspace(Region): _SURFACE_CLASSES = {cls._type: cls for cls in Surface.__subclasses__()} - From 44d2527faf77df9f5dea25b7ae26fd4484e70133 Mon Sep 17 00:00:00 2001 From: Ethan Peterson Date: Wed, 19 Feb 2020 09:56:46 -0500 Subject: [PATCH 149/166] applied more suggestions from code review and added coefficient cache for general cones and cylinders --- openmc/surface.py | 209 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 157 insertions(+), 52 deletions(-) diff --git a/openmc/surface.py b/openmc/surface.py index c73ce6fc8..918be232b 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -187,6 +187,7 @@ class Surface(IDManagerMixin, metaclass=ABCMeta): def normalize(self, coeffs=None): """Normalize coefficients by first nonzero value + Parameters ---------- coeffs : tuple, optional @@ -222,12 +223,47 @@ class Surface(IDManagerMixin, metaclass=ABCMeta): return np.all(np.isclose(coeffs1, coeffs2, rtol=0., atol=self._atol)) @abstractmethod - def evaluate(self, point): - pass + def _get_base_coeffs(self): + """Return polynomial coefficients representing the implicit surface + equation. + + """ @abstractmethod - def translate(self, vector): - pass + def evaluate(self, point): + """Evaluate the surface equation at a given point. + + Parameters + ---------- + point : 3-tuple of float + The Cartesian coordinates, :math:`(x',y',z')`, at which the surface + equation should be evaluated. + + Returns + ------- + float + Evaluation of the surface polynomial at point :math:`(x',y',z')` + + """ + + @abstractmethod + def translate(self, vector, inplace=False): + """Translate surface in given direction + + Parameters + ---------- + vector : iterable of float + Direction in which surface should be translated + inplace : boolean + Whether or not to return a new instance of this Surface or to + modify the coefficients of this Surface. Defaults to False + + Returns + ------- + instance of openmc.Surface + Translated surface + + """ def to_xml_element(self): """Return XML representation of the surface @@ -325,13 +361,11 @@ class PlaneMixin(metaclass=ABCMeta): self._periodic_surface = periodic_surface periodic_surface._periodic_surface = self - @abstractmethod def _get_base_coeffs(self): """Return coefficients a, b, c, d representing a general plane of the form :math:`ax + by + cz = d`. """ - pass def evaluate(self, point): """Evaluate the surface equation at a given point. @@ -374,10 +408,7 @@ class PlaneMixin(metaclass=ABCMeta): a, b, c, d = self._get_base_coeffs() d = d + a*vx + b*vy + c*vz - if inplace: - surf = self - else: - surf = self.clone() + surf = self if inplace else self.clone() setattr(surf, surf._coeff_keys[-1], d) @@ -798,17 +829,13 @@ Plane.register(ZPlane) class QuadricMixin(metaclass=ABCMeta): """A Mixin class implementing common functionality for quadric surfaces""" - def __init__(self, **kwargs): - super().__init__(**kwargs) - @abstractmethod def _get_base_coeffs(self): """Return coefficients a, b, c, d, e, f, g, h, j, k representing a general quadric surface of the form: :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. """ - pass def get_Abc(self, coeffs=None): """Compute matrix, vector, and scalar coefficients for this surface or @@ -891,10 +918,7 @@ class QuadricMixin(metaclass=ABCMeta): """ vector = np.asarray(vector) - if inplace: - surf = self - else: - surf = self.clone() + surf = self if inplace else self.clone() if set(('x0', 'y0', 'z0')).intersection(set(surf._coeff_keys)): for vi, xi in zip(vector, ('x0', 'y0', 'z0')): @@ -916,8 +940,8 @@ class QuadricMixin(metaclass=ABCMeta): class Cylinder(QuadricMixin, Surface): """A cylinder with radius r, centered on the point (x0, y0, z0) with an - axis specified by the line through points (x0, y0, z0) and (x0+u, y0+v, - z0+w) + axis specified by the line through points (x0, y0, z0) and (x0+dx, y0+dy, + z0+dz) Parameters ---------- @@ -982,6 +1006,10 @@ 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) @@ -1020,55 +1048,90 @@ class Cylinder(QuadricMixin, Surface): def x0(self, x0): check_type('x0 coefficient', x0, Real) self._coefficients['x0'] = x0 + self._out_of_date = True @y0.setter def y0(self, y0): check_type('y0 coefficient', y0, Real) self._coefficients['y0'] = y0 + self._out_of_date = True @z0.setter def z0(self, z0): check_type('z0 coefficient', z0, Real) self._coefficients['z0'] = z0 + self._out_of_date = True @r.setter def r(self, r): check_type('r coefficient', r, Real) self._coefficients['r'] = r + self._out_of_date = True @dx.setter def dx(self, dx): check_type('dx coefficient', dx, Real) self._coefficients['dx'] = dx + self._out_of_date = True @dy.setter def dy(self, dy): check_type('dy coefficient', dy, Real) self._coefficients['dy'] = dy + self._out_of_date = True @dz.setter def dz(self, dz): check_type('dz coefficient', dz, Real) self._coefficients['dz'] = dz + self._out_of_date = True + + def _set_base_coeffs(self, coeffs): + """Set quadric coefficients for quick access + + Parameters + ---------- + coeffs : tuple + Tuple of Quadric coefficients (a, b, c, d, e, f, g, h, j, k) + """ + + for k, c in zip(Quadric._coeff_keys, coeffs): + setattr(self, '_' + k, c) + self._out_of_date = False def _get_base_coeffs(self): - x0, y0, z0, r = self.x0, self.y0, self.z0, self.r - dx, dy, dz = self.dx, self.dy, self.dz - dx2, dy2, dz2 = dx*dx, dy*dy, dz*dz + if self._out_of_date: + # 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 + r = self.r - a = dy2 + dz2 - b = dx2 + dz2 - c = dx2 + dy2 - d = -2*dx*dy - e = -2*dy*dz - f = -2*dx*dz - g = 2*(dx*(z0*dz + y0*dy) - x0*(dy2 + dz2)) - h = 2*(dy*(z0*dz + x0*dx) - y0*(dx2 + dz2)) - j = 2*(dz*(y0*dy + x0*dx) - z0*(dx2 + dy2)) - k = x0*x0*(dy2 + dz2) + y0*y0*(dx2 + dz2) + z0*z0*(dx2 + dy2) \ - + e*y0*z0 + f*x0*z0 + d*x0*y0 - r*r*(dx2 + dy2 + dz2) + # 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 - return (a, b, c, d, e, f, g, h, j, k) + # 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. + a = dy*dy + dz*dz + b = dx*dx + dz*dz + c = dx*dx + dy*dy + d = -2*dx*dy + e = -2*dy*dz + f = -2*dx*dz + g = 2*(cy*dz - cz*dy) + h = 2*(cz*dx - cx*dz) + j = 2*(cx*dy - cy*dx) + k = cx*cx + cy*cy + cz*cz - (dx*dx + dy*dy + dz*dz)*r*r + self._set_base_coeffs((a, b, c, d, e, f, g, h, j, k)) + + return tuple(getattr(self, '_' + k) for k in Quadric._coeff_keys) @classmethod def from_points(cls, p1, p2, r=1., **kwargs): @@ -1090,6 +1153,10 @@ 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) @@ -1624,6 +1691,9 @@ 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'), @@ -1666,55 +1736,92 @@ class Cone(QuadricMixin, Surface): def x0(self, x0): check_type('x0 coefficient', x0, Real) self._coefficients['x0'] = x0 + self._out_of_date = True @y0.setter def y0(self, y0): check_type('y0 coefficient', y0, Real) self._coefficients['y0'] = y0 + self._out_of_date = True @z0.setter def z0(self, z0): check_type('z0 coefficient', z0, Real) self._coefficients['z0'] = z0 + self._out_of_date = True @r2.setter def r2(self, r2): check_type('r^2 coefficient', r2, Real) self._coefficients['r2'] = r2 + self._out_of_date = True @dx.setter def dx(self, dx): check_type('dx coefficient', dx, Real) self._coefficients['dx'] = dx + self._out_of_date = True @dy.setter def dy(self, dy): check_type('dy coefficient', dy, Real) self._coefficients['dy'] = dy + self._out_of_date = True @dz.setter def dz(self, dz): check_type('dz coefficient', dz, Real) self._coefficients['dz'] = dz + self._out_of_date = True + + def _set_base_coeffs(self, coeffs): + """Set quadric coefficients for quick access + + Parameters + ---------- + coeffs : tuple + Tuple of Quadric coefficients (a, b, c, d, e, f, g, h, j, k) + """ + + for k, c in zip(Quadric._coeff_keys, coeffs): + setattr(self, '_' + k, c) + self._out_of_date = False def _get_base_coeffs(self): - x0, y0, z0, r2 = self.x0, self.y0, self.z0, self.r2 - dx, dy, dz = self.dx, self.dy, self.dz - cos2 = 1 / (1 + r2) - c1 = (dx*dx + dy*dy + dz*dz)*cos2 + # The equation for a general cone with vertex at point p = (x0, y0, z0) + # and axis specified by the unit vector d = (dx, dy, dz) and opening + # half angle theta can be described by the equation + # + # (d*(r - p))^2 - (r - p)*(r - p)cos^2(theta) = 0 + # + # where * is the dot product and the vector r is the evaulation point + # r = (x, y, z) + # + # The argument r2 for cones is actually tan^2(theta) so that + # cos^2(theta) = 1 / (1 + r2) - a = dx*dx - c1 - b = dy*dy - c1 - c = dz*dz - c1 - d = 2*dx*dy - e = 2*dy*dz - f = 2*dx*dz - g = -2*(dx*dx*x0 + dx*dy*y0 + dx*dz*z0 - c1) - h = -2*(dy*dy*y0 + dx*dy*x0 + dy*dz*z0 - c1) - j = -2*(dz*dz*y0 + dx*dz*x0 + dy*dz*y0 - c1) - k = (dx*x0 + dy*y0 + dz*z0)**2 - c1*(x0*x0 + y0*y0 + z0*z0) + if self._out_of_date: + 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) - return (a, b, c, d, e, f, g, h, j, k) + 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*y0 + dx*dz*x0 + dy*dz*y0 - cos2) + k = (dx*x0 + dy*y0 + dz*z0)**2 - cos2*(x0*x0 + y0*y0 + z0*z0) + self._set_base_coeffs((a, b, c, d, e, f, g, h, j, k)) + + return tuple(getattr(self, '_' + k) for k in Quadric._coeff_keys) class XCone(QuadricMixin, Surface): @@ -2404,5 +2511,3 @@ class Halfspace(Region): _SURFACE_CLASSES = {cls._type: cls for cls in Surface.__subclasses__()} - - From 6c2ef8b7f0590da2fbd5a20c6747d69ac541bb73 Mon Sep 17 00:00:00 2001 From: Ethan Peterson Date: Wed, 19 Feb 2020 14:23:12 -0500 Subject: [PATCH 150/166] removed coefficient caching --- openmc/surface.py | 147 ++++++++++++++-------------------------------- 1 file changed, 45 insertions(+), 102 deletions(-) diff --git a/openmc/surface.py b/openmc/surface.py index 578e902ed..92c828d65 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -361,12 +361,6 @@ class PlaneMixin(metaclass=ABCMeta): self._periodic_surface = periodic_surface periodic_surface._periodic_surface = self - def _get_base_coeffs(self): - """Return coefficients a, b, c, d representing a general plane of the - form :math:`ax + by + cz = d`. - - """ - def evaluate(self, point): """Evaluate the surface equation at a given point. @@ -830,13 +824,6 @@ Plane.register(ZPlane) class QuadricMixin(metaclass=ABCMeta): """A Mixin class implementing common functionality for quadric surfaces""" - def _get_base_coeffs(self): - """Return coefficients a, b, c, d, e, f, g, h, j, k representing a - general quadric surface of the form: - :math:`ax^2 + by^2 + cz^2 + dxy + eyz + fxz+ gx + hy + jz +k = 0`. - - """ - def get_Abc(self, coeffs=None): """Compute matrix, vector, and scalar coefficients for this surface or for a specified set of coefficients. @@ -1048,90 +1035,68 @@ class Cylinder(QuadricMixin, Surface): def x0(self, x0): check_type('x0 coefficient', x0, Real) self._coefficients['x0'] = x0 - self._out_of_date = True @y0.setter def y0(self, y0): check_type('y0 coefficient', y0, Real) self._coefficients['y0'] = y0 - self._out_of_date = True @z0.setter def z0(self, z0): check_type('z0 coefficient', z0, Real) self._coefficients['z0'] = z0 - self._out_of_date = True @r.setter def r(self, r): check_type('r coefficient', r, Real) self._coefficients['r'] = r - self._out_of_date = True @dx.setter def dx(self, dx): check_type('dx coefficient', dx, Real) self._coefficients['dx'] = dx - self._out_of_date = True @dy.setter def dy(self, dy): check_type('dy coefficient', dy, Real) self._coefficients['dy'] = dy - self._out_of_date = True @dz.setter def dz(self, dz): check_type('dz coefficient', dz, Real) self._coefficients['dz'] = dz - self._out_of_date = True - - def _set_base_coeffs(self, coeffs): - """Set quadric coefficients for quick access - - Parameters - ---------- - coeffs : tuple - Tuple of Quadric coefficients (a, b, c, d, e, f, g, h, j, k) - """ - - for k, c in zip(Quadric._coeff_keys, coeffs): - setattr(self, '_' + k, c) - self._out_of_date = False def _get_base_coeffs(self): - if self._out_of_date: - # 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 - r = self.r + # 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 + r = self.r - # 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 + # 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. - a = dy*dy + dz*dz - b = dx*dx + dz*dz - c = dx*dx + dy*dy - d = -2*dx*dy - e = -2*dy*dz - f = -2*dx*dz - g = 2*(cy*dz - cz*dy) - h = 2*(cz*dx - cx*dz) - j = 2*(cx*dy - cy*dx) - k = cx*cx + cy*cy + cz*cz - (dx*dx + dy*dy + dz*dz)*r*r - self._set_base_coeffs((a, b, c, d, e, f, g, h, j, k)) + # 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. + a = dy*dy + dz*dz + b = dx*dx + dz*dz + c = dx*dx + dy*dy + d = -2*dx*dy + e = -2*dy*dz + f = -2*dx*dz + g = 2*(cy*dz - cz*dy) + h = 2*(cz*dx - cx*dz) + j = 2*(cx*dy - cy*dx) + k = cx*cx + cy*cy + cz*cz - (dx*dx + dy*dy + dz*dz)*r*r - return tuple(getattr(self, '_' + k) for k in Quadric._coeff_keys) + return (a, b, c, d, e, f, g, h, j, k) @classmethod def from_points(cls, p1, p2, r=1., **kwargs): @@ -1736,56 +1701,36 @@ class Cone(QuadricMixin, Surface): def x0(self, x0): check_type('x0 coefficient', x0, Real) self._coefficients['x0'] = x0 - self._out_of_date = True @y0.setter def y0(self, y0): check_type('y0 coefficient', y0, Real) self._coefficients['y0'] = y0 - self._out_of_date = True @z0.setter def z0(self, z0): check_type('z0 coefficient', z0, Real) self._coefficients['z0'] = z0 - self._out_of_date = True @r2.setter def r2(self, r2): check_type('r^2 coefficient', r2, Real) self._coefficients['r2'] = r2 - self._out_of_date = True @dx.setter def dx(self, dx): check_type('dx coefficient', dx, Real) self._coefficients['dx'] = dx - self._out_of_date = True @dy.setter def dy(self, dy): check_type('dy coefficient', dy, Real) self._coefficients['dy'] = dy - self._out_of_date = True @dz.setter def dz(self, dz): check_type('dz coefficient', dz, Real) self._coefficients['dz'] = dz - self._out_of_date = True - - def _set_base_coeffs(self, coeffs): - """Set quadric coefficients for quick access - - Parameters - ---------- - coeffs : tuple - Tuple of Quadric coefficients (a, b, c, d, e, f, g, h, j, k) - """ - - for k, c in zip(Quadric._coeff_keys, coeffs): - setattr(self, '_' + k, c) - self._out_of_date = False def _get_base_coeffs(self): # The equation for a general cone with vertex at point p = (x0, y0, z0) @@ -1800,28 +1745,26 @@ class Cone(QuadricMixin, Surface): # The argument r2 for cones is actually tan^2(theta) so that # cos^2(theta) = 1 / (1 + r2) - if self._out_of_date: - 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, 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) - 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*y0 + dx*dz*x0 + dy*dz*y0 - cos2) - k = (dx*x0 + dy*y0 + dz*z0)**2 - cos2*(x0*x0 + y0*y0 + z0*z0) - self._set_base_coeffs((a, b, c, d, e, f, g, h, j, k)) + 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*y0 + dx*dz*x0 + dy*dz*y0 - cos2) + k = (dx*x0 + dy*y0 + dz*z0)**2 - cos2*(x0*x0 + y0*y0 + z0*z0) - return tuple(getattr(self, '_' + k) for k in Quadric._coeff_keys) + return (a, b, c, d, e, f, g, h, j, k) class XCone(QuadricMixin, Surface): From 030a5873257d926eb3e02dd51ebe1ce062eac7a0 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 19 Feb 2020 12:48:11 -0600 Subject: [PATCH 151/166] 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 3f61b2cd8..000000000 --- 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 23f3d8438..23878ac20 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 1c174b775..12969cf29 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 6bd4c5cd900ab38088faa68024d29f46e5c72552 Mon Sep 17 00:00:00 2001 From: Sam Powell-Gill Date: Thu, 20 Feb 2020 08:48:26 +0000 Subject: [PATCH 152/166] Added suggested changes for #1489 --- tests/unit_tests/test_data_photon.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/tests/unit_tests/test_data_photon.py b/tests/unit_tests/test_data_photon.py index 02635bcc9..2fa0f85a7 100644 --- a/tests/unit_tests/test_data_photon.py +++ b/tests/unit_tests/test_data_photon.py @@ -2,6 +2,7 @@ from collections.abc import Mapping, Callable import os +from pathlib import Path import numpy as np import pandas as pd @@ -145,10 +146,8 @@ def test_export_to_hdf5(tmpdir, element): # Export to hdf5 again element2.export_to_hdf5(filename, 'w') -def test_photodat_only(tmpdir): - endf_data = os.environ['OPENMC_ENDF_DATA'] - filename = str(tmpdir.join('tmp.h5')) - p_file = 'photoat-{:03}_{}_000.endf'.format(1, 'H') - p_path = os.path.join(endf_data, 'photoat', p_file) - data=openmc.data.IncidentPhoton.from_endf(p_path) - data.export_to_hdf5(filename, 'w') \ No newline at end of file +def test_photodat_only(run_in_tmpdir): + endf_dir = Path(os.environ['OPENMC_ENDF_DATA']) + photoatomic_file = endf_dir / 'photoat/photoat-001_H_000.endf' + data=openmc.data.IncidentPhoton.from_endf(photoatomic_file) + data.export_to_hdf5('tmp.h5', 'w') \ No newline at end of file From 0fbfad4d49c543f9203967bb7f9ec5a5225d3734 Mon Sep 17 00:00:00 2001 From: Sam Powell-Gill Date: Thu, 20 Feb 2020 16:29:08 +0000 Subject: [PATCH 153/166] Made requested formatting change --- tests/unit_tests/test_data_photon.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit_tests/test_data_photon.py b/tests/unit_tests/test_data_photon.py index 2fa0f85a7..f7274e9c1 100644 --- a/tests/unit_tests/test_data_photon.py +++ b/tests/unit_tests/test_data_photon.py @@ -148,6 +148,6 @@ def test_export_to_hdf5(tmpdir, element): def test_photodat_only(run_in_tmpdir): endf_dir = Path(os.environ['OPENMC_ENDF_DATA']) - photoatomic_file = endf_dir / 'photoat/photoat-001_H_000.endf' - data=openmc.data.IncidentPhoton.from_endf(photoatomic_file) + photoatomic_file = endf_dir / 'photoat' / 'photoat-001_H_000.endf' + data = openmc.data.IncidentPhoton.from_endf(photoatomic_file) data.export_to_hdf5('tmp.h5', 'w') \ No newline at end of file From 48ee3e78ec9e82919d2e3cf11b7ba2d8dbfbe20c Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 20 Feb 2020 13:09:15 -0600 Subject: [PATCH 154/166] Make sure DAGMC is discoverable if linking against OpenMC --- CMakeLists.txt | 24 ++++++++++++------- cmake/Modules/FindDAGMC.cmake | 10 ++++---- ...enMCConfig.cmake => OpenMCConfig.cmake.in} | 7 ++++++ 3 files changed, 28 insertions(+), 13 deletions(-) rename cmake/{OpenMCConfig.cmake => OpenMCConfig.cmake.in} (58%) diff --git a/CMakeLists.txt b/CMakeLists.txt index ec6b500b4..c776a7747 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,11 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) # Set module path set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules) +# Allow user to specify _ROOT variables +if (NOT (CMAKE_VERSION VERSION_LESS 3.12)) + cmake_policy(SET CMP0074 NEW) +endif() + #=============================================================================== # Command line options #=============================================================================== @@ -35,18 +40,18 @@ endif() #=============================================================================== if(dagmc) find_package(DAGMC REQUIRED) - link_directories(${DAGMC_LIBRARY_DIRS}) + add_library(dagmc-imported INTERFACE IMPORTED) + target_link_directories(dagmc-imported INTERFACE ${DAGMC_LIBRARY_DIRS}) + target_link_libraries(dagmc-imported INTERFACE ${DAGMC_LIBRARIES}) + target_include_directories(dagmc-imported INTERFACE ${DAGMC_INCLUDE_DIRS}) +else() + set(DAGMC_FOUND false) endif() #=============================================================================== # HDF5 for binary output #=============================================================================== -# Allow user to specify HDF5_ROOT -if (NOT (CMAKE_VERSION VERSION_LESS 3.12)) - cmake_policy(SET CMP0074 NEW) -endif() - # Unfortunately FindHDF5.cmake will always prefer a serial HDF5 installation # over a parallel installation if both appear on the user's PATH. To get around # this, we check for the environment variable HDF5_ROOT and if it exists, use it @@ -357,8 +362,7 @@ target_link_libraries(libopenmc ${ldflags} ${HDF5_LIBRARIES} ${HDF5_HL_LIBRARIES if(dagmc) target_compile_definitions(libopenmc PRIVATE DAGMC) - target_link_libraries(libopenmc ${DAGMC_LIBRARIES}) - target_include_directories(libopenmc PRIVATE ${DAGMC_INCLUDE_DIRS}) + target_link_libraries(libopenmc dagmc-imported) endif() #=============================================================================== @@ -388,6 +392,8 @@ add_custom_command(TARGET libopenmc POST_BUILD # Install executable, scripts, manpage, license #=============================================================================== +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 EXPORT openmc-targets @@ -401,7 +407,7 @@ install(EXPORT openmc-targets DESTINATION ${INSTALL_CONFIGDIR}) install(DIRECTORY src/relaxng DESTINATION ${CMAKE_INSTALL_DATADIR}/openmc) -install(FILES cmake/OpenMCConfig.cmake DESTINATION ${INSTALL_CONFIGDIR}) +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}) diff --git a/cmake/Modules/FindDAGMC.cmake b/cmake/Modules/FindDAGMC.cmake index c644c8886..bb9bc56dd 100644 --- a/cmake/Modules/FindDAGMC.cmake +++ b/cmake/Modules/FindDAGMC.cmake @@ -12,7 +12,9 @@ find_path(DAGMC_CMAKE_CONFIG NAMES DAGMCConfig.cmake PATHS ENV LD_LIBRARY_PATH PATH_SUFFIXES lib Lib cmake lib/cmake NO_DEFAULT_PATH) - -message(STATUS "Found DAGMC in ${DAGMC_CMAKE_CONFIG}") - -include(${DAGMC_CMAKE_CONFIG}/DAGMCConfig.cmake) +if(DAGMC_CMAKE_CONFIG) + message(STATUS "Found DAGMC in ${DAGMC_CMAKE_CONFIG}") + include(${DAGMC_CMAKE_CONFIG}/DAGMCConfig.cmake) +else() + message(WARNING "Cound not find DAGMC") +endif() diff --git a/cmake/OpenMCConfig.cmake b/cmake/OpenMCConfig.cmake.in similarity index 58% rename from cmake/OpenMCConfig.cmake rename to cmake/OpenMCConfig.cmake.in index 29a0e4542..ad00a2861 100644 --- a/cmake/OpenMCConfig.cmake +++ b/cmake/OpenMCConfig.cmake.in @@ -5,6 +5,13 @@ find_package(gsl-lite REQUIRED HINTS ${OpenMC_CMAKE_DIR}/../gsl-lite) find_package(pugixml REQUIRED HINTS ${OpenMC_CMAKE_DIR}/../pugixml) find_package(xtl REQUIRED HINTS ${OpenMC_CMAKE_DIR}/../xtl) find_package(xtensor REQUIRED HINTS ${OpenMC_CMAKE_DIR}/../xtensor) +if(@DAGMC_FOUND@) + find_package(DAGMC REQUIRED HINTS @DAGMC_LIBRARY_DIRS@) + add_library(dagmc-imported INTERFACE IMPORTED) + target_link_directories(dagmc-imported INTERFACE ${DAGMC_LIBRARY_DIRS}) + target_link_libraries(dagmc-imported INTERFACE ${DAGMC_LIBRARIES}) + target_include_directories(dagmc-imported INTERFACE ${DAGMC_INCLUDE_DIRS}) +endif() if(NOT TARGET OpenMC::libopenmc) include("${OpenMC_CMAKE_DIR}/OpenMCTargets.cmake") From c7c3db82467869c85a5249e5567ee12d0cd2c620 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Sun, 23 Feb 2020 22:15:28 -0600 Subject: [PATCH 155/166] Don't use target_link_directorires (only supported in cmake 3.13+) --- CMakeLists.txt | 2 +- cmake/OpenMCConfig.cmake.in | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c776a7747..29eed4fb0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,7 +41,7 @@ endif() if(dagmc) find_package(DAGMC REQUIRED) add_library(dagmc-imported INTERFACE IMPORTED) - target_link_directories(dagmc-imported INTERFACE ${DAGMC_LIBRARY_DIRS}) + link_directories(${DAGMC_LIBRARY_DIRS}) target_link_libraries(dagmc-imported INTERFACE ${DAGMC_LIBRARIES}) target_include_directories(dagmc-imported INTERFACE ${DAGMC_INCLUDE_DIRS}) else() diff --git a/cmake/OpenMCConfig.cmake.in b/cmake/OpenMCConfig.cmake.in index ad00a2861..1c6a488f5 100644 --- a/cmake/OpenMCConfig.cmake.in +++ b/cmake/OpenMCConfig.cmake.in @@ -8,8 +8,10 @@ find_package(xtensor REQUIRED HINTS ${OpenMC_CMAKE_DIR}/../xtensor) if(@DAGMC_FOUND@) find_package(DAGMC REQUIRED HINTS @DAGMC_LIBRARY_DIRS@) add_library(dagmc-imported INTERFACE IMPORTED) - target_link_directories(dagmc-imported INTERFACE ${DAGMC_LIBRARY_DIRS}) target_link_libraries(dagmc-imported INTERFACE ${DAGMC_LIBRARIES}) + foreach(dir ${DAGMC_LIBRARY_DIRS}) + target_link_libraries(dagmc-imported INTERFACE "-L${dir}") + endforeach() target_include_directories(dagmc-imported INTERFACE ${DAGMC_INCLUDE_DIRS}) endif() From 6bea6fe081d53ca4c0270bc3a2382d21cb6b59e2 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 24 Feb 2020 19:38:34 -0600 Subject: [PATCH 156/166] Revert changes in CMakeLists.txt --- CMakeLists.txt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5e74ce96d..29eed4fb0 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 157/166] 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 28754e716..83aa0691f 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 0feb28702..d68122dd7 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 339d41723..7b709321f 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 158/166] Refactoring improvements --- openmc/surface.py | 333 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 246 insertions(+), 87 deletions(-) diff --git a/openmc/surface.py b/openmc/surface.py index 92c828d65..0c1513a52 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 159/166] 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 0c1513a52..62ea83456 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 160/166] removed extraneous definition --- openmc/surface.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/openmc/surface.py b/openmc/surface.py index 62ea83456..a4c8b8c88 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 161/166] 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 a4c8b8c88..a1696b08c 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 162/166] 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 66440a3c3..ec10fea4f 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 3ee505240..9999c383f 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 a1696b08c..4d0434a4b 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 99db83148..c193645c3 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 703010a05..86e01b6af 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 163/166] 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 4d0434a4b..cc92a810b 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 164/166] 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 cc92a810b..e4c11692a 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 86e01b6af..b6c739877 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 165/166] 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 e4c11692a..567968b2c 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 b6c739877..749ae9621 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 166/166] 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 7e352fbf0..0aae5adb7 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 7d68b3a3d..b8a258df0 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):