MAST
pk_flutter_solver.cpp
Go to the documentation of this file.
1 /*
2  * MAST: Multidisciplinary-design Adaptation and Sensitivity Toolkit
3  * Copyright (C) 2013-2019 Manav Bhatia
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18  */
19 
20 
21 // MAST includes
28 #include "base/parameter.h"
29 
30 
33 _velocity_param(nullptr),
34 _kred_param(nullptr),
35 _bref_param(nullptr),
36 _V_range(std::pair<Real, Real>(0., 0.)),
37 _n_V_divs(0),
38 _kr_range(std::pair<Real, Real>(0., 0.)),
39 _n_k_red_divs(0)
40 { }
41 
42 
43 
45 
46  this->clear();
47 }
48 
49 
50 
51 void
53 
54  this->clear_solutions();
55 
56  _velocity_param = nullptr;
57  _kred_param = nullptr;
58  _bref_param = nullptr;
59  _V_range = std::pair<Real, Real>(0.,0.);
60  _kr_range = std::pair<Real, Real>(0.,0.);
61  _n_V_divs = 0;
62  _n_k_red_divs = 0;
63 
65 }
66 
67 
68 
69 void
72  MAST::Parameter& kr_param,
73  MAST::Parameter& bref_param,
74  Real rho,
75  Real V_lower,
76  Real V_upper,
77  unsigned int n_V_divs,
78  Real kr_lower,
79  Real kr_upper,
80  unsigned int n_kr_divs,
81  std::vector<libMesh::NumericVector<Real>*>& basis) {
82 
83 
84  _velocity_param = &V_param;
85  _kred_param = &kr_param;
86  _bref_param = &bref_param;
87  _rho = rho;
88  _V_range.first = V_lower;
89  _V_range.second = V_upper;
90  _n_V_divs = n_V_divs;
91  _kr_range.first = kr_lower;
92  _kr_range.second = kr_upper;
93  _n_k_red_divs = n_kr_divs;
94 
96 
97 }
98 
99 
100 
101 
102 void
104 
105  std::map<Real, MAST::FlutterSolutionBase*>::iterator it =
106  _flutter_solutions.begin();
107 
108  for ( ; it != _flutter_solutions.end(); it++)
109  delete it->second;
110 
111  std::multimap<Real, MAST::FlutterRootCrossoverBase*>::iterator cross_it =
112  _flutter_crossovers.begin();
113 
114  for ( ; cross_it != _flutter_crossovers.end(); cross_it++)
115  delete cross_it->second;
116 
117  _flutter_solutions.clear();
118  _flutter_crossovers.clear();
119 }
120 
121 
122 
123 
124 unsigned int
126 
127  std::multimap<Real, MAST::FlutterRootCrossoverBase*>::const_iterator
128  it = _flutter_crossovers.begin(),
129  end = _flutter_crossovers.end();
130 
131  unsigned int n = 0;
132  for ( ; it!=end; it++)
133  if (it->second->root) // a valid root pointer has been assigned
134  n++;
135 
136  return n;
137 }
138 
139 
140 
141 void
143 
144  // if the initial scanning has not been done, then do it now
145  if (!_flutter_solutions.size()) {
146 
147  // the outer loop consists of the reference velocity at which
148  // aerodynamics is calculated
149  Real current_k_red = _kr_range.second,
150  delta_k_red = (_kr_range.second-_kr_range.first)/_n_k_red_divs;
151 
152  std::vector<Real> k_red_vals(_n_k_red_divs+1);
153  for (unsigned int i=0; i<_n_k_red_divs+1; i++) {
154  k_red_vals[i] = current_k_red;
155  current_k_red -= delta_k_red;
156  }
157  k_red_vals[_n_k_red_divs] = _kr_range.first; // to get around finite-precision arithmetic
158 
159  //
160  // outer loop is on reduced frequency
161  //
162  for (unsigned int j=0; j<_n_k_red_divs+1; j++) {
163 
164  current_k_red = k_red_vals[j];
165 
166  // march from the upper limit to the lower to find the roots
167  Real current_v_ref = _V_range.first,
168  delta_v_ref = (_V_range.second-_V_range.first)/_n_V_divs;
169 
170  std::vector<Real> v_ref_vals(_n_V_divs+1);
171  for (unsigned int i=0; i<_n_V_divs+1; i++) {
172  v_ref_vals[i] = current_v_ref;
173  current_v_ref += delta_v_ref;
174  }
175  v_ref_vals[_n_V_divs] = _V_range.second; // to get around finite-precision arithmetic
176 
177  MAST::FlutterSolutionBase* prev_sol = nullptr;
178 
179  //
180  // inner loop is on reduced frequencies
181  //
182  for (unsigned int i=0; i<_n_V_divs+1; i++) {
183  current_v_ref = v_ref_vals[i];
184  std::unique_ptr<MAST::FlutterSolutionBase> sol =
185  _analyze(current_k_red,
186  current_v_ref,
187  prev_sol);
188 
189 
190  if (_output)
191  sol->print(*_output);
192 
193  // add the solution to this solver
194  _insert_new_solution(current_k_red, sol.release());
195 
196  // now get a pointer to the previous solution
197  // get the solution from the database for this reduced frequency
198  std::map<Real, MAST::FlutterSolutionBase*>::iterator it =
199  _flutter_solutions.find(current_v_ref);
200 
201  libmesh_assert(it != _flutter_solutions.end());
202  prev_sol = it->second;
203  }
204 
205  }
207  }
208 }
209 
210 
211 
212 void
214 {
215 
216  // write only if the output was set
217  if (!_output)
218  return;
219 
220  std::map<Real, MAST::FlutterSolutionBase*>::const_iterator
221  sol_it = _flutter_solutions.begin(),
222  sol_end = _flutter_solutions.end();
223  libmesh_assert(sol_it != sol_end); // solutions should have been evaluated
224 
225  unsigned int nvals = sol_it->second->n_roots();
226  libmesh_assert(nvals); // should not be zero
227 
228  // each root is written separately one after another
229  for (unsigned int i=0; i<nvals; i++)
230  {
231  // print the headers
232  *_output
233  << "** Root # "
234  << std::setw(5) << i << " **" << std::endl
235  << std::setw(15) << "kr"
236  << std::setw(15) << "g"
237  << std::setw(15) << "V" << std::endl;
238 
239  // update the iterator for this analysis
240  sol_it = _flutter_solutions.begin();
241 
242  // write the data from all solutions
243  for ( ; sol_it != sol_end; sol_it++)
244  {
245  const MAST::FlutterRootBase& root =
246  sol_it->second->get_root(i);
247 
248  *_output
249  << std::setw(15) << root.kr
250  << std::setw(15) << root.g
251  << std::setw(15) << root.V << std::endl;
252  }
253  *_output << std::endl << std::endl;
254  }
255 
256 
257  // write the roots identified using iterative search technique
258  std::streamsize prec = _output->precision();
259 
260  unsigned int nroots = this->n_roots_found();
261  *_output << std::endl
262  << "n critical roots identified: " << nroots << std::endl;
263  for (unsigned int i=0; i<nroots; i++)
264  {
265  const MAST::FlutterRootBase& root = this->get_root(i);
266  *_output
267  << "** Root : " << std::setw(5) << i << " **" << std::endl
268  << "kr = " << std::setw(15) << std::setprecision(15) << root.kr << std::endl
269  << "V = " << std::setw(15) << std::setprecision(15) << root.V << std::endl
270  << "g = " << std::setw(15) << root.g << std::endl
271  << "omega = " << std::setw(15) << root.omega << std::endl
272  << std::setprecision((int) prec) // set the precision to the default value
273  << "Modal Participation : " << std::endl ;
274  for (unsigned int j=0; j<nvals; j++)
275  *_output
276  << "(" << std::setw(5) << j << "): "
277  << std::setw(10) << root.modal_participation(j)
278  << std::setw(3) << " ";
279  *_output << std::endl << std::endl;
280  }
281 }
282 
283 
284 void
286 {
287  // print only if the output was set
288  if (!_output)
289  return;
290 
291  *_output << "n crossover points found: "
292  << std::setw(5) << _flutter_crossovers.size() << std::endl;
293 
294  std::multimap<Real, MAST::FlutterRootCrossoverBase*>::const_iterator
295  it = _flutter_crossovers.begin(), end = _flutter_crossovers.end();
296 
297  unsigned int i=0;
298 
299  for ( ; it != end; it++) {
300  *_output << "** Point : " << std::setw(5) << i << " **" << std::endl;
301  it->second->print(*_output);
302  *_output << std::endl;
303  i++;
304  }
305 }
306 
307 
308 
309 
310 std::pair<bool, MAST::FlutterSolutionBase*>
312  MAST::FlutterSolutionBase*>& ref_sol_range,
313  const unsigned int root_num,
314  const Real g_tol,
315  const unsigned int max_iters) {
316 
317  // assumes that the upper k_val has +ve g val and lower k_val has -ve
318  // k_val
319  Real
320  lower_k = ref_sol_range.first->get_root(root_num).kr,
321  lower_v = ref_sol_range.first->get_root(root_num).V,
322  lower_g = ref_sol_range.first->get_root(root_num).g,
323  upper_k = ref_sol_range.second->get_root(root_num).kr,
324  upper_v = ref_sol_range.second->get_root(root_num).V,
325  upper_g = ref_sol_range.second->get_root(root_num).g,
326  new_k = lower_k + (upper_k-lower_k)/(upper_g-lower_g)*(0.-lower_g),
327  new_v = 0.; // linear interpolation
328  unsigned int n_iters = 0;
329 
330  std::unique_ptr<MAST::FlutterSolutionBase> new_sol;
331  std::pair<bool, MAST::FlutterSolutionBase*> rval(false, nullptr);
332 
333  while (n_iters < max_iters) {
334 
335  new_v = lower_v + (upper_v-lower_v)/(upper_g-lower_g)*(0.-lower_g); // linear interpolation
336 
337  new_sol.reset(_analyze(new_k,
338  new_v,
339  ref_sol_range.first).release());
340 
341  if (_output)
342  new_sol->print(*_output);
343 
344  // add the solution to this solver
345  _insert_new_solution(new_k, new_sol.release());
346 
347  // get the solution from the database for this reduced frequency
348  std::map<Real, MAST::FlutterSolutionBase*>::iterator it =
349  _flutter_solutions.find(new_v);
350 
351  libmesh_assert(it != _flutter_solutions.end());
352  rval.second = it->second;
353  const MAST::FlutterRootBase& root = rval.second->get_root(root_num);
354 
355  // use the estimated flutter velocity to get the next
356  // V_ref value for aerodynamic matrices.
357  new_k = root.kr;
358 
359  // check if the new damping value
360  if (fabs(root.g) <= g_tol) {
361  rval.first = true;
362  return rval;
363  }
364 
365  // update the v_val
366  if (root.g < 0.) {
367  lower_v = new_v;
368  lower_g = root.g;
369  }
370  else {
371  upper_v = new_v;
372  upper_g = root.g;
373  }
374 
375  n_iters++;
376  }
377 
378  // return false, along with the latest sol
379  rval.first = false;
380 
381  return rval;
382 }
383 
384 
385 
386 
388 MAST::PKFlutterSolver::get_root(const unsigned int n) const {
389 
390  libmesh_assert(n < n_roots_found());
391 
392  std::multimap<Real, MAST::FlutterRootCrossoverBase*>::const_iterator
393  it = _flutter_crossovers.begin(),
394  end = _flutter_crossovers.end();
395 
396  unsigned int root_num = 0;
397  for ( ; it!=end; it++) {
398  if (it->second->root) { // a valid root pointer has been assigned
399  if (root_num == n) // root num matches the one being requested
400  return *(it->second->root);
401  else
402  root_num++;
403  }
404  }
405 
406  libmesh_assert(false); // should not get here
407 }
408 
409 
410 
411 std::pair<bool, MAST::FlutterRootBase*>
413  const unsigned int n_bisection_iters)
414 {
415  // iterate over the cross-over points and calculate the next that has
416  // not been evaluated
417  std::multimap<Real, MAST::FlutterRootCrossoverBase*>::iterator
418  it = _flutter_crossovers.begin(), end = _flutter_crossovers.end();
419  while ( it != end)
420  {
421  MAST::FlutterRootCrossoverBase* cross = it->second;
422 
423  if (!cross->root) {
424  const unsigned int root_num = cross->root_num;
425  std::pair<bool, MAST::FlutterSolutionBase*> sol;
426  // first try the Newton search. If that fails, then
427  // try the bisection search
428  /*sol = newton_search(*cross->crossover_solutions.second,
429  root_num,
430  g_tol,
431  n_bisection_iters);
432  if (!sol.first)*/
434  root_num, g_tol, n_bisection_iters);
435 
436  cross->root = &(sol.second->get_root(root_num));
437 
438  // now, remove this entry from the _flutter_crossover points and
439  // reinsert it with the actual critical velocity
440  _flutter_crossovers.erase(it);
441  std::pair<Real, MAST::FlutterRootCrossoverBase*>
442  val(cross->root->V, cross);
443  _flutter_crossovers.insert(val);
444  return std::pair<bool, MAST::FlutterRootBase*> (true, cross->root);
445  }
446 
447  it++;
448  }
449 
450  // if it gets here, no new root was found
451  return std::pair<bool, MAST::FlutterRootBase*> (false, nullptr);
452 }
453 
454 
455 
456 std::pair<bool, MAST::FlutterRootBase*>
458  const unsigned int n_bisection_iters)
459 {
460  // iterate over the cross-over points and calculate the next that has
461  // not been evaluated
462  std::multimap<Real, MAST::FlutterRootCrossoverBase*>::iterator
463  it = _flutter_crossovers.begin(), end = _flutter_crossovers.end();
464 
465  if (it == end) // no potential cross-over points were identified
466  return std::pair<bool, MAST::FlutterRootBase*> (false, nullptr);
467 
468  // it is possible that once the root has been found, its velocity end up
469  // putting it at a higher velocity in the map, so we need to check if
470  // the critical root has changed
471  while (!it->second->root)
472  {
473  MAST::FlutterRootCrossoverBase* cross = it->second;
474 
475  if (!cross->root) {
476 
477  const unsigned int root_num = cross->root_num;
478  std::pair<bool, MAST::FlutterSolutionBase*> sol;
479  // first try the Newton search. If that fails, then
480  // try the bisection search
481  /*sol = newton_search(*cross->crossover_solutions.second,
482  root_num,
483  g_tol,
484  n_bisection_iters);
485  if (!sol.first)*/
487  root_num, g_tol, n_bisection_iters);
488 
489  cross->root = &(sol.second->get_root(root_num));
490 
491  // now, remove this entry from the _flutter_crossover points and
492  // reinsert it with the actual critical velocity
493  _flutter_crossovers.erase(it);
494  std::pair<Real, MAST::FlutterRootCrossoverBase*>
495  val(cross->root->V, cross);
496  _flutter_crossovers.insert(val);
497  }
498 
499  // update the iterator to make sure that this is updated
500  it = _flutter_crossovers.begin(); end = _flutter_crossovers.end();
501  }
502 
503  // if it gets here, then the root was successfully found
504  return std::pair<bool, MAST::FlutterRootBase*> (true, it->second->root);
505 }
506 
507 
508 
509 
510 /*std::pair<bool, MAST::FlutterSolutionBase*>
511 MAST::PKFlutterSolver::_newton_search(const MAST::FlutterSolutionBase& init_sol,
512  const unsigned int root_num,
513  const Real tol,
514  const unsigned int max_iters) {
515 
516  libmesh_error(); // to be implemented
517 
518  std::pair<bool, MAST::FlutterSolutionBase*> rval(false, nullptr);
519  // assumes that the upper k_val has +ve g val and lower k_val has -ve
520  // k_val
521  Real k_red, v_ref;
522  unsigned int n_iters = 0;
523 
524  std::unique_ptr<MAST::FlutterSolutionBase> new_sol;
525 
526  RealVectorX res, sol, dsol;
527  RealMatrixX jac, stiff;
528  ComplexMatrixX mat_A, mat_B, mat_A_sens, mat_B_sens;
529  ComplexVectorX v;
530 
531  res.resize(2); sol.resize(2); dsol.resize(2);
532  jac.resize(2,2);
533 
534  // initialize the solution to the values of init_sol
535  k_red = init_sol.get_root(root_num).k_red_ref;
536  v_ref = init_sol.get_root(root_num).V_ref;
537  sol(0) = k_red;
538  sol(1) = v_ref;
539 
540  const MAST::FlutterSolutionBase* prev_sol = &init_sol;
541 
542  bool if_continue = true;
543 
544  while (if_continue) {
545 
546  // evaluate the residual and Jacobians
547  std::unique_ptr<MAST::FlutterSolutionBase> pk_sol =
548  this->analyze(k_red, v_ref, prev_sol);
549 
550  pk_sol->print(_output);
551 
552  // add the solution to this solver
553  _insert_new_solution(v_ref, pk_sol.release());
554 
555  // now get a pointer to the previous solution
556  // get the solution from the database for this reduced frequency
557  std::map<Real, MAST::FlutterSolutionBase*>::iterator it =
558  _flutter_solutions.find(v_ref);
559 
560  libmesh_assert(it != _flutter_solutions.end());
561  rval.second = it->second;
562  prev_sol = it->second;
563 
564  // solve the Newton update problem
565  const MAST::FlutterRootBase& root = prev_sol->get_root(root_num);
566  Complex eig = root.root, eig_k_red_sens = 0., den = 0., eig_V_ref_sens = 0.;
567 
568  // initialize the baseline matrices
569  initialize_matrices(k_red, v_ref, mat_A, mat_B, stiff);
570 
571  // solve the sensitivity problem
572  // first with respect to k_red
573  // next we need the sensitivity of k_red before we can calculate
574  // the sensitivity of flutter eigenvalue
575  initialize_matrix_sensitivity_for_reduced_freq(k_red,
576  v_ref,
577  mat_A_sens,
578  mat_B_sens);
579 
580  // now calculate the quotient for sensitivity wrt k_red
581  // calculate numerator
582  mat_B_sens *= -eig;
583  mat_B_sens += mat_A_sens;
584  v = mat_B_sens*root.eig_vec_right;
585  den = root.eig_vec_left.dot(mat_B*root.eig_vec_right);
586  eig_k_red_sens = root.eig_vec_left.dot(v) / den;
587 
588  //std::unique_ptr<MAST::FlutterSolutionBase> PK_dsol =
589  //this->analyze(k_red+.001, v_ref, prev_sol);
590  //eig -= PK_dsol->get_root(root_num).root;
591  //eig /= -.001;
592 
593  //std::cout << eig << std::endl;
594  //std::cout << eig_k_red_sens << std::endl;
595 
596  // next, sensitivity wrt V_ref
597  initialize_matrix_sensitivity_for_V_ref(k_red,
598  v_ref,
599  mat_A_sens,
600  mat_B_sens);
601 
602  // now calculate the quotient for sensitivity wrt V_ref
603  // calculate numerator
604  mat_B_sens *= -eig;
605  mat_B_sens += mat_A_sens;
606  v = mat_B_sens*root.eig_vec_right;
607  den = root.eig_vec_left.dot(mat_B*root.eig_vec_right);
608  eig_V_ref_sens = root.eig_vec_left.dot(v) / den;
609 
610  //std::unique_ptr<MAST::FlutterSolutionBase> PK_dsol =
611  //this->analyze(k_red, v_ref+.001, prev_sol);
612  //eig -= PK_dsol->get_root(root_num).root;
613  //eig /= -.001;
614 
615  //std::cout << eig << std::endl;
616  //std::cout << eig_V_ref_sens << std::endl;
617 
618  // residual
619  res(0) = root.root.imag();
620  res(1) = v_ref*std::sqrt(root.root.real()) - 1.;
621 
622  // Jacobian
623  jac(0,0) = eig_k_red_sens.imag();
624  jac(0,1) = eig_V_ref_sens.imag();
625  jac(1,0) = 0.5*v_ref*pow(eig.real(), -0.5)*eig_k_red_sens.real();
626  jac(1,1) = std::sqrt(eig.real()) + 0.5*v_ref*pow(eig.real(), -0.5)*eig_V_ref_sens.real();
627 
628  // now calculate the updates
629  // r0 + J *dx = 0
630  // => dx = - inv(J) * r0
631  // => x1 = x0 + dx
632 
633  dsol = -jac.inverse()*res;
634  sol += dsol;
635 
636  // get the updated parameter values
637  k_red = sol(0);
638  v_ref = sol(1);
639 
640  // increment the iteration counter
641  n_iters++;
642 
643  // set the flag
644  if (fabs(root.g) < tol) {
645  rval.first = true;
646  return rval;
647  }
648 
649  if (n_iters >= max_iters)
650  if_continue = false;
651  }
652 
653  // return false, along with the latest sol
654  rval.first = false;
655 
656  return rval;
657 }
658 */
659 
660 
661 void
664 
665  const Real v_ref = sol->ref_val();
666 
667  // if the value does not already exist, then insert it in the map
668  std::map<Real, MAST::FlutterSolutionBase*>::iterator it =
669  _flutter_solutions.find(v_ref);
670 
671  if ( it == _flutter_solutions.end()) {
672  bool if_success =
673  _flutter_solutions.insert(std::pair<Real, MAST::FlutterSolutionBase*>
674  (v_ref, sol)).second;
675 
676  libmesh_assert(if_success);
677 
678  // do not delete the solution since it is stored in the solver
679  }
680  else {
681  // assuming that the roots are sorted, compare them with the existing
682  // root and swap the ones that are closer to v_ref
683 
684  // first make sure that the two have the same number of roots
685  libmesh_assert_equal_to(it->second->n_roots(), sol->n_roots());
686 
687  Real dk_old = 0., k_ref_old = 0., dk_new = 0.;
688  MAST::FlutterRootBase *old_root, *new_root;
689 
690  for (unsigned int i=0; i<sol->n_roots(); i++) {
691  old_root = &(it->second->get_root(i));
692  new_root = &(sol->get_root(i));
693  k_ref_old = old_root->kr;
694  dk_old = fabs(fabs(old_root->kr) - k_ref_old);
695  dk_new = fabs(fabs(new_root->kr) - k_red);
696 
697  if (dk_new < dk_old)
698  old_root->copy_root(*new_root);
699  }
700 
701  // delete the solution since its relevants roots have been swapped
702  // out
703  delete sol;
704  }
705 }
706 
707 
708 
709 
710 
712 {
713  // if the initial scanning has not been done, then do it now
714  const Real tol = 1.0e-5, max_allowable_g = 0.75;
715 
716  const unsigned int nvals = _flutter_solutions.begin()->second->n_roots();
717  // make sure that the solution has been generated
718  libmesh_assert(nvals);
719 
720  //
721  // for some cases some roots trail along the g=0 axis
722  // and should not be considered as flutter. These are simply
723  // modes where aerodynamics do not provide any damping.
724  // These modes will not have a damping more than tolerance
725  //
726 
727  std::vector<bool> modes_to_neglect(nvals);
728  std::fill(modes_to_neglect.begin(),
729  modes_to_neglect.end(), false);
730 
731  // look for the max g val for a mode, which will indicate if the
732  // mode is undamped or not
733  for (unsigned int i=0; i<nvals; i++) {
734  std::map<Real, MAST::FlutterSolutionBase*>::const_iterator
735  sol_it = _flutter_solutions.begin(),
736  sol_end = _flutter_solutions.end();
737  Real max_g_val = 0., val = 0.;
738  for ( ; sol_it!=sol_end; sol_it++) {
739  val = fabs(sol_it->second->get_root(i).g);
740  if (val > max_g_val)
741  max_g_val = val;
742  }
743  // check the maximum damping seen for this mode
744  if (max_g_val < tol)
745  modes_to_neglect[i] = true;
746  }
747 
748  // identify the flutter cross-overs. For this, move from
749  // higher k to lower k, and handle the k=0 cases separately
750  {
751  std::map<Real, MAST::FlutterSolutionBase*>::const_iterator
752  sol_it = _flutter_solutions.begin(), // first of the pair
753  sol_end = _flutter_solutions.end();
754  if (sol_it == sol_end)
755  return;
756 
757  // check if k=0 exists, and identify
758  if (fabs(sol_it->second->ref_val()) < tol) { // k = 0
759 
760  // k=0 makes sense only for divergence roots. Do not use them
761  // for crossover points if a finite damping was seen. Hence,
762  // do nothing here, and move to the next iterator
763  for (unsigned int i=0; i<nvals; i++) {
764  if (!sol_it->second->get_root(i).if_nonphysical_root &&
765  fabs(sol_it->second->get_root(i).g) < tol) {
768  cross->crossover_solutions.first = sol_it->second;
769  cross->crossover_solutions.second = sol_it->second;
770  cross->root = &sol_it->second->get_root(i);
771  cross->root_num = i;
772  std::pair<Real, MAST::FlutterRootCrossoverBase*>
773  val( cross->root->V, cross);
774  _flutter_crossovers.insert(val);
775  }
776  }
777  }
778  }
779 
780  // now look for oscillatory roots crossover points in decreasing
781  // order of k_red
782  for (unsigned int i=0; i<nvals; i++) {
783  std::map<Real, MAST::FlutterSolutionBase*>::const_reverse_iterator
784  sol_rit = _flutter_solutions.rbegin(), // first of the pair
785  sol_ritp1 = _flutter_solutions.rbegin(), // first of the pair
786  sol_rend = _flutter_solutions.rend();
787  if (sol_rit == sol_rend)
788  return;
789 
790  sol_ritp1++; // increment for the next pair of results
791  while (sol_ritp1 != sol_rend) {
792  // do not use k_red = 0, or if the root is invalid
793  if (sol_rit->second->get_root(i).if_nonphysical_root ||
794  sol_ritp1->second->get_root(i).if_nonphysical_root ||
795  fabs(sol_rit->second->ref_val()) < tol ||
796  fabs(sol_ritp1->second->ref_val()) < tol ||
797  fabs(sol_rit->second->get_root(i).g) > max_allowable_g ||
798  fabs(sol_ritp1->second->get_root(i).g) > max_allowable_g) {
799  // do nothing
800  }
801  else if (!modes_to_neglect[i]) { // look for the flutter roots
802  MAST::FlutterSolutionBase *lower = sol_rit->second,
803  *upper = sol_ritp1->second;
804 
805  if ((lower->get_root(i).g <= 0.) &&
806  (upper->get_root(i).g > 0.)) {
809  cross->crossover_solutions.first = lower; // -ve g
810  cross->crossover_solutions.second = upper; // +ve g
811  cross->root_num = i;
812  std::pair<Real, MAST::FlutterRootCrossoverBase*>
813  val( lower->get_root(i).V, cross);
814  _flutter_crossovers.insert(val);
815  }
816  else if ((lower->get_root(i).g > 0.) &&
817  (upper->get_root(i).g <= 0.)) {
820  cross->crossover_solutions.first = upper; // -ve g
821  cross->crossover_solutions.second = lower; // +ve g
822  cross->root_num = i;
823  std::pair<Real, MAST::FlutterRootCrossoverBase*>
824  val( upper->get_root(i).V, cross);
825  _flutter_crossovers.insert(val);
826  }
827  }
828 
829  // increment the pointers for next pair of roots
830  sol_rit++;
831  sol_ritp1++;
832  }
833  }
834 }
835 
836 
837 
838 std::unique_ptr<MAST::FlutterSolutionBase>
840  const Real v_ref,
841  const MAST::FlutterSolutionBase* prev_sol) {
842  // solve the eigenproblem L x = lambda R x
843  ComplexMatrixX R, L;
844  RealMatrixX stiff;
845 
846  libMesh::out
847  << " ====================================================" << std::endl
848  << "PK Solution" << std::endl
849  << " k_red = " << std::setw(10) << k_red << std::endl
850  << " V_ref = " << std::setw(10) << v_ref << std::endl;
851 
852  _initialize_matrices(k_red, v_ref, L, R, stiff);
853  LAPACK_ZGGEV ges;
854  ges.compute(L, R);
856 
858  root->init(*this,
859  k_red, v_ref,
860  (*_bref_param)(),
861  stiff, ges);
862  if (prev_sol)
863  root->sort(*prev_sol);
864 
865  libMesh::out
866  << "Finished PK Solution" << std::endl
867  << " ====================================================" << std::endl;
868 
869 
870  return std::unique_ptr<MAST::FlutterSolutionBase> (root);
871 }
872 
873 
874 
875 
876 void
878  const MAST::FunctionBase& p) {
879 
880  /*
881  libMesh::out
882  << " ====================================================" << std::endl
883  << "PK Sensitivity Solution" << std::endl
884  << " k_red = " << std::setw(10) << root.kr << std::endl
885  << " V_ref = " << std::setw(10) << root.V << std::endl;
886 
887  Complex eig = root.root, sens = 0., k_sens = 0., den = 0.;
888  Real par_g_par_alpha = 0., par_g_par_kref = 0., par_k_par_alpha = 0.,
889  V_sens=0.;
890 
891  // get the sensitivity of the matrices
892  ComplexMatrixX mat_A, mat_B, mat_A_sens, mat_B_sens;
893  ComplexVectorX v;
894  RealMatrixX stiff;
895 
896  // initialize the baseline matrices
897  _initialize_matrices(root.kr, root.V, mat_A, mat_B, stiff);
898 
899  // calculate the eigenproblem sensitivity
900  _initialize_matrix_sensitivity_for_param(params, i,
901  root.kr,
902  root.V,
903  mat_A_sens,
904  mat_B_sens);
905 
906  // the eigenproblem is A x - lambda B x = 0
907  // therefore, the denominator is obtained from the inner product of
908  // x^T B x
909  // sensitivity is
910  // -dlambda/dp x^T B x = - x^T (dA/dp - lambda dB/dp)
911  // or
912  // dlambda/dp = [x^T (dA/dp - lambda dB/dp)]/(x^T B x)
913 
914  // now calculate the quotient for sensitivity
915  // numerator = ( dA/dp - lambda dB/dp)
916  mat_B_sens *= -eig;
917  mat_B_sens += mat_A_sens;
918  v = mat_B_sens*root.eig_vec_right;
919  den = root.eig_vec_left.dot(mat_B*root.eig_vec_right);
920  sens = root.eig_vec_left.dot(v)/den;
921 
922  // now add the correction from sensitivity of g(k) = 0
923  par_g_par_alpha =
924  sens.imag()/eig.real() - eig.imag()/pow(eig.real(),2) * sens.real();
925 
926 
927  // next we need the sensitivity of k_red before we can calculate
928  // the sensitivity of flutter eigenvalue
929  _initialize_matrix_sensitivity_for_reduced_freq(root.kr,
930  root.V,
931  mat_A_sens,
932  mat_B_sens);
933 
934  // now calculate the quotient for sensitivity wrt k_red
935  // calculate numerator
936  mat_B_sens *= -eig;
937  mat_B_sens += mat_A_sens;
938  v = mat_B_sens*root.eig_vec_right;
939  k_sens = root.eig_vec_left.dot(v) / den;
940 
941  // use this to calculate the partial derivative of g wrt k_red
942  par_g_par_kref =
943  k_sens.imag()/eig.real() - eig.imag()/pow(eig.real(),2) * k_sens.real();
944 
945  // use this to calculate the sensitivity of k_red wrt alpha
946  par_k_par_alpha = -par_g_par_alpha / par_g_par_kref;
947 
948  // finally add the correction to the flutter sensitivity
949  sens += k_sens * par_k_par_alpha;
950 
951  // finally, the flutter speed sensitivity
952  V_sens = -.5*sens.real()/pow(eig.real(), 1.5);
953 
954  // set value in the return root
955  root.has_sensitivity_data = true;
956  root.root_sens = sens;
957  root.V_sens = V_sens;
958 
959  libMesh::out
960  << "Finished PK Sensitivity Solution" << std::endl
961  << " ====================================================" << std::endl;
962  */
963 }
964 
965 
966 
967 
968 void
970  const Real v_ref,
971  ComplexMatrixX& A, // stiff, aero, damp
972  ComplexMatrixX& B, // mass
973  RealMatrixX& stiff)// stiffness
974 {
975  // the PK method equations are
976  //
977  // p [ I 0 ] { X } = [ 0 I ] { X }
978  // [ 0 M ] { pX } [-K-qA -C] { pX }
979  // where M and K are the structural reduced-order mass and stiffness
980  // matrices, and A(kr) is the generalized aerodynamic force matrix.
981  //
982 
983 
984  const unsigned int n = (unsigned int)_basis_vectors->size();
985 
987  m = RealMatrixX::Zero(n, n),
988  k = RealMatrixX::Zero(n, n);
989 
991  a = ComplexMatrixX::Zero(n, n);
992 
993 
994  // now prepare a map of the quantities and ask the assembly object to
995  // calculate the quantities of interest.
996  std::map<MAST::StructuralQuantityType, RealMatrixX*> qty_map;
997  qty_map[MAST::MASS] = &m;
998  qty_map[MAST::STIFFNESS] = &k;
999 
1000 
1001  // set the velocity value in the parameter that was provided
1002  (*_kred_param) = k_red;
1003  (*_velocity_param) = v_ref;
1004 
1006 
1008  assemble_generalized_aerodynamic_force_matrix(*_basis_vectors, a);
1009 
1010  // scale the force vector by -1 since MAST calculates all quantities
1011  // for a R(X)=0 equation so that matrix/vector quantity is assumed
1012  // to be on the left of the equality. This is not consistent with
1013  // the expectation of a flutter solver, which expects the force
1014  // vector to be defined on the RHS. Hence, we multiply the quantity
1015  // here to maintain consistency.
1016  a *= -1.;
1017 
1018  A.topRightCorner (n, n) = ComplexMatrixX::Identity(n, n);
1019  A.bottomLeftCorner (n, n) = -k.cast<Complex>() + _rho/2.*v_ref*v_ref*a;
1020  B.topLeftCorner (n, n) = ComplexMatrixX::Identity(n, n);
1021  B.bottomRightCorner (n, n) = m.cast<Complex>();
1022 
1023 }
1024 
1025 
1026 
1027 void
1030  const Real k_red,
1031  const Real v_ref,
1032  ComplexMatrixX& L, // stiff, aero, damp
1033  ComplexMatrixX& R) { // mass
1034 
1035 
1036  libmesh_error(); // to be implemented
1037 }
1038 
1039 
1040 
void _insert_new_solution(const Real k_red_ref, MAST::FlutterSolutionBase *sol)
virtual void copy_root(const MAST::FlutterRootBase &f)
const MAST::FlutterRootBase & get_root(const unsigned int i) const
virtual std::unique_ptr< MAST::FlutterSolutionBase > _analyze(const Real k_red, const Real v_ref, const MAST::FlutterSolutionBase *prev_sol=nullptr)
Newton method to look for cross-over point method search.
std::vector< libMesh::NumericVector< Real > * > * _basis_vectors
basis vector used to define the reduced order model
void _initialize_matrices(const Real k_red, const Real v_ref, ComplexMatrixX &L, ComplexMatrixX &R, RealMatrixX &stiff)
initializes the matrices for the specified k_red.
virtual void sort(const MAST::FlutterSolutionBase &sol)
sort this root with respect to the given solution from a previous eigen solution. ...
virtual void compute(const ComplexMatrixX &A, const ComplexMatrixX &B, bool computeEigenvectors=true)
computes the eigensolution for .
virtual std::pair< bool, MAST::FlutterSolutionBase * > _bisection_search(const std::pair< MAST::FlutterSolutionBase *, MAST::FlutterSolutionBase * > &ref_sol_range, const unsigned int root_num, const Real g_tol, const unsigned int max_iters)
unsigned int _n_k_red_divs
number of division in the reference value range for initial scanning
Real _rho
flight density
This is a scalar function whose value can be changed and one that can be used as a design variable in...
Definition: parameter.h:35
std::pair< Real, Real > _kr_range
range of reference values within which to find flutter roots
std::multimap< Real, MAST::FlutterRootCrossoverBase * > _flutter_crossovers
the map of flutter crossover points versus average velocity of the two bounding roots ...
libMesh::Real Real
void _initialize_matrix_sensitivity_for_param(const MAST::FunctionBase &f, const Real k_red, const Real U_inf, ComplexMatrixX &L, ComplexMatrixX &R)
Assembles the reduced order system structural and aerodynmaic matrices for specified flight velocity ...
virtual std::pair< bool, MAST::FlutterRootBase * > find_next_root(const Real g_tol, const unsigned int n_bisection_iters)
Looks through the list of flutter cross-over points and iteratively zooms in to find the cross-over p...
unsigned int _n_V_divs
number of division in the reference value range for initial scanning
virtual void calculate_sensitivity(MAST::FlutterRootBase &root, const MAST::FunctionBase &f)
Calculate the sensitivity of the flutter root with respect to the parameter f.
libMesh::Complex Complex
virtual void clear_solutions()
clears the solutions stored from a previous analysis.
virtual void scan_for_roots()
Scans for flutter roots in the analyzed points, and identified the divergence (if k_red = 0...
std::ofstream * _output
file to which the result will be written
unsigned int n_roots() const
number of roots in this solution
virtual void clear()
clears the solution and other data from this solver
void scale_eigenvectors_to_identity_innerproduct()
Scales the right eigenvector so that the inner product with respect to the B matrix is equal to an Id...
Matrix< Real, Dynamic, Dynamic > RealMatrixX
Matrix< Complex, Dynamic, Dynamic > ComplexMatrixX
virtual void assemble_reduced_order_quantity(std::vector< libMesh::NumericVector< Real > * > &basis, std::map< MAST::StructuralQuantityType, RealMatrixX * > &mat_qty_map)
calculates the reduced order matrix given the basis provided in basis.
virtual void print_crossover_points()
Prints the crossover points output.
virtual void print_sorted_roots()
Prints the sorted roots to the output.
void initialize(std::vector< libMesh::NumericVector< Real > * > &basis)
initializes the data structres for a flutter solution.
virtual unsigned int n_roots_found() const
finds the number of critical points already identified in the procedure.
Real ref_val() const
the reduced frequency for this solution
virtual void _identify_crossover_points()
identifies all cross-over and divergence points from analyzed roots
std::pair< MAST::FlutterSolutionBase *, MAST::FlutterSolutionBase * > crossover_solutions
const MAST::FlutterRootBase & get_root(const unsigned int n) const
returns the n th root in terms of ascending velocity that is found by the solver
virtual void init(const MAST::PKFlutterSolver &solver, const Real k_red, const Real v_ref, const Real bref, const RealMatrixX &kmat, const MAST::LAPACK_ZGGEV &eig_sol)
initializes the flutter solution from an eigensolution
MAST::Parameter * _kred_param
Parameter that define the reduced frequency.
MAST::StructuralFluidInteractionAssembly * _assembly
structural assembly that provides the assembly of the system matrices.
MAST::Parameter * _velocity_param
Parameter that define the velocity.
std::pair< Real, Real > _V_range
range of reference values within which to find flutter roots
RealVectorX modal_participation
virtual std::pair< bool, MAST::FlutterRootBase * > find_critical_root(const Real g_tol, const unsigned int n_bisection_iters)
This method checks if the flutter root corresponding to the lowest velocity crossover has been calcul...
virtual void clear()
clears the solution and other data from this solver
MAST::Parameter * _bref_param
reference chord
std::map< Real, MAST::FlutterSolutionBase * > _flutter_solutions
map of velocity sorted flutter solutions
void initialize(MAST::Parameter &V_param, MAST::Parameter &kr_param, MAST::Parameter &bref_param, Real rho, Real V_lower, Real V_upper, unsigned int n_V_divs, Real kr_lower, Real kr_upper, unsigned int n_kr_divs, std::vector< libMesh::NumericVector< Real > * > &basis)
initializes the data structres for a flutter solution.