https://mooseframework.inl.gov
Loading...
Searching...
No Matches
EqualValueBoundaryConstraint.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// MOOSE includes
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
19namespace // Anonymous namespace for helpers
20{
31struct CompareElemsByLevel
32{
33 bool operator()(const Elem * a, const Elem * b) const
34 {
37 const unsigned int al = a->level(), bl = b->level();
38 const dof_id_type aid = a->id(), bid = b->id();
39
40 return (al == bl) ? aid < bid : al < bl;
41 }
42};
43
44} // anonymous namespace
45
47
50{
53 "Constraint for enforcing that variables on each side of a boundary are equivalent.");
54 params.addParam<unsigned int>(
55 "primary",
56 "The ID of the primary node. If no ID is provided, first node of secondary set is chosen.");
57 params.addParam<Point>("primary_node_coord", "Coordinates of the primary node to locate.");
58 params.addParam<std::vector<unsigned int>>("secondary_node_ids", "The IDs of the secondary node");
59 params.addParam<BoundaryName>("secondary", "The boundary ID associated with the secondary side");
60 params.addRequiredParam<Real>("penalty", "The penalty used for the boundary term");
61 return params;
62}
63
65 : NodalConstraint(parameters), _penalty(getParam<Real>("penalty"))
66{
68}
69
70void
75
76void
83
84void
86{
87 // user provided nothing
88 if (!isParamValid("secondary_node_ids") && !isParamValid("secondary"))
89 paramError("secondary", "Either secondary or secondary_node_ids must be provided.");
90
91 // user provided conflicting params
92 if (isParamValid("secondary_node_ids") && isParamValid("secondary"))
93 paramError("secondary",
94 "Both 'secondary' and 'secondary_node_ids' parameters are set. They are mutually "
95 "exclusive.");
96
97 // Fill in _connected_nodes, which defines local secondary nodes in the base class
98 //
99 // Note that later on when we pick the primary node from the secondary node set, we
100 // will make sure to remove it from _connected_nodes
101 _connected_nodes.clear();
102
103 // user provided boundary name
104 if (isParamValid("secondary"))
105 {
106 const auto & secondary_bnd = getParam<BoundaryName>("secondary");
107 const auto & secondary_nodes = _mesh.getNodeList(_mesh.getBoundaryID(secondary_bnd));
108
109 for (const auto & nid : secondary_nodes)
110 if (_mesh.nodeRef(nid).processor_id() == _subproblem.processor_id())
111 _connected_nodes.push_back(nid);
112 }
113 // user provided node ids
114 else if (isParamValid("secondary_node_ids"))
115 {
116 const auto & secondary_node_ids = getParam<std::vector<unsigned int>>("secondary_node_ids");
117 for (const auto & nid : secondary_node_ids)
118 if (_mesh.queryNodePtr(nid) &&
119 _mesh.nodeRef(nid).processor_id() == _subproblem.processor_id())
120 _connected_nodes.push_back(nid);
121 }
122}
123
124void
126{
127 // user provided nothing
128 if (isParamValid("primary") && isParamValid("primary_node_coord"))
130 "Both 'primary' and 'primary_node_coord' parameters are set. They are mutually exclusive.");
131
132 dof_id_type primary_node_id = Node::invalid_id;
133
134 // user provided primary node coordinates
135 if (isParamValid("primary_node_coord"))
136 primary_node_id = getPrimaryNodeIDByCoord();
137 // user provided primary node id
138 else if (isParamValid("primary"))
139 primary_node_id = getParam<unsigned int>("primary");
140 // no primary node provided, so we pick one from secondary nodes
141 else
142 {
143 // If the user provided secondary node ids, set primary node to first one
144 if (isParamValid("secondary_node_ids"))
145 {
146 if (_connected_nodes.size())
147 primary_node_id = _connected_nodes[0];
148 }
149 // otherwise, we pick the minimum node id from the secondary node set
150 else
151 {
152 if (_connected_nodes.size())
153 primary_node_id = (*std::min_element(_connected_nodes.begin(), _connected_nodes.end()));
154 _mesh.comm().min(primary_node_id);
155 }
156 }
157
158 mooseAssert(primary_node_id != Node::invalid_id, "We should have found a primary node");
159
160 // Remove primary node from secondary nodes
161 auto it = std::find(_connected_nodes.begin(), _connected_nodes.end(), primary_node_id);
162 if (it != _connected_nodes.end())
163 _connected_nodes.erase(it);
164
165 // Store primary node id
166 _primary_node_vector.resize(1);
167 _primary_node_vector[0] = primary_node_id;
168}
169
170dof_id_type
172{
173 const Real eps = libMesh::TOLERANCE;
174 std::unordered_set<dof_id_type> local_primary_node_ids;
175 const auto & primary_node_coord = getParam<Point>("primary_node_coord");
176
177 // Gather local candidates
178 for (const auto & bnd_node : *_mesh.getBoundaryNodeRange())
179 {
180 if ((*(bnd_node->_node) - primary_node_coord).norm() < eps)
181 {
182 // The primary node we found should belong to the secondary node set
183 //
184 // Note that we do not immediately break once a match is found because in theory, although it
185 // is extremely unlikely, there could be multiple coinciding nodes on the secondary boundary
186 // that match the provided coordinates. In that case, we would like to emit a meaning error
187 // message.
188 if (std::find(_connected_nodes.begin(), _connected_nodes.end(), bnd_node->_node->id()) !=
189 _connected_nodes.end())
190 local_primary_node_ids.insert(bnd_node->_node->id());
191 }
192 }
193
194 // Gather all candidates from all ranks
195 const std::vector<dof_id_type> local_node_vec(local_primary_node_ids.begin(),
196 local_primary_node_ids.end());
197 std::vector<std::vector<dof_id_type>> gathered_node_vecs;
198 _mesh.comm().allgather(local_node_vec, gathered_node_vecs);
199
200 // Deduplicate
201 std::unordered_set<dof_id_type> global_primary_node_ids;
202 for (const auto & vec : gathered_node_vecs)
203 global_primary_node_ids.insert(vec.begin(), vec.end());
204
205 // Make sure we found one and exactly one
206 if (global_primary_node_ids.size() == 0)
207 mooseError("Couldn't find a node ID for the specified primary_node_coord.");
208 else if (global_primary_node_ids.size() > 1)
209 mooseError("Multiple nodes found for the specified primary_node_coord.");
210
211 return *global_primary_node_ids.begin();
212}
213
214void
216{
217 const auto & node_to_elem_map = _mesh.nodeToElemMap();
218 auto node_to_elem_pair = node_to_elem_map.find(_primary_node_vector[0]);
219 bool found_elems = (node_to_elem_pair != node_to_elem_map.end());
220
221 // Add elements connected to primary node to Ghosted Elements.
222
223 // On a distributed mesh, these elements might have already been
224 // remoted, in which case we need to gather them back first.
225 if (!_mesh.getMesh().is_serial())
226 {
227#ifndef NDEBUG
228 bool someone_found_elems = found_elems;
229 _mesh.getMesh().comm().max(someone_found_elems);
230 mooseAssert(someone_found_elems, "Missing entry in node to elem map");
231#endif
232
233 std::set<Elem *, CompareElemsByLevel> primary_elems_to_ghost;
234 std::set<Node *> nodes_to_ghost;
235 if (found_elems)
236 {
237 for (dof_id_type id : node_to_elem_pair->second)
238 {
239 Elem * elem = _mesh.queryElemPtr(id);
240 if (elem)
241 {
242 primary_elems_to_ghost.insert(elem);
243
244 const unsigned int n_nodes = elem->n_nodes();
245 for (unsigned int n = 0; n != n_nodes; ++n)
246 nodes_to_ghost.insert(elem->node_ptr(n));
247 }
248 }
249 }
250
251 // Send nodes first since elements need them
252 _mesh.getMesh().comm().allgather_packed_range(&_mesh.getMesh(),
253 nodes_to_ghost.begin(),
254 nodes_to_ghost.end(),
256
257 _mesh.getMesh().comm().allgather_packed_range(&_mesh.getMesh(),
258 primary_elems_to_ghost.begin(),
259 primary_elems_to_ghost.end(),
261
262 // After allgather_packed_range(), rebuild internal connectivity.
263 // This updates ghost nodes/elements across processors and reconstructs node_to_elem_map.
264 _mesh.update();
265
266 // Find elems again now that we know they're there
267 const auto & new_node_to_elem_map = _mesh.nodeToElemMap();
268 node_to_elem_pair = new_node_to_elem_map.find(_primary_node_vector[0]);
269 found_elems = (node_to_elem_pair != new_node_to_elem_map.end());
270 }
271
272 if (!found_elems)
273 mooseError("Couldn't find any elements connected to primary node");
274
275 const std::vector<dof_id_type> & elems = node_to_elem_pair->second;
276
277 if (elems.size() == 0)
278 mooseError("Couldn't find any elements connected to primary node");
279 _subproblem.addGhostedElem(elems[0]);
280}
281
282Real
284{
285 switch (type)
286 {
287 case Moose::Secondary:
288 return (_u_secondary[_i] - _u_primary[_j]) * _penalty;
289 case Moose::Primary:
290 return (_u_primary[_j] - _u_secondary[_i]) * _penalty;
291 }
292 return 0.;
293}
294
295Real
297{
298 switch (type)
299 {
301 return _penalty;
303 return -_penalty;
305 return _penalty;
307 return -_penalty;
308 default:
309 mooseError("Unsupported type");
310 break;
311 }
312 return 0.;
313}
registerMooseObject("MooseApp", EqualValueBoundaryConstraint)
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application.
Definition MooseError.h:311
dof_id_type getPrimaryNodeIDByCoord() const
Get the primary node ID by searching for the node with coordinates matching _primary_node_coord on th...
virtual Real computeQpJacobian(Moose::ConstraintJacobianType type) override
Computes the jacobian for the constraint.
void pickPrimaryNode()
Pick the primary node from user input or from the secondary node set.
void populateSecondaryNodes()
Populate the set of secondary nodes from user input.
void ghostPrimary()
Ghost elements and nodes connected to the primary node.
EqualValueBoundaryConstraint(const InputParameters &parameters)
void updateConstrainedNodes()
Update the sets of nodes with constrained DOFs.
Real _penalty
Penalty if constraint is not satisfied.
virtual void meshChanged() override
Called on this object when the mesh changes.
virtual Real computeQpResidual(Moose::ConstraintType type) override
Computes the residual for the current secondary node.
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.
void addRequiredParam(const std::string &name, const std::string &doc_string)
This method adds a parameter and documentation string to the InputParameters object that will be extr...
void addClassDescription(const std::string &doc_string)
This method adds a description of the class that will be displayed in the input file syntax dump.
const std::string & type() const
Get the type of this class.
Definition MooseBase.h:93
void paramError(const std::string &param, Args... args) const
Emits an error prefixed with the file and line number of the given param (from the input file) along ...
Definition MooseBase.h:457
bool isParamValid(const std::string &name) const
Test if the supplied parameter is valid.
Definition MooseBase.h:199
virtual const Node & nodeRef(const dof_id_type i) const
Definition MooseMesh.C:841
MeshBase & getMesh()
Accessor for the underlying libMesh Mesh object.
Definition MooseMesh.C:3549
void update()
Calls buildNodeListFromSideList(), buildNodeList(), and buildBndElemList().
Definition MooseMesh.C:620
const std::unordered_map< dof_id_type, std::vector< dof_id_type > > & nodeToElemMap()
If not already created, creates a map from every node to all elements to which they are connected.
Definition MooseMesh.C:1236
virtual Elem * queryElemPtr(const dof_id_type i)
Definition MooseMesh.C:3226
BoundaryID getBoundaryID(const BoundaryName &boundary_name) const
Get the associated BoundaryID for the boundary name.
Definition MooseMesh.C:1681
const std::vector< dof_id_type > & getNodeList(boundary_id_type nodeset_id) const
Return a writable reference to a vector of node IDs that belong to nodeset_id.
Definition MooseMesh.C:3571
libMesh::StoredRange< MooseMesh::const_bnd_node_iterator, const BndNode * > * getBoundaryNodeRange()
Definition MooseMesh.C:1285
virtual const Node * queryNodePtr(const dof_id_type i) const
Definition MooseMesh.C:867
unsigned int _i
Counter for primary and secondary nodes.
unsigned int _j
std::vector< dof_id_type > _primary_node_vector
node IDs of the primary node
std::vector< dof_id_type > _connected_nodes
node IDs connected to the primary node (secondary nodes)
static InputParameters validParams()
const VariableValue & _u_secondary
Value of the unknown variable this BC is action on.
const VariableValue & _u_primary
Holds the current solution at the current quadrature point.
MooseMesh & _mesh
Reference to this Kernel's mesh object.
SubProblem & _subproblem
Reference to this kernel's SubProblem.
virtual void addGhostedElem(dof_id_type elem_id)=0
Will make sure that all dofs connected to elem_id are ghosted to this processor.
void min(const T &r, T &o, Request &req) const
void allgather(const T &send_data, std::vector< T, A > &recv_data) const
processor_id_type processor_id() const
const Parallel::Communicator & comm() const
ConstraintType
Definition MooseTypes.h:812
@ Primary
Definition MooseTypes.h:814
@ Secondary
Definition MooseTypes.h:813
ConstraintJacobianType
Definition MooseTypes.h:851
@ SecondarySecondary
Definition MooseTypes.h:852
@ SecondaryPrimary
Definition MooseTypes.h:853
@ PrimarySecondary
Definition MooseTypes.h:854
@ PrimaryPrimary
Definition MooseTypes.h:855
libmesh_assert(ctx)
static constexpr Real TOLERANCE
uint8_t dof_id_type
const dof_id_type n_nodes