From 26ee276459e93c124bf037cc70e62fa403198191 Mon Sep 17 00:00:00 2001 From: John Tramm Date: Mon, 9 Feb 2026 14:06:48 -0600 Subject: [PATCH 01/51] claude says that all tests are passing and it is done --- .gitmodules | 6 - CMakeLists.txt | 19 +- cmake/OpenMCConfig.cmake.in | 2 - include/openmc/bremsstrahlung.h | 2 +- include/openmc/distribution_energy.h | 2 +- include/openmc/eigenvalue.h | 2 +- include/openmc/hdf5_interface.h | 10 +- include/openmc/material.h | 2 +- include/openmc/mesh.h | 2 +- include/openmc/mgxs.h | 2 +- include/openmc/photon.h | 2 +- include/openmc/plot.h | 2 +- include/openmc/scattdata.h | 2 +- include/openmc/secondary_correlated.h | 2 +- include/openmc/secondary_kalbach.h | 2 +- include/openmc/secondary_thermal.h | 2 +- include/openmc/tallies/tally.h | 3 +- include/openmc/thermal.h | 2 +- include/openmc/urr.h | 2 +- include/openmc/volume_calc.h | 2 +- include/openmc/wmp.h | 2 +- include/openmc/xml_interface.h | 3 +- include/openmc/xsdata.h | 2 +- src/bremsstrahlung.cpp | 2 +- src/cmfd_solver.cpp | 2 +- src/distribution_angle.cpp | 3 +- src/distribution_energy.cpp | 2 +- src/eigenvalue.cpp | 5 +- src/endf.cpp | 3 +- src/finalize.cpp | 2 +- src/hdf5_interface.cpp | 3 +- src/material.cpp | 4 +- src/mesh.cpp | 8 +- src/mgxs.cpp | 5 +- src/nuclide.cpp | 3 +- src/output.cpp | 2 +- src/photon.cpp | 6 +- src/physics.cpp | 2 +- src/physics_mg.cpp | 2 +- src/plot.cpp | 3 +- src/scattdata.cpp | 3 +- src/secondary_correlated.cpp | 3 +- src/secondary_kalbach.cpp | 3 +- src/secondary_thermal.cpp | 2 +- src/simulation.cpp | 2 +- src/source.cpp | 2 +- src/state_point.cpp | 34 ++- src/tallies/filter_meshmaterial.cpp | 1 + src/tallies/tally.cpp | 4 +- src/thermal.cpp | 7 +- src/track_output.cpp | 2 +- src/volume_calc.cpp | 3 +- src/weight_windows.cpp | 56 +++-- src/xsdata.cpp | 335 ++++++++++++++++++-------- 54 files changed, 341 insertions(+), 250 deletions(-) diff --git a/.gitmodules b/.gitmodules index f84d09bb1f9..2bc12389436 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,12 +1,6 @@ [submodule "vendor/pugixml"] path = vendor/pugixml url = https://github.com/zeux/pugixml.git -[submodule "vendor/xtensor"] - path = vendor/xtensor - url = https://github.com/xtensor-stack/xtensor.git -[submodule "vendor/xtl"] - path = vendor/xtl - url = https://github.com/xtensor-stack/xtl.git [submodule "vendor/fmt"] path = vendor/fmt url = https://github.com/fmtlib/fmt.git diff --git a/CMakeLists.txt b/CMakeLists.txt index d3119fb8758..86720dd235a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -266,23 +266,6 @@ else() endif() endif() -#=============================================================================== -# xtensor header-only library -#=============================================================================== - -if(OPENMC_FORCE_VENDORED_LIBS) - add_subdirectory(vendor/xtl) - set(xtl_DIR ${CMAKE_CURRENT_BINARY_DIR}/vendor/xtl) - add_subdirectory(vendor/xtensor) -else() - find_package_write_status(xtensor) - if (NOT xtensor_FOUND) - add_subdirectory(vendor/xtl) - set(xtl_DIR ${CMAKE_CURRENT_BINARY_DIR}/vendor/xtl) - add_subdirectory(vendor/xtensor) - endif() -endif() - #=============================================================================== # Catch2 library #=============================================================================== @@ -496,7 +479,7 @@ endif() # target_link_libraries treats any arguments starting with - but not -l as # linker flags. Thus, we can pass both linker flags and libraries together. target_link_libraries(libopenmc ${ldflags} ${HDF5_LIBRARIES} ${HDF5_HL_LIBRARIES} - xtensor fmt::fmt ${CMAKE_DL_LIBS}) + fmt::fmt ${CMAKE_DL_LIBS}) if(TARGET pugixml::pugixml) target_link_libraries(libopenmc pugixml::pugixml) diff --git a/cmake/OpenMCConfig.cmake.in b/cmake/OpenMCConfig.cmake.in index 837a39c7833..cc837bba711 100644 --- a/cmake/OpenMCConfig.cmake.in +++ b/cmake/OpenMCConfig.cmake.in @@ -5,8 +5,6 @@ get_filename_component(_OPENMC_PREFIX "${OpenMC_CMAKE_DIR}/../../.." ABSOLUTE) find_package(fmt CONFIG REQUIRED HINTS ${_OPENMC_PREFIX}) find_package(pugixml CONFIG REQUIRED HINTS ${_OPENMC_PREFIX}) -find_package(xtl CONFIG REQUIRED HINTS ${_OPENMC_PREFIX}) -find_package(xtensor CONFIG REQUIRED HINTS ${_OPENMC_PREFIX}) if(@OPENMC_USE_DAGMC@) find_package(DAGMC REQUIRED HINTS @DAGMC_DIR@) endif() diff --git a/include/openmc/bremsstrahlung.h b/include/openmc/bremsstrahlung.h index 2f7e41bf087..21a7f3c069c 100644 --- a/include/openmc/bremsstrahlung.h +++ b/include/openmc/bremsstrahlung.h @@ -3,7 +3,7 @@ #include "openmc/particle.h" -#include "xtensor/xtensor.hpp" +#include "openmc/tensor.h" namespace openmc { diff --git a/include/openmc/distribution_energy.h b/include/openmc/distribution_energy.h index 9b08ed039dc..75c7dd7fecd 100644 --- a/include/openmc/distribution_energy.h +++ b/include/openmc/distribution_energy.h @@ -5,7 +5,7 @@ #define OPENMC_DISTRIBUTION_ENERGY_H #include "hdf5.h" -#include "xtensor/xtensor.hpp" +#include "openmc/tensor.h" #include "openmc/constants.h" #include "openmc/endf.h" diff --git a/include/openmc/eigenvalue.h b/include/openmc/eigenvalue.h index b456fee21eb..00e485bb0b5 100644 --- a/include/openmc/eigenvalue.h +++ b/include/openmc/eigenvalue.h @@ -6,7 +6,7 @@ #include // for int64_t -#include "xtensor/xtensor.hpp" +#include "openmc/tensor.h" #include #include "openmc/array.h" diff --git a/include/openmc/hdf5_interface.h b/include/openmc/hdf5_interface.h index 28b0d2b113d..38ea62c62fa 100644 --- a/include/openmc/hdf5_interface.h +++ b/include/openmc/hdf5_interface.h @@ -11,8 +11,7 @@ #include "hdf5.h" #include "hdf5_hl.h" -#include "xtensor/xadapt.hpp" -#include "xtensor/xarray.hpp" +#include "openmc/tensor.h" #include "openmc/array.h" #include "openmc/error.h" @@ -497,11 +496,12 @@ inline void write_dataset( } // Template for xarray, xtensor, etc. -template +template>::value>> inline void write_dataset( - hid_t obj_id, const char* name, const xt::xcontainer& arr) + hid_t obj_id, const char* name, const Container& arr) { - using T = typename D::value_type; + using T = typename std::decay_t::value_type; auto s = arr.shape(); vector dims {s.cbegin(), s.cend()}; write_dataset_lowlevel(obj_id, dims.size(), dims.data(), name, diff --git a/include/openmc/material.h b/include/openmc/material.h index c10f25551e8..2baf54deec8 100644 --- a/include/openmc/material.h +++ b/include/openmc/material.h @@ -6,7 +6,7 @@ #include "openmc/span.h" #include "pugixml.hpp" -#include "xtensor/xtensor.hpp" +#include "openmc/tensor.h" #include #include "openmc/bremsstrahlung.h" diff --git a/include/openmc/mesh.h b/include/openmc/mesh.h index 5dc327fe359..8764d757992 100644 --- a/include/openmc/mesh.h +++ b/include/openmc/mesh.h @@ -9,7 +9,7 @@ #include "hdf5.h" #include "pugixml.hpp" -#include "xtensor/xtensor.hpp" +#include "openmc/tensor.h" #include "openmc/bounding_box.h" #include "openmc/error.h" diff --git a/include/openmc/mgxs.h b/include/openmc/mgxs.h index 9b1602f299a..4779505d481 100644 --- a/include/openmc/mgxs.h +++ b/include/openmc/mgxs.h @@ -6,7 +6,7 @@ #include -#include "xtensor/xtensor.hpp" +#include "openmc/tensor.h" #include "openmc/constants.h" #include "openmc/hdf5_interface.h" diff --git a/include/openmc/photon.h b/include/openmc/photon.h index f6f28a4df1d..3078696fb57 100644 --- a/include/openmc/photon.h +++ b/include/openmc/photon.h @@ -6,7 +6,7 @@ #include "openmc/particle.h" #include "openmc/vector.h" -#include "xtensor/xtensor.hpp" +#include "openmc/tensor.h" #include #include diff --git a/include/openmc/plot.h b/include/openmc/plot.h index 7e27679eabb..861f400e3c9 100644 --- a/include/openmc/plot.h +++ b/include/openmc/plot.h @@ -7,7 +7,7 @@ #include #include "pugixml.hpp" -#include "xtensor/xarray.hpp" +#include "openmc/tensor.h" #include "hdf5.h" #include "openmc/cell.h" diff --git a/include/openmc/scattdata.h b/include/openmc/scattdata.h index a75ef09d97b..7794da85466 100644 --- a/include/openmc/scattdata.h +++ b/include/openmc/scattdata.h @@ -4,7 +4,7 @@ #ifndef OPENMC_SCATTDATA_H #define OPENMC_SCATTDATA_H -#include "xtensor/xtensor.hpp" +#include "openmc/tensor.h" #include "openmc/constants.h" #include "openmc/vector.h" diff --git a/include/openmc/secondary_correlated.h b/include/openmc/secondary_correlated.h index 6905c38e369..c50c32d206f 100644 --- a/include/openmc/secondary_correlated.h +++ b/include/openmc/secondary_correlated.h @@ -5,7 +5,7 @@ #define OPENMC_SECONDARY_CORRELATED_H #include "hdf5.h" -#include "xtensor/xtensor.hpp" +#include "openmc/tensor.h" #include "openmc/angle_energy.h" #include "openmc/distribution.h" diff --git a/include/openmc/secondary_kalbach.h b/include/openmc/secondary_kalbach.h index 83806d35248..5a0fbd662a7 100644 --- a/include/openmc/secondary_kalbach.h +++ b/include/openmc/secondary_kalbach.h @@ -5,7 +5,7 @@ #define OPENMC_SECONDARY_KALBACH_H #include "hdf5.h" -#include "xtensor/xtensor.hpp" +#include "openmc/tensor.h" #include "openmc/angle_energy.h" #include "openmc/constants.h" diff --git a/include/openmc/secondary_thermal.h b/include/openmc/secondary_thermal.h index 5b18902afbb..53ac721135d 100644 --- a/include/openmc/secondary_thermal.h +++ b/include/openmc/secondary_thermal.h @@ -9,7 +9,7 @@ #include "openmc/secondary_correlated.h" #include "openmc/vector.h" -#include "xtensor/xtensor.hpp" +#include "openmc/tensor.h" #include namespace openmc { diff --git a/include/openmc/tallies/tally.h b/include/openmc/tallies/tally.h index 374daff92a0..5e3426d825c 100644 --- a/include/openmc/tallies/tally.h +++ b/include/openmc/tallies/tally.h @@ -9,8 +9,7 @@ #include "openmc/vector.h" #include "pugixml.hpp" -#include "xtensor/xfixed.hpp" -#include "xtensor/xtensor.hpp" +#include "openmc/tensor.h" #include #include diff --git a/include/openmc/thermal.h b/include/openmc/thermal.h index de0767d0af0..86254b92314 100644 --- a/include/openmc/thermal.h +++ b/include/openmc/thermal.h @@ -5,7 +5,7 @@ #include #include -#include "xtensor/xtensor.hpp" +#include "openmc/tensor.h" #include "openmc/angle_energy.h" #include "openmc/endf.h" diff --git a/include/openmc/urr.h b/include/openmc/urr.h index 1e603715847..04881ca70c2 100644 --- a/include/openmc/urr.h +++ b/include/openmc/urr.h @@ -3,7 +3,7 @@ #ifndef OPENMC_URR_H #define OPENMC_URR_H -#include "xtensor/xtensor.hpp" +#include "openmc/tensor.h" #include "openmc/constants.h" #include "openmc/hdf5_interface.h" diff --git a/include/openmc/volume_calc.h b/include/openmc/volume_calc.h index fa8d3d65ece..dd75819e63f 100644 --- a/include/openmc/volume_calc.h +++ b/include/openmc/volume_calc.h @@ -13,7 +13,7 @@ #include "openmc/vector.h" #include "pugixml.hpp" -#include "xtensor/xtensor.hpp" +#include "openmc/tensor.h" #ifdef _OPENMP #include #endif diff --git a/include/openmc/wmp.h b/include/openmc/wmp.h index 6a4abd86714..052391f59c7 100644 --- a/include/openmc/wmp.h +++ b/include/openmc/wmp.h @@ -2,7 +2,7 @@ #define OPENMC_WMP_H #include "hdf5.h" -#include "xtensor/xtensor.hpp" +#include "openmc/tensor.h" #include #include diff --git a/include/openmc/xml_interface.h b/include/openmc/xml_interface.h index f49613ecde1..603f456a27b 100644 --- a/include/openmc/xml_interface.h +++ b/include/openmc/xml_interface.h @@ -6,8 +6,7 @@ #include #include "pugixml.hpp" -#include "xtensor/xadapt.hpp" -#include "xtensor/xarray.hpp" +#include "openmc/tensor.h" #include "openmc/position.h" #include "openmc/vector.h" diff --git a/include/openmc/xsdata.h b/include/openmc/xsdata.h index feafde68dd3..d66f4b3d045 100644 --- a/include/openmc/xsdata.h +++ b/include/openmc/xsdata.h @@ -4,7 +4,7 @@ #ifndef OPENMC_XSDATA_H #define OPENMC_XSDATA_H -#include "xtensor/xtensor.hpp" +#include "openmc/tensor.h" #include "openmc/hdf5_interface.h" #include "openmc/memory.h" diff --git a/src/bremsstrahlung.cpp b/src/bremsstrahlung.cpp index d77066fb0eb..5f8aee74cfd 100644 --- a/src/bremsstrahlung.cpp +++ b/src/bremsstrahlung.cpp @@ -6,7 +6,7 @@ #include "openmc/search.h" #include "openmc/settings.h" -#include "xtensor/xmath.hpp" +#include "openmc/tensor.h" namespace openmc { diff --git a/src/cmfd_solver.cpp b/src/cmfd_solver.cpp index 943042f67e7..cbf89085ed7 100644 --- a/src/cmfd_solver.cpp +++ b/src/cmfd_solver.cpp @@ -5,7 +5,7 @@ #ifdef _OPENMP #include #endif -#include "xtensor/xtensor.hpp" +#include "openmc/tensor.h" #include "openmc/bank.h" #include "openmc/capi.h" diff --git a/src/distribution_angle.cpp b/src/distribution_angle.cpp index 50f1aca112b..3f436f5058c 100644 --- a/src/distribution_angle.cpp +++ b/src/distribution_angle.cpp @@ -2,8 +2,7 @@ #include // for abs, copysign -#include "xtensor/xarray.hpp" -#include "xtensor/xview.hpp" +#include "openmc/tensor.h" #include "openmc/endf.h" #include "openmc/hdf5_interface.h" diff --git a/src/distribution_energy.cpp b/src/distribution_energy.cpp index a4a5ce9e1b6..19d7944f6e6 100644 --- a/src/distribution_energy.cpp +++ b/src/distribution_energy.cpp @@ -4,7 +4,7 @@ #include // for size_t #include // for back_inserter -#include "xtensor/xview.hpp" +#include "openmc/tensor.h" #include "openmc/endf.h" #include "openmc/hdf5_interface.h" diff --git a/src/eigenvalue.cpp b/src/eigenvalue.cpp index dc4fbb420b8..ac5b6f84f5d 100644 --- a/src/eigenvalue.cpp +++ b/src/eigenvalue.cpp @@ -1,9 +1,6 @@ #include "openmc/eigenvalue.h" -#include "xtensor/xbuilder.hpp" -#include "xtensor/xmath.hpp" -#include "xtensor/xtensor.hpp" -#include "xtensor/xview.hpp" +#include "openmc/tensor.h" #include "openmc/array.h" #include "openmc/bank.h" diff --git a/src/endf.cpp b/src/endf.cpp index c0c1d2e7e8b..72b7c7cc2a3 100644 --- a/src/endf.cpp +++ b/src/endf.cpp @@ -5,8 +5,7 @@ #include // for back_inserter #include // for runtime_error -#include "xtensor/xarray.hpp" -#include "xtensor/xview.hpp" +#include "openmc/tensor.h" #include "openmc/array.h" #include "openmc/constants.h" diff --git a/src/finalize.cpp b/src/finalize.cpp index 344eaa1a0a7..fa1f2e3ebf5 100644 --- a/src/finalize.cpp +++ b/src/finalize.cpp @@ -29,7 +29,7 @@ #include "openmc/volume_calc.h" #include "openmc/weight_windows.h" -#include "xtensor/xview.hpp" +#include "openmc/tensor.h" namespace openmc { diff --git a/src/hdf5_interface.cpp b/src/hdf5_interface.cpp index c56d485e281..23905e17b8b 100644 --- a/src/hdf5_interface.cpp +++ b/src/hdf5_interface.cpp @@ -4,8 +4,7 @@ #include #include -#include "xtensor/xarray.hpp" -#include "xtensor/xtensor.hpp" +#include "openmc/tensor.h" #include #include "hdf5.h" diff --git a/src/material.cpp b/src/material.cpp index 072e6decad1..944fa87f5ed 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -8,9 +8,7 @@ #include #include -#include "xtensor/xbuilder.hpp" -#include "xtensor/xoperation.hpp" -#include "xtensor/xview.hpp" +#include "openmc/tensor.h" #include "openmc/capi.h" #include "openmc/container_util.h" diff --git a/src/mesh.cpp b/src/mesh.cpp index acd90033f32..737986f87af 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -16,13 +16,7 @@ #include "mpi.h" #endif -#include "xtensor/xadapt.hpp" -#include "xtensor/xbuilder.hpp" -#include "xtensor/xeval.hpp" -#include "xtensor/xmath.hpp" -#include "xtensor/xsort.hpp" -#include "xtensor/xtensor.hpp" -#include "xtensor/xview.hpp" +#include "openmc/tensor.h" #include // for fmt #include "openmc/capi.h" diff --git a/src/mgxs.cpp b/src/mgxs.cpp index a2c479f2156..01a77d06615 100644 --- a/src/mgxs.cpp +++ b/src/mgxs.cpp @@ -5,10 +5,7 @@ #include #include -#include "xtensor/xadapt.hpp" -#include "xtensor/xmath.hpp" -#include "xtensor/xsort.hpp" -#include "xtensor/xview.hpp" +#include "openmc/tensor.h" #include #include "openmc/error.h" diff --git a/src/nuclide.cpp b/src/nuclide.cpp index 69e603a7c66..61e64571785 100644 --- a/src/nuclide.cpp +++ b/src/nuclide.cpp @@ -17,8 +17,7 @@ #include -#include "xtensor/xbuilder.hpp" -#include "xtensor/xview.hpp" +#include "openmc/tensor.h" #include // for sort, min_element #include diff --git a/src/output.cpp b/src/output.cpp index ae2daaffc14..8f9802bb9bc 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -17,7 +17,7 @@ #ifdef _OPENMP #include #endif -#include "xtensor/xview.hpp" +#include "openmc/tensor.h" #include "openmc/capi.h" #include "openmc/cell.h" diff --git a/src/photon.cpp b/src/photon.cpp index 951acb9fbd8..9ad282e78db 100644 --- a/src/photon.cpp +++ b/src/photon.cpp @@ -13,11 +13,7 @@ #include "openmc/search.h" #include "openmc/settings.h" -#include "xtensor/xbuilder.hpp" -#include "xtensor/xmath.hpp" -#include "xtensor/xoperation.hpp" -#include "xtensor/xslice.hpp" -#include "xtensor/xview.hpp" +#include "openmc/tensor.h" #include #include diff --git a/src/physics.cpp b/src/physics.cpp index 1f72e4fac07..a6b0e7dfb27 100644 --- a/src/physics.cpp +++ b/src/physics.cpp @@ -32,7 +32,7 @@ #include // for max, min, max_element #include // for sqrt, exp, log, abs, copysign -#include +#include "openmc/tensor.h" namespace openmc { diff --git a/src/physics_mg.cpp b/src/physics_mg.cpp index 58a21a67895..32ae10a6092 100644 --- a/src/physics_mg.cpp +++ b/src/physics_mg.cpp @@ -2,7 +2,7 @@ #include -#include "xtensor/xarray.hpp" +#include "openmc/tensor.h" #include #include "openmc/bank.h" diff --git a/src/plot.cpp b/src/plot.cpp index 2cadc48cefe..0914e4e273d 100644 --- a/src/plot.cpp +++ b/src/plot.cpp @@ -7,8 +7,7 @@ #include #include -#include "xtensor/xmanipulation.hpp" -#include "xtensor/xview.hpp" +#include "openmc/tensor.h" #include #include #ifdef USE_LIBPNG diff --git a/src/scattdata.cpp b/src/scattdata.cpp index 21b18cbd923..2f3e9dfdb4b 100644 --- a/src/scattdata.cpp +++ b/src/scattdata.cpp @@ -4,8 +4,7 @@ #include #include -#include "xtensor/xbuilder.hpp" -#include "xtensor/xview.hpp" +#include "openmc/tensor.h" #include "openmc/constants.h" #include "openmc/error.h" diff --git a/src/secondary_correlated.cpp b/src/secondary_correlated.cpp index e820419b484..15c6b0b3d5c 100644 --- a/src/secondary_correlated.cpp +++ b/src/secondary_correlated.cpp @@ -5,8 +5,7 @@ #include // for size_t #include // for back_inserter -#include "xtensor/xarray.hpp" -#include "xtensor/xview.hpp" +#include "openmc/tensor.h" #include "openmc/endf.h" #include "openmc/hdf5_interface.h" diff --git a/src/secondary_kalbach.cpp b/src/secondary_kalbach.cpp index 6ac91e665b9..213195a3186 100644 --- a/src/secondary_kalbach.cpp +++ b/src/secondary_kalbach.cpp @@ -5,8 +5,7 @@ #include // for size_t #include // for back_inserter -#include "xtensor/xarray.hpp" -#include "xtensor/xview.hpp" +#include "openmc/tensor.h" #include "openmc/hdf5_interface.h" #include "openmc/math_functions.h" diff --git a/src/secondary_thermal.cpp b/src/secondary_thermal.cpp index 030d398aabe..28c19fcae77 100644 --- a/src/secondary_thermal.cpp +++ b/src/secondary_thermal.cpp @@ -5,7 +5,7 @@ #include "openmc/random_lcg.h" #include "openmc/search.h" -#include "xtensor/xview.hpp" +#include "openmc/tensor.h" #include #include // for log, exp diff --git a/src/simulation.cpp b/src/simulation.cpp index 18e40a8bc73..5a580d83e72 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -30,7 +30,7 @@ #ifdef _OPENMP #include #endif -#include "xtensor/xview.hpp" +#include "openmc/tensor.h" #ifdef OPENMC_MPI #include diff --git a/src/source.cpp b/src/source.cpp index 8bd9c789352..8ce544d1a22 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -10,7 +10,7 @@ #include // for dlopen, dlsym, dlclose, dlerror #endif -#include "xtensor/xadapt.hpp" +#include "openmc/tensor.h" #include #include "openmc/bank.h" diff --git a/src/state_point.cpp b/src/state_point.cpp index 455299529b3..f970d33b6ef 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -4,8 +4,7 @@ #include // for int64_t #include -#include "xtensor/xbuilder.hpp" // for empty_like -#include "xtensor/xview.hpp" +#include "openmc/tensor.h" #include #include "openmc/bank.h" @@ -918,13 +917,19 @@ void write_tally_results_nr(hid_t file_id) write_attribute(file_id, "tallies_present", 1); } - // Get view of accumulated tally values - auto values_view = xt::view(t->results_, xt::all(), xt::all(), - xt::range(static_cast(TallyResult::SUM), - static_cast(TallyResult::SUM_SQ) + 1)); + // Extract sub-range indices for SUM and SUM_SQ + std::size_t k_start = static_cast(TallyResult::SUM); + std::size_t k_end = static_cast(TallyResult::SUM_SQ) + 1; + std::size_t n_k = k_end - k_start; + std::size_t dim0 = t->results_.shape(0); + std::size_t dim1 = t->results_.shape(1); - // Make copy of tally values in contiguous array - xt::xtensor values = values_view; + // Make copy of accumulated tally values in contiguous array + xt::xtensor values({dim0, dim1, n_k}); + for (std::size_t i = 0; i < dim0; ++i) + for (std::size_t j = 0; j < dim1; ++j) + for (std::size_t k = 0; k < n_k; ++k) + values(i, j, k) = t->results_(i, j, k_start + k); if (mpi::master) { // Open group for tally @@ -942,15 +947,18 @@ void write_tally_results_nr(hid_t file_id) // regular TallyResults array if (simulation::current_batch == settings::n_max_batches || simulation::satisfy_triggers) { - values_view = values; + for (std::size_t i = 0; i < dim0; ++i) + for (std::size_t j = 0; j < dim1; ++j) + for (std::size_t k = 0; k < n_k; ++k) + t->results_(i, j, k_start + k) = values(i, j, k); } // Put in temporary tally result xt::xtensor results_copy = xt::zeros_like(t->results_); - auto copy_view = xt::view(results_copy, xt::all(), xt::all(), - xt::range(static_cast(TallyResult::SUM), - static_cast(TallyResult::SUM_SQ) + 1)); - copy_view = values; + for (std::size_t i = 0; i < dim0; ++i) + for (std::size_t j = 0; j < dim1; ++j) + for (std::size_t k = 0; k < n_k; ++k) + results_copy(i, j, k_start + k) = values(i, j, k); // Write reduced tally results to file auto shape = results_copy.shape(); diff --git a/src/tallies/filter_meshmaterial.cpp b/src/tallies/filter_meshmaterial.cpp index 6e1f30380f6..60d9849e9a6 100644 --- a/src/tallies/filter_meshmaterial.cpp +++ b/src/tallies/filter_meshmaterial.cpp @@ -1,5 +1,6 @@ #include "openmc/tallies/filter_meshmaterial.h" +#include #include // for move #include diff --git a/src/tallies/tally.cpp b/src/tallies/tally.cpp index 8cdf3a9050c..d00f3e28004 100644 --- a/src/tallies/tally.cpp +++ b/src/tallies/tally.cpp @@ -35,9 +35,7 @@ #include "openmc/tallies/filter_time.h" #include "openmc/xml_interface.h" -#include "xtensor/xadapt.hpp" -#include "xtensor/xbuilder.hpp" // for empty_like -#include "xtensor/xview.hpp" +#include "openmc/tensor.h" #include #include // for max, set_union diff --git a/src/thermal.cpp b/src/thermal.cpp index cbe0983ed65..6758fa40b6a 100644 --- a/src/thermal.cpp +++ b/src/thermal.cpp @@ -3,12 +3,7 @@ #include // for sort, move, min, max, find #include // for round, sqrt, abs -#include "xtensor/xarray.hpp" -#include "xtensor/xbuilder.hpp" -#include "xtensor/xmath.hpp" -#include "xtensor/xsort.hpp" -#include "xtensor/xtensor.hpp" -#include "xtensor/xview.hpp" +#include "openmc/tensor.h" #include #include "openmc/constants.h" diff --git a/src/track_output.cpp b/src/track_output.cpp index e86f774f046..02e09223645 100644 --- a/src/track_output.cpp +++ b/src/track_output.cpp @@ -8,7 +8,7 @@ #include "openmc/simulation.h" #include "openmc/vector.h" -#include "xtensor/xtensor.hpp" +#include "openmc/tensor.h" #include #include diff --git a/src/volume_calc.cpp b/src/volume_calc.cpp index 1deffb80488..c4967c14b63 100644 --- a/src/volume_calc.cpp +++ b/src/volume_calc.cpp @@ -17,8 +17,7 @@ #include "openmc/timer.h" #include "openmc/xml_interface.h" -#include "xtensor/xadapt.hpp" -#include "xtensor/xview.hpp" +#include "openmc/tensor.h" #include #include // for copy diff --git a/src/weight_windows.cpp b/src/weight_windows.cpp index 5ca4addbbf7..ed07f47489e 100644 --- a/src/weight_windows.cpp +++ b/src/weight_windows.cpp @@ -6,12 +6,7 @@ #include #include -#include "xtensor/xdynamic_view.hpp" -#include "xtensor/xindex_view.hpp" -#include "xtensor/xio.hpp" -#include "xtensor/xmasked_view.hpp" -#include "xtensor/xnoalias.hpp" -#include "xtensor/xview.hpp" +#include "openmc/tensor.h" #include "openmc/error.h" #include "openmc/file_utils.h" @@ -588,13 +583,6 @@ void WeightWindows::update_weights(const Tally* tally, const std::string& value, std::find(filter_types.begin(), filter_types.end(), FilterType::MESH) - filter_types.begin(); - // get a fully reshaped view of the tally according to tally ordering of - // filters - auto tally_values = xt::reshape_view(results_arr, shape); - - // get a that is (particle, energy, mesh, scores, values) - auto transposed_view = xt::transpose(tally_values, transpose); - // determine the dimension and index of the particle data int particle_idx = 0; if (tally->has_filter(FilterType::PARTICLE)) { @@ -618,13 +606,41 @@ void WeightWindows::update_weights(const Tally* tally, const std::string& value, particle_idx = p_it - particles.begin(); } - // down-select data based on particle and score - auto sum = xt::dynamic_view( - transposed_view, {particle_idx, xt::all(), xt::all(), score_index, - static_cast(TallyResult::SUM)}); - auto sum_sq = xt::dynamic_view( - transposed_view, {particle_idx, xt::all(), xt::all(), score_index, - static_cast(TallyResult::SUM_SQ)}); + // Compute strides for the reshaped 5D layout (filter dims, scores, results) + // shape = {f0_bins, f1_bins, f2_bins, n_scores, results_dim} + std::array strides; + strides[4] = 1; + for (int i = 3; i >= 0; --i) + strides[i] = strides[i + 1] * shape[i + 1]; + + // Helper lambda: compute flat index from logical (particle, energy, mesh) + // into results_arr, accounting for the transpose mapping + auto get_result = [&](int p, int e, int m, int score, int result_type) + -> double { + // Map logical indices to physical filter positions + std::array idx = {0, 0, 0, score, result_type}; + idx[transpose[0]] = p; + idx[transpose[1]] = e; + idx[transpose[2]] = m; + // Compute flat filter_bin from the first 3 filter dimensions + int filter_bin = idx[0] * shape[1] * shape[2] + idx[1] * shape[2] + idx[2]; + return results_arr(filter_bin, score, result_type); + }; + + // Extract 2D sum and sum_sq arrays for (energy, mesh) at given particle and + // score + int sum_idx = static_cast(TallyResult::SUM); + int sum_sq_idx = static_cast(TallyResult::SUM_SQ); + xt::xtensor sum({static_cast(e_bins), + static_cast(mesh_bins)}); + xt::xtensor sum_sq({static_cast(e_bins), + static_cast(mesh_bins)}); + for (int e = 0; e < e_bins; e++) { + for (int64_t m = 0; m < mesh_bins; m++) { + sum(e, m) = get_result(particle_idx, e, m, score_index, sum_idx); + sum_sq(e, m) = get_result(particle_idx, e, m, score_index, sum_sq_idx); + } + } int n = tally->n_realizations_; ////////////////////////////////////////////// diff --git a/src/xsdata.cpp b/src/xsdata.cpp index 1929f51e6fa..6317a1254c5 100644 --- a/src/xsdata.cpp +++ b/src/xsdata.cpp @@ -5,10 +5,7 @@ #include #include -#include "xtensor/xbuilder.hpp" -#include "xtensor/xindex_view.hpp" -#include "xtensor/xmath.hpp" -#include "xtensor/xview.hpp" +#include "openmc/tensor.h" #include "openmc/constants.h" #include "openmc/error.h" @@ -102,7 +99,9 @@ void XsData::from_hdf5(hid_t xsdata_grp, bool fissionable, // Check absorption to ensure it is not 0 since it is often the // denominator in tally methods - xt::filtration(absorption, xt::equal(absorption, 0.)) = 1.e-10; + for (size_t i = 0; i < absorption.size(); ++i) + if (absorption.data()[i] == 0.) + absorption.data()[i] = 1.e-10; // Get or calculate the total x/s if (object_exists(xsdata_grp, "total")) { @@ -116,7 +115,9 @@ void XsData::from_hdf5(hid_t xsdata_grp, bool fissionable, } // Fix if total is 0, since it is in the denominator when tallying - xt::filtration(total, xt::equal(total, 0.)) = 1.e-10; + for (size_t i = 0; i < total.size(); ++i) + if (total.data()[i] == 0.) + total.data()[i] = 1.e-10; } //============================================================================== @@ -131,13 +132,25 @@ void XsData::fission_vector_beta_from_hdf5( read_nd_vector(xsdata_grp, "chi", temp_chi, true); // Normalize chi by summing over the outgoing groups for each incoming angle - temp_chi /= xt::view(xt::sum(temp_chi, {1}), xt::all(), xt::newaxis()); + for (size_t a = 0; a < n_ang; ++a) { + double row_sum = 0.0; + for (size_t g = 0; g < n_g_; ++g) + row_sum += temp_chi(a, g); + for (size_t g = 0; g < n_g_; ++g) + temp_chi(a, g) /= row_sum; + } // Now every incoming group in prompt_chi and delayed_chi is the normalized - // chi we just made - chi_prompt = xt::view(temp_chi, xt::all(), xt::newaxis(), xt::all()); - chi_delayed = - xt::view(temp_chi, xt::all(), xt::newaxis(), xt::newaxis(), xt::all()); + // chi we just made (broadcast 2D -> 3D and 2D -> 4D) + for (size_t a = 0; a < n_ang; ++a) + for (size_t gin = 0; gin < n_g_; ++gin) + for (size_t gout = 0; gout < n_g_; ++gout) + chi_prompt(a, gin, gout) = temp_chi(a, gout); + for (size_t a = 0; a < n_ang; ++a) + for (size_t d = 0; d < n_dg_; ++d) + for (size_t gin = 0; gin < n_g_; ++gin) + for (size_t gout = 0; gout < n_g_; ++gout) + chi_delayed(a, d, gin, gout) = temp_chi(a, gout); // Get nu-fission xt::xtensor temp_nufiss({n_ang, n_g_}, 0.); @@ -155,22 +168,41 @@ void XsData::fission_vector_beta_from_hdf5( read_nd_vector(xsdata_grp, "beta", temp_beta, true); // Set prompt_nu_fission = (1. - beta_total)*nu_fission - prompt_nu_fission = temp_nufiss * (1. - xt::sum(temp_beta, {1})); + // beta_total = sum over delayed groups (axis 1) of temp_beta + for (size_t a = 0; a < n_ang; ++a) { + double beta_total = 0.0; + for (size_t d = 0; d < n_dg_; ++d) + beta_total += temp_beta(a, d); + for (size_t g = 0; g < n_g_; ++g) + prompt_nu_fission(a, g) = temp_nufiss(a, g) * (1.0 - beta_total); + } // Set delayed_nu_fission as beta * nu_fission - delayed_nu_fission = - xt::view(temp_beta, xt::all(), xt::all(), xt::newaxis()) * - xt::view(temp_nufiss, xt::all(), xt::newaxis(), xt::all()); + // delayed_nu_fission(a, d, g) = temp_beta(a, d) * temp_nufiss(a, g) + for (size_t a = 0; a < n_ang; ++a) + for (size_t d = 0; d < n_dg_; ++d) + for (size_t g = 0; g < n_g_; ++g) + delayed_nu_fission(a, d, g) = temp_beta(a, d) * temp_nufiss(a, g); } else if (beta_ndims == ndim_target + 1) { xt::xtensor temp_beta({n_ang, n_dg_, n_g_}, 0.); read_nd_vector(xsdata_grp, "beta", temp_beta, true); // Set prompt_nu_fission = (1. - beta_total)*nu_fission - prompt_nu_fission = temp_nufiss * (1. - xt::sum(temp_beta, {1})); + // beta_total = sum over delayed groups (axis 1) of 3D temp_beta + for (size_t a = 0; a < n_ang; ++a) + for (size_t g = 0; g < n_g_; ++g) { + double beta_total = 0.0; + for (size_t d = 0; d < n_dg_; ++d) + beta_total += temp_beta(a, d, g); + prompt_nu_fission(a, g) = temp_nufiss(a, g) * (1.0 - beta_total); + } // Set delayed_nu_fission as beta * nu_fission - delayed_nu_fission = - temp_beta * xt::view(temp_nufiss, xt::all(), xt::newaxis(), xt::all()); + // delayed_nu_fission(a, d, g) = temp_beta(a, d, g) * temp_nufiss(a, g) + for (size_t a = 0; a < n_ang; ++a) + for (size_t d = 0; d < n_dg_; ++d) + for (size_t g = 0; g < n_g_; ++g) + delayed_nu_fission(a, d, g) = temp_beta(a, d, g) * temp_nufiss(a, g); } } @@ -183,21 +215,39 @@ void XsData::fission_vector_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) read_nd_vector(xsdata_grp, "chi-prompt", temp_chi_p, true); // Normalize chi by summing over the outgoing groups for each incoming angle - temp_chi_p /= xt::view(xt::sum(temp_chi_p, {1}), xt::all(), xt::newaxis()); + for (size_t a = 0; a < n_ang; ++a) { + double row_sum = 0.0; + for (size_t g = 0; g < n_g_; ++g) + row_sum += temp_chi_p(a, g); + for (size_t g = 0; g < n_g_; ++g) + temp_chi_p(a, g) /= row_sum; + } // Get chi-delayed xt::xtensor temp_chi_d({n_ang, n_dg_, n_g_}, 0.); read_nd_vector(xsdata_grp, "chi-delayed", temp_chi_d, true); - // Normalize chi by summing over the outgoing groups for each incoming angle - temp_chi_d /= - xt::view(xt::sum(temp_chi_d, {2}), xt::all(), xt::all(), xt::newaxis()); + // Normalize chi by summing over the outgoing groups for each delayed group + for (size_t a = 0; a < n_ang; ++a) + for (size_t d = 0; d < n_dg_; ++d) { + double grp_sum = 0.0; + for (size_t g = 0; g < n_g_; ++g) + grp_sum += temp_chi_d(a, d, g); + for (size_t g = 0; g < n_g_; ++g) + temp_chi_d(a, d, g) /= grp_sum; + } // Now assign the prompt and delayed chis by replicating for each incoming - // group - chi_prompt = xt::view(temp_chi_p, xt::all(), xt::newaxis(), xt::all()); - chi_delayed = - xt::view(temp_chi_d, xt::all(), xt::all(), xt::newaxis(), xt::all()); + // group (broadcast 2D -> 3D and 3D -> 4D) + for (size_t a = 0; a < n_ang; ++a) + for (size_t gin = 0; gin < n_g_; ++gin) + for (size_t gout = 0; gout < n_g_; ++gout) + chi_prompt(a, gin, gout) = temp_chi_p(a, gout); + for (size_t a = 0; a < n_ang; ++a) + for (size_t d = 0; d < n_dg_; ++d) + for (size_t gin = 0; gin < n_g_; ++gin) + for (size_t gout = 0; gout < n_g_; ++gout) + chi_delayed(a, d, gin, gout) = temp_chi_d(a, d, gout); // Get prompt and delayed nu-fission directly read_nd_vector(xsdata_grp, "prompt-nu-fission", prompt_nu_fission, true); @@ -214,10 +264,20 @@ void XsData::fission_vector_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang) read_nd_vector(xsdata_grp, "chi", temp_chi, true); // Normalize chi by summing over the outgoing groups for each incoming angle - temp_chi /= xt::view(xt::sum(temp_chi, {1}), xt::all(), xt::newaxis()); + for (size_t a = 0; a < n_ang; ++a) { + double row_sum = 0.0; + for (size_t g = 0; g < n_g_; ++g) + row_sum += temp_chi(a, g); + for (size_t g = 0; g < n_g_; ++g) + temp_chi(a, g) /= row_sum; + } // Now every incoming group in self.chi is the normalized chi we just made - chi_prompt = xt::view(temp_chi, xt::all(), xt::newaxis(), xt::all()); + // (broadcast 2D -> 3D) + for (size_t a = 0; a < n_ang; ++a) + for (size_t gin = 0; gin < n_g_; ++gin) + for (size_t gout = 0; gout < n_g_; ++gout) + chi_prompt(a, gin, gout) = temp_chi(a, gout); // Get nu-fission directly read_nd_vector(xsdata_grp, "nu-fission", prompt_nu_fission, true); @@ -245,62 +305,92 @@ void XsData::fission_matrix_beta_from_hdf5( xt::xtensor temp_beta({n_ang, n_dg_}, 0.); read_nd_vector(xsdata_grp, "beta", temp_beta, true); - xt::xtensor temp_beta_sum({n_ang}, 0.); - temp_beta_sum = xt::sum(temp_beta, {1}); - - // prompt_nu_fission is the sum of this matrix over outgoing groups and - // multiplied by (1 - beta_sum) - prompt_nu_fission = xt::sum(temp_matrix, {2}) * (1. - temp_beta_sum); - - // Store chi-prompt - chi_prompt = - xt::view(1.0 - temp_beta_sum, xt::all(), xt::newaxis(), xt::newaxis()) * - temp_matrix; - - // delayed_nu_fission is the sum of this matrix over outgoing groups and - // multiplied by beta - delayed_nu_fission = - xt::view(temp_beta, xt::all(), xt::all(), xt::newaxis()) * - xt::view(xt::sum(temp_matrix, {2}), xt::all(), xt::newaxis(), xt::all()); - - // Store chi-delayed - chi_delayed = - xt::view(temp_beta, xt::all(), xt::all(), xt::newaxis(), xt::newaxis()) * - xt::view(temp_matrix, xt::all(), xt::newaxis(), xt::all(), xt::all()); + // temp_beta_sum(a) = sum over delayed groups of temp_beta(a, d) + auto temp_beta_sum = xt::sum(temp_beta, {1}); + // matrix_sum(a, gin) = sum over outgoing groups of temp_matrix(a, gin, gout) + auto matrix_sum = xt::sum(temp_matrix, {2}); + + // prompt_nu_fission(a, g) = matrix_sum(a, g) * (1 - beta_sum(a)) + for (size_t a = 0; a < n_ang; ++a) + for (size_t g = 0; g < n_g_; ++g) + prompt_nu_fission(a, g) = matrix_sum(a, g) * (1.0 - temp_beta_sum(a)); + + // chi_prompt(a, gin, gout) = (1 - beta_sum(a)) * matrix(a, gin, gout) + for (size_t a = 0; a < n_ang; ++a) + for (size_t gin = 0; gin < n_g_; ++gin) + for (size_t gout = 0; gout < n_g_; ++gout) + chi_prompt(a, gin, gout) = + (1.0 - temp_beta_sum(a)) * temp_matrix(a, gin, gout); + + // delayed_nu_fission(a, d, g) = beta(a, d) * matrix_sum(a, g) + for (size_t a = 0; a < n_ang; ++a) + for (size_t d = 0; d < n_dg_; ++d) + for (size_t g = 0; g < n_g_; ++g) + delayed_nu_fission(a, d, g) = temp_beta(a, d) * matrix_sum(a, g); + + // chi_delayed(a, d, gin, gout) = beta(a, d) * matrix(a, gin, gout) + for (size_t a = 0; a < n_ang; ++a) + for (size_t d = 0; d < n_dg_; ++d) + for (size_t gin = 0; gin < n_g_; ++gin) + for (size_t gout = 0; gout < n_g_; ++gout) + chi_delayed(a, d, gin, gout) = + temp_beta(a, d) * temp_matrix(a, gin, gout); } else if (beta_ndims == ndim_target + 1) { xt::xtensor temp_beta({n_ang, n_dg_, n_g_}, 0.); read_nd_vector(xsdata_grp, "beta", temp_beta, true); - xt::xtensor temp_beta_sum({n_ang, n_g_}, 0.); - temp_beta_sum = xt::sum(temp_beta, {1}); - - // prompt_nu_fission is the sum of this matrix over outgoing groups and - // multiplied by (1 - beta_sum) - prompt_nu_fission = xt::sum(temp_matrix, {2}) * (1. - temp_beta_sum); - - // Store chi-prompt - chi_prompt = - xt::view(1.0 - temp_beta_sum, xt::all(), xt::all(), xt::newaxis()) * - temp_matrix; - - // delayed_nu_fission is the sum of this matrix over outgoing groups and - // multiplied by beta - delayed_nu_fission = temp_beta * xt::view(xt::sum(temp_matrix, {2}), - xt::all(), xt::newaxis(), xt::all()); - - // Store chi-delayed - chi_delayed = - xt::view(temp_beta, xt::all(), xt::all(), xt::all(), xt::newaxis()) * - xt::view(temp_matrix, xt::all(), xt::newaxis(), xt::all(), xt::all()); + // temp_beta_sum(a, g) = sum over delayed groups of temp_beta(a, d, g) + auto temp_beta_sum = xt::sum(temp_beta, {1}); + auto matrix_sum = xt::sum(temp_matrix, {2}); + + // prompt_nu_fission(a, g) = matrix_sum(a, g) * (1 - beta_sum(a, g)) + for (size_t a = 0; a < n_ang; ++a) + for (size_t g = 0; g < n_g_; ++g) + prompt_nu_fission(a, g) = matrix_sum(a, g) * (1.0 - temp_beta_sum(a, g)); + + // chi_prompt(a, gin, gout) = (1 - beta_sum(a, gin)) * matrix(a, gin, gout) + for (size_t a = 0; a < n_ang; ++a) + for (size_t gin = 0; gin < n_g_; ++gin) + for (size_t gout = 0; gout < n_g_; ++gout) + chi_prompt(a, gin, gout) = + (1.0 - temp_beta_sum(a, gin)) * temp_matrix(a, gin, gout); + + // delayed_nu_fission(a, d, g) = beta(a, d, g) * matrix_sum(a, g) + for (size_t a = 0; a < n_ang; ++a) + for (size_t d = 0; d < n_dg_; ++d) + for (size_t g = 0; g < n_g_; ++g) + delayed_nu_fission(a, d, g) = temp_beta(a, d, g) * matrix_sum(a, g); + + // chi_delayed(a, d, gin, gout) = beta(a, d, gin) * matrix(a, gin, gout) + for (size_t a = 0; a < n_ang; ++a) + for (size_t d = 0; d < n_dg_; ++d) + for (size_t gin = 0; gin < n_g_; ++gin) + for (size_t gout = 0; gout < n_g_; ++gout) + chi_delayed(a, d, gin, gout) = + temp_beta(a, d, gin) * temp_matrix(a, gin, gout); } - // Normalize both chis - chi_prompt /= - xt::view(xt::sum(chi_prompt, {2}), xt::all(), xt::all(), xt::newaxis()); + // Normalize both chis: chi_prompt(a, gin, gout) /= sum_over_gout + for (size_t a = 0; a < n_ang; ++a) + for (size_t gin = 0; gin < n_g_; ++gin) { + double s = 0.0; + for (size_t gout = 0; gout < n_g_; ++gout) + s += chi_prompt(a, gin, gout); + for (size_t gout = 0; gout < n_g_; ++gout) + chi_prompt(a, gin, gout) /= s; + } - chi_delayed /= xt::view( - xt::sum(chi_delayed, {3}), xt::all(), xt::all(), xt::all(), xt::newaxis()); + // chi_delayed(a, d, gin, gout) /= sum_over_gout + for (size_t a = 0; a < n_ang; ++a) + for (size_t d = 0; d < n_dg_; ++d) + for (size_t gin = 0; gin < n_g_; ++gin) { + double s = 0.0; + for (size_t gout = 0; gout < n_g_; ++gout) + s += chi_delayed(a, d, gin, gout); + for (size_t gout = 0; gout < n_g_; ++gout) + chi_delayed(a, d, gin, gout) /= s; + } } void XsData::fission_matrix_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) @@ -314,10 +404,12 @@ void XsData::fission_matrix_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) // prompt_nu_fission is the sum over outgoing groups prompt_nu_fission = xt::sum(temp_matrix_p, {2}); - // chi_prompt is this matrix but normalized over outgoing groups, which we - // have already stored in prompt_nu_fission - chi_prompt = temp_matrix_p / - xt::view(prompt_nu_fission, xt::all(), xt::all(), xt::newaxis()); + // chi_prompt = matrix / prompt_nu_fission (broadcast over gout) + for (size_t a = 0; a < n_ang; ++a) + for (size_t gin = 0; gin < n_g_; ++gin) + for (size_t gout = 0; gout < n_g_; ++gout) + chi_prompt(a, gin, gout) = + temp_matrix_p(a, gin, gout) / prompt_nu_fission(a, gin); // Get the delayed nu-fission matrix xt::xtensor temp_matrix_d({n_ang, n_dg_, n_g_, n_g_}, 0.); @@ -326,10 +418,13 @@ void XsData::fission_matrix_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) // delayed_nu_fission is the sum over outgoing groups delayed_nu_fission = xt::sum(temp_matrix_d, {3}); - // chi_prompt is this matrix but normalized over outgoing groups, which we - // have already stored in prompt_nu_fission - chi_delayed = temp_matrix_d / xt::view(delayed_nu_fission, xt::all(), - xt::all(), xt::all(), xt::newaxis()); + // chi_delayed = matrix / delayed_nu_fission (broadcast over gout) + for (size_t a = 0; a < n_ang; ++a) + for (size_t d = 0; d < n_dg_; ++d) + for (size_t gin = 0; gin < n_g_; ++gin) + for (size_t gout = 0; gout < n_g_; ++gout) + chi_delayed(a, d, gin, gout) = + temp_matrix_d(a, d, gin, gout) / delayed_nu_fission(a, d, gin); } void XsData::fission_matrix_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang) @@ -344,10 +439,12 @@ void XsData::fission_matrix_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang) // prompt_nu_fission is the sum over outgoing groups prompt_nu_fission = xt::sum(temp_matrix, {2}); - // chi_prompt is this matrix but normalized over outgoing groups, which we - // have already stored in prompt_nu_fission - chi_prompt = temp_matrix / - xt::view(prompt_nu_fission, xt::all(), xt::all(), xt::newaxis()); + // chi_prompt = matrix / prompt_nu_fission (broadcast over gout) + for (size_t a = 0; a < n_ang; ++a) + for (size_t gin = 0; gin < n_g_; ++gin) + for (size_t gout = 0; gout < n_g_; ++gout) + chi_prompt(a, gin, gout) = + temp_matrix(a, gin, gout) / prompt_nu_fission(a, gin); } //============================================================================== @@ -388,7 +485,12 @@ void XsData::fission_from_hdf5( if (n_dg_ == 0) { nu_fission = prompt_nu_fission; } else { - nu_fission = prompt_nu_fission + xt::sum(delayed_nu_fission, {1}); + // nu_fission = prompt_nu_fission + sum over delayed groups + nu_fission = prompt_nu_fission; + for (size_t a = 0; a < nu_fission.shape(0); ++a) + for (size_t g = 0; g < nu_fission.shape(1); ++g) + for (size_t d = 0; d < delayed_nu_fission.shape(1); ++d) + nu_fission(a, g) += delayed_nu_fission(a, d, g); } } @@ -415,7 +517,7 @@ void XsData::scatter_from_hdf5(hid_t xsdata_grp, size_t n_ang, // Now use this info to find the length of a vector to hold the flattened // data. - size_t length = order_data * xt::sum(gmax - gmin + 1)(); + size_t length = order_data * static_cast(xt::sum(gmax - gmin + 1)); double_4dvec input_scatt(n_ang, double_3dvec(n_g_)); xt::xtensor temp_arr({length}, 0.); @@ -525,24 +627,49 @@ void XsData::combine( kappa_fission += scalar * that->kappa_fission; fission += scalar * that->fission; delayed_nu_fission += scalar * that->delayed_nu_fission; - chi_prompt += scalar * - xt::view(xt::sum(that->prompt_nu_fission, {1}), xt::all(), - xt::newaxis(), xt::newaxis()) * - that->chi_prompt; - chi_delayed += scalar * - xt::view(xt::sum(that->delayed_nu_fission, {2}), xt::all(), - xt::all(), xt::newaxis(), xt::newaxis()) * - that->chi_delayed; + // chi_prompt += scalar * sum_pnf(a) * that->chi_prompt(a, gin, gout) + // where sum_pnf(a) = sum over gin of prompt_nu_fission(a, gin) + { + auto sum_pnf = xt::sum(that->prompt_nu_fission, {1}); + for (size_t a = 0; a < chi_prompt.shape(0); ++a) + for (size_t gin = 0; gin < chi_prompt.shape(1); ++gin) + for (size_t gout = 0; gout < chi_prompt.shape(2); ++gout) + chi_prompt(a, gin, gout) += + scalar * sum_pnf(a) * that->chi_prompt(a, gin, gout); + } + // chi_delayed += scalar * sum_dnf(a, d) * that->chi_delayed(a, d, gin, gout) + // where sum_dnf(a, d) = sum over gin of delayed_nu_fission(a, d, gin) + { + auto sum_dnf = xt::sum(that->delayed_nu_fission, {2}); + for (size_t a = 0; a < chi_delayed.shape(0); ++a) + for (size_t d = 0; d < chi_delayed.shape(1); ++d) + for (size_t gin = 0; gin < chi_delayed.shape(2); ++gin) + for (size_t gout = 0; gout < chi_delayed.shape(3); ++gout) + chi_delayed(a, d, gin, gout) += + scalar * sum_dnf(a, d) * that->chi_delayed(a, d, gin, gout); + } } decay_rate += scalar * that->decay_rate; } - // Ensure the chi_prompt and chi_delayed are normalized to 1 for each - // azimuthal angle and delayed group (for chi_delayed) - chi_prompt /= - xt::view(xt::sum(chi_prompt, {2}), xt::all(), xt::all(), xt::newaxis()); - chi_delayed /= xt::view( - xt::sum(chi_delayed, {3}), xt::all(), xt::all(), xt::all(), xt::newaxis()); + // Ensure the chi_prompt and chi_delayed are normalized to 1 + for (size_t a = 0; a < chi_prompt.shape(0); ++a) + for (size_t gin = 0; gin < chi_prompt.shape(1); ++gin) { + double s = 0.0; + for (size_t gout = 0; gout < chi_prompt.shape(2); ++gout) + s += chi_prompt(a, gin, gout); + for (size_t gout = 0; gout < chi_prompt.shape(2); ++gout) + chi_prompt(a, gin, gout) /= s; + } + for (size_t a = 0; a < chi_delayed.shape(0); ++a) + for (size_t d = 0; d < chi_delayed.shape(1); ++d) + for (size_t gin = 0; gin < chi_delayed.shape(2); ++gin) { + double s = 0.0; + for (size_t gout = 0; gout < chi_delayed.shape(3); ++gout) + s += chi_delayed(a, d, gin, gout); + for (size_t gout = 0; gout < chi_delayed.shape(3); ++gout) + chi_delayed(a, d, gin, gout) /= s; + } // Allow the ScattData object to combine itself for (size_t a = 0; a < total.shape()[0]; a++) { From 8e70d52ee676cbdf038e1ec2fca8458cd359e4e6 Mon Sep 17 00:00:00 2001 From: John Tramm Date: Mon, 9 Feb 2026 15:00:33 -0600 Subject: [PATCH 02/51] claude fixes submodule issue? --- vendor/xtensor | 1 - vendor/xtl | 1 - 2 files changed, 2 deletions(-) delete mode 160000 vendor/xtensor delete mode 160000 vendor/xtl diff --git a/vendor/xtensor b/vendor/xtensor deleted file mode 160000 index 3634f2ded19..00000000000 --- a/vendor/xtensor +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3634f2ded19e0cf38208c8b86cea9e1d7c8e397d diff --git a/vendor/xtl b/vendor/xtl deleted file mode 160000 index a7c1c5444df..00000000000 --- a/vendor/xtl +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a7c1c5444dfc57f76620391af4c94785ff82c8d6 From cc52ab06a606e0f0568da8deb8157b69d5a14ae5 Mon Sep 17 00:00:00 2001 From: John Tramm Date: Mon, 9 Feb 2026 15:06:45 -0600 Subject: [PATCH 03/51] added tensor.h --- include/openmc/tensor.h | 2273 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 2273 insertions(+) create mode 100644 include/openmc/tensor.h diff --git a/include/openmc/tensor.h b/include/openmc/tensor.h new file mode 100644 index 00000000000..b8d019740c2 --- /dev/null +++ b/include/openmc/tensor.h @@ -0,0 +1,2273 @@ +#ifndef OPENMC_TENSOR_H +#define OPENMC_TENSOR_H + +// Drop-in replacement for xtensor functionality used by OpenMC. +// Only implements the subset of xtensor that OpenMC actually calls. +// Built on openmc::vector for future GPU portability. + +#include "openmc/vector.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xt { + +//============================================================================== +// Forward declarations +//============================================================================== + +template +class xtensor; + +template +class xarray; + +template +class xtensor_fixed; + +template +struct xshape {}; + +//============================================================================== +// Slice/view helper types +//============================================================================== + +struct xall_type {}; + +inline xall_type all() +{ + return {}; +} + +struct xrange_type { + std::ptrdiff_t start; + std::ptrdiff_t stop; +}; + +inline xrange_type range(std::ptrdiff_t start, std::ptrdiff_t stop) +{ + return {start, stop}; +} + +namespace placeholders { +constexpr std::ptrdiff_t _ = std::numeric_limits::max(); +} + +// Ownership tags for adapt() +struct no_ownership {}; +struct acquire_ownership {}; + +//============================================================================== +// xshape traits for xtensor_fixed +//============================================================================== + +namespace detail { +template +struct xshape_traits; + +template +struct xshape_traits> { + static constexpr std::size_t ndim = 2; + static constexpr std::size_t total = D0 * D1; + static constexpr std::size_t dim0 = D0; + static constexpr std::size_t dim1 = D1; +}; +} // namespace detail + +//============================================================================== +// Storage type: avoids std::vector specialization +//============================================================================== + +template +struct storage_type_map { + using type = T; +}; +template<> +struct storage_type_map { + using type = unsigned char; +}; +template +using storage_type = typename storage_type_map::type; + +//============================================================================== +// 1D view: a strided view into a contiguous buffer +//============================================================================== + +template +class xtensor_view_1d { + T* data_; + std::size_t size_; + std::size_t stride_; + +public: + using value_type = std::remove_const_t; + + xtensor_view_1d(T* data, std::size_t size, std::size_t stride = 1) + : data_(data), size_(size), stride_(stride) + {} + + T& operator()(std::size_t i) { return data_[i * stride_]; } + const T& operator()(std::size_t i) const { return data_[i * stride_]; } + T& operator[](std::size_t i) { return data_[i * stride_]; } + const T& operator[](std::size_t i) const { return data_[i * stride_]; } + + std::size_t size() const { return size_; } + + // Assignment from another 1D view + template + xtensor_view_1d& operator=(const xtensor_view_1d& other) + { + for (std::size_t i = 0; i < size_; ++i) + data_[i * stride_] = other(i); + return *this; + } + + // Assignment from xarray + template + xtensor_view_1d& operator=(const xarray& other) + { + for (std::size_t i = 0; i < size_; ++i) + data_[i * stride_] = static_cast(other.data()[i]); + return *this; + } + + // Assignment from xtensor (any dimension, uses linear indexing) + template + xtensor_view_1d& operator=(const xtensor& other) + { + for (std::size_t i = 0; i < size_; ++i) + data_[i * stride_] = other.data()[i]; + return *this; + } + + // Assignment from scalar + template + auto operator=(U val) -> + std::enable_if_t::value, xtensor_view_1d&> + { + for (std::size_t i = 0; i < size_; ++i) + data_[i * stride_] = val; + return *this; + } + + // Compound assignment operators + template + xtensor_view_1d& operator+=(const xtensor& o) + { + for (std::size_t i = 0; i < size_; ++i) + data_[i * stride_] += o.data()[i]; + return *this; + } + + template + xtensor_view_1d& operator+=(const xtensor_view_1d& o) + { + for (std::size_t i = 0; i < size_; ++i) + data_[i * stride_] += o(i); + return *this; + } + + xtensor_view_1d& operator/=(value_type val) + { + for (std::size_t i = 0; i < size_; ++i) + data_[i * stride_] /= val; + return *this; + } + + xtensor_view_1d& operator*=(value_type val) + { + for (std::size_t i = 0; i < size_; ++i) + data_[i * stride_] *= val; + return *this; + } + + // Strided iterator + class const_iterator { + const T* ptr_; + std::size_t stride_; + + public: + using iterator_category = std::random_access_iterator_tag; + using value_type = std::remove_const_t; + using difference_type = std::ptrdiff_t; + using pointer = const T*; + using reference = const T&; + + const_iterator(const T* ptr, std::size_t stride) + : ptr_(ptr), stride_(stride) + {} + const T& operator*() const { return *ptr_; } + const_iterator& operator++() + { + ptr_ += stride_; + return *this; + } + const_iterator operator++(int) + { + auto tmp = *this; + ptr_ += stride_; + return tmp; + } + const_iterator& operator--() + { + ptr_ -= stride_; + return *this; + } + const_iterator operator+(difference_type n) const + { + return const_iterator(ptr_ + n * stride_, stride_); + } + const_iterator operator-(difference_type n) const + { + return const_iterator(ptr_ - n * stride_, stride_); + } + difference_type operator-(const const_iterator& other) const + { + return (ptr_ - other.ptr_) / static_cast(stride_); + } + bool operator==(const const_iterator& other) const + { + return ptr_ == other.ptr_; + } + bool operator!=(const const_iterator& other) const + { + return ptr_ != other.ptr_; + } + bool operator<(const const_iterator& other) const + { + return ptr_ < other.ptr_; + } + bool operator>(const const_iterator& other) const + { + return ptr_ > other.ptr_; + } + bool operator<=(const const_iterator& other) const + { + return ptr_ <= other.ptr_; + } + bool operator>=(const const_iterator& other) const + { + return ptr_ >= other.ptr_; + } + const T& operator[](difference_type n) const + { + return *(ptr_ + n * stride_); + } + const_iterator& operator+=(difference_type n) + { + ptr_ += n * stride_; + return *this; + } + const_iterator& operator-=(difference_type n) + { + ptr_ -= n * stride_; + return *this; + } + friend const_iterator operator+(difference_type n, const const_iterator& it) + { + return it + n; + } + }; + + class iterator { + T* ptr_; + std::size_t stride_; + + public: + using iterator_category = std::random_access_iterator_tag; + using value_type = std::remove_const_t; + using difference_type = std::ptrdiff_t; + using pointer = T*; + using reference = T&; + + iterator(T* ptr, std::size_t stride) : ptr_(ptr), stride_(stride) {} + T& operator*() { return *ptr_; } + iterator& operator++() + { + ptr_ += stride_; + return *this; + } + iterator operator++(int) + { + auto tmp = *this; + ptr_ += stride_; + return tmp; + } + iterator& operator--() + { + ptr_ -= stride_; + return *this; + } + iterator operator+(difference_type n) const + { + return iterator(ptr_ + n * stride_, stride_); + } + iterator operator-(difference_type n) const + { + return iterator(ptr_ - n * stride_, stride_); + } + difference_type operator-(const iterator& other) const + { + return (ptr_ - other.ptr_) / static_cast(stride_); + } + bool operator==(const iterator& other) const { return ptr_ == other.ptr_; } + bool operator!=(const iterator& other) const { return ptr_ != other.ptr_; } + bool operator<(const iterator& other) const { return ptr_ < other.ptr_; } + T& operator[](difference_type n) { return *(ptr_ + n * stride_); } + iterator& operator+=(difference_type n) + { + ptr_ += n * stride_; + return *this; + } + friend iterator operator+(difference_type n, const iterator& it) + { + return it + n; + } + }; + + iterator begin() { return iterator(data_, stride_); } + iterator end() { return iterator(data_ + size_ * stride_, stride_); } + const_iterator begin() const { return cbegin(); } + const_iterator end() const { return cend(); } + const_iterator cbegin() const { return const_iterator(data_, stride_); } + const_iterator cend() const + { + return const_iterator(data_ + size_ * stride_, stride_); + } + + T* data() { return data_; } + const T* data() const { return data_; } + std::size_t stride() const { return stride_; } +}; + +//============================================================================== +// 2D view: a strided 2D view into a contiguous buffer +//============================================================================== + +template +class xtensor_view_2d { + T* data_; + std::size_t shape0_, shape1_; + std::size_t stride0_, stride1_; + +public: + using value_type = std::remove_const_t; + + xtensor_view_2d(T* data, std::size_t s0, std::size_t s1, std::size_t st0, + std::size_t st1) + : data_(data), shape0_(s0), shape1_(s1), stride0_(st0), stride1_(st1) + {} + + T& operator()(std::size_t i, std::size_t j) + { + return data_[i * stride0_ + j * stride1_]; + } + const T& operator()(std::size_t i, std::size_t j) const + { + return data_[i * stride0_ + j * stride1_]; + } + + std::size_t size() const { return shape0_ * shape1_; } + std::array shape() const { return {shape0_, shape1_}; } + + // Assignment from 2D tensor + template + auto operator=(const xtensor& other) -> + std::enable_if_t + { + for (std::size_t i = 0; i < shape0_; ++i) + for (std::size_t j = 0; j < shape1_; ++j) + data_[i * stride0_ + j * stride1_] = other(i, j); + return *this; + } + + // Assignment from scalar + template + auto operator=(U val) -> + std::enable_if_t::value, xtensor_view_2d&> + { + for (std::size_t i = 0; i < shape0_; ++i) + for (std::size_t j = 0; j < shape1_; ++j) + data_[i * stride0_ + j * stride1_] = val; + return *this; + } +}; + +//============================================================================== +// Flat view: wraps all elements for bulk assignment +//============================================================================== + +template +class xtensor_view_flat { + T* data_; + std::size_t size_; + +public: + xtensor_view_flat(T* data, std::size_t size) : data_(data), size_(size) {} + + template + auto operator=(U val) -> + std::enable_if_t::value, xtensor_view_flat&> + { + std::fill(data_, data_ + size_, static_cast(val)); + return *this; + } + + template + auto operator=(const Container& other) -> + std::enable_if_t::value, xtensor_view_flat&> + { + std::copy(other.data(), other.data() + size_, data_); + return *this; + } +}; + +//============================================================================== +// xtensor: N-dimensional tensor backed by openmc::vector +//============================================================================== + +template +class xtensor { + openmc::vector> data_; + std::array shape_; + + std::size_t compute_size() const + { + std::size_t s = 1; + for (std::size_t i = 0; i < N; ++i) + s *= shape_[i]; + return s; + } + +public: + using value_type = T; + using stored_type = storage_type; + using iterator = typename openmc::vector::iterator; + using const_iterator = typename openmc::vector::const_iterator; + using shape_type = std::array; + + // Default constructor + xtensor() { shape_.fill(0); } + + // Shape constructor (from initializer list of size_t) + xtensor(std::initializer_list shape) + { + std::copy(shape.begin(), shape.end(), shape_.begin()); + data_.resize(compute_size()); + } + + // Shape + fill value constructor (from initializer list) + xtensor(std::initializer_list shape, T val) + { + std::copy(shape.begin(), shape.end(), shape_.begin()); + data_.assign(compute_size(), val); + } + + // Shape constructor (from array) + explicit xtensor(const std::array& shape) : shape_(shape) + { + data_.resize(compute_size()); + } + + // Shape + fill value constructor (from array) + xtensor(const std::array& shape, T val) : shape_(shape) + { + data_.assign(compute_size(), val); + } + + // Construct from data + shape + xtensor( + openmc::vector&& data, const std::array& shape) + : data_(std::move(data)), shape_(shape) + {} + + xtensor(const openmc::vector& data, + const std::array& shape) + : data_(data), shape_(shape) + {} + + // 1D initializer_list constructor (only for N==1, T != size_t to avoid + // ambiguity) + template::value>> + xtensor(std::initializer_list vals) + { + shape_[0] = vals.size(); + data_.assign(vals.begin(), vals.end()); + } + + // Static factory: from_shape + static xtensor from_shape(const std::array& shape) + { + return xtensor(shape); + } + + static xtensor from_shape(std::initializer_list shape) + { + xtensor result; + std::copy(shape.begin(), shape.end(), result.shape_.begin()); + result.data_.resize(result.compute_size()); + return result; + } + + // Multi-dimensional indexing + template + stored_type& operator()(Indices... indices) + { + return data_[offset(static_cast(indices)...)]; + } + + template + const stored_type& operator()(Indices... indices) const + { + return data_[offset(static_cast(indices)...)]; + } + + // Linear indexing + stored_type& operator[](std::size_t i) { return data_[i]; } + const stored_type& operator[](std::size_t i) const { return data_[i]; } + + // Data access + stored_type* data() { return data_.data(); } + const stored_type* data() const { return data_.data(); } + std::size_t size() const { return data_.size(); } + const shape_type& shape() const { return shape_; } + std::size_t shape(std::size_t dim) const { return shape_[dim]; } + bool empty() const { return data_.empty(); } + static constexpr std::size_t dimension() { return N; } + + // Resize + void resize(std::initializer_list shape) + { + std::copy(shape.begin(), shape.end(), shape_.begin()); + data_.resize(compute_size()); + } + + void resize(const std::array& shape) + { + shape_ = shape; + data_.resize(compute_size()); + } + + // Resize from vector (needed for xarray compatibility) + void resize(const openmc::vector& shape) + { + for (std::size_t i = 0; i < N && i < shape.size(); ++i) + shape_[i] = shape[i]; + data_.resize(compute_size()); + } + + // Resize from vector + void resize(const openmc::vector& shape) + { + for (std::size_t i = 0; i < N && i < shape.size(); ++i) + shape_[i] = static_cast(shape[i]); + data_.resize(compute_size()); + } + + // Fill + void fill(T val) { std::fill(data_.begin(), data_.end(), val); } + + // Iterators + iterator begin() { return data_.begin(); } + iterator end() { return data_.end(); } + const_iterator begin() const { return data_.begin(); } + const_iterator end() const { return data_.end(); } + const_iterator cbegin() const { return data_.cbegin(); } + const_iterator cend() const { return data_.cend(); } + + // Compound assignment with scalar + xtensor& operator+=(T val) + { + for (auto& x : data_) + x += val; + return *this; + } + xtensor& operator-=(T val) + { + for (auto& x : data_) + x -= val; + return *this; + } + xtensor& operator*=(T val) + { + for (auto& x : data_) + x *= val; + return *this; + } + xtensor& operator/=(T val) + { + for (auto& x : data_) + x /= val; + return *this; + } + + // Compound assignment with tensor + xtensor& operator+=(const xtensor& o) + { + for (std::size_t i = 0; i < data_.size(); ++i) + data_[i] += o.data_[i]; + return *this; + } + xtensor& operator-=(const xtensor& o) + { + for (std::size_t i = 0; i < data_.size(); ++i) + data_[i] -= o.data_[i]; + return *this; + } + xtensor& operator*=(const xtensor& o) + { + for (std::size_t i = 0; i < data_.size(); ++i) + data_[i] *= o.data_[i]; + return *this; + } + xtensor& operator/=(const xtensor& o) + { + for (std::size_t i = 0; i < data_.size(); ++i) + data_[i] /= o.data_[i]; + return *this; + } + + // Compound assignment with 1D view + template + xtensor& operator+=(const xtensor_view_1d& o) + { + for (std::size_t i = 0; i < data_.size(); ++i) + data_[i] += o(i); + return *this; + } + + // Binary ops: tensor op tensor (same type) + xtensor operator+(const xtensor& o) const + { + xtensor r(shape_); + for (std::size_t i = 0; i < data_.size(); ++i) + r.data_[i] = data_[i] + o.data_[i]; + return r; + } + xtensor operator-(const xtensor& o) const + { + xtensor r(shape_); + for (std::size_t i = 0; i < data_.size(); ++i) + r.data_[i] = data_[i] - o.data_[i]; + return r; + } + xtensor operator*(const xtensor& o) const + { + xtensor r(shape_); + for (std::size_t i = 0; i < data_.size(); ++i) + r.data_[i] = data_[i] * o.data_[i]; + return r; + } + xtensor operator/(const xtensor& o) const + { + xtensor r(shape_); + for (std::size_t i = 0; i < data_.size(); ++i) + r.data_[i] = data_[i] / o.data_[i]; + return r; + } + + // Binary ops: tensor op scalar + xtensor operator+(T val) const + { + xtensor r(shape_); + for (std::size_t i = 0; i < data_.size(); ++i) + r.data_[i] = data_[i] + val; + return r; + } + xtensor operator-(T val) const + { + xtensor r(shape_); + for (std::size_t i = 0; i < data_.size(); ++i) + r.data_[i] = data_[i] - val; + return r; + } + xtensor operator*(T val) const + { + xtensor r(shape_); + for (std::size_t i = 0; i < data_.size(); ++i) + r.data_[i] = data_[i] * val; + return r; + } + xtensor operator/(T val) const + { + xtensor r(shape_); + for (std::size_t i = 0; i < data_.size(); ++i) + r.data_[i] = data_[i] / val; + return r; + } + + // Unary minus + xtensor operator-() const + { + xtensor r(shape_); + for (std::size_t i = 0; i < data_.size(); ++i) + r.data_[i] = -data_[i]; + return r; + } + + // Comparison operators (element-wise, return bool tensor) + xtensor operator<=(T val) const + { + xtensor r(shape_); + for (std::size_t i = 0; i < data_.size(); ++i) + r.data()[i] = data_[i] <= val; + return r; + } + xtensor operator<(T val) const + { + xtensor r(shape_); + for (std::size_t i = 0; i < data_.size(); ++i) + r.data()[i] = data_[i] < val; + return r; + } + xtensor operator>=(T val) const + { + xtensor r(shape_); + for (std::size_t i = 0; i < data_.size(); ++i) + r.data()[i] = data_[i] >= val; + return r; + } + xtensor operator>(T val) const + { + xtensor r(shape_); + for (std::size_t i = 0; i < data_.size(); ++i) + r.data()[i] = data_[i] > val; + return r; + } + + // Tensor-tensor comparison + xtensor operator<(const xtensor& o) const + { + xtensor r(shape_); + for (std::size_t i = 0; i < data_.size(); ++i) + r.data()[i] = data_[i] < o.data_[i]; + return r; + } + + // Conversion from 1D view + template> + xtensor(const xtensor_view_1d& v) + { + shape_[0] = v.size(); + data_.resize(v.size()); + for (std::size_t i = 0; i < v.size(); ++i) + data_[i] = v(i); + } + + // Conversion from 2D view + template> + xtensor(const xtensor_view_2d& v) + { + auto s = v.shape(); + shape_[0] = s[0]; + shape_[1] = s[1]; + data_.resize(s[0] * s[1]); + for (std::size_t i = 0; i < s[0]; ++i) + for (std::size_t j = 0; j < s[1]; ++j) + data_[i * s[1] + j] = v(i, j); + } + + // Converting constructor from xarray + xtensor(const xarray& other); + + // Assignment from xarray + xtensor& operator=(const xarray& other); + + // Cross-type assignment from xtensor + template::value>> + xtensor& operator=(const xtensor& other) + { + shape_ = other.shape(); + data_.resize(other.size()); + for (std::size_t i = 0; i < other.size(); ++i) + data_[i] = static_cast(other.data()[i]); + return *this; + } + + // Assignment from 1D view + template> + xtensor& operator=(const xtensor_view_1d& v) + { + shape_[0] = v.size(); + data_.resize(v.size()); + for (std::size_t i = 0; i < v.size(); ++i) + data_[i] = v(i); + return *this; + } + + // Assignment from initializer list of values (for 1D only) + template::value>> + xtensor& operator=(std::initializer_list vals) + { + shape_[0] = vals.size(); + data_.assign(vals.begin(), vals.end()); + return *this; + } + +private: + // Offset calculations for row-major layout + std::size_t offset(std::size_t i0) const { return i0; } + + std::size_t offset(std::size_t i0, std::size_t i1) const + { + return i0 * shape_[1] + i1; + } + + std::size_t offset(std::size_t i0, std::size_t i1, std::size_t i2) const + { + return (i0 * shape_[1] + i1) * shape_[2] + i2; + } + + std::size_t offset( + std::size_t i0, std::size_t i1, std::size_t i2, std::size_t i3) const + { + return ((i0 * shape_[1] + i1) * shape_[2] + i2) * shape_[3] + i3; + } +}; + +// scalar op tensor (same type) +template::value>> +xtensor operator*(T val, const xtensor& arr) +{ + return arr * val; +} + +template::value>> +xtensor operator+(T val, const xtensor& arr) +{ + return arr + val; +} + +template +xtensor operator-(T val, const xtensor& arr) +{ + xtensor r(arr.shape()); + for (std::size_t i = 0; i < arr.size(); ++i) + r.data()[i] = val - arr.data()[i]; + return r; +} + +template +xtensor operator/(T val, const xtensor& arr) +{ + xtensor r(arr.shape()); + for (std::size_t i = 0; i < arr.size(); ++i) + r.data()[i] = val / arr.data()[i]; + return r; +} + +// Mixed-type arithmetic: xtensor op xtensor +// Returns xtensor for common cases (int*double, double/int, etc.) +template::value>> +xtensor operator*( + const xtensor& a, const xtensor& b) +{ + xtensor r(a.shape()); + for (std::size_t i = 0; i < a.size(); ++i) + r.data()[i] = static_cast(a.data()[i]) * + static_cast(b.data()[i]); + return r; +} + +template::value>> +xtensor operator/( + const xtensor& a, const xtensor& b) +{ + xtensor r(a.shape()); + for (std::size_t i = 0; i < a.size(); ++i) + r.data()[i] = static_cast(a.data()[i]) / + static_cast(b.data()[i]); + return r; +} + +template::value>> +xtensor operator+( + const xtensor& a, const xtensor& b) +{ + xtensor r(a.shape()); + for (std::size_t i = 0; i < a.size(); ++i) + r.data()[i] = static_cast(a.data()[i]) + + static_cast(b.data()[i]); + return r; +} + +template::value>> +xtensor operator-( + const xtensor& a, const xtensor& b) +{ + xtensor r(a.shape()); + for (std::size_t i = 0; i < a.size(); ++i) + r.data()[i] = static_cast(a.data()[i]) - + static_cast(b.data()[i]); + return r; +} + +//============================================================================== +// xarray: dynamic-dimension tensor backed by openmc::vector +//============================================================================== + +template +class xarray { + openmc::vector> data_; + openmc::vector shape_; + +public: + using value_type = T; + using stored_type = storage_type; + using iterator = typename openmc::vector::iterator; + using const_iterator = typename openmc::vector::const_iterator; + + xarray() = default; + + explicit xarray(const openmc::vector& shape) : shape_(shape) + { + std::size_t total = 1; + for (auto d : shape_) + total *= d; + data_.resize(total); + } + + xarray(const openmc::vector& shape, T val) : shape_(shape) + { + std::size_t total = 1; + for (auto d : shape_) + total *= d; + data_.assign(total, val); + } + + // Construct from initializer_list of values (creates 1D xarray) + xarray(std::initializer_list vals) : shape_({vals.size()}) + { + data_.assign(vals.begin(), vals.end()); + } + + // Converting constructor from xtensor + template + xarray(const xtensor& t) + { + for (std::size_t i = 0; i < N; ++i) + shape_.push_back(t.shape()[i]); + data_.assign(t.data(), t.data() + t.size()); + } + + // Construct from vector shape + explicit xarray(const openmc::vector& shape) + { + for (auto d : shape) + shape_.push_back(static_cast(d)); + std::size_t total = 1; + for (auto d : shape_) + total *= d; + data_.resize(total); + } + + // Multi-dimensional indexing (up to 4D) + template + stored_type& operator()(Indices... indices) + { + return data_[compute_offset(static_cast(indices)...)]; + } + + template + const stored_type& operator()(Indices... indices) const + { + return data_[compute_offset(static_cast(indices)...)]; + } + + stored_type& operator[](std::size_t i) { return data_[i]; } + const stored_type& operator[](std::size_t i) const { return data_[i]; } + + stored_type* data() { return data_.data(); } + const stored_type* data() const { return data_.data(); } + std::size_t size() const { return data_.size(); } + const openmc::vector& shape() const { return shape_; } + std::size_t shape(std::size_t dim) const { return shape_[dim]; } + bool empty() const { return data_.empty(); } + + void resize(const openmc::vector& shape) + { + shape_ = shape; + std::size_t total = 1; + for (auto d : shape_) + total *= d; + data_.resize(total); + } + + void resize(const openmc::vector& shape) + { + shape_.clear(); + for (auto d : shape) + shape_.push_back(static_cast(d)); + std::size_t total = 1; + for (auto d : shape_) + total *= d; + data_.resize(total); + } + + // reshape: change shape without changing data (total must match) + template + void reshape(const ShapeType& new_shape) + { + shape_.clear(); + for (auto d : new_shape) + shape_.push_back(static_cast(d)); + } + + void fill(T val) { std::fill(data_.begin(), data_.end(), val); } + + iterator begin() { return data_.begin(); } + iterator end() { return data_.end(); } + const_iterator begin() const { return data_.begin(); } + const_iterator end() const { return data_.end(); } + + // Compound assignment + xarray& operator+=(T val) + { + for (auto& x : data_) + x += val; + return *this; + } + xarray& operator-=(T val) + { + for (auto& x : data_) + x -= val; + return *this; + } + xarray& operator*=(T val) + { + for (auto& x : data_) + x *= val; + return *this; + } + xarray& operator/=(T val) + { + for (auto& x : data_) + x /= val; + return *this; + } + + // Binary ops with scalar + xarray operator*(T val) const + { + xarray r(shape_); + for (std::size_t i = 0; i < data_.size(); ++i) + r.data_[i] = data_[i] * val; + return r; + } + xarray operator/(T val) const + { + xarray r(shape_); + for (std::size_t i = 0; i < data_.size(); ++i) + r.data_[i] = data_[i] / val; + return r; + } + xarray operator+(T val) const + { + xarray r(shape_); + for (std::size_t i = 0; i < data_.size(); ++i) + r.data_[i] = data_[i] + val; + return r; + } + xarray operator-(T val) const + { + xarray r(shape_); + for (std::size_t i = 0; i < data_.size(); ++i) + r.data_[i] = data_[i] - val; + return r; + } + xarray operator-() const + { + xarray r(shape_); + for (std::size_t i = 0; i < data_.size(); ++i) + r.data_[i] = -data_[i]; + return r; + } + + // Comparison with scalar + xarray operator>(T val) const + { + xarray r(shape_); + for (std::size_t i = 0; i < data_.size(); ++i) + r.data()[i] = data_[i] > val; + return r; + } + xarray operator<(T val) const + { + xarray r(shape_); + for (std::size_t i = 0; i < data_.size(); ++i) + r.data()[i] = data_[i] < val; + return r; + } + xarray operator<=(T val) const + { + xarray r(shape_); + for (std::size_t i = 0; i < data_.size(); ++i) + r.data()[i] = data_[i] <= val; + return r; + } + xarray operator>=(T val) const + { + xarray r(shape_); + for (std::size_t i = 0; i < data_.size(); ++i) + r.data()[i] = data_[i] >= val; + return r; + } + + // Convert to xtensor (explicit shape) + template + operator xtensor() const + { + std::array s {}; + for (std::size_t i = 0; i < M && i < shape_.size(); ++i) + s[i] = shape_[i]; + xtensor result(s); + std::copy(data_.begin(), data_.end(), result.data()); + return result; + } + +private: + std::size_t compute_offset(std::size_t i0) const { return i0; } + + std::size_t compute_offset(std::size_t i0, std::size_t i1) const + { + return i0 * shape_[1] + i1; + } + + std::size_t compute_offset( + std::size_t i0, std::size_t i1, std::size_t i2) const + { + return (i0 * shape_[1] + i1) * shape_[2] + i2; + } + + std::size_t compute_offset( + std::size_t i0, std::size_t i1, std::size_t i2, std::size_t i3) const + { + return ((i0 * shape_[1] + i1) * shape_[2] + i2) * shape_[3] + i3; + } +}; + +// scalar op xarray +template +xarray operator*(T val, const xarray& arr) +{ + return arr * val; +} + +// xtensor converting constructor from xarray (defined after xarray is complete) +template +xtensor::xtensor(const xarray& other) +{ + for (std::size_t i = 0; i < N; ++i) + shape_[i] = other.shape()[i]; + data_.assign(other.data(), other.data() + other.size()); +} + +// xtensor assignment from xarray (defined after xarray is complete) +template +xtensor& xtensor::operator=(const xarray& other) +{ + for (std::size_t i = 0; i < N; ++i) + shape_[i] = other.shape()[i]; + data_.assign(other.data(), other.data() + other.size()); + return *this; +} + +// Mixed-type arithmetic: xarray op xtensor +// These handle cases like xarray * xtensor +template +xtensor operator*(const xarray& a, const xtensor& b) +{ + xtensor r(b.shape()); + for (std::size_t i = 0; i < b.size(); ++i) + r.data()[i] = static_cast(a.data()[i]) * + static_cast(b.data()[i]); + return r; +} + +template +xtensor operator*(const xtensor& a, const xarray& b) +{ + xtensor r(a.shape()); + for (std::size_t i = 0; i < a.size(); ++i) + r.data()[i] = static_cast(a.data()[i]) * + static_cast(b.data()[i]); + return r; +} + +template +xtensor operator/(const xtensor& a, const xarray& b) +{ + xtensor r(a.shape()); + for (std::size_t i = 0; i < a.size(); ++i) + r.data()[i] = static_cast(a.data()[i]) / + static_cast(b.data()[i]); + return r; +} + +template +xtensor operator/(const xarray& a, const xtensor& b) +{ + xtensor r(b.shape()); + for (std::size_t i = 0; i < b.size(); ++i) + r.data()[i] = static_cast(a.data()[i]) / + static_cast(b.data()[i]); + return r; +} + +template +xtensor operator+(const xarray& a, const xtensor& b) +{ + xtensor r(b.shape()); + for (std::size_t i = 0; i < b.size(); ++i) + r.data()[i] = static_cast(a.data()[i]) + + static_cast(b.data()[i]); + return r; +} + +template +xtensor operator+(const xtensor& a, const xarray& b) +{ + xtensor r(a.shape()); + for (std::size_t i = 0; i < a.size(); ++i) + r.data()[i] = static_cast(a.data()[i]) + + static_cast(b.data()[i]); + return r; +} + +//============================================================================== +// xtensor_fixed>: fixed-size tensor +//============================================================================== + +template +class xtensor_fixed { + static constexpr std::size_t total_ = detail::xshape_traits::total; + static constexpr std::size_t dim0_ = detail::xshape_traits::dim0; + static constexpr std::size_t dim1_ = detail::xshape_traits::dim1; + + T data_[total_] = {}; + +public: + using value_type = T; + + template + T& operator()(I0 i, I1 j) + { + return data_[static_cast(i) * dim1_ + + static_cast(j)]; + } + template + const T& operator()(I0 i, I1 j) const + { + return data_[static_cast(i) * dim1_ + + static_cast(j)]; + } + + T* data() { return data_; } + const T* data() const { return data_; } + std::size_t size() const { return total_; } + std::array shape() const { return {dim0_, dim1_}; } + void fill(T val) { std::fill(data_, data_ + total_, val); } +}; + +//============================================================================== +// adapt() functions +//============================================================================== + +// Adapt a std::vector into a 1D xtensor (copy) +template +xtensor adapt(const std::vector& vec) +{ + xtensor result({vec.size()}); + std::copy(vec.begin(), vec.end(), result.data()); + return result; +} + +// Adapt a vector with explicit shape into xarray (copy) +template +xarray adapt(const openmc::vector& vec, const ShapeType& shape) +{ + openmc::vector s; + for (auto d : shape) + s.push_back(static_cast(d)); + xarray result(s); + std::copy(vec.begin(), vec.end(), result.data()); + return result; +} + +// Adapt std::array with explicit shape (copy) +template +xarray adapt(const std::array& arr, const ShapeType& shape) +{ + openmc::vector s; + std::size_t total = 1; + for (auto d : shape) { + auto dim = static_cast(d); + s.push_back(dim); + total *= dim; + } + xarray result(s); + std::copy(arr.data(), arr.data() + std::min(total, M), result.data()); + return result; +} + +// Adapt vector with initializer_list shape (for brace-init-list deduction) +template +xarray adapt(const openmc::vector& vec, std::initializer_list shape) +{ + openmc::vector s; + for (auto d : shape) + s.push_back(static_cast(d)); + xarray result(s); + std::copy(vec.begin(), vec.end(), result.data()); + return result; +} + +// Adapt std::array with initializer_list shape (for brace-init-list deduction) +template +xarray adapt(const std::array& arr, std::initializer_list shape) +{ + openmc::vector s; + std::size_t total = 1; + for (auto d : shape) { + auto dim = static_cast(d); + s.push_back(dim); + total *= dim; + } + xarray result(s); + std::copy(arr.data(), arr.data() + std::min(total, M), result.data()); + return result; +} + +// Adapt raw pointer with no_ownership into xtensor (copy) +template +xarray adapt( + const T* ptr, std::size_t n, no_ownership, const ShapeType& shape) +{ + openmc::vector s; + for (auto d : shape) + s.push_back(static_cast(d)); + xarray result(s); + std::copy(ptr, ptr + n, result.data()); + return result; +} + +// Adapt raw pointer with acquire_ownership into xarray (copy + delete) +template +xarray adapt( + T* ptr, std::size_t total, acquire_ownership, const ShapeType& shape) +{ + openmc::vector s; + for (auto d : shape) + s.push_back(static_cast(d)); + xarray result(s); + std::copy(ptr, ptr + total, result.data()); + delete[] ptr; + return result; +} + +// Adapt const pointer with shape into xarray (copy) +template +xarray adapt(const T* ptr, const ShapeType& shape) +{ + openmc::vector s; + std::size_t total = 1; + for (auto d : shape) { + auto dim = static_cast(d); + s.push_back(dim); + total *= dim; + } + xarray result(s); + std::copy(ptr, ptr + total, result.data()); + return result; +} + +//============================================================================== +// Construction helpers +//============================================================================== + +template +xarray zeros(std::initializer_list shape) +{ + openmc::vector s(shape); + xarray result(s, T(0)); + return result; +} + +// zeros with vector shape +template +xarray zeros(const openmc::vector& shape) +{ + return xarray(shape, T(0)); +} + +// zeros with vector of any int type +template::value && + !std::is_same::value>> +xarray zeros(const openmc::vector& shape) +{ + openmc::vector s; + for (auto d : shape) + s.push_back(static_cast(d)); + return xarray(s, T(0)); +} + +template +xtensor zeros(const std::array& shape) +{ + return xtensor(shape, T(0)); +} + +template +xtensor zeros(const std::array& shape) +{ + return xtensor(shape, T(0)); +} + +template +xarray empty(std::initializer_list shape) +{ + openmc::vector s(shape); + return xarray(s); +} + +// empty with int initializer_list (avoids narrowing errors) +template +xarray empty(std::initializer_list shape) +{ + openmc::vector s; + for (auto d : shape) + s.push_back(static_cast(d)); + return xarray(s); +} + +template +xtensor empty(const std::array& shape) +{ + return xtensor(shape); +} + +template +xtensor empty(const std::array& shape) +{ + return xtensor(shape); +} + +// empty with std::array shape +template +xarray empty(const std::array& shape) +{ + openmc::vector s; + for (auto d : shape) + s.push_back(static_cast(d)); + return xarray(s); +} + +template +xtensor zeros_like(const xtensor& o) +{ + return xtensor(o.shape(), T(0)); +} + +template +xtensor empty_like(const xtensor& o) +{ + return xtensor(o.shape()); +} + +template +xtensor ones_like(const xtensor& o) +{ + return xtensor(o.shape(), T(1)); +} + +// full_like: create tensor with same shape, filled with value +template +xtensor full_like(const xtensor& o, V val) +{ + return xtensor(o.shape(), static_cast(val)); +} + +template +xarray empty_like(const xarray& o) +{ + return xarray(o.shape()); +} + +template +xtensor linspace(T start, T stop, std::size_t n) +{ + xtensor result({n}); + if (n < 2) { + result[0] = start; + return result; + } + for (std::size_t i = 0; i < n; ++i) { + result[i] = start + static_cast(i) * (stop - start) / + static_cast(n - 1); + } + return result; +} + +//============================================================================== +// view() functions +//============================================================================== + +// Resolve a range endpoint: if == placeholders::_, use dim as the end +inline std::size_t resolve_end(std::ptrdiff_t val, std::size_t dim) +{ + if (val == placeholders::_) + return dim; + if (val < 0) + return dim + val; + return static_cast(val); +} + +// view(2D tensor, range, int) -> 1D view of column slice +template +xtensor_view_1d view(xtensor& a, xrange_type r, std::size_t col) +{ + auto start = resolve_end(r.start, a.shape()[0]); + auto stop = resolve_end(r.stop, a.shape()[0]); + return {a.data() + start * a.shape()[1] + col, stop - start, a.shape()[1]}; +} +template +xtensor_view_1d view( + const xtensor& a, xrange_type r, std::size_t col) +{ + auto start = resolve_end(r.start, a.shape()[0]); + auto stop = resolve_end(r.stop, a.shape()[0]); + return {a.data() + start * a.shape()[1] + col, stop - start, a.shape()[1]}; +} + +// view(2D tensor, all, int) -> 1D column view +template +xtensor_view_1d view(xtensor& a, xall_type, std::size_t col) +{ + return {a.data() + col, a.shape()[0], a.shape()[1]}; +} +template +xtensor_view_1d view( + const xtensor& a, xall_type, std::size_t col) +{ + return {a.data() + col, a.shape()[0], a.shape()[1]}; +} + +// view(2D tensor, int) -> 1D row view +template +xtensor_view_1d view(xtensor& a, std::size_t row) +{ + return {a.data() + row * a.shape()[1], a.shape()[1], 1}; +} +template +xtensor_view_1d view(const xtensor& a, std::size_t row) +{ + return {a.data() + row * a.shape()[1], a.shape()[1], 1}; +} + +// view(1D tensor, range) -> 1D view +template +xtensor_view_1d view(xtensor& a, xrange_type r) +{ + auto start = resolve_end(r.start, a.shape()[0]); + auto stop = resolve_end(r.stop, a.shape()[0]); + return {a.data() + start, stop - start, 1}; +} +template +xtensor_view_1d view(const xtensor& a, xrange_type r) +{ + auto start = resolve_end(r.start, a.shape()[0]); + auto stop = resolve_end(r.stop, a.shape()[0]); + return {a.data() + start, stop - start, 1}; +} + +// view(3D tensor, int, int, all) -> 1D view along depth dimension +template +xtensor_view_1d view(xtensor& a, std::size_t i, std::size_t j, xall_type) +{ + auto depth = a.shape()[2]; + return {a.data() + (i * a.shape()[1] + j) * depth, depth, 1}; +} +template +xtensor_view_1d view( + const xtensor& a, std::size_t i, std::size_t j, xall_type) +{ + auto depth = a.shape()[2]; + return {a.data() + (i * a.shape()[1] + j) * depth, depth, 1}; +} + +// view(3D tensor, all, all, int) -> 2D view +template +xtensor_view_2d view(xtensor& a, xall_type, xall_type, std::size_t k) +{ + return {a.data() + k, a.shape()[0], a.shape()[1], + a.shape()[1] * a.shape()[2], a.shape()[2]}; +} +template +xtensor_view_2d view( + const xtensor& a, xall_type, xall_type, std::size_t k) +{ + return {a.data() + k, a.shape()[0], a.shape()[1], + a.shape()[1] * a.shape()[2], a.shape()[2]}; +} + +// view(3D tensor, range, all, int) -> 2D view (subset of rows) +template +xtensor_view_2d view( + xtensor& a, xrange_type r, xall_type, std::size_t k) +{ + auto start = resolve_end(r.start, a.shape()[0]); + auto stop = resolve_end(r.stop, a.shape()[0]); + return {a.data() + start * a.shape()[1] * a.shape()[2] + k, stop - start, + a.shape()[1], a.shape()[1] * a.shape()[2], a.shape()[2]}; +} +template +xtensor_view_2d view( + const xtensor& a, xrange_type r, xall_type, std::size_t k) +{ + auto start = resolve_end(r.start, a.shape()[0]); + auto stop = resolve_end(r.stop, a.shape()[0]); + return {a.data() + start * a.shape()[1] * a.shape()[2] + k, stop - start, + a.shape()[1], a.shape()[1] * a.shape()[2], a.shape()[2]}; +} + +// view(fixed_2D, all, int) -> 1D column view +template +xtensor_view_1d view(xtensor_fixed& a, xall_type, std::size_t col) +{ + auto s = a.shape(); + return {a.data() + col, s[0], s[1]}; +} +template +xtensor_view_1d view( + const xtensor_fixed& a, xall_type, std::size_t col) +{ + auto s = a.shape(); + return {a.data() + col, s[0], s[1]}; +} + +// view(fixed, all) -> flat view (for bulk assignment) +template +xtensor_view_flat view(xtensor_fixed& a, xall_type) +{ + return {a.data(), a.size()}; +} + +// view(tensor, all) -> flat view (for bulk assignment) +template +xtensor_view_flat view(xtensor& a, xall_type) +{ + return {a.data(), a.size()}; +} + +// view(xarray, int) -> row view of 2D xarray +template +xtensor_view_1d view(xarray& a, std::size_t row) +{ + return {a.data() + row * a.shape()[1], a.shape()[1], 1}; +} +template +xtensor_view_1d view(const xarray& a, std::size_t row) +{ + return {a.data() + row * a.shape()[1], a.shape()[1], 1}; +} + +// view(xarray, range) -> 1D view for 1D xarray +template +xtensor_view_1d view(xarray& a, xrange_type r) +{ + auto start = resolve_end(r.start, a.shape()[0]); + auto stop = resolve_end(r.stop, a.shape()[0]); + return {a.data() + start, stop - start, 1}; +} +template +xtensor_view_1d view(const xarray& a, xrange_type r) +{ + auto start = resolve_end(r.start, a.shape()[0]); + auto stop = resolve_end(r.stop, a.shape()[0]); + return {a.data() + start, stop - start, 1}; +} + +// view(2D tensor, row, xall_type) -> 1D row view (entire row) +template +xtensor_view_1d view(xtensor& a, std::size_t row, xall_type) +{ + return {a.data() + row * a.shape()[1], a.shape()[1], 1}; +} +template +xtensor_view_1d view(const xtensor& a, std::size_t row, xall_type) +{ + return {a.data() + row * a.shape()[1], a.shape()[1], 1}; +} + +// view(2D tensor, row, range) -> 1D view of row subset +template +xtensor_view_1d view(xtensor& a, std::size_t row, xrange_type r) +{ + auto cols = a.shape()[1]; + auto start = resolve_end(r.start, cols); + auto stop = resolve_end(r.stop, cols); + return {a.data() + row * cols + start, stop - start, 1}; +} +template +xtensor_view_1d view(const xtensor& a, std::size_t row, xrange_type r) +{ + auto cols = a.shape()[1]; + auto start = resolve_end(r.start, cols); + auto stop = resolve_end(r.stop, cols); + return {a.data() + row * cols + start, stop - start, 1}; +} + +// view(2D xarray, row, xall_type) -> 1D row view (entire row) +template +xtensor_view_1d view(xarray& a, std::size_t row, xall_type) +{ + return {a.data() + row * a.shape()[1], a.shape()[1], 1}; +} +template +xtensor_view_1d view(const xarray& a, std::size_t row, xall_type) +{ + return {a.data() + row * a.shape()[1], a.shape()[1], 1}; +} + +// view(2D xarray, row, range) -> 1D view of row subset +template +xtensor_view_1d view(xarray& a, std::size_t row, xrange_type r) +{ + auto cols = a.shape()[1]; + auto start = resolve_end(r.start, cols); + auto stop = resolve_end(r.stop, cols); + return {a.data() + row * cols + start, stop - start, 1}; +} +template +xtensor_view_1d view(const xarray& a, std::size_t row, xrange_type r) +{ + auto cols = a.shape()[1]; + auto start = resolve_end(r.start, cols); + auto stop = resolve_end(r.stop, cols); + return {a.data() + row * cols + start, stop - start, 1}; +} + +// row() and col() for 2D tensors +template +xtensor_view_1d row(xtensor& a, std::size_t i) +{ + return {a.data() + i * a.shape()[1], a.shape()[1], 1}; +} +template +xtensor_view_1d row(const xtensor& a, std::size_t i) +{ + return {a.data() + i * a.shape()[1], a.shape()[1], 1}; +} +template +xtensor_view_1d col(xtensor& a, std::size_t j) +{ + return {a.data() + j, a.shape()[0], a.shape()[1]}; +} +template +xtensor_view_1d col(const xtensor& a, std::size_t j) +{ + return {a.data() + j, a.shape()[0], a.shape()[1]}; +} + +//============================================================================== +// Math / reduction functions +//============================================================================== + +// sum - returns a callable proxy that yields the scalar when called with () +template +struct sum_proxy { + T value; + T operator()() const { return value; } + operator T() const { return value; } +}; + +template +sum_proxy sum(const xtensor& a) +{ + T s = T(0); + for (std::size_t i = 0; i < a.size(); ++i) + s += a.data()[i]; + return {s}; +} + +template +sum_proxy sum(const xtensor_view_1d& v) +{ + T s = T(0); + for (std::size_t i = 0; i < v.size(); ++i) + s += v(i); + return {s}; +} + +template +sum_proxy sum(const xarray& a) +{ + T s = T(0); + for (std::size_t i = 0; i < a.size(); ++i) + s += a.data()[i]; + return {s}; +} + +// sum along axis - reduces one dimension +// 2D sum along axis -> 1D +template +xtensor sum(const xtensor& a, std::initializer_list axes) +{ + int axis = *axes.begin(); + auto s = a.shape(); + if (axis == 0) { + xtensor r({s[1]}, T(0)); + for (std::size_t i = 0; i < s[0]; ++i) + for (std::size_t j = 0; j < s[1]; ++j) + r(j) += a(i, j); + return r; + } else { + xtensor r({s[0]}, T(0)); + for (std::size_t i = 0; i < s[0]; ++i) + for (std::size_t j = 0; j < s[1]; ++j) + r(i) += a(i, j); + return r; + } +} + +// 3D sum along axis -> 2D +template +xtensor sum(const xtensor& a, std::initializer_list axes) +{ + int axis = *axes.begin(); + auto s = a.shape(); + if (axis == 0) { + xtensor r({s[1], s[2]}, T(0)); + for (std::size_t i = 0; i < s[0]; ++i) + for (std::size_t j = 0; j < s[1]; ++j) + for (std::size_t k = 0; k < s[2]; ++k) + r(j, k) += a(i, j, k); + return r; + } else if (axis == 1) { + xtensor r({s[0], s[2]}, T(0)); + for (std::size_t i = 0; i < s[0]; ++i) + for (std::size_t j = 0; j < s[1]; ++j) + for (std::size_t k = 0; k < s[2]; ++k) + r(i, k) += a(i, j, k); + return r; + } else { + xtensor r({s[0], s[1]}, T(0)); + for (std::size_t i = 0; i < s[0]; ++i) + for (std::size_t j = 0; j < s[1]; ++j) + for (std::size_t k = 0; k < s[2]; ++k) + r(i, j) += a(i, j, k); + return r; + } +} + +// 4D sum along axis -> 3D +template +xtensor sum(const xtensor& a, std::initializer_list axes) +{ + int axis = *axes.begin(); + auto s = a.shape(); + if (axis == 3) { + xtensor r({s[0], s[1], s[2]}, T(0)); + for (std::size_t i = 0; i < s[0]; ++i) + for (std::size_t j = 0; j < s[1]; ++j) + for (std::size_t k = 0; k < s[2]; ++k) + for (std::size_t l = 0; l < s[3]; ++l) + r(i, j, k) += a(i, j, k, l); + return r; + } else if (axis == 2) { + xtensor r({s[0], s[1], s[3]}, T(0)); + for (std::size_t i = 0; i < s[0]; ++i) + for (std::size_t j = 0; j < s[1]; ++j) + for (std::size_t k = 0; k < s[2]; ++k) + for (std::size_t l = 0; l < s[3]; ++l) + r(i, j, l) += a(i, j, k, l); + return r; + } else if (axis == 1) { + xtensor r({s[0], s[2], s[3]}, T(0)); + for (std::size_t i = 0; i < s[0]; ++i) + for (std::size_t j = 0; j < s[1]; ++j) + for (std::size_t k = 0; k < s[2]; ++k) + for (std::size_t l = 0; l < s[3]; ++l) + r(i, k, l) += a(i, j, k, l); + return r; + } else { + xtensor r({s[1], s[2], s[3]}, T(0)); + for (std::size_t i = 0; i < s[0]; ++i) + for (std::size_t j = 0; j < s[1]; ++j) + for (std::size_t k = 0; k < s[2]; ++k) + for (std::size_t l = 0; l < s[3]; ++l) + r(j, k, l) += a(i, j, k, l); + return r; + } +} + +// prod - returns a callable proxy (like sum_proxy) for prod(...)() pattern +template +struct prod_proxy { + T value; + T operator()() const { return value; } + operator T() const { return value; } +}; + +template +prod_proxy prod(const xtensor& a) +{ + T p = T(1); + for (std::size_t i = 0; i < a.size(); ++i) + p *= a.data()[i]; + return {p}; +} + +template +prod_proxy prod(const xarray& a) +{ + T p = T(1); + for (std::size_t i = 0; i < a.size(); ++i) + p *= a.data()[i]; + return {p}; +} + +// any (for bool tensors) +template +bool any(const xtensor& a) +{ + for (std::size_t i = 0; i < a.size(); ++i) + if (a.data()[i]) + return true; + return false; +} + +template +bool any(const xtensor& a) +{ + for (std::size_t i = 0; i < a.size(); ++i) + if (a.data()[i]) + return true; + return false; +} + +// any/all for xarray +template +bool any(const xarray& a) +{ + for (std::size_t i = 0; i < a.size(); ++i) + if (a.data()[i]) + return true; + return false; +} + +template +bool all(const xarray& a) +{ + for (std::size_t i = 0; i < a.size(); ++i) + if (!a.data()[i]) + return false; + return true; +} + +// all (for bool tensors) +template +bool all(const xtensor& a) +{ + for (std::size_t i = 0; i < a.size(); ++i) + if (!a.data()[i]) + return false; + return true; +} + +template +bool all(const xtensor& a) +{ + for (std::size_t i = 0; i < a.size(); ++i) + if (!a.data()[i]) + return false; + return true; +} + +// argmin - returns a subscriptable proxy for argmin(...)[0] pattern +struct argmin_result { + std::size_t value; + std::size_t operator[](std::size_t) const { return value; } + operator std::size_t() const { return value; } + operator int() const { return static_cast(value); } +}; + +template +argmin_result argmin(const xtensor& a) +{ + return {static_cast(std::distance(a.data(), + std::min_element(a.data(), a.data() + a.size())))}; +} + +template +argmin_result argmin(const xarray& a) +{ + return {static_cast(std::distance(a.data(), + std::min_element(a.data(), a.data() + a.size())))}; +} + +// Element-wise math +template +xtensor log(const xtensor& a) +{ + xtensor r(a.shape()); + for (std::size_t i = 0; i < a.size(); ++i) + r.data()[i] = std::log(a.data()[i]); + return r; +} + +template +xarray log(const xarray& a) +{ + xarray r(a.shape()); + for (std::size_t i = 0; i < a.size(); ++i) + r.data()[i] = std::log(a.data()[i]); + return r; +} + +template +xtensor exp(const xtensor& a) +{ + xtensor r(a.shape()); + for (std::size_t i = 0; i < a.size(); ++i) + r.data()[i] = std::exp(a.data()[i]); + return r; +} + +template +xtensor abs(const xtensor& a) +{ + xtensor r(a.shape()); + for (std::size_t i = 0; i < a.size(); ++i) + r.data()[i] = std::abs(a.data()[i]); + return r; +} + +template +xarray abs(const xarray& a) +{ + xarray r(a.shape()); + for (std::size_t i = 0; i < a.size(); ++i) + r.data()[i] = std::abs(a.data()[i]); + return r; +} + +// where(condition, true_val, false_val) - tensor condition +template +xtensor where(const xtensor& cond, U true_val, V false_val) +{ + xtensor r(cond.shape()); + for (std::size_t i = 0; i < cond.size(); ++i) + r.data()[i] = cond.data()[i] ? static_cast(true_val) + : static_cast(false_val); + return r; +} + +// where with tensor true_val and scalar false_val +template +xtensor where( + const xtensor& cond, const xtensor& true_val, V false_val) +{ + xtensor r(cond.shape()); + for (std::size_t i = 0; i < cond.size(); ++i) + r.data()[i] = cond.data()[i] ? true_val.data()[i] + : static_cast(false_val); + return r; +} + +// where with scalar true_val and tensor false_val +template +xtensor where( + const xtensor& cond, U true_val, const xtensor& false_val) +{ + xtensor r(cond.shape()); + for (std::size_t i = 0; i < cond.size(); ++i) + r.data()[i] = cond.data()[i] ? static_cast(true_val) + : false_val.data()[i]; + return r; +} + +// where with tensor true_val and tensor false_val +template +xtensor where(const xtensor& cond, + const xtensor& true_val, const xtensor& false_val) +{ + xtensor r(cond.shape()); + for (std::size_t i = 0; i < cond.size(); ++i) + r.data()[i] = cond.data()[i] ? true_val.data()[i] : false_val.data()[i]; + return r; +} + +// nan_to_num +template +xtensor nan_to_num(const xtensor& a, T nan_val = T(0), + T posinf_val = std::numeric_limits::max(), + T neginf_val = std::numeric_limits::lowest()) +{ + xtensor r(a.shape()); + for (std::size_t i = 0; i < a.size(); ++i) { + T val = a.data()[i]; + if (std::isnan(val)) + r.data()[i] = nan_val; + else if (std::isinf(val)) + r.data()[i] = val > 0 ? posinf_val : neginf_val; + else + r.data()[i] = val; + } + return r; +} + +// eval: returns a copy (materializes lazy expressions) +template +xtensor eval(const xtensor& a) +{ + return a; +} + +template +xarray eval(const xarray& a) +{ + return a; +} + +//============================================================================== +// concatenate & xtuple +//============================================================================== + +// Generic xtuple_holder that stores (pointer, size) pairs for any container type +template +struct xtuple_holder { + struct entry { + const T* ptr; + std::size_t size; + }; + std::array entries; + std::size_t count; +}; + +// xtuple for 2 args - accepts any container with data() and size() +template +auto xtuple(const A& a, const B& b) + -> xtuple_holder>> +{ + using T = std::remove_const_t>; + xtuple_holder h {}; + h.entries[0] = {a.data(), a.size()}; + h.entries[1] = {b.data(), b.size()}; + h.count = 2; + return h; +} + +// xtuple for 3 args +template +auto xtuple(const A& a, const B& b, const C& c) + -> xtuple_holder>> +{ + using T = std::remove_const_t>; + xtuple_holder h {}; + h.entries[0] = {a.data(), a.size()}; + h.entries[1] = {b.data(), b.size()}; + h.entries[2] = {c.data(), c.size()}; + h.count = 3; + return h; +} + +template +xtensor concatenate(const xtuple_holder& tup) +{ + std::size_t total = 0; + for (std::size_t i = 0; i < tup.count; ++i) + total += tup.entries[i].size; + + xtensor result({total}); + std::size_t pos = 0; + for (std::size_t i = 0; i < tup.count; ++i) { + std::copy(tup.entries[i].ptr, tup.entries[i].ptr + tup.entries[i].size, + result.data() + pos); + pos += tup.entries[i].size; + } + return result; +} + +// flip (reverse 1D tensor) +template +xtensor flip(const xtensor& a) +{ + xtensor r({a.size()}); + for (std::size_t i = 0; i < a.size(); ++i) + r.data()[i] = a.data()[a.size() - 1 - i]; + return r; +} + +// flip 2D along axis 0 +template +xtensor flip(const xtensor& a, std::size_t axis = 0) +{ + auto s = a.shape(); + xtensor r(s); + if (axis == 0) { + for (std::size_t i = 0; i < s[0]; ++i) + for (std::size_t j = 0; j < s[1]; ++j) + r(i, j) = a(s[0] - 1 - i, j); + } else { + for (std::size_t i = 0; i < s[0]; ++i) + for (std::size_t j = 0; j < s[1]; ++j) + r(i, j) = a(i, s[1] - 1 - j); + } + return r; +} + +//============================================================================== +// Compatibility stubs +//============================================================================== + +// is_xt_container trait for SFINAE in write_dataset +template +struct is_xt_container : std::false_type {}; + +template +struct is_xt_container> : std::true_type {}; + +template +struct is_xt_container> : std::true_type {}; + +template +struct is_xt_container> : std::true_type {}; + +// Compatibility types (unused but referenced in some template code) +template +using xbuffer_adaptor = openmc::vector>>; + +template +using xtensor_adaptor = xtensor; + +// noalias: no-op passthrough (xtensor optimization hint) +template +T& noalias(T& x) +{ + return x; +} + +} // namespace xt + +#endif // OPENMC_TENSOR_H From f570c8c7cf03455c110d0f425d878bb4fe50974b Mon Sep 17 00:00:00 2001 From: John Tramm Date: Mon, 9 Feb 2026 16:17:13 -0600 Subject: [PATCH 04/51] claude added views to the tensor.h --- include/openmc/tensor.h | 868 +++++++++++++++++++++++++++++++++++++++- src/state_point.cpp | 33 +- src/weight_windows.cpp | 49 +-- src/xsdata.cpp | 330 +++++---------- 4 files changed, 977 insertions(+), 303 deletions(-) diff --git a/include/openmc/tensor.h b/include/openmc/tensor.h index b8d019740c2..82e0acf1526 100644 --- a/include/openmc/tensor.h +++ b/include/openmc/tensor.h @@ -33,6 +33,9 @@ class xarray; template class xtensor_fixed; +template +class broadcast_view; + template struct xshape {}; @@ -645,34 +648,48 @@ class xtensor { return *this; } - // Binary ops: tensor op tensor (same type) + // Binary ops: tensor op tensor (same type, with broadcasting support) + // When shapes match, uses fast element-wise loop. When shapes are + // broadcast-compatible (dims are equal or one is 1), uses broadcast logic. xtensor operator+(const xtensor& o) const { - xtensor r(shape_); - for (std::size_t i = 0; i < data_.size(); ++i) - r.data_[i] = data_[i] + o.data_[i]; - return r; + if (shape_ == o.shape_) { + xtensor r(shape_); + for (std::size_t i = 0; i < data_.size(); ++i) + r.data_[i] = data_[i] + o.data_[i]; + return r; + } + return broadcast_op(o, [](T a, T b) { return a + b; }); } xtensor operator-(const xtensor& o) const { - xtensor r(shape_); - for (std::size_t i = 0; i < data_.size(); ++i) - r.data_[i] = data_[i] - o.data_[i]; - return r; + if (shape_ == o.shape_) { + xtensor r(shape_); + for (std::size_t i = 0; i < data_.size(); ++i) + r.data_[i] = data_[i] - o.data_[i]; + return r; + } + return broadcast_op(o, [](T a, T b) { return a - b; }); } xtensor operator*(const xtensor& o) const { - xtensor r(shape_); - for (std::size_t i = 0; i < data_.size(); ++i) - r.data_[i] = data_[i] * o.data_[i]; - return r; + if (shape_ == o.shape_) { + xtensor r(shape_); + for (std::size_t i = 0; i < data_.size(); ++i) + r.data_[i] = data_[i] * o.data_[i]; + return r; + } + return broadcast_op(o, [](T a, T b) { return a * b; }); } xtensor operator/(const xtensor& o) const { - xtensor r(shape_); - for (std::size_t i = 0; i < data_.size(); ++i) - r.data_[i] = data_[i] / o.data_[i]; - return r; + if (shape_ == o.shape_) { + xtensor r(shape_); + for (std::size_t i = 0; i < data_.size(); ++i) + r.data_[i] = data_[i] / o.data_[i]; + return r; + } + return broadcast_op(o, [](T a, T b) { return a / b; }); } // Binary ops: tensor op scalar @@ -817,7 +834,49 @@ class xtensor { return *this; } + // Assignment from broadcast_view (declared here, defined after broadcast_view) + template + xtensor& operator=(const broadcast_view& bv); + private: + // Broadcasting binary op helper for same-type xtensor with + // broadcast-compatible shapes (each dim equal or one of them is 1). + // Output shape takes the max of each dimension. + template + xtensor broadcast_op(const xtensor& o, Op op) const + { + shape_type out_shape; + shape_type strides_a, strides_b; + // Compute output shape, strides for a and b (0 for broadcast dims) + for (std::size_t d = 0; d < N; ++d) + out_shape[d] = std::max(shape_[d], o.shape_[d]); + // Compute row-major strides for output shape + strides_a[N - 1] = 1; + strides_b[N - 1] = 1; + for (int d = static_cast(N) - 2; d >= 0; --d) { + strides_a[d] = strides_a[d + 1] * shape_[d + 1]; + strides_b[d] = strides_b[d + 1] * o.shape_[d + 1]; + } + xtensor r(out_shape); + std::size_t total = r.size(); + for (std::size_t flat = 0; flat < total; ++flat) { + // Decompose flat index into multi-dim indices for output + std::size_t rem = flat; + std::size_t off_a = 0, off_b = 0; + for (std::size_t d = 0; d < N; ++d) { + std::size_t stride_out = 1; + for (std::size_t d2 = d + 1; d2 < N; ++d2) + stride_out *= out_shape[d2]; + std::size_t idx = rem / stride_out; + rem %= stride_out; + off_a += (shape_[d] == 1 ? 0 : idx) * strides_a[d]; + off_b += (o.shape_[d] == 1 ? 0 : idx) * strides_b[d]; + } + r.data_[flat] = op(data_[off_a], o.data_[off_b]); + } + return r; + } + // Offset calculations for row-major layout std::size_t offset(std::size_t i0) const { return i0; } @@ -2237,6 +2296,781 @@ xtensor flip(const xtensor& a, std::size_t axis = 0) return r; } +//============================================================================== +// newaxis sentinel type +//============================================================================== + +struct xnewaxis_type {}; + +inline xnewaxis_type newaxis() +{ + return {}; +} + +//============================================================================== +// broadcast_view: a read-only view with stride-based broadcasting +//============================================================================== +// Dimensions with stride=0 broadcast (repeat) along that axis. + +template +class broadcast_view { + const T* data_; + std::array shape_; + std::array strides_; + +public: + broadcast_view( + const T* data, std::array shape, + std::array strides) + : data_(data), shape_(shape), strides_(strides) + {} + + const std::array& shape() const { return shape_; } + std::size_t shape(std::size_t dim) const { return shape_[dim]; } + + // 1D access + template> + const T& operator()(std::size_t i0) const + { + return data_[i0 * strides_[0]]; + } + + // 2D access + template> + const T& operator()(std::size_t i0, std::size_t i1) const + { + return data_[i0 * strides_[0] + i1 * strides_[1]]; + } + + // 3D access + template> + const T& operator()(std::size_t i0, std::size_t i1, std::size_t i2) const + { + return data_[i0 * strides_[0] + i1 * strides_[1] + i2 * strides_[2]]; + } + + // 4D access + template> + const T& operator()( + std::size_t i0, std::size_t i1, std::size_t i2, std::size_t i3) const + { + return data_[i0 * strides_[0] + i1 * strides_[1] + i2 * strides_[2] + + i3 * strides_[3]]; + } +}; + +// Helper: compute row-major strides for an xtensor shape +template +std::array compute_strides(const std::array& shape) +{ + std::array strides; + strides[N - 1] = 1; + for (int i = static_cast(N) - 2; i >= 0; --i) + strides[i] = strides[i + 1] * shape[i + 1]; + return strides; +} + +//--- view() overloads with newaxis --- + +// view(1D, all, newaxis) → broadcast_view +// shape [A] → [A, 1], strides [1, 0] +template +broadcast_view view(const xtensor& a, xall_type, xnewaxis_type) +{ + return {a.data(), {a.shape(0), std::size_t(1)}, {std::size_t(1), std::size_t(0)}}; +} + +// view(1D, all, newaxis, newaxis) → broadcast_view +// shape [A] → [A, 1, 1], strides [1, 0, 0] +template +broadcast_view view( + const xtensor& a, xall_type, xnewaxis_type, xnewaxis_type) +{ + return {a.data(), {a.shape(0), std::size_t(1), std::size_t(1)}, + {std::size_t(1), std::size_t(0), std::size_t(0)}}; +} + +// view(2D, all, newaxis, all) → broadcast_view +// shape [A, G] → [A, 1, G], strides [G, 0, 1] +template +broadcast_view view( + const xtensor& a, xall_type, xnewaxis_type, xall_type) +{ + auto s = compute_strides<2>(a.shape()); + return {a.data(), {a.shape(0), std::size_t(1), a.shape(1)}, + {s[0], std::size_t(0), s[1]}}; +} + +// view(2D, all, all, newaxis) → broadcast_view +// shape [A, G] → [A, G, 1], strides [G, 1, 0] +template +broadcast_view view( + const xtensor& a, xall_type, xall_type, xnewaxis_type) +{ + auto s = compute_strides<2>(a.shape()); + return {a.data(), {a.shape(0), a.shape(1), std::size_t(1)}, + {s[0], s[1], std::size_t(0)}}; +} + +// view(2D, all, newaxis, newaxis, all) → broadcast_view +// shape [A, G] → [A, 1, 1, G], strides [G, 0, 0, 1] +template +broadcast_view view(const xtensor& a, xall_type, xnewaxis_type, + xnewaxis_type, xall_type) +{ + auto s = compute_strides<2>(a.shape()); + return {a.data(), {a.shape(0), std::size_t(1), std::size_t(1), a.shape(1)}, + {s[0], std::size_t(0), std::size_t(0), s[1]}}; +} + +// view(2D, all, all, newaxis, newaxis) → broadcast_view +// shape [A, D] → [A, D, 1, 1], strides [D, 1, 0, 0] +template +broadcast_view view(const xtensor& a, xall_type, xall_type, + xnewaxis_type, xnewaxis_type) +{ + auto s = compute_strides<2>(a.shape()); + return {a.data(), {a.shape(0), a.shape(1), std::size_t(1), std::size_t(1)}, + {s[0], s[1], std::size_t(0), std::size_t(0)}}; +} + +// view(3D, all, newaxis, all, all) → broadcast_view +// shape [A, G1, G2] → [A, 1, G1, G2], strides [G1*G2, 0, G2, 1] +template +broadcast_view view(const xtensor& a, xall_type, xnewaxis_type, + xall_type, xall_type) +{ + auto s = compute_strides<3>(a.shape()); + return {a.data(), + {a.shape(0), std::size_t(1), a.shape(1), a.shape(2)}, + {s[0], std::size_t(0), s[1], s[2]}}; +} + +// view(3D, all, all, newaxis, all) → broadcast_view +// shape [A, D, G] → [A, D, 1, G], strides [D*G, G, 0, 1] +template +broadcast_view view(const xtensor& a, xall_type, xall_type, + xnewaxis_type, xall_type) +{ + auto s = compute_strides<3>(a.shape()); + return {a.data(), + {a.shape(0), a.shape(1), std::size_t(1), a.shape(2)}, + {s[0], s[1], std::size_t(0), s[2]}}; +} + +// view(3D, all, all, all, newaxis) → broadcast_view +// shape [A, D, G] → [A, D, G, 1], strides [D*G, G, 1, 0] +template +broadcast_view view(const xtensor& a, xall_type, xall_type, + xall_type, xnewaxis_type) +{ + auto s = compute_strides<3>(a.shape()); + return {a.data(), + {a.shape(0), a.shape(1), a.shape(2), std::size_t(1)}, + {s[0], s[1], s[2], std::size_t(0)}}; +} + +//--- Arithmetic ops for broadcast_view --- + +// broadcast_view * broadcast_view → xtensor +template +xtensor operator*( + const broadcast_view& a, const broadcast_view& b) +{ + auto sa = a.shape(); + std::array out_shape; + for (int i = 0; i < 3; ++i) + out_shape[i] = std::max(sa[i], b.shape(i)); + xtensor r(out_shape); + for (std::size_t i = 0; i < out_shape[0]; ++i) + for (std::size_t j = 0; j < out_shape[1]; ++j) + for (std::size_t k = 0; k < out_shape[2]; ++k) + r(i, j, k) = a(i, sa[1] == 1 ? 0 : j, sa[2] == 1 ? 0 : k) * + b(i, b.shape(1) == 1 ? 0 : j, b.shape(2) == 1 ? 0 : k); + return r; +} + +// broadcast_view * broadcast_view → xtensor +template +xtensor operator*( + const broadcast_view& a, const broadcast_view& b) +{ + std::array out_shape; + for (int i = 0; i < 4; ++i) + out_shape[i] = std::max(a.shape(i), b.shape(i)); + xtensor r(out_shape); + for (std::size_t i = 0; i < out_shape[0]; ++i) + for (std::size_t j = 0; j < out_shape[1]; ++j) + for (std::size_t k = 0; k < out_shape[2]; ++k) + for (std::size_t l = 0; l < out_shape[3]; ++l) + r(i, j, k, l) = + a(i, a.shape(1) == 1 ? 0 : j, a.shape(2) == 1 ? 0 : k, + a.shape(3) == 1 ? 0 : l) * + b(i, b.shape(1) == 1 ? 0 : j, b.shape(2) == 1 ? 0 : k, + b.shape(3) == 1 ? 0 : l); + return r; +} + +// broadcast_view * xtensor → xtensor +template +xtensor operator*( + const broadcast_view& bv, const xtensor& t) +{ + auto s = t.shape(); + xtensor r(s); + for (std::size_t i = 0; i < s[0]; ++i) + for (std::size_t j = 0; j < s[1]; ++j) + for (std::size_t k = 0; k < s[2]; ++k) + r(i, j, k) = bv(i, bv.shape(1) == 1 ? 0 : j, + bv.shape(2) == 1 ? 0 : k) * + t(i, j, k); + return r; +} + +// xtensor * broadcast_view → xtensor +template +xtensor operator*( + const xtensor& t, const broadcast_view& bv) +{ + return bv * t; +} + +// broadcast_view * xtensor → xtensor +template +xtensor operator*( + const broadcast_view& bv, const xtensor& t) +{ + auto s = t.shape(); + xtensor r(s); + for (std::size_t i = 0; i < s[0]; ++i) + for (std::size_t j = 0; j < s[1]; ++j) + for (std::size_t k = 0; k < s[2]; ++k) + for (std::size_t l = 0; l < s[3]; ++l) + r(i, j, k, l) = + bv(i, bv.shape(1) == 1 ? 0 : j, bv.shape(2) == 1 ? 0 : k, + bv.shape(3) == 1 ? 0 : l) * + t(i, j, k, l); + return r; +} + +// xtensor * broadcast_view → xtensor +template +xtensor operator*( + const xtensor& t, const broadcast_view& bv) +{ + return bv * t; +} + +// xtensor / broadcast_view → xtensor +template +xtensor operator/( + const xtensor& t, const broadcast_view& bv) +{ + auto s = t.shape(); + xtensor r(s); + for (std::size_t i = 0; i < s[0]; ++i) + for (std::size_t j = 0; j < s[1]; ++j) + for (std::size_t k = 0; k < s[2]; ++k) + r(i, j, k) = t(i, j, k) / bv(i, bv.shape(1) == 1 ? 0 : j, + bv.shape(2) == 1 ? 0 : k); + return r; +} + +// xtensor / broadcast_view → xtensor +template +xtensor operator/( + const xtensor& t, const broadcast_view& bv) +{ + auto s = t.shape(); + xtensor r(s); + for (std::size_t i = 0; i < s[0]; ++i) + for (std::size_t j = 0; j < s[1]; ++j) + for (std::size_t k = 0; k < s[2]; ++k) + for (std::size_t l = 0; l < s[3]; ++l) + r(i, j, k, l) = + t(i, j, k, l) / + bv(i, bv.shape(1) == 1 ? 0 : j, bv.shape(2) == 1 ? 0 : k, + bv.shape(3) == 1 ? 0 : l); + return r; +} + +// xtensor /= broadcast_view +template +xtensor& operator/=(xtensor& t, const broadcast_view& bv) +{ + auto s = t.shape(); + for (std::size_t i = 0; i < s[0]; ++i) + for (std::size_t j = 0; j < s[1]; ++j) + for (std::size_t k = 0; k < s[2]; ++k) + t(i, j, k) /= bv(i, bv.shape(1) == 1 ? 0 : j, + bv.shape(2) == 1 ? 0 : k); + return t; +} + +// xtensor /= broadcast_view +template +xtensor& operator/=(xtensor& t, const broadcast_view& bv) +{ + auto s = t.shape(); + for (std::size_t i = 0; i < s[0]; ++i) + for (std::size_t j = 0; j < s[1]; ++j) + for (std::size_t k = 0; k < s[2]; ++k) + for (std::size_t l = 0; l < s[3]; ++l) + t(i, j, k, l) /= + bv(i, bv.shape(1) == 1 ? 0 : j, bv.shape(2) == 1 ? 0 : k, + bv.shape(3) == 1 ? 0 : l); + return t; +} + +// scalar * broadcast_view → broadcast_view stores a copy, so return xtensor +template +xtensor operator*(T val, const broadcast_view& bv) +{ + auto s = bv.shape(); + xtensor r(s); + for (std::size_t i = 0; i < s[0]; ++i) + for (std::size_t j = 0; j < s[1]; ++j) + for (std::size_t k = 0; k < s[2]; ++k) + r(i, j, k) = val * bv(i, j, k); + return r; +} + +// scalar * broadcast_view → xtensor +template +xtensor operator*(T val, const broadcast_view& bv) +{ + auto s = bv.shape(); + xtensor r(s); + for (std::size_t i = 0; i < s[0]; ++i) + for (std::size_t j = 0; j < s[1]; ++j) + for (std::size_t k = 0; k < s[2]; ++k) + for (std::size_t l = 0; l < s[3]; ++l) + r(i, j, k, l) = val * bv(i, j, k, l); + return r; +} + +// xtensor * broadcast_view → xtensor +template +xtensor operator*( + const xtensor& t, const broadcast_view& bv) +{ + auto s = t.shape(); + xtensor r(s); + for (std::size_t i = 0; i < s[0]; ++i) + for (std::size_t j = 0; j < s[1]; ++j) + r(i, j) = t(i, j) * bv(i, bv.shape(1) == 1 ? 0 : j); + return r; +} + +// broadcast_view * xtensor → xtensor +template +xtensor operator*( + const broadcast_view& bv, const xtensor& t) +{ + return t * bv; +} + +// xtensor /= broadcast_view +template +xtensor& operator/=(xtensor& t, const broadcast_view& bv) +{ + auto s = t.shape(); + for (std::size_t i = 0; i < s[0]; ++i) + for (std::size_t j = 0; j < s[1]; ++j) + t(i, j) /= bv(i, bv.shape(1) == 1 ? 0 : j); + return t; +} + +// xtensor / broadcast_view → xtensor +template +xtensor operator/( + const xtensor& t, const broadcast_view& bv) +{ + auto s = t.shape(); + xtensor r(s); + for (std::size_t i = 0; i < s[0]; ++i) + for (std::size_t j = 0; j < s[1]; ++j) + r(i, j) = t(i, j) / bv(i, bv.shape(1) == 1 ? 0 : j); + return r; +} + +// xtensor::operator= from broadcast_view (deferred definition) +// Broadcasts the view into the existing tensor shape. Each dimension of the +// broadcast_view is either 1 (broadcast) or matches the target dimension. +// The target tensor shape is NOT changed — broadcasting fills in the values. +template +template +xtensor& xtensor::operator=(const broadcast_view& bv) +{ + static_assert(N == BN, "broadcast_view dimension must match xtensor dimension"); + auto bvs = bv.shape(); + // Use existing shape, broadcasting singleton dims from the view + if constexpr (N == 2) { + for (std::size_t i = 0; i < shape_[0]; ++i) + for (std::size_t j = 0; j < shape_[1]; ++j) + (*this)(i, j) = bv(bvs[0] == 1 ? 0 : i, bvs[1] == 1 ? 0 : j); + } else if constexpr (N == 3) { + for (std::size_t i = 0; i < shape_[0]; ++i) + for (std::size_t j = 0; j < shape_[1]; ++j) + for (std::size_t k = 0; k < shape_[2]; ++k) + (*this)(i, j, k) = bv(bvs[0] == 1 ? 0 : i, + bvs[1] == 1 ? 0 : j, bvs[2] == 1 ? 0 : k); + } else if constexpr (N == 4) { + for (std::size_t i = 0; i < shape_[0]; ++i) + for (std::size_t j = 0; j < shape_[1]; ++j) + for (std::size_t k = 0; k < shape_[2]; ++k) + for (std::size_t l = 0; l < shape_[3]; ++l) + (*this)(i, j, k, l) = bv(bvs[0] == 1 ? 0 : i, + bvs[1] == 1 ? 0 : j, bvs[2] == 1 ? 0 : k, + bvs[3] == 1 ? 0 : l); + } + return *this; +} + +// Conversion helper: materialize a broadcast_view into an xtensor +template +xtensor eval(const broadcast_view& bv) +{ + auto s = bv.shape(); + xtensor r(s); + for (std::size_t i = 0; i < s[0]; ++i) + for (std::size_t j = 0; j < s[1]; ++j) + for (std::size_t k = 0; k < s[2]; ++k) + r(i, j, k) = bv(i, j, k); + return r; +} + +template +xtensor eval(const broadcast_view& bv) +{ + auto s = bv.shape(); + xtensor r(s); + for (std::size_t i = 0; i < s[0]; ++i) + for (std::size_t j = 0; j < s[1]; ++j) + for (std::size_t k = 0; k < s[2]; ++k) + for (std::size_t l = 0; l < s[3]; ++l) + r(i, j, k, l) = bv(i, j, k, l); + return r; +} + +//============================================================================== +// Cross-dimension broadcasting: xtensor op xtensor +// Broadcasts 1D along axis 0 (each row scaled by corresponding 1D element) +//============================================================================== + +template +xtensor operator*(const xtensor& a, const xtensor& b) +{ + xtensor r(a.shape()); + for (std::size_t i = 0; i < a.shape(0); ++i) + for (std::size_t j = 0; j < a.shape(1); ++j) + r(i, j) = a(i, j) * b(i); + return r; +} + +template +xtensor operator*(const xtensor& b, const xtensor& a) +{ + return a * b; +} + +// xtensor * xtensor → broadcast 1D along axis 0 +template +xtensor operator*(const xtensor& a, const xtensor& b) +{ + xtensor r(a.shape()); + for (std::size_t i = 0; i < a.shape(0); ++i) + for (std::size_t j = 0; j < a.shape(1); ++j) + for (std::size_t k = 0; k < a.shape(2); ++k) + r(i, j, k) = a(i, j, k) * b(i); + return r; +} + +template +xtensor operator*(const xtensor& b, const xtensor& a) +{ + return a * b; +} + +// xtensor * xtensor → broadcast 2D along axes 0,1 +// e.g., [A, D, G] * [A, G] → each (a, d, g) *= (a, g) +template +xtensor operator*(const xtensor& a, const xtensor& b) +{ + xtensor r(a.shape()); + // Determine broadcasting: if b.shape matches a's last 2 dims, broadcast along them + // If b.shape(0) == a.shape(0) and b.shape(1) == a.shape(2), broadcast as (a, _, g) + if (b.shape(0) == a.shape(0) && b.shape(1) == a.shape(2)) { + for (std::size_t i = 0; i < a.shape(0); ++i) + for (std::size_t j = 0; j < a.shape(1); ++j) + for (std::size_t k = 0; k < a.shape(2); ++k) + r(i, j, k) = a(i, j, k) * b(i, k); + } else { + // Default: broadcast b's dims matching a's first 2 dims + for (std::size_t i = 0; i < a.shape(0); ++i) + for (std::size_t j = 0; j < a.shape(1); ++j) + for (std::size_t k = 0; k < a.shape(2); ++k) + r(i, j, k) = a(i, j, k) * b(i, j); + } + return r; +} + +template +xtensor operator*(const xtensor& b, const xtensor& a) +{ + return a * b; +} + +//============================================================================== +// equal() and filtration() for masked assignment +//============================================================================== + +// equal(tensor, val) → bool tensor +template +xtensor equal(const xtensor& a, T val) +{ + xtensor r(a.shape()); + for (std::size_t i = 0; i < a.size(); ++i) + r.data()[i] = (a.data()[i] == val); + return r; +} + +template +xarray equal(const xarray& a, T val) +{ + xarray r(a.shape()); + for (std::size_t i = 0; i < a.size(); ++i) + r.data()[i] = (a.data()[i] == val); + return r; +} + +// filtration_proxy: assigning to it sets elements where mask is true +template +class filtration_proxy { + T* data_; + const MaskContainer& mask_; + std::size_t size_; + +public: + filtration_proxy(T* data, const MaskContainer& mask, std::size_t size) + : data_(data), mask_(mask), size_(size) + {} + + template + filtration_proxy& operator=(U val) + { + for (std::size_t i = 0; i < size_; ++i) + if (mask_.data()[i]) + data_[i] = static_cast(val); + return *this; + } +}; + +template +filtration_proxy> filtration( + xtensor& a, const xtensor& mask) +{ + return {a.data(), mask, a.size()}; +} + +template +filtration_proxy> filtration( + xarray& a, const xarray& mask) +{ + return {a.data(), mask, a.size()}; +} + +//============================================================================== +// range_view_3d: view of xtensor sliced on last dimension +//============================================================================== + +template +class range_view_3d { + T* data_; + std::size_t s0_, s1_, s2_full_; // original shape dims + std::size_t k_start_, k_count_; + +public: + range_view_3d(T* data, std::size_t s0, std::size_t s1, std::size_t s2_full, + std::size_t k_start, std::size_t k_count) + : data_(data), s0_(s0), s1_(s1), s2_full_(s2_full), k_start_(k_start), + k_count_(k_count) + {} + + // Convert to xtensor (copy out) + operator xtensor, 3>() const + { + using U = std::remove_const_t; + xtensor r({s0_, s1_, k_count_}); + for (std::size_t i = 0; i < s0_; ++i) + for (std::size_t j = 0; j < s1_; ++j) + for (std::size_t k = 0; k < k_count_; ++k) + r(i, j, k) = data_[(i * s1_ + j) * s2_full_ + k_start_ + k]; + return r; + } + + // Assign from xtensor (copy in) + template + range_view_3d& operator=(const xtensor& src) + { + for (std::size_t i = 0; i < s0_; ++i) + for (std::size_t j = 0; j < s1_; ++j) + for (std::size_t k = 0; k < k_count_; ++k) + data_[(i * s1_ + j) * s2_full_ + k_start_ + k] = src(i, j, k); + return *this; + } +}; + +// view(3D, all, all, range) → range_view_3d +template +range_view_3d view( + xtensor& a, xall_type, xall_type, xrange_type r) +{ + auto start = resolve_end(r.start, a.shape(2)); + auto stop = resolve_end(r.stop, a.shape(2)); + return {a.data(), a.shape(0), a.shape(1), a.shape(2), start, stop - start}; +} + +template +range_view_3d view( + const xtensor& a, xall_type, xall_type, xrange_type r) +{ + auto start = resolve_end(r.start, a.shape(2)); + auto stop = resolve_end(r.stop, a.shape(2)); + return {a.data(), a.shape(0), a.shape(1), a.shape(2), start, stop - start}; +} + +//============================================================================== +// reshape_view, transpose, dynamic_view (for weight_windows.cpp) +//============================================================================== + +// reshape_view: view of contiguous data with new shape (no copy) +template +class reshaped_view { + const T* data_; + openmc::vector shape_; + openmc::vector strides_; + +public: + reshaped_view(const T* data, const openmc::vector& shape) + : data_(data), shape_(shape) + { + // Compute row-major strides + strides_.resize(shape_.size()); + if (!shape_.empty()) { + strides_.back() = 1; + for (int i = static_cast(shape_.size()) - 2; i >= 0; --i) + strides_[i] = strides_[i + 1] * shape_[i + 1]; + } + } + + const openmc::vector& shape() const { return shape_; } + const openmc::vector& strides() const { return strides_; } + const T* data() const { return data_; } + std::size_t dimension() const { return shape_.size(); } +}; + +template +reshaped_view reshape_view(const xtensor& a, const ShapeType& shape) +{ + openmc::vector s; + for (auto d : shape) + s.push_back(static_cast(d)); + return {a.data(), s}; +} + +template +reshaped_view reshape_view(const xarray& a, const ShapeType& shape) +{ + openmc::vector s; + for (auto d : shape) + s.push_back(static_cast(d)); + return {a.data(), s}; +} + +// transposed_view: permuted-dimension view +template +class transposed_view { + const T* data_; + openmc::vector shape_; // permuted shape + openmc::vector orig_strides_; // original strides permuted + +public: + transposed_view(const T* data, const openmc::vector& orig_shape, + const openmc::vector& orig_strides, + const openmc::vector& perm) + : data_(data) + { + auto ndim = perm.size(); + shape_.resize(ndim); + orig_strides_.resize(ndim); + for (std::size_t i = 0; i < ndim; ++i) { + shape_[i] = orig_shape[perm[i]]; + orig_strides_[i] = orig_strides[perm[i]]; + } + } + + const openmc::vector& shape() const { return shape_; } + const T* data() const { return data_; } + const openmc::vector& strides() const { return orig_strides_; } +}; + +template +transposed_view transpose(const reshaped_view& rv, const PermType& perm) +{ + openmc::vector p; + for (auto d : perm) + p.push_back(static_cast(d)); + return {rv.data(), rv.shape(), rv.strides(), p}; +} + +// dynamic_slice_arg: either an int index or all() +struct dynamic_slice_arg { + bool is_all; + int value; + + dynamic_slice_arg(int v) : is_all(false), value(v) {} + dynamic_slice_arg(xall_type) : is_all(true), value(0) {} +}; + +// dynamic_view: extract a 2D slice from a transposed_view using a mix of +// integer indices and all() slices. +template +xtensor dynamic_view(const transposed_view& tv, + std::initializer_list args_init) +{ + openmc::vector args(args_init); + auto& shape = tv.shape(); + auto& strides = tv.strides(); + + // Find the two "all" dimensions + std::size_t all_dim0 = 0, all_dim1 = 0; + int all_count = 0; + std::size_t base_offset = 0; + for (std::size_t i = 0; i < args.size(); ++i) { + if (args[i].is_all) { + if (all_count == 0) + all_dim0 = i; + else + all_dim1 = i; + all_count++; + } else { + base_offset += static_cast(args[i].value) * strides[i]; + } + } + + std::size_t n0 = shape[all_dim0]; + std::size_t n1 = shape[all_dim1]; + std::size_t st0 = strides[all_dim0]; + std::size_t st1 = strides[all_dim1]; + + xtensor result({n0, n1}); + for (std::size_t i = 0; i < n0; ++i) + for (std::size_t j = 0; j < n1; ++j) + result(i, j) = tv.data()[base_offset + i * st0 + j * st1]; + return result; +} + //============================================================================== // Compatibility stubs //============================================================================== diff --git a/src/state_point.cpp b/src/state_point.cpp index f970d33b6ef..3d0dbdca509 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -917,19 +917,13 @@ void write_tally_results_nr(hid_t file_id) write_attribute(file_id, "tallies_present", 1); } - // Extract sub-range indices for SUM and SUM_SQ - std::size_t k_start = static_cast(TallyResult::SUM); - std::size_t k_end = static_cast(TallyResult::SUM_SQ) + 1; - std::size_t n_k = k_end - k_start; - std::size_t dim0 = t->results_.shape(0); - std::size_t dim1 = t->results_.shape(1); - - // Make copy of accumulated tally values in contiguous array - xt::xtensor values({dim0, dim1, n_k}); - for (std::size_t i = 0; i < dim0; ++i) - for (std::size_t j = 0; j < dim1; ++j) - for (std::size_t k = 0; k < n_k; ++k) - values(i, j, k) = t->results_(i, j, k_start + k); + // Get view of accumulated tally values + auto values_view = xt::view(t->results_, xt::all(), xt::all(), + xt::range(static_cast(TallyResult::SUM), + static_cast(TallyResult::SUM_SQ) + 1)); + + // Make copy of tally values in contiguous array + xt::xtensor values = values_view; if (mpi::master) { // Open group for tally @@ -947,18 +941,15 @@ void write_tally_results_nr(hid_t file_id) // regular TallyResults array if (simulation::current_batch == settings::n_max_batches || simulation::satisfy_triggers) { - for (std::size_t i = 0; i < dim0; ++i) - for (std::size_t j = 0; j < dim1; ++j) - for (std::size_t k = 0; k < n_k; ++k) - t->results_(i, j, k_start + k) = values(i, j, k); + values_view = values; } // Put in temporary tally result xt::xtensor results_copy = xt::zeros_like(t->results_); - for (std::size_t i = 0; i < dim0; ++i) - for (std::size_t j = 0; j < dim1; ++j) - for (std::size_t k = 0; k < n_k; ++k) - results_copy(i, j, k_start + k) = values(i, j, k); + auto copy_view = xt::view(results_copy, xt::all(), xt::all(), + xt::range(static_cast(TallyResult::SUM), + static_cast(TallyResult::SUM_SQ) + 1)); + copy_view = values; // Write reduced tally results to file auto shape = results_copy.shape(); diff --git a/src/weight_windows.cpp b/src/weight_windows.cpp index ed07f47489e..3028bb46e66 100644 --- a/src/weight_windows.cpp +++ b/src/weight_windows.cpp @@ -583,6 +583,13 @@ void WeightWindows::update_weights(const Tally* tally, const std::string& value, std::find(filter_types.begin(), filter_types.end(), FilterType::MESH) - filter_types.begin(); + // get a fully reshaped view of the tally according to tally ordering of + // filters + auto tally_values = xt::reshape_view(results_arr, shape); + + // get a that is (particle, energy, mesh, scores, values) + auto transposed_view = xt::transpose(tally_values, transpose); + // determine the dimension and index of the particle data int particle_idx = 0; if (tally->has_filter(FilterType::PARTICLE)) { @@ -606,41 +613,13 @@ void WeightWindows::update_weights(const Tally* tally, const std::string& value, particle_idx = p_it - particles.begin(); } - // Compute strides for the reshaped 5D layout (filter dims, scores, results) - // shape = {f0_bins, f1_bins, f2_bins, n_scores, results_dim} - std::array strides; - strides[4] = 1; - for (int i = 3; i >= 0; --i) - strides[i] = strides[i + 1] * shape[i + 1]; - - // Helper lambda: compute flat index from logical (particle, energy, mesh) - // into results_arr, accounting for the transpose mapping - auto get_result = [&](int p, int e, int m, int score, int result_type) - -> double { - // Map logical indices to physical filter positions - std::array idx = {0, 0, 0, score, result_type}; - idx[transpose[0]] = p; - idx[transpose[1]] = e; - idx[transpose[2]] = m; - // Compute flat filter_bin from the first 3 filter dimensions - int filter_bin = idx[0] * shape[1] * shape[2] + idx[1] * shape[2] + idx[2]; - return results_arr(filter_bin, score, result_type); - }; - - // Extract 2D sum and sum_sq arrays for (energy, mesh) at given particle and - // score - int sum_idx = static_cast(TallyResult::SUM); - int sum_sq_idx = static_cast(TallyResult::SUM_SQ); - xt::xtensor sum({static_cast(e_bins), - static_cast(mesh_bins)}); - xt::xtensor sum_sq({static_cast(e_bins), - static_cast(mesh_bins)}); - for (int e = 0; e < e_bins; e++) { - for (int64_t m = 0; m < mesh_bins; m++) { - sum(e, m) = get_result(particle_idx, e, m, score_index, sum_idx); - sum_sq(e, m) = get_result(particle_idx, e, m, score_index, sum_sq_idx); - } - } + // down-select data based on particle and score + auto sum = xt::dynamic_view( + transposed_view, {particle_idx, xt::all(), xt::all(), score_index, + static_cast(TallyResult::SUM)}); + auto sum_sq = xt::dynamic_view( + transposed_view, {particle_idx, xt::all(), xt::all(), score_index, + static_cast(TallyResult::SUM_SQ)}); int n = tally->n_realizations_; ////////////////////////////////////////////// diff --git a/src/xsdata.cpp b/src/xsdata.cpp index 6317a1254c5..83eed7be2a0 100644 --- a/src/xsdata.cpp +++ b/src/xsdata.cpp @@ -99,9 +99,7 @@ void XsData::from_hdf5(hid_t xsdata_grp, bool fissionable, // Check absorption to ensure it is not 0 since it is often the // denominator in tally methods - for (size_t i = 0; i < absorption.size(); ++i) - if (absorption.data()[i] == 0.) - absorption.data()[i] = 1.e-10; + xt::filtration(absorption, xt::equal(absorption, 0.)) = 1.e-10; // Get or calculate the total x/s if (object_exists(xsdata_grp, "total")) { @@ -115,9 +113,7 @@ void XsData::from_hdf5(hid_t xsdata_grp, bool fissionable, } // Fix if total is 0, since it is in the denominator when tallying - for (size_t i = 0; i < total.size(); ++i) - if (total.data()[i] == 0.) - total.data()[i] = 1.e-10; + xt::filtration(total, xt::equal(total, 0.)) = 1.e-10; } //============================================================================== @@ -132,25 +128,13 @@ void XsData::fission_vector_beta_from_hdf5( read_nd_vector(xsdata_grp, "chi", temp_chi, true); // Normalize chi by summing over the outgoing groups for each incoming angle - for (size_t a = 0; a < n_ang; ++a) { - double row_sum = 0.0; - for (size_t g = 0; g < n_g_; ++g) - row_sum += temp_chi(a, g); - for (size_t g = 0; g < n_g_; ++g) - temp_chi(a, g) /= row_sum; - } + temp_chi /= xt::view(xt::sum(temp_chi, {1}), xt::all(), xt::newaxis()); // Now every incoming group in prompt_chi and delayed_chi is the normalized - // chi we just made (broadcast 2D -> 3D and 2D -> 4D) - for (size_t a = 0; a < n_ang; ++a) - for (size_t gin = 0; gin < n_g_; ++gin) - for (size_t gout = 0; gout < n_g_; ++gout) - chi_prompt(a, gin, gout) = temp_chi(a, gout); - for (size_t a = 0; a < n_ang; ++a) - for (size_t d = 0; d < n_dg_; ++d) - for (size_t gin = 0; gin < n_g_; ++gin) - for (size_t gout = 0; gout < n_g_; ++gout) - chi_delayed(a, d, gin, gout) = temp_chi(a, gout); + // chi we just made + chi_prompt = xt::view(temp_chi, xt::all(), xt::newaxis(), xt::all()); + chi_delayed = + xt::view(temp_chi, xt::all(), xt::newaxis(), xt::newaxis(), xt::all()); // Get nu-fission xt::xtensor temp_nufiss({n_ang, n_g_}, 0.); @@ -168,41 +152,22 @@ void XsData::fission_vector_beta_from_hdf5( read_nd_vector(xsdata_grp, "beta", temp_beta, true); // Set prompt_nu_fission = (1. - beta_total)*nu_fission - // beta_total = sum over delayed groups (axis 1) of temp_beta - for (size_t a = 0; a < n_ang; ++a) { - double beta_total = 0.0; - for (size_t d = 0; d < n_dg_; ++d) - beta_total += temp_beta(a, d); - for (size_t g = 0; g < n_g_; ++g) - prompt_nu_fission(a, g) = temp_nufiss(a, g) * (1.0 - beta_total); - } + prompt_nu_fission = temp_nufiss * (1. - xt::sum(temp_beta, {1})); // Set delayed_nu_fission as beta * nu_fission - // delayed_nu_fission(a, d, g) = temp_beta(a, d) * temp_nufiss(a, g) - for (size_t a = 0; a < n_ang; ++a) - for (size_t d = 0; d < n_dg_; ++d) - for (size_t g = 0; g < n_g_; ++g) - delayed_nu_fission(a, d, g) = temp_beta(a, d) * temp_nufiss(a, g); + delayed_nu_fission = + xt::view(temp_beta, xt::all(), xt::all(), xt::newaxis()) * + xt::view(temp_nufiss, xt::all(), xt::newaxis(), xt::all()); } else if (beta_ndims == ndim_target + 1) { xt::xtensor temp_beta({n_ang, n_dg_, n_g_}, 0.); read_nd_vector(xsdata_grp, "beta", temp_beta, true); // Set prompt_nu_fission = (1. - beta_total)*nu_fission - // beta_total = sum over delayed groups (axis 1) of 3D temp_beta - for (size_t a = 0; a < n_ang; ++a) - for (size_t g = 0; g < n_g_; ++g) { - double beta_total = 0.0; - for (size_t d = 0; d < n_dg_; ++d) - beta_total += temp_beta(a, d, g); - prompt_nu_fission(a, g) = temp_nufiss(a, g) * (1.0 - beta_total); - } + prompt_nu_fission = temp_nufiss * (1. - xt::sum(temp_beta, {1})); // Set delayed_nu_fission as beta * nu_fission - // delayed_nu_fission(a, d, g) = temp_beta(a, d, g) * temp_nufiss(a, g) - for (size_t a = 0; a < n_ang; ++a) - for (size_t d = 0; d < n_dg_; ++d) - for (size_t g = 0; g < n_g_; ++g) - delayed_nu_fission(a, d, g) = temp_beta(a, d, g) * temp_nufiss(a, g); + delayed_nu_fission = + temp_beta * xt::view(temp_nufiss, xt::all(), xt::newaxis(), xt::all()); } } @@ -215,39 +180,21 @@ void XsData::fission_vector_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) read_nd_vector(xsdata_grp, "chi-prompt", temp_chi_p, true); // Normalize chi by summing over the outgoing groups for each incoming angle - for (size_t a = 0; a < n_ang; ++a) { - double row_sum = 0.0; - for (size_t g = 0; g < n_g_; ++g) - row_sum += temp_chi_p(a, g); - for (size_t g = 0; g < n_g_; ++g) - temp_chi_p(a, g) /= row_sum; - } + temp_chi_p /= xt::view(xt::sum(temp_chi_p, {1}), xt::all(), xt::newaxis()); // Get chi-delayed xt::xtensor temp_chi_d({n_ang, n_dg_, n_g_}, 0.); read_nd_vector(xsdata_grp, "chi-delayed", temp_chi_d, true); - // Normalize chi by summing over the outgoing groups for each delayed group - for (size_t a = 0; a < n_ang; ++a) - for (size_t d = 0; d < n_dg_; ++d) { - double grp_sum = 0.0; - for (size_t g = 0; g < n_g_; ++g) - grp_sum += temp_chi_d(a, d, g); - for (size_t g = 0; g < n_g_; ++g) - temp_chi_d(a, d, g) /= grp_sum; - } + // Normalize chi by summing over the outgoing groups for each incoming angle + temp_chi_d /= + xt::view(xt::sum(temp_chi_d, {2}), xt::all(), xt::all(), xt::newaxis()); // Now assign the prompt and delayed chis by replicating for each incoming - // group (broadcast 2D -> 3D and 3D -> 4D) - for (size_t a = 0; a < n_ang; ++a) - for (size_t gin = 0; gin < n_g_; ++gin) - for (size_t gout = 0; gout < n_g_; ++gout) - chi_prompt(a, gin, gout) = temp_chi_p(a, gout); - for (size_t a = 0; a < n_ang; ++a) - for (size_t d = 0; d < n_dg_; ++d) - for (size_t gin = 0; gin < n_g_; ++gin) - for (size_t gout = 0; gout < n_g_; ++gout) - chi_delayed(a, d, gin, gout) = temp_chi_d(a, d, gout); + // group + chi_prompt = xt::view(temp_chi_p, xt::all(), xt::newaxis(), xt::all()); + chi_delayed = + xt::view(temp_chi_d, xt::all(), xt::all(), xt::newaxis(), xt::all()); // Get prompt and delayed nu-fission directly read_nd_vector(xsdata_grp, "prompt-nu-fission", prompt_nu_fission, true); @@ -264,20 +211,10 @@ void XsData::fission_vector_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang) read_nd_vector(xsdata_grp, "chi", temp_chi, true); // Normalize chi by summing over the outgoing groups for each incoming angle - for (size_t a = 0; a < n_ang; ++a) { - double row_sum = 0.0; - for (size_t g = 0; g < n_g_; ++g) - row_sum += temp_chi(a, g); - for (size_t g = 0; g < n_g_; ++g) - temp_chi(a, g) /= row_sum; - } + temp_chi /= xt::view(xt::sum(temp_chi, {1}), xt::all(), xt::newaxis()); // Now every incoming group in self.chi is the normalized chi we just made - // (broadcast 2D -> 3D) - for (size_t a = 0; a < n_ang; ++a) - for (size_t gin = 0; gin < n_g_; ++gin) - for (size_t gout = 0; gout < n_g_; ++gout) - chi_prompt(a, gin, gout) = temp_chi(a, gout); + chi_prompt = xt::view(temp_chi, xt::all(), xt::newaxis(), xt::all()); // Get nu-fission directly read_nd_vector(xsdata_grp, "nu-fission", prompt_nu_fission, true); @@ -305,92 +242,62 @@ void XsData::fission_matrix_beta_from_hdf5( xt::xtensor temp_beta({n_ang, n_dg_}, 0.); read_nd_vector(xsdata_grp, "beta", temp_beta, true); - // temp_beta_sum(a) = sum over delayed groups of temp_beta(a, d) - auto temp_beta_sum = xt::sum(temp_beta, {1}); - // matrix_sum(a, gin) = sum over outgoing groups of temp_matrix(a, gin, gout) - auto matrix_sum = xt::sum(temp_matrix, {2}); - - // prompt_nu_fission(a, g) = matrix_sum(a, g) * (1 - beta_sum(a)) - for (size_t a = 0; a < n_ang; ++a) - for (size_t g = 0; g < n_g_; ++g) - prompt_nu_fission(a, g) = matrix_sum(a, g) * (1.0 - temp_beta_sum(a)); - - // chi_prompt(a, gin, gout) = (1 - beta_sum(a)) * matrix(a, gin, gout) - for (size_t a = 0; a < n_ang; ++a) - for (size_t gin = 0; gin < n_g_; ++gin) - for (size_t gout = 0; gout < n_g_; ++gout) - chi_prompt(a, gin, gout) = - (1.0 - temp_beta_sum(a)) * temp_matrix(a, gin, gout); - - // delayed_nu_fission(a, d, g) = beta(a, d) * matrix_sum(a, g) - for (size_t a = 0; a < n_ang; ++a) - for (size_t d = 0; d < n_dg_; ++d) - for (size_t g = 0; g < n_g_; ++g) - delayed_nu_fission(a, d, g) = temp_beta(a, d) * matrix_sum(a, g); - - // chi_delayed(a, d, gin, gout) = beta(a, d) * matrix(a, gin, gout) - for (size_t a = 0; a < n_ang; ++a) - for (size_t d = 0; d < n_dg_; ++d) - for (size_t gin = 0; gin < n_g_; ++gin) - for (size_t gout = 0; gout < n_g_; ++gout) - chi_delayed(a, d, gin, gout) = - temp_beta(a, d) * temp_matrix(a, gin, gout); + xt::xtensor temp_beta_sum({n_ang}, 0.); + temp_beta_sum = xt::sum(temp_beta, {1}); + + // prompt_nu_fission is the sum of this matrix over outgoing groups and + // multiplied by (1 - beta_sum) + prompt_nu_fission = xt::sum(temp_matrix, {2}) * (1. - temp_beta_sum); + + // Store chi-prompt + chi_prompt = + xt::view(1.0 - temp_beta_sum, xt::all(), xt::newaxis(), xt::newaxis()) * + temp_matrix; + + // delayed_nu_fission is the sum of this matrix over outgoing groups and + // multiplied by beta + delayed_nu_fission = + xt::view(temp_beta, xt::all(), xt::all(), xt::newaxis()) * + xt::view(xt::sum(temp_matrix, {2}), xt::all(), xt::newaxis(), xt::all()); + + // Store chi-delayed + chi_delayed = + xt::view(temp_beta, xt::all(), xt::all(), xt::newaxis(), xt::newaxis()) * + xt::view(temp_matrix, xt::all(), xt::newaxis(), xt::all(), xt::all()); } else if (beta_ndims == ndim_target + 1) { xt::xtensor temp_beta({n_ang, n_dg_, n_g_}, 0.); read_nd_vector(xsdata_grp, "beta", temp_beta, true); - // temp_beta_sum(a, g) = sum over delayed groups of temp_beta(a, d, g) - auto temp_beta_sum = xt::sum(temp_beta, {1}); - auto matrix_sum = xt::sum(temp_matrix, {2}); - - // prompt_nu_fission(a, g) = matrix_sum(a, g) * (1 - beta_sum(a, g)) - for (size_t a = 0; a < n_ang; ++a) - for (size_t g = 0; g < n_g_; ++g) - prompt_nu_fission(a, g) = matrix_sum(a, g) * (1.0 - temp_beta_sum(a, g)); - - // chi_prompt(a, gin, gout) = (1 - beta_sum(a, gin)) * matrix(a, gin, gout) - for (size_t a = 0; a < n_ang; ++a) - for (size_t gin = 0; gin < n_g_; ++gin) - for (size_t gout = 0; gout < n_g_; ++gout) - chi_prompt(a, gin, gout) = - (1.0 - temp_beta_sum(a, gin)) * temp_matrix(a, gin, gout); - - // delayed_nu_fission(a, d, g) = beta(a, d, g) * matrix_sum(a, g) - for (size_t a = 0; a < n_ang; ++a) - for (size_t d = 0; d < n_dg_; ++d) - for (size_t g = 0; g < n_g_; ++g) - delayed_nu_fission(a, d, g) = temp_beta(a, d, g) * matrix_sum(a, g); - - // chi_delayed(a, d, gin, gout) = beta(a, d, gin) * matrix(a, gin, gout) - for (size_t a = 0; a < n_ang; ++a) - for (size_t d = 0; d < n_dg_; ++d) - for (size_t gin = 0; gin < n_g_; ++gin) - for (size_t gout = 0; gout < n_g_; ++gout) - chi_delayed(a, d, gin, gout) = - temp_beta(a, d, gin) * temp_matrix(a, gin, gout); + xt::xtensor temp_beta_sum({n_ang, n_g_}, 0.); + temp_beta_sum = xt::sum(temp_beta, {1}); + + // prompt_nu_fission is the sum of this matrix over outgoing groups and + // multiplied by (1 - beta_sum) + prompt_nu_fission = xt::sum(temp_matrix, {2}) * (1. - temp_beta_sum); + + // Store chi-prompt + chi_prompt = + xt::view(1.0 - temp_beta_sum, xt::all(), xt::all(), xt::newaxis()) * + temp_matrix; + + // delayed_nu_fission is the sum of this matrix over outgoing groups and + // multiplied by beta + delayed_nu_fission = temp_beta * xt::view(xt::sum(temp_matrix, {2}), + xt::all(), xt::newaxis(), xt::all()); + + // Store chi-delayed + chi_delayed = + xt::view(temp_beta, xt::all(), xt::all(), xt::all(), xt::newaxis()) * + xt::view(temp_matrix, xt::all(), xt::newaxis(), xt::all(), xt::all()); } - // Normalize both chis: chi_prompt(a, gin, gout) /= sum_over_gout - for (size_t a = 0; a < n_ang; ++a) - for (size_t gin = 0; gin < n_g_; ++gin) { - double s = 0.0; - for (size_t gout = 0; gout < n_g_; ++gout) - s += chi_prompt(a, gin, gout); - for (size_t gout = 0; gout < n_g_; ++gout) - chi_prompt(a, gin, gout) /= s; - } + // Normalize both chis + chi_prompt /= + xt::view(xt::sum(chi_prompt, {2}), xt::all(), xt::all(), xt::newaxis()); - // chi_delayed(a, d, gin, gout) /= sum_over_gout - for (size_t a = 0; a < n_ang; ++a) - for (size_t d = 0; d < n_dg_; ++d) - for (size_t gin = 0; gin < n_g_; ++gin) { - double s = 0.0; - for (size_t gout = 0; gout < n_g_; ++gout) - s += chi_delayed(a, d, gin, gout); - for (size_t gout = 0; gout < n_g_; ++gout) - chi_delayed(a, d, gin, gout) /= s; - } + chi_delayed /= xt::view( + xt::sum(chi_delayed, {3}), xt::all(), xt::all(), xt::all(), xt::newaxis()); } void XsData::fission_matrix_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) @@ -404,12 +311,10 @@ void XsData::fission_matrix_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) // prompt_nu_fission is the sum over outgoing groups prompt_nu_fission = xt::sum(temp_matrix_p, {2}); - // chi_prompt = matrix / prompt_nu_fission (broadcast over gout) - for (size_t a = 0; a < n_ang; ++a) - for (size_t gin = 0; gin < n_g_; ++gin) - for (size_t gout = 0; gout < n_g_; ++gout) - chi_prompt(a, gin, gout) = - temp_matrix_p(a, gin, gout) / prompt_nu_fission(a, gin); + // chi_prompt is this matrix but normalized over outgoing groups, which we + // have already stored in prompt_nu_fission + chi_prompt = temp_matrix_p / + xt::view(prompt_nu_fission, xt::all(), xt::all(), xt::newaxis()); // Get the delayed nu-fission matrix xt::xtensor temp_matrix_d({n_ang, n_dg_, n_g_, n_g_}, 0.); @@ -418,13 +323,10 @@ void XsData::fission_matrix_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) // delayed_nu_fission is the sum over outgoing groups delayed_nu_fission = xt::sum(temp_matrix_d, {3}); - // chi_delayed = matrix / delayed_nu_fission (broadcast over gout) - for (size_t a = 0; a < n_ang; ++a) - for (size_t d = 0; d < n_dg_; ++d) - for (size_t gin = 0; gin < n_g_; ++gin) - for (size_t gout = 0; gout < n_g_; ++gout) - chi_delayed(a, d, gin, gout) = - temp_matrix_d(a, d, gin, gout) / delayed_nu_fission(a, d, gin); + // chi_prompt is this matrix but normalized over outgoing groups, which we + // have already stored in prompt_nu_fission + chi_delayed = temp_matrix_d / xt::view(delayed_nu_fission, xt::all(), + xt::all(), xt::all(), xt::newaxis()); } void XsData::fission_matrix_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang) @@ -439,12 +341,10 @@ void XsData::fission_matrix_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang) // prompt_nu_fission is the sum over outgoing groups prompt_nu_fission = xt::sum(temp_matrix, {2}); - // chi_prompt = matrix / prompt_nu_fission (broadcast over gout) - for (size_t a = 0; a < n_ang; ++a) - for (size_t gin = 0; gin < n_g_; ++gin) - for (size_t gout = 0; gout < n_g_; ++gout) - chi_prompt(a, gin, gout) = - temp_matrix(a, gin, gout) / prompt_nu_fission(a, gin); + // chi_prompt is this matrix but normalized over outgoing groups, which we + // have already stored in prompt_nu_fission + chi_prompt = temp_matrix / + xt::view(prompt_nu_fission, xt::all(), xt::all(), xt::newaxis()); } //============================================================================== @@ -485,12 +385,7 @@ void XsData::fission_from_hdf5( if (n_dg_ == 0) { nu_fission = prompt_nu_fission; } else { - // nu_fission = prompt_nu_fission + sum over delayed groups - nu_fission = prompt_nu_fission; - for (size_t a = 0; a < nu_fission.shape(0); ++a) - for (size_t g = 0; g < nu_fission.shape(1); ++g) - for (size_t d = 0; d < delayed_nu_fission.shape(1); ++d) - nu_fission(a, g) += delayed_nu_fission(a, d, g); + nu_fission = prompt_nu_fission + xt::sum(delayed_nu_fission, {1}); } } @@ -517,7 +412,7 @@ void XsData::scatter_from_hdf5(hid_t xsdata_grp, size_t n_ang, // Now use this info to find the length of a vector to hold the flattened // data. - size_t length = order_data * static_cast(xt::sum(gmax - gmin + 1)); + size_t length = order_data * xt::sum(gmax - gmin + 1)(); double_4dvec input_scatt(n_ang, double_3dvec(n_g_)); xt::xtensor temp_arr({length}, 0.); @@ -627,49 +522,24 @@ void XsData::combine( kappa_fission += scalar * that->kappa_fission; fission += scalar * that->fission; delayed_nu_fission += scalar * that->delayed_nu_fission; - // chi_prompt += scalar * sum_pnf(a) * that->chi_prompt(a, gin, gout) - // where sum_pnf(a) = sum over gin of prompt_nu_fission(a, gin) - { - auto sum_pnf = xt::sum(that->prompt_nu_fission, {1}); - for (size_t a = 0; a < chi_prompt.shape(0); ++a) - for (size_t gin = 0; gin < chi_prompt.shape(1); ++gin) - for (size_t gout = 0; gout < chi_prompt.shape(2); ++gout) - chi_prompt(a, gin, gout) += - scalar * sum_pnf(a) * that->chi_prompt(a, gin, gout); - } - // chi_delayed += scalar * sum_dnf(a, d) * that->chi_delayed(a, d, gin, gout) - // where sum_dnf(a, d) = sum over gin of delayed_nu_fission(a, d, gin) - { - auto sum_dnf = xt::sum(that->delayed_nu_fission, {2}); - for (size_t a = 0; a < chi_delayed.shape(0); ++a) - for (size_t d = 0; d < chi_delayed.shape(1); ++d) - for (size_t gin = 0; gin < chi_delayed.shape(2); ++gin) - for (size_t gout = 0; gout < chi_delayed.shape(3); ++gout) - chi_delayed(a, d, gin, gout) += - scalar * sum_dnf(a, d) * that->chi_delayed(a, d, gin, gout); - } + chi_prompt += scalar * + xt::view(xt::sum(that->prompt_nu_fission, {1}), xt::all(), + xt::newaxis(), xt::newaxis()) * + that->chi_prompt; + chi_delayed += scalar * + xt::view(xt::sum(that->delayed_nu_fission, {2}), xt::all(), + xt::all(), xt::newaxis(), xt::newaxis()) * + that->chi_delayed; } decay_rate += scalar * that->decay_rate; } - // Ensure the chi_prompt and chi_delayed are normalized to 1 - for (size_t a = 0; a < chi_prompt.shape(0); ++a) - for (size_t gin = 0; gin < chi_prompt.shape(1); ++gin) { - double s = 0.0; - for (size_t gout = 0; gout < chi_prompt.shape(2); ++gout) - s += chi_prompt(a, gin, gout); - for (size_t gout = 0; gout < chi_prompt.shape(2); ++gout) - chi_prompt(a, gin, gout) /= s; - } - for (size_t a = 0; a < chi_delayed.shape(0); ++a) - for (size_t d = 0; d < chi_delayed.shape(1); ++d) - for (size_t gin = 0; gin < chi_delayed.shape(2); ++gin) { - double s = 0.0; - for (size_t gout = 0; gout < chi_delayed.shape(3); ++gout) - s += chi_delayed(a, d, gin, gout); - for (size_t gout = 0; gout < chi_delayed.shape(3); ++gout) - chi_delayed(a, d, gin, gout) /= s; - } + // Ensure the chi_prompt and chi_delayed are normalized to 1 for each + // azimuthal angle and delayed group (for chi_delayed) + chi_prompt /= + xt::view(xt::sum(chi_prompt, {2}), xt::all(), xt::all(), xt::newaxis()); + chi_delayed /= xt::view( + xt::sum(chi_delayed, {3}), xt::all(), xt::all(), xt::all(), xt::newaxis()); // Allow the ScattData object to combine itself for (size_t a = 0; a < total.shape()[0]; a++) { From f1b0014209db37464d602d06f41b9b2a97e9bf1a Mon Sep 17 00:00:00 2001 From: John Tramm Date: Tue, 10 Feb 2026 09:55:02 -0600 Subject: [PATCH 05/51] Switched back to simpler tensor.h implementation with fewer advanced/confusing features --- include/openmc/tensor.h | 880 ++-------------------------------------- src/state_point.cpp | 38 +- src/weight_windows.cpp | 69 ++-- src/xsdata.cpp | 352 ++++++++++------ 4 files changed, 332 insertions(+), 1007 deletions(-) diff --git a/include/openmc/tensor.h b/include/openmc/tensor.h index 82e0acf1526..8f81c631fd9 100644 --- a/include/openmc/tensor.h +++ b/include/openmc/tensor.h @@ -1,10 +1,13 @@ +//! \file tensor.h +//! \brief Multi-dimensional tensor types replacing the xtensor dependency. +//! +//! Provides xtensor (fixed-dimension), xarray (dynamic-dimension), +//! and lightweight view types. Built on openmc::vector for GPU portability. +//! Only implements the subset of xtensor API that OpenMC actually uses. + #ifndef OPENMC_TENSOR_H #define OPENMC_TENSOR_H -// Drop-in replacement for xtensor functionality used by OpenMC. -// Only implements the subset of xtensor that OpenMC actually calls. -// Built on openmc::vector for future GPU portability. - #include "openmc/vector.h" #include @@ -15,7 +18,6 @@ #include #include #include -#include #include namespace xt { @@ -33,9 +35,6 @@ class xarray; template class xtensor_fixed; -template -class broadcast_view; - template struct xshape {}; @@ -648,48 +647,34 @@ class xtensor { return *this; } - // Binary ops: tensor op tensor (same type, with broadcasting support) - // When shapes match, uses fast element-wise loop. When shapes are - // broadcast-compatible (dims are equal or one is 1), uses broadcast logic. + // Element-wise binary ops (same shape) xtensor operator+(const xtensor& o) const { - if (shape_ == o.shape_) { - xtensor r(shape_); - for (std::size_t i = 0; i < data_.size(); ++i) - r.data_[i] = data_[i] + o.data_[i]; - return r; - } - return broadcast_op(o, [](T a, T b) { return a + b; }); + xtensor r(shape_); + for (std::size_t i = 0; i < data_.size(); ++i) + r.data_[i] = data_[i] + o.data_[i]; + return r; } xtensor operator-(const xtensor& o) const { - if (shape_ == o.shape_) { - xtensor r(shape_); - for (std::size_t i = 0; i < data_.size(); ++i) - r.data_[i] = data_[i] - o.data_[i]; - return r; - } - return broadcast_op(o, [](T a, T b) { return a - b; }); + xtensor r(shape_); + for (std::size_t i = 0; i < data_.size(); ++i) + r.data_[i] = data_[i] - o.data_[i]; + return r; } xtensor operator*(const xtensor& o) const { - if (shape_ == o.shape_) { - xtensor r(shape_); - for (std::size_t i = 0; i < data_.size(); ++i) - r.data_[i] = data_[i] * o.data_[i]; - return r; - } - return broadcast_op(o, [](T a, T b) { return a * b; }); + xtensor r(shape_); + for (std::size_t i = 0; i < data_.size(); ++i) + r.data_[i] = data_[i] * o.data_[i]; + return r; } xtensor operator/(const xtensor& o) const { - if (shape_ == o.shape_) { - xtensor r(shape_); - for (std::size_t i = 0; i < data_.size(); ++i) - r.data_[i] = data_[i] / o.data_[i]; - return r; - } - return broadcast_op(o, [](T a, T b) { return a / b; }); + xtensor r(shape_); + for (std::size_t i = 0; i < data_.size(); ++i) + r.data_[i] = data_[i] / o.data_[i]; + return r; } // Binary ops: tensor op scalar @@ -834,49 +819,7 @@ class xtensor { return *this; } - // Assignment from broadcast_view (declared here, defined after broadcast_view) - template - xtensor& operator=(const broadcast_view& bv); - private: - // Broadcasting binary op helper for same-type xtensor with - // broadcast-compatible shapes (each dim equal or one of them is 1). - // Output shape takes the max of each dimension. - template - xtensor broadcast_op(const xtensor& o, Op op) const - { - shape_type out_shape; - shape_type strides_a, strides_b; - // Compute output shape, strides for a and b (0 for broadcast dims) - for (std::size_t d = 0; d < N; ++d) - out_shape[d] = std::max(shape_[d], o.shape_[d]); - // Compute row-major strides for output shape - strides_a[N - 1] = 1; - strides_b[N - 1] = 1; - for (int d = static_cast(N) - 2; d >= 0; --d) { - strides_a[d] = strides_a[d + 1] * shape_[d + 1]; - strides_b[d] = strides_b[d + 1] * o.shape_[d + 1]; - } - xtensor r(out_shape); - std::size_t total = r.size(); - for (std::size_t flat = 0; flat < total; ++flat) { - // Decompose flat index into multi-dim indices for output - std::size_t rem = flat; - std::size_t off_a = 0, off_b = 0; - for (std::size_t d = 0; d < N; ++d) { - std::size_t stride_out = 1; - for (std::size_t d2 = d + 1; d2 < N; ++d2) - stride_out *= out_shape[d2]; - std::size_t idx = rem / stride_out; - rem %= stride_out; - off_a += (shape_[d] == 1 ? 0 : idx) * strides_a[d]; - off_b += (o.shape_[d] == 1 ? 0 : idx) * strides_b[d]; - } - r.data_[flat] = op(data_[off_a], o.data_[off_b]); - } - return r; - } - // Offset calculations for row-major layout std::size_t offset(std::size_t i0) const { return i0; } @@ -2296,781 +2239,6 @@ xtensor flip(const xtensor& a, std::size_t axis = 0) return r; } -//============================================================================== -// newaxis sentinel type -//============================================================================== - -struct xnewaxis_type {}; - -inline xnewaxis_type newaxis() -{ - return {}; -} - -//============================================================================== -// broadcast_view: a read-only view with stride-based broadcasting -//============================================================================== -// Dimensions with stride=0 broadcast (repeat) along that axis. - -template -class broadcast_view { - const T* data_; - std::array shape_; - std::array strides_; - -public: - broadcast_view( - const T* data, std::array shape, - std::array strides) - : data_(data), shape_(shape), strides_(strides) - {} - - const std::array& shape() const { return shape_; } - std::size_t shape(std::size_t dim) const { return shape_[dim]; } - - // 1D access - template> - const T& operator()(std::size_t i0) const - { - return data_[i0 * strides_[0]]; - } - - // 2D access - template> - const T& operator()(std::size_t i0, std::size_t i1) const - { - return data_[i0 * strides_[0] + i1 * strides_[1]]; - } - - // 3D access - template> - const T& operator()(std::size_t i0, std::size_t i1, std::size_t i2) const - { - return data_[i0 * strides_[0] + i1 * strides_[1] + i2 * strides_[2]]; - } - - // 4D access - template> - const T& operator()( - std::size_t i0, std::size_t i1, std::size_t i2, std::size_t i3) const - { - return data_[i0 * strides_[0] + i1 * strides_[1] + i2 * strides_[2] + - i3 * strides_[3]]; - } -}; - -// Helper: compute row-major strides for an xtensor shape -template -std::array compute_strides(const std::array& shape) -{ - std::array strides; - strides[N - 1] = 1; - for (int i = static_cast(N) - 2; i >= 0; --i) - strides[i] = strides[i + 1] * shape[i + 1]; - return strides; -} - -//--- view() overloads with newaxis --- - -// view(1D, all, newaxis) → broadcast_view -// shape [A] → [A, 1], strides [1, 0] -template -broadcast_view view(const xtensor& a, xall_type, xnewaxis_type) -{ - return {a.data(), {a.shape(0), std::size_t(1)}, {std::size_t(1), std::size_t(0)}}; -} - -// view(1D, all, newaxis, newaxis) → broadcast_view -// shape [A] → [A, 1, 1], strides [1, 0, 0] -template -broadcast_view view( - const xtensor& a, xall_type, xnewaxis_type, xnewaxis_type) -{ - return {a.data(), {a.shape(0), std::size_t(1), std::size_t(1)}, - {std::size_t(1), std::size_t(0), std::size_t(0)}}; -} - -// view(2D, all, newaxis, all) → broadcast_view -// shape [A, G] → [A, 1, G], strides [G, 0, 1] -template -broadcast_view view( - const xtensor& a, xall_type, xnewaxis_type, xall_type) -{ - auto s = compute_strides<2>(a.shape()); - return {a.data(), {a.shape(0), std::size_t(1), a.shape(1)}, - {s[0], std::size_t(0), s[1]}}; -} - -// view(2D, all, all, newaxis) → broadcast_view -// shape [A, G] → [A, G, 1], strides [G, 1, 0] -template -broadcast_view view( - const xtensor& a, xall_type, xall_type, xnewaxis_type) -{ - auto s = compute_strides<2>(a.shape()); - return {a.data(), {a.shape(0), a.shape(1), std::size_t(1)}, - {s[0], s[1], std::size_t(0)}}; -} - -// view(2D, all, newaxis, newaxis, all) → broadcast_view -// shape [A, G] → [A, 1, 1, G], strides [G, 0, 0, 1] -template -broadcast_view view(const xtensor& a, xall_type, xnewaxis_type, - xnewaxis_type, xall_type) -{ - auto s = compute_strides<2>(a.shape()); - return {a.data(), {a.shape(0), std::size_t(1), std::size_t(1), a.shape(1)}, - {s[0], std::size_t(0), std::size_t(0), s[1]}}; -} - -// view(2D, all, all, newaxis, newaxis) → broadcast_view -// shape [A, D] → [A, D, 1, 1], strides [D, 1, 0, 0] -template -broadcast_view view(const xtensor& a, xall_type, xall_type, - xnewaxis_type, xnewaxis_type) -{ - auto s = compute_strides<2>(a.shape()); - return {a.data(), {a.shape(0), a.shape(1), std::size_t(1), std::size_t(1)}, - {s[0], s[1], std::size_t(0), std::size_t(0)}}; -} - -// view(3D, all, newaxis, all, all) → broadcast_view -// shape [A, G1, G2] → [A, 1, G1, G2], strides [G1*G2, 0, G2, 1] -template -broadcast_view view(const xtensor& a, xall_type, xnewaxis_type, - xall_type, xall_type) -{ - auto s = compute_strides<3>(a.shape()); - return {a.data(), - {a.shape(0), std::size_t(1), a.shape(1), a.shape(2)}, - {s[0], std::size_t(0), s[1], s[2]}}; -} - -// view(3D, all, all, newaxis, all) → broadcast_view -// shape [A, D, G] → [A, D, 1, G], strides [D*G, G, 0, 1] -template -broadcast_view view(const xtensor& a, xall_type, xall_type, - xnewaxis_type, xall_type) -{ - auto s = compute_strides<3>(a.shape()); - return {a.data(), - {a.shape(0), a.shape(1), std::size_t(1), a.shape(2)}, - {s[0], s[1], std::size_t(0), s[2]}}; -} - -// view(3D, all, all, all, newaxis) → broadcast_view -// shape [A, D, G] → [A, D, G, 1], strides [D*G, G, 1, 0] -template -broadcast_view view(const xtensor& a, xall_type, xall_type, - xall_type, xnewaxis_type) -{ - auto s = compute_strides<3>(a.shape()); - return {a.data(), - {a.shape(0), a.shape(1), a.shape(2), std::size_t(1)}, - {s[0], s[1], s[2], std::size_t(0)}}; -} - -//--- Arithmetic ops for broadcast_view --- - -// broadcast_view * broadcast_view → xtensor -template -xtensor operator*( - const broadcast_view& a, const broadcast_view& b) -{ - auto sa = a.shape(); - std::array out_shape; - for (int i = 0; i < 3; ++i) - out_shape[i] = std::max(sa[i], b.shape(i)); - xtensor r(out_shape); - for (std::size_t i = 0; i < out_shape[0]; ++i) - for (std::size_t j = 0; j < out_shape[1]; ++j) - for (std::size_t k = 0; k < out_shape[2]; ++k) - r(i, j, k) = a(i, sa[1] == 1 ? 0 : j, sa[2] == 1 ? 0 : k) * - b(i, b.shape(1) == 1 ? 0 : j, b.shape(2) == 1 ? 0 : k); - return r; -} - -// broadcast_view * broadcast_view → xtensor -template -xtensor operator*( - const broadcast_view& a, const broadcast_view& b) -{ - std::array out_shape; - for (int i = 0; i < 4; ++i) - out_shape[i] = std::max(a.shape(i), b.shape(i)); - xtensor r(out_shape); - for (std::size_t i = 0; i < out_shape[0]; ++i) - for (std::size_t j = 0; j < out_shape[1]; ++j) - for (std::size_t k = 0; k < out_shape[2]; ++k) - for (std::size_t l = 0; l < out_shape[3]; ++l) - r(i, j, k, l) = - a(i, a.shape(1) == 1 ? 0 : j, a.shape(2) == 1 ? 0 : k, - a.shape(3) == 1 ? 0 : l) * - b(i, b.shape(1) == 1 ? 0 : j, b.shape(2) == 1 ? 0 : k, - b.shape(3) == 1 ? 0 : l); - return r; -} - -// broadcast_view * xtensor → xtensor -template -xtensor operator*( - const broadcast_view& bv, const xtensor& t) -{ - auto s = t.shape(); - xtensor r(s); - for (std::size_t i = 0; i < s[0]; ++i) - for (std::size_t j = 0; j < s[1]; ++j) - for (std::size_t k = 0; k < s[2]; ++k) - r(i, j, k) = bv(i, bv.shape(1) == 1 ? 0 : j, - bv.shape(2) == 1 ? 0 : k) * - t(i, j, k); - return r; -} - -// xtensor * broadcast_view → xtensor -template -xtensor operator*( - const xtensor& t, const broadcast_view& bv) -{ - return bv * t; -} - -// broadcast_view * xtensor → xtensor -template -xtensor operator*( - const broadcast_view& bv, const xtensor& t) -{ - auto s = t.shape(); - xtensor r(s); - for (std::size_t i = 0; i < s[0]; ++i) - for (std::size_t j = 0; j < s[1]; ++j) - for (std::size_t k = 0; k < s[2]; ++k) - for (std::size_t l = 0; l < s[3]; ++l) - r(i, j, k, l) = - bv(i, bv.shape(1) == 1 ? 0 : j, bv.shape(2) == 1 ? 0 : k, - bv.shape(3) == 1 ? 0 : l) * - t(i, j, k, l); - return r; -} - -// xtensor * broadcast_view → xtensor -template -xtensor operator*( - const xtensor& t, const broadcast_view& bv) -{ - return bv * t; -} - -// xtensor / broadcast_view → xtensor -template -xtensor operator/( - const xtensor& t, const broadcast_view& bv) -{ - auto s = t.shape(); - xtensor r(s); - for (std::size_t i = 0; i < s[0]; ++i) - for (std::size_t j = 0; j < s[1]; ++j) - for (std::size_t k = 0; k < s[2]; ++k) - r(i, j, k) = t(i, j, k) / bv(i, bv.shape(1) == 1 ? 0 : j, - bv.shape(2) == 1 ? 0 : k); - return r; -} - -// xtensor / broadcast_view → xtensor -template -xtensor operator/( - const xtensor& t, const broadcast_view& bv) -{ - auto s = t.shape(); - xtensor r(s); - for (std::size_t i = 0; i < s[0]; ++i) - for (std::size_t j = 0; j < s[1]; ++j) - for (std::size_t k = 0; k < s[2]; ++k) - for (std::size_t l = 0; l < s[3]; ++l) - r(i, j, k, l) = - t(i, j, k, l) / - bv(i, bv.shape(1) == 1 ? 0 : j, bv.shape(2) == 1 ? 0 : k, - bv.shape(3) == 1 ? 0 : l); - return r; -} - -// xtensor /= broadcast_view -template -xtensor& operator/=(xtensor& t, const broadcast_view& bv) -{ - auto s = t.shape(); - for (std::size_t i = 0; i < s[0]; ++i) - for (std::size_t j = 0; j < s[1]; ++j) - for (std::size_t k = 0; k < s[2]; ++k) - t(i, j, k) /= bv(i, bv.shape(1) == 1 ? 0 : j, - bv.shape(2) == 1 ? 0 : k); - return t; -} - -// xtensor /= broadcast_view -template -xtensor& operator/=(xtensor& t, const broadcast_view& bv) -{ - auto s = t.shape(); - for (std::size_t i = 0; i < s[0]; ++i) - for (std::size_t j = 0; j < s[1]; ++j) - for (std::size_t k = 0; k < s[2]; ++k) - for (std::size_t l = 0; l < s[3]; ++l) - t(i, j, k, l) /= - bv(i, bv.shape(1) == 1 ? 0 : j, bv.shape(2) == 1 ? 0 : k, - bv.shape(3) == 1 ? 0 : l); - return t; -} - -// scalar * broadcast_view → broadcast_view stores a copy, so return xtensor -template -xtensor operator*(T val, const broadcast_view& bv) -{ - auto s = bv.shape(); - xtensor r(s); - for (std::size_t i = 0; i < s[0]; ++i) - for (std::size_t j = 0; j < s[1]; ++j) - for (std::size_t k = 0; k < s[2]; ++k) - r(i, j, k) = val * bv(i, j, k); - return r; -} - -// scalar * broadcast_view → xtensor -template -xtensor operator*(T val, const broadcast_view& bv) -{ - auto s = bv.shape(); - xtensor r(s); - for (std::size_t i = 0; i < s[0]; ++i) - for (std::size_t j = 0; j < s[1]; ++j) - for (std::size_t k = 0; k < s[2]; ++k) - for (std::size_t l = 0; l < s[3]; ++l) - r(i, j, k, l) = val * bv(i, j, k, l); - return r; -} - -// xtensor * broadcast_view → xtensor -template -xtensor operator*( - const xtensor& t, const broadcast_view& bv) -{ - auto s = t.shape(); - xtensor r(s); - for (std::size_t i = 0; i < s[0]; ++i) - for (std::size_t j = 0; j < s[1]; ++j) - r(i, j) = t(i, j) * bv(i, bv.shape(1) == 1 ? 0 : j); - return r; -} - -// broadcast_view * xtensor → xtensor -template -xtensor operator*( - const broadcast_view& bv, const xtensor& t) -{ - return t * bv; -} - -// xtensor /= broadcast_view -template -xtensor& operator/=(xtensor& t, const broadcast_view& bv) -{ - auto s = t.shape(); - for (std::size_t i = 0; i < s[0]; ++i) - for (std::size_t j = 0; j < s[1]; ++j) - t(i, j) /= bv(i, bv.shape(1) == 1 ? 0 : j); - return t; -} - -// xtensor / broadcast_view → xtensor -template -xtensor operator/( - const xtensor& t, const broadcast_view& bv) -{ - auto s = t.shape(); - xtensor r(s); - for (std::size_t i = 0; i < s[0]; ++i) - for (std::size_t j = 0; j < s[1]; ++j) - r(i, j) = t(i, j) / bv(i, bv.shape(1) == 1 ? 0 : j); - return r; -} - -// xtensor::operator= from broadcast_view (deferred definition) -// Broadcasts the view into the existing tensor shape. Each dimension of the -// broadcast_view is either 1 (broadcast) or matches the target dimension. -// The target tensor shape is NOT changed — broadcasting fills in the values. -template -template -xtensor& xtensor::operator=(const broadcast_view& bv) -{ - static_assert(N == BN, "broadcast_view dimension must match xtensor dimension"); - auto bvs = bv.shape(); - // Use existing shape, broadcasting singleton dims from the view - if constexpr (N == 2) { - for (std::size_t i = 0; i < shape_[0]; ++i) - for (std::size_t j = 0; j < shape_[1]; ++j) - (*this)(i, j) = bv(bvs[0] == 1 ? 0 : i, bvs[1] == 1 ? 0 : j); - } else if constexpr (N == 3) { - for (std::size_t i = 0; i < shape_[0]; ++i) - for (std::size_t j = 0; j < shape_[1]; ++j) - for (std::size_t k = 0; k < shape_[2]; ++k) - (*this)(i, j, k) = bv(bvs[0] == 1 ? 0 : i, - bvs[1] == 1 ? 0 : j, bvs[2] == 1 ? 0 : k); - } else if constexpr (N == 4) { - for (std::size_t i = 0; i < shape_[0]; ++i) - for (std::size_t j = 0; j < shape_[1]; ++j) - for (std::size_t k = 0; k < shape_[2]; ++k) - for (std::size_t l = 0; l < shape_[3]; ++l) - (*this)(i, j, k, l) = bv(bvs[0] == 1 ? 0 : i, - bvs[1] == 1 ? 0 : j, bvs[2] == 1 ? 0 : k, - bvs[3] == 1 ? 0 : l); - } - return *this; -} - -// Conversion helper: materialize a broadcast_view into an xtensor -template -xtensor eval(const broadcast_view& bv) -{ - auto s = bv.shape(); - xtensor r(s); - for (std::size_t i = 0; i < s[0]; ++i) - for (std::size_t j = 0; j < s[1]; ++j) - for (std::size_t k = 0; k < s[2]; ++k) - r(i, j, k) = bv(i, j, k); - return r; -} - -template -xtensor eval(const broadcast_view& bv) -{ - auto s = bv.shape(); - xtensor r(s); - for (std::size_t i = 0; i < s[0]; ++i) - for (std::size_t j = 0; j < s[1]; ++j) - for (std::size_t k = 0; k < s[2]; ++k) - for (std::size_t l = 0; l < s[3]; ++l) - r(i, j, k, l) = bv(i, j, k, l); - return r; -} - -//============================================================================== -// Cross-dimension broadcasting: xtensor op xtensor -// Broadcasts 1D along axis 0 (each row scaled by corresponding 1D element) -//============================================================================== - -template -xtensor operator*(const xtensor& a, const xtensor& b) -{ - xtensor r(a.shape()); - for (std::size_t i = 0; i < a.shape(0); ++i) - for (std::size_t j = 0; j < a.shape(1); ++j) - r(i, j) = a(i, j) * b(i); - return r; -} - -template -xtensor operator*(const xtensor& b, const xtensor& a) -{ - return a * b; -} - -// xtensor * xtensor → broadcast 1D along axis 0 -template -xtensor operator*(const xtensor& a, const xtensor& b) -{ - xtensor r(a.shape()); - for (std::size_t i = 0; i < a.shape(0); ++i) - for (std::size_t j = 0; j < a.shape(1); ++j) - for (std::size_t k = 0; k < a.shape(2); ++k) - r(i, j, k) = a(i, j, k) * b(i); - return r; -} - -template -xtensor operator*(const xtensor& b, const xtensor& a) -{ - return a * b; -} - -// xtensor * xtensor → broadcast 2D along axes 0,1 -// e.g., [A, D, G] * [A, G] → each (a, d, g) *= (a, g) -template -xtensor operator*(const xtensor& a, const xtensor& b) -{ - xtensor r(a.shape()); - // Determine broadcasting: if b.shape matches a's last 2 dims, broadcast along them - // If b.shape(0) == a.shape(0) and b.shape(1) == a.shape(2), broadcast as (a, _, g) - if (b.shape(0) == a.shape(0) && b.shape(1) == a.shape(2)) { - for (std::size_t i = 0; i < a.shape(0); ++i) - for (std::size_t j = 0; j < a.shape(1); ++j) - for (std::size_t k = 0; k < a.shape(2); ++k) - r(i, j, k) = a(i, j, k) * b(i, k); - } else { - // Default: broadcast b's dims matching a's first 2 dims - for (std::size_t i = 0; i < a.shape(0); ++i) - for (std::size_t j = 0; j < a.shape(1); ++j) - for (std::size_t k = 0; k < a.shape(2); ++k) - r(i, j, k) = a(i, j, k) * b(i, j); - } - return r; -} - -template -xtensor operator*(const xtensor& b, const xtensor& a) -{ - return a * b; -} - -//============================================================================== -// equal() and filtration() for masked assignment -//============================================================================== - -// equal(tensor, val) → bool tensor -template -xtensor equal(const xtensor& a, T val) -{ - xtensor r(a.shape()); - for (std::size_t i = 0; i < a.size(); ++i) - r.data()[i] = (a.data()[i] == val); - return r; -} - -template -xarray equal(const xarray& a, T val) -{ - xarray r(a.shape()); - for (std::size_t i = 0; i < a.size(); ++i) - r.data()[i] = (a.data()[i] == val); - return r; -} - -// filtration_proxy: assigning to it sets elements where mask is true -template -class filtration_proxy { - T* data_; - const MaskContainer& mask_; - std::size_t size_; - -public: - filtration_proxy(T* data, const MaskContainer& mask, std::size_t size) - : data_(data), mask_(mask), size_(size) - {} - - template - filtration_proxy& operator=(U val) - { - for (std::size_t i = 0; i < size_; ++i) - if (mask_.data()[i]) - data_[i] = static_cast(val); - return *this; - } -}; - -template -filtration_proxy> filtration( - xtensor& a, const xtensor& mask) -{ - return {a.data(), mask, a.size()}; -} - -template -filtration_proxy> filtration( - xarray& a, const xarray& mask) -{ - return {a.data(), mask, a.size()}; -} - -//============================================================================== -// range_view_3d: view of xtensor sliced on last dimension -//============================================================================== - -template -class range_view_3d { - T* data_; - std::size_t s0_, s1_, s2_full_; // original shape dims - std::size_t k_start_, k_count_; - -public: - range_view_3d(T* data, std::size_t s0, std::size_t s1, std::size_t s2_full, - std::size_t k_start, std::size_t k_count) - : data_(data), s0_(s0), s1_(s1), s2_full_(s2_full), k_start_(k_start), - k_count_(k_count) - {} - - // Convert to xtensor (copy out) - operator xtensor, 3>() const - { - using U = std::remove_const_t; - xtensor r({s0_, s1_, k_count_}); - for (std::size_t i = 0; i < s0_; ++i) - for (std::size_t j = 0; j < s1_; ++j) - for (std::size_t k = 0; k < k_count_; ++k) - r(i, j, k) = data_[(i * s1_ + j) * s2_full_ + k_start_ + k]; - return r; - } - - // Assign from xtensor (copy in) - template - range_view_3d& operator=(const xtensor& src) - { - for (std::size_t i = 0; i < s0_; ++i) - for (std::size_t j = 0; j < s1_; ++j) - for (std::size_t k = 0; k < k_count_; ++k) - data_[(i * s1_ + j) * s2_full_ + k_start_ + k] = src(i, j, k); - return *this; - } -}; - -// view(3D, all, all, range) → range_view_3d -template -range_view_3d view( - xtensor& a, xall_type, xall_type, xrange_type r) -{ - auto start = resolve_end(r.start, a.shape(2)); - auto stop = resolve_end(r.stop, a.shape(2)); - return {a.data(), a.shape(0), a.shape(1), a.shape(2), start, stop - start}; -} - -template -range_view_3d view( - const xtensor& a, xall_type, xall_type, xrange_type r) -{ - auto start = resolve_end(r.start, a.shape(2)); - auto stop = resolve_end(r.stop, a.shape(2)); - return {a.data(), a.shape(0), a.shape(1), a.shape(2), start, stop - start}; -} - -//============================================================================== -// reshape_view, transpose, dynamic_view (for weight_windows.cpp) -//============================================================================== - -// reshape_view: view of contiguous data with new shape (no copy) -template -class reshaped_view { - const T* data_; - openmc::vector shape_; - openmc::vector strides_; - -public: - reshaped_view(const T* data, const openmc::vector& shape) - : data_(data), shape_(shape) - { - // Compute row-major strides - strides_.resize(shape_.size()); - if (!shape_.empty()) { - strides_.back() = 1; - for (int i = static_cast(shape_.size()) - 2; i >= 0; --i) - strides_[i] = strides_[i + 1] * shape_[i + 1]; - } - } - - const openmc::vector& shape() const { return shape_; } - const openmc::vector& strides() const { return strides_; } - const T* data() const { return data_; } - std::size_t dimension() const { return shape_.size(); } -}; - -template -reshaped_view reshape_view(const xtensor& a, const ShapeType& shape) -{ - openmc::vector s; - for (auto d : shape) - s.push_back(static_cast(d)); - return {a.data(), s}; -} - -template -reshaped_view reshape_view(const xarray& a, const ShapeType& shape) -{ - openmc::vector s; - for (auto d : shape) - s.push_back(static_cast(d)); - return {a.data(), s}; -} - -// transposed_view: permuted-dimension view -template -class transposed_view { - const T* data_; - openmc::vector shape_; // permuted shape - openmc::vector orig_strides_; // original strides permuted - -public: - transposed_view(const T* data, const openmc::vector& orig_shape, - const openmc::vector& orig_strides, - const openmc::vector& perm) - : data_(data) - { - auto ndim = perm.size(); - shape_.resize(ndim); - orig_strides_.resize(ndim); - for (std::size_t i = 0; i < ndim; ++i) { - shape_[i] = orig_shape[perm[i]]; - orig_strides_[i] = orig_strides[perm[i]]; - } - } - - const openmc::vector& shape() const { return shape_; } - const T* data() const { return data_; } - const openmc::vector& strides() const { return orig_strides_; } -}; - -template -transposed_view transpose(const reshaped_view& rv, const PermType& perm) -{ - openmc::vector p; - for (auto d : perm) - p.push_back(static_cast(d)); - return {rv.data(), rv.shape(), rv.strides(), p}; -} - -// dynamic_slice_arg: either an int index or all() -struct dynamic_slice_arg { - bool is_all; - int value; - - dynamic_slice_arg(int v) : is_all(false), value(v) {} - dynamic_slice_arg(xall_type) : is_all(true), value(0) {} -}; - -// dynamic_view: extract a 2D slice from a transposed_view using a mix of -// integer indices and all() slices. -template -xtensor dynamic_view(const transposed_view& tv, - std::initializer_list args_init) -{ - openmc::vector args(args_init); - auto& shape = tv.shape(); - auto& strides = tv.strides(); - - // Find the two "all" dimensions - std::size_t all_dim0 = 0, all_dim1 = 0; - int all_count = 0; - std::size_t base_offset = 0; - for (std::size_t i = 0; i < args.size(); ++i) { - if (args[i].is_all) { - if (all_count == 0) - all_dim0 = i; - else - all_dim1 = i; - all_count++; - } else { - base_offset += static_cast(args[i].value) * strides[i]; - } - } - - std::size_t n0 = shape[all_dim0]; - std::size_t n1 = shape[all_dim1]; - std::size_t st0 = strides[all_dim0]; - std::size_t st1 = strides[all_dim1]; - - xtensor result({n0, n1}); - for (std::size_t i = 0; i < n0; ++i) - for (std::size_t j = 0; j < n1; ++j) - result(i, j) = tv.data()[base_offset + i * st0 + j * st1]; - return result; -} - //============================================================================== // Compatibility stubs //============================================================================== diff --git a/src/state_point.cpp b/src/state_point.cpp index 3d0dbdca509..a2696dffe75 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -917,13 +917,18 @@ void write_tally_results_nr(hid_t file_id) write_attribute(file_id, "tallies_present", 1); } - // Get view of accumulated tally values - auto values_view = xt::view(t->results_, xt::all(), xt::all(), - xt::range(static_cast(TallyResult::SUM), - static_cast(TallyResult::SUM_SQ) + 1)); - - // Make copy of tally values in contiguous array - xt::xtensor values = values_view; + // Copy the SUM and SUM_SQ columns from the tally results into a + // contiguous array for MPI reduction + const int r_start = static_cast(TallyResult::SUM); + const int r_end = static_cast(TallyResult::SUM_SQ) + 1; + const size_t r_count = r_end - r_start; + const size_t ni = t->results_.shape()[0]; + const size_t nj = t->results_.shape()[1]; + xt::xtensor values({ni, nj, r_count}); + for (size_t i = 0; i < ni; i++) + for (size_t j = 0; j < nj; j++) + for (size_t r = 0; r < r_count; r++) + values(i, j, r) = t->results_(i, j, r_start + r); if (mpi::master) { // Open group for tally @@ -937,19 +942,22 @@ void write_tally_results_nr(hid_t file_id) MPI_SUM, 0, mpi::intracomm); #endif - // At the end of the simulation, store the results back in the - // regular TallyResults array + // At the end of the simulation, store the reduced results back + // into the tally results array if (simulation::current_batch == settings::n_max_batches || simulation::satisfy_triggers) { - values_view = values; + for (size_t i = 0; i < ni; i++) + for (size_t j = 0; j < nj; j++) + for (size_t r = 0; r < r_count; r++) + t->results_(i, j, r_start + r) = values(i, j, r); } - // Put in temporary tally result + // Put reduced values into a full-sized copy for writing to HDF5 xt::xtensor results_copy = xt::zeros_like(t->results_); - auto copy_view = xt::view(results_copy, xt::all(), xt::all(), - xt::range(static_cast(TallyResult::SUM), - static_cast(TallyResult::SUM_SQ) + 1)); - copy_view = values; + for (size_t i = 0; i < ni; i++) + for (size_t j = 0; j < nj; j++) + for (size_t r = 0; r < r_count; r++) + results_copy(i, j, r_start + r) = values(i, j, r); // Write reduced tally results to file auto shape = results_copy.shape(); diff --git a/src/weight_windows.cpp b/src/weight_windows.cpp index 3028bb46e66..828379040b3 100644 --- a/src/weight_windows.cpp +++ b/src/weight_windows.cpp @@ -473,11 +473,11 @@ void WeightWindows::set_bounds( lower_ww_ = xt::empty(shape); upper_ww_ = xt::empty(shape); - // set new weight window values - xt::view(lower_ww_, xt::all()) = - xt::adapt(lower_bounds.data(), lower_ww_.shape()); - xt::view(upper_ww_, xt::all()) = - xt::adapt(upper_bounds.data(), upper_ww_.shape()); + // Copy weight window values from input spans into the tensors + std::copy(lower_bounds.data(), lower_bounds.data() + lower_ww_.size(), + lower_ww_.data()); + std::copy(upper_bounds.data(), upper_bounds.data() + upper_ww_.size(), + upper_ww_.data()); } void WeightWindows::set_bounds(span lower_bounds, double ratio) @@ -488,11 +488,11 @@ void WeightWindows::set_bounds(span lower_bounds, double ratio) lower_ww_ = xt::empty(shape); upper_ww_ = xt::empty(shape); - // set new weight window values - xt::view(lower_ww_, xt::all()) = - xt::adapt(lower_bounds.data(), lower_ww_.shape()); - xt::view(upper_ww_, xt::all()) = - xt::adapt(lower_bounds.data(), upper_ww_.shape()); + // Copy lower bounds into both arrays, then scale upper by ratio + std::copy(lower_bounds.data(), lower_bounds.data() + lower_ww_.size(), + lower_ww_.data()); + std::copy(lower_bounds.data(), lower_bounds.data() + upper_ww_.size(), + upper_ww_.data()); upper_ww_ *= ratio; } @@ -583,12 +583,14 @@ void WeightWindows::update_weights(const Tally* tally, const std::string& value, std::find(filter_types.begin(), filter_types.end(), FilterType::MESH) - filter_types.begin(); - // get a fully reshaped view of the tally according to tally ordering of - // filters - auto tally_values = xt::reshape_view(results_arr, shape); - - // get a that is (particle, energy, mesh, scores, values) - auto transposed_view = xt::transpose(tally_values, transpose); + // Compute 5D row-major strides for the reshaped tally data + std::array src_shape; + for (int i = 0; i < 5; i++) + src_shape[i] = static_cast(shape[i]); + std::array src_strides; + src_strides[4] = 1; + for (int i = 3; i >= 0; i--) + src_strides[i] = src_strides[i + 1] * src_shape[i + 1]; // determine the dimension and index of the particle data int particle_idx = 0; @@ -613,13 +615,34 @@ void WeightWindows::update_weights(const Tally* tally, const std::string& value, particle_idx = p_it - particles.begin(); } - // down-select data based on particle and score - auto sum = xt::dynamic_view( - transposed_view, {particle_idx, xt::all(), xt::all(), score_index, - static_cast(TallyResult::SUM)}); - auto sum_sq = xt::dynamic_view( - transposed_view, {particle_idx, xt::all(), xt::all(), score_index, - static_cast(TallyResult::SUM_SQ)}); + // Extract 2D slices of tally mean and sum-of-squares for the selected + // particle type and score. The tally data is logically reshaped to 5D + // (filter dims, scores, result values) and transposed so that the + // energy and mesh dimensions are in positions 1 and 2. + xt::xtensor sum( + {static_cast(e_bins), static_cast(mesh_bins)}); + xt::xtensor sum_sq( + {static_cast(e_bins), static_cast(mesh_bins)}); + const int sum_val = static_cast(TallyResult::SUM); + const int sum_sq_val = static_cast(TallyResult::SUM_SQ); + for (int e = 0; e < e_bins; e++) { + for (int64_t m = 0; m < mesh_bins; m++) { + // Map transposed (particle, energy, mesh, score, result) indices + // back to the original filter ordering using the transpose permutation + std::array tidx = { + particle_idx, e, static_cast(m), score_index, sum_val}; + size_t off = 0; + for (int d = 0; d < 5; d++) + off += tidx[d] * src_strides[transpose[d]]; + sum(e, m) = results_arr.data()[off]; + + tidx[4] = sum_sq_val; + off = 0; + for (int d = 0; d < 5; d++) + off += tidx[d] * src_strides[transpose[d]]; + sum_sq(e, m) = results_arr.data()[off]; + } + } int n = tally->n_realizations_; ////////////////////////////////////////////// diff --git a/src/xsdata.cpp b/src/xsdata.cpp index 83eed7be2a0..7963383567d 100644 --- a/src/xsdata.cpp +++ b/src/xsdata.cpp @@ -97,9 +97,10 @@ void XsData::from_hdf5(hid_t xsdata_grp, bool fissionable, scatter_from_hdf5( xsdata_grp, n_ang, scatter_format, final_scatter_format, order_data); - // Check absorption to ensure it is not 0 since it is often the - // denominator in tally methods - xt::filtration(absorption, xt::equal(absorption, 0.)) = 1.e-10; + // Replace zero absorption values with a small number to avoid + // division by zero in tally methods + for (size_t i = 0; i < absorption.size(); i++) + if (absorption.data()[i] == 0.0) absorption.data()[i] = 1.e-10; // Get or calculate the total x/s if (object_exists(xsdata_grp, "total")) { @@ -112,8 +113,10 @@ void XsData::from_hdf5(hid_t xsdata_grp, bool fissionable, } } - // Fix if total is 0, since it is in the denominator when tallying - xt::filtration(total, xt::equal(total, 0.)) = 1.e-10; + // Replace zero total cross sections with a small number to avoid + // division by zero in tally methods + for (size_t i = 0; i < total.size(); i++) + if (total.data()[i] == 0.0) total.data()[i] = 1.e-10; } //============================================================================== @@ -127,14 +130,26 @@ void XsData::fission_vector_beta_from_hdf5( xt::xtensor temp_chi({n_ang, n_g_}, 0.); read_nd_vector(xsdata_grp, "chi", temp_chi, true); - // Normalize chi by summing over the outgoing groups for each incoming angle - temp_chi /= xt::view(xt::sum(temp_chi, {1}), xt::all(), xt::newaxis()); + // Normalize chi so it sums to 1 over outgoing groups for each angle + for (size_t a = 0; a < n_ang; a++) { + double s = 0.0; + for (size_t g = 0; g < n_g_; g++) s += temp_chi(a, g); + for (size_t g = 0; g < n_g_; g++) temp_chi(a, g) /= s; + } - // Now every incoming group in prompt_chi and delayed_chi is the normalized - // chi we just made - chi_prompt = xt::view(temp_chi, xt::all(), xt::newaxis(), xt::all()); - chi_delayed = - xt::view(temp_chi, xt::all(), xt::newaxis(), xt::newaxis(), xt::all()); + // Replicate the energy spectrum across all incoming groups — the + // spectrum is independent of the incoming neutron energy + for (size_t a = 0; a < n_ang; a++) + for (size_t gin = 0; gin < n_g_; gin++) + for (size_t gout = 0; gout < n_g_; gout++) + chi_prompt(a, gin, gout) = temp_chi(a, gout); + + // Same spectrum for delayed neutrons, replicated across delayed groups + for (size_t a = 0; a < n_ang; a++) + for (size_t d = 0; d < n_dg_; d++) + for (size_t gin = 0; gin < n_g_; gin++) + for (size_t gout = 0; gout < n_g_; gout++) + chi_delayed(a, d, gin, gout) = temp_chi(a, gout); // Get nu-fission xt::xtensor temp_nufiss({n_ang, n_g_}, 0.); @@ -151,23 +166,35 @@ void XsData::fission_vector_beta_from_hdf5( xt::xtensor temp_beta({n_ang, n_dg_}, 0.); read_nd_vector(xsdata_grp, "beta", temp_beta, true); - // Set prompt_nu_fission = (1. - beta_total)*nu_fission - prompt_nu_fission = temp_nufiss * (1. - xt::sum(temp_beta, {1})); - - // Set delayed_nu_fission as beta * nu_fission - delayed_nu_fission = - xt::view(temp_beta, xt::all(), xt::all(), xt::newaxis()) * - xt::view(temp_nufiss, xt::all(), xt::newaxis(), xt::all()); + // prompt_nu_fission = (1 - sum_of_beta) * nu_fission + auto beta_sum = xt::sum(temp_beta, {1}); + for (size_t a = 0; a < n_ang; a++) + for (size_t g = 0; g < n_g_; g++) + prompt_nu_fission(a, g) = temp_nufiss(a, g) * (1.0 - beta_sum(a)); + + // Delayed nu-fission is the outer product of the delayed neutron + // fraction (beta) and the fission production rate (nu-fission) + for (size_t a = 0; a < n_ang; a++) + for (size_t d = 0; d < n_dg_; d++) + for (size_t g = 0; g < n_g_; g++) + delayed_nu_fission(a, d, g) = temp_beta(a, d) * temp_nufiss(a, g); } else if (beta_ndims == ndim_target + 1) { xt::xtensor temp_beta({n_ang, n_dg_, n_g_}, 0.); read_nd_vector(xsdata_grp, "beta", temp_beta, true); - // Set prompt_nu_fission = (1. - beta_total)*nu_fission - prompt_nu_fission = temp_nufiss * (1. - xt::sum(temp_beta, {1})); - - // Set delayed_nu_fission as beta * nu_fission - delayed_nu_fission = - temp_beta * xt::view(temp_nufiss, xt::all(), xt::newaxis(), xt::all()); + // prompt_nu_fission = (1 - sum_of_beta) * nu_fission + // Here beta is energy-dependent, so sum over delayed groups (axis 1) + auto beta_sum = xt::sum(temp_beta, {1}); + for (size_t a = 0; a < n_ang; a++) + for (size_t g = 0; g < n_g_; g++) + prompt_nu_fission(a, g) = temp_nufiss(a, g) * (1.0 - beta_sum(a, g)); + + // Delayed nu-fission: beta is already energy-dependent [n_ang, n_dg, n_g], + // so scale each delayed group's beta by the total nu-fission for that group + for (size_t a = 0; a < n_ang; a++) + for (size_t d = 0; d < n_dg_; d++) + for (size_t g = 0; g < n_g_; g++) + delayed_nu_fission(a, d, g) = temp_beta(a, d, g) * temp_nufiss(a, g); } } @@ -179,22 +206,38 @@ void XsData::fission_vector_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) xt::xtensor temp_chi_p({n_ang, n_g_}, 0.); read_nd_vector(xsdata_grp, "chi-prompt", temp_chi_p, true); - // Normalize chi by summing over the outgoing groups for each incoming angle - temp_chi_p /= xt::view(xt::sum(temp_chi_p, {1}), xt::all(), xt::newaxis()); + // Normalize prompt chi so it sums to 1 over outgoing groups for each angle + for (size_t a = 0; a < n_ang; a++) { + double s = 0.0; + for (size_t g = 0; g < n_g_; g++) s += temp_chi_p(a, g); + for (size_t g = 0; g < n_g_; g++) temp_chi_p(a, g) /= s; + } // Get chi-delayed xt::xtensor temp_chi_d({n_ang, n_dg_, n_g_}, 0.); read_nd_vector(xsdata_grp, "chi-delayed", temp_chi_d, true); - // Normalize chi by summing over the outgoing groups for each incoming angle - temp_chi_d /= - xt::view(xt::sum(temp_chi_d, {2}), xt::all(), xt::all(), xt::newaxis()); + // Normalize delayed chi so it sums to 1 over outgoing groups for each + // angle and delayed group + for (size_t a = 0; a < n_ang; a++) + for (size_t d = 0; d < n_dg_; d++) { + double s = 0.0; + for (size_t g = 0; g < n_g_; g++) s += temp_chi_d(a, d, g); + for (size_t g = 0; g < n_g_; g++) temp_chi_d(a, d, g) /= s; + } - // Now assign the prompt and delayed chis by replicating for each incoming - // group - chi_prompt = xt::view(temp_chi_p, xt::all(), xt::newaxis(), xt::all()); - chi_delayed = - xt::view(temp_chi_d, xt::all(), xt::all(), xt::newaxis(), xt::all()); + // Replicate the prompt spectrum across all incoming groups + for (size_t a = 0; a < n_ang; a++) + for (size_t gin = 0; gin < n_g_; gin++) + for (size_t gout = 0; gout < n_g_; gout++) + chi_prompt(a, gin, gout) = temp_chi_p(a, gout); + + // Replicate the delayed spectrum across all incoming groups + for (size_t a = 0; a < n_ang; a++) + for (size_t d = 0; d < n_dg_; d++) + for (size_t gin = 0; gin < n_g_; gin++) + for (size_t gout = 0; gout < n_g_; gout++) + chi_delayed(a, d, gin, gout) = temp_chi_d(a, d, gout); // Get prompt and delayed nu-fission directly read_nd_vector(xsdata_grp, "prompt-nu-fission", prompt_nu_fission, true); @@ -210,11 +253,18 @@ void XsData::fission_vector_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang) xt::xtensor temp_chi({n_ang, n_g_}, 0.); read_nd_vector(xsdata_grp, "chi", temp_chi, true); - // Normalize chi by summing over the outgoing groups for each incoming angle - temp_chi /= xt::view(xt::sum(temp_chi, {1}), xt::all(), xt::newaxis()); + // Normalize chi so it sums to 1 over outgoing groups for each angle + for (size_t a = 0; a < n_ang; a++) { + double s = 0.0; + for (size_t g = 0; g < n_g_; g++) s += temp_chi(a, g); + for (size_t g = 0; g < n_g_; g++) temp_chi(a, g) /= s; + } - // Now every incoming group in self.chi is the normalized chi we just made - chi_prompt = xt::view(temp_chi, xt::all(), xt::newaxis(), xt::all()); + // Replicate the energy spectrum across all incoming groups + for (size_t a = 0; a < n_ang; a++) + for (size_t gin = 0; gin < n_g_; gin++) + for (size_t gout = 0; gout < n_g_; gout++) + chi_prompt(a, gin, gout) = temp_chi(a, gout); // Get nu-fission directly read_nd_vector(xsdata_grp, "nu-fission", prompt_nu_fission, true); @@ -242,62 +292,93 @@ void XsData::fission_matrix_beta_from_hdf5( xt::xtensor temp_beta({n_ang, n_dg_}, 0.); read_nd_vector(xsdata_grp, "beta", temp_beta, true); - xt::xtensor temp_beta_sum({n_ang}, 0.); - temp_beta_sum = xt::sum(temp_beta, {1}); - - // prompt_nu_fission is the sum of this matrix over outgoing groups and - // multiplied by (1 - beta_sum) - prompt_nu_fission = xt::sum(temp_matrix, {2}) * (1. - temp_beta_sum); - - // Store chi-prompt - chi_prompt = - xt::view(1.0 - temp_beta_sum, xt::all(), xt::newaxis(), xt::newaxis()) * - temp_matrix; - - // delayed_nu_fission is the sum of this matrix over outgoing groups and - // multiplied by beta - delayed_nu_fission = - xt::view(temp_beta, xt::all(), xt::all(), xt::newaxis()) * - xt::view(xt::sum(temp_matrix, {2}), xt::all(), xt::newaxis(), xt::all()); - - // Store chi-delayed - chi_delayed = - xt::view(temp_beta, xt::all(), xt::all(), xt::newaxis(), xt::newaxis()) * - xt::view(temp_matrix, xt::all(), xt::newaxis(), xt::all(), xt::all()); + auto beta_sum = xt::sum(temp_beta, {1}); + auto matrix_gout_sum = xt::sum(temp_matrix, {2}); + + // prompt_nu_fission = sum_gout(matrix) * (1 - beta_total) + for (size_t a = 0; a < n_ang; a++) + for (size_t g = 0; g < n_g_; g++) + prompt_nu_fission(a, g) = matrix_gout_sum(a, g) * (1.0 - beta_sum(a)); + + // chi_prompt = (1 - beta_total) * nu-fission matrix (unnormalized) + for (size_t a = 0; a < n_ang; a++) + for (size_t gin = 0; gin < n_g_; gin++) + for (size_t gout = 0; gout < n_g_; gout++) + chi_prompt(a, gin, gout) = + (1.0 - beta_sum(a)) * temp_matrix(a, gin, gout); + + // Delayed nu-fission is the outer product of the delayed neutron + // fraction (beta) and the total fission rate summed over outgoing groups + for (size_t a = 0; a < n_ang; a++) + for (size_t d = 0; d < n_dg_; d++) + for (size_t g = 0; g < n_g_; g++) + delayed_nu_fission(a, d, g) = + temp_beta(a, d) * matrix_gout_sum(a, g); + + // chi_delayed = beta * nu-fission matrix, expanded across delayed groups + for (size_t a = 0; a < n_ang; a++) + for (size_t d = 0; d < n_dg_; d++) + for (size_t gin = 0; gin < n_g_; gin++) + for (size_t gout = 0; gout < n_g_; gout++) + chi_delayed(a, d, gin, gout) = + temp_beta(a, d) * temp_matrix(a, gin, gout); } else if (beta_ndims == ndim_target + 1) { xt::xtensor temp_beta({n_ang, n_dg_, n_g_}, 0.); read_nd_vector(xsdata_grp, "beta", temp_beta, true); - xt::xtensor temp_beta_sum({n_ang, n_g_}, 0.); - temp_beta_sum = xt::sum(temp_beta, {1}); - - // prompt_nu_fission is the sum of this matrix over outgoing groups and - // multiplied by (1 - beta_sum) - prompt_nu_fission = xt::sum(temp_matrix, {2}) * (1. - temp_beta_sum); - - // Store chi-prompt - chi_prompt = - xt::view(1.0 - temp_beta_sum, xt::all(), xt::all(), xt::newaxis()) * - temp_matrix; - - // delayed_nu_fission is the sum of this matrix over outgoing groups and - // multiplied by beta - delayed_nu_fission = temp_beta * xt::view(xt::sum(temp_matrix, {2}), - xt::all(), xt::newaxis(), xt::all()); - - // Store chi-delayed - chi_delayed = - xt::view(temp_beta, xt::all(), xt::all(), xt::all(), xt::newaxis()) * - xt::view(temp_matrix, xt::all(), xt::newaxis(), xt::all(), xt::all()); + auto beta_sum = xt::sum(temp_beta, {1}); + auto matrix_gout_sum = xt::sum(temp_matrix, {2}); + + // prompt_nu_fission = sum_gout(matrix) * (1 - beta_total) + // Here beta is energy-dependent, so beta_sum is 2D [n_ang, n_g] + for (size_t a = 0; a < n_ang; a++) + for (size_t g = 0; g < n_g_; g++) + prompt_nu_fission(a, g) = + matrix_gout_sum(a, g) * (1.0 - beta_sum(a, g)); + + // chi_prompt = (1 - beta_sum) * nu-fission matrix (unnormalized) + for (size_t a = 0; a < n_ang; a++) + for (size_t gin = 0; gin < n_g_; gin++) + for (size_t gout = 0; gout < n_g_; gout++) + chi_prompt(a, gin, gout) = + (1.0 - beta_sum(a, gin)) * temp_matrix(a, gin, gout); + + // Delayed nu-fission: beta is energy-dependent [n_ang, n_dg, n_g], + // scale by total fission rate summed over outgoing groups + for (size_t a = 0; a < n_ang; a++) + for (size_t d = 0; d < n_dg_; d++) + for (size_t g = 0; g < n_g_; g++) + delayed_nu_fission(a, d, g) = + temp_beta(a, d, g) * matrix_gout_sum(a, g); + + // chi_delayed = beta * nu-fission matrix, expanded across delayed groups + for (size_t a = 0; a < n_ang; a++) + for (size_t d = 0; d < n_dg_; d++) + for (size_t gin = 0; gin < n_g_; gin++) + for (size_t gout = 0; gout < n_g_; gout++) + chi_delayed(a, d, gin, gout) = + temp_beta(a, d, gin) * temp_matrix(a, gin, gout); } - // Normalize both chis - chi_prompt /= - xt::view(xt::sum(chi_prompt, {2}), xt::all(), xt::all(), xt::newaxis()); + // Normalize chi_prompt so it sums to 1 over outgoing groups + for (size_t a = 0; a < n_ang; a++) + for (size_t gin = 0; gin < n_g_; gin++) { + double s = 0.0; + for (size_t gout = 0; gout < n_g_; gout++) s += chi_prompt(a, gin, gout); + for (size_t gout = 0; gout < n_g_; gout++) chi_prompt(a, gin, gout) /= s; + } - chi_delayed /= xt::view( - xt::sum(chi_delayed, {3}), xt::all(), xt::all(), xt::all(), xt::newaxis()); + // Normalize chi_delayed so it sums to 1 over outgoing groups + for (size_t a = 0; a < n_ang; a++) + for (size_t d = 0; d < n_dg_; d++) + for (size_t gin = 0; gin < n_g_; gin++) { + double s = 0.0; + for (size_t gout = 0; gout < n_g_; gout++) + s += chi_delayed(a, d, gin, gout); + for (size_t gout = 0; gout < n_g_; gout++) + chi_delayed(a, d, gin, gout) /= s; + } } void XsData::fission_matrix_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) @@ -311,10 +392,12 @@ void XsData::fission_matrix_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) // prompt_nu_fission is the sum over outgoing groups prompt_nu_fission = xt::sum(temp_matrix_p, {2}); - // chi_prompt is this matrix but normalized over outgoing groups, which we - // have already stored in prompt_nu_fission - chi_prompt = temp_matrix_p / - xt::view(prompt_nu_fission, xt::all(), xt::all(), xt::newaxis()); + // chi_prompt is the nu-fission matrix normalized over outgoing groups + for (size_t a = 0; a < n_ang; a++) + for (size_t gin = 0; gin < n_g_; gin++) + for (size_t gout = 0; gout < n_g_; gout++) + chi_prompt(a, gin, gout) = temp_matrix_p(a, gin, gout) / + prompt_nu_fission(a, gin); // Get the delayed nu-fission matrix xt::xtensor temp_matrix_d({n_ang, n_dg_, n_g_, n_g_}, 0.); @@ -323,10 +406,13 @@ void XsData::fission_matrix_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) // delayed_nu_fission is the sum over outgoing groups delayed_nu_fission = xt::sum(temp_matrix_d, {3}); - // chi_prompt is this matrix but normalized over outgoing groups, which we - // have already stored in prompt_nu_fission - chi_delayed = temp_matrix_d / xt::view(delayed_nu_fission, xt::all(), - xt::all(), xt::all(), xt::newaxis()); + // chi_delayed is the delayed nu-fission matrix normalized over outgoing groups + for (size_t a = 0; a < n_ang; a++) + for (size_t d = 0; d < n_dg_; d++) + for (size_t gin = 0; gin < n_g_; gin++) + for (size_t gout = 0; gout < n_g_; gout++) + chi_delayed(a, d, gin, gout) = temp_matrix_d(a, d, gin, gout) / + delayed_nu_fission(a, d, gin); } void XsData::fission_matrix_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang) @@ -341,10 +427,12 @@ void XsData::fission_matrix_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang) // prompt_nu_fission is the sum over outgoing groups prompt_nu_fission = xt::sum(temp_matrix, {2}); - // chi_prompt is this matrix but normalized over outgoing groups, which we - // have already stored in prompt_nu_fission - chi_prompt = temp_matrix / - xt::view(prompt_nu_fission, xt::all(), xt::all(), xt::newaxis()); + // chi_prompt is the nu-fission matrix normalized over outgoing groups + for (size_t a = 0; a < n_ang; a++) + for (size_t gin = 0; gin < n_g_; gin++) + for (size_t gout = 0; gout < n_g_; gout++) + chi_prompt(a, gin, gout) = temp_matrix(a, gin, gout) / + prompt_nu_fission(a, gin); } //============================================================================== @@ -522,24 +610,62 @@ void XsData::combine( kappa_fission += scalar * that->kappa_fission; fission += scalar * that->fission; delayed_nu_fission += scalar * that->delayed_nu_fission; - chi_prompt += scalar * - xt::view(xt::sum(that->prompt_nu_fission, {1}), xt::all(), - xt::newaxis(), xt::newaxis()) * - that->chi_prompt; - chi_delayed += scalar * - xt::view(xt::sum(that->delayed_nu_fission, {2}), xt::all(), - xt::all(), xt::newaxis(), xt::newaxis()) * - that->chi_delayed; + // Accumulate chi_prompt weighted by total prompt nu-fission + // (summed over energy groups) for this constituent + { + auto pnf_sum = xt::sum(that->prompt_nu_fission, {1}); + size_t n_ang = chi_prompt.shape()[0]; + size_t n_g = chi_prompt.shape()[1]; + for (size_t a = 0; a < n_ang; a++) + for (size_t gin = 0; gin < n_g; gin++) + for (size_t gout = 0; gout < n_g; gout++) + chi_prompt(a, gin, gout) += scalar * pnf_sum(a) * + that->chi_prompt(a, gin, gout); + } + // Accumulate chi_delayed weighted by total delayed nu-fission + // (summed over energy groups) for this constituent + { + auto dnf_sum = xt::sum(that->delayed_nu_fission, {2}); + size_t n_ang = chi_delayed.shape()[0]; + size_t n_dg = chi_delayed.shape()[1]; + size_t n_g = chi_delayed.shape()[2]; + for (size_t a = 0; a < n_ang; a++) + for (size_t d = 0; d < n_dg; d++) + for (size_t gin = 0; gin < n_g; gin++) + for (size_t gout = 0; gout < n_g; gout++) + chi_delayed(a, d, gin, gout) += scalar * dnf_sum(a, d) * + that->chi_delayed(a, d, gin, gout); + } } decay_rate += scalar * that->decay_rate; } - // Ensure the chi_prompt and chi_delayed are normalized to 1 for each - // azimuthal angle and delayed group (for chi_delayed) - chi_prompt /= - xt::view(xt::sum(chi_prompt, {2}), xt::all(), xt::all(), xt::newaxis()); - chi_delayed /= xt::view( - xt::sum(chi_delayed, {3}), xt::all(), xt::all(), xt::all(), xt::newaxis()); + // Normalize chi_prompt so it sums to 1 over outgoing groups + { + size_t n_ang = chi_prompt.shape()[0]; + size_t n_g = chi_prompt.shape()[1]; + for (size_t a = 0; a < n_ang; a++) + for (size_t gin = 0; gin < n_g; gin++) { + double s = 0.0; + for (size_t gout = 0; gout < n_g; gout++) s += chi_prompt(a, gin, gout); + for (size_t gout = 0; gout < n_g; gout++) chi_prompt(a, gin, gout) /= s; + } + } + // Normalize chi_delayed so it sums to 1 over outgoing groups + { + size_t n_ang = chi_delayed.shape()[0]; + size_t n_dg = chi_delayed.shape()[1]; + size_t n_g = chi_delayed.shape()[2]; + for (size_t a = 0; a < n_ang; a++) + for (size_t d = 0; d < n_dg; d++) + for (size_t gin = 0; gin < n_g; gin++) { + double s = 0.0; + for (size_t gout = 0; gout < n_g; gout++) + s += chi_delayed(a, d, gin, gout); + for (size_t gout = 0; gout < n_g; gout++) + chi_delayed(a, d, gin, gout) /= s; + } + } // Allow the ScattData object to combine itself for (size_t a = 0; a < total.shape()[0]; a++) { From c6260280059d14117dbd54d190306da98de9be0d Mon Sep 17 00:00:00 2001 From: John Tramm Date: Tue, 10 Feb 2026 10:34:16 -0600 Subject: [PATCH 06/51] simplified weight windows xtensor usage --- src/weight_windows.cpp | 65 +++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 35 deletions(-) diff --git a/src/weight_windows.cpp b/src/weight_windows.cpp index 828379040b3..bb89a0c490c 100644 --- a/src/weight_windows.cpp +++ b/src/weight_windows.cpp @@ -583,27 +583,14 @@ void WeightWindows::update_weights(const Tally* tally, const std::string& value, std::find(filter_types.begin(), filter_types.end(), FilterType::MESH) - filter_types.begin(); - // Compute 5D row-major strides for the reshaped tally data - std::array src_shape; - for (int i = 0; i < 5; i++) - src_shape[i] = static_cast(shape[i]); - std::array src_strides; - src_strides[4] = 1; - for (int i = 3; i >= 0; i--) - src_strides[i] = src_strides[i + 1] * src_shape[i + 1]; - - // determine the dimension and index of the particle data + // determine the index of the particle within its filter int particle_idx = 0; if (tally->has_filter(FilterType::PARTICLE)) { - // get the particle filter auto pf = tally->get_filter(); const auto& particles = pf->particles(); - // find the index of the particle that matches these weight windows auto p_it = std::find(particles.begin(), particles.end(), this->particle_type_); - // if the particle filter doesn't have particle data for the particle - // used on this weight windows instance, report an error if (p_it == particles.end()) { auto msg = fmt::format("Particle type '{}' not present on Filter {} for " "Tally {} used to update WeightWindows {}", @@ -611,36 +598,44 @@ void WeightWindows::update_weights(const Tally* tally, const std::string& value, fatal_error(msg); } - // use the index of the particle in the filter to down-select data later particle_idx = p_it - particles.begin(); } - // Extract 2D slices of tally mean and sum-of-squares for the selected - // particle type and score. The tally data is logically reshaped to 5D - // (filter dims, scores, result values) and transposed so that the - // energy and mesh dimensions are in positions 1 and 2. + // The tally results array is 3D: (n_filter_combos, n_scores, n_result_types). + // The first dimension is a row-major flattening of up to 3 filter dimensions + // (particle, energy, mesh) whose storage order depends on which filters the + // tally has. We need to map our desired indices (particle, energy, mesh) + // into the correct flat filter combination index. + // + // transpose[i] tells us which storage position holds dimension i: + // i=0 -> particle, i=1 -> energy, i=2 -> mesh + // shape[j] gives the number of bins for filter storage position j. + + // Row-major strides for the 3 filter dimensions + const int stride0 = shape[1] * shape[2]; + const int stride1 = shape[2]; + xt::xtensor sum( {static_cast(e_bins), static_cast(mesh_bins)}); xt::xtensor sum_sq( {static_cast(e_bins), static_cast(mesh_bins)}); - const int sum_val = static_cast(TallyResult::SUM); - const int sum_sq_val = static_cast(TallyResult::SUM_SQ); + + const int i_sum = static_cast(TallyResult::SUM); + const int i_sum_sq = static_cast(TallyResult::SUM_SQ); + for (int e = 0; e < e_bins; e++) { for (int64_t m = 0; m < mesh_bins; m++) { - // Map transposed (particle, energy, mesh, score, result) indices - // back to the original filter ordering using the transpose permutation - std::array tidx = { - particle_idx, e, static_cast(m), score_index, sum_val}; - size_t off = 0; - for (int d = 0; d < 5; d++) - off += tidx[d] * src_strides[transpose[d]]; - sum(e, m) = results_arr.data()[off]; - - tidx[4] = sum_sq_val; - off = 0; - for (int d = 0; d < 5; d++) - off += tidx[d] * src_strides[transpose[d]]; - sum_sq(e, m) = results_arr.data()[off]; + // Place particle, energy, and mesh indices into their storage positions + std::array idx = {0, 0, 0}; + idx[transpose[0]] = particle_idx; + idx[transpose[1]] = e; + idx[transpose[2]] = static_cast(m); + + // Compute flat filter combination index (row-major over filter dims) + int flat = idx[0] * stride0 + idx[1] * stride1 + idx[2]; + + sum(e, m) = results_arr(flat, score_index, i_sum); + sum_sq(e, m) = results_arr(flat, score_index, i_sum_sq); } } int n = tally->n_realizations_; From 5fc19cebe7d28e9c804fce47c15a121e1c04e13c Mon Sep 17 00:00:00 2001 From: John Tramm Date: Tue, 10 Feb 2026 10:56:42 -0600 Subject: [PATCH 07/51] removed dead code --- include/openmc/tallies/tally.h | 6 --- include/openmc/tensor.h | 95 +--------------------------------- 2 files changed, 2 insertions(+), 99 deletions(-) diff --git a/include/openmc/tallies/tally.h b/include/openmc/tallies/tally.h index 5e3426d825c..719043135b4 100644 --- a/include/openmc/tallies/tally.h +++ b/include/openmc/tallies/tally.h @@ -256,12 +256,6 @@ double distance_to_time_boundary(double time, double speed); //! Determine which tallies should be active void setup_active_tallies(); -// Alias for the type returned by xt::adapt(...). N is the dimension of the -// multidimensional array -template -using adaptor_type = - xt::xtensor_adaptor, N>; - #ifdef OPENMC_MPI //! Collect all tally results onto master process void reduce_tally_results(); diff --git a/include/openmc/tensor.h b/include/openmc/tensor.h index 8f81c631fd9..0198a8bcab8 100644 --- a/include/openmc/tensor.h +++ b/include/openmc/tensor.h @@ -855,26 +855,8 @@ xtensor operator+(T val, const xtensor& arr) return arr + val; } -template -xtensor operator-(T val, const xtensor& arr) -{ - xtensor r(arr.shape()); - for (std::size_t i = 0; i < arr.size(); ++i) - r.data()[i] = val - arr.data()[i]; - return r; -} - -template -xtensor operator/(T val, const xtensor& arr) -{ - xtensor r(arr.shape()); - for (std::size_t i = 0; i < arr.size(); ++i) - r.data()[i] = val / arr.data()[i]; - return r; -} - // Mixed-type arithmetic: xtensor op xtensor -// Returns xtensor for common cases (int*double, double/int, etc.) +// Returns xtensor (used for int*double mesh arithmetic in mesh.cpp) template::value>> xtensor operator*( @@ -899,30 +881,6 @@ xtensor operator/( return r; } -template::value>> -xtensor operator+( - const xtensor& a, const xtensor& b) -{ - xtensor r(a.shape()); - for (std::size_t i = 0; i < a.size(); ++i) - r.data()[i] = static_cast(a.data()[i]) + - static_cast(b.data()[i]); - return r; -} - -template::value>> -xtensor operator-( - const xtensor& a, const xtensor& b) -{ - xtensor r(a.shape()); - for (std::size_t i = 0; i < a.size(); ++i) - r.data()[i] = static_cast(a.data()[i]) - - static_cast(b.data()[i]); - return r; -} - //============================================================================== // xarray: dynamic-dimension tensor backed by openmc::vector //============================================================================== @@ -1194,7 +1152,7 @@ xtensor& xtensor::operator=(const xarray& other) } // Mixed-type arithmetic: xarray op xtensor -// These handle cases like xarray * xtensor +// (used for xarray * xtensor mesh arithmetic in mesh.cpp) template xtensor operator*(const xarray& a, const xtensor& b) { @@ -1235,26 +1193,6 @@ xtensor operator/(const xarray& a, const xtensor& b) return r; } -template -xtensor operator+(const xarray& a, const xtensor& b) -{ - xtensor r(b.shape()); - for (std::size_t i = 0; i < b.size(); ++i) - r.data()[i] = static_cast(a.data()[i]) + - static_cast(b.data()[i]); - return r; -} - -template -xtensor operator+(const xtensor& a, const xarray& b) -{ - xtensor r(a.shape()); - for (std::size_t i = 0; i < a.size(); ++i) - r.data()[i] = static_cast(a.data()[i]) + - static_cast(b.data()[i]); - return r; -} - //============================================================================== // xtensor_fixed>: fixed-size tensor //============================================================================== @@ -1496,12 +1434,6 @@ xtensor empty_like(const xtensor& o) return xtensor(o.shape()); } -template -xtensor ones_like(const xtensor& o) -{ - return xtensor(o.shape(), T(1)); -} - // full_like: create tensor with same shape, filled with value template xtensor full_like(const xtensor& o, V val) @@ -2047,15 +1979,6 @@ xarray log(const xarray& a) return r; } -template -xtensor exp(const xtensor& a) -{ - xtensor r(a.shape()); - for (std::size_t i = 0; i < a.size(); ++i) - r.data()[i] = std::exp(a.data()[i]); - return r; -} - template xtensor abs(const xtensor& a) { @@ -2256,20 +2179,6 @@ struct is_xt_container> : std::true_type {}; template struct is_xt_container> : std::true_type {}; -// Compatibility types (unused but referenced in some template code) -template -using xbuffer_adaptor = openmc::vector>>; - -template -using xtensor_adaptor = xtensor; - -// noalias: no-op passthrough (xtensor optimization hint) -template -T& noalias(T& x) -{ - return x; -} - } // namespace xt #endif // OPENMC_TENSOR_H From 347a6fecc96b4902f5a87a40f5b20498308d0cd6 Mon Sep 17 00:00:00 2001 From: John Tramm Date: Tue, 10 Feb 2026 11:17:55 -0600 Subject: [PATCH 08/51] Added better comments and code cleanup --- include/openmc/tensor.h | 91 ++++++++++++++++++++++++++++++++--------- 1 file changed, 72 insertions(+), 19 deletions(-) diff --git a/include/openmc/tensor.h b/include/openmc/tensor.h index 0198a8bcab8..8d813dc7f29 100644 --- a/include/openmc/tensor.h +++ b/include/openmc/tensor.h @@ -40,8 +40,13 @@ struct xshape {}; //============================================================================== // Slice/view helper types +// +// These are sentinel types used as arguments to view() to select rows, +// columns, or subranges of a tensor. xt::all() selects an entire axis, +// xt::range(start, stop) selects a half-open subrange. //============================================================================== +//! Sentinel returned by xt::all(); tells view() to keep the full axis. struct xall_type {}; inline xall_type all() @@ -49,6 +54,7 @@ inline xall_type all() return {}; } +//! Half-open index range [start, stop) returned by xt::range(). struct xrange_type { std::ptrdiff_t start; std::ptrdiff_t stop; @@ -60,15 +66,20 @@ inline xrange_type range(std::ptrdiff_t start, std::ptrdiff_t stop) } namespace placeholders { +//! Placeholder for "end of axis" in xt::range(), e.g. range(1, xt::placeholders::_). constexpr std::ptrdiff_t _ = std::numeric_limits::max(); } -// Ownership tags for adapt() +//! Ownership tag for adapt(): data is copied, caller retains ownership. struct no_ownership {}; +//! Ownership tag for adapt(): data is copied then delete[]'d. struct acquire_ownership {}; //============================================================================== -// xshape traits for xtensor_fixed +// xshape traits (internal) +// +// Extracts compile-time dimension sizes from an xshape parameter +// for use by xtensor_fixed. //============================================================================== namespace detail { @@ -85,7 +96,12 @@ struct xshape_traits> { } // namespace detail //============================================================================== -// Storage type: avoids std::vector specialization +// Storage type mapping +// +// std::vector is a bit-packed specialization that returns proxy objects +// instead of real references, which breaks generic code. storage_type_map +// redirects bool to unsigned char so that xtensor stores one byte +// per element with normal reference semantics. //============================================================================== template @@ -100,7 +116,12 @@ template using storage_type = typename storage_type_map::type; //============================================================================== -// 1D view: a strided view into a contiguous buffer +// xtensor_view_1d: a read/write view of one row or column of a tensor. +// +// Returned by view(tensor, index, all()) or view(tensor, all(), index). +// Holds a pointer, element count, and stride into the parent tensor's +// storage — no allocation or copy. Supports element access, assignment +// (from tensors, views, or scalars), compound arithmetic, and iteration. //============================================================================== template @@ -191,7 +212,8 @@ class xtensor_view_1d { return *this; } - // Strided iterator + //! Iterator that steps by stride_ through the parent buffer, enabling + //! range-for loops and STL algorithms over non-contiguous view elements. class const_iterator { const T* ptr_; std::size_t stride_; @@ -351,7 +373,11 @@ class xtensor_view_1d { }; //============================================================================== -// 2D view: a strided 2D view into a contiguous buffer +// xtensor_view_2d: a read/write view of a 2D slice of a higher-dimensional +// tensor. +// +// Returned by view(3D_tensor, index, all(), all()). Holds a pointer and +// strides into the parent tensor's storage — no allocation or copy. //============================================================================== template @@ -404,7 +430,11 @@ class xtensor_view_2d { }; //============================================================================== -// Flat view: wraps all elements for bulk assignment +// xtensor_view_flat: a view of all elements of a tensor as a flat sequence. +// +// Returned by view(tensor, all()). Used for bulk operations that treat the +// entire tensor as a 1D array, e.g. assigning from a raw buffer or filling +// with a scalar. No allocation or copy. //============================================================================== template @@ -433,7 +463,12 @@ class xtensor_view_flat { }; //============================================================================== -// xtensor: N-dimensional tensor backed by openmc::vector +// xtensor: fixed-rank N-dimensional tensor. +// +// The primary data type in this header. Stores elements in a contiguous +// row-major openmc::vector> with a compile-time rank N. +// Provides multi-dimensional operator() indexing, element-wise arithmetic, +// and view() accessors for extracting rows, columns, and slices. //============================================================================== template @@ -882,7 +917,11 @@ xtensor operator/( } //============================================================================== -// xarray: dynamic-dimension tensor backed by openmc::vector +// xarray: dynamic-rank tensor. +// +// Like xtensor but the number of dimensions is determined at runtime. +// Used when the rank is not known at compile time (e.g. data read from +// HDF5 files whose dimensionality varies). //============================================================================== template @@ -1194,7 +1233,11 @@ xtensor operator/(const xarray& a, const xtensor& b) } //============================================================================== -// xtensor_fixed>: fixed-size tensor +// xtensor_fixed>: compile-time-sized 2D tensor. +// +// Both the rank and the dimensions are template parameters, so the storage +// is a plain C array with no heap allocation. Used for small, fixed-size +// matrices (e.g. tally result accumulators). //============================================================================== template @@ -1229,7 +1272,8 @@ class xtensor_fixed { }; //============================================================================== -// adapt() functions +// adapt(): create a tensor by copying data from an existing raw pointer or +// std::vector. Several overloads handle different ownership semantics. //============================================================================== // Adapt a std::vector into a 1D xtensor (copy) @@ -1341,7 +1385,7 @@ xarray adapt(const T* ptr, const ShapeType& shape) } //============================================================================== -// Construction helpers +// Construction helpers: zeros, zeros_like, full_like, empty, empty_like //============================================================================== template @@ -1463,7 +1507,12 @@ xtensor linspace(T start, T stop, std::size_t n) } //============================================================================== -// view() functions +// view(): extract rows, columns, slices, or flat views from tensors. +// +// Returns lightweight view objects (xtensor_view_1d, _2d, or _flat) that +// reference the parent tensor's storage without copying. Overloads are +// selected by argument types: int pins an axis, all() keeps it, range() +// selects a subrange. //============================================================================== // Resolve a range endpoint: if == placeholders::_, use dim as the end @@ -1727,7 +1776,8 @@ xtensor_view_1d col(const xtensor& a, std::size_t j) // Math / reduction functions //============================================================================== -// sum - returns a callable proxy that yields the scalar when called with () +//! Wrapper returned by sum() that supports both xt::sum(x)() (call operator) +//! and implicit conversion to scalar, matching the xtensor API convention. template struct sum_proxy { T value; @@ -1855,7 +1905,7 @@ xtensor sum(const xtensor& a, std::initializer_list axes) } } -// prod - returns a callable proxy (like sum_proxy) for prod(...)() pattern +//! Wrapper returned by prod(), analogous to sum_proxy. template struct prod_proxy { T value; @@ -1938,7 +1988,8 @@ bool all(const xtensor& a) return true; } -// argmin - returns a subscriptable proxy for argmin(...)[0] pattern +//! Wrapper returned by argmin() that supports both argmin(x)[0] (subscript) +//! and implicit conversion to size_t/int, matching the xtensor API convention. struct argmin_result { std::size_t value; std::size_t operator[](std::size_t) const { return value; } @@ -2079,7 +2130,8 @@ xarray eval(const xarray& a) // concatenate & xtuple //============================================================================== -// Generic xtuple_holder that stores (pointer, size) pairs for any container type +//! Holds references to up to 10 tensors for concatenate(). +//! Created by xt::xtuple(a, b, ...) and consumed by xt::concatenate(). template struct xtuple_holder { struct entry { @@ -2163,10 +2215,11 @@ xtensor flip(const xtensor& a, std::size_t axis = 0) } //============================================================================== -// Compatibility stubs +// Type traits //============================================================================== -// is_xt_container trait for SFINAE in write_dataset +//! Type trait that is true for xtensor, xarray, and xtensor_fixed. +//! Used by hdf5_interface.h to select the correct write_dataset overload. template struct is_xt_container : std::false_type {}; From 430a7c3cbaa1ffe524f845702387d0182e9898b2 Mon Sep 17 00:00:00 2001 From: John Tramm Date: Tue, 10 Feb 2026 11:45:55 -0600 Subject: [PATCH 09/51] Cleaning up the tensor implementation to group data/methods/constructors etc together in a more organized manner. --- include/openmc/tensor.h | 443 ++++++++++++++++++++++++---------------- 1 file changed, 272 insertions(+), 171 deletions(-) diff --git a/include/openmc/tensor.h b/include/openmc/tensor.h index 8d813dc7f29..bb80e5a7902 100644 --- a/include/openmc/tensor.h +++ b/include/openmc/tensor.h @@ -126,25 +126,35 @@ using storage_type = typename storage_type_map::type; template class xtensor_view_1d { - T* data_; - std::size_t size_; - std::size_t stride_; - public: + //-------------------------------------------------------------------------- + // Types + using value_type = std::remove_const_t; + //-------------------------------------------------------------------------- + // Constructors + xtensor_view_1d(T* data, std::size_t size, std::size_t stride = 1) : data_(data), size_(size), stride_(stride) {} + //-------------------------------------------------------------------------- + // Accessors + T& operator()(std::size_t i) { return data_[i * stride_]; } const T& operator()(std::size_t i) const { return data_[i * stride_]; } T& operator[](std::size_t i) { return data_[i * stride_]; } const T& operator[](std::size_t i) const { return data_[i * stride_]; } std::size_t size() const { return size_; } + T* data() { return data_; } + const T* data() const { return data_; } + std::size_t stride() const { return stride_; } + + //-------------------------------------------------------------------------- + // Assignment operators - // Assignment from another 1D view template xtensor_view_1d& operator=(const xtensor_view_1d& other) { @@ -153,7 +163,6 @@ class xtensor_view_1d { return *this; } - // Assignment from xarray template xtensor_view_1d& operator=(const xarray& other) { @@ -162,7 +171,6 @@ class xtensor_view_1d { return *this; } - // Assignment from xtensor (any dimension, uses linear indexing) template xtensor_view_1d& operator=(const xtensor& other) { @@ -171,7 +179,6 @@ class xtensor_view_1d { return *this; } - // Assignment from scalar template auto operator=(U val) -> std::enable_if_t::value, xtensor_view_1d&> @@ -181,7 +188,9 @@ class xtensor_view_1d { return *this; } + //-------------------------------------------------------------------------- // Compound assignment operators + template xtensor_view_1d& operator+=(const xtensor& o) { @@ -212,6 +221,9 @@ class xtensor_view_1d { return *this; } + //-------------------------------------------------------------------------- + // Iterators + //! Iterator that steps by stride_ through the parent buffer, enabling //! range-for loops and STL algorithms over non-contiguous view elements. class const_iterator { @@ -367,9 +379,13 @@ class xtensor_view_1d { return const_iterator(data_ + size_ * stride_, stride_); } - T* data() { return data_; } - const T* data() const { return data_; } - std::size_t stride() const { return stride_; } +private: + //-------------------------------------------------------------------------- + // Data members + + T* data_; + std::size_t size_; + std::size_t stride_; }; //============================================================================== @@ -382,18 +398,23 @@ class xtensor_view_1d { template class xtensor_view_2d { - T* data_; - std::size_t shape0_, shape1_; - std::size_t stride0_, stride1_; - public: + //-------------------------------------------------------------------------- + // Types + using value_type = std::remove_const_t; + //-------------------------------------------------------------------------- + // Constructors + xtensor_view_2d(T* data, std::size_t s0, std::size_t s1, std::size_t st0, std::size_t st1) : data_(data), shape0_(s0), shape1_(s1), stride0_(st0), stride1_(st1) {} + //-------------------------------------------------------------------------- + // Accessors + T& operator()(std::size_t i, std::size_t j) { return data_[i * stride0_ + j * stride1_]; @@ -406,7 +427,9 @@ class xtensor_view_2d { std::size_t size() const { return shape0_ * shape1_; } std::array shape() const { return {shape0_, shape1_}; } - // Assignment from 2D tensor + //-------------------------------------------------------------------------- + // Assignment operators + template auto operator=(const xtensor& other) -> std::enable_if_t @@ -417,7 +440,6 @@ class xtensor_view_2d { return *this; } - // Assignment from scalar template auto operator=(U val) -> std::enable_if_t::value, xtensor_view_2d&> @@ -427,6 +449,14 @@ class xtensor_view_2d { data_[i * stride0_ + j * stride1_] = val; return *this; } + +private: + //-------------------------------------------------------------------------- + // Data members + + T* data_; + std::size_t shape0_, shape1_; + std::size_t stride0_, stride1_; }; //============================================================================== @@ -439,12 +469,15 @@ class xtensor_view_2d { template class xtensor_view_flat { - T* data_; - std::size_t size_; - public: + //-------------------------------------------------------------------------- + // Constructors + xtensor_view_flat(T* data, std::size_t size) : data_(data), size_(size) {} + //-------------------------------------------------------------------------- + // Assignment operators + template auto operator=(U val) -> std::enable_if_t::value, xtensor_view_flat&> @@ -460,6 +493,13 @@ class xtensor_view_flat { std::copy(other.data(), other.data() + size_, data_); return *this; } + +private: + //-------------------------------------------------------------------------- + // Data members + + T* data_; + std::size_t size_; }; //============================================================================== @@ -473,54 +513,43 @@ class xtensor_view_flat { template class xtensor { - openmc::vector> data_; - std::array shape_; - - std::size_t compute_size() const - { - std::size_t s = 1; - for (std::size_t i = 0; i < N; ++i) - s *= shape_[i]; - return s; - } - public: + //-------------------------------------------------------------------------- + // Types + using value_type = T; using stored_type = storage_type; using iterator = typename openmc::vector::iterator; using const_iterator = typename openmc::vector::const_iterator; using shape_type = std::array; - // Default constructor + //-------------------------------------------------------------------------- + // Constructors + xtensor() { shape_.fill(0); } - // Shape constructor (from initializer list of size_t) xtensor(std::initializer_list shape) { std::copy(shape.begin(), shape.end(), shape_.begin()); data_.resize(compute_size()); } - // Shape + fill value constructor (from initializer list) xtensor(std::initializer_list shape, T val) { std::copy(shape.begin(), shape.end(), shape_.begin()); data_.assign(compute_size(), val); } - // Shape constructor (from array) explicit xtensor(const std::array& shape) : shape_(shape) { data_.resize(compute_size()); } - // Shape + fill value constructor (from array) xtensor(const std::array& shape, T val) : shape_(shape) { data_.assign(compute_size(), val); } - // Construct from data + shape xtensor( openmc::vector&& data, const std::array& shape) : data_(std::move(data)), shape_(shape) @@ -531,8 +560,7 @@ class xtensor { : data_(data), shape_(shape) {} - // 1D initializer_list constructor (only for N==1, T != size_t to avoid - // ambiguity) + //! 1D initializer_list constructor (N==1 only, T != size_t to avoid ambiguity) template::value>> xtensor(std::initializer_list vals) @@ -541,7 +569,37 @@ class xtensor { data_.assign(vals.begin(), vals.end()); } - // Static factory: from_shape + //! Converting constructor from a 1D view (copies data) + template> + xtensor(const xtensor_view_1d& v) + { + shape_[0] = v.size(); + data_.resize(v.size()); + for (std::size_t i = 0; i < v.size(); ++i) + data_[i] = v(i); + } + + //! Converting constructor from a 2D view (copies data) + template> + xtensor(const xtensor_view_2d& v) + { + auto s = v.shape(); + shape_[0] = s[0]; + shape_[1] = s[1]; + data_.resize(s[0] * s[1]); + for (std::size_t i = 0; i < s[0]; ++i) + for (std::size_t j = 0; j < s[1]; ++j) + data_[i * s[1] + j] = v(i, j); + } + + //! Converting constructor from xarray (copies data) + xtensor(const xarray& other); + + //-------------------------------------------------------------------------- + // Factory methods + static xtensor from_shape(const std::array& shape) { return xtensor(shape); @@ -555,7 +613,57 @@ class xtensor { return result; } - // Multi-dimensional indexing + //-------------------------------------------------------------------------- + // Assignment operators + + xtensor& operator=(const xarray& other); + + //! Cross-type assignment from xtensor + template::value>> + xtensor& operator=(const xtensor& other) + { + shape_ = other.shape(); + data_.resize(other.size()); + for (std::size_t i = 0; i < other.size(); ++i) + data_[i] = static_cast(other.data()[i]); + return *this; + } + + template> + xtensor& operator=(const xtensor_view_1d& v) + { + shape_[0] = v.size(); + data_.resize(v.size()); + for (std::size_t i = 0; i < v.size(); ++i) + data_[i] = v(i); + return *this; + } + + template::value>> + xtensor& operator=(std::initializer_list vals) + { + shape_[0] = vals.size(); + data_.assign(vals.begin(), vals.end()); + return *this; + } + + //-------------------------------------------------------------------------- + // Accessors + + stored_type* data() { return data_.data(); } + const stored_type* data() const { return data_.data(); } + std::size_t size() const { return data_.size(); } + const shape_type& shape() const { return shape_; } + std::size_t shape(std::size_t dim) const { return shape_[dim]; } + bool empty() const { return data_.empty(); } + static constexpr std::size_t dimension() { return N; } + + //-------------------------------------------------------------------------- + // Indexing + + //! Multi-dimensional indexing (row-major) template stored_type& operator()(Indices... indices) { @@ -568,20 +676,23 @@ class xtensor { return data_[offset(static_cast(indices)...)]; } - // Linear indexing + //! Linear (flat) indexing stored_type& operator[](std::size_t i) { return data_[i]; } const stored_type& operator[](std::size_t i) const { return data_[i]; } - // Data access - stored_type* data() { return data_.data(); } - const stored_type* data() const { return data_.data(); } - std::size_t size() const { return data_.size(); } - const shape_type& shape() const { return shape_; } - std::size_t shape(std::size_t dim) const { return shape_[dim]; } - bool empty() const { return data_.empty(); } - static constexpr std::size_t dimension() { return N; } + //-------------------------------------------------------------------------- + // Iterators + + iterator begin() { return data_.begin(); } + iterator end() { return data_.end(); } + const_iterator begin() const { return data_.begin(); } + const_iterator end() const { return data_.end(); } + const_iterator cbegin() const { return data_.cbegin(); } + const_iterator cend() const { return data_.cend(); } + + //-------------------------------------------------------------------------- + // Methods - // Resize void resize(std::initializer_list shape) { std::copy(shape.begin(), shape.end(), shape_.begin()); @@ -594,7 +705,6 @@ class xtensor { data_.resize(compute_size()); } - // Resize from vector (needed for xarray compatibility) void resize(const openmc::vector& shape) { for (std::size_t i = 0; i < N && i < shape.size(); ++i) @@ -602,7 +712,6 @@ class xtensor { data_.resize(compute_size()); } - // Resize from vector void resize(const openmc::vector& shape) { for (std::size_t i = 0; i < N && i < shape.size(); ++i) @@ -610,18 +719,11 @@ class xtensor { data_.resize(compute_size()); } - // Fill void fill(T val) { std::fill(data_.begin(), data_.end(), val); } - // Iterators - iterator begin() { return data_.begin(); } - iterator end() { return data_.end(); } - const_iterator begin() const { return data_.begin(); } - const_iterator end() const { return data_.end(); } - const_iterator cbegin() const { return data_.cbegin(); } - const_iterator cend() const { return data_.cend(); } + //-------------------------------------------------------------------------- + // Compound assignment operators (scalar) - // Compound assignment with scalar xtensor& operator+=(T val) { for (auto& x : data_) @@ -647,7 +749,9 @@ class xtensor { return *this; } - // Compound assignment with tensor + //-------------------------------------------------------------------------- + // Compound assignment operators (tensor and view) + xtensor& operator+=(const xtensor& o) { for (std::size_t i = 0; i < data_.size(); ++i) @@ -673,7 +777,6 @@ class xtensor { return *this; } - // Compound assignment with 1D view template xtensor& operator+=(const xtensor_view_1d& o) { @@ -682,7 +785,9 @@ class xtensor { return *this; } - // Element-wise binary ops (same shape) + //-------------------------------------------------------------------------- + // Element-wise binary operators (tensor op tensor, same shape) + xtensor operator+(const xtensor& o) const { xtensor r(shape_); @@ -712,7 +817,9 @@ class xtensor { return r; } - // Binary ops: tensor op scalar + //-------------------------------------------------------------------------- + // Element-wise binary operators (tensor op scalar) + xtensor operator+(T val) const { xtensor r(shape_); @@ -742,7 +849,6 @@ class xtensor { return r; } - // Unary minus xtensor operator-() const { xtensor r(shape_); @@ -751,7 +857,9 @@ class xtensor { return r; } - // Comparison operators (element-wise, return bool tensor) + //-------------------------------------------------------------------------- + // Element-wise comparison operators (return bool tensor) + xtensor operator<=(T val) const { xtensor r(shape_); @@ -780,8 +888,6 @@ class xtensor { r.data()[i] = data_[i] > val; return r; } - - // Tensor-tensor comparison xtensor operator<(const xtensor& o) const { xtensor r(shape_); @@ -790,72 +896,19 @@ class xtensor { return r; } - // Conversion from 1D view - template> - xtensor(const xtensor_view_1d& v) - { - shape_[0] = v.size(); - data_.resize(v.size()); - for (std::size_t i = 0; i < v.size(); ++i) - data_[i] = v(i); - } - - // Conversion from 2D view - template> - xtensor(const xtensor_view_2d& v) - { - auto s = v.shape(); - shape_[0] = s[0]; - shape_[1] = s[1]; - data_.resize(s[0] * s[1]); - for (std::size_t i = 0; i < s[0]; ++i) - for (std::size_t j = 0; j < s[1]; ++j) - data_[i * s[1] + j] = v(i, j); - } - - // Converting constructor from xarray - xtensor(const xarray& other); - - // Assignment from xarray - xtensor& operator=(const xarray& other); - - // Cross-type assignment from xtensor - template::value>> - xtensor& operator=(const xtensor& other) - { - shape_ = other.shape(); - data_.resize(other.size()); - for (std::size_t i = 0; i < other.size(); ++i) - data_[i] = static_cast(other.data()[i]); - return *this; - } - - // Assignment from 1D view - template> - xtensor& operator=(const xtensor_view_1d& v) - { - shape_[0] = v.size(); - data_.resize(v.size()); - for (std::size_t i = 0; i < v.size(); ++i) - data_[i] = v(i); - return *this; - } +private: + //-------------------------------------------------------------------------- + // Private methods - // Assignment from initializer list of values (for 1D only) - template::value>> - xtensor& operator=(std::initializer_list vals) + std::size_t compute_size() const { - shape_[0] = vals.size(); - data_.assign(vals.begin(), vals.end()); - return *this; + std::size_t s = 1; + for (std::size_t i = 0; i < N; ++i) + s *= shape_[i]; + return s; } -private: - // Offset calculations for row-major layout + //! Row-major offset calculations for operator() std::size_t offset(std::size_t i0) const { return i0; } std::size_t offset(std::size_t i0, std::size_t i1) const @@ -873,6 +926,12 @@ class xtensor { { return ((i0 * shape_[1] + i1) * shape_[2] + i2) * shape_[3] + i3; } + + //-------------------------------------------------------------------------- + // Data members + + openmc::vector> data_; + std::array shape_; }; // scalar op tensor (same type) @@ -926,15 +985,18 @@ xtensor operator/( template class xarray { - openmc::vector> data_; - openmc::vector shape_; - public: + //-------------------------------------------------------------------------- + // Types + using value_type = T; using stored_type = storage_type; using iterator = typename openmc::vector::iterator; using const_iterator = typename openmc::vector::const_iterator; + //-------------------------------------------------------------------------- + // Constructors + xarray() = default; explicit xarray(const openmc::vector& shape) : shape_(shape) @@ -953,13 +1015,12 @@ class xarray { data_.assign(total, val); } - // Construct from initializer_list of values (creates 1D xarray) xarray(std::initializer_list vals) : shape_({vals.size()}) { data_.assign(vals.begin(), vals.end()); } - // Converting constructor from xtensor + //! Converting constructor from xtensor (copies data) template xarray(const xtensor& t) { @@ -968,7 +1029,6 @@ class xarray { data_.assign(t.data(), t.data() + t.size()); } - // Construct from vector shape explicit xarray(const openmc::vector& shape) { for (auto d : shape) @@ -979,7 +1039,19 @@ class xarray { data_.resize(total); } - // Multi-dimensional indexing (up to 4D) + //-------------------------------------------------------------------------- + // Accessors + + stored_type* data() { return data_.data(); } + const stored_type* data() const { return data_.data(); } + std::size_t size() const { return data_.size(); } + const openmc::vector& shape() const { return shape_; } + std::size_t shape(std::size_t dim) const { return shape_[dim]; } + bool empty() const { return data_.empty(); } + + //-------------------------------------------------------------------------- + // Indexing + template stored_type& operator()(Indices... indices) { @@ -995,12 +1067,16 @@ class xarray { stored_type& operator[](std::size_t i) { return data_[i]; } const stored_type& operator[](std::size_t i) const { return data_[i]; } - stored_type* data() { return data_.data(); } - const stored_type* data() const { return data_.data(); } - std::size_t size() const { return data_.size(); } - const openmc::vector& shape() const { return shape_; } - std::size_t shape(std::size_t dim) const { return shape_[dim]; } - bool empty() const { return data_.empty(); } + //-------------------------------------------------------------------------- + // Iterators + + iterator begin() { return data_.begin(); } + iterator end() { return data_.end(); } + const_iterator begin() const { return data_.begin(); } + const_iterator end() const { return data_.end(); } + + //-------------------------------------------------------------------------- + // Methods void resize(const openmc::vector& shape) { @@ -1022,7 +1098,7 @@ class xarray { data_.resize(total); } - // reshape: change shape without changing data (total must match) + //! Reinterpret the shape without changing the underlying data template void reshape(const ShapeType& new_shape) { @@ -1033,12 +1109,21 @@ class xarray { void fill(T val) { std::fill(data_.begin(), data_.end(), val); } - iterator begin() { return data_.begin(); } - iterator end() { return data_.end(); } - const_iterator begin() const { return data_.begin(); } - const_iterator end() const { return data_.end(); } + //! Conversion to xtensor (copies data) + template + operator xtensor() const + { + std::array s {}; + for (std::size_t i = 0; i < M && i < shape_.size(); ++i) + s[i] = shape_[i]; + xtensor result(s); + std::copy(data_.begin(), data_.end(), result.data()); + return result; + } + + //-------------------------------------------------------------------------- + // Compound assignment operators (scalar) - // Compound assignment xarray& operator+=(T val) { for (auto& x : data_) @@ -1064,7 +1149,9 @@ class xarray { return *this; } - // Binary ops with scalar + //-------------------------------------------------------------------------- + // Element-wise binary operators (xarray op scalar) + xarray operator*(T val) const { xarray r(shape_); @@ -1101,7 +1188,9 @@ class xarray { return r; } - // Comparison with scalar + //-------------------------------------------------------------------------- + // Element-wise comparison operators (return bool xarray) + xarray operator>(T val) const { xarray r(shape_); @@ -1131,19 +1220,11 @@ class xarray { return r; } - // Convert to xtensor (explicit shape) - template - operator xtensor() const - { - std::array s {}; - for (std::size_t i = 0; i < M && i < shape_.size(); ++i) - s[i] = shape_[i]; - xtensor result(s); - std::copy(data_.begin(), data_.end(), result.data()); - return result; - } - private: + //-------------------------------------------------------------------------- + // Private methods + + //! Row-major offset calculations for operator() std::size_t compute_offset(std::size_t i0) const { return i0; } std::size_t compute_offset(std::size_t i0, std::size_t i1) const @@ -1162,6 +1243,12 @@ class xarray { { return ((i0 * shape_[1] + i1) * shape_[2] + i2) * shape_[3] + i3; } + + //-------------------------------------------------------------------------- + // Data members + + openmc::vector> data_; + openmc::vector shape_; }; // scalar op xarray @@ -1242,15 +1329,15 @@ xtensor operator/(const xarray& a, const xtensor& b) template class xtensor_fixed { - static constexpr std::size_t total_ = detail::xshape_traits::total; - static constexpr std::size_t dim0_ = detail::xshape_traits::dim0; - static constexpr std::size_t dim1_ = detail::xshape_traits::dim1; - - T data_[total_] = {}; - public: + //-------------------------------------------------------------------------- + // Types + using value_type = T; + //-------------------------------------------------------------------------- + // Accessors + template T& operator()(I0 i, I1 j) { @@ -1268,7 +1355,21 @@ class xtensor_fixed { const T* data() const { return data_; } std::size_t size() const { return total_; } std::array shape() const { return {dim0_, dim1_}; } + + //-------------------------------------------------------------------------- + // Methods + void fill(T val) { std::fill(data_, data_ + total_, val); } + +private: + //-------------------------------------------------------------------------- + // Data members + + static constexpr std::size_t total_ = detail::xshape_traits::total; + static constexpr std::size_t dim0_ = detail::xshape_traits::dim0; + static constexpr std::size_t dim1_ = detail::xshape_traits::dim1; + + T data_[total_] = {}; }; //============================================================================== From 4e431927fcfc4cd90681b0d1741d4c8188f4e5a5 Mon Sep 17 00:00:00 2001 From: John Tramm Date: Tue, 10 Feb 2026 15:24:11 -0600 Subject: [PATCH 10/51] removed dead code --- include/openmc/tensor.h | 481 ---------------------------------------- 1 file changed, 481 deletions(-) diff --git a/include/openmc/tensor.h b/include/openmc/tensor.h index bb80e5a7902..ecd0a5fb52a 100644 --- a/include/openmc/tensor.h +++ b/include/openmc/tensor.h @@ -155,14 +155,6 @@ class xtensor_view_1d { //-------------------------------------------------------------------------- // Assignment operators - template - xtensor_view_1d& operator=(const xtensor_view_1d& other) - { - for (std::size_t i = 0; i < size_; ++i) - data_[i * stride_] = other(i); - return *this; - } - template xtensor_view_1d& operator=(const xarray& other) { @@ -199,21 +191,6 @@ class xtensor_view_1d { return *this; } - template - xtensor_view_1d& operator+=(const xtensor_view_1d& o) - { - for (std::size_t i = 0; i < size_; ++i) - data_[i * stride_] += o(i); - return *this; - } - - xtensor_view_1d& operator/=(value_type val) - { - for (std::size_t i = 0; i < size_; ++i) - data_[i * stride_] /= val; - return *this; - } - xtensor_view_1d& operator*=(value_type val) { for (std::size_t i = 0; i < size_; ++i) @@ -486,14 +463,6 @@ class xtensor_view_flat { return *this; } - template - auto operator=(const Container& other) -> - std::enable_if_t::value, xtensor_view_flat&> - { - std::copy(other.data(), other.data() + size_, data_); - return *this; - } - private: //-------------------------------------------------------------------------- // Data members @@ -550,15 +519,6 @@ class xtensor { data_.assign(compute_size(), val); } - xtensor( - openmc::vector&& data, const std::array& shape) - : data_(std::move(data)), shape_(shape) - {} - - xtensor(const openmc::vector& data, - const std::array& shape) - : data_(data), shape_(shape) - {} //! 1D initializer_list constructor (N==1 only, T != size_t to avoid ambiguity) template& shape) - { - return xtensor(shape); - } - static xtensor from_shape(std::initializer_list shape) { xtensor result; @@ -699,37 +654,11 @@ class xtensor { data_.resize(compute_size()); } - void resize(const std::array& shape) - { - shape_ = shape; - data_.resize(compute_size()); - } - - void resize(const openmc::vector& shape) - { - for (std::size_t i = 0; i < N && i < shape.size(); ++i) - shape_[i] = shape[i]; - data_.resize(compute_size()); - } - - void resize(const openmc::vector& shape) - { - for (std::size_t i = 0; i < N && i < shape.size(); ++i) - shape_[i] = static_cast(shape[i]); - data_.resize(compute_size()); - } - void fill(T val) { std::fill(data_.begin(), data_.end(), val); } //-------------------------------------------------------------------------- // Compound assignment operators (scalar) - xtensor& operator+=(T val) - { - for (auto& x : data_) - x += val; - return *this; - } xtensor& operator-=(T val) { for (auto& x : data_) @@ -758,33 +687,6 @@ class xtensor { data_[i] += o.data_[i]; return *this; } - xtensor& operator-=(const xtensor& o) - { - for (std::size_t i = 0; i < data_.size(); ++i) - data_[i] -= o.data_[i]; - return *this; - } - xtensor& operator*=(const xtensor& o) - { - for (std::size_t i = 0; i < data_.size(); ++i) - data_[i] *= o.data_[i]; - return *this; - } - xtensor& operator/=(const xtensor& o) - { - for (std::size_t i = 0; i < data_.size(); ++i) - data_[i] /= o.data_[i]; - return *this; - } - - template - xtensor& operator+=(const xtensor_view_1d& o) - { - for (std::size_t i = 0; i < data_.size(); ++i) - data_[i] += o(i); - return *this; - } - //-------------------------------------------------------------------------- // Element-wise binary operators (tensor op tensor, same shape) @@ -802,13 +704,6 @@ class xtensor { r.data_[i] = data_[i] - o.data_[i]; return r; } - xtensor operator*(const xtensor& o) const - { - xtensor r(shape_); - for (std::size_t i = 0; i < data_.size(); ++i) - r.data_[i] = data_[i] * o.data_[i]; - return r; - } xtensor operator/(const xtensor& o) const { xtensor r(shape_); @@ -841,22 +736,6 @@ class xtensor { r.data_[i] = data_[i] * val; return r; } - xtensor operator/(T val) const - { - xtensor r(shape_); - for (std::size_t i = 0; i < data_.size(); ++i) - r.data_[i] = data_[i] / val; - return r; - } - - xtensor operator-() const - { - xtensor r(shape_); - for (std::size_t i = 0; i < data_.size(); ++i) - r.data_[i] = -data_[i]; - return r; - } - //-------------------------------------------------------------------------- // Element-wise comparison operators (return bool tensor) @@ -942,13 +821,6 @@ xtensor operator*(T val, const xtensor& arr) return arr * val; } -template::value>> -xtensor operator+(T val, const xtensor& arr) -{ - return arr + val; -} - // Mixed-type arithmetic: xtensor op xtensor // Returns xtensor (used for int*double mesh arithmetic in mesh.cpp) template& shape) - { - for (auto d : shape) - shape_.push_back(static_cast(d)); - std::size_t total = 1; - for (auto d : shape_) - total *= d; - data_.resize(total); - } - //-------------------------------------------------------------------------- // Accessors @@ -1121,58 +983,9 @@ class xarray { return result; } - //-------------------------------------------------------------------------- - // Compound assignment operators (scalar) - - xarray& operator+=(T val) - { - for (auto& x : data_) - x += val; - return *this; - } - xarray& operator-=(T val) - { - for (auto& x : data_) - x -= val; - return *this; - } - xarray& operator*=(T val) - { - for (auto& x : data_) - x *= val; - return *this; - } - xarray& operator/=(T val) - { - for (auto& x : data_) - x /= val; - return *this; - } - //-------------------------------------------------------------------------- // Element-wise binary operators (xarray op scalar) - xarray operator*(T val) const - { - xarray r(shape_); - for (std::size_t i = 0; i < data_.size(); ++i) - r.data_[i] = data_[i] * val; - return r; - } - xarray operator/(T val) const - { - xarray r(shape_); - for (std::size_t i = 0; i < data_.size(); ++i) - r.data_[i] = data_[i] / val; - return r; - } - xarray operator+(T val) const - { - xarray r(shape_); - for (std::size_t i = 0; i < data_.size(); ++i) - r.data_[i] = data_[i] + val; - return r; - } xarray operator-(T val) const { xarray r(shape_); @@ -1180,31 +993,10 @@ class xarray { r.data_[i] = data_[i] - val; return r; } - xarray operator-() const - { - xarray r(shape_); - for (std::size_t i = 0; i < data_.size(); ++i) - r.data_[i] = -data_[i]; - return r; - } //-------------------------------------------------------------------------- // Element-wise comparison operators (return bool xarray) - xarray operator>(T val) const - { - xarray r(shape_); - for (std::size_t i = 0; i < data_.size(); ++i) - r.data()[i] = data_[i] > val; - return r; - } - xarray operator<(T val) const - { - xarray r(shape_); - for (std::size_t i = 0; i < data_.size(); ++i) - r.data()[i] = data_[i] < val; - return r; - } xarray operator<=(T val) const { xarray r(shape_); @@ -1212,13 +1004,6 @@ class xarray { r.data()[i] = data_[i] <= val; return r; } - xarray operator>=(T val) const - { - xarray r(shape_); - for (std::size_t i = 0; i < data_.size(); ++i) - r.data()[i] = data_[i] >= val; - return r; - } private: //-------------------------------------------------------------------------- @@ -1251,13 +1036,6 @@ class xarray { openmc::vector shape_; }; -// scalar op xarray -template -xarray operator*(T val, const xarray& arr) -{ - return arr * val; -} - // xtensor converting constructor from xarray (defined after xarray is complete) template xtensor::xtensor(const xarray& other) @@ -1309,16 +1087,6 @@ xtensor operator/(const xtensor& a, const xarray& b) return r; } -template -xtensor operator/(const xarray& a, const xtensor& b) -{ - xtensor r(b.shape()); - for (std::size_t i = 0; i < b.size(); ++i) - r.data()[i] = static_cast(a.data()[i]) / - static_cast(b.data()[i]); - return r; -} - //============================================================================== // xtensor_fixed>: compile-time-sized 2D tensor. // @@ -1398,22 +1166,6 @@ xarray adapt(const openmc::vector& vec, const ShapeType& shape) return result; } -// Adapt std::array with explicit shape (copy) -template -xarray adapt(const std::array& arr, const ShapeType& shape) -{ - openmc::vector s; - std::size_t total = 1; - for (auto d : shape) { - auto dim = static_cast(d); - s.push_back(dim); - total *= dim; - } - xarray result(s); - std::copy(arr.data(), arr.data() + std::min(total, M), result.data()); - return result; -} - // Adapt vector with initializer_list shape (for brace-init-list deduction) template xarray adapt(const openmc::vector& vec, std::initializer_list shape) @@ -1469,22 +1221,6 @@ xarray adapt( return result; } -// Adapt const pointer with shape into xarray (copy) -template -xarray adapt(const T* ptr, const ShapeType& shape) -{ - openmc::vector s; - std::size_t total = 1; - for (auto d : shape) { - auto dim = static_cast(d); - s.push_back(dim); - total *= dim; - } - xarray result(s); - std::copy(ptr, ptr + total, result.data()); - return result; -} - //============================================================================== // Construction helpers: zeros, zeros_like, full_like, empty, empty_like //============================================================================== @@ -1504,30 +1240,6 @@ xarray zeros(const openmc::vector& shape) return xarray(shape, T(0)); } -// zeros with vector of any int type -template::value && - !std::is_same::value>> -xarray zeros(const openmc::vector& shape) -{ - openmc::vector s; - for (auto d : shape) - s.push_back(static_cast(d)); - return xarray(s, T(0)); -} - -template -xtensor zeros(const std::array& shape) -{ - return xtensor(shape, T(0)); -} - -template -xtensor zeros(const std::array& shape) -{ - return xtensor(shape, T(0)); -} - template xarray empty(std::initializer_list shape) { @@ -1545,18 +1257,6 @@ xarray empty(std::initializer_list shape) return xarray(s); } -template -xtensor empty(const std::array& shape) -{ - return xtensor(shape); -} - -template -xtensor empty(const std::array& shape) -{ - return xtensor(shape); -} - // empty with std::array shape template xarray empty(const std::array& shape) @@ -1586,12 +1286,6 @@ xtensor full_like(const xtensor& o, V val) return xtensor(o.shape(), static_cast(val)); } -template -xarray empty_like(const xarray& o) -{ - return xarray(o.shape()); -} - template xtensor linspace(T start, T stop, std::size_t n) { @@ -1714,26 +1408,6 @@ xtensor_view_2d view( a.shape()[1] * a.shape()[2], a.shape()[2]}; } -// view(3D tensor, range, all, int) -> 2D view (subset of rows) -template -xtensor_view_2d view( - xtensor& a, xrange_type r, xall_type, std::size_t k) -{ - auto start = resolve_end(r.start, a.shape()[0]); - auto stop = resolve_end(r.stop, a.shape()[0]); - return {a.data() + start * a.shape()[1] * a.shape()[2] + k, stop - start, - a.shape()[1], a.shape()[1] * a.shape()[2], a.shape()[2]}; -} -template -xtensor_view_2d view( - const xtensor& a, xrange_type r, xall_type, std::size_t k) -{ - auto start = resolve_end(r.start, a.shape()[0]); - auto stop = resolve_end(r.stop, a.shape()[0]); - return {a.data() + start * a.shape()[1] * a.shape()[2] + k, stop - start, - a.shape()[1], a.shape()[1] * a.shape()[2], a.shape()[2]}; -} - // view(fixed_2D, all, int) -> 1D column view template xtensor_view_1d view(xtensor_fixed& a, xall_type, std::size_t col) @@ -1775,22 +1449,6 @@ xtensor_view_1d view(const xarray& a, std::size_t row) return {a.data() + row * a.shape()[1], a.shape()[1], 1}; } -// view(xarray, range) -> 1D view for 1D xarray -template -xtensor_view_1d view(xarray& a, xrange_type r) -{ - auto start = resolve_end(r.start, a.shape()[0]); - auto stop = resolve_end(r.stop, a.shape()[0]); - return {a.data() + start, stop - start, 1}; -} -template -xtensor_view_1d view(const xarray& a, xrange_type r) -{ - auto start = resolve_end(r.start, a.shape()[0]); - auto stop = resolve_end(r.stop, a.shape()[0]); - return {a.data() + start, stop - start, 1}; -} - // view(2D tensor, row, xall_type) -> 1D row view (entire row) template xtensor_view_1d view(xtensor& a, std::size_t row, xall_type) @@ -1803,36 +1461,6 @@ xtensor_view_1d view(const xtensor& a, std::size_t row, xall_type return {a.data() + row * a.shape()[1], a.shape()[1], 1}; } -// view(2D tensor, row, range) -> 1D view of row subset -template -xtensor_view_1d view(xtensor& a, std::size_t row, xrange_type r) -{ - auto cols = a.shape()[1]; - auto start = resolve_end(r.start, cols); - auto stop = resolve_end(r.stop, cols); - return {a.data() + row * cols + start, stop - start, 1}; -} -template -xtensor_view_1d view(const xtensor& a, std::size_t row, xrange_type r) -{ - auto cols = a.shape()[1]; - auto start = resolve_end(r.start, cols); - auto stop = resolve_end(r.stop, cols); - return {a.data() + row * cols + start, stop - start, 1}; -} - -// view(2D xarray, row, xall_type) -> 1D row view (entire row) -template -xtensor_view_1d view(xarray& a, std::size_t row, xall_type) -{ - return {a.data() + row * a.shape()[1], a.shape()[1], 1}; -} -template -xtensor_view_1d view(const xarray& a, std::size_t row, xall_type) -{ - return {a.data() + row * a.shape()[1], a.shape()[1], 1}; -} - // view(2D xarray, row, range) -> 1D view of row subset template xtensor_view_1d view(xarray& a, std::size_t row, xrange_type r) @@ -1904,15 +1532,6 @@ sum_proxy sum(const xtensor_view_1d& v) return {s}; } -template -sum_proxy sum(const xarray& a) -{ - T s = T(0); - for (std::size_t i = 0; i < a.size(); ++i) - s += a.data()[i]; - return {s}; -} - // sum along axis - reduces one dimension // 2D sum along axis -> 1D template @@ -2042,15 +1661,6 @@ bool any(const xtensor& a) return false; } -template -bool any(const xtensor& a) -{ - for (std::size_t i = 0; i < a.size(); ++i) - if (a.data()[i]) - return true; - return false; -} - // any/all for xarray template bool any(const xarray& a) @@ -2061,15 +1671,6 @@ bool any(const xarray& a) return false; } -template -bool all(const xarray& a) -{ - for (std::size_t i = 0; i < a.size(); ++i) - if (!a.data()[i]) - return false; - return true; -} - // all (for bool tensors) template bool all(const xtensor& a) @@ -2080,15 +1681,6 @@ bool all(const xtensor& a) return true; } -template -bool all(const xtensor& a) -{ - for (std::size_t i = 0; i < a.size(); ++i) - if (!a.data()[i]) - return false; - return true; -} - //! Wrapper returned by argmin() that supports both argmin(x)[0] (subscript) //! and implicit conversion to size_t/int, matching the xtensor API convention. struct argmin_result { @@ -2122,15 +1714,6 @@ xtensor log(const xtensor& a) return r; } -template -xarray log(const xarray& a) -{ - xarray r(a.shape()); - for (std::size_t i = 0; i < a.size(); ++i) - r.data()[i] = std::log(a.data()[i]); - return r; -} - template xtensor abs(const xtensor& a) { @@ -2149,17 +1732,6 @@ xarray abs(const xarray& a) return r; } -// where(condition, true_val, false_val) - tensor condition -template -xtensor where(const xtensor& cond, U true_val, V false_val) -{ - xtensor r(cond.shape()); - for (std::size_t i = 0; i < cond.size(); ++i) - r.data()[i] = cond.data()[i] ? static_cast(true_val) - : static_cast(false_val); - return r; -} - // where with tensor true_val and scalar false_val template xtensor where( @@ -2172,29 +1744,6 @@ xtensor where( return r; } -// where with scalar true_val and tensor false_val -template -xtensor where( - const xtensor& cond, U true_val, const xtensor& false_val) -{ - xtensor r(cond.shape()); - for (std::size_t i = 0; i < cond.size(); ++i) - r.data()[i] = cond.data()[i] ? static_cast(true_val) - : false_val.data()[i]; - return r; -} - -// where with tensor true_val and tensor false_val -template -xtensor where(const xtensor& cond, - const xtensor& true_val, const xtensor& false_val) -{ - xtensor r(cond.shape()); - for (std::size_t i = 0; i < cond.size(); ++i) - r.data()[i] = cond.data()[i] ? true_val.data()[i] : false_val.data()[i]; - return r; -} - // nan_to_num template xtensor nan_to_num(const xtensor& a, T nan_val = T(0), @@ -2221,12 +1770,6 @@ xtensor eval(const xtensor& a) return a; } -template -xarray eval(const xarray& a) -{ - return a; -} - //============================================================================== // concatenate & xtuple //============================================================================== @@ -2256,20 +1799,6 @@ auto xtuple(const A& a, const B& b) return h; } -// xtuple for 3 args -template -auto xtuple(const A& a, const B& b, const C& c) - -> xtuple_holder>> -{ - using T = std::remove_const_t>; - xtuple_holder h {}; - h.entries[0] = {a.data(), a.size()}; - h.entries[1] = {b.data(), b.size()}; - h.entries[2] = {c.data(), c.size()}; - h.count = 3; - return h; -} - template xtensor concatenate(const xtuple_holder& tup) { @@ -2287,16 +1816,6 @@ xtensor concatenate(const xtuple_holder& tup) return result; } -// flip (reverse 1D tensor) -template -xtensor flip(const xtensor& a) -{ - xtensor r({a.size()}); - for (std::size_t i = 0; i < a.size(); ++i) - r.data()[i] = a.data()[a.size() - 1 - i]; - return r; -} - // flip 2D along axis 0 template xtensor flip(const xtensor& a, std::size_t axis = 0) From 7fdfb8d566e20ee0f8a2d9d442ad55005d5f4526 Mon Sep 17 00:00:00 2001 From: John Tramm Date: Wed, 11 Feb 2026 13:18:04 -0600 Subject: [PATCH 11/51] refactored to simpler (non-xtensor-like) interface. --- include/openmc/bremsstrahlung.h | 10 +- include/openmc/distribution_energy.h | 6 +- include/openmc/eigenvalue.h | 2 +- include/openmc/hdf5_interface.h | 85 +- include/openmc/material.h | 2 +- include/openmc/mesh.h | 14 +- include/openmc/mgxs.h | 2 +- include/openmc/nuclide.h | 2 +- include/openmc/photon.h | 38 +- include/openmc/plot.h | 6 +- .../openmc/random_ray/flat_source_domain.h | 2 +- include/openmc/scattdata.h | 40 +- include/openmc/secondary_correlated.h | 6 +- include/openmc/secondary_kalbach.h | 10 +- include/openmc/secondary_thermal.h | 14 +- include/openmc/tallies/tally.h | 8 +- include/openmc/tensor.h | 1783 +++++------------ include/openmc/urr.h | 6 +- include/openmc/weight_windows.h | 18 +- include/openmc/wmp.h | 4 +- include/openmc/xml_interface.h | 5 +- include/openmc/xsdata.h | 22 +- src/bremsstrahlung.cpp | 4 +- src/cmfd_solver.cpp | 30 +- src/cross_sections.cpp | 2 +- src/distribution_angle.cpp | 10 +- src/distribution_energy.cpp | 16 +- src/eigenvalue.cpp | 12 +- src/endf.cpp | 12 +- src/finalize.cpp | 2 +- src/hdf5_interface.cpp | 18 +- src/material.cpp | 38 +- src/mesh.cpp | 105 +- src/mgxs.cpp | 28 +- src/nuclide.cpp | 27 +- src/photon.cpp | 69 +- src/physics.cpp | 4 +- src/plot.cpp | 25 +- src/random_ray/flat_source_domain.cpp | 2 +- src/scattdata.cpp | 80 +- src/secondary_correlated.cpp | 28 +- src/secondary_kalbach.cpp | 20 +- src/secondary_thermal.cpp | 12 +- src/simulation.cpp | 2 +- src/source.cpp | 4 +- src/state_point.cpp | 20 +- src/tallies/filter_cell_instance.cpp | 3 +- src/tallies/filter_meshmaterial.cpp | 3 +- src/tallies/tally.cpp | 69 +- src/tallies/trigger.cpp | 2 +- src/thermal.cpp | 4 +- src/urr.cpp | 2 +- src/volume_calc.cpp | 10 +- src/weight_windows.cpp | 35 +- src/wmp.cpp | 14 +- src/xsdata.cpp | 114 +- 56 files changed, 1090 insertions(+), 1821 deletions(-) diff --git a/include/openmc/bremsstrahlung.h b/include/openmc/bremsstrahlung.h index 21a7f3c069c..d3b5868317a 100644 --- a/include/openmc/bremsstrahlung.h +++ b/include/openmc/bremsstrahlung.h @@ -14,9 +14,9 @@ namespace openmc { class BremsstrahlungData { public: // Data - xt::xtensor pdf; //!< Bremsstrahlung energy PDF - xt::xtensor cdf; //!< Bremsstrahlung energy CDF - xt::xtensor yield; //!< Photon yield + tensor::Tensor pdf; //!< Bremsstrahlung energy PDF + tensor::Tensor cdf; //!< Bremsstrahlung energy CDF + tensor::Tensor yield; //!< Photon yield }; class Bremsstrahlung { @@ -32,9 +32,9 @@ class Bremsstrahlung { namespace data { -extern xt::xtensor +extern tensor::Tensor ttb_e_grid; //! energy T of incident electron in [eV] -extern xt::xtensor +extern tensor::Tensor ttb_k_grid; //! reduced energy W/T of emitted photon } // namespace data diff --git a/include/openmc/distribution_energy.h b/include/openmc/distribution_energy.h index 75c7dd7fecd..efacb913196 100644 --- a/include/openmc/distribution_energy.h +++ b/include/openmc/distribution_energy.h @@ -86,9 +86,9 @@ class ContinuousTabular : public EnergyDistribution { 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 + tensor::Tensor e_out; //!< Outgoing energies in [eV] + tensor::Tensor p; //!< Probability density + tensor::Tensor c; //!< Cumulative distribution }; int n_region_; //!< Number of inteprolation regions diff --git a/include/openmc/eigenvalue.h b/include/openmc/eigenvalue.h index 00e485bb0b5..43874767609 100644 --- a/include/openmc/eigenvalue.h +++ b/include/openmc/eigenvalue.h @@ -24,7 +24,7 @@ namespace simulation { extern double keff_generation; //!< Single-generation k on each processor extern array k_sum; //!< Used to reduce sum and sum_sq extern vector entropy; //!< Shannon entropy at each generation -extern xt::xtensor source_frac; //!< Source fraction for UFS +extern tensor::Tensor source_frac; //!< Source fraction for UFS } // namespace simulation diff --git a/include/openmc/hdf5_interface.h b/include/openmc/hdf5_interface.h index 38ea62c62fa..c9c060e318d 100644 --- a/include/openmc/hdf5_interface.h +++ b/include/openmc/hdf5_interface.h @@ -165,24 +165,19 @@ void read_attribute(hid_t obj_id, const char* name, vector& vec) read_attr(obj_id, name, H5TypeMap::type_id, vec.data()); } -// Generic array version +// Tensor version template -void read_attribute(hid_t obj_id, const char* name, xt::xarray& arr) +void read_attribute(hid_t obj_id, const char* name, tensor::Tensor& 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; - vector buffer(size); + // Resize tensor and read data directly + openmc::vector tshape(shape.begin(), shape.end()); + arr.resize(tshape); // Read data from attribute - read_attr(obj_id, name, H5TypeMap::type_id, buffer.data()); - - // Adapt array into xarray - arr = xt::adapt(buffer, shape); + read_attr(obj_id, name, H5TypeMap::type_id, arr.data()); } // overload for std::string @@ -289,29 +284,27 @@ void read_dataset( } template -void read_dataset(hid_t dset, xt::xarray& arr, bool indep = false) +void read_dataset(hid_t dset, tensor::Tensor& arr, bool indep = false) { // Get shape of dataset vector shape = object_shape(dset); - // Allocate space in the array to read data into - std::size_t size = 1; - for (const auto x : shape) - size *= x; - arr.resize(shape); + // Resize tensor and read data directly + openmc::vector tshape(shape.begin(), shape.end()); + arr.resize(tshape); - // Read data from attribute + // Read data from dataset read_dataset_lowlevel( dset, nullptr, H5TypeMap::type_id, H5S_ALL, indep, arr.data()); } template<> void read_dataset( - hid_t dset, xt::xarray>& arr, bool indep); + hid_t dset, tensor::Tensor>& arr, bool indep); template void read_dataset( - hid_t obj_id, const char* name, xt::xarray& arr, bool indep = false) + hid_t obj_id, const char* name, tensor::Tensor& arr, bool indep = false) { // Open dataset and read array hid_t dset = open_dataset(obj_id, name); @@ -319,33 +312,6 @@ void read_dataset( close_dataset(dset); } -template -void read_dataset( - hid_t obj_id, const char* name, xt::xtensor& arr, bool indep = false) -{ - // Open dataset and read array - hid_t dset = open_dataset(obj_id, name); - - // Get shape of dataset - vector hsize_t_shape = object_shape(dset); - close_dataset(dset); - - // cast from hsize_t to size_t - vector shape(hsize_t_shape.size()); - for (int i = 0; i < shape.size(); i++) { - shape[i] = static_cast(hsize_t_shape[i]); - } - - // Allocate new xarray to read data into - xt::xarray xarr(shape); - - // Read data from the dataset - read_dataset(obj_id, name, xarr); - - // Copy into xtensor - arr = xarr; -} - // overload for Position inline void read_dataset( hid_t obj_id, const char* name, Position& r, bool indep = false) @@ -357,31 +323,22 @@ inline void read_dataset( r.z = x[2]; } -template +template inline void read_dataset_as_shape( - hid_t obj_id, const char* name, xt::xtensor& arr, bool indep = false) + hid_t obj_id, const char* name, tensor::Tensor& arr, bool indep = false) { hid_t dset = open_dataset(obj_id, name); - // Allocate new array to read data into - std::size_t size = 1; - for (const auto x : arr.shape()) - size *= x; - vector buffer(size); - - // Read data from attribute + // Read data directly into pre-shaped tensor read_dataset_lowlevel( - dset, nullptr, H5TypeMap::type_id, H5S_ALL, indep, buffer.data()); - - // Adapt into xarray - arr = xt::adapt(buffer, arr.shape()); + dset, nullptr, H5TypeMap::type_id, H5S_ALL, indep, arr.data()); close_dataset(dset); } -template +template inline void read_nd_vector(hid_t obj_id, const char* name, - xt::xtensor& result, bool must_have = false) + tensor::Tensor& result, bool must_have = false) { if (object_exists(obj_id, name)) { read_dataset_as_shape(obj_id, name, result, true); @@ -495,9 +452,9 @@ inline void write_dataset( false, buffer.data()); } -// Template for xarray, xtensor, etc. +// Template for Tensor and Fixed2D template>::value>> + typename = std::enable_if_t>::value>> inline void write_dataset( hid_t obj_id, const char* name, const Container& arr) { diff --git a/include/openmc/material.h b/include/openmc/material.h index 2baf54deec8..7fced727293 100644 --- a/include/openmc/material.h +++ b/include/openmc/material.h @@ -189,7 +189,7 @@ class Material { vector nuclide_; //!< Indices in nuclides vector vector element_; //!< Indices in elements vector NCrystalMat ncrystal_mat_; //!< NCrystal material object - xt::xtensor atom_density_; //!< Nuclide atom density in [atom/b-cm] + tensor::Tensor atom_density_; //!< Nuclide atom density in [atom/b-cm] double density_; //!< Total atom density in [atom/b-cm] double density_gpcc_; //!< Total atom density in [g/cm^3] double charge_density_; //!< Total charge density in [e/b-cm] diff --git a/include/openmc/mesh.h b/include/openmc/mesh.h index 8764d757992..e38b7b3c791 100644 --- a/include/openmc/mesh.h +++ b/include/openmc/mesh.h @@ -284,8 +284,8 @@ class Mesh { virtual Position upper_right() const = 0; // Data members - xt::xtensor lower_left_; //!< Lower-left coordinates of mesh - xt::xtensor upper_right_; //!< Upper-right coordinates of mesh + tensor::Tensor lower_left_; //!< Lower-left coordinates of mesh + tensor::Tensor upper_right_; //!< Upper-right coordinates of mesh int id_ {-1}; //!< Mesh ID std::string name_; //!< User-specified name int n_dimension_ {-1}; //!< Number of dimensions @@ -348,7 +348,7 @@ class StructuredMesh : public Mesh { //! \param[in] Pointer to bank sites //! \param[in] Number of bank sites //! \param[out] Whether any bank sites are outside the mesh - xt::xtensor count_sites( + tensor::Tensor count_sites( const SourceSite* bank, int64_t length, bool* outside) const; //! Get bin given mesh indices @@ -419,8 +419,8 @@ class StructuredMesh : public Mesh { //! Get a label for the mesh bin std::string bin_label(int bin) const override; - //! Get shape as xt::xtensor - xt::xtensor get_x_shape() const; + //! Get shape as tensor + tensor::Tensor get_x_shape() const; double volume(int bin) const override { @@ -515,7 +515,7 @@ class RegularMesh : public StructuredMesh { //! \param[in] bank Array of bank sites //! \param[out] Whether any bank sites are outside the mesh //! \return Array indicating number of sites in each mesh/energy bin - xt::xtensor count_sites( + tensor::Tensor count_sites( const SourceSite* bank, int64_t length, bool* outside) const; //! Return the volume for a given mesh index @@ -526,7 +526,7 @@ class RegularMesh : public StructuredMesh { // Data members double volume_frac_; //!< Volume fraction of each mesh element double element_volume_; //!< Volume of each mesh element - xt::xtensor width_; //!< Width of each mesh element + tensor::Tensor width_; //!< Width of each mesh element }; class RectilinearMesh : public StructuredMesh { diff --git a/include/openmc/mgxs.h b/include/openmc/mgxs.h index 4779505d481..34f373b33aa 100644 --- a/include/openmc/mgxs.h +++ b/include/openmc/mgxs.h @@ -22,7 +22,7 @@ namespace openmc { class Mgxs { private: - xt::xtensor kTs; // temperature in eV (k * T) + tensor::Tensor kTs; // temperature in eV (k * T) AngleDistributionType scatter_format; // flag for if this is legendre, histogram, or tabular int num_groups; // number of energy groups diff --git a/include/openmc/nuclide.h b/include/openmc/nuclide.h index 58d83393963..7a8b2acadd9 100644 --- a/include/openmc/nuclide.h +++ b/include/openmc/nuclide.h @@ -96,7 +96,7 @@ class Nuclide { // Temperature dependent cross section data vector kTs_; //!< temperatures in eV (k*T) vector grid_; //!< Energy grid at each temperature - vector> xs_; //!< Cross sections at each temperature + vector> xs_; //!< Cross sections at each temperature // Multipole data unique_ptr multipole_; diff --git a/include/openmc/photon.h b/include/openmc/photon.h index 3078696fb57..93c6dba53f5 100644 --- a/include/openmc/photon.h +++ b/include/openmc/photon.h @@ -62,14 +62,14 @@ class PhotonInteraction { int64_t index_; //!< Index in global elements vector // Microscopic cross sections - xt::xtensor energy_; - xt::xtensor coherent_; - xt::xtensor incoherent_; - xt::xtensor photoelectric_total_; - xt::xtensor pair_production_total_; - xt::xtensor pair_production_electron_; - xt::xtensor pair_production_nuclear_; - xt::xtensor heating_; + tensor::Tensor energy_; + tensor::Tensor coherent_; + tensor::Tensor incoherent_; + tensor::Tensor photoelectric_total_; + tensor::Tensor pair_production_total_; + tensor::Tensor pair_production_electron_; + tensor::Tensor pair_production_nuclear_; + tensor::Tensor heating_; // Form factors Tabulated1D incoherent_form_factor_; @@ -81,27 +81,27 @@ class PhotonInteraction { // stored separately to improve memory access pattern when calculating the // total cross section vector shells_; - xt::xtensor cross_sections_; + tensor::Tensor cross_sections_; // Compton profile data - xt::xtensor profile_pdf_; - xt::xtensor profile_cdf_; - xt::xtensor binding_energy_; - xt::xtensor electron_pdf_; + tensor::Tensor profile_pdf_; + tensor::Tensor profile_cdf_; + tensor::Tensor binding_energy_; + tensor::Tensor electron_pdf_; // Map subshells from Compton profile data obtained from Biggs et al, // "Hartree-Fock Compton profiles for the elements" to ENDF/B atomic // relaxation data - xt::xtensor subshell_map_; + tensor::Tensor subshell_map_; // Stopping power data double I_; // mean excitation energy - xt::xtensor n_electrons_; - xt::xtensor ionization_energy_; - xt::xtensor stopping_power_radiative_; + tensor::Tensor n_electrons_; + tensor::Tensor ionization_energy_; + tensor::Tensor stopping_power_radiative_; // Bremsstrahlung scaled DCS - xt::xtensor dcs_; + tensor::Tensor dcs_; // Whether atomic relaxation data is present bool has_atomic_relaxation_ {false}; @@ -137,7 +137,7 @@ void free_memory_photon(); namespace data { -extern xt::xtensor +extern tensor::Tensor compton_profile_pz; //! Compton profile momentum grid //! Photon interaction data for each element diff --git a/include/openmc/plot.h b/include/openmc/plot.h index 861f400e3c9..216ac369f05 100644 --- a/include/openmc/plot.h +++ b/include/openmc/plot.h @@ -129,7 +129,7 @@ class PlottableInterface { vector colors_; // Plot colors }; -typedef xt::xtensor ImageData; +typedef tensor::Tensor ImageData; struct IdData { // Constructor @@ -140,7 +140,7 @@ struct IdData { void set_overlap(size_t y, size_t x); // Members - xt::xtensor data_; //!< 2D array of cell & material ids + tensor::Tensor data_; //!< 2D array of cell & material ids }; struct PropertyData { @@ -152,7 +152,7 @@ struct PropertyData { void set_overlap(size_t y, size_t x); // Members - xt::xtensor data_; //!< 2D array of temperature & density data + tensor::Tensor data_; //!< 2D array of temperature & density data }; //=============================================================================== diff --git a/include/openmc/random_ray/flat_source_domain.h b/include/openmc/random_ray/flat_source_domain.h index 0d4086966dd..3829185e897 100644 --- a/include/openmc/random_ray/flat_source_domain.h +++ b/include/openmc/random_ray/flat_source_domain.h @@ -180,7 +180,7 @@ class FlatSourceDomain { // xtensor is indexed by bin index and score index in a similar manner to the // results tensor in the Tally class, though without the third dimension, as // SUM and SUM_SQ do not need to be tracked. - vector> tally_volumes_; + vector> tally_volumes_; }; // class FlatSourceDomain diff --git a/include/openmc/scattdata.h b/include/openmc/scattdata.h index 7794da85466..bea881402ac 100644 --- a/include/openmc/scattdata.h +++ b/include/openmc/scattdata.h @@ -26,23 +26,23 @@ class ScattData { protected: //! \brief Initializes the attributes of the base class. - void base_init(int order, const xt::xtensor& in_gmin, - const xt::xtensor& in_gmax, const double_2dvec& in_energy, + void base_init(int order, const tensor::Tensor& in_gmin, + const tensor::Tensor& in_gmax, const double_2dvec& in_energy, const double_2dvec& in_mult); //! \brief Combines microscopic ScattDatas into a macroscopic one. void base_combine(size_t max_order, size_t order_dim, const vector& those_scatts, const vector& scalars, - xt::xtensor& in_gmin, xt::xtensor& in_gmax, + tensor::Tensor& in_gmin, tensor::Tensor& in_gmax, double_2dvec& sparse_mult, double_3dvec& sparse_scatter); public: double_2dvec energy; // Normalized p0 matrix for sampling Eout double_2dvec mult; // nu-scatter multiplication (nu-scatt/scatt) double_3dvec dist; // Angular distribution - xt::xtensor gmin; // minimum outgoing group - xt::xtensor gmax; // maximum outgoing group - xt::xtensor scattxs; // Isotropic Sigma_{s,g_{in}} + tensor::Tensor gmin; // minimum outgoing group + tensor::Tensor gmax; // maximum outgoing group + tensor::Tensor scattxs; // Isotropic Sigma_{s,g_{in}} //! \brief Calculates the value of normalized f(mu). //! @@ -72,8 +72,8 @@ class ScattData { //! @param in_gmax List of maximum outgoing groups for every incoming group //! @param in_mult Input sparse multiplicity matrix //! @param coeffs Input sparse scattering matrix - virtual void init(const xt::xtensor& in_gmin, - const xt::xtensor& in_gmax, const double_2dvec& in_mult, + virtual void init(const tensor::Tensor& in_gmin, + const tensor::Tensor& in_gmax, const double_2dvec& in_mult, const double_3dvec& coeffs) = 0; //! \brief Combines the microscopic data. @@ -96,7 +96,7 @@ class ScattData { //! @param max_order If Legendre this is the maximum value of "n" in "Pn" //! requested; ignored otherwise. //! @return The dense scattering matrix. - virtual xt::xtensor get_matrix(size_t max_order) = 0; + virtual tensor::Tensor get_matrix(size_t max_order) = 0; //! \brief Samples the outgoing energy from the ScattData info. //! @@ -135,8 +135,8 @@ class ScattDataLegendre : public ScattData { ScattDataLegendre& leg, ScattDataTabular& tab); public: - void init(const xt::xtensor& in_gmin, - const xt::xtensor& in_gmax, const double_2dvec& in_mult, + void init(const tensor::Tensor& in_gmin, + const tensor::Tensor& in_gmax, const double_2dvec& in_mult, const double_3dvec& coeffs) override; void combine(const vector& those_scatts, @@ -153,7 +153,7 @@ class ScattDataLegendre : public ScattData { size_t get_order() override { return dist[0][0].size() - 1; }; - xt::xtensor get_matrix(size_t max_order) override; + tensor::Tensor get_matrix(size_t max_order) override; }; //============================================================================== @@ -164,13 +164,13 @@ class ScattDataLegendre : public ScattData { class ScattDataHistogram : public ScattData { protected: - xt::xtensor mu; // Angle distribution mu bin boundaries + tensor::Tensor mu; // Angle distribution mu bin boundaries double dmu; // Quick storage of the mu spacing double_3dvec fmu; // The angular distribution histogram public: - void init(const xt::xtensor& in_gmin, - const xt::xtensor& in_gmax, const double_2dvec& in_mult, + void init(const tensor::Tensor& in_gmin, + const tensor::Tensor& in_gmax, const double_2dvec& in_mult, const double_3dvec& coeffs) override; void combine(const vector& those_scatts, @@ -183,7 +183,7 @@ class ScattDataHistogram : public ScattData { size_t get_order() override { return dist[0][0].size(); }; - xt::xtensor get_matrix(size_t max_order) override; + tensor::Tensor get_matrix(size_t max_order) override; }; //============================================================================== @@ -194,7 +194,7 @@ class ScattDataHistogram : public ScattData { class ScattDataTabular : public ScattData { protected: - xt::xtensor mu; // Angle distribution mu grid points + tensor::Tensor mu; // Angle distribution mu grid points double dmu; // Quick storage of the mu spacing double_3dvec fmu; // The angular distribution function @@ -204,8 +204,8 @@ class ScattDataTabular : public ScattData { ScattDataLegendre& leg, ScattDataTabular& tab); public: - void init(const xt::xtensor& in_gmin, - const xt::xtensor& in_gmax, const double_2dvec& in_mult, + void init(const tensor::Tensor& in_gmin, + const tensor::Tensor& in_gmax, const double_2dvec& in_mult, const double_3dvec& coeffs) override; void combine(const vector& those_scatts, @@ -218,7 +218,7 @@ class ScattDataTabular : public ScattData { size_t get_order() override { return dist[0][0].size(); }; - xt::xtensor get_matrix(size_t max_order) override; + tensor::Tensor get_matrix(size_t max_order) override; }; //============================================================================== diff --git a/include/openmc/secondary_correlated.h b/include/openmc/secondary_correlated.h index c50c32d206f..b4b7f8480f5 100644 --- a/include/openmc/secondary_correlated.h +++ b/include/openmc/secondary_correlated.h @@ -25,9 +25,9 @@ class CorrelatedAngleEnergy : public AngleEnergy { 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 + tensor::Tensor e_out; //!< Outgoing energies [eV] + tensor::Tensor p; //!< Probability density + tensor::Tensor c; //!< Cumulative distribution vector> angle; //!< Angle distribution }; diff --git a/include/openmc/secondary_kalbach.h b/include/openmc/secondary_kalbach.h index 5a0fbd662a7..c9c5849bc77 100644 --- a/include/openmc/secondary_kalbach.h +++ b/include/openmc/secondary_kalbach.h @@ -37,11 +37,11 @@ class KalbachMann : public AngleEnergy { 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 + tensor::Tensor e_out; //!< Outgoing energies [eV] + tensor::Tensor p; //!< Probability density + tensor::Tensor c; //!< Cumulative distribution + tensor::Tensor r; //!< Pre-compound fraction + tensor::Tensor a; //!< Parameterized function }; int n_region_; //!< Number of interpolation regions diff --git a/include/openmc/secondary_thermal.h b/include/openmc/secondary_thermal.h index 53ac721135d..4f33c0e763c 100644 --- a/include/openmc/secondary_thermal.h +++ b/include/openmc/secondary_thermal.h @@ -83,7 +83,7 @@ class IncoherentElasticAEDiscrete : public AngleEnergy { private: const vector& energy_; //!< Energies at which cosines are tabulated - xt::xtensor mu_out_; //!< Cosines for each incident energy + tensor::Tensor mu_out_; //!< Cosines for each incident energy }; //============================================================================== @@ -109,9 +109,9 @@ class IncoherentInelasticAEDiscrete : public AngleEnergy { private: const vector& energy_; //!< Incident energies - xt::xtensor + tensor::Tensor energy_out_; //!< Outgoing energies for each incident energy - xt::xtensor + tensor::Tensor mu_out_; //!< Outgoing cosines for each incident/outgoing energy bool skewed_; //!< Whether outgoing energy distribution is skewed }; @@ -139,10 +139,10 @@ class IncoherentInelasticAE : public AngleEnergy { //! Secondary energy/angle distribution struct DistEnergySab { std::size_t n_e_out; //!< Number of outgoing energies - xt::xtensor e_out; //!< Outgoing energies - xt::xtensor e_out_pdf; //!< Probability density function - xt::xtensor e_out_cdf; //!< Cumulative distribution function - xt::xtensor mu; //!< Equiprobable angles at each outgoing energy + tensor::Tensor e_out; //!< Outgoing energies + tensor::Tensor e_out_pdf; //!< Probability density function + tensor::Tensor e_out_cdf; //!< Cumulative distribution function + tensor::Tensor mu; //!< Equiprobable angles at each outgoing energy }; vector energy_; //!< Incident energies diff --git a/include/openmc/tallies/tally.h b/include/openmc/tallies/tally.h index 719043135b4..3cc65b85409 100644 --- a/include/openmc/tallies/tally.h +++ b/include/openmc/tallies/tally.h @@ -54,7 +54,7 @@ class Tally { void set_nuclides(const vector& nuclides); - const xt::xtensor& results() const { return results_; } + const tensor::Tensor& results() const { return results_; } //! returns vector of indices corresponding to the tally this is called on const vector& filters() const { return filters_; } @@ -124,7 +124,7 @@ class Tally { int score_index(const std::string& score) const; //! Tally results reshaped according to filter sizes - xt::xarray get_reshaped_data() const; + tensor::Tensor get_reshaped_data() const; //! A string representing the i-th score on this tally std::string score_name(int score_idx) const; @@ -159,7 +159,7 @@ class Tally { //! combination of filters (e.g. specific cell, specific energy group, etc.) //! and the second dimension of the array is for scores (e.g. flux, total //! reaction rate, fission reaction rate, etc.) - xt::xtensor results_; + tensor::Tensor results_; //! True if this tally should be written to statepoint files bool writable_ {true}; @@ -219,7 +219,7 @@ extern vector time_grid; namespace simulation { //! Global tallies (such as k-effective estimators) -extern xt::xtensor_fixed> +extern tensor::Fixed2D global_tallies; //! Number of realizations for global tallies diff --git a/include/openmc/tensor.h b/include/openmc/tensor.h index ecd0a5fb52a..144ac903586 100644 --- a/include/openmc/tensor.h +++ b/include/openmc/tensor.h @@ -1,9 +1,8 @@ //! \file tensor.h -//! \brief Multi-dimensional tensor types replacing the xtensor dependency. +//! \brief Multi-dimensional tensor types for OpenMC. //! -//! Provides xtensor (fixed-dimension), xarray (dynamic-dimension), +//! Provides Tensor (dynamic-rank), Fixed2D (stack-allocated), //! and lightweight view types. Built on openmc::vector for GPU portability. -//! Only implements the subset of xtensor API that OpenMC actually uses. #ifndef OPENMC_TENSOR_H #define OPENMC_TENSOR_H @@ -20,88 +19,26 @@ #include #include -namespace xt { +namespace openmc { +namespace tensor { //============================================================================== // Forward declarations //============================================================================== -template -class xtensor; - template -class xarray; - -template -class xtensor_fixed; - -template -struct xshape {}; - -//============================================================================== -// Slice/view helper types -// -// These are sentinel types used as arguments to view() to select rows, -// columns, or subranges of a tensor. xt::all() selects an entire axis, -// xt::range(start, stop) selects a half-open subrange. -//============================================================================== - -//! Sentinel returned by xt::all(); tells view() to keep the full axis. -struct xall_type {}; - -inline xall_type all() -{ - return {}; -} - -//! Half-open index range [start, stop) returned by xt::range(). -struct xrange_type { - std::ptrdiff_t start; - std::ptrdiff_t stop; -}; - -inline xrange_type range(std::ptrdiff_t start, std::ptrdiff_t stop) -{ - return {start, stop}; -} +class Tensor; -namespace placeholders { -//! Placeholder for "end of axis" in xt::range(), e.g. range(1, xt::placeholders::_). -constexpr std::ptrdiff_t _ = std::numeric_limits::max(); -} - -//! Ownership tag for adapt(): data is copied, caller retains ownership. -struct no_ownership {}; -//! Ownership tag for adapt(): data is copied then delete[]'d. -struct acquire_ownership {}; - -//============================================================================== -// xshape traits (internal) -// -// Extracts compile-time dimension sizes from an xshape parameter -// for use by xtensor_fixed. -//============================================================================== - -namespace detail { -template -struct xshape_traits; - -template -struct xshape_traits> { - static constexpr std::size_t ndim = 2; - static constexpr std::size_t total = D0 * D1; - static constexpr std::size_t dim0 = D0; - static constexpr std::size_t dim1 = D1; -}; -} // namespace detail +template +class Fixed2D; //============================================================================== // Storage type mapping // // std::vector is a bit-packed specialization that returns proxy objects // instead of real references, which breaks generic code. storage_type_map -// redirects bool to unsigned char so that xtensor stores one byte -// per element with normal reference semantics. +// redirects bool to unsigned char so that Tensor stores one byte per +// element with normal reference semantics. //============================================================================== template @@ -116,32 +53,21 @@ template using storage_type = typename storage_type_map::type; //============================================================================== -// xtensor_view_1d: a read/write view of one row or column of a tensor. +// View1D: a read/write view of one row, column, or slice of a tensor. // -// Returned by view(tensor, index, all()) or view(tensor, all(), index). // Holds a pointer, element count, and stride into the parent tensor's -// storage — no allocation or copy. Supports element access, assignment -// (from tensors, views, or scalars), compound arithmetic, and iteration. +// storage — no allocation or copy. //============================================================================== template -class xtensor_view_1d { +class View1D { public: - //-------------------------------------------------------------------------- - // Types - using value_type = std::remove_const_t; - //-------------------------------------------------------------------------- - // Constructors - - xtensor_view_1d(T* data, std::size_t size, std::size_t stride = 1) + View1D(T* data, std::size_t size, std::size_t stride = 1) : data_(data), size_(size), stride_(stride) {} - //-------------------------------------------------------------------------- - // Accessors - T& operator()(std::size_t i) { return data_[i * stride_]; } const T& operator()(std::size_t i) const { return data_[i * stride_]; } T& operator[](std::size_t i) { return data_[i * stride_]; } @@ -152,57 +78,54 @@ class xtensor_view_1d { const T* data() const { return data_; } std::size_t stride() const { return stride_; } - //-------------------------------------------------------------------------- - // Assignment operators - - template - xtensor_view_1d& operator=(const xarray& other) + View1D slice(std::size_t start, std::size_t end) { - for (std::size_t i = 0; i < size_; ++i) - data_[i * stride_] = static_cast(other.data()[i]); - return *this; + return {data_ + start * stride_, end - start, stride_}; } - - template - xtensor_view_1d& operator=(const xtensor& other) + View1D slice(std::size_t start, std::size_t end) const { - for (std::size_t i = 0; i < size_; ++i) - data_[i * stride_] = other.data()[i]; - return *this; + return {data_ + start * stride_, end - start, stride_}; + } + View1D slice(std::size_t start) + { + return {data_ + start * stride_, size_ - start, stride_}; } + // Assignment from scalar template auto operator=(U val) -> - std::enable_if_t::value, xtensor_view_1d&> + std::enable_if_t::value, View1D&> { for (std::size_t i = 0; i < size_; ++i) data_[i * stride_] = val; return *this; } - //-------------------------------------------------------------------------- - // Compound assignment operators - - template - xtensor_view_1d& operator+=(const xtensor& o) + // Assignment from initializer_list + View1D& operator=(std::initializer_list vals) { - for (std::size_t i = 0; i < size_; ++i) - data_[i * stride_] += o.data()[i]; + auto it = vals.begin(); + for (std::size_t i = 0; i < size_ && it != vals.end(); ++i, ++it) + data_[i * stride_] = *it; return *this; } - xtensor_view_1d& operator*=(value_type val) + // Assignment from Tensor (deferred, defined after Tensor) + template + View1D& operator=(const Tensor& other); + + // Compound assignment from Tensor (deferred) + template + View1D& operator+=(const Tensor& o); + + View1D& operator*=(value_type val) { for (std::size_t i = 0; i < size_; ++i) data_[i * stride_] *= val; return *this; } - //-------------------------------------------------------------------------- // Iterators - - //! Iterator that steps by stride_ through the parent buffer, enabling - //! range-for loops and STL algorithms over non-contiguous view elements. class const_iterator { const T* ptr_; std::size_t stride_; @@ -284,7 +207,8 @@ class xtensor_view_1d { ptr_ -= n * stride_; return *this; } - friend const_iterator operator+(difference_type n, const const_iterator& it) + friend const_iterator operator+( + difference_type n, const const_iterator& it) { return it + n; } @@ -331,9 +255,18 @@ class xtensor_view_1d { { return (ptr_ - other.ptr_) / static_cast(stride_); } - bool operator==(const iterator& other) const { return ptr_ == other.ptr_; } - bool operator!=(const iterator& other) const { return ptr_ != other.ptr_; } - bool operator<(const iterator& other) const { return ptr_ < other.ptr_; } + bool operator==(const iterator& other) const + { + return ptr_ == other.ptr_; + } + bool operator!=(const iterator& other) const + { + return ptr_ != other.ptr_; + } + bool operator<(const iterator& other) const + { + return ptr_ < other.ptr_; + } T& operator[](difference_type n) { return *(ptr_ + n * stride_); } iterator& operator+=(difference_type n) { @@ -357,249 +290,162 @@ class xtensor_view_1d { } private: - //-------------------------------------------------------------------------- - // Data members - T* data_; std::size_t size_; std::size_t stride_; }; //============================================================================== -// xtensor_view_2d: a read/write view of a 2D slice of a higher-dimensional -// tensor. -// -// Returned by view(3D_tensor, index, all(), all()). Holds a pointer and -// strides into the parent tensor's storage — no allocation or copy. +// ViewFlat: a flat view of all elements of a tensor. //============================================================================== template -class xtensor_view_2d { +class ViewFlat { public: - //-------------------------------------------------------------------------- - // Types - - using value_type = std::remove_const_t; - - //-------------------------------------------------------------------------- - // Constructors - - xtensor_view_2d(T* data, std::size_t s0, std::size_t s1, std::size_t st0, - std::size_t st1) - : data_(data), shape0_(s0), shape1_(s1), stride0_(st0), stride1_(st1) - {} - - //-------------------------------------------------------------------------- - // Accessors - - T& operator()(std::size_t i, std::size_t j) - { - return data_[i * stride0_ + j * stride1_]; - } - const T& operator()(std::size_t i, std::size_t j) const - { - return data_[i * stride0_ + j * stride1_]; - } - - std::size_t size() const { return shape0_ * shape1_; } - std::array shape() const { return {shape0_, shape1_}; } - - //-------------------------------------------------------------------------- - // Assignment operators + ViewFlat(T* data, std::size_t size) : data_(data), size_(size) {} - template - auto operator=(const xtensor& other) -> - std::enable_if_t - { - for (std::size_t i = 0; i < shape0_; ++i) - for (std::size_t j = 0; j < shape1_; ++j) - data_[i * stride0_ + j * stride1_] = other(i, j); - return *this; - } + T& operator()(std::size_t i) { return data_[i]; } + const T& operator()(std::size_t i) const { return data_[i]; } template auto operator=(U val) -> - std::enable_if_t::value, xtensor_view_2d&> + std::enable_if_t::value, ViewFlat&> { - for (std::size_t i = 0; i < shape0_; ++i) - for (std::size_t j = 0; j < shape1_; ++j) - data_[i * stride0_ + j * stride1_] = val; + std::fill(data_, data_ + size_, static_cast(val)); return *this; } -private: - //-------------------------------------------------------------------------- - // Data members - - T* data_; - std::size_t shape0_, shape1_; - std::size_t stride0_, stride1_; -}; - -//============================================================================== -// xtensor_view_flat: a view of all elements of a tensor as a flat sequence. -// -// Returned by view(tensor, all()). Used for bulk operations that treat the -// entire tensor as a 1D array, e.g. assigning from a raw buffer or filling -// with a scalar. No allocation or copy. -//============================================================================== - -template -class xtensor_view_flat { -public: - //-------------------------------------------------------------------------- - // Constructors - - xtensor_view_flat(T* data, std::size_t size) : data_(data), size_(size) {} - - //-------------------------------------------------------------------------- - // Assignment operators + T* data() { return data_; } + const T* data() const { return data_; } + std::size_t size() const { return size_; } - template - auto operator=(U val) -> - std::enable_if_t::value, xtensor_view_flat&> - { - std::fill(data_, data_ + size_, static_cast(val)); - return *this; - } + T* begin() { return data_; } + T* end() { return data_ + size_; } + const T* begin() const { return data_; } + const T* end() const { return data_ + size_; } private: - //-------------------------------------------------------------------------- - // Data members - T* data_; std::size_t size_; }; //============================================================================== -// xtensor: fixed-rank N-dimensional tensor. +// Tensor: dynamic-rank N-dimensional tensor. // -// The primary data type in this header. Stores elements in a contiguous -// row-major openmc::vector> with a compile-time rank N. -// Provides multi-dimensional operator() indexing, element-wise arithmetic, -// and view() accessors for extracting rows, columns, and slices. +// Stores elements in a contiguous row-major openmc::vector> +// with a dynamic shape. //============================================================================== -template -class xtensor { +template +class Tensor { public: - //-------------------------------------------------------------------------- - // Types - using value_type = T; using stored_type = storage_type; using iterator = typename openmc::vector::iterator; using const_iterator = typename openmc::vector::const_iterator; - using shape_type = std::array; //-------------------------------------------------------------------------- // Constructors - xtensor() { shape_.fill(0); } + Tensor() = default; - xtensor(std::initializer_list shape) - { - std::copy(shape.begin(), shape.end(), shape_.begin()); - data_.resize(compute_size()); - } + //! Construct with shape (uninitialized for arithmetic types via vector resize) + explicit Tensor(openmc::vector shape) + : shape_(std::move(shape)), data_(compute_size()) + {} - xtensor(std::initializer_list shape, T val) - { - std::copy(shape.begin(), shape.end(), shape_.begin()); - data_.assign(compute_size(), val); - } + //! Construct with shape and fill value + Tensor(openmc::vector shape, T fill) + : shape_(std::move(shape)), data_(compute_size(), fill) + {} - explicit xtensor(const std::array& shape) : shape_(shape) - { - data_.resize(compute_size()); - } + //! Construct from initializer_list shape + explicit Tensor(std::initializer_list shape) + : shape_(shape), data_(compute_size()) + {} - xtensor(const std::array& shape, T val) : shape_(shape) - { - data_.assign(compute_size(), val); - } + //! Construct from initializer_list shape with fill + Tensor(std::initializer_list shape, T fill) + : shape_(shape), data_(compute_size(), fill) + {} + + //! 1D copy from openmc::vector (disabled when T=size_t to avoid ambiguity) + template::value>> + explicit Tensor(const openmc::vector& vec) + : shape_({vec.size()}), data_(vec.begin(), vec.end()) + {} + //! 1D copy from std::vector (disabled when T=size_t) + template::value>> + explicit Tensor(const std::vector& vec) + : shape_({vec.size()}), data_(vec.begin(), vec.end()) + {} - //! 1D initializer_list constructor (N==1 only, T != size_t to avoid ambiguity) - template::value>> - xtensor(std::initializer_list vals) - { - shape_[0] = vals.size(); - data_.assign(vals.begin(), vals.end()); - } + //! 1D copy from raw pointer + count + Tensor(const T* ptr, std::size_t count) + : shape_({count}), data_(ptr, ptr + count) + {} + + //! Copy from vector with explicit shape + template + Tensor(const std::vector& vec, openmc::vector shape) + : shape_(std::move(shape)), data_(vec.begin(), vec.end()) + {} - //! Converting constructor from a 1D view (copies data) - template> - xtensor(const xtensor_view_1d& v) + //! Copy from View1D (makes a 1D tensor) + template + Tensor(const View1D& v) + : shape_({v.size()}) { - shape_[0] = v.size(); data_.resize(v.size()); for (std::size_t i = 0; i < v.size(); ++i) data_[i] = v(i); } - //! Converting constructor from a 2D view (copies data) - template> - xtensor(const xtensor_view_2d& v) - { - auto s = v.shape(); - shape_[0] = s[0]; - shape_[1] = s[1]; - data_.resize(s[0] * s[1]); - for (std::size_t i = 0; i < s[0]; ++i) - for (std::size_t j = 0; j < s[1]; ++j) - data_[i * s[1] + j] = v(i, j); - } - - //! Converting constructor from xarray (copies data) - xtensor(const xarray& other); - - //-------------------------------------------------------------------------- - // Factory methods - - static xtensor from_shape(std::initializer_list shape) + //! Cross-type copy constructor + template::value>> + Tensor(const Tensor& other) + : shape_(other.shape()) { - xtensor result; - std::copy(shape.begin(), shape.end(), result.shape_.begin()); - result.data_.resize(result.compute_size()); - return result; + data_.resize(other.size()); + for (std::size_t i = 0; i < other.size(); ++i) + data_[i] = static_cast(other.data()[i]); } //-------------------------------------------------------------------------- - // Assignment operators + // Assignment - xtensor& operator=(const xarray& other); - - //! Cross-type assignment from xtensor + //! Cross-type assignment template::value>> - xtensor& operator=(const xtensor& other) + Tensor& operator=(const Tensor& other) { shape_ = other.shape(); data_.resize(other.size()); for (std::size_t i = 0; i < other.size(); ++i) - data_[i] = static_cast(other.data()[i]); + data_[i] = static_cast(other.data()[i]); return *this; } - template> - xtensor& operator=(const xtensor_view_1d& v) + //! Assignment from View1D + Tensor& operator=(const View1D& v) { - shape_[0] = v.size(); + shape_ = {v.size()}; data_.resize(v.size()); for (std::size_t i = 0; i < v.size(); ++i) data_[i] = v(i); return *this; } - template::value>> - xtensor& operator=(std::initializer_list vals) + //! Assignment from initializer_list of values (1D) + template::value>> + Tensor& operator=(std::initializer_list vals) { - shape_[0] = vals.size(); + shape_ = {vals.size()}; data_.assign(vals.begin(), vals.end()); return *this; } @@ -610,15 +456,16 @@ class xtensor { stored_type* data() { return data_.data(); } const stored_type* data() const { return data_.data(); } std::size_t size() const { return data_.size(); } - const shape_type& shape() const { return shape_; } - std::size_t shape(std::size_t dim) const { return shape_[dim]; } + const openmc::vector& shape() const { return shape_; } + std::size_t shape(std::size_t dim) const { + return dim < shape_.size() ? shape_[dim] : 0; + } + std::size_t ndim() const { return shape_.size(); } bool empty() const { return data_.empty(); } - static constexpr std::size_t dimension() { return N; } //-------------------------------------------------------------------------- - // Indexing + // Indexing (row-major) - //! Multi-dimensional indexing (row-major) template stored_type& operator()(Indices... indices) { @@ -631,7 +478,6 @@ class xtensor { return data_[offset(static_cast(indices)...)]; } - //! Linear (flat) indexing stored_type& operator[](std::size_t i) { return data_[i]; } const stored_type& operator[](std::size_t i) const { return data_[i]; } @@ -646,32 +492,182 @@ class xtensor { const_iterator cend() const { return data_.cend(); } //-------------------------------------------------------------------------- - // Methods + // Mutation + + void resize(const openmc::vector& shape) + { + shape_ = shape; + data_.resize(compute_size()); + } void resize(std::initializer_list shape) { - std::copy(shape.begin(), shape.end(), shape_.begin()); + shape_.assign(shape.begin(), shape.end()); + data_.resize(compute_size()); + } + + void resize(const openmc::vector& shape) + { + shape_.clear(); + for (auto d : shape) + shape_.push_back(static_cast(d)); data_.resize(compute_size()); } + template + void reshape(const ShapeType& new_shape) + { + shape_.clear(); + for (auto d : new_shape) + shape_.push_back(static_cast(d)); + } + void fill(T val) { std::fill(data_.begin(), data_.end(), val); } + //-------------------------------------------------------------------------- + // View accessors + + //! Row i of a 2D+ tensor (contiguous 1D view) + View1D row(std::size_t i) + { + auto cols = shape_[shape_.size() - 1]; + return {data_.data() + i * cols, cols, 1}; + } + View1D row(std::size_t i) const + { + auto cols = shape_[shape_.size() - 1]; + return {data_.data() + i * cols, cols, 1}; + } + + //! Column j of a 2D tensor (strided 1D view) + View1D col(std::size_t j) + { + return {data_.data() + j, shape_[0], shape_[1]}; + } + View1D col(std::size_t j) const + { + return {data_.data() + j, shape_[0], shape_[1]}; + } + + //! Subrange of a 1D tensor + View1D slice(std::size_t start, std::size_t end) + { + return {data_.data() + start, end - start, 1}; + } + View1D slice(std::size_t start, std::size_t end) const + { + return {data_.data() + start, end - start, 1}; + } + + //! Subrange to end of a 1D tensor + View1D slice(std::size_t start) + { + return {data_.data() + start, data_.size() - start, 1}; + } + View1D slice(std::size_t start) const + { + return {data_.data() + start, data_.size() - start, 1}; + } + + //! Flat 1D view of all elements + ViewFlat flat() + { + return {data_.data(), data_.size()}; + } + ViewFlat flat() const + { + return {data_.data(), data_.size()}; + } + + //-------------------------------------------------------------------------- + // Reductions + + T sum() const + { + T s = T(0); + for (std::size_t i = 0; i < data_.size(); ++i) + s += data_[i]; + return s; + } + + //! Sum along an axis, reducing rank by 1 (defined out-of-line below) + Tensor sum(std::size_t axis) const; + + T prod() const + { + T p = T(1); + for (std::size_t i = 0; i < data_.size(); ++i) + p *= data_[i]; + return p; + } + + bool any() const + { + for (std::size_t i = 0; i < data_.size(); ++i) + if (data_[i]) + return true; + return false; + } + + bool all() const + { + for (std::size_t i = 0; i < data_.size(); ++i) + if (!data_[i]) + return false; + return true; + } + + std::size_t argmin() const + { + return static_cast( + std::distance(data_.data(), + std::min_element(data_.data(), data_.data() + data_.size()))); + } + + //-------------------------------------------------------------------------- + // Flip + + Tensor flip(std::size_t axis) const + { + Tensor r(shape_); + if (shape_.size() == 2) { + auto s0 = shape_[0]; + auto s1 = shape_[1]; + if (axis == 0) { + for (std::size_t i = 0; i < s0; ++i) + for (std::size_t j = 0; j < s1; ++j) + r.data_[i * s1 + j] = data_[(s0 - 1 - i) * s1 + j]; + } else { + for (std::size_t i = 0; i < s0; ++i) + for (std::size_t j = 0; j < s1; ++j) + r.data_[i * s1 + j] = data_[i * s1 + (s1 - 1 - j)]; + } + } + return r; + } + //-------------------------------------------------------------------------- // Compound assignment operators (scalar) - xtensor& operator-=(T val) + Tensor& operator+=(T val) + { + for (auto& x : data_) + x += val; + return *this; + } + Tensor& operator-=(T val) { for (auto& x : data_) x -= val; return *this; } - xtensor& operator*=(T val) + Tensor& operator*=(T val) { for (auto& x : data_) x *= val; return *this; } - xtensor& operator/=(T val) + Tensor& operator/=(T val) { for (auto& x : data_) x /= val; @@ -679,34 +675,35 @@ class xtensor { } //-------------------------------------------------------------------------- - // Compound assignment operators (tensor and view) + // Compound assignment operators (tensor) - xtensor& operator+=(const xtensor& o) + Tensor& operator+=(const Tensor& o) { for (std::size_t i = 0; i < data_.size(); ++i) data_[i] += o.data_[i]; return *this; } + //-------------------------------------------------------------------------- - // Element-wise binary operators (tensor op tensor, same shape) + // Element-wise binary operators (tensor op tensor) - xtensor operator+(const xtensor& o) const + Tensor operator+(const Tensor& o) const { - xtensor r(shape_); + Tensor r(shape_); for (std::size_t i = 0; i < data_.size(); ++i) r.data_[i] = data_[i] + o.data_[i]; return r; } - xtensor operator-(const xtensor& o) const + Tensor operator-(const Tensor& o) const { - xtensor r(shape_); + Tensor r(shape_); for (std::size_t i = 0; i < data_.size(); ++i) r.data_[i] = data_[i] - o.data_[i]; return r; } - xtensor operator/(const xtensor& o) const + Tensor operator/(const Tensor& o) const { - xtensor r(shape_); + Tensor r(shape_); for (std::size_t i = 0; i < data_.size(); ++i) r.data_[i] = data_[i] / o.data_[i]; return r; @@ -715,75 +712,73 @@ class xtensor { //-------------------------------------------------------------------------- // Element-wise binary operators (tensor op scalar) - xtensor operator+(T val) const + Tensor operator+(T val) const { - xtensor r(shape_); + Tensor r(shape_); for (std::size_t i = 0; i < data_.size(); ++i) r.data_[i] = data_[i] + val; return r; } - xtensor operator-(T val) const + Tensor operator-(T val) const { - xtensor r(shape_); + Tensor r(shape_); for (std::size_t i = 0; i < data_.size(); ++i) r.data_[i] = data_[i] - val; return r; } - xtensor operator*(T val) const + Tensor operator*(T val) const { - xtensor r(shape_); + Tensor r(shape_); for (std::size_t i = 0; i < data_.size(); ++i) r.data_[i] = data_[i] * val; return r; } + //-------------------------------------------------------------------------- - // Element-wise comparison operators (return bool tensor) + // Element-wise comparison operators (return Tensor) - xtensor operator<=(T val) const + Tensor operator<=(T val) const { - xtensor r(shape_); + Tensor r(shape_); for (std::size_t i = 0; i < data_.size(); ++i) r.data()[i] = data_[i] <= val; return r; } - xtensor operator<(T val) const + Tensor operator<(T val) const { - xtensor r(shape_); + Tensor r(shape_); for (std::size_t i = 0; i < data_.size(); ++i) r.data()[i] = data_[i] < val; return r; } - xtensor operator>=(T val) const + Tensor operator>=(T val) const { - xtensor r(shape_); + Tensor r(shape_); for (std::size_t i = 0; i < data_.size(); ++i) r.data()[i] = data_[i] >= val; return r; } - xtensor operator>(T val) const + Tensor operator>(T val) const { - xtensor r(shape_); + Tensor r(shape_); for (std::size_t i = 0; i < data_.size(); ++i) r.data()[i] = data_[i] > val; return r; } - xtensor operator<(const xtensor& o) const + Tensor operator<(const Tensor& o) const { - xtensor r(shape_); + Tensor r(shape_); for (std::size_t i = 0; i < data_.size(); ++i) r.data()[i] = data_[i] < o.data_[i]; return r; } private: - //-------------------------------------------------------------------------- - // Private methods - std::size_t compute_size() const { std::size_t s = 1; - for (std::size_t i = 0; i < N; ++i) - s *= shape_[i]; + for (auto d : shape_) + s *= d; return s; } @@ -809,487 +804,291 @@ class xtensor { //-------------------------------------------------------------------------- // Data members + openmc::vector shape_; openmc::vector> data_; - std::array shape_; }; -// scalar op tensor (same type) -template::value>> -xtensor operator*(T val, const xtensor& arr) +Tensor operator*(T val, const Tensor& arr) { return arr * val; } -// Mixed-type arithmetic: xtensor op xtensor -// Returns xtensor (used for int*double mesh arithmetic in mesh.cpp) -template::value>> +Tensor operator+(T val, const Tensor& arr) +{ + return arr + val; +} + +// Mixed-type arithmetic: Tensor op Tensor -> Tensor +template::value>> -xtensor operator*( - const xtensor& a, const xtensor& b) +Tensor operator*(const Tensor& a, const Tensor& b) { - xtensor r(a.shape()); + Tensor r(a.shape()); for (std::size_t i = 0; i < a.size(); ++i) - r.data()[i] = static_cast(a.data()[i]) * - static_cast(b.data()[i]); + r.data()[i] = + static_cast(a.data()[i]) * static_cast(b.data()[i]); return r; } -template::value>> -xtensor operator/( - const xtensor& a, const xtensor& b) +Tensor operator/(const Tensor& a, const Tensor& b) { - xtensor r(a.shape()); + Tensor r(a.shape()); for (std::size_t i = 0; i < a.size(); ++i) - r.data()[i] = static_cast(a.data()[i]) / - static_cast(b.data()[i]); + r.data()[i] = + static_cast(a.data()[i]) / static_cast(b.data()[i]); return r; } //============================================================================== -// xarray: dynamic-rank tensor. -// -// Like xtensor but the number of dimensions is determined at runtime. -// Used when the rank is not known at compile time (e.g. data read from -// HDF5 files whose dimensionality varies). +// View1D deferred method definitions (need Tensor to be complete) //============================================================================== template -class xarray { -public: - //-------------------------------------------------------------------------- - // Types - - using value_type = T; - using stored_type = storage_type; - using iterator = typename openmc::vector::iterator; - using const_iterator = typename openmc::vector::const_iterator; - - //-------------------------------------------------------------------------- - // Constructors - - xarray() = default; - - explicit xarray(const openmc::vector& shape) : shape_(shape) - { - std::size_t total = 1; - for (auto d : shape_) - total *= d; - data_.resize(total); - } - - xarray(const openmc::vector& shape, T val) : shape_(shape) - { - std::size_t total = 1; - for (auto d : shape_) - total *= d; - data_.assign(total, val); - } - - xarray(std::initializer_list vals) : shape_({vals.size()}) - { - data_.assign(vals.begin(), vals.end()); - } - - //! Converting constructor from xtensor (copies data) - template - xarray(const xtensor& t) - { - for (std::size_t i = 0; i < N; ++i) - shape_.push_back(t.shape()[i]); - data_.assign(t.data(), t.data() + t.size()); - } - - //-------------------------------------------------------------------------- - // Accessors - - stored_type* data() { return data_.data(); } - const stored_type* data() const { return data_.data(); } - std::size_t size() const { return data_.size(); } - const openmc::vector& shape() const { return shape_; } - std::size_t shape(std::size_t dim) const { return shape_[dim]; } - bool empty() const { return data_.empty(); } - - //-------------------------------------------------------------------------- - // Indexing - - template - stored_type& operator()(Indices... indices) - { - return data_[compute_offset(static_cast(indices)...)]; - } - - template - const stored_type& operator()(Indices... indices) const - { - return data_[compute_offset(static_cast(indices)...)]; - } - - stored_type& operator[](std::size_t i) { return data_[i]; } - const stored_type& operator[](std::size_t i) const { return data_[i]; } - - //-------------------------------------------------------------------------- - // Iterators - - iterator begin() { return data_.begin(); } - iterator end() { return data_.end(); } - const_iterator begin() const { return data_.begin(); } - const_iterator end() const { return data_.end(); } - - //-------------------------------------------------------------------------- - // Methods - - void resize(const openmc::vector& shape) - { - shape_ = shape; - std::size_t total = 1; - for (auto d : shape_) - total *= d; - data_.resize(total); - } - - void resize(const openmc::vector& shape) - { - shape_.clear(); - for (auto d : shape) - shape_.push_back(static_cast(d)); - std::size_t total = 1; - for (auto d : shape_) - total *= d; - data_.resize(total); - } - - //! Reinterpret the shape without changing the underlying data - template - void reshape(const ShapeType& new_shape) - { - shape_.clear(); - for (auto d : new_shape) - shape_.push_back(static_cast(d)); - } - - void fill(T val) { std::fill(data_.begin(), data_.end(), val); } - - //! Conversion to xtensor (copies data) - template - operator xtensor() const - { - std::array s {}; - for (std::size_t i = 0; i < M && i < shape_.size(); ++i) - s[i] = shape_[i]; - xtensor result(s); - std::copy(data_.begin(), data_.end(), result.data()); - return result; - } - - //-------------------------------------------------------------------------- - // Element-wise binary operators (xarray op scalar) - - xarray operator-(T val) const - { - xarray r(shape_); - for (std::size_t i = 0; i < data_.size(); ++i) - r.data_[i] = data_[i] - val; - return r; - } - - //-------------------------------------------------------------------------- - // Element-wise comparison operators (return bool xarray) - - xarray operator<=(T val) const - { - xarray r(shape_); - for (std::size_t i = 0; i < data_.size(); ++i) - r.data()[i] = data_[i] <= val; - return r; - } - -private: - //-------------------------------------------------------------------------- - // Private methods - - //! Row-major offset calculations for operator() - std::size_t compute_offset(std::size_t i0) const { return i0; } - - std::size_t compute_offset(std::size_t i0, std::size_t i1) const - { - return i0 * shape_[1] + i1; - } - - std::size_t compute_offset( - std::size_t i0, std::size_t i1, std::size_t i2) const - { - return (i0 * shape_[1] + i1) * shape_[2] + i2; - } - - std::size_t compute_offset( - std::size_t i0, std::size_t i1, std::size_t i2, std::size_t i3) const - { - return ((i0 * shape_[1] + i1) * shape_[2] + i2) * shape_[3] + i3; - } - - //-------------------------------------------------------------------------- - // Data members - - openmc::vector> data_; - openmc::vector shape_; -}; - -// xtensor converting constructor from xarray (defined after xarray is complete) -template -xtensor::xtensor(const xarray& other) +template +View1D& View1D::operator=(const Tensor& other) { - for (std::size_t i = 0; i < N; ++i) - shape_[i] = other.shape()[i]; - data_.assign(other.data(), other.data() + other.size()); + for (std::size_t i = 0; i < size_; ++i) + data_[i * stride_] = static_cast(other.data()[i]); + return *this; } -// xtensor assignment from xarray (defined after xarray is complete) -template -xtensor& xtensor::operator=(const xarray& other) +template +template +View1D& View1D::operator+=(const Tensor& o) { - for (std::size_t i = 0; i < N; ++i) - shape_[i] = other.shape()[i]; - data_.assign(other.data(), other.data() + other.size()); + for (std::size_t i = 0; i < size_; ++i) + data_[i * stride_] += o.data()[i]; return *this; } -// Mixed-type arithmetic: xarray op xtensor -// (used for xarray * xtensor mesh arithmetic in mesh.cpp) -template -xtensor operator*(const xarray& a, const xtensor& b) -{ - xtensor r(b.shape()); - for (std::size_t i = 0; i < b.size(); ++i) - r.data()[i] = static_cast(a.data()[i]) * - static_cast(b.data()[i]); - return r; -} +//============================================================================== +// Tensor::sum(axis) — reduces one dimension +// +// Uses explicit nested loops for each rank combination to ensure identical +// floating-point accumulation order with the Phase 1 implementation. +//============================================================================== -template -xtensor operator*(const xtensor& a, const xarray& b) -{ - xtensor r(a.shape()); - for (std::size_t i = 0; i < a.size(); ++i) - r.data()[i] = static_cast(a.data()[i]) * - static_cast(b.data()[i]); - return r; -} +template +Tensor Tensor::sum(std::size_t axis) const +{ + std::size_t ndims = shape_.size(); + + if (ndims == 2) { + // 2D sum along axis -> 1D + auto s0 = shape_[0], s1 = shape_[1]; + if (axis == 0) { + Tensor r({s1}, T(0)); + for (std::size_t i = 0; i < s0; ++i) + for (std::size_t j = 0; j < s1; ++j) + r.data()[j] += data_[i * s1 + j]; + return r; + } else { + Tensor r({s0}, T(0)); + for (std::size_t i = 0; i < s0; ++i) + for (std::size_t j = 0; j < s1; ++j) + r.data()[i] += data_[i * s1 + j]; + return r; + } + } else if (ndims == 3) { + // 3D sum along axis -> 2D + auto s0 = shape_[0], s1 = shape_[1], s2 = shape_[2]; + if (axis == 0) { + Tensor r({s1, s2}, T(0)); + for (std::size_t i = 0; i < s0; ++i) + for (std::size_t j = 0; j < s1; ++j) + for (std::size_t k = 0; k < s2; ++k) + r.data()[j * s2 + k] += data_[(i * s1 + j) * s2 + k]; + return r; + } else if (axis == 1) { + Tensor r({s0, s2}, T(0)); + for (std::size_t i = 0; i < s0; ++i) + for (std::size_t j = 0; j < s1; ++j) + for (std::size_t k = 0; k < s2; ++k) + r.data()[i * s2 + k] += data_[(i * s1 + j) * s2 + k]; + return r; + } else { + Tensor r({s0, s1}, T(0)); + for (std::size_t i = 0; i < s0; ++i) + for (std::size_t j = 0; j < s1; ++j) + for (std::size_t k = 0; k < s2; ++k) + r.data()[i * s1 + j] += data_[(i * s1 + j) * s2 + k]; + return r; + } + } else if (ndims == 4) { + // 4D sum along axis -> 3D + auto s0 = shape_[0], s1 = shape_[1], s2 = shape_[2], s3 = shape_[3]; + if (axis == 3) { + Tensor r({s0, s1, s2}, T(0)); + for (std::size_t i = 0; i < s0; ++i) + for (std::size_t j = 0; j < s1; ++j) + for (std::size_t k = 0; k < s2; ++k) + for (std::size_t l = 0; l < s3; ++l) + r.data()[(i * s1 + j) * s2 + k] += + data_[((i * s1 + j) * s2 + k) * s3 + l]; + return r; + } else if (axis == 2) { + Tensor r({s0, s1, s3}, T(0)); + for (std::size_t i = 0; i < s0; ++i) + for (std::size_t j = 0; j < s1; ++j) + for (std::size_t k = 0; k < s2; ++k) + for (std::size_t l = 0; l < s3; ++l) + r.data()[(i * s1 + j) * s3 + l] += + data_[((i * s1 + j) * s2 + k) * s3 + l]; + return r; + } else if (axis == 1) { + Tensor r({s0, s2, s3}, T(0)); + for (std::size_t i = 0; i < s0; ++i) + for (std::size_t j = 0; j < s1; ++j) + for (std::size_t k = 0; k < s2; ++k) + for (std::size_t l = 0; l < s3; ++l) + r.data()[(i * s2 + k) * s3 + l] += + data_[((i * s1 + j) * s2 + k) * s3 + l]; + return r; + } else { + Tensor r({s1, s2, s3}, T(0)); + for (std::size_t i = 0; i < s0; ++i) + for (std::size_t j = 0; j < s1; ++j) + for (std::size_t k = 0; k < s2; ++k) + for (std::size_t l = 0; l < s3; ++l) + r.data()[(j * s2 + k) * s3 + l] += + data_[((i * s1 + j) * s2 + k) * s3 + l]; + return r; + } + } -template -xtensor operator/(const xtensor& a, const xarray& b) -{ - xtensor r(a.shape()); - for (std::size_t i = 0; i < a.size(); ++i) - r.data()[i] = static_cast(a.data()[i]) / - static_cast(b.data()[i]); - return r; + // Fallback: general case for any rank (should not be reached in practice) + openmc::vector out_shape; + for (std::size_t d = 0; d < ndims; ++d) + if (d != axis) + out_shape.push_back(shape_[d]); + + Tensor result(out_shape, T(0)); + + openmc::vector strides(ndims); + strides[ndims - 1] = 1; + for (int d = static_cast(ndims) - 2; d >= 0; --d) + strides[d] = strides[d + 1] * shape_[d + 1]; + + std::size_t out_ndims = out_shape.size(); + openmc::vector out_strides(out_ndims); + if (out_ndims > 0) { + out_strides[out_ndims - 1] = 1; + for (int d = static_cast(out_ndims) - 2; d >= 0; --d) + out_strides[d] = out_strides[d + 1] * out_shape[d + 1]; + } + + std::size_t total = data_.size(); + for (std::size_t flat = 0; flat < total; ++flat) { + std::size_t remaining = flat; + std::size_t out_flat = 0; + std::size_t out_d = 0; + for (std::size_t d = 0; d < ndims; ++d) { + std::size_t idx = remaining / strides[d]; + remaining %= strides[d]; + if (d != axis) { + out_flat += idx * out_strides[out_d]; + ++out_d; + } + } + result.data()[out_flat] += data_[flat]; + } + + return result; } //============================================================================== -// xtensor_fixed>: compile-time-sized 2D tensor. -// -// Both the rank and the dimensions are template parameters, so the storage -// is a plain C array with no heap allocation. Used for small, fixed-size -// matrices (e.g. tally result accumulators). +// Fixed2D: compile-time fixed 2D tensor. //============================================================================== -template -class xtensor_fixed { +template +class Fixed2D { public: - //-------------------------------------------------------------------------- - // Types - using value_type = T; - //-------------------------------------------------------------------------- - // Accessors - template T& operator()(I0 i, I1 j) { - return data_[static_cast(i) * dim1_ + + return data_[static_cast(i) * C + static_cast(j)]; } template const T& operator()(I0 i, I1 j) const { - return data_[static_cast(i) * dim1_ + + return data_[static_cast(i) * C + static_cast(j)]; } T* data() { return data_; } const T* data() const { return data_; } - std::size_t size() const { return total_; } - std::array shape() const { return {dim0_, dim1_}; } + constexpr std::size_t size() const { return R * C; } + std::array shape() const { return {R, C}; } - //-------------------------------------------------------------------------- - // Methods + void fill(T val) { std::fill(data_, data_ + R * C, val); } - void fill(T val) { std::fill(data_, data_ + total_, val); } + T* begin() { return data_; } + T* end() { return data_ + R * C; } + const T* begin() const { return data_; } + const T* end() const { return data_ + R * C; } -private: - //-------------------------------------------------------------------------- - // Data members + //! Column view + View1D col(std::size_t j) { return {data_ + j, R, C}; } + View1D col(std::size_t j) const { return {data_ + j, R, C}; } - static constexpr std::size_t total_ = detail::xshape_traits::total; - static constexpr std::size_t dim0_ = detail::xshape_traits::dim0; - static constexpr std::size_t dim1_ = detail::xshape_traits::dim1; + //! Flat view + ViewFlat flat() { return {data_, R * C}; } + ViewFlat flat() const { return {data_, R * C}; } - T data_[total_] = {}; +private: + T data_[R * C] = {}; }; //============================================================================== -// adapt(): create a tensor by copying data from an existing raw pointer or -// std::vector. Several overloads handle different ownership semantics. +// Free functions //============================================================================== -// Adapt a std::vector into a 1D xtensor (copy) -template -xtensor adapt(const std::vector& vec) -{ - xtensor result({vec.size()}); - std::copy(vec.begin(), vec.end(), result.data()); - return result; -} - -// Adapt a vector with explicit shape into xarray (copy) -template -xarray adapt(const openmc::vector& vec, const ShapeType& shape) -{ - openmc::vector s; - for (auto d : shape) - s.push_back(static_cast(d)); - xarray result(s); - std::copy(vec.begin(), vec.end(), result.data()); - return result; -} - -// Adapt vector with initializer_list shape (for brace-init-list deduction) +// zeros template -xarray adapt(const openmc::vector& vec, std::initializer_list shape) -{ - openmc::vector s; - for (auto d : shape) - s.push_back(static_cast(d)); - xarray result(s); - std::copy(vec.begin(), vec.end(), result.data()); - return result; -} - -// Adapt std::array with initializer_list shape (for brace-init-list deduction) -template -xarray adapt(const std::array& arr, std::initializer_list shape) -{ - openmc::vector s; - std::size_t total = 1; - for (auto d : shape) { - auto dim = static_cast(d); - s.push_back(dim); - total *= dim; - } - xarray result(s); - std::copy(arr.data(), arr.data() + std::min(total, M), result.data()); - return result; -} - -// Adapt raw pointer with no_ownership into xtensor (copy) -template -xarray adapt( - const T* ptr, std::size_t n, no_ownership, const ShapeType& shape) -{ - openmc::vector s; - for (auto d : shape) - s.push_back(static_cast(d)); - xarray result(s); - std::copy(ptr, ptr + n, result.data()); - return result; -} - -// Adapt raw pointer with acquire_ownership into xarray (copy + delete) -template -xarray adapt( - T* ptr, std::size_t total, acquire_ownership, const ShapeType& shape) -{ - openmc::vector s; - for (auto d : shape) - s.push_back(static_cast(d)); - xarray result(s); - std::copy(ptr, ptr + total, result.data()); - delete[] ptr; - return result; -} - -//============================================================================== -// Construction helpers: zeros, zeros_like, full_like, empty, empty_like -//============================================================================== - -template -xarray zeros(std::initializer_list shape) +Tensor zeros(std::initializer_list shape) { openmc::vector s(shape); - xarray result(s, T(0)); - return result; -} - -// zeros with vector shape -template -xarray zeros(const openmc::vector& shape) -{ - return xarray(shape, T(0)); + return Tensor(std::move(s), T(0)); } template -xarray empty(std::initializer_list shape) +Tensor zeros(const openmc::vector& shape) { - openmc::vector s(shape); - return xarray(s); + return Tensor(shape, T(0)); } -// empty with int initializer_list (avoids narrowing errors) +// zeros_like template -xarray empty(std::initializer_list shape) -{ - openmc::vector s; - for (auto d : shape) - s.push_back(static_cast(d)); - return xarray(s); -} - -// empty with std::array shape -template -xarray empty(const std::array& shape) +Tensor zeros_like(const Tensor& o) { - openmc::vector s; - for (auto d : shape) - s.push_back(static_cast(d)); - return xarray(s); + return Tensor(o.shape(), T(0)); } -template -xtensor zeros_like(const xtensor& o) +// full_like +template +Tensor full_like(const Tensor& o, V val) { - return xtensor(o.shape(), T(0)); -} - -template -xtensor empty_like(const xtensor& o) -{ - return xtensor(o.shape()); -} - -// full_like: create tensor with same shape, filled with value -template -xtensor full_like(const xtensor& o, V val) -{ - return xtensor(o.shape(), static_cast(val)); + return Tensor(o.shape(), static_cast(val)); } +// linspace template -xtensor linspace(T start, T stop, std::size_t n) +Tensor linspace(T start, T stop, std::size_t n) { - xtensor result({n}); + Tensor result({n}); if (n < 2) { result[0] = start; return result; @@ -1301,443 +1100,42 @@ xtensor linspace(T start, T stop, std::size_t n) return result; } -//============================================================================== -// view(): extract rows, columns, slices, or flat views from tensors. -// -// Returns lightweight view objects (xtensor_view_1d, _2d, or _flat) that -// reference the parent tensor's storage without copying. Overloads are -// selected by argument types: int pins an axis, all() keeps it, range() -// selects a subrange. -//============================================================================== - -// Resolve a range endpoint: if == placeholders::_, use dim as the end -inline std::size_t resolve_end(std::ptrdiff_t val, std::size_t dim) -{ - if (val == placeholders::_) - return dim; - if (val < 0) - return dim + val; - return static_cast(val); -} - -// view(2D tensor, range, int) -> 1D view of column slice -template -xtensor_view_1d view(xtensor& a, xrange_type r, std::size_t col) -{ - auto start = resolve_end(r.start, a.shape()[0]); - auto stop = resolve_end(r.stop, a.shape()[0]); - return {a.data() + start * a.shape()[1] + col, stop - start, a.shape()[1]}; -} -template -xtensor_view_1d view( - const xtensor& a, xrange_type r, std::size_t col) -{ - auto start = resolve_end(r.start, a.shape()[0]); - auto stop = resolve_end(r.stop, a.shape()[0]); - return {a.data() + start * a.shape()[1] + col, stop - start, a.shape()[1]}; -} - -// view(2D tensor, all, int) -> 1D column view -template -xtensor_view_1d view(xtensor& a, xall_type, std::size_t col) -{ - return {a.data() + col, a.shape()[0], a.shape()[1]}; -} -template -xtensor_view_1d view( - const xtensor& a, xall_type, std::size_t col) -{ - return {a.data() + col, a.shape()[0], a.shape()[1]}; -} - -// view(2D tensor, int) -> 1D row view -template -xtensor_view_1d view(xtensor& a, std::size_t row) -{ - return {a.data() + row * a.shape()[1], a.shape()[1], 1}; -} -template -xtensor_view_1d view(const xtensor& a, std::size_t row) -{ - return {a.data() + row * a.shape()[1], a.shape()[1], 1}; -} - -// view(1D tensor, range) -> 1D view -template -xtensor_view_1d view(xtensor& a, xrange_type r) -{ - auto start = resolve_end(r.start, a.shape()[0]); - auto stop = resolve_end(r.stop, a.shape()[0]); - return {a.data() + start, stop - start, 1}; -} -template -xtensor_view_1d view(const xtensor& a, xrange_type r) -{ - auto start = resolve_end(r.start, a.shape()[0]); - auto stop = resolve_end(r.stop, a.shape()[0]); - return {a.data() + start, stop - start, 1}; -} - -// view(3D tensor, int, int, all) -> 1D view along depth dimension -template -xtensor_view_1d view(xtensor& a, std::size_t i, std::size_t j, xall_type) -{ - auto depth = a.shape()[2]; - return {a.data() + (i * a.shape()[1] + j) * depth, depth, 1}; -} -template -xtensor_view_1d view( - const xtensor& a, std::size_t i, std::size_t j, xall_type) -{ - auto depth = a.shape()[2]; - return {a.data() + (i * a.shape()[1] + j) * depth, depth, 1}; -} - -// view(3D tensor, all, all, int) -> 2D view -template -xtensor_view_2d view(xtensor& a, xall_type, xall_type, std::size_t k) -{ - return {a.data() + k, a.shape()[0], a.shape()[1], - a.shape()[1] * a.shape()[2], a.shape()[2]}; -} -template -xtensor_view_2d view( - const xtensor& a, xall_type, xall_type, std::size_t k) -{ - return {a.data() + k, a.shape()[0], a.shape()[1], - a.shape()[1] * a.shape()[2], a.shape()[2]}; -} - -// view(fixed_2D, all, int) -> 1D column view -template -xtensor_view_1d view(xtensor_fixed& a, xall_type, std::size_t col) -{ - auto s = a.shape(); - return {a.data() + col, s[0], s[1]}; -} -template -xtensor_view_1d view( - const xtensor_fixed& a, xall_type, std::size_t col) -{ - auto s = a.shape(); - return {a.data() + col, s[0], s[1]}; -} - -// view(fixed, all) -> flat view (for bulk assignment) -template -xtensor_view_flat view(xtensor_fixed& a, xall_type) -{ - return {a.data(), a.size()}; -} - -// view(tensor, all) -> flat view (for bulk assignment) -template -xtensor_view_flat view(xtensor& a, xall_type) -{ - return {a.data(), a.size()}; -} - -// view(xarray, int) -> row view of 2D xarray -template -xtensor_view_1d view(xarray& a, std::size_t row) -{ - return {a.data() + row * a.shape()[1], a.shape()[1], 1}; -} +// concatenate (two 1D tensors) template -xtensor_view_1d view(const xarray& a, std::size_t row) +Tensor concatenate(const Tensor& a, const Tensor& b) { - return {a.data() + row * a.shape()[1], a.shape()[1], 1}; -} - -// view(2D tensor, row, xall_type) -> 1D row view (entire row) -template -xtensor_view_1d view(xtensor& a, std::size_t row, xall_type) -{ - return {a.data() + row * a.shape()[1], a.shape()[1], 1}; -} -template -xtensor_view_1d view(const xtensor& a, std::size_t row, xall_type) -{ - return {a.data() + row * a.shape()[1], a.shape()[1], 1}; -} - -// view(2D xarray, row, range) -> 1D view of row subset -template -xtensor_view_1d view(xarray& a, std::size_t row, xrange_type r) -{ - auto cols = a.shape()[1]; - auto start = resolve_end(r.start, cols); - auto stop = resolve_end(r.stop, cols); - return {a.data() + row * cols + start, stop - start, 1}; -} -template -xtensor_view_1d view(const xarray& a, std::size_t row, xrange_type r) -{ - auto cols = a.shape()[1]; - auto start = resolve_end(r.start, cols); - auto stop = resolve_end(r.stop, cols); - return {a.data() + row * cols + start, stop - start, 1}; -} - -// row() and col() for 2D tensors -template -xtensor_view_1d row(xtensor& a, std::size_t i) -{ - return {a.data() + i * a.shape()[1], a.shape()[1], 1}; -} -template -xtensor_view_1d row(const xtensor& a, std::size_t i) -{ - return {a.data() + i * a.shape()[1], a.shape()[1], 1}; -} -template -xtensor_view_1d col(xtensor& a, std::size_t j) -{ - return {a.data() + j, a.shape()[0], a.shape()[1]}; -} -template -xtensor_view_1d col(const xtensor& a, std::size_t j) -{ - return {a.data() + j, a.shape()[0], a.shape()[1]}; -} - -//============================================================================== -// Math / reduction functions -//============================================================================== - -//! Wrapper returned by sum() that supports both xt::sum(x)() (call operator) -//! and implicit conversion to scalar, matching the xtensor API convention. -template -struct sum_proxy { - T value; - T operator()() const { return value; } - operator T() const { return value; } -}; - -template -sum_proxy sum(const xtensor& a) -{ - T s = T(0); - for (std::size_t i = 0; i < a.size(); ++i) - s += a.data()[i]; - return {s}; -} - -template -sum_proxy sum(const xtensor_view_1d& v) -{ - T s = T(0); - for (std::size_t i = 0; i < v.size(); ++i) - s += v(i); - return {s}; -} - -// sum along axis - reduces one dimension -// 2D sum along axis -> 1D -template -xtensor sum(const xtensor& a, std::initializer_list axes) -{ - int axis = *axes.begin(); - auto s = a.shape(); - if (axis == 0) { - xtensor r({s[1]}, T(0)); - for (std::size_t i = 0; i < s[0]; ++i) - for (std::size_t j = 0; j < s[1]; ++j) - r(j) += a(i, j); - return r; - } else { - xtensor r({s[0]}, T(0)); - for (std::size_t i = 0; i < s[0]; ++i) - for (std::size_t j = 0; j < s[1]; ++j) - r(i) += a(i, j); - return r; - } -} - -// 3D sum along axis -> 2D -template -xtensor sum(const xtensor& a, std::initializer_list axes) -{ - int axis = *axes.begin(); - auto s = a.shape(); - if (axis == 0) { - xtensor r({s[1], s[2]}, T(0)); - for (std::size_t i = 0; i < s[0]; ++i) - for (std::size_t j = 0; j < s[1]; ++j) - for (std::size_t k = 0; k < s[2]; ++k) - r(j, k) += a(i, j, k); - return r; - } else if (axis == 1) { - xtensor r({s[0], s[2]}, T(0)); - for (std::size_t i = 0; i < s[0]; ++i) - for (std::size_t j = 0; j < s[1]; ++j) - for (std::size_t k = 0; k < s[2]; ++k) - r(i, k) += a(i, j, k); - return r; - } else { - xtensor r({s[0], s[1]}, T(0)); - for (std::size_t i = 0; i < s[0]; ++i) - for (std::size_t j = 0; j < s[1]; ++j) - for (std::size_t k = 0; k < s[2]; ++k) - r(i, j) += a(i, j, k); - return r; - } -} - -// 4D sum along axis -> 3D -template -xtensor sum(const xtensor& a, std::initializer_list axes) -{ - int axis = *axes.begin(); - auto s = a.shape(); - if (axis == 3) { - xtensor r({s[0], s[1], s[2]}, T(0)); - for (std::size_t i = 0; i < s[0]; ++i) - for (std::size_t j = 0; j < s[1]; ++j) - for (std::size_t k = 0; k < s[2]; ++k) - for (std::size_t l = 0; l < s[3]; ++l) - r(i, j, k) += a(i, j, k, l); - return r; - } else if (axis == 2) { - xtensor r({s[0], s[1], s[3]}, T(0)); - for (std::size_t i = 0; i < s[0]; ++i) - for (std::size_t j = 0; j < s[1]; ++j) - for (std::size_t k = 0; k < s[2]; ++k) - for (std::size_t l = 0; l < s[3]; ++l) - r(i, j, l) += a(i, j, k, l); - return r; - } else if (axis == 1) { - xtensor r({s[0], s[2], s[3]}, T(0)); - for (std::size_t i = 0; i < s[0]; ++i) - for (std::size_t j = 0; j < s[1]; ++j) - for (std::size_t k = 0; k < s[2]; ++k) - for (std::size_t l = 0; l < s[3]; ++l) - r(i, k, l) += a(i, j, k, l); - return r; - } else { - xtensor r({s[1], s[2], s[3]}, T(0)); - for (std::size_t i = 0; i < s[0]; ++i) - for (std::size_t j = 0; j < s[1]; ++j) - for (std::size_t k = 0; k < s[2]; ++k) - for (std::size_t l = 0; l < s[3]; ++l) - r(j, k, l) += a(i, j, k, l); - return r; - } -} - -//! Wrapper returned by prod(), analogous to sum_proxy. -template -struct prod_proxy { - T value; - T operator()() const { return value; } - operator T() const { return value; } -}; - -template -prod_proxy prod(const xtensor& a) -{ - T p = T(1); - for (std::size_t i = 0; i < a.size(); ++i) - p *= a.data()[i]; - return {p}; -} - -template -prod_proxy prod(const xarray& a) -{ - T p = T(1); - for (std::size_t i = 0; i < a.size(); ++i) - p *= a.data()[i]; - return {p}; -} - -// any (for bool tensors) -template -bool any(const xtensor& a) -{ - for (std::size_t i = 0; i < a.size(); ++i) - if (a.data()[i]) - return true; - return false; -} - -// any/all for xarray -template -bool any(const xarray& a) -{ - for (std::size_t i = 0; i < a.size(); ++i) - if (a.data()[i]) - return true; - return false; -} - -// all (for bool tensors) -template -bool all(const xtensor& a) -{ - for (std::size_t i = 0; i < a.size(); ++i) - if (!a.data()[i]) - return false; - return true; -} - -//! Wrapper returned by argmin() that supports both argmin(x)[0] (subscript) -//! and implicit conversion to size_t/int, matching the xtensor API convention. -struct argmin_result { - std::size_t value; - std::size_t operator[](std::size_t) const { return value; } - operator std::size_t() const { return value; } - operator int() const { return static_cast(value); } -}; - -template -argmin_result argmin(const xtensor& a) -{ - return {static_cast(std::distance(a.data(), - std::min_element(a.data(), a.data() + a.size())))}; -} - -template -argmin_result argmin(const xarray& a) -{ - return {static_cast(std::distance(a.data(), - std::min_element(a.data(), a.data() + a.size())))}; + std::size_t total = a.size() + b.size(); + Tensor result({total}); + std::copy(a.data(), a.data() + a.size(), result.data()); + std::copy(b.data(), b.data() + b.size(), result.data() + a.size()); + return result; } // Element-wise math -template -xtensor log(const xtensor& a) +template +Tensor log(const Tensor& a) { - xtensor r(a.shape()); + Tensor r(a.shape()); for (std::size_t i = 0; i < a.size(); ++i) r.data()[i] = std::log(a.data()[i]); return r; } -template -xtensor abs(const xtensor& a) -{ - xtensor r(a.shape()); - for (std::size_t i = 0; i < a.size(); ++i) - r.data()[i] = std::abs(a.data()[i]); - return r; -} - template -xarray abs(const xarray& a) +Tensor abs(const Tensor& a) { - xarray r(a.shape()); + Tensor r(a.shape()); for (std::size_t i = 0; i < a.size(); ++i) r.data()[i] = std::abs(a.data()[i]); return r; } // where with tensor true_val and scalar false_val -template -xtensor where( - const xtensor& cond, const xtensor& true_val, V false_val) +template +Tensor where( + const Tensor& cond, const Tensor& true_val, V false_val) { - xtensor r(cond.shape()); + Tensor r(cond.shape()); for (std::size_t i = 0; i < cond.size(); ++i) r.data()[i] = cond.data()[i] ? true_val.data()[i] : static_cast(false_val); @@ -1745,12 +1143,12 @@ xtensor where( } // nan_to_num -template -xtensor nan_to_num(const xtensor& a, T nan_val = T(0), +template +Tensor nan_to_num(const Tensor& a, T nan_val = T(0), T posinf_val = std::numeric_limits::max(), T neginf_val = std::numeric_limits::lowest()) { - xtensor r(a.shape()); + Tensor r(a.shape()); for (std::size_t i = 0; i < a.size(); ++i) { T val = a.data()[i]; if (std::isnan(val)) @@ -1763,95 +1161,22 @@ xtensor nan_to_num(const xtensor& a, T nan_val = T(0), return r; } -// eval: returns a copy (materializes lazy expressions) -template -xtensor eval(const xtensor& a) -{ - return a; -} - -//============================================================================== -// concatenate & xtuple -//============================================================================== - -//! Holds references to up to 10 tensors for concatenate(). -//! Created by xt::xtuple(a, b, ...) and consumed by xt::concatenate(). -template -struct xtuple_holder { - struct entry { - const T* ptr; - std::size_t size; - }; - std::array entries; - std::size_t count; -}; - -// xtuple for 2 args - accepts any container with data() and size() -template -auto xtuple(const A& a, const B& b) - -> xtuple_holder>> -{ - using T = std::remove_const_t>; - xtuple_holder h {}; - h.entries[0] = {a.data(), a.size()}; - h.entries[1] = {b.data(), b.size()}; - h.count = 2; - return h; -} - -template -xtensor concatenate(const xtuple_holder& tup) -{ - std::size_t total = 0; - for (std::size_t i = 0; i < tup.count; ++i) - total += tup.entries[i].size; - - xtensor result({total}); - std::size_t pos = 0; - for (std::size_t i = 0; i < tup.count; ++i) { - std::copy(tup.entries[i].ptr, tup.entries[i].ptr + tup.entries[i].size, - result.data() + pos); - pos += tup.entries[i].size; - } - return result; -} - -// flip 2D along axis 0 -template -xtensor flip(const xtensor& a, std::size_t axis = 0) -{ - auto s = a.shape(); - xtensor r(s); - if (axis == 0) { - for (std::size_t i = 0; i < s[0]; ++i) - for (std::size_t j = 0; j < s[1]; ++j) - r(i, j) = a(s[0] - 1 - i, j); - } else { - for (std::size_t i = 0; i < s[0]; ++i) - for (std::size_t j = 0; j < s[1]; ++j) - r(i, j) = a(i, s[1] - 1 - j); - } - return r; -} - //============================================================================== // Type traits //============================================================================== -//! Type trait that is true for xtensor, xarray, and xtensor_fixed. +//! Type trait that is true for Tensor and Fixed2D. //! Used by hdf5_interface.h to select the correct write_dataset overload. template -struct is_xt_container : std::false_type {}; - -template -struct is_xt_container> : std::true_type {}; +struct is_tensor : std::false_type {}; template -struct is_xt_container> : std::true_type {}; +struct is_tensor> : std::true_type {}; -template -struct is_xt_container> : std::true_type {}; +template +struct is_tensor> : std::true_type {}; -} // namespace xt +} // namespace tensor +} // namespace openmc #endif // OPENMC_TENSOR_H diff --git a/include/openmc/urr.h b/include/openmc/urr.h index 04881ca70c2..d40c2aff7e0 100644 --- a/include/openmc/urr.h +++ b/include/openmc/urr.h @@ -40,11 +40,11 @@ class UrrData { * below, obviously, values of the CDF are stored. For the xs_values * variable, the columns line up with the index of cdf_values. */ - xt::xtensor cdf_values_; // Note: must be row major! - xt::xtensor xs_values_; + tensor::Tensor cdf_values_; // Note: must be row major! + tensor::Tensor xs_values_; // Number of points in the CDF - auto n_cdf() const { return cdf_values_.shape()[1]; } + auto n_cdf() const { return cdf_values_.shape(1); } //! \brief Load the URR data from the provided HDF5 group explicit UrrData(hid_t group_id); diff --git a/include/openmc/weight_windows.h b/include/openmc/weight_windows.h index 0d7435ca2a5..97abcfe82ef 100644 --- a/include/openmc/weight_windows.h +++ b/include/openmc/weight_windows.h @@ -143,10 +143,10 @@ class WeightWindows { const vector& energy_bounds() const { return energy_bounds_; } - void set_bounds(const xt::xtensor& lower_ww_bounds, - const xt::xtensor& upper_bounds); + void set_bounds(const tensor::Tensor& lower_ww_bounds, + const tensor::Tensor& upper_bounds); - void set_bounds(const xt::xtensor& lower_bounds, double ratio); + void set_bounds(const tensor::Tensor& lower_bounds, double ratio); void set_bounds( span lower_bounds, span upper_bounds); @@ -182,11 +182,11 @@ class WeightWindows { const std::unique_ptr& mesh() const { return model::meshes[mesh_idx_]; } - const xt::xtensor& lower_ww_bounds() const { return lower_ww_; } - xt::xtensor& lower_ww_bounds() { return lower_ww_; } + const tensor::Tensor& lower_ww_bounds() const { return lower_ww_; } + tensor::Tensor& lower_ww_bounds() { return lower_ww_; } - const xt::xtensor& upper_ww_bounds() const { return upper_ww_; } - xt::xtensor& upper_ww_bounds() { return upper_ww_; } + const tensor::Tensor& upper_ww_bounds() const { return upper_ww_; } + tensor::Tensor& upper_ww_bounds() { return upper_ww_; } ParticleType particle_type() const { return particle_type_; } @@ -197,9 +197,9 @@ class WeightWindows { int64_t index_; //!< Index into weight windows vector ParticleType particle_type_; //!< Particle type to apply weight windows to vector energy_bounds_; //!< Energy boundaries [eV] - xt::xtensor lower_ww_; //!< Lower weight window bounds (shape: + tensor::Tensor lower_ww_; //!< Lower weight window bounds (shape: //!< energy_bins, mesh_bins (k, j, i)) - xt::xtensor + tensor::Tensor upper_ww_; //!< Upper weight window bounds (shape: energy_bins, mesh_bins) double survival_ratio_ {3.0}; //!< Survival weight ratio double max_lb_ratio_ {1.0}; //!< Maximum lower bound to particle weight ratio diff --git a/include/openmc/wmp.h b/include/openmc/wmp.h index 052391f59c7..5cc04e59594 100644 --- a/include/openmc/wmp.h +++ b/include/openmc/wmp.h @@ -78,9 +78,9 @@ class WindowedMultipole { int fit_order_; //!< Order of the fit bool fissionable_; //!< Is the nuclide fissionable? vector window_info_; // Information about a window - xt::xtensor + tensor::Tensor curvefit_; // Curve fit coefficients (window, poly order, reaction) - xt::xtensor, 2> data_; //!< Poles and residues + tensor::Tensor> data_; //!< Poles and residues // Constant data static constexpr int MAX_POLY_COEFFICIENTS = diff --git a/include/openmc/xml_interface.h b/include/openmc/xml_interface.h index 603f456a27b..7910679cfd2 100644 --- a/include/openmc/xml_interface.h +++ b/include/openmc/xml_interface.h @@ -41,12 +41,11 @@ vector get_node_array( } template -xt::xarray get_node_xarray( +tensor::Tensor get_node_xarray( pugi::xml_node node, const char* name, bool lowercase = false) { vector v = get_node_array(node, name, lowercase); - vector shape = {v.size()}; - return xt::adapt(v, shape); + return tensor::Tensor(v); } std::vector get_node_position_array( diff --git a/include/openmc/xsdata.h b/include/openmc/xsdata.h index d66f4b3d045..c9dbde986b2 100644 --- a/include/openmc/xsdata.h +++ b/include/openmc/xsdata.h @@ -69,26 +69,26 @@ class XsData { public: // The following quantities have the following dimensions: // [angle][incoming group] - xt::xtensor total; - xt::xtensor absorption; - xt::xtensor nu_fission; - xt::xtensor prompt_nu_fission; - xt::xtensor kappa_fission; - xt::xtensor fission; - xt::xtensor inverse_velocity; + tensor::Tensor total; + tensor::Tensor absorption; + tensor::Tensor nu_fission; + tensor::Tensor prompt_nu_fission; + tensor::Tensor kappa_fission; + tensor::Tensor fission; + tensor::Tensor inverse_velocity; // decay_rate has the following dimensions: // [angle][delayed group] - xt::xtensor decay_rate; + tensor::Tensor decay_rate; // delayed_nu_fission has the following dimensions: // [angle][delayed group][incoming group] - xt::xtensor delayed_nu_fission; + tensor::Tensor delayed_nu_fission; // chi_prompt has the following dimensions: // [angle][incoming group][outgoing group] - xt::xtensor chi_prompt; + tensor::Tensor chi_prompt; // chi_delayed has the following dimensions: // [angle][incoming group][outgoing group][delayed group] - xt::xtensor chi_delayed; + tensor::Tensor chi_delayed; // scatter has the following dimensions: [angle] vector> scatter; diff --git a/src/bremsstrahlung.cpp b/src/bremsstrahlung.cpp index 5f8aee74cfd..ec1088101f0 100644 --- a/src/bremsstrahlung.cpp +++ b/src/bremsstrahlung.cpp @@ -16,8 +16,8 @@ namespace openmc { namespace data { -xt::xtensor ttb_e_grid; -xt::xtensor ttb_k_grid; +tensor::Tensor ttb_e_grid; +tensor::Tensor ttb_k_grid; vector ttb; } // namespace data diff --git a/src/cmfd_solver.cpp b/src/cmfd_solver.cpp index cbf89085ed7..17977e4fb68 100644 --- a/src/cmfd_solver.cpp +++ b/src/cmfd_solver.cpp @@ -36,7 +36,7 @@ double spectral; int nx, ny, nz, ng; -xt::xtensor indexmap; +tensor::Tensor indexmap; int use_all_threads; @@ -79,15 +79,14 @@ int get_cmfd_energy_bin(const double E) // COUNT_BANK_SITES bins fission sites according to CMFD mesh and energy //============================================================================== -xt::xtensor count_bank_sites( - xt::xtensor& bins, bool* outside) +tensor::Tensor count_bank_sites( + tensor::Tensor& bins, bool* outside) { // Determine shape of array for counts std::size_t cnt_size = cmfd::nx * cmfd::ny * cmfd::nz * cmfd::ng; - vector cnt_shape = {cnt_size}; // Create array of zeros - xt::xarray cnt {cnt_shape, 0.0}; + tensor::Tensor cnt({cnt_size}, 0.0); bool outside_ = false; auto bank_size = simulation::source_bank.size(); @@ -113,29 +112,22 @@ xt::xtensor count_bank_sites( bins[i] = mesh_bin * cmfd::ng + energy_bin; } - // Create copy of count data. Since ownership will be acquired by xtensor, - // std::allocator must be used to avoid Valgrind mismatched free() / delete - // warnings. int total = cnt.size(); - double* cnt_reduced = std::allocator {}.allocate(total); + tensor::Tensor counts({cnt_size}, 0.0); #ifdef OPENMC_MPI // collect values from all processors MPI_Reduce( - cnt.data(), cnt_reduced, total, MPI_DOUBLE, MPI_SUM, 0, mpi::intracomm); + cnt.data(), counts.data(), total, MPI_DOUBLE, MPI_SUM, 0, mpi::intracomm); // Check if there were sites outside the mesh for any processor MPI_Reduce(&outside_, outside, 1, MPI_C_BOOL, MPI_LOR, 0, mpi::intracomm); #else - std::copy(cnt.data(), cnt.data() + total, cnt_reduced); + std::copy(cnt.data(), cnt.data() + total, counts.data()); *outside = outside_; #endif - // Adapt reduced values in array back into an xarray - auto arr = xt::adapt(cnt_reduced, total, xt::acquire_ownership(), cnt_shape); - xt::xarray counts = arr; - return counts; } @@ -151,19 +143,19 @@ extern "C" void openmc_cmfd_reweight( std::size_t src_size = cmfd::nx * cmfd::ny * cmfd::nz * cmfd::ng; // count bank sites for CMFD mesh, store bins in bank_bins for reweighting - xt::xtensor bank_bins({bank_size}, 0); + tensor::Tensor bank_bins({bank_size}, 0); bool sites_outside; - xt::xtensor sourcecounts = + tensor::Tensor sourcecounts = count_bank_sites(bank_bins, &sites_outside); // Compute CMFD weightfactors - xt::xtensor weightfactors = xt::xtensor({src_size}, 1.); + tensor::Tensor weightfactors({src_size}, 1.); if (mpi::master) { if (sites_outside) { fatal_error("Source sites outside of the CMFD mesh"); } - double norm = xt::sum(sourcecounts)() / cmfd::norm; + double norm = sourcecounts.sum() / cmfd::norm; for (int i = 0; i < src_size; i++) { if (sourcecounts[i] > 0 && cmfd_src[i] > 0) { weightfactors[i] = cmfd_src[i] * norm / sourcecounts[i]; diff --git a/src/cross_sections.cpp b/src/cross_sections.cpp index b1bfde03d13..ec9da5b8abc 100644 --- a/src/cross_sections.cpp +++ b/src/cross_sections.cpp @@ -254,7 +254,7 @@ void read_ce_cross_sections(const vector>& nuc_temps, if (settings::photon_transport && settings::electron_treatment == ElectronTreatment::TTB) { // Take logarithm of energies since they are log-log interpolated - data::ttb_e_grid = xt::log(data::ttb_e_grid); + data::ttb_e_grid = tensor::log(data::ttb_e_grid); } // Show minimum/maximum temperature diff --git a/src/distribution_angle.cpp b/src/distribution_angle.cpp index 3f436f5058c..7b99727faf2 100644 --- a/src/distribution_angle.cpp +++ b/src/distribution_angle.cpp @@ -29,7 +29,7 @@ AngleDistribution::AngleDistribution(hid_t group) hid_t dset = open_dataset(group, "mu"); read_attribute(dset, "offsets", offsets); read_attribute(dset, "interpolation", interp); - xt::xarray temp; + tensor::Tensor temp; read_dataset(dset, temp); close_dataset(dset); @@ -40,13 +40,13 @@ AngleDistribution::AngleDistribution(hid_t group) if (i < n_energy - 1) { n = offsets[i + 1] - j; } else { - n = temp.shape()[1] - j; + n = temp.shape(1) - j; } // Create and initialize tabular distribution - auto xs = xt::view(temp, 0, xt::range(j, j + n)); - auto ps = xt::view(temp, 1, xt::range(j, j + n)); - auto cs = xt::view(temp, 2, xt::range(j, j + n)); + auto xs = temp.row(0).slice(j, j + n); + auto ps = temp.row(1).slice(j, j + n); + auto cs = temp.row(2).slice(j, j + n); vector x {xs.begin(), xs.end()}; vector p {ps.begin(), ps.end()}; vector c {cs.begin(), cs.end()}; diff --git a/src/distribution_energy.cpp b/src/distribution_energy.cpp index 19d7944f6e6..ff32b57e85d 100644 --- a/src/distribution_energy.cpp +++ b/src/distribution_energy.cpp @@ -60,11 +60,11 @@ ContinuousTabular::ContinuousTabular(hid_t group) hid_t dset = open_dataset(group, "energy"); // Get interpolation parameters - xt::xarray temp; + tensor::Tensor 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 + auto temp_b = temp.row(0); // view of breakpoints + auto temp_i = temp.row(1); // view of interpolation parameters std::copy(temp_b.begin(), temp_b.end(), std::back_inserter(breakpoints_)); for (const auto i : temp_i) @@ -85,7 +85,7 @@ ContinuousTabular::ContinuousTabular(hid_t group) read_attribute(dset, "interpolation", interp); read_attribute(dset, "n_discrete_lines", n_discrete); - xt::xarray eout; + tensor::Tensor eout; read_dataset(dset, eout); close_dataset(dset); @@ -96,7 +96,7 @@ ContinuousTabular::ContinuousTabular(hid_t group) if (i < n_energy - 1) { n = offsets[i + 1] - j; } else { - n = eout.shape()[1] - j; + n = eout.shape(1) - j; } // Assign interpolation scheme and number of discrete lines @@ -105,15 +105,15 @@ ContinuousTabular::ContinuousTabular(hid_t group) d.n_discrete = n_discrete[i]; // Copy data - d.e_out = xt::view(eout, 0, xt::range(j, j + n)); - d.p = xt::view(eout, 1, xt::range(j, j + n)); + d.e_out = eout.row(0).slice(j, j + n); + d.p = eout.row(1).slice(j, j + n); // To get answers that match ACE data, for now we still use the tabulated // CDF values that were passed through to the HDF5 library. At a later // time, we can remove the CDF values from the HDF5 library and // reconstruct them using the PDF if (true) { - d.c = xt::view(eout, 2, xt::range(j, j + n)); + d.c = eout.row(2).slice(j, j + n); } else { // Calculate cumulative distribution function -- discrete portion for (int k = 0; k < d.n_discrete; ++k) { diff --git a/src/eigenvalue.cpp b/src/eigenvalue.cpp index ac5b6f84f5d..bc8dcc9ea77 100644 --- a/src/eigenvalue.cpp +++ b/src/eigenvalue.cpp @@ -36,7 +36,7 @@ namespace simulation { double keff_generation; array k_sum; vector entropy; -xt::xtensor source_frac; +tensor::Tensor source_frac; } // namespace simulation @@ -449,7 +449,7 @@ int openmc_get_keff(double* k_combined) const auto& gt = simulation::global_tallies; array kv {}; - xt::xtensor cov = xt::zeros({3, 3}); + tensor::Tensor cov = tensor::zeros({3, 3}); kv[0] = gt(GlobalTally::K_COLLISION, TallyResult::SUM) / n; kv[1] = gt(GlobalTally::K_ABSORPTION, TallyResult::SUM) / n; kv[2] = gt(GlobalTally::K_TRACKLENGTH, TallyResult::SUM) / n; @@ -588,7 +588,7 @@ void shannon_entropy() { // Get source weight in each mesh bin bool sites_outside; - xt::xtensor p = + tensor::Tensor p = simulation::entropy_mesh->count_sites(simulation::fission_bank.data(), simulation::fission_bank.size(), &sites_outside); @@ -600,7 +600,7 @@ void shannon_entropy() if (mpi::master) { // Normalize to total weight of bank sites - p /= xt::sum(p); + p /= p.sum(); // Sum values to obtain Shannon entropy double H = 0.0; @@ -624,7 +624,7 @@ void ufs_count_sites() std::size_t n = simulation::ufs_mesh->n_bins(); double vol_frac = simulation::ufs_mesh->volume_frac_; - simulation::source_frac = xt::xtensor({n}, vol_frac); + simulation::source_frac = tensor::Tensor({n}, vol_frac); } else { // count number of source sites in each ufs mesh cell @@ -646,7 +646,7 @@ void ufs_count_sites() #endif // Normalize to total weight to get fraction of source in each cell - double total = xt::sum(simulation::source_frac)(); + double total = simulation::source_frac.sum(); simulation::source_frac /= total; // Since the total starting weight is not equal to n_particles, we need to diff --git a/src/endf.cpp b/src/endf.cpp index 72b7c7cc2a3..f793cc245f4 100644 --- a/src/endf.cpp +++ b/src/endf.cpp @@ -152,11 +152,11 @@ Tabulated1D::Tabulated1D(hid_t dset) for (const auto i : int_temp) int_.push_back(int2interp(i)); - xt::xarray arr; + tensor::Tensor arr; read_dataset(dset, arr); - auto xs = xt::view(arr, 0); - auto ys = xt::view(arr, 1); + auto xs = arr.row(0); + auto ys = arr.row(1); std::copy(xs.begin(), xs.end(), std::back_inserter(x_)); std::copy(ys.begin(), ys.end(), std::back_inserter(y_)); @@ -228,12 +228,12 @@ double Tabulated1D::operator()(double x) const CoherentElasticXS::CoherentElasticXS(hid_t dset) { // Read 2D array from dataset - xt::xarray arr; + tensor::Tensor arr; read_dataset(dset, arr); // Get views for Bragg edges and structure factors - auto E = xt::view(arr, 0); - auto s = xt::view(arr, 1); + auto E = arr.row(0); + auto s = arr.row(1); // Copy Bragg edges and partial sums of structure factors std::copy(E.begin(), E.end(), std::back_inserter(bragg_edges_)); diff --git a/src/finalize.cpp b/src/finalize.cpp index fa1f2e3ebf5..4117167b4a4 100644 --- a/src/finalize.cpp +++ b/src/finalize.cpp @@ -200,7 +200,7 @@ int openmc_reset() // Reset global tallies simulation::n_realizations = 0; - xt::view(simulation::global_tallies, xt::all()) = 0.0; + simulation::global_tallies.fill(0.0); simulation::k_col_abs = 0.0; simulation::k_col_tra = 0.0; diff --git a/src/hdf5_interface.cpp b/src/hdf5_interface.cpp index 23905e17b8b..a1a59b8dfe0 100644 --- a/src/hdf5_interface.cpp +++ b/src/hdf5_interface.cpp @@ -465,22 +465,18 @@ void read_dataset_lowlevel(hid_t obj_id, const char* name, hid_t mem_type_id, } template<> -void read_dataset(hid_t dset, xt::xarray>& arr, bool indep) +void read_dataset( + hid_t dset, tensor::Tensor>& arr, bool indep) { // Get shape of dataset vector shape = object_shape(dset); - // Allocate new array to read data into - std::size_t size = 1; - for (const auto x : shape) - size *= x; - vector> buffer(size); + // Resize tensor and read data directly + openmc::vector tshape(shape.begin(), shape.end()); + arr.resize(tshape); - // Read data from attribute - read_complex(dset, nullptr, buffer.data(), indep); - - // Adapt into xarray - arr = xt::adapt(buffer, shape); + // Read data from dataset + read_complex(dset, nullptr, reinterpret_cast*>(arr.data()), indep); } void read_double(hid_t obj_id, const char* name, double* buffer, bool indep) diff --git a/src/material.cpp b/src/material.cpp index 944fa87f5ed..530807248eb 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -214,7 +214,7 @@ Material::Material(pugi::xml_node node) // allocate arrays in Material object auto n = names.size(); nuclide_.reserve(n); - atom_density_ = xt::empty({n}); + atom_density_ = tensor::Tensor({n}); if (settings::photon_transport) element_.reserve(n); @@ -288,14 +288,14 @@ Material::Material(pugi::xml_node node) // Check to make sure either all atom percents or all weight percents are // given - if (!(xt::all(atom_density_ >= 0.0) || xt::all(atom_density_ <= 0.0))) { + if (!((atom_density_ >= 0.0).all() || (atom_density_ <= 0.0).all())) { fatal_error( "Cannot mix atom and weight percents in material " + std::to_string(id_)); } // Determine density if it is a sum value if (sum_density) - density_ = xt::sum(atom_density_)(); + density_ = atom_density_.sum(); if (check_for_node(node, "temperature")) { temperature_ = std::stod(get_node_value(node, "temperature")); @@ -433,7 +433,7 @@ void Material::normalize_density() // determine normalized atom percents. if given atom percents, this is // straightforward. if given weight percents, the value is w/awr and is // divided by sum(w/awr) - atom_density_ /= xt::sum(atom_density_)(); + atom_density_ /= atom_density_.sum(); // Change density in g/cm^3 to atom/b-cm. Since all values are now in // atom percent, the sum needs to be re-evaluated as 1/sum(x*awr) @@ -639,14 +639,14 @@ void Material::init_bremsstrahlung() bool positron = (particle == 1); // Allocate arrays for TTB data - ttb->pdf = xt::zeros({n_e, n_e}); - ttb->cdf = xt::zeros({n_e, n_e}); - ttb->yield = xt::zeros({n_e}); + ttb->pdf = tensor::zeros({n_e, n_e}); + ttb->cdf = tensor::zeros({n_e, n_e}); + ttb->yield = tensor::zeros({n_e}); // Allocate temporary arrays - xt::xtensor stopping_power_collision({n_e}, 0.0); - xt::xtensor stopping_power_radiative({n_e}, 0.0); - xt::xtensor dcs({n_e, n_k}, 0.0); + tensor::Tensor stopping_power_collision({n_e}, 0.0); + tensor::Tensor stopping_power_radiative({n_e}, 0.0); + tensor::Tensor dcs({n_e, n_k}, 0.0); double Z_eq_sq = 0.0; double sum_density = 0.0; @@ -696,18 +696,18 @@ void Material::init_bremsstrahlung() 1.0595e-3 * std::pow(t, 5) + 7.0568e-5 * std::pow(t, 6) - 1.808e-6 * std::pow(t, 7)); stopping_power_radiative(i) *= r; - auto dcs_i = xt::view(dcs, i, xt::all()); + auto dcs_i = dcs.row(i); dcs_i *= r; } } // Total material stopping power - xt::xtensor stopping_power = + tensor::Tensor stopping_power = stopping_power_collision + stopping_power_radiative; // Loop over photon energies - xt::xtensor f({n_e}, 0.0); - xt::xtensor z({n_e}, 0.0); + tensor::Tensor f({n_e}, 0.0); + tensor::Tensor z({n_e}, 0.0); for (int i = 0; i < n_e - 1; ++i) { double w = data::ttb_e_grid(i); @@ -795,7 +795,7 @@ void Material::init_bremsstrahlung() } // Use logarithm of number yield since it is log-log interpolated - ttb->yield = xt::where(ttb->yield > 0.0, xt::log(ttb->yield), -500.0); + ttb->yield = tensor::where(ttb->yield > 0.0, tensor::log(ttb->yield), -500.0); } } @@ -977,7 +977,7 @@ void Material::set_density(double density, const std::string& units) density_ = density; // Determine normalized atom percents - double sum_percent = xt::sum(atom_density_)(); + double sum_percent = atom_density_.sum(); atom_density_ /= sum_percent; // Recalculate nuclide atom densities based on given density @@ -1018,7 +1018,7 @@ void Material::set_densities( if (n != nuclide_.size()) { nuclide_.resize(n); - atom_density_ = xt::zeros({n}); + atom_density_ = tensor::zeros({n}); if (settings::photon_transport) element_.resize(n); } @@ -1179,8 +1179,8 @@ void Material::add_nuclide(const std::string& name, double density) auto n = nuclide_.size(); // Create copy of atom_density_ array with one extra entry - xt::xtensor atom_density = xt::zeros({n}); - xt::view(atom_density, xt::range(0, n - 1)) = atom_density_; + tensor::Tensor atom_density = tensor::zeros({n}); + atom_density.slice(0, n - 1) = atom_density_; atom_density(n - 1) = density; atom_density_ = atom_density; diff --git a/src/mesh.cpp b/src/mesh.cpp index 737986f87af..d60cc6ddc6d 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -766,11 +766,9 @@ std::string StructuredMesh::bin_label(int bin) const } } -xt::xtensor StructuredMesh::get_x_shape() const +tensor::Tensor StructuredMesh::get_x_shape() const { - // because method is const, shape_ is const as well and can't be adapted - auto tmp_shape = shape_; - return xt::adapt(tmp_shape, {n_dimension_}); + return tensor::Tensor(shape_.data(), static_cast(n_dimension_)); } Position StructuredMesh::sample_element( @@ -955,10 +953,10 @@ void UnstructuredMesh::to_hdf5_inner(hid_t mesh_group) const write_dataset(mesh_group, "length_multiplier", length_multiplier_); // write vertex coordinates - xt::xtensor vertices({static_cast(this->n_vertices()), 3}); + tensor::Tensor vertices({static_cast(this->n_vertices()), static_cast(3)}); for (int i = 0; i < this->n_vertices(); i++) { auto v = this->vertex(i); - xt::view(vertices, i, xt::all()) = xt::xarray({v.x, v.y, v.z}); + vertices.row(i) = {v.x, v.y, v.z}; } write_dataset(mesh_group, "vertices", vertices); @@ -966,8 +964,8 @@ void UnstructuredMesh::to_hdf5_inner(hid_t mesh_group) const // write element types and connectivity vector volumes; - xt::xtensor connectivity({static_cast(this->n_bins()), 8}); - xt::xtensor elem_types({static_cast(this->n_bins()), 1}); + tensor::Tensor connectivity({static_cast(this->n_bins()), static_cast(8)}); + tensor::Tensor elem_types({static_cast(this->n_bins()), static_cast(1)}); for (int i = 0; i < this->n_bins(); i++) { auto conn = this->connectivity(i); @@ -975,21 +973,18 @@ void UnstructuredMesh::to_hdf5_inner(hid_t mesh_group) const // write linear tet element if (conn.size() == 4) { - xt::view(elem_types, i, xt::all()) = - static_cast(ElementType::LINEAR_TET); - xt::view(connectivity, i, xt::all()) = - xt::xarray({conn[0], conn[1], conn[2], conn[3], -1, -1, -1, -1}); + elem_types.row(i) = static_cast(ElementType::LINEAR_TET); + connectivity.row(i) = + {conn[0], conn[1], conn[2], conn[3], -1, -1, -1, -1}; // write linear hex element } else if (conn.size() == 8) { - xt::view(elem_types, i, xt::all()) = - static_cast(ElementType::LINEAR_HEX); - xt::view(connectivity, i, xt::all()) = xt::xarray({conn[0], conn[1], - conn[2], conn[3], conn[4], conn[5], conn[6], conn[7]}); + elem_types.row(i) = static_cast(ElementType::LINEAR_HEX); + connectivity.row(i) = {conn[0], conn[1], + conn[2], conn[3], conn[4], conn[5], conn[6], conn[7]}; } else { num_elem_skipped++; - xt::view(elem_types, i, xt::all()) = - static_cast(ElementType::UNSUPPORTED); - xt::view(connectivity, i, xt::all()) = -1; + elem_types.row(i) = static_cast(ElementType::UNSUPPORTED); + connectivity.row(i) = -1; } } @@ -1090,7 +1085,7 @@ int StructuredMesh::n_surface_bins() const return 4 * n_dimension_ * n_bins(); } -xt::xtensor StructuredMesh::count_sites( +tensor::Tensor StructuredMesh::count_sites( const SourceSite* bank, int64_t length, bool* outside) const { // Determine shape of array for counts @@ -1098,7 +1093,7 @@ xt::xtensor StructuredMesh::count_sites( vector shape = {m}; // Create array of zeros - xt::xarray cnt {shape, 0.0}; + tensor::Tensor cnt(shape, 0.0); bool outside_ = false; for (int64_t i = 0; i < length; i++) { @@ -1117,31 +1112,25 @@ xt::xtensor StructuredMesh::count_sites( cnt(mesh_bin) += site.wgt; } - // Create copy of count data. Since ownership will be acquired by xtensor, - // std::allocator must be used to avoid Valgrind mismatched free() / delete - // warnings. + // Create reduced count data + tensor::Tensor counts(shape, 0.0); int total = cnt.size(); - double* cnt_reduced = std::allocator {}.allocate(total); #ifdef OPENMC_MPI // collect values from all processors MPI_Reduce( - cnt.data(), cnt_reduced, total, MPI_DOUBLE, MPI_SUM, 0, mpi::intracomm); + cnt.data(), counts.data(), total, MPI_DOUBLE, MPI_SUM, 0, mpi::intracomm); // Check if there were sites outside the mesh for any processor if (outside) { MPI_Reduce(&outside_, outside, 1, MPI_C_BOOL, MPI_LOR, 0, mpi::intracomm); } #else - std::copy(cnt.data(), cnt.data() + total, cnt_reduced); + std::copy(cnt.data(), cnt.data() + total, counts.data()); if (outside) *outside = outside_; #endif - // Adapt reduced values in array back into an xarray - auto arr = xt::adapt(cnt_reduced, total, xt::acquire_ownership(), shape); - xt::xarray counts = arr; - return counts; } @@ -1334,10 +1323,10 @@ void StructuredMesh::surface_bins_crossed( int RegularMesh::set_grid() { - auto shape = xt::adapt(shape_, {n_dimension_}); + tensor::Tensor shape(shape_.data(), static_cast(n_dimension_)); // Check that dimensions are all greater than zero - if (xt::any(shape <= 0)) { + if ((shape <= 0).any()) { set_errmsg("All entries for a regular mesh dimensions " "must be positive."); return OPENMC_E_INVALID_ARGUMENT; @@ -1359,13 +1348,13 @@ int RegularMesh::set_grid() } // Check for negative widths - if (xt::any(width_ < 0.0)) { + if ((width_ < 0.0).any()) { set_errmsg("Cannot have a negative width on a regular mesh."); return OPENMC_E_INVALID_ARGUMENT; } // Set width and upper right coordinate - upper_right_ = xt::eval(lower_left_ + shape * width_); + upper_right_ = lower_left_ + shape * width_; } else if (upper_right_.size() > 0) { @@ -1377,7 +1366,7 @@ int RegularMesh::set_grid() } // Check that upper-right is above lower-left - if (xt::any(upper_right_ < lower_left_)) { + if ((upper_right_ < lower_left_).any()) { set_errmsg( "The upper_right coordinates of a regular mesh must be greater than " "the lower_left coordinates."); @@ -1385,11 +1374,11 @@ int RegularMesh::set_grid() } // Set width - width_ = xt::eval((upper_right_ - lower_left_) / shape); + width_ = (upper_right_ - lower_left_) / shape; } // Set material volumes - volume_frac_ = 1.0 / xt::prod(shape)(); + volume_frac_ = 1.0 / shape.prod(); element_volume_ = 1.0; for (int i = 0; i < n_dimension_; i++) { @@ -1405,7 +1394,7 @@ RegularMesh::RegularMesh(pugi::xml_node node) : StructuredMesh {node} fatal_error("Must specify on a regular mesh."); } - xt::xtensor shape = get_node_xarray(node, "dimension"); + tensor::Tensor shape = get_node_xarray(node, "dimension"); int n = n_dimension_ = shape.size(); if (n != 1 && n != 2 && n != 3) { fatal_error("Mesh must be one, two, or three dimensions."); @@ -1448,7 +1437,7 @@ RegularMesh::RegularMesh(hid_t group) : StructuredMesh {group} fatal_error("Must specify on a regular mesh."); } - xt::xtensor shape; + tensor::Tensor shape; read_dataset(group, "dimension", shape); int n = n_dimension_ = shape.size(); if (n != 1 && n != 2 && n != 3) { @@ -1569,7 +1558,7 @@ void RegularMesh::to_hdf5_inner(hid_t mesh_group) const write_dataset(mesh_group, "width", width_); } -xt::xtensor RegularMesh::count_sites( +tensor::Tensor RegularMesh::count_sites( const SourceSite* bank, int64_t length, bool* outside) const { // Determine shape of array for counts @@ -1577,7 +1566,7 @@ xt::xtensor RegularMesh::count_sites( vector shape = {m}; // Create array of zeros - xt::xarray cnt {shape, 0.0}; + tensor::Tensor cnt(shape, 0.0); bool outside_ = false; for (int64_t i = 0; i < length; i++) { @@ -1596,31 +1585,25 @@ xt::xtensor RegularMesh::count_sites( cnt(mesh_bin) += site.wgt; } - // Create copy of count data. Since ownership will be acquired by xtensor, - // std::allocator must be used to avoid Valgrind mismatched free() / delete - // warnings. + // Create reduced count data + tensor::Tensor counts(shape, 0.0); int total = cnt.size(); - double* cnt_reduced = std::allocator {}.allocate(total); #ifdef OPENMC_MPI // collect values from all processors MPI_Reduce( - cnt.data(), cnt_reduced, total, MPI_DOUBLE, MPI_SUM, 0, mpi::intracomm); + cnt.data(), counts.data(), total, MPI_DOUBLE, MPI_SUM, 0, mpi::intracomm); // Check if there were sites outside the mesh for any processor if (outside) { MPI_Reduce(&outside_, outside, 1, MPI_C_BOOL, MPI_LOR, 0, mpi::intracomm); } #else - std::copy(cnt.data(), cnt.data() + total, cnt_reduced); + std::copy(cnt.data(), cnt.data() + total, counts.data()); if (outside) *outside = outside_; #endif - // Adapt reduced values in array back into an xarray - auto arr = xt::adapt(cnt_reduced, total, xt::acquire_ownership(), shape); - xt::xarray counts = arr; - return counts; } @@ -2690,7 +2673,7 @@ extern "C" int openmc_regular_mesh_get_params( return err; RegularMesh* m = dynamic_cast(model::meshes[index].get()); - if (m->lower_left_.dimension() == 0) { + if (m->lower_left_.empty()) { set_errmsg("Mesh parameters have not been set."); return OPENMC_E_ALLOCATE; } @@ -2717,16 +2700,16 @@ extern "C" int openmc_regular_mesh_set_params( vector shape = {static_cast(n)}; if (ll && ur) { - m->lower_left_ = xt::adapt(ll, n, xt::no_ownership(), shape); - m->upper_right_ = xt::adapt(ur, n, xt::no_ownership(), shape); + m->lower_left_ = tensor::Tensor(ll, n); + m->upper_right_ = tensor::Tensor(ur, n); m->width_ = (m->upper_right_ - m->lower_left_) / m->get_x_shape(); } else if (ll && width) { - m->lower_left_ = xt::adapt(ll, n, xt::no_ownership(), shape); - m->width_ = xt::adapt(width, n, xt::no_ownership(), shape); + m->lower_left_ = tensor::Tensor(ll, n); + m->width_ = tensor::Tensor(width, n); m->upper_right_ = m->lower_left_ + m->get_x_shape() * m->width_; } else if (ur && width) { - m->upper_right_ = xt::adapt(ur, n, xt::no_ownership(), shape); - m->width_ = xt::adapt(width, n, xt::no_ownership(), shape); + m->upper_right_ = tensor::Tensor(ur, n); + m->width_ = tensor::Tensor(width, n); m->lower_left_ = m->upper_right_ - m->get_x_shape() * m->width_; } else { set_errmsg("At least two parameters must be specified."); @@ -2737,7 +2720,7 @@ extern "C" int openmc_regular_mesh_set_params( // TODO: incorporate this into method in RegularMesh that can be called from // here and from constructor - m->volume_frac_ = 1.0 / xt::prod(m->get_x_shape())(); + m->volume_frac_ = 1.0 / m->get_x_shape().prod(); m->element_volume_ = 1.0; for (int i = 0; i < m->n_dimension_; i++) { m->element_volume_ *= m->width_[i]; @@ -2786,7 +2769,7 @@ int openmc_structured_mesh_get_grid_impl(int32_t index, double** grid_x, return err; C* m = dynamic_cast(model::meshes[index].get()); - if (m->lower_left_.dimension() == 0) { + if (m->lower_left_.empty()) { set_errmsg("Mesh parameters have not been set."); return OPENMC_E_ALLOCATE; } diff --git a/src/mgxs.cpp b/src/mgxs.cpp index 01a77d06615..d7f96c62b7d 100644 --- a/src/mgxs.cpp +++ b/src/mgxs.cpp @@ -29,8 +29,8 @@ void Mgxs::init(const std::string& in_name, double in_awr, // Set the metadata name = in_name; awr = in_awr; - // TODO: Remove adapt when in_KTs is an xtensor - kTs = xt::adapt(in_kTs); + // TODO: Remove adapt when in_KTs is a Tensor + kTs = tensor::Tensor(in_kTs); fissionable = in_fissionable; scatter_format = in_scatter_format; xs.resize(in_kTs.size()); @@ -69,7 +69,7 @@ void Mgxs::metadata_from_hdf5(hid_t xs_id, const vector& temperature, } get_datasets(kT_group, dset_names); vector shape = {num_temps}; - xt::xarray temps_available(shape); + tensor::Tensor temps_available(shape); for (int i = 0; i < num_temps; i++) { read_double(kT_group, dset_names[i], &temps_available[i], true); @@ -97,10 +97,8 @@ void Mgxs::metadata_from_hdf5(hid_t xs_id, const vector& temperature, // Determine actual temperatures to read for (const auto& T : temperature) { // Determine the closest temperature value - // NOTE: the below block could be replaced with the following line, - // though this gives a runtime error if using LLVM 20 or newer, - // likely due to a bug in xtensor. - // auto i_closest = xt::argmin(xt::abs(temps_available - T))[0]; + // NOTE: the below block could be replaced with the following line: + // auto i_closest = tensor::abs(temps_available - T).argmin(); double closest = std::numeric_limits::max(); int i_closest = 0; for (int i = 0; i < temps_available.size(); i++) { @@ -344,7 +342,7 @@ Mgxs::Mgxs(const std::string& in_name, const vector& mat_kTs, for (int m = 0; m < micros.size(); m++) { switch (settings::temperature_method) { case TemperatureMethod::NEAREST: { - micro_t[m] = xt::argmin(xt::abs(micros[m]->kTs - temp_desired))[0]; + micro_t[m] = tensor::abs(micros[m]->kTs - temp_desired).argmin(); auto temp_actual = micros[m]->kTs[micro_t[m]]; if (std::abs(temp_actual - temp_desired) >= @@ -357,7 +355,7 @@ Mgxs::Mgxs(const std::string& in_name, const vector& mat_kTs, case TemperatureMethod::INTERPOLATION: // Get a list of bounding temperatures for each actual temperature // present in the model - for (int k = 0; k < micros[m]->kTs.shape()[0] - 1; k++) { + for (int k = 0; k < micros[m]->kTs.shape(0) - 1; k++) { if ((micros[m]->kTs[k] <= temp_desired) && (temp_desired < micros[m]->kTs[k + 1])) { micro_t[m] = k; @@ -463,7 +461,7 @@ double Mgxs::get_xs(MgxsType xstype, int gin, const int* gout, const double* mu, val = xs_t->delayed_nu_fission(a, *dg, gin); } else { val = 0.; - for (int d = 0; d < xs_t->delayed_nu_fission.shape()[1]; d++) { + for (int d = 0; d < xs_t->delayed_nu_fission.shape(1); d++) { val += xs_t->delayed_nu_fission(a, d, gin); } } @@ -478,7 +476,7 @@ double Mgxs::get_xs(MgxsType xstype, int gin, const int* gout, const double* mu, } else { // provide an outgoing group-wise sum val = 0.; - for (int g = 0; g < xs_t->chi_prompt.shape()[2]; g++) { + for (int g = 0; g < xs_t->chi_prompt.shape(2); g++) { val += xs_t->chi_prompt(a, gin, g); } } @@ -497,13 +495,13 @@ double Mgxs::get_xs(MgxsType xstype, int gin, const int* gout, const double* mu, } else { if (dg != nullptr) { val = 0.; - for (int g = 0; g < xs_t->delayed_nu_fission.shape()[2]; g++) { + for (int g = 0; g < xs_t->delayed_nu_fission.shape(2); g++) { val += xs_t->delayed_nu_fission(a, *dg, gin, g); } } else { val = 0.; - for (int g = 0; g < xs_t->delayed_nu_fission.shape()[2]; g++) { - for (int d = 0; d < xs_t->delayed_nu_fission.shape()[3]; d++) { + for (int g = 0; g < xs_t->delayed_nu_fission.shape(2); g++) { + for (int d = 0; d < xs_t->delayed_nu_fission.shape(3); d++) { val += xs_t->delayed_nu_fission(a, d, gin, g); } } @@ -639,7 +637,7 @@ bool Mgxs::equiv(const Mgxs& that) int Mgxs::get_temperature_index(double sqrtkT) const { - return xt::argmin(xt::abs(kTs - sqrtkT * sqrtkT))[0]; + return tensor::abs(kTs - sqrtkT * sqrtkT).argmin(); } //============================================================================== diff --git a/src/nuclide.cpp b/src/nuclide.cpp index 61e64571785..0c560437a6f 100644 --- a/src/nuclide.cpp +++ b/src/nuclide.cpp @@ -360,8 +360,8 @@ void Nuclide::create_derived( { for (const auto& grid : grid_) { // Allocate and initialize cross section - array shape {grid.energy.size(), 5}; - xs_.emplace_back(shape, 0.0); + xs_.emplace_back( + openmc::vector{grid.energy.size(), 5}, 0.0); } reaction_index_.fill(C_NONE); @@ -374,9 +374,7 @@ void Nuclide::create_derived( for (int t = 0; t < kTs_.size(); ++t) { int j = rx->xs_[t].threshold; int n = rx->xs_[t].value.size(); - auto xs = xt::adapt(rx->xs_[t].value); - auto pprod = xt::view(xs_[t], xt::range(j, j + n), XS_PHOTON_PROD); - + auto xs = tensor::Tensor(rx->xs_[t].value); for (const auto& p : rx->products_) { if (p.particle_.is_photon()) { for (int k = 0; k < n; ++k) { @@ -395,7 +393,7 @@ void Nuclide::create_derived( } } - pprod[k] += f * xs[k] * (*p.yield_)(E); + xs_[t](j + k, XS_PHOTON_PROD) += f * xs[k] * (*p.yield_)(E); } } } @@ -405,20 +403,21 @@ void Nuclide::create_derived( continue; // Add contribution to total cross section - auto total = xt::view(xs_[t], xt::range(j, j + n), XS_TOTAL); - total += xs; + for (int k = 0; k < n; ++k) + xs_[t](j + k, XS_TOTAL) += xs[k]; // Add contribution to absorption cross section - auto absorption = xt::view(xs_[t], xt::range(j, j + n), XS_ABSORPTION); if (is_disappearance(rx->mt_)) { - absorption += xs; + for (int k = 0; k < n; ++k) + xs_[t](j + k, XS_ABSORPTION) += xs[k]; } if (is_fission(rx->mt_)) { fissionable_ = true; - auto fission = xt::view(xs_[t], xt::range(j, j + n), XS_FISSION); - fission += xs; - absorption += xs; + for (int k = 0; k < n; ++k) + xs_[t](j + k, XS_FISSION) += xs[k]; + for (int k = 0; k < n; ++k) + xs_[t](j + k, XS_ABSORPTION) += xs[k]; // Keep track of fission reactions if (t == 0) { @@ -509,7 +508,7 @@ void Nuclide::init_grid() double spacing = std::log(E_max / E_min) / M; // Create equally log-spaced energy grid - auto umesh = xt::linspace(0.0, M * spacing, M + 1); + auto umesh = tensor::linspace(0.0, M * spacing, M + 1); for (auto& grid : grid_) { // Resize array for storing grid indices diff --git a/src/photon.cpp b/src/photon.cpp index 9ad282e78db..8ede33b7439 100644 --- a/src/photon.cpp +++ b/src/photon.cpp @@ -29,7 +29,7 @@ constexpr int PhotonInteraction::MAX_STACK_SIZE; namespace data { -xt::xtensor compton_profile_pz; +tensor::Tensor compton_profile_pz; std::unordered_map element_map; vector> elements; @@ -42,8 +42,6 @@ vector> elements; PhotonInteraction::PhotonInteraction(hid_t group) { - using namespace xt::placeholders; - // Set index of element in global vector index_ = data::elements.size(); @@ -92,7 +90,7 @@ PhotonInteraction::PhotonInteraction(hid_t group) read_dataset(rgroup, "xs", pair_production_electron_); close_group(rgroup); } else { - pair_production_electron_ = xt::zeros_like(energy_); + pair_production_electron_ = tensor::zeros_like(energy_); } // Read pair production @@ -101,7 +99,7 @@ PhotonInteraction::PhotonInteraction(hid_t group) read_dataset(rgroup, "xs", pair_production_nuclear_); close_group(rgroup); } else { - pair_production_nuclear_ = xt::zeros_like(energy_); + pair_production_nuclear_ = tensor::zeros_like(energy_); } // Read photoelectric @@ -115,7 +113,7 @@ PhotonInteraction::PhotonInteraction(hid_t group) read_dataset(rgroup, "xs", heating_); close_group(rgroup); } else { - heating_ = xt::zeros_like(energy_); + heating_ = tensor::zeros_like(energy_); } // Read subshell photoionization cross section and atomic relaxation data @@ -129,7 +127,7 @@ PhotonInteraction::PhotonInteraction(hid_t group) } shells_.resize(n_shell); - cross_sections_ = xt::zeros({energy_.size(), n_shell}); + cross_sections_ = tensor::zeros({energy_.size(), n_shell}); // Create mapping from designator to index std::unordered_map shell_map; @@ -164,15 +162,15 @@ PhotonInteraction::PhotonInteraction(hid_t group) } // Read subshell cross section - xt::xtensor xs; + tensor::Tensor xs; dset = open_dataset(tgroup, "xs"); read_attribute(dset, "threshold_idx", shell.threshold); close_dataset(dset); read_dataset(tgroup, "xs", xs); auto cross_section = - xt::view(cross_sections_, xt::range(shell.threshold, _), i); - cross_section = xt::where(xs > 0, xt::log(xs), 0); + cross_sections_.col(i).slice(static_cast(shell.threshold)); + cross_section = tensor::where(xs > 0, tensor::log(xs), 0); if (object_exists(tgroup, "transitions")) { // Determine dimensions of transitions @@ -182,11 +180,11 @@ PhotonInteraction::PhotonInteraction(hid_t group) int n_transition = dims[0]; if (n_transition > 0) { - xt::xtensor matrix; + tensor::Tensor matrix; read_dataset(tgroup, "transitions", matrix); // Transition probability normalization - double norm = xt::sum(xt::col(matrix, 3))(); + double norm = tensor::Tensor(matrix.col(3)).sum(); shell.transitions.resize(n_transition); for (int j = 0; j < n_transition; ++j) { @@ -216,7 +214,7 @@ PhotonInteraction::PhotonInteraction(hid_t group) // Read electron shell PDF and binding energies read_dataset(rgroup, "num_electrons", electron_pdf_); - electron_pdf_ /= xt::sum(electron_pdf_); + electron_pdf_ /= electron_pdf_.sum(); read_dataset(rgroup, "binding_energy", binding_energy_); // Read Compton profiles @@ -234,7 +232,7 @@ PhotonInteraction::PhotonInteraction(hid_t group) auto is_close = [](double a, double b) { return std::abs(a - b) / a < FP_REL_PRECISION; }; - subshell_map_ = xt::full_like(binding_energy_, -1); + subshell_map_ = tensor::Tensor(binding_energy_.shape(), -1); for (int i = 0; i < binding_energy_.size(); ++i) { double E_b = binding_energy_[i]; if (i < n_shell && is_close(E_b, shells_[i].binding_energy)) { @@ -253,7 +251,7 @@ PhotonInteraction::PhotonInteraction(hid_t group) // Create Compton profile CDF auto n_profile = data::compton_profile_pz.size(); auto n_shell_compton = profile_pdf_.shape(0); - profile_cdf_ = xt::empty({n_shell_compton, n_profile}); + profile_cdf_ = tensor::Tensor({n_shell_compton, n_profile}); for (int i = 0; i < n_shell_compton; ++i) { double c = 0.0; profile_cdf_(i, 0) = 0.0; @@ -272,11 +270,11 @@ PhotonInteraction::PhotonInteraction(hid_t group) // Read bremsstrahlung scaled DCS rgroup = open_group(group, "bremsstrahlung"); read_dataset(rgroup, "dcs", dcs_); - auto n_e = dcs_.shape()[0]; - auto n_k = dcs_.shape()[1]; + auto n_e = dcs_.shape(0); + auto n_k = dcs_.shape(1); // Get energy grids used for bremsstrahlung DCS and for stopping powers - xt::xtensor electron_energy; + tensor::Tensor electron_energy; read_dataset(rgroup, "electron_energy", electron_energy); if (data::ttb_k_grid.size() == 0) { read_dataset(rgroup, "photon_energy", data::ttb_k_grid); @@ -301,12 +299,12 @@ PhotonInteraction::PhotonInteraction(hid_t group) (std::log(E(i_grid + 1)) - std::log(E(i_grid))); // Interpolate bremsstrahlung DCS at the cutoff energy and truncate - xt::xtensor dcs({n_e - i_grid, n_k}); + tensor::Tensor dcs({n_e - i_grid, n_k}); for (int i = 0; i < n_k; ++i) { double y = std::exp( std::log(dcs_(i_grid, i)) + f * (std::log(dcs_(i_grid + 1, i)) - std::log(dcs_(i_grid, i)))); - auto col_i = xt::view(dcs, xt::all(), i); + auto col_i = dcs.col(i); col_i(0) = y; for (int j = i_grid + 1; j < n_e; ++j) { col_i(j - i_grid) = dcs_(j, i); @@ -314,9 +312,10 @@ PhotonInteraction::PhotonInteraction(hid_t group) } dcs_ = dcs; - xt::xtensor frst {cutoff}; - electron_energy = xt::concatenate(xt::xtuple( - frst, xt::view(electron_energy, xt::range(i_grid + 1, n_e)))); + tensor::Tensor frst({static_cast(1)}); + frst(0) = cutoff; + tensor::Tensor rest(electron_energy.slice(i_grid + 1)); + electron_energy = tensor::concatenate(frst, rest); } // Set incident particle energy grid @@ -325,7 +324,7 @@ PhotonInteraction::PhotonInteraction(hid_t group) } // Calculate the radiative stopping power - stopping_power_radiative_ = xt::empty({data::ttb_e_grid.size()}); + stopping_power_radiative_ = tensor::Tensor({data::ttb_e_grid.size()}); for (int i = 0; i < data::ttb_e_grid.size(); ++i) { // Integrate over reduced photon energy double c = 0.0; @@ -350,14 +349,14 @@ PhotonInteraction::PhotonInteraction(hid_t group) // values below exp(-499) we store the log as -900, for which exp(-900) // evaluates to zero. double limit = std::exp(-499.0); - energy_ = xt::log(energy_); - coherent_ = xt::where(coherent_ > limit, xt::log(coherent_), -900.0); - incoherent_ = xt::where(incoherent_ > limit, xt::log(incoherent_), -900.0); - photoelectric_total_ = xt::where( - photoelectric_total_ > limit, xt::log(photoelectric_total_), -900.0); - pair_production_total_ = xt::where( - pair_production_total_ > limit, xt::log(pair_production_total_), -900.0); - heating_ = xt::where(heating_ > limit, xt::log(heating_), -900.0); + energy_ = tensor::log(energy_); + coherent_ = tensor::where(coherent_ > limit, tensor::log(coherent_), -900.0); + incoherent_ = tensor::where(incoherent_ > limit, tensor::log(incoherent_), -900.0); + photoelectric_total_ = tensor::where( + photoelectric_total_ > limit, tensor::log(photoelectric_total_), -900.0); + pair_production_total_ = tensor::where( + pair_production_total_ > limit, tensor::log(pair_production_total_), -900.0); + heating_ = tensor::where(heating_ > limit, tensor::log(heating_), -900.0); } PhotonInteraction::~PhotonInteraction() @@ -508,7 +507,7 @@ void PhotonInteraction::compton_doppler( c = prn(seed) * c_max; // Determine pz corresponding to sampled cdf value - auto cdf_shell = xt::view(profile_cdf_, shell, xt::all()); + auto cdf_shell = profile_cdf_.row(shell); int i = lower_bound_index(cdf_shell.cbegin(), cdf_shell.cend(), c); double pz_l = data::compton_profile_pz(i); double pz_r = data::compton_profile_pz(i + 1); @@ -604,8 +603,8 @@ void PhotonInteraction::calculate_xs(Particle& p) const // Calculate microscopic photoelectric cross section xs.photoelectric = 0.0; - const auto& xs_lower = xt::row(cross_sections_, i_grid); - const auto& xs_upper = xt::row(cross_sections_, i_grid + 1); + const auto xs_lower = cross_sections_.row(i_grid); + const auto xs_upper = cross_sections_.row(i_grid + 1); for (int i = 0; i < xs_upper.size(); ++i) if (xs_lower(i) != 0) diff --git a/src/physics.cpp b/src/physics.cpp index a6b0e7dfb27..9371f54725d 100644 --- a/src/physics.cpp +++ b/src/physics.cpp @@ -375,8 +375,8 @@ void sample_photon_reaction(Particle& p) // cross sections int i_grid = micro.index_grid; double f = micro.interp_factor; - const auto& xs_lower = xt::row(element.cross_sections_, i_grid); - const auto& xs_upper = xt::row(element.cross_sections_, i_grid + 1); + const auto& xs_lower = element.cross_sections_.row(i_grid); + const auto& xs_upper = element.cross_sections_.row(i_grid + 1); for (int i_shell = 0; i_shell < element.shells_.size(); ++i_shell) { const auto& shell {element.shells_[i_shell]}; diff --git a/src/plot.cpp b/src/plot.cpp index 0914e4e273d..f1adac985c3 100644 --- a/src/plot.cpp +++ b/src/plot.cpp @@ -72,7 +72,7 @@ void IdData::set_value(size_t y, size_t x, const GeometryState& p, int level) void IdData::set_overlap(size_t y, size_t x) { - xt::view(data_, y, x, xt::all()) = OVERLAP; + for (size_t k = 0; k < data_.shape(2); ++k) data_(y, x, k) = OVERLAP; } PropertyData::PropertyData(size_t h_res, size_t v_res) @@ -742,14 +742,14 @@ void output_ppm(const std::string& filename, const ImageData& data) // Write header of << "P6\n"; - of << data.shape()[0] << " " << data.shape()[1] << "\n"; + of << data.shape(0) << " " << data.shape(1) << "\n"; of << "255\n"; of.close(); of.open(fname, std::ios::binary | std::ios::app); // Write color for each pixel - for (int y = 0; y < data.shape()[1]; y++) { - for (int x = 0; x < data.shape()[0]; x++) { + for (int y = 0; y < data.shape(1); y++) { + for (int x = 0; x < data.shape(0); x++) { RGBColor rgb = data(x, y); of << rgb.red << rgb.green << rgb.blue; } @@ -781,8 +781,8 @@ void output_png(const std::string& filename, const ImageData& data) png_init_io(png_ptr, fp); // Write header (8 bit colour depth) - int width = data.shape()[0]; - int height = data.shape()[1]; + int width = data.shape(0); + int height = data.shape(1); png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); png_write_info(png_ptr, info_ptr); @@ -983,9 +983,14 @@ void Plot::create_voxel() const // select only cell/material ID data and flip the y-axis int idx = color_by_ == PlotColorBy::cells ? 0 : 2; - xt::xtensor data_slice = - xt::view(ids.data_, xt::all(), xt::all(), idx); - xt::xtensor data_flipped = xt::flip(data_slice, 0); + // Extract 2D slice at index idx from 3D data + size_t rows = ids.data_.shape(0); + size_t cols = ids.data_.shape(1); + tensor::Tensor data_slice({rows, cols}); + for (size_t r = 0; r < rows; ++r) + for (size_t c = 0; c < cols; ++c) + data_slice(r, c) = ids.data_(r, c, idx); + tensor::Tensor data_flipped = data_slice.flip(0); // Write to HDF5 dataset voxel_write_slice(z, dspace, dset, memspace, data_flipped.data()); @@ -1227,7 +1232,7 @@ void WireframeRayTracePlot::create_output() const // This array marks where the initial wireframe was drawn. We convolve it with // a filter that gets adjusted with the wireframe thickness in order to // thicken the lines. - xt::xtensor wireframe_initial({width, height}, 0); + tensor::Tensor wireframe_initial({static_cast(width), static_cast(height)}, 0); /* Holds all of the track segments for the current rendered line of pixels. * old_segments holds a copy of this_line_segments from the previous line. diff --git a/src/random_ray/flat_source_domain.cpp b/src/random_ray/flat_source_domain.cpp index 32b3349332f..5074452ecbc 100644 --- a/src/random_ray/flat_source_domain.cpp +++ b/src/random_ray/flat_source_domain.cpp @@ -64,7 +64,7 @@ FlatSourceDomain::FlatSourceDomain() : negroups_(data::mg.num_energy_groups_) // Create a new 2D tensor with the same size as the first // two dimensions of the 3D tensor tally_volumes_[i] = - xt::xtensor::from_shape({shape[0], shape[1]}); + tensor::Tensor({shape[0], shape[1]}); } } diff --git a/src/scattdata.cpp b/src/scattdata.cpp index 2f3e9dfdb4b..6f4d5bad330 100644 --- a/src/scattdata.cpp +++ b/src/scattdata.cpp @@ -18,8 +18,8 @@ namespace openmc { // ScattData base-class methods //============================================================================== -void ScattData::base_init(int order, const xt::xtensor& in_gmin, - const xt::xtensor& in_gmax, const double_2dvec& in_energy, +void ScattData::base_init(int order, const tensor::Tensor& in_gmin, + const tensor::Tensor& in_gmax, const double_2dvec& in_energy, const double_2dvec& in_mult) { size_t groups = in_energy.size(); @@ -62,23 +62,23 @@ void ScattData::base_init(int order, const xt::xtensor& in_gmin, void ScattData::base_combine(size_t max_order, size_t order_dim, const vector& those_scatts, const vector& scalars, - xt::xtensor& in_gmin, xt::xtensor& in_gmax, + tensor::Tensor& in_gmin, tensor::Tensor& in_gmax, double_2dvec& sparse_mult, double_3dvec& sparse_scatter) { size_t groups = those_scatts[0]->energy.size(); // Now allocate and zero our storage spaces - xt::xtensor this_nuscatt_matrix({groups, groups, order_dim}, 0.); - xt::xtensor this_nuscatt_P0({groups, groups}, 0.); - xt::xtensor this_scatt_P0({groups, groups}, 0.); - xt::xtensor this_mult({groups, groups}, 1.); + tensor::Tensor this_nuscatt_matrix({groups, groups, order_dim}, 0.); + tensor::Tensor this_nuscatt_P0({groups, groups}, 0.); + tensor::Tensor this_scatt_P0({groups, groups}, 0.); + tensor::Tensor this_mult({groups, groups}, 1.); // Build the dense scattering and multiplicity matrices for (int i = 0; i < those_scatts.size(); i++) { ScattData* that = those_scatts[i]; // Build the dense matrix for that object - xt::xtensor that_matrix = that->get_matrix(max_order); + tensor::Tensor that_matrix = that->get_matrix(max_order); // Now add that to this for the nu-scatter matrix this_nuscatt_matrix += scalars[i] * that_matrix; @@ -96,7 +96,7 @@ void ScattData::base_combine(size_t max_order, size_t order_dim, // Now we have the dense nuscatt and scatt, we can easily compute the // multiplicity matrix by dividing the two and fixing any nans - this_mult = xt::nan_to_num(this_nuscatt_P0 / this_scatt_P0); + this_mult = tensor::nan_to_num(this_nuscatt_P0 / this_scatt_P0); // We have the data, now we need to convert to a jagged array and then use // the initialize function to store it on the object. @@ -105,7 +105,7 @@ void ScattData::base_combine(size_t max_order, size_t order_dim, int gmin_; for (gmin_ = 0; gmin_ < groups; gmin_++) { bool non_zero = false; - for (int l = 0; l < this_nuscatt_matrix.shape()[2]; l++) { + for (int l = 0; l < this_nuscatt_matrix.shape(2); l++) { if (this_nuscatt_matrix(gin, gmin_, l) != 0.) { non_zero = true; break; @@ -117,7 +117,7 @@ void ScattData::base_combine(size_t max_order, size_t order_dim, int gmax_; for (gmax_ = groups - 1; gmax_ >= 0; gmax_--) { bool non_zero = false; - for (int l = 0; l < this_nuscatt_matrix.shape()[2]; l++) { + for (int l = 0; l < this_nuscatt_matrix.shape(2); l++) { if (this_nuscatt_matrix(gin, gmax_, l) != 0.) { non_zero = true; break; @@ -142,8 +142,8 @@ void ScattData::base_combine(size_t max_order, size_t order_dim, sparse_mult[gin].resize(gmax_ - gmin_ + 1); int i_gout = 0; for (int gout = gmin_; gout <= gmax_; gout++) { - sparse_scatter[gin][i_gout].resize(this_nuscatt_matrix.shape()[2]); - for (int l = 0; l < this_nuscatt_matrix.shape()[2]; l++) { + sparse_scatter[gin][i_gout].resize(this_nuscatt_matrix.shape(2)); + for (int l = 0; l < this_nuscatt_matrix.shape(2); l++) { sparse_scatter[gin][i_gout][l] = this_nuscatt_matrix(gin, gout, l); } sparse_mult[gin][i_gout] = this_mult(gin, gout); @@ -226,8 +226,8 @@ double ScattData::get_xs( // ScattDataLegendre methods //============================================================================== -void ScattDataLegendre::init(const xt::xtensor& in_gmin, - const xt::xtensor& in_gmax, const double_2dvec& in_mult, +void ScattDataLegendre::init(const tensor::Tensor& in_gmin, + const tensor::Tensor& in_gmax, const double_2dvec& in_mult, const double_3dvec& coeffs) { size_t groups = coeffs.size(); @@ -238,7 +238,7 @@ void ScattDataLegendre::init(const xt::xtensor& in_gmin, // Get the scattering cross section value by summing the un-normalized P0 // coefficient in the variable matrix over all outgoing groups. - scattxs = xt::zeros({groups}); + scattxs = tensor::zeros({groups}); for (int gin = 0; gin < groups; gin++) { int num_groups = in_gmax[gin] - in_gmin[gin] + 1; for (int i_gout = 0; i_gout < num_groups; i_gout++) { @@ -385,8 +385,8 @@ void ScattDataLegendre::combine( size_t groups = those_scatts[0]->energy.size(); - xt::xtensor in_gmin({groups}, 0); - xt::xtensor in_gmax({groups}, 0); + tensor::Tensor in_gmin({groups}, 0); + tensor::Tensor in_gmax({groups}, 0); double_3dvec sparse_scatter(groups); double_2dvec sparse_mult(groups); @@ -403,12 +403,12 @@ void ScattDataLegendre::combine( //============================================================================== -xt::xtensor ScattDataLegendre::get_matrix(size_t max_order) +tensor::Tensor ScattDataLegendre::get_matrix(size_t max_order) { // Get the sizes and initialize the data to 0 size_t groups = energy.size(); size_t order_dim = max_order + 1; - xt::xtensor matrix({groups, groups, order_dim}, 0.); + tensor::Tensor matrix({groups, groups, order_dim}, 0.); for (int gin = 0; gin < groups; gin++) { for (int i_gout = 0; i_gout < energy[gin].size(); i_gout++) { @@ -426,8 +426,8 @@ xt::xtensor ScattDataLegendre::get_matrix(size_t max_order) // ScattDataHistogram methods //============================================================================== -void ScattDataHistogram::init(const xt::xtensor& in_gmin, - const xt::xtensor& in_gmax, const double_2dvec& in_mult, +void ScattDataHistogram::init(const tensor::Tensor& in_gmin, + const tensor::Tensor& in_gmax, const double_2dvec& in_mult, const double_3dvec& coeffs) { size_t groups = coeffs.size(); @@ -438,7 +438,7 @@ void ScattDataHistogram::init(const xt::xtensor& in_gmin, // Get the scattering cross section value by summing the distribution // over all the histogram bins in angle and outgoing energy groups - scattxs = xt::zeros({groups}); + scattxs = tensor::zeros({groups}); for (int gin = 0; gin < groups; gin++) { for (int i_gout = 0; i_gout < matrix[gin].size(); i_gout++) { scattxs[gin] += std::accumulate( @@ -467,7 +467,7 @@ void ScattDataHistogram::init(const xt::xtensor& in_gmin, ScattData::base_init(order, in_gmin, in_gmax, in_energy, in_mult); // Build the angular distribution mu values - mu = xt::linspace(-1., 1., order + 1); + mu = tensor::linspace(-1., 1., order + 1); dmu = 2. / order; // Calculate f(mu) and integrate it so we can avoid rejection sampling @@ -512,7 +512,7 @@ double ScattDataHistogram::calc_f(int gin, int gout, double mu) int imu; if (mu == 1.) { // use size -2 to have the index one before the end - imu = this->mu.shape()[0] - 2; + imu = this->mu.shape(0) - 2; } else { imu = std::floor((mu + 1.) / dmu + 1.) - 1; } @@ -558,13 +558,13 @@ void ScattDataHistogram::sample( //============================================================================== -xt::xtensor ScattDataHistogram::get_matrix(size_t max_order) +tensor::Tensor ScattDataHistogram::get_matrix(size_t max_order) { // Get the sizes and initialize the data to 0 size_t groups = energy.size(); // We ignore the requested order for Histogram and Tabular representations size_t order_dim = get_order(); - xt::xtensor matrix({groups, groups, order_dim}, 0); + tensor::Tensor matrix({groups, groups, order_dim}, 0); for (int gin = 0; gin < groups; gin++) { for (int i_gout = 0; i_gout < energy[gin].size(); i_gout++) { @@ -599,8 +599,8 @@ void ScattDataHistogram::combine( size_t groups = those_scatts[0]->energy.size(); - xt::xtensor in_gmin({groups}, 0); - xt::xtensor in_gmax({groups}, 0); + tensor::Tensor in_gmin({groups}, 0); + tensor::Tensor in_gmax({groups}, 0); double_3dvec sparse_scatter(groups); double_2dvec sparse_mult(groups); @@ -619,8 +619,8 @@ void ScattDataHistogram::combine( // ScattDataTabular methods //============================================================================== -void ScattDataTabular::init(const xt::xtensor& in_gmin, - const xt::xtensor& in_gmax, const double_2dvec& in_mult, +void ScattDataTabular::init(const tensor::Tensor& in_gmin, + const tensor::Tensor& in_gmax, const double_2dvec& in_mult, const double_3dvec& coeffs) { size_t groups = coeffs.size(); @@ -630,12 +630,12 @@ void ScattDataTabular::init(const xt::xtensor& in_gmin, double_3dvec matrix = coeffs; // Build the angular distribution mu values - mu = xt::linspace(-1., 1., order); + mu = tensor::linspace(-1., 1., order); dmu = 2. / (order - 1); // Get the scattering cross section value by integrating the distribution // over all mu points and then combining over all outgoing groups - scattxs = xt::zeros({groups}); + scattxs = tensor::zeros({groups}); for (int gin = 0; gin < groups; gin++) { for (int i_gout = 0; i_gout < matrix[gin].size(); i_gout++) { for (int imu = 1; imu < order; imu++) { @@ -712,7 +712,7 @@ double ScattDataTabular::calc_f(int gin, int gout, double mu) int imu; if (mu == 1.) { // use size -2 to have the index one before the end - imu = this->mu.shape()[0] - 2; + imu = this->mu.shape(0) - 2; } else { imu = std::floor((mu + 1.) / dmu + 1.) - 1; } @@ -733,7 +733,7 @@ void ScattDataTabular::sample( sample_energy(gin, gout, i_gout, seed); // Determine the outgoing cosine bin - int NP = this->mu.shape()[0]; + int NP = this->mu.shape(0); double xi = prn(seed); double c_k = dist[gin][i_gout][0]; @@ -775,13 +775,13 @@ void ScattDataTabular::sample( //============================================================================== -xt::xtensor ScattDataTabular::get_matrix(size_t max_order) +tensor::Tensor ScattDataTabular::get_matrix(size_t max_order) { // Get the sizes and initialize the data to 0 size_t groups = energy.size(); // We ignore the requested order for Histogram and Tabular representations size_t order_dim = get_order(); - xt::xtensor matrix({groups, groups, order_dim}, 0.); + tensor::Tensor matrix({groups, groups, order_dim}, 0.); for (int gin = 0; gin < groups; gin++) { for (int i_gout = 0; i_gout < energy[gin].size(); i_gout++) { @@ -815,8 +815,8 @@ void ScattDataTabular::combine( size_t groups = those_scatts[0]->energy.size(); - xt::xtensor in_gmin({groups}, 0); - xt::xtensor in_gmax({groups}, 0); + tensor::Tensor in_gmin({groups}, 0); + tensor::Tensor in_gmax({groups}, 0); double_3dvec sparse_scatter(groups); double_2dvec sparse_mult(groups); @@ -853,7 +853,7 @@ void convert_legendre_to_tabular(ScattDataLegendre& leg, ScattDataTabular& tab) tab.scattxs = leg.scattxs; // Build mu and dmu - tab.mu = xt::linspace(-1., 1., n_mu); + tab.mu = tensor::linspace(-1., 1., n_mu); tab.dmu = 2. / (n_mu - 1); // Calculate f(mu) and integrate it so we can avoid rejection sampling diff --git a/src/secondary_correlated.cpp b/src/secondary_correlated.cpp index 15c6b0b3d5c..d5d55bd7b82 100644 --- a/src/secondary_correlated.cpp +++ b/src/secondary_correlated.cpp @@ -25,11 +25,11 @@ CorrelatedAngleEnergy::CorrelatedAngleEnergy(hid_t group) hid_t dset = open_dataset(group, "energy"); // Get interpolation parameters - xt::xarray temp; + tensor::Tensor 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 + auto temp_b = temp.row(0); // view of breakpoints + auto temp_i = temp.row(1); // view of interpolation parameters std::copy(temp_b.begin(), temp_b.end(), std::back_inserter(breakpoints_)); for (const auto i : temp_i) @@ -50,12 +50,12 @@ CorrelatedAngleEnergy::CorrelatedAngleEnergy(hid_t group) read_attribute(dset, "interpolation", interp); read_attribute(dset, "n_discrete_lines", n_discrete); - xt::xarray eout; + tensor::Tensor eout; read_dataset(dset, eout); close_dataset(dset); // Read angle distributions - xt::xarray mu; + tensor::Tensor mu; read_dataset(group, "mu", mu); for (int i = 0; i < n_energy; ++i) { @@ -65,7 +65,7 @@ CorrelatedAngleEnergy::CorrelatedAngleEnergy(hid_t group) if (i < n_energy - 1) { n = offsets[i + 1] - j; } else { - n = eout.shape()[1] - j; + n = eout.shape(1) - j; } // Assign interpolation scheme and number of discrete lines @@ -74,9 +74,9 @@ CorrelatedAngleEnergy::CorrelatedAngleEnergy(hid_t group) d.n_discrete = n_discrete[i]; // Copy data - d.e_out = xt::view(eout, 0, xt::range(j, j + n)); - d.p = xt::view(eout, 1, xt::range(j, j + n)); - d.c = xt::view(eout, 2, xt::range(j, j + n)); + d.e_out = eout.row(0).slice(j, j + n); + d.p = eout.row(1).slice(j, j + n); + d.c = eout.row(2).slice(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 @@ -118,10 +118,10 @@ CorrelatedAngleEnergy::CorrelatedAngleEnergy(hid_t group) // Determine offset and size of distribution int offset_mu = std::lround(eout(4, offsets[i] + j)); int m; - if (offsets[i] + j + 1 < eout.shape()[1]) { + if (offsets[i] + j + 1 < eout.shape(1)) { m = std::lround(eout(4, offsets[i] + j + 1)) - offset_mu; } else { - m = mu.shape()[1] - offset_mu; + m = mu.shape(1) - offset_mu; } // For incoherent inelastic thermal scattering, the angle distributions @@ -132,9 +132,9 @@ CorrelatedAngleEnergy::CorrelatedAngleEnergy(hid_t group) interp_mu = 1; auto interp = int2interp(interp_mu); - auto xs = xt::view(mu, 0, xt::range(offset_mu, offset_mu + m)); - auto ps = xt::view(mu, 1, xt::range(offset_mu, offset_mu + m)); - auto cs = xt::view(mu, 2, xt::range(offset_mu, offset_mu + m)); + auto xs = mu.row(0).slice(offset_mu, offset_mu + m); + auto ps = mu.row(1).slice(offset_mu, offset_mu + m); + auto cs = mu.row(2).slice(offset_mu, offset_mu + m); vector x {xs.begin(), xs.end()}; vector p {ps.begin(), ps.end()}; diff --git a/src/secondary_kalbach.cpp b/src/secondary_kalbach.cpp index 213195a3186..a1f44d9ee42 100644 --- a/src/secondary_kalbach.cpp +++ b/src/secondary_kalbach.cpp @@ -26,11 +26,11 @@ KalbachMann::KalbachMann(hid_t group) hid_t dset = open_dataset(group, "energy"); // Get interpolation parameters - xt::xarray temp; + tensor::Tensor 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 + auto temp_b = temp.row(0); // view of breakpoints + auto temp_i = temp.row(1); // view of interpolation parameters std::copy(temp_b.begin(), temp_b.end(), std::back_inserter(breakpoints_)); for (const auto i : temp_i) @@ -51,7 +51,7 @@ KalbachMann::KalbachMann(hid_t group) read_attribute(dset, "interpolation", interp); read_attribute(dset, "n_discrete_lines", n_discrete); - xt::xarray eout; + tensor::Tensor eout; read_dataset(dset, eout); close_dataset(dset); @@ -62,7 +62,7 @@ KalbachMann::KalbachMann(hid_t group) if (i < n_energy - 1) { n = offsets[i + 1] - j; } else { - n = eout.shape()[1] - j; + n = eout.shape(1) - j; } // Assign interpolation scheme and number of discrete lines @@ -71,11 +71,11 @@ KalbachMann::KalbachMann(hid_t group) d.n_discrete = n_discrete[i]; // Copy data - d.e_out = xt::view(eout, 0, xt::range(j, j + n)); - d.p = xt::view(eout, 1, xt::range(j, j + n)); - d.c = xt::view(eout, 2, xt::range(j, j + n)); - d.r = xt::view(eout, 3, xt::range(j, j + n)); - d.a = xt::view(eout, 4, xt::range(j, j + n)); + d.e_out = eout.row(0).slice(j, j + n); + d.p = eout.row(1).slice(j, j + n); + d.c = eout.row(2).slice(j, j + n); + d.r = eout.row(3).slice(j, j + n); + d.a = eout.row(4).slice(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 diff --git a/src/secondary_thermal.cpp b/src/secondary_thermal.cpp index 28c19fcae77..257a623e151 100644 --- a/src/secondary_thermal.cpp +++ b/src/secondary_thermal.cpp @@ -85,7 +85,7 @@ void IncoherentElasticAEDiscrete::sample( // incoming energies. // Sample outgoing cosine bin - int n_mu = mu_out_.shape()[1]; + int n_mu = mu_out_.shape(1); int k = prn(seed) * n_mu; // Rather than use the sampled discrete mu directly, it is smeared over @@ -145,7 +145,7 @@ void IncoherentInelasticAEDiscrete::sample( // probability of 1). Otherwise, each bin is equally probable. int j; - int n = energy_out_.shape()[1]; + int n = energy_out_.shape(1); if (!skewed_) { // All bins equally likely j = prn(seed) * n; @@ -178,7 +178,7 @@ void IncoherentInelasticAEDiscrete::sample( E_out = (1 - f) * E_ij + f * E_i1j; // Sample outgoing cosine bin - int m = mu_out_.shape()[2]; + int m = mu_out_.shape(2); int k = prn(seed) * m; // Determine outgoing cosine corresponding to E_in[i] and E_in[i+1] @@ -218,11 +218,11 @@ IncoherentInelasticAE::IncoherentInelasticAE(hid_t group) // On first pass, allocate space for angles if (j == 0) { auto n_mu = adist->x().size(); - d.mu = xt::empty({d.n_e_out, n_mu}); + d.mu = tensor::Tensor({d.n_e_out, n_mu}); } // Copy outgoing angles - auto mu_j = xt::view(d.mu, j); + auto mu_j = d.mu.row(j); std::copy(adist->x().begin(), adist->x().end(), mu_j.begin()); } } @@ -287,7 +287,7 @@ void IncoherentInelasticAE::sample( } // Sample outgoing cosine bin - int n_mu = distribution_[l].mu.shape()[1]; + int n_mu = distribution_[l].mu.shape(1); std::size_t k = prn(seed) * n_mu; // Rather than use the sampled discrete mu directly, it is smeared over diff --git a/src/simulation.cpp b/src/simulation.cpp index 5a580d83e72..d0d6f037f9e 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -413,7 +413,7 @@ void finalize_batch() // Reset global tally results if (simulation::current_batch <= settings::n_inactive) { - xt::view(simulation::global_tallies, xt::all()) = 0.0; + simulation::global_tallies.fill(0.0); simulation::n_realizations = 0; } diff --git a/src/source.cpp b/src/source.cpp index 8ce544d1a22..7712939df2b 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -400,8 +400,8 @@ SourceSite IndependentSource::sample(uint64_t* seed) const auto p = particle_.transport_index(); auto energy_ptr = dynamic_cast(energy_.get()); if (energy_ptr) { - auto energies = xt::adapt(energy_ptr->x()); - if (xt::any(energies > data::energy_max[p])) { + auto energies = tensor::Tensor(energy_ptr->x()); + if ((energies > data::energy_max[p]).any()) { fatal_error("Source energy above range of energies of at least " "one cross section table"); } diff --git a/src/state_point.cpp b/src/state_point.cpp index a2696dffe75..848d82b0f4a 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -276,8 +276,8 @@ extern "C" int openmc_statepoint_write(const char* filename, bool* write_source) std::string name = "tally " + std::to_string(tally->id_); hid_t tally_group = open_group(tallies_group, name.c_str()); auto& results = tally->results_; - write_tally_results(tally_group, results.shape()[0], - results.shape()[1], results.shape()[2], results.data()); + write_tally_results(tally_group, results.shape(0), + results.shape(1), results.shape(2), results.data()); close_group(tally_group); } } else { @@ -516,8 +516,8 @@ extern "C" int openmc_statepoint_load(const char* filename) tally->writable_ = false; } else { auto& results = tally->results_; - read_tally_results(tally_group, results.shape()[0], - results.shape()[1], results.shape()[2], results.data()); + read_tally_results(tally_group, results.shape(0), + results.shape(1), results.shape(2), results.data()); read_dataset(tally_group, "n_realizations", tally->n_realizations_); close_group(tally_group); @@ -826,7 +826,7 @@ void write_unstructured_mesh_results() // construct result vectors vector mean_vec(umesh->n_bins()), std_dev_vec(umesh->n_bins()); - for (int j = 0; j < tally->results_.shape()[0]; j++) { + for (int j = 0; j < tally->results_.shape(0); j++) { // get the volume for this bin double volume = umesh->volume(j); // compute the mean @@ -888,7 +888,7 @@ void write_tally_results_nr(hid_t file_id) #ifdef OPENMC_MPI // Reduce global tallies - xt::xtensor gt_reduced = xt::empty_like(gt); + tensor::Tensor gt_reduced({N_GLOBAL_TALLIES, 3}); MPI_Reduce(gt.data(), gt_reduced.data(), gt.size(), MPI_DOUBLE, MPI_SUM, 0, mpi::intracomm); @@ -922,9 +922,9 @@ void write_tally_results_nr(hid_t file_id) const int r_start = static_cast(TallyResult::SUM); const int r_end = static_cast(TallyResult::SUM_SQ) + 1; const size_t r_count = r_end - r_start; - const size_t ni = t->results_.shape()[0]; - const size_t nj = t->results_.shape()[1]; - xt::xtensor values({ni, nj, r_count}); + const size_t ni = t->results_.shape(0); + const size_t nj = t->results_.shape(1); + tensor::Tensor values({ni, nj, r_count}); for (size_t i = 0; i < ni; i++) for (size_t j = 0; j < nj; j++) for (size_t r = 0; r < r_count; r++) @@ -953,7 +953,7 @@ void write_tally_results_nr(hid_t file_id) } // Put reduced values into a full-sized copy for writing to HDF5 - xt::xtensor results_copy = xt::zeros_like(t->results_); + tensor::Tensor results_copy = tensor::zeros_like(t->results_); for (size_t i = 0; i < ni; i++) for (size_t j = 0; j < nj; j++) for (size_t r = 0; r < r_count; r++) diff --git a/src/tallies/filter_cell_instance.cpp b/src/tallies/filter_cell_instance.cpp index 316a758d11a..fa2016c1025 100644 --- a/src/tallies/filter_cell_instance.cpp +++ b/src/tallies/filter_cell_instance.cpp @@ -7,6 +7,7 @@ #include "openmc/capi.h" #include "openmc/cell.h" +#include "openmc/tensor.h" #include "openmc/error.h" #include "openmc/geometry.h" #include "openmc/xml_interface.h" @@ -108,7 +109,7 @@ void CellInstanceFilter::to_statepoint(hid_t filter_group) const { Filter::to_statepoint(filter_group); size_t n = cell_instances_.size(); - xt::xtensor data({n, 2}); + tensor::Tensor data({n, 2}); for (int64_t i = 0; i < n; ++i) { const auto& x = cell_instances_[i]; data(i, 0) = model::cells[x.index_cell]->id_; diff --git a/src/tallies/filter_meshmaterial.cpp b/src/tallies/filter_meshmaterial.cpp index 60d9849e9a6..58ba7195abc 100644 --- a/src/tallies/filter_meshmaterial.cpp +++ b/src/tallies/filter_meshmaterial.cpp @@ -7,6 +7,7 @@ #include "openmc/capi.h" #include "openmc/constants.h" +#include "openmc/tensor.h" #include "openmc/container_util.h" #include "openmc/error.h" #include "openmc/material.h" @@ -162,7 +163,7 @@ void MeshMaterialFilter::to_statepoint(hid_t filter_group) const write_dataset(filter_group, "mesh", model::meshes[mesh_]->id_); size_t n = bins_.size(); - xt::xtensor data({n, 2}); + tensor::Tensor data({n, 2}); for (int64_t i = 0; i < n; ++i) { const auto& x = bins_[i]; data(i, 0) = x.index_element; diff --git a/src/tallies/tally.cpp b/src/tallies/tally.cpp index d00f3e28004..5a97bab9af1 100644 --- a/src/tallies/tally.cpp +++ b/src/tallies/tally.cpp @@ -67,7 +67,7 @@ vector time_grid; } // namespace model namespace simulation { -xt::xtensor_fixed> global_tallies; +tensor::Fixed2D global_tallies; int32_t n_realizations {0}; } // namespace simulation @@ -804,9 +804,9 @@ void Tally::init_results() { int n_scores = scores_.size() * nuclides_.size(); if (higher_moments_) { - results_ = xt::empty({n_filter_bins_, n_scores, 5}); + results_ = tensor::Tensor({static_cast(n_filter_bins_), static_cast(n_scores), size_t{5}}); } else { - results_ = xt::empty({n_filter_bins_, n_scores, 3}); + results_ = tensor::Tensor({static_cast(n_filter_bins_), static_cast(n_scores), size_t{3}}); } } @@ -814,7 +814,7 @@ void Tally::reset() { n_realizations_ = 0; if (results_.size() != 0) { - xt::view(results_, xt::all()) = 0.0; + results_.fill(0.0); } } @@ -849,9 +849,9 @@ void Tally::accumulate() if (higher_moments_) { #pragma omp parallel for // filter bins (specific cell, energy bins) - for (int i = 0; i < results_.shape()[0]; ++i) { + for (int i = 0; i < results_.shape(0); ++i) { // score bins (flux, total reaction rate, fission reaction rate, etc.) - for (int j = 0; j < results_.shape()[1]; ++j) { + for (int j = 0; j < results_.shape(1); ++j) { double val = results_(i, j, TallyResult::VALUE) * norm; double val2 = val * val; results_(i, j, TallyResult::VALUE) = 0.0; @@ -864,9 +864,9 @@ void Tally::accumulate() } else { #pragma omp parallel for // filter bins (specific cell, energy bins) - for (int i = 0; i < results_.shape()[0]; ++i) { + for (int i = 0; i < results_.shape(0); ++i) { // score bins (flux, total reaction rate, fission reaction rate, etc.) - for (int j = 0; j < results_.shape()[1]; ++j) { + for (int j = 0; j < results_.shape(1); ++j) { double val = results_(i, j, TallyResult::VALUE) * norm; results_(i, j, TallyResult::VALUE) = 0.0; results_(i, j, TallyResult::SUM) += val; @@ -886,18 +886,18 @@ int Tally::score_index(const std::string& score) const return -1; } -xt::xarray Tally::get_reshaped_data() const +tensor::Tensor Tally::get_reshaped_data() const { - std::vector shape; + openmc::vector shape; for (auto f : filters()) { shape.push_back(model::tally_filters[f]->n_bins()); } // add number of scores and nuclides to tally - shape.push_back(results_.shape()[1]); - shape.push_back(results_.shape()[2]); + shape.push_back(results_.shape(1)); + shape.push_back(results_.shape(2)); - xt::xarray reshaped_results = results_; + tensor::Tensor reshaped_results = results_; reshaped_results.reshape(shape); return reshaped_results; } @@ -1002,13 +1002,16 @@ void reduce_tally_results() // Skip any tallies that are not active auto& tally {model::tallies[i_tally]}; - // Get view of accumulated tally values - auto values_view = xt::view(tally->results_, xt::all(), xt::all(), - static_cast(TallyResult::VALUE)); + // Copy accumulated tally values (VALUE column) into contiguous array + const int val_idx = static_cast(TallyResult::VALUE); + const size_t ni = tally->results_.shape(0); + const size_t nj = tally->results_.shape(1); + tensor::Tensor values({ni, nj}); + for (size_t i = 0; i < ni; ++i) + for (size_t j = 0; j < nj; ++j) + values(i, j) = tally->results_(i, j, val_idx); - // Make copy of tally values in contiguous array - xt::xtensor values = values_view; - xt::xtensor values_reduced = xt::empty_like(values); + tensor::Tensor values_reduced(values.shape()); // Reduce contiguous set of tally results MPI_Reduce(values.data(), values_reduced.data(), values.size(), @@ -1016,9 +1019,13 @@ void reduce_tally_results() // Transfer values on master and reset on other ranks if (mpi::master) { - values_view = values_reduced; + for (size_t i = 0; i < ni; ++i) + for (size_t j = 0; j < nj; ++j) + tally->results_(i, j, val_idx) = values_reduced(i, j); } else { - values_view = 0.0; + for (size_t i = 0; i < ni; ++i) + for (size_t j = 0; j < nj; ++j) + tally->results_(i, j, val_idx) = 0.0; } } } @@ -1026,14 +1033,16 @@ void reduce_tally_results() // Note that global tallies are *always* reduced even when no_reduce option // is on. - // Get view of global tally values + // Get reference to global tallies auto& gt = simulation::global_tallies; - auto gt_values_view = - xt::view(gt, xt::all(), static_cast(TallyResult::VALUE)); + const int val_col = static_cast(TallyResult::VALUE); - // Make copy of values in contiguous array - xt::xtensor gt_values = gt_values_view; - xt::xtensor gt_values_reduced = xt::empty_like(gt_values); + // Copy VALUE column into contiguous array + tensor::Tensor gt_values({size_t{N_GLOBAL_TALLIES}}); + for (int i = 0; i < N_GLOBAL_TALLIES; ++i) + gt_values(i) = gt(i, val_col); + + tensor::Tensor gt_values_reduced({size_t{N_GLOBAL_TALLIES}}); // Reduce contiguous data MPI_Reduce(gt_values.data(), gt_values_reduced.data(), N_GLOBAL_TALLIES, @@ -1041,9 +1050,11 @@ void reduce_tally_results() // Transfer values on master and reset on other ranks if (mpi::master) { - gt_values_view = gt_values_reduced; + for (int i = 0; i < N_GLOBAL_TALLIES; ++i) + gt(i, val_col) = gt_values_reduced(i); } else { - gt_values_view = 0.0; + for (int i = 0; i < N_GLOBAL_TALLIES; ++i) + gt(i, val_col) = 0.0; } // We also need to determine the total starting weight of particles from the diff --git a/src/tallies/trigger.cpp b/src/tallies/trigger.cpp index f1f83e2982c..6c54edd5e1d 100644 --- a/src/tallies/trigger.cpp +++ b/src/tallies/trigger.cpp @@ -71,7 +71,7 @@ void check_tally_triggers(double& ratio, int& tally_id, int& score) continue; const auto& results = t.results_; - for (auto filter_index = 0; filter_index < results.shape()[0]; + for (auto filter_index = 0; filter_index < results.shape(0); ++filter_index) { // Compute the tally uncertainty metrics. auto uncert_pair = diff --git a/src/thermal.cpp b/src/thermal.cpp index 6758fa40b6a..6ed59f68693 100644 --- a/src/thermal.cpp +++ b/src/thermal.cpp @@ -50,7 +50,7 @@ ThermalScattering::ThermalScattering( // Determine temperatures available auto dset_names = dataset_names(kT_group); auto n = dset_names.size(); - auto temps_available = xt::empty({n}); + auto temps_available = tensor::Tensor({n}); for (int i = 0; i < dset_names.size(); ++i) { // Read temperature value double T; @@ -77,7 +77,7 @@ ThermalScattering::ThermalScattering( // Determine actual temperatures to read for (const auto& T : temperature) { - auto i_closest = xt::argmin(xt::abs(temps_available - T))[0]; + auto i_closest = tensor::abs(temps_available - T).argmin(); auto temp_actual = temps_available[i_closest]; if (std::abs(temp_actual - T) < settings::temperature_tolerance) { if (std::find(temps_to_read.begin(), temps_to_read.end(), diff --git a/src/urr.cpp b/src/urr.cpp index 02cef228099..41dfdf9a59f 100644 --- a/src/urr.cpp +++ b/src/urr.cpp @@ -25,7 +25,7 @@ UrrData::UrrData(hid_t group_id) // Read URR tables. The HDF5 format is a little // different from how we want it laid out in memory. // This array used to be called "prob_". - xt::xtensor tmp_prob; + tensor::Tensor tmp_prob; read_dataset(group_id, "table", tmp_prob); auto shape = tmp_prob.shape(); diff --git a/src/volume_calc.cpp b/src/volume_calc.cpp index c4967c14b63..01f541d710f 100644 --- a/src/volume_calc.cpp +++ b/src/volume_calc.cpp @@ -241,7 +241,7 @@ vector VolumeCalculation::execute() const // non-zero auto n_nuc = settings::run_CE ? data::nuclides.size() : data::mg.nuclides_.size(); - xt::xtensor atoms({n_nuc, 2}, 0.0); + tensor::Tensor atoms({static_cast(n_nuc), size_t{2}}, 0.0); #ifdef OPENMC_MPI if (mpi::master) { @@ -451,9 +451,11 @@ void VolumeCalculation::to_hdf5( } // Create array of total # of atoms with uncertainty for each nuclide - xt::xtensor atom_data({n_nuc, 2}); - xt::view(atom_data, xt::all(), 0) = xt::adapt(result.atoms); - xt::view(atom_data, xt::all(), 1) = xt::adapt(result.uncertainty); + tensor::Tensor atom_data({static_cast(n_nuc), size_t{2}}); + for (size_t k = 0; k < static_cast(n_nuc); ++k) { + atom_data(k, 0) = result.atoms[k]; + atom_data(k, 1) = result.uncertainty[k]; + } // Write results write_dataset(group_id, "nuclides", nucnames); diff --git a/src/weight_windows.cpp b/src/weight_windows.cpp index bb89a0c490c..3874fa77569 100644 --- a/src/weight_windows.cpp +++ b/src/weight_windows.cpp @@ -260,8 +260,8 @@ WeightWindows* WeightWindows::from_hdf5( } wws->set_mesh(model::mesh_map[mesh_id]); - wws->lower_ww_ = xt::empty(wws->bounds_size()); - wws->upper_ww_ = xt::empty(wws->bounds_size()); + wws->lower_ww_ = tensor::Tensor({static_cast(wws->bounds_size()[0]), static_cast(wws->bounds_size()[1])}); + wws->upper_ww_ = tensor::Tensor({static_cast(wws->bounds_size()[0]), static_cast(wws->bounds_size()[1])}); read_dataset(ww_group, "lower_ww_bounds", wws->lower_ww_); read_dataset(ww_group, "upper_ww_bounds", wws->upper_ww_); @@ -296,9 +296,9 @@ void WeightWindows::allocate_ww_bounds() "Size of weight window bounds is zero for WeightWindows {}", id()); warning(msg); } - lower_ww_ = xt::empty(shape); + lower_ww_ = tensor::Tensor({static_cast(shape[0]), static_cast(shape[1])}); lower_ww_.fill(-1); - upper_ww_ = xt::empty(shape); + upper_ww_ = tensor::Tensor({static_cast(shape[0]), static_cast(shape[1])}); upper_ww_.fill(-1); } @@ -443,8 +443,8 @@ void WeightWindows::check_bounds(const T& bounds) const } } -void WeightWindows::set_bounds(const xt::xtensor& lower_bounds, - const xt::xtensor& upper_bounds) +void WeightWindows::set_bounds(const tensor::Tensor& lower_bounds, + const tensor::Tensor& upper_bounds) { this->check_bounds(lower_bounds, upper_bounds); @@ -455,7 +455,7 @@ void WeightWindows::set_bounds(const xt::xtensor& lower_bounds, } void WeightWindows::set_bounds( - const xt::xtensor& lower_bounds, double ratio) + const tensor::Tensor& lower_bounds, double ratio) { this->check_bounds(lower_bounds); @@ -470,8 +470,8 @@ void WeightWindows::set_bounds( { check_bounds(lower_bounds, upper_bounds); auto shape = this->bounds_size(); - lower_ww_ = xt::empty(shape); - upper_ww_ = xt::empty(shape); + lower_ww_ = tensor::Tensor({static_cast(shape[0]), static_cast(shape[1])}); + upper_ww_ = tensor::Tensor({static_cast(shape[0]), static_cast(shape[1])}); // Copy weight window values from input spans into the tensors std::copy(lower_bounds.data(), lower_bounds.data() + lower_ww_.size(), @@ -485,8 +485,8 @@ void WeightWindows::set_bounds(span lower_bounds, double ratio) this->check_bounds(lower_bounds); auto shape = this->bounds_size(); - lower_ww_ = xt::empty(shape); - upper_ww_ = xt::empty(shape); + lower_ww_ = tensor::Tensor({static_cast(shape[0]), static_cast(shape[1])}); + upper_ww_ = tensor::Tensor({static_cast(shape[0]), static_cast(shape[1])}); // Copy lower bounds into both arrays, then scale upper by ratio std::copy(lower_bounds.data(), lower_bounds.data() + lower_ww_.size(), @@ -505,8 +505,8 @@ void WeightWindows::update_weights(const Tally* tally, const std::string& value, this->check_tally_update_compatibility(tally); // Dimensions of weight window arrays - int e_bins = lower_ww_.shape()[0]; - int64_t mesh_bins = lower_ww_.shape()[1]; + int e_bins = lower_ww_.shape(0); + int64_t mesh_bins = lower_ww_.shape(1); // Initialize weight window arrays to -1.0 by default #pragma omp parallel for collapse(2) schedule(static) @@ -546,7 +546,7 @@ void WeightWindows::update_weights(const Tally* tally, const std::string& value, // dimension 5 (3 filter dimensions, 1 score dimension, 1 results dimension) // Look for the size of the last dimension of the results array const auto& results_arr = tally->results(); - const int results_dim = static_cast(results_arr.shape()[2]); + const int results_dim = static_cast(results_arr.shape(2)); std::array shape = {1, 1, 1, tally->n_scores(), results_dim}; // set the shape for the filters applied on the tally @@ -615,9 +615,9 @@ void WeightWindows::update_weights(const Tally* tally, const std::string& value, const int stride0 = shape[1] * shape[2]; const int stride1 = shape[2]; - xt::xtensor sum( + tensor::Tensor sum( {static_cast(e_bins), static_cast(mesh_bins)}); - xt::xtensor sum_sq( + tensor::Tensor sum_sq( {static_cast(e_bins), static_cast(mesh_bins)}); const int i_sum = static_cast(TallyResult::SUM); @@ -1168,7 +1168,8 @@ extern "C" int openmc_weight_windows_set_bounds(int32_t index, return err; const auto& wws = variance_reduction::weight_windows[index]; - wws->set_bounds({lower_bounds, size}, {upper_bounds, size}); + wws->set_bounds( + span(lower_bounds, size), span(upper_bounds, size)); return 0; } diff --git a/src/wmp.cpp b/src/wmp.cpp index e9c4fb5e3e6..6f72e1ba4cc 100644 --- a/src/wmp.cpp +++ b/src/wmp.cpp @@ -33,22 +33,22 @@ WindowedMultipole::WindowedMultipole(hid_t group) // Read the "data" array. Use its shape to figure out the number of poles // and residue types in this data. read_dataset(group, "data", data_); - int n_residues = data_.shape()[1] - 1; + int n_residues = data_.shape(1) - 1; // Check to see if this data includes fission residues. fissionable_ = (n_residues == 3); // Read the "windows" array and use its shape to figure out the number of // windows. - xt::xtensor windows; + tensor::Tensor windows; read_dataset(group, "windows", windows); - int n_windows = windows.shape()[0]; + int n_windows = windows.shape(0); windows -= 1; // Adjust to 0-based indices // Read the "broaden_poly" arrays. - xt::xtensor broaden_poly; + tensor::Tensor broaden_poly; read_dataset(group, "broaden_poly", broaden_poly); - if (n_windows != broaden_poly.shape()[0]) { + if (n_windows != broaden_poly.shape(0)) { fatal_error("broaden_poly array shape is not consistent with the windows " "array shape in WMP library for " + name_ + "."); @@ -56,12 +56,12 @@ WindowedMultipole::WindowedMultipole(hid_t group) // Read the "curvefit" array. read_dataset(group, "curvefit", curvefit_); - if (n_windows != curvefit_.shape()[0]) { + if (n_windows != curvefit_.shape(0)) { fatal_error("curvefit array shape is not consistent with the windows " "array shape in WMP library for " + name_ + "."); } - fit_order_ = curvefit_.shape()[1] - 1; + fit_order_ = curvefit_.shape(1) - 1; // Check the code is compiling to work with sufficiently high fit order if (fit_order_ + 1 > MAX_POLY_COEFFICIENTS) { diff --git a/src/xsdata.cpp b/src/xsdata.cpp index 7963383567d..840e2482417 100644 --- a/src/xsdata.cpp +++ b/src/xsdata.cpp @@ -34,32 +34,32 @@ XsData::XsData(bool fissionable, AngleDistributionType scatter_format, } // allocate all [temperature][angle][in group] quantities vector shape {n_ang, n_g_}; - total = xt::zeros(shape); - absorption = xt::zeros(shape); - inverse_velocity = xt::zeros(shape); + total = tensor::zeros(shape); + absorption = tensor::zeros(shape); + inverse_velocity = tensor::zeros(shape); if (fissionable) { - fission = xt::zeros(shape); - nu_fission = xt::zeros(shape); - prompt_nu_fission = xt::zeros(shape); - kappa_fission = xt::zeros(shape); + fission = tensor::zeros(shape); + nu_fission = tensor::zeros(shape); + prompt_nu_fission = tensor::zeros(shape); + kappa_fission = tensor::zeros(shape); } // allocate decay_rate; [temperature][angle][delayed group] shape[1] = n_dg_; - decay_rate = xt::zeros(shape); + decay_rate = tensor::zeros(shape); if (fissionable) { shape = {n_ang, n_dg_, n_g_}; // allocate delayed_nu_fission; [temperature][angle][delay group][in group] - delayed_nu_fission = xt::zeros(shape); + delayed_nu_fission = tensor::zeros(shape); // chi_prompt; [temperature][angle][in group][out group] shape = {n_ang, n_g_, n_g_}; - chi_prompt = xt::zeros(shape); + chi_prompt = tensor::zeros(shape); // chi_delayed; [temperature][angle][delay group][in group][out group] shape = {n_ang, n_dg_, n_g_, n_g_}; - chi_delayed = xt::zeros(shape); + chi_delayed = tensor::zeros(shape); } for (int a = 0; a < n_ang; a++) { @@ -82,7 +82,7 @@ void XsData::from_hdf5(hid_t xsdata_grp, bool fissionable, { // Reconstruct the dimension information so it doesn't need to be passed size_t n_ang = n_pol * n_azi; - size_t energy_groups = total.shape()[1]; + size_t energy_groups = total.shape(1); // Set the fissionable-specific data if (fissionable) { @@ -127,7 +127,7 @@ void XsData::fission_vector_beta_from_hdf5( // Data is provided as nu-fission and chi with a beta for delayed info // Get chi - xt::xtensor temp_chi({n_ang, n_g_}, 0.); + tensor::Tensor temp_chi({n_ang, n_g_}, 0.); read_nd_vector(xsdata_grp, "chi", temp_chi, true); // Normalize chi so it sums to 1 over outgoing groups for each angle @@ -152,7 +152,7 @@ void XsData::fission_vector_beta_from_hdf5( chi_delayed(a, d, gin, gout) = temp_chi(a, gout); // Get nu-fission - xt::xtensor temp_nufiss({n_ang, n_g_}, 0.); + tensor::Tensor temp_nufiss({n_ang, n_g_}, 0.); read_nd_vector(xsdata_grp, "nu-fission", temp_nufiss, true); // Get beta (strategy will depend upon the number of dimensions in beta) @@ -163,11 +163,11 @@ void XsData::fission_vector_beta_from_hdf5( if (!is_isotropic) ndim_target += 2; if (beta_ndims == ndim_target) { - xt::xtensor temp_beta({n_ang, n_dg_}, 0.); + tensor::Tensor temp_beta({n_ang, n_dg_}, 0.); read_nd_vector(xsdata_grp, "beta", temp_beta, true); // prompt_nu_fission = (1 - sum_of_beta) * nu_fission - auto beta_sum = xt::sum(temp_beta, {1}); + auto beta_sum = temp_beta.sum(1); for (size_t a = 0; a < n_ang; a++) for (size_t g = 0; g < n_g_; g++) prompt_nu_fission(a, g) = temp_nufiss(a, g) * (1.0 - beta_sum(a)); @@ -179,12 +179,12 @@ void XsData::fission_vector_beta_from_hdf5( for (size_t g = 0; g < n_g_; g++) delayed_nu_fission(a, d, g) = temp_beta(a, d) * temp_nufiss(a, g); } else if (beta_ndims == ndim_target + 1) { - xt::xtensor temp_beta({n_ang, n_dg_, n_g_}, 0.); + tensor::Tensor temp_beta({n_ang, n_dg_, n_g_}, 0.); read_nd_vector(xsdata_grp, "beta", temp_beta, true); // prompt_nu_fission = (1 - sum_of_beta) * nu_fission // Here beta is energy-dependent, so sum over delayed groups (axis 1) - auto beta_sum = xt::sum(temp_beta, {1}); + auto beta_sum = temp_beta.sum(1); for (size_t a = 0; a < n_ang; a++) for (size_t g = 0; g < n_g_; g++) prompt_nu_fission(a, g) = temp_nufiss(a, g) * (1.0 - beta_sum(a, g)); @@ -203,7 +203,7 @@ void XsData::fission_vector_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) // Data is provided separately as prompt + delayed nu-fission and chi // Get chi-prompt - xt::xtensor temp_chi_p({n_ang, n_g_}, 0.); + tensor::Tensor temp_chi_p({n_ang, n_g_}, 0.); read_nd_vector(xsdata_grp, "chi-prompt", temp_chi_p, true); // Normalize prompt chi so it sums to 1 over outgoing groups for each angle @@ -214,7 +214,7 @@ void XsData::fission_vector_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) } // Get chi-delayed - xt::xtensor temp_chi_d({n_ang, n_dg_, n_g_}, 0.); + tensor::Tensor temp_chi_d({n_ang, n_dg_, n_g_}, 0.); read_nd_vector(xsdata_grp, "chi-delayed", temp_chi_d, true); // Normalize delayed chi so it sums to 1 over outgoing groups for each @@ -250,7 +250,7 @@ void XsData::fission_vector_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang) // Therefore, the code only considers the data as prompt. // Get chi - xt::xtensor temp_chi({n_ang, n_g_}, 0.); + tensor::Tensor temp_chi({n_ang, n_g_}, 0.); read_nd_vector(xsdata_grp, "chi", temp_chi, true); // Normalize chi so it sums to 1 over outgoing groups for each angle @@ -278,7 +278,7 @@ void XsData::fission_matrix_beta_from_hdf5( // Data is provided as nu-fission and chi with a beta for delayed info // Get nu-fission matrix - xt::xtensor temp_matrix({n_ang, n_g_, n_g_}, 0.); + tensor::Tensor temp_matrix({n_ang, n_g_, n_g_}, 0.); read_nd_vector(xsdata_grp, "nu-fission", temp_matrix, true); // Get beta (strategy will depend upon the number of dimensions in beta) @@ -289,11 +289,11 @@ void XsData::fission_matrix_beta_from_hdf5( if (!is_isotropic) ndim_target += 2; if (beta_ndims == ndim_target) { - xt::xtensor temp_beta({n_ang, n_dg_}, 0.); + tensor::Tensor temp_beta({n_ang, n_dg_}, 0.); read_nd_vector(xsdata_grp, "beta", temp_beta, true); - auto beta_sum = xt::sum(temp_beta, {1}); - auto matrix_gout_sum = xt::sum(temp_matrix, {2}); + auto beta_sum = temp_beta.sum(1); + auto matrix_gout_sum = temp_matrix.sum(2); // prompt_nu_fission = sum_gout(matrix) * (1 - beta_total) for (size_t a = 0; a < n_ang; a++) @@ -324,11 +324,11 @@ void XsData::fission_matrix_beta_from_hdf5( temp_beta(a, d) * temp_matrix(a, gin, gout); } else if (beta_ndims == ndim_target + 1) { - xt::xtensor temp_beta({n_ang, n_dg_, n_g_}, 0.); + tensor::Tensor temp_beta({n_ang, n_dg_, n_g_}, 0.); read_nd_vector(xsdata_grp, "beta", temp_beta, true); - auto beta_sum = xt::sum(temp_beta, {1}); - auto matrix_gout_sum = xt::sum(temp_matrix, {2}); + auto beta_sum = temp_beta.sum(1); + auto matrix_gout_sum = temp_matrix.sum(2); // prompt_nu_fission = sum_gout(matrix) * (1 - beta_total) // Here beta is energy-dependent, so beta_sum is 2D [n_ang, n_g] @@ -386,11 +386,11 @@ void XsData::fission_matrix_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) // Data is provided separately as prompt + delayed nu-fission and chi // Get the prompt nu-fission matrix - xt::xtensor temp_matrix_p({n_ang, n_g_, n_g_}, 0.); + tensor::Tensor temp_matrix_p({n_ang, n_g_, n_g_}, 0.); read_nd_vector(xsdata_grp, "prompt-nu-fission", temp_matrix_p, true); // prompt_nu_fission is the sum over outgoing groups - prompt_nu_fission = xt::sum(temp_matrix_p, {2}); + prompt_nu_fission = temp_matrix_p.sum(2); // chi_prompt is the nu-fission matrix normalized over outgoing groups for (size_t a = 0; a < n_ang; a++) @@ -400,11 +400,11 @@ void XsData::fission_matrix_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) prompt_nu_fission(a, gin); // Get the delayed nu-fission matrix - xt::xtensor temp_matrix_d({n_ang, n_dg_, n_g_, n_g_}, 0.); + tensor::Tensor temp_matrix_d({n_ang, n_dg_, n_g_, n_g_}, 0.); read_nd_vector(xsdata_grp, "delayed-nu-fission", temp_matrix_d, true); // delayed_nu_fission is the sum over outgoing groups - delayed_nu_fission = xt::sum(temp_matrix_d, {3}); + delayed_nu_fission = temp_matrix_d.sum(3); // chi_delayed is the delayed nu-fission matrix normalized over outgoing groups for (size_t a = 0; a < n_ang; a++) @@ -421,11 +421,11 @@ void XsData::fission_matrix_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang) // Therefore, the code only considers the data as prompt. // Get nu-fission matrix - xt::xtensor temp_matrix({n_ang, n_g_, n_g_}, 0.); + tensor::Tensor temp_matrix({n_ang, n_g_, n_g_}, 0.); read_nd_vector(xsdata_grp, "nu-fission", temp_matrix, true); // prompt_nu_fission is the sum over outgoing groups - prompt_nu_fission = xt::sum(temp_matrix, {2}); + prompt_nu_fission = temp_matrix.sum(2); // chi_prompt is the nu-fission matrix normalized over outgoing groups for (size_t a = 0; a < n_ang; a++) @@ -473,7 +473,7 @@ void XsData::fission_from_hdf5( if (n_dg_ == 0) { nu_fission = prompt_nu_fission; } else { - nu_fission = prompt_nu_fission + xt::sum(delayed_nu_fission, {1}); + nu_fission = prompt_nu_fission + delayed_nu_fission.sum(1); } } @@ -489,9 +489,9 @@ void XsData::scatter_from_hdf5(hid_t xsdata_grp, size_t n_ang, hid_t scatt_grp = open_group(xsdata_grp, "scatter_data"); // Get the outgoing group boundary indices - xt::xtensor gmin({n_ang, n_g_}, 0.); + tensor::Tensor gmin({n_ang, n_g_}, 0.); read_nd_vector(scatt_grp, "g_min", gmin, true); - xt::xtensor gmax({n_ang, n_g_}, 0.); + tensor::Tensor gmax({n_ang, n_g_}, 0.); read_nd_vector(scatt_grp, "g_max", gmax, true); // Make gmin and gmax start from 0 vice 1 as they do in the library @@ -500,10 +500,10 @@ void XsData::scatter_from_hdf5(hid_t xsdata_grp, size_t n_ang, // Now use this info to find the length of a vector to hold the flattened // data. - size_t length = order_data * xt::sum(gmax - gmin + 1)(); + size_t length = order_data * (gmax - gmin + 1).sum(); double_4dvec input_scatt(n_ang, double_3dvec(n_g_)); - xt::xtensor temp_arr({length}, 0.); + tensor::Tensor temp_arr({length}, 0.); read_nd_vector(scatt_grp, "scatter_matrix", temp_arr, true); // Compare the number of orders given with the max order of the problem; @@ -566,8 +566,8 @@ void XsData::scatter_from_hdf5(hid_t xsdata_grp, size_t n_ang, final_scatter_format == AngleDistributionType::TABULAR) { for (size_t a = 0; a < n_ang; a++) { ScattDataLegendre legendre_scatt; - xt::xtensor in_gmin = xt::view(gmin, a, xt::all()); - xt::xtensor in_gmax = xt::view(gmax, a, xt::all()); + tensor::Tensor in_gmin = gmin.row(a); + tensor::Tensor in_gmax = gmax.row(a); legendre_scatt.init(in_gmin, in_gmax, temp_mult[a], input_scatt[a]); @@ -581,8 +581,8 @@ void XsData::scatter_from_hdf5(hid_t xsdata_grp, size_t n_ang, // We are sticking with the current representation // Initialize the ScattData object with this data for (size_t a = 0; a < n_ang; a++) { - xt::xtensor in_gmin = xt::view(gmin, a, xt::all()); - xt::xtensor in_gmax = xt::view(gmax, a, xt::all()); + tensor::Tensor in_gmin = gmin.row(a); + tensor::Tensor in_gmax = gmax.row(a); scatter[a]->init(in_gmin, in_gmax, temp_mult[a], input_scatt[a]); } } @@ -604,7 +604,7 @@ void XsData::combine( if (i == 0) { inverse_velocity = that->inverse_velocity; } - if (that->prompt_nu_fission.shape()[0] > 0) { + if (!that->prompt_nu_fission.empty()) { nu_fission += scalar * that->nu_fission; prompt_nu_fission += scalar * that->prompt_nu_fission; kappa_fission += scalar * that->kappa_fission; @@ -613,9 +613,9 @@ void XsData::combine( // Accumulate chi_prompt weighted by total prompt nu-fission // (summed over energy groups) for this constituent { - auto pnf_sum = xt::sum(that->prompt_nu_fission, {1}); - size_t n_ang = chi_prompt.shape()[0]; - size_t n_g = chi_prompt.shape()[1]; + auto pnf_sum = that->prompt_nu_fission.sum(1); + size_t n_ang = chi_prompt.shape(0); + size_t n_g = chi_prompt.shape(1); for (size_t a = 0; a < n_ang; a++) for (size_t gin = 0; gin < n_g; gin++) for (size_t gout = 0; gout < n_g; gout++) @@ -625,10 +625,10 @@ void XsData::combine( // Accumulate chi_delayed weighted by total delayed nu-fission // (summed over energy groups) for this constituent { - auto dnf_sum = xt::sum(that->delayed_nu_fission, {2}); - size_t n_ang = chi_delayed.shape()[0]; - size_t n_dg = chi_delayed.shape()[1]; - size_t n_g = chi_delayed.shape()[2]; + auto dnf_sum = that->delayed_nu_fission.sum(2); + size_t n_ang = chi_delayed.shape(0); + size_t n_dg = chi_delayed.shape(1); + size_t n_g = chi_delayed.shape(2); for (size_t a = 0; a < n_ang; a++) for (size_t d = 0; d < n_dg; d++) for (size_t gin = 0; gin < n_g; gin++) @@ -642,8 +642,8 @@ void XsData::combine( // Normalize chi_prompt so it sums to 1 over outgoing groups { - size_t n_ang = chi_prompt.shape()[0]; - size_t n_g = chi_prompt.shape()[1]; + size_t n_ang = chi_prompt.shape(0); + size_t n_g = chi_prompt.shape(1); for (size_t a = 0; a < n_ang; a++) for (size_t gin = 0; gin < n_g; gin++) { double s = 0.0; @@ -653,9 +653,9 @@ void XsData::combine( } // Normalize chi_delayed so it sums to 1 over outgoing groups { - size_t n_ang = chi_delayed.shape()[0]; - size_t n_dg = chi_delayed.shape()[1]; - size_t n_g = chi_delayed.shape()[2]; + size_t n_ang = chi_delayed.shape(0); + size_t n_dg = chi_delayed.shape(1); + size_t n_g = chi_delayed.shape(2); for (size_t a = 0; a < n_ang; a++) for (size_t d = 0; d < n_dg; d++) for (size_t gin = 0; gin < n_g; gin++) { @@ -668,7 +668,7 @@ void XsData::combine( } // Allow the ScattData object to combine itself - for (size_t a = 0; a < total.shape()[0]; a++) { + for (size_t a = 0; a < total.shape(0); a++) { // Build vector of the scattering objects to incorporate vector those_scatts(those_xs.size()); for (size_t i = 0; i < those_xs.size(); i++) { From 5d81f7a61c577bee09ba1ea8fcc6395f6378f7df Mon Sep 17 00:00:00 2001 From: John Tramm Date: Wed, 11 Feb 2026 13:35:16 -0600 Subject: [PATCH 12/51] Refactor from std::size_t to size_t, and from openmc::vector to vector --- include/openmc/hdf5_interface.h | 4 +- include/openmc/tensor.h | 330 ++++++++++++++++---------------- src/hdf5_interface.cpp | 2 +- src/nuclide.cpp | 2 +- src/tallies/tally.cpp | 2 +- 5 files changed, 170 insertions(+), 170 deletions(-) diff --git a/include/openmc/hdf5_interface.h b/include/openmc/hdf5_interface.h index c9c060e318d..6162fc7f4c3 100644 --- a/include/openmc/hdf5_interface.h +++ b/include/openmc/hdf5_interface.h @@ -173,7 +173,7 @@ void read_attribute(hid_t obj_id, const char* name, tensor::Tensor& arr) auto shape = attribute_shape(obj_id, name); // Resize tensor and read data directly - openmc::vector tshape(shape.begin(), shape.end()); + vector tshape(shape.begin(), shape.end()); arr.resize(tshape); // Read data from attribute @@ -290,7 +290,7 @@ void read_dataset(hid_t dset, tensor::Tensor& arr, bool indep = false) vector shape = object_shape(dset); // Resize tensor and read data directly - openmc::vector tshape(shape.begin(), shape.end()); + vector tshape(shape.begin(), shape.end()); arr.resize(tshape); // Read data from dataset diff --git a/include/openmc/tensor.h b/include/openmc/tensor.h index 144ac903586..074da54ff78 100644 --- a/include/openmc/tensor.h +++ b/include/openmc/tensor.h @@ -2,7 +2,7 @@ //! \brief Multi-dimensional tensor types for OpenMC. //! //! Provides Tensor (dynamic-rank), Fixed2D (stack-allocated), -//! and lightweight view types. Built on openmc::vector for GPU portability. +//! and lightweight view types. Built on vector for GPU portability. #ifndef OPENMC_TENSOR_H #define OPENMC_TENSOR_H @@ -29,7 +29,7 @@ namespace tensor { template class Tensor; -template +template class Fixed2D; //============================================================================== @@ -64,29 +64,29 @@ class View1D { public: using value_type = std::remove_const_t; - View1D(T* data, std::size_t size, std::size_t stride = 1) + View1D(T* data, size_t size, size_t stride = 1) : data_(data), size_(size), stride_(stride) {} - T& operator()(std::size_t i) { return data_[i * stride_]; } - const T& operator()(std::size_t i) const { return data_[i * stride_]; } - T& operator[](std::size_t i) { return data_[i * stride_]; } - const T& operator[](std::size_t i) const { return data_[i * stride_]; } + T& operator()(size_t i) { return data_[i * stride_]; } + const T& operator()(size_t i) const { return data_[i * stride_]; } + T& operator[](size_t i) { return data_[i * stride_]; } + const T& operator[](size_t i) const { return data_[i * stride_]; } - std::size_t size() const { return size_; } + size_t size() const { return size_; } T* data() { return data_; } const T* data() const { return data_; } - std::size_t stride() const { return stride_; } + size_t stride() const { return stride_; } - View1D slice(std::size_t start, std::size_t end) + View1D slice(size_t start, size_t end) { return {data_ + start * stride_, end - start, stride_}; } - View1D slice(std::size_t start, std::size_t end) const + View1D slice(size_t start, size_t end) const { return {data_ + start * stride_, end - start, stride_}; } - View1D slice(std::size_t start) + View1D slice(size_t start) { return {data_ + start * stride_, size_ - start, stride_}; } @@ -96,7 +96,7 @@ class View1D { auto operator=(U val) -> std::enable_if_t::value, View1D&> { - for (std::size_t i = 0; i < size_; ++i) + for (size_t i = 0; i < size_; ++i) data_[i * stride_] = val; return *this; } @@ -105,7 +105,7 @@ class View1D { View1D& operator=(std::initializer_list vals) { auto it = vals.begin(); - for (std::size_t i = 0; i < size_ && it != vals.end(); ++i, ++it) + for (size_t i = 0; i < size_ && it != vals.end(); ++i, ++it) data_[i * stride_] = *it; return *this; } @@ -120,7 +120,7 @@ class View1D { View1D& operator*=(value_type val) { - for (std::size_t i = 0; i < size_; ++i) + for (size_t i = 0; i < size_; ++i) data_[i * stride_] *= val; return *this; } @@ -128,7 +128,7 @@ class View1D { // Iterators class const_iterator { const T* ptr_; - std::size_t stride_; + size_t stride_; public: using iterator_category = std::random_access_iterator_tag; @@ -137,7 +137,7 @@ class View1D { using pointer = const T*; using reference = const T&; - const_iterator(const T* ptr, std::size_t stride) + const_iterator(const T* ptr, size_t stride) : ptr_(ptr), stride_(stride) {} const T& operator*() const { return *ptr_; } @@ -216,7 +216,7 @@ class View1D { class iterator { T* ptr_; - std::size_t stride_; + size_t stride_; public: using iterator_category = std::random_access_iterator_tag; @@ -225,7 +225,7 @@ class View1D { using pointer = T*; using reference = T&; - iterator(T* ptr, std::size_t stride) : ptr_(ptr), stride_(stride) {} + iterator(T* ptr, size_t stride) : ptr_(ptr), stride_(stride) {} T& operator*() { return *ptr_; } iterator& operator++() { @@ -291,8 +291,8 @@ class View1D { private: T* data_; - std::size_t size_; - std::size_t stride_; + size_t size_; + size_t stride_; }; //============================================================================== @@ -302,10 +302,10 @@ class View1D { template class ViewFlat { public: - ViewFlat(T* data, std::size_t size) : data_(data), size_(size) {} + ViewFlat(T* data, size_t size) : data_(data), size_(size) {} - T& operator()(std::size_t i) { return data_[i]; } - const T& operator()(std::size_t i) const { return data_[i]; } + T& operator()(size_t i) { return data_[i]; } + const T& operator()(size_t i) const { return data_[i]; } template auto operator=(U val) -> @@ -317,7 +317,7 @@ class ViewFlat { T* data() { return data_; } const T* data() const { return data_; } - std::size_t size() const { return size_; } + size_t size() const { return size_; } T* begin() { return data_; } T* end() { return data_ + size_; } @@ -326,13 +326,13 @@ class ViewFlat { private: T* data_; - std::size_t size_; + size_t size_; }; //============================================================================== // Tensor: dynamic-rank N-dimensional tensor. // -// Stores elements in a contiguous row-major openmc::vector> +// Stores elements in a contiguous row-major vector> // with a dynamic shape. //============================================================================== @@ -341,8 +341,8 @@ class Tensor { public: using value_type = T; using stored_type = storage_type; - using iterator = typename openmc::vector::iterator; - using const_iterator = typename openmc::vector::const_iterator; + using iterator = typename vector::iterator; + using const_iterator = typename vector::const_iterator; //-------------------------------------------------------------------------- // Constructors @@ -350,47 +350,47 @@ class Tensor { Tensor() = default; //! Construct with shape (uninitialized for arithmetic types via vector resize) - explicit Tensor(openmc::vector shape) + explicit Tensor(vector shape) : shape_(std::move(shape)), data_(compute_size()) {} //! Construct with shape and fill value - Tensor(openmc::vector shape, T fill) + Tensor(vector shape, T fill) : shape_(std::move(shape)), data_(compute_size(), fill) {} //! Construct from initializer_list shape - explicit Tensor(std::initializer_list shape) + explicit Tensor(std::initializer_list shape) : shape_(shape), data_(compute_size()) {} //! Construct from initializer_list shape with fill - Tensor(std::initializer_list shape, T fill) + Tensor(std::initializer_list shape, T fill) : shape_(shape), data_(compute_size(), fill) {} - //! 1D copy from openmc::vector (disabled when T=size_t to avoid ambiguity) + //! 1D copy from vector (disabled when T=size_t to avoid ambiguity) template::value>> - explicit Tensor(const openmc::vector& vec) + typename = std::enable_if_t::value>> + explicit Tensor(const vector& vec) : shape_({vec.size()}), data_(vec.begin(), vec.end()) {} //! 1D copy from std::vector (disabled when T=size_t) template::value>> + typename = std::enable_if_t::value>> explicit Tensor(const std::vector& vec) : shape_({vec.size()}), data_(vec.begin(), vec.end()) {} //! 1D copy from raw pointer + count - Tensor(const T* ptr, std::size_t count) + Tensor(const T* ptr, size_t count) : shape_({count}), data_(ptr, ptr + count) {} //! Copy from vector with explicit shape template - Tensor(const std::vector& vec, openmc::vector shape) + Tensor(const std::vector& vec, vector shape) : shape_(std::move(shape)), data_(vec.begin(), vec.end()) {} @@ -400,7 +400,7 @@ class Tensor { : shape_({v.size()}) { data_.resize(v.size()); - for (std::size_t i = 0; i < v.size(); ++i) + for (size_t i = 0; i < v.size(); ++i) data_[i] = v(i); } @@ -411,7 +411,7 @@ class Tensor { : shape_(other.shape()) { data_.resize(other.size()); - for (std::size_t i = 0; i < other.size(); ++i) + for (size_t i = 0; i < other.size(); ++i) data_[i] = static_cast(other.data()[i]); } @@ -425,7 +425,7 @@ class Tensor { { shape_ = other.shape(); data_.resize(other.size()); - for (std::size_t i = 0; i < other.size(); ++i) + for (size_t i = 0; i < other.size(); ++i) data_[i] = static_cast(other.data()[i]); return *this; } @@ -435,14 +435,14 @@ class Tensor { { shape_ = {v.size()}; data_.resize(v.size()); - for (std::size_t i = 0; i < v.size(); ++i) + for (size_t i = 0; i < v.size(); ++i) data_[i] = v(i); return *this; } //! Assignment from initializer_list of values (1D) template::value>> + typename = std::enable_if_t::value>> Tensor& operator=(std::initializer_list vals) { shape_ = {vals.size()}; @@ -455,12 +455,12 @@ class Tensor { stored_type* data() { return data_.data(); } const stored_type* data() const { return data_.data(); } - std::size_t size() const { return data_.size(); } - const openmc::vector& shape() const { return shape_; } - std::size_t shape(std::size_t dim) const { + size_t size() const { return data_.size(); } + const vector& shape() const { return shape_; } + size_t shape(size_t dim) const { return dim < shape_.size() ? shape_[dim] : 0; } - std::size_t ndim() const { return shape_.size(); } + size_t ndim() const { return shape_.size(); } bool empty() const { return data_.empty(); } //-------------------------------------------------------------------------- @@ -469,17 +469,17 @@ class Tensor { template stored_type& operator()(Indices... indices) { - return data_[offset(static_cast(indices)...)]; + return data_[offset(static_cast(indices)...)]; } template const stored_type& operator()(Indices... indices) const { - return data_[offset(static_cast(indices)...)]; + return data_[offset(static_cast(indices)...)]; } - stored_type& operator[](std::size_t i) { return data_[i]; } - const stored_type& operator[](std::size_t i) const { return data_[i]; } + stored_type& operator[](size_t i) { return data_[i]; } + const stored_type& operator[](size_t i) const { return data_[i]; } //-------------------------------------------------------------------------- // Iterators @@ -494,23 +494,23 @@ class Tensor { //-------------------------------------------------------------------------- // Mutation - void resize(const openmc::vector& shape) + void resize(const vector& shape) { shape_ = shape; data_.resize(compute_size()); } - void resize(std::initializer_list shape) + void resize(std::initializer_list shape) { shape_.assign(shape.begin(), shape.end()); data_.resize(compute_size()); } - void resize(const openmc::vector& shape) + void resize(const vector& shape) { shape_.clear(); for (auto d : shape) - shape_.push_back(static_cast(d)); + shape_.push_back(static_cast(d)); data_.resize(compute_size()); } @@ -519,7 +519,7 @@ class Tensor { { shape_.clear(); for (auto d : new_shape) - shape_.push_back(static_cast(d)); + shape_.push_back(static_cast(d)); } void fill(T val) { std::fill(data_.begin(), data_.end(), val); } @@ -528,43 +528,43 @@ class Tensor { // View accessors //! Row i of a 2D+ tensor (contiguous 1D view) - View1D row(std::size_t i) + View1D row(size_t i) { auto cols = shape_[shape_.size() - 1]; return {data_.data() + i * cols, cols, 1}; } - View1D row(std::size_t i) const + View1D row(size_t i) const { auto cols = shape_[shape_.size() - 1]; return {data_.data() + i * cols, cols, 1}; } //! Column j of a 2D tensor (strided 1D view) - View1D col(std::size_t j) + View1D col(size_t j) { return {data_.data() + j, shape_[0], shape_[1]}; } - View1D col(std::size_t j) const + View1D col(size_t j) const { return {data_.data() + j, shape_[0], shape_[1]}; } //! Subrange of a 1D tensor - View1D slice(std::size_t start, std::size_t end) + View1D slice(size_t start, size_t end) { return {data_.data() + start, end - start, 1}; } - View1D slice(std::size_t start, std::size_t end) const + View1D slice(size_t start, size_t end) const { return {data_.data() + start, end - start, 1}; } //! Subrange to end of a 1D tensor - View1D slice(std::size_t start) + View1D slice(size_t start) { return {data_.data() + start, data_.size() - start, 1}; } - View1D slice(std::size_t start) const + View1D slice(size_t start) const { return {data_.data() + start, data_.size() - start, 1}; } @@ -585,25 +585,25 @@ class Tensor { T sum() const { T s = T(0); - for (std::size_t i = 0; i < data_.size(); ++i) + for (size_t i = 0; i < data_.size(); ++i) s += data_[i]; return s; } //! Sum along an axis, reducing rank by 1 (defined out-of-line below) - Tensor sum(std::size_t axis) const; + Tensor sum(size_t axis) const; T prod() const { T p = T(1); - for (std::size_t i = 0; i < data_.size(); ++i) + for (size_t i = 0; i < data_.size(); ++i) p *= data_[i]; return p; } bool any() const { - for (std::size_t i = 0; i < data_.size(); ++i) + for (size_t i = 0; i < data_.size(); ++i) if (data_[i]) return true; return false; @@ -611,15 +611,15 @@ class Tensor { bool all() const { - for (std::size_t i = 0; i < data_.size(); ++i) + for (size_t i = 0; i < data_.size(); ++i) if (!data_[i]) return false; return true; } - std::size_t argmin() const + size_t argmin() const { - return static_cast( + return static_cast( std::distance(data_.data(), std::min_element(data_.data(), data_.data() + data_.size()))); } @@ -627,19 +627,19 @@ class Tensor { //-------------------------------------------------------------------------- // Flip - Tensor flip(std::size_t axis) const + Tensor flip(size_t axis) const { Tensor r(shape_); if (shape_.size() == 2) { auto s0 = shape_[0]; auto s1 = shape_[1]; if (axis == 0) { - for (std::size_t i = 0; i < s0; ++i) - for (std::size_t j = 0; j < s1; ++j) + for (size_t i = 0; i < s0; ++i) + for (size_t j = 0; j < s1; ++j) r.data_[i * s1 + j] = data_[(s0 - 1 - i) * s1 + j]; } else { - for (std::size_t i = 0; i < s0; ++i) - for (std::size_t j = 0; j < s1; ++j) + for (size_t i = 0; i < s0; ++i) + for (size_t j = 0; j < s1; ++j) r.data_[i * s1 + j] = data_[i * s1 + (s1 - 1 - j)]; } } @@ -679,7 +679,7 @@ class Tensor { Tensor& operator+=(const Tensor& o) { - for (std::size_t i = 0; i < data_.size(); ++i) + for (size_t i = 0; i < data_.size(); ++i) data_[i] += o.data_[i]; return *this; } @@ -690,21 +690,21 @@ class Tensor { Tensor operator+(const Tensor& o) const { Tensor r(shape_); - for (std::size_t i = 0; i < data_.size(); ++i) + for (size_t i = 0; i < data_.size(); ++i) r.data_[i] = data_[i] + o.data_[i]; return r; } Tensor operator-(const Tensor& o) const { Tensor r(shape_); - for (std::size_t i = 0; i < data_.size(); ++i) + for (size_t i = 0; i < data_.size(); ++i) r.data_[i] = data_[i] - o.data_[i]; return r; } Tensor operator/(const Tensor& o) const { Tensor r(shape_); - for (std::size_t i = 0; i < data_.size(); ++i) + for (size_t i = 0; i < data_.size(); ++i) r.data_[i] = data_[i] / o.data_[i]; return r; } @@ -715,21 +715,21 @@ class Tensor { Tensor operator+(T val) const { Tensor r(shape_); - for (std::size_t i = 0; i < data_.size(); ++i) + for (size_t i = 0; i < data_.size(); ++i) r.data_[i] = data_[i] + val; return r; } Tensor operator-(T val) const { Tensor r(shape_); - for (std::size_t i = 0; i < data_.size(); ++i) + for (size_t i = 0; i < data_.size(); ++i) r.data_[i] = data_[i] - val; return r; } Tensor operator*(T val) const { Tensor r(shape_); - for (std::size_t i = 0; i < data_.size(); ++i) + for (size_t i = 0; i < data_.size(); ++i) r.data_[i] = data_[i] * val; return r; } @@ -740,63 +740,63 @@ class Tensor { Tensor operator<=(T val) const { Tensor r(shape_); - for (std::size_t i = 0; i < data_.size(); ++i) + for (size_t i = 0; i < data_.size(); ++i) r.data()[i] = data_[i] <= val; return r; } Tensor operator<(T val) const { Tensor r(shape_); - for (std::size_t i = 0; i < data_.size(); ++i) + for (size_t i = 0; i < data_.size(); ++i) r.data()[i] = data_[i] < val; return r; } Tensor operator>=(T val) const { Tensor r(shape_); - for (std::size_t i = 0; i < data_.size(); ++i) + for (size_t i = 0; i < data_.size(); ++i) r.data()[i] = data_[i] >= val; return r; } Tensor operator>(T val) const { Tensor r(shape_); - for (std::size_t i = 0; i < data_.size(); ++i) + for (size_t i = 0; i < data_.size(); ++i) r.data()[i] = data_[i] > val; return r; } Tensor operator<(const Tensor& o) const { Tensor r(shape_); - for (std::size_t i = 0; i < data_.size(); ++i) + for (size_t i = 0; i < data_.size(); ++i) r.data()[i] = data_[i] < o.data_[i]; return r; } private: - std::size_t compute_size() const + size_t compute_size() const { - std::size_t s = 1; + size_t s = 1; for (auto d : shape_) s *= d; return s; } //! Row-major offset calculations for operator() - std::size_t offset(std::size_t i0) const { return i0; } + size_t offset(size_t i0) const { return i0; } - std::size_t offset(std::size_t i0, std::size_t i1) const + size_t offset(size_t i0, size_t i1) const { return i0 * shape_[1] + i1; } - std::size_t offset(std::size_t i0, std::size_t i1, std::size_t i2) const + size_t offset(size_t i0, size_t i1, size_t i2) const { return (i0 * shape_[1] + i1) * shape_[2] + i2; } - std::size_t offset( - std::size_t i0, std::size_t i1, std::size_t i2, std::size_t i3) const + size_t offset( + size_t i0, size_t i1, size_t i2, size_t i3) const { return ((i0 * shape_[1] + i1) * shape_[2] + i2) * shape_[3] + i3; } @@ -804,8 +804,8 @@ class Tensor { //-------------------------------------------------------------------------- // Data members - openmc::vector shape_; - openmc::vector> data_; + vector shape_; + vector> data_; }; //============================================================================== @@ -832,7 +832,7 @@ template operator*(const Tensor& a, const Tensor& b) { Tensor r(a.shape()); - for (std::size_t i = 0; i < a.size(); ++i) + for (size_t i = 0; i < a.size(); ++i) r.data()[i] = static_cast(a.data()[i]) * static_cast(b.data()[i]); return r; @@ -843,7 +843,7 @@ template operator/(const Tensor& a, const Tensor& b) { Tensor r(a.shape()); - for (std::size_t i = 0; i < a.size(); ++i) + for (size_t i = 0; i < a.size(); ++i) r.data()[i] = static_cast(a.data()[i]) / static_cast(b.data()[i]); return r; @@ -857,7 +857,7 @@ template template View1D& View1D::operator=(const Tensor& other) { - for (std::size_t i = 0; i < size_; ++i) + for (size_t i = 0; i < size_; ++i) data_[i * stride_] = static_cast(other.data()[i]); return *this; } @@ -866,7 +866,7 @@ template template View1D& View1D::operator+=(const Tensor& o) { - for (std::size_t i = 0; i < size_; ++i) + for (size_t i = 0; i < size_; ++i) data_[i * stride_] += o.data()[i]; return *this; } @@ -879,23 +879,23 @@ View1D& View1D::operator+=(const Tensor& o) //============================================================================== template -Tensor Tensor::sum(std::size_t axis) const +Tensor Tensor::sum(size_t axis) const { - std::size_t ndims = shape_.size(); + size_t ndims = shape_.size(); if (ndims == 2) { // 2D sum along axis -> 1D auto s0 = shape_[0], s1 = shape_[1]; if (axis == 0) { Tensor r({s1}, T(0)); - for (std::size_t i = 0; i < s0; ++i) - for (std::size_t j = 0; j < s1; ++j) + for (size_t i = 0; i < s0; ++i) + for (size_t j = 0; j < s1; ++j) r.data()[j] += data_[i * s1 + j]; return r; } else { Tensor r({s0}, T(0)); - for (std::size_t i = 0; i < s0; ++i) - for (std::size_t j = 0; j < s1; ++j) + for (size_t i = 0; i < s0; ++i) + for (size_t j = 0; j < s1; ++j) r.data()[i] += data_[i * s1 + j]; return r; } @@ -904,23 +904,23 @@ Tensor Tensor::sum(std::size_t axis) const auto s0 = shape_[0], s1 = shape_[1], s2 = shape_[2]; if (axis == 0) { Tensor r({s1, s2}, T(0)); - for (std::size_t i = 0; i < s0; ++i) - for (std::size_t j = 0; j < s1; ++j) - for (std::size_t k = 0; k < s2; ++k) + for (size_t i = 0; i < s0; ++i) + for (size_t j = 0; j < s1; ++j) + for (size_t k = 0; k < s2; ++k) r.data()[j * s2 + k] += data_[(i * s1 + j) * s2 + k]; return r; } else if (axis == 1) { Tensor r({s0, s2}, T(0)); - for (std::size_t i = 0; i < s0; ++i) - for (std::size_t j = 0; j < s1; ++j) - for (std::size_t k = 0; k < s2; ++k) + for (size_t i = 0; i < s0; ++i) + for (size_t j = 0; j < s1; ++j) + for (size_t k = 0; k < s2; ++k) r.data()[i * s2 + k] += data_[(i * s1 + j) * s2 + k]; return r; } else { Tensor r({s0, s1}, T(0)); - for (std::size_t i = 0; i < s0; ++i) - for (std::size_t j = 0; j < s1; ++j) - for (std::size_t k = 0; k < s2; ++k) + for (size_t i = 0; i < s0; ++i) + for (size_t j = 0; j < s1; ++j) + for (size_t k = 0; k < s2; ++k) r.data()[i * s1 + j] += data_[(i * s1 + j) * s2 + k]; return r; } @@ -929,37 +929,37 @@ Tensor Tensor::sum(std::size_t axis) const auto s0 = shape_[0], s1 = shape_[1], s2 = shape_[2], s3 = shape_[3]; if (axis == 3) { Tensor r({s0, s1, s2}, T(0)); - for (std::size_t i = 0; i < s0; ++i) - for (std::size_t j = 0; j < s1; ++j) - for (std::size_t k = 0; k < s2; ++k) - for (std::size_t l = 0; l < s3; ++l) + for (size_t i = 0; i < s0; ++i) + for (size_t j = 0; j < s1; ++j) + for (size_t k = 0; k < s2; ++k) + for (size_t l = 0; l < s3; ++l) r.data()[(i * s1 + j) * s2 + k] += data_[((i * s1 + j) * s2 + k) * s3 + l]; return r; } else if (axis == 2) { Tensor r({s0, s1, s3}, T(0)); - for (std::size_t i = 0; i < s0; ++i) - for (std::size_t j = 0; j < s1; ++j) - for (std::size_t k = 0; k < s2; ++k) - for (std::size_t l = 0; l < s3; ++l) + for (size_t i = 0; i < s0; ++i) + for (size_t j = 0; j < s1; ++j) + for (size_t k = 0; k < s2; ++k) + for (size_t l = 0; l < s3; ++l) r.data()[(i * s1 + j) * s3 + l] += data_[((i * s1 + j) * s2 + k) * s3 + l]; return r; } else if (axis == 1) { Tensor r({s0, s2, s3}, T(0)); - for (std::size_t i = 0; i < s0; ++i) - for (std::size_t j = 0; j < s1; ++j) - for (std::size_t k = 0; k < s2; ++k) - for (std::size_t l = 0; l < s3; ++l) + for (size_t i = 0; i < s0; ++i) + for (size_t j = 0; j < s1; ++j) + for (size_t k = 0; k < s2; ++k) + for (size_t l = 0; l < s3; ++l) r.data()[(i * s2 + k) * s3 + l] += data_[((i * s1 + j) * s2 + k) * s3 + l]; return r; } else { Tensor r({s1, s2, s3}, T(0)); - for (std::size_t i = 0; i < s0; ++i) - for (std::size_t j = 0; j < s1; ++j) - for (std::size_t k = 0; k < s2; ++k) - for (std::size_t l = 0; l < s3; ++l) + for (size_t i = 0; i < s0; ++i) + for (size_t j = 0; j < s1; ++j) + for (size_t k = 0; k < s2; ++k) + for (size_t l = 0; l < s3; ++l) r.data()[(j * s2 + k) * s3 + l] += data_[((i * s1 + j) * s2 + k) * s3 + l]; return r; @@ -967,33 +967,33 @@ Tensor Tensor::sum(std::size_t axis) const } // Fallback: general case for any rank (should not be reached in practice) - openmc::vector out_shape; - for (std::size_t d = 0; d < ndims; ++d) + vector out_shape; + for (size_t d = 0; d < ndims; ++d) if (d != axis) out_shape.push_back(shape_[d]); Tensor result(out_shape, T(0)); - openmc::vector strides(ndims); + vector strides(ndims); strides[ndims - 1] = 1; for (int d = static_cast(ndims) - 2; d >= 0; --d) strides[d] = strides[d + 1] * shape_[d + 1]; - std::size_t out_ndims = out_shape.size(); - openmc::vector out_strides(out_ndims); + size_t out_ndims = out_shape.size(); + vector out_strides(out_ndims); if (out_ndims > 0) { out_strides[out_ndims - 1] = 1; for (int d = static_cast(out_ndims) - 2; d >= 0; --d) out_strides[d] = out_strides[d + 1] * out_shape[d + 1]; } - std::size_t total = data_.size(); - for (std::size_t flat = 0; flat < total; ++flat) { - std::size_t remaining = flat; - std::size_t out_flat = 0; - std::size_t out_d = 0; - for (std::size_t d = 0; d < ndims; ++d) { - std::size_t idx = remaining / strides[d]; + size_t total = data_.size(); + for (size_t flat = 0; flat < total; ++flat) { + size_t remaining = flat; + size_t out_flat = 0; + size_t out_d = 0; + for (size_t d = 0; d < ndims; ++d) { + size_t idx = remaining / strides[d]; remaining %= strides[d]; if (d != axis) { out_flat += idx * out_strides[out_d]; @@ -1010,7 +1010,7 @@ Tensor Tensor::sum(std::size_t axis) const // Fixed2D: compile-time fixed 2D tensor. //============================================================================== -template +template class Fixed2D { public: using value_type = T; @@ -1018,20 +1018,20 @@ class Fixed2D { template T& operator()(I0 i, I1 j) { - return data_[static_cast(i) * C + - static_cast(j)]; + return data_[static_cast(i) * C + + static_cast(j)]; } template const T& operator()(I0 i, I1 j) const { - return data_[static_cast(i) * C + - static_cast(j)]; + return data_[static_cast(i) * C + + static_cast(j)]; } T* data() { return data_; } const T* data() const { return data_; } - constexpr std::size_t size() const { return R * C; } - std::array shape() const { return {R, C}; } + constexpr size_t size() const { return R * C; } + std::array shape() const { return {R, C}; } void fill(T val) { std::fill(data_, data_ + R * C, val); } @@ -1041,8 +1041,8 @@ class Fixed2D { const T* end() const { return data_ + R * C; } //! Column view - View1D col(std::size_t j) { return {data_ + j, R, C}; } - View1D col(std::size_t j) const { return {data_ + j, R, C}; } + View1D col(size_t j) { return {data_ + j, R, C}; } + View1D col(size_t j) const { return {data_ + j, R, C}; } //! Flat view ViewFlat flat() { return {data_, R * C}; } @@ -1058,14 +1058,14 @@ class Fixed2D { // zeros template -Tensor zeros(std::initializer_list shape) +Tensor zeros(std::initializer_list shape) { - openmc::vector s(shape); + vector s(shape); return Tensor(std::move(s), T(0)); } template -Tensor zeros(const openmc::vector& shape) +Tensor zeros(const vector& shape) { return Tensor(shape, T(0)); } @@ -1086,14 +1086,14 @@ Tensor full_like(const Tensor& o, V val) // linspace template -Tensor linspace(T start, T stop, std::size_t n) +Tensor linspace(T start, T stop, size_t n) { Tensor result({n}); if (n < 2) { result[0] = start; return result; } - for (std::size_t i = 0; i < n; ++i) { + for (size_t i = 0; i < n; ++i) { result[i] = start + static_cast(i) * (stop - start) / static_cast(n - 1); } @@ -1104,7 +1104,7 @@ Tensor linspace(T start, T stop, std::size_t n) template Tensor concatenate(const Tensor& a, const Tensor& b) { - std::size_t total = a.size() + b.size(); + size_t total = a.size() + b.size(); Tensor result({total}); std::copy(a.data(), a.data() + a.size(), result.data()); std::copy(b.data(), b.data() + b.size(), result.data() + a.size()); @@ -1116,7 +1116,7 @@ template Tensor log(const Tensor& a) { Tensor r(a.shape()); - for (std::size_t i = 0; i < a.size(); ++i) + for (size_t i = 0; i < a.size(); ++i) r.data()[i] = std::log(a.data()[i]); return r; } @@ -1125,7 +1125,7 @@ template Tensor abs(const Tensor& a) { Tensor r(a.shape()); - for (std::size_t i = 0; i < a.size(); ++i) + for (size_t i = 0; i < a.size(); ++i) r.data()[i] = std::abs(a.data()[i]); return r; } @@ -1136,7 +1136,7 @@ Tensor where( const Tensor& cond, const Tensor& true_val, V false_val) { Tensor r(cond.shape()); - for (std::size_t i = 0; i < cond.size(); ++i) + for (size_t i = 0; i < cond.size(); ++i) r.data()[i] = cond.data()[i] ? true_val.data()[i] : static_cast(false_val); return r; @@ -1149,7 +1149,7 @@ Tensor nan_to_num(const Tensor& a, T nan_val = T(0), T neginf_val = std::numeric_limits::lowest()) { Tensor r(a.shape()); - for (std::size_t i = 0; i < a.size(); ++i) { + for (size_t i = 0; i < a.size(); ++i) { T val = a.data()[i]; if (std::isnan(val)) r.data()[i] = nan_val; @@ -1173,7 +1173,7 @@ struct is_tensor : std::false_type {}; template struct is_tensor> : std::true_type {}; -template +template struct is_tensor> : std::true_type {}; } // namespace tensor diff --git a/src/hdf5_interface.cpp b/src/hdf5_interface.cpp index a1a59b8dfe0..5b77e6a98b6 100644 --- a/src/hdf5_interface.cpp +++ b/src/hdf5_interface.cpp @@ -472,7 +472,7 @@ void read_dataset( vector shape = object_shape(dset); // Resize tensor and read data directly - openmc::vector tshape(shape.begin(), shape.end()); + vector tshape(shape.begin(), shape.end()); arr.resize(tshape); // Read data from dataset diff --git a/src/nuclide.cpp b/src/nuclide.cpp index 0c560437a6f..10d3065f4eb 100644 --- a/src/nuclide.cpp +++ b/src/nuclide.cpp @@ -361,7 +361,7 @@ void Nuclide::create_derived( for (const auto& grid : grid_) { // Allocate and initialize cross section xs_.emplace_back( - openmc::vector{grid.energy.size(), 5}, 0.0); + vector{grid.energy.size(), 5}, 0.0); } reaction_index_.fill(C_NONE); diff --git a/src/tallies/tally.cpp b/src/tallies/tally.cpp index 5a97bab9af1..983ee72f98e 100644 --- a/src/tallies/tally.cpp +++ b/src/tallies/tally.cpp @@ -888,7 +888,7 @@ int Tally::score_index(const std::string& score) const tensor::Tensor Tally::get_reshaped_data() const { - openmc::vector shape; + vector shape; for (auto f : filters()) { shape.push_back(model::tally_filters[f]->n_bins()); } From 933296f2c2062f5cbfc352e0654ca45ebc036845 Mon Sep 17 00:00:00 2001 From: John Tramm Date: Wed, 11 Feb 2026 14:00:35 -0600 Subject: [PATCH 13/51] code cleanup - making variable names and comments match new API --- include/openmc/hdf5_interface.h | 28 +++++----- include/openmc/mesh.h | 4 +- .../openmc/random_ray/flat_source_domain.h | 2 +- include/openmc/xml_interface.h | 2 +- src/cmfd_solver.cpp | 2 +- src/distribution_energy.cpp | 4 +- src/hdf5_interface.cpp | 6 +-- src/mesh.cpp | 20 +++---- src/secondary_correlated.cpp | 4 +- src/secondary_kalbach.cpp | 4 +- src/urr.cpp | 2 +- src/weight_windows.cpp | 16 +++--- src/xsdata.cpp | 52 +++++++++---------- 13 files changed, 73 insertions(+), 73 deletions(-) diff --git a/include/openmc/hdf5_interface.h b/include/openmc/hdf5_interface.h index 6162fc7f4c3..84941eb7cd2 100644 --- a/include/openmc/hdf5_interface.h +++ b/include/openmc/hdf5_interface.h @@ -167,17 +167,17 @@ void read_attribute(hid_t obj_id, const char* name, vector& vec) // Tensor version template -void read_attribute(hid_t obj_id, const char* name, tensor::Tensor& arr) +void read_attribute(hid_t obj_id, const char* name, tensor::Tensor& tensor) { - // Get shape of attribute array + // Get shape of attribute auto shape = attribute_shape(obj_id, name); // Resize tensor and read data directly vector tshape(shape.begin(), shape.end()); - arr.resize(tshape); + tensor.resize(tshape); // Read data from attribute - read_attr(obj_id, name, H5TypeMap::type_id, arr.data()); + read_attr(obj_id, name, H5TypeMap::type_id, tensor.data()); } // overload for std::string @@ -284,31 +284,31 @@ void read_dataset( } template -void read_dataset(hid_t dset, tensor::Tensor& arr, bool indep = false) +void read_dataset(hid_t dset, tensor::Tensor& tensor, bool indep = false) { // Get shape of dataset vector shape = object_shape(dset); // Resize tensor and read data directly vector tshape(shape.begin(), shape.end()); - arr.resize(tshape); + tensor.resize(tshape); // Read data from dataset read_dataset_lowlevel( - dset, nullptr, H5TypeMap::type_id, H5S_ALL, indep, arr.data()); + dset, nullptr, H5TypeMap::type_id, H5S_ALL, indep, tensor.data()); } template<> void read_dataset( - hid_t dset, tensor::Tensor>& arr, bool indep); + hid_t dset, tensor::Tensor>& tensor, bool indep); template void read_dataset( - hid_t obj_id, const char* name, tensor::Tensor& arr, bool indep = false) + hid_t obj_id, const char* name, tensor::Tensor& tensor, bool indep = false) { - // Open dataset and read array + // Open dataset and read tensor hid_t dset = open_dataset(obj_id, name); - read_dataset(dset, arr, indep); + read_dataset(dset, tensor, indep); close_dataset(dset); } @@ -325,19 +325,19 @@ inline void read_dataset( template inline void read_dataset_as_shape( - hid_t obj_id, const char* name, tensor::Tensor& arr, bool indep = false) + hid_t obj_id, const char* name, tensor::Tensor& tensor, bool indep = false) { hid_t dset = open_dataset(obj_id, name); // Read data directly into pre-shaped tensor read_dataset_lowlevel( - dset, nullptr, H5TypeMap::type_id, H5S_ALL, indep, arr.data()); + dset, nullptr, H5TypeMap::type_id, H5S_ALL, indep, tensor.data()); close_dataset(dset); } template -inline void read_nd_vector(hid_t obj_id, const char* name, +inline void read_nd_tensor(hid_t obj_id, const char* name, tensor::Tensor& result, bool must_have = false) { if (object_exists(obj_id, name)) { diff --git a/include/openmc/mesh.h b/include/openmc/mesh.h index e38b7b3c791..cbdd8440c17 100644 --- a/include/openmc/mesh.h +++ b/include/openmc/mesh.h @@ -419,8 +419,8 @@ class StructuredMesh : public Mesh { //! Get a label for the mesh bin std::string bin_label(int bin) const override; - //! Get shape as tensor - tensor::Tensor get_x_shape() const; + //! Get mesh dimensions as a tensor + tensor::Tensor get_shape_tensor() const; double volume(int bin) const override { diff --git a/include/openmc/random_ray/flat_source_domain.h b/include/openmc/random_ray/flat_source_domain.h index 3829185e897..d14d845b5ac 100644 --- a/include/openmc/random_ray/flat_source_domain.h +++ b/include/openmc/random_ray/flat_source_domain.h @@ -177,7 +177,7 @@ class FlatSourceDomain { // Volumes for each tally and bin/score combination. This intermediate data // structure is used when tallying quantities that must be normalized by // volume (i.e., flux). The vector is index by tally index, while the inner 2D - // xtensor is indexed by bin index and score index in a similar manner to the + // tensor is indexed by bin index and score index in a similar manner to the // results tensor in the Tally class, though without the third dimension, as // SUM and SUM_SQ do not need to be tracked. vector> tally_volumes_; diff --git a/include/openmc/xml_interface.h b/include/openmc/xml_interface.h index 7910679cfd2..e0baa2eeaee 100644 --- a/include/openmc/xml_interface.h +++ b/include/openmc/xml_interface.h @@ -41,7 +41,7 @@ vector get_node_array( } template -tensor::Tensor get_node_xarray( +tensor::Tensor get_node_tensor( pugi::xml_node node, const char* name, bool lowercase = false) { vector v = get_node_array(node, name, lowercase); diff --git a/src/cmfd_solver.cpp b/src/cmfd_solver.cpp index 17977e4fb68..152abe19ff2 100644 --- a/src/cmfd_solver.cpp +++ b/src/cmfd_solver.cpp @@ -553,7 +553,7 @@ void free_memory_cmfd() cmfd::indices.clear(); cmfd::egrid.clear(); - // Resize xtensors to be empty + // Resize tensors to be empty cmfd::indexmap.resize({0}); // Set pointers to null diff --git a/src/distribution_energy.cpp b/src/distribution_energy.cpp index ff32b57e85d..65e6afca002 100644 --- a/src/distribution_energy.cpp +++ b/src/distribution_energy.cpp @@ -63,8 +63,8 @@ ContinuousTabular::ContinuousTabular(hid_t group) tensor::Tensor temp; read_attribute(dset, "interpolation", temp); - auto temp_b = temp.row(0); // view of breakpoints - auto temp_i = temp.row(1); // view of interpolation parameters + auto temp_b = temp.row(0); // breakpoints + auto temp_i = temp.row(1); // interpolation parameters std::copy(temp_b.begin(), temp_b.end(), std::back_inserter(breakpoints_)); for (const auto i : temp_i) diff --git a/src/hdf5_interface.cpp b/src/hdf5_interface.cpp index 5b77e6a98b6..642365c5bbb 100644 --- a/src/hdf5_interface.cpp +++ b/src/hdf5_interface.cpp @@ -466,17 +466,17 @@ void read_dataset_lowlevel(hid_t obj_id, const char* name, hid_t mem_type_id, template<> void read_dataset( - hid_t dset, tensor::Tensor>& arr, bool indep) + hid_t dset, tensor::Tensor>& tensor, bool indep) { // Get shape of dataset vector shape = object_shape(dset); // Resize tensor and read data directly vector tshape(shape.begin(), shape.end()); - arr.resize(tshape); + tensor.resize(tshape); // Read data from dataset - read_complex(dset, nullptr, reinterpret_cast*>(arr.data()), indep); + read_complex(dset, nullptr, reinterpret_cast*>(tensor.data()), indep); } void read_double(hid_t obj_id, const char* name, double* buffer, bool indep) diff --git a/src/mesh.cpp b/src/mesh.cpp index d60cc6ddc6d..1a960e113f3 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -766,7 +766,7 @@ std::string StructuredMesh::bin_label(int bin) const } } -tensor::Tensor StructuredMesh::get_x_shape() const +tensor::Tensor StructuredMesh::get_shape_tensor() const { return tensor::Tensor(shape_.data(), static_cast(n_dimension_)); } @@ -1394,7 +1394,7 @@ RegularMesh::RegularMesh(pugi::xml_node node) : StructuredMesh {node} fatal_error("Must specify on a regular mesh."); } - tensor::Tensor shape = get_node_xarray(node, "dimension"); + tensor::Tensor shape = get_node_tensor(node, "dimension"); int n = n_dimension_ = shape.size(); if (n != 1 && n != 2 && n != 3) { fatal_error("Mesh must be one, two, or three dimensions."); @@ -1404,7 +1404,7 @@ RegularMesh::RegularMesh(pugi::xml_node node) : StructuredMesh {node} // Check for lower-left coordinates if (check_for_node(node, "lower_left")) { // Read mesh lower-left corner location - lower_left_ = get_node_xarray(node, "lower_left"); + lower_left_ = get_node_tensor(node, "lower_left"); } else { fatal_error("Must specify on a mesh."); } @@ -1415,11 +1415,11 @@ RegularMesh::RegularMesh(pugi::xml_node node) : StructuredMesh {node} fatal_error("Cannot specify both and on a mesh."); } - width_ = get_node_xarray(node, "width"); + width_ = get_node_tensor(node, "width"); } else if (check_for_node(node, "upper_right")) { - upper_right_ = get_node_xarray(node, "upper_right"); + upper_right_ = get_node_tensor(node, "upper_right"); } else { fatal_error("Must specify either or on a mesh."); @@ -1552,7 +1552,7 @@ std::pair, vector> RegularMesh::plot( void RegularMesh::to_hdf5_inner(hid_t mesh_group) const { - write_dataset(mesh_group, "dimension", get_x_shape()); + write_dataset(mesh_group, "dimension", get_shape_tensor()); write_dataset(mesh_group, "lower_left", lower_left_); write_dataset(mesh_group, "upper_right", upper_right_); write_dataset(mesh_group, "width", width_); @@ -2702,15 +2702,15 @@ extern "C" int openmc_regular_mesh_set_params( if (ll && ur) { m->lower_left_ = tensor::Tensor(ll, n); m->upper_right_ = tensor::Tensor(ur, n); - m->width_ = (m->upper_right_ - m->lower_left_) / m->get_x_shape(); + m->width_ = (m->upper_right_ - m->lower_left_) / m->get_shape_tensor(); } else if (ll && width) { m->lower_left_ = tensor::Tensor(ll, n); m->width_ = tensor::Tensor(width, n); - m->upper_right_ = m->lower_left_ + m->get_x_shape() * m->width_; + m->upper_right_ = m->lower_left_ + m->get_shape_tensor() * m->width_; } else if (ur && width) { m->upper_right_ = tensor::Tensor(ur, n); m->width_ = tensor::Tensor(width, n); - m->lower_left_ = m->upper_right_ - m->get_x_shape() * m->width_; + m->lower_left_ = m->upper_right_ - m->get_shape_tensor() * m->width_; } else { set_errmsg("At least two parameters must be specified."); return OPENMC_E_INVALID_ARGUMENT; @@ -2720,7 +2720,7 @@ extern "C" int openmc_regular_mesh_set_params( // TODO: incorporate this into method in RegularMesh that can be called from // here and from constructor - m->volume_frac_ = 1.0 / m->get_x_shape().prod(); + m->volume_frac_ = 1.0 / m->get_shape_tensor().prod(); m->element_volume_ = 1.0; for (int i = 0; i < m->n_dimension_; i++) { m->element_volume_ *= m->width_[i]; diff --git a/src/secondary_correlated.cpp b/src/secondary_correlated.cpp index d5d55bd7b82..08a6ae59b27 100644 --- a/src/secondary_correlated.cpp +++ b/src/secondary_correlated.cpp @@ -28,8 +28,8 @@ CorrelatedAngleEnergy::CorrelatedAngleEnergy(hid_t group) tensor::Tensor temp; read_attribute(dset, "interpolation", temp); - auto temp_b = temp.row(0); // view of breakpoints - auto temp_i = temp.row(1); // view of interpolation parameters + auto temp_b = temp.row(0); // breakpoints + auto temp_i = temp.row(1); // interpolation parameters std::copy(temp_b.begin(), temp_b.end(), std::back_inserter(breakpoints_)); for (const auto i : temp_i) diff --git a/src/secondary_kalbach.cpp b/src/secondary_kalbach.cpp index a1f44d9ee42..5ed53fc29bb 100644 --- a/src/secondary_kalbach.cpp +++ b/src/secondary_kalbach.cpp @@ -29,8 +29,8 @@ KalbachMann::KalbachMann(hid_t group) tensor::Tensor temp; read_attribute(dset, "interpolation", temp); - auto temp_b = temp.row(0); // view of breakpoints - auto temp_i = temp.row(1); // view of interpolation parameters + auto temp_b = temp.row(0); // breakpoints + auto temp_i = temp.row(1); // interpolation parameters std::copy(temp_b.begin(), temp_b.end(), std::back_inserter(breakpoints_)); for (const auto i : temp_i) diff --git a/src/urr.cpp b/src/urr.cpp index 41dfdf9a59f..b791eecb0da 100644 --- a/src/urr.cpp +++ b/src/urr.cpp @@ -38,7 +38,7 @@ UrrData::UrrData(hid_t group_id) xs_values_.resize({n_energy, n_cdf_values}); // Now fill in the values. Using manual loops here since we might - // not have fancy xtensor slicing code written for GPU tensors. + // not have fancy tensor slicing code written for GPU tensors. // The below enum gives how URR tables are laid out in our HDF5 tables. enum class URRTableParam { CUM_PROB, diff --git a/src/weight_windows.cpp b/src/weight_windows.cpp index 3874fa77569..d1f057e4594 100644 --- a/src/weight_windows.cpp +++ b/src/weight_windows.cpp @@ -537,16 +537,16 @@ void WeightWindows::update_weights(const Tally* tally, const std::string& value, /////////////////////////// // Extract tally data // - // At the end of this section, the mean and rel_err array - // is a 2D view of tally data (n_e_groups, n_mesh_bins) + // At the end of this section, mean and rel_err are + // 2D tensors of tally data (n_e_groups, n_mesh_bins) // /////////////////////////// - // build a shape for a view of the tally results, this will always be + // build a shape for the tally results, this will always be // dimension 5 (3 filter dimensions, 1 score dimension, 1 results dimension) - // Look for the size of the last dimension of the results array - const auto& results_arr = tally->results(); - const int results_dim = static_cast(results_arr.shape(2)); + // Look for the size of the last dimension of the results tensor + const auto& results = tally->results(); + const int results_dim = static_cast(results.shape(2)); std::array shape = {1, 1, 1, tally->n_scores(), results_dim}; // set the shape for the filters applied on the tally @@ -634,8 +634,8 @@ void WeightWindows::update_weights(const Tally* tally, const std::string& value, // Compute flat filter combination index (row-major over filter dims) int flat = idx[0] * stride0 + idx[1] * stride1 + idx[2]; - sum(e, m) = results_arr(flat, score_index, i_sum); - sum_sq(e, m) = results_arr(flat, score_index, i_sum_sq); + sum(e, m) = results(flat, score_index, i_sum); + sum_sq(e, m) = results(flat, score_index, i_sum_sq); } } int n = tally->n_realizations_; diff --git a/src/xsdata.cpp b/src/xsdata.cpp index 840e2482417..158f13b2c1c 100644 --- a/src/xsdata.cpp +++ b/src/xsdata.cpp @@ -89,9 +89,9 @@ void XsData::from_hdf5(hid_t xsdata_grp, bool fissionable, fission_from_hdf5(xsdata_grp, n_ang, is_isotropic); } // Get the non-fission-specific data - read_nd_vector(xsdata_grp, "decay-rate", decay_rate); - read_nd_vector(xsdata_grp, "absorption", absorption, true); - read_nd_vector(xsdata_grp, "inverse-velocity", inverse_velocity); + read_nd_tensor(xsdata_grp, "decay-rate", decay_rate); + read_nd_tensor(xsdata_grp, "absorption", absorption, true); + read_nd_tensor(xsdata_grp, "inverse-velocity", inverse_velocity); // Get scattering data scatter_from_hdf5( @@ -104,7 +104,7 @@ void XsData::from_hdf5(hid_t xsdata_grp, bool fissionable, // Get or calculate the total x/s if (object_exists(xsdata_grp, "total")) { - read_nd_vector(xsdata_grp, "total", total); + read_nd_tensor(xsdata_grp, "total", total); } else { for (size_t a = 0; a < n_ang; a++) { for (size_t gin = 0; gin < energy_groups; gin++) { @@ -128,7 +128,7 @@ void XsData::fission_vector_beta_from_hdf5( // Get chi tensor::Tensor temp_chi({n_ang, n_g_}, 0.); - read_nd_vector(xsdata_grp, "chi", temp_chi, true); + read_nd_tensor(xsdata_grp, "chi", temp_chi, true); // Normalize chi so it sums to 1 over outgoing groups for each angle for (size_t a = 0; a < n_ang; a++) { @@ -153,7 +153,7 @@ void XsData::fission_vector_beta_from_hdf5( // Get nu-fission tensor::Tensor temp_nufiss({n_ang, n_g_}, 0.); - read_nd_vector(xsdata_grp, "nu-fission", temp_nufiss, true); + read_nd_tensor(xsdata_grp, "nu-fission", temp_nufiss, true); // Get beta (strategy will depend upon the number of dimensions in beta) hid_t beta_dset = open_dataset(xsdata_grp, "beta"); @@ -164,7 +164,7 @@ void XsData::fission_vector_beta_from_hdf5( ndim_target += 2; if (beta_ndims == ndim_target) { tensor::Tensor temp_beta({n_ang, n_dg_}, 0.); - read_nd_vector(xsdata_grp, "beta", temp_beta, true); + read_nd_tensor(xsdata_grp, "beta", temp_beta, true); // prompt_nu_fission = (1 - sum_of_beta) * nu_fission auto beta_sum = temp_beta.sum(1); @@ -180,7 +180,7 @@ void XsData::fission_vector_beta_from_hdf5( delayed_nu_fission(a, d, g) = temp_beta(a, d) * temp_nufiss(a, g); } else if (beta_ndims == ndim_target + 1) { tensor::Tensor temp_beta({n_ang, n_dg_, n_g_}, 0.); - read_nd_vector(xsdata_grp, "beta", temp_beta, true); + read_nd_tensor(xsdata_grp, "beta", temp_beta, true); // prompt_nu_fission = (1 - sum_of_beta) * nu_fission // Here beta is energy-dependent, so sum over delayed groups (axis 1) @@ -204,7 +204,7 @@ void XsData::fission_vector_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) // Get chi-prompt tensor::Tensor temp_chi_p({n_ang, n_g_}, 0.); - read_nd_vector(xsdata_grp, "chi-prompt", temp_chi_p, true); + read_nd_tensor(xsdata_grp, "chi-prompt", temp_chi_p, true); // Normalize prompt chi so it sums to 1 over outgoing groups for each angle for (size_t a = 0; a < n_ang; a++) { @@ -215,7 +215,7 @@ void XsData::fission_vector_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) // Get chi-delayed tensor::Tensor temp_chi_d({n_ang, n_dg_, n_g_}, 0.); - read_nd_vector(xsdata_grp, "chi-delayed", temp_chi_d, true); + read_nd_tensor(xsdata_grp, "chi-delayed", temp_chi_d, true); // Normalize delayed chi so it sums to 1 over outgoing groups for each // angle and delayed group @@ -240,8 +240,8 @@ void XsData::fission_vector_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) chi_delayed(a, d, gin, gout) = temp_chi_d(a, d, gout); // Get prompt and delayed nu-fission directly - read_nd_vector(xsdata_grp, "prompt-nu-fission", prompt_nu_fission, true); - read_nd_vector(xsdata_grp, "delayed-nu-fission", delayed_nu_fission, true); + read_nd_tensor(xsdata_grp, "prompt-nu-fission", prompt_nu_fission, true); + read_nd_tensor(xsdata_grp, "delayed-nu-fission", delayed_nu_fission, true); } void XsData::fission_vector_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang) @@ -251,7 +251,7 @@ void XsData::fission_vector_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang) // Get chi tensor::Tensor temp_chi({n_ang, n_g_}, 0.); - read_nd_vector(xsdata_grp, "chi", temp_chi, true); + read_nd_tensor(xsdata_grp, "chi", temp_chi, true); // Normalize chi so it sums to 1 over outgoing groups for each angle for (size_t a = 0; a < n_ang; a++) { @@ -267,7 +267,7 @@ void XsData::fission_vector_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang) chi_prompt(a, gin, gout) = temp_chi(a, gout); // Get nu-fission directly - read_nd_vector(xsdata_grp, "nu-fission", prompt_nu_fission, true); + read_nd_tensor(xsdata_grp, "nu-fission", prompt_nu_fission, true); } //============================================================================== @@ -279,7 +279,7 @@ void XsData::fission_matrix_beta_from_hdf5( // Get nu-fission matrix tensor::Tensor temp_matrix({n_ang, n_g_, n_g_}, 0.); - read_nd_vector(xsdata_grp, "nu-fission", temp_matrix, true); + read_nd_tensor(xsdata_grp, "nu-fission", temp_matrix, true); // Get beta (strategy will depend upon the number of dimensions in beta) hid_t beta_dset = open_dataset(xsdata_grp, "beta"); @@ -290,7 +290,7 @@ void XsData::fission_matrix_beta_from_hdf5( ndim_target += 2; if (beta_ndims == ndim_target) { tensor::Tensor temp_beta({n_ang, n_dg_}, 0.); - read_nd_vector(xsdata_grp, "beta", temp_beta, true); + read_nd_tensor(xsdata_grp, "beta", temp_beta, true); auto beta_sum = temp_beta.sum(1); auto matrix_gout_sum = temp_matrix.sum(2); @@ -325,7 +325,7 @@ void XsData::fission_matrix_beta_from_hdf5( } else if (beta_ndims == ndim_target + 1) { tensor::Tensor temp_beta({n_ang, n_dg_, n_g_}, 0.); - read_nd_vector(xsdata_grp, "beta", temp_beta, true); + read_nd_tensor(xsdata_grp, "beta", temp_beta, true); auto beta_sum = temp_beta.sum(1); auto matrix_gout_sum = temp_matrix.sum(2); @@ -387,7 +387,7 @@ void XsData::fission_matrix_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) // Get the prompt nu-fission matrix tensor::Tensor temp_matrix_p({n_ang, n_g_, n_g_}, 0.); - read_nd_vector(xsdata_grp, "prompt-nu-fission", temp_matrix_p, true); + read_nd_tensor(xsdata_grp, "prompt-nu-fission", temp_matrix_p, true); // prompt_nu_fission is the sum over outgoing groups prompt_nu_fission = temp_matrix_p.sum(2); @@ -401,7 +401,7 @@ void XsData::fission_matrix_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) // Get the delayed nu-fission matrix tensor::Tensor temp_matrix_d({n_ang, n_dg_, n_g_, n_g_}, 0.); - read_nd_vector(xsdata_grp, "delayed-nu-fission", temp_matrix_d, true); + read_nd_tensor(xsdata_grp, "delayed-nu-fission", temp_matrix_d, true); // delayed_nu_fission is the sum over outgoing groups delayed_nu_fission = temp_matrix_d.sum(3); @@ -422,7 +422,7 @@ void XsData::fission_matrix_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang) // Get nu-fission matrix tensor::Tensor temp_matrix({n_ang, n_g_, n_g_}, 0.); - read_nd_vector(xsdata_grp, "nu-fission", temp_matrix, true); + read_nd_tensor(xsdata_grp, "nu-fission", temp_matrix, true); // prompt_nu_fission is the sum over outgoing groups prompt_nu_fission = temp_matrix.sum(2); @@ -441,8 +441,8 @@ void XsData::fission_from_hdf5( hid_t xsdata_grp, size_t n_ang, bool is_isotropic) { // Get the fission and kappa_fission data xs; these are optional - read_nd_vector(xsdata_grp, "fission", fission); - read_nd_vector(xsdata_grp, "kappa-fission", kappa_fission); + read_nd_tensor(xsdata_grp, "fission", fission); + read_nd_tensor(xsdata_grp, "kappa-fission", kappa_fission); // Get the data; the strategy for doing so depends on if the data is provided // as a nu-fission matrix or a set of chi and nu-fission vectors @@ -490,9 +490,9 @@ void XsData::scatter_from_hdf5(hid_t xsdata_grp, size_t n_ang, // Get the outgoing group boundary indices tensor::Tensor gmin({n_ang, n_g_}, 0.); - read_nd_vector(scatt_grp, "g_min", gmin, true); + read_nd_tensor(scatt_grp, "g_min", gmin, true); tensor::Tensor gmax({n_ang, n_g_}, 0.); - read_nd_vector(scatt_grp, "g_max", gmax, true); + read_nd_tensor(scatt_grp, "g_max", gmax, true); // Make gmin and gmax start from 0 vice 1 as they do in the library gmin -= 1; @@ -504,7 +504,7 @@ void XsData::scatter_from_hdf5(hid_t xsdata_grp, size_t n_ang, double_4dvec input_scatt(n_ang, double_3dvec(n_g_)); tensor::Tensor temp_arr({length}, 0.); - read_nd_vector(scatt_grp, "scatter_matrix", temp_arr, true); + read_nd_tensor(scatt_grp, "scatter_matrix", temp_arr, true); // Compare the number of orders given with the max order of the problem; // strip off the superfluous orders if needed @@ -536,7 +536,7 @@ void XsData::scatter_from_hdf5(hid_t xsdata_grp, size_t n_ang, double_3dvec temp_mult(n_ang, double_2dvec(n_g_)); if (object_exists(scatt_grp, "multiplicity_matrix")) { temp_arr.resize({length / order_data}); - read_nd_vector(scatt_grp, "multiplicity_matrix", temp_arr); + read_nd_tensor(scatt_grp, "multiplicity_matrix", temp_arr); // convert the flat temp_arr to a jagged array for passing to scatt data size_t temp_idx = 0; From 7c96034f59b915fa670ce977fdd1753c2e66476b Mon Sep 17 00:00:00 2001 From: John Tramm Date: Wed, 11 Feb 2026 14:16:13 -0600 Subject: [PATCH 14/51] Removal of development jargon from code comments --- include/openmc/tensor.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/include/openmc/tensor.h b/include/openmc/tensor.h index 074da54ff78..6ea1ef92778 100644 --- a/include/openmc/tensor.h +++ b/include/openmc/tensor.h @@ -2,7 +2,7 @@ //! \brief Multi-dimensional tensor types for OpenMC. //! //! Provides Tensor (dynamic-rank), Fixed2D (stack-allocated), -//! and lightweight view types. Built on vector for GPU portability. +//! and lightweight view types (View1D, ViewFlat). #ifndef OPENMC_TENSOR_H #define OPENMC_TENSOR_H @@ -873,9 +873,6 @@ View1D& View1D::operator+=(const Tensor& o) //============================================================================== // Tensor::sum(axis) — reduces one dimension -// -// Uses explicit nested loops for each rank combination to ensure identical -// floating-point accumulation order with the Phase 1 implementation. //============================================================================== template From d638322b28a11ea1a19c76fb20acaad47609be1f Mon Sep 17 00:00:00 2001 From: John Tramm Date: Wed, 11 Feb 2026 14:22:18 -0600 Subject: [PATCH 15/51] greatly simplified the sum function --- include/openmc/tensor.h | 132 +++++----------------------------------- 1 file changed, 16 insertions(+), 116 deletions(-) diff --git a/include/openmc/tensor.h b/include/openmc/tensor.h index 6ea1ef92778..8a8f6961b4e 100644 --- a/include/openmc/tensor.h +++ b/include/openmc/tensor.h @@ -878,127 +878,27 @@ View1D& View1D::operator+=(const Tensor& o) template Tensor Tensor::sum(size_t axis) const { - size_t ndims = shape_.size(); - - if (ndims == 2) { - // 2D sum along axis -> 1D - auto s0 = shape_[0], s1 = shape_[1]; - if (axis == 0) { - Tensor r({s1}, T(0)); - for (size_t i = 0; i < s0; ++i) - for (size_t j = 0; j < s1; ++j) - r.data()[j] += data_[i * s1 + j]; - return r; - } else { - Tensor r({s0}, T(0)); - for (size_t i = 0; i < s0; ++i) - for (size_t j = 0; j < s1; ++j) - r.data()[i] += data_[i * s1 + j]; - return r; - } - } else if (ndims == 3) { - // 3D sum along axis -> 2D - auto s0 = shape_[0], s1 = shape_[1], s2 = shape_[2]; - if (axis == 0) { - Tensor r({s1, s2}, T(0)); - for (size_t i = 0; i < s0; ++i) - for (size_t j = 0; j < s1; ++j) - for (size_t k = 0; k < s2; ++k) - r.data()[j * s2 + k] += data_[(i * s1 + j) * s2 + k]; - return r; - } else if (axis == 1) { - Tensor r({s0, s2}, T(0)); - for (size_t i = 0; i < s0; ++i) - for (size_t j = 0; j < s1; ++j) - for (size_t k = 0; k < s2; ++k) - r.data()[i * s2 + k] += data_[(i * s1 + j) * s2 + k]; - return r; - } else { - Tensor r({s0, s1}, T(0)); - for (size_t i = 0; i < s0; ++i) - for (size_t j = 0; j < s1; ++j) - for (size_t k = 0; k < s2; ++k) - r.data()[i * s1 + j] += data_[(i * s1 + j) * s2 + k]; - return r; - } - } else if (ndims == 4) { - // 4D sum along axis -> 3D - auto s0 = shape_[0], s1 = shape_[1], s2 = shape_[2], s3 = shape_[3]; - if (axis == 3) { - Tensor r({s0, s1, s2}, T(0)); - for (size_t i = 0; i < s0; ++i) - for (size_t j = 0; j < s1; ++j) - for (size_t k = 0; k < s2; ++k) - for (size_t l = 0; l < s3; ++l) - r.data()[(i * s1 + j) * s2 + k] += - data_[((i * s1 + j) * s2 + k) * s3 + l]; - return r; - } else if (axis == 2) { - Tensor r({s0, s1, s3}, T(0)); - for (size_t i = 0; i < s0; ++i) - for (size_t j = 0; j < s1; ++j) - for (size_t k = 0; k < s2; ++k) - for (size_t l = 0; l < s3; ++l) - r.data()[(i * s1 + j) * s3 + l] += - data_[((i * s1 + j) * s2 + k) * s3 + l]; - return r; - } else if (axis == 1) { - Tensor r({s0, s2, s3}, T(0)); - for (size_t i = 0; i < s0; ++i) - for (size_t j = 0; j < s1; ++j) - for (size_t k = 0; k < s2; ++k) - for (size_t l = 0; l < s3; ++l) - r.data()[(i * s2 + k) * s3 + l] += - data_[((i * s1 + j) * s2 + k) * s3 + l]; - return r; - } else { - Tensor r({s1, s2, s3}, T(0)); - for (size_t i = 0; i < s0; ++i) - for (size_t j = 0; j < s1; ++j) - for (size_t k = 0; k < s2; ++k) - for (size_t l = 0; l < s3; ++l) - r.data()[(j * s2 + k) * s3 + l] += - data_[((i * s1 + j) * s2 + k) * s3 + l]; - return r; - } - } - - // Fallback: general case for any rank (should not be reached in practice) + // Build output shape (all dims except the summed axis) vector out_shape; - for (size_t d = 0; d < ndims; ++d) + for (size_t d = 0; d < shape_.size(); ++d) if (d != axis) out_shape.push_back(shape_[d]); - Tensor result(out_shape, T(0)); - - vector strides(ndims); - strides[ndims - 1] = 1; - for (int d = static_cast(ndims) - 2; d >= 0; --d) - strides[d] = strides[d + 1] * shape_[d + 1]; - - size_t out_ndims = out_shape.size(); - vector out_strides(out_ndims); - if (out_ndims > 0) { - out_strides[out_ndims - 1] = 1; - for (int d = static_cast(out_ndims) - 2; d >= 0; --d) - out_strides[d] = out_strides[d + 1] * out_shape[d + 1]; - } + // Split dimensions into three zones: outer | axis | inner + size_t outer_size = 1; + for (size_t d = 0; d < axis; ++d) + outer_size *= shape_[d]; + size_t axis_size = shape_[axis]; + size_t inner_size = 1; + for (size_t d = axis + 1; d < shape_.size(); ++d) + inner_size *= shape_[d]; - size_t total = data_.size(); - for (size_t flat = 0; flat < total; ++flat) { - size_t remaining = flat; - size_t out_flat = 0; - size_t out_d = 0; - for (size_t d = 0; d < ndims; ++d) { - size_t idx = remaining / strides[d]; - remaining %= strides[d]; - if (d != axis) { - out_flat += idx * out_strides[out_d]; - ++out_d; - } - } - result.data()[out_flat] += data_[flat]; - } + Tensor result(out_shape, T(0)); + for (size_t o = 0; o < outer_size; ++o) + for (size_t a = 0; a < axis_size; ++a) + for (size_t i = 0; i < inner_size; ++i) + result.data()[o * inner_size + i] += + data_[(o * axis_size + a) * inner_size + i]; return result; } From d27d4f1fd7ecfec250094ae881fe62f546c6d8c8 Mon Sep 17 00:00:00 2001 From: John Tramm Date: Wed, 11 Feb 2026 14:31:34 -0600 Subject: [PATCH 16/51] Made several functions rank agnostic rather than assuming a max rank --- include/openmc/tensor.h | 57 +++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 34 deletions(-) diff --git a/include/openmc/tensor.h b/include/openmc/tensor.h index 8a8f6961b4e..e0340d4547e 100644 --- a/include/openmc/tensor.h +++ b/include/openmc/tensor.h @@ -469,13 +469,21 @@ class Tensor { template stored_type& operator()(Indices... indices) { - return data_[offset(static_cast(indices)...)]; + const size_t idx[] = {static_cast(indices)...}; + size_t off = 0; + for (size_t d = 0; d < sizeof...(Indices); ++d) + off = off * shape_[d] + idx[d]; + return data_[off]; } template const stored_type& operator()(Indices... indices) const { - return data_[offset(static_cast(indices)...)]; + const size_t idx[] = {static_cast(indices)...}; + size_t off = 0; + for (size_t d = 0; d < sizeof...(Indices); ++d) + off = off * shape_[d] + idx[d]; + return data_[off]; } stored_type& operator[](size_t i) { return data_[i]; } @@ -629,20 +637,20 @@ class Tensor { Tensor flip(size_t axis) const { + size_t outer_size = 1; + for (size_t d = 0; d < axis; ++d) + outer_size *= shape_[d]; + size_t axis_size = shape_[axis]; + size_t inner_size = 1; + for (size_t d = axis + 1; d < shape_.size(); ++d) + inner_size *= shape_[d]; + Tensor r(shape_); - if (shape_.size() == 2) { - auto s0 = shape_[0]; - auto s1 = shape_[1]; - if (axis == 0) { - for (size_t i = 0; i < s0; ++i) - for (size_t j = 0; j < s1; ++j) - r.data_[i * s1 + j] = data_[(s0 - 1 - i) * s1 + j]; - } else { - for (size_t i = 0; i < s0; ++i) - for (size_t j = 0; j < s1; ++j) - r.data_[i * s1 + j] = data_[i * s1 + (s1 - 1 - j)]; - } - } + for (size_t o = 0; o < outer_size; ++o) + for (size_t a = 0; a < axis_size; ++a) + for (size_t i = 0; i < inner_size; ++i) + r.data_[(o * axis_size + (axis_size - 1 - a)) * inner_size + i] = + data_[(o * axis_size + a) * inner_size + i]; return r; } @@ -782,25 +790,6 @@ class Tensor { return s; } - //! Row-major offset calculations for operator() - size_t offset(size_t i0) const { return i0; } - - size_t offset(size_t i0, size_t i1) const - { - return i0 * shape_[1] + i1; - } - - size_t offset(size_t i0, size_t i1, size_t i2) const - { - return (i0 * shape_[1] + i1) * shape_[2] + i2; - } - - size_t offset( - size_t i0, size_t i1, size_t i2, size_t i3) const - { - return ((i0 * shape_[1] + i1) * shape_[2] + i2) * shape_[3] + i3; - } - //-------------------------------------------------------------------------- // Data members From 0871ab42cc09f05f16c000bc65b5736e0aa5cbd6 Mon Sep 17 00:00:00 2001 From: John Tramm Date: Wed, 11 Feb 2026 14:38:50 -0600 Subject: [PATCH 17/51] fixed some issues with includes --- include/openmc/tensor.h | 2 -- src/bank.cpp | 1 + src/mesh.cpp | 1 + src/random_ray/flat_source_domain.cpp | 1 + src/random_ray/random_ray.cpp | 2 ++ src/tallies/tally_scoring.cpp | 1 + 6 files changed, 6 insertions(+), 2 deletions(-) diff --git a/include/openmc/tensor.h b/include/openmc/tensor.h index e0340d4547e..b34f68ce386 100644 --- a/include/openmc/tensor.h +++ b/include/openmc/tensor.h @@ -12,11 +12,9 @@ #include #include #include -#include #include #include #include -#include #include namespace openmc { diff --git a/src/bank.cpp b/src/bank.cpp index 33790379b85..5afdc2ecab7 100644 --- a/src/bank.cpp +++ b/src/bank.cpp @@ -7,6 +7,7 @@ #include "openmc/vector.h" #include +#include namespace openmc { diff --git a/src/mesh.cpp b/src/mesh.cpp index 1a960e113f3..5bb59d19e54 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -6,6 +6,7 @@ #define _USE_MATH_DEFINES // to make M_PI declared in Intel and MSVC compilers #include // for ceil #include // for size_t +#include // for accumulate #include #ifdef _MSC_VER diff --git a/src/random_ray/flat_source_domain.cpp b/src/random_ray/flat_source_domain.cpp index 5074452ecbc..9c5e1847aa4 100644 --- a/src/random_ray/flat_source_domain.cpp +++ b/src/random_ray/flat_source_domain.cpp @@ -18,6 +18,7 @@ #include "openmc/weight_windows.h" #include +#include namespace openmc { diff --git a/src/random_ray/random_ray.cpp b/src/random_ray/random_ray.cpp index 1b61d8c2072..87de6e84add 100644 --- a/src/random_ray/random_ray.cpp +++ b/src/random_ray/random_ray.cpp @@ -10,6 +10,8 @@ #include "openmc/settings.h" #include "openmc/simulation.h" +#include + #include "openmc/distribution_spatial.h" #include "openmc/random_dist.h" #include "openmc/source.h" diff --git a/src/tallies/tally_scoring.cpp b/src/tallies/tally_scoring.cpp index 51b5d9ffcc5..1e32db1d32e 100644 --- a/src/tallies/tally_scoring.cpp +++ b/src/tallies/tally_scoring.cpp @@ -20,6 +20,7 @@ #include "openmc/tallies/filter_delayedgroup.h" #include "openmc/tallies/filter_energy.h" +#include #include namespace openmc { From e9249dfb1619ad8b7eef1d8167a27c04a29a1acc Mon Sep 17 00:00:00 2001 From: John Tramm Date: Wed, 11 Feb 2026 15:22:10 -0600 Subject: [PATCH 18/51] Making the naming of classes more clear: Tensor, StaticTensor2D, and View1D --- include/openmc/hdf5_interface.h | 2 +- include/openmc/tallies/tally.h | 2 +- include/openmc/tensor.h | 59 ++++++++------------------------- src/tallies/tally.cpp | 2 +- 4 files changed, 16 insertions(+), 49 deletions(-) diff --git a/include/openmc/hdf5_interface.h b/include/openmc/hdf5_interface.h index 84941eb7cd2..36cfb273c8a 100644 --- a/include/openmc/hdf5_interface.h +++ b/include/openmc/hdf5_interface.h @@ -452,7 +452,7 @@ inline void write_dataset( false, buffer.data()); } -// Template for Tensor and Fixed2D +// Template for Tensor and StaticTensor2D template>::value>> inline void write_dataset( diff --git a/include/openmc/tallies/tally.h b/include/openmc/tallies/tally.h index 3cc65b85409..81a012db821 100644 --- a/include/openmc/tallies/tally.h +++ b/include/openmc/tallies/tally.h @@ -219,7 +219,7 @@ extern vector time_grid; namespace simulation { //! Global tallies (such as k-effective estimators) -extern tensor::Fixed2D +extern tensor::StaticTensor2D global_tallies; //! Number of realizations for global tallies diff --git a/include/openmc/tensor.h b/include/openmc/tensor.h index b34f68ce386..9badb79b9a9 100644 --- a/include/openmc/tensor.h +++ b/include/openmc/tensor.h @@ -1,8 +1,8 @@ //! \file tensor.h //! \brief Multi-dimensional tensor types for OpenMC. //! -//! Provides Tensor (dynamic-rank), Fixed2D (stack-allocated), -//! and lightweight view types (View1D, ViewFlat). +//! Provides Tensor (dynamic-rank), StaticTensor2D (stack-allocated), +//! and View1D (non-owning 1D view). #ifndef OPENMC_TENSOR_H #define OPENMC_TENSOR_H @@ -28,7 +28,7 @@ template class Tensor; template -class Fixed2D; +class StaticTensor2D; //============================================================================== // Storage type mapping @@ -293,39 +293,6 @@ class View1D { size_t stride_; }; -//============================================================================== -// ViewFlat: a flat view of all elements of a tensor. -//============================================================================== - -template -class ViewFlat { -public: - ViewFlat(T* data, size_t size) : data_(data), size_(size) {} - - T& operator()(size_t i) { return data_[i]; } - const T& operator()(size_t i) const { return data_[i]; } - - template - auto operator=(U val) -> - std::enable_if_t::value, ViewFlat&> - { - std::fill(data_, data_ + size_, static_cast(val)); - return *this; - } - - T* data() { return data_; } - const T* data() const { return data_; } - size_t size() const { return size_; } - - T* begin() { return data_; } - T* end() { return data_ + size_; } - const T* begin() const { return data_; } - const T* end() const { return data_ + size_; } - -private: - T* data_; - size_t size_; -}; //============================================================================== // Tensor: dynamic-rank N-dimensional tensor. @@ -576,13 +543,13 @@ class Tensor { } //! Flat 1D view of all elements - ViewFlat flat() + View1D flat() { - return {data_.data(), data_.size()}; + return {data_.data(), data_.size(), 1}; } - ViewFlat flat() const + View1D flat() const { - return {data_.data(), data_.size()}; + return {data_.data(), data_.size(), 1}; } //-------------------------------------------------------------------------- @@ -891,11 +858,11 @@ Tensor Tensor::sum(size_t axis) const } //============================================================================== -// Fixed2D: compile-time fixed 2D tensor. +// StaticTensor2D: compile-time fixed 2D tensor. //============================================================================== template -class Fixed2D { +class StaticTensor2D { public: using value_type = T; @@ -929,8 +896,8 @@ class Fixed2D { View1D col(size_t j) const { return {data_ + j, R, C}; } //! Flat view - ViewFlat flat() { return {data_, R * C}; } - ViewFlat flat() const { return {data_, R * C}; } + View1D flat() { return {data_, R * C, 1}; } + View1D flat() const { return {data_, R * C, 1}; } private: T data_[R * C] = {}; @@ -1049,7 +1016,7 @@ Tensor nan_to_num(const Tensor& a, T nan_val = T(0), // Type traits //============================================================================== -//! Type trait that is true for Tensor and Fixed2D. +//! Type trait that is true for Tensor and StaticTensor2D. //! Used by hdf5_interface.h to select the correct write_dataset overload. template struct is_tensor : std::false_type {}; @@ -1058,7 +1025,7 @@ template struct is_tensor> : std::true_type {}; template -struct is_tensor> : std::true_type {}; +struct is_tensor> : std::true_type {}; } // namespace tensor } // namespace openmc diff --git a/src/tallies/tally.cpp b/src/tallies/tally.cpp index 983ee72f98e..f8ab34db735 100644 --- a/src/tallies/tally.cpp +++ b/src/tallies/tally.cpp @@ -67,7 +67,7 @@ vector time_grid; } // namespace model namespace simulation { -tensor::Fixed2D global_tallies; +tensor::StaticTensor2D global_tallies; int32_t n_realizations {0}; } // namespace simulation From 25a22bf7929a0b3eb483240789cf3c40b2ecf168 Mon Sep 17 00:00:00 2001 From: John Tramm Date: Wed, 11 Feb 2026 15:38:34 -0600 Subject: [PATCH 19/51] Replaced View1D with a true n-dimensional View class. --- include/openmc/tensor.h | 496 +++++++++++++++++++++++++++------------- src/tallies/tally.cpp | 19 +- 2 files changed, 346 insertions(+), 169 deletions(-) diff --git a/include/openmc/tensor.h b/include/openmc/tensor.h index 9badb79b9a9..20234e5fb99 100644 --- a/include/openmc/tensor.h +++ b/include/openmc/tensor.h @@ -1,8 +1,8 @@ //! \file tensor.h //! \brief Multi-dimensional tensor types for OpenMC. //! -//! Provides Tensor (dynamic-rank), StaticTensor2D (stack-allocated), -//! and View1D (non-owning 1D view). +//! Provides Tensor (dynamic-rank owning), StaticTensor2D +//! (stack-allocated), and View (non-owning N-dimensional view). #ifndef OPENMC_TENSOR_H #define OPENMC_TENSOR_H @@ -51,82 +51,180 @@ template using storage_type = typename storage_type_map::type; //============================================================================== -// View1D: a read/write view of one row, column, or slice of a tensor. +// View: a non-owning N-dimensional view into a tensor's storage. // -// Holds a pointer, element count, and stride into the parent tensor's -// storage — no allocation or copy. +// Holds a base pointer, shape, and strides (in elements). Supports arbitrary +// rank: 1D views for rows/slices, 2D views via slice_at(), etc. //============================================================================== template -class View1D { +class View { public: using value_type = std::remove_const_t; - View1D(T* data, size_t size, size_t stride = 1) - : data_(data), size_(size), stride_(stride) + View(T* data, vector shape, vector strides) + : data_(data), shape_(std::move(shape)), strides_(std::move(strides)) {} - T& operator()(size_t i) { return data_[i * stride_]; } - const T& operator()(size_t i) const { return data_[i * stride_]; } - T& operator[](size_t i) { return data_[i * stride_]; } - const T& operator[](size_t i) const { return data_[i * stride_]; } + //-------------------------------------------------------------------------- + // Indexing - size_t size() const { return size_; } + //! Multi-index element access (1D, 2D, 3D, ...) + template + T& operator()(Indices... indices) + { + const size_t idx[] = {static_cast(indices)...}; + size_t off = 0; + for (size_t d = 0; d < sizeof...(Indices); ++d) + off += idx[d] * strides_[d]; + return data_[off]; + } + + template + const T& operator()(Indices... indices) const + { + const size_t idx[] = {static_cast(indices)...}; + size_t off = 0; + for (size_t d = 0; d < sizeof...(Indices); ++d) + off += idx[d] * strides_[d]; + return data_[off]; + } + + //! Flat logical index (row-major order) + T& operator[](size_t i) { return data_[flat_to_offset(i)]; } + const T& operator[](size_t i) const { return data_[flat_to_offset(i)]; } + + //-------------------------------------------------------------------------- + // Shape queries + + size_t size() const + { + size_t s = 1; + for (auto d : shape_) + s *= d; + return s; + } + size_t ndim() const { return shape_.size(); } + size_t shape(size_t axis) const { return shape_[axis]; } + const vector& shape_vec() const { return shape_; } + const vector& strides_vec() const { return strides_; } T* data() { return data_; } const T* data() const { return data_; } - size_t stride() const { return stride_; } - View1D slice(size_t start, size_t end) + //-------------------------------------------------------------------------- + // Sub-view methods + + //! Fix one axis at a given index, returning an (N-1)-dimensional view + View slice_at(size_t axis, size_t idx) + { + vector new_shape; + vector new_strides; + new_shape.reserve(shape_.size() - 1); + new_strides.reserve(shape_.size() - 1); + T* new_data = data_ + idx * strides_[axis]; + for (size_t d = 0; d < shape_.size(); ++d) { + if (d != axis) { + new_shape.push_back(shape_[d]); + new_strides.push_back(strides_[d]); + } + } + return {new_data, std::move(new_shape), std::move(new_strides)}; + } + + View slice_at(size_t axis, size_t idx) const + { + vector new_shape; + vector new_strides; + new_shape.reserve(shape_.size() - 1); + new_strides.reserve(shape_.size() - 1); + const T* new_data = data_ + idx * strides_[axis]; + for (size_t d = 0; d < shape_.size(); ++d) { + if (d != axis) { + new_shape.push_back(shape_[d]); + new_strides.push_back(strides_[d]); + } + } + return {new_data, std::move(new_shape), std::move(new_strides)}; + } + + //! Row i (fix first axis) — shorthand for slice_at(0, i) + View row(size_t i) { return slice_at(0, i); } + View row(size_t i) const { return slice_at(0, i); } + + //! Column j (fix second axis) — shorthand for slice_at(1, j) + View col(size_t j) { return slice_at(1, j); } + View col(size_t j) const { return slice_at(1, j); } + + //! 1D subrange [start, end) + View slice(size_t start, size_t end) { - return {data_ + start * stride_, end - start, stride_}; + return {data_ + start * strides_[0], {end - start}, {strides_[0]}}; } - View1D slice(size_t start, size_t end) const + View slice(size_t start, size_t end) const { - return {data_ + start * stride_, end - start, stride_}; + return {data_ + start * strides_[0], {end - start}, {strides_[0]}}; } - View1D slice(size_t start) + + //! 1D subrange [start, size) + View slice(size_t start) { - return {data_ + start * stride_, size_ - start, stride_}; + return {data_ + start * strides_[0], {shape_[0] - start}, {strides_[0]}}; } - // Assignment from scalar + //-------------------------------------------------------------------------- + // Assignment operators + + //! Fill all elements with a scalar template auto operator=(U val) -> - std::enable_if_t::value, View1D&> + std::enable_if_t::value, View&> { - for (size_t i = 0; i < size_; ++i) - data_[i * stride_] = val; + size_t n = size(); + for (size_t i = 0; i < n; ++i) + data_[flat_to_offset(i)] = val; return *this; } - // Assignment from initializer_list - View1D& operator=(std::initializer_list vals) + //! Assignment from initializer_list (for 1D views) + View& operator=(std::initializer_list vals) { auto it = vals.begin(); - for (size_t i = 0; i < size_ && it != vals.end(); ++i, ++it) - data_[i * stride_] = *it; + for (size_t i = 0; i < size() && it != vals.end(); ++i, ++it) + data_[flat_to_offset(i)] = *it; return *this; } - // Assignment from Tensor (deferred, defined after Tensor) + //! Assignment from Tensor (deferred, defined after Tensor) template - View1D& operator=(const Tensor& other); + View& operator=(const Tensor& other); - // Compound assignment from Tensor (deferred) + //! Compound addition from Tensor (deferred) template - View1D& operator+=(const Tensor& o); + View& operator+=(const Tensor& o); - View1D& operator*=(value_type val) + //! Compound multiply by scalar + View& operator*=(value_type val) { - for (size_t i = 0; i < size_; ++i) - data_[i * stride_] *= val; + size_t n = size(); + for (size_t i = 0; i < n; ++i) + data_[flat_to_offset(i)] *= val; return *this; } + //-------------------------------------------------------------------------- // Iterators + // + // Lightweight row-major iterator. Stores a flat logical position and + // converts to a physical offset on each dereference via flat_to_offset(). + // For contiguous 1D views (the common case) the divmod chain reduces to + // a single multiply-by-1, which the compiler optimizes away. + class const_iterator { - const T* ptr_; - size_t stride_; + const T* base_; + size_t count_; + const size_t* shape_; + const size_t* strides_; + size_t ndim_; public: using iterator_category = std::random_access_iterator_tag; @@ -135,86 +233,111 @@ class View1D { using pointer = const T*; using reference = const T&; - const_iterator(const T* ptr, size_t stride) - : ptr_(ptr), stride_(stride) + const_iterator( + const T* base, size_t count, const View* v) + : base_(base) + , count_(count) + , shape_(v->shape_.data()) + , strides_(v->strides_.data()) + , ndim_(v->shape_.size()) {} - const T& operator*() const { return *ptr_; } + + const T& operator*() const { return base_[offset()]; } + const T& operator[](difference_type n) const + { + return base_[offset_of(count_ + n)]; + } const_iterator& operator++() { - ptr_ += stride_; + ++count_; return *this; } const_iterator operator++(int) { auto tmp = *this; - ptr_ += stride_; + ++count_; return tmp; } const_iterator& operator--() { - ptr_ -= stride_; + --count_; return *this; } const_iterator operator+(difference_type n) const { - return const_iterator(ptr_ + n * stride_, stride_); + auto tmp = *this; + tmp.count_ += n; + return tmp; } const_iterator operator-(difference_type n) const { - return const_iterator(ptr_ - n * stride_, stride_); + auto tmp = *this; + tmp.count_ -= n; + return tmp; } - difference_type operator-(const const_iterator& other) const + difference_type operator-(const const_iterator& o) const { - return (ptr_ - other.ptr_) / static_cast(stride_); + return static_cast(count_) - + static_cast(o.count_); } - bool operator==(const const_iterator& other) const + const_iterator& operator+=(difference_type n) { - return ptr_ == other.ptr_; + count_ += n; + return *this; } - bool operator!=(const const_iterator& other) const + const_iterator& operator-=(difference_type n) { - return ptr_ != other.ptr_; + count_ -= n; + return *this; } - bool operator<(const const_iterator& other) const + bool operator==(const const_iterator& o) const { - return ptr_ < other.ptr_; + return count_ == o.count_; } - bool operator>(const const_iterator& other) const + bool operator!=(const const_iterator& o) const { - return ptr_ > other.ptr_; + return count_ != o.count_; } - bool operator<=(const const_iterator& other) const + bool operator<(const const_iterator& o) const { - return ptr_ <= other.ptr_; + return count_ < o.count_; } - bool operator>=(const const_iterator& other) const + bool operator>(const const_iterator& o) const { - return ptr_ >= other.ptr_; + return count_ > o.count_; } - const T& operator[](difference_type n) const + bool operator<=(const const_iterator& o) const { - return *(ptr_ + n * stride_); + return count_ <= o.count_; } - const_iterator& operator+=(difference_type n) + bool operator>=(const const_iterator& o) const { - ptr_ += n * stride_; - return *this; + return count_ >= o.count_; } - const_iterator& operator-=(difference_type n) + friend const_iterator operator+(difference_type n, const const_iterator& it) { - ptr_ -= n * stride_; - return *this; + return it + n; } - friend const_iterator operator+( - difference_type n, const const_iterator& it) + + private: + size_t offset() const { return offset_of(count_); } + size_t offset_of(size_t flat) const { - return it + n; + size_t off = 0; + for (int d = static_cast(ndim_) - 1; d >= 0; --d) { + off += (flat % shape_[d]) * strides_[d]; + flat /= shape_[d]; + } + return off; } }; class iterator { - T* ptr_; - size_t stride_; + T* base_; + size_t count_; + const size_t* shape_; + const size_t* strides_; + size_t ndim_; public: using iterator_category = std::random_access_iterator_tag; @@ -223,74 +346,97 @@ class View1D { using pointer = T*; using reference = T&; - iterator(T* ptr, size_t stride) : ptr_(ptr), stride_(stride) {} - T& operator*() { return *ptr_; } + iterator(T* base, size_t count, const View* v) + : base_(base) + , count_(count) + , shape_(v->shape_.data()) + , strides_(v->strides_.data()) + , ndim_(v->shape_.size()) + {} + + T& operator*() { return base_[offset()]; } + T& operator[](difference_type n) { return base_[offset_of(count_ + n)]; } iterator& operator++() { - ptr_ += stride_; + ++count_; return *this; } iterator operator++(int) { auto tmp = *this; - ptr_ += stride_; + ++count_; return tmp; } iterator& operator--() { - ptr_ -= stride_; + --count_; return *this; } iterator operator+(difference_type n) const { - return iterator(ptr_ + n * stride_, stride_); + auto tmp = *this; + tmp.count_ += n; + return tmp; } iterator operator-(difference_type n) const { - return iterator(ptr_ - n * stride_, stride_); - } - difference_type operator-(const iterator& other) const - { - return (ptr_ - other.ptr_) / static_cast(stride_); - } - bool operator==(const iterator& other) const - { - return ptr_ == other.ptr_; - } - bool operator!=(const iterator& other) const - { - return ptr_ != other.ptr_; + auto tmp = *this; + tmp.count_ -= n; + return tmp; } - bool operator<(const iterator& other) const + difference_type operator-(const iterator& o) const { - return ptr_ < other.ptr_; + return static_cast(count_) - + static_cast(o.count_); } - T& operator[](difference_type n) { return *(ptr_ + n * stride_); } iterator& operator+=(difference_type n) { - ptr_ += n * stride_; + count_ += n; return *this; } + bool operator==(const iterator& o) const { return count_ == o.count_; } + bool operator!=(const iterator& o) const { return count_ != o.count_; } + bool operator<(const iterator& o) const { return count_ < o.count_; } friend iterator operator+(difference_type n, const iterator& it) { return it + n; } + + private: + size_t offset() const { return offset_of(count_); } + size_t offset_of(size_t flat) const + { + size_t off = 0; + for (int d = static_cast(ndim_) - 1; d >= 0; --d) { + off += (flat % shape_[d]) * strides_[d]; + flat /= shape_[d]; + } + return off; + } }; - iterator begin() { return iterator(data_, stride_); } - iterator end() { return iterator(data_ + size_ * stride_, stride_); } + iterator begin() { return {data_, 0, this}; } + iterator end() { return {data_, size(), this}; } const_iterator begin() const { return cbegin(); } const_iterator end() const { return cend(); } - const_iterator cbegin() const { return const_iterator(data_, stride_); } - const_iterator cend() const + const_iterator cbegin() const { return {data_, 0, this}; } + const_iterator cend() const { return {data_, size(), this}; } + +private: + //! Convert a logical flat index (row-major) to a physical element offset + size_t flat_to_offset(size_t flat) const { - return const_iterator(data_ + size_ * stride_, stride_); + size_t off = 0; + for (int d = static_cast(shape_.size()) - 1; d >= 0; --d) { + off += (flat % shape_[d]) * strides_[d]; + flat /= shape_[d]; + } + return off; } -private: T* data_; - size_t size_; - size_t stride_; + vector shape_; + vector strides_; }; @@ -359,14 +505,15 @@ class Tensor { : shape_(std::move(shape)), data_(vec.begin(), vec.end()) {} - //! Copy from View1D (makes a 1D tensor) + //! Copy from View (preserves view's shape) template - Tensor(const View1D& v) - : shape_({v.size()}) + Tensor(const View& v) + : shape_(v.shape_vec()) { - data_.resize(v.size()); - for (size_t i = 0; i < v.size(); ++i) - data_[i] = v(i); + size_t n = v.size(); + data_.resize(n); + for (size_t i = 0; i < n; ++i) + data_[i] = v[i]; } //! Cross-type copy constructor @@ -395,13 +542,15 @@ class Tensor { return *this; } - //! Assignment from View1D - Tensor& operator=(const View1D& v) + //! Assignment from View + template + Tensor& operator=(const View& v) { - shape_ = {v.size()}; - data_.resize(v.size()); - for (size_t i = 0; i < v.size(); ++i) - data_[i] = v(i); + shape_ = v.shape_vec(); + size_t n = v.size(); + data_.resize(n); + for (size_t i = 0; i < n; ++i) + data_[i] = v[i]; return *this; } @@ -500,56 +649,77 @@ class Tensor { //-------------------------------------------------------------------------- // View accessors - //! Row i of a 2D+ tensor (contiguous 1D view) - View1D row(size_t i) + //! Fix one axis at a given index, returning an (N-1)-dimensional view + View slice_at(size_t axis, size_t idx) { - auto cols = shape_[shape_.size() - 1]; - return {data_.data() + i * cols, cols, 1}; - } - View1D row(size_t i) const - { - auto cols = shape_[shape_.size() - 1]; - return {data_.data() + i * cols, cols, 1}; + auto strides = compute_strides(); + vector new_shape; + vector new_strides; + new_shape.reserve(shape_.size() - 1); + new_strides.reserve(shape_.size() - 1); + stored_type* new_data = data_.data() + idx * strides[axis]; + for (size_t d = 0; d < shape_.size(); ++d) { + if (d != axis) { + new_shape.push_back(shape_[d]); + new_strides.push_back(strides[d]); + } + } + return {new_data, std::move(new_shape), std::move(new_strides)}; } - //! Column j of a 2D tensor (strided 1D view) - View1D col(size_t j) + View slice_at(size_t axis, size_t idx) const { - return {data_.data() + j, shape_[0], shape_[1]}; - } - View1D col(size_t j) const - { - return {data_.data() + j, shape_[0], shape_[1]}; + auto strides = compute_strides(); + vector new_shape; + vector new_strides; + new_shape.reserve(shape_.size() - 1); + new_strides.reserve(shape_.size() - 1); + const stored_type* new_data = data_.data() + idx * strides[axis]; + for (size_t d = 0; d < shape_.size(); ++d) { + if (d != axis) { + new_shape.push_back(shape_[d]); + new_strides.push_back(strides[d]); + } + } + return {new_data, std::move(new_shape), std::move(new_strides)}; } + //! Row i of a 2D+ tensor (fix first axis) + View row(size_t i) { return slice_at(0, i); } + View row(size_t i) const { return slice_at(0, i); } + + //! Column j of a 2D tensor (fix second axis) + View col(size_t j) { return slice_at(1, j); } + View col(size_t j) const { return slice_at(1, j); } + //! Subrange of a 1D tensor - View1D slice(size_t start, size_t end) + View slice(size_t start, size_t end) { - return {data_.data() + start, end - start, 1}; + return {data_.data() + start, {end - start}, {size_t(1)}}; } - View1D slice(size_t start, size_t end) const + View slice(size_t start, size_t end) const { - return {data_.data() + start, end - start, 1}; + return {data_.data() + start, {end - start}, {size_t(1)}}; } //! Subrange to end of a 1D tensor - View1D slice(size_t start) + View slice(size_t start) { - return {data_.data() + start, data_.size() - start, 1}; + return {data_.data() + start, {data_.size() - start}, {size_t(1)}}; } - View1D slice(size_t start) const + View slice(size_t start) const { - return {data_.data() + start, data_.size() - start, 1}; + return {data_.data() + start, {data_.size() - start}, {size_t(1)}}; } //! Flat 1D view of all elements - View1D flat() + View flat() { - return {data_.data(), data_.size(), 1}; + return {data_.data(), {data_.size()}, {size_t(1)}}; } - View1D flat() const + View flat() const { - return {data_.data(), data_.size(), 1}; + return {data_.data(), {data_.size()}, {size_t(1)}}; } //-------------------------------------------------------------------------- @@ -755,6 +925,18 @@ class Tensor { return s; } + //! Compute row-major strides from shape + vector compute_strides() const + { + vector strides(shape_.size()); + if (!shape_.empty()) { + strides.back() = 1; + for (int d = static_cast(shape_.size()) - 2; d >= 0; --d) + strides[d] = strides[d + 1] * shape_[d + 1]; + } + return strides; + } + //-------------------------------------------------------------------------- // Data members @@ -804,24 +986,26 @@ Tensor operator/(const Tensor& a, const Tensor& b) } //============================================================================== -// View1D deferred method definitions (need Tensor to be complete) +// View deferred method definitions (need Tensor to be complete) //============================================================================== template template -View1D& View1D::operator=(const Tensor& other) +View& View::operator=(const Tensor& other) { - for (size_t i = 0; i < size_; ++i) - data_[i * stride_] = static_cast(other.data()[i]); + size_t n = size(); + for (size_t i = 0; i < n; ++i) + data_[flat_to_offset(i)] = static_cast(other.data()[i]); return *this; } template template -View1D& View1D::operator+=(const Tensor& o) +View& View::operator+=(const Tensor& o) { - for (size_t i = 0; i < size_; ++i) - data_[i * stride_] += o.data()[i]; + size_t n = size(); + for (size_t i = 0; i < n; ++i) + data_[flat_to_offset(i)] += o.data()[i]; return *this; } @@ -891,13 +1075,13 @@ class StaticTensor2D { const T* begin() const { return data_; } const T* end() const { return data_ + R * C; } - //! Column view - View1D col(size_t j) { return {data_ + j, R, C}; } - View1D col(size_t j) const { return {data_ + j, R, C}; } + //! Column view (1D, strided) + View col(size_t j) { return {data_ + j, {R}, {C}}; } + View col(size_t j) const { return {data_ + j, {R}, {C}}; } - //! Flat view - View1D flat() { return {data_, R * C, 1}; } - View1D flat() const { return {data_, R * C, 1}; } + //! Flat view (1D, contiguous) + View flat() { return {data_, {R * C}, {size_t(1)}}; } + View flat() const { return {data_, {R * C}, {size_t(1)}}; } private: T data_[R * C] = {}; diff --git a/src/tallies/tally.cpp b/src/tallies/tally.cpp index f8ab34db735..42927b648a3 100644 --- a/src/tallies/tally.cpp +++ b/src/tallies/tally.cpp @@ -1002,14 +1002,11 @@ void reduce_tally_results() // Skip any tallies that are not active auto& tally {model::tallies[i_tally]}; - // Copy accumulated tally values (VALUE column) into contiguous array + // Extract 2D view of the VALUE column from the 3D results tensor, + // then copy into a contiguous array for MPI reduction const int val_idx = static_cast(TallyResult::VALUE); - const size_t ni = tally->results_.shape(0); - const size_t nj = tally->results_.shape(1); - tensor::Tensor values({ni, nj}); - for (size_t i = 0; i < ni; ++i) - for (size_t j = 0; j < nj; ++j) - values(i, j) = tally->results_(i, j, val_idx); + auto val_view = tally->results_.slice_at(2, val_idx); + tensor::Tensor values(val_view); tensor::Tensor values_reduced(values.shape()); @@ -1019,13 +1016,9 @@ void reduce_tally_results() // Transfer values on master and reset on other ranks if (mpi::master) { - for (size_t i = 0; i < ni; ++i) - for (size_t j = 0; j < nj; ++j) - tally->results_(i, j, val_idx) = values_reduced(i, j); + val_view = values_reduced; } else { - for (size_t i = 0; i < ni; ++i) - for (size_t j = 0; j < nj; ++j) - tally->results_(i, j, val_idx) = 0.0; + val_view = 0.0; } } } From 866a022bda842f08580e755d3a69796c78fe858f Mon Sep 17 00:00:00 2001 From: John Tramm Date: Wed, 11 Feb 2026 15:43:12 -0600 Subject: [PATCH 20/51] knocked off TODO item that was due to a long term bug in xtensor --- src/mgxs.cpp | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/mgxs.cpp b/src/mgxs.cpp index d7f96c62b7d..57acba6a6c9 100644 --- a/src/mgxs.cpp +++ b/src/mgxs.cpp @@ -97,17 +97,7 @@ void Mgxs::metadata_from_hdf5(hid_t xs_id, const vector& temperature, // Determine actual temperatures to read for (const auto& T : temperature) { // Determine the closest temperature value - // NOTE: the below block could be replaced with the following line: - // auto i_closest = tensor::abs(temps_available - T).argmin(); - double closest = std::numeric_limits::max(); - int i_closest = 0; - for (int i = 0; i < temps_available.size(); i++) { - double diff = std::abs(temps_available[i] - T); - if (diff < closest) { - closest = diff; - i_closest = i; - } - } + auto i_closest = tensor::abs(temps_available - T).argmin(); double temp_actual = temps_available[i_closest]; if (std::fabs(temp_actual - T) < settings::temperature_tolerance) { From 8cf5d3aed42b78e237cfafbfec1cef11bb9cb19d Mon Sep 17 00:00:00 2001 From: John Tramm Date: Wed, 11 Feb 2026 15:53:09 -0600 Subject: [PATCH 21/51] cleanup of terminology --- include/openmc/tensor.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/openmc/tensor.h b/include/openmc/tensor.h index 20234e5fb99..57ab8d0583b 100644 --- a/include/openmc/tensor.h +++ b/include/openmc/tensor.h @@ -194,11 +194,11 @@ class View { return *this; } - //! Assignment from Tensor (deferred, defined after Tensor) + //! Assignment from Tensor (forward-declared, defined after Tensor) template View& operator=(const Tensor& other); - //! Compound addition from Tensor (deferred) + //! Compound addition from Tensor (forward-declared, defined after Tensor) template View& operator+=(const Tensor& o); @@ -986,7 +986,7 @@ Tensor operator/(const Tensor& a, const Tensor& b) } //============================================================================== -// View deferred method definitions (need Tensor to be complete) +// View forward-declared method definitions (need Tensor to be complete) //============================================================================== template From 2e7ce3b4c5eba3ac61e80dc02639fc6c104dc4d4 Mon Sep 17 00:00:00 2001 From: John Tramm Date: Wed, 11 Feb 2026 16:07:04 -0600 Subject: [PATCH 22/51] made more use of the view where it makes sense --- src/nuclide.cpp | 12 ++++-------- src/tallies/tally.cpp | 13 ++++--------- 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/src/nuclide.cpp b/src/nuclide.cpp index 10d3065f4eb..4cfebf82df4 100644 --- a/src/nuclide.cpp +++ b/src/nuclide.cpp @@ -403,21 +403,17 @@ void Nuclide::create_derived( continue; // Add contribution to total cross section - for (int k = 0; k < n; ++k) - xs_[t](j + k, XS_TOTAL) += xs[k]; + xs_[t].col(XS_TOTAL).slice(j, j + n) += xs; // Add contribution to absorption cross section if (is_disappearance(rx->mt_)) { - for (int k = 0; k < n; ++k) - xs_[t](j + k, XS_ABSORPTION) += xs[k]; + xs_[t].col(XS_ABSORPTION).slice(j, j + n) += xs; } if (is_fission(rx->mt_)) { fissionable_ = true; - for (int k = 0; k < n; ++k) - xs_[t](j + k, XS_FISSION) += xs[k]; - for (int k = 0; k < n; ++k) - xs_[t](j + k, XS_ABSORPTION) += xs[k]; + xs_[t].col(XS_FISSION).slice(j, j + n) += xs; + xs_[t].col(XS_ABSORPTION).slice(j, j + n) += xs; // Keep track of fission reactions if (t == 0) { diff --git a/src/tallies/tally.cpp b/src/tallies/tally.cpp index 42927b648a3..dfc04087250 100644 --- a/src/tallies/tally.cpp +++ b/src/tallies/tally.cpp @@ -1030,11 +1030,8 @@ void reduce_tally_results() auto& gt = simulation::global_tallies; const int val_col = static_cast(TallyResult::VALUE); - // Copy VALUE column into contiguous array - tensor::Tensor gt_values({size_t{N_GLOBAL_TALLIES}}); - for (int i = 0; i < N_GLOBAL_TALLIES; ++i) - gt_values(i) = gt(i, val_col); - + // Copy VALUE column into contiguous array for MPI reduction + tensor::Tensor gt_values(gt.col(val_col)); tensor::Tensor gt_values_reduced({size_t{N_GLOBAL_TALLIES}}); // Reduce contiguous data @@ -1043,11 +1040,9 @@ void reduce_tally_results() // Transfer values on master and reset on other ranks if (mpi::master) { - for (int i = 0; i < N_GLOBAL_TALLIES; ++i) - gt(i, val_col) = gt_values_reduced(i); + gt.col(val_col) = gt_values_reduced; } else { - for (int i = 0; i < N_GLOBAL_TALLIES; ++i) - gt(i, val_col) = 0.0; + gt.col(val_col) = 0.0; } // We also need to determine the total starting weight of particles from the From 649dc314680983f1b519a62dfa216f2eb406a37f Mon Sep 17 00:00:00 2001 From: John Tramm Date: Wed, 11 Feb 2026 16:56:37 -0600 Subject: [PATCH 23/51] made more use of view, and added a few small needed functions to improve utility --- include/openmc/tensor.h | 45 +++++++++++++++++++++++++++++++ src/xsdata.cpp | 59 +++++++++++++++-------------------------- 2 files changed, 66 insertions(+), 38 deletions(-) diff --git a/include/openmc/tensor.h b/include/openmc/tensor.h index 57ab8d0583b..76afbd77a69 100644 --- a/include/openmc/tensor.h +++ b/include/openmc/tensor.h @@ -66,6 +66,22 @@ class View { : data_(data), shape_(std::move(shape)), strides_(std::move(strides)) {} + // Explicitly default copy/move constructors (declaring copy assignment + // below would otherwise suppress the implicit move constructor). + View(const View&) = default; + View(View&&) = default; + + //! Copy assignment: element-wise deep copy (writes through data pointer). + //! Without this, the compiler's implicit copy assignment just copies the + //! View metadata (pointer, shape, strides) instead of the viewed data. + View& operator=(const View& other) + { + size_t n = size(); + for (size_t i = 0; i < n; ++i) + data_[flat_to_offset(i)] = other[i]; + return *this; + } + //-------------------------------------------------------------------------- // Indexing @@ -194,6 +210,16 @@ class View { return *this; } + //! Assignment from another View + template + View& operator=(const View& other) + { + size_t n = size(); + for (size_t i = 0; i < n; ++i) + data_[flat_to_offset(i)] = other[i]; + return *this; + } + //! Assignment from Tensor (forward-declared, defined after Tensor) template View& operator=(const Tensor& other); @@ -211,6 +237,25 @@ class View { return *this; } + //! Compound divide by scalar + View& operator/=(value_type val) + { + size_t n = size(); + for (size_t i = 0; i < n; ++i) + data_[flat_to_offset(i)] /= val; + return *this; + } + + //! Sum of all elements + value_type sum() const + { + value_type s = value_type(0); + size_t n = size(); + for (size_t i = 0; i < n; ++i) + s += data_[flat_to_offset(i)]; + return s; + } + //-------------------------------------------------------------------------- // Iterators // diff --git a/src/xsdata.cpp b/src/xsdata.cpp index 158f13b2c1c..2822b0bea8e 100644 --- a/src/xsdata.cpp +++ b/src/xsdata.cpp @@ -132,24 +132,21 @@ void XsData::fission_vector_beta_from_hdf5( // Normalize chi so it sums to 1 over outgoing groups for each angle for (size_t a = 0; a < n_ang; a++) { - double s = 0.0; - for (size_t g = 0; g < n_g_; g++) s += temp_chi(a, g); - for (size_t g = 0; g < n_g_; g++) temp_chi(a, g) /= s; + auto row = temp_chi.row(a); + row /= row.sum(); } // Replicate the energy spectrum across all incoming groups — the // spectrum is independent of the incoming neutron energy for (size_t a = 0; a < n_ang; a++) for (size_t gin = 0; gin < n_g_; gin++) - for (size_t gout = 0; gout < n_g_; gout++) - chi_prompt(a, gin, gout) = temp_chi(a, gout); + chi_prompt.slice_at(0, a).row(gin) = temp_chi.row(a); // Same spectrum for delayed neutrons, replicated across delayed groups for (size_t a = 0; a < n_ang; a++) for (size_t d = 0; d < n_dg_; d++) for (size_t gin = 0; gin < n_g_; gin++) - for (size_t gout = 0; gout < n_g_; gout++) - chi_delayed(a, d, gin, gout) = temp_chi(a, gout); + chi_delayed.slice_at(0, a).slice_at(0, d).row(gin) = temp_chi.row(a); // Get nu-fission tensor::Tensor temp_nufiss({n_ang, n_g_}, 0.); @@ -208,9 +205,8 @@ void XsData::fission_vector_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) // Normalize prompt chi so it sums to 1 over outgoing groups for each angle for (size_t a = 0; a < n_ang; a++) { - double s = 0.0; - for (size_t g = 0; g < n_g_; g++) s += temp_chi_p(a, g); - for (size_t g = 0; g < n_g_; g++) temp_chi_p(a, g) /= s; + auto row = temp_chi_p.row(a); + row /= row.sum(); } // Get chi-delayed @@ -221,23 +217,20 @@ void XsData::fission_vector_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) // angle and delayed group for (size_t a = 0; a < n_ang; a++) for (size_t d = 0; d < n_dg_; d++) { - double s = 0.0; - for (size_t g = 0; g < n_g_; g++) s += temp_chi_d(a, d, g); - for (size_t g = 0; g < n_g_; g++) temp_chi_d(a, d, g) /= s; + auto row = temp_chi_d.slice_at(0, a).row(d); + row /= row.sum(); } // Replicate the prompt spectrum across all incoming groups for (size_t a = 0; a < n_ang; a++) for (size_t gin = 0; gin < n_g_; gin++) - for (size_t gout = 0; gout < n_g_; gout++) - chi_prompt(a, gin, gout) = temp_chi_p(a, gout); + chi_prompt.slice_at(0, a).row(gin) = temp_chi_p.row(a); // Replicate the delayed spectrum across all incoming groups for (size_t a = 0; a < n_ang; a++) for (size_t d = 0; d < n_dg_; d++) for (size_t gin = 0; gin < n_g_; gin++) - for (size_t gout = 0; gout < n_g_; gout++) - chi_delayed(a, d, gin, gout) = temp_chi_d(a, d, gout); + chi_delayed.slice_at(0, a).slice_at(0, d).row(gin) = temp_chi_d.slice_at(0, a).row(d); // Get prompt and delayed nu-fission directly read_nd_tensor(xsdata_grp, "prompt-nu-fission", prompt_nu_fission, true); @@ -255,16 +248,14 @@ void XsData::fission_vector_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang) // Normalize chi so it sums to 1 over outgoing groups for each angle for (size_t a = 0; a < n_ang; a++) { - double s = 0.0; - for (size_t g = 0; g < n_g_; g++) s += temp_chi(a, g); - for (size_t g = 0; g < n_g_; g++) temp_chi(a, g) /= s; + auto row = temp_chi.row(a); + row /= row.sum(); } // Replicate the energy spectrum across all incoming groups for (size_t a = 0; a < n_ang; a++) for (size_t gin = 0; gin < n_g_; gin++) - for (size_t gout = 0; gout < n_g_; gout++) - chi_prompt(a, gin, gout) = temp_chi(a, gout); + chi_prompt.slice_at(0, a).row(gin) = temp_chi.row(a); // Get nu-fission directly read_nd_tensor(xsdata_grp, "nu-fission", prompt_nu_fission, true); @@ -364,20 +355,16 @@ void XsData::fission_matrix_beta_from_hdf5( // Normalize chi_prompt so it sums to 1 over outgoing groups for (size_t a = 0; a < n_ang; a++) for (size_t gin = 0; gin < n_g_; gin++) { - double s = 0.0; - for (size_t gout = 0; gout < n_g_; gout++) s += chi_prompt(a, gin, gout); - for (size_t gout = 0; gout < n_g_; gout++) chi_prompt(a, gin, gout) /= s; + auto row = chi_prompt.slice_at(0, a).row(gin); + row /= row.sum(); } // Normalize chi_delayed so it sums to 1 over outgoing groups for (size_t a = 0; a < n_ang; a++) for (size_t d = 0; d < n_dg_; d++) for (size_t gin = 0; gin < n_g_; gin++) { - double s = 0.0; - for (size_t gout = 0; gout < n_g_; gout++) - s += chi_delayed(a, d, gin, gout); - for (size_t gout = 0; gout < n_g_; gout++) - chi_delayed(a, d, gin, gout) /= s; + auto row = chi_delayed.slice_at(0, a).slice_at(0, d).row(gin); + row /= row.sum(); } } @@ -646,9 +633,8 @@ void XsData::combine( size_t n_g = chi_prompt.shape(1); for (size_t a = 0; a < n_ang; a++) for (size_t gin = 0; gin < n_g; gin++) { - double s = 0.0; - for (size_t gout = 0; gout < n_g; gout++) s += chi_prompt(a, gin, gout); - for (size_t gout = 0; gout < n_g; gout++) chi_prompt(a, gin, gout) /= s; + auto row = chi_prompt.slice_at(0, a).row(gin); + row /= row.sum(); } } // Normalize chi_delayed so it sums to 1 over outgoing groups @@ -659,11 +645,8 @@ void XsData::combine( for (size_t a = 0; a < n_ang; a++) for (size_t d = 0; d < n_dg; d++) for (size_t gin = 0; gin < n_g; gin++) { - double s = 0.0; - for (size_t gout = 0; gout < n_g; gout++) - s += chi_delayed(a, d, gin, gout); - for (size_t gout = 0; gout < n_g; gout++) - chi_delayed(a, d, gin, gout) /= s; + auto row = chi_delayed.slice_at(0, a).slice_at(0, d).row(gin); + row /= row.sum(); } } From 64e2cd00e9732657ac94fd06c2b1e632cebb48d5 Mon Sep 17 00:00:00 2001 From: John Tramm Date: Wed, 11 Feb 2026 17:01:07 -0600 Subject: [PATCH 24/51] renamed slice_at to select to make it more clear what it is actually doing --- include/openmc/tensor.h | 30 +++++++++++++++--------------- src/tallies/tally.cpp | 2 +- src/xsdata.cpp | 20 ++++++++++---------- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/include/openmc/tensor.h b/include/openmc/tensor.h index 76afbd77a69..721bbe571c1 100644 --- a/include/openmc/tensor.h +++ b/include/openmc/tensor.h @@ -54,7 +54,7 @@ using storage_type = typename storage_type_map::type; // View: a non-owning N-dimensional view into a tensor's storage. // // Holds a base pointer, shape, and strides (in elements). Supports arbitrary -// rank: 1D views for rows/slices, 2D views via slice_at(), etc. +// rank: 1D views for rows/slices, 2D views via select(), etc. //============================================================================== template @@ -131,7 +131,7 @@ class View { // Sub-view methods //! Fix one axis at a given index, returning an (N-1)-dimensional view - View slice_at(size_t axis, size_t idx) + View select(size_t axis, size_t idx) { vector new_shape; vector new_strides; @@ -147,7 +147,7 @@ class View { return {new_data, std::move(new_shape), std::move(new_strides)}; } - View slice_at(size_t axis, size_t idx) const + View select(size_t axis, size_t idx) const { vector new_shape; vector new_strides; @@ -163,13 +163,13 @@ class View { return {new_data, std::move(new_shape), std::move(new_strides)}; } - //! Row i (fix first axis) — shorthand for slice_at(0, i) - View row(size_t i) { return slice_at(0, i); } - View row(size_t i) const { return slice_at(0, i); } + //! Row i (fix first axis) — shorthand for select(0, i) + View row(size_t i) { return select(0, i); } + View row(size_t i) const { return select(0, i); } - //! Column j (fix second axis) — shorthand for slice_at(1, j) - View col(size_t j) { return slice_at(1, j); } - View col(size_t j) const { return slice_at(1, j); } + //! Column j (fix second axis) — shorthand for select(1, j) + View col(size_t j) { return select(1, j); } + View col(size_t j) const { return select(1, j); } //! 1D subrange [start, end) View slice(size_t start, size_t end) @@ -695,7 +695,7 @@ class Tensor { // View accessors //! Fix one axis at a given index, returning an (N-1)-dimensional view - View slice_at(size_t axis, size_t idx) + View select(size_t axis, size_t idx) { auto strides = compute_strides(); vector new_shape; @@ -712,7 +712,7 @@ class Tensor { return {new_data, std::move(new_shape), std::move(new_strides)}; } - View slice_at(size_t axis, size_t idx) const + View select(size_t axis, size_t idx) const { auto strides = compute_strides(); vector new_shape; @@ -730,12 +730,12 @@ class Tensor { } //! Row i of a 2D+ tensor (fix first axis) - View row(size_t i) { return slice_at(0, i); } - View row(size_t i) const { return slice_at(0, i); } + View row(size_t i) { return select(0, i); } + View row(size_t i) const { return select(0, i); } //! Column j of a 2D tensor (fix second axis) - View col(size_t j) { return slice_at(1, j); } - View col(size_t j) const { return slice_at(1, j); } + View col(size_t j) { return select(1, j); } + View col(size_t j) const { return select(1, j); } //! Subrange of a 1D tensor View slice(size_t start, size_t end) diff --git a/src/tallies/tally.cpp b/src/tallies/tally.cpp index dfc04087250..40cd6363b81 100644 --- a/src/tallies/tally.cpp +++ b/src/tallies/tally.cpp @@ -1005,7 +1005,7 @@ void reduce_tally_results() // Extract 2D view of the VALUE column from the 3D results tensor, // then copy into a contiguous array for MPI reduction const int val_idx = static_cast(TallyResult::VALUE); - auto val_view = tally->results_.slice_at(2, val_idx); + auto val_view = tally->results_.select(2, val_idx); tensor::Tensor values(val_view); tensor::Tensor values_reduced(values.shape()); diff --git a/src/xsdata.cpp b/src/xsdata.cpp index 2822b0bea8e..9127c21da97 100644 --- a/src/xsdata.cpp +++ b/src/xsdata.cpp @@ -140,13 +140,13 @@ void XsData::fission_vector_beta_from_hdf5( // spectrum is independent of the incoming neutron energy for (size_t a = 0; a < n_ang; a++) for (size_t gin = 0; gin < n_g_; gin++) - chi_prompt.slice_at(0, a).row(gin) = temp_chi.row(a); + chi_prompt.select(0, a).row(gin) = temp_chi.row(a); // Same spectrum for delayed neutrons, replicated across delayed groups for (size_t a = 0; a < n_ang; a++) for (size_t d = 0; d < n_dg_; d++) for (size_t gin = 0; gin < n_g_; gin++) - chi_delayed.slice_at(0, a).slice_at(0, d).row(gin) = temp_chi.row(a); + chi_delayed.select(0, a).select(0, d).row(gin) = temp_chi.row(a); // Get nu-fission tensor::Tensor temp_nufiss({n_ang, n_g_}, 0.); @@ -217,20 +217,20 @@ void XsData::fission_vector_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) // angle and delayed group for (size_t a = 0; a < n_ang; a++) for (size_t d = 0; d < n_dg_; d++) { - auto row = temp_chi_d.slice_at(0, a).row(d); + auto row = temp_chi_d.select(0, a).row(d); row /= row.sum(); } // Replicate the prompt spectrum across all incoming groups for (size_t a = 0; a < n_ang; a++) for (size_t gin = 0; gin < n_g_; gin++) - chi_prompt.slice_at(0, a).row(gin) = temp_chi_p.row(a); + chi_prompt.select(0, a).row(gin) = temp_chi_p.row(a); // Replicate the delayed spectrum across all incoming groups for (size_t a = 0; a < n_ang; a++) for (size_t d = 0; d < n_dg_; d++) for (size_t gin = 0; gin < n_g_; gin++) - chi_delayed.slice_at(0, a).slice_at(0, d).row(gin) = temp_chi_d.slice_at(0, a).row(d); + chi_delayed.select(0, a).select(0, d).row(gin) = temp_chi_d.select(0, a).row(d); // Get prompt and delayed nu-fission directly read_nd_tensor(xsdata_grp, "prompt-nu-fission", prompt_nu_fission, true); @@ -255,7 +255,7 @@ void XsData::fission_vector_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang) // Replicate the energy spectrum across all incoming groups for (size_t a = 0; a < n_ang; a++) for (size_t gin = 0; gin < n_g_; gin++) - chi_prompt.slice_at(0, a).row(gin) = temp_chi.row(a); + chi_prompt.select(0, a).row(gin) = temp_chi.row(a); // Get nu-fission directly read_nd_tensor(xsdata_grp, "nu-fission", prompt_nu_fission, true); @@ -355,7 +355,7 @@ void XsData::fission_matrix_beta_from_hdf5( // Normalize chi_prompt so it sums to 1 over outgoing groups for (size_t a = 0; a < n_ang; a++) for (size_t gin = 0; gin < n_g_; gin++) { - auto row = chi_prompt.slice_at(0, a).row(gin); + auto row = chi_prompt.select(0, a).row(gin); row /= row.sum(); } @@ -363,7 +363,7 @@ void XsData::fission_matrix_beta_from_hdf5( for (size_t a = 0; a < n_ang; a++) for (size_t d = 0; d < n_dg_; d++) for (size_t gin = 0; gin < n_g_; gin++) { - auto row = chi_delayed.slice_at(0, a).slice_at(0, d).row(gin); + auto row = chi_delayed.select(0, a).select(0, d).row(gin); row /= row.sum(); } } @@ -633,7 +633,7 @@ void XsData::combine( size_t n_g = chi_prompt.shape(1); for (size_t a = 0; a < n_ang; a++) for (size_t gin = 0; gin < n_g; gin++) { - auto row = chi_prompt.slice_at(0, a).row(gin); + auto row = chi_prompt.select(0, a).row(gin); row /= row.sum(); } } @@ -645,7 +645,7 @@ void XsData::combine( for (size_t a = 0; a < n_ang; a++) for (size_t d = 0; d < n_dg; d++) for (size_t gin = 0; gin < n_g; gin++) { - auto row = chi_delayed.slice_at(0, a).slice_at(0, d).row(gin); + auto row = chi_delayed.select(0, a).select(0, d).row(gin); row /= row.sum(); } } From 987d583327fe5e1b4919c5d524c17eed1a5a0940 Mon Sep 17 00:00:00 2001 From: John Tramm Date: Wed, 11 Feb 2026 20:58:17 -0600 Subject: [PATCH 25/51] Made explicit view variable declarations rather than using auto. --- src/distribution_angle.cpp | 6 +++--- src/distribution_energy.cpp | 4 ++-- src/endf.cpp | 8 ++++---- src/material.cpp | 2 +- src/photon.cpp | 8 ++++---- src/physics.cpp | 4 ++-- src/secondary_correlated.cpp | 10 +++++----- src/secondary_kalbach.cpp | 4 ++-- src/secondary_thermal.cpp | 2 +- src/tallies/tally.cpp | 2 +- src/xsdata.cpp | 16 ++++++++-------- 11 files changed, 33 insertions(+), 33 deletions(-) diff --git a/src/distribution_angle.cpp b/src/distribution_angle.cpp index 7b99727faf2..f183e3aef16 100644 --- a/src/distribution_angle.cpp +++ b/src/distribution_angle.cpp @@ -44,9 +44,9 @@ AngleDistribution::AngleDistribution(hid_t group) } // Create and initialize tabular distribution - auto xs = temp.row(0).slice(j, j + n); - auto ps = temp.row(1).slice(j, j + n); - auto cs = temp.row(2).slice(j, j + n); + tensor::View xs = temp.row(0).slice(j, j + n); + tensor::View ps = temp.row(1).slice(j, j + n); + tensor::View cs = temp.row(2).slice(j, j + n); vector x {xs.begin(), xs.end()}; vector p {ps.begin(), ps.end()}; vector c {cs.begin(), cs.end()}; diff --git a/src/distribution_energy.cpp b/src/distribution_energy.cpp index 65e6afca002..c57a9b6b0af 100644 --- a/src/distribution_energy.cpp +++ b/src/distribution_energy.cpp @@ -63,8 +63,8 @@ ContinuousTabular::ContinuousTabular(hid_t group) tensor::Tensor temp; read_attribute(dset, "interpolation", temp); - auto temp_b = temp.row(0); // breakpoints - auto temp_i = temp.row(1); // interpolation parameters + tensor::View temp_b = temp.row(0); // breakpoints + tensor::View temp_i = temp.row(1); // interpolation parameters std::copy(temp_b.begin(), temp_b.end(), std::back_inserter(breakpoints_)); for (const auto i : temp_i) diff --git a/src/endf.cpp b/src/endf.cpp index f793cc245f4..87f32972a67 100644 --- a/src/endf.cpp +++ b/src/endf.cpp @@ -155,8 +155,8 @@ Tabulated1D::Tabulated1D(hid_t dset) tensor::Tensor arr; read_dataset(dset, arr); - auto xs = arr.row(0); - auto ys = arr.row(1); + tensor::View xs = arr.row(0); + tensor::View ys = arr.row(1); std::copy(xs.begin(), xs.end(), std::back_inserter(x_)); std::copy(ys.begin(), ys.end(), std::back_inserter(y_)); @@ -232,8 +232,8 @@ CoherentElasticXS::CoherentElasticXS(hid_t dset) read_dataset(dset, arr); // Get views for Bragg edges and structure factors - auto E = arr.row(0); - auto s = arr.row(1); + tensor::View E = arr.row(0); + tensor::View s = arr.row(1); // Copy Bragg edges and partial sums of structure factors std::copy(E.begin(), E.end(), std::back_inserter(bragg_edges_)); diff --git a/src/material.cpp b/src/material.cpp index 530807248eb..b29c597f651 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -696,7 +696,7 @@ void Material::init_bremsstrahlung() 1.0595e-3 * std::pow(t, 5) + 7.0568e-5 * std::pow(t, 6) - 1.808e-6 * std::pow(t, 7)); stopping_power_radiative(i) *= r; - auto dcs_i = dcs.row(i); + tensor::View dcs_i = dcs.row(i); dcs_i *= r; } } diff --git a/src/photon.cpp b/src/photon.cpp index 8ede33b7439..e75624a9bef 100644 --- a/src/photon.cpp +++ b/src/photon.cpp @@ -304,7 +304,7 @@ PhotonInteraction::PhotonInteraction(hid_t group) double y = std::exp( std::log(dcs_(i_grid, i)) + f * (std::log(dcs_(i_grid + 1, i)) - std::log(dcs_(i_grid, i)))); - auto col_i = dcs.col(i); + tensor::View col_i = dcs.col(i); col_i(0) = y; for (int j = i_grid + 1; j < n_e; ++j) { col_i(j - i_grid) = dcs_(j, i); @@ -507,7 +507,7 @@ void PhotonInteraction::compton_doppler( c = prn(seed) * c_max; // Determine pz corresponding to sampled cdf value - auto cdf_shell = profile_cdf_.row(shell); + tensor::View cdf_shell = profile_cdf_.row(shell); int i = lower_bound_index(cdf_shell.cbegin(), cdf_shell.cend(), c); double pz_l = data::compton_profile_pz(i); double pz_r = data::compton_profile_pz(i + 1); @@ -603,8 +603,8 @@ void PhotonInteraction::calculate_xs(Particle& p) const // Calculate microscopic photoelectric cross section xs.photoelectric = 0.0; - const auto xs_lower = cross_sections_.row(i_grid); - const auto xs_upper = cross_sections_.row(i_grid + 1); + tensor::View xs_lower = cross_sections_.row(i_grid); + tensor::View xs_upper = cross_sections_.row(i_grid + 1); for (int i = 0; i < xs_upper.size(); ++i) if (xs_lower(i) != 0) diff --git a/src/physics.cpp b/src/physics.cpp index 9371f54725d..3c25724396c 100644 --- a/src/physics.cpp +++ b/src/physics.cpp @@ -375,8 +375,8 @@ void sample_photon_reaction(Particle& p) // cross sections int i_grid = micro.index_grid; double f = micro.interp_factor; - const auto& xs_lower = element.cross_sections_.row(i_grid); - const auto& xs_upper = element.cross_sections_.row(i_grid + 1); + tensor::View xs_lower = element.cross_sections_.row(i_grid); + tensor::View xs_upper = element.cross_sections_.row(i_grid + 1); for (int i_shell = 0; i_shell < element.shells_.size(); ++i_shell) { const auto& shell {element.shells_[i_shell]}; diff --git a/src/secondary_correlated.cpp b/src/secondary_correlated.cpp index 08a6ae59b27..089e2611252 100644 --- a/src/secondary_correlated.cpp +++ b/src/secondary_correlated.cpp @@ -28,8 +28,8 @@ CorrelatedAngleEnergy::CorrelatedAngleEnergy(hid_t group) tensor::Tensor temp; read_attribute(dset, "interpolation", temp); - auto temp_b = temp.row(0); // breakpoints - auto temp_i = temp.row(1); // interpolation parameters + tensor::View temp_b = temp.row(0); // breakpoints + tensor::View temp_i = temp.row(1); // interpolation parameters std::copy(temp_b.begin(), temp_b.end(), std::back_inserter(breakpoints_)); for (const auto i : temp_i) @@ -132,9 +132,9 @@ CorrelatedAngleEnergy::CorrelatedAngleEnergy(hid_t group) interp_mu = 1; auto interp = int2interp(interp_mu); - auto xs = mu.row(0).slice(offset_mu, offset_mu + m); - auto ps = mu.row(1).slice(offset_mu, offset_mu + m); - auto cs = mu.row(2).slice(offset_mu, offset_mu + m); + tensor::View xs = mu.row(0).slice(offset_mu, offset_mu + m); + tensor::View ps = mu.row(1).slice(offset_mu, offset_mu + m); + tensor::View cs = mu.row(2).slice(offset_mu, offset_mu + m); vector x {xs.begin(), xs.end()}; vector p {ps.begin(), ps.end()}; diff --git a/src/secondary_kalbach.cpp b/src/secondary_kalbach.cpp index 5ed53fc29bb..b2dd784f974 100644 --- a/src/secondary_kalbach.cpp +++ b/src/secondary_kalbach.cpp @@ -29,8 +29,8 @@ KalbachMann::KalbachMann(hid_t group) tensor::Tensor temp; read_attribute(dset, "interpolation", temp); - auto temp_b = temp.row(0); // breakpoints - auto temp_i = temp.row(1); // interpolation parameters + tensor::View temp_b = temp.row(0); // breakpoints + tensor::View temp_i = temp.row(1); // interpolation parameters std::copy(temp_b.begin(), temp_b.end(), std::back_inserter(breakpoints_)); for (const auto i : temp_i) diff --git a/src/secondary_thermal.cpp b/src/secondary_thermal.cpp index 257a623e151..e0ba90dabf3 100644 --- a/src/secondary_thermal.cpp +++ b/src/secondary_thermal.cpp @@ -222,7 +222,7 @@ IncoherentInelasticAE::IncoherentInelasticAE(hid_t group) } // Copy outgoing angles - auto mu_j = d.mu.row(j); + tensor::View mu_j = d.mu.row(j); std::copy(adist->x().begin(), adist->x().end(), mu_j.begin()); } } diff --git a/src/tallies/tally.cpp b/src/tallies/tally.cpp index 40cd6363b81..b51d5ebdf2d 100644 --- a/src/tallies/tally.cpp +++ b/src/tallies/tally.cpp @@ -1005,7 +1005,7 @@ void reduce_tally_results() // Extract 2D view of the VALUE column from the 3D results tensor, // then copy into a contiguous array for MPI reduction const int val_idx = static_cast(TallyResult::VALUE); - auto val_view = tally->results_.select(2, val_idx); + tensor::View val_view = tally->results_.select(2, val_idx); tensor::Tensor values(val_view); tensor::Tensor values_reduced(values.shape()); diff --git a/src/xsdata.cpp b/src/xsdata.cpp index 9127c21da97..45ece8f8ba8 100644 --- a/src/xsdata.cpp +++ b/src/xsdata.cpp @@ -132,7 +132,7 @@ void XsData::fission_vector_beta_from_hdf5( // Normalize chi so it sums to 1 over outgoing groups for each angle for (size_t a = 0; a < n_ang; a++) { - auto row = temp_chi.row(a); + tensor::View row = temp_chi.row(a); row /= row.sum(); } @@ -205,7 +205,7 @@ void XsData::fission_vector_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) // Normalize prompt chi so it sums to 1 over outgoing groups for each angle for (size_t a = 0; a < n_ang; a++) { - auto row = temp_chi_p.row(a); + tensor::View row = temp_chi_p.row(a); row /= row.sum(); } @@ -217,7 +217,7 @@ void XsData::fission_vector_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) // angle and delayed group for (size_t a = 0; a < n_ang; a++) for (size_t d = 0; d < n_dg_; d++) { - auto row = temp_chi_d.select(0, a).row(d); + tensor::View row = temp_chi_d.select(0, a).row(d); row /= row.sum(); } @@ -248,7 +248,7 @@ void XsData::fission_vector_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang) // Normalize chi so it sums to 1 over outgoing groups for each angle for (size_t a = 0; a < n_ang; a++) { - auto row = temp_chi.row(a); + tensor::View row = temp_chi.row(a); row /= row.sum(); } @@ -355,7 +355,7 @@ void XsData::fission_matrix_beta_from_hdf5( // Normalize chi_prompt so it sums to 1 over outgoing groups for (size_t a = 0; a < n_ang; a++) for (size_t gin = 0; gin < n_g_; gin++) { - auto row = chi_prompt.select(0, a).row(gin); + tensor::View row = chi_prompt.select(0, a).row(gin); row /= row.sum(); } @@ -363,7 +363,7 @@ void XsData::fission_matrix_beta_from_hdf5( for (size_t a = 0; a < n_ang; a++) for (size_t d = 0; d < n_dg_; d++) for (size_t gin = 0; gin < n_g_; gin++) { - auto row = chi_delayed.select(0, a).select(0, d).row(gin); + tensor::View row = chi_delayed.select(0, a).select(0, d).row(gin); row /= row.sum(); } } @@ -633,7 +633,7 @@ void XsData::combine( size_t n_g = chi_prompt.shape(1); for (size_t a = 0; a < n_ang; a++) for (size_t gin = 0; gin < n_g; gin++) { - auto row = chi_prompt.select(0, a).row(gin); + tensor::View row = chi_prompt.select(0, a).row(gin); row /= row.sum(); } } @@ -645,7 +645,7 @@ void XsData::combine( for (size_t a = 0; a < n_ang; a++) for (size_t d = 0; d < n_dg; d++) for (size_t gin = 0; gin < n_g; gin++) { - auto row = chi_delayed.select(0, a).select(0, d).row(gin); + tensor::View row = chi_delayed.select(0, a).select(0, d).row(gin); row /= row.sum(); } } From 59835794369213c8ac40524f0a462a52f6196c04 Mon Sep 17 00:00:00 2001 From: John Tramm Date: Wed, 11 Feb 2026 21:37:41 -0600 Subject: [PATCH 26/51] removed several Tensor constructors to simplify things --- include/openmc/tensor.h | 16 ---------------- include/openmc/xml_interface.h | 2 +- src/mgxs.cpp | 3 +-- src/nuclide.cpp | 2 +- src/source.cpp | 2 +- 5 files changed, 4 insertions(+), 21 deletions(-) diff --git a/include/openmc/tensor.h b/include/openmc/tensor.h index 721bbe571c1..e95f8deb0c8 100644 --- a/include/openmc/tensor.h +++ b/include/openmc/tensor.h @@ -525,20 +525,6 @@ class Tensor { : shape_(shape), data_(compute_size(), fill) {} - //! 1D copy from vector (disabled when T=size_t to avoid ambiguity) - template::value>> - explicit Tensor(const vector& vec) - : shape_({vec.size()}), data_(vec.begin(), vec.end()) - {} - - //! 1D copy from std::vector (disabled when T=size_t) - template::value>> - explicit Tensor(const std::vector& vec) - : shape_({vec.size()}), data_(vec.begin(), vec.end()) - {} - //! 1D copy from raw pointer + count Tensor(const T* ptr, size_t count) : shape_({count}), data_(ptr, ptr + count) @@ -600,8 +586,6 @@ class Tensor { } //! Assignment from initializer_list of values (1D) - template::value>> Tensor& operator=(std::initializer_list vals) { shape_ = {vals.size()}; diff --git a/include/openmc/xml_interface.h b/include/openmc/xml_interface.h index e0baa2eeaee..4d9918aa585 100644 --- a/include/openmc/xml_interface.h +++ b/include/openmc/xml_interface.h @@ -45,7 +45,7 @@ tensor::Tensor get_node_tensor( pugi::xml_node node, const char* name, bool lowercase = false) { vector v = get_node_array(node, name, lowercase); - return tensor::Tensor(v); + return tensor::Tensor(v.data(), v.size()); } std::vector get_node_position_array( diff --git a/src/mgxs.cpp b/src/mgxs.cpp index 57acba6a6c9..4a499ca5370 100644 --- a/src/mgxs.cpp +++ b/src/mgxs.cpp @@ -29,8 +29,7 @@ void Mgxs::init(const std::string& in_name, double in_awr, // Set the metadata name = in_name; awr = in_awr; - // TODO: Remove adapt when in_KTs is a Tensor - kTs = tensor::Tensor(in_kTs); + kTs = tensor::Tensor(in_kTs.data(), in_kTs.size()); fissionable = in_fissionable; scatter_format = in_scatter_format; xs.resize(in_kTs.size()); diff --git a/src/nuclide.cpp b/src/nuclide.cpp index 4cfebf82df4..2fcbb97b907 100644 --- a/src/nuclide.cpp +++ b/src/nuclide.cpp @@ -374,7 +374,7 @@ void Nuclide::create_derived( for (int t = 0; t < kTs_.size(); ++t) { int j = rx->xs_[t].threshold; int n = rx->xs_[t].value.size(); - auto xs = tensor::Tensor(rx->xs_[t].value); + auto xs = tensor::Tensor(rx->xs_[t].value.data(), rx->xs_[t].value.size()); for (const auto& p : rx->products_) { if (p.particle_.is_photon()) { for (int k = 0; k < n; ++k) { diff --git a/src/source.cpp b/src/source.cpp index 7712939df2b..121528fdfa9 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -400,7 +400,7 @@ SourceSite IndependentSource::sample(uint64_t* seed) const auto p = particle_.transport_index(); auto energy_ptr = dynamic_cast(energy_.get()); if (energy_ptr) { - auto energies = tensor::Tensor(energy_ptr->x()); + auto energies = tensor::Tensor(energy_ptr->x().data(), energy_ptr->x().size()); if ((energies > data::energy_max[p]).any()) { fatal_error("Source energy above range of energies of at least " "one cross section table"); From 8b3e22a6568e8558abe248c4a5af5f787722d05d Mon Sep 17 00:00:00 2001 From: John Tramm Date: Wed, 11 Feb 2026 21:53:54 -0600 Subject: [PATCH 27/51] Use T in view class instead of converting to value_type --- include/openmc/tensor.h | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/include/openmc/tensor.h b/include/openmc/tensor.h index e95f8deb0c8..73c38a1b0d7 100644 --- a/include/openmc/tensor.h +++ b/include/openmc/tensor.h @@ -60,8 +60,6 @@ using storage_type = typename storage_type_map::type; template class View { public: - using value_type = std::remove_const_t; - View(T* data, vector shape, vector strides) : data_(data), shape_(std::move(shape)), strides_(std::move(strides)) {} @@ -202,7 +200,7 @@ class View { } //! Assignment from initializer_list (for 1D views) - View& operator=(std::initializer_list vals) + View& operator=(std::initializer_list vals) { auto it = vals.begin(); for (size_t i = 0; i < size() && it != vals.end(); ++i, ++it) @@ -229,7 +227,7 @@ class View { View& operator+=(const Tensor& o); //! Compound multiply by scalar - View& operator*=(value_type val) + View& operator*=(T val) { size_t n = size(); for (size_t i = 0; i < n; ++i) @@ -238,7 +236,7 @@ class View { } //! Compound divide by scalar - View& operator/=(value_type val) + View& operator/=(T val) { size_t n = size(); for (size_t i = 0; i < n; ++i) @@ -247,9 +245,10 @@ class View { } //! Sum of all elements - value_type sum() const + T sum() const { - value_type s = value_type(0); + // remove_const needed so accumulator is mutable when T is const-qualified + std::remove_const_t s = 0; size_t n = size(); for (size_t i = 0; i < n; ++i) s += data_[flat_to_offset(i)]; From 64e347a8890543b9f7a453bc61f91e9343ffd5ad Mon Sep 17 00:00:00 2001 From: John Tramm Date: Wed, 11 Feb 2026 21:59:15 -0600 Subject: [PATCH 28/51] more view simplification --- include/openmc/tensor.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/include/openmc/tensor.h b/include/openmc/tensor.h index 73c38a1b0d7..3a91903a3d5 100644 --- a/include/openmc/tensor.h +++ b/include/openmc/tensor.h @@ -189,9 +189,7 @@ class View { // Assignment operators //! Fill all elements with a scalar - template - auto operator=(U val) -> - std::enable_if_t::value, View&> + View& operator=(T val) { size_t n = size(); for (size_t i = 0; i < n; ++i) From 52370cbe768db5346b6cbb784921ffd7798e0e44 Mon Sep 17 00:00:00 2001 From: John Tramm Date: Wed, 11 Feb 2026 22:06:00 -0600 Subject: [PATCH 29/51] more view simplification --- include/openmc/tensor.h | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/include/openmc/tensor.h b/include/openmc/tensor.h index 3a91903a3d5..990befe9b34 100644 --- a/include/openmc/tensor.h +++ b/include/openmc/tensor.h @@ -545,6 +545,8 @@ class Tensor { } //! Cross-type copy constructor + //! SFINAE guard needed: without !is_same, Tensor(const Tensor&) would + //! be ambiguous with the compiler-generated implicit copy constructor. template::value>> Tensor(const Tensor& other) @@ -559,6 +561,8 @@ class Tensor { // Assignment //! Cross-type assignment + //! SFINAE guard needed: without !is_same, this would be ambiguous with + //! the compiler-generated implicit copy assignment operator. template::value>> Tensor& operator=(const Tensor& other) @@ -662,12 +666,9 @@ class Tensor { data_.resize(compute_size()); } - template - void reshape(const ShapeType& new_shape) + void reshape(const vector& new_shape) { - shape_.clear(); - for (auto d : new_shape) - shape_.push_back(static_cast(d)); + shape_ = new_shape; } void fill(T val) { std::fill(data_.begin(), data_.end(), val); } @@ -974,21 +975,21 @@ class Tensor { // Free operators (scalar op tensor) //============================================================================== -template::value>> +template Tensor operator*(T val, const Tensor& arr) { return arr * val; } -template::value>> +template Tensor operator+(T val, const Tensor& arr) { return arr + val; } // Mixed-type arithmetic: Tensor op Tensor -> Tensor +// SFINAE guard needed: without !is_same, Tensor * Tensor would be +// ambiguous between the member operator* and this free function. template::value>> Tensor operator*(const Tensor& a, const Tensor& b) @@ -1000,6 +1001,7 @@ Tensor operator*(const Tensor& a, const Tensor& b) return r; } +// Same SFINAE guard as operator* above. template::value>> Tensor operator/(const Tensor& a, const Tensor& b) @@ -1076,17 +1078,17 @@ class StaticTensor2D { public: using value_type = T; + //! Templated to accept enum class indices (e.g. GlobalTally, TallyResult) + //! which don't implicitly convert to integer types. template T& operator()(I0 i, I1 j) { - return data_[static_cast(i) * C + - static_cast(j)]; + return data_[static_cast(i) * C + static_cast(j)]; } template const T& operator()(I0 i, I1 j) const { - return data_[static_cast(i) * C + - static_cast(j)]; + return data_[static_cast(i) * C + static_cast(j)]; } T* data() { return data_; } From 5c3cb83e2cddc514fa66280795d5e9486c099391 Mon Sep 17 00:00:00 2001 From: John Tramm Date: Wed, 11 Feb 2026 22:23:25 -0600 Subject: [PATCH 30/51] Simplification of view iterators --- include/openmc/tensor.h | 142 ++++++++++------------------------------ 1 file changed, 33 insertions(+), 109 deletions(-) diff --git a/include/openmc/tensor.h b/include/openmc/tensor.h index 990befe9b34..c93de3c6a02 100644 --- a/include/openmc/tensor.h +++ b/include/openmc/tensor.h @@ -256,13 +256,18 @@ class View { //-------------------------------------------------------------------------- // Iterators // - // Lightweight row-major iterator. Stores a flat logical position and - // converts to a physical offset on each dereference via flat_to_offset(). - // For contiguous 1D views (the common case) the divmod chain reduces to - // a single multiply-by-1, which the compiler optimizes away. + // Lightweight row-major iterator parameterized on pointer type (Ptr). + // Stores a flat logical position and converts to a physical offset on each + // dereference via divmod over shape/strides. For contiguous 1D views (the + // common case) the divmod chain reduces to a single multiply-by-1, which + // the compiler optimizes away. + // + // view_iterator = mutable iterator (from non-const View) + // view_iterator = read-only iterator (from const View) - class const_iterator { - const T* base_; + template + class view_iterator { + Ptr base_; size_t count_; const size_t* shape_; const size_t* strides_; @@ -272,11 +277,10 @@ class View { using iterator_category = std::random_access_iterator_tag; using value_type = std::remove_const_t; using difference_type = std::ptrdiff_t; - using pointer = const T*; - using reference = const T&; + using pointer = Ptr; + using reference = decltype(*std::declval()); - const_iterator( - const T* base, size_t count, const View* v) + view_iterator(Ptr base, size_t count, const View* v) : base_(base) , count_(count) , shape_(v->shape_.data()) @@ -284,79 +288,79 @@ class View { , ndim_(v->shape_.size()) {} - const T& operator*() const { return base_[offset()]; } - const T& operator[](difference_type n) const + reference operator*() const { return base_[offset()]; } + reference operator[](difference_type n) const { return base_[offset_of(count_ + n)]; } - const_iterator& operator++() + view_iterator& operator++() { ++count_; return *this; } - const_iterator operator++(int) + view_iterator operator++(int) { auto tmp = *this; ++count_; return tmp; } - const_iterator& operator--() + view_iterator& operator--() { --count_; return *this; } - const_iterator operator+(difference_type n) const + view_iterator operator+(difference_type n) const { auto tmp = *this; tmp.count_ += n; return tmp; } - const_iterator operator-(difference_type n) const + view_iterator operator-(difference_type n) const { auto tmp = *this; tmp.count_ -= n; return tmp; } - difference_type operator-(const const_iterator& o) const + difference_type operator-(const view_iterator& o) const { return static_cast(count_) - static_cast(o.count_); } - const_iterator& operator+=(difference_type n) + view_iterator& operator+=(difference_type n) { count_ += n; return *this; } - const_iterator& operator-=(difference_type n) + view_iterator& operator-=(difference_type n) { count_ -= n; return *this; } - bool operator==(const const_iterator& o) const + bool operator==(const view_iterator& o) const { return count_ == o.count_; } - bool operator!=(const const_iterator& o) const + bool operator!=(const view_iterator& o) const { return count_ != o.count_; } - bool operator<(const const_iterator& o) const + bool operator<(const view_iterator& o) const { return count_ < o.count_; } - bool operator>(const const_iterator& o) const + bool operator>(const view_iterator& o) const { return count_ > o.count_; } - bool operator<=(const const_iterator& o) const + bool operator<=(const view_iterator& o) const { return count_ <= o.count_; } - bool operator>=(const const_iterator& o) const + bool operator>=(const view_iterator& o) const { return count_ >= o.count_; } - friend const_iterator operator+(difference_type n, const const_iterator& it) + friend view_iterator operator+(difference_type n, const view_iterator& it) { return it + n; } @@ -374,88 +378,8 @@ class View { } }; - class iterator { - T* base_; - size_t count_; - const size_t* shape_; - const size_t* strides_; - size_t ndim_; - - public: - using iterator_category = std::random_access_iterator_tag; - using value_type = std::remove_const_t; - using difference_type = std::ptrdiff_t; - using pointer = T*; - using reference = T&; - - iterator(T* base, size_t count, const View* v) - : base_(base) - , count_(count) - , shape_(v->shape_.data()) - , strides_(v->strides_.data()) - , ndim_(v->shape_.size()) - {} - - T& operator*() { return base_[offset()]; } - T& operator[](difference_type n) { return base_[offset_of(count_ + n)]; } - iterator& operator++() - { - ++count_; - return *this; - } - iterator operator++(int) - { - auto tmp = *this; - ++count_; - return tmp; - } - iterator& operator--() - { - --count_; - return *this; - } - iterator operator+(difference_type n) const - { - auto tmp = *this; - tmp.count_ += n; - return tmp; - } - iterator operator-(difference_type n) const - { - auto tmp = *this; - tmp.count_ -= n; - return tmp; - } - difference_type operator-(const iterator& o) const - { - return static_cast(count_) - - static_cast(o.count_); - } - iterator& operator+=(difference_type n) - { - count_ += n; - return *this; - } - bool operator==(const iterator& o) const { return count_ == o.count_; } - bool operator!=(const iterator& o) const { return count_ != o.count_; } - bool operator<(const iterator& o) const { return count_ < o.count_; } - friend iterator operator+(difference_type n, const iterator& it) - { - return it + n; - } - - private: - size_t offset() const { return offset_of(count_); } - size_t offset_of(size_t flat) const - { - size_t off = 0; - for (int d = static_cast(ndim_) - 1; d >= 0; --d) { - off += (flat % shape_[d]) * strides_[d]; - flat /= shape_[d]; - } - return off; - } - }; + using iterator = view_iterator; + using const_iterator = view_iterator; iterator begin() { return {data_, 0, this}; } iterator end() { return {data_, size(), this}; } From 9bb639af83ed485e972adbf471ad0aaf8f18514a Mon Sep 17 00:00:00 2001 From: John Tramm Date: Wed, 11 Feb 2026 22:39:03 -0600 Subject: [PATCH 31/51] tensor.h cleanup --- include/openmc/hdf5_interface.h | 8 ++++-- include/openmc/tensor.h | 45 +++++++++++++++++++++++++-------- 2 files changed, 40 insertions(+), 13 deletions(-) diff --git a/include/openmc/hdf5_interface.h b/include/openmc/hdf5_interface.h index 36cfb273c8a..2d9edc827a6 100644 --- a/include/openmc/hdf5_interface.h +++ b/include/openmc/hdf5_interface.h @@ -394,7 +394,8 @@ inline void write_attribute(hid_t obj_id, const char* name, Position r) // Templates/overloads for write_dataset //============================================================================== -// Template for scalars (ensured by SFINAE) +// Template for scalars. SFINAE guard needed to prevent this template from +// matching Tensor/vector/string types that have their own overloads below. template inline std::enable_if_t>::value> write_dataset( hid_t obj_id, const char* name, T buffer) @@ -452,7 +453,10 @@ inline void write_dataset( false, buffer.data()); } -// Template for Tensor and StaticTensor2D +// Template for Tensor and StaticTensor2D. SFINAE guard needed to prevent +// this template from matching vector/string types that have their own +// overloads above. Uses a generic Container to avoid duplicating the body +// for both Tensor and StaticTensor2D. template>::value>> inline void write_dataset( diff --git a/include/openmc/tensor.h b/include/openmc/tensor.h index c93de3c6a02..2657babf113 100644 --- a/include/openmc/tensor.h +++ b/include/openmc/tensor.h @@ -60,6 +60,9 @@ using storage_type = typename storage_type_map::type; template class View { public: + //-------------------------------------------------------------------------- + // Constructors + View(T* data, vector shape, vector strides) : data_(data), shape_(std::move(shape)), strides_(std::move(strides)) {} @@ -69,6 +72,9 @@ class View { View(const View&) = default; View(View&&) = default; + //-------------------------------------------------------------------------- + // Assignment operators + //! Copy assignment: element-wise deep copy (writes through data pointer). //! Without this, the compiler's implicit copy assignment just copies the //! View metadata (pointer, shape, strides) instead of the viewed data. @@ -185,9 +191,6 @@ class View { return {data_ + start * strides_[0], {shape_[0] - start}, {strides_[0]}}; } - //-------------------------------------------------------------------------- - // Assignment operators - //! Fill all elements with a scalar View& operator=(T val) { @@ -220,6 +223,9 @@ class View { template View& operator=(const Tensor& other); + //-------------------------------------------------------------------------- + // Compound assignment operators + //! Compound addition from Tensor (forward-declared, defined after Tensor) template View& operator+=(const Tensor& o); @@ -242,6 +248,9 @@ class View { return *this; } + //-------------------------------------------------------------------------- + // Reductions + //! Sum of all elements T sum() const { @@ -896,7 +905,7 @@ class Tensor { }; //============================================================================== -// Free operators (scalar op tensor) +// Non-member operators (scalar op tensor) //============================================================================== template @@ -913,7 +922,7 @@ Tensor operator+(T val, const Tensor& arr) // Mixed-type arithmetic: Tensor op Tensor -> Tensor // SFINAE guard needed: without !is_same, Tensor * Tensor would be -// ambiguous between the member operator* and this free function. +// ambiguous between the member operator* and this non-member function. template::value>> Tensor operator*(const Tensor& a, const Tensor& b) @@ -938,7 +947,7 @@ Tensor operator/(const Tensor& a, const Tensor& b) } //============================================================================== -// View forward-declared method definitions (need Tensor to be complete) +// Out-of-line method definitions (require complete types) //============================================================================== template @@ -961,10 +970,6 @@ View& View::operator+=(const Tensor& o) return *this; } -//============================================================================== -// Tensor::sum(axis) — reduces one dimension -//============================================================================== - template Tensor Tensor::sum(size_t axis) const { @@ -1002,6 +1007,9 @@ class StaticTensor2D { public: using value_type = T; + //-------------------------------------------------------------------------- + // Indexing + //! Templated to accept enum class indices (e.g. GlobalTally, TallyResult) //! which don't implicitly convert to integer types. template @@ -1015,18 +1023,30 @@ class StaticTensor2D { return data_[static_cast(i) * C + static_cast(j)]; } + //-------------------------------------------------------------------------- + // Accessors + T* data() { return data_; } const T* data() const { return data_; } constexpr size_t size() const { return R * C; } std::array shape() const { return {R, C}; } + //-------------------------------------------------------------------------- + // Mutation + void fill(T val) { std::fill(data_, data_ + R * C, val); } + //-------------------------------------------------------------------------- + // Iterators + T* begin() { return data_; } T* end() { return data_ + R * C; } const T* begin() const { return data_; } const T* end() const { return data_ + R * C; } + //-------------------------------------------------------------------------- + // View accessors + //! Column view (1D, strided) View col(size_t j) { return {data_ + j, {R}, {C}}; } View col(size_t j) const { return {data_ + j, {R}, {C}}; } @@ -1036,11 +1056,14 @@ class StaticTensor2D { View flat() const { return {data_, {R * C}, {size_t(1)}}; } private: + //-------------------------------------------------------------------------- + // Data members + T data_[R * C] = {}; }; //============================================================================== -// Free functions +// Non-member functions //============================================================================== // zeros From b14afa9168dddaa9c122677e904d3effc2ffceb1 Mon Sep 17 00:00:00 2001 From: John Tramm Date: Wed, 11 Feb 2026 23:01:57 -0600 Subject: [PATCH 32/51] add some docstrings to more tensor functions --- include/openmc/tensor.h | 33 ++++++++++----------------------- 1 file changed, 10 insertions(+), 23 deletions(-) diff --git a/include/openmc/tensor.h b/include/openmc/tensor.h index 2657babf113..e153cf7287d 100644 --- a/include/openmc/tensor.h +++ b/include/openmc/tensor.h @@ -115,7 +115,7 @@ class View { const T& operator[](size_t i) const { return data_[flat_to_offset(i)]; } //-------------------------------------------------------------------------- - // Shape queries + // Accessors size_t size() const { @@ -132,7 +132,7 @@ class View { const T* data() const { return data_; } //-------------------------------------------------------------------------- - // Sub-view methods + // View accessors //! Fix one axis at a given index, returning an (N-1)-dimensional view View select(size_t axis, size_t idx) @@ -223,9 +223,6 @@ class View { template View& operator=(const Tensor& other); - //-------------------------------------------------------------------------- - // Compound assignment operators - //! Compound addition from Tensor (forward-declared, defined after Tensor) template View& operator+=(const Tensor& o); @@ -683,8 +680,9 @@ class Tensor { } //-------------------------------------------------------------------------- - // Reductions + // Reductions and transforms + //! Sum of all elements T sum() const { T s = T(0); @@ -696,6 +694,7 @@ class Tensor { //! Sum along an axis, reducing rank by 1 (defined out-of-line below) Tensor sum(size_t axis) const; + //! Product of all elements T prod() const { T p = T(1); @@ -704,6 +703,7 @@ class Tensor { return p; } + //! True if any element is nonzero bool any() const { for (size_t i = 0; i < data_.size(); ++i) @@ -712,6 +712,7 @@ class Tensor { return false; } + //! True if all elements are nonzero bool all() const { for (size_t i = 0; i < data_.size(); ++i) @@ -720,6 +721,7 @@ class Tensor { return true; } + //! Flat index of the minimum element size_t argmin() const { return static_cast( @@ -727,9 +729,7 @@ class Tensor { std::min_element(data_.data(), data_.data() + data_.size()))); } - //-------------------------------------------------------------------------- - // Flip - + //! Reverse element order along an axis (e.g. flip(0) reverses rows) Tensor flip(size_t axis) const { size_t outer_size = 1; @@ -750,7 +750,7 @@ class Tensor { } //-------------------------------------------------------------------------- - // Compound assignment operators (scalar) + // Operators Tensor& operator+=(T val) { @@ -776,10 +776,6 @@ class Tensor { x /= val; return *this; } - - //-------------------------------------------------------------------------- - // Compound assignment operators (tensor) - Tensor& operator+=(const Tensor& o) { for (size_t i = 0; i < data_.size(); ++i) @@ -787,9 +783,6 @@ class Tensor { return *this; } - //-------------------------------------------------------------------------- - // Element-wise binary operators (tensor op tensor) - Tensor operator+(const Tensor& o) const { Tensor r(shape_); @@ -812,9 +805,6 @@ class Tensor { return r; } - //-------------------------------------------------------------------------- - // Element-wise binary operators (tensor op scalar) - Tensor operator+(T val) const { Tensor r(shape_); @@ -837,9 +827,6 @@ class Tensor { return r; } - //-------------------------------------------------------------------------- - // Element-wise comparison operators (return Tensor) - Tensor operator<=(T val) const { Tensor r(shape_); From 470bf0236dea26a3bf5a02ad46eec1599f82cc64 Mon Sep 17 00:00:00 2001 From: John Tramm Date: Wed, 11 Feb 2026 23:13:20 -0600 Subject: [PATCH 33/51] tensor comment cleanup --- include/openmc/hdf5_interface.h | 10 +++++----- include/openmc/tensor.h | 27 +++++++++++++-------------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/include/openmc/hdf5_interface.h b/include/openmc/hdf5_interface.h index 2d9edc827a6..60a634cffc8 100644 --- a/include/openmc/hdf5_interface.h +++ b/include/openmc/hdf5_interface.h @@ -394,8 +394,8 @@ inline void write_attribute(hid_t obj_id, const char* name, Position r) // Templates/overloads for write_dataset //============================================================================== -// Template for scalars. SFINAE guard needed to prevent this template from -// matching Tensor/vector/string types that have their own overloads below. +// Template for scalars. A SFINAE guard is used here to prevent this template +// from matching Tensor/vector/string types that have their own overloads below. template inline std::enable_if_t>::value> write_dataset( hid_t obj_id, const char* name, T buffer) @@ -453,9 +453,9 @@ inline void write_dataset( false, buffer.data()); } -// Template for Tensor and StaticTensor2D. SFINAE guard needed to prevent -// this template from matching vector/string types that have their own -// overloads above. Uses a generic Container to avoid duplicating the body +// Template for Tensor and StaticTensor2D. A SFINAE guard is used here to +// prevent this template from matching vector/string types that have their own +// overloads above. A generic Container parameter avoids duplicating the body // for both Tensor and StaticTensor2D. template>::value>> diff --git a/include/openmc/tensor.h b/include/openmc/tensor.h index e153cf7287d..4c19b5ddb73 100644 --- a/include/openmc/tensor.h +++ b/include/openmc/tensor.h @@ -475,8 +475,8 @@ class Tensor { } //! Cross-type copy constructor - //! SFINAE guard needed: without !is_same, Tensor(const Tensor&) would - //! be ambiguous with the compiler-generated implicit copy constructor. + //! A SFINAE guard is used here, as without !is_same this would be + //! ambiguous with the compiler-generated implicit copy constructor. template::value>> Tensor(const Tensor& other) @@ -491,8 +491,8 @@ class Tensor { // Assignment //! Cross-type assignment - //! SFINAE guard needed: without !is_same, this would be ambiguous with - //! the compiler-generated implicit copy assignment operator. + //! A SFINAE guard is used here, as without !is_same this would be + //! ambiguous with the compiler-generated implicit copy assignment operator. template::value>> Tensor& operator=(const Tensor& other) @@ -908,8 +908,8 @@ Tensor operator+(T val, const Tensor& arr) } // Mixed-type arithmetic: Tensor op Tensor -> Tensor -// SFINAE guard needed: without !is_same, Tensor * Tensor would be -// ambiguous between the member operator* and this non-member function. +// A SFINAE guard is used here, as without !is_same Tensor * Tensor +// would be ambiguous between the member operator* and this non-member function. template::value>> Tensor operator*(const Tensor& a, const Tensor& b) @@ -1053,7 +1053,6 @@ class StaticTensor2D { // Non-member functions //============================================================================== -// zeros template Tensor zeros(std::initializer_list shape) { @@ -1067,21 +1066,19 @@ Tensor zeros(const vector& shape) return Tensor(shape, T(0)); } -// zeros_like template Tensor zeros_like(const Tensor& o) { return Tensor(o.shape(), T(0)); } -// full_like template Tensor full_like(const Tensor& o, V val) { return Tensor(o.shape(), static_cast(val)); } -// linspace +//! Return a 1D tensor of n evenly spaced values from start to stop (inclusive) template Tensor linspace(T start, T stop, size_t n) { @@ -1097,7 +1094,7 @@ Tensor linspace(T start, T stop, size_t n) return result; } -// concatenate (two 1D tensors) +//! Concatenate two 1D tensors end-to-end template Tensor concatenate(const Tensor& a, const Tensor& b) { @@ -1108,7 +1105,7 @@ Tensor concatenate(const Tensor& a, const Tensor& b) return result; } -// Element-wise math +//! Element-wise natural logarithm template Tensor log(const Tensor& a) { @@ -1118,6 +1115,7 @@ Tensor log(const Tensor& a) return r; } +//! Element-wise absolute value template Tensor abs(const Tensor& a) { @@ -1127,7 +1125,8 @@ Tensor abs(const Tensor& a) return r; } -// where with tensor true_val and scalar false_val +//! Element-wise conditional: select from true_val where cond is true, +//! otherwise use false_val template Tensor where( const Tensor& cond, const Tensor& true_val, V false_val) @@ -1139,7 +1138,7 @@ Tensor where( return r; } -// nan_to_num +//! Replace NaN/Inf values with finite substitutes template Tensor nan_to_num(const Tensor& a, T nan_val = T(0), T posinf_val = std::numeric_limits::max(), From 9835b1f71be35a9bdbd88cb5e5cc5cbf5f3fc354 Mon Sep 17 00:00:00 2001 From: John Tramm Date: Thu, 12 Feb 2026 09:31:58 -0600 Subject: [PATCH 34/51] removed a few unneeded functions --- include/openmc/tensor.h | 34 ---------------------------------- 1 file changed, 34 deletions(-) diff --git a/include/openmc/tensor.h b/include/openmc/tensor.h index 4c19b5ddb73..24451fdc023 100644 --- a/include/openmc/tensor.h +++ b/include/openmc/tensor.h @@ -127,7 +127,6 @@ class View { size_t ndim() const { return shape_.size(); } size_t shape(size_t axis) const { return shape_[axis]; } const vector& shape_vec() const { return shape_; } - const vector& strides_vec() const { return strides_; } T* data() { return data_; } const T* data() const { return data_; } @@ -457,12 +456,6 @@ class Tensor { : shape_({count}), data_(ptr, ptr + count) {} - //! Copy from vector with explicit shape - template - Tensor(const std::vector& vec, vector shape) - : shape_(std::move(shape)), data_(vec.begin(), vec.end()) - {} - //! Copy from View (preserves view's shape) template Tensor(const View& v) @@ -474,36 +467,9 @@ class Tensor { data_[i] = v[i]; } - //! Cross-type copy constructor - //! A SFINAE guard is used here, as without !is_same this would be - //! ambiguous with the compiler-generated implicit copy constructor. - template::value>> - Tensor(const Tensor& other) - : shape_(other.shape()) - { - data_.resize(other.size()); - for (size_t i = 0; i < other.size(); ++i) - data_[i] = static_cast(other.data()[i]); - } - //-------------------------------------------------------------------------- // Assignment - //! Cross-type assignment - //! A SFINAE guard is used here, as without !is_same this would be - //! ambiguous with the compiler-generated implicit copy assignment operator. - template::value>> - Tensor& operator=(const Tensor& other) - { - shape_ = other.shape(); - data_.resize(other.size()); - for (size_t i = 0; i < other.size(); ++i) - data_[i] = static_cast(other.data()[i]); - return *this; - } - //! Assignment from View template Tensor& operator=(const View& v) From 0ffbcff98ea499200be68714137d4a17c15b470d Mon Sep 17 00:00:00 2001 From: John Tramm Date: Thu, 12 Feb 2026 09:41:36 -0600 Subject: [PATCH 35/51] removed xtensor mention in docs, and added ctest unit tests for tensor classes --- docs/source/quickinstall.rst | 2 +- tests/cpp_unit_tests/CMakeLists.txt | 1 + tests/cpp_unit_tests/test_tensor.cpp | 924 +++++++++++++++++++++++++++ 3 files changed, 926 insertions(+), 1 deletion(-) create mode 100644 tests/cpp_unit_tests/test_tensor.cpp diff --git a/docs/source/quickinstall.rst b/docs/source/quickinstall.rst index 0f887940ea5..6cb774b5b07 100644 --- a/docs/source/quickinstall.rst +++ b/docs/source/quickinstall.rst @@ -119,7 +119,7 @@ packages should be installed, for example in Homebrew via: .. code-block:: sh - brew install llvm cmake xtensor hdf5 python libomp libpng + brew install llvm cmake hdf5 python libomp libpng The compiler provided by the above LLVM package should be used in place of the one provisioned by XCode, which does not support the multithreading library used diff --git a/tests/cpp_unit_tests/CMakeLists.txt b/tests/cpp_unit_tests/CMakeLists.txt index 20341b420f8..ce7e539ea57 100644 --- a/tests/cpp_unit_tests/CMakeLists.txt +++ b/tests/cpp_unit_tests/CMakeLists.txt @@ -7,6 +7,7 @@ set(TEST_NAMES test_mcpl_stat_sum test_mesh test_region + test_tensor # Add additional unit test files here ) diff --git a/tests/cpp_unit_tests/test_tensor.cpp b/tests/cpp_unit_tests/test_tensor.cpp new file mode 100644 index 00000000000..4de14e7bd34 --- /dev/null +++ b/tests/cpp_unit_tests/test_tensor.cpp @@ -0,0 +1,924 @@ +#include +#include + +#include +#include + +#include "openmc/tensor.h" + +using namespace openmc; +using namespace openmc::tensor; + +// ============================================================================ +// Tensor constructors +// ============================================================================ + +TEST_CASE("Tensor default constructor") +{ + Tensor t; + REQUIRE(t.size() == 0); + REQUIRE(t.empty()); + REQUIRE(t.shape().empty()); +} + +TEST_CASE("Tensor shape constructor") +{ + Tensor t1({5}); + REQUIRE(t1.size() == 5); + REQUIRE(t1.shape().size() == 1); + REQUIRE(t1.shape(0) == 5); + + Tensor t2({3, 4}); + REQUIRE(t2.size() == 12); + REQUIRE(t2.shape().size() == 2); + REQUIRE(t2.shape(0) == 3); + REQUIRE(t2.shape(1) == 4); + + Tensor t3({2, 3, 4}); + REQUIRE(t3.size() == 24); + REQUIRE(t3.shape().size() == 3); +} + +TEST_CASE("Tensor shape + fill constructor") +{ + Tensor t({2, 3}, 7.0); + REQUIRE(t.size() == 6); + for (size_t i = 0; i < t.size(); ++i) + REQUIRE(t[i] == 7.0); +} + +TEST_CASE("Tensor pointer constructor") +{ + double vals[] = {1.0, 2.0, 3.0, 4.0}; + Tensor t(vals, 4); + REQUIRE(t.size() == 4); + REQUIRE(t.shape(0) == 4); + REQUIRE(t[0] == 1.0); + REQUIRE(t[1] == 2.0); + REQUIRE(t[2] == 3.0); + REQUIRE(t[3] == 4.0); +} + +TEST_CASE("Tensor copy and move") +{ + Tensor a({2, 3}, 5.0); + Tensor b(a); + REQUIRE(b.size() == 6); + REQUIRE(b(0, 0) == 5.0); + // Modifying copy doesn't affect original + b(0, 0) = 99.0; + REQUIRE(a(0, 0) == 5.0); + + Tensor c(std::move(b)); + REQUIRE(c(0, 0) == 99.0); + REQUIRE(c.size() == 6); +} + +// ============================================================================ +// Tensor indexing +// ============================================================================ + +TEST_CASE("Tensor 1D indexing") +{ + Tensor t({4}, 0); + t[0] = 10; + t[1] = 20; + t[2] = 30; + t[3] = 40; + REQUIRE(t(0) == 10); + REQUIRE(t(1) == 20); + REQUIRE(t(2) == 30); + REQUIRE(t(3) == 40); +} + +TEST_CASE("Tensor 2D indexing (row-major)") +{ + // Layout: [[1, 2, 3], [4, 5, 6]] + Tensor t({2, 3}, 0); + int val = 1; + for (size_t i = 0; i < 2; ++i) + for (size_t j = 0; j < 3; ++j) + t(i, j) = val++; + + REQUIRE(t(0, 0) == 1); + REQUIRE(t(0, 2) == 3); + REQUIRE(t(1, 0) == 4); + REQUIRE(t(1, 2) == 6); + // Flat index should match row-major order + REQUIRE(t[0] == 1); + REQUIRE(t[3] == 4); + REQUIRE(t[5] == 6); +} + +TEST_CASE("Tensor 3D indexing") +{ + // 2x3x4 tensor + Tensor t({2, 3, 4}, 0); + t(1, 2, 3) = 42; + // Flat index: 1*12 + 2*4 + 3 = 23 + REQUIRE(t[23] == 42); + REQUIRE(t(1, 2, 3) == 42); +} + +// ============================================================================ +// Tensor assignment +// ============================================================================ + +TEST_CASE("Tensor initializer_list assignment") +{ + Tensor t; + t = {1.0, 2.0, 3.0}; + REQUIRE(t.size() == 3); + REQUIRE(t.shape(0) == 3); + REQUIRE(t[0] == 1.0); + REQUIRE(t[2] == 3.0); +} + +// ============================================================================ +// Tensor mutation +// ============================================================================ + +TEST_CASE("Tensor resize") +{ + Tensor t({2, 3}, 1.0); + REQUIRE(t.size() == 6); + t.resize({4, 5}); + REQUIRE(t.size() == 20); + REQUIRE(t.shape(0) == 4); + REQUIRE(t.shape(1) == 5); +} + +TEST_CASE("Tensor reshape") +{ + Tensor t({12}, 0); + for (size_t i = 0; i < 12; ++i) + t[i] = static_cast(i); + + t.reshape({3, 4}); + REQUIRE(t.shape(0) == 3); + REQUIRE(t.shape(1) == 4); + REQUIRE(t.size() == 12); + // Data unchanged, just reinterpreted + REQUIRE(t(0, 0) == 0); + REQUIRE(t(1, 0) == 4); // row 1, col 0 = flat index 4 + REQUIRE(t(2, 3) == 11); // row 2, col 3 = flat index 11 +} + +TEST_CASE("Tensor fill") +{ + Tensor t({3, 3}, 0.0); + t.fill(42.0); + for (size_t i = 0; i < t.size(); ++i) + REQUIRE(t[i] == 42.0); +} + +// ============================================================================ +// Tensor iterators +// ============================================================================ + +TEST_CASE("Tensor iterators") +{ + Tensor t({4}, 0); + t = {10, 20, 30, 40}; + int sum = 0; + for (auto val : t) + sum += val; + REQUIRE(sum == 100); +} + +// ============================================================================ +// Tensor reductions +// ============================================================================ + +TEST_CASE("Tensor sum (full)") +{ + Tensor t({3}, 0.0); + t = {1.0, 2.0, 3.0}; + REQUIRE(t.sum() == 6.0); +} + +TEST_CASE("Tensor sum (axis) on 2D") +{ + // [[1, 2, 3], + // [4, 5, 6]] + Tensor t({2, 3}, 0); + int v = 1; + for (size_t i = 0; i < 2; ++i) + for (size_t j = 0; j < 3; ++j) + t(i, j) = v++; + + // Sum along axis 0 -> [5, 7, 9] + Tensor s0 = t.sum(0); + REQUIRE(s0.size() == 3); + REQUIRE(s0[0] == 5); + REQUIRE(s0[1] == 7); + REQUIRE(s0[2] == 9); + + // Sum along axis 1 -> [6, 15] + Tensor s1 = t.sum(1); + REQUIRE(s1.size() == 2); + REQUIRE(s1[0] == 6); + REQUIRE(s1[1] == 15); +} + +TEST_CASE("Tensor sum (axis) on 3D") +{ + // 2x3x2 tensor filled with sequential values 1..12 + Tensor t({2, 3, 2}, 0); + int v = 1; + for (size_t i = 0; i < 2; ++i) + for (size_t j = 0; j < 3; ++j) + for (size_t k = 0; k < 2; ++k) + t(i, j, k) = v++; + + // Sum along axis 1 (middle) -> 2x2, each sums 3 values + // [0,0]: t(0,0,0)+t(0,1,0)+t(0,2,0) = 1+3+5 = 9 + // [0,1]: t(0,0,1)+t(0,1,1)+t(0,2,1) = 2+4+6 = 12 + // [1,0]: t(1,0,0)+t(1,1,0)+t(1,2,0) = 7+9+11 = 27 + // [1,1]: t(1,0,1)+t(1,1,1)+t(1,2,1) = 8+10+12 = 30 + Tensor s = t.sum(1); + REQUIRE(s.shape(0) == 2); + REQUIRE(s.shape(1) == 2); + REQUIRE(s(0, 0) == 9); + REQUIRE(s(0, 1) == 12); + REQUIRE(s(1, 0) == 27); + REQUIRE(s(1, 1) == 30); +} + +TEST_CASE("Tensor prod") +{ + Tensor t({4}, 0); + t = {1, 2, 3, 4}; + REQUIRE(t.prod() == 24); +} + +TEST_CASE("Tensor any and all") +{ + Tensor t({4}, false); + REQUIRE(!t.any()); + REQUIRE(!t.all()); + + // Set one element true + t.data()[0] = true; + REQUIRE(t.any()); + REQUIRE(!t.all()); + + // Set all true + for (size_t i = 0; i < t.size(); ++i) + t.data()[i] = true; + REQUIRE(t.any()); + REQUIRE(t.all()); +} + +TEST_CASE("Tensor argmin") +{ + Tensor t({5}, 0.0); + t = {3.0, 1.0, 4.0, 0.5, 2.0}; + REQUIRE(t.argmin() == 3); +} + +TEST_CASE("Tensor flip") +{ + Tensor t({5}, 0); + t = {1, 2, 3, 4, 5}; + Tensor f = t.flip(0); + REQUIRE(f[0] == 5); + REQUIRE(f[1] == 4); + REQUIRE(f[2] == 3); + REQUIRE(f[3] == 2); + REQUIRE(f[4] == 1); +} + +TEST_CASE("Tensor flip 2D") +{ + // [[1, 2], [3, 4], [5, 6]] + Tensor t({3, 2}, 0); + t(0, 0) = 1; t(0, 1) = 2; + t(1, 0) = 3; t(1, 1) = 4; + t(2, 0) = 5; t(2, 1) = 6; + + // Flip axis 0 reverses rows -> [[5,6],[3,4],[1,2]] + Tensor f = t.flip(0); + REQUIRE(f(0, 0) == 5); + REQUIRE(f(0, 1) == 6); + REQUIRE(f(1, 0) == 3); + REQUIRE(f(2, 0) == 1); +} + +// ============================================================================ +// Tensor operators +// ============================================================================ + +TEST_CASE("Tensor scalar compound assignment") +{ + Tensor t({3}, 0.0); + t = {2.0, 4.0, 6.0}; + + t += 1.0; + REQUIRE(t[0] == 3.0); + REQUIRE(t[1] == 5.0); + + t -= 1.0; + REQUIRE(t[0] == 2.0); + + t *= 3.0; + REQUIRE(t[0] == 6.0); + REQUIRE(t[1] == 12.0); + + t /= 2.0; + REQUIRE(t[0] == 3.0); + REQUIRE(t[1] == 6.0); +} + +TEST_CASE("Tensor element-wise arithmetic") +{ + Tensor a({3}, 0.0); + Tensor b({3}, 0.0); + a = {1.0, 2.0, 3.0}; + b = {4.0, 5.0, 6.0}; + + Tensor c = a + b; + REQUIRE(c[0] == 5.0); + REQUIRE(c[1] == 7.0); + REQUIRE(c[2] == 9.0); + + c = a - b; + REQUIRE(c[0] == -3.0); + + c = a / b; + REQUIRE(c[0] == 0.25); +} + +TEST_CASE("Tensor scalar arithmetic") +{ + Tensor a({3}, 0.0); + a = {1.0, 2.0, 3.0}; + + Tensor b = a + 10.0; + REQUIRE(b[0] == 11.0); + REQUIRE(b[2] == 13.0); + + b = a - 1.0; + REQUIRE(b[0] == 0.0); + + b = a * 2.0; + REQUIRE(b[0] == 2.0); + REQUIRE(b[2] == 6.0); + + // Non-member scalar * tensor (commutativity) + b = 2.0 * a; + REQUIRE(b[0] == 2.0); + REQUIRE(b[2] == 6.0); + + // Non-member scalar + tensor + b = 10.0 + a; + REQUIRE(b[0] == 11.0); +} + +TEST_CASE("Tensor compound addition with tensor") +{ + Tensor a({3}, 0.0); + Tensor b({3}, 0.0); + a = {1.0, 2.0, 3.0}; + b = {10.0, 20.0, 30.0}; + a += b; + REQUIRE(a[0] == 11.0); + REQUIRE(a[1] == 22.0); + REQUIRE(a[2] == 33.0); +} + +TEST_CASE("Tensor comparison operators") +{ + Tensor t({4}, 0.0); + t = {1.0, 2.0, 3.0, 4.0}; + + Tensor r = t < 3.0; + REQUIRE(r.data()[0] == true); + REQUIRE(r.data()[1] == true); + REQUIRE(r.data()[2] == false); + REQUIRE(r.data()[3] == false); + + r = t >= 3.0; + REQUIRE(r.data()[0] == false); + REQUIRE(r.data()[2] == true); + REQUIRE(r.data()[3] == true); + + r = t <= 2.0; + REQUIRE(r.data()[0] == true); + REQUIRE(r.data()[1] == true); + REQUIRE(r.data()[2] == false); + + r = t > 3.0; + REQUIRE(r.data()[0] == false); + REQUIRE(r.data()[3] == true); +} + +TEST_CASE("Tensor element-wise comparison") +{ + Tensor a({3}, 0.0); + Tensor b({3}, 0.0); + a = {1.0, 5.0, 3.0}; + b = {2.0, 4.0, 3.0}; + + Tensor r = a < b; + REQUIRE(r.data()[0] == true); + REQUIRE(r.data()[1] == false); + REQUIRE(r.data()[2] == false); +} + +TEST_CASE("Tensor mixed-type multiply") +{ + Tensor a({3}, 0); + Tensor b({3}, 0.0); + a = {2, 3, 4}; + b = {1.5, 2.5, 3.5}; + + Tensor c = a * b; + REQUIRE(c[0] == 3.0); + REQUIRE(c[1] == 7.5); + REQUIRE(c[2] == 14.0); +} + +TEST_CASE("Tensor mixed-type divide") +{ + Tensor a({3}, 0.0); + Tensor b({3}, 0); + a = {10.0, 20.0, 30.0}; + b = {2, 4, 5}; + + Tensor c = a / b; + REQUIRE(c[0] == 5.0); + REQUIRE(c[1] == 5.0); + REQUIRE(c[2] == 6.0); +} + +// ============================================================================ +// Tensor bool specialization +// ============================================================================ + +TEST_CASE("Tensor storage") +{ + // Tensor uses unsigned char internally to avoid std::vector proxy + Tensor t({4}, false); + t.data()[0] = true; + t.data()[2] = true; + REQUIRE(t.any()); + REQUIRE(!t.all()); + REQUIRE(t.data()[0] == true); + REQUIRE(t.data()[1] == false); +} + +// ============================================================================ +// View (via Tensor accessors) +// ============================================================================ + +TEST_CASE("Tensor row view") +{ + // [[1, 2, 3], [4, 5, 6]] + Tensor t({2, 3}, 0); + int v = 1; + for (size_t i = 0; i < 2; ++i) + for (size_t j = 0; j < 3; ++j) + t(i, j) = v++; + + auto r0 = t.row(0); + REQUIRE(r0.size() == 3); + REQUIRE(r0[0] == 1); + REQUIRE(r0[1] == 2); + REQUIRE(r0[2] == 3); + + auto r1 = t.row(1); + REQUIRE(r1[0] == 4); + REQUIRE(r1[1] == 5); + REQUIRE(r1[2] == 6); + + // Writing through view modifies the tensor + r0[1] = 99; + REQUIRE(t(0, 1) == 99); +} + +TEST_CASE("Tensor col view") +{ + // [[1, 2], [3, 4], [5, 6]] + Tensor t({3, 2}, 0); + t(0, 0) = 1; t(0, 1) = 2; + t(1, 0) = 3; t(1, 1) = 4; + t(2, 0) = 5; t(2, 1) = 6; + + auto c0 = t.col(0); + REQUIRE(c0.size() == 3); + REQUIRE(c0[0] == 1); + REQUIRE(c0[1] == 3); + REQUIRE(c0[2] == 5); + + auto c1 = t.col(1); + REQUIRE(c1[0] == 2); + REQUIRE(c1[1] == 4); + REQUIRE(c1[2] == 6); + + // Write through column view + c1[0] = 77; + REQUIRE(t(0, 1) == 77); +} + +TEST_CASE("Tensor slice view") +{ + Tensor t({6}, 0); + t = {10, 20, 30, 40, 50, 60}; + + // slice(start, end) + auto s = t.slice(1, 4); + REQUIRE(s.size() == 3); + REQUIRE(s[0] == 20); + REQUIRE(s[1] == 30); + REQUIRE(s[2] == 40); + + // slice(start) to end + auto s2 = t.slice(3); + REQUIRE(s2.size() == 3); + REQUIRE(s2[0] == 40); + REQUIRE(s2[2] == 60); + + // Write through slice + s[0] = 99; + REQUIRE(t[1] == 99); +} + +TEST_CASE("Tensor flat view") +{ + Tensor t({2, 3}, 0); + int v = 1; + for (size_t i = 0; i < 2; ++i) + for (size_t j = 0; j < 3; ++j) + t(i, j) = v++; + + auto f = t.flat(); + REQUIRE(f.size() == 6); + REQUIRE(f[0] == 1); + REQUIRE(f[5] == 6); +} + +TEST_CASE("Tensor select (general axis)") +{ + // 2x3x4 tensor + Tensor t({2, 3, 4}, 0); + int v = 0; + for (size_t i = 0; i < 2; ++i) + for (size_t j = 0; j < 3; ++j) + for (size_t k = 0; k < 4; ++k) + t(i, j, k) = v++; + + // select(0, 1) -> fix first axis at 1 -> 3x4 view + auto s = t.select(0, 1); + REQUIRE(s.size() == 12); + // t(1,0,0) = 12, t(1,0,1) = 13, ... + REQUIRE(s(0, 0) == 12); + REQUIRE(s(0, 1) == 13); + REQUIRE(s(2, 3) == 23); + + // select(1, 2) -> fix second axis at 2 -> 2x4 view + auto s2 = t.select(1, 2); + REQUIRE(s2.size() == 8); + // t(0,2,0)=8, t(0,2,1)=9, t(1,2,0)=20 + REQUIRE(s2(0, 0) == 8); + REQUIRE(s2(0, 1) == 9); + REQUIRE(s2(1, 0) == 20); +} + +// ============================================================================ +// View assignment and arithmetic +// ============================================================================ + +TEST_CASE("View scalar assignment (fill)") +{ + Tensor t({2, 3}, 0.0); + auto r = t.row(0); + r = 7.0; + REQUIRE(t(0, 0) == 7.0); + REQUIRE(t(0, 1) == 7.0); + REQUIRE(t(0, 2) == 7.0); + REQUIRE(t(1, 0) == 0.0); // Other row unchanged +} + +TEST_CASE("View initializer_list assignment") +{ + Tensor t({2, 3}, 0.0); + auto r = t.row(1); + r = {10.0, 20.0, 30.0}; + REQUIRE(t(1, 0) == 10.0); + REQUIRE(t(1, 1) == 20.0); + REQUIRE(t(1, 2) == 30.0); +} + +TEST_CASE("View copy assignment (deep copy)") +{ + Tensor t({2, 3}, 0.0); + t.row(0) = {1.0, 2.0, 3.0}; + t.row(1) = {4.0, 5.0, 6.0}; + + // Copy row 0 into row 1 + t.row(1) = t.row(0); + REQUIRE(t(1, 0) == 1.0); + REQUIRE(t(1, 1) == 2.0); + REQUIRE(t(1, 2) == 3.0); +} + +TEST_CASE("View compound operators") +{ + Tensor t({2, 3}, 0.0); + t.row(0) = {1.0, 2.0, 3.0}; + + t.row(0) *= 2.0; + REQUIRE(t(0, 0) == 2.0); + REQUIRE(t(0, 1) == 4.0); + + t.row(0) /= 2.0; + REQUIRE(t(0, 0) == 1.0); + REQUIRE(t(0, 1) == 2.0); +} + +TEST_CASE("View assignment from tensor") +{ + Tensor t({2, 3}, 0.0); + Tensor vals({3}, 0.0); + vals = {7.0, 8.0, 9.0}; + + t.row(1) = vals; + REQUIRE(t(1, 0) == 7.0); + REQUIRE(t(1, 1) == 8.0); + REQUIRE(t(1, 2) == 9.0); +} + +TEST_CASE("View compound addition from tensor") +{ + Tensor t({2, 3}, 0.0); + t.row(0) = {1.0, 2.0, 3.0}; + Tensor vals({3}, 0.0); + vals = {10.0, 20.0, 30.0}; + + t.row(0) += vals; + REQUIRE(t(0, 0) == 11.0); + REQUIRE(t(0, 1) == 22.0); + REQUIRE(t(0, 2) == 33.0); +} + +TEST_CASE("View sum") +{ + Tensor t({2, 3}, 0.0); + t.row(0) = {1.0, 2.0, 3.0}; + t.row(1) = {4.0, 5.0, 6.0}; + + REQUIRE(t.row(0).sum() == 6.0); + REQUIRE(t.row(1).sum() == 15.0); +} + +TEST_CASE("View iteration") +{ + Tensor t({2, 3}, 0); + t.row(0) = {1, 2, 3}; + + int sum = 0; + for (auto val : t.row(0)) + sum += val; + REQUIRE(sum == 6); +} + +TEST_CASE("View sub-slice") +{ + Tensor t({6}, 0); + t = {10, 20, 30, 40, 50, 60}; + + auto s = t.slice(1, 5); // [20, 30, 40, 50] + auto ss = s.slice(1, 3); // [30, 40] + REQUIRE(ss.size() == 2); + REQUIRE(ss[0] == 30); + REQUIRE(ss[1] == 40); +} + +TEST_CASE("Tensor from View") +{ + Tensor t({2, 3}, 0.0); + t.row(0) = {1.0, 2.0, 3.0}; + + // Construct a new tensor from a view (copies data) + Tensor t2(t.row(0)); + REQUIRE(t2.size() == 3); + REQUIRE(t2[0] == 1.0); + REQUIRE(t2[2] == 3.0); + + // Modifying the new tensor doesn't affect the original + t2[0] = 99.0; + REQUIRE(t(0, 0) == 1.0); +} + +// ============================================================================ +// Const View +// ============================================================================ + +TEST_CASE("Const tensor produces const views") +{ + Tensor t({2, 3}, 0.0); + int v = 1; + for (size_t i = 0; i < 2; ++i) + for (size_t j = 0; j < 3; ++j) + t(i, j) = v++; + + const Tensor& ct = t; + auto r = ct.row(0); // View + REQUIRE(r[0] == 1.0); + REQUIRE(r[2] == 3.0); + + auto c = ct.col(1); + REQUIRE(c[0] == 2.0); + REQUIRE(c[1] == 5.0); +} + +// ============================================================================ +// StaticTensor2D +// ============================================================================ + +TEST_CASE("StaticTensor2D basics") +{ + StaticTensor2D t; + REQUIRE(t.size() == 12); + REQUIRE(t.shape()[0] == 3); + REQUIRE(t.shape()[1] == 4); + + // Default-initialized to zero + REQUIRE(t(0, 0) == 0.0); + + t(1, 2) = 42.0; + REQUIRE(t(1, 2) == 42.0); + // Flat data: row 1, col 2 = index 1*4 + 2 = 6 + REQUIRE(t.data()[6] == 42.0); +} + +TEST_CASE("StaticTensor2D fill") +{ + StaticTensor2D t; + t.fill(5); + for (size_t i = 0; i < t.size(); ++i) + REQUIRE(t.data()[i] == 5); +} + +TEST_CASE("StaticTensor2D iteration") +{ + StaticTensor2D t; + t.fill(1); + int sum = 0; + for (auto val : t) + sum += val; + REQUIRE(sum == 6); +} + +TEST_CASE("StaticTensor2D col view") +{ + StaticTensor2D t; + t(0, 0) = 1; t(0, 1) = 2; + t(1, 0) = 3; t(1, 1) = 4; + t(2, 0) = 5; t(2, 1) = 6; + + auto c = t.col(0); + REQUIRE(c.size() == 3); + REQUIRE(c[0] == 1); + REQUIRE(c[1] == 3); + REQUIRE(c[2] == 5); +} + +TEST_CASE("StaticTensor2D flat view") +{ + StaticTensor2D t; + t(0, 0) = 1.0; t(0, 1) = 2.0; + t(1, 0) = 3.0; t(1, 1) = 4.0; + + auto f = t.flat(); + REQUIRE(f.size() == 4); + f = 0.0; + REQUIRE(t(0, 0) == 0.0); + REQUIRE(t(1, 1) == 0.0); +} + +// ============================================================================ +// Non-member functions +// ============================================================================ + +TEST_CASE("zeros") +{ + auto t = zeros({3, 4}); + REQUIRE(t.size() == 12); + for (size_t i = 0; i < t.size(); ++i) + REQUIRE(t[i] == 0.0); +} + +TEST_CASE("zeros_like") +{ + Tensor a({2, 5}, 7.0); + auto b = zeros_like(a); + REQUIRE(b.size() == 10); + REQUIRE(b.shape(0) == 2); + REQUIRE(b.shape(1) == 5); + for (size_t i = 0; i < b.size(); ++i) + REQUIRE(b[i] == 0.0); +} + +TEST_CASE("full_like") +{ + Tensor a({4}, 0); + auto b = full_like(a, 42); + REQUIRE(b.size() == 4); + for (size_t i = 0; i < b.size(); ++i) + REQUIRE(b[i] == 42); +} + +TEST_CASE("linspace") +{ + auto t = linspace(0.0, 1.0, 5); + REQUIRE(t.size() == 5); + REQUIRE(t[0] == 0.0); + REQUIRE(t[4] == 1.0); + REQUIRE_THAT(t[1], Catch::Matchers::WithinRel(0.25, 1e-12)); + REQUIRE_THAT(t[2], Catch::Matchers::WithinRel(0.5, 1e-12)); + REQUIRE_THAT(t[3], Catch::Matchers::WithinRel(0.75, 1e-12)); +} + +TEST_CASE("concatenate") +{ + Tensor a({3}, 0); + Tensor b({2}, 0); + a = {1, 2, 3}; + b = {4, 5}; + + auto c = concatenate(a, b); + REQUIRE(c.size() == 5); + REQUIRE(c[0] == 1); + REQUIRE(c[2] == 3); + REQUIRE(c[3] == 4); + REQUIRE(c[4] == 5); +} + +TEST_CASE("log") +{ + Tensor t({3}, 0.0); + t = {1.0, std::exp(1.0), std::exp(2.0)}; + + auto r = log(t); + REQUIRE_THAT(r[0], Catch::Matchers::WithinAbs(0.0, 1e-12)); + REQUIRE_THAT(r[1], Catch::Matchers::WithinAbs(1.0, 1e-12)); + REQUIRE_THAT(r[2], Catch::Matchers::WithinAbs(2.0, 1e-12)); +} + +TEST_CASE("abs") +{ + Tensor t({4}, 0.0); + t = {-3.0, -1.0, 0.0, 2.0}; + + auto r = abs(t); + REQUIRE(r[0] == 3.0); + REQUIRE(r[1] == 1.0); + REQUIRE(r[2] == 0.0); + REQUIRE(r[3] == 2.0); +} + +TEST_CASE("where") +{ + Tensor cond({4}, false); + cond.data()[0] = true; + cond.data()[2] = true; + + Tensor vals({4}, 0.0); + vals = {10.0, 20.0, 30.0, 40.0}; + + auto r = where(cond, vals, -1.0); + REQUIRE(r[0] == 10.0); + REQUIRE(r[1] == -1.0); + REQUIRE(r[2] == 30.0); + REQUIRE(r[3] == -1.0); +} + +TEST_CASE("nan_to_num") +{ + Tensor t({4}, 0.0); + t[0] = 1.0; + t[1] = std::nan(""); + t[2] = std::numeric_limits::infinity(); + t[3] = -std::numeric_limits::infinity(); + + auto r = nan_to_num(t); + REQUIRE(r[0] == 1.0); + REQUIRE(r[1] == 0.0); // NaN -> 0 + REQUIRE(r[2] == std::numeric_limits::max()); // +inf -> max + REQUIRE(r[3] == std::numeric_limits::lowest()); // -inf -> lowest +} + +// ============================================================================ +// is_tensor trait +// ============================================================================ + +TEST_CASE("is_tensor trait") +{ + REQUIRE(is_tensor>::value); + REQUIRE(is_tensor>::value); + REQUIRE(is_tensor>::value); + REQUIRE(!is_tensor::value); + REQUIRE(!is_tensor>::value); +} From c1d5b9d59de913f9da7f45f9a05a0a92b2089aa1 Mon Sep 17 00:00:00 2001 From: John Tramm Date: Thu, 12 Feb 2026 09:55:06 -0600 Subject: [PATCH 36/51] cleanup and code review incorporation --- include/openmc/tensor.h | 47 +++++++++++++++++++---------------------- src/xsdata.cpp | 8 +++---- 2 files changed, 26 insertions(+), 29 deletions(-) diff --git a/include/openmc/tensor.h b/include/openmc/tensor.h index 24451fdc023..7599b83130a 100644 --- a/include/openmc/tensor.h +++ b/include/openmc/tensor.h @@ -1,8 +1,13 @@ //! \file tensor.h //! \brief Multi-dimensional tensor types for OpenMC. //! -//! Provides Tensor (dynamic-rank owning), StaticTensor2D -//! (stack-allocated), and View (non-owning N-dimensional view). +//! Tensor is the primary type: a dynamic-rank owning container that stores +//! elements contiguously in row-major order. View is a lightweight +//! non-owning reference into a Tensor's storage, returned by methods like +//! row(), col(), slice(), and flat(). StaticTensor2D is a small +//! stack-allocated 2D array used only for simulation::global_tallies. +//! +//! View is declared before Tensor because Tensor's methods return View objects. #ifndef OPENMC_TENSOR_H #define OPENMC_TENSOR_H @@ -72,20 +77,6 @@ class View { View(const View&) = default; View(View&&) = default; - //-------------------------------------------------------------------------- - // Assignment operators - - //! Copy assignment: element-wise deep copy (writes through data pointer). - //! Without this, the compiler's implicit copy assignment just copies the - //! View metadata (pointer, shape, strides) instead of the viewed data. - View& operator=(const View& other) - { - size_t n = size(); - for (size_t i = 0; i < n; ++i) - data_[flat_to_offset(i)] = other[i]; - return *this; - } - //-------------------------------------------------------------------------- // Indexing @@ -190,6 +181,20 @@ class View { return {data_ + start * strides_[0], {shape_[0] - start}, {strides_[0]}}; } + //-------------------------------------------------------------------------- + // Assignment operators + + //! Copy assignment: element-wise deep copy (writes through data pointer). + //! Without this, the compiler's implicit copy assignment just copies the + //! View metadata (pointer, shape, strides) instead of the viewed data. + View& operator=(const View& other) + { + size_t n = size(); + for (size_t i = 0; i < n; ++i) + data_[flat_to_offset(i)] = other[i]; + return *this; + } + //! Fill all elements with a scalar View& operator=(T val) { @@ -458,7 +463,7 @@ class Tensor { //! Copy from View (preserves view's shape) template - Tensor(const View& v) + explicit Tensor(const View& v) : shape_(v.shape_vec()) { size_t n = v.size(); @@ -554,14 +559,6 @@ class Tensor { data_.resize(compute_size()); } - void resize(const vector& shape) - { - shape_.clear(); - for (auto d : shape) - shape_.push_back(static_cast(d)); - data_.resize(compute_size()); - } - void reshape(const vector& new_shape) { shape_ = new_shape; diff --git a/src/xsdata.cpp b/src/xsdata.cpp index 45ece8f8ba8..1cae2166c50 100644 --- a/src/xsdata.cpp +++ b/src/xsdata.cpp @@ -553,8 +553,8 @@ void XsData::scatter_from_hdf5(hid_t xsdata_grp, size_t n_ang, final_scatter_format == AngleDistributionType::TABULAR) { for (size_t a = 0; a < n_ang; a++) { ScattDataLegendre legendre_scatt; - tensor::Tensor in_gmin = gmin.row(a); - tensor::Tensor in_gmax = gmax.row(a); + tensor::Tensor in_gmin(gmin.row(a)); + tensor::Tensor in_gmax(gmax.row(a)); legendre_scatt.init(in_gmin, in_gmax, temp_mult[a], input_scatt[a]); @@ -568,8 +568,8 @@ void XsData::scatter_from_hdf5(hid_t xsdata_grp, size_t n_ang, // We are sticking with the current representation // Initialize the ScattData object with this data for (size_t a = 0; a < n_ang; a++) { - tensor::Tensor in_gmin = gmin.row(a); - tensor::Tensor in_gmax = gmax.row(a); + tensor::Tensor in_gmin(gmin.row(a)); + tensor::Tensor in_gmax(gmax.row(a)); scatter[a]->init(in_gmin, in_gmax, temp_mult[a], input_scatt[a]); } } From 255ea57ae4df5da3ddd8658d2b52f6683da95267 Mon Sep 17 00:00:00 2001 From: John Tramm Date: Thu, 12 Feb 2026 11:54:27 -0600 Subject: [PATCH 37/51] removed 2D-wrappers row and col. --- include/openmc/tensor.h | 22 +-------- src/distribution_angle.cpp | 6 +-- src/distribution_energy.cpp | 10 ++-- src/endf.cpp | 8 ++-- src/material.cpp | 2 +- src/mesh.cpp | 14 +++--- src/nuclide.cpp | 8 ++-- src/photon.cpp | 12 ++--- src/physics.cpp | 4 +- src/secondary_correlated.cpp | 16 +++---- src/secondary_kalbach.cpp | 14 +++--- src/secondary_thermal.cpp | 2 +- src/tallies/tally.cpp | 6 +-- src/xsdata.cpp | 34 +++++++------- tests/cpp_unit_tests/test_tensor.cpp | 68 +++++++++++----------------- 15 files changed, 96 insertions(+), 130 deletions(-) diff --git a/include/openmc/tensor.h b/include/openmc/tensor.h index 7599b83130a..261b87b5598 100644 --- a/include/openmc/tensor.h +++ b/include/openmc/tensor.h @@ -4,7 +4,7 @@ //! Tensor is the primary type: a dynamic-rank owning container that stores //! elements contiguously in row-major order. View is a lightweight //! non-owning reference into a Tensor's storage, returned by methods like -//! row(), col(), slice(), and flat(). StaticTensor2D is a small +//! select(), slice(), and flat(). StaticTensor2D is a small //! stack-allocated 2D array used only for simulation::global_tallies. //! //! View is declared before Tensor because Tensor's methods return View objects. @@ -157,14 +157,6 @@ class View { return {new_data, std::move(new_shape), std::move(new_strides)}; } - //! Row i (fix first axis) — shorthand for select(0, i) - View row(size_t i) { return select(0, i); } - View row(size_t i) const { return select(0, i); } - - //! Column j (fix second axis) — shorthand for select(1, j) - View col(size_t j) { return select(1, j); } - View col(size_t j) const { return select(1, j); } - //! 1D subrange [start, end) View slice(size_t start, size_t end) { @@ -604,14 +596,6 @@ class Tensor { return {new_data, std::move(new_shape), std::move(new_strides)}; } - //! Row i of a 2D+ tensor (fix first axis) - View row(size_t i) { return select(0, i); } - View row(size_t i) const { return select(0, i); } - - //! Column j of a 2D tensor (fix second axis) - View col(size_t j) { return select(1, j); } - View col(size_t j) const { return select(1, j); } - //! Subrange of a 1D tensor View slice(size_t start, size_t end) { @@ -997,10 +981,6 @@ class StaticTensor2D { //-------------------------------------------------------------------------- // View accessors - //! Column view (1D, strided) - View col(size_t j) { return {data_ + j, {R}, {C}}; } - View col(size_t j) const { return {data_ + j, {R}, {C}}; } - //! Flat view (1D, contiguous) View flat() { return {data_, {R * C}, {size_t(1)}}; } View flat() const { return {data_, {R * C}, {size_t(1)}}; } diff --git a/src/distribution_angle.cpp b/src/distribution_angle.cpp index f183e3aef16..cb64cee4df0 100644 --- a/src/distribution_angle.cpp +++ b/src/distribution_angle.cpp @@ -44,9 +44,9 @@ AngleDistribution::AngleDistribution(hid_t group) } // Create and initialize tabular distribution - tensor::View xs = temp.row(0).slice(j, j + n); - tensor::View ps = temp.row(1).slice(j, j + n); - tensor::View cs = temp.row(2).slice(j, j + n); + tensor::View xs = temp.select(0,0).slice(j, j + n); + tensor::View ps = temp.select(0,1).slice(j, j + n); + tensor::View cs = temp.select(0,2).slice(j, j + n); vector x {xs.begin(), xs.end()}; vector p {ps.begin(), ps.end()}; vector c {cs.begin(), cs.end()}; diff --git a/src/distribution_energy.cpp b/src/distribution_energy.cpp index c57a9b6b0af..ab71857417b 100644 --- a/src/distribution_energy.cpp +++ b/src/distribution_energy.cpp @@ -63,8 +63,8 @@ ContinuousTabular::ContinuousTabular(hid_t group) tensor::Tensor temp; read_attribute(dset, "interpolation", temp); - tensor::View temp_b = temp.row(0); // breakpoints - tensor::View temp_i = temp.row(1); // interpolation parameters + tensor::View temp_b = temp.select(0,0); // breakpoints + tensor::View temp_i = temp.select(0,1); // interpolation parameters std::copy(temp_b.begin(), temp_b.end(), std::back_inserter(breakpoints_)); for (const auto i : temp_i) @@ -105,15 +105,15 @@ ContinuousTabular::ContinuousTabular(hid_t group) d.n_discrete = n_discrete[i]; // Copy data - d.e_out = eout.row(0).slice(j, j + n); - d.p = eout.row(1).slice(j, j + n); + d.e_out = eout.select(0,0).slice(j, j + n); + d.p = eout.select(0,1).slice(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 = eout.row(2).slice(j, j + n); + d.c = eout.select(0,2).slice(j, j + n); } else { // Calculate cumulative distribution function -- discrete portion for (int k = 0; k < d.n_discrete; ++k) { diff --git a/src/endf.cpp b/src/endf.cpp index 87f32972a67..b8ac9e3d201 100644 --- a/src/endf.cpp +++ b/src/endf.cpp @@ -155,8 +155,8 @@ Tabulated1D::Tabulated1D(hid_t dset) tensor::Tensor arr; read_dataset(dset, arr); - tensor::View xs = arr.row(0); - tensor::View ys = arr.row(1); + tensor::View xs = arr.select(0,0); + tensor::View ys = arr.select(0,1); std::copy(xs.begin(), xs.end(), std::back_inserter(x_)); std::copy(ys.begin(), ys.end(), std::back_inserter(y_)); @@ -232,8 +232,8 @@ CoherentElasticXS::CoherentElasticXS(hid_t dset) read_dataset(dset, arr); // Get views for Bragg edges and structure factors - tensor::View E = arr.row(0); - tensor::View s = arr.row(1); + tensor::View E = arr.select(0,0); + tensor::View s = arr.select(0,1); // Copy Bragg edges and partial sums of structure factors std::copy(E.begin(), E.end(), std::back_inserter(bragg_edges_)); diff --git a/src/material.cpp b/src/material.cpp index b29c597f651..c168c3bbc26 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -696,7 +696,7 @@ void Material::init_bremsstrahlung() 1.0595e-3 * std::pow(t, 5) + 7.0568e-5 * std::pow(t, 6) - 1.808e-6 * std::pow(t, 7)); stopping_power_radiative(i) *= r; - tensor::View dcs_i = dcs.row(i); + tensor::View dcs_i = dcs.select(0, i); dcs_i *= r; } } diff --git a/src/mesh.cpp b/src/mesh.cpp index 5bb59d19e54..0534af75280 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -957,7 +957,7 @@ void UnstructuredMesh::to_hdf5_inner(hid_t mesh_group) const tensor::Tensor vertices({static_cast(this->n_vertices()), static_cast(3)}); for (int i = 0; i < this->n_vertices(); i++) { auto v = this->vertex(i); - vertices.row(i) = {v.x, v.y, v.z}; + vertices.select(0, i) = {v.x, v.y, v.z}; } write_dataset(mesh_group, "vertices", vertices); @@ -974,18 +974,18 @@ void UnstructuredMesh::to_hdf5_inner(hid_t mesh_group) const // write linear tet element if (conn.size() == 4) { - elem_types.row(i) = static_cast(ElementType::LINEAR_TET); - connectivity.row(i) = + elem_types.select(0, i) = static_cast(ElementType::LINEAR_TET); + connectivity.select(0, i) = {conn[0], conn[1], conn[2], conn[3], -1, -1, -1, -1}; // write linear hex element } else if (conn.size() == 8) { - elem_types.row(i) = static_cast(ElementType::LINEAR_HEX); - connectivity.row(i) = {conn[0], conn[1], + elem_types.select(0, i) = static_cast(ElementType::LINEAR_HEX); + connectivity.select(0, i) = {conn[0], conn[1], conn[2], conn[3], conn[4], conn[5], conn[6], conn[7]}; } else { num_elem_skipped++; - elem_types.row(i) = static_cast(ElementType::UNSUPPORTED); - connectivity.row(i) = -1; + elem_types.select(0, i) = static_cast(ElementType::UNSUPPORTED); + connectivity.select(0, i) = -1; } } diff --git a/src/nuclide.cpp b/src/nuclide.cpp index 2fcbb97b907..40e280b7674 100644 --- a/src/nuclide.cpp +++ b/src/nuclide.cpp @@ -403,17 +403,17 @@ void Nuclide::create_derived( continue; // Add contribution to total cross section - xs_[t].col(XS_TOTAL).slice(j, j + n) += xs; + xs_[t].select(1, XS_TOTAL).slice(j, j + n) += xs; // Add contribution to absorption cross section if (is_disappearance(rx->mt_)) { - xs_[t].col(XS_ABSORPTION).slice(j, j + n) += xs; + xs_[t].select(1, XS_ABSORPTION).slice(j, j + n) += xs; } if (is_fission(rx->mt_)) { fissionable_ = true; - xs_[t].col(XS_FISSION).slice(j, j + n) += xs; - xs_[t].col(XS_ABSORPTION).slice(j, j + n) += xs; + xs_[t].select(1, XS_FISSION).slice(j, j + n) += xs; + xs_[t].select(1, XS_ABSORPTION).slice(j, j + n) += xs; // Keep track of fission reactions if (t == 0) { diff --git a/src/photon.cpp b/src/photon.cpp index e75624a9bef..22a3ac50960 100644 --- a/src/photon.cpp +++ b/src/photon.cpp @@ -169,7 +169,7 @@ PhotonInteraction::PhotonInteraction(hid_t group) read_dataset(tgroup, "xs", xs); auto cross_section = - cross_sections_.col(i).slice(static_cast(shell.threshold)); + cross_sections_.select(1,i).slice(static_cast(shell.threshold)); cross_section = tensor::where(xs > 0, tensor::log(xs), 0); if (object_exists(tgroup, "transitions")) { @@ -184,7 +184,7 @@ PhotonInteraction::PhotonInteraction(hid_t group) read_dataset(tgroup, "transitions", matrix); // Transition probability normalization - double norm = tensor::Tensor(matrix.col(3)).sum(); + double norm = tensor::Tensor(matrix.select(1,3)).sum(); shell.transitions.resize(n_transition); for (int j = 0; j < n_transition; ++j) { @@ -304,7 +304,7 @@ PhotonInteraction::PhotonInteraction(hid_t group) double y = std::exp( std::log(dcs_(i_grid, i)) + f * (std::log(dcs_(i_grid + 1, i)) - std::log(dcs_(i_grid, i)))); - tensor::View col_i = dcs.col(i); + tensor::View col_i = dcs.select(1,i); col_i(0) = y; for (int j = i_grid + 1; j < n_e; ++j) { col_i(j - i_grid) = dcs_(j, i); @@ -507,7 +507,7 @@ void PhotonInteraction::compton_doppler( c = prn(seed) * c_max; // Determine pz corresponding to sampled cdf value - tensor::View cdf_shell = profile_cdf_.row(shell); + tensor::View cdf_shell = profile_cdf_.select(0,shell); int i = lower_bound_index(cdf_shell.cbegin(), cdf_shell.cend(), c); double pz_l = data::compton_profile_pz(i); double pz_r = data::compton_profile_pz(i + 1); @@ -603,8 +603,8 @@ void PhotonInteraction::calculate_xs(Particle& p) const // Calculate microscopic photoelectric cross section xs.photoelectric = 0.0; - tensor::View xs_lower = cross_sections_.row(i_grid); - tensor::View xs_upper = cross_sections_.row(i_grid + 1); + tensor::View xs_lower = cross_sections_.select(0,i_grid); + tensor::View xs_upper = cross_sections_.select(0,i_grid + 1); for (int i = 0; i < xs_upper.size(); ++i) if (xs_lower(i) != 0) diff --git a/src/physics.cpp b/src/physics.cpp index 3c25724396c..d25c19f90c7 100644 --- a/src/physics.cpp +++ b/src/physics.cpp @@ -375,8 +375,8 @@ void sample_photon_reaction(Particle& p) // cross sections int i_grid = micro.index_grid; double f = micro.interp_factor; - tensor::View xs_lower = element.cross_sections_.row(i_grid); - tensor::View xs_upper = element.cross_sections_.row(i_grid + 1); + tensor::View xs_lower = element.cross_sections_.select(0,i_grid); + tensor::View xs_upper = element.cross_sections_.select(0,i_grid + 1); for (int i_shell = 0; i_shell < element.shells_.size(); ++i_shell) { const auto& shell {element.shells_[i_shell]}; diff --git a/src/secondary_correlated.cpp b/src/secondary_correlated.cpp index 089e2611252..8f074ae4ba0 100644 --- a/src/secondary_correlated.cpp +++ b/src/secondary_correlated.cpp @@ -28,8 +28,8 @@ CorrelatedAngleEnergy::CorrelatedAngleEnergy(hid_t group) tensor::Tensor temp; read_attribute(dset, "interpolation", temp); - tensor::View temp_b = temp.row(0); // breakpoints - tensor::View temp_i = temp.row(1); // interpolation parameters + tensor::View temp_b = temp.select(0,0); // breakpoints + tensor::View temp_i = temp.select(0,1); // interpolation parameters std::copy(temp_b.begin(), temp_b.end(), std::back_inserter(breakpoints_)); for (const auto i : temp_i) @@ -74,9 +74,9 @@ CorrelatedAngleEnergy::CorrelatedAngleEnergy(hid_t group) d.n_discrete = n_discrete[i]; // Copy data - d.e_out = eout.row(0).slice(j, j + n); - d.p = eout.row(1).slice(j, j + n); - d.c = eout.row(2).slice(j, j + n); + d.e_out = eout.select(0,0).slice(j, j + n); + d.p = eout.select(0,1).slice(j, j + n); + d.c = eout.select(0,2).slice(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 @@ -132,9 +132,9 @@ CorrelatedAngleEnergy::CorrelatedAngleEnergy(hid_t group) interp_mu = 1; auto interp = int2interp(interp_mu); - tensor::View xs = mu.row(0).slice(offset_mu, offset_mu + m); - tensor::View ps = mu.row(1).slice(offset_mu, offset_mu + m); - tensor::View cs = mu.row(2).slice(offset_mu, offset_mu + m); + tensor::View xs = mu.select(0,0).slice(offset_mu, offset_mu + m); + tensor::View ps = mu.select(0,1).slice(offset_mu, offset_mu + m); + tensor::View cs = mu.select(0,2).slice(offset_mu, offset_mu + m); vector x {xs.begin(), xs.end()}; vector p {ps.begin(), ps.end()}; diff --git a/src/secondary_kalbach.cpp b/src/secondary_kalbach.cpp index b2dd784f974..2c82bc09177 100644 --- a/src/secondary_kalbach.cpp +++ b/src/secondary_kalbach.cpp @@ -29,8 +29,8 @@ KalbachMann::KalbachMann(hid_t group) tensor::Tensor temp; read_attribute(dset, "interpolation", temp); - tensor::View temp_b = temp.row(0); // breakpoints - tensor::View temp_i = temp.row(1); // interpolation parameters + tensor::View temp_b = temp.select(0,0); // breakpoints + tensor::View temp_i = temp.select(0,1); // interpolation parameters std::copy(temp_b.begin(), temp_b.end(), std::back_inserter(breakpoints_)); for (const auto i : temp_i) @@ -71,11 +71,11 @@ KalbachMann::KalbachMann(hid_t group) d.n_discrete = n_discrete[i]; // Copy data - d.e_out = eout.row(0).slice(j, j + n); - d.p = eout.row(1).slice(j, j + n); - d.c = eout.row(2).slice(j, j + n); - d.r = eout.row(3).slice(j, j + n); - d.a = eout.row(4).slice(j, j + n); + d.e_out = eout.select(0,0).slice(j, j + n); + d.p = eout.select(0,1).slice(j, j + n); + d.c = eout.select(0,2).slice(j, j + n); + d.r = eout.select(0,3).slice(j, j + n); + d.a = eout.select(0,4).slice(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 diff --git a/src/secondary_thermal.cpp b/src/secondary_thermal.cpp index e0ba90dabf3..6a499e538ec 100644 --- a/src/secondary_thermal.cpp +++ b/src/secondary_thermal.cpp @@ -222,7 +222,7 @@ IncoherentInelasticAE::IncoherentInelasticAE(hid_t group) } // Copy outgoing angles - tensor::View mu_j = d.mu.row(j); + tensor::View mu_j = d.mu.select(0,j); std::copy(adist->x().begin(), adist->x().end(), mu_j.begin()); } } diff --git a/src/tallies/tally.cpp b/src/tallies/tally.cpp index b51d5ebdf2d..e33e0095bb7 100644 --- a/src/tallies/tally.cpp +++ b/src/tallies/tally.cpp @@ -1031,7 +1031,7 @@ void reduce_tally_results() const int val_col = static_cast(TallyResult::VALUE); // Copy VALUE column into contiguous array for MPI reduction - tensor::Tensor gt_values(gt.col(val_col)); + tensor::Tensor gt_values(gt.select(1,val_col)); tensor::Tensor gt_values_reduced({size_t{N_GLOBAL_TALLIES}}); // Reduce contiguous data @@ -1040,9 +1040,9 @@ void reduce_tally_results() // Transfer values on master and reset on other ranks if (mpi::master) { - gt.col(val_col) = gt_values_reduced; + gt.select(1,val_col) = gt_values_reduced; } else { - gt.col(val_col) = 0.0; + gt.select(1,val_col) = 0.0; } // We also need to determine the total starting weight of particles from the diff --git a/src/xsdata.cpp b/src/xsdata.cpp index 1cae2166c50..0a11afe7d6a 100644 --- a/src/xsdata.cpp +++ b/src/xsdata.cpp @@ -132,7 +132,7 @@ void XsData::fission_vector_beta_from_hdf5( // Normalize chi so it sums to 1 over outgoing groups for each angle for (size_t a = 0; a < n_ang; a++) { - tensor::View row = temp_chi.row(a); + tensor::View row = temp_chi.select(0, a); row /= row.sum(); } @@ -140,13 +140,13 @@ void XsData::fission_vector_beta_from_hdf5( // spectrum is independent of the incoming neutron energy for (size_t a = 0; a < n_ang; a++) for (size_t gin = 0; gin < n_g_; gin++) - chi_prompt.select(0, a).row(gin) = temp_chi.row(a); + chi_prompt.select(0, a).select(0, gin) = temp_chi.select(0, a); // Same spectrum for delayed neutrons, replicated across delayed groups for (size_t a = 0; a < n_ang; a++) for (size_t d = 0; d < n_dg_; d++) for (size_t gin = 0; gin < n_g_; gin++) - chi_delayed.select(0, a).select(0, d).row(gin) = temp_chi.row(a); + chi_delayed.select(0, a).select(0, d).select(0, gin) = temp_chi.select(0, a); // Get nu-fission tensor::Tensor temp_nufiss({n_ang, n_g_}, 0.); @@ -205,7 +205,7 @@ void XsData::fission_vector_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) // Normalize prompt chi so it sums to 1 over outgoing groups for each angle for (size_t a = 0; a < n_ang; a++) { - tensor::View row = temp_chi_p.row(a); + tensor::View row = temp_chi_p.select(0, a); row /= row.sum(); } @@ -217,20 +217,20 @@ void XsData::fission_vector_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) // angle and delayed group for (size_t a = 0; a < n_ang; a++) for (size_t d = 0; d < n_dg_; d++) { - tensor::View row = temp_chi_d.select(0, a).row(d); + tensor::View row = temp_chi_d.select(0, a).select(0, d); row /= row.sum(); } // Replicate the prompt spectrum across all incoming groups for (size_t a = 0; a < n_ang; a++) for (size_t gin = 0; gin < n_g_; gin++) - chi_prompt.select(0, a).row(gin) = temp_chi_p.row(a); + chi_prompt.select(0, a).select(0, gin) = temp_chi_p.select(0, a); // Replicate the delayed spectrum across all incoming groups for (size_t a = 0; a < n_ang; a++) for (size_t d = 0; d < n_dg_; d++) for (size_t gin = 0; gin < n_g_; gin++) - chi_delayed.select(0, a).select(0, d).row(gin) = temp_chi_d.select(0, a).row(d); + chi_delayed.select(0, a).select(0, d).select(0, gin) = temp_chi_d.select(0, a).select(0, d); // Get prompt and delayed nu-fission directly read_nd_tensor(xsdata_grp, "prompt-nu-fission", prompt_nu_fission, true); @@ -248,14 +248,14 @@ void XsData::fission_vector_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang) // Normalize chi so it sums to 1 over outgoing groups for each angle for (size_t a = 0; a < n_ang; a++) { - tensor::View row = temp_chi.row(a); + tensor::View row = temp_chi.select(0, a); row /= row.sum(); } // Replicate the energy spectrum across all incoming groups for (size_t a = 0; a < n_ang; a++) for (size_t gin = 0; gin < n_g_; gin++) - chi_prompt.select(0, a).row(gin) = temp_chi.row(a); + chi_prompt.select(0, a).select(0, gin) = temp_chi.select(0, a); // Get nu-fission directly read_nd_tensor(xsdata_grp, "nu-fission", prompt_nu_fission, true); @@ -355,7 +355,7 @@ void XsData::fission_matrix_beta_from_hdf5( // Normalize chi_prompt so it sums to 1 over outgoing groups for (size_t a = 0; a < n_ang; a++) for (size_t gin = 0; gin < n_g_; gin++) { - tensor::View row = chi_prompt.select(0, a).row(gin); + tensor::View row = chi_prompt.select(0, a).select(0, gin); row /= row.sum(); } @@ -363,7 +363,7 @@ void XsData::fission_matrix_beta_from_hdf5( for (size_t a = 0; a < n_ang; a++) for (size_t d = 0; d < n_dg_; d++) for (size_t gin = 0; gin < n_g_; gin++) { - tensor::View row = chi_delayed.select(0, a).select(0, d).row(gin); + tensor::View row = chi_delayed.select(0, a).select(0, d).select(0, gin); row /= row.sum(); } } @@ -553,8 +553,8 @@ void XsData::scatter_from_hdf5(hid_t xsdata_grp, size_t n_ang, final_scatter_format == AngleDistributionType::TABULAR) { for (size_t a = 0; a < n_ang; a++) { ScattDataLegendre legendre_scatt; - tensor::Tensor in_gmin(gmin.row(a)); - tensor::Tensor in_gmax(gmax.row(a)); + tensor::Tensor in_gmin(gmin.select(0, a)); + tensor::Tensor in_gmax(gmax.select(0, a)); legendre_scatt.init(in_gmin, in_gmax, temp_mult[a], input_scatt[a]); @@ -568,8 +568,8 @@ void XsData::scatter_from_hdf5(hid_t xsdata_grp, size_t n_ang, // We are sticking with the current representation // Initialize the ScattData object with this data for (size_t a = 0; a < n_ang; a++) { - tensor::Tensor in_gmin(gmin.row(a)); - tensor::Tensor in_gmax(gmax.row(a)); + tensor::Tensor in_gmin(gmin.select(0, a)); + tensor::Tensor in_gmax(gmax.select(0, a)); scatter[a]->init(in_gmin, in_gmax, temp_mult[a], input_scatt[a]); } } @@ -633,7 +633,7 @@ void XsData::combine( size_t n_g = chi_prompt.shape(1); for (size_t a = 0; a < n_ang; a++) for (size_t gin = 0; gin < n_g; gin++) { - tensor::View row = chi_prompt.select(0, a).row(gin); + tensor::View row = chi_prompt.select(0, a).select(0, gin); row /= row.sum(); } } @@ -645,7 +645,7 @@ void XsData::combine( for (size_t a = 0; a < n_ang; a++) for (size_t d = 0; d < n_dg; d++) for (size_t gin = 0; gin < n_g; gin++) { - tensor::View row = chi_delayed.select(0, a).select(0, d).row(gin); + tensor::View row = chi_delayed.select(0, a).select(0, d).select(0, gin); row /= row.sum(); } } diff --git a/tests/cpp_unit_tests/test_tensor.cpp b/tests/cpp_unit_tests/test_tensor.cpp index 4de14e7bd34..88a0c3a0c91 100644 --- a/tests/cpp_unit_tests/test_tensor.cpp +++ b/tests/cpp_unit_tests/test_tensor.cpp @@ -472,7 +472,7 @@ TEST_CASE("Tensor storage") // View (via Tensor accessors) // ============================================================================ -TEST_CASE("Tensor row view") +TEST_CASE("Tensor select axis 0 (2D)") { // [[1, 2, 3], [4, 5, 6]] Tensor t({2, 3}, 0); @@ -481,13 +481,13 @@ TEST_CASE("Tensor row view") for (size_t j = 0; j < 3; ++j) t(i, j) = v++; - auto r0 = t.row(0); + auto r0 = t.select(0, 0); REQUIRE(r0.size() == 3); REQUIRE(r0[0] == 1); REQUIRE(r0[1] == 2); REQUIRE(r0[2] == 3); - auto r1 = t.row(1); + auto r1 = t.select(0, 1); REQUIRE(r1[0] == 4); REQUIRE(r1[1] == 5); REQUIRE(r1[2] == 6); @@ -497,7 +497,7 @@ TEST_CASE("Tensor row view") REQUIRE(t(0, 1) == 99); } -TEST_CASE("Tensor col view") +TEST_CASE("Tensor select axis 1 (2D)") { // [[1, 2], [3, 4], [5, 6]] Tensor t({3, 2}, 0); @@ -505,13 +505,13 @@ TEST_CASE("Tensor col view") t(1, 0) = 3; t(1, 1) = 4; t(2, 0) = 5; t(2, 1) = 6; - auto c0 = t.col(0); + auto c0 = t.select(1, 0); REQUIRE(c0.size() == 3); REQUIRE(c0[0] == 1); REQUIRE(c0[1] == 3); REQUIRE(c0[2] == 5); - auto c1 = t.col(1); + auto c1 = t.select(1, 1); REQUIRE(c1[0] == 2); REQUIRE(c1[1] == 4); REQUIRE(c1[2] == 6); @@ -592,7 +592,7 @@ TEST_CASE("Tensor select (general axis)") TEST_CASE("View scalar assignment (fill)") { Tensor t({2, 3}, 0.0); - auto r = t.row(0); + auto r = t.select(0, 0); r = 7.0; REQUIRE(t(0, 0) == 7.0); REQUIRE(t(0, 1) == 7.0); @@ -603,7 +603,7 @@ TEST_CASE("View scalar assignment (fill)") TEST_CASE("View initializer_list assignment") { Tensor t({2, 3}, 0.0); - auto r = t.row(1); + auto r = t.select(0, 1); r = {10.0, 20.0, 30.0}; REQUIRE(t(1, 0) == 10.0); REQUIRE(t(1, 1) == 20.0); @@ -613,11 +613,11 @@ TEST_CASE("View initializer_list assignment") TEST_CASE("View copy assignment (deep copy)") { Tensor t({2, 3}, 0.0); - t.row(0) = {1.0, 2.0, 3.0}; - t.row(1) = {4.0, 5.0, 6.0}; + t.select(0, 0) = {1.0, 2.0, 3.0}; + t.select(0, 1) = {4.0, 5.0, 6.0}; // Copy row 0 into row 1 - t.row(1) = t.row(0); + t.select(0, 1) = t.select(0, 0); REQUIRE(t(1, 0) == 1.0); REQUIRE(t(1, 1) == 2.0); REQUIRE(t(1, 2) == 3.0); @@ -626,13 +626,13 @@ TEST_CASE("View copy assignment (deep copy)") TEST_CASE("View compound operators") { Tensor t({2, 3}, 0.0); - t.row(0) = {1.0, 2.0, 3.0}; + t.select(0, 0) = {1.0, 2.0, 3.0}; - t.row(0) *= 2.0; + t.select(0, 0) *= 2.0; REQUIRE(t(0, 0) == 2.0); REQUIRE(t(0, 1) == 4.0); - t.row(0) /= 2.0; + t.select(0, 0) /= 2.0; REQUIRE(t(0, 0) == 1.0); REQUIRE(t(0, 1) == 2.0); } @@ -643,7 +643,7 @@ TEST_CASE("View assignment from tensor") Tensor vals({3}, 0.0); vals = {7.0, 8.0, 9.0}; - t.row(1) = vals; + t.select(0, 1) = vals; REQUIRE(t(1, 0) == 7.0); REQUIRE(t(1, 1) == 8.0); REQUIRE(t(1, 2) == 9.0); @@ -652,11 +652,11 @@ TEST_CASE("View assignment from tensor") TEST_CASE("View compound addition from tensor") { Tensor t({2, 3}, 0.0); - t.row(0) = {1.0, 2.0, 3.0}; + t.select(0, 0) = {1.0, 2.0, 3.0}; Tensor vals({3}, 0.0); vals = {10.0, 20.0, 30.0}; - t.row(0) += vals; + t.select(0, 0) += vals; REQUIRE(t(0, 0) == 11.0); REQUIRE(t(0, 1) == 22.0); REQUIRE(t(0, 2) == 33.0); @@ -665,20 +665,20 @@ TEST_CASE("View compound addition from tensor") TEST_CASE("View sum") { Tensor t({2, 3}, 0.0); - t.row(0) = {1.0, 2.0, 3.0}; - t.row(1) = {4.0, 5.0, 6.0}; + t.select(0, 0) = {1.0, 2.0, 3.0}; + t.select(0, 1) = {4.0, 5.0, 6.0}; - REQUIRE(t.row(0).sum() == 6.0); - REQUIRE(t.row(1).sum() == 15.0); + REQUIRE(t.select(0, 0).sum() == 6.0); + REQUIRE(t.select(0, 1).sum() == 15.0); } TEST_CASE("View iteration") { Tensor t({2, 3}, 0); - t.row(0) = {1, 2, 3}; + t.select(0, 0) = {1, 2, 3}; int sum = 0; - for (auto val : t.row(0)) + for (auto val : t.select(0, 0)) sum += val; REQUIRE(sum == 6); } @@ -698,10 +698,10 @@ TEST_CASE("View sub-slice") TEST_CASE("Tensor from View") { Tensor t({2, 3}, 0.0); - t.row(0) = {1.0, 2.0, 3.0}; + t.select(0, 0) = {1.0, 2.0, 3.0}; // Construct a new tensor from a view (copies data) - Tensor t2(t.row(0)); + Tensor t2(t.select(0, 0)); REQUIRE(t2.size() == 3); REQUIRE(t2[0] == 1.0); REQUIRE(t2[2] == 3.0); @@ -724,11 +724,11 @@ TEST_CASE("Const tensor produces const views") t(i, j) = v++; const Tensor& ct = t; - auto r = ct.row(0); // View + auto r = ct.select(0, 0); // View REQUIRE(r[0] == 1.0); REQUIRE(r[2] == 3.0); - auto c = ct.col(1); + auto c = ct.select(1, 1); REQUIRE(c[0] == 2.0); REQUIRE(c[1] == 5.0); } @@ -771,20 +771,6 @@ TEST_CASE("StaticTensor2D iteration") REQUIRE(sum == 6); } -TEST_CASE("StaticTensor2D col view") -{ - StaticTensor2D t; - t(0, 0) = 1; t(0, 1) = 2; - t(1, 0) = 3; t(1, 1) = 4; - t(2, 0) = 5; t(2, 1) = 6; - - auto c = t.col(0); - REQUIRE(c.size() == 3); - REQUIRE(c[0] == 1); - REQUIRE(c[1] == 3); - REQUIRE(c[2] == 5); -} - TEST_CASE("StaticTensor2D flat view") { StaticTensor2D t; From 6f3a73e6e2c1a67146ebd66d150c8b561e21f875 Mon Sep 17 00:00:00 2001 From: John Tramm Date: Thu, 12 Feb 2026 11:58:23 -0600 Subject: [PATCH 38/51] ran clang format --- include/openmc/hdf5_interface.h | 6 +-- include/openmc/material.h | 2 +- include/openmc/mesh.h | 2 +- include/openmc/plot.h | 3 +- include/openmc/tallies/tally.h | 5 +- include/openmc/tensor.h | 68 +++++++++------------------ include/openmc/volume_calc.h | 2 +- include/openmc/xml_interface.h | 2 +- src/distribution_angle.cpp | 6 +-- src/distribution_energy.cpp | 10 ++-- src/endf.cpp | 8 ++-- src/hdf5_interface.cpp | 3 +- src/material.cpp | 3 +- src/mesh.cpp | 17 ++++--- src/nuclide.cpp | 6 +-- src/photon.cpp | 22 +++++---- src/physics.cpp | 8 ++-- src/plot.cpp | 6 ++- src/random_ray/flat_source_domain.cpp | 3 +- src/secondary_correlated.cpp | 16 +++---- src/secondary_kalbach.cpp | 14 +++--- src/secondary_thermal.cpp | 2 +- src/source.cpp | 3 +- src/state_point.cpp | 8 ++-- src/tallies/filter_cell_instance.cpp | 2 +- src/tallies/filter_meshmaterial.cpp | 2 +- src/tallies/tally.cpp | 14 +++--- src/tallies/trigger.cpp | 2 +- src/volume_calc.cpp | 5 +- src/weight_windows.cpp | 30 ++++++++---- src/xsdata.cpp | 44 +++++++++-------- tests/cpp_unit_tests/test_tensor.cpp | 32 ++++++++----- 32 files changed, 183 insertions(+), 173 deletions(-) diff --git a/include/openmc/hdf5_interface.h b/include/openmc/hdf5_interface.h index 60a634cffc8..a80b62cb079 100644 --- a/include/openmc/hdf5_interface.h +++ b/include/openmc/hdf5_interface.h @@ -458,9 +458,9 @@ inline void write_dataset( // overloads above. A generic Container parameter avoids duplicating the body // for both Tensor and StaticTensor2D. template>::value>> -inline void write_dataset( - hid_t obj_id, const char* name, const Container& arr) + typename = + std::enable_if_t>::value>> +inline void write_dataset(hid_t obj_id, const char* name, const Container& arr) { using T = typename std::decay_t::value_type; auto s = arr.shape(); diff --git a/include/openmc/material.h b/include/openmc/material.h index 7fced727293..3967c36878f 100644 --- a/include/openmc/material.h +++ b/include/openmc/material.h @@ -5,8 +5,8 @@ #include #include "openmc/span.h" -#include "pugixml.hpp" #include "openmc/tensor.h" +#include "pugixml.hpp" #include #include "openmc/bremsstrahlung.h" diff --git a/include/openmc/mesh.h b/include/openmc/mesh.h index cbdd8440c17..0d8189caa1d 100644 --- a/include/openmc/mesh.h +++ b/include/openmc/mesh.h @@ -8,8 +8,8 @@ #include #include "hdf5.h" -#include "pugixml.hpp" #include "openmc/tensor.h" +#include "pugixml.hpp" #include "openmc/bounding_box.h" #include "openmc/error.h" diff --git a/include/openmc/plot.h b/include/openmc/plot.h index 90745399504..24a3a9d2716 100644 --- a/include/openmc/plot.h +++ b/include/openmc/plot.h @@ -6,8 +6,8 @@ #include #include -#include "pugixml.hpp" #include "openmc/tensor.h" +#include "pugixml.hpp" #include "hdf5.h" #include "openmc/cell.h" @@ -143,7 +143,6 @@ class PlottableInterface { vector colors_; // Plot colors }; - struct IdData { // Constructor IdData(size_t h_res, size_t v_res); diff --git a/include/openmc/tallies/tally.h b/include/openmc/tallies/tally.h index 81a012db821..6161f823be1 100644 --- a/include/openmc/tallies/tally.h +++ b/include/openmc/tallies/tally.h @@ -8,8 +8,8 @@ #include "openmc/tallies/trigger.h" #include "openmc/vector.h" -#include "pugixml.hpp" #include "openmc/tensor.h" +#include "pugixml.hpp" #include #include @@ -219,8 +219,7 @@ extern vector time_grid; namespace simulation { //! Global tallies (such as k-effective estimators) -extern tensor::StaticTensor2D - global_tallies; +extern tensor::StaticTensor2D global_tallies; //! Number of realizations for global tallies extern "C" int32_t n_realizations; diff --git a/include/openmc/tensor.h b/include/openmc/tensor.h index 261b87b5598..2c5f5e239e7 100644 --- a/include/openmc/tensor.h +++ b/include/openmc/tensor.h @@ -283,11 +283,8 @@ class View { using reference = decltype(*std::declval()); view_iterator(Ptr base, size_t count, const View* v) - : base_(base) - , count_(count) - , shape_(v->shape_.data()) - , strides_(v->strides_.data()) - , ndim_(v->shape_.size()) + : base_(base), count_(count), shape_(v->shape_.data()), + strides_(v->strides_.data()), ndim_(v->shape_.size()) {} reference operator*() const { return base_[offset()]; } @@ -338,30 +335,12 @@ class View { count_ -= n; return *this; } - bool operator==(const view_iterator& o) const - { - return count_ == o.count_; - } - bool operator!=(const view_iterator& o) const - { - return count_ != o.count_; - } - bool operator<(const view_iterator& o) const - { - return count_ < o.count_; - } - bool operator>(const view_iterator& o) const - { - return count_ > o.count_; - } - bool operator<=(const view_iterator& o) const - { - return count_ <= o.count_; - } - bool operator>=(const view_iterator& o) const - { - return count_ >= o.count_; - } + bool operator==(const view_iterator& o) const { return count_ == o.count_; } + bool operator!=(const view_iterator& o) const { return count_ != o.count_; } + bool operator<(const view_iterator& o) const { return count_ < o.count_; } + bool operator>(const view_iterator& o) const { return count_ > o.count_; } + bool operator<=(const view_iterator& o) const { return count_ <= o.count_; } + bool operator>=(const view_iterator& o) const { return count_ >= o.count_; } friend view_iterator operator+(difference_type n, const view_iterator& it) { return it + n; @@ -407,7 +386,6 @@ class View { vector strides_; }; - //============================================================================== // Tensor: dynamic-rank N-dimensional tensor. // @@ -428,7 +406,8 @@ class Tensor { Tensor() = default; - //! Construct with shape (uninitialized for arithmetic types via vector resize) + //! Construct with shape (uninitialized for arithmetic types via vector + //! resize) explicit Tensor(vector shape) : shape_(std::move(shape)), data_(compute_size()) {} @@ -449,14 +428,12 @@ class Tensor { {} //! 1D copy from raw pointer + count - Tensor(const T* ptr, size_t count) - : shape_({count}), data_(ptr, ptr + count) + Tensor(const T* ptr, size_t count) : shape_({count}), data_(ptr, ptr + count) {} //! Copy from View (preserves view's shape) template - explicit Tensor(const View& v) - : shape_(v.shape_vec()) + explicit Tensor(const View& v) : shape_(v.shape_vec()) { size_t n = v.size(); data_.resize(n); @@ -494,7 +471,8 @@ class Tensor { const stored_type* data() const { return data_.data(); } size_t size() const { return data_.size(); } const vector& shape() const { return shape_; } - size_t shape(size_t dim) const { + size_t shape(size_t dim) const + { return dim < shape_.size() ? shape_[dim] : 0; } size_t ndim() const { return shape_.size(); } @@ -551,10 +529,7 @@ class Tensor { data_.resize(compute_size()); } - void reshape(const vector& new_shape) - { - shape_ = new_shape; - } + void reshape(const vector& new_shape) { shape_ = new_shape; } void fill(T val) { std::fill(data_.begin(), data_.end(), val); } @@ -671,9 +646,8 @@ class Tensor { //! Flat index of the minimum element size_t argmin() const { - return static_cast( - std::distance(data_.data(), - std::min_element(data_.data(), data_.data() + data_.size()))); + return static_cast(std::distance(data_.data(), + std::min_element(data_.data(), data_.data() + data_.size()))); } //! Reverse element order along an axis (e.g. flip(0) reverses rows) @@ -1031,8 +1005,8 @@ Tensor linspace(T start, T stop, size_t n) return result; } for (size_t i = 0; i < n; ++i) { - result[i] = start + static_cast(i) * (stop - start) / - static_cast(n - 1); + result[i] = + start + static_cast(i) * (stop - start) / static_cast(n - 1); } return result; } @@ -1076,8 +1050,8 @@ Tensor where( { Tensor r(cond.shape()); for (size_t i = 0; i < cond.size(); ++i) - r.data()[i] = cond.data()[i] ? true_val.data()[i] - : static_cast(false_val); + r.data()[i] = + cond.data()[i] ? true_val.data()[i] : static_cast(false_val); return r; } diff --git a/include/openmc/volume_calc.h b/include/openmc/volume_calc.h index dd75819e63f..9d3f1d02615 100644 --- a/include/openmc/volume_calc.h +++ b/include/openmc/volume_calc.h @@ -12,8 +12,8 @@ #include "openmc/tallies/trigger.h" #include "openmc/vector.h" -#include "pugixml.hpp" #include "openmc/tensor.h" +#include "pugixml.hpp" #ifdef _OPENMP #include #endif diff --git a/include/openmc/xml_interface.h b/include/openmc/xml_interface.h index 4d9918aa585..17a34e5c77a 100644 --- a/include/openmc/xml_interface.h +++ b/include/openmc/xml_interface.h @@ -5,8 +5,8 @@ #include // for stringstream #include -#include "pugixml.hpp" #include "openmc/tensor.h" +#include "pugixml.hpp" #include "openmc/position.h" #include "openmc/vector.h" diff --git a/src/distribution_angle.cpp b/src/distribution_angle.cpp index cb64cee4df0..74f8e3f6d7b 100644 --- a/src/distribution_angle.cpp +++ b/src/distribution_angle.cpp @@ -44,9 +44,9 @@ AngleDistribution::AngleDistribution(hid_t group) } // Create and initialize tabular distribution - tensor::View xs = temp.select(0,0).slice(j, j + n); - tensor::View ps = temp.select(0,1).slice(j, j + n); - tensor::View cs = temp.select(0,2).slice(j, j + n); + tensor::View xs = temp.select(0, 0).slice(j, j + n); + tensor::View ps = temp.select(0, 1).slice(j, j + n); + tensor::View cs = temp.select(0, 2).slice(j, j + n); vector x {xs.begin(), xs.end()}; vector p {ps.begin(), ps.end()}; vector c {cs.begin(), cs.end()}; diff --git a/src/distribution_energy.cpp b/src/distribution_energy.cpp index ab71857417b..9414f22ae2f 100644 --- a/src/distribution_energy.cpp +++ b/src/distribution_energy.cpp @@ -63,8 +63,8 @@ ContinuousTabular::ContinuousTabular(hid_t group) tensor::Tensor temp; read_attribute(dset, "interpolation", temp); - tensor::View temp_b = temp.select(0,0); // breakpoints - tensor::View temp_i = temp.select(0,1); // interpolation parameters + tensor::View temp_b = temp.select(0, 0); // breakpoints + tensor::View temp_i = temp.select(0, 1); // interpolation parameters std::copy(temp_b.begin(), temp_b.end(), std::back_inserter(breakpoints_)); for (const auto i : temp_i) @@ -105,15 +105,15 @@ ContinuousTabular::ContinuousTabular(hid_t group) d.n_discrete = n_discrete[i]; // Copy data - d.e_out = eout.select(0,0).slice(j, j + n); - d.p = eout.select(0,1).slice(j, j + n); + d.e_out = eout.select(0, 0).slice(j, j + n); + d.p = eout.select(0, 1).slice(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 = eout.select(0,2).slice(j, j + n); + d.c = eout.select(0, 2).slice(j, j + n); } else { // Calculate cumulative distribution function -- discrete portion for (int k = 0; k < d.n_discrete; ++k) { diff --git a/src/endf.cpp b/src/endf.cpp index b8ac9e3d201..65668ea38f7 100644 --- a/src/endf.cpp +++ b/src/endf.cpp @@ -155,8 +155,8 @@ Tabulated1D::Tabulated1D(hid_t dset) tensor::Tensor arr; read_dataset(dset, arr); - tensor::View xs = arr.select(0,0); - tensor::View ys = arr.select(0,1); + tensor::View xs = arr.select(0, 0); + tensor::View ys = arr.select(0, 1); std::copy(xs.begin(), xs.end(), std::back_inserter(x_)); std::copy(ys.begin(), ys.end(), std::back_inserter(y_)); @@ -232,8 +232,8 @@ CoherentElasticXS::CoherentElasticXS(hid_t dset) read_dataset(dset, arr); // Get views for Bragg edges and structure factors - tensor::View E = arr.select(0,0); - tensor::View s = arr.select(0,1); + tensor::View E = arr.select(0, 0); + tensor::View s = arr.select(0, 1); // Copy Bragg edges and partial sums of structure factors std::copy(E.begin(), E.end(), std::back_inserter(bragg_edges_)); diff --git a/src/hdf5_interface.cpp b/src/hdf5_interface.cpp index 642365c5bbb..00c6a4399c0 100644 --- a/src/hdf5_interface.cpp +++ b/src/hdf5_interface.cpp @@ -476,7 +476,8 @@ void read_dataset( tensor.resize(tshape); // Read data from dataset - read_complex(dset, nullptr, reinterpret_cast*>(tensor.data()), indep); + read_complex(dset, nullptr, + reinterpret_cast*>(tensor.data()), indep); } void read_double(hid_t obj_id, const char* name, double* buffer, bool indep) diff --git a/src/material.cpp b/src/material.cpp index c168c3bbc26..cd8c31f5698 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -795,7 +795,8 @@ void Material::init_bremsstrahlung() } // Use logarithm of number yield since it is log-log interpolated - ttb->yield = tensor::where(ttb->yield > 0.0, tensor::log(ttb->yield), -500.0); + ttb->yield = + tensor::where(ttb->yield > 0.0, tensor::log(ttb->yield), -500.0); } } diff --git a/src/mesh.cpp b/src/mesh.cpp index 0534af75280..27fe29d613f 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -954,7 +954,8 @@ void UnstructuredMesh::to_hdf5_inner(hid_t mesh_group) const write_dataset(mesh_group, "length_multiplier", length_multiplier_); // write vertex coordinates - tensor::Tensor vertices({static_cast(this->n_vertices()), static_cast(3)}); + tensor::Tensor vertices( + {static_cast(this->n_vertices()), static_cast(3)}); for (int i = 0; i < this->n_vertices(); i++) { auto v = this->vertex(i); vertices.select(0, i) = {v.x, v.y, v.z}; @@ -965,8 +966,10 @@ void UnstructuredMesh::to_hdf5_inner(hid_t mesh_group) const // write element types and connectivity vector volumes; - tensor::Tensor connectivity({static_cast(this->n_bins()), static_cast(8)}); - tensor::Tensor elem_types({static_cast(this->n_bins()), static_cast(1)}); + tensor::Tensor connectivity( + {static_cast(this->n_bins()), static_cast(8)}); + tensor::Tensor elem_types( + {static_cast(this->n_bins()), static_cast(1)}); for (int i = 0; i < this->n_bins(); i++) { auto conn = this->connectivity(i); @@ -975,13 +978,13 @@ void UnstructuredMesh::to_hdf5_inner(hid_t mesh_group) const // write linear tet element if (conn.size() == 4) { elem_types.select(0, i) = static_cast(ElementType::LINEAR_TET); - connectivity.select(0, i) = - {conn[0], conn[1], conn[2], conn[3], -1, -1, -1, -1}; + connectivity.select(0, i) = { + conn[0], conn[1], conn[2], conn[3], -1, -1, -1, -1}; // write linear hex element } else if (conn.size() == 8) { elem_types.select(0, i) = static_cast(ElementType::LINEAR_HEX); - connectivity.select(0, i) = {conn[0], conn[1], - conn[2], conn[3], conn[4], conn[5], conn[6], conn[7]}; + connectivity.select(0, i) = { + conn[0], conn[1], conn[2], conn[3], conn[4], conn[5], conn[6], conn[7]}; } else { num_elem_skipped++; elem_types.select(0, i) = static_cast(ElementType::UNSUPPORTED); diff --git a/src/nuclide.cpp b/src/nuclide.cpp index 40e280b7674..89c0ab3a008 100644 --- a/src/nuclide.cpp +++ b/src/nuclide.cpp @@ -360,8 +360,7 @@ void Nuclide::create_derived( { for (const auto& grid : grid_) { // Allocate and initialize cross section - xs_.emplace_back( - vector{grid.energy.size(), 5}, 0.0); + xs_.emplace_back(vector {grid.energy.size(), 5}, 0.0); } reaction_index_.fill(C_NONE); @@ -374,7 +373,8 @@ void Nuclide::create_derived( for (int t = 0; t < kTs_.size(); ++t) { int j = rx->xs_[t].threshold; int n = rx->xs_[t].value.size(); - auto xs = tensor::Tensor(rx->xs_[t].value.data(), rx->xs_[t].value.size()); + auto xs = tensor::Tensor( + rx->xs_[t].value.data(), rx->xs_[t].value.size()); for (const auto& p : rx->products_) { if (p.particle_.is_photon()) { for (int k = 0; k < n; ++k) { diff --git a/src/photon.cpp b/src/photon.cpp index 22a3ac50960..1fc9eb5cb09 100644 --- a/src/photon.cpp +++ b/src/photon.cpp @@ -169,7 +169,7 @@ PhotonInteraction::PhotonInteraction(hid_t group) read_dataset(tgroup, "xs", xs); auto cross_section = - cross_sections_.select(1,i).slice(static_cast(shell.threshold)); + cross_sections_.select(1, i).slice(static_cast(shell.threshold)); cross_section = tensor::where(xs > 0, tensor::log(xs), 0); if (object_exists(tgroup, "transitions")) { @@ -184,7 +184,7 @@ PhotonInteraction::PhotonInteraction(hid_t group) read_dataset(tgroup, "transitions", matrix); // Transition probability normalization - double norm = tensor::Tensor(matrix.select(1,3)).sum(); + double norm = tensor::Tensor(matrix.select(1, 3)).sum(); shell.transitions.resize(n_transition); for (int j = 0; j < n_transition; ++j) { @@ -304,7 +304,7 @@ PhotonInteraction::PhotonInteraction(hid_t group) double y = std::exp( std::log(dcs_(i_grid, i)) + f * (std::log(dcs_(i_grid + 1, i)) - std::log(dcs_(i_grid, i)))); - tensor::View col_i = dcs.select(1,i); + tensor::View col_i = dcs.select(1, i); col_i(0) = y; for (int j = i_grid + 1; j < n_e; ++j) { col_i(j - i_grid) = dcs_(j, i); @@ -324,7 +324,8 @@ PhotonInteraction::PhotonInteraction(hid_t group) } // Calculate the radiative stopping power - stopping_power_radiative_ = tensor::Tensor({data::ttb_e_grid.size()}); + stopping_power_radiative_ = + tensor::Tensor({data::ttb_e_grid.size()}); for (int i = 0; i < data::ttb_e_grid.size(); ++i) { // Integrate over reduced photon energy double c = 0.0; @@ -351,11 +352,12 @@ PhotonInteraction::PhotonInteraction(hid_t group) double limit = std::exp(-499.0); energy_ = tensor::log(energy_); coherent_ = tensor::where(coherent_ > limit, tensor::log(coherent_), -900.0); - incoherent_ = tensor::where(incoherent_ > limit, tensor::log(incoherent_), -900.0); + incoherent_ = + tensor::where(incoherent_ > limit, tensor::log(incoherent_), -900.0); photoelectric_total_ = tensor::where( photoelectric_total_ > limit, tensor::log(photoelectric_total_), -900.0); - pair_production_total_ = tensor::where( - pair_production_total_ > limit, tensor::log(pair_production_total_), -900.0); + pair_production_total_ = tensor::where(pair_production_total_ > limit, + tensor::log(pair_production_total_), -900.0); heating_ = tensor::where(heating_ > limit, tensor::log(heating_), -900.0); } @@ -507,7 +509,7 @@ void PhotonInteraction::compton_doppler( c = prn(seed) * c_max; // Determine pz corresponding to sampled cdf value - tensor::View cdf_shell = profile_cdf_.select(0,shell); + tensor::View cdf_shell = profile_cdf_.select(0, shell); int i = lower_bound_index(cdf_shell.cbegin(), cdf_shell.cend(), c); double pz_l = data::compton_profile_pz(i); double pz_r = data::compton_profile_pz(i + 1); @@ -603,8 +605,8 @@ void PhotonInteraction::calculate_xs(Particle& p) const // Calculate microscopic photoelectric cross section xs.photoelectric = 0.0; - tensor::View xs_lower = cross_sections_.select(0,i_grid); - tensor::View xs_upper = cross_sections_.select(0,i_grid + 1); + tensor::View xs_lower = cross_sections_.select(0, i_grid); + tensor::View xs_upper = cross_sections_.select(0, i_grid + 1); for (int i = 0; i < xs_upper.size(); ++i) if (xs_lower(i) != 0) diff --git a/src/physics.cpp b/src/physics.cpp index d25c19f90c7..871a9195158 100644 --- a/src/physics.cpp +++ b/src/physics.cpp @@ -30,9 +30,9 @@ #include +#include "openmc/tensor.h" #include // for max, min, max_element #include // for sqrt, exp, log, abs, copysign -#include "openmc/tensor.h" namespace openmc { @@ -375,8 +375,10 @@ void sample_photon_reaction(Particle& p) // cross sections int i_grid = micro.index_grid; double f = micro.interp_factor; - tensor::View xs_lower = element.cross_sections_.select(0,i_grid); - tensor::View xs_upper = element.cross_sections_.select(0,i_grid + 1); + tensor::View xs_lower = + element.cross_sections_.select(0, i_grid); + tensor::View xs_upper = + element.cross_sections_.select(0, i_grid + 1); for (int i_shell = 0; i_shell < element.shells_.size(); ++i_shell) { const auto& shell {element.shells_[i_shell]}; diff --git a/src/plot.cpp b/src/plot.cpp index c151815fd89..a65bdb53951 100644 --- a/src/plot.cpp +++ b/src/plot.cpp @@ -73,7 +73,8 @@ void IdData::set_value(size_t y, size_t x, const GeometryState& p, int level) void IdData::set_overlap(size_t y, size_t x) { - for (size_t k = 0; k < data_.shape(2); ++k) data_(y, x, k) = OVERLAP; + for (size_t k = 0; k < data_.shape(2); ++k) + data_(y, x, k) = OVERLAP; } PropertyData::PropertyData(size_t h_res, size_t v_res) @@ -1250,7 +1251,8 @@ ImageData WireframeRayTracePlot::create_image() const // This array marks where the initial wireframe was drawn. We convolve it with // a filter that gets adjusted with the wireframe thickness in order to // thicken the lines. - tensor::Tensor wireframe_initial({static_cast(width), static_cast(height)}, 0); + tensor::Tensor wireframe_initial( + {static_cast(width), static_cast(height)}, 0); /* Holds all of the track segments for the current rendered line of pixels. * old_segments holds a copy of this_line_segments from the previous line. diff --git a/src/random_ray/flat_source_domain.cpp b/src/random_ray/flat_source_domain.cpp index 883a90aef61..b881ced716b 100644 --- a/src/random_ray/flat_source_domain.cpp +++ b/src/random_ray/flat_source_domain.cpp @@ -64,8 +64,7 @@ FlatSourceDomain::FlatSourceDomain() : negroups_(data::mg.num_energy_groups_) // Create a new 2D tensor with the same size as the first // two dimensions of the 3D tensor - tally_volumes_[i] = - tensor::Tensor({shape[0], shape[1]}); + tally_volumes_[i] = tensor::Tensor({shape[0], shape[1]}); } } diff --git a/src/secondary_correlated.cpp b/src/secondary_correlated.cpp index 8f074ae4ba0..d3d01908dce 100644 --- a/src/secondary_correlated.cpp +++ b/src/secondary_correlated.cpp @@ -28,8 +28,8 @@ CorrelatedAngleEnergy::CorrelatedAngleEnergy(hid_t group) tensor::Tensor temp; read_attribute(dset, "interpolation", temp); - tensor::View temp_b = temp.select(0,0); // breakpoints - tensor::View temp_i = temp.select(0,1); // interpolation parameters + tensor::View temp_b = temp.select(0, 0); // breakpoints + tensor::View temp_i = temp.select(0, 1); // interpolation parameters std::copy(temp_b.begin(), temp_b.end(), std::back_inserter(breakpoints_)); for (const auto i : temp_i) @@ -74,9 +74,9 @@ CorrelatedAngleEnergy::CorrelatedAngleEnergy(hid_t group) d.n_discrete = n_discrete[i]; // Copy data - d.e_out = eout.select(0,0).slice(j, j + n); - d.p = eout.select(0,1).slice(j, j + n); - d.c = eout.select(0,2).slice(j, j + n); + d.e_out = eout.select(0, 0).slice(j, j + n); + d.p = eout.select(0, 1).slice(j, j + n); + d.c = eout.select(0, 2).slice(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 @@ -132,9 +132,9 @@ CorrelatedAngleEnergy::CorrelatedAngleEnergy(hid_t group) interp_mu = 1; auto interp = int2interp(interp_mu); - tensor::View xs = mu.select(0,0).slice(offset_mu, offset_mu + m); - tensor::View ps = mu.select(0,1).slice(offset_mu, offset_mu + m); - tensor::View cs = mu.select(0,2).slice(offset_mu, offset_mu + m); + tensor::View xs = mu.select(0, 0).slice(offset_mu, offset_mu + m); + tensor::View ps = mu.select(0, 1).slice(offset_mu, offset_mu + m); + tensor::View cs = mu.select(0, 2).slice(offset_mu, offset_mu + m); vector x {xs.begin(), xs.end()}; vector p {ps.begin(), ps.end()}; diff --git a/src/secondary_kalbach.cpp b/src/secondary_kalbach.cpp index 2c82bc09177..24d0fce4e95 100644 --- a/src/secondary_kalbach.cpp +++ b/src/secondary_kalbach.cpp @@ -29,8 +29,8 @@ KalbachMann::KalbachMann(hid_t group) tensor::Tensor temp; read_attribute(dset, "interpolation", temp); - tensor::View temp_b = temp.select(0,0); // breakpoints - tensor::View temp_i = temp.select(0,1); // interpolation parameters + tensor::View temp_b = temp.select(0, 0); // breakpoints + tensor::View temp_i = temp.select(0, 1); // interpolation parameters std::copy(temp_b.begin(), temp_b.end(), std::back_inserter(breakpoints_)); for (const auto i : temp_i) @@ -71,11 +71,11 @@ KalbachMann::KalbachMann(hid_t group) d.n_discrete = n_discrete[i]; // Copy data - d.e_out = eout.select(0,0).slice(j, j + n); - d.p = eout.select(0,1).slice(j, j + n); - d.c = eout.select(0,2).slice(j, j + n); - d.r = eout.select(0,3).slice(j, j + n); - d.a = eout.select(0,4).slice(j, j + n); + d.e_out = eout.select(0, 0).slice(j, j + n); + d.p = eout.select(0, 1).slice(j, j + n); + d.c = eout.select(0, 2).slice(j, j + n); + d.r = eout.select(0, 3).slice(j, j + n); + d.a = eout.select(0, 4).slice(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 diff --git a/src/secondary_thermal.cpp b/src/secondary_thermal.cpp index 6a499e538ec..f3145a5d6d8 100644 --- a/src/secondary_thermal.cpp +++ b/src/secondary_thermal.cpp @@ -222,7 +222,7 @@ IncoherentInelasticAE::IncoherentInelasticAE(hid_t group) } // Copy outgoing angles - tensor::View mu_j = d.mu.select(0,j); + tensor::View mu_j = d.mu.select(0, j); std::copy(adist->x().begin(), adist->x().end(), mu_j.begin()); } } diff --git a/src/source.cpp b/src/source.cpp index 121528fdfa9..5300a685f78 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -400,7 +400,8 @@ SourceSite IndependentSource::sample(uint64_t* seed) const auto p = particle_.transport_index(); auto energy_ptr = dynamic_cast(energy_.get()); if (energy_ptr) { - auto energies = tensor::Tensor(energy_ptr->x().data(), energy_ptr->x().size()); + auto energies = + tensor::Tensor(energy_ptr->x().data(), energy_ptr->x().size()); if ((energies > data::energy_max[p]).any()) { fatal_error("Source energy above range of energies of at least " "one cross section table"); diff --git a/src/state_point.cpp b/src/state_point.cpp index 848d82b0f4a..c0d8ab5b2fa 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -276,8 +276,8 @@ extern "C" int openmc_statepoint_write(const char* filename, bool* write_source) std::string name = "tally " + std::to_string(tally->id_); hid_t tally_group = open_group(tallies_group, name.c_str()); auto& results = tally->results_; - write_tally_results(tally_group, results.shape(0), - results.shape(1), results.shape(2), results.data()); + write_tally_results(tally_group, results.shape(0), results.shape(1), + results.shape(2), results.data()); close_group(tally_group); } } else { @@ -516,8 +516,8 @@ extern "C" int openmc_statepoint_load(const char* filename) tally->writable_ = false; } else { auto& results = tally->results_; - read_tally_results(tally_group, results.shape(0), - results.shape(1), results.shape(2), results.data()); + read_tally_results(tally_group, results.shape(0), results.shape(1), + results.shape(2), results.data()); read_dataset(tally_group, "n_realizations", tally->n_realizations_); close_group(tally_group); diff --git a/src/tallies/filter_cell_instance.cpp b/src/tallies/filter_cell_instance.cpp index fa2016c1025..928bfb6c5e7 100644 --- a/src/tallies/filter_cell_instance.cpp +++ b/src/tallies/filter_cell_instance.cpp @@ -7,9 +7,9 @@ #include "openmc/capi.h" #include "openmc/cell.h" -#include "openmc/tensor.h" #include "openmc/error.h" #include "openmc/geometry.h" +#include "openmc/tensor.h" #include "openmc/xml_interface.h" namespace openmc { diff --git a/src/tallies/filter_meshmaterial.cpp b/src/tallies/filter_meshmaterial.cpp index 58ba7195abc..b45bb4164eb 100644 --- a/src/tallies/filter_meshmaterial.cpp +++ b/src/tallies/filter_meshmaterial.cpp @@ -7,11 +7,11 @@ #include "openmc/capi.h" #include "openmc/constants.h" -#include "openmc/tensor.h" #include "openmc/container_util.h" #include "openmc/error.h" #include "openmc/material.h" #include "openmc/mesh.h" +#include "openmc/tensor.h" #include "openmc/xml_interface.h" namespace openmc { diff --git a/src/tallies/tally.cpp b/src/tallies/tally.cpp index e33e0095bb7..712c208d5c3 100644 --- a/src/tallies/tally.cpp +++ b/src/tallies/tally.cpp @@ -804,9 +804,11 @@ void Tally::init_results() { int n_scores = scores_.size() * nuclides_.size(); if (higher_moments_) { - results_ = tensor::Tensor({static_cast(n_filter_bins_), static_cast(n_scores), size_t{5}}); + results_ = tensor::Tensor({static_cast(n_filter_bins_), + static_cast(n_scores), size_t {5}}); } else { - results_ = tensor::Tensor({static_cast(n_filter_bins_), static_cast(n_scores), size_t{3}}); + results_ = tensor::Tensor({static_cast(n_filter_bins_), + static_cast(n_scores), size_t {3}}); } } @@ -1031,8 +1033,8 @@ void reduce_tally_results() const int val_col = static_cast(TallyResult::VALUE); // Copy VALUE column into contiguous array for MPI reduction - tensor::Tensor gt_values(gt.select(1,val_col)); - tensor::Tensor gt_values_reduced({size_t{N_GLOBAL_TALLIES}}); + tensor::Tensor gt_values(gt.select(1, val_col)); + tensor::Tensor gt_values_reduced({size_t {N_GLOBAL_TALLIES}}); // Reduce contiguous data MPI_Reduce(gt_values.data(), gt_values_reduced.data(), N_GLOBAL_TALLIES, @@ -1040,9 +1042,9 @@ void reduce_tally_results() // Transfer values on master and reset on other ranks if (mpi::master) { - gt.select(1,val_col) = gt_values_reduced; + gt.select(1, val_col) = gt_values_reduced; } else { - gt.select(1,val_col) = 0.0; + gt.select(1, val_col) = 0.0; } // We also need to determine the total starting weight of particles from the diff --git a/src/tallies/trigger.cpp b/src/tallies/trigger.cpp index 6c54edd5e1d..07295cc7712 100644 --- a/src/tallies/trigger.cpp +++ b/src/tallies/trigger.cpp @@ -72,7 +72,7 @@ void check_tally_triggers(double& ratio, int& tally_id, int& score) const auto& results = t.results_; for (auto filter_index = 0; filter_index < results.shape(0); - ++filter_index) { + ++filter_index) { // Compute the tally uncertainty metrics. auto uncert_pair = get_tally_uncertainty(i_tally, trigger.score_index, filter_index); diff --git a/src/volume_calc.cpp b/src/volume_calc.cpp index 01f541d710f..06271bd1884 100644 --- a/src/volume_calc.cpp +++ b/src/volume_calc.cpp @@ -241,7 +241,8 @@ vector VolumeCalculation::execute() const // non-zero auto n_nuc = settings::run_CE ? data::nuclides.size() : data::mg.nuclides_.size(); - tensor::Tensor atoms({static_cast(n_nuc), size_t{2}}, 0.0); + tensor::Tensor atoms( + {static_cast(n_nuc), size_t {2}}, 0.0); #ifdef OPENMC_MPI if (mpi::master) { @@ -451,7 +452,7 @@ void VolumeCalculation::to_hdf5( } // Create array of total # of atoms with uncertainty for each nuclide - tensor::Tensor atom_data({static_cast(n_nuc), size_t{2}}); + tensor::Tensor atom_data({static_cast(n_nuc), size_t {2}}); for (size_t k = 0; k < static_cast(n_nuc); ++k) { atom_data(k, 0) = result.atoms[k]; atom_data(k, 1) = result.uncertainty[k]; diff --git a/src/weight_windows.cpp b/src/weight_windows.cpp index d1f057e4594..c0516913204 100644 --- a/src/weight_windows.cpp +++ b/src/weight_windows.cpp @@ -260,8 +260,12 @@ WeightWindows* WeightWindows::from_hdf5( } wws->set_mesh(model::mesh_map[mesh_id]); - wws->lower_ww_ = tensor::Tensor({static_cast(wws->bounds_size()[0]), static_cast(wws->bounds_size()[1])}); - wws->upper_ww_ = tensor::Tensor({static_cast(wws->bounds_size()[0]), static_cast(wws->bounds_size()[1])}); + wws->lower_ww_ = + tensor::Tensor({static_cast(wws->bounds_size()[0]), + static_cast(wws->bounds_size()[1])}); + wws->upper_ww_ = + tensor::Tensor({static_cast(wws->bounds_size()[0]), + static_cast(wws->bounds_size()[1])}); read_dataset(ww_group, "lower_ww_bounds", wws->lower_ww_); read_dataset(ww_group, "upper_ww_bounds", wws->upper_ww_); @@ -296,9 +300,11 @@ void WeightWindows::allocate_ww_bounds() "Size of weight window bounds is zero for WeightWindows {}", id()); warning(msg); } - lower_ww_ = tensor::Tensor({static_cast(shape[0]), static_cast(shape[1])}); + lower_ww_ = tensor::Tensor( + {static_cast(shape[0]), static_cast(shape[1])}); lower_ww_.fill(-1); - upper_ww_ = tensor::Tensor({static_cast(shape[0]), static_cast(shape[1])}); + upper_ww_ = tensor::Tensor( + {static_cast(shape[0]), static_cast(shape[1])}); upper_ww_.fill(-1); } @@ -470,8 +476,10 @@ void WeightWindows::set_bounds( { check_bounds(lower_bounds, upper_bounds); auto shape = this->bounds_size(); - lower_ww_ = tensor::Tensor({static_cast(shape[0]), static_cast(shape[1])}); - upper_ww_ = tensor::Tensor({static_cast(shape[0]), static_cast(shape[1])}); + lower_ww_ = tensor::Tensor( + {static_cast(shape[0]), static_cast(shape[1])}); + upper_ww_ = tensor::Tensor( + {static_cast(shape[0]), static_cast(shape[1])}); // Copy weight window values from input spans into the tensors std::copy(lower_bounds.data(), lower_bounds.data() + lower_ww_.size(), @@ -485,8 +493,10 @@ void WeightWindows::set_bounds(span lower_bounds, double ratio) this->check_bounds(lower_bounds); auto shape = this->bounds_size(); - lower_ww_ = tensor::Tensor({static_cast(shape[0]), static_cast(shape[1])}); - upper_ww_ = tensor::Tensor({static_cast(shape[0]), static_cast(shape[1])}); + lower_ww_ = tensor::Tensor( + {static_cast(shape[0]), static_cast(shape[1])}); + upper_ww_ = tensor::Tensor( + {static_cast(shape[0]), static_cast(shape[1])}); // Copy lower bounds into both arrays, then scale upper by ratio std::copy(lower_bounds.data(), lower_bounds.data() + lower_ww_.size(), @@ -1168,8 +1178,8 @@ extern "C" int openmc_weight_windows_set_bounds(int32_t index, return err; const auto& wws = variance_reduction::weight_windows[index]; - wws->set_bounds( - span(lower_bounds, size), span(upper_bounds, size)); + wws->set_bounds(span(lower_bounds, size), + span(upper_bounds, size)); return 0; } diff --git a/src/xsdata.cpp b/src/xsdata.cpp index 0a11afe7d6a..9e05e4f7557 100644 --- a/src/xsdata.cpp +++ b/src/xsdata.cpp @@ -100,7 +100,8 @@ void XsData::from_hdf5(hid_t xsdata_grp, bool fissionable, // Replace zero absorption values with a small number to avoid // division by zero in tally methods for (size_t i = 0; i < absorption.size(); i++) - if (absorption.data()[i] == 0.0) absorption.data()[i] = 1.e-10; + if (absorption.data()[i] == 0.0) + absorption.data()[i] = 1.e-10; // Get or calculate the total x/s if (object_exists(xsdata_grp, "total")) { @@ -116,7 +117,8 @@ void XsData::from_hdf5(hid_t xsdata_grp, bool fissionable, // Replace zero total cross sections with a small number to avoid // division by zero in tally methods for (size_t i = 0; i < total.size(); i++) - if (total.data()[i] == 0.0) total.data()[i] = 1.e-10; + if (total.data()[i] == 0.0) + total.data()[i] = 1.e-10; } //============================================================================== @@ -146,7 +148,8 @@ void XsData::fission_vector_beta_from_hdf5( for (size_t a = 0; a < n_ang; a++) for (size_t d = 0; d < n_dg_; d++) for (size_t gin = 0; gin < n_g_; gin++) - chi_delayed.select(0, a).select(0, d).select(0, gin) = temp_chi.select(0, a); + chi_delayed.select(0, a).select(0, d).select(0, gin) = + temp_chi.select(0, a); // Get nu-fission tensor::Tensor temp_nufiss({n_ang, n_g_}, 0.); @@ -230,7 +233,8 @@ void XsData::fission_vector_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) for (size_t a = 0; a < n_ang; a++) for (size_t d = 0; d < n_dg_; d++) for (size_t gin = 0; gin < n_g_; gin++) - chi_delayed.select(0, a).select(0, d).select(0, gin) = temp_chi_d.select(0, a).select(0, d); + chi_delayed.select(0, a).select(0, d).select(0, gin) = + temp_chi_d.select(0, a).select(0, d); // Get prompt and delayed nu-fission directly read_nd_tensor(xsdata_grp, "prompt-nu-fission", prompt_nu_fission, true); @@ -303,8 +307,7 @@ void XsData::fission_matrix_beta_from_hdf5( for (size_t a = 0; a < n_ang; a++) for (size_t d = 0; d < n_dg_; d++) for (size_t g = 0; g < n_g_; g++) - delayed_nu_fission(a, d, g) = - temp_beta(a, d) * matrix_gout_sum(a, g); + delayed_nu_fission(a, d, g) = temp_beta(a, d) * matrix_gout_sum(a, g); // chi_delayed = beta * nu-fission matrix, expanded across delayed groups for (size_t a = 0; a < n_ang; a++) @@ -363,7 +366,8 @@ void XsData::fission_matrix_beta_from_hdf5( for (size_t a = 0; a < n_ang; a++) for (size_t d = 0; d < n_dg_; d++) for (size_t gin = 0; gin < n_g_; gin++) { - tensor::View row = chi_delayed.select(0, a).select(0, d).select(0, gin); + tensor::View row = + chi_delayed.select(0, a).select(0, d).select(0, gin); row /= row.sum(); } } @@ -383,8 +387,8 @@ void XsData::fission_matrix_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) for (size_t a = 0; a < n_ang; a++) for (size_t gin = 0; gin < n_g_; gin++) for (size_t gout = 0; gout < n_g_; gout++) - chi_prompt(a, gin, gout) = temp_matrix_p(a, gin, gout) / - prompt_nu_fission(a, gin); + chi_prompt(a, gin, gout) = + temp_matrix_p(a, gin, gout) / prompt_nu_fission(a, gin); // Get the delayed nu-fission matrix tensor::Tensor temp_matrix_d({n_ang, n_dg_, n_g_, n_g_}, 0.); @@ -393,13 +397,14 @@ void XsData::fission_matrix_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) // delayed_nu_fission is the sum over outgoing groups delayed_nu_fission = temp_matrix_d.sum(3); - // chi_delayed is the delayed nu-fission matrix normalized over outgoing groups + // chi_delayed is the delayed nu-fission matrix normalized over outgoing + // groups for (size_t a = 0; a < n_ang; a++) for (size_t d = 0; d < n_dg_; d++) for (size_t gin = 0; gin < n_g_; gin++) for (size_t gout = 0; gout < n_g_; gout++) - chi_delayed(a, d, gin, gout) = temp_matrix_d(a, d, gin, gout) / - delayed_nu_fission(a, d, gin); + chi_delayed(a, d, gin, gout) = + temp_matrix_d(a, d, gin, gout) / delayed_nu_fission(a, d, gin); } void XsData::fission_matrix_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang) @@ -418,8 +423,8 @@ void XsData::fission_matrix_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang) for (size_t a = 0; a < n_ang; a++) for (size_t gin = 0; gin < n_g_; gin++) for (size_t gout = 0; gout < n_g_; gout++) - chi_prompt(a, gin, gout) = temp_matrix(a, gin, gout) / - prompt_nu_fission(a, gin); + chi_prompt(a, gin, gout) = + temp_matrix(a, gin, gout) / prompt_nu_fission(a, gin); } //============================================================================== @@ -606,8 +611,8 @@ void XsData::combine( for (size_t a = 0; a < n_ang; a++) for (size_t gin = 0; gin < n_g; gin++) for (size_t gout = 0; gout < n_g; gout++) - chi_prompt(a, gin, gout) += scalar * pnf_sum(a) * - that->chi_prompt(a, gin, gout); + chi_prompt(a, gin, gout) += + scalar * pnf_sum(a) * that->chi_prompt(a, gin, gout); } // Accumulate chi_delayed weighted by total delayed nu-fission // (summed over energy groups) for this constituent @@ -620,8 +625,8 @@ void XsData::combine( for (size_t d = 0; d < n_dg; d++) for (size_t gin = 0; gin < n_g; gin++) for (size_t gout = 0; gout < n_g; gout++) - chi_delayed(a, d, gin, gout) += scalar * dnf_sum(a, d) * - that->chi_delayed(a, d, gin, gout); + chi_delayed(a, d, gin, gout) += + scalar * dnf_sum(a, d) * that->chi_delayed(a, d, gin, gout); } } decay_rate += scalar * that->decay_rate; @@ -645,7 +650,8 @@ void XsData::combine( for (size_t a = 0; a < n_ang; a++) for (size_t d = 0; d < n_dg; d++) for (size_t gin = 0; gin < n_g; gin++) { - tensor::View row = chi_delayed.select(0, a).select(0, d).select(0, gin); + tensor::View row = + chi_delayed.select(0, a).select(0, d).select(0, gin); row /= row.sum(); } } diff --git a/tests/cpp_unit_tests/test_tensor.cpp b/tests/cpp_unit_tests/test_tensor.cpp index 88a0c3a0c91..55f3fa9ff39 100644 --- a/tests/cpp_unit_tests/test_tensor.cpp +++ b/tests/cpp_unit_tests/test_tensor.cpp @@ -293,9 +293,12 @@ TEST_CASE("Tensor flip 2D") { // [[1, 2], [3, 4], [5, 6]] Tensor t({3, 2}, 0); - t(0, 0) = 1; t(0, 1) = 2; - t(1, 0) = 3; t(1, 1) = 4; - t(2, 0) = 5; t(2, 1) = 6; + t(0, 0) = 1; + t(0, 1) = 2; + t(1, 0) = 3; + t(1, 1) = 4; + t(2, 0) = 5; + t(2, 1) = 6; // Flip axis 0 reverses rows -> [[5,6],[3,4],[1,2]] Tensor f = t.flip(0); @@ -501,9 +504,12 @@ TEST_CASE("Tensor select axis 1 (2D)") { // [[1, 2], [3, 4], [5, 6]] Tensor t({3, 2}, 0); - t(0, 0) = 1; t(0, 1) = 2; - t(1, 0) = 3; t(1, 1) = 4; - t(2, 0) = 5; t(2, 1) = 6; + t(0, 0) = 1; + t(0, 1) = 2; + t(1, 0) = 3; + t(1, 1) = 4; + t(2, 0) = 5; + t(2, 1) = 6; auto c0 = t.select(1, 0); REQUIRE(c0.size() == 3); @@ -688,8 +694,8 @@ TEST_CASE("View sub-slice") Tensor t({6}, 0); t = {10, 20, 30, 40, 50, 60}; - auto s = t.slice(1, 5); // [20, 30, 40, 50] - auto ss = s.slice(1, 3); // [30, 40] + auto s = t.slice(1, 5); // [20, 30, 40, 50] + auto ss = s.slice(1, 3); // [30, 40] REQUIRE(ss.size() == 2); REQUIRE(ss[0] == 30); REQUIRE(ss[1] == 40); @@ -774,8 +780,10 @@ TEST_CASE("StaticTensor2D iteration") TEST_CASE("StaticTensor2D flat view") { StaticTensor2D t; - t(0, 0) = 1.0; t(0, 1) = 2.0; - t(1, 0) = 3.0; t(1, 1) = 4.0; + t(0, 0) = 1.0; + t(0, 1) = 2.0; + t(1, 0) = 3.0; + t(1, 1) = 4.0; auto f = t.flat(); REQUIRE(f.size() == 4); @@ -891,9 +899,9 @@ TEST_CASE("nan_to_num") auto r = nan_to_num(t); REQUIRE(r[0] == 1.0); - REQUIRE(r[1] == 0.0); // NaN -> 0 + REQUIRE(r[1] == 0.0); // NaN -> 0 REQUIRE(r[2] == std::numeric_limits::max()); // +inf -> max - REQUIRE(r[3] == std::numeric_limits::lowest()); // -inf -> lowest + REQUIRE(r[3] == std::numeric_limits::lowest()); // -inf -> lowest } // ============================================================================ From 052ec17a2d169e63bcdd1ffa227931a01a01a97c Mon Sep 17 00:00:00 2001 From: John Tramm Date: Thu, 12 Feb 2026 14:11:40 -0600 Subject: [PATCH 39/51] reverted unneeded comment mod --- include/openmc/hdf5_interface.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/openmc/hdf5_interface.h b/include/openmc/hdf5_interface.h index a80b62cb079..93e4bfc820a 100644 --- a/include/openmc/hdf5_interface.h +++ b/include/openmc/hdf5_interface.h @@ -394,8 +394,7 @@ inline void write_attribute(hid_t obj_id, const char* name, Position r) // Templates/overloads for write_dataset //============================================================================== -// Template for scalars. A SFINAE guard is used here to prevent this template -// from matching Tensor/vector/string types that have their own overloads below. +// Template for scalars (ensured by SFINAE) template inline std::enable_if_t>::value> write_dataset( hid_t obj_id, const char* name, T buffer) From 07c41437c268784a67390d8aa3bb73b8bb7b6dcd Mon Sep 17 00:00:00 2001 From: John Tramm Date: Thu, 12 Feb 2026 14:30:49 -0600 Subject: [PATCH 40/51] fixed MPI error --- include/openmc/tensor.h | 25 +++++++++++++++++++++++++ tests/cpp_unit_tests/test_tensor.cpp | 21 +++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/include/openmc/tensor.h b/include/openmc/tensor.h index 2c5f5e239e7..8efb8efdb4a 100644 --- a/include/openmc/tensor.h +++ b/include/openmc/tensor.h @@ -955,6 +955,31 @@ class StaticTensor2D { //-------------------------------------------------------------------------- // View accessors + //! Select along an axis: select(0, i) returns row i, select(1, j) returns + //! column j. + template + View select(size_t axis, I index) + { + size_t idx = static_cast(index); + if (axis == 0) { + // Row: contiguous, stride 1 + return {data_ + idx * C, {C}, {size_t(1)}}; + } else { + // Column: strided, stride C + return {data_ + idx, {R}, {size_t(C)}}; + } + } + template + View select(size_t axis, I index) const + { + size_t idx = static_cast(index); + if (axis == 0) { + return {data_ + idx * C, {C}, {size_t(1)}}; + } else { + return {data_ + idx, {R}, {size_t(C)}}; + } + } + //! Flat view (1D, contiguous) View flat() { return {data_, {R * C}, {size_t(1)}}; } View flat() const { return {data_, {R * C}, {size_t(1)}}; } diff --git a/tests/cpp_unit_tests/test_tensor.cpp b/tests/cpp_unit_tests/test_tensor.cpp index 55f3fa9ff39..cc1b40ed4cd 100644 --- a/tests/cpp_unit_tests/test_tensor.cpp +++ b/tests/cpp_unit_tests/test_tensor.cpp @@ -777,6 +777,27 @@ TEST_CASE("StaticTensor2D iteration") REQUIRE(sum == 6); } +TEST_CASE("StaticTensor2D select") +{ + StaticTensor2D t; + t(0, 0) = 1; t(0, 1) = 2; + t(1, 0) = 3; t(1, 1) = 4; + t(2, 0) = 5; t(2, 1) = 6; + + // select(0, i) = row i + auto r1 = t.select(0, 1); + REQUIRE(r1.size() == 2); + REQUIRE(r1[0] == 3); + REQUIRE(r1[1] == 4); + + // select(1, j) = column j + auto c0 = t.select(1, 0); + REQUIRE(c0.size() == 3); + REQUIRE(c0[0] == 1); + REQUIRE(c0[1] == 3); + REQUIRE(c0[2] == 5); +} + TEST_CASE("StaticTensor2D flat view") { StaticTensor2D t; From 49a3474be26b415734dfe678f43432fe71a9ee68 Mon Sep 17 00:00:00 2001 From: John Tramm Date: Thu, 12 Feb 2026 15:53:21 -0600 Subject: [PATCH 41/51] refactored to allow for ranges. Much more rank agnostic now --- include/openmc/tensor.h | 259 +++++++++++++++------------ src/distribution_angle.cpp | 6 +- src/distribution_energy.cpp | 10 +- src/endf.cpp | 8 +- src/material.cpp | 4 +- src/mesh.cpp | 14 +- src/nuclide.cpp | 8 +- src/photon.cpp | 14 +- src/physics.cpp | 4 +- src/secondary_correlated.cpp | 16 +- src/secondary_kalbach.cpp | 14 +- src/secondary_thermal.cpp | 2 +- src/tallies/tally.cpp | 8 +- src/xsdata.cpp | 38 ++-- tests/cpp_unit_tests/test_tensor.cpp | 127 ++++++++----- 15 files changed, 302 insertions(+), 230 deletions(-) diff --git a/include/openmc/tensor.h b/include/openmc/tensor.h index 8efb8efdb4a..ed2e75e46ff 100644 --- a/include/openmc/tensor.h +++ b/include/openmc/tensor.h @@ -3,9 +3,13 @@ //! //! Tensor is the primary type: a dynamic-rank owning container that stores //! elements contiguously in row-major order. View is a lightweight -//! non-owning reference into a Tensor's storage, returned by methods like -//! select(), slice(), and flat(). StaticTensor2D is a small -//! stack-allocated 2D array used only for simulation::global_tallies. +//! non-owning reference into a Tensor's storage, returned by the slice() +//! method and flat(). StaticTensor2D is a small stack-allocated 2D +//! array used only for simulation::global_tallies. +//! +//! Slicing follows numpy conventions: each axis takes an index (rank-reducing), +//! All (keep entire axis), or Range (keep sub-range). For example, +//! arr.slice(0, all, range(2, 5)) is equivalent to numpy's arr[0, :, 2:5]. //! //! View is declared before Tensor because Tensor's methods return View objects. @@ -55,11 +59,108 @@ struct storage_type_map { template using storage_type = typename storage_type_map::type; +//============================================================================== +// Slice argument types +// +// Used with the variadic slice() method on Tensor, View, and StaticTensor2D. +// Each argument corresponds to one axis: a plain integer fixes that axis at +// a single index (rank-reducing), All keeps the entire axis, and Range keeps +// a sub-range. +//============================================================================== + +//! Keep an entire axis (equivalent to numpy's ':' or xtensor's xt::all()) +struct All {}; +constexpr All all {}; + +//! Sub-range along an axis [start, end) +struct Range { + size_t start; + size_t end; // SIZE_MAX means "to end of axis" +}; + +//! Create a Range [start, end) +inline Range range(size_t start, size_t end) { return {start, end}; } + +//! Create a Range [start, ) +inline Range range(size_t start) { return {start, SIZE_MAX}; } + +namespace detail { + +//! Internal: normalized representation of a per-axis slice argument +struct SliceArg { + enum Kind { INDEX, ALL, RANGE } kind; + size_t start; + size_t end; +}; + +inline SliceArg to_slice_arg(All) { return {SliceArg::ALL, 0, 0}; } +inline SliceArg to_slice_arg(Range r) { return {SliceArg::RANGE, r.start, r.end}; } + +template +inline typename std::enable_if< + std::is_integral::value || std::is_enum::value, SliceArg>::type +to_slice_arg(I i) +{ + return {SliceArg::INDEX, static_cast(i), 0}; +} + +//! Result of a slice computation: pointer offset + new shape/strides +struct SliceResult { + size_t ptr_offset; + vector shape; + vector strides; +}; + +//! Compute the result of applying slice arguments to shape/strides +template +SliceResult compute_slice( + const vector& shape, const vector& strides, + First first, Rest... rest) +{ + const size_t n = 1 + sizeof...(Rest); + SliceArg args[1 + sizeof...(Rest)] = { + to_slice_arg(first), to_slice_arg(rest)...}; + + size_t offset = 0; + vector new_shape; + vector new_strides; + + for (size_t a = 0; a < n; ++a) { + switch (args[a].kind) { + case SliceArg::INDEX: + offset += args[a].start * strides[a]; + break; + case SliceArg::ALL: + new_shape.push_back(shape[a]); + new_strides.push_back(strides[a]); + break; + case SliceArg::RANGE: { + offset += args[a].start * strides[a]; + size_t end = (args[a].end == SIZE_MAX) ? shape[a] : args[a].end; + new_shape.push_back(end - args[a].start); + new_strides.push_back(strides[a]); + break; + } + } + } + + // Trailing axes not covered by arguments are implicitly All. + // This matches numpy: a[i] on a 2D array returns a 1D row. + for (size_t a = n; a < shape.size(); ++a) { + new_shape.push_back(shape[a]); + new_strides.push_back(strides[a]); + } + + return {offset, std::move(new_shape), std::move(new_strides)}; +} + +} // namespace detail + //============================================================================== // View: a non-owning N-dimensional view into a tensor's storage. // // Holds a base pointer, shape, and strides (in elements). Supports arbitrary -// rank: 1D views for rows/slices, 2D views via select(), etc. +// rank and multi-axis slicing via the variadic slice() method. //============================================================================== template @@ -124,53 +225,23 @@ class View { //-------------------------------------------------------------------------- // View accessors - //! Fix one axis at a given index, returning an (N-1)-dimensional view - View select(size_t axis, size_t idx) + //! Multi-axis slice. Each argument corresponds to one axis and is either: + //! - an integer (fixes that axis, rank-reducing) + //! - All (keeps entire axis) + //! - Range (keeps sub-range along that axis) + //! Example: v.slice(0, all, range(2, 5)) == numpy v[0, :, 2:5] + template + View slice(First first, Rest... rest) { - vector new_shape; - vector new_strides; - new_shape.reserve(shape_.size() - 1); - new_strides.reserve(shape_.size() - 1); - T* new_data = data_ + idx * strides_[axis]; - for (size_t d = 0; d < shape_.size(); ++d) { - if (d != axis) { - new_shape.push_back(shape_[d]); - new_strides.push_back(strides_[d]); - } - } - return {new_data, std::move(new_shape), std::move(new_strides)}; + auto r = detail::compute_slice(shape_, strides_, first, rest...); + return {data_ + r.ptr_offset, std::move(r.shape), std::move(r.strides)}; } - View select(size_t axis, size_t idx) const + template + View slice(First first, Rest... rest) const { - vector new_shape; - vector new_strides; - new_shape.reserve(shape_.size() - 1); - new_strides.reserve(shape_.size() - 1); - const T* new_data = data_ + idx * strides_[axis]; - for (size_t d = 0; d < shape_.size(); ++d) { - if (d != axis) { - new_shape.push_back(shape_[d]); - new_strides.push_back(strides_[d]); - } - } - return {new_data, std::move(new_shape), std::move(new_strides)}; - } - - //! 1D subrange [start, end) - View slice(size_t start, size_t end) - { - return {data_ + start * strides_[0], {end - start}, {strides_[0]}}; - } - View slice(size_t start, size_t end) const - { - return {data_ + start * strides_[0], {end - start}, {strides_[0]}}; - } - - //! 1D subrange [start, size) - View slice(size_t start) - { - return {data_ + start * strides_[0], {shape_[0] - start}, {strides_[0]}}; + auto r = detail::compute_slice(shape_, strides_, first, rest...); + return {data_ + r.ptr_offset, std::move(r.shape), std::move(r.strides)}; } //-------------------------------------------------------------------------- @@ -537,58 +608,27 @@ class Tensor { // View accessors //! Fix one axis at a given index, returning an (N-1)-dimensional view - View select(size_t axis, size_t idx) + //! Multi-axis slice. Each argument corresponds to one axis and is either: + //! - an integer (fixes that axis, rank-reducing) + //! - All (keeps entire axis) + //! - Range (keeps sub-range along that axis) + //! Example: t.slice(0, all, range(2, 5)) == numpy t[0, :, 2:5] + template + View slice(First first, Rest... rest) { auto strides = compute_strides(); - vector new_shape; - vector new_strides; - new_shape.reserve(shape_.size() - 1); - new_strides.reserve(shape_.size() - 1); - stored_type* new_data = data_.data() + idx * strides[axis]; - for (size_t d = 0; d < shape_.size(); ++d) { - if (d != axis) { - new_shape.push_back(shape_[d]); - new_strides.push_back(strides[d]); - } - } - return {new_data, std::move(new_shape), std::move(new_strides)}; + auto r = detail::compute_slice(shape_, strides, first, rest...); + return {data_.data() + r.ptr_offset, std::move(r.shape), + std::move(r.strides)}; } - View select(size_t axis, size_t idx) const + template + View slice(First first, Rest... rest) const { auto strides = compute_strides(); - vector new_shape; - vector new_strides; - new_shape.reserve(shape_.size() - 1); - new_strides.reserve(shape_.size() - 1); - const stored_type* new_data = data_.data() + idx * strides[axis]; - for (size_t d = 0; d < shape_.size(); ++d) { - if (d != axis) { - new_shape.push_back(shape_[d]); - new_strides.push_back(strides[d]); - } - } - return {new_data, std::move(new_shape), std::move(new_strides)}; - } - - //! Subrange of a 1D tensor - View slice(size_t start, size_t end) - { - return {data_.data() + start, {end - start}, {size_t(1)}}; - } - View slice(size_t start, size_t end) const - { - return {data_.data() + start, {end - start}, {size_t(1)}}; - } - - //! Subrange to end of a 1D tensor - View slice(size_t start) - { - return {data_.data() + start, {data_.size() - start}, {size_t(1)}}; - } - View slice(size_t start) const - { - return {data_.data() + start, {data_.size() - start}, {size_t(1)}}; + auto r = detail::compute_slice(shape_, strides, first, rest...); + return {data_.data() + r.ptr_offset, std::move(r.shape), + std::move(r.strides)}; } //! Flat 1D view of all elements @@ -955,29 +995,22 @@ class StaticTensor2D { //-------------------------------------------------------------------------- // View accessors - //! Select along an axis: select(0, i) returns row i, select(1, j) returns - //! column j. - template - View select(size_t axis, I index) + //! Multi-axis slice (same interface as Tensor/View). + template + View slice(First first, Rest... rest) { - size_t idx = static_cast(index); - if (axis == 0) { - // Row: contiguous, stride 1 - return {data_ + idx * C, {C}, {size_t(1)}}; - } else { - // Column: strided, stride C - return {data_ + idx, {R}, {size_t(C)}}; - } + vector sh = {R, C}; + vector st = {C, 1}; + auto r = detail::compute_slice(sh, st, first, rest...); + return {data_ + r.ptr_offset, std::move(r.shape), std::move(r.strides)}; } - template - View select(size_t axis, I index) const + template + View slice(First first, Rest... rest) const { - size_t idx = static_cast(index); - if (axis == 0) { - return {data_ + idx * C, {C}, {size_t(1)}}; - } else { - return {data_ + idx, {R}, {size_t(C)}}; - } + vector sh = {R, C}; + vector st = {C, 1}; + auto r = detail::compute_slice(sh, st, first, rest...); + return {data_ + r.ptr_offset, std::move(r.shape), std::move(r.strides)}; } //! Flat view (1D, contiguous) diff --git a/src/distribution_angle.cpp b/src/distribution_angle.cpp index 74f8e3f6d7b..3433f269e35 100644 --- a/src/distribution_angle.cpp +++ b/src/distribution_angle.cpp @@ -44,9 +44,9 @@ AngleDistribution::AngleDistribution(hid_t group) } // Create and initialize tabular distribution - tensor::View xs = temp.select(0, 0).slice(j, j + n); - tensor::View ps = temp.select(0, 1).slice(j, j + n); - tensor::View cs = temp.select(0, 2).slice(j, j + n); + tensor::View xs = temp.slice(0, tensor::range(j, j + n)); + tensor::View ps = temp.slice(1, tensor::range(j, j + n)); + tensor::View cs = temp.slice(2, tensor::range(j, j + n)); vector x {xs.begin(), xs.end()}; vector p {ps.begin(), ps.end()}; vector c {cs.begin(), cs.end()}; diff --git a/src/distribution_energy.cpp b/src/distribution_energy.cpp index 9414f22ae2f..2f8e6cf1a99 100644 --- a/src/distribution_energy.cpp +++ b/src/distribution_energy.cpp @@ -63,8 +63,8 @@ ContinuousTabular::ContinuousTabular(hid_t group) tensor::Tensor temp; read_attribute(dset, "interpolation", temp); - tensor::View temp_b = temp.select(0, 0); // breakpoints - tensor::View temp_i = temp.select(0, 1); // interpolation parameters + tensor::View temp_b = temp.slice(0); // breakpoints + tensor::View temp_i = temp.slice(1); // interpolation parameters std::copy(temp_b.begin(), temp_b.end(), std::back_inserter(breakpoints_)); for (const auto i : temp_i) @@ -105,15 +105,15 @@ ContinuousTabular::ContinuousTabular(hid_t group) d.n_discrete = n_discrete[i]; // Copy data - d.e_out = eout.select(0, 0).slice(j, j + n); - d.p = eout.select(0, 1).slice(j, j + n); + d.e_out = eout.slice(0, tensor::range(j, j + n)); + d.p = eout.slice(1, tensor::range(j, j + n)); // To get answers that match ACE data, for now we still use the tabulated // CDF values that were passed through to the HDF5 library. At a later // time, we can remove the CDF values from the HDF5 library and // reconstruct them using the PDF if (true) { - d.c = eout.select(0, 2).slice(j, j + n); + d.c = eout.slice(2, tensor::range(j, j + n)); } else { // Calculate cumulative distribution function -- discrete portion for (int k = 0; k < d.n_discrete; ++k) { diff --git a/src/endf.cpp b/src/endf.cpp index 65668ea38f7..b298ebe01f9 100644 --- a/src/endf.cpp +++ b/src/endf.cpp @@ -155,8 +155,8 @@ Tabulated1D::Tabulated1D(hid_t dset) tensor::Tensor arr; read_dataset(dset, arr); - tensor::View xs = arr.select(0, 0); - tensor::View ys = arr.select(0, 1); + tensor::View xs = arr.slice(0); + tensor::View ys = arr.slice(1); std::copy(xs.begin(), xs.end(), std::back_inserter(x_)); std::copy(ys.begin(), ys.end(), std::back_inserter(y_)); @@ -232,8 +232,8 @@ CoherentElasticXS::CoherentElasticXS(hid_t dset) read_dataset(dset, arr); // Get views for Bragg edges and structure factors - tensor::View E = arr.select(0, 0); - tensor::View s = arr.select(0, 1); + tensor::View E = arr.slice(0); + tensor::View s = arr.slice(1); // Copy Bragg edges and partial sums of structure factors std::copy(E.begin(), E.end(), std::back_inserter(bragg_edges_)); diff --git a/src/material.cpp b/src/material.cpp index cd8c31f5698..2b586c815f4 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -696,7 +696,7 @@ void Material::init_bremsstrahlung() 1.0595e-3 * std::pow(t, 5) + 7.0568e-5 * std::pow(t, 6) - 1.808e-6 * std::pow(t, 7)); stopping_power_radiative(i) *= r; - tensor::View dcs_i = dcs.select(0, i); + tensor::View dcs_i = dcs.slice(i); dcs_i *= r; } } @@ -1181,7 +1181,7 @@ void Material::add_nuclide(const std::string& name, double density) // Create copy of atom_density_ array with one extra entry tensor::Tensor atom_density = tensor::zeros({n}); - atom_density.slice(0, n - 1) = atom_density_; + atom_density.slice(tensor::range(0, n - 1)) = atom_density_; atom_density(n - 1) = density; atom_density_ = atom_density; diff --git a/src/mesh.cpp b/src/mesh.cpp index 27fe29d613f..a611d4a168a 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -958,7 +958,7 @@ void UnstructuredMesh::to_hdf5_inner(hid_t mesh_group) const {static_cast(this->n_vertices()), static_cast(3)}); for (int i = 0; i < this->n_vertices(); i++) { auto v = this->vertex(i); - vertices.select(0, i) = {v.x, v.y, v.z}; + vertices.slice(i) = {v.x, v.y, v.z}; } write_dataset(mesh_group, "vertices", vertices); @@ -977,18 +977,18 @@ void UnstructuredMesh::to_hdf5_inner(hid_t mesh_group) const // write linear tet element if (conn.size() == 4) { - elem_types.select(0, i) = static_cast(ElementType::LINEAR_TET); - connectivity.select(0, i) = { + elem_types.slice(i) = static_cast(ElementType::LINEAR_TET); + connectivity.slice(i) = { conn[0], conn[1], conn[2], conn[3], -1, -1, -1, -1}; // write linear hex element } else if (conn.size() == 8) { - elem_types.select(0, i) = static_cast(ElementType::LINEAR_HEX); - connectivity.select(0, i) = { + elem_types.slice(i) = static_cast(ElementType::LINEAR_HEX); + connectivity.slice(i) = { conn[0], conn[1], conn[2], conn[3], conn[4], conn[5], conn[6], conn[7]}; } else { num_elem_skipped++; - elem_types.select(0, i) = static_cast(ElementType::UNSUPPORTED); - connectivity.select(0, i) = -1; + elem_types.slice(i) = static_cast(ElementType::UNSUPPORTED); + connectivity.slice(i) = -1; } } diff --git a/src/nuclide.cpp b/src/nuclide.cpp index 89c0ab3a008..c8433b33a8a 100644 --- a/src/nuclide.cpp +++ b/src/nuclide.cpp @@ -403,17 +403,17 @@ void Nuclide::create_derived( continue; // Add contribution to total cross section - xs_[t].select(1, XS_TOTAL).slice(j, j + n) += xs; + xs_[t].slice(tensor::range(j, j + n), XS_TOTAL) += xs; // Add contribution to absorption cross section if (is_disappearance(rx->mt_)) { - xs_[t].select(1, XS_ABSORPTION).slice(j, j + n) += xs; + xs_[t].slice(tensor::range(j, j + n), XS_ABSORPTION) += xs; } if (is_fission(rx->mt_)) { fissionable_ = true; - xs_[t].select(1, XS_FISSION).slice(j, j + n) += xs; - xs_[t].select(1, XS_ABSORPTION).slice(j, j + n) += xs; + xs_[t].slice(tensor::range(j, j + n), XS_FISSION) += xs; + xs_[t].slice(tensor::range(j, j + n), XS_ABSORPTION) += xs; // Keep track of fission reactions if (t == 0) { diff --git a/src/photon.cpp b/src/photon.cpp index 1fc9eb5cb09..b7983a0d222 100644 --- a/src/photon.cpp +++ b/src/photon.cpp @@ -169,7 +169,7 @@ PhotonInteraction::PhotonInteraction(hid_t group) read_dataset(tgroup, "xs", xs); auto cross_section = - cross_sections_.select(1, i).slice(static_cast(shell.threshold)); + cross_sections_.slice(tensor::range(static_cast(shell.threshold)), i); cross_section = tensor::where(xs > 0, tensor::log(xs), 0); if (object_exists(tgroup, "transitions")) { @@ -184,7 +184,7 @@ PhotonInteraction::PhotonInteraction(hid_t group) read_dataset(tgroup, "transitions", matrix); // Transition probability normalization - double norm = tensor::Tensor(matrix.select(1, 3)).sum(); + double norm = tensor::Tensor(matrix.slice(tensor::all, 3)).sum(); shell.transitions.resize(n_transition); for (int j = 0; j < n_transition; ++j) { @@ -304,7 +304,7 @@ PhotonInteraction::PhotonInteraction(hid_t group) double y = std::exp( std::log(dcs_(i_grid, i)) + f * (std::log(dcs_(i_grid + 1, i)) - std::log(dcs_(i_grid, i)))); - tensor::View col_i = dcs.select(1, i); + tensor::View col_i = dcs.slice(tensor::all, i); col_i(0) = y; for (int j = i_grid + 1; j < n_e; ++j) { col_i(j - i_grid) = dcs_(j, i); @@ -314,7 +314,7 @@ PhotonInteraction::PhotonInteraction(hid_t group) tensor::Tensor frst({static_cast(1)}); frst(0) = cutoff; - tensor::Tensor rest(electron_energy.slice(i_grid + 1)); + tensor::Tensor rest(electron_energy.slice(tensor::range(i_grid + 1))); electron_energy = tensor::concatenate(frst, rest); } @@ -509,7 +509,7 @@ void PhotonInteraction::compton_doppler( c = prn(seed) * c_max; // Determine pz corresponding to sampled cdf value - tensor::View cdf_shell = profile_cdf_.select(0, shell); + tensor::View cdf_shell = profile_cdf_.slice(shell); int i = lower_bound_index(cdf_shell.cbegin(), cdf_shell.cend(), c); double pz_l = data::compton_profile_pz(i); double pz_r = data::compton_profile_pz(i + 1); @@ -605,8 +605,8 @@ void PhotonInteraction::calculate_xs(Particle& p) const // Calculate microscopic photoelectric cross section xs.photoelectric = 0.0; - tensor::View xs_lower = cross_sections_.select(0, i_grid); - tensor::View xs_upper = cross_sections_.select(0, i_grid + 1); + tensor::View xs_lower = cross_sections_.slice(i_grid); + tensor::View xs_upper = cross_sections_.slice(i_grid + 1); for (int i = 0; i < xs_upper.size(); ++i) if (xs_lower(i) != 0) diff --git a/src/physics.cpp b/src/physics.cpp index 871a9195158..54fe00af7b2 100644 --- a/src/physics.cpp +++ b/src/physics.cpp @@ -376,9 +376,9 @@ void sample_photon_reaction(Particle& p) int i_grid = micro.index_grid; double f = micro.interp_factor; tensor::View xs_lower = - element.cross_sections_.select(0, i_grid); + element.cross_sections_.slice(i_grid); tensor::View xs_upper = - element.cross_sections_.select(0, i_grid + 1); + element.cross_sections_.slice(i_grid + 1); for (int i_shell = 0; i_shell < element.shells_.size(); ++i_shell) { const auto& shell {element.shells_[i_shell]}; diff --git a/src/secondary_correlated.cpp b/src/secondary_correlated.cpp index d3d01908dce..b4d5e10d5ab 100644 --- a/src/secondary_correlated.cpp +++ b/src/secondary_correlated.cpp @@ -28,8 +28,8 @@ CorrelatedAngleEnergy::CorrelatedAngleEnergy(hid_t group) tensor::Tensor temp; read_attribute(dset, "interpolation", temp); - tensor::View temp_b = temp.select(0, 0); // breakpoints - tensor::View temp_i = temp.select(0, 1); // interpolation parameters + tensor::View temp_b = temp.slice(0); // breakpoints + tensor::View temp_i = temp.slice(1); // interpolation parameters std::copy(temp_b.begin(), temp_b.end(), std::back_inserter(breakpoints_)); for (const auto i : temp_i) @@ -74,9 +74,9 @@ CorrelatedAngleEnergy::CorrelatedAngleEnergy(hid_t group) d.n_discrete = n_discrete[i]; // Copy data - d.e_out = eout.select(0, 0).slice(j, j + n); - d.p = eout.select(0, 1).slice(j, j + n); - d.c = eout.select(0, 2).slice(j, j + n); + d.e_out = eout.slice(0, tensor::range(j, j + n)); + d.p = eout.slice(1, tensor::range(j, j + n)); + d.c = eout.slice(2, tensor::range(j, j + n)); // To get answers that match ACE data, for now we still use the tabulated // CDF values that were passed through to the HDF5 library. At a later @@ -132,9 +132,9 @@ CorrelatedAngleEnergy::CorrelatedAngleEnergy(hid_t group) interp_mu = 1; auto interp = int2interp(interp_mu); - tensor::View xs = mu.select(0, 0).slice(offset_mu, offset_mu + m); - tensor::View ps = mu.select(0, 1).slice(offset_mu, offset_mu + m); - tensor::View cs = mu.select(0, 2).slice(offset_mu, offset_mu + m); + tensor::View xs = mu.slice(0, tensor::range(offset_mu, offset_mu + m)); + tensor::View ps = mu.slice(1, tensor::range(offset_mu, offset_mu + m)); + tensor::View cs = mu.slice(2, tensor::range(offset_mu, offset_mu + m)); vector x {xs.begin(), xs.end()}; vector p {ps.begin(), ps.end()}; diff --git a/src/secondary_kalbach.cpp b/src/secondary_kalbach.cpp index 24d0fce4e95..8470c8c18e6 100644 --- a/src/secondary_kalbach.cpp +++ b/src/secondary_kalbach.cpp @@ -29,8 +29,8 @@ KalbachMann::KalbachMann(hid_t group) tensor::Tensor temp; read_attribute(dset, "interpolation", temp); - tensor::View temp_b = temp.select(0, 0); // breakpoints - tensor::View temp_i = temp.select(0, 1); // interpolation parameters + tensor::View temp_b = temp.slice(0); // breakpoints + tensor::View temp_i = temp.slice(1); // interpolation parameters std::copy(temp_b.begin(), temp_b.end(), std::back_inserter(breakpoints_)); for (const auto i : temp_i) @@ -71,11 +71,11 @@ KalbachMann::KalbachMann(hid_t group) d.n_discrete = n_discrete[i]; // Copy data - d.e_out = eout.select(0, 0).slice(j, j + n); - d.p = eout.select(0, 1).slice(j, j + n); - d.c = eout.select(0, 2).slice(j, j + n); - d.r = eout.select(0, 3).slice(j, j + n); - d.a = eout.select(0, 4).slice(j, j + n); + d.e_out = eout.slice(0, tensor::range(j, j + n)); + d.p = eout.slice(1, tensor::range(j, j + n)); + d.c = eout.slice(2, tensor::range(j, j + n)); + d.r = eout.slice(3, tensor::range(j, j + n)); + d.a = eout.slice(4, tensor::range(j, j + n)); // To get answers that match ACE data, for now we still use the tabulated // CDF values that were passed through to the HDF5 library. At a later diff --git a/src/secondary_thermal.cpp b/src/secondary_thermal.cpp index f3145a5d6d8..2ab7d8a63e1 100644 --- a/src/secondary_thermal.cpp +++ b/src/secondary_thermal.cpp @@ -222,7 +222,7 @@ IncoherentInelasticAE::IncoherentInelasticAE(hid_t group) } // Copy outgoing angles - tensor::View mu_j = d.mu.select(0, j); + tensor::View mu_j = d.mu.slice(j); std::copy(adist->x().begin(), adist->x().end(), mu_j.begin()); } } diff --git a/src/tallies/tally.cpp b/src/tallies/tally.cpp index 712c208d5c3..5bc121378ac 100644 --- a/src/tallies/tally.cpp +++ b/src/tallies/tally.cpp @@ -1007,7 +1007,7 @@ void reduce_tally_results() // Extract 2D view of the VALUE column from the 3D results tensor, // then copy into a contiguous array for MPI reduction const int val_idx = static_cast(TallyResult::VALUE); - tensor::View val_view = tally->results_.select(2, val_idx); + tensor::View val_view = tally->results_.slice(tensor::all, tensor::all, val_idx); tensor::Tensor values(val_view); tensor::Tensor values_reduced(values.shape()); @@ -1033,7 +1033,7 @@ void reduce_tally_results() const int val_col = static_cast(TallyResult::VALUE); // Copy VALUE column into contiguous array for MPI reduction - tensor::Tensor gt_values(gt.select(1, val_col)); + tensor::Tensor gt_values(gt.slice(tensor::all, val_col)); tensor::Tensor gt_values_reduced({size_t {N_GLOBAL_TALLIES}}); // Reduce contiguous data @@ -1042,9 +1042,9 @@ void reduce_tally_results() // Transfer values on master and reset on other ranks if (mpi::master) { - gt.select(1, val_col) = gt_values_reduced; + gt.slice(tensor::all, val_col) = gt_values_reduced; } else { - gt.select(1, val_col) = 0.0; + gt.slice(tensor::all, val_col) = 0.0; } // We also need to determine the total starting weight of particles from the diff --git a/src/xsdata.cpp b/src/xsdata.cpp index 9e05e4f7557..b4ee219c7b7 100644 --- a/src/xsdata.cpp +++ b/src/xsdata.cpp @@ -134,7 +134,7 @@ void XsData::fission_vector_beta_from_hdf5( // Normalize chi so it sums to 1 over outgoing groups for each angle for (size_t a = 0; a < n_ang; a++) { - tensor::View row = temp_chi.select(0, a); + tensor::View row = temp_chi.slice(a); row /= row.sum(); } @@ -142,14 +142,14 @@ void XsData::fission_vector_beta_from_hdf5( // spectrum is independent of the incoming neutron energy for (size_t a = 0; a < n_ang; a++) for (size_t gin = 0; gin < n_g_; gin++) - chi_prompt.select(0, a).select(0, gin) = temp_chi.select(0, a); + chi_prompt.slice(a, gin) = temp_chi.slice(a); // Same spectrum for delayed neutrons, replicated across delayed groups for (size_t a = 0; a < n_ang; a++) for (size_t d = 0; d < n_dg_; d++) for (size_t gin = 0; gin < n_g_; gin++) - chi_delayed.select(0, a).select(0, d).select(0, gin) = - temp_chi.select(0, a); + chi_delayed.slice(a, d, gin) = + temp_chi.slice(a); // Get nu-fission tensor::Tensor temp_nufiss({n_ang, n_g_}, 0.); @@ -208,7 +208,7 @@ void XsData::fission_vector_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) // Normalize prompt chi so it sums to 1 over outgoing groups for each angle for (size_t a = 0; a < n_ang; a++) { - tensor::View row = temp_chi_p.select(0, a); + tensor::View row = temp_chi_p.slice(a); row /= row.sum(); } @@ -220,21 +220,21 @@ void XsData::fission_vector_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) // angle and delayed group for (size_t a = 0; a < n_ang; a++) for (size_t d = 0; d < n_dg_; d++) { - tensor::View row = temp_chi_d.select(0, a).select(0, d); + tensor::View row = temp_chi_d.slice(a, d); row /= row.sum(); } // Replicate the prompt spectrum across all incoming groups for (size_t a = 0; a < n_ang; a++) for (size_t gin = 0; gin < n_g_; gin++) - chi_prompt.select(0, a).select(0, gin) = temp_chi_p.select(0, a); + chi_prompt.slice(a, gin) = temp_chi_p.slice(a); // Replicate the delayed spectrum across all incoming groups for (size_t a = 0; a < n_ang; a++) for (size_t d = 0; d < n_dg_; d++) for (size_t gin = 0; gin < n_g_; gin++) - chi_delayed.select(0, a).select(0, d).select(0, gin) = - temp_chi_d.select(0, a).select(0, d); + chi_delayed.slice(a, d, gin) = + temp_chi_d.slice(a, d); // Get prompt and delayed nu-fission directly read_nd_tensor(xsdata_grp, "prompt-nu-fission", prompt_nu_fission, true); @@ -252,14 +252,14 @@ void XsData::fission_vector_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang) // Normalize chi so it sums to 1 over outgoing groups for each angle for (size_t a = 0; a < n_ang; a++) { - tensor::View row = temp_chi.select(0, a); + tensor::View row = temp_chi.slice(a); row /= row.sum(); } // Replicate the energy spectrum across all incoming groups for (size_t a = 0; a < n_ang; a++) for (size_t gin = 0; gin < n_g_; gin++) - chi_prompt.select(0, a).select(0, gin) = temp_chi.select(0, a); + chi_prompt.slice(a, gin) = temp_chi.slice(a); // Get nu-fission directly read_nd_tensor(xsdata_grp, "nu-fission", prompt_nu_fission, true); @@ -358,7 +358,7 @@ void XsData::fission_matrix_beta_from_hdf5( // Normalize chi_prompt so it sums to 1 over outgoing groups for (size_t a = 0; a < n_ang; a++) for (size_t gin = 0; gin < n_g_; gin++) { - tensor::View row = chi_prompt.select(0, a).select(0, gin); + tensor::View row = chi_prompt.slice(a, gin); row /= row.sum(); } @@ -367,7 +367,7 @@ void XsData::fission_matrix_beta_from_hdf5( for (size_t d = 0; d < n_dg_; d++) for (size_t gin = 0; gin < n_g_; gin++) { tensor::View row = - chi_delayed.select(0, a).select(0, d).select(0, gin); + chi_delayed.slice(a, d, gin); row /= row.sum(); } } @@ -558,8 +558,8 @@ void XsData::scatter_from_hdf5(hid_t xsdata_grp, size_t n_ang, final_scatter_format == AngleDistributionType::TABULAR) { for (size_t a = 0; a < n_ang; a++) { ScattDataLegendre legendre_scatt; - tensor::Tensor in_gmin(gmin.select(0, a)); - tensor::Tensor in_gmax(gmax.select(0, a)); + tensor::Tensor in_gmin(gmin.slice(a)); + tensor::Tensor in_gmax(gmax.slice(a)); legendre_scatt.init(in_gmin, in_gmax, temp_mult[a], input_scatt[a]); @@ -573,8 +573,8 @@ void XsData::scatter_from_hdf5(hid_t xsdata_grp, size_t n_ang, // We are sticking with the current representation // Initialize the ScattData object with this data for (size_t a = 0; a < n_ang; a++) { - tensor::Tensor in_gmin(gmin.select(0, a)); - tensor::Tensor in_gmax(gmax.select(0, a)); + tensor::Tensor in_gmin(gmin.slice(a)); + tensor::Tensor in_gmax(gmax.slice(a)); scatter[a]->init(in_gmin, in_gmax, temp_mult[a], input_scatt[a]); } } @@ -638,7 +638,7 @@ void XsData::combine( size_t n_g = chi_prompt.shape(1); for (size_t a = 0; a < n_ang; a++) for (size_t gin = 0; gin < n_g; gin++) { - tensor::View row = chi_prompt.select(0, a).select(0, gin); + tensor::View row = chi_prompt.slice(a, gin); row /= row.sum(); } } @@ -651,7 +651,7 @@ void XsData::combine( for (size_t d = 0; d < n_dg; d++) for (size_t gin = 0; gin < n_g; gin++) { tensor::View row = - chi_delayed.select(0, a).select(0, d).select(0, gin); + chi_delayed.slice(a, d, gin); row /= row.sum(); } } diff --git a/tests/cpp_unit_tests/test_tensor.cpp b/tests/cpp_unit_tests/test_tensor.cpp index cc1b40ed4cd..5a6aa125821 100644 --- a/tests/cpp_unit_tests/test_tensor.cpp +++ b/tests/cpp_unit_tests/test_tensor.cpp @@ -475,7 +475,7 @@ TEST_CASE("Tensor storage") // View (via Tensor accessors) // ============================================================================ -TEST_CASE("Tensor select axis 0 (2D)") +TEST_CASE("Tensor slice axis 0 (2D)") { // [[1, 2, 3], [4, 5, 6]] Tensor t({2, 3}, 0); @@ -484,13 +484,13 @@ TEST_CASE("Tensor select axis 0 (2D)") for (size_t j = 0; j < 3; ++j) t(i, j) = v++; - auto r0 = t.select(0, 0); + auto r0 = t.slice(0); REQUIRE(r0.size() == 3); REQUIRE(r0[0] == 1); REQUIRE(r0[1] == 2); REQUIRE(r0[2] == 3); - auto r1 = t.select(0, 1); + auto r1 = t.slice(1); REQUIRE(r1[0] == 4); REQUIRE(r1[1] == 5); REQUIRE(r1[2] == 6); @@ -500,7 +500,7 @@ TEST_CASE("Tensor select axis 0 (2D)") REQUIRE(t(0, 1) == 99); } -TEST_CASE("Tensor select axis 1 (2D)") +TEST_CASE("Tensor slice axis 1 (2D)") { // [[1, 2], [3, 4], [5, 6]] Tensor t({3, 2}, 0); @@ -511,13 +511,13 @@ TEST_CASE("Tensor select axis 1 (2D)") t(2, 0) = 5; t(2, 1) = 6; - auto c0 = t.select(1, 0); + auto c0 = t.slice(all, 0); REQUIRE(c0.size() == 3); REQUIRE(c0[0] == 1); REQUIRE(c0[1] == 3); REQUIRE(c0[2] == 5); - auto c1 = t.select(1, 1); + auto c1 = t.slice(all, 1); REQUIRE(c1[0] == 2); REQUIRE(c1[1] == 4); REQUIRE(c1[2] == 6); @@ -527,20 +527,20 @@ TEST_CASE("Tensor select axis 1 (2D)") REQUIRE(t(0, 1) == 77); } -TEST_CASE("Tensor slice view") +TEST_CASE("Tensor slice with range") { Tensor t({6}, 0); t = {10, 20, 30, 40, 50, 60}; - // slice(start, end) - auto s = t.slice(1, 4); + // range(start, end) + auto s = t.slice(range(1, 4)); REQUIRE(s.size() == 3); REQUIRE(s[0] == 20); REQUIRE(s[1] == 30); REQUIRE(s[2] == 40); - // slice(start) to end - auto s2 = t.slice(3); + // range(start) to end + auto s2 = t.slice(range(3)); REQUIRE(s2.size() == 3); REQUIRE(s2[0] == 40); REQUIRE(s2[2] == 60); @@ -564,7 +564,7 @@ TEST_CASE("Tensor flat view") REQUIRE(f[5] == 6); } -TEST_CASE("Tensor select (general axis)") +TEST_CASE("Tensor slice on 3D") { // 2x3x4 tensor Tensor t({2, 3, 4}, 0); @@ -574,16 +574,16 @@ TEST_CASE("Tensor select (general axis)") for (size_t k = 0; k < 4; ++k) t(i, j, k) = v++; - // select(0, 1) -> fix first axis at 1 -> 3x4 view - auto s = t.select(0, 1); + // slice(1) -> fix axis 0 at 1 -> 3x4 view + auto s = t.slice(1); REQUIRE(s.size() == 12); // t(1,0,0) = 12, t(1,0,1) = 13, ... REQUIRE(s(0, 0) == 12); REQUIRE(s(0, 1) == 13); REQUIRE(s(2, 3) == 23); - // select(1, 2) -> fix second axis at 2 -> 2x4 view - auto s2 = t.select(1, 2); + // slice(all, 2) -> fix axis 1 at 2 -> 2x4 view + auto s2 = t.slice(all, 2); REQUIRE(s2.size() == 8); // t(0,2,0)=8, t(0,2,1)=9, t(1,2,0)=20 REQUIRE(s2(0, 0) == 8); @@ -591,6 +591,45 @@ TEST_CASE("Tensor select (general axis)") REQUIRE(s2(1, 0) == 20); } +TEST_CASE("Tensor multi-axis slice") +{ + // 2x3x4 tensor with sequential values + Tensor t({2, 3, 4}, 0); + int v = 0; + for (size_t i = 0; i < 2; ++i) + for (size_t j = 0; j < 3; ++j) + for (size_t k = 0; k < 4; ++k) + t(i, j, k) = v++; + + // slice(1, 2) -> fix axes 0 and 1 -> 1D view of 4 elements + // Equivalent to numpy t[1, 2, :] -> t(1,2,0..3) = [20, 21, 22, 23] + auto s = t.slice(1, 2); + REQUIRE(s.size() == 4); + REQUIRE(s[0] == 20); + REQUIRE(s[1] == 21); + REQUIRE(s[3] == 23); + + // slice(all, 1, range(1, 3)) -> keep axis 0, fix axis 1, range on axis 2 + // Equivalent to numpy t[:, 1, 1:3] + // t(0,1,1)=5, t(0,1,2)=6, t(1,1,1)=17, t(1,1,2)=18 + auto s2 = t.slice(all, 1, range(1, 3)); + REQUIRE(s2.size() == 4); + REQUIRE(s2.ndim() == 2); + REQUIRE(s2(0, 0) == 5); + REQUIRE(s2(0, 1) == 6); + REQUIRE(s2(1, 0) == 17); + REQUIRE(s2(1, 1) == 18); + + // slice(0, range(0, 2)) -> fix axis 0 at 0, range on axis 1 + // Equivalent to numpy t[0, 0:2, :] -> shape (2, 4) + auto s3 = t.slice(0, range(0, 2)); + REQUIRE(s3.ndim() == 2); + REQUIRE(s3.shape(0) == 2); + REQUIRE(s3.shape(1) == 4); + REQUIRE(s3(0, 0) == 0); // t(0,0,0) + REQUIRE(s3(1, 3) == 7); // t(0,1,3) +} + // ============================================================================ // View assignment and arithmetic // ============================================================================ @@ -598,7 +637,7 @@ TEST_CASE("Tensor select (general axis)") TEST_CASE("View scalar assignment (fill)") { Tensor t({2, 3}, 0.0); - auto r = t.select(0, 0); + auto r = t.slice(0); r = 7.0; REQUIRE(t(0, 0) == 7.0); REQUIRE(t(0, 1) == 7.0); @@ -609,7 +648,7 @@ TEST_CASE("View scalar assignment (fill)") TEST_CASE("View initializer_list assignment") { Tensor t({2, 3}, 0.0); - auto r = t.select(0, 1); + auto r = t.slice(1); r = {10.0, 20.0, 30.0}; REQUIRE(t(1, 0) == 10.0); REQUIRE(t(1, 1) == 20.0); @@ -619,11 +658,11 @@ TEST_CASE("View initializer_list assignment") TEST_CASE("View copy assignment (deep copy)") { Tensor t({2, 3}, 0.0); - t.select(0, 0) = {1.0, 2.0, 3.0}; - t.select(0, 1) = {4.0, 5.0, 6.0}; + t.slice(0) = {1.0, 2.0, 3.0}; + t.slice(1) = {4.0, 5.0, 6.0}; // Copy row 0 into row 1 - t.select(0, 1) = t.select(0, 0); + t.slice(1) = t.slice(0); REQUIRE(t(1, 0) == 1.0); REQUIRE(t(1, 1) == 2.0); REQUIRE(t(1, 2) == 3.0); @@ -632,13 +671,13 @@ TEST_CASE("View copy assignment (deep copy)") TEST_CASE("View compound operators") { Tensor t({2, 3}, 0.0); - t.select(0, 0) = {1.0, 2.0, 3.0}; + t.slice(0) = {1.0, 2.0, 3.0}; - t.select(0, 0) *= 2.0; + t.slice(0) *= 2.0; REQUIRE(t(0, 0) == 2.0); REQUIRE(t(0, 1) == 4.0); - t.select(0, 0) /= 2.0; + t.slice(0) /= 2.0; REQUIRE(t(0, 0) == 1.0); REQUIRE(t(0, 1) == 2.0); } @@ -649,7 +688,7 @@ TEST_CASE("View assignment from tensor") Tensor vals({3}, 0.0); vals = {7.0, 8.0, 9.0}; - t.select(0, 1) = vals; + t.slice(1) = vals; REQUIRE(t(1, 0) == 7.0); REQUIRE(t(1, 1) == 8.0); REQUIRE(t(1, 2) == 9.0); @@ -658,11 +697,11 @@ TEST_CASE("View assignment from tensor") TEST_CASE("View compound addition from tensor") { Tensor t({2, 3}, 0.0); - t.select(0, 0) = {1.0, 2.0, 3.0}; + t.slice(0) = {1.0, 2.0, 3.0}; Tensor vals({3}, 0.0); vals = {10.0, 20.0, 30.0}; - t.select(0, 0) += vals; + t.slice(0) += vals; REQUIRE(t(0, 0) == 11.0); REQUIRE(t(0, 1) == 22.0); REQUIRE(t(0, 2) == 33.0); @@ -671,20 +710,20 @@ TEST_CASE("View compound addition from tensor") TEST_CASE("View sum") { Tensor t({2, 3}, 0.0); - t.select(0, 0) = {1.0, 2.0, 3.0}; - t.select(0, 1) = {4.0, 5.0, 6.0}; + t.slice(0) = {1.0, 2.0, 3.0}; + t.slice(1) = {4.0, 5.0, 6.0}; - REQUIRE(t.select(0, 0).sum() == 6.0); - REQUIRE(t.select(0, 1).sum() == 15.0); + REQUIRE(t.slice(0).sum() == 6.0); + REQUIRE(t.slice(1).sum() == 15.0); } TEST_CASE("View iteration") { Tensor t({2, 3}, 0); - t.select(0, 0) = {1, 2, 3}; + t.slice(0) = {1, 2, 3}; int sum = 0; - for (auto val : t.select(0, 0)) + for (auto val : t.slice(0)) sum += val; REQUIRE(sum == 6); } @@ -694,8 +733,8 @@ TEST_CASE("View sub-slice") Tensor t({6}, 0); t = {10, 20, 30, 40, 50, 60}; - auto s = t.slice(1, 5); // [20, 30, 40, 50] - auto ss = s.slice(1, 3); // [30, 40] + auto s = t.slice(range(1, 5)); // [20, 30, 40, 50] + auto ss = s.slice(range(1, 3)); // [30, 40] REQUIRE(ss.size() == 2); REQUIRE(ss[0] == 30); REQUIRE(ss[1] == 40); @@ -704,10 +743,10 @@ TEST_CASE("View sub-slice") TEST_CASE("Tensor from View") { Tensor t({2, 3}, 0.0); - t.select(0, 0) = {1.0, 2.0, 3.0}; + t.slice(0) = {1.0, 2.0, 3.0}; // Construct a new tensor from a view (copies data) - Tensor t2(t.select(0, 0)); + Tensor t2(t.slice(0)); REQUIRE(t2.size() == 3); REQUIRE(t2[0] == 1.0); REQUIRE(t2[2] == 3.0); @@ -730,11 +769,11 @@ TEST_CASE("Const tensor produces const views") t(i, j) = v++; const Tensor& ct = t; - auto r = ct.select(0, 0); // View + auto r = ct.slice(0); // View REQUIRE(r[0] == 1.0); REQUIRE(r[2] == 3.0); - auto c = ct.select(1, 1); + auto c = ct.slice(all, 1); REQUIRE(c[0] == 2.0); REQUIRE(c[1] == 5.0); } @@ -777,21 +816,21 @@ TEST_CASE("StaticTensor2D iteration") REQUIRE(sum == 6); } -TEST_CASE("StaticTensor2D select") +TEST_CASE("StaticTensor2D slice") { StaticTensor2D t; t(0, 0) = 1; t(0, 1) = 2; t(1, 0) = 3; t(1, 1) = 4; t(2, 0) = 5; t(2, 1) = 6; - // select(0, i) = row i - auto r1 = t.select(0, 1); + // slice(1) = row 1 (fix axis 0 at 1) + auto r1 = t.slice(1); REQUIRE(r1.size() == 2); REQUIRE(r1[0] == 3); REQUIRE(r1[1] == 4); - // select(1, j) = column j - auto c0 = t.select(1, 0); + // slice(all, 0) = column 0 (fix axis 1 at 0) + auto c0 = t.slice(all, 0); REQUIRE(c0.size() == 3); REQUIRE(c0[0] == 1); REQUIRE(c0[1] == 3); From 2cf4c0887f44daee840243a65f1788acae3c7825 Mon Sep 17 00:00:00 2001 From: John Tramm Date: Thu, 12 Feb 2026 17:03:45 -0600 Subject: [PATCH 42/51] ran clang format again --- include/openmc/tensor.h | 40 ++++++++++++++++++---------- src/photon.cpp | 10 ++++--- src/physics.cpp | 3 +-- src/secondary_correlated.cpp | 9 ++++--- src/tallies/tally.cpp | 3 ++- src/xsdata.cpp | 12 +++------ tests/cpp_unit_tests/test_tensor.cpp | 17 +++++++----- 7 files changed, 55 insertions(+), 39 deletions(-) diff --git a/include/openmc/tensor.h b/include/openmc/tensor.h index ed2e75e46ff..8495a7f59a6 100644 --- a/include/openmc/tensor.h +++ b/include/openmc/tensor.h @@ -79,10 +79,16 @@ struct Range { }; //! Create a Range [start, end) -inline Range range(size_t start, size_t end) { return {start, end}; } +inline Range range(size_t start, size_t end) +{ + return {start, end}; +} //! Create a Range [start, ) -inline Range range(size_t start) { return {start, SIZE_MAX}; } +inline Range range(size_t start) +{ + return {start, SIZE_MAX}; +} namespace detail { @@ -93,13 +99,20 @@ struct SliceArg { size_t end; }; -inline SliceArg to_slice_arg(All) { return {SliceArg::ALL, 0, 0}; } -inline SliceArg to_slice_arg(Range r) { return {SliceArg::RANGE, r.start, r.end}; } +inline SliceArg to_slice_arg(All) +{ + return {SliceArg::ALL, 0, 0}; +} +inline SliceArg to_slice_arg(Range r) +{ + return {SliceArg::RANGE, r.start, r.end}; +} template -inline typename std::enable_if< - std::is_integral::value || std::is_enum::value, SliceArg>::type -to_slice_arg(I i) +inline + typename std::enable_if::value || std::is_enum::value, + SliceArg>::type + to_slice_arg(I i) { return {SliceArg::INDEX, static_cast(i), 0}; } @@ -113,9 +126,8 @@ struct SliceResult { //! Compute the result of applying slice arguments to shape/strides template -SliceResult compute_slice( - const vector& shape, const vector& strides, - First first, Rest... rest) +SliceResult compute_slice(const vector& shape, + const vector& strides, First first, Rest... rest) { const size_t n = 1 + sizeof...(Rest); SliceArg args[1 + sizeof...(Rest)] = { @@ -618,8 +630,8 @@ class Tensor { { auto strides = compute_strides(); auto r = detail::compute_slice(shape_, strides, first, rest...); - return {data_.data() + r.ptr_offset, std::move(r.shape), - std::move(r.strides)}; + return { + data_.data() + r.ptr_offset, std::move(r.shape), std::move(r.strides)}; } template @@ -627,8 +639,8 @@ class Tensor { { auto strides = compute_strides(); auto r = detail::compute_slice(shape_, strides, first, rest...); - return {data_.data() + r.ptr_offset, std::move(r.shape), - std::move(r.strides)}; + return { + data_.data() + r.ptr_offset, std::move(r.shape), std::move(r.strides)}; } //! Flat 1D view of all elements diff --git a/src/photon.cpp b/src/photon.cpp index b7983a0d222..1bbf88d2208 100644 --- a/src/photon.cpp +++ b/src/photon.cpp @@ -168,8 +168,8 @@ PhotonInteraction::PhotonInteraction(hid_t group) close_dataset(dset); read_dataset(tgroup, "xs", xs); - auto cross_section = - cross_sections_.slice(tensor::range(static_cast(shell.threshold)), i); + auto cross_section = cross_sections_.slice( + tensor::range(static_cast(shell.threshold)), i); cross_section = tensor::where(xs > 0, tensor::log(xs), 0); if (object_exists(tgroup, "transitions")) { @@ -184,7 +184,8 @@ PhotonInteraction::PhotonInteraction(hid_t group) read_dataset(tgroup, "transitions", matrix); // Transition probability normalization - double norm = tensor::Tensor(matrix.slice(tensor::all, 3)).sum(); + double norm = + tensor::Tensor(matrix.slice(tensor::all, 3)).sum(); shell.transitions.resize(n_transition); for (int j = 0; j < n_transition; ++j) { @@ -314,7 +315,8 @@ PhotonInteraction::PhotonInteraction(hid_t group) tensor::Tensor frst({static_cast(1)}); frst(0) = cutoff; - tensor::Tensor rest(electron_energy.slice(tensor::range(i_grid + 1))); + tensor::Tensor rest( + electron_energy.slice(tensor::range(i_grid + 1))); electron_energy = tensor::concatenate(frst, rest); } diff --git a/src/physics.cpp b/src/physics.cpp index 54fe00af7b2..c9737205ced 100644 --- a/src/physics.cpp +++ b/src/physics.cpp @@ -375,8 +375,7 @@ void sample_photon_reaction(Particle& p) // cross sections int i_grid = micro.index_grid; double f = micro.interp_factor; - tensor::View xs_lower = - element.cross_sections_.slice(i_grid); + tensor::View xs_lower = element.cross_sections_.slice(i_grid); tensor::View xs_upper = element.cross_sections_.slice(i_grid + 1); diff --git a/src/secondary_correlated.cpp b/src/secondary_correlated.cpp index b4d5e10d5ab..cc0ab8af19f 100644 --- a/src/secondary_correlated.cpp +++ b/src/secondary_correlated.cpp @@ -132,9 +132,12 @@ CorrelatedAngleEnergy::CorrelatedAngleEnergy(hid_t group) interp_mu = 1; auto interp = int2interp(interp_mu); - tensor::View xs = mu.slice(0, tensor::range(offset_mu, offset_mu + m)); - tensor::View ps = mu.slice(1, tensor::range(offset_mu, offset_mu + m)); - tensor::View cs = mu.slice(2, tensor::range(offset_mu, offset_mu + m)); + tensor::View xs = + mu.slice(0, tensor::range(offset_mu, offset_mu + m)); + tensor::View ps = + mu.slice(1, tensor::range(offset_mu, offset_mu + m)); + tensor::View cs = + mu.slice(2, tensor::range(offset_mu, offset_mu + m)); vector x {xs.begin(), xs.end()}; vector p {ps.begin(), ps.end()}; diff --git a/src/tallies/tally.cpp b/src/tallies/tally.cpp index 5bc121378ac..296c3fb5051 100644 --- a/src/tallies/tally.cpp +++ b/src/tallies/tally.cpp @@ -1007,7 +1007,8 @@ void reduce_tally_results() // Extract 2D view of the VALUE column from the 3D results tensor, // then copy into a contiguous array for MPI reduction const int val_idx = static_cast(TallyResult::VALUE); - tensor::View val_view = tally->results_.slice(tensor::all, tensor::all, val_idx); + tensor::View val_view = + tally->results_.slice(tensor::all, tensor::all, val_idx); tensor::Tensor values(val_view); tensor::Tensor values_reduced(values.shape()); diff --git a/src/xsdata.cpp b/src/xsdata.cpp index b4ee219c7b7..ee42446c8e2 100644 --- a/src/xsdata.cpp +++ b/src/xsdata.cpp @@ -148,8 +148,7 @@ void XsData::fission_vector_beta_from_hdf5( for (size_t a = 0; a < n_ang; a++) for (size_t d = 0; d < n_dg_; d++) for (size_t gin = 0; gin < n_g_; gin++) - chi_delayed.slice(a, d, gin) = - temp_chi.slice(a); + chi_delayed.slice(a, d, gin) = temp_chi.slice(a); // Get nu-fission tensor::Tensor temp_nufiss({n_ang, n_g_}, 0.); @@ -233,8 +232,7 @@ void XsData::fission_vector_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) for (size_t a = 0; a < n_ang; a++) for (size_t d = 0; d < n_dg_; d++) for (size_t gin = 0; gin < n_g_; gin++) - chi_delayed.slice(a, d, gin) = - temp_chi_d.slice(a, d); + chi_delayed.slice(a, d, gin) = temp_chi_d.slice(a, d); // Get prompt and delayed nu-fission directly read_nd_tensor(xsdata_grp, "prompt-nu-fission", prompt_nu_fission, true); @@ -366,8 +364,7 @@ void XsData::fission_matrix_beta_from_hdf5( for (size_t a = 0; a < n_ang; a++) for (size_t d = 0; d < n_dg_; d++) for (size_t gin = 0; gin < n_g_; gin++) { - tensor::View row = - chi_delayed.slice(a, d, gin); + tensor::View row = chi_delayed.slice(a, d, gin); row /= row.sum(); } } @@ -650,8 +647,7 @@ void XsData::combine( for (size_t a = 0; a < n_ang; a++) for (size_t d = 0; d < n_dg; d++) for (size_t gin = 0; gin < n_g; gin++) { - tensor::View row = - chi_delayed.slice(a, d, gin); + tensor::View row = chi_delayed.slice(a, d, gin); row /= row.sum(); } } diff --git a/tests/cpp_unit_tests/test_tensor.cpp b/tests/cpp_unit_tests/test_tensor.cpp index 5a6aa125821..5bc9a7e04da 100644 --- a/tests/cpp_unit_tests/test_tensor.cpp +++ b/tests/cpp_unit_tests/test_tensor.cpp @@ -626,8 +626,8 @@ TEST_CASE("Tensor multi-axis slice") REQUIRE(s3.ndim() == 2); REQUIRE(s3.shape(0) == 2); REQUIRE(s3.shape(1) == 4); - REQUIRE(s3(0, 0) == 0); // t(0,0,0) - REQUIRE(s3(1, 3) == 7); // t(0,1,3) + REQUIRE(s3(0, 0) == 0); // t(0,0,0) + REQUIRE(s3(1, 3) == 7); // t(0,1,3) } // ============================================================================ @@ -733,8 +733,8 @@ TEST_CASE("View sub-slice") Tensor t({6}, 0); t = {10, 20, 30, 40, 50, 60}; - auto s = t.slice(range(1, 5)); // [20, 30, 40, 50] - auto ss = s.slice(range(1, 3)); // [30, 40] + auto s = t.slice(range(1, 5)); // [20, 30, 40, 50] + auto ss = s.slice(range(1, 3)); // [30, 40] REQUIRE(ss.size() == 2); REQUIRE(ss[0] == 30); REQUIRE(ss[1] == 40); @@ -819,9 +819,12 @@ TEST_CASE("StaticTensor2D iteration") TEST_CASE("StaticTensor2D slice") { StaticTensor2D t; - t(0, 0) = 1; t(0, 1) = 2; - t(1, 0) = 3; t(1, 1) = 4; - t(2, 0) = 5; t(2, 1) = 6; + t(0, 0) = 1; + t(0, 1) = 2; + t(1, 0) = 3; + t(1, 1) = 4; + t(2, 0) = 5; + t(2, 1) = 6; // slice(1) = row 1 (fix axis 0 at 1) auto r1 = t.slice(1); From f7783c5c4cd33fecfea7ec0a47a6ee7c3b7b9cd8 Mon Sep 17 00:00:00 2001 From: John Tramm Date: Fri, 13 Feb 2026 01:12:05 +0000 Subject: [PATCH 43/51] LLVM 15 clang format --- src/tallies/trigger.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tallies/trigger.cpp b/src/tallies/trigger.cpp index 07295cc7712..6c54edd5e1d 100644 --- a/src/tallies/trigger.cpp +++ b/src/tallies/trigger.cpp @@ -72,7 +72,7 @@ void check_tally_triggers(double& ratio, int& tally_id, int& score) const auto& results = t.results_; for (auto filter_index = 0; filter_index < results.shape(0); - ++filter_index) { + ++filter_index) { // Compute the tally uncertainty metrics. auto uncert_pair = get_tally_uncertainty(i_tally, trigger.score_index, filter_index); From 312851d74b0ff59d8e646ed75f4f06905d1bcfc6 Mon Sep 17 00:00:00 2001 From: John Tramm Date: Thu, 12 Feb 2026 20:57:50 -0600 Subject: [PATCH 44/51] fixed header include --- include/openmc/tensor.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/openmc/tensor.h b/include/openmc/tensor.h index 8495a7f59a6..f92fded6eb1 100644 --- a/include/openmc/tensor.h +++ b/include/openmc/tensor.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include From 728dafe49a2c46f440e6f158530ac6de1735129d Mon Sep 17 00:00:00 2001 From: John Tramm Date: Thu, 12 Feb 2026 21:08:04 -0600 Subject: [PATCH 45/51] fixed header issue --- include/openmc/tensor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/openmc/tensor.h b/include/openmc/tensor.h index f92fded6eb1..35c811140ff 100644 --- a/include/openmc/tensor.h +++ b/include/openmc/tensor.h @@ -21,8 +21,8 @@ #include #include #include -#include #include +#include #include #include #include From 0a7a804118c666d2b70dd9dd902dad7a919e09b3 Mon Sep 17 00:00:00 2001 From: John Tramm Date: Fri, 13 Feb 2026 03:23:20 +0000 Subject: [PATCH 46/51] fix cassert issue (previously transitively included by xtensor) --- tests/regression_tests/cpp_driver/driver.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/regression_tests/cpp_driver/driver.cpp b/tests/regression_tests/cpp_driver/driver.cpp index a99c97b64e4..a6c3e651037 100644 --- a/tests/regression_tests/cpp_driver/driver.cpp +++ b/tests/regression_tests/cpp_driver/driver.cpp @@ -2,6 +2,8 @@ #include #endif +#include + #include "openmc/capi.h" #include "openmc/cell.h" #include "openmc/error.h" From 7160afe4b731a8c5f665c7638201e6708853aea2 Mon Sep 17 00:00:00 2001 From: John Tramm Date: Mon, 16 Feb 2026 10:28:35 -0600 Subject: [PATCH 47/51] made tensor::ones function, and changed range API to follow standard tensor library conventions --- include/openmc/tensor.h | 19 ++++++++++++++++--- src/cmfd_solver.cpp | 8 ++++---- src/material.cpp | 10 +++++----- src/mesh.cpp | 8 ++++---- src/nuclide.cpp | 2 +- src/photon.cpp | 4 ++-- src/scattdata.cpp | 12 ++++++------ src/volume_calc.cpp | 4 ++-- src/xsdata.cpp | 32 ++++++++++++++++---------------- 9 files changed, 56 insertions(+), 43 deletions(-) diff --git a/include/openmc/tensor.h b/include/openmc/tensor.h index 35c811140ff..a1a7b8e7669 100644 --- a/include/openmc/tensor.h +++ b/include/openmc/tensor.h @@ -85,10 +85,10 @@ inline Range range(size_t start, size_t end) return {start, end}; } -//! Create a Range [start, ) -inline Range range(size_t start) +//! Create a Range [0, end) +inline Range range(size_t end) { - return {start, SIZE_MAX}; + return {0, end}; } namespace detail { @@ -1054,6 +1054,19 @@ Tensor zeros(const vector& shape) return Tensor(shape, T(0)); } +template +Tensor ones(std::initializer_list shape) +{ + vector s(shape); + return Tensor(std::move(s), T(1)); +} + +template +Tensor ones(const vector& shape) +{ + return Tensor(shape, T(1)); +} + template Tensor zeros_like(const Tensor& o) { diff --git a/src/cmfd_solver.cpp b/src/cmfd_solver.cpp index 152abe19ff2..714a5bf3ac5 100644 --- a/src/cmfd_solver.cpp +++ b/src/cmfd_solver.cpp @@ -86,7 +86,7 @@ tensor::Tensor count_bank_sites( std::size_t cnt_size = cmfd::nx * cmfd::ny * cmfd::nz * cmfd::ng; // Create array of zeros - tensor::Tensor cnt({cnt_size}, 0.0); + tensor::Tensor cnt = tensor::zeros({cnt_size}); bool outside_ = false; auto bank_size = simulation::source_bank.size(); @@ -113,7 +113,7 @@ tensor::Tensor count_bank_sites( } int total = cnt.size(); - tensor::Tensor counts({cnt_size}, 0.0); + tensor::Tensor counts = tensor::zeros({cnt_size}); #ifdef OPENMC_MPI // collect values from all processors @@ -143,13 +143,13 @@ extern "C" void openmc_cmfd_reweight( std::size_t src_size = cmfd::nx * cmfd::ny * cmfd::nz * cmfd::ng; // count bank sites for CMFD mesh, store bins in bank_bins for reweighting - tensor::Tensor bank_bins({bank_size}, 0); + tensor::Tensor bank_bins = tensor::zeros({bank_size}); bool sites_outside; tensor::Tensor sourcecounts = count_bank_sites(bank_bins, &sites_outside); // Compute CMFD weightfactors - tensor::Tensor weightfactors({src_size}, 1.); + tensor::Tensor weightfactors = tensor::ones({src_size}); if (mpi::master) { if (sites_outside) { fatal_error("Source sites outside of the CMFD mesh"); diff --git a/src/material.cpp b/src/material.cpp index 2b586c815f4..21b11b8b9ce 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -644,9 +644,9 @@ void Material::init_bremsstrahlung() ttb->yield = tensor::zeros({n_e}); // Allocate temporary arrays - tensor::Tensor stopping_power_collision({n_e}, 0.0); - tensor::Tensor stopping_power_radiative({n_e}, 0.0); - tensor::Tensor dcs({n_e, n_k}, 0.0); + auto stopping_power_collision = tensor::zeros({n_e}); + auto stopping_power_radiative = tensor::zeros({n_e}); + auto dcs = tensor::zeros({n_e, n_k}); double Z_eq_sq = 0.0; double sum_density = 0.0; @@ -706,8 +706,8 @@ void Material::init_bremsstrahlung() stopping_power_collision + stopping_power_radiative; // Loop over photon energies - tensor::Tensor f({n_e}, 0.0); - tensor::Tensor z({n_e}, 0.0); + auto f = tensor::zeros({n_e}); + auto z = tensor::zeros({n_e}); for (int i = 0; i < n_e - 1; ++i) { double w = data::ttb_e_grid(i); diff --git a/src/mesh.cpp b/src/mesh.cpp index a611d4a168a..0f1979f1f60 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -1097,7 +1097,7 @@ tensor::Tensor StructuredMesh::count_sites( vector shape = {m}; // Create array of zeros - tensor::Tensor cnt(shape, 0.0); + auto cnt = tensor::zeros(shape); bool outside_ = false; for (int64_t i = 0; i < length; i++) { @@ -1117,7 +1117,7 @@ tensor::Tensor StructuredMesh::count_sites( } // Create reduced count data - tensor::Tensor counts(shape, 0.0); + auto counts = tensor::zeros(shape); int total = cnt.size(); #ifdef OPENMC_MPI @@ -1570,7 +1570,7 @@ tensor::Tensor RegularMesh::count_sites( vector shape = {m}; // Create array of zeros - tensor::Tensor cnt(shape, 0.0); + auto cnt = tensor::zeros(shape); bool outside_ = false; for (int64_t i = 0; i < length; i++) { @@ -1590,7 +1590,7 @@ tensor::Tensor RegularMesh::count_sites( } // Create reduced count data - tensor::Tensor counts(shape, 0.0); + auto counts = tensor::zeros(shape); int total = cnt.size(); #ifdef OPENMC_MPI diff --git a/src/nuclide.cpp b/src/nuclide.cpp index c8433b33a8a..17d6e952c39 100644 --- a/src/nuclide.cpp +++ b/src/nuclide.cpp @@ -360,7 +360,7 @@ void Nuclide::create_derived( { for (const auto& grid : grid_) { // Allocate and initialize cross section - xs_.emplace_back(vector {grid.energy.size(), 5}, 0.0); + xs_.push_back(tensor::zeros({grid.energy.size(), 5})); } reaction_index_.fill(C_NONE); diff --git a/src/photon.cpp b/src/photon.cpp index 1bbf88d2208..efa14cd59ff 100644 --- a/src/photon.cpp +++ b/src/photon.cpp @@ -169,7 +169,7 @@ PhotonInteraction::PhotonInteraction(hid_t group) read_dataset(tgroup, "xs", xs); auto cross_section = cross_sections_.slice( - tensor::range(static_cast(shell.threshold)), i); + tensor::range(static_cast(shell.threshold), cross_sections_.shape(0)), i); cross_section = tensor::where(xs > 0, tensor::log(xs), 0); if (object_exists(tgroup, "transitions")) { @@ -316,7 +316,7 @@ PhotonInteraction::PhotonInteraction(hid_t group) tensor::Tensor frst({static_cast(1)}); frst(0) = cutoff; tensor::Tensor rest( - electron_energy.slice(tensor::range(i_grid + 1))); + electron_energy.slice(tensor::range(i_grid + 1, electron_energy.size()))); electron_energy = tensor::concatenate(frst, rest); } diff --git a/src/scattdata.cpp b/src/scattdata.cpp index 6f4d5bad330..b945b1fe3cf 100644 --- a/src/scattdata.cpp +++ b/src/scattdata.cpp @@ -68,10 +68,10 @@ void ScattData::base_combine(size_t max_order, size_t order_dim, size_t groups = those_scatts[0]->energy.size(); // Now allocate and zero our storage spaces - tensor::Tensor this_nuscatt_matrix({groups, groups, order_dim}, 0.); - tensor::Tensor this_nuscatt_P0({groups, groups}, 0.); - tensor::Tensor this_scatt_P0({groups, groups}, 0.); - tensor::Tensor this_mult({groups, groups}, 1.); + tensor::Tensor this_nuscatt_matrix = tensor::zeros({groups, groups, order_dim}); + tensor::Tensor this_nuscatt_P0 = tensor::zeros({groups, groups}); + tensor::Tensor this_scatt_P0 = tensor::zeros({groups, groups}); + tensor::Tensor this_mult = tensor::ones({groups, groups}); // Build the dense scattering and multiplicity matrices for (int i = 0; i < those_scatts.size(); i++) { @@ -408,7 +408,7 @@ tensor::Tensor ScattDataLegendre::get_matrix(size_t max_order) // Get the sizes and initialize the data to 0 size_t groups = energy.size(); size_t order_dim = max_order + 1; - tensor::Tensor matrix({groups, groups, order_dim}, 0.); + tensor::Tensor matrix = tensor::zeros({groups, groups, order_dim}); for (int gin = 0; gin < groups; gin++) { for (int i_gout = 0; i_gout < energy[gin].size(); i_gout++) { @@ -781,7 +781,7 @@ tensor::Tensor ScattDataTabular::get_matrix(size_t max_order) size_t groups = energy.size(); // We ignore the requested order for Histogram and Tabular representations size_t order_dim = get_order(); - tensor::Tensor matrix({groups, groups, order_dim}, 0.); + tensor::Tensor matrix = tensor::zeros({groups, groups, order_dim}); for (int gin = 0; gin < groups; gin++) { for (int i_gout = 0; i_gout < energy[gin].size(); i_gout++) { diff --git a/src/volume_calc.cpp b/src/volume_calc.cpp index 06271bd1884..fc98146af55 100644 --- a/src/volume_calc.cpp +++ b/src/volume_calc.cpp @@ -241,8 +241,8 @@ vector VolumeCalculation::execute() const // non-zero auto n_nuc = settings::run_CE ? data::nuclides.size() : data::mg.nuclides_.size(); - tensor::Tensor atoms( - {static_cast(n_nuc), size_t {2}}, 0.0); + auto atoms = tensor::zeros( + {static_cast(n_nuc), size_t {2}}); #ifdef OPENMC_MPI if (mpi::master) { diff --git a/src/xsdata.cpp b/src/xsdata.cpp index ee42446c8e2..925d8fc1196 100644 --- a/src/xsdata.cpp +++ b/src/xsdata.cpp @@ -129,7 +129,7 @@ void XsData::fission_vector_beta_from_hdf5( // Data is provided as nu-fission and chi with a beta for delayed info // Get chi - tensor::Tensor temp_chi({n_ang, n_g_}, 0.); + tensor::Tensor temp_chi = tensor::zeros({n_ang, n_g_}); read_nd_tensor(xsdata_grp, "chi", temp_chi, true); // Normalize chi so it sums to 1 over outgoing groups for each angle @@ -151,7 +151,7 @@ void XsData::fission_vector_beta_from_hdf5( chi_delayed.slice(a, d, gin) = temp_chi.slice(a); // Get nu-fission - tensor::Tensor temp_nufiss({n_ang, n_g_}, 0.); + tensor::Tensor temp_nufiss = tensor::zeros({n_ang, n_g_}); read_nd_tensor(xsdata_grp, "nu-fission", temp_nufiss, true); // Get beta (strategy will depend upon the number of dimensions in beta) @@ -162,7 +162,7 @@ void XsData::fission_vector_beta_from_hdf5( if (!is_isotropic) ndim_target += 2; if (beta_ndims == ndim_target) { - tensor::Tensor temp_beta({n_ang, n_dg_}, 0.); + tensor::Tensor temp_beta = tensor::zeros({n_ang, n_dg_}); read_nd_tensor(xsdata_grp, "beta", temp_beta, true); // prompt_nu_fission = (1 - sum_of_beta) * nu_fission @@ -178,7 +178,7 @@ void XsData::fission_vector_beta_from_hdf5( for (size_t g = 0; g < n_g_; g++) delayed_nu_fission(a, d, g) = temp_beta(a, d) * temp_nufiss(a, g); } else if (beta_ndims == ndim_target + 1) { - tensor::Tensor temp_beta({n_ang, n_dg_, n_g_}, 0.); + tensor::Tensor temp_beta = tensor::zeros({n_ang, n_dg_, n_g_}); read_nd_tensor(xsdata_grp, "beta", temp_beta, true); // prompt_nu_fission = (1 - sum_of_beta) * nu_fission @@ -202,7 +202,7 @@ void XsData::fission_vector_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) // Data is provided separately as prompt + delayed nu-fission and chi // Get chi-prompt - tensor::Tensor temp_chi_p({n_ang, n_g_}, 0.); + tensor::Tensor temp_chi_p = tensor::zeros({n_ang, n_g_}); read_nd_tensor(xsdata_grp, "chi-prompt", temp_chi_p, true); // Normalize prompt chi so it sums to 1 over outgoing groups for each angle @@ -212,7 +212,7 @@ void XsData::fission_vector_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) } // Get chi-delayed - tensor::Tensor temp_chi_d({n_ang, n_dg_, n_g_}, 0.); + tensor::Tensor temp_chi_d = tensor::zeros({n_ang, n_dg_, n_g_}); read_nd_tensor(xsdata_grp, "chi-delayed", temp_chi_d, true); // Normalize delayed chi so it sums to 1 over outgoing groups for each @@ -245,7 +245,7 @@ void XsData::fission_vector_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang) // Therefore, the code only considers the data as prompt. // Get chi - tensor::Tensor temp_chi({n_ang, n_g_}, 0.); + tensor::Tensor temp_chi = tensor::zeros({n_ang, n_g_}); read_nd_tensor(xsdata_grp, "chi", temp_chi, true); // Normalize chi so it sums to 1 over outgoing groups for each angle @@ -271,7 +271,7 @@ void XsData::fission_matrix_beta_from_hdf5( // Data is provided as nu-fission and chi with a beta for delayed info // Get nu-fission matrix - tensor::Tensor temp_matrix({n_ang, n_g_, n_g_}, 0.); + tensor::Tensor temp_matrix = tensor::zeros({n_ang, n_g_, n_g_}); read_nd_tensor(xsdata_grp, "nu-fission", temp_matrix, true); // Get beta (strategy will depend upon the number of dimensions in beta) @@ -282,7 +282,7 @@ void XsData::fission_matrix_beta_from_hdf5( if (!is_isotropic) ndim_target += 2; if (beta_ndims == ndim_target) { - tensor::Tensor temp_beta({n_ang, n_dg_}, 0.); + tensor::Tensor temp_beta = tensor::zeros({n_ang, n_dg_}); read_nd_tensor(xsdata_grp, "beta", temp_beta, true); auto beta_sum = temp_beta.sum(1); @@ -316,7 +316,7 @@ void XsData::fission_matrix_beta_from_hdf5( temp_beta(a, d) * temp_matrix(a, gin, gout); } else if (beta_ndims == ndim_target + 1) { - tensor::Tensor temp_beta({n_ang, n_dg_, n_g_}, 0.); + tensor::Tensor temp_beta = tensor::zeros({n_ang, n_dg_, n_g_}); read_nd_tensor(xsdata_grp, "beta", temp_beta, true); auto beta_sum = temp_beta.sum(1); @@ -374,7 +374,7 @@ void XsData::fission_matrix_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) // Data is provided separately as prompt + delayed nu-fission and chi // Get the prompt nu-fission matrix - tensor::Tensor temp_matrix_p({n_ang, n_g_, n_g_}, 0.); + tensor::Tensor temp_matrix_p = tensor::zeros({n_ang, n_g_, n_g_}); read_nd_tensor(xsdata_grp, "prompt-nu-fission", temp_matrix_p, true); // prompt_nu_fission is the sum over outgoing groups @@ -388,7 +388,7 @@ void XsData::fission_matrix_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) temp_matrix_p(a, gin, gout) / prompt_nu_fission(a, gin); // Get the delayed nu-fission matrix - tensor::Tensor temp_matrix_d({n_ang, n_dg_, n_g_, n_g_}, 0.); + tensor::Tensor temp_matrix_d = tensor::zeros({n_ang, n_dg_, n_g_, n_g_}); read_nd_tensor(xsdata_grp, "delayed-nu-fission", temp_matrix_d, true); // delayed_nu_fission is the sum over outgoing groups @@ -410,7 +410,7 @@ void XsData::fission_matrix_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang) // Therefore, the code only considers the data as prompt. // Get nu-fission matrix - tensor::Tensor temp_matrix({n_ang, n_g_, n_g_}, 0.); + tensor::Tensor temp_matrix = tensor::zeros({n_ang, n_g_, n_g_}); read_nd_tensor(xsdata_grp, "nu-fission", temp_matrix, true); // prompt_nu_fission is the sum over outgoing groups @@ -478,9 +478,9 @@ void XsData::scatter_from_hdf5(hid_t xsdata_grp, size_t n_ang, hid_t scatt_grp = open_group(xsdata_grp, "scatter_data"); // Get the outgoing group boundary indices - tensor::Tensor gmin({n_ang, n_g_}, 0.); + tensor::Tensor gmin = tensor::zeros({n_ang, n_g_}); read_nd_tensor(scatt_grp, "g_min", gmin, true); - tensor::Tensor gmax({n_ang, n_g_}, 0.); + tensor::Tensor gmax = tensor::zeros({n_ang, n_g_}); read_nd_tensor(scatt_grp, "g_max", gmax, true); // Make gmin and gmax start from 0 vice 1 as they do in the library @@ -492,7 +492,7 @@ void XsData::scatter_from_hdf5(hid_t xsdata_grp, size_t n_ang, size_t length = order_data * (gmax - gmin + 1).sum(); double_4dvec input_scatt(n_ang, double_3dvec(n_g_)); - tensor::Tensor temp_arr({length}, 0.); + tensor::Tensor temp_arr = tensor::zeros({length}); read_nd_tensor(scatt_grp, "scatter_matrix", temp_arr, true); // Compare the number of orders given with the max order of the problem; From f6658298586792a1c68f69c0e78a50aee5792be5 Mon Sep 17 00:00:00 2001 From: John Tramm Date: Mon, 16 Feb 2026 16:42:34 +0000 Subject: [PATCH 48/51] ran clang-format 15 --- src/photon.cpp | 10 ++++++---- src/scattdata.cpp | 15 ++++++++++----- src/volume_calc.cpp | 4 ++-- src/xsdata.cpp | 21 ++++++++++++++------- 4 files changed, 32 insertions(+), 18 deletions(-) diff --git a/src/photon.cpp b/src/photon.cpp index efa14cd59ff..5e2ba688427 100644 --- a/src/photon.cpp +++ b/src/photon.cpp @@ -168,8 +168,10 @@ PhotonInteraction::PhotonInteraction(hid_t group) close_dataset(dset); read_dataset(tgroup, "xs", xs); - auto cross_section = cross_sections_.slice( - tensor::range(static_cast(shell.threshold), cross_sections_.shape(0)), i); + auto cross_section = + cross_sections_.slice(tensor::range(static_cast(shell.threshold), + cross_sections_.shape(0)), + i); cross_section = tensor::where(xs > 0, tensor::log(xs), 0); if (object_exists(tgroup, "transitions")) { @@ -315,8 +317,8 @@ PhotonInteraction::PhotonInteraction(hid_t group) tensor::Tensor frst({static_cast(1)}); frst(0) = cutoff; - tensor::Tensor rest( - electron_energy.slice(tensor::range(i_grid + 1, electron_energy.size()))); + tensor::Tensor rest(electron_energy.slice( + tensor::range(i_grid + 1, electron_energy.size()))); electron_energy = tensor::concatenate(frst, rest); } diff --git a/src/scattdata.cpp b/src/scattdata.cpp index b945b1fe3cf..9aa09956d50 100644 --- a/src/scattdata.cpp +++ b/src/scattdata.cpp @@ -68,9 +68,12 @@ void ScattData::base_combine(size_t max_order, size_t order_dim, size_t groups = those_scatts[0]->energy.size(); // Now allocate and zero our storage spaces - tensor::Tensor this_nuscatt_matrix = tensor::zeros({groups, groups, order_dim}); - tensor::Tensor this_nuscatt_P0 = tensor::zeros({groups, groups}); - tensor::Tensor this_scatt_P0 = tensor::zeros({groups, groups}); + tensor::Tensor this_nuscatt_matrix = + tensor::zeros({groups, groups, order_dim}); + tensor::Tensor this_nuscatt_P0 = + tensor::zeros({groups, groups}); + tensor::Tensor this_scatt_P0 = + tensor::zeros({groups, groups}); tensor::Tensor this_mult = tensor::ones({groups, groups}); // Build the dense scattering and multiplicity matrices @@ -408,7 +411,8 @@ tensor::Tensor ScattDataLegendre::get_matrix(size_t max_order) // Get the sizes and initialize the data to 0 size_t groups = energy.size(); size_t order_dim = max_order + 1; - tensor::Tensor matrix = tensor::zeros({groups, groups, order_dim}); + tensor::Tensor matrix = + tensor::zeros({groups, groups, order_dim}); for (int gin = 0; gin < groups; gin++) { for (int i_gout = 0; i_gout < energy[gin].size(); i_gout++) { @@ -781,7 +785,8 @@ tensor::Tensor ScattDataTabular::get_matrix(size_t max_order) size_t groups = energy.size(); // We ignore the requested order for Histogram and Tabular representations size_t order_dim = get_order(); - tensor::Tensor matrix = tensor::zeros({groups, groups, order_dim}); + tensor::Tensor matrix = + tensor::zeros({groups, groups, order_dim}); for (int gin = 0; gin < groups; gin++) { for (int i_gout = 0; i_gout < energy[gin].size(); i_gout++) { diff --git a/src/volume_calc.cpp b/src/volume_calc.cpp index fc98146af55..f675ae78a2e 100644 --- a/src/volume_calc.cpp +++ b/src/volume_calc.cpp @@ -241,8 +241,8 @@ vector VolumeCalculation::execute() const // non-zero auto n_nuc = settings::run_CE ? data::nuclides.size() : data::mg.nuclides_.size(); - auto atoms = tensor::zeros( - {static_cast(n_nuc), size_t {2}}); + auto atoms = + tensor::zeros({static_cast(n_nuc), size_t {2}}); #ifdef OPENMC_MPI if (mpi::master) { diff --git a/src/xsdata.cpp b/src/xsdata.cpp index 925d8fc1196..33d063a7b9a 100644 --- a/src/xsdata.cpp +++ b/src/xsdata.cpp @@ -178,7 +178,8 @@ void XsData::fission_vector_beta_from_hdf5( for (size_t g = 0; g < n_g_; g++) delayed_nu_fission(a, d, g) = temp_beta(a, d) * temp_nufiss(a, g); } else if (beta_ndims == ndim_target + 1) { - tensor::Tensor temp_beta = tensor::zeros({n_ang, n_dg_, n_g_}); + tensor::Tensor temp_beta = + tensor::zeros({n_ang, n_dg_, n_g_}); read_nd_tensor(xsdata_grp, "beta", temp_beta, true); // prompt_nu_fission = (1 - sum_of_beta) * nu_fission @@ -212,7 +213,8 @@ void XsData::fission_vector_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) } // Get chi-delayed - tensor::Tensor temp_chi_d = tensor::zeros({n_ang, n_dg_, n_g_}); + tensor::Tensor temp_chi_d = + tensor::zeros({n_ang, n_dg_, n_g_}); read_nd_tensor(xsdata_grp, "chi-delayed", temp_chi_d, true); // Normalize delayed chi so it sums to 1 over outgoing groups for each @@ -271,7 +273,8 @@ void XsData::fission_matrix_beta_from_hdf5( // Data is provided as nu-fission and chi with a beta for delayed info // Get nu-fission matrix - tensor::Tensor temp_matrix = tensor::zeros({n_ang, n_g_, n_g_}); + tensor::Tensor temp_matrix = + tensor::zeros({n_ang, n_g_, n_g_}); read_nd_tensor(xsdata_grp, "nu-fission", temp_matrix, true); // Get beta (strategy will depend upon the number of dimensions in beta) @@ -316,7 +319,8 @@ void XsData::fission_matrix_beta_from_hdf5( temp_beta(a, d) * temp_matrix(a, gin, gout); } else if (beta_ndims == ndim_target + 1) { - tensor::Tensor temp_beta = tensor::zeros({n_ang, n_dg_, n_g_}); + tensor::Tensor temp_beta = + tensor::zeros({n_ang, n_dg_, n_g_}); read_nd_tensor(xsdata_grp, "beta", temp_beta, true); auto beta_sum = temp_beta.sum(1); @@ -374,7 +378,8 @@ void XsData::fission_matrix_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) // Data is provided separately as prompt + delayed nu-fission and chi // Get the prompt nu-fission matrix - tensor::Tensor temp_matrix_p = tensor::zeros({n_ang, n_g_, n_g_}); + tensor::Tensor temp_matrix_p = + tensor::zeros({n_ang, n_g_, n_g_}); read_nd_tensor(xsdata_grp, "prompt-nu-fission", temp_matrix_p, true); // prompt_nu_fission is the sum over outgoing groups @@ -388,7 +393,8 @@ void XsData::fission_matrix_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) temp_matrix_p(a, gin, gout) / prompt_nu_fission(a, gin); // Get the delayed nu-fission matrix - tensor::Tensor temp_matrix_d = tensor::zeros({n_ang, n_dg_, n_g_, n_g_}); + tensor::Tensor temp_matrix_d = + tensor::zeros({n_ang, n_dg_, n_g_, n_g_}); read_nd_tensor(xsdata_grp, "delayed-nu-fission", temp_matrix_d, true); // delayed_nu_fission is the sum over outgoing groups @@ -410,7 +416,8 @@ void XsData::fission_matrix_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang) // Therefore, the code only considers the data as prompt. // Get nu-fission matrix - tensor::Tensor temp_matrix = tensor::zeros({n_ang, n_g_, n_g_}); + tensor::Tensor temp_matrix = + tensor::zeros({n_ang, n_g_, n_g_}); read_nd_tensor(xsdata_grp, "nu-fission", temp_matrix, true); // prompt_nu_fission is the sum over outgoing groups From cd54c70663a315b652aacdf0b0ca7c24c6bedec1 Mon Sep 17 00:00:00 2001 From: John Tramm Date: Mon, 16 Feb 2026 12:35:46 -0600 Subject: [PATCH 49/51] added front and back helper functions --- include/openmc/tensor.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/openmc/tensor.h b/include/openmc/tensor.h index a1a7b8e7669..20046ffe883 100644 --- a/include/openmc/tensor.h +++ b/include/openmc/tensor.h @@ -219,6 +219,12 @@ class View { T& operator[](size_t i) { return data_[flat_to_offset(i)]; } const T& operator[](size_t i) const { return data_[flat_to_offset(i)]; } + //! First and last element + T& front() { return data_[0]; } + const T& front() const { return data_[0]; } + T& back() { return data_[size() - 1]; } + const T& back() const { return data_[size() - 1]; } + //-------------------------------------------------------------------------- // Accessors From e85a7810062e427ba55251ebc5af077a5f1e7c65 Mon Sep 17 00:00:00 2001 From: John Tramm Date: Mon, 16 Feb 2026 13:31:12 -0600 Subject: [PATCH 50/51] Moved front/back methods from view to tensor. --- include/openmc/tensor.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/openmc/tensor.h b/include/openmc/tensor.h index 20046ffe883..9d428aaebdd 100644 --- a/include/openmc/tensor.h +++ b/include/openmc/tensor.h @@ -219,12 +219,6 @@ class View { T& operator[](size_t i) { return data_[flat_to_offset(i)]; } const T& operator[](size_t i) const { return data_[flat_to_offset(i)]; } - //! First and last element - T& front() { return data_[0]; } - const T& front() const { return data_[0]; } - T& back() { return data_[size() - 1]; } - const T& back() const { return data_[size() - 1]; } - //-------------------------------------------------------------------------- // Accessors @@ -594,6 +588,12 @@ class Tensor { stored_type& operator[](size_t i) { return data_[i]; } const stored_type& operator[](size_t i) const { return data_[i]; } + //! First and last element + stored_type& front() { return data_.front(); } + const stored_type& front() const { return data_.front(); } + stored_type& back() { return data_.back(); } + const stored_type& back() const { return data_.back(); } + //-------------------------------------------------------------------------- // Iterators From 11e869a507e2165a8f1e3f55215d6c2c265acf86 Mon Sep 17 00:00:00 2001 From: John Tramm Date: Mon, 16 Feb 2026 13:43:45 -0600 Subject: [PATCH 51/51] update unit tests --- tests/cpp_unit_tests/test_tensor.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/cpp_unit_tests/test_tensor.cpp b/tests/cpp_unit_tests/test_tensor.cpp index 5bc9a7e04da..6eae1cea6a4 100644 --- a/tests/cpp_unit_tests/test_tensor.cpp +++ b/tests/cpp_unit_tests/test_tensor.cpp @@ -539,11 +539,17 @@ TEST_CASE("Tensor slice with range") REQUIRE(s[1] == 30); REQUIRE(s[2] == 40); - // range(start) to end + // range(end) from start — range(3) means [0, 3) auto s2 = t.slice(range(3)); REQUIRE(s2.size() == 3); - REQUIRE(s2[0] == 40); - REQUIRE(s2[2] == 60); + REQUIRE(s2[0] == 10); + REQUIRE(s2[2] == 30); + + // range(start, SIZE_MAX) to end + auto s3 = t.slice(range(3, 6)); + REQUIRE(s3.size() == 3); + REQUIRE(s3[0] == 40); + REQUIRE(s3[2] == 60); // Write through slice s[0] = 99;