Replace xtensor with internal Tensor/View classes (#3805)

Co-authored-by: John Tramm <jtramm@gmail.com>
This commit is contained in:
John Tramm 2026-02-17 09:50:38 -06:00 committed by GitHub
parent c6ef84d1d5
commit 977ade79a1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
73 changed files with 3111 additions and 908 deletions

6
.gitmodules vendored
View file

@ -1,12 +1,6 @@
[submodule "vendor/pugixml"] [submodule "vendor/pugixml"]
path = vendor/pugixml path = vendor/pugixml
url = https://github.com/zeux/pugixml.git url = https://github.com/zeux/pugixml.git
[submodule "vendor/xtensor"]
path = vendor/xtensor
url = https://github.com/xtensor-stack/xtensor.git
[submodule "vendor/xtl"]
path = vendor/xtl
url = https://github.com/xtensor-stack/xtl.git
[submodule "vendor/fmt"] [submodule "vendor/fmt"]
path = vendor/fmt path = vendor/fmt
url = https://github.com/fmtlib/fmt.git url = https://github.com/fmtlib/fmt.git

View file

@ -266,23 +266,6 @@ else()
endif() endif()
endif() endif()
#===============================================================================
# xtensor header-only library
#===============================================================================
if(OPENMC_FORCE_VENDORED_LIBS)
add_subdirectory(vendor/xtl)
set(xtl_DIR ${CMAKE_CURRENT_BINARY_DIR}/vendor/xtl)
add_subdirectory(vendor/xtensor)
else()
find_package_write_status(xtensor)
if (NOT xtensor_FOUND)
add_subdirectory(vendor/xtl)
set(xtl_DIR ${CMAKE_CURRENT_BINARY_DIR}/vendor/xtl)
add_subdirectory(vendor/xtensor)
endif()
endif()
#=============================================================================== #===============================================================================
# Catch2 library # Catch2 library
#=============================================================================== #===============================================================================
@ -498,7 +481,7 @@ endif()
# target_link_libraries treats any arguments starting with - but not -l as # target_link_libraries treats any arguments starting with - but not -l as
# linker flags. Thus, we can pass both linker flags and libraries together. # linker flags. Thus, we can pass both linker flags and libraries together.
target_link_libraries(libopenmc ${ldflags} ${HDF5_LIBRARIES} ${HDF5_HL_LIBRARIES} target_link_libraries(libopenmc ${ldflags} ${HDF5_LIBRARIES} ${HDF5_HL_LIBRARIES}
xtensor fmt::fmt ${CMAKE_DL_LIBS}) fmt::fmt ${CMAKE_DL_LIBS})
if(TARGET pugixml::pugixml) if(TARGET pugixml::pugixml)
target_link_libraries(libopenmc pugixml::pugixml) target_link_libraries(libopenmc pugixml::pugixml)

View file

@ -5,8 +5,6 @@ get_filename_component(_OPENMC_PREFIX "${OpenMC_CMAKE_DIR}/../../.." ABSOLUTE)
find_package(fmt CONFIG REQUIRED HINTS ${_OPENMC_PREFIX}) find_package(fmt CONFIG REQUIRED HINTS ${_OPENMC_PREFIX})
find_package(pugixml CONFIG REQUIRED HINTS ${_OPENMC_PREFIX}) find_package(pugixml CONFIG REQUIRED HINTS ${_OPENMC_PREFIX})
find_package(xtl CONFIG REQUIRED HINTS ${_OPENMC_PREFIX})
find_package(xtensor CONFIG REQUIRED HINTS ${_OPENMC_PREFIX})
if(@OPENMC_USE_DAGMC@) if(@OPENMC_USE_DAGMC@)
find_package(DAGMC REQUIRED HINTS @DAGMC_DIR@) find_package(DAGMC REQUIRED HINTS @DAGMC_DIR@)
endif() endif()

View file

@ -119,7 +119,7 @@ packages should be installed, for example in Homebrew via:
.. code-block:: sh .. code-block:: sh
brew install llvm cmake xtensor hdf5 python libomp libpng brew install llvm cmake hdf5 python libomp libpng
The compiler provided by the above LLVM package should be used in place of the The compiler provided by the above LLVM package should be used in place of the
one provisioned by XCode, which does not support the multithreading library used one provisioned by XCode, which does not support the multithreading library used

View file

@ -3,7 +3,7 @@
#include "openmc/particle.h" #include "openmc/particle.h"
#include "xtensor/xtensor.hpp" #include "openmc/tensor.h"
namespace openmc { namespace openmc {
@ -14,9 +14,9 @@ namespace openmc {
class BremsstrahlungData { class BremsstrahlungData {
public: public:
// Data // Data
xt::xtensor<double, 2> pdf; //!< Bremsstrahlung energy PDF tensor::Tensor<double> pdf; //!< Bremsstrahlung energy PDF
xt::xtensor<double, 2> cdf; //!< Bremsstrahlung energy CDF tensor::Tensor<double> cdf; //!< Bremsstrahlung energy CDF
xt::xtensor<double, 1> yield; //!< Photon yield tensor::Tensor<double> yield; //!< Photon yield
}; };
class Bremsstrahlung { class Bremsstrahlung {
@ -32,9 +32,9 @@ public:
namespace data { namespace data {
extern xt::xtensor<double, 1> extern tensor::Tensor<double>
ttb_e_grid; //! energy T of incident electron in [eV] ttb_e_grid; //! energy T of incident electron in [eV]
extern xt::xtensor<double, 1> extern tensor::Tensor<double>
ttb_k_grid; //! reduced energy W/T of emitted photon ttb_k_grid; //! reduced energy W/T of emitted photon
} // namespace data } // namespace data

View file

@ -5,7 +5,7 @@
#define OPENMC_DISTRIBUTION_ENERGY_H #define OPENMC_DISTRIBUTION_ENERGY_H
#include "hdf5.h" #include "hdf5.h"
#include "xtensor/xtensor.hpp" #include "openmc/tensor.h"
#include "openmc/constants.h" #include "openmc/constants.h"
#include "openmc/endf.h" #include "openmc/endf.h"
@ -86,9 +86,9 @@ private:
struct CTTable { struct CTTable {
Interpolation interpolation; //!< Interpolation law Interpolation interpolation; //!< Interpolation law
int n_discrete; //!< Number of of discrete energies int n_discrete; //!< Number of of discrete energies
xt::xtensor<double, 1> e_out; //!< Outgoing energies in [eV] tensor::Tensor<double> e_out; //!< Outgoing energies in [eV]
xt::xtensor<double, 1> p; //!< Probability density tensor::Tensor<double> p; //!< Probability density
xt::xtensor<double, 1> c; //!< Cumulative distribution tensor::Tensor<double> c; //!< Cumulative distribution
}; };
int n_region_; //!< Number of inteprolation regions int n_region_; //!< Number of inteprolation regions

View file

@ -6,7 +6,7 @@
#include <cstdint> // for int64_t #include <cstdint> // for int64_t
#include "xtensor/xtensor.hpp" #include "openmc/tensor.h"
#include <hdf5.h> #include <hdf5.h>
#include "openmc/array.h" #include "openmc/array.h"
@ -24,7 +24,7 @@ namespace simulation {
extern double keff_generation; //!< Single-generation k on each processor extern double keff_generation; //!< Single-generation k on each processor
extern array<double, 2> k_sum; //!< Used to reduce sum and sum_sq extern array<double, 2> k_sum; //!< Used to reduce sum and sum_sq
extern vector<double> entropy; //!< Shannon entropy at each generation extern vector<double> entropy; //!< Shannon entropy at each generation
extern xt::xtensor<double, 1> source_frac; //!< Source fraction for UFS extern tensor::Tensor<double> source_frac; //!< Source fraction for UFS
} // namespace simulation } // namespace simulation

View file

@ -11,8 +11,7 @@
#include "hdf5.h" #include "hdf5.h"
#include "hdf5_hl.h" #include "hdf5_hl.h"
#include "xtensor/xadapt.hpp" #include "openmc/tensor.h"
#include "xtensor/xarray.hpp"
#include "openmc/array.h" #include "openmc/array.h"
#include "openmc/error.h" #include "openmc/error.h"
@ -166,24 +165,19 @@ void read_attribute(hid_t obj_id, const char* name, vector<T>& vec)
read_attr(obj_id, name, H5TypeMap<T>::type_id, vec.data()); read_attr(obj_id, name, H5TypeMap<T>::type_id, vec.data());
} }
// Generic array version // Tensor version
template<typename T> template<typename T>
void read_attribute(hid_t obj_id, const char* name, xt::xarray<T>& arr) void read_attribute(hid_t obj_id, const char* name, tensor::Tensor<T>& tensor)
{ {
// Get shape of attribute array // Get shape of attribute
auto shape = attribute_shape(obj_id, name); auto shape = attribute_shape(obj_id, name);
// Allocate new array to read data into // Resize tensor and read data directly
std::size_t size = 1; vector<size_t> tshape(shape.begin(), shape.end());
for (const auto x : shape) tensor.resize(tshape);
size *= x;
vector<T> buffer(size);
// Read data from attribute // Read data from attribute
read_attr(obj_id, name, H5TypeMap<T>::type_id, buffer.data()); read_attr(obj_id, name, H5TypeMap<T>::type_id, tensor.data());
// Adapt array into xarray
arr = xt::adapt(buffer, shape);
} }
// overload for std::string // overload for std::string
@ -290,63 +284,34 @@ void read_dataset(
} }
template<typename T> template<typename T>
void read_dataset(hid_t dset, xt::xarray<T>& arr, bool indep = false) void read_dataset(hid_t dset, tensor::Tensor<T>& tensor, bool indep = false)
{ {
// Get shape of dataset // Get shape of dataset
vector<hsize_t> shape = object_shape(dset); vector<hsize_t> shape = object_shape(dset);
// Allocate space in the array to read data into // Resize tensor and read data directly
std::size_t size = 1; vector<size_t> tshape(shape.begin(), shape.end());
for (const auto x : shape) tensor.resize(tshape);
size *= x;
arr.resize(shape);
// Read data from attribute // Read data from dataset
read_dataset_lowlevel( read_dataset_lowlevel(
dset, nullptr, H5TypeMap<T>::type_id, H5S_ALL, indep, arr.data()); dset, nullptr, H5TypeMap<T>::type_id, H5S_ALL, indep, tensor.data());
} }
template<> template<>
void read_dataset( void read_dataset(
hid_t dset, xt::xarray<std::complex<double>>& arr, bool indep); hid_t dset, tensor::Tensor<std::complex<double>>& tensor, bool indep);
template<typename T> template<typename T>
void read_dataset( void read_dataset(
hid_t obj_id, const char* name, xt::xarray<T>& arr, bool indep = false) hid_t obj_id, const char* name, tensor::Tensor<T>& tensor, bool indep = false)
{ {
// Open dataset and read array // Open dataset and read tensor
hid_t dset = open_dataset(obj_id, name); hid_t dset = open_dataset(obj_id, name);
read_dataset(dset, arr, indep); read_dataset(dset, tensor, indep);
close_dataset(dset); close_dataset(dset);
} }
template<typename T, std::size_t N>
void read_dataset(
hid_t obj_id, const char* name, xt::xtensor<T, N>& arr, bool indep = false)
{
// Open dataset and read array
hid_t dset = open_dataset(obj_id, name);
// Get shape of dataset
vector<hsize_t> hsize_t_shape = object_shape(dset);
close_dataset(dset);
// cast from hsize_t to size_t
vector<size_t> shape(hsize_t_shape.size());
for (int i = 0; i < shape.size(); i++) {
shape[i] = static_cast<size_t>(hsize_t_shape[i]);
}
// Allocate new xarray to read data into
xt::xarray<T> xarr(shape);
// Read data from the dataset
read_dataset(obj_id, name, xarr);
// Copy into xtensor
arr = xarr;
}
// overload for Position // overload for Position
inline void read_dataset( inline void read_dataset(
hid_t obj_id, const char* name, Position& r, bool indep = false) hid_t obj_id, const char* name, Position& r, bool indep = false)
@ -358,31 +323,22 @@ inline void read_dataset(
r.z = x[2]; r.z = x[2];
} }
template<typename T, std::size_t N> template<typename T>
inline void read_dataset_as_shape( inline void read_dataset_as_shape(
hid_t obj_id, const char* name, xt::xtensor<T, N>& arr, bool indep = false) hid_t obj_id, const char* name, tensor::Tensor<T>& tensor, bool indep = false)
{ {
hid_t dset = open_dataset(obj_id, name); hid_t dset = open_dataset(obj_id, name);
// Allocate new array to read data into // Read data directly into pre-shaped tensor
std::size_t size = 1;
for (const auto x : arr.shape())
size *= x;
vector<T> buffer(size);
// Read data from attribute
read_dataset_lowlevel( read_dataset_lowlevel(
dset, nullptr, H5TypeMap<T>::type_id, H5S_ALL, indep, buffer.data()); dset, nullptr, H5TypeMap<T>::type_id, H5S_ALL, indep, tensor.data());
// Adapt into xarray
arr = xt::adapt(buffer, arr.shape());
close_dataset(dset); close_dataset(dset);
} }
template<typename T, std::size_t N> template<typename T>
inline void read_nd_vector(hid_t obj_id, const char* name, inline void read_nd_tensor(hid_t obj_id, const char* name,
xt::xtensor<T, N>& result, bool must_have = false) tensor::Tensor<T>& result, bool must_have = false)
{ {
if (object_exists(obj_id, name)) { if (object_exists(obj_id, name)) {
read_dataset_as_shape(obj_id, name, result, true); read_dataset_as_shape(obj_id, name, result, true);
@ -496,12 +452,16 @@ inline void write_dataset(
false, buffer.data()); false, buffer.data());
} }
// Template for xarray, xtensor, etc. // Template for Tensor and StaticTensor2D. A SFINAE guard is used here to
template<typename D> // prevent this template from matching vector/string types that have their own
inline void write_dataset( // overloads above. A generic Container parameter avoids duplicating the body
hid_t obj_id, const char* name, const xt::xcontainer<D>& arr) // for both Tensor<T> and StaticTensor2D<T,R,C>.
template<typename Container,
typename =
std::enable_if_t<tensor::is_tensor<std::decay_t<Container>>::value>>
inline void write_dataset(hid_t obj_id, const char* name, const Container& arr)
{ {
using T = typename D::value_type; using T = typename std::decay_t<Container>::value_type;
auto s = arr.shape(); auto s = arr.shape();
vector<hsize_t> dims {s.cbegin(), s.cend()}; vector<hsize_t> dims {s.cbegin(), s.cend()};
write_dataset_lowlevel(obj_id, dims.size(), dims.data(), name, write_dataset_lowlevel(obj_id, dims.size(), dims.data(), name,

View file

@ -5,8 +5,8 @@
#include <unordered_map> #include <unordered_map>
#include "openmc/span.h" #include "openmc/span.h"
#include "openmc/tensor.h"
#include "pugixml.hpp" #include "pugixml.hpp"
#include "xtensor/xtensor.hpp"
#include <hdf5.h> #include <hdf5.h>
#include "openmc/bremsstrahlung.h" #include "openmc/bremsstrahlung.h"
@ -189,7 +189,7 @@ public:
vector<int> nuclide_; //!< Indices in nuclides vector vector<int> nuclide_; //!< Indices in nuclides vector
vector<int> element_; //!< Indices in elements vector vector<int> element_; //!< Indices in elements vector
NCrystalMat ncrystal_mat_; //!< NCrystal material object NCrystalMat ncrystal_mat_; //!< NCrystal material object
xt::xtensor<double, 1> atom_density_; //!< Nuclide atom density in [atom/b-cm] tensor::Tensor<double> atom_density_; //!< Nuclide atom density in [atom/b-cm]
double density_; //!< Total atom density in [atom/b-cm] double density_; //!< Total atom density in [atom/b-cm]
double density_gpcc_; //!< Total atom density in [g/cm^3] double density_gpcc_; //!< Total atom density in [g/cm^3]
double charge_density_; //!< Total charge density in [e/b-cm] double charge_density_; //!< Total charge density in [e/b-cm]

View file

@ -8,8 +8,8 @@
#include <unordered_map> #include <unordered_map>
#include "hdf5.h" #include "hdf5.h"
#include "openmc/tensor.h"
#include "pugixml.hpp" #include "pugixml.hpp"
#include "xtensor/xtensor.hpp"
#include "openmc/bounding_box.h" #include "openmc/bounding_box.h"
#include "openmc/error.h" #include "openmc/error.h"
@ -284,8 +284,8 @@ public:
virtual Position upper_right() const = 0; virtual Position upper_right() const = 0;
// Data members // Data members
xt::xtensor<double, 1> lower_left_; //!< Lower-left coordinates of mesh tensor::Tensor<double> lower_left_; //!< Lower-left coordinates of mesh
xt::xtensor<double, 1> upper_right_; //!< Upper-right coordinates of mesh tensor::Tensor<double> upper_right_; //!< Upper-right coordinates of mesh
int id_ {-1}; //!< Mesh ID int id_ {-1}; //!< Mesh ID
std::string name_; //!< User-specified name std::string name_; //!< User-specified name
int n_dimension_ {-1}; //!< Number of dimensions int n_dimension_ {-1}; //!< Number of dimensions
@ -348,7 +348,7 @@ public:
//! \param[in] Pointer to bank sites //! \param[in] Pointer to bank sites
//! \param[in] Number of bank sites //! \param[in] Number of bank sites
//! \param[out] Whether any bank sites are outside the mesh //! \param[out] Whether any bank sites are outside the mesh
xt::xtensor<double, 1> count_sites( tensor::Tensor<double> count_sites(
const SourceSite* bank, int64_t length, bool* outside) const; const SourceSite* bank, int64_t length, bool* outside) const;
//! Get bin given mesh indices //! Get bin given mesh indices
@ -419,8 +419,8 @@ public:
//! Get a label for the mesh bin //! Get a label for the mesh bin
std::string bin_label(int bin) const override; std::string bin_label(int bin) const override;
//! Get shape as xt::xtensor //! Get mesh dimensions as a tensor
xt::xtensor<int, 1> get_x_shape() const; tensor::Tensor<int> get_shape_tensor() const;
double volume(int bin) const override double volume(int bin) const override
{ {
@ -515,7 +515,7 @@ public:
//! \param[in] bank Array of bank sites //! \param[in] bank Array of bank sites
//! \param[out] Whether any bank sites are outside the mesh //! \param[out] Whether any bank sites are outside the mesh
//! \return Array indicating number of sites in each mesh/energy bin //! \return Array indicating number of sites in each mesh/energy bin
xt::xtensor<double, 1> count_sites( tensor::Tensor<double> count_sites(
const SourceSite* bank, int64_t length, bool* outside) const; const SourceSite* bank, int64_t length, bool* outside) const;
//! Return the volume for a given mesh index //! Return the volume for a given mesh index
@ -526,7 +526,7 @@ public:
// Data members // Data members
double volume_frac_; //!< Volume fraction of each mesh element double volume_frac_; //!< Volume fraction of each mesh element
double element_volume_; //!< Volume of each mesh element double element_volume_; //!< Volume of each mesh element
xt::xtensor<double, 1> width_; //!< Width of each mesh element tensor::Tensor<double> width_; //!< Width of each mesh element
}; };
class RectilinearMesh : public StructuredMesh { class RectilinearMesh : public StructuredMesh {

View file

@ -6,7 +6,7 @@
#include <string> #include <string>
#include "xtensor/xtensor.hpp" #include "openmc/tensor.h"
#include "openmc/constants.h" #include "openmc/constants.h"
#include "openmc/hdf5_interface.h" #include "openmc/hdf5_interface.h"
@ -22,7 +22,7 @@ namespace openmc {
class Mgxs { class Mgxs {
private: private:
xt::xtensor<double, 1> kTs; // temperature in eV (k * T) tensor::Tensor<double> kTs; // temperature in eV (k * T)
AngleDistributionType AngleDistributionType
scatter_format; // flag for if this is legendre, histogram, or tabular scatter_format; // flag for if this is legendre, histogram, or tabular
int num_groups; // number of energy groups int num_groups; // number of energy groups

View file

@ -96,7 +96,7 @@ public:
// Temperature dependent cross section data // Temperature dependent cross section data
vector<double> kTs_; //!< temperatures in eV (k*T) vector<double> kTs_; //!< temperatures in eV (k*T)
vector<EnergyGrid> grid_; //!< Energy grid at each temperature vector<EnergyGrid> grid_; //!< Energy grid at each temperature
vector<xt::xtensor<double, 2>> xs_; //!< Cross sections at each temperature vector<tensor::Tensor<double>> xs_; //!< Cross sections at each temperature
// Multipole data // Multipole data
unique_ptr<WindowedMultipole> multipole_; unique_ptr<WindowedMultipole> multipole_;

View file

@ -6,7 +6,7 @@
#include "openmc/particle.h" #include "openmc/particle.h"
#include "openmc/vector.h" #include "openmc/vector.h"
#include "xtensor/xtensor.hpp" #include "openmc/tensor.h"
#include <hdf5.h> #include <hdf5.h>
#include <string> #include <string>
@ -62,14 +62,14 @@ public:
int64_t index_; //!< Index in global elements vector int64_t index_; //!< Index in global elements vector
// Microscopic cross sections // Microscopic cross sections
xt::xtensor<double, 1> energy_; tensor::Tensor<double> energy_;
xt::xtensor<double, 1> coherent_; tensor::Tensor<double> coherent_;
xt::xtensor<double, 1> incoherent_; tensor::Tensor<double> incoherent_;
xt::xtensor<double, 1> photoelectric_total_; tensor::Tensor<double> photoelectric_total_;
xt::xtensor<double, 1> pair_production_total_; tensor::Tensor<double> pair_production_total_;
xt::xtensor<double, 1> pair_production_electron_; tensor::Tensor<double> pair_production_electron_;
xt::xtensor<double, 1> pair_production_nuclear_; tensor::Tensor<double> pair_production_nuclear_;
xt::xtensor<double, 1> heating_; tensor::Tensor<double> heating_;
// Form factors // Form factors
Tabulated1D incoherent_form_factor_; Tabulated1D incoherent_form_factor_;
@ -81,27 +81,27 @@ public:
// stored separately to improve memory access pattern when calculating the // stored separately to improve memory access pattern when calculating the
// total cross section // total cross section
vector<ElectronSubshell> shells_; vector<ElectronSubshell> shells_;
xt::xtensor<double, 2> cross_sections_; tensor::Tensor<double> cross_sections_;
// Compton profile data // Compton profile data
xt::xtensor<double, 2> profile_pdf_; tensor::Tensor<double> profile_pdf_;
xt::xtensor<double, 2> profile_cdf_; tensor::Tensor<double> profile_cdf_;
xt::xtensor<double, 1> binding_energy_; tensor::Tensor<double> binding_energy_;
xt::xtensor<double, 1> electron_pdf_; tensor::Tensor<double> electron_pdf_;
// Map subshells from Compton profile data obtained from Biggs et al, // Map subshells from Compton profile data obtained from Biggs et al,
// "Hartree-Fock Compton profiles for the elements" to ENDF/B atomic // "Hartree-Fock Compton profiles for the elements" to ENDF/B atomic
// relaxation data // relaxation data
xt::xtensor<int, 1> subshell_map_; tensor::Tensor<int> subshell_map_;
// Stopping power data // Stopping power data
double I_; // mean excitation energy double I_; // mean excitation energy
xt::xtensor<int, 1> n_electrons_; tensor::Tensor<int> n_electrons_;
xt::xtensor<double, 1> ionization_energy_; tensor::Tensor<double> ionization_energy_;
xt::xtensor<double, 1> stopping_power_radiative_; tensor::Tensor<double> stopping_power_radiative_;
// Bremsstrahlung scaled DCS // Bremsstrahlung scaled DCS
xt::xtensor<double, 2> dcs_; tensor::Tensor<double> dcs_;
// Whether atomic relaxation data is present // Whether atomic relaxation data is present
bool has_atomic_relaxation_ {false}; bool has_atomic_relaxation_ {false};
@ -137,7 +137,7 @@ void free_memory_photon();
namespace data { namespace data {
extern xt::xtensor<double, 1> extern tensor::Tensor<double>
compton_profile_pz; //! Compton profile momentum grid compton_profile_pz; //! Compton profile momentum grid
//! Photon interaction data for each element //! Photon interaction data for each element

View file

@ -6,8 +6,8 @@
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
#include "openmc/tensor.h"
#include "pugixml.hpp" #include "pugixml.hpp"
#include "xtensor/xarray.hpp"
#include "hdf5.h" #include "hdf5.h"
#include "openmc/cell.h" #include "openmc/cell.h"
@ -90,7 +90,7 @@ const RGBColor BLACK {0, 0, 0};
* visualized. * visualized.
*/ */
typedef xt::xtensor<RGBColor, 2> ImageData; typedef tensor::Tensor<RGBColor> ImageData;
class PlottableInterface { class PlottableInterface {
public: public:
PlottableInterface() = default; PlottableInterface() = default;
@ -154,7 +154,7 @@ struct IdData {
void set_overlap(size_t y, size_t x); void set_overlap(size_t y, size_t x);
// Members // Members
xt::xtensor<int32_t, 3> data_; //!< 2D array of cell & material ids tensor::Tensor<int32_t> data_; //!< 2D array of cell & material ids
}; };
struct PropertyData { struct PropertyData {
@ -166,7 +166,7 @@ struct PropertyData {
void set_overlap(size_t y, size_t x); void set_overlap(size_t y, size_t x);
// Members // Members
xt::xtensor<double, 3> data_; //!< 2D array of temperature & density data tensor::Tensor<double> data_; //!< 2D array of temperature & density data
}; };
//=============================================================================== //===============================================================================

View file

@ -178,10 +178,10 @@ protected:
// Volumes for each tally and bin/score combination. This intermediate data // Volumes for each tally and bin/score combination. This intermediate data
// structure is used when tallying quantities that must be normalized by // structure is used when tallying quantities that must be normalized by
// volume (i.e., flux). The vector is index by tally index, while the inner 2D // volume (i.e., flux). The vector is index by tally index, while the inner 2D
// xtensor is indexed by bin index and score index in a similar manner to the // tensor is indexed by bin index and score index in a similar manner to the
// results tensor in the Tally class, though without the third dimension, as // results tensor in the Tally class, though without the third dimension, as
// SUM and SUM_SQ do not need to be tracked. // SUM and SUM_SQ do not need to be tracked.
vector<xt::xtensor<double, 2>> tally_volumes_; vector<tensor::Tensor<double>> tally_volumes_;
}; // class FlatSourceDomain }; // class FlatSourceDomain

View file

@ -4,7 +4,7 @@
#ifndef OPENMC_SCATTDATA_H #ifndef OPENMC_SCATTDATA_H
#define OPENMC_SCATTDATA_H #define OPENMC_SCATTDATA_H
#include "xtensor/xtensor.hpp" #include "openmc/tensor.h"
#include "openmc/constants.h" #include "openmc/constants.h"
#include "openmc/vector.h" #include "openmc/vector.h"
@ -26,23 +26,23 @@ public:
protected: protected:
//! \brief Initializes the attributes of the base class. //! \brief Initializes the attributes of the base class.
void base_init(int order, const xt::xtensor<int, 1>& in_gmin, void base_init(int order, const tensor::Tensor<int>& in_gmin,
const xt::xtensor<int, 1>& in_gmax, const double_2dvec& in_energy, const tensor::Tensor<int>& in_gmax, const double_2dvec& in_energy,
const double_2dvec& in_mult); const double_2dvec& in_mult);
//! \brief Combines microscopic ScattDatas into a macroscopic one. //! \brief Combines microscopic ScattDatas into a macroscopic one.
void base_combine(size_t max_order, size_t order_dim, void base_combine(size_t max_order, size_t order_dim,
const vector<ScattData*>& those_scatts, const vector<double>& scalars, const vector<ScattData*>& those_scatts, const vector<double>& scalars,
xt::xtensor<int, 1>& in_gmin, xt::xtensor<int, 1>& in_gmax, tensor::Tensor<int>& in_gmin, tensor::Tensor<int>& in_gmax,
double_2dvec& sparse_mult, double_3dvec& sparse_scatter); double_2dvec& sparse_mult, double_3dvec& sparse_scatter);
public: public:
double_2dvec energy; // Normalized p0 matrix for sampling Eout double_2dvec energy; // Normalized p0 matrix for sampling Eout
double_2dvec mult; // nu-scatter multiplication (nu-scatt/scatt) double_2dvec mult; // nu-scatter multiplication (nu-scatt/scatt)
double_3dvec dist; // Angular distribution double_3dvec dist; // Angular distribution
xt::xtensor<int, 1> gmin; // minimum outgoing group tensor::Tensor<int> gmin; // minimum outgoing group
xt::xtensor<int, 1> gmax; // maximum outgoing group tensor::Tensor<int> gmax; // maximum outgoing group
xt::xtensor<double, 1> scattxs; // Isotropic Sigma_{s,g_{in}} tensor::Tensor<double> scattxs; // Isotropic Sigma_{s,g_{in}}
//! \brief Calculates the value of normalized f(mu). //! \brief Calculates the value of normalized f(mu).
//! //!
@ -72,8 +72,8 @@ public:
//! @param in_gmax List of maximum outgoing groups for every incoming group //! @param in_gmax List of maximum outgoing groups for every incoming group
//! @param in_mult Input sparse multiplicity matrix //! @param in_mult Input sparse multiplicity matrix
//! @param coeffs Input sparse scattering matrix //! @param coeffs Input sparse scattering matrix
virtual void init(const xt::xtensor<int, 1>& in_gmin, virtual void init(const tensor::Tensor<int>& in_gmin,
const xt::xtensor<int, 1>& in_gmax, const double_2dvec& in_mult, const tensor::Tensor<int>& in_gmax, const double_2dvec& in_mult,
const double_3dvec& coeffs) = 0; const double_3dvec& coeffs) = 0;
//! \brief Combines the microscopic data. //! \brief Combines the microscopic data.
@ -96,7 +96,7 @@ public:
//! @param max_order If Legendre this is the maximum value of "n" in "Pn" //! @param max_order If Legendre this is the maximum value of "n" in "Pn"
//! requested; ignored otherwise. //! requested; ignored otherwise.
//! @return The dense scattering matrix. //! @return The dense scattering matrix.
virtual xt::xtensor<double, 3> get_matrix(size_t max_order) = 0; virtual tensor::Tensor<double> get_matrix(size_t max_order) = 0;
//! \brief Samples the outgoing energy from the ScattData info. //! \brief Samples the outgoing energy from the ScattData info.
//! //!
@ -135,8 +135,8 @@ protected:
ScattDataLegendre& leg, ScattDataTabular& tab); ScattDataLegendre& leg, ScattDataTabular& tab);
public: public:
void init(const xt::xtensor<int, 1>& in_gmin, void init(const tensor::Tensor<int>& in_gmin,
const xt::xtensor<int, 1>& in_gmax, const double_2dvec& in_mult, const tensor::Tensor<int>& in_gmax, const double_2dvec& in_mult,
const double_3dvec& coeffs) override; const double_3dvec& coeffs) override;
void combine(const vector<ScattData*>& those_scatts, void combine(const vector<ScattData*>& those_scatts,
@ -153,7 +153,7 @@ public:
size_t get_order() override { return dist[0][0].size() - 1; }; size_t get_order() override { return dist[0][0].size() - 1; };
xt::xtensor<double, 3> get_matrix(size_t max_order) override; tensor::Tensor<double> get_matrix(size_t max_order) override;
}; };
//============================================================================== //==============================================================================
@ -164,13 +164,13 @@ public:
class ScattDataHistogram : public ScattData { class ScattDataHistogram : public ScattData {
protected: protected:
xt::xtensor<double, 1> mu; // Angle distribution mu bin boundaries tensor::Tensor<double> mu; // Angle distribution mu bin boundaries
double dmu; // Quick storage of the mu spacing double dmu; // Quick storage of the mu spacing
double_3dvec fmu; // The angular distribution histogram double_3dvec fmu; // The angular distribution histogram
public: public:
void init(const xt::xtensor<int, 1>& in_gmin, void init(const tensor::Tensor<int>& in_gmin,
const xt::xtensor<int, 1>& in_gmax, const double_2dvec& in_mult, const tensor::Tensor<int>& in_gmax, const double_2dvec& in_mult,
const double_3dvec& coeffs) override; const double_3dvec& coeffs) override;
void combine(const vector<ScattData*>& those_scatts, void combine(const vector<ScattData*>& those_scatts,
@ -183,7 +183,7 @@ public:
size_t get_order() override { return dist[0][0].size(); }; size_t get_order() override { return dist[0][0].size(); };
xt::xtensor<double, 3> get_matrix(size_t max_order) override; tensor::Tensor<double> get_matrix(size_t max_order) override;
}; };
//============================================================================== //==============================================================================
@ -194,7 +194,7 @@ public:
class ScattDataTabular : public ScattData { class ScattDataTabular : public ScattData {
protected: protected:
xt::xtensor<double, 1> mu; // Angle distribution mu grid points tensor::Tensor<double> mu; // Angle distribution mu grid points
double dmu; // Quick storage of the mu spacing double dmu; // Quick storage of the mu spacing
double_3dvec fmu; // The angular distribution function double_3dvec fmu; // The angular distribution function
@ -204,8 +204,8 @@ protected:
ScattDataLegendre& leg, ScattDataTabular& tab); ScattDataLegendre& leg, ScattDataTabular& tab);
public: public:
void init(const xt::xtensor<int, 1>& in_gmin, void init(const tensor::Tensor<int>& in_gmin,
const xt::xtensor<int, 1>& in_gmax, const double_2dvec& in_mult, const tensor::Tensor<int>& in_gmax, const double_2dvec& in_mult,
const double_3dvec& coeffs) override; const double_3dvec& coeffs) override;
void combine(const vector<ScattData*>& those_scatts, void combine(const vector<ScattData*>& those_scatts,
@ -218,7 +218,7 @@ public:
size_t get_order() override { return dist[0][0].size(); }; size_t get_order() override { return dist[0][0].size(); };
xt::xtensor<double, 3> get_matrix(size_t max_order) override; tensor::Tensor<double> get_matrix(size_t max_order) override;
}; };
//============================================================================== //==============================================================================

View file

@ -5,7 +5,7 @@
#define OPENMC_SECONDARY_CORRELATED_H #define OPENMC_SECONDARY_CORRELATED_H
#include "hdf5.h" #include "hdf5.h"
#include "xtensor/xtensor.hpp" #include "openmc/tensor.h"
#include "openmc/angle_energy.h" #include "openmc/angle_energy.h"
#include "openmc/distribution.h" #include "openmc/distribution.h"
@ -25,9 +25,9 @@ public:
struct CorrTable { struct CorrTable {
int n_discrete; //!< Number of discrete lines int n_discrete; //!< Number of discrete lines
Interpolation interpolation; //!< Interpolation law Interpolation interpolation; //!< Interpolation law
xt::xtensor<double, 1> e_out; //!< Outgoing energies [eV] tensor::Tensor<double> e_out; //!< Outgoing energies [eV]
xt::xtensor<double, 1> p; //!< Probability density tensor::Tensor<double> p; //!< Probability density
xt::xtensor<double, 1> c; //!< Cumulative distribution tensor::Tensor<double> c; //!< Cumulative distribution
vector<unique_ptr<Tabular>> angle; //!< Angle distribution vector<unique_ptr<Tabular>> angle; //!< Angle distribution
}; };

View file

@ -5,7 +5,7 @@
#define OPENMC_SECONDARY_KALBACH_H #define OPENMC_SECONDARY_KALBACH_H
#include "hdf5.h" #include "hdf5.h"
#include "xtensor/xtensor.hpp" #include "openmc/tensor.h"
#include "openmc/angle_energy.h" #include "openmc/angle_energy.h"
#include "openmc/constants.h" #include "openmc/constants.h"
@ -37,11 +37,11 @@ private:
struct KMTable { struct KMTable {
int n_discrete; //!< Number of discrete lines int n_discrete; //!< Number of discrete lines
Interpolation interpolation; //!< Interpolation law Interpolation interpolation; //!< Interpolation law
xt::xtensor<double, 1> e_out; //!< Outgoing energies [eV] tensor::Tensor<double> e_out; //!< Outgoing energies [eV]
xt::xtensor<double, 1> p; //!< Probability density tensor::Tensor<double> p; //!< Probability density
xt::xtensor<double, 1> c; //!< Cumulative distribution tensor::Tensor<double> c; //!< Cumulative distribution
xt::xtensor<double, 1> r; //!< Pre-compound fraction tensor::Tensor<double> r; //!< Pre-compound fraction
xt::xtensor<double, 1> a; //!< Parameterized function tensor::Tensor<double> a; //!< Parameterized function
}; };
int n_region_; //!< Number of interpolation regions int n_region_; //!< Number of interpolation regions

View file

@ -9,7 +9,7 @@
#include "openmc/secondary_correlated.h" #include "openmc/secondary_correlated.h"
#include "openmc/vector.h" #include "openmc/vector.h"
#include "xtensor/xtensor.hpp" #include "openmc/tensor.h"
#include <hdf5.h> #include <hdf5.h>
namespace openmc { namespace openmc {
@ -83,7 +83,7 @@ public:
private: private:
const vector<double>& energy_; //!< Energies at which cosines are tabulated const vector<double>& energy_; //!< Energies at which cosines are tabulated
xt::xtensor<double, 2> mu_out_; //!< Cosines for each incident energy tensor::Tensor<double> mu_out_; //!< Cosines for each incident energy
}; };
//============================================================================== //==============================================================================
@ -109,9 +109,9 @@ public:
private: private:
const vector<double>& energy_; //!< Incident energies const vector<double>& energy_; //!< Incident energies
xt::xtensor<double, 2> tensor::Tensor<double>
energy_out_; //!< Outgoing energies for each incident energy energy_out_; //!< Outgoing energies for each incident energy
xt::xtensor<double, 3> tensor::Tensor<double>
mu_out_; //!< Outgoing cosines for each incident/outgoing energy mu_out_; //!< Outgoing cosines for each incident/outgoing energy
bool skewed_; //!< Whether outgoing energy distribution is skewed bool skewed_; //!< Whether outgoing energy distribution is skewed
}; };
@ -139,10 +139,10 @@ private:
//! Secondary energy/angle distribution //! Secondary energy/angle distribution
struct DistEnergySab { struct DistEnergySab {
std::size_t n_e_out; //!< Number of outgoing energies std::size_t n_e_out; //!< Number of outgoing energies
xt::xtensor<double, 1> e_out; //!< Outgoing energies tensor::Tensor<double> e_out; //!< Outgoing energies
xt::xtensor<double, 1> e_out_pdf; //!< Probability density function tensor::Tensor<double> e_out_pdf; //!< Probability density function
xt::xtensor<double, 1> e_out_cdf; //!< Cumulative distribution function tensor::Tensor<double> e_out_cdf; //!< Cumulative distribution function
xt::xtensor<double, 2> mu; //!< Equiprobable angles at each outgoing energy tensor::Tensor<double> mu; //!< Equiprobable angles at each outgoing energy
}; };
vector<double> energy_; //!< Incident energies vector<double> energy_; //!< Incident energies

View file

@ -8,9 +8,8 @@
#include "openmc/tallies/trigger.h" #include "openmc/tallies/trigger.h"
#include "openmc/vector.h" #include "openmc/vector.h"
#include "openmc/tensor.h"
#include "pugixml.hpp" #include "pugixml.hpp"
#include "xtensor/xfixed.hpp"
#include "xtensor/xtensor.hpp"
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
@ -55,7 +54,7 @@ public:
void set_nuclides(const vector<std::string>& nuclides); void set_nuclides(const vector<std::string>& nuclides);
const xt::xtensor<double, 3>& results() const { return results_; } const tensor::Tensor<double>& results() const { return results_; }
//! returns vector of indices corresponding to the tally this is called on //! returns vector of indices corresponding to the tally this is called on
const vector<int32_t>& filters() const { return filters_; } const vector<int32_t>& filters() const { return filters_; }
@ -125,7 +124,7 @@ public:
int score_index(const std::string& score) const; int score_index(const std::string& score) const;
//! Tally results reshaped according to filter sizes //! Tally results reshaped according to filter sizes
xt::xarray<double> get_reshaped_data() const; tensor::Tensor<double> get_reshaped_data() const;
//! A string representing the i-th score on this tally //! A string representing the i-th score on this tally
std::string score_name(int score_idx) const; std::string score_name(int score_idx) const;
@ -160,7 +159,7 @@ public:
//! combination of filters (e.g. specific cell, specific energy group, etc.) //! combination of filters (e.g. specific cell, specific energy group, etc.)
//! and the second dimension of the array is for scores (e.g. flux, total //! and the second dimension of the array is for scores (e.g. flux, total
//! reaction rate, fission reaction rate, etc.) //! reaction rate, fission reaction rate, etc.)
xt::xtensor<double, 3> results_; tensor::Tensor<double> results_;
//! True if this tally should be written to statepoint files //! True if this tally should be written to statepoint files
bool writable_ {true}; bool writable_ {true};
@ -220,8 +219,7 @@ extern vector<double> time_grid;
namespace simulation { namespace simulation {
//! Global tallies (such as k-effective estimators) //! Global tallies (such as k-effective estimators)
extern xt::xtensor_fixed<double, xt::xshape<N_GLOBAL_TALLIES, 3>> extern tensor::StaticTensor2D<double, N_GLOBAL_TALLIES, 3> global_tallies;
global_tallies;
//! Number of realizations for global tallies //! Number of realizations for global tallies
extern "C" int32_t n_realizations; extern "C" int32_t n_realizations;
@ -257,12 +255,6 @@ double distance_to_time_boundary(double time, double speed);
//! Determine which tallies should be active //! Determine which tallies should be active
void setup_active_tallies(); void setup_active_tallies();
// Alias for the type returned by xt::adapt(...). N is the dimension of the
// multidimensional array
template<std::size_t N>
using adaptor_type =
xt::xtensor_adaptor<xt::xbuffer_adaptor<double*&, xt::no_ownership>, N>;
#ifdef OPENMC_MPI #ifdef OPENMC_MPI
//! Collect all tally results onto master process //! Collect all tally results onto master process
void reduce_tally_results(); void reduce_tally_results();

1185
include/openmc/tensor.h Normal file

File diff suppressed because it is too large Load diff

View file

@ -5,7 +5,7 @@
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include "xtensor/xtensor.hpp" #include "openmc/tensor.h"
#include "openmc/angle_energy.h" #include "openmc/angle_energy.h"
#include "openmc/endf.h" #include "openmc/endf.h"

View file

@ -3,7 +3,7 @@
#ifndef OPENMC_URR_H #ifndef OPENMC_URR_H
#define OPENMC_URR_H #define OPENMC_URR_H
#include "xtensor/xtensor.hpp" #include "openmc/tensor.h"
#include "openmc/constants.h" #include "openmc/constants.h"
#include "openmc/hdf5_interface.h" #include "openmc/hdf5_interface.h"
@ -40,11 +40,11 @@ public:
* below, obviously, values of the CDF are stored. For the xs_values * below, obviously, values of the CDF are stored. For the xs_values
* variable, the columns line up with the index of cdf_values. * variable, the columns line up with the index of cdf_values.
*/ */
xt::xtensor<double, 2> cdf_values_; // Note: must be row major! tensor::Tensor<double> cdf_values_; // Note: must be row major!
xt::xtensor<XSSet, 2> xs_values_; tensor::Tensor<XSSet> xs_values_;
// Number of points in the CDF // Number of points in the CDF
auto n_cdf() const { return cdf_values_.shape()[1]; } auto n_cdf() const { return cdf_values_.shape(1); }
//! \brief Load the URR data from the provided HDF5 group //! \brief Load the URR data from the provided HDF5 group
explicit UrrData(hid_t group_id); explicit UrrData(hid_t group_id);

View file

@ -12,8 +12,8 @@
#include "openmc/tallies/trigger.h" #include "openmc/tallies/trigger.h"
#include "openmc/vector.h" #include "openmc/vector.h"
#include "openmc/tensor.h"
#include "pugixml.hpp" #include "pugixml.hpp"
#include "xtensor/xtensor.hpp"
#ifdef _OPENMP #ifdef _OPENMP
#include <omp.h> #include <omp.h>
#endif #endif

View file

@ -143,10 +143,10 @@ public:
const vector<double>& energy_bounds() const { return energy_bounds_; } const vector<double>& energy_bounds() const { return energy_bounds_; }
void set_bounds(const xt::xtensor<double, 2>& lower_ww_bounds, void set_bounds(const tensor::Tensor<double>& lower_ww_bounds,
const xt::xtensor<double, 2>& upper_bounds); const tensor::Tensor<double>& upper_bounds);
void set_bounds(const xt::xtensor<double, 2>& lower_bounds, double ratio); void set_bounds(const tensor::Tensor<double>& lower_bounds, double ratio);
void set_bounds( void set_bounds(
span<const double> lower_bounds, span<const double> upper_bounds); span<const double> lower_bounds, span<const double> upper_bounds);
@ -182,11 +182,11 @@ public:
const std::unique_ptr<Mesh>& mesh() const { return model::meshes[mesh_idx_]; } const std::unique_ptr<Mesh>& mesh() const { return model::meshes[mesh_idx_]; }
const xt::xtensor<double, 2>& lower_ww_bounds() const { return lower_ww_; } const tensor::Tensor<double>& lower_ww_bounds() const { return lower_ww_; }
xt::xtensor<double, 2>& lower_ww_bounds() { return lower_ww_; } tensor::Tensor<double>& lower_ww_bounds() { return lower_ww_; }
const xt::xtensor<double, 2>& upper_ww_bounds() const { return upper_ww_; } const tensor::Tensor<double>& upper_ww_bounds() const { return upper_ww_; }
xt::xtensor<double, 2>& upper_ww_bounds() { return upper_ww_; } tensor::Tensor<double>& upper_ww_bounds() { return upper_ww_; }
ParticleType particle_type() const { return particle_type_; } ParticleType particle_type() const { return particle_type_; }
@ -197,9 +197,9 @@ private:
int64_t index_; //!< Index into weight windows vector int64_t index_; //!< Index into weight windows vector
ParticleType particle_type_; //!< Particle type to apply weight windows to ParticleType particle_type_; //!< Particle type to apply weight windows to
vector<double> energy_bounds_; //!< Energy boundaries [eV] vector<double> energy_bounds_; //!< Energy boundaries [eV]
xt::xtensor<double, 2> lower_ww_; //!< Lower weight window bounds (shape: tensor::Tensor<double> lower_ww_; //!< Lower weight window bounds (shape:
//!< energy_bins, mesh_bins (k, j, i)) //!< energy_bins, mesh_bins (k, j, i))
xt::xtensor<double, 2> tensor::Tensor<double>
upper_ww_; //!< Upper weight window bounds (shape: energy_bins, mesh_bins) upper_ww_; //!< Upper weight window bounds (shape: energy_bins, mesh_bins)
double survival_ratio_ {3.0}; //!< Survival weight ratio double survival_ratio_ {3.0}; //!< Survival weight ratio
double max_lb_ratio_ {1.0}; //!< Maximum lower bound to particle weight ratio double max_lb_ratio_ {1.0}; //!< Maximum lower bound to particle weight ratio

View file

@ -2,7 +2,7 @@
#define OPENMC_WMP_H #define OPENMC_WMP_H
#include "hdf5.h" #include "hdf5.h"
#include "xtensor/xtensor.hpp" #include "openmc/tensor.h"
#include <complex> #include <complex>
#include <string> #include <string>
@ -78,9 +78,9 @@ public:
int fit_order_; //!< Order of the fit int fit_order_; //!< Order of the fit
bool fissionable_; //!< Is the nuclide fissionable? bool fissionable_; //!< Is the nuclide fissionable?
vector<WindowInfo> window_info_; // Information about a window vector<WindowInfo> window_info_; // Information about a window
xt::xtensor<double, 3> tensor::Tensor<double>
curvefit_; // Curve fit coefficients (window, poly order, reaction) curvefit_; // Curve fit coefficients (window, poly order, reaction)
xt::xtensor<std::complex<double>, 2> data_; //!< Poles and residues tensor::Tensor<std::complex<double>> data_; //!< Poles and residues
// Constant data // Constant data
static constexpr int MAX_POLY_COEFFICIENTS = static constexpr int MAX_POLY_COEFFICIENTS =

View file

@ -5,9 +5,8 @@
#include <sstream> // for stringstream #include <sstream> // for stringstream
#include <string> #include <string>
#include "openmc/tensor.h"
#include "pugixml.hpp" #include "pugixml.hpp"
#include "xtensor/xadapt.hpp"
#include "xtensor/xarray.hpp"
#include "openmc/position.h" #include "openmc/position.h"
#include "openmc/vector.h" #include "openmc/vector.h"
@ -42,12 +41,11 @@ vector<T> get_node_array(
} }
template<typename T> template<typename T>
xt::xarray<T> get_node_xarray( tensor::Tensor<T> get_node_tensor(
pugi::xml_node node, const char* name, bool lowercase = false) pugi::xml_node node, const char* name, bool lowercase = false)
{ {
vector<T> v = get_node_array<T>(node, name, lowercase); vector<T> v = get_node_array<T>(node, name, lowercase);
vector<std::size_t> shape = {v.size()}; return tensor::Tensor<T>(v.data(), v.size());
return xt::adapt(v, shape);
} }
std::vector<Position> get_node_position_array( std::vector<Position> get_node_position_array(

View file

@ -4,7 +4,7 @@
#ifndef OPENMC_XSDATA_H #ifndef OPENMC_XSDATA_H
#define OPENMC_XSDATA_H #define OPENMC_XSDATA_H
#include "xtensor/xtensor.hpp" #include "openmc/tensor.h"
#include "openmc/hdf5_interface.h" #include "openmc/hdf5_interface.h"
#include "openmc/memory.h" #include "openmc/memory.h"
@ -69,26 +69,26 @@ private:
public: public:
// The following quantities have the following dimensions: // The following quantities have the following dimensions:
// [angle][incoming group] // [angle][incoming group]
xt::xtensor<double, 2> total; tensor::Tensor<double> total;
xt::xtensor<double, 2> absorption; tensor::Tensor<double> absorption;
xt::xtensor<double, 2> nu_fission; tensor::Tensor<double> nu_fission;
xt::xtensor<double, 2> prompt_nu_fission; tensor::Tensor<double> prompt_nu_fission;
xt::xtensor<double, 2> kappa_fission; tensor::Tensor<double> kappa_fission;
xt::xtensor<double, 2> fission; tensor::Tensor<double> fission;
xt::xtensor<double, 2> inverse_velocity; tensor::Tensor<double> inverse_velocity;
// decay_rate has the following dimensions: // decay_rate has the following dimensions:
// [angle][delayed group] // [angle][delayed group]
xt::xtensor<double, 2> decay_rate; tensor::Tensor<double> decay_rate;
// delayed_nu_fission has the following dimensions: // delayed_nu_fission has the following dimensions:
// [angle][delayed group][incoming group] // [angle][delayed group][incoming group]
xt::xtensor<double, 3> delayed_nu_fission; tensor::Tensor<double> delayed_nu_fission;
// chi_prompt has the following dimensions: // chi_prompt has the following dimensions:
// [angle][incoming group][outgoing group] // [angle][incoming group][outgoing group]
xt::xtensor<double, 3> chi_prompt; tensor::Tensor<double> chi_prompt;
// chi_delayed has the following dimensions: // chi_delayed has the following dimensions:
// [angle][incoming group][outgoing group][delayed group] // [angle][incoming group][outgoing group][delayed group]
xt::xtensor<double, 4> chi_delayed; tensor::Tensor<double> chi_delayed;
// scatter has the following dimensions: [angle] // scatter has the following dimensions: [angle]
vector<std::shared_ptr<ScattData>> scatter; vector<std::shared_ptr<ScattData>> scatter;

View file

@ -7,6 +7,7 @@
#include "openmc/vector.h" #include "openmc/vector.h"
#include <cstdint> #include <cstdint>
#include <numeric>
namespace openmc { namespace openmc {

View file

@ -6,7 +6,7 @@
#include "openmc/search.h" #include "openmc/search.h"
#include "openmc/settings.h" #include "openmc/settings.h"
#include "xtensor/xmath.hpp" #include "openmc/tensor.h"
namespace openmc { namespace openmc {
@ -16,8 +16,8 @@ namespace openmc {
namespace data { namespace data {
xt::xtensor<double, 1> ttb_e_grid; tensor::Tensor<double> ttb_e_grid;
xt::xtensor<double, 1> ttb_k_grid; tensor::Tensor<double> ttb_k_grid;
vector<Bremsstrahlung> ttb; vector<Bremsstrahlung> ttb;
} // namespace data } // namespace data

View file

@ -5,7 +5,7 @@
#ifdef _OPENMP #ifdef _OPENMP
#include <omp.h> #include <omp.h>
#endif #endif
#include "xtensor/xtensor.hpp" #include "openmc/tensor.h"
#include "openmc/bank.h" #include "openmc/bank.h"
#include "openmc/capi.h" #include "openmc/capi.h"
@ -36,7 +36,7 @@ double spectral;
int nx, ny, nz, ng; int nx, ny, nz, ng;
xt::xtensor<int, 2> indexmap; tensor::Tensor<int> indexmap;
int use_all_threads; int use_all_threads;
@ -79,15 +79,14 @@ int get_cmfd_energy_bin(const double E)
// COUNT_BANK_SITES bins fission sites according to CMFD mesh and energy // COUNT_BANK_SITES bins fission sites according to CMFD mesh and energy
//============================================================================== //==============================================================================
xt::xtensor<double, 1> count_bank_sites( tensor::Tensor<double> count_bank_sites(
xt::xtensor<int, 1>& bins, bool* outside) tensor::Tensor<int>& bins, bool* outside)
{ {
// Determine shape of array for counts // Determine shape of array for counts
std::size_t cnt_size = cmfd::nx * cmfd::ny * cmfd::nz * cmfd::ng; std::size_t cnt_size = cmfd::nx * cmfd::ny * cmfd::nz * cmfd::ng;
vector<std::size_t> cnt_shape = {cnt_size};
// Create array of zeros // Create array of zeros
xt::xarray<double> cnt {cnt_shape, 0.0}; tensor::Tensor<double> cnt = tensor::zeros<double>({cnt_size});
bool outside_ = false; bool outside_ = false;
auto bank_size = simulation::source_bank.size(); auto bank_size = simulation::source_bank.size();
@ -113,29 +112,22 @@ xt::xtensor<double, 1> count_bank_sites(
bins[i] = mesh_bin * cmfd::ng + energy_bin; bins[i] = mesh_bin * cmfd::ng + energy_bin;
} }
// Create copy of count data. Since ownership will be acquired by xtensor,
// std::allocator must be used to avoid Valgrind mismatched free() / delete
// warnings.
int total = cnt.size(); int total = cnt.size();
double* cnt_reduced = std::allocator<double> {}.allocate(total); tensor::Tensor<double> counts = tensor::zeros<double>({cnt_size});
#ifdef OPENMC_MPI #ifdef OPENMC_MPI
// collect values from all processors // collect values from all processors
MPI_Reduce( MPI_Reduce(
cnt.data(), cnt_reduced, total, MPI_DOUBLE, MPI_SUM, 0, mpi::intracomm); cnt.data(), counts.data(), total, MPI_DOUBLE, MPI_SUM, 0, mpi::intracomm);
// Check if there were sites outside the mesh for any processor // Check if there were sites outside the mesh for any processor
MPI_Reduce(&outside_, outside, 1, MPI_C_BOOL, MPI_LOR, 0, mpi::intracomm); MPI_Reduce(&outside_, outside, 1, MPI_C_BOOL, MPI_LOR, 0, mpi::intracomm);
#else #else
std::copy(cnt.data(), cnt.data() + total, cnt_reduced); std::copy(cnt.data(), cnt.data() + total, counts.data());
*outside = outside_; *outside = outside_;
#endif #endif
// Adapt reduced values in array back into an xarray
auto arr = xt::adapt(cnt_reduced, total, xt::acquire_ownership(), cnt_shape);
xt::xarray<double> counts = arr;
return counts; return counts;
} }
@ -151,19 +143,19 @@ extern "C" void openmc_cmfd_reweight(
std::size_t src_size = cmfd::nx * cmfd::ny * cmfd::nz * cmfd::ng; std::size_t src_size = cmfd::nx * cmfd::ny * cmfd::nz * cmfd::ng;
// count bank sites for CMFD mesh, store bins in bank_bins for reweighting // count bank sites for CMFD mesh, store bins in bank_bins for reweighting
xt::xtensor<int, 1> bank_bins({bank_size}, 0); tensor::Tensor<int> bank_bins = tensor::zeros<int>({bank_size});
bool sites_outside; bool sites_outside;
xt::xtensor<double, 1> sourcecounts = tensor::Tensor<double> sourcecounts =
count_bank_sites(bank_bins, &sites_outside); count_bank_sites(bank_bins, &sites_outside);
// Compute CMFD weightfactors // Compute CMFD weightfactors
xt::xtensor<double, 1> weightfactors = xt::xtensor<double, 1>({src_size}, 1.); tensor::Tensor<double> weightfactors = tensor::ones<double>({src_size});
if (mpi::master) { if (mpi::master) {
if (sites_outside) { if (sites_outside) {
fatal_error("Source sites outside of the CMFD mesh"); fatal_error("Source sites outside of the CMFD mesh");
} }
double norm = xt::sum(sourcecounts)() / cmfd::norm; double norm = sourcecounts.sum() / cmfd::norm;
for (int i = 0; i < src_size; i++) { for (int i = 0; i < src_size; i++) {
if (sourcecounts[i] > 0 && cmfd_src[i] > 0) { if (sourcecounts[i] > 0 && cmfd_src[i] > 0) {
weightfactors[i] = cmfd_src[i] * norm / sourcecounts[i]; weightfactors[i] = cmfd_src[i] * norm / sourcecounts[i];
@ -561,7 +553,7 @@ void free_memory_cmfd()
cmfd::indices.clear(); cmfd::indices.clear();
cmfd::egrid.clear(); cmfd::egrid.clear();
// Resize xtensors to be empty // Resize tensors to be empty
cmfd::indexmap.resize({0}); cmfd::indexmap.resize({0});
// Set pointers to null // Set pointers to null

View file

@ -254,7 +254,7 @@ void read_ce_cross_sections(const vector<vector<double>>& nuc_temps,
if (settings::photon_transport && if (settings::photon_transport &&
settings::electron_treatment == ElectronTreatment::TTB) { settings::electron_treatment == ElectronTreatment::TTB) {
// Take logarithm of energies since they are log-log interpolated // Take logarithm of energies since they are log-log interpolated
data::ttb_e_grid = xt::log(data::ttb_e_grid); data::ttb_e_grid = tensor::log(data::ttb_e_grid);
} }
// Show minimum/maximum temperature // Show minimum/maximum temperature

View file

@ -2,8 +2,7 @@
#include <cmath> // for abs, copysign #include <cmath> // for abs, copysign
#include "xtensor/xarray.hpp" #include "openmc/tensor.h"
#include "xtensor/xview.hpp"
#include "openmc/endf.h" #include "openmc/endf.h"
#include "openmc/hdf5_interface.h" #include "openmc/hdf5_interface.h"
@ -30,7 +29,7 @@ AngleDistribution::AngleDistribution(hid_t group)
hid_t dset = open_dataset(group, "mu"); hid_t dset = open_dataset(group, "mu");
read_attribute(dset, "offsets", offsets); read_attribute(dset, "offsets", offsets);
read_attribute(dset, "interpolation", interp); read_attribute(dset, "interpolation", interp);
xt::xarray<double> temp; tensor::Tensor<double> temp;
read_dataset(dset, temp); read_dataset(dset, temp);
close_dataset(dset); close_dataset(dset);
@ -41,13 +40,13 @@ AngleDistribution::AngleDistribution(hid_t group)
if (i < n_energy - 1) { if (i < n_energy - 1) {
n = offsets[i + 1] - j; n = offsets[i + 1] - j;
} else { } else {
n = temp.shape()[1] - j; n = temp.shape(1) - j;
} }
// Create and initialize tabular distribution // Create and initialize tabular distribution
auto xs = xt::view(temp, 0, xt::range(j, j + n)); tensor::View<double> xs = temp.slice(0, tensor::range(j, j + n));
auto ps = xt::view(temp, 1, xt::range(j, j + n)); tensor::View<double> ps = temp.slice(1, tensor::range(j, j + n));
auto cs = xt::view(temp, 2, xt::range(j, j + n)); tensor::View<double> cs = temp.slice(2, tensor::range(j, j + n));
vector<double> x {xs.begin(), xs.end()}; vector<double> x {xs.begin(), xs.end()};
vector<double> p {ps.begin(), ps.end()}; vector<double> p {ps.begin(), ps.end()};
vector<double> c {cs.begin(), cs.end()}; vector<double> c {cs.begin(), cs.end()};

View file

@ -4,7 +4,7 @@
#include <cstddef> // for size_t #include <cstddef> // for size_t
#include <iterator> // for back_inserter #include <iterator> // for back_inserter
#include "xtensor/xview.hpp" #include "openmc/tensor.h"
#include "openmc/endf.h" #include "openmc/endf.h"
#include "openmc/hdf5_interface.h" #include "openmc/hdf5_interface.h"
@ -60,11 +60,11 @@ ContinuousTabular::ContinuousTabular(hid_t group)
hid_t dset = open_dataset(group, "energy"); hid_t dset = open_dataset(group, "energy");
// Get interpolation parameters // Get interpolation parameters
xt::xarray<int> temp; tensor::Tensor<int> temp;
read_attribute(dset, "interpolation", temp); read_attribute(dset, "interpolation", temp);
auto temp_b = xt::view(temp, 0); // view of breakpoints tensor::View<int> temp_b = temp.slice(0); // breakpoints
auto temp_i = xt::view(temp, 1); // view of interpolation parameters tensor::View<int> temp_i = temp.slice(1); // interpolation parameters
std::copy(temp_b.begin(), temp_b.end(), std::back_inserter(breakpoints_)); std::copy(temp_b.begin(), temp_b.end(), std::back_inserter(breakpoints_));
for (const auto i : temp_i) for (const auto i : temp_i)
@ -85,7 +85,7 @@ ContinuousTabular::ContinuousTabular(hid_t group)
read_attribute(dset, "interpolation", interp); read_attribute(dset, "interpolation", interp);
read_attribute(dset, "n_discrete_lines", n_discrete); read_attribute(dset, "n_discrete_lines", n_discrete);
xt::xarray<double> eout; tensor::Tensor<double> eout;
read_dataset(dset, eout); read_dataset(dset, eout);
close_dataset(dset); close_dataset(dset);
@ -96,7 +96,7 @@ ContinuousTabular::ContinuousTabular(hid_t group)
if (i < n_energy - 1) { if (i < n_energy - 1) {
n = offsets[i + 1] - j; n = offsets[i + 1] - j;
} else { } else {
n = eout.shape()[1] - j; n = eout.shape(1) - j;
} }
// Assign interpolation scheme and number of discrete lines // Assign interpolation scheme and number of discrete lines
@ -105,15 +105,15 @@ ContinuousTabular::ContinuousTabular(hid_t group)
d.n_discrete = n_discrete[i]; d.n_discrete = n_discrete[i];
// Copy data // Copy data
d.e_out = xt::view(eout, 0, xt::range(j, j + n)); d.e_out = eout.slice(0, tensor::range(j, j + n));
d.p = xt::view(eout, 1, xt::range(j, j + n)); d.p = eout.slice(1, tensor::range(j, j + n));
// To get answers that match ACE data, for now we still use the tabulated // To get answers that match ACE data, for now we still use the tabulated
// CDF values that were passed through to the HDF5 library. At a later // CDF values that were passed through to the HDF5 library. At a later
// time, we can remove the CDF values from the HDF5 library and // time, we can remove the CDF values from the HDF5 library and
// reconstruct them using the PDF // reconstruct them using the PDF
if (true) { if (true) {
d.c = xt::view(eout, 2, xt::range(j, j + n)); d.c = eout.slice(2, tensor::range(j, j + n));
} else { } else {
// Calculate cumulative distribution function -- discrete portion // Calculate cumulative distribution function -- discrete portion
for (int k = 0; k < d.n_discrete; ++k) { for (int k = 0; k < d.n_discrete; ++k) {

View file

@ -1,9 +1,6 @@
#include "openmc/eigenvalue.h" #include "openmc/eigenvalue.h"
#include "xtensor/xbuilder.hpp" #include "openmc/tensor.h"
#include "xtensor/xmath.hpp"
#include "xtensor/xtensor.hpp"
#include "xtensor/xview.hpp"
#include "openmc/array.h" #include "openmc/array.h"
#include "openmc/bank.h" #include "openmc/bank.h"
@ -39,7 +36,7 @@ namespace simulation {
double keff_generation; double keff_generation;
array<double, 2> k_sum; array<double, 2> k_sum;
vector<double> entropy; vector<double> entropy;
xt::xtensor<double, 1> source_frac; tensor::Tensor<double> source_frac;
} // namespace simulation } // namespace simulation
@ -452,7 +449,7 @@ int openmc_get_keff(double* k_combined)
const auto& gt = simulation::global_tallies; const auto& gt = simulation::global_tallies;
array<double, 3> kv {}; array<double, 3> kv {};
xt::xtensor<double, 2> cov = xt::zeros<double>({3, 3}); tensor::Tensor<double> cov = tensor::zeros<double>({3, 3});
kv[0] = gt(GlobalTally::K_COLLISION, TallyResult::SUM) / n; kv[0] = gt(GlobalTally::K_COLLISION, TallyResult::SUM) / n;
kv[1] = gt(GlobalTally::K_ABSORPTION, TallyResult::SUM) / n; kv[1] = gt(GlobalTally::K_ABSORPTION, TallyResult::SUM) / n;
kv[2] = gt(GlobalTally::K_TRACKLENGTH, TallyResult::SUM) / n; kv[2] = gt(GlobalTally::K_TRACKLENGTH, TallyResult::SUM) / n;
@ -591,7 +588,7 @@ void shannon_entropy()
{ {
// Get source weight in each mesh bin // Get source weight in each mesh bin
bool sites_outside; bool sites_outside;
xt::xtensor<double, 1> p = tensor::Tensor<double> p =
simulation::entropy_mesh->count_sites(simulation::fission_bank.data(), simulation::entropy_mesh->count_sites(simulation::fission_bank.data(),
simulation::fission_bank.size(), &sites_outside); simulation::fission_bank.size(), &sites_outside);
@ -603,7 +600,7 @@ void shannon_entropy()
if (mpi::master) { if (mpi::master) {
// Normalize to total weight of bank sites // Normalize to total weight of bank sites
p /= xt::sum(p); p /= p.sum();
// Sum values to obtain Shannon entropy // Sum values to obtain Shannon entropy
double H = 0.0; double H = 0.0;
@ -627,7 +624,7 @@ void ufs_count_sites()
std::size_t n = simulation::ufs_mesh->n_bins(); std::size_t n = simulation::ufs_mesh->n_bins();
double vol_frac = simulation::ufs_mesh->volume_frac_; double vol_frac = simulation::ufs_mesh->volume_frac_;
simulation::source_frac = xt::xtensor<double, 1>({n}, vol_frac); simulation::source_frac = tensor::Tensor<double>({n}, vol_frac);
} else { } else {
// count number of source sites in each ufs mesh cell // count number of source sites in each ufs mesh cell
@ -649,7 +646,7 @@ void ufs_count_sites()
#endif #endif
// Normalize to total weight to get fraction of source in each cell // Normalize to total weight to get fraction of source in each cell
double total = xt::sum(simulation::source_frac)(); double total = simulation::source_frac.sum();
simulation::source_frac /= total; simulation::source_frac /= total;
// Since the total starting weight is not equal to n_particles, we need to // Since the total starting weight is not equal to n_particles, we need to

View file

@ -5,8 +5,7 @@
#include <iterator> // for back_inserter #include <iterator> // for back_inserter
#include <stdexcept> // for runtime_error #include <stdexcept> // for runtime_error
#include "xtensor/xarray.hpp" #include "openmc/tensor.h"
#include "xtensor/xview.hpp"
#include "openmc/array.h" #include "openmc/array.h"
#include "openmc/constants.h" #include "openmc/constants.h"
@ -153,11 +152,11 @@ Tabulated1D::Tabulated1D(hid_t dset)
for (const auto i : int_temp) for (const auto i : int_temp)
int_.push_back(int2interp(i)); int_.push_back(int2interp(i));
xt::xarray<double> arr; tensor::Tensor<double> arr;
read_dataset(dset, arr); read_dataset(dset, arr);
auto xs = xt::view(arr, 0); tensor::View<double> xs = arr.slice(0);
auto ys = xt::view(arr, 1); tensor::View<double> ys = arr.slice(1);
std::copy(xs.begin(), xs.end(), std::back_inserter(x_)); std::copy(xs.begin(), xs.end(), std::back_inserter(x_));
std::copy(ys.begin(), ys.end(), std::back_inserter(y_)); std::copy(ys.begin(), ys.end(), std::back_inserter(y_));
@ -229,12 +228,12 @@ double Tabulated1D::operator()(double x) const
CoherentElasticXS::CoherentElasticXS(hid_t dset) CoherentElasticXS::CoherentElasticXS(hid_t dset)
{ {
// Read 2D array from dataset // Read 2D array from dataset
xt::xarray<double> arr; tensor::Tensor<double> arr;
read_dataset(dset, arr); read_dataset(dset, arr);
// Get views for Bragg edges and structure factors // Get views for Bragg edges and structure factors
auto E = xt::view(arr, 0); tensor::View<double> E = arr.slice(0);
auto s = xt::view(arr, 1); tensor::View<double> s = arr.slice(1);
// Copy Bragg edges and partial sums of structure factors // Copy Bragg edges and partial sums of structure factors
std::copy(E.begin(), E.end(), std::back_inserter(bragg_edges_)); std::copy(E.begin(), E.end(), std::back_inserter(bragg_edges_));

View file

@ -30,7 +30,7 @@
#include "openmc/volume_calc.h" #include "openmc/volume_calc.h"
#include "openmc/weight_windows.h" #include "openmc/weight_windows.h"
#include "xtensor/xview.hpp" #include "openmc/tensor.h"
namespace openmc { namespace openmc {
@ -203,7 +203,7 @@ int openmc_reset()
// Reset global tallies // Reset global tallies
simulation::n_realizations = 0; simulation::n_realizations = 0;
xt::view(simulation::global_tallies, xt::all()) = 0.0; simulation::global_tallies.fill(0.0);
simulation::k_col_abs = 0.0; simulation::k_col_abs = 0.0;
simulation::k_col_tra = 0.0; simulation::k_col_tra = 0.0;

View file

@ -4,8 +4,7 @@
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
#include "xtensor/xarray.hpp" #include "openmc/tensor.h"
#include "xtensor/xtensor.hpp"
#include <fmt/core.h> #include <fmt/core.h>
#include "hdf5.h" #include "hdf5.h"
@ -466,22 +465,19 @@ void read_dataset_lowlevel(hid_t obj_id, const char* name, hid_t mem_type_id,
} }
template<> template<>
void read_dataset(hid_t dset, xt::xarray<std::complex<double>>& arr, bool indep) void read_dataset(
hid_t dset, tensor::Tensor<std::complex<double>>& tensor, bool indep)
{ {
// Get shape of dataset // Get shape of dataset
vector<hsize_t> shape = object_shape(dset); vector<hsize_t> shape = object_shape(dset);
// Allocate new array to read data into // Resize tensor and read data directly
std::size_t size = 1; vector<size_t> tshape(shape.begin(), shape.end());
for (const auto x : shape) tensor.resize(tshape);
size *= x;
vector<std::complex<double>> buffer(size);
// Read data from attribute // Read data from dataset
read_complex(dset, nullptr, buffer.data(), indep); read_complex(dset, nullptr,
reinterpret_cast<std::complex<double>*>(tensor.data()), indep);
// Adapt into xarray
arr = xt::adapt(buffer, shape);
} }
void read_double(hid_t obj_id, const char* name, double* buffer, bool indep) void read_double(hid_t obj_id, const char* name, double* buffer, bool indep)

View file

@ -8,9 +8,7 @@
#include <string> #include <string>
#include <unordered_set> #include <unordered_set>
#include "xtensor/xbuilder.hpp" #include "openmc/tensor.h"
#include "xtensor/xoperation.hpp"
#include "xtensor/xview.hpp"
#include "openmc/capi.h" #include "openmc/capi.h"
#include "openmc/container_util.h" #include "openmc/container_util.h"
@ -216,7 +214,7 @@ Material::Material(pugi::xml_node node)
// allocate arrays in Material object // allocate arrays in Material object
auto n = names.size(); auto n = names.size();
nuclide_.reserve(n); nuclide_.reserve(n);
atom_density_ = xt::empty<double>({n}); atom_density_ = tensor::Tensor<double>({n});
if (settings::photon_transport) if (settings::photon_transport)
element_.reserve(n); element_.reserve(n);
@ -290,14 +288,14 @@ Material::Material(pugi::xml_node node)
// Check to make sure either all atom percents or all weight percents are // Check to make sure either all atom percents or all weight percents are
// given // given
if (!(xt::all(atom_density_ >= 0.0) || xt::all(atom_density_ <= 0.0))) { if (!((atom_density_ >= 0.0).all() || (atom_density_ <= 0.0).all())) {
fatal_error( fatal_error(
"Cannot mix atom and weight percents in material " + std::to_string(id_)); "Cannot mix atom and weight percents in material " + std::to_string(id_));
} }
// Determine density if it is a sum value // Determine density if it is a sum value
if (sum_density) if (sum_density)
density_ = xt::sum(atom_density_)(); density_ = atom_density_.sum();
if (check_for_node(node, "temperature")) { if (check_for_node(node, "temperature")) {
temperature_ = std::stod(get_node_value(node, "temperature")); temperature_ = std::stod(get_node_value(node, "temperature"));
@ -435,7 +433,7 @@ void Material::normalize_density()
// determine normalized atom percents. if given atom percents, this is // determine normalized atom percents. if given atom percents, this is
// straightforward. if given weight percents, the value is w/awr and is // straightforward. if given weight percents, the value is w/awr and is
// divided by sum(w/awr) // divided by sum(w/awr)
atom_density_ /= xt::sum(atom_density_)(); atom_density_ /= atom_density_.sum();
// Change density in g/cm^3 to atom/b-cm. Since all values are now in // Change density in g/cm^3 to atom/b-cm. Since all values are now in
// atom percent, the sum needs to be re-evaluated as 1/sum(x*awr) // atom percent, the sum needs to be re-evaluated as 1/sum(x*awr)
@ -641,14 +639,14 @@ void Material::init_bremsstrahlung()
bool positron = (particle == 1); bool positron = (particle == 1);
// Allocate arrays for TTB data // Allocate arrays for TTB data
ttb->pdf = xt::zeros<double>({n_e, n_e}); ttb->pdf = tensor::zeros<double>({n_e, n_e});
ttb->cdf = xt::zeros<double>({n_e, n_e}); ttb->cdf = tensor::zeros<double>({n_e, n_e});
ttb->yield = xt::zeros<double>({n_e}); ttb->yield = tensor::zeros<double>({n_e});
// Allocate temporary arrays // Allocate temporary arrays
xt::xtensor<double, 1> stopping_power_collision({n_e}, 0.0); auto stopping_power_collision = tensor::zeros<double>({n_e});
xt::xtensor<double, 1> stopping_power_radiative({n_e}, 0.0); auto stopping_power_radiative = tensor::zeros<double>({n_e});
xt::xtensor<double, 2> dcs({n_e, n_k}, 0.0); auto dcs = tensor::zeros<double>({n_e, n_k});
double Z_eq_sq = 0.0; double Z_eq_sq = 0.0;
double sum_density = 0.0; double sum_density = 0.0;
@ -698,18 +696,18 @@ void Material::init_bremsstrahlung()
1.0595e-3 * std::pow(t, 5) + 7.0568e-5 * std::pow(t, 6) - 1.0595e-3 * std::pow(t, 5) + 7.0568e-5 * std::pow(t, 6) -
1.808e-6 * std::pow(t, 7)); 1.808e-6 * std::pow(t, 7));
stopping_power_radiative(i) *= r; stopping_power_radiative(i) *= r;
auto dcs_i = xt::view(dcs, i, xt::all()); tensor::View<double> dcs_i = dcs.slice(i);
dcs_i *= r; dcs_i *= r;
} }
} }
// Total material stopping power // Total material stopping power
xt::xtensor<double, 1> stopping_power = tensor::Tensor<double> stopping_power =
stopping_power_collision + stopping_power_radiative; stopping_power_collision + stopping_power_radiative;
// Loop over photon energies // Loop over photon energies
xt::xtensor<double, 1> f({n_e}, 0.0); auto f = tensor::zeros<double>({n_e});
xt::xtensor<double, 1> z({n_e}, 0.0); auto z = tensor::zeros<double>({n_e});
for (int i = 0; i < n_e - 1; ++i) { for (int i = 0; i < n_e - 1; ++i) {
double w = data::ttb_e_grid(i); double w = data::ttb_e_grid(i);
@ -797,7 +795,8 @@ void Material::init_bremsstrahlung()
} }
// Use logarithm of number yield since it is log-log interpolated // Use logarithm of number yield since it is log-log interpolated
ttb->yield = xt::where(ttb->yield > 0.0, xt::log(ttb->yield), -500.0); ttb->yield =
tensor::where(ttb->yield > 0.0, tensor::log(ttb->yield), -500.0);
} }
} }
@ -979,7 +978,7 @@ void Material::set_density(double density, const std::string& units)
density_ = density; density_ = density;
// Determine normalized atom percents // Determine normalized atom percents
double sum_percent = xt::sum(atom_density_)(); double sum_percent = atom_density_.sum();
atom_density_ /= sum_percent; atom_density_ /= sum_percent;
// Recalculate nuclide atom densities based on given density // Recalculate nuclide atom densities based on given density
@ -1020,7 +1019,7 @@ void Material::set_densities(
if (n != nuclide_.size()) { if (n != nuclide_.size()) {
nuclide_.resize(n); nuclide_.resize(n);
atom_density_ = xt::zeros<double>({n}); atom_density_ = tensor::zeros<double>({n});
if (settings::photon_transport) if (settings::photon_transport)
element_.resize(n); element_.resize(n);
} }
@ -1181,8 +1180,8 @@ void Material::add_nuclide(const std::string& name, double density)
auto n = nuclide_.size(); auto n = nuclide_.size();
// Create copy of atom_density_ array with one extra entry // Create copy of atom_density_ array with one extra entry
xt::xtensor<double, 1> atom_density = xt::zeros<double>({n}); tensor::Tensor<double> atom_density = tensor::zeros<double>({n});
xt::view(atom_density, xt::range(0, n - 1)) = atom_density_; atom_density.slice(tensor::range(0, n - 1)) = atom_density_;
atom_density(n - 1) = density; atom_density(n - 1) = density;
atom_density_ = atom_density; atom_density_ = atom_density;

View file

@ -6,6 +6,7 @@
#define _USE_MATH_DEFINES // to make M_PI declared in Intel and MSVC compilers #define _USE_MATH_DEFINES // to make M_PI declared in Intel and MSVC compilers
#include <cmath> // for ceil #include <cmath> // for ceil
#include <cstddef> // for size_t #include <cstddef> // for size_t
#include <numeric> // for accumulate
#include <string> #include <string>
#ifdef _MSC_VER #ifdef _MSC_VER
@ -16,13 +17,7 @@
#include "mpi.h" #include "mpi.h"
#endif #endif
#include "xtensor/xadapt.hpp" #include "openmc/tensor.h"
#include "xtensor/xbuilder.hpp"
#include "xtensor/xeval.hpp"
#include "xtensor/xmath.hpp"
#include "xtensor/xsort.hpp"
#include "xtensor/xtensor.hpp"
#include "xtensor/xview.hpp"
#include <fmt/core.h> // for fmt #include <fmt/core.h> // for fmt
#include "openmc/capi.h" #include "openmc/capi.h"
@ -772,11 +767,9 @@ std::string StructuredMesh::bin_label(int bin) const
} }
} }
xt::xtensor<int, 1> StructuredMesh::get_x_shape() const tensor::Tensor<int> StructuredMesh::get_shape_tensor() const
{ {
// because method is const, shape_ is const as well and can't be adapted return tensor::Tensor<int>(shape_.data(), static_cast<size_t>(n_dimension_));
auto tmp_shape = shape_;
return xt::adapt(tmp_shape, {n_dimension_});
} }
Position StructuredMesh::sample_element( Position StructuredMesh::sample_element(
@ -961,10 +954,11 @@ void UnstructuredMesh::to_hdf5_inner(hid_t mesh_group) const
write_dataset(mesh_group, "length_multiplier", length_multiplier_); write_dataset(mesh_group, "length_multiplier", length_multiplier_);
// write vertex coordinates // write vertex coordinates
xt::xtensor<double, 2> vertices({static_cast<size_t>(this->n_vertices()), 3}); tensor::Tensor<double> vertices(
{static_cast<size_t>(this->n_vertices()), static_cast<size_t>(3)});
for (int i = 0; i < this->n_vertices(); i++) { for (int i = 0; i < this->n_vertices(); i++) {
auto v = this->vertex(i); auto v = this->vertex(i);
xt::view(vertices, i, xt::all()) = xt::xarray<double>({v.x, v.y, v.z}); vertices.slice(i) = {v.x, v.y, v.z};
} }
write_dataset(mesh_group, "vertices", vertices); write_dataset(mesh_group, "vertices", vertices);
@ -972,8 +966,10 @@ void UnstructuredMesh::to_hdf5_inner(hid_t mesh_group) const
// write element types and connectivity // write element types and connectivity
vector<double> volumes; vector<double> volumes;
xt::xtensor<int, 2> connectivity({static_cast<size_t>(this->n_bins()), 8}); tensor::Tensor<int> connectivity(
xt::xtensor<int, 2> elem_types({static_cast<size_t>(this->n_bins()), 1}); {static_cast<size_t>(this->n_bins()), static_cast<size_t>(8)});
tensor::Tensor<int> elem_types(
{static_cast<size_t>(this->n_bins()), static_cast<size_t>(1)});
for (int i = 0; i < this->n_bins(); i++) { for (int i = 0; i < this->n_bins(); i++) {
auto conn = this->connectivity(i); auto conn = this->connectivity(i);
@ -981,21 +977,18 @@ void UnstructuredMesh::to_hdf5_inner(hid_t mesh_group) const
// write linear tet element // write linear tet element
if (conn.size() == 4) { if (conn.size() == 4) {
xt::view(elem_types, i, xt::all()) = elem_types.slice(i) = static_cast<int>(ElementType::LINEAR_TET);
static_cast<int>(ElementType::LINEAR_TET); connectivity.slice(i) = {
xt::view(connectivity, i, xt::all()) = conn[0], conn[1], conn[2], conn[3], -1, -1, -1, -1};
xt::xarray<int>({conn[0], conn[1], conn[2], conn[3], -1, -1, -1, -1});
// write linear hex element // write linear hex element
} else if (conn.size() == 8) { } else if (conn.size() == 8) {
xt::view(elem_types, i, xt::all()) = elem_types.slice(i) = static_cast<int>(ElementType::LINEAR_HEX);
static_cast<int>(ElementType::LINEAR_HEX); connectivity.slice(i) = {
xt::view(connectivity, i, xt::all()) = xt::xarray<int>({conn[0], conn[1], conn[0], conn[1], conn[2], conn[3], conn[4], conn[5], conn[6], conn[7]};
conn[2], conn[3], conn[4], conn[5], conn[6], conn[7]});
} else { } else {
num_elem_skipped++; num_elem_skipped++;
xt::view(elem_types, i, xt::all()) = elem_types.slice(i) = static_cast<int>(ElementType::UNSUPPORTED);
static_cast<int>(ElementType::UNSUPPORTED); connectivity.slice(i) = -1;
xt::view(connectivity, i, xt::all()) = -1;
} }
} }
@ -1096,7 +1089,7 @@ int StructuredMesh::n_surface_bins() const
return 4 * n_dimension_ * n_bins(); return 4 * n_dimension_ * n_bins();
} }
xt::xtensor<double, 1> StructuredMesh::count_sites( tensor::Tensor<double> StructuredMesh::count_sites(
const SourceSite* bank, int64_t length, bool* outside) const const SourceSite* bank, int64_t length, bool* outside) const
{ {
// Determine shape of array for counts // Determine shape of array for counts
@ -1104,7 +1097,7 @@ xt::xtensor<double, 1> StructuredMesh::count_sites(
vector<std::size_t> shape = {m}; vector<std::size_t> shape = {m};
// Create array of zeros // Create array of zeros
xt::xarray<double> cnt {shape, 0.0}; auto cnt = tensor::zeros<double>(shape);
bool outside_ = false; bool outside_ = false;
for (int64_t i = 0; i < length; i++) { for (int64_t i = 0; i < length; i++) {
@ -1123,31 +1116,25 @@ xt::xtensor<double, 1> StructuredMesh::count_sites(
cnt(mesh_bin) += site.wgt; cnt(mesh_bin) += site.wgt;
} }
// Create copy of count data. Since ownership will be acquired by xtensor, // Create reduced count data
// std::allocator must be used to avoid Valgrind mismatched free() / delete auto counts = tensor::zeros<double>(shape);
// warnings.
int total = cnt.size(); int total = cnt.size();
double* cnt_reduced = std::allocator<double> {}.allocate(total);
#ifdef OPENMC_MPI #ifdef OPENMC_MPI
// collect values from all processors // collect values from all processors
MPI_Reduce( MPI_Reduce(
cnt.data(), cnt_reduced, total, MPI_DOUBLE, MPI_SUM, 0, mpi::intracomm); cnt.data(), counts.data(), total, MPI_DOUBLE, MPI_SUM, 0, mpi::intracomm);
// Check if there were sites outside the mesh for any processor // Check if there were sites outside the mesh for any processor
if (outside) { if (outside) {
MPI_Reduce(&outside_, outside, 1, MPI_C_BOOL, MPI_LOR, 0, mpi::intracomm); MPI_Reduce(&outside_, outside, 1, MPI_C_BOOL, MPI_LOR, 0, mpi::intracomm);
} }
#else #else
std::copy(cnt.data(), cnt.data() + total, cnt_reduced); std::copy(cnt.data(), cnt.data() + total, counts.data());
if (outside) if (outside)
*outside = outside_; *outside = outside_;
#endif #endif
// Adapt reduced values in array back into an xarray
auto arr = xt::adapt(cnt_reduced, total, xt::acquire_ownership(), shape);
xt::xarray<double> counts = arr;
return counts; return counts;
} }
@ -1340,10 +1327,10 @@ void StructuredMesh::surface_bins_crossed(
int RegularMesh::set_grid() int RegularMesh::set_grid()
{ {
auto shape = xt::adapt(shape_, {n_dimension_}); tensor::Tensor<int> shape(shape_.data(), static_cast<size_t>(n_dimension_));
// Check that dimensions are all greater than zero // Check that dimensions are all greater than zero
if (xt::any(shape <= 0)) { if ((shape <= 0).any()) {
set_errmsg("All entries for a regular mesh dimensions " set_errmsg("All entries for a regular mesh dimensions "
"must be positive."); "must be positive.");
return OPENMC_E_INVALID_ARGUMENT; return OPENMC_E_INVALID_ARGUMENT;
@ -1365,13 +1352,13 @@ int RegularMesh::set_grid()
} }
// Check for negative widths // Check for negative widths
if (xt::any(width_ < 0.0)) { if ((width_ < 0.0).any()) {
set_errmsg("Cannot have a negative width on a regular mesh."); set_errmsg("Cannot have a negative width on a regular mesh.");
return OPENMC_E_INVALID_ARGUMENT; return OPENMC_E_INVALID_ARGUMENT;
} }
// Set width and upper right coordinate // Set width and upper right coordinate
upper_right_ = xt::eval(lower_left_ + shape * width_); upper_right_ = lower_left_ + shape * width_;
} else if (upper_right_.size() > 0) { } else if (upper_right_.size() > 0) {
@ -1383,7 +1370,7 @@ int RegularMesh::set_grid()
} }
// Check that upper-right is above lower-left // Check that upper-right is above lower-left
if (xt::any(upper_right_ < lower_left_)) { if ((upper_right_ < lower_left_).any()) {
set_errmsg( set_errmsg(
"The upper_right coordinates of a regular mesh must be greater than " "The upper_right coordinates of a regular mesh must be greater than "
"the lower_left coordinates."); "the lower_left coordinates.");
@ -1391,11 +1378,11 @@ int RegularMesh::set_grid()
} }
// Set width // Set width
width_ = xt::eval((upper_right_ - lower_left_) / shape); width_ = (upper_right_ - lower_left_) / shape;
} }
// Set material volumes // Set material volumes
volume_frac_ = 1.0 / xt::prod(shape)(); volume_frac_ = 1.0 / shape.prod();
element_volume_ = 1.0; element_volume_ = 1.0;
for (int i = 0; i < n_dimension_; i++) { for (int i = 0; i < n_dimension_; i++) {
@ -1411,7 +1398,7 @@ RegularMesh::RegularMesh(pugi::xml_node node) : StructuredMesh {node}
fatal_error("Must specify <dimension> on a regular mesh."); fatal_error("Must specify <dimension> on a regular mesh.");
} }
xt::xtensor<int, 1> shape = get_node_xarray<int>(node, "dimension"); tensor::Tensor<int> shape = get_node_tensor<int>(node, "dimension");
int n = n_dimension_ = shape.size(); int n = n_dimension_ = shape.size();
if (n != 1 && n != 2 && n != 3) { if (n != 1 && n != 2 && n != 3) {
fatal_error("Mesh must be one, two, or three dimensions."); fatal_error("Mesh must be one, two, or three dimensions.");
@ -1421,7 +1408,7 @@ RegularMesh::RegularMesh(pugi::xml_node node) : StructuredMesh {node}
// Check for lower-left coordinates // Check for lower-left coordinates
if (check_for_node(node, "lower_left")) { if (check_for_node(node, "lower_left")) {
// Read mesh lower-left corner location // Read mesh lower-left corner location
lower_left_ = get_node_xarray<double>(node, "lower_left"); lower_left_ = get_node_tensor<double>(node, "lower_left");
} else { } else {
fatal_error("Must specify <lower_left> on a mesh."); fatal_error("Must specify <lower_left> on a mesh.");
} }
@ -1432,11 +1419,11 @@ RegularMesh::RegularMesh(pugi::xml_node node) : StructuredMesh {node}
fatal_error("Cannot specify both <upper_right> and <width> on a mesh."); fatal_error("Cannot specify both <upper_right> and <width> on a mesh.");
} }
width_ = get_node_xarray<double>(node, "width"); width_ = get_node_tensor<double>(node, "width");
} else if (check_for_node(node, "upper_right")) { } else if (check_for_node(node, "upper_right")) {
upper_right_ = get_node_xarray<double>(node, "upper_right"); upper_right_ = get_node_tensor<double>(node, "upper_right");
} else { } else {
fatal_error("Must specify either <upper_right> or <width> on a mesh."); fatal_error("Must specify either <upper_right> or <width> on a mesh.");
@ -1454,7 +1441,7 @@ RegularMesh::RegularMesh(hid_t group) : StructuredMesh {group}
fatal_error("Must specify <dimension> on a regular mesh."); fatal_error("Must specify <dimension> on a regular mesh.");
} }
xt::xtensor<int, 1> shape; tensor::Tensor<int> shape;
read_dataset(group, "dimension", shape); read_dataset(group, "dimension", shape);
int n = n_dimension_ = shape.size(); int n = n_dimension_ = shape.size();
if (n != 1 && n != 2 && n != 3) { if (n != 1 && n != 2 && n != 3) {
@ -1569,13 +1556,13 @@ std::pair<vector<double>, vector<double>> RegularMesh::plot(
void RegularMesh::to_hdf5_inner(hid_t mesh_group) const void RegularMesh::to_hdf5_inner(hid_t mesh_group) const
{ {
write_dataset(mesh_group, "dimension", get_x_shape()); write_dataset(mesh_group, "dimension", get_shape_tensor());
write_dataset(mesh_group, "lower_left", lower_left_); write_dataset(mesh_group, "lower_left", lower_left_);
write_dataset(mesh_group, "upper_right", upper_right_); write_dataset(mesh_group, "upper_right", upper_right_);
write_dataset(mesh_group, "width", width_); write_dataset(mesh_group, "width", width_);
} }
xt::xtensor<double, 1> RegularMesh::count_sites( tensor::Tensor<double> RegularMesh::count_sites(
const SourceSite* bank, int64_t length, bool* outside) const const SourceSite* bank, int64_t length, bool* outside) const
{ {
// Determine shape of array for counts // Determine shape of array for counts
@ -1583,7 +1570,7 @@ xt::xtensor<double, 1> RegularMesh::count_sites(
vector<std::size_t> shape = {m}; vector<std::size_t> shape = {m};
// Create array of zeros // Create array of zeros
xt::xarray<double> cnt {shape, 0.0}; auto cnt = tensor::zeros<double>(shape);
bool outside_ = false; bool outside_ = false;
for (int64_t i = 0; i < length; i++) { for (int64_t i = 0; i < length; i++) {
@ -1602,31 +1589,25 @@ xt::xtensor<double, 1> RegularMesh::count_sites(
cnt(mesh_bin) += site.wgt; cnt(mesh_bin) += site.wgt;
} }
// Create copy of count data. Since ownership will be acquired by xtensor, // Create reduced count data
// std::allocator must be used to avoid Valgrind mismatched free() / delete auto counts = tensor::zeros<double>(shape);
// warnings.
int total = cnt.size(); int total = cnt.size();
double* cnt_reduced = std::allocator<double> {}.allocate(total);
#ifdef OPENMC_MPI #ifdef OPENMC_MPI
// collect values from all processors // collect values from all processors
MPI_Reduce( MPI_Reduce(
cnt.data(), cnt_reduced, total, MPI_DOUBLE, MPI_SUM, 0, mpi::intracomm); cnt.data(), counts.data(), total, MPI_DOUBLE, MPI_SUM, 0, mpi::intracomm);
// Check if there were sites outside the mesh for any processor // Check if there were sites outside the mesh for any processor
if (outside) { if (outside) {
MPI_Reduce(&outside_, outside, 1, MPI_C_BOOL, MPI_LOR, 0, mpi::intracomm); MPI_Reduce(&outside_, outside, 1, MPI_C_BOOL, MPI_LOR, 0, mpi::intracomm);
} }
#else #else
std::copy(cnt.data(), cnt.data() + total, cnt_reduced); std::copy(cnt.data(), cnt.data() + total, counts.data());
if (outside) if (outside)
*outside = outside_; *outside = outside_;
#endif #endif
// Adapt reduced values in array back into an xarray
auto arr = xt::adapt(cnt_reduced, total, xt::acquire_ownership(), shape);
xt::xarray<double> counts = arr;
return counts; return counts;
} }
@ -2698,7 +2679,7 @@ extern "C" int openmc_regular_mesh_get_params(
return err; return err;
RegularMesh* m = dynamic_cast<RegularMesh*>(model::meshes[index].get()); RegularMesh* m = dynamic_cast<RegularMesh*>(model::meshes[index].get());
if (m->lower_left_.dimension() == 0) { if (m->lower_left_.empty()) {
set_errmsg("Mesh parameters have not been set."); set_errmsg("Mesh parameters have not been set.");
return OPENMC_E_ALLOCATE; return OPENMC_E_ALLOCATE;
} }
@ -2725,17 +2706,17 @@ extern "C" int openmc_regular_mesh_set_params(
vector<std::size_t> shape = {static_cast<std::size_t>(n)}; vector<std::size_t> shape = {static_cast<std::size_t>(n)};
if (ll && ur) { if (ll && ur) {
m->lower_left_ = xt::adapt(ll, n, xt::no_ownership(), shape); m->lower_left_ = tensor::Tensor<double>(ll, n);
m->upper_right_ = xt::adapt(ur, n, xt::no_ownership(), shape); m->upper_right_ = tensor::Tensor<double>(ur, n);
m->width_ = (m->upper_right_ - m->lower_left_) / m->get_x_shape(); m->width_ = (m->upper_right_ - m->lower_left_) / m->get_shape_tensor();
} else if (ll && width) { } else if (ll && width) {
m->lower_left_ = xt::adapt(ll, n, xt::no_ownership(), shape); m->lower_left_ = tensor::Tensor<double>(ll, n);
m->width_ = xt::adapt(width, n, xt::no_ownership(), shape); m->width_ = tensor::Tensor<double>(width, n);
m->upper_right_ = m->lower_left_ + m->get_x_shape() * m->width_; m->upper_right_ = m->lower_left_ + m->get_shape_tensor() * m->width_;
} else if (ur && width) { } else if (ur && width) {
m->upper_right_ = xt::adapt(ur, n, xt::no_ownership(), shape); m->upper_right_ = tensor::Tensor<double>(ur, n);
m->width_ = xt::adapt(width, n, xt::no_ownership(), shape); m->width_ = tensor::Tensor<double>(width, n);
m->lower_left_ = m->upper_right_ - m->get_x_shape() * m->width_; m->lower_left_ = m->upper_right_ - m->get_shape_tensor() * m->width_;
} else { } else {
set_errmsg("At least two parameters must be specified."); set_errmsg("At least two parameters must be specified.");
return OPENMC_E_INVALID_ARGUMENT; return OPENMC_E_INVALID_ARGUMENT;
@ -2745,7 +2726,7 @@ extern "C" int openmc_regular_mesh_set_params(
// TODO: incorporate this into method in RegularMesh that can be called from // TODO: incorporate this into method in RegularMesh that can be called from
// here and from constructor // here and from constructor
m->volume_frac_ = 1.0 / xt::prod(m->get_x_shape())(); m->volume_frac_ = 1.0 / m->get_shape_tensor().prod();
m->element_volume_ = 1.0; m->element_volume_ = 1.0;
for (int i = 0; i < m->n_dimension_; i++) { for (int i = 0; i < m->n_dimension_; i++) {
m->element_volume_ *= m->width_[i]; m->element_volume_ *= m->width_[i];
@ -2794,7 +2775,7 @@ int openmc_structured_mesh_get_grid_impl(int32_t index, double** grid_x,
return err; return err;
C* m = dynamic_cast<C*>(model::meshes[index].get()); C* m = dynamic_cast<C*>(model::meshes[index].get());
if (m->lower_left_.dimension() == 0) { if (m->lower_left_.empty()) {
set_errmsg("Mesh parameters have not been set."); set_errmsg("Mesh parameters have not been set.");
return OPENMC_E_ALLOCATE; return OPENMC_E_ALLOCATE;
} }

View file

@ -5,10 +5,7 @@
#include <cstdlib> #include <cstdlib>
#include <sstream> #include <sstream>
#include "xtensor/xadapt.hpp" #include "openmc/tensor.h"
#include "xtensor/xmath.hpp"
#include "xtensor/xsort.hpp"
#include "xtensor/xview.hpp"
#include <fmt/core.h> #include <fmt/core.h>
#include "openmc/error.h" #include "openmc/error.h"
@ -33,8 +30,7 @@ void Mgxs::init(const std::string& in_name, double in_awr,
// Set the metadata // Set the metadata
name = in_name; name = in_name;
awr = in_awr; awr = in_awr;
// TODO: Remove adapt when in_KTs is an xtensor kTs = tensor::Tensor<double>(in_kTs.data(), in_kTs.size());
kTs = xt::adapt(in_kTs);
fissionable = in_fissionable; fissionable = in_fissionable;
scatter_format = in_scatter_format; scatter_format = in_scatter_format;
xs.resize(in_kTs.size()); xs.resize(in_kTs.size());
@ -73,7 +69,7 @@ void Mgxs::metadata_from_hdf5(hid_t xs_id, const vector<double>& temperature,
} }
get_datasets(kT_group, dset_names); get_datasets(kT_group, dset_names);
vector<size_t> shape = {num_temps}; vector<size_t> shape = {num_temps};
xt::xarray<double> temps_available(shape); tensor::Tensor<double> temps_available(shape);
for (int i = 0; i < num_temps; i++) { for (int i = 0; i < num_temps; i++) {
read_double(kT_group, dset_names[i], &temps_available[i], true); read_double(kT_group, dset_names[i], &temps_available[i], true);
@ -108,19 +104,7 @@ void Mgxs::metadata_from_hdf5(hid_t xs_id, const vector<double>& temperature,
// Determine actual temperatures to read // Determine actual temperatures to read
for (const auto& T : temperature) { for (const auto& T : temperature) {
// Determine the closest temperature value // Determine the closest temperature value
// NOTE: the below block could be replaced with the following line, auto i_closest = tensor::abs(temps_available - T).argmin();
// though this gives a runtime error if using LLVM 20 or newer,
// likely due to a bug in xtensor.
// auto i_closest = xt::argmin(xt::abs(temps_available - T))[0];
double closest = std::numeric_limits<double>::max();
int i_closest = 0;
for (int i = 0; i < temps_available.size(); i++) {
double diff = std::abs(temps_available[i] - T);
if (diff < closest) {
closest = diff;
i_closest = i;
}
}
double temp_actual = temps_available[i_closest]; double temp_actual = temps_available[i_closest];
if (std::fabs(temp_actual - T) < settings::temperature_tolerance) { if (std::fabs(temp_actual - T) < settings::temperature_tolerance) {
@ -355,7 +339,7 @@ Mgxs::Mgxs(const std::string& in_name, const vector<double>& mat_kTs,
for (int m = 0; m < micros.size(); m++) { for (int m = 0; m < micros.size(); m++) {
switch (settings::temperature_method) { switch (settings::temperature_method) {
case TemperatureMethod::NEAREST: { case TemperatureMethod::NEAREST: {
micro_t[m] = xt::argmin(xt::abs(micros[m]->kTs - temp_desired))[0]; micro_t[m] = tensor::abs(micros[m]->kTs - temp_desired).argmin();
auto temp_actual = micros[m]->kTs[micro_t[m]]; auto temp_actual = micros[m]->kTs[micro_t[m]];
if (std::abs(temp_actual - temp_desired) >= if (std::abs(temp_actual - temp_desired) >=
@ -368,7 +352,7 @@ Mgxs::Mgxs(const std::string& in_name, const vector<double>& mat_kTs,
case TemperatureMethod::INTERPOLATION: case TemperatureMethod::INTERPOLATION:
// Get a list of bounding temperatures for each actual temperature // Get a list of bounding temperatures for each actual temperature
// present in the model // present in the model
for (int k = 0; k < micros[m]->kTs.shape()[0] - 1; k++) { for (int k = 0; k < micros[m]->kTs.shape(0) - 1; k++) {
if ((micros[m]->kTs[k] <= temp_desired) && if ((micros[m]->kTs[k] <= temp_desired) &&
(temp_desired < micros[m]->kTs[k + 1])) { (temp_desired < micros[m]->kTs[k + 1])) {
micro_t[m] = k; micro_t[m] = k;
@ -474,7 +458,7 @@ double Mgxs::get_xs(MgxsType xstype, int gin, const int* gout, const double* mu,
val = xs_t->delayed_nu_fission(a, *dg, gin); val = xs_t->delayed_nu_fission(a, *dg, gin);
} else { } else {
val = 0.; val = 0.;
for (int d = 0; d < xs_t->delayed_nu_fission.shape()[1]; d++) { for (int d = 0; d < xs_t->delayed_nu_fission.shape(1); d++) {
val += xs_t->delayed_nu_fission(a, d, gin); val += xs_t->delayed_nu_fission(a, d, gin);
} }
} }
@ -489,7 +473,7 @@ double Mgxs::get_xs(MgxsType xstype, int gin, const int* gout, const double* mu,
} else { } else {
// provide an outgoing group-wise sum // provide an outgoing group-wise sum
val = 0.; val = 0.;
for (int g = 0; g < xs_t->chi_prompt.shape()[2]; g++) { for (int g = 0; g < xs_t->chi_prompt.shape(2); g++) {
val += xs_t->chi_prompt(a, gin, g); val += xs_t->chi_prompt(a, gin, g);
} }
} }
@ -508,13 +492,13 @@ double Mgxs::get_xs(MgxsType xstype, int gin, const int* gout, const double* mu,
} else { } else {
if (dg != nullptr) { if (dg != nullptr) {
val = 0.; val = 0.;
for (int g = 0; g < xs_t->delayed_nu_fission.shape()[2]; g++) { for (int g = 0; g < xs_t->delayed_nu_fission.shape(2); g++) {
val += xs_t->delayed_nu_fission(a, *dg, gin, g); val += xs_t->delayed_nu_fission(a, *dg, gin, g);
} }
} else { } else {
val = 0.; val = 0.;
for (int g = 0; g < xs_t->delayed_nu_fission.shape()[2]; g++) { 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++) { for (int d = 0; d < xs_t->delayed_nu_fission.shape(3); d++) {
val += xs_t->delayed_nu_fission(a, d, gin, g); val += xs_t->delayed_nu_fission(a, d, gin, g);
} }
} }
@ -650,7 +634,7 @@ bool Mgxs::equiv(const Mgxs& that)
int Mgxs::get_temperature_index(double sqrtkT) const int Mgxs::get_temperature_index(double sqrtkT) const
{ {
return xt::argmin(xt::abs(kTs - sqrtkT * sqrtkT))[0]; return tensor::abs(kTs - sqrtkT * sqrtkT).argmin();
} }
//============================================================================== //==============================================================================

View file

@ -17,8 +17,7 @@
#include <fmt/core.h> #include <fmt/core.h>
#include "xtensor/xbuilder.hpp" #include "openmc/tensor.h"
#include "xtensor/xview.hpp"
#include <algorithm> // for sort, min_element #include <algorithm> // for sort, min_element
#include <cassert> #include <cassert>
@ -361,8 +360,7 @@ void Nuclide::create_derived(
{ {
for (const auto& grid : grid_) { for (const auto& grid : grid_) {
// Allocate and initialize cross section // Allocate and initialize cross section
array<size_t, 2> shape {grid.energy.size(), 5}; xs_.push_back(tensor::zeros<double>({grid.energy.size(), 5}));
xs_.emplace_back(shape, 0.0);
} }
reaction_index_.fill(C_NONE); reaction_index_.fill(C_NONE);
@ -375,9 +373,8 @@ void Nuclide::create_derived(
for (int t = 0; t < kTs_.size(); ++t) { for (int t = 0; t < kTs_.size(); ++t) {
int j = rx->xs_[t].threshold; int j = rx->xs_[t].threshold;
int n = rx->xs_[t].value.size(); int n = rx->xs_[t].value.size();
auto xs = xt::adapt(rx->xs_[t].value); auto xs = tensor::Tensor<double>(
auto pprod = xt::view(xs_[t], xt::range(j, j + n), XS_PHOTON_PROD); rx->xs_[t].value.data(), rx->xs_[t].value.size());
for (const auto& p : rx->products_) { for (const auto& p : rx->products_) {
if (p.particle_.is_photon()) { if (p.particle_.is_photon()) {
for (int k = 0; k < n; ++k) { for (int k = 0; k < n; ++k) {
@ -396,7 +393,7 @@ void Nuclide::create_derived(
} }
} }
pprod[k] += f * xs[k] * (*p.yield_)(E); xs_[t](j + k, XS_PHOTON_PROD) += f * xs[k] * (*p.yield_)(E);
} }
} }
} }
@ -406,20 +403,17 @@ void Nuclide::create_derived(
continue; continue;
// Add contribution to total cross section // Add contribution to total cross section
auto total = xt::view(xs_[t], xt::range(j, j + n), XS_TOTAL); xs_[t].slice(tensor::range(j, j + n), XS_TOTAL) += xs;
total += xs;
// Add contribution to absorption cross section // Add contribution to absorption cross section
auto absorption = xt::view(xs_[t], xt::range(j, j + n), XS_ABSORPTION);
if (is_disappearance(rx->mt_)) { if (is_disappearance(rx->mt_)) {
absorption += xs; xs_[t].slice(tensor::range(j, j + n), XS_ABSORPTION) += xs;
} }
if (is_fission(rx->mt_)) { if (is_fission(rx->mt_)) {
fissionable_ = true; fissionable_ = true;
auto fission = xt::view(xs_[t], xt::range(j, j + n), XS_FISSION); xs_[t].slice(tensor::range(j, j + n), XS_FISSION) += xs;
fission += xs; xs_[t].slice(tensor::range(j, j + n), XS_ABSORPTION) += xs;
absorption += xs;
// Keep track of fission reactions // Keep track of fission reactions
if (t == 0) { if (t == 0) {
@ -510,7 +504,7 @@ void Nuclide::init_grid()
double spacing = std::log(E_max / E_min) / M; double spacing = std::log(E_max / E_min) / M;
// Create equally log-spaced energy grid // Create equally log-spaced energy grid
auto umesh = xt::linspace(0.0, M * spacing, M + 1); auto umesh = tensor::linspace(0.0, M * spacing, M + 1);
for (auto& grid : grid_) { for (auto& grid : grid_) {
// Resize array for storing grid indices // Resize array for storing grid indices

View file

@ -17,7 +17,7 @@
#ifdef _OPENMP #ifdef _OPENMP
#include <omp.h> #include <omp.h>
#endif #endif
#include "xtensor/xview.hpp" #include "openmc/tensor.h"
#include "openmc/capi.h" #include "openmc/capi.h"
#include "openmc/cell.h" #include "openmc/cell.h"

View file

@ -13,11 +13,7 @@
#include "openmc/search.h" #include "openmc/search.h"
#include "openmc/settings.h" #include "openmc/settings.h"
#include "xtensor/xbuilder.hpp" #include "openmc/tensor.h"
#include "xtensor/xmath.hpp"
#include "xtensor/xoperation.hpp"
#include "xtensor/xslice.hpp"
#include "xtensor/xview.hpp"
#include <cmath> #include <cmath>
#include <fmt/core.h> #include <fmt/core.h>
@ -33,7 +29,7 @@ constexpr int PhotonInteraction::MAX_STACK_SIZE;
namespace data { namespace data {
xt::xtensor<double, 1> compton_profile_pz; tensor::Tensor<double> compton_profile_pz;
std::unordered_map<std::string, int> element_map; std::unordered_map<std::string, int> element_map;
vector<unique_ptr<PhotonInteraction>> elements; vector<unique_ptr<PhotonInteraction>> elements;
@ -46,8 +42,6 @@ vector<unique_ptr<PhotonInteraction>> elements;
PhotonInteraction::PhotonInteraction(hid_t group) PhotonInteraction::PhotonInteraction(hid_t group)
{ {
using namespace xt::placeholders;
// Set index of element in global vector // Set index of element in global vector
index_ = data::elements.size(); index_ = data::elements.size();
@ -96,7 +90,7 @@ PhotonInteraction::PhotonInteraction(hid_t group)
read_dataset(rgroup, "xs", pair_production_electron_); read_dataset(rgroup, "xs", pair_production_electron_);
close_group(rgroup); close_group(rgroup);
} else { } else {
pair_production_electron_ = xt::zeros_like(energy_); pair_production_electron_ = tensor::zeros_like(energy_);
} }
// Read pair production // Read pair production
@ -105,7 +99,7 @@ PhotonInteraction::PhotonInteraction(hid_t group)
read_dataset(rgroup, "xs", pair_production_nuclear_); read_dataset(rgroup, "xs", pair_production_nuclear_);
close_group(rgroup); close_group(rgroup);
} else { } else {
pair_production_nuclear_ = xt::zeros_like(energy_); pair_production_nuclear_ = tensor::zeros_like(energy_);
} }
// Read photoelectric // Read photoelectric
@ -119,7 +113,7 @@ PhotonInteraction::PhotonInteraction(hid_t group)
read_dataset(rgroup, "xs", heating_); read_dataset(rgroup, "xs", heating_);
close_group(rgroup); close_group(rgroup);
} else { } else {
heating_ = xt::zeros_like(energy_); heating_ = tensor::zeros_like(energy_);
} }
// Read subshell photoionization cross section and atomic relaxation data // Read subshell photoionization cross section and atomic relaxation data
@ -133,7 +127,7 @@ PhotonInteraction::PhotonInteraction(hid_t group)
} }
shells_.resize(n_shell); shells_.resize(n_shell);
cross_sections_ = xt::zeros<double>({energy_.size(), n_shell}); cross_sections_ = tensor::zeros<double>({energy_.size(), n_shell});
// Create mapping from designator to index // Create mapping from designator to index
std::unordered_map<int, int> shell_map; std::unordered_map<int, int> shell_map;
@ -168,15 +162,17 @@ PhotonInteraction::PhotonInteraction(hid_t group)
} }
// Read subshell cross section // Read subshell cross section
xt::xtensor<double, 1> xs; tensor::Tensor<double> xs;
dset = open_dataset(tgroup, "xs"); dset = open_dataset(tgroup, "xs");
read_attribute(dset, "threshold_idx", shell.threshold); read_attribute(dset, "threshold_idx", shell.threshold);
close_dataset(dset); close_dataset(dset);
read_dataset(tgroup, "xs", xs); read_dataset(tgroup, "xs", xs);
auto cross_section = auto cross_section =
xt::view(cross_sections_, xt::range(shell.threshold, _), i); cross_sections_.slice(tensor::range(static_cast<size_t>(shell.threshold),
cross_section = xt::where(xs > 0, xt::log(xs), 0); cross_sections_.shape(0)),
i);
cross_section = tensor::where(xs > 0, tensor::log(xs), 0);
if (object_exists(tgroup, "transitions")) { if (object_exists(tgroup, "transitions")) {
// Determine dimensions of transitions // Determine dimensions of transitions
@ -186,11 +182,12 @@ PhotonInteraction::PhotonInteraction(hid_t group)
int n_transition = dims[0]; int n_transition = dims[0];
if (n_transition > 0) { if (n_transition > 0) {
xt::xtensor<double, 2> matrix; tensor::Tensor<double> matrix;
read_dataset(tgroup, "transitions", matrix); read_dataset(tgroup, "transitions", matrix);
// Transition probability normalization // Transition probability normalization
double norm = xt::sum(xt::col(matrix, 3))(); double norm =
tensor::Tensor<double>(matrix.slice(tensor::all, 3)).sum();
shell.transitions.resize(n_transition); shell.transitions.resize(n_transition);
for (int j = 0; j < n_transition; ++j) { for (int j = 0; j < n_transition; ++j) {
@ -220,7 +217,7 @@ PhotonInteraction::PhotonInteraction(hid_t group)
// Read electron shell PDF and binding energies // Read electron shell PDF and binding energies
read_dataset(rgroup, "num_electrons", electron_pdf_); read_dataset(rgroup, "num_electrons", electron_pdf_);
electron_pdf_ /= xt::sum(electron_pdf_); electron_pdf_ /= electron_pdf_.sum();
read_dataset(rgroup, "binding_energy", binding_energy_); read_dataset(rgroup, "binding_energy", binding_energy_);
// Read Compton profiles // Read Compton profiles
@ -238,7 +235,7 @@ PhotonInteraction::PhotonInteraction(hid_t group)
auto is_close = [](double a, double b) { auto is_close = [](double a, double b) {
return std::abs(a - b) / a < FP_REL_PRECISION; return std::abs(a - b) / a < FP_REL_PRECISION;
}; };
subshell_map_ = xt::full_like(binding_energy_, -1); subshell_map_ = tensor::Tensor<int>(binding_energy_.shape(), -1);
for (int i = 0; i < binding_energy_.size(); ++i) { for (int i = 0; i < binding_energy_.size(); ++i) {
double E_b = binding_energy_[i]; double E_b = binding_energy_[i];
if (i < n_shell && is_close(E_b, shells_[i].binding_energy)) { if (i < n_shell && is_close(E_b, shells_[i].binding_energy)) {
@ -257,7 +254,7 @@ PhotonInteraction::PhotonInteraction(hid_t group)
// Create Compton profile CDF // Create Compton profile CDF
auto n_profile = data::compton_profile_pz.size(); auto n_profile = data::compton_profile_pz.size();
auto n_shell_compton = profile_pdf_.shape(0); auto n_shell_compton = profile_pdf_.shape(0);
profile_cdf_ = xt::empty<double>({n_shell_compton, n_profile}); profile_cdf_ = tensor::Tensor<double>({n_shell_compton, n_profile});
for (int i = 0; i < n_shell_compton; ++i) { for (int i = 0; i < n_shell_compton; ++i) {
double c = 0.0; double c = 0.0;
profile_cdf_(i, 0) = 0.0; profile_cdf_(i, 0) = 0.0;
@ -276,11 +273,11 @@ PhotonInteraction::PhotonInteraction(hid_t group)
// Read bremsstrahlung scaled DCS // Read bremsstrahlung scaled DCS
rgroup = open_group(group, "bremsstrahlung"); rgroup = open_group(group, "bremsstrahlung");
read_dataset(rgroup, "dcs", dcs_); read_dataset(rgroup, "dcs", dcs_);
auto n_e = dcs_.shape()[0]; auto n_e = dcs_.shape(0);
auto n_k = dcs_.shape()[1]; auto n_k = dcs_.shape(1);
// Get energy grids used for bremsstrahlung DCS and for stopping powers // Get energy grids used for bremsstrahlung DCS and for stopping powers
xt::xtensor<double, 1> electron_energy; tensor::Tensor<double> electron_energy;
read_dataset(rgroup, "electron_energy", electron_energy); read_dataset(rgroup, "electron_energy", electron_energy);
if (data::ttb_k_grid.size() == 0) { if (data::ttb_k_grid.size() == 0) {
read_dataset(rgroup, "photon_energy", data::ttb_k_grid); read_dataset(rgroup, "photon_energy", data::ttb_k_grid);
@ -305,12 +302,12 @@ PhotonInteraction::PhotonInteraction(hid_t group)
(std::log(E(i_grid + 1)) - std::log(E(i_grid))); (std::log(E(i_grid + 1)) - std::log(E(i_grid)));
// Interpolate bremsstrahlung DCS at the cutoff energy and truncate // Interpolate bremsstrahlung DCS at the cutoff energy and truncate
xt::xtensor<double, 2> dcs({n_e - i_grid, n_k}); tensor::Tensor<double> dcs({n_e - i_grid, n_k});
for (int i = 0; i < n_k; ++i) { for (int i = 0; i < n_k; ++i) {
double y = std::exp( double y = std::exp(
std::log(dcs_(i_grid, i)) + std::log(dcs_(i_grid, i)) +
f * (std::log(dcs_(i_grid + 1, i)) - std::log(dcs_(i_grid, i)))); f * (std::log(dcs_(i_grid + 1, i)) - std::log(dcs_(i_grid, i))));
auto col_i = xt::view(dcs, xt::all(), i); tensor::View<double> col_i = dcs.slice(tensor::all, i);
col_i(0) = y; col_i(0) = y;
for (int j = i_grid + 1; j < n_e; ++j) { for (int j = i_grid + 1; j < n_e; ++j) {
col_i(j - i_grid) = dcs_(j, i); col_i(j - i_grid) = dcs_(j, i);
@ -318,9 +315,11 @@ PhotonInteraction::PhotonInteraction(hid_t group)
} }
dcs_ = dcs; dcs_ = dcs;
xt::xtensor<double, 1> frst {cutoff}; tensor::Tensor<double> frst({static_cast<size_t>(1)});
electron_energy = xt::concatenate(xt::xtuple( frst(0) = cutoff;
frst, xt::view(electron_energy, xt::range(i_grid + 1, n_e)))); tensor::Tensor<double> rest(electron_energy.slice(
tensor::range(i_grid + 1, electron_energy.size())));
electron_energy = tensor::concatenate(frst, rest);
} }
// Set incident particle energy grid // Set incident particle energy grid
@ -329,7 +328,8 @@ PhotonInteraction::PhotonInteraction(hid_t group)
} }
// Calculate the radiative stopping power // Calculate the radiative stopping power
stopping_power_radiative_ = xt::empty<double>({data::ttb_e_grid.size()}); stopping_power_radiative_ =
tensor::Tensor<double>({data::ttb_e_grid.size()});
for (int i = 0; i < data::ttb_e_grid.size(); ++i) { for (int i = 0; i < data::ttb_e_grid.size(); ++i) {
// Integrate over reduced photon energy // Integrate over reduced photon energy
double c = 0.0; double c = 0.0;
@ -354,14 +354,15 @@ PhotonInteraction::PhotonInteraction(hid_t group)
// values below exp(-499) we store the log as -900, for which exp(-900) // values below exp(-499) we store the log as -900, for which exp(-900)
// evaluates to zero. // evaluates to zero.
double limit = std::exp(-499.0); double limit = std::exp(-499.0);
energy_ = xt::log(energy_); energy_ = tensor::log(energy_);
coherent_ = xt::where(coherent_ > limit, xt::log(coherent_), -900.0); coherent_ = tensor::where(coherent_ > limit, tensor::log(coherent_), -900.0);
incoherent_ = xt::where(incoherent_ > limit, xt::log(incoherent_), -900.0); incoherent_ =
photoelectric_total_ = xt::where( tensor::where(incoherent_ > limit, tensor::log(incoherent_), -900.0);
photoelectric_total_ > limit, xt::log(photoelectric_total_), -900.0); photoelectric_total_ = tensor::where(
pair_production_total_ = xt::where( photoelectric_total_ > limit, tensor::log(photoelectric_total_), -900.0);
pair_production_total_ > limit, xt::log(pair_production_total_), -900.0); pair_production_total_ = tensor::where(pair_production_total_ > limit,
heating_ = xt::where(heating_ > limit, xt::log(heating_), -900.0); tensor::log(pair_production_total_), -900.0);
heating_ = tensor::where(heating_ > limit, tensor::log(heating_), -900.0);
} }
PhotonInteraction::~PhotonInteraction() PhotonInteraction::~PhotonInteraction()
@ -512,7 +513,7 @@ void PhotonInteraction::compton_doppler(
c = prn(seed) * c_max; c = prn(seed) * c_max;
// Determine pz corresponding to sampled cdf value // Determine pz corresponding to sampled cdf value
auto cdf_shell = xt::view(profile_cdf_, shell, xt::all()); tensor::View<const double> cdf_shell = profile_cdf_.slice(shell);
int i = lower_bound_index(cdf_shell.cbegin(), cdf_shell.cend(), c); int i = lower_bound_index(cdf_shell.cbegin(), cdf_shell.cend(), c);
double pz_l = data::compton_profile_pz(i); double pz_l = data::compton_profile_pz(i);
double pz_r = data::compton_profile_pz(i + 1); double pz_r = data::compton_profile_pz(i + 1);
@ -608,8 +609,8 @@ void PhotonInteraction::calculate_xs(Particle& p) const
// Calculate microscopic photoelectric cross section // Calculate microscopic photoelectric cross section
xs.photoelectric = 0.0; xs.photoelectric = 0.0;
const auto& xs_lower = xt::row(cross_sections_, i_grid); tensor::View<const double> xs_lower = cross_sections_.slice(i_grid);
const auto& xs_upper = xt::row(cross_sections_, i_grid + 1); tensor::View<const double> xs_upper = cross_sections_.slice(i_grid + 1);
for (int i = 0; i < xs_upper.size(); ++i) for (int i = 0; i < xs_upper.size(); ++i)
if (xs_lower(i) != 0) if (xs_lower(i) != 0)

View file

@ -30,9 +30,9 @@
#include <fmt/core.h> #include <fmt/core.h>
#include "openmc/tensor.h"
#include <algorithm> // for max, min, max_element #include <algorithm> // for max, min, max_element
#include <cmath> // for sqrt, exp, log, abs, copysign #include <cmath> // for sqrt, exp, log, abs, copysign
#include <xtensor/xview.hpp>
namespace openmc { namespace openmc {
@ -375,8 +375,9 @@ void sample_photon_reaction(Particle& p)
// cross sections // cross sections
int i_grid = micro.index_grid; int i_grid = micro.index_grid;
double f = micro.interp_factor; double f = micro.interp_factor;
const auto& xs_lower = xt::row(element.cross_sections_, i_grid); tensor::View<const double> xs_lower = element.cross_sections_.slice(i_grid);
const auto& xs_upper = xt::row(element.cross_sections_, i_grid + 1); tensor::View<const double> xs_upper =
element.cross_sections_.slice(i_grid + 1);
for (int i_shell = 0; i_shell < element.shells_.size(); ++i_shell) { for (int i_shell = 0; i_shell < element.shells_.size(); ++i_shell) {
const auto& shell {element.shells_[i_shell]}; const auto& shell {element.shells_[i_shell]};

View file

@ -2,7 +2,7 @@
#include <stdexcept> #include <stdexcept>
#include "xtensor/xarray.hpp" #include "openmc/tensor.h"
#include <fmt/core.h> #include <fmt/core.h>
#include "openmc/bank.h" #include "openmc/bank.h"

View file

@ -7,8 +7,7 @@
#include <fstream> #include <fstream>
#include <sstream> #include <sstream>
#include "xtensor/xmanipulation.hpp" #include "openmc/tensor.h"
#include "xtensor/xview.hpp"
#include <fmt/core.h> #include <fmt/core.h>
#include <fmt/ostream.h> #include <fmt/ostream.h>
#ifdef USE_LIBPNG #ifdef USE_LIBPNG
@ -74,7 +73,8 @@ void IdData::set_value(size_t y, size_t x, const GeometryState& p, int level)
void IdData::set_overlap(size_t y, size_t x) void IdData::set_overlap(size_t y, size_t x)
{ {
xt::view(data_, y, x, xt::all()) = OVERLAP; for (size_t k = 0; k < data_.shape(2); ++k)
data_(y, x, k) = OVERLAP;
} }
PropertyData::PropertyData(size_t h_res, size_t v_res) PropertyData::PropertyData(size_t h_res, size_t v_res)
@ -783,14 +783,14 @@ void output_ppm(const std::string& filename, const ImageData& data)
// Write header // Write header
of << "P6\n"; of << "P6\n";
of << data.shape()[0] << " " << data.shape()[1] << "\n"; of << data.shape(0) << " " << data.shape(1) << "\n";
of << "255\n"; of << "255\n";
of.close(); of.close();
of.open(fname, std::ios::binary | std::ios::app); of.open(fname, std::ios::binary | std::ios::app);
// Write color for each pixel // Write color for each pixel
for (int y = 0; y < data.shape()[1]; y++) { for (int y = 0; y < data.shape(1); y++) {
for (int x = 0; x < data.shape()[0]; x++) { for (int x = 0; x < data.shape(0); x++) {
RGBColor rgb = data(x, y); RGBColor rgb = data(x, y);
of << rgb.red << rgb.green << rgb.blue; of << rgb.red << rgb.green << rgb.blue;
} }
@ -822,8 +822,8 @@ void output_png(const std::string& filename, const ImageData& data)
png_init_io(png_ptr, fp); png_init_io(png_ptr, fp);
// Write header (8 bit colour depth) // Write header (8 bit colour depth)
int width = data.shape()[0]; int width = data.shape(0);
int height = data.shape()[1]; int height = data.shape(1);
png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB, png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
png_write_info(png_ptr, info_ptr); png_write_info(png_ptr, info_ptr);
@ -1024,9 +1024,14 @@ void Plot::create_voxel() const
// select only cell/material ID data and flip the y-axis // select only cell/material ID data and flip the y-axis
int idx = color_by_ == PlotColorBy::cells ? 0 : 2; int idx = color_by_ == PlotColorBy::cells ? 0 : 2;
xt::xtensor<int32_t, 2> data_slice = // Extract 2D slice at index idx from 3D data
xt::view(ids.data_, xt::all(), xt::all(), idx); size_t rows = ids.data_.shape(0);
xt::xtensor<int32_t, 2> data_flipped = xt::flip(data_slice, 0); size_t cols = ids.data_.shape(1);
tensor::Tensor<int32_t> data_slice({rows, cols});
for (size_t r = 0; r < rows; ++r)
for (size_t c = 0; c < cols; ++c)
data_slice(r, c) = ids.data_(r, c, idx);
tensor::Tensor<int32_t> data_flipped = data_slice.flip(0);
// Write to HDF5 dataset // Write to HDF5 dataset
voxel_write_slice(z, dspace, dset, memspace, data_flipped.data()); voxel_write_slice(z, dspace, dset, memspace, data_flipped.data());
@ -1272,7 +1277,8 @@ ImageData WireframeRayTracePlot::create_image() const
// This array marks where the initial wireframe was drawn. We convolve it with // This array marks where the initial wireframe was drawn. We convolve it with
// a filter that gets adjusted with the wireframe thickness in order to // a filter that gets adjusted with the wireframe thickness in order to
// thicken the lines. // thicken the lines.
xt::xtensor<int, 2> wireframe_initial({width, height}, 0); tensor::Tensor<int> wireframe_initial(
{static_cast<size_t>(width), static_cast<size_t>(height)}, 0);
/* Holds all of the track segments for the current rendered line of pixels. /* Holds all of the track segments for the current rendered line of pixels.
* old_segments holds a copy of this_line_segments from the previous line. * old_segments holds a copy of this_line_segments from the previous line.

View file

@ -18,6 +18,7 @@
#include "openmc/weight_windows.h" #include "openmc/weight_windows.h"
#include <cstdio> #include <cstdio>
#include <numeric>
namespace openmc { namespace openmc {
@ -63,8 +64,7 @@ FlatSourceDomain::FlatSourceDomain() : negroups_(data::mg.num_energy_groups_)
// Create a new 2D tensor with the same size as the first // Create a new 2D tensor with the same size as the first
// two dimensions of the 3D tensor // two dimensions of the 3D tensor
tally_volumes_[i] = tally_volumes_[i] = tensor::Tensor<double>({shape[0], shape[1]});
xt::xtensor<double, 2>::from_shape({shape[0], shape[1]});
} }
} }

View file

@ -10,6 +10,8 @@
#include "openmc/settings.h" #include "openmc/settings.h"
#include "openmc/simulation.h" #include "openmc/simulation.h"
#include <numeric>
#include "openmc/distribution_spatial.h" #include "openmc/distribution_spatial.h"
#include "openmc/random_dist.h" #include "openmc/random_dist.h"
#include "openmc/source.h" #include "openmc/source.h"

View file

@ -4,8 +4,7 @@
#include <cmath> #include <cmath>
#include <numeric> #include <numeric>
#include "xtensor/xbuilder.hpp" #include "openmc/tensor.h"
#include "xtensor/xview.hpp"
#include "openmc/constants.h" #include "openmc/constants.h"
#include "openmc/error.h" #include "openmc/error.h"
@ -19,8 +18,8 @@ namespace openmc {
// ScattData base-class methods // ScattData base-class methods
//============================================================================== //==============================================================================
void ScattData::base_init(int order, const xt::xtensor<int, 1>& in_gmin, void ScattData::base_init(int order, const tensor::Tensor<int>& in_gmin,
const xt::xtensor<int, 1>& in_gmax, const double_2dvec& in_energy, const tensor::Tensor<int>& in_gmax, const double_2dvec& in_energy,
const double_2dvec& in_mult) const double_2dvec& in_mult)
{ {
size_t groups = in_energy.size(); size_t groups = in_energy.size();
@ -63,23 +62,26 @@ void ScattData::base_init(int order, const xt::xtensor<int, 1>& in_gmin,
void ScattData::base_combine(size_t max_order, size_t order_dim, void ScattData::base_combine(size_t max_order, size_t order_dim,
const vector<ScattData*>& those_scatts, const vector<double>& scalars, const vector<ScattData*>& those_scatts, const vector<double>& scalars,
xt::xtensor<int, 1>& in_gmin, xt::xtensor<int, 1>& in_gmax, tensor::Tensor<int>& in_gmin, tensor::Tensor<int>& in_gmax,
double_2dvec& sparse_mult, double_3dvec& sparse_scatter) double_2dvec& sparse_mult, double_3dvec& sparse_scatter)
{ {
size_t groups = those_scatts[0]->energy.size(); size_t groups = those_scatts[0]->energy.size();
// Now allocate and zero our storage spaces // Now allocate and zero our storage spaces
xt::xtensor<double, 3> this_nuscatt_matrix({groups, groups, order_dim}, 0.); tensor::Tensor<double> this_nuscatt_matrix =
xt::xtensor<double, 2> this_nuscatt_P0({groups, groups}, 0.); tensor::zeros<double>({groups, groups, order_dim});
xt::xtensor<double, 2> this_scatt_P0({groups, groups}, 0.); tensor::Tensor<double> this_nuscatt_P0 =
xt::xtensor<double, 2> this_mult({groups, groups}, 1.); tensor::zeros<double>({groups, groups});
tensor::Tensor<double> this_scatt_P0 =
tensor::zeros<double>({groups, groups});
tensor::Tensor<double> this_mult = tensor::ones<double>({groups, groups});
// Build the dense scattering and multiplicity matrices // Build the dense scattering and multiplicity matrices
for (int i = 0; i < those_scatts.size(); i++) { for (int i = 0; i < those_scatts.size(); i++) {
ScattData* that = those_scatts[i]; ScattData* that = those_scatts[i];
// Build the dense matrix for that object // Build the dense matrix for that object
xt::xtensor<double, 3> that_matrix = that->get_matrix(max_order); tensor::Tensor<double> that_matrix = that->get_matrix(max_order);
// Now add that to this for the nu-scatter matrix // Now add that to this for the nu-scatter matrix
this_nuscatt_matrix += scalars[i] * that_matrix; this_nuscatt_matrix += scalars[i] * that_matrix;
@ -97,7 +99,7 @@ void ScattData::base_combine(size_t max_order, size_t order_dim,
// Now we have the dense nuscatt and scatt, we can easily compute the // Now we have the dense nuscatt and scatt, we can easily compute the
// multiplicity matrix by dividing the two and fixing any nans // multiplicity matrix by dividing the two and fixing any nans
this_mult = xt::nan_to_num(this_nuscatt_P0 / this_scatt_P0); this_mult = tensor::nan_to_num(this_nuscatt_P0 / this_scatt_P0);
// We have the data, now we need to convert to a jagged array and then use // 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. // the initialize function to store it on the object.
@ -106,7 +108,7 @@ void ScattData::base_combine(size_t max_order, size_t order_dim,
int gmin_; int gmin_;
for (gmin_ = 0; gmin_ < groups; gmin_++) { for (gmin_ = 0; gmin_ < groups; gmin_++) {
bool non_zero = false; bool non_zero = false;
for (int l = 0; l < this_nuscatt_matrix.shape()[2]; l++) { for (int l = 0; l < this_nuscatt_matrix.shape(2); l++) {
if (this_nuscatt_matrix(gin, gmin_, l) != 0.) { if (this_nuscatt_matrix(gin, gmin_, l) != 0.) {
non_zero = true; non_zero = true;
break; break;
@ -118,7 +120,7 @@ void ScattData::base_combine(size_t max_order, size_t order_dim,
int gmax_; int gmax_;
for (gmax_ = groups - 1; gmax_ >= 0; gmax_--) { for (gmax_ = groups - 1; gmax_ >= 0; gmax_--) {
bool non_zero = false; bool non_zero = false;
for (int l = 0; l < this_nuscatt_matrix.shape()[2]; l++) { for (int l = 0; l < this_nuscatt_matrix.shape(2); l++) {
if (this_nuscatt_matrix(gin, gmax_, l) != 0.) { if (this_nuscatt_matrix(gin, gmax_, l) != 0.) {
non_zero = true; non_zero = true;
break; break;
@ -143,8 +145,8 @@ void ScattData::base_combine(size_t max_order, size_t order_dim,
sparse_mult[gin].resize(gmax_ - gmin_ + 1); sparse_mult[gin].resize(gmax_ - gmin_ + 1);
int i_gout = 0; int i_gout = 0;
for (int gout = gmin_; gout <= gmax_; gout++) { for (int gout = gmin_; gout <= gmax_; gout++) {
sparse_scatter[gin][i_gout].resize(this_nuscatt_matrix.shape()[2]); sparse_scatter[gin][i_gout].resize(this_nuscatt_matrix.shape(2));
for (int l = 0; l < this_nuscatt_matrix.shape()[2]; l++) { for (int l = 0; l < this_nuscatt_matrix.shape(2); l++) {
sparse_scatter[gin][i_gout][l] = this_nuscatt_matrix(gin, gout, l); sparse_scatter[gin][i_gout][l] = this_nuscatt_matrix(gin, gout, l);
} }
sparse_mult[gin][i_gout] = this_mult(gin, gout); sparse_mult[gin][i_gout] = this_mult(gin, gout);
@ -227,8 +229,8 @@ double ScattData::get_xs(
// ScattDataLegendre methods // ScattDataLegendre methods
//============================================================================== //==============================================================================
void ScattDataLegendre::init(const xt::xtensor<int, 1>& in_gmin, void ScattDataLegendre::init(const tensor::Tensor<int>& in_gmin,
const xt::xtensor<int, 1>& in_gmax, const double_2dvec& in_mult, const tensor::Tensor<int>& in_gmax, const double_2dvec& in_mult,
const double_3dvec& coeffs) const double_3dvec& coeffs)
{ {
size_t groups = coeffs.size(); size_t groups = coeffs.size();
@ -239,7 +241,7 @@ void ScattDataLegendre::init(const xt::xtensor<int, 1>& in_gmin,
// Get the scattering cross section value by summing the un-normalized P0 // Get the scattering cross section value by summing the un-normalized P0
// coefficient in the variable matrix over all outgoing groups. // coefficient in the variable matrix over all outgoing groups.
scattxs = xt::zeros<double>({groups}); scattxs = tensor::zeros<double>({groups});
for (int gin = 0; gin < groups; gin++) { for (int gin = 0; gin < groups; gin++) {
int num_groups = in_gmax[gin] - in_gmin[gin] + 1; int num_groups = in_gmax[gin] - in_gmin[gin] + 1;
for (int i_gout = 0; i_gout < num_groups; i_gout++) { for (int i_gout = 0; i_gout < num_groups; i_gout++) {
@ -386,8 +388,8 @@ void ScattDataLegendre::combine(
size_t groups = those_scatts[0]->energy.size(); size_t groups = those_scatts[0]->energy.size();
xt::xtensor<int, 1> in_gmin({groups}, 0); tensor::Tensor<int> in_gmin({groups}, 0);
xt::xtensor<int, 1> in_gmax({groups}, 0); tensor::Tensor<int> in_gmax({groups}, 0);
double_3dvec sparse_scatter(groups); double_3dvec sparse_scatter(groups);
double_2dvec sparse_mult(groups); double_2dvec sparse_mult(groups);
@ -404,12 +406,13 @@ void ScattDataLegendre::combine(
//============================================================================== //==============================================================================
xt::xtensor<double, 3> ScattDataLegendre::get_matrix(size_t max_order) tensor::Tensor<double> ScattDataLegendre::get_matrix(size_t max_order)
{ {
// Get the sizes and initialize the data to 0 // Get the sizes and initialize the data to 0
size_t groups = energy.size(); size_t groups = energy.size();
size_t order_dim = max_order + 1; size_t order_dim = max_order + 1;
xt::xtensor<double, 3> matrix({groups, groups, order_dim}, 0.); tensor::Tensor<double> matrix =
tensor::zeros<double>({groups, groups, order_dim});
for (int gin = 0; gin < groups; gin++) { for (int gin = 0; gin < groups; gin++) {
for (int i_gout = 0; i_gout < energy[gin].size(); i_gout++) { for (int i_gout = 0; i_gout < energy[gin].size(); i_gout++) {
@ -427,8 +430,8 @@ xt::xtensor<double, 3> ScattDataLegendre::get_matrix(size_t max_order)
// ScattDataHistogram methods // ScattDataHistogram methods
//============================================================================== //==============================================================================
void ScattDataHistogram::init(const xt::xtensor<int, 1>& in_gmin, void ScattDataHistogram::init(const tensor::Tensor<int>& in_gmin,
const xt::xtensor<int, 1>& in_gmax, const double_2dvec& in_mult, const tensor::Tensor<int>& in_gmax, const double_2dvec& in_mult,
const double_3dvec& coeffs) const double_3dvec& coeffs)
{ {
size_t groups = coeffs.size(); size_t groups = coeffs.size();
@ -439,7 +442,7 @@ void ScattDataHistogram::init(const xt::xtensor<int, 1>& in_gmin,
// Get the scattering cross section value by summing the distribution // Get the scattering cross section value by summing the distribution
// over all the histogram bins in angle and outgoing energy groups // over all the histogram bins in angle and outgoing energy groups
scattxs = xt::zeros<double>({groups}); scattxs = tensor::zeros<double>({groups});
for (int gin = 0; gin < groups; gin++) { for (int gin = 0; gin < groups; gin++) {
for (int i_gout = 0; i_gout < matrix[gin].size(); i_gout++) { for (int i_gout = 0; i_gout < matrix[gin].size(); i_gout++) {
scattxs[gin] += std::accumulate( scattxs[gin] += std::accumulate(
@ -468,7 +471,7 @@ void ScattDataHistogram::init(const xt::xtensor<int, 1>& in_gmin,
ScattData::base_init(order, in_gmin, in_gmax, in_energy, in_mult); ScattData::base_init(order, in_gmin, in_gmax, in_energy, in_mult);
// Build the angular distribution mu values // Build the angular distribution mu values
mu = xt::linspace(-1., 1., order + 1); mu = tensor::linspace(-1., 1., order + 1);
dmu = 2. / order; dmu = 2. / order;
// Calculate f(mu) and integrate it so we can avoid rejection sampling // Calculate f(mu) and integrate it so we can avoid rejection sampling
@ -513,7 +516,7 @@ double ScattDataHistogram::calc_f(int gin, int gout, double mu)
int imu; int imu;
if (mu == 1.) { if (mu == 1.) {
// use size -2 to have the index one before the end // use size -2 to have the index one before the end
imu = this->mu.shape()[0] - 2; imu = this->mu.shape(0) - 2;
} else { } else {
imu = std::floor((mu + 1.) / dmu + 1.) - 1; imu = std::floor((mu + 1.) / dmu + 1.) - 1;
} }
@ -559,13 +562,13 @@ void ScattDataHistogram::sample(
//============================================================================== //==============================================================================
xt::xtensor<double, 3> ScattDataHistogram::get_matrix(size_t max_order) tensor::Tensor<double> ScattDataHistogram::get_matrix(size_t max_order)
{ {
// Get the sizes and initialize the data to 0 // Get the sizes and initialize the data to 0
size_t groups = energy.size(); size_t groups = energy.size();
// We ignore the requested order for Histogram and Tabular representations // We ignore the requested order for Histogram and Tabular representations
size_t order_dim = get_order(); size_t order_dim = get_order();
xt::xtensor<double, 3> matrix({groups, groups, order_dim}, 0); tensor::Tensor<double> matrix({groups, groups, order_dim}, 0);
for (int gin = 0; gin < groups; gin++) { for (int gin = 0; gin < groups; gin++) {
for (int i_gout = 0; i_gout < energy[gin].size(); i_gout++) { for (int i_gout = 0; i_gout < energy[gin].size(); i_gout++) {
@ -600,8 +603,8 @@ void ScattDataHistogram::combine(
size_t groups = those_scatts[0]->energy.size(); size_t groups = those_scatts[0]->energy.size();
xt::xtensor<int, 1> in_gmin({groups}, 0); tensor::Tensor<int> in_gmin({groups}, 0);
xt::xtensor<int, 1> in_gmax({groups}, 0); tensor::Tensor<int> in_gmax({groups}, 0);
double_3dvec sparse_scatter(groups); double_3dvec sparse_scatter(groups);
double_2dvec sparse_mult(groups); double_2dvec sparse_mult(groups);
@ -620,8 +623,8 @@ void ScattDataHistogram::combine(
// ScattDataTabular methods // ScattDataTabular methods
//============================================================================== //==============================================================================
void ScattDataTabular::init(const xt::xtensor<int, 1>& in_gmin, void ScattDataTabular::init(const tensor::Tensor<int>& in_gmin,
const xt::xtensor<int, 1>& in_gmax, const double_2dvec& in_mult, const tensor::Tensor<int>& in_gmax, const double_2dvec& in_mult,
const double_3dvec& coeffs) const double_3dvec& coeffs)
{ {
size_t groups = coeffs.size(); size_t groups = coeffs.size();
@ -631,12 +634,12 @@ void ScattDataTabular::init(const xt::xtensor<int, 1>& in_gmin,
double_3dvec matrix = coeffs; double_3dvec matrix = coeffs;
// Build the angular distribution mu values // Build the angular distribution mu values
mu = xt::linspace(-1., 1., order); mu = tensor::linspace(-1., 1., order);
dmu = 2. / (order - 1); dmu = 2. / (order - 1);
// Get the scattering cross section value by integrating the distribution // Get the scattering cross section value by integrating the distribution
// over all mu points and then combining over all outgoing groups // over all mu points and then combining over all outgoing groups
scattxs = xt::zeros<double>({groups}); scattxs = tensor::zeros<double>({groups});
for (int gin = 0; gin < groups; gin++) { for (int gin = 0; gin < groups; gin++) {
for (int i_gout = 0; i_gout < matrix[gin].size(); i_gout++) { for (int i_gout = 0; i_gout < matrix[gin].size(); i_gout++) {
for (int imu = 1; imu < order; imu++) { for (int imu = 1; imu < order; imu++) {
@ -713,7 +716,7 @@ double ScattDataTabular::calc_f(int gin, int gout, double mu)
int imu; int imu;
if (mu == 1.) { if (mu == 1.) {
// use size -2 to have the index one before the end // use size -2 to have the index one before the end
imu = this->mu.shape()[0] - 2; imu = this->mu.shape(0) - 2;
} else { } else {
imu = std::floor((mu + 1.) / dmu + 1.) - 1; imu = std::floor((mu + 1.) / dmu + 1.) - 1;
} }
@ -734,7 +737,7 @@ void ScattDataTabular::sample(
sample_energy(gin, gout, i_gout, seed); sample_energy(gin, gout, i_gout, seed);
// Determine the outgoing cosine bin // Determine the outgoing cosine bin
int NP = this->mu.shape()[0]; int NP = this->mu.shape(0);
double xi = prn(seed); double xi = prn(seed);
double c_k = dist[gin][i_gout][0]; double c_k = dist[gin][i_gout][0];
@ -776,13 +779,14 @@ void ScattDataTabular::sample(
//============================================================================== //==============================================================================
xt::xtensor<double, 3> ScattDataTabular::get_matrix(size_t max_order) tensor::Tensor<double> ScattDataTabular::get_matrix(size_t max_order)
{ {
// Get the sizes and initialize the data to 0 // Get the sizes and initialize the data to 0
size_t groups = energy.size(); size_t groups = energy.size();
// We ignore the requested order for Histogram and Tabular representations // We ignore the requested order for Histogram and Tabular representations
size_t order_dim = get_order(); size_t order_dim = get_order();
xt::xtensor<double, 3> matrix({groups, groups, order_dim}, 0.); tensor::Tensor<double> matrix =
tensor::zeros<double>({groups, groups, order_dim});
for (int gin = 0; gin < groups; gin++) { for (int gin = 0; gin < groups; gin++) {
for (int i_gout = 0; i_gout < energy[gin].size(); i_gout++) { for (int i_gout = 0; i_gout < energy[gin].size(); i_gout++) {
@ -816,8 +820,8 @@ void ScattDataTabular::combine(
size_t groups = those_scatts[0]->energy.size(); size_t groups = those_scatts[0]->energy.size();
xt::xtensor<int, 1> in_gmin({groups}, 0); tensor::Tensor<int> in_gmin({groups}, 0);
xt::xtensor<int, 1> in_gmax({groups}, 0); tensor::Tensor<int> in_gmax({groups}, 0);
double_3dvec sparse_scatter(groups); double_3dvec sparse_scatter(groups);
double_2dvec sparse_mult(groups); double_2dvec sparse_mult(groups);
@ -854,7 +858,7 @@ void convert_legendre_to_tabular(ScattDataLegendre& leg, ScattDataTabular& tab)
tab.scattxs = leg.scattxs; tab.scattxs = leg.scattxs;
// Build mu and dmu // Build mu and dmu
tab.mu = xt::linspace(-1., 1., n_mu); tab.mu = tensor::linspace(-1., 1., n_mu);
tab.dmu = 2. / (n_mu - 1); tab.dmu = 2. / (n_mu - 1);
// Calculate f(mu) and integrate it so we can avoid rejection sampling // Calculate f(mu) and integrate it so we can avoid rejection sampling

View file

@ -5,8 +5,7 @@
#include <cstddef> // for size_t #include <cstddef> // for size_t
#include <iterator> // for back_inserter #include <iterator> // for back_inserter
#include "xtensor/xarray.hpp" #include "openmc/tensor.h"
#include "xtensor/xview.hpp"
#include "openmc/endf.h" #include "openmc/endf.h"
#include "openmc/hdf5_interface.h" #include "openmc/hdf5_interface.h"
@ -26,11 +25,11 @@ CorrelatedAngleEnergy::CorrelatedAngleEnergy(hid_t group)
hid_t dset = open_dataset(group, "energy"); hid_t dset = open_dataset(group, "energy");
// Get interpolation parameters // Get interpolation parameters
xt::xarray<int> temp; tensor::Tensor<int> temp;
read_attribute(dset, "interpolation", temp); read_attribute(dset, "interpolation", temp);
auto temp_b = xt::view(temp, 0); // view of breakpoints tensor::View<int> temp_b = temp.slice(0); // breakpoints
auto temp_i = xt::view(temp, 1); // view of interpolation parameters tensor::View<int> temp_i = temp.slice(1); // interpolation parameters
std::copy(temp_b.begin(), temp_b.end(), std::back_inserter(breakpoints_)); std::copy(temp_b.begin(), temp_b.end(), std::back_inserter(breakpoints_));
for (const auto i : temp_i) for (const auto i : temp_i)
@ -51,12 +50,12 @@ CorrelatedAngleEnergy::CorrelatedAngleEnergy(hid_t group)
read_attribute(dset, "interpolation", interp); read_attribute(dset, "interpolation", interp);
read_attribute(dset, "n_discrete_lines", n_discrete); read_attribute(dset, "n_discrete_lines", n_discrete);
xt::xarray<double> eout; tensor::Tensor<double> eout;
read_dataset(dset, eout); read_dataset(dset, eout);
close_dataset(dset); close_dataset(dset);
// Read angle distributions // Read angle distributions
xt::xarray<double> mu; tensor::Tensor<double> mu;
read_dataset(group, "mu", mu); read_dataset(group, "mu", mu);
for (int i = 0; i < n_energy; ++i) { for (int i = 0; i < n_energy; ++i) {
@ -66,7 +65,7 @@ CorrelatedAngleEnergy::CorrelatedAngleEnergy(hid_t group)
if (i < n_energy - 1) { if (i < n_energy - 1) {
n = offsets[i + 1] - j; n = offsets[i + 1] - j;
} else { } else {
n = eout.shape()[1] - j; n = eout.shape(1) - j;
} }
// Assign interpolation scheme and number of discrete lines // Assign interpolation scheme and number of discrete lines
@ -75,9 +74,9 @@ CorrelatedAngleEnergy::CorrelatedAngleEnergy(hid_t group)
d.n_discrete = n_discrete[i]; d.n_discrete = n_discrete[i];
// Copy data // Copy data
d.e_out = xt::view(eout, 0, xt::range(j, j + n)); d.e_out = eout.slice(0, tensor::range(j, j + n));
d.p = xt::view(eout, 1, xt::range(j, j + n)); d.p = eout.slice(1, tensor::range(j, j + n));
d.c = xt::view(eout, 2, xt::range(j, j + n)); d.c = eout.slice(2, tensor::range(j, j + n));
// To get answers that match ACE data, for now we still use the tabulated // To get answers that match ACE data, for now we still use the tabulated
// CDF values that were passed through to the HDF5 library. At a later // CDF values that were passed through to the HDF5 library. At a later
@ -119,10 +118,10 @@ CorrelatedAngleEnergy::CorrelatedAngleEnergy(hid_t group)
// Determine offset and size of distribution // Determine offset and size of distribution
int offset_mu = std::lround(eout(4, offsets[i] + j)); int offset_mu = std::lround(eout(4, offsets[i] + j));
int m; int m;
if (offsets[i] + j + 1 < eout.shape()[1]) { if (offsets[i] + j + 1 < eout.shape(1)) {
m = std::lround(eout(4, offsets[i] + j + 1)) - offset_mu; m = std::lround(eout(4, offsets[i] + j + 1)) - offset_mu;
} else { } else {
m = mu.shape()[1] - offset_mu; m = mu.shape(1) - offset_mu;
} }
// For incoherent inelastic thermal scattering, the angle distributions // For incoherent inelastic thermal scattering, the angle distributions
@ -133,9 +132,12 @@ CorrelatedAngleEnergy::CorrelatedAngleEnergy(hid_t group)
interp_mu = 1; interp_mu = 1;
auto interp = int2interp(interp_mu); auto interp = int2interp(interp_mu);
auto xs = xt::view(mu, 0, xt::range(offset_mu, offset_mu + m)); tensor::View<double> xs =
auto ps = xt::view(mu, 1, xt::range(offset_mu, offset_mu + m)); mu.slice(0, tensor::range(offset_mu, offset_mu + m));
auto cs = xt::view(mu, 2, xt::range(offset_mu, offset_mu + m)); tensor::View<double> ps =
mu.slice(1, tensor::range(offset_mu, offset_mu + m));
tensor::View<double> cs =
mu.slice(2, tensor::range(offset_mu, offset_mu + m));
vector<double> x {xs.begin(), xs.end()}; vector<double> x {xs.begin(), xs.end()};
vector<double> p {ps.begin(), ps.end()}; vector<double> p {ps.begin(), ps.end()};

View file

@ -5,8 +5,7 @@
#include <cstddef> // for size_t #include <cstddef> // for size_t
#include <iterator> // for back_inserter #include <iterator> // for back_inserter
#include "xtensor/xarray.hpp" #include "openmc/tensor.h"
#include "xtensor/xview.hpp"
#include "openmc/hdf5_interface.h" #include "openmc/hdf5_interface.h"
#include "openmc/math_functions.h" #include "openmc/math_functions.h"
@ -27,11 +26,11 @@ KalbachMann::KalbachMann(hid_t group)
hid_t dset = open_dataset(group, "energy"); hid_t dset = open_dataset(group, "energy");
// Get interpolation parameters // Get interpolation parameters
xt::xarray<int> temp; tensor::Tensor<int> temp;
read_attribute(dset, "interpolation", temp); read_attribute(dset, "interpolation", temp);
auto temp_b = xt::view(temp, 0); // view of breakpoints tensor::View<int> temp_b = temp.slice(0); // breakpoints
auto temp_i = xt::view(temp, 1); // view of interpolation parameters tensor::View<int> temp_i = temp.slice(1); // interpolation parameters
std::copy(temp_b.begin(), temp_b.end(), std::back_inserter(breakpoints_)); std::copy(temp_b.begin(), temp_b.end(), std::back_inserter(breakpoints_));
for (const auto i : temp_i) for (const auto i : temp_i)
@ -52,7 +51,7 @@ KalbachMann::KalbachMann(hid_t group)
read_attribute(dset, "interpolation", interp); read_attribute(dset, "interpolation", interp);
read_attribute(dset, "n_discrete_lines", n_discrete); read_attribute(dset, "n_discrete_lines", n_discrete);
xt::xarray<double> eout; tensor::Tensor<double> eout;
read_dataset(dset, eout); read_dataset(dset, eout);
close_dataset(dset); close_dataset(dset);
@ -63,7 +62,7 @@ KalbachMann::KalbachMann(hid_t group)
if (i < n_energy - 1) { if (i < n_energy - 1) {
n = offsets[i + 1] - j; n = offsets[i + 1] - j;
} else { } else {
n = eout.shape()[1] - j; n = eout.shape(1) - j;
} }
// Assign interpolation scheme and number of discrete lines // Assign interpolation scheme and number of discrete lines
@ -72,11 +71,11 @@ KalbachMann::KalbachMann(hid_t group)
d.n_discrete = n_discrete[i]; d.n_discrete = n_discrete[i];
// Copy data // Copy data
d.e_out = xt::view(eout, 0, xt::range(j, j + n)); d.e_out = eout.slice(0, tensor::range(j, j + n));
d.p = xt::view(eout, 1, xt::range(j, j + n)); d.p = eout.slice(1, tensor::range(j, j + n));
d.c = xt::view(eout, 2, xt::range(j, j + n)); d.c = eout.slice(2, tensor::range(j, j + n));
d.r = xt::view(eout, 3, xt::range(j, j + n)); d.r = eout.slice(3, tensor::range(j, j + n));
d.a = xt::view(eout, 4, xt::range(j, j + n)); d.a = eout.slice(4, tensor::range(j, j + n));
// To get answers that match ACE data, for now we still use the tabulated // To get answers that match ACE data, for now we still use the tabulated
// CDF values that were passed through to the HDF5 library. At a later // CDF values that were passed through to the HDF5 library. At a later

View file

@ -5,7 +5,7 @@
#include "openmc/random_lcg.h" #include "openmc/random_lcg.h"
#include "openmc/search.h" #include "openmc/search.h"
#include "xtensor/xview.hpp" #include "openmc/tensor.h"
#include <cassert> #include <cassert>
#include <cmath> // for log, exp #include <cmath> // for log, exp
@ -85,7 +85,7 @@ void IncoherentElasticAEDiscrete::sample(
// incoming energies. // incoming energies.
// Sample outgoing cosine bin // Sample outgoing cosine bin
int n_mu = mu_out_.shape()[1]; int n_mu = mu_out_.shape(1);
int k = prn(seed) * n_mu; int k = prn(seed) * n_mu;
// Rather than use the sampled discrete mu directly, it is smeared over // Rather than use the sampled discrete mu directly, it is smeared over
@ -145,7 +145,7 @@ void IncoherentInelasticAEDiscrete::sample(
// probability of 1). Otherwise, each bin is equally probable. // probability of 1). Otherwise, each bin is equally probable.
int j; int j;
int n = energy_out_.shape()[1]; int n = energy_out_.shape(1);
if (!skewed_) { if (!skewed_) {
// All bins equally likely // All bins equally likely
j = prn(seed) * n; j = prn(seed) * n;
@ -178,7 +178,7 @@ void IncoherentInelasticAEDiscrete::sample(
E_out = (1 - f) * E_ij + f * E_i1j; E_out = (1 - f) * E_ij + f * E_i1j;
// Sample outgoing cosine bin // Sample outgoing cosine bin
int m = mu_out_.shape()[2]; int m = mu_out_.shape(2);
int k = prn(seed) * m; int k = prn(seed) * m;
// Determine outgoing cosine corresponding to E_in[i] and E_in[i+1] // Determine outgoing cosine corresponding to E_in[i] and E_in[i+1]
@ -218,11 +218,11 @@ IncoherentInelasticAE::IncoherentInelasticAE(hid_t group)
// On first pass, allocate space for angles // On first pass, allocate space for angles
if (j == 0) { if (j == 0) {
auto n_mu = adist->x().size(); auto n_mu = adist->x().size();
d.mu = xt::empty<double>({d.n_e_out, n_mu}); d.mu = tensor::Tensor<double>({d.n_e_out, n_mu});
} }
// Copy outgoing angles // Copy outgoing angles
auto mu_j = xt::view(d.mu, j); tensor::View<double> mu_j = d.mu.slice(j);
std::copy(adist->x().begin(), adist->x().end(), mu_j.begin()); std::copy(adist->x().begin(), adist->x().end(), mu_j.begin());
} }
} }
@ -287,7 +287,7 @@ void IncoherentInelasticAE::sample(
} }
// Sample outgoing cosine bin // Sample outgoing cosine bin
int n_mu = distribution_[l].mu.shape()[1]; int n_mu = distribution_[l].mu.shape(1);
std::size_t k = prn(seed) * n_mu; std::size_t k = prn(seed) * n_mu;
// Rather than use the sampled discrete mu directly, it is smeared over // Rather than use the sampled discrete mu directly, it is smeared over

View file

@ -30,7 +30,7 @@
#ifdef _OPENMP #ifdef _OPENMP
#include <omp.h> #include <omp.h>
#endif #endif
#include "xtensor/xview.hpp" #include "openmc/tensor.h"
#ifdef OPENMC_MPI #ifdef OPENMC_MPI
#include <mpi.h> #include <mpi.h>
@ -413,7 +413,7 @@ void finalize_batch()
// Reset global tally results // Reset global tally results
if (simulation::current_batch <= settings::n_inactive) { if (simulation::current_batch <= settings::n_inactive) {
xt::view(simulation::global_tallies, xt::all()) = 0.0; simulation::global_tallies.fill(0.0);
simulation::n_realizations = 0; simulation::n_realizations = 0;
} }

View file

@ -10,7 +10,7 @@
#include <dlfcn.h> // for dlopen, dlsym, dlclose, dlerror #include <dlfcn.h> // for dlopen, dlsym, dlclose, dlerror
#endif #endif
#include "xtensor/xadapt.hpp" #include "openmc/tensor.h"
#include <fmt/core.h> #include <fmt/core.h>
#include "openmc/bank.h" #include "openmc/bank.h"
@ -400,8 +400,9 @@ SourceSite IndependentSource::sample(uint64_t* seed) const
auto p = particle_.transport_index(); auto p = particle_.transport_index();
auto energy_ptr = dynamic_cast<Discrete*>(energy_.get()); auto energy_ptr = dynamic_cast<Discrete*>(energy_.get());
if (energy_ptr) { if (energy_ptr) {
auto energies = xt::adapt(energy_ptr->x()); auto energies =
if (xt::any(energies > data::energy_max[p])) { tensor::Tensor<double>(energy_ptr->x().data(), energy_ptr->x().size());
if ((energies > data::energy_max[p]).any()) {
fatal_error("Source energy above range of energies of at least " fatal_error("Source energy above range of energies of at least "
"one cross section table"); "one cross section table");
} }

View file

@ -4,8 +4,7 @@
#include <cstdint> // for int64_t #include <cstdint> // for int64_t
#include <string> #include <string>
#include "xtensor/xbuilder.hpp" // for empty_like #include "openmc/tensor.h"
#include "xtensor/xview.hpp"
#include <fmt/core.h> #include <fmt/core.h>
#include "openmc/bank.h" #include "openmc/bank.h"
@ -277,8 +276,8 @@ extern "C" int openmc_statepoint_write(const char* filename, bool* write_source)
std::string name = "tally " + std::to_string(tally->id_); std::string name = "tally " + std::to_string(tally->id_);
hid_t tally_group = open_group(tallies_group, name.c_str()); hid_t tally_group = open_group(tallies_group, name.c_str());
auto& results = tally->results_; auto& results = tally->results_;
write_tally_results(tally_group, results.shape()[0], write_tally_results(tally_group, results.shape(0), results.shape(1),
results.shape()[1], results.shape()[2], results.data()); results.shape(2), results.data());
close_group(tally_group); close_group(tally_group);
} }
} else { } else {
@ -517,8 +516,8 @@ extern "C" int openmc_statepoint_load(const char* filename)
tally->writable_ = false; tally->writable_ = false;
} else { } else {
auto& results = tally->results_; auto& results = tally->results_;
read_tally_results(tally_group, results.shape()[0], read_tally_results(tally_group, results.shape(0), results.shape(1),
results.shape()[1], results.shape()[2], results.data()); results.shape(2), results.data());
read_dataset(tally_group, "n_realizations", tally->n_realizations_); read_dataset(tally_group, "n_realizations", tally->n_realizations_);
close_group(tally_group); close_group(tally_group);
@ -827,7 +826,7 @@ void write_unstructured_mesh_results()
// construct result vectors // construct result vectors
vector<double> mean_vec(umesh->n_bins()), vector<double> mean_vec(umesh->n_bins()),
std_dev_vec(umesh->n_bins()); std_dev_vec(umesh->n_bins());
for (int j = 0; j < tally->results_.shape()[0]; j++) { for (int j = 0; j < tally->results_.shape(0); j++) {
// get the volume for this bin // get the volume for this bin
double volume = umesh->volume(j); double volume = umesh->volume(j);
// compute the mean // compute the mean
@ -889,7 +888,7 @@ void write_tally_results_nr(hid_t file_id)
#ifdef OPENMC_MPI #ifdef OPENMC_MPI
// Reduce global tallies // Reduce global tallies
xt::xtensor<double, 2> gt_reduced = xt::empty_like(gt); tensor::Tensor<double> gt_reduced({N_GLOBAL_TALLIES, 3});
MPI_Reduce(gt.data(), gt_reduced.data(), gt.size(), MPI_DOUBLE, MPI_SUM, 0, MPI_Reduce(gt.data(), gt_reduced.data(), gt.size(), MPI_DOUBLE, MPI_SUM, 0,
mpi::intracomm); mpi::intracomm);
@ -918,13 +917,18 @@ void write_tally_results_nr(hid_t file_id)
write_attribute(file_id, "tallies_present", 1); write_attribute(file_id, "tallies_present", 1);
} }
// Get view of accumulated tally values // Copy the SUM and SUM_SQ columns from the tally results into a
auto values_view = xt::view(t->results_, xt::all(), xt::all(), // contiguous array for MPI reduction
xt::range(static_cast<int>(TallyResult::SUM), const int r_start = static_cast<int>(TallyResult::SUM);
static_cast<int>(TallyResult::SUM_SQ) + 1)); const int r_end = static_cast<int>(TallyResult::SUM_SQ) + 1;
const size_t r_count = r_end - r_start;
// Make copy of tally values in contiguous array const size_t ni = t->results_.shape(0);
xt::xtensor<double, 3> values = values_view; const size_t nj = t->results_.shape(1);
tensor::Tensor<double> values({ni, nj, r_count});
for (size_t i = 0; i < ni; i++)
for (size_t j = 0; j < nj; j++)
for (size_t r = 0; r < r_count; r++)
values(i, j, r) = t->results_(i, j, r_start + r);
if (mpi::master) { if (mpi::master) {
// Open group for tally // Open group for tally
@ -938,19 +942,22 @@ void write_tally_results_nr(hid_t file_id)
MPI_SUM, 0, mpi::intracomm); MPI_SUM, 0, mpi::intracomm);
#endif #endif
// At the end of the simulation, store the results back in the // At the end of the simulation, store the reduced results back
// regular TallyResults array // into the tally results array
if (simulation::current_batch == settings::n_max_batches || if (simulation::current_batch == settings::n_max_batches ||
simulation::satisfy_triggers) { simulation::satisfy_triggers) {
values_view = values; for (size_t i = 0; i < ni; i++)
for (size_t j = 0; j < nj; j++)
for (size_t r = 0; r < r_count; r++)
t->results_(i, j, r_start + r) = values(i, j, r);
} }
// Put in temporary tally result // Put reduced values into a full-sized copy for writing to HDF5
xt::xtensor<double, 3> results_copy = xt::zeros_like(t->results_); tensor::Tensor<double> results_copy = tensor::zeros_like(t->results_);
auto copy_view = xt::view(results_copy, xt::all(), xt::all(), for (size_t i = 0; i < ni; i++)
xt::range(static_cast<int>(TallyResult::SUM), for (size_t j = 0; j < nj; j++)
static_cast<int>(TallyResult::SUM_SQ) + 1)); for (size_t r = 0; r < r_count; r++)
copy_view = values; results_copy(i, j, r_start + r) = values(i, j, r);
// Write reduced tally results to file // Write reduced tally results to file
auto shape = results_copy.shape(); auto shape = results_copy.shape();

View file

@ -9,6 +9,7 @@
#include "openmc/cell.h" #include "openmc/cell.h"
#include "openmc/error.h" #include "openmc/error.h"
#include "openmc/geometry.h" #include "openmc/geometry.h"
#include "openmc/tensor.h"
#include "openmc/xml_interface.h" #include "openmc/xml_interface.h"
namespace openmc { namespace openmc {
@ -108,7 +109,7 @@ void CellInstanceFilter::to_statepoint(hid_t filter_group) const
{ {
Filter::to_statepoint(filter_group); Filter::to_statepoint(filter_group);
size_t n = cell_instances_.size(); size_t n = cell_instances_.size();
xt::xtensor<size_t, 2> data({n, 2}); tensor::Tensor<size_t> data({n, 2});
for (int64_t i = 0; i < n; ++i) { for (int64_t i = 0; i < n; ++i) {
const auto& x = cell_instances_[i]; const auto& x = cell_instances_[i];
data(i, 0) = model::cells[x.index_cell]->id_; data(i, 0) = model::cells[x.index_cell]->id_;

View file

@ -1,5 +1,6 @@
#include "openmc/tallies/filter_meshmaterial.h" #include "openmc/tallies/filter_meshmaterial.h"
#include <cassert>
#include <utility> // for move #include <utility> // for move
#include <fmt/core.h> #include <fmt/core.h>
@ -10,6 +11,7 @@
#include "openmc/error.h" #include "openmc/error.h"
#include "openmc/material.h" #include "openmc/material.h"
#include "openmc/mesh.h" #include "openmc/mesh.h"
#include "openmc/tensor.h"
#include "openmc/xml_interface.h" #include "openmc/xml_interface.h"
namespace openmc { namespace openmc {
@ -161,7 +163,7 @@ void MeshMaterialFilter::to_statepoint(hid_t filter_group) const
write_dataset(filter_group, "mesh", model::meshes[mesh_]->id_); write_dataset(filter_group, "mesh", model::meshes[mesh_]->id_);
size_t n = bins_.size(); size_t n = bins_.size();
xt::xtensor<size_t, 2> data({n, 2}); tensor::Tensor<size_t> data({n, 2});
for (int64_t i = 0; i < n; ++i) { for (int64_t i = 0; i < n; ++i) {
const auto& x = bins_[i]; const auto& x = bins_[i];
data(i, 0) = x.index_element; data(i, 0) = x.index_element;

View file

@ -35,9 +35,7 @@
#include "openmc/tallies/filter_time.h" #include "openmc/tallies/filter_time.h"
#include "openmc/xml_interface.h" #include "openmc/xml_interface.h"
#include "xtensor/xadapt.hpp" #include "openmc/tensor.h"
#include "xtensor/xbuilder.hpp" // for empty_like
#include "xtensor/xview.hpp"
#include <fmt/core.h> #include <fmt/core.h>
#include <algorithm> // for max, set_union #include <algorithm> // for max, set_union
@ -69,7 +67,7 @@ vector<double> time_grid;
} // namespace model } // namespace model
namespace simulation { namespace simulation {
xt::xtensor_fixed<double, xt::xshape<N_GLOBAL_TALLIES, 3>> global_tallies; tensor::StaticTensor2D<double, N_GLOBAL_TALLIES, 3> global_tallies;
int32_t n_realizations {0}; int32_t n_realizations {0};
} // namespace simulation } // namespace simulation
@ -806,9 +804,11 @@ void Tally::init_results()
{ {
int n_scores = scores_.size() * nuclides_.size(); int n_scores = scores_.size() * nuclides_.size();
if (higher_moments_) { if (higher_moments_) {
results_ = xt::empty<double>({n_filter_bins_, n_scores, 5}); results_ = tensor::Tensor<double>({static_cast<size_t>(n_filter_bins_),
static_cast<size_t>(n_scores), size_t {5}});
} else { } else {
results_ = xt::empty<double>({n_filter_bins_, n_scores, 3}); results_ = tensor::Tensor<double>({static_cast<size_t>(n_filter_bins_),
static_cast<size_t>(n_scores), size_t {3}});
} }
} }
@ -816,7 +816,7 @@ void Tally::reset()
{ {
n_realizations_ = 0; n_realizations_ = 0;
if (results_.size() != 0) { if (results_.size() != 0) {
xt::view(results_, xt::all()) = 0.0; results_.fill(0.0);
} }
} }
@ -851,9 +851,9 @@ void Tally::accumulate()
if (higher_moments_) { if (higher_moments_) {
#pragma omp parallel for #pragma omp parallel for
// filter bins (specific cell, energy bins) // filter bins (specific cell, energy bins)
for (int i = 0; i < results_.shape()[0]; ++i) { for (int i = 0; i < results_.shape(0); ++i) {
// score bins (flux, total reaction rate, fission reaction rate, etc.) // score bins (flux, total reaction rate, fission reaction rate, etc.)
for (int j = 0; j < results_.shape()[1]; ++j) { for (int j = 0; j < results_.shape(1); ++j) {
double val = results_(i, j, TallyResult::VALUE) * norm; double val = results_(i, j, TallyResult::VALUE) * norm;
double val2 = val * val; double val2 = val * val;
results_(i, j, TallyResult::VALUE) = 0.0; results_(i, j, TallyResult::VALUE) = 0.0;
@ -866,9 +866,9 @@ void Tally::accumulate()
} else { } else {
#pragma omp parallel for #pragma omp parallel for
// filter bins (specific cell, energy bins) // filter bins (specific cell, energy bins)
for (int i = 0; i < results_.shape()[0]; ++i) { for (int i = 0; i < results_.shape(0); ++i) {
// score bins (flux, total reaction rate, fission reaction rate, etc.) // score bins (flux, total reaction rate, fission reaction rate, etc.)
for (int j = 0; j < results_.shape()[1]; ++j) { for (int j = 0; j < results_.shape(1); ++j) {
double val = results_(i, j, TallyResult::VALUE) * norm; double val = results_(i, j, TallyResult::VALUE) * norm;
results_(i, j, TallyResult::VALUE) = 0.0; results_(i, j, TallyResult::VALUE) = 0.0;
results_(i, j, TallyResult::SUM) += val; results_(i, j, TallyResult::SUM) += val;
@ -888,18 +888,18 @@ int Tally::score_index(const std::string& score) const
return -1; return -1;
} }
xt::xarray<double> Tally::get_reshaped_data() const tensor::Tensor<double> Tally::get_reshaped_data() const
{ {
std::vector<uint64_t> shape; vector<size_t> shape;
for (auto f : filters()) { for (auto f : filters()) {
shape.push_back(model::tally_filters[f]->n_bins()); shape.push_back(model::tally_filters[f]->n_bins());
} }
// add number of scores and nuclides to tally // add number of scores and nuclides to tally
shape.push_back(results_.shape()[1]); shape.push_back(results_.shape(1));
shape.push_back(results_.shape()[2]); shape.push_back(results_.shape(2));
xt::xarray<double> reshaped_results = results_; tensor::Tensor<double> reshaped_results = results_;
reshaped_results.reshape(shape); reshaped_results.reshape(shape);
return reshaped_results; return reshaped_results;
} }
@ -1004,13 +1004,14 @@ void reduce_tally_results()
// Skip any tallies that are not active // Skip any tallies that are not active
auto& tally {model::tallies[i_tally]}; auto& tally {model::tallies[i_tally]};
// Get view of accumulated tally values // Extract 2D view of the VALUE column from the 3D results tensor,
auto values_view = xt::view(tally->results_, xt::all(), xt::all(), // then copy into a contiguous array for MPI reduction
static_cast<int>(TallyResult::VALUE)); const int val_idx = static_cast<int>(TallyResult::VALUE);
tensor::View<double> val_view =
tally->results_.slice(tensor::all, tensor::all, val_idx);
tensor::Tensor<double> values(val_view);
// Make copy of tally values in contiguous array tensor::Tensor<double> values_reduced(values.shape());
xt::xtensor<double, 2> values = values_view;
xt::xtensor<double, 2> values_reduced = xt::empty_like(values);
// Reduce contiguous set of tally results // Reduce contiguous set of tally results
MPI_Reduce(values.data(), values_reduced.data(), values.size(), MPI_Reduce(values.data(), values_reduced.data(), values.size(),
@ -1018,9 +1019,9 @@ void reduce_tally_results()
// Transfer values on master and reset on other ranks // Transfer values on master and reset on other ranks
if (mpi::master) { if (mpi::master) {
values_view = values_reduced; val_view = values_reduced;
} else { } else {
values_view = 0.0; val_view = 0.0;
} }
} }
} }
@ -1028,14 +1029,13 @@ void reduce_tally_results()
// Note that global tallies are *always* reduced even when no_reduce option // Note that global tallies are *always* reduced even when no_reduce option
// is on. // is on.
// Get view of global tally values // Get reference to global tallies
auto& gt = simulation::global_tallies; auto& gt = simulation::global_tallies;
auto gt_values_view = const int val_col = static_cast<int>(TallyResult::VALUE);
xt::view(gt, xt::all(), static_cast<int>(TallyResult::VALUE));
// Make copy of values in contiguous array // Copy VALUE column into contiguous array for MPI reduction
xt::xtensor<double, 1> gt_values = gt_values_view; tensor::Tensor<double> gt_values(gt.slice(tensor::all, val_col));
xt::xtensor<double, 1> gt_values_reduced = xt::empty_like(gt_values); tensor::Tensor<double> gt_values_reduced({size_t {N_GLOBAL_TALLIES}});
// Reduce contiguous data // Reduce contiguous data
MPI_Reduce(gt_values.data(), gt_values_reduced.data(), N_GLOBAL_TALLIES, MPI_Reduce(gt_values.data(), gt_values_reduced.data(), N_GLOBAL_TALLIES,
@ -1043,9 +1043,9 @@ void reduce_tally_results()
// Transfer values on master and reset on other ranks // Transfer values on master and reset on other ranks
if (mpi::master) { if (mpi::master) {
gt_values_view = gt_values_reduced; gt.slice(tensor::all, val_col) = gt_values_reduced;
} else { } else {
gt_values_view = 0.0; gt.slice(tensor::all, val_col) = 0.0;
} }
// We also need to determine the total starting weight of particles from the // We also need to determine the total starting weight of particles from the

View file

@ -20,6 +20,7 @@
#include "openmc/tallies/filter_delayedgroup.h" #include "openmc/tallies/filter_delayedgroup.h"
#include "openmc/tallies/filter_energy.h" #include "openmc/tallies/filter_energy.h"
#include <numeric>
#include <string> #include <string>
namespace openmc { namespace openmc {

View file

@ -71,7 +71,7 @@ void check_tally_triggers(double& ratio, int& tally_id, int& score)
continue; continue;
const auto& results = t.results_; const auto& results = t.results_;
for (auto filter_index = 0; filter_index < results.shape()[0]; for (auto filter_index = 0; filter_index < results.shape(0);
++filter_index) { ++filter_index) {
// Compute the tally uncertainty metrics. // Compute the tally uncertainty metrics.
auto uncert_pair = auto uncert_pair =

View file

@ -3,12 +3,7 @@
#include <algorithm> // for sort, move, min, max, find #include <algorithm> // for sort, move, min, max, find
#include <cmath> // for round, sqrt, abs #include <cmath> // for round, sqrt, abs
#include "xtensor/xarray.hpp" #include "openmc/tensor.h"
#include "xtensor/xbuilder.hpp"
#include "xtensor/xmath.hpp"
#include "xtensor/xsort.hpp"
#include "xtensor/xtensor.hpp"
#include "xtensor/xview.hpp"
#include <fmt/core.h> #include <fmt/core.h>
#include "openmc/constants.h" #include "openmc/constants.h"
@ -55,7 +50,7 @@ ThermalScattering::ThermalScattering(
// Determine temperatures available // Determine temperatures available
auto dset_names = dataset_names(kT_group); auto dset_names = dataset_names(kT_group);
auto n = dset_names.size(); auto n = dset_names.size();
auto temps_available = xt::empty<double>({n}); auto temps_available = tensor::Tensor<double>({n});
for (int i = 0; i < dset_names.size(); ++i) { for (int i = 0; i < dset_names.size(); ++i) {
// Read temperature value // Read temperature value
double T; double T;
@ -82,7 +77,7 @@ ThermalScattering::ThermalScattering(
// Determine actual temperatures to read // Determine actual temperatures to read
for (const auto& T : temperature) { for (const auto& T : temperature) {
auto i_closest = xt::argmin(xt::abs(temps_available - T))[0]; auto i_closest = tensor::abs(temps_available - T).argmin();
auto temp_actual = temps_available[i_closest]; auto temp_actual = temps_available[i_closest];
if (std::abs(temp_actual - T) < settings::temperature_tolerance) { if (std::abs(temp_actual - T) < settings::temperature_tolerance) {
if (std::find(temps_to_read.begin(), temps_to_read.end(), if (std::find(temps_to_read.begin(), temps_to_read.end(),

View file

@ -8,7 +8,7 @@
#include "openmc/simulation.h" #include "openmc/simulation.h"
#include "openmc/vector.h" #include "openmc/vector.h"
#include "xtensor/xtensor.hpp" #include "openmc/tensor.h"
#include <fmt/core.h> #include <fmt/core.h>
#include <hdf5.h> #include <hdf5.h>

View file

@ -25,7 +25,7 @@ UrrData::UrrData(hid_t group_id)
// Read URR tables. The HDF5 format is a little // Read URR tables. The HDF5 format is a little
// different from how we want it laid out in memory. // different from how we want it laid out in memory.
// This array used to be called "prob_". // This array used to be called "prob_".
xt::xtensor<double, 3> tmp_prob; tensor::Tensor<double> tmp_prob;
read_dataset(group_id, "table", tmp_prob); read_dataset(group_id, "table", tmp_prob);
auto shape = tmp_prob.shape(); auto shape = tmp_prob.shape();
@ -38,7 +38,7 @@ UrrData::UrrData(hid_t group_id)
xs_values_.resize({n_energy, n_cdf_values}); xs_values_.resize({n_energy, n_cdf_values});
// Now fill in the values. Using manual loops here since we might // Now fill in the values. Using manual loops here since we might
// not have fancy xtensor slicing code written for GPU tensors. // not have fancy tensor slicing code written for GPU tensors.
// The below enum gives how URR tables are laid out in our HDF5 tables. // The below enum gives how URR tables are laid out in our HDF5 tables.
enum class URRTableParam { enum class URRTableParam {
CUM_PROB, CUM_PROB,

View file

@ -17,8 +17,7 @@
#include "openmc/timer.h" #include "openmc/timer.h"
#include "openmc/xml_interface.h" #include "openmc/xml_interface.h"
#include "xtensor/xadapt.hpp" #include "openmc/tensor.h"
#include "xtensor/xview.hpp"
#include <fmt/core.h> #include <fmt/core.h>
#include <algorithm> // for copy #include <algorithm> // for copy
@ -242,7 +241,8 @@ vector<VolumeCalculation::Result> VolumeCalculation::execute() const
// non-zero // non-zero
auto n_nuc = auto n_nuc =
settings::run_CE ? data::nuclides.size() : data::mg.nuclides_.size(); settings::run_CE ? data::nuclides.size() : data::mg.nuclides_.size();
xt::xtensor<double, 2> atoms({n_nuc, 2}, 0.0); auto atoms =
tensor::zeros<double>({static_cast<size_t>(n_nuc), size_t {2}});
#ifdef OPENMC_MPI #ifdef OPENMC_MPI
if (mpi::master) { if (mpi::master) {
@ -452,9 +452,11 @@ void VolumeCalculation::to_hdf5(
} }
// Create array of total # of atoms with uncertainty for each nuclide // Create array of total # of atoms with uncertainty for each nuclide
xt::xtensor<double, 2> atom_data({n_nuc, 2}); tensor::Tensor<double> atom_data({static_cast<size_t>(n_nuc), size_t {2}});
xt::view(atom_data, xt::all(), 0) = xt::adapt(result.atoms); for (size_t k = 0; k < static_cast<size_t>(n_nuc); ++k) {
xt::view(atom_data, xt::all(), 1) = xt::adapt(result.uncertainty); atom_data(k, 0) = result.atoms[k];
atom_data(k, 1) = result.uncertainty[k];
}
// Write results // Write results
write_dataset(group_id, "nuclides", nucnames); write_dataset(group_id, "nuclides", nucnames);

View file

@ -6,12 +6,7 @@
#include <set> #include <set>
#include <string> #include <string>
#include "xtensor/xdynamic_view.hpp" #include "openmc/tensor.h"
#include "xtensor/xindex_view.hpp"
#include "xtensor/xio.hpp"
#include "xtensor/xmasked_view.hpp"
#include "xtensor/xnoalias.hpp"
#include "xtensor/xview.hpp"
#include "openmc/error.h" #include "openmc/error.h"
#include "openmc/file_utils.h" #include "openmc/file_utils.h"
@ -265,8 +260,12 @@ WeightWindows* WeightWindows::from_hdf5(
} }
wws->set_mesh(model::mesh_map[mesh_id]); wws->set_mesh(model::mesh_map[mesh_id]);
wws->lower_ww_ = xt::empty<double>(wws->bounds_size()); wws->lower_ww_ =
wws->upper_ww_ = xt::empty<double>(wws->bounds_size()); tensor::Tensor<double>({static_cast<size_t>(wws->bounds_size()[0]),
static_cast<size_t>(wws->bounds_size()[1])});
wws->upper_ww_ =
tensor::Tensor<double>({static_cast<size_t>(wws->bounds_size()[0]),
static_cast<size_t>(wws->bounds_size()[1])});
read_dataset<double>(ww_group, "lower_ww_bounds", wws->lower_ww_); read_dataset<double>(ww_group, "lower_ww_bounds", wws->lower_ww_);
read_dataset<double>(ww_group, "upper_ww_bounds", wws->upper_ww_); read_dataset<double>(ww_group, "upper_ww_bounds", wws->upper_ww_);
@ -301,9 +300,11 @@ void WeightWindows::allocate_ww_bounds()
"Size of weight window bounds is zero for WeightWindows {}", id()); "Size of weight window bounds is zero for WeightWindows {}", id());
warning(msg); warning(msg);
} }
lower_ww_ = xt::empty<double>(shape); lower_ww_ = tensor::Tensor<double>(
{static_cast<size_t>(shape[0]), static_cast<size_t>(shape[1])});
lower_ww_.fill(-1); lower_ww_.fill(-1);
upper_ww_ = xt::empty<double>(shape); upper_ww_ = tensor::Tensor<double>(
{static_cast<size_t>(shape[0]), static_cast<size_t>(shape[1])});
upper_ww_.fill(-1); upper_ww_.fill(-1);
} }
@ -448,8 +449,8 @@ void WeightWindows::check_bounds(const T& bounds) const
} }
} }
void WeightWindows::set_bounds(const xt::xtensor<double, 2>& lower_bounds, void WeightWindows::set_bounds(const tensor::Tensor<double>& lower_bounds,
const xt::xtensor<double, 2>& upper_bounds) const tensor::Tensor<double>& upper_bounds)
{ {
this->check_bounds(lower_bounds, upper_bounds); this->check_bounds(lower_bounds, upper_bounds);
@ -460,7 +461,7 @@ void WeightWindows::set_bounds(const xt::xtensor<double, 2>& lower_bounds,
} }
void WeightWindows::set_bounds( void WeightWindows::set_bounds(
const xt::xtensor<double, 2>& lower_bounds, double ratio) const tensor::Tensor<double>& lower_bounds, double ratio)
{ {
this->check_bounds(lower_bounds); this->check_bounds(lower_bounds);
@ -475,14 +476,16 @@ void WeightWindows::set_bounds(
{ {
check_bounds(lower_bounds, upper_bounds); check_bounds(lower_bounds, upper_bounds);
auto shape = this->bounds_size(); auto shape = this->bounds_size();
lower_ww_ = xt::empty<double>(shape); lower_ww_ = tensor::Tensor<double>(
upper_ww_ = xt::empty<double>(shape); {static_cast<size_t>(shape[0]), static_cast<size_t>(shape[1])});
upper_ww_ = tensor::Tensor<double>(
{static_cast<size_t>(shape[0]), static_cast<size_t>(shape[1])});
// set new weight window values // Copy weight window values from input spans into the tensors
xt::view(lower_ww_, xt::all()) = std::copy(lower_bounds.data(), lower_bounds.data() + lower_ww_.size(),
xt::adapt(lower_bounds.data(), lower_ww_.shape()); lower_ww_.data());
xt::view(upper_ww_, xt::all()) = std::copy(upper_bounds.data(), upper_bounds.data() + upper_ww_.size(),
xt::adapt(upper_bounds.data(), upper_ww_.shape()); upper_ww_.data());
} }
void WeightWindows::set_bounds(span<const double> lower_bounds, double ratio) void WeightWindows::set_bounds(span<const double> lower_bounds, double ratio)
@ -490,14 +493,16 @@ void WeightWindows::set_bounds(span<const double> lower_bounds, double ratio)
this->check_bounds(lower_bounds); this->check_bounds(lower_bounds);
auto shape = this->bounds_size(); auto shape = this->bounds_size();
lower_ww_ = xt::empty<double>(shape); lower_ww_ = tensor::Tensor<double>(
upper_ww_ = xt::empty<double>(shape); {static_cast<size_t>(shape[0]), static_cast<size_t>(shape[1])});
upper_ww_ = tensor::Tensor<double>(
{static_cast<size_t>(shape[0]), static_cast<size_t>(shape[1])});
// set new weight window values // Copy lower bounds into both arrays, then scale upper by ratio
xt::view(lower_ww_, xt::all()) = std::copy(lower_bounds.data(), lower_bounds.data() + lower_ww_.size(),
xt::adapt(lower_bounds.data(), lower_ww_.shape()); lower_ww_.data());
xt::view(upper_ww_, xt::all()) = std::copy(lower_bounds.data(), lower_bounds.data() + upper_ww_.size(),
xt::adapt(lower_bounds.data(), upper_ww_.shape()); upper_ww_.data());
upper_ww_ *= ratio; upper_ww_ *= ratio;
} }
@ -510,8 +515,8 @@ void WeightWindows::update_weights(const Tally* tally, const std::string& value,
this->check_tally_update_compatibility(tally); this->check_tally_update_compatibility(tally);
// Dimensions of weight window arrays // Dimensions of weight window arrays
int e_bins = lower_ww_.shape()[0]; int e_bins = lower_ww_.shape(0);
int64_t mesh_bins = lower_ww_.shape()[1]; int64_t mesh_bins = lower_ww_.shape(1);
// Initialize weight window arrays to -1.0 by default // Initialize weight window arrays to -1.0 by default
#pragma omp parallel for collapse(2) schedule(static) #pragma omp parallel for collapse(2) schedule(static)
@ -542,16 +547,16 @@ void WeightWindows::update_weights(const Tally* tally, const std::string& value,
/////////////////////////// ///////////////////////////
// Extract tally data // Extract tally data
// //
// At the end of this section, the mean and rel_err array // At the end of this section, mean and rel_err are
// is a 2D view of tally data (n_e_groups, n_mesh_bins) // 2D tensors of tally data (n_e_groups, n_mesh_bins)
// //
/////////////////////////// ///////////////////////////
// build a shape for a view of the tally results, this will always be // build a shape for the tally results, this will always be
// dimension 5 (3 filter dimensions, 1 score dimension, 1 results dimension) // dimension 5 (3 filter dimensions, 1 score dimension, 1 results dimension)
// Look for the size of the last dimension of the results array // Look for the size of the last dimension of the results tensor
const auto& results_arr = tally->results(); const auto& results = tally->results();
const int results_dim = static_cast<int>(results_arr.shape()[2]); const int results_dim = static_cast<int>(results.shape(2));
std::array<int, 5> shape = {1, 1, 1, tally->n_scores(), results_dim}; std::array<int, 5> shape = {1, 1, 1, tally->n_scores(), results_dim};
// set the shape for the filters applied on the tally // set the shape for the filters applied on the tally
@ -588,25 +593,14 @@ void WeightWindows::update_weights(const Tally* tally, const std::string& value,
std::find(filter_types.begin(), filter_types.end(), FilterType::MESH) - std::find(filter_types.begin(), filter_types.end(), FilterType::MESH) -
filter_types.begin(); filter_types.begin();
// get a fully reshaped view of the tally according to tally ordering of // determine the index of the particle within its filter
// filters
auto tally_values = xt::reshape_view(results_arr, shape);
// get a that is (particle, energy, mesh, scores, values)
auto transposed_view = xt::transpose(tally_values, transpose);
// determine the dimension and index of the particle data
int particle_idx = 0; int particle_idx = 0;
if (tally->has_filter(FilterType::PARTICLE)) { if (tally->has_filter(FilterType::PARTICLE)) {
// get the particle filter
auto pf = tally->get_filter<ParticleFilter>(); auto pf = tally->get_filter<ParticleFilter>();
const auto& particles = pf->particles(); const auto& particles = pf->particles();
// find the index of the particle that matches these weight windows
auto p_it = auto p_it =
std::find(particles.begin(), particles.end(), this->particle_type_); std::find(particles.begin(), particles.end(), this->particle_type_);
// if the particle filter doesn't have particle data for the particle
// used on this weight windows instance, report an error
if (p_it == particles.end()) { if (p_it == particles.end()) {
auto msg = fmt::format("Particle type '{}' not present on Filter {} for " auto msg = fmt::format("Particle type '{}' not present on Filter {} for "
"Tally {} used to update WeightWindows {}", "Tally {} used to update WeightWindows {}",
@ -614,17 +608,46 @@ void WeightWindows::update_weights(const Tally* tally, const std::string& value,
fatal_error(msg); fatal_error(msg);
} }
// use the index of the particle in the filter to down-select data later
particle_idx = p_it - particles.begin(); particle_idx = p_it - particles.begin();
} }
// down-select data based on particle and score // The tally results array is 3D: (n_filter_combos, n_scores, n_result_types).
auto sum = xt::dynamic_view( // The first dimension is a row-major flattening of up to 3 filter dimensions
transposed_view, {particle_idx, xt::all(), xt::all(), score_index, // (particle, energy, mesh) whose storage order depends on which filters the
static_cast<int>(TallyResult::SUM)}); // tally has. We need to map our desired indices (particle, energy, mesh)
auto sum_sq = xt::dynamic_view( // into the correct flat filter combination index.
transposed_view, {particle_idx, xt::all(), xt::all(), score_index, //
static_cast<int>(TallyResult::SUM_SQ)}); // transpose[i] tells us which storage position holds dimension i:
// i=0 -> particle, i=1 -> energy, i=2 -> mesh
// shape[j] gives the number of bins for filter storage position j.
// Row-major strides for the 3 filter dimensions
const int stride0 = shape[1] * shape[2];
const int stride1 = shape[2];
tensor::Tensor<double> sum(
{static_cast<size_t>(e_bins), static_cast<size_t>(mesh_bins)});
tensor::Tensor<double> sum_sq(
{static_cast<size_t>(e_bins), static_cast<size_t>(mesh_bins)});
const int i_sum = static_cast<int>(TallyResult::SUM);
const int i_sum_sq = static_cast<int>(TallyResult::SUM_SQ);
for (int e = 0; e < e_bins; e++) {
for (int64_t m = 0; m < mesh_bins; m++) {
// Place particle, energy, and mesh indices into their storage positions
std::array<int, 3> idx = {0, 0, 0};
idx[transpose[0]] = particle_idx;
idx[transpose[1]] = e;
idx[transpose[2]] = static_cast<int>(m);
// Compute flat filter combination index (row-major over filter dims)
int flat = idx[0] * stride0 + idx[1] * stride1 + idx[2];
sum(e, m) = results(flat, score_index, i_sum);
sum_sq(e, m) = results(flat, score_index, i_sum_sq);
}
}
int n = tally->n_realizations_; int n = tally->n_realizations_;
////////////////////////////////////////////// //////////////////////////////////////////////
@ -1155,7 +1178,8 @@ extern "C" int openmc_weight_windows_set_bounds(int32_t index,
return err; return err;
const auto& wws = variance_reduction::weight_windows[index]; const auto& wws = variance_reduction::weight_windows[index];
wws->set_bounds({lower_bounds, size}, {upper_bounds, size}); wws->set_bounds(span<const double>(lower_bounds, size),
span<const double>(upper_bounds, size));
return 0; return 0;
} }

View file

@ -33,22 +33,22 @@ WindowedMultipole::WindowedMultipole(hid_t group)
// Read the "data" array. Use its shape to figure out the number of poles // Read the "data" array. Use its shape to figure out the number of poles
// and residue types in this data. // and residue types in this data.
read_dataset(group, "data", data_); read_dataset(group, "data", data_);
int n_residues = data_.shape()[1] - 1; int n_residues = data_.shape(1) - 1;
// Check to see if this data includes fission residues. // Check to see if this data includes fission residues.
fissionable_ = (n_residues == 3); fissionable_ = (n_residues == 3);
// Read the "windows" array and use its shape to figure out the number of // Read the "windows" array and use its shape to figure out the number of
// windows. // windows.
xt::xtensor<int, 2> windows; tensor::Tensor<int> windows;
read_dataset(group, "windows", windows); read_dataset(group, "windows", windows);
int n_windows = windows.shape()[0]; int n_windows = windows.shape(0);
windows -= 1; // Adjust to 0-based indices windows -= 1; // Adjust to 0-based indices
// Read the "broaden_poly" arrays. // Read the "broaden_poly" arrays.
xt::xtensor<bool, 1> broaden_poly; tensor::Tensor<bool> broaden_poly;
read_dataset(group, "broaden_poly", broaden_poly); read_dataset(group, "broaden_poly", broaden_poly);
if (n_windows != broaden_poly.shape()[0]) { if (n_windows != broaden_poly.shape(0)) {
fatal_error("broaden_poly array shape is not consistent with the windows " fatal_error("broaden_poly array shape is not consistent with the windows "
"array shape in WMP library for " + "array shape in WMP library for " +
name_ + "."); name_ + ".");
@ -56,12 +56,12 @@ WindowedMultipole::WindowedMultipole(hid_t group)
// Read the "curvefit" array. // Read the "curvefit" array.
read_dataset(group, "curvefit", curvefit_); read_dataset(group, "curvefit", curvefit_);
if (n_windows != curvefit_.shape()[0]) { if (n_windows != curvefit_.shape(0)) {
fatal_error("curvefit array shape is not consistent with the windows " fatal_error("curvefit array shape is not consistent with the windows "
"array shape in WMP library for " + "array shape in WMP library for " +
name_ + "."); name_ + ".");
} }
fit_order_ = curvefit_.shape()[1] - 1; fit_order_ = curvefit_.shape(1) - 1;
// Check the code is compiling to work with sufficiently high fit order // Check the code is compiling to work with sufficiently high fit order
if (fit_order_ + 1 > MAX_POLY_COEFFICIENTS) { if (fit_order_ + 1 > MAX_POLY_COEFFICIENTS) {

View file

@ -5,10 +5,7 @@
#include <cstdlib> #include <cstdlib>
#include <numeric> #include <numeric>
#include "xtensor/xbuilder.hpp" #include "openmc/tensor.h"
#include "xtensor/xindex_view.hpp"
#include "xtensor/xmath.hpp"
#include "xtensor/xview.hpp"
#include "openmc/constants.h" #include "openmc/constants.h"
#include "openmc/error.h" #include "openmc/error.h"
@ -37,32 +34,32 @@ XsData::XsData(bool fissionable, AngleDistributionType scatter_format,
} }
// allocate all [temperature][angle][in group] quantities // allocate all [temperature][angle][in group] quantities
vector<size_t> shape {n_ang, n_g_}; vector<size_t> shape {n_ang, n_g_};
total = xt::zeros<double>(shape); total = tensor::zeros<double>(shape);
absorption = xt::zeros<double>(shape); absorption = tensor::zeros<double>(shape);
inverse_velocity = xt::zeros<double>(shape); inverse_velocity = tensor::zeros<double>(shape);
if (fissionable) { if (fissionable) {
fission = xt::zeros<double>(shape); fission = tensor::zeros<double>(shape);
nu_fission = xt::zeros<double>(shape); nu_fission = tensor::zeros<double>(shape);
prompt_nu_fission = xt::zeros<double>(shape); prompt_nu_fission = tensor::zeros<double>(shape);
kappa_fission = xt::zeros<double>(shape); kappa_fission = tensor::zeros<double>(shape);
} }
// allocate decay_rate; [temperature][angle][delayed group] // allocate decay_rate; [temperature][angle][delayed group]
shape[1] = n_dg_; shape[1] = n_dg_;
decay_rate = xt::zeros<double>(shape); decay_rate = tensor::zeros<double>(shape);
if (fissionable) { if (fissionable) {
shape = {n_ang, n_dg_, n_g_}; shape = {n_ang, n_dg_, n_g_};
// allocate delayed_nu_fission; [temperature][angle][delay group][in group] // allocate delayed_nu_fission; [temperature][angle][delay group][in group]
delayed_nu_fission = xt::zeros<double>(shape); delayed_nu_fission = tensor::zeros<double>(shape);
// chi_prompt; [temperature][angle][in group][out group] // chi_prompt; [temperature][angle][in group][out group]
shape = {n_ang, n_g_, n_g_}; shape = {n_ang, n_g_, n_g_};
chi_prompt = xt::zeros<double>(shape); chi_prompt = tensor::zeros<double>(shape);
// chi_delayed; [temperature][angle][delay group][in group][out group] // chi_delayed; [temperature][angle][delay group][in group][out group]
shape = {n_ang, n_dg_, n_g_, n_g_}; shape = {n_ang, n_dg_, n_g_, n_g_};
chi_delayed = xt::zeros<double>(shape); chi_delayed = tensor::zeros<double>(shape);
} }
for (int a = 0; a < n_ang; a++) { for (int a = 0; a < n_ang; a++) {
@ -85,28 +82,30 @@ void XsData::from_hdf5(hid_t xsdata_grp, bool fissionable,
{ {
// Reconstruct the dimension information so it doesn't need to be passed // Reconstruct the dimension information so it doesn't need to be passed
size_t n_ang = n_pol * n_azi; size_t n_ang = n_pol * n_azi;
size_t energy_groups = total.shape()[1]; size_t energy_groups = total.shape(1);
// Set the fissionable-specific data // Set the fissionable-specific data
if (fissionable) { if (fissionable) {
fission_from_hdf5(xsdata_grp, n_ang, is_isotropic); fission_from_hdf5(xsdata_grp, n_ang, is_isotropic);
} }
// Get the non-fission-specific data // Get the non-fission-specific data
read_nd_vector(xsdata_grp, "decay-rate", decay_rate); read_nd_tensor(xsdata_grp, "decay-rate", decay_rate);
read_nd_vector(xsdata_grp, "absorption", absorption, true); read_nd_tensor(xsdata_grp, "absorption", absorption, true);
read_nd_vector(xsdata_grp, "inverse-velocity", inverse_velocity); read_nd_tensor(xsdata_grp, "inverse-velocity", inverse_velocity);
// Get scattering data // Get scattering data
scatter_from_hdf5( scatter_from_hdf5(
xsdata_grp, n_ang, scatter_format, final_scatter_format, order_data); xsdata_grp, n_ang, scatter_format, final_scatter_format, order_data);
// Check absorption to ensure it is not 0 since it is often the // Replace zero absorption values with a small number to avoid
// denominator in tally methods // division by zero in tally methods
xt::filtration(absorption, xt::equal(absorption, 0.)) = 1.e-10; for (size_t i = 0; i < absorption.size(); i++)
if (absorption.data()[i] == 0.0)
absorption.data()[i] = 1.e-10;
// Get or calculate the total x/s // Get or calculate the total x/s
if (object_exists(xsdata_grp, "total")) { if (object_exists(xsdata_grp, "total")) {
read_nd_vector(xsdata_grp, "total", total); read_nd_tensor(xsdata_grp, "total", total);
} else { } else {
for (size_t a = 0; a < n_ang; a++) { for (size_t a = 0; a < n_ang; a++) {
for (size_t gin = 0; gin < energy_groups; gin++) { for (size_t gin = 0; gin < energy_groups; gin++) {
@ -115,8 +114,11 @@ void XsData::from_hdf5(hid_t xsdata_grp, bool fissionable,
} }
} }
// Fix if total is 0, since it is in the denominator when tallying // Replace zero total cross sections with a small number to avoid
xt::filtration(total, xt::equal(total, 0.)) = 1.e-10; // division by zero in tally methods
for (size_t i = 0; i < total.size(); i++)
if (total.data()[i] == 0.0)
total.data()[i] = 1.e-10;
} }
//============================================================================== //==============================================================================
@ -127,21 +129,30 @@ void XsData::fission_vector_beta_from_hdf5(
// Data is provided as nu-fission and chi with a beta for delayed info // Data is provided as nu-fission and chi with a beta for delayed info
// Get chi // Get chi
xt::xtensor<double, 2> temp_chi({n_ang, n_g_}, 0.); tensor::Tensor<double> temp_chi = tensor::zeros<double>({n_ang, n_g_});
read_nd_vector(xsdata_grp, "chi", temp_chi, true); read_nd_tensor(xsdata_grp, "chi", temp_chi, true);
// Normalize chi by summing over the outgoing groups for each incoming angle // Normalize chi so it sums to 1 over outgoing groups for each angle
temp_chi /= xt::view(xt::sum(temp_chi, {1}), xt::all(), xt::newaxis()); for (size_t a = 0; a < n_ang; a++) {
tensor::View<double> row = temp_chi.slice(a);
row /= row.sum();
}
// Now every incoming group in prompt_chi and delayed_chi is the normalized // Replicate the energy spectrum across all incoming groups — the
// chi we just made // spectrum is independent of the incoming neutron energy
chi_prompt = xt::view(temp_chi, xt::all(), xt::newaxis(), xt::all()); for (size_t a = 0; a < n_ang; a++)
chi_delayed = for (size_t gin = 0; gin < n_g_; gin++)
xt::view(temp_chi, xt::all(), xt::newaxis(), xt::newaxis(), xt::all()); chi_prompt.slice(a, gin) = temp_chi.slice(a);
// Same spectrum for delayed neutrons, replicated across delayed groups
for (size_t a = 0; a < n_ang; a++)
for (size_t d = 0; d < n_dg_; d++)
for (size_t gin = 0; gin < n_g_; gin++)
chi_delayed.slice(a, d, gin) = temp_chi.slice(a);
// Get nu-fission // Get nu-fission
xt::xtensor<double, 2> temp_nufiss({n_ang, n_g_}, 0.); tensor::Tensor<double> temp_nufiss = tensor::zeros<double>({n_ang, n_g_});
read_nd_vector(xsdata_grp, "nu-fission", temp_nufiss, true); read_nd_tensor(xsdata_grp, "nu-fission", temp_nufiss, true);
// Get beta (strategy will depend upon the number of dimensions in beta) // Get beta (strategy will depend upon the number of dimensions in beta)
hid_t beta_dset = open_dataset(xsdata_grp, "beta"); hid_t beta_dset = open_dataset(xsdata_grp, "beta");
@ -151,26 +162,39 @@ void XsData::fission_vector_beta_from_hdf5(
if (!is_isotropic) if (!is_isotropic)
ndim_target += 2; ndim_target += 2;
if (beta_ndims == ndim_target) { if (beta_ndims == ndim_target) {
xt::xtensor<double, 2> temp_beta({n_ang, n_dg_}, 0.); tensor::Tensor<double> temp_beta = tensor::zeros<double>({n_ang, n_dg_});
read_nd_vector(xsdata_grp, "beta", temp_beta, true); read_nd_tensor(xsdata_grp, "beta", temp_beta, true);
// Set prompt_nu_fission = (1. - beta_total)*nu_fission // prompt_nu_fission = (1 - sum_of_beta) * nu_fission
prompt_nu_fission = temp_nufiss * (1. - xt::sum(temp_beta, {1})); auto beta_sum = temp_beta.sum(1);
for (size_t a = 0; a < n_ang; a++)
for (size_t g = 0; g < n_g_; g++)
prompt_nu_fission(a, g) = temp_nufiss(a, g) * (1.0 - beta_sum(a));
// Set delayed_nu_fission as beta * nu_fission // Delayed nu-fission is the outer product of the delayed neutron
delayed_nu_fission = // fraction (beta) and the fission production rate (nu-fission)
xt::view(temp_beta, xt::all(), xt::all(), xt::newaxis()) * for (size_t a = 0; a < n_ang; a++)
xt::view(temp_nufiss, xt::all(), xt::newaxis(), xt::all()); for (size_t d = 0; d < n_dg_; d++)
for (size_t g = 0; g < n_g_; g++)
delayed_nu_fission(a, d, g) = temp_beta(a, d) * temp_nufiss(a, g);
} else if (beta_ndims == ndim_target + 1) { } else if (beta_ndims == ndim_target + 1) {
xt::xtensor<double, 3> temp_beta({n_ang, n_dg_, n_g_}, 0.); tensor::Tensor<double> temp_beta =
read_nd_vector(xsdata_grp, "beta", temp_beta, true); tensor::zeros<double>({n_ang, n_dg_, n_g_});
read_nd_tensor(xsdata_grp, "beta", temp_beta, true);
// Set prompt_nu_fission = (1. - beta_total)*nu_fission // prompt_nu_fission = (1 - sum_of_beta) * nu_fission
prompt_nu_fission = temp_nufiss * (1. - xt::sum(temp_beta, {1})); // Here beta is energy-dependent, so sum over delayed groups (axis 1)
auto beta_sum = temp_beta.sum(1);
for (size_t a = 0; a < n_ang; a++)
for (size_t g = 0; g < n_g_; g++)
prompt_nu_fission(a, g) = temp_nufiss(a, g) * (1.0 - beta_sum(a, g));
// Set delayed_nu_fission as beta * nu_fission // Delayed nu-fission: beta is already energy-dependent [n_ang, n_dg, n_g],
delayed_nu_fission = // so scale each delayed group's beta by the total nu-fission for that group
temp_beta * xt::view(temp_nufiss, xt::all(), xt::newaxis(), xt::all()); for (size_t a = 0; a < n_ang; a++)
for (size_t d = 0; d < n_dg_; d++)
for (size_t g = 0; g < n_g_; g++)
delayed_nu_fission(a, d, g) = temp_beta(a, d, g) * temp_nufiss(a, g);
} }
} }
@ -179,29 +203,42 @@ void XsData::fission_vector_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang)
// Data is provided separately as prompt + delayed nu-fission and chi // Data is provided separately as prompt + delayed nu-fission and chi
// Get chi-prompt // Get chi-prompt
xt::xtensor<double, 2> temp_chi_p({n_ang, n_g_}, 0.); tensor::Tensor<double> temp_chi_p = tensor::zeros<double>({n_ang, n_g_});
read_nd_vector(xsdata_grp, "chi-prompt", temp_chi_p, true); read_nd_tensor(xsdata_grp, "chi-prompt", temp_chi_p, true);
// Normalize chi by summing over the outgoing groups for each incoming angle // Normalize prompt chi so it sums to 1 over outgoing groups for each angle
temp_chi_p /= xt::view(xt::sum(temp_chi_p, {1}), xt::all(), xt::newaxis()); for (size_t a = 0; a < n_ang; a++) {
tensor::View<double> row = temp_chi_p.slice(a);
row /= row.sum();
}
// Get chi-delayed // Get chi-delayed
xt::xtensor<double, 3> temp_chi_d({n_ang, n_dg_, n_g_}, 0.); tensor::Tensor<double> temp_chi_d =
read_nd_vector(xsdata_grp, "chi-delayed", temp_chi_d, true); tensor::zeros<double>({n_ang, n_dg_, n_g_});
read_nd_tensor(xsdata_grp, "chi-delayed", temp_chi_d, true);
// Normalize chi by summing over the outgoing groups for each incoming angle // Normalize delayed chi so it sums to 1 over outgoing groups for each
temp_chi_d /= // angle and delayed group
xt::view(xt::sum(temp_chi_d, {2}), xt::all(), xt::all(), xt::newaxis()); for (size_t a = 0; a < n_ang; a++)
for (size_t d = 0; d < n_dg_; d++) {
tensor::View<double> row = temp_chi_d.slice(a, d);
row /= row.sum();
}
// Now assign the prompt and delayed chis by replicating for each incoming // Replicate the prompt spectrum across all incoming groups
// group for (size_t a = 0; a < n_ang; a++)
chi_prompt = xt::view(temp_chi_p, xt::all(), xt::newaxis(), xt::all()); for (size_t gin = 0; gin < n_g_; gin++)
chi_delayed = chi_prompt.slice(a, gin) = temp_chi_p.slice(a);
xt::view(temp_chi_d, xt::all(), xt::all(), xt::newaxis(), xt::all());
// Replicate the delayed spectrum across all incoming groups
for (size_t a = 0; a < n_ang; a++)
for (size_t d = 0; d < n_dg_; d++)
for (size_t gin = 0; gin < n_g_; gin++)
chi_delayed.slice(a, d, gin) = temp_chi_d.slice(a, d);
// Get prompt and delayed nu-fission directly // Get prompt and delayed nu-fission directly
read_nd_vector(xsdata_grp, "prompt-nu-fission", prompt_nu_fission, true); read_nd_tensor(xsdata_grp, "prompt-nu-fission", prompt_nu_fission, true);
read_nd_vector(xsdata_grp, "delayed-nu-fission", delayed_nu_fission, true); read_nd_tensor(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) void XsData::fission_vector_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang)
@ -210,17 +247,22 @@ void XsData::fission_vector_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang)
// Therefore, the code only considers the data as prompt. // Therefore, the code only considers the data as prompt.
// Get chi // Get chi
xt::xtensor<double, 2> temp_chi({n_ang, n_g_}, 0.); tensor::Tensor<double> temp_chi = tensor::zeros<double>({n_ang, n_g_});
read_nd_vector(xsdata_grp, "chi", temp_chi, true); read_nd_tensor(xsdata_grp, "chi", temp_chi, true);
// Normalize chi by summing over the outgoing groups for each incoming angle // Normalize chi so it sums to 1 over outgoing groups for each angle
temp_chi /= xt::view(xt::sum(temp_chi, {1}), xt::all(), xt::newaxis()); for (size_t a = 0; a < n_ang; a++) {
tensor::View<double> row = temp_chi.slice(a);
row /= row.sum();
}
// Now every incoming group in self.chi is the normalized chi we just made // Replicate the energy spectrum across all incoming groups
chi_prompt = xt::view(temp_chi, xt::all(), xt::newaxis(), xt::all()); for (size_t a = 0; a < n_ang; a++)
for (size_t gin = 0; gin < n_g_; gin++)
chi_prompt.slice(a, gin) = temp_chi.slice(a);
// Get nu-fission directly // Get nu-fission directly
read_nd_vector(xsdata_grp, "nu-fission", prompt_nu_fission, true); read_nd_tensor(xsdata_grp, "nu-fission", prompt_nu_fission, true);
} }
//============================================================================== //==============================================================================
@ -231,8 +273,9 @@ void XsData::fission_matrix_beta_from_hdf5(
// Data is provided as nu-fission and chi with a beta for delayed info // Data is provided as nu-fission and chi with a beta for delayed info
// Get nu-fission matrix // Get nu-fission matrix
xt::xtensor<double, 3> temp_matrix({n_ang, n_g_, n_g_}, 0.); tensor::Tensor<double> temp_matrix =
read_nd_vector(xsdata_grp, "nu-fission", temp_matrix, true); tensor::zeros<double>({n_ang, n_g_, n_g_});
read_nd_tensor(xsdata_grp, "nu-fission", temp_matrix, true);
// Get beta (strategy will depend upon the number of dimensions in beta) // Get beta (strategy will depend upon the number of dimensions in beta)
hid_t beta_dset = open_dataset(xsdata_grp, "beta"); hid_t beta_dset = open_dataset(xsdata_grp, "beta");
@ -242,65 +285,92 @@ void XsData::fission_matrix_beta_from_hdf5(
if (!is_isotropic) if (!is_isotropic)
ndim_target += 2; ndim_target += 2;
if (beta_ndims == ndim_target) { if (beta_ndims == ndim_target) {
xt::xtensor<double, 2> temp_beta({n_ang, n_dg_}, 0.); tensor::Tensor<double> temp_beta = tensor::zeros<double>({n_ang, n_dg_});
read_nd_vector(xsdata_grp, "beta", temp_beta, true); read_nd_tensor(xsdata_grp, "beta", temp_beta, true);
xt::xtensor<double, 1> temp_beta_sum({n_ang}, 0.); auto beta_sum = temp_beta.sum(1);
temp_beta_sum = xt::sum(temp_beta, {1}); auto matrix_gout_sum = temp_matrix.sum(2);
// prompt_nu_fission is the sum of this matrix over outgoing groups and // prompt_nu_fission = sum_gout(matrix) * (1 - beta_total)
// multiplied by (1 - beta_sum) for (size_t a = 0; a < n_ang; a++)
prompt_nu_fission = xt::sum(temp_matrix, {2}) * (1. - temp_beta_sum); for (size_t g = 0; g < n_g_; g++)
prompt_nu_fission(a, g) = matrix_gout_sum(a, g) * (1.0 - beta_sum(a));
// Store chi-prompt // chi_prompt = (1 - beta_total) * nu-fission matrix (unnormalized)
chi_prompt = for (size_t a = 0; a < n_ang; a++)
xt::view(1.0 - temp_beta_sum, xt::all(), xt::newaxis(), xt::newaxis()) * for (size_t gin = 0; gin < n_g_; gin++)
temp_matrix; for (size_t gout = 0; gout < n_g_; gout++)
chi_prompt(a, gin, gout) =
(1.0 - beta_sum(a)) * temp_matrix(a, gin, gout);
// delayed_nu_fission is the sum of this matrix over outgoing groups and // Delayed nu-fission is the outer product of the delayed neutron
// multiplied by beta // fraction (beta) and the total fission rate summed over outgoing groups
delayed_nu_fission = for (size_t a = 0; a < n_ang; a++)
xt::view(temp_beta, xt::all(), xt::all(), xt::newaxis()) * for (size_t d = 0; d < n_dg_; d++)
xt::view(xt::sum(temp_matrix, {2}), xt::all(), xt::newaxis(), xt::all()); for (size_t g = 0; g < n_g_; g++)
delayed_nu_fission(a, d, g) = temp_beta(a, d) * matrix_gout_sum(a, g);
// Store chi-delayed // chi_delayed = beta * nu-fission matrix, expanded across delayed groups
chi_delayed = for (size_t a = 0; a < n_ang; a++)
xt::view(temp_beta, xt::all(), xt::all(), xt::newaxis(), xt::newaxis()) * for (size_t d = 0; d < n_dg_; d++)
xt::view(temp_matrix, xt::all(), xt::newaxis(), xt::all(), xt::all()); for (size_t gin = 0; gin < n_g_; gin++)
for (size_t gout = 0; gout < n_g_; gout++)
chi_delayed(a, d, gin, gout) =
temp_beta(a, d) * temp_matrix(a, gin, gout);
} else if (beta_ndims == ndim_target + 1) { } else if (beta_ndims == ndim_target + 1) {
xt::xtensor<double, 3> temp_beta({n_ang, n_dg_, n_g_}, 0.); tensor::Tensor<double> temp_beta =
read_nd_vector(xsdata_grp, "beta", temp_beta, true); tensor::zeros<double>({n_ang, n_dg_, n_g_});
read_nd_tensor(xsdata_grp, "beta", temp_beta, true);
xt::xtensor<double, 2> temp_beta_sum({n_ang, n_g_}, 0.); auto beta_sum = temp_beta.sum(1);
temp_beta_sum = xt::sum(temp_beta, {1}); auto matrix_gout_sum = temp_matrix.sum(2);
// prompt_nu_fission is the sum of this matrix over outgoing groups and // prompt_nu_fission = sum_gout(matrix) * (1 - beta_total)
// multiplied by (1 - beta_sum) // Here beta is energy-dependent, so beta_sum is 2D [n_ang, n_g]
prompt_nu_fission = xt::sum(temp_matrix, {2}) * (1. - temp_beta_sum); for (size_t a = 0; a < n_ang; a++)
for (size_t g = 0; g < n_g_; g++)
prompt_nu_fission(a, g) =
matrix_gout_sum(a, g) * (1.0 - beta_sum(a, g));
// Store chi-prompt // chi_prompt = (1 - beta_sum) * nu-fission matrix (unnormalized)
chi_prompt = for (size_t a = 0; a < n_ang; a++)
xt::view(1.0 - temp_beta_sum, xt::all(), xt::all(), xt::newaxis()) * for (size_t gin = 0; gin < n_g_; gin++)
temp_matrix; for (size_t gout = 0; gout < n_g_; gout++)
chi_prompt(a, gin, gout) =
(1.0 - beta_sum(a, gin)) * temp_matrix(a, gin, gout);
// delayed_nu_fission is the sum of this matrix over outgoing groups and // Delayed nu-fission: beta is energy-dependent [n_ang, n_dg, n_g],
// multiplied by beta // scale by total fission rate summed over outgoing groups
delayed_nu_fission = temp_beta * xt::view(xt::sum(temp_matrix, {2}), for (size_t a = 0; a < n_ang; a++)
xt::all(), xt::newaxis(), xt::all()); for (size_t d = 0; d < n_dg_; d++)
for (size_t g = 0; g < n_g_; g++)
delayed_nu_fission(a, d, g) =
temp_beta(a, d, g) * matrix_gout_sum(a, g);
// Store chi-delayed // chi_delayed = beta * nu-fission matrix, expanded across delayed groups
chi_delayed = for (size_t a = 0; a < n_ang; a++)
xt::view(temp_beta, xt::all(), xt::all(), xt::all(), xt::newaxis()) * for (size_t d = 0; d < n_dg_; d++)
xt::view(temp_matrix, xt::all(), xt::newaxis(), xt::all(), xt::all()); for (size_t gin = 0; gin < n_g_; gin++)
for (size_t gout = 0; gout < n_g_; gout++)
chi_delayed(a, d, gin, gout) =
temp_beta(a, d, gin) * temp_matrix(a, gin, gout);
} }
// Normalize both chis // Normalize chi_prompt so it sums to 1 over outgoing groups
chi_prompt /= for (size_t a = 0; a < n_ang; a++)
xt::view(xt::sum(chi_prompt, {2}), xt::all(), xt::all(), xt::newaxis()); for (size_t gin = 0; gin < n_g_; gin++) {
tensor::View<double> row = chi_prompt.slice(a, gin);
row /= row.sum();
}
chi_delayed /= xt::view( // Normalize chi_delayed so it sums to 1 over outgoing groups
xt::sum(chi_delayed, {3}), xt::all(), xt::all(), xt::all(), xt::newaxis()); for (size_t a = 0; a < n_ang; a++)
for (size_t d = 0; d < n_dg_; d++)
for (size_t gin = 0; gin < n_g_; gin++) {
tensor::View<double> row = chi_delayed.slice(a, d, gin);
row /= row.sum();
}
} }
void XsData::fission_matrix_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) void XsData::fission_matrix_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang)
@ -308,28 +378,36 @@ void XsData::fission_matrix_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang)
// Data is provided separately as prompt + delayed nu-fission and chi // Data is provided separately as prompt + delayed nu-fission and chi
// Get the prompt nu-fission matrix // Get the prompt nu-fission matrix
xt::xtensor<double, 3> temp_matrix_p({n_ang, n_g_, n_g_}, 0.); tensor::Tensor<double> temp_matrix_p =
read_nd_vector(xsdata_grp, "prompt-nu-fission", temp_matrix_p, true); tensor::zeros<double>({n_ang, n_g_, n_g_});
read_nd_tensor(xsdata_grp, "prompt-nu-fission", temp_matrix_p, true);
// prompt_nu_fission is the sum over outgoing groups // prompt_nu_fission is the sum over outgoing groups
prompt_nu_fission = xt::sum(temp_matrix_p, {2}); prompt_nu_fission = temp_matrix_p.sum(2);
// chi_prompt is this matrix but normalized over outgoing groups, which we // chi_prompt is the nu-fission matrix normalized over outgoing groups
// have already stored in prompt_nu_fission for (size_t a = 0; a < n_ang; a++)
chi_prompt = temp_matrix_p / for (size_t gin = 0; gin < n_g_; gin++)
xt::view(prompt_nu_fission, xt::all(), xt::all(), xt::newaxis()); for (size_t gout = 0; gout < n_g_; gout++)
chi_prompt(a, gin, gout) =
temp_matrix_p(a, gin, gout) / prompt_nu_fission(a, gin);
// Get the delayed nu-fission matrix // Get the delayed nu-fission matrix
xt::xtensor<double, 4> temp_matrix_d({n_ang, n_dg_, n_g_, n_g_}, 0.); tensor::Tensor<double> temp_matrix_d =
read_nd_vector(xsdata_grp, "delayed-nu-fission", temp_matrix_d, true); tensor::zeros<double>({n_ang, n_dg_, n_g_, n_g_});
read_nd_tensor(xsdata_grp, "delayed-nu-fission", temp_matrix_d, true);
// delayed_nu_fission is the sum over outgoing groups // delayed_nu_fission is the sum over outgoing groups
delayed_nu_fission = xt::sum(temp_matrix_d, {3}); delayed_nu_fission = temp_matrix_d.sum(3);
// chi_prompt is this matrix but normalized over outgoing groups, which we // chi_delayed is the delayed nu-fission matrix normalized over outgoing
// have already stored in prompt_nu_fission // groups
chi_delayed = temp_matrix_d / xt::view(delayed_nu_fission, xt::all(), for (size_t a = 0; a < n_ang; a++)
xt::all(), xt::all(), xt::newaxis()); for (size_t d = 0; d < n_dg_; d++)
for (size_t gin = 0; gin < n_g_; gin++)
for (size_t gout = 0; gout < n_g_; gout++)
chi_delayed(a, d, gin, gout) =
temp_matrix_d(a, d, gin, gout) / delayed_nu_fission(a, d, gin);
} }
void XsData::fission_matrix_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang) void XsData::fission_matrix_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang)
@ -338,16 +416,19 @@ void XsData::fission_matrix_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang)
// Therefore, the code only considers the data as prompt. // Therefore, the code only considers the data as prompt.
// Get nu-fission matrix // Get nu-fission matrix
xt::xtensor<double, 3> temp_matrix({n_ang, n_g_, n_g_}, 0.); tensor::Tensor<double> temp_matrix =
read_nd_vector(xsdata_grp, "nu-fission", temp_matrix, true); tensor::zeros<double>({n_ang, n_g_, n_g_});
read_nd_tensor(xsdata_grp, "nu-fission", temp_matrix, true);
// prompt_nu_fission is the sum over outgoing groups // prompt_nu_fission is the sum over outgoing groups
prompt_nu_fission = xt::sum(temp_matrix, {2}); prompt_nu_fission = temp_matrix.sum(2);
// chi_prompt is this matrix but normalized over outgoing groups, which we // chi_prompt is the nu-fission matrix normalized over outgoing groups
// have already stored in prompt_nu_fission for (size_t a = 0; a < n_ang; a++)
chi_prompt = temp_matrix / for (size_t gin = 0; gin < n_g_; gin++)
xt::view(prompt_nu_fission, xt::all(), xt::all(), xt::newaxis()); for (size_t gout = 0; gout < n_g_; gout++)
chi_prompt(a, gin, gout) =
temp_matrix(a, gin, gout) / prompt_nu_fission(a, gin);
} }
//============================================================================== //==============================================================================
@ -356,8 +437,8 @@ void XsData::fission_from_hdf5(
hid_t xsdata_grp, size_t n_ang, bool is_isotropic) hid_t xsdata_grp, size_t n_ang, bool is_isotropic)
{ {
// Get the fission and kappa_fission data xs; these are optional // Get the fission and kappa_fission data xs; these are optional
read_nd_vector(xsdata_grp, "fission", fission); read_nd_tensor(xsdata_grp, "fission", fission);
read_nd_vector(xsdata_grp, "kappa-fission", kappa_fission); read_nd_tensor(xsdata_grp, "kappa-fission", kappa_fission);
// Get the data; the strategy for doing so depends on if the data is provided // 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 // as a nu-fission matrix or a set of chi and nu-fission vectors
@ -388,7 +469,7 @@ void XsData::fission_from_hdf5(
if (n_dg_ == 0) { if (n_dg_ == 0) {
nu_fission = prompt_nu_fission; nu_fission = prompt_nu_fission;
} else { } else {
nu_fission = prompt_nu_fission + xt::sum(delayed_nu_fission, {1}); nu_fission = prompt_nu_fission + delayed_nu_fission.sum(1);
} }
} }
@ -404,10 +485,10 @@ void XsData::scatter_from_hdf5(hid_t xsdata_grp, size_t n_ang,
hid_t scatt_grp = open_group(xsdata_grp, "scatter_data"); hid_t scatt_grp = open_group(xsdata_grp, "scatter_data");
// Get the outgoing group boundary indices // Get the outgoing group boundary indices
xt::xtensor<int, 2> gmin({n_ang, n_g_}, 0.); tensor::Tensor<int> gmin = tensor::zeros<int>({n_ang, n_g_});
read_nd_vector(scatt_grp, "g_min", gmin, true); read_nd_tensor(scatt_grp, "g_min", gmin, true);
xt::xtensor<int, 2> gmax({n_ang, n_g_}, 0.); tensor::Tensor<int> gmax = tensor::zeros<int>({n_ang, n_g_});
read_nd_vector(scatt_grp, "g_max", gmax, true); read_nd_tensor(scatt_grp, "g_max", gmax, true);
// Make gmin and gmax start from 0 vice 1 as they do in the library // Make gmin and gmax start from 0 vice 1 as they do in the library
gmin -= 1; gmin -= 1;
@ -415,11 +496,11 @@ void XsData::scatter_from_hdf5(hid_t xsdata_grp, size_t n_ang,
// Now use this info to find the length of a vector to hold the flattened // Now use this info to find the length of a vector to hold the flattened
// data. // data.
size_t length = order_data * xt::sum(gmax - gmin + 1)(); size_t length = order_data * (gmax - gmin + 1).sum();
double_4dvec input_scatt(n_ang, double_3dvec(n_g_)); double_4dvec input_scatt(n_ang, double_3dvec(n_g_));
xt::xtensor<double, 1> temp_arr({length}, 0.); tensor::Tensor<double> temp_arr = tensor::zeros<double>({length});
read_nd_vector(scatt_grp, "scatter_matrix", temp_arr, true); read_nd_tensor(scatt_grp, "scatter_matrix", temp_arr, true);
// Compare the number of orders given with the max order of the problem; // Compare the number of orders given with the max order of the problem;
// strip off the superfluous orders if needed // strip off the superfluous orders if needed
@ -451,7 +532,7 @@ void XsData::scatter_from_hdf5(hid_t xsdata_grp, size_t n_ang,
double_3dvec temp_mult(n_ang, double_2dvec(n_g_)); double_3dvec temp_mult(n_ang, double_2dvec(n_g_));
if (object_exists(scatt_grp, "multiplicity_matrix")) { 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); read_nd_tensor(scatt_grp, "multiplicity_matrix", temp_arr);
// convert the flat temp_arr to a jagged array for passing to scatt data // convert the flat temp_arr to a jagged array for passing to scatt data
size_t temp_idx = 0; size_t temp_idx = 0;
@ -481,8 +562,8 @@ void XsData::scatter_from_hdf5(hid_t xsdata_grp, size_t n_ang,
final_scatter_format == AngleDistributionType::TABULAR) { final_scatter_format == AngleDistributionType::TABULAR) {
for (size_t a = 0; a < n_ang; a++) { for (size_t a = 0; a < n_ang; a++) {
ScattDataLegendre legendre_scatt; ScattDataLegendre legendre_scatt;
xt::xtensor<int, 1> in_gmin = xt::view(gmin, a, xt::all()); tensor::Tensor<int> in_gmin(gmin.slice(a));
xt::xtensor<int, 1> in_gmax = xt::view(gmax, a, xt::all()); tensor::Tensor<int> in_gmax(gmax.slice(a));
legendre_scatt.init(in_gmin, in_gmax, temp_mult[a], input_scatt[a]); legendre_scatt.init(in_gmin, in_gmax, temp_mult[a], input_scatt[a]);
@ -496,8 +577,8 @@ void XsData::scatter_from_hdf5(hid_t xsdata_grp, size_t n_ang,
// We are sticking with the current representation // We are sticking with the current representation
// Initialize the ScattData object with this data // Initialize the ScattData object with this data
for (size_t a = 0; a < n_ang; a++) { for (size_t a = 0; a < n_ang; a++) {
xt::xtensor<int, 1> in_gmin = xt::view(gmin, a, xt::all()); tensor::Tensor<int> in_gmin(gmin.slice(a));
xt::xtensor<int, 1> in_gmax = xt::view(gmax, a, xt::all()); tensor::Tensor<int> in_gmax(gmax.slice(a));
scatter[a]->init(in_gmin, in_gmax, temp_mult[a], input_scatt[a]); scatter[a]->init(in_gmin, in_gmax, temp_mult[a], input_scatt[a]);
} }
} }
@ -519,33 +600,67 @@ void XsData::combine(
if (i == 0) { if (i == 0) {
inverse_velocity = that->inverse_velocity; inverse_velocity = that->inverse_velocity;
} }
if (that->prompt_nu_fission.shape()[0] > 0) { if (!that->prompt_nu_fission.empty()) {
nu_fission += scalar * that->nu_fission; nu_fission += scalar * that->nu_fission;
prompt_nu_fission += scalar * that->prompt_nu_fission; prompt_nu_fission += scalar * that->prompt_nu_fission;
kappa_fission += scalar * that->kappa_fission; kappa_fission += scalar * that->kappa_fission;
fission += scalar * that->fission; fission += scalar * that->fission;
delayed_nu_fission += scalar * that->delayed_nu_fission; delayed_nu_fission += scalar * that->delayed_nu_fission;
chi_prompt += scalar * // Accumulate chi_prompt weighted by total prompt nu-fission
xt::view(xt::sum(that->prompt_nu_fission, {1}), xt::all(), // (summed over energy groups) for this constituent
xt::newaxis(), xt::newaxis()) * {
that->chi_prompt; auto pnf_sum = that->prompt_nu_fission.sum(1);
chi_delayed += scalar * size_t n_ang = chi_prompt.shape(0);
xt::view(xt::sum(that->delayed_nu_fission, {2}), xt::all(), size_t n_g = chi_prompt.shape(1);
xt::all(), xt::newaxis(), xt::newaxis()) * for (size_t a = 0; a < n_ang; a++)
that->chi_delayed; for (size_t gin = 0; gin < n_g; gin++)
for (size_t gout = 0; gout < n_g; gout++)
chi_prompt(a, gin, gout) +=
scalar * pnf_sum(a) * that->chi_prompt(a, gin, gout);
}
// Accumulate chi_delayed weighted by total delayed nu-fission
// (summed over energy groups) for this constituent
{
auto dnf_sum = that->delayed_nu_fission.sum(2);
size_t n_ang = chi_delayed.shape(0);
size_t n_dg = chi_delayed.shape(1);
size_t n_g = chi_delayed.shape(2);
for (size_t a = 0; a < n_ang; a++)
for (size_t d = 0; d < n_dg; d++)
for (size_t gin = 0; gin < n_g; gin++)
for (size_t gout = 0; gout < n_g; gout++)
chi_delayed(a, d, gin, gout) +=
scalar * dnf_sum(a, d) * that->chi_delayed(a, d, gin, gout);
}
} }
decay_rate += scalar * that->decay_rate; decay_rate += scalar * that->decay_rate;
} }
// Ensure the chi_prompt and chi_delayed are normalized to 1 for each // Normalize chi_prompt so it sums to 1 over outgoing groups
// azimuthal angle and delayed group (for chi_delayed) {
chi_prompt /= size_t n_ang = chi_prompt.shape(0);
xt::view(xt::sum(chi_prompt, {2}), xt::all(), xt::all(), xt::newaxis()); size_t n_g = chi_prompt.shape(1);
chi_delayed /= xt::view( for (size_t a = 0; a < n_ang; a++)
xt::sum(chi_delayed, {3}), xt::all(), xt::all(), xt::all(), xt::newaxis()); for (size_t gin = 0; gin < n_g; gin++) {
tensor::View<double> row = chi_prompt.slice(a, gin);
row /= row.sum();
}
}
// Normalize chi_delayed so it sums to 1 over outgoing groups
{
size_t n_ang = chi_delayed.shape(0);
size_t n_dg = chi_delayed.shape(1);
size_t n_g = chi_delayed.shape(2);
for (size_t a = 0; a < n_ang; a++)
for (size_t d = 0; d < n_dg; d++)
for (size_t gin = 0; gin < n_g; gin++) {
tensor::View<double> row = chi_delayed.slice(a, d, gin);
row /= row.sum();
}
}
// Allow the ScattData object to combine itself // Allow the ScattData object to combine itself
for (size_t a = 0; a < total.shape()[0]; a++) { for (size_t a = 0; a < total.shape(0); a++) {
// Build vector of the scattering objects to incorporate // Build vector of the scattering objects to incorporate
vector<ScattData*> those_scatts(those_xs.size()); vector<ScattData*> those_scatts(those_xs.size());
for (size_t i = 0; i < those_xs.size(); i++) { for (size_t i = 0; i < those_xs.size(); i++) {

View file

@ -7,6 +7,7 @@ set(TEST_NAMES
test_mcpl_stat_sum test_mcpl_stat_sum
test_mesh test_mesh
test_region test_region
test_tensor
# Add additional unit test files here # Add additional unit test files here
) )

View file

@ -0,0 +1,987 @@
#include <cmath>
#include <vector>
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include "openmc/tensor.h"
using namespace openmc;
using namespace openmc::tensor;
// ============================================================================
// Tensor constructors
// ============================================================================
TEST_CASE("Tensor default constructor")
{
Tensor<double> t;
REQUIRE(t.size() == 0);
REQUIRE(t.empty());
REQUIRE(t.shape().empty());
}
TEST_CASE("Tensor shape constructor")
{
Tensor<double> t1({5});
REQUIRE(t1.size() == 5);
REQUIRE(t1.shape().size() == 1);
REQUIRE(t1.shape(0) == 5);
Tensor<double> t2({3, 4});
REQUIRE(t2.size() == 12);
REQUIRE(t2.shape().size() == 2);
REQUIRE(t2.shape(0) == 3);
REQUIRE(t2.shape(1) == 4);
Tensor<int> t3({2, 3, 4});
REQUIRE(t3.size() == 24);
REQUIRE(t3.shape().size() == 3);
}
TEST_CASE("Tensor shape + fill constructor")
{
Tensor<double> t({2, 3}, 7.0);
REQUIRE(t.size() == 6);
for (size_t i = 0; i < t.size(); ++i)
REQUIRE(t[i] == 7.0);
}
TEST_CASE("Tensor pointer constructor")
{
double vals[] = {1.0, 2.0, 3.0, 4.0};
Tensor<double> t(vals, 4);
REQUIRE(t.size() == 4);
REQUIRE(t.shape(0) == 4);
REQUIRE(t[0] == 1.0);
REQUIRE(t[1] == 2.0);
REQUIRE(t[2] == 3.0);
REQUIRE(t[3] == 4.0);
}
TEST_CASE("Tensor copy and move")
{
Tensor<double> a({2, 3}, 5.0);
Tensor<double> b(a);
REQUIRE(b.size() == 6);
REQUIRE(b(0, 0) == 5.0);
// Modifying copy doesn't affect original
b(0, 0) = 99.0;
REQUIRE(a(0, 0) == 5.0);
Tensor<double> c(std::move(b));
REQUIRE(c(0, 0) == 99.0);
REQUIRE(c.size() == 6);
}
// ============================================================================
// Tensor indexing
// ============================================================================
TEST_CASE("Tensor 1D indexing")
{
Tensor<int> t({4}, 0);
t[0] = 10;
t[1] = 20;
t[2] = 30;
t[3] = 40;
REQUIRE(t(0) == 10);
REQUIRE(t(1) == 20);
REQUIRE(t(2) == 30);
REQUIRE(t(3) == 40);
}
TEST_CASE("Tensor 2D indexing (row-major)")
{
// Layout: [[1, 2, 3], [4, 5, 6]]
Tensor<int> t({2, 3}, 0);
int val = 1;
for (size_t i = 0; i < 2; ++i)
for (size_t j = 0; j < 3; ++j)
t(i, j) = val++;
REQUIRE(t(0, 0) == 1);
REQUIRE(t(0, 2) == 3);
REQUIRE(t(1, 0) == 4);
REQUIRE(t(1, 2) == 6);
// Flat index should match row-major order
REQUIRE(t[0] == 1);
REQUIRE(t[3] == 4);
REQUIRE(t[5] == 6);
}
TEST_CASE("Tensor 3D indexing")
{
// 2x3x4 tensor
Tensor<int> t({2, 3, 4}, 0);
t(1, 2, 3) = 42;
// Flat index: 1*12 + 2*4 + 3 = 23
REQUIRE(t[23] == 42);
REQUIRE(t(1, 2, 3) == 42);
}
// ============================================================================
// Tensor assignment
// ============================================================================
TEST_CASE("Tensor initializer_list assignment")
{
Tensor<double> t;
t = {1.0, 2.0, 3.0};
REQUIRE(t.size() == 3);
REQUIRE(t.shape(0) == 3);
REQUIRE(t[0] == 1.0);
REQUIRE(t[2] == 3.0);
}
// ============================================================================
// Tensor mutation
// ============================================================================
TEST_CASE("Tensor resize")
{
Tensor<double> t({2, 3}, 1.0);
REQUIRE(t.size() == 6);
t.resize({4, 5});
REQUIRE(t.size() == 20);
REQUIRE(t.shape(0) == 4);
REQUIRE(t.shape(1) == 5);
}
TEST_CASE("Tensor reshape")
{
Tensor<int> t({12}, 0);
for (size_t i = 0; i < 12; ++i)
t[i] = static_cast<int>(i);
t.reshape({3, 4});
REQUIRE(t.shape(0) == 3);
REQUIRE(t.shape(1) == 4);
REQUIRE(t.size() == 12);
// Data unchanged, just reinterpreted
REQUIRE(t(0, 0) == 0);
REQUIRE(t(1, 0) == 4); // row 1, col 0 = flat index 4
REQUIRE(t(2, 3) == 11); // row 2, col 3 = flat index 11
}
TEST_CASE("Tensor fill")
{
Tensor<double> t({3, 3}, 0.0);
t.fill(42.0);
for (size_t i = 0; i < t.size(); ++i)
REQUIRE(t[i] == 42.0);
}
// ============================================================================
// Tensor iterators
// ============================================================================
TEST_CASE("Tensor iterators")
{
Tensor<int> t({4}, 0);
t = {10, 20, 30, 40};
int sum = 0;
for (auto val : t)
sum += val;
REQUIRE(sum == 100);
}
// ============================================================================
// Tensor reductions
// ============================================================================
TEST_CASE("Tensor sum (full)")
{
Tensor<double> t({3}, 0.0);
t = {1.0, 2.0, 3.0};
REQUIRE(t.sum() == 6.0);
}
TEST_CASE("Tensor sum (axis) on 2D")
{
// [[1, 2, 3],
// [4, 5, 6]]
Tensor<int> t({2, 3}, 0);
int v = 1;
for (size_t i = 0; i < 2; ++i)
for (size_t j = 0; j < 3; ++j)
t(i, j) = v++;
// Sum along axis 0 -> [5, 7, 9]
Tensor<int> s0 = t.sum(0);
REQUIRE(s0.size() == 3);
REQUIRE(s0[0] == 5);
REQUIRE(s0[1] == 7);
REQUIRE(s0[2] == 9);
// Sum along axis 1 -> [6, 15]
Tensor<int> s1 = t.sum(1);
REQUIRE(s1.size() == 2);
REQUIRE(s1[0] == 6);
REQUIRE(s1[1] == 15);
}
TEST_CASE("Tensor sum (axis) on 3D")
{
// 2x3x2 tensor filled with sequential values 1..12
Tensor<int> t({2, 3, 2}, 0);
int v = 1;
for (size_t i = 0; i < 2; ++i)
for (size_t j = 0; j < 3; ++j)
for (size_t k = 0; k < 2; ++k)
t(i, j, k) = v++;
// Sum along axis 1 (middle) -> 2x2, each sums 3 values
// [0,0]: t(0,0,0)+t(0,1,0)+t(0,2,0) = 1+3+5 = 9
// [0,1]: t(0,0,1)+t(0,1,1)+t(0,2,1) = 2+4+6 = 12
// [1,0]: t(1,0,0)+t(1,1,0)+t(1,2,0) = 7+9+11 = 27
// [1,1]: t(1,0,1)+t(1,1,1)+t(1,2,1) = 8+10+12 = 30
Tensor<int> s = t.sum(1);
REQUIRE(s.shape(0) == 2);
REQUIRE(s.shape(1) == 2);
REQUIRE(s(0, 0) == 9);
REQUIRE(s(0, 1) == 12);
REQUIRE(s(1, 0) == 27);
REQUIRE(s(1, 1) == 30);
}
TEST_CASE("Tensor prod")
{
Tensor<int> t({4}, 0);
t = {1, 2, 3, 4};
REQUIRE(t.prod() == 24);
}
TEST_CASE("Tensor any and all")
{
Tensor<bool> t({4}, false);
REQUIRE(!t.any());
REQUIRE(!t.all());
// Set one element true
t.data()[0] = true;
REQUIRE(t.any());
REQUIRE(!t.all());
// Set all true
for (size_t i = 0; i < t.size(); ++i)
t.data()[i] = true;
REQUIRE(t.any());
REQUIRE(t.all());
}
TEST_CASE("Tensor argmin")
{
Tensor<double> t({5}, 0.0);
t = {3.0, 1.0, 4.0, 0.5, 2.0};
REQUIRE(t.argmin() == 3);
}
TEST_CASE("Tensor flip")
{
Tensor<int> t({5}, 0);
t = {1, 2, 3, 4, 5};
Tensor<int> f = t.flip(0);
REQUIRE(f[0] == 5);
REQUIRE(f[1] == 4);
REQUIRE(f[2] == 3);
REQUIRE(f[3] == 2);
REQUIRE(f[4] == 1);
}
TEST_CASE("Tensor flip 2D")
{
// [[1, 2], [3, 4], [5, 6]]
Tensor<int> t({3, 2}, 0);
t(0, 0) = 1;
t(0, 1) = 2;
t(1, 0) = 3;
t(1, 1) = 4;
t(2, 0) = 5;
t(2, 1) = 6;
// Flip axis 0 reverses rows -> [[5,6],[3,4],[1,2]]
Tensor<int> f = t.flip(0);
REQUIRE(f(0, 0) == 5);
REQUIRE(f(0, 1) == 6);
REQUIRE(f(1, 0) == 3);
REQUIRE(f(2, 0) == 1);
}
// ============================================================================
// Tensor operators
// ============================================================================
TEST_CASE("Tensor scalar compound assignment")
{
Tensor<double> t({3}, 0.0);
t = {2.0, 4.0, 6.0};
t += 1.0;
REQUIRE(t[0] == 3.0);
REQUIRE(t[1] == 5.0);
t -= 1.0;
REQUIRE(t[0] == 2.0);
t *= 3.0;
REQUIRE(t[0] == 6.0);
REQUIRE(t[1] == 12.0);
t /= 2.0;
REQUIRE(t[0] == 3.0);
REQUIRE(t[1] == 6.0);
}
TEST_CASE("Tensor element-wise arithmetic")
{
Tensor<double> a({3}, 0.0);
Tensor<double> b({3}, 0.0);
a = {1.0, 2.0, 3.0};
b = {4.0, 5.0, 6.0};
Tensor<double> c = a + b;
REQUIRE(c[0] == 5.0);
REQUIRE(c[1] == 7.0);
REQUIRE(c[2] == 9.0);
c = a - b;
REQUIRE(c[0] == -3.0);
c = a / b;
REQUIRE(c[0] == 0.25);
}
TEST_CASE("Tensor scalar arithmetic")
{
Tensor<double> a({3}, 0.0);
a = {1.0, 2.0, 3.0};
Tensor<double> b = a + 10.0;
REQUIRE(b[0] == 11.0);
REQUIRE(b[2] == 13.0);
b = a - 1.0;
REQUIRE(b[0] == 0.0);
b = a * 2.0;
REQUIRE(b[0] == 2.0);
REQUIRE(b[2] == 6.0);
// Non-member scalar * tensor (commutativity)
b = 2.0 * a;
REQUIRE(b[0] == 2.0);
REQUIRE(b[2] == 6.0);
// Non-member scalar + tensor
b = 10.0 + a;
REQUIRE(b[0] == 11.0);
}
TEST_CASE("Tensor compound addition with tensor")
{
Tensor<double> a({3}, 0.0);
Tensor<double> b({3}, 0.0);
a = {1.0, 2.0, 3.0};
b = {10.0, 20.0, 30.0};
a += b;
REQUIRE(a[0] == 11.0);
REQUIRE(a[1] == 22.0);
REQUIRE(a[2] == 33.0);
}
TEST_CASE("Tensor comparison operators")
{
Tensor<double> t({4}, 0.0);
t = {1.0, 2.0, 3.0, 4.0};
Tensor<bool> r = t < 3.0;
REQUIRE(r.data()[0] == true);
REQUIRE(r.data()[1] == true);
REQUIRE(r.data()[2] == false);
REQUIRE(r.data()[3] == false);
r = t >= 3.0;
REQUIRE(r.data()[0] == false);
REQUIRE(r.data()[2] == true);
REQUIRE(r.data()[3] == true);
r = t <= 2.0;
REQUIRE(r.data()[0] == true);
REQUIRE(r.data()[1] == true);
REQUIRE(r.data()[2] == false);
r = t > 3.0;
REQUIRE(r.data()[0] == false);
REQUIRE(r.data()[3] == true);
}
TEST_CASE("Tensor element-wise comparison")
{
Tensor<double> a({3}, 0.0);
Tensor<double> b({3}, 0.0);
a = {1.0, 5.0, 3.0};
b = {2.0, 4.0, 3.0};
Tensor<bool> r = a < b;
REQUIRE(r.data()[0] == true);
REQUIRE(r.data()[1] == false);
REQUIRE(r.data()[2] == false);
}
TEST_CASE("Tensor mixed-type multiply")
{
Tensor<int> a({3}, 0);
Tensor<double> b({3}, 0.0);
a = {2, 3, 4};
b = {1.5, 2.5, 3.5};
Tensor<double> c = a * b;
REQUIRE(c[0] == 3.0);
REQUIRE(c[1] == 7.5);
REQUIRE(c[2] == 14.0);
}
TEST_CASE("Tensor mixed-type divide")
{
Tensor<double> a({3}, 0.0);
Tensor<int> b({3}, 0);
a = {10.0, 20.0, 30.0};
b = {2, 4, 5};
Tensor<double> c = a / b;
REQUIRE(c[0] == 5.0);
REQUIRE(c[1] == 5.0);
REQUIRE(c[2] == 6.0);
}
// ============================================================================
// Tensor bool specialization
// ============================================================================
TEST_CASE("Tensor<bool> storage")
{
// Tensor<bool> uses unsigned char internally to avoid std::vector<bool> proxy
Tensor<bool> t({4}, false);
t.data()[0] = true;
t.data()[2] = true;
REQUIRE(t.any());
REQUIRE(!t.all());
REQUIRE(t.data()[0] == true);
REQUIRE(t.data()[1] == false);
}
// ============================================================================
// View (via Tensor accessors)
// ============================================================================
TEST_CASE("Tensor slice axis 0 (2D)")
{
// [[1, 2, 3], [4, 5, 6]]
Tensor<int> t({2, 3}, 0);
int v = 1;
for (size_t i = 0; i < 2; ++i)
for (size_t j = 0; j < 3; ++j)
t(i, j) = v++;
auto r0 = t.slice(0);
REQUIRE(r0.size() == 3);
REQUIRE(r0[0] == 1);
REQUIRE(r0[1] == 2);
REQUIRE(r0[2] == 3);
auto r1 = t.slice(1);
REQUIRE(r1[0] == 4);
REQUIRE(r1[1] == 5);
REQUIRE(r1[2] == 6);
// Writing through view modifies the tensor
r0[1] = 99;
REQUIRE(t(0, 1) == 99);
}
TEST_CASE("Tensor slice axis 1 (2D)")
{
// [[1, 2], [3, 4], [5, 6]]
Tensor<int> t({3, 2}, 0);
t(0, 0) = 1;
t(0, 1) = 2;
t(1, 0) = 3;
t(1, 1) = 4;
t(2, 0) = 5;
t(2, 1) = 6;
auto c0 = t.slice(all, 0);
REQUIRE(c0.size() == 3);
REQUIRE(c0[0] == 1);
REQUIRE(c0[1] == 3);
REQUIRE(c0[2] == 5);
auto c1 = t.slice(all, 1);
REQUIRE(c1[0] == 2);
REQUIRE(c1[1] == 4);
REQUIRE(c1[2] == 6);
// Write through column view
c1[0] = 77;
REQUIRE(t(0, 1) == 77);
}
TEST_CASE("Tensor slice with range")
{
Tensor<int> t({6}, 0);
t = {10, 20, 30, 40, 50, 60};
// range(start, end)
auto s = t.slice(range(1, 4));
REQUIRE(s.size() == 3);
REQUIRE(s[0] == 20);
REQUIRE(s[1] == 30);
REQUIRE(s[2] == 40);
// range(end) from start — range(3) means [0, 3)
auto s2 = t.slice(range(3));
REQUIRE(s2.size() == 3);
REQUIRE(s2[0] == 10);
REQUIRE(s2[2] == 30);
// range(start, SIZE_MAX) to end
auto s3 = t.slice(range(3, 6));
REQUIRE(s3.size() == 3);
REQUIRE(s3[0] == 40);
REQUIRE(s3[2] == 60);
// Write through slice
s[0] = 99;
REQUIRE(t[1] == 99);
}
TEST_CASE("Tensor flat view")
{
Tensor<int> t({2, 3}, 0);
int v = 1;
for (size_t i = 0; i < 2; ++i)
for (size_t j = 0; j < 3; ++j)
t(i, j) = v++;
auto f = t.flat();
REQUIRE(f.size() == 6);
REQUIRE(f[0] == 1);
REQUIRE(f[5] == 6);
}
TEST_CASE("Tensor slice on 3D")
{
// 2x3x4 tensor
Tensor<int> t({2, 3, 4}, 0);
int v = 0;
for (size_t i = 0; i < 2; ++i)
for (size_t j = 0; j < 3; ++j)
for (size_t k = 0; k < 4; ++k)
t(i, j, k) = v++;
// slice(1) -> fix axis 0 at 1 -> 3x4 view
auto s = t.slice(1);
REQUIRE(s.size() == 12);
// t(1,0,0) = 12, t(1,0,1) = 13, ...
REQUIRE(s(0, 0) == 12);
REQUIRE(s(0, 1) == 13);
REQUIRE(s(2, 3) == 23);
// slice(all, 2) -> fix axis 1 at 2 -> 2x4 view
auto s2 = t.slice(all, 2);
REQUIRE(s2.size() == 8);
// t(0,2,0)=8, t(0,2,1)=9, t(1,2,0)=20
REQUIRE(s2(0, 0) == 8);
REQUIRE(s2(0, 1) == 9);
REQUIRE(s2(1, 0) == 20);
}
TEST_CASE("Tensor multi-axis slice")
{
// 2x3x4 tensor with sequential values
Tensor<int> t({2, 3, 4}, 0);
int v = 0;
for (size_t i = 0; i < 2; ++i)
for (size_t j = 0; j < 3; ++j)
for (size_t k = 0; k < 4; ++k)
t(i, j, k) = v++;
// slice(1, 2) -> fix axes 0 and 1 -> 1D view of 4 elements
// Equivalent to numpy t[1, 2, :] -> t(1,2,0..3) = [20, 21, 22, 23]
auto s = t.slice(1, 2);
REQUIRE(s.size() == 4);
REQUIRE(s[0] == 20);
REQUIRE(s[1] == 21);
REQUIRE(s[3] == 23);
// slice(all, 1, range(1, 3)) -> keep axis 0, fix axis 1, range on axis 2
// Equivalent to numpy t[:, 1, 1:3]
// t(0,1,1)=5, t(0,1,2)=6, t(1,1,1)=17, t(1,1,2)=18
auto s2 = t.slice(all, 1, range(1, 3));
REQUIRE(s2.size() == 4);
REQUIRE(s2.ndim() == 2);
REQUIRE(s2(0, 0) == 5);
REQUIRE(s2(0, 1) == 6);
REQUIRE(s2(1, 0) == 17);
REQUIRE(s2(1, 1) == 18);
// slice(0, range(0, 2)) -> fix axis 0 at 0, range on axis 1
// Equivalent to numpy t[0, 0:2, :] -> shape (2, 4)
auto s3 = t.slice(0, range(0, 2));
REQUIRE(s3.ndim() == 2);
REQUIRE(s3.shape(0) == 2);
REQUIRE(s3.shape(1) == 4);
REQUIRE(s3(0, 0) == 0); // t(0,0,0)
REQUIRE(s3(1, 3) == 7); // t(0,1,3)
}
// ============================================================================
// View assignment and arithmetic
// ============================================================================
TEST_CASE("View scalar assignment (fill)")
{
Tensor<double> t({2, 3}, 0.0);
auto r = t.slice(0);
r = 7.0;
REQUIRE(t(0, 0) == 7.0);
REQUIRE(t(0, 1) == 7.0);
REQUIRE(t(0, 2) == 7.0);
REQUIRE(t(1, 0) == 0.0); // Other row unchanged
}
TEST_CASE("View initializer_list assignment")
{
Tensor<double> t({2, 3}, 0.0);
auto r = t.slice(1);
r = {10.0, 20.0, 30.0};
REQUIRE(t(1, 0) == 10.0);
REQUIRE(t(1, 1) == 20.0);
REQUIRE(t(1, 2) == 30.0);
}
TEST_CASE("View copy assignment (deep copy)")
{
Tensor<double> t({2, 3}, 0.0);
t.slice(0) = {1.0, 2.0, 3.0};
t.slice(1) = {4.0, 5.0, 6.0};
// Copy row 0 into row 1
t.slice(1) = t.slice(0);
REQUIRE(t(1, 0) == 1.0);
REQUIRE(t(1, 1) == 2.0);
REQUIRE(t(1, 2) == 3.0);
}
TEST_CASE("View compound operators")
{
Tensor<double> t({2, 3}, 0.0);
t.slice(0) = {1.0, 2.0, 3.0};
t.slice(0) *= 2.0;
REQUIRE(t(0, 0) == 2.0);
REQUIRE(t(0, 1) == 4.0);
t.slice(0) /= 2.0;
REQUIRE(t(0, 0) == 1.0);
REQUIRE(t(0, 1) == 2.0);
}
TEST_CASE("View assignment from tensor")
{
Tensor<double> t({2, 3}, 0.0);
Tensor<double> vals({3}, 0.0);
vals = {7.0, 8.0, 9.0};
t.slice(1) = vals;
REQUIRE(t(1, 0) == 7.0);
REQUIRE(t(1, 1) == 8.0);
REQUIRE(t(1, 2) == 9.0);
}
TEST_CASE("View compound addition from tensor")
{
Tensor<double> t({2, 3}, 0.0);
t.slice(0) = {1.0, 2.0, 3.0};
Tensor<double> vals({3}, 0.0);
vals = {10.0, 20.0, 30.0};
t.slice(0) += vals;
REQUIRE(t(0, 0) == 11.0);
REQUIRE(t(0, 1) == 22.0);
REQUIRE(t(0, 2) == 33.0);
}
TEST_CASE("View sum")
{
Tensor<double> t({2, 3}, 0.0);
t.slice(0) = {1.0, 2.0, 3.0};
t.slice(1) = {4.0, 5.0, 6.0};
REQUIRE(t.slice(0).sum() == 6.0);
REQUIRE(t.slice(1).sum() == 15.0);
}
TEST_CASE("View iteration")
{
Tensor<int> t({2, 3}, 0);
t.slice(0) = {1, 2, 3};
int sum = 0;
for (auto val : t.slice(0))
sum += val;
REQUIRE(sum == 6);
}
TEST_CASE("View sub-slice")
{
Tensor<int> t({6}, 0);
t = {10, 20, 30, 40, 50, 60};
auto s = t.slice(range(1, 5)); // [20, 30, 40, 50]
auto ss = s.slice(range(1, 3)); // [30, 40]
REQUIRE(ss.size() == 2);
REQUIRE(ss[0] == 30);
REQUIRE(ss[1] == 40);
}
TEST_CASE("Tensor from View")
{
Tensor<double> t({2, 3}, 0.0);
t.slice(0) = {1.0, 2.0, 3.0};
// Construct a new tensor from a view (copies data)
Tensor<double> t2(t.slice(0));
REQUIRE(t2.size() == 3);
REQUIRE(t2[0] == 1.0);
REQUIRE(t2[2] == 3.0);
// Modifying the new tensor doesn't affect the original
t2[0] = 99.0;
REQUIRE(t(0, 0) == 1.0);
}
// ============================================================================
// Const View
// ============================================================================
TEST_CASE("Const tensor produces const views")
{
Tensor<double> t({2, 3}, 0.0);
int v = 1;
for (size_t i = 0; i < 2; ++i)
for (size_t j = 0; j < 3; ++j)
t(i, j) = v++;
const Tensor<double>& ct = t;
auto r = ct.slice(0); // View<const double>
REQUIRE(r[0] == 1.0);
REQUIRE(r[2] == 3.0);
auto c = ct.slice(all, 1);
REQUIRE(c[0] == 2.0);
REQUIRE(c[1] == 5.0);
}
// ============================================================================
// StaticTensor2D
// ============================================================================
TEST_CASE("StaticTensor2D basics")
{
StaticTensor2D<double, 3, 4> t;
REQUIRE(t.size() == 12);
REQUIRE(t.shape()[0] == 3);
REQUIRE(t.shape()[1] == 4);
// Default-initialized to zero
REQUIRE(t(0, 0) == 0.0);
t(1, 2) = 42.0;
REQUIRE(t(1, 2) == 42.0);
// Flat data: row 1, col 2 = index 1*4 + 2 = 6
REQUIRE(t.data()[6] == 42.0);
}
TEST_CASE("StaticTensor2D fill")
{
StaticTensor2D<int, 2, 3> t;
t.fill(5);
for (size_t i = 0; i < t.size(); ++i)
REQUIRE(t.data()[i] == 5);
}
TEST_CASE("StaticTensor2D iteration")
{
StaticTensor2D<int, 2, 3> t;
t.fill(1);
int sum = 0;
for (auto val : t)
sum += val;
REQUIRE(sum == 6);
}
TEST_CASE("StaticTensor2D slice")
{
StaticTensor2D<int, 3, 2> t;
t(0, 0) = 1;
t(0, 1) = 2;
t(1, 0) = 3;
t(1, 1) = 4;
t(2, 0) = 5;
t(2, 1) = 6;
// slice(1) = row 1 (fix axis 0 at 1)
auto r1 = t.slice(1);
REQUIRE(r1.size() == 2);
REQUIRE(r1[0] == 3);
REQUIRE(r1[1] == 4);
// slice(all, 0) = column 0 (fix axis 1 at 0)
auto c0 = t.slice(all, 0);
REQUIRE(c0.size() == 3);
REQUIRE(c0[0] == 1);
REQUIRE(c0[1] == 3);
REQUIRE(c0[2] == 5);
}
TEST_CASE("StaticTensor2D flat view")
{
StaticTensor2D<double, 2, 2> t;
t(0, 0) = 1.0;
t(0, 1) = 2.0;
t(1, 0) = 3.0;
t(1, 1) = 4.0;
auto f = t.flat();
REQUIRE(f.size() == 4);
f = 0.0;
REQUIRE(t(0, 0) == 0.0);
REQUIRE(t(1, 1) == 0.0);
}
// ============================================================================
// Non-member functions
// ============================================================================
TEST_CASE("zeros")
{
auto t = zeros<double>({3, 4});
REQUIRE(t.size() == 12);
for (size_t i = 0; i < t.size(); ++i)
REQUIRE(t[i] == 0.0);
}
TEST_CASE("zeros_like")
{
Tensor<double> a({2, 5}, 7.0);
auto b = zeros_like(a);
REQUIRE(b.size() == 10);
REQUIRE(b.shape(0) == 2);
REQUIRE(b.shape(1) == 5);
for (size_t i = 0; i < b.size(); ++i)
REQUIRE(b[i] == 0.0);
}
TEST_CASE("full_like")
{
Tensor<int> a({4}, 0);
auto b = full_like(a, 42);
REQUIRE(b.size() == 4);
for (size_t i = 0; i < b.size(); ++i)
REQUIRE(b[i] == 42);
}
TEST_CASE("linspace")
{
auto t = linspace(0.0, 1.0, 5);
REQUIRE(t.size() == 5);
REQUIRE(t[0] == 0.0);
REQUIRE(t[4] == 1.0);
REQUIRE_THAT(t[1], Catch::Matchers::WithinRel(0.25, 1e-12));
REQUIRE_THAT(t[2], Catch::Matchers::WithinRel(0.5, 1e-12));
REQUIRE_THAT(t[3], Catch::Matchers::WithinRel(0.75, 1e-12));
}
TEST_CASE("concatenate")
{
Tensor<int> a({3}, 0);
Tensor<int> b({2}, 0);
a = {1, 2, 3};
b = {4, 5};
auto c = concatenate(a, b);
REQUIRE(c.size() == 5);
REQUIRE(c[0] == 1);
REQUIRE(c[2] == 3);
REQUIRE(c[3] == 4);
REQUIRE(c[4] == 5);
}
TEST_CASE("log")
{
Tensor<double> t({3}, 0.0);
t = {1.0, std::exp(1.0), std::exp(2.0)};
auto r = log(t);
REQUIRE_THAT(r[0], Catch::Matchers::WithinAbs(0.0, 1e-12));
REQUIRE_THAT(r[1], Catch::Matchers::WithinAbs(1.0, 1e-12));
REQUIRE_THAT(r[2], Catch::Matchers::WithinAbs(2.0, 1e-12));
}
TEST_CASE("abs")
{
Tensor<double> t({4}, 0.0);
t = {-3.0, -1.0, 0.0, 2.0};
auto r = abs(t);
REQUIRE(r[0] == 3.0);
REQUIRE(r[1] == 1.0);
REQUIRE(r[2] == 0.0);
REQUIRE(r[3] == 2.0);
}
TEST_CASE("where")
{
Tensor<bool> cond({4}, false);
cond.data()[0] = true;
cond.data()[2] = true;
Tensor<double> vals({4}, 0.0);
vals = {10.0, 20.0, 30.0, 40.0};
auto r = where(cond, vals, -1.0);
REQUIRE(r[0] == 10.0);
REQUIRE(r[1] == -1.0);
REQUIRE(r[2] == 30.0);
REQUIRE(r[3] == -1.0);
}
TEST_CASE("nan_to_num")
{
Tensor<double> t({4}, 0.0);
t[0] = 1.0;
t[1] = std::nan("");
t[2] = std::numeric_limits<double>::infinity();
t[3] = -std::numeric_limits<double>::infinity();
auto r = nan_to_num(t);
REQUIRE(r[0] == 1.0);
REQUIRE(r[1] == 0.0); // NaN -> 0
REQUIRE(r[2] == std::numeric_limits<double>::max()); // +inf -> max
REQUIRE(r[3] == std::numeric_limits<double>::lowest()); // -inf -> lowest
}
// ============================================================================
// is_tensor trait
// ============================================================================
TEST_CASE("is_tensor trait")
{
REQUIRE(is_tensor<Tensor<double>>::value);
REQUIRE(is_tensor<Tensor<int>>::value);
REQUIRE(is_tensor<StaticTensor2D<double, 3, 3>>::value);
REQUIRE(!is_tensor<double>::value);
REQUIRE(!is_tensor<std::vector<double>>::value);
}

View file

@ -2,6 +2,8 @@
#include <mpi.h> #include <mpi.h>
#endif #endif
#include <cassert>
#include "openmc/capi.h" #include "openmc/capi.h"
#include "openmc/cell.h" #include "openmc/cell.h"
#include "openmc/error.h" #include "openmc/error.h"

1
vendor/xtensor vendored

@ -1 +0,0 @@
Subproject commit 3634f2ded19e0cf38208c8b86cea9e1d7c8e397d

1
vendor/xtl vendored

@ -1 +0,0 @@
Subproject commit a7c1c5444dfc57f76620391af4c94785ff82c8d6