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