LCOV - code coverage report
Current view: top level - src/constraints - MortarInterfaceWarehouse.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: #33390 (250e9c) with base 846a5c Lines: 95 110 86.4 %
Date: 2026-07-31 18:15:22 Functions: 9 11 81.8 %
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 "MortarInterfaceWarehouse.h"
      11             : #include "SubProblem.h"
      12             : #include "MooseMesh.h"
      13             : #include "MooseError.h"
      14             : #include "MooseEnum.h"
      15             : #include "MooseUtils.h"
      16             : #include "MortarExecutorInterface.h"
      17             : #include "AutomaticMortarGeneration.h"
      18             : 
      19             : namespace
      20             : {
      21             : MortarSegmentTriangulationMode
      22        1481 : toTriangulationMode(const MooseEnum & triangulation)
      23             : {
      24        1481 :   if (triangulation == "vertex")
      25          12 :     return MortarSegmentTriangulationMode::Vertex;
      26        1469 :   if (triangulation == "centroid")
      27        1445 :     return MortarSegmentTriangulationMode::Centroid;
      28          24 :   if (triangulation == "ear_clipping")
      29          12 :     return MortarSegmentTriangulationMode::EarClipping;
      30             : #if defined(LIBMESH_HAVE_TRIANGLE) || defined(LIBMESH_HAVE_POLY2TRI)
      31          12 :   if (triangulation == "delaunay")
      32          12 :     return MortarSegmentTriangulationMode::Delaunay;
      33             : #endif
      34           0 :   mooseError("Unsupported mortar triangulation option: ", triangulation);
      35             : }
      36             : }
      37             : 
      38       63000 : MortarInterfaceWarehouse::MortarInterfaceWarehouse(const libMesh::ParallelObject & other)
      39       63000 :   : libMesh::ParallelObject(other), _mortar_initd(false)
      40             : {
      41       63000 : }
      42             : 
      43             : void
      44        1481 : MortarInterfaceWarehouse::createMortarInterface(
      45             :     const std::pair<BoundaryID, BoundaryID> & boundary_key,
      46             :     const std::pair<SubdomainID, SubdomainID> & subdomain_key,
      47             :     SubProblem & subproblem,
      48             :     bool on_displaced,
      49             :     bool periodic,
      50             :     const bool debug,
      51             :     const bool correct_edge_dropping,
      52             :     const Real minimum_projection_angle,
      53             :     const Mortar3DSubpatchPlane mortar_3d_subpatch_plane,
      54             :     const MooseEnum & triangulation,
      55             :     const bool triangulate_triangles,
      56             :     const Mortar3DQuadraturePointMapping mortar_3d_qp_mapping)
      57             : {
      58        1481 :   _mortar_subdomain_coverage.insert(subdomain_key.first);
      59        1481 :   _mortar_subdomain_coverage.insert(subdomain_key.second);
      60             : 
      61        1481 :   _mortar_boundary_coverage.insert(boundary_key.first);
      62        1481 :   _mortar_boundary_coverage.insert(boundary_key.second);
      63             : 
      64        1481 :   MeshBase & mesh = subproblem.mesh().getMesh();
      65             : 
      66        1481 :   auto & mortar_interfaces = on_displaced ? _displaced_mortar_interfaces : _mortar_interfaces;
      67        1481 :   const auto triangulation_mode = toTriangulationMode(triangulation);
      68             : 
      69        1481 :   auto interface_iterator = mortar_interfaces.find(boundary_key);
      70        1481 :   if (interface_iterator != mortar_interfaces.end())
      71             :   {
      72             :     // Existing entry: every per-interface flag must agree across constraints sharing the same
      73             :     // primary-secondary surface pair.
      74         402 :     const auto & existing = interface_iterator->second;
      75         402 :     if (existing.periodic != periodic)
      76           0 :       mooseError("We do not currently support enforcing both periodic and non-periodic constraints "
      77             :                  "on the same boundary primary-secondary pair");
      78         402 :     if (existing.debug != debug)
      79           0 :       mooseError(
      80             :           "We do not currently support generating and not generating debug output "
      81             :           "on the same boundary primary-secondary surface pair. Please set debug_mesh = true for "
      82             :           "all constraints sharing the same primary-secondary surface pairs");
      83         402 :     if (!MooseUtils::absoluteFuzzyEqual(existing.minimum_projection_angle,
      84             :                                         minimum_projection_angle))
      85           3 :       mooseError("We do not currently support multiple values of 'minimum_projection_angle' on "
      86             :                  "the same boundary primary-secondary surface pair.");
      87         399 :     if (existing.mortar_3d_subpatch_plane != mortar_3d_subpatch_plane)
      88           3 :       mooseError("Mortar constraints sharing the same primary/secondary mortar interface must use "
      89             :                  "the same 'mortar_3d_subpatch_plane' value.");
      90         396 :     if (existing.triangulation != triangulation_mode)
      91           0 :       mooseError("We do not currently support multiple values of 'triangulation' on the same "
      92             :                  "boundary primary-secondary surface pair.");
      93         396 :     if (existing.triangulate_triangles != triangulate_triangles)
      94           0 :       mooseError("We do not currently support multiple values of 'triangulate_triangles' on the "
      95             :                  "same boundary primary-secondary surface pair.");
      96         396 :     if (existing.mortar_3d_qp_mapping != mortar_3d_qp_mapping)
      97           0 :       mooseError("We do not currently support multiple values of 'mortar_3d_qp_mapping' on the "
      98             :                  "same boundary primary-secondary surface pair.");
      99             :   }
     100             :   else
     101             :   {
     102             :     MortarInterfaceConfig config{
     103             :         std::make_unique<AutomaticMortarGeneration>(subproblem.getMooseApp(),
     104             :                                                     mesh,
     105             :                                                     boundary_key,
     106             :                                                     subdomain_key,
     107             :                                                     on_displaced,
     108             :                                                     periodic,
     109             :                                                     debug,
     110             :                                                     correct_edge_dropping,
     111             :                                                     minimum_projection_angle,
     112             :                                                     mortar_3d_subpatch_plane,
     113             :                                                     triangulation_mode,
     114             :                                                     triangulate_triangles,
     115             :                                                     mortar_3d_qp_mapping),
     116             :         periodic,
     117             :         debug,
     118             :         minimum_projection_angle,
     119             :         mortar_3d_subpatch_plane,
     120             :         triangulation_mode,
     121             :         triangulate_triangles,
     122        1079 :         mortar_3d_qp_mapping};
     123        1079 :     config.amg->initOutput();
     124        1079 :     mortar_interfaces.emplace(boundary_key, std::move(config));
     125        1079 :   }
     126             : 
     127             :   // See whether to query the mesh
     128        1475 :   SubdomainID key1 = subdomain_key.first;
     129        1475 :   SubdomainID key2 = subdomain_key.second;
     130             : 
     131             :   // it(1,2) is a a pair consisting of an iterator to the inserted element (or to the element that
     132             :   // prevented the insertion) and a bool denoting whether the insertion took place.
     133        1475 :   auto it1 = _lower_d_sub_to_higher_d_subs.insert(std::make_pair(key1, std::set<SubdomainID>{}));
     134        1475 :   auto it2 = _lower_d_sub_to_higher_d_subs.insert(std::make_pair(key2, std::set<SubdomainID>{}));
     135             : 
     136             :   // Each entry in this vector will be a pair. The first member of the pair corresponds to
     137             :   // the lower dimensional subomain ID. The second member of the pair corresponds to the higher
     138             :   // dimensional subdomain ids of the lower dimeionsal interior parents
     139        1475 :   std::vector<std::pair<SubdomainID, std::set<SubdomainID> *>> subdomains_to_probe;
     140             : 
     141        1475 :   if (it1.second)
     142        1079 :     subdomains_to_probe.push_back(std::make_pair(key1, &it1.first->second));
     143        1475 :   if (it2.second)
     144        1079 :     subdomains_to_probe.push_back(std::make_pair(key2, &it2.first->second));
     145             : 
     146        3633 :   for (auto & pr : subdomains_to_probe)
     147             :   {
     148        4316 :     for (const Elem * lower_d_elem : as_range(mesh.active_local_subdomain_elements_begin(pr.first),
     149       31627 :                                               mesh.active_local_subdomain_elements_end(pr.first)))
     150             :     {
     151       25153 :       const Elem * ip = lower_d_elem->interior_parent();
     152             :       mooseAssert(
     153             :           ip,
     154             :           "Lower dimensional elements should always have an interior parent set when using mortar");
     155       25153 :       pr.second->insert(ip->subdomain_id());
     156        2158 :     }
     157             : 
     158             :     // Make sure that we get this right in parallel
     159        2158 :     _communicator.set_union(*pr.second);
     160             :   }
     161        1475 : }
     162             : 
     163             : const AutomaticMortarGeneration &
     164      256642 : MortarInterfaceWarehouse::getMortarInterface(
     165             :     const std::pair<BoundaryID, BoundaryID> & boundary_key,
     166             :     const std::pair<SubdomainID, SubdomainID> & /*subdomain_key*/,
     167             :     bool on_displaced) const
     168             : {
     169      256642 :   auto & mortar_interfaces = on_displaced ? _displaced_mortar_interfaces : _mortar_interfaces;
     170      256642 :   auto it = mortar_interfaces.find(boundary_key);
     171      256642 :   if (it == mortar_interfaces.end())
     172           0 :     mooseError(
     173             :         "The requested mortar interface AutomaticMortarGeneration object does not yet exist!");
     174      513284 :   return *it->second.amg;
     175             : }
     176             : 
     177             : AutomaticMortarGeneration &
     178      256642 : MortarInterfaceWarehouse::getMortarInterface(
     179             :     const std::pair<BoundaryID, BoundaryID> & boundary_key,
     180             :     const std::pair<SubdomainID, SubdomainID> & subdomain_key,
     181             :     bool on_displaced)
     182             : {
     183             :   return const_cast<AutomaticMortarGeneration &>(
     184      256642 :       const_cast<const MortarInterfaceWarehouse *>(this)->getMortarInterface(
     185      256642 :           boundary_key, subdomain_key, on_displaced));
     186             : }
     187             : 
     188             : void
     189       71591 : MortarInterfaceWarehouse::update()
     190             : {
     191       72643 :   for (auto & mortar_pair : _mortar_interfaces)
     192        1058 :     update(*mortar_pair.second.amg);
     193       75151 :   for (auto & mortar_pair : _displaced_mortar_interfaces)
     194        3566 :     update(*mortar_pair.second.amg);
     195             : 
     196       71585 :   _mortar_initd = true;
     197       71585 : }
     198             : 
     199             : void
     200        7044 : MortarInterfaceWarehouse::meshChanged()
     201             : {
     202        7146 :   for (auto & mortar_pair : _mortar_interfaces)
     203         102 :     mortar_pair.second.amg->meshChanged();
     204        7114 :   for (auto & mortar_pair : _displaced_mortar_interfaces)
     205          70 :     mortar_pair.second.amg->meshChanged();
     206        7044 :   update();
     207        7044 : }
     208             : 
     209             : void
     210        4624 : MortarInterfaceWarehouse::update(AutomaticMortarGeneration & amg)
     211             : {
     212             :   // Clear exiting data
     213        4624 :   amg.clear();
     214             : 
     215        4624 :   const auto dim = amg.dim();
     216             : 
     217        4624 :   if (dim == 1)
     218           3 :     mooseError("Mortar constraints are not currently supported for 1D meshes");
     219        4621 :   else if (dim != 2 && dim != 3)
     220           0 :     mooseError("Invalid mesh dimension for mortar constraint");
     221             : 
     222             :   // Construct maps from nodes -> lower dimensional elements on the primary and secondary
     223             :   // boundaries.
     224        4621 :   amg.buildNodeToElemMaps();
     225             : 
     226             :   // Compute nodal geometry (normals and tangents).
     227        4621 :   amg.computeNodalGeometry();
     228             : 
     229        4621 :   if (dim == 2)
     230             :   {
     231             :     // Project secondary nodes (find xi^(2) values).
     232        4263 :     amg.projectSecondaryNodes();
     233             : 
     234             :     // Project primary nodes (find xi^(1) values).
     235        4263 :     amg.projectPrimaryNodes();
     236             : 
     237             :     // Build the mortar segment mesh on the secondary boundary.
     238        4263 :     amg.buildMortarSegmentMesh();
     239             :   }
     240             :   else // dim == 3
     241         358 :     amg.buildMortarSegmentMesh3d();
     242             : 
     243        4618 :   amg.computeInactiveLMNodes();
     244        4618 :   amg.computeInactiveLMElems();
     245        4618 : }
     246             : 
     247             : const std::set<SubdomainID> &
     248        2950 : MortarInterfaceWarehouse::getHigherDimSubdomainIDs(SubdomainID lower_d_subdomain_id) const
     249             : {
     250        2950 :   if (_lower_d_sub_to_higher_d_subs.find(lower_d_subdomain_id) ==
     251        5900 :       _lower_d_sub_to_higher_d_subs.end())
     252           0 :     mooseError("The lower dimensional ID ",
     253             :                lower_d_subdomain_id,
     254             :                " has not been added to MortarInterfaceWarehouse yet");
     255        2950 :   return _lower_d_sub_to_higher_d_subs.at(lower_d_subdomain_id);
     256             : }
     257             : 
     258             : void
     259           0 : MortarInterfaceWarehouse::notifyWhenMortarSetup(MortarExecutorInterface * const mei_obj)
     260             : {
     261           0 :   _mei_objs.insert(mei_obj);
     262           0 : }
     263             : 
     264             : void
     265           0 : MortarInterfaceWarehouse::dontNotifyWhenMortarSetup(MortarExecutorInterface * const mei_obj)
     266             : {
     267           0 :   _mei_objs.erase(mei_obj);
     268           0 : }

Generated by: LCOV version 1.14