ExaDG
Loading...
Searching...
No Matches
partitioned_solver.h
1/* ______________________________________________________________________
2 *
3 * ExaDG - High-Order Discontinuous Galerkin for the Exa-Scale
4 *
5 * Copyright (C) 2021 by the ExaDG authors
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 * ______________________________________________________________________
20 */
21
22
23#ifndef INCLUDE_EXADG_FLUID_STRUCTURE_INTERACTION_ACCELERATION_SCHEMES_PARTITIONED_SOLVER_H_
24#define INCLUDE_EXADG_FLUID_STRUCTURE_INTERACTION_ACCELERATION_SCHEMES_PARTITIONED_SOLVER_H_
25
26// FSI
27#include <exadg/fluid_structure_interaction/acceleration_schemes/linear_algebra.h>
28#include <exadg/fluid_structure_interaction/acceleration_schemes/parameters.h>
29#include <exadg/fluid_structure_interaction/single_field_solvers/fluid.h>
30#include <exadg/fluid_structure_interaction/single_field_solvers/structure.h>
31
32// utilities
33#include <exadg/utilities/print_solver_results.h>
34#include <exadg/utilities/timer_tree.h>
35
36namespace ExaDG
37{
38namespace FSI
39{
40template<int dim, typename Number>
41class PartitionedSolver
42{
43private:
44 typedef dealii::LinearAlgebra::distributed::Vector<Number> VectorType;
45
46public:
47 PartitionedSolver(Parameters const & parameters, MPI_Comm const & comm);
48
49 void
50 setup(std::shared_ptr<SolverFluid<dim, Number>> fluid_,
51 std::shared_ptr<SolverStructure<dim, Number>> structure_);
52
53 void
54 solve(std::function<void(VectorType &, VectorType const &, unsigned int)> const &
55 apply_dirichlet_neumann_scheme);
56
57 void
58 print_iterations(dealii::ConditionalOStream const & pcout) const;
59
60 std::shared_ptr<TimerTree>
61 get_timings() const;
62
63 void
64 get_structure_velocity(VectorType & velocity_structure, unsigned int const iteration) const;
65
66private:
67 void
68 get_structure_displacement(VectorType & displacement_structure,
69 unsigned int const iteration) const;
70
71 bool
72 check_convergence(VectorType const & residual) const;
73
74 void
75 print_solver_info_header(unsigned int const iteration) const;
76
77 void
78 print_solver_info_converged(unsigned int const iteration) const;
79
80 Parameters parameters;
81
82 // output to std::cout
83 dealii::ConditionalOStream pcout;
84
85 std::shared_ptr<SolverFluid<dim, Number>> fluid;
86 std::shared_ptr<SolverStructure<dim, Number>> structure;
87
88 // required for quasi-Newton methods
89 std::vector<std::shared_ptr<std::vector<VectorType>>> D_history, R_history, Z_history;
90
91 // Computation time (wall clock time).
92 std::shared_ptr<TimerTree> timer_tree;
93
94 /*
95 * The first number counts the number of time steps, the second number the total number
96 * (accumulated over all time steps) of iterations of the partitioned FSI scheme.
97 */
98 std::pair<unsigned int, unsigned long long> partitioned_iterations;
99};
100
101template<int dim, typename Number>
102PartitionedSolver<dim, Number>::PartitionedSolver(Parameters const & parameters,
103 MPI_Comm const & comm)
104 : parameters(parameters),
105 pcout(std::cout, dealii::Utilities::MPI::this_mpi_process(comm) == 0),
106 partitioned_iterations({0, 0})
107{
108 timer_tree = std::make_shared<TimerTree>();
109}
110
111template<int dim, typename Number>
112void
113PartitionedSolver<dim, Number>::setup(std::shared_ptr<SolverFluid<dim, Number>> fluid_,
114 std::shared_ptr<SolverStructure<dim, Number>> structure_)
115{
116 fluid = fluid_;
117 structure = structure_;
118}
119
120template<int dim, typename Number>
121bool
122PartitionedSolver<dim, Number>::check_convergence(VectorType const & residual) const
123{
124 double const residual_norm = residual.l2_norm();
125 double const ref_norm_abs = std::sqrt(structure->pde_operator->get_number_of_dofs());
126 double const ref_norm_rel = structure->time_integrator->get_velocity_np().l2_norm() *
127 structure->time_integrator->get_time_step_size();
128
129 bool const converged = (residual_norm < parameters.abs_tol * ref_norm_abs) or
130 (residual_norm < parameters.rel_tol * ref_norm_rel);
131
132 return converged;
133}
134
135template<int dim, typename Number>
136void
137PartitionedSolver<dim, Number>::print_solver_info_header(unsigned int const iteration) const
138{
139 if(fluid->time_integrator->print_solver_info())
140 {
141 pcout << std::endl
142 << "======================================================================" << std::endl
143 << " Partitioned FSI: iteration counter = " << std::left << std::setw(8) << iteration
144 << std::endl
145 << "======================================================================" << std::endl;
146 }
147}
148
149template<int dim, typename Number>
150void
151PartitionedSolver<dim, Number>::print_solver_info_converged(unsigned int const iteration) const
152{
153 if(fluid->time_integrator->print_solver_info())
154 {
155 pcout << std::endl
156 << "Partitioned FSI iteration converged in " << iteration << " iterations." << std::endl;
157 }
158}
159
160template<int dim, typename Number>
161void
162PartitionedSolver<dim, Number>::print_iterations(dealii::ConditionalOStream const & pcout) const
163{
164 std::vector<std::string> names;
165 std::vector<double> iterations_avg;
166
167 names = {"Partitioned iterations"};
168 iterations_avg.resize(1);
169 iterations_avg[0] =
170 (double)partitioned_iterations.second / std::max(1.0, (double)partitioned_iterations.first);
171
172 print_list_of_iterations(pcout, names, iterations_avg);
173}
174
175template<int dim, typename Number>
176std::shared_ptr<TimerTree>
177PartitionedSolver<dim, Number>::get_timings() const
178{
179 return timer_tree;
180}
181
182template<int dim, typename Number>
183void
184PartitionedSolver<dim, Number>::get_structure_velocity(VectorType & velocity_structure,
185 unsigned int iteration) const
186{
187 if(iteration == 0)
188 {
189 if(parameters.initial_guess_coupling_scheme ==
190 InitialGuessCouplingScheme::SolutionExtrapolatedToEndOfTimeStep)
191 {
192 structure->time_integrator->extrapolate_velocity_to_np(velocity_structure);
193 }
194 else if(parameters.initial_guess_coupling_scheme ==
195 InitialGuessCouplingScheme::ConvergedSolutionOfPreviousTimeStep)
196 {
197 velocity_structure = structure->time_integrator->get_velocity_n();
198 }
199 else
200 {
201 AssertThrow(false,
202 dealii::ExcMessage(
203 "Behavior for this `InitialGuessCouplingScheme` is not defined."));
204 }
205 }
206 else
207 {
208 velocity_structure = structure->time_integrator->get_velocity_np();
209 }
210}
211
212template<int dim, typename Number>
213void
214PartitionedSolver<dim, Number>::get_structure_displacement(VectorType & displacement_structure,
215 unsigned int const iteration) const
216{
217 if(iteration == 0)
218 {
219 if(this->parameters.initial_guess_coupling_scheme ==
220 InitialGuessCouplingScheme::SolutionExtrapolatedToEndOfTimeStep)
221 {
222 structure->time_integrator->extrapolate_displacement_to_np(displacement_structure);
223 }
224 else if(this->parameters.initial_guess_coupling_scheme ==
225 InitialGuessCouplingScheme::ConvergedSolutionOfPreviousTimeStep)
226 {
227 displacement_structure = structure->time_integrator->get_displacement_n();
228 }
229 else
230 {
231 AssertThrow(false,
232 dealii::ExcMessage(
233 "Behavior for this `InitialGuessCouplingScheme` is not defined."));
234 }
235 }
236 else
237 {
238 displacement_structure = structure->time_integrator->get_displacement_np();
239 }
240}
241
242template<int dim, typename Number>
243void
244PartitionedSolver<dim, Number>::solve(
245 std::function<void(VectorType &, VectorType const &, unsigned int)> const &
246 apply_dirichlet_neumann_scheme)
247{
248 // iteration counter
249 unsigned int k = 0;
250
251 // fixed-point iteration with dynamic relaxation (Aitken relaxation)
252 if(parameters.acceleration_method == AccelerationMethod::Aitken)
253 {
254 VectorType r_old, d;
255 structure->pde_operator->initialize_dof_vector(r_old);
256 structure->pde_operator->initialize_dof_vector(d);
257
258 bool converged = false;
259 double omega = 1.0;
260 while(not(converged) and k < parameters.partitioned_iter_max)
261 {
262 print_solver_info_header(k);
263
264 get_structure_displacement(d, k);
265
266 VectorType d_tilde(d);
267 apply_dirichlet_neumann_scheme(d_tilde, d, k);
268
269 // compute residual and check convergence
270 VectorType r = d_tilde;
271 r.add(-1.0, d);
272 converged = check_convergence(r);
273
274 // relaxation
275 if(not(converged))
276 {
277 dealii::Timer timer;
278 timer.restart();
279
280 if(k == 0)
281 {
282 omega = parameters.omega_init;
283 }
284 else
285 {
286 VectorType delta_r = r;
287 delta_r.add(-1.0, r_old);
288 omega *= -(r_old * delta_r) / delta_r.norm_sqr();
289 }
290
291 r_old = r;
292
293 d.add(omega, r);
294 structure->time_integrator->set_displacement(d);
295
296 timer_tree->insert({"Aitken"}, timer.wall_time());
297 }
298
299 // increment counter of partitioned iteration
300 ++k;
301 }
302 }
303 else if(parameters.acceleration_method == AccelerationMethod::IQN_ILS)
304 {
305 std::shared_ptr<std::vector<VectorType>> D, R;
306 D = std::make_shared<std::vector<VectorType>>();
307 R = std::make_shared<std::vector<VectorType>>();
308
309 VectorType d, d_tilde, d_tilde_old, r, r_old;
310 structure->pde_operator->initialize_dof_vector(d);
311 structure->pde_operator->initialize_dof_vector(d_tilde);
312 structure->pde_operator->initialize_dof_vector(d_tilde_old);
313 structure->pde_operator->initialize_dof_vector(r);
314 structure->pde_operator->initialize_dof_vector(r_old);
315
316 unsigned int const q = parameters.reused_time_steps;
317 unsigned int const n = fluid->time_integrator->get_number_of_time_steps();
318
319 bool converged = false;
320 while(not(converged) and k < parameters.partitioned_iter_max)
321 {
322 print_solver_info_header(k);
323
324 get_structure_displacement(d, k);
325
326 apply_dirichlet_neumann_scheme(d_tilde, d, k);
327
328 // compute residual and check convergence
329 r = d_tilde;
330 r.add(-1.0, d);
331 converged = check_convergence(r);
332
333 // relaxation
334 if(not(converged))
335 {
336 dealii::Timer timer;
337 timer.restart();
338
339 if(k == 0 and (q == 0 or n == 0))
340 {
341 d.add(parameters.omega_init, r);
342 }
343 else
344 {
345 if(k >= 1)
346 {
347 // append D, R matrices
348 VectorType delta_d_tilde = d_tilde;
349 delta_d_tilde.add(-1.0, d_tilde_old);
350 D->push_back(delta_d_tilde);
351
352 VectorType delta_r = r;
353 delta_r.add(-1.0, r_old);
354 R->push_back(delta_r);
355 }
356
357 // fill vectors (including reuse)
358 std::vector<VectorType> Q = *R;
359 for(auto R_q : R_history)
360 for(auto delta_r : *R_q)
361 Q.push_back(delta_r);
362 std::vector<VectorType> D_all = *D;
363 for(auto D_q : D_history)
364 for(auto delta_d : *D_q)
365 D_all.push_back(delta_d);
366
367 AssertThrow(D_all.size() == Q.size(),
368 dealii::ExcMessage("D, Q vectors must have same size."));
369
370 unsigned int const k_all = Q.size();
371 if(k_all >= 1)
372 {
373 // compute QR-decomposition
374 Matrix<Number> U(k_all);
375 compute_QR_decomposition(Q, U);
376
377 std::vector<Number> rhs(k_all, 0.0);
378 for(unsigned int i = 0; i < k_all; ++i)
379 rhs[i] = -Number(Q[i] * r);
380
381 // alpha = U^{-1} rhs
382 std::vector<Number> alpha(k_all, 0.0);
383 backward_substitution(U, alpha, rhs);
384
385 // d_{k+1} = d_tilde_{k} + delta d_tilde
386 d = d_tilde;
387 for(unsigned int i = 0; i < k_all; ++i)
388 d.add(alpha[i], D_all[i]);
389 }
390 else // despite reuse, the vectors might be empty
391 {
392 d.add(parameters.omega_init, r);
393 }
394 }
395
396 d_tilde_old = d_tilde;
397 r_old = r;
398
399 structure->time_integrator->set_displacement(d);
400
401 timer_tree->insert({"IQN-ILS"}, timer.wall_time());
402 }
403
404 // increment counter of partitioned iteration
405 ++k;
406 }
407
408 dealii::Timer timer;
409 timer.restart();
410
411 // Update history
412 D_history.push_back(D);
413 R_history.push_back(R);
414 if(D_history.size() > q)
415 D_history.erase(D_history.begin());
416 if(R_history.size() > q)
417 R_history.erase(R_history.begin());
418
419 timer_tree->insert({"IQN-ILS"}, timer.wall_time());
420 }
421 else if(parameters.acceleration_method == AccelerationMethod::IQN_IMVLS)
422 {
423 std::shared_ptr<std::vector<VectorType>> D, R;
424 D = std::make_shared<std::vector<VectorType>>();
425 R = std::make_shared<std::vector<VectorType>>();
426
427 std::vector<VectorType> B;
428
429 VectorType d, d_tilde, d_tilde_old, r, r_old, b, b_old;
430 structure->pde_operator->initialize_dof_vector(d);
431 structure->pde_operator->initialize_dof_vector(d_tilde);
432 structure->pde_operator->initialize_dof_vector(d_tilde_old);
433 structure->pde_operator->initialize_dof_vector(r);
434 structure->pde_operator->initialize_dof_vector(r_old);
435 structure->pde_operator->initialize_dof_vector(b);
436 structure->pde_operator->initialize_dof_vector(b_old);
437
438 std::shared_ptr<Matrix<Number>> U;
439 std::vector<VectorType> Q;
440
441 unsigned int const q = parameters.reused_time_steps;
442 unsigned int const n = fluid->time_integrator->get_number_of_time_steps();
443
444 bool converged = false;
445 while(not(converged) and k < parameters.partitioned_iter_max)
446 {
447 print_solver_info_header(k);
448
449 get_structure_displacement(d, k);
450
451 apply_dirichlet_neumann_scheme(d_tilde, d, k);
452
453 // compute residual and check convergence
454 r = d_tilde;
455 r.add(-1.0, d);
456 converged = check_convergence(r);
457
458 // relaxation
459 if(not(converged))
460 {
461 dealii::Timer timer;
462 timer.restart();
463
464 // compute b vector
465 inv_jacobian_times_residual(b, D_history, R_history, Z_history, r);
466
467 if(k == 0 and (q == 0 or n == 0))
468 {
469 d.add(parameters.omega_init, r);
470 }
471 else
472 {
473 d = d_tilde;
474 d.add(-1.0, b);
475
476 if(k >= 1)
477 {
478 // append D, R, B matrices
479 VectorType delta_d_tilde = d_tilde;
480 delta_d_tilde.add(-1.0, d_tilde_old);
481 D->push_back(delta_d_tilde);
482
483 VectorType delta_r = r;
484 delta_r.add(-1.0, r_old);
485 R->push_back(delta_r);
486
487 VectorType delta_b = delta_d_tilde;
488 delta_b.add(1.0, b_old);
489 delta_b.add(-1.0, b);
490 B.push_back(delta_b);
491
492 // compute QR-decomposition
493 U = std::make_shared<Matrix<Number>>(k);
494 Q = *R;
495 compute_QR_decomposition(Q, *U);
496
497 std::vector<Number> rhs(k, 0.0);
498 for(unsigned int i = 0; i < k; ++i)
499 rhs[i] = -Number(Q[i] * r);
500
501 // alpha = U^{-1} rhs
502 std::vector<Number> alpha(k, 0.0);
503 backward_substitution(*U, alpha, rhs);
504
505 for(unsigned int i = 0; i < k; ++i)
506 d.add(alpha[i], B[i]);
507 }
508 }
509
510 d_tilde_old = d_tilde;
511 r_old = r;
512 b_old = b;
513
514 structure->time_integrator->set_displacement(d);
515
516 timer_tree->insert({"IQN-IMVLS"}, timer.wall_time());
517 }
518
519 // increment counter of partitioned iteration
520 ++k;
521 }
522
523 dealii::Timer timer;
524 timer.restart();
525
526 // Update history
527 D_history.push_back(D);
528 R_history.push_back(R);
529 if(D_history.size() > q)
530 D_history.erase(D_history.begin());
531 if(R_history.size() > q)
532 R_history.erase(R_history.begin());
533
534 // compute Z and add to Z_history
535 std::shared_ptr<std::vector<VectorType>> Z;
536 Z = std::make_shared<std::vector<VectorType>>();
537 *Z = Q; // make sure that Z has correct size
538 backward_substitution_multiple_rhs(*U, *Z, Q);
539 Z_history.push_back(Z);
540 if(Z_history.size() > q)
541 Z_history.erase(Z_history.begin());
542
543 timer_tree->insert({"IQN-IMVLS"}, timer.wall_time());
544 }
545 else
546 {
547 AssertThrow(false, dealii::ExcMessage("This AccelerationMethod is not implemented."));
548 }
549
550 partitioned_iterations.first += 1;
551 partitioned_iterations.second += k;
552
553 print_solver_info_converged(k);
554}
555
556} // namespace FSI
557} // namespace ExaDG
558
559#endif /* INCLUDE_EXADG_FLUID_STRUCTURE_INTERACTION_ACCELERATION_SCHEMES_PARTITIONED_SOLVER_H_ */
Definition linear_algebra.h:40
Definition fluid.h:52
Definition structure.h:38
Definition interpolate.h:32
Definition driver.cpp:33
Definition parameters.h:47