LCOV - code coverage report
Current view: top level - src/components - FileMeshComponent.C (source / functions) Hit Total Coverage
Test: idaholab/moose thermal_hydraulics: #30301 (3b550b) with base 2ad78d Lines: 98 100 98.0 %
Date: 2025-07-30 13:02:48 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 "FileMeshComponent.h"
      11             : #include "THMMesh.h"
      12             : #include "MooseUtils.h"
      13             : #include "libmesh/exodusII_io.h"
      14             : #include "libmesh/exodusII_io_helper.h"
      15             : 
      16             : registerMooseObject("ThermalHydraulicsApp", FileMeshComponent);
      17             : 
      18             : InputParameters
      19         464 : FileMeshComponent::validParams()
      20             : {
      21         464 :   InputParameters params = GeometricalComponent::validParams();
      22             : 
      23         928 :   params.addRequiredParam<FileName>("file", "The ExodusII mesh file name");
      24         928 :   params.addRequiredParam<Point>("position", "Translation vector for the file mesh [m]");
      25             : 
      26         464 :   params.addClassDescription("Loads a mesh from an ExodusII file without adding physics.");
      27             : 
      28         464 :   return params;
      29           0 : }
      30             : 
      31         233 : FileMeshComponent::FileMeshComponent(const InputParameters & parameters)
      32             :   : GeometricalComponent(parameters),
      33         233 :     _file_name(getParam<FileName>("file")),
      34         464 :     _file_is_readable(MooseUtils::pathExists(_file_name) &&
      35         231 :                       MooseUtils::checkFileReadable(_file_name, false, false)),
      36         699 :     _position(getParam<Point>("position"))
      37             : {
      38             :   // The following is a mooseError instead of logError because the 'add_variable'
      39             :   // and 'add_aux_variable' tasks must execute before the integrity check, so if
      40             :   // the file is unreadable, the user would just get an error message about variables
      41             :   // being added to non-existent blocks.
      42         233 :   if (!_file_is_readable)
      43           2 :     mooseError("The file '",
      44             :                _file_name,
      45             :                "' could not be opened. Check that the file exists and that "
      46             :                "you have permission to open it.");
      47         231 : }
      48             : 
      49             : void
      50          35 : FileMeshComponent::setupMesh()
      51             : {
      52          35 :   if (_file_is_readable)
      53             :   {
      54          35 :     buildMesh();
      55             : 
      56             :     // apply translation vector to all nodes
      57        3701 :     for (auto && node_id : _node_ids)
      58             :     {
      59        3666 :       Node & node = mesh().nodeRef(node_id);
      60             :       RealVectorValue p(node(0), node(1), node(2));
      61        3666 :       node = p + _position;
      62             :     }
      63             :   }
      64          35 : }
      65             : 
      66             : std::vector<std::string>
      67         231 : FileMeshComponent::buildMesh()
      68             : {
      69             :   std::vector<std::string> subdomain_names;
      70             : 
      71         231 :   auto & thm_mesh = mesh();
      72             : 
      73         231 :   libMesh::ExodusII_IO_Helper exio_helper(*this, false, true, false);
      74         231 :   exio_helper.open(_file_name.c_str(), true);
      75         231 :   exio_helper.read_and_store_header_info();
      76             : 
      77             :   // maps from Exodus IDs to THMMesh IDs
      78             :   std::map<int, unsigned int> node_id_map;
      79             :   std::map<int, unsigned int> elem_id_map;
      80             :   std::map<int, unsigned int> boundary_id_map;
      81             : 
      82             :   // Loop over nodes:
      83             :   // - Add the nodes into THMMesh
      84             :   // - Populate node_id_map
      85         231 :   exio_helper.read_nodes();
      86         231 :   exio_helper.read_node_num_map();
      87       70665 :   for (int i = 0; i < exio_helper.num_nodes; i++)
      88             :   {
      89       70434 :     int exodus_id = exio_helper.node_num_map[i];
      90             : 
      91       70434 :     Point p(exio_helper.x[i], exio_helper.y[i], exio_helper.z[i]);
      92       70434 :     const Node * node = addNode(p);
      93       70434 :     node_id_map[exodus_id] = node->id();
      94             :   }
      95             : 
      96             :   // Loop over blocks:
      97             :   // - Populate subdomain_names
      98             :   // - Set the blocks in THMMesh
      99             :   // - Add the elements into THMMesh
     100         231 :   exio_helper.read_block_info();
     101         231 :   exio_helper.read_elem_num_map();
     102             :   int jmax_last_block = 0;
     103         478 :   for (int i = 0; i < exio_helper.num_elem_blk; i++)
     104             :   {
     105         247 :     exio_helper.read_elem_in_block(i);
     106             : 
     107             :     // Get subdomain name from file (or ID if no name) and populate subdomain_names
     108         247 :     std::string subdomain_name = exio_helper.get_block_name(i);
     109         247 :     if (subdomain_name.empty())
     110         336 :       subdomain_name = Moose::stringify(exio_helper.get_block_id(i));
     111         247 :     subdomain_names.push_back(subdomain_name);
     112             : 
     113             :     // Generate the subdomain name and ID for THMMesh, and set them
     114         247 :     const std::string component_subdomain_name = genName(_name, subdomain_name);
     115         247 :     SubdomainID sid = thm_mesh.getNextSubdomainId();
     116         247 :     setSubdomainInfo(sid, component_subdomain_name, Moose::COORD_XYZ);
     117             : 
     118         247 :     const std::string type_str(exio_helper.get_elem_type());
     119         247 :     const auto & conv = exio_helper.get_conversion(type_str);
     120             : 
     121             :     // Loop over elements in block
     122         247 :     int jmax = jmax_last_block + exio_helper.num_elem_this_blk;
     123       50587 :     for (int j = jmax_last_block; j < jmax; j++)
     124             :     {
     125             :       // Loop over nodes on element:
     126             :       // - Get the node IDs on the element
     127       50340 :       std::vector<dof_id_type> node_ids(exio_helper.num_nodes_per_elem);
     128      441588 :       for (int k = 0; k < exio_helper.num_nodes_per_elem; k++)
     129             :       {
     130      391248 :         int gi = (j - jmax_last_block) * exio_helper.num_nodes_per_elem + conv.get_node_map(k);
     131      391248 :         int ex_node_id = exio_helper.node_num_map[exio_helper.connect[gi] - 1];
     132      391248 :         node_ids[k] = node_id_map[ex_node_id];
     133             :       }
     134             : 
     135             :       // Add the element and set its subdomain
     136       50340 :       Elem * elem = addElement(conv.libmesh_elem_type(), node_ids);
     137       50340 :       elem->subdomain_id() = sid;
     138             : 
     139             :       // Populate elem_id_map
     140       50340 :       int exodus_id = exio_helper.elem_num_map[j];
     141       50340 :       elem_id_map[exodus_id] = elem->id();
     142             :     }
     143         247 :     jmax_last_block += exio_helper.num_elem_this_blk;
     144             :   }
     145             : 
     146             :   // Loop over boundaries:
     147             :   // - Get the Exodus boundary names and store them
     148             :   // - Generate the boundary IDs
     149         231 :   exio_helper.read_sideset_info();
     150             :   int offset = 0;
     151             :   std::unordered_map<BoundaryID, BoundaryName> new_ids_to_names;
     152        1401 :   for (int i = 0; i < exio_helper.num_side_sets; i++)
     153             :   {
     154             :     // Compute new offset
     155        1170 :     offset += (i > 0 ? exio_helper.num_sides_per_set[i - 1] : 0);
     156        1170 :     exio_helper.read_sideset(i, offset);
     157             : 
     158             :     // Get boundary name from file (or ID if no name)
     159        1170 :     int ex_sideset_id = exio_helper.get_side_set_id(i);
     160        1170 :     std::string sideset_name = exio_helper.get_side_set_name(i);
     161        1170 :     if (sideset_name.empty())
     162         284 :       sideset_name = Moose::stringify(ex_sideset_id);
     163             : 
     164             :     // Generate the boundary ID for THMMesh and populate boundary_id_map
     165        1170 :     unsigned int bc_id = thm_mesh.getNextBoundaryId();
     166        1170 :     boundary_id_map[ex_sideset_id] = bc_id;
     167        2340 :     new_ids_to_names.emplace(bc_id, genName(_name, sideset_name));
     168             :   }
     169             : 
     170         231 :   auto & boundary_info = thm_mesh.getMesh().get_boundary_info();
     171             : 
     172             :   // Loop over the elements on boundaries
     173             :   // Add the boundary elem/side pairs to the boundary
     174       35977 :   for (auto e : index_range(exio_helper.elem_list))
     175             :   {
     176             :     // Get the element
     177       35746 :     int ex_elem_id = exio_helper.elem_num_map[exio_helper.elem_list[e] - 1];
     178       35746 :     dof_id_type elem_id = elem_id_map[ex_elem_id];
     179       35746 :     Elem * elem = thm_mesh.elemPtr(elem_id);
     180             : 
     181             :     // Get the side index
     182       35746 :     const auto & conv = exio_helper.get_conversion(elem->type());
     183             :     // Map the zero-based Exodus side numbering to the libmesh side numbering
     184       35746 :     unsigned int raw_side_index = exio_helper.side_list[e] - 1;
     185       35746 :     std::size_t side_index_offset = conv.get_shellface_index_offset();
     186       35746 :     unsigned int side_index = static_cast<unsigned int>(raw_side_index - side_index_offset);
     187       35746 :     int mapped_side = conv.get_side_map(side_index);
     188             : 
     189             :     // Get the boundary ID and add the elem/side pair to the boundary
     190       35746 :     unsigned int bc_id = boundary_id_map[exio_helper.id_list[e]];
     191       35746 :     boundary_info.add_side(elem, mapped_side, bc_id);
     192       35746 :     _boundary_info[new_ids_to_names[bc_id]].push_back(
     193       35746 :         std::tuple<dof_id_type, unsigned short int>(elem->id(), mapped_side));
     194             :   }
     195             : 
     196             :   // Generate and set the boundary name
     197        1401 :   for (const auto & id_and_name : new_ids_to_names)
     198             :   {
     199        1170 :     boundary_info.sideset_name(id_and_name.first) = id_and_name.second;
     200        1170 :     boundary_info.nodeset_name(id_and_name.first) = id_and_name.second;
     201        1170 :     _boundary_names.push_back(id_and_name.second);
     202             :   }
     203             : 
     204             :   // This appears to be necessary to get the nodesets named correctly, despite
     205             :   // the fact that it gets called later.
     206         231 :   boundary_info.build_node_list_from_side_list();
     207             : 
     208         231 :   return subdomain_names;
     209         231 : }
     210             : 
     211             : bool
     212         223 : FileMeshComponent::hasBoundary(const BoundaryName & boundary_name) const
     213             : {
     214         223 :   checkSetupStatus(MESH_PREPARED);
     215             : 
     216         223 :   return std::find(_boundary_names.begin(), _boundary_names.end(), boundary_name) !=
     217         223 :          _boundary_names.end();
     218             : }
     219             : 
     220             : const std::vector<std::tuple<dof_id_type, unsigned short int>> &
     221         108 : FileMeshComponent::getBoundaryInfo(const BoundaryName & boundary_name) const
     222             : {
     223         108 :   checkSetupStatus(MESH_PREPARED);
     224             : 
     225         108 :   if (_boundary_info.find(boundary_name) != _boundary_info.end())
     226         108 :     return _boundary_info.at(boundary_name);
     227             :   else
     228           0 :     mooseError(name(),
     229             :                ": No boundary info exists for the boundary '",
     230             :                boundary_name,
     231             :                "' on this component.");
     232             : }

Generated by: LCOV version 1.14