b2api
B2000++ API Reference Manual, VERSION 4.6
 
Loading...
Searching...
No Matches
b2frequency_dependent_free_vibration_solver.H
1//------------------------------------------------------------------------
2// b2frequency_dependent_free_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 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 B2FREQUENCY_DEPENDENT_FREE_VIBRATION_SOLVER_H_
22#define B2FREQUENCY_DEPENDENT_FREE_VIBRATION_SOLVER_H_
23
24#include "b2ppconfig.h"
26#include "model/b2domain.H"
27#include "model/b2element.H"
28#include "model/b2model.H"
29#include "model/b2solution.H"
30#include "solvers/b2free_vibration_solver.H"
31#include "utils/b2constants.H"
32#include "utils/b2exception.H"
33#include "utils/b2logging.H"
34#include "utils/b2object.H"
35
36namespace b2000::solver {
37
38template <typename T, typename MATRIX_FORMAT>
39class FrequencyDependentFreeVibrationSolver : public Solver {
40public:
41 using eig_matrix_type = eigen_matrix_type<T, MATRIX_FORMAT>;
42 using ebc_t = TypedEssentialBoundaryCondition<T>;
43 using mrbc_t = TypedModelReductionBoundaryCondition<T>;
44 using type_t = ObjectTypeComplete<FrequencyDependentFreeVibrationSolver, Solver::type_t>;
45
46 void solve() override {
47 while (solve_iteration()) { ; }
48 }
49
50 bool solve_iteration() override {
51 if (model_ == nullptr) { Exception() << THROW; }
52 if (case_iterator_.get() == nullptr) {
53 case_iterator_ = model_->get_case_list().get_case_iterator();
54 }
55 case_ = case_iterator_->next();
56 if (case_) {
57 if (case_->get_stage_size() != 1.0) {
58 logging::get_logger("solver.free_vibration")
59 << logging::warning << "The stage size of the case " << case_->get_id()
60 << " is equal to " << case_->get_stage_size()
61 << " but the free vibration solver only handle a case with one stage of size "
62 "equal to one. The specified stage size is ignored."
63 << logging::LOGFLUSH;
64 }
65 if (case_->get_number_of_stage() != 1) {
66 logging::get_logger("solver.free_vibration")
67 << logging::warning << "The number of stage of the case" << case_->get_id()
68 << " is " << case_->get_stage_size()
69 << " but the free vibration solver only handle a case with one stage of size "
70 "equal to one. The extra stages are ignored."
71 << logging::LOGFLUSH;
72 }
73 solve_on_case(*case_);
74 return true;
75 }
76 return false;
77 }
78
79 static type_t type;
80
81protected:
82 void solve_on_case(Case& case_);
83 logging::Logger& logger{logging::get_logger("solver.frequency_dependent_free_vibration")};
84 // Number of eigen modes
85 int nb_eigenmodes_;
86 // Contains eigen value for each mode. Size: nModes
87 b2linalg::Vector<T, b2linalg::Vdense> eigenvalues_;
88 // Contains eigen vector for each mode. Size: nDofs, nModes
89 b2linalg::Matrix<T, b2linalg::Mrectangle> eigenvectors_;
90
91 void compute_K_and_M(
92 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible>& K_augm,
93 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible>& M_augm,
94 b2linalg::Matrix<T, b2linalg::Mcompressed_col>& trans_R,
95 b2linalg::Matrix<T, b2linalg::Mcompressed_col>& trans_L) {
96 Domain& domain = model_->get_domain();
97 size_t number_of_dof = domain.get_number_of_dof();
98
99 K_augm.set_zero_same_struct();
100 M_augm.set_zero_same_struct();
101
102 Domain::element_iterator i = domain.get_element_iterator();
103 b2linalg::Index index;
104 std::vector<bool> d_value_d_dof_flags(3);
105 d_value_d_dof_flags[0] = d_value_d_dof_flags[2] = true;
106 std::vector<b2linalg::Matrix<T, typename MATRIX_FORMAT::dense> > d_value_d_dof(3);
107 bool log_el_flag = logger.is_enabled_for(logging::data);
108 for (Element* e = i->next(); e != nullptr; e = i->next()) {
109 d_value_d_dof[0].resize(index.size(), index.size());
110 d_value_d_dof[2].resize(index.size(), index.size());
111 if (auto te = dynamic_cast<TypedElement<T>*>(e); te != nullptr) {
112 te->get_value(
113 *model_,
114 true, // linear
115 true, // equilibrium_solution
116 0, // time
117 0, // delta_time
118 b2linalg::Matrix<T, b2linalg::Mrectangle_constref>::null, // dof
119 0, // gradient_container
120 0, // solver_hints
121 index, b2linalg::Vector<T, b2linalg::Vdense>::null, d_value_d_dof_flags,
122 d_value_d_dof, b2linalg::Vector<T, b2linalg::Vdense>::null);
123 }
124 K_augm += trans_R * StructuredMatrix(number_of_dof, index, d_value_d_dof[0])
125 * transposed(trans_R);
126 M_augm += trans_R * StructuredMatrix(number_of_dof, index, d_value_d_dof[2])
127 * transposed(trans_R);
128 if (log_el_flag) {
129 b2linalg::Matrix<T> tmp_element(d_value_d_dof[0]);
130 logger << logging::data << "elementary second variation "
131 << logging::DBName("ELSV", 0, e->get_object_name()) << tmp_element
132 << " of element id " << e->get_object_name() << logging::LOGFLUSH;
133 tmp_element = d_value_d_dof[2];
134 logger << logging::data << "elementary mass matrix "
135 << logging::DBName("ELSV", 2, e->get_object_name()) << tmp_element
136 << " of element id " << e->get_object_name() << logging::LOGFLUSH;
137 }
138 }
139
140 K_augm(
141 b2linalg::Interval(trans_R.size1(), K_augm.size1()),
142 b2linalg::Interval(0, trans_R.size1())) +=
143 b2linalg::transposed(trans_L) * b2linalg::transposed(trans_R);
144 }
145};
146
147template <typename T, typename MATRIX_FORMAT>
148typename FrequencyDependentFreeVibrationSolver<T, MATRIX_FORMAT>::type_t
149 FrequencyDependentFreeVibrationSolver<T, MATRIX_FORMAT>::type(
150 "FrequencyDependentFreeVibrationSolver", type_name<T, MATRIX_FORMAT>(),
151 StringList("FREQUENCY_DEPENDENT_FREE_VIBRATION"), module, &Solver::type);
152
153} // namespace b2000::solver
154
155/* Perform a free vibration analysis on a dynamic linear problem using the
156 Lagrange Multiplier Adjunction method. This version accepts only
157 one essential boundary condition. The non-linear part of the
158 essential boundary condition are ignored.
159
160Solve the free vibration frequency dependent problem:
161 K * x + M * x'' = 0
162with the essential boundary conditions:
163 x = R * x_r
164 L * x = 0
165
166Where:
167 - K (matrix) is the derivative of the value relatively to the degree
168 of freedom.
169 - M (matrix) is the derivative of the value relatively to the acceleration.
170 - x (vector) is the degree of freedom.
171 - x'' (vector) is the acceleration (the double derivative of the
172 degree of freedom relatively to the time).
173 - x_r (vector) is the degree of freedom in the reduction model.
174 - f (vector) is the natural boundary conditions.
175 - R (matrix) define the linear model reduction. r (vector) is ignored.
176 - L (matrix) define the linear essential boundary conditions. l
177 (vector) is ignored.
178
179Using the model reduction equation, the system to solve become:
180 trans(R) * K * R * x_r + trans(R) * M * R * x_r'' = 0
181Where:
182 - x_r'' (vector) is the acceleration in the reduced system (the
183 double derivative of x_r relatively to the time).
184
185with the boundary conditions:
186 L * R * x_r = 0
187
188Using the Lagrange Multiplier Adjunction method for imposing the
189displacement boundary conditions, the generalised eigen-problem of the
190free vibration equilibrium are:
191
192 | trans(R) * K * R trans(L * R) | | x_r | | trans(R) * M * R 0 | | x_r |
193 | | * | | = e * | | * | |
194 | L * R 0 | | m | | 0 0 | | m |
195
196The eigen-vector x is then obtained from the eigen-vector x_r using the model
197reduction equation.
198
199
200
201K and M are frequency dependent. In the first step, an approximation
202of all the eigenvalues are computed with the initial frequency equal
203to the shift value. In the second step each exact frequency dependent
204eigenvalues are computed with an iterative approach.
205
206*/
207template <typename T, typename MATRIX_FORMAT>
208void b2000::solver::FrequencyDependentFreeVibrationSolver<T, MATRIX_FORMAT>::solve_on_case(
209 Case& case_) {
210 logger << logging::info << "Start the frequency dependent free vibration solver"
211 << logging::LOGFLUSH;
212
213 model_->set_case(case_);
214
215 Domain& domain = model_->get_domain();
216 SolutionFreeVibration<T>* solution =
217 &dynamic_cast<SolutionFreeVibration<T>&>(model_->get_solution());
218 if (solution == 0) {
219 TypeError() << "The solver only accepts solutions of type "
220 "SolutionFreeVibration<T>"
221 << THROW;
222 }
223
224 T sigma;
225 T sigma_max;
226 char which[3];
227 solution->which_modes(nb_eigenmodes_, which, sigma, sigma_max);
228
229 double tol_exact_value = case_.get_double("TOL", 0.0000001);
230 double tol_predictor_value = case_.get_double("TOL_PREDICTOR", 0.01);
231
232 // Get the number of dof of the model
233 size_t number_of_dof = domain.get_number_of_dof();
234
235 // Get the model reduction (x = R * x_r + r)
236 mrbc_t* mrbc = &dynamic_cast<mrbc_t&>(
237 model_->get_model_reduction_boundary_condition(mrbc_t::type.get_subtype(
238 "TypedModelReductionBoundaryConditionListComponent" + type_name<T>())));
239
240 b2linalg::Vector<T, b2linalg::Vdense> r(number_of_dof);
241 b2linalg::Matrix<T, b2linalg::Mcompressed_col> trans_R;
242 mrbc->get_linear_value(r, trans_R);
243
244 // Get the displacement boundary condition (L * x + l = 0)
245 ebc_t* dbc =
246 &dynamic_cast<ebc_t&>(model_->get_essential_boundary_condition(ebc_t::type.get_subtype(
247 "TypedEssentialBoundaryConditionListComponent" + type_name<T>())));
248 b2linalg::Vector<T, b2linalg::Vdense> l(dbc->get_size(true));
249 b2linalg::Matrix<T, b2linalg::Mcompressed_col> trans_L;
250 dbc->get_linear_value(l, trans_L);
251
252 size_t K_augm_size = trans_R.size1() + trans_L.size2();
253 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible> K_augm(
254 K_augm_size, domain.get_dof_connectivity_type(), case_);
255 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible> M_augm(
256 K_augm_size, domain.get_dof_connectivity_type(), case_);
257
258 logger << logging::info
259 << "Compute an approximation of all the required modes with spectral shift value = "
260 << sqrt(sigma) / T(2 * M_PIl) << "." << logging::LOGFLUSH;
261
262 // First compute an approximation of all the eigenvalues.
263 // use the value that come form the database material.
264 // case_.set("FREQ_INIT", sqrt(sigma) / T(2 * M_PIl));
265 // domain.set_case(case_);
266
267 compute_K_and_M(K_augm, M_augm, trans_R, trans_L);
268
269 eigenvalues_.resize(nb_eigenmodes_);
270 eigenvectors_.resize(number_of_dof, nb_eigenmodes_);
271 b2linalg::Matrix<T, b2linalg::Mrectangle> eigenvectors_red;
272 eigenvectors_red = eigenvector(
273 K_augm, eig_matrix_type::A, M_augm, eig_matrix_type::B, eigenvalues_, which, sigma);
274
275 // Iterate on the approximated eigenvalues.
276 int mode = 0;
277 while (mode != nb_eigenmodes_) {
278 T local_sigma = eigenvalues_[mode];
279
280 logger << logging::info << "Start to compute the exact (frequency dependent) mode " << mode
281 << " with the spectral shift value " << sqrt(local_sigma) / T(2 * M_PIl) << "."
282 << logging::LOGFLUSH;
283
284 T previous_eigenvalue;
285 b2linalg::Vector<T, b2linalg::Vdense> current_eigenvalue(1);
286 current_eigenvalue[0] = eigenvalues_[mode];
287 b2linalg::Matrix<T, b2linalg::Mrectangle> eigenvector_red;
288
289 b2linalg::Vector<T, b2linalg::Vdense> restart_eigenvector;
290 restart_eigenvector = eigenvectors_red[mode];
291
292 int ncv = 2;
293
294 // Iterate at one frequency until the frequency residue (the residue
295 // between the previous current_eigenvalue and the new current_eigenvalue
296 // after an update of the frequency of the domain) become small.
297 do {
298 logger << logging::info << "Update the domain frequency to "
299 << sqrt(current_eigenvalue[0]) / T(2 * M_PIl) << logging::LOGFLUSH;
300
301 // Update the frequency of the domain.
302 case_.set("FREQ_INIT", sqrt(current_eigenvalue[0]) / T(2 * M_PIl));
303 domain.set_case(case_);
304
305 // Compute the stiffness and mass matrix for the new frequency.
306 compute_K_and_M(K_augm, M_augm, trans_R, trans_L);
307
308 previous_eigenvalue = current_eigenvalue[0];
309
310 while (1) {
311 try {
312 eigenvector_red = eigenvector(
313 K_augm, eig_matrix_type::A, M_augm, eig_matrix_type::B,
314 current_eigenvalue, "SM", local_sigma, ncv, -1, -1, restart_eigenvector);
315 restart_eigenvector = eigenvector_red[0];
316 local_sigma = current_eigenvalue[0];
317 break;
318 } catch (Exception& e) {
319 ncv = std::min(32, ncv * 2);
320 logger << logging::info << "The current_eigenvalue solver cannot converge ("
321 << e
322 << "). Retry by increasing the number of Lanczos basis vectors (ncv="
323 << ncv << "." << logging::LOGFLUSH;
324 }
325 }
326
327 logger << logging::info << "Frequency residue = "
328 << std::abs(current_eigenvalue[0] - previous_eigenvalue)
329 / std::abs(current_eigenvalue[0])
330 << logging::LOGFLUSH;
331 } while (std::abs(current_eigenvalue[0]) > tol_exact_value
332 && (std::abs(current_eigenvalue[0] - previous_eigenvalue)
333 / std::abs(current_eigenvalue[0])
334 > tol_exact_value));
335
336 if (std::abs(eigenvalues_[mode] - current_eigenvalue[0]) / std::abs(current_eigenvalue[0])
337 > tol_predictor_value) {
338 logger << logging::info
339 << "The approximation of all the required modes is too far from the exact "
340 "computed mode, update the approximation with the domain frequency equal to "
341 << sqrt(current_eigenvalue[0]) / T(2 * M_PIl) << logging::LOGFLUSH;
342
343 eigenvectors_red = eigenvector(
344 K_augm, eig_matrix_type::A, M_augm, eig_matrix_type::B, eigenvalues_, which,
345 sigma);
346
347 if (std::abs(eigenvalues_[mode] - current_eigenvalue[0])
348 / std::abs(current_eigenvalue[0])
349 > tol_exact_value) {
350 logger << logging::info
351 << "The exact computed mode is a jump to an other mode, restart the "
352 "computation from the mode "
353 << mode << "." << logging::LOGFLUSH;
354 continue;
355 }
356 }
357
358 logger << logging::info << "End of computation of the mode " << mode
359 << ". Eigenfrequency = " << sqrt(current_eigenvalue[0]) / T(2 * M_PIl) << "."
360 << logging::LOGFLUSH;
361
362 // Convert the modes from the reduction model
363 eigenvectors_[mode] = transposed(trans_R) * eigenvector_red[0];
364
365 // Store the computed mode.
366 solution->set_mode(mode, current_eigenvalue[0], eigenvectors_[mode]);
367
368 // go to the next mode
369 ++mode;
370 }
371
372 RTable attributes;
373 solution->terminate_case(true, attributes);
374 case_.warn_on_non_used_key();
375
376 logger << logging::info << "End of frequency dependent free vibration solver"
377 << logging::LOGFLUSH;
378}
379
380#endif // B2FREQUENCY_DEPENDENT_FREE_VIBRATION_SOLVER_H_
csda< T > sqrt(const csda< T > &a)
Square root of a csda number.
Definition b2csda.H:396
#define THROW
Definition b2exception.H:198
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
virtual size_t get_number_of_dof() const =0
virtual CaseList & get_case_list()=0
virtual Domain & get_domain()=0
Case * case_
This also.
Definition b2solver.H:93
Model * model_
Definition b2solver.H:91
Logger & get_logger(const std::string &logger_name="")
Definition b2logging.H:829
GenericException< TypeError_name > TypeError
Definition b2exception.H:325