b2api
B2000++ API Reference Manual, VERSION 4.6
 
Loading...
Searching...
No Matches
b2static_nonlinear_utile.H
1//------------------------------------------------------------------------
2// b2static_nonlinear_utile.H --
3//
4//
5// written by Mathias Doreille
6// Neda Ebrahimi Pour <neda.ebrahimipour@dlr.de>
7// Thomas Blome <thomas.blome@dlr.de>
8//
9// (c) 2004-2012,2016 SMR Engineering & Development SA
10// 2502 Bienne, Switzerland
11//
12// (c) 2023 Deutsches Zentrum für Luft- und Raumfahrt (DLR) e.V.
13// Linder Höhe, 51147 Köln
14//
15// All Rights Reserved. Proprietary source code. The contents of
16// this file may not be disclosed to third parties, copied or
17// duplicated in any form, in whole or in part, without the prior
18// written permission of SMR.
19//------------------------------------------------------------------------
20
21// #define CHECK_RESIDUE_DERIVATIVE 1
22
23#ifndef B2STATIC_NONLINEAR_UTILE_H_
24#define B2STATIC_NONLINEAR_UTILE_H_
25
26#include <cmath>
27#include <limits>
28
29#include "b2ppconfig.h"
31#include "model/b2case.H"
32#include "model/b2domain.H"
33#include "model/b2element.H"
34#include "model/b2model.H"
35#include "model/b2solution.H"
36#include "model/b2solver.H"
37#include "solvers/b2constraint_util.H"
38#include "solvers/b2solver_utils.H"
39#include "utils/b2linear_algebra.H"
40#include "utils/b2logging.H"
41#include "utils/b2numerical_differentiation.H"
42#include "utils/b2object.H"
43#include "utils/b2util.H"
44
45namespace b2000 { namespace solver {
46
47template <typename T, typename MATRIX_FORMAT>
48class StaticResidueFunction;
49
50template <typename T, typename MATRIX_FORMAT>
51class IncrementConstraintStrategy;
52
53template <typename T, typename MATRIX_FORMAT>
54class CorrectionStrategy;
55
56template <typename T>
57class CorrectionTerminationTest;
58class LineSearch;
59
60template <typename T>
61class StaticResidueFunctionForTerminationTest : public Object {
62public:
63 virtual double get_energy_normalisation1() {
65 return 0;
66 }
67
68 virtual void mrbc_get_nonlinear_value(
69 const b2linalg::Vector<T, b2linalg::Vdense_constref>& dof, double time,
70 EquilibriumSolution equilibrium_solution, b2linalg::Vector<T, b2linalg::Vdense_ref> value,
71 b2linalg::Matrix<T, b2linalg::Mcompressed_col>& d_value_d_dof_trans,
72 b2linalg::Vector<T, b2linalg::Vdense_ref> d_value_d_time, SolverHints* solver_hints) = 0;
73
74 virtual size_t get_number_of_dof() = 0;
75
76 virtual size_t get_number_of_non_lagrange_dof() = 0;
77};
78
79template <typename T>
80class StaticResidueFunctionGeneric : public StaticResidueFunctionForTerminationTest<T> {
81public:
82 virtual void init(Model& model, const AttributeList& attribute) = 0;
83
84 virtual Model& get_model() = 0;
85
86 virtual T get_residue_normalization() {
88 return 0;
89 }
90
91 virtual double get_energy() = 0;
92
93 virtual double get_energy_increment(const b2linalg::Vector<T>& dof_increment) = 0;
94
95 virtual const b2linalg::Vector<T, b2linalg::Vdense_constref> get_nbc_sol() = 0;
96
97 virtual const b2linalg::Vector<T, b2linalg::Vdense_constref> get_residuum_sol(
98 const b2linalg::Vector<T, b2linalg::Vdense_constref>& dof, const double time) = 0;
99
100 virtual void get_solution(
101 const b2linalg::Vector<T, b2linalg::Vdense_constref> dof_red, const double time,
102 EquilibriumSolution equilibrium_solution,
103 b2linalg::Vector<T, b2linalg::Vdense_ref> dof) = 0;
104};
105
106template <typename T, typename MATRIX_FORMAT>
107class StaticResidueFunction : public StaticResidueFunctionGeneric<T> {
108public:
109 StaticResidueFunction()
110 : logger(logging::get_logger("solver.static_nonlinear.ResidueFunction")) {}
111
112 virtual const b2linalg::Vector<T, b2linalg::Vindex1_constref> get_matrix_diagonal(
113 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible>& d_residue_d_dof) = 0;
114
115 virtual void get_residue(
116 const b2linalg::Vector<T, b2linalg::Vdense_constref>& dof, const double time,
117 const double delta_time, EquilibriumSolution& equilibrium_solution,
118 b2linalg::Vector<T, b2linalg::Vdense_ref> residue,
119 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible>& d_residue_d_dof,
120 b2linalg::Vector<T, b2linalg::Vdense_ref> d_residue_d_time, SolverHints* solver_hints,
121 CorrectionTerminationTest<T>* termination_test = 0,
122 GradientContainer* gradient_container = nullptr) = 0;
123
124 void get_d_dof_d_path(
125 const b2linalg::Vector<T, b2linalg::Vdense_constref> dof, const double time,
126 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible_ext>& K,
127 b2linalg::Vector<T, b2linalg::Vdense_ref> q,
128 b2linalg::Vector<T, b2linalg::Vdense_ref> arc,
129 b2linalg::Matrix<T, b2linalg::Mrectangle_ref> d_dof_and_time_d_path,
130 int approximation_order = 1, double h_scale = 1.0) {
131 const size_t rs = K.size1();
132 d_dof_and_time_d_path.set_zero();
133 if (d_dof_and_time_d_path.size2() > 0) {
134 EquilibriumSolution est(true);
135 get_residue(
136 dof, time, 0, est, b2linalg::Vector<T, b2linalg::Vdense_ref>::null, K, q, 0);
137 d_dof_and_time_d_path(rs - 1, 0) = 1;
138 StaticResidueFunction<T, MATRIX_FORMAT>::logger
140 << "get_d_dof_d_path: Factorize tangent "
141 "stiffness matrix ("
142 << K.size1() << " rows and columns)." << logging::LOGFLUSH;
143 d_dof_and_time_d_path[0] = update_extension_and_inverse(
144 K, q, arc(b2linalg::Interval(0, rs - 1)), arc[rs - 1])
145 * d_dof_and_time_d_path[0];
146 d_dof_and_time_d_path[0] *= 1.0 / (d_dof_and_time_d_path[0] * d_dof_and_time_d_path[0]);
147 arc = d_dof_and_time_d_path[0];
148 h_scale /= d_dof_and_time_d_path[0](b2linalg::Interval(0, rs - 1)).norm2();
149 }
150
151 /* python script to generate the J values
152 * TODO replace it by the Leibnitz Formula for the n’th Derivative of a Product
153 n = 20
154 r = ''
155 a = {(1, 2): 1}
156 for i in range(3, n):
157 b = {}
158 def add_to_b(k1, k2, v):
159 if k1 > k2:
160 k1, k2 = k2, k1
161 b[(k1, k2)] = b.setdefault((k1, k2), 0) + v
162 for k, v in a.items():
163 add_to_b(k[0] + 1, k[1], v)
164 add_to_b(k[0], k[1] + 1, v)
165 a = b
166 r += repr([[-v, k[0], k[1]] for k, v in a.items() if k != (1, i)]) + ',\n'
167 print """
168 static const int J[%d][%d][3] = {
169 {},""" % (n - 2, len(a))
170 print r.replace('[', '{').replace(']', '}') + '};'
171 */
172 static const int J[18][10][3] = {
173 {},
174 {{-1, 2, 2}},
175 {{-3, 2, 3}},
176 {{-3, 3, 3}, {-4, 2, 4}},
177 {{-10, 3, 4}, {-5, 2, 5}},
178 {{-6, 2, 6}, {-10, 4, 4}, {-15, 3, 5}},
179 {{-7, 2, 7}, {-35, 4, 5}, {-21, 3, 6}},
180 {{-28, 3, 7}, {-56, 4, 6}, {-35, 5, 5}, {-8, 2, 8}},
181 {{-36, 3, 8}, {-126, 5, 6}, {-84, 4, 7}, {-9, 2, 9}},
182 {{-126, 6, 6}, {-210, 5, 7}, {-10, 2, 10}, {-120, 4, 8}, {-45, 3, 9}},
183 {{-165, 4, 9}, {-55, 3, 10}, {-11, 2, 11}, {-462, 6, 7}, {-330, 5, 8}},
184 {{-495, 5, 9}, {-792, 6, 8}, {-220, 4, 10}, {-66, 3, 11}, {-462, 7, 7}, {-12, 2, 12}},
185 {{-1287, 6, 9},
186 {-715, 5, 10},
187 {-13, 2, 13},
188 {-78, 3, 12},
189 {-1716, 7, 8},
190 {-286, 4, 11}},
191 {{-2002, 6, 10},
192 {-91, 3, 13},
193 {-1716, 8, 8},
194 {-1001, 5, 11},
195 {-364, 4, 12},
196 {-14, 2, 14},
197 {-3003, 7, 9}},
198 {{-6435, 8, 9},
199 {-3003, 6, 11},
200 {-1365, 5, 12},
201 {-455, 4, 13},
202 {-5005, 7, 10},
203 {-15, 2, 15},
204 {-105, 3, 14}},
205 {{-120, 3, 15},
206 {-11440, 8, 10},
207 {-16, 2, 16},
208 {-560, 4, 14},
209 {-1820, 5, 13},
210 {-6435, 9, 9},
211 {-4368, 6, 12},
212 {-8008, 7, 11}},
213 {{-2380, 5, 14},
214 {-12376, 7, 12},
215 {-680, 4, 15},
216 {-24310, 9, 10},
217 {-6188, 6, 13},
218 {-136, 3, 16},
219 {-19448, 8, 11},
220 {-17, 2, 17}},
221 {{-18564, 7, 13},
222 {-24310, 10, 10},
223 {-31824, 8, 12},
224 {-816, 4, 16},
225 {-3060, 5, 15},
226 {-43758, 9, 11},
227 {-153, 3, 17},
228 {-18, 2, 18},
229 {-8568, 6, 14}},
230 };
231
232 int ao = approximation_order + d_dof_and_time_d_path.size2() - 2;
233 for (size_t i = 1; i < d_dof_and_time_d_path.size2(); ++i, --ao) {
234 for (int j = 0; J[i - 1][j][0] != 0; ++j) {
235 d_dof_and_time_d_path(rs - 1, i) += J[i - 1][j][0]
236 * (d_dof_and_time_d_path[J[i - 1][j][1] - 1]
237 * d_dof_and_time_d_path[J[i - 1][j][2] - 1]);
238 }
239 TaylorPathResidue tpr = {dof, time, d_dof_and_time_d_path, i, *this, arc};
240 std::vector<b2linalg::Vector<T, b2linalg::Vdense_ref> > res;
241 res.push_back(d_dof_and_time_d_path[i](b2linalg::Interval(0, rs - 1)));
242
243#ifdef TEST_D_DOF_D_PATH
244 for (size_t ii = 1; ii != i + 2; ++ii) {
246 TaylorPathResidue, Vector<T, Vdense_ref>, Vector<T, Vdense> >(
247 tpr, 0, res, ii, ao, ii, std::pow(1e-15, 1.0 / (ao + ii)));
248 std::cout << "ii=" << ii << ", " << res[0][0] << std::endl;
249 res[0].set_zero();
250 }
251
252 for (int ii = 0; ii != 10; ++ii) {
253 Vector<T, Vdense> tmp;
254 tpr(0.5 * ii, tmp);
255 std::cout << "x=" << 0.5 * ii << ", " << tmp[0] << std::endl;
256 }
257#endif
258 const int aoo = ao + ao % 2;
260 TaylorPathResidue, b2linalg::Vector<T, b2linalg::Vdense_ref>,
261 b2linalg::Vector<T, b2linalg::Vdense> >(
262 tpr, 0, res, i + 1, aoo, i + 1, std::pow(1e-15, 1.0 / (aoo + i + 1)) * h_scale);
263 if (i == 1) {
264 StaticResidueFunction<T, MATRIX_FORMAT>::logger
266 << "get_d_dof_d_path: Factorize tangent "
267 "stiffness matrix ("
268 << K.size1() << " rows and columns)." << logging::LOGFLUSH;
269 d_dof_and_time_d_path[i] =
270 update_extension_and_inverse(
271 K, q, arc(b2linalg::Interval(0, rs - 1)), arc[rs - 1])
272 * d_dof_and_time_d_path[i];
273 } else {
274 d_dof_and_time_d_path[i] = inverse(K) * d_dof_and_time_d_path[i];
275 }
276 d_dof_and_time_d_path[i] *= -1;
277 }
278 }
279
280 void get_d2_residue_d_dof_d_path(
281 const b2linalg::Vector<T, b2linalg::Vdense_constref> dof, const double time,
282 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible>& K,
283 b2linalg::Matrix<T, b2linalg::Mrectangle_ref> d_dof_and_time_d_path,
284 std::vector<b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible> >&
285 d2_residue_d_dof_d_path,
286 int approximation_order = 1, double h_scale = 1.0) {
287 for (size_t i = 0; i != d2_residue_d_dof_d_path.size(); ++i) {
288 d2_residue_d_dof_d_path[i].set_same_structure(K);
289 }
290 TaylorPath_d_value_d_dof tpdvdd = {dof, time, d_dof_and_time_d_path, *this, K};
292 TaylorPath_d_value_d_dof,
293 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible>,
294 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible> >(
295 tpdvdd, 0, d2_residue_d_dof_d_path, 1, approximation_order, 0,
296 std::pow(1e-15, 1.0 / (approximation_order + d2_residue_d_dof_d_path.size() + 1))
297 * h_scale);
298 }
299
300 using type_t = ObjectTypeIncomplete<StaticResidueFunction>;
301 static type_t type;
302
303protected:
304 logging::Logger& logger;
305
306private:
307 struct TaylorPathResidue {
308 const b2linalg::Vector<T, b2linalg::Vdense_constref>& dof;
309 const double time;
310 const b2linalg::Matrix<T, b2linalg::Mrectangle_ref>& d_dof_and_time_d_path;
311 const size_t s;
312 StaticResidueFunction& residue;
313 const b2linalg::Vector<T, b2linalg::Vdense_constref> arc;
314 void operator()(double x, b2linalg::Vector<T, b2linalg::Vdense>& res) {
315 const b2linalg::Interval inter = b2linalg::Interval(0, dof.size());
316 b2linalg::Vector<T, b2linalg::Vdense> d(d_dof_and_time_d_path.size1());
317 d(inter) = dof;
318 d[dof.size()] = time;
319 b2linalg::Vector<T, b2linalg::Vdense> p(s);
320 {
321 int fact = 1;
322 double v = 1;
323 for (size_t i = 0; i != s; ++i) {
324 v *= x;
325 fact *= i + 1;
326 p[i] = v / fact;
327 }
328 }
329 d += d_dof_and_time_d_path(b2linalg::Interval(0, s)) * p;
330 res.resize(d_dof_and_time_d_path.size1() - 1);
331 res.set_zero();
332 EquilibriumSolution esf(false);
333 residue.get_residue(
334 d(inter), d[dof.size()], 0, esf, res,
335 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible>::null,
336 b2linalg::Vector<T, b2linalg::Vdense_ref>::null, 0, 0);
337 }
338 };
339
340 struct TaylorPath_d_value_d_dof {
341 const b2linalg::Vector<T, b2linalg::Vdense_constref>& dof;
342 const double time;
343 const b2linalg::Matrix<T, b2linalg::Mrectangle_ref>& d_dof_and_time_d_path;
344 StaticResidueFunction& residue;
345 const b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible>& K;
346 void operator()(
347 double x,
348 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible>& d_value_d_dof) {
349 d_value_d_dof.set_same_structure(K);
350 const b2linalg::Interval inter = b2linalg::Interval(0, dof.size());
351 b2linalg::Vector<T, b2linalg::Vdense> d(d_dof_and_time_d_path.size1());
352 d(inter) = dof;
353 d[dof.size()] = time;
354 b2linalg::Vector<T, b2linalg::Vdense> p(d_dof_and_time_d_path.size2());
355 {
356 int fact = 1;
357 double v = 1;
358 for (size_t i = 0; i != p.size(); ++i) {
359 v *= x;
360 fact *= i + 1;
361 p[i] = v / fact;
362 }
363 }
364 d += d_dof_and_time_d_path * p;
365 EquilibriumSolution esf(false);
366 d_value_d_dof.set_same_structure(K);
367 residue.get_residue(
368 d(inter), d[dof.size()], 0, esf, b2linalg::Vector<T, b2linalg::Vdense_ref>::null,
369 d_value_d_dof, b2linalg::Vector<T, b2linalg::Vdense_ref>::null, 0, 0);
370 }
371 };
372};
373
374template <typename T, typename MATRIX_FORMAT>
375typename StaticResidueFunction<T, MATRIX_FORMAT>::type_t
376 StaticResidueFunction<T, MATRIX_FORMAT>::type(
377 "StaticResidueFunction", type_name<T, MATRIX_FORMAT>(),
378 StringList("STATIC_RESIDUE_FUNCTION"), module);
379
380template <typename T, typename MATRIX_FORMAT>
381class IncrementConstraintStrategy : public Object {
382public:
383 enum Result {
384 in_stage,
385 in_stage_and_retry,
386 end_of_stage_with_success,
387 end_of_stage_with_error
388 };
389
390 IncrementConstraintStrategy()
391 : max_time(0),
392 logger(logging::get_logger("solver.static_nonlinear.increment_constraint_strategy")) {}
393
394 virtual void init(Model& model, const AttributeList& attribute) {}
395
396 void set_max_time(double time) { max_time = time; }
397
398 virtual Result set_increment(
399 StaticResidueFunction<T, MATRIX_FORMAT>& residue_function,
400 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible_ext>& K,
401 b2linalg::Vector<T, b2linalg::Vdense_ref> q, b2linalg::Vector<T, b2linalg::Vdense_ref> d,
402 double& time, EquilibriumSolution& equilibrium_solution) = 0;
403
404 virtual void get_constraint(
405 const b2linalg::Vector<T, b2linalg::Vdense>& dof, const double time, T* constraint,
406 b2linalg::Vector<T, b2linalg::Vdense>* d_constraint_d_dof, T* d_constraint_d_time) = 0;
407
408 virtual Result set_correction_result(
409 b2linalg::Vector<T, b2linalg::Vdense>& dof, double& time, bool converged,
410 int number_of_iteration) = 0;
411
412 virtual double get_delta_time() const = 0;
413
414 using type_t = ObjectTypeIncomplete<IncrementConstraintStrategy>;
415 static type_t type;
416
417protected:
418 double max_time;
419 logging::Logger& logger;
420};
421
422template <typename T, typename MATRIX_FORMAT>
423typename IncrementConstraintStrategy<T, MATRIX_FORMAT>::type_t
424 IncrementConstraintStrategy<T, MATRIX_FORMAT>::type(
425 "IncrementConstraintStrategy", type_name<T, MATRIX_FORMAT>(),
426 StringList("INCREMENT_CONSRAINT_STRATEGY"), module);
427
428template <typename T, typename MATRIX_FORMAT>
429class CorrectionStrategy : public Object {
430public:
431 virtual void init(Model& model, const AttributeList& attribute) {}
432
433 virtual typename IncrementConstraintStrategy<T, MATRIX_FORMAT>::Result compute_correction(
434 StaticResidueFunction<T, MATRIX_FORMAT>& residue_function,
435 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible_ext>& K,
436 b2linalg::Vector<T, b2linalg::Vdense>& q,
437 IncrementConstraintStrategy<T, MATRIX_FORMAT>& constraint,
438 b2linalg::Vector<T, b2linalg::Vdense>& d, double& time,
439 EquilibriumSolution& equilibrium_solution) = 0;
440
441 using type_t = ObjectTypeIncomplete<CorrectionStrategy>;
442 static type_t type;
443
444protected:
445 logging::Logger& logger{logging::get_logger("solver.static_nonlinear.correction_strategy")};
446};
447
448template <typename T, typename MATRIX_FORMAT>
449typename CorrectionStrategy<T, MATRIX_FORMAT>::type_t CorrectionStrategy<T, MATRIX_FORMAT>::type(
450 "CorrectionStrategy", type_name<T, MATRIX_FORMAT>(), StringList("CORRECTION_STRATEGY"),
451 module);
452
453template <typename T>
454class CorrectionTerminationTest : public DofFieldFlux<T> {
455public:
456 enum Termination { converged, diverged, max_iteration, unfinished };
457
458 virtual void init(Model& model, const AttributeList& attribute) {}
459
460 virtual void new_step() {}
461
462 virtual void new_iteration() {}
463
464 virtual void new_evaluation() {}
465
466 virtual void converged_step() {}
467
468 virtual bool need_flux() { return false; }
469
470 virtual void add_flux(b2linalg::Index& index, const b2linalg::Vector<T, b2linalg::Vdense>& v) {}
471
472 virtual void add_flux(const b2linalg::Vector<T, b2linalg::Vdense_constref>& v) {}
473
474 virtual double norm_termination(
475 const b2linalg::Vector<T, b2linalg::Vdense_constref>& r,
476 const b2linalg::Vector<T, b2linalg::Vdense_constref>& delta_dof,
477 const double delta_dof_scale, const b2linalg::Vector<T, b2linalg::Vdense_constref>& dof,
478 const double constraint, const double d_constraint_d_time, const double delta_time,
479 const double time, StaticResidueFunctionForTerminationTest<T>& residue,
480 const b2linalg::Vector<T, b2linalg::Vindex1_constref>& diag_K) {
481 return 1;
482 }
483
484 virtual Termination is_terminated(
485 int number_of_iteration, const b2linalg::Vector<T, b2linalg::Vdense_constref>& r,
486 const b2linalg::Vector<T, b2linalg::Vdense_constref>& delta_dof,
487 const double delta_dof_scale, const b2linalg::Vector<T, b2linalg::Vdense_constref>& dof,
488 const b2linalg::Vector<T, b2linalg::Vdense_constref>& dof_at_start_step,
489 const double constraint, const double d_constraint_d_time, const double delta_time,
490 const double time, StaticResidueFunctionForTerminationTest<T>& residue,
491 const b2linalg::Vector<T, b2linalg::Vindex1_constref>& diag_K,
492 double& distance_to_iterative_convergence, const SolverHints* solver_hints) = 0;
493
494 virtual int get_nb_divergence() const { return 0; }
495
496 using type_t = ObjectTypeIncomplete<CorrectionTerminationTest>;
497 static type_t type;
498
499protected:
500 logging::Logger& logger{
501 logging::get_logger("solver.static_nonlinear.correction_termination_test")};
502};
503
504template <typename T>
505typename CorrectionTerminationTest<T>::type_t CorrectionTerminationTest<T>::type(
506 "CorrectionTerminationTest", type_name<T>(), StringList("CORRECTION_TERMINATION_TEST"),
507 module);
508
509class LineSearch : public Object {
510public:
511 struct Function {
512 virtual ~Function() {}
513 virtual double operator()(double h) = 0;
514 };
515
516 virtual void init(Model& model, const AttributeList& attribute) = 0;
517
518 virtual double get_minimum(Function& g, double g0, double g1) = 0;
519
520 using type_t = ObjectTypeIncomplete<LineSearch>;
521 static type_t type;
522
523protected:
524 logging::Logger& logger{logging::get_logger("solver.static_nonlinear.line_search")};
525};
526
527// =====================================================
528// Implementation
529// =====================================================
530
531template <typename T, typename MATRIX_FORMAT>
532class DefaultStaticResidueFunction : public StaticResidueFunction<T, MATRIX_FORMAT> {
533public:
534 using nbc_t = TypedNaturalBoundaryCondition<T, typename MATRIX_FORMAT::sparse_inversible>;
535 using ebc_t = TypedEssentialBoundaryCondition<T>;
536 using mrbc_t = TypedModelReductionBoundaryCondition<T>;
537
538 DefaultStaticResidueFunction()
539 : number_of_dof(0),
540 number_of_non_lagrang_dof(0),
541 model(nullptr),
542 domain(nullptr),
543 mrbc(0),
544 ebc(0),
545 nbc(0),
546 constraint_has_penalty(false),
547 penalty_factor(0),
548 lagrange_mult_scale(0),
549 autospc(false),
550 rcfo_only_spc(false),
551 elements_linear(false) {}
552
553 void init(Model& model_, const AttributeList& attribute) {
554 model = &model_;
555 domain = &model->get_domain();
556 ebc = &dynamic_cast<ebc_t&>(model->get_essential_boundary_condition(ebc_t::type.get_subtype(
557 "TypedEssentialBoundaryConditionListComponent" + type_name<T>())));
558 mrbc = &dynamic_cast<mrbc_t&>(
559 model->get_model_reduction_boundary_condition(mrbc_t::type.get_subtype(
560 "TypedModelReductionBoundaryConditionListComponent" + type_name<T>())));
561 nbc = &dynamic_cast<nbc_t&>(model->get_natural_boundary_condition(nbc_t::type.get_subtype(
562 "TypedNaturalBoundaryConditionListComponent"
563 + type_name<T, typename MATRIX_FORMAT::sparse_inversible>())));
564 nbc_sol.resize(domain->get_number_of_dof());
565 fi.resize(domain->get_number_of_dof());
566
567 constraint_has_penalty =
568 attribute.get_string("CONSTRAINT_METHOD", "REDUCTION") == "PENALTY";
569
570 constraint_has_penalty |=
571 attribute.get_string("NONLINEAR_CONSTRAINT_METHOD", "OTHER") == "PENALTY";
572
573 number_of_dof = number_of_non_lagrang_dof =
574 mrbc->get_size(false, b2linalg::Vector<T, b2linalg::Vdense>::null, 1);
575 if (constraint_has_penalty) {
576 penalty_factor = attribute.get_double("CONSTRAINT_PENALTY", 1e10);
577 } else {
578 if (attribute.get_bool("REMOVE_DEPENDENT_CONSTRAINTS", false)) {
579 // Calculate the linear reduction matrix.
580 b2linalg::Matrix<T, b2linalg::Mcompressed_col> trans_d_r_d_dof_lin;
581 DefaultStaticResidueFunction<T, MATRIX_FORMAT>::mrbc->get_linear_value(
582 b2linalg::Vector<T, b2linalg::Vdense>::null, trans_d_r_d_dof_lin);
583
584 // Calculate the constraint equations.
585 b2linalg::Matrix<T, b2linalg::Mcompressed_col> trans_d_l_d_dof;
586 r.resize(domain->get_number_of_dof());
587 ebc->get_nonlinear_value(
588 r, 0, false, b2linalg::Vector<T, b2linalg::Vdense_ref>::null, trans_d_l_d_dof,
589 b2linalg::Vector<T, b2linalg::Vdense_ref>::null, 0);
590
591 // Reduce them and get the dependent columns.
592 b2linalg::Matrix<T, b2linalg::Mcompressed_col> trans_d_l_d_dof_red;
593 trans_d_l_d_dof_red = trans_d_r_d_dof_lin * trans_d_l_d_dof;
594
595 get_dependent_columns(trans_d_l_d_dof_red, ebc_to_remove);
596 // trans_d_l_d_dof_red.get_dep_columns(ebc_to_remove, 3e-13, 1.5);
597 if (!ebc_to_remove.empty()) {
598 StaticResidueFunction<T, MATRIX_FORMAT>::logger
599 << logging::info << "Remove " << ebc_to_remove.size()
600 << " dependent constraints." << logging::LOGFLUSH;
601 }
602 }
603 number_of_dof += ebc->get_size(false) - ebc_to_remove.size();
604 penalty_factor = attribute.get_double("CONSTRAINT_PENALTY", 1);
605 if (attribute.get_string("NONLINEAR_CONSTRAINT_METHOD", "OTHER") == "LAGRANGE") {
606 penalty_factor = 0;
607 }
608 lagrange_mult_scale = attribute.get_double("LAGRANGE_MULT_SCALE_PENALTY", 1.0);
609 }
610 autospc = attribute.get_bool("AUTOSPC", false);
611
612 rcfo_only_spc = attribute.get_bool("RCFO_ONLY_SPC", false);
613 if (rcfo_only_spc
614 && attribute.get_string("CONSTRAINT_METHOD", "REDUCTION") == "REDUCTION") {
615 Exception() << "This is not possible to compute reaction force "
616 "only due to the single point constraint with the reduction "
617 "method. Use Lagrange, augmented Lagrange or penalty method "
618 "instead."
619 << THROW;
620 }
621 elements_linear = attribute.get_bool("ELEMENTS_LINEAR", false);
622 }
623
624 Model& get_model() { return *model; }
625
626 size_t get_number_of_dof() { return number_of_dof; }
627
628 size_t get_number_of_non_lagrange_dof() { return number_of_non_lagrang_dof; }
629
630 void get_solution(
631 const b2linalg::Vector<T, b2linalg::Vdense_constref> dof, const double time,
632 EquilibriumSolution equilibrium_solution,
633 b2linalg::Vector<T, b2linalg::Vdense_ref> dof_sol) {
634 mrbc_get_nonlinear_value(
635 dof(b2linalg::Interval(
636 0, mrbc->get_size(false, b2linalg::Vector<T, b2linalg::Vdense>::null, 1))),
637 time, equilibrium_solution, dof_sol,
638 b2linalg::Matrix<T, b2linalg::Mcompressed_col>::null,
639 b2linalg::Vector<T, b2linalg::Vdense_ref>::null, 0);
640 }
641
642 const b2linalg::Vector<T, b2linalg::Vdense_constref> get_residuum_sol(
643 const b2linalg::Vector<T, b2linalg::Vdense_constref>& dof, const double time) {
644 if (rcfo_only_spc) {
645 // recompute fi using only the spc contribution
646
647 b2linalg::Matrix<T, b2linalg::Mcompressed_col> trans_d_l_d_dof;
648 b2linalg::Vector<T> l(ebc->get_size(false));
649 ebc->get_nonlinear_value(
650 r, time, false, l, trans_d_l_d_dof,
651 b2linalg::Vector<T, b2linalg::Vdense_ref>::null, 0);
652 if (!ebc_to_remove.empty()) {
653 l.remove(ebc_to_remove);
654 trans_d_l_d_dof.remove_col(ebc_to_remove);
655 }
656
657 if (constraint_has_penalty) {
658 l *= -penalty_factor;
659 } else {
660 b2linalg::Interval lagrange_mult_range(
661 mrbc->get_size(false, b2linalg::Vector<T, b2linalg::Vdense>::null, 1),
662 number_of_dof);
663 if (MATRIX_FORMAT::symmetric && penalty_factor != 0) {
664 // Apply a penalty method with a small factor
665 // to obtain a definite positive matrix.
666 l *= -penalty_factor;
667 l += -lagrange_mult_scale * dof(lagrange_mult_range);
668 } else {
669 l = -lagrange_mult_scale * dof(lagrange_mult_range);
670 }
671 }
672 for (size_t j = 0; j != trans_d_l_d_dof.size2(); ++j) {
673 if (trans_d_l_d_dof.get_nb_nonzero(j) != 1) { l[j] = 0; }
674 }
675 fi = trans_d_l_d_dof * l;
676 }
677 return fi;
678 }
679
680 const b2linalg::Vector<T, b2linalg::Vdense_constref> get_nbc_sol() { return nbc_sol; }
681
682 T get_residue_normalization() { return std::max(fi * fi, nbc_sol * nbc_sol); }
683
684 double get_energy_normalisation1() {
685 double res = 0;
686 for (size_t i = 0; i != r.size(); ++i) {
687 res += std::max(norm(r[i] * nbc_sol[i]), norm(r[i] * fi[i]));
688 }
689 return res;
690 }
691
692 double get_energy() {
693 double res = 0;
694 for (size_t i = 0; i != r.size(); ++i) { res += r[i] * (fi[i] + nbc_sol[i]); }
695 return res;
696 }
697
698 double get_energy_increment(const b2linalg::Vector<T>& dof_increment) {
699 double res = 0;
700 for (size_t i = 0; i != dof_increment.size(); ++i) {
701 res += dof_increment[i] * (fi[i] + nbc_sol[i]);
702 }
703 return res;
704 }
705
706 const b2linalg::Vector<T, b2linalg::Vindex1_constref> get_matrix_diagonal(
707 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible>& d_residue_d_dof) {
708 b2linalg::Vector<T, b2linalg::Vindex1_constref> res = d_residue_d_dof.get_diagonal();
709 res.set_default_value(lagrange_mult_scale);
710 return res;
711 }
712
713 void get_residue(
714 const b2linalg::Vector<T, b2linalg::Vdense_constref>& dof, const double time,
715 const double delta_time, EquilibriumSolution& equilibrium_solution,
716 b2linalg::Vector<T, b2linalg::Vdense_ref> residue,
717 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible>& d_residue_d_dof,
718 b2linalg::Vector<T, b2linalg::Vdense_ref> d_residue_d_time, SolverHints* solver_hints,
719 CorrectionTerminationTest<T>* termination_test, GradientContainer* gradient_container) {
720 // DefaultStaticResidueFunction<T, MATRIX_FORMAT>::logger <<
721 // logging::debug << "Default::get_residue" <<
722 // logging::LOGFLUSH;
723 b2linalg::Interval disp_range(
724 0, mrbc->get_size(false, b2linalg::Vector<T, b2linalg::Vdense>::null, 1));
725 b2linalg::Interval lagrange_mult_range(
726 mrbc->get_size(false, b2linalg::Vector<T, b2linalg::Vdense>::null, 1), number_of_dof);
727
728 if (!d_residue_d_time.is_null()) { d_residue_d_dof.set_zero_same_struct(); }
729
730 r.resize(domain->get_number_of_dof());
731 b2linalg::Matrix<T, b2linalg::Mcompressed_col> trans_d_r_d_dof;
732 b2linalg::Vector<T, b2linalg::Vdense> d_r_d_time(domain->get_number_of_dof());
733 mrbc_get_nonlinear_value(
734 dof(disp_range), time, equilibrium_solution, r, trans_d_r_d_dof, d_r_d_time,
735 solver_hints);
736 // DefaultStaticResidueFunction<T, MATRIX_FORMAT>::logger <<
737 // logging::debug << "Default::after mrbc_get_nonlinear_value" <<
738 // logging::LOGFLUSH;
739
740 if (residue.is_null() && d_residue_d_dof.is_null() && d_residue_d_time.is_null()) {
741 solver_utile.get_elements_values(
742 b2linalg::Matrix<T, b2linalg::Mrectangle_constref>(r), time, delta_time,
743 equilibrium_solution, elements_linear, trans_d_r_d_dof, d_r_d_time,
744 b2linalg::Vector<double, b2linalg::Vdense_constref>::null, *model,
745 b2linalg::Vector<T, b2linalg::Vdense_ref>::null, d_residue_d_dof,
746 b2linalg::Vector<T, b2linalg::Vdense_ref>::null, gradient_container, solver_hints,
747 StaticResidueFunction<T, MATRIX_FORMAT>::logger, termination_test);
748 nbc->get_nonlinear_value(
749 r, time, equilibrium_solution, b2linalg::Vector<T, b2linalg::Vdense_ref>::null,
750 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible>::null,
751 b2linalg::Vector<T, b2linalg::Vdense_ref>::null, solver_hints);
752 return;
753 }
754
755 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible> d_fe_d_dof;
756 b2linalg::Vector<T, b2linalg::Vdense> d_fe_d_time(domain->get_number_of_dof());
757 nbc_sol.set_zero();
758 nbc->get_nonlinear_value(
759 r, time, equilibrium_solution,
760 (residue.is_null() ? residue : b2linalg::Vector<T, b2linalg::Vdense_ref>(nbc_sol)),
761 d_residue_d_dof.is_null()
762 ? b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible>::null
763 : d_fe_d_dof,
764 (d_residue_d_time.is_null() ? d_residue_d_time
765 : b2linalg::Vector<T, b2linalg::Vdense_ref>(d_fe_d_time)),
766 solver_hints);
767 if (termination_test) { termination_test->add_flux(nbc_sol); }
768 fi = -nbc_sol;
769 d_fe_d_time = -d_fe_d_time;
770 if (!d_residue_d_dof.is_null() && d_fe_d_dof.size1() != 0) {
771 d_residue_d_dof += -(trans_d_r_d_dof * d_fe_d_dof * transposed(trans_d_r_d_dof));
772 }
773 // DefaultStaticResidueFunction<T, MATRIX_FORMAT>::logger <<
774 // logging::debug << "Default::after nbc_get_nonlinear_value" <<
775 // logging::LOGFLUSH;
776
777 b2linalg::Matrix<T, b2linalg::Mcompressed_col> trans_d_l_d_dof;
778 b2linalg::Vector<T> l;
779 b2linalg::Vector<T> d_l_d_time;
780
781 if (constraint_has_penalty) {
782 if (!residue.is_null()) { l.resize(ebc->get_size(false)); }
783 if (!d_residue_d_time.is_null()) { d_l_d_time.resize(ebc->get_size(false)); }
784 ebc->get_nonlinear_value(
785 r, time, equilibrium_solution,
786 (residue.is_null() ? b2linalg::Vector<T>::null : l), trans_d_l_d_dof,
787 (d_residue_d_time.is_null()
788 ? b2linalg::Vector<T, b2linalg::Vdense_ref>::null
789 : b2linalg::Vector<T, b2linalg::Vdense_ref>(d_l_d_time)),
790 solver_hints);
791 } else {
792 const size_t num_lm_full = lagrange_mult_range.size() + ebc_to_remove.size();
793 assert(ebc->get_size(false) == num_lm_full);
794 // Vector<T> residue_lm_tmp(num_lm_full);
795 b2linalg::Vector<T> residue_lm_tmp;
796 if (!residue.is_null()) { residue_lm_tmp.resize(num_lm_full); }
797
798 // Vector<T> d_residue_d_time_lm_tmp(num_lm_full);
799 b2linalg::Vector<T> d_residue_d_time_lm_tmp;
800 if (!d_residue_d_time.is_null()) { d_residue_d_time_lm_tmp.resize(num_lm_full); }
801
802 ebc->get_nonlinear_value(
803 r, time, equilibrium_solution,
804 // Vector<T, Vdense_ref>(residue_lm_tmp),
805 (residue.is_null() ? b2linalg::Vector<T, b2linalg::Vdense_ref>::null
806 : b2linalg::Vector<T, b2linalg::Vdense_ref>(residue_lm_tmp)),
807 trans_d_l_d_dof,
808 // Vector<T, Vdense_ref>(d_residue_d_time_lm_tmp),
809 (d_residue_d_time.is_null()
810 ? b2linalg::Vector<T, b2linalg::Vdense_ref>::null
811 : b2linalg::Vector<T, b2linalg::Vdense_ref>(d_residue_d_time_lm_tmp)),
812 solver_hints);
813 if (!residue.is_null()) {
814 if (!ebc_to_remove.empty()) { residue_lm_tmp.remove(ebc_to_remove); }
815 residue(lagrange_mult_range) = residue_lm_tmp;
816 }
817 if (!ebc_to_remove.empty()) { trans_d_l_d_dof.remove_col(ebc_to_remove); }
818 if (!d_residue_d_time.is_null()) {
819 if (!ebc_to_remove.empty()) { d_residue_d_time_lm_tmp.remove(ebc_to_remove); }
820 d_residue_d_time(lagrange_mult_range) = d_residue_d_time_lm_tmp;
821 d_residue_d_time(lagrange_mult_range) += transposed(trans_d_l_d_dof) * d_r_d_time;
822 }
823 }
824
825 if (!d_residue_d_time.is_null() && d_fe_d_dof.size1()) {
826 d_fe_d_time -= d_fe_d_dof * d_r_d_time;
827 }
828
829 // DefaultStaticResidueFunction<T, MATRIX_FORMAT>::logger <<
830 // logging::debug << "Default::after ebc_get_nonlinear_value" <<
831 // logging::LOGFLUSH;
832
833 StaticResidueFunction<T, MATRIX_FORMAT>::logger
834 << logging::debug << "Element assembly ("
835 << (d_residue_d_dof.is_null() ? "first" : "second") << " variation)"
836 << logging::LOGFLUSH;
837 solver_utile.get_elements_values(
838 b2linalg::Matrix<T, b2linalg::Mrectangle_constref>(r), time, delta_time,
839 equilibrium_solution, elements_linear, trans_d_r_d_dof, d_r_d_time,
840 b2linalg::Vector<double, b2linalg::Vdense_constref>::null, *model,
841 (residue.is_null() ? b2linalg::Vector<T, b2linalg::Vdense_ref>::null
842 : b2linalg::Vector<T, b2linalg::Vdense_ref>(fi)),
843 d_residue_d_dof,
844 (d_residue_d_time.is_null() ? b2linalg::Vector<T, b2linalg::Vdense_ref>::null
845 : b2linalg::Vector<T, b2linalg::Vdense_ref>(d_fe_d_time)),
846 gradient_container, solver_hints, StaticResidueFunction<T, MATRIX_FORMAT>::logger,
847 termination_test);
848 if (equilibrium_solution.input_level == 0) { equilibrium_solution.input_level = 1; }
849
850 if (StaticResidueFunction<T, MATRIX_FORMAT>::logger.is_enabled_for(logging::data)) {
851 StaticResidueFunction<T, MATRIX_FORMAT>::logger << logging::data << "dof "
852 << logging::DBName("DOF.GLOB") << r
853 << logging::LOGFLUSH;
854 if (!residue.is_null()) {
855 StaticResidueFunction<T, MATRIX_FORMAT>::logger
856 << logging::data << "value " << logging::DBName("VALUE.GLOB") << fi
857 << logging::LOGFLUSH;
858 }
859 if (!d_residue_d_dof.is_null()) {
860 StaticResidueFunction<T, MATRIX_FORMAT>::logger
861 << logging::data << "d_value_d_dof " << logging::DBName("D_VALUE_D_DOF.GLOB")
862 << d_residue_d_dof << logging::LOGFLUSH;
863 }
864 }
865
866 if (!residue.is_null()) {
867 b2linalg::Vector<T, b2linalg::Vdense> tmp;
868 tmp = fi;
869 if (constraint_has_penalty) {
870 tmp += (trans_d_l_d_dof * l) * penalty_factor;
871 } else {
872 if (MATRIX_FORMAT::symmetric && penalty_factor != 0) {
873 // Apply a penalty method with a small factor to
874 // obtain a definite positive matrix.
875 b2linalg::Vector<T> tmp1;
876 tmp1 = residue(lagrange_mult_range) * penalty_factor;
877 tmp1 += dof(lagrange_mult_range) * lagrange_mult_scale;
878 tmp += trans_d_l_d_dof * tmp1;
879 } else {
880 tmp += lagrange_mult_scale * (trans_d_l_d_dof * dof(lagrange_mult_range));
881 }
882 residue(lagrange_mult_range) *= lagrange_mult_scale;
883 }
884 residue(disp_range) = trans_d_r_d_dof * tmp;
885 if (StaticResidueFunction<T, MATRIX_FORMAT>::logger.is_enabled_for(logging::data)) {
886 // for (size_t i = 0; i != tmp.size(); ++i)
887 // tmp[i] *= dof[i];
888 dynamic_cast<SolutionStaticNonlinear<T>&>(model->get_solution())
889 .set_equilibrium(time, tmp);
890 }
891 }
892
893 b2linalg::Matrix<T, b2linalg::Mcompressed_col> trans_LR;
894 trans_LR = trans_d_r_d_dof * trans_d_l_d_dof;
895 if (!d_residue_d_dof.is_null()) {
896 if (penalty_factor != 0 && (constraint_has_penalty || MATRIX_FORMAT::symmetric)) {
897 // Apply a penalty method with a small factor to
898 // obtain a definite positive matrix when Lagrange
899 // multipliers method is used.
900 d_residue_d_dof(disp_range, disp_range) +=
901 (trans_LR * b2linalg::transposed(trans_LR)) * penalty_factor;
902 }
903 if (!constraint_has_penalty) {
904 trans_LR *= lagrange_mult_scale;
905 b2linalg::Matrix<T, b2linalg::Mcompressed_col> LR = b2linalg::transposed(trans_LR);
906 d_residue_d_dof(lagrange_mult_range, disp_range) += LR;
907 if (!MATRIX_FORMAT::symmetric) {
908 d_residue_d_dof(disp_range, lagrange_mult_range) += trans_LR;
909 }
910 }
911
912 //
913 // Check matrix for structural singularities. Fix them if
914 // autospc is active.
915 //
916 if (1) {
917 std::set<size_t> zero_columns_r;
918 for (size_t i = 0; i != disp_range.get_end() /*d_residue_d_dof.size1()*/; ++i) {
919 if (d_residue_d_dof(i, i) == T(0)) {
920 bool all_zero = true;
921 for (size_t j = 0; all_zero && j != d_residue_d_dof.size2(); ++j) {
922 if (d_residue_d_dof(i, j) != T(0)) { all_zero = false; }
923 }
924 if (all_zero) {
925 zero_columns_r.insert(i);
926 if (autospc) { d_residue_d_dof(i, i) += T(1); }
927 }
928 }
929 }
930
931 if (!zero_columns_r.empty()) {
932 b2linalg::Matrix<T, b2linalg::Mrectangle> nks_r;
933 nks_r.resize(d_residue_d_dof.size1(), 1);
934 nks_r.set_zero();
935 for (auto i : zero_columns_r) { nks_r(i, 0) = T(1); }
936 b2linalg::Matrix<T, b2linalg::Mrectangle> nks(
937 model->get_domain().get_number_of_dof(), nks_r.size2());
938 for (size_t i = 0; i != nks_r.size2(); ++i) {
939 get_solution(nks_r[i], time, equilibrium_solution, nks[i]);
940 }
941 for (size_t i = 0; i != nks.size1(); ++i) {
942 nks(i, 0) = (nks(i, 0) != T(0) ? T(1) : T(0));
943 }
944 model->get_solution().set_system_null_space(nks, false, "NS_STRUCTURE");
945 }
946 }
947 }
948
949 if (!d_residue_d_time.is_null()) {
950 d_residue_d_time(disp_range) = trans_d_r_d_dof * d_fe_d_time;
951 if (constraint_has_penalty) {
952 d_residue_d_time(disp_range) +=
953 penalty_factor * (trans_LR /*trans_d_l_d_dof*/ * d_l_d_time);
954 } else {
955 d_residue_d_time(lagrange_mult_range) *= lagrange_mult_scale;
956 }
957 }
958 }
959
960 void mrbc_get_nonlinear_value(
961 const b2linalg::Vector<T, b2linalg::Vdense_constref>& dof, double time,
962 EquilibriumSolution equilibrium_solution, b2linalg::Vector<T, b2linalg::Vdense_ref> value,
963 b2linalg::Matrix<T, b2linalg::Mcompressed_col>& d_value_d_dof_trans,
964 b2linalg::Vector<T, b2linalg::Vdense_ref> d_value_d_time, SolverHints* solver_hints) {
965 mrbc->get_nonlinear_value(
966 dof, time, equilibrium_solution, value, d_value_d_dof_trans,
967 b2linalg::Matrix<T, b2linalg::Mrectangle_ref>(d_value_d_time), solver_hints);
968 }
969
970 using type_t = ObjectTypeComplete<
971 DefaultStaticResidueFunction, typename StaticResidueFunction<T, MATRIX_FORMAT>::type_t>;
972 static type_t type;
973
974protected:
975 size_t number_of_dof;
976 size_t number_of_non_lagrang_dof;
977 Model* model;
978 Domain* domain;
979 mrbc_t* mrbc;
980 ebc_t* ebc;
981 nbc_t* nbc;
982 b2linalg::Vector<T, b2linalg::Vdense> fi;
983 b2linalg::Vector<T, b2linalg::Vdense> nbc_sol;
984 b2linalg::Vector<T, b2linalg::Vdense> r;
985 b2linalg::Index ebc_to_remove;
986 SolverUtils<T, MATRIX_FORMAT> solver_utile;
987 bool constraint_has_penalty;
988 double penalty_factor;
989 double lagrange_mult_scale;
990 bool autospc;
991 bool rcfo_only_spc;
992 bool elements_linear;
993};
994
995template <typename T, typename MATRIX_FORMAT>
996typename DefaultStaticResidueFunction<T, MATRIX_FORMAT>::type_t
997 DefaultStaticResidueFunction<T, MATRIX_FORMAT>::type(
998 "DefaultStaticResidueFunction", type_name<T, MATRIX_FORMAT>(),
999 StringList("DEFAULT_STATIC_RESIDUE_FUNCTION"), module);
1000
1001template <typename T, typename MATRIX_FORMAT>
1002class ScaledStaticResidueFunction : public DefaultStaticResidueFunction<T, MATRIX_FORMAT> {
1003public:
1004 using base_class = DefaultStaticResidueFunction<T, MATRIX_FORMAT>;
1005
1006 T get_residue_normalization() { return 1; }
1007
1008 void get_residue(
1009 const b2linalg::Vector<T, b2linalg::Vdense_constref>& dof, const double time,
1010 const double delta_time, EquilibriumSolution& equilibrium_solution,
1011 b2linalg::Vector<T, b2linalg::Vdense_ref> residue,
1012 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible>& d_residue_d_dof,
1013 b2linalg::Vector<T, b2linalg::Vdense_ref> d_residue_d_time, SolverHints* solver_hints,
1014 CorrectionTerminationTest<T>* termination_test, GradientContainer* gradient_container) {
1015 if (scale_vector.empty()) {
1016 if (d_residue_d_dof.is_null() || d_residue_d_time.is_null()) {
1018 }
1019 scale_vector.reserve(d_residue_d_dof.size1());
1020 for (size_t i = 0; i != d_residue_d_dof.size1(); ++i) { scale_vector.push_back(1); }
1021 b2linalg::Vector<T, b2linalg::Vdense> dof_tmp(dof.size());
1022 base_class::get_residue(
1023 dof_tmp, time, delta_time, equilibrium_solution, residue, d_residue_d_dof,
1024 d_residue_d_time, solver_hints, termination_test, gradient_container);
1025 for (size_t i = 0; i != d_residue_d_dof.size1(); ++i) {
1026 scale_vector[i] = 1 / std::sqrt(std::abs(d_residue_d_dof(i, i)));
1027 }
1028 if (d_residue_d_time.is_null()) { UnimplementedError() << THROW; }
1029 d_residue_d_time = inverse(d_residue_d_dof) * d_residue_d_time;
1030 T t = 0;
1031 for (size_t i = 0; i != d_residue_d_time.size(); ++i) {
1032 const T tt = d_residue_d_time[i] / scale_vector[i];
1033 t += tt * tt;
1034 }
1035 scale_vector *= std::sqrt(t);
1036 }
1037
1038 base_class::get_residue(
1039 dof, time, delta_time, equilibrium_solution, residue, d_residue_d_dof,
1040 d_residue_d_time, solver_hints, termination_test, gradient_container);
1041 }
1042
1043 void mrbc_get_nonlinear_value(
1044 const b2linalg::Vector<T, b2linalg::Vdense_constref>& dof, double time,
1045 EquilibriumSolution equilibrium_solution, b2linalg::Vector<T, b2linalg::Vdense_ref> value,
1046 b2linalg::Matrix<T, b2linalg::Mcompressed_col>& d_value_d_dof_trans,
1047 b2linalg::Vector<T, b2linalg::Vdense_ref> d_value_d_time, SolverHints* solver_hints) {
1048 b2linalg::Vector<T, b2linalg::Vdense> dof_tmp;
1049 dof_tmp = dof;
1050 dof_tmp.scale(scale_vector);
1051 base_class::mrbc->get_nonlinear_value(
1052 dof_tmp, time, equilibrium_solution, value, d_value_d_dof_trans,
1053 b2linalg::Matrix<T, b2linalg::Mrectangle_ref>(d_value_d_time), solver_hints);
1054 if (!d_value_d_dof_trans.is_null()) { d_value_d_dof_trans.scale_row(scale_vector); }
1055 }
1056
1057 using type_t = ObjectTypeComplete<
1058 ScaledStaticResidueFunction,
1059 typename DefaultStaticResidueFunction<T, MATRIX_FORMAT>::type_t>;
1060 static type_t type;
1061
1062private:
1063 b2linalg::Vector<T, b2linalg::Vdense> scale_vector;
1064};
1065
1066template <typename T, typename MATRIX_FORMAT>
1067typename ScaledStaticResidueFunction<T, MATRIX_FORMAT>::type_t
1068 ScaledStaticResidueFunction<T, MATRIX_FORMAT>::type(
1069 "ScaledStaticResidueFunction", type_name<T, MATRIX_FORMAT>(),
1070 StringList("SCALED_STATIC_RESIDUE_FUNCTION"), module);
1071
1072template <typename T, typename MATRIX_FORMAT>
1073class ArtificialDampingStaticResidueFunction
1074 : public DefaultStaticResidueFunction<T, MATRIX_FORMAT> {
1075public:
1076 ArtificialDampingStaticResidueFunction()
1077 : DefaultStaticResidueFunction<T, MATRIX_FORMAT>(),
1078 rayleigh_damping_alpha(0.0),
1079 rayleigh_damping_beta(0.0),
1080 dissipated_energy_fraction(0.0),
1081 sfactor(0.0),
1082 old_time(0.0) {}
1083
1084 void init(Model& model, const AttributeList& attribute) {
1085 DefaultStaticResidueFunction<T, MATRIX_FORMAT>::init(model, attribute);
1086
1087 const char* alpha_s = "RAYLEIGH_ALPHA";
1088 const char* beta_s = "RAYLEIGH_BETA";
1089 const char* def_s = "DISSIPATED_ENERGY_FRACTION";
1090
1091 if (attribute.has_key(alpha_s) && attribute.has_key(beta_s)) {
1092 rayleigh_damping_alpha = attribute.get_double(alpha_s);
1093 rayleigh_damping_beta = attribute.get_double(beta_s);
1094 sfactor = 1;
1095 } else {
1096 rayleigh_damping_alpha = 1;
1097 rayleigh_damping_beta = 0;
1098 dissipated_energy_fraction = attribute.get_double(def_s, 1.e-4);
1099 // We do not recompute the sfactor between the stages
1100 if (d_residue_d_dofdot.size1() == 0) { sfactor = 0; }
1101 }
1102 old_dof.clear();
1103 }
1104
1105 void get_residue(
1106 const b2linalg::Vector<T, b2linalg::Vdense_constref>& dof, const double time,
1107 const double delta_time, EquilibriumSolution& equilibrium_solution,
1108 b2linalg::Vector<T, b2linalg::Vdense_ref> residue,
1109 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible>& d_residue_d_dof,
1110 b2linalg::Vector<T, b2linalg::Vdense_ref> d_residue_d_time, SolverHints* solver_hints,
1111 CorrectionTerminationTest<T>* termination_test, GradientContainer* gradient_container) {
1112 EquilibriumSolution equilibrium_solution_input = equilibrium_solution;
1113 // DefaultStaticResidueFunction<T, MATRIX_FORMAT>::logger <<
1114 // logging::debug << "ArtificialDamping::get_residue" <<
1115 // logging::LOGFLUSH;
1116 if (d_residue_d_dofdot.size1()
1117 != DefaultStaticResidueFunction<T, MATRIX_FORMAT>::number_of_dof
1118 && sfactor == 1) {
1119 compute_damping_matrix(b2linalg::Vector<T, b2linalg::Vdense>::null);
1120 }
1121 DefaultStaticResidueFunction<T, MATRIX_FORMAT>::get_residue(
1122 dof, time, delta_time, equilibrium_solution, residue, d_residue_d_dof,
1123 d_residue_d_time, solver_hints, termination_test, gradient_container);
1124 if (old_dof.size() == 0) {
1125 old_dof = dof;
1126 old_time = time;
1127 return;
1128 }
1129 if (delta_time <= 0) {
1130 Exception() << "Artificial damping can only be used with a load-controlled constraint "
1131 "strategy (e.g. increment_control_type=load)."
1132 << THROW;
1133 }
1134 const double i_delta_time = 1.0 / delta_time;
1135
1136 b2linalg::Vector<T, b2linalg::Vdense> tmp;
1137 if (d_residue_d_dofdot.size1()
1138 == DefaultStaticResidueFunction<T, MATRIX_FORMAT>::number_of_dof
1139 && (!residue.is_null() || !d_residue_d_time.is_null())
1140 && d_residue_d_dofdot.size1() != 0) {
1141 b2linalg::Vector<T, b2linalg::Vdense> delta_dof;
1142 delta_dof = dof - old_dof;
1143 tmp = sfactor * i_delta_time * (d_residue_d_dofdot * delta_dof);
1144 }
1145 if (d_residue_d_dofdot.size1()
1146 == DefaultStaticResidueFunction<T, MATRIX_FORMAT>::number_of_dof
1147 && !residue.is_null() && !tmp.empty()) {
1148 residue += tmp;
1149 }
1150 if (!d_residue_d_dof.is_null()
1151 && d_residue_d_dofdot.size1()
1152 == DefaultStaticResidueFunction<T, MATRIX_FORMAT>::number_of_dof
1153 && !tmp.empty()) {
1154 d_residue_d_dof += sfactor * i_delta_time * d_residue_d_dofdot;
1155 }
1156 if (d_residue_d_dofdot.size1()
1157 == DefaultStaticResidueFunction<T, MATRIX_FORMAT>::number_of_dof
1158 && !d_residue_d_time.is_null() && !tmp.empty()) {
1159 d_residue_d_time += i_delta_time * tmp;
1160 }
1161 if (equilibrium_solution_input && time > old_time) {
1162 if (d_residue_d_dofdot.size1() == 0) {
1163 b2linalg::Vector<T, b2linalg::Vdense> dof_ur(
1164 DefaultStaticResidueFunction<T, MATRIX_FORMAT>::model->get_domain()
1165 .get_number_of_dof());
1166 DefaultStaticResidueFunction<T, MATRIX_FORMAT>::get_solution(
1167 dof, time, equilibrium_solution_input, dof_ur);
1168 b2linalg::Vector<T, b2linalg::Vdense> old_dof_ur(dof_ur.size());
1169 DefaultStaticResidueFunction<T, MATRIX_FORMAT>::get_solution(
1170 old_dof, old_time, equilibrium_solution_input, old_dof_ur);
1171 dof_ur -= old_dof_ur;
1172 const double s1 = DefaultStaticResidueFunction<T, MATRIX_FORMAT>::get_energy();
1173 const double s2 = compute_damping_matrix(dof_ur) / delta_time;
1174 sfactor = dissipated_energy_fraction * s1 / s2;
1175 DefaultStaticResidueFunction<T, MATRIX_FORMAT>::logger
1176 << logging::debug << "scale factor for the stabilised method " << sfactor
1177 << logging::LOGFLUSH;
1178 }
1179 {
1180 b2linalg::Vector<T, b2linalg::Vdense> delta_dof;
1181 delta_dof = dof - old_dof;
1182 b2linalg::Vector<T> tmp;
1183 tmp = sfactor * i_delta_time * (d_residue_d_dofdot * delta_dof);
1184 const double dissipated_energy = norm(tmp * delta_dof);
1185 double energy_increment;
1186 {
1187 b2linalg::Vector<T, b2linalg::Vdense> dof_ur(
1188 DefaultStaticResidueFunction<T, MATRIX_FORMAT>::model->get_domain()
1189 .get_number_of_dof());
1190 DefaultStaticResidueFunction<T, MATRIX_FORMAT>::get_solution(
1191 dof, time, equilibrium_solution_input, dof_ur);
1192 b2linalg::Vector<T, b2linalg::Vdense> old_dof_ur(dof_ur.size());
1193 DefaultStaticResidueFunction<T, MATRIX_FORMAT>::get_solution(
1194 old_dof, old_time, equilibrium_solution_input, old_dof_ur);
1195 dof_ur -= old_dof_ur;
1196 energy_increment =
1197 DefaultStaticResidueFunction<T, MATRIX_FORMAT>::get_energy_increment(
1198 dof_ur);
1199 }
1200 DefaultStaticResidueFunction<T, MATRIX_FORMAT>::logger
1201 << logging::debug << "dissipated energy in the step = " << dissipated_energy
1202 << ", by increment unit = " << dissipated_energy * i_delta_time
1203 << ", energy increment = " << energy_increment << "." << logging::LOGFLUSH;
1204 Solution& solution =
1205 DefaultStaticResidueFunction<T, MATRIX_FORMAT>::model->get_solution();
1206 solution.set_value("AD_DISSIPATED_ENERGY_IN_STEP", dissipated_energy);
1207 solution.set_value("MODEL_ENERGY_INCREMENT", energy_increment);
1208 }
1209 old_time = time;
1210 old_dof = dof;
1211 }
1212 }
1213
1214 using type_t = ObjectTypeComplete<
1215 ArtificialDampingStaticResidueFunction,
1216 typename StaticResidueFunction<T, MATRIX_FORMAT>::type_t>;
1217 static type_t type;
1218
1219private:
1220 double rayleigh_damping_alpha;
1221 double rayleigh_damping_beta;
1222 double dissipated_energy_fraction;
1223 double sfactor;
1224 double old_time;
1225 b2linalg::Vector<T, b2linalg::Vdense> old_dof;
1226 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible> d_residue_d_dofdot;
1227
1228 double compute_damping_matrix(b2linalg::Vector<T, b2linalg::Vdense>& dof) {
1229 b2linalg::Matrix<T, b2linalg::Mcompressed_col> trans_d_r_d_dof;
1230 DefaultStaticResidueFunction<T, MATRIX_FORMAT>::mrbc->get_linear_value(
1231 b2linalg::Vector<T, b2linalg::Vdense>::null, trans_d_r_d_dof);
1232 d_residue_d_dofdot.resize(DefaultStaticResidueFunction<T, MATRIX_FORMAT>::number_of_dof);
1233 b2linalg::Index index;
1234 std::vector<b2linalg::Matrix<T, typename MATRIX_FORMAT::dense> > d_fe_d_dof_elem(3);
1235 std::vector<bool> d_value_d_dof_flags(3, true);
1236 d_value_d_dof_flags[1] = false;
1237 std::unique_ptr<Domain::ElementIterator> i =
1238 DefaultStaticResidueFunction<T, MATRIX_FORMAT>::domain->get_element_iterator();
1239 DefaultStaticResidueFunction<T, MATRIX_FORMAT>::domain->set_dof(
1240 b2linalg::Vector<double, b2linalg::Vdense_constref>::null);
1241 b2linalg::Vector<T, b2linalg::Vdense> dof_res(dof.size());
1242 double norm_inf_mass_matrix = 0;
1243 for (Element* e = i->next(); e != nullptr; e = i->next()) {
1244 if (auto te = dynamic_cast<TypedElement<T>*>(e); te != nullptr) {
1245 te->get_value(
1246 *DefaultStaticResidueFunction<T, MATRIX_FORMAT>::model,
1247 false, // linear
1248 true, // equilibrium_solution
1249 0, // time
1250 0, // delta_time
1251 b2linalg::Matrix<T, b2linalg::Mrectangle_constref>::null,
1252 0, // gradient_container
1253 0, // solver_hints
1254 index, b2linalg::Vector<T, b2linalg::Vdense>::null, d_value_d_dof_flags,
1255 d_fe_d_dof_elem, b2linalg::Vector<T, b2linalg::Vdense>::null);
1256 }
1257 norm_inf_mass_matrix += d_fe_d_dof_elem[2].norm_inf();
1258 d_fe_d_dof_elem[0] *= T(rayleigh_damping_beta);
1259 d_fe_d_dof_elem[0] += T(rayleigh_damping_alpha) * d_fe_d_dof_elem[2];
1260 if (!dof.is_null()) { dof_res(index) += d_fe_d_dof_elem[0] * dof(index); }
1261 d_residue_d_dofdot +=
1262 trans_d_r_d_dof
1263 * StructuredMatrix(trans_d_r_d_dof.size2(), index, d_fe_d_dof_elem[0])
1264 * transposed(trans_d_r_d_dof);
1265 }
1266 if (norm_inf_mass_matrix < 1e-15 && rayleigh_damping_alpha != 0) {
1267 Exception() << "Artificial damping on a zero mass model." << THROW;
1268 }
1269 return dof * dof_res;
1270 }
1271};
1272
1273template <typename T, typename MATRIX_FORMAT>
1274typename ArtificialDampingStaticResidueFunction<T, MATRIX_FORMAT>::type_t
1275 ArtificialDampingStaticResidueFunction<T, MATRIX_FORMAT>::type(
1276 "ArtificialDampingStaticResidueFunction", type_name<T, MATRIX_FORMAT>(),
1277 StringList("ARTIFICIAL_DAMPING_STATIC_RESIDUE_FUNCTION"), module);
1278
1279template <typename T, typename MATRIX_FORMAT>
1280class LoadControlConstraintStrategy : public IncrementConstraintStrategy<T, MATRIX_FORMAT> {
1281public:
1282 LoadControlConstraintStrategy()
1283 : time(0),
1284 step_size(0),
1285 step_size_min(0),
1286 step_size_max(0),
1287 time_at_start_step(0),
1288 nb_iteration(5),
1289 corrector_nb_newton_iteration(0),
1290 refactorization(true) {}
1291
1292 void init(Model& model, const AttributeList& attribute) {
1293 step_size = attribute.get_double("STEP_SIZE_INIT", 0.1);
1294 step_size_min = attribute.get_double("STEP_SIZE_MIN", 1e-12);
1295 step_size_max = attribute.get_double("STEP_SIZE_MAX", 1e100);
1296 step_size_match = attribute.get_double("STEP_SIZE_MATCH", 0);
1297 nb_iteration =
1298 attribute.get_int("NB_ITERATION", attribute.get_int("MAX_NEWTON_ITERATIONS", 50) / 2);
1299 corrector_nb_newton_iteration = attribute.get_double("NB_ITERATION_CORRECTION", 0.5);
1300 refactorization = true;
1301 }
1302
1303 typename IncrementConstraintStrategy<T, MATRIX_FORMAT>::Result set_increment(
1304 StaticResidueFunction<T, MATRIX_FORMAT>& residue_function,
1305 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible_ext>& K,
1306 b2linalg::Vector<T, b2linalg::Vdense_ref> q, b2linalg::Vector<T, b2linalg::Vdense_ref> d,
1307 double& time_, EquilibriumSolution& equilibrium_solution) {
1308 dof_at_start_step = d;
1309 time_at_start_step = time_;
1310 time = time_ + step_size;
1311 if (step_size_match != 0
1312 && int(time_at_start_step / step_size_match) < int(time / step_size_match)) {
1313 time = (int(time_at_start_step / step_size_match) + 1) * step_size_match;
1314 }
1315 if (time > IncrementConstraintStrategy<T, MATRIX_FORMAT>::max_time - step_size_min) {
1316 time = IncrementConstraintStrategy<T, MATRIX_FORMAT>::max_time;
1317 }
1318
1319 Solution& solution = residue_function.get_model().get_solution();
1320 if (refactorization) {
1321 residue_function.get_residue(
1322 d, time_, step_size, equilibrium_solution,
1323 b2linalg::Vector<T, b2linalg::Vdense_ref>::null, K, q, 0);
1324 }
1325
1326 b2linalg::Vector<T, b2linalg::Vdense> v(q.size() + 1);
1327 v[q.size()] = 1;
1328 Model& model = residue_function.get_model();
1329 const ssize_t nbnsv = model.get_case()->get_int("COMPUTE_NULL_SPACE_VECTOR", 0);
1330 b2linalg::Matrix<T, b2linalg::Mrectangle> nks_r;
1331 b2linalg::SingularMatrixError ee;
1332 bool is_singular = false;
1333 try {
1334 if (refactorization) {
1335 IncrementConstraintStrategy<T, MATRIX_FORMAT>::logger
1337 << "Compute prediction and factorize tangent "
1338 "stiffness matrix ("
1339 << K.size1() << " rows and columns)." << logging::LOGFLUSH;
1340 v = update_extension_and_inverse(
1341 K, q, v(b2linalg::Interval(0, q.size())), 1.0, nks_r, nbnsv)
1342 * v;
1343 } else {
1344 v = inverse(K, nks_r, nbnsv) * v;
1345 }
1346 } catch (b2linalg::SingularMatrixError& e) {
1347 ee = e;
1348 is_singular = true;
1349 if (!e.singular_dofs.empty()) {
1350 if (nks_r.size2() == 0) {
1351 nks_r.resize(K.size1(), 1);
1352 nks_r.set_zero();
1353 }
1354 for (auto i : e.singular_dofs) {
1355 assert(i < nks_r.size1());
1356 for (size_t j = 0; j != nks_r.size2(); ++j) { nks_r(i, j) = T(1); }
1357 }
1358 }
1359 }
1360 if (nks_r.size2() != 0) {
1361 b2linalg::Matrix<T, b2linalg::Mrectangle> nks(
1362 model.get_domain().get_number_of_dof(), nks_r.size2());
1363 for (size_t i = 0; i != nks_r.size2(); ++i) {
1364 residue_function.get_solution(nks_r[i], time, equilibrium_solution, nks[i]);
1365 }
1366 solution.set_system_null_space(nks);
1367 }
1368 if (is_singular) { ee << THROW; }
1369
1370 d += (time - time_) * v(b2linalg::Interval(0, q.size()));
1371 time_ = time;
1372 return IncrementConstraintStrategy<T, MATRIX_FORMAT>::in_stage;
1373 }
1374
1375 void get_constraint(
1376 const b2linalg::Vector<T, b2linalg::Vdense>& dof, const double time_, T* constraint,
1377 b2linalg::Vector<T, b2linalg::Vdense>* d_constraint_d_dof, T* d_constraint_d_time) {
1378 if (constraint) { *constraint = time_ - time; }
1379 if (d_constraint_d_dof) { d_constraint_d_dof->set_zero(); }
1380 if (d_constraint_d_time) { *d_constraint_d_time = 1; }
1381 }
1382
1383 typename IncrementConstraintStrategy<T, MATRIX_FORMAT>::Result set_correction_result(
1384 b2linalg::Vector<T, b2linalg::Vdense>& dof, double& time_, bool converged,
1385 int number_of_iteration) {
1386 if (converged) {
1387 if (time > IncrementConstraintStrategy<T, MATRIX_FORMAT>::max_time - 1e-8) {
1388 return IncrementConstraintStrategy<T, MATRIX_FORMAT>::end_of_stage_with_success;
1389 }
1390 const double step_size_scale = std::pow(
1391 corrector_nb_newton_iteration,
1392 double(number_of_iteration - nb_iteration) / (nb_iteration - 1));
1393 step_size *= step_size_scale;
1394 step_size = std::max(step_size, step_size_min);
1395 if (step_size > step_size_max) {
1396 step_size = step_size_max;
1397 refactorization = false;
1398 } else if (step_size_scale > 1) {
1399 refactorization = false;
1400 } else {
1401 refactorization = true;
1402 }
1403 IncrementConstraintStrategy<T, MATRIX_FORMAT>::logger
1404 << logging::debug << "new step size = " << step_size << logging::LOGFLUSH;
1405 return IncrementConstraintStrategy<T, MATRIX_FORMAT>::in_stage;
1406 }
1407 refactorization = true;
1408 step_size *= 0.5;
1409 if (step_size < step_size_min) {
1410 IncrementConstraintStrategy<T, MATRIX_FORMAT>::logger
1411 << logging::error << "Cannot continue, the step size (" << step_size
1412 << ") is below the minimum step size(" << step_size_min << ")."
1413 << logging::LOGFLUSH;
1414 return IncrementConstraintStrategy<T, MATRIX_FORMAT>::end_of_stage_with_error;
1415 }
1416 dof = dof_at_start_step;
1417 time_ = time = time_at_start_step;
1418 IncrementConstraintStrategy<T, MATRIX_FORMAT>::logger
1419 << logging::debug << "new step size = " << step_size << logging::LOGFLUSH;
1420 return IncrementConstraintStrategy<T, MATRIX_FORMAT>::in_stage_and_retry;
1421 }
1422
1423 double get_delta_time() const { return step_size; }
1424
1425 using type_t = ObjectTypeComplete<
1426 LoadControlConstraintStrategy,
1427 typename IncrementConstraintStrategy<T, MATRIX_FORMAT>::type_t>;
1428 static type_t type;
1429
1430private:
1431 double time;
1432 double step_size;
1433 double step_size_min;
1434 double step_size_max;
1435 double step_size_match;
1436 b2linalg::Vector<T, b2linalg::Vdense> dof_at_start_step;
1437 double time_at_start_step;
1438 int nb_iteration;
1439 double corrector_nb_newton_iteration;
1440 bool refactorization;
1441 bool autospc;
1442};
1443
1444template <typename T, typename MATRIX_FORMAT>
1445typename LoadControlConstraintStrategy<T, MATRIX_FORMAT>::type_t
1446 LoadControlConstraintStrategy<T, MATRIX_FORMAT>::type(
1447 "LoadControlConstraintStrategy", type_name<T, MATRIX_FORMAT>(),
1448 StringList("LOAD_CONTROL_CONSTRAINT_STRATEGY"), module);
1449
1450template <typename T, typename MATRIX_FORMAT>
1451class StateControlConstraintStrategy : public IncrementConstraintStrategy<T, MATRIX_FORMAT> {
1452public:
1453 StateControlConstraintStrategy()
1454 : stage_termination_procedure(false),
1455 step_size(0),
1456 step_size_min(0),
1457 step_size_max(0),
1458 time_at_start_step(0),
1459 nb_iteration(0),
1460 corrector_nb_newton_iteration(0) {}
1461
1462 void init(Model& model, const AttributeList& attribute) {
1463 step_size = attribute.get_double("STEP_SIZE_INIT", 0.1);
1464 step_size_min = attribute.get_double("STEP_SIZE_MIN", 1e-12);
1465 step_size_max = attribute.get_double("STEP_SIZE_MAX", 1e100);
1466 nb_iteration =
1467 attribute.get_int("NB_ITERATION", attribute.get_int("MAX_NEWTON_ITERATIONS", 50) / 2);
1468 corrector_nb_newton_iteration = attribute.get_double("NB_ITERATION_CORRECTION", 0.5);
1469 stage_termination_procedure = false;
1470 }
1471
1472 typename IncrementConstraintStrategy<T, MATRIX_FORMAT>::Result set_increment(
1473 StaticResidueFunction<T, MATRIX_FORMAT>& residue_function,
1474 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible_ext>& K,
1475 b2linalg::Vector<T, b2linalg::Vdense_ref> q, b2linalg::Vector<T, b2linalg::Vdense_ref> d,
1476 double& time, EquilibriumSolution& equilibrium_solution) {
1477 dof_at_start_step = d;
1478 time_at_start_step = time;
1479 residue_function.get_residue(
1480 d, time, step_size, equilibrium_solution,
1481 b2linalg::Vector<T, b2linalg::Vdense_ref>::null, K, q, 0);
1482 b2linalg::Vector<T, b2linalg::Vdense> v(q.size() + 1);
1483 v[q.size()] = 1;
1484 Model& model = residue_function.get_model();
1485 const ssize_t nbnsv = model.get_case()->get_int("COMPUTE_NULL_SPACE_VECTOR", 0);
1486 b2linalg::Matrix<T, b2linalg::Mrectangle> nks_r;
1487 try {
1488 IncrementConstraintStrategy<T, MATRIX_FORMAT>::logger
1490 << "Compute prediction and factorize tangent "
1491 "stiffness matrix ("
1492 << K.size1() << " rows and columns)." << logging::LOGFLUSH;
1493 v = update_extension_and_inverse(
1494 K, q, v(b2linalg::Interval(0, q.size())), 1.0, nks_r, nbnsv)
1495 * v;
1496 } catch (b2linalg::SingularMatrixError& e) {
1497 if (nks_r.size2() != 0) {
1498 b2linalg::Matrix<T, b2linalg::Mrectangle> nks(
1499 model.get_domain().get_number_of_dof(), nks_r.size2());
1500 for (size_t i = 0; i != nks_r.size2(); ++i) {
1501 residue_function.get_solution(nks_r[i], time, equilibrium_solution, nks[i]);
1502 }
1503 residue_function.get_model().get_solution().set_system_null_space(nks);
1504 }
1505 throw;
1506 }
1507 if (nks_r.size2() != 0) {
1508 b2linalg::Matrix<T, b2linalg::Mrectangle> nks(
1509 model.get_domain().get_number_of_dof(), nks_r.size2());
1510 for (size_t i = 0; i != nks_r.size2(); ++i) {
1511 residue_function.get_solution(nks_r[i], time, equilibrium_solution, nks[i]);
1512 }
1513 residue_function.get_model().get_solution().set_system_null_space(nks);
1514 }
1515
1516 double d_time = std::sqrt(step_size / ((v * v) - 1));
1517 if (stage_termination_procedure
1518 || time + d_time
1519 > IncrementConstraintStrategy<T, MATRIX_FORMAT>::max_time - step_size_min) {
1520 stage_termination_procedure = true;
1521 d += (time - IncrementConstraintStrategy<T, MATRIX_FORMAT>::max_time)
1522 * v(b2linalg::Interval(0, q.size()));
1523 time = IncrementConstraintStrategy<T, MATRIX_FORMAT>::max_time;
1524 } else {
1525 d += d_time * v(b2linalg::Interval(0, q.size()));
1526 time += d_time;
1527 }
1528 return IncrementConstraintStrategy<T, MATRIX_FORMAT>::in_stage;
1529 }
1530
1531 void get_constraint(
1532 const b2linalg::Vector<T, b2linalg::Vdense>& dof, const double time, T* constraint,
1533 b2linalg::Vector<T, b2linalg::Vdense>* d_constraint_d_dof, T* d_constraint_d_time) {
1534 if (stage_termination_procedure) {
1535 if (constraint) {
1536 *constraint = time - IncrementConstraintStrategy<T, MATRIX_FORMAT>::max_time;
1537 }
1538 if (d_constraint_d_dof) { d_constraint_d_dof->set_zero(); }
1539 if (d_constraint_d_time) { *d_constraint_d_time = 1; }
1540 } else {
1541 b2linalg::Vector<T, b2linalg::Vdense> d_dof;
1542 d_dof = dof - dof_at_start_step;
1543 if (constraint) { *constraint = d_dof * d_dof - step_size * step_size; }
1544 if (d_constraint_d_dof) { *d_constraint_d_dof = 2.0 * d_dof; }
1545 if (d_constraint_d_time) { *d_constraint_d_time = 0; }
1546 }
1547 }
1548
1549 typename IncrementConstraintStrategy<T, MATRIX_FORMAT>::Result set_correction_result(
1550 b2linalg::Vector<T, b2linalg::Vdense>& dof, double& time, bool converged,
1551 int number_of_iteration) {
1552 if (converged) {
1553 if (time > IncrementConstraintStrategy<T, MATRIX_FORMAT>::max_time - 1e-8
1554 || stage_termination_procedure) {
1555 stage_termination_procedure = false;
1556 return IncrementConstraintStrategy<T, MATRIX_FORMAT>::end_of_stage_with_success;
1557 }
1558 step_size *= std::pow(
1559 corrector_nb_newton_iteration,
1560 double(number_of_iteration - nb_iteration) / (nb_iteration - 1));
1561 step_size = std::min(std::max(step_size, step_size_min), step_size_max);
1562 return IncrementConstraintStrategy<T, MATRIX_FORMAT>::in_stage;
1563 }
1564 stage_termination_procedure = false;
1565 step_size *= 0.5;
1566 if (step_size < step_size_min) {
1567 IncrementConstraintStrategy<T, MATRIX_FORMAT>::logger
1568 << logging::error << "Cannot continue, the step size (" << step_size
1569 << ") is below the minimum step size(" << step_size_min << ")."
1570 << logging::LOGFLUSH;
1571 return IncrementConstraintStrategy<T, MATRIX_FORMAT>::end_of_stage_with_error;
1572 }
1573 dof = dof_at_start_step;
1574 time = time_at_start_step;
1575 IncrementConstraintStrategy<T, MATRIX_FORMAT>::logger
1576 << logging::debug << "new step size = " << step_size << logging::LOGFLUSH;
1577 return IncrementConstraintStrategy<T, MATRIX_FORMAT>::in_stage_and_retry;
1578 }
1579
1580 double get_delta_time() const { return step_size; }
1581
1582 using type_t = ObjectTypeComplete<
1583 StateControlConstraintStrategy,
1584 typename IncrementConstraintStrategy<T, MATRIX_FORMAT>::type_t>;
1585 static type_t type;
1586
1587private:
1588 bool stage_termination_procedure;
1589 double step_size;
1590 double step_size_min;
1591 double step_size_max;
1592 b2linalg::Vector<T, b2linalg::Vdense> dof_at_start_step;
1593 double time_at_start_step;
1594 int nb_iteration;
1595 double corrector_nb_newton_iteration;
1596};
1597
1598template <typename T, typename MATRIX_FORMAT>
1599typename StateControlConstraintStrategy<T, MATRIX_FORMAT>::type_t
1600 StateControlConstraintStrategy<T, MATRIX_FORMAT>::type(
1601 "StateControlConstraintStrategy", type_name<T, MATRIX_FORMAT>(),
1602 StringList("STATE_CONTROL_CONSTRAINT_STRATEGY"), module);
1603
1604template <typename T, typename MATRIX_FORMAT>
1605class HyperplaneControlConstraintStrategy : public IncrementConstraintStrategy<T, MATRIX_FORMAT> {
1606public:
1607 HyperplaneControlConstraintStrategy()
1608 : stage_termination_procedure(false),
1609 step_size(0),
1610 step_size_min(0),
1611 step_size_max(0),
1612 step_size_lambda_min(0),
1613 step_size_lambda_max(0),
1614 step_size_dof_min(0),
1615 step_size_dof_max(0),
1616 step_size_dof_lambda_fact(-1),
1617 step_size_lambda_fact(-1),
1618 step_size_dof_fact(-1),
1619 time_at_start_step(0),
1620 nb_iteration(0),
1621 corrector_nb_newton_iteration(0) {}
1622
1623 void init(Model& model, const AttributeList& attribute) {
1624 step_size = attribute.get_double("STEP_SIZE_INIT", 0.1);
1625 step_size_min = attribute.get_double(
1626 "STEP_SIZE_PATH_MIN", attribute.get_double("STEP_SIZE_MIN", 1e-12));
1627 step_size_max = attribute.get_double(
1628 "STEP_SIZE_PATH_MAX", attribute.get_double("STEP_SIZE_MAX", 1e100));
1629 step_size_lambda_min = attribute.get_double("STEP_SIZE_LAMBDA_MIN", 1e-12);
1630 step_size_lambda_max = attribute.get_double(
1631 "STEP_SIZE_LAMBDA_MAX", IncrementConstraintStrategy<T, MATRIX_FORMAT>::max_time);
1632 step_size_dof_min = attribute.get_double("STEP_SIZE_DOF_MIN", 1e-12);
1633 step_size_dof_max = attribute.get_double("STEP_SIZE_DOF_MAX", 1e100);
1634 nb_iteration =
1635 attribute.get_int("NB_ITERATION", attribute.get_int("MAX_NEWTON_ITERATIONS", 50) / 2);
1636 corrector_nb_newton_iteration = attribute.get_double("NB_ITERATION_CORRECTION", 0.5);
1637 stage_termination_procedure = false;
1638 predictor_taylor_order_path =
1639 attribute.get_int("TAYLOR_ORDER_PATH", attribute.get_int("TAYLOR_ORDER", 1));
1640 predictor_approximation_order_path = attribute.get_int(
1641 "APPROXIMATION_ORDER_PATH", attribute.get_int("APPROXIMATION_ORDER", 2));
1642 }
1643
1644 typename IncrementConstraintStrategy<T, MATRIX_FORMAT>::Result set_increment(
1645 StaticResidueFunction<T, MATRIX_FORMAT>& residue_function,
1646 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible_ext>& K,
1647 b2linalg::Vector<T, b2linalg::Vdense_ref> q, b2linalg::Vector<T, b2linalg::Vdense_ref> d,
1648 double& time, EquilibriumSolution& equilibrium_solution) {
1649 residue_function.get_residue(
1650 d, time, step_size, equilibrium_solution,
1651 b2linalg::Vector<T, b2linalg::Vdense_ref>::null, K, q, 0);
1652 size_t rs = residue_function.get_number_of_dof();
1653 if (dof_at_start_step.size() == 0) {
1654 d_dof_d_time_at_previous_step.resize(rs + 1);
1655 d_dof_d_time_at_previous_step[rs] = 1;
1656 velocity_at_start_step.resize(rs + 1);
1657 velocity_at_start_step[rs] = 1;
1658 } else {
1659 velocity_at_start_step.set_zero();
1660 velocity_at_start_step[rs] =
1661 d_dof_d_time_at_previous_step * d_dof_d_time_at_previous_step;
1662 }
1663 dof_at_start_step = d;
1664 time_at_start_step = time;
1665 Model& model = residue_function.get_model();
1666 const ssize_t nbnsv = model.get_case()->get_int("COMPUTE_NULL_SPACE_VECTOR", 0);
1667 b2linalg::Matrix<T, b2linalg::Mrectangle> nks_r;
1668 try {
1669 IncrementConstraintStrategy<T, MATRIX_FORMAT>::logger
1671 << "Compute prediction and factorize tangent "
1672 "stiffness matrix ("
1673 << K.size1() << " rows and columns)." << logging::LOGFLUSH;
1674 velocity_at_start_step =
1675 update_extension_and_inverse(
1676 K, q, d_dof_d_time_at_previous_step(b2linalg::Interval(0, rs)),
1677 d_dof_d_time_at_previous_step[rs], nks_r, nbnsv)
1678 * velocity_at_start_step;
1679 } catch (b2linalg::SingularMatrixError& e) {
1680 if (nks_r.size2() != 0) {
1681 b2linalg::Matrix<T, b2linalg::Mrectangle> nks(
1682 model.get_domain().get_number_of_dof(), nks_r.size2());
1683 for (size_t i = 0; i != nks_r.size2(); ++i) {
1684 residue_function.get_solution(nks_r[i], time, equilibrium_solution, nks[i]);
1685 }
1686 residue_function.get_model().get_solution().set_system_null_space(nks);
1687 }
1688 throw;
1689 }
1690 if (nks_r.size2() != 0) {
1691 b2linalg::Matrix<T, b2linalg::Mrectangle> nks(
1692 model.get_domain().get_number_of_dof(), nks_r.size2());
1693 for (size_t i = 0; i != nks_r.size2(); ++i) {
1694 residue_function.get_solution(nks_r[i], time, equilibrium_solution, nks[i]);
1695 }
1696 residue_function.get_model().get_solution().set_system_null_space(nks);
1697 }
1698
1699 const size_t nnld = residue_function.get_number_of_non_lagrange_dof();
1700 double norm_nld = velocity_at_start_step(b2linalg::Interval(0, nnld)).norm2();
1701 double norm_ld = velocity_at_start_step(b2linalg::Interval(nnld, rs - 1)).norm2();
1702 double norm_lambda = std::abs(velocity_at_start_step[rs]);
1703 {
1704 const double v =
1705 1
1706 / std::sqrt(norm_nld * norm_nld + norm_ld * norm_ld + norm_lambda * norm_lambda);
1707 velocity_at_start_step *= v;
1708 norm_nld *= v;
1709 norm_ld *= v;
1710 norm_lambda *= v;
1711 }
1712 // The first step size is a lambda step size
1713 if (step_size_lambda_fact == -1) {
1714 if (velocity_at_start_step[rs] == velocity_at_start_step[rs]) {
1715 step_size /= velocity_at_start_step[rs];
1716 }
1717 }
1718
1719 {
1720 step_size_dof_lambda_fact = std::sqrt(norm_nld * norm_nld + norm_lambda * norm_lambda);
1721 if (step_size_dof_lambda_fact * step_size > step_size_max) {
1722 step_size = step_size_max / step_size_dof_lambda_fact;
1723 }
1724 if (step_size_dof_lambda_fact * step_size < step_size_min) {
1725 step_size = step_size_min / step_size_dof_lambda_fact;
1726 }
1727 }
1728 if (norm_lambda > 0) {
1729 step_size_lambda_fact = norm_lambda;
1730 if (step_size_lambda_fact * step_size > step_size_lambda_max) {
1731 step_size = step_size_lambda_max / step_size_lambda_fact;
1732 }
1733 if (step_size_lambda_fact * step_size < step_size_lambda_min) {
1734 step_size = step_size_lambda_min / step_size_lambda_fact;
1735 }
1736 }
1737 if (norm_nld > 0) {
1738 step_size_dof_fact = norm_nld;
1739 if (step_size_dof_fact * step_size > step_size_dof_max) {
1740 step_size = step_size_dof_max / step_size_dof_fact;
1741 }
1742 if (step_size_dof_fact * step_size < step_size_dof_min) {
1743 step_size = step_size_dof_min / step_size_dof_fact;
1744 }
1745 }
1746 if (time + step_size * step_size_lambda_fact
1747 > IncrementConstraintStrategy<T, MATRIX_FORMAT>::max_time
1748 - std::max(step_size_lambda_min, step_size_min * step_size_lambda_fact)) {
1749 stage_termination_procedure = true;
1750 }
1751 if (stage_termination_procedure) {
1752 d += ((IncrementConstraintStrategy<T, MATRIX_FORMAT>::max_time - time)
1753 / velocity_at_start_step[rs])
1754 * velocity_at_start_step(b2linalg::Interval(0, rs));
1755 time = IncrementConstraintStrategy<T, MATRIX_FORMAT>::max_time;
1756 } else {
1757 d += step_size * velocity_at_start_step(b2linalg::Interval(0, rs));
1758 time += step_size * velocity_at_start_step[rs];
1759 }
1760 return IncrementConstraintStrategy<T, MATRIX_FORMAT>::in_stage;
1761 }
1762
1763 void get_constraint(
1764 const b2linalg::Vector<T, b2linalg::Vdense>& dof, const double time, T* constraint,
1765 b2linalg::Vector<T, b2linalg::Vdense>* d_constraint_d_dof, T* d_constraint_d_time) {
1766 if (stage_termination_procedure
1767 || time >= IncrementConstraintStrategy<T, MATRIX_FORMAT>::max_time) {
1768 if (constraint) {
1769 *constraint = time - IncrementConstraintStrategy<T, MATRIX_FORMAT>::max_time;
1770 }
1771 if (d_constraint_d_dof) { d_constraint_d_dof->set_zero(); }
1772 if (d_constraint_d_time) { *d_constraint_d_time = 1; }
1773 } else {
1774 b2linalg::Vector<T, b2linalg::Vdense> d_dof;
1775 d_dof = dof - dof_at_start_step;
1776 size_t rs = velocity_at_start_step.size() - 1;
1777 if (constraint) {
1778 *constraint = velocity_at_start_step(b2linalg::Interval(0, rs)) * d_dof
1779 + velocity_at_start_step[rs] * (time - time_at_start_step)
1780 - step_size;
1781 }
1782 if (d_constraint_d_dof) {
1783 *d_constraint_d_dof = velocity_at_start_step(b2linalg::Interval(0, rs));
1784 }
1785 if (d_constraint_d_time) { *d_constraint_d_time = velocity_at_start_step[rs]; }
1786 }
1787 }
1788
1789 typename IncrementConstraintStrategy<T, MATRIX_FORMAT>::Result set_correction_result(
1790 b2linalg::Vector<T, b2linalg::Vdense>& dof, double& time, bool converged,
1791 int number_of_iteration) {
1792 if (converged) {
1793 d_dof_d_time_at_previous_step(b2linalg::Interval(0, dof.size())) =
1794 dof - dof_at_start_step;
1795 d_dof_d_time_at_previous_step[dof.size()] = time - time_at_start_step;
1796 if (time > IncrementConstraintStrategy<T, MATRIX_FORMAT>::max_time - 1e-8
1797 || stage_termination_procedure) {
1798 stage_termination_procedure = false;
1799 return IncrementConstraintStrategy<T, MATRIX_FORMAT>::end_of_stage_with_success;
1800 }
1801 step_size *= std::pow(
1802 corrector_nb_newton_iteration,
1803 double(number_of_iteration - nb_iteration) / (nb_iteration - 1));
1804 return IncrementConstraintStrategy<T, MATRIX_FORMAT>::in_stage;
1805 }
1806 step_size *= 0.5;
1807 stage_termination_procedure = false;
1808 if (step_size * step_size_dof_lambda_fact < step_size_min) {
1809 IncrementConstraintStrategy<T, MATRIX_FORMAT>::logger
1811 << "Cannot continue, the step size along the continuation "
1812 "path ("
1813 << step_size * step_size_dof_lambda_fact << ") is below the minimum ("
1814 << step_size_min << ")." << logging::LOGFLUSH;
1815 return IncrementConstraintStrategy<T, MATRIX_FORMAT>::end_of_stage_with_error;
1816 }
1817 if (step_size * step_size_lambda_fact < step_size_lambda_min) {
1818 IncrementConstraintStrategy<T, MATRIX_FORMAT>::logger
1820 << "Cannot continue, the step size on the lambda parameter "
1821 "control ("
1822 << step_size * step_size_lambda_fact << ") is below the minimum ("
1823 << step_size_lambda_min << ")." << logging::LOGFLUSH;
1824 return IncrementConstraintStrategy<T, MATRIX_FORMAT>::end_of_stage_with_error;
1825 }
1826 if (step_size * step_size_dof_fact < step_size_dof_min) {
1827 IncrementConstraintStrategy<T, MATRIX_FORMAT>::logger
1829 << "Cannot continue, the step size on the norm of the "
1830 "dof ("
1831 << step_size * step_size_dof_fact << ") is below the minimum ("
1832 << step_size_dof_min << ")." << logging::LOGFLUSH;
1833 return IncrementConstraintStrategy<T, MATRIX_FORMAT>::end_of_stage_with_error;
1834 }
1835 dof = dof_at_start_step;
1836 time = time_at_start_step;
1837 IncrementConstraintStrategy<T, MATRIX_FORMAT>::logger
1838 << logging::debug << "new step size = " << step_size << logging::LOGFLUSH;
1839 return IncrementConstraintStrategy<T, MATRIX_FORMAT>::in_stage_and_retry;
1840 }
1841
1842 double get_delta_time() const { return step_size; }
1843
1844 using type_t = ObjectTypeComplete<
1845 HyperplaneControlConstraintStrategy,
1846 typename IncrementConstraintStrategy<T, MATRIX_FORMAT>::type_t>;
1847 static type_t type;
1848
1849private:
1850 bool stage_termination_procedure;
1851 double step_size;
1852 double step_size_min;
1853 double step_size_max;
1854 double step_size_lambda_min;
1855 double step_size_lambda_max;
1856 double step_size_dof_min;
1857 double step_size_dof_max;
1858 double step_size_dof_lambda_fact;
1859 double step_size_lambda_fact;
1860 double step_size_dof_fact;
1861 int predictor_taylor_order_path;
1862 int predictor_approximation_order_path;
1863 b2linalg::Vector<T, b2linalg::Vdense> dof_at_start_step;
1864 double time_at_start_step;
1865 b2linalg::Vector<T, b2linalg::Vdense> velocity_at_start_step;
1866 b2linalg::Vector<T, b2linalg::Vdense> d_dof_d_time_at_previous_step;
1867 int nb_iteration;
1868 double corrector_nb_newton_iteration;
1869};
1870
1871template <typename T, typename MATRIX_FORMAT>
1872typename HyperplaneControlConstraintStrategy<T, MATRIX_FORMAT>::type_t
1873 HyperplaneControlConstraintStrategy<T, MATRIX_FORMAT>::type(
1874 "HyperplaneControlConstraintStrategy", type_name<T, MATRIX_FORMAT>(),
1875 StringList("HYPERPLANE_CONTROL_CONSTRAINT_STRATEGY"), module);
1876
1877template <typename T, typename MATRIX_FORMAT>
1878class NormalFlowControlConstraintStrategy : public IncrementConstraintStrategy<T, MATRIX_FORMAT> {
1879public:
1880 NormalFlowControlConstraintStrategy()
1881 : stage_termination_procedure(false),
1882 step_size(0),
1883 step_size_min(0),
1884 step_size_max(0),
1885 step_size_lambda_min(0),
1886 step_size_lambda_max(0),
1887 step_size_dof_min(0),
1888 step_size_dof_max(0),
1889 step_size_dof_lambda_fact(-1),
1890 step_size_lambda_fact(-1),
1891 step_size_dof_fact(-1),
1892 time_at_start_step(0),
1893 nb_iteration(0),
1894 corrector_nb_newton_iteration(0),
1895 K_ptr(0),
1896 q_ptr(0) {}
1897
1898 void init(Model& model, const AttributeList& attribute) {
1899 step_size = attribute.get_double("STEP_SIZE_INIT", 0.1);
1900 step_size_min = attribute.get_double(
1901 "STEP_SIZE_PATH_MIN", attribute.get_double("STEP_SIZE_MIN", 1e-12));
1902 step_size_max = attribute.get_double(
1903 "STEP_SIZE_PATH_MAX", attribute.get_double("STEP_SIZE_MAX", 1e100));
1904 step_size_lambda_min = attribute.get_double("STEP_SIZE_LAMBDA_MIN", 1e-12);
1905 step_size_lambda_max = attribute.get_double(
1906 "STEP_SIZE_LAMBDA_MAX", IncrementConstraintStrategy<T, MATRIX_FORMAT>::max_time);
1907 step_size_dof_min = attribute.get_double("STEP_SIZE_DOF_MIN", 1e-12);
1908 step_size_dof_max = attribute.get_double("STEP_SIZE_DOF_MAX", 1e100);
1909 nb_iteration =
1910 attribute.get_int("NB_ITERATION", attribute.get_int("MAX_NEWTON_ITERATIONS", 50) / 2);
1911 corrector_nb_newton_iteration = attribute.get_double("NB_ITERATION_CORRECTION", 0.5);
1912 stage_termination_procedure = false;
1913 predictor_taylor_order_path =
1914 attribute.get_int("TAYLOR_ORDER_PATH", attribute.get_int("TAYLOR_ORDER", 1));
1915 predictor_approximation_order_path = attribute.get_int(
1916 "APPROXIMATION_ORDER_PATH", attribute.get_int("APPROXIMATION_ORDER", 2));
1917 }
1918
1919 typename IncrementConstraintStrategy<T, MATRIX_FORMAT>::Result set_increment(
1920 StaticResidueFunction<T, MATRIX_FORMAT>& residue_function,
1921 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible_ext>& K,
1922 b2linalg::Vector<T, b2linalg::Vdense_ref> q, b2linalg::Vector<T, b2linalg::Vdense_ref> d,
1923 double& time, EquilibriumSolution& equilibrium_solution) {
1924 residue_function.get_residue(
1925 d, time, step_size, equilibrium_solution,
1926 b2linalg::Vector<T, b2linalg::Vdense_ref>::null, K, q, 0);
1927 K_ptr = &K;
1928 q_ptr = &q;
1929 size_t rs = residue_function.get_number_of_dof();
1930 if (dof_at_start_step.size() == 0) {
1931 d_dof_d_time_at_previous_step.resize(rs + 1);
1932 d_dof_d_time_at_previous_step[rs] = 1;
1933 velocity_at_start_step.resize(rs + 1);
1934 velocity_at_start_step[rs] = 1;
1935 } else {
1936 velocity_at_start_step.set_zero();
1937 velocity_at_start_step[rs] =
1938 d_dof_d_time_at_previous_step * d_dof_d_time_at_previous_step;
1939 }
1940 dof_at_start_step = d;
1941 time_at_start_step = time;
1942 Model& model = residue_function.get_model();
1943 const ssize_t nbnsv = model.get_case()->get_int("COMPUTE_NULL_SPACE_VECTOR", 0);
1944 b2linalg::Matrix<T, b2linalg::Mrectangle> nks_r;
1945 try {
1946 IncrementConstraintStrategy<T, MATRIX_FORMAT>::logger
1948 << "Compute prediction and factorize tangent "
1949 "stiffness matrix ("
1950 << K.size1() << " rows and columns)." << logging::LOGFLUSH;
1951 velocity_at_start_step =
1952 update_extension_and_inverse(
1953 K, q, d_dof_d_time_at_previous_step(b2linalg::Interval(0, rs)),
1954 d_dof_d_time_at_previous_step[rs], nks_r, nbnsv)
1955 * velocity_at_start_step;
1956 } catch (b2linalg::SingularMatrixError& e) {
1957 if (nks_r.size2() != 0) {
1958 b2linalg::Matrix<T, b2linalg::Mrectangle> nks(
1959 model.get_domain().get_number_of_dof(), nks_r.size2());
1960 for (size_t i = 0; i != nks_r.size2(); ++i) {
1961 residue_function.get_solution(nks_r[i], time, equilibrium_solution, nks[i]);
1962 }
1963 residue_function.get_model().get_solution().set_system_null_space(nks);
1964 }
1965 throw;
1966 }
1967 if (nks_r.size2() != 0) {
1968 b2linalg::Matrix<T, b2linalg::Mrectangle> nks(
1969 model.get_domain().get_number_of_dof(), nks_r.size2());
1970 for (size_t i = 0; i != nks_r.size2(); ++i) {
1971 residue_function.get_solution(nks_r[i], time, equilibrium_solution, nks[i]);
1972 }
1973 residue_function.get_model().get_solution().set_system_null_space(nks);
1974 }
1975 const size_t nnld = residue_function.get_number_of_non_lagrange_dof();
1976 double norm_nld = velocity_at_start_step(b2linalg::Interval(0, nnld)).norm2();
1977 double norm_ld = velocity_at_start_step(b2linalg::Interval(nnld, rs - 1)).norm2();
1978 double norm_lambda = std::abs(velocity_at_start_step[rs]);
1979 {
1980 const double v =
1981 1
1982 / std::sqrt(norm_nld * norm_nld + norm_ld * norm_ld + norm_lambda * norm_lambda);
1983 velocity_at_start_step *= v;
1984 norm_nld *= v;
1985 norm_ld *= v;
1986 norm_lambda *= v;
1987 }
1988 // The first step size is a lambda step size
1989 if (step_size_lambda_fact == -1) {
1990 if (velocity_at_start_step[rs] == velocity_at_start_step[rs]) {
1991 step_size /= velocity_at_start_step[rs];
1992 }
1993 }
1994
1995 {
1996 step_size_dof_lambda_fact = std::sqrt(norm_nld * norm_nld + norm_lambda * norm_lambda);
1997 if (step_size_dof_lambda_fact * step_size > step_size_max) {
1998 step_size = step_size_max / step_size_dof_lambda_fact;
1999 }
2000 if (step_size_dof_lambda_fact * step_size < step_size_min) {
2001 step_size = step_size_min / step_size_dof_lambda_fact;
2002 }
2003 }
2004 if (norm_lambda > 0) {
2005 step_size_lambda_fact = norm_lambda;
2006 if (step_size_lambda_fact * step_size > step_size_lambda_max) {
2007 step_size = step_size_lambda_max / step_size_lambda_fact;
2008 }
2009 if (step_size_lambda_fact * step_size < step_size_lambda_min) {
2010 step_size = step_size_lambda_min / step_size_lambda_fact;
2011 }
2012 }
2013 if (norm_nld > 0) {
2014 step_size_dof_fact = norm_nld;
2015 if (step_size_dof_fact * step_size > step_size_dof_max) {
2016 step_size = step_size_dof_max / step_size_dof_fact;
2017 }
2018 if (step_size_dof_fact * step_size < step_size_dof_min) {
2019 step_size = step_size_dof_min / step_size_dof_fact;
2020 }
2021 }
2022 if (time + step_size * step_size_lambda_fact
2023 > IncrementConstraintStrategy<T, MATRIX_FORMAT>::max_time
2024 - std::max(step_size_lambda_min, step_size_min * step_size_lambda_fact)) {
2025 stage_termination_procedure = true;
2026 }
2027 if (stage_termination_procedure) {
2028 d += ((IncrementConstraintStrategy<T, MATRIX_FORMAT>::max_time - time)
2029 / velocity_at_start_step[rs])
2030 * velocity_at_start_step(b2linalg::Interval(0, rs));
2031 time = IncrementConstraintStrategy<T, MATRIX_FORMAT>::max_time;
2032 } else {
2033 d += step_size * velocity_at_start_step(b2linalg::Interval(0, rs));
2034 time += step_size * velocity_at_start_step[rs];
2035 }
2036 return IncrementConstraintStrategy<T, MATRIX_FORMAT>::in_stage;
2037 }
2038
2039 void get_constraint(
2040 const b2linalg::Vector<T, b2linalg::Vdense>& dof, const double time, T* constraint,
2041 b2linalg::Vector<T, b2linalg::Vdense>* d_constraint_d_dof, T* d_constraint_d_time) {
2042 if (stage_termination_procedure
2043 || time >= IncrementConstraintStrategy<T, MATRIX_FORMAT>::max_time) {
2044 if (constraint) {
2045 *constraint = time - IncrementConstraintStrategy<T, MATRIX_FORMAT>::max_time;
2046 }
2047 if (d_constraint_d_dof) { d_constraint_d_dof->set_zero(); }
2048 if (d_constraint_d_time) { *d_constraint_d_time = 1; }
2049 } else {
2050 b2linalg::Vector<T, b2linalg::Vdense> d_dof;
2051 d_dof = dof - dof_at_start_step;
2052 size_t rs = velocity_at_start_step.size() - 1;
2053 if (constraint) { *constraint = 0; }
2054 if (d_constraint_d_dof || d_constraint_d_time) {
2055 velocity_at_start_step(b2linalg::Interval(0, rs)) =
2056 b2linalg::inverse((
2057 b2linalg::Matrix<T, b2linalg::Msym_compressed_col_update_inv>&)(*K_ptr))
2058 * (*q_ptr);
2059 velocity_at_start_step[rs] = 1;
2060 velocity_at_start_step.normalize();
2061 velocity_at_start_step *= -1;
2062 if (d_constraint_d_dof) {
2063 *d_constraint_d_dof = velocity_at_start_step(b2linalg::Interval(0, rs));
2064 }
2065 if (d_constraint_d_time) { *d_constraint_d_time = velocity_at_start_step[rs]; }
2066 }
2067 }
2068 }
2069
2070 typename IncrementConstraintStrategy<T, MATRIX_FORMAT>::Result set_correction_result(
2071 b2linalg::Vector<T, b2linalg::Vdense>& dof, double& time, bool converged,
2072 int number_of_iteration) {
2073 if (converged) {
2074 d_dof_d_time_at_previous_step(b2linalg::Interval(0, dof.size())) =
2075 dof - dof_at_start_step;
2076 d_dof_d_time_at_previous_step[dof.size()] = time - time_at_start_step;
2077 if (time > IncrementConstraintStrategy<T, MATRIX_FORMAT>::max_time - 1e-8
2078 || stage_termination_procedure) {
2079 stage_termination_procedure = false;
2080 return IncrementConstraintStrategy<T, MATRIX_FORMAT>::end_of_stage_with_success;
2081 }
2082 step_size *= std::pow(
2083 corrector_nb_newton_iteration,
2084 double(number_of_iteration - nb_iteration) / (nb_iteration - 1));
2085 return IncrementConstraintStrategy<T, MATRIX_FORMAT>::in_stage;
2086 }
2087 step_size *= 0.5;
2088 stage_termination_procedure = false;
2089 if (step_size * step_size_dof_lambda_fact < step_size_min) {
2090 IncrementConstraintStrategy<T, MATRIX_FORMAT>::logger
2092 << "Cannot continue, the step size along the continuation "
2093 "path ("
2094 << step_size * step_size_dof_lambda_fact << ") is below the minimum ("
2095 << step_size_min << ")." << logging::LOGFLUSH;
2096 return IncrementConstraintStrategy<T, MATRIX_FORMAT>::end_of_stage_with_error;
2097 }
2098 if (step_size * step_size_lambda_fact < step_size_lambda_min) {
2099 IncrementConstraintStrategy<T, MATRIX_FORMAT>::logger
2101 << "Cannot continue, the step size on the lambda parameter "
2102 "control ("
2103 << step_size * step_size_lambda_fact << ") is below the minimum ("
2104 << step_size_lambda_min << ")." << logging::LOGFLUSH;
2105 return IncrementConstraintStrategy<T, MATRIX_FORMAT>::end_of_stage_with_error;
2106 }
2107 if (step_size * step_size_dof_fact < step_size_dof_min) {
2108 IncrementConstraintStrategy<T, MATRIX_FORMAT>::logger
2110 << "Cannot continue, the step size on the norm of the "
2111 "dof ("
2112 << step_size * step_size_dof_fact << ") is below the minimum ("
2113 << step_size_dof_min << ")." << logging::LOGFLUSH;
2114 return IncrementConstraintStrategy<T, MATRIX_FORMAT>::end_of_stage_with_error;
2115 }
2116 dof = dof_at_start_step;
2117 time = time_at_start_step;
2118 IncrementConstraintStrategy<T, MATRIX_FORMAT>::logger
2119 << logging::debug << "new step size = " << step_size << logging::LOGFLUSH;
2120 return IncrementConstraintStrategy<T, MATRIX_FORMAT>::in_stage_and_retry;
2121 }
2122
2123 double get_delta_time() const { return step_size; }
2124
2125 using type_t = ObjectTypeComplete<
2126 NormalFlowControlConstraintStrategy,
2127 typename IncrementConstraintStrategy<T, MATRIX_FORMAT>::type_t>;
2128 static type_t type;
2129
2130private:
2131 bool stage_termination_procedure;
2132 double step_size;
2133 double step_size_min;
2134 double step_size_max;
2135 double step_size_lambda_min;
2136 double step_size_lambda_max;
2137 double step_size_dof_min;
2138 double step_size_dof_max;
2139 double step_size_dof_lambda_fact;
2140 double step_size_lambda_fact;
2141 double step_size_dof_fact;
2142 int predictor_taylor_order_path;
2143 int predictor_approximation_order_path;
2144 b2linalg::Vector<T, b2linalg::Vdense> dof_at_start_step;
2145 double time_at_start_step;
2146 b2linalg::Vector<T, b2linalg::Vdense> velocity_at_start_step;
2147 b2linalg::Vector<T, b2linalg::Vdense> d_dof_d_time_at_previous_step;
2148 int nb_iteration;
2149 double corrector_nb_newton_iteration;
2150 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible_ext>* K_ptr;
2151 b2linalg::Vector<T, b2linalg::Vdense_ref>* q_ptr;
2152};
2153
2154template <typename T, typename MATRIX_FORMAT>
2155typename NormalFlowControlConstraintStrategy<T, MATRIX_FORMAT>::type_t
2156 NormalFlowControlConstraintStrategy<T, MATRIX_FORMAT>::type(
2157 "NormalFlowControlConstraintStrategy", type_name<T, MATRIX_FORMAT>(),
2158 StringList("NORMALFLOW_CONTROL_CONSTRAINT_STRATEGY"), module);
2159
2160template <typename T, typename MATRIX_FORMAT>
2161class LocalHyperellipticControlConstraintStrategy
2162 : public IncrementConstraintStrategy<T, MATRIX_FORMAT> {
2163public:
2164 LocalHyperellipticControlConstraintStrategy()
2165 : stage_termination_procedure(false),
2166 step_size(0),
2167 step_size_min(0),
2168 step_size_max(0),
2169 step_size_lambda_min(0),
2170 step_size_lambda_max(0),
2171 step_size_dof_min(0),
2172 step_size_dof_max(0),
2173 step_size_dof_lambda_fact(-1),
2174 step_size_lambda_fact(-1),
2175 step_size_dof_fact(-1),
2176 ellipse_a_2(0),
2177 ellipse_b_2(0),
2178 time_at_start_step(0),
2179 nb_iteration(0),
2180 corrector_nb_newton_iteration(0) {}
2181
2182 void init(Model& model, const AttributeList& attribute) {
2183 step_size = attribute.get_double("STEP_SIZE_INIT", 0.1);
2184 step_size_min = attribute.get_double(
2185 "STEP_SIZE_PATH_MIN", attribute.get_double("STEP_SIZE_MIN", 1e-12));
2186 step_size_max = attribute.get_double(
2187 "STEP_SIZE_PATH_MAX", attribute.get_double("STEP_SIZE_MAX", 1e100));
2188 step_size_lambda_min = attribute.get_double("STEP_SIZE_LAMBDA_MIN", 1e-12);
2189 step_size_lambda_max = attribute.get_double(
2190 "STEP_SIZE_LAMBDA_MAX", IncrementConstraintStrategy<T, MATRIX_FORMAT>::max_time);
2191 step_size_dof_min = attribute.get_double("STEP_SIZE_DOF_MIN", 1e-12);
2192 step_size_dof_max = attribute.get_double("STEP_SIZE_DOF_MAX", 1e100);
2193 nb_iteration =
2194 attribute.get_int("NB_ITERATION", attribute.get_int("MAX_NEWTON_ITERATIONS", 50) / 2);
2195 corrector_nb_newton_iteration = attribute.get_double("NB_ITERATION_CORRECTION", 0.5);
2196 ellipse_a_2 = attribute.get_double("HYPERELLIPTIC_A", 1.0);
2197 ellipse_a_2 *= ellipse_a_2;
2198 ellipse_b_2 = attribute.get_double("HYPERELLIPTIC_B", 1.0);
2199 ellipse_b_2 *= ellipse_b_2;
2200 stage_termination_procedure = false;
2201 }
2202
2203 typename IncrementConstraintStrategy<T, MATRIX_FORMAT>::Result set_increment(
2204 StaticResidueFunction<T, MATRIX_FORMAT>& residue_function,
2205 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible_ext>& K,
2206 b2linalg::Vector<T, b2linalg::Vdense_ref> q, b2linalg::Vector<T, b2linalg::Vdense_ref> d,
2207 double& time, EquilibriumSolution& equilibrium_solution) {
2208 residue_function.get_residue(
2209 d, time, step_size, equilibrium_solution,
2210 b2linalg::Vector<T, b2linalg::Vdense_ref>::null, K, q, 0);
2211 size_t rs = residue_function.get_number_of_dof();
2212 if (dof_at_start_step.size() == 0) {
2213 dof_at_start_step = d;
2214 time_at_start_step = time;
2215 d_dof_d_time_at_previous_step.resize(rs + 1);
2216 d_dof_d_time_at_previous_step[rs] = 1;
2217 velocity_at_start_step.resize(rs + 1);
2218 velocity_at_start_step[rs] = 1;
2219 } else {
2220 velocity_at_start_step.set_zero();
2221 velocity_at_start_step[rs] =
2222 d_dof_d_time_at_previous_step * d_dof_d_time_at_previous_step;
2223 }
2224 dof_at_start_step = d;
2225 time_at_start_step = time;
2226
2227 Model& model = residue_function.get_model();
2228 const ssize_t nbnsv = model.get_case()->get_int("COMPUTE_NULL_SPACE_VECTOR", 0);
2229 b2linalg::Matrix<T, b2linalg::Mrectangle> nks_r;
2230 try {
2231 IncrementConstraintStrategy<T, MATRIX_FORMAT>::logger
2233 << "Compute prediction and factorize tangent "
2234 "stiffness matrix ("
2235 << K.size1() << " rows and columns)." << logging::LOGFLUSH;
2236 velocity_at_start_step =
2237 update_extension_and_inverse(
2238 K, q, d_dof_d_time_at_previous_step(b2linalg::Interval(0, rs)),
2239 d_dof_d_time_at_previous_step[rs], nks_r, nbnsv)
2240 * velocity_at_start_step;
2241 } catch (b2linalg::SingularMatrixError& e) {
2242 if (nks_r.size2() != 0) {
2243 b2linalg::Matrix<T, b2linalg::Mrectangle> nks(
2244 model.get_domain().get_number_of_dof(), nks_r.size2());
2245 for (size_t i = 0; i != nks_r.size2(); ++i) {
2246 residue_function.get_solution(nks_r[i], time, equilibrium_solution, nks[i]);
2247 }
2248 residue_function.get_model().get_solution().set_system_null_space(nks);
2249 }
2250 throw;
2251 }
2252 if (nks_r.size2() != 0) {
2253 b2linalg::Matrix<T, b2linalg::Mrectangle> nks(
2254 model.get_domain().get_number_of_dof(), nks_r.size2());
2255 for (size_t i = 0; i != nks_r.size2(); ++i) {
2256 residue_function.get_solution(nks_r[i], time, equilibrium_solution, nks[i]);
2257 }
2258 residue_function.get_model().get_solution().set_system_null_space(nks);
2259 }
2260 const size_t nnld = residue_function.get_number_of_non_lagrange_dof();
2261 double norm_nld = velocity_at_start_step(b2linalg::Interval(0, nnld)).norm2();
2262 double norm_ld = velocity_at_start_step(b2linalg::Interval(nnld, rs - 1)).norm2();
2263 double norm_lambda = std::abs(velocity_at_start_step[rs]);
2264 {
2265 const double v =
2266 1
2267 / std::sqrt(norm_nld * norm_nld + norm_ld * norm_ld + norm_lambda * norm_lambda);
2268 velocity_at_start_step *= v;
2269 norm_nld *= v;
2270 norm_ld *= v;
2271 norm_lambda *= v;
2272 }
2273 // The first step size is a lambda step size
2274 if (step_size_lambda_fact == -1) {
2275 if (velocity_at_start_step[rs] == velocity_at_start_step[rs]) {
2276 step_size /= velocity_at_start_step[rs];
2277 }
2278 }
2279 {
2280 step_size_dof_lambda_fact = std::sqrt(norm_nld * norm_nld + norm_lambda * norm_lambda);
2281 if (step_size_dof_lambda_fact * step_size > step_size_max) {
2282 step_size = step_size_max / step_size_dof_lambda_fact;
2283 }
2284 if (step_size_dof_lambda_fact * step_size < step_size_min) {
2285 step_size = step_size_min / step_size_dof_lambda_fact;
2286 }
2287 }
2288 if (norm_lambda > 0) {
2289 step_size_lambda_fact = norm_lambda;
2290 if (step_size_lambda_fact * step_size > step_size_lambda_max) {
2291 step_size = step_size_lambda_max / step_size_lambda_fact;
2292 }
2293 if (step_size_lambda_fact * step_size < step_size_lambda_min) {
2294 step_size = step_size_lambda_min / step_size_lambda_fact;
2295 }
2296 }
2297 if (norm_nld > 0) {
2298 step_size_dof_fact = norm_nld;
2299 if (step_size_dof_fact * step_size > step_size_dof_max) {
2300 step_size = step_size_dof_max / step_size_dof_fact;
2301 }
2302 if (step_size_dof_fact * step_size < step_size_dof_min) {
2303 step_size = step_size_dof_min / step_size_dof_fact;
2304 }
2305 }
2306 if (time + step_size * step_size_lambda_fact
2307 > IncrementConstraintStrategy<T, MATRIX_FORMAT>::max_time
2308 - std::max(step_size_lambda_min, step_size_min * step_size_lambda_fact)) {
2309 stage_termination_procedure = true;
2310 }
2311 if (stage_termination_procedure) {
2312 d += ((IncrementConstraintStrategy<T, MATRIX_FORMAT>::max_time - time)
2313 / velocity_at_start_step[rs])
2314 * velocity_at_start_step(b2linalg::Interval(0, rs));
2315 time = IncrementConstraintStrategy<T, MATRIX_FORMAT>::max_time;
2316 } else {
2317 d += step_size * velocity_at_start_step(b2linalg::Interval(0, rs));
2318 time += step_size * velocity_at_start_step[rs];
2319 }
2320 return IncrementConstraintStrategy<T, MATRIX_FORMAT>::in_stage;
2321 }
2322
2323 void get_constraint(
2324 const b2linalg::Vector<T, b2linalg::Vdense>& dof, const double time, T* constraint,
2325 b2linalg::Vector<T, b2linalg::Vdense>* d_constraint_d_dof, T* d_constraint_d_time) {
2326 if (stage_termination_procedure
2327 || time >= IncrementConstraintStrategy<T, MATRIX_FORMAT>::max_time) {
2328 if (constraint) {
2329 *constraint = time - IncrementConstraintStrategy<T, MATRIX_FORMAT>::max_time;
2330 }
2331 if (d_constraint_d_dof) { d_constraint_d_dof->set_zero(); }
2332 if (d_constraint_d_time) { *d_constraint_d_time = 1; }
2333 } else {
2334 size_t rs = velocity_at_start_step.size() - 1;
2335 b2linalg::Vector<T, b2linalg::Vdense> d_dof(rs + 1);
2336 d_dof(b2linalg::Interval(0, rs)) = dof - dof_at_start_step;
2337 d_dof[rs] = time - time_at_start_step;
2338 T v_dd = velocity_at_start_step * d_dof;
2339 b2linalg::Vector<T, b2linalg::Vdense> d_dof_s;
2340 d_dof_s = d_dof - velocity_at_start_step * v_dd;
2341 if (constraint) {
2342 *constraint = ellipse_a_2 * v_dd + ellipse_b_2 * (d_dof_s * d_dof_s) - step_size;
2343 }
2344 if (d_constraint_d_dof || d_constraint_d_time) {
2345 double s = 0;
2346 for (size_t i = 0; i != d_dof_s.size(); ++i) {
2347 s += d_dof_s[i] * (velocity_at_start_step[i] * velocity_at_start_step[i]);
2348 }
2349 if (d_constraint_d_dof) {
2350 for (size_t i = 0; i != rs; ++i) {
2351 (*d_constraint_d_dof)[i] = 2 * ellipse_a_2 * velocity_at_start_step[i]
2352 + 2 * ellipse_b_2 * (d_dof_s[i] - s);
2353 }
2354 }
2355 if (d_constraint_d_time) {
2356 *d_constraint_d_time = 2 * ellipse_a_2 * velocity_at_start_step[rs]
2357 + 2 * ellipse_b_2 * (d_dof_s[rs] - s);
2358 }
2359 }
2360 }
2361 }
2362
2363 typename IncrementConstraintStrategy<T, MATRIX_FORMAT>::Result set_correction_result(
2364 b2linalg::Vector<T, b2linalg::Vdense>& dof, double& time, bool converged,
2365 int number_of_iteration) {
2366 IncrementConstraintStrategy<T, MATRIX_FORMAT>::logger
2367 << logging::debug << "Insertion of the correction result in the predictor";
2368 if (converged) {
2369 d_dof_d_time_at_previous_step(b2linalg::Interval(0, dof.size())) =
2370 dof - dof_at_start_step;
2371 d_dof_d_time_at_previous_step[dof.size()] = time - time_at_start_step;
2372 if (time > IncrementConstraintStrategy<T, MATRIX_FORMAT>::max_time - 1e-8
2373 || stage_termination_procedure) {
2374 stage_termination_procedure = false;
2375 IncrementConstraintStrategy<T, MATRIX_FORMAT>::logger
2376 << ", end of stage with success." << logging::LOGFLUSH;
2377 return IncrementConstraintStrategy<T, MATRIX_FORMAT>::end_of_stage_with_success;
2378 }
2379 step_size *= std::pow(
2380 corrector_nb_newton_iteration,
2381 double(number_of_iteration - nb_iteration) / (nb_iteration - 1));
2382 IncrementConstraintStrategy<T, MATRIX_FORMAT>::logger
2383 << ", next step with step size=" << step_size << "." << logging::LOGFLUSH;
2384 return IncrementConstraintStrategy<T, MATRIX_FORMAT>::in_stage;
2385 }
2386 step_size *= 0.5;
2387 stage_termination_procedure = false;
2388 if (step_size * step_size_dof_lambda_fact < step_size_min) {
2389 IncrementConstraintStrategy<T, MATRIX_FORMAT>::logger
2391 << "Cannot continue, the step size along the continuation "
2392 "path ("
2393 << step_size * step_size_dof_lambda_fact << ") is below the minimum ("
2394 << step_size_min << ")." << logging::LOGFLUSH;
2395 return IncrementConstraintStrategy<T, MATRIX_FORMAT>::end_of_stage_with_error;
2396 }
2397 if (step_size * step_size_lambda_fact < step_size_lambda_min) {
2398 IncrementConstraintStrategy<T, MATRIX_FORMAT>::logger
2400 << "Cannot continue, the step size on the lambda parameter "
2401 "control ("
2402 << step_size * step_size_lambda_fact << ") is below the minimum ("
2403 << step_size_lambda_min << ")." << logging::LOGFLUSH;
2404 return IncrementConstraintStrategy<T, MATRIX_FORMAT>::end_of_stage_with_error;
2405 }
2406 if (step_size * step_size_dof_fact < step_size_dof_min) {
2407 IncrementConstraintStrategy<T, MATRIX_FORMAT>::logger
2409 << "Cannot continue, the step size on the norm of the "
2410 "dof ("
2411 << step_size * step_size_dof_fact << ") is below the minimum ("
2412 << step_size_dof_min << ")." << logging::LOGFLUSH;
2413 return IncrementConstraintStrategy<T, MATRIX_FORMAT>::end_of_stage_with_error;
2414 }
2415 dof = dof_at_start_step;
2416 time = time_at_start_step;
2417 IncrementConstraintStrategy<T, MATRIX_FORMAT>::logger
2418 << ", retry step with step size=" << step_size << "." << logging::LOGFLUSH;
2419 return IncrementConstraintStrategy<T, MATRIX_FORMAT>::in_stage_and_retry;
2420 }
2421
2422 double get_delta_time() const { return step_size; }
2423
2424 using type_t = ObjectTypeComplete<
2425 LocalHyperellipticControlConstraintStrategy,
2426 typename IncrementConstraintStrategy<T, MATRIX_FORMAT>::type_t>;
2427 static type_t type;
2428
2429private:
2430 bool stage_termination_procedure;
2431 double step_size;
2432 double step_size_min;
2433 double step_size_max;
2434 double step_size_lambda_min;
2435 double step_size_lambda_max;
2436 double step_size_dof_min;
2437 double step_size_dof_max;
2438 double step_size_dof_lambda_fact;
2439 double step_size_lambda_fact;
2440 double step_size_dof_fact;
2441 double ellipse_a_2;
2442 double ellipse_b_2;
2443 b2linalg::Vector<T, b2linalg::Vdense> dof_at_start_step;
2444 double time_at_start_step;
2445 b2linalg::Vector<T, b2linalg::Vdense> velocity_at_start_step;
2446 b2linalg::Vector<T, b2linalg::Vdense> d_dof_d_time_at_previous_step;
2447 int nb_iteration;
2448 double corrector_nb_newton_iteration;
2449};
2450
2451template <typename T, typename MATRIX_FORMAT>
2452typename LocalHyperellipticControlConstraintStrategy<T, MATRIX_FORMAT>::type_t
2453 LocalHyperellipticControlConstraintStrategy<T, MATRIX_FORMAT>::type(
2454 "LocalHyperellipticControlConstraintStrategy", type_name<T, MATRIX_FORMAT>(),
2455 StringList(
2456 "LOCAL_HYPERELLIPTIC_CONTROL_CONSTRAINT_STRATEGY",
2457 "HYPERELLIPTIC_CONTROL_CONSTRAINT_STRATEGY"),
2458 module);
2459
2460template <typename T, typename MATRIX_FORMAT>
2461class NewtonMethod : public CorrectionStrategy<T, MATRIX_FORMAT> {
2462public:
2463 enum Method { conventional, modified, delayed_modified };
2464
2465 NewtonMethod() : termination_test(0), method(modified), periodic_update(100) {}
2466
2467 virtual ~NewtonMethod() {
2468 if (termination_test) { termination_test->free(); }
2469 }
2470
2471 void init(Model& model, const AttributeList& attribute) {
2472 std::string termination_s =
2473 attribute.get_string("CORRECTION_TERMINATION_TEST", "FLUX_NORMALISED");
2474 if (termination_s != "") { termination_s += "_CORRECTION_TERMINATION_TEST"; }
2475 termination_s = type_name<T>(termination_s);
2476 typename CorrectionTerminationTest<T>::type_t* termination_t =
2477 CorrectionTerminationTest<T>::type.get_subtype(termination_s, solver::module);
2478 if (termination_test) { termination_test->free(); }
2479 termination_test = termination_t->new_object(allocator);
2480 termination_test->init(model, attribute);
2481 std::string method_s = attribute.get_string("NEWTON_METHOD", "CONVENTIONAL");
2482 if (method_s == "CONVENTIONAL") {
2483 method = conventional;
2484 } else {
2485 periodic_update = attribute.get_int("NEWTON_PERIODIC_UPDATE", 100);
2486 if (method_s == "MODIFIED") {
2487 method = modified;
2488 } else if (method_s == "DELAYED_MODIFIED") {
2489 method = delayed_modified;
2490 }
2491 }
2492 if (attribute.get_int("FULLNEWTON", 0) != 0) { method = conventional; }
2493 }
2494
2495 typename IncrementConstraintStrategy<T, MATRIX_FORMAT>::Result compute_correction(
2496 StaticResidueFunction<T, MATRIX_FORMAT>& residue_function,
2497 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible_ext>& K,
2498 b2linalg::Vector<T, b2linalg::Vdense>& q,
2499 IncrementConstraintStrategy<T, MATRIX_FORMAT>& constraint,
2500 b2linalg::Vector<T, b2linalg::Vdense>& d, double& time,
2501 EquilibriumSolution& equilibrium_solution) {
2502 const size_t number_of_dof = residue_function.get_number_of_dof();
2503 typename CorrectionTerminationTest<T>::Termination result;
2504 b2linalg::Vector<T, b2linalg::Vdense> r(number_of_dof + 1);
2505 b2linalg::Interval residu_int(0, number_of_dof);
2506 b2linalg::Vector<T, b2linalg::Vdense> c_d(number_of_dof);
2507 T c_c;
2508 int nb_iter = 0;
2509 int nb_iter_first = 0;
2510 bool has_degradation = false;
2511 b2linalg::Vector<T, b2linalg::Vdense> d_old = d;
2512 double time_old = time;
2513 double e_old = std::numeric_limits<double>::max();
2514 bool always_refactorization = false;
2515 bool refactorization = false;
2516
2517 if (CorrectionStrategy<T, MATRIX_FORMAT>::logger.is_enabled_for(logging::data)) {
2518 Model& model = residue_function.get_model();
2519 b2linalg::Vector<T, b2linalg::Vdense> g(model.get_domain().get_number_of_dof());
2520 residue_function.get_solution(d, time, equilibrium_solution, g);
2521 SolutionStaticNonlinear<T>& solution =
2522 dynamic_cast<SolutionStaticNonlinear<T>&>(model.get_solution());
2523 solution.set_dof(
2524 time, g, residue_function.get_nbc_sol(),
2525 residue_function.get_residuum_sol(d, time), true);
2526 }
2527
2528 int nb_iter_without_refactorization = 0;
2529 SolverHints solver_hints;
2530 termination_test->new_step();
2531 do {
2532 termination_test->new_iteration();
2533 termination_test->new_evaluation();
2534 refactorization = always_refactorization || (method == conventional)
2535 || (method == delayed_modified && nb_iter == 0)
2536 || (method == modified
2537 && ++nb_iter_without_refactorization % periodic_update == 0);
2538
2539 if (refactorization) {
2540 nb_iter_without_refactorization = 0;
2541 solver_hints.clear();
2542 residue_function.get_residue(
2543 d, time, constraint.get_delta_time(), equilibrium_solution, r(residu_int), K,
2544 q, &solver_hints, (termination_test->need_flux() ? termination_test : 0));
2545#ifdef CHECK_RESIDUE_DERIVATIVE
2546 // std::cout << "K=" << K << std::endl;
2547 const double h = 1e-5;
2548 const double espilon = 1e-2;
2549 b2linalg::Vector<T> d_tmp(d);
2550 b2linalg::Vector<T> r_tmp1(d.size());
2551 b2linalg::Vector<T> r_tmp2(d.size());
2552 EquilibriumSolution qs(1, -1) for (size_t j = 0; j != d.size(); ++j) {
2553 d_tmp[j] -= h / 2;
2554 residue_function.get_residue(
2555 d_tmp, time, constraint.get_delta_time(), es, r_tmp1,
2556 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible_ext>::null,
2557 b2linalg::Vector<T, b2linalg::Vdense_ref>::null, 0, 0);
2558 d_tmp[j] += h;
2559 residue_function.get_residue(
2560 d_tmp, time, constraint.get_delta_time(), es, r_tmp2,
2561 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible_ext>::null,
2562 b2linalg::Vector<T, b2linalg::Vdense_ref>::null, 0, 0);
2563 d_tmp[j] = d[j];
2564 /*
2565 std::cout << "r_tmp1=" << std::endl;
2566 std::cout << r_tmp1 << std::endl;
2567 std::cout << "r_tmp2=" << std::endl;
2568 std::cout << r_tmp2 << std::endl;
2569 */
2570 r_tmp2 -= r_tmp1;
2571 r_tmp2 *= 1.0 / h;
2572 /*
2573 std::cout << "num_der=" << std::endl;
2574 std::cout << r_tmp2 << std::endl;
2575 */
2576 for (size_t i = 0; i != d.size(); ++i) {
2577 if (norm(K(i, j) - r_tmp2[i])
2578 > std::max(espilon, espilon * norm(r_tmp2[i]))) {
2579 std::cout
2580 << "Error: K(" << i << ", " << j << ")=" << K(i, j)
2581 << ", computed =" << r_tmp2[i] << ", diff=" << K(i, j) - r_tmp2[i]
2582 << ", n_diff=" << (K(i, j) - r_tmp2[i]) / r_tmp2[i] << std::endl;
2583 }
2584 }
2585 }
2586 double time_tmp = time;
2587 time_tmp -= h / 2;
2588 residue_function.get_residue(
2589 d, time_tmp, constraint.get_delta_time(), es, r_tmp1,
2590 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible_ext>::null,
2591 b2linalg::Vector<T, b2linalg::Vdense_ref>::null, 0, 0);
2592 time_tmp += h;
2593 residue_function.get_residue(
2594 d, time_tmp, constraint.get_delta_time(), es, r_tmp2,
2595 Matrix<T, typename MATRIX_FORMAT::sparse_inversible_ext>::null,
2596 Vector<T, Vdense_ref>::null, 0, 0);
2597 r_tmp2 -= r_tmp1;
2598 r_tmp2 *= 1 / h;
2599 for (size_t i = 0; i != d.size(); ++i) {
2600 if (norm(q[i] - r_tmp2[i]) > std::max(espilon, espilon * norm(r_tmp2[i]))) {
2601 std::cout << "Error: d_K_d_time[" << i << "]=" << q[i]
2602 << ", computed =" << r_tmp2[i] << ", diff=" << q[i] - r_tmp2[i]
2603 << ", n_diff=" << (q[i] - r_tmp2[i]) / r_tmp2[i] << std::endl;
2604 }
2605 }
2606#endif /* CHECK_RESIDUE_DERIVATIVE */
2607 } else {
2608 solver_hints.clear();
2609 residue_function.get_residue(
2610 d, time, constraint.get_delta_time(), equilibrium_solution, r(residu_int),
2611 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible_ext>::null,
2612 b2linalg::Vector<T, b2linalg::Vdense_ref>::null, &solver_hints,
2613 (termination_test->need_flux() ? termination_test : 0));
2614 }
2615 constraint.get_constraint(
2616 d, time, &r[number_of_dof], refactorization || nb_iter == 0 ? &c_d : 0, &c_c);
2617
2618 b2linalg::Vector<T> delta_d;
2619 Model& model = residue_function.get_model();
2620 const ssize_t nbnsv = model.get_case()->get_int("COMPUTE_NULL_SPACE_VECTOR", 0);
2621 b2linalg::Matrix<T, b2linalg::Mrectangle> nks_r;
2622 try {
2623 if (refactorization || nb_iter == 0) {
2624 CorrectionStrategy<T, MATRIX_FORMAT>::logger
2626 << "Compute correction and factorize "
2627 "tangent stiffness matrix ("
2628 << K.size1() << " rows and columns)." << logging::LOGFLUSH;
2629 delta_d = update_extension_and_inverse(K, q, c_d, c_c, nks_r, nbnsv) * r;
2630 } else {
2631 delta_d = inverse(K, nks_r, nbnsv) * r;
2632 }
2633 } catch (b2linalg::SingularMatrixError& e) {
2634 if (nks_r.size2() != 0) {
2635 b2linalg::Matrix<T, b2linalg::Mrectangle> nks(
2636 model.get_domain().get_number_of_dof(), nks_r.size2());
2637 for (size_t i = 0; i != nks_r.size2(); ++i) {
2638 residue_function.get_solution(nks_r[i], time, equilibrium_solution, nks[i]);
2639 }
2640 residue_function.get_model().get_solution().set_system_null_space(nks);
2641 }
2642 throw;
2643 }
2644 if (nks_r.size2() != 0) {
2645 b2linalg::Matrix<T, b2linalg::Mrectangle> nks(
2646 model.get_domain().get_number_of_dof(), nks_r.size2());
2647 for (size_t i = 0; i != nks_r.size2(); ++i) {
2648 residue_function.get_solution(nks_r[i], time, equilibrium_solution, nks[i]);
2649 }
2650 residue_function.get_model().get_solution().set_system_null_space(nks);
2651 }
2652
2653 d += -delta_d(residu_int);
2654 time += -delta_d[number_of_dof];
2655
2656 if (*solver_hints & SolverHints::degradation) { has_degradation = true; }
2657 if (!has_degradation) { ++nb_iter_first; }
2658
2659 result = termination_test->is_terminated(
2660 ++nb_iter, r(residu_int), delta_d(residu_int), 1, d, d_old, r[number_of_dof], c_c,
2661 delta_d[number_of_dof], time, residue_function,
2662 residue_function.get_matrix_diagonal(K),
2663 equilibrium_solution.distance_to_iterative_convergence, &solver_hints);
2664
2665 if (CorrectionStrategy<T, MATRIX_FORMAT>::logger.is_enabled_for(logging::data)) {
2666 Model& model = residue_function.get_model();
2667 b2linalg::Vector<T, b2linalg::Vdense> g(model.get_domain().get_number_of_dof());
2668 residue_function.get_solution(d, time, false, g);
2669 SolutionStaticNonlinear<T>& solution =
2670 dynamic_cast<SolutionStaticNonlinear<T>&>(model.get_solution());
2671 solution.set_dof(
2672 time, g, residue_function.get_nbc_sol(),
2673 residue_function.get_residuum_sol(d, time), true);
2674 }
2675
2676 if (!always_refactorization) {
2677 if (nb_iter > 0 && !refactorization
2678 && result == CorrectionTerminationTest<T>::diverged) {
2679 d = d_old;
2680 time = time_old;
2681 CorrectionStrategy<T, MATRIX_FORMAT>::logger
2683 << "Bad convergence, re-factorize the matrix from "
2684 "the last good obtained solution at time="
2685 << time << "." << logging::LOGFLUSH;
2686 } else if (result == CorrectionTerminationTest<T>::unfinished) {
2687 if ((equilibrium_solution.distance_to_iterative_convergence >= 0
2688 && equilibrium_solution.distance_to_iterative_convergence < e_old)
2689 || (*solver_hints & SolverHints::degradation)) {
2690 d_old = d;
2691 time_old = time;
2692 e_old = equilibrium_solution.distance_to_iterative_convergence;
2693 }
2694 }
2695 }
2696 } while (result == CorrectionTerminationTest<T>::unfinished);
2697
2698 if (result == CorrectionTerminationTest<T>::converged) {
2699 termination_test->converged_step();
2700 }
2701 return constraint.set_correction_result(
2702 d, time, result == CorrectionTerminationTest<T>::converged, nb_iter_first);
2703 }
2704
2705 using type_t =
2706 ObjectTypeComplete<NewtonMethod, typename CorrectionStrategy<T, MATRIX_FORMAT>::type_t>;
2707 static type_t type;
2708
2709protected:
2710 CorrectionTerminationTest<T>* termination_test;
2711 Allocator allocator;
2712 Method method;
2713 int periodic_update;
2714};
2715
2716template <typename T, typename MATRIX_FORMAT>
2717typename NewtonMethod<T, MATRIX_FORMAT>::type_t NewtonMethod<T, MATRIX_FORMAT>::type(
2718 "NewtonMethod", type_name<T, MATRIX_FORMAT>(), StringList("NEWTON_METHOD"), module);
2719
2720template <typename T, typename MATRIX_FORMAT>
2721class AcceleratedNewtonMethod : public CorrectionStrategy<T, MATRIX_FORMAT> {
2722public:
2723 enum Method { conventional, modified, delayed_modified };
2724
2725 AcceleratedNewtonMethod() : termination_test(0), line_search(nullptr), method(modified) {}
2726
2727 ~AcceleratedNewtonMethod() {
2728 if (termination_test) { termination_test->free(); }
2729 }
2730
2731 void init(Model& model, const AttributeList& attribute) {
2732 std::string termination_s =
2733 attribute.get_string("CORRECTION_TERMINATION_TEST", "FLUX_NORMALISED");
2734 if (termination_s != "") { termination_s += "_CORRECTION_TERMINATION_TEST"; }
2735 termination_s = type_name<T>(termination_s);
2736 typename CorrectionTerminationTest<T>::type_t* termination_t =
2737 CorrectionTerminationTest<T>::type.get_subtype(termination_s, solver::module);
2738 if (termination_test) { termination_test->free(); }
2739 termination_test = termination_t->new_object(allocator);
2740 termination_test->init(model, attribute);
2741
2742 std::string line_search_s = attribute.get_string("LINE_SEARCH_ALGORITHM", "STRONG_WOLFE");
2743 if (line_search_s != "") { line_search_s += "_LINE_SEARCH"; }
2744 typename LineSearch::type_t* line_search_t =
2745 LineSearch::type.get_subtype(line_search_s, solver::module);
2746 line_search = line_search_t->new_object(allocator);
2747 line_search->init(model, attribute);
2748 std::string method_s = attribute.get_string("NEWTON_METHOD", "CONVENTIONAL");
2749 if (method_s == "CONVENTIONAL") {
2750 method = conventional;
2751 } else if (method_s == "MODIFIED") {
2752 method = modified;
2753 } else if (method_s == "DELAYED_MODIFIED") {
2754 method = delayed_modified;
2755 } else {
2756 KeyError("Unknown Newton method specified.") << THROW;
2757 }
2758 if (attribute.get_int("FULLNEWTON", 0) == 1) { method = conventional; }
2759 }
2760
2761 typename IncrementConstraintStrategy<T, MATRIX_FORMAT>::Result compute_correction(
2762 StaticResidueFunction<T, MATRIX_FORMAT>& residue_function,
2763 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible_ext>& K,
2764 b2linalg::Vector<T, b2linalg::Vdense>& q,
2765 IncrementConstraintStrategy<T, MATRIX_FORMAT>& constraint,
2766 b2linalg::Vector<T, b2linalg::Vdense>& d, double& time,
2767 EquilibriumSolution& equilibrium_solution) {
2768 typename CorrectionTerminationTest<T>::Termination result;
2769 int nb_dof = residue_function.get_number_of_dof();
2770 b2linalg::Vector<T, b2linalg::Vdense> d_old = d;
2771 b2linalg::Vector<T, b2linalg::Vdense> r(nb_dof + 1);
2772 b2linalg::Vector<T, b2linalg::Vdense> director(nb_dof + 1);
2773 b2linalg::Vector<T, b2linalg::Vdense> d_test(nb_dof);
2774 b2linalg::Vector<T, b2linalg::Vdense> r_test(nb_dof + 1);
2775 double time_test;
2776 b2linalg::Interval residu_int(0, nb_dof);
2777 b2linalg::Vector<T, b2linalg::Vdense> c_d(nb_dof);
2778 T c_c;
2779 int refactorization; // 0=no, 1=yes second variation already computed, 2=yes second
2780 // variation no computed
2781
2782 SolverHints solver_hints;
2783 if (method == delayed_modified || method == conventional) {
2784 residue_function.get_residue(
2785 d, time, constraint.get_delta_time(), equilibrium_solution, r(residu_int), K, q,
2786 &solver_hints);
2787 constraint.get_constraint(d, time, &r[nb_dof], &c_d, &c_c);
2788 refactorization = 1;
2789 } else {
2790 residue_function.get_residue(
2791 d, time, constraint.get_delta_time(), equilibrium_solution, r(residu_int),
2792 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible_ext>::null,
2793 b2linalg::Vector<T, b2linalg::Vdense_ref>::null, &solver_hints);
2794 constraint.get_constraint(d, time, &r[nb_dof], 0, &c_c);
2795 refactorization = 0;
2796 }
2797
2798 T r0_norm = 0;
2799 for (size_t i = 0; i != d.size(); ++i) { r0_norm += norm(r[i] * r[i] * d[i]); }
2800 r0_norm += norm(r[nb_dof] * r[nb_dof] * time);
2801
2802 int nb_iter = 0;
2803 int nb_iter_first = 0;
2804 bool has_degradation = false;
2805 double h = 0;
2806 bool hard_conv = false;
2807
2808 if (CorrectionStrategy<T, MATRIX_FORMAT>::logger.is_enabled_for(logging::data)) {
2809 Model& model = residue_function.get_model();
2810 b2linalg::Vector<T, b2linalg::Vdense> g(model.get_domain().get_number_of_dof());
2811 residue_function.get_solution(d, time, equilibrium_solution, g);
2812 SolutionStaticNonlinear<T>& solution =
2813 dynamic_cast<SolutionStaticNonlinear<T>&>(model.get_solution());
2814 solution.set_dof(
2815 time, g, residue_function.get_nbc_sol(),
2816 residue_function.get_residuum_sol(d, time), true);
2817 }
2818
2819 int nb_divergence = 0;
2820 Model& model = residue_function.get_model();
2821 const ssize_t nbnsv = model.get_case()->get_int("COMPUTE_NULL_SPACE_VECTOR", 0);
2822 termination_test->new_step();
2823 do {
2824 termination_test->new_iteration();
2825 termination_test->new_evaluation();
2826 // Compute the director
2827 b2linalg::Matrix<T, b2linalg::Mrectangle> nks_r;
2828 try {
2829 if (refactorization > 0 || method == conventional || hard_conv || nb_divergence > 2
2830 || nb_iter == 0) {
2831 if (refactorization == 2 || method == conventional || hard_conv
2832 || nb_divergence > 2) {
2833 solver_hints.clear();
2834 residue_function.get_residue(
2835 d, time, constraint.get_delta_time(), equilibrium_solution,
2836 b2linalg::Vector<T, b2linalg::Vdense_ref>::null, K, q, &solver_hints,
2837 0);
2838 constraint.get_constraint(d, time, &r[nb_dof], &c_d, &c_c);
2839 refactorization = 2;
2840 }
2841 CorrectionStrategy<T, MATRIX_FORMAT>::logger
2843 << "Compute correction and factorize "
2844 "tangent stiffness matrix ("
2845 << K.size1() << " rows and columns)." << logging::LOGFLUSH;
2846 director = update_extension_and_inverse(K, q, c_d, c_c, nks_r, nbnsv) * r;
2847 } else {
2848 director = inverse(K, nks_r, nbnsv) * r;
2849 }
2850 } catch (b2linalg::SingularMatrixError& e) {
2851 if (nks_r.size2() != 0) {
2852 b2linalg::Matrix<T, b2linalg::Mrectangle> nks(
2853 model.get_domain().get_number_of_dof(), nks_r.size2());
2854 for (size_t i = 0; i != nks_r.size2(); ++i) {
2855 residue_function.get_solution(nks_r[i], time, equilibrium_solution, nks[i]);
2856 }
2857 residue_function.get_model().get_solution().set_system_null_space(nks);
2858 }
2859 throw;
2860 }
2861 if (nks_r.size2() != 0) {
2862 b2linalg::Matrix<T, b2linalg::Mrectangle> nks(
2863 model.get_domain().get_number_of_dof(), nks_r.size2());
2864 for (size_t i = 0; i != nks_r.size2(); ++i) {
2865 residue_function.get_solution(nks_r[i], time, equilibrium_solution, nks[i]);
2866 }
2867 residue_function.get_model().get_solution().set_system_null_space(nks);
2868 }
2869
2870 d_test = d - director(residu_int);
2871 time_test = time - director[nb_dof];
2872
2873 solver_hints.clear();
2874 residue_function.get_residue(
2875 d_test, time_test, constraint.get_delta_time(), equilibrium_solution,
2876 r_test(residu_int),
2877 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible_ext>::null,
2878 b2linalg::Vector<T, b2linalg::Vdense_ref>::null, &solver_hints,
2879 termination_test->need_flux() ? termination_test : 0);
2880 constraint.get_constraint(d_test, time_test, &r_test[nb_dof], 0, 0);
2881
2882 double r1_norm = 0;
2883 for (size_t i = 0; i != d_test.size(); ++i) {
2884 r1_norm += norm(r_test[i] * r_test[i] * d_test[i]);
2885 }
2886 r1_norm += norm(r_test[nb_dof] * r_test[nb_dof] * time_test);
2887 double rh_norm = r1_norm;
2888
2889 if (r1_norm > r0_norm + 1e-5) {
2890 b2linalg::Vector<T> r_tmp = r_test;
2891 // linear search
2892 solver_hints.clear();
2893 Function f(
2894 residue_function, constraint, director, d, d_test, r_test, time, time_test,
2895 rh_norm, &solver_hints);
2896 h = line_search->get_minimum(f, r0_norm, r1_norm);
2897
2898 // d_test, time_test and rh_norm is set for h_aprox_min
2899
2900 CorrectionStrategy<T, MATRIX_FORMAT>::logger
2901 << logging::debug << "line search h = " << h << logging::LOGFLUSH;
2902
2903 if (h < 0.1) {
2904 if (refactorization == 2) {
2905 h = 1.0;
2906 d_test = d - director(residu_int);
2907 r_test.swap(r_tmp);
2908 time_test = time - director[nb_dof];
2909 rh_norm = r1_norm;
2910 result = CorrectionTerminationTest<T>::unfinished;
2911 hard_conv = true;
2912 } else {
2913 // refactorization
2914 refactorization = 2;
2915 result = CorrectionTerminationTest<T>::unfinished;
2916 continue;
2917 }
2918 } else {
2919 refactorization = 0;
2920 }
2921 } else {
2922 refactorization = 0;
2923 h = 1;
2924 }
2925 r0_norm = rh_norm;
2926 r.swap(r_test);
2927 d.swap(d_test);
2928 time = time_test;
2929
2930 if (*solver_hints & SolverHints::degradation) { has_degradation = true; }
2931 if (!has_degradation) { nb_iter_first++; }
2932
2933 result = termination_test->is_terminated(
2934 ++nb_iter, r(residu_int), director(residu_int), h, d, d_old, r[nb_dof], c_c,
2935 director[nb_dof] * h, time, residue_function,
2936 residue_function.get_matrix_diagonal(K),
2937 equilibrium_solution.distance_to_iterative_convergence, &solver_hints);
2938 nb_divergence = termination_test->get_nb_divergence();
2939
2940 if (CorrectionStrategy<T, MATRIX_FORMAT>::logger.is_enabled_for(logging::data)) {
2941 Model& model = residue_function.get_model();
2942 b2linalg::Vector<T, b2linalg::Vdense> g(model.get_domain().get_number_of_dof());
2943 residue_function.get_solution(d, time, equilibrium_solution, g);
2944 SolutionStaticNonlinear<T>& solution =
2945 dynamic_cast<SolutionStaticNonlinear<T>&>(model.get_solution());
2946 solution.set_dof(
2947 time, g, residue_function.get_nbc_sol(),
2948 residue_function.get_residuum_sol(d, time), true);
2949 }
2950 } while (result == CorrectionTerminationTest<T>::unfinished);
2951
2952 if (result == CorrectionTerminationTest<T>::converged) {
2953 termination_test->converged_step();
2954 }
2955 return constraint.set_correction_result(
2956 d, time, result == CorrectionTerminationTest<T>::converged, nb_iter_first);
2957 }
2958
2959 using type_t = ObjectTypeComplete<
2960 AcceleratedNewtonMethod, typename CorrectionStrategy<T, MATRIX_FORMAT>::type_t>;
2961 static type_t type;
2962
2963protected:
2964 struct Function : public LineSearch::Function {
2965 Function(
2966 StaticResidueFunction<T, MATRIX_FORMAT>& residue_function_,
2967 IncrementConstraintStrategy<T, MATRIX_FORMAT>& constraint_,
2968 b2linalg::Vector<T, b2linalg::Vdense>& director_,
2969 b2linalg::Vector<T, b2linalg::Vdense>& d_,
2970 b2linalg::Vector<T, b2linalg::Vdense>& d_test_,
2971 b2linalg::Vector<T, b2linalg::Vdense>& r_test_, double time_, double& time_test_,
2972 double& rh_norm_, SolverHints* solver_hints_)
2973 : residue_function(residue_function_),
2974 constraint(constraint_),
2975 director(director_),
2976 d(d_),
2977 d_test(d_test_),
2978 r_test(r_test_),
2979 time(time_),
2980 time_test(time_test_),
2981 rh_norm(rh_norm_),
2982 solver_hints(solver_hints_) {}
2983
2984 double operator()(double h) override {
2985 size_t nb_dof = r_test.size() - 1;
2986 b2linalg::Interval residu_int(0, r_test.size() - 1);
2987 d_test = d - h * director(residu_int);
2988 time_test = time - h * director[nb_dof];
2989 EquilibriumSolution es(1, -1);
2990 residue_function.get_residue(
2991 d_test, time_test, constraint.get_delta_time(), es, r_test(residu_int),
2992 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible_ext>::null,
2993 b2linalg::Vector<T, b2linalg::Vdense_ref>::null, solver_hints);
2994 constraint.get_constraint(d_test, time_test, &r_test[nb_dof], 0, 0);
2995 rh_norm = 0;
2996 for (size_t i = 0; i != d_test.size(); ++i) {
2997 rh_norm += norm(r_test[i] * r_test[i] * d_test[i]);
2998 }
2999 rh_norm += norm(r_test[nb_dof] * r_test[nb_dof] * time_test);
3000 return rh_norm;
3001 }
3002
3003 StaticResidueFunction<T, MATRIX_FORMAT>& residue_function;
3004 IncrementConstraintStrategy<T, MATRIX_FORMAT>& constraint;
3005 b2linalg::Vector<T, b2linalg::Vdense>& director;
3006 b2linalg::Vector<T, b2linalg::Vdense>& d;
3007 b2linalg::Vector<T, b2linalg::Vdense>& d_test;
3008 b2linalg::Vector<T, b2linalg::Vdense>& r_test;
3009 double time;
3010 double& time_test;
3011 double& rh_norm;
3012 SolverHints* solver_hints;
3013 };
3014
3015 CorrectionTerminationTest<T>* termination_test;
3016 LineSearch* line_search;
3017 Allocator allocator;
3018 Method method;
3019};
3020
3021template <typename T, typename MATRIX_FORMAT>
3022typename AcceleratedNewtonMethod<T, MATRIX_FORMAT>::type_t
3023 AcceleratedNewtonMethod<T, MATRIX_FORMAT>::type(
3024 "AcceleratedNewtonMethod", type_name<T, MATRIX_FORMAT>(),
3025 StringList("ACCELERATED_NEWTON_METHOD"), module);
3026
3027template <typename T>
3028class DofAndResidueCorrectionTerminationTest : public CorrectionTerminationTest<T> {
3029public:
3030 void init(Model& model, const AttributeList& attribute) {
3031 e_r = attribute.get_double("TOL_RESIDUUM", 1e-3);
3032 e_d = attribute.get_double("TOL_SOLUTION", 1e-3);
3033 g_r = std::max(e_r, attribute.get_double("MAX_RESIDUUM", 1.0e8));
3034 g_d = std::max(e_d, attribute.get_double("MAX_SOLUTION", 1.0e8));
3035 max_number_of_iterations = attribute.get_int("MAX_NEWTON_ITERATIONS", 50);
3036 max_additional_iterations = 0;
3037 max_number_of_divergences = attribute.get_int("MAX_DIVERGENCES", 4);
3038 number_of_divergences = 0;
3039 last_rn = std::numeric_limits<double>::max();
3040 best_rn = std::numeric_limits<double>::max();
3041 }
3042
3043 typename CorrectionTerminationTest<T>::Termination is_terminated(
3044 int number_of_iteration, const b2linalg::Vector<T, b2linalg::Vdense_constref>& res,
3045 const b2linalg::Vector<T, b2linalg::Vdense_constref>& delta_dof,
3046 const double delta_dof_scale, const b2linalg::Vector<T, b2linalg::Vdense_constref>& dof,
3047 const b2linalg::Vector<T, b2linalg::Vdense_constref>& dof_at_start_step,
3048 const double constraint, const double d_constraint_d_time, const double delta_time,
3049 const double time, StaticResidueFunctionForTerminationTest<T>& residue,
3050 const b2linalg::Vector<T, b2linalg::Vindex1_constref>& diag_K,
3051 double& distance_to_iterative_convergence, const SolverHints* solver_hints) {
3052 double r = res.norm2();
3053 double d = delta_dof.norm2() * delta_dof_scale;
3054 if (number_of_iteration == 1) {
3055 max_additional_iterations = 0;
3056 number_of_divergences = 0;
3057 last_rn = std::numeric_limits<double>::max();
3058 best_rn = std::numeric_limits<double>::max();
3059 return CorrectionTerminationTest<T>::unfinished;
3060 }
3061 double rn = norm(r);
3062 double dn = norm(d);
3063 CorrectionTerminationTest<T>::logger
3064 << logging::debug << "termination test: residue=" << rn << ", last correction=" << dn
3065 << ", number of correction iterations=" << number_of_iteration;
3066 if (rn != rn) {
3067 CorrectionTerminationTest<T>::logger
3068 << ", diverged because the residue is not a number." << logging::LOGFLUSH;
3069 distance_to_iterative_convergence = -1;
3070 return CorrectionTerminationTest<T>::diverged;
3071 }
3072 if (dn != dn) {
3073 CorrectionTerminationTest<T>::logger
3074 << ", diverged because the last correction is not a number." << logging::LOGFLUSH;
3075 distance_to_iterative_convergence = -1;
3076 return CorrectionTerminationTest<T>::diverged;
3077 }
3078 if (solver_hints != nullptr && (**solver_hints & SolverHints::diverge)) {
3079 distance_to_iterative_convergence = -1;
3080 CorrectionTerminationTest<T>::logger << ", elements/materials/boundary conditions "
3081 "indicated divergence."
3082 << logging::LOGFLUSH;
3083 return CorrectionTerminationTest<T>::diverged;
3084 }
3085 distance_to_iterative_convergence = std::max(rn / e_r, dn / e_d);
3086 if ((rn < e_r) && (dn < e_d)
3087 && (solver_hints == nullptr || **solver_hints == SolverHints::none)) {
3088 CorrectionTerminationTest<T>::logger << ", converged." << logging::LOGFLUSH;
3089 return CorrectionTerminationTest<T>::converged;
3090 }
3091 if (number_of_iteration > 1 && rn > g_r) {
3092 CorrectionTerminationTest<T>::logger << ", diverged because the residue exceeds the "
3093 "maximum ("
3094 << g_r << ")." << logging::LOGFLUSH;
3095 return CorrectionTerminationTest<T>::diverged;
3096 }
3097 if (number_of_iteration > 1 && dn > g_d) {
3098 CorrectionTerminationTest<T>::logger
3099 << ", diverged because the last correction exceeds "
3100 "the maximum ("
3101 << g_d << ")." << logging::LOGFLUSH;
3102 return CorrectionTerminationTest<T>::diverged;
3103 }
3104 if (solver_hints != nullptr && (**solver_hints & SolverHints::degradation)) {
3105 const int remaining = std::max(
3106 0, max_number_of_iterations + max_additional_iterations - number_of_iteration);
3107 max_additional_iterations += std::max(0, max_number_of_iterations - remaining);
3108 number_of_divergences = 0;
3109 last_rn = std::numeric_limits<double>::max();
3110 best_rn = std::numeric_limits<double>::max();
3111 } else if (
3112 rn > last_rn
3113 || (number_of_iteration + max_number_of_divergences > 5 && rn > best_rn)) {
3114 if (++number_of_divergences > max_number_of_divergences) {
3115 CorrectionTerminationTest<T>::logger
3117 << ", diverged because the maximum number of consecutive "
3118 "divergences ("
3119 << max_number_of_divergences << ") has been exceeded (last=" << last_rn
3120 << ", best=" << best_rn << ")." << logging::LOGFLUSH;
3121 distance_to_iterative_convergence = -1;
3122 return CorrectionTerminationTest<T>::diverged;
3123 }
3124 last_rn = rn;
3125 best_rn = std::min(best_rn, rn);
3126 } else {
3127 number_of_divergences = 0;
3128 last_rn = rn;
3129 best_rn = std::min(best_rn, rn);
3130 }
3131
3132 if (number_of_iteration > max_number_of_iterations + max_additional_iterations) {
3133 CorrectionTerminationTest<T>::logger
3134 << ", diverged because the number of correction iterations "
3135 "exceeds the maximum ("
3136 << max_number_of_iterations;
3137 if (max_additional_iterations > 0) {
3138 CorrectionTerminationTest<T>::logger << "+" << max_additional_iterations;
3139 }
3140 CorrectionTerminationTest<T>::logger << ")." << logging::LOGFLUSH;
3141 return CorrectionTerminationTest<T>::max_iteration;
3142 }
3143 if (solver_hints != nullptr && (**solver_hints & SolverHints::degradation)) {
3144 CorrectionTerminationTest<T>::logger
3145 << ", elements/materials/boundary conditions indicated "
3146 "degradation";
3147 } else if (
3148 distance_to_iterative_convergence >= 0.0 && distance_to_iterative_convergence <= 1.0
3149 && solver_hints != nullptr && (**solver_hints & SolverHints::unfinished)) {
3150 CorrectionTerminationTest<T>::logger
3151 << ", elements/materials/boundary conditions indicated "
3152 "need for further Newton iterations";
3153 }
3154 CorrectionTerminationTest<T>::logger << ", unfinished." << logging::LOGFLUSH;
3155 return CorrectionTerminationTest<T>::unfinished;
3156 }
3157
3158 using type_t = ObjectTypeComplete<
3159 DofAndResidueCorrectionTerminationTest, typename CorrectionTerminationTest<T>::type_t>;
3160 static type_t type;
3161
3162private:
3163 double e_r;
3164 double e_d;
3165 double g_r;
3166 double g_d;
3167 double last_rn;
3168 double best_rn;
3169 int max_number_of_iterations;
3170 int max_additional_iterations;
3171 int number_of_divergences;
3172 int max_number_of_divergences;
3173};
3174
3175template <typename T>
3176typename DofAndResidueCorrectionTerminationTest<T>::type_t
3177 DofAndResidueCorrectionTerminationTest<T>::type(
3178 "DofAndResidueCorrectionTerminationTest", type_name<T>(),
3179 StringList("DOF_AND_RESIDUE_CORRECTION_TERMINATION_TEST"), module);
3180
3181template <typename T>
3182class NormalizedDofAndResidueCorrectionTerminationTest : public CorrectionTerminationTest<T> {
3183public:
3184 void init(Model& model, const AttributeList& attribute) {
3185 e_r = attribute.get_double("TOL_RESIDUUM", 1e-3);
3186 e_r *= e_r;
3187 e_d = attribute.get_double("TOL_SOLUTION", 1e-3);
3188 e_d *= e_d;
3189 g_r = std::max(e_r, attribute.get_double("MAX_RESIDUUM", 10.0e10));
3190 g_d = std::max(e_d, attribute.get_double("MAX_SOLUTION", 10.0e10));
3191 max_number_of_iteration = attribute.get_int("MAX_NEWTON_ITERATIONS", 50);
3192 max_additional_iterations = 0;
3193 max_number_of_divergences = attribute.get_int("MAX_DIVERGENCES", 4);
3194 number_of_divergences = 0;
3195 last_rn = std::numeric_limits<double>::max();
3196 best_rn = std::numeric_limits<double>::max();
3197 }
3198
3199 typename CorrectionTerminationTest<T>::Termination is_terminated(
3200 int number_of_iteration, const b2linalg::Vector<T, b2linalg::Vdense_constref>& res,
3201 const b2linalg::Vector<T, b2linalg::Vdense_constref>& delta_dof,
3202 const double delta_dof_scale, const b2linalg::Vector<T, b2linalg::Vdense_constref>& dof,
3203 const b2linalg::Vector<T, b2linalg::Vdense_constref>& dof_at_start_step,
3204 const double constraint, const double d_constraint_d_time, const double delta_time,
3205 const double time, StaticResidueFunctionForTerminationTest<T>& residue,
3206 const b2linalg::Vector<T, b2linalg::Vindex1_constref>& diag_K,
3207 double& distance_to_iterative_convergence, const SolverHints* solver_hints) {
3208 double r = res.norm2();
3209 double d = delta_dof.norm2() * delta_dof_scale;
3210 if (number_of_iteration == 1) {
3211 r_0 = r;
3212 d_0 = d;
3213 number_of_divergences = 0;
3214 last_rn = std::numeric_limits<double>::max();
3215 best_rn = std::numeric_limits<double>::max();
3216 return CorrectionTerminationTest<T>::unfinished;
3217 }
3218 double rn;
3219 if (norm(r_0) < 10e-15) {
3220 rn = 0;
3221 } else {
3222 rn = norm(r) / norm(r_0);
3223 }
3224 double dn;
3225 if (norm(d) < 10e-15) {
3226 dn = 0;
3227 } else {
3228 dn = norm(d) / norm(d_0);
3229 }
3230 CorrectionTerminationTest<T>::logger
3231 << logging::debug << "termination test: residue=" << r
3232 << ", normalised residue (scale=" << r_0 << ")=" << rn << ", last correction=" << d
3233 << ", normalised last correction (scale=" << d_0 << ")=" << dn
3234 << ", number of correction iterations=" << number_of_iteration;
3235 if (rn != rn) {
3236 CorrectionTerminationTest<T>::logger
3237 << ", diverged because the normalised residue is not a "
3238 "number."
3239 << logging::LOGFLUSH;
3240 distance_to_iterative_convergence = -1;
3241 return CorrectionTerminationTest<T>::diverged;
3242 }
3243 if (dn != dn) {
3244 CorrectionTerminationTest<T>::logger
3245 << ", diverged because the normalised last correction is not "
3246 "a number."
3247 << logging::LOGFLUSH;
3248 distance_to_iterative_convergence = -1;
3249 return CorrectionTerminationTest<T>::diverged;
3250 }
3251 if (solver_hints != nullptr && (**solver_hints & SolverHints::diverge)) {
3252 distance_to_iterative_convergence = -1;
3253 CorrectionTerminationTest<T>::logger << ", elements/materials/boundary conditions "
3254 "indicated divergence."
3255 << logging::LOGFLUSH;
3256 return CorrectionTerminationTest<T>::diverged;
3257 }
3258 distance_to_iterative_convergence = std::max(rn / e_r, dn / e_d);
3259 if ((rn < e_r) && (dn < e_d)
3260 && (solver_hints == nullptr || **solver_hints == SolverHints::none)) {
3261 CorrectionTerminationTest<T>::logger << ", converged." << logging::LOGFLUSH;
3262 return CorrectionTerminationTest<T>::converged;
3263 }
3264 if (solver_hints != nullptr && (**solver_hints & SolverHints::degradation)) {
3265 const int remaining = std::max(
3266 0, max_number_of_iteration + max_additional_iterations - number_of_iteration);
3267 max_additional_iterations += std::max(0, max_number_of_iteration - remaining);
3268 number_of_divergences = 0;
3269 last_rn = std::numeric_limits<double>::max();
3270 best_rn = std::numeric_limits<double>::max();
3271 } else if ((number_of_iteration > 2 && rn > last_rn)) {
3272 if (++number_of_divergences > max_number_of_divergences) {
3273 CorrectionTerminationTest<T>::logger
3275 << ", diverged because the maximum number of consecutive "
3276 "divergences ("
3277 << max_number_of_divergences << ") has been exceeded." << logging::LOGFLUSH;
3278 distance_to_iterative_convergence = -1;
3279 return CorrectionTerminationTest<T>::diverged;
3280 }
3281 last_rn = rn;
3282 best_rn = std::min(best_rn, rn);
3283 } else {
3284 number_of_divergences = 0;
3285 last_rn = rn;
3286 best_rn = std::min(best_rn, rn);
3287 }
3288
3289 if (number_of_iteration > max_number_of_iteration + max_additional_iterations) {
3290 CorrectionTerminationTest<T>::logger
3291 << ", diverged because the number of correction iterations "
3292 "exceeds the maximum ("
3293 << max_number_of_iteration;
3294 if (max_additional_iterations > 0) {
3295 CorrectionTerminationTest<T>::logger << "+" << max_additional_iterations;
3296 }
3297 CorrectionTerminationTest<T>::logger << ")." << logging::LOGFLUSH;
3298 return CorrectionTerminationTest<T>::max_iteration;
3299 }
3300 if (number_of_iteration > 1 && rn > g_r) {
3301 CorrectionTerminationTest<T>::logger
3302 << ", diverged because the normalised residue exceeds the "
3303 "maximum ("
3304 << g_r << ")." << logging::LOGFLUSH;
3305 return CorrectionTerminationTest<T>::diverged;
3306 }
3307 if (number_of_iteration > 1 && dn > g_d) {
3308 CorrectionTerminationTest<T>::logger
3309 << ", diverged because the normalised last correction exceeds "
3310 "the maximum ("
3311 << g_d << ")." << logging::LOGFLUSH;
3312 return CorrectionTerminationTest<T>::diverged;
3313 }
3314 if (solver_hints != nullptr && (**solver_hints & SolverHints::degradation)) {
3315 CorrectionTerminationTest<T>::logger
3316 << ", elements/materials/boundary conditions indicated "
3317 "degradation";
3318 } else if (
3319 distance_to_iterative_convergence >= 0.0 && distance_to_iterative_convergence <= 1.0
3320 && solver_hints != nullptr && (**solver_hints & SolverHints::unfinished)) {
3321 CorrectionTerminationTest<T>::logger
3322 << ", elements/materials/boundary conditions indicated "
3323 "need for further Newton iterations";
3324 }
3325 CorrectionTerminationTest<T>::logger << ", unfinished." << logging::LOGFLUSH;
3326 return CorrectionTerminationTest<T>::unfinished;
3327 }
3328
3329 using type_t = ObjectTypeComplete<
3330 NormalizedDofAndResidueCorrectionTerminationTest,
3331 typename CorrectionTerminationTest<T>::type_t>;
3332 static type_t type;
3333
3334private:
3335 double e_r;
3336 double e_d;
3337 double g_r;
3338 double g_d;
3339 double r_0;
3340 double d_0;
3341 double last_rn;
3342 double best_rn;
3343 int max_number_of_iteration;
3344 int max_additional_iterations;
3345 int number_of_divergences;
3346 int max_number_of_divergences;
3347};
3348
3349template <typename T>
3350typename NormalizedDofAndResidueCorrectionTerminationTest<T>::type_t
3351 NormalizedDofAndResidueCorrectionTerminationTest<T>::type(
3352 "NormalizedDofAndResidueCorrectionTerminationTest", type_name<T>(),
3353 StringList("NORMALISED_DOF_AND_RESIDUE_CORRECTION_TERMINATION_TEST"), module);
3354
3355template <typename T>
3356class RegularizedCorrectionTerminationTest : public CorrectionTerminationTest<T> {
3357public:
3358 void init(Model& model, const AttributeList& attribute) {
3359 e_r = attribute.get_double("TOL_RESIDUUM", 1e-3);
3360 e_d = attribute.get_double("TOL_SOLUTION", 1e-3);
3361 e_w = attribute.get_double("TOL_WORK", 1e-7);
3362 max_number_of_iteration = attribute.get_int("MAX_NEWTON_ITERATIONS", 50);
3363 max_divergence = attribute.get_int("MAX_DIVERGENCES", 4);
3364 max_additional_iterations = 0;
3365 }
3366
3367 double norm_termination(
3368 const b2linalg::Vector<T, b2linalg::Vdense_constref>& r,
3369 const b2linalg::Vector<T, b2linalg::Vdense_constref>& delta_dof,
3370 const double delta_dof_scale, const b2linalg::Vector<T, b2linalg::Vdense_constref>& dof,
3371 const double constraint, const double d_constraint_d_time, const double delta_time,
3372 const double time, StaticResidueFunctionForTerminationTest<T>& residue,
3373 const b2linalg::Vector<T, b2linalg::Vindex1_constref>& diag_K) {
3374 const double w_normalization = residue.get_energy_normalisation1();
3375 double w = 0;
3376 double p = 0;
3377 double norm1_d_dof = 0;
3378 double diag_d_dof = 0;
3379 double diag_dof = 0;
3380 for (size_t i = 0; i != delta_dof.size(); ++i) {
3381 const double ddof = delta_dof[i] * delta_dof_scale;
3382 w += norm(r[i] * ddof);
3383 p += norm(r[i] * dof[i]);
3384 norm1_d_dof += norm(ddof);
3385 const double sqrt_diag_K = std::sqrt(norm(diag_K[i]));
3386 diag_d_dof += norm(sqrt_diag_K * ddof);
3387 diag_dof += norm(sqrt_diag_K * dof[i]);
3388 }
3389 w += norm(constraint * delta_time);
3390 p += norm(constraint * time);
3391 norm1_d_dof += norm(delta_time);
3392 diag_d_dof += norm(std::sqrt(norm(d_constraint_d_time)) * delta_time);
3393 diag_dof += norm(std::sqrt(norm(d_constraint_d_time)) * time);
3394 double q;
3395 if (last_norm1_d_dof < 1e-15) {
3396 q = 0;
3397 } else {
3398 q = 2 * norm1_d_dof / (3 * last_norm1_d_dof) + last_q / 3;
3399 }
3400 double e_dof;
3401 if (diag_dof < 1e-15) {
3402 e_dof = 0;
3403 } else if (q >= 0.99) {
3404 e_dof = q * diag_d_dof / (0.01 * diag_dof);
3405 } else {
3406 e_dof = q * diag_d_dof / ((1 - q) * diag_dof);
3407 }
3408 e_dof /= e_d;
3409 const double e_work = w / (w_normalization * e_w);
3410 const double e_res = p / (w_normalization * e_r);
3411
3412 return std::max(e_work, std::max(e_res, e_dof));
3413 }
3414
3415 typename CorrectionTerminationTest<T>::Termination is_terminated(
3416 int number_of_iteration, const b2linalg::Vector<T, b2linalg::Vdense_constref>& r,
3417 const b2linalg::Vector<T, b2linalg::Vdense_constref>& delta_dof,
3418 const double delta_dof_scale, const b2linalg::Vector<T, b2linalg::Vdense_constref>& dof,
3419 const b2linalg::Vector<T, b2linalg::Vdense_constref>& dof_at_start_step,
3420 const double constraint, const double d_constraint_d_time, const double delta_time,
3421 const double time, StaticResidueFunctionForTerminationTest<T>& residue,
3422 const b2linalg::Vector<T, b2linalg::Vindex1_constref>& diag_K,
3423 double& distance_to_iterative_convergence, const SolverHints* solver_hints) {
3424 double w2 = 0;
3425 for (size_t i = 0; i != dof.size(); ++i) { w2 += norm(r[i] * r[i] * dof[i]); }
3426 w2 += norm(constraint * constraint * time);
3427 if (w2 != w2) {
3428 CorrectionTerminationTest<T>::logger
3430 << "diverged because a term of the residue or the correction is "
3431 "not a number."
3432 << logging::LOGFLUSH;
3433 return CorrectionTerminationTest<T>::diverged;
3434 }
3435 if (solver_hints != nullptr && (**solver_hints & SolverHints::diverge)) {
3436 distance_to_iterative_convergence = -1;
3437 CorrectionTerminationTest<T>::logger << logging::info
3438 << "elements/materials/boundary conditions "
3439 << "indicated divergence." << logging::LOGFLUSH;
3440 return CorrectionTerminationTest<T>::diverged;
3441 }
3442 if (number_of_iteration == 1) {
3443 last_q = 0.99;
3444 last_w2 = w2;
3445 nb_divergence = 0;
3446 last_norm1_d_dof = 0;
3447 for (size_t i = 0; i != delta_dof.size(); ++i) {
3448 last_norm1_d_dof += norm(delta_dof[i] * delta_dof_scale);
3449 }
3450 distance_to_iterative_convergence = -1;
3451 return CorrectionTerminationTest<T>::unfinished;
3452 }
3453 {
3454 const double divergence = w2 == 0 ? 0 : (last_w2 == 0 ? w2 : w2 / last_w2);
3455 if (solver_hints != nullptr && (**solver_hints & SolverHints::degradation)) {
3456 nb_divergence = 0;
3457 } else if (divergence > 1 + 1e-5 || divergence < -1e12) {
3458 nb_divergence += 2;
3459 } else if (divergence < -1) {
3460 nb_divergence += 1;
3461 } else {
3462 nb_divergence = 0;
3463 }
3464 // std::cout << "w2=" << w2 << ", divergence=" << divergence << ", nb_divergence=" <<
3465 // nb_divergence << std::endl;
3466 if (nb_divergence > max_divergence) {
3467 CorrectionTerminationTest<T>::logger
3469 << "diverged because the maximum number of consecutive "
3470 "divergences ("
3471 << max_divergence << ") has been exceeded." << logging::LOGFLUSH;
3472 distance_to_iterative_convergence = -1;
3473 return CorrectionTerminationTest<T>::diverged;
3474 }
3475 last_w2 = w2;
3476 }
3477 const double w_normalization = residue.get_energy_normalisation1();
3478 double w = 0;
3479 double p = 0;
3480 double norm1_d_dof = 0;
3481 double diag_d_dof = 0;
3482 double diag_dof = 0;
3483 for (size_t i = 0; i != delta_dof.size(); ++i) {
3484 const double ddof = delta_dof[i] * delta_dof_scale;
3485 w += norm(r[i] * ddof);
3486 p += norm(r[i] * dof[i]);
3487 norm1_d_dof += norm(ddof);
3488 const double sqrt_diag_K = std::sqrt(norm(diag_K[i]));
3489 diag_d_dof += norm(sqrt_diag_K * ddof);
3490 diag_dof += norm(sqrt_diag_K * dof[i]);
3491 }
3492 w += norm(constraint * delta_time);
3493 p += norm(constraint * time);
3494 norm1_d_dof += norm(delta_time);
3495 diag_d_dof += norm(std::sqrt(norm(d_constraint_d_time)) * delta_time);
3496 diag_dof += norm(std::sqrt(norm(d_constraint_d_time)) * time);
3497 double q;
3498 if (last_norm1_d_dof < 1e-15) {
3499 q = 0;
3500 } else {
3501 q = 2 * norm1_d_dof / (3 * last_norm1_d_dof) + last_q / 3;
3502 }
3503 double e_dof;
3504 if (diag_dof < 1e-15) {
3505 e_dof = 0;
3506 } else if (q >= 0.99) {
3507 e_dof = q * diag_d_dof / (0.01 * diag_dof);
3508 } else {
3509 e_dof = q * diag_d_dof / ((1 - q) * diag_dof);
3510 }
3511 const double e_work = w / w_normalization;
3512 const double e_res = p / w_normalization;
3513 distance_to_iterative_convergence =
3514 std::max(e_work / e_w, std::max(e_res / e_r, e_dof / e_d));
3515 if (e_work < e_w && e_res < e_r && e_dof < e_d
3516 && (solver_hints == nullptr || **solver_hints == SolverHints::none)) {
3517 CorrectionTerminationTest<T>::logger << logging::debug << "converged."
3518 << logging::LOGFLUSH;
3519 return CorrectionTerminationTest<T>::converged;
3520 }
3521 if (solver_hints != nullptr && (**solver_hints & SolverHints::degradation)) {
3522 const int remaining = std::max(
3523 0, max_number_of_iteration + max_additional_iterations - number_of_iteration);
3524 max_additional_iterations += std::max(0, max_number_of_iteration - remaining);
3525 }
3526 if (number_of_iteration > max_number_of_iteration + max_additional_iterations) {
3527 CorrectionTerminationTest<T>::logger
3529 << "diverged because the number of correction iterations "
3530 "exceeds the maximum ("
3531 << max_number_of_iteration;
3532 if (max_additional_iterations > 0) {
3533 CorrectionTerminationTest<T>::logger << "+" << max_additional_iterations;
3534 }
3535 CorrectionTerminationTest<T>::logger << ")." << logging::LOGFLUSH;
3536 return CorrectionTerminationTest<T>::max_iteration;
3537 }
3538
3539 last_norm1_d_dof = norm1_d_dof;
3540 last_q = q;
3541 if (solver_hints != nullptr && (**solver_hints & SolverHints::degradation)) {
3542 CorrectionTerminationTest<T>::logger
3543 << logging::info
3544 << "elements/materials/boundary conditions indicated "
3545 "degradation, ";
3546 } else if (
3547 distance_to_iterative_convergence >= 0.0 && distance_to_iterative_convergence <= 1.0
3548 && solver_hints != nullptr && (**solver_hints & SolverHints::unfinished)) {
3549 CorrectionTerminationTest<T>::logger
3550 << logging::info
3551 << "elements/materials/boundary conditions indicated "
3552 "need for further Newton iterations, ";
3553 }
3554 CorrectionTerminationTest<T>::logger
3555 << logging::debug << "unfinished, iter=" << number_of_iteration
3556 << ", e_work=" << e_work << ", e_residue=" << e_res << ", e_dof=" << e_dof << "."
3557 << logging::LOGFLUSH;
3558 return CorrectionTerminationTest<T>::unfinished;
3559 }
3560
3561 int get_nb_divergence() const { return nb_divergence; }
3562
3563 using type_t = ObjectTypeComplete<
3564 RegularizedCorrectionTerminationTest, typename CorrectionTerminationTest<T>::type_t>;
3565 static type_t type;
3566
3567private:
3568 double e_r;
3569 double e_d;
3570 double e_w;
3571 double last_w2;
3572 double last_norm1_d_dof;
3573 double last_q;
3574 int nb_divergence;
3575 int max_divergence;
3576 int max_number_of_iteration;
3577 int max_additional_iterations;
3578};
3579
3580template <typename T>
3581typename RegularizedCorrectionTerminationTest<T>::type_t
3582 RegularizedCorrectionTerminationTest<T>::type(
3583 "RegularizedCorrectionTerminationTest", type_name<T>(),
3584 StringList("REGULARISED_CORRECTION_TERMINATION_TEST"), module);
3585
3586template <typename T>
3587class FluxNormalizedCorrectionTerminationTest : public CorrectionTerminationTest<T> {
3588public:
3589 void init(Model& model, const AttributeList& attribute) {
3590 min_number_of_iteration = attribute.get_int("MIN_NEWTON_ITERATIONS", 0);
3591 max_number_of_iteration = attribute.get_int("MAX_NEWTON_ITERATIONS", 50);
3592 max_additional_iteration = 0;
3593 max_divergence = attribute.get_int("MAX_DIVERGENCES", 4);
3594 I0 = attribute.get_int("MIN_NEWTON_ITERATIONS_CHECK_DIVERGENCE", 4);
3595 I0_effective = I0;
3596 tol_work_lagrange = attribute.get_double(
3597 "TOL_WORK.LAGRANGE_MULTIPLIER",
3598 attribute.get_double("TOL_WORK.LAGRANGE", attribute.get_double("TOL_WORK", 1e-7)));
3599 const std::pair<std::map<std::string, size_t>, b2linalg::Index>& df =
3600 model.get_domain().get_dof_field();
3601 df_index = &df.second;
3602
3603 size_t s = 0;
3604 for (std::map<std::string, size_t>::const_iterator i = df.first.begin();
3605 i != df.first.end(); ++i) {
3606 s = std::max(s, i->second);
3607 }
3608
3609 list_field.resize(s + 1);
3610 for (std::map<std::string, size_t>::const_iterator i = df.first.begin();
3611 i != df.first.end(); ++i) {
3612 list_field[i->second].init(i->first, attribute);
3613 }
3614 }
3615
3616 void new_step() {
3617 for (typename std::vector<Field>::iterator i = list_field.begin(); i != list_field.end();
3618 ++i) {
3619 i->new_step();
3620 }
3621 }
3622
3623 void new_iteration() {
3624 for (typename std::vector<Field>::iterator i = list_field.begin(); i != list_field.end();
3625 ++i) {
3626 i->new_iteration();
3627 }
3628 }
3629
3630 void new_evaluation() {
3631 for (typename std::vector<Field>::iterator i = list_field.begin(); i != list_field.end();
3632 ++i) {
3633 i->new_evaluation();
3634 }
3635 }
3636
3637 void converged_step() {
3638 for (typename std::vector<Field>::iterator i = list_field.begin(); i != list_field.end();
3639 ++i) {
3640 i->converged_step();
3641 }
3642 }
3643
3644 bool need_flux() { return true; }
3645
3646 void add_flux(b2linalg::Index& index, const b2linalg::Vector<T, b2linalg::Vdense>& v) {
3647 for (size_t i = 0; i != index.size(); ++i) {
3648 list_field[(*df_index)[index[i]]].add_flux(norm(v[i]));
3649 }
3650 }
3651
3652 void add_flux(const b2linalg::Vector<T, b2linalg::Vdense_constref>& v) {
3653 for (size_t i = 0; i != v.size(); ++i) { list_field[(*df_index)[i]].add_flux(norm(v[i])); }
3654 }
3655
3656 typename CorrectionTerminationTest<T>::Termination is_terminated(
3657 int number_of_iteration, const b2linalg::Vector<T, b2linalg::Vdense_constref>& r,
3658 const b2linalg::Vector<T, b2linalg::Vdense_constref>& delta_dof,
3659 const double delta_dof_scale, const b2linalg::Vector<T, b2linalg::Vdense_constref>& dof,
3660 const b2linalg::Vector<T, b2linalg::Vdense_constref>& dof_at_start_step,
3661 const double constraint, const double d_constraint_d_time, const double delta_time,
3662 const double time, StaticResidueFunctionForTerminationTest<T>& residue,
3663 const b2linalg::Vector<T, b2linalg::Vindex1_constref>& diag_K,
3664 double& distance_to_iterative_convergence, const SolverHints* solver_hints) {
3665 distance_to_iterative_convergence = 0;
3666 // TODO optimise to not copy d_value_d_dof_trans each time
3667 b2linalg::Matrix<T, b2linalg::Mcompressed_col> d_value_d_dof_trans;
3668 residue.mrbc_get_nonlinear_value(
3669 dof, time, false, b2linalg::Vector<T, b2linalg::Vdense_ref>::null,
3670 d_value_d_dof_trans, b2linalg::Vector<T, b2linalg::Vdense_ref>::null, 0);
3671 b2linalg::Vector<T> tmp(d_value_d_dof_trans.size2());
3672 b2linalg::Interval dof_int(0, d_value_d_dof_trans.size1());
3673
3674 tmp = transposed(d_value_d_dof_trans) * r(dof_int);
3675 std::vector<T> r_max(list_field.size());
3676 for (size_t i = 0; i != df_index->size(); ++i) {
3677 const size_t j = (*df_index)[i];
3678 if (r_max[j] == r_max[j]) {
3679 if (tmp[i] == tmp[i]) {
3680 r_max[j] = std::max(r_max[j], norm(tmp[i]));
3681 } else {
3682 r_max[j] = norm(tmp[i]);
3683 }
3684 }
3685 }
3686
3687 tmp = transposed(d_value_d_dof_trans) * delta_dof(dof_int);
3688 std::vector<T> delta_dof_max(list_field.size());
3689 for (size_t i = 0; i != df_index->size(); ++i) {
3690 const size_t j = (*df_index)[i];
3691 if (delta_dof_max[j] == delta_dof_max[j]) {
3692 if (tmp[i] == tmp[i]) {
3693 delta_dof_max[j] = std::max(delta_dof_max[j], norm(tmp[i]));
3694 } else {
3695 delta_dof_max[j] = norm(tmp[i]);
3696 }
3697 }
3698 }
3699
3700 tmp = transposed(d_value_d_dof_trans) * dof_at_start_step(dof_int);
3701 std::vector<T> step_dof_max(list_field.size());
3702 for (size_t i = 0; i != df_index->size(); ++i) {
3703 const size_t j = (*df_index)[i];
3704 if (step_dof_max[j] == step_dof_max[j]) {
3705 if (tmp[i] == tmp[i]) {
3706 step_dof_max[j] = std::max(step_dof_max[j], norm(tmp[i]));
3707 } else {
3708 step_dof_max[j] = norm(tmp[i]);
3709 }
3710 }
3711 }
3712
3713 bool degradation = false;
3714 bool diverged = false;
3715 bool unfinished = false;
3716
3717 // Check for solver hints and do logging.
3718 CorrectionTerminationTest<T>::logger
3719 << logging::debug << "Number of correction iterations = " << number_of_iteration;
3720 if (solver_hints != nullptr && (**solver_hints & SolverHints::degradation)) {
3721 degradation = unfinished = true;
3722
3723 // Allow more iterations.
3724 const int remaining = std::max(
3725 0, max_number_of_iteration + max_additional_iteration - number_of_iteration);
3726 max_additional_iteration += std::max(0, max_number_of_iteration - remaining);
3727
3728 // Divergence check resumes in I0 iterations.
3729 I0_effective = I0 + number_of_iteration;
3730
3731 CorrectionTerminationTest<T>::logger
3732 << ", elements/materials/boundary conditions indicated "
3733 "degradation";
3734 } else if (solver_hints != nullptr && (**solver_hints & SolverHints::unfinished)) {
3735 unfinished = true;
3736 CorrectionTerminationTest<T>::logger
3737 << ", elements/materials/boundary conditions indicated "
3738 "need for further Newton iterations";
3739 }
3740 CorrectionTerminationTest<T>::logger << "." << logging::LOGFLUSH;
3741
3742 // Check convergence of the fields.
3743 for (size_t i = 0; i != list_field.size(); ++i) {
3744 if (!list_field[i].is_init()) { continue; }
3745 typename CorrectionTerminationTest<T>::Termination r = list_field[i].is_terminated(
3746 *this, number_of_iteration, r_max[i], step_dof_max[i],
3747 delta_dof_max[i] * delta_dof_scale, distance_to_iterative_convergence,
3748 solver_hints);
3749 if (!degradation && r == CorrectionTerminationTest<T>::diverged) { diverged = true; }
3750 if (r == CorrectionTerminationTest<T>::unfinished) { unfinished = true; }
3751 }
3752
3753 // check the work one the lagrange multiplier
3754 if (residue.get_number_of_non_lagrange_dof() != dof.size()) {
3755 double max_l_work = 0;
3756 for (size_t i = residue.get_number_of_non_lagrange_dof(); i != dof.size(); ++i) {
3757 max_l_work = std::max(max_l_work, norm(r[i] * delta_dof[i]));
3758 }
3759 max_l_work *= delta_dof_scale;
3760 CorrectionTerminationTest<T>::logger
3761 << logging::debug << "Field Lagrange multiplier, work_max = " << max_l_work;
3762 if (max_l_work > tol_work_lagrange) {
3763 unfinished = true;
3764 CorrectionTerminationTest<T>::logger << ", unfinished.";
3765 } else {
3766 CorrectionTerminationTest<T>::logger << ", converged.";
3767 }
3768 CorrectionTerminationTest<T>::logger << logging::LOGFLUSH;
3769 distance_to_iterative_convergence =
3770 std::max(distance_to_iterative_convergence, max_l_work / tol_work_lagrange);
3771 }
3772
3773 if (diverged) {
3774 distance_to_iterative_convergence = -1;
3775 return CorrectionTerminationTest<T>::diverged;
3776 }
3777 if (!unfinished && number_of_iteration < min_number_of_iteration) { unfinished = true; }
3778 if (unfinished) {
3779 if (number_of_iteration > max_number_of_iteration + max_additional_iteration) {
3780 CorrectionTerminationTest<T>::logger
3782 << "diverged because the number of correction iterations "
3783 "exceeds the maximum ("
3784 << max_number_of_iteration;
3785 if (max_additional_iteration > 0) {
3786 CorrectionTerminationTest<T>::logger << "+" << max_additional_iteration;
3787 }
3788 CorrectionTerminationTest<T>::logger << ")." << logging::LOGFLUSH;
3789 return CorrectionTerminationTest<T>::diverged;
3790 }
3791 return CorrectionTerminationTest<T>::unfinished;
3792 }
3793 return CorrectionTerminationTest<T>::converged;
3794 }
3795
3796 int get_nb_divergence() const { return 0; }
3797
3798 using type_t = ObjectTypeComplete<
3799 FluxNormalizedCorrectionTerminationTest, typename CorrectionTerminationTest<T>::type_t>;
3800 static type_t type;
3801
3802private:
3803 int I0;
3804 int I0_effective;
3805 int min_number_of_iteration;
3806 int max_number_of_iteration;
3807 int max_additional_iteration;
3808 int max_divergence;
3809 double tol_work_lagrange;
3810 const b2linalg::Index* df_index;
3811
3812 class Field {
3813 public:
3814 Field()
3815 : is_init_(false),
3816 Rn(0),
3817 Rm(0),
3818 Rp(0),
3819 Ip(0),
3820 Rl(0),
3821 Cn(0),
3822 Cm(0),
3823 q_averaged_0(0),
3824 q_averaged_n(0),
3825 epsilon(0),
3826 epsilon_l(0),
3827 Cepsilon(0),
3828 delta_dof_max(0),
3829 delta_dof_max_in_step(0),
3830 nb_flux(0),
3831 q_time_nb_flux(0),
3832 nb_flux_active(0),
3833 q_time_nb_flux_active(0),
3834 q_averaged_nb_step(0),
3835 q_averaged_time_nb_step(0),
3836 q_max(0),
3837 q_max_averaged(0),
3838 nonquadratic_convergence(0),
3839 nb_divergence(0),
3840 divergence_in_step(false) {}
3841
3842 void init(const std::string& name_, const AttributeList& attribute) {
3843 is_init_ = true;
3844 name = name_;
3845 Rn = attribute.get_double(
3846 "TOL_RESIDUUM." + name_, attribute.get_double("TOL_RESIDUUM", 1e-5));
3847 Rm = std::max(
3848 Rn, attribute.get_double(
3849 "MAX_RESIDUUM." + name_, attribute.get_double("MAX_RESIDUUM", 1e16)));
3850 Rp = attribute.get_double(
3851 "TOL_RESIDUUM_NONQUADRATIC_CONVERGENCE." + name_,
3852 attribute.get_double("TOL_RESIDUUM_NONQUADRATIC_CONVERGENCE", 2e-2));
3853 Ip = attribute.get_int(
3854 "NB_ITER_NONQUADRATIC_CONVERGENCE." + name_,
3855 attribute.get_int("NB_ITER_NONQUADRATIC_CONVERGENCE", 9));
3856 Ip = std::max(size_t(3), Ip);
3857 Rl = attribute.get_double(
3858 "TOL_RESIDUUM_LINEAR_PROBLEM." + name_,
3859 attribute.get_double("TOL_RESIDUUM_LINEAR_PROBLEM", 1e-8));
3860 Cn = attribute.get_double(
3861 "TOL_SOLUTION." + name_, attribute.get_double("TOL_SOLUTION", 1e-5));
3862 Cm = std::max(
3863 Cn, attribute.get_double(
3864 "MAX_SOLUTION." + name_, attribute.get_double("MAX_SOLUTION", 1e16)));
3865 q_averaged_0 = attribute.get_double(
3866 "INITIAL_AVERAGE_FLUX." + name_,
3867 attribute.get_double("INITIAL_AVERAGE_FLUX", 1e-2));
3868 q_averaged_n = attribute.get_double(
3869 "AVERAGE_FLUX." + name, attribute.get_double("AVERAGE_FLUX", -1));
3870 epsilon = attribute.get_double(
3871 "ZERO_FLUX_CRITERION." + name_,
3872 attribute.get_double("ZERO_FLUX_CRITERION", 1e-5));
3873 epsilon_l = attribute.get_double(
3874 "ZERO_FLUX_RELATIVE_CRITERION." + name_,
3875 attribute.get_double("ZERO_FLUX_RELATIVE_CRITERION", 1e-5));
3876 Cepsilon = attribute.get_double(
3877 "TOL_SOLUTION_ZERO_FLUX." + name_,
3878 attribute.get_double("TOL_SOLUTION_ZERO_FLUX", 1e-5));
3879 q_averaged_nb_step = 0;
3880 }
3881
3882 bool is_init() const { return is_init_; }
3883
3884 void new_step() {
3885 delta_dof_max_in_step = 0;
3886 std::fill_n(last_r_max, 3, 0);
3887 std::fill_n(best_r_max, 3, std::numeric_limits<double>::max());
3888 nonquadratic_convergence = false;
3889 nb_divergence = 0;
3890 divergence_in_step = false;
3891 }
3892
3893 void new_iteration() {
3894 last_r_max[0] = last_r_max[1];
3895 last_r_max[1] = last_r_max[2];
3896 best_r_max[0] = best_r_max[1];
3897 best_r_max[1] = best_r_max[2];
3898 }
3899
3900 void new_evaluation() {
3901 nb_flux = 0;
3902 q_time_nb_flux = 0;
3903 nb_flux_active = 0;
3904 q_time_nb_flux_active = 0;
3905 q_max = 0;
3906 }
3907
3908 void converged_step() {
3909 if (nb_flux == 0) { return; }
3910 if (q_averaged_n < 0) {
3911 double q;
3912 if (q_max >= 0.1 * q_max_averaged) {
3913 q = q_time_nb_flux_active / nb_flux_active;
3914 } else {
3915 q = q_time_nb_flux / nb_flux;
3916 }
3917 if (q_averaged_time_nb_step == 0) {
3918 if (q > epsilon * q_averaged_0) {
3919 q_averaged_nb_step = 1;
3920 q_averaged_time_nb_step = q;
3921 }
3922 } else if (q > epsilon * q_averaged_time_nb_step / q_averaged_nb_step) {
3923 ++q_averaged_nb_step;
3924 q_averaged_time_nb_step += q;
3925 }
3926 }
3927 q_max_averaged = std::max(q_max_averaged, q_max);
3928 }
3929
3930 void add_flux(const double flux) {
3931 ++nb_flux;
3932 q_time_nb_flux += flux;
3933 if (flux >= epsilon_l * q_max_averaged) {
3934 ++nb_flux_active;
3935 q_time_nb_flux_active += flux;
3936 }
3937 q_max = std::max(q_max, flux);
3938 }
3939
3940 typename CorrectionTerminationTest<T>::Termination is_terminated(
3941 FluxNormalizedCorrectionTerminationTest& parent, size_t nb_iteration, double r_max,
3942 double step_dof_max, double delta_dof_max, double& distance_to_iterative_convergence,
3943 const SolverHints* solver_hints) {
3944 if (solver_hints != nullptr && (**solver_hints & SolverHints::degradation)) {
3945 std::fill_n(best_r_max, 3, std::numeric_limits<double>::max());
3946 }
3947
3948 if (nb_flux == 0) { return CorrectionTerminationTest<T>::converged; }
3949
3950 // test on divergence
3951 if (r_max != r_max || delta_dof_max != delta_dof_max) {
3952 parent.logger << logging::debug << "Field " << name << " diverged due to nan"
3953 << logging::LOGFLUSH;
3954 return CorrectionTerminationTest<T>::diverged;
3955 }
3956 if (std::isinf(r_max) || std::isinf(delta_dof_max)) {
3957 parent.logger << logging::debug << "Field " << name << " diverged due to inf"
3958 << logging::LOGFLUSH;
3959 return CorrectionTerminationTest<T>::diverged;
3960 }
3961
3962 if (nb_iteration > size_t(parent.I0_effective) && r_max > best_r_max[1] * 1.00001) {
3963 nb_divergence += 1;
3964 if (nb_divergence > parent.max_divergence) {
3965 parent.logger << logging::debug << "Field " << name
3966 << ", r_max = " << std::min(r_max, last_r_max[1])
3967 << ", previous r_max = " << last_r_max[1]
3968 << ", best r_max = " << best_r_max[1] << ", diverged after "
3969 << nb_divergence << " consecutive divergence(s)."
3970 << logging::LOGFLUSH;
3971 return CorrectionTerminationTest<T>::diverged;
3972 }
3973 divergence_in_step = true;
3974 } else {
3975 nb_divergence = 0;
3976 }
3977
3978 if (nb_iteration > 1 && r_max > Rm) {
3979 parent.logger << logging::debug << "Field " << name
3980 << ", r_max = " << std::min(r_max, last_r_max[1])
3981 << ", previous r_max = " << last_r_max[1]
3982 << ", best r_max = " << best_r_max[1]
3983 << ", diverged because the residue exceeds the "
3984 << "maximum (" << Rm << ")." << logging::LOGFLUSH;
3985 return CorrectionTerminationTest<T>::diverged;
3986 }
3987 if (nb_iteration > 1 && step_dof_max > Cm) {
3988 parent.logger << logging::debug << "Field " << name
3989 << ", r_max = " << std::min(r_max, last_r_max[1])
3990 << ", previous r_max = " << last_r_max[1]
3991 << ", best r_max = " << best_r_max[1]
3992 << ", diverged because the last correction exceeds "
3993 << "the maximum (" << Cm << ")." << logging::LOGFLUSH;
3994 return CorrectionTerminationTest<T>::diverged;
3995 }
3996
3997 last_r_max[2] = r_max;
3998 if (nb_iteration >= size_t(parent.I0_effective)) {
3999 best_r_max[2] = std::min(best_r_max[1], r_max);
4000 }
4001 double q_averaged;
4002 if (q_averaged_n > 0) {
4003 q_averaged = q_averaged_n;
4004 } else {
4005 double q;
4006 if (q_max >= 0.1 * q_max_averaged) {
4007 q = q_time_nb_flux_active / nb_flux_active;
4008 } else {
4009 q = q_time_nb_flux / nb_flux;
4010 }
4011 if (q_averaged_nb_step == 0) {
4012 if (q > epsilon * q_averaged_0) {
4013 q_averaged = q;
4014 } else {
4015 q_averaged = q_averaged_0;
4016 }
4017 } else if (q > epsilon * q_averaged_time_nb_step / q_averaged_nb_step) {
4018 q_averaged = (q + q_averaged_time_nb_step) / (q_averaged_nb_step + 1);
4019 } else {
4020 q_averaged = q_averaged_time_nb_step / q_averaged_nb_step;
4021 }
4022 }
4023
4024 parent.logger << logging::debug << "Field " << name << ", r_max = " << r_max
4025 << ", averaged flux = " << q_averaged << ", c_max = " << delta_dof_max
4026 << ", delta_dof_max = " << step_dof_max << ", ";
4027
4028 // test on linear increment
4029 if (r_max <= Rl * q_averaged) {
4030 parent.logger << "linear increment, converged." << logging::LOGFLUSH;
4031 nb_divergence = 0;
4032 return CorrectionTerminationTest<T>::converged;
4033 }
4034
4035 // test on zero flux
4036 if (q_max <= epsilon * q_averaged
4037 && (r_max <= epsilon * q_averaged || delta_dof_max <= Cepsilon * step_dof_max)) {
4038 parent.logger << "zero flux, converged" << logging::LOGFLUSH;
4039 nb_divergence = 0;
4040 return CorrectionTerminationTest<T>::converged;
4041 }
4042
4043 // test on nonquadratic convergence
4044 if (nb_iteration > Ip) {
4045 if (!nonquadratic_convergence
4046 && std::min(r_max, last_r_max[1]) < 2 * (last_r_max[0] * last_r_max[0])) {
4047 nonquadratic_convergence = true;
4048 }
4049 if (nonquadratic_convergence && r_max <= Rp * q_averaged
4050 && delta_dof_max <= Cn * step_dof_max) {
4051 parent.logger << "nonquadratic convergence, converged." << logging::LOGFLUSH;
4052 nb_divergence = 0;
4053 return CorrectionTerminationTest<T>::converged;
4054 }
4055 }
4056
4057 distance_to_iterative_convergence =
4058 std::max(distance_to_iterative_convergence, r_max / (Rn * q_averaged));
4059 distance_to_iterative_convergence = std::max(
4060 distance_to_iterative_convergence,
4061 delta_dof_max * std::min(1.0, r_max / std::min(last_r_max[0], last_r_max[1]))
4062 / (Cn * step_dof_max));
4063
4064 if (r_max <= Rn * q_averaged
4065 && (delta_dof_max <= Cn * step_dof_max
4066 || (!divergence_in_step && nb_iteration > 2
4067 && r_max / std::min(last_r_max[0], last_r_max[1]) * delta_dof_max
4068 <= Cn * step_dof_max))) {
4069 parent.logger << "converged." << logging::LOGFLUSH;
4070 nb_divergence = 0;
4071 return CorrectionTerminationTest<T>::converged;
4072 }
4073
4074 parent.logger << "unfinished." << logging::LOGFLUSH;
4075 return CorrectionTerminationTest<T>::unfinished;
4076 }
4077
4078 private:
4079 bool is_init_;
4080 std::string name;
4081 double Rn;
4082 double Rm;
4083 double Rp;
4084 size_t Ip;
4085 double Rl;
4086 double Cn;
4087 double Cm;
4088 double q_averaged_0;
4089 double q_averaged_n;
4090 double epsilon;
4091 double epsilon_l;
4092 double Cepsilon;
4093
4094 double delta_dof_max;
4095 double delta_dof_max_in_step;
4096
4097 size_t nb_flux;
4098 double q_time_nb_flux;
4099 size_t nb_flux_active;
4100 double q_time_nb_flux_active;
4101 size_t q_averaged_nb_step;
4102 double q_averaged_time_nb_step;
4103 double q_max;
4104 double q_max_averaged;
4105 bool nonquadratic_convergence;
4106 double last_r_max[3];
4107 double best_r_max[3];
4108 int nb_divergence;
4109 bool divergence_in_step;
4110 };
4111
4112 friend class Field;
4113 std::vector<Field> list_field;
4114};
4115
4116template <typename T>
4117typename FluxNormalizedCorrectionTerminationTest<T>::type_t
4118 FluxNormalizedCorrectionTerminationTest<T>::type(
4119 "FluxNormalizedCorrectionTerminationTest", type_name<T>(),
4120 StringList("FLUX_NORMALISED_CORRECTION_TERMINATION_TEST"), module);
4121
4122class StrongWolfeLineSearch : public LineSearch {
4123public:
4124 StrongWolfeLineSearch() : max_iter(8), c1(0.3), c2(0.9), h_derivative(0.01) {}
4125
4126 void init(Model& model, const AttributeList& attribute) override;
4127
4128 double get_minimum(LineSearch::Function& g, double g0, double g1) override;
4129
4130 using type_t = ObjectTypeComplete<StrongWolfeLineSearch, LineSearch::type_t>;
4131 static type_t type;
4132
4133private:
4134 void clear_interpolation() { value.clear(); }
4135
4136 void add_value(double h, double v) { value[h] = v; }
4137
4138 double get_minimum_interpolation(double low, double hig);
4139
4140 using value_t = std::map<double, double>;
4141 value_t value;
4142
4143 static double quadratic_interpolation_minimisation(
4144 double a1, double g_a1, double dg_a1, double a2, double g_a2) {
4145 double a2_ = a2 - a1;
4146 return a1 + dg_a1 * a2_ * a2_ * 0.5 / (g_a2 - g_a1 - dg_a1 * a2_);
4147 }
4148
4149 static double cubic_interpolation_minimisation(
4150 double a1, double g_a1, double dg_a1, double a2, double g_a2, double a3, double g_a3) {
4151 // todo
4152 return 0;
4153 }
4154
4155 int max_iter;
4156 double c1; // sufficient decrease
4157 double c2; // curvature condition
4158 double h_derivative;
4159};
4160
4161}} // namespace b2000::solver
4162
4163#endif // B2STATIC_NONLINEAR_UTILE_H_
csda< T > norm(const csda< T > &a)
Definition b2csda.H:343
#define THROW
Definition b2exception.H:198
Interface to C++ representations of FE solvers.
@ diverge
Definition b2solver.H:183
@ none
Definition b2solver.H:173
@ unfinished
Definition b2solver.H:179
@ degradation
Definition b2solver.H:188
Logger & get_logger(const std::string &logger_name="")
Definition b2logging.H:829
Contains the base classes for implementing Finite Elements.
Definition b2boundary_condition.H:32
GenericException< KeyError_name > KeyError
Definition b2exception.H:320
void central_numerical_differentiation(FUNCTION &f, const double x, std::vector< T > &res, int derivative_order=1, int approximation_order=2, int first_nonzero_derivative_order=0, double h=0)
Definition b2numerical_differentiation.H:79
GenericException< UnimplementedError_name > UnimplementedError
Definition b2exception.H:314