LCOV - code coverage report
Current view: top level - src/utils - MeshAlignment.C (source / functions) Hit Total Coverage
Test: idaholab/moose thermal_hydraulics: #32971 (54bef8) with base c6cf66 Lines: 117 117 100.0 %
Date: 2026-05-29 20:41:18 Functions: 11 11 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 "MeshAlignment.h"
      11             : #include "Assembly.h"
      12             : #include "KDTree.h"
      13             : 
      14             : #include "libmesh/elem.h"
      15             : 
      16         243 : MeshAlignment::MeshAlignment(const MooseMesh & mesh, bool require_same_translation)
      17             :   : MeshAlignmentBase(mesh),
      18         243 :     _require_same_translation(require_same_translation),
      19         243 :     _meshes_are_coincident(false),
      20         243 :     _meshes_have_same_translation(false)
      21             : {
      22         243 : }
      23             : 
      24             : void
      25         155 : MeshAlignment::initialize(
      26             :     const std::vector<dof_id_type> & primary_elem_ids,
      27             :     const std::vector<std::tuple<dof_id_type, unsigned short int>> & secondary_boundary_info)
      28             : {
      29         155 :   _primary_elem_ids = primary_elem_ids;
      30         155 :   extractFrom1DElements(
      31         155 :       _primary_elem_ids, _primary_elem_points, _primary_node_ids, _primary_node_points);
      32             : 
      33         155 :   extractFromBoundaryInfo(secondary_boundary_info,
      34         155 :                           _secondary_elem_ids,
      35         155 :                           _secondary_side_ids,
      36         155 :                           _secondary_elem_points,
      37         155 :                           _secondary_node_ids,
      38         155 :                           _secondary_node_points);
      39             : 
      40         155 :   buildMapping();
      41         155 : }
      42             : 
      43             : void
      44          76 : MeshAlignment::initialize(
      45             :     const std::vector<std::tuple<dof_id_type, unsigned short int>> & primary_boundary_info,
      46             :     const std::vector<std::tuple<dof_id_type, unsigned short int>> & secondary_boundary_info)
      47             : {
      48          76 :   extractFromBoundaryInfo(primary_boundary_info,
      49          76 :                           _primary_elem_ids,
      50          76 :                           _primary_side_ids,
      51          76 :                           _primary_elem_points,
      52          76 :                           _primary_node_ids,
      53          76 :                           _primary_node_points);
      54             : 
      55          76 :   extractFromBoundaryInfo(secondary_boundary_info,
      56          76 :                           _secondary_elem_ids,
      57          76 :                           _secondary_side_ids,
      58          76 :                           _secondary_elem_points,
      59          76 :                           _secondary_node_ids,
      60          76 :                           _secondary_node_points);
      61             : 
      62          76 :   buildMapping();
      63          76 : }
      64             : 
      65             : void
      66         231 : MeshAlignment::buildMapping()
      67             : {
      68         231 :   _meshes_are_coincident = true;
      69         231 :   std::vector<unsigned int> primary_elem_pairing_count(_primary_elem_ids.size(), 0);
      70             : 
      71             :   // Build the element mapping
      72         231 :   if (_primary_elem_points.size() > 0 && _secondary_elem_points.size() > 0)
      73             :   {
      74             :     // find the primary elements that are nearest to the secondary elements
      75         231 :     KDTree kd_tree(_primary_elem_points, _mesh.getMaxLeafSize());
      76        2934 :     for (std::size_t i_secondary = 0; i_secondary < _secondary_elem_points.size(); i_secondary++)
      77             :     {
      78             :       unsigned int patch_size = 1;
      79        2703 :       std::vector<std::size_t> return_index(patch_size);
      80        2703 :       kd_tree.neighborSearch(_secondary_elem_points[i_secondary], patch_size, return_index);
      81        2703 :       const std::size_t i_primary = return_index[0];
      82             : 
      83             :       // Flip flag if any pair of points are not coincident
      84        2703 :       if (!_secondary_elem_points[i_secondary].absolute_fuzzy_equals(
      85             :               _primary_elem_points[i_primary]))
      86         844 :         _meshes_are_coincident = false;
      87             : 
      88        2703 :       const auto primary_elem_id = _primary_elem_ids[i_primary];
      89        2703 :       const auto secondary_elem_id = _secondary_elem_ids[i_secondary];
      90             : 
      91        2703 :       _coupled_elem_ids.insert({primary_elem_id, secondary_elem_id});
      92        2703 :       _coupled_elem_ids.insert({secondary_elem_id, primary_elem_id});
      93             : 
      94        2703 :       primary_elem_pairing_count[i_primary]++;
      95        2703 :     }
      96             :   }
      97             : 
      98             :   // Build the node mapping
      99             :   Point reference_translation_vector;
     100         231 :   _meshes_have_same_translation = true;
     101         231 :   if (_primary_node_points.size() > 0 && _secondary_node_points.size() > 0)
     102             :   {
     103             :     // find the primary nodes that are nearest to the secondary nodes
     104         231 :     KDTree kd_tree(_primary_node_points, _mesh.getMaxLeafSize());
     105        3165 :     for (std::size_t i_secondary = 0; i_secondary < _secondary_node_points.size(); i_secondary++)
     106             :     {
     107             :       unsigned int patch_size = 1;
     108        2934 :       std::vector<std::size_t> return_index(patch_size);
     109        2934 :       kd_tree.neighborSearch(_secondary_node_points[i_secondary], patch_size, return_index);
     110        2934 :       const std::size_t i_primary = return_index[0];
     111             : 
     112             :       const Point translation_vector =
     113             :           _primary_node_points[i_primary] - _secondary_node_points[i_secondary];
     114             : 
     115        2934 :       if (i_secondary == 0)
     116         231 :         reference_translation_vector = translation_vector;
     117             :       else
     118             :       {
     119        2703 :         if (!MooseUtils::absoluteFuzzyEqual(
     120        2703 :                 (reference_translation_vector - translation_vector).norm(), 0))
     121          66 :           _meshes_have_same_translation = false;
     122             :       }
     123             : 
     124        2934 :       const auto primary_node_id = _primary_node_ids[i_primary];
     125        2934 :       const auto secondary_node_id = _secondary_node_ids[i_secondary];
     126             : 
     127        2934 :       _coupled_node_ids.insert({primary_node_id, secondary_node_id});
     128        2934 :       _coupled_node_ids.insert({secondary_node_id, primary_node_id});
     129        2934 :     }
     130             : 
     131         454 :     if (_meshes_have_same_translation &&
     132         223 :         MooseUtils::absoluteFuzzyEqual(reference_translation_vector.norm(), 0))
     133         133 :       _meshes_are_coincident = true;
     134             :     else
     135          98 :       _meshes_are_coincident = false;
     136             : 
     137             :     // Check if meshes are aligned
     138         231 :     if (_require_same_translation)
     139         155 :       _meshes_are_aligned = _meshes_have_same_translation;
     140             :     else
     141             :     {
     142             :       // Else require that all primary boundary elements have exactly one pairing
     143          76 :       _meshes_are_aligned = true;
     144         756 :       for (std::size_t i_primary = 0; i_primary < _primary_elem_ids.size(); i_primary++)
     145         680 :         if (primary_elem_pairing_count[i_primary] != 1)
     146          24 :           _meshes_are_aligned = false;
     147             :     }
     148             :   }
     149         231 : }
     150             : 
     151             : void
     152         173 : MeshAlignment::buildCoupledElemQpIndexMap(Assembly & assembly)
     153             : {
     154             :   // get local quadrature point maps on each side
     155             :   std::map<dof_id_type, std::vector<Point>> primary_qp_map =
     156         173 :       getLocalQuadraturePointMap(assembly, _primary_elem_ids, _primary_side_ids);
     157             :   std::map<dof_id_type, std::vector<Point>> secondary_qp_map =
     158         173 :       getLocalQuadraturePointMap(assembly, _secondary_elem_ids, _secondary_side_ids);
     159             : 
     160             :   // build mapping
     161        2513 :   for (const auto & primary_elem_id : _primary_elem_ids)
     162             :   {
     163        2340 :     const Elem * primary_elem = _mesh.queryElemPtr(primary_elem_id);
     164             :     // The PID check is needed to exclude ghost elements, since those don't
     165             :     // necessarily have a coupled element on this processor:
     166        2340 :     if (primary_elem && primary_elem->processor_id() == _mesh.processor_id())
     167             :     {
     168        1726 :       std::vector<Point> & primary_qps = primary_qp_map[primary_elem_id];
     169             : 
     170        1726 :       const dof_id_type secondary_elem_id = getCoupledElemID(primary_elem_id);
     171        1726 :       std::vector<Point> & secondary_qps = secondary_qp_map[secondary_elem_id];
     172             : 
     173             :       mooseAssert(primary_qps.size() == secondary_qps.size(),
     174             :                   "The numbers of quadrature points for each element must be the same");
     175             : 
     176        1726 :       _coupled_elem_qp_indices[primary_elem_id].resize(primary_qps.size());
     177        1726 :       KDTree kd_tree_qp(secondary_qps, _mesh.getMaxLeafSize());
     178        5178 :       for (std::size_t i = 0; i < primary_qps.size(); i++)
     179             :       {
     180             :         unsigned int patch_size = 1;
     181        3452 :         std::vector<std::size_t> return_index(patch_size);
     182        3452 :         kd_tree_qp.neighborSearch(primary_qps[i], patch_size, return_index);
     183        3452 :         _coupled_elem_qp_indices[primary_elem_id][i] = return_index[0];
     184        3452 :       }
     185             :     }
     186             :   }
     187         173 : }
     188             : 
     189             : std::map<dof_id_type, std::vector<Point>>
     190         346 : MeshAlignment::getLocalQuadraturePointMap(Assembly & assembly,
     191             :                                           const std::vector<dof_id_type> & elem_ids,
     192             :                                           const std::vector<unsigned short int> & side_ids) const
     193             : {
     194             :   std::map<dof_id_type, std::vector<Point>> elem_id_to_qps;
     195        5026 :   for (unsigned int i = 0; i < elem_ids.size(); i++)
     196             :   {
     197        4680 :     const auto elem_id = elem_ids[i];
     198        4680 :     const Elem * elem = _mesh.queryElemPtr(elem_id);
     199        4680 :     if (elem)
     200             :     {
     201             :       assembly.setCurrentSubdomainID(elem->subdomain_id());
     202             : 
     203             :       MooseArray<Point> q_points;
     204        4349 :       if (side_ids.size() > 0)
     205             :       {
     206        2191 :         assembly.reinit(elem, side_ids[i]);
     207        2191 :         q_points = assembly.qPointsFace();
     208             :       }
     209             :       else
     210             :       {
     211        2158 :         assembly.reinit(elem);
     212        2158 :         q_points = assembly.qPoints();
     213             :       }
     214             : 
     215       13047 :       for (std::size_t i = 0; i < q_points.size(); i++)
     216        8698 :         elem_id_to_qps[elem_id].push_back(q_points[i]);
     217             :     }
     218             :   }
     219             : 
     220         346 :   return elem_id_to_qps;
     221             : }
     222             : 
     223             : bool
     224        2637 : MeshAlignment::hasCoupledElemID(const dof_id_type & elem_id) const
     225             : {
     226        2637 :   return _coupled_elem_ids.find(elem_id) != _coupled_elem_ids.end();
     227             : }
     228             : 
     229             : const dof_id_type &
     230       60866 : MeshAlignment::getCoupledElemID(const dof_id_type & elem_id) const
     231             : {
     232             :   mooseAssert(hasCoupledElemID(elem_id), "The element ID has no coupled element.");
     233       60866 :   return _coupled_elem_ids.find(elem_id)->second;
     234             : }
     235             : 
     236             : bool
     237      960000 : MeshAlignment::hasCoupledNodeID(const dof_id_type & node_id) const
     238             : {
     239      960000 :   return _coupled_node_ids.find(node_id) != _coupled_node_ids.end();
     240             : }
     241             : 
     242             : const dof_id_type &
     243     1108690 : MeshAlignment::getCoupledNodeID(const dof_id_type & node_id) const
     244             : {
     245             :   mooseAssert(hasCoupledNodeID(node_id), "The node ID has no coupled node.");
     246     1108690 :   return _coupled_node_ids.find(node_id)->second;
     247             : }
     248             : 
     249             : unsigned int
     250      113006 : MeshAlignment::getCoupledElemQpIndex(const dof_id_type & elem_id, const unsigned int & qp) const
     251             : {
     252             :   auto it = _coupled_elem_qp_indices.find(elem_id);
     253             :   mooseAssert(it != _coupled_elem_qp_indices.end(),
     254             :               "The element ID has no coupled quadrature point indices.");
     255             :   mooseAssert(qp < it->second.size(), "The quadrature index does not exist in the map.");
     256      113006 :   return it->second[qp];
     257             : }

Generated by: LCOV version 1.14