b2api
B2000++ API Reference Manual, VERSION 4.6
 
Loading...
Searching...
No Matches
b2boundary_condition_nlr_coupling.H
1//------------------------------------------------------------------------
2// b2boundary_condition_database.C --
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) 2012 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#ifndef B2BOUNDARY_CONDITION_NLR_COUPLING_H_
22#define B2BOUNDARY_CONDITION_NLR_COUPLING_H_
23
24#include <set>
25
26#include "b2model_database.H"
27#include "b2solution_database.H"
28#include "model/b2case.H"
29#include "model/b2domain.H"
30#include "model/b2element.H"
31#include "model_imp/b2boundary_condition_component.H"
32#include "utils/b2linear_algebra.H"
33
34// #define BCNC_CHECK_DERIVATIVE 1
35// #define CHECK_GLOBAL_TO_LOCAL_MAPPING 1e-5
36
37namespace b2000::b2dbv3 {
38
39class StaticResidueFunctionNLRCoupling {
40public:
41 typedef double T;
42 typedef b2linalg::Msymmetric MATRIX_FORMAT;
43 typedef TypedNaturalBoundaryConditionListComponent<T, MATRIX_FORMAT::sparse_inversible>
44 nbc_list_t;
45 typedef TypedEssentialBoundaryConditionListComponent<T> ebc_list_t;
46 typedef TypedModelReductionBoundaryConditionListComponent<T> mrbc_list_t;
47
48 StaticResidueFunctionNLRCoupling()
49 : number_of_dof(0),
50 number_of_non_lagrang_dof(0),
51 model(nullptr),
52 domain(nullptr),
53 mrbc(nullptr),
54 ebc(nullptr),
55 nbc(nullptr),
56 constraint_has_penalty(false),
57 penalty_factor(0),
58 lagrange_mult_scale(0) {}
59
60 virtual ~StaticResidueFunctionNLRCoupling() {}
61
62 void init(Model& model_, const AttributeList& attribute) {
63 model = &model_;
64 domain = &model->get_domain();
65 ebc = &dynamic_cast<ebc_list_t::typed_base_t&>(
66 model->get_essential_boundary_condition(&ebc_list_t::type));
67 mrbc = &dynamic_cast<mrbc_list_t::typed_base_t&>(
68 model->get_model_reduction_boundary_condition(&mrbc_list_t::type));
69 nbc = &dynamic_cast<nbc_list_t::typed_base_t&>(
70 model->get_natural_boundary_condition(&nbc_list_t::type));
71 nbc_sol.resize(domain->get_number_of_dof());
72 fi.resize(domain->get_number_of_dof());
73
74 constraint_has_penalty =
75 attribute.get_string("CONSTRAINT_METHOD", "REDUCTION") == "PENALTY";
76
77 constraint_has_penalty |=
78 attribute.get_string("NONLINEAR_CONSTRAINT_METHOD", "OTHER") == "PENALTY";
79
80 number_of_dof = number_of_non_lagrang_dof =
81 mrbc->get_size(false, b2linalg::Vector<T, b2linalg::Vdense>::null, 1);
82 if (constraint_has_penalty) {
83 penalty_factor = attribute.get_double("CONSTRAINT_PENALTY", 1e10);
84 } else {
85 number_of_dof += ebc->get_size(false);
86 penalty_factor = attribute.get_double("CONSTRAINT_PENALTY", 1);
87 if (attribute.get_string("NONLINEAR_CONSTRAINT_METHOD", "OTHER") == "LAGRANGE") {
88 penalty_factor = 0;
89 }
90 lagrange_mult_scale = attribute.get_double("LAGRANGE_MULT_SCALE_PENALTY", 1.0);
91 }
92 }
93
94 size_t get_number_of_dof() { return number_of_dof; }
95
96 size_t get_number_of_non_lagrange_dof() { return number_of_non_lagrang_dof; }
97
98 void get_solution(
99 const b2linalg::Vector<T, b2linalg::Vdense_constref> dof, const double time,
100 EquilibriumSolution equilibrium_solution,
101 b2linalg::Vector<T, b2linalg::Vdense_ref> dof_sol) {
102 mrbc_get_nonlinear_value(
103 dof(b2linalg::Interval(
104 0, mrbc->get_size(false, b2linalg::Vector<T, b2linalg::Vdense>::null, 1))),
105 time, equilibrium_solution, dof_sol,
106 b2linalg::Matrix<T, b2linalg::Mcompressed_col>::null,
107 b2linalg::Vector<T, b2linalg::Vdense_ref>::null, nullptr);
108 }
109
110 const b2linalg::Vector<T, b2linalg::Vdense_constref> get_residuum_sol() { return fi; }
111
112 const b2linalg::Vector<T, b2linalg::Vdense_constref> get_nbc_sol() { return nbc_sol; }
113
114 T get_residue_normalization() { return std::max(fi * fi, nbc_sol * nbc_sol); }
115
116 double get_energy_normalisation1() {
117 double res = 0;
118 for (size_t i = 0; i != r.size(); ++i) {
119 res += std::max(norm(r[i] * nbc_sol[i]), norm(r[i] * fi[i]));
120 }
121 return res;
122 }
123
124 double get_energy() {
125 double res = 0;
126 for (size_t i = 0; i != r.size(); ++i) { res += r[i] * (fi[i] + nbc_sol[i]); }
127 return res;
128 }
129
130 void get_residue(
131 const b2linalg::Vector<T, b2linalg::Vdense_constref>& dof_global,
132 const b2linalg::Vector<T, b2linalg::Vdense_constref>& dof, const double time,
133 const double delta_time, const EquilibriumSolution equilibrium_solution,
134 b2linalg::Vector<T, b2linalg::Vdense_ref> residue,
135 b2linalg::Matrix<T, MATRIX_FORMAT::sparse_inversible>& d_residue_d_dof,
136 const b2linalg::Matrix<T>& C, b2linalg::Matrix<T>& RtKC,
137 b2linalg::Matrix<T, b2linalg::Mrectangle>& CtKC,
138 b2linalg::Vector<T, b2linalg::Vdense_ref> d_residue_d_time, SolverHints* solver_hints) {
139 b2linalg::Interval disp_range(
140 0, mrbc->get_size(false, b2linalg::Vector<T, b2linalg::Vdense>::null, 1));
141 b2linalg::Interval lagrange_mult_range(
142 mrbc->get_size(false, b2linalg::Vector<T, b2linalg::Vdense>::null, 1), number_of_dof);
143
144 const size_t nb_dof = domain->get_number_of_dof();
145
146 if (!d_residue_d_dof.is_null()) {
147 if (d_residue_d_dof.size1() != dof.size()) { d_residue_d_dof.resize(dof.size()); }
148 d_residue_d_dof.set_zero_same_struct();
149 RtKC.resize(dof.size(), C.size2());
150 RtKC.set_zero();
151 }
152
153 r.resize(nb_dof);
154 b2linalg::Matrix<T, b2linalg::Mcompressed_col> trans_d_r_d_dof;
155 b2linalg::Vector<T, b2linalg::Vdense> d_r_d_time(domain->get_number_of_dof());
156 mrbc_get_nonlinear_value(
157 dof(disp_range), time, equilibrium_solution, r, trans_d_r_d_dof, d_r_d_time,
158 solver_hints);
159 r += dof_global;
160
161 b2linalg::Matrix<T, MATRIX_FORMAT::sparse_inversible> d_fe_d_dof;
162 b2linalg::Vector<T, b2linalg::Vdense> d_fe_d_time(domain->get_number_of_dof());
163 nbc_sol.set_zero();
164 nbc->get_nonlinear_value(
165 r, time, equilibrium_solution, residue,
166 (d_residue_d_dof.is_null()
167 ? b2linalg::Matrix<T, MATRIX_FORMAT::sparse_inversible>::null
168 : d_fe_d_dof),
169 (d_residue_d_time.is_null() ? d_residue_d_time
170 : b2linalg::Vector<T, b2linalg::Vdense_ref>(d_fe_d_time)),
171 solver_hints);
172 fi = -nbc_sol;
173 d_fe_d_time = -d_fe_d_time;
174 if (d_fe_d_dof.size1() != 0) {
175 d_residue_d_dof += -trans_d_r_d_dof * d_fe_d_dof * transposed(trans_d_r_d_dof);
176 }
177
178 b2linalg::Matrix<T, b2linalg::Mcompressed_col> trans_d_l_d_dof;
179 b2linalg::Vector<T> l;
180
181 if (constraint_has_penalty) {
182 l.resize(ebc->get_size(false));
183 ebc->get_nonlinear_value(
184 r, time, equilibrium_solution, l, trans_d_l_d_dof,
185 b2linalg::Vector<T, b2linalg::Vdense_ref>::null, solver_hints);
186 } else {
187 ebc->get_nonlinear_value(
188 r, time, equilibrium_solution, residue(lagrange_mult_range), trans_d_l_d_dof,
189 d_residue_d_time.is_null() ? d_residue_d_time
190 : d_residue_d_time(lagrange_mult_range),
191 solver_hints);
192 if (!d_residue_d_time.is_null()) {
193 d_residue_d_time(lagrange_mult_range) +=
194 b2linalg::transposed(trans_d_l_d_dof) * d_r_d_time;
195 }
196 }
197
198 if (!d_residue_d_time.is_null() && d_fe_d_dof.size1()) {
199 d_fe_d_time -= d_fe_d_dof * d_r_d_time;
200 }
201
202 // iterate on the elements
203 {
204 domain->set_dof(r);
205 b2linalg::Index index;
206 b2linalg::Vector<T, b2linalg::Vdense> value_e;
207 b2linalg::Vector<T, b2linalg::Vdense> d_value_d_time_e;
208 std::vector<bool> d_value_d_dof_e_flags(
209 d_residue_d_dof.is_null() && d_residue_d_time.is_null() ? 0 : 1, true);
210 std::vector<b2linalg::Matrix<T, MATRIX_FORMAT::dense> > d_value_d_dof_e(
211 d_value_d_dof_e_flags.size());
212 std::unique_ptr<b2000::Domain::ElementIterator> i = domain->get_element_iterator();
213 if (K.size1() == 0) { K.resize(nb_dof, nb_dof); }
214 K.set_zero_same_struct();
215 for (Element* element = i->next(); element != nullptr; element = i->next()) {
216 if (auto te = dynamic_cast<TypedElement<T>*>(element); te != nullptr) {
217 te->get_value(
218 *model, false, equilibrium_solution, time, delta_time,
219 b2linalg::Matrix<T, b2linalg::Mrectangle_constref>(r),
220 nullptr, // gradient_container
221 solver_hints, index, value_e, d_value_d_dof_e_flags, d_value_d_dof_e,
222 (d_residue_d_time.is_null() ? b2linalg::Vector<T, b2linalg::Vdense>::null
223 : d_value_d_time_e));
224 }
225 fi(index) += value_e;
226
227 if (!d_residue_d_time.is_null()) {
228 d_value_d_time_e += d_value_d_dof_e[0] * d_r_d_time(index);
229 d_residue_d_time(index) += d_value_d_time_e;
230 }
231 if (!d_residue_d_dof.is_null()) {
232 b2linalg::Matrix<
233 T, b2linalg::Mstructured_constref<b2linalg::Mpacked_st_constref> >
234 Kelem = b2linalg::StructuredMatrix(nb_dof, index, d_value_d_dof_e[0]);
235 d_residue_d_dof +=
236 trans_d_r_d_dof * Kelem * b2linalg::transposed(trans_d_r_d_dof);
237 K += Kelem;
238 }
239 }
240 {
241 b2linalg::Vector<double> KCi;
242 b2linalg::Matrix<T, b2linalg::Msym_compressed_col_st_constref> Kref(K);
243 for (size_t j = 0; j != C.size2(); ++j) {
244 KCi = Kref * C[j];
245 RtKC[j] += trans_d_r_d_dof * KCi;
246 CtKC[j] += b2linalg::transposed(C) * KCi;
247 }
248 }
249 }
250
251 {
252 b2linalg::Vector<T> tmp = fi;
253 if (constraint_has_penalty) {
254 tmp += (trans_d_l_d_dof * l) * penalty_factor;
255 } else {
256 if (MATRIX_FORMAT::symmetric && penalty_factor != 0) {
257 // We apply penalty method with a small factor to obtain a definite positive
258 // matrix.
259 b2linalg::Vector<T> tmp1;
260 tmp1 = residue(lagrange_mult_range) * penalty_factor;
261 tmp1 += dof(lagrange_mult_range) * lagrange_mult_scale;
262 tmp += trans_d_l_d_dof * tmp1;
263 residue(lagrange_mult_range) *= lagrange_mult_scale;
264 } else {
265 tmp += lagrange_mult_scale * trans_d_l_d_dof * dof(lagrange_mult_range);
266 }
267 }
268 residue(disp_range) = trans_d_r_d_dof * tmp;
269 }
270
271 if (!d_residue_d_dof.is_null()) {
272 b2linalg::Matrix<T, b2linalg::Mcompressed_col> trans_LR;
273 trans_LR = trans_d_r_d_dof * trans_d_l_d_dof;
274 if (penalty_factor != 0 && (constraint_has_penalty || MATRIX_FORMAT::symmetric)) {
275 // We apply penalty method but with a small factor to
276 // obtain a definite positive matrix when Lagrange multipliers method is used.
277 d_residue_d_dof(disp_range, disp_range) +=
278 (trans_LR * transposed(trans_LR)) * penalty_factor;
279 }
280 if (!constraint_has_penalty) {
281 trans_LR *= lagrange_mult_scale;
282 b2linalg::Matrix<T, b2linalg::Mcompressed_col> LR = transposed(trans_LR);
283 d_residue_d_dof(lagrange_mult_range, disp_range) += LR;
284 if (!MATRIX_FORMAT::symmetric) {
285 d_residue_d_dof(disp_range, lagrange_mult_range) += trans_LR;
286 }
287 }
288 }
289
290 if (!d_residue_d_time.is_null()) {
291 d_residue_d_time(disp_range) = trans_d_r_d_dof * d_fe_d_time;
292 }
293 }
294
295 void mrbc_get_nonlinear_inverse_value(
296 const b2linalg::Vector<T, b2linalg::Vdense_constref>& dof, double time,
297 b2linalg::Vector<T, b2linalg::Vdense_ref> reduced_dof) {
298 mrbc->get_nonlinear_inverse_value(
299 b2linalg::Matrix<T, b2linalg::Mrectangle_constref>(dof), time,
300 b2linalg::Matrix<T, b2linalg::Mrectangle_ref>(reduced_dof));
301 }
302
303protected:
304 virtual void mrbc_get_nonlinear_value(
305 const b2linalg::Vector<T, b2linalg::Vdense_constref>& dof, double time,
306 EquilibriumSolution equilibrium_solution, b2linalg::Vector<T, b2linalg::Vdense_ref> value,
307 b2linalg::Matrix<T, b2linalg::Mcompressed_col>& d_value_d_dof_trans,
308 b2linalg::Vector<T, b2linalg::Vdense_ref> d_value_d_time, SolverHints* solver_hints) {
309 mrbc->get_nonlinear_value(
310 dof, time, equilibrium_solution, value, d_value_d_dof_trans,
311 b2linalg::Matrix<T, b2linalg::Mrectangle_ref>(d_value_d_time), solver_hints);
312 }
313
314 size_t number_of_dof;
315 size_t number_of_non_lagrang_dof;
316 Model* model;
317 b2000::Domain* domain;
318 TypedModelReductionBoundaryCondition<T>* mrbc;
319 ebc_list_t::typed_base_t* ebc;
320 nbc_list_t::typed_base_t* nbc;
321 b2linalg::Vector<T, b2linalg::Vdense> fi;
322 b2linalg::Vector<T, b2linalg::Vdense> nbc_sol;
323 b2linalg::Vector<T, b2linalg::Vdense> r;
324 bool constraint_has_penalty;
325 double penalty_factor;
326 double lagrange_mult_scale;
327 b2linalg::Matrix<T, MATRIX_FORMAT::sparse_inversible> K;
328};
329
330class NBC_NLR_Coupling;
331
332class SolutionStaticNonlinearNLRCoupling : public b2000::SolutionStaticNonlinear<double> {
333public:
334 SolutionStaticNonlinearNLRCoupling()
335 : nlr_coupling(nullptr), local_solution(nullptr), subcase_id(0) {}
336
337 void set_nlr_coupling(NBC_NLR_Coupling& nlr_coupling);
338
339 void set_model(b2000::Model& model) override {}
340
341 void set_case(b2000::Case& case_) override {}
342
343 void set_subcase_id(const int subcase_id_) override { subcase_id = subcase_id_; }
344
345 void set_need_refinement() override {}
346
347 b2000::SolutionStaticNonlinear<double>::gradient_container get_gradient_container() override {
348 return local_solution->get_gradient_container();
349 }
350
351 void new_cycle(bool end_of_stage = false) override { local_solution->new_cycle(end_of_stage); }
352
353 void terminate_cycle(double time, double dtime, const RTable& attributes) override {
354 local_solution->terminate_cycle(time, dtime, attributes);
355 }
356
357 void terminate_stage(bool success = true) override;
358
359 void terminate_case(bool success, const RTable& attributes) override;
360
361 int get_subcase_id() const { return subcase_id; }
362
363 size_t get_cycle_id() override { return local_solution->get_cycle_id(); }
364
365 size_t get_subcycle_id() override { return local_solution->get_subcycle_id(); }
366
367 std::string get_domain_state_id() override { return local_solution->get_domain_state_id(); }
368
369 void set_dof(
370 double time, const b2linalg::Vector<double, b2linalg::Vdense_constref>& dof,
371 const b2linalg::Vector<double, b2linalg::Vdense_constref>& nbc,
372 const b2linalg::Vector<double, b2linalg::Vdense_constref>& residue,
373 bool unconverged_subcycle = false) override;
374
375 void set_equilibrium(
376 double time, const b2linalg::Vector<double, b2linalg::Vdense_constref>& dof) override {}
377
378 void get_dof(double& time, b2linalg::Vector<double, b2linalg::Vdense_ref> dof) override {
380 }
381
382private:
383 NBC_NLR_Coupling* nlr_coupling;
384 b2000::b2dbv3::SolutionStaticNonlinear<double>* local_solution;
385 int subcase_id;
386};
387
388class NBC_NLR_Coupling : public TypedNaturalBoundaryConditionComponent<
389 double, b2linalg::Msym_compressed_col_update_inv> {
390public:
391 NBC_NLR_Coupling() : local_case(nullptr), global_model(nullptr), C_linear(true) {}
392
393 void init(
394 const std::string& id_, TimeFunction* scalef_, b2000::Model& model_,
395 const b2000::Case& case_, const int subcase_id_) override {
396 id = id_;
397 global_model = &model_;
398 subcase_id = subcase_id_;
399 }
400
401 std::string get_id() const override { return id; }
402
403 int get_subcase_id() const override { return subcase_id; }
404
405 NaturalBoundaryCondition::Type get_nbc_type() const override {
407 }
408
409 void add_nonlinear_nbc_value(
410 const b2linalg::Vector<double, b2linalg::Vdense_constref>& global_dof_all, double time,
411 EquilibriumSolution equilibrium_solution,
412 b2linalg::Vector<double, b2linalg::Vdense_ref> value_,
413 b2linalg::Matrix<double, b2linalg::Msym_compressed_col_update_inv>& d_value_d_dof,
414 b2linalg::Vector<double, b2linalg::Vdense_ref> d_value_d_time,
415 SolverHints* solver_hints) override {
416 // warning: the ebv values are subtracted to the internal force of the model
417
418 if (C.size1() == 0) { update_local_model(); }
419
420 if (equilibrium_solution.input_level == 0) {
421 prev_global_dof = conv_global_dof;
422 prev_local_dof = conv_local_dof;
423 }
424 b2linalg::Vector<double> local_dof = prev_local_dof;
425
426 b2linalg::Vector<double> global_dof = global_dof_all(C_index);
427 // update the local dof using the delta of the global dof
428 {
429 if (prev_global_dof.size() != 0) {
430 b2linalg::Vector<double> delta_global_dof;
431 delta_global_dof = prev_global_dof - global_dof;
432 b2linalg::Vector<double> delta_d;
433 delta_d = local_RtKC * delta_global_dof;
434 delta_d = b2linalg::inverse(local_RtKR) * delta_d;
435 local_dof += delta_d;
436 }
437 if (equilibrium_solution.output_level == 0) { conv_global_dof = global_dof; }
438 if (equilibrium_solution.output_level != -1) { prev_global_dof = global_dof; }
439 }
440
441 b2linalg::Matrix<double, b2linalg::Mrectangle> local_CtKC(C.size2(), C.size2());
442
443 // compute the residue of the local model and this derivative
444 b2linalg::Vector<double> residue(local_model_residue.get_number_of_dof());
445 {
446 b2linalg::Vector<double> global_dof_on_local;
447 if (C_linear) {
448 global_dof_on_local = C * global_dof;
449 } else {
450 get_global_to_local_dof(global_dof_all, global_dof_on_local, C);
451 }
452 local_model_residue.get_residue(
453 global_dof_on_local, local_dof, time, 0, equilibrium_solution, residue,
454 (d_value_d_dof.is_null()
455 ? b2linalg::Matrix<double, b2linalg::Msym_compressed_col_update_inv>::null
456 : local_RtKR),
457 C, local_RtKC, local_CtKC, b2linalg::Vector<double, b2linalg::Vdense_ref>::null,
458 solver_hints);
459 }
460
461 // compute the contribution to the second variation of the global model
462 if (!d_value_d_dof.is_null() && local_RtKR.size1() != 0) {
463 b2linalg::Matrix<double> M;
464 M = inverse(local_RtKR) * local_RtKC;
465 local_CtKC -= (b2linalg::transposed(local_RtKC) * M);
466
467 // addition of the second derivative component
468 // This is expensive (numerical computation of the derivative and don't improve the
469 // convergence
470 /*if (!C_linear) {
471 b2linalg::Vector<double> global_dof_all_tmp = global_dof_all;
472 b2linalg::Matrix<double> C_tmp(C.size1(), C.size2());
473 b2linalg::Vector<double> global_dof_on_local_tmp;
474 const double h = 1e-3;
475 for (size_t i = 0; i != C_index.size(); ++i) {
476 const double dtmp = global_dof_all_tmp[i];
477 global_dof_all_tmp[i] += h;
478 get_global_to_local_dof(global_dof_all_tmp, global_dof_on_local_tmp, C_tmp);
479 C_tmp += -C;
480 {
481 std::cout << i << std::endl;
482 b2linalg::Vector<double> tmp;
483 tmp = ((1 /h) * (transposed(C_tmp) *
484 local_model_residue.get_residuum_sol())); std::cout << tmp << std::endl;
485 }
486 local_CtKC[i] += (1 /h) * (transposed(C_tmp) *
487 local_model_residue.get_residuum_sol()); global_dof_all_tmp[i] = dtmp;
488 }
489 }*/
490 }
491
492 b2linalg::Vector<double> delta_d;
493 // update the local dof using the residue (for the next Newton iteration)
494 if (equilibrium_solution.output_level != -1 || !value_.is_null()) {
495 delta_d = b2linalg::inverse(local_RtKR) * residue;
496 if (equilibrium_solution.output_level != -1) {
497 local_dof -= delta_d;
498 prev_local_dof = local_dof;
499 }
500 }
501
502 // compute the contribution to the first variation of the global model
503 if (!value_.is_null()) {
504 b2linalg::Vector<double> tmp;
505 tmp = -transposed(C) * local_model_residue.get_residuum_sol();
506 tmp += transposed(local_RtKC) * delta_d;
507 value_(C_index) += tmp;
508 }
509
510 // compute the contribution to the second variation of the global model
511 if (!d_value_d_dof.is_null()) {
512 if (d_value_d_dof.size1() == 0) {
513 d_value_d_dof.resize(global_dof_all.size(), global_dof_all.size());
514 }
515 b2linalg::Matrix<double, b2linalg::Mupper_packed> local_CtKC_sym;
516 local_CtKC_sym = -local_CtKC;
517 d_value_d_dof +=
518 b2linalg::StructuredMatrix(d_value_d_dof.size1(), C_index, local_CtKC_sym);
519 }
520
521 // remove of the global element contribution
522 {
523 b2linalg::Index index_e;
524 b2linalg::Vector<double, b2linalg::Vdense> value_e;
525 b2linalg::Vector<double, b2linalg::Vdense> d_value_d_time_e;
526 std::vector<bool> d_value_d_dof_e_flags(
527 d_value_d_dof.is_null() && d_value_d_time.is_null() ? 0 : 1, true);
528 std::vector<b2linalg::Matrix<double, b2linalg::Mpacked> > d_value_d_dof_e(
529 d_value_d_dof_e_flags.size());
530 for (global_elements_t::iterator e = global_elements.begin();
531 e != global_elements.end(); ++e) {
532 if (auto te = dynamic_cast<TypedElement<double>*>(*e); te != nullptr) {
533 te->get_value(
534 *global_model, false, equilibrium_solution, time, 0,
535 b2linalg::Matrix<double, b2linalg::Mrectangle_constref>(global_dof_all),
536 nullptr, // gradient_container
537 solver_hints, index_e,
538 (value_.is_null() ? b2linalg::Vector<double, b2linalg::Vdense>::null
539 : value_e),
540 d_value_d_dof_e_flags, d_value_d_dof_e,
541 (d_value_d_time.is_null()
542 ? b2linalg::Vector<double, b2linalg::Vdense>::null
543 : d_value_d_time_e));
544 }
545 if (!value_.is_null()) { value_(index_e) += value_e; }
546 if (!d_value_d_dof.is_null()) {
547 d_value_d_dof += b2linalg::StructuredMatrix(
548 d_value_d_dof.size1(), index_e, d_value_d_dof_e[0]);
549 }
550 }
551 }
552 }
553
554 typedef ObjectTypeComplete<
555 NBC_NLR_Coupling, TypedNaturalBoundaryConditionComponent<double>::type_t>
556 type_t;
557 static type_t type;
558
559private:
560 using global_elements_t = std::set<TypedElement<double>*>;
561 friend class SolutionStaticNonlinearNLRCoupling;
562 SolutionStaticNonlinearNLRCoupling solution;
563
564 std::string id;
565
566 // the local case iterator
567 b2000::CaseList::case_iterator local_case_iterator;
568
569 // The current local case
570 b2000::Case* local_case;
571
572 // The fine scale model
573 b2000::b2dbv3::Model local_model;
574
575 // The coarse scale model
576 b2000::Model* global_model;
577
578 int subcase_id;
579
580 // The interpolation of the coarse scale displacement d2 in the fine scale model
581 // N(i, j) * d2[j] is the contribution of the coarse scale dof j in the fine scale dof i
582 b2linalg::Matrix<double> C;
583 b2linalg::Index C_index;
584 bool C_linear;
585 std::map<size_t, size_t> C_index_set;
586
587 // update the C matrix
588 // this is independent of the dof, to call each time the mesh of the local model is updated
589 void update_local_model() {
590 local_model.init(id);
591
592 local_case_iterator = local_model.get_case_list().get_case_iterator();
593 local_case = local_case_iterator->next();
594 local_model.set_case(*local_case);
595
596 solution.set_nlr_coupling(*this);
597 dynamic_cast<b2000::b2dbv3::SolutionStaticNonlinear<double>&>(global_model->get_solution())
598 .add_sub_solution(solution);
599
600 // computation of the global element overlapped by local model
601 b2000::Domain& local_domain = local_model.get_domain();
602 b2000::Domain& global_domain = global_model->get_domain();
603 {
604 global_elements.clear();
605 std::unique_ptr<b2000::Domain::ElementIterator> local_element_iter =
606 local_domain.get_element_iterator();
607 Element* element = nullptr;
608 for (element = local_element_iter->next(); element != nullptr;
609 element = local_element_iter->next()) {
610 Element* global_element = local_domain.get_parent_element_and_nodes_mapping(
611 global_domain, *element, b2linalg::Matrix<double>::null);
612 TypedElement<double>& global_element_typed =
613 dynamic_cast<TypedElement<double>&>(*global_element);
614 global_elements.insert(&global_element_typed);
615 }
616 }
617
618 // computation of the kinematic coupling operator from the global dof to the local dof
619 // we assume that the coupling operator is linear. Thus this will don't
620 // work with structured element (shell or beam).
621 {
622 C_linear = true;
623 // iteration on the local node
624 std::unique_ptr<b2000::Domain::NodeIterator> local_node_iter =
625 local_domain.get_node_iterator();
626 const std::string field_name = "DISP";
627 for (Node* local_node = local_node_iter->next(); local_node != nullptr;
628 local_node = local_node_iter->next()) {
629 b2linalg::Vector<double> global_icoor;
630 Element* global_element = local_domain.get_parent_element_mapping(
631 global_domain, *local_node, global_icoor);
632 b2linalg::Index dof_numbering;
633 b2linalg::Matrix<double, b2linalg::Mrectangle> d_value_d_dof;
634 TypedElement<double>& e = dynamic_cast<TypedElement<double>&>(*global_element);
635#ifdef CHECK_GLOBAL_TO_LOCAL_MAPPING
636 {
637 b2linalg::Vector<double> global_coor;
638 e.body_geom(
639 global_icoor, global_coor,
640 b2linalg::Matrix<double, b2linalg::Mrectangle>::null);
641 std::pair<int, const coor_type*> local_coor = local_node->get_coor();
642 for (int i = 0; i != local_coor.first; ++i) {
643 if (norm(local_coor.second[i] - global_coor[i])
644 > CHECK_GLOBAL_TO_LOCAL_MAPPING) {
645 Exception()
646 << "The local mapping for the local node "
647 << local_node->get_object_name() << " is not correct." << THROW;
648 }
649 }
650 }
651#endif
652 if (!e.body_field_linear_on_dof(field_name)) { C_linear = false; }
653 e.body_field_value(
654 field_name, global_icoor,
655 b2linalg::Matrix<double, b2linalg::Mrectangle_constref>::null, 1,
656 b2linalg::Vector<double, b2linalg::Vdense>::null,
657 b2linalg::Matrix<double, b2linalg::Mrectangle>::null, dof_numbering,
658 d_value_d_dof);
659 for (size_t i = 0; i != dof_numbering.size(); ++i) {
660 C_index_set[dof_numbering[i]] = 0;
661 }
662 }
663
664 C_index.resize(C_index_set.size());
665 {
666 size_t ii = 0;
667 for (std::map<size_t, size_t>::iterator i = C_index_set.begin();
668 i != C_index_set.end(); ++i, ++ii) {
669 C_index[ii] = i->first;
670 i->second = ii;
671 }
672 }
673
674 C.resize(local_domain.get_number_of_dof(), C_index.size());
675
676 if (C_linear) {
677 C.set_zero();
678 // iteration on the local node
679 get_global_to_local_dof(
680 b2linalg::Vector<double, b2linalg::Vdense_constref>::null,
681 b2linalg::Vector<double, b2linalg::Vdense>::null, C);
682 }
683 }
684
685 // resize the matrix and vector used in the local model
686 local_model_residue.init(local_model, *local_case);
687 conv_local_dof.clear();
688 conv_local_dof.resize(local_model_residue.get_number_of_dof());
689 prev_local_dof = conv_local_dof;
690 }
691
692 void get_global_to_local_dof(
693 const b2linalg::Vector<double, b2linalg::Vdense_constref>& dof_global,
694 b2linalg::Vector<double, b2linalg::Vdense>& dof_local,
695 b2linalg::Matrix<double, b2linalg::Mrectangle_ref> C) {
696 b2000::Domain& local_domain = local_model.get_domain();
697 b2000::Domain& global_domain = global_model->get_domain();
698 const std::string field_name = "DISP";
699 if (!dof_local.is_null()) {
700 dof_local.set_zero();
701 dof_local.resize(local_domain.get_number_of_dof());
702 }
703 if (!C.is_null()) { C.set_zero(); }
704 std::unique_ptr<b2000::Domain::NodeIterator> local_node_iter =
705 local_domain.get_node_iterator();
706 size_t ii = 0;
707 for (Node* local_node = local_node_iter->next(); local_node != nullptr;
708 local_node = local_node_iter->next()) {
709 b2linalg::Vector<double> global_icoor;
710 Element* global_element =
711 local_domain.get_parent_element_mapping(global_domain, *local_node, global_icoor);
712 b2linalg::Index dof_numbering;
713 b2linalg::Vector<double, b2linalg::Vdense> value;
714 b2linalg::Matrix<double, b2linalg::Mrectangle> d_value_d_dof;
715 dynamic_cast<TypedElement<double>&>(*global_element)
716 .body_field_value(
717 field_name, global_icoor,
718 (dof_global.is_null()
719 ? b2linalg::Matrix<double, b2linalg::Mrectangle_constref>::null
720 : b2linalg::Matrix<double, b2linalg::Mrectangle_constref>(
721 dof_global)),
722 1,
723 (dof_local.is_null() ? b2linalg::Vector<double, b2linalg::Vdense>::null
724 : value),
725 b2linalg::Matrix<double, b2linalg::Mrectangle>::null, dof_numbering,
726 (C.is_null() ? b2linalg::Matrix<double, b2linalg::Mrectangle>::null
727 : d_value_d_dof));
728 if (!dof_local.is_null()) {
729 for (size_t i = 0; i != value.size(); ++i) { dof_local[ii + i] = value[i]; }
730 }
731 if (!C.is_null()) {
732#if BCNC_CHECK_DERIVATIVE
733 if (!dof_global.is_null()) {
734 const double h = 1e-4;
735 const double inv_12h = 1.0 / (12 * h);
736 b2linalg::Vector<double> dof_h = dof_global;
737 b2linalg::Vector<double> diff;
738 b2linalg::Vector<double> value_tmp;
739 TypedElement<double>& e = dynamic_cast<TypedElement<double>&>(*global_element);
740 bool first_error = true;
741 for (size_t j = 0; j != dof_numbering.size(); ++j) {
742 const size_t jj = dof_numbering[j];
743 dof_h[jj] = dof_global[jj] - 2 * h;
744 e.body_field_value(
745 field_name, global_icoor,
746 b2linalg::Matrix<double, b2linalg::Mrectangle_constref>(dof_h), 1,
747 value_tmp, b2linalg::Matrix<double, b2linalg::Mrectangle>::null,
748 b2linalg::Index::null,
749 b2linalg::Matrix<double, b2linalg::Mrectangle>::null);
750 diff = inv_12h * value_tmp;
751
752 dof_h[jj] = dof_global[jj] + 2 * h;
753 e.body_field_value(
754 field_name, global_icoor,
755 b2linalg::Matrix<double, b2linalg::Mrectangle_constref>(dof_h), 1,
756 value_tmp, b2linalg::Matrix<double, b2linalg::Mrectangle>::null,
757 b2linalg::Index::null,
758 b2linalg::Matrix<double, b2linalg::Mrectangle>::null);
759 diff += -inv_12h * value_tmp;
760
761 dof_h[jj] = dof_global[jj] - h;
762 e.body_field_value(
763 field_name, global_icoor,
764 b2linalg::Matrix<double, b2linalg::Mrectangle_constref>(dof_h), 1,
765 value_tmp, b2linalg::Matrix<double, b2linalg::Mrectangle>::null,
766 b2linalg::Index::null,
767 b2linalg::Matrix<double, b2linalg::Mrectangle>::null);
768 diff += (-8.0 * inv_12h) * value_tmp;
769
770 dof_h[jj] = dof_global[jj] + h;
771 e.body_field_value(
772 field_name, global_icoor,
773 b2linalg::Matrix<double, b2linalg::Mrectangle_constref>(dof_h), 1,
774 value_tmp, b2linalg::Matrix<double, b2linalg::Mrectangle>::null,
775 b2linalg::Index::null,
776 b2linalg::Matrix<double, b2linalg::Mrectangle>::null);
777 diff += (8.0 * inv_12h) * value_tmp;
778
779 dof_h[jj] = dof_global[jj];
780
781 for (size_t i = 0; i != diff.size(); ++i) {
782 if (std::abs(diff[i] - d_value_d_dof(i, j)) > 1e-6) {
783 if (first_error) {
784 std::cerr << "element " << e.get_object_name()
785 << ", local node=" << local_node->get_object_name()
786 << ", coor=" << global_icoor[0] << ", "
787 << global_icoor[1] << ", " << global_icoor[2]
788 << std::endl;
789 first_error = false;
790 }
791 std::cerr << " d_value_d_dof(" << i << ", " << j
792 << ")=" << d_value_d_dof(i, j) << ", numeric=" << diff[i]
793 << std::endl;
794 }
795 }
796 }
797 }
798#endif
799 for (size_t j = 0; j != d_value_d_dof.size2(); ++j) {
800 const size_t jj = C_index_set[dof_numbering[j]];
801 for (size_t i = 0; i != d_value_d_dof.size1(); ++i) {
802 C(ii + i, jj) = d_value_d_dof(i, j);
803 }
804 }
805 }
806 ii += dof_local.is_null() ? d_value_d_dof.size1() : value.size();
807 }
808 }
809
810 // the global dof of the previous iteration (used to compute the delta dof)
811 b2linalg::Vector<double> conv_global_dof;
812 b2linalg::Vector<double> prev_global_dof;
813 b2linalg::Vector<double> conv_local_dof;
814 b2linalg::Vector<double> prev_local_dof;
815 b2linalg::Matrix<double, b2linalg::Msym_compressed_col_update_inv> local_RtKR;
816 b2linalg::Matrix<double> local_RtKC;
817
818 global_elements_t global_elements;
819
820 StaticResidueFunctionNLRCoupling local_model_residue;
821};
822
823inline void SolutionStaticNonlinearNLRCoupling::set_nlr_coupling(NBC_NLR_Coupling& nlr_coupling_) {
824 nlr_coupling = &nlr_coupling_;
825 local_solution = &dynamic_cast<b2000::b2dbv3::SolutionStaticNonlinear<double>&>(
826 nlr_coupling->local_model.get_solution());
827}
828
829inline void SolutionStaticNonlinearNLRCoupling::set_dof(
830 double time, const b2linalg::Vector<double, b2linalg::Vdense_constref>& global_dof_all,
831 const b2linalg::Vector<double, b2linalg::Vdense_constref>& nbc,
832 const b2linalg::Vector<double, b2linalg::Vdense_constref>& residue,
833 bool unconverged_subcycle) {
834 // compute the local dof without the local reduction
835 b2000::Domain& local_domain = nlr_coupling->local_model.get_domain();
836 b2linalg::Vector<double, b2linalg::Vdense> g;
837 g.resize(local_domain.get_number_of_dof());
838 nlr_coupling->conv_local_dof = nlr_coupling->prev_local_dof;
839 nlr_coupling->local_model_residue.get_solution(nlr_coupling->conv_local_dof, time, true, g);
840
841 // store the local hierarchical dof in the local database
842 {
843 SetName name = nlr_coupling->local_case->get_string("DOF_SOL", "DISP") + "_H1";
844 if (unconverged_subcycle) {
845 name.cycle(local_solution->get_cycle_id() + 1);
846 name.subcycle(local_solution->get_subcycle_id() + 1);
847 } else {
848 name.cycle(local_solution->get_cycle_id());
849 }
850 if (name.case_undef()) { name.case_(nlr_coupling->local_case->get_id()); }
851 name.subcase(subcase_id);
852 dynamic_cast<DataBaseFieldSet&>(nlr_coupling->local_model)
853 .add_field_entry(
854 "DOF Solution", name.get_gname(), "NODE", "BRANCH", false, false, false);
855 RTable rtable;
856 rtable.set("STAGE_ID", nlr_coupling->local_case->get_stage_id());
857 rtable.set("STAGE", nlr_coupling->local_case->get_stage_number());
858 rtable.set("LAMBDA", time);
859 dynamic_cast<b2dbv3::Domain&>(nlr_coupling->local_model.get_domain())
860 .save_global_dof(
861 name, b2linalg::Vector<double, b2linalg::Vdense_constref>(g), rtable);
862 }
863
864 // add the global dof to the local hierarchical dof
865 {
866 if (nlr_coupling->C_linear) {
867 b2linalg::Vector<double> global_dof = global_dof_all(nlr_coupling->C_index);
868 g += nlr_coupling->C * global_dof;
869 } else {
870 b2linalg::Vector<double> global_dof_on_local;
871 nlr_coupling->get_global_to_local_dof(
872 global_dof_all, global_dof_on_local, nlr_coupling->C);
873 g += global_dof_on_local;
874 }
875 }
876
877 // store the local (non hierarchical) dof in the database
878 local_solution->set_dof(
879 time, g, nlr_coupling->local_model_residue.get_nbc_sol(),
880 nlr_coupling->local_model_residue.get_residuum_sol(), unconverged_subcycle);
881
882 // store the local (non hierarchical) gradient in the database
883 b2000::SolutionStaticNonlinear<double>::gradient_container gc =
884 local_solution->get_gradient_container();
885 {
886 local_domain.set_dof(g);
887 b2000::Domain::element_iterator ii = local_domain.get_element_iterator();
888 b2linalg::Index index;
889 for (Element* e = ii->next(); e != nullptr; e = ii->next()) {
890 if (auto te = dynamic_cast<TypedElement<double>*>(e); te != nullptr) {
891 te->get_value(
892 nlr_coupling->local_model,
893 false, // linear
894 true, // equilibrium_solution
895 time,
896 0, // delta_time
897 b2linalg::Matrix<double, b2linalg::Mrectangle_constref>(g),
898 gc.get() != nullptr && gc->set_current_element(*e) ? gc.get() : nullptr,
899 nullptr, // solver_hints
900 index, b2linalg::Vector<double, b2linalg::Vdense>::null,
901 TypedElement<double>::d_value_d_dof_flags_null,
902 TypedElement<double>::d_value_d_dof_null,
903 b2linalg::Vector<double, b2linalg::Vdense>::null);
904 }
905 }
906 }
907}
908
909inline void SolutionStaticNonlinearNLRCoupling::terminate_stage(bool success) {
910 local_solution->terminate_stage(success);
911 if (nlr_coupling->local_case->next_stage()) {
912 nlr_coupling->local_model.set_case(*nlr_coupling->local_case);
913 }
914}
915
916inline void SolutionStaticNonlinearNLRCoupling::terminate_case(
917 bool success, const RTable& attributes) {
918 local_solution->terminate_case(success, attributes);
919 nlr_coupling->local_case = nlr_coupling->local_case_iterator->next();
920 if (nlr_coupling->local_case) { nlr_coupling->local_model.set_case(*nlr_coupling->local_case); }
921}
922
923} // namespace b2000::b2dbv3
924
925#endif
csda< T > norm(const csda< T > &a)
Definition b2csda.H:343
#define THROW
Definition b2exception.H:198
Definition b2case.H:110
Definition b2domain.H:70
virtual Element * get_parent_element_and_nodes_mapping(Domain &parent_domain, const Element &element, b2linalg::Matrix< double > &parent_nodes_internal_coor)
Definition b2domain.H:431
virtual size_t get_number_of_dof() const =0
virtual element_iterator get_element_iterator()=0
virtual node_iterator get_node_iterator()=0
virtual Element * get_parent_element_mapping(Domain &parent_domain, const Node &node, b2linalg::Vector< double > &parent_internal_coor)
Definition b2domain.H:453
const std::string & get_object_name() const override
Definition b2element.H:220
virtual void body_geom(const b2linalg::Vector< double, b2linalg::Vdense_constref > &internal_coor, b2linalg::Vector< double > &geom, b2linalg::Matrix< double, b2linalg::Mrectangle > &d_geom_d_icoor)
Definition b2element.H:963
Definition b2model.H:69
virtual Solution & get_solution()=0
virtual Domain & get_domain()=0
Type
Definition b2boundary_condition.H:49
@ conservative
Definition b2boundary_condition.H:60
Definition b2model_database.H:44
Domain & get_domain() override
Definition b2model_database.C:164
void init(const std::string &dbname="", Dictionary *cmdline_set_=nullptr) override
Definition b2model_database.C:47
CaseList & get_case_list() override
Definition b2model_database.C:154
void set_case(b2000::Case &case_) override
Definition b2model_database.C:81
GenericException< UnimplementedError_name > UnimplementedError
Definition b2exception.H:314