LCOV - code coverage report
Current view: top level - include/constraints - AutomaticMortarGeneration.h (source / functions) Hit Total Coverage
Test: idaholab/moose framework: #33390 (250e9c) with base 846a5c Lines: 31 31 100.0 %
Date: 2026-07-31 18:15:22 Functions: 16 16 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             : #pragma once
      11             : 
      12             : #include "MortarSegmentInfo.h"
      13             : #include "Mortar3DSubpatchPlane.h"
      14             : #include "MooseHashing.h"
      15             : #include "ConsoleStreamInterface.h"
      16             : #include "MooseError.h"
      17             : #include "MooseUtils.h"
      18             : 
      19             : // libMesh includes
      20             : #include "libmesh/id_types.h"
      21             : #include "libmesh/equation_systems.h"
      22             : #include "libmesh/elem.h"
      23             : #include "libmesh/int_range.h"
      24             : #include "libmesh/vector_value.h"
      25             : 
      26             : #include "metaphysicl/raw_type.h"
      27             : 
      28             : // C++ includes
      29             : #include <array>
      30             : #include <cmath>
      31             : #include <functional>
      32             : #include <optional>
      33             : #include <set>
      34             : #include <memory>
      35             : #include <vector>
      36             : #include <unordered_map>
      37             : 
      38             : // Forward declarations
      39             : namespace libMesh
      40             : {
      41             : class MeshBase;
      42             : class System;
      43             : }
      44             : class GetPot;
      45             : 
      46             : // Using statements
      47             : using libMesh::boundary_id_type;
      48             : using libMesh::CompareDofObjectsByID;
      49             : using libMesh::dof_id_type;
      50             : using libMesh::Elem;
      51             : using libMesh::MeshBase;
      52             : using libMesh::Node;
      53             : using libMesh::Point;
      54             : using libMesh::Real;
      55             : using libMesh::subdomain_id_type;
      56             : using libMesh::VectorValue;
      57             : 
      58             : typedef boundary_id_type BoundaryID;
      59             : typedef subdomain_id_type SubdomainID;
      60             : 
      61             : namespace Moose
      62             : {
      63             : namespace Mortar
      64             : {
      65             : /**
      66             :  * Construct the two tangent vectors used by mortar from a unit normal. The template keeps the
      67             :  * Real-valued and AD contact geometry on the same Householder chart and therefore gives them
      68             :  * identical values.
      69             :  * See Lopes, Silva, and Ambrosio, Computer-Aided Design 45(3), 2013, pp. 683-694.
      70             :  */
      71             : template <typename Vector>
      72             : std::array<Vector, 2>
      73       43060 : householderTangents(const Vector & normal)
      74             : {
      75             :   mooseAssert(MooseUtils::absoluteFuzzyEqual(MetaPhysicL::raw_value(normal.norm()), 1),
      76             :               "The input nodal normal should have unity norm");
      77             : 
      78       43060 :   const Vector h_vector(normal(0) + 1.0, normal(1), normal(2));
      79             : 
      80             :   // This chart is singular at (-1, 0, 0), where a constant orthonormal basis with zero
      81             :   // derivatives is used.
      82       43060 :   if (std::abs(MetaPhysicL::raw_value(h_vector(0))) < TOLERANCE)
      83        1878 :     return {{Vector(0, 1, 0), Vector(0, 0, -1)}};
      84             : 
      85       41180 :   const auto h = h_vector.norm();
      86       41178 :   return {{Vector(-2.0 * h_vector(0) * h_vector(1) / (h * h),
      87       41178 :                   1.0 - 2.0 * h_vector(1) * h_vector(1) / (h * h),
      88       41178 :                   -2.0 * h_vector(1) * h_vector(2) / (h * h)),
      89       41178 :            Vector(-2.0 * h_vector(0) * h_vector(2) / (h * h),
      90       41178 :                   -2.0 * h_vector(1) * h_vector(2) / (h * h),
      91      205878 :                   1.0 - 2.0 * h_vector(2) * h_vector(2) / (h * h))}};
      92           4 : }
      93             : }
      94             : }
      95             : 
      96             : /**
      97             :  * Parent-face reference coordinates associated with the vertices of one triangular mortar segment.
      98             :  */
      99             : struct MortarSegmentReferencePoints
     100             : {
     101             :   std::array<Point, 3> secondary_reference_points;
     102             :   std::array<Point, 3> primary_reference_points;
     103             : };
     104             : 
     105             : /**
     106             :  * This class is a container/interface for the objects involved in
     107             :  * automatic generation of mortar spaces.
     108             :  */
     109             : class AutomaticMortarGeneration : public ConsoleStreamInterface
     110             : {
     111             : public:
     112             :   /**
     113             :    * The name of the nodal normals system. We store this in one place
     114             :    * so it's easy to change later.
     115             :    */
     116             :   const static std::string system_name;
     117             : 
     118             :   /**
     119             :    * Must be constructed with a reference to the Mesh we are
     120             :    * generating mortar spaces for.
     121             :    */
     122             :   AutomaticMortarGeneration(MooseApp & app,
     123             :                             MeshBase & mesh_in,
     124             :                             const std::pair<BoundaryID, BoundaryID> & boundary_key,
     125             :                             const std::pair<SubdomainID, SubdomainID> & subdomain_key,
     126             :                             bool on_displaced,
     127             :                             bool periodic,
     128             :                             const bool debug,
     129             :                             const bool correct_edge_dropping,
     130             :                             const Real minimum_projection_angle,
     131             :                             const Mortar3DSubpatchPlane mortar_3d_subpatch_plane,
     132             :                             const MortarSegmentTriangulationMode triangulation_mode,
     133             :                             const bool triangulate_triangles,
     134             :                             const Mortar3DQuadraturePointMapping mortar_3d_qp_mapping =
     135             :                                 Mortar3DQuadraturePointMapping::NORMAL_PROJECTION);
     136             : 
     137             :   /**
     138             :    * Once the secondary_requested_boundary_ids and
     139             :    * primary_requested_boundary_ids containers have been filled in,
     140             :    * call this function to build node-to-Elem maps for the
     141             :    * lower-dimensional elements.
     142             :    */
     143             :   void buildNodeToElemMaps();
     144             : 
     145             :   /**
     146             :    * Computes and stores the nodal normal/tangent vectors in a local data
     147             :    * structure instead of using the ExplicitSystem/NumericVector
     148             :    * approach. This design was triggered by the way that the
     149             :    * GhostingFunctor operates, but I think it is a better/more
     150             :    * efficient way to do it anyway.
     151             :    */
     152             :   void computeNodalGeometry();
     153             : 
     154             :   /**
     155             :    * Project secondary nodes (find xi^(2) values) to the closest points on
     156             :    * the primary surface.
     157             :    * Inputs:
     158             :    * - The nodal normals values
     159             :    * - mesh
     160             :    * - nodes_to_primary_elem_map
     161             :    *
     162             :    * Outputs:
     163             :    * - secondary_node_and_elem_to_xi2_primary_elem
     164             :    *
     165             :    * Defined in the file project_secondary_nodes.C.
     166             :    */
     167             :   void projectSecondaryNodes();
     168             : 
     169             :   /**
     170             :    * (Inverse) project primary nodes to the points on the secondary surface
     171             :    * where they would have come from (find (xi^(1) values)).
     172             :    *
     173             :    * Inputs:
     174             :    * - The nodal normals values
     175             :    * - mesh
     176             :    * - nodes_to_secondary_elem_map
     177             :    *
     178             :    * Outputs:
     179             :    * - primary_node_and_elem_to_xi1_secondary_elem
     180             :    *
     181             :    * Defined in the file project_primary_nodes.C.
     182             :    */
     183             :   void projectPrimaryNodes();
     184             : 
     185             :   /**
     186             :    * Builds the mortar segment mesh once the secondary and primary node
     187             :    * projections have been completed.
     188             :    *
     189             :    * Inputs:
     190             :    * - mesh
     191             :    * - primary_node_and_elem_to_xi1_secondary_elem
     192             :    * - secondary_node_and_elem_to_xi2_primary_elem
     193             :    * - nodes_to_primary_elem_map
     194             :    *
     195             :    * Outputs:
     196             :    * - mortar_segment_mesh
     197             :    * - msm_elem_to_info
     198             :    *
     199             :    * Defined in the file build_mortar_segment_mesh.C.
     200             :    */
     201             :   void buildMortarSegmentMesh();
     202             : 
     203             :   /**
     204             :    * Builds the mortar segment mesh once the secondary and primary node
     205             :    * projections have been completed.
     206             :    *
     207             :    * Inputs:
     208             :    * - mesh
     209             :    *
     210             :    * Outputs:
     211             :    * - mortar_segment_mesh
     212             :    * - msm_elem_to_info
     213             :    */
     214             :   void buildMortarSegmentMesh3d();
     215             : 
     216             :   /**
     217             :    * Statistics for one primary-secondary subdomain pair.
     218             :    * Secondary/primary lower-d stats reflect local data (all data for replicated meshes).
     219             :    */
     220             :   struct MsmSubdomainStats
     221             :   {
     222             :     SubdomainID primary_subd_id;
     223             :     SubdomainID secondary_subd_id;
     224             :     std::size_t secondary_lower_n_elems;
     225             :     Real secondary_lower_max_volume;
     226             :     Real secondary_lower_min_volume;
     227             :     Real secondary_lower_median_volume;
     228             :     std::size_t primary_lower_n_elems;
     229             :     Real primary_lower_max_volume;
     230             :     Real primary_lower_min_volume;
     231             :     Real primary_lower_median_volume;
     232             :     std::size_t msm_n_elems;
     233             :     Real msm_max_volume;
     234             :     Real msm_min_volume;
     235             :     Real msm_median_volume;
     236             :   };
     237             : 
     238             :   /**
     239             :    * Computes mortar segment mesh statistics and returns one entry per subdomain pair.
     240             :    * Must be called collectively on all ranks.
     241             :    */
     242             :   std::vector<MsmSubdomainStats> computeMsmStatistics();
     243             : 
     244             :   /**
     245             :    * Prints mortar segment mesh statistics to console (calls computeMsmStatistics internally)
     246             :    */
     247             :   void msmStatistics();
     248             : 
     249             :   /**
     250             :    * Clears the mortar segment mesh and accompanying data structures
     251             :    */
     252             :   void clear();
     253             : 
     254             :   /**
     255             :    * Invalidates the cached MSM node/element ID starting offset so that the next call to
     256             :    * buildMortarSegmentMesh3d() recomputes it via allgather. Call this when mesh topology changes.
     257             :    */
     258         172 :   void meshChanged() { _msm_node_id_start = std::nullopt; }
     259             : 
     260             :   /**
     261             :    * returns whether this object is on the displaced mesh
     262             :    */
     263             :   bool onDisplaced() const { return _on_displaced; }
     264             : 
     265             :   /**
     266             :    * @return The nodal normals associated with the provided \p secondary_elem
     267             :    */
     268             :   std::vector<Point> getNodalNormals(const Elem & secondary_elem) const;
     269             : 
     270             :   /**
     271             :    * Compute the two nodal tangents, which are built on-the-fly.
     272             :    * @return The nodal tangents associated with the provided \p secondary_elem
     273             :    */
     274             :   std::array<MooseUtils::SemidynamicVector<Point, 9>, 2>
     275             :   getNodalTangents(const Elem & secondary_elem) const;
     276             : 
     277             :   /**
     278             :    * Build the normalized JxW-weighted secondary nodal normals from AD nodal coordinates.
     279             :    *
     280             :    * The coordinate functor combines the node's displacement degrees of freedom with the coordinate
     281             :    * snapshot used to compute the stored nodal geometry. This keeps residual values and their
     282             :    * derivatives evaluated at the same geometry state.
     283             :    */
     284             :   void
     285             :   computeADNodalNormals(const std::function<ADPoint(const Node &, const Point &)> & coordinate,
     286             :                         std::unordered_map<const Node *, ADRealVectorValue> & nodal_normals) const;
     287             : 
     288             :   /**
     289             :    * Compute on-the-fly mapping from secondary interior parent nodes to lower dimensional nodes
     290             :    * @return The map from secondary interior parent nodes to lower dimensional nodes
     291             :    */
     292             :   std::map<unsigned int, unsigned int>
     293             :   getSecondaryIpToLowerElementMap(const Elem & lower_secondary_elem) const;
     294             : 
     295             :   /**
     296             :    * Compute on-the-fly mapping from primary interior parent nodes to its corresponding lower
     297             :    * dimensional nodes
     298             :    * @return The map from primary interior parent nodes to its corresponding lower dimensional
     299             :    * nodes
     300             :    */
     301             :   std::map<unsigned int, unsigned int>
     302             :   getPrimaryIpToLowerElementMap(const Elem & primary_elem,
     303             :                                 const Elem & primary_elem_ip,
     304             :                                 const Elem & lower_secondary_elem) const;
     305             : 
     306             :   /**
     307             :    * Compute the normals at given reference points on a secondary element
     308             :    * @param secondary_elem The secondary element used to query for associated nodal normals
     309             :    * @param xi1_pts The reference points on the secondary element to evaluate the normals at. The
     310             :    * points should only be non-zero in the zeroth entry  because right now our mortar mesh elements
     311             :    * are always 1D
     312             :    * @return The normals
     313             :    */
     314             :   std::vector<Point> getNormals(const Elem & secondary_elem,
     315             :                                 const std::vector<Point> & xi1_pts) const;
     316             : 
     317             :   /**
     318             :    * Compute the normals at given reference points on a secondary element
     319             :    * @param secondary_elem The secondary element used to query for associated nodal normals
     320             :    * @param 1d_xi1_pts The reference points on the secondary element to evaluate the normals at. The
     321             :    * "points" are single reals corresponding to xi because right now our mortar mesh elements are
     322             :    * always 1D
     323             :    * @return The normals
     324             :    */
     325             :   std::vector<Point> getNormals(const Elem & secondary_elem,
     326             :                                 const std::vector<Real> & oned_xi1_pts) const;
     327             : 
     328             :   /**
     329             :    * Return lower dimensional secondary element given its interior parent. Helpful outside the
     330             :    * mortar generation to locate mortar-related quantities.
     331             :    * @param secondary_elem_id The secondary interior parent element id used to query for associated
     332             :    * lower dimensional element
     333             :    * @return The corresponding lower dimensional secondary element
     334             :    */
     335             :   const Elem * getSecondaryLowerdElemFromSecondaryElem(dof_id_type secondary_elem_id) const;
     336             : 
     337             :   /**
     338             :    * Get list of secondary nodes that don't contribute to interaction with any primary element.
     339             :    * Used to enforce zero values on inactive DoFs of nodal variables.
     340             :    */
     341             :   void computeInactiveLMNodes();
     342             : 
     343             :   /**
     344             :    * Computes inactive secondary nodes when incorrect edge dropping behavior is enabled
     345             :    * (any node touching a partially or fully dropped element is dropped)
     346             :    */
     347             :   void computeIncorrectEdgeDroppingInactiveLMNodes();
     348             : 
     349             :   /**
     350             :    * Get list of secondary elems without any corresponding primary elements.
     351             :    * Used to enforce zero values on inactive DoFs of elemental variables.
     352             :    */
     353             :   void computeInactiveLMElems();
     354             : 
     355             :   /**
     356             :    * @return The mortar interface coupling
     357             :    */
     358             :   const std::unordered_map<dof_id_type, std::unordered_set<dof_id_type>> &
     359      491460 :   mortarInterfaceCoupling() const
     360             :   {
     361      491460 :     return _mortar_interface_coupling;
     362             :   }
     363             : 
     364             :   /**
     365             :    * @return The primary-secondary boundary ID pair
     366             :    */
     367             :   const std::pair<BoundaryID, BoundaryID> & primarySecondaryBoundaryIDPair() const;
     368             : 
     369             :   /**
     370             :    * @return The mortar segment mesh
     371             :    */
     372        2194 :   const MeshBase & mortarSegmentMesh() const { return *_mortar_segment_mesh; }
     373             : 
     374             :   /**
     375             :    * @return The mortar segment element to corresponding information
     376             :    */
     377     1128008 :   const std::unordered_map<const Elem *, MortarSegmentInfo> & mortarSegmentMeshElemToInfo() const
     378             :   {
     379     1128008 :     return _msm_elem_to_info;
     380             :   }
     381             : 
     382       23389 :   int dim() const { return _mesh.mesh_dimension(); }
     383             : 
     384             :   /// Return the 3D mortar quadrature-point mapping method.
     385      499816 :   Mortar3DQuadraturePointMapping mortar3DQpMapping() const { return _mortar_3d_qp_mapping; }
     386             : 
     387             :   /// Return the parent-face reference coordinates for a mortar segment.
     388             :   const MortarSegmentReferencePoints &
     389             :   mortarSegmentReferencePoints(const Elem & mortar_segment_elem) const;
     390             : 
     391             :   /**
     392             :    * @return The set of nodes on which mortar constraints are not active
     393             :    */
     394       21873 :   const std::unordered_set<const Node *> & getInactiveLMNodes() const
     395             :   {
     396       21873 :     return _inactive_local_lm_nodes;
     397             :   }
     398             : 
     399             :   /**
     400             :    * @return The list of secondary elems on which mortar constraint is not active
     401             :    */
     402       15414 :   const std::unordered_set<const Elem *> & getInactiveLMElems() const
     403             :   {
     404       15414 :     return _inactive_local_lm_elems;
     405             :   }
     406             : 
     407       15414 :   bool incorrectEdgeDropping() const { return !_correct_edge_dropping; }
     408             : 
     409             :   using MortarFilterIter =
     410             :       std::unordered_map<dof_id_type, std::set<Elem *, CompareDofObjectsByID>>::const_iterator;
     411             : 
     412             :   /**
     413             :    * @return A vector of iterators that point to the lower dimensional secondary elements and their
     414             :    * associated mortar segment elements that would have nonzero values for a Lagrange shape function
     415             :    * associated with the provided node. This method may return an empty container if the node is
     416             :    * away from the mortar mesh
     417             :    */
     418             :   std::vector<MortarFilterIter> secondariesToMortarSegments(const Node & node) const;
     419             : 
     420             :   /**
     421             :    * @return the lower dimensional secondary element ids and their associated mortar segment
     422             :    * elements
     423             :    */
     424             :   const std::unordered_map<dof_id_type, std::set<Elem *, CompareDofObjectsByID>> &
     425       12744 :   secondariesToMortarSegments() const
     426             :   {
     427       12744 :     return _secondary_elems_to_mortar_segments;
     428             :   }
     429             : 
     430             :   /**
     431             :    * @return All the secondary interior parent subdomain IDs associated with the mortar mesh
     432             :    */
     433        1300 :   const std::set<SubdomainID> & secondaryIPSubIDs() const { return _secondary_ip_sub_ids; }
     434             : 
     435             :   /**
     436             :    * @return All the primary interior parent subdomain IDs associated with the mortar mesh
     437             :    */
     438        1300 :   const std::set<SubdomainID> & primaryIPSubIDs() const { return _primary_ip_sub_ids; }
     439             : 
     440             :   /**
     441             :    * @return Map from node id to secondary lower-d element pointer
     442             :    */
     443             :   const std::unordered_map<dof_id_type, std::vector<const Elem *>> & nodesToSecondaryElem() const
     444             :   {
     445             :     return _nodes_to_secondary_elem_map;
     446             :   }
     447             : 
     448             :   /**
     449             :    * initialize mortar-mesh based output
     450             :    */
     451             :   void initOutput();
     452             : 
     453             : private:
     454             :   /**
     455             :    * Write the mortar segment mesh to exodus
     456             :    */
     457             :   void outputMortarMesh();
     458             : 
     459             :   /**
     460             :    * @returns A string uniquely identifying this mortar interface
     461             :    */
     462             :   std::string mortarInterfaceName() const;
     463             : 
     464             :   /**
     465             :    * build the \p _mortar_interface_coupling data
     466             :    */
     467             :   void buildCouplingInformation();
     468             : 
     469             :   /// The Moose app
     470             :   MooseApp & _app;
     471             : 
     472             :   /// Reference to the mesh stored in equation_systems.
     473             :   MeshBase & _mesh;
     474             : 
     475             :   /// The boundary ids corresponding to all the secondary surfaces.
     476             :   std::set<BoundaryID> _secondary_requested_boundary_ids;
     477             : 
     478             :   /// The boundary ids corresponding to all the primary surfaces.
     479             :   std::set<BoundaryID> _primary_requested_boundary_ids;
     480             : 
     481             :   /// A list of primary/secondary boundary id pairs corresponding to each
     482             :   /// side of the mortar interface.
     483             :   std::vector<std::pair<BoundaryID, BoundaryID>> _primary_secondary_boundary_id_pairs;
     484             : 
     485             :   /// Map from nodes to connected lower-dimensional elements on the secondary/primary subdomains.
     486             :   std::unordered_map<dof_id_type, std::vector<const Elem *>> _nodes_to_secondary_elem_map;
     487             :   std::unordered_map<dof_id_type, std::vector<const Elem *>> _nodes_to_primary_elem_map;
     488             : 
     489             :   /// Similar to the map above, but associates a (Secondary Node, Secondary Elem)
     490             :   /// pair to a (xi^(2), primary Elem) pair. This allows a single secondary node, which is
     491             :   /// potentially connected to two elements on the secondary side, to be associated with
     492             :   /// multiple primary Elem/xi^(2) values to handle the case where the primary and secondary
     493             :   /// nodes are "matching".
     494             :   /// In this configuration:
     495             :   ///
     496             :   ///    A     B
     497             :   /// o-----o-----o  (secondary orientation ->)
     498             :   ///       |
     499             :   ///       v
     500             :   /// ------x------ (primary orientation <-)
     501             :   ///    C     D
     502             :   ///
     503             :   /// The entries in the map should be:
     504             :   /// (Elem A, Node 1) -> (Elem C, xi^(2)=-1)
     505             :   /// (Elem B, Node 0) -> (Elem D, xi^(2)=+1)
     506             :   std::unordered_map<std::pair<const Node *, const Elem *>, std::pair<Real, const Elem *>>
     507             :       _secondary_node_and_elem_to_xi2_primary_elem;
     508             : 
     509             :   /// Same type of container, but for mapping (Primary Node ID, Primary Node,
     510             :   /// Primary Elem) -> (xi^(1), Secondary Elem) where they are inverse-projected along
     511             :   /// the nodal normal direction. Note that the first item of the key, the primary
     512             :   /// node ID, is important for storing the key-value pairs in a consistent order
     513             :   /// across processes, e.g. this container has to be ordered!
     514             :   std::map<std::tuple<dof_id_type, const Node *, const Elem *>, std::pair<Real, const Elem *>>
     515             :       _primary_node_and_elem_to_xi1_secondary_elem;
     516             : 
     517             :   /// 1D Mesh of mortar segment elements which gets built by the call
     518             :   /// to build_mortar_segment_mesh().
     519             :   std::unique_ptr<MeshBase> _mortar_segment_mesh;
     520             : 
     521             :   /// Map between Elems in the mortar segment mesh and their info
     522             :   /// structs. This gets filled in by the call to
     523             :   /// build_mortar_segment_mesh().
     524             :   std::unordered_map<const Elem *, MortarSegmentInfo> _msm_elem_to_info;
     525             : 
     526             :   /// Keeps track of the mapping between lower-dimensional elements and
     527             :   /// the side_id of the interior_parent which they are.
     528             :   std::unordered_map<const Elem *, unsigned int> _lower_elem_to_side_id;
     529             : 
     530             :   /// A list of primary/secondary subdomain id pairs corresponding to each
     531             :   /// side of the mortar interface.
     532             :   std::vector<std::pair<SubdomainID, SubdomainID>> _primary_secondary_subdomain_id_pairs;
     533             : 
     534             :   /// The secondary/primary lower-dimensional boundary subdomain ids are the
     535             :   /// secondary/primary *boundary* ids
     536             :   std::set<SubdomainID> _secondary_boundary_subdomain_ids;
     537             :   std::set<SubdomainID> _primary_boundary_subdomain_ids;
     538             : 
     539             :   /// Used by the AugmentSparsityOnInterface functor to determine
     540             :   /// whether a given Elem is coupled to any others across the gap, and
     541             :   /// to explicitly set up the dependence between interior_parent()
     542             :   /// elements on the secondary side and their lower-dimensional sides
     543             :   /// which are on the interface. This latter type of coupling must be
     544             :   /// explicitly declared when there is no primary_elem for a given
     545             :   /// mortar segment and you are using e.g.  a P^1-P^0 discretization
     546             :   /// which does not induce the coupling automatically.
     547             :   std::unordered_map<dof_id_type, std::unordered_set<dof_id_type>> _mortar_interface_coupling;
     548             : 
     549             :   /// Container for storing the nodal normal vector associated with each secondary node.
     550             :   std::unordered_map<const Node *, Point> _secondary_node_to_nodal_normal;
     551             : 
     552             :   /// Container for storing the nodal tangent/binormal vectors associated with each secondary node
     553             :   /// (Householder approach).
     554             :   std::unordered_map<const Node *, std::array<Point, 2>> _secondary_node_to_hh_nodal_tangents;
     555             : 
     556             :   /// Coordinates used to construct the stored nodal normals
     557             :   std::unordered_map<const Node *, Point> _nodal_geometry_coordinate_snapshot;
     558             : 
     559             :   /// Map from full dimensional secondary element id to lower dimensional secondary element
     560             :   std::unordered_map<dof_id_type, const Elem *> _secondary_element_to_secondary_lowerd_element;
     561             : 
     562             :   // List of inactive lagrange multiplier nodes (for nodal variables)
     563             :   std::unordered_set<const Node *> _inactive_local_lm_nodes;
     564             : 
     565             :   /// List of inactive lagrange multiplier nodes (for elemental variables)
     566             :   std::unordered_set<const Elem *> _inactive_local_lm_elems;
     567             : 
     568             :   /// We maintain a mapping from lower-dimensional secondary elements in the original mesh to (sets
     569             :   /// of) elements in mortar_segment_mesh.  This allows us to quickly determine which elements need
     570             :   /// to be split.
     571             :   std::unordered_map<dof_id_type, std::set<Elem *, CompareDofObjectsByID>>
     572             :       _secondary_elems_to_mortar_segments;
     573             : 
     574             :   /// All the secondary interior parent subdomain IDs associated with the mortar mesh
     575             :   std::set<SubdomainID> _secondary_ip_sub_ids;
     576             : 
     577             :   /// All the primary interior parent subdomain IDs associated with the mortar mesh
     578             :   std::set<SubdomainID> _primary_ip_sub_ids;
     579             : 
     580             :   /**
     581             :    * Helper function responsible for projecting secondary nodes
     582             :    * onto primary elements for a single primary/secondary pair. Called by the class member
     583             :    * AutomaticMortarGeneration::project_secondary_nodes().
     584             :    */
     585             :   void projectSecondaryNodesSinglePair(SubdomainID lower_dimensional_primary_subdomain_id,
     586             :                                        SubdomainID lower_dimensional_secondary_subdomain_id);
     587             : 
     588             :   /**
     589             :    * Helper function used internally by AutomaticMortarGeneration::project_primary_nodes().
     590             :    */
     591             :   void projectPrimaryNodesSinglePair(SubdomainID lower_dimensional_primary_subdomain_id,
     592             :                                      SubdomainID lower_dimensional_secondary_subdomain_id);
     593             : 
     594             :   /**
     595             :    * Process aligned nodes
     596             :    * @returns whether mortar segment(s) were created
     597             :    */
     598             :   bool processAlignedNodes(const Node & secondary_node,
     599             :                            const Node & primary_node,
     600             :                            const std::vector<const Elem *> * secondary_node_neighbors,
     601             :                            const std::vector<const Elem *> * primary_node_neighbors,
     602             :                            const VectorValue<Real> & nodal_normal,
     603             :                            const Elem & candidate_element,
     604             :                            std::set<const Elem *> & rejected_element_candidates);
     605             : 
     606             :   /// Whether to print debug output
     607             :   const bool _debug;
     608             : 
     609             :   /// Whether this object is on the displaced mesh
     610             :   const bool _on_displaced;
     611             : 
     612             :   /// Whether this object will be generating a mortar segment mesh for periodic constraints
     613             :   const bool _periodic;
     614             : 
     615             :   /// Whether the mortar segment mesh is distributed
     616             :   const bool _distributed;
     617             : 
     618             :   /// Newton solve tolerance for node projections
     619             :   Real _newton_tolerance = 1e-12;
     620             : 
     621             :   /// Tolerance for checking projection xi values. Usually we are checking whether we projected onto
     622             :   /// a certain element (in which case -1 <= xi <= 1) or whether we should have *already* projected
     623             :   /// a primary node (in which case we error if abs(xi) is sufficiently close to 1)
     624             :   Real _xi_tolerance = 1e-6;
     625             : 
     626             :   /// Flag to enable regressed treatment of edge dropping where all LM DoFs on edge dropping element
     627             :   /// are strongly set to 0.
     628             :   const bool _correct_edge_dropping;
     629             : 
     630             :   /// Parameter to control which angle (in degrees) is admissible for the creation of mortar segments.
     631             :   /// If set to a value close to zero, very oblique projections are allowed, which can result in mortar
     632             :   /// segments solving physics not meaningfully and overprojection of primary nodes onto the mortar
     633             :   /// segment mesh in extreme cases. This parameter is mostly intended for mortar mesh debugging purposes in 2D.
     634             :   const Real _minimum_projection_angle;
     635             : 
     636             :   /// Method used to define the local projection planes for 3D secondary subpatches.
     637             :   const Mortar3DSubpatchPlane _mortar_3d_subpatch_plane;
     638             : 
     639             :   /// Triangulation mode used for clipped 3D mortar polygons.
     640             :   const MortarSegmentTriangulationMode _triangulation_mode;
     641             : 
     642             :   /// Whether already-triangular clipped polygons should still be centroid-subdivided.
     643             :   const bool _triangulate_triangles;
     644             : 
     645             :   /// Method used to map 3D mortar segment quadrature points to primary and secondary faces.
     646             :   const Mortar3DQuadraturePointMapping _mortar_3d_qp_mapping;
     647             : 
     648             :   /// Reference-coordinate data used only by the reference-interpolation mapping mode.
     649             :   std::unordered_map<const Elem *, MortarSegmentReferencePoints> _msm_elem_to_reference_points;
     650             : 
     651             :   /// Storage for the input parameters used by the mortar nodal geometry output
     652             :   std::unique_ptr<InputParameters> _output_params;
     653             : 
     654             :   /// Cached per-rank starting ID for 3D MSM nodes/elements. nullopt forces recomputation on next
     655             :   /// buildMortarSegmentMesh3d() call. Reset by meshChanged() on topology change so the allgather
     656             :   /// is skipped for displaced-mesh residual updates that only move nodes.
     657             :   std::optional<dof_id_type> _msm_node_id_start;
     658             : 
     659             :   /// Debugging container for printing information about fraction of successful projections for
     660             :   /// secondary nodes. If !_debug then this should always be empty
     661             :   std::unordered_set<dof_id_type> _projected_secondary_nodes;
     662             : 
     663             :   /// Secondary nodes that failed to project
     664             :   std::unordered_set<dof_id_type> _failed_secondary_node_projections;
     665             : 
     666             :   friend class MortarNodalGeometryOutput;
     667             :   friend class AugmentSparsityOnInterface;
     668             : };
     669             : 
     670             : inline const std::pair<BoundaryID, BoundaryID> &
     671       14639 : AutomaticMortarGeneration::primarySecondaryBoundaryIDPair() const
     672             : {
     673             :   mooseAssert(_primary_secondary_boundary_id_pairs.size() == 1,
     674             :               "We currently only support a single boundary pair per mortar generation object");
     675             : 
     676       14639 :   return _primary_secondary_boundary_id_pairs.front();
     677             : }

Generated by: LCOV version 1.14