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-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
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::FlutterRootBase & get_root(const unsigned int i) 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 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
virtual void print_sorted_roots()
Prints the sorted roots to the output.
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.
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
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.
libMesh::Real Real
virtual unsigned int n_roots_found() const
finds the number of critical points already identified in the procedure.
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...
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
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.
bool _include_highest_kr_unstable
flag allows check to see if the root started out as critical at the highest k value.
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
void _initialize_matrix_sensitivity_for_kr(Real kr, ComplexMatrixX &A, ComplexMatrixX &B)
Assembles the sensitivity of matrices wrt kr.
void initialize(std::vector< libMesh::NumericVector< Real > * > &basis)
initializes the data structres for a flutter solution.
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
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 _identify_crossover_points()
identifies all cross-over and divergence points from analyzed roots
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 ...
MAST::StructuralFluidInteractionAssembly * _assembly
structural assembly that provides the assembly of the system matrices.
RealVectorX modal_participation
ComplexVectorX eig_vec_right
right and left eigenvevtors
const MAST::NonlinearSystem & system() const
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.
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...