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
|