diff --git a/CMakeLists.txt b/CMakeLists.txt index 57d2c6dac..d6821c414 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -312,7 +312,6 @@ add_library(libopenmc SHARED src/material_header.F90 src/math.F90 src/matrix_header.F90 - src/mesh.F90 src/mesh_header.F90 src/message_passing.F90 src/mgxs_data.F90 @@ -380,11 +379,13 @@ add_library(libopenmc SHARED src/tallies/trigger.F90 src/tallies/trigger_header.F90 src/cell.cpp + src/cmfd_execute.cpp src/distribution.cpp src/distribution_angle.cpp src/distribution_energy.cpp src/distribution_multi.cpp src/distribution_spatial.cpp + src/eigenvalue.cpp src/endf.cpp src/initialize.cpp src/finalize.cpp @@ -394,6 +395,7 @@ add_library(libopenmc SHARED src/lattice.cpp src/material.cpp src/math_functions.cpp + src/mesh.cpp src/message_passing.cpp src/mgxs.cpp src/mgxs_interface.cpp diff --git a/include/openmc/capi.h b/include/openmc/capi.h index bc4371592..c48b212bd 100644 --- a/include/openmc/capi.h +++ b/include/openmc/capi.h @@ -37,6 +37,7 @@ extern "C" { int openmc_filter_set_type(int32_t index, const char* type); int openmc_finalize(); int openmc_find_cell(double* xyz, int32_t* index, int32_t* instance); + int openmc_fission_bank(struct Bank** ptr, int64_t* n); int openmc_get_cell_index(int32_t id, int32_t* index); int openmc_get_filter_index(int32_t id, int32_t* index); void openmc_get_filter_next_id(int32_t* id); @@ -71,7 +72,7 @@ extern "C" { int openmc_mesh_get_params(int32_t index, double** ll, double** ur, double** width, int* n); int openmc_mesh_set_id(int32_t index, int32_t id); int openmc_mesh_set_dimension(int32_t index, int n, const int* dims); - int openmc_mesh_set_params(int32_t index, const double* ll, const double* ur, const double* width, int n); + int openmc_mesh_set_params(int32_t index, int n, const double* ll, const double* ur, const double* width); int openmc_meshsurface_filter_get_mesh(int32_t index, int32_t* index_mesh); int openmc_meshsurface_filter_set_mesh(int32_t index, int32_t index_mesh); int openmc_next_batch(int* status); @@ -135,16 +136,11 @@ extern "C" { extern char openmc_err_msg[256]; extern double openmc_keff; extern double openmc_keff_std; - extern int32_t gen_per_batch; - extern int32_t n_batches; extern int32_t n_cells; extern int32_t n_filters; - extern int32_t n_inactive; extern int32_t n_lattices; extern int32_t n_materials; - extern int32_t n_meshes; extern int n_nuclides; - extern int64_t n_particles; extern int32_t n_plots; extern int32_t n_realizations; extern int32_t n_sab_tables; @@ -152,9 +148,7 @@ extern "C" { extern int32_t n_surfaces; extern int32_t n_tallies; extern int32_t n_universes; - extern int openmc_run_mode; extern bool openmc_simulation_initialized; - extern int openmc_verbosity; // Variables that are shared by necessity (can be removed from public header // later) @@ -164,13 +158,6 @@ extern "C" { extern int openmc_rank; extern int64_t openmc_work; - // Run modes - const int RUN_MODE_FIXEDSOURCE = 1; - const int RUN_MODE_EIGENVALUE = 2; - const int RUN_MODE_PLOTTING = 3; - const int RUN_MODE_PARTICLE = 4; - const int RUN_MODE_VOLUME = 5; - #ifdef __cplusplus } #endif diff --git a/include/openmc/constants.h b/include/openmc/constants.h index 12dc21b71..9c96258fb 100644 --- a/include/openmc/constants.h +++ b/include/openmc/constants.h @@ -11,16 +11,9 @@ namespace openmc { -// TODO: Replace with xtensor/other library? -typedef std::vector double_1dvec; -typedef std::vector > double_2dvec; -typedef std::vector > > double_3dvec; -typedef std::vector > > > double_4dvec; -typedef std::vector > > > > double_5dvec; -typedef std::vector > > > > > double_6dvec; -typedef std::vector int_1dvec; -typedef std::vector > int_2dvec; -typedef std::vector > > int_3dvec; +using double_2dvec = std::vector>; +using double_3dvec = std::vector>>; +using double_4dvec = std::vector>>>; // ============================================================================ // VERSIONING NUMBERS @@ -431,6 +424,13 @@ enum class Interpolation { histogram, lin_lin, lin_log, log_lin, log_log }; +// Run modes +constexpr int RUN_MODE_FIXEDSOURCE {1}; +constexpr int RUN_MODE_EIGENVALUE {2}; +constexpr int RUN_MODE_PLOTTING {3}; +constexpr int RUN_MODE_PARTICLE {4}; +constexpr int RUN_MODE_VOLUME {5}; + } // namespace openmc #endif // OPENMC_CONSTANTS_H diff --git a/include/openmc/eigenvalue.h b/include/openmc/eigenvalue.h new file mode 100644 index 000000000..cc879461e --- /dev/null +++ b/include/openmc/eigenvalue.h @@ -0,0 +1,41 @@ +#ifndef OPENMC_EIGENVALUE_H +#define OPENMC_EIGENVALUE_H + +#include // for int64_t +#include + +#include "xtensor/xtensor.hpp" + +#include "openmc/particle.h" + +namespace openmc { + +//============================================================================== +// Global variables +//============================================================================== + +extern std::vector entropy; //!< Shannon entropy at each generation +extern xt::xtensor source_frac; //!< Source fraction for UFS + +extern "C" int64_t n_bank; +#pragma omp threadprivate(n_bank) + +//============================================================================== +// Non-member functions +//============================================================================== + +//! Calculates the Shannon entropy of the fission source distribution to assess +//! source convergence +extern "C" void shannon_entropy(); + +//! Determines the source fraction in each UFS mesh cell and reweights the +//! source bank so that the sum of the weights is equal to n_particles. The +//! 'source_frac' variable is used later to bias the production of fission sites +extern "C" void ufs_count_sites(); + +//! Get UFS weight corresponding to particle's location +extern "C" double ufs_get_weight(const Particle* p); + +} // namespace openmc + +#endif // OPENMC_EIGENVALUE_H diff --git a/include/openmc/hdf5_interface.h b/include/openmc/hdf5_interface.h index 48ce41d0e..468c25980 100644 --- a/include/openmc/hdf5_interface.h +++ b/include/openmc/hdf5_interface.h @@ -14,6 +14,7 @@ #include "xtensor/xarray.hpp" #include "openmc/position.h" +#include "openmc/error.h" namespace openmc { @@ -46,39 +47,6 @@ hid_t file_open(const std::string& filename, char mode, bool parallel=false); void write_string(hid_t group_id, const char* name, const std::string& buffer, bool indep); -void -read_nd_vector(hid_t obj_id, const char* name, std::vector& result, - bool must_have = false); - -void -read_nd_vector(hid_t obj_id, const char* name, - std::vector >& result, - bool must_have = false); - -void -read_nd_vector(hid_t obj_id, const char* name, - std::vector >& result, bool must_have = false); - -void -read_nd_vector(hid_t obj_id, const char* name, - std::vector > >& result, - bool must_have = false); - -void -read_nd_vector(hid_t obj_id, const char* name, - std::vector > >& result, - bool must_have = false); - -void -read_nd_vector(hid_t obj_id, const char* name, - std::vector > > >& result, - bool must_have = false); - -void -read_nd_vector(hid_t obj_id, const char* name, - std::vector > > > >& result, - bool must_have = false); - std::vector attribute_shape(hid_t obj_id, const char* name); std::vector dataset_names(hid_t group_id); void ensure_exists(hid_t group_id, const char* name); @@ -236,7 +204,7 @@ read_attribute(hid_t obj_id, const char* name, std::vector& vec) } //============================================================================== -// Templates/overloads for read_dataset +// Templates/overloads for read_dataset and related methods //============================================================================== template @@ -294,6 +262,40 @@ void read_dataset(hid_t obj_id, const char* name, xt::xarray& arr, bool indep close_dataset(dset); } + +template +void read_dataset_as_shape(hid_t obj_id, const char* name, + xt::xtensor& arr, bool indep=false) +{ + hid_t dset = open_dataset(obj_id, name); + + // Allocate new array to read data into + std::size_t size = 1; + for (const auto x : arr.shape()) + size *= x; + T* buffer = new T[size]; + + // Read data from attribute + read_dataset(dset, nullptr, H5TypeMap::type_id, buffer, indep); + + // Adapt into xarray + arr = xt::adapt(buffer, size, xt::acquire_ownership(), arr.shape()); + + close_dataset(dset); +} + + +template +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); + } else if (must_have) { + fatal_error(std::string("Must provide " + std::string(name) + "!")); + } +} + //============================================================================== // Templates/overloads for write_attribute //============================================================================== @@ -317,6 +319,14 @@ write_attribute(hid_t obj_id, const char* name, const std::array& buffer) write_attr(obj_id, 1, dims, name, H5TypeMap::type_id, buffer.data()); } +template inline void +write_attribute(hid_t obj_id, const char* name, const std::vector& buffer) +{ + hsize_t dims[] {buffer.size()}; + write_attr(obj_id, 1, dims, name, H5TypeMap::type_id, buffer.data()); +} + + //============================================================================== // Templates/overloads for write_dataset //============================================================================== @@ -347,6 +357,15 @@ write_dataset(hid_t obj_id, const char* name, const std::vector& buffer) write_dataset(obj_id, 1, dims, name, H5TypeMap::type_id, buffer.data(), false); } +template inline void +write_dataset(hid_t obj_id, const char* name, const xt::xarray& arr) +{ + 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); +} + inline void write_dataset(hid_t obj_id, const char* name, Position r) { diff --git a/include/openmc/mesh.h b/include/openmc/mesh.h new file mode 100644 index 000000000..3d31fb489 --- /dev/null +++ b/include/openmc/mesh.h @@ -0,0 +1,131 @@ +//! \file mesh.h +//! \brief Mesh types used for tallies, Shannon entropy, CMFD, etc. + +#ifndef OPENMC_MESH_H +#define OPENMC_MESH_H + +#include // for unique_ptr +#include +#include + +#include "hdf5.h" +#include "pugixml.hpp" +#include "xtensor/xarray.hpp" + +#include "openmc/particle.h" +#include "openmc/position.h" + +namespace openmc { + +//============================================================================== +//! Tessellation of n-dimensional Euclidean space by congruent squares or cubes +//============================================================================== + +class RegularMesh { +public: + // Constructors + RegularMesh() = default; + RegularMesh(pugi::xml_node node); + + // Methods + + //! Determine which bins were crossed by a particle + //! + //! \param[in] p Particle to check + //! \param[out] bins Bins that were crossed + //! \param[out] lengths Fraction of tracklength in each bin + void bins_crossed(const Particle* p, std::vector& bins, + std::vector& lengths) const; + + //! Determine which surface bins were crossed by a particle + //! + //! \param[in] p Particle to check + //! \param[out] bins Surface bins that were crossed + void surface_bins_crossed(const Particle* p, std::vector& bins) const; + + //! Get bin at a given position in space + //! + //! \param[in] r Position to get bin for + //! \return Mesh bin + int get_bin(Position r) const; + + //! Get bin given mesh indices + //! + //! \param[in] Array of mesh indices + //! \return Mesh bin + int get_bin_from_indices(const int* ijk) const; + + //! Get mesh indices given a position + //! + //! \param[in] r Position to get indices for + //! \param[out] ijk Array of mesh indices + //! \param[out] in_mesh Whether position is in mesh + void get_indices(Position r, int* ijk, bool* in_mesh) const; + + //! Get mesh indices corresponding to a mesh bin + //! + //! \param[in] bin Mesh bin + //! \param[out] ijk Mesh indices + void get_indices_from_bin(int bin, int* ijk) const; + + //! Check if a line connected by two points intersects the mesh + //! + //! \param[in] r0 Starting position + //! \param[in] r1 Ending position + //! \return Whether line connecting r0 and r1 intersects mesh + bool intersects(Position r0, Position r1) const; + + //! Write mesh data to an HDF5 group + //! + //! \param[in] group HDF5 group + void to_hdf5(hid_t group) const; + + //! Count number of bank sites in each mesh bin / energy bin + //! + //! \param[in] n Number of bank sites + //! \param[in] bank Array of bank sites + //! \param[in] n_energy Number of energies + //! \param[in] energies Array of energies + //! \param[out] Whether any bank sites are outside the mesh + //! \return Array indicating number of sites in each mesh/energy bin + xt::xarray count_sites(int64_t n, const Bank* bank, + int n_energy, const double* energies, bool* outside) const; + + int id_ {-1}; //!< User-specified ID + int n_dimension_; //!< Number of dimensions + double volume_frac_; //!< Volume fraction of each mesh element + xt::xarray shape_; //!< Number of mesh elements in each dimension + xt::xarray lower_left_; //!< Lower-left coordinates of mesh + xt::xarray upper_right_; //!< Upper-right coordinates of mesh + xt::xarray width_; //!< Width of each mesh element + +private: + bool intersects_1d(Position r0, Position r1) const; + bool intersects_2d(Position r0, Position r1) const; + bool intersects_3d(Position r0, Position r1) const; +}; + +//============================================================================== +// Non-member functions +//============================================================================== + +//! Read meshes from either settings/tallies +//! \param[in] root XML node +extern "C" void read_meshes(pugi::xml_node* root); + +//! Write mesh data to an HDF5 group +//! \param[in] group HDF5 group +extern "C" void meshes_to_hdf5(hid_t group); + +//============================================================================== +// Global variables +//============================================================================== + +extern std::vector> meshes; + +extern std::unordered_map mesh_map; + + +} // namespace openmc + +#endif // OPENMC_MESH_H diff --git a/include/openmc/message_passing.h b/include/openmc/message_passing.h index 1dab43139..c910210db 100644 --- a/include/openmc/message_passing.h +++ b/include/openmc/message_passing.h @@ -10,6 +10,7 @@ namespace mpi { extern int rank; extern int n_procs; + extern bool master; #ifdef OPENMC_MPI extern MPI_Datatype bank; diff --git a/include/openmc/mgxs.h b/include/openmc/mgxs.h index 40746c291..e80355bee 100644 --- a/include/openmc/mgxs.h +++ b/include/openmc/mgxs.h @@ -7,6 +7,8 @@ #include #include +#include "xtensor/xtensor.hpp" + #include "openmc/constants.h" #include "openmc/hdf5_interface.h" #include "openmc/xsdata.h" @@ -35,7 +37,7 @@ struct CacheData { class Mgxs { private: - double_1dvec kTs; // temperature in eV (k * T) + xt::xtensor kTs; // temperature in eV (k * T) int scatter_format; // flag for if this is legendre, histogram, or tabular int num_delayed_groups; // number of delayed neutron groups int num_groups; // number of energy groups @@ -44,8 +46,8 @@ class Mgxs { bool is_isotropic; // used to skip search for angle indices if isotropic int n_pol; int n_azi; - double_1dvec polar; - double_1dvec azimuthal; + std::vector polar; + std::vector azimuthal; //! \brief Initializes the Mgxs object metadata //! @@ -62,10 +64,10 @@ class Mgxs { //! @param in_polar Polar angle grid. //! @param in_azimuthal Azimuthal angle grid. void - init(const std::string& in_name, double in_awr, const double_1dvec& in_kTs, + init(const std::string& in_name, double in_awr, const std::vector& in_kTs, bool in_fissionable, int in_scatter_format, int in_num_groups, int in_num_delayed_groups, bool in_is_isotropic, - const double_1dvec& in_polar, const double_1dvec& in_azimuthal); + const std::vector& in_polar, const std::vector& in_azimuthal); //! \brief Initializes the Mgxs object metadata from the HDF5 file //! @@ -80,8 +82,8 @@ class Mgxs { //! @param method Method of choosing nearest temperatures. void metadata_from_hdf5(hid_t xs_id, int in_num_groups, - int in_num_delayed_groups, const double_1dvec& temperature, - double tolerance, int_1dvec& temps_to_read, int& order_dim, + int in_num_delayed_groups, const std::vector& temperature, + double tolerance, std::vector& temps_to_read, int& order_dim, int& method); //! \brief Performs the actual act of combining the microscopic data for a @@ -93,8 +95,8 @@ class Mgxs { //! corresponds to the temperature of interest. //! @param this_t The temperature index of the macroscopic object. void - combine(const std::vector& micros, const double_1dvec& scalars, - const int_1dvec& micro_ts, int this_t); + combine(const std::vector& micros, const std::vector& scalars, + const std::vector& micro_ts, int this_t); //! \brief Checks to see if this and that are able to be combined //! @@ -128,7 +130,7 @@ class Mgxs { //! provides the number of points to use in the tabular representation. //! @param method Method of choosing nearest temperatures. Mgxs(hid_t xs_id, int energy_groups, - int delayed_groups, const double_1dvec& temperature, double tolerance, + int delayed_groups, const std::vector& temperature, double tolerance, int max_order, bool legendre_to_tabular, int legendre_to_tabular_points, int& method); @@ -141,8 +143,8 @@ class Mgxs { //! @param atom_densities Atom densities of those microscopic quantities. //! @param tolerance Tolerance of temperature selection method. //! @param method Method of choosing nearest temperatures. - Mgxs(const std::string& in_name, const double_1dvec& mat_kTs, - const std::vector& micros, const double_1dvec& atom_densities, + Mgxs(const std::string& in_name, const std::vector& mat_kTs, + const std::vector& micros, const std::vector& atom_densities, double tolerance, int& method); //! \brief Provides a cross section value given certain parameters diff --git a/include/openmc/output.h b/include/openmc/output.h index cabd39ede..1ba5d6599 100644 --- a/include/openmc/output.h +++ b/include/openmc/output.h @@ -22,5 +22,7 @@ void header(const char* msg, int level); extern "C" void print_overlap_check(); +extern "C" void title(); + } // namespace openmc #endif // OPENMC_OUTPUT_H diff --git a/include/openmc/particle.h b/include/openmc/particle.h index 846d2e5a6..298b2878a 100644 --- a/include/openmc/particle.h +++ b/include/openmc/particle.h @@ -161,7 +161,7 @@ extern "C" { {mark_as_lost(message.str());} //! create a particle restart HDF5 file - void write_restart(); + void write_restart() const; }; diff --git a/include/openmc/position.h b/include/openmc/position.h index 97ab6f0e6..d7a13c01e 100644 --- a/include/openmc/position.h +++ b/include/openmc/position.h @@ -1,6 +1,7 @@ #ifndef OPENMC_POSITION_H #define OPENMC_POSITION_H +#include #include namespace openmc { @@ -46,6 +47,9 @@ struct Position { inline double dot(Position other) { return x*other.x + y*other.y + z*other.z; } + inline double norm() { + return std::sqrt(x*x + y*y + z*z); + } // Data members double x = 0.; diff --git a/include/openmc/scattdata.h b/include/openmc/scattdata.h index 980238aeb..8ea81f252 100644 --- a/include/openmc/scattdata.h +++ b/include/openmc/scattdata.h @@ -6,6 +6,8 @@ #include +#include "xtensor/xtensor.hpp" + #include "openmc/constants.h" namespace openmc { @@ -25,23 +27,25 @@ class ScattData { protected: //! \brief Initializes the attributes of the base class. void - base_init(int order, const int_1dvec& in_gmin, const int_1dvec& in_gmax, - const double_2dvec& in_energy, const double_2dvec& in_mult); + base_init(int order, const xt::xtensor& in_gmin, + const xt::xtensor& in_gmax, const double_2dvec& in_energy, + const double_2dvec& in_mult); //! \brief Combines microscopic ScattDatas into a macroscopic one. void - base_combine(int max_order, const std::vector& those_scatts, - const double_1dvec& scalars, int_1dvec& in_gmin, int_1dvec& in_gmax, - double_2dvec& sparse_mult, double_3dvec& sparse_scatter); + base_combine(size_t max_order, const std::vector& those_scatts, + const std::vector& scalars, xt::xtensor& in_gmin, + xt::xtensor& in_gmax, double_2dvec& sparse_mult, + double_3dvec& sparse_scatter); public: - double_2dvec energy; // Normalized p0 matrix for sampling Eout - double_2dvec mult; // nu-scatter multiplication (nu-scatt/scatt) - double_3dvec dist; // Angular distribution - int_1dvec gmin; // minimum outgoing group - int_1dvec gmax; // maximum outgoing group - double_1dvec scattxs; // Isotropic Sigma_{s,g_{in}} + double_2dvec energy; // Normalized p0 matrix for sampling Eout + double_2dvec mult; // nu-scatter multiplication (nu-scatt/scatt) + double_3dvec dist; // Angular distribution + xt::xtensor gmin; // minimum outgoing group + xt::xtensor gmax; // maximum outgoing group + xt::xtensor scattxs; // Isotropic Sigma_{s,g_{in}} //! \brief Calculates the value of normalized f(mu). //! @@ -72,7 +76,7 @@ class ScattData { //! @param in_mult Input sparse multiplicity matrix //! @param coeffs Input sparse scattering matrix virtual void - init(const int_1dvec& in_gmin, const int_1dvec& in_gmax, + init(const xt::xtensor& in_gmin, const xt::xtensor& in_gmax, const double_2dvec& in_mult, const double_3dvec& coeffs) = 0; //! \brief Combines the microscopic data. @@ -81,7 +85,7 @@ class ScattData { //! @param scalars Scalars to multiply the microscopic data by. virtual void combine(const std::vector& those_scatts, - const double_1dvec& scalars) = 0; + const std::vector& scalars) = 0; //! \brief Getter for the dimensionality of the scattering order. //! @@ -89,7 +93,7 @@ class ScattData { //! of points, and for Histogram this is the number of bins. //! //! @return The order. - virtual int + virtual size_t get_order() = 0; //! \brief Builds a dense scattering matrix from the constituent parts @@ -97,8 +101,8 @@ class ScattData { //! @param max_order If Legendre this is the maximum value of "n" in "Pn" //! requested; ignored otherwise. //! @return The dense scattering matrix. - virtual double_3dvec - get_matrix(int max_order) = 0; + virtual xt::xtensor + get_matrix(size_t max_order) = 0; //! \brief Samples the outgoing energy from the ScattData info. //! @@ -142,12 +146,12 @@ class ScattDataLegendre: public ScattData { public: void - init(const int_1dvec& in_gmin, const int_1dvec& in_gmax, + init(const xt::xtensor& in_gmin, const xt::xtensor& in_gmax, const double_2dvec& in_mult, const double_3dvec& coeffs); void combine(const std::vector& those_scatts, - const double_1dvec& scalars); + const std::vector& scalars); //! \brief Find the maximal value of the angular distribution to use as a // bounding box with rejection sampling. @@ -160,11 +164,11 @@ class ScattDataLegendre: public ScattData { void sample(int gin, int& gout, double& mu, double& wgt); - int + size_t get_order() {return dist[0][0].size() - 1;}; - double_3dvec - get_matrix(int max_order); + xt::xtensor + get_matrix(size_t max_order); }; //============================================================================== @@ -176,19 +180,19 @@ class ScattDataHistogram: public ScattData { protected: - double_1dvec mu; // Angle distribution mu bin boundaries - double dmu; // Quick storage of the spacing between the mu bin points - double_3dvec fmu; // The angular distribution histogram + xt::xtensor mu; // Angle distribution mu bin boundaries + double dmu; // Quick storage of the mu spacing + double_3dvec fmu; // The angular distribution histogram public: void - init(const int_1dvec& in_gmin, const int_1dvec& in_gmax, + init(const xt::xtensor& in_gmin, const xt::xtensor& in_gmax, const double_2dvec& in_mult, const double_3dvec& coeffs); void combine(const std::vector& those_scatts, - const double_1dvec& scalars); + const std::vector& scalars); double calc_f(int gin, int gout, double mu); @@ -196,11 +200,11 @@ class ScattDataHistogram: public ScattData { void sample(int gin, int& gout, double& mu, double& wgt); - int + size_t get_order() {return dist[0][0].size();}; - double_3dvec - get_matrix(int max_order); + xt::xtensor + get_matrix(size_t max_order); }; //============================================================================== @@ -212,9 +216,9 @@ class ScattDataTabular: public ScattData { protected: - double_1dvec mu; // Angle distribution mu grid points - double dmu; // Quick storage of the spacing between the mu points - double_3dvec fmu; // The angular distribution function + xt::xtensor mu; // Angle distribution mu grid points + double dmu; // Quick storage of the mu spacing + double_3dvec fmu; // The angular distribution function // Friend convert_legendre_to_tabular so it has access to protected // parameters @@ -225,12 +229,12 @@ class ScattDataTabular: public ScattData { public: void - init(const int_1dvec& in_gmin, const int_1dvec& in_gmax, + init(const xt::xtensor& in_gmin, const xt::xtensor& in_gmax, const double_2dvec& in_mult, const double_3dvec& coeffs); void combine(const std::vector& those_scatts, - const double_1dvec& scalars); + const std::vector& scalars); double calc_f(int gin, int gout, double mu); @@ -238,10 +242,11 @@ class ScattDataTabular: public ScattData { void sample(int gin, int& gout, double& mu, double& wgt); - int + size_t get_order() {return dist[0][0].size();}; - double_3dvec get_matrix(int max_order); + xt::xtensor + get_matrix(size_t max_order); }; //============================================================================== diff --git a/include/openmc/settings.h b/include/openmc/settings.h index 5a29d0dc8..e5684c7ae 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -5,6 +5,7 @@ //! \brief Settings for OpenMC #include +#include #include #include "pugixml.hpp" @@ -15,39 +16,84 @@ namespace openmc { // Global variable declarations //============================================================================== -// Defined on Fortran side -extern "C" bool openmc_check_overlaps; -extern "C" bool openmc_particle_restart_run; -extern "C" bool openmc_photon_transport; -extern "C" bool openmc_restart_run; -extern "C" bool openmc_run_CE; -extern "C" int openmc_verbosity; -extern "C" bool openmc_write_all_tracks; -extern "C" bool openmc_write_initial_source; +namespace settings { -// Defined in .cpp -// TODO: Make strings instead of char* once Fortran is gone -extern "C" char* openmc_path_input; -extern "C" char* openmc_path_statepoint; -extern "C" char* openmc_path_sourcepoint; -extern "C" char* openmc_path_particle_restart; -extern std::string path_cross_sections; -extern std::string path_multipole; -extern std::string path_output; +// Boolean flags +extern "C" bool assume_separate; //!< assume tallies are spatially separate? +extern "C" bool check_overlaps; //!< check overlaps in geometry? +extern "C" bool cmfd_run; //!< use CMFD? +extern "C" bool confidence_intervals; //!< use confidence intervals for results? +extern "C" bool create_fission_neutrons; //!< create fission neutrons (fixed source)? +extern "C" bool entropy_on; //!< calculate Shannon entropy? +extern "C" bool legendre_to_tabular; //!< convert Legendre distributions to tabular? +extern "C" bool output_summary; //!< write summary.h5? +extern "C" bool output_tallies; //!< write tallies.out? +extern "C" bool particle_restart_run; //!< particle restart run? +extern "C" bool photon_transport; //!< photon transport turned on? +extern "C" bool reduce_tallies; //!< reduce tallies at end of batch? +extern "C" bool res_scat_on; //!< use resonance upscattering method? +extern "C" bool restart_run; //!< restart run? +extern "C" bool run_CE; //!< run with continuous-energy data? +extern "C" bool source_latest; //!< write latest source at each batch? +extern "C" bool source_separate; //!< write source to separate file? +extern "C" bool source_write; //!< write source in HDF5 files? +extern "C" bool survival_biasing; //!< use survival biasing? +extern "C" bool temperature_multipole; //!< use multipole data? +extern "C" bool trigger_on; //!< tally triggers enabled? +extern "C" bool trigger_predict; //!< predict batches for triggers? +extern "C" bool ufs_on; //!< uniform fission site method on? +extern "C" bool urr_ptables_on; //!< use unresolved resonance prob. tables? +extern "C" bool write_all_tracks; //!< write track files for every particle? +extern "C" bool write_initial_source; //!< write out initial source file? + +// Paths to various files +extern std::string path_cross_sections; //!< path to cross_sections.xml +extern std::string path_input; //!< directory where main .xml files resides +extern std::string path_multipole; //!< directory containing multipole files +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_sourcepoint; //!< path to a source file +extern std::string path_statepoint; //!< path to a statepoint file -extern int temperature_method; -extern bool temperature_multipole; -extern double temperature_tolerance; -extern double temperature_default; -extern std::array temperature_range; +extern "C" int32_t index_entropy_mesh; //!< Index of entropy mesh in global mesh array +extern "C" int32_t index_ufs_mesh; //!< Index of UFS mesh in global mesh array + +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" int electron_treatment; //!< how to treat secondary electrons +extern "C" double energy_cutoff[4]; //!< Energy cutoff in [eV] for each particle type +extern "C" int legendre_to_tabular_points; //!< number of points to convert Legendres +extern "C" int max_order; //!< Maximum Legendre order for multigroup data +extern "C" int n_log_bins; //!< number of bins for logarithmic energy grid +extern "C" int n_max_batches; //!< Maximum number of batches + +extern "C" int res_scat_method; //!< resonance upscattering method +extern "C" double res_scat_energy_min; //!< Min energy in [eV] for res. upscattering +extern "C" double res_scat_energy_max; //!< Max energy in [eV] for res. upscattering +extern "C" int run_mode; //!< Run mode (eigenvalue, fixed src, etc.) +extern "C" int temperature_method; //!< method for choosing temperatures +extern "C" double temperature_tolerance; //!< Tolerance in [K] on choosing temperatures +extern "C" double temperature_default; //!< Default T in [K] +extern "C" double temperature_range[2]; //!< Min/max T in [K] over which to load xs +extern "C" int trace_batch; //!< Batch to trace particle on +extern "C" int trace_gen; //!< Generation to trace particle on +extern "C" int64_t trace_particle; //!< Particle ID to enable trace on +extern "C" int trigger_batch_interval; //!< Batch interval for triggers +extern "C" int verbosity; //!< How verbose to make output +extern "C" double weight_cutoff; //!< Weight cutoff for Russian roulette +extern "C" double weight_survive; //!< Survival weight after Russian roulette + +} // namespace settings -//============================================================================== //! Read settings from XML file //! \param[in] root XML node for -//============================================================================== +extern "C" void read_settings_xml(); -extern "C" void read_settings(pugi::xml_node* root); +extern "C" void read_settings_xml_f(pugi::xml_node_struct* root_ptr); } // namespace openmc diff --git a/include/openmc/xml_interface.h b/include/openmc/xml_interface.h index fb87ffdae..85db26faa 100644 --- a/include/openmc/xml_interface.h +++ b/include/openmc/xml_interface.h @@ -1,11 +1,14 @@ #ifndef OPENMC_XML_INTERFACE_H #define OPENMC_XML_INTERFACE_H +#include // for size_t #include // for stringstream #include #include #include "pugixml.hpp" +#include "xtensor/xarray.hpp" +#include "xtensor/xadapt.hpp" namespace openmc { @@ -38,5 +41,14 @@ std::vector get_node_array(pugi::xml_node node, const char* name, return values; } +template +xt::xarray get_node_xarray(pugi::xml_node node, const char* name, + bool lowercase=false) +{ + std::vector v = get_node_array(node, name, lowercase); + std::vector shape = {v.size()}; + return xt::adapt(v, shape); +} + } // namespace openmc #endif // OPENMC_XML_INTERFACE_H diff --git a/include/openmc/xsdata.h b/include/openmc/xsdata.h index 156708c78..fa75b10aa 100644 --- a/include/openmc/xsdata.h +++ b/include/openmc/xsdata.h @@ -7,6 +7,8 @@ #include #include +#include "xtensor/xtensor.hpp" + #include "openmc/hdf5_interface.h" #include "openmc/scattdata.h" @@ -22,41 +24,77 @@ class XsData { private: //! \brief Reads scattering data from the HDF5 file void - scatter_from_hdf5(hid_t xsdata_grp, int n_pol, int n_azi, int energy_groups, + scatter_from_hdf5(hid_t xsdata_grp, size_t n_ang, size_t energy_groups, int scatter_format, int final_scatter_format, int order_data, int max_order, int legendre_to_tabular_points); //! \brief Reads fission data from the HDF5 file void - fission_from_hdf5(hid_t xsdata_grp, int n_pol, int n_azi, int energy_groups, - int delayed_groups, bool is_isotropic); + fission_from_hdf5(hid_t xsdata_grp, size_t n_ang, size_t energy_groups, + size_t delayed_groups, bool is_isotropic); + + //! \brief Reads fission data formatted as chi and nu-fission vectors from + // the HDF5 file when beta is provided. + void + fission_vector_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang, + size_t energy_groups, size_t delayed_groups, bool is_isotropic); + + //! \brief Reads fission data formatted as chi and nu-fission vectors from + // the HDF5 file when beta is not provided. + void + fission_vector_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang, + size_t energy_groups, size_t delayed_groups); + + //! \brief Reads fission data formatted as chi and nu-fission vectors from + // the HDF5 file when no delayed data is provided. + void + fission_vector_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang, + size_t energy_groups); + + //! \brief Reads fission data formatted as a nu-fission matrix from + // the HDF5 file when beta is provided. + void + fission_matrix_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang, + size_t energy_groups, size_t delayed_groups, bool is_isotropic); + + //! \brief Reads fission data formatted as a nu-fission matrix from + // the HDF5 file when beta is not provided. + void + fission_matrix_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang, + size_t energy_groups, size_t delayed_groups); + + //! \brief Reads fission data formatted as a nu-fission matrix from + // the HDF5 file when no delayed data is provided. + void + fission_matrix_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang, + size_t energy_groups); public: // The following quantities have the following dimensions: // [angle][incoming group] - double_2dvec total; - double_2dvec absorption; - double_2dvec nu_fission; - double_2dvec prompt_nu_fission; - double_2dvec kappa_fission; - double_2dvec fission; - double_2dvec inverse_velocity; + xt::xtensor total; + xt::xtensor absorption; + xt::xtensor nu_fission; + xt::xtensor prompt_nu_fission; + xt::xtensor kappa_fission; + xt::xtensor fission; + xt::xtensor inverse_velocity; // decay_rate has the following dimensions: // [angle][delayed group] - double_2dvec decay_rate; + xt::xtensor decay_rate; // delayed_nu_fission has the following dimensions: // [angle][incoming group][delayed group] - double_3dvec delayed_nu_fission; + xt::xtensor delayed_nu_fission; // chi_prompt has the following dimensions: // [angle][incoming group][outgoing group] - double_3dvec chi_prompt; + xt::xtensor chi_prompt; // chi_delayed has the following dimensions: // [angle][incoming group][outgoing group][delayed group] - double_4dvec chi_delayed; + xt::xtensor chi_delayed; // scatter has the following dimensions: [angle] - std::vector > scatter; + std::vector> scatter; XsData() = default; @@ -68,7 +106,7 @@ class XsData { //! @param scatter_format The scattering representation of the file. //! @param n_pol Number of polar angles. //! @param n_azi Number of azimuthal angles. - XsData(int num_groups, int num_delayed_groups, bool fissionable, + XsData(size_t num_groups, size_t num_delayed_groups, bool fissionable, int scatter_format, int n_pol, int n_azi); //! \brief Loads the XsData object from the HDF5 file @@ -101,7 +139,7 @@ class XsData { //! @param micros Microscopic objects to combine. //! @param scalars Scalars to multiply the microscopic data by. void - combine(const std::vector& those_xs, const double_1dvec& scalars); + combine(const std::vector& those_xs, const std::vector& scalars); //! \brief Checks to see if this and that are able to be combined //! diff --git a/openmc/capi/mesh.py b/openmc/capi/mesh.py index 091c5194b..70a4345b0 100644 --- a/openmc/capi/mesh.py +++ b/openmc/capi/mesh.py @@ -41,6 +41,8 @@ _dll.openmc_mesh_set_params.errcheck = _error_handler _dll.openmc_get_mesh_index.argtypes = [c_int32, POINTER(c_int32)] _dll.openmc_get_mesh_index.restype = c_int _dll.openmc_get_mesh_index.errcheck = _error_handler +_dll.n_meshes.argtypes = [] +_dll.n_meshes.restype = c_int class Mesh(_FortranObjectWithID): @@ -172,10 +174,10 @@ class _MeshMapping(Mapping): def __iter__(self): for i in range(len(self)): - yield Mesh(index=i + 1).id + yield Mesh(index=i).id def __len__(self): - return c_int32.in_dll(_dll, 'n_meshes').value + return _dll.n_meshes() def __repr__(self): return repr(dict(self)) diff --git a/openmc/capi/settings.py b/openmc/capi/settings.py index d706112c4..1063d6463 100644 --- a/openmc/capi/settings.py +++ b/openmc/capi/settings.py @@ -20,11 +20,11 @@ class _Settings(object): generations_per_batch = _DLLGlobal(c_int32, 'gen_per_batch') inactive = _DLLGlobal(c_int32, 'n_inactive') particles = _DLLGlobal(c_int64, 'n_particles') - verbosity = _DLLGlobal(c_int, 'openmc_verbosity') + verbosity = _DLLGlobal(c_int, 'verbosity') @property def run_mode(self): - i = c_int.in_dll(_dll, 'openmc_run_mode').value + i = c_int.in_dll(_dll, 'run_mode').value try: return _RUN_MODES[i] except KeyError: @@ -32,7 +32,7 @@ class _Settings(object): @run_mode.setter def run_mode(self, mode): - current_idx = c_int.in_dll(_dll, 'openmc_run_mode') + current_idx = c_int.in_dll(_dll, 'run_mode') for idx, mode_value in _RUN_MODES.items(): if mode_value == mode: current_idx.value = idx diff --git a/openmc/examples.py b/openmc/examples.py index d48d26839..a5138377e 100644 --- a/openmc/examples.py +++ b/openmc/examples.py @@ -1,3 +1,5 @@ +from numbers import Integral + import numpy as np import openmc @@ -538,20 +540,20 @@ def pwr_assembly(): return model -def slab_mg(reps=None, as_macro=True): - """Create a one-group, 1D slab model. +def slab_mg(num_regions=1, mat_names=None, mgxslib_name='2g.h5'): + """Create a 1D slab model. Parameters ---------- - reps : list, optional - List of angular representations. Each item corresponds to materials and - dictates the angular representation of the multi-group cross - sections---isotropic ('iso') or angle-dependent ('ang'), and if Legendre - scattering or tabular scattering ('mu') is used. Thus, items can be - 'ang', 'ang_mu', 'iso', or 'iso_mu'. + num_regions : int, optional + Number of regions in the problem, each with a unique MGXS dataset. + Defaults to 1. - as_macro : bool, optional - Whether :class:`openmc.Macroscopic` is used + mat_names : Iterable of str, optional + List of the material names to use; defaults to ['mat_1', 'mat_2',...]. + + mgxslib_name : str, optional + MGXS Library file to use; defaults to '2g.h5'. Returns ------- @@ -559,71 +561,82 @@ def slab_mg(reps=None, as_macro=True): One-group, 1D slab model """ + + openmc.check_type('num_regions', num_regions, Integral) + openmc.check_greater_than('num_regions', num_regions, 0) + if mat_names is not None: + openmc.check_length('mat_names', mat_names, num_regions) + openmc.check_iterable_type('mat_names', mat_names, str) + else: + mat_names = [] + for i in range(num_regions): + mat_names.append('mat_' + str(i + 1)) + + # # Make Materials + materials_file = openmc.Materials() + macros = [] + mats = [] + for i in range(len(mat_names)): + macros.append(openmc.Macroscopic('mat_' + str(i + 1))) + mats.append(openmc.Material(name=mat_names[i])) + mats[-1].set_density('macro', 1.0) + mats[-1].add_macroscopic(macros[-1]) + + materials_file += mats + + materials_file.cross_sections = mgxslib_name + + # # Make Geometry + rad_outer = 929.45 + # Set a cell boundary to exist for every material above (exclude the 0) + rads = np.linspace(0., rad_outer, len(mats) + 1, endpoint=True)[1:] + + # Instantiate Universe + root = openmc.Universe(universe_id=0, name='root universe') + cells = [] + + surfs = [] + surfs.append(openmc.XPlane(x0=0., boundary_type='reflective')) + for r, rad in enumerate(rads): + if r == len(rads) - 1: + surfs.append(openmc.XPlane(x0=rad, boundary_type='vacuum')) + else: + surfs.append(openmc.XPlane(x0=rad)) + + # Instantiate Cells + cells = [] + for c in range(len(surfs) - 1): + cells.append(openmc.Cell()) + cells[-1].region = (+surfs[c] & -surfs[c + 1]) + cells[-1].fill = mats[c] + + # Register Cells with Universe + root.add_cells(cells) + + # Instantiate a Geometry, register the root Universe, and export to XML + geometry_file = openmc.Geometry(root) + + # # Make Settings + # Instantiate a Settings object, set all runtime parameters + settings_file = openmc.Settings() + settings_file.energy_mode = "multi-group" + settings_file.tabular_legendre = {'enable': False} + settings_file.batches = 10 + settings_file.inactive = 5 + settings_file.particles = 1000 + + # Build source distribution + INF = 1000. + bounds = [0., -INF, -INF, rads[0], INF, INF] + uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:]) + settings_file.source = openmc.source.Source(space=uniform_dist) + + settings_file.output = {'summary': False} + model = openmc.model.Model() - - # Define materials needed for 1D/1G slab problem - mat_names = ['uo2', 'clad', 'lwtr'] - mgxs_reps = ['ang', 'ang_mu', 'iso', 'iso_mu'] - - if reps is None: - reps = mgxs_reps - - xs = [] - i = 0 - for mat in mat_names: - for rep in reps: - i += 1 - name = mat + '_' + rep - xs.append(name) - if as_macro: - m = openmc.Material(name=str(i)) - m.set_density('macro', 1.) - m.add_macroscopic(name) - else: - m = openmc.Material(name=str(i)) - m.set_density('atom/b-cm', 1.) - m.add_nuclide(name, 1.0, 'ao') - model.materials.append(m) - - # Define the materials file - model.xs_data = xs - model.materials.cross_sections = "../../1d_mgxs.h5" - - # Define surfaces. - # Assembly/Problem Boundary - left = openmc.XPlane(x0=0.0, boundary_type='reflective') - right = openmc.XPlane(x0=10.0, boundary_type='reflective') - bottom = openmc.YPlane(y0=0.0, boundary_type='reflective') - top = openmc.YPlane(y0=10.0, boundary_type='reflective') - - # for each material add a plane - planes = [openmc.ZPlane(z0=0.0, boundary_type='reflective')] - dz = round(5. / float(len(model.materials)), 4) - for i in range(len(model.materials) - 1): - planes.append(openmc.ZPlane(z0=dz * float(i + 1))) - planes.append(openmc.ZPlane(z0=5.0, boundary_type='reflective')) - - # Define cells for each material - model.geometry.root_universe = openmc.Universe(name='root universe') - xy = +left & -right & +bottom & -top - for i, mat in enumerate(model.materials): - c = openmc.Cell(fill=mat, region=xy & +planes[i] & -planes[i + 1]) - model.geometry.root_universe.add_cell(c) - - model.settings.batches = 10 - model.settings.inactive = 5 - model.settings.particles = 100 - model.settings.source = openmc.Source(space=openmc.stats.Box( - [0.0, 0.0, 0.0], [10.0, 10.0, 5.])) - model.settings.energy_mode = "multi-group" - - plot = openmc.Plot() - plot.filename = 'mat' - plot.origin = (5.0, 5.0, 2.5) - plot.width = (2.5, 2.5) - plot.basis = 'xz' - plot.pixels = (3000, 3000) - plot.color_by = 'material' - model.plots.append(plot) + model.geometry = geometry_file + model.materials = materials_file + model.settings = settings_file + model.xs_data = macros return model diff --git a/scripts/openmc-get-nndc-data b/scripts/openmc-get-nndc-data index a84a9b282..ff02d3e4c 100755 --- a/scripts/openmc-get-nndc-data +++ b/scripts/openmc-get-nndc-data @@ -148,16 +148,11 @@ if not response or response.lower().startswith('y'): # get a list of all ACE files ace_files = sorted(glob.glob(os.path.join('nndc', '**', '*.ace*'))) -# Get path to fission energy release data -data_dir = os.path.dirname(sys.modules['openmc.data'].__file__) -fer_file = os.path.join(data_dir, 'fission_Q_data_endfb71.h5') - # Call the ace-to-hdf5 conversion script pwd = os.path.dirname(os.path.realpath(__file__)) ace2hdf5 = os.path.join(pwd, 'openmc-ace-to-hdf5') subprocess.call([ace2hdf5, '-d', 'nndc_hdf5', - '--fission_energy_release', fer_file, '--libver', args.libver] + ace_files) # Generate photo interaction library files diff --git a/setup.py b/setup.py index e04bc82f6..cbf564480 100755 --- a/setup.py +++ b/setup.py @@ -32,7 +32,7 @@ kwargs = { # Data files and librarries 'package_data': { 'openmc.capi': ['libopenmc.{}'.format(suffix)], - 'openmc.data': ['mass.mas12', '*.h5'] + 'openmc.data': ['mass.mas12', 'BREMX.DAT', '*.h5'] }, # Metadata @@ -52,6 +52,7 @@ kwargs = { 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', ], # Required dependencies diff --git a/src/api.F90 b/src/api.F90 index 0fa537e52..b39a2f8fa 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -11,7 +11,6 @@ module openmc_api use hdf5_interface use material_header use math - use mesh_header use message_passing use nuclide_header use initialize, only: openmc_init_f @@ -132,7 +131,7 @@ contains legendre_to_tabular_points = C_NONE n_batch_interval = 1 n_lost_particles = 0 - n_particles = 0 + n_particles = -1 n_source_points = 0 n_state_points = 0 n_tallies = 0 @@ -149,7 +148,7 @@ contains restart_run = .false. root_universe = -1 run_CE = .true. - run_mode = NONE + run_mode = -1 satisfy_triggers = .false. call openmc_set_seed(DEFAULT_SEED) source_latest = .false. @@ -305,6 +304,9 @@ contains interface subroutine free_memory_source() bind(C) end subroutine + + subroutine free_memory_mesh() bind(C) + end subroutine free_memory_mesh end interface call free_memory_geometry() diff --git a/src/bank_header.F90 b/src/bank_header.F90 index 2a998740e..c39bf8b78 100644 --- a/src/bank_header.F90 +++ b/src/bank_header.F90 @@ -28,7 +28,7 @@ module bank_header type(Bank), allocatable, target :: master_fission_bank(:) #endif - integer(8) :: n_bank ! # of sites in fission bank + integer(C_INT64_T), bind(C) :: n_bank ! # of sites in fission bank !$omp threadprivate(fission_bank, n_bank) @@ -71,4 +71,20 @@ contains end if end function openmc_source_bank + function openmc_fission_bank(ptr, n) result(err) bind(C) + ! Return a pointer to the source bank + type(C_PTR), intent(out) :: ptr + integer(C_INT64_T), intent(out) :: n + integer(C_INT) :: err + + if (.not. allocated(fission_bank)) then + err = E_ALLOCATE + call set_errmsg("Fission bank has not been allocated.") + else + err = 0 + ptr = C_LOC(fission_bank) + n = size(fission_bank) + end if + end function openmc_fission_bank + end module bank_header diff --git a/src/cell.cpp b/src/cell.cpp index c0eaa3ebf..e579b9db4 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -620,7 +620,7 @@ read_cells(pugi::xml_node* node) universes.shrink_to_fit(); // Allocate the cell overlap count if necessary. - if (openmc_check_overlaps) overlap_check_count.resize(n_cells, 0); + if (settings::check_overlaps) overlap_check_count.resize(n_cells, 0); } //============================================================================== diff --git a/src/cmfd_data.F90 b/src/cmfd_data.F90 index ce4825426..ec07a4d04 100644 --- a/src/cmfd_data.F90 +++ b/src/cmfd_data.F90 @@ -80,7 +80,7 @@ contains integer :: i_mesh ! flattend index for mesh logical :: energy_filters! energy filters present real(8) :: flux ! temp variable for flux - type(RegularMesh), pointer :: m ! pointer for mesh object + type(RegularMesh) :: m ! pointer for mesh object ! Extract spatial and energy indices from object nx = cmfd % indices(1) @@ -99,7 +99,7 @@ contains select type(filt => filters(i_filter_mesh) % obj) type is (MeshFilter) - m => meshes(filt % mesh) + m = meshes(filt % mesh) end select ! Set mesh widths @@ -354,9 +354,6 @@ contains ! Normalize openmc source distribution cmfd % openmc_src = cmfd % openmc_src/sum(cmfd % openmc_src)*cmfd%norm - ! Nullify all pointers - if (associated(m)) nullify(m) - end subroutine compute_xs !=============================================================================== diff --git a/src/cmfd_execute.F90 b/src/cmfd_execute.F90 index 7fe153d27..18f5ebec1 100644 --- a/src/cmfd_execute.F90 +++ b/src/cmfd_execute.F90 @@ -214,8 +214,6 @@ contains use bank_header, only: source_bank use constants, only: ZERO, ONE use error, only: warning, fatal_error - use mesh_header, only: RegularMesh - use mesh, only: count_bank_sites use message_passing use string, only: to_str @@ -224,7 +222,7 @@ contains integer :: nx ! maximum number of cells in x direction integer :: ny ! maximum number of cells in y direction integer :: nz ! maximum number of cells in z direction - integer :: ng ! maximum number of energy groups + integer(C_INT) :: ng ! maximum number of energy groups integer :: i ! iteration counter integer :: g ! index for group integer :: ijk(3) ! spatial bin location @@ -232,12 +230,22 @@ contains integer :: mesh_bin ! mesh bin of soruce particle integer :: n_groups ! number of energy groups real(8) :: norm ! normalization factor - logical :: outside ! any source sites outside mesh + logical(C_BOOL) :: outside ! any source sites outside mesh logical :: in_mesh ! source site is inside mesh #ifdef OPENMC_MPI integer :: mpi_err #endif + interface + subroutine cmfd_populate_sourcecounts(ng, energies, source_counts, outside) bind(C) + import C_INT, C_DOUBLE, C_BOOL + integer(C_INT), value :: ng + real(C_DOUBLE), intent(in) :: energies + real(C_DOUBLE), intent(out) :: source_counts + logical(C_BOOL), intent(out) :: outside + end subroutine + end interface + ! Get maximum of spatial and group indices nx = cmfd % indices(1) ny = cmfd % indices(2) @@ -261,8 +269,8 @@ contains cmfd%weightfactors = ONE ! Count bank sites in mesh and reverse due to egrid structure - call count_bank_sites(cmfd_mesh, source_bank, cmfd%sourcecounts, & - cmfd % egrid, sites_outside=outside, size_bank=work) + call cmfd_populate_sourcecounts(ng + 1, cmfd % egrid(1), & + cmfd % sourcecounts(1,1), outside) ! Check for sites outside of the mesh if (master .and. outside) then diff --git a/src/cmfd_execute.cpp b/src/cmfd_execute.cpp new file mode 100644 index 000000000..7774cfe1d --- /dev/null +++ b/src/cmfd_execute.cpp @@ -0,0 +1,34 @@ +#include // for copy +#include +#include + +#include "xtensor/xarray.hpp" +#include "xtensor/xio.hpp" + +#include "openmc/capi.h" +#include "openmc/mesh.h" + +namespace openmc { + +extern "C" int index_cmfd_mesh; + +extern "C" void +cmfd_populate_sourcecounts(int n_energy, const double* energies, + double* source_counts, bool* outside) +{ + // Get pointer to source bank + Bank* source_bank; + int64_t n; + openmc_source_bank(&source_bank, &n); + + // Get source counts in each mesh bin / energy bin + auto& m = meshes.at(index_cmfd_mesh); + xt::xarray counts = m->count_sites(openmc_work, source_bank, n_energy, energies, outside); + + std::cout << counts << "\n"; + + // Copy data from the xarray into the source counts array + std::copy(counts.begin(), counts.end(), source_counts); +} + +} // namespace openmc diff --git a/src/cmfd_header.F90 b/src/cmfd_header.F90 index 2e6162b49..e5e5cc423 100644 --- a/src/cmfd_header.F90 +++ b/src/cmfd_header.F90 @@ -1,5 +1,7 @@ module cmfd_header + use, intrinsic :: ISO_C_BINDING + use constants, only: CMFD_NOACCEL, ZERO, ONE use mesh_header, only: RegularMesh use set_header, only: SetInt @@ -24,7 +26,7 @@ module cmfd_header integer :: mat_dim = CMFD_NOACCEL ! Energy grid - real(8), allocatable :: egrid(:) + real(C_DOUBLE), allocatable :: egrid(:) ! Cross sections real(8), allocatable :: totalxs(:,:,:,:) @@ -53,7 +55,7 @@ module cmfd_header real(8), allocatable :: openmc_src(:,:,:,:) ! Source sites in each mesh box - real(8), allocatable :: sourcecounts(:,:) + real(C_DOUBLE), allocatable :: sourcecounts(:,:) ! Weight adjustment factors real(8), allocatable :: weightfactors(:,:,:,:) @@ -95,7 +97,8 @@ module cmfd_header ! Main object type(cmfd_type), public :: cmfd - type(RegularMesh), public, pointer :: cmfd_mesh => null() + integer(C_INT), public, bind(C) :: index_cmfd_mesh + type(RegularMesh), public :: cmfd_mesh ! Pointers for different tallies type(TallyContainer), public, pointer :: cmfd_tallies(:) => null() diff --git a/src/cmfd_input.F90 b/src/cmfd_input.F90 index de73bc03f..d1e0f6a9a 100644 --- a/src/cmfd_input.F90 +++ b/src/cmfd_input.F90 @@ -3,7 +3,7 @@ module cmfd_input use, intrinsic :: ISO_C_BINDING use cmfd_header - use mesh_header, only: mesh_dict + use mesh_header use mgxs_interface, only: energy_bins, num_energy_groups use tally use tally_header @@ -241,7 +241,7 @@ contains use constants, only: MAX_LINE_LEN use error, only: fatal_error, warning - use mesh_header, only: RegularMesh, openmc_extend_meshes + use mesh_header use string use tally, only: openmc_tally_allocate use tally_header, only: openmc_extend_tallies @@ -263,115 +263,23 @@ contains integer :: i_filt ! index in filters array integer :: filt_id integer :: tally_id - integer :: iarray3(3) ! temp integer array - real(8) :: rarray3(3) ! temp double array real(C_DOUBLE), allocatable :: energies(:) - type(RegularMesh), pointer :: m type(XMLNode) :: node_mesh - err = openmc_extend_meshes(1, i_start) + ! Read CMFD mesh + call read_meshes(root % ptr) - ! Allocate mesh - cmfd_mesh => meshes(i_start) - m => meshes(i_start) + ! Get index of cmfd mesh and set ID + i_start = n_meshes() - 1 + err = openmc_mesh_set_id(i_start, i_start) - ! Set mesh id - m % id = i_start - - ! Set mesh type to rectangular - m % type = MESH_REGULAR + ! Save reference to CMFD mesh + index_cmfd_mesh = i_start + cmfd_mesh = meshes(i_start) ! Get pointer to mesh XML node node_mesh = root % child("mesh") - ! Determine number of dimensions for mesh - n = node_word_count(node_mesh, "dimension") - if (n /= 2 .and. n /= 3) then - call fatal_error("Mesh must be two or three dimensions.") - end if - m % n_dimension = n - - ! Allocate attribute arrays - allocate(m % dimension(n)) - allocate(m % lower_left(n)) - allocate(m % width(n)) - allocate(m % upper_right(n)) - - ! Check that dimensions are all greater than zero - call get_node_array(node_mesh, "dimension", iarray3(1:n)) - if (any(iarray3(1:n) <= 0)) then - call fatal_error("All entries on the element for a tally mesh& - & must be positive.") - end if - - ! Read dimensions in each direction - m % dimension = iarray3(1:n) - - ! Read mesh lower-left corner location - if (m % n_dimension /= node_word_count(node_mesh, "lower_left")) then - call fatal_error("Number of entries on must be the same as & - &the number of entries on .") - end if - call get_node_array(node_mesh, "lower_left", m % lower_left) - - ! Make sure both upper-right or width were specified - if (check_for_node(node_mesh, "upper_right") .and. & - check_for_node(node_mesh, "width")) then - call fatal_error("Cannot specify both and on a & - &tally mesh.") - end if - - ! Make sure either upper-right or width was specified - if (.not.check_for_node(node_mesh, "upper_right") .and. & - .not.check_for_node(node_mesh, "width")) then - call fatal_error("Must specify either and on a & - &tally mesh.") - end if - - if (check_for_node(node_mesh, "width")) then - ! Check to ensure width has same dimensions - if (node_word_count(node_mesh, "width") /= & - node_word_count(node_mesh, "lower_left")) then - call fatal_error("Number of entries on must be the same as the & - &number of entries on .") - end if - - ! Check for negative widths - call get_node_array(node_mesh, "width", rarray3(1:n)) - if (any(rarray3(1:n) < ZERO)) then - call fatal_error("Cannot have a negative on a tally mesh.") - end if - - ! Set width and upper right coordinate - m % width = rarray3(1:n) - m % upper_right = m % lower_left + m % dimension * m % width - - elseif (check_for_node(node_mesh, "upper_right")) then - ! Check to ensure width has same dimensions - if (node_word_count(node_mesh, "upper_right") /= & - node_word_count(node_mesh, "lower_left")) then - call fatal_error("Number of entries on must be the same & - &as the number of entries on .") - end if - - ! Check that upper-right is above lower-left - call get_node_array(node_mesh, "upper_right", rarray3(1:n)) - if (any(rarray3(1:n) < m % lower_left)) then - call fatal_error("The coordinates must be greater than & - &the coordinates on a tally mesh.") - end if - - ! Set upper right coordinate and width - m % upper_right = rarray3(1:n) - m % width = (m % upper_right - m % lower_left) / real(m % dimension, 8) - end if - - ! Set volume fraction - m % volume_frac = ONE/real(product(m % dimension),8) - - ! Add mesh to dictionary - call mesh_dict % set(m % id, i_start) - ! Determine number of filters energy_filters = check_for_node(node_mesh, "energy") n = merge(5, 3, energy_filters) diff --git a/src/eigenvalue.F90 b/src/eigenvalue.F90 index d47bc4dab..bd2a20b41 100644 --- a/src/eigenvalue.F90 +++ b/src/eigenvalue.F90 @@ -6,8 +6,6 @@ module eigenvalue use constants, only: ZERO use error, only: fatal_error, warning use math, only: t_percentile - use mesh, only: count_bank_sites - use mesh_header, only: RegularMesh, meshes use message_passing use random_lcg, only: prn, set_particle_seed, advance_prn_seed use settings @@ -294,46 +292,6 @@ contains end subroutine synchronize_bank -!=============================================================================== -! SHANNON_ENTROPY calculates the Shannon entropy of the fission source -! distribution to assess source convergence -!=============================================================================== - - subroutine shannon_entropy() - - integer :: i ! index for mesh elements - real(8) :: entropy_gen ! entropy at this generation - logical :: sites_outside ! were there sites outside entropy box? - - associate (m => meshes(index_entropy_mesh)) - ! count number of fission sites over mesh - call count_bank_sites(m, fission_bank, entropy_p, & - size_bank=n_bank, sites_outside=sites_outside) - - ! display warning message if there were sites outside entropy box - if (sites_outside) then - if (master) call warning("Fission source site(s) outside of entropy box.") - end if - - ! sum values to obtain shannon entropy - if (master) then - ! Normalize to total weight of bank sites - entropy_p = entropy_p / sum(entropy_p) - - entropy_gen = ZERO - do i = 1, size(entropy_p, 2) - if (entropy_p(1,i) > ZERO) then - entropy_gen = entropy_gen - & - entropy_p(1,i) * log(entropy_p(1,i))/log(TWO) - end if - end do - - ! Add value to vector - call entropy % push_back(entropy_gen) - end if - end associate - end subroutine shannon_entropy - !=============================================================================== ! CALCULATE_GENERATION_KEFF collects the single-processor tracklength k's onto ! the master processor and normalizes them. This should work whether or not the @@ -577,61 +535,6 @@ contains end function openmc_get_keff -!=============================================================================== -! COUNT_SOURCE_FOR_UFS determines the source fraction in each UFS mesh cell and -! reweights the source bank so that the sum of the weights is equal to -! n_particles. The 'source_frac' variable is used later to bias the production -! of fission sites -!=============================================================================== - - subroutine count_source_for_ufs() - - real(8) :: total ! total weight in source bank - logical :: sites_outside ! were there sites outside the ufs mesh? -#ifdef OPENMC_MPI - integer :: n ! total number of ufs mesh cells - integer :: mpi_err ! MPI error code -#endif - - associate (m => meshes(index_ufs_mesh)) - - if (current_batch == 1 .and. current_gen == 1) then - ! On the first generation, just assume that the source is already evenly - ! distributed so that effectively the production of fission sites is not - ! biased - - source_frac = m % volume_frac - - else - ! count number of source sites in each ufs mesh cell - call count_bank_sites(m, source_bank, source_frac, & - sites_outside=sites_outside, size_bank=work) - - ! Check for sites outside of the mesh - if (master .and. sites_outside) then - call fatal_error("Source sites outside of the UFS mesh!") - end if - -#ifdef OPENMC_MPI - ! Send source fraction to all processors - n = product(m % dimension) - call MPI_BCAST(source_frac, n, MPI_REAL8, 0, mpi_intracomm, mpi_err) -#endif - - ! Normalize to total weight to get fraction of source in each cell - total = sum(source_frac) - source_frac = source_frac / total - - ! Since the total starting weight is not equal to n_particles, we need to - ! renormalize the weight of the source sites - - source_bank % wgt = source_bank % wgt * n_particles / total - end if - - end associate - - end subroutine count_source_for_ufs - #ifdef _OPENMP !=============================================================================== ! JOIN_BANK_FROM_THREADS joins threadprivate fission banks into a single fission diff --git a/src/eigenvalue.cpp b/src/eigenvalue.cpp new file mode 100644 index 000000000..d4f629e06 --- /dev/null +++ b/src/eigenvalue.cpp @@ -0,0 +1,155 @@ +#include "openmc/eigenvalue.h" + +#include "xtensor/xmath.hpp" +#include "xtensor/xtensor.hpp" +#include "xtensor/xview.hpp" + +#include "openmc/capi.h" +#include "openmc/error.h" +#include "openmc/hdf5_interface.h" +#include "openmc/mesh.h" +#include "openmc/message_passing.h" +#include "openmc/settings.h" +#include "openmc/simulation.h" + +namespace openmc { + +//============================================================================== +// Global variables +//============================================================================== + +std::vector entropy; +xt::xtensor source_frac; + +//============================================================================== +// Non-member functions +//============================================================================== + +void shannon_entropy() +{ + // Get pointer to entropy mesh + auto& m = meshes[settings::index_entropy_mesh]; + + // Get pointer to fission bank + Bank* fission_bank; + int64_t n; + openmc_fission_bank(&fission_bank, &n); + + // Get source weight in each mesh bin + bool sites_outside; + xt::xtensor p = m->count_sites( + n_bank, fission_bank, 0, nullptr, &sites_outside); + + // display warning message if there were sites outside entropy box + if (sites_outside) { + if (mpi::master) warning("Fission source site(s) outside of entropy box."); + } + + // sum values to obtain shannon entropy + if (mpi::master) { + // Normalize to total weight of bank sites + p /= xt::sum(p); + + double H = 0.0; + for (auto p_i : p) { + if (p_i > 0.0) { + H -= p_i * std::log(p_i)/std::log(2.0); + } + } + + // Add value to vector + entropy.push_back(H); + } +} + +void ufs_count_sites() +{ + auto &m = meshes[settings::index_ufs_mesh]; + + if (openmc_current_batch == 1 && openmc_current_gen == 1) { + // On the first generation, just assume that the source is already evenly + // distributed so that effectively the production of fission sites is not + // biased + + auto s = xt::view(source_frac, xt::all()); + s = m->volume_frac_; + + } else { + // Get pointer to source bank + Bank* source_bank; + int64_t n; + openmc_source_bank(&source_bank, &n); + + // count number of source sites in each ufs mesh cell + bool sites_outside; + source_frac = m->count_sites(openmc_work, source_bank, 0, nullptr, + &sites_outside); + + // Check for sites outside of the mesh + if (mpi::master && sites_outside) { + fatal_error("Source sites outside of the UFS mesh!"); + } + +#ifdef OPENMC_MPI + // Send source fraction to all processors + int n_bins = xt::prod(m->shape_)(); + MPI_Bcast(source_frac.data(), n_bins, MPI_DOUBLE, 0, mpi::intracomm); +#endif + + // Normalize to total weight to get fraction of source in each cell + double total = xt::sum(source_frac)(); + source_frac /= total; + + // Since the total starting weight is not equal to n_particles, we need to + // renormalize the weight of the source sites + for (int i = 0; i < openmc_work; ++i) { + source_bank[i].wgt *= settings::n_particles / total; + } + } +} + +double ufs_get_weight(const Particle* p) +{ + auto& m = meshes[settings::index_ufs_mesh]; + + // Determine indices on ufs mesh for current location + // TODO: off by one + int mesh_bin = m->get_bin({p->coord[0].xyz}) - 1; + if (mesh_bin < 0) { + p->write_restart(); + fatal_error("Source site outside UFS mesh!"); + } + + if (source_frac(mesh_bin) != 0.0) { + return m->volume_frac_ / source_frac(mesh_bin); + } else { + return 1.0; + } +} + +extern "C" void entropy_to_hdf5(hid_t group) +{ + if (settings::entropy_on) { + write_dataset(group, "entropy", entropy); + } +} + +extern "C" void entropy_from_hdf5(hid_t group) +{ + if (settings::entropy_on) { + read_dataset(group, "entropy", entropy); + } +} + +extern "C" double entropy_c(int i) +{ + return entropy.at(i - 1); +} + +extern "C" double entropy_clear() +{ + entropy.clear(); +} + + +} // namespace openmc diff --git a/src/geometry.cpp b/src/geometry.cpp index 2c83e3836..857dbd32e 100644 --- a/src/geometry.cpp +++ b/src/geometry.cpp @@ -7,6 +7,7 @@ #include "openmc/constants.h" #include "openmc/error.h" #include "openmc/lattice.h" +#include "openmc/settings.h" #include "openmc/simulation.h" #include "openmc/surface.h" @@ -90,7 +91,7 @@ find_cell(Particle* p, int search_surf) { if (cells[i_cell]->contains(r, u, surf)) { p->coord[p->n_coord-1].cell = i_cell; - if (openmc_verbosity >= 10 || openmc_trace) { + if (settings::verbosity >= 10 || openmc_trace) { std::stringstream msg; msg << " Entering cell " << cells[i_cell]->id_; write_message(msg, 1); @@ -243,6 +244,8 @@ find_cell(Particle* p, int search_surf) { return find_cell(p, 0); } } + + return found; } //============================================================================== @@ -252,7 +255,7 @@ cross_lattice(Particle* p, int lattice_translation[3]) { Lattice& lat {*lattices[p->coord[p->n_coord-1].lattice-1]}; - if (openmc_verbosity >= 10 || openmc_trace) { + if (settings::verbosity >= 10 || openmc_trace) { std::stringstream msg; msg << " Crossing lattice " << lat.id_ << ". Current position (" << p->coord[p->n_coord-1].lattice_x << "," diff --git a/src/geometry_aux.cpp b/src/geometry_aux.cpp index a30479012..273aad444 100644 --- a/src/geometry_aux.cpp +++ b/src/geometry_aux.cpp @@ -100,7 +100,8 @@ assign_temperatures() c->sqrtkT_.push_back(std::sqrt(K_BOLTZMANN * T)); } else { // Use the global default temperature. - c->sqrtkT_.push_back(std::sqrt(K_BOLTZMANN * temperature_default)); + c->sqrtkT_.push_back(std::sqrt(K_BOLTZMANN * + settings::temperature_default)); } } } diff --git a/src/hdf5_interface.cpp b/src/hdf5_interface.cpp index 5056e5f78..4badc0441 100644 --- a/src/hdf5_interface.cpp +++ b/src/hdf5_interface.cpp @@ -5,13 +5,15 @@ #include #include +#include "xtensor/xtensor.hpp" +#include "xtensor/xarray.hpp" + #include "hdf5.h" #include "hdf5_hl.h" #ifdef OPENMC_MPI #include "mpi.h" #include "openmc/message_passing.h" #endif -#include "openmc/error.h" namespace openmc { @@ -532,172 +534,6 @@ read_complex(hid_t obj_id, const char* name, std::complex* buffer, bool } -void -read_nd_vector(hid_t obj_id, const char* name, std::vector& result, - bool must_have) -{ - if (object_exists(obj_id, name)) { - read_double(obj_id, name, result.data(), true); - } else if (must_have) { - fatal_error(std::string("Must provide " + std::string(name) + "!")); - } -} - - -void -read_nd_vector(hid_t obj_id, const char* name, - std::vector >& result, bool must_have) -{ - if (object_exists(obj_id, name)) { - int dim1 = result.size(); - int dim2 = result[0].size(); - double temp_arr[dim1 * dim2]; - read_double(obj_id, name, temp_arr, true); - - int temp_idx = 0; - for (int i = 0; i < dim1; i++) { - for (int j = 0; j < dim2; j++) { - result[i][j] = temp_arr[temp_idx++]; - } - } - } else if (must_have) { - fatal_error(std::string("Must provide " + std::string(name) + "!")); - } -} - - -void -read_nd_vector(hid_t obj_id, const char* name, - std::vector >& result, bool must_have) -{ - if (object_exists(obj_id, name)) { - int dim1 = result.size(); - int dim2 = result[0].size(); - int temp_arr[dim1 * dim2]; - read_int(obj_id, name, temp_arr, true); - - int temp_idx = 0; - for (int i = 0; i < dim1; i++) { - for (int j = 0; j < dim2; j++) { - result[i][j] = temp_arr[temp_idx++]; - } - } - } else if (must_have) { - fatal_error(std::string("Must provide " + std::string(name) + "!")); - } -} - - -void -read_nd_vector(hid_t obj_id, const char* name, - std::vector > >& result, - bool must_have) -{ - if (object_exists(obj_id, name)) { - int dim1 = result.size(); - int dim2 = result[0].size(); - int dim3 = result[0][0].size(); - double temp_arr[dim1 * dim2 * dim3]; - read_double(obj_id, name, temp_arr, true); - - int temp_idx = 0; - for (int i = 0; i < dim1; i++) { - for (int j = 0; j < dim2; j++) { - for (int k = 0; k < dim3; k++) { - result[i][j][k] = temp_arr[temp_idx++]; - } - } - } - } else if (must_have) { - fatal_error(std::string("Must provide " + std::string(name) + "!")); - } -} - -void -read_nd_vector(hid_t obj_id, const char* name, - std::vector > >& result, - bool must_have) -{ - if (object_exists(obj_id, name)) { - int dim1 = result.size(); - int dim2 = result[0].size(); - int dim3 = result[0][0].size(); - int temp_arr[dim1 * dim2 * dim3]; - read_int(obj_id, name, temp_arr, true); - - int temp_idx = 0; - for (int i = 0; i < dim1; i++) { - for (int j = 0; j < dim2; j++) { - for (int k = 0; k < dim3; k++) { - result[i][j][k] = temp_arr[temp_idx++]; - } - } - } - } else if (must_have) { - fatal_error(std::string("Must provide " + std::string(name) + "!")); - } -} - -void -read_nd_vector(hid_t obj_id, const char* name, - std::vector > > >& result, - bool must_have) -{ - if (object_exists(obj_id, name)) { - int dim1 = result.size(); - int dim2 = result[0].size(); - int dim3 = result[0][0].size(); - int dim4 = result[0][0][0].size(); - double temp_arr[dim1 * dim2 * dim3 * dim4]; - read_double(obj_id, name, temp_arr, true); - - int temp_idx = 0; - for (int i = 0; i < dim1; i++) { - for (int j = 0; j < dim2; j++) { - for (int k = 0; k < dim3; k++) { - for (int l = 0; l < dim4; l++) { - result[i][j][k][l] = temp_arr[temp_idx++]; - } - } - } - } - } else if (must_have) { - fatal_error(std::string("Must provide " + std::string(name) + "!")); - } -} - -void -read_nd_vector(hid_t obj_id, const char* name, - std::vector > > > >& result, - bool must_have) -{ - if (object_exists(obj_id, name)) { - int dim1 = result.size(); - int dim2 = result[0].size(); - int dim3 = result[0][0].size(); - int dim4 = result[0][0][0].size(); - int dim5 = result[0][0][0][0].size(); - double temp_arr[dim1 * dim2 * dim3 * dim4 * dim5]; - read_double(obj_id, name, temp_arr, true); - - int temp_idx = 0; - for (int i = 0; i < dim1; i++) { - for (int j = 0; j < dim2; j++) { - for (int k = 0; k < dim3; k++) { - for (int l = 0; l < dim4; l++) { - for (int m = 0; m < dim5; m++) { - result[i][j][k][l][m] = temp_arr[temp_idx++]; - } - } - } - } - } - } else if (must_have) { - fatal_error(std::string("Must provide " + std::string(name) + "!")); - } -} - - void read_tally_results(hid_t group_id, hsize_t n_filter, hsize_t n_score, double* results) { diff --git a/src/initialize.F90 b/src/initialize.F90 index d5ce32b31..0dbaa83bf 100644 --- a/src/initialize.F90 +++ b/src/initialize.F90 @@ -17,10 +17,28 @@ module initialize implicit none - type(C_PTR), bind(C) :: openmc_path_input - type(C_PTR), bind(C) :: openmc_path_statepoint - type(C_PTR), bind(C) :: openmc_path_sourcepoint - type(C_PTR), bind(C) :: openmc_path_particle_restart + interface + function openmc_path_input() result(ptr) bind(C) + import C_PTR + type(C_PTR) :: ptr + end function + function openmc_path_output() result(ptr) bind(C) + import C_PTR + type(C_PTR) :: ptr + end function + function openmc_path_particle_restart() result(ptr) bind(C) + import C_PTR + type(C_PTR) :: ptr + end function + function openmc_path_statepoint() result(ptr) bind(C) + import C_PTR + type(C_PTR) :: ptr + end function + function openmc_path_sourcepoint() result(ptr) bind(C) + import C_PTR + type(C_PTR) :: ptr + end function + end interface contains @@ -164,29 +182,24 @@ contains end function is_null end interface - if (.not. is_null(openmc_path_input)) then - call c_f_pointer(openmc_path_input, string, [255]) + if (.not. is_null(openmc_path_input())) then + call c_f_pointer(openmc_path_input(), string, [255]) path_input = to_f_string(string) else path_input = '' end if - if (.not. is_null(openmc_path_statepoint)) then - call c_f_pointer(openmc_path_statepoint, string, [255]) + if (.not. is_null(openmc_path_statepoint())) then + call c_f_pointer(openmc_path_statepoint(), string, [255]) path_state_point = to_f_string(string) end if - if (.not. is_null(openmc_path_sourcepoint)) then - call c_f_pointer(openmc_path_sourcepoint, string, [255]) + if (.not. is_null(openmc_path_sourcepoint())) then + call c_f_pointer(openmc_path_sourcepoint(), string, [255]) path_source_point = to_f_string(string) end if - if (.not. is_null(openmc_path_particle_restart)) then - call c_f_pointer(openmc_path_particle_restart, string, [255]) + if (.not. is_null(openmc_path_particle_restart())) then + call c_f_pointer(openmc_path_particle_restart(), string, [255]) path_particle_restart = to_f_string(string) end if - - ! Add slash at end of directory if it isn't there - if (len_trim(path_input) > 0 .and. .not. ends_with(path_input, "/")) then - path_input = trim(path_input) // "/" - end if end subroutine read_command_line end module initialize diff --git a/src/initialize.cpp b/src/initialize.cpp index 21ab93b72..19fc82c85 100644 --- a/src/initialize.cpp +++ b/src/initialize.cpp @@ -10,10 +10,12 @@ #endif #include "openmc/capi.h" +#include "openmc/constants.h" #include "openmc/error.h" #include "openmc/hdf5_interface.h" #include "openmc/message_passing.h" #include "openmc/settings.h" +#include "openmc/string_utils.h" // data/functions from Fortran side extern "C" void print_usage(); @@ -59,7 +61,7 @@ namespace openmc { #ifdef OPENMC_MPI void initialize_mpi(MPI_Comm intracomm) { - openmc::mpi::intracomm = intracomm; + mpi::intracomm = intracomm; // Initialize MPI int flag; @@ -67,13 +69,13 @@ void initialize_mpi(MPI_Comm intracomm) if (!flag) MPI_Init(nullptr, nullptr); // Determine number of processes and rank for each - MPI_Comm_size(intracomm, &openmc::mpi::n_procs); - MPI_Comm_rank(intracomm, &openmc::mpi::rank); + MPI_Comm_size(intracomm, &mpi::n_procs); + MPI_Comm_rank(intracomm, &mpi::rank); // Set variable for Fortran side - openmc_n_procs = openmc::mpi::n_procs; - openmc_rank = openmc::mpi::rank; - openmc_master = (openmc::mpi::rank == 0); + openmc_n_procs = mpi::n_procs; + openmc_rank = mpi::rank; + openmc_master = mpi::master = (mpi::rank == 0); // Create bank datatype Bank b; @@ -86,19 +88,12 @@ void initialize_mpi(MPI_Comm intracomm) }; int blocks[] {1, 3, 3, 1, 1}; MPI_Datatype types[] {MPI_DOUBLE, MPI_DOUBLE, MPI_DOUBLE, MPI_DOUBLE, MPI_INT}; - MPI_Type_create_struct(5, blocks, disp, types, &openmc::mpi::bank); - MPI_Type_commit(&openmc::mpi::bank); + MPI_Type_create_struct(5, blocks, disp, types, &mpi::bank); + MPI_Type_commit(&mpi::bank); } #endif // OPENMC_MPI -inline bool ends_with(std::string const& value, std::string const& ending) -{ - if (ending.size() > value.size()) return false; - return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); -} - - int parse_command_line(int argc, char* argv[]) { @@ -108,12 +103,12 @@ parse_command_line(int argc, char* argv[]) std::string arg {argv[i]}; if (arg[0] == '-') { if (arg == "-p" || arg == "--plot") { - openmc_run_mode = RUN_MODE_PLOTTING; - openmc_check_overlaps = true; + settings::run_mode = RUN_MODE_PLOTTING; + settings::check_overlaps = true; } else if (arg == "-n" || arg == "--particles") { i += 1; - n_particles = std::stoll(argv[i]); + settings::n_particles = std::stoll(argv[i]); } else if (arg == "-r" || arg == "--restart") { i += 1; @@ -127,11 +122,11 @@ parse_command_line(int argc, char* argv[]) // Set path and flag for type of run if (filetype == "statepoint") { - openmc_path_statepoint = argv[i]; - openmc_restart_run = true; + settings::path_statepoint = argv[i]; + settings::restart_run = true; } else if (filetype == "particle restart") { - openmc_path_particle_restart = argv[i]; - openmc_particle_restart_run = true; + settings::path_particle_restart = argv[i]; + settings::particle_restart_run = true; } else { std::stringstream msg; msg << "Unrecognized file after restart flag: " << filetype << "."; @@ -140,7 +135,7 @@ parse_command_line(int argc, char* argv[]) } // If its a restart run check for additional source file - if (openmc_restart_run && i + 1 < argc) { + if (settings::restart_run && i + 1 < argc) { // Check if it has extension we can read if (ends_with(argv[i+1], ".h5")) { @@ -156,23 +151,23 @@ parse_command_line(int argc, char* argv[]) } // It is a source file - openmc_path_sourcepoint = argv[i+1]; + settings::path_sourcepoint = argv[i+1]; i += 1; } else { // Source is in statepoint file - openmc_path_sourcepoint = openmc_path_statepoint; + settings::path_sourcepoint = settings::path_statepoint; } } else { // Source is assumed to be in statepoint file - openmc_path_sourcepoint = openmc_path_statepoint; + settings::path_sourcepoint = settings::path_statepoint; } } else if (arg == "-g" || arg == "--geometry-debug") { - openmc_check_overlaps = true; + settings::check_overlaps = true; } else if (arg == "-c" || arg == "--volume") { - openmc_run_mode = RUN_MODE_VOLUME; + settings::run_mode = RUN_MODE_VOLUME; } else if (arg == "-s" || arg == "--threads") { // Read number of threads i += 1; @@ -200,7 +195,7 @@ parse_command_line(int argc, char* argv[]) return OPENMC_E_UNASSIGNED; } else if (arg == "-t" || arg == "--track") { - openmc_write_all_tracks = true; + settings::write_all_tracks = true; } else { std::cerr << "Unknown option: " << argv[i] << '\n'; @@ -213,7 +208,14 @@ parse_command_line(int argc, char* argv[]) } // Determine directory where XML input files are - if (argc > 1 && last_flag < argc) openmc_path_input = argv[last_flag + 1]; + if (argc > 1 && last_flag < argc - 1) { + settings::path_input = std::string(argv[last_flag + 1]); + + // Add slash at end of directory if it isn't there + if (!ends_with(settings::path_input, "/")) { + settings::path_input += "/"; + } + } return 0; } diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 010e09973..7361dd85b 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -4,7 +4,7 @@ module input_xml use algorithm, only: find use cmfd_input, only: configure_cmfd - use cmfd_header, only: cmfd_mesh + use cmfd_header, only: index_cmfd_mesh use constants use dict_header, only: DictIntInt, DictCharInt, DictEntryCI use endf, only: reaction_name @@ -22,7 +22,7 @@ module input_xml use output, only: title, header, print_plot use photon_header use plot_header - use random_lcg, only: prn, openmc_set_seed + use random_lcg, only: prn use surface_header use set_header, only: SetChar use settings @@ -78,10 +78,8 @@ module input_xml type(C_PTR) :: node_ptr end subroutine read_lattices - subroutine read_settings(node_ptr) bind(C) - import C_PTR - type(C_PTR) :: node_ptr - end subroutine read_settings + subroutine read_settings_xml() bind(C) + end subroutine read_settings_xml subroutine read_materials(node_ptr) bind(C) import C_PTR @@ -183,7 +181,7 @@ contains ! Assign temperatures to cells that don't have temperatures already assigned call assign_temperatures() - ! Determine desired txemperatures for each nuclide and S(a,b) table + ! Determine desired temperatures for each nuclide and S(a,b) table call get_temperatures(nuc_temps, sab_temps) ! Check to make sure there are not too many nested coordinate levels in the @@ -202,311 +200,25 @@ contains ! for errors and placing properly-formatted data in the right data structures !=============================================================================== - subroutine read_settings_xml() + subroutine read_settings_xml_f(root_ptr) bind(C) + type(C_PTR), value :: root_ptr - character(MAX_LINE_LEN) :: temp_str integer :: i integer :: n - integer :: temp_int - integer :: temp_int_array3(3) - integer(C_INT32_T) :: i_start, i_end - integer(C_INT64_T) :: seed - integer(C_INT) :: err integer, allocatable :: temp_int_array(:) integer :: n_tracks - logical :: file_exists - character(MAX_LINE_LEN) :: filename - type(XMLDocument) :: doc type(XMLNode) :: root - type(XMLNode) :: node_mode - type(XMLNode) :: node_cutoff - type(XMLNode) :: node_entropy - type(XMLNode) :: node_ufs type(XMLNode) :: node_sp - type(XMLNode) :: node_output type(XMLNode) :: node_res_scat - type(XMLNode) :: node_trigger type(XMLNode) :: node_vol - type(XMLNode) :: node_tab_leg - type(XMLNode), allocatable :: node_mesh_list(:) type(XMLNode), allocatable :: node_vol_list(:) - ! Check if settings.xml exists - filename = trim(path_input) // "settings.xml" - inquire(FILE=filename, EXIST=file_exists) - if (.not. file_exists) then - if (run_mode /= MODE_PLOTTING) then - call fatal_error("Settings XML file '" // trim(filename) // "' does & - ¬ 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.") - else - ! The settings.xml file is optional if we just want to make a plot. - return - end if - end if + ! Get proper XMLNode type given pointer + root % ptr = root_ptr - ! Parse settings.xml file - call doc % load_file(filename) - root = doc % document_element() - - ! Read settings from C++ side - call read_settings(root % ptr) - - ! Verbosity - if (check_for_node(root, "verbosity")) then - call get_node_value(root, "verbosity", verbosity) - end if - - ! To this point, we haven't displayed any output since we didn't know what - ! the verbosity is. Now that we checked for it, show the title if necessary - if (master) then - if (verbosity >= 2) call title() - end if - call write_message("Reading settings XML file...", 5) - - ! Find if a multi-group or continuous-energy simulation is desired - if (check_for_node(root, "energy_mode")) then - call get_node_value(root, "energy_mode", temp_str) - temp_str = trim(to_lower(temp_str)) - if (temp_str == "mg" .or. temp_str == "multi-group") then - run_CE = .false. - else if (temp_str == "ce" .or. temp_str == "continuous-energy") then - run_CE = .true. - end if - end if - - ! Look for deprecated cross_sections.xml file in settings.xml - if (check_for_node(root, "cross_sections")) then - call warning("Setting cross_sections in settings.xml has been deprecated.& - & The cross_sections are now set in materials.xml and the & - &cross_sections input to materials.xml and the OPENMC_CROSS_SECTIONS& - & environment variable will take precendent over setting & - &cross_sections in settings.xml.") - call get_node_value(root, "cross_sections", path_cross_sections) - end if - - ! Look for deprecated windowed_multipole file in settings.xml - if (run_mode /= MODE_PLOTTING) then - if (check_for_node(root, "multipole_library")) then - call warning("Setting multipole_library in settings.xml has been & - &deprecated. The multipole_library is now set in materials.xml and& - & the multipole_library input to materials.xml and the & - &OPENMC_MULTIPOLE_LIBRARY environment variable will take & - &precendent over setting multipole_library in settings.xml.") - call get_node_value(root, "multipole_library", path_multipole) - end if - if (.not. ends_with(path_multipole, "/")) & - path_multipole = trim(path_multipole) // "/" - end if - - if (.not. run_CE) then - ! Scattering Treatments - if (check_for_node(root, "max_order")) then - call get_node_value(root, "max_order", max_order) - else - ! Set to default of largest int - 1, which means to use whatever is - ! contained in library. - ! This is largest int - 1 because for legendre scattering, a value of - ! 1 is added to the order; adding 1 to huge(0) gets you the largest - ! negative integer, which is not what we want. - max_order = huge(0) - 1 - end if - else - max_order = 0 - end if - - ! Check for a trigger node and get trigger information - if (check_for_node(root, "trigger")) then - node_trigger = root % child("trigger") - - ! Check if trigger(s) are to be turned on - call get_node_value(node_trigger, "active", trigger_on) - - if (trigger_on) then - if (check_for_node(node_trigger, "max_batches") )then - call get_node_value(node_trigger, "max_batches", n_max_batches) - else - call fatal_error("The max_batches must be specified with triggers") - end if - - ! Get the batch interval to check triggers - if (.not. check_for_node(node_trigger, "batch_interval"))then - pred_batches = .true. - else - call get_node_value(node_trigger, "batch_interval", temp_int) - n_batch_interval = temp_int - if (n_batch_interval <= 0) then - call fatal_error("The batch interval must be greater than zero") - end if - end if - end if - end if - - ! Check run mode if it hasn't been set from the command line - if (run_mode == NONE) then - if (check_for_node(root, "run_mode")) then - call get_node_value(root, "run_mode", temp_str) - select case (to_lower(temp_str)) - case ("eigenvalue") - run_mode = MODE_EIGENVALUE - case ("fixed source") - run_mode = MODE_FIXEDSOURCE - case ("plot") - run_mode = MODE_PLOTTING - case ("particle restart") - run_mode = MODE_PARTICLE - case ("volume") - run_mode = MODE_VOLUME - case default - call fatal_error("Unrecognized run mode: " // & - trim(temp_str) // ".") - end select - - ! Assume XML specifics , , etc. directly - node_mode = root - else - call warning(" should be specified.") - - ! Make sure that either eigenvalue or fixed source was specified - node_mode = root % child("eigenvalue") - if (node_mode % associated()) then - if (run_mode == NONE) run_mode = MODE_EIGENVALUE - else - node_mode = root % child("fixed_source") - if (node_mode % associated()) then - if (run_mode == NONE) run_mode = MODE_FIXEDSOURCE - else - call fatal_error(" or not specified.") - end if - end if - end if - end if - - if (run_mode == MODE_EIGENVALUE .or. run_mode == MODE_FIXEDSOURCE) then - ! Read run parameters - call get_run_parameters(node_mode) - - ! Check number of active batches, inactive batches, and particles - if (n_batches <= n_inactive) then - call fatal_error("Number of active batches must be greater than zero.") - elseif (n_inactive < 0) then - call fatal_error("Number of inactive batches must be non-negative.") - elseif (n_particles <= 0) then - call fatal_error("Number of particles must be greater than zero.") - end if - end if - - ! Copy random number seed if specified - if (check_for_node(root, "seed")) then - call get_node_value(root, "seed", seed) - call openmc_set_seed(seed) - end if - - ! Check for electron treatment - if (check_for_node(root, "electron_treatment")) then - call get_node_value(root, "electron_treatment", temp_str) - select case (to_lower(temp_str)) - case ("led") - electron_treatment = ELECTRON_LED - case ("ttb") - electron_treatment = ELECTRON_TTB - case default - call fatal_error("Unrecognized electron treatment: " // & - trim(temp_str) // ".") - end select - end if - - ! Check for photon transport - if (check_for_node(root, "photon_transport")) then - call get_node_value(root, "photon_transport", photon_transport) - - if (.not. run_CE .and. photon_transport) then - call fatal_error("Photon transport is not currently supported & - &in Multi-group mode") - end if - end if - - ! Number of bins for logarithmic grid - if (check_for_node(root, "log_grid_bins")) then - call get_node_value(root, "log_grid_bins", n_log_bins) - if (n_log_bins < 1) then - call fatal_error("Number of bins for logarithmic grid must be & - &greater than zero.") - end if - else - n_log_bins = 8000 - end if - - ! Number of OpenMP threads - if (check_for_node(root, "threads")) then -#ifdef _OPENMP - if (n_threads == NONE) then - call get_node_value(root, "threads", n_threads) - if (n_threads < 1) then - call fatal_error("Invalid number of threads: " // to_str(n_threads)) - end if - call omp_set_num_threads(n_threads) - end if -#else - if (master) call warning("Ignoring number of threads.") -#endif - end if - - ! ========================================================================== - ! EXTERNAL SOURCE - - ! Handled on C++ side - - ! Check if we want to write out source - if (check_for_node(root, "write_initial_source")) then - call get_node_value(root, "write_initial_source", write_initial_source) - end if - - ! Survival biasing - if (check_for_node(root, "survival_biasing")) then - call get_node_value(root, "survival_biasing", survival_biasing) - end if - - ! Probability tables - if (check_for_node(root, "ptables")) then - call get_node_value(root, "ptables", urr_ptables_on) - end if - - ! Cutoffs - if (check_for_node(root, "cutoff")) then - node_cutoff = root % child("cutoff") - if (check_for_node(node_cutoff, "weight")) then - call get_node_value(node_cutoff, "weight", weight_cutoff) - end if - if (check_for_node(node_cutoff, "weight_avg")) then - call get_node_value(node_cutoff, "weight_avg", weight_survive) - end if - if (check_for_node(node_cutoff, "energy_neutron")) then - call get_node_value(node_cutoff, "energy_neutron", energy_cutoff(1)) - elseif (check_for_node(node_cutoff, "energy")) then - call warning("The use of an cutoff is deprecated and should & - &be replaced by .") - call get_node_value(node_cutoff, "energy", energy_cutoff(1)) - end if - if (check_for_node(node_cutoff, "energy_photon")) then - call get_node_value(node_cutoff, "energy_photon", energy_cutoff(2)) - end if - if (check_for_node(node_cutoff, "energy_electron")) then - call get_node_value(node_cutoff, "energy_electron", energy_cutoff(3)) - end if - if (check_for_node(node_cutoff, "energy_positron")) then - call get_node_value(node_cutoff, "energy_positron", energy_cutoff(4)) - end if - end if - - ! Particle trace - if (check_for_node(root, "trace")) then - call get_node_array(root, "trace", temp_int_array3) - trace_batch = temp_int_array3(1) - trace_gen = temp_int_array3(2) - trace_particle = int(temp_int_array3(3), 8) + if (run_mode == MODE_EIGENVALUE) then + ! Preallocate space for keff and entropy by generation + call k_generation % reserve(n_max_batches*gen_per_batch) end if ! Particle tracks @@ -528,110 +240,6 @@ contains track_identifiers = reshape(temp_int_array, [3, n_tracks/3]) end if - ! Read meshes - call get_node_list(root, "mesh", node_mesh_list) - - ! Check for user meshes and allocate - n = size(node_mesh_list) - if (n > 0) then - err = openmc_extend_meshes(n, i_start, i_end) - end if - - do i = 1, n - associate (m => meshes(i_start + i - 1)) - ! Instantiate mesh from XML node - call m % from_xml(node_mesh_list(i)) - - ! Add mesh to dictionary - call mesh_dict % set(m % id, i_start + i - 1) - end associate - end do - - ! Shannon Entropy mesh - if (check_for_node(root, "entropy_mesh")) then - call get_node_value(root, "entropy_mesh", temp_int) - if (mesh_dict % has(temp_int)) then - index_entropy_mesh = mesh_dict % get(temp_int) - else - call fatal_error("Mesh " // to_str(temp_int) // " specified for & - &Shannon entropy does not exist.") - end if - elseif (check_for_node(root, "entropy")) then - call warning("Specifying a Shannon entropy mesh via the element & - &is deprecated. Please create a mesh using and then reference & - &it by specifying its ID in an element.") - - ! Get pointer to entropy node - node_entropy = root % child("entropy") - - err = openmc_extend_meshes(1, index_entropy_mesh) - - associate (m => meshes(index_entropy_mesh)) - ! Assign ID - m % id = 10000 - - call m % from_xml(node_entropy) - end associate - end if - - if (index_entropy_mesh > 0) then - associate(m => meshes(index_entropy_mesh)) - if (.not. allocated(m % dimension)) then - ! If the user did not specify how many mesh cells are to be used in - ! each direction, we automatically determine an appropriate number of - ! cells - m % n_dimension = 3 - allocate(m % dimension(3)) - m % dimension = ceiling((n_particles/20)**(ONE/THREE)) - - ! Calculate width - m % width = (m % upper_right - m % lower_left) / m % dimension - end if - - ! Allocate space for storing number of fission sites in each mesh cell - allocate(entropy_p(1, product(m % dimension))) - end associate - - ! Turn on Shannon entropy calculation - entropy_on = .true. - end if - - ! Uniform fission source weighting mesh - if (check_for_node(root, "ufs_mesh")) then - call get_node_value(root, "ufs_mesh", temp_int) - if (mesh_dict % has(temp_int)) then - index_ufs_mesh = mesh_dict % get(temp_int) - else - call fatal_error("Mesh " // to_str(temp_int) // " specified for & - &uniform fission site method does not exist.") - end if - elseif (check_for_node(root, "uniform_fs")) then - call warning("Specifying a UFS mesh via the element & - &is deprecated. Please create a mesh using and then reference & - &it by specifying its ID in a element.") - - ! Get pointer to ufs node - node_ufs = root % child("uniform_fs") - - err = openmc_extend_meshes(1, index_ufs_mesh) - - ! Allocate mesh object and coordinates on mesh - associate (m => meshes(index_ufs_mesh)) - ! Assign ID - m % id = 10001 - - call m % from_xml(node_ufs) - end associate - end if - - if (index_ufs_mesh > 0) then - ! Allocate array to store source fraction for UFS - allocate(source_frac(1, product(meshes(index_ufs_mesh) % dimension))) - - ! Turn on uniform fission source weighting - ufs = .true. - end if - ! Check if the user has specified to write state points if (check_for_node(root, "state_point")) then @@ -693,22 +301,9 @@ contains call sourcepoint_batch % add(statepoint_batch % get_item(i)) end do end if - - ! Check if the user has specified to write binary source file - if (check_for_node(node_sp, "separate")) then - call get_node_value(node_sp, "separate", source_separate) - end if - if (check_for_node(node_sp, "write")) then - call get_node_value(node_sp, "write", source_write) - end if - if (check_for_node(node_sp, "overwrite_latest")) then - call get_node_value(node_sp, "overwrite_latest", source_latest) - source_separate = source_latest - end if else ! If no tag was present, by default we keep source bank in ! statepoint file and write it out at statepoints intervals - source_separate = .false. n_source_points = n_state_points do i = 1, n_state_points call sourcepoint_batch % add(statepoint_batch % get_item(i)) @@ -728,91 +323,10 @@ contains end do end if - ! Check if the user has specified to not reduce tallies at the end of every - ! batch - if (check_for_node(root, "no_reduce")) then - call get_node_value(root, "no_reduce", reduce_tallies) - end if - - ! Check if the user has specified to use confidence intervals for - ! uncertainties rather than standard deviations - if (check_for_node(root, "confidence_intervals")) then - call get_node_value(root, "confidence_intervals", confidence_intervals) - end if - - ! Check for output options - if (check_for_node(root, "output")) then - - ! Get pointer to output node - node_output = root % child("output") - - ! Check for summary option - if (check_for_node(node_output, "summary")) then - call get_node_value(node_output, "summary", output_summary) - end if - - ! Check for ASCII tallies output option - if (check_for_node(node_output, "tallies")) then - call get_node_value(node_output, "tallies", output_tallies) - end if - - ! Set output directory if a path has been specified - if (check_for_node(node_output, "path")) then - call get_node_value(node_output, "path", path_output) - if (.not. ends_with(path_output, "/")) & - path_output = trim(path_output) // "/" - end if - end if - - ! Check for cmfd run - if (check_for_node(root, "run_cmfd")) then - call get_node_value(root, "run_cmfd", cmfd_run) - end if - ! Resonance scattering parameters if (check_for_node(root, "resonance_scattering")) then node_res_scat = root % child("resonance_scattering") - ! See if resonance scattering is enabled - if (check_for_node(node_res_scat, "enable")) then - call get_node_value(node_res_scat, "enable", res_scat_on) - else - res_scat_on = .true. - end if - - ! Determine what method is used - if (check_for_node(node_res_scat, "method")) then - call get_node_value(node_res_scat, "method", temp_str) - select case(to_lower(temp_str)) - case ('ares') - res_scat_method = RES_SCAT_ARES - case ('dbrc') - res_scat_method = RES_SCAT_DBRC - case ('wcm') - res_scat_method = RES_SCAT_WCM - case default - call fatal_error("Unrecognized resonance elastic scattering method: " & - // trim(temp_str) // ".") - end select - end if - - ! Minimum energy for resonance scattering - if (check_for_node(node_res_scat, "energy_min")) then - call get_node_value(node_res_scat, "energy_min", res_scat_energy_min) - end if - if (res_scat_energy_min < ZERO) then - call fatal_error("Lower resonance scattering energy bound is negative") - end if - - ! Maximum energy for resonance scattering - if (check_for_node(node_res_scat, "energy_max")) then - call get_node_value(node_res_scat, "energy_max", res_scat_energy_max) - end if - if (res_scat_energy_max < res_scat_energy_min) then - call fatal_error("Upper resonance scattering energy bound is below the & - &lower resonance scattering energy bound.") - end if - ! Get nuclides that resonance scattering should be applied to if (check_for_node(node_res_scat, "nuclides")) then n = node_word_count(node_res_scat, "nuclides") @@ -831,138 +345,7 @@ contains call volume_calcs(i) % from_xml(node_vol) end do - ! Get temperature settings - if (check_for_node(root, "temperature_default")) then - call get_node_value(root, "temperature_default", temperature_default) - end if - if (check_for_node(root, "temperature_method")) then - call get_node_value(root, "temperature_method", temp_str) - select case (to_lower(temp_str)) - case ('nearest') - temperature_method = TEMPERATURE_NEAREST - case ('interpolation') - temperature_method = TEMPERATURE_INTERPOLATION - case default - call fatal_error("Unknown temperature method: " // trim(temp_str)) - end select - end if - if (check_for_node(root, "temperature_tolerance")) then - call get_node_value(root, "temperature_tolerance", temperature_tolerance) - end if - if (check_for_node(root, "temperature_multipole")) then - call get_node_value(root, "temperature_multipole", temperature_multipole) - end if - if (check_for_node(root, "temperature_range")) then - call get_node_array(root, "temperature_range", temperature_range) - end if - - ! Check for tabular_legendre options - if (check_for_node(root, "tabular_legendre")) then - - ! Get pointer to tabular_legendre node - node_tab_leg = root % child("tabular_legendre") - - ! Check for enable option - if (check_for_node(node_tab_leg, "enable")) then - call get_node_value(node_tab_leg, "enable", legendre_to_tabular) - end if - - ! Check for the number of points - if (check_for_node(node_tab_leg, "num_points")) then - call get_node_value(node_tab_leg, "num_points", & - legendre_to_tabular_points) - if (legendre_to_tabular_points <= 1 .and. (.not. run_CE)) then - call fatal_error("The 'num_points' subelement/attribute of the & - &'tabular_legendre' element must contain a value greater than 1") - end if - end if - end if - - ! Check whether create fission sites - if (run_mode == MODE_FIXEDSOURCE) then - if (check_for_node(root, "create_fission_neutrons")) then - call get_node_value(root, "create_fission_neutrons", & - create_fission_neutrons) - end if - end if - - ! Close settings XML file - call doc % clear() - - end subroutine read_settings_xml - -!=============================================================================== -! GET_RUN_PARAMETERS -!=============================================================================== - - subroutine get_run_parameters(node_base) - type(XMLNode), intent(in) :: node_base - - character(MAX_LINE_LEN) :: temp_str - type(XMLNode) :: node_keff_trigger - - ! Check number of particles - if (.not. check_for_node(node_base, "particles")) then - call fatal_error("Need to specify number of particles.") - end if - - ! Get number of particles if it wasn't specified as a command-line argument - if (n_particles == 0) then - call get_node_value(node_base, "particles", n_particles) - end if - - ! Get number of basic batches - call get_node_value(node_base, "batches", n_batches) - if (.not. trigger_on) then - n_max_batches = n_batches - end if - n_inactive = 0 - gen_per_batch = 1 - - ! Get number of inactive batches - if (run_mode == MODE_EIGENVALUE) then - call get_node_value(node_base, "inactive", n_inactive) - if (check_for_node(node_base, "generations_per_batch")) then - call get_node_value(node_base, "generations_per_batch", gen_per_batch) - end if - - ! Preallocate space for keff and entropy by generation - call k_generation % reserve(n_max_batches*gen_per_batch) - call entropy % reserve(n_max_batches*gen_per_batch) - - ! Get the trigger information for keff - if (check_for_node(node_base, "keff_trigger")) then - node_keff_trigger = node_base % child("keff_trigger") - - if (check_for_node(node_keff_trigger, "type")) then - call get_node_value(node_keff_trigger, "type", temp_str) - temp_str = trim(to_lower(temp_str)) - - select case (temp_str) - case ('std_dev') - keff_trigger % trigger_type = STANDARD_DEVIATION - case ('variance') - keff_trigger % trigger_type = VARIANCE - case ('rel_err') - keff_trigger % trigger_type = RELATIVE_ERROR - case default - call fatal_error("Unrecognized keff trigger type " // temp_str) - end select - - else - call fatal_error("Specify keff trigger type in settings XML") - end if - - if (check_for_node(node_keff_trigger, "threshold")) then - call get_node_value(node_keff_trigger, "threshold", & - keff_trigger % threshold) - else - call fatal_error("Specify keff trigger threshold in settings XML") - end if - end if - end if - - end subroutine get_run_parameters + end subroutine read_settings_xml_f !=============================================================================== ! READ_GEOMETRY_XML reads data from a geometry.xml file and parses it, checking @@ -1773,13 +1156,11 @@ contains character(MAX_WORD_LEN), allocatable :: sarray(:) type(DictCharInt) :: trigger_scores type(TallyFilterContainer), pointer :: f - type(RegularMesh), pointer :: m type(XMLDocument) :: doc type(XMLNode) :: root type(XMLNode) :: node_tal type(XMLNode) :: node_filt type(XMLNode) :: node_trigger - type(XMLNode), allocatable :: node_mesh_list(:) type(XMLNode), allocatable :: node_tal_list(:) type(XMLNode), allocatable :: node_filt_list(:) type(XMLNode), allocatable :: node_trigger_list(:) @@ -1810,9 +1191,6 @@ contains ! ========================================================================== ! DETERMINE SIZE OF ARRAYS AND ALLOCATE - ! Get pointer list to XML - call get_node_list(root, "mesh", node_mesh_list) - ! Get pointer list to XML call get_node_list(root, "filter", node_filt_list) @@ -1828,20 +1206,7 @@ contains ! READ MESH DATA ! Check for user meshes and allocate - n = size(node_mesh_list) - if (n > 0) then - err = openmc_extend_meshes(n, i_start, i_end) - end if - - do i = 1, n - m => meshes(i_start + i - 1) - - ! Instantiate mesh from XML node - call m % from_xml(node_mesh_list(i)) - - ! Add mesh to dictionary - call mesh_dict % set(m % id, i_start + i - 1) - end do + call read_meshes(root % ptr) ! We only need the mesh info for plotting if (run_mode == MODE_PLOTTING) then @@ -2683,6 +2048,7 @@ contains integer :: i, j integer :: n_cols, col_id, n_comp, n_masks, n_meshlines integer :: meshid + integer(C_INT) :: err, idx integer, allocatable :: iarray(:) logical :: file_exists ! does plots.xml file exist? character(MAX_LINE_LEN) :: filename ! absolute path to plots.xml @@ -3005,7 +2371,7 @@ contains // trim(to_str(pl % id))) end if - pl % meshlines_mesh => meshes(index_ufs_mesh) + pl % index_meshlines_mesh = index_ufs_mesh case ('cmfd') @@ -3014,7 +2380,7 @@ contains &meshlines on plot " // trim(to_str(pl % id))) end if - pl % meshlines_mesh => cmfd_mesh + pl % index_meshlines_mesh = index_cmfd_mesh case ('entropy') @@ -3023,7 +2389,7 @@ contains // trim(to_str(pl % id))) end if - pl % meshlines_mesh => meshes(index_entropy_mesh) + pl % index_meshlines_mesh = index_entropy_mesh case ('tally') @@ -3036,17 +2402,13 @@ contains end if ! Check if the specified tally mesh exists - if (mesh_dict % has(meshid)) then - pl % meshlines_mesh => meshes(mesh_dict % get(meshid)) - if (meshes(meshid) % type /= MESH_REGULAR) then - call fatal_error("Non-rectangular mesh specified in & - &meshlines for plot " // trim(to_str(pl % id))) - end if - else + err = openmc_get_mesh_index(meshid, idx) + if (err /= 0) then call fatal_error("Could not find mesh " & // trim(to_str(meshid)) // " specified in meshlines for & &plot " // trim(to_str(pl % id))) end if + pl % index_meshlines_mesh = idx case default call fatal_error("Invalid type for meshlines on plot " & diff --git a/src/main.cpp b/src/main.cpp index 54b78d38d..f4e2d3f8e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,7 +2,9 @@ #include "mpi.h" #endif #include "openmc/capi.h" +#include "openmc/constants.h" #include "openmc/error.h" +#include "openmc/settings.h" int main(int argc, char* argv[]) { @@ -23,18 +25,18 @@ int main(int argc, char* argv[]) { } // start problem based on mode - switch (openmc_run_mode) { - case RUN_MODE_FIXEDSOURCE: - case RUN_MODE_EIGENVALUE: + switch (openmc::settings::run_mode) { + case openmc::RUN_MODE_FIXEDSOURCE: + case openmc::RUN_MODE_EIGENVALUE: err = openmc_run(); break; - case RUN_MODE_PLOTTING: + case openmc::RUN_MODE_PLOTTING: err = openmc_plot_geometry(); break; - case RUN_MODE_PARTICLE: + case openmc::RUN_MODE_PARTICLE: if (openmc_master) err = openmc_particle_restart(); break; - case RUN_MODE_VOLUME: + case openmc::RUN_MODE_VOLUME: err = openmc_calculate_volumes(); break; } diff --git a/src/mesh.F90 b/src/mesh.F90 deleted file mode 100644 index 790dc7d88..000000000 --- a/src/mesh.F90 +++ /dev/null @@ -1,106 +0,0 @@ -module mesh - - use algorithm, only: binary_search - use bank_header, only: bank - use constants - use mesh_header - use message_passing - - implicit none - -contains - -!=============================================================================== -! COUNT_BANK_SITES determines the number of fission bank sites in each cell of a -! given mesh as well as an optional energy group structure. This can be used for -! a variety of purposes (Shannon entropy, CMFD, uniform fission source -! weighting) -!=============================================================================== - - subroutine count_bank_sites(m, bank_array, cnt, energies, size_bank, & - sites_outside) - - type(RegularMesh), intent(in) :: m ! mesh to count sites - type(Bank), intent(in) :: bank_array(:) ! fission or source bank - real(8), intent(out) :: cnt(:,:) ! weight of sites in each - ! cell and energy group - real(8), intent(in), optional :: energies(:) ! energy grid to search - integer(8), intent(in), optional :: size_bank ! # of bank sites (on each proc) - logical, intent(inout), optional :: sites_outside ! were there sites outside mesh? - real(8), allocatable :: cnt_(:,:) - - integer :: i ! loop index for local fission sites - integer :: n_sites ! size of bank array - integer :: n ! number of energy groups / size - integer :: mesh_bin ! mesh bin - integer :: e_bin ! energy bin -#ifdef OPENMC_MPI - integer :: mpi_err ! MPI error code -#endif - logical :: outside ! was any site outside mesh? - - ! initialize variables - allocate(cnt_(size(cnt,1), size(cnt,2))) - cnt_ = ZERO - outside = .false. - - ! Set size of bank - if (present(size_bank)) then - n_sites = int(size_bank,4) - else - n_sites = size(bank_array) - end if - - ! Determine number of energies in group structure - if (present(energies)) then - n = size(energies) - 1 - else - n = 1 - end if - - ! loop over fission sites and count how many are in each mesh box - FISSION_SITES: do i = 1, n_sites - ! determine scoring bin for entropy mesh - call m % get_bin(bank_array(i) % xyz, mesh_bin) - - ! if outside mesh, skip particle - if (mesh_bin == NO_BIN_FOUND) then - outside = .true. - cycle - end if - - ! determine energy bin - if (present(energies)) then - if (bank_array(i) % E < energies(1)) then - e_bin = 1 - elseif (bank_array(i) % E > energies(n + 1)) then - e_bin = n - else - e_bin = binary_search(energies, n + 1, bank_array(i) % E) - end if - else - e_bin = 1 - end if - - ! add to appropriate mesh box - cnt_(e_bin, mesh_bin) = cnt_(e_bin, mesh_bin) + bank_array(i) % wgt - end do FISSION_SITES - -#ifdef OPENMC_MPI - ! collect values from all processors - n = size(cnt_) - call MPI_REDUCE(cnt_, cnt, n, MPI_REAL8, MPI_SUM, 0, mpi_intracomm, mpi_err) - - ! Check if there were sites outside the mesh for any processor - if (present(sites_outside)) then - call MPI_REDUCE(outside, sites_outside, 1, MPI_LOGICAL, MPI_LOR, 0, & - mpi_intracomm, mpi_err) - end if -#else - sites_outside = outside - cnt = cnt_ -#endif - - end subroutine count_bank_sites - -end module mesh diff --git a/src/mesh.cpp b/src/mesh.cpp new file mode 100644 index 000000000..c1bd830a6 --- /dev/null +++ b/src/mesh.cpp @@ -0,0 +1,976 @@ +#include "openmc/mesh.h" + +#include // for copy, min +#include // for size_t +#include // for ceil +#include + +#ifdef OPENMC_MPI +#include "mpi.h" +#endif +#include "xtensor/xbuilder.hpp" +#include "xtensor/xeval.hpp" +#include "xtensor/xmath.hpp" +#include "xtensor/xsort.hpp" +#include "xtensor/xtensor.hpp" + +#include "openmc/capi.h" +#include "openmc/constants.h" +#include "openmc/error.h" +#include "openmc/hdf5_interface.h" +#include "openmc/message_passing.h" +#include "openmc/search.h" +#include "openmc/xml_interface.h" + +namespace openmc { + +//============================================================================== +// Global variables +//============================================================================== + +std::vector> meshes; + +std::unordered_map mesh_map; + +//============================================================================== +// RegularMesh implementation +//============================================================================== + +RegularMesh::RegularMesh(pugi::xml_node node) +{ + // Copy mesh id + if (check_for_node(node, "id")) { + id_ = std::stoi(get_node_value(node, "id")); + + // Check to make sure 'id' hasn't been used + if (mesh_map.find(id_) != mesh_map.end()) { + fatal_error("Two or more meshes use the same unique ID: " + + std::to_string(id_)); + } + } + + // Read mesh type + if (check_for_node(node, "type")) { + auto temp = get_node_value(node, "type", true, true); + if (temp == "regular") { + // TODO: move elsewhere + } else { + fatal_error("Invalid mesh type: " + temp); + } + } + + // Determine number of dimensions for mesh + if (check_for_node(node, "dimension")) { + shape_ = get_node_xarray(node, "dimension"); + int n = n_dimension_ = shape_.size(); + if (n != 1 && n != 2 && n != 3) { + fatal_error("Mesh must be one, two, or three dimensions."); + } + + // Check that dimensions are all greater than zero + if (xt::any(shape_ <= 0)) { + fatal_error("All entries on the element for a tally " + "mesh must be positive."); + } + } + + // Check for lower-left coordinates + if (check_for_node(node, "lower_left")) { + // Read mesh lower-left corner location + lower_left_ = get_node_xarray(node, "lower_left"); + } else { + fatal_error("Must specify on a mesh."); + } + + if (check_for_node(node, "width")) { + // Make sure both upper-right or width were specified + if (check_for_node(node, "upper_right")) { + fatal_error("Cannot specify both and on a mesh."); + } + + width_ = get_node_xarray(node, "width"); + + // Check to ensure width has same dimensions + auto n = width_.size(); + if (n != lower_left_.size()) { + fatal_error("Number of entries on must be the same as " + "the number of entries on ."); + } + + // Check for negative widths + if (xt::any(width_ < 0.0)) { + fatal_error("Cannot have a negative on a tally mesh."); + } + + // Set width and upper right coordinate + upper_right_ = xt::eval(lower_left_ + shape_ * width_); + + } else if (check_for_node(node, "upper_right")) { + upper_right_ = get_node_xarray(node, "upper_right"); + + // Check to ensure width has same dimensions + auto n = upper_right_.size(); + if (n != lower_left_.size()) { + fatal_error("Number of entries on must be the " + "same as the number of entries on ."); + } + + // Check that upper-right is above lower-left + if (xt::any(upper_right_ < lower_left_)) { + fatal_error("The coordinates must be greater than " + "the coordinates on a tally mesh."); + } + + // Set width and upper right coordinate + width_ = xt::eval((upper_right_ - lower_left_) / shape_); + } else { + fatal_error("Must specify either and on a mesh."); + } + + if (shape_.dimension() > 0) { + if (shape_.size() != lower_left_.size()) { + fatal_error("Number of entries on must be the same " + "as the number of entries on ."); + } + + // Set volume fraction + volume_frac_ = 1.0/xt::prod(shape_)(); + } +} + +int RegularMesh::get_bin(Position r) const +{ + // Loop over the dimensions of the mesh + for (int i = 0; i < n_dimension_; ++i) { + // Check for cases where particle is outside of mesh + if (r[i] < lower_left_[i]) { + return -1; + } else if (r[i] > upper_right_[i]) { + return -1; + } + } + + // Determine indices + int ijk[n_dimension_]; + bool in_mesh; + get_indices(r, ijk, &in_mesh); + if (!in_mesh) return -1; + + // Convert indices to bin + return get_bin_from_indices(ijk); +} + +int RegularMesh::get_bin_from_indices(const int* ijk) const +{ + if (n_dimension_ == 1) { + return ijk[0]; + } else if (n_dimension_ == 2) { + return (ijk[1] - 1)*shape_[0] + ijk[0]; + } else if (n_dimension_ == 3) { + return ((ijk[2] - 1)*shape_[1] + (ijk[1] - 1))*shape_[0] + ijk[0]; + } +} + +void RegularMesh::get_indices(Position r, int* ijk, bool* in_mesh) const +{ + // Find particle in mesh + *in_mesh = true; + for (int i = 0; i < n_dimension_; ++i) { + ijk[i] = std::ceil((r[i] - lower_left_[i]) / width_[i]); + + // Check if indices are within bounds + if (ijk[i] < 1 || ijk[i] > shape_[i]) *in_mesh = false; + } +} + +void RegularMesh::get_indices_from_bin(int bin, int* ijk) const +{ + if (n_dimension_ == 1) { + ijk[0] = bin; + } else if (n_dimension_ == 2) { + ijk[0] = (bin - 1) % shape_[0] + 1; + ijk[1] = (bin - 1) / shape_[0] + 1; + } else if (n_dimension_ == 3) { + ijk[0] = (bin - 1) % shape_[0] + 1; + ijk[1] = ((bin - 1) % (shape_[0] * shape_[1])) / shape_[0] + 1; + ijk[2] = (bin - 1) / (shape_[0] * shape_[1]) + 1; + } +} + +bool RegularMesh::intersects(Position r0, Position r1) const +{ + switch(n_dimension_) { + case 1: + return intersects_1d(r0, r1); + case 2: + return intersects_2d(r0, r1); + case 3: + return intersects_3d(r0, r1); + } +} + +bool RegularMesh::intersects_1d(Position r0, Position r1) const +{ + // Copy coordinates of mesh lower_left and upper_right + double left = lower_left_[0]; + double right = upper_right_[0]; + + // Check if line intersects either left or right surface + if (r0.x < left) { + return r1.x > left; + } else if (r0.x < right) { + return r1.x < left || r1.x > right; + } else { + return r1.x < right; + } +} + +bool RegularMesh::intersects_2d(Position r0, Position r1) const +{ + // Copy coordinates of starting point + double x0 = r0.x; + double y0 = r0.y; + + // Copy coordinates of ending point + double x1 = r1.x; + double y1 = r1.y; + + // Copy coordinates of mesh lower_left + double xm0 = lower_left_[0]; + double ym0 = lower_left_[1]; + + // Copy coordinates of mesh upper_right + double xm1 = upper_right_[0]; + double ym1 = upper_right_[1]; + + // Check if line intersects left surface -- calculate the intersection point y + if ((x0 < xm0 && x1 > xm0) || (x0 > xm0 && x1 < xm0)) { + double yi = y0 + (xm0 - x0) * (y1 - y0) / (x1 - x0); + if (yi >= ym0 && yi < ym1) { + return true; + } + } + + // Check if line intersects back surface -- calculate the intersection point + // x + if ((y0 < ym0 && y1 > ym0) || (y0 > ym0 && y1 < ym0)) { + double xi = x0 + (ym0 - y0) * (x1 - x0) / (y1 - y0); + if (xi >= xm0 && xi < xm1) { + return true; + } + } + + // Check if line intersects right surface -- calculate the intersection + // point y + if ((x0 < xm1 && x1 > xm1) || (x0 > xm1 && x1 < xm1)) { + double yi = y0 + (xm1 - x0) * (y1 - y0) / (x1 - x0); + if (yi >= ym0 && yi < ym1) { + return true; + } + } + + // Check if line intersects front surface -- calculate the intersection point + // x + if ((y0 < ym1 && y1 > ym1) || (y0 > ym1 && y1 < ym1)) { + double xi = x0 + (ym1 - y0) * (x1 - x0) / (y1 - y0); + if (xi >= xm0 && xi < xm1) { + return true; + } + } + return false; +} + +bool RegularMesh::intersects_3d(Position r0, Position r1) const +{ + // Copy coordinates of starting point + double x0 = r0.x; + double y0 = r0.y; + double z0 = r0.z; + + // Copy coordinates of ending point + double x1 = r1.x; + double y1 = r1.y; + double z1 = r1.z; + + // Copy coordinates of mesh lower_left + double xm0 = lower_left_[0]; + double ym0 = lower_left_[1]; + double zm0 = lower_left_[2]; + + // Copy coordinates of mesh upper_right + double xm1 = upper_right_[0]; + double ym1 = upper_right_[1]; + double zm1 = upper_right_[2]; + + // Check if line intersects left surface -- calculate the intersection point + // (y,z) + if ((x0 < xm0 && x1 > xm0) || (x0 > xm0 && x1 < xm0)) { + double yi = y0 + (xm0 - x0) * (y1 - y0) / (x1 - x0); + double zi = z0 + (xm0 - x0) * (z1 - z0) / (x1 - x0); + if (yi >= ym0 && yi < ym1 && zi >= zm0 && zi < zm1) { + return true; + } + } + + // Check if line intersects back surface -- calculate the intersection point + // (x,z) + if ((y0 < ym0 && y1 > ym0) || (y0 > ym0 && y1 < ym0)) { + double xi = x0 + (ym0 - y0) * (x1 - x0) / (y1 - y0); + double zi = z0 + (ym0 - y0) * (z1 - z0) / (y1 - y0); + if (xi >= xm0 && xi < xm1 && zi >= zm0 && zi < zm1) { + return true; + } + } + + // Check if line intersects bottom surface -- calculate the intersection + // point (x,y) + if ((z0 < zm0 && z1 > zm0) || (z0 > zm0 && z1 < zm0)) { + double xi = x0 + (zm0 - z0) * (x1 - x0) / (z1 - z0); + double yi = y0 + (zm0 - z0) * (y1 - y0) / (z1 - z0); + if (xi >= xm0 && xi < xm1 && yi >= ym0 && yi < ym1) { + return true; + } + } + + // Check if line intersects right surface -- calculate the intersection point + // (y,z) + if ((x0 < xm1 && x1 > xm1) || (x0 > xm1 && x1 < xm1)) { + double yi = y0 + (xm1 - x0) * (y1 - y0) / (x1 - x0); + double zi = z0 + (xm1 - x0) * (z1 - z0) / (x1 - x0); + if (yi >= ym0 && yi < ym1 && zi >= zm0 && zi < zm1) { + return true; + } + } + + // Check if line intersects front surface -- calculate the intersection point + // (x,z) + if ((y0 < ym1 && y1 > ym1) || (y0 > ym1 && y1 < ym1)) { + double xi = x0 + (ym1 - y0) * (x1 - x0) / (y1 - y0); + double zi = z0 + (ym1 - y0) * (z1 - z0) / (y1 - y0); + if (xi >= xm0 && xi < xm1 && zi >= zm0 && zi < zm1) { + return true; + } + } + + // Check if line intersects top surface -- calculate the intersection point + // (x,y) + if ((z0 < zm1 && z1 > zm1) || (z0 > zm1 && z1 < zm1)) { + double xi = x0 + (zm1 - z0) * (x1 - x0) / (z1 - z0); + double yi = y0 + (zm1 - z0) * (y1 - y0) / (z1 - z0); + if (xi >= xm0 && xi < xm1 && yi >= ym0 && yi < ym1) { + return true; + } + } + return false; +} + +void RegularMesh::bins_crossed(const Particle* p, std::vector& bins, + std::vector& lengths) const +{ + constexpr int MAX_SEARCH_ITER = 100; + + // ======================================================================== + // Determine if the track intersects the tally mesh. + + // Copy the starting and ending coordinates of the particle. Offset these + // just a bit for the purposes of determining if there was an intersection + // in case the mesh surfaces coincide with lattice/geometric surfaces which + // might produce finite-precision errors. + Position last_r {p->last_xyz}; + Position r {p->coord[0].xyz}; + Direction u {p->coord[0].uvw}; + + Position r0 = last_r + TINY_BIT*u; + Position r1 = r - TINY_BIT*u; + + // Determine indices for starting and ending location. + int n = n_dimension_; + xt::xtensor ijk0 = xt::empty({n}); + bool start_in_mesh; + get_indices(r0, ijk0.data(), &start_in_mesh); + xt::xtensor ijk1 = xt::empty({n}); + bool end_in_mesh; + get_indices(r1, ijk1.data(), &end_in_mesh); + + // Check if the track intersects any part of the mesh. + if (!start_in_mesh && !end_in_mesh) { + if (!intersects(r0, r1)) return; + } + + // ======================================================================== + // Figure out which mesh cell to tally. + + // Copy the un-modified coordinates the particle direction. + r0 = last_r; + r1 = r; + + // Compute the length of the entire track. + double total_distance = (r1 - r0).norm(); + + // We are looking for the first valid mesh bin. Check to see if the + // particle starts inside the mesh. + if (!start_in_mesh) { + xt::xtensor d = xt::zeros({n}); + + // The particle does not start in the mesh. Note that we nudged the + // start and end coordinates by a TINY_BIT each so we will have + // difficulty resolving tracks that are less than 2*TINY_BIT in length. + // If the track is that short, it is also insignificant so we can + // safely ignore it in the tallies. + if (total_distance < 2*TINY_BIT) return; + + // The particle does not start in the mesh so keep iterating the ijk0 + // indices to cross the nearest mesh surface until we've found a valid + // bin. MAX_SEARCH_ITER prevents an infinite loop. + int search_iter = 0; + int j; + while (xt::any(ijk0 < 1) || xt::any(ijk0 > shape_)) { + if (search_iter == MAX_SEARCH_ITER) { + warning("Failed to find a mesh intersection on a tally mesh filter."); + return; + } + + for (j = 0; j < n; ++j) { + if (std::fabs(u[j]) < FP_PRECISION) { + d(j) = INFTY; + } else if (u[j] > 0.0) { + double xyz_cross = lower_left_[j] + ijk0(j) * width_[j]; + d(j) = (xyz_cross - r0[j]) / u[j]; + } else { + double xyz_cross = lower_left_[j] + (ijk0(j) - 1) * width_[j]; + d(j) = (xyz_cross - r0[j]) / u[j]; + } + } + + j = xt::argmin(d)(0); + if (u[j] > 0.0) { + ++ijk0(j); + } else { + --ijk0(j); + } + + ++search_iter; + } + + // Advance position + r0 += d(j) * u; + } + + while (true) { + // ======================================================================== + // Compute the length of the track segment in the each mesh cell and return + + double distance; + int j; + if (ijk0 == ijk1) { + // The track ends in this cell. Use the particle end location rather + // than the mesh surface. + distance = (r1 - r0).norm(); + } else { + // The track exits this cell. Determine the distance to the closest mesh + // surface. + xt::xtensor d = xt::zeros({n}); + for (int j = 0; j < n; ++j) { + if (std::fabs(u[j]) < FP_PRECISION) { + d(j) = INFTY; + } else if (u[j] > 0) { + double xyz_cross = lower_left_[j] + ijk0(j) * width_[j]; + d(j) = (xyz_cross - r0[j]) / u[j]; + } else { + double xyz_cross = lower_left_[j] + (ijk0(j) - 1) * width_[j]; + d(j) = (xyz_cross - r0[j]) / u[j]; + } + } + j = xt::argmin(d)(0); + distance = d(j); + } + + // Assign the next tally bin and the score. + int bin = get_bin_from_indices(ijk0.data()); + bins.push_back(bin); + lengths.push_back(distance / total_distance); + + // If the particle track ends in that bin, then we are done. + if (ijk0 == ijk1) break; + + // Translate the starting coordintes by the distance to that face. This + // should be the xyz that we computed the distance to in the last + // iteration of the filter loop. + r0 += distance * u; + + // Increment the indices into the next mesh cell. + if (u[j] > 0.0) { + ++ijk0(j); + } else { + --ijk0(j); + } + + // If the next indices are invalid, then the track has left the mesh and + // we are done. + if (xt::any(ijk0 < 1) || xt::any(ijk0 > shape_)) break; + } +} + +void RegularMesh::surface_bins_crossed(const Particle* p, std::vector& bins) const +{ + // ======================================================================== + // Determine if the track intersects the tally mesh. + + // Copy the starting and ending coordinates of the particle. + Position r0 {p->last_xyz_current}; + Position r1 {p->coord[0].xyz}; + Direction u {p->coord[0].uvw}; + + // Determine indices for starting and ending location. + int n = n_dimension_; + xt::xtensor ijk0 = xt::empty({n}); + bool start_in_mesh; + get_indices(r0, ijk0.data(), &start_in_mesh); + xt::xtensor ijk1 = xt::empty({n}); + bool end_in_mesh; + get_indices(r1, ijk1.data(), &end_in_mesh); + + // Check if the track intersects any part of the mesh. + if (!start_in_mesh && !end_in_mesh) { + if (!intersects(r0, r1)) return; + } + + // ======================================================================== + // Figure out which mesh cell to tally. + + // Calculate number of surface crossings + int n_cross = xt::sum(xt::abs(ijk1 - ijk0))(); + if (n_cross == 0) return; + + // Bounding coordinates + Position xyz_cross; + for (int i = 0; i < n; ++i) { + if (u[i] > 0.0) { + xyz_cross[i] = lower_left_[i] + ijk0[i] * width_[i]; + } else { + xyz_cross[i] = lower_left_[i] + (ijk0[i] - 1) * width_[i]; + } + } + + for (int j = 0; j < n_cross; ++j) { + // Set the distances to infinity + Position d {INFTY, INFTY, INFTY}; + + // Determine closest bounding surface. We need to treat + // special case where the cosine of the angle is zero since this would + // result in a divide-by-zero. + double distance = INFTY; + for (int i = 0; i < n; ++i) { + if (u[i] == 0) { + d[i] = INFINITY; + } else { + d[i] = (xyz_cross[i] - r0[i])/u[i]; + } + distance = std::min(distance, d[i]); + } + + // Loop over the dimensions + for (int i = 0; i < n; ++i) { + // Check whether distance is the shortest distance + if (distance == d[i]) { + + // Check whether particle is moving in positive i direction + if (u[i] > 0) { + + // Outward current on i max surface + if (xt::all(ijk0 >= 1) && xt::all(ijk0 <= shape_)) { + int i_surf = 4*i + 3; + int i_mesh = get_bin_from_indices(ijk0.data()); + int i_bin = 4*n*(i_mesh - 1) + i_surf; + + bins.push_back(i_bin); + } + + // Advance position + ++ijk0[i]; + xyz_cross[i] += width_[i]; + + // If the particle crossed the surface, tally the inward current on + // i min surface + if (xt::all(ijk0 >= 1) && xt::all(ijk0 <= shape_)) { + int i_surf = 4*i + 2; + int i_mesh = get_bin_from_indices(ijk0.data()); + int i_bin = 4*n*(i_mesh - 1) + i_surf; + + bins.push_back(i_bin); + } + + } else { + // The particle is moving in the negative i direction + + // Outward current on i min surface + if (xt::all(ijk0 >= 1) && xt::all(ijk0 <= shape_) ){ + int i_surf = 4*i + 1; + int i_mesh = get_bin_from_indices(ijk0.data()); + int i_bin = 4*n*(i_mesh - 1) + i_surf; + + bins.push_back(i_bin); + } + + // Advance position + --ijk0[i]; + xyz_cross[i] -= width_[i]; + + // If the particle crossed the surface, tally the inward current on + // i max surface + if (xt::all(ijk0 >= 1) && xt::all(ijk0 <= shape_)) { + int i_surf = 4*i + 4; + int i_mesh = get_bin_from_indices(ijk0.data()); + int i_bin = 4*n*(i_mesh - 1) + i_surf; + + bins.push_back(i_bin); + } + } + } + } + + // Calculate new coordinates + r0 += distance * u; + } +} + +void RegularMesh::to_hdf5(hid_t group) const +{ + hid_t mesh_group = create_group(group, "mesh " + std::to_string(id_)); + + write_dataset(mesh_group, "type", "regular"); + write_dataset(mesh_group, "dimension", shape_); + write_dataset(mesh_group, "lower_left", lower_left_); + write_dataset(mesh_group, "upper_right", upper_right_); + write_dataset(mesh_group, "width", width_); + + close_group(mesh_group); +} + +xt::xarray RegularMesh::count_sites(int64_t n, const Bank* bank, + int n_energy, const double* energies, bool* outside) const +{ + // Determine shape of array for counts + std::size_t m = xt::prod(shape_)(); + std::vector shape; + if (n_energy > 0) { + shape = {m, static_cast(n_energy - 1)}; + } else { + shape = {m}; + } + + // Create array of zeros + xt::xarray cnt {shape, 0.0}; + bool outside_ = false; + + for (int64_t i = 0; i < n; ++i) { + // determine scoring bin for entropy mesh + // TODO: off-by-one + int mesh_bin = get_bin({bank[i].xyz}) - 1; + + // if outside mesh, skip particle + if (mesh_bin < 0) { + outside_ = true; + continue; + } + + if (n_energy > 0) { + double E = bank[i].E; + if (E >= energies[0] && E <= energies[n_energy - 1]) { + // determine energy bin + int e_bin = lower_bound_index(energies, energies + n_energy, E); + + // Add to appropriate bin + cnt(mesh_bin, e_bin) += bank[i].wgt; + } + } else { + // Add to appropriate bin + cnt(mesh_bin) += bank[i].wgt; + } + } + + // Create copy of count data + int total = cnt.size(); + double* cnt_reduced = new double[total]; + +#ifdef OPENMC_MPI + // collect values from all processors + MPI_Reduce(cnt.data(), cnt_reduced, total, MPI_DOUBLE, MPI_SUM, 0, + mpi::intracomm); + + // Check if there were sites outside the mesh for any processor + if (outside) { + MPI_Reduce(&outside_, outside, 1, MPI_C_BOOL, MPI_LOR, 0, mpi::intracomm); + } +#else + std::copy(cnt.data(), cnt.data() + total, cnt_reduced); + if (outside) *outside = outside_; +#endif + + // Adapt reduced values in array back into an xarray + auto arr = xt::adapt(cnt_reduced, total, xt::acquire_ownership(), shape); + xt::xarray counts = arr; + + return counts; +} + +//============================================================================== +// C API functions +//============================================================================== + +//! Extend the meshes array by n elements +extern "C" int +openmc_extend_meshes(int32_t n, int32_t* index_start, int32_t* index_end) +{ + if (index_start) *index_start = meshes.size(); + for (int i = 0; i < n; ++i) { + meshes.emplace_back(new RegularMesh{}); + } + if (index_end) *index_end = meshes.size() - 1; + + return 0; +} + +//! Return the index in the meshes array of a mesh with a given ID +extern "C" int +openmc_get_mesh_index(int32_t id, int32_t* index) +{ + auto pair = mesh_map.find(id); + if (pair == mesh_map.end()) { + set_errmsg("No mesh exists with ID=" + std::to_string(id) + "."); + return OPENMC_E_INVALID_ID; + } + *index = pair->second; + return 0; +} + +// Return the ID of a mesh +extern "C" int +openmc_mesh_get_id(int32_t index, int32_t* id) +{ + if (index < 0 || index >= meshes.size()) { + set_errmsg("Index in meshes array is out of bounds."); + return OPENMC_E_OUT_OF_BOUNDS; + } + *id = meshes[index]->id_; + return 0; +} + +//! Set the ID of a mesh +extern "C" int +openmc_mesh_set_id(int32_t index, int32_t id) +{ + if (index < 0 || index >= meshes.size()) { + set_errmsg("Index in meshes array is out of bounds."); + return OPENMC_E_OUT_OF_BOUNDS; + } + meshes[index]->id_ = id; + mesh_map[id] = index; + return 0; +} + +//! Get the dimension of a mesh +extern "C" int +openmc_mesh_get_dimension(int32_t index, int** dims, int* n) +{ + if (index < 0 || index >= meshes.size()) { + set_errmsg("Index in meshes array is out of bounds."); + return OPENMC_E_OUT_OF_BOUNDS; + } + *dims = meshes[index]->shape_.data(); + *n = meshes[index]->n_dimension_; + return 0; +} + +//! Set the dimension of a mesh +extern "C" int +openmc_mesh_set_dimension(int32_t index, int n, const int* dims) +{ + if (index < 0 || index >= meshes.size()) { + set_errmsg("Index in meshes array is out of bounds."); + return OPENMC_E_OUT_OF_BOUNDS; + } + + // Copy dimension + std::vector shape = {static_cast(n)}; + auto& m = meshes[index]; + m->shape_ = xt::adapt(dims, n, xt::no_ownership(), shape); + m->n_dimension_ = m->shape_.size(); + + return 0; +} + +//! Get the mesh parameters +extern "C" int +openmc_mesh_get_params(int32_t index, double** ll, double** ur, double** width, int* n) +{ + if (index < 0 || index >= meshes.size()) { + set_errmsg("Index in meshes array is out of bounds."); + return OPENMC_E_OUT_OF_BOUNDS; + } + + auto& m = meshes[index]; + if (m->lower_left_.dimension() == 0) { + set_errmsg("Mesh parameters have not been set."); + return OPENMC_E_ALLOCATE; + } + + *ll = m->lower_left_.data(); + *ur = m->upper_right_.data(); + *width = m->width_.data(); + *n = m->n_dimension_; + return 0; +} + +//! Set the mesh parameters +extern "C" int +openmc_mesh_set_params(int32_t index, int n, const double* ll, const double* ur, + const double* width) +{ + if (index < 0 || index >= meshes.size()) { + set_errmsg("Index in meshes array is out of bounds."); + return OPENMC_E_OUT_OF_BOUNDS; + } + + auto& m = meshes[index]; + std::vector shape = {static_cast(n)}; + if (ll && ur) { + m->lower_left_ = xt::adapt(ll, n, xt::no_ownership(), shape); + m->upper_right_ = xt::adapt(ur, n, xt::no_ownership(), shape); + m->width_ = (m->upper_right_ - m->lower_left_) / m->shape_; + } else if (ll && width) { + m->lower_left_ = xt::adapt(ll, n, xt::no_ownership(), shape); + m->width_ = xt::adapt(width, n, xt::no_ownership(), shape); + m->upper_right_ = m->lower_left_ + m->shape_ * m->width_; + } else if (ur && width) { + m->upper_right_ = xt::adapt(ur, n, xt::no_ownership(), shape); + m->width_ = xt::adapt(width, n, xt::no_ownership(), shape); + m->lower_left_ = m->upper_right_ - m->shape_ * m->width_; + } else { + set_errmsg("At least two parameters must be specified."); + return OPENMC_E_INVALID_ARGUMENT; + } + + return 0; +} + +//============================================================================== +// Non-member functions +//============================================================================== + +void read_meshes(pugi::xml_node* root) +{ + for (auto node : root->children("mesh")) { + // Read mesh and add to vector + meshes.emplace_back(new RegularMesh{node}); + + // Map ID to position in vector + mesh_map[meshes.back()->id_] = meshes.size() - 1; + } +} + +void meshes_to_hdf5(hid_t group) +{ + // Write number of meshes + hid_t meshes_group = create_group(group, "meshes"); + int32_t n_meshes = meshes.size(); + write_attribute(meshes_group, "n_meshes", n_meshes); + + if (n_meshes > 0) { + // Write IDs of meshes + std::vector ids; + for (const auto& m : meshes) { + m->to_hdf5(meshes_group); + ids.push_back(m->id_); + } + write_attribute(meshes_group, "ids", ids); + } + + close_group(meshes_group); +} + +//============================================================================== +// Fortran compatibility +//============================================================================== + +extern "C" { + // Declaration of Fortran procedures + void vector_int_push_back(void* ptr, int value); + void vector_real_push_back(void* ptr, double value); + + int n_meshes() { return meshes.size(); } + + RegularMesh* mesh_ptr(int i) { return meshes.at(i).get(); } + + int32_t mesh_id(RegularMesh* m) { return m->id_; } + + double mesh_volume_frac(RegularMesh* m) { return m->volume_frac_; } + + int mesh_n_dimension(RegularMesh* m) { return m->n_dimension_; } + + int mesh_dimension(RegularMesh* m, int i) { return m->shape_(i - 1); } + + double mesh_lower_left(RegularMesh* m, int i) { return m->lower_left_(i - 1); } + + double mesh_upper_right(RegularMesh* m, int i) { return m->upper_right_(i - 1); } + + double mesh_width(RegularMesh* m, int i) { return m->width_(i - 1); } + + int mesh_get_bin(RegularMesh* m, const double* xyz) + { + return m->get_bin({xyz}); + } + + int mesh_get_bin_from_indices(RegularMesh* m, const int* ijk) + { + return m->get_bin_from_indices(ijk); + } + + void mesh_get_indices(RegularMesh* m, const double* xyz, int* ijk, bool* in_mesh) + { + m->get_indices({xyz}, ijk, in_mesh); + } + + void mesh_get_indices_from_bin(RegularMesh* m, int bin, int* ijk) + { + m->get_indices_from_bin(bin, ijk); + } + + void mesh_bins_crossed(RegularMesh* m, const Particle* p, void* match_bins, + void* match_weights) + { + // Get bins crossed + std::vector bins; + std::vector lengths; + m->bins_crossed(p, bins, lengths); + + // Call bindings for VectorInt and VectorReal on Fortran side + for (int i = 0; i < bins.size(); ++i) { + vector_int_push_back(match_bins, bins[i]); + vector_real_push_back(match_weights, lengths[i]); + } + } + + void mesh_surface_bins_crossed(RegularMesh* m, const Particle* p, + void* match_bins, void* match_weights) + { + // Get surface bins crossed + std::vector bins; + m->surface_bins_crossed(p, bins); + + // Call bindings for VectorInt and VectorReal + for (auto b : bins) { + vector_int_push_back(match_bins, b); + vector_real_push_back(match_weights, 1.0); + } + } + + void free_memory_mesh() + { + meshes.clear(); + mesh_map.clear(); + } +} + + +} // namespace openmc diff --git a/src/mesh_header.F90 b/src/mesh_header.F90 index fb22d2ad3..d7e26fc6b 100644 --- a/src/mesh_header.F90 +++ b/src/mesh_header.F90 @@ -2,185 +2,242 @@ module mesh_header use, intrinsic :: ISO_C_BINDING - use constants - use dict_header, only: DictIntInt - use error - use hdf5_interface - use string, only: to_str, to_lower - use xml_interface - implicit none - private - public :: free_memory_mesh - public :: openmc_extend_meshes - public :: openmc_get_mesh_index - public :: openmc_mesh_get_id - public :: openmc_mesh_get_dimension - public :: openmc_mesh_get_params - public :: openmc_mesh_set_id - public :: openmc_mesh_set_dimension - public :: openmc_mesh_set_params !=============================================================================== ! STRUCTUREDMESH represents a tessellation of n-dimensional Euclidean space by ! congruent squares or cubes !=============================================================================== - type, public :: RegularMesh - integer :: id = -1 ! user-specified id - integer :: type = MESH_REGULAR ! rectangular, hexagonal - integer(C_INT) :: n_dimension ! rank of mesh - real(8) :: volume_frac ! volume fraction of each cell - integer(C_INT), allocatable :: dimension(:) ! number of cells in each direction - real(C_DOUBLE), allocatable :: lower_left(:) ! lower-left corner of mesh - real(C_DOUBLE), allocatable :: upper_right(:) ! upper-right corner of mesh - real(C_DOUBLE), allocatable :: width(:) ! width of each mesh cell + type :: RegularMesh + type(C_PTR) :: ptr contains - procedure :: from_xml => regular_from_xml + procedure :: id => regular_id + procedure :: volume_frac => regular_volume_frac + procedure :: n_dimension => regular_n_dimension + procedure :: dimension => regular_dimension + procedure :: lower_left => regular_lower_left + procedure :: upper_right => regular_upper_right + procedure :: width => regular_width + procedure :: get_bin => regular_get_bin procedure :: get_indices => regular_get_indices procedure :: get_bin_from_indices => regular_get_bin_from_indices procedure :: get_indices_from_bin => regular_get_indices_from_bin - procedure :: intersects => regular_intersects - procedure :: to_hdf5 => regular_to_hdf5 end type RegularMesh - integer(C_INT32_T), public, bind(C) :: n_meshes = 0 ! # of structured meshes + interface + function openmc_extend_meshes(n, index_start, index_end) result(err) bind(C) + import C_INT32_T, C_INT + integer(C_INT32_T), value, intent(in) :: n + integer(C_INT32_T), optional, intent(out) :: index_start + integer(C_INT32_T), optional, intent(out) :: index_end + integer(C_INT) :: err + end function openmc_extend_meshes - type(RegularMesh), public, allocatable, target :: meshes(:) + function openmc_get_mesh_index(id, index) result(err) bind(C) + import C_INT32_T, C_INT + integer(C_INT32_T), value :: id + integer(C_INT32_T), intent(out) :: index + integer(C_INT) :: err + end function openmc_get_mesh_index - ! Dictionary that maps user IDs to indices in 'meshes' - type(DictIntInt), public :: mesh_dict + function openmc_mesh_get_id(index, id) result(err) bind(C) + import C_INT32_T, C_INT + integer(C_INT32_T), value :: index + integer(C_INT32_T), intent(out) :: id + integer(C_INT) :: err + end function openmc_mesh_get_id + + function openmc_mesh_set_id(index, id) result(err) bind(C) + import C_INT32_T, C_INT + integer(C_INT32_T), value, intent(in) :: index + integer(C_INT32_T), value, intent(in) :: id + integer(C_INT) :: err + end function openmc_mesh_set_id + + function openmc_mesh_get_dimension(index, dims, n) result(err) bind(C) + import C_INT32_T, C_PTR, C_INT + integer(C_INT32_T), value, intent(in) :: index + type(C_PTR), intent(out) :: dims + integer(C_INT), intent(out) :: n + integer(C_INT) :: err + end function openmc_mesh_get_dimension + + function openmc_mesh_set_dimension(index, n, dims) result(err) bind(C) + import C_INT32_T, C_INT + integer(C_INT32_T), value, intent(in) :: index + integer(C_INT), value, intent(in) :: n + integer(C_INT), intent(in) :: dims(n) + integer(C_INT) :: err + end function openmc_mesh_set_dimension + + function openmc_mesh_get_params(index, ll, ur, width, n) result(err) bind(C) + import C_INT32_T, C_PTR, C_INT + integer(C_INT32_T), value, intent(in) :: index + type(C_PTR), intent(out) :: ll + type(C_PTR), intent(out) :: ur + type(C_PTR), intent(out) :: width + integer(C_INT), intent(out) :: n + integer(C_INT) :: err + end function openmc_mesh_get_params + + function openmc_mesh_set_params(index, n, ll, ur, width) result(err) bind(C) + import C_INT32_T, C_INT, C_DOUBLE + integer(C_INT32_T), value, intent(in) :: index + integer(C_INT), value, intent(in) :: n + real(C_DOUBLE), intent(in), optional :: ll(n) + real(C_DOUBLE), intent(in), optional :: ur(n) + real(C_DOUBLE), intent(in), optional :: width(n) + integer(C_INT) :: err + end function openmc_mesh_set_params + + function mesh_id(ptr) result(id) bind(C) + import C_PTR, C_INT32_T + type(C_PTR), value :: ptr + integer(C_INT32_T) :: id + end function + + function mesh_volume_frac(ptr) result(volume_frac) bind(C) + import C_PTR, C_DOUBLE + type(C_PTR), value :: ptr + real(C_DOUBLE) :: volume_frac + end function + + function mesh_n_dimension(ptr) result(n) bind(C) + import C_PTR, C_INT + type(C_PTR), value :: ptr + integer(C_INT) :: n + end function + + function mesh_dimension(ptr, i) result(d) bind(C) + import C_PTR, C_INT + type(C_PTR), value :: ptr + integer(C_INT), value :: i + integer(C_INT) :: d + end function + + function mesh_lower_left(ptr, i) result(ll) bind(C) + import C_PTR, C_INT, C_DOUBLE + type(C_PTR), value :: ptr + integer(C_INT), value :: i + real(C_DOUBLE) :: ll + end function + + function mesh_upper_right(ptr, i) result(ur) bind(C) + import C_PTR, C_INT, C_DOUBLE + type(C_PTR), value :: ptr + integer(C_INT), value :: i + real(C_DOUBLE) :: ur + end function + + function mesh_width(ptr, i) result(w) bind(C) + import C_PTR, C_INT, C_DOUBLE + type(C_PTR), value :: ptr + integer(C_INT), value :: i + real(C_DOUBLE) :: w + end function + + pure function mesh_get_bin(ptr, xyz) result(bin) bind(C) + import C_PTR, C_DOUBLE, C_INT + type(C_PTR), value :: ptr + real(C_DOUBLE), intent(in) :: xyz(*) + integer(C_INT) :: bin + end function + + pure function mesh_get_bin_from_indices(ptr, ijk) result(bin) bind(C) + import C_PTR, C_INT + type(C_PTR), value :: ptr + integer(C_INT), intent(in) :: ijk(*) + integer(C_INT) :: bin + end function + + pure subroutine mesh_get_indices(ptr, xyz, ijk, in_mesh) bind(C) + import C_PTR, C_DOUBLE, C_INT, C_BOOL + type(C_PTR), value :: ptr + real(C_DOUBLE), intent(in) :: xyz(*) + integer(C_INT), intent(out) :: ijk(*) + logical(C_BOOL), intent(out) :: in_mesh + end subroutine + + pure subroutine mesh_get_indices_from_bin(ptr, bin, ijk) bind(C) + import C_PTR, C_INT + type(C_PTR), value :: ptr + integer(C_INT), value :: bin + integer(C_INT), intent(out) :: ijk(*) + end subroutine + + function mesh_ptr(i) result(ptr) bind(C) + import C_INT, C_PTR + integer(C_INT), value :: i + type(C_PTR) :: ptr + end function + + subroutine read_meshes(node_ptr) bind(C) + import C_PTR + type(C_PTR) :: node_ptr + end subroutine + + function n_meshes() result(n) bind(C) + import C_INT + integer(C_INT) :: n + end function + end interface contains - subroutine regular_from_xml(this, node) - class(RegularMesh), intent(inout) :: this - type(XMLNode), intent(in) :: node + function meshes(i) result(m) + integer, intent(in) :: i + type(RegularMesh) :: m - integer :: n - character(MAX_LINE_LEN) :: temp_str + m % ptr = mesh_ptr(i) + end function - ! Copy mesh id - if (check_for_node(node, "id")) then - call get_node_value(node, "id", this % id) + function regular_id(this) result(id) + class(RegularMesh), intent(in) :: this + integer(C_INT32_T) :: id + id = mesh_id(this % ptr) + end function - ! Check to make sure 'id' hasn't been used - if (mesh_dict % has(this % id)) then - call fatal_error("Two or more meshes use the same unique ID: " & - // to_str(this % id)) - end if - end if + function regular_volume_frac(this) result(volume_frac) + class(RegularMesh), intent(in) :: this + real(C_DOUBLE) :: volume_frac + volume_frac = mesh_volume_frac(this % ptr) + end function - ! Read mesh type - if (check_for_node(node, "type")) then - call get_node_value(node, "type", temp_str) - select case (to_lower(temp_str)) - case ('rect', 'rectangle', 'rectangular') - call warning("Mesh type '" // trim(temp_str) // "' is deprecated. & - &Please use 'regular' instead.") - this % type = MESH_REGULAR - case ('regular') - this % type = MESH_REGULAR - case default - call fatal_error("Invalid mesh type: " // trim(temp_str)) - end select - else - this % type = MESH_REGULAR - end if + function regular_n_dimension(this) result(n) + class(RegularMesh), intent(in) :: this + integer(C_INT) :: n + n = mesh_n_dimension(this % ptr) + end function - ! Determine number of dimensions for mesh - if (check_for_node(node, "dimension")) then - n = node_word_count(node, "dimension") - if (n /= 1 .and. n /= 2 .and. n /= 3) then - call fatal_error("Mesh must be one, two, or three dimensions.") - end if - this % n_dimension = n + function regular_dimension(this, i) result(d) + class(RegularMesh), intent(in) :: this + integer(C_INT), intent(in) :: i + integer(C_INT) :: d + d = mesh_dimension(this % ptr, i) + end function - ! Allocate attribute arrays - allocate(this % dimension(n)) + function regular_lower_left(this, i) result(ll) + class(RegularMesh), intent(in) :: this + integer(C_INT), intent(in) :: i + real(C_DOUBLE) :: ll + ll = mesh_lower_left(this % ptr, i) + end function - ! Check that dimensions are all greater than zero - call get_node_array(node, "dimension", this % dimension) - if (any(this % dimension <= 0)) then - call fatal_error("All entries on the element for a tally & - &mesh must be positive.") - end if - end if + function regular_upper_right(this, i) result(ur) + class(RegularMesh), intent(in) :: this + integer(C_INT), intent(in) :: i + real(C_DOUBLE) :: ur + ur = mesh_upper_right(this % ptr, i) + end function - ! Check for lower-left coordinates - if (check_for_node(node, "lower_left")) then - n = node_word_count(node, "lower_left") - allocate(this % lower_left(n)) - - ! Read mesh lower-left corner location - call get_node_array(node, "lower_left", this % lower_left) - else - call fatal_error("Must specify on a mesh.") - end if - - if (check_for_node(node, "width")) then - ! Make sure both upper-right or width were specified - if (check_for_node(node, "upper_right")) then - call fatal_error("Cannot specify both and on a & - &mesh.") - end if - - n = node_word_count(node, "width") - allocate(this % width(n)) - allocate(this % upper_right(n)) - - ! Check to ensure width has same dimensions - if (n /= size(this % lower_left)) then - call fatal_error("Number of entries on must be the same as & - &the number of entries on .") - end if - - ! Check for negative widths - call get_node_array(node, "width", this % width) - if (any(this % width < ZERO)) then - call fatal_error("Cannot have a negative on a tally mesh.") - end if - - ! Set width and upper right coordinate - this % upper_right = this % lower_left + this % dimension * this % width - - elseif (check_for_node(node, "upper_right")) then - n = node_word_count(node, "upper_right") - allocate(this % upper_right(n)) - allocate(this % width(n)) - - ! Check to ensure width has same dimensions - if (n /= size(this % lower_left)) then - call fatal_error("Number of entries on must be the & - &same as the number of entries on .") - end if - - ! Check that upper-right is above lower-left - call get_node_array(node, "upper_right", this % upper_right) - if (any(this % upper_right < this % lower_left)) then - call fatal_error("The coordinates must be greater than & - &the coordinates on a tally mesh.") - end if - - ! Set width and upper right coordinate - this % width = (this % upper_right - this % lower_left) / this % dimension - else - call fatal_error("Must specify either and on a & - &mesh.") - end if - - if (allocated(this % dimension)) then - if (size(this % dimension) /= size(this % lower_left)) then - call fatal_error("Number of entries on must be the same & - &as the number of entries on .") - end if - - ! Set volume fraction - this % volume_frac = ONE/real(product(this % dimension),8) - end if - - end subroutine regular_from_xml + function regular_width(this, i) result(w) + class(RegularMesh), intent(in) :: this + integer(C_INT), intent(in) :: i + real(C_DOUBLE) :: w + w = mesh_width(this % ptr, i) + end function !=============================================================================== ! GET_MESH_BIN determines the tally bin for a particle in a structured mesh @@ -191,38 +248,8 @@ contains real(8), intent(in) :: xyz(:) ! coordinates integer, intent(out) :: bin ! tally bin - integer :: n ! size of mesh - integer :: d ! mesh dimension index - integer :: ijk(3) ! indices in mesh - logical :: in_mesh ! was given coordinate in mesh at all? - - ! Get number of dimensions - n = this % n_dimension - - ! Loop over the dimensions of the mesh - do d = 1, n - - ! Check for cases where particle is outside of mesh - if (xyz(d) < this % lower_left(d)) then - bin = NO_BIN_FOUND - return - elseif (xyz(d) > this % upper_right(d)) then - bin = NO_BIN_FOUND - return - end if - end do - - ! Determine indices - call this % get_indices(xyz, ijk, in_mesh) - - ! Convert indices to bin - if (in_mesh) then - bin = this % get_bin_from_indices(ijk) - else - bin = NO_BIN_FOUND - end if - - end subroutine regular_get_bin + bin = mesh_get_bin(this % ptr, xyz) + end subroutine !=============================================================================== ! GET_MESH_INDICES determines the indices of a particle in a structured mesh @@ -234,18 +261,10 @@ contains integer, intent(out) :: ijk(:) ! indices in mesh logical, intent(out) :: in_mesh ! were given coords in mesh? - ! Find particle in mesh - ijk(:this % n_dimension) = ceiling((xyz(:this % n_dimension) - & - this % lower_left)/this % width) - - ! Determine if particle is in mesh - if (any(ijk(:this % n_dimension) < 1) .or. & - any(ijk(:this % n_dimension) > this % dimension)) then - in_mesh = .false. - else - in_mesh = .true. - end if + logical(C_BOOL) :: in_mesh_ + call mesh_get_indices(this % ptr, xyz, ijk, in_mesh_) + in_mesh = in_mesh_ end subroutine regular_get_indices !=============================================================================== @@ -258,15 +277,7 @@ contains integer, intent(in) :: ijk(:) integer :: bin - if (this % n_dimension == 1) then - bin = ijk(1) - elseif (this % n_dimension == 2) then - bin = (ijk(2) - 1) * this % dimension(1) + ijk(1) - elseif (this % n_dimension == 3) then - bin = ((ijk(3) - 1) * this % dimension(2) + (ijk(2) - 1)) & - * this % dimension(1) + ijk(1) - end if - + bin = mesh_get_bin_from_indices(this % ptr, ijk) end function regular_get_bin_from_indices !=============================================================================== @@ -279,498 +290,7 @@ contains integer, intent(in) :: bin integer, intent(out) :: ijk(:) - if (this % n_dimension == 1) then - ijk(1) = bin - else if (this % n_dimension == 2) then - ijk(1) = mod(bin - 1, this % dimension(1)) + 1 - ijk(2) = (bin - 1)/this % dimension(1) + 1 - else if (this % n_dimension == 3) then - ijk(1) = mod(bin - 1, this % dimension(1)) + 1 - ijk(2) = mod(bin - 1, this % dimension(1) * this % dimension(2)) & - / this % dimension(1) + 1 - ijk(3) = (bin - 1)/(this % dimension(1) * this % dimension(2)) + 1 - end if - + call mesh_get_indices_from_bin(this % ptr, bin, ijk) end subroutine regular_get_indices_from_bin -!=============================================================================== -! MESH_INTERSECTS determines if a line between xyz0 and xyz1 intersects the -! outer boundary of the given mesh. This is important for determining whether a -! track will score to a mesh tally. -!=============================================================================== - - pure function regular_intersects(this, xyz0, xyz1) result(intersects) - class(RegularMesh), intent(in) :: this - real(8), intent(in) :: xyz0(:) - real(8), intent(in) :: xyz1(:) - logical :: intersects - - select case(this % n_dimension) - case (1) - intersects = mesh_intersects_1d(this, xyz0, xyz1) - case (2) - intersects = mesh_intersects_2d(this, xyz0, xyz1) - case (3) - intersects = mesh_intersects_3d(this, xyz0, xyz1) - end select - end function regular_intersects - - pure function mesh_intersects_1d(m, xyz0, xyz1) result(intersects) - type(RegularMesh), intent(in) :: m - real(8), intent(in) :: xyz0(:) - real(8), intent(in) :: xyz1(:) - logical :: intersects - - real(8) :: x0 ! track start point - real(8) :: x1 ! track end point - real(8) :: xm0 ! lower-left coordinates of mesh - real(8) :: xm1 ! upper-right coordinates of mesh - - ! Copy coordinates of starting point - x0 = xyz0(1) - - ! Copy coordinates of ending point - x1 = xyz1(1) - - ! Copy coordinates of mesh lower_left - xm0 = m % lower_left(1) - - ! Copy coordinates of mesh upper_right - xm1 = m % upper_right(1) - - ! Set default value for intersects - intersects = .false. - - ! Check if line intersects left surface - if ((x0 < xm0 .and. x1 > xm0) .or. (x0 > xm0 .and. x1 < xm0)) then - intersects = .true. - return - end if - - ! Check if line intersects right surface - if ((x0 < xm1 .and. x1 > xm1) .or. (x0 > xm1 .and. x1 < xm1)) then - intersects = .true. - return - end if - - end function mesh_intersects_1d - - pure function mesh_intersects_2d(m, xyz0, xyz1) result(intersects) - type(RegularMesh), intent(in) :: m - real(8), intent(in) :: xyz0(:) - real(8), intent(in) :: xyz1(:) - logical :: intersects - - real(8) :: x0, y0 ! track start point - real(8) :: x1, y1 ! track end point - real(8) :: xi, yi ! track intersection point with mesh - real(8) :: xm0, ym0 ! lower-left coordinates of mesh - real(8) :: xm1, ym1 ! upper-right coordinates of mesh - - ! Copy coordinates of starting point - x0 = xyz0(1) - y0 = xyz0(2) - - ! Copy coordinates of ending point - x1 = xyz1(1) - y1 = xyz1(2) - - ! Copy coordinates of mesh lower_left - xm0 = m % lower_left(1) - ym0 = m % lower_left(2) - - ! Copy coordinates of mesh upper_right - xm1 = m % upper_right(1) - ym1 = m % upper_right(2) - - ! Set default value for intersects - intersects = .false. - - ! Check if line intersects left surface -- calculate the intersection point - ! y - if ((x0 < xm0 .and. x1 > xm0) .or. (x0 > xm0 .and. x1 < xm0)) then - yi = y0 + (xm0 - x0) * (y1 - y0) / (x1 - x0) - if (yi >= ym0 .and. yi < ym1) then - intersects = .true. - return - end if - end if - - ! Check if line intersects back surface -- calculate the intersection point - ! x - if ((y0 < ym0 .and. y1 > ym0) .or. (y0 > ym0 .and. y1 < ym0)) then - xi = x0 + (ym0 - y0) * (x1 - x0) / (y1 - y0) - if (xi >= xm0 .and. xi < xm1) then - intersects = .true. - return - end if - end if - - ! Check if line intersects right surface -- calculate the intersection - ! point y - if ((x0 < xm1 .and. x1 > xm1) .or. (x0 > xm1 .and. x1 < xm1)) then - yi = y0 + (xm1 - x0) * (y1 - y0) / (x1 - x0) - if (yi >= ym0 .and. yi < ym1) then - intersects = .true. - return - end if - end if - - ! Check if line intersects front surface -- calculate the intersection point - ! x - if ((y0 < ym1 .and. y1 > ym1) .or. (y0 > ym1 .and. y1 < ym1)) then - xi = x0 + (ym1 - y0) * (x1 - x0) / (y1 - y0) - if (xi >= xm0 .and. xi < xm1) then - intersects = .true. - return - end if - end if - - end function mesh_intersects_2d - - pure function mesh_intersects_3d(m, xyz0, xyz1) result(intersects) - type(RegularMesh), intent(in) :: m - real(8), intent(in) :: xyz0(:) - real(8), intent(in) :: xyz1(:) - logical :: intersects - - real(8) :: x0, y0, z0 ! track start point - real(8) :: x1, y1, z1 ! track end point - real(8) :: xi, yi, zi ! track intersection point with mesh - real(8) :: xm0, ym0, zm0 ! lower-left coordinates of mesh - real(8) :: xm1, ym1, zm1 ! upper-right coordinates of mesh - - ! Copy coordinates of starting point - x0 = xyz0(1) - y0 = xyz0(2) - z0 = xyz0(3) - - ! Copy coordinates of ending point - x1 = xyz1(1) - y1 = xyz1(2) - z1 = xyz1(3) - - ! Copy coordinates of mesh lower_left - xm0 = m % lower_left(1) - ym0 = m % lower_left(2) - zm0 = m % lower_left(3) - - ! Copy coordinates of mesh upper_right - xm1 = m % upper_right(1) - ym1 = m % upper_right(2) - zm1 = m % upper_right(3) - - ! Set default value for intersects - intersects = .false. - - ! Check if line intersects left surface -- calculate the intersection point - ! (y,z) - if ((x0 < xm0 .and. x1 > xm0) .or. (x0 > xm0 .and. x1 < xm0)) then - yi = y0 + (xm0 - x0) * (y1 - y0) / (x1 - x0) - zi = z0 + (xm0 - x0) * (z1 - z0) / (x1 - x0) - if (yi >= ym0 .and. yi < ym1 .and. zi >= zm0 .and. zi < zm1) then - intersects = .true. - return - end if - end if - - ! Check if line intersects back surface -- calculate the intersection point - ! (x,z) - if ((y0 < ym0 .and. y1 > ym0) .or. (y0 > ym0 .and. y1 < ym0)) then - xi = x0 + (ym0 - y0) * (x1 - x0) / (y1 - y0) - zi = z0 + (ym0 - y0) * (z1 - z0) / (y1 - y0) - if (xi >= xm0 .and. xi < xm1 .and. zi >= zm0 .and. zi < zm1) then - intersects = .true. - return - end if - end if - - ! Check if line intersects bottom surface -- calculate the intersection - ! point (x,y) - if ((z0 < zm0 .and. z1 > zm0) .or. (z0 > zm0 .and. z1 < zm0)) then - xi = x0 + (zm0 - z0) * (x1 - x0) / (z1 - z0) - yi = y0 + (zm0 - z0) * (y1 - y0) / (z1 - z0) - if (xi >= xm0 .and. xi < xm1 .and. yi >= ym0 .and. yi < ym1) then - intersects = .true. - return - end if - end if - - ! Check if line intersects right surface -- calculate the intersection point - ! (y,z) - if ((x0 < xm1 .and. x1 > xm1) .or. (x0 > xm1 .and. x1 < xm1)) then - yi = y0 + (xm1 - x0) * (y1 - y0) / (x1 - x0) - zi = z0 + (xm1 - x0) * (z1 - z0) / (x1 - x0) - if (yi >= ym0 .and. yi < ym1 .and. zi >= zm0 .and. zi < zm1) then - intersects = .true. - return - end if - end if - - ! Check if line intersects front surface -- calculate the intersection point - ! (x,z) - if ((y0 < ym1 .and. y1 > ym1) .or. (y0 > ym1 .and. y1 < ym1)) then - xi = x0 + (ym1 - y0) * (x1 - x0) / (y1 - y0) - zi = z0 + (ym1 - y0) * (z1 - z0) / (y1 - y0) - if (xi >= xm0 .and. xi < xm1 .and. zi >= zm0 .and. zi < zm1) then - intersects = .true. - return - end if - end if - - ! Check if line intersects top surface -- calculate the intersection point - ! (x,y) - if ((z0 < zm1 .and. z1 > zm1) .or. (z0 > zm1 .and. z1 < zm1)) then - xi = x0 + (zm1 - z0) * (x1 - x0) / (z1 - z0) - yi = y0 + (zm1 - z0) * (y1 - y0) / (z1 - z0) - if (xi >= xm0 .and. xi < xm1 .and. yi >= ym0 .and. yi < ym1) then - intersects = .true. - return - end if - end if - - end function mesh_intersects_3d - -!=============================================================================== -! TO_HDF5 writes the mesh data to an HDF5 group -!=============================================================================== - - subroutine regular_to_hdf5(this, group) - class(RegularMesh), intent(in) :: this - integer(HID_T), intent(in) :: group - - integer(HID_T) :: mesh_group - - mesh_group = create_group(group, "mesh " // trim(to_str(this % id))) - - call write_dataset(mesh_group, "type", "regular") - call write_dataset(mesh_group, "dimension", this % dimension) - call write_dataset(mesh_group, "lower_left", this % lower_left) - call write_dataset(mesh_group, "upper_right", this % upper_right) - call write_dataset(mesh_group, "width", this % width) - - call close_group(mesh_group) - end subroutine regular_to_hdf5 - -!=============================================================================== -! FREE_MEMORY_MESH deallocates global arrays defined in this module -!=============================================================================== - - subroutine free_memory_mesh() - n_meshes = 0 - if (allocated(meshes)) deallocate(meshes) - call mesh_dict % clear() - end subroutine free_memory_mesh - -!=============================================================================== -! C API FUNCTIONS -!=============================================================================== - - function openmc_extend_meshes(n, index_start, index_end) result(err) bind(C) - ! Extend the meshes array by n elements - integer(C_INT32_T), value, intent(in) :: n - integer(C_INT32_T), optional, intent(out) :: index_start - integer(C_INT32_T), optional, intent(out) :: index_end - integer(C_INT) :: err - - type(RegularMesh), allocatable :: temp(:) ! temporary meshes array - - if (n_meshes == 0) then - ! Allocate meshes array - allocate(meshes(n)) - else - ! Allocate meshes array with increased size - allocate(temp(n_meshes + n)) - - ! Copy original meshes to temporary array - temp(1:n_meshes) = meshes - - ! Move allocation from temporary array - call move_alloc(FROM=temp, TO=meshes) - end if - - ! Return indices in meshes array - if (present(index_start)) index_start = n_meshes + 1 - if (present(index_end)) index_end = n_meshes + n - n_meshes = n_meshes + n - - err = 0 - end function openmc_extend_meshes - - - function openmc_get_mesh_index(id, index) result(err) bind(C) - ! Return the index in the meshes array of a mesh with a given ID - integer(C_INT32_T), value :: id - integer(C_INT32_T), intent(out) :: index - integer(C_INT) :: err - - if (allocated(meshes)) then - if (mesh_dict % has(id)) then - index = mesh_dict % get(id) - err = 0 - else - err = E_INVALID_ID - call set_errmsg("No mesh exists with ID=" // trim(to_str(id)) // ".") - end if - else - err = E_ALLOCATE - call set_errmsg("Memory has not been allocated for meshes.") - end if - end function openmc_get_mesh_index - - - function openmc_mesh_get_id(index, id) result(err) bind(C) - ! Return the ID of a mesh - integer(C_INT32_T), value :: index - integer(C_INT32_T), intent(out) :: id - integer(C_INT) :: err - - if (index >= 1 .and. index <= size(meshes)) then - id = meshes(index) % id - err = 0 - else - err = E_OUT_OF_BOUNDS - call set_errmsg("Index in meshes array is out of bounds.") - end if - end function openmc_mesh_get_id - - - function openmc_mesh_set_id(index, id) result(err) bind(C) - ! Set the ID of a mesh - integer(C_INT32_T), value, intent(in) :: index - integer(C_INT32_T), value, intent(in) :: id - integer(C_INT) :: err - - if (index >= 1 .and. index <= n_meshes) then - meshes(index) % id = id - call mesh_dict % set(id, index) - err = 0 - else - err = E_OUT_OF_BOUNDS - call set_errmsg("Index in meshes array is out of bounds.") - end if - end function openmc_mesh_set_id - - - function openmc_mesh_get_dimension(index, dims, n) result(err) bind(C) - ! Get the dimension of a mesh - integer(C_INT32_T), value, intent(in) :: index - type(C_PTR), intent(out) :: dims - integer(C_INT), intent(out) :: n - integer(C_INT) :: err - - if (index >= 1 .and. index <= n_meshes) then - dims = C_LOC(meshes(index) % dimension) - n = meshes(index) % n_dimension - err = 0 - else - err = E_OUT_OF_BOUNDS - call set_errmsg("Index in meshes array is out of bounds.") - end if - end function openmc_mesh_get_dimension - - - function openmc_mesh_set_dimension(index, n, dims) result(err) bind(C) - ! Set the dimension of a mesh - integer(C_INT32_T), value, intent(in) :: index - integer(C_INT), value, intent(in) :: n - integer(C_INT), intent(in) :: dims(n) - integer(C_INT) :: err - - if (index >= 1 .and. index <= n_meshes) then - associate (m => meshes(index)) - if (allocated(m % dimension)) deallocate (m % dimension) - if (allocated(m % lower_left)) deallocate (m % lower_left) - if (allocated(m % upper_right)) deallocate (m % upper_right) - if (allocated(m % width)) deallocate (m % width) - - m % n_dimension = n - allocate(m % dimension(n)) - allocate(m % lower_left(n)) - allocate(m % upper_right(n)) - allocate(m % width(n)) - - ! Copy dimension - m % dimension(:) = dims - end associate - err = 0 - else - err = E_OUT_OF_BOUNDS - call set_errmsg("Index in meshes array is out of bounds.") - end if - end function openmc_mesh_set_dimension - - - function openmc_mesh_get_params(index, ll, ur, width, n) result(err) bind(C) - ! Get the mesh parameters - integer(C_INT32_T), value, intent(in) :: index - type(C_PTR), intent(out) :: ll - type(C_PTR), intent(out) :: ur - type(C_PTR), intent(out) :: width - integer(C_INT), intent(out) :: n - integer(C_INT) :: err - - err = 0 - if (index >= 1 .and. index <= n_meshes) then - associate (m => meshes(index)) - if (allocated(m % lower_left)) then - ll = C_LOC(m % lower_left(1)) - ur = C_LOC(m % upper_right(1)) - width = C_LOC(m % width(1)) - n = m % n_dimension - else - err = E_ALLOCATE - call set_errmsg("Mesh parameters have not been set.") - end if - end associate - else - err = E_OUT_OF_BOUNDS - call set_errmsg("Index in meshes array is out of bounds.") - end if - end function openmc_mesh_get_params - - - function openmc_mesh_set_params(index, n, ll, ur, width) result(err) bind(C) - ! Set the mesh parameters - integer(C_INT32_T), value, intent(in) :: index - integer(C_INT), value, intent(in) :: n - real(C_DOUBLE), intent(in), optional :: ll(n) - real(C_DOUBLE), intent(in), optional :: ur(n) - real(C_DOUBLE), intent(in), optional :: width(n) - integer(C_INT) :: err - - err = 0 - if (index >= 1 .and. index <= n_meshes) then - associate (m => meshes(index)) - if (allocated(m % lower_left)) deallocate (m % lower_left) - if (allocated(m % upper_right)) deallocate (m % upper_right) - if (allocated(m % width)) deallocate (m % width) - - allocate(m % lower_left(n)) - allocate(m % upper_right(n)) - allocate(m % width(n)) - - if (present(ll) .and. present(ur)) then - m % lower_left(:) = ll - m % upper_right(:) = ur - m % width(:) = (ur - ll) / m % dimension - elseif (present(ll) .and. present(width)) then - m % lower_left(:) = ll - m % width(:) = width - m % upper_right(:) = ll + width * m % dimension - elseif (present(ur) .and. present(width)) then - m % upper_right(:) = ur - m % width(:) = width - m % lower_left(:) = ur - width * m % dimension - else - err = E_INVALID_ARGUMENT - call set_errmsg("At least two parameters must be specified.") - end if - end associate - else - err = E_OUT_OF_BOUNDS - call set_errmsg("Index in meshes array is out of bounds.") - end if - end function openmc_mesh_set_params - end module mesh_header diff --git a/src/message_passing.cpp b/src/message_passing.cpp index aac677c6e..dc287e3b3 100644 --- a/src/message_passing.cpp +++ b/src/message_passing.cpp @@ -5,6 +5,7 @@ namespace mpi { int rank {0}; int n_procs {1}; +bool master {true}; #ifdef OPENMC_MPI MPI_Comm intracomm; diff --git a/src/mgxs.cpp b/src/mgxs.cpp index eb13a55f6..12c73fab4 100644 --- a/src/mgxs.cpp +++ b/src/mgxs.cpp @@ -3,12 +3,17 @@ #include #include #include -#include +#include #ifdef _OPENMP #include #endif +#include "xtensor/xmath.hpp" +#include "xtensor/xsort.hpp" +#include "xtensor/xadapt.hpp" +#include "xtensor/xview.hpp" + #include "openmc/error.h" #include "openmc/math_functions.h" #include "openmc/random_lcg.h" @@ -28,14 +33,15 @@ std::vector macro_xs; void Mgxs::init(const std::string& in_name, double in_awr, - const double_1dvec& in_kTs, bool in_fissionable, int in_scatter_format, + const std::vector& in_kTs, bool in_fissionable, int in_scatter_format, int in_num_groups, int in_num_delayed_groups, bool in_is_isotropic, - const double_1dvec& in_polar, const double_1dvec& in_azimuthal) + const std::vector& in_polar, const std::vector& in_azimuthal) { // Set the metadata name = in_name; awr = in_awr; - kTs = in_kTs; + //TODO: Remove adapt when in_KTs is an xtensor + kTs = xt::adapt(in_kTs); fissionable = in_fissionable; scatter_format = in_scatter_format; num_groups = in_num_groups; @@ -61,8 +67,8 @@ Mgxs::init(const std::string& in_name, double in_awr, void Mgxs::metadata_from_hdf5(hid_t xs_id, int in_num_groups, - int in_num_delayed_groups, const double_1dvec& temperature, - double tolerance, int_1dvec& temps_to_read, int& order_dim, int& method) + int in_num_delayed_groups, const std::vector& temperature, + double tolerance, std::vector& temps_to_read, int& order_dim, int& method) { // get name char char_name[MAX_WORD_LEN]; @@ -87,7 +93,7 @@ Mgxs::metadata_from_hdf5(hid_t xs_id, int in_num_groups, dset_names[i] = new char[151]; } get_datasets(kT_group, dset_names); - double_1dvec available_temps(num_temps); + xt::xarray available_temps(num_temps); for (int i = 0; i < num_temps; i++) { read_double(kT_group, dset_names[i], &available_temps[i], true); @@ -110,24 +116,20 @@ Mgxs::metadata_from_hdf5(hid_t xs_id, int in_num_groups, switch(method) { case TEMPERATURE_NEAREST: - // Find the minimum difference - for (int i = 0; i < temperature.size(); i++) { - std::valarray temp_diff(available_temps.data(), - available_temps.size()); - temp_diff = std::abs(temp_diff - temperature[i]); - int i_closest = std::min_element(std::begin(temp_diff), std::end(temp_diff)) - - std::begin(temp_diff); + // Determine actual temperatures to read + for (const auto& T : temperature) { + auto i_closest = xt::argmin(xt::abs(available_temps - T))[0]; double temp_actual = available_temps[i_closest]; - - if (std::abs(temp_actual - temperature[i]) < tolerance) { - if (std::find(temps_to_read.begin(), temps_to_read.end(), - std::round(temp_actual)) == temps_to_read.end()) { + if (std::fabs(temp_actual - T) < tolerance) { + if (std::find(temps_to_read.begin(), temps_to_read.end(), std::round(temp_actual)) + == temps_to_read.end()) { temps_to_read.push_back(std::round(temp_actual)); - } else { - fatal_error("MGXS Library does not contain cross section for " + - in_name + " at or near " + - std::to_string(std::round(temperature[i])) + " K."); } + } 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); } } break; @@ -160,7 +162,7 @@ Mgxs::metadata_from_hdf5(hid_t xs_id, int in_num_groups, // Get the library's temperatures int n_temperature = temps_to_read.size(); - double_1dvec in_kTs(n_temperature); + std::vector in_kTs(n_temperature); for (int i = 0; i < n_temperature; i++) { std::string temp_str(std::to_string(temps_to_read[i]) + "K"); @@ -254,12 +256,12 @@ Mgxs::metadata_from_hdf5(hid_t xs_id, int in_num_groups, } // Set the angular bins to use equally-spaced bins - double_1dvec in_polar(in_n_pol); + std::vector in_polar(in_n_pol); double dangle = PI / in_n_pol; for (int p = 0; p < in_n_pol; p++) { in_polar[p] = (p + 0.5) * dangle; } - double_1dvec in_azimuthal(in_n_azi); + std::vector in_azimuthal(in_n_azi); dangle = 2. * PI / in_n_azi; for (int a = 0; a < in_n_azi; a++) { in_azimuthal[a] = (a + 0.5) * dangle - PI; @@ -274,12 +276,12 @@ Mgxs::metadata_from_hdf5(hid_t xs_id, int in_num_groups, //============================================================================== Mgxs::Mgxs(hid_t xs_id, int energy_groups, int delayed_groups, - const double_1dvec& temperature, double tolerance, int max_order, + const std::vector& temperature, double tolerance, int max_order, bool legendre_to_tabular, int legendre_to_tabular_points, int& method) { // Call generic data gathering routine (will populate the metadata) int order_data; - int_1dvec temps_to_read; + std::vector temps_to_read; metadata_from_hdf5(xs_id, energy_groups, delayed_groups, temperature, tolerance, temps_to_read, order_data, method); @@ -310,8 +312,8 @@ Mgxs::Mgxs(hid_t xs_id, int energy_groups, int delayed_groups, //============================================================================== -Mgxs::Mgxs(const std::string& in_name, const double_1dvec& mat_kTs, - const std::vector& micros, const double_1dvec& atom_densities, +Mgxs::Mgxs(const std::string& in_name, const std::vector& mat_kTs, + const std::vector& micros, const std::vector& atom_densities, double tolerance, int& method) { // Get the minimum data needed to initialize: @@ -328,8 +330,8 @@ Mgxs::Mgxs(const std::string& in_name, const double_1dvec& mat_kTs, int in_num_groups = micros[0]->num_groups; int in_num_delayed_groups = micros[0]->num_delayed_groups; bool in_is_isotropic = micros[0]->is_isotropic; - double_1dvec in_polar = micros[0]->polar; - double_1dvec in_azimuthal = micros[0]->azimuthal; + std::vector in_polar = micros[0]->polar; + std::vector in_azimuthal = micros[0]->azimuthal; init(in_name, in_awr, mat_kTs, in_fissionable, in_scatter_format, in_num_groups, in_num_delayed_groups, in_is_isotropic, in_polar, @@ -345,33 +347,27 @@ Mgxs::Mgxs(const std::string& in_name, const double_1dvec& mat_kTs, // Create the list of temperature indices and interpolation factors for // each microscopic data at the material temperature - int_1dvec micro_t(micros.size(), 0); - double_1dvec micro_t_interp(micros.size(), 0.); + std::vector micro_t(micros.size(), 0); + std::vector micro_t_interp(micros.size(), 0.); for (int m = 0; m < micros.size(); m++) { switch(method) { case TEMPERATURE_NEAREST: { - // Find the nearest temperature - std::valarray temp_diff(micros[m]->kTs.data(), - micros[m]->kTs.size()); - temp_diff = std::abs(temp_diff - temp_desired); - micro_t[m] = std::min_element(std::begin(temp_diff), - std::end(temp_diff)) - - std::begin(temp_diff); - double temp_actual = micros[m]->kTs[micro_t[m]]; + micro_t[m] = xt::argmin(xt::abs(micros[m]->kTs - temp_desired))[0]; + auto temp_actual = micros[m]->kTs[micro_t[m]]; if (std::abs(temp_actual - temp_desired) >= K_BOLTZMANN * tolerance) { - fatal_error("MGXS Library does not contain cross section for " + - name + " at or near " + - std::to_string(std::round(temp_desired / K_BOLTZMANN)) - + " K."); + 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); } } break; case TEMPERATURE_INTERPOLATION: // Get a list of bounding temperatures for each actual temperature // present in the model - for (int k = 0; k < micros[m]->kTs.size() - 1; k++) { + for (int k = 0; k < micros[m]->kTs.shape()[0] - 1; k++) { if ((micros[m]->kTs[k] <= temp_desired) && (temp_desired < micros[m]->kTs[k + 1])) { micro_t[m] = k; @@ -394,8 +390,8 @@ Mgxs::Mgxs(const std::string& in_name, const double_1dvec& mat_kTs, int num_interp_points = 2; if (method == TEMPERATURE_NEAREST) num_interp_points = 1; for (int interp_point = 0; interp_point < num_interp_points; interp_point++) { - double_1dvec interp(micros.size()); - double_1dvec temp_indices(micros.size()); + std::vector interp(micros.size()); + std::vector temp_indices(micros.size()); for (int m = 0; m < micros.size(); m++) { interp[m] = (1. - micro_t_interp[m]) * atom_densities[m]; temp_indices[m] = micro_t[m] + interp_point; @@ -409,8 +405,8 @@ Mgxs::Mgxs(const std::string& in_name, const double_1dvec& mat_kTs, //============================================================================== void -Mgxs::combine(const std::vector& micros, const double_1dvec& scalars, - const int_1dvec& micro_ts, int this_t) +Mgxs::combine(const std::vector& micros, const std::vector& scalars, + const std::vector& micro_ts, int this_t) { // Build the vector of pointers to the xs objects within micros std::vector those_xs(micros.size()); @@ -441,19 +437,19 @@ Mgxs::get_xs(int xstype, int gin, int* gout, double* mu, int* dg) double val; switch(xstype) { case MG_GET_XS_TOTAL: - val = xs_t->total[a][gin]; + val = xs_t->total(a, gin); break; case MG_GET_XS_NU_FISSION: - val = fissionable ? xs_t->nu_fission[a][gin] : 0.; + val = fissionable ? xs_t->nu_fission(a, gin) : 0.; break; case MG_GET_XS_ABSORPTION: - val = xs_t->absorption[a][gin]; + val = xs_t->absorption(a, gin);; break; case MG_GET_XS_FISSION: - val = fissionable ? xs_t->fission[a][gin] : 0.; + val = fissionable ? xs_t->fission(a, gin) : 0.; break; case MG_GET_XS_KAPPA_FISSION: - val = fissionable ? xs_t->kappa_fission[a][gin] : 0.; + val = fissionable ? xs_t->kappa_fission(a, gin) : 0.; break; case MG_GET_XS_SCATTER: case MG_GET_XS_SCATTER_MULT: @@ -462,16 +458,16 @@ Mgxs::get_xs(int xstype, int gin, int* gout, double* mu, int* dg) val = xs_t->scatter[a]->get_xs(xstype, gin, gout, mu); break; case MG_GET_XS_PROMPT_NU_FISSION: - val = fissionable ? xs_t->prompt_nu_fission[a][gin] : 0.; + val = fissionable ? xs_t->prompt_nu_fission(a, gin) : 0.; break; case MG_GET_XS_DELAYED_NU_FISSION: if (fissionable) { if (dg != nullptr) { - val = xs_t->delayed_nu_fission[a][gin][*dg]; + val = xs_t->delayed_nu_fission(a, *dg, gin); } else { val = 0.; - for (auto& num : xs_t->delayed_nu_fission[a][gin]) { - val += num; + for (int d = 0; d < xs_t->delayed_nu_fission.shape()[2]; d++) { + val += xs_t->delayed_nu_fission(a, d, gin); } } } else { @@ -481,12 +477,12 @@ Mgxs::get_xs(int xstype, int gin, int* gout, double* mu, int* dg) case MG_GET_XS_CHI_PROMPT: if (fissionable) { if (gout != nullptr) { - val = xs_t->chi_prompt[a][gin][*gout]; + val = xs_t->chi_prompt(a, gin, *gout); } else { // provide an outgoing group-wise sum val = 0.; - for (auto& num : xs_t->chi_prompt[a][gin]) { - val += num; + for (int g = 0; g < xs_t->chi_prompt.shape()[2]; g++) { + val += xs_t->chi_prompt(a, gin, g); } } } else { @@ -497,21 +493,21 @@ Mgxs::get_xs(int xstype, int gin, int* gout, double* mu, int* dg) if (fissionable) { if (gout != nullptr) { if (dg != nullptr) { - val = xs_t->chi_delayed[a][gin][*gout][*dg]; + val = xs_t->chi_delayed(a, *dg, gin, *gout); } else { - val = xs_t->chi_delayed[a][gin][*gout][0]; + val = xs_t->chi_delayed(a, 0, gin, *gout); } } else { if (dg != nullptr) { val = 0.; - for (int i = 0; i < xs_t->chi_delayed[a][gin].size(); i++) { - val += xs_t->chi_delayed[a][gin][i][*dg]; + for (int g = 0; g < xs_t->delayed_nu_fission.shape()[2]; g++) { + val += xs_t->delayed_nu_fission(a, *dg, gin, g); } } else { val = 0.; - for (int i = 0; i < xs_t->chi_delayed[a][gin].size(); i++) { - for (auto& num : xs_t->chi_delayed[a][gin][i]) { - val += num; + for (int g = 0; g < xs_t->delayed_nu_fission.shape()[2]; g++) { + for (int d = 0; d < xs_t->delayed_nu_fission.shape()[3]; d++) { + val += xs_t->delayed_nu_fission(a, d, gin, g); } } } @@ -521,13 +517,13 @@ Mgxs::get_xs(int xstype, int gin, int* gout, double* mu, int* dg) } break; case MG_GET_XS_INVERSE_VELOCITY: - val = xs_t->inverse_velocity[a][gin]; + val = xs_t->inverse_velocity(a, gin); break; case MG_GET_XS_DECAY_RATE: if (dg != nullptr) { - val = xs_t->decay_rate[a][*dg + 1]; + val = xs_t->decay_rate(a, *dg + 1); } else { - val = xs_t->decay_rate[a][0]; + val = xs_t->decay_rate(a, 0); } break; default: @@ -548,11 +544,11 @@ Mgxs::sample_fission_energy(int gin, int& dg, int& gout) int tid = 0; #endif XsData* xs_t = &xs[cache[tid].t]; - double nu_fission = xs_t->nu_fission[cache[tid].a][gin]; + double nu_fission = xs_t->nu_fission(cache[tid].a, gin); // Find the probability of having a prompt neutron double prob_prompt = - xs_t->prompt_nu_fission[cache[tid].a][gin]; + xs_t->prompt_nu_fission(cache[tid].a, gin); // sample random numbers double xi_pd = prn() * nu_fission; @@ -568,10 +564,10 @@ Mgxs::sample_fission_energy(int gin, int& dg, int& gout) // sample the outgoing energy group gout = 0; double prob_gout = - xs_t->chi_prompt[cache[tid].a][gin][gout]; + xs_t->chi_prompt(cache[tid].a, gin, gout); while (prob_gout < xi_gout) { gout++; - prob_gout += xs_t->chi_prompt[cache[tid].a][gin][gout]; + prob_gout += xs_t->chi_prompt(cache[tid].a, gin, gout); } } else { @@ -582,7 +578,7 @@ Mgxs::sample_fission_energy(int gin, int& dg, int& gout) while (xi_pd >= prob_prompt) { dg++; prob_prompt += - xs_t->delayed_nu_fission[cache[tid].a][gin][dg]; + xs_t->delayed_nu_fission(cache[tid].a, dg, gin); } // adjust dg in case of round-off error @@ -591,11 +587,11 @@ Mgxs::sample_fission_energy(int gin, int& dg, int& gout) // sample the outgoing energy group gout = 0; double prob_gout = - xs_t->chi_delayed[cache[tid].a][gin][gout][dg]; + xs_t->chi_delayed(cache[tid].a, dg, gin, gout); while (prob_gout < xi_gout) { gout++; prob_gout += - xs_t->chi_delayed[cache[tid].a][gin][gout][dg]; + xs_t->chi_delayed(cache[tid].a, dg, gin, gout); } } } @@ -630,10 +626,10 @@ Mgxs::calculate_xs(int gin, double sqrtkT, const double uvw[3], set_temperature_index(sqrtkT); set_angle_index(uvw); XsData* xs_t = &xs[cache[tid].t]; - total_xs = xs_t->total[cache[tid].a][gin]; - abs_xs = xs_t->absorption[cache[tid].a][gin]; + total_xs = xs_t->total(cache[tid].a, gin); + abs_xs = xs_t->absorption(cache[tid].a, gin); - nu_fiss_xs = fissionable ? xs_t->nu_fission[cache[tid].a][gin] : 0.; + nu_fiss_xs = fissionable ? xs_t->nu_fission(cache[tid].a, gin) : 0.; } //============================================================================== @@ -662,17 +658,7 @@ Mgxs::set_temperature_index(double sqrtkT) int tid = 0; #endif if (sqrtkT != cache[tid].sqrtkT) { - double kT = sqrtkT * sqrtkT; - - // initialize vector for storage of the differences - std::valarray temp_diff(kTs.data(), kTs.size()); - - // Find the minimum difference of kT and kTs - temp_diff = std::abs(temp_diff - kT); - cache[tid].t = std::min_element(std::begin(temp_diff), std::end(temp_diff)) - - std::begin(temp_diff); - - // store this temperature as the last one used + cache[tid].t = xt::argmin(xt::abs(kTs - sqrtkT * sqrtkT))[0]; cache[tid].sqrtkT = sqrtkT; } } diff --git a/src/mgxs_interface.cpp b/src/mgxs_interface.cpp index f601f3c71..56f3392ff 100644 --- a/src/mgxs_interface.cpp +++ b/src/mgxs_interface.cpp @@ -19,7 +19,7 @@ add_mgxs_c(hid_t file_id, const char* name, int energy_groups, int& method) { // Convert temps to a vector for the from_hdf5 function - double_1dvec temperature(temps, temps + n_temps); + std::vector temperature(temps, temps + n_temps); write_message("Loading " + std::string(name) + " data...", 6); @@ -60,10 +60,10 @@ create_macro_xs_c(const char* mat_name, int n_nuclides, const int i_nuclides[], { if (n_temps > 0) { // // Convert temps to a vector - double_1dvec temperature(temps, temps + n_temps); + std::vector temperature(temps, temps + n_temps); // Convert atom_densities to a vector - double_1dvec atom_densities_vec(atom_densities, + std::vector atom_densities_vec(atom_densities, atom_densities + n_nuclides); // Build array of pointers to nuclides_MG's Mgxs objects needed for this diff --git a/src/multipole_header.F90 b/src/multipole_header.F90 index 05eb62e1a..64610e471 100644 --- a/src/multipole_header.F90 +++ b/src/multipole_header.F90 @@ -1,7 +1,6 @@ module multipole_header use constants - use dict_header, only: DictIntInt use error, only: fatal_error use hdf5_interface @@ -74,12 +73,11 @@ contains character(len=*), intent(in) :: filename character(len=10) :: version - integer :: i, n_poles, n_residues, n_windows + integer :: n_poles, n_residues, n_windows integer(HSIZE_T) :: dims_1d(1), dims_2d(2), dims_3d(3) integer(HID_T) :: file_id integer(HID_T) :: group_id integer(HID_T) :: dset - type(DictIntInt) :: l_val_dict ! Open file for reading and move into the /isotope group file_id = file_open(filename, 'r', parallel=.true.) diff --git a/src/nuclide_header.F90 b/src/nuclide_header.F90 index 6330cce43..8d1db8f2e 100644 --- a/src/nuclide_header.F90 +++ b/src/nuclide_header.F90 @@ -601,7 +601,6 @@ contains integer :: i, j, k, l integer :: t - integer :: m integer :: n integer :: n_grid integer :: i_fission diff --git a/src/output.F90 b/src/output.F90 index a4d734154..4363731f0 100644 --- a/src/output.F90 +++ b/src/output.F90 @@ -10,7 +10,6 @@ module output use error, only: fatal_error, warning use geometry_header use math, only: t_percentile - use mesh_header, only: RegularMesh, meshes use message_passing, only: master, n_procs use mgxs_interface use nuclide_header @@ -34,6 +33,14 @@ module output integer :: ou = OUTPUT_UNIT integer :: eu = ERROR_UNIT + interface + function entropy(i) result(h) bind(C, name='entropy_c') + import C_INT, C_DOUBLE + integer(C_INT), value :: i + real(C_DOUBLE) :: h + end function + end interface + contains !=============================================================================== @@ -41,7 +48,7 @@ contains ! developers, version, and date/time which the problem was run. !=============================================================================== - subroutine title() + subroutine title() bind(C) #ifdef _OPENMP use omp_lib @@ -336,7 +343,7 @@ contains ! write out entropy info if (entropy_on) write(UNIT=OUTPUT_UNIT, FMT='(3X, F8.5)', ADVANCE='NO') & - entropy % data(i) + entropy(i) if (n > 1) then write(UNIT=OUTPUT_UNIT, FMT='(3X, F8.5," +/-",F8.5)', ADVANCE='NO') & @@ -370,7 +377,7 @@ contains ! write out entropy info if (entropy_on) write(UNIT=OUTPUT_UNIT, FMT='(3X, F8.5)', ADVANCE='NO') & - entropy % data(i) + entropy(i) ! write out accumulated k-effective if after first active batch if (n > 1) then @@ -653,6 +660,10 @@ contains if (n_tallies == 0) return allocate(matches(n_filters)) + do i = 1, n_filters + allocate(matches(i) % bins) + allocate(matches(i) % weights) + end do ! Initialize names for scores score_names(abs(SCORE_FLUX)) = "Flux" @@ -848,6 +859,11 @@ contains close(UNIT=unit_tally) + do i = 1, n_filters + deallocate(matches(i) % bins) + deallocate(matches(i) % weights) + end do + end subroutine write_tallies !=============================================================================== diff --git a/src/output.cpp b/src/output.cpp index 7964a33c8..fba6e5af7 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -34,7 +34,7 @@ header(const char* msg, int level) { for (int i = 0; i < n_suffix; i++) out << '='; // Print header based on verbosity level. - if (openmc_verbosity >= level) { + if (settings::verbosity >= level) { std::cout << out.str() << std::endl << std::endl; } } diff --git a/src/particle.cpp b/src/particle.cpp index af8c1c64c..ca57c2faa 100644 --- a/src/particle.cpp +++ b/src/particle.cpp @@ -130,7 +130,7 @@ Particle::mark_as_lost(const char* message) openmc_n_lost_particles += 1; // Count the total number of simulated particles (on this processor) - auto n = openmc_current_batch * gen_per_batch * openmc_work; + auto n = openmc_current_batch * settings::gen_per_batch * openmc_work; // Abort the simulation if the maximum number of lost particles has been // reached @@ -141,14 +141,15 @@ Particle::mark_as_lost(const char* message) } void -Particle::write_restart() +Particle::write_restart() const { // Dont write another restart file if in particle restart mode - if (openmc_run_mode == RUN_MODE_PARTICLE) return; + if (settings::run_mode == RUN_MODE_PARTICLE) return; // Set up file name std::stringstream filename; - filename << path_output << "particle_" << openmc_current_batch << '_' << id << ".h5"; + filename << settings::path_output << "particle_" << openmc_current_batch + << '_' << id << ".h5"; #pragma omp critical (WriteParticleRestart) { @@ -165,10 +166,10 @@ Particle::write_restart() // Write data to file write_dataset(file_id, "current_batch", openmc_current_batch); - write_dataset(file_id, "generations_per_batch", gen_per_batch); + write_dataset(file_id, "generations_per_batch", settings::gen_per_batch); write_dataset(file_id, "current_generation", openmc_current_gen); - write_dataset(file_id, "n_particles", n_particles); - switch (openmc_run_mode) { + write_dataset(file_id, "n_particles", settings::n_particles); + switch (settings::run_mode) { case RUN_MODE_FIXEDSOURCE: write_dataset(file_id, "run_mode", "fixed source"); break; diff --git a/src/physics.F90 b/src/physics.F90 index 992e48767..29a806495 100644 --- a/src/physics.F90 +++ b/src/physics.F90 @@ -6,7 +6,6 @@ module physics use error, only: fatal_error, warning, write_message use material_header, only: Material, materials use math - use mesh_header, only: meshes use message_passing use nuclide_header use particle_header @@ -1182,11 +1181,18 @@ contains integer :: nu_d(MAX_DELAYED_GROUPS) ! number of delayed neutrons born integer :: i ! loop index integer :: nu ! actual number of neutrons produced - integer :: mesh_bin ! mesh bin for source site real(8) :: nu_t ! total nu real(8) :: weight ! weight adjustment for ufs method type(Nuclide), pointer :: nuc + interface + function ufs_get_weight(p) result(weight) bind(C) + import Particle, C_DOUBLE + type(Particle), intent(in) :: p + real(C_DOUBLE) :: WEIGHT + end function + end interface + ! Get pointers nuc => nuclides(i_nuclide) @@ -1196,20 +1202,7 @@ contains ! the expected number of fission sites produced if (ufs) then - associate (m => meshes(index_ufs_mesh)) - ! Determine indices on ufs mesh for current location - call m % get_bin(p % coord(1) % xyz, mesh_bin) - if (mesh_bin == NO_BIN_FOUND) then - call particle_write_restart(p) - call fatal_error("Source site outside UFS mesh!") - end if - - if (source_frac(1, mesh_bin) /= ZERO) then - weight = m % volume_frac / source_frac(1, mesh_bin) - else - weight = ONE - end if - end associate + weight = ufs_get_weight(p) else weight = ONE end if diff --git a/src/physics_mg.F90 b/src/physics_mg.F90 index 40df6e1cb..1d60b371e 100644 --- a/src/physics_mg.F90 +++ b/src/physics_mg.F90 @@ -7,7 +7,6 @@ module physics_mg use error, only: fatal_error, warning, write_message use material_header, only: Material, materials use math, only: rotate_angle - use mesh_header, only: meshes use mgxs_interface use message_passing use nuclide_header, only: material_xs @@ -168,33 +167,26 @@ contains integer :: dg ! delayed group integer :: gout ! group out integer :: nu ! actual number of neutrons produced - integer :: mesh_bin ! mesh bin for source site real(8) :: nu_t ! total nu real(8) :: mu ! fission neutron angular cosine real(8) :: phi ! fission neutron azimuthal angle real(8) :: weight ! weight adjustment for ufs method + interface + function ufs_get_weight(p) result(weight) bind(C) + import Particle, C_DOUBLE + type(Particle), intent(in) :: p + real(C_DOUBLE) :: WEIGHT + end function + end interface + ! TODO: Heat generation from fission ! If uniform fission source weighting is turned on, we increase of decrease ! the expected number of fission sites produced if (ufs) then - associate (m => meshes(index_ufs_mesh)) - ! Determine indices on ufs mesh for current location - call m % get_bin(p % coord(1) % xyz, mesh_bin) - - if (mesh_bin == NO_BIN_FOUND) then - call particle_write_restart(p) - call fatal_error("Source site outside UFS mesh!") - end if - - if (source_frac(1, mesh_bin) /= ZERO) then - weight = m % volume_frac / source_frac(1, mesh_bin) - else - weight = ONE - end if - end associate + weight = ufs_get_weight(p) else weight = ONE end if diff --git a/src/plot.F90 b/src/plot.F90 index 517c27fa1..bfc8bc225 100644 --- a/src/plot.F90 +++ b/src/plot.F90 @@ -9,6 +9,7 @@ module plot use hdf5_interface use output, only: time_stamp use material_header, only: materials + use mesh_header, only: meshes, RegularMesh use particle_header use plot_header use progress_header, only: ProgressBar @@ -184,7 +185,7 @@ contains !$omp end parallel do ! Draw tally mesh boundaries on the image if requested - if (associated(pl % meshlines_mesh)) call draw_mesh_lines(pl, data) + if (pl % index_meshlines_mesh >= 0) call draw_mesh_lines(pl, data) ! Write out the ppm to a file call output_ppm(pl, data) @@ -214,6 +215,7 @@ contains real(8) :: xyz_ur_plot(3) ! upper right xyz of plot image real(8) :: xyz_ll(3) ! lower left xyz real(8) :: xyz_ur(3) ! upper right xyz + type(RegularMesh) :: m rgb(:) = pl % meshlines_color % rgb @@ -239,57 +241,56 @@ contains width = xyz_ur_plot - xyz_ll_plot - associate (m => pl % meshlines_mesh) - call m % get_indices(xyz_ll_plot, ijk_ll(:m % n_dimension), in_mesh) - call m % get_indices(xyz_ur_plot, ijk_ur(:m % n_dimension), in_mesh) + m = meshes(pl % index_meshlines_mesh) + call m % get_indices(xyz_ll_plot, ijk_ll(:m % n_dimension()), in_mesh) + call m % get_indices(xyz_ur_plot, ijk_ur(:m % n_dimension()), in_mesh) - ! sweep through all meshbins on this plane and draw borders - do i = ijk_ll(outer), ijk_ur(outer) - do j = ijk_ll(inner), ijk_ur(inner) - ! check if we're in the mesh for this ijk - if (i > 0 .and. i <= m % dimension(outer) .and. & - j > 0 .and. j <= m % dimension(inner)) then + ! sweep through all meshbins on this plane and draw borders + do i = ijk_ll(outer), ijk_ur(outer) + do j = ijk_ll(inner), ijk_ur(inner) + ! check if we're in the mesh for this ijk + if (i > 0 .and. i <= m % dimension(outer) .and. & + j > 0 .and. j <= m % dimension(inner)) then - ! get xyz's of lower left and upper right of this mesh cell - xyz_ll(outer) = m % lower_left(outer) + m % width(outer) * (i - 1) - xyz_ll(inner) = m % lower_left(inner) + m % width(inner) * (j - 1) - xyz_ur(outer) = m % lower_left(outer) + m % width(outer) * i - xyz_ur(inner) = m % lower_left(inner) + m % width(inner) * j + ! get xyz's of lower left and upper right of this mesh cell + xyz_ll(outer) = m % lower_left(outer) + m % width(outer) * (i - 1) + xyz_ll(inner) = m % lower_left(inner) + m % width(inner) * (j - 1) + xyz_ur(outer) = m % lower_left(outer) + m % width(outer) * i + xyz_ur(inner) = m % lower_left(inner) + m % width(inner) * j - ! map the xyz ranges to pixel ranges + ! map the xyz ranges to pixel ranges - frac = (xyz_ll(outer) - xyz_ll_plot(outer)) / width(outer) - outrange(1) = int(frac * real(pl % pixels(1), 8)) - frac = (xyz_ur(outer) - xyz_ll_plot(outer)) / width(outer) - outrange(2) = int(frac * real(pl % pixels(1), 8)) + frac = (xyz_ll(outer) - xyz_ll_plot(outer)) / width(outer) + outrange(1) = int(frac * real(pl % pixels(1), 8)) + frac = (xyz_ur(outer) - xyz_ll_plot(outer)) / width(outer) + outrange(2) = int(frac * real(pl % pixels(1), 8)) - frac = (xyz_ur(inner) - xyz_ll_plot(inner)) / width(inner) - inrange(1) = int((ONE - frac) * real(pl % pixels(2), 8)) - frac = (xyz_ll(inner) - xyz_ll_plot(inner)) / width(inner) - inrange(2) = int((ONE - frac) * real(pl % pixels(2), 8)) + frac = (xyz_ur(inner) - xyz_ll_plot(inner)) / width(inner) + inrange(1) = int((ONE - frac) * real(pl % pixels(2), 8)) + frac = (xyz_ll(inner) - xyz_ll_plot(inner)) / width(inner) + inrange(2) = int((ONE - frac) * real(pl % pixels(2), 8)) - ! draw lines - do out_ = outrange(1), outrange(2) - do plus = 0, pl % meshlines_width - data(:, out_ + 1, inrange(1) + plus + 1) = rgb - data(:, out_ + 1, inrange(2) + plus + 1) = rgb - data(:, out_ + 1, inrange(1) - plus + 1) = rgb - data(:, out_ + 1, inrange(2) - plus + 1) = rgb - end do + ! draw lines + do out_ = outrange(1), outrange(2) + do plus = 0, pl % meshlines_width + data(:, out_ + 1, inrange(1) + plus + 1) = rgb + data(:, out_ + 1, inrange(2) + plus + 1) = rgb + data(:, out_ + 1, inrange(1) - plus + 1) = rgb + data(:, out_ + 1, inrange(2) - plus + 1) = rgb end do - do in_ = inrange(1), inrange(2) - do plus = 0, pl % meshlines_width - data(:, outrange(1) + plus + 1, in_ + 1) = rgb - data(:, outrange(2) + plus + 1, in_ + 1) = rgb - data(:, outrange(1) - plus + 1, in_ + 1) = rgb - data(:, outrange(2) - plus + 1, in_ + 1) = rgb - end do + end do + do in_ = inrange(1), inrange(2) + do plus = 0, pl % meshlines_width + data(:, outrange(1) + plus + 1, in_ + 1) = rgb + data(:, outrange(2) + plus + 1, in_ + 1) = rgb + data(:, outrange(1) - plus + 1, in_ + 1) = rgb + data(:, outrange(2) - plus + 1, in_ + 1) = rgb end do + end do - end if - end do + end if end do - end associate + end do end subroutine draw_mesh_lines diff --git a/src/plot_header.F90 b/src/plot_header.F90 index 1091bd73a..761481b33 100644 --- a/src/plot_header.F90 +++ b/src/plot_header.F90 @@ -4,7 +4,6 @@ module plot_header use constants use dict_header, only: DictIntInt - use mesh_header, only: RegularMesh implicit none @@ -31,7 +30,7 @@ module plot_header integer :: pixels(3) ! pixel width/height of plot slice integer :: meshlines_width ! pixel width of meshlines integer :: level ! universe depth to plot the cells of - type(RegularMesh), pointer :: meshlines_mesh => null() ! mesh to plot + integer :: index_meshlines_mesh = -1 ! index of mesh to plot type(ObjectColor) :: meshlines_color ! Color for meshlines type(ObjectColor) :: not_found ! color for positions where no cell found type(ObjectColor), allocatable :: colors(:) ! colors of cells/mats diff --git a/src/scattdata.cpp b/src/scattdata.cpp index 3e18c168f..8d7138624 100644 --- a/src/scattdata.cpp +++ b/src/scattdata.cpp @@ -4,6 +4,8 @@ #include #include +#include "xtensor/xbuilder.hpp" + #include "openmc/constants.h" #include "openmc/error.h" #include "openmc/math_functions.h" @@ -16,11 +18,11 @@ namespace openmc { //============================================================================== void -ScattData::base_init(int order, const int_1dvec& in_gmin, - const int_1dvec& in_gmax, const double_2dvec& in_energy, +ScattData::base_init(int order, const xt::xtensor& in_gmin, + const xt::xtensor& in_gmax, const double_2dvec& in_energy, const double_2dvec& in_mult) { - int groups = in_energy.size(); + size_t groups = in_energy.size(); gmin = in_gmin; gmax = in_gmax; @@ -51,18 +53,17 @@ ScattData::base_init(int order, const int_1dvec& in_gmin, //============================================================================== void -ScattData::base_combine(int max_order, - const std::vector& those_scatts, const double_1dvec& scalars, - int_1dvec& in_gmin, int_1dvec& in_gmax, double_2dvec& sparse_mult, +ScattData::base_combine(size_t max_order, + const std::vector& those_scatts, const std::vector& scalars, + xt::xtensor& in_gmin, xt::xtensor& in_gmax, double_2dvec& sparse_mult, double_3dvec& sparse_scatter) { - int groups = those_scatts[0] -> energy.size(); + size_t groups = those_scatts[0] -> energy.size(); // Now allocate and zero our storage spaces - double_3dvec this_matrix = double_3dvec(groups, double_2dvec(groups, - double_1dvec(max_order, 0.))); - double_2dvec mult_numer(groups, double_1dvec(groups, 0.)); - double_2dvec mult_denom(groups, double_1dvec(groups, 0.)); + xt::xtensor this_matrix({groups, groups, max_order}, 0.); + xt::xtensor mult_numer({groups, groups}, 0.); + xt::xtensor mult_denom({groups, groups}, 0.); // Build the dense scattering and multiplicity matrices // Get the multiplicity_matrix @@ -80,26 +81,26 @@ ScattData::base_combine(int max_order, ScattData* that = those_scatts[i]; // Build the dense matrix for that object - double_3dvec that_matrix = that->get_matrix(max_order); + xt::xtensor that_matrix = that->get_matrix(max_order); // Now add that to this for the scattering and multiplicity for (int gin = 0; gin < groups; gin++) { // Only spend time adding that's gmin to gmax data since the rest will // be zeros int i_gout = 0; - for (int gout = that->gmin[gin]; gout <= that->gmax[gin]; gout++) { + for (int gout = that->gmin(gin); gout <= that->gmax(gin); gout++) { // Do the scattering matrix for (int l = 0; l < max_order; l++) { - this_matrix[gin][gout][l] += scalars[i] * that_matrix[gin][gout][l]; + this_matrix(gin, gout, l) += scalars[i] * that_matrix(gin, gout, l); } // Incorporate that's contribution to the multiplicity matrix data - double nuscatt = that->scattxs[gin] * that->energy[gin][i_gout]; - mult_numer[gin][gout] += scalars[i] * nuscatt; + double nuscatt = that->scattxs(gin) * that->energy[gin][i_gout]; + mult_numer(gin, gout) += scalars[i] * nuscatt; if (that->mult[gin][i_gout] > 0.) { - mult_denom[gin][gout] += scalars[i] * nuscatt / that->mult[gin][i_gout]; + mult_denom(gin, gout) += scalars[i] * nuscatt / that->mult[gin][i_gout]; } else { - mult_denom[gin][gout] += scalars[i]; + mult_denom(gin, gout) += scalars[i]; } i_gout++; } @@ -107,16 +108,8 @@ ScattData::base_combine(int max_order, } // Combine mult_numer and mult_denom into the combined multiplicity matrix - double_2dvec this_mult(groups, double_1dvec(groups, 1.)); - for (int gin = 0; gin < groups; gin++) { - for (int gout = 0; gout < groups; gout++) { - if (mult_denom[gin][gout] > 0.) { - this_mult[gin][gout] = mult_numer[gin][gout] / mult_denom[gin][gout]; - } - } - } - mult_numer.clear(); - mult_denom.clear(); + xt::xtensor this_mult({groups, groups}, 1.); + this_mult = xt::nan_to_num(mult_numer / mult_denom); // We have the data, now we need to convert to a jagged array and then use // the initialize function to store it on the object. @@ -125,8 +118,8 @@ ScattData::base_combine(int max_order, int gmin_; for (gmin_ = 0; gmin_ < groups; gmin_++) { bool non_zero = false; - for (int l = 0; l < this_matrix[gin][gmin_].size(); l++) { - if (this_matrix[gin][gmin_][l] != 0.) { + for (int l = 0; l < this_matrix.shape()[2]; l++) { + if (this_matrix(gin, gmin_, l) != 0.) { non_zero = true; break; } @@ -136,8 +129,8 @@ ScattData::base_combine(int max_order, int gmax_; for (gmax_ = groups - 1; gmax_ >= 0; gmax_--) { bool non_zero = false; - for (int l = 0; l < this_matrix[gin][gmax_].size(); l++) { - if (this_matrix[gin][gmax_][l] != 0.) { + for (int l = 0; l < this_matrix.shape()[2]; l++) { + if (this_matrix(gin, gmax_, l) != 0.) { non_zero = true; break; } @@ -160,8 +153,11 @@ ScattData::base_combine(int max_order, sparse_mult[gin].resize(gmax_ - gmin_ + 1); int i_gout = 0; for (int gout = gmin_; gout <= gmax_; gout++) { - sparse_scatter[gin][i_gout] = this_matrix[gin][gout]; - sparse_mult[gin][i_gout] = this_mult[gin][gout]; + sparse_scatter[gin][i_gout].resize(this_matrix.shape()[2]); + for (int l = 0; l < this_matrix.shape()[2]; l++) { + sparse_scatter[gin][i_gout][l] = this_matrix(gin, gout, l); + } + sparse_mult[gin][i_gout] = this_mult(gin, gout); i_gout++; } } @@ -241,21 +237,21 @@ ScattData::get_xs(int xstype, int gin, const int* gout, const double* mu) //============================================================================== void -ScattDataLegendre::init(const int_1dvec& in_gmin, const int_1dvec& in_gmax, - const double_2dvec& in_mult, const double_3dvec& coeffs) +ScattDataLegendre::init(const xt::xtensor& in_gmin, + const xt::xtensor& in_gmax, const double_2dvec& in_mult, + const double_3dvec& coeffs) { - int groups = coeffs.size(); - int order = coeffs[0][0].size(); + size_t groups = coeffs.size(); + size_t order = coeffs[0][0].size(); // make a copy of coeffs that we can use to both extract data and normalize double_3dvec matrix = coeffs; // Get the scattering cross section value by summing the un-normalized P0 // coefficient in the variable matrix over all outgoing groups. - scattxs.resize(groups); + scattxs = xt::zeros({groups}); for (int gin = 0; gin < groups; gin++) { int num_groups = in_gmax[gin] - in_gmin[gin] + 1; - scattxs[gin] = 0.; for (int i_gout = 0; i_gout < num_groups; i_gout++) { scattxs[gin] += matrix[gin][i_gout][0]; } @@ -301,7 +297,7 @@ ScattDataLegendre::init(const int_1dvec& in_gmin, const int_1dvec& in_gmax, void ScattDataLegendre::update_max_val() { - int groups = max_val.size(); + size_t groups = max_val.size(); // Step through the polynomial with fixed number of points to identify the // maximal value int Nmu = 1001; @@ -384,25 +380,25 @@ ScattDataLegendre::sample(int gin, int& gout, double& mu, double& wgt) void ScattDataLegendre::combine(const std::vector& those_scatts, - const double_1dvec& scalars) + const std::vector& scalars) { // Find the max order in the data set and make sure we can combine the sets - int max_order = 0; + size_t max_order = 0; for (int i = 0; i < those_scatts.size(); i++) { // Lets also make sure these items are combineable ScattDataLegendre* that = dynamic_cast(those_scatts[i]); if (!that) { fatal_error("Cannot combine the ScattData objects!"); } - int that_order = that->get_order(); + size_t that_order = that->get_order(); if (that_order > max_order) max_order = that_order; } max_order++; // Add one since this is a Legendre - int groups = those_scatts[0] -> energy.size(); + size_t groups = those_scatts[0] -> energy.size(); - int_1dvec in_gmin(groups); - int_1dvec in_gmax(groups); + xt::xtensor in_gmin({groups}, 0); + xt::xtensor in_gmax({groups}, 0); double_3dvec sparse_scatter(groups); double_2dvec sparse_mult(groups); @@ -418,20 +414,19 @@ ScattDataLegendre::combine(const std::vector& those_scatts, //============================================================================== -double_3dvec -ScattDataLegendre::get_matrix(int max_order) +xt::xtensor +ScattDataLegendre::get_matrix(size_t max_order) { // Get the sizes and initialize the data to 0 - int groups = energy.size(); - int order_dim = max_order + 1; - double_3dvec matrix = double_3dvec(groups, double_2dvec(groups, - double_1dvec(order_dim, 0.))); + size_t groups = energy.size(); + size_t order_dim = max_order + 1; + xt::xtensor matrix({groups, groups, order_dim}, 0.); for (int gin = 0; gin < groups; gin++) { for (int i_gout = 0; i_gout < energy[gin].size(); i_gout++) { int gout = i_gout + gmin[gin]; for (int l = 0; l < order_dim; l++) { - matrix[gin][gout][l] = scattxs[gin] * energy[gin][i_gout] * + matrix(gin, gout, l) = scattxs[gin] * energy[gin][i_gout] * dist[gin][i_gout][l]; } } @@ -444,20 +439,20 @@ ScattDataLegendre::get_matrix(int max_order) //============================================================================== void -ScattDataHistogram::init(const int_1dvec& in_gmin, const int_1dvec& in_gmax, - const double_2dvec& in_mult, const double_3dvec& coeffs) +ScattDataHistogram::init(const xt::xtensor& in_gmin, + const xt::xtensor& in_gmax, const double_2dvec& in_mult, + const double_3dvec& coeffs) { - int groups = coeffs.size(); - int order = coeffs[0][0].size(); + size_t groups = coeffs.size(); + size_t order = coeffs[0][0].size(); // make a copy of coeffs that we can use to both extract data and normalize double_3dvec matrix = coeffs; // Get the scattering cross section value by summing the distribution // over all the histogram bins in angle and outgoing energy groups - scattxs.resize(groups); + scattxs = xt::zeros({groups}); for (int gin = 0; gin < groups; gin++) { - scattxs[gin] = 0.; for (int i_gout = 0; i_gout < matrix[gin].size(); i_gout++) { scattxs[gin] += std::accumulate(matrix[gin][i_gout].begin(), matrix[gin][i_gout].end(), 0.); @@ -484,12 +479,8 @@ ScattDataHistogram::init(const int_1dvec& in_gmin, const int_1dvec& in_gmax, ScattData::base_init(order, in_gmin, in_gmax, in_energy, in_mult); // Build the angular distribution mu values - mu = double_1dvec(order); + mu = xt::linspace(-1., 1., order + 1); dmu = 2. / order; - mu[0] = -1.; - for (int imu = 1; imu < order; imu++) { - mu[imu] = -1. + imu * dmu; - } // Calculate f(mu) and integrate it so we can avoid rejection sampling fmu.resize(groups); @@ -534,7 +525,7 @@ ScattDataHistogram::calc_f(int gin, int gout, double mu) int imu; if (mu == 1.) { // use size -2 to have the index one before the end - imu = this->mu.size() - 2; + imu = this->mu.shape()[0] - 2; } else { imu = std::floor((mu + 1.) / dmu + 1.) - 1; } @@ -560,7 +551,6 @@ ScattDataHistogram::sample(int gin, int& gout, double& mu, double& wgt) if (xi < dist[gin][i_gout][0]) { imu = 0; } else { - // TODO lower_bound? + 1? imu = std::upper_bound(dist[gin][i_gout].begin(), dist[gin][i_gout].end(), xi) - dist[gin][i_gout].begin(); @@ -581,21 +571,20 @@ ScattDataHistogram::sample(int gin, int& gout, double& mu, double& wgt) //============================================================================== -double_3dvec -ScattDataHistogram::get_matrix(int max_order) +xt::xtensor +ScattDataHistogram::get_matrix(size_t max_order) { // Get the sizes and initialize the data to 0 - int groups = energy.size(); + size_t groups = energy.size(); // We ignore the requested order for Histogram and Tabular representations - int order_dim = get_order(); - double_3dvec matrix = double_3dvec(groups, double_2dvec(groups, - double_1dvec(order_dim, 0.))); + size_t order_dim = get_order(); + xt::xtensor matrix({groups, groups, order_dim}, 0); for (int gin = 0; gin < groups; gin++) { for (int i_gout = 0; i_gout < energy[gin].size(); i_gout++) { int gout = i_gout + gmin[gin]; for (int l = 0; l < order_dim; l++) { - matrix[gin][gout][l] = scattxs[gin] * energy[gin][i_gout] * + matrix(gin, gout, l) = scattxs[gin] * energy[gin][i_gout] * fmu[gin][i_gout][l]; } } @@ -607,10 +596,10 @@ ScattDataHistogram::get_matrix(int max_order) void ScattDataHistogram::combine(const std::vector& those_scatts, - const double_1dvec& scalars) + const std::vector& scalars) { // Find the max order in the data set and make sure we can combine the sets - int max_order = those_scatts[0]->get_order(); + size_t max_order = those_scatts[0]->get_order(); for (int i = 0; i < those_scatts.size(); i++) { // Lets also make sure these items are combineable ScattDataHistogram* that = dynamic_cast(those_scatts[i]); @@ -622,10 +611,10 @@ ScattDataHistogram::combine(const std::vector& those_scatts, } } - int groups = those_scatts[0] -> energy.size(); + size_t groups = those_scatts[0] -> energy.size(); - int_1dvec in_gmin(groups); - int_1dvec in_gmax(groups); + xt::xtensor in_gmin({groups}, 0); + xt::xtensor in_gmax({groups}, 0); double_3dvec sparse_scatter(groups); double_2dvec sparse_mult(groups); @@ -633,7 +622,7 @@ ScattDataHistogram::combine(const std::vector& those_scatts, // so we use a base class method to sum up xs and create new energy and mult // matrices ScattData::base_combine(max_order, those_scatts, scalars, in_gmin, in_gmax, - sparse_mult, sparse_scatter); + sparse_mult, sparse_scatter); // Got everything we need, store it. init(in_gmin, in_gmax, sparse_mult, sparse_scatter); @@ -644,29 +633,24 @@ ScattDataHistogram::combine(const std::vector& those_scatts, //============================================================================== void -ScattDataTabular::init(const int_1dvec& in_gmin, const int_1dvec& in_gmax, - const double_2dvec& in_mult, const double_3dvec& coeffs) +ScattDataTabular::init(const xt::xtensor& in_gmin, + const xt::xtensor& in_gmax, const double_2dvec& in_mult, + const double_3dvec& coeffs) { - int groups = coeffs.size(); - int order = coeffs[0][0].size(); + size_t groups = coeffs.size(); + size_t order = coeffs[0][0].size(); // make a copy of coeffs that we can use to both extract data and normalize double_3dvec matrix = coeffs; // Build the angular distribution mu values - mu = double_1dvec(order); + mu = xt::linspace(-1., 1., order); dmu = 2. / (order - 1); - mu[0] = -1.; - for (int imu = 1; imu < order - 1; imu++) { - mu[imu] = -1. + imu * dmu; - } - mu[order - 1] = 1.; // Get the scattering cross section value by integrating the distribution // over all mu points and then combining over all outgoing groups - scattxs.resize(groups); + scattxs = xt::zeros({groups}); for (int gin = 0; gin < groups; gin++) { - scattxs[gin] = 0.; for (int i_gout = 0; i_gout < matrix[gin].size(); i_gout++) { for (int imu = 1; imu < order; imu++) { scattxs[gin] += 0.5 * dmu * (matrix[gin][i_gout][imu - 1] + @@ -743,7 +727,7 @@ ScattDataTabular::calc_f(int gin, int gout, double mu) int imu; if (mu == 1.) { // use size -2 to have the index one before the end - imu = this->mu.size() - 2; + imu = this->mu.shape()[0] - 2; } else { imu = std::floor((mu + 1.) / dmu + 1.) - 1; } @@ -764,7 +748,7 @@ ScattDataTabular::sample(int gin, int& gout, double& mu, double& wgt) sample_energy(gin, gout, i_gout); // Determine the outgoing cosine bin - int NP = this->mu.size(); + int NP = this->mu.shape()[0]; double xi = prn(); double c_k = dist[gin][i_gout][0]; @@ -804,21 +788,20 @@ ScattDataTabular::sample(int gin, int& gout, double& mu, double& wgt) //============================================================================== -double_3dvec -ScattDataTabular::get_matrix(int max_order) +xt::xtensor +ScattDataTabular::get_matrix(size_t max_order) { // Get the sizes and initialize the data to 0 - int groups = energy.size(); + size_t groups = energy.size(); // We ignore the requested order for Histogram and Tabular representations - int order_dim = get_order(); - double_3dvec matrix = double_3dvec(groups, double_2dvec(groups, - double_1dvec(order_dim, 0.))); + size_t order_dim = get_order(); + xt::xtensor matrix({groups, groups, order_dim}, 0.); for (int gin = 0; gin < groups; gin++) { for (int i_gout = 0; i_gout < energy[gin].size(); i_gout++) { int gout = i_gout + gmin[gin]; for (int l = 0; l < order_dim; l++) { - matrix[gin][gout][l] = scattxs[gin] * energy[gin][i_gout] * + matrix(gin, gout, l) = scattxs[gin] * energy[gin][i_gout] * fmu[gin][i_gout][l]; } } @@ -830,10 +813,10 @@ ScattDataTabular::get_matrix(int max_order) void ScattDataTabular::combine(const std::vector& those_scatts, - const double_1dvec& scalars) + const std::vector& scalars) { // Find the max order in the data set and make sure we can combine the sets - int max_order = those_scatts[0]->get_order(); + size_t max_order = those_scatts[0]->get_order(); for (int i = 0; i < those_scatts.size(); i++) { // Lets also make sure these items are combineable ScattDataTabular* that = dynamic_cast(those_scatts[i]); @@ -845,10 +828,10 @@ ScattDataTabular::combine(const std::vector& those_scatts, } } - int groups = those_scatts[0] -> energy.size(); + size_t groups = those_scatts[0] -> energy.size(); - int_1dvec in_gmin(groups); - int_1dvec in_gmax(groups); + xt::xtensor in_gmin({groups}, 0); + xt::xtensor in_gmax({groups}, 0); double_3dvec sparse_scatter(groups); double_2dvec sparse_mult(groups); @@ -856,7 +839,7 @@ ScattDataTabular::combine(const std::vector& those_scatts, // so we use a base class method to sum up xs and create new energy and mult // matrices ScattData::base_combine(max_order, those_scatts, scalars, in_gmin, in_gmax, - sparse_mult, sparse_scatter); + sparse_mult, sparse_scatter); // Got everything we need, store it. init(in_gmin, in_gmax, sparse_mult, sparse_scatter); @@ -885,16 +868,11 @@ convert_legendre_to_tabular(ScattDataLegendre& leg, ScattDataTabular& tab, tab.scattxs = leg.scattxs; // Build mu and dmu - tab.mu = double_1dvec(n_mu); + tab.mu = xt::linspace(-1., 1., n_mu); tab.dmu = 2. / (n_mu - 1); - tab.mu[0] = -1.; - for (int imu = 1; imu < n_mu - 1; imu++) { - tab.mu[imu] = -1. + imu * tab.dmu; - } - tab.mu[n_mu - 1] = 1.; // Calculate f(mu) and integrate it so we can avoid rejection sampling - int groups = tab.energy.size(); + size_t groups = tab.energy.size(); tab.fmu.resize(groups); for (int gin = 0; gin < groups; gin++) { int num_groups = tab.gmax[gin] - tab.gmin[gin] + 1; diff --git a/src/settings.F90 b/src/settings.F90 index e198dfa2e..ec7bbf2c8 100644 --- a/src/settings.F90 +++ b/src/settings.F90 @@ -9,106 +9,103 @@ module settings ! ============================================================================ ! ENERGY TREATMENT RELATED VARIABLES - logical(C_BOOL), bind(C, name='openmc_run_CE') :: run_CE = .true. ! Run in CE mode? + logical(C_BOOL), bind(C, name='run_CE') :: run_CE ! Run in CE mode? ! ============================================================================ ! CONTINUOUS-ENERGY CROSS SECTION RELATED VARIABLES ! Unreoslved resonance probablity tables - logical :: urr_ptables_on = .true. + logical(C_BOOL), bind(C) :: urr_ptables_on ! Default temperature and method for choosing temperatures - integer(C_INT) :: temperature_method = TEMPERATURE_NEAREST - logical :: temperature_multipole = .false. - real(C_DOUBLE) :: temperature_tolerance = 10.0_8 - real(C_DOUBLE) :: temperature_default = 293.6_8 - real(8) :: temperature_range(2) = [ZERO, ZERO] + integer(C_INT), bind(C) :: temperature_method + logical(C_BOOL), bind(C) :: temperature_multipole + real(C_DOUBLE), bind(C) :: temperature_tolerance + real(C_DOUBLE), bind(C) :: temperature_default + real(C_DOUBLE), bind(C) :: temperature_range(2) - integer :: n_log_bins ! number of bins for logarithmic grid + integer(C_INT), bind(C) :: n_log_bins ! number of bins for logarithmic grid - logical(C_BOOL), bind(C, name='openmc_photon_transport') :: photon_transport = .false. - integer :: electron_treatment = ELECTRON_TTB + logical(C_BOOL), bind(C) :: photon_transport + integer(C_INT), bind(C) :: electron_treatment ! ============================================================================ ! MULTI-GROUP CROSS SECTION RELATED VARIABLES ! Maximum Data Order - integer(C_INT) :: max_order + integer(C_INT), bind(C) :: max_order ! Whether or not to convert Legendres to tabulars - logical :: legendre_to_tabular = .true. + logical(C_BOOL), bind(C) :: legendre_to_tabular ! Number of points to use in the Legendre to tabular conversion - integer(C_INT) :: legendre_to_tabular_points = C_NONE + integer(C_INT), bind(C) :: legendre_to_tabular_points ! ============================================================================ ! SIMULATION VARIABLES ! Assume all tallies are spatially distinct - logical :: assume_separate = .false. + logical(C_BOOL), bind(C) :: assume_separate ! Use confidence intervals for results instead of standard deviations - logical :: confidence_intervals = .false. + logical(C_BOOL), bind(C) :: confidence_intervals - integer(C_INT64_T), bind(C) :: n_particles = 0 ! # of particles per generation - integer(C_INT32_T), bind(C) :: n_batches ! # of batches - integer(C_INT32_T), bind(C) :: n_inactive ! # of inactive batches - integer(C_INT32_T), bind(C) :: gen_per_batch = 1 ! # of generations per batch + integer(C_INT64_T), bind(C) :: n_particles ! # of particles per generation + integer(C_INT32_T), bind(C) :: n_batches ! # of batches + integer(C_INT32_T), bind(C) :: n_inactive ! # of inactive batches + integer(C_INT32_T), bind(C) :: gen_per_batch ! # of generations per batch - integer :: n_max_batches ! max # of batches - integer :: n_batch_interval = 1 ! batch interval for triggers - logical :: pred_batches = .false. ! predict batches for triggers - logical :: trigger_on = .false. ! flag for turning triggers on/off + integer(C_INT), bind(C) :: n_max_batches ! max # of batches + integer(C_INT), bind(C, name='trigger_batch_interval') :: n_batch_interval ! batch interval for triggers + logical(C_BOOL), bind(C, name='trigger_predict') :: pred_batches ! predict batches for triggers + logical(C_BOOL), bind(C) :: trigger_on ! flag for turning triggers on/off - logical :: entropy_on = .false. - integer :: index_entropy_mesh = -1 + logical(C_BOOL), bind(C) :: entropy_on + integer(C_INT32_T), bind(C) :: index_entropy_mesh - logical :: ufs = .false. - integer :: index_ufs_mesh = -1 + logical(C_BOOL), bind(C, name='ufs_on') :: ufs + integer(C_INT32_T), bind(C) :: index_ufs_mesh ! Write source at end of simulation - logical :: source_separate = .false. - logical :: source_write = .true. - logical :: source_latest = .false. + logical(C_BOOL), bind(C) :: source_separate + logical(C_BOOL), bind(C) :: source_write + logical(C_BOOL), bind(C) :: source_latest ! Variance reduction settins - logical :: survival_biasing = .false. - real(8) :: weight_cutoff = 0.25_8 - real(8) :: energy_cutoff(4) = [ZERO, 1000.0_8, ZERO, ZERO] - real(8) :: weight_survive = ONE + logical(C_BOOL), bind(C) :: survival_biasing + real(C_DOUBLE), bind(C) :: weight_cutoff + real(C_DOUBLE), bind(C) :: energy_cutoff(4) + real(C_DOUBLE), bind(C) :: weight_survive ! Mode to run in (fixed source, eigenvalue, plotting, etc) - integer(C_INT), bind(C, name='openmc_run_mode') :: run_mode = NONE + integer(C_INT), bind(C) :: run_mode ! Restart run - logical(C_BOOL), bind(C, name='openmc_restart_run') :: restart_run = .false. + logical(C_BOOL), bind(C) :: restart_run ! The verbosity controls how much information will be printed to the screen ! and in logs - integer(C_INT), bind(C, name='openmc_verbosity') :: verbosity = 7 + integer(C_INT), bind(C) :: verbosity - logical(C_BOOL), bind(C, name='openmc_check_overlaps') :: check_overlaps = .false. + logical(C_BOOL), bind(C) :: check_overlaps ! Trace for single particle - integer :: trace_batch - integer :: trace_gen - integer(8) :: trace_particle + integer(C_INT), bind(C) :: trace_batch + integer(C_INT), bind(C) :: trace_gen + integer(C_INT64_T), bind(C) :: trace_particle ! Particle tracks - logical(C_BOOL), bind(C, name='openmc_write_all_tracks') :: & - write_all_tracks = .false. + logical(C_BOOL), bind(C) :: write_all_tracks integer, allocatable :: track_identifiers(:,:) ! Particle restart run - logical(C_BOOL), bind(C, name='openmc_particle_restart_run') :: & - particle_restart_run = .false. + logical(C_BOOL), bind(C) :: particle_restart_run ! Write out initial source - logical(C_BOOL), bind(C, name='openmc_write_initial_source') :: & - write_initial_source = .false. + logical(C_BOOL), bind(C) :: write_initial_source ! Whether create fission neutrons or not. Only applied for MODE_FIXEDSOURCE - logical :: create_fission_neutrons = .true. + logical(C_BOOL), bind(C) :: create_fission_neutrons ! Information about state points to be written integer :: n_state_points = 0 @@ -127,21 +124,21 @@ module settings character(MAX_FILE_LEN) :: path_output = '' ! Path to output directory ! Various output options - logical :: output_summary = .true. - logical :: output_tallies = .true. + logical(C_BOOL), bind(C) :: output_summary + logical(C_BOOL), bind(C) :: output_tallies ! Resonance scattering settings - logical :: res_scat_on = .false. ! is resonance scattering treated? - integer :: res_scat_method = RES_SCAT_ARES ! resonance scattering method - real(8) :: res_scat_energy_min = 0.01_8 - real(8) :: res_scat_energy_max = 1000.0_8 + logical(C_BOOL), bind(C) :: res_scat_on ! is resonance scattering treated? + integer(C_INT), bind(C) :: res_scat_method ! resonance scattering method + real(C_DOUBLE), bind(C) :: res_scat_energy_min + real(C_DOUBLE), bind(C) :: res_scat_energy_max character(10), allocatable :: res_scat_nuclides(:) ! Is CMFD active - logical :: cmfd_run = .false. + logical(C_BOOL), bind(C) :: cmfd_run ! No reduction at end of batch - logical :: reduce_tallies = .true. + logical(C_BOOL), bind(C) :: reduce_tallies contains diff --git a/src/settings.cpp b/src/settings.cpp index aafa0fb23..007c515b0 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1,11 +1,22 @@ #include "openmc/settings.h" +#include // for ceil, pow +#include // for numeric_limits +#include +#include + +#include + #include "openmc/capi.h" #include "openmc/constants.h" #include "openmc/distribution.h" #include "openmc/distribution_multi.h" #include "openmc/distribution_spatial.h" #include "openmc/error.h" +#include "openmc/file_utils.h" +#include "openmc/mesh.h" +#include "openmc/output.h" +#include "openmc/random_lcg.h" #include "openmc/source.h" #include "openmc/string_utils.h" #include "openmc/xml_interface.h" @@ -16,98 +27,380 @@ namespace openmc { // Global variables //============================================================================== -char* openmc_path_input; -char* openmc_path_statepoint; -char* openmc_path_sourcepoint; -char* openmc_path_particle_restart; +namespace settings { + +// Default values for boolean flags +bool assume_separate {false}; +bool check_overlaps {false}; +bool cmfd_run {false}; +bool confidence_intervals {false}; +bool create_fission_neutrons {true}; +bool entropy_on {false}; +bool legendre_to_tabular {true}; +bool output_summary {true}; +bool output_tallies {true}; +bool particle_restart_run {false}; +bool photon_transport {false}; +bool reduce_tallies {true}; +bool res_scat_on {false}; +bool restart_run {false}; +bool run_CE {true}; +bool source_latest {false}; +bool source_separate {false}; +bool source_write {true}; +bool survival_biasing {false}; +bool temperature_multipole {false}; +bool trigger_on {false}; +bool trigger_predict {false}; +bool ufs_on {false}; +bool urr_ptables_on {true}; +bool write_all_tracks {false}; +bool write_initial_source {false}; + std::string path_cross_sections; +std::string path_input; std::string path_multipole; std::string path_output; +std::string path_particle_restart; std::string path_source; +std::string path_sourcepoint; +std::string path_statepoint; +int32_t index_entropy_mesh {-1}; +int32_t index_ufs_mesh {-1}; + +int32_t n_batches; +int32_t n_inactive {0}; +int32_t gen_per_batch {1}; +int64_t n_particles {-1}; + +int electron_treatment {ELECTRON_TTB}; +double energy_cutoff[4] {0.0, 1000.0, 0.0, 0.0}; +int legendre_to_tabular_points {C_NONE}; +int max_order {0}; +int n_log_bins {8000}; +int n_max_batches; +int res_scat_method {RES_SCAT_ARES}; +double res_scat_energy_min {0.01}; +double res_scat_energy_max {1000.0}; +int run_mode {-1}; int temperature_method {TEMPERATURE_NEAREST}; -bool temperature_multipole {false}; double temperature_tolerance {10.0}; double temperature_default {293.6}; -std::array temperature_range {0.0, 0.0}; +double temperature_range[2] {0.0, 0.0}; +int trace_batch; +int trace_gen; +int64_t trace_particle; +int trigger_batch_interval {1}; +int verbosity {7}; +double weight_cutoff {0.25}; +double weight_survive {1.0}; + +// TODO: Move to separate file +struct KTrigger { + int type; + double threshold; +}; +extern "C" KTrigger keff_trigger; + +} // namespace settings //============================================================================== // Functions //============================================================================== -void read_settings(pugi::xml_node* root) +void get_run_parameters(pugi::xml_node node_base) { + using namespace settings; + using namespace pugi; + + // Check number of particles + if (!check_for_node(node_base, "particles")) { + fatal_error("Need to specify number of particles."); + } + + // Get number of particles if it wasn't specified as a command-line argument + if (n_particles == -1) { + n_particles = std::stoll(get_node_value(node_base, "particles")); + } + + // Get number of basic batches + if (check_for_node(node_base, "batches")) { + n_batches = std::stoi(get_node_value(node_base, "batches")); + } + if (!trigger_on) n_max_batches = n_batches; + + // Get number of inactive batches + if (run_mode == RUN_MODE_EIGENVALUE) { + if (check_for_node(node_base, "inactive")) { + n_inactive = std::stoi(get_node_value(node_base, "inactive")); + } + if (check_for_node(node_base, "generations_per_batch")) { + gen_per_batch = std::stoi(get_node_value(node_base, "generations_per_batch")); + } + + // TODO: Preallocate space for keff and entropy by generation + + // Get the trigger information for keff + if (check_for_node(node_base, "keff_trigger")) { + xml_node node_keff_trigger = node_base.child("keff_trigger"); + + if (check_for_node(node_keff_trigger, "type")) { + auto temp = get_node_value(node_keff_trigger, "type", true, true); + if (temp == "std_dev") { + keff_trigger.type = STANDARD_DEVIATION; + } else if (temp == "variance") { + keff_trigger.type = VARIANCE; + } else if (temp == "rel_err") { + keff_trigger.type = RELATIVE_ERROR; + } else { + fatal_error("Unrecognized keff trigger type " + temp); + } + } else { + fatal_error("Specify keff trigger type in settings XML"); + } + + if (check_for_node(node_keff_trigger, "threshold")) { + keff_trigger.threshold = std::stod(get_node_value( + node_keff_trigger, "threshold")); + } else { + fatal_error("Specify keff trigger threshold in settings XML"); + } + } + } +} + +void read_settings_xml() +{ + using namespace settings; + using namespace pugi; + + // Check if settings.xml exists + std::string filename = std::string(path_input) + "settings.xml"; + if (!file_exists(filename)) { + if (run_mode != RUN_MODE_PLOTTING) { + std::stringstream msg; + msg << "Settings XML file '" << filename << "' 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); + } else { + // The settings.xml file is optional if we just want to make a plot. + return; + } + } + + // Parse settings.xml file + xml_document doc; + auto result = doc.load_file("settings.xml"); + if (!result) { + fatal_error("Error processing settings.xml file."); + } + + // Get root element + xml_node root = doc.document_element(); + + // Verbosity + if (check_for_node(root, "verbosity")) { + verbosity = std::stoi(get_node_value(root, "verbosity")); + } + + // To this point, we haven't displayed any output since we didn't know what + // the verbosity is. Now that we checked for it, show the title if necessary + if (openmc_master) { + if (verbosity >= 2) title(); + } + write_message("Reading settings XML file...", 5); + + // Find if a multi-group or continuous-energy simulation is desired + if (check_for_node(root, "energy_mode")) { + std::string temp_str = get_node_value(root, "energy_mode", true, true); + if (temp_str == "mg" || temp_str == "multi-group") { + run_CE = false; + } else if (temp_str == "ce" || temp_str == "continuous-energy") { + run_CE = true; + } + } + // Look for deprecated cross_sections.xml file in settings.xml - if (check_for_node(*root, "cross_sections")) { + if (check_for_node(root, "cross_sections")) { warning("Setting cross_sections in settings.xml has been deprecated." " The cross_sections are now set in materials.xml and the " "cross_sections input to materials.xml and the OPENMC_CROSS_SECTIONS" " environment variable will take precendent over setting " "cross_sections in settings.xml."); - path_cross_sections = get_node_value(*root, "cross_sections"); + path_cross_sections = get_node_value(root, "cross_sections"); } // Look for deprecated windowed_multipole file in settings.xml - if (openmc_run_mode != RUN_MODE_PLOTTING) { - if (check_for_node(*root, "multipole_library")) { + if (run_mode != RUN_MODE_PLOTTING) { + if (check_for_node(root, "multipole_library")) { warning("Setting multipole_library in settings.xml has been " "deprecated. The multipole_library is now set in materials.xml and" " the multipole_library input to materials.xml and the " "OPENMC_MULTIPOLE_LIBRARY environment variable will take " "precendent over setting multipole_library in settings.xml."); - path_multipole = get_node_value(*root, "multipole_library"); + path_multipole = get_node_value(root, "multipole_library"); } if (!ends_with(path_multipole, "/")) { path_multipole += "/"; } } - // Check for output options - if (check_for_node(*root, "output")) { + if (!run_CE) { + // Scattering Treatments + if (check_for_node(root, "max_order")) { + max_order = std::stoi(get_node_value(root, "max_order")); + } else { + // Set to default of largest int - 1, which means to use whatever is + // contained in library. This is largest int - 1 because for legendre + // scattering, a value of 1 is added to the order; adding 1 to the largest + // int gets you the largest negative integer, which is not what we want. + max_order = std::numeric_limits::max() - 1; + } + } - // Get pointer to output node - pugi::xml_node node_output = root->child("output"); + // Check for a trigger node and get trigger information + if (check_for_node(root, "trigger")) { + xml_node node_trigger = root.child("trigger"); - // Set output directory if a path has been specified - if (check_for_node(node_output, "path")) { - path_output = get_node_value(node_output, "path"); - if (!ends_with(path_output, "/")) { - path_output += "/"; + // Check if trigger(s) are to be turned on + trigger_on = get_node_value_bool(node_trigger, "active"); + + if (trigger_on) { + if (check_for_node(node_trigger, "max_batches") ){ + n_max_batches = std::stoi(get_node_value(node_trigger, "max_batches")); + } else { + fatal_error(" must be specified with triggers"); + } + + // Get the batch interval to check triggers + if (!check_for_node(node_trigger, "batch_interval")){ + trigger_predict = true; + } else { + trigger_batch_interval = std::stoi(get_node_value(node_trigger, "batch_interval")); + if (trigger_batch_interval <= 0) { + fatal_error("Trigger batch interval must be greater than zero"); + } } } } - // Get temperature settings - if (check_for_node(*root, "temperature_default")) { - temperature_default = std::stod(get_node_value(*root, "temperature_default")); - } - if (check_for_node(*root, "temperature_method")) { - auto temp_str = get_node_value(*root, "temperature_method", true, true); - if (temp_str == "nearest") { - temperature_method = TEMPERATURE_NEAREST; - } else if (temp_str == "interpolation") { - temperature_method = TEMPERATURE_INTERPOLATION; + // Check run mode if it hasn't been set from the command line + xml_node node_mode; + if (run_mode == C_NONE) { + if (check_for_node(root, "run_mode")) { + std::string temp_str = get_node_value(root, "run_mode", true, true); + if (temp_str == "eigenvalue") { + run_mode = RUN_MODE_EIGENVALUE; + } else if (temp_str == "fixed source") { + run_mode = RUN_MODE_FIXEDSOURCE; + } else if (temp_str == "plot") { + run_mode = RUN_MODE_PLOTTING; + } else if (temp_str == "particle restart") { + run_mode = RUN_MODE_PARTICLE; + } else if (temp_str == "volume") { + run_mode = RUN_MODE_VOLUME; + } else { + fatal_error("Unrecognized run mode: " + temp_str); + } + + // Assume XML specifies , , etc. directly + node_mode = root; } else { - fatal_error("Unknown temperature method: " + temp_str); + warning(" should be specified."); + + // Make sure that either eigenvalue or fixed source was specified + node_mode = root.child("eigenvalue"); + if (node_mode) { + run_mode = RUN_MODE_EIGENVALUE; + } else { + node_mode = root.child("fixed_source"); + if (node_mode) { + run_mode = RUN_MODE_FIXEDSOURCE; + } else { + fatal_error(" or not specified."); + } + } } } - if (check_for_node(*root, "temperature_tolerance")) { - temperature_tolerance = std::stod(get_node_value(*root, "temperature_tolerance")); + + if (run_mode == RUN_MODE_EIGENVALUE || run_mode == RUN_MODE_FIXEDSOURCE) { + // Read run parameters + get_run_parameters(node_mode); + + // Check number of active batches, inactive batches, 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."); + } } - if (check_for_node(*root, "temperature_multipole")) { - temperature_multipole = get_node_value_bool(*root, "temperature_multipole"); + + // Copy random number seed if specified + if (check_for_node(root, "seed")) { + auto seed = std::stoll(get_node_value(root, "seed")); + openmc_set_seed(seed); } - if (check_for_node(*root, "temperature_range")) { - auto range = get_node_array(*root, "temperature_range"); - temperature_range[0] = range[0]; - temperature_range[1] = range[1]; + + // Check for electron treatment + if (check_for_node(root, "electron_treatment")) { + auto temp_str = get_node_value(root, "electron_treatment", true, true); + if (temp_str == "led") { + electron_treatment = ELECTRON_LED; + } else if (temp_str == "ttb") { + electron_treatment = ELECTRON_TTB; + } else { + fatal_error("Unrecognized electron treatment: " + temp_str + "."); + } + } + + // Check for photon transport + if (check_for_node(root, "photon_transport")) { + photon_transport = get_node_value_bool(root, "photon_transport"); + + if (!run_CE && photon_transport) { + fatal_error("Photon transport is not currently supported in " + "multigroup mode"); + } + } + + // Number of bins for logarithmic grid + if (check_for_node(root, "log_grid_bins")) { + n_log_bins = std::stoi(get_node_value(root, "log_grid_bins")); + if (n_log_bins < 1) { + fatal_error("Number of bins for logarithmic grid must be greater " + "than zero."); + } + } + + // Number of OpenMP threads + if (check_for_node(root, "threads")) { +#ifdef _OPENMP + if (openmc_n_threads == 0) { + openmc_n_threads = std::stoi(get_node_value(root, "threads")); + if (openmc_n_threads < 1) { + std::stringstream msg; + msg << "Invalid number of threads: " << openmc_n_threads; + fatal_error(msg); + } + omp_set_num_threads(openmc_n_threads); + } +#else + if (openmc_master) warning("OpenMC was not compiled with OpenMP support; " + "ignoring number of threads."); +#endif } // ========================================================================== // EXTERNAL SOURCE // Get point to list of elements and make sure there is at least one - for (pugi::xml_node node : root->children("source")) { + for (pugi::xml_node node : root.children("source")) { external_sources.emplace_back(node); } @@ -120,6 +413,351 @@ void read_settings(pugi::xml_node* root) }; external_sources.push_back(std::move(source)); } + + // Check if we want to write out source + if (check_for_node(root, "write_initial_source")) { + write_initial_source = get_node_value_bool(root, "write_initial_source"); + } + + // Survival biasing + if (check_for_node(root, "survival_biasing")) { + survival_biasing = get_node_value_bool(root, "survival_biasing"); + } + + // Probability tables + if (check_for_node(root, "ptables")) { + urr_ptables_on = get_node_value_bool(root, "ptables"); + } + + // Cutoffs + if (check_for_node(root, "cutoff")) { + xml_node node_cutoff = root.child("cutoff"); + if (check_for_node(node_cutoff, "weight")) { + weight_cutoff = std::stod(get_node_value(node_cutoff, "weight")); + } + if (check_for_node(node_cutoff, "weight_avg")) { + weight_survive = std::stod(get_node_value(node_cutoff, "weight_avg")); + } + if (check_for_node(node_cutoff, "energy_neutron")) { + energy_cutoff[0] = std::stod(get_node_value(node_cutoff, "energy_neutron")); + } else if (check_for_node(node_cutoff, "energy")) { + warning("The use of an cutoff is deprecated and should " + "be replaced by ."); + energy_cutoff[0] = std::stod(get_node_value(node_cutoff, "energy")); + } + if (check_for_node(node_cutoff, "energy_photon")) { + energy_cutoff[1] = std::stod(get_node_value(node_cutoff, "energy_photon")); + } + if (check_for_node(node_cutoff, "energy_electron")) { + energy_cutoff[2] = std::stof(get_node_value(node_cutoff, "energy_electron")); + } + if (check_for_node(node_cutoff, "energy_positron")) { + energy_cutoff[3] = std::stod(get_node_value(node_cutoff, "energy_positron")); + } + } + + // Particle trace + if (check_for_node(root, "trace")) { + auto temp = get_node_array(root, "trace"); + if (temp.size() != 3) { + fatal_error("Must provide 3 integers for that specify the " + "batch, generation, and particle number."); + } + trace_batch = temp.at(0); + trace_gen = temp.at(1); + trace_particle = temp.at(2); + } + + // Particle tracks + if (check_for_node(root, "track")) { + // Get values and make sure there are three per particle + auto temp = get_node_array(root, "track"); + if (temp.size() % 3 != 0) { + fatal_error("Number of integers specified in 'track' is not " + "divisible by 3. Please provide 3 integers per particle to be " + "tracked."); + } + + // Reshape into track_identifiers + //allocate(track_identifiers(3, n_tracks/3)) + //track_identifiers = reshape(temp_int_array, [3, n_tracks/3]) + } + + // Read meshes + read_meshes(&root); + + // Shannon Entropy mesh + if (check_for_node(root, "entropy_mesh")) { + int temp = std::stoi(get_node_value(root, "entropy_mesh")); + if (mesh_map.find(temp) == mesh_map.end()) { + std::stringstream msg; + msg << "Mesh " << temp << " specified for Shannon entropy does not exist."; + fatal_error(msg); + } + index_entropy_mesh = mesh_map.at(temp); + + } else if (check_for_node(root, "entropy")) { + warning("Specifying a Shannon entropy mesh via the element " + "is deprecated. Please create a mesh using and then reference " + "it by specifying its ID in an element."); + + // Read entropy mesh from + auto node_entropy = root.child("entropy"); + meshes.emplace_back(new RegularMesh{node_entropy}); + + // Set entropy mesh index + index_entropy_mesh = meshes.size() - 1; + + // Assign ID and set mapping + meshes.back()->id_ = 10000; + mesh_map[10000] = index_entropy_mesh; + } + + if (index_entropy_mesh >= 0) { + auto& m = *meshes[index_entropy_mesh]; + if (m.shape_.dimension() == 0) { + // If the user did not specify how many mesh cells are to be used in + // each direction, we automatically determine an appropriate number of + // cells + int n = std::ceil(std::pow(settings::n_particles / 20.0, 1.0/3.0)); + m.shape_ = {n, n, n}; + m.n_dimension_ = 3; + + // Calculate width + m.width_ = (m.upper_right_ - m.lower_left_) / m.shape_; + } + + // Turn on Shannon entropy calculation + settings::entropy_on = true; + } + + // Uniform fission source weighting mesh + if (check_for_node(root, "ufs_mesh")) { + auto temp = std::stoi(get_node_value(root, "ufs_mesh")); + if (mesh_map.find(temp) == mesh_map.end()) { + std::stringstream msg; + msg << "Mesh " << temp << " specified for uniform fission site method " + "does not exist."; + fatal_error(msg); + } + index_ufs_mesh = mesh_map.at(temp); + + } else if (check_for_node(root, "uniform_fs")) { + warning("Specifying a UFS mesh via the element " + "is deprecated. Please create a mesh using and then reference " + "it by specifying its ID in a element."); + + // Read entropy mesh from + auto node_ufs = root.child("uniform_fs"); + meshes.emplace_back(new RegularMesh{node_ufs}); + + // Set entropy mesh index + index_ufs_mesh = meshes.size() - 1; + + // Assign ID and set mapping + meshes.back()->id_ = 10001; + mesh_map[10001] = index_entropy_mesh; + } + + if (index_ufs_mesh >= 0) { + // Turn on uniform fission source weighting + settings::ufs_on = true; + } + + // TODO: Read + + + // Check if the user has specified to write source points + if (check_for_node(root, "source_point")) { + // Get source_point node + xml_node node_sp = root.child("source_point"); + + // TODO: Read source point batches + + // Check if the user has specified to write binary source file + if (check_for_node(node_sp, "separate")) { + source_separate = get_node_value_bool(node_sp, "separate"); + } + if (check_for_node(node_sp, "write")) { + source_write = get_node_value_bool(node_sp, "write"); + } + if (check_for_node(node_sp, "overwrite_latest")) { + source_latest = get_node_value_bool(node_sp, "overwrite_latest"); + source_separate = source_latest; + } + } else { + // If no tag was present, by default we keep source bank in + // statepoint file and write it out at statepoints intervals + source_separate = false; + // TODO: add defaults + } + + // TODO: Check source points are subset + + // Check if the user has specified to not reduce tallies at the end of every + // batch + if (check_for_node(root, "no_reduce")) { + reduce_tallies = get_node_value_bool(root, "no_reduce"); + } + + // Check if the user has specified to use confidence intervals for + // uncertainties rather than standard deviations + if (check_for_node(root, "confidence_intervals")) { + confidence_intervals = get_node_value_bool(root, "confidence_intervals"); + } + + // Check for output options + if (check_for_node(root, "output")) { + // Get pointer to output node + pugi::xml_node node_output = root.child("output"); + + // Check for summary option + if (check_for_node(node_output, "summary")) { + output_summary = get_node_value_bool(node_output, "summary"); + } + + // Check for ASCII tallies output option + if (check_for_node(node_output, "tallies")) { + output_tallies = get_node_value_bool(node_output, "tallies"); + } + + // Set output directory if a path has been specified + if (check_for_node(node_output, "path")) { + path_output = get_node_value(node_output, "path"); + if (!ends_with(path_output, "/")) { + path_output += "/"; + } + } + } + + // Check for cmfd run + if (check_for_node(root, "run_cmfd")) { + cmfd_run = get_node_value_bool(root, "run_cmfd"); + } + + // Resonance scattering parameters + if (check_for_node(root, "resonance_scattering")) { + xml_node node_res_scat = root.child("resonance_scattering"); + + // See if resonance scattering is enabled + if (check_for_node(node_res_scat, "enable")) { + res_scat_on = get_node_value_bool(node_res_scat, "enable"); + } else { + res_scat_on = true; + } + + // Determine what method is used + if (check_for_node(node_res_scat, "method")) { + auto temp = get_node_value(node_res_scat, "method", true, true); + if (temp == "ares") { + res_scat_method = RES_SCAT_ARES; + } else if (temp == "dbrc") { + res_scat_method = RES_SCAT_DBRC; + } else if (temp == "wcm") { + res_scat_method = RES_SCAT_WCM; + } else { + fatal_error("Unrecognized resonance elastic scattering method: " + + temp + "."); + } + } + + // Minimum energy for resonance scattering + if (check_for_node(node_res_scat, "energy_min")) { + res_scat_energy_min = std::stod(get_node_value(node_res_scat, "energy_min")); + } + if (res_scat_energy_min < 0.0) { + fatal_error("Lower resonance scattering energy bound is negative"); + } + + // Maximum energy for resonance scattering + if (check_for_node(node_res_scat, "energy_max")) { + res_scat_energy_max = std::stod(get_node_value(node_res_scat, "energy_max")); + } + if (res_scat_energy_max < res_scat_energy_min) { + fatal_error("Upper resonance scattering energy bound is below the " + "lower resonance scattering energy bound."); + } + + // TODO: Get resonance scattering nuclides + } + + // TODO: Get volume calculations + + // Get temperature settings + if (check_for_node(root, "temperature_default")) { + temperature_default = std::stod(get_node_value(root, "temperature_default")); + } + if (check_for_node(root, "temperature_method")) { + auto temp = get_node_value(root, "temperature_method", true, true); + if (temp == "nearest") { + temperature_method = TEMPERATURE_NEAREST; + } else if (temp == "interpolation") { + temperature_method = TEMPERATURE_INTERPOLATION; + } else { + fatal_error("Unknown temperature method: " + temp); + } + } + if (check_for_node(root, "temperature_tolerance")) { + temperature_tolerance = std::stod(get_node_value(root, "temperature_tolerance")); + } + if (check_for_node(root, "temperature_multipole")) { + temperature_multipole = get_node_value_bool(root, "temperature_multipole"); + } + if (check_for_node(root, "temperature_range")) { + auto range = get_node_array(root, "temperature_range"); + temperature_range[0] = range.at(0); + temperature_range[1] = range.at(1); + } + + // Check for tabular_legendre options + if (check_for_node(root, "tabular_legendre")) { + // Get pointer to tabular_legendre node + xml_node node_tab_leg = root.child("tabular_legendre"); + + // Check for enable option + if (check_for_node(node_tab_leg, "enable")) { + legendre_to_tabular = get_node_value_bool(node_tab_leg, "enable"); + } + + // Check for the number of points + if (check_for_node(node_tab_leg, "num_points")) { + legendre_to_tabular_points = std::stoi(get_node_value( + node_tab_leg, "num_points")); + if (legendre_to_tabular_points <= 1 && !run_CE) { + fatal_error("The 'num_points' subelement/attribute of the " + " element must contain a value greater than 1"); + } + } + } + + // Check whether create fission sites + if (run_mode == RUN_MODE_FIXEDSOURCE) { + if (check_for_node(root, "create_fission_neutrons")) { + create_fission_neutrons = get_node_value_bool(root, "create_fission_neutrons"); + } + } + + // Read remaining settings from Fortran side + read_settings_xml_f(root.internal_object()); +} + +//============================================================================== +// Fortran compatibility functions +//============================================================================== + +extern "C" { + const char* openmc_path_input() { + return settings::path_input.c_str(); + } + const char* openmc_path_statepoint() { + return settings::path_statepoint.c_str(); + } + const char* openmc_path_sourcepoint() { + return settings::path_sourcepoint.c_str(); + } + const char* openmc_path_particle_restart() { + return settings::path_particle_restart.c_str(); + } } } // namespace openmc diff --git a/src/simulation.F90 b/src/simulation.F90 index d334b46b8..176dfb921 100644 --- a/src/simulation.F90 +++ b/src/simulation.F90 @@ -10,8 +10,7 @@ module simulation use cmfd_execute, only: cmfd_init_batch, cmfd_tally_init, execute_cmfd use cmfd_header, only: cmfd_on use constants, only: ZERO - use eigenvalue, only: count_source_for_ufs, calculate_average_keff, & - calculate_generation_keff, shannon_entropy, & + use eigenvalue, only: calculate_average_keff, calculate_generation_keff, & synchronize_bank, keff_generation, k_sum #ifdef _OPENMP use eigenvalue, only: join_bank_from_threads @@ -234,12 +233,17 @@ contains subroutine initialize_generation() + interface + subroutine ufs_count_sites() bind(C) + end subroutine + end interface + if (run_mode == MODE_EIGENVALUE) then ! Reset number of fission bank sites n_bank = 0 ! Count source sites if using uniform fission source weighting - if (ufs) call count_source_for_ufs() + if (ufs) call ufs_count_sites() ! Store current value of tracklength k keff_generation = global_tallies(RESULT_VALUE, K_TRACKLENGTH) @@ -256,6 +260,9 @@ contains interface subroutine fill_source_bank_fixedsource() bind(C) end subroutine + + subroutine shannon_entropy() bind(C) + end subroutine end interface ! Update global tallies with the omp private accumulation variables @@ -465,13 +472,17 @@ contains ! Allocate array for matching filter bins allocate(filter_matches(n_filters)) + do i = 1, n_filters + allocate(filter_matches(i) % bins) + allocate(filter_matches(i) % weights) + end do !$omp end parallel ! Reset global variables -- this is done before loading state point (as that ! will potentially populate k_generation and entropy) current_batch = 0 call k_generation % clear() - call entropy % clear() + call entropy_clear() need_depletion_rx = .false. ! If this is a restart run, load the state point data and binary source @@ -550,6 +561,10 @@ contains deallocate(materials(i) % mat_nuclide_index) end do !$omp parallel + do i = 1, size(filter_matches) + deallocate(filter_matches(i) % bins) + deallocate(filter_matches(i) % weights) + end do deallocate(micro_xs, micro_photon_xs, filter_matches) !$omp end parallel diff --git a/src/simulation.cpp b/src/simulation.cpp index 3514f4a47..74a3c14fc 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -2,6 +2,7 @@ #include "openmc/capi.h" #include "openmc/message_passing.h" +#include "openmc/settings.h" // OPENMC_RUN encompasses all the main logic where iterations are performed // over the batches, generations, and histories in a fixed source or k-eigenvalue @@ -41,10 +42,10 @@ void openmc_simulation_init_c() void calculate_work() { // Determine minimum amount of particles to simulate on each processor - int64_t min_work = n_particles/mpi::n_procs; + int64_t min_work = settings::n_particles / mpi::n_procs; // Determine number of processors that have one extra particle - int64_t remainder = n_particles % mpi::n_procs; + int64_t remainder = settings::n_particles % mpi::n_procs; int64_t i_bank = 0; work_index.reserve(mpi::n_procs); diff --git a/src/simulation_header.F90 b/src/simulation_header.F90 index 0e38b76f2..ad5513c61 100644 --- a/src/simulation_header.F90 +++ b/src/simulation_header.F90 @@ -47,13 +47,6 @@ module simulation_header real(8) :: k_col_tra = ZERO ! sum over batches of k_collision * k_tracklength real(8) :: k_abs_tra = ZERO ! sum over batches of k_absorption * k_tracklength - ! Shannon entropy - type(VectorReal) :: entropy ! shannon entropy at each generation - real(8), allocatable :: entropy_p(:,:) ! % of source sites in each cell - - ! Uniform fission source weighting - real(8), allocatable :: source_frac(:,:) - ! ============================================================================ ! PARALLEL PROCESSING VARIABLES @@ -71,6 +64,12 @@ module simulation_header !$omp threadprivate(trace, thread_id, current_work) + interface + subroutine entropy_clear() bind(C) + end subroutine + end interface + + contains !=============================================================================== @@ -87,14 +86,12 @@ contains !=============================================================================== subroutine free_memory_simulation() - if (allocated(entropy_p)) deallocate(entropy_p) - if (allocated(source_frac)) deallocate(source_frac) + if (allocated(work_index)) deallocate(work_index) call k_generation % clear() call k_generation % shrink_to_fit() - call entropy % clear() - call entropy % shrink_to_fit() + call entropy_clear() end subroutine free_memory_simulation end module simulation_header diff --git a/src/source.cpp b/src/source.cpp index 92e6bf6ec..666bbb783 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -45,7 +45,7 @@ SourceDistribution::SourceDistribution(pugi::xml_node node) particle_ = ParticleType::neutron; } else if (temp_str == "photon") { particle_ = ParticleType::photon; - openmc_photon_transport = true; + settings::photon_transport = true; } else { fatal_error(std::string("Unknown source particle type: ") + temp_str); } @@ -59,12 +59,12 @@ SourceDistribution::SourceDistribution(pugi::xml_node node) // Check for external source file if (check_for_node(node, "file")) { // Copy path of source file - path_source = get_node_value(node, "file", false, true); + settings::path_source = get_node_value(node, "file", false, true); // Check if source file exists - if (!file_exists(path_source)) { + if (!file_exists(settings::path_source)) { std::stringstream msg; - msg << "Source file '" << path_source << "' does not exist."; + msg << "Source file '" << settings::path_source << "' does not exist."; fatal_error(msg); } @@ -243,16 +243,16 @@ void initialize_source() int64_t n; openmc_source_bank(&source_bank, &n); - if (path_source != "") { + if (settings::path_source != "") { // Read the source from a binary file instead of sampling from some // assumed source distribution std::stringstream msg; - msg << "Reading source file from " << path_source << "..."; + msg << "Reading source file from " << settings::path_source << "..."; write_message(msg, 6); // Open the binary file - hid_t file_id = file_open(path_source, 'r', true); + hid_t file_id = file_open(settings::path_source, 'r', true); // Read the file type std::string filetype; @@ -273,7 +273,8 @@ void initialize_source() // Generation source sites from specified distribution in user input for (int64_t i = 0; i < openmc_work; ++i) { // initialize random number seed - int64_t id = openmc_total_gen*n_particles + work_index[openmc::mpi::rank] + i + 1; + int64_t id = openmc_total_gen*settings::n_particles + + work_index[openmc::mpi::rank] + i + 1; set_particle_seed(id); // sample external source distribution @@ -282,9 +283,9 @@ void initialize_source() } // Write out initial source - if (openmc_write_initial_source) { + if (settings::write_initial_source) { write_message("Writing out initial source...", 5); - std::string filename = path_output + "initial_source.h5"; + std::string filename = settings::path_output + "initial_source.h5"; hid_t file_id = file_open(filename, 'w', true); write_source_bank(file_id, work_index.data(), source_bank); file_close(file_id); @@ -318,7 +319,7 @@ Bank sample_external_source() Bank site {external_sources[i].sample()}; // If running in MG, convert site % E to group - if (!openmc_run_CE) { + if (!settings::run_CE) { // Get pointer to rev_energy_bins array on Fortran side double* rev_energy_bins = rev_energy_bins_ptr(); @@ -357,7 +358,7 @@ extern "C" int overall_generation(); //! Fill source bank at end of generation for fixed source simulations extern "C" void fill_source_bank_fixedsource() { - if (path_source.empty()) { + if (settings::path_source.empty()) { // Get pointer to source bank Bank* source_bank; int64_t n; @@ -365,8 +366,8 @@ extern "C" void fill_source_bank_fixedsource() for (int64_t i = 0; i < openmc_work; ++i) { // initialize random number seed - int64_t id = (openmc_total_gen + overall_generation())*n_particles + - work_index[openmc::mpi::rank] + i + 1; + int64_t id = (openmc_total_gen + overall_generation()) * + settings::n_particles + work_index[openmc::mpi::rank] + i + 1; set_particle_seed(id); // sample external source distribution diff --git a/src/state_point.F90 b/src/state_point.F90 index bc6474de2..526066c52 100644 --- a/src/state_point.F90 +++ b/src/state_point.F90 @@ -20,7 +20,6 @@ module state_point use endf, only: reaction_name use error, only: fatal_error, warning, write_message use hdf5_interface - use mesh_header, only: RegularMesh, meshes, n_meshes use message_passing use mgxs_interface use nuclide_header, only: nuclides @@ -68,7 +67,7 @@ contains integer :: i_xs integer, allocatable :: id_array(:) integer(HID_T) :: file_id - integer(HID_T) :: cmfd_group, tallies_group, tally_group, meshes_group, & + integer(HID_T) :: cmfd_group, tallies_group, tally_group, & filters_group, filter_group, derivs_group, & deriv_group, runtime_group integer(C_INT) :: ignored_err @@ -79,6 +78,17 @@ contains character(MAX_WORD_LEN, kind=C_CHAR) :: temp_name logical :: parallel + interface + subroutine meshes_to_hdf5(group) bind(C) + import HID_T + integer(HID_T), value :: group + end subroutine + subroutine entropy_to_hdf5(group) bind(C) + import HID_T + integer(HID_T), value :: group + end subroutine + end interface + err = 0 ! Set the filename @@ -163,9 +173,7 @@ contains call write_dataset(file_id, "generations_per_batch", gen_per_batch) k = k_generation % size() call write_dataset(file_id, "k_generation", k_generation % data(1:k)) - if (entropy_on) then - call write_dataset(file_id, "entropy", entropy % data(1:k)) - end if + call entropy_to_hdf5(file_id) call write_dataset(file_id, "k_col_abs", k_col_abs) call write_dataset(file_id, "k_col_tra", k_col_tra) call write_dataset(file_id, "k_abs_tra", k_abs_tra) @@ -192,26 +200,8 @@ contains tallies_group = create_group(file_id, "tallies") - ! Write number of meshes - meshes_group = create_group(tallies_group, "meshes") - call write_attribute(meshes_group, "n_meshes", n_meshes) - - if (n_meshes > 0) then - ! Write IDs of meshes - allocate(id_array(n_meshes)) - do i = 1, n_meshes - id_array(i) = meshes(i) % id - end do - call write_attribute(meshes_group, "ids", id_array) - deallocate(id_array) - - ! Write information for meshes - MESH_LOOP: do i = 1, n_meshes - call meshes(i) % to_hdf5(meshes_group) - end do MESH_LOOP - end if - - call close_group(meshes_group) + ! Write meshes + call meshes_to_hdf5(tallies_group) ! Write information for derivatives. if (size(tally_derivs) > 0) then @@ -641,6 +631,11 @@ contains logical :: source_present character(MAX_WORD_LEN) :: word + interface + subroutine entropy_from_hdf5() bind(C) + end subroutine + end interface + ! Write message call write_message("Loading state point " // trim(path_state_point) & // "...", 5) @@ -722,10 +717,7 @@ contains call k_generation % resize(n) call read_dataset(k_generation % data(1:n), file_id, "k_generation") - if (entropy_on) then - call entropy % resize(n) - call read_dataset(entropy % data(1:n), file_id, "entropy") - end if + call entropy_from_hdf5() call read_dataset(k_col_abs, file_id, "k_col_abs") call read_dataset(k_col_tra, file_id, "k_col_tra") call read_dataset(k_abs_tra, file_id, "k_abs_tra") diff --git a/src/state_point.cpp b/src/state_point.cpp index 1e4c8bdfd..dba0fe1b8 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -10,6 +10,7 @@ #include "openmc/capi.h" #include "openmc/error.h" #include "openmc/message_passing.h" +#include "openmc/settings.h" namespace openmc { @@ -39,7 +40,7 @@ write_source_bank(hid_t group_id, int64_t* work_index, Bank* source_bank) #ifdef PHDF5 // Set size of total dataspace for all procs and rank - hsize_t dims[] {static_cast(n_particles)}; + hsize_t dims[] {static_cast(settings::n_particles)}; hid_t dspace = H5Screate_simple(1, dims, nullptr); hid_t dset = H5Dcreate(group_id, "source_bank", banktype, dspace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); @@ -69,7 +70,7 @@ write_source_bank(hid_t group_id, int64_t* work_index, Bank* source_bank) if (openmc_master) { // Create dataset big enough to hold all source sites - hsize_t dims[] {static_cast(n_particles)}; + hsize_t dims[] {static_cast(settings::n_particles)}; hid_t dspace = H5Screate_simple(1, dims, nullptr); hid_t dset = H5Dcreate(group_id, "source_bank", banktype, dspace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); diff --git a/src/stl_vector.F90 b/src/stl_vector.F90 index 06f487dc1..156290781 100644 --- a/src/stl_vector.F90 +++ b/src/stl_vector.F90 @@ -35,6 +35,8 @@ module stl_vector ! ! size -- Returns the number of elements in the vector. + use, intrinsic :: ISO_C_BINDING + implicit none private @@ -521,4 +523,28 @@ contains size = this%size_ end function size_char +!=============================================================================== +! Procedures to be called from C++ +!=============================================================================== + + subroutine vector_int_push_back(ptr, val) bind(C) + type(C_PTR), value :: ptr + integer(C_INT), value :: val + + type(VectorInt), pointer :: vec + + call C_F_POINTER(ptr, vec) + call vec % push_back(val) + end subroutine + + subroutine vector_real_push_back(ptr, val) bind(C) + type(C_PTR), value :: ptr + real(C_DOUBLE), value :: val + + type(VectorReal), pointer :: vec + + call C_F_POINTER(ptr, vec) + call vec % push_back(val) + end subroutine + end module stl_vector diff --git a/src/summary.F90 b/src/summary.F90 index 750a9899f..0fafd7e5d 100644 --- a/src/summary.F90 +++ b/src/summary.F90 @@ -6,7 +6,6 @@ module summary use geometry_header use hdf5_interface use material_header, only: Material, n_materials, openmc_material_get_volume - use mesh_header, only: RegularMesh use message_passing use mgxs_interface use nuclide_header diff --git a/src/tallies/tally.F90 b/src/tallies/tally.F90 index 9967606d1..a618f351e 100644 --- a/src/tallies/tally.F90 +++ b/src/tallies/tally.F90 @@ -8,7 +8,6 @@ module tally use error, only: fatal_error use geometry_header use math, only: t_percentile - use mesh_header, only: RegularMesh, meshes use message_passing use mgxs_interface use nuclide_header diff --git a/src/tallies/tally_filter_header.F90 b/src/tallies/tally_filter_header.F90 index e57423ddf..d6aef4abd 100644 --- a/src/tallies/tally_filter_header.F90 +++ b/src/tallies/tally_filter_header.F90 @@ -28,8 +28,8 @@ module tally_filter_header type, public :: TallyFilterMatch ! Index of the bin and weight being used in the current filter combination integer :: i_bin - type(VectorInt) :: bins - type(VectorReal) :: weights + type(VectorInt), pointer :: bins + type(VectorReal), pointer :: weights ! Indicates whether all valid bins for this filter have been found logical :: bins_present = .false. diff --git a/src/tallies/tally_filter_mesh.F90 b/src/tallies/tally_filter_mesh.F90 index 486569da6..dda0bc0ce 100644 --- a/src/tallies/tally_filter_mesh.F90 +++ b/src/tallies/tally_filter_mesh.F90 @@ -5,7 +5,7 @@ module tally_filter_mesh use constants use dict_header, only: EMPTY use error - use mesh_header, only: RegularMesh, meshes, n_meshes, mesh_dict + use mesh_header use hdf5_interface use particle_header, only: Particle use string, only: to_str @@ -38,10 +38,11 @@ contains class(MeshFilter), intent(inout) :: this type(XMLNode), intent(in) :: node - integer :: i_mesh + integer :: i integer :: id integer :: n - integer :: val + integer(C_INT) :: err + type(RegularMesh) :: m n = node_word_count(node, "bins") @@ -52,19 +53,18 @@ contains call get_node_value(node, "bins", id) ! Get pointer to mesh - val = mesh_dict % get(id) - if (val /= EMPTY) then - i_mesh = val - else + err = openmc_get_mesh_index(id, this % mesh) + if (err /= 0) then call fatal_error("Could not find mesh " // trim(to_str(id)) & // " specified on filter.") end if ! Determine number of bins - this % n_bins = product(meshes(i_mesh) % dimension) - - ! Store the index of the mesh - this % mesh = i_mesh + m = meshes(this % mesh) + this % n_bins = 1 + do i = 1, m % n_dimension() + this % n_bins = this % n_bins * m % dimension(i) + end do end subroutine from_xml subroutine get_all_bins_mesh(this, p, estimator, match) @@ -73,184 +73,50 @@ contains integer, intent(in) :: estimator type(TallyFilterMatch), intent(inout) :: match - integer, parameter :: MAX_SEARCH_ITER = 100 ! Maximum number of times we can - ! can loop while trying to find - ! the first intersection. - - integer :: j ! loop index for direction - integer :: n - integer :: ijk0(3) ! indices of starting coordinates - integer :: ijk1(3) ! indices of ending coordinates - integer :: search_iter ! loop count for intersection search integer :: bin - real(8) :: uvw(3) ! cosine of angle of particle - real(8) :: xyz0(3) ! starting/intermediate coordinates - real(8) :: xyz1(3) ! ending coordinates of particle - real(8) :: xyz_cross ! coordinates of next boundary - real(8) :: d(3) ! distance to each bounding surface - real(8) :: total_distance ! distance of entire particle track - real(8) :: distance ! distance traveled in mesh cell - logical :: start_in_mesh ! starting coordinates inside mesh? - logical :: end_in_mesh ! ending coordinates inside mesh? - type(RegularMesh), pointer :: m + type(RegularMesh) :: m + type(C_PTR) :: ptr_bins, ptr_weights + + interface + subroutine mesh_bins_crossed(m, p, bins, weights) bind(C) + import C_PTR, Particle + type(C_PTR), value :: m + type(Particle), intent(in) :: p + type(C_PTR), value :: bins + type(C_PTR), value :: weights + end subroutine + end interface ! Get a pointer to the mesh. - m => meshes(this % mesh) - n = m % n_dimension + m = meshes(this % mesh) if (estimator /= ESTIMATOR_TRACKLENGTH) then ! If this is an analog or collision tally, then there can only be one ! valid mesh bin. call m % get_bin(p % coord(1) % xyz, bin) - if (bin /= NO_BIN_FOUND) then + if (bin >= 0) then call match % bins % push_back(bin) call match % weights % push_back(ONE) end if return + else + ptr_bins = C_LOC(match % bins) + ptr_weights = C_LOC(match % weights) + call mesh_bins_crossed(m % ptr, p, ptr_bins, ptr_weights) end if - ! A track can span multiple mesh bins so we need to handle a lot of - ! intersection logic for tracklength tallies. - - ! ======================================================================== - ! Determine if the track intersects the tally mesh. - - ! Copy the starting and ending coordinates of the particle. Offset these - ! just a bit for the purposes of determining if there was an intersection - ! in case the mesh surfaces coincide with lattice/geometric surfaces which - ! might produce finite-precision errors. - xyz0 = p % last_xyz + TINY_BIT * p % coord(1) % uvw - xyz1 = p % coord(1) % xyz - TINY_BIT * p % coord(1) % uvw - - ! Determine indices for starting and ending location. - call m % get_indices(xyz0, ijk0(:n), start_in_mesh) - call m % get_indices(xyz1, ijk1(:n), end_in_mesh) - - ! If this is the first iteration of the filter loop, check if the track - ! intersects any part of the mesh. - if ((.not. start_in_mesh) .and. (.not. end_in_mesh)) then - if (.not. m % intersects(xyz0, xyz1)) return - end if - - ! ======================================================================== - ! Figure out which mesh cell to tally. - - ! Copy the un-modified coordinates the particle direction. - xyz0 = p % last_xyz - xyz1 = p % coord(1) % xyz - uvw = p % coord(1) % uvw - - ! Compute the length of the entire track. - total_distance = sqrt(sum((xyz1 - xyz0)**2)) - - ! We are looking for the first valid mesh bin. Check to see if the - ! particle starts inside the mesh. - if (any(ijk0(:n) < 1) .or. any(ijk0(:n) > m % dimension)) then - ! The particle does not start in the mesh. Note that we nudged the - ! start and end coordinates by a TINY_BIT each so we will have - ! difficulty resolving tracks that are less than 2*TINY_BIT in length. - ! If the track is that short, it is also insignificant so we can - ! safely ignore it in the tallies. - if (total_distance < 2*TINY_BIT) return - - ! The particle does not start in the mesh so keep iterating the ijk0 - ! indices to cross the nearest mesh surface until we've found a valid - ! bin. MAX_SEARCH_ITER prevents an infinite loop. - search_iter = 0 - do while (any(ijk0(:n) < 1) .or. any(ijk0(:n) > m % dimension)) - if (search_iter == MAX_SEARCH_ITER) then - call warning("Failed to find a mesh intersection on a tally mesh & - &filter.") - return - end if - - do j = 1, n - if (abs(uvw(j)) < FP_PRECISION) then - d(j) = INFINITY - else if (uvw(j) > 0) then - xyz_cross = m % lower_left(j) + ijk0(j) * m % width(j) - d(j) = (xyz_cross - xyz0(j)) / uvw(j) - else - xyz_cross = m % lower_left(j) + (ijk0(j) - 1) * m % width(j) - d(j) = (xyz_cross - xyz0(j)) / uvw(j) - end if - end do - j = minloc(d(:n), 1) - if (uvw(j) > ZERO) then - ijk0(j) = ijk0(j) + 1 - else - ijk0(j) = ijk0(j) - 1 - end if - - search_iter = search_iter + 1 - end do - distance = d(j) - xyz0 = xyz0 + distance * uvw - end if - - do - ! ======================================================================== - ! Compute the length of the track segment in the appropiate mesh cell and - ! return. - - if (all(ijk0(:n) == ijk1(:n))) then - ! The track ends in this cell. Use the particle end location rather - ! than the mesh surface. - distance = sqrt(sum((xyz1 - xyz0)**2)) - else - ! The track exits this cell. Determine the distance to the closest mesh - ! surface. - do j = 1, n - if (abs(uvw(j)) < FP_PRECISION) then - d(j) = INFINITY - else if (uvw(j) > 0) then - xyz_cross = m % lower_left(j) + ijk0(j) * m % width(j) - d(j) = (xyz_cross - xyz0(j)) / uvw(j) - else - xyz_cross = m % lower_left(j) + (ijk0(j) - 1) * m % width(j) - d(j) = (xyz_cross - xyz0(j)) / uvw(j) - end if - end do - j = minloc(d(:n), 1) - distance = d(j) - end if - - ! Assign the next tally bin and the score. - bin = m % get_bin_from_indices(ijk0(:n)) - call match % bins % push_back(bin) - call match % weights % push_back(distance / total_distance) - - ! Find the next mesh cell that the particle enters. - - ! If the particle track ends in that bin, then we are done. - if (all(ijk0(:n) == ijk1(:n))) exit - - ! Translate the starting coordintes by the distance to that face. This - ! should be the xyz that we computed the distance to in the last - ! iteration of the filter loop. - xyz0 = xyz0 + distance * uvw - - ! Increment the indices into the next mesh cell. - if (uvw(j) > ZERO) then - ijk0(j) = ijk0(j) + 1 - else - ijk0(j) = ijk0(j) - 1 - end if - - ! If the next indices are invalid, then the track has left the mesh and - ! we are done. - if (any(ijk0(:n) < 1) .or. any(ijk0(:n) > m % dimension)) exit - end do - end subroutine get_all_bins_mesh subroutine to_statepoint_mesh(this, filter_group) class(MeshFilter), intent(in) :: this integer(HID_T), intent(in) :: filter_group + type(RegularMesh) :: m + + m = meshes(this % mesh) call write_dataset(filter_group, "type", "mesh") call write_dataset(filter_group, "n_bins", this % n_bins) - call write_dataset(filter_group, "bins", meshes(this % mesh) % id) + call write_dataset(filter_group, "bins", m % id()) end subroutine to_statepoint_mesh function text_label_mesh(this, bin) result(label) @@ -259,20 +125,20 @@ contains character(MAX_LINE_LEN) :: label integer, allocatable :: ijk(:) + type(RegularMesh) :: m - associate (m => meshes(this % mesh)) - allocate(ijk(m % n_dimension)) - call m % get_indices_from_bin(bin, ijk) - if (m % n_dimension == 1) then - label = "Mesh Index (" // trim(to_str(ijk(1))) // ")" - elseif (m % n_dimension == 2) then - label = "Mesh Index (" // trim(to_str(ijk(1))) // ", " // & - trim(to_str(ijk(2))) // ")" - elseif (m % n_dimension == 3) then - label = "Mesh Index (" // trim(to_str(ijk(1))) // ", " // & - trim(to_str(ijk(2))) // ", " // trim(to_str(ijk(3))) // ")" - end if - end associate + m = meshes(this % mesh) + allocate(ijk(m % n_dimension())) + call m % get_indices_from_bin(bin, ijk) + if (m % n_dimension() == 1) then + label = "Mesh Index (" // trim(to_str(ijk(1))) // ")" + elseif (m % n_dimension() == 2) then + label = "Mesh Index (" // trim(to_str(ijk(1))) // ", " // & + trim(to_str(ijk(2))) // ")" + elseif (m % n_dimension() == 3) then + label = "Mesh Index (" // trim(to_str(ijk(1))) // ", " // & + trim(to_str(ijk(2))) // ", " // trim(to_str(ijk(3))) // ")" + end if end function text_label_mesh !=============================================================================== @@ -304,18 +170,25 @@ contains integer(C_INT32_T), value, intent(in) :: index_mesh integer(C_INT) :: err + type(RegularMesh) :: m + integer :: i + err = verify_filter(index) if (err == 0) then select type (f => filters(index) % obj) type is (MeshFilter) - if (index_mesh >= 1 .and. index_mesh <= n_meshes) then + if (index_mesh >= 0 .and. index_mesh < n_meshes()) then f % mesh = index_mesh - f % n_bins = product(meshes(index_mesh) % dimension) + f % n_bins = 1 + m = meshes(index_mesh) + do i = 1, m % n_dimension() + f % n_bins = f % n_bins * m % dimension(i) + end do else err = E_OUT_OF_BOUNDS call set_errmsg("Index in 'meshes' array is out of bounds.") end if - class default + class default err = E_INVALID_TYPE call set_errmsg("Tried to set mesh on a non-mesh filter.") end select diff --git a/src/tallies/tally_filter_meshsurface.F90 b/src/tallies/tally_filter_meshsurface.F90 index 801d4252c..f08bc35ef 100644 --- a/src/tallies/tally_filter_meshsurface.F90 +++ b/src/tallies/tally_filter_meshsurface.F90 @@ -3,9 +3,8 @@ module tally_filter_meshsurface use, intrinsic :: ISO_C_BINDING use constants - use dict_header, only: EMPTY use error - use mesh_header, only: RegularMesh, meshes, n_meshes, mesh_dict + use mesh_header use hdf5_interface use particle_header, only: Particle use string, only: to_str @@ -38,11 +37,11 @@ contains class(MeshSurfaceFilter), intent(inout) :: this type(XMLNode), intent(in) :: node - integer :: i_mesh + integer :: i integer :: id integer :: n - integer :: n_dim - integer :: val + integer(C_INT) :: err + type(RegularMesh) :: m n = node_word_count(node, "bins") @@ -53,20 +52,18 @@ contains call get_node_value(node, "bins", id) ! Get pointer to mesh - val = mesh_dict % get(id) - if (val /= EMPTY) then - i_mesh = val - else + err = openmc_get_mesh_index(id, this % mesh) + if (err /= 0) then call fatal_error("Could not find mesh " // trim(to_str(id)) & // " specified on filter.") end if ! Determine number of bins - n_dim = meshes(i_mesh) % n_dimension - this % n_bins = 4*n_dim*product(meshes(i_mesh) % dimension) - - ! Store the index of the mesh - this % mesh = i_mesh + m = meshes(this % mesh) + this % n_bins = 4 * m % n_dimension() + do i = 1, m % n_dimension() + this % n_bins = this % n_bins * m % dimension(i) + end do end subroutine from_xml subroutine get_all_bins(this, p, estimator, match) @@ -75,150 +72,25 @@ contains integer, intent(in) :: estimator type(TallyFilterMatch), intent(inout) :: match - integer :: j ! loop indices - integer :: n_dim ! num dimensions of the mesh - integer :: d1 ! dimension index - integer :: ijk0(3) ! indices of starting coordinates - integer :: ijk1(3) ! indices of ending coordinates - integer :: n_cross ! number of surface crossings - integer :: i_mesh ! flattened mesh bin index - integer :: i_surf ! surface index (1--12) - integer :: i_bin ! actual index for filter - real(8) :: uvw(3) ! cosine of angle of particle - real(8) :: xyz0(3) ! starting/intermediate coordinates - real(8) :: xyz1(3) ! ending coordinates of particle - real(8) :: xyz_cross(3) ! coordinates of bounding surfaces - real(8) :: d(3) ! distance to each bounding surface - real(8) :: distance ! actual distance traveled - logical :: start_in_mesh ! particle's starting xyz in mesh? - logical :: end_in_mesh ! particle's ending xyz in mesh? + type(RegularMesh) :: m + type(C_PTR) :: ptr_bins, ptr_weights - ! Copy starting and ending location of particle - xyz0 = p % last_xyz_current - xyz1 = p % coord(1) % xyz + interface + subroutine mesh_surface_bins_crossed(m, p, bins, weights) bind(C) + import C_PTR, Particle + type(C_PTR), value :: m + type(Particle), intent(in) :: p + type(C_PTR), value :: bins + type(C_PTR), value :: weights + end subroutine + end interface - associate (m => meshes(this % mesh)) - n_dim = m % n_dimension + ! Get a pointer to the mesh. + m = meshes(this % mesh) - ! Determine indices for starting and ending location - call m % get_indices(xyz0, ijk0, start_in_mesh) - call m % get_indices(xyz1, ijk1, end_in_mesh) - - ! Check to see if start or end is in mesh -- if not, check if track still - ! intersects with mesh - if ((.not. start_in_mesh) .and. (.not. end_in_mesh)) then - if (.not. m % intersects(xyz0, xyz1)) return - end if - - ! Calculate number of surface crossings - n_cross = sum(abs(ijk1(:n_dim) - ijk0(:n_dim))) - if (n_cross == 0) return - - ! Copy particle's direction - uvw = p % coord(1) % uvw - - ! Bounding coordinates - do d1 = 1, n_dim - if (uvw(d1) > 0) then - xyz_cross(d1) = m % lower_left(d1) + ijk0(d1) * m % width(d1) - else - xyz_cross(d1) = m % lower_left(d1) + (ijk0(d1) - 1) * m % width(d1) - end if - end do - - do j = 1, n_cross - ! Set the distances to infinity - d = INFINITY - - ! Calculate distance to each bounding surface. We need to treat - ! special case where the cosine of the angle is zero since this would - ! result in a divide-by-zero. - do d1 = 1, n_dim - if (uvw(d1) == 0) then - d(d1) = INFINITY - else - d(d1) = (xyz_cross(d1) - xyz0(d1))/uvw(d1) - end if - end do - - ! Determine the closest bounding surface of the mesh cell by - ! calculating the minimum distance. Then use the minimum distance and - ! direction of the particle to determine which surface was crossed. - distance = minval(d) - - ! Loop over the dimensions - do d1 = 1, n_dim - - ! Check whether distance is the shortest distance - if (distance == d(d1)) then - - ! Check whether particle is moving in positive d1 direction - if (uvw(d1) > 0) then - - ! Outward current on d1 max surface - if (all(ijk0(:n_dim) >= 1) .and. & - all(ijk0(:n_dim) <= m % dimension)) then - i_surf = d1 * 4 - 1 - i_mesh = m % get_bin_from_indices(ijk0) - i_bin = 4*n_dim*(i_mesh - 1) + i_surf - - call match % bins % push_back(i_bin) - call match % weights % push_back(ONE) - end if - - ! Advance position - ijk0(d1) = ijk0(d1) + 1 - xyz_cross(d1) = xyz_cross(d1) + m % width(d1) - - ! If the particle crossed the surface, tally the inward current on - ! d1 min surface - if (all(ijk0(:n_dim) >= 1) .and. & - all(ijk0(:n_dim) <= m % dimension)) then - i_surf = d1 * 4 - 2 - i_mesh = m % get_bin_from_indices(ijk0) - i_bin = 4*n_dim*(i_mesh - 1) + i_surf - - call match % bins % push_back(i_bin) - call match % weights % push_back(ONE) - end if - - else - ! The particle is moving in the negative d1 direction - - ! Outward current on d1 min surface - if (all(ijk0(:n_dim) >= 1) .and. & - all(ijk0(:n_dim) <= m % dimension)) then - i_surf = d1 * 4 - 3 - i_mesh = m % get_bin_from_indices(ijk0) - i_bin = 4*n_dim*(i_mesh - 1) + i_surf - - call match % bins % push_back(i_bin) - call match % weights % push_back(ONE) - end if - - ! Advance position - ijk0(d1) = ijk0(d1) - 1 - xyz_cross(d1) = xyz_cross(d1) - m % width(d1) - - ! If the particle crossed the surface, tally the inward current on - ! d1 max surface - if (all(ijk0(:n_dim) >= 1) .and. & - all(ijk0(:n_dim) <= m % dimension)) then - i_surf = d1 * 4 - i_mesh = m % get_bin_from_indices(ijk0) - i_bin = 4*n_dim*(i_mesh - 1) + i_surf - - call match % bins % push_back(i_bin) - call match % weights % push_back(ONE) - end if - end if - end if - end do - - ! Calculate new coordinates - xyz0 = xyz0 + distance * uvw - end do - end associate + ptr_bins = C_LOC(match % bins) + ptr_weights = C_LOC(match % weights) + call mesh_surface_bins_crossed(m % ptr, p, ptr_bins, ptr_weights) end subroutine get_all_bins @@ -226,9 +98,12 @@ contains class(MeshSurfaceFilter), intent(in) :: this integer(HID_T), intent(in) :: filter_group + type(RegularMesh) :: m + + m = meshes(this % mesh) call write_dataset(filter_group, "type", "meshsurface") call write_dataset(filter_group, "n_bins", this % n_bins) - call write_dataset(filter_group, "bins", meshes(this % mesh) % id) + call write_dataset(filter_group, "bins", m % id()) end subroutine to_statepoint function text_label(this, bin) result(label) @@ -240,55 +115,55 @@ contains integer :: i_surf integer :: n_dim integer, allocatable :: ijk(:) + type(RegularMesh) :: m - associate (m => meshes(this % mesh)) - n_dim = m % n_dimension - allocate(ijk(n_dim)) + m = meshes(this % mesh) + n_dim = m % n_dimension() + allocate(ijk(n_dim)) - ! Get flattend mesh index and surface index - i_mesh = (bin - 1) / (4*n_dim) + 1 - i_surf = mod(bin - 1, 4*n_dim) + 1 + ! Get flattend mesh index and surface index + i_mesh = (bin - 1) / (4*n_dim) + 1 + i_surf = mod(bin - 1, 4*n_dim) + 1 - ! Get mesh index part of label - call m % get_indices_from_bin(i_mesh, ijk) - if (m % n_dimension == 1) then - label = "Mesh Index (" // trim(to_str(ijk(1))) // ")" - elseif (m % n_dimension == 2) then - label = "Mesh Index (" // trim(to_str(ijk(1))) // ", " // & - trim(to_str(ijk(2))) // ")" - elseif (m % n_dimension == 3) then - label = "Mesh Index (" // trim(to_str(ijk(1))) // ", " // & - trim(to_str(ijk(2))) // ", " // trim(to_str(ijk(3))) // ")" - end if + ! Get mesh index part of label + call m % get_indices_from_bin(i_mesh, ijk) + if (m % n_dimension() == 1) then + label = "Mesh Index (" // trim(to_str(ijk(1))) // ")" + elseif (m % n_dimension() == 2) then + label = "Mesh Index (" // trim(to_str(ijk(1))) // ", " // & + trim(to_str(ijk(2))) // ")" + elseif (m % n_dimension() == 3) then + label = "Mesh Index (" // trim(to_str(ijk(1))) // ", " // & + trim(to_str(ijk(2))) // ", " // trim(to_str(ijk(3))) // ")" + end if - ! Get surface part of label - select case (i_surf) - case (OUT_LEFT) - label = trim(label) // " Outgoing, x-min" - case (IN_LEFT) - label = trim(label) // " Incoming, x-min" - case (OUT_RIGHT) - label = trim(label) // " Outgoing, x-max" - case (IN_RIGHT) - label = trim(label) // " Incoming, x-max" - case (OUT_BACK) - label = trim(label) // " Outgoing, y-min" - case (IN_BACK) - label = trim(label) // " Incoming, y-min" - case (OUT_FRONT) - label = trim(label) // " Outgoing, y-max" - case (IN_FRONT) - label = trim(label) // " Incoming, y-max" - case (OUT_BOTTOM) - label = trim(label) // " Outgoing, z-min" - case (IN_BOTTOM) - label = trim(label) // " Incoming, z-min" - case (OUT_TOP) - label = trim(label) // " Outgoing, z-max" - case (IN_TOP) - label = trim(label) // " Incoming, z-max" - end select - end associate + ! Get surface part of label + select case (i_surf) + case (OUT_LEFT) + label = trim(label) // " Outgoing, x-min" + case (IN_LEFT) + label = trim(label) // " Incoming, x-min" + case (OUT_RIGHT) + label = trim(label) // " Outgoing, x-max" + case (IN_RIGHT) + label = trim(label) // " Incoming, x-max" + case (OUT_BACK) + label = trim(label) // " Outgoing, y-min" + case (IN_BACK) + label = trim(label) // " Incoming, y-min" + case (OUT_FRONT) + label = trim(label) // " Outgoing, y-max" + case (IN_FRONT) + label = trim(label) // " Incoming, y-max" + case (OUT_BOTTOM) + label = trim(label) // " Outgoing, z-min" + case (IN_BOTTOM) + label = trim(label) // " Incoming, z-min" + case (OUT_TOP) + label = trim(label) // " Outgoing, z-max" + case (IN_TOP) + label = trim(label) // " Incoming, z-max" + end select end function text_label !=============================================================================== @@ -320,16 +195,22 @@ contains integer(C_INT32_T), value, intent(in) :: index_mesh integer(C_INT) :: err + integer :: i integer :: n_dim + type(RegularMesh) :: m err = verify_filter(index) if (err == 0) then select type (f => filters(index) % obj) type is (MeshSurfaceFilter) - if (index_mesh >= 1 .and. index_mesh <= n_meshes) then + if (index_mesh >= 0 .and. index_mesh < n_meshes()) then f % mesh = index_mesh - n_dim = meshes(index_mesh) % n_dimension - f % n_bins = 4*n_dim*product(meshes(index_mesh) % dimension) + m = meshes(index_mesh) + n_dim = m % n_dimension() + f % n_bins = 4*n_dim + do i = 1, n_dim + f % n_bins = f % n_bins * m % dimension(i) + end do else err = E_OUT_OF_BOUNDS call set_errmsg("Index in 'meshes' array is out of bounds.") diff --git a/src/tallies/trigger.F90 b/src/tallies/trigger.F90 index 2cb3bf819..27aa5ec50 100644 --- a/src/tallies/trigger.F90 +++ b/src/tallies/trigger.F90 @@ -257,14 +257,14 @@ contains logical :: print_ebin ! should incoming energy bin be displayed? real(8) :: rel_err = ZERO ! temporary relative error of result real(8) :: std_dev = ZERO ! temporary standard deviration of result - type(RegularMesh), pointer :: m ! surface current mesh + type(RegularMesh) :: m ! surface current mesh ! Get pointer to mesh i_filter_mesh = t % filter(t % find_filter(FILTER_MESH)) i_filter_surf = t % filter(t % find_filter(FILTER_SURFACE)) select type(filt => filters(i_filter_mesh) % obj) type is (MeshFilter) - m => meshes(filt % mesh) + m = meshes(filt % mesh) end select ! initialize bins array @@ -285,8 +285,11 @@ contains end if ! Get the dimensions and number of cells in the mesh - n_dim = m % n_dimension - n_cells = product(m % dimension) + n_dim = m % n_dimension() + n_cells = 1 + do j = 1, n_dim + n_cells = n_cells * m % dimension(j) + end do ! Loop over all the mesh cells do i = 1, n_cells diff --git a/src/tallies/trigger_header.F90 b/src/tallies/trigger_header.F90 index 87507224a..8b3bdbff2 100644 --- a/src/tallies/trigger_header.F90 +++ b/src/tallies/trigger_header.F90 @@ -1,5 +1,7 @@ module trigger_header + use, intrinsic :: ISO_C_BINDING + use constants, only: NONE, N_FILTER_TYPES, ZERO implicit none @@ -22,11 +24,11 @@ module trigger_header !=============================================================================== ! KTRIGGER describes a user-specified precision trigger for k-effective !=============================================================================== - type, public :: KTrigger - integer :: trigger_type = 0 - real(8) :: threshold = ZERO + type, public, bind(C) :: KTrigger + integer(C_INT) :: trigger_type = 0 + real(C_DOUBLE) :: threshold = ZERO end type KTrigger - type(KTrigger), public :: keff_trigger ! trigger for k-effective + type(KTrigger), public, bind(C) :: keff_trigger ! trigger for k-effective end module trigger_header diff --git a/src/thermal.cpp b/src/thermal.cpp index b63d2e6d1..229aa35bc 100644 --- a/src/thermal.cpp +++ b/src/thermal.cpp @@ -153,10 +153,10 @@ ThermalScattering::calculate_xs(double E, double sqrtkT, int* i_temp, // Determine temperature for S(a,b) table double kT = sqrtkT*sqrtkT; int i; - if (temperature_method == TEMPERATURE_NEAREST) { + if (settings::temperature_method == TEMPERATURE_NEAREST) { // If using nearest temperature, do linear search on temperature for (i = 0; i < kTs_.size(); ++i) { - if (abs(kTs_[i] - kT) < K_BOLTZMANN*temperature_tolerance) { + if (abs(kTs_[i] - kT) < K_BOLTZMANN*settings::temperature_tolerance) { break; } } diff --git a/src/xsdata.cpp b/src/xsdata.cpp index d9863694a..8abc1edfd 100644 --- a/src/xsdata.cpp +++ b/src/xsdata.cpp @@ -5,6 +5,11 @@ #include #include +#include "xtensor/xview.hpp" +#include "xtensor/xindex_view.hpp" +#include "xtensor/xmath.hpp" +#include "xtensor/xbuilder.hpp" + #include "openmc/constants.h" #include "openmc/error.h" #include "openmc/math_functions.h" @@ -17,54 +22,53 @@ namespace openmc { // XsData class methods //============================================================================== -XsData::XsData(int energy_groups, int num_delayed_groups, bool fissionable, +XsData::XsData(size_t energy_groups, size_t num_delayed_groups, bool fissionable, int scatter_format, int n_pol, int n_azi) { - int n_ang = n_pol * n_azi; + size_t n_ang = n_pol * n_azi; // check to make sure scatter format is OK before we allocate if (scatter_format != ANGLE_HISTOGRAM && scatter_format != ANGLE_TABULAR && scatter_format != ANGLE_LEGENDRE) { fatal_error("Invalid scatter_format!"); } - // allocate all [temperature][phi][theta][in group] quantities - total = double_2dvec(n_ang, double_1dvec(energy_groups, 0.)); - absorption = double_2dvec(n_ang, double_1dvec(energy_groups, 0.)); - inverse_velocity = double_2dvec(n_ang, double_1dvec(energy_groups, 0.)); + // allocate all [temperature][angle][in group] quantities + std::vector shape = {n_ang, energy_groups}; + total = xt::zeros(shape); + absorption = xt::zeros(shape); + inverse_velocity = xt::zeros(shape); if (fissionable) { - fission = double_2dvec(n_ang, double_1dvec(energy_groups, 0.)); - nu_fission = double_2dvec(n_ang, double_1dvec(energy_groups, 0.)); - prompt_nu_fission = double_2dvec(n_ang, double_1dvec(energy_groups, 0.)); - kappa_fission = double_2dvec(n_ang, double_1dvec(energy_groups, 0.)); + fission = xt::zeros(shape); + nu_fission = xt::zeros(shape); + prompt_nu_fission = xt::zeros(shape); + kappa_fission = xt::zeros(shape); } - // allocate decay_rate; [temperature][phi][theta][delayed group] - decay_rate = double_2dvec(n_ang, double_1dvec(num_delayed_groups, 0.)); + // allocate decay_rate; [temperature][angle][delayed group] + shape[1] = num_delayed_groups; + decay_rate = xt::zeros(shape); if (fissionable) { - // allocate delayed_nu_fission; [temperature][phi][theta][in group][delay group] - delayed_nu_fission = double_3dvec(n_ang, double_2dvec(energy_groups, - double_1dvec(num_delayed_groups, 0.))); + shape = {n_ang, num_delayed_groups, energy_groups}; + // allocate delayed_nu_fission; [temperature][angle][delay group][in group] + delayed_nu_fission = xt::zeros(shape); - // chi_prompt; [temperature][phi][theta][in group][delayed group] - chi_prompt = double_3dvec(n_ang, double_2dvec(energy_groups, - double_1dvec(energy_groups, 0.))); + // chi_prompt; [temperature][angle][in group][out group] + shape = {n_ang, energy_groups, energy_groups}; + chi_prompt = xt::zeros(shape); - // chi_delayed; [temperature][phi][theta][in group][out group][delay group] - chi_delayed = double_4dvec(n_ang, double_3dvec(energy_groups, - double_2dvec(energy_groups, double_1dvec(num_delayed_groups, 0.)))); + // chi_delayed; [temperature][angle][delay group][in group][out group] + shape = {n_ang, num_delayed_groups, energy_groups, energy_groups}; + chi_delayed = xt::zeros(shape); } for (int a = 0; a < n_ang; a++) { if (scatter_format == ANGLE_HISTOGRAM) { - // scatter[a] = std::make_unique(ScattDataHistogram); scatter.emplace_back(new ScattDataHistogram); } else if (scatter_format == ANGLE_TABULAR) { - // scatter[a] = std::make_unique(ScattDataTabular); scatter.emplace_back(new ScattDataTabular); } else if (scatter_format == ANGLE_LEGENDRE) { - // scatter[a] = std::make_unique(ScattDataLegendre); scatter.emplace_back(new ScattDataLegendre); } } @@ -78,13 +82,13 @@ XsData::from_hdf5(hid_t xsdata_grp, bool fissionable, int scatter_format, int legendre_to_tabular_points, bool is_isotropic, int n_pol, int n_azi) { // Reconstruct the dimension information so it doesn't need to be passed - int n_ang = n_pol * n_azi; - int energy_groups = total[0].size(); - int delayed_groups = decay_rate[0].size(); + size_t n_ang = n_pol * n_azi; + size_t energy_groups = total.shape()[1]; + size_t delayed_groups = decay_rate.shape()[1]; // Set the fissionable-specific data if (fissionable) { - fission_from_hdf5(xsdata_grp, n_pol, n_azi, energy_groups, delayed_groups, + fission_from_hdf5(xsdata_grp, n_ang, energy_groups, delayed_groups, is_isotropic); } // Get the non-fission-specific data @@ -93,445 +97,343 @@ XsData::from_hdf5(hid_t xsdata_grp, bool fissionable, int scatter_format, read_nd_vector(xsdata_grp, "inverse-velocity", inverse_velocity); // Get scattering data - scatter_from_hdf5(xsdata_grp, n_pol, n_azi, energy_groups, scatter_format, + scatter_from_hdf5(xsdata_grp, n_ang, energy_groups, scatter_format, final_scatter_format, order_data, max_order, legendre_to_tabular_points); // Check absorption to ensure it is not 0 since it is often the // denominator in tally methods - for (int a = 0; a < n_ang; a++) { - for (int gin = 0; gin < energy_groups; gin++) { - if (absorption[a][gin] == 0.) absorption[a][gin] = 1.e-10; - } - } + xt::filtration(absorption, xt::equal(absorption, 0.)) = 1.e-10; // Get or calculate the total x/s if (object_exists(xsdata_grp, "total")) { read_nd_vector(xsdata_grp, "total", total); } else { - for (int a = 0; a < n_ang; a++) { - for (int gin = 0; gin < energy_groups; gin++) { - total[a][gin] = absorption[a][gin] + scatter[a]->scattxs[gin]; + for (size_t a = 0; a < n_ang; a++) { + for (size_t gin = 0; gin < energy_groups; gin++) { + total(a, gin) = absorption(a, gin) + scatter[a]->scattxs[gin]; } } } // Fix if total is 0, since it is in the denominator when tallying - for (int a = 0; a < n_ang; a++) { - for (int gin = 0; gin < energy_groups; gin++) { - if (total[a][gin] == 0.) total[a][gin] = 1.e-10; - } - } + xt::filtration(total, xt::equal(total, 0.)) = 1.e-10; } //============================================================================== void -XsData::fission_from_hdf5(hid_t xsdata_grp, int n_pol, int n_azi, - int energy_groups, int delayed_groups, bool is_isotropic) +XsData::fission_vector_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang, + size_t energy_groups, size_t delayed_groups, bool is_isotropic) +{ + // Data is provided as nu-fission and chi with a beta for delayed info + + // Get chi + xt::xtensor temp_chi({n_ang, energy_groups}, 0.); + read_nd_vector(xsdata_grp, "chi", temp_chi, true); + + // Normalize chi by summing over the outgoing groups for each incoming angle + temp_chi /= xt::view(xt::sum(temp_chi, {1}), xt::all(), xt::newaxis()); + + // Now every incoming group in prompt_chi and delayed_chi is the normalized + // chi we just made + chi_prompt = xt::view(temp_chi, xt::all(), xt::newaxis(), xt::all()); + chi_delayed = xt::view(temp_chi, xt::all(), xt::newaxis(), xt::newaxis(), + xt::all()); + + // Get nu-fission + xt::xtensor temp_nufiss({n_ang, energy_groups}, 0.); + read_nd_vector(xsdata_grp, "nu-fission", temp_nufiss, true); + + // Get beta (strategy will depend upon the number of dimensions in beta) + hid_t beta_dset = open_dataset(xsdata_grp, "beta"); + int beta_ndims = dataset_ndims(beta_dset); + close_dataset(beta_dset); + int ndim_target = 1; + if (!is_isotropic) ndim_target += 2; + if (beta_ndims == ndim_target) { + xt::xtensor temp_beta({n_ang, delayed_groups}, 0.); + read_nd_vector(xsdata_grp, "beta", temp_beta, true); + + // Set prompt_nu_fission = (1. - beta_total)*nu_fission + prompt_nu_fission = temp_nufiss * (1. - xt::sum(temp_beta, {1})); + + // Set delayed_nu_fission as beta * nu_fission + delayed_nu_fission = + xt::view(temp_beta, xt::all(), xt::all(), xt::newaxis()) * + xt::view(temp_nufiss, xt::all(), xt::newaxis(), xt::all()); + } else if (beta_ndims == ndim_target + 1) { + xt::xtensor temp_beta({n_ang, delayed_groups, energy_groups}, + 0.); + read_nd_vector(xsdata_grp, "beta", temp_beta, true); + + // Set prompt_nu_fission = (1. - beta_total)*nu_fission + prompt_nu_fission = temp_nufiss * (1. - xt::sum(temp_beta, {1})); + + // Set delayed_nu_fission as beta * nu_fission + delayed_nu_fission = temp_beta * + xt::view(temp_nufiss, xt::all(), xt::newaxis(), xt::all()); + } +} + +void +XsData::fission_vector_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang, + size_t energy_groups, size_t delayed_groups) +{ + // Data is provided separately as prompt + delayed nu-fission and chi + + // Get chi-prompt + xt::xtensor temp_chi_p({n_ang, energy_groups}, 0.); + read_nd_vector(xsdata_grp, "chi-prompt", temp_chi_p, true); + + // Normalize chi by summing over the outgoing groups for each incoming angle + temp_chi_p /= xt::view(xt::sum(temp_chi_p, {1}), xt::all(), xt::newaxis()); + + // Get chi-delayed + xt::xtensor temp_chi_d({n_ang, delayed_groups, energy_groups}, 0.); + read_nd_vector(xsdata_grp, "chi-delayed", temp_chi_d, true); + + // Normalize chi by summing over the outgoing groups for each incoming angle + temp_chi_d /= xt::view(xt::sum(temp_chi_d, {2}), + xt::all(), xt::all(), xt::newaxis()); + + // Now assign the prompt and delayed chis by replicating for each incoming group + chi_prompt = xt::view(temp_chi_p, xt::all(), xt::newaxis(), xt::all()); + chi_delayed = xt::view(temp_chi_d, xt::all(), xt::all(), xt::newaxis(), + xt::all()); + + // Get prompt and delayed nu-fission directly + read_nd_vector(xsdata_grp, "prompt-nu-fission", prompt_nu_fission, + true); + read_nd_vector(xsdata_grp, "delayed-nu-fission", + delayed_nu_fission, true); +} + +void +XsData::fission_vector_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang, + size_t energy_groups) +{ + // No beta is provided and there is no prompt/delay distinction. + // Therefore, the code only considers the data as prompt. + + // Get chi + xt::xtensor temp_chi({n_ang, energy_groups}, 0.); + read_nd_vector(xsdata_grp, "chi", temp_chi, true); + + // Normalize chi by summing over the outgoing groups for each incoming angle + temp_chi /= xt::view(xt::sum(temp_chi, {1}), xt::all(), xt::newaxis()); + + // Now every incoming group in self.chi is the normalized chi we just made + chi_prompt = xt::view(temp_chi, xt::all(), xt::newaxis(), xt::all()); + + // Get nu-fission directly + read_nd_vector(xsdata_grp, "nu-fission", prompt_nu_fission, true); +} + +//============================================================================== + +void +XsData::fission_matrix_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang, + size_t energy_groups, size_t delayed_groups, bool is_isotropic) +{ + // Data is provided as nu-fission and chi with a beta for delayed info + + // Get nu-fission matrix + xt::xtensor temp_matrix({n_ang, energy_groups, energy_groups}, 0.); + read_nd_vector(xsdata_grp, "nu-fission", temp_matrix, true); + + // Get beta (strategy will depend upon the number of dimensions in beta) + hid_t beta_dset = open_dataset(xsdata_grp, "beta"); + int beta_ndims = dataset_ndims(beta_dset); + close_dataset(beta_dset); + int ndim_target = 1; + if (!is_isotropic) ndim_target += 2; + if (beta_ndims == ndim_target) { + xt::xtensor temp_beta({n_ang, delayed_groups}, 0.); + read_nd_vector(xsdata_grp, "beta", temp_beta, true); + + xt::xtensor temp_beta_sum({n_ang}, 0.); + temp_beta_sum = xt::sum(temp_beta, {1}); + + // prompt_nu_fission is the sum of this matrix over outgoing groups and + // multiplied by (1 - beta_sum) + prompt_nu_fission = xt::sum(temp_matrix, {2}) * (1. - temp_beta_sum); + + // Store chi-prompt + chi_prompt = xt::view(1.0 - temp_beta_sum, xt::all(), xt::newaxis(), + xt::newaxis()) * temp_matrix; + + // delayed_nu_fission is the sum of this matrix over outgoing groups and + // multiplied by beta + delayed_nu_fission = + xt::view(temp_beta, xt::all(), xt::all(), xt::newaxis()) * + xt::view(xt::sum(temp_matrix, {2}), xt::all(), xt::newaxis(), xt::all()); + + // Store chi-delayed + chi_delayed = + xt::view(temp_beta, xt::all(), xt::all(), xt::newaxis(), xt::newaxis()) * + xt::view(temp_matrix, xt::all(), xt::newaxis(), xt::all(), xt::all()); + + } else if (beta_ndims == ndim_target + 1) { + xt::xtensor temp_beta({n_ang, delayed_groups, energy_groups}, 0.); + read_nd_vector(xsdata_grp, "beta", temp_beta, true); + + xt::xtensor temp_beta_sum({n_ang, energy_groups}, 0.); + temp_beta_sum = xt::sum(temp_beta, {1}); + + // prompt_nu_fission is the sum of this matrix over outgoing groups and + // multiplied by (1 - beta_sum) + prompt_nu_fission = xt::sum(temp_matrix, {2}) * (1. - temp_beta_sum); + + // Store chi-prompt + chi_prompt = xt::view(1.0 - temp_beta_sum, xt::all(), xt::all(), + xt::newaxis()) * temp_matrix; + + // delayed_nu_fission is the sum of this matrix over outgoing groups and + // multiplied by beta + delayed_nu_fission = temp_beta * + xt::view(xt::sum(temp_matrix, {2}), xt::all(), xt::newaxis(), xt::all()); + + // Store chi-delayed + chi_delayed = + xt::view(temp_beta, xt::all(), xt::all(), xt::all(), xt::newaxis()) * + xt::view(temp_matrix, xt::all(), xt::newaxis(), xt::all(), xt::all()); + } + + //Normalize both chis + chi_prompt /= xt::view(xt::sum(chi_prompt, {2}), + xt::all(), xt::all(), xt::newaxis()); + + chi_delayed /= xt::view(xt::sum(chi_delayed, {3}), + xt::all(), xt::all(), xt::all(), xt::newaxis()); +} + +void +XsData::fission_matrix_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang, + size_t energy_groups, size_t delayed_groups) +{ + // Data is provided separately as prompt + delayed nu-fission and chi + + // Get the prompt nu-fission matrix + xt::xtensor temp_matrix_p({n_ang, energy_groups, energy_groups}, 0.); + read_nd_vector(xsdata_grp, "prompt-nu-fission", temp_matrix_p, true); + + // prompt_nu_fission is the sum over outgoing groups + prompt_nu_fission = xt::sum(temp_matrix_p, {2}); + + // chi_prompt is this matrix but normalized over outgoing groups, which we + // have already stored in prompt_nu_fission + chi_prompt = temp_matrix_p / + xt::view(prompt_nu_fission, xt::all(), xt::all(), xt::newaxis()); + + // Get the delayed nu-fission matrix + xt::xtensor temp_matrix_d({n_ang, delayed_groups, energy_groups, + energy_groups}, 0.); + read_nd_vector(xsdata_grp, "delayed-nu-fission", temp_matrix_d, true); + + // delayed_nu_fission is the sum over outgoing groups + delayed_nu_fission = xt::sum(temp_matrix_d, {3}); + + // chi_prompt is this matrix but normalized over outgoing groups, which we + // have already stored in prompt_nu_fission + chi_delayed = temp_matrix_d / + xt::view(delayed_nu_fission, xt::all(), xt::all(), xt::all(), xt::newaxis()); +} + +void +XsData::fission_matrix_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang, + size_t energy_groups) +{ + // No beta is provided and there is no prompt/delay distinction. + // Therefore, the code only considers the data as prompt. + + // Get nu-fission matrix + xt::xtensor temp_matrix({n_ang, energy_groups, energy_groups}, 0.); + read_nd_vector(xsdata_grp, "nu-fission", temp_matrix, true); + + // prompt_nu_fission is the sum over outgoing groups + prompt_nu_fission = xt::sum(temp_matrix, {2}); + + // chi_prompt is this matrix but normalized over outgoing groups, which we + // have already stored in prompt_nu_fission + chi_prompt = temp_matrix / xt::view(prompt_nu_fission, xt::all(), xt::all(), + xt::newaxis()); +} + +//============================================================================== + +void +XsData::fission_from_hdf5(hid_t xsdata_grp, size_t n_ang, size_t energy_groups, + size_t delayed_groups, bool is_isotropic) { - int n_ang = n_pol * n_azi; // Get the fission and kappa_fission data xs; these are optional read_nd_vector(xsdata_grp, "fission", fission); read_nd_vector(xsdata_grp, "kappa-fission", kappa_fission); - // Set/get beta - double_3dvec temp_beta =double_3dvec(n_ang, double_2dvec(energy_groups, - double_1dvec(delayed_groups, 0.))); - if (object_exists(xsdata_grp, "beta")) { - hid_t xsdata = open_dataset(xsdata_grp, "beta"); - int ndims = dataset_ndims(xsdata); - - // raise ndims to make the isotropic ndims the same as angular - if (is_isotropic) ndims += 2; - - if (ndims == 3) { - // Beta is input as [delayed group] - double_1dvec temp_arr(n_pol * n_azi * delayed_groups); - read_nd_vector(xsdata_grp, "beta", temp_arr); - - // Broadcast to all incoming groups - int temp_idx = 0; - for (int a = 0; a < n_ang; a++) { - for (int dg = 0; dg < delayed_groups; dg++) { - // Set the first group index and copy the rest - temp_beta[a][0][dg] = temp_arr[temp_idx++]; - for (int gin = 1; gin < energy_groups; gin++) { - temp_beta[a][gin] = temp_beta[a][0]; - } - } - } - } else if (ndims == 4) { - // Beta is input as [in group][delayed group] - read_nd_vector(xsdata_grp, "beta", temp_beta); + // Get the data; the strategy for doing so depends on if the data is provided + // as a nu-fission matrix or a set of chi and nu-fission vectors + if (object_exists(xsdata_grp, "chi") || + object_exists(xsdata_grp, "chi-prompt")) { + if (delayed_groups == 0) { + fission_vector_no_delayed_from_hdf5(xsdata_grp, n_ang, energy_groups); } else { - fatal_error("beta must be provided as a 3D or 4D array!"); - } - } - - // If chi is provided, set chi-prompt and chi-delayed - if (object_exists(xsdata_grp, "chi")) { - double_2dvec temp_arr(n_ang, double_1dvec(energy_groups)); - read_nd_vector(xsdata_grp, "chi", temp_arr); - - for (int a = 0; a < n_ang; a++) { - // First set the first group - for (int gout = 0; gout < energy_groups; gout++) { - chi_prompt[a][0][gout] = temp_arr[a][gout]; - } - - // Now normalize this data - double chi_sum = std::accumulate(chi_prompt[a][0].begin(), - chi_prompt[a][0].end(), - 0.); - if (chi_sum <= 0.) { - fatal_error("Encountered chi for a group that is <= 0!"); - } - for (int gout = 0; gout < energy_groups; gout++) { - chi_prompt[a][0][gout] /= chi_sum; - } - - // And extend to the remaining incoming groups - for (int gin = 1; gin < energy_groups; gin++) { - chi_prompt[a][gin] = chi_prompt[a][0]; - } - - // Finally set chi-delayed equal to chi-prompt - // Set chi-delayed to chi-prompt - for(int gin = 0; gin < energy_groups; gin++) { - for (int gout = 0; gout < energy_groups; gout++) { - for (int dg = 0; dg < delayed_groups; dg++) { - chi_delayed[a][gin][gout][dg] = - chi_prompt[a][gin][gout]; - } - } + if (object_exists(xsdata_grp, "beta")) { + fission_vector_beta_from_hdf5(xsdata_grp, n_ang, energy_groups, + delayed_groups, is_isotropic); + } else { + fission_vector_no_beta_from_hdf5(xsdata_grp, n_ang, energy_groups, + delayed_groups); } } - } - - // If nu-fission is provided, set prompt- and delayed-nu-fission; - // if nu-fission is a matrix, set chi-prompt and chi-delayed. - if (object_exists(xsdata_grp, "nu-fission")) { - hid_t xsdata = open_dataset(xsdata_grp, "nu-fission"); - int ndims = dataset_ndims(xsdata); - // raise ndims to make the isotropic ndims the same as angular - if (is_isotropic) ndims += 2; - - if (ndims == 3) { - // nu-fission is a 3-d array - read_nd_vector(xsdata_grp, "nu-fission", prompt_nu_fission); - - // set delayed-nu-fission and correct prompt-nu-fission with beta - for (int a = 0; a < n_ang; a++) { - for (int gin = 0; gin < energy_groups; gin++) { - for (int dg = 0; dg < delayed_groups; dg++) { - delayed_nu_fission[a][gin][dg] = - temp_beta[a][gin][dg] * prompt_nu_fission[a][gin]; - } - - // Correct the prompt-nu-fission using the delayed neutron fraction - if (delayed_groups > 0) { - double beta_sum = std::accumulate(temp_beta[a][gin].begin(), - temp_beta[a][gin].end(), 0.); - prompt_nu_fission[a][gin] *= (1. - beta_sum); - } - } - } - - } else if (ndims == 4) { - // nu-fission is a matrix - read_nd_vector(xsdata_grp, "nu-fission", chi_prompt); - - // Normalize the chi info so the CDF is 1. - for (int a = 0; a < n_ang; a++) { - for (int gin = 0; gin < energy_groups; gin++) { - double chi_sum = std::accumulate(chi_prompt[a][gin].begin(), - chi_prompt[a][gin].end(), 0.); - // Set the vector nu-fission from the matrix nu-fission - prompt_nu_fission[a][gin] = chi_sum; - - if (chi_sum >= 0.) { - for (int gout = 0; gout < energy_groups; gout++) { - chi_prompt[a][gin][gout] /= chi_sum; - } - } else { - fatal_error("Encountered chi for a group that is <= 0!"); - } - } - - // set chi-delayed to chi-prompt - for (int gin = 0; gin < energy_groups; gin++) { - for (int gout = 0; gout < energy_groups; gout++) { - for (int dg = 0; dg < delayed_groups; dg++) { - chi_delayed[a][gin][gout][dg] = - chi_prompt[a][gin][gout]; - } - } - } - - // Set the delayed-nu-fission and correct prompt-nu-fission with beta - for (int gin = 0; gin < energy_groups; gin++) { - for (int dg = 0; dg < delayed_groups; dg++) { - delayed_nu_fission[a][gin][dg] = - temp_beta[a][gin][dg] * - prompt_nu_fission[a][gin]; - } - - // Correct prompt-nu-fission using the delayed neutron fraction - if (delayed_groups > 0) { - double beta_sum = std::accumulate(temp_beta[a][gin].begin(), - temp_beta[a][gin].end(), 0.); - prompt_nu_fission[a][gin] *= (1. - beta_sum); - } - } - } + } else { + if (delayed_groups == 0) { + fission_matrix_no_delayed_from_hdf5(xsdata_grp, n_ang, energy_groups); } else { - fatal_error("nu-fission must be provided as a 3D or 4D array!"); - } - - close_dataset(xsdata); - } - - // If chi-prompt is provided, set chi-prompt - if (object_exists(xsdata_grp, "chi-prompt")) { - double_2dvec temp_arr(n_ang, double_1dvec(energy_groups)); - read_nd_vector(xsdata_grp, "chi-prompt", temp_arr); - - for (int a = 0; a < n_ang; a++) { - for (int gin = 0; gin < energy_groups; gin++) { - for (int gout = 0; gout < energy_groups; gout++) { - chi_prompt[a][gin][gout] = temp_arr[a][gout]; - } - - // Normalize chi so its CDF goes to 1 - double chi_sum = std::accumulate(chi_prompt[a][gin].begin(), - chi_prompt[a][gin].end(), 0.); - if (chi_sum >= 0.) { - for (int gout = 0; gout < energy_groups; gout++) { - chi_prompt[a][gin][gout] /= chi_sum; - } - } else { - fatal_error("Encountered chi-prompt for a group that is <= 0.!"); - } + if (object_exists(xsdata_grp, "beta")) { + fission_matrix_beta_from_hdf5(xsdata_grp, n_ang, energy_groups, + delayed_groups, is_isotropic); + } else { + fission_matrix_no_beta_from_hdf5(xsdata_grp, n_ang, energy_groups, + delayed_groups); } } } - // If chi-delayed is provided, set chi-delayed - if (object_exists(xsdata_grp, "chi-delayed")) { - hid_t xsdata = open_dataset(xsdata_grp, "chi-delayed"); - int ndims = dataset_ndims(xsdata); - // raise ndims to make the isotropic ndims the same as angular - if (is_isotropic) ndims += 2; - close_dataset(xsdata); - - if (ndims == 3) { - // chi-delayed is a [in group] vector - double_2dvec temp_arr(n_ang, double_1dvec(energy_groups)); - read_nd_vector(xsdata_grp, "chi-delayed", temp_arr); - - for (int a = 0; a < n_ang; a++) { - // normalize the chi CDF to 1 - double chi_sum = std::accumulate(temp_arr[a].begin(), - temp_arr[a].end(), 0.); - if (chi_sum <= 0.) { - fatal_error("Encountered chi-delayed for a group that is <= 0!"); - } - - // set chi-delayed - for (int gin = 0; gin < energy_groups; gin++) { - for (int gout = 0; gout < energy_groups; gout++) { - for (int dg = 0; dg < delayed_groups; dg++) { - chi_delayed[a][gin][gout][dg] = temp_arr[a][gout] / chi_sum; - } - } - } - } - } else if (ndims == 4) { - // chi_delayed is a matrix - read_nd_vector(xsdata_grp, "chi-delayed", chi_delayed); - - // Normalize the chi info so the CDF is 1. - for (int a = 0; a < n_ang; a++) { - for (int dg = 0; dg < delayed_groups; dg++) { - for (int gin = 0; gin < energy_groups; gin++) { - double chi_sum = 0.; - for (int gout = 0; gout < energy_groups; gout++) { - chi_sum += chi_delayed[a][gin][gout][dg]; - } - - if (chi_sum > 0.) { - for (int gout = 0; gout < energy_groups; gout++) { - chi_delayed[a][gin][gout][dg] /= chi_sum; - } - } else { - fatal_error("Encountered chi-delayed for a group that is <= 0!"); - } - } - } - } - } else { - fatal_error("chi-delayed must be provided as a 3D or 4D array!"); - } - } - - // Get prompt-nu-fission, if present - if (object_exists(xsdata_grp, "prompt-nu-fission")) { - hid_t xsdata = open_dataset(xsdata_grp, "prompt-nu-fission"); - int ndims = dataset_ndims(xsdata); - // raise ndims to make the isotropic ndims the same as angular - if (is_isotropic) ndims += 2; - close_dataset(xsdata); - - if (ndims == 3) { - // prompt-nu-fission is a [in group] vector - read_nd_vector(xsdata_grp, "prompt-nu-fission", - prompt_nu_fission); - } else if (ndims == 4) { - // prompt nu fission is a matrix, - // so set prompt_nu_fiss & chi_prompt - double_3dvec temp_arr(n_ang, double_2dvec(energy_groups, - double_1dvec(energy_groups))); - read_nd_vector(xsdata_grp, "prompt-nu-fission", temp_arr); - - // The prompt_nu_fission vector from the matrix form - for (int a = 0; a < n_ang; a++) { - for (int gin = 0; gin < energy_groups; gin++) { - double prompt_sum = std::accumulate(temp_arr[a][gin].begin(), - temp_arr[a][gin].end(), 0.); - prompt_nu_fission[a][gin] = prompt_sum; - } - - // The chi_prompt data is just the normalized fission matrix - for (int gin= 0; gin < energy_groups; gin++) { - if (prompt_nu_fission[a][gin] > 0.) { - for (int gout = 0; gout < energy_groups; gout++) { - chi_prompt[a][gin][gout] = - temp_arr[a][gin][gout] / prompt_nu_fission[a][gin]; - } - } else { - fatal_error("Encountered chi-prompt for a group that is <= 0!"); - } - } - } - - } else { - fatal_error("prompt-nu-fission must be provided as a 3D or 4D array!"); - } - } - - // Get delayed-nu-fission, if present - if (object_exists(xsdata_grp, "delayed-nu-fission")) { - hid_t xsdata = open_dataset(xsdata_grp, "delayed-nu-fission"); - int ndims = dataset_ndims(xsdata); - close_dataset(xsdata); - // raise ndims to make the isotropic ndims the same as angular - if (is_isotropic) ndims += 2; - - if (ndims == 3) { - // delayed-nu-fission is an [in group] vector - if (temp_beta[0][0][0] == 0.) { - fatal_error("cannot set delayed-nu-fission with a 1D array if " - "beta is not provided"); - } - double_2dvec temp_arr(n_ang, double_1dvec(energy_groups)); - read_nd_vector(xsdata_grp, "delayed-nu-fission", temp_arr); - - for (int a = 0; a < n_ang; a++) { - for (int gin = 0; gin < energy_groups; gin++) { - for (int dg = 0; dg < delayed_groups; dg++) { - // Set delayed-nu-fission using beta - delayed_nu_fission[a][gin][dg] = - temp_beta[a][gin][dg] * temp_arr[a][gin]; - } - } - } - - } else if (ndims == 4) { - read_nd_vector(xsdata_grp, "delayed-nu-fission", - delayed_nu_fission); - - } else if (ndims == 5) { - // This will contain delayed-nu-fision and chi-delayed data - double_4dvec temp_arr(n_ang, double_3dvec(energy_groups, - double_2dvec(energy_groups, double_1dvec(delayed_groups)))); - read_nd_vector(xsdata_grp, "delayed-nu-fission", temp_arr); - - // Set the 3D delayed-nu-fission matrix and 4D chi-delayed matrix - // from the 4D delayed-nu-fission matrix - for (int a = 0; a < n_ang; a++) { - for (int dg = 0; dg < delayed_groups; dg++) { - for (int gin = 0; gin < energy_groups; gin++) { - double gout_sum = 0.; - for (int gout = 0; gout < energy_groups; gout++) { - gout_sum += temp_arr[a][gin][gout][dg]; - chi_delayed[a][gin][gout][dg] = temp_arr[a][gin][gout][dg]; - } - delayed_nu_fission[a][gin][dg] = gout_sum; - // Normalize chi-delayed - if (gout_sum > 0.) { - for (int gout = 0; gout < energy_groups; gout++) { - chi_delayed[a][gin][gout][dg] /= gout_sum; - } - } else { - fatal_error("Encountered chi-delayed for a group that is <= 0!"); - } - } - } - } - - } else { - fatal_error("prompt-nu-fission must be provided as a 3D, 4D, or 5D " - "array!"); - } - } - // Combine prompt_nu_fission and delayed_nu_fission into nu_fission - for (int a = 0; a < n_ang; a++) { - for (int gin = 0; gin < energy_groups; gin++) { - nu_fission[a][gin] = - std::accumulate(delayed_nu_fission[a][gin].begin(), - delayed_nu_fission[a][gin].end(), - prompt_nu_fission[a][gin]); - } + if (delayed_groups == 0) { + nu_fission = prompt_nu_fission; + } else { + nu_fission = prompt_nu_fission + xt::sum(delayed_nu_fission, {1}); } } //============================================================================== void -XsData::scatter_from_hdf5(hid_t xsdata_grp, int n_pol, int n_azi, - int energy_groups, int scatter_format, int final_scatter_format, - int order_data, int max_order, int legendre_to_tabular_points) +XsData::scatter_from_hdf5(hid_t xsdata_grp, size_t n_ang, size_t energy_groups, + int scatter_format, int final_scatter_format, int order_data, + int max_order, int legendre_to_tabular_points) { - int n_ang = n_pol * n_azi; if (!object_exists(xsdata_grp, "scatter_data")) { fatal_error("Must provide scatter_data group!"); } hid_t scatt_grp = open_group(xsdata_grp, "scatter_data"); // Get the outgoing group boundary indices - int_2dvec gmin(n_ang, int_1dvec(energy_groups)); + xt::xtensor gmin({n_ang, energy_groups}, 0.); read_nd_vector(scatt_grp, "g_min", gmin, true); - int_2dvec gmax(n_ang, int_1dvec(energy_groups)); + xt::xtensor gmax({n_ang, energy_groups}, 0.); read_nd_vector(scatt_grp, "g_max", gmax, true); // Make gmin and gmax start from 0 vice 1 as they do in the library - for (int a = 0; a < n_ang; a++) { - for (int gin = 0; gin < energy_groups; gin++) { - gmin[a][gin] -= 1; - gmax[a][gin] -= 1; - } - } + gmin -= 1; + gmax -= 1; // Now use this info to find the length of a vector to hold the flattened // data. - int length = 0; - for (int a = 0; a < n_ang; a++) { - for (int gin = 0; gin < energy_groups; gin++) { - length += order_data * (gmax[a][gin] - gmin[a][gin] + 1); - } - } - double_1dvec temp_arr(length); + size_t length = order_data * xt::sum(gmax - gmin + 1)(); + + double_4dvec input_scatt(n_ang, double_3dvec(energy_groups)); + xt::xtensor temp_arr({length}, 0.); read_nd_vector(scatt_grp, "scatter_matrix", temp_arr, true); // Compare the number of orders given with the max order of the problem; @@ -545,15 +447,13 @@ XsData::scatter_from_hdf5(hid_t xsdata_grp, int n_pol, int n_azi, // convert the flattened temp_arr to a jagged array for passing to // scatt data - double_4dvec input_scatt(n_ang, double_3dvec(energy_groups)); - - int temp_idx = 0; - for (int a = 0; a < n_ang; a++) { - for (int gin = 0; gin < energy_groups; gin++) { - input_scatt[a][gin].resize(gmax[a][gin] - gmin[a][gin] + 1); - for (int i_gout = 0; i_gout < input_scatt[a][gin].size(); i_gout++) { + size_t temp_idx = 0; + for (size_t a = 0; a < n_ang; a++) { + for (size_t gin = 0; gin < energy_groups; gin++) { + input_scatt[a][gin].resize(gmax(a, gin) - gmin(a, gin) + 1); + for (size_t i_gout = 0; i_gout < input_scatt[a][gin].size(); i_gout++) { input_scatt[a][gin][i_gout].resize(order_dim); - for (int l = 0; l < order_dim; l++) { + for (size_t l = 0; l < order_dim; l++) { input_scatt[a][gin][i_gout][l] = temp_arr[temp_idx++]; } // Adjust index for the orders we didnt take @@ -561,44 +461,46 @@ XsData::scatter_from_hdf5(hid_t xsdata_grp, int n_pol, int n_azi, } } } - temp_arr.clear(); // Get multiplication matrix double_3dvec temp_mult(n_ang, double_2dvec(energy_groups)); if (object_exists(scatt_grp, "multiplicity_matrix")) { - temp_arr.resize(length / order_data); + temp_arr.resize({length / order_data}); read_nd_vector(scatt_grp, "multiplicity_matrix", temp_arr); // convert the flat temp_arr to a jagged array for passing to scatt data - int temp_idx = 0; - for (int a = 0; a < n_ang; a++) { - for (int gin = 0; gin < energy_groups; gin++) { - temp_mult[a][gin].resize(gmax[a][gin] - gmin[a][gin] + 1); - for (int i_gout = 0; i_gout < temp_mult[a][gin].size(); i_gout++) { + size_t temp_idx = 0; + for (size_t a = 0; a < n_ang; a++) { + for (size_t gin = 0; gin < energy_groups; gin++) { + temp_mult[a][gin].resize(gmax(a, gin) - gmin(a, gin) + 1); + for (size_t i_gout = 0; i_gout < temp_mult[a][gin].size(); i_gout++) { temp_mult[a][gin][i_gout] = temp_arr[temp_idx++]; } } } } else { // Use a default: multiplicities are 1.0. - for (int a = 0; a < n_ang; a++) { - for (int gin = 0; gin < energy_groups; gin++) { - temp_mult[a][gin].resize(gmax[a][gin] - gmin[a][gin] + 1); - for (int i_gout = 0; i_gout < temp_mult[a][gin].size(); i_gout++) { + for (size_t a = 0; a < n_ang; a++) { + for (size_t gin = 0; gin < energy_groups; gin++) { + temp_mult[a][gin].resize(gmax(a, gin) - gmin(a, gin) + 1); + for (size_t i_gout = 0; i_gout < temp_mult[a][gin].size(); i_gout++) { temp_mult[a][gin][i_gout] = 1.; } } } } - temp_arr.clear(); close_group(scatt_grp); // Finally, convert the Legendre data to tabular, if needed if (scatter_format == ANGLE_LEGENDRE && final_scatter_format == ANGLE_TABULAR) { - for (int a = 0; a < n_ang; a++) { + for (size_t a = 0; a < n_ang; a++) { ScattDataLegendre legendre_scatt; - legendre_scatt.init(gmin[a], gmax[a], temp_mult[a], input_scatt[a]); + xt::xtensor in_gmin = xt::view(gmin, a, xt::all()); + xt::xtensor in_gmax = xt::view(gmax, a, xt::all()); + + legendre_scatt.init(in_gmin, in_gmax, + temp_mult[a], input_scatt[a]); // Now create a tabular version of legendre_scatt convert_legendre_to_tabular(legendre_scatt, @@ -610,8 +512,10 @@ XsData::scatter_from_hdf5(hid_t xsdata_grp, int n_pol, int n_azi, } else { // We are sticking with the current representation // Initialize the ScattData object with this data - for (int a = 0; a < n_ang; a++) { - scatter[a]->init(gmin[a], gmax[a], temp_mult[a], input_scatt[a]); + for (size_t a = 0; a < n_ang; a++) { + xt::xtensor in_gmin = xt::view(gmin, a, xt::all()); + xt::xtensor in_gmax = xt::view(gmax, a, xt::all()); + scatter[a]->init(in_gmin, in_gmax, temp_mult[a], input_scatt[a]); } } } @@ -620,80 +524,35 @@ XsData::scatter_from_hdf5(hid_t xsdata_grp, int n_pol, int n_azi, void XsData::combine(const std::vector& those_xs, - const double_1dvec& scalars) + const std::vector& scalars) { // Combine the non-scattering data - for (int i = 0; i < those_xs.size(); i++) { + for (size_t i = 0; i < those_xs.size(); i++) { XsData* that = those_xs[i]; if (!equiv(*that)) fatal_error("Cannot combine the XsData objects!"); double scalar = scalars[i]; - for (int a = 0; a < total.size(); a++) { - for (int gin = 0; gin < total[a].size(); gin++) { - total[a][gin] += scalar * that->total[a][gin]; - absorption[a][gin] += scalar * that->absorption[a][gin]; - if (i == 0) { - inverse_velocity[a][gin] = that->inverse_velocity[a][gin]; - } - if (that->prompt_nu_fission.size() > 0) { - nu_fission[a][gin] += scalar * that->nu_fission[a][gin]; - prompt_nu_fission[a][gin] += - scalar * that->prompt_nu_fission[a][gin]; - kappa_fission[a][gin] += scalar * that->kappa_fission[a][gin]; - fission[a][gin] += scalar * that->fission[a][gin]; - - for (int dg = 0; dg < delayed_nu_fission[a][gin].size(); dg++) { - delayed_nu_fission[a][gin][dg] += - scalar * that->delayed_nu_fission[a][gin][dg]; - } - - for (int gout = 0; gout < chi_prompt[a][gin].size(); gout++) { - chi_prompt[a][gin][gout] += - scalar * that->chi_prompt[a][gin][gout]; - - for (int dg = 0; dg < chi_delayed[a][gin][gout].size(); dg++) { - chi_delayed[a][gin][gout][dg] += - scalar * that->chi_delayed[a][gin][gout][dg]; - } - } - } - } - - for (int dg = 0; dg < decay_rate[a].size(); dg++) { - decay_rate[a][dg] += scalar * that->decay_rate[a][dg]; - } - - // Normalize chi - if (chi_prompt.size() > 0) { - for (int gin = 0; gin < chi_prompt[a].size(); gin++) { - double norm = std::accumulate(chi_prompt[a][gin].begin(), - chi_prompt[a][gin].end(), 0.); - if (norm > 0.) { - for (int gout = 0; gout < chi_prompt[a][gin].size(); gout++) { - chi_prompt[a][gin][gout] /= norm; - } - } - - for (int dg = 0; dg < chi_delayed[a][gin][0].size(); dg++) { - norm = 0.; - for (int gout = 0; gout < chi_delayed[a][gin].size(); gout++) { - norm += chi_delayed[a][gin][gout][dg]; - } - if (norm > 0.) { - for (int gout = 0; gout < chi_delayed[a][gin].size(); gout++) { - chi_delayed[a][gin][gout][dg] /= norm; - } - } - } - } - } + total += scalar * that->total; + absorption += scalar * that->absorption; + if (i == 0) { + inverse_velocity = that->inverse_velocity; } + if (that->prompt_nu_fission.shape()[0] > 0) { + nu_fission += scalar * that->nu_fission; + prompt_nu_fission += scalar * that->prompt_nu_fission; + kappa_fission += scalar * that->kappa_fission; + fission += scalar * that->fission; + delayed_nu_fission += scalar * that->delayed_nu_fission; + chi_prompt += scalar * that->chi_prompt; + chi_delayed += scalar * that->chi_delayed; + } + decay_rate += scalar * that->decay_rate; } // Allow the ScattData object to combine itself - for (int a = 0; a < total.size(); a++) { + for (size_t a = 0; a < total.shape()[0]; a++) { // Build vector of the scattering objects to incorporate std::vector those_scatts(those_xs.size()); - for (int i = 0; i < those_xs.size(); i++) { + for (size_t i = 0; i < those_xs.size(); i++) { those_scatts[i] = those_xs[i]->scatter[a].get(); } @@ -707,8 +566,7 @@ XsData::combine(const std::vector& those_xs, bool XsData::equiv(const XsData& that) { - return ((absorption.size() == that.absorption.size()) && - (absorption[0].size() == that.absorption[0].size())); + return (absorption.shape() == that.absorption.shape()); } } //namespace openmc diff --git a/tests/1d_mgxs.h5 b/tests/1d_mgxs.h5 deleted file mode 100644 index 0f747345a..000000000 Binary files a/tests/1d_mgxs.h5 and /dev/null differ diff --git a/tests/regression_tests/mg_basic/inputs_true.dat b/tests/regression_tests/mg_basic/inputs_true.dat index a0efdbde0..4f2fd3f0b 100644 --- a/tests/regression_tests/mg_basic/inputs_true.dat +++ b/tests/regression_tests/mg_basic/inputs_true.dat @@ -1,97 +1,64 @@ - - - - - - - - - - - - + + + + + + - - - - - - - - - - - - - - - - + + + + + + - ../../1d_mgxs.h5 - + 2g.h5 + - + - + - + - + - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + eigenvalue - 100 + 1000 10 5 - 0.0 0.0 0.0 10.0 10.0 5.0 + 0.0 -1000.0 -1000.0 154.90833333333333 1000.0 1000.0 + + false + multi-group + + false + diff --git a/tests/regression_tests/mg_basic/results_true.dat b/tests/regression_tests/mg_basic/results_true.dat index ddb57d00b..dede97007 100644 --- a/tests/regression_tests/mg_basic/results_true.dat +++ b/tests/regression_tests/mg_basic/results_true.dat @@ -1,2 +1,2 @@ k-combined: -1.073147E+00 1.602384E-02 +1.005345E+00 1.109180E-02 diff --git a/tests/regression_tests/mg_basic/test.py b/tests/regression_tests/mg_basic/test.py index 3c22e6040..7b456bb9d 100644 --- a/tests/regression_tests/mg_basic/test.py +++ b/tests/regression_tests/mg_basic/test.py @@ -1,9 +1,97 @@ +import os + +import numpy as np + +import openmc from openmc.examples import slab_mg from tests.testing_harness import PyAPITestHarness +def create_library(): + # Instantiate the energy group data and file object + groups = openmc.mgxs.EnergyGroups(group_edges=[0.0, 0.625, 20.0e6]) + + mg_cross_sections_file = openmc.MGXSLibrary(groups) + + # Make the base, isotropic data + nu = [2.50, 2.50] + fiss = np.array([0.002817, 0.097]) + capture = [0.008708, 0.02518] + absorption = np.add(capture, fiss) + scatter = np.array( + [[[0.31980, 0.06694], [0.004555, -0.0003972]], + [[0.00000, 0.00000], [0.424100, 0.05439000]]]) + total = [0.33588, 0.54628] + chi = [1., 0.] + + mat_1 = openmc.XSdata('mat_1', groups) + mat_1.order = 1 + mat_1.set_nu_fission(np.multiply(nu, fiss)) + mat_1.set_absorption(absorption) + mat_1.set_scatter_matrix(scatter) + mat_1.set_total(total) + mat_1.set_chi(chi) + mg_cross_sections_file.add_xsdata(mat_1) + + # Make a version of mat-1 which has a tabular representation of the + # scattering vice Legendre with 33 points + mat_2 = mat_1.convert_scatter_format('tabular', 33) + mat_2.name = 'mat_2' + mg_cross_sections_file.add_xsdata(mat_2) + + # Make a version of mat-1 which has a histogram representation of the + # scattering vice Legendre with 33 bins + mat_3 = mat_1.convert_scatter_format('histogram', 33) + mat_3.name = 'mat_3' + mg_cross_sections_file.add_xsdata(mat_3) + + # Make a version which uses a fission matrix vice chi & nu-fission + mat_4 = openmc.XSdata('mat_4', groups) + mat_4.order = 1 + mat_4.set_nu_fission(np.outer(np.multiply(nu, fiss), chi)) + mat_4.set_absorption(absorption) + mat_4.set_scatter_matrix(scatter) + mat_4.set_total(total) + mg_cross_sections_file.add_xsdata(mat_4) + + # Make an angle-dependent version of mat_1 with 2 polar and 2 azim. angles + mat_5 = mat_1.convert_representation('angle', 2, 2) + mat_5.name = 'mat_5' + mg_cross_sections_file.add_xsdata(mat_5) + + # Make a copy of mat_1 for testing microscopic cross sections + mat_6 = openmc.XSdata('mat_6', groups) + mat_6.order = 1 + mat_6.set_nu_fission(np.multiply(nu, fiss)) + mat_6.set_absorption(absorption) + mat_6.set_scatter_matrix(scatter) + mat_6.set_total(total) + mat_6.set_chi(chi) + mg_cross_sections_file.add_xsdata(mat_6) + + # Write the file + mg_cross_sections_file.export_to_hdf5('2g.h5') + + +class MGXSTestHarness(PyAPITestHarness): + def _cleanup(self): + super()._cleanup() + f = '2g.h5' + if os.path.exists(f): + os.remove(f) + + def test_mg_basic(): - model = slab_mg() + create_library() + mat_names = ['base leg', 'base tab', 'base hist', 'base matrix', + 'base ang', 'micro'] + model = slab_mg(num_regions=6, mat_names=mat_names) + # Modify the last material to be a microscopic combination of nuclides + model.materials[-1] = openmc.Material(name='micro', material_id=6) + model.materials[-1].set_density("sum") + model.materials[-1].add_nuclide("mat_1", 0.5) + model.materials[-1].add_nuclide("mat_6", 0.5) + harness = PyAPITestHarness('statepoint.10.h5', model) harness.main() diff --git a/tests/regression_tests/mg_nuclide/__init__.py b/tests/regression_tests/mg_basic_delayed/__init__.py similarity index 100% rename from tests/regression_tests/mg_nuclide/__init__.py rename to tests/regression_tests/mg_basic_delayed/__init__.py diff --git a/tests/regression_tests/mg_basic_delayed/inputs_true.dat b/tests/regression_tests/mg_basic_delayed/inputs_true.dat new file mode 100644 index 000000000..9fb7afe7e --- /dev/null +++ b/tests/regression_tests/mg_basic_delayed/inputs_true.dat @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + 2g.h5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 1000 + 10 + 5 + + + 0.0 -1000.0 -1000.0 154.90833333333333 1000.0 1000.0 + + + + false + + multi-group + + false + + diff --git a/tests/regression_tests/mg_basic_delayed/results_true.dat b/tests/regression_tests/mg_basic_delayed/results_true.dat new file mode 100644 index 000000000..85312b8e1 --- /dev/null +++ b/tests/regression_tests/mg_basic_delayed/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +1.003463E+00 2.173155E-02 diff --git a/tests/regression_tests/mg_basic_delayed/test.py b/tests/regression_tests/mg_basic_delayed/test.py new file mode 100644 index 000000000..f0474a567 --- /dev/null +++ b/tests/regression_tests/mg_basic_delayed/test.py @@ -0,0 +1,131 @@ +import os + +import numpy as np + +import openmc +from openmc.examples import slab_mg + +from tests.testing_harness import PyAPITestHarness + + +def create_library(): + # Instantiate the energy group data and file object + groups = openmc.mgxs.EnergyGroups(group_edges=[0.0, 0.625, 20.0e6]) + n_dg = 2 + + mg_cross_sections_file = openmc.MGXSLibrary(groups) + mg_cross_sections_file.num_delayed_groups = n_dg + + beta = np.array([0.003, 0.003]) + one_m_beta = 1. - np.sum(beta) + nu = [2.50, 2.50] + fiss = np.array([0.002817, 0.097]) + capture = [0.008708, 0.02518] + absorption = np.add(capture, fiss) + scatter = np.array( + [[[0.31980, 0.06694], [0.004555, -0.0003972]], + [[0.00000, 0.00000], [0.424100, 0.05439000]]]) + total = [0.33588, 0.54628] + chi = [1., 0.] + + # Make the base data that uses chi & nu-fission vectors with a beta + mat_1 = openmc.XSdata('mat_1', groups) + mat_1.order = 1 + mat_1.num_delayed_groups = 2 + mat_1.set_beta(beta) + mat_1.set_nu_fission(np.multiply(nu, fiss)) + mat_1.set_absorption(absorption) + mat_1.set_scatter_matrix(scatter) + mat_1.set_total(total) + mat_1.set_chi(chi) + mg_cross_sections_file.add_xsdata(mat_1) + + # Make a version that uses prompt and delayed version of nufiss and chi + mat_2 = openmc.XSdata('mat_2', groups) + mat_2.order = 1 + mat_2.num_delayed_groups = 2 + mat_2.set_prompt_nu_fission(one_m_beta * np.multiply(nu, fiss)) + delay_nu_fiss = np.zeros((n_dg, groups.num_groups)) + for dg in range(n_dg): + for g in range(groups.num_groups): + delay_nu_fiss[dg, g] = beta[dg] * nu[g] * fiss[g] + mat_2.set_delayed_nu_fission(delay_nu_fiss) + mat_2.set_absorption(absorption) + mat_2.set_scatter_matrix(scatter) + mat_2.set_total(total) + mat_2.set_chi_prompt(chi) + mat_2.set_chi_delayed(np.stack([chi] * n_dg)) + mg_cross_sections_file.add_xsdata(mat_2) + + # Make a version that uses a nu-fission matrix with a beta + mat_3 = openmc.XSdata('mat_3', groups) + mat_3.order = 1 + mat_3.num_delayed_groups = 2 + mat_3.set_beta(beta) + mat_3.set_nu_fission(np.outer(np.multiply(nu, fiss), chi)) + mat_3.set_absorption(absorption) + mat_3.set_scatter_matrix(scatter) + mat_3.set_total(total) + mg_cross_sections_file.add_xsdata(mat_3) + + # Make a version that uses prompt and delayed version of the nufiss matrix + mat_4 = openmc.XSdata('mat_4', groups) + mat_4.order = 1 + mat_4.num_delayed_groups = 2 + mat_4.set_prompt_nu_fission(one_m_beta * + np.outer(np.multiply(nu, fiss), chi)) + delay_nu_fiss = np.zeros((n_dg, groups.num_groups, groups.num_groups)) + for dg in range(n_dg): + for g in range(groups.num_groups): + for go in range(groups.num_groups): + delay_nu_fiss[dg, g, go] = beta[dg] * nu[g] * fiss[g] * chi[go] + mat_4.set_delayed_nu_fission(delay_nu_fiss) + mat_4.set_absorption(absorption) + mat_4.set_scatter_matrix(scatter) + mat_4.set_total(total) + mg_cross_sections_file.add_xsdata(mat_4) + + # Make the base data that uses chi & nu-fiss vectors with a group-wise beta + mat_5 = openmc.XSdata('mat_5', groups) + mat_5.order = 1 + mat_5.num_delayed_groups = 2 + mat_5.set_beta(np.stack([beta] * groups.num_groups)) + mat_5.set_nu_fission(np.multiply(nu, fiss)) + mat_5.set_absorption(absorption) + mat_5.set_scatter_matrix(scatter) + mat_5.set_total(total) + mat_5.set_chi(chi) + mg_cross_sections_file.add_xsdata(mat_5) + + # Make a version that uses a nu-fission matrix with a group-wise beta + mat_6 = openmc.XSdata('mat_6', groups) + mat_6.order = 1 + mat_6.num_delayed_groups = 2 + mat_6.set_beta(np.stack([beta] * groups.num_groups)) + mat_6.set_nu_fission(np.outer(np.multiply(nu, fiss), chi)) + mat_6.set_absorption(absorption) + mat_6.set_scatter_matrix(scatter) + mat_6.set_total(total) + mg_cross_sections_file.add_xsdata(mat_6) + + # Write the file + mg_cross_sections_file.export_to_hdf5('2g.h5') + + +class MGXSTestHarness(PyAPITestHarness): + def _cleanup(self): + super()._cleanup() + f = '2g.h5' + if os.path.exists(f): + os.remove(f) + + +def test_mg_basic_delayed(): + create_library() + model = slab_mg(num_regions=6, mat_names=['vec beta', 'vec no beta', + 'matrix beta', 'matrix no beta', + 'vec group beta', + 'matrix group beta']) + + harness = PyAPITestHarness('statepoint.10.h5', model) + harness.main() diff --git a/tests/regression_tests/mg_convert/test.py b/tests/regression_tests/mg_convert/test.py index 1ace10c80..4cc6b656a 100755 --- a/tests/regression_tests/mg_convert/test.py +++ b/tests/regression_tests/mg_convert/test.py @@ -17,7 +17,7 @@ def build_mgxs_library(convert): # Instantiate the energy group data groups = openmc.mgxs.EnergyGroups(group_edges=[1e-5, 0.625, 20.0e6]) - # Instantiate the 7-group (C5G7) cross section data + # Instantiate the 2-group (C5G7) cross section data uo2_xsdata = openmc.XSdata('UO2', groups) uo2_xsdata.order = 2 uo2_xsdata.set_total([2., 2.]) diff --git a/tests/regression_tests/mg_legendre/inputs_true.dat b/tests/regression_tests/mg_legendre/inputs_true.dat index 754808095..ad3b434e6 100644 --- a/tests/regression_tests/mg_legendre/inputs_true.dat +++ b/tests/regression_tests/mg_legendre/inputs_true.dat @@ -1,44 +1,31 @@ - - - + - - - - - - - + - ../../1d_mgxs.h5 - + 2g.h5 + - - - - - - - - - + eigenvalue - 100 + 1000 10 5 - 0.0 0.0 0.0 10.0 10.0 5.0 + 0.0 -1000.0 -1000.0 929.45 1000.0 1000.0 + + false + multi-group false diff --git a/tests/regression_tests/mg_legendre/results_true.dat b/tests/regression_tests/mg_legendre/results_true.dat index d0f8c319e..4a9d98237 100644 --- a/tests/regression_tests/mg_legendre/results_true.dat +++ b/tests/regression_tests/mg_legendre/results_true.dat @@ -1,2 +1,2 @@ k-combined: -1.110122E+00 2.549637E-02 +9.934975E-01 2.679669E-02 diff --git a/tests/regression_tests/mg_legendre/test.py b/tests/regression_tests/mg_legendre/test.py index 5a57f758e..b5a05c706 100644 --- a/tests/regression_tests/mg_legendre/test.py +++ b/tests/regression_tests/mg_legendre/test.py @@ -1,10 +1,54 @@ +import os + +import numpy as np + +import openmc from openmc.examples import slab_mg from tests.testing_harness import PyAPITestHarness +def create_library(): + # Instantiate the energy group data and file object + groups = openmc.mgxs.EnergyGroups(group_edges=[0.0, 0.625, 20.0e6]) + + mg_cross_sections_file = openmc.MGXSLibrary(groups) + + # Make the base, isotropic data + nu = [2.50, 2.50] + fiss = np.array([0.002817, 0.097]) + capture = [0.008708, 0.02518] + absorption = np.add(capture, fiss) + scatter = np.array( + [[[0.31980, 0.06694], [0.004555, -0.0003972]], + [[0.00000, 0.00000], [0.424100, 0.05439000]]]) + total = [0.33588, 0.54628] + chi = [1., 0.] + + mat_1 = openmc.XSdata('mat_1', groups) + mat_1.order = 1 + mat_1.set_nu_fission(np.multiply(nu, fiss)) + mat_1.set_absorption(absorption) + mat_1.set_scatter_matrix(scatter) + mat_1.set_total(total) + mat_1.set_chi(chi) + mg_cross_sections_file.add_xsdata(mat_1) + + # Write the file + mg_cross_sections_file.export_to_hdf5('2g.h5') + + +class MGXSTestHarness(PyAPITestHarness): + def _cleanup(self): + super()._cleanup() + f = '2g.h5' + if os.path.exists(f): + os.remove(f) + + def test_mg_legendre(): - model = slab_mg(reps=['iso']) + create_library() + model = slab_mg() model.settings.tabular_legendre = {'enable': False} harness = PyAPITestHarness('statepoint.10.h5', model) diff --git a/tests/regression_tests/mg_max_order/inputs_true.dat b/tests/regression_tests/mg_max_order/inputs_true.dat index 023d468d4..2ac83852c 100644 --- a/tests/regression_tests/mg_max_order/inputs_true.dat +++ b/tests/regression_tests/mg_max_order/inputs_true.dat @@ -1,44 +1,34 @@ - - - + - - - - - - - + - ../../1d_mgxs.h5 - + 2g.h5 + - - - - - - - - - + eigenvalue - 100 + 1000 10 5 - 0.0 0.0 0.0 10.0 10.0 5.0 + 0.0 -1000.0 -1000.0 929.45 1000.0 1000.0 + + false + multi-group 1 + + false + diff --git a/tests/regression_tests/mg_max_order/results_true.dat b/tests/regression_tests/mg_max_order/results_true.dat index adfcd44a8..4a9d98237 100644 --- a/tests/regression_tests/mg_max_order/results_true.dat +++ b/tests/regression_tests/mg_max_order/results_true.dat @@ -1,2 +1,2 @@ k-combined: -1.074551E+00 1.871525E-02 +9.934975E-01 2.679669E-02 diff --git a/tests/regression_tests/mg_max_order/test.py b/tests/regression_tests/mg_max_order/test.py index 20cc4f805..97d3f57d7 100644 --- a/tests/regression_tests/mg_max_order/test.py +++ b/tests/regression_tests/mg_max_order/test.py @@ -1,10 +1,55 @@ +import os + +import numpy as np + +import openmc from openmc.examples import slab_mg from tests.testing_harness import PyAPITestHarness +def create_library(): + # Instantiate the energy group data and file object + groups = openmc.mgxs.EnergyGroups(group_edges=[0.0, 0.625, 20.0e6]) + + mg_cross_sections_file = openmc.MGXSLibrary(groups) + + # Make the base, isotropic data + nu = [2.50, 2.50] + fiss = np.array([0.002817, 0.097]) + capture = [0.008708, 0.02518] + absorption = np.add(capture, fiss) + scatter = np.array( + [[[0.31980, 0.06694, 0.003], [0.004555, -0.0003972, 0.00002]], + [[0.00000, 0.00000, 0.000], [0.424100, 0.05439000, 0.0025]]]) + total = [0.33588, 0.54628] + chi = [1., 0.] + + mat_1 = openmc.XSdata('mat_1', groups) + mat_1.order = 2 + mat_1.set_nu_fission(np.multiply(nu, fiss)) + mat_1.set_absorption(absorption) + mat_1.set_scatter_matrix(scatter) + mat_1.set_total(total) + mat_1.set_chi(chi) + mg_cross_sections_file.add_xsdata(mat_1) + + # Write the file + mg_cross_sections_file.export_to_hdf5('2g.h5') + + +class MGXSTestHarness(PyAPITestHarness): + def _cleanup(self): + super()._cleanup() + f = '2g.h5' + if os.path.exists(f): + os.remove(f) + + def test_mg_max_order(): - model = slab_mg(reps=['iso']) + create_library() + model = slab_mg() model.settings.max_order = 1 + harness = PyAPITestHarness('statepoint.10.h5', model) harness.main() diff --git a/tests/regression_tests/mg_nuclide/inputs_true.dat b/tests/regression_tests/mg_nuclide/inputs_true.dat deleted file mode 100644 index e11b9e3f0..000000000 --- a/tests/regression_tests/mg_nuclide/inputs_true.dat +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ../../1d_mgxs.h5 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - eigenvalue - 100 - 10 - 5 - - - 0.0 0.0 0.0 10.0 10.0 5.0 - - - multi-group - diff --git a/tests/regression_tests/mg_nuclide/results_true.dat b/tests/regression_tests/mg_nuclide/results_true.dat deleted file mode 100644 index ddb57d00b..000000000 --- a/tests/regression_tests/mg_nuclide/results_true.dat +++ /dev/null @@ -1,2 +0,0 @@ -k-combined: -1.073147E+00 1.602384E-02 diff --git a/tests/regression_tests/mg_nuclide/test.py b/tests/regression_tests/mg_nuclide/test.py deleted file mode 100644 index 44206ef28..000000000 --- a/tests/regression_tests/mg_nuclide/test.py +++ /dev/null @@ -1,9 +0,0 @@ -from openmc.examples import slab_mg - -from tests.testing_harness import PyAPITestHarness - - -def test_mg_nuclide(): - model = slab_mg(as_macro=False) - harness = PyAPITestHarness('statepoint.10.h5', model) - harness.main() diff --git a/tests/regression_tests/mg_survival_biasing/inputs_true.dat b/tests/regression_tests/mg_survival_biasing/inputs_true.dat index 4bc79d48e..5ece3ce9f 100644 --- a/tests/regression_tests/mg_survival_biasing/inputs_true.dat +++ b/tests/regression_tests/mg_survival_biasing/inputs_true.dat @@ -1,98 +1,34 @@ - - - - - - - - - - - - + - - - - - - - - - - - - - - - - + - ../../1d_mgxs.h5 - + 2g.h5 + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + eigenvalue - 100 + 1000 10 5 - 0.0 0.0 0.0 10.0 10.0 5.0 + 0.0 -1000.0 -1000.0 929.45 1000.0 1000.0 + + false + multi-group true + + false + diff --git a/tests/regression_tests/mg_survival_biasing/results_true.dat b/tests/regression_tests/mg_survival_biasing/results_true.dat index b20d63288..ebe98679f 100644 --- a/tests/regression_tests/mg_survival_biasing/results_true.dat +++ b/tests/regression_tests/mg_survival_biasing/results_true.dat @@ -1,2 +1,2 @@ k-combined: -1.080832E+00 1.336780E-02 +9.979905E-01 6.207495E-03 diff --git a/tests/regression_tests/mg_survival_biasing/test.py b/tests/regression_tests/mg_survival_biasing/test.py index 3c6c77a37..5d75611a9 100644 --- a/tests/regression_tests/mg_survival_biasing/test.py +++ b/tests/regression_tests/mg_survival_biasing/test.py @@ -1,10 +1,55 @@ +import os + +import numpy as np + +import openmc from openmc.examples import slab_mg from tests.testing_harness import PyAPITestHarness +def create_library(): + # Instantiate the energy group data and file object + groups = openmc.mgxs.EnergyGroups(group_edges=[0.0, 0.625, 20.0e6]) + + mg_cross_sections_file = openmc.MGXSLibrary(groups) + + # Make the base, isotropic data + nu = [2.50, 2.50] + fiss = np.array([0.002817, 0.097]) + capture = [0.008708, 0.02518] + absorption = np.add(capture, fiss) + scatter = np.array( + [[[0.31980, 0.06694], [0.004555, -0.0003972]], + [[0.00000, 0.00000], [0.424100, 0.05439000]]]) + total = [0.33588, 0.54628] + chi = [1., 0.] + + mat_1 = openmc.XSdata('mat_1', groups) + mat_1.order = 1 + mat_1.set_nu_fission(np.multiply(nu, fiss)) + mat_1.set_absorption(absorption) + mat_1.set_scatter_matrix(scatter) + mat_1.set_total(total) + mat_1.set_chi(chi) + mg_cross_sections_file.add_xsdata(mat_1) + + # Write the file + mg_cross_sections_file.export_to_hdf5('2g.h5') + + +class MGXSTestHarness(PyAPITestHarness): + def _cleanup(self): + super()._cleanup() + f = '2g.h5' + if os.path.exists(f): + os.remove(f) + + def test_mg_survival_biasing(): + create_library() model = slab_mg() model.settings.survival_biasing = True + harness = PyAPITestHarness('statepoint.10.h5', model) harness.main() diff --git a/tests/regression_tests/mg_tallies/inputs_true.dat b/tests/regression_tests/mg_tallies/inputs_true.dat index 7b5067014..a9b821c56 100644 --- a/tests/regression_tests/mg_tallies/inputs_true.dat +++ b/tests/regression_tests/mg_tallies/inputs_true.dat @@ -1,112 +1,48 @@ - - - - - - - - - - - - + - - - - - - - - - - - - - - - - + - ../../1d_mgxs.h5 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + 2g.h5 + + + eigenvalue - 100 + 1000 10 5 - 0.0 0.0 0.0 10.0 10.0 5.0 + 0.0 -1000.0 -1000.0 929.45 1000.0 1000.0 + + false + multi-group + + false + - 1 1 10 + 10 1 1 0.0 0.0 0.0 - 10 10 5 + 929.45 1000 1000 1 - 1 2 3 4 5 6 7 8 9 10 11 12 + 1 0.0 20000000.0 @@ -115,10 +51,10 @@ 0.0 20000000.0 - 1e-05 0.0635 10.0 100.0 1000.0 500000.0 1000000.0 20000000.0 + 0.0 0.625 20000000.0 - 1e-05 0.0635 10.0 100.0 1000.0 500000.0 1000000.0 20000000.0 + 0.0 0.625 20000000.0 5 @@ -170,60 +106,60 @@ 5 - uo2_ang uo2_ang_mu uo2_iso uo2_iso_mu clad_ang clad_ang_mu clad_iso clad_iso_mu lwtr_ang lwtr_ang_mu lwtr_iso lwtr_iso_mu + mat_1 total absorption fission nu-fission analog 5 - uo2_ang uo2_ang_mu uo2_iso uo2_iso_mu clad_ang clad_ang_mu clad_iso clad_iso_mu lwtr_ang lwtr_ang_mu lwtr_iso lwtr_iso_mu + mat_1 total absorption fission nu-fission tracklength 6 1 - uo2_ang uo2_ang_mu uo2_iso uo2_iso_mu clad_ang clad_ang_mu clad_iso clad_iso_mu lwtr_ang lwtr_ang_mu lwtr_iso lwtr_iso_mu + mat_1 total absorption fission nu-fission scatter nu-scatter analog 6 1 - uo2_ang uo2_ang_mu uo2_iso uo2_iso_mu clad_ang clad_ang_mu clad_iso clad_iso_mu lwtr_ang lwtr_ang_mu lwtr_iso lwtr_iso_mu + mat_1 total absorption fission nu-fission collision 6 1 - uo2_ang uo2_ang_mu uo2_iso uo2_iso_mu clad_ang clad_ang_mu clad_iso clad_iso_mu lwtr_ang lwtr_ang_mu lwtr_iso lwtr_iso_mu + mat_1 total absorption fission nu-fission tracklength 6 1 2 - uo2_ang uo2_ang_mu uo2_iso uo2_iso_mu clad_ang clad_ang_mu clad_iso clad_iso_mu lwtr_ang lwtr_ang_mu lwtr_iso lwtr_iso_mu + mat_1 scatter nu-scatter nu-fission 6 3 - uo2_ang uo2_ang_mu uo2_iso uo2_iso_mu clad_ang clad_ang_mu clad_iso clad_iso_mu lwtr_ang lwtr_ang_mu lwtr_iso lwtr_iso_mu + mat_1 total absorption fission nu-fission scatter nu-scatter analog 6 3 - uo2_ang uo2_ang_mu uo2_iso uo2_iso_mu clad_ang clad_ang_mu clad_iso clad_iso_mu lwtr_ang lwtr_ang_mu lwtr_iso lwtr_iso_mu + mat_1 total absorption fission nu-fission collision 6 3 - uo2_ang uo2_ang_mu uo2_iso uo2_iso_mu clad_ang clad_ang_mu clad_iso clad_iso_mu lwtr_ang lwtr_ang_mu lwtr_iso lwtr_iso_mu + mat_1 total absorption fission nu-fission tracklength 6 3 4 - uo2_ang uo2_ang_mu uo2_iso uo2_iso_mu clad_ang clad_ang_mu clad_iso clad_iso_mu lwtr_ang lwtr_ang_mu lwtr_iso lwtr_iso_mu + mat_1 scatter nu-scatter nu-fission diff --git a/tests/regression_tests/mg_tallies/results_true.dat b/tests/regression_tests/mg_tallies/results_true.dat index 87470bdf4..78a5883fe 100644 --- a/tests/regression_tests/mg_tallies/results_true.dat +++ b/tests/regression_tests/mg_tallies/results_true.dat @@ -1 +1 @@ -9183f8b191f2e62334f992acd865d29e3f4e3f871a6df498e280fc4e2d91f2d2d20c732fbd75fa88e2e8c576f86e744f7655af6bb9da66e9b28b1009c8742899 \ No newline at end of file +41ea1f6b17c58a8141921af2f1d044eda93f3a9bca9463ee023af2e9865da613ace90fc8a25b42edde128ed827182ea9df0fe09d9b7887282d0ec092692cf717 \ No newline at end of file diff --git a/tests/regression_tests/mg_tallies/test.py b/tests/regression_tests/mg_tallies/test.py index 8952cc4ad..26d53c230 100644 --- a/tests/regression_tests/mg_tallies/test.py +++ b/tests/regression_tests/mg_tallies/test.py @@ -1,23 +1,66 @@ +import os + +import numpy as np + import openmc from openmc.examples import slab_mg from tests.testing_harness import HashedPyAPITestHarness +def create_library(): + # Instantiate the energy group data and file object + groups = openmc.mgxs.EnergyGroups(group_edges=[0.0, 0.625, 20.0e6]) + + mg_cross_sections_file = openmc.MGXSLibrary(groups) + + # Make the base, isotropic data + nu = [2.50, 2.50] + fiss = np.array([0.002817, 0.097]) + capture = [0.008708, 0.02518] + absorption = np.add(capture, fiss) + scatter = np.array( + [[[0.31980, 0.06694], [0.004555, -0.0003972]], + [[0.00000, 0.00000], [0.424100, 0.05439000]]]) + total = [0.33588, 0.54628] + chi = [1., 0.] + + mat_1 = openmc.XSdata('mat_1', groups) + mat_1.order = 1 + mat_1.set_nu_fission(np.multiply(nu, fiss)) + mat_1.set_absorption(absorption) + mat_1.set_scatter_matrix(scatter) + mat_1.set_total(total) + mat_1.set_chi(chi) + mg_cross_sections_file.add_xsdata(mat_1) + + # Write the file + mg_cross_sections_file.export_to_hdf5('2g.h5') + + +class MGXSTestHarness(HashedPyAPITestHarness): + def _cleanup(self): + super()._cleanup() + f = '2g.h5' + if os.path.exists(f): + os.remove(f) + + def test_mg_tallies(): - model = slab_mg(as_macro=False) + create_library() + model = slab_mg() # Instantiate a tally mesh mesh = openmc.Mesh(mesh_id=1) mesh.type = 'regular' - mesh.dimension = [1, 1, 10] + mesh.dimension = [10, 1, 1] mesh.lower_left = [0.0, 0.0, 0.0] - mesh.upper_right = [10, 10, 5] + mesh.upper_right = [929.45, 1000, 1000] # Instantiate some tally filters energy_filter = openmc.EnergyFilter([0.0, 20.0e6]) energyout_filter = openmc.EnergyoutFilter([0.0, 20.0e6]) - energies = [1e-5, 0.0635, 10.0, 1.0e2, 1.0e3, 0.5e6, 1.0e6, 20.0e6] + energies = [0.0, 0.625, 20.0e6] matching_energy_filter = openmc.EnergyFilter(energies) matching_eout_filter = openmc.EnergyoutFilter(energies) mesh_filter = openmc.MeshFilter(mesh) diff --git a/tools/ci/travis-install.sh b/tools/ci/travis-install.sh index 5ad238169..88c59d051 100755 --- a/tools/ci/travis-install.sh +++ b/tools/ci/travis-install.sh @@ -22,4 +22,4 @@ python tools/ci/travis-install.py pip install -e .[test] # For uploading to coveralls -pip install python-coveralls +pip install coveralls