https://mooseframework.inl.gov
Loading...
Searching...
No Matches
ElementJacobianDamper.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
11#include "FEProblem.h"
12#include "MooseMesh.h"
13#include "DisplacedProblem.h"
14#include "Assembly.h"
15
16#include "libmesh/quadrature.h" // _qrule
17
18// C++
19#include <algorithm>
20#include <cstring> // for "Jacobian" exception test
21#include <limits>
22
24
25namespace
26{
27void
28restoreNodes(Elem & elem, const std::vector<Point> & point_copies)
29{
30 mooseAssert(elem.n_nodes() == point_copies.size(), "Node restore cache is the wrong size");
31
32 for (const auto i : make_range(elem.n_nodes()))
33 elem.node_ref(i) = point_copies[i];
34}
35}
36
39{
41 params.addClassDescription("Damper that limits the change in element Jacobians");
42 params.addParam<std::vector<VariableName>>(
43 "displacements", {}, "The nonlinear displacement variables");
44 params.addParam<Real>(
45 "max_increment",
46 0.1,
47 "The maximum permissible relative increment in the Jacobian per Newton iteration");
48 params.addParam<bool>(
49 "use_backtracking",
50 false,
51 "If true, iteratively cut back the probed Newton update when a full trial update would "
52 "create a degenerate displaced element map. The accepted trial must also satisfy "
53 "max_increment.");
54 params.addRangeCheckedParam<Real>(
55 "backtrack_factor",
56 0.5,
57 "backtrack_factor > 0 & backtrack_factor < 1",
58 "Multiplicative cutback applied to the trial damping during ElementJacobianDamper "
59 "backtracking.");
60 params.addParam<unsigned int>(
61 "max_backtrack_steps", 12, "Maximum number of ElementJacobianDamper backtracking attempts.");
62 params.set<bool>("use_displaced_mesh") = true;
63 return params;
64}
65
67 : GeneralDamper(parameters),
68 _tid(parameters.get<THREAD_ID>("_tid")),
69 _assembly(_subproblem.assembly(_tid, _sys.number())),
70 _qrule(_assembly.qRule()),
71 _JxW(_assembly.JxW()),
72 _fe_problem(*parameters.getCheckedPointerParam<FEProblemBase *>("_fe_problem_base")),
73 _displaced_problem(_fe_problem.getDisplacedProblem()),
74 _max_jacobian_diff(getParam<Real>("max_increment")),
75 _use_backtracking(getParam<bool>("use_backtracking")),
76 _backtrack_factor(getParam<Real>("backtrack_factor")),
77 _max_backtrack_steps(getParam<unsigned int>("max_backtrack_steps"))
78{
79 if (_displaced_problem == NULL)
80 mooseError("ElementJacobianDamper: Must use displaced problem");
81
82 _mesh = &_displaced_problem->mesh();
83
84 const std::vector<VariableName> & nl_vnames(getParam<std::vector<VariableName>>("displacements"));
85 _ndisp = nl_vnames.size();
86
87 for (unsigned int i = 0; i < _ndisp; ++i)
88 {
89 _disp_var.push_back(&_sys.getFieldVariable<Real>(_tid, nl_vnames[i]));
90 _disp_incr.push_back(_disp_var.back()->increment());
91 }
92}
93
94void
98
99Real
100ElementJacobianDamper::computeDamping(const NumericVector<Number> & /* solution */,
101 const NumericVector<Number> & update)
102{
103 auto probe_trial =
104 [&](const Real trial_damping, Real & max_difference, std::string & error_message)
105 {
106 const bool trial_nondegenerate_local =
107 probeDamping(update, trial_damping, max_difference, error_message);
108
109 _communicator.max(max_difference);
110
111 const unsigned int invalid_local = trial_nondegenerate_local ? 0 : 1;
112 unsigned int invalid = invalid_local;
113 _communicator.max(invalid);
114
115 if (invalid)
116 {
117 processor_id_type first_invalid_processor =
118 invalid_local ? _communicator.rank() : std::numeric_limits<processor_id_type>::max();
119 _communicator.min(first_invalid_processor);
120 _communicator.broadcast(error_message, first_invalid_processor);
121 }
122
123 return !invalid;
124 };
125
127 {
128 Real max_difference = 0.0;
129 std::string error_message;
130
131 PARALLEL_TRY
132 {
133 if (!probe_trial(/*trial_damping=*/1.0, max_difference, error_message))
134 _fe_problem.setException(error_message);
135 }
136 PARALLEL_CATCH;
137
138 if (max_difference > _max_jacobian_diff)
139 return _max_jacobian_diff / max_difference;
140
141 return 1.0;
142 }
143
144 const Real minimum_trial_damping = std::max(_min_damping, std::numeric_limits<Real>::epsilon());
145 Real damping = 1.0;
146 std::string error_message;
147
148 // Try 1 step, then at most max_backtrack_steps extra steps
149 for (unsigned int step = 0; step <= _max_backtrack_steps; ++step)
150 {
151 Real max_difference = 0.0;
152 bool trial_nondegenerate = false;
153
154 PARALLEL_TRY { trial_nondegenerate = probe_trial(damping, max_difference, error_message); }
155 PARALLEL_CATCH;
156
157 if (trial_nondegenerate && max_difference <= _max_jacobian_diff)
158 return damping;
159
160 if (damping <= minimum_trial_damping || step == _max_backtrack_steps)
161 {
162 if (!trial_nondegenerate)
163 _fe_problem.setException(error_message.empty()
164 ? "ElementJacobianDamper could not find a nondegenerate "
165 "trial update."
166 : error_message);
167 else
168 _fe_problem.setException("ElementJacobianDamper could not reduce the relative Jacobian "
169 "increment below max_increment without driving the damping "
170 "factor below a usable threshold.");
171
172 // The exception flag will abort the nonlinear step after the damper returns, so return the
173 // last trial damping only to satisfy the interface.
174 return damping;
175 }
176
177 if (!trial_nondegenerate)
178 damping *= _backtrack_factor;
179 else
180 damping =
181 std::min(damping * _backtrack_factor, damping * _max_jacobian_diff / max_difference);
182 }
183
184 return damping;
185}
186
187bool
188ElementJacobianDamper::probeDamping(const NumericVector<Number> & update,
189 const Real damping,
190 Real & max_difference,
191 std::string & error_message)
192{
193 MooseArray<Real> JxW_displaced;
194
195 // Vector for storing the original node coordinates
196 std::vector<Point> point_copies;
197
198 max_difference = 0.0;
199 error_message.clear();
200
201 try
202 {
203 // Loop over elements in the mesh
204 for (auto & current_elem : _mesh->getMesh().active_local_element_ptr_range())
205 {
206 point_copies.clear();
207 point_copies.reserve(current_elem->n_nodes());
208
209 // Displace nodes with the trial Newton increment
210 for (unsigned int i = 0; i < current_elem->n_nodes(); ++i)
211 {
212 Node & displaced_node = current_elem->node_ref(i);
213
214 point_copies.push_back(displaced_node);
215
216 for (unsigned int j = 0; j < _ndisp; ++j)
217 {
218 const dof_id_type disp_dof_num =
219 displaced_node.dof_number(_sys.number(), _disp_var[j]->number(), 0);
220 displaced_node(j) += damping * update(disp_dof_num);
221 }
222 }
223
224 try
225 {
226 // Reinit element to compute Jacobian of the trial displaced element
227 _assembly.reinit(current_elem);
228 JxW_displaced = _JxW;
229 }
230 catch (const std::exception & e)
231 {
232 restoreNodes(*current_elem, point_copies);
233
234 // Degenerate-map failures mean this trial damping is too aggressive
235 if (strstr(e.what(), "Jacobian") || strstr(e.what(), "singular") ||
236 strstr(e.what(), "det != 0"))
237 {
238 error_message = e.what();
239 return false;
240 }
241
242 throw;
243 }
244
245 restoreNodes(*current_elem, point_copies);
246
247 // Reinit element to compute Jacobian before displacement
248 try
249 {
250 _assembly.reinit(current_elem);
251 }
252 catch (const std::exception & e)
253 {
254 if (strstr(e.what(), "Jacobian") || strstr(e.what(), "singular") ||
255 strstr(e.what(), "det != 0"))
256 {
257 error_message = e.what();
258 return false;
259 }
260
261 throw;
262 }
263
264 for (unsigned int qp = 0; qp < _qrule->n_points(); ++qp)
265 {
266 const Real denominator = std::max(std::abs(_JxW[qp]), libMesh::TOLERANCE);
267 const Real diff = std::abs(JxW_displaced[qp] - _JxW[qp]) / denominator;
268 if (diff > max_difference)
269 max_difference = diff;
270 }
271
272 JxW_displaced.release();
273 }
274 }
275 catch (const MooseException & e)
276 {
277 error_message = e.what();
279 return false;
280 }
281
282 return true;
283}
registerMooseObject("SolidMechanicsApp", ElementJacobianDamper)
unsigned int THREAD_ID
void ErrorVector unsigned int
void reinit(const Elem *elem)
const Real & _min_damping
SystemBase & _sys
This class implements a damper that limits the change in the Jacobian of elements.
std::vector< MooseVariable * > _disp_var
The displacement variables.
static InputParameters validParams()
const QBase *const & _qrule
Quadrature rule.
std::vector< VariableValue > _disp_incr
The current Newton increment in the displacement variables.
const Real _max_jacobian_diff
Maximum allowed relative increment in Jacobian.
unsigned int _ndisp
The number of displacement variables.
ElementJacobianDamper(const InputParameters &parameters)
MooseSharedPointer< DisplacedProblem > _displaced_problem
The displaced problem.
const unsigned int _max_backtrack_steps
Maximum number of backtracking attempts.
MooseMesh * _mesh
The displaced mesh.
const bool _use_backtracking
Whether to backtrack the trial update when probing would otherwise create a degenerate map.
FEProblemBase & _fe_problem
The FE problem.
const MooseArray< Real > & _JxW
Transformed Jacobian weights.
bool probeDamping(const NumericVector< Number > &update, Real damping, Real &max_difference, std::string &error_message)
Probe a damped update on the displaced mesh and report the maximum relative change in JxW.
virtual Real computeDamping(const NumericVector< Number > &, const NumericVector< Number > &update) override
Computes this Damper's damping.
virtual void initialSetup() override
const Real _backtrack_factor
Multiplicative cutback applied during backtracking.
virtual void setException(const std::string &message)
static InputParameters validParams()
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
void addClassDescription(const std::string &doc_string)
T & set(const std::string &name, bool quiet_mode=false)
void addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
void clear()
void release()
void mooseError(Args &&... args) const
const T & getParam(const std::string &name) const
virtual const char * what() const
MeshBase & getMesh()
MooseVariableFE< T > & getFieldVariable(THREAD_ID tid, const std::string &var_name)
unsigned int number() const
void max(const T &r, T &o, Request &req) const
processor_id_type rank() const
void min(const T &r, T &o, Request &req) const
void broadcast(T &data, const unsigned int root_id=0, const bool identical_sizes=false) const
const Parallel::Communicator & _communicator
static constexpr Real TOLERANCE
const dof_id_type n_nodes