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