https://mooseframework.inl.gov
Loading...
Searching...
No Matches
NodalConstraint.C
Go to the documentation of this file.
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 "NodalConstraint.h"
11
12// MOOSE includes
13#include "Assembly.h"
14#include "MooseMesh.h"
15#include "MooseVariableFE.h"
16#include "SubProblem.h"
17#include "SystemBase.h"
18
19#include "libmesh/compare_elems_by_level.h"
20#include "libmesh/distributed_mesh.h"
21#include "libmesh/null_output_iterator.h"
22#include "libmesh/parallel_elem.h"
23#include "libmesh/parallel_node.h"
24#include "libmesh/sparse_matrix.h"
25
26#include <algorithm>
27
30{
32 MooseEnum formulationtype("penalty kinematic", "penalty");
33 params.addParam<MooseEnum>("formulation",
34 formulationtype,
35 "Formulation used to calculate constraint - penalty or kinematic.");
36 params.addParam<NonlinearVariableName>("variable_secondary",
37 "The name of the variable for the secondary nodes, if it "
38 "is different from the primary nodes' variable");
39 return params;
40}
41
43 : Constraint(parameters),
46 this, true, Moose::VarKindType::VAR_SOLVER, Moose::VarFieldType::VAR_FIELD_STANDARD),
47 _var(_sys.getFieldVariable<Real>(_tid, parameters.get<NonlinearVariableName>("variable"))),
48 _var_secondary(_sys.getFieldVariable<Real>(
49 _tid,
50 isParamValid("variable_secondary")
51 ? parameters.get<NonlinearVariableName>("variable_secondary")
52 : parameters.get<NonlinearVariableName>("variable"))),
53 _u_secondary(_var_secondary.dofValuesNeighbor()),
54 _u_primary(_var.dofValues())
55{
58
59 MooseEnum temp_formulation = getParam<MooseEnum>("formulation");
60 if (temp_formulation == "penalty")
62 else if (temp_formulation == "kinematic")
64 else
65 mooseError("Formulation must be either Penalty or Kinematic");
66}
67
68std::vector<dof_id_type>
70 const std::vector<dof_id_type> & node_ids)
71{
72 const auto & node_to_elem_map = mesh.nodeToElemMap();
73 auto * const distributed_mesh = dynamic_cast<libMesh::DistributedMesh *>(&mesh.getMesh());
74
75 // local reference to the retained elements for this mesh, so we don't have to look it up in the
76 // map every time
77 auto & retained_elems = _retained_elems[&mesh];
78
79 // Elements connected to these nodes may already be remote on a distributed mesh, so gather
80 // gather one locally available connected element for each node before rebuilding the connectivity
81 // map.
82 if (distributed_mesh)
83 {
84 // Mesh adaptation may delete elements retained by a previous invocation. Remove their raw
85 // pointers from DistributedMesh before replacing them with the current connected elements.
86 distributed_mesh->clear_extra_ghost_elems(retained_elems);
87 retained_elems.clear();
88
89 std::set<Elem *, libMesh::CompareElemIdsByLevel> elems_to_ghost;
90 std::set<Node *> nodes_to_ghost;
91
92 // Loop over each node, and find one element connected to it.
93 for (const auto node_id : node_ids)
94 {
95 const auto node_to_elem_pair = node_to_elem_map.find(node_id);
96#ifndef NDEBUG
97 // Debugging check should be per node (inside the node loop)
98 bool someone_found_elem = false;
99#endif
100
101 if (node_to_elem_pair != node_to_elem_map.end())
102 for (const auto elem_id : node_to_elem_pair->second)
103 if (auto * const elem = mesh.queryElemPtr(elem_id))
104 {
105 elems_to_ghost.insert(elem);
106 for (const auto n : make_range(elem->n_nodes()))
107 nodes_to_ghost.insert(elem->node_ptr(n));
108#ifndef NDEBUG
109 someone_found_elem = true;
110#endif
111 break; // Only need one element to retain the node
112 }
113#ifndef NDEBUG
114 // gather through all processors to make sure at least one processor found an element for this
115 // node
116 mesh.getMesh().comm().max(someone_found_elem);
117 mooseAssert(someone_found_elem || node_ids.empty(), "Missing entry in node to elem map");
118#endif
119 }
120
121 // Send nodes first since elements need them.
122 mesh.getMesh().comm().allgather_packed_range(&mesh.getMesh(),
123 nodes_to_ghost.begin(),
124 nodes_to_ghost.end(),
126 mesh.getMesh().comm().allgather_packed_range(&mesh.getMesh(),
127 elems_to_ghost.begin(),
128 elems_to_ghost.end(),
130
131 // Rebuild the node-to-element map after gathering the remote mesh entities.
132 mesh.update();
133 }
134
135 // After rebuilding connectivity, select one canonical element ID per node.
136 std::vector<dof_id_type> elem_ids;
137 for (const auto node_id : node_ids)
138 {
139 // Reacquire the iterator after mesh.update().
140 const auto node_to_elem_pair = node_to_elem_map.find(node_id);
141 if (node_to_elem_pair == node_to_elem_map.end() || node_to_elem_pair->second.empty())
142 mooseError("Couldn't find any elements connected to primary node");
143
144 const auto elem_id =
145 node_to_elem_pair->second.front(); // Just need one element to retain the node, like above,
146 // just need one element to be ghosted
147 elem_ids.push_back(elem_id);
148
149 // Keep gathered elements when libMesh later deletes unneeded remote elements.
150 if (distributed_mesh)
151 {
152 auto * const elem = mesh.elemPtr(elem_id);
153 distributed_mesh->add_extra_ghost_elem(elem);
154 retained_elems.insert(elem);
155 }
156 }
157
158 // We only need one element per node.
159 mooseAssert(node_ids.size() == elem_ids.size(),
160 "Mismatch between number of primary nodes and connected elements");
161
162 return elem_ids;
163}
164
165void
167{
168 // _subproblem is the displaced problem when this constraint uses the displaced mesh, which is
169 // where its variables (and therefore the dof indices the assembly loops iterate over) live.
172}
173
174void
175NodalConstraint::computeResidual(const NumericVector<Number> & residual)
176{
177 if ((_weights.size() == 0) && (_primary_node_vector.size() == 1))
178 _weights.push_back(1.0);
179
180 std::vector<dof_id_type> primarydof = _var.dofIndices();
181 std::vector<dof_id_type> secondarydof = _var_secondary.dofIndicesNeighbor();
182
183 DenseVector<Number> re(primarydof.size());
184 DenseVector<Number> neighbor_re(secondarydof.size());
185
186 re.zero();
187 neighbor_re.zero();
188
189 for (_i = 0; _i < secondarydof.size(); ++_i)
190 {
191 for (_j = 0; _j < primarydof.size(); ++_j)
192 {
193 switch (_formulation)
194 {
195 case Moose::Penalty:
198 break;
199 case Moose::Kinematic:
200 // Transfer the current residual of the secondary node to the primary nodes
201 Real res = residual(secondarydof[_i]);
202 re(_j) += res * _weights[_j];
203 neighbor_re(_i) +=
205 break;
206 }
207 }
208 }
209 // We've already applied scaling
210 if (!primarydof.empty())
211 addResiduals(_assembly, re, primarydof, /*scaling_factor=*/1);
212 if (!secondarydof.empty())
213 addResiduals(_assembly, neighbor_re, secondarydof, /*scaling_factor=*/1);
214}
215
216void
217NodalConstraint::computeJacobian(const SparseMatrix<Number> & jacobian)
218{
219 if ((_weights.size() == 0) && (_primary_node_vector.size() == 1))
220 _weights.push_back(1.0);
221
222 // Calculate the dense-block Jacobian entries
223 std::vector<dof_id_type> secondarydof = _var_secondary.dofIndicesNeighbor();
224 std::vector<dof_id_type> primarydof = _var.dofIndices();
225
226 DenseMatrix<Number> Kee(primarydof.size(), primarydof.size());
227 DenseMatrix<Number> Ken(primarydof.size(), secondarydof.size());
228 DenseMatrix<Number> Kne(secondarydof.size(), primarydof.size());
229
230 Kee.zero();
231 Ken.zero();
232 Kne.zero();
233
234 for (_i = 0; _i < secondarydof.size(); ++_i)
235 {
236 for (_j = 0; _j < primarydof.size(); ++_j)
237 {
238 switch (_formulation)
239 {
240 case Moose::Penalty:
244 break;
245 case Moose::Kinematic:
246 Kee(_j, _j) = 0.;
247 Ken(_j, _i) += jacobian(secondarydof[_i], primarydof[_j]) * _weights[_j];
248 Kne(_i, _j) += -jacobian(secondarydof[_i], primarydof[_j]) / primarydof.size() +
250 break;
251 }
252 }
253 }
254 addJacobian(_assembly, Kee, primarydof, primarydof, _var.scalingFactor());
255 addJacobian(_assembly, Ken, primarydof, secondarydof, _var.scalingFactor());
256 addJacobian(_assembly, Kne, secondarydof, primarydof, _var_secondary.scalingFactor());
257
258 // Calculate and cache the diagonal secondary-secondary entries
259 for (_i = 0; _i < secondarydof.size(); ++_i)
260 {
261 Number value = 0.0;
262 switch (_formulation)
263 {
264 case Moose::Penalty:
266 break;
267 case Moose::Kinematic:
268 value = -jacobian(secondarydof[_i], secondarydof[_i]) / primarydof.size() +
270 break;
271 }
273 _assembly, value, secondarydof[_i], secondarydof[_i], _var_secondary.scalingFactor());
274 }
275}
276
277void
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application.
Definition MooseError.h:311
Base class for all Constraint types.
Definition Constraint.h:20
static InputParameters validParams()
Definition Constraint.C:15
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system.
void addParam(const std::string &name, const S &value, const std::string &doc_string)
These methods add an optional parameter and a documentation string to the InputParameters object.
This is a "smart" enum class intended to replace many of the shortcomings in the C++ enum type It sho...
Definition MooseEnum.h:55
MooseMesh wraps a libMesh::Mesh object and enhances its capabilities by caching additional data and s...
Definition MooseMesh.h:95
void scalingFactor(const std::vector< Real > &factor)
Set the scaling factor for this variable.
void addMooseVariableDependency(MooseVariableFieldBase *var)
Call this function to add the passed in MooseVariableFieldBase as a variable that this object depends...
const std::vector< dof_id_type > & dofIndicesNeighbor() const final
Get neighbor DOF indices for currently selected element.
const std::vector< dof_id_type > & dofIndices() const final
Get local DoF indices.
virtual const OutputTools< T >::VariableValue & value()
The value of the variable this object is operating on.
Intermediate base class that ties together all the interfaces for getting MooseVariables with the Moo...
Enhances MooseVariableInterface interface provide values from neighbor elements.
virtual void computeResidual() override final
Computes the nodal residual.
Moose::ConstraintFormulationType _formulation
Specifies formulation type used to apply constraints.
unsigned int _i
Counter for primary and secondary nodes.
virtual Real computeQpResidual(Moose::ConstraintType type)=0
This is the virtual that derived classes should override for computing the residual on neighboring el...
std::vector< dof_id_type > _primary_node_vector
node IDs of the primary node
NodalConstraint(const InputParameters &parameters)
std::vector< dof_id_type > _connected_nodes
node IDs connected to the primary node (secondary nodes)
std::vector< Real > _weights
When the secondary node is constrained to move as a linear combination of the primary nodes,...
std::vector< dof_id_type > gatherAndRetainConnectedElems(MooseMesh &mesh, const std::vector< dof_id_type > &node_ids)
Gather and retain elements connected to the provided nodes on the provided mesh.
std::map< MooseMesh *, std::set< Elem * > > _retained_elems
Elements this constraint retained on each distributed mesh during the previous mesh update.
static InputParameters validParams()
virtual void updateConnectivity()
Built the connectivity for this constraint.
virtual Real computeQpJacobian(Moose::ConstraintJacobianType type)=0
This is the virtual that derived classes should override for computing the Jacobian on neighboring el...
MooseVariable & _var
virtual void computeJacobian() override final
Computes the jacobian for the current element.
MooseVariable & _var_secondary
void reinitConstraintNodes()
Reinitialize the primary and secondary nodes on the SubProblem that owns this constraint's variables.
THREAD_ID _tid
The thread ID for this kernel.
Assembly & _assembly
Reference to this Kernel's assembly object.
SubProblem & _subproblem
Reference to this kernel's SubProblem.
void reinitNodesNeighbor(const std::vector< dof_id_type > &nodes, const THREAD_ID tid)
Definition SubProblem.C:994
void reinitNodes(const std::vector< dof_id_type > &nodes, const THREAD_ID tid)
Definition SubProblem.C:986
void addJacobianElement(Assembly &assembly, Real value, dof_id_type row_index, dof_id_type column_index, Real scaling_factor)
Add into a single Jacobian element.
void addJacobian(Assembly &assembly, const Residuals &residuals, const Indices &dof_indices, Real scaling_factor)
Add the provided residual derivatives into the Jacobian for the provided dof indices.
void addResiduals(Assembly &assembly, const Residuals &residuals, const Indices &dof_indices, Real scaling_factor)
Add the provided incoming residuals corresponding to the provided dof indices.
virtual void clear_extra_ghost_elems()
MeshBase & mesh
MOOSE now contains C++17 code, so give a reasonable error message stating what the user can do to add...
@ Penalty
Definition MooseTypes.h:973
@ Kinematic
Definition MooseTypes.h:974
@ Primary
Definition MooseTypes.h:814
@ Secondary
Definition MooseTypes.h:813
@ SecondarySecondary
Definition MooseTypes.h:852
@ SecondaryPrimary
Definition MooseTypes.h:853
@ PrimarySecondary
Definition MooseTypes.h:854
@ PrimaryPrimary
Definition MooseTypes.h:855