https://mooseframework.inl.gov
ElementGroupCentroidPositions.C
Go to the documentation of this file.
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 
11 
12 #include "libmesh/parallel_algebra.h"
13 
15 
18 {
21  params.addClassDescription("Gets the Positions of the centroid of groups of elements. "
22  "Groups may be defined using subdomains or element extra ids.");
23 
24  // To enable extra element ID groups
25  params.addParam<MooseEnum>("grouping_type", groupTypeEnum(), "Type of group of elements");
26  params.addParam<std::vector<ExtraElementIDName>>("extra_id_name",
27  "Name(s) of the extra element ID(s) to use");
28  params.addParam<std::vector<std::vector<unsigned int>>>(
29  "extra_id",
30  "Specific ID(s), for each extra id name, for grouping elements. "
31  "If empty, all *valid* ids will be used to bin");
32 
33  // Order already arises from the block/ID bins
34  params.set<bool>("auto_sort") = false;
35  // Full replicated mesh is used on every rank to generate positions
36  params.set<bool>("auto_broadcast") = false;
37 
38  return params;
39 }
40 
42  : Positions(parameters),
43  BlockRestrictable(this),
44  _mesh(_fe_problem.mesh()),
45  _group_type(getParam<MooseEnum>("grouping_type"))
46 {
47  // We are not excluding using both block restriction and extra element ids
48  if (_group_type == "extra_id" || _group_type == "block_and_extra_id")
49  {
50  _extra_id_names = getParam<std::vector<ExtraElementIDName>>("extra_id_name");
51  for (const auto & name : _extra_id_names)
52  _extra_id_indices.push_back(_mesh.getMesh().get_elem_integer_index(name));
53 
54  if (isParamValid("extra_id"))
55  _extra_id_group_indices = getParam<std::vector<std::vector<unsigned int>>>("extra_id");
56  else
58 
59  if (_extra_id_group_indices.size() != _extra_id_names.size())
60  paramError("extra_id",
61  "Number of extra id names and the indices to select must match. "
62  "If you want all indices for an extra id, use an empty vector entry");
63 
64  // Can only have so many groups though, considering 4D max capability
65  if (_extra_id_indices.size() > unsigned(3 + blockRestricted()))
66  mooseError("Positions currently supports only up to 4D storage");
67  }
68  else
69  {
70  if (isParamValid("extra_id_name"))
71  paramError("extra_id_name",
72  "An extra id name was specified but elements are not grouped by extra ids");
73  if (isParamValid("extra_ids"))
74  paramError("extra_ids",
75  "An extra id was specified but elements are not grouped by extra ids");
76  }
77 
78  // Insert subdomain as an extra element id to simplify computation logic
79  if (blockRestricted() || !isParamValid("extra_id_name"))
80  {
81  _blocks_in_use = true;
82  _extra_id_names.insert(_extra_id_names.begin(), "block");
84  _extra_id_group_indices.insert(_extra_id_group_indices.begin(), std::vector<unsigned int>());
85  // Add real block restriction
86  if (blockRestricted())
87  for (const auto & block : blockIDs())
88  _extra_id_group_indices[0].push_back(block);
89  // Just add all blocks
90  else
91  for (const auto & block : meshBlockIDs())
92  _extra_id_group_indices[0].push_back(block);
93  }
94  else
95  _blocks_in_use = false;
96 
97  // Mesh is ready at construction
98  initialize();
99  // Sort the output (by XYZ) if requested
100  finalize();
101 }
102 
103 void
105 {
106  clearPositions();
107  // By default, initialize should be called on meshChanged()
108 
109  // If users did not specify a value for an extra element integer, they want all the bins
110  // We'll need to loop through the mesh to find the element extra ids
111  for (const auto i : index_range(_extra_id_group_indices))
112  {
113  auto & indices = _extra_id_group_indices[i];
114  if (indices.empty())
115  {
116  std::set<unsigned int> ids;
117  for (const auto & elem : _mesh.getMesh().active_local_element_ptr_range())
118  ids.insert(id(*elem, _extra_id_indices[i], _blocks_in_use && i == 0));
119  _mesh.comm().set_union(ids);
120  for (const auto & id : ids)
121  indices.push_back(id);
122  }
123  }
124 
125  // Allocate some vectors holding the volumes
126  std::vector<Real> volumes;
127  std::vector<std::vector<Real>> volumes_2d;
128  std::vector<std::vector<std::vector<Real>>> volumes_3d;
129  std::vector<std::vector<std::vector<std::vector<Real>>>> volumes_4d;
130 
131  // Default indexing: blocks, extra_id_name_1, extra_id_name_2 ...
132  // Allocate vectors holding the positions
133  if (_extra_id_names.size() == 1)
134  {
135  _positions.resize(_extra_id_group_indices[0].size());
136  volumes.resize(_positions.size());
137  }
138  else if (_extra_id_names.size() == 2)
139  {
140  _positions_2d.resize(_extra_id_group_indices[0].size());
141  volumes_2d.resize(_positions_2d.size());
142  for (auto & pos_group : _positions_2d)
143  pos_group.resize(_extra_id_group_indices[1].size());
144  for (auto & vol_group : volumes_2d)
145  vol_group.resize(_extra_id_group_indices[1].size());
146  }
147  else if (_extra_id_names.size() == 3)
148  {
149  _positions_3d.resize(_extra_id_group_indices[0].size());
150  volumes_3d.resize(_positions_3d.size());
151  for (auto & vec_pos_group : _positions_3d)
152  {
153  vec_pos_group.resize(_extra_id_group_indices[1].size());
154  for (auto & pos_group : vec_pos_group)
155  pos_group.resize(_extra_id_group_indices[2].size());
156  }
157  for (auto & vec_vol_group : volumes_3d)
158  {
159  vec_vol_group.resize(_extra_id_group_indices[1].size());
160  for (auto & vol_group : vec_vol_group)
161  vol_group.resize(_extra_id_group_indices[2].size());
162  }
163  }
164  else if (_extra_id_names.size() == 4)
165  {
166  _positions_4d.resize(_extra_id_group_indices[0].size());
167  volumes_4d.resize(_positions_4d.size());
168  for (auto & vec_vec_pos_group : _positions_4d)
169  {
170  vec_vec_pos_group.resize(_extra_id_group_indices[1].size());
171  for (auto & vec_pos_group : vec_vec_pos_group)
172  {
173  vec_pos_group.resize(_extra_id_group_indices[2].size());
174  for (auto & pos_group : vec_pos_group)
175  pos_group.resize(_extra_id_group_indices[3].size());
176  }
177  }
178  for (auto & vec_vec_vol_group : volumes_4d)
179  {
180  vec_vec_vol_group.resize(_extra_id_group_indices[1].size());
181  for (auto & vec_vol_group : vec_vec_vol_group)
182  {
183  vec_vol_group.resize(_extra_id_group_indices[2].size());
184  for (auto & vol_group : vec_vol_group)
185  vol_group.resize(_extra_id_group_indices[3].size());
186  }
187  }
188  }
189  else
190  mooseError("Too much dimensionality for positions");
191 
192  // To simplify retrieving the final positions vector
193  auto getNestedPositions =
194  [this](const std::vector<unsigned int> & indices) -> std::vector<Point> &
195  {
196  mooseAssert(indices[_extra_id_names.size() - 1] == 0, "Indexing issue");
197  if (_extra_id_names.size() == 1)
198  return _positions;
199  else if (_extra_id_names.size() == 2)
200  return _positions_2d[indices[0]];
201  else if (_extra_id_names.size() == 3)
202  return _positions_3d[indices[0]][indices[1]];
203  else
204  return _positions_4d[indices[0]][indices[1]][indices[2]];
205  };
206  auto getNestedVolumes = [this, &volumes, &volumes_2d, &volumes_3d, &volumes_4d](
207  const std::vector<unsigned int> & indices) -> std::vector<Real> &
208  {
209  mooseAssert(indices[_extra_id_names.size() - 1] == 0, "Indexing issue");
210  if (_extra_id_names.size() == 1)
211  return volumes;
212  else if (_extra_id_names.size() == 2)
213  return volumes_2d[indices[0]];
214  else if (_extra_id_names.size() == 3)
215  return volumes_3d[indices[0]][indices[1]];
216  else
217  return volumes_4d[indices[0]][indices[1]][indices[2]];
218  };
219 
220  std::vector<std::map<unsigned int, unsigned int>> positions_indexing(
221  _extra_id_group_indices.size());
222 
223  // Make index maps to go from the extra element id to the positions array index
224  for (const auto i : index_range(_extra_id_group_indices))
225  {
226  auto & indices = _extra_id_group_indices[i];
227  auto j = 0;
228  for (const auto extra_id : indices)
229  positions_indexing[i][extra_id] = j++;
230  }
231 
232  for (const auto & elem : _mesh.getMesh().active_local_element_ptr_range())
233  {
234  // Pre-compute the centroid, this is expensive but may be used for multiple element ids
235  const auto centroid = elem->true_centroid();
236  const auto volume = elem->volume();
237 
238  // Keeps track of indices in multi-D arrays
239  std::vector<unsigned int> previous_indices(4);
240 
241  for (const auto i : index_range(_extra_id_names))
242  {
243  auto iter =
244  positions_indexing[i].find(id(*elem, _extra_id_indices[i], _blocks_in_use && (i == 0)));
245  if (iter == positions_indexing[i].end())
246  break;
247  else if (_extra_id_names.size() == i + 1)
248  {
249  getNestedPositions(previous_indices)[iter->second] += volume * centroid;
250  getNestedVolumes(previous_indices)[iter->second] += volume;
251  break;
252  }
253  else
254  previous_indices[i] = iter->second;
255  }
256  }
257 
258  // Report the zero volumes
259  unsigned int num_zeros = 0;
260  if (_extra_id_names.size() == 1)
261  {
262  _mesh.comm().sum(volumes);
264  for (const auto & vol : volumes)
265  if (MooseUtils::absoluteFuzzyEqual(vol, 0))
266  num_zeros++;
267  }
268  if (_extra_id_names.size() == 2)
269  {
270  for (const auto i : index_range(volumes_2d))
271  {
272  _mesh.comm().sum(volumes_2d[i]);
273  _mesh.comm().sum(_positions_2d[i]);
274  }
275  for (const auto & vol_vec : volumes_2d)
276  for (const auto & vol : vol_vec)
277  if (MooseUtils::absoluteFuzzyEqual(vol, 0))
278  num_zeros++;
279  }
280  if (_extra_id_names.size() == 3)
281  {
282  for (const auto i : index_range(volumes_3d))
283  for (const auto j : index_range(volumes_3d[i]))
284  {
285  _mesh.comm().sum(volumes_3d[i][j]);
286  _mesh.comm().sum(_positions_3d[i][j]);
287  }
288  for (const auto & vol_vec_vec : volumes_3d)
289  for (const auto & vol_vec : vol_vec_vec)
290  for (const auto & vol : vol_vec)
291  if (MooseUtils::absoluteFuzzyEqual(vol, 0))
292  num_zeros++;
293  }
294  if (_extra_id_names.size() == 4)
295  {
296  for (const auto i : index_range(volumes_4d))
297  for (const auto j : index_range(volumes_4d[i]))
298  for (const auto k : index_range(volumes_4d[i][j]))
299  {
300  _mesh.comm().sum(volumes_4d[i][j][k]);
301  _mesh.comm().sum(_positions_4d[i][j][k]);
302  }
303  for (const auto & vol_vec_vec_vec : volumes_4d)
304  for (const auto & vol_vec_vec : vol_vec_vec_vec)
305  for (const auto & vol_vec : vol_vec_vec)
306  for (const auto & vol : vol_vec)
307  if (MooseUtils::absoluteFuzzyEqual(vol, 0))
308  num_zeros++;
309  }
310  if (num_zeros)
311  mooseWarning(std::to_string(num_zeros) +
312  " zero volume bins detected during group centroid position calculation. "
313  "The corresponding positions will be removed from consideration.");
314 
315  // Renormalize by the total bin volumes
316  if (_extra_id_names.size() == 1)
317  for (MooseIndex(_positions) i = 0; i < _positions.size(); i++)
318  {
319  if (volumes[i] != 0)
320  _positions[i] /= volumes[i];
321  else
322  {
323  _positions.erase(_positions.begin() + i);
324  volumes.erase(volumes.begin() + i);
325  i--;
326  }
327  }
328  else if (_extra_id_names.size() == 2)
329  for (const auto i : index_range(_positions_2d))
330  for (MooseIndex(_positions) j = 0; j < _positions_2d[i].size(); j++)
331  {
332  if (volumes_2d[i][j] != 0)
333  _positions_2d[i][j] /= volumes_2d[i][j];
334  else
335  {
336  _positions_2d[i].erase(_positions_2d[i].begin() + j);
337  volumes_2d[i].erase(volumes_2d[i].begin() + j);
338  j--;
339  }
340  }
341  else if (_extra_id_names.size() == 3)
342  for (const auto i : index_range(_positions_3d))
343  for (const auto j : index_range(_positions_3d[i]))
344  for (MooseIndex(_positions) k = 0; k < _positions_3d[i][j].size(); k++)
345  {
346  if (volumes_3d[i][j][k] != 0)
347  _positions_3d[i][j][k] /= volumes_3d[i][j][k];
348  else
349  {
350  _positions_3d[i][j].erase(_positions_3d[i][j].begin() + k);
351  volumes_3d[i][j].erase(volumes_3d[i][j].begin() + k);
352  k--;
353  }
354  }
355  else if (_extra_id_names.size() == 4)
356  for (const auto i : index_range(_positions_4d))
357  for (const auto j : index_range(_positions_4d[i]))
358  for (const auto k : index_range(_positions_4d[i][j]))
359  for (MooseIndex(_positions) l = 0; l < _positions_4d[i][j][k].size(); l++)
360  {
361  if (volumes_4d[i][j][k][l] != 0)
362  _positions_4d[i][j][k][l] /= volumes_4d[i][j][k][l];
363  else
364  {
365  _positions_4d[i][j][k].erase(_positions_4d[i][j][k].begin() + l);
366  volumes_4d[i][j][k].erase(volumes_4d[i][j][k].begin() + l);
367  l--;
368  }
369  }
370 
371  // Fill the 1D position vector
373  _initialized = true;
374 }
375 
376 unsigned int
377 ElementGroupCentroidPositions::id(const Elem & elem, unsigned int id_index, bool use_subdomains)
378 {
379  mooseAssert(!use_subdomains || (id_index == std::numeric_limits<unsigned short>::max()),
380  "We do not expect a valid element extra integer index for subdomains");
381  if (use_subdomains)
382  return elem.subdomain_id();
383  else
384  return elem.get_extra_integer(id_index);
385 }
ElementGroupCentroidPositions(const InputParameters &parameters)
std::vector< ExtraElementIDName > _extra_id_names
The names of the extra element ids to use to make groups.
void initialize() override
In charge of computing / loading the positions.
static InputParameters validParams()
Definition: Positions.C:15
bool absoluteFuzzyEqual(const T &var1, const T2 &var2, const T3 &tol=libMesh::TOLERANCE *libMesh::TOLERANCE)
Function to check whether two variables are equal within an absolute tolerance.
Definition: MooseUtils.h:380
unsigned int id(const Elem &elem, unsigned int id_index, bool use_subdomains)
static MooseEnum groupTypeEnum()
{ How to group elements, to compute centroids for these groups
void paramError(const std::string &param, Args... args) const
Emits an error prefixed with the file and line number of the given param (from the input file) along ...
Definition: MooseBase.h:435
const T & getParam(const std::string &name) const
Retrieve a parameter for the object.
Definition: MooseBase.h:384
void clearPositions()
Clear all positions vectors.
Definition: Positions.C:154
Positions objects are under the hood Reporters.
Definition: Positions.h:20
T & set(const std::string &name, bool quiet_mode=false)
Returns a writable reference to the named parameters.
MeshBase & mesh
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system...
const Parallel::Communicator & comm() const
virtual const std::set< SubdomainID > & blockIDs() const
Return the block subdomain ids for this object Note, if this is not block restricted, this function returns all mesh subdomain ids.
virtual bool blockRestricted() const
Returns true if this object has been restricted to a block.
void unrollMultiDPositions()
Unrolls the multi-dimensional position vectors.
Definition: Positions.C:163
auto max(const L &left, const R &right)
bool _initialized
Whether the positions object has been initialized. This must be set by derived objects.
Definition: Positions.h:116
static InputParameters validParams()
registerMooseObject("MooseApp", ElementGroupCentroidPositions)
std::vector< std::vector< Point > > _positions_2d
2D storage for all the positions
Definition: Positions.h:95
const std::string & name() const
Get the name of the class.
Definition: MooseBase.h:99
Positions from centroids of groups of elements (subdomains, extra element ids) in the mesh...
MeshBase & getMesh()
Accessor for the underlying libMesh Mesh object.
Definition: MooseMesh.C:3448
std::vector< Point > & _positions
For now, only the 1D vector will be shared across all ranks.
Definition: Positions.h:92
This is a "smart" enum class intended to replace many of the shortcomings in the C++ enum type It sho...
Definition: MooseEnum.h:33
std::vector< std::vector< std::vector< Point > > > _positions_3d
3D storage for all the positions
Definition: Positions.h:98
std::vector< std::vector< unsigned int > > _extra_id_group_indices
The particular extra id values, for each extra id of interest.
Real volume(const MeshBase &mesh, unsigned int dim=libMesh::invalid_uint)
An interface that restricts an object to subdomains via the &#39;blocks&#39; input parameter.
void mooseWarning(Args &&... args) const
Emits a warning prefixed with object name and type.
Definition: MooseBase.h:295
const std::set< SubdomainID > & meshBlockIDs() const
Return all of the SubdomainIDs for the mesh.
virtual void finalize() override
In charge of reduction across all ranks & sorting for consistent output.
Definition: Positions.C:220
void mooseError(Args &&... args) const
Emits an error prefixed with object name and type and optionally a file path to the top-level block p...
Definition: MooseBase.h:267
void addClassDescription(const std::string &doc_string)
This method adds a description of the class that will be displayed in the input file syntax dump...
void addParam(const std::string &name, const S &value, const std::string &doc_string)
These methods add an optional parameter and a documentation string to the InputParameters object...
std::vector< unsigned int > _extra_id_indices
The indices of the extra element ids to use to make groups.
bool isParamValid(const std::string &name) const
Test if the supplied parameter is valid.
Definition: MooseBase.h:195
std::vector< std::vector< std::vector< std::vector< Point > > > > _positions_4d
4D storage for all the positions : space & time
Definition: Positions.h:101
auto index_range(const T &sizable)
void set_union(T &data, const unsigned int root_id) const