LCOV - code coverage report
Current view: top level - src/interfaces - MortarConsumerInterface.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: #33390 (250e9c) with base 846a5c Lines: 128 129 99.2 %
Date: 2026-07-31 18:15:22 Functions: 7 7 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             : // MOOSE Includes
      11             : #include "MortarConsumerInterface.h"
      12             : #include "InputParameters.h"
      13             : #include "MooseObject.h"
      14             : #include "FEProblemBase.h"
      15             : #include "MooseMesh.h"
      16             : #include "Mortar3DSubpatchPlane.h"
      17             : #include "MortarInterfaceWarehouse.h"
      18             : #include "Assembly.h"
      19             : #include "AutomaticMortarGeneration.h"
      20             : #include "libmesh/quadrature.h"
      21             : 
      22             : #include <algorithm>
      23             : 
      24             : InputParameters
      25       58574 : MortarConsumerInterface::validParams()
      26             : {
      27             :   // Create InputParameters object that will be appended to the parameters for the inheriting object
      28       58574 :   InputParameters params = emptyInputParameters();
      29             :   // On a displaced mesh this will geometrically and algebraically ghost the entire interface
      30      175722 :   params.addRelationshipManager(
      31             :       "AugmentSparsityOnInterface",
      32             :       Moose::RelationshipManagerType::GEOMETRIC | Moose::RelationshipManagerType::ALGEBRAIC,
      33       58574 :       [](const InputParameters & obj_params, InputParameters & rm_params)
      34             :       {
      35        8940 :         rm_params.set<bool>("use_displaced_mesh") = obj_params.get<bool>("use_displaced_mesh");
      36        8940 :         rm_params.set<BoundaryName>("secondary_boundary") =
      37        8940 :             obj_params.get<BoundaryName>("secondary_boundary");
      38        8940 :         rm_params.set<BoundaryName>("primary_boundary") =
      39        8940 :             obj_params.get<BoundaryName>("primary_boundary");
      40        8940 :         rm_params.set<SubdomainName>("secondary_subdomain") =
      41        8940 :             obj_params.get<SubdomainName>("secondary_subdomain");
      42        8940 :         rm_params.set<SubdomainName>("primary_subdomain") =
      43        8940 :             obj_params.get<SubdomainName>("primary_subdomain");
      44        4470 :         rm_params.set<bool>("ghost_point_neighbors") =
      45        4470 :             obj_params.get<bool>("ghost_point_neighbors");
      46        4470 :         rm_params.set<bool>("ghost_higher_d_neighbors") =
      47        4470 :             obj_params.get<bool>("ghost_higher_d_neighbors");
      48        4470 :       });
      49             : 
      50      234296 :   params.addRequiredParam<BoundaryName>("primary_boundary",
      51             :                                         "The name of the primary boundary sideset.");
      52      234296 :   params.addRequiredParam<BoundaryName>("secondary_boundary",
      53             :                                         "The name of the secondary boundary sideset.");
      54      234296 :   params.addRequiredParam<SubdomainName>("primary_subdomain", "The name of the primary subdomain.");
      55      234296 :   params.addRequiredParam<SubdomainName>("secondary_subdomain",
      56             :                                          "The name of the secondary subdomain.");
      57      175722 :   params.addParam<bool>(
      58             :       "periodic",
      59      117148 :       false,
      60             :       "Whether this constraint is going to be used to enforce a periodic condition. This has the "
      61             :       "effect of changing the normals vector for projection from outward to inward facing");
      62             : 
      63      175722 :   params.addParam<bool>(
      64             :       "debug_mesh",
      65      117148 :       false,
      66             :       "Whether this constraint is going to enable mortar segment mesh debug information. An exodus"
      67             :       "file will be generated if the user sets this flag to true");
      68             : 
      69      175722 :   params.addParam<bool>(
      70             :       "correct_edge_dropping",
      71      117148 :       false,
      72             :       "Whether to enable correct edge dropping treatment for mortar constraints. When disabled "
      73             :       "any Lagrange Multiplier degree of freedom on a secondary element without full primary "
      74             :       "contributions will be set (strongly) to 0.");
      75             : 
      76      175722 :   params.addParam<bool>(
      77             :       "interpolate_normals",
      78      117148 :       true,
      79             :       "Whether to interpolate the nodal normals (e.g. classic idea of evaluating field at "
      80             :       "quadrature points). If this is set to false, then non-interpolated nodal normals will be "
      81             :       "used, and then the _normals member should be indexed with _i instead of _qp");
      82             : 
      83      175722 :   params.addParam<bool>("ghost_point_neighbors",
      84      117148 :                         false,
      85             :                         "Whether we should ghost point neighbors of secondary face elements, their "
      86             :                         "interior parents, and their mortar interface couples.");
      87      175722 :   params.addParam<Real>(
      88             :       "minimum_projection_angle",
      89      117148 :       40.0,
      90             :       "Parameter to control which angle (in degrees) is admissible for the creation of mortar "
      91             :       "segments.  If set to a value close to zero, very oblique projections are allowed, which "
      92             :       "can result in mortar segments solving physics not meaningfully, and overprojection of "
      93             :       "primary nodes onto the mortar segment mesh in extreme cases. In 3D with "
      94             :       "mortar_3d_subpatch_plane = GEOMETRIC_NORMAL, this parameter also controls which primary "
      95             :       "and secondary subpatch normal pairings are admissible before polygon clipping.");
      96      117148 :   params.addParam<MooseEnum>(
      97             :       "mortar_3d_subpatch_plane",
      98      175722 :       MooseEnum(getMortar3DSubpatchPlaneOptions(), "GEOMETRIC_NORMAL"),
      99             :       "Method used to construct the local 3D mortar subpatch planes used for projection and "
     100             :       "clipping. GEOMETRIC_NORMAL uses the geometric normal of each linear or bilinear secondary "
     101             :       "subpatch. AVERAGED_NODAL_NORMAL uses the averaged nodal normal on each secondary subpatch.");
     102       58574 :   params += MortarConsumerInterface::triangulationParams();
     103             : 
     104      117148 :   MooseEnum mortar_3d_qp_mapping(getMortar3DQuadraturePointMappingOptions(), "normal_projection");
     105      234296 :   mortar_3d_qp_mapping.addDocumentation(
     106             :       "normal_projection",
     107             :       "Project each mortar-segment quadrature point onto the linearized primary and secondary "
     108             :       "sub-elements along the mortar-segment normal.");
     109      234296 :   mortar_3d_qp_mapping.addDocumentation(
     110             :       "reference_interpolation",
     111             :       "Interpolate stored primary and secondary parent-face reference coordinates over each "
     112             :       "triangular mortar segment.");
     113      234296 :   params.addParam<MooseEnum>(
     114             :       "mortar_3d_qp_mapping",
     115             :       mortar_3d_qp_mapping,
     116             :       "Method used to map quadrature points on 3D mortar segments to primary and secondary "
     117             :       "parent-face reference coordinates. This parameter has no effect for 2D mortar.");
     118             : 
     119      117148 :   params.addParam<bool>(
     120             :       "ghost_higher_d_neighbors",
     121      117148 :       false,
     122             :       "Whether we should ghost higher-dimensional neighbors. This is necessary when we are doing "
     123             :       "second order mortar with finite volume primal variables, because in order for the method to "
     124             :       "be second order we must use cell gradients, which couples in the neighbor cells.");
     125             : 
     126      117148 :   return params;
     127       58574 : }
     128             : 
     129             : InputParameters
     130       58574 : MortarConsumerInterface::triangulationParams()
     131             : {
     132       58574 :   InputParameters params = emptyInputParameters();
     133             : 
     134             :   MooseEnum triangulation(
     135             : #if defined(LIBMESH_HAVE_TRIANGLE) || defined(LIBMESH_HAVE_POLY2TRI)
     136             :       "vertex centroid ear_clipping delaunay",
     137             : #else
     138             :       "vertex centroid ear_clipping",
     139             : #endif
     140      234296 :       "centroid");
     141      234296 :   triangulation.addDocumentation(
     142             :       "vertex",
     143             :       "Triangulate clipped 3D mortar polygons by forming a fan from an existing polygon vertex.");
     144      234296 :   triangulation.addDocumentation(
     145             :       "centroid",
     146             :       "Triangulate clipped 3D mortar polygons by forming a fan from a polygon centroid.");
     147      234296 :   triangulation.addDocumentation(
     148             :       "ear_clipping", "Triangulate clipped 3D mortar polygons with an ear-clipping algorithm.");
     149             : #if defined(LIBMESH_HAVE_TRIANGLE) || defined(LIBMESH_HAVE_POLY2TRI)
     150      234296 :   triangulation.addDocumentation(
     151             :       "delaunay",
     152             :       "Triangulate clipped 3D mortar polygons using libMesh's constrained-Delaunay PSLG "
     153             :       "triangulation backend while preserving polygon boundary edges.");
     154             : #endif
     155      234296 :   params.addParam<MooseEnum>(
     156             :       "triangulation",
     157             :       triangulation,
     158             :       "Strategy used to triangulate clipped 3D mortar polygons into mortar segments. The default "
     159             :       "is 'centroid' to preserve the legacy 3D mortar segmentation behavior.");
     160      117148 :   params.addParam<bool>(
     161             :       "triangulate_triangles",
     162      117148 :       false,
     163             :       "Whether a clipped 3D mortar polygon that is already a triangle should still be subdivided "
     164             :       "during triangulation. When enabled, already-triangular polygons are subdivided with the "
     165             :       "centroid-based path because the vertex-fan, ear-clipping, and Delaunay backends cannot "
     166             :       "refine a triangle any further on their own.");
     167             : 
     168      117148 :   return params;
     169       58574 : }
     170             : 
     171             : // Standard constructor
     172        1484 : MortarConsumerInterface::MortarConsumerInterface(const MooseObject * moose_object)
     173        5936 :   : _mci_fe_problem(*moose_object->getCheckedPointerParam<FEProblemBase *>("_fe_problem_base")),
     174        5936 :     _mci_subproblem(*moose_object->getCheckedPointerParam<SubProblem *>("_subproblem")),
     175        2968 :     _mci_tid(moose_object->getParam<THREAD_ID>("_tid")),
     176        1484 :     _mci_mesh(_mci_subproblem.mesh()),
     177             :     // all geometric assembly information should be correct for nl system number 0
     178        1484 :     _mci_assembly(_mci_subproblem.assembly(_mci_tid, 0)),
     179        1484 :     _mortar_data(_mci_fe_problem.mortarData()),
     180        1484 :     _secondary_id(
     181        2968 :         _mci_mesh.getBoundaryID(moose_object->getParam<BoundaryName>("secondary_boundary"))),
     182        2968 :     _primary_id(_mci_mesh.getBoundaryID(moose_object->getParam<BoundaryName>("primary_boundary"))),
     183        1484 :     _secondary_subdomain_id(
     184        2968 :         _mci_mesh.getSubdomainID(moose_object->getParam<SubdomainName>("secondary_subdomain"))),
     185        1484 :     _primary_subdomain_id(
     186        2968 :         _mci_mesh.getSubdomainID(moose_object->getParam<SubdomainName>("primary_subdomain"))),
     187        2968 :     _secondary_set({_secondary_id}),
     188        2968 :     _interpolate_normals(moose_object->getParam<bool>("interpolate_normals")),
     189        1484 :     _phys_points_secondary(_mci_assembly.qPointsFace()),
     190        1484 :     _phys_points_primary(_mci_assembly.qPointsFaceNeighbor()),
     191        1484 :     _qrule_msm(_mci_assembly.qRuleMortar()),
     192        1484 :     _qrule_face(_mci_assembly.qRuleFace()),
     193        1484 :     _lower_secondary_elem(_mci_assembly.lowerDElem()),
     194        1484 :     _lower_primary_elem(_mci_assembly.neighborLowerDElem()),
     195        1484 :     _JxW_msm(_mci_assembly.jxWMortar()),
     196        2968 :     _msm_elem(_mci_assembly.msmElem())
     197             : {
     198        2968 :   const bool displaced = moose_object->isParamValid("use_displaced_mesh")
     199        5936 :                              ? moose_object->getParam<bool>("use_displaced_mesh")
     200        1484 :                              : false;
     201        2968 :   const auto minimum_projection_angle = moose_object->getParam<Real>("minimum_projection_angle");
     202             :   const auto mortar_3d_subpatch_plane =
     203        4452 :       moose_object->getParam<MooseEnum>("mortar_3d_subpatch_plane")
     204        1484 :           .getEnum<Mortar3DSubpatchPlane>();
     205             : 
     206             :   // Only geometric 3D mode interprets this value as an angle between subpatch normals. Averaged
     207             :   // normal and 2D interfaces continue to use the other projection-angle checks.
     208        1902 :   if (_mci_mesh.dimension() == 3 &&
     209        2292 :       mortar_3d_subpatch_plane == Mortar3DSubpatchPlane::GEOMETRIC_NORMAL &&
     210         390 :       (minimum_projection_angle < 0.0 || minimum_projection_angle > 90.0))
     211           6 :     moose_object->paramError("minimum_projection_angle",
     212             :                              "Must be between 0 and 90 degrees when "
     213             :                              "mortar_3d_subpatch_plane = GEOMETRIC_NORMAL.");
     214             : 
     215             :   // Create the mortar interface if it hasn't already been created
     216        2962 :   _mci_fe_problem.createMortarInterface(
     217        1481 :       std::make_pair(_primary_id, _secondary_id),
     218           0 :       std::make_pair(_primary_subdomain_id, _secondary_subdomain_id),
     219             :       displaced,
     220        2956 :       moose_object->getParam<bool>("periodic"),
     221        2956 :       moose_object->getParam<bool>("debug_mesh"),
     222        5912 :       moose_object->getParam<bool>("correct_edge_dropping"),
     223             :       minimum_projection_angle,
     224             :       mortar_3d_subpatch_plane,
     225             :       moose_object->getParam<MooseEnum>("triangulation"),
     226        4437 :       moose_object->getParam<bool>("triangulate_triangles"),
     227        4437 :       moose_object->getParam<MooseEnum>("mortar_3d_qp_mapping")
     228             :           .getEnum<Mortar3DQuadraturePointMapping>());
     229             : 
     230        1475 :   _amg = &_mci_fe_problem.getMortarInterface(
     231        1475 :       std::make_pair(_primary_id, _secondary_id),
     232        1475 :       std::make_pair(_primary_subdomain_id, _secondary_subdomain_id),
     233             :       displaced);
     234             : 
     235        1475 :   const auto & secondary_set = _mortar_data.getHigherDimSubdomainIDs(_secondary_subdomain_id);
     236        1475 :   const auto & primary_set = _mortar_data.getHigherDimSubdomainIDs(_primary_subdomain_id);
     237             : 
     238        2950 :   std::set_union(secondary_set.begin(),
     239             :                  secondary_set.end(),
     240             :                  primary_set.begin(),
     241             :                  primary_set.end(),
     242        1475 :                  std::inserter(_higher_dim_subdomain_ids, _higher_dim_subdomain_ids.begin()));
     243        1475 :   _boundary_ids = {_secondary_id, _primary_id};
     244        1475 : }
     245             : 
     246             : void
     247      581232 : MortarConsumerInterface::setNormals()
     248             : {
     249      581232 :   if (interpolateNormals())
     250      580036 :     _normals = amg().getNormals(*_lower_secondary_elem, _qrule_face->get_points());
     251             :   else
     252        1196 :     _normals = amg().getNodalNormals(*_lower_secondary_elem);
     253      581232 : }
     254             : 
     255             : void
     256      196448 : MortarConsumerInterface::trimDerivative(const dof_id_type remove_derivative_index,
     257             :                                         ADReal & dual_number)
     258             : {
     259      196448 :   auto md_it = dual_number.derivatives().nude_data().begin();
     260      196448 :   auto mi_it = dual_number.derivatives().nude_indices().begin();
     261             : 
     262      196448 :   auto d_it = dual_number.derivatives().nude_data().begin();
     263             : 
     264      196448 :   for (auto i_it = dual_number.derivatives().nude_indices().begin();
     265      554252 :        i_it != dual_number.derivatives().nude_indices().end();
     266      357804 :        ++i_it, ++d_it)
     267      357804 :     if (*i_it != remove_derivative_index)
     268             :     {
     269      333806 :       *mi_it = *i_it;
     270      333806 :       *md_it = *d_it;
     271      333806 :       ++mi_it;
     272      333806 :       ++md_it;
     273             :     }
     274             : 
     275      196448 :   std::size_t n_indices = md_it - dual_number.derivatives().nude_data().begin();
     276      196448 :   dual_number.derivatives().nude_indices().resize(n_indices);
     277      196448 :   dual_number.derivatives().nude_data().resize(n_indices);
     278      196448 : }

Generated by: LCOV version 1.14