LCOV - code coverage report
Current view: top level - include/utils - StochasticToolsUtils.h (source / functions) Hit Total Coverage
Test: idaholab/moose stochastic_tools: f45d79 Lines: 56 94 59.6 %
Date: 2025-07-25 05:00:46 Functions: 8 28 28.6 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //* This file is part of the MOOSE framework
       2             : //* https://mooseframework.inl.gov
       3             : //*
       4             : //* All rights reserved, see COPYRIGHT for full restrictions
       5             : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
       6             : //*
       7             : //* Licensed under LGPL 2.1, please see LICENSE for details
       8             : //* https://www.gnu.org/licenses/lgpl-2.1.html
       9             : 
      10             : #pragma once
      11             : 
      12             : #include "MooseUtils.h"
      13             : #include "libmesh/communicator.h"
      14             : 
      15             : namespace StochasticTools
      16             : {
      17             : 
      18             : /**
      19             :  * Custom type trait that has a ::value of true for types that can be gathered
      20             :  */
      21             : template <typename T>
      22             : struct canDefaultGather
      23             : {
      24             :   static constexpr bool value = false;
      25             : };
      26             : template <typename T>
      27             : struct canDefaultGather<std::vector<T>>
      28             : {
      29             :   static constexpr bool value = std::is_base_of<TIMPI::DataType, TIMPI::StandardType<T>>::value;
      30             : };
      31             : template <typename T>
      32             : struct canStochasticGather
      33             : {
      34             :   static constexpr bool value = false;
      35             : };
      36             : template <typename T>
      37             : struct canStochasticGather<std::vector<T>>
      38             : {
      39             :   static constexpr bool value = canStochasticGather<T>::value ||
      40             :                                 std::is_base_of<TIMPI::DataType, TIMPI::StandardType<T>>::value ||
      41             :                                 std::is_same<T, std::string>::value || std::is_same<T, bool>::value;
      42             : };
      43             : 
      44             : /*
      45             :  * Methods for gathering nested vectors
      46             :  */
      47             : template <typename T>
      48             : void
      49           0 : stochasticGather(const libMesh::Parallel::Communicator &, processor_id_type, T &)
      50             : {
      51           0 :   ::mooseError("Cannot gather values of type ", MooseUtils::prettyCppType<T>());
      52             : }
      53             : template <typename T,
      54             :           typename std::enable_if<canDefaultGather<std::vector<T>>::value, int>::type = 0>
      55             : void
      56             : stochasticGather(const libMesh::Parallel::Communicator & comm,
      57             :                  processor_id_type root_id,
      58             :                  std::vector<T> & val)
      59             : {
      60        7564 :   comm.gather(root_id, val);
      61        7564 : }
      62             : template <
      63             :     typename T,
      64             :     typename std::enable_if<canStochasticGather<std::vector<std::vector<T>>>::value, int>::type = 0>
      65             : void
      66         352 : stochasticGather(const libMesh::Parallel::Communicator & comm,
      67             :                  processor_id_type root_id,
      68             :                  std::vector<std::vector<T>> & val)
      69             : {
      70             :   // Get local vector sizes
      71             :   std::size_t num_local_vecs = val.size();
      72             :   std::vector<std::size_t> val_sizes;
      73         352 :   val_sizes.reserve(num_local_vecs);
      74             :   std::size_t num_local_vals = 0;
      75        4502 :   for (const auto & v : val)
      76             :   {
      77        4150 :     val_sizes.push_back(v.size());
      78        4150 :     num_local_vals += v.size();
      79             :   }
      80             : 
      81             :   // Flatten the local vector of vectors
      82             :   std::vector<T> val_exp;
      83         352 :   val_exp.reserve(num_local_vals);
      84        4502 :   for (auto & v : val)
      85           0 :     std::copy(v.begin(), v.end(), std::back_inserter(val_exp));
      86             : 
      87             :   // Gather the vector sizes and the flattened vector
      88         352 :   comm.gather(root_id, val_sizes);
      89           0 :   stochasticGather(comm, root_id, val_exp);
      90             : 
      91             :   // Build the vector of vectors from the gathered flatten vector
      92         352 :   if (comm.rank() == root_id)
      93             :   {
      94         220 :     val.resize(val_sizes.size());
      95             :     std::size_t ind = num_local_vals;
      96        1414 :     for (std::size_t i = num_local_vecs; i < val_sizes.size(); ++i)
      97             :     {
      98        1194 :       val[i].resize(val_sizes[i]);
      99        1194 :       std::move(val_exp.begin() + ind, val_exp.begin() + ind + val_sizes[i], val[i].begin());
     100        1194 :       ind += val_sizes[i];
     101             :     }
     102             :   }
     103         352 : }
     104             : // Gathering a vector of strings hasn't been implemented in libMesh, so just gonna do it the hard
     105             : // way
     106             : template <typename T>
     107             : void
     108          64 : stochasticGather(const libMesh::Parallel::Communicator & comm,
     109             :                  processor_id_type root_id,
     110             :                  std::vector<std::basic_string<T>> & val)
     111             : {
     112          64 :   std::vector<std::basic_string<T>> val_gath = val;
     113          64 :   comm.allgather(val_gath);
     114          64 :   if (comm.rank() == root_id)
     115             :     val = std::move(val_gath);
     116          64 : }
     117             : // Gathering bool is weird
     118             : template <typename A>
     119             : void
     120        6112 : stochasticGather(const libMesh::Parallel::Communicator & comm,
     121             :                  processor_id_type root_id,
     122             :                  std::vector<bool, A> & val)
     123             : {
     124        6112 :   std::vector<unsigned short int> temp(val.size());
     125       20922 :   for (std::size_t i = 0; i < val.size(); ++i)
     126       16360 :     temp[i] = val[i] ? 1 : 0;
     127        6112 :   comm.gather(root_id, temp);
     128        6112 :   if (comm.rank() == root_id)
     129             :   {
     130        3660 :     val.resize(temp.size());
     131       18470 :     for (std::size_t i = 0; i < temp.size(); ++i)
     132       14810 :       val[i] = temp[i] == 1;
     133             :   }
     134        6112 : }
     135             : 
     136             : /*
     137             :  * Methods for gathering nested vectors on all processors
     138             :  */
     139             : template <typename T>
     140             : void
     141           0 : stochasticAllGather(const libMesh::Parallel::Communicator &, T &)
     142             : {
     143           0 :   ::mooseError("Cannot gather values of type ", MooseUtils::prettyCppType<T>());
     144             : }
     145             : template <typename T,
     146             :           typename std::enable_if<canDefaultGather<std::vector<T>>::value, int>::type = 0>
     147             : void
     148             : stochasticAllGather(const libMesh::Parallel::Communicator & comm, std::vector<T> & val)
     149             : {
     150           0 :   comm.allgather(val);
     151           0 : }
     152             : template <
     153             :     typename T,
     154             :     typename std::enable_if<canStochasticGather<std::vector<std::vector<T>>>::value, int>::type = 0>
     155             : void
     156           0 : stochasticAllGather(const libMesh::Parallel::Communicator & comm, std::vector<std::vector<T>> & val)
     157             : {
     158             :   // Get local vector sizes
     159             :   std::size_t num_local_vecs = val.size();
     160             :   std::vector<std::size_t> val_sizes;
     161           0 :   val_sizes.reserve(num_local_vecs);
     162             :   std::size_t num_local_vals = 0;
     163           0 :   for (const auto & v : val)
     164             :   {
     165           0 :     val_sizes.push_back(v.size());
     166           0 :     num_local_vals += v.size();
     167             :   }
     168             : 
     169             :   // Flatten the local vector of vectors
     170             :   std::vector<T> val_exp;
     171           0 :   val_exp.reserve(num_local_vals);
     172           0 :   for (auto & v : val)
     173           0 :     std::copy(v.begin(), v.end(), std::back_inserter(val_exp));
     174             : 
     175             :   // Gather the vector sizes and the flattened vector
     176           0 :   comm.allgather(val_sizes);
     177           0 :   stochasticAllGather(comm, val_exp);
     178             : 
     179             :   // Build the vector of vectors from the gathered flatten vector
     180           0 :   val.resize(val_sizes.size());
     181             :   std::size_t ind = 0;
     182           0 :   for (std::size_t i = 0; i < val_sizes.size(); ++i)
     183             :   {
     184           0 :     val[i].resize(val_sizes[i]);
     185           0 :     std::move(val_exp.begin() + ind, val_exp.begin() + ind + val_sizes[i], val[i].begin());
     186           0 :     ind += val_sizes[i];
     187             :   }
     188           0 : }
     189             : // Gathering a vector of strings hasn't been implemented in libMesh, so just gonna do it the hard
     190             : // way
     191             : template <typename T>
     192             : void
     193             : stochasticAllGather(const libMesh::Parallel::Communicator & comm,
     194             :                     std::vector<std::basic_string<T>> & val)
     195             : {
     196           0 :   comm.allgather(val);
     197           0 : }
     198             : // Gathering bool is weird
     199             : template <typename A>
     200             : void
     201           0 : stochasticAllGather(const libMesh::Parallel::Communicator & comm, std::vector<bool, A> & val)
     202             : {
     203           0 :   std::vector<unsigned short int> temp(val.size());
     204           0 :   for (std::size_t i = 0; i < val.size(); ++i)
     205           0 :     temp[i] = val[i] ? 1 : 0;
     206           0 :   comm.allgather(temp);
     207           0 :   val.resize(temp.size());
     208           0 :   for (std::size_t i = 0; i < temp.size(); ++i)
     209           0 :     val[i] = temp[i] == 1;
     210           0 : }
     211             : 
     212             : /*
     213             :  * Methods for sorting vectors of vectors with elements inplace. For example:
     214             :  * {{7, 5, 2}, {3, 8, 6}, {9, 9, 1}} -> {{3, 5, 1}, {7, 8, 2}, {9, 9, 6}}
     215             :  */
     216             : template <typename T>
     217             : void
     218        9229 : inplaceSort(std::vector<T> & values)
     219             : {
     220        9229 :   std::sort(values.begin(), values.end());
     221        9229 : }
     222             : template <typename T>
     223             : void
     224        1325 : inplaceSort(std::vector<std::vector<T>> & values)
     225             : {
     226        1325 :   if (values.empty())
     227           0 :     return;
     228             : 
     229             :   const std::size_t sz = values[0].size();
     230             :   mooseAssert(std::find_if(values.begin(),
     231             :                            values.end(),
     232             :                            [&sz](const std::vector<T> & val)
     233             :                            { return val.size() != sz; }) == values.end(),
     234             :               "All vectors must be same size to sort.");
     235             : 
     236        1325 :   std::vector<T> vals(values.size());
     237        7672 :   for (const auto & i : make_range(sz))
     238             :   {
     239    23974267 :     for (const auto & k : index_range(values))
     240    23967920 :       vals[k] = values[k][i];
     241        6347 :     inplaceSort(vals);
     242    23974267 :     for (const auto & k : index_range(values))
     243    23535920 :       values[k][i] = std::move(vals[k]);
     244             :   }
     245          16 : }
     246             : 
     247             : /**
     248             :  * Reshape a vector into matrix-like vector of vectors
     249             :  *
     250             :  * @param vec Input vector to reshape
     251             :  * @param n Leading dimension size,
     252             :             number of columns if row-major, number of rows if column-major
     253             :  * @param row_major True if @param vec is in row-major format
     254             :  *                  see https://en.wikipedia.org/wiki/Row-_and_column-major_order
     255             :  * @return vector of vectors representing reshaped vector ([row][col])
     256             :  */
     257             : template <typename T>
     258             : std::vector<std::vector<T>>
     259         800 : reshapeVector(const std::vector<T> & vec, std::size_t n, bool row_major)
     260             : {
     261         800 :   const auto nelem = vec.size();
     262         800 :   const auto nrow = row_major ? nelem / n : n;
     263         800 :   const auto ncol = row_major ? n : nelem / n;
     264         800 :   if (nelem % n != 0)
     265           0 :     ::mooseError(
     266             :         "Reshaping dimensions (", nrow, ", ", ncol, ") does not match vector size (", nelem, ").");
     267             : 
     268         800 :   std::vector<std::vector<T>> mat(nrow, std::vector<T>(ncol));
     269     4134444 :   for (const auto & i : make_range(nrow))
     270    57986360 :     for (const auto & j : make_range(ncol))
     271             :     {
     272    53852716 :       const auto k = row_major ? (i * ncol + j) : (j * nrow + i);
     273    53852716 :       mat[i][j] = vec[k];
     274             :     }
     275         800 :   return mat;
     276           0 : }
     277             : 
     278             : } // StochasticTools namespace

Generated by: LCOV version 1.14