MAST
Multidisciplinary-design Adaptation and Sensitivity Toolkit (MAST)
ug_flutter_solver.cpp
Go to the documentation of this file.
1 /*
2  * MAST: Multidisciplinary-design Adaptation and Sensitivity Toolkit
3  * Copyright (C) 2013-2020 Manav Bhatia and MAST authors
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
31 #include "base/parameter.h"
32 #include "base/nonlinear_system.h"
33 
34 
37 _kr_param(nullptr),
38 _bref_param(nullptr),
39 _kr_range(),
40 _n_kr_divs(0.),
41 _include_highest_kr_unstable(false) {
42 
43 }
44 
45 
46 
48 
49  this->clear();
50 }
51 
52 
53 
54 
55 void
57 
58  this->clear_solutions();
59 
60  _kr_param = nullptr;
61  _bref_param = nullptr;
62  _kr_range = std::pair<Real, Real>(0.,0.);
63  _n_kr_divs = 0;
64 
66 }
67 
68 
69 
70 
71 void
74  MAST::Parameter& bref_param,
75  Real rho,
76  Real kr_lower,
77  Real kr_upper,
78  unsigned int n_kr_divs,
79  std::vector<libMesh::NumericVector<Real> *>& basis) {
80 
81 
82  _kr_param = &kr_param;
83  _bref_param = &bref_param;
84  _rho = rho;
85  _kr_range.first = kr_lower;
86  _kr_range.second = kr_upper;
87  _n_kr_divs = n_kr_divs;
88 
90 }
91 
92 
93 
94 
95 void
97 
98  std::map<Real, MAST::FlutterSolutionBase*>::iterator it =
99  _flutter_solutions.begin();
100 
101  for ( ; it != _flutter_solutions.end(); it++)
102  delete it->second;
103 
104  std::multimap<Real, MAST::FlutterRootCrossoverBase*>::iterator cross_it =
105  _flutter_crossovers.begin();
106 
107  for ( ; cross_it != _flutter_crossovers.end(); cross_it++)
108  delete cross_it->second;
109 
110  _flutter_solutions.clear();
111  _flutter_crossovers.clear();
112 }
113 
114 
115 
116 
117 unsigned int
119 
120  std::multimap<Real, MAST::FlutterRootCrossoverBase*>::const_iterator
121  it = _flutter_crossovers.begin(),
122  end = _flutter_crossovers.end();
123 
124  unsigned int n = 0;
125  for ( ; it!=end; it++)
126  if (it->second->root) // a valid root pointer has been assigned
127  n++;
128 
129  return n;
130 }
131 
132 
133 
134 
135 
137 MAST::UGFlutterSolver::get_root(const unsigned int n) const {
138 
139  libmesh_assert(n < n_roots_found());
140 
141  std::multimap<Real, MAST::FlutterRootCrossoverBase*>::const_iterator
142  it = _flutter_crossovers.begin(),
143  end = _flutter_crossovers.end();
144 
145  unsigned int root_num = 0;
146  for ( ; it!=end; it++) {
147  if (it->second->root) { // a valid root pointer has been assigned
148  if (root_num == n) // root num matches the one being requested
149  return *(it->second->root);
150  else
151  root_num++;
152  }
153  }
154 
155  libmesh_assert(false); // should not get here
156 }
157 
158 
159 
160 
161 
162 std::pair<bool, MAST::FlutterRootBase*>
164  const unsigned int n_bisection_iters)
165 {
166  // iterate over the cross-over points and calculate the next that has
167  // not been evaluated
168  std::multimap<Real, MAST::FlutterRootCrossoverBase*>::iterator
169  it = _flutter_crossovers.begin(), end = _flutter_crossovers.end();
170  while ( it != end)
171  {
172  MAST::FlutterRootCrossoverBase* cross = it->second;
173 
174  if (!cross->root) {
175  const unsigned int root_num = cross->root_num;
176  std::pair<bool, MAST::FlutterSolutionBase*> sol;
177  // first try the Newton search. If that fails, then
178  // try the bisection search
179  /*sol = newton_search(*cross->crossover_solutions.second,
180  root_num,
181  g_tol,
182  n_bisection_iters);
183  if (!sol.first)*/
185  root_num, g_tol, n_bisection_iters);
186 
187  cross->root = &(sol.second->get_root(root_num));
188 
189  // now, remove this entry from the _flutter_crossover points and
190  // reinsert it with the actual critical velocity
191  _flutter_crossovers.erase(it);
192  std::pair<Real, MAST::FlutterRootCrossoverBase*>
193  val(cross->root->V, cross);
194  _flutter_crossovers.insert(val);
195  return std::pair<bool, MAST::FlutterRootBase*> (true, cross->root);
196  }
197 
198  it++;
199  }
200 
201  // if it gets here, no new root was found
202  return std::pair<bool, MAST::FlutterRootBase*> (false, nullptr);
203 }
204 
205 
206 
207 std::pair<bool, MAST::FlutterRootBase*>
209  const unsigned int n_bisection_iters)
210 {
211  // iterate over the cross-over points and calculate the next that has
212  // not been evaluated
213  std::multimap<Real, MAST::FlutterRootCrossoverBase*>::iterator
214  it = _flutter_crossovers.begin(), end = _flutter_crossovers.end();
215 
216  if (it == end) // no potential cross-over points were identified
217  return std::pair<bool, MAST::FlutterRootBase*> (false, nullptr);
218 
219  // it is possible that once the root has been found, its velocity end up
220  // putting it at a higher velocity in the map, so we need to check if
221  // the critical root has changed
222  while (!it->second->root)
223  {
224  MAST::FlutterRootCrossoverBase* cross = it->second;
225 
226  if (!cross->root) {
227 
228  const unsigned int root_num = cross->root_num;
229  std::pair<bool, MAST::FlutterSolutionBase*> sol;
230  // first try the Newton search. If that fails, then
231  // try the bisection search
232  /*sol = newton_search(*cross->crossover_solutions.second,
233  root_num,
234  g_tol,
235  n_bisection_iters);
236  if (!sol.first)*/
238  root_num, g_tol, n_bisection_iters);
239 
240  cross->root = &(sol.second->get_root(root_num));
241 
242  // now, remove this entry from the _flutter_crossover points and
243  // reinsert it with the actual critical velocity
244  _flutter_crossovers.erase(it);
245  std::pair<Real, MAST::FlutterRootCrossoverBase*>
246  val(cross->root->V, cross);
247  _flutter_crossovers.insert(val);
248  }
249 
250  // update the iterator to make sure that this is updated
251  it = _flutter_crossovers.begin(); end = _flutter_crossovers.end();
252  }
253 
254  // if it gets here, then the root was successfully found
255  return std::pair<bool, MAST::FlutterRootBase*> (true, it->second->root);
256 }
257 
258 
259 
260 
261 void
263 
264  // if the initial scanning has not been done, then do it now
265  if (!_flutter_solutions.size()) {
266  // march from the upper limit to the lower to find the roots
267  Real
268  current_kr = _kr_range.second,
269  delta_kr = (_kr_range.second - _kr_range.first)/_n_kr_divs;
270 
271  std::vector<Real> k_vals(_n_kr_divs+1);
272  for (unsigned int i=0; i<_n_kr_divs+1; i++) {
273  k_vals[i] = current_kr;
274  current_kr -= delta_kr;
275  }
276  k_vals[_n_kr_divs] = _kr_range.first; // to get around finite-precision arithmetic
277 
278  MAST::FlutterSolutionBase* prev_sol = nullptr;
279  for (unsigned int i=0; i< _n_kr_divs+1; i++) {
280 
281  current_kr = k_vals[i];
282  std::unique_ptr<MAST::FlutterSolutionBase> sol =
283  _analyze(current_kr, prev_sol);
284 
285  prev_sol = sol.get();
286 
287  if (_output)
288  sol->print(*_output);
289 
290  // add the solution to this solver
291  bool if_success =
292  _flutter_solutions.insert(std::pair<Real, MAST::FlutterSolutionBase*>
293  (current_kr, sol.release())).second;
294 
295  libmesh_assert(if_success);
296  }
297 
299  }
300 }
301 
302 
303 
304 
305 
306 void
308 {
309 
310  // write only if the output was set
311  if (!_output)
312  return;
313 
314  std::map<Real, MAST::FlutterSolutionBase*>::const_iterator
315  sol_it = _flutter_solutions.begin(),
316  sol_end = _flutter_solutions.end();
317  libmesh_assert(sol_it != sol_end); // solutions should have been evaluated
318 
319  unsigned int nvals = sol_it->second->n_roots();
320  libmesh_assert(nvals); // should not be zero
321 
322  // each root is written separately one after another
323  for (unsigned int i=0; i<nvals; i++)
324  {
325  // print the headers
326  *_output
327  << "** Root # "
328  << std::setw(5) << i << " **" << std::endl
329  << std::setw(15) << "kr"
330  << std::setw(15) << "g"
331  << std::setw(15) << "V" << std::endl;
332 
333  // update the iterator for this analysis
334  sol_it = _flutter_solutions.begin();
335 
336  // write the data from all solutions
337  for ( ; sol_it != sol_end; sol_it++)
338  {
339  const MAST::FlutterRootBase& root =
340  sol_it->second->get_root(i);
341 
342  *_output
343  << std::setw(15) << root.kr
344  << std::setw(15) << root.g
345  << std::setw(15) << root.V << std::endl;
346  }
347  *_output << std::endl << std::endl;
348  }
349 
350 
351  // write the roots identified using iterative search technique
352  std::streamsize prec = _output->precision();
353 
354  unsigned int nroots = this->n_roots_found();
355  *_output << std::endl
356  << "n critical roots identified: " << nroots << std::endl;
357  for (unsigned int i=0; i<nroots; i++)
358  {
359  const MAST::FlutterRootBase& root = this->get_root(i);
360  *_output
361  << "** Root : " << std::setw(5) << i << " **" << std::endl
362  << "kr = " << std::setw(15) << std::setprecision(15) << root.kr << std::endl
363  << "V = " << std::setw(15) << std::setprecision(15) << root.V << std::endl
364  << "g = " << std::setw(15) << root.g << std::endl
365  << "omega = " << std::setw(15) << root.omega << std::endl
366  << std::setprecision((int) prec) // set the precision to the default value
367  << "Modal Participation : " << std::endl ;
368  for (unsigned int j=0; j<nvals; j++)
369  *_output
370  << "(" << std::setw(5) << j << "): "
371  << std::setw(10) << root.modal_participation(j)
372  << std::setw(3) << " ";
373  *_output << std::endl << std::endl;
374  }
375 }
376 
377 
378 void
380 {
381  // print only if the output was set
382  if (!_output)
383  return;
384 
385  *_output << "n crossover points found: "
386  << std::setw(5) << _flutter_crossovers.size() << std::endl;
387 
388  std::multimap<Real, MAST::FlutterRootCrossoverBase*>::const_iterator
389  it = _flutter_crossovers.begin(), end = _flutter_crossovers.end();
390 
391  unsigned int i=0;
392 
393  for ( ; it != end; it++) {
394  *_output << "** Point : " << std::setw(5) << i << " **" << std::endl;
395  it->second->print(*_output);
396  *_output << std::endl;
397  i++;
398  }
399 }
400 
401 
402 
403 
404 
405 std::pair<bool, MAST::FlutterSolutionBase*>
408  MAST::FlutterSolutionBase*>& ref_sol_range,
409  const unsigned int root_num,
410  const Real g_tol,
411  const unsigned int max_iters) {
412 
413  // assumes that the upper k_val has +ve g val and lower k_val has -ve
414  // k_val
415  Real
416  lower_kr = ref_sol_range.first->get_root(root_num).kr,
417  lower_g = ref_sol_range.first->get_root(root_num).g,
418  upper_kr = ref_sol_range.second->get_root(root_num).kr,
419  upper_g = ref_sol_range.second->get_root(root_num).g,
420  new_kr = 0.;
421  unsigned int n_iters = 0;
422 
423  MAST::FlutterSolutionBase* new_sol = nullptr;
424  std::pair<bool, MAST::FlutterSolutionBase*> rval(false, nullptr);
425 
426  while (n_iters < max_iters) {
427 
428  libMesh::out
429  << upper_kr << " "
430  << upper_g << " " << std::endl
431  << lower_kr << " "
432  << lower_g << " " << std::endl;
433 
434  new_kr = lower_kr +
435  (upper_kr-lower_kr)/(upper_g-lower_g)*(0.-lower_g); // linear interpolation
436 
437  new_sol = _analyze(new_kr, ref_sol_range.first).release();
438 
439  if (_output)
440  new_sol->print(*_output);
441 
442  // add the solution to this solver
443  bool if_success =
444  _flutter_solutions.insert(std::pair<Real, MAST::FlutterSolutionBase*>
445  (new_kr, new_sol)).second;
446 
447  libmesh_assert(if_success);
448 
449  const MAST::UGFlutterRoot& root =
450  dynamic_cast<const MAST::UGFlutterRoot&>(new_sol->get_root(root_num));
451 
452  // check if the new damping value
453  if (fabs(root.g) <= g_tol) {
454 
455  rval.first = true;
456  rval.second = new_sol;
457  return rval;
458  }
459 
460  // update the V value
461  if (root.g < 0.) {
462 
463  lower_kr = new_kr;
464  lower_g = root.g;
465  }
466  else {
467 
468  upper_kr = new_kr;
469  upper_g = root.g;
470  }
471 
472  n_iters++;
473  }
474 
475  // return false, along with the latest sol
476  rval.first = false;
477  rval.second = new_sol;
478 
479  return rval;
480 }
481 
482 
483 
484 
485 std::unique_ptr<MAST::FlutterSolutionBase>
487  const MAST::FlutterSolutionBase* prev_sol) {
488 
489  libMesh::out
490  << " ====================================================" << std::endl
491  << "Eigensolution" << std::endl
492  << " kr_ref = " << std::setw(10) << kr_ref << std::endl;
493 
495  A,
496  B;
497 
498  // initialize the matrices for the structure.
499  _initialize_matrices(kr_ref, A, B);
500 
501  MAST::LAPACK_ZGGEV ges;
502  ges.compute(A, B);
504 
506  root->init(*this, kr_ref, (*_bref_param)(), ges);
507  if (prev_sol)
508  root->sort(*prev_sol);
509 
510  libMesh::out
511  << "Finished Eigensolution" << std::endl
512  << " ====================================================" << std::endl;
513 
514 
515  return std::unique_ptr<MAST::FlutterSolutionBase> (root);
516 }
517 
518 
519 
520 
521 void
523  ComplexMatrixX &A,
524  ComplexMatrixX &B) {
525 
526  // the UG method equations are
527  //
528  // ((kr/b)^2 M + rho/2 A(kr))q = lambda K q
529  // where M and K are the structural reduced-order mass and stiffness
530  // matrices, and A(kr) is the generalized aerodynamic force matrix.
531  //
532 
533  const unsigned int n = (unsigned int)_basis_vectors->size();
534 
536  m = RealMatrixX::Zero(n, n),
537  k = RealMatrixX::Zero(n, n);
538 
540  a = ComplexMatrixX::Zero(n, n);
541 
542  // now prepare a map of the quantities and ask the assembly object to
543  // calculate the quantities of interest.
544  std::map<MAST::StructuralQuantityType, RealMatrixX*> qty_map;
545  qty_map[MAST::MASS] = &m;
546  qty_map[MAST::STIFFNESS] = &k;
547 
548 
549  // set the velocity value in the parameter that was provided
550  (*_kr_param) = kr;
551 
553 
555  assemble_generalized_aerodynamic_force_matrix(*_basis_vectors, a);
556 
557 
558  // scale the force vector by -1 since MAST calculates all quantities
559  // for a R(X)=0 equation so that matrix/vector quantity is assumed
560  // to be on the left of the equality. This is not consistent with
561  // the expectation of a flutter solver, which expects the force
562  // vector to be defined on the RHS. Hence, we multiply the quantity
563  // here to maintain consistency.
564  a *= -1.;
565 
566 
567  A = pow(kr/(*_bref_param)(),2) * m.cast<Complex>() + (_rho/2.) * a;
568  B = k.cast<Complex>();
569 }
570 
571 
572 
573 
574 
575 
576 void
579  const libMesh::NumericVector<Real>& dXdp,
580  Real kr,
581  ComplexMatrixX& A,
582  ComplexMatrixX& B) {
583 
584  // the UG method equations are
585  //
586  // ((kr/b)^2 M + rho/2 A(kr))q = lambda K q
587  // where M and K are the structural reduced-order mass and stiffness
588  // matrices, and A(kr) is the generalized aerodynamic force matrix.
589  //
590 
591  const unsigned int n = (unsigned int)_basis_vectors->size();
592 
594  m = RealMatrixX::Zero(n, n),
595  k = RealMatrixX::Zero(n, n);
596 
598  a = ComplexMatrixX::Zero(n, n);
599 
600  // now prepare a map of the quantities and ask the assembly object to
601  // calculate the quantities of interest.
602  std::map<MAST::StructuralQuantityType, RealMatrixX*> qty_map;
603  qty_map[MAST::MASS] = &m;
604  qty_map[MAST::STIFFNESS] = &k;
605 
606 
607  // set the velocity value in the parameter that was provided
608  (*_kr_param) = kr;
609 
612  qty_map);
613 
614  // currently, sensitivity of generalized aero matrix is available only
615  // for freq (ie non-structural parameters). Once the dependence on
616  // base sol sensitivity is introduced, this will change.
617  //dynamic_cast<MAST::FSIGeneralizedAeroForceAssembly*>(_assembly)->
618  //assemble_generalized_aerodynamic_force_matrix(*_basis_vectors, a);
619 
620 
621  // scale the force vector by -1 since MAST calculates all quantities
622  // for a R(X)=0 equation so that matrix/vector quantity is assumed
623  // to be on the left of the equality. This is not consistent with
624  // the expectation of a flutter solver, which expects the force
625  // vector to be defined on the RHS. Hence, we multiply the quantity
626  // here to maintain consistency.
627  a *= -1.;
628 
629  A = pow(kr/(*_bref_param)(),2) * m.cast<Complex>() + (_rho/2.) * a;
630  B = k.cast<Complex>();
631 }
632 
633 
634 
635 
636 
637 
638 void
641  ComplexMatrixX& A,
642  ComplexMatrixX& B) {
643 
644  // the UG method equations are
645  //
646  // ((kr/b)^2 M + rho/2 A(kr))q = lambda K q
647  // where M and K are the structural reduced-order mass and stiffness
648  // matrices, and A(kr) is the generalized aerodynamic force matrix.
649  //
650 
651  const unsigned int n = (unsigned int)_basis_vectors->size();
652 
654  m = RealMatrixX::Zero(n, n);
655 
657  a = ComplexMatrixX::Zero(n, n);
658 
659  // now prepare a map of the quantities and ask the assembly object to
660  // calculate the quantities of interest.
661  std::map<MAST::StructuralQuantityType, RealMatrixX*> qty_map;
662  qty_map[MAST::MASS] = &m;
663 
664 
665  // set the velocity value in the parameter that was provided
666  (*_kr_param) = kr;
667 
669  qty_map);
670 
672  assemble_generalized_aerodynamic_force_matrix(*_basis_vectors, a, _kr_param);
673 
674  // scale the force vector by -1 since MAST calculates all quantities
675  // for a R(X)=0 equation so that matrix/vector quantity is assumed
676  // to be on the left of the equality. This is not consistent with
677  // the expectation of a flutter solver, which expects the force
678  // vector to be defined on the RHS. Hence, we multiply the quantity
679  // here to maintain consistency.
680  a *= -1.;
681 
682 
683  A = 2.*kr*pow((*_bref_param)(),-2) * m.cast<Complex>() + (_rho/2.) * a;
684  B = ComplexMatrixX::Zero(n, n);
685 }
686 
687 
688 
689 
690 
691 void
693 
694  // if the initial scanning has not been done, then do it now
695  const Real tol = 1.0e-5, max_allowable_g = 0.75;
696 
697  const unsigned int nvals = _flutter_solutions.begin()->second->n_roots();
698  // make sure that the solution has been generated
699  libmesh_assert(nvals);
700 
701  //
702  // for some cases some roots trail along the g=0 axis
703  // and should not be considered as flutter. These are simply
704  // modes where aerodynamics do not provide any damping.
705  // These modes will not have a damping more than tolerance
706  //
707 
708  std::vector<bool> modes_to_neglect(nvals);
709  std::fill(modes_to_neglect.begin(),
710  modes_to_neglect.end(), false);
711 
712  // look for the max g val for a mode, which will indicate if the
713  // mode is undamped or not
714  for (unsigned int i=0; i<nvals; i++) {
715  std::map<Real, MAST::FlutterSolutionBase*>::const_iterator
716  sol_it = _flutter_solutions.begin(),
717  sol_end = _flutter_solutions.end();
718  Real max_g_val = 0., val = 0.;
719  for ( ; sol_it!=sol_end; sol_it++) {
720  val = fabs(sol_it->second->get_root(i).g);
721  if (val > max_g_val)
722  max_g_val = val;
723  }
724  // check the maximum damping seen for this mode
725  if (max_g_val < tol)
726  modes_to_neglect[i] = true;
727  }
728 
729  // identify the flutter cross-overs. For this, move from
730  // higher k to lower k, and handle the k=0 cases separately
731  {
732  std::map<Real, MAST::FlutterSolutionBase*>::const_iterator
733  sol_it = _flutter_solutions.begin(), // first of the pair
734  sol_end = _flutter_solutions.end();
735  if (sol_it == sol_end)
736  return;
737 
738  // check if k=0 exists, and identify
739  if (fabs(sol_it->second->ref_val()) < tol) { // k = 0
740 
741  // k=0 makes sense only for divergence roots. Do not use them
742  // for crossover points if a finite damping was seen. Hence,
743  // do nothing here, and move to the next iterator
744  for (unsigned int i=0; i<nvals; i++) {
745  if (!sol_it->second->get_root(i).if_nonphysical_root &&
746  fabs(sol_it->second->get_root(i).g) < tol) {
749  cross->crossover_solutions.first = sol_it->second;
750  cross->crossover_solutions.second = sol_it->second;
751  cross->root = &sol_it->second->get_root(i);
752  cross->root_num = i;
753  std::pair<Real, MAST::FlutterRootCrossoverBase*>
754  val( cross->root->V, cross);
755  _flutter_crossovers.insert(val);
756  }
757  }
758  }
759  }
760 
761  // now look for oscillatory roots crossover points in decreasing
762  // order of k_red
763  for (unsigned int i=0; i<nvals; i++) {
764  std::map<Real, MAST::FlutterSolutionBase*>::const_reverse_iterator
765  sol_rit = _flutter_solutions.rbegin(), // first of the pair
766  sol_ritp1 = _flutter_solutions.rbegin(), // first of the pair
767  sol_rend = _flutter_solutions.rend();
768  if (sol_rit == sol_rend)
769  return;
770 
771  sol_ritp1++; // increment for the next pair of results
772  bool if_process = false;
773 
774  while (sol_ritp1 != sol_rend) {
775  if_process =
776  (// both should be valid roots
777  (!sol_rit->second->get_root(i).if_nonphysical_root &&
778  !sol_ritp1->second->get_root(i).if_nonphysical_root) &&
779  // atleast one |g| > tol
780  (fabs(sol_rit->second->ref_val()) > tol ||
781  fabs(sol_ritp1->second->ref_val()) > tol) &&
782  // both |g| < max_g
783  (fabs(sol_rit->second->get_root(i).g) < max_allowable_g &&
784  fabs(sol_ritp1->second->get_root(i).g) < max_allowable_g) &&
785  // if the mode has been identified to be trailing along g =0,
786  // neglect it
787  !modes_to_neglect[i]);
788 
789  // do not use k_red = 0, or if the root is invalid
790  if (if_process) { // look for the flutter roots
791  MAST::FlutterSolutionBase *lower = sol_rit->second,
792  *upper = sol_ritp1->second;
793 
794  if ((lower->get_root(i).g <= 0.) &&
795  (upper->get_root(i).g > 0.)) {
798  cross->crossover_solutions.first = lower; // -ve g
799  cross->crossover_solutions.second = upper; // +ve g
800  cross->root_num = i;
801  std::pair<Real, MAST::FlutterRootCrossoverBase*>
802  val( lower->get_root(i).V, cross);
803  _flutter_crossovers.insert(val);
804  }
805  else if ((lower->get_root(i).g > 0.) &&
806  (upper->get_root(i).g <= 0.)) {
809  cross->crossover_solutions.first = upper; // -ve g
810  cross->crossover_solutions.second = lower; // +ve g
811  cross->root_num = i;
812  std::pair<Real, MAST::FlutterRootCrossoverBase*>
813  val( upper->get_root(i).V, cross);
814  _flutter_crossovers.insert(val);
815  }
816  }
817 
818  // increment the pointers for next pair of roots
819  sol_rit++;
820  sol_ritp1++;
821  }
822 
823  // now check to see if the root started out as critical at
824  // the highest k value.
825  sol_rit = _flutter_solutions.rbegin();
826  sol_ritp1 = _flutter_solutions.rbegin();
827  sol_ritp1++;
828 
829  if_process =
830  (// both should be valid roots
831  (!sol_rit->second->get_root(i).if_nonphysical_root &&
832  !sol_ritp1->second->get_root(i).if_nonphysical_root) &&
833  // atleast one |g| > tol
834  (fabs(sol_rit->second->ref_val()) > tol ||
835  fabs(sol_ritp1->second->ref_val()) > tol) &&
836  // both |g| < max_g
837  (fabs(sol_rit->second->get_root(i).g) < max_allowable_g &&
838  fabs(sol_ritp1->second->get_root(i).g) < max_allowable_g) &&
839  // if the mode has been identified to be trailing along g =0,
840  // neglect it
841  !modes_to_neglect[i]);
842 
843  Real g_val = sol_rit->second->get_root(i).g;
844  if (if_process &&
845  g_val > 0 &&
846  g_val < max_allowable_g &&
850  // here, both roots for crossover are set to be the same
851  // note that this will not work with bisection search, since
852  // it needs a bracket to begin with.
853  // However, the Newton search should be able to find the
854  // critical root location.
855  cross->crossover_solutions.first = sol_rit->second;
856  cross->crossover_solutions.second = sol_rit->second;
857  cross->root_num = i;
858  std::pair<Real, MAST::FlutterRootCrossoverBase*>
859  val( sol_rit->second->get_root(i).V, cross);
860  _flutter_crossovers.insert(val);
861  }
862 
863  }
864 }
865 
866 
867 
868 
869 void
872  const MAST::FunctionBase& f,
873  libMesh::NumericVector<Real>* dXdp,
874  libMesh::NumericVector<Real>* dXdkr) {
875 
876 
877 
878  libMesh::out
879  << " ====================================================" << std::endl
880  << "UG Sensitivity Solution" << std::endl
881  << " k_red = " << std::setw(10) << root.kr << std::endl
882  << " V_ref = " << std::setw(10) << root.V << std::endl;
883 
884  Complex
885  eig = root.root,
886  deig_dp = 0.,
887  deig_dkr = 0.,
888  den = 0.;
889 
890  Real
891  dkr_dp = 0.,
892  dg_dp = 0.,
893  dg_dkr = 0.;
894 
895 
896  // get the sensitivity of the matrices
898  mat_A,
899  mat_B,
900  mat_A_sens,
901  mat_B_sens;
902 
903  ComplexVectorX v;
904 
905  // initialize the baseline matrices
906  _initialize_matrices(root.kr, mat_A, mat_B);
907 
908  // if the sensitivity of the solution was provided, then use that.
909  // otherwise pass a zero vector
910  libMesh::NumericVector<Real>* sol_sens = dXdp;
911  std::unique_ptr<libMesh::NumericVector<Real> > zero_sol_sens;
912  if (!dXdp) {
913  zero_sol_sens.reset(_assembly->system().solution->zero_clone().release());
914  sol_sens = zero_sol_sens.get();
915  }
916  else
917  sol_sens = dXdp;
918 
919  // calculate the eigenproblem sensitivity
921  *sol_sens,
922  root.kr,
923  mat_A_sens,
924  mat_B_sens);
925 
926 
927  // the eigenproblem is y^T A x - lambda y^T B x = 0
928  // therefore, the denominator is obtained from the inner product of
929  // y^T B x
930  // sensitivity is
931  // -dlambda/dp y^T B x = - y^T (dA/dp - lambda dB/dp)
932  // or
933  // dlambda/dp = [y^T (dA/dp - lambda dB/dp)]/(y^T B x)
934 
935  // now calculate the numerator for sensitivity
936  // numerator = ( dA/dp - lambda dB/dp)
937  den = root.eig_vec_left.dot(mat_B*root.eig_vec_right);
938  deig_dp = root.eig_vec_left.dot((mat_A_sens.cast<Complex>() -
939  eig*mat_B_sens.cast<Complex>())*root.eig_vec_right)/den;
940 
941  // next we need the sensitivity of eigenvalue wrt kr
942  // identify the sensitivity of solution to be used based on the
943  // function arguments
944  sol_sens = zero_sol_sens.get();
945 
947  mat_A_sens,
948  mat_B_sens);
949 
950  // now calculate the quotient for sensitivity wrt k_red
951  // calculate numerator
952  deig_dkr = root.eig_vec_left.dot((mat_A_sens.cast<Complex>() -
953  eig*mat_B_sens.cast<Complex>())*root.eig_vec_right)/den;
954 
955  // using this, the following quantities are caluclated
956  // since eig = lambda = (1+ig)/V^2.
957  // V = (1/re(eig))^(-1/2)
958  // g = im(eig)/re(eig)
959  //
960  // Therefore, the sensitivity of g and V are
961  // dV/dp = -1/2 (1/re(eig))^(-3/2) deig_re/dp
962  // dg/dp = deig_im/dp / re(eig) - im(eig)/re(eig)^2 deig_re/dp
963 
964  dg_dp =
965  deig_dp.imag()/eig.real() - eig.imag()/pow(eig.real(),2) * deig_dp.real();
966  dg_dkr =
967  deig_dkr.imag()/eig.real() - eig.imag()/pow(eig.real(),2) * deig_dkr.real();
968 
969 
970  // since the constraint that defines flutter speed is that damping = 0,
971  // g(p, kr) = 0,
972  // then the total derivative of this constraint is
973  // dg/dp + dg/dkr dkr/dp = 0
974  // or, dkr/dp = -dg/dp / dg/dkr
975  dkr_dp = -dg_dp / dg_dkr;
976  root.kr_sens = dkr_dp;
977 
978  // Using this, the sensitivity of flutter speed if calculated as
979  // dV/dp = dV/dp + dV/dkr dkr/dp
980  root.root_sens = deig_dp + deig_dkr * dkr_dp;
981  root.V_sens = -.5*root.root_sens.real()/pow(eig.real(), 1.5);
982  root.has_sensitivity_data = true;
983 
984  libMesh::out
985  << "Finished Flutter Sensitivity Solution" << std::endl
986  << " ====================================================" << std::endl;
987 
988 }
989 
990 
991 
unsigned int _n_kr_divs
number of division in the reference value range for initial scanning
const MAST::NonlinearSystem & system() const
ComplexVectorX eig_vec_left
std::vector< libMesh::NumericVector< Real > * > * _basis_vectors
basis vector used to define the reduced order model
virtual void compute(const ComplexMatrixX &A, const ComplexMatrixX &B, bool computeEigenvectors=true)
computes the eigensolution for .
virtual void print(std::ostream &output)=0
prints the data and modes from this solution
virtual void print_sorted_roots()
Prints the sorted roots to the output.
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)
bisection method search
Matrix< Complex, Dynamic, 1 > ComplexVectorX
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
virtual unsigned int n_roots_found() const
finds the number of critical points already identified in the procedure.
libMesh::Real Real
libMesh::Complex Complex
std::ofstream * _output
file to which the result will be written
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 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.
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...
virtual void print_crossover_points()
Prints the crossover points output.
Matrix< Real, Dynamic, Dynamic > RealMatrixX
Matrix< Complex, Dynamic, Dynamic > ComplexMatrixX
bool _include_highest_kr_unstable
flag allows check to see if the root started out as critical at the highest k value.
const MAST::FlutterRootBase & get_root(const unsigned int i) const
virtual void calculate_sensitivity(MAST::FlutterRootBase &root, const MAST::FunctionBase &f, libMesh::NumericVector< Real > *dXdp=nullptr, libMesh::NumericVector< Real > *dXdkr=nullptr)
Calculate the sensitivity of the flutter root with respect to the f parameter.
std::pair< Real, Real > _kr_range
range of reference values within which to find flutter roots
void _initialize_matrix_sensitivity_for_param(const MAST::FunctionBase &f, const libMesh::NumericVector< Real > &dXdp, Real kr, ComplexMatrixX &A, ComplexMatrixX &B)
Assembles the reduced order system structural and aerodynmaic matrices for specified flight velocity ...
void _initialize_matrices(Real kr, ComplexMatrixX &A, ComplexMatrixX &B)
Assembles the reduced order system structural and aerodynmaic matrices for specified reduced freq kr...
void init(const MAST::UGFlutterSolver &solver, const Real v_ref, const Real b_ref, const MAST::LAPACK_ZGGEV_Base &eig_sol)
initializes the root
virtual void scan_for_roots()
Scans for flutter roots in the analyzed points, and identified the divergence (if k_red = 0...
std::multimap< Real, MAST::FlutterRootCrossoverBase * > _flutter_crossovers
the map of flutter crossover points versus average kr of the two bounding roots
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
void _initialize_matrix_sensitivity_for_kr(Real kr, ComplexMatrixX &A, ComplexMatrixX &B)
Assembles the sensitivity of matrices wrt kr.
MAST::Parameter * _bref_param
reference chord
MAST::Parameter * _kr_param
Parameter that define the reduced frequency.
Real _rho
flight density
std::pair< MAST::FlutterSolutionBase *, MAST::FlutterSolutionBase * > crossover_solutions
virtual void _identify_crossover_points()
identifies all cross-over and divergence points from analyzed roots
virtual void assemble_reduced_order_quantity_sensitivity(const MAST::FunctionBase &f, std::vector< libMesh::NumericVector< Real > *> &basis, std::map< MAST::StructuralQuantityType, RealMatrixX *> &mat_qty_map)
calculates the sensitivity of reduced order matrix given the basis provided in basis.
virtual void sort(const MAST::FlutterSolutionBase &sol)
sort this root with respect to the given solution from a previous eigen solution. ...
std::map< Real, MAST::FlutterSolutionBase * > _flutter_solutions
map of kr sorted flutter solutions
virtual std::unique_ptr< MAST::FlutterSolutionBase > _analyze(const Real kr_ref, const MAST::FlutterSolutionBase *prev_sol=nullptr)
performs an eigensolution at the specified reference value, and sort the roots based on the provided ...
void initialize(MAST::Parameter &kr_param, MAST::Parameter &bref_param, Real rho, 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.
MAST::StructuralFluidInteractionAssembly * _assembly
structural assembly that provides the assembly of the system matrices.
RealVectorX modal_participation
ComplexVectorX eig_vec_right
right and left eigenvevtors
UGFlutterSolver()
defalut constructor
void clear()
clears the solution and other data from this solver
virtual void clear()
clears the solution and other data from this solver
virtual void clear_solutions()
clears the solutions stored from a previous analysis.
void initialize(std::vector< libMesh::NumericVector< Real > *> &basis)
initializes the data structres for a flutter solution.
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...