LCOV - code coverage report
Current view: top level - src/constraints - NodeFaceConstraint.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 419b9d Lines: 140 156 89.7 %
Date: 2025-08-08 20:01:16 Functions: 12 14 85.7 %
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 "NodeFaceConstraint.h"
      11             : 
      12             : // MOOSE includes
      13             : #include "Assembly.h"
      14             : #include "MooseEnum.h"
      15             : #include "MooseMesh.h"
      16             : #include "MooseVariableFE.h"
      17             : #include "PenetrationLocator.h"
      18             : #include "SystemBase.h"
      19             : 
      20             : #include "libmesh/string_to_enum.h"
      21             : 
      22             : InputParameters
      23       42999 : NodeFaceConstraint::validParams()
      24             : {
      25       42999 :   MooseEnum orders("FIRST SECOND THIRD FOURTH", "FIRST");
      26       42999 :   InputParameters params = Constraint::validParams();
      27       42999 :   params.addParam<BoundaryName>("secondary", "The boundary ID associated with the secondary side");
      28       42999 :   params.addParam<BoundaryName>("primary", "The boundary ID associated with the primary side");
      29       42999 :   params.addParam<Real>("tangential_tolerance",
      30             :                         "Tangential distance to extend edges of contact surfaces");
      31       42999 :   params.addParam<Real>(
      32             :       "normal_smoothing_distance",
      33             :       "Distance from edge in parametric coordinates over which to smooth contact normal");
      34       42999 :   params.addParam<std::string>("normal_smoothing_method",
      35             :                                "Method to use to smooth normals (edge_based|nodal_normal_based)");
      36       42999 :   params.addParam<MooseEnum>("order", orders, "The finite element order used for projections");
      37             : 
      38       42999 :   params.addCoupledVar("primary_variable", "The variable on the primary side of the domain");
      39             : 
      40       85998 :   return params;
      41       42999 : }
      42             : 
      43         101 : NodeFaceConstraint::NodeFaceConstraint(const InputParameters & parameters)
      44             :   : Constraint(parameters),
      45             :     // The secondary side is at nodes (hence passing 'true').  The neighbor side is the primary side
      46             :     // and it is not at nodes (so passing false)
      47             :     NeighborCoupleableMooseVariableDependencyIntermediateInterface(this, true, false),
      48             :     NeighborMooseVariableInterface<Real>(
      49             :         this, true, Moose::VarKindType::VAR_SOLVER, Moose::VarFieldType::VAR_FIELD_STANDARD),
      50         101 :     _secondary(_mesh.getBoundaryID(getParam<BoundaryName>("secondary"))),
      51         101 :     _primary(_mesh.getBoundaryID(getParam<BoundaryName>("primary"))),
      52         101 :     _var(_sys.getFieldVariable<Real>(_tid, parameters.get<NonlinearVariableName>("variable"))),
      53             : 
      54         101 :     _primary_q_point(_assembly.qPointsFace()),
      55         101 :     _primary_qrule(_assembly.qRuleFace()),
      56             : 
      57         101 :     _penetration_locator(
      58         101 :         getPenetrationLocator(getParam<BoundaryName>("primary"),
      59             :                               getParam<BoundaryName>("secondary"),
      60         202 :                               Utility::string_to_enum<Order>(getParam<MooseEnum>("order")))),
      61             : 
      62         101 :     _current_node(_var.node()),
      63         101 :     _current_primary(_var.neighbor()),
      64         101 :     _u_secondary(_var.dofValues()),
      65         101 :     _phi_secondary(1),  // One entry
      66         101 :     _test_secondary(1), // One entry
      67             : 
      68         101 :     _primary_var(*getVar("primary_variable", 0)),
      69         101 :     _primary_var_num(_primary_var.number()),
      70             : 
      71         101 :     _phi_primary(_assembly.phiFaceNeighbor(_primary_var)),
      72         101 :     _grad_phi_primary(_assembly.gradPhiFaceNeighbor(_primary_var)),
      73             : 
      74         101 :     _test_primary(_var.phiFaceNeighbor()),
      75         101 :     _grad_test_primary(_var.gradPhiFaceNeighbor()),
      76             : 
      77         101 :     _u_primary(_primary_var.slnNeighbor()),
      78         101 :     _grad_u_primary(_primary_var.gradSlnNeighbor()),
      79             : 
      80         101 :     _dof_map(_sys.dofMap()),
      81         101 :     _node_to_elem_map(_mesh.nodeToElemMap()),
      82             : 
      83         101 :     _overwrite_secondary_residual(true),
      84         303 :     _primary_JxW(_assembly.JxWNeighbor())
      85             : {
      86         101 :   addMooseVariableDependency(&_var);
      87             : 
      88         101 :   if (parameters.isParamValid("tangential_tolerance"))
      89             :   {
      90           0 :     _penetration_locator.setTangentialTolerance(getParam<Real>("tangential_tolerance"));
      91             :   }
      92         101 :   if (parameters.isParamValid("normal_smoothing_distance"))
      93             :   {
      94           0 :     _penetration_locator.setNormalSmoothingDistance(getParam<Real>("normal_smoothing_distance"));
      95             :   }
      96         101 :   if (parameters.isParamValid("normal_smoothing_method"))
      97             :   {
      98           0 :     _penetration_locator.setNormalSmoothingMethod(
      99           0 :         parameters.get<std::string>("normal_smoothing_method"));
     100             :   }
     101             :   // Put a "1" into test_secondary
     102             :   // will always only have one entry that is 1
     103         101 :   _test_secondary[0].push_back(1);
     104         101 : }
     105             : 
     106         202 : NodeFaceConstraint::~NodeFaceConstraint()
     107             : {
     108         101 :   _phi_secondary.release();
     109         101 :   _test_secondary.release();
     110         101 : }
     111             : 
     112             : void
     113        2128 : NodeFaceConstraint::computeSecondaryValue(NumericVector<Number> & current_solution)
     114             : {
     115        2128 :   const dof_id_type & dof_idx = _var.nodalDofIndex();
     116        2128 :   _qp = 0;
     117        2128 :   current_solution.set(dof_idx, computeQpSecondaryValue());
     118        2128 : }
     119             : 
     120             : void
     121       14004 : NodeFaceConstraint::residualSetup()
     122             : {
     123       14004 :   _secondary_residual_computed = false;
     124       14004 : }
     125             : 
     126             : Real
     127       44234 : NodeFaceConstraint::secondaryResidual() const
     128             : {
     129             :   mooseAssert(_secondary_residual_computed,
     130             :               "The secondary residual has not yet been computed, so the value will be garbage!");
     131       44234 :   return _secondary_residual;
     132             : }
     133             : 
     134             : void
     135       44234 : NodeFaceConstraint::computeResidual()
     136             : {
     137       44234 :   _qp = 0;
     138             : 
     139       44234 :   prepareVectorTagNeighbor(_assembly, _primary_var.number());
     140      221170 :   for (_i = 0; _i < _test_primary.size(); _i++)
     141      176936 :     _local_re(_i) += computeQpResidual(Moose::Primary);
     142       44234 :   accumulateTaggedLocalResidual();
     143             : 
     144       44234 :   prepareVectorTag(_assembly, _var.number());
     145       44234 :   _i = 0;
     146       44234 :   _secondary_residual = _local_re(0) = computeQpResidual(Moose::Secondary);
     147       44234 :   assignTaggedLocalResidual();
     148       44234 :   _secondary_residual_computed = true;
     149       44234 : }
     150             : 
     151             : void
     152        4184 : NodeFaceConstraint::computeJacobian()
     153             : {
     154        4184 :   getConnectedDofIndices(_var.number());
     155             : 
     156        4184 :   _Kee.resize(_test_secondary.size(), _connected_dof_indices.size());
     157        4184 :   _Kne.resize(_test_primary.size(), _connected_dof_indices.size());
     158             : 
     159        4184 :   _phi_secondary.resize(_connected_dof_indices.size());
     160             : 
     161        4184 :   _qp = 0;
     162             : 
     163             :   // Fill up _phi_secondary so that it is 1 when j corresponds to this dof and 0 for every other dof
     164             :   // This corresponds to evaluating all of the connected shape functions at _this_ node
     165       26716 :   for (unsigned int j = 0; j < _connected_dof_indices.size(); j++)
     166             :   {
     167       22532 :     _phi_secondary[j].resize(1);
     168             : 
     169       22532 :     if (_connected_dof_indices[j] == _var.nodalDofIndex())
     170        4184 :       _phi_secondary[j][_qp] = 1.0;
     171             :     else
     172       18348 :       _phi_secondary[j][_qp] = 0.0;
     173             :   }
     174             : 
     175        8368 :   for (_i = 0; _i < _test_secondary.size(); _i++)
     176             :     // Loop over the connected dof indices so we can get all the jacobian contributions
     177       26716 :     for (_j = 0; _j < _connected_dof_indices.size(); _j++)
     178       22532 :       _Kee(_i, _j) += computeQpJacobian(Moose::SecondarySecondary);
     179             : 
     180             :   // Just do a direct assignment here because the Jacobian coming from assembly has already been
     181             :   // properly sized according to the neighbor _var dof indices. It has also been zeroed
     182        4184 :   prepareMatrixTagNeighbor(_assembly, _var.number(), _var.number(), Moose::ElementNeighbor, _Ken);
     183        4184 :   if (_Ken.m() && _Ken.n())
     184        8224 :     for (_i = 0; _i < _test_secondary.size(); _i++)
     185       20560 :       for (_j = 0; _j < _phi_primary.size(); _j++)
     186       16448 :         _Ken(_i, _j) += computeQpJacobian(Moose::SecondaryPrimary);
     187             :   // Don't accumulate here
     188             : 
     189       20920 :   for (_i = 0; _i < _test_primary.size(); _i++)
     190             :     // Loop over the connected dof indices so we can get all the jacobian contributions
     191      106864 :     for (_j = 0; _j < _connected_dof_indices.size(); _j++)
     192       90128 :       _Kne(_i, _j) += computeQpJacobian(Moose::PrimarySecondary);
     193             : 
     194        4184 :   prepareMatrixTagNeighbor(
     195        4184 :       _assembly, _primary_var.number(), _var.number(), Moose::NeighborNeighbor);
     196        4184 :   if (_local_ke.m() && _local_ke.n())
     197       20560 :     for (_i = 0; _i < _test_primary.size(); _i++)
     198       82240 :       for (_j = 0; _j < _phi_primary.size(); _j++)
     199       65792 :         _local_ke(_i, _j) += computeQpJacobian(Moose::PrimaryPrimary);
     200        4184 :   accumulateTaggedLocalMatrix();
     201        4184 : }
     202             : 
     203             : void
     204          72 : NodeFaceConstraint::computeOffDiagJacobian(const unsigned int jvar_num)
     205             : {
     206          72 :   getConnectedDofIndices(jvar_num);
     207             : 
     208          72 :   _Kee.resize(_test_secondary.size(), _connected_dof_indices.size());
     209          72 :   _Kne.resize(_test_primary.size(), _connected_dof_indices.size());
     210             : 
     211          72 :   _phi_secondary.resize(_connected_dof_indices.size());
     212             : 
     213          72 :   _qp = 0;
     214             : 
     215          72 :   auto primary_jsize = getVariable(jvar_num).dofIndicesNeighbor().size();
     216             : 
     217             :   // Fill up _phi_secondary so that it is 1 when j corresponds to this dof and 0 for every other dof
     218             :   // This corresponds to evaluating all of the connected shape functions at _this_ node
     219          72 :   for (unsigned int j = 0; j < _connected_dof_indices.size(); j++)
     220             :   {
     221           0 :     _phi_secondary[j].resize(1);
     222             : 
     223           0 :     if (_connected_dof_indices[j] == _var.nodalDofIndex())
     224           0 :       _phi_secondary[j][_qp] = 1.0;
     225             :     else
     226           0 :       _phi_secondary[j][_qp] = 0.0;
     227             :   }
     228             : 
     229         144 :   for (_i = 0; _i < _test_secondary.size(); _i++)
     230             :     // Loop over the connected dof indices so we can get all the jacobian contributions
     231          72 :     for (_j = 0; _j < _connected_dof_indices.size(); _j++)
     232           0 :       _Kee(_i, _j) += computeQpOffDiagJacobian(Moose::SecondarySecondary, jvar_num);
     233             : 
     234             :   // Just do a direct assignment here because the Jacobian coming from assembly has already been
     235             :   // properly sized according to the jvar neighbor dof indices. It has also been zeroed
     236          72 :   prepareMatrixTagNeighbor(_assembly, _var.number(), jvar_num, Moose::ElementNeighbor, _Ken);
     237         144 :   for (_i = 0; _i < _test_secondary.size(); _i++)
     238         360 :     for (_j = 0; _j < primary_jsize; _j++)
     239         288 :       _Ken(_i, _j) += computeQpOffDiagJacobian(Moose::SecondaryPrimary, jvar_num);
     240             :   // Don't accumulate here
     241             : 
     242          72 :   if (_Kne.m() && _Kne.n())
     243           0 :     for (_i = 0; _i < _test_primary.size(); _i++)
     244             :       // Loop over the connected dof indices so we can get all the jacobian contributions
     245           0 :       for (_j = 0; _j < _connected_dof_indices.size(); _j++)
     246           0 :         _Kne(_i, _j) += computeQpOffDiagJacobian(Moose::PrimarySecondary, jvar_num);
     247             : 
     248          72 :   prepareMatrixTagNeighbor(_assembly, _primary_var.number(), jvar_num, Moose::NeighborNeighbor);
     249         360 :   for (_i = 0; _i < _test_primary.size(); _i++)
     250        1440 :     for (_j = 0; _j < primary_jsize; _j++)
     251        1152 :       _local_ke(_i, _j) += computeQpOffDiagJacobian(Moose::PrimaryPrimary, jvar_num);
     252          72 :   accumulateTaggedLocalMatrix();
     253          72 : }
     254             : 
     255             : void
     256        4256 : NodeFaceConstraint::getConnectedDofIndices(unsigned int var_num)
     257             : {
     258        4256 :   MooseVariableFEBase & var = _sys.getVariable(0, var_num);
     259             : 
     260        4256 :   _connected_dof_indices.clear();
     261        4256 :   std::set<dof_id_type> unique_dof_indices;
     262             : 
     263        4256 :   auto node_to_elem_pair = _node_to_elem_map.find(_current_node->id());
     264             :   mooseAssert(node_to_elem_pair != _node_to_elem_map.end(), "Missing entry in node to elem map");
     265        4256 :   const std::vector<dof_id_type> & elems = node_to_elem_pair->second;
     266             : 
     267             :   // Get the dof indices from each elem connected to the node
     268       11446 :   for (const auto & cur_elem : elems)
     269             :   {
     270        7190 :     std::vector<dof_id_type> dof_indices;
     271             : 
     272        7190 :     var.getDofIndices(_mesh.elemPtr(cur_elem), dof_indices);
     273             : 
     274       35518 :     for (const auto & dof : dof_indices)
     275       28328 :       unique_dof_indices.insert(dof);
     276        7190 :   }
     277             : 
     278       26788 :   for (const auto & dof : unique_dof_indices)
     279       22532 :     _connected_dof_indices.push_back(dof);
     280        4256 : }
     281             : 
     282             : bool
     283       52602 : NodeFaceConstraint::overwriteSecondaryResidual()
     284             : {
     285       52602 :   return _overwrite_secondary_residual;
     286             : }
     287             : 
     288             : const std::set<BoundaryID> &
     289           0 : NodeFaceConstraint::buildBoundaryIDs()
     290             : {
     291           0 :   _boundary_ids.insert(_primary);
     292           0 :   _boundary_ids.insert(_secondary);
     293           0 :   return _boundary_ids;
     294             : }
     295             : 
     296             : std::set<SubdomainID>
     297          13 : NodeFaceConstraint::getSecondaryConnectedBlocks() const
     298             : {
     299          13 :   return _mesh.getBoundaryConnectedBlocks(_secondary);
     300             : }

Generated by: LCOV version 1.14