b2api
B2000++ API Reference Manual, VERSION 4.6
 
Loading...
Searching...
No Matches
b2free_vibration_solver.H
1//------------------------------------------------------------------------
2// b2free_vibration_solver.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#ifndef B2FREE_VIBRATION_SOLVER_H_
22#define B2FREE_VIBRATION_SOLVER_H_
23
24#include <filesystem>
25
26#include "b2ppconfig.h"
28#include "model/b2domain.H"
29#include "model/b2element.H"
31#include "model/b2model.H"
32#include "model/b2solution.H"
33#include "model/b2solver.H"
34#include "utils/b2exception.H"
35#include "utils/b2logging.H"
36#include "utils/b2object.H"
37
38namespace b2000::solver {
39
40template <typename T, typename MATRIX_FORMAT>
41struct EigenDecompDense {
42 void resolve(
43 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible>& K_augm,
44 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible>& M_augm,
45 const std::string& which, b2linalg::Vector<T, b2linalg::Vdense>& eigenvalue,
46 b2linalg::Matrix<T, b2linalg::Mrectangle>& eigenvector) {
48 }
49};
50
51template <typename MATRIX_FORMAT>
52struct EigenDecompDense<double, MATRIX_FORMAT> {
53 void resolve(
54 b2linalg::Matrix<double, typename MATRIX_FORMAT::sparse_inversible>& K_augm,
55 b2linalg::Matrix<double, typename MATRIX_FORMAT::sparse_inversible>& M_augm,
56 const std::string& which, b2linalg::Vector<double, b2linalg::Vdense>& eigenvalue,
57 b2linalg::Matrix<double, b2linalg::Mrectangle>& eigenvector) {
58 assert(eigenvalue.size() <= K_augm.size1());
59 if (which != "SM" && which != "LM") { UnimplementedError() << THROW; }
60
61 b2linalg::Matrix<double, b2linalg::Mrectangle> K(K_augm.size1(), K_augm.size2());
62 for (size_t i = 0; i != K.size1(); ++i) {
63 for (size_t j = 0; j != K.size2(); ++j) { K(i, j) = K_augm(i, j); }
64 }
65
66 b2linalg::Matrix<double, b2linalg::Mrectangle> M(M_augm.size1(), M_augm.size2());
67 for (size_t i = 0; i != M.size1(); ++i) {
68 for (size_t j = 0; j != M.size2(); ++j) { M(i, j) = M_augm(i, j); }
69 }
70
71 const int n = int(K.size1());
72 b2linalg::Vector<double, b2linalg::Vdense> eigvalue_all(n);
73 eigvalue_all.set_zero();
74
75 std::vector<double> work(2 * (1 + 6 * n + 2 * n * n));
76 std::vector<int> iwork(2 * (3 + 5 * n));
77 int info = 0;
78 lapack::sygvd(
79 1, 'V', 'U', n, &K(0, 0), n, &M(0, 0), n, &eigvalue_all[0], &work[0],
80 int(work.size()), &iwork[0], int(iwork.size()), info);
81
82 assert(info >= 0);
83 if (info > 0 && info <= n) {
84 Exception() << "Eigendecomposition with lapack routine dsygvd "
85 "failed, "
86 << info
87 << " off-diagonal elements of an "
88 "intermediate tridiagonal form did not converge to zero."
89 << THROW;
90 }
91 if (info > n) {
92 Exception() << "Eigendecomposition with lapack routine dsygvd "
93 "failed, the "
94 << info - n
95 << ". leading minor of the "
96 "("
97 << M.size1() << "x" << M.size2()
98 << ") mass matrix is "
99 "not positive-definite."
100 << THROW;
101 }
102
103 const size_t o = (which == "SM" ? 0 : eigvalue_all.size() - eigenvalue.size());
104 eigenvector.resize(K.size1(), eigenvalue.size());
105 for (size_t i = 0; i != eigenvalue.size(); ++i) {
106 eigenvalue[i] = eigvalue_all[o + i];
107 eigenvector[i] = K[o + i];
108 }
109 }
110};
111
112template <typename T, typename MATRIX_FORMAT>
113void eigen_decomposition_dense(
114 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible>& K_augm,
115 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible>& M_augm,
116 const std::string& which, b2linalg::Vector<T, b2linalg::Vdense>& eigenvalue,
117 b2linalg::Matrix<T, b2linalg::Mrectangle>& eigenvector) {
118 EigenDecompDense<T, MATRIX_FORMAT> ed;
119 ed.resolve(K_augm, M_augm, which, eigenvalue, eigenvector);
120}
121
122template <typename T, typename MATRIX_FORMAT>
123struct eigen_matrix_type {};
124
125template <typename T>
126struct eigen_matrix_type<std::complex<T>, b2linalg::Munsymmetric> {
127 static const char A = 'N';
128 static const char B = 'N';
129};
130
131template <typename T>
132struct eigen_matrix_type<T, b2linalg::Msymmetric> {
133 static const char A = 'P';
134 static const char B = 'S';
135};
136
138
145template <typename T, typename MATRIX_FORMAT>
147public:
148 using eig_matrix_type = eigen_matrix_type<T, MATRIX_FORMAT>;
152
153 void solve() override {
154 while (solve_iteration()) { ; }
155 }
156
157 bool solve_iteration() override {
158 if (model_ == nullptr) { Exception() << THROW; }
159 if (case_iterator_.get() == nullptr) {
160 case_iterator_ = model_->get_case_list().get_case_iterator();
161 }
162 case_ = case_iterator_->next();
163 if (case_) {
164 if (case_->get_stage_size() != 1.0) {
165 logging::get_logger("solver.free_vibration")
166 << logging::warning << "The stage size of the case " << case_->get_id()
167 << " is equal to " << case_->get_stage_size()
168 << " but the free vibration solver only handle a case with"
169 " one stage of size equal to one."
170 " The specified stage size is ignored."
171 << logging::LOGFLUSH;
172 }
173 if (case_->get_number_of_stage() != 1) {
174 logging::get_logger("solver.free_vibration")
175 << logging::warning << "The number of stage of the case" << case_->get_id()
176 << " is " << case_->get_stage_size()
177 << " but the free vibration solver only handle a case with"
178 " one stage of size equal to one. The extra stages are"
179 " ignored."
180 << logging::LOGFLUSH;
181 }
182 solve_on_case(*case_);
183 return true;
184 }
185 return false;
186 }
187
188 static type_t type;
189
190protected:
191 void solve_on_case(Case& case_);
192 // Number of eigen modes
193 size_t nb_eigenmodes_;
194 // Contains eigen value for each mode. Size: nModes
195 b2linalg::Vector<T, b2linalg::Vdense> eigenvalues_;
196 // Contains eigen vector for each mode. Size: nDofs, nModes
197 b2linalg::Matrix<T, b2linalg::Mrectangle> eigenvectors_;
198};
199
200template <typename T, typename MATRIX_FORMAT>
201typename FreeVibrationSolver<T, MATRIX_FORMAT>::type_t FreeVibrationSolver<T, MATRIX_FORMAT>::type(
202 "FreeVibrationSolver", type_name<T, MATRIX_FORMAT>(),
203 StringList("FREE_VIBRATION", "VIBRATION"), module, &Solver::type);
204
205} // namespace b2000::solver
206
207/* Perform a free vibration analysis on a dynamic linear problem using the
208 Lagrange Multiplier Adjunction method. This version accept only
209 one essential boundary condition. The non-linear part of the
210 essential boundary condition are ignored.
211
212Solve the free vibration problem:
213 K * x + M * x'' = 0
214with the essential boundary conditions:
215 x = R * x_r
216 L * x = 0
217
218Where:
219 - K (matrix) is the derivative of the value relatively to the degree of freedom.
220 - M (matrix) is the derivative of the value relatively to the acceleration.
221 - x (vector) is the degree of freedom.
222 - x'' (vector) is the acceleration (the double derivative of the
223 degree of freedom relatively to the time).
224 - x_r (vector) is the degree of freedom in the reduction model.
225 - f (vector) is the natural boundary conditions.
226 - R (matrix) define the linear model reduction. r (vector) is ignored.
227 - L (matrix) define the linear essential boundary conditions. l
228 (vector) is ignored.
229
230Using the model reduction equation, the system to solve becomes:
231 trans(R) * K * R * x_r + trans(R) * M * R * x_r'' = 0
232Where:
233 - x_r'' (vector) is the acceleration in the reduced system (the
234 double derivative of x_r relatively to the time).
235
236with the boundary conditions:
237 L * R * x_r = 0
238
239Using the Lagrange Multiplier Adjunction method for imposing the
240displacement boundary conditions, the generalised eigenproblem of the
241free vibration equilibrium are:
242
243 | trans(R) * K * R trans(L * R) | | x_r | | trans(R) * M * R 0 | | x_r |
244 | | * | | = e * | | * | |
245 | L * R 0 | | m | | 0 0 | | m |
246
247The eigenvector x is then obtained from the eigenvector x_r using the model
248reduction equation.
249
250*/
251template <typename T, typename MATRIX_FORMAT>
253 logging::Logger& logger = logging::get_logger("solver.free_vibration");
254
255 logger << logging::info << "Start the free vibration solver" << logging::LOGFLUSH;
256
257 model_->set_case(case_);
258
259 Domain& domain = model_->get_domain();
260 SolutionFreeVibration<T>* solution =
261 &dynamic_cast<SolutionFreeVibration<T>&>(model_->get_solution());
262 if (solution == 0) {
263 TypeError() << "The solver only accepts a solution of type"
264 << " SolutionFreeVibration<T>" << THROW;
265 }
266
267 T sigma;
268 T sigma_max;
269 char which[3];
270 {
271 int n;
272 solution->which_modes(n, which, sigma, sigma_max);
273 assert(n > 0);
274 nb_eigenmodes_ = size_t(n);
275 }
276 const std::string constraint_string = case_.get_string("CONSTRAINT_METHOD", "REDUCTION");
277
278 const std::string matrices_dir = case_.get_string("MATRICES_DIR", "");
279 const bool export_sys_matrices = (matrices_dir != "");
280
281 const size_t number_of_dof = domain.get_number_of_dof();
282
283 b2linalg::Vector<T, b2linalg::Vdense> dof;
284 double time = 0;
285 int stage;
286 std::string domain_state_id;
287 const bool nonline = !model_->get_initial_condition().is_zero();
288 if (nonline) {
289 dof.resize(number_of_dof);
290 dynamic_cast<TypedInitialCondition<T>&>(model_->get_initial_condition())
291 .get_static_initial_condition_value(dof, time, stage, domain_state_id);
292 if (stage != 0) { case_.set_stage(stage); }
293 if (!domain_state_id.empty()) { model_->get_domain().load_state(domain_state_id); }
294 }
295 EquilibriumSolution esf(false);
296
297 // Get the model reduction (x = R * x_r + r)
298 mrbc_t* mrbc = &dynamic_cast<mrbc_t&>(
299 model_->get_model_reduction_boundary_condition(mrbc_t::type.get_subtype(
300 "TypedModelReductionBoundaryConditionListComponent" + type_name<T>())));
301
302 b2linalg::Matrix<T, b2linalg::Mcompressed_col> trans_R;
303 mrbc->get_linear_value(b2linalg::Vector<T, b2linalg::Vdense_ref>::null, trans_R);
304
305 // Get the displacement boundary condition (L * x + l = 0)
306 ebc_t* dbc =
307 &dynamic_cast<ebc_t&>(model_->get_essential_boundary_condition(ebc_t::type.get_subtype(
308 "TypedEssentialBoundaryConditionListComponent" + type_name<T>())));
309 b2linalg::Vector<T, b2linalg::Vdense> l(dbc->get_size(true));
310 b2linalg::Matrix<T, b2linalg::Mcompressed_col> trans_L;
311 if (nonline) {
312 dbc->get_nonlinear_value(
313 dof, time, esf, l, trans_L, b2linalg::Vector<T, b2linalg::Vdense_ref>::null, 0);
314 } else {
315 dbc->get_linear_value(l, trans_L);
316 }
317
318 if (!l.empty()) {
319 // scale the ebc constraint
320 b2linalg::Vector<double> trans_L_scale(trans_L.size2());
321 trans_L.scale_col_to_norminf(trans_L_scale);
322 for (size_t i = 0; i != l.size(); ++i) { l[i] *= trans_L_scale[i]; }
323 trans_L.remove_zero();
324
325 // procedure to remove dependency on the Lagrange multiplier
326 if (case_.get_bool("REMOVE_DEPENDENT_CONSTRAINTS", true)
327 && (constraint_string == "LAGRANGE" || constraint_string == "AUGMENTED_LAGRANGE")) {
328 b2linalg::Index P;
329 trans_L.get_dep_columns(P, 3e-13, 1.5);
330 if (!P.empty()) {
331 l.remove(P);
332 trans_L.remove_col(P);
333 logger << logging::info << "Remove " << P.size() << " dependent constraints."
334 << logging::LOGFLUSH;
335 }
336 }
337 }
338
339 // Assembly the matrices K and M by computing
340 // (trans(R) * K * R) = K_augm = sum (trans(R) * K_element * R)
341 // (trans(R) * M * R) = M_augm = sum (trans(R) * M_element * R)
342 logger << logging::info << "Element matrix assembly" << logging::LOGFLUSH;
343 size_t K_augm_size = trans_R.size1() + (constraint_string == "PENALTY" ? 0 : trans_L.size2());
344 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible> K_augm(
345 K_augm_size, domain.get_dof_connectivity_type(), case_);
346 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible> M_augm(
347 K_augm_size, domain.get_dof_connectivity_type(), case_);
348
349 auto Kgg = export_sys_matrices
350 ? b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible>(
351 number_of_dof, domain.get_dof_connectivity_type(), case_)
352 : b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible>();
353 auto Mgg = export_sys_matrices
354 ? b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible>(
355 number_of_dof, domain.get_dof_connectivity_type(), case_)
356 : b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible>();
357
358 domain.set_dof(b2linalg::Vector<T, b2linalg::Vdense_constref>::null);
359 {
360 Domain::element_iterator i = domain.get_element_iterator();
361 b2linalg::Index index;
362 std::vector<bool> d_value_d_dof_flags(3);
363 d_value_d_dof_flags[0] = d_value_d_dof_flags[2] = true;
364 std::vector<b2linalg::Matrix<T, typename MATRIX_FORMAT::dense> > d_value_d_dof(3);
365 const bool log_el_flag = logger.is_enabled_for(logging::data);
366 for (Element* e = i->next(); e != nullptr; e = i->next()) {
367 if (auto te = dynamic_cast<TypedElement<T>*>(e); te != nullptr) {
368 te->get_value(
369 *model_, !nonline,
370 true, // equilibrium_solution
371 time,
372 0, // delta_time
373 nonline ? b2linalg::Matrix<T, b2linalg::Mrectangle_constref>(dof)
374 : b2linalg::Matrix<T, b2linalg::Mrectangle_constref>::null,
375 0, // gradient_container
376 0, // solver_hints
377 index, b2linalg::Vector<T, b2linalg::Vdense>::null, d_value_d_dof_flags,
378 d_value_d_dof, b2linalg::Vector<T, b2linalg::Vdense>::null);
379 }
380 K_augm += trans_R * StructuredMatrix(number_of_dof, index, d_value_d_dof[0])
381 * transposed(trans_R);
382 M_augm += trans_R * StructuredMatrix(number_of_dof, index, d_value_d_dof[2])
383 * transposed(trans_R);
384
385 if (export_sys_matrices) {
386 Kgg += StructuredMatrix(number_of_dof, index, d_value_d_dof[0]);
387 Mgg += StructuredMatrix(number_of_dof, index, d_value_d_dof[2]);
388 }
389
390 if (log_el_flag) {
391 b2linalg::Matrix<T> tmp_element(d_value_d_dof[0]);
392 logger << logging::data << "elementary second variation "
393 << logging::DBName("ELSV", 0, e->get_object_name()) << tmp_element
394 << " of element id " << e->get_object_name() << logging::LOGFLUSH;
395 tmp_element = d_value_d_dof[2];
396 logger << logging::data << "elementary mass matrix "
397 << logging::DBName("ELSV", 2, e->get_object_name()) << tmp_element
398 << " of element id " << e->get_object_name() << logging::LOGFLUSH;
399 }
400 }
401 }
402 logger << logging::info << "Element matrix assembly finished" << logging::LOGFLUSH;
403
404 if (trans_L.size2() > 0) {
405 b2linalg::Matrix<T, b2linalg::Mcompressed_col> trans_LR;
406 trans_LR = trans_R * trans_L;
407 if (constraint_string == "PENALTY" || constraint_string == "AUGMENTED_LAGRANGE") {
408 const double penalty_factor =
409 case_.get_double("CONSTRAINT_PENALTY", constraint_string == "PENALTY" ? 1e10 : 1);
410 K_augm += (trans_LR * transposed(trans_LR)) * penalty_factor;
411 }
412 if (constraint_string == "LAGRANGE" || constraint_string == "AUGMENTED_LAGRANGE") {
413 const double lagrange_mult_scale = case_.get_double("LAGRANGE_MULT_SCALE_PENALTY", 1.0);
414 trans_LR *= lagrange_mult_scale;
415 if (!MATRIX_FORMAT::symmetric) {
416 K_augm(
417 b2linalg::Interval(0, trans_R.size1()),
418 b2linalg::Interval(trans_R.size1(), K_augm_size)) += trans_LR;
419 }
420 b2linalg::Matrix<T, b2linalg::Mcompressed_col> LR = transposed(trans_LR);
421 K_augm(
422 b2linalg::Interval(trans_R.size1(), K_augm_size),
423 b2linalg::Interval(0, trans_R.size1())) += LR;
424 }
425 }
426
427 if (logger.is_enabled_for(logging::data)) {
428 logger << logging::data << "d_value_d_dof "
429 << logging::DBName("D_VALUE_D_DOF.GLOB", 0, 0, case_.get_id()) << K_augm
430 << logging::LOGFLUSH;
431 logger << logging::data << "d_value_d_dofdotdot "
432 << logging::DBName("D_VALUE_D_ACCELERATION.GLOB", 0, 0, case_.get_id()) << M_augm
433 << logging::LOGFLUSH;
434 }
435
436 if (export_sys_matrices) {
437 // This section exports the system matrices to the
438 // directory given in the MATRICES_DIR option as csv-files.
439 // The resulting tables can, for example, be read with np.loadtxt() in
440 // Python.
441
442 std::filesystem::path out_path(matrices_dir);
443 if (std::filesystem::create_directories(out_path)) {
444 logger << logging::info << "Created new directory " << matrices_dir
445 << logging::LOGFLUSH;
446 }
447 // give the user some feedback where to look for the output
448 logger << logging::info << "Writing system matrices as CSV file(s) to " << out_path
449 << logging::LOGFLUSH;
450
451 // K_augm output
452 logger << logging::info << " - writing "
453 << "Kaa"
454 << " with (" << K_augm.size1() << " x " << K_augm.size2() << ") DoF"
455 << logging::LOGFLUSH;
456 export_csv(K_augm, "Kaa", out_path / "Kaa.csv");
457
458 // M_augm output
459 logger << logging::info << " - writing "
460 << "Maa"
461 << " with (" << M_augm.size1() << " x " << M_augm.size2() << ") DoF"
462 << logging::LOGFLUSH;
463 export_csv(M_augm, "Maa", out_path / "Maa.csv");
464
465 // R output
466 logger << logging::info << " - writing "
467 << "Rtrans"
468 << " with (" << trans_R.size1() << " x " << trans_R.size2() << ") DoF"
469 << logging::LOGFLUSH;
470 export_csv(trans_R, "Rtrans", out_path / "Rtrans.csv");
471
472 // L output
473 if (trans_L.size2() > 0) {
474 logger << logging::info << " - writing "
475 << "Ltrans"
476 << " with (" << trans_L.size1() << " x " << trans_L.size2() << ") DoF"
477 << logging::LOGFLUSH;
478 export_csv(trans_L, "Ltrans", out_path / "Ltrans.csv");
479 } else {
480 logger << logging::info << " - skipping "
481 << "Ltrans"
482 << " with (" << trans_L.size1() << " x " << trans_L.size2() << ") DoF"
483 << logging::LOGFLUSH;
484 }
485
486 // Kgg output
487 logger << logging::info << " - writing "
488 << "Kgg"
489 << " with (" << Kgg.size1() << " x " << Kgg.size2() << ") DoF" << logging::LOGFLUSH;
490 export_csv(Kgg, "Kgg", out_path / "Kgg.csv");
491
492 // Mgg output
493 logger << logging::info << " - writing "
494 << "Mgg"
495 << " with (" << Mgg.size1() << " x " << Mgg.size2() << ") DoF" << logging::LOGFLUSH;
496 export_csv(Mgg, "Mgg", out_path / "Mgg.csv");
497 }
498
499 logger << logging::info << "Eigenvalue problem resolution" << logging::LOGFLUSH;
500
501 if (nb_eigenmodes_ > K_augm.size1()) {
502 Exception() << "The number of requested free-vibration modes "
503 "(NMODES="
504 << nb_eigenmodes_
505 << ") is greater than the size of the "
506 "reduced system of equations ("
507 << K_augm.size1() << ")." << THROW;
508 }
509 eigenvalues_.resize(nb_eigenmodes_);
510 b2linalg::Matrix<T, b2linalg::Mrectangle> eigenvector_red;
511 if (nb_eigenmodes_ == K_augm.size1() && K_augm.size1() < 100 && typeid(T) == typeid(double)
512 && eig_matrix_type::A == 'P' && eig_matrix_type::B == 'S' && sigma == T(0)
513 && constraint_string != "LAGRANGE" && constraint_string != "AUGMENTED_LAGRANGE") {
514 eigen_decomposition_dense<T, MATRIX_FORMAT>(
515 K_augm, M_augm, which, eigenvalues_, eigenvector_red);
516 } else {
517 eigenvector_red = eigenvector(
518 K_augm, eig_matrix_type::A, M_augm, eig_matrix_type::B, eigenvalues_, which, sigma);
519 }
520
521 eigenvectors_.resize(number_of_dof, nb_eigenmodes_);
522 eigenvectors_ = transposed(trans_R)
523 * eigenvector_red(
524 b2linalg::Interval(0, trans_R.size1()),
525 b2linalg::Interval(0, eigenvector_red.size2()));
526
527 solution->set_modes(eigenvalues_(b2linalg::Interval(0, eigenvectors_.size2())), eigenvectors_);
528 RTable attributes;
529 solution->terminate_case(true, attributes);
530
531 case_.warn_on_non_used_key();
532
533 logger << logging::info << "End of free vibration solver" << logging::LOGFLUSH;
534}
535
536#endif // B2FREE_VIBRATION_SOLVER_H_
#define THROW
Definition b2exception.H:198
Interface to C++ representations of FE solvers.
Definition b2case.H:110
virtual double get_stage_size(int stage=-1) const =0
virtual std::string get_id() const =0
virtual size_t get_number_of_stage() const =0
Definition b2exception.H:131
virtual CaseList & get_case_list()=0
Definition b2object.H:415
A Solver instance executes an FE analysis. It is created and initialized by the Model instance and,...
Definition b2solver.H:50
Case * case_
This also.
Definition b2solver.H:93
Model * model_
Definition b2solver.H:91
Definition b2util.H:54
Definition b2boundary_condition.H:198
Definition b2boundary_condition.H:302
A solver to find free vibrations of a system.
Definition b2free_vibration_solver.H:146
void solve() override
This function is usually called by the Model instance.
Definition b2free_vibration_solver.H:153
bool solve_iteration() override
This function is called by the Solver instance itself.
Definition b2free_vibration_solver.H:157
Logger & get_logger(const std::string &logger_name="")
Definition b2logging.H:829
GenericException< UnimplementedError_name > UnimplementedError
Definition b2exception.H:314