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 1878 : 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 1878 : NearestNodeLocator & nearest_node)
31 : : Restartable(subproblem.getMooseApp(),
32 3756 : Moose::stringify(primary_id) + "to" + Moose::stringify(secondary_id),
33 : "PenetrationLocator",
34 : 0),
35 1878 : PerfGraphInterface(subproblem.getMooseApp().perfGraph(),
36 3756 : "PenetrationLocator_" + Moose::stringify(primary_id) + "_" +
37 1878 : Moose::stringify(secondary_id)),
38 1878 : _subproblem(subproblem),
39 1878 : _mesh(mesh),
40 1878 : _primary_boundary(primary_id),
41 1878 : _secondary_boundary(secondary_id),
42 1878 : _fe_type(order),
43 1878 : _nearest_node(nearest_node),
44 1878 : _penetration_info(declareRestartableDataWithContext<std::map<dof_id_type, PenetrationInfo *>>(
45 1878 : "penetration_info", &_mesh)),
46 3756 : _has_penetrated(declareRestartableData<std::set<dof_id_type>>("has_penetrated")),
47 1878 : _check_whether_reasonable(true),
48 3756 : _update_location(declareRestartableData<bool>("update_location", true)),
49 1878 : _tangential_tolerance(0.0),
50 1878 : _do_normal_smoothing(false),
51 1878 : _normal_smoothing_distance(0.0),
52 1878 : _normal_smoothing_method(NSM_EDGE_BASED),
53 1878 : _use_point_locator(false),
54 18780 : _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 1878 : _fe.resize(libMesh::n_threads());
60 3904 : for (unsigned int i = 0; i < libMesh::n_threads(); i++)
61 : {
62 2026 : unsigned int n_dims = _mesh.dimension();
63 2026 : _fe[i].resize(n_dims + 1);
64 8918 : for (unsigned int dim = 0; dim <= n_dims; ++dim)
65 : {
66 6892 : _fe[i][dim] = FEBase::build(dim, _fe_type).release();
67 6892 : _fe[i][dim]->get_xyz();
68 6892 : _fe[i][dim]->get_phi();
69 6892 : _fe[i][dim]->get_dphi();
70 6892 : _fe[i][dim]->get_dxyzdxi();
71 6892 : _fe[i][dim]->get_d2xyzdxi2();
72 6892 : _fe[i][dim]->get_d2xyzdxideta();
73 6892 : _fe[i][dim]->get_dxyzdeta();
74 6892 : _fe[i][dim]->get_d2xyzdeta2();
75 6892 : _fe[i][dim]->get_d2xyzdxideta();
76 : }
77 : }
78 :
79 1878 : 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 1878 : }
91 :
92 3740 : PenetrationLocator::~PenetrationLocator()
93 : {
94 3886 : for (unsigned int i = 0; i < libMesh::n_threads(); i++)
95 8878 : for (unsigned int dim = 0; dim < _fe[i].size(); dim++)
96 6862 : delete _fe[i][dim];
97 :
98 33253 : for (auto & it : _penetration_info)
99 31383 : delete it.second;
100 3740 : }
101 :
102 : void
103 186064 : PenetrationLocator::detectPenetration()
104 : {
105 930320 : TIME_SECTION("detectPenetration", 3, "Detecting Penetration");
106 :
107 : // Grab the secondary nodes we need to worry about from the NearestNodeLocator
108 186064 : NodeIdRange & secondary_node_range = _nearest_node.secondaryNodeRange();
109 :
110 : // Make sure a master point locator has been built if we'll need one
111 186064 : if (_use_point_locator)
112 41 : _mesh.getPointLocator();
113 :
114 : PenetrationThread pt(_subproblem,
115 186064 : _mesh,
116 186064 : _primary_boundary,
117 186064 : _secondary_boundary,
118 : _penetration_info,
119 186064 : _check_whether_reasonable,
120 186064 : _update_location,
121 : _tangential_tolerance,
122 186064 : _do_normal_smoothing,
123 : _normal_smoothing_distance,
124 : _normal_smoothing_method,
125 186064 : _use_point_locator,
126 186064 : _fe,
127 186064 : _fe_type,
128 : _nearest_node,
129 186064 : _mesh.nodeToElemMap());
130 :
131 186064 : Threads::parallel_reduce(secondary_node_range, pt);
132 :
133 186064 : std::vector<dof_id_type> recheck_secondary_nodes = pt._recheck_secondary_nodes;
134 :
135 : // Update the patch for the secondary nodes in recheck_secondary_nodes and re-run penetration
136 : // thread on these nodes at every nonlinear iteration if patch update strategy is set to
137 : // "iteration".
138 186064 : if (recheck_secondary_nodes.size() > 0 && _patch_update_strategy == Moose::Iteration &&
139 0 : _subproblem.currentlyComputingJacobian())
140 : {
141 : // Update the patch for this subset of secondary nodes and calculate the nearest neighbor_nodes
142 0 : _nearest_node.updatePatch(recheck_secondary_nodes);
143 :
144 : // Re-run the penetration thread to see if these nodes are in contact with the updated patch
145 : NodeIdRange recheck_secondary_node_range(
146 0 : recheck_secondary_nodes.begin(), recheck_secondary_nodes.end(), 1);
147 :
148 0 : Threads::parallel_reduce(recheck_secondary_node_range, pt);
149 0 : }
150 :
151 260776 : if (recheck_secondary_nodes.size() > 0 && _patch_update_strategy != Moose::Iteration &&
152 74712 : _subproblem.currentlyComputingJacobian())
153 10994 : mooseDoOnce(mooseWarning("Warning in PenetrationLocator. Penetration is not "
154 : "detected for one or more secondary nodes. This could be because "
155 : "those secondary nodes simply do not project to faces on the primary "
156 : "surface. However, this could also be because contact should be "
157 : "enforced on those nodes, but the faces that they project to "
158 : "are outside the contact patch, which will give an erroneous "
159 : "result. Use appropriate options for 'patch_size' and "
160 : "'patch_update_strategy' in the Mesh block to avoid this issue. "
161 : "Setting 'patch_update_strategy=iteration' is recommended because "
162 : "it completely avoids this potential issue. Also note that this "
163 : "warning is printed only once, so a similar situation could occur "
164 : "multiple times during the simulation but this warning is printed "
165 : "only at the first occurrence."));
166 186060 : }
167 :
168 : void
169 126 : PenetrationLocator::reinit()
170 : {
171 630 : TIME_SECTION("reinit", 3, "Reinitializing PenetrationLocator");
172 :
173 : // Delete the PenetrationInfo objects we own before clearing the
174 : // map, or we have a memory leak.
175 463 : for (auto & it : _penetration_info)
176 337 : delete it.second;
177 :
178 126 : _penetration_info.clear();
179 :
180 126 : _has_penetrated.clear();
181 :
182 126 : detectPenetration();
183 126 : }
184 :
185 : Real
186 0 : PenetrationLocator::penetrationDistance(dof_id_type node_id)
187 : {
188 0 : PenetrationInfo * info = _penetration_info[node_id];
189 :
190 0 : if (info)
191 0 : return info->_distance;
192 : else
193 0 : return 0;
194 : }
195 :
196 : RealVectorValue
197 0 : PenetrationLocator::penetrationNormal(dof_id_type node_id)
198 : {
199 : std::map<dof_id_type, PenetrationInfo *>::const_iterator found_it =
200 0 : _penetration_info.find(node_id);
201 :
202 0 : if (found_it != _penetration_info.end())
203 0 : return found_it->second->_normal;
204 : else
205 0 : return RealVectorValue(0, 0, 0);
206 : }
207 :
208 : void
209 39 : PenetrationLocator::setUsePointLocator(bool state)
210 : {
211 39 : _use_point_locator = state;
212 39 : }
213 :
214 : void
215 0 : PenetrationLocator::setCheckWhetherReasonable(bool state)
216 : {
217 0 : _check_whether_reasonable = state;
218 0 : }
219 :
220 : void
221 0 : PenetrationLocator::setUpdate(bool update)
222 : {
223 0 : _update_location = update;
224 0 : }
225 :
226 : void
227 552 : PenetrationLocator::setTangentialTolerance(Real tangential_tolerance)
228 : {
229 552 : _tangential_tolerance = tangential_tolerance;
230 552 : }
231 :
232 : void
233 328 : PenetrationLocator::setNormalSmoothingDistance(Real normal_smoothing_distance)
234 : {
235 328 : _normal_smoothing_distance = normal_smoothing_distance;
236 328 : if (_normal_smoothing_distance > 0.0)
237 328 : _do_normal_smoothing = true;
238 328 : }
239 :
240 : void
241 112 : PenetrationLocator::setNormalSmoothingMethod(std::string nsmString)
242 : {
243 112 : if (nsmString == "edge_based")
244 0 : _normal_smoothing_method = NSM_EDGE_BASED;
245 112 : else if (nsmString == "nodal_normal_based")
246 112 : _normal_smoothing_method = NSM_NODAL_NORMAL_BASED;
247 : else
248 0 : mooseError("Invalid normal_smoothing_method: ", nsmString);
249 112 : _do_normal_smoothing = true;
250 112 : }
|