LCOV - code coverage report
Current view: top level - src/reporters - MeshInfo.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 6f668f Lines: 227 227 100.0 %
Date: 2025-09-22 20:01:15 Functions: 17 17 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 "MeshInfo.h"
      11             : #include "SubProblem.h"
      12             : #include "libmesh/system.h"
      13             : #include "libmesh/equation_systems.h"
      14             : #include "libmesh/parallel_sync.h"
      15             : 
      16             : registerMooseObject("MooseApp", MeshInfo);
      17             : 
      18             : InputParameters
      19       15535 : MeshInfo::validParams()
      20             : {
      21       15535 :   InputParameters params = GeneralReporter::validParams();
      22       31070 :   params.addClassDescription(
      23             :       "Report mesh information, such as the number of elements, nodes, and degrees of freedom.");
      24             : 
      25             :   MultiMooseEnum items(
      26             :       "num_dofs num_dofs_nonlinear num_dofs_auxiliary num_elements num_nodes num_local_dofs "
      27             :       "num_local_dofs_nonlinear num_local_dofs_auxiliary num_local_elements num_local_nodes "
      28             :       "local_sidesets local_sideset_elems sidesets sideset_elems local_subdomains "
      29       31070 :       "local_subdomain_elems subdomains subdomain_elems");
      30       46605 :   params.addParam<MultiMooseEnum>(
      31             :       "items",
      32             :       items,
      33             :       "The iteration information to output, if nothing is provided everything will be output.");
      34             : 
      35       31070 :   return params;
      36       15535 : }
      37             : 
      38         371 : MeshInfo::MeshInfo(const InputParameters & parameters)
      39             :   : GeneralReporter(parameters),
      40         371 :     _items(getParam<MultiMooseEnum>("items")),
      41        1113 :     _num_dofs(declareHelper<unsigned int>("num_dofs", REPORTER_MODE_REPLICATED)),
      42        1113 :     _num_dofs_nl(declareHelper<unsigned int>("num_dofs_nonlinear", REPORTER_MODE_REPLICATED)),
      43        1113 :     _num_dofs_aux(declareHelper<unsigned int>("num_dofs_auxiliary", REPORTER_MODE_REPLICATED)),
      44         371 :     _num_dofs_constrained(
      45        1113 :         declareHelper<unsigned int>("num_dofs_constrained", REPORTER_MODE_REPLICATED)),
      46        1113 :     _num_elem(declareHelper<unsigned int>("num_elements", REPORTER_MODE_REPLICATED)),
      47        1113 :     _num_node(declareHelper<unsigned int>("num_nodes", REPORTER_MODE_REPLICATED)),
      48        1113 :     _num_local_dofs(declareHelper<unsigned int>("num_local_dofs", REPORTER_MODE_DISTRIBUTED)),
      49         371 :     _num_local_dofs_nl(
      50        1113 :         declareHelper<unsigned int>("num_dofs_local_nonlinear", REPORTER_MODE_DISTRIBUTED)),
      51         371 :     _num_local_dofs_aux(
      52        1113 :         declareHelper<unsigned int>("num_dofs_local_auxiliary", REPORTER_MODE_DISTRIBUTED)),
      53        1113 :     _num_local_elem(declareHelper<unsigned int>("num_local_elements", REPORTER_MODE_DISTRIBUTED)),
      54        1113 :     _num_local_node(declareHelper<unsigned int>("num_local_nodes", REPORTER_MODE_DISTRIBUTED)),
      55             : 
      56        1113 :     _local_sidesets(declareHelper<std::map<BoundaryID, SidesetInfo>>("local_sidesets",
      57             :                                                                      REPORTER_MODE_DISTRIBUTED)),
      58        1113 :     _local_sideset_elems(declareHelper<std::map<BoundaryID, SidesetInfo>>(
      59             :         "local_sideset_elems", REPORTER_MODE_DISTRIBUTED)),
      60        1113 :     _sidesets(declareHelper<std::map<BoundaryID, SidesetInfo>>("sidesets", REPORTER_MODE_ROOT)),
      61         371 :     _sideset_elems(
      62        1113 :         declareHelper<std::map<BoundaryID, SidesetInfo>>("sideset_elems", REPORTER_MODE_ROOT)),
      63             : 
      64        1113 :     _local_subdomains(declareHelper<std::map<SubdomainID, SubdomainInfo>>(
      65             :         "local_subdomains", REPORTER_MODE_DISTRIBUTED)),
      66        1113 :     _local_subdomain_elems(declareHelper<std::map<SubdomainID, SubdomainInfo>>(
      67             :         "local_subdomain_elems", REPORTER_MODE_DISTRIBUTED)),
      68         371 :     _subdomains(
      69        1113 :         declareHelper<std::map<SubdomainID, SubdomainInfo>>("subdomains", REPORTER_MODE_ROOT)),
      70         371 :     _subdomain_elems(
      71        1113 :         declareHelper<std::map<SubdomainID, SubdomainInfo>>("subdomain_elems", REPORTER_MODE_ROOT)),
      72             : 
      73         371 :     _equation_systems(_fe_problem.es()),
      74         371 :     _nonlinear_system(_fe_problem.es().get_system("nl0")),
      75         371 :     _aux_system(_fe_problem.es().get_system("aux0")),
      76         742 :     _mesh(_fe_problem.mesh().getMesh())
      77             : {
      78         371 : }
      79             : 
      80             : void
      81         344 : MeshInfo::execute()
      82             : {
      83         344 :   _num_dofs_nl = _nonlinear_system.n_dofs();
      84         344 :   _num_dofs_aux = _aux_system.n_dofs();
      85         344 :   _num_dofs = _equation_systems.n_dofs();
      86         344 :   _num_dofs_constrained = 0;
      87        1032 :   for (auto s : make_range(_equation_systems.n_systems()))
      88         688 :     _num_dofs_constrained += _equation_systems.get_system(s).n_constrained_dofs();
      89             : 
      90         344 :   _num_node = _mesh.n_nodes();
      91         344 :   _num_elem = _mesh.n_elem();
      92         344 :   _num_local_dofs_nl = _nonlinear_system.n_local_dofs();
      93         344 :   _num_local_dofs_aux = _aux_system.n_local_dofs();
      94         344 :   _num_local_dofs = _num_local_dofs_nl + _num_local_dofs_aux;
      95         344 :   _num_local_node = _mesh.n_local_nodes();
      96         344 :   _num_local_elem = _mesh.n_local_elem();
      97             : 
      98         344 :   possiblyAddSidesetInfo();
      99         344 :   possiblyAddSubdomainInfo();
     100         344 : }
     101             : 
     102             : void
     103         344 : MeshInfo::possiblyAddSidesetInfo()
     104             : {
     105             :   // Helper for adding the sideset names to a given map of sidesets
     106         313 :   auto add_sideset_names = [&](std::map<BoundaryID, SidesetInfo> & sidesets)
     107             :   {
     108        1192 :     for (auto & pair : sidesets)
     109         879 :       pair.second.name = _mesh.get_boundary_info().get_sideset_name(pair.second.id);
     110         313 :   };
     111             : 
     112             :   // Helper for sorting all of the sides in each sideset
     113         224 :   auto sort_sides = [](std::map<BoundaryID, SidesetInfo> & sidesets)
     114             :   {
     115         801 :     for (auto & pair : sidesets)
     116         577 :       std::sort(pair.second.sides.begin(), pair.second.sides.end());
     117         224 :   };
     118             : 
     119         344 :   const bool include_all = !_items.isValid();
     120             : 
     121        1214 :   if (include_all || _items.isValueSet("local_sidesets") ||
     122        1794 :       _items.isValueSet("local_sideset_elems") || _items.isValueSet("sideset_elems"))
     123             :   {
     124         170 :     _local_sidesets.clear();
     125         170 :     _local_sideset_elems.clear();
     126         170 :     _sideset_elems.clear();
     127             : 
     128             :     // Fill the local sideset information; all cases need it
     129         170 :     std::map<BoundaryID, SidesetInfo> sidesets;
     130         170 :     for (const auto & bnd_elem :
     131        2984 :          as_range(_fe_problem.mesh().bndElemsBegin(), _fe_problem.mesh().bndElemsEnd()))
     132        2644 :       if (bnd_elem->_elem->processor_id() == processor_id())
     133             :       {
     134        1744 :         auto & entry = sidesets[bnd_elem->_bnd_id];
     135        1744 :         entry.id = bnd_elem->_bnd_id;
     136        1744 :         entry.sides.emplace_back(bnd_elem->_elem->id(), bnd_elem->_side);
     137         170 :       }
     138             : 
     139             :     // For local sidesets: copy over the local info, remove the sides, and add the names
     140         402 :     if (include_all || _items.isValueSet("local_sidesets"))
     141             :     {
     142             :       // Copy over the local sideset info, remove the sides, and add the names
     143          54 :       _local_sidesets = sidesets;
     144         216 :       for (auto & pair : _local_sidesets)
     145         162 :         pair.second.sides.clear();
     146          54 :       add_sideset_names(_local_sidesets);
     147             :     }
     148             : 
     149             :     // For local sideset elems: copy over the local info, and add the names
     150         402 :     if (include_all || _items.isValueSet("local_sideset_elems"))
     151             :     {
     152          54 :       _local_sideset_elems = sidesets;
     153          54 :       sort_sides(_local_sideset_elems);
     154          54 :       add_sideset_names(_local_sideset_elems);
     155             :     }
     156             : 
     157             :     // For the global sideset elems, we need to communicate all of the elems
     158         402 :     if (include_all || _items.isValueSet("sideset_elems"))
     159             :     {
     160             :       // Set up a structure for sending each (id, elem id, side) tuple to root
     161             :       std::map<processor_id_type,
     162             :                std::vector<std::tuple<boundary_id_type, dof_id_type, unsigned int>>>
     163         170 :           send_info;
     164             :       // Avoid empty sends
     165         170 :       if (!sidesets.empty())
     166             :       {
     167         170 :         auto & root_info = send_info[0];
     168         691 :         for (const auto & pair : sidesets)
     169        2265 :           for (const auto & side : pair.second.sides)
     170        1744 :             root_info.emplace_back(pair.second.id, side.first, side.second);
     171             :       }
     172             : 
     173             :       // Take the received information and insert it into _sideset_elems
     174             :       auto accumulate_info =
     175         170 :           [this](processor_id_type,
     176             :                  const std::vector<std::tuple<boundary_id_type, dof_id_type, unsigned int>> & info)
     177             :       {
     178        1914 :         for (const auto & tuple : info)
     179             :         {
     180        1744 :           const auto id = std::get<0>(tuple);
     181        1744 :           auto & entry = _sideset_elems[id];
     182        1744 :           entry.id = id;
     183        1744 :           entry.sides.emplace_back(std::get<1>(tuple), std::get<2>(tuple));
     184             :         }
     185         170 :       };
     186             : 
     187             :       // Push the information and insert it into _sideset_elems on root
     188         170 :       Parallel::push_parallel_vector_data(comm(), send_info, accumulate_info);
     189             : 
     190         170 :       sort_sides(_sideset_elems);
     191         170 :       add_sideset_names(_sideset_elems);
     192         170 :     }
     193         170 :   }
     194             : 
     195             :   // For global sideset information without elements, we can simplify communication.
     196             :   // All we need are the boundary IDs from libMesh (may not be reduced, so take the union)
     197             :   // and then add the names (global)
     198         924 :   if (include_all || _items.isValueSet("sidesets"))
     199             :   {
     200          64 :     _sidesets.clear();
     201             : 
     202          64 :     auto boundary_ids = _mesh.get_boundary_info().get_boundary_ids();
     203          64 :     comm().set_union(boundary_ids, 0);
     204          64 :     if (processor_id() == 0)
     205             :     {
     206         175 :       for (const auto id : boundary_ids)
     207         140 :         _sidesets[id].id = id;
     208          35 :       add_sideset_names(_sidesets);
     209             :     }
     210          64 :   }
     211         344 : }
     212             : 
     213             : void
     214         478 : to_json(nlohmann::json & json, const std::map<BoundaryID, MeshInfo::SidesetInfo> & sidesets)
     215             : {
     216        1303 :   for (const auto & pair : sidesets)
     217             :   {
     218         825 :     const MeshInfo::SidesetInfo & sideset_info = pair.second;
     219             : 
     220         825 :     nlohmann::json sideset_json;
     221         825 :     sideset_json["id"] = sideset_info.id;
     222         825 :     if (sideset_info.name.size())
     223         825 :       sideset_json["name"] = sideset_info.name;
     224         825 :     if (sideset_info.sides.size())
     225             :     {
     226         550 :       auto & sides_json = sideset_json["sides"];
     227             : 
     228        3194 :       for (const std::pair<dof_id_type, unsigned int> & pair : sideset_info.sides)
     229             :       {
     230        2644 :         nlohmann::json side_json;
     231        2644 :         side_json["elem_id"] = pair.first;
     232        2644 :         side_json["side"] = pair.second;
     233        2644 :         sides_json.push_back(side_json);
     234        2644 :       }
     235             :     }
     236             : 
     237         825 :     json.push_back(sideset_json);
     238         825 :   }
     239         478 : }
     240             : 
     241             : void
     242          95 : dataStore(std::ostream & stream, MeshInfo::SidesetInfo & sideset_info, void * context)
     243             : {
     244          95 :   storeHelper(stream, sideset_info.id, context);
     245          95 :   storeHelper(stream, sideset_info.name, context);
     246          95 :   storeHelper(stream, sideset_info.sides, context);
     247          95 : }
     248             : 
     249             : void
     250          95 : dataLoad(std::istream & stream, MeshInfo::SidesetInfo & sideset_info, void * context)
     251             : {
     252          95 :   loadHelper(stream, sideset_info.id, context);
     253          95 :   loadHelper(stream, sideset_info.name, context);
     254          95 :   loadHelper(stream, sideset_info.sides, context);
     255          95 : }
     256             : 
     257             : void
     258         344 : MeshInfo::possiblyAddSubdomainInfo()
     259             : {
     260             :   // Helper for adding the subdomain names to a given map of subdomains
     261         242 :   auto add_subdomain_names = [&](std::map<SubdomainID, SubdomainInfo> & subdomains)
     262             :   {
     263         690 :     for (auto & pair : subdomains)
     264         448 :       pair.second.name = _mesh.subdomain_name(pair.second.id);
     265         242 :   };
     266             : 
     267             :   // Helper for sorting all of the elems in each subdomain
     268         153 :   auto sort_elems = [](std::map<SubdomainID, SubdomainInfo> & subdomains)
     269             :   {
     270         504 :     for (auto & pair : subdomains)
     271         351 :       std::sort(pair.second.elems.begin(), pair.second.elems.end());
     272         153 :   };
     273             : 
     274         344 :   const bool include_all = !_items.isValid();
     275             : 
     276        1214 :   if (include_all || _items.isValueSet("local_subdomains") ||
     277        1794 :       _items.isValueSet("local_subdomain_elems") || _items.isValueSet("subdomain_elems"))
     278             :   {
     279         150 :     _local_subdomains.clear();
     280         150 :     _local_subdomain_elems.clear();
     281         150 :     _subdomain_elems.clear();
     282             : 
     283             :     // Fill the local subdomain information; all cases need it
     284         150 :     std::map<SubdomainID, SubdomainInfo> subdomains;
     285        4002 :     for (const auto & elem : *_fe_problem.mesh().getActiveLocalElementRange())
     286             :     {
     287        3852 :       auto & entry = subdomains[elem->subdomain_id()];
     288        3852 :       entry.id = elem->subdomain_id();
     289        3852 :       entry.elems.push_back(elem->id());
     290             :     }
     291             : 
     292             :     // For local subdomains: copy over the local info, remove the elems, and add the names
     293         342 :     if (include_all || _items.isValueSet("local_subdomains"))
     294             :     {
     295          54 :       _local_subdomains = subdomains;
     296         108 :       for (auto & pair : _local_subdomains)
     297          54 :         pair.second.elems.clear();
     298          54 :       add_subdomain_names(_local_subdomains);
     299             :     }
     300             : 
     301             :     // For local subdomain elems: copy over the local info, and add the names
     302         342 :     if (include_all || _items.isValueSet("local_subdomain_elems"))
     303             :     {
     304          54 :       _local_subdomain_elems = subdomains;
     305          54 :       sort_elems(_local_subdomain_elems);
     306          54 :       add_subdomain_names(_local_subdomain_elems);
     307             :     }
     308             : 
     309             :     // For the global subdomain elems, we need to communicate all of the elems
     310         342 :     if (include_all || _items.isValueSet("subdomain_elems"))
     311             :     {
     312             :       // Set up a structure for sending each (id, elem id) to root
     313         150 :       std::map<processor_id_type, std::vector<std::pair<subdomain_id_type, dof_id_type>>> send_info;
     314             :       // Avoid creating empty entry
     315         150 :       if (!subdomains.empty())
     316             :       {
     317         150 :         auto & root_info = send_info[0];
     318         480 :         for (const auto & pair : subdomains)
     319        4182 :           for (const auto elem_id : pair.second.elems)
     320        3852 :             root_info.emplace_back(pair.second.id, elem_id);
     321             :       }
     322             : 
     323             :       // Take the received information and insert it into _subdomain_elems
     324             :       auto accumulate_info =
     325         150 :           [this](processor_id_type,
     326             :                  const std::vector<std::pair<subdomain_id_type, dof_id_type>> & info)
     327             :       {
     328        4002 :         for (const auto & subdomain_elem_pair : info)
     329             :         {
     330        3852 :           auto & entry = _subdomain_elems[subdomain_elem_pair.first];
     331        3852 :           entry.id = subdomain_elem_pair.first;
     332        3852 :           entry.elems.emplace_back(subdomain_elem_pair.second);
     333             :         }
     334         150 :       };
     335             : 
     336             :       // Push the information and insert it into _subdomain_elems on root
     337         150 :       Parallel::push_parallel_vector_data(comm(), send_info, accumulate_info);
     338             : 
     339         150 :       if (processor_id() == 0)
     340             :       {
     341          99 :         sort_elems(_subdomain_elems);
     342          99 :         add_subdomain_names(_subdomain_elems);
     343             :       }
     344         150 :     }
     345         150 :   }
     346             : 
     347             :   // For global subdomain information without elements, we can simplify communication.
     348             :   // All we need are the subdomain IDs from libMesh and then add the names (global)
     349         924 :   if (include_all || _items.isValueSet("subdomains"))
     350             :   {
     351          64 :     _subdomains.clear();
     352             : 
     353          64 :     std::set<subdomain_id_type> subdomain_ids;
     354          64 :     _mesh.subdomain_ids(subdomain_ids);
     355             : 
     356          64 :     if (processor_id() == 0)
     357             :     {
     358          78 :       for (const auto id : subdomain_ids)
     359          43 :         _subdomains[id].id = id;
     360          35 :       add_subdomain_names(_subdomains);
     361             :     }
     362          64 :   }
     363         344 : }
     364             : 
     365             : void
     366         448 : to_json(nlohmann::json & json, const std::map<SubdomainID, MeshInfo::SubdomainInfo> & subdomains)
     367             : {
     368         878 :   for (const auto & pair : subdomains)
     369             :   {
     370         430 :     const MeshInfo::SubdomainInfo & subdomain_info = pair.second;
     371             : 
     372         430 :     nlohmann::json subdomain_json;
     373         430 :     subdomain_json["id"] = subdomain_info.id;
     374         430 :     if (subdomain_info.name.size())
     375         286 :       subdomain_json["name"] = subdomain_info.name;
     376         430 :     if (subdomain_info.elems.size())
     377             :     {
     378         342 :       auto & sides_json = subdomain_json["elems"];
     379        6444 :       for (const auto & id : subdomain_info.elems)
     380        6102 :         sides_json.push_back(id);
     381             :     }
     382             : 
     383         430 :     json.push_back(subdomain_json);
     384         430 :   }
     385         448 : }
     386             : 
     387             : void
     388          50 : dataStore(std::ostream & stream, MeshInfo::SubdomainInfo & subdomain_info, void * context)
     389             : {
     390          50 :   storeHelper(stream, subdomain_info.id, context);
     391          50 :   storeHelper(stream, subdomain_info.name, context);
     392          50 :   storeHelper(stream, subdomain_info.elems, context);
     393          50 : }
     394             : 
     395             : void
     396          50 : dataLoad(std::istream & stream, MeshInfo::SubdomainInfo & subdomain_info, void * context)
     397             : {
     398          50 :   loadHelper(stream, subdomain_info.id, context);
     399          50 :   loadHelper(stream, subdomain_info.name, context);
     400          50 :   loadHelper(stream, subdomain_info.elems, context);
     401          50 : }

Generated by: LCOV version 1.14