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<dof_id_type>>>(
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(_subproblem.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<dof_id_type>>>("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<dof_id_type>());
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<dof_id_type> ids;
117  for (const auto & elem : _mesh.getMesh().active_local_element_ptr_range())
118  {
119  auto eeid = id(*elem, _extra_id_indices[i], _blocks_in_use && i == 0);
120  if (eeid != DofObject::invalid_id)
121  ids.insert(eeid);
122  }
123  _mesh.comm().set_union(ids);
124  for (const auto & id : ids)
125  indices.push_back(id);
126  }
127  }
128 
129  // Allocate some vectors holding the volumes
130  std::vector<Real> volumes;
131  std::vector<std::vector<Real>> volumes_2d;
132  std::vector<std::vector<std::vector<Real>>> volumes_3d;
133  std::vector<std::vector<std::vector<std::vector<Real>>>> volumes_4d;
134 
135  // Default indexing: blocks, extra_id_name_1, extra_id_name_2 ...
136  // Allocate vectors holding the positions
137  if (_extra_id_names.size() == 1)
138  {
139  _positions.resize(_extra_id_group_indices[0].size());
140  volumes.resize(_positions.size());
141  }
142  else if (_extra_id_names.size() == 2)
143  {
144  _positions_2d.resize(_extra_id_group_indices[0].size());
145  volumes_2d.resize(_positions_2d.size());
146  for (auto & pos_group : _positions_2d)
147  pos_group.resize(_extra_id_group_indices[1].size());
148  for (auto & vol_group : volumes_2d)
149  vol_group.resize(_extra_id_group_indices[1].size());
150  }
151  else if (_extra_id_names.size() == 3)
152  {
153  _positions_3d.resize(_extra_id_group_indices[0].size());
154  volumes_3d.resize(_positions_3d.size());
155  for (auto & vec_pos_group : _positions_3d)
156  {
157  vec_pos_group.resize(_extra_id_group_indices[1].size());
158  for (auto & pos_group : vec_pos_group)
159  pos_group.resize(_extra_id_group_indices[2].size());
160  }
161  for (auto & vec_vol_group : volumes_3d)
162  {
163  vec_vol_group.resize(_extra_id_group_indices[1].size());
164  for (auto & vol_group : vec_vol_group)
165  vol_group.resize(_extra_id_group_indices[2].size());
166  }
167  }
168  else if (_extra_id_names.size() == 4)
169  {
170  _positions_4d.resize(_extra_id_group_indices[0].size());
171  volumes_4d.resize(_positions_4d.size());
172  for (auto & vec_vec_pos_group : _positions_4d)
173  {
174  vec_vec_pos_group.resize(_extra_id_group_indices[1].size());
175  for (auto & vec_pos_group : vec_vec_pos_group)
176  {
177  vec_pos_group.resize(_extra_id_group_indices[2].size());
178  for (auto & pos_group : vec_pos_group)
179  pos_group.resize(_extra_id_group_indices[3].size());
180  }
181  }
182  for (auto & vec_vec_vol_group : volumes_4d)
183  {
184  vec_vec_vol_group.resize(_extra_id_group_indices[1].size());
185  for (auto & vec_vol_group : vec_vec_vol_group)
186  {
187  vec_vol_group.resize(_extra_id_group_indices[2].size());
188  for (auto & vol_group : vec_vol_group)
189  vol_group.resize(_extra_id_group_indices[3].size());
190  }
191  }
192  }
193  else
194  mooseError("Too much dimensionality for positions");
195 
196  // To simplify retrieving the final positions vector
197  auto getNestedPositions =
198  [this](const std::vector<unsigned int> & indices) -> std::vector<Point> &
199  {
200  mooseAssert(indices[_extra_id_names.size() - 1] == 0, "Indexing issue");
201  if (_extra_id_names.size() == 1)
202  return _positions;
203  else if (_extra_id_names.size() == 2)
204  return _positions_2d[indices[0]];
205  else if (_extra_id_names.size() == 3)
206  return _positions_3d[indices[0]][indices[1]];
207  else
208  return _positions_4d[indices[0]][indices[1]][indices[2]];
209  };
210  auto getNestedVolumes = [this, &volumes, &volumes_2d, &volumes_3d, &volumes_4d](
211  const std::vector<unsigned int> & indices) -> std::vector<Real> &
212  {
213  mooseAssert(indices[_extra_id_names.size() - 1] == 0, "Indexing issue");
214  if (_extra_id_names.size() == 1)
215  return volumes;
216  else if (_extra_id_names.size() == 2)
217  return volumes_2d[indices[0]];
218  else if (_extra_id_names.size() == 3)
219  return volumes_3d[indices[0]][indices[1]];
220  else
221  return volumes_4d[indices[0]][indices[1]][indices[2]];
222  };
223 
224  std::vector<std::map<unsigned int, unsigned int>> positions_indexing(
225  _extra_id_group_indices.size());
226 
227  // Make index maps to go from the extra element id to the positions array index
228  for (const auto i : index_range(_extra_id_group_indices))
229  {
230  auto & indices = _extra_id_group_indices[i];
231  auto j = 0;
232  for (const auto extra_id : indices)
233  positions_indexing[i][extra_id] = j++;
234  }
235 
236  for (const auto & elem : _mesh.getMesh().active_local_element_ptr_range())
237  {
238  // Pre-compute the centroid, this is expensive but may be used for multiple element ids
239  const auto centroid = elem->true_centroid();
240  const auto volume = elem->volume();
241 
242  // Keeps track of indices in multi-D arrays
243  std::vector<unsigned int> previous_indices(4);
244 
245  for (const auto i : index_range(_extra_id_names))
246  {
247  auto iter =
248  positions_indexing[i].find(id(*elem, _extra_id_indices[i], _blocks_in_use && (i == 0)));
249  if (iter == positions_indexing[i].end())
250  break;
251  else if (_extra_id_names.size() == i + 1)
252  {
253  getNestedPositions(previous_indices)[iter->second] += volume * centroid;
254  getNestedVolumes(previous_indices)[iter->second] += volume;
255  break;
256  }
257  else
258  previous_indices[i] = iter->second;
259  }
260  }
261 
262  // Report the zero volumes
263  unsigned int num_zeros = 0;
264  if (_extra_id_names.size() == 1)
265  {
266  _mesh.comm().sum(volumes);
268  for (const auto & vol : volumes)
269  if (MooseUtils::absoluteFuzzyEqual(vol, 0))
270  num_zeros++;
271  }
272  if (_extra_id_names.size() == 2)
273  {
274  for (const auto i : index_range(volumes_2d))
275  {
276  _mesh.comm().sum(volumes_2d[i]);
277  _mesh.comm().sum(_positions_2d[i]);
278  }
279  for (const auto & vol_vec : volumes_2d)
280  for (const auto & vol : vol_vec)
281  if (MooseUtils::absoluteFuzzyEqual(vol, 0))
282  num_zeros++;
283  }
284  if (_extra_id_names.size() == 3)
285  {
286  for (const auto i : index_range(volumes_3d))
287  for (const auto j : index_range(volumes_3d[i]))
288  {
289  _mesh.comm().sum(volumes_3d[i][j]);
290  _mesh.comm().sum(_positions_3d[i][j]);
291  }
292  for (const auto & vol_vec_vec : volumes_3d)
293  for (const auto & vol_vec : vol_vec_vec)
294  for (const auto & vol : vol_vec)
295  if (MooseUtils::absoluteFuzzyEqual(vol, 0))
296  num_zeros++;
297  }
298  if (_extra_id_names.size() == 4)
299  {
300  for (const auto i : index_range(volumes_4d))
301  for (const auto j : index_range(volumes_4d[i]))
302  for (const auto k : index_range(volumes_4d[i][j]))
303  {
304  _mesh.comm().sum(volumes_4d[i][j][k]);
305  _mesh.comm().sum(_positions_4d[i][j][k]);
306  }
307  for (const auto & vol_vec_vec_vec : volumes_4d)
308  for (const auto & vol_vec_vec : vol_vec_vec_vec)
309  for (const auto & vol_vec : vol_vec_vec)
310  for (const auto & vol : vol_vec)
311  if (MooseUtils::absoluteFuzzyEqual(vol, 0))
312  num_zeros++;
313  }
314  if (num_zeros)
315  mooseWarning(std::to_string(num_zeros) +
316  " zero volume bins detected during group centroid position calculation. "
317  "The corresponding positions will be removed from consideration.");
318 
319  // Renormalize by the total bin volumes
320  if (_extra_id_names.size() == 1)
321  for (MooseIndex(_positions) i = 0; i < _positions.size(); i++)
322  {
323  if (volumes[i] != 0)
324  _positions[i] /= volumes[i];
325  else
326  {
327  _positions.erase(_positions.begin() + i);
328  volumes.erase(volumes.begin() + i);
329  i--;
330  }
331  }
332  else if (_extra_id_names.size() == 2)
333  for (const auto i : index_range(_positions_2d))
334  for (MooseIndex(_positions) j = 0; j < _positions_2d[i].size(); j++)
335  {
336  if (volumes_2d[i][j] != 0)
337  _positions_2d[i][j] /= volumes_2d[i][j];
338  else
339  {
340  _positions_2d[i].erase(_positions_2d[i].begin() + j);
341  volumes_2d[i].erase(volumes_2d[i].begin() + j);
342  j--;
343  }
344  }
345  else if (_extra_id_names.size() == 3)
346  for (const auto i : index_range(_positions_3d))
347  for (const auto j : index_range(_positions_3d[i]))
348  for (MooseIndex(_positions) k = 0; k < _positions_3d[i][j].size(); k++)
349  {
350  if (volumes_3d[i][j][k] != 0)
351  _positions_3d[i][j][k] /= volumes_3d[i][j][k];
352  else
353  {
354  _positions_3d[i][j].erase(_positions_3d[i][j].begin() + k);
355  volumes_3d[i][j].erase(volumes_3d[i][j].begin() + k);
356  k--;
357  }
358  }
359  else if (_extra_id_names.size() == 4)
360  for (const auto i : index_range(_positions_4d))
361  for (const auto j : index_range(_positions_4d[i]))
362  for (const auto k : index_range(_positions_4d[i][j]))
363  for (MooseIndex(_positions) l = 0; l < _positions_4d[i][j][k].size(); l++)
364  {
365  if (volumes_4d[i][j][k][l] != 0)
366  _positions_4d[i][j][k][l] /= volumes_4d[i][j][k][l];
367  else
368  {
369  _positions_4d[i][j][k].erase(_positions_4d[i][j][k].begin() + l);
370  volumes_4d[i][j][k].erase(volumes_4d[i][j][k].begin() + l);
371  l--;
372  }
373  }
374 
375  // Fill the 1D position vector
377  _initialized = true;
378 }
379 
381 ElementGroupCentroidPositions::id(const Elem & elem, unsigned int id_index, bool use_subdomains)
382 {
383  mooseAssert(!use_subdomains || (id_index == std::numeric_limits<unsigned short>::max()),
384  "We do not expect a valid element extra integer index for subdomains");
385  if (use_subdomains)
386  return elem.subdomain_id();
387  else
388  return elem.get_extra_integer(id_index);
389 }
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:382
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:439
const T & getParam(const std::string &name) const
Retrieve a parameter for the object.
Definition: MooseBase.h:388
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()
void mooseWarning(Args &&... args) const
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:103
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:3469
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
dof_id_type id(const Elem &elem, unsigned int id_index, bool use_subdomains)
std::vector< std::vector< std::vector< Point > > > _positions_3d
3D storage for all the positions
Definition: Positions.h:98
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.
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:271
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:199
std::vector< std::vector< dof_id_type > > _extra_id_group_indices
The particular extra id values, for each extra id of interest.
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)
uint8_t dof_id_type
void set_union(T &data, const unsigned int root_id) const