LCOV - code coverage report
Current view: top level - src/constraints - NodeFaceConstraint.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 140 156 89.7 %
Date: 2025-07-17 01:28:37 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       42983 : NodeFaceConstraint::validParams()
      24             : {
      25       42983 :   MooseEnum orders("FIRST SECOND THIRD FOURTH", "FIRST");
      26       42983 :   InputParameters params = Constraint::validParams();
      27       42983 :   params.addParam<BoundaryName>("secondary", "The boundary ID associated with the secondary side");
      28       42983 :   params.addParam<BoundaryName>("primary", "The boundary ID associated with the primary side");
      29       42983 :   params.addParam<Real>("tangential_tolerance",
      30             :                         "Tangential distance to extend edges of contact surfaces");
      31       42983 :   params.addParam<Real>(
      32             :       "normal_smoothing_distance",
      33             :       "Distance from edge in parametric coordinates over which to smooth contact normal");
      34       42983 :   params.addParam<std::string>("normal_smoothing_method",
      35             :                                "Method to use to smooth normals (edge_based|nodal_normal_based)");
      36       42983 :   params.addParam<MooseEnum>("order", orders, "The finite element order used for projections");
      37             : 
      38       42983 :   params.addCoupledVar("primary_variable", "The variable on the primary side of the domain");
      39             : 
      40       85966 :   return params;
      41       42983 : }
      42             : 
      43          93 : 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          93 :     _secondary(_mesh.getBoundaryID(getParam<BoundaryName>("secondary"))),
      51          93 :     _primary(_mesh.getBoundaryID(getParam<BoundaryName>("primary"))),
      52          93 :     _var(_sys.getFieldVariable<Real>(_tid, parameters.get<NonlinearVariableName>("variable"))),
      53             : 
      54          93 :     _primary_q_point(_assembly.qPointsFace()),
      55          93 :     _primary_qrule(_assembly.qRuleFace()),
      56             : 
      57          93 :     _penetration_locator(
      58          93 :         getPenetrationLocator(getParam<BoundaryName>("primary"),
      59             :                               getParam<BoundaryName>("secondary"),
      60         186 :                               Utility::string_to_enum<Order>(getParam<MooseEnum>("order")))),
      61             : 
      62          93 :     _current_node(_var.node()),
      63          93 :     _current_primary(_var.neighbor()),
      64          93 :     _u_secondary(_var.dofValues()),
      65          93 :     _phi_secondary(1),  // One entry
      66          93 :     _test_secondary(1), // One entry
      67             : 
      68          93 :     _primary_var(*getVar("primary_variable", 0)),
      69          93 :     _primary_var_num(_primary_var.number()),
      70             : 
      71          93 :     _phi_primary(_assembly.phiFaceNeighbor(_primary_var)),
      72          93 :     _grad_phi_primary(_assembly.gradPhiFaceNeighbor(_primary_var)),
      73             : 
      74          93 :     _test_primary(_var.phiFaceNeighbor()),
      75          93 :     _grad_test_primary(_var.gradPhiFaceNeighbor()),
      76             : 
      77          93 :     _u_primary(_primary_var.slnNeighbor()),
      78          93 :     _grad_u_primary(_primary_var.gradSlnNeighbor()),
      79             : 
      80          93 :     _dof_map(_sys.dofMap()),
      81          93 :     _node_to_elem_map(_mesh.nodeToElemMap()),
      82             : 
      83          93 :     _overwrite_secondary_residual(true),
      84         279 :     _primary_JxW(_assembly.JxWNeighbor())
      85             : {
      86          93 :   addMooseVariableDependency(&_var);
      87             : 
      88          93 :   if (parameters.isParamValid("tangential_tolerance"))
      89             :   {
      90           0 :     _penetration_locator.setTangentialTolerance(getParam<Real>("tangential_tolerance"));
      91             :   }
      92          93 :   if (parameters.isParamValid("normal_smoothing_distance"))
      93             :   {
      94           0 :     _penetration_locator.setNormalSmoothingDistance(getParam<Real>("normal_smoothing_distance"));
      95             :   }
      96          93 :   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          93 :   _test_secondary[0].push_back(1);
     104          93 : }
     105             : 
     106         186 : NodeFaceConstraint::~NodeFaceConstraint()
     107             : {
     108          93 :   _phi_secondary.release();
     109          93 :   _test_secondary.release();
     110          93 : }
     111             : 
     112             : void
     113        1880 : NodeFaceConstraint::computeSecondaryValue(NumericVector<Number> & current_solution)
     114             : {
     115        1880 :   const dof_id_type & dof_idx = _var.nodalDofIndex();
     116        1880 :   _qp = 0;
     117        1880 :   current_solution.set(dof_idx, computeQpSecondaryValue());
     118        1880 : }
     119             : 
     120             : void
     121       12822 : NodeFaceConstraint::residualSetup()
     122             : {
     123       12822 :   _secondary_residual_computed = false;
     124       12822 : }
     125             : 
     126             : Real
     127       39276 : 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       39276 :   return _secondary_residual;
     132             : }
     133             : 
     134             : void
     135       39276 : NodeFaceConstraint::computeResidual()
     136             : {
     137       39276 :   _qp = 0;
     138             : 
     139       39276 :   prepareVectorTagNeighbor(_assembly, _primary_var.number());
     140      196380 :   for (_i = 0; _i < _test_primary.size(); _i++)
     141      157104 :     _local_re(_i) += computeQpResidual(Moose::Primary);
     142       39276 :   accumulateTaggedLocalResidual();
     143             : 
     144       39276 :   prepareVectorTag(_assembly, _var.number());
     145       39276 :   _i = 0;
     146       39276 :   _secondary_residual = _local_re(0) = computeQpResidual(Moose::Secondary);
     147       39276 :   assignTaggedLocalResidual();
     148       39276 :   _secondary_residual_computed = true;
     149       39276 : }
     150             : 
     151             : void
     152        3696 : NodeFaceConstraint::computeJacobian()
     153             : {
     154        3696 :   getConnectedDofIndices(_var.number());
     155             : 
     156        3696 :   _Kee.resize(_test_secondary.size(), _connected_dof_indices.size());
     157        3696 :   _Kne.resize(_test_primary.size(), _connected_dof_indices.size());
     158             : 
     159        3696 :   _phi_secondary.resize(_connected_dof_indices.size());
     160             : 
     161        3696 :   _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       23600 :   for (unsigned int j = 0; j < _connected_dof_indices.size(); j++)
     166             :   {
     167       19904 :     _phi_secondary[j].resize(1);
     168             : 
     169       19904 :     if (_connected_dof_indices[j] == _var.nodalDofIndex())
     170        3696 :       _phi_secondary[j][_qp] = 1.0;
     171             :     else
     172       16208 :       _phi_secondary[j][_qp] = 0.0;
     173             :   }
     174             : 
     175        7392 :   for (_i = 0; _i < _test_secondary.size(); _i++)
     176             :     // Loop over the connected dof indices so we can get all the jacobian contributions
     177       23600 :     for (_j = 0; _j < _connected_dof_indices.size(); _j++)
     178       19904 :       _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        3696 :   prepareMatrixTagNeighbor(_assembly, _var.number(), _var.number(), Moose::ElementNeighbor, _Ken);
     183        3696 :   if (_Ken.m() && _Ken.n())
     184        7264 :     for (_i = 0; _i < _test_secondary.size(); _i++)
     185       18160 :       for (_j = 0; _j < _phi_primary.size(); _j++)
     186       14528 :         _Ken(_i, _j) += computeQpJacobian(Moose::SecondaryPrimary);
     187             :   // Don't accumulate here
     188             : 
     189       18480 :   for (_i = 0; _i < _test_primary.size(); _i++)
     190             :     // Loop over the connected dof indices so we can get all the jacobian contributions
     191       94400 :     for (_j = 0; _j < _connected_dof_indices.size(); _j++)
     192       79616 :       _Kne(_i, _j) += computeQpJacobian(Moose::PrimarySecondary);
     193             : 
     194        3696 :   prepareMatrixTagNeighbor(
     195        3696 :       _assembly, _primary_var.number(), _var.number(), Moose::NeighborNeighbor);
     196        3696 :   if (_local_ke.m() && _local_ke.n())
     197       18160 :     for (_i = 0; _i < _test_primary.size(); _i++)
     198       72640 :       for (_j = 0; _j < _phi_primary.size(); _j++)
     199       58112 :         _local_ke(_i, _j) += computeQpJacobian(Moose::PrimaryPrimary);
     200        3696 :   accumulateTaggedLocalMatrix();
     201        3696 : }
     202             : 
     203             : void
     204          64 : NodeFaceConstraint::computeOffDiagJacobian(const unsigned int jvar_num)
     205             : {
     206          64 :   getConnectedDofIndices(jvar_num);
     207             : 
     208          64 :   _Kee.resize(_test_secondary.size(), _connected_dof_indices.size());
     209          64 :   _Kne.resize(_test_primary.size(), _connected_dof_indices.size());
     210             : 
     211          64 :   _phi_secondary.resize(_connected_dof_indices.size());
     212             : 
     213          64 :   _qp = 0;
     214             : 
     215          64 :   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          64 :   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         128 :   for (_i = 0; _i < _test_secondary.size(); _i++)
     230             :     // Loop over the connected dof indices so we can get all the jacobian contributions
     231          64 :     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          64 :   prepareMatrixTagNeighbor(_assembly, _var.number(), jvar_num, Moose::ElementNeighbor, _Ken);
     237         128 :   for (_i = 0; _i < _test_secondary.size(); _i++)
     238         320 :     for (_j = 0; _j < primary_jsize; _j++)
     239         256 :       _Ken(_i, _j) += computeQpOffDiagJacobian(Moose::SecondaryPrimary, jvar_num);
     240             :   // Don't accumulate here
     241             : 
     242          64 :   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          64 :   prepareMatrixTagNeighbor(_assembly, _primary_var.number(), jvar_num, Moose::NeighborNeighbor);
     249         320 :   for (_i = 0; _i < _test_primary.size(); _i++)
     250        1280 :     for (_j = 0; _j < primary_jsize; _j++)
     251        1024 :       _local_ke(_i, _j) += computeQpOffDiagJacobian(Moose::PrimaryPrimary, jvar_num);
     252          64 :   accumulateTaggedLocalMatrix();
     253          64 : }
     254             : 
     255             : void
     256        3760 : NodeFaceConstraint::getConnectedDofIndices(unsigned int var_num)
     257             : {
     258        3760 :   MooseVariableFEBase & var = _sys.getVariable(0, var_num);
     259             : 
     260        3760 :   _connected_dof_indices.clear();
     261        3760 :   std::set<dof_id_type> unique_dof_indices;
     262             : 
     263        3760 :   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        3760 :   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       10112 :   for (const auto & cur_elem : elems)
     269             :   {
     270        6352 :     std::vector<dof_id_type> dof_indices;
     271             : 
     272        6352 :     var.getDofIndices(_mesh.elemPtr(cur_elem), dof_indices);
     273             : 
     274       31376 :     for (const auto & dof : dof_indices)
     275       25024 :       unique_dof_indices.insert(dof);
     276        6352 :   }
     277             : 
     278       23664 :   for (const auto & dof : unique_dof_indices)
     279       19904 :     _connected_dof_indices.push_back(dof);
     280        3760 : }
     281             : 
     282             : bool
     283       46668 : NodeFaceConstraint::overwriteSecondaryResidual()
     284             : {
     285       46668 :   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          12 : NodeFaceConstraint::getSecondaryConnectedBlocks() const
     298             : {
     299          12 :   return _mesh.getBoundaryConnectedBlocks(_secondary);
     300             : }

Generated by: LCOV version 1.14