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