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"]
path = vendor/pugixml
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"]
path = vendor/fmt
url = https://github.com/fmtlib/fmt.git

View file

@ -266,23 +266,6 @@ else()
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
#===============================================================================
@ -498,7 +481,7 @@ endif()
# target_link_libraries treats any arguments starting with - but not -l as
# linker flags. Thus, we can pass both linker flags and libraries together.
target_link_libraries(libopenmc ${ldflags} ${HDF5_LIBRARIES} ${HDF5_HL_LIBRARIES}
xtensor fmt::fmt ${CMAKE_DL_LIBS})
fmt::fmt ${CMAKE_DL_LIBS})
if(TARGET 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(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@)
find_package(DAGMC REQUIRED HINTS @DAGMC_DIR@)
endif()

View file

@ -119,7 +119,7 @@ packages should be installed, for example in Homebrew via:
.. 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
one provisioned by XCode, which does not support the multithreading library used

View file

@ -3,7 +3,7 @@
#include "openmc/particle.h"
#include "xtensor/xtensor.hpp"
#include "openmc/tensor.h"
namespace openmc {
@ -14,9 +14,9 @@ namespace openmc {
class BremsstrahlungData {
public:
// Data
xt::xtensor<double, 2> pdf; //!< Bremsstrahlung energy PDF
xt::xtensor<double, 2> cdf; //!< Bremsstrahlung energy CDF
xt::xtensor<double, 1> yield; //!< Photon yield
tensor::Tensor<double> pdf; //!< Bremsstrahlung energy PDF
tensor::Tensor<double> cdf; //!< Bremsstrahlung energy CDF
tensor::Tensor<double> yield; //!< Photon yield
};
class Bremsstrahlung {
@ -32,9 +32,9 @@ public:
namespace data {
extern xt::xtensor<double, 1>
extern tensor::Tensor<double>
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
} // namespace data

View file

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

View file

@ -6,7 +6,7 @@
#include <cstdint> // for int64_t
#include "xtensor/xtensor.hpp"
#include "openmc/tensor.h"
#include <hdf5.h>
#include "openmc/array.h"
@ -24,7 +24,7 @@ namespace simulation {
extern double keff_generation; //!< Single-generation k on each processor
extern array<double, 2> k_sum; //!< Used to reduce sum and sum_sq
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

View file

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

View file

@ -5,8 +5,8 @@
#include <unordered_map>
#include "openmc/span.h"
#include "openmc/tensor.h"
#include "pugixml.hpp"
#include "xtensor/xtensor.hpp"
#include <hdf5.h>
#include "openmc/bremsstrahlung.h"
@ -189,7 +189,7 @@ public:
vector<int> nuclide_; //!< Indices in nuclides vector
vector<int> element_; //!< Indices in elements vector
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_gpcc_; //!< Total atom density in [g/cm^3]
double charge_density_; //!< Total charge density in [e/b-cm]

View file

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

View file

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

View file

@ -96,7 +96,7 @@ public:
// Temperature dependent cross section data
vector<double> kTs_; //!< temperatures in eV (k*T)
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
unique_ptr<WindowedMultipole> multipole_;

View file

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

View file

@ -6,8 +6,8 @@
#include <unordered_map>
#include <unordered_set>
#include "openmc/tensor.h"
#include "pugixml.hpp"
#include "xtensor/xarray.hpp"
#include "hdf5.h"
#include "openmc/cell.h"
@ -90,7 +90,7 @@ const RGBColor BLACK {0, 0, 0};
* visualized.
*/
typedef xt::xtensor<RGBColor, 2> ImageData;
typedef tensor::Tensor<RGBColor> ImageData;
class PlottableInterface {
public:
PlottableInterface() = default;
@ -154,7 +154,7 @@ struct IdData {
void set_overlap(size_t y, size_t x);
// 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 {
@ -166,7 +166,7 @@ struct PropertyData {
void set_overlap(size_t y, size_t x);
// 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
// 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
// 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
// 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

View file

@ -4,7 +4,7 @@
#ifndef OPENMC_SCATTDATA_H
#define OPENMC_SCATTDATA_H
#include "xtensor/xtensor.hpp"
#include "openmc/tensor.h"
#include "openmc/constants.h"
#include "openmc/vector.h"
@ -26,23 +26,23 @@ public:
protected:
//! \brief Initializes the attributes of the base class.
void base_init(int order, const xt::xtensor<int, 1>& in_gmin,
const xt::xtensor<int, 1>& in_gmax, const double_2dvec& in_energy,
void base_init(int order, const tensor::Tensor<int>& in_gmin,
const tensor::Tensor<int>& in_gmax, const double_2dvec& in_energy,
const double_2dvec& in_mult);
//! \brief Combines microscopic ScattDatas into a macroscopic one.
void base_combine(size_t max_order, size_t order_dim,
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);
public:
double_2dvec energy; // Normalized p0 matrix for sampling Eout
double_2dvec mult; // nu-scatter multiplication (nu-scatt/scatt)
double_3dvec dist; // Angular distribution
xt::xtensor<int, 1> gmin; // minimum outgoing group
xt::xtensor<int, 1> gmax; // maximum outgoing group
xt::xtensor<double, 1> scattxs; // Isotropic Sigma_{s,g_{in}}
tensor::Tensor<int> gmin; // minimum outgoing group
tensor::Tensor<int> gmax; // maximum outgoing group
tensor::Tensor<double> scattxs; // Isotropic Sigma_{s,g_{in}}
//! \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_mult Input sparse multiplicity matrix
//! @param coeffs Input sparse scattering matrix
virtual void init(const xt::xtensor<int, 1>& in_gmin,
const xt::xtensor<int, 1>& in_gmax, const double_2dvec& in_mult,
virtual void init(const tensor::Tensor<int>& in_gmin,
const tensor::Tensor<int>& in_gmax, const double_2dvec& in_mult,
const double_3dvec& coeffs) = 0;
//! \brief Combines the microscopic data.
@ -96,7 +96,7 @@ public:
//! @param max_order If Legendre this is the maximum value of "n" in "Pn"
//! requested; ignored otherwise.
//! @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.
//!
@ -135,8 +135,8 @@ protected:
ScattDataLegendre& leg, ScattDataTabular& tab);
public:
void init(const xt::xtensor<int, 1>& in_gmin,
const xt::xtensor<int, 1>& in_gmax, const double_2dvec& in_mult,
void init(const tensor::Tensor<int>& in_gmin,
const tensor::Tensor<int>& in_gmax, const double_2dvec& in_mult,
const double_3dvec& coeffs) override;
void combine(const vector<ScattData*>& those_scatts,
@ -153,7 +153,7 @@ public:
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 {
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_3dvec fmu; // The angular distribution histogram
public:
void init(const xt::xtensor<int, 1>& in_gmin,
const xt::xtensor<int, 1>& in_gmax, const double_2dvec& in_mult,
void init(const tensor::Tensor<int>& in_gmin,
const tensor::Tensor<int>& in_gmax, const double_2dvec& in_mult,
const double_3dvec& coeffs) override;
void combine(const vector<ScattData*>& those_scatts,
@ -183,7 +183,7 @@ public:
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 {
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_3dvec fmu; // The angular distribution function
@ -204,8 +204,8 @@ protected:
ScattDataLegendre& leg, ScattDataTabular& tab);
public:
void init(const xt::xtensor<int, 1>& in_gmin,
const xt::xtensor<int, 1>& in_gmax, const double_2dvec& in_mult,
void init(const tensor::Tensor<int>& in_gmin,
const tensor::Tensor<int>& in_gmax, const double_2dvec& in_mult,
const double_3dvec& coeffs) override;
void combine(const vector<ScattData*>& those_scatts,
@ -218,7 +218,7 @@ public:
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
#include "hdf5.h"
#include "xtensor/xtensor.hpp"
#include "openmc/tensor.h"
#include "openmc/angle_energy.h"
#include "openmc/distribution.h"
@ -25,9 +25,9 @@ public:
struct CorrTable {
int n_discrete; //!< Number of discrete lines
Interpolation interpolation; //!< Interpolation law
xt::xtensor<double, 1> e_out; //!< Outgoing energies [eV]
xt::xtensor<double, 1> p; //!< Probability density
xt::xtensor<double, 1> c; //!< Cumulative distribution
tensor::Tensor<double> e_out; //!< Outgoing energies [eV]
tensor::Tensor<double> p; //!< Probability density
tensor::Tensor<double> c; //!< Cumulative distribution
vector<unique_ptr<Tabular>> angle; //!< Angle distribution
};

View file

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

View file

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

View file

@ -8,9 +8,8 @@
#include "openmc/tallies/trigger.h"
#include "openmc/vector.h"
#include "openmc/tensor.h"
#include "pugixml.hpp"
#include "xtensor/xfixed.hpp"
#include "xtensor/xtensor.hpp"
#include <string>
#include <unordered_map>
@ -55,7 +54,7 @@ public:
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
const vector<int32_t>& filters() const { return filters_; }
@ -125,7 +124,7 @@ public:
int score_index(const std::string& score) const;
//! 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
std::string score_name(int score_idx) const;
@ -160,7 +159,7 @@ public:
//! 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
//! 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
bool writable_ {true};
@ -220,8 +219,7 @@ extern vector<double> time_grid;
namespace simulation {
//! Global tallies (such as k-effective estimators)
extern xt::xtensor_fixed<double, xt::xshape<N_GLOBAL_TALLIES, 3>>
global_tallies;
extern tensor::StaticTensor2D<double, N_GLOBAL_TALLIES, 3> global_tallies;
//! Number of realizations for global tallies
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
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
//! Collect all tally results onto master process
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 <unordered_map>
#include "xtensor/xtensor.hpp"
#include "openmc/tensor.h"
#include "openmc/angle_energy.h"
#include "openmc/endf.h"

View file

@ -3,7 +3,7 @@
#ifndef OPENMC_URR_H
#define OPENMC_URR_H
#include "xtensor/xtensor.hpp"
#include "openmc/tensor.h"
#include "openmc/constants.h"
#include "openmc/hdf5_interface.h"
@ -40,11 +40,11 @@ public:
* below, obviously, values of the CDF are stored. For the xs_values
* variable, the columns line up with the index of cdf_values.
*/
xt::xtensor<double, 2> cdf_values_; // Note: must be row major!
xt::xtensor<XSSet, 2> xs_values_;
tensor::Tensor<double> cdf_values_; // Note: must be row major!
tensor::Tensor<XSSet> xs_values_;
// 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
explicit UrrData(hid_t group_id);

View file

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

View file

@ -143,10 +143,10 @@ public:
const vector<double>& energy_bounds() const { return energy_bounds_; }
void set_bounds(const xt::xtensor<double, 2>& lower_ww_bounds,
const xt::xtensor<double, 2>& upper_bounds);
void set_bounds(const tensor::Tensor<double>& lower_ww_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(
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 xt::xtensor<double, 2>& lower_ww_bounds() const { return lower_ww_; }
xt::xtensor<double, 2>& lower_ww_bounds() { return lower_ww_; }
const tensor::Tensor<double>& lower_ww_bounds() const { return lower_ww_; }
tensor::Tensor<double>& lower_ww_bounds() { return lower_ww_; }
const xt::xtensor<double, 2>& upper_ww_bounds() const { return upper_ww_; }
xt::xtensor<double, 2>& upper_ww_bounds() { return upper_ww_; }
const tensor::Tensor<double>& upper_ww_bounds() const { return upper_ww_; }
tensor::Tensor<double>& upper_ww_bounds() { return upper_ww_; }
ParticleType particle_type() const { return particle_type_; }
@ -197,9 +197,9 @@ private:
int64_t index_; //!< Index into weight windows vector
ParticleType particle_type_; //!< Particle type to apply weight windows to
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))
xt::xtensor<double, 2>
tensor::Tensor<double>
upper_ww_; //!< Upper weight window bounds (shape: energy_bins, mesh_bins)
double survival_ratio_ {3.0}; //!< Survival 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
#include "hdf5.h"
#include "xtensor/xtensor.hpp"
#include "openmc/tensor.h"
#include <complex>
#include <string>
@ -78,9 +78,9 @@ public:
int fit_order_; //!< Order of the fit
bool fissionable_; //!< Is the nuclide fissionable?
vector<WindowInfo> window_info_; // Information about a window
xt::xtensor<double, 3>
tensor::Tensor<double>
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
static constexpr int MAX_POLY_COEFFICIENTS =

View file

@ -5,9 +5,8 @@
#include <sstream> // for stringstream
#include <string>
#include "openmc/tensor.h"
#include "pugixml.hpp"
#include "xtensor/xadapt.hpp"
#include "xtensor/xarray.hpp"
#include "openmc/position.h"
#include "openmc/vector.h"
@ -42,12 +41,11 @@ vector<T> get_node_array(
}
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)
{
vector<T> v = get_node_array<T>(node, name, lowercase);
vector<std::size_t> shape = {v.size()};
return xt::adapt(v, shape);
return tensor::Tensor<T>(v.data(), v.size());
}
std::vector<Position> get_node_position_array(

View file

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

View file

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

View file

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

View file

@ -5,7 +5,7 @@
#ifdef _OPENMP
#include <omp.h>
#endif
#include "xtensor/xtensor.hpp"
#include "openmc/tensor.h"
#include "openmc/bank.h"
#include "openmc/capi.h"
@ -36,7 +36,7 @@ double spectral;
int nx, ny, nz, ng;
xt::xtensor<int, 2> indexmap;
tensor::Tensor<int> indexmap;
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
//==============================================================================
xt::xtensor<double, 1> count_bank_sites(
xt::xtensor<int, 1>& bins, bool* outside)
tensor::Tensor<double> count_bank_sites(
tensor::Tensor<int>& bins, bool* outside)
{
// Determine shape of array for counts
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
xt::xarray<double> cnt {cnt_shape, 0.0};
tensor::Tensor<double> cnt = tensor::zeros<double>({cnt_size});
bool outside_ = false;
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;
}
// 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();
double* cnt_reduced = std::allocator<double> {}.allocate(total);
tensor::Tensor<double> counts = tensor::zeros<double>({cnt_size});
#ifdef OPENMC_MPI
// collect values from all processors
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
MPI_Reduce(&outside_, outside, 1, MPI_C_BOOL, MPI_LOR, 0, mpi::intracomm);
#else
std::copy(cnt.data(), cnt.data() + total, cnt_reduced);
std::copy(cnt.data(), cnt.data() + total, counts.data());
*outside = outside_;
#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;
}
@ -151,19 +143,19 @@ extern "C" void openmc_cmfd_reweight(
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
xt::xtensor<int, 1> bank_bins({bank_size}, 0);
tensor::Tensor<int> bank_bins = tensor::zeros<int>({bank_size});
bool sites_outside;
xt::xtensor<double, 1> sourcecounts =
tensor::Tensor<double> sourcecounts =
count_bank_sites(bank_bins, &sites_outside);
// 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 (sites_outside) {
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++) {
if (sourcecounts[i] > 0 && cmfd_src[i] > 0) {
weightfactors[i] = cmfd_src[i] * norm / sourcecounts[i];
@ -561,7 +553,7 @@ void free_memory_cmfd()
cmfd::indices.clear();
cmfd::egrid.clear();
// Resize xtensors to be empty
// Resize tensors to be empty
cmfd::indexmap.resize({0});
// 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 &&
settings::electron_treatment == ElectronTreatment::TTB) {
// 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

View file

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

View file

@ -4,7 +4,7 @@
#include <cstddef> // for size_t
#include <iterator> // for back_inserter
#include "xtensor/xview.hpp"
#include "openmc/tensor.h"
#include "openmc/endf.h"
#include "openmc/hdf5_interface.h"
@ -60,11 +60,11 @@ ContinuousTabular::ContinuousTabular(hid_t group)
hid_t dset = open_dataset(group, "energy");
// Get interpolation parameters
xt::xarray<int> temp;
tensor::Tensor<int> temp;
read_attribute(dset, "interpolation", temp);
auto temp_b = xt::view(temp, 0); // view of breakpoints
auto temp_i = xt::view(temp, 1); // view of interpolation parameters
tensor::View<int> temp_b = temp.slice(0); // breakpoints
tensor::View<int> temp_i = temp.slice(1); // interpolation parameters
std::copy(temp_b.begin(), temp_b.end(), std::back_inserter(breakpoints_));
for (const auto i : temp_i)
@ -85,7 +85,7 @@ ContinuousTabular::ContinuousTabular(hid_t group)
read_attribute(dset, "interpolation", interp);
read_attribute(dset, "n_discrete_lines", n_discrete);
xt::xarray<double> eout;
tensor::Tensor<double> eout;
read_dataset(dset, eout);
close_dataset(dset);
@ -96,7 +96,7 @@ ContinuousTabular::ContinuousTabular(hid_t group)
if (i < n_energy - 1) {
n = offsets[i + 1] - j;
} else {
n = eout.shape()[1] - j;
n = eout.shape(1) - j;
}
// Assign interpolation scheme and number of discrete lines
@ -105,15 +105,15 @@ ContinuousTabular::ContinuousTabular(hid_t group)
d.n_discrete = n_discrete[i];
// Copy data
d.e_out = xt::view(eout, 0, xt::range(j, j + n));
d.p = xt::view(eout, 1, xt::range(j, j + n));
d.e_out = eout.slice(0, tensor::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
// 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
// reconstruct them using the PDF
if (true) {
d.c = xt::view(eout, 2, xt::range(j, j + n));
d.c = eout.slice(2, tensor::range(j, j + n));
} else {
// Calculate cumulative distribution function -- discrete portion
for (int k = 0; k < d.n_discrete; ++k) {

View file

@ -1,9 +1,6 @@
#include "openmc/eigenvalue.h"
#include "xtensor/xbuilder.hpp"
#include "xtensor/xmath.hpp"
#include "xtensor/xtensor.hpp"
#include "xtensor/xview.hpp"
#include "openmc/tensor.h"
#include "openmc/array.h"
#include "openmc/bank.h"
@ -39,7 +36,7 @@ namespace simulation {
double keff_generation;
array<double, 2> k_sum;
vector<double> entropy;
xt::xtensor<double, 1> source_frac;
tensor::Tensor<double> source_frac;
} // namespace simulation
@ -452,7 +449,7 @@ int openmc_get_keff(double* k_combined)
const auto& gt = simulation::global_tallies;
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[1] = gt(GlobalTally::K_ABSORPTION, 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
bool sites_outside;
xt::xtensor<double, 1> p =
tensor::Tensor<double> p =
simulation::entropy_mesh->count_sites(simulation::fission_bank.data(),
simulation::fission_bank.size(), &sites_outside);
@ -603,7 +600,7 @@ void shannon_entropy()
if (mpi::master) {
// Normalize to total weight of bank sites
p /= xt::sum(p);
p /= p.sum();
// Sum values to obtain Shannon entropy
double H = 0.0;
@ -627,7 +624,7 @@ void ufs_count_sites()
std::size_t n = simulation::ufs_mesh->n_bins();
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 {
// count number of source sites in each ufs mesh cell
@ -649,7 +646,7 @@ void ufs_count_sites()
#endif
// 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;
// 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 <stdexcept> // for runtime_error
#include "xtensor/xarray.hpp"
#include "xtensor/xview.hpp"
#include "openmc/tensor.h"
#include "openmc/array.h"
#include "openmc/constants.h"
@ -153,11 +152,11 @@ Tabulated1D::Tabulated1D(hid_t dset)
for (const auto i : int_temp)
int_.push_back(int2interp(i));
xt::xarray<double> arr;
tensor::Tensor<double> arr;
read_dataset(dset, arr);
auto xs = xt::view(arr, 0);
auto ys = xt::view(arr, 1);
tensor::View<double> xs = arr.slice(0);
tensor::View<double> ys = arr.slice(1);
std::copy(xs.begin(), xs.end(), std::back_inserter(x_));
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)
{
// Read 2D array from dataset
xt::xarray<double> arr;
tensor::Tensor<double> arr;
read_dataset(dset, arr);
// Get views for Bragg edges and structure factors
auto E = xt::view(arr, 0);
auto s = xt::view(arr, 1);
tensor::View<double> E = arr.slice(0);
tensor::View<double> s = arr.slice(1);
// Copy Bragg edges and partial sums of structure factors
std::copy(E.begin(), E.end(), std::back_inserter(bragg_edges_));

View file

@ -30,7 +30,7 @@
#include "openmc/volume_calc.h"
#include "openmc/weight_windows.h"
#include "xtensor/xview.hpp"
#include "openmc/tensor.h"
namespace openmc {
@ -203,7 +203,7 @@ int openmc_reset()
// Reset global tallies
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_tra = 0.0;

View file

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

View file

@ -8,9 +8,7 @@
#include <string>
#include <unordered_set>
#include "xtensor/xbuilder.hpp"
#include "xtensor/xoperation.hpp"
#include "xtensor/xview.hpp"
#include "openmc/tensor.h"
#include "openmc/capi.h"
#include "openmc/container_util.h"
@ -216,7 +214,7 @@ Material::Material(pugi::xml_node node)
// allocate arrays in Material object
auto n = names.size();
nuclide_.reserve(n);
atom_density_ = xt::empty<double>({n});
atom_density_ = tensor::Tensor<double>({n});
if (settings::photon_transport)
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
// 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(
"Cannot mix atom and weight percents in material " + std::to_string(id_));
}
// Determine density if it is a sum value
if (sum_density)
density_ = xt::sum(atom_density_)();
density_ = atom_density_.sum();
if (check_for_node(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
// straightforward. if given weight percents, the value is w/awr and is
// 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
// 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);
// Allocate arrays for TTB data
ttb->pdf = xt::zeros<double>({n_e, n_e});
ttb->cdf = xt::zeros<double>({n_e, n_e});
ttb->yield = xt::zeros<double>({n_e});
ttb->pdf = tensor::zeros<double>({n_e, n_e});
ttb->cdf = tensor::zeros<double>({n_e, n_e});
ttb->yield = tensor::zeros<double>({n_e});
// Allocate temporary arrays
xt::xtensor<double, 1> stopping_power_collision({n_e}, 0.0);
xt::xtensor<double, 1> stopping_power_radiative({n_e}, 0.0);
xt::xtensor<double, 2> dcs({n_e, n_k}, 0.0);
auto stopping_power_collision = tensor::zeros<double>({n_e});
auto stopping_power_radiative = tensor::zeros<double>({n_e});
auto dcs = tensor::zeros<double>({n_e, n_k});
double Z_eq_sq = 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.808e-6 * std::pow(t, 7));
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;
}
}
// Total material stopping power
xt::xtensor<double, 1> stopping_power =
tensor::Tensor<double> stopping_power =
stopping_power_collision + stopping_power_radiative;
// Loop over photon energies
xt::xtensor<double, 1> f({n_e}, 0.0);
xt::xtensor<double, 1> z({n_e}, 0.0);
auto f = tensor::zeros<double>({n_e});
auto z = tensor::zeros<double>({n_e});
for (int i = 0; i < n_e - 1; ++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
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;
// Determine normalized atom percents
double sum_percent = xt::sum(atom_density_)();
double sum_percent = atom_density_.sum();
atom_density_ /= sum_percent;
// Recalculate nuclide atom densities based on given density
@ -1020,7 +1019,7 @@ void Material::set_densities(
if (n != nuclide_.size()) {
nuclide_.resize(n);
atom_density_ = xt::zeros<double>({n});
atom_density_ = tensor::zeros<double>({n});
if (settings::photon_transport)
element_.resize(n);
}
@ -1181,8 +1180,8 @@ void Material::add_nuclide(const std::string& name, double density)
auto n = nuclide_.size();
// Create copy of atom_density_ array with one extra entry
xt::xtensor<double, 1> atom_density = xt::zeros<double>({n});
xt::view(atom_density, xt::range(0, n - 1)) = atom_density_;
tensor::Tensor<double> atom_density = tensor::zeros<double>({n});
atom_density.slice(tensor::range(0, n - 1)) = atom_density_;
atom_density(n - 1) = 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
#include <cmath> // for ceil
#include <cstddef> // for size_t
#include <numeric> // for accumulate
#include <string>
#ifdef _MSC_VER
@ -16,13 +17,7 @@
#include "mpi.h"
#endif
#include "xtensor/xadapt.hpp"
#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 "openmc/tensor.h"
#include <fmt/core.h> // for fmt
#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
auto tmp_shape = shape_;
return xt::adapt(tmp_shape, {n_dimension_});
return tensor::Tensor<int>(shape_.data(), static_cast<size_t>(n_dimension_));
}
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 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++) {
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);
@ -972,8 +966,10 @@ void UnstructuredMesh::to_hdf5_inner(hid_t mesh_group) const
// write element types and connectivity
vector<double> volumes;
xt::xtensor<int, 2> connectivity({static_cast<size_t>(this->n_bins()), 8});
xt::xtensor<int, 2> elem_types({static_cast<size_t>(this->n_bins()), 1});
tensor::Tensor<int> connectivity(
{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++) {
auto conn = this->connectivity(i);
@ -981,21 +977,18 @@ void UnstructuredMesh::to_hdf5_inner(hid_t mesh_group) const
// write linear tet element
if (conn.size() == 4) {
xt::view(elem_types, i, xt::all()) =
static_cast<int>(ElementType::LINEAR_TET);
xt::view(connectivity, i, xt::all()) =
xt::xarray<int>({conn[0], conn[1], conn[2], conn[3], -1, -1, -1, -1});
elem_types.slice(i) = static_cast<int>(ElementType::LINEAR_TET);
connectivity.slice(i) = {
conn[0], conn[1], conn[2], conn[3], -1, -1, -1, -1};
// write linear hex element
} else if (conn.size() == 8) {
xt::view(elem_types, i, xt::all()) =
static_cast<int>(ElementType::LINEAR_HEX);
xt::view(connectivity, i, xt::all()) = xt::xarray<int>({conn[0], conn[1],
conn[2], conn[3], conn[4], conn[5], conn[6], conn[7]});
elem_types.slice(i) = static_cast<int>(ElementType::LINEAR_HEX);
connectivity.slice(i) = {
conn[0], conn[1], conn[2], conn[3], conn[4], conn[5], conn[6], conn[7]};
} else {
num_elem_skipped++;
xt::view(elem_types, i, xt::all()) =
static_cast<int>(ElementType::UNSUPPORTED);
xt::view(connectivity, i, xt::all()) = -1;
elem_types.slice(i) = static_cast<int>(ElementType::UNSUPPORTED);
connectivity.slice(i) = -1;
}
}
@ -1096,7 +1089,7 @@ int StructuredMesh::n_surface_bins() const
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
{
// Determine shape of array for counts
@ -1104,7 +1097,7 @@ xt::xtensor<double, 1> StructuredMesh::count_sites(
vector<std::size_t> shape = {m};
// Create array of zeros
xt::xarray<double> cnt {shape, 0.0};
auto cnt = tensor::zeros<double>(shape);
bool outside_ = false;
for (int64_t i = 0; i < length; i++) {
@ -1123,31 +1116,25 @@ xt::xtensor<double, 1> StructuredMesh::count_sites(
cnt(mesh_bin) += site.wgt;
}
// Create copy of count data. Since ownership will be acquired by xtensor,
// std::allocator must be used to avoid Valgrind mismatched free() / delete
// warnings.
// Create reduced count data
auto counts = tensor::zeros<double>(shape);
int total = cnt.size();
double* cnt_reduced = std::allocator<double> {}.allocate(total);
#ifdef OPENMC_MPI
// collect values from all processors
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
if (outside) {
MPI_Reduce(&outside_, outside, 1, MPI_C_BOOL, MPI_LOR, 0, mpi::intracomm);
}
#else
std::copy(cnt.data(), cnt.data() + total, cnt_reduced);
std::copy(cnt.data(), cnt.data() + total, counts.data());
if (outside)
*outside = outside_;
#endif
// Adapt reduced values in array back into an xarray
auto arr = xt::adapt(cnt_reduced, total, xt::acquire_ownership(), shape);
xt::xarray<double> counts = arr;
return counts;
}
@ -1340,10 +1327,10 @@ void StructuredMesh::surface_bins_crossed(
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
if (xt::any(shape <= 0)) {
if ((shape <= 0).any()) {
set_errmsg("All entries for a regular mesh dimensions "
"must be positive.");
return OPENMC_E_INVALID_ARGUMENT;
@ -1365,13 +1352,13 @@ int RegularMesh::set_grid()
}
// 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.");
return OPENMC_E_INVALID_ARGUMENT;
}
// 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) {
@ -1383,7 +1370,7 @@ int RegularMesh::set_grid()
}
// Check that upper-right is above lower-left
if (xt::any(upper_right_ < lower_left_)) {
if ((upper_right_ < lower_left_).any()) {
set_errmsg(
"The upper_right coordinates of a regular mesh must be greater than "
"the lower_left coordinates.");
@ -1391,11 +1378,11 @@ int RegularMesh::set_grid()
}
// Set width
width_ = xt::eval((upper_right_ - lower_left_) / shape);
width_ = (upper_right_ - lower_left_) / shape;
}
// Set material volumes
volume_frac_ = 1.0 / xt::prod(shape)();
volume_frac_ = 1.0 / shape.prod();
element_volume_ = 1.0;
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.");
}
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();
if (n != 1 && n != 2 && n != 3) {
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
if (check_for_node(node, "lower_left")) {
// 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 {
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.");
}
width_ = get_node_xarray<double>(node, "width");
width_ = get_node_tensor<double>(node, "width");
} 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 {
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.");
}
xt::xtensor<int, 1> shape;
tensor::Tensor<int> shape;
read_dataset(group, "dimension", shape);
int n = n_dimension_ = shape.size();
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
{
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, "upper_right", upper_right_);
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
{
// Determine shape of array for counts
@ -1583,7 +1570,7 @@ xt::xtensor<double, 1> RegularMesh::count_sites(
vector<std::size_t> shape = {m};
// Create array of zeros
xt::xarray<double> cnt {shape, 0.0};
auto cnt = tensor::zeros<double>(shape);
bool outside_ = false;
for (int64_t i = 0; i < length; i++) {
@ -1602,31 +1589,25 @@ xt::xtensor<double, 1> RegularMesh::count_sites(
cnt(mesh_bin) += site.wgt;
}
// Create copy of count data. Since ownership will be acquired by xtensor,
// std::allocator must be used to avoid Valgrind mismatched free() / delete
// warnings.
// Create reduced count data
auto counts = tensor::zeros<double>(shape);
int total = cnt.size();
double* cnt_reduced = std::allocator<double> {}.allocate(total);
#ifdef OPENMC_MPI
// collect values from all processors
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
if (outside) {
MPI_Reduce(&outside_, outside, 1, MPI_C_BOOL, MPI_LOR, 0, mpi::intracomm);
}
#else
std::copy(cnt.data(), cnt.data() + total, cnt_reduced);
std::copy(cnt.data(), cnt.data() + total, counts.data());
if (outside)
*outside = outside_;
#endif
// Adapt reduced values in array back into an xarray
auto arr = xt::adapt(cnt_reduced, total, xt::acquire_ownership(), shape);
xt::xarray<double> counts = arr;
return counts;
}
@ -2698,7 +2679,7 @@ extern "C" int openmc_regular_mesh_get_params(
return err;
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.");
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)};
if (ll && ur) {
m->lower_left_ = xt::adapt(ll, n, xt::no_ownership(), shape);
m->upper_right_ = xt::adapt(ur, n, xt::no_ownership(), shape);
m->width_ = (m->upper_right_ - m->lower_left_) / m->get_x_shape();
m->lower_left_ = tensor::Tensor<double>(ll, n);
m->upper_right_ = tensor::Tensor<double>(ur, n);
m->width_ = (m->upper_right_ - m->lower_left_) / m->get_shape_tensor();
} else if (ll && width) {
m->lower_left_ = xt::adapt(ll, n, xt::no_ownership(), shape);
m->width_ = xt::adapt(width, n, xt::no_ownership(), shape);
m->upper_right_ = m->lower_left_ + m->get_x_shape() * m->width_;
m->lower_left_ = tensor::Tensor<double>(ll, n);
m->width_ = tensor::Tensor<double>(width, n);
m->upper_right_ = m->lower_left_ + m->get_shape_tensor() * m->width_;
} else if (ur && width) {
m->upper_right_ = xt::adapt(ur, n, xt::no_ownership(), shape);
m->width_ = xt::adapt(width, n, xt::no_ownership(), shape);
m->lower_left_ = m->upper_right_ - m->get_x_shape() * m->width_;
m->upper_right_ = tensor::Tensor<double>(ur, n);
m->width_ = tensor::Tensor<double>(width, n);
m->lower_left_ = m->upper_right_ - m->get_shape_tensor() * m->width_;
} else {
set_errmsg("At least two parameters must be specified.");
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
// 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;
for (int i = 0; i < m->n_dimension_; 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;
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.");
return OPENMC_E_ALLOCATE;
}

View file

@ -5,10 +5,7 @@
#include <cstdlib>
#include <sstream>
#include "xtensor/xadapt.hpp"
#include "xtensor/xmath.hpp"
#include "xtensor/xsort.hpp"
#include "xtensor/xview.hpp"
#include "openmc/tensor.h"
#include <fmt/core.h>
#include "openmc/error.h"
@ -33,8 +30,7 @@ void Mgxs::init(const std::string& in_name, double in_awr,
// Set the metadata
name = in_name;
awr = in_awr;
// TODO: Remove adapt when in_KTs is an xtensor
kTs = xt::adapt(in_kTs);
kTs = tensor::Tensor<double>(in_kTs.data(), in_kTs.size());
fissionable = in_fissionable;
scatter_format = in_scatter_format;
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);
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++) {
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
for (const auto& T : temperature) {
// Determine the closest temperature value
// NOTE: the below block could be replaced with the following line,
// 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;
}
}
auto i_closest = tensor::abs(temps_available - T).argmin();
double temp_actual = temps_available[i_closest];
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++) {
switch (settings::temperature_method) {
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]];
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:
// Get a list of bounding temperatures for each actual temperature
// 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) &&
(temp_desired < micros[m]->kTs[k + 1])) {
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);
} else {
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);
}
}
@ -489,7 +473,7 @@ double Mgxs::get_xs(MgxsType xstype, int gin, const int* gout, const double* mu,
} else {
// provide an outgoing group-wise sum
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);
}
}
@ -508,13 +492,13 @@ double Mgxs::get_xs(MgxsType xstype, int gin, const int* gout, const double* mu,
} else {
if (dg != nullptr) {
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);
}
} else {
val = 0.;
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 g = 0; g < xs_t->delayed_nu_fission.shape(2); g++) {
for (int d = 0; d < xs_t->delayed_nu_fission.shape(3); d++) {
val += xs_t->delayed_nu_fission(a, d, gin, g);
}
}
@ -650,7 +634,7 @@ bool Mgxs::equiv(const Mgxs& that)
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 "xtensor/xbuilder.hpp"
#include "xtensor/xview.hpp"
#include "openmc/tensor.h"
#include <algorithm> // for sort, min_element
#include <cassert>
@ -361,8 +360,7 @@ void Nuclide::create_derived(
{
for (const auto& grid : grid_) {
// Allocate and initialize cross section
array<size_t, 2> shape {grid.energy.size(), 5};
xs_.emplace_back(shape, 0.0);
xs_.push_back(tensor::zeros<double>({grid.energy.size(), 5}));
}
reaction_index_.fill(C_NONE);
@ -375,9 +373,8 @@ void Nuclide::create_derived(
for (int t = 0; t < kTs_.size(); ++t) {
int j = rx->xs_[t].threshold;
int n = rx->xs_[t].value.size();
auto xs = xt::adapt(rx->xs_[t].value);
auto pprod = xt::view(xs_[t], xt::range(j, j + n), XS_PHOTON_PROD);
auto xs = tensor::Tensor<double>(
rx->xs_[t].value.data(), rx->xs_[t].value.size());
for (const auto& p : rx->products_) {
if (p.particle_.is_photon()) {
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;
// Add contribution to total cross section
auto total = xt::view(xs_[t], xt::range(j, j + n), XS_TOTAL);
total += xs;
xs_[t].slice(tensor::range(j, j + n), XS_TOTAL) += xs;
// Add contribution to absorption cross section
auto absorption = xt::view(xs_[t], xt::range(j, j + n), XS_ABSORPTION);
if (is_disappearance(rx->mt_)) {
absorption += xs;
xs_[t].slice(tensor::range(j, j + n), XS_ABSORPTION) += xs;
}
if (is_fission(rx->mt_)) {
fissionable_ = true;
auto fission = xt::view(xs_[t], xt::range(j, j + n), XS_FISSION);
fission += xs;
absorption += xs;
xs_[t].slice(tensor::range(j, j + n), XS_FISSION) += xs;
xs_[t].slice(tensor::range(j, j + n), XS_ABSORPTION) += xs;
// Keep track of fission reactions
if (t == 0) {
@ -510,7 +504,7 @@ void Nuclide::init_grid()
double spacing = std::log(E_max / E_min) / M;
// 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_) {
// Resize array for storing grid indices

View file

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

View file

@ -13,11 +13,7 @@
#include "openmc/search.h"
#include "openmc/settings.h"
#include "xtensor/xbuilder.hpp"
#include "xtensor/xmath.hpp"
#include "xtensor/xoperation.hpp"
#include "xtensor/xslice.hpp"
#include "xtensor/xview.hpp"
#include "openmc/tensor.h"
#include <cmath>
#include <fmt/core.h>
@ -33,7 +29,7 @@ constexpr int PhotonInteraction::MAX_STACK_SIZE;
namespace data {
xt::xtensor<double, 1> compton_profile_pz;
tensor::Tensor<double> compton_profile_pz;
std::unordered_map<std::string, int> element_map;
vector<unique_ptr<PhotonInteraction>> elements;
@ -46,8 +42,6 @@ vector<unique_ptr<PhotonInteraction>> elements;
PhotonInteraction::PhotonInteraction(hid_t group)
{
using namespace xt::placeholders;
// Set index of element in global vector
index_ = data::elements.size();
@ -96,7 +90,7 @@ PhotonInteraction::PhotonInteraction(hid_t group)
read_dataset(rgroup, "xs", pair_production_electron_);
close_group(rgroup);
} else {
pair_production_electron_ = xt::zeros_like(energy_);
pair_production_electron_ = tensor::zeros_like(energy_);
}
// Read pair production
@ -105,7 +99,7 @@ PhotonInteraction::PhotonInteraction(hid_t group)
read_dataset(rgroup, "xs", pair_production_nuclear_);
close_group(rgroup);
} else {
pair_production_nuclear_ = xt::zeros_like(energy_);
pair_production_nuclear_ = tensor::zeros_like(energy_);
}
// Read photoelectric
@ -119,7 +113,7 @@ PhotonInteraction::PhotonInteraction(hid_t group)
read_dataset(rgroup, "xs", heating_);
close_group(rgroup);
} else {
heating_ = xt::zeros_like(energy_);
heating_ = tensor::zeros_like(energy_);
}
// Read subshell photoionization cross section and atomic relaxation data
@ -133,7 +127,7 @@ PhotonInteraction::PhotonInteraction(hid_t group)
}
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
std::unordered_map<int, int> shell_map;
@ -168,15 +162,17 @@ PhotonInteraction::PhotonInteraction(hid_t group)
}
// Read subshell cross section
xt::xtensor<double, 1> xs;
tensor::Tensor<double> xs;
dset = open_dataset(tgroup, "xs");
read_attribute(dset, "threshold_idx", shell.threshold);
close_dataset(dset);
read_dataset(tgroup, "xs", xs);
auto cross_section =
xt::view(cross_sections_, xt::range(shell.threshold, _), i);
cross_section = xt::where(xs > 0, xt::log(xs), 0);
cross_sections_.slice(tensor::range(static_cast<size_t>(shell.threshold),
cross_sections_.shape(0)),
i);
cross_section = tensor::where(xs > 0, tensor::log(xs), 0);
if (object_exists(tgroup, "transitions")) {
// Determine dimensions of transitions
@ -186,11 +182,12 @@ PhotonInteraction::PhotonInteraction(hid_t group)
int n_transition = dims[0];
if (n_transition > 0) {
xt::xtensor<double, 2> matrix;
tensor::Tensor<double> matrix;
read_dataset(tgroup, "transitions", matrix);
// 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);
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_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 Compton profiles
@ -238,7 +235,7 @@ PhotonInteraction::PhotonInteraction(hid_t group)
auto is_close = [](double a, double b) {
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) {
double E_b = binding_energy_[i];
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
auto n_profile = data::compton_profile_pz.size();
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) {
double c = 0.0;
profile_cdf_(i, 0) = 0.0;
@ -276,11 +273,11 @@ PhotonInteraction::PhotonInteraction(hid_t group)
// Read bremsstrahlung scaled DCS
rgroup = open_group(group, "bremsstrahlung");
read_dataset(rgroup, "dcs", dcs_);
auto n_e = dcs_.shape()[0];
auto n_k = dcs_.shape()[1];
auto n_e = dcs_.shape(0);
auto n_k = dcs_.shape(1);
// 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);
if (data::ttb_k_grid.size() == 0) {
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)));
// 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) {
double y = std::exp(
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;
for (int j = i_grid + 1; j < n_e; ++j) {
col_i(j - i_grid) = dcs_(j, i);
@ -318,9 +315,11 @@ PhotonInteraction::PhotonInteraction(hid_t group)
}
dcs_ = dcs;
xt::xtensor<double, 1> frst {cutoff};
electron_energy = xt::concatenate(xt::xtuple(
frst, xt::view(electron_energy, xt::range(i_grid + 1, n_e))));
tensor::Tensor<double> frst({static_cast<size_t>(1)});
frst(0) = cutoff;
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
@ -329,7 +328,8 @@ PhotonInteraction::PhotonInteraction(hid_t group)
}
// 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) {
// Integrate over reduced photon energy
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)
// evaluates to zero.
double limit = std::exp(-499.0);
energy_ = xt::log(energy_);
coherent_ = xt::where(coherent_ > limit, xt::log(coherent_), -900.0);
incoherent_ = xt::where(incoherent_ > limit, xt::log(incoherent_), -900.0);
photoelectric_total_ = xt::where(
photoelectric_total_ > limit, xt::log(photoelectric_total_), -900.0);
pair_production_total_ = xt::where(
pair_production_total_ > limit, xt::log(pair_production_total_), -900.0);
heating_ = xt::where(heating_ > limit, xt::log(heating_), -900.0);
energy_ = tensor::log(energy_);
coherent_ = tensor::where(coherent_ > limit, tensor::log(coherent_), -900.0);
incoherent_ =
tensor::where(incoherent_ > limit, tensor::log(incoherent_), -900.0);
photoelectric_total_ = tensor::where(
photoelectric_total_ > limit, tensor::log(photoelectric_total_), -900.0);
pair_production_total_ = tensor::where(pair_production_total_ > limit,
tensor::log(pair_production_total_), -900.0);
heating_ = tensor::where(heating_ > limit, tensor::log(heating_), -900.0);
}
PhotonInteraction::~PhotonInteraction()
@ -512,7 +513,7 @@ void PhotonInteraction::compton_doppler(
c = prn(seed) * c_max;
// 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);
double pz_l = data::compton_profile_pz(i);
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
xs.photoelectric = 0.0;
const auto& xs_lower = xt::row(cross_sections_, i_grid);
const auto& xs_upper = xt::row(cross_sections_, i_grid + 1);
tensor::View<const double> xs_lower = cross_sections_.slice(i_grid);
tensor::View<const double> xs_upper = cross_sections_.slice(i_grid + 1);
for (int i = 0; i < xs_upper.size(); ++i)
if (xs_lower(i) != 0)

View file

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

View file

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

View file

@ -7,8 +7,7 @@
#include <fstream>
#include <sstream>
#include "xtensor/xmanipulation.hpp"
#include "xtensor/xview.hpp"
#include "openmc/tensor.h"
#include <fmt/core.h>
#include <fmt/ostream.h>
#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)
{
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)
@ -783,14 +783,14 @@ void output_ppm(const std::string& filename, const ImageData& data)
// Write header
of << "P6\n";
of << data.shape()[0] << " " << data.shape()[1] << "\n";
of << data.shape(0) << " " << data.shape(1) << "\n";
of << "255\n";
of.close();
of.open(fname, std::ios::binary | std::ios::app);
// Write color for each pixel
for (int y = 0; y < data.shape()[1]; y++) {
for (int x = 0; x < data.shape()[0]; x++) {
for (int y = 0; y < data.shape(1); y++) {
for (int x = 0; x < data.shape(0); x++) {
RGBColor rgb = data(x, y);
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);
// Write header (8 bit colour depth)
int width = data.shape()[0];
int height = data.shape()[1];
int width = data.shape(0);
int height = data.shape(1);
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_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
int idx = color_by_ == PlotColorBy::cells ? 0 : 2;
xt::xtensor<int32_t, 2> data_slice =
xt::view(ids.data_, xt::all(), xt::all(), idx);
xt::xtensor<int32_t, 2> data_flipped = xt::flip(data_slice, 0);
// Extract 2D slice at index idx from 3D data
size_t rows = ids.data_.shape(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
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
// a filter that gets adjusted with the wireframe thickness in order to
// 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.
* 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 <cstdio>
#include <numeric>
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
// two dimensions of the 3D tensor
tally_volumes_[i] =
xt::xtensor<double, 2>::from_shape({shape[0], shape[1]});
tally_volumes_[i] = tensor::Tensor<double>({shape[0], shape[1]});
}
}

View file

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

View file

@ -4,8 +4,7 @@
#include <cmath>
#include <numeric>
#include "xtensor/xbuilder.hpp"
#include "xtensor/xview.hpp"
#include "openmc/tensor.h"
#include "openmc/constants.h"
#include "openmc/error.h"
@ -19,8 +18,8 @@ namespace openmc {
// ScattData base-class methods
//==============================================================================
void ScattData::base_init(int order, const xt::xtensor<int, 1>& in_gmin,
const xt::xtensor<int, 1>& in_gmax, const double_2dvec& in_energy,
void ScattData::base_init(int order, const tensor::Tensor<int>& in_gmin,
const tensor::Tensor<int>& in_gmax, const double_2dvec& in_energy,
const double_2dvec& in_mult)
{
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,
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)
{
size_t groups = those_scatts[0]->energy.size();
// Now allocate and zero our storage spaces
xt::xtensor<double, 3> this_nuscatt_matrix({groups, groups, order_dim}, 0.);
xt::xtensor<double, 2> this_nuscatt_P0({groups, groups}, 0.);
xt::xtensor<double, 2> this_scatt_P0({groups, groups}, 0.);
xt::xtensor<double, 2> this_mult({groups, groups}, 1.);
tensor::Tensor<double> this_nuscatt_matrix =
tensor::zeros<double>({groups, groups, order_dim});
tensor::Tensor<double> this_nuscatt_P0 =
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
for (int i = 0; i < those_scatts.size(); i++) {
ScattData* that = those_scatts[i];
// 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
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
// 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
// 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_;
for (gmin_ = 0; gmin_ < groups; gmin_++) {
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.) {
non_zero = true;
break;
@ -118,7 +120,7 @@ void ScattData::base_combine(size_t max_order, size_t order_dim,
int gmax_;
for (gmax_ = groups - 1; gmax_ >= 0; gmax_--) {
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.) {
non_zero = true;
break;
@ -143,8 +145,8 @@ void ScattData::base_combine(size_t max_order, size_t order_dim,
sparse_mult[gin].resize(gmax_ - gmin_ + 1);
int i_gout = 0;
for (int gout = gmin_; gout <= gmax_; gout++) {
sparse_scatter[gin][i_gout].resize(this_nuscatt_matrix.shape()[2]);
for (int l = 0; l < this_nuscatt_matrix.shape()[2]; l++) {
sparse_scatter[gin][i_gout].resize(this_nuscatt_matrix.shape(2));
for (int l = 0; l < this_nuscatt_matrix.shape(2); l++) {
sparse_scatter[gin][i_gout][l] = this_nuscatt_matrix(gin, gout, l);
}
sparse_mult[gin][i_gout] = this_mult(gin, gout);
@ -227,8 +229,8 @@ double ScattData::get_xs(
// ScattDataLegendre methods
//==============================================================================
void ScattDataLegendre::init(const xt::xtensor<int, 1>& in_gmin,
const xt::xtensor<int, 1>& in_gmax, const double_2dvec& in_mult,
void ScattDataLegendre::init(const tensor::Tensor<int>& in_gmin,
const tensor::Tensor<int>& in_gmax, const double_2dvec& in_mult,
const double_3dvec& coeffs)
{
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
// 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++) {
int num_groups = in_gmax[gin] - in_gmin[gin] + 1;
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();
xt::xtensor<int, 1> in_gmin({groups}, 0);
xt::xtensor<int, 1> in_gmax({groups}, 0);
tensor::Tensor<int> in_gmin({groups}, 0);
tensor::Tensor<int> in_gmax({groups}, 0);
double_3dvec sparse_scatter(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
size_t groups = energy.size();
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 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
//==============================================================================
void ScattDataHistogram::init(const xt::xtensor<int, 1>& in_gmin,
const xt::xtensor<int, 1>& in_gmax, const double_2dvec& in_mult,
void ScattDataHistogram::init(const tensor::Tensor<int>& in_gmin,
const tensor::Tensor<int>& in_gmax, const double_2dvec& in_mult,
const double_3dvec& coeffs)
{
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
// 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 i_gout = 0; i_gout < matrix[gin].size(); i_gout++) {
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);
// Build the angular distribution mu values
mu = xt::linspace(-1., 1., order + 1);
mu = tensor::linspace(-1., 1., order + 1);
dmu = 2. / order;
// 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;
if (mu == 1.) {
// use size -2 to have the index one before the end
imu = this->mu.shape()[0] - 2;
imu = this->mu.shape(0) - 2;
} else {
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
size_t groups = energy.size();
// We ignore the requested order for Histogram and Tabular representations
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 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();
xt::xtensor<int, 1> in_gmin({groups}, 0);
xt::xtensor<int, 1> in_gmax({groups}, 0);
tensor::Tensor<int> in_gmin({groups}, 0);
tensor::Tensor<int> in_gmax({groups}, 0);
double_3dvec sparse_scatter(groups);
double_2dvec sparse_mult(groups);
@ -620,8 +623,8 @@ void ScattDataHistogram::combine(
// ScattDataTabular methods
//==============================================================================
void ScattDataTabular::init(const xt::xtensor<int, 1>& in_gmin,
const xt::xtensor<int, 1>& in_gmax, const double_2dvec& in_mult,
void ScattDataTabular::init(const tensor::Tensor<int>& in_gmin,
const tensor::Tensor<int>& in_gmax, const double_2dvec& in_mult,
const double_3dvec& coeffs)
{
size_t groups = coeffs.size();
@ -631,12 +634,12 @@ void ScattDataTabular::init(const xt::xtensor<int, 1>& in_gmin,
double_3dvec matrix = coeffs;
// Build the angular distribution mu values
mu = xt::linspace(-1., 1., order);
mu = tensor::linspace(-1., 1., order);
dmu = 2. / (order - 1);
// Get the scattering cross section value by integrating the distribution
// 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 i_gout = 0; i_gout < matrix[gin].size(); i_gout++) {
for (int imu = 1; imu < order; imu++) {
@ -713,7 +716,7 @@ double ScattDataTabular::calc_f(int gin, int gout, double mu)
int imu;
if (mu == 1.) {
// use size -2 to have the index one before the end
imu = this->mu.shape()[0] - 2;
imu = this->mu.shape(0) - 2;
} else {
imu = std::floor((mu + 1.) / dmu + 1.) - 1;
}
@ -734,7 +737,7 @@ void ScattDataTabular::sample(
sample_energy(gin, gout, i_gout, seed);
// Determine the outgoing cosine bin
int NP = this->mu.shape()[0];
int NP = this->mu.shape(0);
double xi = prn(seed);
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
size_t groups = energy.size();
// We ignore the requested order for Histogram and Tabular representations
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 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();
xt::xtensor<int, 1> in_gmin({groups}, 0);
xt::xtensor<int, 1> in_gmax({groups}, 0);
tensor::Tensor<int> in_gmin({groups}, 0);
tensor::Tensor<int> in_gmax({groups}, 0);
double_3dvec sparse_scatter(groups);
double_2dvec sparse_mult(groups);
@ -854,7 +858,7 @@ void convert_legendre_to_tabular(ScattDataLegendre& leg, ScattDataTabular& tab)
tab.scattxs = leg.scattxs;
// 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);
// Calculate f(mu) and integrate it so we can avoid rejection sampling

View file

@ -5,8 +5,7 @@
#include <cstddef> // for size_t
#include <iterator> // for back_inserter
#include "xtensor/xarray.hpp"
#include "xtensor/xview.hpp"
#include "openmc/tensor.h"
#include "openmc/endf.h"
#include "openmc/hdf5_interface.h"
@ -26,11 +25,11 @@ CorrelatedAngleEnergy::CorrelatedAngleEnergy(hid_t group)
hid_t dset = open_dataset(group, "energy");
// Get interpolation parameters
xt::xarray<int> temp;
tensor::Tensor<int> temp;
read_attribute(dset, "interpolation", temp);
auto temp_b = xt::view(temp, 0); // view of breakpoints
auto temp_i = xt::view(temp, 1); // view of interpolation parameters
tensor::View<int> temp_b = temp.slice(0); // breakpoints
tensor::View<int> temp_i = temp.slice(1); // interpolation parameters
std::copy(temp_b.begin(), temp_b.end(), std::back_inserter(breakpoints_));
for (const auto i : temp_i)
@ -51,12 +50,12 @@ CorrelatedAngleEnergy::CorrelatedAngleEnergy(hid_t group)
read_attribute(dset, "interpolation", interp);
read_attribute(dset, "n_discrete_lines", n_discrete);
xt::xarray<double> eout;
tensor::Tensor<double> eout;
read_dataset(dset, eout);
close_dataset(dset);
// Read angle distributions
xt::xarray<double> mu;
tensor::Tensor<double> mu;
read_dataset(group, "mu", mu);
for (int i = 0; i < n_energy; ++i) {
@ -66,7 +65,7 @@ CorrelatedAngleEnergy::CorrelatedAngleEnergy(hid_t group)
if (i < n_energy - 1) {
n = offsets[i + 1] - j;
} else {
n = eout.shape()[1] - j;
n = eout.shape(1) - j;
}
// Assign interpolation scheme and number of discrete lines
@ -75,9 +74,9 @@ CorrelatedAngleEnergy::CorrelatedAngleEnergy(hid_t group)
d.n_discrete = n_discrete[i];
// Copy data
d.e_out = xt::view(eout, 0, xt::range(j, j + n));
d.p = xt::view(eout, 1, xt::range(j, j + n));
d.c = xt::view(eout, 2, xt::range(j, j + n));
d.e_out = eout.slice(0, tensor::range(j, j + n));
d.p = eout.slice(1, tensor::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
// 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
int offset_mu = std::lround(eout(4, offsets[i] + j));
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;
} else {
m = mu.shape()[1] - offset_mu;
m = mu.shape(1) - offset_mu;
}
// For incoherent inelastic thermal scattering, the angle distributions
@ -133,9 +132,12 @@ CorrelatedAngleEnergy::CorrelatedAngleEnergy(hid_t group)
interp_mu = 1;
auto interp = int2interp(interp_mu);
auto xs = xt::view(mu, 0, xt::range(offset_mu, offset_mu + m));
auto ps = xt::view(mu, 1, xt::range(offset_mu, offset_mu + m));
auto cs = xt::view(mu, 2, xt::range(offset_mu, offset_mu + m));
tensor::View<double> xs =
mu.slice(0, tensor::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> p {ps.begin(), ps.end()};

View file

@ -5,8 +5,7 @@
#include <cstddef> // for size_t
#include <iterator> // for back_inserter
#include "xtensor/xarray.hpp"
#include "xtensor/xview.hpp"
#include "openmc/tensor.h"
#include "openmc/hdf5_interface.h"
#include "openmc/math_functions.h"
@ -27,11 +26,11 @@ KalbachMann::KalbachMann(hid_t group)
hid_t dset = open_dataset(group, "energy");
// Get interpolation parameters
xt::xarray<int> temp;
tensor::Tensor<int> temp;
read_attribute(dset, "interpolation", temp);
auto temp_b = xt::view(temp, 0); // view of breakpoints
auto temp_i = xt::view(temp, 1); // view of interpolation parameters
tensor::View<int> temp_b = temp.slice(0); // breakpoints
tensor::View<int> temp_i = temp.slice(1); // interpolation parameters
std::copy(temp_b.begin(), temp_b.end(), std::back_inserter(breakpoints_));
for (const auto i : temp_i)
@ -52,7 +51,7 @@ KalbachMann::KalbachMann(hid_t group)
read_attribute(dset, "interpolation", interp);
read_attribute(dset, "n_discrete_lines", n_discrete);
xt::xarray<double> eout;
tensor::Tensor<double> eout;
read_dataset(dset, eout);
close_dataset(dset);
@ -63,7 +62,7 @@ KalbachMann::KalbachMann(hid_t group)
if (i < n_energy - 1) {
n = offsets[i + 1] - j;
} else {
n = eout.shape()[1] - j;
n = eout.shape(1) - j;
}
// Assign interpolation scheme and number of discrete lines
@ -72,11 +71,11 @@ KalbachMann::KalbachMann(hid_t group)
d.n_discrete = n_discrete[i];
// Copy data
d.e_out = xt::view(eout, 0, xt::range(j, j + n));
d.p = xt::view(eout, 1, xt::range(j, j + n));
d.c = xt::view(eout, 2, xt::range(j, j + n));
d.r = xt::view(eout, 3, xt::range(j, j + n));
d.a = xt::view(eout, 4, xt::range(j, j + n));
d.e_out = eout.slice(0, tensor::range(j, j + n));
d.p = eout.slice(1, tensor::range(j, j + n));
d.c = eout.slice(2, tensor::range(j, j + n));
d.r = eout.slice(3, tensor::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
// 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/search.h"
#include "xtensor/xview.hpp"
#include "openmc/tensor.h"
#include <cassert>
#include <cmath> // for log, exp
@ -85,7 +85,7 @@ void IncoherentElasticAEDiscrete::sample(
// incoming energies.
// Sample outgoing cosine bin
int n_mu = mu_out_.shape()[1];
int n_mu = mu_out_.shape(1);
int k = prn(seed) * n_mu;
// 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.
int j;
int n = energy_out_.shape()[1];
int n = energy_out_.shape(1);
if (!skewed_) {
// All bins equally likely
j = prn(seed) * n;
@ -178,7 +178,7 @@ void IncoherentInelasticAEDiscrete::sample(
E_out = (1 - f) * E_ij + f * E_i1j;
// Sample outgoing cosine bin
int m = mu_out_.shape()[2];
int m = mu_out_.shape(2);
int k = prn(seed) * m;
// 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
if (j == 0) {
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
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());
}
}
@ -287,7 +287,7 @@ void IncoherentInelasticAE::sample(
}
// 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;
// Rather than use the sampled discrete mu directly, it is smeared over

View file

@ -30,7 +30,7 @@
#ifdef _OPENMP
#include <omp.h>
#endif
#include "xtensor/xview.hpp"
#include "openmc/tensor.h"
#ifdef OPENMC_MPI
#include <mpi.h>
@ -413,7 +413,7 @@ void finalize_batch()
// Reset global tally results
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;
}

View file

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

View file

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

View file

@ -9,6 +9,7 @@
#include "openmc/cell.h"
#include "openmc/error.h"
#include "openmc/geometry.h"
#include "openmc/tensor.h"
#include "openmc/xml_interface.h"
namespace openmc {
@ -108,7 +109,7 @@ void CellInstanceFilter::to_statepoint(hid_t filter_group) const
{
Filter::to_statepoint(filter_group);
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) {
const auto& x = cell_instances_[i];
data(i, 0) = model::cells[x.index_cell]->id_;

View file

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

View file

@ -35,9 +35,7 @@
#include "openmc/tallies/filter_time.h"
#include "openmc/xml_interface.h"
#include "xtensor/xadapt.hpp"
#include "xtensor/xbuilder.hpp" // for empty_like
#include "xtensor/xview.hpp"
#include "openmc/tensor.h"
#include <fmt/core.h>
#include <algorithm> // for max, set_union
@ -69,7 +67,7 @@ vector<double> time_grid;
} // namespace model
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};
} // namespace simulation
@ -806,9 +804,11 @@ void Tally::init_results()
{
int n_scores = scores_.size() * nuclides_.size();
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 {
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;
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_) {
#pragma omp parallel for
// 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.)
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 val2 = val * val;
results_(i, j, TallyResult::VALUE) = 0.0;
@ -866,9 +866,9 @@ void Tally::accumulate()
} else {
#pragma omp parallel for
// 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.)
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;
results_(i, j, TallyResult::VALUE) = 0.0;
results_(i, j, TallyResult::SUM) += val;
@ -888,18 +888,18 @@ int Tally::score_index(const std::string& score) const
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()) {
shape.push_back(model::tally_filters[f]->n_bins());
}
// add number of scores and nuclides to tally
shape.push_back(results_.shape()[1]);
shape.push_back(results_.shape()[2]);
shape.push_back(results_.shape(1));
shape.push_back(results_.shape(2));
xt::xarray<double> reshaped_results = results_;
tensor::Tensor<double> reshaped_results = results_;
reshaped_results.reshape(shape);
return reshaped_results;
}
@ -1004,13 +1004,14 @@ void reduce_tally_results()
// Skip any tallies that are not active
auto& tally {model::tallies[i_tally]};
// Get view of accumulated tally values
auto values_view = xt::view(tally->results_, xt::all(), xt::all(),
static_cast<int>(TallyResult::VALUE));
// Extract 2D view of the VALUE column from the 3D results tensor,
// then copy into a contiguous array for MPI reduction
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
xt::xtensor<double, 2> values = values_view;
xt::xtensor<double, 2> values_reduced = xt::empty_like(values);
tensor::Tensor<double> values_reduced(values.shape());
// Reduce contiguous set of tally results
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
if (mpi::master) {
values_view = values_reduced;
val_view = values_reduced;
} 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
// is on.
// Get view of global tally values
// Get reference to global tallies
auto& gt = simulation::global_tallies;
auto gt_values_view =
xt::view(gt, xt::all(), static_cast<int>(TallyResult::VALUE));
const int val_col = static_cast<int>(TallyResult::VALUE);
// Make copy of values in contiguous array
xt::xtensor<double, 1> gt_values = gt_values_view;
xt::xtensor<double, 1> gt_values_reduced = xt::empty_like(gt_values);
// Copy VALUE column into contiguous array for MPI reduction
tensor::Tensor<double> gt_values(gt.slice(tensor::all, val_col));
tensor::Tensor<double> gt_values_reduced({size_t {N_GLOBAL_TALLIES}});
// Reduce contiguous data
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
if (mpi::master) {
gt_values_view = gt_values_reduced;
gt.slice(tensor::all, val_col) = gt_values_reduced;
} 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

View file

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

View file

@ -71,7 +71,7 @@ void check_tally_triggers(double& ratio, int& tally_id, int& score)
continue;
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) {
// Compute the tally uncertainty metrics.
auto uncert_pair =

View file

@ -3,12 +3,7 @@
#include <algorithm> // for sort, move, min, max, find
#include <cmath> // for round, sqrt, abs
#include "xtensor/xarray.hpp"
#include "xtensor/xbuilder.hpp"
#include "xtensor/xmath.hpp"
#include "xtensor/xsort.hpp"
#include "xtensor/xtensor.hpp"
#include "xtensor/xview.hpp"
#include "openmc/tensor.h"
#include <fmt/core.h>
#include "openmc/constants.h"
@ -55,7 +50,7 @@ ThermalScattering::ThermalScattering(
// Determine temperatures available
auto dset_names = dataset_names(kT_group);
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) {
// Read temperature value
double T;
@ -82,7 +77,7 @@ ThermalScattering::ThermalScattering(
// Determine actual temperatures to read
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];
if (std::abs(temp_actual - T) < settings::temperature_tolerance) {
if (std::find(temps_to_read.begin(), temps_to_read.end(),

View file

@ -8,7 +8,7 @@
#include "openmc/simulation.h"
#include "openmc/vector.h"
#include "xtensor/xtensor.hpp"
#include "openmc/tensor.h"
#include <fmt/core.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
// different from how we want it laid out in memory.
// 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);
auto shape = tmp_prob.shape();
@ -38,7 +38,7 @@ UrrData::UrrData(hid_t group_id)
xs_values_.resize({n_energy, n_cdf_values});
// 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.
enum class URRTableParam {
CUM_PROB,

View file

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

View file

@ -6,12 +6,7 @@
#include <set>
#include <string>
#include "xtensor/xdynamic_view.hpp"
#include "xtensor/xindex_view.hpp"
#include "xtensor/xio.hpp"
#include "xtensor/xmasked_view.hpp"
#include "xtensor/xnoalias.hpp"
#include "xtensor/xview.hpp"
#include "openmc/tensor.h"
#include "openmc/error.h"
#include "openmc/file_utils.h"
@ -265,8 +260,12 @@ WeightWindows* WeightWindows::from_hdf5(
}
wws->set_mesh(model::mesh_map[mesh_id]);
wws->lower_ww_ = xt::empty<double>(wws->bounds_size());
wws->upper_ww_ = xt::empty<double>(wws->bounds_size());
wws->lower_ww_ =
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, "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());
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);
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);
}
@ -448,8 +449,8 @@ void WeightWindows::check_bounds(const T& bounds) const
}
}
void WeightWindows::set_bounds(const xt::xtensor<double, 2>& lower_bounds,
const xt::xtensor<double, 2>& upper_bounds)
void WeightWindows::set_bounds(const tensor::Tensor<double>& lower_bounds,
const tensor::Tensor<double>& 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(
const xt::xtensor<double, 2>& lower_bounds, double ratio)
const tensor::Tensor<double>& lower_bounds, double ratio)
{
this->check_bounds(lower_bounds);
@ -475,14 +476,16 @@ void WeightWindows::set_bounds(
{
check_bounds(lower_bounds, upper_bounds);
auto shape = this->bounds_size();
lower_ww_ = xt::empty<double>(shape);
upper_ww_ = xt::empty<double>(shape);
lower_ww_ = tensor::Tensor<double>(
{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
xt::view(lower_ww_, xt::all()) =
xt::adapt(lower_bounds.data(), lower_ww_.shape());
xt::view(upper_ww_, xt::all()) =
xt::adapt(upper_bounds.data(), upper_ww_.shape());
// Copy weight window values from input spans into the tensors
std::copy(lower_bounds.data(), lower_bounds.data() + lower_ww_.size(),
lower_ww_.data());
std::copy(upper_bounds.data(), upper_bounds.data() + upper_ww_.size(),
upper_ww_.data());
}
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);
auto shape = this->bounds_size();
lower_ww_ = xt::empty<double>(shape);
upper_ww_ = xt::empty<double>(shape);
lower_ww_ = tensor::Tensor<double>(
{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
xt::view(lower_ww_, xt::all()) =
xt::adapt(lower_bounds.data(), lower_ww_.shape());
xt::view(upper_ww_, xt::all()) =
xt::adapt(lower_bounds.data(), upper_ww_.shape());
// Copy lower bounds into both arrays, then scale upper by ratio
std::copy(lower_bounds.data(), lower_bounds.data() + lower_ww_.size(),
lower_ww_.data());
std::copy(lower_bounds.data(), lower_bounds.data() + upper_ww_.size(),
upper_ww_.data());
upper_ww_ *= ratio;
}
@ -510,8 +515,8 @@ void WeightWindows::update_weights(const Tally* tally, const std::string& value,
this->check_tally_update_compatibility(tally);
// Dimensions of weight window arrays
int e_bins = lower_ww_.shape()[0];
int64_t mesh_bins = lower_ww_.shape()[1];
int e_bins = lower_ww_.shape(0);
int64_t mesh_bins = lower_ww_.shape(1);
// Initialize weight window arrays to -1.0 by default
#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
//
// At the end of this section, the mean and rel_err array
// is a 2D view of tally data (n_e_groups, n_mesh_bins)
// At the end of this section, mean and rel_err are
// 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)
// Look for the size of the last dimension of the results array
const auto& results_arr = tally->results();
const int results_dim = static_cast<int>(results_arr.shape()[2]);
// Look for the size of the last dimension of the results tensor
const auto& results = tally->results();
const int results_dim = static_cast<int>(results.shape(2));
std::array<int, 5> shape = {1, 1, 1, tally->n_scores(), results_dim};
// 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) -
filter_types.begin();
// get a fully reshaped view of the tally according to tally ordering of
// 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
// determine the index of the particle within its filter
int particle_idx = 0;
if (tally->has_filter(FilterType::PARTICLE)) {
// get the particle filter
auto pf = tally->get_filter<ParticleFilter>();
const auto& particles = pf->particles();
// find the index of the particle that matches these weight windows
auto p_it =
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()) {
auto msg = fmt::format("Particle type '{}' not present on Filter {} for "
"Tally {} used to update WeightWindows {}",
@ -614,17 +608,46 @@ void WeightWindows::update_weights(const Tally* tally, const std::string& value,
fatal_error(msg);
}
// use the index of the particle in the filter to down-select data later
particle_idx = p_it - particles.begin();
}
// down-select data based on particle and score
auto sum = xt::dynamic_view(
transposed_view, {particle_idx, xt::all(), xt::all(), score_index,
static_cast<int>(TallyResult::SUM)});
auto sum_sq = xt::dynamic_view(
transposed_view, {particle_idx, xt::all(), xt::all(), score_index,
static_cast<int>(TallyResult::SUM_SQ)});
// The tally results array is 3D: (n_filter_combos, n_scores, n_result_types).
// The first dimension is a row-major flattening of up to 3 filter dimensions
// (particle, energy, mesh) whose storage order depends on which filters the
// tally has. We need to map our desired indices (particle, energy, mesh)
// into the correct flat filter combination index.
//
// 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_;
//////////////////////////////////////////////
@ -1155,7 +1178,8 @@ extern "C" int openmc_weight_windows_set_bounds(int32_t index,
return err;
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;
}

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
// and residue types in this 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.
fissionable_ = (n_residues == 3);
// Read the "windows" array and use its shape to figure out the number of
// windows.
xt::xtensor<int, 2> windows;
tensor::Tensor<int> 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
// Read the "broaden_poly" arrays.
xt::xtensor<bool, 1> broaden_poly;
tensor::Tensor<bool> 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 "
"array shape in WMP library for " +
name_ + ".");
@ -56,12 +56,12 @@ WindowedMultipole::WindowedMultipole(hid_t group)
// Read the "curvefit" array.
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 "
"array shape in WMP library for " +
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
if (fit_order_ + 1 > MAX_POLY_COEFFICIENTS) {

View file

@ -5,10 +5,7 @@
#include <cstdlib>
#include <numeric>
#include "xtensor/xbuilder.hpp"
#include "xtensor/xindex_view.hpp"
#include "xtensor/xmath.hpp"
#include "xtensor/xview.hpp"
#include "openmc/tensor.h"
#include "openmc/constants.h"
#include "openmc/error.h"
@ -37,32 +34,32 @@ XsData::XsData(bool fissionable, AngleDistributionType scatter_format,
}
// allocate all [temperature][angle][in group] quantities
vector<size_t> shape {n_ang, n_g_};
total = xt::zeros<double>(shape);
absorption = xt::zeros<double>(shape);
inverse_velocity = xt::zeros<double>(shape);
total = tensor::zeros<double>(shape);
absorption = tensor::zeros<double>(shape);
inverse_velocity = tensor::zeros<double>(shape);
if (fissionable) {
fission = xt::zeros<double>(shape);
nu_fission = xt::zeros<double>(shape);
prompt_nu_fission = xt::zeros<double>(shape);
kappa_fission = xt::zeros<double>(shape);
fission = tensor::zeros<double>(shape);
nu_fission = tensor::zeros<double>(shape);
prompt_nu_fission = tensor::zeros<double>(shape);
kappa_fission = tensor::zeros<double>(shape);
}
// allocate decay_rate; [temperature][angle][delayed group]
shape[1] = n_dg_;
decay_rate = xt::zeros<double>(shape);
decay_rate = tensor::zeros<double>(shape);
if (fissionable) {
shape = {n_ang, n_dg_, n_g_};
// 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]
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]
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++) {
@ -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
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
if (fissionable) {
fission_from_hdf5(xsdata_grp, n_ang, is_isotropic);
}
// Get the non-fission-specific data
read_nd_vector(xsdata_grp, "decay-rate", decay_rate);
read_nd_vector(xsdata_grp, "absorption", absorption, true);
read_nd_vector(xsdata_grp, "inverse-velocity", inverse_velocity);
read_nd_tensor(xsdata_grp, "decay-rate", decay_rate);
read_nd_tensor(xsdata_grp, "absorption", absorption, true);
read_nd_tensor(xsdata_grp, "inverse-velocity", inverse_velocity);
// Get scattering data
scatter_from_hdf5(
xsdata_grp, n_ang, scatter_format, final_scatter_format, order_data);
// Check absorption to ensure it is not 0 since it is often the
// denominator in tally methods
xt::filtration(absorption, xt::equal(absorption, 0.)) = 1.e-10;
// Replace zero absorption values with a small number to avoid
// division by zero in tally methods
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
if (object_exists(xsdata_grp, "total")) {
read_nd_vector(xsdata_grp, "total", total);
read_nd_tensor(xsdata_grp, "total", total);
} else {
for (size_t a = 0; a < n_ang; a++) {
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
xt::filtration(total, xt::equal(total, 0.)) = 1.e-10;
// Replace zero total cross sections with a small number to avoid
// 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
// Get chi
xt::xtensor<double, 2> temp_chi({n_ang, n_g_}, 0.);
read_nd_vector(xsdata_grp, "chi", temp_chi, true);
tensor::Tensor<double> temp_chi = tensor::zeros<double>({n_ang, n_g_});
read_nd_tensor(xsdata_grp, "chi", temp_chi, true);
// Normalize chi by summing over the outgoing groups for each incoming angle
temp_chi /= xt::view(xt::sum(temp_chi, {1}), xt::all(), xt::newaxis());
// Normalize chi so it sums to 1 over outgoing groups for each angle
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
// chi we just made
chi_prompt = xt::view(temp_chi, xt::all(), xt::newaxis(), xt::all());
chi_delayed =
xt::view(temp_chi, xt::all(), xt::newaxis(), xt::newaxis(), xt::all());
// Replicate the energy spectrum across all incoming groups — the
// spectrum is independent of the incoming neutron energy
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);
// 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
xt::xtensor<double, 2> temp_nufiss({n_ang, n_g_}, 0.);
read_nd_vector(xsdata_grp, "nu-fission", temp_nufiss, true);
tensor::Tensor<double> temp_nufiss = tensor::zeros<double>({n_ang, n_g_});
read_nd_tensor(xsdata_grp, "nu-fission", temp_nufiss, true);
// Get beta (strategy will depend upon the number of dimensions in beta)
hid_t beta_dset = open_dataset(xsdata_grp, "beta");
@ -151,26 +162,39 @@ void XsData::fission_vector_beta_from_hdf5(
if (!is_isotropic)
ndim_target += 2;
if (beta_ndims == ndim_target) {
xt::xtensor<double, 2> temp_beta({n_ang, n_dg_}, 0.);
read_nd_vector(xsdata_grp, "beta", temp_beta, true);
tensor::Tensor<double> temp_beta = tensor::zeros<double>({n_ang, n_dg_});
read_nd_tensor(xsdata_grp, "beta", temp_beta, true);
// Set prompt_nu_fission = (1. - beta_total)*nu_fission
prompt_nu_fission = temp_nufiss * (1. - xt::sum(temp_beta, {1}));
// prompt_nu_fission = (1 - sum_of_beta) * nu_fission
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 =
xt::view(temp_beta, xt::all(), xt::all(), xt::newaxis()) *
xt::view(temp_nufiss, xt::all(), xt::newaxis(), xt::all());
// Delayed nu-fission is the outer product of the delayed neutron
// fraction (beta) and the fission production rate (nu-fission)
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) * temp_nufiss(a, g);
} else if (beta_ndims == ndim_target + 1) {
xt::xtensor<double, 3> temp_beta({n_ang, n_dg_, n_g_}, 0.);
read_nd_vector(xsdata_grp, "beta", temp_beta, true);
tensor::Tensor<double> temp_beta =
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 = temp_nufiss * (1. - xt::sum(temp_beta, {1}));
// prompt_nu_fission = (1 - sum_of_beta) * nu_fission
// 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 =
temp_beta * xt::view(temp_nufiss, xt::all(), xt::newaxis(), xt::all());
// Delayed nu-fission: beta is already energy-dependent [n_ang, n_dg, n_g],
// so scale each delayed group's beta by the total nu-fission for that group
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
// Get chi-prompt
xt::xtensor<double, 2> temp_chi_p({n_ang, n_g_}, 0.);
read_nd_vector(xsdata_grp, "chi-prompt", temp_chi_p, true);
tensor::Tensor<double> temp_chi_p = tensor::zeros<double>({n_ang, n_g_});
read_nd_tensor(xsdata_grp, "chi-prompt", temp_chi_p, true);
// Normalize chi by summing over the outgoing groups for each incoming angle
temp_chi_p /= xt::view(xt::sum(temp_chi_p, {1}), xt::all(), xt::newaxis());
// Normalize prompt chi so it sums to 1 over outgoing groups for each angle
for (size_t a = 0; a < n_ang; a++) {
tensor::View<double> row = temp_chi_p.slice(a);
row /= row.sum();
}
// Get chi-delayed
xt::xtensor<double, 3> temp_chi_d({n_ang, n_dg_, n_g_}, 0.);
read_nd_vector(xsdata_grp, "chi-delayed", temp_chi_d, true);
tensor::Tensor<double> temp_chi_d =
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
temp_chi_d /=
xt::view(xt::sum(temp_chi_d, {2}), xt::all(), xt::all(), xt::newaxis());
// Normalize delayed chi so it sums to 1 over outgoing groups for each
// angle and delayed group
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
// group
chi_prompt = xt::view(temp_chi_p, xt::all(), xt::newaxis(), xt::all());
chi_delayed =
xt::view(temp_chi_d, xt::all(), xt::all(), xt::newaxis(), xt::all());
// Replicate the prompt spectrum across all incoming groups
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_p.slice(a);
// 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
read_nd_vector(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, "prompt-nu-fission", prompt_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)
@ -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.
// Get chi
xt::xtensor<double, 2> temp_chi({n_ang, n_g_}, 0.);
read_nd_vector(xsdata_grp, "chi", temp_chi, true);
tensor::Tensor<double> temp_chi = tensor::zeros<double>({n_ang, n_g_});
read_nd_tensor(xsdata_grp, "chi", temp_chi, true);
// Normalize chi by summing over the outgoing groups for each incoming angle
temp_chi /= xt::view(xt::sum(temp_chi, {1}), xt::all(), xt::newaxis());
// Normalize chi so it sums to 1 over outgoing groups for each angle
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
chi_prompt = xt::view(temp_chi, xt::all(), xt::newaxis(), xt::all());
// Replicate the energy spectrum across all incoming groups
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
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
// Get nu-fission matrix
xt::xtensor<double, 3> temp_matrix({n_ang, n_g_, n_g_}, 0.);
read_nd_vector(xsdata_grp, "nu-fission", temp_matrix, true);
tensor::Tensor<double> temp_matrix =
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)
hid_t beta_dset = open_dataset(xsdata_grp, "beta");
@ -242,65 +285,92 @@ void XsData::fission_matrix_beta_from_hdf5(
if (!is_isotropic)
ndim_target += 2;
if (beta_ndims == ndim_target) {
xt::xtensor<double, 2> temp_beta({n_ang, n_dg_}, 0.);
read_nd_vector(xsdata_grp, "beta", temp_beta, true);
tensor::Tensor<double> temp_beta = tensor::zeros<double>({n_ang, n_dg_});
read_nd_tensor(xsdata_grp, "beta", temp_beta, true);
xt::xtensor<double, 1> temp_beta_sum({n_ang}, 0.);
temp_beta_sum = xt::sum(temp_beta, {1});
auto beta_sum = temp_beta.sum(1);
auto matrix_gout_sum = temp_matrix.sum(2);
// prompt_nu_fission is the sum of this matrix over outgoing groups and
// multiplied by (1 - beta_sum)
prompt_nu_fission = xt::sum(temp_matrix, {2}) * (1. - temp_beta_sum);
// prompt_nu_fission = sum_gout(matrix) * (1 - beta_total)
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));
// Store chi-prompt
chi_prompt =
xt::view(1.0 - temp_beta_sum, xt::all(), xt::newaxis(), xt::newaxis()) *
temp_matrix;
// chi_prompt = (1 - beta_total) * nu-fission matrix (unnormalized)
for (size_t a = 0; a < n_ang; a++)
for (size_t gin = 0; gin < n_g_; gin++)
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
// multiplied by beta
delayed_nu_fission =
xt::view(temp_beta, xt::all(), xt::all(), xt::newaxis()) *
xt::view(xt::sum(temp_matrix, {2}), xt::all(), xt::newaxis(), xt::all());
// Delayed nu-fission is the outer product of the delayed neutron
// fraction (beta) and the total fission rate summed over outgoing groups
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) * matrix_gout_sum(a, g);
// Store chi-delayed
chi_delayed =
xt::view(temp_beta, xt::all(), xt::all(), xt::newaxis(), xt::newaxis()) *
xt::view(temp_matrix, xt::all(), xt::newaxis(), xt::all(), xt::all());
// chi_delayed = beta * nu-fission matrix, expanded 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++)
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) {
xt::xtensor<double, 3> temp_beta({n_ang, n_dg_, n_g_}, 0.);
read_nd_vector(xsdata_grp, "beta", temp_beta, true);
tensor::Tensor<double> temp_beta =
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.);
temp_beta_sum = xt::sum(temp_beta, {1});
auto beta_sum = temp_beta.sum(1);
auto matrix_gout_sum = temp_matrix.sum(2);
// prompt_nu_fission is the sum of this matrix over outgoing groups and
// multiplied by (1 - beta_sum)
prompt_nu_fission = xt::sum(temp_matrix, {2}) * (1. - temp_beta_sum);
// prompt_nu_fission = sum_gout(matrix) * (1 - beta_total)
// Here beta is energy-dependent, so beta_sum is 2D [n_ang, n_g]
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 =
xt::view(1.0 - temp_beta_sum, xt::all(), xt::all(), xt::newaxis()) *
temp_matrix;
// chi_prompt = (1 - beta_sum) * nu-fission matrix (unnormalized)
for (size_t a = 0; a < n_ang; a++)
for (size_t gin = 0; gin < n_g_; gin++)
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
// multiplied by beta
delayed_nu_fission = temp_beta * xt::view(xt::sum(temp_matrix, {2}),
xt::all(), xt::newaxis(), xt::all());
// Delayed nu-fission: beta is energy-dependent [n_ang, n_dg, n_g],
// scale by total fission rate summed over outgoing groups
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) * matrix_gout_sum(a, g);
// Store chi-delayed
chi_delayed =
xt::view(temp_beta, xt::all(), xt::all(), xt::all(), xt::newaxis()) *
xt::view(temp_matrix, xt::all(), xt::newaxis(), xt::all(), xt::all());
// chi_delayed = beta * nu-fission matrix, expanded 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++)
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
chi_prompt /=
xt::view(xt::sum(chi_prompt, {2}), xt::all(), xt::all(), xt::newaxis());
// Normalize chi_prompt so it sums to 1 over outgoing groups
for (size_t a = 0; a < n_ang; a++)
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(
xt::sum(chi_delayed, {3}), xt::all(), xt::all(), xt::all(), xt::newaxis());
// Normalize chi_delayed so it sums to 1 over outgoing 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++) {
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)
@ -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
// Get the prompt nu-fission matrix
xt::xtensor<double, 3> temp_matrix_p({n_ang, n_g_, n_g_}, 0.);
read_nd_vector(xsdata_grp, "prompt-nu-fission", temp_matrix_p, true);
tensor::Tensor<double> temp_matrix_p =
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 = 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
// have already stored in prompt_nu_fission
chi_prompt = temp_matrix_p /
xt::view(prompt_nu_fission, xt::all(), xt::all(), xt::newaxis());
// chi_prompt is the nu-fission matrix normalized over outgoing groups
for (size_t a = 0; a < n_ang; a++)
for (size_t gin = 0; gin < n_g_; gin++)
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
xt::xtensor<double, 4> temp_matrix_d({n_ang, n_dg_, n_g_, n_g_}, 0.);
read_nd_vector(xsdata_grp, "delayed-nu-fission", temp_matrix_d, true);
tensor::Tensor<double> temp_matrix_d =
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 = 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
// have already stored in prompt_nu_fission
chi_delayed = temp_matrix_d / xt::view(delayed_nu_fission, xt::all(),
xt::all(), xt::all(), xt::newaxis());
// chi_delayed is the delayed nu-fission matrix normalized over outgoing
// 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++)
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)
@ -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.
// Get nu-fission matrix
xt::xtensor<double, 3> temp_matrix({n_ang, n_g_, n_g_}, 0.);
read_nd_vector(xsdata_grp, "nu-fission", temp_matrix, true);
tensor::Tensor<double> temp_matrix =
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 = xt::sum(temp_matrix, {2});
prompt_nu_fission = temp_matrix.sum(2);
// chi_prompt is this matrix but normalized over outgoing groups, which we
// have already stored in prompt_nu_fission
chi_prompt = temp_matrix /
xt::view(prompt_nu_fission, xt::all(), xt::all(), xt::newaxis());
// chi_prompt is the nu-fission matrix normalized over outgoing groups
for (size_t a = 0; a < n_ang; a++)
for (size_t gin = 0; gin < n_g_; gin++)
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)
{
// Get the fission and kappa_fission data xs; these are optional
read_nd_vector(xsdata_grp, "fission", fission);
read_nd_vector(xsdata_grp, "kappa-fission", kappa_fission);
read_nd_tensor(xsdata_grp, "fission", 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
// 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) {
nu_fission = prompt_nu_fission;
} 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");
// Get the outgoing group boundary indices
xt::xtensor<int, 2> gmin({n_ang, n_g_}, 0.);
read_nd_vector(scatt_grp, "g_min", gmin, true);
xt::xtensor<int, 2> gmax({n_ang, n_g_}, 0.);
read_nd_vector(scatt_grp, "g_max", gmax, true);
tensor::Tensor<int> gmin = tensor::zeros<int>({n_ang, n_g_});
read_nd_tensor(scatt_grp, "g_min", gmin, true);
tensor::Tensor<int> gmax = tensor::zeros<int>({n_ang, n_g_});
read_nd_tensor(scatt_grp, "g_max", gmax, true);
// Make gmin and gmax start from 0 vice 1 as they do in the library
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
// 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_));
xt::xtensor<double, 1> temp_arr({length}, 0.);
read_nd_vector(scatt_grp, "scatter_matrix", temp_arr, true);
tensor::Tensor<double> temp_arr = tensor::zeros<double>({length});
read_nd_tensor(scatt_grp, "scatter_matrix", temp_arr, true);
// Compare the number of orders given with the max order of the problem;
// 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_));
if (object_exists(scatt_grp, "multiplicity_matrix")) {
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
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) {
for (size_t a = 0; a < n_ang; a++) {
ScattDataLegendre legendre_scatt;
xt::xtensor<int, 1> in_gmin = xt::view(gmin, a, xt::all());
xt::xtensor<int, 1> in_gmax = xt::view(gmax, a, xt::all());
tensor::Tensor<int> in_gmin(gmin.slice(a));
tensor::Tensor<int> in_gmax(gmax.slice(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
// Initialize the ScattData object with this data
for (size_t a = 0; a < n_ang; a++) {
xt::xtensor<int, 1> in_gmin = xt::view(gmin, a, xt::all());
xt::xtensor<int, 1> in_gmax = xt::view(gmax, a, xt::all());
tensor::Tensor<int> in_gmin(gmin.slice(a));
tensor::Tensor<int> in_gmax(gmax.slice(a));
scatter[a]->init(in_gmin, in_gmax, temp_mult[a], input_scatt[a]);
}
}
@ -519,33 +600,67 @@ void XsData::combine(
if (i == 0) {
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;
prompt_nu_fission += scalar * that->prompt_nu_fission;
kappa_fission += scalar * that->kappa_fission;
fission += scalar * that->fission;
delayed_nu_fission += scalar * that->delayed_nu_fission;
chi_prompt += scalar *
xt::view(xt::sum(that->prompt_nu_fission, {1}), xt::all(),
xt::newaxis(), xt::newaxis()) *
that->chi_prompt;
chi_delayed += scalar *
xt::view(xt::sum(that->delayed_nu_fission, {2}), xt::all(),
xt::all(), xt::newaxis(), xt::newaxis()) *
that->chi_delayed;
// Accumulate chi_prompt weighted by total prompt nu-fission
// (summed over energy groups) for this constituent
{
auto pnf_sum = that->prompt_nu_fission.sum(1);
size_t n_ang = chi_prompt.shape(0);
size_t n_g = chi_prompt.shape(1);
for (size_t a = 0; a < n_ang; a++)
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;
}
// Ensure the chi_prompt and chi_delayed are normalized to 1 for each
// azimuthal angle and delayed group (for chi_delayed)
chi_prompt /=
xt::view(xt::sum(chi_prompt, {2}), xt::all(), xt::all(), xt::newaxis());
chi_delayed /= xt::view(
xt::sum(chi_delayed, {3}), xt::all(), xt::all(), xt::all(), xt::newaxis());
// Normalize chi_prompt so it sums to 1 over outgoing groups
{
size_t n_ang = chi_prompt.shape(0);
size_t n_g = chi_prompt.shape(1);
for (size_t a = 0; a < n_ang; a++)
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
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
vector<ScattData*> those_scatts(those_xs.size());
for (size_t i = 0; i < those_xs.size(); i++) {

View file

@ -7,6 +7,7 @@ set(TEST_NAMES
test_mcpl_stat_sum
test_mesh
test_region
test_tensor
# 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>
#endif
#include <cassert>
#include "openmc/capi.h"
#include "openmc/cell.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