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 "PenetrationLocator.h"
11 :
12 : #include "ArbitraryQuadrature.h"
13 : #include "Conversion.h"
14 : #include "GeometricSearchData.h"
15 : #include "LineSegment.h"
16 : #include "MooseMesh.h"
17 : #include "NearestNodeLocator.h"
18 : #include "PenetrationThread.h"
19 : #include "SubProblem.h"
20 : #include "MooseApp.h"
21 :
22 : using namespace libMesh;
23 :
24 1728 : PenetrationLocator::PenetrationLocator(SubProblem & subproblem,
25 : GeometricSearchData & /*geom_search_data*/,
26 : MooseMesh & mesh,
27 : const unsigned int primary_id,
28 : const unsigned int secondary_id,
29 : Order order,
30 1728 : NearestNodeLocator & nearest_node)
31 : : Restartable(subproblem.getMooseApp(),
32 3456 : Moose::stringify(primary_id) + "to" + Moose::stringify(secondary_id),
33 : "PenetrationLocator",
34 : 0),
35 1728 : PerfGraphInterface(subproblem.getMooseApp().perfGraph(),
36 3456 : "PenetrationLocator_" + Moose::stringify(primary_id) + "_" +
37 1728 : Moose::stringify(secondary_id)),
38 1728 : _subproblem(subproblem),
39 1728 : _mesh(mesh),
40 1728 : _primary_boundary(primary_id),
41 1728 : _secondary_boundary(secondary_id),
42 1728 : _fe_type(order),
43 1728 : _nearest_node(nearest_node),
44 1728 : _penetration_info(declareRestartableDataWithContext<std::map<dof_id_type, PenetrationInfo *>>(
45 1728 : "penetration_info", &_mesh)),
46 3456 : _has_penetrated(declareRestartableData<std::set<dof_id_type>>("has_penetrated")),
47 1728 : _check_whether_reasonable(true),
48 3456 : _update_location(declareRestartableData<bool>("update_location", true)),
49 1728 : _tangential_tolerance(0.0),
50 1728 : _do_normal_smoothing(false),
51 1728 : _normal_smoothing_distance(0.0),
52 1728 : _normal_smoothing_method(NSM_EDGE_BASED),
53 1728 : _use_point_locator(false),
54 17280 : _patch_update_strategy(_mesh.getPatchUpdateStrategy())
55 : {
56 : // Preconstruct an FE object for each thread we're going to use and for each lower-dimensional
57 : // element
58 : // This is a time savings so that the thread objects don't do this themselves multiple times
59 1728 : _fe.resize(libMesh::n_threads());
60 3604 : for (unsigned int i = 0; i < libMesh::n_threads(); i++)
61 : {
62 1876 : unsigned int n_dims = _mesh.dimension();
63 1876 : _fe[i].resize(n_dims + 1);
64 8259 : for (unsigned int dim = 0; dim <= n_dims; ++dim)
65 : {
66 6383 : _fe[i][dim] = FEBase::build(dim, _fe_type).release();
67 6383 : _fe[i][dim]->get_xyz();
68 6383 : _fe[i][dim]->get_phi();
69 6383 : _fe[i][dim]->get_dphi();
70 6383 : _fe[i][dim]->get_dxyzdxi();
71 6383 : _fe[i][dim]->get_d2xyzdxi2();
72 6383 : _fe[i][dim]->get_d2xyzdxideta();
73 6383 : _fe[i][dim]->get_dxyzdeta();
74 6383 : _fe[i][dim]->get_d2xyzdeta2();
75 6383 : _fe[i][dim]->get_d2xyzdxideta();
76 : }
77 : }
78 :
79 1728 : if (_normal_smoothing_method == NSM_NODAL_NORMAL_BASED)
80 : {
81 0 : if (!((_subproblem.hasVariable("nodal_normal_x")) &&
82 0 : (_subproblem.hasVariable("nodal_normal_y")) &&
83 0 : (_subproblem.hasVariable("nodal_normal_z"))))
84 : {
85 0 : mooseError(
86 : "To use nodal-normal-based smoothing, the nodal_normal_x, nodal_normal_y, and "
87 : "nodal_normal_z variables must exist. Are you missing the \\[NodalNormals\\] block?");
88 : }
89 : }
90 :
91 : // Request the nodeToElem map upfront
92 1728 : _mesh.nodeToElemMap();
93 1728 : }
94 :
95 3444 : PenetrationLocator::~PenetrationLocator()
96 : {
97 3590 : for (unsigned int i = 0; i < libMesh::n_threads(); i++)
98 8227 : for (unsigned int dim = 0; dim < _fe[i].size(); dim++)
99 6359 : delete _fe[i][dim];
100 :
101 30313 : for (auto & it : _penetration_info)
102 28591 : delete it.second;
103 3444 : }
104 :
105 : void
106 169968 : PenetrationLocator::detectPenetration()
107 : {
108 849840 : TIME_SECTION("detectPenetration", 3, "Detecting Penetration");
109 :
110 : // Grab the secondary nodes we need to worry about from the NearestNodeLocator
111 169968 : NodeIdRange & secondary_node_range = _nearest_node.secondaryNodeRange();
112 :
113 : // Make sure a master point locator has been built if we'll need one
114 169968 : if (_use_point_locator)
115 38 : _mesh.getPointLocator();
116 :
117 : PenetrationThread pt(_subproblem,
118 169968 : _mesh,
119 169968 : _primary_boundary,
120 169968 : _secondary_boundary,
121 : _penetration_info,
122 169968 : _check_whether_reasonable,
123 169968 : _update_location,
124 : _tangential_tolerance,
125 169968 : _do_normal_smoothing,
126 : _normal_smoothing_distance,
127 : _normal_smoothing_method,
128 169968 : _use_point_locator,
129 169968 : _fe,
130 169968 : _fe_type,
131 : _nearest_node,
132 169968 : _mesh.nodeToElemMap());
133 :
134 169968 : Threads::parallel_reduce(secondary_node_range, pt);
135 :
136 169968 : std::vector<dof_id_type> recheck_secondary_nodes = pt._recheck_secondary_nodes;
137 :
138 : // Update the patch for the secondary nodes in recheck_secondary_nodes and re-run penetration
139 : // thread on these nodes at every nonlinear iteration if patch update strategy is set to
140 : // "iteration".
141 169968 : if (recheck_secondary_nodes.size() > 0 && _patch_update_strategy == Moose::Iteration &&
142 0 : _subproblem.currentlyComputingJacobian())
143 : {
144 : // Update the patch for this subset of secondary nodes and calculate the nearest neighbor_nodes
145 0 : _nearest_node.updatePatch(recheck_secondary_nodes);
146 :
147 : // Re-run the penetration thread to see if these nodes are in contact with the updated patch
148 : NodeIdRange recheck_secondary_node_range(
149 0 : recheck_secondary_nodes.begin(), recheck_secondary_nodes.end(), 1);
150 :
151 0 : Threads::parallel_reduce(recheck_secondary_node_range, pt);
152 0 : }
153 :
154 238382 : if (recheck_secondary_nodes.size() > 0 && _patch_update_strategy != Moose::Iteration &&
155 68414 : _subproblem.currentlyComputingJacobian())
156 10061 : mooseDoOnce(mooseWarning("Warning in PenetrationLocator. Penetration is not "
157 : "detected for one or more secondary nodes. This could be because "
158 : "those secondary nodes simply do not project to faces on the primary "
159 : "surface. However, this could also be because contact should be "
160 : "enforced on those nodes, but the faces that they project to "
161 : "are outside the contact patch, which will give an erroneous "
162 : "result. Use appropriate options for 'patch_size' and "
163 : "'patch_update_strategy' in the Mesh block to avoid this issue. "
164 : "Setting 'patch_update_strategy=iteration' is recommended because "
165 : "it completely avoids this potential issue. Also note that this "
166 : "warning is printed only once, so a similar situation could occur "
167 : "multiple times during the simulation but this warning is printed "
168 : "only at the first occurrence."));
169 169965 : }
170 :
171 : void
172 116 : PenetrationLocator::reinit()
173 : {
174 580 : TIME_SECTION("reinit", 3, "Reinitializing PenetrationLocator");
175 :
176 : // Delete the PenetrationInfo objects we own before clearing the
177 : // map, or we have a memory leak.
178 427 : for (auto & it : _penetration_info)
179 311 : delete it.second;
180 :
181 116 : _penetration_info.clear();
182 :
183 116 : _has_penetrated.clear();
184 :
185 116 : detectPenetration();
186 116 : }
187 :
188 : Real
189 0 : PenetrationLocator::penetrationDistance(dof_id_type node_id)
190 : {
191 0 : PenetrationInfo * info = _penetration_info[node_id];
192 :
193 0 : if (info)
194 0 : return info->_distance;
195 : else
196 0 : return 0;
197 : }
198 :
199 : RealVectorValue
200 0 : PenetrationLocator::penetrationNormal(dof_id_type node_id)
201 : {
202 : std::map<dof_id_type, PenetrationInfo *>::const_iterator found_it =
203 0 : _penetration_info.find(node_id);
204 :
205 0 : if (found_it != _penetration_info.end())
206 0 : return found_it->second->_normal;
207 : else
208 0 : return RealVectorValue(0, 0, 0);
209 : }
210 :
211 : void
212 36 : PenetrationLocator::setUsePointLocator(bool state)
213 : {
214 36 : _use_point_locator = state;
215 36 : }
216 :
217 : void
218 0 : PenetrationLocator::setCheckWhetherReasonable(bool state)
219 : {
220 0 : _check_whether_reasonable = state;
221 0 : }
222 :
223 : void
224 0 : PenetrationLocator::setUpdate(bool update)
225 : {
226 0 : _update_location = update;
227 0 : }
228 :
229 : void
230 512 : PenetrationLocator::setTangentialTolerance(Real tangential_tolerance)
231 : {
232 512 : _tangential_tolerance = tangential_tolerance;
233 512 : }
234 :
235 : void
236 304 : PenetrationLocator::setNormalSmoothingDistance(Real normal_smoothing_distance)
237 : {
238 304 : _normal_smoothing_distance = normal_smoothing_distance;
239 304 : if (_normal_smoothing_distance > 0.0)
240 304 : _do_normal_smoothing = true;
241 304 : }
242 :
243 : void
244 104 : PenetrationLocator::setNormalSmoothingMethod(std::string nsmString)
245 : {
246 104 : if (nsmString == "edge_based")
247 0 : _normal_smoothing_method = NSM_EDGE_BASED;
248 104 : else if (nsmString == "nodal_normal_based")
249 104 : _normal_smoothing_method = NSM_NODAL_NORMAL_BASED;
250 : else
251 0 : mooseError("Invalid normal_smoothing_method: ", nsmString);
252 104 : _do_normal_smoothing = true;
253 104 : }
|