LCOV - code coverage report
Current view: top level - src/constraints - EqualValueBoundaryConstraint.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 79 99 79.8 %
Date: 2025-07-17 01:28:37 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 "EqualValueBoundaryConstraint.h"
      12             : #include "MooseMesh.h"
      13             : 
      14             : #include "libmesh/null_output_iterator.h"
      15             : #include "libmesh/parallel.h"
      16             : #include "libmesh/parallel_elem.h"
      17             : #include "libmesh/parallel_node.h"
      18             : 
      19             : // C++ includes
      20             : #include <limits.h>
      21             : 
      22             : namespace // Anonymous namespace for helpers
      23             : {
      24             : /**
      25             :  * Specific weak ordering for Elem *'s to be used in a set.
      26             :  * We use the id, but first sort by level.  This guarantees
      27             :  * when traversing the set from beginning to end the lower
      28             :  * level (parent) elements are encountered first.
      29             :  *
      30             :  * This was swiped from libMesh mesh_communication.C, and ought to be
      31             :  * replaced with libMesh::CompareElemIdsByLevel just as soon as I refactor to
      32             :  * create that - @roystgnr
      33             :  */
      34             : struct CompareElemsByLevel
      35             : {
      36           6 :   bool operator()(const Elem * a, const Elem * b) const
      37             :   {
      38             :     libmesh_assert(a);
      39             :     libmesh_assert(b);
      40           6 :     const unsigned int al = a->level(), bl = b->level();
      41           6 :     const dof_id_type aid = a->id(), bid = b->id();
      42             : 
      43           6 :     return (al == bl) ? aid < bid : al < bl;
      44             :   }
      45             : };
      46             : 
      47             : } // anonymous namespace
      48             : 
      49             : registerMooseObject("MooseApp", EqualValueBoundaryConstraint);
      50             : 
      51             : InputParameters
      52       14309 : EqualValueBoundaryConstraint::validParams()
      53             : {
      54       14309 :   InputParameters params = NodalConstraint::validParams();
      55       14309 :   params.addClassDescription(
      56             :       "Constraint for enforcing that variables on each side of a boundary are equivalent.");
      57       42927 :   params.addParam<unsigned int>(
      58             :       "primary",
      59       28618 :       std::numeric_limits<unsigned int>::max(),
      60             :       "The ID of the primary node. If no ID is provided, first node of secondary set is chosen.");
      61       14309 :   params.addParam<std::vector<unsigned int>>(
      62             :       "secondary_node_ids", {}, "The IDs of the secondary node");
      63       14309 :   params.addParam<BoundaryName>(
      64             :       "secondary", "NaN", "The boundary ID associated with the secondary side");
      65       14309 :   params.addRequiredParam<Real>("penalty", "The penalty used for the boundary term");
      66       14309 :   return params;
      67           0 : }
      68             : 
      69          22 : EqualValueBoundaryConstraint::EqualValueBoundaryConstraint(const InputParameters & parameters)
      70             :   : NodalConstraint(parameters),
      71          22 :     _primary_node_id(getParam<unsigned int>("primary")),
      72          22 :     _secondary_node_ids(getParam<std::vector<unsigned int>>("secondary_node_ids")),
      73          22 :     _secondary_node_set_id(getParam<BoundaryName>("secondary")),
      74          44 :     _penalty(getParam<Real>("penalty"))
      75             : {
      76          22 :   updateConstrainedNodes();
      77          22 : }
      78             : 
      79             : void
      80          18 : EqualValueBoundaryConstraint::meshChanged()
      81             : {
      82          18 :   updateConstrainedNodes();
      83          18 : }
      84             : 
      85             : void
      86          40 : EqualValueBoundaryConstraint::updateConstrainedNodes()
      87             : {
      88          40 :   _primary_node_vector.clear();
      89          40 :   _connected_nodes.clear();
      90             : 
      91          40 :   if ((_secondary_node_ids.size() == 0) && (_secondary_node_set_id == "NaN"))
      92           0 :     mooseError("Please specify secondary node ids or boundary id.");
      93          40 :   else if ((_secondary_node_ids.size() == 0) && (_secondary_node_set_id != "NaN"))
      94             :   {
      95             :     std::vector<dof_id_type> nodelist =
      96          40 :         _mesh.getNodeList(_mesh.getBoundaryID(_secondary_node_set_id));
      97          40 :     std::vector<dof_id_type>::iterator in;
      98             : 
      99             :     // Set primary node to first node of the secondary node set if no primary node id is provided
     100             :     //_primary_node_vector defines primary nodes in the base class
     101          40 :     if (_primary_node_id == std::numeric_limits<unsigned int>::max())
     102             :     {
     103           0 :       in = std::min_element(nodelist.begin(), nodelist.end());
     104           0 :       dof_id_type node_id = (in == nodelist.end()) ? DofObject::invalid_id : *in;
     105           0 :       _communicator.min(node_id);
     106           0 :       _primary_node_vector.push_back(node_id);
     107             :     }
     108             :     else
     109          40 :       _primary_node_vector.push_back(_primary_node_id);
     110             : 
     111             :     // Fill in _connected_nodes, which defines secondary nodes in the base class
     112         532 :     for (in = nodelist.begin(); in != nodelist.end(); ++in)
     113             :     {
     114         944 :       if ((*in != _primary_node_vector[0]) &&
     115         452 :           (_mesh.nodeRef(*in).processor_id() == _subproblem.processor_id()))
     116         354 :         _connected_nodes.push_back(*in);
     117             :     }
     118          40 :   }
     119           0 :   else if ((_secondary_node_ids.size() != 0) && (_secondary_node_set_id == "NaN"))
     120             :   {
     121           0 :     if (_primary_node_id == std::numeric_limits<unsigned int>::max())
     122           0 :       _primary_node_vector.push_back(
     123           0 :           _secondary_node_ids[0]); //_primary_node_vector defines primary nodes in the base class
     124             : 
     125             :     // Fill in _connected_nodes, which defines secondary nodes in the base class
     126           0 :     for (const auto & dof : _secondary_node_ids)
     127             :     {
     128           0 :       if (_mesh.queryNodePtr(dof) &&
     129           0 :           (_mesh.nodeRef(dof).processor_id() == _subproblem.processor_id()) &&
     130           0 :           (dof != _primary_node_vector[0]))
     131           0 :         _connected_nodes.push_back(dof);
     132             :     }
     133             :   }
     134             : 
     135          40 :   const auto & node_to_elem_map = _mesh.nodeToElemMap();
     136          40 :   auto node_to_elem_pair = node_to_elem_map.find(_primary_node_vector[0]);
     137             : 
     138          40 :   bool found_elems = (node_to_elem_pair != node_to_elem_map.end());
     139             : 
     140             :   // Add elements connected to primary node to Ghosted Elements.
     141             : 
     142             :   // On a distributed mesh, these elements might have already been
     143             :   // remoted, in which case we need to gather them back first.
     144          40 :   if (!_mesh.getMesh().is_serial())
     145             :   {
     146             : #ifndef NDEBUG
     147             :     bool someone_found_elems = found_elems;
     148             :     _mesh.getMesh().comm().max(someone_found_elems);
     149             :     mooseAssert(someone_found_elems, "Missing entry in node to elem map");
     150             : #endif
     151             : 
     152           2 :     std::set<Elem *, CompareElemsByLevel> primary_elems_to_ghost;
     153           2 :     std::set<Node *> nodes_to_ghost;
     154           2 :     if (found_elems)
     155             :     {
     156           6 :       for (dof_id_type id : node_to_elem_pair->second)
     157             :       {
     158           4 :         Elem * elem = _mesh.queryElemPtr(id);
     159           4 :         if (elem)
     160             :         {
     161           4 :           primary_elems_to_ghost.insert(elem);
     162             : 
     163           4 :           const unsigned int n_nodes = elem->n_nodes();
     164          20 :           for (unsigned int n = 0; n != n_nodes; ++n)
     165          16 :             nodes_to_ghost.insert(elem->node_ptr(n));
     166             :         }
     167             :       }
     168             :     }
     169             : 
     170             :     // Send nodes first since elements need them
     171           2 :     _mesh.getMesh().comm().allgather_packed_range(&_mesh.getMesh(),
     172             :                                                   nodes_to_ghost.begin(),
     173             :                                                   nodes_to_ghost.end(),
     174             :                                                   libMesh::null_output_iterator<Node>());
     175             : 
     176           2 :     _mesh.getMesh().comm().allgather_packed_range(&_mesh.getMesh(),
     177             :                                                   primary_elems_to_ghost.begin(),
     178             :                                                   primary_elems_to_ghost.end(),
     179             :                                                   libMesh::null_output_iterator<Elem>());
     180             : 
     181           2 :     _mesh.update(); // Rebuild node_to_elem_map
     182             : 
     183             :     // Find elems again now that we know they're there
     184           2 :     const auto & new_node_to_elem_map = _mesh.nodeToElemMap();
     185           2 :     node_to_elem_pair = new_node_to_elem_map.find(_primary_node_vector[0]);
     186           2 :     found_elems = (node_to_elem_pair != new_node_to_elem_map.end());
     187           2 :   }
     188             : 
     189          40 :   if (!found_elems)
     190           0 :     mooseError("Couldn't find any elements connected to primary node");
     191             : 
     192          40 :   const std::vector<dof_id_type> & elems = node_to_elem_pair->second;
     193             : 
     194          40 :   if (elems.size() == 0)
     195           0 :     mooseError("Couldn't find any elements connected to primary node");
     196          40 :   _subproblem.addGhostedElem(elems[0]);
     197          40 : }
     198             : 
     199             : Real
     200        3204 : EqualValueBoundaryConstraint::computeQpResidual(Moose::ConstraintType type)
     201             : {
     202        3204 :   switch (type)
     203             :   {
     204        1602 :     case Moose::Secondary:
     205        1602 :       return (_u_secondary[_i] - _u_primary[_j]) * _penalty;
     206        1602 :     case Moose::Primary:
     207        1602 :       return (_u_primary[_j] - _u_secondary[_i]) * _penalty;
     208             :   }
     209           0 :   return 0.;
     210             : }
     211             : 
     212             : Real
     213        1752 : EqualValueBoundaryConstraint::computeQpJacobian(Moose::ConstraintJacobianType type)
     214             : {
     215        1752 :   switch (type)
     216             :   {
     217         438 :     case Moose::SecondarySecondary:
     218         438 :       return _penalty;
     219         438 :     case Moose::SecondaryPrimary:
     220         438 :       return -_penalty;
     221         438 :     case Moose::PrimaryPrimary:
     222         438 :       return _penalty;
     223         438 :     case Moose::PrimarySecondary:
     224         438 :       return -_penalty;
     225           0 :     default:
     226           0 :       mooseError("Unsupported type");
     227             :       break;
     228             :   }
     229             :   return 0.;
     230             : }

Generated by: LCOV version 1.14