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

Generated by: LCOV version 1.14