diff --git a/.travis.yml b/.travis.yml index de83325e03..f61fa1db0b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,16 +1,18 @@ sudo: required -dist: trusty +dist: xenial language: python python: - - "3.4" - "3.5" - "3.6" + - "3.7" addons: apt: packages: - - gfortran - - mpich - - libmpich-dev + - gfortran + - mpich + - libmpich-dev + - libhdf5-serial-dev + - libhdf5-mpich-dev cache: directories: - $HOME/nndc_hdf5 @@ -31,10 +33,6 @@ env: - OMP=y MPI=n PHDF5=n - OMP=n MPI=y PHDF5=n - OMP=n MPI=y PHDF5=y -before_install: - - sudo add-apt-repository ppa:nschloe/hdf5-backports -y - - sudo apt-get update -q - - sudo apt-get install libhdf5-serial-dev libhdf5-mpich-dev -y install: - ./tools/ci/travis-install.sh before_script: diff --git a/CMakeLists.txt b/CMakeLists.txt index c94f4df5fd..dc9a1231ba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -84,8 +84,8 @@ if(CMAKE_Fortran_COMPILER_ID STREQUAL GNU) # Make sure version is sufficient execute_process(COMMAND ${CMAKE_Fortran_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) - if(GCC_VERSION VERSION_LESS 4.8) - message(FATAL_ERROR "gfortran version must be 4.8 or higher") + if(GCC_VERSION VERSION_LESS 4.9) + message(FATAL_ERROR "gcc version must be 4.9 or higher") endif() # GCC compiler options @@ -210,7 +210,7 @@ elseif(CMAKE_C_COMPILER_ID MATCHES Clang) endif() -list(APPEND cxxflags -std=c++11 -O2) +list(APPEND cxxflags -std=c++14 -O2) if(debug) list(REMOVE_ITEM cxxflags -O2) list(APPEND cxxflags -g -O0) @@ -236,6 +236,14 @@ message(STATUS "Linker flags: ${ldflags}") add_library(pugixml vendor/pugixml/pugixml.cpp) target_include_directories(pugixml PUBLIC vendor/pugixml/) +#=============================================================================== +# xtensor header-only library +#=============================================================================== + +add_subdirectory(vendor/xtl) +add_subdirectory(vendor/xtensor) +target_link_libraries(xtensor INTERFACE xtl) + #=============================================================================== # RPATH information #=============================================================================== @@ -280,7 +288,6 @@ set_target_properties(faddeeva PROPERTIES add_library(libopenmc SHARED src/algorithm.F90 - src/angle_distribution.F90 src/angleenergy_header.F90 src/bank_header.F90 src/api.F90 @@ -298,7 +305,6 @@ add_library(libopenmc SHARED src/eigenvalue.F90 src/endf.F90 src/endf_header.F90 - src/energy_distribution.F90 src/error.F90 src/geometry.F90 src/geometry_header.F90 @@ -326,7 +332,6 @@ add_library(libopenmc SHARED src/physics_mg.F90 src/plot.F90 src/plot_header.F90 - src/product_header.F90 src/progress_header.F90 src/pugixml/pugixml_f.F90 src/random_lcg.F90 @@ -334,9 +339,6 @@ add_library(libopenmc SHARED src/relaxng src/sab_header.F90 src/secondary_correlated.F90 - src/secondary_kalbach.F90 - src/secondary_nbody.F90 - src/secondary_uncorrelated.F90 src/set_header.F90 src/settings.F90 src/simulation_header.F90 @@ -384,6 +386,12 @@ add_library(libopenmc SHARED src/tallies/trigger.F90 src/tallies/trigger_header.F90 src/cell.cpp + src/distribution.cpp + src/distribution_angle.cpp + src/distribution_energy.cpp + src/distribution_multi.cpp + src/distribution_spatial.cpp + src/endf.cpp src/initialize.cpp src/finalize.cpp src/geometry_aux.cpp @@ -398,6 +406,12 @@ add_library(libopenmc SHARED src/position.cpp src/pugixml/pugixml_c.cpp src/random_lcg.cpp + src/reaction.cpp + src/reaction_product.cpp + src/secondary_correlated.cpp + src/secondary_kalbach.cpp + src/secondary_nbody.cpp + src/secondary_uncorrelated.cpp src/scattdata.cpp src/settings.cpp src/simulation.cpp @@ -450,7 +464,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} pugixml - faddeeva) + faddeeva xtensor) #=============================================================================== # openmc executable diff --git a/docs/source/devguide/styleguide.rst b/docs/source/devguide/styleguide.rst index e0e67a6d62..2b366c191e 100644 --- a/docs/source/devguide/styleguide.rst +++ b/docs/source/devguide/styleguide.rst @@ -174,9 +174,7 @@ Follow the `C++ Core Guidelines`_ except when they conflict with another guideline listed here. For convenience, many important guidelines from that list are repeated here. -Conform to the C++11 standard. Note that this is a significant difference -between our style and the C++ Core Guidelines. Many suggestions in those -Guidelines require C++14. +Conform to the C++14 standard. Always use C++-style comments (``//``) as opposed to C-style (``/**/``). (It is more difficult to comment out a large section of code that uses C-style diff --git a/src/angle_distribution.F90 b/src/angle_distribution.F90 deleted file mode 100644 index a1f20e7799..0000000000 --- a/src/angle_distribution.F90 +++ /dev/null @@ -1,130 +0,0 @@ -module angle_distribution - - use algorithm, only: binary_search - use constants, only: ZERO, ONE, HISTOGRAM, LINEAR_LINEAR - use distribution_univariate, only: DistributionContainer, Tabular - use hdf5_interface, only: read_attribute, get_shape, read_dataset, & - open_dataset, close_dataset, HID_T, HSIZE_T - use random_lcg, only: prn - - implicit none - private - -!=============================================================================== -! ANGLEDISTRIBUTION represents an angular distribution that is to be used in an -! uncorrelated angle-energy distribution. This occurs whenever the angle -! distrbution is given in File 4 in an ENDF file. The distribution of angles -! depends on the incoming energy of the neutron, so this type stores a -! distribution for each of a set of incoming energies. -!=============================================================================== - - type, public :: AngleDistribution - real(8), allocatable :: energy(:) - type(DistributionContainer), allocatable :: distribution(:) - contains - procedure :: sample => angle_sample - procedure :: from_hdf5 => angle_from_hdf5 - end type AngleDistribution - -contains - - function angle_sample(this, E) result(mu) - class(AngleDistribution), intent(in) :: this - real(8), intent(in) :: E ! incoming energy - real(8) :: mu ! sampled cosine of scattering angle - - integer :: i ! index on incoming energy grid - integer :: n ! number of incoming energies - real(8) :: r ! interpolation factor on incoming energy grid - - ! Determine number of incoming energies - n = size(this%energy) - - ! Find energy bin and calculate interpolation factor -- if the energy is - ! outside the range of the tabulated energies, choose the first or last bins - if (E < this%energy(1)) then - i = 1 - r = ZERO - elseif (E > this%energy(n)) then - i = n - 1 - r = ONE - else - i = binary_search(this%energy, n, E) - r = (E - this%energy(i))/(this%energy(i+1) - this%energy(i)) - end if - - ! Sample between the ith and (i+1)th bin - if (r > prn()) i = i + 1 - - ! Sample i-th distribution - mu = this%distribution(i)%obj%sample() - - ! Make sure mu is in range [-1,1] - if (abs(mu) > ONE) mu = sign(ONE, mu) - end function angle_sample - - subroutine angle_from_hdf5(this, group_id) - class(AngleDistribution), intent(inout) :: this - integer(HID_T), intent(in) :: group_id - - integer :: i, j - integer :: n - integer :: n_energy - integer(HID_T) :: dset_id - integer(HSIZE_T) :: dims(1), dims2(2) - integer, allocatable :: offsets(:) - integer, allocatable :: interp(:) - real(8), allocatable :: temp(:,:) - - ! Get incoming energies - dset_id = open_dataset(group_id, 'energy') - call get_shape(dset_id, dims) - n_energy = int(dims(1), 4) - allocate(this % energy(n_energy)) - allocate(this % distribution(n_energy)) - call read_dataset(this % energy, dset_id) - call close_dataset(dset_id) - - ! Get outgoing energy distribution data - dset_id = open_dataset(group_id, 'mu') - call read_attribute(offsets, dset_id, 'offsets') - call read_attribute(interp, dset_id, 'interpolation') - call get_shape(dset_id, dims2) - allocate(temp(dims2(1), dims2(2))) - call read_dataset(temp, dset_id) - call close_dataset(dset_id) - - do i = 1, n_energy - ! Determine number of outgoing energies - j = offsets(i) - if (i < n_energy) then - n = offsets(i+1) - j - else - n = size(temp, 1) - j - end if - - ! Create and initialize tabular distribution - allocate(Tabular :: this % distribution(i) % obj) - select type (mudist => this % distribution(i) % obj) - type is (Tabular) - mudist % interpolation = interp(i) - allocate(mudist % x(n), mudist % p(n), mudist % c(n)) - mudist % x(:) = temp(j+1:j+n, 1) - mudist % p(:) = temp(j+1:j+n, 2) - - ! 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.) then - mudist % c(:) = temp(j+1:j+n, 3) - else - call mudist % initialize(temp(j+1:j+n, 1), temp(j+1:j+n, 2), interp(i)) - end if - end select - - j = j + n - end do - end subroutine angle_from_hdf5 - -end module angle_distribution diff --git a/src/angle_energy.h b/src/angle_energy.h new file mode 100644 index 0000000000..9c04ff8c70 --- /dev/null +++ b/src/angle_energy.h @@ -0,0 +1,21 @@ +#ifndef OPENMC_ANGLE_ENERGY_H +#define OPENMC_ANGLE_ENERGY_H + +namespace openmc { + +//============================================================================== +//! Abstract type that defines a correlated or uncorrelated angle-energy +//! distribution that is a function of incoming energy. Each derived type must +//! implement a sample() method that returns an outgoing energy and +//! scattering cosine given an incoming energy. +//============================================================================== + +class AngleEnergy { +public: + virtual void sample(double E_in, double& E_out, double& mu) const = 0; + virtual ~AngleEnergy() = default; +}; + +} + +#endif // OPENMC_ANGLE_ENERGY_H diff --git a/src/cell.cpp b/src/cell.cpp index acf1473bb8..798935358f 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -1,7 +1,6 @@ #include "cell.h" #include -#include #include #include @@ -19,14 +18,13 @@ namespace openmc { // Constants //============================================================================== +// TODO: Convert to enum constexpr int32_t OP_LEFT_PAREN {std::numeric_limits::max()}; constexpr int32_t OP_RIGHT_PAREN {std::numeric_limits::max() - 1}; constexpr int32_t OP_COMPLEMENT {std::numeric_limits::max() - 2}; constexpr int32_t OP_INTERSECTION {std::numeric_limits::max() - 3}; constexpr int32_t OP_UNION {std::numeric_limits::max() - 4}; -extern "C" double FP_PRECISION; - //============================================================================== // Global variables //============================================================================== diff --git a/src/cell.h b/src/cell.h index 062251b48c..0b42a656c3 100644 --- a/src/cell.h +++ b/src/cell.h @@ -2,6 +2,7 @@ #define OPENMC_CELL_H #include +#include #include #include #include @@ -18,6 +19,7 @@ namespace openmc { // Constants //============================================================================== +// TODO: Convert to enum extern "C" int FILL_MATERIAL; extern "C" int FILL_UNIVERSE; extern "C" int FILL_LATTICE; diff --git a/src/constants.F90 b/src/constants.F90 index 4de52322e0..ab85b277a6 100644 --- a/src/constants.F90 +++ b/src/constants.F90 @@ -131,34 +131,6 @@ module constants ! Void material integer, parameter :: MATERIAL_VOID = -1 - ! Lattice types - integer, parameter :: & - LATTICE_RECT = 1, & ! Rectangular lattice - LATTICE_HEX = 2 ! Hexagonal lattice - - ! Lattice boundary crossings - integer, parameter :: & - LATTICE_LEFT = 1, & ! Flag for crossing left (x) lattice boundary - LATTICE_RIGHT = 2, & ! Flag for crossing right (x) lattice boundary - LATTICE_BACK = 3, & ! Flag for crossing back (y) lattice boundary - LATTICE_FRONT = 4, & ! Flag for crossing front (y) lattice boundary - LATTICE_BOTTOM = 5, & ! Flag for crossing bottom (z) lattice boundary - LATTICE_TOP = 6 ! Flag for crossing top (z) lattice boundary - - ! Surface types - integer, parameter :: & - SURF_PX = 1, & ! Plane parallel to x-plane - SURF_PY = 2, & ! Plane parallel to y-plane - SURF_PZ = 3, & ! Plane parallel to z-plane - SURF_PLANE = 4, & ! Arbitrary plane - SURF_CYL_X = 5, & ! Cylinder along x-axis - SURF_CYL_Y = 6, & ! Cylinder along y-axis - SURF_CYL_Z = 7, & ! Cylinder along z-axis - SURF_SPHERE = 8, & ! Sphere - SURF_CONE_X = 9, & ! Cone parallel to x-axis - SURF_CONE_Y = 10, & ! Cone parallel to y-axis - SURF_CONE_Z = 11 ! Cone parallel to z-axis - ! Flag to say that the outside of a lattice is not defined integer, parameter :: NO_OUTER_UNIVERSE = -1 @@ -238,12 +210,6 @@ module constants ! Depletion reactions integer, parameter :: DEPLETION_RX(6) = [N_GAMMA, N_P, N_A, N_2N, N_3N, N_4N] - ! ACE table types - integer, parameter :: & - ACE_NEUTRON = 1, & ! continuous-energy neutron - ACE_THERMAL = 2, & ! thermal S(a,b) scattering data - ACE_DOSIMETRY = 3 ! dosimetry cross sections - ! MGXS Table Types integer, parameter :: & MGXS_ISOTROPIC = 1, & ! Isotropically Weighted Data @@ -265,11 +231,6 @@ module constants EMISSION_DELAYED = 2, & ! Delayed emission of secondary particle EMISSION_TOTAL = 3 ! Yield represents total emission (prompt + delayed) - ! Cross section filetypes - integer, parameter :: & - ASCII = 1, & ! ASCII cross section file - BINARY = 2 ! Binary cross section file - ! Library types integer, parameter :: & LIBRARY_NEUTRON = 1, & @@ -350,9 +311,6 @@ module constants SCORE_FISS_Q_RECOV = -15, & ! recoverable fission Q-value SCORE_DECAY_RATE = -16 ! delayed neutron precursor decay rate - ! Maximum scattering order supported - integer, parameter :: MAX_ANG_ORDER = 10 - ! Tally map bin finding integer, parameter :: NO_BIN_FOUND = -1 diff --git a/src/constants.h b/src/constants.h index 016a6b3484..b6bb82d375 100644 --- a/src/constants.h +++ b/src/constants.h @@ -1,8 +1,8 @@ //! \file constants.h //! A collection of constants -#ifndef CONSTANTS_H -#define CONSTANTS_H +#ifndef OPENMC_CONSTANTS_H +#define OPENMC_CONSTANTS_H #include #include @@ -11,6 +11,7 @@ namespace openmc { +// TODO: Replace with xtensor/other library? typedef std::vector double_1dvec; typedef std::vector > double_2dvec; typedef std::vector > > double_3dvec; @@ -21,29 +22,283 @@ typedef std::vector int_1dvec; typedef std::vector > int_2dvec; typedef std::vector > > int_3dvec; -constexpr int MAX_SAMPLE {10000}; +// ============================================================================ +// VERSIONING NUMBERS -constexpr std::array VERSION {0, 10, 0}; +// OpenMC major, minor, and release numbers +constexpr int VERSION_MAJOR {0}; +constexpr int VERSION_MINOR {10}; +constexpr int VERSION_RELEASE {0}; +constexpr std::array VERSION {VERSION_MAJOR, VERSION_MINOR, VERSION_RELEASE}; + +// HDF5 data format +constexpr int HDF5_VERSION[] {1, 0}; + +// Version numbers for binary files constexpr std::array VERSION_PARTICLE_RESTART {2, 0}; +constexpr std::array VERSION_TRACK {2, 0}; +constexpr std::array VERSION_SUMMARY {6, 0}; +constexpr std::array VERSION_VOLUME {1, 0}; +constexpr std::array VERSION_VOXEL {1, 0}; +constexpr std::array VERSION_MGXS_LIBRARY {1, 0}; +constexpr char VERSION_MULTIPOLE[] {"v0.2"}; + +// ============================================================================ +// ADJUSTABLE PARAMETERS + +// NOTE: This is the only section of the constants module that should ever be +// adjusted. Modifying constants in other sections may cause the code to fail. + +// Monoatomic ideal-gas scattering treatment threshold +constexpr double FREE_GAS_THRESHOLD {400.0}; + +// Significance level for confidence intervals +constexpr double CONFIDENCE_LEVEL {0.95}; + +// Used for surface current tallies +constexpr double TINY_BIT {1e-8}; + +// User for precision in geometry +constexpr double FP_PRECISION {1e-14}; +constexpr double FP_REL_PRECISION {1e-5}; +constexpr double FP_COINCIDENT {1e-12}; + +// Maximum number of collisions/crossings +constexpr int MAX_EVENTS {1000000}; +constexpr int MAX_SAMPLE {100000}; // Maximum number of words in a single line, length of line, and length of // single word -constexpr int MAX_WORDS {500}; -constexpr int MAX_LINE_LEN {250}; constexpr int MAX_WORD_LEN {150}; -constexpr int MAX_FILE_LEN {255}; -// Physical Constants -constexpr double K_BOLTZMANN {8.6173303e-5}; // Boltzmann constant in eV/K +// Maximum number of external source spatial resamples to encounter before an +// error is thrown. +constexpr int EXTSRC_REJECT_THRESHOLD {10000}; +constexpr double EXTSRC_REJECT_FRACTION {0.05}; + +// ============================================================================ +// MATH AND PHYSICAL CONSTANTS + +// Values here are from the Committee on Data for Science and Technology +// (CODATA) 2014 recommendation (doi:10.1103/RevModPhys.88.035009). + +// TODO: cmath::M_PI has 3 more digits precision than the Fortran constant we +// use so for now we will reuse the Fortran constant until we are OK with +// modifying test results +constexpr double PI {3.1415926535898}; +const double SQRT_PI {std::sqrt(PI)}; +constexpr double INFTY {std::numeric_limits::max()}; + +// Physical constants +constexpr double MASS_NEUTRON {1.00866491588}; // mass of a neutron in amu +constexpr double MASS_NEUTRON_EV {939.5654133e6}; // mass of a neutron in eV/c^2 +constexpr double MASS_PROTON {1.007276466879}; // mass of a proton in amu +constexpr double MASS_ELECTRON_EV {0.5109989461e6}; // electron mass energy equivalent in eV/c^2 +constexpr double FINE_STRUCTURE {137.035999139}; // inverse fine structure constant +constexpr double PLANCK_C {1.2398419739062977e4}; // Planck's constant times c in eV-Angstroms +constexpr double AMU {1.660539040e-27}; // 1 amu in kg +constexpr double C_LIGHT {2.99792458e8}; // speed of light in m/s +constexpr double N_AVOGADRO {0.6022140857}; // Avogadro's number in 10^24/mol +constexpr double K_BOLTZMANN {8.6173303e-5}; // Boltzmann constant in eV/K + +// Electron subshell labels +constexpr char SUBSHELLS[][4] { + "K ", "L1 ", "L2 ", "L3 ", "M1 ", "M2 ", "M3 ", "M4 ", "M5 ", + "N1 ", "N2 ", "N3 ", "N4 ", "N5 ", "N6 ", "N7 ", "O1 ", "O2 ", + "O3 ", "O4 ", "O5 ", "O6 ", "O7 ", "O8 ", "O9 ", "P1 ", "P2 ", + "P3 ", "P4 ", "P5 ", "P6 ", "P7 ", "P8 ", "P9 ", "P10", "P11", + "Q1 ", "Q2 ", "Q3 " +}; + +// Void material +// TODO: refactor and remove +constexpr int MATERIAL_VOID {-1}; + +// ============================================================================ +// CROSS SECTION RELATED CONSTANTS // Angular distribution type +// TODO: Convert to enum constexpr int ANGLE_ISOTROPIC {1}; constexpr int ANGLE_32_EQUI {2}; constexpr int ANGLE_TABULAR {3}; constexpr int ANGLE_LEGENDRE {4}; constexpr int ANGLE_HISTOGRAM {5}; +// Temperature treatment method +// TODO: Convert to enum? +constexpr int TEMPERATURE_NEAREST {1}; +constexpr int TEMPERATURE_INTERPOLATION {2}; + +// Secondary energy mode for S(a,b) inelastic scattering +// TODO: Convert to enum +constexpr int SAB_SECONDARY_EQUAL {0}; // Equally-likely outgoing energy bins +constexpr int SAB_SECONDARY_SKEWED {1}; // Skewed outgoing energy bins +constexpr int SAB_SECONDARY_CONT {2}; // Continuous, linear-linear interpolation + +// Elastic mode for S(a,b) elastic scattering +// TODO: Convert to enum +constexpr int SAB_ELASTIC_DISCRETE {3}; // Sample from discrete cosines +constexpr int SAB_ELASTIC_EXACT {4}; // Exact treatment for coherent elastic + +// Reaction types +// TODO: Convert to enum +constexpr int TOTAL_XS {1}; +constexpr int ELASTIC {2}; +constexpr int N_NONELASTIC {3}; +constexpr int N_LEVEL {4}; +constexpr int MISC {5}; +constexpr int N_2ND {11}; +constexpr int N_2N {16}; +constexpr int N_3N {17}; +constexpr int N_FISSION {18}; +constexpr int N_F {19}; +constexpr int N_NF {20}; +constexpr int N_2NF {21}; +constexpr int N_NA {22}; +constexpr int N_N3A {23}; +constexpr int N_2NA {24}; +constexpr int N_3NA {25}; +constexpr int N_NP {28}; +constexpr int N_N2A {29}; +constexpr int N_2N2A {30}; +constexpr int N_ND {32}; +constexpr int N_NT {33}; +constexpr int N_N3HE {34}; +constexpr int N_ND2A {35}; +constexpr int N_NT2A {36}; +constexpr int N_4N {37}; +constexpr int N_3NF {38}; +constexpr int N_2NP {41}; +constexpr int N_3NP {42}; +constexpr int N_N2P {44}; +constexpr int N_NPA {45}; +constexpr int N_N1 {51}; +constexpr int N_N40 {90}; +constexpr int N_NC {91}; +constexpr int N_DISAPPEAR {101}; +constexpr int N_GAMMA {102}; +constexpr int N_P {103}; +constexpr int N_D {104}; +constexpr int N_T {105}; +constexpr int N_3HE {106}; +constexpr int N_A {107}; +constexpr int N_2A {108}; +constexpr int N_3A {109}; +constexpr int N_2P {111}; +constexpr int N_PA {112}; +constexpr int N_T2A {113}; +constexpr int N_D2A {114}; +constexpr int N_PD {115}; +constexpr int N_PT {116}; +constexpr int N_DA {117}; +constexpr int N_5N {152}; +constexpr int N_6N {153}; +constexpr int N_2NT {154}; +constexpr int N_TA {155}; +constexpr int N_4NP {156}; +constexpr int N_3ND {157}; +constexpr int N_NDA {158}; +constexpr int N_2NPA {159}; +constexpr int N_7N {160}; +constexpr int N_8N {161}; +constexpr int N_5NP {162}; +constexpr int N_6NP {163}; +constexpr int N_7NP {164}; +constexpr int N_4NA {165}; +constexpr int N_5NA {166}; +constexpr int N_6NA {167}; +constexpr int N_7NA {168}; +constexpr int N_4ND {169}; +constexpr int N_5ND {170}; +constexpr int N_6ND {171}; +constexpr int N_3NT {172}; +constexpr int N_4NT {173}; +constexpr int N_5NT {174}; +constexpr int N_6NT {175}; +constexpr int N_2N3HE {176}; +constexpr int N_3N3HE {177}; +constexpr int N_4N3HE {178}; +constexpr int N_3N2P {179}; +constexpr int N_3N3A {180}; +constexpr int N_3NPA {181}; +constexpr int N_DT {182}; +constexpr int N_NPD {183}; +constexpr int N_NPT {184}; +constexpr int N_NDT {185}; +constexpr int N_NP3HE {186}; +constexpr int N_ND3HE {187}; +constexpr int N_NT3HE {188}; +constexpr int N_NTA {189}; +constexpr int N_2N2P {190}; +constexpr int N_P3HE {191}; +constexpr int N_D3HE {192}; +constexpr int N_3HEA {193}; +constexpr int N_4N2P {194}; +constexpr int N_4N2A {195}; +constexpr int N_4NPA {196}; +constexpr int N_3P {197}; +constexpr int N_N3P {198}; +constexpr int N_3N2PA {199}; +constexpr int N_5N2P {200}; +constexpr int COHERENT {502}; +constexpr int INCOHERENT {504}; +constexpr int PAIR_PROD_ELEC {515}; +constexpr int PAIR_PROD {516}; +constexpr int PAIR_PROD_NUC {517}; +constexpr int PHOTOELECTRIC {522}; +constexpr int N_P0 {600}; +constexpr int N_PC {649}; +constexpr int N_D0 {650}; +constexpr int N_DC {699}; +constexpr int N_T0 {700}; +constexpr int N_TC {749}; +constexpr int N_3HE0 {750}; +constexpr int N_3HEC {799}; +constexpr int N_A0 {800}; +constexpr int N_AC {849}; +constexpr int N_2N0 {875}; +constexpr int N_2NC {891}; + +// Fission neutron emission (nu) type +constexpr int NU_NONE {0}; // No nu values (non-fissionable) +constexpr int NU_POLYNOMIAL {1}; // Nu values given by polynomial +constexpr int NU_TABULAR {2}; // Nu values given by tabular distribution + +// Library types +constexpr int LIBRARY_NEUTRON {1}; +constexpr int LIBRARY_THERMAL {2}; +constexpr int LIBRARY_PHOTON {3}; +constexpr int LIBRARY_MULTIGROUP {4}; + +// Probability table parameters +constexpr int URR_CUM_PROB {1}; +constexpr int URR_TOTAL {2}; +constexpr int URR_ELASTIC {3}; +constexpr int URR_FISSION {4}; +constexpr int URR_N_GAMMA {5}; +constexpr int URR_HEATING {6}; + +// Maximum number of partial fission reactions +constexpr int PARTIAL_FISSION_MAX {4}; + +// Resonance elastic scattering methods +// TODO: Convert to enum +constexpr int RES_SCAT_ARES {1}; +constexpr int RES_SCAT_DBRC {2}; +constexpr int RES_SCAT_WCM {3}; +constexpr int RES_SCAT_CXS {4}; + +// Electron treatments +// TODO: Convert to enum +constexpr int ELECTRON_LED {1}; // Local Energy Deposition +constexpr int ELECTRON_TTB {2}; // Thick Target Bremsstrahlung + +// ============================================================================ +// MULTIGROUP RELATED + // MGXS Table Types +// TODO: Convert to enum constexpr int MGXS_ISOTROPIC {1}; // Isotroically weighted data constexpr int MGXS_ANGLE {2}; // Data by angular bins @@ -53,18 +308,8 @@ constexpr double MACROSCOPIC_AWR {-2.}; // Number of mu bins to use when converting Legendres to tabular type constexpr int DEFAULT_NMU {33}; -// Temperature treatment method -constexpr int TEMPERATURE_NEAREST {1}; -constexpr int TEMPERATURE_INTERPOLATION {2}; - -// TODO: cmath::M_PI has 3 more digits precision than the Fortran constant we -// use so for now we will reuse the Fortran constant until we are OK with -// modifying test results -constexpr double PI {3.1415926535898}; - -const double SQRT_PI {std::sqrt(PI)}; - // Mgxs::get_xs enumerated types +// TODO: Convert to enum constexpr int MG_GET_XS_TOTAL {0}; constexpr int MG_GET_XS_ABSORPTION {1}; constexpr int MG_GET_XS_INVERSE_VELOCITY {2}; @@ -81,11 +326,120 @@ constexpr int MG_GET_XS_NU_FISSION {12}; constexpr int MG_GET_XS_CHI_PROMPT {13}; constexpr int MG_GET_XS_CHI_DELAYED {14}; -extern "C" double FP_COINCIDENT; -extern "C" double FP_PRECISION; -constexpr double INFTY {std::numeric_limits::max()}; +// ============================================================================ +// TALLY-RELATED CONSTANTS + +// Tally result entries +constexpr int RESULT_VALUE {1}; +constexpr int RESULT_SUM {2}; +constexpr int RESULT_SUM_SQ {3}; + +// Tally type +// TODO: Convert to enum +constexpr int TALLY_VOLUME {1}; +constexpr int TALLY_MESH_SURFACE {2}; +constexpr int TALLY_SURFACE {3}; + +// Tally estimator types +// TODO: Convert to enum +constexpr int ESTIMATOR_ANALOG {1}; +constexpr int ESTIMATOR_TRACKLENGTH {2}; +constexpr int ESTIMATOR_COLLISION {3}; + +// Event types for tallies +// TODO: Convert to enum +constexpr int EVENT_SURFACE {-2}; +constexpr int EVENT_LATTICE {-1}; +constexpr int EVENT_SCATTER {1}; +constexpr int EVENT_ABSORB {2}; + +// Tally score type -- if you change these, make sure you also update the +// _SCORES dictionary in openmc/capi/tally.py +// TODO: Convert to enum +constexpr int SCORE_FLUX {-1}; // flux +constexpr int SCORE_TOTAL {-2}; // total reaction rate +constexpr int SCORE_SCATTER {-3}; // scattering rate +constexpr int SCORE_NU_SCATTER {-4}; // scattering production rate +constexpr int SCORE_ABSORPTION {-5}; // absorption rate +constexpr int SCORE_FISSION {-6}; // fission rate +constexpr int SCORE_NU_FISSION {-7}; // neutron production rate +constexpr int SCORE_KAPPA_FISSION {-8}; // fission energy production rate +constexpr int SCORE_CURRENT {-9}; // current +constexpr int SCORE_EVENTS {-10}; // number of events +constexpr int SCORE_DELAYED_NU_FISSION {-11}; // delayed neutron production rate +constexpr int SCORE_PROMPT_NU_FISSION {-12}; // prompt neutron production rate +constexpr int SCORE_INVERSE_VELOCITY {-13}; // flux-weighted inverse velocity +constexpr int SCORE_FISS_Q_PROMPT {-14}; // prompt fission Q-value +constexpr int SCORE_FISS_Q_RECOV {-15}; // recoverable fission Q-value +constexpr int SCORE_DECAY_RATE {-16}; // delayed neutron precursor decay rate + +// Tally map bin finding +constexpr int NO_BIN_FOUND {-1}; + +// Tally filter and map types +// TODO: Refactor to remove or convert to enum +constexpr int FILTER_UNIVERSE {1}; +constexpr int FILTER_MATERIAL {2}; +constexpr int FILTER_CELL {3}; +constexpr int FILTER_CELLBORN {4}; +constexpr int FILTER_SURFACE {5}; +constexpr int FILTER_MESH {6}; +constexpr int FILTER_ENERGYIN {7}; +constexpr int FILTER_ENERGYOUT {8}; +constexpr int FILTER_DISTRIBCELL {9}; +constexpr int FILTER_MU {10}; +constexpr int FILTER_POLAR {11}; +constexpr int FILTER_AZIMUTHAL {12}; +constexpr int FILTER_DELAYEDGROUP {13}; +constexpr int FILTER_ENERGYFUNCTION {14}; +constexpr int FILTER_CELLFROM {15}; +constexpr int FILTER_MESHSURFACE {16}; +constexpr int FILTER_LEGENDRE {17}; +constexpr int FILTER_SPH_HARMONICS {18}; +constexpr int FILTER_SPTL_LEGENDRE {19}; +constexpr int FILTER_ZERNIKE {20}; +constexpr int FILTER_PARTICLE {21}; + +// Mesh types +constexpr int MESH_REGULAR {1}; + +// Tally surface current directions +constexpr int OUT_LEFT {1}; // x min +constexpr int IN_LEFT {2}; // x min +constexpr int OUT_RIGHT {3}; // x max +constexpr int IN_RIGHT {4}; // x max +constexpr int OUT_BACK {5}; // y min +constexpr int IN_BACK {6}; // y min +constexpr int OUT_FRONT {7}; // y max +constexpr int IN_FRONT {8}; // y max +constexpr int OUT_BOTTOM {9}; // z min +constexpr int IN_BOTTOM {10}; // z min +constexpr int OUT_TOP {11}; // z max +constexpr int IN_TOP {12}; // z max + +// Tally trigger types and threshold +constexpr int VARIANCE {1}; +constexpr int RELATIVE_ERROR {2}; +constexpr int STANDARD_DEVIATION {3}; + +// Global tally parameters +constexpr int K_COLLISION {1}; +constexpr int K_ABSORPTION {2}; +constexpr int K_TRACKLENGTH {3}; +constexpr int LEAKAGE {4}; + +// Differential tally independent variables +constexpr int DIFF_DENSITY {1}; +constexpr int DIFF_NUCLIDE_DENSITY {2}; +constexpr int DIFF_TEMPERATURE {3}; + constexpr int C_NONE {-1}; +// Interpolation rules +enum class Interpolation { + histogram, lin_lin, lin_log, log_lin, log_log +}; + } // namespace openmc -#endif // CONSTANTS_H +#endif // OPENMC_CONSTANTS_H diff --git a/src/distribution.cpp b/src/distribution.cpp new file mode 100644 index 0000000000..f513701fb0 --- /dev/null +++ b/src/distribution.cpp @@ -0,0 +1,266 @@ +#include "distribution.h" + +#include // for copy +#include // for sqrt, floor, max +#include // for back_inserter +#include // for accumulate +#include // for string, stod + +#include "error.h" +#include "math_functions.h" +#include "random_lcg.h" +#include "xml_interface.h" + +namespace openmc { + +//============================================================================== +// Discrete implementation +//============================================================================== + +Discrete::Discrete(pugi::xml_node node) +{ + auto params = get_node_array(node, "parameters"); + + std::size_t n = params.size(); + std::copy(params.begin(), params.begin() + n/2, std::back_inserter(x_)); + std::copy(params.begin() + n/2, params.end(), std::back_inserter(p_)); + + normalize(); +} + +Discrete::Discrete(const double* x, const double* p, int n) + : x_{x, x+n}, p_{p, p+n} +{ + normalize(); +} + +double Discrete::sample() const +{ + int n = x_.size(); + if (n > 1) { + double xi = prn(); + double c = 0.0; + for (int i = 0; i < n; ++i) { + c += p_[i]; + if (xi < c) return x_[i]; + } + // throw exception? + } else { + return x_[0]; + } +} + +void Discrete::normalize() +{ + // Renormalize density function so that it sums to unity + double norm = std::accumulate(p_.begin(), p_.end(), 0.0); + for (auto& p_i : p_) + p_i /= norm; +} + +//============================================================================== +// Uniform implementation +//============================================================================== + +Uniform::Uniform(pugi::xml_node node) +{ + auto params = get_node_array(node, "parameters"); + if (params.size() != 2) + openmc::fatal_error("Uniform distribution must have two " + "parameters specified."); + + a_ = params.at(0); + b_ = params.at(1); +} + +double Uniform::sample() const +{ + return a_ + prn()*(b_ - a_); +} + +//============================================================================== +// Maxwell implementation +//============================================================================== + +Maxwell::Maxwell(pugi::xml_node node) +{ + theta_ = std::stod(get_node_value(node, "parameters")); +} + +double Maxwell::sample() const +{ + return maxwell_spectrum_c(theta_); +} + +//============================================================================== +// Watt implementation +//============================================================================== + +Watt::Watt(pugi::xml_node node) +{ + auto params = get_node_array(node, "parameters"); + if (params.size() != 2) + openmc::fatal_error("Watt energy distribution must have two " + "parameters specified."); + + a_ = params.at(0); + b_ = params.at(1); +} + +double Watt::sample() const +{ + return watt_spectrum_c(a_, b_); +} + +//============================================================================== +// Tabular implementation +//============================================================================== + +Tabular::Tabular(pugi::xml_node node) +{ + if (check_for_node(node, "interpolation")) { + std::string temp = get_node_value(node, "interpolation"); + if (temp == "histogram") { + interp_ = Interpolation::histogram; + } else if (temp == "linear-linear") { + interp_ = Interpolation::lin_lin; + } else { + openmc::fatal_error("Unknown interpolation type for distribution: " + temp); + } + } else { + interp_ = Interpolation::histogram; + } + + // Read and initialize tabular distribution + auto params = get_node_array(node, "parameters"); + std::size_t n = params.size() / 2; + const double* x = params.data(); + const double* p = x + n; + init(x, p, n); +} + +Tabular::Tabular(const double* x, const double* p, int n, Interpolation interp, const double* c) + : interp_{interp} +{ + init(x, p, n, c); +} + +void Tabular::init(const double* x, const double* p, std::size_t n, const double* c) +{ + // Copy x/p arrays into vectors + std::copy(x, x + n, std::back_inserter(x_)); + std::copy(p, p + n, std::back_inserter(p_)); + + // Check interpolation parameter + if (interp_ != Interpolation::histogram && + interp_ != Interpolation::lin_lin) { + openmc::fatal_error("Only histogram and linear-linear interpolation " + "for tabular distribution is supported."); + } + + // Calculate cumulative distribution function + if (c) { + std::copy(c, c + n, std::back_inserter(c_)); + } else { + c_.resize(n); + c_[0] = 0.0; + for (int i = 1; i < n; ++i) { + if (interp_ == Interpolation::histogram) { + c_[i] = c_[i-1] + p_[i-1]*(x_[i] - x_[i-1]); + } else if (interp_ == Interpolation::lin_lin) { + c_[i] = c_[i-1] + 0.5*(p_[i-1] + p_[i]) * (x_[i] - x_[i-1]); + } + } + } + + // Normalize density and distribution functions + for (int i = 0; i < n; ++i) { + p_[i] = p_[i]/c_[n-1]; + c_[i] = c_[i]/c_[n-1]; + } +} + +double Tabular::sample() const +{ + // Sample value of CDF + double c = prn(); + + // Find first CDF bin which is above the sampled value + double c_i = c_[0]; + int i; + std::size_t n = c_.size(); + for (i = 0; i < n - 1; ++i) { + if (c <= c_[i+1]) break; + c_i = c_[i+1]; + } + + // Determine bounding PDF values + double x_i = x_[i]; + double p_i = p_[i]; + + if (interp_ == Interpolation::histogram) { + // Histogram interpolation + if (p_i > 0.0) { + return x_i + (c - c_i)/p_i; + } else { + return x_i; + } + } else { + // Linear-linear interpolation + double x_i1 = x_[i + 1]; + double p_i1 = p_[i + 1]; + + double m = (p_i1 - p_i)/(x_i1 - x_i); + if (m == 0.0) { + return x_i + (c - c_i)/p_i; + } else { + return x_i + (std::sqrt(std::max(0.0, p_i*p_i + 2*m*(c - c_i))) - p_i)/m; + } + } +} + +//============================================================================== +// Equiprobable implementation +//============================================================================== + +double Equiprobable::sample() const +{ + std::size_t n = x_.size(); + + double r = prn(); + int i = std::floor((n - 1)*r); + + double xl = x_[i]; + double xr = x_[i+i]; + return xl + ((n - 1)*r - i) * (xr - xl); +} + +//============================================================================== +// Helper function +//============================================================================== + +UPtrDist distribution_from_xml(pugi::xml_node node) +{ + if (!check_for_node(node, "type")) + openmc::fatal_error("Distribution type must be specified."); + + // Determine type of distribution + std::string type = get_node_value(node, "type", true, true); + + // Allocate extension of Distribution + if (type == "uniform") { + return UPtrDist{new Uniform(node)}; + } else if (type == "maxwell") { + return UPtrDist{new Maxwell(node)}; + } else if (type == "watt") { + return UPtrDist{new Watt(node)}; + } else if (type == "discrete") { + return UPtrDist{new Discrete(node)}; + } else if (type == "tabular") { + return UPtrDist{new Tabular(node)}; + } else { + openmc::fatal_error("Invalid distribution type: " + type); + } +} + +} // namespace openmc diff --git a/src/distribution.h b/src/distribution.h new file mode 100644 index 0000000000..84819e3f7c --- /dev/null +++ b/src/distribution.h @@ -0,0 +1,150 @@ +//! \file distribution.h +//! Univariate probability distributions + +#ifndef OPENMC_DISTRIBUTION_H +#define OPENMC_DISTRIBUTION_H + +#include // for size_t +#include // for unique_ptr +#include // for vector + +#include "pugixml.hpp" + +#include "constants.h" + +namespace openmc { + +//============================================================================== +//! Abstract class representing a univariate probability distribution +//============================================================================== + +class Distribution { +public: + virtual ~Distribution() = default; + virtual double sample() const = 0; +}; + +//============================================================================== +//! A discrete distribution (probability mass function) +//============================================================================== + +class Discrete : public Distribution { +public: + explicit Discrete(pugi::xml_node node); + Discrete(const double* x, const double* p, int n); + + //! Sample a value from the distribution + //! \return Sampled value + double sample() const; +private: + std::vector x_; //!< Possible outcomes + std::vector p_; //!< Probability of each outcome + + //! Normalize distribution so that probabilities sum to unity + void normalize(); +}; + +//============================================================================== +//! Uniform distribution over the interval [a,b] +//============================================================================== + +class Uniform : public Distribution { +public: + explicit Uniform(pugi::xml_node node); + Uniform(double a, double b) : a_{a}, b_{b} {}; + + //! Sample a value from the distribution + //! \return Sampled value + double sample() const; +private: + double a_; //!< Lower bound of distribution + double b_; //!< Upper bound of distribution +}; + +//============================================================================== +//! Maxwellian distribution of form c*E*exp(-E/theta) +//============================================================================== + +class Maxwell : public Distribution { +public: + explicit Maxwell(pugi::xml_node node); + Maxwell(double theta) : theta_{theta} { }; + + //! Sample a value from the distribution + //! \return Sampled value + double sample() const; +private: + double theta_; //!< Factor in exponential [eV] +}; + +//============================================================================== +//! Watt fission spectrum with form c*exp(-E/a)*sinh(sqrt(b*E)) +//============================================================================== + +class Watt : public Distribution { +public: + explicit Watt(pugi::xml_node node); + Watt(double a, double b) : a_{a}, b_{b} { }; + + //! Sample a value from the distribution + //! \return Sampled value + double sample() const; +private: + double a_; //!< Factor in exponential [eV] + double b_; //!< Factor in square root [1/eV] +}; + +//============================================================================== +//! Histogram or linear-linear interpolated tabular distribution +//============================================================================== + +class Tabular : public Distribution { +public: + explicit Tabular(pugi::xml_node node); + Tabular(const double* x, const double* p, int n, Interpolation interp, + const double* c=nullptr); + + //! Sample a value from the distribution + //! \return Sampled value + double sample() const; +private: + std::vector x_; //!< tabulated independent variable + std::vector p_; //!< tabulated probability density + std::vector c_; //!< cumulative distribution at tabulated values + Interpolation interp_; //!< interpolation rule + + //! Initialize tabulated probability density function + //! \param x Array of values for independent variable + //! \param p Array of tabulated probabilities + //! \param n Number of tabulated values + void init(const double* x, const double* p, std::size_t n, + const double* c=nullptr); +}; + +//============================================================================== +//! Equiprobable distribution +//============================================================================== + +class Equiprobable : public Distribution { +public: + explicit Equiprobable(pugi::xml_node node); + Equiprobable(const double* x, int n) : x_{x, x+n} { }; + + //! Sample a value from the distribution + //! \return Sampled value + double sample() const; +private: + std::vector x_; //! Possible outcomes +}; + + +using UPtrDist = std::unique_ptr; + +//! Return univariate probability distribution specified in XML file +//! \param[in] node XML node representing distribution +//! \return Unique pointer to distribution +UPtrDist distribution_from_xml(pugi::xml_node node); + +} // namespace openmc + +#endif // OPENMC_DISTRIBUTION_H diff --git a/src/distribution_angle.cpp b/src/distribution_angle.cpp new file mode 100644 index 0000000000..e5263e4ac2 --- /dev/null +++ b/src/distribution_angle.cpp @@ -0,0 +1,95 @@ +#include "distribution_angle.h" + +#include // for abs, copysign +#include // for vector + +#include "endf.h" +#include "hdf5_interface.h" +#include "random_lcg.h" +#include "search.h" +#include "xtensor/xarray.hpp" +#include "xtensor/xview.hpp" + +namespace openmc { + +//============================================================================== +// AngleDistribution implementation +//============================================================================== + +AngleDistribution::AngleDistribution(hid_t group) +{ + // Get incoming energies + read_dataset(group, "energy", energy_); + int n_energy = energy_.size(); + + // Get outgoing energy distribution data + std::vector offsets; + std::vector interp; + hid_t dset = open_dataset(group, "mu"); + read_attribute(dset, "offsets", offsets); + read_attribute(dset, "interpolation", interp); + xt::xarray temp; + read_dataset(dset, temp); + close_dataset(dset); + + for (int i = 0; i < n_energy; ++i) { + // Determine number of outgoing energies + int j = offsets[i]; + int n; + if (i < n_energy - 1) { + n = offsets[i+1] - j; + } else { + 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)); + std::vector x {xs.begin(), xs.end()}; + std::vector p {ps.begin(), ps.end()}; + std::vector c {cs.begin(), cs.end()}; + + // 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 + Tabular* mudist = new Tabular{x.data(), p.data(), n, int2interp(interp[i]), + c.data()}; + + distribution_.emplace_back(mudist); + } +} + +double AngleDistribution::sample(double E) const +{ + // Determine number of incoming energies + auto n = energy_.size(); + + // Find energy bin and calculate interpolation factor -- if the energy is + // outside the range of the tabulated energies, choose the first or last bins + int i; + double r; + if (E < energy_[0]) { + i = 0; + r = 0.0; + } else if (E > energy_[n - 1]) { + i = n - 2; + r = 1.0; + } else { + i = lower_bound_index(energy_.begin(), energy_.end(), E); + r = (E - energy_[i])/(energy_[i+1] - energy_[i]); + } + + // Sample between the ith and (i+1)th bin + if (r > prn()) ++i; + + // Sample i-th distribution + double mu = distribution_[i]->sample(); + + // Make sure mu is in range [-1,1] and return + if (std::abs(mu) > 1.0) mu = std::copysign(1.0, mu); + return mu; +} + +} // namespace openmc diff --git a/src/distribution_angle.h b/src/distribution_angle.h new file mode 100644 index 0000000000..1797e086a5 --- /dev/null +++ b/src/distribution_angle.h @@ -0,0 +1,39 @@ +//! \file distribution_angle.h +//! Angle distribution dependent on incident particle energy + +#ifndef OPENMC_DISTRIBUTION_ANGLE_H +#define OPENMC_DISTRIBUTION_ANGLE_H + +#include // for vector + +#include "distribution.h" +#include "hdf5.h" + +namespace openmc { + +//============================================================================== +//! Angle distribution that depends on incident particle energy +//============================================================================== + +class AngleDistribution { +public: + AngleDistribution() = default; + explicit AngleDistribution(hid_t group); + + //! Sample an angle given an incident particle energy + //! \param[in] E Particle energy in [eV] + //! \return Cosine of the angle in the range [-1,1] + double sample(double E) const; + + //! Determine whether angle distribution is empty + //! \return Whether distribution is empty + bool empty() const { return energy_.empty(); } + +private: + std::vector energy_; + std::vector distribution_; +}; + +} // namespace openmc + +#endif // OPENMC_DISTRIBUTION_ANGLE_H diff --git a/src/distribution_energy.cpp b/src/distribution_energy.cpp new file mode 100644 index 0000000000..cadeeb3df1 --- /dev/null +++ b/src/distribution_energy.cpp @@ -0,0 +1,337 @@ +#include "distribution_energy.h" + +#include // for max, min, copy, move +#include // for size_t +#include // for back_inserter + +#include "endf.h" +#include "hdf5_interface.h" +#include "math_functions.h" +#include "random_lcg.h" +#include "search.h" +#include "xtensor/xview.hpp" + +namespace openmc { + +//============================================================================== +// DiscretePhoton implementation +//============================================================================== + +DiscretePhoton::DiscretePhoton(hid_t group) +{ + read_attribute(group, "primary_flag", primary_flag_); + read_attribute(group, "energy", energy_); + read_attribute(group, "atomic_weight_ratio", A_); +} + +double DiscretePhoton::sample(double E) const +{ + if (primary_flag_ == 2) { + return energy_ + A_/(A_+ 1)*E; + } else { + return energy_; + } +} + +//============================================================================== +// LevelInelastic implementation +//============================================================================== + +LevelInelastic::LevelInelastic(hid_t group) +{ + read_attribute(group, "threshold", threshold_); + read_attribute(group, "mass_ratio", mass_ratio_); +} + +double LevelInelastic::sample(double E) const +{ + return mass_ratio_*(E - threshold_); +} + +//============================================================================== +// ContinuousTabular implementation +//============================================================================== + +ContinuousTabular::ContinuousTabular(hid_t group) +{ + // Open incoming energy dataset + hid_t dset = open_dataset(group, "energy"); + + // Get interpolation parameters + xt::xarray 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 + + std::copy(temp_b.begin(), temp_b.end(), std::back_inserter(breakpoints_)); + for (const auto i : temp_i) + interpolation_.push_back(int2interp(i)); + n_region_ = breakpoints_.size(); + + // Get incoming energies + read_dataset(dset, energy_); + std::size_t n_energy = energy_.size(); + close_dataset(dset); + + // Get outgoing energy distribution data + dset = open_dataset(group, "distribution"); + std::vector offsets; + std::vector interp; + std::vector n_discrete; + read_attribute(dset, "offsets", offsets); + read_attribute(dset, "interpolation", interp); + read_attribute(dset, "n_discrete_lines", n_discrete); + + xt::xarray eout; + read_dataset(dset, eout); + close_dataset(dset); + + for (int i = 0; i < n_energy; ++i) { + // Determine number of outgoing energies + int j = offsets[i]; + int n; + if (i < n_energy - 1) { + n = offsets[i+1] - j; + } else { + n = eout.shape()[1] - j; + } + + // Assign interpolation scheme and number of discrete lines + CTTable d; + d.interpolation = int2interp(interp[i]); + 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)); + + // 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)); + } else { + // Calculate cumulative distribution function -- discrete portion + for (int k = 0; k < d.n_discrete; ++k) { + if (k == 0) { + d.c[k] = d.p[k]; + } else { + d.c[k] = d.c[k-1] + d.p[k]; + } + } + + // Continuous portion + for (int k = d.n_discrete; k < n; ++k) { + if (k == d.n_discrete) { + d.c[k] = d.c[k-1] + d.p[k]; + } else { + if (d.interpolation == Interpolation::histogram) { + d.c[k] = d.c[k-1] + d.p[k-1]*(d.e_out[k] - d.e_out[k-1]); + } else if (d.interpolation == Interpolation::lin_lin) { + d.c[k] = d.c[k-1] + 0.5*(d.p[k-1] + d.p[k]) * + (d.e_out[k] - d.e_out[k-1]); + } + } + } + + // Normalize density and distribution functions + d.p /= d.c[n - 1]; + d.c /= d.c[n - 1]; + } + + distribution_.push_back(std::move(d)); + } // incoming energies +} + +double ContinuousTabular::sample(double E) const +{ + // Read number of interpolation regions and incoming energies + bool histogram_interp; + if (n_region_ == 1) { + histogram_interp = (interpolation_[0] == Interpolation::histogram); + } else { + histogram_interp = false; + } + + // Find energy bin and calculate interpolation factor -- if the energy is + // outside the range of the tabulated energies, choose the first or last bins + auto n_energy_in = energy_.size(); + int i; + double r; + if (E < energy_[0]) { + i = 0; + r = 0.0; + } else if (E > energy_[n_energy_in - 1]) { + i = n_energy_in - 2; + r = 1.0; + } else { + i = lower_bound_index(energy_.begin(), energy_.end(), E); + r = (E - energy_[i]) / (energy_[i+1] - energy_[i]); + } + + // Sample between the ith and [i+1]th bin + int l; + if (histogram_interp) { + l = i; + } else { + l = r > prn() ? i + 1 : i; + } + + // Interpolation for energy E1 and EK + int n_energy_out = distribution_[i].e_out.size(); + double E_i_1 = distribution_[i].e_out[0]; + double E_i_K = distribution_[i].e_out[n_energy_out - 1]; + + n_energy_out = distribution_[i+1].e_out.size(); + double E_i1_1 = distribution_[i+1].e_out[0]; + double E_i1_K = distribution_[i+1].e_out[n_energy_out - 1]; + + double E_1 = E_i_1 + r*(E_i1_1 - E_i_1); + double E_K = E_i_K + r*(E_i1_K - E_i_K); + + // Determine outgoing energy bin + n_energy_out = distribution_[l].e_out.size(); + double r1 = prn(); + double c_k = distribution_[l].c[0]; + double c_k1; + int k; + for (k = 0; k < n_energy_out - 2; ++k) { + c_k1 = distribution_[l].c[k+1]; + if (r1 < c_k1) break; + c_k = c_k1; + } + + // Check to make sure 1 <= k <= NP - 1 + k = std::max(0, std::min(k, n_energy_out - 2)); + + double E_l_k = distribution_[l].e_out[k]; + double p_l_k = distribution_[l].p[k]; + double E_out; + if (distribution_[l].interpolation == Interpolation::histogram) { + // Histogram interpolation + if (p_l_k > 0.0) { + E_out = E_l_k + (r1 - c_k)/p_l_k; + } else { + E_out = E_l_k; + } + + } else if (distribution_[l].interpolation == Interpolation::lin_lin) { + // Linear-linear interpolation + double E_l_k1 = distribution_[l].e_out[k+1]; + double p_l_k1 = distribution_[l].p[k+1]; + + double frac = (p_l_k1 - p_l_k)/(E_l_k1 - E_l_k); + if (frac == 0.0) { + E_out = E_l_k + (r1 - c_k)/p_l_k; + } else { + E_out = E_l_k + (std::sqrt(std::max(0.0, p_l_k*p_l_k + + 2.0*frac*(r1 - c_k))) - p_l_k)/frac; + } + } + + // Now interpolate between incident energy bins i and i + 1 + if (!histogram_interp && n_energy_out > 1) { + if (l == i) { + return E_1 + (E_out - E_i_1)*(E_K - E_1)/(E_i_K - E_i_1); + } else { + return E_1 + (E_out - E_i1_1)*(E_K - E_1)/(E_i1_K - E_i1_1); + } + } else { + return E_out; + } + +} + +//============================================================================== +// MaxwellEnergy implementation +//============================================================================== + +MaxwellEnergy::MaxwellEnergy(hid_t group) +{ + read_attribute(group, "u", u_); + hid_t dset = open_dataset(group, "theta"); + theta_ = Tabulated1D{dset}; + close_dataset(dset); +} + +double MaxwellEnergy::sample(double E) const +{ + // Get temperature corresponding to incoming energy + double theta = theta_(E); + + while (true) { + // Sample maxwell fission spectrum + double E_out = maxwell_spectrum_c(theta); + + // Accept energy based on restriction energy + if (E_out <= E - u_) return E_out; + } +} + +//============================================================================== +// Evaporation implementation +//============================================================================== + +Evaporation::Evaporation(hid_t group) +{ + read_attribute(group, "u", u_); + hid_t dset = open_dataset(group, "theta"); + theta_ = Tabulated1D{dset}; + close_dataset(dset); +} + +double Evaporation::sample(double E) const +{ + // Get temperature corresponding to incoming energy + double theta = theta_(E); + + double y = (E - u_)/theta; + double v = 1.0 - std::exp(-y); + + // Sample outgoing energy based on evaporation spectrum probability + // density function + double x; + while (true) { + x = -std::log((1.0 - v*prn())*(1.0 - v*prn())); + if (x <= y) break; + } + + return x*theta; +} + +//============================================================================== +// WattEnergy implementation +//============================================================================== + +WattEnergy::WattEnergy(hid_t group) +{ + // Read restriction energy + read_attribute(group, "u", u_); + + // Read tabulated functions + hid_t dset = open_dataset(group, "a"); + a_ = Tabulated1D{dset}; + close_dataset(dset); + dset = open_dataset(group, "b"); + b_ = Tabulated1D{dset}; + close_dataset(dset); +} + +double WattEnergy::sample(double E) const +{ + // Determine Watt parameters at incident energy + double a = a_(E); + double b = b_(E); + + while (true) { + // Sample energy-dependent Watt fission spectrum + double E_out = watt_spectrum_c(a, b); + + // Accept energy based on restriction energy + if (E_out <= E - u_) return E_out; + } +} + +} diff --git a/src/distribution_energy.h b/src/distribution_energy.h new file mode 100644 index 0000000000..8dc4baffe4 --- /dev/null +++ b/src/distribution_energy.h @@ -0,0 +1,152 @@ +//! \file distribution_energy.h +//! Energy distributions that depend on incident particle energy + +#ifndef OPENMC_DISTRIBUTION_ENERGY_H +#define OPENMC_DISTRIBUTION_ENERGY_H + +#include + +#include "xtensor/xtensor.hpp" +#include "hdf5.h" + +#include "constants.h" +#include "endf.h" + +namespace openmc { + +//=============================================================================== +//! Abstract class defining an energy distribution that is a function of the +//! incident energy of a projectile. Each derived type must implement a sample() +//! function that returns a sampled outgoing energy given an incoming energy +//=============================================================================== + +class EnergyDistribution { +public: + virtual double sample(double E) const = 0; + virtual ~EnergyDistribution() = default; +}; + +//=============================================================================== +//! Discrete photon energy distribution +//=============================================================================== + +class DiscretePhoton : public EnergyDistribution { +public: + explicit DiscretePhoton(hid_t group); + + //! Sample energy distribution + //! \param[in] E Incident particle energy in [eV] + //! \return Sampled energy in [eV] + double sample(double E) const; +private: + int primary_flag_; //!< Indicator of whether the photon is a primary or + //!< non-primary photon. + double energy_; //!< Photon energy or binding energy + double A_; //!< Atomic weight ratio of the target nuclide +}; + +//=============================================================================== +//! Level inelastic scattering distribution +//=============================================================================== + +class LevelInelastic : public EnergyDistribution { +public: + explicit LevelInelastic(hid_t group); + + //! Sample energy distribution + //! \param[in] E Incident particle energy in [eV] + //! \return Sampled energy in [eV] + double sample(double E) const; +private: + double threshold_; //!< Energy threshold in lab, (A + 1)/A * |Q| + double mass_ratio_; //!< (A/(A+1))^2 +}; + +//=============================================================================== +//! An energy distribution represented as a tabular distribution with histogram +//! or linear-linear interpolation. This corresponds to ACE law 4, which NJOY +//! produces for a number of ENDF energy distributions. +//=============================================================================== + +class ContinuousTabular : public EnergyDistribution { +public: + explicit ContinuousTabular(hid_t group); + + //! Sample energy distribution + //! \param[in] E Incident particle energy in [eV] + //! \return Sampled energy in [eV] + double sample(double E) const; +private: + //! Outgoing energy for a single incoming energy + struct CTTable { + Interpolation interpolation; //!< Interpolation law + int n_discrete; //!< Number of of discrete energies + xt::xtensor e_out; //!< Outgoing energies in [eV] + xt::xtensor p; //!< Probability density + xt::xtensor c; //!< Cumulative distribution + }; + + int n_region_; //!< Number of inteprolation regions + std::vector breakpoints_; //!< Breakpoints between regions + std::vector interpolation_; //!< Interpolation laws + std::vector energy_; //!< Incident energy in [eV] + std::vector distribution_; //!< Distributions for each incident energy +}; + +//=============================================================================== +//! Evaporation spectrum corresponding to ACE law 9 and ENDF File 5, LF=9. +//=============================================================================== + +class Evaporation : public EnergyDistribution { +public: + explicit Evaporation(hid_t group); + + //! Sample energy distribution + //! \param[in] E Incident particle energy in [eV] + //! \return Sampled energy in [eV] + double sample(double E) const; +private: + Tabulated1D theta_; //!< Incoming energy dependent parameter + double u_; //!< Restriction energy +}; + +//=============================================================================== +//! Energy distribution of neutrons emitted from a Maxwell fission spectrum. +//! This corresponds to ACE law 7 and ENDF File 5, LF=7. +//=============================================================================== + +class MaxwellEnergy : public EnergyDistribution { +public: + explicit MaxwellEnergy(hid_t group); + + //! Sample energy distribution + //! \param[in] E Incident particle energy in [eV] + //! \return Sampled energy in [eV] + double sample(double E) const; +private: + Tabulated1D theta_; //!< Incoming energy dependent parameter + double u_; //!< Restriction energy +}; + +//=============================================================================== +//! Energy distribution of neutrons emitted from a Watt fission spectrum. This +//! corresponds to ACE law 11 and ENDF File 5, LF=11. +//=============================================================================== + +class WattEnergy : public EnergyDistribution { +public: + explicit WattEnergy(hid_t group); + + //! Sample energy distribution + //! \param[in] E Incident particle energy in [eV] + //! \return Sampled energy in [eV] + double sample(double E) const; +private: + Tabulated1D a_; //!< Energy-dependent 'a' parameter + Tabulated1D b_; //!< Energy-dependent 'b' parameter + double u_; //!< Restriction energy +}; + +} // namespace openmc + +#endif // OPENMC_DISTRIBUTION_ENERGY_H diff --git a/src/distribution_multi.cpp b/src/distribution_multi.cpp new file mode 100644 index 0000000000..36184f7527 --- /dev/null +++ b/src/distribution_multi.cpp @@ -0,0 +1,51 @@ +#include "distribution_multi.h" + +#include // for move +#include // for sqrt, sin, cos, max + +#include "constants.h" +#include "math_functions.h" +#include "random_lcg.h" + +namespace openmc { + +//============================================================================== +// PolarAzimuthal implementation +//============================================================================== + +PolarAzimuthal::PolarAzimuthal(Direction u, UPtrDist mu, UPtrDist phi) : + UnitSphereDistribution{u}, mu_{std::move(mu)}, phi_{std::move(phi)} { } + +Direction PolarAzimuthal::sample() const +{ + // Sample cosine of polar angle + double mu = mu_->sample(); + if (mu == 1.0) return u_ref; + + // Sample azimuthal angle + double phi = phi_->sample(); + return rotate_angle(u_ref, mu, &phi); +} + +//============================================================================== +// Isotropic implementation +//============================================================================== + +Direction Isotropic::sample() const +{ + double phi = 2.0*PI*prn(); + double mu = 2.0*prn() - 1.0; + return {mu, std::sqrt(1.0 - mu*mu) * std::cos(phi), + std::sqrt(1.0 - mu*mu) * std::sin(phi)}; +} + +//============================================================================== +// Monodirectional implementation +//============================================================================== + +Direction Monodirectional::sample() const +{ + return u_ref; +} + +} // namespace openmc diff --git a/src/distribution_multi.h b/src/distribution_multi.h new file mode 100644 index 0000000000..2d4dab42c1 --- /dev/null +++ b/src/distribution_multi.h @@ -0,0 +1,73 @@ +#ifndef DISTRIBUTION_MULTI_H +#define DISTRIBUTION_MULTI_H + +#include + +#include "distribution.h" +#include "position.h" + +namespace openmc { + +//============================================================================== +//! Probability density function for points on the unit sphere. Extensions of +//! this type are used to sample angular distributions for starting sources +//============================================================================== + +class UnitSphereDistribution { +public: + UnitSphereDistribution() { }; + explicit UnitSphereDistribution(Direction u) : u_ref{u} { }; + virtual ~UnitSphereDistribution() = default; + + //! Sample a direction from the distribution + //! \return Direction sampled + virtual Direction sample() const = 0; + + Direction u_ref {0.0, 0.0, 1.0}; //!< reference direction +}; + +//============================================================================== +//! Explicit distribution of polar and azimuthal angles +//============================================================================== + +class PolarAzimuthal : public UnitSphereDistribution { +public: + PolarAzimuthal(Direction u, UPtrDist mu, UPtrDist phi); + + //! Sample a direction from the distribution + //! \return Direction sampled + Direction sample() const; +private: + UPtrDist mu_; //!< Distribution of polar angle + UPtrDist phi_; //!< Distribution of azimuthal angle +}; + +//============================================================================== +//! Uniform distribution on the unit sphere +//============================================================================== + +class Isotropic : public UnitSphereDistribution { +public: + Isotropic() { }; + + //! Sample a direction from the distribution + //! \return Sampled direction + Direction sample() const; +}; + +//============================================================================== +//! Monodirectional distribution +//============================================================================== + +class Monodirectional : public UnitSphereDistribution { +public: + Monodirectional(Direction u) : UnitSphereDistribution{u} { }; + + //! Sample a direction from the distribution + //! \return Sampled direction + Direction sample() const; +}; + +} // namespace openmc + +#endif // DISTRIBUTION_MULTI_H diff --git a/src/distribution_spatial.cpp b/src/distribution_spatial.cpp new file mode 100644 index 0000000000..d870a5f93a --- /dev/null +++ b/src/distribution_spatial.cpp @@ -0,0 +1,97 @@ +#include "distribution_spatial.h" + +#include "error.h" +#include "random_lcg.h" +#include "xml_interface.h" + +namespace openmc { + +//============================================================================== +// CartesianIndependent implementation +//============================================================================== + +CartesianIndependent::CartesianIndependent(pugi::xml_node node) +{ + // Read distribution for x coordinate + if (check_for_node(node, "x")) { + pugi::xml_node node_dist = node.child("x"); + x_ = distribution_from_xml(node_dist); + } else { + // If no distribution was specified, default to a single point at x=0 + double x[] {0.0}; + double p[] {1.0}; + x_ = UPtrDist{new Discrete{x, p, 1}}; + } + + // Read distribution for y coordinate + if (check_for_node(node, "y")) { + pugi::xml_node node_dist = node.child("y"); + y_ = distribution_from_xml(node_dist); + } else { + // If no distribution was specified, default to a single point at y=0 + double x[] {0.0}; + double p[] {1.0}; + y_ = UPtrDist{new Discrete{x, p, 1}}; + } + + // Read distribution for z coordinate + if (check_for_node(node, "z")) { + pugi::xml_node node_dist = node.child("z"); + z_ = distribution_from_xml(node_dist); + } else { + // If no distribution was specified, default to a single point at z=0 + double x[] {0.0}; + double p[] {1.0}; + z_ = UPtrDist{new Discrete{x, p, 1}}; + } +} + +Position CartesianIndependent::sample() const +{ + return {x_->sample(), y_->sample(), z_->sample()}; +} + +//============================================================================== +// SpatialBox implementation +//============================================================================== + +SpatialBox::SpatialBox(pugi::xml_node node) +{ + // Read lower-right/upper-left coordinates + auto params = get_node_array(node, "parameters"); + if (params.size() != 6) + openmc::fatal_error("Box/fission spatial source must have six " + "parameters specified."); + + lower_left_ = Position{params[0], params[1], params[2]}; + upper_right_ = Position{params[3], params[4], params[5]}; +} + +Position SpatialBox::sample() const +{ + Position xi {prn(), prn(), prn()}; + return lower_left_ + xi*(upper_right_ - lower_left_); +} + +//============================================================================== +// SpatialPoint implementation +//============================================================================== + +SpatialPoint::SpatialPoint(pugi::xml_node node) +{ + // Read location of point source + auto params = get_node_array(node, "parameters"); + if (params.size() != 3) + openmc::fatal_error("Point spatial source must have three " + "parameters specified."); + + // Set position + r_ = Position{params.data()}; +} + +Position SpatialPoint::sample() const +{ + return r_; +} + +} // namespace openmc diff --git a/src/distribution_spatial.h b/src/distribution_spatial.h new file mode 100644 index 0000000000..7474b497ca --- /dev/null +++ b/src/distribution_spatial.h @@ -0,0 +1,74 @@ +#ifndef OPENMC_DISTRIBTUION_SPATIAL_H +#define OPENMC_DISTRIBUTION_SPATIAL_H + +#include "pugixml.hpp" + +#include "distribution.h" +#include "position.h" + +namespace openmc { + +//============================================================================== +//! Probability density function for points in Euclidean space +//============================================================================== + +class SpatialDistribution { +public: + virtual ~SpatialDistribution() = default; + + //! Sample a position from the distribution + virtual Position sample() const = 0; +}; + +//============================================================================== +//! Distribution of points specified by independent distributions in x,y,z +//============================================================================== + +class CartesianIndependent : public SpatialDistribution { +public: + explicit CartesianIndependent(pugi::xml_node node); + + //! Sample a position from the distribution + //! \return Sampled position + Position sample() const; +private: + UPtrDist x_; //!< Distribution of x coordinates + UPtrDist y_; //!< Distribution of y coordinates + UPtrDist z_; //!< Distribution of z coordinates +}; + +//============================================================================== +//! Uniform distribution of points over a box +//============================================================================== + +class SpatialBox : public SpatialDistribution { +public: + explicit SpatialBox(pugi::xml_node node); + + //! Sample a position from the distribution + //! \return Sampled position + Position sample() const; +private: + Position lower_left_; //!< Lower-left coordinates of box + Position upper_right_; //!< Upper-right coordinates of box + bool only_fissionable {false}; //!< Only accept sites in fissionable region? +}; + +//============================================================================== +//! Distribution at a single point +//============================================================================== + +class SpatialPoint : public SpatialDistribution { +public: + explicit SpatialPoint(pugi::xml_node node); + + //! Sample a position from the distribution + //! \return Sampled position + Position sample() const; +private: + Position r_; //!< Single position at which sites are generated +}; + +} // namespace openmc + +#endif // OPENMC_DISTRIBUTION_SPATIAL_H diff --git a/src/endf.cpp b/src/endf.cpp new file mode 100644 index 0000000000..859183d12f --- /dev/null +++ b/src/endf.cpp @@ -0,0 +1,147 @@ +#include "endf.h" + +#include // for copy +#include // for log, exp +#include // for back_inserter + +#include "constants.h" +#include "hdf5_interface.h" +#include "search.h" +#include "xtensor/xarray.hpp" +#include "xtensor/xview.hpp" + +namespace openmc { + +//============================================================================== +// Functions +//============================================================================== + +Interpolation int2interp(int i) +{ + switch (i) { + case 1: + return Interpolation::histogram; + case 2: + return Interpolation::lin_lin; + case 3: + return Interpolation::lin_log; + case 4: + return Interpolation::log_lin; + case 5: + return Interpolation::log_log; + } +} + +bool is_fission(int mt) +{ + return mt == 18 || mt == 19 || mt == 20 || mt == 21 || mt == 38; +} + +//============================================================================== +// Polynomial implementation +//============================================================================== + +Polynomial::Polynomial(hid_t dset) +{ + // Read coefficients into a vector + read_dataset(dset, coef_); +} + +double Polynomial::operator()(double x) const +{ + // Use Horner's rule to evaluate polynomial. Note that coefficients are + // ordered in increasing powers of x. + double y = 0.0; + for (auto c = coef_.crbegin(); c != coef_.crend(); ++c) { + y = y*x + *c; + } + return y; +} + +//============================================================================== +// Tabulated1D implementation +//============================================================================== + +Tabulated1D::Tabulated1D(hid_t dset) +{ + read_attribute(dset, "breakpoints", nbt_); + n_regions_ = nbt_.size(); + + // Change 1-indexing to 0-indexing + for (auto& b : nbt_) --b; + + std::vector int_temp; + read_attribute(dset, "interpolation", int_temp); + + // Convert vector of ints into Interpolation + for (const auto i : int_temp) + int_.push_back(int2interp(i)); + + xt::xarray arr; + read_dataset(dset, arr); + + auto xs = xt::view(arr, 0); + auto ys = xt::view(arr, 1); + + std::copy(xs.begin(), xs.end(), std::back_inserter(x_)); + std::copy(ys.begin(), ys.end(), std::back_inserter(y_)); + n_pairs_ = x_.size(); +} + +double Tabulated1D::operator()(double x) const +{ + // find which bin the abscissa is in -- if the abscissa is outside the + // tabulated range, the first or last point is chosen, i.e. no interpolation + // is done outside the energy range + int i; + if (x < x_[0]) { + return y_[0]; + } else if (x > x_[n_pairs_ - 1]) { + return y_[n_pairs_ - 1]; + } else { + i = lower_bound_index(x_.begin(), x_.end(), x); + } + + // determine interpolation scheme + Interpolation interp; + if (n_regions_ == 0) { + interp = Interpolation::lin_lin; + } else if (n_regions_ == 1) { + interp = int_[0]; + } else if (n_regions_ > 1) { + for (int j = 0; j < n_regions_; ++j) { + if (i < nbt_[j]) { + interp = int_[j]; + break; + } + } + } + + // handle special case of histogram interpolation + if (interp == Interpolation::histogram) return y_[i]; + + // determine bounding values + double x0 = x_[i]; + double x1 = x_[i + 1]; + double y0 = y_[i]; + double y1 = y_[i + 1]; + + // determine interpolation factor and interpolated value + double r; + switch (interp) { + case Interpolation::lin_lin: + r = (x - x0)/(x1 - x0); + return y0 + r*(y1 - y0); + case Interpolation::lin_log: + r = log(x/x0)/log(x1/x0); + return y0 + r*(y1 - y0); + case Interpolation::log_lin: + r = (x - x0)/(x1 - x0); + return y0*exp(r*log(y1/y0)); + case Interpolation::log_log: + r = log(x/x0)/log(x1/x0); + return y0*exp(r*log(y1/y0)); + } +} + +} // namespace openmc diff --git a/src/endf.h b/src/endf.h new file mode 100644 index 0000000000..d2d67685dc --- /dev/null +++ b/src/endf.h @@ -0,0 +1,78 @@ +//! \file endf.h +//! Classes and functions related to the ENDF-6 format + +#ifndef OPENMC_ENDF_H +#define OPENMC_ENDF_H + +#include + +#include "constants.h" +#include "hdf5.h" + +namespace openmc { + +//! Convert integer representing interpolation law to enum +//! \param[in] i Intereger (e.g. 1=histogram, 2=lin-lin) +//! \return Corresponding enum value +Interpolation int2interp(int i); + +//! Determine whether MT number corresponds to a fission reaction +//! \param[in] MT ENDF MT value +//! \return Whether corresponding reaction is a fission reaction +bool is_fission(int MT); + +//============================================================================== +//! Abstract one-dimensional function +//============================================================================== + +class Function1D { +public: + virtual double operator()(double x) const = 0; +}; + +//============================================================================== +//! One-dimensional function expressed as a polynomial +//============================================================================== + +class Polynomial : public Function1D { +public: + //! Construct polynomial from HDF5 data + //! \param[in] dset Dataset containing coefficients + explicit Polynomial(hid_t dset); + + //! Evaluate the polynomials + //! \param[in] x independent variable + //! \return Polynomial evaluated at x + double operator()(double x) const; +private: + std::vector coef_; //!< Polynomial coefficients +}; + +//============================================================================== +//! One-dimensional interpolable function +//============================================================================== + +class Tabulated1D : public Function1D { +public: + Tabulated1D() = default; + + //! Construct function from HDF5 data + //! \param[in] dset Dataset containing tabulated data + explicit Tabulated1D(hid_t dset); + + //! Evaluate the tabulated function + //! \param[in] x independent variable + //! \return Function evaluated at x + double operator()(double x) const; +private: + std::size_t n_regions_ {0}; //!< number of interpolation regions + std::vector nbt_; //!< values separating interpolation regions + std::vector int_; //!< interpolation schemes + std::size_t n_pairs_; //!< number of (x,y) pairs + std::vector x_; //!< values of abscissa + std::vector y_; //!< values of ordinate +}; + +} // namespace openmc + +#endif // OPENMC_ENDF_H diff --git a/src/energy_distribution.F90 b/src/energy_distribution.F90 deleted file mode 100644 index 2be945d80e..0000000000 --- a/src/energy_distribution.F90 +++ /dev/null @@ -1,583 +0,0 @@ -module energy_distribution - - use algorithm, only: binary_search - use constants, only: ZERO, ONE, HALF, TWO, PI, HISTOGRAM, LINEAR_LINEAR - use endf_header, only: Tabulated1D - use hdf5_interface - use math, only: maxwell_spectrum, watt_spectrum - use random_lcg, only: prn - -!=============================================================================== -! ENERGYDISTRIBUTION (abstract) defines an energy distribution that is a -! function of the incident energy of a projectile. Each derived type must -! implement a sample() function that returns a sampled outgoing energy given an -! incoming energy -!=============================================================================== - - type, abstract :: EnergyDistribution - contains - procedure(energy_distribution_sample_), deferred :: sample - procedure(energy_distribution_from_hdf5_), deferred :: from_hdf5 - end type EnergyDistribution - - abstract interface - function energy_distribution_sample_(this, E_in) result(E_out) - import EnergyDistribution - class(EnergyDistribution), intent(in) :: this - real(8), intent(in) :: E_in - real(8) :: E_out - end function energy_distribution_sample_ - - subroutine energy_distribution_from_hdf5_(this, group_id) - import EnergyDistribution - import HID_T - class(EnergyDistribution), intent(inout) :: this - integer(HID_T), intent(in) :: group_id - end subroutine energy_distribution_from_hdf5_ - end interface - - type :: EnergyDistributionContainer - class(EnergyDistribution), allocatable :: obj - end type EnergyDistributionContainer - -!=============================================================================== -! Derived classes -!=============================================================================== - -!=============================================================================== -! TABULAREQUIPROBABLE represents an energy distribution with tabular -! equiprobable energy bins as given in ACE law 1. This is an older -! representation that has largely been replaced with ACE laws 4, 44, and 61. -!=============================================================================== - - type, extends(EnergyDistribution) :: TabularEquiprobable - integer :: n_region ! number of interpolation regions - integer, allocatable :: breakpoints(:) ! breakpoints of interpolation regions - integer, allocatable :: interpolation(:) ! interpolation region codes - real(8), allocatable :: energy_in(:) ! incoming energies - real(8), allocatable :: energy_out(:,:) ! table of outgoing energies for - ! each incoming energy - contains - procedure :: sample => equiprobable_sample - procedure :: from_hdf5 => equiprobable_from_hdf5 - end type TabularEquiprobable - -!=============================================================================== -! DISCRETEPHOTON gives the energy distribution for a discrete photon (usually -! used for photon production from an incident-neutron reaction) -!=============================================================================== - - type, extends(EnergyDistribution) :: DiscretePhoton - integer :: primary_flag - real(8) :: energy - real(8) :: A - contains - procedure :: sample => discrete_photon_sample - procedure :: from_hdf5 => discrete_photon_from_hdf5 - end type DiscretePhoton - -!=============================================================================== -! LEVELINELASTIC gives the energy distribution for level inelastic scattering by -! neutrons as in ENDF MT=51--90. -!=============================================================================== - - type, extends(EnergyDistribution) :: LevelInelastic - real(8) :: threshold - real(8) :: mass_ratio - contains - procedure :: sample => level_inelastic_sample - procedure :: from_hdf5 => level_inelastic_from_hdf5 - end type LevelInelastic - -!=============================================================================== -! CONTINUOUSTABULAR gives an energy distribution represented as a tabular -! distribution with histogram or linear-linear interpolation. This corresponds -! to ACE law 4, which NJOY produces for a number of ENDF energy distributions. -!=============================================================================== - - type CTTable - integer :: interpolation - integer :: n_discrete - real(8), allocatable :: e_out(:) - real(8), allocatable :: p(:) - real(8), allocatable :: c(:) - end type CTTable - - type, extends(EnergyDistribution) :: ContinuousTabular - integer :: n_region - integer, allocatable :: breakpoints(:) - integer, allocatable :: interpolation(:) - real(8), allocatable :: energy(:) - type(CTTable), allocatable :: distribution(:) - contains - procedure :: sample => continuous_sample - procedure :: from_hdf5 => continuous_from_hdf5 - end type ContinuousTabular - -!=============================================================================== -! MAXWELLENERGY gives the energy distribution of neutrons emitted from a Maxwell -! fission spectrum. This corresponds to ACE law 7 and ENDF File 5, LF=7. -!=============================================================================== - - type, extends(EnergyDistribution) :: MaxwellEnergy - type(Tabulated1D) :: theta ! incoming-energy-dependent parameter - real(8) :: u ! restriction energy - contains - procedure :: sample => maxwellenergy_sample - procedure :: from_hdf5 => maxwellenergy_from_hdf5 - end type MaxwellEnergy - -!=============================================================================== -! EVAPORATION represents an evaporation spectrum corresponding to ACE law 9 and -! ENDF File 5, LF=9. -!=============================================================================== - - type, extends(EnergyDistribution) :: Evaporation - type(Tabulated1D) :: theta - real(8) :: u - contains - procedure :: sample => evaporation_sample - procedure :: from_hdf5 => evaporation_from_hdf5 - end type Evaporation - -!=============================================================================== -! WATTENERGY gives the energy distribution of neutrons emitted from a Watt -! fission spectrum. This corresponds to ACE law 11 and ENDF File 5, LF=11. -!=============================================================================== - - type, extends(EnergyDistribution) :: WattEnergy - type(Tabulated1D) :: a - type(Tabulated1D) :: b - real(8) :: u - contains - procedure :: sample => watt_sample - procedure :: from_hdf5 => watt_from_hdf5 - end type WattEnergy - -contains - - function equiprobable_sample(this, E_in) result(E_out) - class(TabularEquiprobable), intent(in) :: this - real(8), intent(in) :: E_in ! incoming energy - real(8) :: E_out ! sampled outgoing energy - - integer :: i, k, l ! indices - integer :: n_energy_in ! number of incoming energies - integer :: n_energy_out ! number of outgoing energies - real(8) :: r ! interpolation factor on incoming energy - real(8) :: E_i_1, E_i_K ! endpoints on outgoing grid i - real(8) :: E_i1_1, E_i1_K ! endpoints on outgoing grid i+1 - real(8) :: E_1, E_K ! endpoints interpolated between i and i+1 - real(8) :: E_l_k, E_l_k1 ! adjacent E on outgoing grid l - - ! Determine number of incoming/outgoing energies - n_energy_in = size(this%energy_in) - n_energy_out = size(this%energy_out, 1) - - ! Determine index on incoming energy grid and interpolation factor - i = binary_search(this%energy_in, size(this%energy_in), E_in) - r = (E_in - this%energy_in(i)) / & - (this%energy_in(i+1) - this%energy_in(i)) - - ! Sample outgoing energy bin - k = 1 + int(n_energy_out * prn()) - - ! Determine E_1 and E_K - E_i_1 = this%energy_out(1, i) - E_i_K = this%energy_out(n_energy_out, i) - - E_i1_1 = this%energy_out(1, i+1) - E_i1_K = this%energy_out(n_energy_out, i+1) - - E_1 = E_i_1 + r*(E_i1_1 - E_i_1) - E_K = E_i_K + r*(E_i1_K - E_i_K) - - ! Randomly select between the outgoing table for incoming energy E_i and - ! E_(i+1) - if (prn() < r) then - l = i + 1 - else - l = i - end if - - ! Determine E_l_k and E_l_k+1 - E_l_k = this%energy_out(k, l) - E_l_k1 = this%energy_out(k+1, l) - - ! Determine E' (denoted here as E_out) - E_out = E_l_k + prn()*(E_l_k1 - E_l_k) - - ! Now interpolate between incident energy bins i and i + 1 - if (l == i) then - E_out = E_1 + (E_out - E_i_1)*(E_K - E_1)/(E_i_K - E_i_1) - else - E_out = E_1 + (E_out - E_i1_1)*(E_K - E_1)/(E_i1_K - E_i1_1) - end if - end function equiprobable_sample - - subroutine equiprobable_from_hdf5(this, group_id) - class(TabularEquiprobable), intent(inout) :: this - integer(HID_T), intent(in) :: group_id - end subroutine equiprobable_from_hdf5 - - function discrete_photon_sample(this, E_in) result(E_out) - class(DiscretePhoton), intent(in) :: this - real(8), intent(in) :: E_in - real(8) :: E_out - - if (this % primary_flag == 2) then - E_out = this % energy + this % A/(this % A + 1)*E_in - else - E_out = this % energy - end if - end function discrete_photon_sample - - subroutine discrete_photon_from_hdf5(this, group_id) - class(DiscretePhoton), intent(inout) :: this - integer(HID_T), intent(in) :: group_id - - call read_attribute(this % primary_flag, group_id, 'primary_flag') - call read_attribute(this % energy, group_id, 'energy') - call read_attribute(this % A, group_id, 'atomic_weight_ratio') - end subroutine discrete_photon_from_hdf5 - - function level_inelastic_sample(this, E_in) result(E_out) - class(LevelInelastic), intent(in) :: this - real(8), intent(in) :: E_in - real(8) :: E_out - - E_out = this%mass_ratio*(E_in - this%threshold) - end function level_inelastic_sample - - subroutine level_inelastic_from_hdf5(this, group_id) - class(LevelInelastic), intent(inout) :: this - integer(HID_T), intent(in) :: group_id - - call read_attribute(this%threshold, group_id, 'threshold') - call read_attribute(this%mass_ratio, group_id, 'mass_ratio') - end subroutine level_inelastic_from_hdf5 - - function continuous_sample(this, E_in) result(E_out) - class(ContinuousTabular), intent(in) :: this - real(8), intent(in) :: E_in ! incoming energy - real(8) :: E_out ! sampled outgoing energy - - integer :: i, k, l ! indices - integer :: n_energy_in ! number of incoming energies - integer :: n_energy_out ! number of outgoing energies - real(8) :: r ! interpolation factor on incoming energy - real(8) :: r1 ! random number on [0,1) - real(8) :: frac ! interpolation factor on outgoing energy - real(8) :: E_i_1, E_i_K ! endpoints on outgoing grid i - real(8) :: E_i1_1, E_i1_K ! endpoints on outgoing grid i+1 - real(8) :: E_1, E_K ! endpoints interpolated between i and i+1 - real(8) :: E_l_k, E_l_k1 ! adjacent E on outgoing grid l - real(8) :: p_l_k, p_l_k1 ! adjacent p on outgoing grid l - real(8) :: c_k, c_k1 ! cumulative probability - logical :: histogram_interp ! whether histogram interpolation is used - - ! Read number of interpolation regions and incoming energies - if (this%n_region == 1) then - histogram_interp = (this%interpolation(1) == 1) - else - histogram_interp = .false. - end if - - ! Find energy bin and calculate interpolation factor -- if the energy is - ! outside the range of the tabulated energies, choose the first or last bins - n_energy_in = size(this%energy) - if (E_in < this%energy(1)) then - i = 1 - r = ZERO - elseif (E_in > this%energy(n_energy_in)) then - i = n_energy_in - 1 - r = ONE - else - i = binary_search(this%energy, n_energy_in, E_in) - r = (E_in - this%energy(i)) / & - (this%energy(i+1) - this%energy(i)) - end if - - ! Sample between the ith and (i+1)th bin - if (histogram_interp) then - l = i - else - if (r > prn()) then - l = i + 1 - else - l = i - end if - end if - - ! Interpolation for energy E1 and EK - n_energy_out = size(this%distribution(i)%e_out) - E_i_1 = this%distribution(i)%e_out(1) - E_i_K = this%distribution(i)%e_out(n_energy_out) - - n_energy_out = size(this%distribution(i+1)%e_out) - E_i1_1 = this%distribution(i+1)%e_out(1) - E_i1_K = this%distribution(i+1)%e_out(n_energy_out) - - E_1 = E_i_1 + r*(E_i1_1 - E_i_1) - E_K = E_i_K + r*(E_i1_K - E_i_K) - - ! Determine outgoing energy bin - n_energy_out = size(this%distribution(l)%e_out) - r1 = prn() - c_k = this%distribution(l)%c(1) - do k = 1, n_energy_out - 1 - c_k1 = this%distribution(l)%c(k+1) - if (r1 < c_k1) exit - c_k = c_k1 - end do - - ! Check to make sure 1 <= k <= NP - 1 - k = max(1, min(k, n_energy_out - 1)) - - E_l_k = this%distribution(l)%e_out(k) - p_l_k = this%distribution(l)%p(k) - if (this%distribution(l)%interpolation == HISTOGRAM) then - ! Histogram interpolation - if (p_l_k > ZERO) then - E_out = E_l_k + (r1 - c_k)/p_l_k - else - E_out = E_l_k - end if - - elseif (this%distribution(l)%interpolation == LINEAR_LINEAR) then - ! Linear-linear interpolation - E_l_k1 = this%distribution(l)%e_out(k+1) - p_l_k1 = this%distribution(l)%p(k+1) - - frac = (p_l_k1 - p_l_k)/(E_l_k1 - E_l_k) - if (frac == ZERO) then - E_out = E_l_k + (r1 - c_k)/p_l_k - else - E_out = E_l_k + (sqrt(max(ZERO, p_l_k*p_l_k + & - TWO*frac*(r1 - c_k))) - p_l_k)/frac - end if - end if - - ! Now interpolate between incident energy bins i and i + 1 - if (.not. histogram_interp .and. n_energy_out > 1) then - if (l == i) then - E_out = E_1 + (E_out - E_i_1)*(E_K - E_1)/(E_i_K - E_i_1) - else - E_out = E_1 + (E_out - E_i1_1)*(E_K - E_1)/(E_i1_K - E_i1_1) - end if - end if - end function continuous_sample - - subroutine continuous_from_hdf5(this, group_id) - class(ContinuousTabular), intent(inout) :: this - integer(HID_T), intent(in) :: group_id - - integer :: i, j, k - integer :: n - integer :: n_energy - integer(HID_T) :: dset_id - integer(HSIZE_T) :: dims(1), dims2(2) - integer, allocatable :: temp(:,:) - integer, allocatable :: offsets(:) - integer, allocatable :: interp(:) - integer, allocatable :: n_discrete(:) - real(8), allocatable :: eout(:,:) - - ! Open incoming energy dataset - dset_id = open_dataset(group_id, 'energy') - - ! Get interpolation parameters - call read_attribute(temp, dset_id, 'interpolation') - allocate(this%breakpoints(size(temp, 1))) - allocate(this%interpolation(size(temp, 1))) - this%breakpoints(:) = temp(:, 1) - this%interpolation(:) = temp(:, 2) - this%n_region = size(this%breakpoints) - - ! Get incoming energies - call get_shape(dset_id, dims) - n_energy = int(dims(1), 4) - allocate(this%energy(n_energy)) - allocate(this%distribution(n_energy)) - call read_dataset(this%energy, dset_id) - call close_dataset(dset_id) - - ! Get outgoing energy distribution data - dset_id = open_dataset(group_id, 'distribution') - call read_attribute(offsets, dset_id, 'offsets') - call read_attribute(interp, dset_id, 'interpolation') - call read_attribute(n_discrete, dset_id, 'n_discrete_lines') - call get_shape(dset_id, dims2) - allocate(eout(dims2(1), dims2(2))) - call read_dataset(eout, dset_id) - call close_dataset(dset_id) - - do i = 1, n_energy - ! Determine number of outgoing energies - j = offsets(i) - if (i < n_energy) then - n = offsets(i+1) - j - else - n = size(eout, 1) - j - end if - - associate (d => this % distribution(i)) - ! Assign interpolation scheme and number of discrete lines - d % interpolation = interp(i) - d % n_discrete = n_discrete(i) - - ! Allocate arrays for energies and PDF/CDF - allocate(d % e_out(n)) - allocate(d % p(n)) - allocate(d % c(n)) - - ! Copy data - d % e_out(:) = eout(j+1:j+n, 1) - d % p(:) = eout(j+1:j+n, 2) - - ! 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.) then - d % c(:) = eout(j+1:j+n, 3) - else - ! Calculate cumulative distribution function -- discrete portion - do k = 1, n_discrete(i) - if (k == 1) then - d % c(k) = d % p(k) - else - d % c(k) = d % c(k-1) + d % p(k) - end if - end do - - ! Continuous portion - do k = d % n_discrete + 1, n - if (k == d % n_discrete + 1) then - d % c(k) = sum(d % p(1:d % n_discrete)) - else - if (d % interpolation == HISTOGRAM) then - d % c(k) = d % c(k-1) + d % p(k-1) * & - (d % e_out(k) - d % e_out(k-1)) - elseif (d % interpolation == LINEAR_LINEAR) then - d % c(k) = d % c(k-1) + HALF*(d % p(k-1) + d % p(k)) * & - (d % e_out(k) - d % e_out(k-1)) - end if - end if - end do - - ! Normalize density and distribution functions - d % p(:) = d % p(:)/d % c(n) - d % c(:) = d % c(:)/d % c(n) - end if - end associate - end do - end subroutine continuous_from_hdf5 - - function maxwellenergy_sample(this, E_in) result(E_out) - class(MaxwellEnergy), intent(in) :: this - real(8), intent(in) :: E_in ! incoming energy - real(8) :: E_out ! sampled outgoing energy - - real(8) :: theta ! Maxwell distribution parameter - - ! Get temperature corresponding to incoming energy - theta = this % theta % evaluate(E_in) - - do - ! Sample maxwell fission spectrum - E_out = maxwell_spectrum(theta) - - ! Accept energy based on restriction energy - if (E_out <= E_in - this%u) exit - end do - end function maxwellenergy_sample - - subroutine maxwellenergy_from_hdf5(this, group_id) - class(MaxwellEnergy), intent(inout) :: this - integer(HID_T), intent(in) :: group_id - - integer(HID_T) :: dset_id - - call read_attribute(this%u, group_id, 'u') - dset_id = open_dataset(group_id, 'theta') - call this%theta%from_hdf5(dset_id) - call close_dataset(dset_id) - end subroutine maxwellenergy_from_hdf5 - - function evaporation_sample(this, E_in) result(E_out) - class(Evaporation), intent(in) :: this - real(8), intent(in) :: E_in ! incoming energy - real(8) :: E_out ! sampled outgoing energy - - real(8) :: theta ! evaporation spectrum parameter - real(8) :: x, y, v - - ! Get temperature corresponding to incoming energy - theta = this % theta % evaluate(E_in) - - y = (E_in - this%u)/theta - v = 1 - exp(-y) - - ! Sample outgoing energy based on evaporation spectrum probability - ! density function - do - x = -log((ONE - v*prn())*(ONE - v*prn())) - if (x <= y) exit - end do - - E_out = x*theta - end function evaporation_sample - - subroutine evaporation_from_hdf5(this, group_id) - class(Evaporation), intent(inout) :: this - integer(HID_T), intent(in) :: group_id - - integer(HID_T) :: dset_id - - call read_attribute(this%u, group_id, 'u') - dset_id = open_dataset(group_id, 'theta') - call this%theta%from_hdf5(dset_id) - call close_dataset(dset_id) - end subroutine evaporation_from_hdf5 - - function watt_sample(this, E_in) result(E_out) - class(WattEnergy), intent(in) :: this - real(8), intent(in) :: E_in ! incoming energy - real(8) :: E_out ! sampled outgoing energy - - real(8) :: a, b ! Watt spectrum parameters - - ! Determine Watt parameter 'a' from tabulated function - a = this % a % evaluate(E_in) - - ! Determine Watt parameter 'b' from tabulated function - b = this % b % evaluate(E_in) - - do - ! Sample energy-dependent Watt fission spectrum - E_out = watt_spectrum(a, b) - - ! Accept energy based on restriction energy - if (E_out <= E_in - this%u) exit - end do - end function watt_sample - - subroutine watt_from_hdf5(this, group_id) - class(WattEnergy), intent(inout) :: this - integer(HID_T), intent(in) :: group_id - - integer(HID_T) :: dset_id - - call read_attribute(this%u, group_id, 'u') - - dset_id = open_dataset(group_id, 'a') - call this%a%from_hdf5(dset_id) - call close_dataset(dset_id) - - dset_id = open_dataset(group_id, 'b') - call this%b%from_hdf5(dset_id) - call close_dataset(dset_id) - end subroutine watt_from_hdf5 - -end module energy_distribution diff --git a/src/hdf5_interface.cpp b/src/hdf5_interface.cpp index 9226cb9adf..5d6926479c 100644 --- a/src/hdf5_interface.cpp +++ b/src/hdf5_interface.cpp @@ -51,6 +51,35 @@ get_shape(hid_t obj_id, hsize_t* dims) } +std::vector attribute_shape(hid_t obj_id, const char* name) +{ + hid_t attr = H5Aopen(obj_id, name, H5P_DEFAULT); + std::vector shape = object_shape(attr); + H5Aclose(attr); + return shape; +} + +std::vector object_shape(hid_t obj_id) +{ + // Get number of dimensions + auto type = H5Iget_type(obj_id); + hid_t dspace; + if (type == H5I_DATASET) { + dspace = H5Dget_space(obj_id); + } else if (type == H5I_ATTR) { + dspace = H5Aget_space(obj_id); + } + int n = H5Sget_simple_extent_ndims(dspace); + + // Get shape of array + std::vector shape(n); + H5Sget_simple_extent_dims(dspace, shape.data(), nullptr); + + // Free resources and return + H5Sclose(dspace); + return shape; +} + void get_shape_attr(hid_t obj_id, const char* name, hsize_t* dims) { @@ -116,6 +145,18 @@ dataset_typesize(hid_t dset) } +void +ensure_exists(hid_t group_id, const char* name) +{ + if (!object_exists(group_id, name)) { + std::stringstream err_msg; + err_msg << "Object \"" << name << "\" does not exist in group " + << object_name(group_id); + fatal_error(err_msg); + } +} + + hid_t file_open(const char* filename, char mode, bool parallel) { @@ -285,6 +326,36 @@ get_groups(hid_t group_id, char* name[]) } } +std::vector +group_names(hid_t group_id) +{ + // Determine number of links in the group + H5G_info_t info; + H5Gget_info(group_id, &info); + + // Iterate over links to get names + H5O_info_t oinfo; + size_t size; + std::vector names; + for (hsize_t i = 0; i < info.nlinks; ++i) { + // Determine type of object (and skip non-group) + H5Oget_info_by_idx(group_id, ".", H5_INDEX_NAME, H5_ITER_INC, i, &oinfo, + H5P_DEFAULT); + if (oinfo.type != H5O_TYPE_GROUP) continue; + + // Get size of name + size = 1 + H5Lget_name_by_idx(group_id, ".", H5_INDEX_NAME, H5_ITER_INC, + i, nullptr, 0, H5P_DEFAULT); + + // Read name + char buffer[size]; + H5Lget_name_by_idx(group_id, ".", H5_INDEX_NAME, H5_ITER_INC, i, + buffer, size, H5P_DEFAULT); + names.emplace_back(&buffer[0], size); + } + return names; +} + bool object_exists(hid_t object_id, const char* name) @@ -299,14 +370,23 @@ object_exists(hid_t object_id, const char* name) } +std::string +object_name(hid_t obj_id) +{ + // Determine size and create buffer + size_t size = 1 + H5Iget_name(obj_id, nullptr, 0); + char buffer[size]; + + // Read and return name + H5Iget_name(obj_id, buffer, size); + return {buffer, size}; +} + + hid_t open_dataset(hid_t group_id, const char* name) { - if (!object_exists(group_id, name)) { - std::stringstream err_msg; - err_msg << "Group \"" << name << "\" does not exist"; - fatal_error(err_msg); - } + ensure_exists(group_id, name); return H5Dopen(group_id, name, H5P_DEFAULT); } @@ -314,11 +394,7 @@ open_dataset(hid_t group_id, const char* name) hid_t open_group(hid_t group_id, const char* name) { - if (!object_exists(group_id, name)) { - std::stringstream err_msg; - err_msg << "Group \"" << name << "\" does not exist"; - fatal_error(err_msg); - } + ensure_exists(group_id, name); return H5Gopen(group_id, name, H5P_DEFAULT); } @@ -425,7 +501,7 @@ read_string(hid_t obj_id, const char* name, size_t slen, char* buffer, bool inde void -read_complex(hid_t obj_id, const char* name, double _Complex* buffer, bool indep) +read_complex(hid_t obj_id, const char* name, std::complex* buffer, bool indep) { // Create compound datatype for complex numbers struct complex_t { diff --git a/src/hdf5_interface.h b/src/hdf5_interface.h index 152c52c1cd..65a9e2c995 100644 --- a/src/hdf5_interface.h +++ b/src/hdf5_interface.h @@ -1,19 +1,20 @@ #ifndef OPENMC_HDF5_INTERFACE_H #define OPENMC_HDF5_INTERFACE_H -#include "hdf5.h" -#include "hdf5_hl.h" - #include +#include #include #include #include #include -#include + +#include "hdf5.h" +#include "hdf5_hl.h" +#include "xtensor/xadapt.hpp" +#include "xtensor/xarray.hpp" #include "position.h" - namespace openmc { //============================================================================== @@ -21,7 +22,7 @@ namespace openmc { //============================================================================== void read_attr(hid_t obj_id, const char* name, hid_t mem_type_id, - const void* buffer); + void* buffer); void write_attr(hid_t obj_id, int ndim, const hsize_t* dims, const char* name, hid_t mem_type_id, const void* buffer); void read_dataset(hid_t obj_id, const char* name, hid_t mem_type_id, @@ -72,6 +73,12 @@ read_nd_vector(hid_t obj_id, const char* name, std::vector > > > >& result, bool must_have = false); +std::vector attribute_shape(hid_t obj_id, const char* name); +void ensure_exists(hid_t group_id, const char* name); +std::vector group_names(hid_t group_id); +std::vector object_shape(hid_t obj_id); +std::string object_name(hid_t obj_id); + //============================================================================== // Fortran compatibility functions //============================================================================== @@ -101,7 +108,7 @@ extern "C" { void read_attr_string(hid_t obj_id, const char* name, size_t slen, char* buffer); void read_complex(hid_t obj_id, const char* name, - double _Complex* buffer, bool indep); + std::complex* buffer, bool indep); void read_double(hid_t obj_id, const char* name, double* buffer, bool indep); void read_int(hid_t obj_id, const char* name, int* buffer, @@ -142,7 +149,127 @@ template struct H5TypeMap { static const hid_t type_id; }; //============================================================================== -// Template functions used to provide simple interface to lower-level functions +// Templates/overloads for read_attribute +//============================================================================== + +// Scalar version +template +void read_attribute(hid_t obj_id, const char* name, T& buffer) +{ + read_attr(obj_id, name, H5TypeMap::type_id, &buffer); +} + +// vector version +template +void read_attribute(hid_t obj_id, const char* name, std::vector& vec) +{ + // Get shape of attribute array + 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; + vec.resize(size); + + // Read data from attribute + read_attr(obj_id, name, H5TypeMap::type_id, vec.data()); +} + +// Generic array version +template +void read_attribute(hid_t obj_id, const char* name, xt::xarray& arr) +{ + // Get shape of attribute array + 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; + T* buffer = new T[size]; + + // Read data from attribute + read_attr(obj_id, name, H5TypeMap::type_id, buffer); + + // Adapt array into xarray + arr = xt::adapt(buffer, size, xt::acquire_ownership(), shape); +} + +// overload for std::string +inline void +read_attribute(hid_t obj_id, const char* name, std::string& str) +{ + // Create buffer to read data into + auto n = attribute_typesize(obj_id, name); + char buffer[n]; + + // Read attribute and set string + read_attr_string(obj_id, name, n, buffer); + str = std::string{buffer, n}; +} + +//============================================================================== +// Templates/overloads for read_dataset +//============================================================================== + +template +void read_dataset(hid_t obj_id, const char* name, T buffer, bool indep=false) +{ + read_dataset(obj_id, name, H5TypeMap::type_id, &buffer, indep); +} + +template +void read_dataset(hid_t dset, std::vector& vec, bool indep=false) +{ + // Get shape of dataset + std::vector shape = object_shape(dset); + + // Resize vector to appropriate size + vec.resize(shape[0]); + + // Read data into vector + read_dataset(dset, nullptr, H5TypeMap::type_id, vec.data(), indep); +} + +template +void read_dataset(hid_t obj_id, const char* name, std::vector& vec, bool indep=false) +{ + hid_t dset = open_dataset(obj_id, name); + read_dataset(dset, vec, indep); + close_dataset(dset); +} + +template +void read_dataset(hid_t dset, xt::xarray& arr, bool indep=false) +{ + // Get shape of dataset + std::vector shape = object_shape(dset); + + // Allocate new array to read data into + std::size_t size = 1; + for (const auto x : shape) + size *= x; + T* buffer = new T[size]; + + // Read data from attribute + read_dataset(dset, nullptr, H5TypeMap::type_id, buffer, indep); + + // Adapt into xarray + arr = xt::adapt(buffer, size, xt::acquire_ownership(), shape); +} + +template +void read_dataset(hid_t obj_id, const char* name, xt::xarray& arr, bool indep=false) +{ + // Open dataset and read array + hid_t dset = open_dataset(obj_id, name); + read_dataset(dset, arr, indep); + close_dataset(dset); +} + +//============================================================================== +// Templates/overloads for write_attribute //============================================================================== template inline void @@ -151,8 +278,8 @@ write_attribute(hid_t obj_id, const char* name, T buffer) write_attr(obj_id, name, 0, nullptr, H5TypeMap::type_id, &buffer); } -template<> inline void -write_attribute(hid_t obj_id, const char* name, const char* buffer) +inline void +write_attribute(hid_t obj_id, const char* name, const char* buffer) { write_attr_string(obj_id, name, buffer); } @@ -164,14 +291,18 @@ write_attribute(hid_t obj_id, const char* name, const std::array& buffer) write_attr(obj_id, 1, dims, name, H5TypeMap::type_id, buffer.data()); } +//============================================================================== +// Templates/overloads for write_dataset +//============================================================================== + template inline void write_dataset(hid_t obj_id, const char* name, T buffer) { write_dataset(obj_id, 0, nullptr, name, H5TypeMap::type_id, &buffer, false); } -template<> inline void -write_dataset(hid_t obj_id, const char* name, const char* buffer) +inline void +write_dataset(hid_t obj_id, const char* name, const char* buffer) { write_string(obj_id, name, buffer, false); } diff --git a/src/input_xml.F90 b/src/input_xml.F90 index e81bb92f7b..da33a6627f 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -3254,7 +3254,7 @@ contains ! Check if the specified tally mesh exists if (mesh_dict % has(meshid)) then pl % meshlines_mesh => meshes(mesh_dict % get(meshid)) - if (meshes(meshid) % type /= LATTICE_RECT) then + if (meshes(meshid) % type /= MESH_REGULAR) then call fatal_error("Non-rectangular mesh specified in & &meshlines for plot " // trim(to_str(pl % id))) end if diff --git a/src/math_functions.cpp b/src/math_functions.cpp index 8a36f7b922..f2a5e415dd 100644 --- a/src/math_functions.cpp +++ b/src/math_functions.cpp @@ -650,6 +650,14 @@ void rotate_angle_c(double uvw[3], double mu, double* phi) { } +Direction rotate_angle(Direction u, double mu, double* phi) +{ + double uvw[] {u.x, u.y, u.z}; + rotate_angle_c(uvw, mu, phi); + return {uvw[0], uvw[1], uvw[2]}; +} + + double maxwell_spectrum_c(double T) { // Set the random numbers double r1 = prn(); diff --git a/src/math_functions.h b/src/math_functions.h index 7ef39c4461..05748e031b 100644 --- a/src/math_functions.h +++ b/src/math_functions.h @@ -1,13 +1,14 @@ //! \file math_functions.h //! A collection of elementary math functions. -#ifndef MATH_FUNCTIONS_H -#define MATH_FUNCTIONS_H +#ifndef OPENMC_MATH_FUNCTIONS_H +#define OPENMC_MATH_FUNCTIONS_H #include #include #include "constants.h" +#include "position.h" #include "random_lcg.h" @@ -124,6 +125,8 @@ extern "C" void calc_zn_rad_c(int n, double rho, double zn_rad[]); extern "C" void rotate_angle_c(double uvw[3], double mu, double* phi); +Direction rotate_angle(Direction u, double mu, double* phi); + //============================================================================== //! Samples an energy from the Maxwell fission distribution based on a direct //! sampling scheme. @@ -220,4 +223,4 @@ extern "C" double spline_integrate_c(int n, const double x[], const double y[], const double z[], double xa, double xb); } // namespace openmc -#endif // MATH_FUNCTIONS_H +#endif // OPENMC_MATH_FUNCTIONS_H diff --git a/src/nuclide_header.F90 b/src/nuclide_header.F90 index 931df199e5..8cd225bc98 100644 --- a/src/nuclide_header.F90 +++ b/src/nuclide_header.F90 @@ -17,11 +17,9 @@ module nuclide_header FIT_T, FIT_A, FIT_F, MultipoleArray use message_passing use multipole_header, only: MultipoleArray - use product_header, only: AngleEnergyContainer use random_lcg, only: prn, future_prn, prn_set_stream use reaction_header, only: Reaction use sab_header, only: SAlphaBeta, sab_tables - use secondary_uncorrelated, only: UncorrelatedAngleEnergy use settings use stl_vector, only: VectorInt, VectorReal use string @@ -642,29 +640,33 @@ contains if (rx % MT >= N_2N0 .and. rx % MT <= N_2NC .and. find(MTs, N_2N) /= -1) cycle do t = 1, n_temperature - j = rx % xs(t) % threshold - n = size(rx % xs(t) % value) + j = rx % xs_threshold(t) + n = rx % xs_size(t) ! Add contribution to total cross section - this % xs(t) % value(XS_TOTAL,j:j+n-1) = this % xs(t) % & - value(XS_TOTAL,j:j+n-1) + rx % xs(t) % value + do k = j, j + n - 1 + this % xs(t) % value(XS_TOTAL,k) = this % xs(t) % & + value(XS_TOTAL,k) + rx % xs(t, k - j + 1) + end do ! Calculate photon production cross section - do k = 1, size(rx % products) - if (rx % products(k) % particle == PHOTON) then + do k = 1, rx % products_size() + if (rx % product_particle(k) == PHOTON) then do l = 1, n this % xs(t) % value(XS_PHOTON_PROD,l+j-1) = & this % xs(t) % value(XS_PHOTON_PROD,l+j-1) + & - rx % xs(t) % value(l) * rx % products(k) % & - yield % evaluate(this % grid(t) % energy(l+j-1)) + rx % xs(t, l) * rx % product_yield(k, & + this % grid(t) % energy(l+j-1)) end do end if end do ! Add contribution to absorption cross section if (is_disappearance(rx % MT)) then - this % xs(t) % value(XS_ABSORPTION,j:j+n-1) = this % xs(t) % & - value(XS_ABSORPTION,j:j+n-1) + rx % xs(t) % value + do k = j, j + n - 1 + this % xs(t) % value(XS_ABSORPTION,k) = this % xs(t) % & + value(XS_ABSORPTION,k) + rx % xs(t, k - j + 1) + end do end if ! Information about fission reactions @@ -680,39 +682,20 @@ contains ! Add contribution to fission cross section if (is_fission(rx % MT)) then this % fissionable = .true. - this % xs(t) % value(XS_FISSION,j:j+n-1) = this % xs(t) % & - value(XS_FISSION,j:j+n-1) + rx % xs(t) % value + do k = j, j + n - 1 + this % xs(t) % value(XS_FISSION,k) = this % xs(t) % & + value(XS_FISSION,k) + rx % xs(t, k - j + 1) - ! Also need to add fission cross sections to absorption - this % xs(t) % value(XS_ABSORPTION,j:j+n-1) = this % xs(t) % & - value(XS_ABSORPTION,j:j+n-1) + rx % xs(t) % value + ! Also need to add fission cross sections to absorption + this % xs(t) % value(XS_ABSORPTION,k) = this % xs(t) % & + value(XS_ABSORPTION,k) + rx % xs(t, k - j + 1) + end do ! Keep track of this reaction for easy searching later if (t == 1) then i_fission = i_fission + 1 this % index_fission(i_fission) = i this % n_fission = this % n_fission + 1 - - ! <<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<< - ! Before the secondary distribution refactor, when the angle/energy - ! distribution was uncorrelated, no angle was actually sampled. With - ! the refactor, an angle is always sampled for an uncorrelated - ! distribution even when no angle distribution exists in the ACE file - ! (isotropic is assumed). To preserve the RNG stream, we explicitly - ! mark fission reactions so that we avoid the angle sampling. - do k = 1, size(rx % products) - if (rx % products(k) % particle == NEUTRON) then - do m = 1, size(rx % products(k) % distribution) - associate (aedist => rx % products(k) % distribution(m) % obj) - select type (aedist) - type is (UncorrelatedAngleEnergy) - aedist % fission = .true. - end select - end associate - end do - end if - end do - ! <<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<< end if end if ! fission end do ! temperature @@ -721,12 +704,13 @@ contains ! Determine number of delayed neutron precursors if (this % fissionable) then - do i = 1, size(this % reactions(this % index_fission(1)) % products) - if (this % reactions(this % index_fission(1)) % products(i) % & - emission_mode == EMISSION_DELAYED) then - this % n_precursor = this % n_precursor + 1 - end if - end do + associate (rx => this % reactions(this % index_fission(1))) + do i = 1, rx % products_size() + if (rx % product_emission_mode(i) == EMISSION_DELAYED) then + this % n_precursor = this % n_precursor + 1 + end if + end do + end associate end if ! Calculate nu-fission cross section @@ -761,36 +745,30 @@ contains select case (emission_mode) case (EMISSION_PROMPT) - associate (product => this % reactions(this % index_fission(1)) % products(1)) - nu = product % yield % evaluate(E) + associate (rx => this % reactions(this % index_fission(1))) + nu = rx % product_yield(1, E) end associate case (EMISSION_DELAYED) if (this % n_precursor > 0) then - if (present(group) .and. group < & - size(this % reactions(this % index_fission(1)) % products)) then - ! If delayed group specified, determine yield immediately - associate(p => this % reactions(this % index_fission(1)) % products(1 + group)) - nu = p % yield % evaluate(E) - end associate + associate(rx => this % reactions(this % index_fission(1))) + if (present(group) .and. group < rx % products_size()) then + ! If delayed group specified, determine yield immediately + nu = rx % product_yield(1 + group, E) + else + nu = ZERO - else - nu = ZERO + do i = 2, rx % products_size() + ! Skip any non-neutron products + if (rx % product_particle(i) /= NEUTRON) exit - associate (rx => this % reactions(this % index_fission(1))) - do i = 2, size(rx % products) - associate (product => rx % products(i)) - ! Skip any non-neutron products - if (product % particle /= NEUTRON) exit - - ! Evaluate yield - if (product % emission_mode == EMISSION_DELAYED) then - nu = nu + product % yield % evaluate(E) - end if - end associate + ! Evaluate yield + if (rx % product_emission_mode(i) == EMISSION_DELAYED) then + nu = nu + rx % product_yield(i, E) + end if end do - end associate - end if + end if + end associate else nu = ZERO end if @@ -799,8 +777,8 @@ contains if (allocated(this % total_nu)) then nu = this % total_nu % evaluate(E) else - associate (product => this % reactions(this % index_fission(1)) % products(1)) - nu = product % yield % evaluate(E) + associate (rx => this % reactions(this % index_fission(1))) + nu = rx % product_yield(1, E) end associate end if end select @@ -868,6 +846,7 @@ contains integer :: i_high ! upper logarithmic mapping index integer :: i_rxn ! reaction index integer :: j ! index in DEPLETION_RX + integer :: threshold ! threshold energy index real(8) :: f ! interp factor on nuclide energy grid real(8) :: kT ! temperature in eV real(8) :: sig_t, sig_a, sig_f ! Intermediate multipole variables @@ -1009,10 +988,11 @@ contains ! need to specifically check its threshold index i_rxn = this % reaction_index(DEPLETION_RX(1)) if (i_rxn > 0) then - associate (xs => this % reactions(i_rxn) % xs(i_temp)) + associate (rx => this % reactions(i_rxn)) + threshold = rx % xs_threshold(i_temp) micro_xs % reaction(1) = (ONE - f) * & - xs % value(i_grid - xs % threshold + 1) + & - f * xs % value(i_grid - xs % threshold + 2) + rx % xs(i_temp, i_grid - threshold + 1) + & + f * rx % xs(i_temp, i_grid - threshold + 2) end associate end if @@ -1022,11 +1002,12 @@ contains ! reaction xs appropriately i_rxn = this % reaction_index(DEPLETION_RX(j)) if (i_rxn > 0) then - associate (xs => this % reactions(i_rxn) % xs(i_temp)) - if (i_grid >= xs % threshold) then + associate (rx => this % reactions(i_rxn)) + threshold = rx % xs_threshold(i_temp) + if (i_grid >= threshold) then micro_xs % reaction(j) = (ONE - f) * & - xs % value(i_grid - xs % threshold + 1) + & - f * xs % value(i_grid - xs % threshold + 2) + rx % xs(i_temp, i_grid - threshold + 1) + & + f * rx % xs(i_temp, i_grid - threshold + 2) elseif (j >= 4) then ! One can show that the the threshold for (n,(x+1)n) is always ! higher than the threshold for (n,xn). Thus, if we are below @@ -1091,8 +1072,9 @@ contains f = micro_xs % interp_factor if (i_temp > 0) then - associate (xs => this % reactions(1) % xs(i_temp) % value) - micro_xs % elastic = (ONE - f) * xs(i_grid) + f * xs(i_grid + 1) + associate (rx => this % reactions(1)) + micro_xs % elastic = (ONE - f) * rx % xs(i_temp, i_grid) + & + f * rx % xs(i_temp, i_grid + 1) end associate else ! For multipole, elastic is total - absorption @@ -1459,6 +1441,7 @@ contains integer :: i_energy ! index for energy integer :: i_low ! band index at lower bounding energy integer :: i_up ! band index at upper bounding energy + integer :: threshold ! threshold energy index real(8) :: f ! interpolation factor real(8) :: r ! pseudo-random number real(8) :: elastic ! elastic cross section @@ -1551,10 +1534,11 @@ contains f = micro_xs % interp_factor ! Determine inelastic scattering cross section - associate (xs => this % reactions(this % urr_inelastic) % xs(i_temp)) - if (i_energy >= xs % threshold) then - inelastic = (ONE - f) * xs % value(i_energy - xs % threshold + 1) + & - f * xs % value(i_energy - xs % threshold + 2) + associate (rx => this % reactions(this % urr_inelastic)) + threshold = rx % xs_threshold(i_temp) + if (i_energy >= threshold) then + inelastic = (ONE - f) * rx % xs(i_temp, i_energy - threshold + 1) + & + f * rx % xs(i_temp, i_energy - threshold + 2) end if end associate end if diff --git a/src/particle.cpp b/src/particle.cpp index 74523eef23..e46d06c011 100644 --- a/src/particle.cpp +++ b/src/particle.cpp @@ -62,7 +62,7 @@ Particle::initialize() clear(); // Set particle to neutron that's alive - type = NEUTRON; + type = static_cast(ParticleType::neutron); alive = true; // clear attributes diff --git a/src/particle.h b/src/particle.h index 18244098e0..b533971710 100644 --- a/src/particle.h +++ b/src/particle.h @@ -15,12 +15,27 @@ namespace openmc { // Constants //============================================================================== +// Since cross section libraries come with different numbers of delayed groups +// (e.g. ENDF/B-VII.1 has 6 and JEFF 3.1.1 has 8 delayed groups) and we don't +// yet know what cross section library is being used when the tallies.xml file +// is read in, we want to have an upper bound on the size of the array we +// use to store the bins for delayed group tallies. constexpr int MAX_DELAYED_GROUPS {8}; + +// Maximum number of secondary particles created constexpr int MAX_SECONDARY {1000}; -constexpr int NEUTRON {1}; + +// Maximum number of lost particles constexpr int MAX_LOST_PARTICLES {10}; + +// Maximum number of lost particles, relative to the total number of particles constexpr double REL_MAX_LOST_PARTICLES {1.0e-6}; +//! Particle types +enum class ParticleType { + neutron, photon, electron, positron +}; + extern "C" { struct LocalCoord { diff --git a/src/physics.F90 b/src/physics.F90 index 0eed834189..d5f6302a82 100644 --- a/src/physics.F90 +++ b/src/physics.F90 @@ -18,7 +18,6 @@ module physics use random_lcg, only: prn, advance_prn_seed, prn_set_stream use reaction_header, only: Reaction use sab_header, only: sab_tables - use secondary_uncorrelated, only: UncorrelatedAngleEnergy use settings use simulation_header use string, only: to_str @@ -506,6 +505,7 @@ contains integer :: i integer :: i_grid integer :: i_temp + integer :: threshold real(8) :: f real(8) :: prob real(8) :: cutoff @@ -545,13 +545,14 @@ contains FISSION_REACTION_LOOP: do i = 1, nuc % n_fission i_reaction = nuc % index_fission(i) - associate (xs => nuc % reactions(i_reaction) % xs(i_temp)) + associate (rx => nuc % reactions(i_reaction)) ! if energy is below threshold for this reaction, skip it - if (i_grid < xs % threshold) cycle + threshold = rx % xs_threshold(i_temp) + if (i_grid < threshold) cycle ! add to cumulative probability - prob = prob + ((ONE - f) * xs % value(i_grid - xs % threshold + 1) & - + f*(xs % value(i_grid - xs % threshold + 2))) + prob = prob + ((ONE - f) * rx % xs(i_temp, i_grid - threshold + 1) & + + f*(rx % xs(i_temp, i_grid - threshold + 2))) end associate ! Create fission bank sites if fission occurs @@ -593,17 +594,17 @@ contains ! Loop through each reaction type REACTION_LOOP: do i_reaction = 1, size(nuc % reactions) associate (rx => nuc % reactions(i_reaction)) - threshold = rx % xs(i_temp) % threshold + threshold = rx % xs_threshold(i_temp) ! if energy is below threshold for this reaction, skip it if (i_grid < threshold) cycle - do i_product = 1, size(rx % products) - if (rx % products(i_product) % particle == PHOTON) then + do i_product = 1, rx % products_size() + if (rx % product_particle(i_product) == PHOTON) then ! add to cumulative probability - yield = rx % products(i_product) % yield % evaluate(E) - prob = prob + ((ONE - f) * rx % xs(i_temp) % value(i_grid - threshold + 1) & - + f*(rx % xs(i_temp) % value(i_grid - threshold + 2))) * yield + yield = rx % product_yield(i_product, E) + prob = prob + ((ONE - f) * rx % xs(i_temp, i_grid - threshold + 1) & + + f*(rx % xs(i_temp, i_grid - threshold + 2))) * yield if (prob > cutoff) return last_valid_reaction = i_reaction @@ -672,6 +673,7 @@ contains integer :: j integer :: i_temp integer :: i_grid + integer :: threshold real(8) :: f real(8) :: prob real(8) :: cutoff @@ -750,14 +752,14 @@ contains &// trim(nuc % name)) end if - associate (rx => nuc % reactions(i), & - xs => nuc % reactions(i) % xs(i_temp)) + associate (rx => nuc % reactions(i)) ! if energy is below threshold for this reaction, skip it - if (i_grid < xs % threshold) cycle + threshold = rx % xs_threshold(i_temp) + if (i_grid < threshold) cycle ! add to cumulative probability - prob = prob + ((ONE - f)*xs % value(i_grid - xs % threshold + 1) & - + f*(xs % value(i_grid - xs % threshold + 2))) + prob = prob + ((ONE - f)*rx % xs(i_temp, i_grid - threshold + 1) & + + f*(rx % xs(i_temp, i_grid - threshold + 2))) end associate end do @@ -839,14 +841,7 @@ contains vel = sqrt(dot_product(v_n, v_n)) ! Sample scattering angle - select type (dist => rxn % products(1) % distribution(1) % obj) - type is (UncorrelatedAngleEnergy) - if (allocated(dist % angle % energy)) then - mu_cm = dist % angle % sample(E) - else - mu_cm = TWO*prn() - ONE - end if - end select + mu_cm = rxn % sample_elastic_mu(E) ! Determine direction cosines in CM uvw_cm = v_n/vel @@ -1581,7 +1576,7 @@ contains do group = 1, nuc % n_precursor ! determine delayed neutron precursor yield for group j - yield = rxn % products(1 + group) % yield % evaluate(E_in) + yield = rxn % product_yield(1 + group, E_in) ! Check if this group is sampled prob = prob + yield @@ -1600,7 +1595,7 @@ contains do ! sample from energy/angle distribution -- note that mu has already been ! sampled above and doesn't need to be resampled - call rxn % products(1 + group) % sample(E_in, site % E, mu) + call rxn % product_sample(1 + group, E_in, site % E, mu) ! resample if energy is greater than maximum neutron energy if (site % E < energy_max(NEUTRON)) exit @@ -1624,7 +1619,7 @@ contains ! sample from prompt neutron energy distribution n_sample = 0 do - call rxn % products(1) % sample(E_in, site % E, mu) + call rxn % product_sample(1, E_in, site % E, mu) ! resample if energy is greater than maximum neutron energy if (site % E < energy_max(NEUTRON)) exit @@ -1663,7 +1658,7 @@ contains E_in = p % E ! sample outgoing energy and scattering cosine - call rxn % products(1) % sample(E_in, E, mu) + call rxn % product_sample(1, E_in, E, mu) ! if scattering system is in center-of-mass, transfer cosine of scattering ! angle and outgoing energy from CM to LAB @@ -1692,7 +1687,7 @@ contains p % coord(1) % uvw = rotate_angle(p % coord(1) % uvw, mu) ! evaluate yield - yield = rxn % products(1) % yield % evaluate(E_in) + yield = rxn % product_yield(1, E_in) if (mod(yield, ONE) == ZERO) then ! If yield is integral, create exactly that many secondary particles do i = 1, nint(yield) - 1 @@ -1740,8 +1735,8 @@ contains call sample_photon_product(i_nuclide, p % E, i_reaction, i_product) ! Sample the outgoing energy and angle - call nuclides(i_nuclide) % reactions(i_reaction) % products(i_product) & - % sample(p % E, E, mu) + call nuclides(i_nuclide) % reactions(i_reaction) % & + product_sample(i_product, p % E, E, mu) ! Sample the new direction uvw = rotate_angle(p % coord(1) % uvw, mu) diff --git a/src/product_header.F90 b/src/product_header.F90 deleted file mode 100644 index a5e23f4b6c..0000000000 --- a/src/product_header.F90 +++ /dev/null @@ -1,153 +0,0 @@ -module product_header - - use angleenergy_header, only: AngleEnergyContainer - use constants, only: ZERO, MAX_WORD_LEN, EMISSION_PROMPT, EMISSION_DELAYED, & - EMISSION_TOTAL, NEUTRON, PHOTON - use endf_header, only: Tabulated1D, Function1D, Polynomial - use hdf5_interface, only: read_attribute, open_group, close_group, & - open_dataset, close_dataset, read_dataset, HID_T - use random_lcg, only: prn - use secondary_correlated, only: CorrelatedAngleEnergy - use secondary_kalbach, only: KalbachMann - use secondary_nbody, only: NBodyPhaseSpace - use secondary_uncorrelated, only: UncorrelatedAngleEnergy - use string, only: to_str - -!=============================================================================== -! REACTIONPRODUCT stores a data for a reaction product including its yield and -! angle-energy distributions, each of which has a given probability of occurring -! for a given incoming energy. In general, most products only have one -! angle-energy distribution, but for some cases (e.g., (n,2n) in certain -! nuclides) multiple distinct distributions exist. -!=============================================================================== - - type :: ReactionProduct - integer :: particle - integer :: emission_mode ! prompt, delayed, or total emission - real(8) :: decay_rate ! Decay rate for delayed neutron precursors - class(Function1D), pointer :: yield => null() ! Energy-dependent neutron yield - type(Tabulated1D), allocatable :: applicability(:) - type(AngleEnergyContainer), allocatable :: distribution(:) - contains - procedure :: sample => reactionproduct_sample - procedure :: from_hdf5 => reactionproduct_from_hdf5 - end type ReactionProduct - -contains - - subroutine reactionproduct_sample(this, E_in, E_out, mu) - class(ReactionProduct), intent(in) :: this - real(8), intent(in) :: E_in ! incoming energy - real(8), intent(out) :: E_out ! sampled outgoing energy - real(8), intent(out) :: mu ! sampled scattering cosine - - integer :: i ! loop counter - integer :: n ! number of angle-energy distributions - real(8) :: prob ! cumulative probability - real(8) :: c ! sampled cumulative probability - - n = size(this%applicability) - if (n > 1) then - prob = ZERO - c = prn() - do i = 1, n - ! Determine probability that i-th energy distribution is sampled - prob = prob + this % applicability(i) % evaluate(E_in) - - ! If i-th distribution is sampled, sample energy from the distribution - if (c <= prob) then - call this%distribution(i)%obj%sample(E_in, E_out, mu) - exit - end if - end do - else - ! If only one distribution is present, go ahead and sample it - call this%distribution(1)%obj%sample(E_in, E_out, mu) - end if - - end subroutine reactionproduct_sample - - subroutine reactionproduct_from_hdf5(this, group_id) - class(ReactionProduct), intent(inout) :: this - integer(HID_T), intent(in) :: group_id - - integer :: i - integer :: n - integer(HID_T) :: dgroup - integer(HID_T) :: app - integer(HID_T) :: yield - character(MAX_WORD_LEN) :: temp - - ! Read particle type - call read_attribute(temp, group_id, 'particle') - select case (temp) - case ('neutron') - this % particle = NEUTRON - case ('photon') - this % particle = PHOTON - end select - - ! Read emission mode and decay rate - call read_attribute(temp, group_id, 'emission_mode') - select case (temp) - case ('prompt') - this % emission_mode = EMISSION_PROMPT - case ('delayed') - this % emission_mode = EMISSION_DELAYED - case ('total') - this % emission_mode = EMISSION_TOTAL - end select - - ! Read decay rate for delayed emission - if (this % emission_mode == EMISSION_DELAYED) then - call read_attribute(this % decay_rate, group_id, 'decay_rate') - end if - - ! Read secondary particle yield - yield = open_dataset(group_id, 'yield') - call read_attribute(temp, yield, 'type') - select case (temp) - case ('Tabulated1D') - allocate(Tabulated1D :: this % yield) - case ('Polynomial') - allocate(Polynomial :: this % yield) - end select - call this % yield % from_hdf5(yield) - call close_dataset(yield) - - call read_attribute(n, group_id, 'n_distribution') - allocate(this%applicability(n)) - allocate(this%distribution(n)) - - do i = 1, n - dgroup = open_group(group_id, trim('distribution_' // to_str(i - 1))) - - ! Read applicability - if (n > 1) then - app = open_dataset(dgroup, 'applicability') - call this%applicability(i)%from_hdf5(app) - call close_dataset(app) - end if - - ! Read type of distribution and allocate accordingly - call read_attribute(temp, dgroup, 'type') - select case (temp) - case ('uncorrelated') - allocate(UncorrelatedAngleEnergy :: this%distribution(i)%obj) - case ('correlated') - allocate(CorrelatedAngleEnergy :: this%distribution(i)%obj) - case ('nbody') - allocate(NBodyPhaseSpace :: this%distribution(i)%obj) - case ('kalbach-mann') - allocate(KalbachMann :: this%distribution(i)%obj) - end select - - ! Read distribution data - call this%distribution(i)%obj%from_hdf5(dgroup) - - call close_group(dgroup) - end do - - end subroutine reactionproduct_from_hdf5 - -end module product_header diff --git a/src/reaction.cpp b/src/reaction.cpp new file mode 100644 index 0000000000..10e07b18aa --- /dev/null +++ b/src/reaction.cpp @@ -0,0 +1,161 @@ +#include "reaction.h" + +#include +#include // for move + +#include "hdf5_interface.h" +#include "endf.h" +#include "random_lcg.h" +#include "secondary_uncorrelated.h" + +namespace openmc { + +Reaction::Reaction(hid_t group, const std::vector& temperatures) +{ + read_attribute(group, "Q_value", q_value_); + read_attribute(group, "mt", mt_); + int cm; + read_attribute(group, "center_of_mass", cm); + scatter_in_cm_ = (cm == 1); + + // Read cross section and threshold_idx data + for (auto t : temperatures) { + // Get group corresponding to temperature + std::string temp_str {std::to_string(t) + "K"}; + hid_t temp_group = open_group(group, temp_str.c_str()); + hid_t dset = open_dataset(temp_group, "xs"); + + // Get threshold index + TemperatureXS xs; + read_attribute(dset, "threshold_idx", xs.threshold); + + // Read cross section values + read_dataset(dset, xs.value); + close_dataset(dset); + close_group(temp_group); + + // create new entry in xs vector + xs_.push_back(std::move(xs)); + } + + // Read products + for (const auto& name : group_names(group)) { + if (name.rfind("product_", 0) == 0) { + hid_t pgroup = open_group(group, name.c_str()); + products_.emplace_back(pgroup); + close_group(pgroup); + } + } + + // <<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<< + // Before the secondary distribution refactor, when the angle/energy + // distribution was uncorrelated, no angle was actually sampled. With + // the refactor, an angle is always sampled for an uncorrelated + // distribution even when no angle distribution exists in the ACE file + // (isotropic is assumed). To preserve the RNG stream, we explicitly + // mark fission reactions so that we avoid the angle sampling. + if (is_fission(mt_)) { + for (auto& p : products_) { + if (p.particle_ == ParticleType::neutron) { + for (auto& d : p.distribution_) { + auto d_ = dynamic_cast(d.get()); + if (d_) d_->fission() = true; + } + } + } + } + // <<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<< +} + +//============================================================================== +// Fortran compatibility functions +//============================================================================== + +Reaction* reaction_from_hdf5(hid_t group, int* temperatures, int n) +{ + std::vector temps {temperatures, temperatures + n}; + return new Reaction{group, temps}; +} + +void reaction_delete(Reaction* rx) { delete rx; } + +int reaction_mt(Reaction* rx) { return rx->mt_; } + +double reaction_q_value(Reaction* rx) { return rx->q_value_; } + +bool reaction_scatter_in_cm(Reaction* rx) { return rx->scatter_in_cm_; } + +double reaction_product_decay_rate(Reaction* rx, int product) +{ + return rx->products_[product - 1].decay_rate_; +} + +int reaction_product_emission_mode(Reaction* rx, int product) +{ + switch (rx->products_[product - 1].emission_mode_) { + case ReactionProduct::EmissionMode::prompt: + return 1; + case ReactionProduct::EmissionMode::delayed: + return 2; + case ReactionProduct::EmissionMode::total: + return 3; + } +} + +int reaction_product_particle(Reaction* rx, int product) +{ + switch (rx->products_[product - 1].particle_) { + case ParticleType::neutron: + return 1; + case ParticleType::photon: + return 2; + case ParticleType::electron: + return 3; + case ParticleType::positron: + return 4; + } +} + +void reaction_product_sample(Reaction* rx, int product, double E_in, double* E_out, double* mu) +{ + rx->products_[product - 1].sample(E_in, *E_out, *mu); +} + +double reaction_product_yield(Reaction* rx, int product, double E) +{ + return (*rx->products_[product - 1].yield_)(E); +} + +int reaction_products_size(Reaction* rx) { return rx->products_.size(); } + +double reaction_xs(Reaction* rx, int temperature, int energy) +{ + return rx->xs_[temperature - 1].value[energy - 1]; +} + +double reaction_sample_elastic_mu(Reaction* rx, double E) +{ + // Get elastic scattering distribution + auto& d = rx->products_[0].distribution_[0]; + + // Check if it is an uncorrelated angle-energy distribution + auto d_ = dynamic_cast(d.get()); + if (d_) { + return d_->angle().sample(E); + } else { + return 2.0*prn() - 1.0; + } + +} + +int reaction_xs_size(Reaction* rx, int temperature) +{ + return rx->xs_[temperature - 1].value.size(); +} + +int reaction_xs_threshold(Reaction* rx, int temperature) +{ + return rx->xs_[temperature - 1].threshold; +} + +} diff --git a/src/reaction.h b/src/reaction.h new file mode 100644 index 0000000000..5efda042ad --- /dev/null +++ b/src/reaction.h @@ -0,0 +1,65 @@ +//! \file reaction.h +//! Data for an incident neutron reaction + +#ifndef OPENMC_REACTION_H +#define OPENMC_REACTION_H + +#include + +#include "hdf5.h" +#include "reaction_product.h" + +namespace openmc { + +//============================================================================== +//! Data for a single reaction including cross sections (possibly at multiple +//! temperatures) and reaction products (with secondary angle-energy +//! distributions) +//============================================================================== + +class Reaction { +public: + //! Construct reaction from HDF5 data + //! \param[in] group HDF5 group containing reaction data + //! \param[in] temperatures Desired temperatures for cross sections + explicit Reaction(hid_t group, const std::vector& temperatures); + + //! Cross section at a single temperature + struct TemperatureXS { + int threshold; + std::vector value; + }; + + int mt_; //!< ENDF MT value + double q_value_; //!< Reaction Q value in [eV] + bool scatter_in_cm_; //!< scattering system in center-of-mass? + std::vector xs_; //!< Cross section at each temperature + std::vector products_; //!< Reaction products +}; + +//============================================================================== +// Fortran compatibility functions +//============================================================================== + +extern "C" { + Reaction* reaction_from_hdf5(hid_t group, int* temperatures, int n); + void reaction_delete(Reaction* rx); + int reaction_mt(Reaction* rx); + double reaction_q_value(Reaction* rx); + bool reaction_scatter_in_cm(Reaction* rx); + double reaction_product_decay_rate(Reaction* rx, int product); + int reaction_product_emission_mode(Reaction* rx, int product); + int reaction_product_particle(Reaction* rx, int product); + void reaction_product_sample(Reaction* rx, int product, double E_in, + double* E_out, double* mu); + int reaction_products_size(Reaction* rx); + double reaction_product_yield(Reaction* rx, int product, double E); + double reaction_sample_elastic_mu(Reaction* rx, double E); + double reaction_xs(Reaction* xs, int temperature, int energy); + int reaction_xs_size(Reaction* xs, int temperature); + int reaction_xs_threshold(Reaction* xs, int temperature); +} + +} // namespace openmc + +#endif // OPENMC_REACTION_H diff --git a/src/reaction_header.F90 b/src/reaction_header.F90 index 48973336bf..915bf48337 100644 --- a/src/reaction_header.F90 +++ b/src/reaction_header.F90 @@ -1,83 +1,268 @@ module reaction_header + use, intrinsic :: ISO_C_BINDING + use constants, only: MAX_WORD_LEN use hdf5_interface - use product_header, only: ReactionProduct use stl_vector, only: VectorInt use string, only: to_str, starts_with implicit none + private !=============================================================================== ! REACTION contains the cross-section and secondary energy and angle ! distributions for a single reaction in a continuous-energy ACE-format table !=============================================================================== - type TemperatureXS - integer :: threshold ! Energy grid index of threshold - real(8), allocatable :: value(:) ! Cross section values - end type TemperatureXS - - type Reaction - integer :: MT ! ENDF MT value - real(8) :: Q_value ! Reaction Q value - logical :: scatter_in_cm ! scattering system in center-of-mass? - type(TemperatureXS), allocatable :: xs(:) - type(ReactionProduct), allocatable :: products(:) + type, public :: Reaction + type(C_PTR) :: ptr + integer(C_INT) :: MT ! ENDF MT value + real(C_DOUBLE) :: Q_value ! Reaction Q value + logical(C_BOOL) :: scatter_in_cm ! scattering system in center-of-mass? contains - procedure :: from_hdf5 => reaction_from_hdf5 + procedure :: from_hdf5 + procedure :: mt_ + procedure :: q_value_ + procedure :: scatter_in_cm_ + procedure :: product_decay_rate + procedure :: product_emission_mode + procedure :: product_particle + procedure :: product_sample + procedure :: product_yield + procedure :: products_size + procedure :: sample_elastic_mu + procedure :: xs + procedure :: xs_size + procedure :: xs_threshold end type Reaction + interface + function reaction_from_hdf5(group, temperatures, n) result(ptr) bind(C) + import C_PTR, HID_T, C_INT + integer(HID_T), value :: group + integer(C_INT), intent(in) :: temperatures + integer(C_INT), value :: n + type(C_PTR) :: ptr + end function + + function reaction_mt(ptr) result(mt) bind(C) + import C_PTR, C_INT + type(C_PTR), value :: ptr + integer(C_INT) :: mt + end function + + function reaction_q_value(ptr) result(q_value) bind(C) + import C_PTR, C_DOUBLE + type(C_PTR), value :: ptr + real(C_DOUBLE) :: q_value + end function + + function reaction_scatter_in_cm(ptr) result(b) bind(C) + import C_PTR, C_BOOL + type(C_PTR), value :: ptr + logical(C_BOOL) :: b + end function + + pure function reaction_product_decay_rate(ptr, product) result(rate) bind(C) + import C_PTR, C_INT, C_DOUBLE + type(C_PTR), value :: ptr + integer(C_INT), value :: product + real(C_DOUBLE) :: rate + end function + + pure function reaction_product_emission_mode(ptr, product) result(m) bind(C) + import C_PTR, C_INT + type(C_PTR), value :: ptr + integer(C_INT), value :: product + integer(C_INT) :: m + end function + + pure function reaction_product_particle(ptr, product) result(particle) bind(C) + import C_PTR, C_INT + type(C_PTR), value :: ptr + integer(C_INT), value :: product + integer(C_INT) :: particle + end function + + subroutine reaction_product_sample(ptr, product, E_in, E_out, mu) bind(C) + import C_PTR, C_INT, C_DOUBLE + type(C_PTR), value :: ptr + integer(C_INT), value :: product + real(C_DOUBLE), value :: E_in + real(C_DOUBLE), intent(out) :: E_out + real(C_DOUBLE), intent(out) :: mu + end subroutine + + pure function reaction_product_yield(ptr, product, E) result(val) bind(C) + import C_PTR, C_INT, C_DOUBLE + type(C_PTR), value :: ptr + integer(C_INT), value :: product + real(C_DOUBLE), value :: E + real(C_DOUBLE) :: val + end function + + pure function reaction_products_size(ptr) result(sz) bind(C) + import C_PTR, C_INT + type(C_PTR), value :: ptr + integer(C_INT) :: sz + end function + + function reaction_sample_elastic_mu(ptr, E) result(mu) bind(C) + import C_PTR, C_INT, C_DOUBLE + type(C_PTR), value :: ptr + real(C_DOUBLE), value :: E + real(C_DOUBLE) :: mu + end function + + function reaction_xs(ptr, temperature, energy) result(xs) bind(C) + import C_PTR, C_INT, C_DOUBLE + type(C_PTR), value :: ptr + integer(C_INT), value :: temperature + integer(C_INT), value :: energy + real(C_DOUBLE) :: xs + end function + + function reaction_xs_size(ptr, temperature) result(sz) bind(C) + import C_PTR, C_INT + type(C_PTR), value :: ptr + integer(C_INT), value :: temperature + integer(C_INT) :: sz + end function + + function reaction_xs_threshold(ptr, temperature) result(threshold) bind(C) + import C_PTR, C_INT + type(C_PTR), value :: ptr + integer(C_INT), value :: temperature + integer(C_INT) :: threshold + end function + end interface + contains - subroutine reaction_from_hdf5(this, group_id, temperatures) + subroutine from_hdf5(this, group_id, temperatures) class(Reaction), intent(inout) :: this integer(HID_T), intent(in) :: group_id type(VectorInt), intent(in) :: temperatures - integer :: i - integer :: cm - integer :: n_product - integer(HID_T) :: pgroup - integer(HID_T) :: xs, temp_group - integer(HSIZE_T) :: dims(1) - integer(HSIZE_T) :: j - character(MAX_WORD_LEN) :: temp_str ! temperature dataset name, e.g. '294K' - character(MAX_WORD_LEN), allocatable :: grp_names(:) + integer(C_INT) :: dummy + integer(C_INT) :: n - call read_attribute(this % Q_value, group_id, 'Q_value') - call read_attribute(this % MT, group_id, 'mt') - call read_attribute(cm, group_id, 'center_of_mass') - this % scatter_in_cm = (cm == 1) + n = temperatures % size() + if (n > 0) then + this % ptr = reaction_from_hdf5(group_id, temperatures % data(1), n) + else + ! In this case, temperatures % data(1) doesn't exist, so we just pass a + ! dummy value + this % ptr = reaction_from_hdf5(group_id, dummy, n) + end if + this % MT = reaction_mt(this % ptr) + this % Q_value = reaction_q_value(this % ptr) + this % scatter_in_cm = reaction_scatter_in_cm(this % ptr) + end subroutine from_hdf5 - ! Read cross section and threshold_idx data - allocate(this % xs(temperatures % size())) - do i = 1, temperatures % size() - temp_str = trim(to_str(temperatures % data(i))) // "K" - temp_group = open_group(group_id, temp_str) - xs = open_dataset(temp_group, 'xs') - call read_attribute(this % xs(i) % threshold, xs, 'threshold_idx') - call get_shape(xs, dims) - allocate(this % xs(i) % value(dims(1))) - call read_dataset(this % xs(i) % value, xs) - call close_dataset(xs) - call close_group(temp_group) - end do + function mt_(this) result(mt) + class(Reaction), intent(in) :: this + integer(C_INT) :: MT - ! Determine number of products - n_product = 0 - call get_groups(group_id, grp_names) - do j = 1, size(grp_names) - if (starts_with(grp_names(j), "product_")) n_product = n_product + 1 - end do + mt = reaction_mt(this % ptr) + end function - ! Read products - allocate(this % products(n_product)) - do i = 1, n_product - pgroup = open_group(group_id, 'product_' // trim(to_str(i - 1))) - call this % products(i) % from_hdf5(pgroup) - call close_group(pgroup) - end do - end subroutine reaction_from_hdf5 + function q_value_(this) result(q_value) + class(Reaction), intent(in) :: this + real(C_DOUBLE) :: q_value + + q_value = reaction_q_value(this % ptr) + end function + + function scatter_in_cm_(this) result(cm) + class (Reaction), intent(in) :: this + logical(C_BOOL) :: cm + + cm = reaction_scatter_in_cm(this % ptr) + end function + + pure function product_decay_rate(this, product) result(rate) + class(Reaction), intent(in) :: this + integer(C_INT), intent(in) :: product + real(C_DOUBLE) :: rate + + rate = reaction_product_decay_rate(this % ptr, product) + end function + + pure function product_emission_mode(this, product) result(m) + class(Reaction), intent(in) :: this + integer(C_INT), intent(in) :: product + integer(C_INT) :: m + + m = reaction_product_emission_mode(this % ptr, product) + end function + + pure function product_particle(this, product) result(p) + class(Reaction), intent(in) :: this + integer(C_INT), intent(in) :: product + integer(C_INT) :: p + + p = reaction_product_particle(this % ptr, product) + end function + + subroutine product_sample(this, product, E_in, E_out, mu) + class(Reaction), intent(in) :: this + integer(C_INT), intent(in) :: product + real(C_DOUBLE), intent(in) :: E_in + real(C_DOUBLE), intent(out) :: E_out + real(C_DOUBLE), intent(out) :: mu + + call reaction_product_sample(this % ptr, product, E_in, E_out, mu) + end subroutine + + pure function product_yield(this, product, E) result(val) + class(Reaction), intent(in) :: this + integer(C_INT), intent(in) :: product + real(C_DOUBLE), intent(in) :: E + real(C_DOUBLE) :: val + + val = reaction_product_yield(this % ptr, product, E) + end function + + pure function products_size(this) result(sz) + class(Reaction), intent(in) :: this + integer(C_INT) :: sz + + sz = reaction_products_size(this % ptr) + end function + + function sample_elastic_mu(this, E) result(mu) + class(Reaction), intent(in) :: this + real(C_DOUBLE), intent(in) :: E + real(C_DOUBLE) :: mu + + mu = reaction_sample_elastic_mu(this % ptr, E) + end function + + function xs(this, temperature, energy) result(val) + class(Reaction), intent(in) :: this + integer(C_INT), intent(in) :: temperature + integer(C_INT), intent(in) :: energy + real(C_DOUBLE) :: val + + val = reaction_xs(this % ptr, temperature, energy) + end function + + function xs_size(this, temperature) result(sz) + class(Reaction), intent(in) :: this + integer(C_INT) :: temperature + integer(C_INT) :: sz + + sz = reaction_xs_size(this % ptr, temperature) + end function + + function xs_threshold(this, temperature) result(val) + class(Reaction), intent(in) :: this + integer(C_INT), intent(in) :: temperature + integer(C_INT) :: val + + val = reaction_xs_threshold(this % ptr, temperature) + end function end module reaction_header diff --git a/src/reaction_product.cpp b/src/reaction_product.cpp new file mode 100644 index 0000000000..ef5c746bde --- /dev/null +++ b/src/reaction_product.cpp @@ -0,0 +1,107 @@ +#include "reaction_product.h" + +#include // for unique_ptr +#include // for string + +#include "hdf5_interface.h" +#include "random_lcg.h" +#include "secondary_correlated.h" +#include "secondary_kalbach.h" +#include "secondary_nbody.h" +#include "secondary_uncorrelated.h" + +namespace openmc { + +//============================================================================== +// ReactionProduct implementation +//============================================================================== + +ReactionProduct::ReactionProduct(hid_t group) +{ + // Read particle type + std::string temp; + read_attribute(group, "particle", temp); + if (temp == "neutron") { + particle_ = ParticleType::neutron; + } else if (temp == "photon") { + particle_ = ParticleType::photon; + } + + // Read emission mode and decay rate + read_attribute(group, "emission_mode", temp); + if (temp == "prompt") { + emission_mode_ = EmissionMode::prompt; + } else if (temp == "delayed") { + emission_mode_ = EmissionMode::delayed; + } else if (temp == "total") { + emission_mode_ = EmissionMode::total; + } + + // Read decay rate for delayed emission + if (emission_mode_ == EmissionMode::delayed) + read_attribute(group, "decay_rate", decay_rate_); + + // Read secondary particle yield + hid_t yield = open_dataset(group, "yield"); + read_attribute(yield, "type", temp); + if (temp == "Tabulated1D") { + yield_ = std::unique_ptr{new Tabulated1D{yield}}; + } else if (temp == "Polynomial") { + yield_ = std::unique_ptr{new Polynomial{yield}}; + } + close_dataset(yield); + + int n; + read_attribute(group, "n_distribution", n); + + for (int i = 0; i < n; ++i) { + std::string s {"distribution_"}; + s.append(std::to_string(i)); + hid_t dgroup = open_group(group, s.c_str()); + + // Read applicability + if (n > 1) { + hid_t app = open_dataset(dgroup, "applicability"); + applicability_.emplace_back(app); + close_dataset(app); + } + + // Determine distribution type and read data + read_attribute(dgroup, "type", temp); + if (temp == "uncorrelated") { + distribution_.emplace_back(new UncorrelatedAngleEnergy{dgroup}); + } else if (temp == "correlated") { + distribution_.emplace_back(new CorrelatedAngleEnergy{dgroup}); + } else if (temp == "nbody") { + distribution_.emplace_back(new NBodyPhaseSpace{dgroup}); + } else if (temp == "kalbach-mann") { + distribution_.emplace_back(new KalbachMann{dgroup}); + } + + close_group(dgroup); + } +} + +void ReactionProduct::sample(double E_in, double& E_out, double& mu) const +{ + auto n = applicability_.size(); + if (n > 1) { + double prob = 0.0; + double c = prn(); + for (int i = 0; i < n; ++i) { + // Determine probability that i-th energy distribution is sampled + prob += applicability_[i](E_in); + + // If i-th distribution is sampled, sample energy from the distribution + if (c <= prob) { + distribution_[i]->sample(E_in, E_out, mu); + break; + } + } + } else { + // If only one distribution is present, go ahead and sample it + distribution_[0]->sample(E_in, E_out, mu); + } +} + +} diff --git a/src/reaction_product.h b/src/reaction_product.h new file mode 100644 index 0000000000..ee8b5cea5f --- /dev/null +++ b/src/reaction_product.h @@ -0,0 +1,56 @@ +//! \file reaction_product.h +//! Data for a reaction product + +#ifndef OPENMC_REACTION_PRODUCT_H +#define OPENMC_REACTION_PRODUCT_H + +#include // for unique_ptr +#include // for vector + +#include "hdf5.h" +#include "angle_energy.h" +#include "endf.h" +#include "particle.h" + +namespace openmc { + +//============================================================================== +//! Data for a reaction product including its yield and angle-energy +//! distributions, each of which has a given probability of occurring for a +//! given incoming energy. In general, most products only have one angle-energy +//! distribution, but for some cases (e.g., (n,2n) in certain nuclides) multiple +//! distinct distributions exist. +//============================================================================== + +class ReactionProduct { +public: + //! Emission mode for product + enum class EmissionMode { + prompt, // Prompt emission of secondary particle + total, // Delayed emission of secondary particle + delayed // Yield represents total emission (prompt + delayed) + }; + + using Secondary = std::unique_ptr; + + //! Construct reaction product from HDF5 data + //! \param[in] group HDF5 group containing data + explicit ReactionProduct(hid_t group); + + //! Sample an outgoing angle and energy + //! \param[in] E_in Incoming energy in [eV] + //! \param[out] E_out Outgoing energy in [eV] + //! \param[out] mu Outgoing cosine with respect to current direction + void sample(double E_in, double& E_out, double& mu) const; + + ParticleType particle_; //!< Particle type + EmissionMode emission_mode_; //!< Emission mode + double decay_rate_; //!< Decay rate (for delayed neutron precursors) in [1/s] + std::unique_ptr yield_; //!< Yield as a function of energy + std::vector applicability_; //!< Applicability of distribution + std::vector distribution_; //!< Secondary angle-energy distribution +}; + +} // namespace opemc + +#endif // OPENMC_REACTION_PRODUCT_H diff --git a/src/scattdata.h b/src/scattdata.h index 72ebaf6771..efa7accb41 100644 --- a/src/scattdata.h +++ b/src/scattdata.h @@ -18,6 +18,8 @@ class ScattDataTabular; //============================================================================== class ScattData { + public: + virtual ~ScattData() = default; protected: //! \brief Initializes the attributes of the base class. void diff --git a/src/search.h b/src/search.h new file mode 100644 index 0000000000..81370a3de0 --- /dev/null +++ b/src/search.h @@ -0,0 +1,23 @@ +//! \file search.h +//! Search algorithms + +#ifndef OPENMC_SEARCH_H +#define OPENMC_SEARCH_H + +#include // for lower_bound + +namespace openmc { + +//! Perform binary search + +template +typename std::iterator_traits::difference_type +lower_bound_index(It first, It last, const T& value) +{ + It index = std::lower_bound(first, last, value) - 1; + return (index == last) ? -1 : index - first; +} + +} // namespace openmc + +#endif // OPENMC_SEARCH_H diff --git a/src/secondary_correlated.cpp b/src/secondary_correlated.cpp new file mode 100644 index 0000000000..b1653f3170 --- /dev/null +++ b/src/secondary_correlated.cpp @@ -0,0 +1,244 @@ +#include "secondary_correlated.h" + +#include // for copy +#include +#include // for size_t +#include // for back_inserter + +#include "hdf5_interface.h" +#include "xtensor/xarray.hpp" +#include "xtensor/xview.hpp" +#include "endf.h" +#include "random_lcg.h" +#include "search.h" + +namespace openmc { + +//============================================================================== +//! CorrelatedAngleEnergy implementation +//============================================================================== + +CorrelatedAngleEnergy::CorrelatedAngleEnergy(hid_t group) +{ + // Open incoming energy dataset + hid_t dset = open_dataset(group, "energy"); + + // Get interpolation parameters + xt::xarray 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 + + std::copy(temp_b.begin(), temp_b.end(), std::back_inserter(breakpoints_)); + for (const auto i : temp_i) + interpolation_.push_back(int2interp(i)); + n_region_ = breakpoints_.size(); + + // Get incoming energies + read_dataset(dset, energy_); + std::size_t n_energy = energy_.size(); + close_dataset(dset); + + // Get outgoing energy distribution data + dset = open_dataset(group, "energy_out"); + std::vector offsets; + std::vector interp; + std::vector n_discrete; + read_attribute(dset, "offsets", offsets); + read_attribute(dset, "interpolation", interp); + read_attribute(dset, "n_discrete_lines", n_discrete); + + xt::xarray eout; + read_dataset(dset, eout); + close_dataset(dset); + + // Read angle distributions + xt::xarray mu; + read_dataset(group, "mu", mu); + + for (int i = 0; i < n_energy; ++i) { + // Determine number of outgoing energies + int j = offsets[i]; + int n; + if (i < n_energy - 1) { + n = offsets[i+1] - j; + } else { + n = eout.shape()[1] - j; + } + + // Assign interpolation scheme and number of discrete lines + CorrTable d; + d.interpolation = int2interp(interp[i]); + 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)); + + // 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 (false) { + // Calculate cumulative distribution function -- discrete portion + for (int k = 0; k < d.n_discrete; ++k) { + if (k == 0) { + d.c[k] = d.p[k]; + } else { + d.c[k] = d.c[k-1] + d.p[k]; + } + } + + // Continuous portion + for (int k = d.n_discrete; k < n; ++k) { + if (k == d.n_discrete) { + d.c[k] = d.c[k-1] + d.p[k]; + } else { + if (d.interpolation == Interpolation::histogram) { + d.c[k] = d.c[k-1] + d.p[k-1]*(d.e_out[k] - d.e_out[k-1]); + } else if (d.interpolation == Interpolation::lin_lin) { + d.c[k] = d.c[k-1] + 0.5*(d.p[k-1] + d.p[k]) * + (d.e_out[k] - d.e_out[k-1]); + } + } + } + + // Normalize density and distribution functions + d.p /= d.c[n - 1]; + d.c /= d.c[n - 1]; + } + + for (j = 0; j < n; ++j) { + // Get interpolation scheme + int interp_mu = std::lround(eout(3, offsets[i] + j)); + + // 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]) { + m = std::lround(eout(4, offsets[i]+j+1)) - offset_mu; + } else { + m = mu.shape()[1] - offset_mu; + } + + 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)); + + std::vector x {xs.begin(), xs.end()}; + std::vector p {ps.begin(), ps.end()}; + std::vector c {cs.begin(), cs.end()}; + + // 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 + Tabular* mudist = new Tabular{x.data(), p.data(), m, interp, c.data()}; + + d.angle.emplace_back(mudist); + } // outgoing energies + + distribution_.push_back(std::move(d)); + } // incoming energies +} + +void CorrelatedAngleEnergy::sample(double E_in, double& E_out, double& mu) const +{ + // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<<<<<< + // Before the secondary distribution refactor, an isotropic polar cosine was + // always sampled but then overwritten with the polar cosine sampled from the + // correlated distribution. To preserve the random number stream, we keep + // this dummy sampling here but can remove it later (will change answers) + mu = 2.0*prn() - 1.0; + // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Find energy bin and calculate interpolation factor -- if the energy is + // outside the range of the tabulated energies, choose the first or last bins + auto n_energy_in = energy_.size(); + int i; + double r; + if (E_in < energy_[0]) { + i = 0; + r = 0.0; + } else if (E_in > energy_[n_energy_in - 1]) { + i = n_energy_in - 2; + r = 1.0; + } else { + i = lower_bound_index(energy_.begin(), energy_.end(), E_in); + r = (E_in - energy_[i]) / (energy_[i+1] - energy_[i]); + } + + // Sample between the ith and [i+1]th bin + int l = r > prn() ? i + 1 : i; + + // Interpolation for energy E1 and EK + int n_energy_out = distribution_[i].e_out.size(); + double E_i_1 = distribution_[i].e_out[0]; + double E_i_K = distribution_[i].e_out[n_energy_out - 1]; + + n_energy_out = distribution_[i+1].e_out.size(); + double E_i1_1 = distribution_[i+1].e_out[0]; + double E_i1_K = distribution_[i+1].e_out[n_energy_out - 1]; + + double E_1 = E_i_1 + r*(E_i1_1 - E_i_1); + double E_K = E_i_K + r*(E_i1_K - E_i_K); + + // Determine outgoing energy bin + n_energy_out = distribution_[l].e_out.size(); + double r1 = prn(); + double c_k = distribution_[l].c[0]; + double c_k1; + int k; + for (k = 0; k < n_energy_out - 2; ++k) { + c_k1 = distribution_[l].c[k+1]; + if (r1 < c_k1) break; + c_k = c_k1; + } + + // Check to make sure 1 <= k <= NP - 1 + k = std::max(0, std::min(k, n_energy_out - 2)); + + double E_l_k = distribution_[l].e_out[k]; + double p_l_k = distribution_[l].p[k]; + if (distribution_[l].interpolation == Interpolation::histogram) { + // Histogram interpolation + if (p_l_k > 0.0) { + E_out = E_l_k + (r1 - c_k)/p_l_k; + } else { + E_out = E_l_k; + } + + } else if (distribution_[l].interpolation == Interpolation::lin_lin) { + // Linear-linear interpolation + double E_l_k1 = distribution_[l].e_out[k+1]; + double p_l_k1 = distribution_[l].p[k+1]; + + double frac = (p_l_k1 - p_l_k)/(E_l_k1 - E_l_k); + if (frac == 0.0) { + E_out = E_l_k + (r1 - c_k)/p_l_k; + } else { + E_out = E_l_k + (std::sqrt(std::max(0.0, p_l_k*p_l_k + + 2.0*frac*(r1 - c_k))) - p_l_k)/frac; + } + + } + + // Now interpolate between incident energy bins i and i + 1 + if (l == i) { + E_out = E_1 + (E_out - E_i_1)*(E_K - E_1)/(E_i_K - E_i_1); + } else { + E_out = E_1 + (E_out - E_i1_1)*(E_K - E_1)/(E_i1_K - E_i1_1); + } + + // Find correlated angular distribution for closest outgoing energy bin + if (r1 - c_k < c_k1 - r1) { + mu = distribution_[l].angle[k]->sample(); + } else { + mu = distribution_[l].angle[k + 1]->sample(); + } +} + +} // namespace openmc diff --git a/src/secondary_correlated.h b/src/secondary_correlated.h new file mode 100644 index 0000000000..3b6aae9c19 --- /dev/null +++ b/src/secondary_correlated.h @@ -0,0 +1,52 @@ +//! \file secondary_correlated.h +//! Correlated angle-energy distribution + +#ifndef OPENMC_SECONDARY_CORRELATED_H +#define OPENMC_SECONDARY_CORRELATED_H + +#include + +#include "hdf5.h" +#include "xtensor/xtensor.hpp" +#include "angle_energy.h" +#include "endf.h" +#include "distribution.h" + +namespace openmc { + +//============================================================================== +//! Correlated angle-energy distribution corresponding to ACE law 61 and ENDF +//! File 6, LAW=1, LANG!=2. +//============================================================================== + +class CorrelatedAngleEnergy : public AngleEnergy { +public: + explicit CorrelatedAngleEnergy(hid_t group); + + //! Sample distribution for an angle and energy + //! \param[in] E_in Incoming energy in [eV] + //! \param[out] E_out Outgoing energy in [eV] + //! \param[out] mu Outgoing cosine with respect to current direction + void sample(double E_in, double& E_out, double& mu) const; +private: + //! Outgoing energy/angle at a single incoming energy + struct CorrTable { + int n_discrete; //!< Number of discrete lines + Interpolation interpolation; //!< Interpolation law + xt::xtensor e_out; //!< Outgoing energies [eV] + xt::xtensor p; //!< Probability density + xt::xtensor c; //!< Cumulative distribution + std::vector angle; //!< Angle distribution + }; + + int n_region_; //!< Number of interpolation regions + std::vector breakpoints_; //!< Breakpoints between regions + std::vector interpolation_; //!< Interpolation laws + std::vector energy_; //!< Energies [eV] at which distributions + //!< are tabulated + std::vector distribution_; //!< Distribution at each energy +}; + +} // namespace openmc + +#endif // OPENMC_SECONDARY_CORRELATED_H diff --git a/src/secondary_kalbach.F90 b/src/secondary_kalbach.F90 deleted file mode 100644 index 3315174e1d..0000000000 --- a/src/secondary_kalbach.F90 +++ /dev/null @@ -1,278 +0,0 @@ -module secondary_kalbach - - use algorithm, only: binary_search - use angleenergy_header, only: AngleEnergy - use constants, only: ZERO, HALF, ONE, TWO, HISTOGRAM, LINEAR_LINEAR - use hdf5_interface - use random_lcg, only: prn - -!=============================================================================== -! KalbachMann represents a correlated angle-energy distribution with the angular -! distribution represented using Kalbach-Mann systematics. This corresponds to -! ACE law 44 and ENDF File 6, LAW=1, LANG=2. -!=============================================================================== - - type KalbachMannTable - integer :: n_discrete - integer :: interpolation - real(8), allocatable :: e_out(:) - real(8), allocatable :: p(:) - real(8), allocatable :: c(:) - real(8), allocatable :: r(:) - real(8), allocatable :: a(:) - end type KalbachMannTable - - type, extends(AngleEnergy) :: KalbachMann - integer :: n_region ! number of interpolation regions - integer, allocatable :: breakpoints(:) ! breakpoints of interpolation regions - integer, allocatable :: interpolation(:) ! interpolation region codes - real(8), allocatable :: energy(:) ! incoming energies - type(KalbachMannTable), allocatable :: distribution(:) ! outgoing E/mu parameters - contains - procedure :: sample => kalbachmann_sample - procedure :: from_hdf5 => kalbachmann_from_hdf5 - end type KalbachMann - -contains - - subroutine kalbachmann_sample(this, E_in, E_out, mu) - class(KalbachMann), intent(in) :: this - real(8), intent(in) :: E_in ! incoming energy - real(8), intent(out) :: E_out ! sampled outgoing energy - real(8), intent(out) :: mu ! sampled scattering cosine - - integer :: i, k, l ! indices - integer :: n_energy_in ! number of incoming energies - integer :: n_energy_out ! number of outgoing energies - real(8) :: r ! interpolation factor on incoming energy - real(8) :: r1 ! random number on [0,1) - real(8) :: frac ! interpolation factor on outgoing energy - real(8) :: E_i_1, E_i_K ! endpoints on outgoing grid i - real(8) :: E_i1_1, E_i1_K ! endpoints on outgoing grid i+1 - real(8) :: E_1, E_K ! endpoints interpolated between i and i+1 - real(8) :: E_l_k, E_l_k1 ! adjacent E on outgoing grid l - real(8) :: p_l_k, p_l_k1 ! adjacent p on outgoing grid l - real(8) :: c_k, c_k1 ! cumulative probability - real(8) :: km_r, km_a ! Kalbach-Mann parameters - real(8) :: T - - ! <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<<<<<< - ! Before the secondary distribution refactor, an isotropic polar cosine was - ! always sampled but then overwritten with the polar cosine sampled from the - ! correlated distribution. To preserve the random number stream, we keep - ! this dummy sampling here but can remove it later (will change answers) - mu = TWO*prn() - ONE - ! <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<<<<<< - - ! find energy bin and calculate interpolation factor -- if the energy is - ! outside the range of the tabulated energies, choose the first or last bins - n_energy_in = size(this%energy) - if (E_in < this%energy(1)) then - i = 1 - r = ZERO - elseif (E_in > this%energy(n_energy_in)) then - i = n_energy_in - 1 - r = ONE - else - i = binary_search(this%energy, n_energy_in, E_in) - r = (E_in - this%energy(i)) / & - (this%energy(i+1) - this%energy(i)) - end if - - ! Sample between the ith and (i+1)th bin - if (r > prn()) then - l = i + 1 - else - l = i - end if - - ! interpolation for energy E1 and EK - n_energy_out = size(this%distribution(i)%e_out) - E_i_1 = this%distribution(i)%e_out(1) - E_i_K = this%distribution(i)%e_out(n_energy_out) - - n_energy_out = size(this%distribution(i+1)%e_out) - E_i1_1 = this%distribution(i+1)%e_out(1) - E_i1_K = this%distribution(i+1)%e_out(n_energy_out) - - E_1 = E_i_1 + r*(E_i1_1 - E_i_1) - E_K = E_i_K + r*(E_i1_K - E_i_K) - - ! determine outgoing energy bin - n_energy_out = size(this%distribution(l)%e_out) - r1 = prn() - c_k = this%distribution(l)%c(1) - do k = 1, n_energy_out - 1 - c_k1 = this%distribution(l)%c(k+1) - if (r1 < c_k1) exit - c_k = c_k1 - end do - - ! check to make sure k is <= NP - 1 - k = min(k, n_energy_out - 1) - - E_l_k = this%distribution(l)%e_out(k) - p_l_k = this%distribution(l)%p(k) - if (this%distribution(l)%interpolation == HISTOGRAM) then - ! Histogram interpolation - if (p_l_k > ZERO) then - E_out = E_l_k + (r1 - c_k)/p_l_k - else - E_out = E_l_k - end if - - ! Determine Kalbach-Mann parameters - km_r = this%distribution(l)%r(k) - km_a = this%distribution(l)%a(k) - - elseif (this%distribution(l)%interpolation == LINEAR_LINEAR) then - ! Linear-linear interpolation - E_l_k1 = this%distribution(l)%e_out(k+1) - p_l_k1 = this%distribution(l)%p(k+1) - - frac = (p_l_k1 - p_l_k)/(E_l_k1 - E_l_k) - if (frac == ZERO) then - E_out = E_l_k + (r1 - c_k)/p_l_k - else - E_out = E_l_k + (sqrt(max(ZERO, p_l_k*p_l_k + & - TWO*frac*(r1 - c_k))) - p_l_k)/frac - end if - - ! Determine Kalbach-Mann parameters - km_r = this%distribution(l)%r(k) + (E_out - E_l_k)/(E_l_k1 - E_l_k) * & - (this%distribution(l)%r(k+1) - this%distribution(l)%r(k)) - km_a = this%distribution(l)%a(k) + (E_out - E_l_k)/(E_l_k1 - E_l_k) * & - (this%distribution(l)%a(k+1) - this%distribution(l)%a(k)) - end if - - ! Now interpolate between incident energy bins i and i + 1 - if (l == i) then - E_out = E_1 + (E_out - E_i_1)*(E_K - E_1)/(E_i_K - E_i_1) - else - E_out = E_1 + (E_out - E_i1_1)*(E_K - E_1)/(E_i1_K - E_i1_1) - end if - - ! Sampled correlated angle from Kalbach-Mann parameters - if (prn() > km_r) then - T = (TWO*prn() - ONE) * sinh(km_a) - mu = log(T + sqrt(T*T + ONE))/km_a - else - r1 = prn() - mu = log(r1*exp(km_a) + (ONE - r1)*exp(-km_a))/km_a - end if - - end subroutine kalbachmann_sample - - subroutine kalbachmann_from_hdf5(this, group_id) - class(KalbachMann), intent(inout) :: this - integer(HID_T), intent(in) :: group_id - - integer :: i, j, k - integer :: n - integer :: n_energy - integer(HID_T) :: dset_id - integer(HSIZE_T) :: dims(1), dims2(2) - integer, allocatable :: temp(:,:) - integer, allocatable :: offsets(:) - integer, allocatable :: interp(:) - integer, allocatable :: n_discrete(:) - real(8), allocatable :: eout(:,:) - - ! Open incoming energy dataset - dset_id = open_dataset(group_id, 'energy') - - ! Get interpolation parameters - call read_attribute(temp, dset_id, 'interpolation') - allocate(this%breakpoints(size(temp, 1))) - allocate(this%interpolation(size(temp, 1))) - this%breakpoints(:) = temp(:, 1) - this%interpolation(:) = temp(:, 2) - this%n_region = size(this%breakpoints) - - ! Get incoming energies - call get_shape(dset_id, dims) - n_energy = int(dims(1), 4) - allocate(this%energy(n_energy)) - allocate(this%distribution(n_energy)) - call read_dataset(this%energy, dset_id) - call close_dataset(dset_id) - - ! Get outgoing energy distribution data - dset_id = open_dataset(group_id, 'distribution') - call read_attribute(offsets, dset_id, 'offsets') - call read_attribute(interp, dset_id, 'interpolation') - call read_attribute(n_discrete, dset_id, 'n_discrete_lines') - call get_shape(dset_id, dims2) - allocate(eout(dims2(1), dims2(2))) - call read_dataset(eout, dset_id) - call close_dataset(dset_id) - - do i = 1, n_energy - ! Determine number of outgoing energies - j = offsets(i) - if (i < n_energy) then - n = offsets(i+1) - j - else - n = size(eout, 1) - j - end if - - associate (d => this%distribution(i)) - ! Assign interpolation scheme and number of discrete lines - d % interpolation = interp(i) - d % n_discrete = n_discrete(i) - - ! Allocate arrays for energies and PDF/CDF - allocate(d % e_out(n)) - allocate(d % p(n)) - allocate(d % c(n)) - allocate(d % r(n)) - allocate(d % a(n)) - - ! Copy data - d % e_out(:) = eout(j+1:j+n, 1) - d % p(:) = eout(j+1:j+n, 2) - d % c(:) = eout(j+1:j+n, 3) - d % r(:) = eout(j+1:j+n, 4) - d % a(:) = eout(j+1:j+n, 5) - - - ! 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 (.false.) then - ! Calculate cumulative distribution function -- discrete portion - do k = 1, d % n_discrete - if (k == 1) then - d % c(k) = d % p(k) - else - d % c(k) = d % c(k-1) + d % p(k) - end if - end do - - ! Continuous portion - do k = d % n_discrete + 1, n - if (k == d % n_discrete + 1) then - d % c(k) = sum(d % p(1:d % n_discrete)) - else - if (d % interpolation == HISTOGRAM) then - d % c(k) = d % c(k-1) + d % p(k-1) * & - (d % e_out(k) - d % e_out(k-1)) - elseif (d % interpolation == LINEAR_LINEAR) then - d % c(k) = d % c(k-1) + HALF*(d % p(k-1) + d % p(k)) * & - (d % e_out(k) - d % e_out(k-1)) - end if - end if - end do - - ! Normalize density and distribution functions - d % p(:) = d % p(:)/d % c(n) - d % c(:) = d % c(:)/d % c(n) - end if - end associate - - j = j + n - end do - end subroutine kalbachmann_from_hdf5 - -end module secondary_kalbach diff --git a/src/secondary_kalbach.cpp b/src/secondary_kalbach.cpp new file mode 100644 index 0000000000..e8941ff93d --- /dev/null +++ b/src/secondary_kalbach.cpp @@ -0,0 +1,223 @@ +#include "secondary_kalbach.h" + +#include // for copy, move +#include // for log, sqrt, sinh +#include // for size_t +#include // for back_inserter +#include + +#include "xtensor/xarray.hpp" +#include "xtensor/xview.hpp" +#include "hdf5_interface.h" +#include "random_lcg.h" +#include "search.h" + +namespace openmc { + +//============================================================================== +//! KalbachMann implementation +//============================================================================== + +KalbachMann::KalbachMann(hid_t group) +{ + // Open incoming energy dataset + hid_t dset = open_dataset(group, "energy"); + + // Get interpolation parameters + xt::xarray 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 + + std::copy(temp_b.begin(), temp_b.end(), std::back_inserter(breakpoints_)); + for (const auto i : temp_i) + interpolation_.push_back(int2interp(i)); + n_region_ = breakpoints_.size(); + + // Get incoming energies + read_dataset(dset, energy_); + std::size_t n_energy = energy_.size(); + close_dataset(dset); + + // Get outgoing energy distribution data + dset = open_dataset(group, "distribution"); + std::vector offsets; + std::vector interp; + std::vector n_discrete; + read_attribute(dset, "offsets", offsets); + read_attribute(dset, "interpolation", interp); + read_attribute(dset, "n_discrete_lines", n_discrete); + + xt::xarray eout; + read_dataset(dset, eout); + close_dataset(dset); + + for (int i = 0; i < n_energy; ++i) { + // Determine number of outgoing energies + int j = offsets[i]; + int n; + if (i < n_energy - 1) { + n = offsets[i+1] - j; + } else { + n = eout.shape()[1] - j; + } + + // Assign interpolation scheme and number of discrete lines + KMTable d; + d.interpolation = int2interp(interp[i]); + 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)); + + // 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 (false) { + // Calculate cumulative distribution function -- discrete portion + for (int k = 0; k < d.n_discrete; ++k) { + if (k == 0) { + d.c[k] = d.p[k]; + } else { + d.c[k] = d.c[k-1] + d.p[k]; + } + } + + // Continuous portion + for (int k = d.n_discrete; k < n; ++k) { + if (k == d.n_discrete) { + d.c[k] = d.c[k-1] + d.p[k]; + } else { + if (d.interpolation == Interpolation::histogram) { + d.c[k] = d.c[k-1] + d.p[k-1]*(d.e_out[k] - d.e_out[k-1]); + } else if (d.interpolation == Interpolation::lin_lin) { + d.c[k] = d.c[k-1] + 0.5*(d.p[k-1] + d.p[k]) * + (d.e_out[k] - d.e_out[k-1]); + } + } + } + + // Normalize density and distribution functions + d.p /= d.c[n - 1]; + d.c /= d.c[n - 1]; + } + + distribution_.push_back(std::move(d)); + } // incoming energies +} + +void KalbachMann::sample(double E_in, double& E_out, double& mu) const +{ + // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<<<<<< + // Before the secondary distribution refactor, an isotropic polar cosine was + // always sampled but then overwritten with the polar cosine sampled from the + // correlated distribution. To preserve the random number stream, we keep + // this dummy sampling here but can remove it later (will change answers) + mu = 2.0*prn() - 1.0; + // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Find energy bin and calculate interpolation factor -- if the energy is + // outside the range of the tabulated energies, choose the first or last bins + auto n_energy_in = energy_.size(); + int i; + double r; + if (E_in < energy_[0]) { + i = 0; + r = 0.0; + } else if (E_in > energy_[n_energy_in - 1]) { + i = n_energy_in - 2; + r = 1.0; + } else { + i = lower_bound_index(energy_.begin(), energy_.end(), E_in); + r = (E_in - energy_[i]) / (energy_[i+1] - energy_[i]); + } + + // Sample between the ith and [i+1]th bin + int l = r > prn() ? i + 1 : i; + + // Interpolation for energy E1 and EK + int n_energy_out = distribution_[i].e_out.size(); + double E_i_1 = distribution_[i].e_out[0]; + double E_i_K = distribution_[i].e_out[n_energy_out - 1]; + + n_energy_out = distribution_[i+1].e_out.size(); + double E_i1_1 = distribution_[i+1].e_out[0]; + double E_i1_K = distribution_[i+1].e_out[n_energy_out - 1]; + + double E_1 = E_i_1 + r*(E_i1_1 - E_i_1); + double E_K = E_i_K + r*(E_i1_K - E_i_K); + + // Determine outgoing energy bin + n_energy_out = distribution_[l].e_out.size(); + double r1 = prn(); + double c_k = distribution_[l].c[0]; + double c_k1; + int k; + for (k = 0; k < n_energy_out - 2; ++k) { + c_k1 = distribution_[l].c[k+1]; + if (r1 < c_k1) break; + c_k = c_k1; + } + + // Check to make sure 1 <= k <= NP - 1 + k = std::max(0, std::min(k, n_energy_out - 2)); + + double E_l_k = distribution_[l].e_out[k]; + double p_l_k = distribution_[l].p[k]; + double km_r, km_a; + if (distribution_[l].interpolation == Interpolation::histogram) { + // Histogram interpolation + if (p_l_k > 0.0) { + E_out = E_l_k + (r1 - c_k)/p_l_k; + } else { + E_out = E_l_k; + } + + // Determine Kalbach-Mann parameters + km_r = distribution_[l].r[k]; + km_a = distribution_[l].a[k]; + + } else if (distribution_[l].interpolation == Interpolation::lin_lin) { + // Linear-linear interpolation + double E_l_k1 = distribution_[l].e_out[k+1]; + double p_l_k1 = distribution_[l].p[k+1]; + + double frac = (p_l_k1 - p_l_k)/(E_l_k1 - E_l_k); + if (frac == 0.0) { + E_out = E_l_k + (r1 - c_k)/p_l_k; + } else { + E_out = E_l_k + (std::sqrt(std::max(0.0, p_l_k*p_l_k + + 2.0*frac*(r1 - c_k))) - p_l_k)/frac; + } + + // Determine Kalbach-Mann parameters + km_r = distribution_[l].r[k] + (E_out - E_l_k)/(E_l_k1 - E_l_k) * + (distribution_[l].r[k+1] - distribution_[l].r[k]); + km_a = distribution_[l].a[k] + (E_out - E_l_k)/(E_l_k1 - E_l_k) * + (distribution_[l].a[k+1] - distribution_[l].a[k]); + } + + // Now interpolate between incident energy bins i and i + 1 + if (l == i) { + E_out = E_1 + (E_out - E_i_1)*(E_K - E_1)/(E_i_K - E_i_1); + } else { + E_out = E_1 + (E_out - E_i1_1)*(E_K - E_1)/(E_i1_K - E_i1_1); + } + + // Sampled correlated angle from Kalbach-Mann parameters + if (prn() > km_r) { + double T = (2.0*prn() - 1.0) * std::sinh(km_a); + mu = std::log(T + std::sqrt(T*T + 1.0))/km_a; + } else { + double r1 = prn(); + mu = std::log(r1*std::exp(km_a) + (1.0 - r1)*std::exp(-km_a))/km_a; + } +} + +} diff --git a/src/secondary_kalbach.h b/src/secondary_kalbach.h new file mode 100644 index 0000000000..fa4c43694a --- /dev/null +++ b/src/secondary_kalbach.h @@ -0,0 +1,54 @@ +//! \file secondary_kalbach.h +//! Kalbach-Mann angle-energy distribution + +#ifndef OPENMC_SECONDARY_KALBACH_H +#define OPENMC_SECONDARY_KALBACH_H + +#include + +#include "hdf5.h" +#include "xtensor/xtensor.hpp" +#include "angle_energy.h" +#include "constants.h" +#include "endf.h" + +namespace openmc { + +//============================================================================== +//! Correlated angle-energy distribution with the angular distribution +//! represented using Kalbach-Mann systematics. This corresponds to ACE law 44 +//! and ENDF File 6, LAW=1, LANG=2. +//============================================================================== + +class KalbachMann : public AngleEnergy { +public: + explicit KalbachMann(hid_t group); + + //! Sample distribution for an angle and energy + //! \param[in] E_in Incoming energy in [eV] + //! \param[out] E_out Outgoing energy in [eV] + //! \param[out] mu Outgoing cosine with respect to current direction + void sample(double E_in, double& E_out, double& mu) const; +private: + //! Outgoing energy/angle at a single incoming energy + struct KMTable { + int n_discrete; //!< Number of discrete lines + Interpolation interpolation; //!< Interpolation law + xt::xtensor e_out; //!< Outgoing energies [eV] + xt::xtensor p; //!< Probability density + xt::xtensor c; //!< Cumulative distribution + xt::xtensor r; //!< Pre-compound fraction + xt::xtensor a; //!< Parameterized function + }; + + int n_region_; //!< Number of interpolation regions + std::vector breakpoints_; //!< Breakpoints between regions + std::vector interpolation_; //!< Interpolation laws + std::vector energy_; //!< Energies [eV] at which distributions + //!< are tabulated + std::vector distribution_; //!< Distribution at each energy +}; + +} // namespace openmc + +#endif // OPENMC_SECONDARY_KALBACH_H diff --git a/src/secondary_nbody.F90 b/src/secondary_nbody.F90 deleted file mode 100644 index aee4b1ff78..0000000000 --- a/src/secondary_nbody.F90 +++ /dev/null @@ -1,82 +0,0 @@ -module secondary_nbody - - use angleenergy_header, only: AngleEnergy - use constants, only: ONE, TWO, PI - use hdf5_interface, only: read_attribute, HID_T - use math, only: maxwell_spectrum - use random_lcg, only: prn - -!=============================================================================== -! NBODYPHASESPACE gives the energy distribution for particles emitted from -! neutron and charged-particle reactions. This corresponds to ACE law 66 and -! ENDF File 6, LAW=6. -!=============================================================================== - - type, extends(AngleEnergy) :: NBodyPhaseSpace - integer :: n_bodies - real(8) :: mass_ratio - real(8) :: A - real(8) :: Q - contains - procedure :: sample => nbody_sample - procedure :: from_hdf5 => nbody_from_hdf5 - end type NBodyPhaseSpace - -contains - - subroutine nbody_sample(this, E_in, E_out, mu) - class(NBodyPhaseSpace), intent(in) :: this - real(8), intent(in) :: E_in ! incoming energy - real(8), intent(out) :: E_out ! sampled outgoing energy - real(8), intent(out) :: mu ! sampled outgoing energy - - real(8) :: Ap ! total mass of particles in neutron masses - real(8) :: E_max ! maximum possible COM energy - real(8) :: x, y, v - real(8) :: r1, r2, r3, r4, r5, r6 - - ! By definition, the distribution of the angle is isotropic for an N-body - ! phase space distribution - mu = TWO*prn() - ONE - - ! Determine E_max parameter - Ap = this%mass_ratio - E_max = (Ap - ONE)/Ap * (this%A/(this%A + ONE)*E_in + this%Q) - - ! x is essentially a Maxwellian distribution - x = maxwell_spectrum(ONE) - - select case (this%n_bodies) - case (3) - y = maxwell_spectrum(ONE) - case (4) - r1 = prn() - r2 = prn() - r3 = prn() - y = -log(r1*r2*r3) - case (5) - r1 = prn() - r2 = prn() - r3 = prn() - r4 = prn() - r5 = prn() - r6 = prn() - y = -log(r1*r2*r3*r4) - log(r5) * cos(PI/TWO*r6)**2 - end select - - ! Now determine v and E_out - v = x/(x+y) - E_out = E_max * v - end subroutine nbody_sample - - subroutine nbody_from_hdf5(this, group_id) - class(NBodyPhaseSpace), intent(inout) :: this - integer(HID_T), intent(in) :: group_id - - call read_attribute(this%mass_ratio, group_id, 'total_mass') - call read_attribute(this%n_bodies, group_id, 'n_particles') - call read_attribute(this%A, group_id, 'atomic_weight_ratio') - call read_attribute(this%Q, group_id, 'q_value') - end subroutine nbody_from_hdf5 - -end module secondary_nbody diff --git a/src/secondary_nbody.cpp b/src/secondary_nbody.cpp new file mode 100644 index 0000000000..44a66c6bd1 --- /dev/null +++ b/src/secondary_nbody.cpp @@ -0,0 +1,65 @@ +#include "secondary_nbody.h" + +#include // for log + +#include "constants.h" +#include "hdf5_interface.h" +#include "math_functions.h" +#include "random_lcg.h" + +namespace openmc { + +//============================================================================== +// NBodyPhaseSpace implementation +//============================================================================== + +NBodyPhaseSpace::NBodyPhaseSpace(hid_t group) +{ + read_attribute(group, "n_particles", n_bodies_); + read_attribute(group, "total_mass", mass_ratio_); + read_attribute(group, "atomic_weight_ratio", A_); + read_attribute(group, "q_value", Q_); +} + +void NBodyPhaseSpace::sample(double E_in, double& E_out, double& mu) const +{ + // By definition, the distribution of the angle is isotropic for an N-body + // phase space distribution + mu = 2.0*prn() - 1.0; + + // Determine E_max parameter + double Ap = mass_ratio_; + double E_max = (Ap - 1.0)/Ap * (A_/(A_ + 1.0)*E_in + Q_); + + // x is essentially a Maxwellian distribution + double x = maxwell_spectrum_c(1.0); + + double y; + double r1, r2, r3, r4, r5, r6; + switch (n_bodies_) { + case 3: + y = maxwell_spectrum_c(1.0); + break; + case 4: + r1 = prn(); + r2 = prn(); + r3 = prn(); + y = -std::log(r1*r2*r3); + break; + case 5: + r1 = prn(); + r2 = prn(); + r3 = prn(); + r4 = prn(); + r5 = prn(); + r6 = prn(); + y = -std::log(r1*r2*r3*r4) - std::log(r5) * std::pow(std::cos(PI/2.0*r6), 2); + break; + } + + // Now determine v and E_out + double v = x/(x + y); + E_out = E_max * v; +} + +} // namespace openmc diff --git a/src/secondary_nbody.h b/src/secondary_nbody.h new file mode 100644 index 0000000000..f80d50670a --- /dev/null +++ b/src/secondary_nbody.h @@ -0,0 +1,37 @@ +//! \file secondary_nbody.h +//! N-body phase space distribution + +#ifndef OPENMC_SECONDARY_NBODY_H +#define OPENMC_SECONDARY_NBODY_H + +#include "hdf5.h" + +#include "angle_energy.h" + +namespace openmc { + +//============================================================================== +//! Angle-energy distribution for particles emitted from neutron and +//! charged-particle reactions. This corresponds to ACE law 66 and ENDF File 6, +//! LAW=6. +//============================================================================== + +class NBodyPhaseSpace : public AngleEnergy { +public: + explicit NBodyPhaseSpace(hid_t group); + + //! Sample distribution for an angle and energy + //! \param[in] E_in Incoming energy in [eV] + //! \param[out] E_out Outgoing energy in [eV] + //! \param[out] mu Outgoing cosine with respect to current direction + void sample(double E_in, double& E_out, double& mu) const; +private: + int n_bodies_; //!< Number of particles distributed + double mass_ratio_; //!< Total mass of particles [neutron mass] + double A_; //!< Atomic weight ratio + double Q_; //!< Reaction Q-value [eV] +}; + +} // namespace openmc + +#endif // OPENMC_SECONDARY_NBODY_H diff --git a/src/secondary_uncorrelated.F90 b/src/secondary_uncorrelated.F90 deleted file mode 100644 index e559c1e5ae..0000000000 --- a/src/secondary_uncorrelated.F90 +++ /dev/null @@ -1,98 +0,0 @@ -module secondary_uncorrelated - - use angle_distribution, only: AngleDistribution - use angleenergy_header, only: AngleEnergy - use constants, only: ONE, TWO, MAX_WORD_LEN - use energy_distribution, only: EnergyDistribution, LevelInelastic, & - ContinuousTabular, MaxwellEnergy, Evaporation, WattEnergy, DiscretePhoton - use error, only: warning - use hdf5_interface, only: read_attribute, open_group, close_group, & - object_exists, HID_T - use random_lcg, only: prn - -!=============================================================================== -! UNCORRELATEDANGLEENERGY represents an uncorrelated angle-energy -! distribution. This corresponds to when an energy distribution is given in ENDF -! File 5/6 and an angular distribution is given in ENDF File 4. -!=============================================================================== - - type, extends(AngleEnergy) :: UncorrelatedAngleEnergy - logical :: fission = .false. - type(AngleDistribution) :: angle - class(EnergyDistribution), allocatable :: energy - contains - procedure :: sample => uncorrelated_sample - procedure :: from_hdf5 => uncorrelated_from_hdf5 - end type UncorrelatedAngleEnergy - -contains - - subroutine uncorrelated_sample(this, E_in, E_out, mu) - class(UncorrelatedAngleEnergy), intent(in) :: this - real(8), intent(in) :: E_in ! incoming energy - real(8), intent(out) :: E_out ! sampled outgoing energy - real(8), intent(out) :: mu ! sampled scattering cosine - - ! Sample cosine of scattering angle - if (this%fission) then - ! <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<<<<<< - ! For fission, the angle is not used, so just assign a dummy value - mu = ONE - ! <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<<<<<< - elseif (allocated(this%angle%energy)) then - mu = this%angle%sample(E_in) - else - ! no angle distribution given => assume isotropic for all energies - mu = TWO*prn() - ONE - end if - - ! Sample outgoing energy - E_out = this%energy%sample(E_in) - end subroutine uncorrelated_sample - - subroutine uncorrelated_from_hdf5(this, group_id) - class(UncorrelatedAngleEnergy), intent(inout) :: this - integer(HID_T), intent(in) :: group_id - - integer(HID_T) :: energy_group - integer(HID_T) :: angle_group - character(MAX_WORD_LEN) :: type - - ! Check if angle group is present & read - if (object_exists(group_id, 'angle')) then - angle_group = open_group(group_id, 'angle') - call this%angle%from_hdf5(angle_group) - call close_group(angle_group) - end if - - ! Check if energy group is present & read - if (object_exists(group_id, 'energy')) then - energy_group = open_group(group_id, 'energy') - call read_attribute(type, energy_group, 'type') - select case (type) - case ('discrete_photon') - allocate(DiscretePhoton :: this%energy) - case ('level') - allocate(LevelInelastic :: this%energy) - case ('continuous') - allocate(ContinuousTabular :: this%energy) - case ('maxwell') - allocate(MaxwellEnergy :: this%energy) - case ('evaporation') - allocate(Evaporation :: this%energy) - case ('watt') - allocate(WattEnergy :: this%energy) - case default - call warning("Energy distribution type '" // trim(type) & - // "' not implemented.") - end select - - if (allocated(this % energy)) then - call this%energy%from_hdf5(energy_group) - end if - - call close_group(energy_group) - end if - end subroutine uncorrelated_from_hdf5 - -end module secondary_uncorrelated diff --git a/src/secondary_uncorrelated.cpp b/src/secondary_uncorrelated.cpp new file mode 100644 index 0000000000..df5e8139b9 --- /dev/null +++ b/src/secondary_uncorrelated.cpp @@ -0,0 +1,74 @@ +#include "secondary_uncorrelated.h" + +#include // for stringstream +#include // for string + +#include "error.h" +#include "hdf5_interface.h" +#include "random_lcg.h" + +namespace openmc { + +//============================================================================== +// UncorrelatedAngleEnergy implementation +//============================================================================== + +UncorrelatedAngleEnergy::UncorrelatedAngleEnergy(hid_t group) +{ + // Check if angle group is present & read + if (object_exists(group, "angle")) { + hid_t angle_group = open_group(group, "angle"); + angle_ = AngleDistribution{angle_group}; + close_group(angle_group); + } + + // Check if energy group is present & read + if (object_exists(group, "energy")) { + hid_t energy_group = open_group(group, "energy"); + + std::string type; + read_attribute(energy_group, "type", type); + using UPtrEDist = std::unique_ptr; + if (type == "discrete_photon") { + energy_ = UPtrEDist{new DiscretePhoton{energy_group}}; + } else if (type == "level") { + energy_ = UPtrEDist{new LevelInelastic{energy_group}}; + } else if (type == "continuous") { + energy_ = UPtrEDist{new ContinuousTabular{energy_group}}; + } else if (type == "maxwell") { + energy_ = UPtrEDist{new MaxwellEnergy{energy_group}}; + } else if (type == "evaporation") { + energy_ = UPtrEDist{new Evaporation{energy_group}}; + } else if (type == "watt") { + energy_ = UPtrEDist{new WattEnergy{energy_group}}; + } else { + std::stringstream msg; + msg << "Energy distribution type '" << type << "' not implemented."; + warning(msg); + } + close_group(energy_group); + } + +} + +void +UncorrelatedAngleEnergy::sample(double E_in, double& E_out, double& mu) const +{ + // Sample cosine of scattering angle + if (fission_) { + // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<<<<<< + // For fission, the angle is not used, so just assign a dummy value + mu = 1.0; + // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<<<<<< + } else if (!angle_.empty()) { + mu = angle_.sample(E_in); + } else { + // no angle distribution given => assume isotropic for all energies + mu = 2.0*prn() - 1.0; + } + + // Sample outgoing energy + E_out = energy_->sample(E_in); +} + +} // namespace openmc diff --git a/src/secondary_uncorrelated.h b/src/secondary_uncorrelated.h new file mode 100644 index 0000000000..7f067fc0af --- /dev/null +++ b/src/secondary_uncorrelated.h @@ -0,0 +1,44 @@ +//! \file secondary_uncorrelated.h +//! Uncorrelated angle-energy distribution + +#ifndef OPENMC_SECONDARY_UNCORRELATED_H +#define OPENMC_SECONDARY_UNCORRELATED_H + +#include +#include + +#include "hdf5.h" +#include "angle_energy.h" +#include "distribution_angle.h" +#include "distribution_energy.h" + +namespace openmc { + +//============================================================================== +//! Uncorrelated angle-energy distribution. This corresponds to when an energy +//! distribution is given in ENDF File 5/6 and an angular distribution is given +//! in ENDF File 4. +//============================================================================== + +class UncorrelatedAngleEnergy : public AngleEnergy { +public: + explicit UncorrelatedAngleEnergy(hid_t group); + + //! Sample distribution for an angle and energy + //! \param[in] E_in Incoming energy in [eV] + //! \param[out] E_out Outgoing energy in [eV] + //! \param[out] mu Outgoing cosine with respect to current direction + void sample(double E_in, double& E_out, double& mu) const; + + // Accessors + AngleDistribution& angle() { return angle_; } + bool& fission() { return fission_; } +private: + AngleDistribution angle_; //!< Angle distribution + std::unique_ptr energy_; //!< Energy distribution + bool fission_ {false}; //!< Whether distribution is use for fission +}; + +} // namespace openmc + +#endif // OPENMC_SECONDARY_UNCORRELATED_H diff --git a/src/surface.cpp b/src/surface.cpp index 94c24c82db..261a8d7d24 100644 --- a/src/surface.cpp +++ b/src/surface.cpp @@ -151,7 +151,7 @@ Surface::Surface(pugi::xml_node surf_node) } if (check_for_node(surf_node, "boundary")) { - std::string surf_bc = get_node_value(surf_node, "boundary"); + std::string surf_bc = get_node_value(surf_node, "boundary", true, true); if (surf_bc == "transmission" || surf_bc == "transmit" ||surf_bc.empty()) { bc = BC_TRANSMIT; @@ -1040,7 +1040,7 @@ read_surfaces(pugi::xml_node *node) int i_surf; for (surf_node = node->child("surface"), i_surf = 0; surf_node; surf_node = surf_node.next_sibling("surface"), i_surf++) { - std::string surf_type = get_node_value(surf_node, "type"); + std::string surf_type = get_node_value(surf_node, "type", true, true); if (surf_type == "x-plane") { surfaces_c[i_surf] = new SurfaceXPlane(surf_node); diff --git a/src/surface.h b/src/surface.h index 62e0111ca0..d4e6c0230a 100644 --- a/src/surface.h +++ b/src/surface.h @@ -18,6 +18,7 @@ namespace openmc { // Module constant declarations (defined in .cpp) //============================================================================== +// TODO: Convert to enum extern "C" const int BC_TRANSMIT; extern "C" const int BC_VACUUM; extern "C" const int BC_REFLECT; diff --git a/src/tallies/tally.F90 b/src/tallies/tally.F90 index 8ddd42bce3..2c660ce87b 100644 --- a/src/tallies/tally.F90 +++ b/src/tallies/tally.F90 @@ -94,6 +94,7 @@ contains integer :: k ! loop index for bank sites integer :: d_bin ! delayed group bin index integer :: dg_filter ! index of delayed group filter + integer :: threshold ! threshold energy index real(8) :: yield ! delayed neutron yield real(8) :: atom_density_ ! atom/b-cm real(8) :: f ! interpolation factor @@ -227,7 +228,7 @@ contains ! Get yield and apply to score associate (rxn => nuclides(p % event_nuclide) % reactions(m)) score = p % last_wgt * flux & - * rxn % products(1) % yield % evaluate(E) + * rxn % product_yield(1, E) end associate end if @@ -633,7 +634,7 @@ contains score = p % absorb_wgt * yield * & micro_xs(p % event_nuclide) % fission & / micro_xs(p % event_nuclide) % absorption & - * rxn % products(1 + d) % decay_rate * flux + * rxn % product_decay_rate(1 + d) * flux end associate ! Tally to bin @@ -657,9 +658,8 @@ contains ! rxn % products array to be exceeded. Hence, we use the size ! of this array and not the MAX_DELAYED_GROUPS constant for ! this loop. - do d = 1, size(rxn % products) - 2 - - score = score + rxn % products(1 + d) % decay_rate * & + do d = 1, rxn % products_size() - 2 + score = score + rxn % product_decay_rate(1 + d) * & p % absorb_wgt & * micro_xs(p % event_nuclide) % fission & * nuclides(p % event_nuclide) % & @@ -699,7 +699,7 @@ contains ! determine score based on bank site weight and keff. score = score + keff * fission_bank(n_bank - p % n_bank + k) & - % wgt * rxn % products(1 + g) % decay_rate * flux + % wgt * rxn % product_decay_rate(1 + g) * flux end associate ! if the delayed group filter is present, tally to corresponding @@ -755,7 +755,7 @@ contains ! Compute the score and tally to bin score = micro_xs(i_nuclide) % fission * yield * flux * & - atom_density * rxn % products(1 + d) % decay_rate + atom_density * rxn % product_decay_rate(1 + d) end associate ! Tally to bin @@ -778,11 +778,10 @@ contains ! groups since this could cause the range of the rxn % products ! array to be exceeded. Hence, we use the size of this array ! and not the MAX_DELAYED_GROUPS constant for this loop. - do d = 1, size(rxn % products) - 2 - + do d = 1, rxn % products_size() - 2 score = score + micro_xs(i_nuclide) % fission * flux * & nuclides(i_nuclide) % nu(E, EMISSION_DELAYED) * & - atom_density * rxn % products(1 + d) % decay_rate + atom_density * rxn % product_decay_rate(1 + d) end do end associate end if @@ -824,7 +823,7 @@ contains ! Compute the score score = micro_xs(i_nuc) % fission * yield * flux * & atom_density_ & - * rxn % products(1 + d) % decay_rate + * rxn % product_decay_rate(1 + d) end associate ! Tally to bin @@ -860,13 +859,13 @@ contains ! rxn % products array to be exceeded. Hence, we use the ! size of this array and not the MAX_DELAYED_GROUPS ! constant for this loop. - do d = 1, size(rxn % products) - 2 + do d = 1, rxn % products_size() - 2 ! Accumulate the contribution from each nuclide score = score + micro_xs(i_nuc) % fission & * nuclides(i_nuc) % nu(E, EMISSION_DELAYED) & * atom_density_ * flux & - * rxn % products(1 + d) % decay_rate + * rxn % product_decay_rate(1 + d) end do end associate end if @@ -1132,12 +1131,12 @@ contains i_energy = micro_xs(i_nuclide) % index_grid f = micro_xs(i_nuclide) % interp_factor - associate (xs => nuclides(i_nuclide) % reactions(m) & - % xs(i_temp)) - if (i_energy >= xs % threshold) then - score = ((ONE - f) * xs % value(i_energy - & - xs % threshold + 1) + f * xs % value(i_energy - & - xs % threshold + 2)) * atom_density * flux + associate (rx => nuclides(i_nuclide) % reactions(m)) + threshold = rx % xs_threshold(i_temp) + if (i_energy >= threshold) then + score = ((ONE - f) * rx % xs(i_temp, i_energy - & + threshold + 1) + f * rx % xs(i_temp, i_energy - & + threshold + 2)) * atom_density * flux end if end associate else @@ -1165,12 +1164,12 @@ contains i_energy = micro_xs(i_nuc) % index_grid f = micro_xs(i_nuc) % interp_factor - associate (xs => nuclides(i_nuc) % reactions(m) & - % xs(i_temp)) - if (i_energy >= xs % threshold) then - score = score + ((ONE - f) * xs % value(i_energy - & - xs % threshold + 1) + f * xs % value(i_energy - & - xs % threshold + 2)) * atom_density_ * flux + associate (rx => nuclides(i_nuc) % reactions(m)) + threshold = rx % xs_threshold(i_temp) + if (i_energy >= threshold) then + score = score + ((ONE - f) * rx % xs(i_temp, i_energy - & + threshold + 1) + f * rx % xs(i_temp, i_energy - & + threshold + 2)) * atom_density_ * flux end if end associate else diff --git a/src/xml_interface.cpp b/src/xml_interface.cpp index b4fcc524c6..c89e19ef19 100644 --- a/src/xml_interface.cpp +++ b/src/xml_interface.cpp @@ -1,6 +1,6 @@ #include "xml_interface.h" -#include // for std::transform +#include // for transform #include #include "error.h" @@ -9,10 +9,11 @@ namespace openmc { std::string -get_node_value(pugi::xml_node node, const char *name) +get_node_value(pugi::xml_node node, const char* name, bool lowercase, + bool strip) { // Search for either an attribute or child tag and get the data as a char*. - const pugi::char_t *value_char; + const pugi::char_t* value_char; if (node.attribute(name)) { value_char = node.attribute(name).value(); } else if (node.child(name)) { @@ -23,14 +24,18 @@ get_node_value(pugi::xml_node node, const char *name) << node.name() << "\" XML node"; fatal_error(err_msg); } + std::string value {value_char}; - // Convert to lowercase string. - std::string value(value_char); - std::transform(value.begin(), value.end(), value.begin(), ::tolower); + // Convert to lower-case if needed + if (lowercase) { + std::transform(value.begin(), value.end(), value.begin(), ::tolower); + } - // Remove whitespace. - value.erase(0, value.find_first_not_of(" \t\r\n")); - value.erase(value.find_last_not_of(" \t\r\n") + 1); + // Strip leading/trailing whitespace if needed + if (strip) { + value.erase(0, value.find_first_not_of(" \t\r\n")); + value.erase(value.find_last_not_of(" \t\r\n") + 1); + } return value; } diff --git a/src/xml_interface.h b/src/xml_interface.h index de89018efe..f10143697a 100644 --- a/src/xml_interface.h +++ b/src/xml_interface.h @@ -1,6 +1,7 @@ #ifndef XML_INTERFACE_H #define XML_INTERFACE_H +#include // for stringstream #include #include @@ -15,7 +16,24 @@ check_for_node(pugi::xml_node node, const char *name) return node.attribute(name) || node.child(name); } -std::string get_node_value(pugi::xml_node node, const char *name); +std::string get_node_value(pugi::xml_node node, const char *name, + bool lowercase=false, bool strip=false); + +template +std::vector get_node_array(pugi::xml_node node, const char* name) +{ + // Get value of node attribute/child + std::string s {get_node_value(node, name)}; + + // Read values one by one into vector + std::stringstream iss {s}; + T value; + std::vector values; + while (iss >> value) + values.push_back(value); + + return values; +} } // namespace openmc #endif // XML_INTERFACE_H diff --git a/tests/regression_tests/conftest.py b/tests/regression_tests/conftest.py index 00ef247dc4..b4a7644762 100644 --- a/tests/regression_tests/conftest.py +++ b/tests/regression_tests/conftest.py @@ -1,7 +1,15 @@ +import numpy as np import openmc +from pkg_resources import parse_version import pytest +@pytest.fixture(scope='module', autouse=True) +def numpy_version_requirement(): + assert parse_version(np.__version__) >= parse_version("1.14"), \ + "Regression tests require NumPy 1.14 or greater" + + @pytest.fixture(scope='module', autouse=True) def setup_regression_test(request): # Reset autogenerated IDs assigned to OpenMC objects diff --git a/tests/regression_tests/filter_energyfun/results_true.dat b/tests/regression_tests/filter_energyfun/results_true.dat index 110038edfd..8e5577c432 100644 --- a/tests/regression_tests/filter_energyfun/results_true.dat +++ b/tests/regression_tests/filter_energyfun/results_true.dat @@ -1,2 +1,2 @@ energyfunction nuclide score mean std. dev. -0 02180f5f310ee4 Am241 ((n,gamma) / (n,gamma)) 1.00e-01 9.97e-03 +0 d2effa26cb3cf2 Am241 ((n,gamma) / (n,gamma)) 1.00e-01 9.97e-03 diff --git a/tests/regression_tests/mgxs_library_histogram/inputs_true.dat b/tests/regression_tests/mgxs_library_histogram/inputs_true.dat index 799f4796d5..d1bd197a7c 100644 --- a/tests/regression_tests/mgxs_library_histogram/inputs_true.dat +++ b/tests/regression_tests/mgxs_library_histogram/inputs_true.dat @@ -60,7 +60,7 @@ 0.0 0.625 20000000.0 - -1.0 -0.818181818182 -0.636363636364 -0.454545454545 -0.272727272727 -0.0909090909091 0.0909090909091 0.272727272727 0.454545454545 0.636363636364 0.818181818182 1.0 + -1.0 -0.8181818181818181 -0.6363636363636364 -0.4545454545454546 -0.2727272727272727 -0.09090909090909083 0.09090909090909083 0.2727272727272727 0.4545454545454546 0.6363636363636365 0.8181818181818183 1.0 2 diff --git a/tests/regression_tests/multipole/results_true.dat b/tests/regression_tests/multipole/results_true.dat index d734e67bd0..19c34229ff 100644 --- a/tests/regression_tests/multipole/results_true.dat +++ b/tests/regression_tests/multipole/results_true.dat @@ -37,5 +37,5 @@ Cell Fill = Material 2 Region = -1 Rotation = None - Temperature = [ 500. 700. 0. 800.] + Temperature = [500. 700. 0. 800.] Translation = None diff --git a/tests/regression_tests/source/inputs_true.dat b/tests/regression_tests/source/inputs_true.dat index 6dd913edc0..860a36bc8e 100644 --- a/tests/regression_tests/source/inputs_true.dat +++ b/tests/regression_tests/source/inputs_true.dat @@ -48,7 +48,7 @@ - 1.0 1.38949549437 1.93069772888 2.68269579528 3.72759372031 5.17947467923 7.19685673001 10.0 13.8949549437 19.3069772888 26.8269579528 37.2759372031 51.7947467923 71.9685673001 100.0 138.949549437 193.069772888 268.269579528 372.759372031 517.947467923 719.685673001 1000.0 1389.49549437 1930.69772888 2682.69579528 3727.59372031 5179.47467923 7196.85673001 10000.0 13894.9549437 19306.9772888 26826.9579528 37275.9372031 51794.7467923 71968.5673001 100000.0 138949.549437 193069.772888 268269.579528 372759.372031 517947.467923 719685.673001 1000000.0 1389495.49437 1930697.72888 2682695.79528 3727593.72031 5179474.67923 7196856.73001 10000000.0 0.0 2.90864392994e-08 5.80533561806e-08 8.67817193689e-08 1.15153477858e-07 1.43052046006e-07 1.70362782612e-07 1.96973462002e-07 2.22774735186e-07 2.47660579198e-07 2.71528732767e-07 2.9428111653e-07 3.15824236062e-07 3.36069566065e-07 3.54933914133e-07 3.72339762616e-07 3.88215587147e-07 4.02496150558e-07 4.15122770952e-07 4.26043562837e-07 4.35213650335e-07 4.42595351592e-07 4.48158333612e-07 4.5187973691e-07 4.53744269441e-07 4.53744269441e-07 4.5187973691e-07 4.48158333612e-07 4.42595351592e-07 4.35213650335e-07 4.26043562837e-07 4.15122770952e-07 4.02496150558e-07 3.88215587147e-07 3.72339762616e-07 3.54933914133e-07 3.36069566065e-07 3.15824236062e-07 2.9428111653e-07 2.71528732767e-07 2.47660579198e-07 2.22774735186e-07 1.96973462002e-07 1.70362782612e-07 1.43052046006e-07 1.15153477858e-07 8.67817193689e-08 5.80533561806e-08 2.90864392994e-08 5.55962111528e-23 + 1.0 1.3894954943731377 1.93069772888325 2.6826957952797255 3.72759372031494 5.17947467923121 7.196856730011519 10.0 13.894954943731374 19.306977288832496 26.826957952797247 37.2759372031494 51.7947467923121 71.96856730011518 100.0 138.94954943731375 193.06977288832496 268.26957952797244 372.7593720314938 517.9474679231207 719.6856730011514 1000.0 1389.4954943731375 1930.6977288832495 2682.6957952797247 3727.593720314938 5179.474679231207 7196.856730011514 10000.0 13894.95494373136 19306.977288832495 26826.95795279722 37275.93720314938 51794.74679231213 71968.56730011514 100000.0 138949.5494373136 193069.77288832495 268269.5795279722 372759.3720314938 517947.4679231202 719685.6730011514 1000000.0 1389495.494373136 1930697.7288832497 2682695.7952797217 3727593.720314938 5179474.679231202 7196856.730011513 10000000.0 0.0 2.9086439299358713e-08 5.80533561806147e-08 8.67817193689187e-08 1.1515347785771536e-07 1.4305204600565115e-07 1.7036278261198208e-07 1.9697346200185813e-07 2.227747351856934e-07 2.4766057919761985e-07 2.715287327665956e-07 2.9428111652990295e-07 3.1582423606228735e-07 3.360695660646056e-07 3.549339141332686e-07 3.723397626156721e-07 3.882155871468592e-07 4.024961505584776e-07 4.151227709522976e-07 4.260435628367196e-07 4.3521365033538783e-07 4.4259535159179273e-07 4.4815833361210174e-07 4.5187973690993757e-07 4.5374426944091084e-07 4.5374426944091084e-07 4.5187973690993757e-07 4.4815833361210174e-07 4.4259535159179273e-07 4.352136503353879e-07 4.2604356283671966e-07 4.1512277095229767e-07 4.0249615055847764e-07 3.8821558714685926e-07 3.723397626156722e-07 3.5493391413326864e-07 3.360695660646057e-07 3.158242360622874e-07 2.942811165299031e-07 2.715287327665957e-07 2.4766057919762e-07 2.2277473518569352e-07 1.9697346200185819e-07 1.7036278261198226e-07 1.4305204600565126e-07 1.1515347785771556e-07 8.678171936891881e-08 5.805335618061493e-08 2.9086439299358858e-08 5.559621115282002e-23 diff --git a/tests/regression_tests/surface_tally/inputs_true.dat b/tests/regression_tests/surface_tally/inputs_true.dat index fc10110eee..96639a0761 100644 --- a/tests/regression_tests/surface_tally/inputs_true.dat +++ b/tests/regression_tests/surface_tally/inputs_true.dat @@ -48,10 +48,10 @@ 0.0 4000000.0 20000000.0 - 0.0 0.785398163397 3.14159265359 + 0.0 0.7853981633974483 3.141592653589793 - 0.0 0.785398163397 3.14159265359 + 0.0 0.7853981633974483 3.141592653589793 1 diff --git a/tests/regression_tests/tally_aggregation/results_true.dat b/tests/regression_tests/tally_aggregation/results_true.dat index 0f0a3d60a9..3caeceec99 100644 --- a/tests/regression_tests/tally_aggregation/results_true.dat +++ b/tests/regression_tests/tally_aggregation/results_true.dat @@ -1 +1 @@ -8294c7481b3433a4beb25541ae72c1ea915def0c0d0f393f7585371877187f4a2a25e70bb602232e2bc1e877e78db0e9384591e5c71575659f95abb10fae0af3 \ No newline at end of file +c8fb043c5321e698040544dbb0baa9ce82df0e189738fb6013b4f3ff7e05a989a7a59b9b55034b8c9de6bc604ba9f969dc08ad2752fc74da205c19ff5974c9a2 \ No newline at end of file diff --git a/tests/regression_tests/tally_arithmetic/results_true.dat b/tests/regression_tests/tally_arithmetic/results_true.dat index ef2741cc12..6302095d08 100644 --- a/tests/regression_tests/tally_arithmetic/results_true.dat +++ b/tests/regression_tests/tally_arithmetic/results_true.dat @@ -1,134 +1,139 @@ -[[[ 0. 0. 0. 0.] - [ 0. 0. 0. 0.] - [ 0. 0. 0. 0.] - [ 0. 0. 0. 0.]] +[[[0. 0. 0. 0.] + [0. 0. 0. 0.] + [0. 0. 0. 0.] + [0. 0. 0. 0.]] - [[ 0. 0. 0. 0.] - [ 0. 0. 0. 0.] - [ 0. 0. 0. 0.] - [ 0. 0. 0. 0.]] + [[0. 0. 0. 0.] + [0. 0. 0. 0.] + [0. 0. 0. 0.] + [0. 0. 0. 0.]] - [[ 0. 0. 0. 0.] - [ 0. 0. 0. 0.] - [ 0. 0. 0. 0.] - [ 0. 0. 0. 0.]] + [[0. 0. 0. 0.] + [0. 0. 0. 0.] + [0. 0. 0. 0.] + [0. 0. 0. 0.]] - ..., - [[ 0. 0. 0. 0.] - [ 0. 0. 0. 0.] - [ 0. 0. 0. 0.] - [ 0. 0. 0. 0.]] + ... - [[ 0. 0. 0. 0.] - [ 0. 0. 0. 0.] - [ 0. 0. 0. 0.] - [ 0. 0. 0. 0.]] + [[0. 0. 0. 0.] + [0. 0. 0. 0.] + [0. 0. 0. 0.] + [0. 0. 0. 0.]] - [[ 0. 0. 0. 0.] - [ 0. 0. 0. 0.] - [ 0. 0. 0. 0.] - [ 0. 0. 0. 0.]]][[[ 0. 0. 0. 0.] - [ 0. 0. 0. 0.] - [ 0. 0. 0. 0.] - [ 0. 0. 0. 0.]] + [[0. 0. 0. 0.] + [0. 0. 0. 0.] + [0. 0. 0. 0.] + [0. 0. 0. 0.]] - [[ 0. 0. 0. 0.] - [ 0. 0. 0. 0.] - [ 0. 0. 0. 0.] - [ 0. 0. 0. 0.]] + [[0. 0. 0. 0.] + [0. 0. 0. 0.] + [0. 0. 0. 0.] + [0. 0. 0. 0.]]][[[0. 0. 0. 0.] + [0. 0. 0. 0.] + [0. 0. 0. 0.] + [0. 0. 0. 0.]] - [[ 0. 0. 0. 0.] - [ 0. 0. 0. 0.] - [ 0. 0. 0. 0.] - [ 0. 0. 0. 0.]] + [[0. 0. 0. 0.] + [0. 0. 0. 0.] + [0. 0. 0. 0.] + [0. 0. 0. 0.]] - ..., - [[ 0. 0. 0. 0.] - [ 0. 0. 0. 0.] - [ 0. 0. 0. 0.] - [ 0. 0. 0. 0.]] + [[0. 0. 0. 0.] + [0. 0. 0. 0.] + [0. 0. 0. 0.] + [0. 0. 0. 0.]] - [[ 0. 0. 0. 0.] - [ 0. 0. 0. 0.] - [ 0. 0. 0. 0.] - [ 0. 0. 0. 0.]] + ... - [[ 0. 0. 0. 0.] - [ 0. 0. 0. 0.] - [ 0. 0. 0. 0.] - [ 0. 0. 0. 0.]]][[[ 0. 0. 0. 0.] - [ 0. 0. 0. 0.] - [ 0. 0. 0. 0.]] + [[0. 0. 0. 0.] + [0. 0. 0. 0.] + [0. 0. 0. 0.] + [0. 0. 0. 0.]] - [[ 0. 0. 0. 0.] - [ 0. 0. 0. 0.] - [ 0. 0. 0. 0.]] + [[0. 0. 0. 0.] + [0. 0. 0. 0.] + [0. 0. 0. 0.] + [0. 0. 0. 0.]] - [[ 0. 0. 0. 0.] - [ 0. 0. 0. 0.] - [ 0. 0. 0. 0.]] + [[0. 0. 0. 0.] + [0. 0. 0. 0.] + [0. 0. 0. 0.] + [0. 0. 0. 0.]]][[[0. 0. 0. 0.] + [0. 0. 0. 0.] + [0. 0. 0. 0.]] - ..., - [[ 0. 0. 0. 0.] - [ 0. 0. 0. 0.] - [ 0. 0. 0. 0.]] + [[0. 0. 0. 0.] + [0. 0. 0. 0.] + [0. 0. 0. 0.]] - [[ 0. 0. 0. 0.] - [ 0. 0. 0. 0.] - [ 0. 0. 0. 0.]] + [[0. 0. 0. 0.] + [0. 0. 0. 0.] + [0. 0. 0. 0.]] - [[ 0. 0. 0. 0.] - [ 0. 0. 0. 0.] - [ 0. 0. 0. 0.]]][[[ 0. 0. 0.] - [ 0. 0. 0.] - [ 0. 0. 0.] - [ 0. 0. 0.]] + ... - [[ 0. 0. 0.] - [ 0. 0. 0.] - [ 0. 0. 0.] - [ 0. 0. 0.]] + [[0. 0. 0. 0.] + [0. 0. 0. 0.] + [0. 0. 0. 0.]] - [[ 0. 0. 0.] - [ 0. 0. 0.] - [ 0. 0. 0.] - [ 0. 0. 0.]] + [[0. 0. 0. 0.] + [0. 0. 0. 0.] + [0. 0. 0. 0.]] - ..., - [[ 0. 0. 0.] - [ 0. 0. 0.] - [ 0. 0. 0.] - [ 0. 0. 0.]] + [[0. 0. 0. 0.] + [0. 0. 0. 0.] + [0. 0. 0. 0.]]][[[0. 0. 0.] + [0. 0. 0.] + [0. 0. 0.] + [0. 0. 0.]] - [[ 0. 0. 0.] - [ 0. 0. 0.] - [ 0. 0. 0.] - [ 0. 0. 0.]] + [[0. 0. 0.] + [0. 0. 0.] + [0. 0. 0.] + [0. 0. 0.]] - [[ 0. 0. 0.] - [ 0. 0. 0.] - [ 0. 0. 0.] - [ 0. 0. 0.]]][[[ 0. 0. 0.] - [ 0. 0. 0.] - [ 0. 0. 0.]] + [[0. 0. 0.] + [0. 0. 0.] + [0. 0. 0.] + [0. 0. 0.]] - [[ 0. 0. 0.] - [ 0. 0. 0.] - [ 0. 0. 0.]] + ... - [[ 0. 0. 0.] - [ 0. 0. 0.] - [ 0. 0. 0.]] + [[0. 0. 0.] + [0. 0. 0.] + [0. 0. 0.] + [0. 0. 0.]] - ..., - [[ 0. 0. 0.] - [ 0. 0. 0.] - [ 0. 0. 0.]] + [[0. 0. 0.] + [0. 0. 0.] + [0. 0. 0.] + [0. 0. 0.]] - [[ 0. 0. 0.] - [ 0. 0. 0.] - [ 0. 0. 0.]] + [[0. 0. 0.] + [0. 0. 0.] + [0. 0. 0.] + [0. 0. 0.]]][[[0. 0. 0.] + [0. 0. 0.] + [0. 0. 0.]] - [[ 0. 0. 0.] - [ 0. 0. 0.] - [ 0. 0. 0.]]] \ No newline at end of file + [[0. 0. 0.] + [0. 0. 0.] + [0. 0. 0.]] + + [[0. 0. 0.] + [0. 0. 0.] + [0. 0. 0.]] + + ... + + [[0. 0. 0.] + [0. 0. 0.] + [0. 0. 0.]] + + [[0. 0. 0.] + [0. 0. 0.] + [0. 0. 0.]] + + [[0. 0. 0.] + [0. 0. 0.] + [0. 0. 0.]]] \ No newline at end of file diff --git a/tests/regression_tests/test_reference.h5 b/tests/regression_tests/test_reference.h5 index 10dc37b511..186eff0d6f 100644 Binary files a/tests/regression_tests/test_reference.h5 and b/tests/regression_tests/test_reference.h5 differ diff --git a/tests/regression_tests/triso/inputs_true.dat b/tests/regression_tests/triso/inputs_true.dat index 6d674b4502..a131876285 100644 --- a/tests/regression_tests/triso/inputs_true.dat +++ b/tests/regression_tests/triso/inputs_true.dat @@ -6,170 +6,170 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -199,7 +199,7 @@ - 0.333333333333 0.333333333333 0.333333333333 + 0.3333333333333333 0.3333333333333333 0.3333333333333333 38 3 3 3 -0.5 -0.5 -0.5 @@ -226,170 +226,170 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/regression_tests/triso/results_true.dat b/tests/regression_tests/triso/results_true.dat index 3818ba7b11..dfc700c058 100644 --- a/tests/regression_tests/triso/results_true.dat +++ b/tests/regression_tests/triso/results_true.dat @@ -1,2 +1,2 @@ k-combined: -1.681659E+00 7.262745E-02 +1.683226E+00 7.383559E-02 diff --git a/tests/unit_tests/test_deplete_resultslist.py b/tests/unit_tests/test_deplete_resultslist.py index 86896cc151..7de0b1ae81 100644 --- a/tests/unit_tests/test_deplete_resultslist.py +++ b/tests/unit_tests/test_deplete_resultslist.py @@ -19,21 +19,22 @@ def test_get_atoms(res): t, n = res.get_atoms("1", "Xe135") t_ref = [0.0, 1296000.0, 2592000.0, 3888000.0] - n_ref = [6.6747328233649218e+08, 3.5421791038348462e+14, - 3.6208592242443462e+14, 3.3799758969347038e+14] + n_ref = [6.6747328233649218e+08, 3.4589992012016338e+14, + 3.5635060369969225e+14, 3.5195113630100188e+14] np.testing.assert_allclose(t, t_ref) np.testing.assert_allclose(n, n_ref) + def test_get_reaction_rate(res): """Tests evaluating reaction rate.""" t, r = res.get_reaction_rate("1", "Xe135", "(n,gamma)") t_ref = [0.0, 1296000.0, 2592000.0, 3888000.0] - n_ref = np.array([6.6747328233649218e+08, 3.5421791038348462e+14, - 3.6208592242443462e+14, 3.3799758969347038e+14]) - xs_ref = np.array([4.0594392323131994e-05, 3.9249546927524987e-05, - 3.8394587728581798e-05, 4.1521845978371697e-05]) + n_ref = np.array([6.6747328233649218e+08, 3.4589992012016338e+14, + 3.5635060369969225e+14, 3.5195113630100188e+14]) + xs_ref = np.array([4.1340608491478010e-05, 4.1120938620476115e-05, + 4.3341529708654921e-05, 3.8716623651147821e-05]) np.testing.assert_allclose(t, t_ref) np.testing.assert_allclose(r, n_ref * xs_ref) @@ -44,8 +45,8 @@ def test_get_eigenvalue(res): t, k = res.get_eigenvalue() t_ref = [0.0, 1296000.0, 2592000.0, 3888000.0] - k_ref = [1.181281798790367, 1.1798750921988739, 1.1965943696058159, - 1.2207119847790813] + k_ref = [1.1798617938070866, 1.1745713141097096, 1.1732427763487678, + 1.213699703239334] np.testing.assert_allclose(t, t_ref) np.testing.assert_allclose(k, k_ref) diff --git a/tools/ci/travis-install-njoy.sh b/tools/ci/travis-install-njoy.sh index 788cfc7a7f..8255ffea83 100755 --- a/tools/ci/travis-install-njoy.sh +++ b/tools/ci/travis-install-njoy.sh @@ -3,6 +3,5 @@ set -ex cd $HOME git clone https://github.com/njoy/NJOY2016 cd NJOY2016 -sed -i -e 's/5\.1/4.8/' CMakeLists.txt mkdir build && cd build cmake -Dstatic=on .. && make 2>/dev/null && sudo make install diff --git a/tools/ci/travis-install.sh b/tools/ci/travis-install.sh index 67c8a4d96d..1f80453ad6 100755 --- a/tools/ci/travis-install.sh +++ b/tools/ci/travis-install.sh @@ -7,20 +7,13 @@ set -ex # Upgrade pip before doing anything else pip install --upgrade pip -# Running OpenMC's setup.py requires numpy/cython already. NumPy float -# formatting changed in version 1.14, so stick with a lower version until we can -# handle it in our test suite -pip install 'numpy<1.14' +# Running OpenMC's setup.py requires numpy/cython already +pip install numpy pip install cython # pytest installed by default -- make sure we get latest pip install --upgrade pytest -# Pandas stopped supporting Python 3.4 with version 0.21 -if [[ $TRAVIS_PYTHON_VERSION == "3.4" ]]; then - pip install pandas==0.20.3 -fi - # Install mpi4py for MPI configurations if [[ $MPI == 'y' ]]; then pip install --no-binary=mpi4py mpi4py diff --git a/tools/ci/travis-script.sh b/tools/ci/travis-script.sh index ee445b517f..b7e3fb0c2f 100755 --- a/tools/ci/travis-script.sh +++ b/tools/ci/travis-script.sh @@ -2,7 +2,7 @@ set -ex # Run source check -if [[ $TRAVIS_PYTHON_VERSION == "3.4" && $OMP == 'n' && $MPI == 'n' ]]; then +if [[ $TRAVIS_PYTHON_VERSION == "3.5" && $OMP == 'n' && $MPI == 'n' ]]; then pushd tests && python check_source.py && popd fi diff --git a/vendor/xtensor/CMakeLists.txt b/vendor/xtensor/CMakeLists.txt new file mode 100644 index 0000000000..d4d3b355d6 --- /dev/null +++ b/vendor/xtensor/CMakeLists.txt @@ -0,0 +1,191 @@ +############################################################################ +# Copyright (c) 2016, Johan Mabille, Sylvain Corlay and Wolf Vollprecht # +# # +# Distributed under the terms of the BSD 3-Clause License. # +# # +# The full license is in the file LICENSE, distributed with this software. # +############################################################################ + +cmake_minimum_required(VERSION 3.1) +project(xtensor) + +set(XTENSOR_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include) + +# Versionning +# =========== + +file(STRINGS "${XTENSOR_INCLUDE_DIR}/xtensor/xtensor_config.hpp" xtensor_version_defines + REGEX "#define XTENSOR_VERSION_(MAJOR|MINOR|PATCH)") +foreach(ver ${xtensor_version_defines}) + if(ver MATCHES "#define XTENSOR_VERSION_(MAJOR|MINOR|PATCH) +([^ ]+)$") + set(XTENSOR_VERSION_${CMAKE_MATCH_1} "${CMAKE_MATCH_2}" CACHE INTERNAL "") + endif() +endforeach() +set(${PROJECT_NAME}_VERSION + ${XTENSOR_VERSION_MAJOR}.${XTENSOR_VERSION_MINOR}.${XTENSOR_VERSION_PATCH}) +message(STATUS "Building xtensor v${${PROJECT_NAME}_VERSION}") + +# Dependencies +# ============ + +#find_package(xtl 0.4.9 REQUIRED) + +#message(STATUS "Found xtl: ${xtl_INCLUDE_DIRS}/xtl") + +#find_package(nlohmann_json 3.1.1) + +# Build +# ===== + +set(XTENSOR_HEADERS + ${XTENSOR_INCLUDE_DIR}/xtensor/xaccumulator.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xadapt.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xarray.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xassign.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xaxis_iterator.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xbroadcast.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xbuffer_adaptor.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xbuilder.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xcomplex.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xconcepts.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xcontainer.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xcsv.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xeval.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xexception.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xexpression.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xfixed.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xfunction.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xfunctor_view.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xgenerator.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xindex_view.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xinfo.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xio.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xiterable.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xiterator.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xjson.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xlayout.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xmath.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xnoalias.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xnorm.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xnpy.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xoffset_view.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xoperation.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xoptional.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xoptional_assembly.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xoptional_assembly_base.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xoptional_assembly_storage.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xrandom.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xreducer.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xscalar.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xsemantic.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xshape.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xslice.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xsort.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xstorage.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xstrided_view.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xstrided_view_base.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xstrides.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xtensor.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xtensor_config.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xtensor_forward.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xtensor_simd.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xutils.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xvectorize.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xview.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/xview_utils.hpp +) + +add_library(xtensor INTERFACE) +target_include_directories(xtensor INTERFACE $ + $) +target_link_libraries(xtensor INTERFACE xtl) + +OPTION(XTENSOR_ENABLE_ASSERT "xtensor bound check" OFF) +OPTION(XTENSOR_CHECK_DIMENSION "xtensor dimension check" OFF) +OPTION(XTENSOR_USE_XSIMD "simd acceleration for xtensor" OFF) +OPTION(BUILD_TESTS "xtensor test suite" OFF) +OPTION(BUILD_BENCHMARK "xtensor benchmark" OFF) +OPTION(DOWNLOAD_GTEST "build gtest from downloaded sources" OFF) +OPTION(DOWNLOAD_GBENCHMARK "download google benchmark and build from source" ON) +OPTION(DEFAULT_COLUMN_MAJOR "set default layout to column major" OFF) +OPTION(DISABLE_VS2017 "disables the compilation of some test with Visual Studio 2017" OFF) + +if(DOWNLOAD_GTEST OR GTEST_SRC_DIR) + set(BUILD_TESTS ON) +endif() + +if(XTENSOR_ENABLE_ASSERT OR XTENSOR_CHECK_DIMENSION) + add_definitions(-DXTENSOR_ENABLE_ASSERT) +endif() + +if(XTENSOR_CHECK_DIMENSION) + add_definitions(-DXTENSOR_ENABLE_CHECK_DIMENSION) +endif() + +if(XTENSOR_USE_XSIMD) + add_definitions(-DXTENSOR_USE_XSIMD) + find_package(xsimd 4.1.6 REQUIRED) + message(STATUS "Found xsimd: ${xsimd_INCLUDE_DIRS}/xsimd") + target_link_libraries(xtensor INTERFACE xsimd) +endif() + +if(DEFAULT_COLUMN_MAJOR) + add_definitions(-DXTENSOR_DEFAULT_LAYOUT=layout_type::column_major) +endif() + +if(DISABLE_VS2017) + add_definitions(-DDISABLE_VS2017) +endif() + +if(BUILD_TESTS) + add_subdirectory(test) +endif() + +if(BUILD_BENCHMARK) + add_subdirectory(benchmark) +endif() + +# Installation +# ============ + +include(GNUInstallDirs) +include(CMakePackageConfigHelpers) + +install(TARGETS xtensor + EXPORT ${PROJECT_NAME}-targets) + +# Makes the project importable from the build directory +export(EXPORT ${PROJECT_NAME}-targets + FILE "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Targets.cmake") + +install(FILES ${XTENSOR_HEADERS} + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/xtensor) + +set(XTENSOR_CMAKECONFIG_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" CACHE + STRING "install path for xtensorConfig.cmake") + +configure_package_config_file(${PROJECT_NAME}Config.cmake.in + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + INSTALL_DESTINATION ${XTENSOR_CMAKECONFIG_INSTALL_DIR}) + +# xtensor is header-only and does not depend on the architecture. +# Remove CMAKE_SIZEOF_VOID_P from xtensorConfigVersion.cmake so that an xtensorConfig.cmake +# generated for a 64 bit target can be used for 32 bit targets and vice versa. +set(_XTENSOR_CMAKE_SIZEOF_VOID_P ${CMAKE_SIZEOF_VOID_P}) +unset(CMAKE_SIZEOF_VOID_P) +write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake + VERSION ${${PROJECT_NAME}_VERSION} + COMPATIBILITY AnyNewerVersion) +set(CMAKE_SIZEOF_VOID_P ${_XTENSOR_CMAKE_SIZEOF_VOID_P}) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake + DESTINATION ${XTENSOR_CMAKECONFIG_INSTALL_DIR}) +install(EXPORT ${PROJECT_NAME}-targets + FILE ${PROJECT_NAME}Targets.cmake + DESTINATION ${XTENSOR_CMAKECONFIG_INSTALL_DIR}) + +configure_file(${PROJECT_NAME}.pc.in + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc" + @ONLY) +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig/") diff --git a/vendor/xtensor/include/xtensor/xaccumulator.hpp b/vendor/xtensor/include/xtensor/xaccumulator.hpp new file mode 100644 index 0000000000..f2b446ff36 --- /dev/null +++ b/vendor/xtensor/include/xtensor/xaccumulator.hpp @@ -0,0 +1,266 @@ +/*************************************************************************** +* Copyright (c) 2016, Johan Mabille, Sylvain Corlay and Wolf Vollprecht * +* * +* Distributed under the terms of the BSD 3-Clause License. * +* * +* The full license is in the file LICENSE, distributed with this software. * +****************************************************************************/ + +#ifndef XTENSOR_ACCUMULATOR_HPP +#define XTENSOR_ACCUMULATOR_HPP + +#include +#include +#include +#include + +#include "xexpression.hpp" +#include "xstrides.hpp" +#include "xtensor_forward.hpp" + +namespace xt +{ + +#define DEFAULT_STRATEGY_ACCUMULATORS evaluation_strategy::immediate + + /************** + * accumulate * + **************/ + + template + struct xaccumulator_functor + : public std::tuple + { + using self_type = xaccumulator_functor; + using base_type = std::tuple; + using accumulate_functor_type = ACCUMULATE_FUNC; + using init_functor_type = INIT_FUNC; + + xaccumulator_functor() + : base_type() + { + } + + template + xaccumulator_functor(RF&& accumulate_func) + : base_type(std::forward(accumulate_func), INIT_FUNC()) + { + } + + template + xaccumulator_functor(RF&& accumulate_func, IF&& init_func) + : base_type(std::forward(accumulate_func), std::forward(init_func)) + { + } + }; + + template + auto make_xaccumulator_functor(RF&& accumulate_func) + { + using accumulator_type = xaccumulator_functor>; + return accumulator_type(std::forward(accumulate_func)); + } + + template + auto make_xaccumulator_functor(RF&& accumulate_func, IF&& init_func) + { + using accumulator_type = xaccumulator_functor, std::remove_reference_t>; + return accumulator_type(std::forward(accumulate_func), std::forward(init_func)); + } + + namespace detail + { + template + xarray::value_type> accumulator_impl(F&&, E&&, std::size_t, EVS) + { + static_assert(!std::is_same::value, "Lazy accumulators not yet implemented."); + } + + template + xarray::value_type> accumulator_impl(F&&, E&&, EVS) + { + static_assert(!std::is_same::value, "Lazy accumulators not yet implemented."); + } + + template + struct xaccumulator_return_type + { + using type = xarray; + }; + + template + struct xaccumulator_return_type, R> + { + using type = xtensor; + }; + + template + using xaccumulator_return_type_t = typename xaccumulator_return_type::type; + + template + inline auto accumulator_init_with_f(F&& f, E& e, std::size_t axis) + { + // this function is the equivalent (but hopefully faster) to (if axis == 1) + // e[:, 0, :, :, ...] = f(e[:, 0, :, :, ...]) + // so that all "first" values are initialized in a first pass + + std::size_t outer_loop_size, inner_loop_size, outer_stride, inner_stride, pos = 0; + + auto set_loop_sizes = [&outer_loop_size, &inner_loop_size](auto first, auto last, std::ptrdiff_t ax) { + outer_loop_size = std::accumulate(first, first + ax, + std::size_t(1), std::multiplies()); + inner_loop_size = std::accumulate(first + ax + 1, last, + std::size_t(1), std::multiplies()); + }; + + auto set_loop_strides = [&outer_stride, &inner_stride](auto first, auto last, std::ptrdiff_t ax) { + outer_stride = ax == 0 ? 1 : *std::min_element(first, first + ax); + inner_stride = (ax == std::distance(first, last) - 1) ? 1 : *std::min_element(first + ax + 1, last); + }; + + set_loop_sizes(e.shape().begin(), e.shape().end(), static_cast(axis)); + set_loop_strides(e.strides().begin(), e.strides().end(), static_cast(axis)); + + if (e.layout() == layout_type::column_major) + { + // swap for better memory locality (smaller stride in the inner loop) + std::swap(outer_loop_size, inner_loop_size); + std::swap(outer_stride, inner_stride); + } + + for (std::size_t i = 0; i < outer_loop_size; ++i) + { + pos = i * outer_stride; + for (std::size_t j = 0; j < inner_loop_size; ++j) + { + e.storage()[pos] = f(e.storage()[pos]); + pos += inner_stride; + } + } + } + + template + inline auto accumulator_impl(F&& f, E&& e, std::size_t axis, evaluation_strategy::immediate) + { + using accumulate_functor = std::decay_t(f))>; + using function_return_type = typename accumulate_functor::result_type; + using result_type = xaccumulator_return_type_t, function_return_type>; + + if (axis >= e.dimension()) + { + throw std::runtime_error("Axis larger than expression dimension in accumulator."); + } + + result_type result = e; // assign + make a copy, we need it anyways + + std::size_t inner_stride = result.strides()[axis]; + std::size_t outer_stride = 1; // this is either going row- or column-wise (strides.back / strides.front) + std::size_t outer_loop_size = 0; + std::size_t inner_loop_size = 0; + + auto set_loop_sizes = [&outer_loop_size, &inner_loop_size](auto first, auto last, std::ptrdiff_t ax) { + outer_loop_size = std::accumulate(first, + first + ax, + std::size_t(1), std::multiplies()); + + inner_loop_size = std::accumulate(first + ax, + last, + std::size_t(1), std::multiplies()); + }; + + if (result_type::static_layout == layout_type::row_major) + { + set_loop_sizes(result.shape().cbegin(), result.shape().cend(), static_cast(axis)); + } + else + { + set_loop_sizes(result.shape().cbegin(), result.shape().cend(), static_cast(axis + 1)); + std::swap(inner_loop_size, outer_loop_size); + } + + std::size_t pos = 0; + + inner_loop_size = inner_loop_size - inner_stride; + + // activate the init loop if we have an init function other than identity + if (!std::is_same(f)), xtl::identity>::value) + { + accumulator_init_with_f(std::get<1>(f), result, axis); + } + + pos = 0; + for (std::size_t i = 0; i < outer_loop_size; ++i) + { + for (std::size_t j = 0; j < inner_loop_size; ++j) + { + result.storage()[pos + inner_stride] = std::get<0>(f)(result.storage()[pos], + result.storage()[pos + inner_stride]); + pos += outer_stride; + } + pos += inner_stride; + } + return result; + } + + template + inline auto accumulator_impl(F&& f, E&& e, evaluation_strategy::immediate) + { + using accumulate_functor = std::decay_t(f))>; + using T = typename accumulate_functor::result_type; + + using result_type = xtensor; + std::size_t sz = e.size(); + auto result = result_type::from_shape({sz}); + + auto it = e.template begin(); + + result.storage()[0] = std::get<1>(f)(*it); + ++it; + + for (std::size_t idx = 0; it != e.template end(); ++it) + { + result.storage()[idx + 1] = std::get<0>(f)(result.storage()[idx], *it); + ++idx; + } + return result; + } + } + + /** + * Accumulate and flatten array + * **NOTE** This function is not lazy! + * + * @param f functor to use for accumulation + * @param e xexpression to be accumulated + * @param evaluation_strategy evaluation strategy of the accumulation + * + * @return returns xarray filled with accumulated values + */ + template ::value, int> = 0> + inline auto accumulate(F&& f, E&& e, EVS evaluation_strategy = EVS()) + { + // Note we need to check is_integral above in order to prohibit EVS = int, and not taking the std::size_t + // overload below! + return detail::accumulator_impl(std::forward(f), std::forward(e), evaluation_strategy); + } + + /** + * Accumulate over axis + * **NOTE** This function is not lazy! + * + * @param f Functor to use for accumulation + * @param e xexpression to accumulate + * @param axis Axis to perform accumulation over + * @param evaluation_strategy evaluation strategy of the accumulation + * + * @return returns xarray filled with accumulated values + */ + template + inline auto accumulate(F&& f, E&& e, std::size_t axis, EVS evaluation_strategy = EVS()) + { + return detail::accumulator_impl(std::forward(f), std::forward(e), axis, evaluation_strategy); + } +} + +#endif diff --git a/vendor/xtensor/include/xtensor/xadapt.hpp b/vendor/xtensor/include/xtensor/xadapt.hpp new file mode 100644 index 0000000000..b080bd0b7d --- /dev/null +++ b/vendor/xtensor/include/xtensor/xadapt.hpp @@ -0,0 +1,322 @@ +/*************************************************************************** +* Copyright (c) 2016, Johan Mabille, Sylvain Corlay and Wolf Vollprecht * +* * +* Distributed under the terms of the BSD 3-Clause License. * +* * +* The full license is in the file LICENSE, distributed with this software. * +****************************************************************************/ + +#ifndef XTENSOR_ADAPT_HPP +#define XTENSOR_ADAPT_HPP + +#include +#include +#include +#include + +#include + +#include "xarray.hpp" +#include "xtensor.hpp" + +namespace xt +{ + namespace detail + { + template + struct array_size_impl; + + template + struct array_size_impl> + { + static constexpr std::size_t value = N; + }; + + template + using array_size = array_size_impl>; + } + + /************************** + * xarray_adaptor builder * + **************************/ + + /** + * Constructs an xarray_adaptor of the given stl-like container, + * with the specified shape and layout. + * @param container the container to adapt + * @param shape the shape of the xarray_adaptor + * @param l the layout_type of the xarray_adaptor + */ + template >::value, int> = 0> + xarray_adaptor, L, std::decay_t> + adapt(C&& container, const SC& shape, layout_type l = L); + + /** + * Constructs an xarray_adaptor of the given stl-like container, + * with the specified shape and strides. + * @param container the container to adapt + * @param shape the shape of the xarray_adaptor + * @param strides the strides of the xarray_adaptor + */ + template >::value, int> = 0, + typename std::enable_if_t>::value, int> = 0> + xarray_adaptor, layout_type::dynamic, std::decay_t> + adapt(C&& container, SC&& shape, SS&& strides); + + /** + * Constructs an xarray_adaptor of the given dynamically allocated C array, + * with the specified shape and layout. + * @param pointer the pointer to the beginning of the dynamic array + * @param size the size of the dynamic array + * @param ownership indicates whether the adaptor takes ownership of the array. + * Possible values are ``no_ownerhsip()`` or ``acquire_ownership()`` + * @param shape the shape of the xarray_adaptor + * @param l the layout_type of the xarray_adaptor + * @param alloc the allocator used for allocating / deallocating the dynamic array + */ + template >>>, + typename std::enable_if_t>::value, int> = 0> + xarray_adaptor, O, A>, L, SC> + adapt(P&& pointer, typename A::size_type size, O ownership, const SC& shape, layout_type l = L, const A& alloc = A()); + + /** + * Constructs an xarray_adaptor of the given dynamically allocated C array, + * with the specified shape and layout. + * @param pointer the pointer to the beginning of the dynamic array + * @param size the size of the dynamic array + * @param ownership indicates whether the adaptor takes ownership of the array. + * Possible values are ``no_ownerhsip()`` or ``acquire_ownership()`` + * @param shape the shape of the xarray_adaptor + * @param strides the strides of the xarray_adaptor + * @param alloc the allocator used for allocating / deallocating the dynamic array + */ + template >>>, + typename std::enable_if_t>::value, int> = 0, + typename std::enable_if_t>::value, int> = 0> + xarray_adaptor, O, A>, layout_type::dynamic, std::decay_t> + adapt(P&& pointer, typename A::size_type size, O ownership, SC&& shape, SS&& strides, const A& alloc = A()); + + /*************************** + * xtensor_adaptor builder * + ***************************/ + + /** + * Constructs a 1-D xtensor_adaptor of the given stl-like container, + * with the specified layout_type. + * @param container the container to adapt + * @param l the layout_type of the xtensor_adaptor + */ + template + xtensor_adaptor + adapt(C&& container, layout_type l = L); + + /** + * Constructs an xtensor_adaptor of the given stl-like container, + * with the specified shape and layout_type. + * @param container the container to adapt + * @param shape the shape of the xtensor_adaptor + * @param l the layout_type of the xtensor_adaptor + */ + template >::value, int> = 0> + xtensor_adaptor::value, L> + adapt(C&& container, const SC& shape, layout_type l = L); + + /** + * Constructs an xtensor_adaptor of the given stl-like container, + * with the specified shape and strides. + * @param container the container to adapt + * @param shape the shape of the xtensor_adaptor + * @param strides the strides of the xtensor_adaptor + */ + template >::value, int> = 0, + typename std::enable_if_t>::value, int> = 0> + xtensor_adaptor::value, layout_type::dynamic> + adapt(C&& container, SC&& shape, SS&& strides); + + /** + * Constructs a 1-D xtensor_adaptor of the given dynamically allocated C array, + * with the specified layout. + * @param pointer the pointer to the beginning of the dynamic array + * @param size the size of the dynamic array + * @param ownership indicates whether the adaptor takes ownership of the array. + * Possible values are ``no_ownerhsip()`` or ``acquire_ownership()`` + * @param l the layout_type of the xtensor_adaptor + * @param alloc the allocator used for allocating / deallocating the dynamic array + */ + template >>>> + xtensor_adaptor, O, A>, 1, L> + adapt(P&& pointer, typename A::size_type size, O ownership, layout_type l = L, const A& alloc = A()); + + /** + * Constructs an xtensor_adaptor of the given dynamically allocated C array, + * with the specified shape and layout. + * @param pointer the pointer to the beginning of the dynamic array + * @param size the size of the dynamic array + * @param ownership indicates whether the adaptor takes ownership of the array. + * Possible values are ``no_ownerhsip()`` or ``acquire_ownership()`` + * @param shape the shape of the xtensor_adaptor + * @param l the layout_type of the xtensor_adaptor + * @param alloc the allocator used for allocating / deallocating the dynamic array + */ + template >>>, + typename std::enable_if_t>::value, int> = 0> + xtensor_adaptor, O, A>, detail::array_size::value, L> + adapt(P&& pointer, typename A::size_type size, O ownership, const SC& shape, layout_type l = L, const A& alloc = A()); + + /** + * Constructs an xtensor_adaptor of the given dynamically allocated C array, + * with the specified shape and strides. + * @param pointer the pointer to the beginning of the dynamic array + * @param size the size of the dynamic array + * @param ownership indicates whether the adaptor takes ownership of the array. + * Possible values are ``no_ownerhsip()`` or ``acquire_ownership()`` + * @param shape the shape of the xtensor_adaptor + * @param strides the strides of the xtensor_adaptor + * @param alloc the allocator used for allocating / deallocating the dynamic array + */ + template >>>, + typename std::enable_if_t>::value, int> = 0, + typename std::enable_if_t>::value, int> = 0> + xtensor_adaptor, O, A>, detail::array_size::value, layout_type::dynamic> + adapt(P&& pointer, typename A::size_type size, O ownership, SC&& shape, SS&& strides, const A& alloc = A()); + + /***************************************** + * xarray_adaptor builder implementation * + *****************************************/ + + // shape only - container version + template >::value, int>> + inline xarray_adaptor, L, std::decay_t> + adapt(C&& container, const SC& shape, layout_type l) + { + using return_type = xarray_adaptor, L, std::decay_t>; + return return_type(std::forward(container), shape, l); + } + + // shape and strides - container version + template >::value, int>, + typename std::enable_if_t>::value, int>> + inline xarray_adaptor, layout_type::dynamic, std::decay_t> + adapt(C&& container, SC&& shape, SS&& strides) + { + using return_type = xarray_adaptor, layout_type::dynamic, std::decay_t>; + return return_type(std::forward(container), + xtl::forward_sequence(shape), + xtl::forward_sequence(strides)); + } + + // shape only - buffer version + template >::value, int>> + inline xarray_adaptor, O, A>, L, SC> + adapt(P&& pointer, typename A::size_type size, O, const SC& shape, layout_type l, const A& alloc) + { + using buffer_type = xbuffer_adaptor, O, A>; + using return_type = xarray_adaptor; + buffer_type buf(std::forward

(pointer), size, alloc); + return return_type(std::move(buf), shape, l); + } + + // shape and strides - buffer version + template >::value, int>, + typename std::enable_if_t>::value, int>> + inline xarray_adaptor, O, A>, layout_type::dynamic, std::decay_t> + adapt(P&& pointer, typename A::size_type size, O, SC&& shape, SS&& strides, const A& alloc) + { + using buffer_type = xbuffer_adaptor, O, A>; + using return_type = xarray_adaptor>; + buffer_type buf(std::forward

(pointer), size, alloc); + return return_type(std::move(buf), + xtl::forward_sequence(shape), + xtl::forward_sequence(strides)); + } + + /****************************************** + * xtensor_adaptor builder implementation * + ******************************************/ + + // 1-D case - container version + template + inline xtensor_adaptor + adapt(C&& container, layout_type l) + { + const std::array::size_type, 1> shape{container.size()}; + using return_type = xtensor_adaptor, 1, L>; + return return_type(std::forward(container), shape, l); + } + + // shape only - container version + template >::value, int>> + inline xtensor_adaptor::value, L> + adapt(C&& container, const SC& shape, layout_type l) + { + constexpr std::size_t N = detail::array_size::value; + using return_type = xtensor_adaptor, N, L>; + return return_type(std::forward(container), shape, l); + } + + // shape and strides - container version + template >::value, int>, + typename std::enable_if_t>::value, int>> + inline xtensor_adaptor::value, layout_type::dynamic> + adapt(C&& container, SC&& shape, SS&& strides) + { + constexpr std::size_t N = detail::array_size::value; + using return_type = xtensor_adaptor, N, layout_type::dynamic>; + return return_type(std::forward(container), + xtl::forward_sequence(shape), + xtl::forward_sequence(strides)); + } + + // 1-D case - buffer version + template + inline xtensor_adaptor, O, A>, 1, L> + adapt(P&& pointer, typename A::size_type size, O, layout_type l, const A& alloc) + { + using buffer_type = xbuffer_adaptor, O, A>; + using return_type = xtensor_adaptor; + buffer_type buf(std::forward

(pointer), size, alloc); + const std::array shape{size}; + return return_type(std::move(buf), shape, l); + } + + // shape only - buffer version + template >::value, int>> + inline xtensor_adaptor, O, A>, detail::array_size::value, L> + adapt(P&& pointer, typename A::size_type size, O, const SC& shape, layout_type l, const A& alloc) + { + using buffer_type = xbuffer_adaptor, O, A>; + constexpr std::size_t N = detail::array_size::value; + using return_type = xtensor_adaptor; + buffer_type buf(std::forward

(pointer), size, alloc); + return return_type(std::move(buf), shape, l); + } + + // shape and strides - buffer version + template >::value, int>, + typename std::enable_if_t>::value, int>> + inline xtensor_adaptor, O, A>, detail::array_size::value, layout_type::dynamic> + adapt(P&& pointer, typename A::size_type size, O, SC&& shape, SS&& strides, const A& alloc) + { + using buffer_type = xbuffer_adaptor, O, A>; + constexpr std::size_t N = detail::array_size::value; + using return_type = xtensor_adaptor; + buffer_type buf(std::forward

(pointer), size, alloc); + return return_type(std::move(buf), + xtl::forward_sequence(shape), + xtl::forward_sequence(strides)); + } +} + +#endif diff --git a/vendor/xtensor/include/xtensor/xarray.hpp b/vendor/xtensor/include/xtensor/xarray.hpp new file mode 100644 index 0000000000..7c51f28124 --- /dev/null +++ b/vendor/xtensor/include/xtensor/xarray.hpp @@ -0,0 +1,550 @@ +/*************************************************************************** +* Copyright (c) 2016, Johan Mabille, Sylvain Corlay and Wolf Vollprecht * +* * +* Distributed under the terms of the BSD 3-Clause License. * +* * +* The full license is in the file LICENSE, distributed with this software. * +****************************************************************************/ + +#ifndef XTENSOR_ARRAY_HPP +#define XTENSOR_ARRAY_HPP + +#include +#include +#include + +#include + +#include "xbuffer_adaptor.hpp" +#include "xcontainer.hpp" +#include "xsemantic.hpp" + +namespace xt +{ + + /******************************** + * xarray_container declaration * + ********************************/ + + template + struct xcontainer_inner_types> + { + using storage_type = EC; + using shape_type = SC; + using strides_type = shape_type; + using backstrides_type = shape_type; + using inner_shape_type = shape_type; + using inner_strides_type = strides_type; + using inner_backstrides_type = backstrides_type; + using temporary_type = xarray_container; + static constexpr layout_type layout = L; + }; + + template + struct xiterable_inner_types> + : xcontainer_iterable_types> + { + }; + + /** + * @class xarray_container + * @brief Dense multidimensional container with tensor semantic. + * + * The xarray_container class implements a dense multidimensional container + * with tensor semantic. + * + * @tparam EC The type of the container holding the elements. + * @tparam L The layout_type of the container. + * @tparam SC The type of the containers holding the shape and the strides. + * @tparam Tag The expression tag. + * @sa xarray + */ + template + class xarray_container : public xstrided_container>, + public xcontainer_semantic> + { + public: + + using self_type = xarray_container; + using base_type = xstrided_container; + using semantic_base = xcontainer_semantic; + using storage_type = typename base_type::storage_type; + using allocator_type = typename base_type::allocator_type; + using value_type = typename base_type::value_type; + using reference = typename base_type::reference; + using const_reference = typename base_type::const_reference; + using pointer = typename base_type::pointer; + using const_pointer = typename base_type::const_pointer; + using shape_type = typename base_type::shape_type; + using inner_shape_type = typename base_type::inner_shape_type; + using strides_type = typename base_type::strides_type; + using backstrides_type = typename base_type::backstrides_type; + using inner_strides_type = typename base_type::inner_strides_type; + using temporary_type = typename semantic_base::temporary_type; + using expression_tag = Tag; + + xarray_container(); + explicit xarray_container(const shape_type& shape, layout_type l = L); + explicit xarray_container(const shape_type& shape, const_reference value, layout_type l = L); + explicit xarray_container(const shape_type& shape, const strides_type& strides); + explicit xarray_container(const shape_type& shape, const strides_type& strides, const_reference value); + explicit xarray_container(storage_type&& storage, inner_shape_type&& shape, inner_strides_type&& strides); + + xarray_container(const value_type& t); + xarray_container(nested_initializer_list_t t); + xarray_container(nested_initializer_list_t t); + xarray_container(nested_initializer_list_t t); + xarray_container(nested_initializer_list_t t); + xarray_container(nested_initializer_list_t t); + + template + static xarray_container from_shape(S&& s); + + ~xarray_container() = default; + + xarray_container(const xarray_container&) = default; + xarray_container& operator=(const xarray_container&) = default; + + xarray_container(xarray_container&&) = default; + xarray_container& operator=(xarray_container&&) = default; + + template + xarray_container(const xexpression& e); + + template + xarray_container& operator=(const xexpression& e); + + private: + + storage_type m_storage; + + storage_type& storage_impl() noexcept; + const storage_type& storage_impl() const noexcept; + + friend class xcontainer>; + }; + + /****************************** + * xarray_adaptor declaration * + ******************************/ + + template + struct xcontainer_inner_types> + { + using storage_type = std::remove_reference_t; + using shape_type = SC; + using strides_type = shape_type; + using backstrides_type = shape_type; + using inner_shape_type = shape_type; + using inner_strides_type = strides_type; + using inner_backstrides_type = backstrides_type; + using temporary_type = xarray_container, L, SC, Tag>; + static constexpr layout_type layout = L; + }; + + template + struct xiterable_inner_types> + : xcontainer_iterable_types> + { + }; + + /** + * @class xarray_adaptor + * @brief Dense multidimensional container adaptor with + * tensor semantic. + * + * The xarray_adaptor class implements a dense multidimensional + * container adaptor with tensor semantic. It is used to provide + * a multidimensional container semantic and a tensor semantic to + * stl-like containers. + * + * @tparam EC The closure for the container type to adapt. + * @tparam L The layout_type of the adaptor. + * @tparam SC The type of the containers holding the shape and the strides. + * @tparam Tag The expression tag. + */ + template + class xarray_adaptor : public xstrided_container>, + public xcontainer_semantic> + { + public: + + using container_closure_type = EC; + + using self_type = xarray_adaptor; + using base_type = xstrided_container; + using semantic_base = xcontainer_semantic; + using storage_type = typename base_type::storage_type; + using allocator_type = typename base_type::allocator_type; + using shape_type = typename base_type::shape_type; + using strides_type = typename base_type::strides_type; + using backstrides_type = typename base_type::backstrides_type; + using temporary_type = typename semantic_base::temporary_type; + using expression_tag = Tag; + + xarray_adaptor(storage_type&& storage); + xarray_adaptor(const storage_type& storage); + + template + xarray_adaptor(D&& storage, const shape_type& shape, layout_type l = L); + + template + xarray_adaptor(D&& storage, const shape_type& shape, const strides_type& strides); + + ~xarray_adaptor() = default; + + xarray_adaptor(const xarray_adaptor&) = default; + xarray_adaptor& operator=(const xarray_adaptor&); + + xarray_adaptor(xarray_adaptor&&) = default; + xarray_adaptor& operator=(xarray_adaptor&&); + xarray_adaptor& operator=(temporary_type&&); + + template + xarray_adaptor& operator=(const xexpression& e); + + private: + + container_closure_type m_storage; + + storage_type& storage_impl() noexcept; + const storage_type& storage_impl() const noexcept; + + + friend class xcontainer>; + }; + + /*********************************** + * xarray_container implementation * + ***********************************/ + + /** + * @name Constructors + */ + //@{ + /** + * Allocates an uninitialized xarray_container that holds 0 element. + */ + template + inline xarray_container::xarray_container() + : base_type(), m_storage(1, value_type()) + { + } + + /** + * Allocates an uninitialized xarray_container with the specified shape and + * layout_type. + * @param shape the shape of the xarray_container + * @param l the layout_type of the xarray_container + */ + template + inline xarray_container::xarray_container(const shape_type& shape, layout_type l) + : base_type() + { + base_type::resize(shape, l); + } + + /** + * Allocates an xarray_container with the specified shape and layout_type. Elements + * are initialized to the specified value. + * @param shape the shape of the xarray_container + * @param value the value of the elements + * @param l the layout_type of the xarray_container + */ + template + inline xarray_container::xarray_container(const shape_type& shape, const_reference value, layout_type l) + : base_type() + { + base_type::resize(shape, l); + std::fill(m_storage.begin(), m_storage.end(), value); + } + + /** + * Allocates an uninitialized xarray_container with the specified shape and strides. + * @param shape the shape of the xarray_container + * @param strides the strides of the xarray_container + */ + template + inline xarray_container::xarray_container(const shape_type& shape, const strides_type& strides) + : base_type() + { + base_type::resize(shape, strides); + } + + /** + * Allocates an uninitialized xarray_container with the specified shape and strides. + * Elements are initialized to the specified value. + * @param shape the shape of the xarray_container + * @param strides the strides of the xarray_container + * @param value the value of the elements + */ + template + inline xarray_container::xarray_container(const shape_type& shape, const strides_type& strides, const_reference value) + : base_type() + { + base_type::resize(shape, strides); + std::fill(m_storage.begin(), m_storage.end(), value); + } + + /** + * Allocates an xarray_container that holds a single element initialized to the + * specified value. + * @param t the value of the element + */ + template + inline xarray_container::xarray_container(const value_type& t) + : base_type() + { + base_type::resize(xt::shape(t), true); + nested_copy(m_storage.begin(), t); + } + + /** + * Allocates an xarray_container by moving specified data, shape and strides + * + * @param storage the data for the xarray_container + * @param shape the shape of the xarray_container + * @param strides the strides of the xarray_container + */ + template + inline xarray_container::xarray_container(storage_type&& storage, inner_shape_type&& shape, inner_strides_type&& strides) + : base_type(std::move(shape), std::move(strides)), m_storage(std::move(storage)) + { + } + //@} + + /** + * @name Constructors from initializer list + */ + //@{ + /** + * Allocates a one-dimensional xarray_container. + * @param t the elements of the xarray_container + */ + template + inline xarray_container::xarray_container(nested_initializer_list_t t) + : base_type() + { + base_type::resize(xt::shape(t)); + L == layout_type::row_major ? nested_copy(m_storage.begin(), t) : nested_copy(this->template begin(), t); + } + + /** + * Allocates a two-dimensional xarray_container. + * @param t the elements of the xarray_container + */ + template + inline xarray_container::xarray_container(nested_initializer_list_t t) + : base_type() + { + base_type::resize(xt::shape(t)); + L == layout_type::row_major ? nested_copy(m_storage.begin(), t) : nested_copy(this->template begin(), t); + } + + /** + * Allocates a three-dimensional xarray_container. + * @param t the elements of the xarray_container + */ + template + inline xarray_container::xarray_container(nested_initializer_list_t t) + : base_type() + { + base_type::resize(xt::shape(t)); + L == layout_type::row_major ? nested_copy(m_storage.begin(), t) : nested_copy(this->template begin(), t); + } + + /** + * Allocates a four-dimensional xarray_container. + * @param t the elements of the xarray_container + */ + template + inline xarray_container::xarray_container(nested_initializer_list_t t) + : base_type() + { + base_type::resize(xt::shape(t)); + L == layout_type::row_major ? nested_copy(m_storage.begin(), t) : nested_copy(this->template begin(), t); + } + + /** + * Allocates a five-dimensional xarray_container. + * @param t the elements of the xarray_container + */ + template + inline xarray_container::xarray_container(nested_initializer_list_t t) + : base_type() + { + base_type::resize(xt::shape(t)); + L == layout_type::row_major ? nested_copy(m_storage.begin(), t) : nested_copy(this->template begin(), t); + } + //@} + + /** + * Allocates and returns an xarray_container with the specified shape. + * @param s the shape of the xarray_container + */ + template + template + inline xarray_container xarray_container::from_shape(S&& s) + { + shape_type shape = xtl::forward_sequence(s); + return self_type(shape); + } + + /** + * @name Extended copy semantic + */ + //@{ + /** + * The extended copy constructor. + */ + template + template + inline xarray_container::xarray_container(const xexpression& e) + : base_type() + { + // Avoids unintialized data because of (m_shape == shape) condition + // in resize (called by assign), which is always true when dimension == 0. + if (e.derived_cast().dimension() == 0) + { + detail::resize_data_container(m_storage, std::size_t(1)); + } + semantic_base::assign(e); + } + + /** + * The extended assignment operator. + */ + template + template + inline auto xarray_container::operator=(const xexpression& e) -> self_type& + { + return semantic_base::operator=(e); + } + //@} + + template + inline auto xarray_container::storage_impl() noexcept -> storage_type& + { + return m_storage; + } + + template + inline auto xarray_container::storage_impl() const noexcept -> const storage_type& + { + return m_storage; + } + + /****************** + * xarray_adaptor * + ******************/ + + /** + * @name Constructors + */ + //@{ + /** + * Constructs an xarray_adaptor of the given stl-like container. + * @param storage the container to adapt + */ + template + inline xarray_adaptor::xarray_adaptor(storage_type&& storage) + : base_type(), m_storage(std::move(storage)) + { + } + + /** + * Constructs an xarray_adaptor of the given stl-like container. + * @param storage the container to adapt + */ + template + inline xarray_adaptor::xarray_adaptor(const storage_type& storage) + : base_type(), m_storage(storage) + { + } + + /** + * Constructs an xarray_adaptor of the given stl-like container, + * with the specified shape and layout_type. + * @param storage the container to adapt + * @param shape the shape of the xarray_adaptor + * @param l the layout_type of the xarray_adaptor + */ + template + template + inline xarray_adaptor::xarray_adaptor(D&& storage, const shape_type& shape, layout_type l) + : base_type(), m_storage(std::forward(storage)) + { + base_type::resize(shape, l); + } + + /** + * Constructs an xarray_adaptor of the given stl-like container, + * with the specified shape and strides. + * @param storage the container to adapt + * @param shape the shape of the xarray_adaptor + * @param strides the strides of the xarray_adaptor + */ + template + template + inline xarray_adaptor::xarray_adaptor(D&& storage, const shape_type& shape, const strides_type& strides) + : base_type(), m_storage(std::forward(storage)) + { + base_type::resize(shape, strides); + } + //@} + + template + inline auto xarray_adaptor::operator=(const xarray_adaptor& rhs) -> self_type& + { + base_type::operator=(rhs); + m_storage = rhs.m_storage; + return *this; + } + + template + inline auto xarray_adaptor::operator=(xarray_adaptor&& rhs) -> self_type& + { + base_type::operator=(std::move(rhs)); + m_storage = rhs.m_storage; + return *this; + } + + template + inline auto xarray_adaptor::operator=(temporary_type&& rhs) -> self_type& + { + base_type::shape_impl() = std::move(const_cast(rhs.shape())); + base_type::strides_impl() = std::move(const_cast(rhs.strides())); + base_type::backstrides_impl() = std::move(const_cast(rhs.backstrides())); + m_storage = std::move(rhs.storage()); + return *this; + } + + /** + * @name Extended copy semantic + */ + //@{ + /** + * The extended assignment operator. + */ + template + template + inline auto xarray_adaptor::operator=(const xexpression& e) -> self_type& + { + return semantic_base::operator=(e); + } + //@} + + template + inline auto xarray_adaptor::storage_impl() noexcept -> storage_type& + { + return m_storage; + } + + template + inline auto xarray_adaptor::storage_impl() const noexcept -> const storage_type& + { + return m_storage; + } +} + +#endif diff --git a/vendor/xtensor/include/xtensor/xassign.hpp b/vendor/xtensor/include/xtensor/xassign.hpp new file mode 100644 index 0000000000..1b2f4c00d2 --- /dev/null +++ b/vendor/xtensor/include/xtensor/xassign.hpp @@ -0,0 +1,783 @@ +/*************************************************************************** +* Copyright (c) 2016, Johan Mabille, Sylvain Corlay and Wolf Vollprecht * +* * +* Distributed under the terms of the BSD 3-Clause License. * +* * +* The full license is in the file LICENSE, distributed with this software. * +****************************************************************************/ + +#ifndef XTENSOR_ASSIGN_HPP +#define XTENSOR_ASSIGN_HPP + +#include +#include +#include + +#include + +#include "xconcepts.hpp" +#include "xexpression.hpp" +#include "xiterator.hpp" +#include "xstrides.hpp" +#include "xtensor_forward.hpp" +#include "xutils.hpp" + +namespace xt +{ + + /******************** + * Assign functions * + ********************/ + + template + void assign_data(xexpression& e1, const xexpression& e2, bool trivial); + + template + void assign_xexpression(xexpression& e1, const xexpression& e2); + + template + void computed_assign(xexpression& e1, const xexpression& e2); + + template + void scalar_computed_assign(xexpression& e1, const E2& e2, F&& f); + + template + void assert_compatible_shape(const xexpression& e1, const xexpression& e2); + + template + void strided_assign(E1& e1, const E2& e2, std::false_type /*disable*/); + + template + void strided_assign(E1& e1, const E2& e2, std::true_type /*enable*/); + + /************************ + * xexpression_assigner * + ************************/ + + template + class xexpression_assigner_base; + + template <> + class xexpression_assigner_base + { + public: + + template + static void assign_data(xexpression& e1, const xexpression& e2, bool trivial); + }; + + template + class xexpression_assigner : public xexpression_assigner_base + { + public: + + using base_type = xexpression_assigner_base; + + template + static void assign_xexpression(xexpression& e1, const xexpression& e2); + + template + static void computed_assign(xexpression& e1, const xexpression& e2); + + template + static void scalar_computed_assign(xexpression& e1, const E2& e2, F&& f); + + template + static void assert_compatible_shape(const xexpression& e1, const xexpression& e2); + + private: + + template + static bool resize(xexpression& e1, const xexpression& e2); + }; + + /***************** + * data_assigner * + *****************/ + + template + class data_assigner + { + public: + + using lhs_iterator = typename E1::stepper; + using rhs_iterator = typename E2::const_stepper; + using shape_type = typename E1::shape_type; + using index_type = xindex_type_t; + using size_type = typename lhs_iterator::size_type; + using difference_type = typename lhs_iterator::difference_type; + + data_assigner(E1& e1, const E2& e2); + + void run(); + + void step(size_type i); + void step(size_type i, size_type n); + void reset(size_type i); + + void to_end(layout_type); + + private: + + E1& m_e1; + + lhs_iterator m_lhs; + rhs_iterator m_rhs; + + index_type m_index; + }; + + /******************** + * trivial_assigner * + ********************/ + + template + struct trivial_assigner + { + template + static void run(E1& e1, const E2& e2); + }; + + /*********************************** + * Assign functions implementation * + ***********************************/ + + template + inline void assign_data(xexpression& e1, const xexpression& e2, bool trivial) + { + using tag = xexpression_tag_t; + xexpression_assigner::assign_data(e1, e2, trivial); + } + + template + inline void assign_xexpression(xexpression& e1, const xexpression& e2) + { + xtl::mpl::static_if::value>([&](auto self) + { + self(e2).derived_cast().assign_to(e1); + }, /*else*/ [&](auto /*self*/) + { + using tag = xexpression_tag_t; + xexpression_assigner::assign_xexpression(e1, e2); + }); + } + + template + inline void computed_assign(xexpression& e1, const xexpression& e2) + { + using tag = xexpression_tag_t; + xexpression_assigner::computed_assign(e1, e2); + } + + template + inline void scalar_computed_assign(xexpression& e1, const E2& e2, F&& f) + { + using tag = xexpression_tag_t; + xexpression_assigner::scalar_computed_assign(e1, e2, std::forward(f)); + } + + template + inline void assert_compatible_shape(const xexpression& e1, const xexpression& e2) + { + using tag = xexpression_tag_t; + xexpression_assigner::assert_compatible_shape(e1, e2); + } + + /*************************************** + * xexpression_assigner implementation * + ***************************************/ + + namespace detail + { + template + inline bool is_trivial_broadcast(const E1& e1, const E2& e2) + { + return (E1::contiguous_layout && E2::contiguous_layout && (E1::static_layout == E2::static_layout)) + || e2.is_trivial_broadcast(e1.strides()); + } + + template + inline bool is_trivial_broadcast(const xview&, const E2&) + { + return false; + } + + template > + struct forbid_simd_assign + { + static constexpr bool value = true; + }; + + // Double steps check for xfunction because the default + // parameter void_t of forbid_simd_assign prevents additional + // specializations. + template + struct xfunction_forbid_simd; + + template + struct forbid_simd_assign().template load_simd(typename E::size_type(0)))>> + { + static constexpr bool value = false || xfunction_forbid_simd::value; + }; + + template + struct xfunction_forbid_simd + { + static constexpr bool value = false; + }; + + template + struct xfunction_forbid_simd> + { + static constexpr bool value = xtl::disjunction< + std::integral_constant::type>::value>...>::value; + }; + + template + struct has_simd_apply : std::false_type {}; + + template + struct has_simd_apply)>> + : std::true_type + { + }; + + template + struct has_step_leading : std::false_type + { + }; + + template + struct has_step_leading().step_leading())>> + : std::true_type + { + }; + + template + struct use_strided_loop + { + static constexpr bool stepper_deref() { return std::is_reference::value; } + static constexpr bool value = has_strides::value && has_step_leading::value && stepper_deref(); + }; + + template + struct use_strided_loop> + { + static constexpr bool value = true; + }; + + template + struct use_strided_loop> + { + static constexpr bool value = xtl::conjunction>...>::value && + has_simd_apply>::value; + }; + } + + template + struct xassign_traits + { + // constexpr methods instead of constexpr data members avoid the need of difinitions at namespace + // scope of these data members (since they are odr-used). + static constexpr bool contiguous_layout() { return E1::contiguous_layout && E2::contiguous_layout; } + static constexpr bool same_type() { return std::is_same::value; } + static constexpr bool simd_size() { return xsimd::simd_traits::size > 1; } + static constexpr bool forbid_simd() { return detail::forbid_simd_assign::value; } + static constexpr bool simd_assign() { return contiguous_layout() && same_type() && simd_size() && !forbid_simd(); } + static constexpr bool simd_strided_loop() { return same_type() && simd_size() && detail::use_strided_loop::value && detail::use_strided_loop::value; } + }; + + template + inline void xexpression_assigner_base::assign_data(xexpression& e1, const xexpression& e2, bool trivial) + { + E1& de1 = e1.derived_cast(); + const E2& de2 = e2.derived_cast(); + + bool trivial_broadcast = trivial && detail::is_trivial_broadcast(de1, de2); + + if (trivial_broadcast) + { + constexpr bool simd_assign = xassign_traits::simd_assign(); + trivial_assigner::run(de1, de2); + } + else if (xassign_traits::simd_strided_loop()) + { + strided_assign(de1, de2, std::integral_constant::simd_strided_loop()>{}); + } + else + { + data_assigner assigner(de1, de2); + assigner.run(); + } + } + + template + template + inline void xexpression_assigner::assign_xexpression(xexpression& e1, const xexpression& e2) + { + bool trivial_broadcast = resize(e1, e2); + base_type::assign_data(e1, e2, trivial_broadcast); + } + + template + template + inline void xexpression_assigner::computed_assign(xexpression& e1, const xexpression& e2) + { + using shape_type = typename E1::shape_type; + using size_type = typename E1::size_type; + + E1& de1 = e1.derived_cast(); + const E2& de2 = e2.derived_cast(); + + size_type dim = de2.dimension(); + shape_type shape = xtl::make_sequence(dim, size_type(0)); + bool trivial_broadcast = de2.broadcast_shape(shape, true); + + if (dim > de1.dimension() || shape > de1.shape()) + { + typename E1::temporary_type tmp(shape); + base_type::assign_data(tmp, e2, trivial_broadcast); + de1.assign_temporary(std::move(tmp)); + } + else + { + base_type::assign_data(e1, e2, trivial_broadcast); + } + } + + template + template + inline void xexpression_assigner::scalar_computed_assign(xexpression& e1, const E2& e2, F&& f) + { + E1& d = e1.derived_cast(); + using size_type = typename E1::size_type; + auto dst = d.storage().begin(); + for (size_type i = d.size(); i > 0; --i) + { + *dst = f(*dst, e2); + ++dst; + } + } + + template + template + inline void xexpression_assigner::assert_compatible_shape(const xexpression& e1, const xexpression& e2) + { + const E1& de1 = e1.derived_cast(); + const E2& de2 = e2.derived_cast(); + if (!broadcastable(de2.shape(), de1.shape())) + { + throw_broadcast_error(de2.shape(), de1.shape()); + } + } + + template + template + inline bool xexpression_assigner::resize(xexpression& e1, const xexpression& e2) + { + using shape_type = typename E1::shape_type; + using size_type = typename E1::size_type; + const E2& de2 = e2.derived_cast(); + size_type size = de2.dimension(); + shape_type shape = xtl::make_sequence(size, size_type(0)); + bool trivial_broadcast = de2.broadcast_shape(shape, true); + e1.derived_cast().resize(std::move(shape)); + return trivial_broadcast; + } + + /******************************** + * data_assigner implementation * + ********************************/ + + template + inline data_assigner::data_assigner(E1& e1, const E2& e2) + : m_e1(e1), m_lhs(e1.stepper_begin(e1.shape())), + m_rhs(e2.stepper_begin(e1.shape())), + m_index(xtl::make_sequence(e1.shape().size(), size_type(0))) + { + } + + template + inline void data_assigner::run() + { + using size_type = typename E1::size_type; + using argument_type = std::decay_t; + using result_type = std::decay_t; + constexpr bool is_narrowing = is_narrowing_conversion::value; + + size_type s = m_e1.size(); + for (size_type i = 0; i < s; ++i) + { + *m_lhs = conditional_cast(*m_rhs); + stepper_tools::increment_stepper(*this, m_index, m_e1.shape()); + } + } + + template + inline void data_assigner::step(size_type i) + { + m_lhs.step(i); + m_rhs.step(i); + } + + template + inline void data_assigner::step(size_type i, size_type n) + { + m_lhs.step(i, n); + m_rhs.step(i, n); + } + + template + inline void data_assigner::reset(size_type i) + { + m_lhs.reset(i); + m_rhs.reset(i); + } + + template + inline void data_assigner::to_end(layout_type l) + { + m_lhs.to_end(l); + m_rhs.to_end(l); + } + + /*********************************** + * trivial_assigner implementation * + ***********************************/ + + template + template + inline void trivial_assigner::run(E1& e1, const E2& e2) + { + using lhs_align_mode = xsimd::container_alignment_t; + constexpr bool is_aligned = std::is_same::value; + using rhs_align_mode = std::conditional_t; + using value_type = std::common_type_t; + using simd_type = xsimd::simd_type; + using size_type = typename E1::size_type; + size_type size = e1.size(); + size_type simd_size = simd_type::size; + + size_type align_begin = is_aligned ? 0 : xsimd::get_alignment_offset(e1.data(), size, simd_size); + size_type align_end = align_begin + ((size - align_begin) & ~(simd_size - 1)); + + for (size_type i = 0; i < align_begin; ++i) + { + e1.data_element(i) = e2.data_element(i); + } + for (size_type i = align_begin; i < align_end; i += simd_size) + { + e1.template store_simd(i, e2.template load_simd(i)); + } + for (size_type i = align_end; i < size; ++i) + { + e1.data_element(i) = e2.data_element(i); + } + } + + namespace assigner_detail + { + template + inline void assign_loop(It src, Ot dst, std::size_t n) + { + for(; n > 0; --n) + { + *dst = static_cast(*src); + ++src; + ++dst; + } + } + + template + inline void trivial_assigner_run_impl(E1& e1, const E2& e2, std::true_type) + { + using size_type = typename E1::size_type; + auto src = e2.storage_cbegin(); + auto dst = e1.storage_begin(); + assign_loop(src, dst, e1.size()); + } + + template + inline void trivial_assigner_run_impl(E1&, const E2&, std::false_type) + { + XTENSOR_PRECONDITION(false, + "Internal error: trivial_assigner called with unrelated types."); + } + } + + template <> + template + inline void trivial_assigner::run(E1& e1, const E2& e2) + { + using is_convertible = std::is_convertible::value_type, + typename std::decay_t::value_type>; + // If the types are not compatible, this function is still instantiated but never called. + // To avoid compilation problems in effectively unused code trivial_assigner_run_impl is + // empty in this case. + assigner_detail::trivial_assigner_run_impl(e1, e2, is_convertible()); + } + + /*********************** + * Strided assign loop * + ***********************/ + + namespace strided_assign_detail + { + template + struct idx_tools; + + template <> + struct idx_tools + { + template + static void next_idx(T& outer_index, T& outer_shape) + { + auto i = outer_index.size(); + for (; i > 0; --i) + { + if (outer_index[i - 1] + 1 >= outer_shape[i - 1]) + { + outer_index[i - 1] = 0; + } + else + { + outer_index[i - 1]++; + break; + } + } + } + }; + + template <> + struct idx_tools + { + template + static void next_idx(T& outer_index, T& outer_shape) + { + using size_type = typename T::size_type; + size_type i = 0; + auto sz = outer_index.size(); + for (; i < sz; ++i) + { + if (outer_index[i] + 1 >= outer_shape[i]) + { + outer_index[i] = 0; + } + else + { + outer_index[i]++; + break; + } + } + } + }; + + template + struct check_strides_functor + { + using strides_type = S; + + check_strides_functor(const S& strides) + : m_cut(L == layout_type::row_major ? 0 : strides.size()), + m_strides(strides) + { + } + + template + std::enable_if_t + operator()(const T& el) + { + auto var = check_strides_overlap::get(m_strides, el.strides()); + if (var > m_cut) + { + m_cut = var; + } + return m_cut; + } + + template + std::enable_if_t + operator()(const T& el) + { + auto var = check_strides_overlap::get(m_strides, el.strides()); + if (var < m_cut) + { + m_cut = var; + } + return m_cut; + } + + template + std::size_t operator()(const xt::xscalar& /*el*/) + { + return m_cut; + } + + template + std::size_t operator()(const xt::xfunction& xf) + { + xt::for_each(*this, xf.arguments()); + return m_cut; + } + + private: + + std::size_t m_cut; + const strides_type& m_strides; + }; + + template + auto get_loop_sizes(const E1& e1, const E2& e2) + { + std::size_t cut = 0; + + // TODO! if E1 is !contigous --> initialize cut to sensible value! + if (e1.strides().back() == 1) + { + auto csf = check_strides_functor(e1.strides()); + cut = csf(e2); + } + else if (e1.strides().front() == 1) + { + auto csf = check_strides_functor(e1.strides()); + cut = csf(e2); + } + + using shape_value_type = typename E1::shape_type::value_type; + std::size_t outer_loop_size = static_cast( + std::accumulate(e1.shape().begin(), e1.shape().begin() + static_cast(cut), + shape_value_type(1), std::multiplies{})); + std::size_t inner_loop_size = static_cast( + std::accumulate(e1.shape().begin() + static_cast(cut), e1.shape().end(), + shape_value_type(1), std::multiplies{})); + + if (e1.strides().back() != 1) // column major mode + { + std::swap(outer_loop_size, inner_loop_size); + } + + return std::make_tuple(inner_loop_size, outer_loop_size, cut); + } + } + + template + void strided_assign(E1& e1, const E2& e2, std::true_type /*enable*/) + { + bool fallback = false, is_row_major = true; + + std::size_t inner_loop_size, outer_loop_size, cut; + std::tie(inner_loop_size, outer_loop_size, cut) = strided_assign_detail::get_loop_sizes(e1, e2); + + if (E1::static_layout == layout_type::row_major || e1.strides().back() == 1) // row major case + { + if (cut == e1.dimension()) + { + fallback = true; + } + } + else if (E1::static_layout == layout_type::column_major || e1.strides().front() == 1) // col major case + { + is_row_major = false; + if (cut == 0) + { + fallback = true; + } + } + else + { + fallback = true; + } + + if (fallback) + { + data_assigner assigner(e1, e2); + assigner.run(); + return; + } + + // TODO can we get rid of this and use `shape_type`? + dynamic_shape idx; + + using iterator_type = decltype(e1.shape().begin()); + iterator_type max_shape_begin, max_shape_end; + if (is_row_major) + { + xt::resize_container(idx, cut); + max_shape_begin = e1.shape().begin(); + max_shape_end = e1.shape().begin() + static_cast(cut); + } + else + { + xt::resize_container(idx, e1.shape().size() - cut); + max_shape_begin = e1.shape().begin() + static_cast(cut); + max_shape_end = e1.shape().end(); + } + + // add this when we have std::array index! + // std::fill(idx.begin(), idx.end(), 0); + + dynamic_shape max(max_shape_begin, max_shape_end); + + using simd_type = xsimd::simd_type; + + std::size_t simd_size = inner_loop_size / simd_type::size; + std::size_t simd_rest = inner_loop_size % simd_type::size; + + auto fct_stepper = e2.stepper_begin(e1.shape()); + auto res_stepper = e1.stepper_begin(e1.shape()); + + // TODO in 1D case this is ambigous -- could be RM or CM. + // Use default layout to make decision + std::size_t step_dim = 0; + if (!is_row_major) // row major case + { + step_dim = cut; + } + + for (std::size_t ox = 0; ox < outer_loop_size; ++ox) + { + for (std::size_t i = 0; i < simd_size; i++) + { + res_stepper.template store_simd(fct_stepper.template step_simd()); + } + for (std::size_t i = 0; i < simd_rest; ++i) + { + *(res_stepper) = *(fct_stepper); + res_stepper.step_leading(); + fct_stepper.step_leading(); + } + + is_row_major ? + strided_assign_detail::idx_tools::next_idx(idx, max) : + strided_assign_detail::idx_tools::next_idx(idx, max); + + fct_stepper.to_begin(); + + // need to step E1 as well if not contigous assign (e.g. view) + if (!E1::contiguous_layout) + { + res_stepper.to_begin(); + for (std::size_t i = 0; i < idx.size(); ++i) + { + fct_stepper.step(i + step_dim, idx[i]); + res_stepper.step(i + step_dim, idx[i]); + } + } + else + { + for (std::size_t i = 0; i < idx.size(); ++i) + { + fct_stepper.step(i + step_dim, idx[i]); + } + } + } + } + + template + inline void strided_assign(E1& /*e1*/, const E2& /*e2*/, std::false_type /*disable*/) + { + } +} + +#endif diff --git a/vendor/xtensor/include/xtensor/xaxis_iterator.hpp b/vendor/xtensor/include/xtensor/xaxis_iterator.hpp new file mode 100644 index 0000000000..e544791b82 --- /dev/null +++ b/vendor/xtensor/include/xtensor/xaxis_iterator.hpp @@ -0,0 +1,197 @@ +/*************************************************************************** +* Copyright (c) 2016, Johan Mabille, Sylvain Corlay and Wolf Vollprecht * +* * +* Distributed under the terms of the BSD 3-Clause License. * +* * +* The full license is in the file LICENSE, distributed with this software. * +****************************************************************************/ + +#ifndef XTENSOR_AXIS_ITERATOR_HPP +#define XTENSOR_AXIS_ITERATOR_HPP + +#include + +#include "xview.hpp" + +namespace xt +{ + + /****************** + * xaxis_iterator * + ******************/ + + template + class xaxis_iterator + { + public: + + using self_type = xaxis_iterator; + + using xexpression_type = std::decay_t; + using size_type = typename xexpression_type::size_type; + using difference_type = typename xexpression_type::difference_type; + using value_type = xview; + using reference = std::remove_reference_t>; + using pointer = xtl::xclosure_pointer>>; + + using iterator_category = std::forward_iterator_tag; + + xaxis_iterator(); + template + xaxis_iterator(CTA&& e, size_type index); + + self_type& operator++(); + self_type operator++(int); + + reference operator*() const; + pointer operator->() const; + + bool equal(const self_type& rhs) const; + + private: + + using storing_type = xtl::ptr_closure_type_t; + mutable storing_type p_expression; + size_type m_index; + + template + std::enable_if_t::value, std::add_lvalue_reference_t>> + deref(T val) const; + + template + std::enable_if_t::value, T> + deref(T& val) const; + + template + std::enable_if_t::value, T> + get_storage_init(CTA&& e) const; + + template + std::enable_if_t::value, T> + get_storage_init(CTA&& e) const; + }; + + template + bool operator==(const xaxis_iterator& lhs, const xaxis_iterator& rhs); + + template + bool operator!=(const xaxis_iterator& lhs, const xaxis_iterator& rhs); + + template + auto axis_begin(E&& e); + + template + auto axis_end(E&& e); + + /********************************* + * xaxis_iterator implementation * + *********************************/ + + template + template + inline std::enable_if_t::value, std::add_lvalue_reference_t>> + xaxis_iterator::deref(T val) const + { + return *val; + } + + template + template + inline std::enable_if_t::value, T> + xaxis_iterator::deref(T& val) const + { + return val; + } + + template + template + inline std::enable_if_t::value, T> + xaxis_iterator::get_storage_init(CTA&& e) const + { + return &e; + } + + template + template + inline std::enable_if_t::value, T> + xaxis_iterator::get_storage_init(CTA&& e) const + { + return e; + } + + template + inline xaxis_iterator::xaxis_iterator() + : p_expression(nullptr), m_index(0) + { + } + + template + template + inline xaxis_iterator::xaxis_iterator(CTA&& e, size_type index) + : p_expression(get_storage_init(std::forward(e))), m_index(index) + { + } + + template + inline auto xaxis_iterator::operator++() -> self_type& + { + ++m_index; + return *this; + } + + template + inline auto xaxis_iterator::operator++(int) -> self_type + { + self_type tmp(*this); + ++(*this); + return tmp; + } + + template + inline auto xaxis_iterator::operator*() const -> reference + { + return view(deref(p_expression), size_type(m_index)); + } + + template + inline auto xaxis_iterator::operator->() const -> pointer + { + return xtl::closure_pointer(operator*()); + } + + template + inline bool xaxis_iterator::equal(const self_type& rhs) const + { + return p_expression == rhs.p_expression && m_index == rhs.m_index; + } + + template + inline bool operator==(const xaxis_iterator& lhs, const xaxis_iterator& rhs) + { + return lhs.equal(rhs); + } + + template + inline bool operator!=(const xaxis_iterator& lhs, const xaxis_iterator& rhs) + { + return !(lhs == rhs); + } + + template + inline auto axis_begin(E&& e) + { + using return_type = xaxis_iterator>; + using size_type = typename std::decay_t::size_type; + return return_type(std::forward(e), size_type(0)); + } + + template + inline auto axis_end(E&& e) + { + using return_type = xaxis_iterator>; + using size_type = typename std::decay_t::size_type; + return return_type(std::forward(e), size_type(e.shape()[0])); + } +} + +#endif diff --git a/vendor/xtensor/include/xtensor/xbroadcast.hpp b/vendor/xtensor/include/xtensor/xbroadcast.hpp new file mode 100644 index 0000000000..77c04f0416 --- /dev/null +++ b/vendor/xtensor/include/xtensor/xbroadcast.hpp @@ -0,0 +1,412 @@ +/*************************************************************************** +* Copyright (c) 2016, Johan Mabille, Sylvain Corlay and Wolf Vollprecht * +* * +* Distributed under the terms of the BSD 3-Clause License. * +* * +* The full license is in the file LICENSE, distributed with this software. * +****************************************************************************/ + +#ifndef XTENSOR_BROADCAST_HPP +#define XTENSOR_BROADCAST_HPP + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "xexpression.hpp" +#include "xiterable.hpp" +#include "xscalar.hpp" +#include "xstrides.hpp" +#include "xutils.hpp" + +namespace xt +{ + + /************* + * broadcast * + *************/ + + template + auto broadcast(E&& e, const S& s); + +#ifdef X_OLD_CLANG + template + auto broadcast(E&& e, std::initializer_list s); +#else + template + auto broadcast(E&& e, const I (&s)[L]); +#endif + + /************** + * xbroadcast * + **************/ + + template + class xbroadcast; + + template + struct xiterable_inner_types> + { + using xexpression_type = std::decay_t; + using inner_shape_type = promote_shape_t; + using const_stepper = typename xexpression_type::const_stepper; + using stepper = const_stepper; + }; + + /** + * @class xbroadcast + * @brief Broadcasted xexpression to a specified shape. + * + * The xbroadcast class implements the broadcasting of an \ref xexpression + * to a specified shape. xbroadcast is not meant to be used directly, but + * only with the \ref broadcast helper functions. + * + * @tparam CT the closure type of the \ref xexpression to broadcast + * @tparam X the type of the specified shape. + * + * @sa broadcast + */ + template + class xbroadcast : public xexpression>, + public xconst_iterable> + { + public: + + using self_type = xbroadcast; + using xexpression_type = std::decay_t; + + using value_type = typename xexpression_type::value_type; + using reference = typename xexpression_type::reference; + using const_reference = typename xexpression_type::const_reference; + using pointer = typename xexpression_type::pointer; + using const_pointer = typename xexpression_type::const_pointer; + using size_type = typename xexpression_type::size_type; + using difference_type = typename xexpression_type::difference_type; + + using iterable_base = xconst_iterable; + using inner_shape_type = typename iterable_base::inner_shape_type; + using shape_type = inner_shape_type; + + using stepper = typename iterable_base::stepper; + using const_stepper = typename iterable_base::const_stepper; + + static constexpr layout_type static_layout = xexpression_type::static_layout; + //static constexpr bool contiguous_layout = xexpression_type::contiguous_layout; + static constexpr bool contiguous_layout = false; + + template + xbroadcast(CTA&& e, S&& s); + + size_type size() const noexcept; + size_type dimension() const noexcept; + const inner_shape_type& shape() const noexcept; + layout_type layout() const noexcept; + + template + const_reference operator()(Args... args) const; + + template + const_reference at(Args... args) const; + + template + const_reference unchecked(Args... args) const; + + template + disable_integral_t operator[](const S& index) const; + template + const_reference operator[](std::initializer_list index) const; + const_reference operator[](size_type i) const; + + template + const_reference element(It first, It last) const; + + template + bool broadcast_shape(S& shape, bool reuse_cache = false) const; + + template + bool is_trivial_broadcast(const S& strides) const noexcept; + + template + const_stepper stepper_begin(const S& shape) const noexcept; + template + const_stepper stepper_end(const S& shape, layout_type l) const noexcept; + + template ::value>> + void assign_to(xexpression& e) const; + + private: + + CT m_e; + inner_shape_type m_shape; + }; + + /**************************** + * broadcast implementation * + ****************************/ + + /** + * @brief Returns an \ref xexpression broadcasting the given expression to + * a specified shape. + * + * @tparam e the \ref xexpression to broadcast + * @tparam s the specified shape to broadcast. + * + * The returned expression either hold a const reference to \p e or a copy + * depending on whether \p e is an lvalue or an rvalue. + */ + template + inline auto broadcast(E&& e, const S& s) + { + using broadcast_type = xbroadcast, S>; + using shape_type = typename broadcast_type::shape_type; + return broadcast_type(std::forward(e), xtl::forward_sequence(s)); + } + +#ifdef X_OLD_CLANG + template + inline auto broadcast(E&& e, std::initializer_list s) + { + using broadcast_type = xbroadcast, std::vector>; + using shape_type = typename broadcast_type::shape_type; + return broadcast_type(std::forward(e), xtl::forward_sequence(s)); + } +#else + template + inline auto broadcast(E&& e, const I (&s)[L]) + { + using broadcast_type = xbroadcast, std::array>; + using shape_type = typename broadcast_type::shape_type; + return broadcast_type(std::forward(e), xtl::forward_sequence(s)); + } +#endif + + /***************************** + * xbroadcast implementation * + *****************************/ + + /** + * @name Constructor + */ + //@{ + /** + * Constructs an xbroadcast expression broadcasting the specified + * \ref xexpression to the given shape + * + * @param e the expression to broadcast + * @param s the shape to apply + */ + template + template + inline xbroadcast::xbroadcast(CTA&& e, S&& s) + : m_e(std::forward(e)), m_shape(std::forward(s)) + { + xt::broadcast_shape(m_e.shape(), m_shape); + } + //@} + + /** + * @name Size and shape + */ + /** + * Returns the size of the expression. + */ + template + inline auto xbroadcast::size() const noexcept -> size_type + { + return compute_size(shape()); + } + + /** + * Returns the number of dimensions of the expression. + */ + template + inline auto xbroadcast::dimension() const noexcept -> size_type + { + return m_shape.size(); + } + + /** + * Returns the shape of the expression. + */ + template + inline auto xbroadcast::shape() const noexcept -> const inner_shape_type& + { + return m_shape; + } + + /** + * Returns the layout_type of the expression. + */ + template + inline layout_type xbroadcast::layout() const noexcept + { + return m_e.layout(); + } + //@} + + /** + * @name Data + */ + /** + * Returns a constant reference to the element at the specified position in the expression. + * @param args a list of indices specifying the position in the function. Indices + * must be unsigned integers, the number of indices should be equal or greater than + * the number of dimensions of the expression. + */ + template + template + inline auto xbroadcast::operator()(Args... args) const -> const_reference + { + return m_e(args...); + } + + /** + * Returns a constant reference to the element at the specified position in the expression, + * after dimension and bounds checking. + * @param args a list of indices specifying the position in the function. Indices + * must be unsigned integers, the number of indices should be equal to the number of dimensions + * of the expression. + * @exception std::out_of_range if the number of argument is greater than the number of dimensions + * or if indices are out of bounds. + */ + template + template + inline auto xbroadcast::at(Args... args) const -> const_reference + { + check_access(shape(), static_cast(args)...); + return this->operator()(args...); + } + + /** + * Returns a constant reference to the element at the specified position in the expression. + * @param args a list of indices specifying the position in the expression. Indices + * must be unsigned integers, the number of indices must be equal to the number of + * dimensions of the expression, else the behavior is undefined. + * + * @warning This method is meant for performance, for expressions with a dynamic + * number of dimensions (i.e. not known at compile time). Since it may have + * undefined behavior (see parameters), operator() should be prefered whenever + * it is possible. + * @warning This method is NOT compatible with broadcasting, meaning the following + * code has undefined behavior: + * \code{.cpp} + * xt::xarray a = {{0, 1}, {2, 3}}; + * xt::xarray b = {0, 1}; + * auto fd = a + b; + * double res = fd.uncheked(0, 1); + * \endcode + */ + template + template + inline auto xbroadcast::unchecked(Args... args) const -> const_reference + { + return this->operator()(args...); + } + + /** + * Returns a constant reference to the element at the specified position in the expression. + * @param index a sequence of indices specifying the position in the function. Indices + * must be unsigned integers, the number of indices in the sequence should be equal or greater + * than the number of dimensions of the container. + */ + template + template + inline auto xbroadcast::operator[](const S& index) const + -> disable_integral_t + { + return element(index.cbegin(), index.cend()); + } + + template + template + inline auto xbroadcast::operator[](std::initializer_list index) const -> const_reference + { + return element(index.begin(), index.end()); + } + + template + inline auto xbroadcast::operator[](size_type i) const -> const_reference + { + return operator()(i); + } + + /** + * Returns a constant reference to the element at the specified position in the expression. + * @param first iterator starting the sequence of indices + * @param last iterator ending the sequence of indices + * The number of indices in the sequence should be equal to or greater + * than the number of dimensions of the function. + */ + template + template + inline auto xbroadcast::element(It, It last) const -> const_reference + { + return m_e.element(last - dimension(), last); + } + //@} + + /** + * @name Broadcasting + */ + //@{ + /** + * Broadcast the shape of the function to the specified parameter. + * @param shape the result shape + * @param reuse_cache parameter for internal optimization + * @return a boolean indicating whether the broadcasting is trivial + */ + template + template + inline bool xbroadcast::broadcast_shape(S& shape, bool) const + { + return xt::broadcast_shape(m_shape, shape); + } + + /** + * Compares the specified strides with those of the container to see whether + * the broadcasting is trivial. + * @return a boolean indicating whether the broadcasting is trivial + */ + template + template + inline bool xbroadcast::is_trivial_broadcast(const S& strides) const noexcept + { + return dimension() == m_e.dimension() && + std::equal(m_shape.cbegin(), m_shape.cend(), m_e.shape().cbegin()) && + m_e.is_trivial_broadcast(strides); + } + //@} + + template + template + inline auto xbroadcast::stepper_begin(const S& shape) const noexcept -> const_stepper + { + // Could check if (broadcastable(shape, m_shape) + return m_e.stepper_begin(shape); + } + + template + template + inline auto xbroadcast::stepper_end(const S& shape, layout_type l) const noexcept -> const_stepper + { + // Could check if (broadcastable(shape, m_shape) + return m_e.stepper_end(shape, l); + } + + template + template + inline void xbroadcast::assign_to(xexpression& e) const + { + auto& ed = e.derived_cast(); + ed.resize(m_shape); + std::fill(ed.begin(), ed.end(), m_e()); + } +} + +#endif diff --git a/vendor/xtensor/include/xtensor/xbuffer_adaptor.hpp b/vendor/xtensor/include/xtensor/xbuffer_adaptor.hpp new file mode 100644 index 0000000000..d8d3316859 --- /dev/null +++ b/vendor/xtensor/include/xtensor/xbuffer_adaptor.hpp @@ -0,0 +1,623 @@ +/*************************************************************************** +* Copyright (c) 2016, Johan Mabille, Sylvain Corlay and Wolf Vollprecht * +* * +* Distributed under the terms of the BSD 3-Clause License. * +* * +* The full license is in the file LICENSE, distributed with this software. * +****************************************************************************/ + +#ifndef XTENSOR_BUFFER_ADAPTOR_HPP +#define XTENSOR_BUFFER_ADAPTOR_HPP + +#include +#include +#include +#include +#include + +#include + +#include "xstorage.hpp" + +namespace xt +{ + /****************************** + * xbuffer_adator declaration * + ******************************/ + + struct no_ownership + { + }; + + struct acquire_ownership + { + }; + + template >>> + class xbuffer_adaptor; + + /********************************* + * xbuffer_adator implementation * + *********************************/ + + namespace detail + { + template + class xbuffer_storage + { + public: + + using self_type = xbuffer_storage; + using allocator_type = A; + using value_type = typename allocator_type::value_type; + using reference = std::conditional_t>>::value, + typename allocator_type::const_reference, + typename allocator_type::reference>; + using const_reference = typename allocator_type::const_reference; + using pointer = std::conditional_t>>::value, + typename allocator_type::const_pointer, + typename allocator_type::pointer>; + using const_pointer = typename allocator_type::const_pointer; + using size_type = typename allocator_type::size_type; + using difference_type = typename allocator_type::difference_type; + + xbuffer_storage(); + + template + xbuffer_storage(P&& data, size_type size, const allocator_type& alloc = allocator_type()); + + size_type size() const noexcept; + void resize(size_type size); + + pointer data() noexcept; + const_pointer data() const noexcept; + + void swap(self_type& rhs) noexcept; + + private: + + pointer p_data; + size_type m_size; + }; + + template + class xbuffer_owner_storage + { + public: + + using self_type = xbuffer_owner_storage; + using allocator_type = A; + using value_type = typename allocator_type::value_type; + using reference = std::conditional_t>>::value, + typename allocator_type::const_reference, + typename allocator_type::reference>; + using const_reference = typename allocator_type::const_reference; + using pointer = std::conditional_t>>::value, + typename allocator_type::const_pointer, + typename allocator_type::pointer>; + using const_pointer = typename allocator_type::const_pointer; + using size_type = typename allocator_type::size_type; + using difference_type = typename allocator_type::difference_type; + + xbuffer_owner_storage() = default; + + template + xbuffer_owner_storage(P&& data, size_type size, const allocator_type& alloc = allocator_type()); + + ~xbuffer_owner_storage(); + + xbuffer_owner_storage(const self_type&) = delete; + self_type& operator=(const self_type&); + + xbuffer_owner_storage(self_type&&); + self_type& operator=(self_type&&); + + size_type size() const noexcept; + void resize(size_type size); + + pointer data() noexcept; + const_pointer data() const noexcept; + + allocator_type get_allocator() const noexcept; + + void swap(self_type& rhs) noexcept; + + private: + + xtl::xclosure_wrapper m_data; + size_type m_size; + bool m_moved_from; + allocator_type m_allocator; + }; + + template + struct get_buffer_storage + { + using type = xbuffer_storage; + }; + + template + struct get_buffer_storage + { + using type = xbuffer_owner_storage; + }; + + template + using buffer_storage_t = typename get_buffer_storage::type; + } + + template + class xbuffer_adaptor : private detail::buffer_storage_t + { + public: + + using base_type = detail::buffer_storage_t; + using self_type = xbuffer_adaptor; + using allocator_type = typename base_type::allocator_type; + using value_type = typename base_type::value_type; + using reference = typename base_type::reference; + using const_reference = typename base_type::const_reference; + using pointer = typename base_type::pointer; + using const_pointer = typename base_type::const_pointer; + using temporary_type = uvector; + + using size_type = typename base_type::size_type; + using difference_type = typename base_type::difference_type; + + using iterator = pointer; + using const_iterator = const_pointer; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + xbuffer_adaptor() = default; + + template + xbuffer_adaptor(P&& data, size_type size, const allocator_type& alloc = allocator_type()); + + ~xbuffer_adaptor() = default; + + xbuffer_adaptor(const self_type&) = default; + self_type& operator=(const self_type&) = default; + + xbuffer_adaptor(self_type&&) = default; + xbuffer_adaptor& operator=(self_type&&) = default; + + self_type& operator=(temporary_type&&); + + bool empty() const noexcept; + using base_type::size; + using base_type::resize; + + reference operator[](size_type i); + const_reference operator[](size_type i) const; + + reference front(); + const_reference front() const; + + reference back(); + const_reference back() const; + + iterator begin(); + iterator end(); + + const_iterator begin() const; + const_iterator end() const; + const_iterator cbegin() const; + const_iterator cend() const; + + reverse_iterator rbegin(); + reverse_iterator rend(); + + const_reverse_iterator rbegin() const; + const_reverse_iterator rend() const; + const_reverse_iterator crbegin() const; + const_reverse_iterator crend() const; + + using base_type::data; + using base_type::swap; + }; + + template + bool operator==(const xbuffer_adaptor& lhs, + const xbuffer_adaptor& rhs); + + template + bool operator!=(const xbuffer_adaptor& lhs, + const xbuffer_adaptor& rhs); + + template + bool operator<(const xbuffer_adaptor& lhs, + const xbuffer_adaptor& rhs); + + template + bool operator<=(const xbuffer_adaptor& lhs, + const xbuffer_adaptor& rhs); + + template + bool operator>(const xbuffer_adaptor& lhs, + const xbuffer_adaptor& rhs); + + template + bool operator>=(const xbuffer_adaptor& lhs, + const xbuffer_adaptor& rhs); + + template + void swap(xbuffer_adaptor& lhs, + xbuffer_adaptor& rhs) noexcept; + + /************************************ + * temporary_container metafunction * + ************************************/ + + template + struct temporary_container + { + using type = C; + }; + + template + struct temporary_container> + { + using type = typename xbuffer_adaptor::temporary_type; + }; + + template + using temporary_container_t = typename temporary_container::type; + + /********************************** + * xbuffer_storage implementation * + **********************************/ + + namespace detail + { + template + inline xbuffer_storage::xbuffer_storage() + : p_data(nullptr), m_size(0) + { + } + + template + template + inline xbuffer_storage::xbuffer_storage(P&& data, size_type size, const allocator_type&) + : p_data(std::forward

(data)), m_size(size) + { + } + + template + inline auto xbuffer_storage::size() const noexcept -> size_type + { + return m_size; + } + + template + inline void xbuffer_storage::resize(size_type size) + { + if (size != m_size) + { + throw std::runtime_error("xbuffer_storage not resizable"); + } + } + + template + inline auto xbuffer_storage::data() noexcept -> pointer + { + return p_data; + } + + template + inline auto xbuffer_storage::data() const noexcept -> const_pointer + { + return p_data; + } + + template + inline void xbuffer_storage::swap(self_type& rhs) noexcept + { + using std::swap; + swap(p_data, rhs.p_data); + swap(m_size, rhs.m_size); + } + } + + /**************************************** + * xbuffer_owner_storage implementation * + ****************************************/ + + namespace detail + { + template + template + inline xbuffer_owner_storage::xbuffer_owner_storage(P&& data, size_type size, const allocator_type& alloc) + : m_data(std::forward

(data)), m_size(size), m_moved_from(false), m_allocator(alloc) + { + } + + template + inline xbuffer_owner_storage::~xbuffer_owner_storage() + { + if (!m_moved_from) + { + safe_destroy_deallocate(m_allocator, m_data.get(), m_size); + m_size = 0; + } + } + + template + inline auto xbuffer_owner_storage::operator=(const self_type& rhs) -> self_type& + { + using std::swap; + if (this != &rhs) + { + allocator_type al = std::allocator_traits::select_on_container_copy_construction(rhs.get_allocator()); + pointer tmp = safe_init_allocate(al, rhs.m_size); + if (xtrivially_default_constructible::value) + { + std::uninitialized_copy(rhs.m_data.get(), rhs.m_data.get() + rhs.m_size, tmp); + } + else + { + std::copy(rhs.m_data.get(), rhs.m_data.get() + rhs.m_size, tmp); + } + swap(m_data.get(), tmp); + m_size = rhs.m_size; + swap(m_allocator, al); + safe_destroy_deallocate(al, tmp, m_size); + } + return *this; + } + + template + inline xbuffer_owner_storage::xbuffer_owner_storage(self_type&& rhs) + : m_data(std::move(rhs.m_data)), m_size(std::move(rhs.m_size)), m_moved_from(std::move(rhs.m_moved_from)), m_allocator(std::move(rhs.m_allocator)) + { + rhs.m_moved_from = true; + rhs.m_size = 0; + } + + template + inline auto xbuffer_owner_storage::operator=(self_type&& rhs) -> self_type& + { + swap(rhs); + rhs.m_moved_from = true; + return *this; + } + + template + inline auto xbuffer_owner_storage::size() const noexcept -> size_type + { + return m_size; + } + + template + void xbuffer_owner_storage::resize(size_type size) + { + using std::swap; + if (size != m_size) + { + pointer tmp = safe_init_allocate(m_allocator, size); + swap(m_data.get(), tmp); + swap(m_size, size); + safe_destroy_deallocate(m_allocator, tmp, size); + } + } + + template + inline auto xbuffer_owner_storage::data() noexcept -> pointer + { + return m_data.get(); + } + + template + inline auto xbuffer_owner_storage::data() const noexcept -> const_pointer + { + return m_data.get(); + } + + template + inline auto xbuffer_owner_storage::get_allocator() const noexcept -> allocator_type + { + return allocator_type(m_allocator); + } + + template + inline void xbuffer_owner_storage::swap(self_type& rhs) noexcept + { + using std::swap; + swap(m_data, rhs.m_data); + swap(m_size, rhs.m_size); + swap(m_allocator, rhs.m_allocator); + } + } + + /********************************** + * xbuffer_adaptor implementation * + **********************************/ + + template + template + inline xbuffer_adaptor::xbuffer_adaptor(P&& data, size_type size, const allocator_type& alloc) + : base_type(std::forward

(data), size, alloc) + { + } + + template + inline auto xbuffer_adaptor::operator=(temporary_type&& tmp) -> self_type& + { + base_type::resize(tmp.size()); + std::copy(tmp.cbegin(), tmp.cend(), begin()); + return *this; + } + + template + bool xbuffer_adaptor::empty() const noexcept + { + return size() == 0; + } + + template + inline auto xbuffer_adaptor::operator[](size_type i) -> reference + { + return data()[i]; + } + + template + inline auto xbuffer_adaptor::operator[](size_type i) const -> const_reference + { + return data()[i]; + } + + template + inline auto xbuffer_adaptor::front() -> reference + { + return data()[0]; + } + + template + inline auto xbuffer_adaptor::front() const -> const_reference + { + return data()[0]; + } + + template + inline auto xbuffer_adaptor::back() -> reference + { + return data()[size() - 1]; + } + + template + inline auto xbuffer_adaptor::back() const -> const_reference + { + return data()[size() - 1]; + } + + template + inline auto xbuffer_adaptor::begin() -> iterator + { + return data(); + } + + template + inline auto xbuffer_adaptor::end() -> iterator + { + return data() + size(); + } + + template + inline auto xbuffer_adaptor::begin() const -> const_iterator + { + return data(); + } + + template + inline auto xbuffer_adaptor::end() const -> const_iterator + { + return data() + size(); + } + + template + inline auto xbuffer_adaptor::cbegin() const -> const_iterator + { + return begin(); + } + + template + inline auto xbuffer_adaptor::cend() const -> const_iterator + { + return end(); + } + + template + inline auto xbuffer_adaptor::rbegin() -> reverse_iterator + { + return reverse_iterator(end()); + } + + template + inline auto xbuffer_adaptor::rend() -> reverse_iterator + { + return reverse_iterator(begin()); + } + + template + inline auto xbuffer_adaptor::rbegin() const -> const_reverse_iterator + { + return const_reverse_iterator(end()); + } + + template + inline auto xbuffer_adaptor::rend() const -> const_reverse_iterator + { + return const_reverse_iterator(begin()); + } + + template + inline auto xbuffer_adaptor::crbegin() const -> const_reverse_iterator + { + return rbegin(); + } + + template + inline auto xbuffer_adaptor::crend() const -> const_reverse_iterator + { + return rend(); + } + + template + inline bool operator==(const xbuffer_adaptor& lhs, + const xbuffer_adaptor& rhs) + { + return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin()); + } + + template + inline bool operator!=(const xbuffer_adaptor& lhs, + const xbuffer_adaptor& rhs) + { + return !(lhs == rhs); + } + + template + inline bool operator<(const xbuffer_adaptor& lhs, + const xbuffer_adaptor& rhs) + { + return std::lexicographical_compare(lhs.begin(), lhs.end(), + rhs.begin(), rhs.end(), + std::less()); + } + + template + inline bool operator<=(const xbuffer_adaptor& lhs, + const xbuffer_adaptor& rhs) + { + return std::lexicographical_compare(lhs.begin(), lhs.end(), + rhs.begin(), rhs.end(), + std::less_equal()); + } + + template + inline bool operator>(const xbuffer_adaptor& lhs, + const xbuffer_adaptor& rhs) + { + return std::lexicographical_compare(lhs.begin(), lhs.end(), + rhs.begin(), rhs.end(), + std::greater()); + } + + template + inline bool operator>=(const xbuffer_adaptor& lhs, + const xbuffer_adaptor& rhs) + { + return std::lexicographical_compare(lhs.begin(), lhs.end(), + rhs.begin(), rhs.end(), + std::greater_equal()); + } + + template + inline void swap(xbuffer_adaptor& lhs, + xbuffer_adaptor& rhs) noexcept + { + lhs.swap(rhs); + } +} + +#endif diff --git a/vendor/xtensor/include/xtensor/xbuilder.hpp b/vendor/xtensor/include/xtensor/xbuilder.hpp new file mode 100644 index 0000000000..c3e34b8ac3 --- /dev/null +++ b/vendor/xtensor/include/xtensor/xbuilder.hpp @@ -0,0 +1,918 @@ +/*************************************************************************** +* Copyright (c) 2016, Johan Mabille, Sylvain Corlay and Wolf Vollprecht * +* * +* Distributed under the terms of the BSD 3-Clause License. * +* * +* The full license is in the file LICENSE, distributed with this software. * +****************************************************************************/ + +/** + * @brief standard mathematical functions for xexpressions + */ + +#ifndef XTENSOR_BUILDER_HPP +#define XTENSOR_BUILDER_HPP + +#include +#include +#include +#include +#include +#include +#ifdef X_OLD_CLANG + #include +#endif + +#include +#include + +#include "xbroadcast.hpp" +#include "xfunction.hpp" +#include "xgenerator.hpp" +#include "xoperation.hpp" + +namespace xt +{ + + /******** + * ones * + ********/ + + /** + * Returns an \ref xexpression containing ones of the specified shape. + * @tparam shape the shape of the returned expression. + */ + template + inline auto ones(S shape) noexcept + { + return broadcast(T(1), std::forward(shape)); + } + +#ifdef X_OLD_CLANG + template + inline auto ones(std::initializer_list shape) noexcept + { + return broadcast(T(1), shape); + } +#else + template + inline auto ones(const I (&shape)[L]) noexcept + { + return broadcast(T(1), shape); + } +#endif + + /********* + * zeros * + *********/ + + /** + * Returns an \ref xexpression containing zeros of the specified shape. + * @tparam shape the shape of the returned expression. + */ + template + inline auto zeros(S shape) noexcept + { + return broadcast(T(0), std::forward(shape)); + } + +#ifdef X_OLD_CLANG + template + inline auto zeros(std::initializer_list shape) noexcept + { + return broadcast(T(0), shape); + } +#else + template + inline auto zeros(const I (&shape)[L]) noexcept + { + return broadcast(T(0), shape); + } +#endif + + /** + * Create a xcontainer (xarray, xtensor or xtensor_fixed) with uninitialized values of + * with value_type T and shape. Selects the best container match automatically + * from the supplied shape. + * + * - ``std::vector`` → ``xarray`` + * - ``std::array`` or ``initializer_list`` → ``xtensor`` + * - ``xshape`` → ``xtensor_fixed>`` + * + * @param shape shape of the new xcontainer + */ + template + inline xarray empty(const S& shape) + { + return xarray::from_shape(shape); + } + + template + inline xtensor empty(const std::array& shape) + { + using shape_type = typename xtensor::shape_type; + return xtensor(xtl::forward_sequence(shape)); + } + +#ifndef X_OLD_CLANG + template + inline xtensor empty(const I(&shape)[N]) + { + using shape_type = typename xtensor::shape_type; + return xtensor(xtl::forward_sequence(shape)); + } +#endif + + template + inline xtensor_fixed, L> empty(const fixed_shape& /*shape*/) + { + return xtensor_fixed, L>(); + } + + /** + * Create a xcontainer (xarray, xtensor or xtensor_fixed) with uninitialized values of + * the same shape, value type and layout as the input xexpression *e*. + * + * @param e the xexpression from which to extract shape, value type and layout. + */ + template + inline typename E::temporary_type empty_like(const xexpression& e) + { + typename E::temporary_type res(e.derived_cast().shape()); + return res; + } + + /** + * Create a xcontainer (xarray, xtensor or xtensor_fixed), filled with *fill_value* and of + * the same shape, value type and layout as the input xexpression *e*. + * + * @param e the xexpression from which to extract shape, value type and layout. + * @param fill_value the value used to set each element of the returned xcontainer. + */ + template + inline typename E::temporary_type full_like(const xexpression& e, typename E::value_type fill_value) + { + typename E::temporary_type res(e.derived_cast().shape(), fill_value); + return res; + } + + /** + * Create a xcontainer (xarray, xtensor or xtensor_fixed), filled with zeros and of + * the same shape, value type and layout as the input xexpression *e*. + * + * Note: contrary to zeros(shape), this function returns a non-lazy, allocated container! + * Use ``xt::zeros(e.shape());` for a lazy version. + * + * @param e the xexpression from which to extract shape, value type and layout. + */ + template + inline typename E::temporary_type zeros_like(const xexpression& e) + { + return full_like(e, typename E::value_type(0)); + } + + /** + * Create a xcontainer (xarray, xtensor or xtensor_fixed), filled with ones and of + * the same shape, value type and layout as the input xexpression *e*. + * + * Note: contrary to ones(shape), this function returns a non-lazy, evaluated container! + * Use ``xt::ones(e.shape());`` for a lazy version. + * + * @param e the xexpression from which to extract shape, value type and layout. + */ + template + inline typename E::temporary_type ones_like(const xexpression& e) + { + return full_like(e, typename E::value_type(1)); + } + + namespace detail + { + template + class arange_impl + { + public: + + using value_type = T; + + arange_impl(T start, T stop, T step) + : m_start(start), m_stop(stop), m_step(step) + { + } + + template + inline T operator()(Args... args) const + { + return access_impl(args...); + } + + template + inline T element(It first, It) const + { + return m_start + m_step * T(*first); + } + + template + inline void assign_to(xexpression& e) const noexcept + { + auto& de = e.derived_cast(); + value_type value = m_start; + + for (auto& el : de.storage()) + { + el = value; + value += m_step; + } + } + + private: + + value_type m_start; + value_type m_stop; + value_type m_step; + + template + inline T access_impl(T1 t, Args...) const + { + return m_start + m_step * T(t); + } + + inline T access_impl() const + { + return m_start; + } + }; + + template + class fn_impl + { + public: + + using value_type = typename F::value_type; + using size_type = std::size_t; + + fn_impl(F&& f) + : m_ft(f) + { + } + + inline value_type operator()() const + { + size_type idx[1] = {0ul}; + return access_impl(std::begin(idx), std::end(idx)); + } + + template + inline value_type operator()(Args... args) const + { + size_type idx[sizeof...(Args)] = {static_cast(args)...}; + return access_impl(std::begin(idx), std::end(idx)); + } + + template + inline value_type element(It first, It last) const + { + return access_impl(first, last); + } + + private: + + F m_ft; + template + inline value_type access_impl(const It& begin, const It& end) const + { + return m_ft(begin, end); + } + }; + + template + class eye_fn + { + public: + + using value_type = T; + + eye_fn(int k) + : m_k(k) + { + } + + template + inline T operator()(const It& /*begin*/, const It& end) const + { + using lvalue_type = typename std::iterator_traits::value_type; + return *(end - 1) == *(end - 2) + static_cast(static_cast(m_k)) ? T(1) : T(0); + } + + private: + + int m_k; + }; + } + + /** + * Generates an array with ones on the diagonal. + * @param shape shape of the resulting expression + * @param k index of the diagonal. 0 (default) refers to the main diagonal, + * a positive value refers to an upper diagonal, and a negative + * value to a lower diagonal. + * @tparam T value_type of xexpression + * @return xgenerator that generates the values on access + */ + template + inline auto eye(const std::vector& shape, int k = 0) + { + return detail::make_xgenerator(detail::fn_impl>(detail::eye_fn(k)), shape); + } + + /** + * Generates a (n x n) array with ones on the diagonal. + * @param n length of the diagonal. + * @param k index of the diagonal. 0 (default) refers to the main diagonal, + * a positive value refers to an upper diagonal, and a negative + * value to a lower diagonal. + * @tparam T value_type of xexpression + * @return xgenerator that generates the values on access + */ + template + inline auto eye(std::size_t n, int k = 0) + { + return eye({n, n}, k); + } + + /** + * Generates numbers evenly spaced within given half-open interval [start, stop). + * @param start start of the interval + * @param stop stop of the interval + * @param step stepsize + * @tparam T value_type of xexpression + * @return xgenerator that generates the values on access + */ + template + inline auto arange(T start, T stop, T step = 1) noexcept + { + std::size_t shape = static_cast(std::ceil((stop - start) / step)); + return detail::make_xgenerator(detail::arange_impl(start, stop, step), {shape}); + } + + /** + * Generate numbers evenly spaced within given half-open interval [0, stop) + * with a step size of 1. + * @param stop stop of the interval + * @tparam T value_type of xexpression + * @return xgenerator that generates the values on access + */ + template + inline auto arange(T stop) noexcept + { + return arange(T(0), stop, T(1)); + } + + /** + * Generates @a num_samples evenly spaced numbers over given interval + * @param start start of interval + * @param stop stop of interval + * @param num_samples number of samples (defaults to 50) + * @param endpoint if true, include endpoint (defaults to true) + * @tparam T value_type of xexpression + * @return xgenerator that generates the values on access + */ + template + inline auto linspace(T start, T stop, std::size_t num_samples = 50, bool endpoint = true) noexcept + { + using fp_type = std::common_type_t; + fp_type step = fp_type(stop - start) / fp_type(num_samples - (endpoint ? 1 : 0)); + return cast(detail::make_xgenerator(detail::arange_impl(fp_type(start), fp_type(stop), step), {num_samples})); + } + + /** + * Generates @a num_samples numbers evenly spaced on a log scale over given interval + * @param start start of interval (pow(base, start) is the first value). + * @param stop stop of interval (pow(base, stop) is the final value, except if endpoint = false) + * @param num_samples number of samples (defaults to 50) + * @param base the base of the log space. + * @param endpoint if true, include endpoint (defaults to true) + * @tparam T value_type of xexpression + * @return xgenerator that generates the values on access + */ + template + inline auto logspace(T start, T stop, std::size_t num_samples, T base = 10, bool endpoint = true) noexcept + { + return cast(pow(std::move(base), linspace(start, stop, num_samples, endpoint))); + } + + namespace detail + { + template + class concatenate_impl + { + public: + + using size_type = std::size_t; + using value_type = promote_type_t::value_type...>; + + inline concatenate_impl(std::tuple&& t, size_type axis) + : m_t(t), m_axis(axis) + { + } + + template + inline value_type operator()(Args... args) const + { + // TODO: avoid memory allocation + return access_impl(xindex({static_cast(args)...})); + } + + template + inline value_type element(It first, It last) const + { + // TODO: avoid memory allocation + return access_impl(xindex(first, last)); + } + + private: + + inline value_type access_impl(xindex idx) const + { + auto match = [this, &idx](auto& arr) { + if (idx[this->m_axis] >= arr.shape()[this->m_axis]) + { + idx[this->m_axis] -= arr.shape()[this->m_axis]; + return false; + } + return true; + }; + + auto get = [&idx](auto& arr) { + return arr[idx]; + }; + + size_type i = 0; + for (; i < sizeof...(CT); ++i) + { + if (apply(i, match, m_t)) + { + break; + } + } + return apply(i, get, m_t); + } + + std::tuple m_t; + size_type m_axis; + }; + + template + class stack_impl + { + public: + + using size_type = std::size_t; + using value_type = promote_type_t::value_type...>; + + inline stack_impl(std::tuple&& t, size_type axis) + : m_t(t), m_axis(axis) + { + } + + template + inline value_type operator()(Args... args) const + { + // TODO: avoid memory allocation + return access_impl(xindex({static_cast(args)...})); + } + + template + inline value_type element(It first, It last) const + { + // TODO: avoid memory allocation + return access_impl(xindex(first, last)); + } + + private: + + inline value_type access_impl(xindex idx) const + { + auto get_item = [&idx](auto& arr) { + return arr[idx]; + }; + size_type i = idx[m_axis]; + idx.erase(idx.begin() + std::ptrdiff_t(m_axis)); + return apply(i, get_item, m_t); + } + + const std::tuple m_t; + const size_type m_axis; + }; + + template + class repeat_impl + { + public: + + using xexpression_type = std::decay_t; + using size_type = typename xexpression_type::size_type; + using value_type = typename xexpression_type::value_type; + + template + repeat_impl(CTA&& source, size_type axis) + : m_source(std::forward(source)), m_axis(axis) + { + } + + template + value_type operator()(Args... args) const + { + std::array args_arr = {static_cast(args)...}; + return m_source(args_arr[m_axis]); + } + + template + inline value_type element(It first, It) const + { + return m_source(*(first + static_cast(m_axis))); + } + + private: + + CT m_source; + size_type m_axis; + }; + } + + /** + * @brief Creates tuples from arguments for \ref concatenate and \ref stack. + * Very similar to std::make_tuple. + */ + template + inline auto xtuple(Types&&... args) + { + return std::tuple...>(std::forward(args)...); + } + + /** + * @brief Concatenates xexpressions along \em axis. + * + * @param t \ref xtuple of xexpressions to concatenate + * @param axis axis along which elements are concatenated + * @returns xgenerator evaluating to concatenated elements + * + * \code{.cpp} + * xt::xarray a = {{1, 2, 3}}; + * xt::xarray b = {{2, 3, 4}}; + * xt::xarray c = xt::concatenate(xt::xtuple(a, b)); // => {{1, 2, 3}, + * {2, 3, 4}} + * xt::xarray d = xt::concatenate(xt::xtuple(a, b), 1); // => {{1, 2, 3, 2, 3, 4}} + * \endcode + */ + template + inline auto concatenate(std::tuple&& t, std::size_t axis = 0) + { + using shape_type = promote_shape_t::shape_type...>; + shape_type new_shape = xtl::forward_sequence(std::get<0>(t).shape()); + auto shape_at_axis = [&axis](std::size_t prev, auto& arr) -> std::size_t { + return prev + arr.shape()[axis]; + }; + new_shape[axis] += accumulate(shape_at_axis, std::size_t(0), t) - new_shape[axis]; + return detail::make_xgenerator(detail::concatenate_impl(std::forward>(t), axis), new_shape); + } + + namespace detail + { + template + inline std::array add_axis(std::array arr, std::size_t axis, std::size_t value) + { + std::array temp; + std::copy(arr.begin(), arr.begin() + axis, temp.begin()); + temp[axis] = value; + std::copy(arr.begin() + axis, arr.end(), temp.begin() + axis + 1); + return temp; + } + + template + inline T add_axis(T arr, std::size_t axis, std::size_t value) + { + T temp(arr); + temp.insert(temp.begin() + std::ptrdiff_t(axis), value); + return temp; + } + } + + /** + * @brief Stack xexpressions along \em axis. + * Stacking always creates a new dimension along which elements are stacked. + * + * @param t \ref xtuple of xexpressions to concatenate + * @param axis axis along which elements are stacked + * @returns xgenerator evaluating to stacked elements + * + * \code{.cpp} + * xt::xarray a = {1, 2, 3}; + * xt::xarray b = {5, 6, 7}; + * xt::xarray s = xt::stack(xt::xtuple(a, b)); // => {{1, 2, 3}, + * {5, 6, 7}} + * xt::xarray t = xt::stack(xt::xtuple(a, b), 1); // => {{1, 5}, + * {2, 6}, + * {3, 7}} + * \endcode + */ + template + inline auto stack(std::tuple&& t, std::size_t axis = 0) + { + using shape_type = promote_shape_t::shape_type...>; + auto new_shape = detail::add_axis(xtl::forward_sequence(std::get<0>(t).shape()), axis, sizeof...(CT)); + return detail::make_xgenerator(detail::stack_impl(std::forward>(t), axis), new_shape); + } + + namespace detail + { + + template + inline auto meshgrid_impl(std::index_sequence, E&&... e) noexcept + { +#if defined X_OLD_CLANG || defined _MSC_VER + const std::array shape = {e.shape()[0]...}; + return std::make_tuple( + detail::make_xgenerator( + detail::repeat_impl>(std::forward(e), I), + shape + )... + ); +#else + return std::make_tuple( + detail::make_xgenerator( + detail::repeat_impl>(std::forward(e), I), + {e.shape()[0]...} + )... + ); +#endif + } + } + + /** + * @brief Return coordinate tensors from coordinate vectors. + * Make N-D coordinate tensor expressions for vectorized evaluations of N-D scalar/vector + * fields over N-D grids, given one-dimensional coordinate arrays x1, x2,..., xn. + * + * @param e xexpressions to concatenate + * @returns tuple of xgenerator expressions. + */ + template + inline auto meshgrid(E&&... e) noexcept + { + return detail::meshgrid_impl(std::make_index_sequence(), std::forward(e)...); + } + + namespace detail + { + template + class diagonal_fn + { + public: + + using xexpression_type = std::decay_t; + using value_type = typename xexpression_type::value_type; + + template + diagonal_fn(CTA&& source, int offset, std::size_t axis_1, std::size_t axis_2) + : m_source(std::forward(source)), m_offset(offset), m_axis_1(axis_1), m_axis_2(axis_2) + { + } + + template + inline value_type operator()(It begin, It) const + { + xindex idx(m_source.shape().size()); + + for (std::size_t i = 0; i < idx.size(); i++) + { + if (i != m_axis_1 && i != m_axis_2) + { + idx[i] = *begin++; + } + } + using it_vtype = typename std::iterator_traits::value_type; + it_vtype uoffset = static_cast(m_offset); + if (m_offset >= 0) + { + idx[m_axis_1] = *(begin); + idx[m_axis_2] = *(begin) + uoffset; + } + else + { + idx[m_axis_1] = *(begin) - uoffset; + idx[m_axis_2] = *(begin); + } + return m_source[idx]; + } + + private: + + CT m_source; + const int m_offset; + const std::size_t m_axis_1; + const std::size_t m_axis_2; + }; + + template + class diag_fn + { + public: + + using xexpression_type = std::decay_t; + using value_type = typename xexpression_type::value_type; + + template + diag_fn(CTA&& source, int k) + : m_source(std::forward(source)), m_k(k) + { + } + + template + inline value_type operator()(It begin, It) const + { + using it_vtype = typename std::iterator_traits::value_type; + it_vtype umk = static_cast(m_k); + if (m_k > 0) + { + return *begin + umk == *(begin + 1) ? m_source(*begin) : value_type(0); + } + else + { + return *begin + umk == *(begin + 1) ? m_source(*begin + umk) : value_type(0); + } + } + + private: + + CT m_source; + const int m_k; + }; + + template + class trilu_fn + { + public: + + using xexpression_type = std::decay_t; + using value_type = typename xexpression_type::value_type; + using signed_idx_type = long int; + + template + trilu_fn(CTA&& source, int k, Comp comp) + : m_source(std::forward(source)), m_k(k), m_comp(comp) + { + } + + template + inline value_type operator()(It begin, It end) const + { + // have to cast to signed int otherwise -1 can lead to overflow + return m_comp(signed_idx_type(*begin) + m_k, signed_idx_type(*(begin + 1))) ? m_source.element(begin, end) : value_type(0); + } + + private: + + CT m_source; + const signed_idx_type m_k; + const Comp m_comp; + }; + } + + namespace detail + { + // meta-function returning the shape type for a diagonal + template + struct diagonal_shape_type + { + using type = ST; + }; + + template + struct diagonal_shape_type> + { + using type = std::array; + }; + } + + /** + * @brief Returns the elements on the diagonal of arr + * If arr has more than two dimensions, then the axes specified by + * axis_1 and axis_2 are used to determine the 2-D sub-array whose + * diagonal is returned. The shape of the resulting array can be + * determined by removing axis1 and axis2 and appending an index + * to the right equal to the size of the resulting diagonals. + * + * @param arr the input array + * @param offset offset of the diagonal from the main diagonal. Can + * be positive or negative. + * @param axis_1 Axis to be used as the first axis of the 2-D sub-arrays + * from which the diagonals should be taken. + * @param axis_2 Axis to be used as the second axis of the 2-D sub-arrays + * from which the diagonals should be taken. + * @returns xexpression with values of the diagonal + * + * \code{.cpp} + * xt::xarray a = {{1, 2, 3}, + * {4, 5, 6} + * {7, 8, 9}}; + * auto b = xt::diagonal(a); // => {1, 5, 9} + * \endcode + */ + template + inline auto diagonal(E&& arr, int offset = 0, std::size_t axis_1 = 0, std::size_t axis_2 = 1) + { + using CT = xclosure_t; + using shape_type = typename detail::diagonal_shape_type::shape_type>::type; + + auto shape = arr.shape(); + auto dimension = arr.dimension(); + + // The following shape calculation code is an almost verbatim adaptation of numpy: + // https://github.com/numpy/numpy/blob/2aabeafb97bea4e1bfa29d946fbf31e1104e7ae0/numpy/core/src/multiarray/item_selection.c#L1799 + auto ret_shape = xtl::make_sequence(dimension - 1, 0); + int dim_1 = static_cast(shape[axis_1]); + int dim_2 = static_cast(shape[axis_2]); + + offset >= 0 ? dim_2 -= offset : dim_1 += offset; + + auto diag_size = std::size_t(dim_2 < dim_1 ? dim_2 : dim_1); + + std::size_t i = 0; + for (std::size_t idim = 0; idim < dimension; ++idim) + { + if (idim != axis_1 && idim != axis_2) + { + ret_shape[i++] = shape[idim]; + } + } + + ret_shape.back() = diag_size; + + return detail::make_xgenerator(detail::fn_impl>(detail::diagonal_fn(std::forward(arr), offset, axis_1, axis_2)), + ret_shape); + } + + /** + * @brief xexpression with values of arr on the diagonal, zeroes otherwise + * + * @param arr the 1D input array of length n + * @param k the offset of the considered diagonal + * @returns xexpression function with shape n x n and arr on the diagonal + * + * \code{.cpp} + * xt::xarray a = {1, 5, 9}; + * auto b = xt::diag(a); // => {{1, 0, 0}, + * // {0, 5, 0}, + * // {0, 0, 9}} + * \endcode + */ + template + inline auto diag(E&& arr, int k = 0) + { + using CT = xclosure_t; + std::size_t sk = std::size_t(std::abs(k)); + std::size_t s = arr.shape()[0] + sk; + return detail::make_xgenerator(detail::fn_impl>(detail::diag_fn(std::forward(arr), k)), + {s, s}); + } + + /** + * @brief Extract lower triangular matrix from xexpression. The parameter k selects the + * offset of the diagonal. + * + * @param arr the input array + * @param k the diagonal above which to zero elements. 0 (default) selects the main diagonal, + * k < 0 is below the main diagonal, k > 0 above. + * @returns xexpression containing lower triangle from arr, 0 otherwise + */ + template + inline auto tril(E&& arr, int k = 0) + { + using CT = xclosure_t; + auto shape = arr.shape(); + return detail::make_xgenerator(detail::fn_impl>>( + detail::trilu_fn>(std::forward(arr), k, std::greater_equal())), + shape); + } + + /** + * @brief Extract upper triangular matrix from xexpression. The parameter k selects the + * offset of the diagonal. + * + * @param arr the input array + * @param k the diagonal below which to zero elements. 0 (default) selects the main diagonal, + * k < 0 is below the main diagonal, k > 0 above. + * @returns xexpression containing lower triangle from arr, 0 otherwise + */ + template + inline auto triu(E&& arr, int k = 0) + { + using CT = xclosure_t; + auto shape = arr.shape(); + return detail::make_xgenerator(detail::fn_impl>>( + detail::trilu_fn>(std::forward(arr), k, std::less_equal())), + shape); + } +} +#endif diff --git a/vendor/xtensor/include/xtensor/xcomplex.hpp b/vendor/xtensor/include/xtensor/xcomplex.hpp new file mode 100644 index 0000000000..e47842aa11 --- /dev/null +++ b/vendor/xtensor/include/xtensor/xcomplex.hpp @@ -0,0 +1,251 @@ +/*************************************************************************** +* Copyright (c) 2016, Johan Mabille, Sylvain Corlay and Wolf Vollprecht * +* * +* Distributed under the terms of the BSD 3-Clause License. * +* * +* The full license is in the file LICENSE, distributed with this software. * +****************************************************************************/ + +#ifndef XTENSOR_COMPLEX_HPP +#define XTENSOR_COMPLEX_HPP + +#include +#include + +#include + +#include "xtensor/xbuilder.hpp" +#include "xtensor/xexpression.hpp" +#include "xtensor/xoffset_view.hpp" + +namespace xt +{ + + /****************************** + * real and imag declarations * + ******************************/ + + template + decltype(auto) real(E&& e) noexcept; + + template + decltype(auto) imag(E&& e) noexcept; + + /******************************** + * real and imag implementation * + ********************************/ + + namespace detail + { + template + struct complex_helper + { + template + static inline auto real(E&& e) noexcept + { + using real_type = typename std::decay_t::value_type::value_type; + return xoffset_view, real_type, 0>(std::forward(e)); + } + + template + static inline auto imag(E&& e) noexcept + { + using real_type = typename std::decay_t::value_type::value_type; + return xoffset_view, real_type, sizeof(real_type)>(std::forward(e)); + } + }; + + template <> + struct complex_helper + { + template + static inline decltype(auto) real(E&& e) noexcept + { + return e; + } + + template + static inline auto imag(E&& e) noexcept + { + return zeros::value_type>(e.shape()); + } + }; + + template + struct complex_expression_helper + { + template + static inline auto real(E&& e) noexcept + { + return detail::complex_helper::value_type>::value>::real(e); + } + + template + static inline auto imag(E&& e) noexcept + { + return detail::complex_helper::value_type>::value>::imag(e); + } + }; + + template <> + struct complex_expression_helper + { + template + static inline decltype(auto) real(E&& e) noexcept + { + return xtl::forward_real(std::forward(e)); + } + + template + static inline decltype(auto) imag(E&& e) noexcept + { + return xtl::forward_imag(std::forward(e)); + } + }; + } + + /** + * @brief Returns an \ref xexpression representing the real part of the given expression. + * + * @tparam e the \ref xexpression + * + * The returned expression either hold a const reference to \p e or a copy + * depending on whether \p e is an lvalue or an rvalue. + */ + template + inline decltype(auto) real(E&& e) noexcept + { + return detail::complex_expression_helper>::value>::real(std::forward(e)); + } + + /** + * @brief Returns an \ref xexpression representing the imaginary part of the given expression. + * + * @tparam e the \ref xexpression + * + * The returned expression either hold a const reference to \p e or a copy + * depending on whether \p e is an lvalue or an rvalue. + */ + template + inline decltype(auto) imag(E&& e) noexcept + { + return detail::complex_expression_helper>::value>::imag(std::forward(e)); + } + +#define UNARY_COMPLEX_FUNCTOR(NAME) \ + template \ + struct NAME##_fun \ + { \ + using argument_type = T; \ + using result_type = decltype(std::NAME(std::declval())); \ + constexpr result_type operator()(const T& t) const \ + { \ + using std::NAME; \ + return NAME(t); \ + } \ + } + + namespace math + { + UNARY_COMPLEX_FUNCTOR(norm); + UNARY_COMPLEX_FUNCTOR(arg); + + namespace detail + { + // libc++ (OSX) conj is unfortunately broken and returns + // std::complex instead of T. + template + constexpr T conj(const T& c) + { + return c; + } + + template + constexpr std::complex conj(const std::complex& c) + { + return std::complex(c.real(), -c.imag()); + } + } + + template + struct conj_fun + { + using argument_type = T; + using result_type = decltype(detail::conj(std::declval())); + using simd_value_type = xsimd::simd_type; + constexpr result_type operator()(const T& t) const + { + return detail::conj(t); + } + constexpr simd_value_type simd_apply(const simd_value_type& t) const + { + return detail::conj(t); + } + }; + } + +#undef UNARY_COMPLEX_FUNCTOR + + /** + * @brief Returns an \ref xfunction evaluating to the complex conjugate of the given expression. + * + * @param e the \ref xexpression + */ + template + inline auto conj(E&& e) noexcept + { + using value_type = typename std::decay_t::value_type; + using functor = math::conj_fun; + using result_type = typename functor::result_type; + using type = xfunction>; + return type(functor(), std::forward(e)); + } + + /** + * @brief Calculates the phase angle (in radians) elementwise for the complex numbers in e. + * @param e the \ref xexpression + */ + template + inline auto arg(E&& e) noexcept + { + using value_type = typename std::decay_t::value_type; + using functor = math::arg_fun; + using result_type = typename functor::result_type; + using type = xfunction>; + return type(functor(), std::forward(e)); + } + + /** + * @brief Calculates the phase angle elementwise for the complex numbers in e. + * Note that this function might be slightly less perfomant than \ref arg. + * @param e the \ref xexpression + * @param deg calculate angle in degrees instead of radians + */ + template + inline auto angle(E&& e, bool deg = false) noexcept + { + using value_type = xtl::complex_value_type_t::value_type>; + value_type multiplier = 1.0; + if (deg) + { + multiplier = value_type(180) / numeric_constants::PI; + } + return arg(std::forward(e)) * std::move(multiplier); + } + + /** + * Calculates the squared magnitude elementwise for the complex numbers in e. + * Equivalent to pow(real(e), 2) + pow(imag(e), 2). + * @param e the \ref xexpression + */ + template + inline auto norm(E&& e) noexcept + { + using value_type = typename std::decay_t::value_type; + using functor = math::norm_fun; + using result_type = typename functor::result_type; + using type = xfunction>; + return type(functor(), std::forward(e)); + } +} +#endif diff --git a/vendor/xtensor/include/xtensor/xconcepts.hpp b/vendor/xtensor/include/xtensor/xconcepts.hpp new file mode 100644 index 0000000000..1845c6b033 --- /dev/null +++ b/vendor/xtensor/include/xtensor/xconcepts.hpp @@ -0,0 +1,113 @@ +/*************************************************************************** +* Copyright (c) 2017, Ullrich Koethe * +* * +* Distributed under the terms of the BSD 3-Clause License. * +* * +* The full license is in the file LICENSE, distributed with this software. * +****************************************************************************/ + +#ifndef XTENSOR_CONCEPTS_HPP +#define XTENSOR_CONCEPTS_HPP + +#include + +/***************************************************** + * concept checking and type inference functionality * + *****************************************************/ + +namespace xt +{ + + /****************************************** + * XTENSOR_REQUIRE concept checking macro * + ******************************************/ + + struct concept_check_successful + { + }; + + template + using concept_check = typename std::enable_if::type; + + /** @brief Concept checking macro (more readable than sfinae). + + The macro is used as the last argument in a template declaration. + It must be followed by a static boolean expression in angle brackets. + The template will only be included in overload resolution when + this expression evaluates to 'true'. + + Example: + \code + template ::value>> + T foo(T t) + {...} + \endcode + */ + #define XTENSOR_REQUIRE typename = concept_check + + /******************** + * iterator_concept * + ********************/ + + /** @brief Traits class to check if a type is an iterator. + + This is useful in concept checking to make sure that a given template + is only instantiated when the argument is an iterator. + Currently, we apply the simple rule that class @tparam T + is either a pointer or a C-array or has an embedded typedef + 'iterator_category'. More sophisticated checks can easily + be added when needed. + + If @tparam T is indeed an iterator, the class' value member + is true: + \code + template ::value>> + T foo(T t) + {...} + \endcode + */ + template + struct iterator_concept + { + using V = std::decay_t; + + static char test(...); + + template + static int test(U*, typename U::iterator_category* = 0); + + static const bool value = + std::is_array::value || + std::is_pointer::value || + std::is_same())), int>::value; + }; + + /** @brief Check if a conversion may loose information. + + @tparam FROM source type + @tparam TO target type + + When data is converted from a big type (e.g. int64_t or double) + to a smaller type (e.g. int32_t), most compilers issue a 'possible loss of data' + warning. This metafunction allows you to detect these situations and take appropriate + actions. + + If loss of data may occur, member is_narrowing_conversion::value is true. Currently, + the check is only implemented for built-in types, i.e. types where std::is_arithmetic + is true. + */ + template + struct is_narrowing_conversion + { + using argument_type = std::decay_t; + using result_type = std::decay_t; + + static const bool value = std::is_arithmetic::value && + (sizeof(result_type) < sizeof(argument_type) || + (std::is_integral::value && std::is_floating_point::value)); + }; +} // namespace xt + +#endif // XCONCEPTS_HPP diff --git a/vendor/xtensor/include/xtensor/xcontainer.hpp b/vendor/xtensor/include/xtensor/xcontainer.hpp new file mode 100644 index 0000000000..80df1907f9 --- /dev/null +++ b/vendor/xtensor/include/xtensor/xcontainer.hpp @@ -0,0 +1,1418 @@ +/*************************************************************************** +* Copyright (c) 2016, Johan Mabille, Sylvain Corlay and Wolf Vollprecht * +* * +* Distributed under the terms of the BSD 3-Clause License. * +* * +* The full license is in the file LICENSE, distributed with this software. * +****************************************************************************/ + +#ifndef XTENSOR_CONTAINER_HPP +#define XTENSOR_CONTAINER_HPP + +#include +#include +#include +#include +#include + +#include +#include + +#include "xiterable.hpp" +#include "xiterator.hpp" +#include "xmath.hpp" +#include "xoperation.hpp" +#include "xstrides.hpp" +#include "xtensor_forward.hpp" + +namespace xt +{ + template + struct xcontainer_iterable_types + { + using inner_shape_type = typename xcontainer_inner_types::inner_shape_type; + using storage_type = typename xcontainer_inner_types::storage_type; + using stepper = xstepper; + using const_stepper = xstepper; + }; + +#define DL XTENSOR_DEFAULT_LAYOUT + + namespace detail + { + template + struct allocator_type_impl + { + using type = typename T::allocator_type; + }; + + template + struct allocator_type_impl> + { + using type = std::allocator; // fake allocator for testing + }; + } + + template + using allocator_type_t = typename detail::allocator_type_impl::type; + + + /** + * @class xcontainer + * @brief Base class for dense multidimensional containers. + * + * The xcontainer class defines the interface for dense multidimensional + * container classes. It does not embed any data container, this responsibility + * is delegated to the inheriting classes. + * + * @tparam D The derived type, i.e. the inheriting class for which xcontainer + * provides the interface. + */ + template + class xcontainer : private xiterable + { + public: + + using derived_type = D; + + using inner_types = xcontainer_inner_types; + using storage_type = typename inner_types::storage_type; + using allocator_type = allocator_type_t>; + using value_type = typename storage_type::value_type; + using reference = std::conditional_t::value, + typename storage_type::const_reference, + typename storage_type::reference>; + using const_reference = typename storage_type::const_reference; + using pointer = typename storage_type::pointer; + using const_pointer = typename storage_type::const_pointer; + using size_type = typename storage_type::size_type; + using difference_type = typename storage_type::difference_type; + using simd_value_type = xsimd::simd_type; + + using shape_type = typename inner_types::shape_type; + using strides_type = typename inner_types::strides_type; + using backstrides_type = typename inner_types::backstrides_type; + + using inner_shape_type = typename inner_types::inner_shape_type; + using inner_strides_type = typename inner_types::inner_strides_type; + using inner_backstrides_type = typename inner_types::inner_backstrides_type; + + using iterable_base = xiterable; + using stepper = typename iterable_base::stepper; + using const_stepper = typename iterable_base::const_stepper; + + static constexpr layout_type static_layout = inner_types::layout; + static constexpr bool contiguous_layout = static_layout != layout_type::dynamic; + using data_alignment = xsimd::container_alignment_t; + using simd_type = xsimd::simd_type; + + static_assert(static_layout != layout_type::any, "Container layout can never be layout_type::any!"); + + size_type size() const noexcept; + + constexpr size_type dimension() const noexcept; + + constexpr const inner_shape_type& shape() const noexcept; + constexpr const inner_strides_type& strides() const noexcept; + constexpr const inner_backstrides_type& backstrides() const noexcept; + + template + void fill(const T& value); + + template + reference operator()(Args... args); + + template + const_reference operator()(Args... args) const; + + template + reference at(Args... args); + + template + const_reference at(Args... args) const; + + template + reference unchecked(Args... args); + + template + const_reference unchecked(Args... args) const; + + template + disable_integral_t operator[](const S& index); + template + reference operator[](std::initializer_list index); + reference operator[](size_type i); + + template + disable_integral_t operator[](const S& index) const; + template + const_reference operator[](std::initializer_list index) const; + const_reference operator[](size_type i) const; + + template + reference element(It first, It last); + template + const_reference element(It first, It last) const; + + storage_type& storage() noexcept; + const storage_type& storage() const noexcept; + + value_type* data() noexcept; + const value_type* data() const noexcept; + const size_type data_offset() const noexcept; + + template + bool broadcast_shape(S& shape, bool reuse_cache = false) const; + + template + bool is_trivial_broadcast(const S& strides) const noexcept; + + template + stepper stepper_begin(const S& shape) noexcept; + template + stepper stepper_end(const S& shape, layout_type l) noexcept; + + template + const_stepper stepper_begin(const S& shape) const noexcept; + template + const_stepper stepper_end(const S& shape, layout_type l) const noexcept; + + reference data_element(size_type i); + const_reference data_element(size_type i) const; + + template + using simd_return_type = xsimd::simd_return_type; + + template + void store_simd(size_type i, const simd& e); + template + simd_return_type load_simd(size_type i) const; + +#if defined(_MSC_VER) && _MSC_VER >= 1910 + // Workaround for compiler bug in Visual Studio 2017 with respect to alias templates with non-type parameters. + template + using layout_iterator = xiterator; + template + using const_layout_iterator = xiterator; + template + using reverse_layout_iterator = std::reverse_iterator>; + template + using const_reverse_layout_iterator = std::reverse_iterator>; +#else + template + using layout_iterator = typename iterable_base::template layout_iterator; + template + using const_layout_iterator = typename iterable_base::template const_layout_iterator; + template + using reverse_layout_iterator = typename iterable_base::template reverse_layout_iterator; + template + using const_reverse_layout_iterator = typename iterable_base::template const_reverse_layout_iterator; +#endif + + template + using broadcast_iterator = typename iterable_base::template broadcast_iterator; + template + using const_broadcast_iterator = typename iterable_base::template const_broadcast_iterator; + template + using reverse_broadcast_iterator = typename iterable_base::template reverse_broadcast_iterator; + template + using const_reverse_broadcast_iterator = typename iterable_base::template const_reverse_broadcast_iterator; + + using storage_iterator = typename storage_type::iterator; + using const_storage_iterator = typename storage_type::const_iterator; + using reverse_storage_iterator = typename storage_type::reverse_iterator; + using const_reverse_storage_iterator = typename storage_type::const_reverse_iterator; + + template + using select_iterator_impl = std::conditional_t; + + template + using select_iterator = select_iterator_impl>; + template + using select_const_iterator = select_iterator_impl>; + template + using select_reverse_iterator = select_iterator_impl>; + template + using select_const_reverse_iterator = select_iterator_impl>; + + using iterator = select_iterator

; + using const_iterator = select_const_iterator
; + using reverse_iterator = select_reverse_iterator
; + using const_reverse_iterator = select_const_reverse_iterator
; + + template + select_iterator begin() noexcept; + template + select_iterator end() noexcept; + + template + select_const_iterator begin() const noexcept; + template + select_const_iterator end() const noexcept; + template + select_const_iterator cbegin() const noexcept; + template + select_const_iterator cend() const noexcept; + + template + select_reverse_iterator rbegin() noexcept; + template + select_reverse_iterator rend() noexcept; + + template + select_const_reverse_iterator rbegin() const noexcept; + template + select_const_reverse_iterator rend() const noexcept; + template + select_const_reverse_iterator crbegin() const noexcept; + template + select_const_reverse_iterator crend() const noexcept; + + template + broadcast_iterator begin(const S& shape) noexcept; + template + broadcast_iterator end(const S& shape) noexcept; + + template + const_broadcast_iterator begin(const S& shape) const noexcept; + template + const_broadcast_iterator end(const S& shape) const noexcept; + template + const_broadcast_iterator cbegin(const S& shape) const noexcept; + template + const_broadcast_iterator cend(const S& shape) const noexcept; + + + template + reverse_broadcast_iterator rbegin(const S& shape) noexcept; + template + reverse_broadcast_iterator rend(const S& shape) noexcept; + + template + const_reverse_broadcast_iterator rbegin(const S& shape) const noexcept; + template + const_reverse_broadcast_iterator rend(const S& shape) const noexcept; + template + const_reverse_broadcast_iterator crbegin(const S& shape) const noexcept; + template + const_reverse_broadcast_iterator crend(const S& shape) const noexcept; + + template + storage_iterator storage_begin() noexcept; + template + storage_iterator storage_end() noexcept; + + template + const_storage_iterator storage_begin() const noexcept; + template + const_storage_iterator storage_end() const noexcept; + template + const_storage_iterator storage_cbegin() const noexcept; + template + const_storage_iterator storage_cend() const noexcept; + + template + reverse_storage_iterator storage_rbegin() noexcept; + template + reverse_storage_iterator storage_rend() noexcept; + + template + const_reverse_storage_iterator storage_rbegin() const noexcept; + template + const_reverse_storage_iterator storage_rend() const noexcept; + template + const_reverse_storage_iterator storage_crbegin() const noexcept; + template + const_reverse_storage_iterator storage_crend() const noexcept; + + using container_iterator = storage_iterator; + using const_container_iterator = const_storage_iterator; + + protected: + + xcontainer() = default; + ~xcontainer() = default; + + xcontainer(const xcontainer&) = default; + xcontainer& operator=(const xcontainer&) = default; + + xcontainer(xcontainer&&) = default; + xcontainer& operator=(xcontainer&&) = default; + + container_iterator data_xbegin() noexcept; + const_container_iterator data_xbegin() const noexcept; + container_iterator data_xend(layout_type l) noexcept; + const_container_iterator data_xend(layout_type l) const noexcept; + + protected: + + derived_type& derived_cast() & noexcept; + const derived_type& derived_cast() const & noexcept; + derived_type derived_cast() && noexcept; + + private: + + friend class xiterable; + friend class xconst_iterable; + + template + friend class xstepper; + + template + It data_xend_impl(It end, layout_type l) const noexcept; + + inner_shape_type& mutable_shape(); + inner_strides_type& mutable_strides(); + inner_backstrides_type& mutable_backstrides(); + }; + +#undef DL + + /** + * @class xstrided_container + * @brief Partial implementation of xcontainer that embeds the strides and the shape + * + * The xstrided_container class is a partial implementation of the xcontainer interface + * that embed the strides and the shape of the multidimensional container. It does + * not embed the data container, this responsibility is delegated to the inheriting + * classes. + * + * @tparam D The derived type, i.e. the inheriting class for which xstrided_container + * provides the partial imlpementation of xcontainer. + */ + template + class xstrided_container : public xcontainer + { + public: + + using base_type = xcontainer; + using storage_type = typename base_type::storage_type; + using value_type = typename base_type::value_type; + using reference = typename base_type::reference; + using const_reference = typename base_type::const_reference; + using pointer = typename base_type::pointer; + using const_pointer = typename base_type::const_pointer; + using size_type = typename base_type::size_type; + using shape_type = typename base_type::shape_type; + using strides_type = typename base_type::strides_type; + using inner_shape_type = typename base_type::inner_shape_type; + using inner_strides_type = typename base_type::inner_strides_type; + using inner_backstrides_type = typename base_type::inner_backstrides_type; + + template + void resize(S&& shape, bool force = false); + template + void resize(S&& shape, layout_type l); + template + void resize(S&& shape, const strides_type& strides); + + template + void reshape(S&& shape, layout_type layout = base_type::static_layout); + + layout_type layout() const noexcept; + + protected: + + xstrided_container() noexcept; + ~xstrided_container() = default; + + xstrided_container(const xstrided_container&) = default; + xstrided_container& operator=(const xstrided_container&) = default; + + xstrided_container(xstrided_container&&) = default; + xstrided_container& operator=(xstrided_container&&) = default; + + explicit xstrided_container(inner_shape_type&&, inner_strides_type&&) noexcept; + + inner_shape_type& shape_impl() noexcept; + const inner_shape_type& shape_impl() const noexcept; + + inner_strides_type& strides_impl() noexcept; + const inner_strides_type& strides_impl() const noexcept; + + inner_backstrides_type& backstrides_impl() noexcept; + const inner_backstrides_type& backstrides_impl() const noexcept; + + private: + + inner_shape_type m_shape; + inner_strides_type m_strides; + inner_backstrides_type m_backstrides; + layout_type m_layout = base_type::static_layout; + }; + + /****************************** + * xcontainer implementation * + ******************************/ + + template + template + inline It xcontainer::data_xend_impl(It end, layout_type l) const noexcept + { + return strided_data_end(*this, end, l); + } + + template + inline auto xcontainer::mutable_shape() -> inner_shape_type& + { + return derived_cast().shape_impl(); + } + + template + inline auto xcontainer::mutable_strides() -> inner_strides_type& + { + return derived_cast().strides_impl(); + } + + template + inline auto xcontainer::mutable_backstrides() -> inner_backstrides_type& + { + return derived_cast().backstrides_impl(); + } + + /** + * @name Size and shape + */ + //@{ + /** + * Returns the number of element in the container. + */ + template + inline auto xcontainer::size() const noexcept -> size_type + { + return contiguous_layout ? storage().size() : compute_size(shape()); + } + + /** + * Returns the number of dimensions of the container. + */ + template + inline constexpr auto xcontainer::dimension() const noexcept -> size_type + { + return shape().size(); + } + + /** + * Returns the shape of the container. + */ + template + constexpr inline auto xcontainer::shape() const noexcept -> const inner_shape_type& + { + return derived_cast().shape_impl(); + } + + /** + * Returns the strides of the container. + */ + template + constexpr inline auto xcontainer::strides() const noexcept -> const inner_strides_type& + { + return derived_cast().strides_impl(); + } + + /** + * Returns the backstrides of the container. + */ + template + constexpr inline auto xcontainer::backstrides() const noexcept -> const inner_backstrides_type& + { + return derived_cast().backstrides_impl(); + } + //@} + + /** + * @name Data + */ + //@{ + + /** + * Fills the container with the given value. + * @param value the value to fill the container with. + */ + template + template + inline void xcontainer::fill(const T& value) + { + std::fill(storage_begin(), storage_end(), value); + } + + /** + * Returns a reference to the element at the specified position in the container. + * @param args a list of indices specifying the position in the container. Indices + * must be unsigned integers, the number of indices should be equal or greater than + * the number of dimensions of the container. + */ + template + template + inline auto xcontainer::operator()(Args... args) -> reference + { + XTENSOR_TRY(check_index(shape(), args...)); + XTENSOR_CHECK_DIMENSION(shape(), args...); + size_type index = xt::data_offset(strides(), static_cast(args)...); + return storage()[index]; + } + + /** + * Returns a constant reference to the element at the specified position in the container. + * @param args a list of indices specifying the position in the container. Indices + * must be unsigned integers, the number of indices should be equal or greater than + * the number of dimensions of the container. + */ + template + template + inline auto xcontainer::operator()(Args... args) const -> const_reference + { + XTENSOR_TRY(check_index(shape(), args...)); + XTENSOR_CHECK_DIMENSION(shape(), args...); + size_type index = xt::data_offset(strides(), static_cast(args)...); + return storage()[index]; + } + + /** + * Returns a reference to the element at the specified position in the container, + * after dimension and bounds checking. + * @param args a list of indices specifying the position in the container. Indices + * must be unsigned integers, the number of indices should be equal to the number of dimensions + * of the container. + * @exception std::out_of_range if the number of argument is greater than the number of dimensions + * or if indices are out of bounds. + */ + template + template + inline auto xcontainer::at(Args... args) -> reference + { + check_access(shape(), static_cast(args)...); + return this->operator()(args...); + } + + /** + * Returns a constant reference to the element at the specified position in the container, + * after dimension and bounds checking. + * @param args a list of indices specifying the position in the container. Indices + * must be unsigned integers, the number of indices should be equal to the number of dimensions + * of the container. + * @exception std::out_of_range if the number of argument is greater than the number of dimensions + * or if indices are out of bounds. + */ + template + template + inline auto xcontainer::at(Args... args) const -> const_reference + { + check_access(shape(), static_cast(args)...); + return this->operator()(args...); + } + + /** + * Returns a reference to the element at the specified position in the container. + * @param args a list of indices specifying the position in the container. Indices + * must be unsigned integers, the number of indices must be equal to the number of + * dimensions of the container, else the behavior is undefined. + * + * @warning This method is meant for performance, for expressions with a dynamic + * number of dimensions (i.e. not known at compile time). Since it may have + * undefined behavior (see parameters), operator() should be prefered whenever + * it is possible. + * @warning This method is NOT compatible with broadcasting, meaning the following + * code has undefined behavior: + * \code{.cpp} + * xt::xarray a = {{0, 1}, {2, 3}}; + * xt::xarray b = {0, 1}; + * auto fd = a + b; + * double res = fd.uncheked(0, 1); + * \endcode + */ + template + template + inline auto xcontainer::unchecked(Args... args) -> reference + { + size_type index = xt::unchecked_data_offset(strides(), static_cast(args)...); + return storage()[index]; + } + + /** + * Returns a constant reference to the element at the specified position in the container. + * @param args a list of indices specifying the position in the container. Indices + * must be unsigned integers, the number of indices must be equal to the number of + * dimensions of the container, else the behavior is undefined. + * + * @warning This method is meant for performance, for expressions with a dynamic + * number of dimensions (i.e. not known at compile time). Since it may have + * undefined behavior (see parameters), operator() should be prefered whenever + * it is possible. + * @warning This method is NOT compatible with broadcasting, meaning the following + * code has undefined behavior: + * \code{.cpp} + * xt::xarray a = {{0, 1}, {2, 3}}; + * xt::xarray b = {0, 1}; + * auto fd = a + b; + * double res = fd.uncheked(0, 1); + * \endcode + */ + template + template + inline auto xcontainer::unchecked(Args... args) const -> const_reference + { + size_type index = xt::unchecked_data_offset(strides(), static_cast(args)...); + return storage()[index]; + } + + /** + * Returns a reference to the element at the specified position in the container. + * @param index a sequence of indices specifying the position in the container. Indices + * must be unsigned integers, the number of indices in the list should be equal or greater + * than the number of dimensions of the container. + */ + template + template + inline auto xcontainer::operator[](const S& index) + -> disable_integral_t + { + return element(index.cbegin(), index.cend()); + } + + template + template + inline auto xcontainer::operator[](std::initializer_list index) -> reference + { + return element(index.begin(), index.end()); + } + + template + inline auto xcontainer::operator[](size_type i) -> reference + { + return operator()(i); + } + + /** + * Returns a constant reference to the element at the specified position in the container. + * @param index a sequence of indices specifying the position in the container. Indices + * must be unsigned integers, the number of indices in the list should be equal or greater + * than the number of dimensions of the container. + */ + template + template + inline auto xcontainer::operator[](const S& index) const + -> disable_integral_t + { + return element(index.cbegin(), index.cend()); + } + + template + template + inline auto xcontainer::operator[](std::initializer_list index) const -> const_reference + { + return element(index.begin(), index.end()); + } + + template + inline auto xcontainer::operator[](size_type i) const -> const_reference + { + return operator()(i); + } + + /** + * Returns a reference to the element at the specified position in the container. + * @param first iterator starting the sequence of indices + * @param last iterator ending the sequence of indices + * The number of indices in the sequence should be equal to or greater + * than the number of dimensions of the container. + */ + template + template + inline auto xcontainer::element(It first, It last) -> reference + { + XTENSOR_TRY(check_element_index(shape(), first, last)); + return storage()[element_offset(strides(), first, last)]; + } + + /** + * Returns a reference to the element at the specified position in the container. + * @param first iterator starting the sequence of indices + * @param last iterator ending the sequence of indices + * The number of indices in the sequence should be equal to or greater + * than the number of dimensions of the container. + */ + template + template + inline auto xcontainer::element(It first, It last) const -> const_reference + { + XTENSOR_TRY(check_element_index(shape(), first, last)); + return storage()[element_offset(strides(), first, last)]; + } + + /** + * Returns a reference to the buffer containing the elements of the container. + */ + template + inline auto xcontainer::storage() noexcept -> storage_type& + { + return derived_cast().storage_impl(); + } + + /** + * Returns a constant reference to the buffer containing the elements of the + * container. + */ + template + inline auto xcontainer::storage() const noexcept -> const storage_type& + { + return derived_cast().storage_impl(); + } + + /** + * Returns a pointer to the underlying array serving as element storage. The pointer + * is such that range [data(); data() + size()] is always a valid range, even if the + * container is empty (data() is not is not dereferenceable in that case) + */ + template + inline auto xcontainer::data() noexcept -> value_type* + { + return storage().data(); + } + + /** + * Returns a constant pointer to the underlying array serving as element storage. The pointer + * is such that range [data(); data() + size()] is always a valid range, even if the + * container is empty (data() is not is not dereferenceable in that case) + */ + template + inline auto xcontainer::data() const noexcept -> const value_type* + { + return storage().data(); + } + + /** + * Returns the offset to the first element in the container. + */ + template + inline auto xcontainer::data_offset() const noexcept -> const size_type + { + return size_type(0); + } + //@} + + /** + * @name Broadcasting + */ + //@{ + /** + * Broadcast the shape of the container to the specified parameter. + * @param shape the result shape + * @param reuse_cache parameter for internal optimization + * @return a boolean indicating whether the broadcasting is trivial + */ + template + template + inline bool xcontainer::broadcast_shape(S& shape, bool) const + { + return xt::broadcast_shape(this->shape(), shape); + } + + /** + * Compares the specified strides with those of the container to see whether + * the broadcasting is trivial. + * @return a boolean indicating whether the broadcasting is trivial + */ + template + template + inline bool xcontainer::is_trivial_broadcast(const S& str) const noexcept + { + return str.size() == strides().size() && + std::equal(str.cbegin(), str.cend(), strides().begin()); + } + //@} + + /**************** + * Iterator api * + ****************/ + + template + template + inline auto xcontainer::begin() noexcept -> select_iterator + { + return xtl::mpl::static_if([&](auto self) + { + return self(*this).template storage_begin(); + }, /*else*/ [&](auto self) + { + return self(*this).iterable_base::template begin(); + }); + } + + template + template + inline auto xcontainer::end() noexcept -> select_iterator + { + return xtl::mpl::static_if([&](auto self) + { + return self(*this).template storage_end(); + }, /*else*/ [&](auto self) + { + return self(*this).iterable_base::template end(); + }); + } + + template + template + inline auto xcontainer::begin() const noexcept -> select_const_iterator + { + return this->template cbegin(); + } + + template + template + inline auto xcontainer::end() const noexcept -> select_const_iterator + { + return this->template cend(); + } + + template + template + inline auto xcontainer::cbegin() const noexcept -> select_const_iterator + { + return xtl::mpl::static_if([&](auto self) + { + return self(*this).template storage_cbegin(); + }, /*else*/ [&](auto self) + { + return self(*this).iterable_base::template cbegin(); + }); + } + + template + template + inline auto xcontainer::cend() const noexcept -> select_const_iterator + { + return xtl::mpl::static_if([&](auto self) + { + return self(*this).template storage_cend(); + }, /*else*/ [&](auto self) + { + return self(*this).iterable_base::template cend(); + }); + } + + template + template + inline auto xcontainer::rbegin() noexcept -> select_reverse_iterator + { + return xtl::mpl::static_if([&](auto self) + { + return self(*this).template storage_rbegin(); + }, /*else*/ [&](auto self) + { + return self(*this).iterable_base::template rbegin(); + }); + } + + template + template + inline auto xcontainer::rend() noexcept -> select_reverse_iterator + { + return xtl::mpl::static_if([&](auto self) + { + return self(*this).template storage_rend(); + }, /*else*/ [&](auto self) + { + return self(*this).iterable_base::template rend(); + }); + } + + template + template + inline auto xcontainer::rbegin() const noexcept -> select_const_reverse_iterator + { + return this->template crbegin(); + } + + template + template + inline auto xcontainer::rend() const noexcept -> select_const_reverse_iterator + { + return this->template crend(); + } + + template + template + inline auto xcontainer::crbegin() const noexcept -> select_const_reverse_iterator + { + return xtl::mpl::static_if([&](auto self) + { + return self(*this).template storage_crbegin(); + }, /*else*/ [&](auto self) + { + return self(*this).iterable_base::template crbegin(); + }); + } + + template + template + inline auto xcontainer::crend() const noexcept -> select_const_reverse_iterator + { + return xtl::mpl::static_if([&](auto self) + { + return self(*this).template storage_crend(); + }, /*else*/ [&](auto self) + { + return self(*this).iterable_base::template crend(); + }); + } + + /***************************** + * Broadcasting iterator api * + *****************************/ + + template + template + inline auto xcontainer::begin(const S& shape) noexcept -> broadcast_iterator + { + return iterable_base::template begin(shape); + } + + template + template + inline auto xcontainer::end(const S& shape) noexcept -> broadcast_iterator + { + return iterable_base::template end(shape); + } + + template + template + inline auto xcontainer::begin(const S& shape) const noexcept -> const_broadcast_iterator + { + return iterable_base::template begin(shape); + } + + template + template + inline auto xcontainer::end(const S& shape) const noexcept -> const_broadcast_iterator + { + return iterable_base::template end(shape); + } + + template + template + inline auto xcontainer::cbegin(const S& shape) const noexcept -> const_broadcast_iterator + { + return iterable_base::template cbegin(shape); + } + + template + template + inline auto xcontainer::cend(const S& shape) const noexcept -> const_broadcast_iterator + { + return iterable_base::template cend(shape); + } + + template + template + inline auto xcontainer::rbegin(const S& shape) noexcept -> reverse_broadcast_iterator + { + return iterable_base::template rbegin(shape); + } + + template + template + inline auto xcontainer::rend(const S& shape) noexcept -> reverse_broadcast_iterator + { + return iterable_base::template rend(shape); + } + + template + template + inline auto xcontainer::rbegin(const S& shape) const noexcept -> const_reverse_broadcast_iterator + { + return iterable_base::template rbegin(shape); + } + + template + template + inline auto xcontainer::rend(const S& shape) const noexcept -> const_reverse_broadcast_iterator + { + return iterable_base::template rend(shape); + } + + template + template + inline auto xcontainer::crbegin(const S& shape) const noexcept -> const_reverse_broadcast_iterator + { + return iterable_base::template crbegin(shape); + } + + template + template + inline auto xcontainer::crend(const S& shape) const noexcept -> const_reverse_broadcast_iterator + { + return iterable_base::template crend(shape); + } + + /*********************** + * Linear iterator api * + ***********************/ + + template + template + inline auto xcontainer::storage_begin() noexcept -> storage_iterator + { + return storage().begin(); + } + + template + template + inline auto xcontainer::storage_end() noexcept -> storage_iterator + { + return storage().end(); + } + + template + template + inline auto xcontainer::storage_begin() const noexcept -> const_storage_iterator + { + return storage().begin(); + } + + template + template + inline auto xcontainer::storage_end() const noexcept -> const_storage_iterator + { + return storage().end(); + } + + template + template + inline auto xcontainer::storage_cbegin() const noexcept -> const_storage_iterator + { + return storage().cbegin(); + } + + template + template + inline auto xcontainer::storage_cend() const noexcept -> const_storage_iterator + { + return storage().cend(); + } + + template + template + inline auto xcontainer::storage_rbegin() noexcept -> reverse_storage_iterator + { + return storage().rbegin(); + } + + template + template + inline auto xcontainer::storage_rend() noexcept -> reverse_storage_iterator + { + return storage().rend(); + } + + template + template + inline auto xcontainer::storage_rbegin() const noexcept -> const_reverse_storage_iterator + { + return storage().rbegin(); + } + + template + template + inline auto xcontainer::storage_rend() const noexcept -> const_reverse_storage_iterator + { + return storage().rend(); + } + + template + template + inline auto xcontainer::storage_crbegin() const noexcept -> const_reverse_storage_iterator + { + return storage().crbegin(); + } + + template + template + inline auto xcontainer::storage_crend() const noexcept -> const_reverse_storage_iterator + { + return storage().crend(); + } + + /*************** + * stepper api * + ***************/ + + template + template + inline auto xcontainer::stepper_begin(const S& shape) noexcept -> stepper + { + size_type offset = shape.size() - dimension(); + return stepper(static_cast(this), data_xbegin(), offset); + } + + template + template + inline auto xcontainer::stepper_end(const S& shape, layout_type l) noexcept -> stepper + { + size_type offset = shape.size() - dimension(); + return stepper(static_cast(this), data_xend(l), offset); + } + + template + template + inline auto xcontainer::stepper_begin(const S& shape) const noexcept -> const_stepper + { + size_type offset = shape.size() - dimension(); + return const_stepper(static_cast(this), data_xbegin(), offset); + } + + template + template + inline auto xcontainer::stepper_end(const S& shape, layout_type l) const noexcept -> const_stepper + { + size_type offset = shape.size() - dimension(); + return const_stepper(static_cast(this), data_xend(l), offset); + } + + template + inline auto xcontainer::data_xbegin() noexcept -> container_iterator + { + return storage().begin(); + } + + template + inline auto xcontainer::data_xbegin() const noexcept -> const_container_iterator + { + return storage().begin(); + } + + template + inline auto xcontainer::data_xend(layout_type l) noexcept -> container_iterator + { + return data_xend_impl(storage().end(), l); + } + + template + inline auto xcontainer::data_xend(layout_type l) const noexcept -> const_container_iterator + { + return data_xend_impl(storage().end(), l); + } + + template + inline auto xcontainer::derived_cast() & noexcept -> derived_type& + { + return *static_cast(this); + } + + template + inline auto xcontainer::derived_cast() const & noexcept -> const derived_type& + { + return *static_cast(this); + } + + template + inline auto xcontainer::derived_cast() && noexcept -> derived_type + { + return *static_cast(this); + } + + template + inline auto xcontainer::data_element(size_type i) -> reference + { + return storage()[i]; + } + + template + inline auto xcontainer::data_element(size_type i) const -> const_reference + { + return storage()[i]; + } + + template + template + inline void xcontainer::store_simd(size_type i, const simd& e) + { + using align_mode = driven_align_mode_t; + xsimd::store_simd(&(storage()[i]), e, align_mode()); + } + + template + template + inline auto xcontainer::load_simd(size_type i) const -> simd_return_type + { + using align_mode = driven_align_mode_t; + return xsimd::load_simd(&(storage()[i]), align_mode()); + } + + /************************************* + * xstrided_container implementation * + *************************************/ + + template + inline xstrided_container::xstrided_container() noexcept + : base_type() + { + m_shape = xtl::make_sequence(base_type::dimension(), 1); + m_strides = xtl::make_sequence(base_type::dimension(), 0); + m_backstrides = xtl::make_sequence(base_type::dimension(), 0); + } + + template + inline xstrided_container::xstrided_container(inner_shape_type&& shape, inner_strides_type&& strides) noexcept + : base_type(), m_shape(std::move(shape)), m_strides(std::move(strides)) + { + m_backstrides = xtl::make_sequence(m_shape.size(), 0); + adapt_strides(m_shape, m_strides, m_backstrides); + } + + template + inline auto xstrided_container::shape_impl() noexcept -> inner_shape_type& + { + return m_shape; + } + + template + inline auto xstrided_container::shape_impl() const noexcept -> const inner_shape_type& + { + return m_shape; + } + + template + inline auto xstrided_container::strides_impl() noexcept -> inner_strides_type& + { + return m_strides; + } + + template + inline auto xstrided_container::strides_impl() const noexcept -> const inner_strides_type& + { + return m_strides; + } + + template + inline auto xstrided_container::backstrides_impl() noexcept -> inner_backstrides_type& + { + return m_backstrides; + } + + template + inline auto xstrided_container::backstrides_impl() const noexcept -> const inner_backstrides_type& + { + return m_backstrides; + } + + /** + * Return the layout_type of the container + * @return layout_type of the container + */ + template + layout_type xstrided_container::layout() const noexcept + { + return m_layout; + } + + namespace detail + { + template + inline void resize_data_container(C& c, S size) + { + xt::resize_container(c, size); + } + + template + inline void resize_data_container(const C& c, S size) + { + (void) c; // remove unused parameter warning + (void) size; + XTENSOR_ASSERT_MSG(c.size() == size, "Trying to resize const data container with wrong size."); + } + } + + /** + * resizes the container. + * @param shape the new shape + * @param force force reshaping, even if the shape stays the same (default: false) + */ + template + template + inline void xstrided_container::resize(S&& shape, bool force) + { + if (m_shape.size() != shape.size() || !std::equal(std::begin(shape), std::end(shape), std::begin(m_shape)) || force) + { + if (m_layout == layout_type::dynamic || m_layout == layout_type::any) + { + m_layout = XTENSOR_DEFAULT_LAYOUT; // fall back to default layout + } + m_shape = xtl::forward_sequence(shape); + resize_container(m_strides, m_shape.size()); + resize_container(m_backstrides, m_shape.size()); + size_type data_size = compute_strides(m_shape, m_layout, m_strides, m_backstrides); + detail::resize_data_container(this->storage(), data_size); + } + } + + /** + * resizes the container. + * @param shape the new shape + * @param l the new layout_type + */ + template + template + inline void xstrided_container::resize(S&& shape, layout_type l) + { + if (base_type::static_layout != layout_type::dynamic && l != base_type::static_layout) + { + throw std::runtime_error("Cannot change layout_type if template parameter not layout_type::dynamic."); + } + m_layout = l; + resize(std::forward(shape), true); + } + + /** + * Resizes the container. + * @param shape the new shape + * @param strides the new strides + */ + template + template + inline void xstrided_container::resize(S&& shape, const strides_type& strides) + { + if (base_type::static_layout != layout_type::dynamic) + { + throw std::runtime_error("Cannot resize with custom strides when layout() is != layout_type::dynamic."); + } + m_shape = xtl::forward_sequence(shape); + m_strides = strides; + resize_container(m_backstrides, m_strides.size()); + adapt_strides(m_shape, m_strides, m_backstrides); + m_layout = layout_type::dynamic; + detail::resize_data_container(this->storage(), compute_size(m_shape)); + } + + /** + * Reshapes the container and keeps old elements + * @param shape the new shape (has to have same number of elements as the original container) + * @param layout the layout to compute the strides (defaults to static layout of the container, + * or for a container with dynamic layout to XTENSOR_DEFAULT_LAYOUT) + */ + template + template + inline void xstrided_container::reshape(S&& shape, layout_type layout) + { + if (compute_size(shape) != this->size()) + { + throw std::runtime_error("Cannot reshape with incorrect number of elements. Do you mean to resize?"); + } + if (layout == layout_type::dynamic || layout == layout_type::any) + { + layout = XTENSOR_DEFAULT_LAYOUT; // fall back to default layout + } + if (layout != base_type::static_layout && base_type::static_layout != layout_type::dynamic) + { + throw std::runtime_error("Cannot reshape with different layout if static layout != dynamic."); + } + m_layout = layout; + m_shape = xtl::forward_sequence(shape); + resize_container(m_strides, m_shape.size()); + resize_container(m_backstrides, m_shape.size()); + compute_strides(m_shape, m_layout, m_strides, m_backstrides); + } +} + +#endif diff --git a/vendor/xtensor/include/xtensor/xcsv.hpp b/vendor/xtensor/include/xtensor/xcsv.hpp new file mode 100644 index 0000000000..4a2716d400 --- /dev/null +++ b/vendor/xtensor/include/xtensor/xcsv.hpp @@ -0,0 +1,169 @@ +/*************************************************************************** +* Copyright (c) 2016, Johan Mabille, Sylvain Corlay and Wolf Vollprecht * +* * +* Distributed under the terms of the BSD 3-Clause License. * +* * +* The full license is in the file LICENSE, distributed with this software. * +****************************************************************************/ + +#ifndef XTENSOR_CSV_HPP +#define XTENSOR_CSV_HPP + +#include +#include +#include +#include +#include +#include + +#include "xtensor.hpp" + +namespace xt +{ + + /************************************** + * load_csv and dump_csv declarations * + **************************************/ + + template > + using xcsv_tensor = xtensor_container, 2, layout_type::row_major>; + + template > + xcsv_tensor load_csv(std::istream& stream); + + template + void dump_csv(std::ostream& stream, const xexpression& e); + + /***************************************** + * load_csv and dump_csv implementations * + *****************************************/ + + namespace detail + { + template + inline T lexical_cast(const std::string& cell) + { + T res; + std::istringstream iss(cell); + iss >> res; + return res; + } + + template <> + inline float lexical_cast(const std::string& cell) { return std::stof(cell); } + + template <> + inline double lexical_cast(const std::string& cell) { return std::stod(cell); } + + template <> + inline long double lexical_cast(const std::string& cell) { return std::stold(cell); } + + template <> + inline int lexical_cast(const std::string& cell) { return std::stoi(cell); } + + template <> + inline long lexical_cast(const std::string& cell) { return std::stol(cell); } + + template <> + inline long long lexical_cast(const std::string& cell) { return std::stoll(cell); } + + template <> + inline unsigned int lexical_cast(const std::string& cell) { return static_cast(std::stoul(cell)); } + + template <> + inline unsigned long lexical_cast(const std::string& cell) { return std::stoul(cell); } + + template <> + inline unsigned long long lexical_cast(const std::string& cell) { return std::stoull(cell); } + + template + ST load_csv_row(std::istream& row_stream, OI output, std::string cell) + { + ST length = 0; + while (std::getline(row_stream, cell, ',')) + { + *output++ = lexical_cast(cell); + ++length; + } + return length; + } + } + + /** + * @brief Load tensor from CSV. + * + * Returns an \ref xexpression for the parsed CSV + * @param stream the input stream containing the CSV encoded values + */ + template + xcsv_tensor load_csv(std::istream& stream) + { + using tensor_type = xcsv_tensor; + using storage_type = typename tensor_type::storage_type; + using size_type = typename tensor_type::size_type; + using inner_shape_type = typename tensor_type::inner_shape_type; + using inner_strides_type = typename tensor_type::inner_strides_type; + using output_iterator = std::back_insert_iterator; + + storage_type data; + size_type nbrow = 0, nbcol = 0; + { + output_iterator output(data); + std::string row, cell; + while (std::getline(stream, row)) + { + std::stringstream row_stream(row); + nbcol = detail::load_csv_row(row_stream, output, cell); + ++nbrow; + } + } + inner_shape_type shape = {nbrow, nbcol}; + inner_strides_type strides; // no need for initializer list for stack-allocated strides_type + size_type data_size = compute_strides(shape, layout_type::row_major, strides); + // Sanity check for data size. + if (data.size() != data_size) + { + throw std::runtime_error("Inconsistent row lengths in CSV"); + } + return tensor_type(std::move(data), std::move(shape), std::move(strides)); + } + + /** + * @brief Dump tensor to CSV. + * + * @param stream the output stream to write the CSV encoded values + * @param e the tensor expression to serialize + */ + template + void dump_csv(std::ostream& stream, const xexpression& e) + { + using size_type = typename E::size_type; + const E& ex = e.derived_cast(); + if (ex.dimension() != 2) + { + throw std::runtime_error("Only 2-D expressions can be serialized to CSV"); + } + size_type nbrows = ex.shape()[0], nbcols = ex.shape()[1]; + auto st = ex.stepper_begin(ex.shape()); + for (size_type r = 0; r != nbrows; ++r) + { + for (size_type c = 0; c != nbcols; ++c) + { + stream << *st; + if (c != nbcols - 1) + { + st.step(1); + stream << ','; + } + else + { + st.reset(1); + st.step(0); + stream << std::endl; + } + } + } + } +} + +#endif diff --git a/vendor/xtensor/include/xtensor/xeval.hpp b/vendor/xtensor/include/xtensor/xeval.hpp new file mode 100644 index 0000000000..18d53beea6 --- /dev/null +++ b/vendor/xtensor/include/xtensor/xeval.hpp @@ -0,0 +1,57 @@ +/*************************************************************************** +* Copyright (c) 2016, Johan Mabille, Sylvain Corlay and Wolf Vollprecht * +* * +* Distributed under the terms of the BSD 3-Clause License. * +* * +* The full license is in the file LICENSE, distributed with this software. * +****************************************************************************/ + +#ifndef XTENSOR_EVAL_HPP +#define XTENSOR_EVAL_HPP + +#include "xtensor_forward.hpp" + +namespace xt +{ + + namespace detail + { + template + using is_container = std::is_base_of>, T>; + } + + /** + * Force evaluation of xexpression. + * @return xarray or xtensor depending on shape type + * + * \code{.cpp} + * xarray a = {1,2,3,4}; + * auto&& b = xt::eval(a); // b is a reference to a, no copy! + * auto&& c = xt::eval(a + b); // c is xarray, not an xexpression + * \endcode + */ + template + inline auto eval(T&& t) + -> std::enable_if_t>::value, T&&> + { + return std::forward(t); + } + + /// @cond DOXYGEN_INCLUDE_SFINAE + template > + inline auto eval(T&& t) + -> std::enable_if_t::value && detail::is_array::value, xtensor::value>> + { + return xtensor::value>(std::forward(t)); + } + + template > + inline auto eval(T&& t) + -> std::enable_if_t::value && !detail::is_array::value, xt::xarray> + { + return xarray(std::forward(t)); + } + /// @endcond +} + +#endif diff --git a/vendor/xtensor/include/xtensor/xexception.hpp b/vendor/xtensor/include/xtensor/xexception.hpp new file mode 100644 index 0000000000..689ce31e09 --- /dev/null +++ b/vendor/xtensor/include/xtensor/xexception.hpp @@ -0,0 +1,219 @@ +/*************************************************************************** +* Copyright (c) 2016, Johan Mabille, Sylvain Corlay and Wolf Vollprecht * +* * +* Distributed under the terms of the BSD 3-Clause License. * +* * +* The full license is in the file LICENSE, distributed with this software. * +****************************************************************************/ + +#ifndef XTENSOR_EXCEPTION_HPP +#define XTENSOR_EXCEPTION_HPP + +#include +#include +#include +#include + +namespace xt +{ + + /******************* + * broadcast_error * + *******************/ + + class broadcast_error : public std::runtime_error + { + public: + + explicit broadcast_error(const char* msg) + : std::runtime_error(msg) + { + } + }; + + template + [[noreturn]] void throw_broadcast_error(const S1& lhs, const S2& rhs); + + /********************************** + * broadcast_error implementation * + **********************************/ + +#ifdef NDEBUG + // Do not inline this function + template + [[noreturn]] void throw_broadcast_error(const S1&, const S2&) + { + throw broadcast_error("Incompatible dimension of arrays, compile in DEBUG for more info"); + } +#else + template + [[noreturn]] void throw_broadcast_error(const S1& lhs, const S2& rhs) + { + std::ostringstream buf("Incompatible dimension of arrays:", std::ios_base::ate); + + buf << "\n LHS shape = ("; + using size_type1 = typename S1::value_type; + std::ostream_iterator iter1(buf, ", "); + std::copy(lhs.cbegin(), lhs.cend(), iter1); + + buf << ")\n RHS shape = ("; + using size_type2 = typename S2::value_type; + std::ostream_iterator iter2(buf, ", "); + std::copy(rhs.cbegin(), rhs.cend(), iter2); + buf << ")"; + + throw broadcast_error(buf.str().c_str()); + } +#endif + + /******************* + * transpose_error * + *******************/ + + class transpose_error : public std::runtime_error + { + public: + + explicit transpose_error(const char* msg) + : std::runtime_error(msg) + { + } + }; + + /*************** + * check_index * + ***************/ + + template + void check_index(const S& shape, Args... args); + + template + void check_element_index(const S& shape, It first, It last); + + namespace detail + { + template + inline void check_index_impl(const S&) + { + } + + template + inline void check_index_impl(const S& shape, std::size_t arg, Args... args) + { + if (sizeof...(Args) + 1 > shape.size()) + { + check_index_impl(shape, args...); + } + else + { + if (arg >= std::size_t(shape[dim]) && shape[dim] != 1) + { + throw std::out_of_range("index " + std::to_string(arg) + " is out of bounds for axis " + + std::to_string(dim) + " with size " + std::to_string(shape[dim])); + } + check_index_impl(shape, args...); + } + } + } + + template + inline void check_index(const S& shape, Args... args) + { + using value_type = typename S::value_type; + detail::check_index_impl(shape, static_cast(args)...); + } + + template + inline void check_element_index(const S& shape, It first, It last) + { + using value_type = typename std::iterator_traits::value_type; + auto dst = static_cast(last - first); + It efirst = last - static_cast((std::min)(shape.size(), dst)); + std::size_t axis = 0; + while (efirst != last) + { + if (*efirst >= value_type(shape[axis]) && shape[axis] != 1) + { + throw std::out_of_range("index " + std::to_string(*efirst) + " is out of bounds for axis " + + std::to_string(axis) + " with size " + std::to_string(shape[axis])); + } + ++efirst, ++axis; + } + } + + /******************* + * check_dimension * + *******************/ + + template + inline void check_dimension(const S& shape, Args...) + { + if (sizeof...(Args) > shape.size()) + { + throw std::out_of_range("Number of arguments (" + std::to_string(sizeof...(Args)) + ") us greater " + + "than the number of dimensions (" + std::to_string(shape.size()) + ")"); + } + } + + /**************** + * check_access * + ****************/ + + template + inline void check_access(const S& shape, Args... args) + { + check_dimension(shape, args...); + check_index(shape, args...); + } + +#ifdef XTENSOR_ENABLE_ASSERT +#define XTENSOR_TRY(expr) XTENSOR_TRY_IMPL(expr, __FILE__, __LINE__) +#define XTENSOR_TRY_IMPL(expr, file, line) \ + try \ + { \ + expr; \ + } \ + catch (std::exception& e) \ + { \ + throw std::runtime_error(std::string(file) + ':' + std::to_string(line) + ": check failed\n\t" + std::string(e.what())); \ + } +#else +#define XTENSOR_TRY(expr) +#endif + +#ifdef XTENSOR_ENABLE_ASSERT +#define XTENSOR_ASSERT(expr) XTENSOR_ASSERT_IMPL(expr, __FILE__, __LINE__) +#define XTENSOR_ASSERT_IMPL(expr, file, line) \ + if (!(expr)) \ + { \ + throw std::runtime_error(std::string(file) + ':' + std::to_string(line) + ": assertion failed (" #expr ") \n\t"); \ + } +#else +#define XTENSOR_ASSERT(expr) +#endif + +#ifdef XTENSOR_ENABLE_CHECK_DIMENSION +#define XTENSOR_CHECK_DIMENSION(S, ARGS) XTENSOR_TRY(check_dimension(S, ARGS)) +#else +#define XTENSOR_CHECK_DIMENSION(S, ARGS) +#endif + +#ifdef XTENSOR_ENABLE_ASSERT +#define XTENSOR_ASSERT_MSG(expr, msg) \ + if (!(expr)) \ + { \ + throw std::runtime_error(std::string("Assertion error!\n") + msg + \ + "\n " + __FILE__ + '(' + std::to_string(__LINE__) + ")\n"); \ + } +#else +#define XTENSOR_ASSERT_MSG(expr, msg) +#endif + +#define XTENSOR_PRECONDITION(expr, msg) \ + if (!(expr)) \ + { \ + throw std::runtime_error(std::string("Precondition violation!\n") + msg + \ + "\n " + __FILE__ + '(' + std::to_string(__LINE__) + ")\n"); \ + } +} +#endif // XEXCEPTION_HPP diff --git a/vendor/xtensor/include/xtensor/xexpression.hpp b/vendor/xtensor/include/xtensor/xexpression.hpp new file mode 100644 index 0000000000..a83ea0c582 --- /dev/null +++ b/vendor/xtensor/include/xtensor/xexpression.hpp @@ -0,0 +1,307 @@ +/*************************************************************************** +* Copyright (c) 2016, Johan Mabille, Sylvain Corlay and Wolf Vollprecht * +* * +* Distributed under the terms of the BSD 3-Clause License. * +* * +* The full license is in the file LICENSE, distributed with this software. * +****************************************************************************/ + +#ifndef XTENSOR_EXPRESSION_HPP +#define XTENSOR_EXPRESSION_HPP + +#include +#include +#include + +#include +#include + +#include "xshape.hpp" +#include "xutils.hpp" + +namespace xt +{ + + /*************************** + * xexpression declaration * + ***************************/ + + /** + * @class xexpression + * @brief Base class for xexpressions + * + * The xexpression class is the base class for all classes representing an expression + * that can be evaluated to a multidimensional container with tensor semantic. + * Functions that can apply to any xexpression regardless of its specific type should take a + * xexpression argument. + * + * \tparam E The derived type. + * + */ + template + class xexpression + { + public: + + using derived_type = D; + + derived_type& derived_cast() & noexcept; + const derived_type& derived_cast() const & noexcept; + derived_type derived_cast() && noexcept; + + protected: + + xexpression() = default; + ~xexpression() = default; + + xexpression(const xexpression&) = default; + xexpression& operator=(const xexpression&) = default; + + xexpression(xexpression&&) = default; + xexpression& operator=(xexpression&&) = default; + }; + + /****************************** + * xexpression implementation * + ******************************/ + + /** + * @name Downcast functions + */ + //@{ + /** + * Returns a reference to the actual derived type of the xexpression. + */ + template + inline auto xexpression::derived_cast() & noexcept -> derived_type& + { + return *static_cast(this); + } + + /** + * Returns a constant reference to the actual derived type of the xexpression. + */ + template + inline auto xexpression::derived_cast() const & noexcept -> const derived_type& + { + return *static_cast(this); + } + + /** + * Returns a constant reference to the actual derived type of the xexpression. + */ + template + inline auto xexpression::derived_cast() && noexcept -> derived_type + { + return *static_cast(this); + } + //@} + + namespace detail + { + template + struct is_xexpression_impl : std::is_base_of>, std::decay_t> + { + }; + + template + struct is_xexpression_impl> : std::true_type + { + }; + } + + template + using is_xexpression = detail::is_xexpression_impl; + + template + using enable_xexpression = typename std::enable_if::value, R>::type; + + template + using disable_xexpression = typename std::enable_if::value, R>::type; + + template + using has_xexpression = xtl::disjunction...>; + + /************ + * xclosure * + ************/ + + template + class xscalar; + + template + struct xclosure + { + using type = xtl::closure_type_t; + }; + + template + struct xclosure>> + { + using type = xscalar>; + }; + + template + using xclosure_t = typename xclosure::type; + + template + struct const_xclosure + { + using type = xtl::const_closure_type_t; + }; + + template + struct const_xclosure>> + { + using type = xscalar>; + }; + + template + using const_xclosure_t = typename const_xclosure::type; + + /*************** + * xvalue_type * + ***************/ + + namespace detail + { + template + struct xvalue_type_impl + { + using type = E; + }; + + template + struct xvalue_type_impl::value>> + { + using type = typename E::value_type; + }; + } + + template + using xvalue_type = detail::xvalue_type_impl; + + template + using xvalue_type_t = typename xvalue_type::type; + + /************************* + * expression tag system * + *************************/ + + struct xscalar_expression_tag + { + }; + + struct xtensor_expression_tag + { + }; + + struct xoptional_expression_tag + { + }; + + namespace detail + { + template > + struct get_expression_tag + { + using type = xtensor_expression_tag; + }; + + template + struct get_expression_tag::expression_tag>> + { + using type = typename std::decay_t::expression_tag; + }; + + template + using get_expression_tag_t = typename get_expression_tag::type; + + template + struct expression_tag_and; + + template + struct expression_tag_and + { + using type = T; + }; + + template + struct expression_tag_and + { + using type = T; + }; + + template <> + struct expression_tag_and + { + using type = xscalar_expression_tag; + }; + + template + struct expression_tag_and + { + using type = T; + }; + + template + struct expression_tag_and + : expression_tag_and + { + }; + + template <> + struct expression_tag_and + { + using type = xoptional_expression_tag; + }; + + template <> + struct expression_tag_and + : expression_tag_and + { + }; + + template + struct expression_tag_and + : expression_tag_and::type> + { + }; + + template + using expression_tag_and_t = typename expression_tag_and::type; + } + + template + struct xexpression_tag + { + using type = detail::expression_tag_and_t>>...>; + }; + + template + using xexpression_tag_t = typename xexpression_tag::type; + + template + struct is_xtensor_expression : std::is_same, xtensor_expression_tag> + { + }; + + template + struct is_xoptional_expression : std::is_same, xoptional_expression_tag> + { + }; + + /******************************** + * xoptional_comparable concept * + ********************************/ + + template + struct xoptional_comparable : xtl::conjunction, + is_xoptional_expression + >... + > + { + }; +} + +#endif diff --git a/vendor/xtensor/include/xtensor/xfixed.hpp b/vendor/xtensor/include/xtensor/xfixed.hpp new file mode 100644 index 0000000000..4438825975 --- /dev/null +++ b/vendor/xtensor/include/xtensor/xfixed.hpp @@ -0,0 +1,818 @@ +/*************************************************************************** +* Copyright (c) 2016, Johan Mabille, Sylvain Corlay and Wolf Vollprecht * +* * +* Distributed under the terms of the BSD 3-Clause License. * +* * +* The full license is in the file LICENSE, distributed with this software. * +****************************************************************************/ + +#ifndef XTENSOR_FIXED_HPP +#define XTENSOR_FIXED_HPP + +#include +#include +#include +#include +#include + +#include "xcontainer.hpp" +#include "xstrides.hpp" +#include "xstorage.hpp" +#include "xsemantic.hpp" + +#ifdef _MSC_VER + #define XTENSOR_CONSTEXPR_ENHANCED const + #define XTENSOR_CONSTEXPR_ENHANCED_STATIC const + #define XTENSOR_CONSTEXPR_RETURN +#else + #define XTENSOR_CONSTEXPR_ENHANCED constexpr + #define XTENSOR_CONSTEXPR_RETURN constexpr + #define XTENSOR_CONSTEXPR_ENHANCED_STATIC constexpr static + #define XTENSOR_HAS_CONSTEXPR_ENHANCED +#endif + +namespace xt +{ + /** + * @class fixed_shape + * Fixed shape implementation for compile time defined arrays. + * @sa xshape + */ + template + class fixed_shape + { + public: + + using cast_type = const_array; + + constexpr static std::size_t size() + { + return sizeof...(X); + } + + constexpr fixed_shape() + { + } + + constexpr operator cast_type() const + { + return {{X...}}; + } + }; +} + +namespace std +{ + template + class tuple_size> : + public integral_constant + { + }; +} + +namespace xtl +{ + namespace detail + { + template + struct sequence_builder> + { + using sequence_type = xt::const_array; + using value_type = typename sequence_type::value_type; + using size_type = typename sequence_type::size_type; + + inline static sequence_type make(size_type /*size*/, value_type /*v*/) + { + return sequence_type(); + } + }; + } +} + +namespace xt +{ + + /********************** + * xfixed declaration * + **********************/ + + template + class xfixed_container; + + namespace detail + { + /************************************************************************************** + The following is something we can currently only dream about -- for when we drop + support for a lot of the old compilers (e.g. GCC 4.9, MSVC 2017 ;) + + template + constexpr std::size_t calculate_stride(T& shape, std::size_t idx, layout_type L) + { + if (shape[idx] == 1) + { + return std::size_t(0); + } + + std::size_t data_size = 1; + std::size_t stride = 1; + if (L == layout_type::row_major) + { + // because we have a integer sequence that counts + // from 0 to sz - 1, we need to "invert" idx here + idx = shape.size() - idx; + for (std::size_t i = idx; i != 0; --i) + { + stride = data_size; + data_size = stride * shape[i - 1]; + } + } + else + { + for (std::size_t i = 0; i < idx + 1; ++i) + { + stride = data_size; + data_size = stride * shape[i]; + } + } + return stride; + } + + *****************************************************************************************/ + + template + struct at + { + constexpr static std::size_t arr[sizeof...(X)] = {X...}; + constexpr static std::size_t value = arr[IDX]; + }; + + template + struct calculate_stride; + + template + struct calculate_stride + { + constexpr static std::size_t value = Y * calculate_stride::value; + }; + + template + struct calculate_stride + { + constexpr static std::size_t value = 1; + }; + + template + struct calculate_stride_row_major + { + constexpr static std::size_t value = at::value * calculate_stride_row_major::value; + }; + + template + struct calculate_stride_row_major<0, X...> + { + constexpr static std::size_t value = 1; + }; + + template + struct calculate_stride + { + constexpr static std::size_t value = calculate_stride_row_major::value; + }; + + template + constexpr const_array + get_strides_impl(const xt::fixed_shape& /*shape*/, std::index_sequence) + { + static_assert((L == layout_type::row_major) || (L == layout_type::column_major), + "Layout not supported for fixed array"); + return {{at::value == 1 ? 0 : calculate_stride::value...}}; + } + + template + constexpr T get_backstrides_impl(const T& shape, const T& strides, std::index_sequence) + { + return {{(strides[I] * (shape[I] - 1))...}}; + } + + template + struct compute_size_impl; + + template + struct compute_size_impl + { + constexpr static std::size_t value = Y * compute_size_impl::value; + }; + + template + struct compute_size_impl + { + constexpr static std::size_t value = X; + }; + + template <> + struct compute_size_impl<> + { + // support for 0D xtensor fixed (empty shape = xshape<>) + constexpr static std::size_t value = 1; + }; + + // TODO unify with constexpr compute_size when dropping MSVC 2015 + template + struct fixed_compute_size; + + template + struct fixed_compute_size> + { + constexpr static std::size_t value = compute_size_impl::value; + }; + + template + struct get_init_type_impl; + + template + struct get_init_type_impl + { + using type = V[Y]; + }; + + template + struct get_init_type_impl + { + using type = V[1]; + }; + + template + struct get_init_type_impl + { + using tmp_type = typename get_init_type_impl::type; + using type = tmp_type[Y]; + }; + } + + template + constexpr const_array get_strides(const fixed_shape& shape) noexcept + { + return detail::get_strides_impl(shape, std::make_index_sequence{}); + } + + template + constexpr T get_backstrides(const T& shape, const T& strides) noexcept + { + return detail::get_backstrides_impl(shape, strides, + std::make_index_sequence::value>{}); + } + + template + struct get_init_type; + + template + struct get_init_type> + { + using type = typename detail::get_init_type_impl::type; + }; + + template + using get_init_type_t = typename get_init_type::type; + + template + struct xcontainer_inner_types> + { + using inner_shape_type = typename S::cast_type; + using inner_strides_type = inner_shape_type; + using backstrides_type = inner_shape_type; + using inner_backstrides_type = backstrides_type; + using shape_type = std::array::value>; + using strides_type = shape_type; + using storage_type = aligned_array::value>; + using temporary_type = xfixed_container; + static constexpr layout_type layout = L; + }; + + template + struct xiterable_inner_types> + : xcontainer_iterable_types> + { + }; + + /** + * @class xfixed_container + * @brief Dense multidimensional container with tensor semantic and fixed + * dimension. + * + * The xfixed_container class implements a dense multidimensional container + * with tensor semantic and fixed dimension + * + * @tparam ET The type of the elements. + * @tparam S The xshape template paramter of the container. + * @tparam L The layout_type of the tensor. + * @tparam Tag The expression tag. + * @sa xtensor_fixed + */ + template + class xfixed_container : public xcontainer>, + public xcontainer_semantic> + { + public: + + using self_type = xfixed_container; + using base_type = xcontainer; + using semantic_base = xcontainer_semantic; + + using storage_type = typename base_type::storage_type; + using value_type = typename base_type::value_type; + using reference = typename base_type::reference; + using const_reference = typename base_type::const_reference; + using pointer = typename base_type::pointer; + using const_pointer = typename base_type::const_pointer; + using shape_type = typename base_type::shape_type; + using inner_shape_type = typename base_type::inner_shape_type; + using strides_type = typename base_type::strides_type; + using backstrides_type = typename base_type::backstrides_type; + using inner_backstrides_type = typename base_type::inner_backstrides_type; + using inner_strides_type = typename base_type::inner_strides_type; + using temporary_type = typename semantic_base::temporary_type; + using expression_tag = Tag; + + constexpr static std::size_t N = std::tuple_size::value; + + xfixed_container(); +#if defined(_MSC_VER) && _MSC_VER < 1910 + explicit xfixed_container(value_type v); +#else + [[deprecated]] explicit xfixed_container(value_type v); +#endif + explicit xfixed_container(const inner_shape_type& shape, layout_type l = L); + explicit xfixed_container(const inner_shape_type& shape, value_type v, layout_type l = L); + +#ifndef X_OLD_CLANG + xfixed_container(const get_init_type_t& init); +#else + // remove this enable_if when removing the other value_type constructor + template , class EN = std::enable_if_t> + xfixed_container(nested_initializer_list_t t); +#endif + + ~xfixed_container() = default; + + xfixed_container(const xfixed_container&) = default; + xfixed_container& operator=(const xfixed_container&) = default; + + xfixed_container(xfixed_container&&) = default; + xfixed_container& operator=(xfixed_container&&) = default; + + template + xfixed_container(const xexpression& e); + + template + xfixed_container& operator=(const xexpression& e); + + template + void resize(ST&& shape, bool force = false) const; + + template + void reshape(ST&& shape, layout_type layout = L) const; + + template + bool broadcast_shape(ST& s, bool reuse_cache = false) const; + + constexpr layout_type layout() const noexcept; + + private: + + storage_type m_storage; + + XTENSOR_CONSTEXPR_ENHANCED_STATIC inner_shape_type m_shape = S(); + XTENSOR_CONSTEXPR_ENHANCED_STATIC inner_strides_type m_strides = get_strides(S()); + XTENSOR_CONSTEXPR_ENHANCED_STATIC inner_backstrides_type m_backstrides = get_backstrides(m_shape, m_strides); + + storage_type& storage_impl() noexcept; + const storage_type& storage_impl() const noexcept; + + XTENSOR_CONSTEXPR_RETURN const inner_shape_type& shape_impl() const noexcept; + XTENSOR_CONSTEXPR_RETURN const inner_strides_type& strides_impl() const noexcept; + XTENSOR_CONSTEXPR_RETURN const inner_backstrides_type& backstrides_impl() const noexcept; + + friend class xcontainer>; + }; + +#ifdef XTENSOR_HAS_CONSTEXPR_ENHANCED + // Out of line definitions to prevent linker errors prior to C++17 + template + constexpr typename xfixed_container::inner_shape_type xfixed_container::m_shape; + + template + constexpr typename xfixed_container::inner_strides_type xfixed_container::m_strides; + + template + constexpr typename xfixed_container::inner_backstrides_type xfixed_container::m_backstrides; +#endif + + /**************************************** + * xfixed_container_adaptor declaration * + ****************************************/ + + template + class xfixed_adaptor; + + template + struct xcontainer_inner_types> + { + using storage_type = std::remove_reference_t; + using inner_shape_type = typename S::cast_type; + using inner_strides_type = inner_shape_type; + using backstrides_type = inner_shape_type; + using inner_backstrides_type = backstrides_type; + using shape_type = std::array::value>; + using strides_type = shape_type; + using temporary_type = xfixed_container; + static constexpr layout_type layout = L; + }; + + template + struct xiterable_inner_types> + : xcontainer_iterable_types> + { + }; + + /** + * @class xfixed_adaptor + * @brief Dense multidimensional container adaptor with tensor semantic + * and fixed dimension. + * + * The xfixed_adaptor class implements a dense multidimensional + * container adaptor with tensor semantic and fixed dimension. It + * is used to provide a multidimensional container semantic and a + * tensor semantic to stl-like containers. + * + * @tparam EC The closure for the container type to adapt. + * @tparam S The xshape template parameter for the fixed shape of the adaptor + * @tparam L The layout_type of the adaptor. + * @tparam Tag The expression tag. + */ + template + class xfixed_adaptor : public xcontainer>, + public xcontainer_semantic> + { + public: + + using container_closure_type = EC; + + using self_type = xfixed_adaptor; + using base_type = xcontainer; + using semantic_base = xcontainer_semantic; + using storage_type = typename base_type::storage_type; + using shape_type = typename base_type::shape_type; + using strides_type = typename base_type::strides_type; + using backstrides_type = typename base_type::backstrides_type; + using inner_shape_type = typename base_type::inner_shape_type; + using inner_strides_type = typename base_type::inner_strides_type; + using inner_backstrides_type = typename base_type::inner_backstrides_type; + using temporary_type = typename semantic_base::temporary_type; + using expression_tag = Tag; + + xfixed_adaptor(storage_type&& data); + xfixed_adaptor(const storage_type& data); + + template + xfixed_adaptor(D&& data); + + ~xfixed_adaptor() = default; + + xfixed_adaptor(const xfixed_adaptor&) = default; + xfixed_adaptor& operator=(const xfixed_adaptor&); + + xfixed_adaptor(xfixed_adaptor&&) = default; + xfixed_adaptor& operator=(xfixed_adaptor&&); + xfixed_adaptor& operator=(temporary_type&&); + + template + xfixed_adaptor& operator=(const xexpression& e); + + constexpr layout_type layout() const noexcept; + + private: + + container_closure_type m_storage; + + XTENSOR_CONSTEXPR_ENHANCED_STATIC inner_shape_type m_shape = S(); + XTENSOR_CONSTEXPR_ENHANCED_STATIC inner_strides_type m_strides = get_strides(S()); + XTENSOR_CONSTEXPR_ENHANCED_STATIC inner_backstrides_type m_backstrides = get_backstrides(m_shape, m_strides); + + storage_type& storage_impl() noexcept; + const storage_type& storage_impl() const noexcept; + + XTENSOR_CONSTEXPR_RETURN const inner_shape_type& shape_impl() const noexcept; + XTENSOR_CONSTEXPR_RETURN const inner_strides_type& strides_impl() const noexcept; + XTENSOR_CONSTEXPR_RETURN const inner_backstrides_type& backstrides_impl() const noexcept; + + friend class xcontainer>; + }; + +#ifdef XTENSOR_HAS_CONSTEXPR_ENHANCED + // Out of line definitions to prevent linker errors prior to C++17 + template + constexpr typename xfixed_adaptor::inner_shape_type xfixed_adaptor::m_shape; + + template + constexpr typename xfixed_adaptor::inner_strides_type xfixed_adaptor::m_strides; + + template + constexpr typename xfixed_adaptor::inner_backstrides_type xfixed_adaptor::m_backstrides; +#endif + + /************************************ + * xfixed_container implementation * + ************************************/ + + /** + * @name Constructors + */ + //@{ + /** + * Create an uninitialized xfixed_container according to the shape template parameter. + */ + template + inline xfixed_container::xfixed_container() + { + } + + /** + * Create an xfixed_container, and initialize with the value of v. + * + * @param v the fill value + */ + template + inline xfixed_container::xfixed_container(value_type v) + { + std::fill(this->begin(), this->end(), v); + } + + /** + * Create an uninitialized xfixed_container. + * Note this function is only provided for homogenity, and the shape & layout argument is + * disregarded (the template shape is always used). + * + * @param shape the shape of the xfixed_container (unused!) + * @param l the layout_type of the xfixed_container (unused!) + */ + template + inline xfixed_container::xfixed_container(const inner_shape_type& /*shape*/, layout_type /*l*/) + { + } + + /** + * Create an xfixed_container, and initialize with the value of v. + * Note, the shape argument to this function is only provided for homogenity, + * and the shape argument is disregarded (the template shape is always used). + * + * @param shape the shape of the xfixed_container (unused!) + * @param v the fill value + * @param l the layout_type of the xfixed_container (unused!) + */ + template + inline xfixed_container::xfixed_container(const inner_shape_type& /*shape*/, value_type v, layout_type /*l*/) + : xfixed_container(v) + { + } + + /** + * Allocates an xfixed_container with shape S with values from a C array. + * The type returned by get_init_type_t is raw C array ``value_type[X][Y][Z]`` for ``xt::xshape``. + * C arrays can be initialized with the initializer list syntax, but the size is checked at compile + * time to prevent errors. + * Note: for clang < 3.8 this is an initializer_list and the size is not checked at compile-or runtime. + */ +#ifndef X_OLD_CLANG + template + inline xfixed_container::xfixed_container(const get_init_type_t& init) + { + std::copy(reinterpret_cast(&init), reinterpret_cast(&init) + this->size(), + this->template begin()); + } +#else + template + template + inline xfixed_container::xfixed_container(nested_initializer_list_t t) + { + L == layout_type::row_major ? nested_copy(m_storage.begin(), t) : nested_copy(this->template begin(), t); + } +#endif + //@} + + /** + * @name Extended copy semantic + */ + //@{ + /** + * The extended copy constructor. + */ + template + template + inline xfixed_container::xfixed_container(const xexpression& e) + { + semantic_base::assign(e); + } + + /** + * The extended assignment operator. + */ + template + template + inline auto xfixed_container::operator=(const xexpression& e) -> self_type& + { + return semantic_base::operator=(e); + } + //@} + + /** + * Note that the xfixed_container **cannot** be resized. Attempting to resize with a different + * size throws an assert in debug mode. + */ + template + template + inline void xfixed_container::resize(ST&& shape, bool) const + { + (void)(shape); // remove unused parameter warning if XTENSOR_ASSERT undefined + XTENSOR_ASSERT(std::equal(shape.begin(), shape.end(), m_shape.begin()) && shape.size() == m_shape.size()); + } + + /** + * Note that the xfixed_container **cannot** be reshaped to a shape different from ``S``. + */ + template + template + inline void xfixed_container::reshape(ST&& shape, layout_type layout) const + { + if (!(std::equal(shape.begin(), shape.end(), m_shape.begin()) && shape.size() == m_shape.size() && layout == L)) + { + throw std::runtime_error("Trying to reshape xtensor_fixed with different shape or layout."); + } + } + + template + template + inline bool xfixed_container::broadcast_shape(ST& shape, bool) const + { + return xt::broadcast_shape(m_shape, shape); + } + + template + constexpr layout_type xfixed_container::layout() const noexcept + { + return base_type::static_layout; + } + + template + inline auto xfixed_container::storage_impl() noexcept -> storage_type& + { + return m_storage; + } + + template + inline auto xfixed_container::storage_impl() const noexcept -> const storage_type& + { + return m_storage; + } + + template + XTENSOR_CONSTEXPR_RETURN auto xfixed_container::shape_impl() const noexcept -> const inner_shape_type& + { + return m_shape; + } + + template + XTENSOR_CONSTEXPR_RETURN auto xfixed_container::strides_impl() const noexcept -> const inner_strides_type& + { + return m_strides; + } + + template + XTENSOR_CONSTEXPR_RETURN auto xfixed_container::backstrides_impl() const noexcept -> const inner_backstrides_type& + { + return m_backstrides; + } + + /******************* + * xfixed_adaptor * + *******************/ + + /** + * @name Constructors + */ + //@{ + /** + * Constructs an xfixed_adaptor of the given stl-like container. + * @param data the container to adapt + */ + template + inline xfixed_adaptor::xfixed_adaptor(storage_type&& data) + : base_type(), m_storage(std::move(data)) + { + } + + /** + * Constructs an xfixed_adaptor of the given stl-like container. + * @param data the container to adapt + */ + template + inline xfixed_adaptor::xfixed_adaptor(const storage_type& data) + : base_type(), m_storage(data) + { + } + + /** + * Constructs an xfixed_adaptor of the given stl-like container, + * with the specified shape and layout_type. + * @param data the container to adapt + */ + template + template + inline xfixed_adaptor::xfixed_adaptor(D&& data) + : base_type(), m_storage(std::forward(data)) + { + } + //@} + + template + inline auto xfixed_adaptor::operator=(const xfixed_adaptor& rhs) -> self_type& + { + base_type::operator=(rhs); + m_storage = rhs.m_storage; + return *this; + } + + template + inline auto xfixed_adaptor::operator=(xfixed_adaptor&& rhs) -> self_type& + { + base_type::operator=(std::move(rhs)); + m_storage = rhs.m_storage; + return *this; + } + + template + inline auto xfixed_adaptor::operator=(temporary_type&& rhs) -> self_type& + { + m_storage = xtl::forward_sequence(std::move(rhs.storage())); + return *this; + } + + /** + * @name Extended copy semantic + */ + //@{ + /** + * The extended assignment operator. + */ + template + template + inline auto xfixed_adaptor::operator=(const xexpression& e) -> self_type& + { + return semantic_base::operator=(e); + } + //@} + + template + inline auto xfixed_adaptor::storage_impl() noexcept -> storage_type& + { + return m_storage; + } + + template + inline auto xfixed_adaptor::storage_impl() const noexcept -> const storage_type& + { + return m_storage; + } + + template + constexpr layout_type xfixed_adaptor::layout() const noexcept + { + return base_type::static_layout; + } + + template + XTENSOR_CONSTEXPR_RETURN auto xfixed_adaptor::shape_impl() const noexcept -> const inner_shape_type& + { + return m_shape; + } + + template + XTENSOR_CONSTEXPR_RETURN auto xfixed_adaptor::strides_impl() const noexcept -> const inner_strides_type& + { + return m_strides; + } + + template + XTENSOR_CONSTEXPR_RETURN auto xfixed_adaptor::backstrides_impl() const noexcept -> const inner_backstrides_type& + { + return m_backstrides; + } +} + +#undef XTENSOR_CONSTEXPR_ENHANCED +#undef XTENSOR_CONSTEXPR_RETURN +#undef XTENSOR_CONSTEXPR_ENHANCED_STATIC +#undef XTENSOR_HAS_CONSTEXPR_ENHANCED + +#endif diff --git a/vendor/xtensor/include/xtensor/xfunction.hpp b/vendor/xtensor/include/xtensor/xfunction.hpp new file mode 100644 index 0000000000..91fe86f365 --- /dev/null +++ b/vendor/xtensor/include/xtensor/xfunction.hpp @@ -0,0 +1,1184 @@ +/*************************************************************************** +* Copyright (c) 2016, Johan Mabille, Sylvain Corlay and Wolf Vollprecht * +* * +* Distributed under the terms of the BSD 3-Clause License. * +* * +* The full license is in the file LICENSE, distributed with this software. * +****************************************************************************/ + +#ifndef XTENSOR_FUNCTION_HPP +#define XTENSOR_FUNCTION_HPP + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "xexpression.hpp" +#include "xiterable.hpp" +#include "xlayout.hpp" +#include "xscalar.hpp" +#include "xstrides.hpp" +#include "xtensor_simd.hpp" +#include "xutils.hpp" + +namespace xt +{ + + namespace detail + { + + /******************** + * common_size_type * + ********************/ + + template + struct common_size_type + { + using type = std::common_type_t; + }; + + template <> + struct common_size_type<> + { + using type = std::size_t; + }; + + template + using common_size_type_t = typename common_size_type::type; + + template + using conjunction_c = xtl::conjunction...>; + + /************************** + * common_difference type * + **************************/ + + template + struct common_difference_type + { + using type = std::common_type_t; + }; + + template <> + struct common_difference_type<> + { + using type = std::ptrdiff_t; + }; + + template + using common_difference_type_t = typename common_difference_type::type; + + /********************* + * common_value_type * + *********************/ + + template + struct common_value_type + { + using type = promote_type_t...>; + }; + + template + using common_value_type_t = typename common_value_type::type; + + template > + struct simd_return_type + { + }; + + template + struct simd_return_type)>> + { + using type = xsimd::simd_type>; + }; + + template + using simd_return_type_t = typename simd_return_type::type; + + template + struct functor_return_type + { + using type = R; + using simd_type = xsimd::simd_type; + }; + + template + struct functor_return_type> + { + using type = std::complex; + using simd_type = xsimd::simd_type>; + }; + + template + struct functor_return_type + { + using type = bool; + using simd_type = xsimd::simd_bool_type; + }; + + template <> + struct functor_return_type + { + using type = bool; + using simd_type = bool; + }; + } + + template + class xfunction_iterator; + + template + class xfunction_stepper; + + template + class xfunction_base; + + template + struct xiterable_inner_types> + { + using inner_shape_type = promote_shape_t::shape_type...>; + using const_stepper = xfunction_stepper; + using stepper = const_stepper; + }; + + /****************** + * xfunction_base * + ******************/ + +#define DL XTENSOR_DEFAULT_LAYOUT + + /** + * @class xfunction_base + * @brief Base class for multidimensional function operating on + * xexpression. + * + * The xfunction_base class implements a multidimensional function + * operating on xexpression. Inheriting classes specify which + * kind of xexpression the xfunction_base operates on. + * + * @tparam F the function type + * @tparam R the return type of the function + * @tparam CT the closure types for arguments of the function + */ + template + class xfunction_base : private xconst_iterable> + { + public: + + using self_type = xfunction_base; + using only_scalar = all_xscalar; + using functor_type = typename std::remove_reference::type; + using tuple_type = std::tuple; + + using value_type = R; + using reference = value_type; + using const_reference = value_type; + using pointer = value_type*; + using const_pointer = const value_type*; + using size_type = detail::common_size_type_t...>; + using difference_type = detail::common_difference_type_t...>; + using simd_value_type = typename detail::functor_return_type...>, R>::simd_type; + using simd_argument_type = xsimd::simd_type...>>; + using iterable_base = xconst_iterable>; + using inner_shape_type = typename iterable_base::inner_shape_type; + using shape_type = inner_shape_type; + + using stepper = typename iterable_base::stepper; + using const_stepper = typename iterable_base::const_stepper; + + static constexpr layout_type static_layout = compute_layout(std::decay_t::static_layout...); + static constexpr bool contiguous_layout = detail::conjunction_c::contiguous_layout...>::value; + + template + using layout_iterator = typename iterable_base::template layout_iterator; + template + using const_layout_iterator = typename iterable_base::template const_layout_iterator; + template + using reverse_layout_iterator = typename iterable_base::template reverse_layout_iterator; + template + using const_reverse_layout_iterator = typename iterable_base::template const_reverse_layout_iterator; + + template + using broadcast_iterator = typename iterable_base::template broadcast_iterator; + template + using const_broadcast_iterator = typename iterable_base::template const_broadcast_iterator; + template + using reverse_broadcast_iterator = typename iterable_base::template reverse_broadcast_iterator; + template + using const_reverse_broadcast_iterator = typename iterable_base::template const_reverse_broadcast_iterator; + + using const_storage_iterator = xfunction_iterator; + using storage_iterator = const_storage_iterator; + using const_reverse_storage_iterator = std::reverse_iterator; + using reverse_storage_iterator = std::reverse_iterator; + + using iterator = typename iterable_base::iterator; + using const_iterator = typename iterable_base::const_iterator; + using reverse_iterator = typename iterable_base::reverse_iterator; + using const_reverse_iterator = typename iterable_base::const_reverse_iterator; + + size_type size() const noexcept; + size_type dimension() const noexcept; + const shape_type& shape() const; + layout_type layout() const noexcept; + + template + const_reference operator()(Args... args) const; + + template + const_reference at(Args... args) const; + + template + const_reference unchecked(Args... args) const; + + template + disable_integral_t operator[](const S& index) const; + template + const_reference operator[](std::initializer_list index) const; + const_reference operator[](size_type i) const; + + template + const_reference element(It first, It last) const; + + template + bool broadcast_shape(S& shape, bool reuse_cache = false) const; + + template + bool is_trivial_broadcast(const S& strides) const noexcept; + + using iterable_base::begin; + using iterable_base::end; + using iterable_base::cbegin; + using iterable_base::cend; + using iterable_base::rbegin; + using iterable_base::rend; + using iterable_base::crbegin; + using iterable_base::crend; + + template + const_storage_iterator storage_begin() const noexcept; + template + const_storage_iterator storage_end() const noexcept; + template + const_storage_iterator storage_cbegin() const noexcept; + template + const_storage_iterator storage_cend() const noexcept; + + template + const_reverse_storage_iterator storage_rbegin() const noexcept; + template + const_reverse_storage_iterator storage_rend() const noexcept; + template + const_reverse_storage_iterator storage_crbegin() const noexcept; + template + const_reverse_storage_iterator storage_crend() const noexcept; + + template + const_stepper stepper_begin(const S& shape) const noexcept; + template + const_stepper stepper_end(const S& shape, layout_type l) const noexcept; + + const_reference data_element(size_type i) const; + + template ::type> + operator value_type() const; + + template + detail::simd_return_type_t load_simd(size_type i) const; + + const tuple_type& arguments() const noexcept; + + protected: + + template , self_type>::value>> + xfunction_base(Func&& f, CTA&&... e) noexcept; + + ~xfunction_base() = default; + + xfunction_base(const xfunction_base&) = default; + xfunction_base& operator=(const xfunction_base&) = default; + + xfunction_base(xfunction_base&&) = default; + xfunction_base& operator=(xfunction_base&&) = default; + + private: + + template + layout_type layout_impl(std::index_sequence) const noexcept; + + template + const_reference access_impl(std::index_sequence, Args... args) const; + + template + const_reference unchecked_impl(std::index_sequence, Args... args) const; + + template + const_reference element_access_impl(std::index_sequence, It first, It last) const; + + template + const_reference data_element_impl(std::index_sequence, size_type i) const; + + template + auto load_simd_impl(std::index_sequence, size_type i) const; + + template + const_stepper build_stepper(Func&& f, std::index_sequence) const noexcept; + + template + const_storage_iterator build_iterator(Func&& f, std::index_sequence) const noexcept; + + size_type compute_dimension() const noexcept; + + tuple_type m_e; + functor_type m_f; + mutable shape_type m_shape; + mutable bool m_shape_trivial; + mutable bool m_shape_computed; + + friend class xfunction_iterator; + friend class xfunction_stepper; + friend class xconst_iterable; + }; + +#undef DL + + /********************** + * xfunction_iterator * + **********************/ + + template + class xscalar; + + namespace detail + { + template + struct get_iterator_impl + { + using type = typename C::storage_iterator; + }; + + template + struct get_iterator_impl + { + using type = typename C::const_storage_iterator; + }; + + template + struct get_iterator_impl> + { + using type = typename xscalar::dummy_iterator; + }; + + template + struct get_iterator_impl> + { + using type = typename xscalar::const_dummy_iterator; + }; + } + + template + using get_iterator = typename detail::get_iterator_impl::type; + + template + class xfunction_iterator : public xtl::xrandom_access_iterator_base, + typename xfunction_base::value_type, + typename xfunction_base::difference_type, + typename xfunction_base::pointer, + typename xfunction_base::reference> + { + public: + + using self_type = xfunction_iterator; + using functor_type = typename std::remove_reference::type; + using xfunction_type = xfunction_base; + + using value_type = typename xfunction_type::value_type; + using reference = typename xfunction_type::value_type; + using pointer = typename xfunction_type::const_pointer; + using difference_type = typename xfunction_type::difference_type; + using iterator_category = std::random_access_iterator_tag; + + template + xfunction_iterator(const xfunction_type* func, It&&... it) noexcept; + + self_type& operator++(); + self_type& operator--(); + + self_type& operator+=(difference_type n); + self_type& operator-=(difference_type n); + + difference_type operator-(const self_type& rhs) const; + + reference operator*() const; + + bool equal(const self_type& rhs) const; + bool less_than(const self_type& rhs) const; + + private: + + using data_type = std::tuple>...>; + + template + reference deref_impl(std::index_sequence) const; + + template + difference_type tuple_max_diff(std::index_sequence, + const data_type& lhs, + const data_type& rhs) const; + + const xfunction_type* p_f; + data_type m_it; + }; + + template + bool operator==(const xfunction_iterator& it1, + const xfunction_iterator& it2); + + template + bool operator<(const xfunction_iterator& it1, + const xfunction_iterator& it2); + + /********************* + * xfunction_stepper * + *********************/ + + template + class xfunction_stepper + { + public: + + using self_type = xfunction_stepper; + using functor_type = typename std::remove_reference::type; + using xfunction_type = xfunction_base; + + using value_type = typename xfunction_type::value_type; + using reference = typename xfunction_type::value_type; + using pointer = typename xfunction_type::const_pointer; + using size_type = typename xfunction_type::size_type; + using difference_type = typename xfunction_type::difference_type; + + using shape_type = typename xfunction_type::shape_type; + + template + xfunction_stepper(const xfunction_type* func, It&&... it) noexcept; + + void step(size_type dim); + void step_back(size_type dim); + void step(size_type dim, size_type n); + void step_back(size_type dim, size_type n); + void reset(size_type dim); + void reset_back(size_type dim); + + void to_begin(); + void to_end(layout_type l); + + reference operator*() const; + + template + ST step_simd(); + + value_type step_leading(); + + private: + + template + reference deref_impl(std::index_sequence) const; + + template + ST step_simd_impl(std::index_sequence); + + template + value_type step_leading_impl(std::index_sequence); + + const xfunction_type* p_f; + std::tuple::const_stepper...> m_it; + }; + + /************* + * xfunction * + *************/ + + /** + * @class xfunction + * @brief Multidimensional function operating on + * xtensor expressions. + * + * The xfunction class implements a multidimensional function + * operating on xtensor expressions. + * + * @tparam F the function type + * @tparam R the return type of the function + * @tparam CT the closure types for arguments of the function + */ + template + class xfunction : public xfunction_base, + public xexpression> + { + public: + + using self_type = xfunction; + using base_type = xfunction_base; + + template , self_type>::value>> + xfunction(Func&& f, CTA&&... e) noexcept; + + ~xfunction() = default; + + xfunction(const xfunction&) = default; + xfunction& operator=(const xfunction&) = default; + + xfunction(xfunction&&) = default; + xfunction& operator=(xfunction&&) = default; + }; + + /********************************* + * xfunction_base implementation * + *********************************/ + + /** + * @name Constructor + */ + //@{ + /** + * Constructs an xfunction_base applying the specified function to the given + * arguments. + * @param f the function to apply + * @param e the \ref xexpression arguments + */ + template + template + inline xfunction_base::xfunction_base(Func&& f, CTA&&... e) noexcept + : m_e(std::forward(e)...), m_f(std::forward(f)), m_shape(xtl::make_sequence(0, size_type(0))), + m_shape_computed(false) + { + } + //@} + + /** + * @name Size and shape + */ + //@{ + /** + * Returns the size of the expression. + */ + template + inline auto xfunction_base::size() const noexcept -> size_type + { + return compute_size(shape()); + } + + /** + * Returns the number of dimensions of the function. + */ + template + inline auto xfunction_base::dimension() const noexcept -> size_type + { + size_type dimension = m_shape_computed ? m_shape.size() : compute_dimension(); + return dimension; + } + + /** + * Returns the shape of the xfunction. + */ + template + inline auto xfunction_base::shape() const -> const shape_type& + { + if (!m_shape_computed) + { + m_shape = xtl::make_sequence(compute_dimension(), size_type(0)); + m_shape_trivial = broadcast_shape(m_shape, false); + m_shape_computed = true; + } + return m_shape; + } + + /** + * Returns the layout_type of the xfunction. + */ + template + inline layout_type xfunction_base::layout() const noexcept + { + return layout_impl(std::make_index_sequence()); + } + //@} + + /** + * @name Data + */ + /** + * Returns a constant reference to the element at the specified position in the function. + * @param args a list of indices specifying the position in the function. Indices + * must be unsigned integers, the number of indices should be equal or greater than + * the number of dimensions of the function. + */ + template + template + inline auto xfunction_base::operator()(Args... args) const -> const_reference + { + // The static cast prevents the compiler from instantiating the template methods with signed integers, + // leading to warning about signed/unsigned conversions in the deeper layers of the access methods + return access_impl(std::make_index_sequence(), static_cast(args)...); + } + + /** + * Returns a constant reference to the element at the specified position in the expression, + * after dimension and bounds checking. + * @param args a list of indices specifying the position in the function. Indices + * must be unsigned integers, the number of indices should be equal to the number of dimensions + * of the expression. + * @exception std::out_of_range if the number of argument is greater than the number of dimensions + * or if indices are out of bounds. + */ + template + template + inline auto xfunction_base::at(Args... args) const -> const_reference + { + check_access(shape(), static_cast(args)...); + return this->operator()(args...); + } + + /** + * Returns a constant reference to the element at the specified position in the expression. + * @param args a list of indices specifying the position in the expression. Indices + * must be unsigned integers, the number of indices must be equal to the number of + * dimensions of the expression, else the behavior is undefined. + * + * @warning This method is meant for performance, for expressions with a dynamic + * number of dimensions (i.e. not known at compile time). Since it may have + * undefined behavior (see parameters), operator() should be prefered whenever + * it is possible. + * @warning This method is NOT compatible with broadcasting, meaning the following + * code has undefined behavior: + * \code{.cpp} + * xt::xarray a = {{0, 1}, {2, 3}}; + * xt::xarray b = {0, 1}; + * auto fd = a + b; + * double res = fd.uncheked(0, 1); + * \endcode + */ + template + template + inline auto xfunction_base::unchecked(Args... args) const -> const_reference + { + // The static cast prevents the compiler from instantiating the template methods with signed integers, + // leading to warning about signed/unsigned conversions in the deeper layers of the access methods + return unchecked_impl(std::make_index_sequence(), static_cast(args)...); + } + + template + template + inline auto xfunction_base::operator[](const S& index) const + -> disable_integral_t + { + return element(index.cbegin(), index.cend()); + } + + template + template + inline auto xfunction_base::operator[](std::initializer_list index) const -> const_reference + { + return element(index.begin(), index.end()); + } + + template + inline auto xfunction_base::operator[](size_type i) const -> const_reference + { + return operator()(i); + } + + /** + * Returns a constant reference to the element at the specified position in the function. + * @param first iterator starting the sequence of indices + * @param last iterator ending the sequence of indices + * The number of indices in the sequence should be equal to or greater + * than the number of dimensions of the container. + */ + template + template + inline auto xfunction_base::element(It first, It last) const -> const_reference + { + return element_access_impl(std::make_index_sequence(), first, last); + } + //@} + + /** + * @name Broadcasting + */ + //@{ + /** + * Broadcast the shape of the function to the specified parameter. + * @param shape the result shape + * @param reuse_cache boolean for reusing a previously computed shape + * @return a boolean indicating whether the broadcasting is trivial + */ + template + template + inline bool xfunction_base::broadcast_shape(S& shape, bool reuse_cache) const + { + if (reuse_cache && m_shape_computed) + { + std::copy(m_shape.cbegin(), m_shape.cend(), shape.begin()); + return m_shape_trivial; + } + else + { + // e.broadcast_shape must be evaluated even if b is false + auto func = [&shape](bool b, auto&& e) { return e.broadcast_shape(shape) && b; }; + return accumulate(func, true, m_e); + } + } + + /** + * Compares the specified strides with those of the container to see whether + * the broadcasting is trivial. + * @return a boolean indicating whether the broadcasting is trivial + */ + template + template + inline bool xfunction_base::is_trivial_broadcast(const S& strides) const noexcept + { + auto func = [&strides](bool b, auto&& e) { return b && e.is_trivial_broadcast(strides); }; + return accumulate(func, true, m_e); + } + //@} + + template + template + inline auto xfunction_base::storage_begin() const noexcept -> const_storage_iterator + { + return storage_cbegin(); + } + + template + template + inline auto xfunction_base::storage_end() const noexcept -> const_storage_iterator + { + return storage_cend(); + } + + template + template + inline auto xfunction_base::storage_cbegin() const noexcept -> const_storage_iterator + { + auto f = [](const auto& e) noexcept { return detail::trivial_begin(e); }; + return build_iterator(f, std::make_index_sequence()); + } + + template + template + inline auto xfunction_base::storage_cend() const noexcept -> const_storage_iterator + { + auto f = [](const auto& e) noexcept { return detail::trivial_end(e); }; + return build_iterator(f, std::make_index_sequence()); + } + + template + template + inline auto xfunction_base::storage_rbegin() const noexcept -> const_reverse_storage_iterator + { + return storage_crbegin(); + } + + template + template + inline auto xfunction_base::storage_rend() const noexcept -> const_reverse_storage_iterator + { + return storage_crend(); + } + + template + template + inline auto xfunction_base::storage_crbegin() const noexcept -> const_reverse_storage_iterator + { + return const_reverse_storage_iterator(storage_cend()); + } + + template + template + inline auto xfunction_base::storage_crend() const noexcept -> const_reverse_storage_iterator + { + return const_reverse_storage_iterator(storage_cbegin()); + } + + template + template + inline auto xfunction_base::stepper_begin(const S& shape) const noexcept -> const_stepper + { + auto f = [&shape](const auto& e) noexcept { return e.stepper_begin(shape); }; + return build_stepper(f, std::make_index_sequence()); + } + + template + template + inline auto xfunction_base::stepper_end(const S& shape, layout_type l) const noexcept -> const_stepper + { + auto f = [&shape, l](const auto& e) noexcept { return e.stepper_end(shape, l); }; + return build_stepper(f, std::make_index_sequence()); + } + + template + inline auto xfunction_base::data_element(size_type i) const -> const_reference + { + return data_element_impl(std::make_index_sequence(), i); + } + + template + template + inline xfunction_base::operator value_type() const + { + return operator()(); + } + + template + template + inline auto xfunction_base::load_simd(size_type i) const -> detail::simd_return_type_t + { + return load_simd_impl(std::make_index_sequence(), i); + } + + template + inline auto xfunction_base::arguments() const noexcept -> const tuple_type& + { + return m_e; + } + + template + template + inline layout_type xfunction_base::layout_impl(std::index_sequence) const noexcept + { + return compute_layout(std::get(m_e).layout()...); + } + + template + template + inline auto xfunction_base::access_impl(std::index_sequence, Args... args) const -> const_reference + { + XTENSOR_TRY(check_index(shape(), args...)); + XTENSOR_CHECK_DIMENSION(shape(), args...); + return m_f(std::get(m_e)(args...)...); + } + + template + template + inline auto xfunction_base::unchecked_impl(std::index_sequence, Args... args) const -> const_reference + { + return m_f(std::get(m_e).unchecked(args...)...); + } + + template + template + inline auto xfunction_base::element_access_impl(std::index_sequence, It first, It last) const -> const_reference + { + XTENSOR_TRY(check_element_index(shape(), first, last)); + return m_f((std::get(m_e).element(first, last))...); + } + + template + template + inline auto xfunction_base::data_element_impl(std::index_sequence, size_type i) const -> const_reference + { + return m_f((std::get(m_e).data_element(i))...); + } + + namespace detail + { +// TODO: add traits for batch_bool in xsimd and remove this ugly hack + + template + struct is_batch_bool + { + static constexpr bool value = false; + }; + +#ifdef XTENSOR_USE_XSIMD + template + struct is_batch_bool> + { + static constexpr bool value = true; + }; +#endif + + // This metafunction avoids loading boolean values as batches of floating points and + // reciprocally. However, we cannot always load data as batches of their scalar type + // since this prevents mixed arithmetic. + template + struct get_simd_type + { + using simd_value_type = typename std::decay_t::simd_value_type; + static constexpr bool is_arg_bool = is_batch_bool::value; + static constexpr bool is_res_bool = is_batch_bool::value; + using type = std::conditional_t>; + }; + + template + using get_simd_type_t = typename get_simd_type::type; + } + + template + template + inline auto xfunction_base::load_simd_impl(std::index_sequence, size_type i) const + { + return m_f.simd_apply((std::get(m_e) + .template load_simd, simd, simd_argument_type>>(i))...); + } + + template + template + inline auto xfunction_base::build_stepper(Func&& f, std::index_sequence) const noexcept -> const_stepper + { + return const_stepper(this, f(std::get(m_e))...); + } + + template + template + inline auto xfunction_base::build_iterator(Func&& f, std::index_sequence) const noexcept -> const_storage_iterator + { + return const_storage_iterator(this, f(std::get(m_e))...); + } + + template + inline auto xfunction_base::compute_dimension() const noexcept -> size_type + { + auto func = [](size_type d, auto&& e) noexcept { return (std::max)(d, e.dimension()); }; + return accumulate(func, size_type(0), m_e); + } + + /************************************* + * xfunction_iterator implementation * + *************************************/ + + template + template + inline xfunction_iterator::xfunction_iterator(const xfunction_type* func, It&&... it) noexcept + : p_f(func), m_it(std::forward(it)...) + { + } + + template + inline auto xfunction_iterator::operator++() -> self_type& + { + auto f = [](auto& it) { ++it; }; + for_each(f, m_it); + return *this; + } + + template + inline auto xfunction_iterator::operator--() -> self_type& + { + auto f = [](auto& it) { return --it; }; + for_each(f, m_it); + return *this; + } + + template + inline auto xfunction_iterator::operator+=(difference_type n) -> self_type& + { + auto f = [n](auto& it) { it += n; }; + for_each(f, m_it); + return *this; + } + + template + inline auto xfunction_iterator::operator-=(difference_type n) -> self_type& + { + auto f = [n](auto& it) { it -= n; }; + for_each(f, m_it); + return *this; + } + + template + inline auto xfunction_iterator::operator-(const self_type& rhs) const -> difference_type + { + return tuple_max_diff(std::make_index_sequence(), m_it, rhs.m_it); + } + + template + inline auto xfunction_iterator::operator*() const -> reference + { + return deref_impl(std::make_index_sequence()); + } + + template + inline bool xfunction_iterator::equal(const self_type& rhs) const + { + return p_f == rhs.p_f && m_it == rhs.m_it; + } + + template + inline bool xfunction_iterator::less_than(const self_type& rhs) const + { + return p_f == rhs.p_f && m_it < rhs.m_it; + } + + template + template + inline auto xfunction_iterator::deref_impl(std::index_sequence) const -> reference + { + return (p_f->m_f)(*std::get(m_it)...); + } + + template + template + inline auto xfunction_iterator::tuple_max_diff(std::index_sequence, + const data_type& lhs, + const data_type& rhs) const -> difference_type + { + auto diff = std::make_tuple((std::get(lhs) - std::get(rhs))...); + auto func = [](difference_type n, auto&& v) { return (std::max)(n, v); }; + return accumulate(func, difference_type(0), diff); + } + + template + inline bool operator==(const xfunction_iterator& it1, + const xfunction_iterator& it2) + { + return it1.equal(it2); + } + + template + inline bool operator<(const xfunction_iterator& it1, + const xfunction_iterator& it2) + { + return it1.less_than(it2); + } + + /************************************ + * xfunction_stepper implementation * + ************************************/ + + template + template + inline xfunction_stepper::xfunction_stepper(const xfunction_type* func, It&&... it) noexcept + : p_f(func), m_it(std::forward(it)...) + { + } + + template + inline void xfunction_stepper::step(size_type dim) + { + auto f = [dim](auto& it) { it.step(dim); }; + for_each(f, m_it); + } + + template + inline void xfunction_stepper::step_back(size_type dim) + { + auto f = [dim](auto& it) { it.step_back(dim); }; + for_each(f, m_it); + } + + template + inline void xfunction_stepper::step(size_type dim, size_type n) + { + auto f = [dim, n](auto& it) { it.step(dim, n); }; + for_each(f, m_it); + } + + template + inline void xfunction_stepper::step_back(size_type dim, size_type n) + { + auto f = [dim, n](auto& it) { it.step_back(dim, n); }; + for_each(f, m_it); + } + + template + inline void xfunction_stepper::reset(size_type dim) + { + auto f = [dim](auto& it) { it.reset(dim); }; + for_each(f, m_it); + } + + template + inline void xfunction_stepper::reset_back(size_type dim) + { + auto f = [dim](auto& it) { it.reset_back(dim); }; + for_each(f, m_it); + } + + template + inline void xfunction_stepper::to_begin() + { + auto f = [](auto& it) { it.to_begin(); }; + for_each(f, m_it); + } + + template + inline void xfunction_stepper::to_end(layout_type l) + { + auto f = [l](auto& it) { it.to_end(l); }; + for_each(f, m_it); + } + + template + inline auto xfunction_stepper::operator*() const -> reference + { + return deref_impl(std::make_index_sequence()); + } + + template + template + inline auto xfunction_stepper::deref_impl(std::index_sequence) const -> reference + { + return (p_f->m_f)(*std::get(m_it)...); + } + + template + template + inline ST xfunction_stepper::step_simd_impl(std::index_sequence) + { + return (p_f->m_f.simd_apply)(std::get(m_it).template + step_simd, ST, typename xfunction_type::simd_argument_type>>()...); + } + + template + template + inline ST xfunction_stepper::step_simd() + { + return step_simd_impl(std::make_index_sequence()); + } + + template + template + inline auto xfunction_stepper::step_leading_impl(std::index_sequence) + -> value_type + { + return (p_f->m_f)(std::get(m_it).step_leading()...); + } + + template + inline auto xfunction_stepper::step_leading() + -> value_type + { + return step_leading_impl(std::make_index_sequence()); + } + + /**************************** + * xfunction implementation * + ****************************/ + + /** + * Constructs an xfunction applying the specified function to the given + * arguments. + * @param f the function to apply + * @param e the \ref xexpression arguments + */ + template + template + xfunction::xfunction(Func&& f, CTA&&... e) noexcept + : base_type(std::forward(f), std::forward(e)...) + { + } +} + +#endif diff --git a/vendor/xtensor/include/xtensor/xfunctor_view.hpp b/vendor/xtensor/include/xtensor/xfunctor_view.hpp new file mode 100644 index 0000000000..6cba79575a --- /dev/null +++ b/vendor/xtensor/include/xtensor/xfunctor_view.hpp @@ -0,0 +1,1349 @@ +/*************************************************************************** +* Copyright (c) 2016, Johan Mabille, Sylvain Corlay and Wolf Vollprecht * +* * +* Distributed under the terms of the BSD 3-Clause License. * +* * +* The full license is in the file LICENSE, distributed with this software. * +****************************************************************************/ + +#ifndef XTENSOR_FUNCTOR_VIEW_HPP +#define XTENSOR_FUNCTOR_VIEW_HPP + +#include +#include +#include +#include +#include + +#include + +#include "xtensor/xexpression.hpp" +#include "xtensor/xiterator.hpp" +#include "xtensor/xsemantic.hpp" +#include "xtensor/xutils.hpp" + +#include "xtensor/xarray.hpp" +#include "xtensor/xtensor.hpp" + +namespace xt +{ + + /***************************** + * xfunctor_view declaration * + *****************************/ + + template + class xfunctor_iterator; + + template + class xfunctor_stepper; + + template + class xfunctor_view; + + /******************************** + * xfunctor_view_temporary_type * + ********************************/ + + namespace detail + { + template + struct functorview_temporary_type_impl + { + using type = xarray; + }; + + template + struct functorview_temporary_type_impl, L> + { + using type = xtensor; + }; + } + + template + struct xfunctor_view_temporary_type + { + using type = typename detail::functorview_temporary_type_impl::type; + }; + + template + struct xcontainer_inner_types> + { + using xexpression_type = std::decay_t; + using temporary_type = typename xfunctor_view_temporary_type::type; + }; + +#define DL XTENSOR_DEFAULT_LAYOUT + /** + * @class xfunctor_view + * @brief View of an xexpression . + * + * The xfunctor_view class is an expression addressing its elements by applying a functor to the + * corresponding element of an underlying expression. Unlike e.g. xgenerator, an xfunctor_view is + * an lvalue. It is used e.g. to access real and imaginary parts of complex expressions. + * + * xfunctor_view is not meant to be used directly, but through helper functions such + * as \ref real or \ref imag. + * + * @tparam F the functor type to be applied to the elements of specified expression. + * @tparam CT the closure type of the \ref xexpression type underlying this view + * + * @sa real, imag + */ + template + class xfunctor_view : public xview_semantic> + { + public: + + using self_type = xfunctor_view; + using xexpression_type = std::decay_t; + using semantic_base = xview_semantic; + using functor_type = typename std::decay_t; + + using value_type = typename functor_type::value_type; + using reference = typename functor_type::reference; + using const_reference = typename functor_type::const_reference; + using pointer = typename functor_type::pointer; + using const_pointer = typename functor_type::const_pointer; + using size_type = typename xexpression_type::size_type; + using difference_type = typename xexpression_type::difference_type; + + using shape_type = typename xexpression_type::shape_type; + + static constexpr layout_type static_layout = xexpression_type::static_layout; + static constexpr bool contiguous_layout = false; + + using stepper = xfunctor_stepper; + using const_stepper = xfunctor_stepper; + + template + using layout_iterator = xfunctor_iterator>; + template + using const_layout_iterator = xfunctor_iterator>; + + template + using reverse_layout_iterator = xfunctor_iterator>; + template + using const_reverse_layout_iterator = xfunctor_iterator>; + + template + using broadcast_iterator = xfunctor_iterator>; + template + using const_broadcast_iterator = xfunctor_iterator>; + + template + using reverse_broadcast_iterator = xfunctor_iterator>; + template + using const_reverse_broadcast_iterator = xfunctor_iterator>; + + using storage_iterator = xfunctor_iterator; + using const_storage_iterator = xfunctor_iterator; + using reverse_storage_iterator = xfunctor_iterator; + using const_reverse_storage_iterator = xfunctor_iterator; + + using iterator = xfunctor_iterator; + using const_iterator = xfunctor_iterator; + using reverse_iterator = xfunctor_iterator; + using const_reverse_iterator = xfunctor_iterator; + + explicit xfunctor_view(CT) noexcept; + + template + xfunctor_view(Func&&, E&&) noexcept; + + template + self_type& operator=(const xexpression& e); + + template + disable_xexpression& operator=(const E& e); + + size_type size() const noexcept; + size_type dimension() const noexcept; + const shape_type& shape() const noexcept; + layout_type layout() const noexcept; + + template + reference operator()(Args... args); + + template + reference at(Args... args); + + template + reference unchecked(Args... args); + + template + disable_integral_t operator[](const S& index); + template + reference operator[](std::initializer_list index); + reference operator[](size_type i); + + template + reference element(IT first, IT last); + + template + const_reference operator()(Args... args) const; + + template + const_reference unchecked(Args... args) const; + + template + const_reference at(Args... args) const; + + template + disable_integral_t operator[](const S& index) const; + template + const_reference operator[](std::initializer_list index) const; + const_reference operator[](size_type i) const; + + template + const_reference element(IT first, IT last) const; + + template + bool broadcast_shape(S& shape, bool reuse_cache = false) const; + + template + bool is_trivial_broadcast(const S& strides) const; + + template + auto begin() noexcept; + template + auto end() noexcept; + + template + auto begin() const noexcept; + template + auto end() const noexcept; + template + auto cbegin() const noexcept; + template + auto cend() const noexcept; + + template + auto rbegin() noexcept; + template + auto rend() noexcept; + + template + auto rbegin() const noexcept; + template + auto rend() const noexcept; + template + auto crbegin() const noexcept; + template + auto crend() const noexcept; + + template + broadcast_iterator begin(const S& shape) noexcept; + template + broadcast_iterator end(const S& shape) noexcept; + + template + const_broadcast_iterator begin(const S& shape) const noexcept; + template + const_broadcast_iterator end(const S& shape) const noexcept; + template + const_broadcast_iterator cbegin(const S& shape) const noexcept; + template + const_broadcast_iterator cend(const S& shape) const noexcept; + + template + reverse_broadcast_iterator rbegin(const S& shape) noexcept; + template + reverse_broadcast_iterator rend(const S& shape) noexcept; + + template + const_reverse_broadcast_iterator rbegin(const S& shape) const noexcept; + template + const_reverse_broadcast_iterator rend(const S& shape) const noexcept; + template + const_reverse_broadcast_iterator crbegin(const S& shape) const noexcept; + template + const_reverse_broadcast_iterator crend(const S& shape) const noexcept; + + template + storage_iterator storage_begin() noexcept; + template + storage_iterator storage_end() noexcept; + + template + const_storage_iterator storage_begin() const noexcept; + template + const_storage_iterator storage_end() const noexcept; + template + const_storage_iterator storage_cbegin() const noexcept; + template + const_storage_iterator storage_cend() const noexcept; + + template + reverse_storage_iterator storage_rbegin() noexcept; + template + reverse_storage_iterator storage_rend() noexcept; + + template + const_reverse_storage_iterator storage_rbegin() const noexcept; + template + const_reverse_storage_iterator storage_rend() const noexcept; + template + const_reverse_storage_iterator storage_crbegin() const noexcept; + template + const_reverse_storage_iterator storage_crend() const noexcept; + + template + stepper stepper_begin(const S& shape) noexcept; + template + stepper stepper_end(const S& shape, layout_type l) noexcept; + template + const_stepper stepper_begin(const S& shape) const noexcept; + template + const_stepper stepper_end(const S& shape, layout_type l) const noexcept; + + private: + + CT m_e; + functor_type m_functor; + + using temporary_type = typename xcontainer_inner_types::temporary_type; + void assign_temporary_impl(temporary_type&& tmp); + friend class xview_semantic>; + }; + +#undef DL + + /********************************* + * xfunctor_iterator declaration * + *********************************/ + + template + class xfunctor_iterator : public xtl::xrandom_access_iterator_base, + typename F::value_type, + typename std::iterator_traits::difference_type, + typename xtl::xproxy_wrapper()(*(IT())))>::pointer, + xtl::xproxy_wrapper()(*(IT())))>> + { + public: + + using functor_type = std::decay_t; + using subiterator_traits = std::iterator_traits; + + using value_type = typename functor_type::value_type; + using reference = xtl::xproxy_wrapper()(*(IT())))>; + using pointer = typename reference::pointer; + using difference_type = typename subiterator_traits::difference_type; + using iterator_category = typename subiterator_traits::iterator_category; + + using self_type = xfunctor_iterator; + + xfunctor_iterator(const IT&, const functor_type*); + + self_type& operator++(); + self_type& operator--(); + + self_type& operator+=(difference_type n); + self_type& operator-=(difference_type n); + + difference_type operator-(xfunctor_iterator rhs) const; + + reference operator*() const; + pointer operator->() const; + + bool equal(const xfunctor_iterator& rhs) const; + bool less_than(const xfunctor_iterator& rhs) const; + + private: + + IT m_it; + const functor_type* p_functor; + }; + + template + bool operator==(const xfunctor_iterator& lhs, + const xfunctor_iterator& rhs); + + template + bool operator<(const xfunctor_iterator& lhs, + const xfunctor_iterator& rhs); + + /******************************** + * xfunctor_stepper declaration * + ********************************/ + + template + class xfunctor_stepper + { + public: + + using functor_type = std::decay_t; + + using value_type = typename functor_type::value_type; + using reference = apply_cv_t; + using pointer = std::remove_reference_t*; + using size_type = typename ST::size_type; + using difference_type = typename ST::difference_type; + + xfunctor_stepper() = default; + xfunctor_stepper(const ST&, const functor_type*); + + reference operator*() const; + + void step(size_type dim); + void step_back(size_type dim); + void step(size_type dim, size_type n); + void step_back(size_type dim, size_type n); + void reset(size_type dim); + void reset_back(size_type dim); + + void to_begin(); + void to_end(layout_type); + + private: + + ST m_stepper; + const functor_type* p_functor; + }; + + /******************************** + * xfunctor_view implementation * + ********************************/ + + /** + * @name Constructors + */ + //@{ + + /** + * Constructs an xfunctor_view expression wrappering the specified \ref xexpression. + * + * @param e the underlying expression + */ + template + inline xfunctor_view::xfunctor_view(CT e) noexcept + : m_e(e), m_functor(functor_type()) + { + } + + /** + * Constructs an xfunctor_view expression wrappering the specified \ref xexpression. + * + * @param func the functor to be applied to the elements of the underlying expression. + * @param e the underlying expression + */ + template + template + inline xfunctor_view::xfunctor_view(Func&& func, E&& e) noexcept + : m_e(std::forward(e)), m_functor(std::forward(func)) + { + } + //@} + + /** + * @name Extended copy semantic + */ + //@{ + /** + * The extended assignment operator. + */ + template + template + inline auto xfunctor_view::operator=(const xexpression& e) -> self_type& + { + bool cond = (e.derived_cast().shape().size() == dimension()) && std::equal(shape().begin(), shape().end(), e.derived_cast().shape().begin()); + if (!cond) + { + semantic_base::operator=(broadcast(e.derived_cast(), shape())); + } + else + { + semantic_base::operator=(e); + } + return *this; + } + //@} + + template + template + inline auto xfunctor_view::operator=(const E& e) -> disable_xexpression& + { + std::fill(begin(), end(), e); + return *this; + } + + template + inline void xfunctor_view::assign_temporary_impl(temporary_type&& tmp) + { + std::copy(tmp.cbegin(), tmp.cend(), begin()); + } + + /** + * @name Size and shape + */ + /** + * Returns the size of the expression. + */ + template + inline auto xfunctor_view::size() const noexcept -> size_type + { + return m_e.size(); + } + + /** + * Returns the number of dimensions of the expression. + */ + template + inline auto xfunctor_view::dimension() const noexcept -> size_type + { + return m_e.dimension(); + } + + /** + * Returns the shape of the expression. + */ + template + inline auto xfunctor_view::shape() const noexcept -> const shape_type& + { + return m_e.shape(); + } + + /** + * Returns the layout_type of the expression. + */ + template + inline layout_type xfunctor_view::layout() const noexcept + { + return m_e.layout(); + } + //@} + + /** + * @name Data + */ + /** + * Returns a reference to the element at the specified position in the expression. + * @param args a list of indices specifying the position in the function. Indices + * must be unsigned integers, the number of indices should be equal or greater than + * the number of dimensions of the expression. + */ + template + template + inline auto xfunctor_view::operator()(Args... args) -> reference + { + XTENSOR_TRY(check_index(shape(), args...)); + XTENSOR_CHECK_DIMENSION(shape(), args...); + return m_functor(m_e(args...)); + } + + /** + * Returns a reference to the element at the specified position in the expression, + * after dimension and bounds checking. + * @param args a list of indices specifying the position in the function. Indices + * must be unsigned integers, the number of indices should be equal to the number of dimensions + * of the expression. + * @exception std::out_of_range if the number of argument is greater than the number of dimensions + * or if indices are out of bounds. + */ + template + template + inline auto xfunctor_view::at(Args... args) -> reference + { + check_access(shape(), args...); + return this->operator()(args...); + } + + /** + * Returns a reference to the element at the specified position in the expression. + * @param args a list of indices specifying the position in the expression. Indices + * must be unsigned integers, the number of indices must be equal to the number of + * dimensions of the expression, else the behavior is undefined. + * + * @warning This method is meant for performance, for expressions with a dynamic + * number of dimensions (i.e. not known at compile time). Since it may have + * undefined behavior (see parameters), operator() should be prefered whenever + * it is possible. + * @warning This method is NOT compatible with broadcasting, meaning the following + * code has undefined behavior: + * \code{.cpp} + * xt::xarray a = {{0, 1}, {2, 3}}; + * xt::xarray b = {0, 1}; + * auto fd = a + b; + * double res = fd.uncheked(0, 1); + * \endcode + */ + template + template + inline auto xfunctor_view::unchecked(Args... args) -> reference + { + return m_functor(m_e.unchecked(args...)); + } + + /** + * Returns a reference to the element at the specified position in the expression. + * @param index a sequence of indices specifying the position in the function. Indices + * must be unsigned integers, the number of indices in the sequence should be equal or greater + * than the number of dimensions of the container. + */ + template + template + inline auto xfunctor_view::operator[](const S& index) + -> disable_integral_t + { + return m_functor(m_e[index]); + } + + template + template + inline auto xfunctor_view::operator[](std::initializer_list index) + -> reference + { + return m_functor(m_e[index]); + } + + template + inline auto xfunctor_view::operator[](size_type i) -> reference + { + return operator()(i); + } + + /** + * Returns a reference to the element at the specified position in the expression. + * @param first iterator starting the sequence of indices + * @param last iterator ending the sequence of indices + * The number of indices in the sequence should be equal to or greater + * than the number of dimensions of the function. + */ + template + template + inline auto xfunctor_view::element(IT first, IT last) -> reference + { + XTENSOR_TRY(check_element_index(shape(), first, last)); + return m_functor(m_e.element(first, last)); + } + + /** + * Returns a constant reference to the element at the specified position in the expression. + * @param args a list of indices specifying the position in the function. Indices + * must be unsigned integers, the number of indices should be equal or greater than + * the number of dimensions of the expression. + */ + template + template + inline auto xfunctor_view::operator()(Args... args) const -> const_reference + { + XTENSOR_TRY(check_index(shape(), args...)); + XTENSOR_CHECK_DIMENSION(shape(), args...); + return m_functor(m_e(args...)); + } + + /** + * Returns a constant reference to the element at the specified position in the expression, + * after dimension and bounds checking. + * @param args a list of indices specifying the position in the function. Indices + * must be unsigned integers, the number of indices should be equal to the number of dimensions + * of the expression. + * @exception std::out_of_range if the number of argument is greater than the number of dimensions + * or if indices are out of bounds. + */ + template + template + inline auto xfunctor_view::at(Args... args) const -> const_reference + { + check_access(shape(), args...); + return this->operator()(args...); + } + + /** + * Returns a constant reference to the element at the specified position in the expression. + * @param args a list of indices specifying the position in the expression. Indices + * must be unsigned integers, the number of indices must be equal to the number of + * dimensions of the expression, else the behavior is undefined. + * + * @warning This method is meant for performance, for expressions with a dynamic + * number of dimensions (i.e. not known at compile time). Since it may have + * undefined behavior (see parameters), operator() should be prefered whenever + * it is possible. + * @warning This method is NOT compatible with broadcasting, meaning the following + * code has undefined behavior: + * \code{.cpp} + * xt::xarray a = {{0, 1}, {2, 3}}; + * xt::xarray b = {0, 1}; + * auto fd = a + b; + * double res = fd.uncheked(0, 1); + * \endcode + */ + template + template + inline auto xfunctor_view::unchecked(Args... args) const -> const_reference + { + return m_functor(m_e.unchecked(args...)); + } + + /** + * Returns a constant reference to the element at the specified position in the expression. + * @param index a sequence of indices specifying the position in the function. Indices + * must be unsigned integers, the number of indices in the sequence should be equal or greater + * than the number of dimensions of the container. + */ + template + template + inline auto xfunctor_view::operator[](const S& index) const + -> disable_integral_t + { + return m_functor(m_e[index]); + } + + template + template + inline auto xfunctor_view::operator[](std::initializer_list index) const + -> const_reference + { + return m_functor(m_e[index]); + } + + template + inline auto xfunctor_view::operator[](size_type i) const -> const_reference + { + return operator()(i); + } + + /** + * Returns a constant reference to the element at the specified position in the expression. + * @param first iterator starting the sequence of indices + * @param last iterator ending the sequence of indices + * The number of indices in the sequence should be equal to or greater + * than the number of dimensions of the function. + */ + template + template + inline auto xfunctor_view::element(IT first, IT last) const -> const_reference + { + XTENSOR_TRY(check_element_index(shape(), first, last)); + return m_functor(m_e.element(first, last)); + } + //@} + + /** + * @name Broadcasting + */ + //@{ + /** + * Broadcast the shape of the function to the specified parameter. + * @param shape the result shape + * @param reuse_cache boolean for reusing a previously computed shape + * @return a boolean indicating whether the broadcasting is trivial + */ + template + template + inline bool xfunctor_view::broadcast_shape(S& shape, bool reuse_cache) const + { + return m_e.broadcast_shape(shape, reuse_cache); + } + + /** + * Compares the specified strides with those of the container to see whether + * the broadcasting is trivial. + * @return a boolean indicating whether the broadcasting is trivial + */ + template + template + inline bool xfunctor_view::is_trivial_broadcast(const S& strides) const + { + return m_e.is_trivial_broadcast(strides); + } + //@} + + /** + * @name Iterators + */ + //@{ + /** + * Returns an iterator to the first element of the expression. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xfunctor_view::begin() noexcept + { + return xfunctor_iterator())> + (m_e.template begin(), &m_functor); + } + + /** + * Returns an iterator to the element following the last element + * of the expression. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xfunctor_view::end() noexcept + { + return xfunctor_iterator())> + (m_e.template end(), &m_functor); + } + + /** + * Returns a constant iterator to the first element of the expression. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xfunctor_view::begin() const noexcept + { + return this->template cbegin(); + } + + /** + * Returns a constant iterator to the element following the last element + * of the expression. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xfunctor_view::end() const noexcept + { + return this->template cend(); + } + + /** + * Returns a constant iterator to the first element of the expression. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xfunctor_view::cbegin() const noexcept + { + return xfunctor_iterator())> + (m_e.template cbegin(), &m_functor); + } + + /** + * Returns a constant iterator to the element following the last element + * of the expression. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xfunctor_view::cend() const noexcept + { + return xfunctor_iterator())> + (m_e.template cend(), &m_functor); + } + //@} + + /** + * @name Broadcast iterators + */ + //@{ + /** + * Returns a constant iterator to the first element of the expression. The + * iteration is broadcasted to the specified shape. + * @param shape the shape used for broadcasting + * @tparam S type of the \c shape parameter. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xfunctor_view::begin(const S& shape) noexcept -> broadcast_iterator + { + return broadcast_iterator(m_e.template begin(shape), &m_functor); + } + + /** + * Returns a constant iterator to the element following the last element of the + * expression. The iteration is broadcasted to the specified shape. + * @param shape the shape used for broadcasting + * @tparam S type of the \c shape parameter. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xfunctor_view::end(const S& shape) noexcept -> broadcast_iterator + { + return broadcast_iterator(m_e.template end(shape), &m_functor); + } + + /** + * Returns a constant iterator to the first element of the expression. The + * iteration is broadcasted to the specified shape. + * @param shape the shape used for broadcasting + * @tparam S type of the \c shape parameter. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xfunctor_view::begin(const S& shape) const noexcept -> const_broadcast_iterator + { + return cbegin(shape); + } + + /** + * Returns a constant iterator to the element following the last element of the + * expression. The iteration is broadcasted to the specified shape. + * @param shape the shape used for broadcasting + * @tparam S type of the \c shape parameter. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xfunctor_view::end(const S& shape) const noexcept -> const_broadcast_iterator + { + return cend(shape); + } + + /** + * Returns a constant iterator to the first element of the expression. The + * iteration is broadcasted to the specified shape. + * @param shape the shape used for broadcasting + * @tparam S type of the \c shape parameter. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xfunctor_view::cbegin(const S& shape) const noexcept -> const_broadcast_iterator + { + return const_broadcast_iterator(m_e.template cbegin(shape), &m_functor); + } + + /** + * Returns a constant iterator to the element following the last element of the + * expression. The iteration is broadcasted to the specified shape. + * @param shape the shape used for broadcasting + * @tparam S type of the \c shape parameter. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xfunctor_view::cend(const S& shape) const noexcept -> const_broadcast_iterator + { + return const_broadcast_iterator(m_e.template cend(shape), &m_functor); + } + //@} + + /** + * @name Reverse iterators + */ + //@{ + /** + * Returns an iterator to the first element of the reversed expression. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xfunctor_view::rbegin() noexcept + { + return xfunctor_iterator())> + (m_e.template rbegin(), &m_functor); + } + + /** + * Returns an iterator to the element following the last element + * of the reversed expression. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xfunctor_view::rend() noexcept + { + return xfunctor_iterator())> + (m_e.template rend(), &m_functor); + } + + /** + * Returns a constant iterator to the first element of the reversed expression. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xfunctor_view::rbegin() const noexcept + { + return this->template crbegin(); + } + + /** + * Returns a constant iterator to the element following the last element + * of the reversed expression. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xfunctor_view::rend() const noexcept + { + return this->template crend(); + } + + /** + * Returns a constant iterator to the first element of the reversed expression. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xfunctor_view::crbegin() const noexcept + { + return xfunctor_iterator())> + (m_e.template rbegin(), &m_functor); + } + + /** + * Returns a constant iterator to the element following the last element + * of the reversed expression. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xfunctor_view::crend() const noexcept + { + return xfunctor_iterator())> + (m_e.template rend(), &m_functor); + } + //@} + + /** + * @name Reverse broadcast iterators + */ + /** + * Returns an iterator to the first element of the expression. The + * iteration is broadcasted to the specified shape. + * @param shape the shape used for broadcasting + * @tparam S type of the \c shape parameter. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xfunctor_view::rbegin(const S& shape) noexcept -> reverse_broadcast_iterator + { + return reverse_broadcast_iterator(m_e.template rbegin(shape), &m_functor); + } + + /** + * Returns an iterator to the element following the last element of the + * reversed expression. The iteration is broadcasted to the specified shape. + * @param shape the shape used for broadcasting + * @tparam S type of the \c shape parameter. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xfunctor_view::rend(const S& shape) noexcept -> reverse_broadcast_iterator + { + return reverse_broadcast_iterator(m_e.template rend(shape), &m_functor); + } + + /** + * Returns a constant iterator to the first element of the reversed expression. + * The iteration is broadcasted to the specified shape. + * @param shape the shape used for broadcasting + * @tparam S type of the \c shape parameter. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xfunctor_view::rbegin(const S& shape) const noexcept -> const_reverse_broadcast_iterator + { + return crbegin(shape); + } + + /** + * Returns a constant iterator to the element following the last element + * of the reversed expression. + * @param shape the shape used for broadcasting + * @tparam S type of the \c shape parameter. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xfunctor_view::rend(const S& /*shape*/) const noexcept -> const_reverse_broadcast_iterator + { + return crend(); + } + + /** + * Returns a constant iterator to the first element of the reversed expression. + * The iteration is broadcasted to the specified shape. + * @param shape the shape used for broadcasting + * @tparam S type of the \c shape parameter. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xfunctor_view::crbegin(const S& /*shape*/) const noexcept -> const_reverse_broadcast_iterator + { + return const_reverse_broadcast_iterator(m_e.template crbegin(), &m_functor); + } + + /** + * Returns a constant iterator to the element following the last element + * of the reversed expression. + * @param shape the shape used for broadcasting + * @tparam S type of the \c shape parameter. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xfunctor_view::crend(const S& shape) const noexcept -> const_reverse_broadcast_iterator + { + return const_reverse_broadcast_iterator(m_e.template crend(shape), &m_functor); + } + //@} + + template + template + inline auto xfunctor_view::storage_begin() noexcept -> storage_iterator + { + return storage_iterator(m_e.template storage_begin(), &m_functor); + } + + template + template + inline auto xfunctor_view::storage_end() noexcept -> storage_iterator + { + return storage_iterator(m_e.template storage_end(), &m_functor); + } + + template + template + inline auto xfunctor_view::storage_begin() const noexcept -> const_storage_iterator + { + return const_storage_iterator(m_e.template storage_begin(), &m_functor); + } + + template + template + inline auto xfunctor_view::storage_end() const noexcept -> const_storage_iterator + { + return const_storage_iterator(m_e.template storage_end(), &m_functor); + } + + template + template + inline auto xfunctor_view::storage_cbegin() const noexcept -> const_storage_iterator + { + return const_storage_iterator(m_e.template storage_cbegin(), &m_functor); + } + + template + template + inline auto xfunctor_view::storage_cend() const noexcept -> const_storage_iterator + { + return const_storage_iterator(m_e.template storage_cend(), &m_functor); + } + + template + template + inline auto xfunctor_view::storage_rbegin() noexcept -> reverse_storage_iterator + { + return reverse_storage_iterator(m_e.template storage_rbegin(), &m_functor); + } + + template + template + inline auto xfunctor_view::storage_rend() noexcept -> reverse_storage_iterator + { + return reverse_storage_iterator(m_e.template storage_rend(), &m_functor); + } + + template + template + inline auto xfunctor_view::storage_rbegin() const noexcept -> const_reverse_storage_iterator + { + return const_reverse_storage_iterator(m_e.template storage_rbegin(), &m_functor); + } + + template + template + inline auto xfunctor_view::storage_rend() const noexcept -> const_reverse_storage_iterator + { + return const_reverse_storage_iterator(m_e.template storage_rend(), &m_functor); + } + + template + template + inline auto xfunctor_view::storage_crbegin() const noexcept -> const_reverse_storage_iterator + { + return const_reverse_storage_iterator(m_e.template storage_crbegin(), &m_functor); + } + + template + template + inline auto xfunctor_view::storage_crend() const noexcept -> const_reverse_storage_iterator + { + return const_reverse_storage_iterator(m_e.template storage_crend(), &m_functor); + } + + /*************** + * stepper api * + ***************/ + + template + template + inline auto xfunctor_view::stepper_begin(const S& shape) noexcept -> stepper + { + return stepper(m_e.stepper_begin(shape), &m_functor); + } + + template + template + inline auto xfunctor_view::stepper_end(const S& shape, layout_type l) noexcept -> stepper + { + return stepper(m_e.stepper_end(shape, l), &m_functor); + } + + template + template + inline auto xfunctor_view::stepper_begin(const S& shape) const noexcept -> const_stepper + { + const xexpression_type& const_m_e = m_e; + return const_stepper(const_m_e.stepper_begin(shape), &m_functor); + } + + template + template + inline auto xfunctor_view::stepper_end(const S& shape, layout_type l) const noexcept -> const_stepper + { + const xexpression_type& const_m_e = m_e; + return const_stepper(const_m_e.stepper_end(shape, l), &m_functor); + } + + /************************************ + * xfunctor_iterator implementation * + ************************************/ + + template + xfunctor_iterator::xfunctor_iterator(const IT& it, const functor_type* pf) + : m_it(it), p_functor(pf) + { + } + + template + inline auto xfunctor_iterator::operator++() -> self_type& + { + ++m_it; + return *this; + } + + template + inline auto xfunctor_iterator::operator--() -> self_type& + { + --m_it; + return *this; + } + + template + inline auto xfunctor_iterator::operator+=(difference_type n) -> self_type& + { + m_it += n; + return *this; + } + + template + inline auto xfunctor_iterator::operator-=(difference_type n) -> self_type& + { + m_it -= n; + return *this; + } + + template + inline auto xfunctor_iterator::operator-(xfunctor_iterator rhs) const -> difference_type + { + return m_it - rhs.m_it; + } + + template + auto xfunctor_iterator::operator*() const -> reference + { + return xtl::proxy_wrapper((*p_functor)(*m_it)); + } + + template + auto xfunctor_iterator::operator->() const -> pointer + { + return &(operator*()); + } + + template + auto xfunctor_iterator::equal(const xfunctor_iterator& rhs) const -> bool + { + return m_it == rhs.m_it; + } + + template + auto xfunctor_iterator::less_than(const xfunctor_iterator& rhs) const -> bool + { + return m_it < rhs.m_it; + } + + template + bool operator==(const xfunctor_iterator& lhs, + const xfunctor_iterator& rhs) + { + return lhs.equal(rhs); + } + + template + bool operator<(const xfunctor_iterator& lhs, + const xfunctor_iterator& rhs) + { + return !lhs.less_than(rhs); + } + + /*********************************** + * xfunctor_stepper implementation * + ***********************************/ + + template + xfunctor_stepper::xfunctor_stepper(const ST& stepper, const functor_type* pf) + : m_stepper(stepper), p_functor(pf) + { + } + + template + auto xfunctor_stepper::operator*() const -> reference + { + return (*p_functor)(*m_stepper); + } + + template + void xfunctor_stepper::step(size_type dim) + { + m_stepper.step(dim); + } + + template + void xfunctor_stepper::step_back(size_type dim) + { + m_stepper.step_back(dim); + } + + template + void xfunctor_stepper::step(size_type dim, size_type n) + { + m_stepper.step(dim, n); + } + + template + void xfunctor_stepper::step_back(size_type dim, size_type n) + { + m_stepper.step_back(dim, n); + } + + template + void xfunctor_stepper::reset(size_type dim) + { + m_stepper.reset(dim); + } + + template + void xfunctor_stepper::reset_back(size_type dim) + { + m_stepper.reset_back(dim); + } + + template + void xfunctor_stepper::to_begin() + { + m_stepper.to_begin(); + } + + template + void xfunctor_stepper::to_end(layout_type l) + { + m_stepper.to_end(l); + } +} +#endif diff --git a/vendor/xtensor/include/xtensor/xgenerator.hpp b/vendor/xtensor/include/xtensor/xgenerator.hpp new file mode 100644 index 0000000000..f1118fe439 --- /dev/null +++ b/vendor/xtensor/include/xtensor/xgenerator.hpp @@ -0,0 +1,401 @@ +/*************************************************************************** +* Copyright (c) 2016, Johan Mabille, Sylvain Corlay and Wolf Vollprecht * +* * +* Distributed under the terms of the BSD 3-Clause License. * +* * +* The full license is in the file LICENSE, distributed with this software. * +****************************************************************************/ + +#ifndef XTENSOR_GENERATOR_HPP +#define XTENSOR_GENERATOR_HPP + +#include +#include +#include +#include +#include +#include + +#include + +#include "xexpression.hpp" +#include "xiterable.hpp" +#include "xstrides.hpp" +#include "xutils.hpp" + +namespace xt +{ + + /************** + * xgenerator * + **************/ + + template + class xgenerator; + + template + struct xiterable_inner_types> + { + using inner_shape_type = S; + using const_stepper = xindexed_stepper, true>; + using stepper = const_stepper; + }; + + /** + * @class xgenerator + * @brief Multidimensional function operating on indices. + * + * The xgenerator class implements a multidimensional function, + * generating a value from the supplied indices. + * + * @tparam F the function type + * @tparam R the return type of the function + * @tparam S the shape type of the generator + */ + template + class xgenerator : public xexpression>, + public xconst_iterable> + { + public: + + using self_type = xgenerator; + using functor_type = typename std::remove_reference::type; + + using value_type = R; + using reference = value_type; + using const_reference = value_type; + using pointer = value_type*; + using const_pointer = const value_type*; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + using iterable_base = xconst_iterable; + using inner_shape_type = typename iterable_base::inner_shape_type; + using shape_type = inner_shape_type; + using strides_type = S; + + using stepper = typename iterable_base::stepper; + using const_stepper = typename iterable_base::const_stepper; + + static constexpr layout_type static_layout = layout_type::any; + static constexpr bool contiguous_layout = false; + + template + xgenerator(Func&& f, const S& shape) noexcept; + + size_type size() const noexcept; + size_type dimension() const noexcept; + const inner_shape_type& shape() const noexcept; + layout_type layout() const noexcept; + + template + const_reference operator()(Args... args) const; + template + const_reference at(Args... args) const; + template + const_reference unchecked(Args... args) const; + template + disable_integral_t operator[](const OS& index) const; + template + const_reference operator[](std::initializer_list index) const; + const_reference operator[](size_type i) const; + + template + const_reference element(It first, It last) const; + + template + bool broadcast_shape(O& shape, bool reuse_cache = false) const; + + template + bool is_trivial_broadcast(const O& /*strides*/) const noexcept; + + template + const_stepper stepper_begin(const O& shape) const noexcept; + template + const_stepper stepper_end(const O& shape, layout_type) const noexcept; + + template ::value>> + void assign_to(xexpression& e) const noexcept; + + private: + + template + void adapt_index() const; + + template + void adapt_index(I& arg, Args&... args) const; + + functor_type m_f; + inner_shape_type m_shape; + }; + + /***************************** + * xgenerator implementation * + *****************************/ + + /** + * @name Constructor + */ + //@{ + /** + * Constructs an xgenerator applying the specified function over the + * given shape. + * @param f the function to apply + * @param shape the shape of the xgenerator + */ + template + template + inline xgenerator::xgenerator(Func&& f, const S& shape) noexcept + : m_f(std::forward(f)), m_shape(shape) + { + } + //@} + + /** + * @name Size and shape + */ + //@{ + /** + * Returns the size of the expression. + */ + template + inline auto xgenerator::size() const noexcept -> size_type + { + return compute_size(shape()); + } + + /** + * Returns the number of dimensions of the function. + */ + template + inline auto xgenerator::dimension() const noexcept -> size_type + { + return m_shape.size(); + } + + /** + * Returns the shape of the xgenerator. + */ + template + inline auto xgenerator::shape() const noexcept -> const inner_shape_type& + { + return m_shape; + } + + template + inline layout_type xgenerator::layout() const noexcept + { + return static_layout; + } + + //@} + + /** + * @name Data + */ + /** + * Returns the evaluated element at the specified position in the function. + * @param args a list of indices specifying the position in the function. Indices + * must be unsigned integers, the number of indices should be equal or greater than + * the number of dimensions of the function. + */ + template + template + inline auto xgenerator::operator()(Args... args) const -> const_reference + { + XTENSOR_TRY(check_index(shape(), args...)); + adapt_index<0>(args...); + return m_f(args...); + } + + /** + * Returns a constant reference to the element at the specified position in the expression, + * after dimension and bounds checking. + * @param args a list of indices specifying the position in the function. Indices + * must be unsigned integers, the number of indices should be equal to the number of dimensions + * of the expression. + * @exception std::out_of_range if the number of argument is greater than the number of dimensions + * or if indices are out of bounds. + */ + template + template + inline auto xgenerator::at(Args... args) const -> const_reference + { + check_access(shape(), args...); + return this->operator()(args...); + } + + /** + * Returns a constant reference to the element at the specified position in the expression. + * @param args a list of indices specifying the position in the expression. Indices + * must be unsigned integers, the number of indices must be equal to the number of + * dimensions of the expression, else the behavior is undefined. + * + * @warning This method is meant for performance, for expressions with a dynamic + * number of dimensions (i.e. not known at compile time). Since it may have + * undefined behavior (see parameters), operator() should be prefered whenever + * it is possible. + * @warning This method is NOT compatible with broadcasting, meaning the following + * code has undefined behavior: + * \code{.cpp} + * xt::xarray a = {{0, 1}, {2, 3}}; + * xt::xarray b = {0, 1}; + * auto fd = a + b; + * double res = fd.uncheked(0, 1); + * \endcode + */ + template + template + inline auto xgenerator::unchecked(Args... args) const -> const_reference + { + return m_f(args...); + } + + template + template + inline auto xgenerator::operator[](const OS& index) const + -> disable_integral_t + { + return element(index.cbegin(), index.cend()); + } + + template + template + inline auto xgenerator::operator[](std::initializer_list index) const + -> const_reference + { + return element(index.begin(), index.end()); + } + + template + inline auto xgenerator::operator[](size_type i) const -> const_reference + { + return operator()(i); + } + + /** + * Returns a constant reference to the element at the specified position in the function. + * @param first iterator starting the sequence of indices + * @param last iterator ending the sequence of indices + * The number of indices in the sequence should be equal to or greater + * than the number of dimensions of the container. + */ + template + template + inline auto xgenerator::element(It first, It last) const -> const_reference + { + using bounded_iterator = xbounded_iterator; + XTENSOR_TRY(check_element_index(shape(), first, last)); + return m_f.element(bounded_iterator(first, shape().cbegin()), bounded_iterator(last, shape().cend())); + } + //@} + + /** + * @name Broadcasting + */ + //@{ + /** + * Broadcast the shape of the function to the specified parameter. + * @param shape the result shape + * @param reuse_cache parameter for internal optimization + * @return a boolean indicating whether the broadcasting is trivial + */ + template + template + inline bool xgenerator::broadcast_shape(O& shape, bool) const + { + return xt::broadcast_shape(m_shape, shape); + } + + /** + * Compares the specified strides with those of the container to see whether + * the broadcasting is trivial. + * @return a boolean indicating whether the broadcasting is trivial + */ + template + template + inline bool xgenerator::is_trivial_broadcast(const O& /*strides*/) const noexcept + { + return false; + } + //@} + + template + template + inline auto xgenerator::stepper_begin(const O& shape) const noexcept -> const_stepper + { + size_type offset = shape.size() - dimension(); + return const_stepper(this, offset); + } + + template + template + inline auto xgenerator::stepper_end(const O& shape, layout_type) const noexcept -> const_stepper + { + size_type offset = shape.size() - dimension(); + return const_stepper(this, offset, true); + } + + template + template + inline void xgenerator::assign_to(xexpression& e) const noexcept + { + e.derived_cast().resize(m_shape); + m_f.assign_to(e); + } + + template + template + inline void xgenerator::adapt_index() const + { + } + + template + template + inline void xgenerator::adapt_index(I& arg, Args&... args) const + { + using value_type = typename decltype(m_shape)::value_type; + if (sizeof...(Args) + 1 > m_shape.size()) + { + adapt_index(args...); + } + else + { + if (static_cast(arg) >= m_shape[dim] && m_shape[dim] == 1) + { + arg = 0; + } + adapt_index(args...); + } + } + + namespace detail + { +#ifdef X_OLD_CLANG + template + inline auto make_xgenerator(Functor&& f, std::initializer_list shape) noexcept + { + using shape_type = std::vector; + using type = xgenerator; + return type(std::forward(f), xtl::forward_sequence(shape)); + } +#else + template + inline auto make_xgenerator(Functor&& f, const I (&shape)[L]) noexcept + { + using shape_type = std::array; + using type = xgenerator; + return type(std::forward(f), xtl::forward_sequence(shape)); + } +#endif + + template + inline auto make_xgenerator(Functor&& f, S&& shape) noexcept + { + using type = xgenerator>; + return type(std::forward(f), std::forward(shape)); + } + } +} + +#endif diff --git a/vendor/xtensor/include/xtensor/xindex_view.hpp b/vendor/xtensor/include/xtensor/xindex_view.hpp new file mode 100644 index 0000000000..ef46c1bbbd --- /dev/null +++ b/vendor/xtensor/include/xtensor/xindex_view.hpp @@ -0,0 +1,745 @@ +/*************************************************************************** +* Copyright (c) 2016, Johan Mabille, Sylvain Corlay and Wolf Vollprecht * +* * +* Distributed under the terms of the BSD 3-Clause License. * +* * +* The full license is in the file LICENSE, distributed with this software. * +****************************************************************************/ + +#ifndef XTENSOR_INDEX_VIEW_HPP +#define XTENSOR_INDEX_VIEW_HPP + +#include +#include +#include +#include +#include + +#include "xexpression.hpp" +#include "xiterable.hpp" +#include "xstrides.hpp" +#include "xutils.hpp" + +namespace xt +{ + + template + class xindex_view; + + template + struct xcontainer_inner_types> + { + using xexpression_type = std::decay_t; + using temporary_type = xarray; + }; + + template + struct xiterable_inner_types> + { + using inner_shape_type = std::array; + using const_stepper = xindexed_stepper, true>; + using stepper = xindexed_stepper, false>; + }; + + /*************** + * xindex_view * + ***************/ + + /** + * @class xindex_view + * @brief View of an xexpression from vector of indices. + * + * The xindex_view class implements a flat (1D) view into a multidimensional + * xexpression yielding the values at the indices of the index array. + * xindex_view is not meant to be used directly, but only with the \ref index_view + * and \ref filter helper functions. + * + * @tparam CT the closure type of the \ref xexpression type underlying this view + * @tparam I the index array type of the view + * + * @sa index_view, filter + */ + template + class xindex_view : public xview_semantic>, + public xiterable> + { + public: + + using self_type = xindex_view; + using xexpression_type = std::decay_t; + using semantic_base = xview_semantic; + + using value_type = typename xexpression_type::value_type; + using reference = typename xexpression_type::reference; + using const_reference = typename xexpression_type::const_reference; + using pointer = typename xexpression_type::pointer; + using const_pointer = typename xexpression_type::const_pointer; + using size_type = typename xexpression_type::size_type; + using difference_type = typename xexpression_type::difference_type; + + using iterable_base = xiterable; + using inner_shape_type = typename iterable_base::inner_shape_type; + using shape_type = inner_shape_type; + using strides_type = shape_type; + + using indices_type = I; + + using stepper = typename iterable_base::stepper; + using const_stepper = typename iterable_base::const_stepper; + + using temporary_type = typename xcontainer_inner_types::temporary_type; + using base_index_type = xindex_type_t; + + static constexpr layout_type static_layout = layout_type::dynamic; + static constexpr bool contiguous_layout = false; + + template + xindex_view(CTA&& e, I2&& indices) noexcept; + + template + self_type& operator=(const xexpression& e); + + template + disable_xexpression& operator=(const E& e); + + size_type size() const noexcept; + size_type dimension() const noexcept; + const inner_shape_type& shape() const noexcept; + layout_type layout() const noexcept; + + template + void fill(const T& value); + + reference operator()(size_type idx = size_type(0)); + template + reference operator()(size_type idx0, size_type idx1, Args... args); + reference unchecked(size_type idx); + template + disable_integral_t operator[](const S& index); + template + reference operator[](std::initializer_list index); + reference operator[](size_type i); + + template + reference element(It first, It last); + + const_reference operator()(size_type idx = size_type(0)) const; + template + const_reference operator()(size_type idx0, size_type idx1, Args... args) const; + const_reference unchecked(size_type idx) const; + template + disable_integral_t operator[](const S& index) const; + template + const_reference operator[](std::initializer_list index) const; + const_reference operator[](size_type i) const; + + template + const_reference element(It first, It last) const; + + template + bool broadcast_shape(O& shape, bool reuse_cache = false) const; + + template + bool is_trivial_broadcast(const O& /*strides*/) const noexcept; + + template + stepper stepper_begin(const ST& shape); + template + stepper stepper_end(const ST& shape, layout_type); + + template + const_stepper stepper_begin(const ST& shape) const; + template + const_stepper stepper_end(const ST& shape, layout_type) const; + + private: + + CT m_e; + const indices_type m_indices; + const inner_shape_type m_shape; + + void assign_temporary_impl(temporary_type&& tmp); + + friend class xview_semantic>; + }; + + /*************** + * xfiltration * + ***************/ + + /** + * @class xfiltration + * @brief Filter of a xexpression for fast scalar assign. + * + * The xfiltration class implements a lazy filtration of a multidimentional + * \ref xexpression, optimized for scalar and computed scalar assignments. + * Actually, the \ref xfiltration class IS NOT an \ref xexpression and the + * scalar and computed scalar assignments are the only method it provides. + * The filtering condition is not evaluated until the filtration is assigned. + * + * xfiltration is not meant to be used directly, but only with the \ref filtration + * helper function. + * + * @tparam ECT the closure type of the \ref xexpression type underlying this filtration + * @tparam CCR the closure type of the filtering \ref xexpression type + * + * @sa filtration + */ + template + class xfiltration + { + public: + + using self_type = xfiltration; + using xexpression_type = std::decay_t; + using const_reference = typename xexpression_type::const_reference; + + template + xfiltration(ECTA&& e, CCTA&& condition); + + template + disable_xexpression operator=(const E&); + + template + disable_xexpression operator+=(const E&); + + template + disable_xexpression operator-=(const E&); + + template + disable_xexpression operator*=(const E&); + + template + disable_xexpression operator/=(const E&); + + template + disable_xexpression operator%=(const E&); + + private: + + template + self_type& apply(F&& func); + + ECT m_e; + CCT m_condition; + }; + + /****************************** + * xindex_view implementation * + ******************************/ + + /** + * @name Constructor + */ + //@{ + /** + * Constructs an xindex_view, selecting the indices specified by \a indices. + * The resulting xexpression has a 1D shape with a length of n for n indices. + * + * @param e the underlying xexpression for this view + * @param indices the indices to select + */ + template + template + inline xindex_view::xindex_view(CTA&& e, I2&& indices) noexcept + : m_e(std::forward(e)), m_indices(std::forward(indices)), m_shape({ m_indices.size() }) + { + } + //@} + + /** + * @name Extended copy semantic + */ + //@{ + /** + * The extended assignment operator. + */ + template + template + inline auto xindex_view::operator=(const xexpression& e) -> self_type& + { + return semantic_base::operator=(e); + } + //@} + + template + template + inline auto xindex_view::operator=(const E& e) -> disable_xexpression& + { + std::fill(this->begin(), this->end(), e); + return *this; + } + + template + inline void xindex_view::assign_temporary_impl(temporary_type&& tmp) + { + std::copy(tmp.cbegin(), tmp.cend(), this->begin()); + } + + /** + * @name Size and shape + */ + //@{ + /** + * Returns the size of the xindex_view. + */ + template + inline auto xindex_view::size() const noexcept -> size_type + { + return compute_size(shape()); + } + + /** + * Returns the number of dimensions of the xindex_view. + */ + template + inline auto xindex_view::dimension() const noexcept -> size_type + { + return 1; + } + + /** + * Returns the shape of the xindex_view. + */ + template + inline auto xindex_view::shape() const noexcept -> const inner_shape_type& + { + return m_shape; + } + + template + inline layout_type xindex_view::layout() const noexcept + { + return static_layout; + } + + //@} + + /** + * @name Data + */ + //@{ + + /** + * Fills the view with the given value. + * @param value the value to fill the view with. + */ + template + template + inline void xindex_view::fill(const T& value) + { + std::fill(this->storage_begin(), this->storage_end(), value); + } + + /** + * Returns a reference to the element at the specified position in the xindex_view. + * @param idx index specifying the position in the index_view. More indices may be provided, + * only the last one will be used. + */ + template + inline auto xindex_view::operator()(size_type idx) -> reference + { + return m_e[m_indices[idx]]; + } + + template + template + inline auto xindex_view::operator()(size_type, size_type idx1, Args... args) -> reference + { + return this->operator()(idx1, static_cast(args)...); + } + + /** + * Returns a reference to the element at the specified position in the xindex_view. + * @param idx index specifying the position in the index_view. + */ + template + inline auto xindex_view::unchecked(size_type idx) -> reference + { + return this->operator()(idx); + } + + /** + * Returns a constant reference to the element at the specified position in the xindex_view. + * @param idx index specifying the position in the index_view. More indices may be provided, + * only the last one will be used. + */ + template + inline auto xindex_view::operator()(size_type idx) const -> const_reference + { + return m_e[m_indices[idx]]; + } + + template + template + inline auto xindex_view::operator()(size_type, size_type idx1, Args... args) const -> const_reference + { + return this->operator()(idx1, args...); + } + + /** + * Returns a constant reference to the element at the specified position in the xindex_view. + * @param idx index specifying the position in the index_view. + */ + template + inline auto xindex_view::unchecked(size_type idx) const -> const_reference + { + return this->operator()(idx); + } + + /** + * Returns a reference to the element at the specified position in the container. + * @param index a sequence of indices specifying the position in the container. Indices + * must be unsigned integers, the number of indices in the list should be equal or greater + * than the number of dimensions of the container. + */ + template + template + inline auto xindex_view::operator[](const S& index) + -> disable_integral_t + { + return m_e[m_indices[index[0]]]; + } + + template + template + inline auto xindex_view::operator[](std::initializer_list index) + -> reference + { + return m_e[m_indices[*(index.begin())]]; + } + + template + inline auto xindex_view::operator[](size_type i) -> reference + { + return operator()(i); + } + + /** + * Returns a constant reference to the element at the specified position in the container. + * @param index a sequence of indices specifying the position in the container. Indices + * must be unsigned integers, the number of indices in the list should be equal or greater + * than the number of dimensions of the container. + */ + template + template + inline auto xindex_view::operator[](const S& index) const + -> disable_integral_t + { + return m_e[m_indices[index[0]]]; + } + + template + template + inline auto xindex_view::operator[](std::initializer_list index) const + -> const_reference + { + return m_e[m_indices[*(index.begin())]]; + } + + template + inline auto xindex_view::operator[](size_type i) const -> const_reference + { + return operator()(i); + } + + /** + * Returns a reference to the element at the specified position in the xindex_view. + * @param first iterator starting the sequence of indices + * The number of indices in the sequence should be equal to or greater 1. + */ + template + template + inline auto xindex_view::element(It first, It /*last*/) -> reference + { + return m_e[m_indices[(*first)]]; + } + + /** + * Returns a reference to the element at the specified position in the xindex_view. + * @param first iterator starting the sequence of indices + * The number of indices in the sequence should be equal to or greater 1. + */ + template + template + inline auto xindex_view::element(It first, It /*last*/) const -> const_reference + { + return m_e[m_indices[(*first)]]; + } + //@} + + /** + * @name Broadcasting + */ + //@{ + /** + * Broadcast the shape of the xindex_view to the specified parameter. + * @param shape the result shape + * @param reuse_cache parameter for internal optimization + * @return a boolean indicating whether the broadcasting is trivial + */ + template + template + inline bool xindex_view::broadcast_shape(O& shape, bool) const + { + return xt::broadcast_shape(m_shape, shape); + } + + /** + * Compares the specified strides with those of the container to see whether + * the broadcasting is trivial. + * @return a boolean indicating whether the broadcasting is trivial + */ + template + template + inline bool xindex_view::is_trivial_broadcast(const O& /*strides*/) const noexcept + { + return false; + } + //@} + + /*************** + * stepper api * + ***************/ + + template + template + inline auto xindex_view::stepper_begin(const ST& shape) -> stepper + { + size_type offset = shape.size() - dimension(); + return stepper(this, offset); + } + + template + template + inline auto xindex_view::stepper_end(const ST& shape, layout_type) -> stepper + { + size_type offset = shape.size() - dimension(); + return stepper(this, offset, true); + } + + template + template + inline auto xindex_view::stepper_begin(const ST& shape) const -> const_stepper + { + size_type offset = shape.size() - dimension(); + return const_stepper(this, offset); + } + + template + template + inline auto xindex_view::stepper_end(const ST& shape, layout_type) const -> const_stepper + { + size_type offset = shape.size() - dimension(); + return const_stepper(this, offset, true); + } + + /****************************** + * xfiltration implementation * + ******************************/ + + /** + * @name Constructor + */ + //@{ + /** + * Constructs a xfiltration on the given expression \c e, selecting + * the elements matching the specified \c condition. + * + * @param e the \ref xexpression to filter. + * @param condition the filtering \ref xexpression to apply. + */ + template + template + inline xfiltration::xfiltration(ECTA&& e, CCTA&& condition) + : m_e(std::forward(e)), m_condition(std::forward(condition)) + { + } + //@} + + /** + * @name Extended copy semantic + */ + //@{ + /** + * Assigns the scalar \c e to \c *this. + * @param e the scalar to assign. + * @return a reference to \ *this. + */ + template + template + inline auto xfiltration::operator=(const E& e) -> disable_xexpression + { + return apply([this, &e](const_reference v, bool cond) { return cond ? e : v; }); + } + //@} + + /** + * @name Computed assignement + */ + //@{ + /** + * Adds the scalar \c e to \c *this. + * @param e the scalar to add. + * @return a reference to \c *this. + */ + template + template + inline auto xfiltration::operator+=(const E& e) -> disable_xexpression + { + return apply([this, &e](const_reference v, bool cond) { return cond ? v + e : v; }); + } + + /** + * Subtracts the scalar \c e from \c *this. + * @param e the scalar to subtract. + * @return a reference to \c *this. + */ + template + template + inline auto xfiltration::operator-=(const E& e) -> disable_xexpression + { + return apply([this, &e](const_reference v, bool cond) { return cond ? v - e : v; }); + } + + /** + * Multiplies \c *this with the scalar \c e. + * @param e the scalar involved in the operation. + * @return a reference to \c *this. + */ + template + template + inline auto xfiltration::operator*=(const E& e) -> disable_xexpression + { + return apply([this, &e](const_reference v, bool cond) { return cond ? v * e : v; }); + } + + /** + * Divides \c *this by the scalar \c e. + * @param e the scalar involved in the operation. + * @return a reference to \c *this. + */ + template + template + inline auto xfiltration::operator/=(const E& e) -> disable_xexpression + { + return apply([this, &e](const_reference v, bool cond) { return cond ? v / e : v; }); + } + + /** + * Computes the remainder of \c *this after division by the scalar \c e. + * @param e the scalar involved in the operation. + * @return a reference to \c *this. + */ + template + template + inline auto xfiltration::operator%=(const E& e) -> disable_xexpression + { + return apply([this, &e](const_reference v, bool cond) { return cond ? v % e : v; }); + } + + template + template + inline auto xfiltration::apply(F&& func) -> self_type& + { + std::transform(m_e.cbegin(), m_e.cend(), m_condition.cbegin(), m_e.begin(), func); + return *this; + } + + /** + * @brief creates an indexview from a container of indices. + * + * Returns a 1D view with the elements at \a indices selected. + * + * @param e the underlying xexpression + * @param indices the indices to select + * + * \code{.cpp} + * xarray a = {{1,5,3}, {4,5,6}}; + * b = index_view(a, {{0, 0}, {1, 0}, {1, 1}}); + * std::cout << b << std::endl; // {1, 4, 5} + * b += 100; + * std::cout << a << std::endl; // {{101, 5, 3}, {104, 105, 6}} + * \endcode + */ + template + inline auto index_view(E&& e, I&& indices) noexcept + { + using view_type = xindex_view, std::decay_t>; + return view_type(std::forward(e), std::forward(indices)); + } +#ifdef X_OLD_CLANG + template + inline auto index_view(E&& e, std::initializer_list> indices) noexcept + { + std::vector idx; + for (auto it = indices.begin(); it != indices.end(); ++it) + { + idx.emplace_back(xindex(it->begin(), it->end())); + } + using view_type = xindex_view, std::vector>; + return view_type(std::forward(e), std::move(idx)); + } +#else + template + inline auto index_view(E&& e, const xindex (&indices)[L]) noexcept + { + using view_type = xindex_view, std::array>; + return view_type(std::forward(e), to_array(indices)); + } +#endif + + /** + * @brief creates a view into \a e filtered by \a condition. + * + * Returns a 1D view with the elements selected where \a condition evaluates to \em true. + * This is equivalent to \verbatim{index_view(e, where(condition));}\endverbatim + * The returned view is not optimal if you just want to assign a scalar to the filtered + * elements. In that case, you should consider using the \ref filtration function + * instead. + * + * @param e the underlying xexpression + * @param condition xexpression with shape of \a e which selects indices + * + * \code{.cpp} + * xarray a = {{1,5,3}, {4,5,6}}; + * b = filter(a, a >= 5); + * std::cout << b << std::endl; // {5, 5, 6} + * \endcode + * + * \sa filtration + */ + template + inline auto filter(E&& e, O&& condition) noexcept + { + auto indices = where(std::forward(condition)); + using view_type = xindex_view, decltype(indices)>; + return view_type(std::forward(e), std::move(indices)); + } + + /** + * @brief creates a filtration of \c e filtered by \a condition. + * + * Returns a lazy filtration optimized for scalar assignment. + * Actually, scalar assignment and computed scalar assignments + * are the only available methods of the filtration, the filtration + * IS NOT an \ref xexpression. + * + * @param e the \ref xexpression to filter + * @param condition the filtering \ref xexpression + * + * \code{.cpp} + * xarray a = {{1,5,3}, {4,5,6}}; + * filtration(a, a >= 5) += 2; + * std::cout << a << std::endl; // {{1, 7, 3}, {4, 7, 8}} + * \endcode + */ + template + inline auto filtration(E&& e, C&& condition) noexcept + { + using filtration_type = xfiltration, xclosure_t>; + return filtration_type(std::forward(e), std::forward(condition)); + } +} + +#endif diff --git a/vendor/xtensor/include/xtensor/xinfo.hpp b/vendor/xtensor/include/xtensor/xinfo.hpp new file mode 100644 index 0000000000..50da4f6cd4 --- /dev/null +++ b/vendor/xtensor/include/xtensor/xinfo.hpp @@ -0,0 +1,139 @@ +/*************************************************************************** +* Copyright (c) 2016, Johan Mabille, Sylvain Corlay and Wolf Vollprecht * +* * +* Distributed under the terms of the BSD 3-Clause License. * +* * +* The full license is in the file LICENSE, distributed with this software. * +****************************************************************************/ + +#ifndef XTENSOR_INFO_HPP +#define XTENSOR_INFO_HPP + +#include + +#ifndef _MSC_VER +# if __cplusplus < 201103 +# define CONSTEXPR11_TN +# define CONSTEXPR14_TN +# define NOEXCEPT_TN +# elif __cplusplus < 201402 +# define CONSTEXPR11_TN constexpr +# define CONSTEXPR14_TN +# define NOEXCEPT_TN noexcept +# else +# define CONSTEXPR11_TN constexpr +# define CONSTEXPR14_TN constexpr +# define NOEXCEPT_TN noexcept +# endif +#else // _MSC_VER +# if _MSC_VER < 1900 +# define CONSTEXPR11_TN +# define CONSTEXPR14_TN +# define NOEXCEPT_TN +# elif _MSC_VER < 2000 +# define CONSTEXPR11_TN constexpr +# define CONSTEXPR14_TN +# define NOEXCEPT_TN noexcept +# else +# define CONSTEXPR11_TN constexpr +# define CONSTEXPR14_TN constexpr +# define NOEXCEPT_TN noexcept +# endif +#endif + +namespace xt +{ + // see http://stackoverflow.com/a/20170989 + struct static_string + { + template + explicit CONSTEXPR11_TN static_string(const char (&a)[N]) NOEXCEPT_TN + : data(a), size(N - 1) + { + } + + CONSTEXPR11_TN static_string(const char* a, const std::size_t sz) NOEXCEPT_TN + : data(a), size(sz) + { + } + + const char* const data; + const std::size_t size; + }; + + template + CONSTEXPR14_TN static_string type_name() + { +#ifdef __clang__ + static_string p(__PRETTY_FUNCTION__); + return static_string(p.data + 39, p.size - 39 - 1); +#elif defined(__GNUC__) + static_string p(__PRETTY_FUNCTION__); +#if __cplusplus < 201402 + return static_string(p.data + 36, p.size - 36 - 1); +#else + return static_string(p.data + 54, p.size - 54 - 1); +#endif +#elif defined(_MSC_VER) + static const static_string p(__FUNCSIG__); + return static_string(p.data + 47, p.size - 47 - 7); +#endif + } + + template + std::string type_to_string() + { + static_string static_name = type_name(); + return std::string(static_name.data, static_name.size); + } + + template + std::string info(const T& t) + { + std::string s; + s += "\nValue type: " + type_to_string(); + s += "\nLayout: "; + if (t.layout() == layout_type::row_major) + { + s += "row_major"; + } + else if (t.layout() == layout_type::column_major) + { + s += "column_major"; + } + else if (t.layout() == layout_type::dynamic) + { + s += "dynamic"; + } + else + { + s += "any"; + } + s += "\nShape: ("; + bool first = true; + for (const auto& el : t.shape()) + { + if (!first) + { + s += ", "; + } + first = false; + s += std::to_string(el); + } + s += ")\nStrides: ("; + first = true; + for (const auto& el : t.strides()) + { + if (!first) + { + s += ", "; + } + first = false; + s += std::to_string(el); + } + s += ")\nSize: " + std::to_string(t.size()) + "\n"; + return s; + } +} + +#endif diff --git a/vendor/xtensor/include/xtensor/xio.hpp b/vendor/xtensor/include/xtensor/xio.hpp new file mode 100644 index 0000000000..fa778996da --- /dev/null +++ b/vendor/xtensor/include/xtensor/xio.hpp @@ -0,0 +1,634 @@ +/*************************************************************************** +* Copyright (c) 2016, Johan Mabille, Sylvain Corlay and Wolf Vollprecht * +* * +* Distributed under the terms of the BSD 3-Clause License. * +* * +* The full license is in the file LICENSE, distributed with this software. * +****************************************************************************/ + +#ifndef XTENSOR_IO_HPP +#define XTENSOR_IO_HPP + +#include +#include +#include +#include +#include +#include +#include + +#include "xexpression.hpp" +#include "xmath.hpp" +#include "xstrided_view.hpp" + +namespace xt +{ + + template + inline std::ostream& operator<<(std::ostream& out, const xexpression& e); + + namespace print_options + { + struct print_options_impl + { + std::size_t edgeitems = 3; + std::size_t line_width = 75; + std::size_t threshold = 1000; + std::streamsize precision = -1; // default precision + }; + + inline print_options_impl& print_options() + { + static print_options_impl po; + return po; + } + + /** + * @brief Sets the line width. After \a line_width chars, + * a new line is added. + * + * @param line_width The line width + */ + inline void set_line_width(std::size_t line_width) + { + print_options().line_width = line_width; + } + + /** + * @brief Sets the threshold after which summarization is triggered (default: 1000). + * + * @param threshold The number of elements in the xexpression that triggers + * summarization in the output + */ + inline void set_threshold(std::size_t threshold) + { + print_options().threshold = threshold; + } + + /** + * @brief Sets the number of edge items. If the summarization is + * triggered, this value defines how many items of each dimension + * are printed. + * + * @param edgeitems The number of edge items + */ + inline void set_edgeitems(std::size_t edgeitems) + { + print_options().edgeitems = edgeitems; + } + + /** + * @brief Sets the precision for printing floating point values. + * + * @param precision The number of digits for floating point output + */ + inline void set_precision(std::streamsize precision) + { + print_options().precision = precision; + } + } + + /************************************** + * xexpression ostream implementation * + **************************************/ + + namespace detail + { + template + std::ostream& xoutput(std::ostream& out, const E& e, + xstrided_slice_vector& slices, F& printer, std::size_t blanks, + std::streamsize element_width, std::size_t edgeitems, std::size_t line_width) + { + using size_type = typename E::size_type; + + const auto view = xt::strided_view(e, slices); + if (view.dimension() == 0) + { + printer.print_next(out); + } + else + { + std::string indents(blanks, ' '); + + size_type i = 0; + size_type elems_on_line = 0; + size_type ewp2 = static_cast(element_width) + size_type(2); + size_type line_lim = static_cast(std::floor(line_width / ewp2)); + + out << '{'; + for (; i != size_type(view.shape()[0] - 1); ++i) + { + if (edgeitems && size_type(view.shape()[0]) > (edgeitems * 2) && i == edgeitems) + { + out << "..., "; + if (view.dimension() > 1) + { + elems_on_line = 0; + out << std::endl + << indents; + } + i = size_type(view.shape()[0]) - edgeitems; + } + if (view.dimension() == 1 && line_lim != 0 && elems_on_line >= line_lim) + { + out << std::endl + << indents; + elems_on_line = 0; + } + slices.push_back(static_cast(i)); + xoutput(out, e, slices, printer, blanks + 1, element_width, edgeitems, line_width) << ','; + slices.pop_back(); + elems_on_line++; + + if (view.dimension() == 1) + { + out << ' '; + } + else + { + out << std::endl + << indents; + } + } + if (view.dimension() == 1 && line_lim != 0 && elems_on_line >= line_lim) + { + out << std::endl + << indents; + } + slices.push_back(static_cast(i)); + xoutput(out, e, slices, printer, blanks + 1, element_width, edgeitems, line_width) << '}'; + slices.pop_back(); + } + return out; + } + + template + static void recurser_run(F& fn, const E& e, xstrided_slice_vector& slices, std::size_t lim = 0) + { + using size_type = typename E::size_type; + const auto view = strided_view(e, slices); + if (view.dimension() == 0) + { + fn.update(view()); + } + else + { + size_type i = 0; + for (; i != static_cast(view.shape()[0] - 1); ++i) + { + if (lim && size_type(view.shape()[0]) > (lim * 2) && i == lim) + { + i = static_cast(view.shape()[0]) - lim; + } + slices.push_back(static_cast(i)); + recurser_run(fn, e, slices, lim); + slices.pop_back(); + } + slices.push_back(static_cast(i)); + recurser_run(fn, e, slices, lim); + slices.pop_back(); + } + } + + template + struct printer; + + template + struct printer::value>> + { + using value_type = std::decay_t; + using cache_type = std::vector; + using cache_iterator = typename cache_type::const_iterator; + + explicit printer(std::streamsize precision) + : m_precision(precision) + { + } + + void init() + { + m_precision = m_required_precision < m_precision ? m_required_precision : m_precision; + m_it = m_cache.cbegin(); + if (m_scientific) + { + // 3 = sign, number and dot and 4 = "e+00" + m_width = m_precision + 7; + if (m_large_exponent) + { + // = e+000 (additional number) + m_width += 1; + } + } + else + { + std::streamsize decimals = 1; // print a leading 0 + if (std::floor(m_max) != 0) + { + decimals += std::streamsize(std::log10(std::floor(m_max))); + } + // 2 => sign and dot + m_width = 2 + decimals + m_precision; + } + if (!m_required_precision) + { + --m_width; + } + } + + std::ostream& print_next(std::ostream& out) + { + if (!m_scientific) + { + std::stringstream buf; + buf.width(m_width); + buf << std::fixed; + buf.precision(m_precision); + buf << (*m_it); + if (!m_required_precision) + { + buf << '.'; + } + std::string res = buf.str(); + auto sit = res.rbegin(); + while (*sit == '0') + { + *sit = ' '; + ++sit; + } + out << res; + } + else + { + if (!m_large_exponent) + { + out << std::scientific; + out.width(m_width); + out << (*m_it); + } + else + { + std::stringstream buf; + buf.width(m_width); + buf << std::scientific; + buf.precision(m_precision); + buf << (*m_it); + std::string res = buf.str(); + + if (res[res.size() - 4] == 'e') + { + res.erase(0, 1); + res.insert(res.size() - 2, "0"); + } + out << res; + } + } + ++m_it; + return out; + } + + void update(const value_type& val) + { + if (val != 0 && !std::isinf(val) && !std::isnan(val)) + { + if (!m_scientific || !m_large_exponent) + { + int exponent = 1 + int(std::log10(math::abs(val))); + if (exponent <= -5 || exponent > 7) + { + m_scientific = true; + m_required_precision = m_precision; + if (exponent <= -100 || exponent >= 100) + { + m_large_exponent = true; + } + } + } + if (math::abs(val) > m_max) + { + m_max = math::abs(val); + } + if (m_required_precision < m_precision) + { + while (std::floor(val * std::pow(10, m_required_precision)) != val * std::pow(10, m_required_precision)) + { + m_required_precision++; + } + } + } + m_cache.push_back(val); + } + + std::streamsize width() + { + return m_width; + } + + private: + + bool m_large_exponent = false; + bool m_scientific = false; + std::streamsize m_width = 9; + std::streamsize m_precision; + std::streamsize m_required_precision = 0; + value_type m_max = 0; + + cache_type m_cache; + cache_iterator m_it; + }; + + template + struct printer::value && !std::is_same::value>> + { + using value_type = std::decay_t; + using cache_type = std::vector; + using cache_iterator = typename cache_type::const_iterator; + + explicit printer(std::streamsize) + { + } + + void init() + { + m_it = m_cache.cbegin(); + m_width = 1 + std::streamsize(std::log10(m_max)) + m_sign; + } + + std::ostream& print_next(std::ostream& out) + { + // + enables printing of chars etc. as numbers + // TODO should chars be printed as numbers? + out.width(m_width); + out << +(*m_it); + ++m_it; + return out; + } + + void update(const value_type& val) + { + if (math::abs(val) > m_max) + { + m_max = math::abs(val); + } + if (std::is_signed::value && val < 0) + { + m_sign = true; + } + m_cache.push_back(val); + } + + std::streamsize width() + { + return m_width; + } + + private: + + std::streamsize m_width; + bool m_sign = false; + value_type m_max = 0; + + cache_type m_cache; + cache_iterator m_it; + }; + + template + struct printer::value>> + { + using value_type = bool; + using cache_type = std::vector; + using cache_iterator = typename cache_type::const_iterator; + + explicit printer(std::streamsize) + { + } + + void init() + { + m_it = m_cache.cbegin(); + } + + std::ostream& print_next(std::ostream& out) + { + if (*m_it) + { + out << " true"; + } + else + { + out << "false"; + } + // TODO: the following std::setw(5) isn't working correctly on OSX. + //out << std::boolalpha << std::setw(m_width) << (*m_it); + ++m_it; + return out; + } + + void update(const value_type& val) + { + m_cache.push_back(val); + } + + std::streamsize width() + { + return m_width; + } + + private: + + std::streamsize m_width = 5; + + cache_type m_cache; + cache_iterator m_it; + }; + + template + struct printer::value>> + { + using value_type = std::decay_t; + using cache_type = std::vector; + using cache_iterator = typename cache_type::const_iterator; + + explicit printer(std::streamsize precision) + : real_printer(precision), imag_printer(precision) + { + } + + void init() + { + real_printer.init(); + imag_printer.init(); + m_it = m_signs.cbegin(); + } + + std::ostream& print_next(std::ostream& out) + { + real_printer.print_next(out); + if (*m_it) + { + out << "-"; + } + else + { + out << "+"; + } + std::stringstream buf; + imag_printer.print_next(buf); + std::string s = buf.str(); + if (s[0] == ' ') + { + s.erase(0, 1); // erase space for +/- + } + // insert j at end of number + std::size_t idx = s.find_last_not_of(" "); + s.insert(idx + 1, "i"); + out << s; + ++m_it; + return out; + } + + void update(const value_type& val) + { + real_printer.update(val.real()); + imag_printer.update(std::abs(val.imag())); + m_signs.push_back(std::signbit(val.imag())); + } + + std::streamsize width() + { + return real_printer.width() + imag_printer.width() + 2; + } + + private: + + printer real_printer, imag_printer; + cache_type m_signs; + cache_iterator m_it; + }; + + template + struct printer::value && !xtl::is_complex::value>> + { + using value_type = std::decay_t; + using cache_type = std::vector; + using cache_iterator = typename cache_type::const_iterator; + + explicit printer(std::streamsize) + { + } + + void init() + { + m_it = m_cache.cbegin(); + if (m_width > 20) + { + m_width = 0; + } + } + + std::ostream& print_next(std::ostream& out) + { + out.width(m_width); + out << *m_it; + ++m_it; + return out; + } + + void update(const value_type& val) + { + std::stringstream buf; + buf << val; + std::string s = buf.str(); + if (int(s.size()) > m_width) + { + m_width = std::streamsize(s.size()); + } + m_cache.push_back(s); + } + + std::streamsize width() + { + return m_width; + } + + private: + + std::streamsize m_width = 0; + cache_type m_cache; + cache_iterator m_it; + }; + + template + struct custom_formatter + { + using value_type = std::decay_t; + + template + custom_formatter(F&& func) + : m_func(func) + { + } + + std::string operator()(const value_type& val) const + { + return m_func(val); + } + + private: + + std::function m_func; + }; + } + + template + std::ostream& pretty_print(const xexpression& e, F&& func, std::ostream& out = std::cout) + { + xfunction, std::string, const_xclosure_t> print_fun(detail::custom_formatter(std::forward(func)), e); + return pretty_print(print_fun, out); + } + + template + std::ostream& pretty_print(const xexpression& e, std::ostream& out = std::cout) + { + const E& d = e.derived_cast(); + + size_t lim = 0; + std::size_t sz = compute_size(d.shape()); + if (sz > print_options::print_options().threshold) + { + lim = print_options::print_options().edgeitems; + } + if (sz == 0) + { + out << "{}"; + return out; + } + + auto temp_precision = out.precision(); + auto precision = temp_precision; + if (print_options::print_options().precision != -1) + { + out.precision(print_options::print_options().precision); + precision = print_options::print_options().precision; + } + + detail::printer p(precision); + + xstrided_slice_vector sv; + detail::recurser_run(p, d, sv, lim); + p.init(); + sv.clear(); + xoutput(out, d, sv, p, 1, p.width(), lim, print_options::print_options().line_width); + + out.precision(temp_precision); // restore precision + + return out; + } + + template + inline std::ostream& operator<<(std::ostream& out, const xexpression& e) + { + return pretty_print(e, out); + } +} + +#endif diff --git a/vendor/xtensor/include/xtensor/xiterable.hpp b/vendor/xtensor/include/xtensor/xiterable.hpp new file mode 100644 index 0000000000..9eb4a55f28 --- /dev/null +++ b/vendor/xtensor/include/xtensor/xiterable.hpp @@ -0,0 +1,824 @@ +/*************************************************************************** +* Copyright (c) 2016, Johan Mabille, Sylvain Corlay and Wolf Vollprecht * +* * +* Distributed under the terms of the BSD 3-Clause License. * +* * +* The full license is in the file LICENSE, distributed with this software. * +****************************************************************************/ + +#ifndef XTENSOR_ITERABLE_HPP +#define XTENSOR_ITERABLE_HPP + +#include "xiterator.hpp" + +namespace xt +{ + + /******************* + * xconst_iterable * + *******************/ + + template + struct xiterable_inner_types; + +#define DL XTENSOR_DEFAULT_LAYOUT + + /** + * @class xconst_iterable + * @brief Base class for multidimensional iterable constant expressions + * + * The xconst_iterable class defines the interface for multidimensional + * constant expressions that can be iterated. + * + * @tparam D The derived type, i.e. the inheriting class for which xconst_iterable + * provides the interface. + */ + template + class xconst_iterable + { + public: + + using derived_type = D; + + using iterable_types = xiterable_inner_types; + using inner_shape_type = typename iterable_types::inner_shape_type; + + using stepper = typename iterable_types::stepper; + using const_stepper = typename iterable_types::const_stepper; + + template + using layout_iterator = xiterator; + template + using const_layout_iterator = xiterator; + template + using reverse_layout_iterator = std::reverse_iterator>; + template + using const_reverse_layout_iterator = std::reverse_iterator>; + + template + using broadcast_iterator = xiterator; + template + using const_broadcast_iterator = xiterator; + template + using reverse_broadcast_iterator = std::reverse_iterator>; + template + using const_reverse_broadcast_iterator = std::reverse_iterator>; + + using storage_iterator = layout_iterator
; + using const_storage_iterator = const_layout_iterator
; + using reverse_storage_iterator = reverse_layout_iterator
; + using const_reverse_storage_iterator = const_reverse_layout_iterator
; + + using iterator = layout_iterator
; + using const_iterator = const_layout_iterator
; + using reverse_iterator = reverse_layout_iterator
; + using const_reverse_iterator = const_reverse_layout_iterator
; + + template + const_layout_iterator begin() const noexcept; + template + const_layout_iterator end() const noexcept; + template + const_layout_iterator cbegin() const noexcept; + template + const_layout_iterator cend() const noexcept; + + template + const_reverse_layout_iterator rbegin() const noexcept; + template + const_reverse_layout_iterator rend() const noexcept; + template + const_reverse_layout_iterator crbegin() const noexcept; + template + const_reverse_layout_iterator crend() const noexcept; + + template + const_broadcast_iterator begin(const S& shape) const noexcept; + template + const_broadcast_iterator end(const S& shape) const noexcept; + template + const_broadcast_iterator cbegin(const S& shape) const noexcept; + template + const_broadcast_iterator cend(const S& shape) const noexcept; + + template + const_reverse_broadcast_iterator rbegin(const S& shape) const noexcept; + template + const_reverse_broadcast_iterator rend(const S& shape) const noexcept; + template + const_reverse_broadcast_iterator crbegin(const S& shape) const noexcept; + template + const_reverse_broadcast_iterator crend(const S& shape) const noexcept; + + template + const_layout_iterator storage_begin() const noexcept; + template + const_layout_iterator storage_end() const noexcept; + template + const_layout_iterator storage_cbegin() const noexcept; + template + const_layout_iterator storage_cend() const noexcept; + + template + const_reverse_layout_iterator storage_rbegin() const noexcept; + template + const_reverse_layout_iterator storage_rend() const noexcept; + template + const_reverse_layout_iterator storage_crbegin() const noexcept; + template + const_reverse_layout_iterator storage_crend() const noexcept; + + protected: + + const inner_shape_type& get_shape() const; + + private: + + template + const_layout_iterator get_cbegin(bool end_index) const noexcept; + template + const_layout_iterator get_cend(bool end_index) const noexcept; + + template + const_broadcast_iterator get_cbegin(const S& shape, bool end_index) const noexcept; + template + const_broadcast_iterator get_cend(const S& shape, bool end_index) const noexcept; + + template + const_stepper get_stepper_begin(const S& shape) const noexcept; + template + const_stepper get_stepper_end(const S& shape, layout_type l) const noexcept; + + const derived_type& derived_cast() const; + }; + + /************* + * xiterable * + *************/ + + /** + * @class xiterable + * @brief Base class for multidimensional iterable expressions + * + * The xiterable class defines the interface for multidimensional + * expressions that can be iterated. + * + * @tparam D The derived type, i.e. the inheriting class for which xiterable + * provides the interface. + */ + template + class xiterable : public xconst_iterable + { + public: + + using derived_type = D; + + using base_type = xconst_iterable; + using inner_shape_type = typename base_type::inner_shape_type; + + using stepper = typename base_type::stepper; + using const_stepper = typename base_type::const_stepper; + + template + using layout_iterator = typename base_type::template layout_iterator; + template + using const_layout_iterator = typename base_type::template const_layout_iterator; + template + using reverse_layout_iterator = typename base_type::template reverse_layout_iterator; + template + using const_reverse_layout_iterator = typename base_type::template const_reverse_layout_iterator; + + template + using broadcast_iterator = typename base_type::template broadcast_iterator; + template + using const_broadcast_iterator = typename base_type::template const_broadcast_iterator; + template + using reverse_broadcast_iterator = typename base_type::template reverse_broadcast_iterator; + template + using const_reverse_broadcast_iterator = typename base_type::template const_reverse_broadcast_iterator; + + using iterator = typename base_type::iterator; + using const_iterator = typename base_type::const_iterator; + using reverse_iterator = typename base_type::reverse_iterator; + using const_reverse_iterator = typename base_type::const_reverse_iterator; + + using base_type::begin; + using base_type::end; + using base_type::rbegin; + using base_type::rend; + using base_type::storage_begin; + using base_type::storage_end; + + template + layout_iterator begin() noexcept; + template + layout_iterator end() noexcept; + + template + reverse_layout_iterator rbegin() noexcept; + template + reverse_layout_iterator rend() noexcept; + + template + broadcast_iterator begin(const S& shape) noexcept; + template + broadcast_iterator end(const S& shape) noexcept; + + template + reverse_broadcast_iterator rbegin(const S& shape) noexcept; + template + reverse_broadcast_iterator rend(const S& shape) noexcept; + + template + layout_iterator storage_begin() noexcept; + template + layout_iterator storage_end() noexcept; + + template + reverse_layout_iterator storage_rbegin() noexcept; + template + reverse_layout_iterator storage_rend() noexcept; + + private: + + template + layout_iterator get_begin(bool end_index) noexcept; + template + layout_iterator get_end(bool end_index) noexcept; + + template + broadcast_iterator get_begin(const S& shape, bool end_index) noexcept; + template + broadcast_iterator get_end(const S& shape, bool end_index) noexcept; + + template + stepper get_stepper_begin(const S& shape) noexcept; + template + stepper get_stepper_end(const S& shape, layout_type l) noexcept; + + template + const_stepper get_stepper_begin(const S& shape) const noexcept; + template + const_stepper get_stepper_end(const S& shape, layout_type l) const noexcept; + + derived_type& derived_cast(); + }; + +#undef DL + + /********************************** + * xconst_iterable implementation * + **********************************/ + + /** + * @name Constant iterators + */ + //@{ + /** + * Returns a constant iterator to the first element of the expression. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xconst_iterable::begin() const noexcept -> const_layout_iterator + { + return this->template cbegin(); + } + + /** + * Returns a constant iterator to the element following the last element + * of the expression. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xconst_iterable::end() const noexcept -> const_layout_iterator + { + return this->template cend(); + } + + /** + * Returns a constant iterator to the first element of the expression. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xconst_iterable::cbegin() const noexcept -> const_layout_iterator + { + return this->template get_cbegin(false); + } + + /** + * Returns a constant iterator to the element following the last element + * of the expression. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xconst_iterable::cend() const noexcept -> const_layout_iterator + { + return this->template get_cend(true); + } + //@} + + /** + * @name Constant reverse iterators + */ + //@{ + /** + * Returns a constant iterator to the first element of the reversed expression. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xconst_iterable::rbegin() const noexcept -> const_reverse_layout_iterator + { + return this->template crbegin(); + } + + /** + * Returns a constant iterator to the element following the last element + * of the reversed expression. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xconst_iterable::rend() const noexcept -> const_reverse_layout_iterator + { + return this->template crend(); + } + + /** + * Returns a constant iterator to the first element of the reversed expression. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xconst_iterable::crbegin() const noexcept -> const_reverse_layout_iterator + { + return const_reverse_layout_iterator(get_cend(true)); + } + + /** + * Returns a constant iterator to the element following the last element + * of the reversed expression. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xconst_iterable::crend() const noexcept -> const_reverse_layout_iterator + { + return const_reverse_layout_iterator(get_cbegin(false)); + } + //@} + + /** + * @name Constant broadcast iterators + */ + //@{ + /** + * Returns a constant iterator to the first element of the expression. The + * iteration is broadcasted to the specified shape. + * @param shape the shape used for broadcasting + * @tparam S type of the \c shape parameter. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xconst_iterable::begin(const S& shape) const noexcept -> const_broadcast_iterator + { + return cbegin(shape); + } + + /** + * Returns a constant iterator to the element following the last element of the + * expression. The iteration is broadcasted to the specified shape. + * @param shape the shape used for broadcasting + * @tparam S type of the \c shape parameter. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xconst_iterable::end(const S& shape) const noexcept -> const_broadcast_iterator + { + return cend(shape); + } + + /** + * Returns a constant iterator to the first element of the expression. The + * iteration is broadcasted to the specified shape. + * @param shape the shape used for broadcasting + * @tparam S type of the \c shape parameter. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xconst_iterable::cbegin(const S& shape) const noexcept -> const_broadcast_iterator + { + return get_cbegin(shape, false); + } + + /** + * Returns a constant iterator to the element following the last element of the + * expression. The iteration is broadcasted to the specified shape. + * @param shape the shape used for broadcasting + * @tparam S type of the \c shape parameter. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xconst_iterable::cend(const S& shape) const noexcept -> const_broadcast_iterator + { + return get_cend(shape, true); + } + //@} + + /** + * @name Constant reverse broadcast iterators + */ + //@{ + /** + * Returns a constant iterator to the first element of the reversed expression. + * The iteration is broadcasted to the specified shape. + * @param shape the shape used for broadcasting + * @tparam S type of the \c shape parameter. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xconst_iterable::rbegin(const S& shape) const noexcept -> const_reverse_broadcast_iterator + { + return crbegin(shape); + } + + /** + * Returns a constant iterator to the element following the last element of the + * reversed expression. The iteration is broadcasted to the specified shape. + * @param shape the shape used for broadcasting + * @tparam S type of the \c shape parameter. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xconst_iterable::rend(const S& shape) const noexcept -> const_reverse_broadcast_iterator + { + return crend(shape); + } + + /** + * Returns a constant iterator to the first element of the reversed expression. + * The iteration is broadcasted to the specified shape. + * @param shape the shape used for broadcasting + * @tparam S type of the \c shape parameter. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xconst_iterable::crbegin(const S& shape) const noexcept -> const_reverse_broadcast_iterator + { + return const_reverse_broadcast_iterator(get_cend(shape, true)); + } + + /** + * Returns a constant iterator to the element following the last element of the + * reversed expression. The iteration is broadcasted to the specified shape. + * @param shape the shape used for broadcasting + * @tparam S type of the \c shape parameter. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xconst_iterable::crend(const S& shape) const noexcept -> const_reverse_broadcast_iterator + { + return const_reverse_broadcast_iterator(get_cbegin(shape, false)); + } + //@} + + template + template + inline auto xconst_iterable::storage_begin() const noexcept -> const_layout_iterator + { + return this->template cbegin(); + } + + template + template + inline auto xconst_iterable::storage_end() const noexcept -> const_layout_iterator + { + return this->template cend(); + } + + template + template + inline auto xconst_iterable::storage_cbegin() const noexcept -> const_layout_iterator + { + return this->template cbegin(); + } + + template + template + inline auto xconst_iterable::storage_cend() const noexcept -> const_layout_iterator + { + return this->template cend(); + } + + template + template + inline auto xconst_iterable::storage_rbegin() const noexcept -> const_reverse_layout_iterator + { + return this->template crbegin(); + } + + template + template + inline auto xconst_iterable::storage_rend() const noexcept -> const_reverse_layout_iterator + { + return this->template crend(); + } + + template + template + inline auto xconst_iterable::storage_crbegin() const noexcept -> const_reverse_layout_iterator + { + return this->template crbegin(); + } + + template + template + inline auto xconst_iterable::storage_crend() const noexcept -> const_reverse_layout_iterator + { + return this->template crend(); + } + + template + template + inline auto xconst_iterable::get_cbegin(bool end_index) const noexcept -> const_layout_iterator + { + return const_layout_iterator(get_stepper_begin(get_shape()), &get_shape(), end_index); + } + + template + template + inline auto xconst_iterable::get_cend(bool end_index) const noexcept -> const_layout_iterator + { + return const_layout_iterator(get_stepper_end(get_shape(), L), &get_shape(), end_index); + } + + template + template + inline auto xconst_iterable::get_cbegin(const S& shape, bool end_index) const noexcept -> const_broadcast_iterator + { + return const_broadcast_iterator(get_stepper_begin(shape), shape, end_index); + } + + template + template + inline auto xconst_iterable::get_cend(const S& shape, bool end_index) const noexcept -> const_broadcast_iterator + { + return const_broadcast_iterator(get_stepper_end(shape, L), shape, end_index); + } + + template + template + inline auto xconst_iterable::get_stepper_begin(const S& shape) const noexcept -> const_stepper + { + return derived_cast().stepper_begin(shape); + } + + template + template + inline auto xconst_iterable::get_stepper_end(const S& shape, layout_type l) const noexcept -> const_stepper + { + return derived_cast().stepper_end(shape, l); + } + + template + inline auto xconst_iterable::get_shape() const -> const inner_shape_type& + { + return derived_cast().shape(); + } + + template + inline auto xconst_iterable::derived_cast() const -> const derived_type& + { + return *static_cast(this); + } + + /**************************** + * xiterable implementation * + ****************************/ + + /** + * @name Iterators + */ + //@{ + /** + * Returns an iterator to the first element of the expression. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xiterable::begin() noexcept -> layout_iterator + { + return get_begin(false); + } + + /** + * Returns an iterator to the element following the last element + * of the expression. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xiterable::end() noexcept -> layout_iterator + { + return get_end(true); + } + //@} + + /** + * @name Reverse iterators + */ + //@{ + /** + * Returns an iterator to the first element of the reversed expression. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xiterable::rbegin() noexcept -> reverse_layout_iterator + { + return reverse_layout_iterator(get_end(true)); + } + + /** + * Returns an iterator to the element following the last element + * of the reversed expression. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xiterable::rend() noexcept -> reverse_layout_iterator + { + return reverse_layout_iterator(get_begin(false)); + } + //@} + + /** + * @name Broadcast iterators + */ + //@{ + /** + * Returns an iterator to the first element of the expression. The + * iteration is broadcasted to the specified shape. + * @param shape the shape used for broadcasting + * @tparam S type of the \c shape parameter. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xiterable::begin(const S& shape) noexcept -> broadcast_iterator + { + return get_begin(shape, false); + } + + /** + * Returns an iterator to the element following the last element of the + * expression. The iteration is broadcasted to the specified shape. + * @param shape the shape used for broadcasting + * @tparam S type of the \c shape parameter. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xiterable::end(const S& shape) noexcept -> broadcast_iterator + { + return get_end(shape, true); + } + //@} + + /** + * @name Reverse broadcast iterators + */ + //@{ + /** + * Returns an iterator to the first element of the reversed expression. The + * iteration is broadcasted to the specified shape. + * @param shape the shape used for broadcasting + * @tparam S type of the \c shape parameter. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xiterable::rbegin(const S& shape) noexcept -> reverse_broadcast_iterator + { + return reverse_broadcast_iterator(get_end(shape, true)); + } + + /** + * Returns an iterator to the element following the last element of the + * reversed expression. The iteration is broadcasted to the specified shape. + * @param shape the shape used for broadcasting + * @tparam S type of the \c shape parameter. + * @tparam L layout used for the traversal. Default value is \c XTENSOR_DEFAULT_LAYOUT. + */ + template + template + inline auto xiterable::rend(const S& shape) noexcept -> reverse_broadcast_iterator + { + return reverse_broadcast_iterator(get_begin(shape, false)); + } + //@} + + template + template + inline auto xiterable::storage_begin() noexcept -> layout_iterator + { + return this->template begin(); + } + + template + template + inline auto xiterable::storage_end() noexcept -> layout_iterator + { + return this->template end(); + } + + template + template + inline auto xiterable::storage_rbegin() noexcept -> reverse_layout_iterator + { + return this->template rbegin(); + } + + template + template + inline auto xiterable::storage_rend() noexcept -> reverse_layout_iterator + { + return this->template rend(); + } + + template + template + inline auto xiterable::get_begin(bool end_index) noexcept -> layout_iterator + { + return layout_iterator(get_stepper_begin(this->get_shape()), &(this->get_shape()), end_index); + } + + template + template + inline auto xiterable::get_end(bool end_index) noexcept -> layout_iterator + { + return layout_iterator(get_stepper_end(this->get_shape(), L), &(this->get_shape()), end_index); + } + + template + template + inline auto xiterable::get_begin(const S& shape, bool end_index) noexcept -> broadcast_iterator + { + return broadcast_iterator(get_stepper_begin(shape), shape, end_index); + } + + template + template + inline auto xiterable::get_end(const S& shape, bool end_index) noexcept -> broadcast_iterator + { + return broadcast_iterator(get_stepper_end(shape, L), shape, end_index); + } + + template + template + inline auto xiterable::get_stepper_begin(const S& shape) noexcept -> stepper + { + return derived_cast().stepper_begin(shape); + } + + template + template + inline auto xiterable::get_stepper_end(const S& shape, layout_type l) noexcept -> stepper + { + return derived_cast().stepper_end(shape, l); + } + + template + template + inline auto xiterable::get_stepper_begin(const S& shape) const noexcept -> const_stepper + { + return derived_cast().stepper_begin(shape); + } + + template + template + inline auto xiterable::get_stepper_end(const S& shape, layout_type l) const noexcept -> const_stepper + { + return derived_cast().stepper_end(shape, l); + } + + template + inline auto xiterable::derived_cast() -> derived_type& + { + return *static_cast(this); + } +} + +#endif diff --git a/vendor/xtensor/include/xtensor/xiterator.hpp b/vendor/xtensor/include/xtensor/xiterator.hpp new file mode 100644 index 0000000000..01b7950515 --- /dev/null +++ b/vendor/xtensor/include/xtensor/xiterator.hpp @@ -0,0 +1,1123 @@ +/*************************************************************************** +* Copyright (c) 2016, Johan Mabille, Sylvain Corlay and Wolf Vollprecht * +* * +* Distributed under the terms of the BSD 3-Clause License. * +* * +* The full license is in the file LICENSE, distributed with this software. * +****************************************************************************/ + +#ifndef XTENSOR_ITERATOR_HPP +#define XTENSOR_ITERATOR_HPP + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "xexception.hpp" +#include "xlayout.hpp" +#include "xshape.hpp" +#include "xutils.hpp" + +namespace xt +{ + + /*********************** + * iterator meta utils * + ***********************/ + + template + class xscalar; + + namespace detail + { + template + struct get_stepper_iterator_impl + { + using type = typename C::container_iterator; + }; + + template + struct get_stepper_iterator_impl + { + using type = typename C::const_container_iterator; + }; + + template + struct get_stepper_iterator_impl> + { + using type = typename xscalar::dummy_iterator; + }; + + template + struct get_stepper_iterator_impl> + { + using type = typename xscalar::const_dummy_iterator; + }; + } + + template + using get_stepper_iterator = typename detail::get_stepper_iterator_impl::type; + + namespace detail + { + template + struct index_type_impl + { + using type = dynamic_shape; + }; + + template + struct index_type_impl> + { + using type = std::array; + }; + } + + template + using xindex_type_t = typename detail::index_type_impl::type; + + /************ + * xstepper * + ************/ + + template + class xstepper + { + public: + + using storage_type = C; + using subiterator_type = get_stepper_iterator; + using subiterator_traits = std::iterator_traits; + using value_type = typename subiterator_traits::value_type; + using reference = typename subiterator_traits::reference; + using pointer = typename subiterator_traits::pointer; + using difference_type = typename subiterator_traits::difference_type; + using size_type = typename storage_type::size_type; + using shape_type = typename storage_type::shape_type; + using simd_type = xsimd::simd_type; + + xstepper() = default; + xstepper(storage_type* c, subiterator_type it, size_type offset) noexcept; + + reference operator*() const; + + void step(size_type dim, size_type n = 1); + void step_back(size_type dim, size_type n = 1); + void reset(size_type dim); + void reset_back(size_type dim); + + void to_begin(); + void to_end(layout_type l); + + template + R step_simd(); + + value_type step_leading(); + + template + void store_simd(const R& vec); + + private: + + storage_type* p_c; + subiterator_type m_it; + size_type m_offset; + }; + + template + struct stepper_tools + { + // For performance reasons, increment_stepper and decrement_stepper are + // specialized for the case where n=1, which underlies operator++ and + // operator-- on xiterators. + + template + static void increment_stepper(S& stepper, + IT& index, + const ST& shape); + + template + static void decrement_stepper(S& stepper, + IT& index, + const ST& shape); + + template + static void increment_stepper(S& stepper, + IT& index, + const ST& shape, + typename S::size_type n); + + template + static void decrement_stepper(S& stepper, + IT& index, + const ST& shape, + typename S::size_type n); + }; + + /******************** + * xindexed_stepper * + ********************/ + + template + class xindexed_stepper + { + public: + + using self_type = xindexed_stepper; + using xexpression_type = std::conditional_t; + + using value_type = typename xexpression_type::value_type; + using reference = std::conditional_t; + using pointer = std::conditional_t; + using size_type = typename xexpression_type::size_type; + using difference_type = typename xexpression_type::difference_type; + + using shape_type = typename xexpression_type::shape_type; + using index_type = xindex_type_t; + + xindexed_stepper() = default; + xindexed_stepper(xexpression_type* e, size_type offset, bool end = false) noexcept; + + reference operator*() const; + + void step(size_type dim, size_type n = 1); + void step_back(size_type dim, size_type n = 1); + void reset(size_type dim); + void reset_back(size_type dim); + + void to_begin(); + void to_end(layout_type l); + + private: + + xexpression_type* p_e; + index_type m_index; + size_type m_offset; + }; + + template + struct is_indexed_stepper + { + static const bool value = false; + }; + + template + struct is_indexed_stepper> + { + static const bool value = true; + }; + + /************* + * xiterator * + *************/ + + namespace detail + { + template + class shape_storage + { + public: + + using shape_type = S; + using param_type = const S&; + + shape_storage() = default; + shape_storage(param_type shape); + const S& shape() const; + + private: + + S m_shape; + }; + + template + class shape_storage + { + public: + + using shape_type = S; + using param_type = const S*; + + shape_storage(param_type shape = 0); + const S& shape() const; + + private: + + const S* p_shape; + }; + + template + struct LAYOUT_FORBIDEN_FOR_XITERATOR; + } + + template + class xiterator : public xtl::xrandom_access_iterator_base, + typename It::value_type, + typename It::difference_type, + typename It::pointer, + typename It::reference>, + private detail::shape_storage + { + public: + + using self_type = xiterator; + + using subiterator_type = It; + using value_type = typename subiterator_type::value_type; + using reference = typename subiterator_type::reference; + using pointer = typename subiterator_type::pointer; + using difference_type = typename subiterator_type::difference_type; + using size_type = typename subiterator_type::size_type; + using iterator_category = std::random_access_iterator_tag; + + using private_base = detail::shape_storage; + using shape_type = typename private_base::shape_type; + using shape_param_type = typename private_base::param_type; + using index_type = xindex_type_t; + + xiterator() = default; + // end_index means either reverse_iterator && !end or !reverse_iterator && end + xiterator(It it, shape_param_type shape, bool end_index); + + self_type& operator++(); + self_type& operator--(); + + self_type& operator+=(difference_type n); + self_type& operator-=(difference_type n); + + difference_type operator-(const self_type& rhs) const; + + reference operator*() const; + pointer operator->() const; + + bool equal(const xiterator& rhs) const; + bool less_than(const xiterator& rhs) const; + + private: + + subiterator_type m_it; + index_type m_index; + difference_type m_linear_index; + + using checking_type = typename detail::LAYOUT_FORBIDEN_FOR_XITERATOR::type; + }; + + template + bool operator==(const xiterator& lhs, + const xiterator& rhs); + + template + bool operator<(const xiterator& lhs, + const xiterator& rhs); + + /********************* + * xbounded_iterator * + *********************/ + + template + class xbounded_iterator : public xtl::xrandom_access_iterator_base, + typename std::iterator_traits::value_type, + typename std::iterator_traits::difference_type, + typename std::iterator_traits::pointer, + typename std::iterator_traits::reference> + { + public: + + using self_type = xbounded_iterator; + + using subiterator_type = It; + using bound_iterator_type = BIt; + using value_type = typename std::iterator_traits::value_type; + using reference = typename std::iterator_traits::reference; + using pointer = typename std::iterator_traits::pointer; + using difference_type = typename std::iterator_traits::difference_type; + using iterator_category = std::random_access_iterator_tag; + + xbounded_iterator() = default; + xbounded_iterator(It it, BIt bound_it); + + self_type& operator++(); + self_type& operator--(); + + self_type& operator+=(difference_type n); + self_type& operator-=(difference_type n); + + difference_type operator-(const self_type& rhs) const; + + value_type operator*() const; + + bool equal(const self_type& rhs) const; + bool less_than(const self_type& rhs) const; + + private: + + subiterator_type m_it; + bound_iterator_type m_bound_it; + }; + + template + bool operator==(const xbounded_iterator& lhs, + const xbounded_iterator& rhs); + + template + bool operator<(const xbounded_iterator& lhs, + const xbounded_iterator& rhs); + + /******************************* + * trivial_begin / trivial_end * + *******************************/ + + namespace detail + { + template + constexpr auto trivial_begin(C& c) noexcept + { + return c.storage_begin(); + } + + template + constexpr auto trivial_end(C& c) noexcept + { + return c.storage_end(); + } + + template + constexpr auto trivial_begin(const C& c) noexcept + { + return c.storage_begin(); + } + + template + constexpr auto trivial_end(const C& c) noexcept + { + return c.storage_end(); + } + } + + /*************************** + * xstepper implementation * + ***************************/ + + template + inline xstepper::xstepper(storage_type* c, subiterator_type it, size_type offset) noexcept + : p_c(c), m_it(it), m_offset(offset) + { + } + + template + inline auto xstepper::operator*() const -> reference + { + return *m_it; + } + + template + inline void xstepper::step(size_type dim, size_type n) + { + if (dim >= m_offset) + { + using strides_value_type = decltype(p_c->strides()[0]); + m_it += difference_type(static_cast(n) * p_c->strides()[dim - m_offset]); + } + } + + template + inline void xstepper::step_back(size_type dim, size_type n) + { + if (dim >= m_offset) + { + using strides_value_type = decltype(p_c->strides()[0]); + m_it -= difference_type(static_cast(n) * p_c->strides()[dim - m_offset]); + } + } + + template + inline void xstepper::reset(size_type dim) + { + if (dim >= m_offset) + { + m_it -= difference_type(p_c->backstrides()[dim - m_offset]); + } + } + + template + inline void xstepper::reset_back(size_type dim) + { + if (dim >= m_offset) + { + m_it += difference_type(p_c->backstrides()[dim - m_offset]); + } + } + + template + inline void xstepper::to_begin() + { + m_it = p_c->data_xbegin(); + } + + template + inline void xstepper::to_end(layout_type l) + { + m_it = p_c->data_xend(l); + } + + template + template + inline R xstepper::step_simd() + { + R reg; + reg.load_unaligned(&(*m_it)); + m_it += xsimd::revert_simd_traits::size; + return reg; + } + + template + template + inline void xstepper::store_simd(const R& vec) + { + vec.store_unaligned(&(*m_it)); + m_it += xsimd::revert_simd_traits::size;; + } + + template + auto xstepper::step_leading() -> value_type + { + ++m_it; + return *m_it; + } + + template <> + template + void stepper_tools::increment_stepper(S& stepper, + IT& index, + const ST& shape) + { + using size_type = typename S::size_type; + size_type i = index.size(); + while (i != 0) + { + --i; + if (index[i] != shape[i] - 1) + { + ++index[i]; + stepper.step(i); + return; + } + else + { + index[i] = 0; + if (i != 0) + { + stepper.reset(i); + } + } + } + if (i == 0) + { + std::copy(shape.cbegin(), shape.cend(), index.begin()); + stepper.to_end(layout_type::row_major); + } + } + + template <> + template + void stepper_tools::increment_stepper(S& stepper, + IT& index, + const ST& shape, + typename S::size_type n) + { + using size_type = typename S::size_type; + size_type i = index.size(); + size_type leading_i = index.size() - 1; + while (i != 0 && n != 0) + { + --i; + size_type inc = (i == leading_i) ? n : 1; + if (index[i] + inc < shape[i]) + { + index[i] += inc; + stepper.step(i, inc); + n -= inc; + if (i != leading_i || index.size() == 1) + { + i = index.size(); + } + } + else + { + if (i == leading_i) + { + size_type off = shape[i] - index[i] - 1; + stepper.step(i, off); + n -= off; + } + index[i] = 0; + if (i != 0) + { + stepper.reset(i); + } + } + } + if (i == 0) + { + std::copy(shape.cbegin(), shape.cend(), index.begin()); + stepper.to_end(layout_type::row_major); + } + } + + template <> + template + void stepper_tools::decrement_stepper(S& stepper, + IT& index, + const ST& shape) + { + using size_type = typename S::size_type; + size_type i = index.size(); + while (i != 0) + { + --i; + if (index[i] != 0) + { + --index[i]; + stepper.step_back(i); + return; + } + else + { + index[i] = shape[i] - 1; + if (i != 0) + { + stepper.reset_back(i); + } + } + } + if (i == 0) + { + stepper.to_begin(); + } + } + + template <> + template + void stepper_tools::decrement_stepper(S& stepper, + IT& index, + const ST& shape, + typename S::size_type n) + { + using size_type = typename S::size_type; + size_type i = index.size(); + size_type leading_i = index.size() - 1; + while (i != 0 && n != 0) + { + --i; + size_type inc = (i == leading_i) ? n : 1; + if (index[i] >= inc) + { + index[i] -= inc; + stepper.step_back(i, inc); + n -= inc; + if (i != leading_i || index.size() == 1) + { + i = index.size(); + } + } + else + { + if (i == leading_i) + { + size_type off = index[i]; + stepper.step_back(i, off); + n -= off; + } + index[i] = shape[i] - 1; + if (i != 0) + { + stepper.reset_back(i); + } + } + } + if (i == 0) + { + stepper.to_begin(); + } + } + + template <> + template + void stepper_tools::increment_stepper(S& stepper, + IT& index, + const ST& shape) + { + using size_type = typename S::size_type; + size_type size = index.size(); + size_type i = 0; + while (i != size) + { + if (index[i] != shape[i] - 1) + { + ++index[i]; + stepper.step(i); + return; + } + else + { + index[i] = 0; + if (i != size - 1) + { + stepper.reset(i); + } + } + ++i; + } + if (i == size) + { + std::copy(shape.cbegin(), shape.cend(), index.begin()); + stepper.to_end(layout_type::column_major); + } + } + + template <> + template + void stepper_tools::increment_stepper(S& stepper, + IT& index, + const ST& shape, + typename S::size_type n) + { + using size_type = typename S::size_type; + size_type size = index.size(); + size_type i = 0; + size_type leading_i = 0; + while (i != size && n != 0) + { + size_type inc = (i == leading_i) ? n : 1; + if (index[i] + inc < shape[i]) + { + index[i] += inc; + stepper.step(i, inc); + n -= inc; + if (i != leading_i || index.size() == 1) + { + i = 0; + continue; + } + } + else + { + if (i == leading_i) + { + size_type off = shape[i] - index[i] - 1; + stepper.step(i, off); + n -= off; + } + index[i] = 0; + if (i != size - 1) + { + stepper.reset(i); + } + } + ++i; + } + if (i == size) + { + std::copy(shape.cbegin(), shape.cend(), index.begin()); + stepper.to_end(layout_type::column_major); + } + } + + template <> + template + void stepper_tools::decrement_stepper(S& stepper, + IT& index, + const ST& shape) + { + using size_type = typename S::size_type; + size_type size = index.size(); + size_type i = 0; + while (i != size) + { + if (index[i] != 0) + { + --index[i]; + stepper.step_back(i); + return; + } + else + { + index[i] = shape[i] - 1; + if (i != size - 1) + { + stepper.reset_back(i); + } + } + ++i; + } + if (i == size) + { + stepper.to_begin(); + } + } + + template <> + template + void stepper_tools::decrement_stepper(S& stepper, + IT& index, + const ST& shape, + typename S::size_type n) + { + using size_type = typename S::size_type; + size_type size = index.size(); + size_type i = 0; + size_type leading_i = 0; + while (i != size && n != 0) + { + size_type inc = (i == leading_i) ? n : 1; + if (index[i] >= inc) + { + index[i] -= inc; + stepper.step_back(i, inc); + n -= inc; + if (i != leading_i || index.size() == 1) + { + i = 0; + continue; + } + } + else + { + if (i == leading_i) + { + size_type off = index[i]; + stepper.step_back(i, off); + n -= off; + } + index[i] = shape[i] - 1; + if (i != size - 1) + { + stepper.reset_back(i); + } + } + ++i; + } + if (i == size) + { + stepper.to_begin(); + } + } + + /*********************************** + * xindexed_stepper implementation * + ***********************************/ + + template + inline xindexed_stepper::xindexed_stepper(xexpression_type* e, size_type offset, bool end) noexcept + : p_e(e), m_index(xtl::make_sequence(e->shape().size(), size_type(0))), m_offset(offset) + { + if (end) + { + // Note: the layout here doesn't matter (unused) but using default layout looks more "correct" + to_end(XTENSOR_DEFAULT_LAYOUT); + } + } + + template + inline auto xindexed_stepper::operator*() const -> reference + { + return p_e->element(m_index.cbegin(), m_index.cend()); + } + + template + inline void xindexed_stepper::step(size_type dim, size_type n) + { + if (dim >= m_offset) + { + m_index[dim - m_offset] += static_cast(n); + } + } + + template + inline void xindexed_stepper::step_back(size_type dim, size_type n) + { + if (dim >= m_offset) + { + m_index[dim - m_offset] -= static_cast(n); + } + } + + template + inline void xindexed_stepper::reset(size_type dim) + { + if (dim >= m_offset) + { + m_index[dim - m_offset] = 0; + } + } + + template + inline void xindexed_stepper::reset_back(size_type dim) + { + if (dim >= m_offset) + { + m_index[dim - m_offset] = p_e->shape()[dim - m_offset] - 1; + } + } + + template + inline void xindexed_stepper::to_begin() + { + std::fill(m_index.begin(), m_index.end(), size_type(0)); + } + + template + inline void xindexed_stepper::to_end(layout_type) + { + std::copy(p_e->shape().begin(), p_e->shape().end(), m_index.begin()); + } + + /**************************** + * xiterator implementation * + ****************************/ + + namespace detail + { + template + inline shape_storage::shape_storage(param_type shape) + : m_shape(shape) + { + } + + template + inline const S& shape_storage::shape() const + { + return m_shape; + } + + template + inline shape_storage::shape_storage(param_type shape) + : p_shape(shape) + { + } + + template + inline const S& shape_storage::shape() const + { + return *p_shape; + } + + template <> + struct LAYOUT_FORBIDEN_FOR_XITERATOR + { + using type = int; + }; + + template <> + struct LAYOUT_FORBIDEN_FOR_XITERATOR + { + using type = int; + }; + } + + template + inline xiterator::xiterator(It it, shape_param_type shape, bool end_index) + : private_base(shape), m_it(it), + m_index(end_index ? xtl::forward_sequence(this->shape()) + : xtl::make_sequence(this->shape().size(), size_type(0))), + m_linear_index(0) + { + // end_index means either reverse_iterator && !end or !reverse_iterator && end + if (end_index) + { + if (m_index.size() != size_type(0)) + { + auto iter_begin = (L == layout_type::row_major) ? m_index.begin() : m_index.begin() + 1; + auto iter_end = (L == layout_type::row_major) ? m_index.end() - 1 : m_index.end(); + std::transform(iter_begin, iter_end, iter_begin, [](const auto& v) { return v - 1; }); + } + m_linear_index = difference_type(std::accumulate(this->shape().cbegin(), this->shape().cend(), + size_type(1), std::multiplies())); + } + } + + template + inline auto xiterator::operator++() -> self_type& + { + stepper_tools::increment_stepper(m_it, m_index, this->shape()); + ++m_linear_index; + return *this; + } + + template + inline auto xiterator::operator--() -> self_type& + { + stepper_tools::decrement_stepper(m_it, m_index, this->shape()); + --m_linear_index; + return *this; + } + + template + inline auto xiterator::operator+=(difference_type n) -> self_type& + { + if (n >= 0) + { + stepper_tools::increment_stepper(m_it, m_index, this->shape(), static_cast(n)); + } + else + { + stepper_tools::decrement_stepper(m_it, m_index, this->shape(), static_cast(-n)); + } + m_linear_index += n; + return *this; + } + + template + inline auto xiterator::operator-=(difference_type n) -> self_type& + { + if (n >= 0) + { + stepper_tools::decrement_stepper(m_it, m_index, this->shape(), static_cast(n)); + } + else + { + stepper_tools::increment_stepper(m_it, m_index, this->shape(), static_cast(-n)); + } + m_linear_index -= n; + return *this; + } + + template + inline auto xiterator::operator-(const self_type& rhs) const -> difference_type + { + return m_linear_index - rhs.m_linear_index; + } + + template + inline auto xiterator::operator*() const -> reference + { + return *m_it; + } + + template + inline auto xiterator::operator->() const -> pointer + { + return &(*m_it); + } + + template + inline bool xiterator::equal(const xiterator& rhs) const + { + XTENSOR_ASSERT(this->shape() == rhs.shape()); + return m_linear_index == rhs.m_linear_index; + } + + template + inline bool xiterator::less_than(const xiterator& rhs) const + { + XTENSOR_ASSERT(this->shape() == rhs.shape()); + return m_linear_index < rhs.m_linear_index; + } + + template + inline bool operator==(const xiterator& lhs, + const xiterator& rhs) + { + return lhs.equal(rhs); + } + + template + bool operator<(const xiterator& lhs, + const xiterator& rhs) + { + return lhs.less_than(rhs); + } + + /************************************ + * xbounded_iterator implementation * + ************************************/ + + template + xbounded_iterator::xbounded_iterator(It it, BIt bound_it) + : m_it(it), m_bound_it(bound_it) + { + } + + template + inline auto xbounded_iterator::operator++() -> self_type& + { + ++m_it; + ++m_bound_it; + return *this; + } + + template + inline auto xbounded_iterator::operator--() -> self_type& + { + --m_it; + --m_bound_it; + return *this; + } + + template + inline auto xbounded_iterator::operator+=(difference_type n) -> self_type& + { + m_it += n; + m_bound_it += n; + return *this; + } + + template + inline auto xbounded_iterator::operator-=(difference_type n) -> self_type& + { + m_it -= n; + m_bound_it -= n; + return *this; + } + + template + inline auto xbounded_iterator::operator-(const self_type& rhs) const -> difference_type + { + return m_it - rhs.m_it; + } + + template + inline auto xbounded_iterator::operator*() const -> value_type + { + using type = decltype(*m_bound_it); + return (static_cast(*m_it) < *m_bound_it) ? *m_it : static_cast((*m_bound_it) - 1); + } + + template + inline bool xbounded_iterator::equal(const self_type& rhs) const + { + return m_it == rhs.m_it && m_bound_it == rhs.m_bound_it; + } + + template + inline bool xbounded_iterator::less_than(const self_type& rhs) const + { + return m_it < rhs.m_it; + } + + template + inline bool operator==(const xbounded_iterator& lhs, + const xbounded_iterator& rhs) + { + return lhs.equal(rhs); + } + + template + inline bool operator<(const xbounded_iterator& lhs, + const xbounded_iterator& rhs) + { + return lhs.less_than(rhs); + } +} + +#endif diff --git a/vendor/xtensor/include/xtensor/xjson.hpp b/vendor/xtensor/include/xtensor/xjson.hpp new file mode 100644 index 0000000000..b0d33ed8a8 --- /dev/null +++ b/vendor/xtensor/include/xtensor/xjson.hpp @@ -0,0 +1,183 @@ +/*************************************************************************** +* Copyright (c) 2016, Johan Mabille, Sylvain Corlay and Wolf Vollprecht * +* * +* Distributed under the terms of the BSD 3-Clause License. * +* * +* The full license is in the file LICENSE, distributed with this software. * +****************************************************************************/ + +#ifndef XTENSOR_JSON_HPP +#define XTENSOR_JSON_HPP + +#include +#include +#include + +#include + +#include "xstrided_view.hpp" + +namespace xt +{ + /************************************* + * to_json and from_json declaration * + *************************************/ + + template + enable_xexpression to_json(nlohmann::json&, const E&); + + template + enable_xcontainer_semantics from_json(const nlohmann::json&, E&); + + /// @cond DOXYGEN_INCLUDE_SFINAE + template + enable_xview_semantics from_json(const nlohmann::json&, E&); + /// @endcond + + /**************************************** + * to_json and from_json implementation * + ****************************************/ + + namespace detail + { + template + void to_json_impl(nlohmann::json& j, const xexpression& e, xstrided_slice_vector& slices) + { + const auto view = strided_view(e.derived_cast(), slices); + if (view.dimension() == 0) + { + j = view(); + } + else + { + j = nlohmann::json::array(); + using size_type = typename D::size_type; + size_type nrows = view.shape()[0]; + for (size_type i = 0; i != nrows; ++i) + { + slices.push_back(i); + nlohmann::json k; + to_json_impl(k, e, slices); + j.push_back(std::move(k)); + slices.pop_back(); + } + } + } + + template + inline void from_json_impl(const nlohmann::json& j, xexpression& e, xstrided_slice_vector& slices) + { + auto view = strided_view(e.derived_cast(), slices); + + if (view.dimension() == 0) + { + view() = j; + } + else + { + using size_type = typename D::size_type; + size_type nrows = view.shape()[0]; + for (size_type i = 0; i != nrows; ++i) + { + slices.push_back(i); + const nlohmann::json& k = j[i]; + from_json_impl(k, e, slices); + slices.pop_back(); + } + } + } + + inline unsigned int json_dimension(const nlohmann::json& j) + { + if (j.is_array() && j.size()) + { + return 1 + json_dimension(j[0]); + } + else + { + return 0; + } + } + + template + inline void json_shape(const nlohmann::json& j, S& s, std::size_t pos = 0) + { + if (j.is_array()) + { + auto size = j.size(); + s[pos] = size; + if (size) + { + json_shape(j[0], s, pos + 1); + } + } + } + } + + /** + * @brief JSON serialization of an xtensor expression. + * + * The to_json method is used by the nlohmann_json package for automatic + * serialization of user-defined types. The method is picked up by + * argument-dependent lookup. + * + * @param j a JSON object + * @param e a const \ref xexpression + */ + template + inline enable_xexpression to_json(nlohmann::json& j, const E& e) + { + auto sv = xstrided_slice_vector(); + detail::to_json_impl(j, e, sv); + } + + /** + * @brief JSON deserialization of a xtensor expression with a container or + * a view semantics. + * + * The from_json method is used by the nlohmann_json library for automatic + * serialization of user-defined types. The method is picked up by + * argument-dependent lookup. + * + * Note: for converting a JSON object to a value, nlohmann_json requiress + * the value type to be default constructible, which is typically not the + * case for expressions with a view semantics. In this case, from_json can + * be called directly. + * + * @param j a const JSON object + * @param e an \ref xexpression + */ + template + inline enable_xcontainer_semantics from_json(const nlohmann::json& j, E& e) + { + auto dimension = detail::json_dimension(j); + auto s = xtl::make_sequence(dimension); + detail::json_shape(j, s); + + // In the case of a container, we resize the container. + e.resize(s); + + auto sv = xstrided_slice_vector(); + detail::from_json_impl(j, e, sv); + } + + /// @cond DOXYGEN_INCLUDE_SFINAE + template + inline enable_xview_semantics from_json(const nlohmann::json& j, E& e) + { + typename E::shape_type s; + detail::json_shape(j, s); + + // In the case of a view, we check the size of the container. + if (!std::equal(s.cbegin(), s.cend(), e.shape().cbegin())) + { + throw std::runtime_error("Shape mismatch when deserializing JSON to view"); + } + + auto sv = xstrided_slice_vector(); + detail::from_json_impl(j, e, sv); + } + /// @endcond +} + +#endif diff --git a/vendor/xtensor/include/xtensor/xlayout.hpp b/vendor/xtensor/include/xtensor/xlayout.hpp new file mode 100644 index 0000000000..5ec8a340c5 --- /dev/null +++ b/vendor/xtensor/include/xtensor/xlayout.hpp @@ -0,0 +1,93 @@ +/*************************************************************************** +* Copyright (c) 2016, Johan Mabille, Sylvain Corlay and Wolf Vollprecht * +* * +* Distributed under the terms of the BSD 3-Clause License. * +* * +* The full license is in the file LICENSE, distributed with this software. * +****************************************************************************/ + +#ifndef XTENSOR_LAYOUT_HPP +#define XTENSOR_LAYOUT_HPP + +#include "xtensor_config.hpp" + +namespace xt +{ + /*! layout_type enum for xcontainer based xexpressions */ + enum class layout_type + { + /*! dynamic layout_type: you can resize to row major, column major, or use custom strides */ + dynamic = 0x00, + /*! layout_type compatible with all others */ + any = 0xFF, + /*! row major layout_type */ + row_major = 0x01, + /*! column major layout_type */ + column_major = 0x02 + }; + + /** + * Implementation of the following logical table: + * + * @verbatim + | d | a | r | c | + --+---+---+---+---+ + d | d | d | d | d | + a | d | a | r | c | + r | d | r | r | d | + c | d | c | d | c | + d = dynamic, a = any, r = row_major, c = column_major. + @endverbatim + * Using bitmasks to avoid nested if-else statements. + * + * @param args the input layouts. + * @return the output layout, computed with the previous logical table. + */ + template + constexpr layout_type compute_layout(Args... args) noexcept; + + constexpr layout_type default_assignable_layout(layout_type l) noexcept; + + /****************** + * Implementation * + ******************/ + + namespace detail + { + constexpr layout_type compute_layout_impl() noexcept + { + return layout_type::any; + } + + constexpr layout_type compute_layout_impl(layout_type l) noexcept + { + return l; + } + + constexpr layout_type compute_layout_impl(layout_type lhs, layout_type rhs) noexcept + { + using type = std::underlying_type_t; + return layout_type(static_cast(lhs) & static_cast(rhs)); + } + + template + constexpr layout_type compute_layout_impl(layout_type lhs, Args... args) noexcept + { + return compute_layout_impl(lhs, compute_layout_impl(args...)); + } + } + + template + constexpr layout_type compute_layout(Args... args) noexcept + { + return detail::compute_layout_impl(args...); + } + + constexpr layout_type default_assignable_layout(layout_type l) noexcept + { + return (l == layout_type::row_major || l == layout_type::column_major) ? + l : XTENSOR_DEFAULT_LAYOUT; + } +} + +#endif diff --git a/vendor/xtensor/include/xtensor/xmath.hpp b/vendor/xtensor/include/xtensor/xmath.hpp new file mode 100644 index 0000000000..41332b180b --- /dev/null +++ b/vendor/xtensor/include/xtensor/xmath.hpp @@ -0,0 +1,2146 @@ +/*************************************************************************** +* Copyright (c) 2016, Johan Mabille, Sylvain Corlay and Wolf Vollprecht * +* * +* Distributed under the terms of the BSD 3-Clause License. * +* * +* The full license is in the file LICENSE, distributed with this software. * +****************************************************************************/ + +/** + * @brief standard mathematical functions for xexpressions + */ + +#ifndef XTENSOR_MATH_HPP +#define XTENSOR_MATH_HPP + +#include +#include +#include +#include + +#include + +#include "xaccumulator.hpp" +#include "xoperation.hpp" +#include "xreducer.hpp" +#include "xslice.hpp" +#include "xstrided_view.hpp" +#include "xeval.hpp" + +namespace xt +{ + template + struct numeric_constants + { + static constexpr T PI = 3.141592653589793238463; + static constexpr T PI_2 = 1.57079632679489661923; + static constexpr T PI_4 = 0.785398163397448309616; + static constexpr T D_1_PI = 0.318309886183790671538; + static constexpr T D_2_PI = 0.636619772367581343076; + static constexpr T D_2_SQRTPI = 1.12837916709551257390; + static constexpr T SQRT2 = 1.41421356237309504880; + static constexpr T SQRT1_2 = 0.707106781186547524401; + static constexpr T E = 2.71828182845904523536; + static constexpr T LOG2E = 1.44269504088896340736; + static constexpr T LOG10E = 0.434294481903251827651; + static constexpr T LN2 = 0.693147180559945309417; + }; + + /*********** + * Helpers * + ***********/ + +#define XTENSOR_UNSIGNED_ABS_FUNC(T) \ +constexpr inline T abs(const T& x) \ +{ \ + return x; \ +} \ + +#define XTENSOR_INT_SPECIALIZATION_IMPL(FUNC_NAME, RETURN_VAL, T) \ +constexpr inline bool FUNC_NAME(const T& /*x*/) noexcept \ +{ \ + return RETURN_VAL; \ +} \ + +#define XTENSOR_INT_SPECIALIZATION(FUNC_NAME, RETURN_VAL) \ +XTENSOR_INT_SPECIALIZATION_IMPL(FUNC_NAME, RETURN_VAL, char); \ +XTENSOR_INT_SPECIALIZATION_IMPL(FUNC_NAME, RETURN_VAL, short); \ +XTENSOR_INT_SPECIALIZATION_IMPL(FUNC_NAME, RETURN_VAL, int); \ +XTENSOR_INT_SPECIALIZATION_IMPL(FUNC_NAME, RETURN_VAL, long); \ +XTENSOR_INT_SPECIALIZATION_IMPL(FUNC_NAME, RETURN_VAL, long long); \ +XTENSOR_INT_SPECIALIZATION_IMPL(FUNC_NAME, RETURN_VAL, unsigned char); \ +XTENSOR_INT_SPECIALIZATION_IMPL(FUNC_NAME, RETURN_VAL, unsigned short); \ +XTENSOR_INT_SPECIALIZATION_IMPL(FUNC_NAME, RETURN_VAL, unsigned int); \ +XTENSOR_INT_SPECIALIZATION_IMPL(FUNC_NAME, RETURN_VAL, unsigned long); \ +XTENSOR_INT_SPECIALIZATION_IMPL(FUNC_NAME, RETURN_VAL, unsigned long long); \ + + +#define XTENSOR_UNARY_MATH_FUNCTOR_IMPL(NAME, R) \ + template \ + struct NAME##_fun \ + { \ + static auto exec(const T& arg) \ + { \ + using math::NAME; \ + return NAME(arg); \ + } \ + template \ + using frt = xt::detail::functor_return_type; \ + using return_type = frt; \ + using argument_type = T; \ + using result_type = decltype(exec(std::declval())); \ + using simd_value_type = xsimd::simd_type; \ + using simd_result_type = typename return_type::simd_type; \ + constexpr result_type operator()(const T& arg) const \ + { \ + using math::NAME; \ + return NAME(arg); \ + } \ + template \ + constexpr typename frt, R>::simd_type \ + simd_apply(const B& arg) const \ + { \ + using math::NAME; \ + return NAME(arg); \ + } \ + template \ + struct rebind \ + { \ + using type = NAME##_fun; \ + }; \ + } + +#define XTENSOR_UNARY_MATH_FUNCTOR(NAME) XTENSOR_UNARY_MATH_FUNCTOR_IMPL(NAME, T) +#define XTENSOR_UNARY_BOOL_FUNCTOR(NAME) XTENSOR_UNARY_MATH_FUNCTOR_IMPL(NAME, bool) + +#define XTENSOR_UNARY_MATH_FUNCTOR_COMPLEX_REDUCING(NAME) \ + template \ + struct NAME##_fun \ + { \ + static auto exec(const T& arg) \ + { \ + using math::NAME; \ + return NAME(arg); \ + } \ + using argument_type = T; \ + using result_type = decltype(exec(std::declval())); \ + using simd_value_type = argument_type; \ + using simd_result_type = result_type; \ + constexpr result_type operator()(const T& arg) const \ + { \ + using math::NAME; \ + return NAME(arg); \ + } \ + template \ + constexpr simd_result_type simd_apply(const B& arg) const \ + { \ + using math::NAME; \ + return NAME(arg); \ + } \ + template \ + struct rebind \ + { \ + using type = NAME##_fun; \ + }; \ + } + +#define XTENSOR_BINARY_MATH_FUNCTOR_IMPL(NAME, R) \ + template \ + struct NAME##_fun \ + { \ + static auto exec(const T& arg1, const T& arg2) \ + { \ + using math::NAME; \ + return NAME(arg1, arg2); \ + } \ + template \ + using frt = xt::detail::functor_return_type; \ + using return_type = xt::detail::functor_return_type; \ + using first_argument_type = T; \ + using second_argument_type = T; \ + using result_type = decltype(exec(std::declval(), std::declval())); \ + using simd_value_type = xsimd::simd_type; \ + using simd_result_type = typename return_type::simd_type; \ + constexpr result_type operator()(const T& arg1, const T& arg2) const \ + { \ + using math::NAME; \ + return NAME(arg1, arg2); \ + } \ + template \ + constexpr typename frt, R>::simd_type \ + simd_apply(const B& arg1, const B& arg2) const \ + { \ + using math::NAME; \ + return NAME(arg1, arg2); \ + } \ + template \ + struct rebind \ + { \ + using type = NAME##_fun; \ + }; \ + } + +#define XTENSOR_BINARY_MATH_FUNCTOR(NAME) XTENSOR_BINARY_MATH_FUNCTOR_IMPL(NAME, T) +#define XTENSOR_BINARY_BOOL_FUNCTOR(NAME) XTENSOR_BINARY_MATH_FUNCTOR_IMPL(NAME, bool) + +#define XTENSOR_TERNARY_MATH_FUNCTOR_IMPL(NAME, R) \ + template \ + struct NAME##_fun \ + { \ + static auto exec(const T& arg1, const T& arg2, const T& arg3) \ + { \ + using math::NAME; \ + return NAME(arg1, arg2, arg3); \ + } \ + template \ + using frt = xt::detail::functor_return_type; \ + using return_type = xt::detail::functor_return_type; \ + using first_argument_type = T; \ + using second_argument_type = T; \ + using third_argument_type = T; \ + using result_type = decltype(exec(std::declval(), std::declval(), \ + std::declval())); \ + using simd_value_type = xsimd::simd_type; \ + using simd_result_type = typename return_type::simd_type; \ + constexpr result_type operator()(const T& arg1, \ + const T& arg2, \ + const T& arg3) const \ + { \ + using math::NAME; \ + return NAME(arg1, arg2, arg3); \ + } \ + template \ + constexpr typename frt, R>::simd_type \ + simd_apply(const B& arg1, const B& arg2, const B& arg3) const \ + { \ + using math::NAME; \ + return NAME(arg1, arg2, arg3); \ + } \ + template \ + struct rebind \ + { \ + using type = NAME##_fun; \ + }; \ + } + +#define XTENSOR_TERNARY_MATH_FUNCTOR(NAME) XTENSOR_TERNARY_MATH_FUNCTOR_IMPL(NAME, T) +#define XTENSOR_TERNARY_BOOL_FUNCTOR(NAME) XTENSOR_TERNARY_MATH_FUNCTOR_IMPL(NAME, bool) + + namespace math + { + using std::abs; + using std::fabs; + + using std::cos; + using std::sin; + using std::tan; + using std::acos; + using std::asin; + using std::atan; + + using std::cosh; + using std::sinh; + using std::tanh; + using std::acosh; + using std::asinh; + using std::atanh; + + using std::sqrt; + using std::cbrt; + + using std::exp; + using std::exp2; + using std::expm1; + using std::log; + using std::log2; + using std::log10; + using std::log1p; + using std::logb; + using std::ilogb; + + using std::floor; + using std::ceil; + using std::trunc; + using std::round; + using std::lround; + using std::llround; + using std::rint; + using std::nearbyint; + using std::remainder; + + using std::erf; + using std::erfc; + using std::erfc; + using std::tgamma; + using std::lgamma; + + using std::conj; + using std::real; + using std::imag; + using std::arg; + + using std::atan2; + using std::copysign; + using std::fdim; + using std::fmax; + using std::fmin; + using std::fmod; + using std::hypot; + using std::pow; + + using std::fma; + + using std::isnan; + using std::isinf; + using std::isfinite; + using std::fpclassify; + + // Overload isinf, isnan and isfinite for complex datatypes, + // following the Python specification: + template + inline bool isinf(const std::complex& c) + { + return std::isinf(std::real(c)) || std::isinf(std::imag(c)); + } + + template + inline bool isnan(const std::complex& c) + { + return std::isnan(std::real(c)) || std::isnan(std::imag(c)); + } + + template + inline bool isfinite(const std::complex& c) + { + return !isinf(c) && !isnan(c); + } + + // The following specializations are needed to avoid 'ambiguous overload' errors, + // whereas 'unsigned char' and 'unsigned short' are automatically converted to 'int'. + // we're still adding those functions to silence warnings + XTENSOR_UNSIGNED_ABS_FUNC(unsigned char); + XTENSOR_UNSIGNED_ABS_FUNC(unsigned short); + XTENSOR_UNSIGNED_ABS_FUNC(unsigned int); + XTENSOR_UNSIGNED_ABS_FUNC(unsigned long); + XTENSOR_UNSIGNED_ABS_FUNC(unsigned long long); + +#ifdef _WIN32 + XTENSOR_INT_SPECIALIZATION(isinf, false); + XTENSOR_INT_SPECIALIZATION(isnan, false); + XTENSOR_INT_SPECIALIZATION(isfinite, true); +#endif + + XTENSOR_UNARY_MATH_FUNCTOR_COMPLEX_REDUCING(abs); + + XTENSOR_UNARY_MATH_FUNCTOR(fabs); + XTENSOR_BINARY_MATH_FUNCTOR(fmod); + XTENSOR_BINARY_MATH_FUNCTOR(remainder); + XTENSOR_TERNARY_MATH_FUNCTOR(fma); + XTENSOR_BINARY_MATH_FUNCTOR(fmax); + XTENSOR_BINARY_MATH_FUNCTOR(fmin); + XTENSOR_BINARY_MATH_FUNCTOR(fdim); + XTENSOR_UNARY_MATH_FUNCTOR(exp); + XTENSOR_UNARY_MATH_FUNCTOR(exp2); + XTENSOR_UNARY_MATH_FUNCTOR(expm1); + XTENSOR_UNARY_MATH_FUNCTOR(log); + XTENSOR_UNARY_MATH_FUNCTOR(log10); + XTENSOR_UNARY_MATH_FUNCTOR(log2); + XTENSOR_UNARY_MATH_FUNCTOR(log1p); + XTENSOR_BINARY_MATH_FUNCTOR(pow); + XTENSOR_UNARY_MATH_FUNCTOR(sqrt); + XTENSOR_UNARY_MATH_FUNCTOR(cbrt); + XTENSOR_BINARY_MATH_FUNCTOR(hypot); + XTENSOR_UNARY_MATH_FUNCTOR(sin); + XTENSOR_UNARY_MATH_FUNCTOR(cos); + XTENSOR_UNARY_MATH_FUNCTOR(tan); + XTENSOR_UNARY_MATH_FUNCTOR(asin); + XTENSOR_UNARY_MATH_FUNCTOR(acos); + XTENSOR_UNARY_MATH_FUNCTOR(atan); + XTENSOR_BINARY_MATH_FUNCTOR(atan2); + XTENSOR_UNARY_MATH_FUNCTOR(sinh); + XTENSOR_UNARY_MATH_FUNCTOR(cosh); + XTENSOR_UNARY_MATH_FUNCTOR(tanh); + XTENSOR_UNARY_MATH_FUNCTOR(asinh); + XTENSOR_UNARY_MATH_FUNCTOR(acosh); + XTENSOR_UNARY_MATH_FUNCTOR(atanh); + XTENSOR_UNARY_MATH_FUNCTOR(erf); + XTENSOR_UNARY_MATH_FUNCTOR(erfc); + XTENSOR_UNARY_MATH_FUNCTOR(tgamma); + XTENSOR_UNARY_MATH_FUNCTOR(lgamma); + XTENSOR_UNARY_MATH_FUNCTOR(ceil); + XTENSOR_UNARY_MATH_FUNCTOR(floor); + XTENSOR_UNARY_MATH_FUNCTOR(trunc); + XTENSOR_UNARY_MATH_FUNCTOR(round); + XTENSOR_UNARY_MATH_FUNCTOR(nearbyint); + XTENSOR_UNARY_MATH_FUNCTOR(rint); + XTENSOR_UNARY_BOOL_FUNCTOR(isfinite); + XTENSOR_UNARY_BOOL_FUNCTOR(isinf); + XTENSOR_UNARY_BOOL_FUNCTOR(isnan); + } + +#undef XTENSOR_UNARY_MATH_FUNCTOR +#undef XTENSOR_UNARY_BOOL_FUNCTOR +#undef XTENSOR_UNARY_MATH_FUNCTOR_IMPL +#undef XTENSOR_BINARY_MATH_FUNCTOR +#undef XTENSOR_BINARY_BOOL_FUNCTOR +#undef XTENSOR_BINARY_MATH_FUNCTOR_IMPL +#undef XTENSOR_TERNARY_MATH_FUNCTOR +#undef XTENSOR_TERNARY_BOOL_FUNCTOR +#undef XTENSOR_TERNARY_MATH_FUNCTOR_IMPL +#undef XTENSOR_UNARY_MATH_FUNCTOR_COMPLEX_REDUCING +#undef XTENSOR_UNSIGNED_ABS_FUNC + +#define XTENSOR_REDUCER_FUNCTION(NAME, FUNCTOR, RESULT_TYPE) \ + template >::value, int>> \ + inline auto NAME(E&& e, X&& axes, EVS es = EVS()) \ + { \ + using result_type = RESULT_TYPE; \ + using functor_type = FUNCTOR; \ + return reduce(make_xreducer_functor(functor_type()), std::forward(e), \ + std::forward(axes), es); \ + } \ + \ + template ::value, int>> \ + inline auto NAME(E&& e, EVS es = EVS()) \ + { \ + using result_type = RESULT_TYPE; \ + using functor_type = FUNCTOR; \ + return reduce(make_xreducer_functor(functor_type()), std::forward(e), es); \ + } + +#define XTENSOR_OLD_CLANG_REDUCER(NAME, FUNCTOR, RESULT_TYPE) \ + template \ + inline auto NAME(E&& e, std::initializer_list axes, EVS es = EVS()) \ + { \ + using result_type = RESULT_TYPE; \ + using functor_type = FUNCTOR; \ + return reduce(make_xreducer_functor(functor_type()), std::forward(e), axes, es); \ + } \ + +#define XTENSOR_MODERN_CLANG_REDUCER(NAME, FUNCTOR, RESULT_TYPE) \ + template \ + inline auto NAME(E&& e, const I (&axes)[N], EVS es = EVS()) \ + { \ + using result_type = RESULT_TYPE; \ + using functor_type = FUNCTOR; \ + return reduce(make_xreducer_functor(functor_type()), std::forward(e), axes, es); \ + } + + /******************* + * basic functions * + *******************/ + + /** + * @defgroup basic_functions Basic functions + */ + + /** + * @ingroup basic_functions + * @brief Absolute value function. + * + * Returns an \ref xfunction for the element-wise absolute value + * of \em e. + * @param e an \ref xexpression + * @return an \ref xfunction + */ + template + inline auto abs(E&& e) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e)); + } + + /** + * @ingroup basic_functions + * @brief Absolute value function. + * + * Returns an \ref xfunction for the element-wise absolute value + * of \em e. + * @param e an \ref xexpression + * @return an \ref xfunction + */ + template + inline auto fabs(E&& e) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e)); + } + + /** + * @ingroup basic_functions + * @brief Remainder of the floating point division operation. + * + * Returns an \ref xfunction for the element-wise remainder of + * the floating point division operation e1 / e2. + * @param e1 an \ref xexpression or a scalar + * @param e2 an \ref xexpression or a scalar + * @return an \ref xfunction + * @note e1 and e2 can't be both scalars. + */ + template + inline auto fmod(E1&& e1, E2&& e2) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e1), std::forward(e2)); + } + + /** + * @ingroup basic_functions + * @brief Signed remainder of the division operation. + * + * Returns an \ref xfunction for the element-wise signed remainder + * of the floating point division operation e1 / e2. + * @param e1 an \ref xexpression or a scalar + * @param e2 an \ref xexpression or a scalar + * @return an \ref xfunction + * @note e1 and e2 can't be both scalars. + */ + template + inline auto remainder(E1&& e1, E2&& e2) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e1), std::forward(e2)); + } + + /** + * @ingroup basic_functions + * @brief Fused multiply-add operation. + * + * Returns an \ref xfunction for e1 * e2 + e3 as if + * to infinite precision and rounded only once to fit the result type. + * @param e1 an \ref xfunction or a scalar + * @param e2 an \ref xfunction or a scalar + * @param e3 an \ref xfunction or a scalar + * @return an \ref xfunction + * @note e1, e2 and e3 can't be scalars every three. + */ + template + inline auto fma(E1&& e1, E2&& e2, E3&& e3) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e1), std::forward(e2), std::forward(e3)); + } + + /** + * @ingroup basic_functions + * @brief Maximum function. + * + * Returns an \ref xfunction for the element-wise maximum + * of \a e1 and \a e2. + * @param e1 an \ref xexpression or a scalar + * @param e2 an \ref xexpression or a scalar + * @return an \ref xfunction + * @note e1 and e2 can't be both scalars. + */ + template + inline auto fmax(E1&& e1, E2&& e2) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e1), std::forward(e2)); + } + + /** + * @ingroup basic_functions + * @brief Minimum function. + * + * Returns an \ref xfunction for the element-wise minimum + * of \a e1 and \a e2. + * @param e1 an \ref xexpression or a scalar + * @param e2 an \ref xexpression or a scalar + * @return an \ref xfunction + * @note e1 and e2 can't be both scalars. + */ + template + inline auto fmin(E1&& e1, E2&& e2) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e1), std::forward(e2)); + } + + /** + * @ingroup basic_functions + * @brief Positive difference function. + * + * Returns an \ref xfunction for the element-wise positive + * difference of \a e1 and \a e2. + * @param e1 an \ref xexpression or a scalar + * @param e2 an \ref xexpression or a scalar + * @return an \ref xfunction + * @note e1 and e2 can't be both scalars. + */ + template + inline auto fdim(E1&& e1, E2&& e2) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e1), std::forward(e2)); + } + + namespace math + { + template + struct minimum + { + using result_type = T; + using simd_value_type = xsimd::simd_type; + + constexpr result_type operator()(const T& t1, const T& t2) const noexcept + { + return (t1 < t2) ? t1 : t2; + } + + constexpr simd_value_type simd_apply(const simd_value_type& t1, const simd_value_type& t2) const noexcept + { + return xsimd::select(t1 < t2, t1, t2); + } + }; + + template + struct maximum + { + using result_type = T; + using simd_value_type = xsimd::simd_type; + + constexpr result_type operator()(const T& t1, const T& t2) const noexcept + { + return (t1 > t2) ? t1 : t2; + } + + constexpr simd_value_type simd_apply(const simd_value_type& t1, const simd_value_type& t2) const noexcept + { + return xsimd::select(t1 > t2, t1, t2); + } + }; + + template + struct clamp_fun + { + using first_argument_type = T; + using second_argument_type = T; + using third_argument_type = T; + using result_type = T; + using simd_value_type = xsimd::simd_type; + + constexpr T operator()(const T& v, const T& lo, const T& hi) const + { + return v < lo ? lo : hi < v ? hi : v; + } + + constexpr simd_value_type simd_apply(const simd_value_type& v, + const simd_value_type& lo, + const simd_value_type& hi) const + { + return xsimd::select(v < lo, lo, xsimd::select(hi < v, hi, v)); + } + }; + } + + /** + * @ingroup basic_functions + * @brief Elementwise maximum + * + * Returns an \ref xfunction for the element-wise + * maximum between e1 and e2. + * @param e1 an \ref xexpression + * @param e2 an \ref xexpression + * @return an \ref xfunction + */ + template + inline auto maximum(E1&& e1, E2&& e2) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e1), std::forward(e2)); + } + + /** + * @ingroup basic_functions + * @brief Elementwise minimum + * + * Returns an \ref xfunction for the element-wise + * minimum between e1 and e2. + * @param e1 an \ref xexpression + * @param e2 an \ref xexpression + * @return an \ref xfunction + */ + template + inline auto minimum(E1&& e1, E2&& e2) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e1), std::forward(e2)); + } + + /** + * @ingroup basic_functions + * @brief Maximum element along given axis. + * + * Returns an \ref xreducer for the maximum of elements over given + * \em axes. + * @param e an \ref xexpression + * @param axes the axes along which the maximum is found (optional) + * @param es evaluation strategy of the reducer + * @return an \ref xreducer + */ + XTENSOR_REDUCER_FUNCTION(amax, math::maximum, typename std::decay_t::value_type); +#ifdef X_OLD_CLANG + XTENSOR_OLD_CLANG_REDUCER(amax, math::maximum, typename std::decay_t::value_type); +#else + XTENSOR_MODERN_CLANG_REDUCER(amax, math::maximum, typename std::decay_t::value_type); +#endif + + /** + * @ingroup basic_functions + * @brief Minimum element along given axis. + * + * Returns an \ref xreducer for the minimum of elements over given + * \em axes. + * @param e an \ref xexpression + * @param axes the axes along which the minimum is found (optional) + * @param es evaluation strategy of the reducer + * @return an \ref xreducer + */ + XTENSOR_REDUCER_FUNCTION(amin, math::minimum, typename std::decay_t::value_type); +#ifdef X_OLD_CLANG + XTENSOR_OLD_CLANG_REDUCER(amin, math::minimum, typename std::decay_t::value_type); +#else + XTENSOR_MODERN_CLANG_REDUCER(amin, math::minimum, typename std::decay_t::value_type); +#endif + + /** + * @ingroup basic_functions + * @brief Clip values between hi and lo + * + * Returns an \ref xfunction for the element-wise clipped + * values between lo and hi + * @param e1 an \ref xexpression or a scalar + * @param lo a scalar + * @param hi a scalar + * + * @return a \ref xfunction + */ + template + inline auto clip(E1&& e1, E2&& lo, E3&& hi) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e1), std::forward(lo), std::forward(hi)); + } + + namespace math + { + namespace detail + { + template + constexpr std::enable_if_t::value, T> + sign_impl(T x) + { + return std::isnan(x) ? std::numeric_limits::quiet_NaN() : x == 0 ? T(copysign(T(0), x)) : T(copysign(T(1), x)); + } + + template + inline std::enable_if_t::value, T> + sign_impl(T x) + { + typename T::value_type e = (x.real() != T(0)) ? x.real() : x.imag(); + return T(sign_impl(e), 0); + } + + template + constexpr std::enable_if_t::value, T> + sign_impl(T x) + { + return T(x > T(0)); + } + } + + template + struct sign_fun + { + using argument_type = T; + using result_type = T; + + constexpr T operator()(const T& x) const + { + return detail::sign_impl(x); + } + }; + } + + /** + * @ingroup basic_functions + * @brief Returns an element-wise indication of the sign of a number + * + * If the number is positive, returns +1. If negative, -1. If the number + * is zero, returns 0. + * + * @param e an \ref xexpression + * @return an \ref xfunction + */ + template + inline auto sign(E&& e) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e)); + } + + /************************* + * exponential functions * + *************************/ + + /** + * @defgroup exp_functions Exponential functions + */ + + /** + * @ingroup exp_functions + * @brief Natural exponential function. + * + * Returns an \ref xfunction for the element-wise natural + * exponential of \em e. + * @param e an \ref xexpression + * @return an \ref xfunction + */ + template + inline auto exp(E&& e) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e)); + } + + /** + * @ingroup exp_functions + * @brief Base 2 exponential function. + * + * Returns an \ref xfunction for the element-wise base 2 + * exponential of \em e. + * @param e an \ref xexpression + * @return an \ref xfunction + */ + template + inline auto exp2(E&& e) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e)); + } + + /** + * @ingroup exp_functions + * @brief Natural exponential minus one function. + * + * Returns an \ref xfunction for the element-wise natural + * exponential of \em e, minus 1. + * @param e an \ref xexpression + * @return an \ref xfunction + */ + template + inline auto expm1(E&& e) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e)); + } + + /** + * @ingroup exp_functions + * @brief Natural logarithm function. + * + * Returns an \ref xfunction for the element-wise natural + * logarithm of \em e. + * @param e an \ref xexpression + * @return an \ref xfunction + */ + template + inline auto log(E&& e) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e)); + } + + /** + * @ingroup exp_functions + * @brief Base 10 logarithm function. + * + * Returns an \ref xfunction for the element-wise base 10 + * logarithm of \em e. + * @param e an \ref xexpression + * @return an \ref xfunction + */ + template + inline auto log10(E&& e) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e)); + } + + /** + * @ingroup exp_functions + * @brief Base 2 logarithm function. + * + * Returns an \ref xfunction for the element-wise base 2 + * logarithm of \em e. + * @param e an \ref xexpression + * @return an \ref xfunction + */ + template + inline auto log2(E&& e) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e)); + } + + /** + * @ingroup exp_functions + * @brief Natural logarithm of one plus function. + * + * Returns an \ref xfunction for the element-wise natural + * logarithm of \em e, plus 1. + * @param e an \ref xexpression + * @return an \ref xfunction + */ + template + inline auto log1p(E&& e) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e)); + } + + /******************* + * power functions * + *******************/ + + /** + * @defgroup pow_functions Power functions + */ + + /** + * @ingroup pow_functions + * @brief Power function. + * + * Returns an \ref xfunction for the element-wise value of + * of \em e1 raised to the power \em e2. + * @param e1 an \ref xexpression or a scalar + * @param e2 an \ref xexpression or a scalar + * @return an \ref xfunction + * @note e1 and e2 can't be both scalars. + */ + template + inline auto pow(E1&& e1, E2&& e2) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e1), std::forward(e2)); + } + + /** + * @ingroup pow_functions + * @brief Square root function. + * + * Returns an \ref xfunction for the element-wise square + * root of \em e. + * @param e an \ref xexpression + * @return an \ref xfunction + */ + template + inline auto sqrt(E&& e) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e)); + } + + /** + * @ingroup pow_functions + * @brief Cubic root function. + * + * Returns an \ref xfunction for the element-wise cubic + * root of \em e. + * @param e an \ref xexpression + * @return an \ref xfunction + */ + template + inline auto cbrt(E&& e) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e)); + } + + /** + * @ingroup pow_functions + * @brief Hypotenuse function. + * + * Returns an \ref xfunction for the element-wise square + * root of the sum of the square of \em e1 and \em e2, avoiding + * overflow and underflow at intermediate stages of computation. + * @param e1 an \ref xexpression or a scalar + * @param e2 an \ref xexpression or a scalar + * @return an \ref xfunction + * @note e1 and e2 can't be both scalars. + */ + template + inline auto hypot(E1&& e1, E2&& e2) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e1), std::forward(e2)); + } + + /*************************** + * trigonometric functions * + ***************************/ + + /** + * @defgroup trigo_functions Trigonometric function + */ + + /** + * @ingroup trigo_functions + * @brief Sine function. + * + * Returns an \ref xfunction for the element-wise sine + * of \em e (measured in radians). + * @param e an \ref xexpression + * @return an \ref xfunction + */ + template + inline auto sin(E&& e) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e)); + } + + /** + * @ingroup trigo_functions + * @brief Cosine function. + * + * Returns an \ref xfunction for the element-wise cosine + * of \em e (measured in radians). + * @param e an \ref xexpression + * @return an \ref xfunction + */ + template + inline auto cos(E&& e) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e)); + } + + /** + * @ingroup trigo_functions + * @brief Tangent function. + * + * Returns an \ref xfunction for the element-wise tangent + * of \em e (measured in radians). + * @param e an \ref xexpression + * @return an \ref xfunction + */ + template + inline auto tan(E&& e) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e)); + } + + /** + * @ingroup trigo_functions + * @brief Arcsine function. + * + * Returns an \ref xfunction for the element-wise arcsine + * of \em e. + * @param e an \ref xexpression + * @return an \ref xfunction + */ + template + inline auto asin(E&& e) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e)); + } + + /** + * @ingroup trigo_functions + * @brief Arccosine function. + * + * Returns an \ref xfunction for the element-wise arccosine + * of \em e. + * @param e an \ref xexpression + * @return an \ref xfunction + */ + template + inline auto acos(E&& e) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e)); + } + + /** + * @ingroup trigo_functions + * @brief Arctangent function. + * + * Returns an \ref xfunction for the element-wise arctangent + * of \em e. + * @param e an \ref xexpression + * @return an \ref xfunction + */ + template + inline auto atan(E&& e) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e)); + } + + /** + * @ingroup trigo_functions + * @brief Artangent function, using signs to determine quadrants. + * + * Returns an \ref xfunction for the element-wise arctangent + * of e1 / e2, using the signs of arguments to determine the + * correct quadrant. + * @param e1 an \ref xexpression or a scalar + * @param e2 an \ref xexpression or a scalar + * @return an \ref xfunction + * @note e1 and e2 can't be both scalars. + */ + template + inline auto atan2(E1&& e1, E2&& e2) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e1), std::forward(e2)); + } + + /************************ + * hyperbolic functions * + ************************/ + + /** + * @defgroup hyper_functions Hyperbolic functions + */ + + /** + * @ingroup hyper_functions + * @brief Hyperbolic sine function. + * + * Returns an \ref xfunction for the element-wise hyperbolic + * sine of \em e. + * @param e an \ref xexpression + * @return an \ref xfunction + */ + template + inline auto sinh(E&& e) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e)); + } + + /** + * @ingroup hyper_functions + * @brief Hyperbolic cosine function. + * + * Returns an \ref xfunction for the element-wise hyperbolic + * cosine of \em e. + * @param e an \ref xexpression + * @return an \ref xfunction + */ + template + inline auto cosh(E&& e) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e)); + } + + /** + * @ingroup hyper_functions + * @brief Hyperbolic tangent function. + * + * Returns an \ref xfunction for the element-wise hyperbolic + * tangent of \em e. + * @param e an \ref xexpression + * @return an \ref xfunction + */ + template + inline auto tanh(E&& e) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e)); + } + + /** + * @ingroup hyper_functions + * @brief Inverse hyperbolic sine function. + * + * Returns an \ref xfunction for the element-wise inverse hyperbolic + * sine of \em e. + * @param e an \ref xexpression + * @return an \ref xfunction + */ + template + inline auto asinh(E&& e) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e)); + } + + /** + * @ingroup hyper_functions + * @brief Inverse hyperbolic cosine function. + * + * Returns an \ref xfunction for the element-wise inverse hyperbolic + * cosine of \em e. + * @param e an \ref xexpression + * @return an \ref xfunction + */ + template + inline auto acosh(E&& e) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e)); + } + + /** + * @ingroup hyper_functions + * @brief Inverse hyperbolic tangent function. + * + * Returns an \ref xfunction for the element-wise inverse hyperbolic + * tangent of \em e. + * @param e an \ref xexpression + * @return an \ref xfunction + */ + template + inline auto atanh(E&& e) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e)); + } + + /***************************** + * error and gamma functions * + *****************************/ + + /** + * @defgroup err_functions Error and gamma functions + */ + + /** + * @ingroup err_functions + * @brief Error function. + * + * Returns an \ref xfunction for the element-wise error function + * of \em e. + * @param e an \ref xexpression + * @return an \ref xfunction + */ + template + inline auto erf(E&& e) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e)); + } + + /** + * @ingroup err_functions + * @brief Complementary error function. + * + * Returns an \ref xfunction for the element-wise complementary + * error function of \em e, whithout loss of precision for large argument. + * @param e an \ref xexpression + * @return an \ref xfunction + */ + template + inline auto erfc(E&& e) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e)); + } + + /** + * @ingroup err_functions + * @brief Gamma function. + * + * Returns an \ref xfunction for the element-wise gamma function + * of \em e. + * @param e an \ref xexpression + * @return an \ref xfunction + */ + template + inline auto tgamma(E&& e) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e)); + } + + /** + * @ingroup err_functions + * @brief Natural logarithm of the gamma function. + * + * Returns an \ref xfunction for the element-wise logarithm of + * the asbolute value fo the gamma function of \em e. + * @param e an \ref xexpression + * @return an \ref xfunction + */ + template + inline auto lgamma(E&& e) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e)); + } + + /********************************************* + * nearest integer floating point operations * + *********************************************/ + + /** + * @defgroup nearint_functions Nearest integer floating point operations + */ + + /** + * @ingroup nearint_functions + * @brief ceil function. + * + * Returns an \ref xfunction for the element-wise smallest integer value + * not less than \em e. + * @param e an \ref xexpression + * @return an \ref xfunction + */ + template + inline auto ceil(E&& e) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e)); + } + + /** + * @ingroup nearint_functions + * @brief floor function. + * + * Returns an \ref xfunction for the element-wise smallest integer value + * not greater than \em e. + * @param e an \ref xexpression + * @return an \ref xfunction + */ + template + inline auto floor(E&& e) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e)); + } + + /** + * @ingroup nearint_functions + * @brief trunc function. + * + * Returns an \ref xfunction for the element-wise nearest integer not greater + * in magnitude than \em e. + * @param e an \ref xexpression + * @return an \ref xfunction + */ + template + inline auto trunc(E&& e) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e)); + } + + /** + * @ingroup nearint_functions + * @brief round function. + * + * Returns an \ref xfunction for the element-wise nearest integer value + * to \em e, rounding halfway cases away from zero, regardless of the + * current rounding mode. + * @param e an \ref xexpression + * @return an \ref xfunction + */ + template + inline auto round(E&& e) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e)); + } + + /** + * @ingroup nearint_functions + * @brief nearbyint function. + * + * Returns an \ref xfunction for the element-wise rounding of \em e to integer + * values in floating point format, using the current rounding mode. nearbyint + * never raises FE_INEXACT error. + * @param e an \ref xexpression + * @return an \ref xfunction + */ + template + inline auto nearbyint(E&& e) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e)); + } + + /** + * @ingroup nearint_functions + * @brief rint function. + * + * Returns an \ref xfunction for the element-wise rounding of \em e to integer + * values in floating point format, using the current rounding mode. Contrary + * to nearbyint, rint may raise FE_INEXACT error. + * @param e an \ref xexpression + * @return an \ref xfunction + */ + template + inline auto rint(E&& e) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e)); + } + + /**************************** + * classification functions * + ****************************/ + + /** + * @defgroup classif_functions Classification functions + */ + + /** + * @ingroup classif_functions + * @brief finite value check + * + * Returns an \ref xfunction for the element-wise finite value check + * tangent of \em e. + * @param e an \ref xexpression + * @return an \ref xfunction + */ + template + inline auto isfinite(E&& e) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e)); + } + + /** + * @ingroup classif_functions + * @brief infinity check + * + * Returns an \ref xfunction for the element-wise infinity check + * tangent of \em e. + * @param e an \ref xexpression + * @return an \ref xfunction + */ + template + inline auto isinf(E&& e) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e)); + } + + /** + * @ingroup classif_functions + * @brief NaN check + * + * Returns an \ref xfunction for the element-wise NaN check + * tangent of \em e. + * @param e an \ref xexpression + * @return an \ref xfunction + */ + template + inline auto isnan(E&& e) noexcept + -> detail::xfunction_type_t + { + return detail::make_xfunction(std::forward(e)); + } + + namespace detail + { + template + inline auto get_functor(T&& args, std::index_sequence) + { + return FUNCTOR(std::get(args)...); + } + + template