LCOV - code coverage report
Current view: top level - src/positions - ElementGroupCentroidPositions.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 419b9d Lines: 200 215 93.0 %
Date: 2025-08-08 20:01:16 Functions: 6 6 100.0 %
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             : #include "ElementGroupCentroidPositions.h"
      11             : 
      12             : #include "libmesh/parallel_algebra.h"
      13             : 
      14             : registerMooseObject("MooseApp", ElementGroupCentroidPositions);
      15             : 
      16             : InputParameters
      17       14421 : ElementGroupCentroidPositions::validParams()
      18             : {
      19       14421 :   InputParameters params = Positions::validParams();
      20       14421 :   params += BlockRestrictable::validParams();
      21       14421 :   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       14421 :   params.addParam<MooseEnum>("grouping_type", groupTypeEnum(), "Type of group of elements");
      26       14421 :   params.addParam<std::vector<ExtraElementIDName>>("extra_id_name",
      27             :                                                    "Name(s) of the extra element ID(s) to use");
      28       14421 :   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       14421 :   params.set<bool>("auto_sort") = false;
      35             :   // Full replicated mesh is used on every rank to generate positions
      36       14421 :   params.set<bool>("auto_broadcast") = false;
      37             : 
      38       14421 :   return params;
      39           0 : }
      40             : 
      41          78 : ElementGroupCentroidPositions::ElementGroupCentroidPositions(const InputParameters & parameters)
      42             :   : Positions(parameters),
      43             :     BlockRestrictable(this),
      44         156 :     _mesh(_fe_problem.mesh()),
      45          78 :     _group_type(getParam<MooseEnum>("grouping_type"))
      46             : {
      47             :   // We are not excluding using both block restriction and extra element ids
      48          78 :   if (_group_type == "extra_id" || _group_type == "block_and_extra_id")
      49             :   {
      50          52 :     _extra_id_names = getParam<std::vector<ExtraElementIDName>>("extra_id_name");
      51         169 :     for (const auto & name : _extra_id_names)
      52         117 :       _extra_id_indices.push_back(_mesh.getMesh().get_elem_integer_index(name));
      53             : 
      54          52 :     if (isParamValid("extra_id"))
      55          52 :       _extra_id_group_indices = getParam<std::vector<std::vector<unsigned int>>>("extra_id");
      56             :     else
      57           0 :       _extra_id_group_indices.resize(_extra_id_names.size());
      58             : 
      59          52 :     if (_extra_id_group_indices.size() != _extra_id_names.size())
      60           0 :       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          52 :     if (_extra_id_indices.size() > unsigned(3 + blockRestricted()))
      66           0 :       mooseError("Positions currently supports only up to 4D storage");
      67             :   }
      68             :   else
      69             :   {
      70          26 :     if (isParamValid("extra_id_name"))
      71           0 :       paramError("extra_id_name",
      72             :                  "An extra id name was specified but elements are not grouped by extra ids");
      73          26 :     if (isParamValid("extra_ids"))
      74           0 :       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          78 :   if (blockRestricted() || !isParamValid("extra_id_name"))
      80             :   {
      81          65 :     _blocks_in_use = true;
      82          65 :     _extra_id_names.insert(_extra_id_names.begin(), "block");
      83          65 :     _extra_id_indices.insert(_extra_id_indices.begin(), std::numeric_limits<unsigned short>::max());
      84          65 :     _extra_id_group_indices.insert(_extra_id_group_indices.begin(), std::vector<unsigned int>());
      85             :     // Add real block restriction
      86          65 :     if (blockRestricted())
      87         143 :       for (const auto & block : blockIDs())
      88          91 :         _extra_id_group_indices[0].push_back(block);
      89             :     // Just add all blocks
      90             :     else
      91          52 :       for (const auto & block : meshBlockIDs())
      92          39 :         _extra_id_group_indices[0].push_back(block);
      93             :   }
      94             :   else
      95          13 :     _blocks_in_use = false;
      96             : 
      97             :   // Mesh is ready at construction
      98          78 :   initialize();
      99             :   // Sort the output (by XYZ) if requested
     100          78 :   finalize();
     101          78 : }
     102             : 
     103             : void
     104          78 : ElementGroupCentroidPositions::initialize()
     105             : {
     106          78 :   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         260 :   for (const auto i : index_range(_extra_id_group_indices))
     112             :   {
     113         182 :     auto & indices = _extra_id_group_indices[i];
     114         182 :     if (indices.empty())
     115             :     {
     116          26 :       std::set<unsigned int> ids;
     117       71706 :       for (const auto & elem : _mesh.getMesh().active_local_element_ptr_range())
     118       71706 :         ids.insert(id(*elem, _extra_id_indices[i], _blocks_in_use && i == 0));
     119          26 :       _mesh.comm().set_union(ids);
     120         130 :       for (const auto & id : ids)
     121         104 :         indices.push_back(id);
     122          26 :     }
     123             :   }
     124             : 
     125             :   // Allocate some vectors holding the volumes
     126          78 :   std::vector<Real> volumes;
     127          78 :   std::vector<std::vector<Real>> volumes_2d;
     128          78 :   std::vector<std::vector<std::vector<Real>>> volumes_3d;
     129          78 :   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          78 :   if (_extra_id_names.size() == 1)
     134             :   {
     135          26 :     _positions.resize(_extra_id_group_indices[0].size());
     136          26 :     volumes.resize(_positions.size());
     137             :   }
     138          52 :   else if (_extra_id_names.size() == 2)
     139             :   {
     140          13 :     _positions_2d.resize(_extra_id_group_indices[0].size());
     141          13 :     volumes_2d.resize(_positions_2d.size());
     142          39 :     for (auto & pos_group : _positions_2d)
     143          26 :       pos_group.resize(_extra_id_group_indices[1].size());
     144          39 :     for (auto & vol_group : volumes_2d)
     145          26 :       vol_group.resize(_extra_id_group_indices[1].size());
     146             :   }
     147          39 :   else if (_extra_id_names.size() == 3)
     148             :   {
     149          26 :     _positions_3d.resize(_extra_id_group_indices[0].size());
     150          26 :     volumes_3d.resize(_positions_3d.size());
     151          65 :     for (auto & vec_pos_group : _positions_3d)
     152             :     {
     153          39 :       vec_pos_group.resize(_extra_id_group_indices[1].size());
     154         117 :       for (auto & pos_group : vec_pos_group)
     155          78 :         pos_group.resize(_extra_id_group_indices[2].size());
     156             :     }
     157          65 :     for (auto & vec_vol_group : volumes_3d)
     158             :     {
     159          39 :       vec_vol_group.resize(_extra_id_group_indices[1].size());
     160         117 :       for (auto & vol_group : vec_vol_group)
     161          78 :         vol_group.resize(_extra_id_group_indices[2].size());
     162             :     }
     163             :   }
     164          13 :   else if (_extra_id_names.size() == 4)
     165             :   {
     166          13 :     _positions_4d.resize(_extra_id_group_indices[0].size());
     167          13 :     volumes_4d.resize(_positions_4d.size());
     168          39 :     for (auto & vec_vec_pos_group : _positions_4d)
     169             :     {
     170          26 :       vec_vec_pos_group.resize(_extra_id_group_indices[1].size());
     171          52 :       for (auto & vec_pos_group : vec_vec_pos_group)
     172             :       {
     173          26 :         vec_pos_group.resize(_extra_id_group_indices[2].size());
     174         130 :         for (auto & pos_group : vec_pos_group)
     175         104 :           pos_group.resize(_extra_id_group_indices[3].size());
     176             :       }
     177             :     }
     178          39 :     for (auto & vec_vec_vol_group : volumes_4d)
     179             :     {
     180          26 :       vec_vec_vol_group.resize(_extra_id_group_indices[1].size());
     181          52 :       for (auto & vec_vol_group : vec_vec_vol_group)
     182             :       {
     183          26 :         vec_vol_group.resize(_extra_id_group_indices[2].size());
     184         130 :         for (auto & vol_group : vec_vol_group)
     185         104 :           vol_group.resize(_extra_id_group_indices[3].size());
     186             :       }
     187             :     }
     188             :   }
     189             :   else
     190           0 :     mooseError("Too much dimensionality for positions");
     191             : 
     192             :   // To simplify retrieving the final positions vector
     193             :   auto getNestedPositions =
     194      149760 :       [this](const std::vector<unsigned int> & indices) -> std::vector<Point> &
     195             :   {
     196             :     mooseAssert(indices[_extra_id_names.size() - 1] == 0, "Indexing issue");
     197       64000 :     if (_extra_id_names.size() == 1)
     198       51200 :       return _positions;
     199       12800 :     else if (_extra_id_names.size() == 2)
     200        3840 :       return _positions_2d[indices[0]];
     201        8960 :     else if (_extra_id_names.size() == 3)
     202        6400 :       return _positions_3d[indices[0]][indices[1]];
     203             :     else
     204        2560 :       return _positions_4d[indices[0]][indices[1]][indices[2]];
     205          78 :   };
     206       64000 :   auto getNestedVolumes = [this, &volumes, &volumes_2d, &volumes_3d, &volumes_4d](
     207       98560 :                               const std::vector<unsigned int> & indices) -> std::vector<Real> &
     208             :   {
     209             :     mooseAssert(indices[_extra_id_names.size() - 1] == 0, "Indexing issue");
     210       64000 :     if (_extra_id_names.size() == 1)
     211       51200 :       return volumes;
     212       12800 :     else if (_extra_id_names.size() == 2)
     213        3840 :       return volumes_2d[indices[0]];
     214        8960 :     else if (_extra_id_names.size() == 3)
     215        6400 :       return volumes_3d[indices[0]][indices[1]];
     216             :     else
     217        2560 :       return volumes_4d[indices[0]][indices[1]][indices[2]];
     218          78 :   };
     219             : 
     220             :   std::vector<std::map<unsigned int, unsigned int>> positions_indexing(
     221          78 :       _extra_id_group_indices.size());
     222             : 
     223             :   // Make index maps to go from the extra element id to the positions array index
     224         260 :   for (const auto i : index_range(_extra_id_group_indices))
     225             :   {
     226         182 :     auto & indices = _extra_id_group_indices[i];
     227         182 :     auto j = 0;
     228         637 :     for (const auto extra_id : indices)
     229         455 :       positions_indexing[i][extra_id] = j++;
     230             :   }
     231             : 
     232      430158 :   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      215040 :     const auto centroid = elem->true_centroid();
     236      215040 :     const auto volume = elem->volume();
     237             : 
     238             :     // Keeps track of indices in multi-D arrays
     239      215040 :     std::vector<unsigned int> previous_indices(4);
     240             : 
     241      321920 :     for (const auto i : index_range(_extra_id_names))
     242             :     {
     243             :       auto iter =
     244      321920 :           positions_indexing[i].find(id(*elem, _extra_id_indices[i], _blocks_in_use && (i == 0)));
     245      321920 :       if (iter == positions_indexing[i].end())
     246      215040 :         break;
     247      170880 :       else if (_extra_id_names.size() == i + 1)
     248             :       {
     249       64000 :         getNestedPositions(previous_indices)[iter->second] += volume * centroid;
     250       64000 :         getNestedVolumes(previous_indices)[iter->second] += volume;
     251       64000 :         break;
     252             :       }
     253             :       else
     254      106880 :         previous_indices[i] = iter->second;
     255             :     }
     256      215118 :   }
     257             : 
     258             :   // Report the zero volumes
     259          78 :   unsigned int num_zeros = 0;
     260          78 :   if (_extra_id_names.size() == 1)
     261             :   {
     262          26 :     _mesh.comm().sum(volumes);
     263          26 :     _mesh.comm().sum(_positions);
     264          78 :     for (const auto & vol : volumes)
     265          52 :       if (MooseUtils::absoluteFuzzyEqual(vol, 0))
     266           0 :         num_zeros++;
     267             :   }
     268          78 :   if (_extra_id_names.size() == 2)
     269             :   {
     270          39 :     for (const auto i : index_range(volumes_2d))
     271             :     {
     272          26 :       _mesh.comm().sum(volumes_2d[i]);
     273          26 :       _mesh.comm().sum(_positions_2d[i]);
     274             :     }
     275          39 :     for (const auto & vol_vec : volumes_2d)
     276         104 :       for (const auto & vol : vol_vec)
     277          78 :         if (MooseUtils::absoluteFuzzyEqual(vol, 0))
     278           0 :           num_zeros++;
     279             :   }
     280          78 :   if (_extra_id_names.size() == 3)
     281             :   {
     282          65 :     for (const auto i : index_range(volumes_3d))
     283         117 :       for (const auto j : index_range(volumes_3d[i]))
     284             :       {
     285          78 :         _mesh.comm().sum(volumes_3d[i][j]);
     286          78 :         _mesh.comm().sum(_positions_3d[i][j]);
     287             :       }
     288          65 :     for (const auto & vol_vec_vec : volumes_3d)
     289         117 :       for (const auto & vol_vec : vol_vec_vec)
     290         364 :         for (const auto & vol : vol_vec)
     291         286 :           if (MooseUtils::absoluteFuzzyEqual(vol, 0))
     292          52 :             num_zeros++;
     293             :   }
     294          78 :   if (_extra_id_names.size() == 4)
     295             :   {
     296          39 :     for (const auto i : index_range(volumes_4d))
     297          52 :       for (const auto j : index_range(volumes_4d[i]))
     298         130 :         for (const auto k : index_range(volumes_4d[i][j]))
     299             :         {
     300         104 :           _mesh.comm().sum(volumes_4d[i][j][k]);
     301         104 :           _mesh.comm().sum(_positions_4d[i][j][k]);
     302             :         }
     303          39 :     for (const auto & vol_vec_vec_vec : volumes_4d)
     304          52 :       for (const auto & vol_vec_vec : vol_vec_vec_vec)
     305         130 :         for (const auto & vol_vec : vol_vec_vec)
     306         520 :           for (const auto & vol : vol_vec)
     307         416 :             if (MooseUtils::absoluteFuzzyEqual(vol, 0))
     308         312 :               num_zeros++;
     309             :   }
     310          78 :   if (num_zeros)
     311          26 :     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          78 :   if (_extra_id_names.size() == 1)
     317          78 :     for (MooseIndex(_positions) i = 0; i < _positions.size(); i++)
     318             :     {
     319          52 :       if (volumes[i] != 0)
     320          52 :         _positions[i] /= volumes[i];
     321             :       else
     322             :       {
     323           0 :         _positions.erase(_positions.begin() + i);
     324           0 :         volumes.erase(volumes.begin() + i);
     325           0 :         i--;
     326             :       }
     327             :     }
     328          52 :   else if (_extra_id_names.size() == 2)
     329          39 :     for (const auto i : index_range(_positions_2d))
     330         104 :       for (MooseIndex(_positions) j = 0; j < _positions_2d[i].size(); j++)
     331             :       {
     332          78 :         if (volumes_2d[i][j] != 0)
     333          78 :           _positions_2d[i][j] /= volumes_2d[i][j];
     334             :         else
     335             :         {
     336           0 :           _positions_2d[i].erase(_positions_2d[i].begin() + j);
     337           0 :           volumes_2d[i].erase(volumes_2d[i].begin() + j);
     338           0 :           j--;
     339             :         }
     340             :       }
     341          39 :   else if (_extra_id_names.size() == 3)
     342          65 :     for (const auto i : index_range(_positions_3d))
     343         117 :       for (const auto j : index_range(_positions_3d[i]))
     344         364 :         for (MooseIndex(_positions) k = 0; k < _positions_3d[i][j].size(); k++)
     345             :         {
     346         286 :           if (volumes_3d[i][j][k] != 0)
     347         234 :             _positions_3d[i][j][k] /= volumes_3d[i][j][k];
     348             :           else
     349             :           {
     350          52 :             _positions_3d[i][j].erase(_positions_3d[i][j].begin() + k);
     351          52 :             volumes_3d[i][j].erase(volumes_3d[i][j].begin() + k);
     352          52 :             k--;
     353             :           }
     354             :         }
     355          13 :   else if (_extra_id_names.size() == 4)
     356          39 :     for (const auto i : index_range(_positions_4d))
     357          52 :       for (const auto j : index_range(_positions_4d[i]))
     358         130 :         for (const auto k : index_range(_positions_4d[i][j]))
     359         520 :           for (MooseIndex(_positions) l = 0; l < _positions_4d[i][j][k].size(); l++)
     360             :           {
     361         416 :             if (volumes_4d[i][j][k][l] != 0)
     362         104 :               _positions_4d[i][j][k][l] /= volumes_4d[i][j][k][l];
     363             :             else
     364             :             {
     365         312 :               _positions_4d[i][j][k].erase(_positions_4d[i][j][k].begin() + l);
     366         312 :               volumes_4d[i][j][k].erase(volumes_4d[i][j][k].begin() + l);
     367         312 :               l--;
     368             :             }
     369             :           }
     370             : 
     371             :   // Fill the 1D position vector
     372          78 :   unrollMultiDPositions();
     373          78 :   _initialized = true;
     374          78 : }
     375             : 
     376             : unsigned int
     377      393600 : 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      393600 :   if (use_subdomains)
     382      179200 :     return elem.subdomain_id();
     383             :   else
     384      214400 :     return elem.get_extra_integer(id_index);
     385             : }

Generated by: LCOV version 1.14