MAST
time_domain_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 // MAST includes
30 #include "base/parameter.h"
31 #include "base/nonlinear_system.h"
32 
33 
36 _velocity_param(nullptr),
37 _V_range(),
38 _n_V_divs(0.) {
39 
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  _V_range = std::pair<Real, Real>(0.,0.);
58  _n_V_divs = 0;
59 
61 }
62 
63 
64 
65 
66 void
68 initialize(MAST::Parameter& velocity_param,
69  Real V_lower,
70  Real V_upper,
71  unsigned int n_V_divs,
72  std::vector<libMesh::NumericVector<Real> *>& basis) {
73 
74  _velocity_param = &velocity_param;
75  _V_range.first = V_lower;
76  _V_range.second = V_upper;
77  _n_V_divs = n_V_divs;
78 
80 }
81 
82 
83 
84 
85 void
87 
88  std::map<Real, MAST::FlutterSolutionBase*>::iterator it =
89  _flutter_solutions.begin();
90 
91  for ( ; it != _flutter_solutions.end(); it++)
92  delete it->second;
93 
94  std::multimap<Real, MAST::FlutterRootCrossoverBase*>::iterator cross_it =
95  _flutter_crossovers.begin();
96 
97  for ( ; cross_it != _flutter_crossovers.end(); cross_it++)
98  delete cross_it->second;
99 
100  _flutter_solutions.clear();
101  _flutter_crossovers.clear();
102 }
103 
104 
105 
106 
107 unsigned int
109 
110  std::multimap<Real, MAST::FlutterRootCrossoverBase*>::const_iterator
111  it = _flutter_crossovers.begin(),
112  end = _flutter_crossovers.end();
113 
114  unsigned int n = 0;
115  for ( ; it!=end; it++)
116  if (it->second->root) // a valid root pointer has been assigned
117  n++;
118 
119  return n;
120 }
121 
122 
123 
124 
125 
127 MAST::TimeDomainFlutterSolver::get_root(const unsigned int n) const {
128 
129  libmesh_assert(n < n_roots_found());
130 
131  std::multimap<Real, MAST::FlutterRootCrossoverBase*>::const_iterator
132  it = _flutter_crossovers.begin(),
133  end = _flutter_crossovers.end();
134 
135  unsigned int root_num = 0;
136  for ( ; it!=end; it++) {
137  if (it->second->root) { // a valid root pointer has been assigned
138  if (root_num == n) // root num matches the one being requested
139  return *(it->second->root);
140  else
141  root_num++;
142  }
143  }
144 
145  libmesh_assert(false); // should not get here
146 }
147 
148 
149 
150 
151 
152 std::pair<bool, MAST::FlutterRootBase*>
154  const unsigned int n_bisection_iters)
155 {
156  // iterate over the cross-over points and calculate the next that has
157  // not been evaluated
158  std::multimap<Real, MAST::FlutterRootCrossoverBase*>::iterator
159  it = _flutter_crossovers.begin(),
160  end = _flutter_crossovers.end();
161  while ( it != end)
162  {
163  MAST::FlutterRootCrossoverBase* cross = it->second;
164 
165  if (!cross->root) {
166  const unsigned int root_num = cross->root_num;
167  std::pair<bool, MAST::FlutterSolutionBase*> sol;
168  // first try the Newton search. If that fails, then
169  // try the bisection search
170  /*sol = newton_search(*cross->crossover_solutions.second,
171  root_num,
172  g_tol,
173  n_bisection_iters);
174  if (!sol.first)*/
176  root_num, g_tol, n_bisection_iters);
177 
178  cross->root = &(sol.second->get_root(root_num));
179 
180  // now, remove this entry from the _flutter_crossover points and
181  // reinsert it with the actual critical velocity
182  _flutter_crossovers.erase(it);
183  std::pair<Real, MAST::FlutterRootCrossoverBase*>
184  val(cross->root->V, cross);
185  _flutter_crossovers.insert(val);
186  return std::pair<bool, MAST::FlutterRootBase*> (true, cross->root);
187  }
188 
189  it++;
190  }
191 
192  // if it gets here, no new root was found
193  return std::pair<bool, MAST::FlutterRootBase*> (false, nullptr);
194 }
195 
196 
197 
198 std::pair<bool, MAST::FlutterRootBase*>
200  const unsigned int n_bisection_iters)
201 {
202  // iterate over the cross-over points and calculate the next that has
203  // not been evaluated
204  std::multimap<Real, MAST::FlutterRootCrossoverBase*>::iterator
205  it = _flutter_crossovers.begin(),
206  end = _flutter_crossovers.end();
207 
208  if (it == end) // no potential cross-over points were identified
209  return std::pair<bool, MAST::FlutterRootBase*> (false, nullptr);
210 
211  // it is possible that once the root has been found, its velocity end up
212  // putting it at a higher velocity in the map, so we need to check if
213  // the critical root has changed
214  while (!it->second->root)
215  {
216  MAST::FlutterRootCrossoverBase* cross = it->second;
217 
218  if (!cross->root) {
219 
220  const unsigned int root_num = cross->root_num;
221  std::pair<bool, MAST::FlutterSolutionBase*> sol;
222  // first try the Newton search. If that fails, then
223  // try the bisection search
224  /*sol = newton_search(*cross->crossover_solutions.second,
225  root_num,
226  g_tol,
227  n_bisection_iters);
228  if (!sol.first)*/
230  root_num, g_tol, n_bisection_iters);
231 
232  cross->root = &(sol.second->get_root(root_num));
233 
234  // now, remove this entry from the _flutter_crossover points and
235  // reinsert it with the actual critical velocity
236  _flutter_crossovers.erase(it);
237  std::pair<Real, MAST::FlutterRootCrossoverBase*>
238  val(cross->root->V, cross);
239  _flutter_crossovers.insert(val);
240  }
241 
242  // update the iterator to make sure that this is updated
243  it = _flutter_crossovers.begin(); end = _flutter_crossovers.end();
244  }
245 
246  // if it gets here, then the root was successfully found
247  return std::pair<bool, MAST::FlutterRootBase*> (true, it->second->root);
248 }
249 
250 
251 
252 
253 
254 std::pair<bool, MAST::FlutterRootBase*>
257  const unsigned int n_bisection_iters) {
258 
259  // make sure that there are no solutions already stored
260  libmesh_assert(! _flutter_solutions.size());
261  libmesh_assert(!_flutter_crossovers.size());
262 
263 
264  //
265  // start with the previous velocity and increment till a single
266  // root cross-over is available. This is done without need for
267  // mode tracking. Presently, the algorithm expects that the number
268  // of unstable roots at the first velocity be zero.
269  //
270  Real
271  lower_V = _V_range.first,
272  upper_V = _V_range.second,
273  lower_g = 0.,
274  upper_g = 0.,
275  dV = (_V_range.second - _V_range.first)/_n_V_divs,
276  new_V = 0.;
277 
279  *lower_root = nullptr,
280  *upper_root = nullptr;
281 
282  std::pair<unsigned int, unsigned int>
283  bracket_n_unstable_roots(0, 0);
284 
285  // first analyze at both the ends of the bracket
287  *sol = _analyze(lower_V).release();
288  lower_root = sol->get_critical_root(g_tol);
289  bracket_n_unstable_roots.first = sol->n_unstable_roots_in_upper_complex_half(g_tol);
290  if (_output)
291  sol->print(*_output);
292 
293  // presently the algorithm requires that the first velocity has no unstable
294  // roots
295  if (lower_V > 0)
296  libmesh_assert(!bracket_n_unstable_roots.first);
297 
298  // add the solution to this solver
299  bool if_success =
300  _flutter_solutions.insert(std::pair<Real, MAST::FlutterSolutionBase*>
301  (lower_V, sol)).second;
302 
303  libmesh_assert(if_success);
304 
305 
306  // now increment velocity till atleast one root is found to be critical
307  bool
308  cont = true;
309 
310  while (cont) {
311 
312  // add a new analysis point
313  upper_V = lower_V + dV;
314 
315  sol = _analyze(upper_V, sol).release();
316  bracket_n_unstable_roots.second = sol->n_unstable_roots_in_upper_complex_half(g_tol);
317  upper_root = sol->get_critical_root(g_tol);
318  if (_output)
319  sol->print(*_output);
320 
321  // add the solution to this solver
322  if_success =
323  _flutter_solutions.insert(std::pair<Real, MAST::FlutterSolutionBase*>
324  (upper_V, sol)).second;
325 
326  libmesh_assert(if_success);
327 
328  // check if any new roots were found
329  if (bracket_n_unstable_roots.second ||
330  upper_V >= _V_range.second) {
331 
332  // we have found a pair of roots that surround a cross-over point
333  cont = false;
334  }
335  else {
336 
337  // increment the velocity
338  lower_V = upper_V;
339  upper_V += dV;
340  bracket_n_unstable_roots.first = bracket_n_unstable_roots.second;
341  bracket_n_unstable_roots.second = 0;
342  lower_root = upper_root;
343  }
344  }
345 
346  std::pair<bool, MAST::FlutterRootBase*> rval(false, nullptr);
347 
348  // if no critical roots were found, the return with false
349  if (!bracket_n_unstable_roots.second)
350  return rval;
351 
352  // next, refine the bracket till a root is found till the desired accuracy
353  unsigned int n_iters = 0;
354 
355  while (n_iters < n_bisection_iters) {
356 
357  // get the damping values from the two critical roots
358  lower_g = lower_root->root.real();
359  upper_g = upper_root->root.real();
360 
361  // if the lower velocity is 0, then we calculate V using upper V
362  // as the reference.
363  //if (lower_V > 0.)
364  // new_V =
365  // lower_V + (upper_V-lower_V)/(upper_g-lower_g)*(0.-lower_g); // using lower V as reference
366  //else
367  // a dampig factor of 0.1 is added to slow down the reduction. This is
368  // done because the g value tends to increase very quickly after the
369  // collision of roots.
370  new_V = upper_V +
371  0.5*(upper_V-lower_V)/(upper_g-lower_g)*(1.e-4-upper_g); // using upper V as reference
372 
373  sol = _analyze(new_V, sol).release();
374  if (_output)
375  sol->print(*_output);
376 
377  // get the critical root from here
378  const MAST::FlutterRootBase* root = sol->get_critical_root(g_tol);
379 
380  // add the solution to this solver
381  if_success =
382  _flutter_solutions.insert(std::pair<Real, MAST::FlutterSolutionBase*>
383  (new_V, sol)).second;
384 
385  libmesh_assert(if_success);
386 
387 
388  // check the new damping value
389  if ((root->root.real() > 0.) && // only positively unstable roots will be used.
390  (root->root.real() <= g_tol)) {
391 
392  rval.first = true;
393  rval.second = sol->get_critical_root(g_tol);
394  return rval;
395  }
396 
397  // update the V value
398  if (root->root.real() < 0.) {
399 
400  lower_V = new_V;
401  lower_g = root->root.real();
402  lower_root = sol->get_critical_root(g_tol);
403  }
404  else {
405 
406  upper_V = new_V;
407  upper_g = root->root.real();
408  upper_root = sol->get_critical_root(g_tol);
409  }
410 
411  n_iters++;
412  }
413 
414  // return false, along with the latest sol
415  rval.first = false;
416  rval.second = sol->get_critical_root(g_tol);
417 
418  return rval;
419 }
420 
421 
422 
423 
424 void
426 
427  // if the initial scanning has not been done, then do it now
428  if (!_flutter_solutions.size()) {
429  // march from the upper limit to the lower to find the roots
430  Real current_V = _V_range.first,
431  delta_V = (_V_range.second - _V_range.first)/_n_V_divs;
432 
433  std::vector<Real> V_vals(_n_V_divs+1);
434  for (unsigned int i=0; i<_n_V_divs+1; i++) {
435  V_vals[i] = current_V;
436  current_V += delta_V;
437  }
438  V_vals[_n_V_divs] = _V_range.second; // to get around finite-precision arithmetic
439 
440  MAST::FlutterSolutionBase* prev_sol = nullptr;
441  for (unsigned int i=0; i<_n_V_divs+1; i++) {
442  current_V = V_vals[i];
443  std::unique_ptr<MAST::TimeDomainFlutterSolution>
444  sol = _analyze(current_V, prev_sol);
445 
446  prev_sol = sol.get();
447 
448  if (_output)
449  sol->print(*_output);
450 
451  // add the solution to this solver
452  bool if_success =
453  _flutter_solutions.insert(std::pair<Real, MAST::FlutterSolutionBase*>
454  (current_V, sol.release())).second;
455 
456  libmesh_assert(if_success);
457  }
458 
460  }
461 }
462 
463 
464 
465 
466 
467 void
469 
470  // write only if the output is set
471  if (!_output)
472  return;
473 
474  std::map<Real, MAST::FlutterSolutionBase*>::const_iterator
475  sol_it = _flutter_solutions.begin(),
476  sol_end = _flutter_solutions.end();
477  libmesh_assert(sol_it != sol_end); // solutions should have been evaluated
478 
479  unsigned int nvals = sol_it->second->n_roots();
480  libmesh_assert(nvals); // should not be zero
481 
482  // each root is written separately one after another
483  for (unsigned int i=0; i<nvals; i++)
484  {
485  // print the headers
486  *_output
487  << "** Root # "
488  << std::setw(5) << i << " **" << std::endl
489  << std::setw(15) << "V_ref"
490  << std::setw(15) << "Re"
491  << std::setw(15) << "Im" << std::endl;
492 
493  // update the iterator for this analysis
494  sol_it = _flutter_solutions.begin();
495 
496  // write the data from all solutions
497  for ( ; sol_it != sol_end; sol_it++)
498  {
499  const MAST::FlutterRootBase& root =
500  sol_it->second->get_root(i);
501 
502  *_output
503  << std::setw(15) << root.V
504  << std::setw(15) << std::real(root.root)
505  << std::setw(15) << std::imag(root.root) << std::endl;
506  }
507  *_output << std::endl << std::endl;
508  }
509 
510 
511  // write the roots identified using iterative search technique
512  std::streamsize prec = _output->precision();
513 
514  unsigned int nroots = this->n_roots_found();
515  *_output << std::endl
516  << "n critical roots identified: " << nroots << std::endl;
517  for (unsigned int i=0; i<nroots; i++)
518  {
519  const MAST::FlutterRootBase& root = this->get_root(i);
520  *_output
521  << "** Root : " << std::setw(5) << i << " **" << std::endl
522  << "V = " << std::setw(15) << std::setprecision(15) << root.V << std::endl
523  << "g = " << std::setw(15) << std::real(root.root) << std::endl
524  << "omega = " << std::setw(15) << std::imag(root.root) << std::endl
525  << std::setprecision((int) prec) // set the precision to the default value
526  << "Modal Participation : " << std::endl ;
527  for (unsigned int j=0; j<nvals; j++)
528  *_output
529  << "(" << std::setw(5) << j << "): "
530  << std::setw(10) << root.modal_participation(j)
531  << std::setw(3) << " ";
532  *_output << std::endl << std::endl;
533  }
534 
535 
536 }
537 
538 
539 void
541 
542  // print only if the output was specified
543  if (!_output)
544  return;
545 
546  *_output << "n crossover points found: "
547  << std::setw(5) << _flutter_crossovers.size() << std::endl;
548 
549  std::multimap<Real, MAST::FlutterRootCrossoverBase*>::const_iterator
550  it = _flutter_crossovers.begin(), end = _flutter_crossovers.end();
551 
552  unsigned int i=0;
553 
554  for ( ; it != end; it++) {
555  *_output << "** Point : " << std::setw(5) << i << " **" << std::endl;
556  it->second->print(*_output);
557  *_output << std::endl;
558  i++;
559  }
560 }
561 
562 
563 
564 
565 
566 std::pair<bool, MAST::FlutterSolutionBase*>
569  MAST::FlutterSolutionBase*>& ref_sol_range,
570  const unsigned int root_num,
571  const Real g_tol,
572  const unsigned int max_iters) {
573 
574  // assumes that the upper k_val has +ve g val and lower k_val has -ve
575  // k_val
576  Real
577  lower_V = ref_sol_range.first->ref_val(),
578  lower_g = ref_sol_range.first->get_root(root_num).root.real(),
579  upper_V = ref_sol_range.second->ref_val(),
580  upper_g = ref_sol_range.second->get_root(root_num).root.real(),
581  new_V = 0.;
582  unsigned int n_iters = 0;
583 
584  MAST::FlutterSolutionBase* new_sol = nullptr;
585  std::pair<bool, MAST::FlutterSolutionBase*> rval(false, nullptr);
586 
587  while (n_iters < max_iters) {
588 
589  new_V = lower_V +
590  (upper_V-lower_V)/(upper_g-lower_g)*(0.-lower_g); // linear interpolation
591 
592  new_sol = _analyze(new_V, ref_sol_range.first).release();
593 
594  if (_output)
595  new_sol->print(*_output);
596 
597  // add the solution to this solver
598  bool if_success =
599  _flutter_solutions.insert(std::pair<Real, MAST::FlutterSolutionBase*>
600  (new_V, new_sol)).second;
601 
602  libmesh_assert(if_success);
603 
604  const MAST::FlutterRootBase& root = new_sol->get_root(root_num);
605 
606  // check if the new damping value
607  if (fabs(root.root.real()) <= g_tol) {
608 
609  rval.first = true;
610  rval.second = new_sol;
611  return rval;
612  }
613 
614  // update the V value
615  if (root.root.real() < 0.) {
616 
617  lower_V = new_V;
618  lower_g = root.root.real();
619  }
620  else {
621 
622  upper_V = new_V;
623  upper_g = root.root.real();
624  }
625 
626  n_iters++;
627  }
628 
629  // return false, along with the latest sol
630  rval.first = false;
631  rval.second = new_sol;
632 
633  return rval;
634 }
635 
636 
637 
638 
639 std::unique_ptr<MAST::TimeDomainFlutterSolution>
641  const MAST::FlutterSolutionBase* prev_sol) {
642 
643  libMesh::out
644  << " ====================================================" << std::endl
645  << "Eigensolution" << std::endl
646  << " V_ref = " << std::setw(10) << v_ref << std::endl;
647 
649  A,
650  B;
651 
652  // initialize the matrices for the structure.
653  _initialize_matrices(v_ref, A, B);
654 
655  MAST::LAPACK_DGGEV ges;
656  ges.compute(A, B);
658 
660  root->init(*this, v_ref, ges);
661  if (prev_sol)
662  root->sort(*prev_sol);
663 
664  libMesh::out
665  << "Finished Eigensolution" << std::endl
666  << " ====================================================" << std::endl;
667 
668 
669  return std::unique_ptr<MAST::TimeDomainFlutterSolution> (root);
670 }
671 
672 
673 
674 
675 void
677  RealMatrixX &A,
678  RealMatrixX &B) {
679 
680  // now create the matrices for first-order model
681  // original equations are
682  // M x_ddot + C x_dot + K x = q_dyn (A0 x + A1 x_dot)
683  // defining x1 = x_dot, the first order governing equations become
684  // [ I 0 ] x_dot = [ 0 I ] x
685  // [ 0 M ] x1_dot = [ K1 C1] x1
686  // with K1 = -K + q_dyn A0, and
687  // C1 = -C + q_dyn A1.
688  //
689  // Then, the eigenproblem is formulated as
690  // A y = p B y, where
691  // y = {x^T x1^T}^T;
692  // A = [ 0 I ]
693  // [-K1 -C1 ], and
694  // B = [ I 0 ]
695  // [ 0 M ].
696  //
697 
698 
699  // set the velocity value in the parameter that was provided
700  (*_velocity_param) = U_inf;
701 
702 
703  // if the steady solver object is provided, then solve for the
704  // steady state using this velocity
705  if (_steady_solver) {
706  libMesh::out
707  << "*** Performing Steady State Solve ***" << std::endl;
708 
710  }
711 
712 
713  const unsigned int n = (unsigned int)_basis_vectors->size();
714 
716  m = RealMatrixX::Zero(n, n),
717  c = RealMatrixX::Zero(n, n),
718  k = RealMatrixX::Zero(n, n);
719 
720 
721  // now prepare a map of the quantities and ask the assembly object to
722  // calculate the quantities of interest.
723  std::map<MAST::StructuralQuantityType, RealMatrixX*> qty_map;
724  qty_map[MAST::MASS] = &m;
725  qty_map[MAST::DAMPING] = &c;
726  qty_map[MAST::STIFFNESS] = &k;
727 
728 
730  qty_map);
731 
732 
733  // put the matrices back in the system matrices
734  A.setZero(2*n, 2*n);
735  B.setZero(2*n, 2*n);
736 
737 
738  B.topLeftCorner(n, n) = RealMatrixX::Identity(n,n );
739  B.bottomRightCorner(n, n) = m;
740 
741 
742  A.topRightCorner(n, n) = RealMatrixX::Identity(n, n);
743  A.bottomLeftCorner(n, n) = -k;
744  A.bottomRightCorner(n, n) = -c;
745 }
746 
747 
748 
749 
750 
751 
752 void
755  const libMesh::NumericVector<Real>& dXdp,
756  Real U_inf,
757  RealMatrixX& A,
758  RealMatrixX& B) {
759 
760  // now create the matrices for first-order model
761  // original equations are
762  // M x_ddot + C x_dot + K x = q_dyn (A0 x + A1 x_dot)
763  // defining x1 = x_dot, the first order governing equations become
764  // [ I 0 ] x_dot = [ 0 I ] x
765  // [ 0 M ] x1_dot = [ K1 C1] x1
766  // with K1 = -K + q_dyn A0, and
767  // C1 = -C + q_dyn A1.
768  //
769  // Then, the eigenproblem is formulated as
770  // A y = p B y, where
771  // y = {x^T x1^T}^T;
772  // A = [ 0 I ]
773  // [-K1 -C1 ], and
774  // B = [ I 0 ]
775  // [ 0 M ].
776  //
777 
778  const unsigned int n = (unsigned int)_basis_vectors->size();
779 
781  m = RealMatrixX::Zero(n, n),
782  c = RealMatrixX::Zero(n, n),
783  k = RealMatrixX::Zero(n, n);
784 
785 
786  // now prepare a map of the quantities and ask the assembly object to
787  // calculate the quantities of interest.
788  std::map<MAST::StructuralQuantityType, RealMatrixX*> qty_map;
789  qty_map[MAST::MASS] = &m;
790  qty_map[MAST::DAMPING] = &c;
791  qty_map[MAST::STIFFNESS] = &k;
792 
793 
794  // set the velocity value in the parameter that was provided
795  (*_velocity_param) = U_inf;
796 
797  _assembly->set_base_solution(dXdp, true);
799  (f, *_basis_vectors, qty_map);
801 
802 
803  // put the matrices back in the system matrices
804  A.setZero(2*n, 2*n);
805  B.setZero(2*n, 2*n);
806 
807 
808  B.topLeftCorner(n, n) = RealMatrixX::Identity(n,n );
809  B.bottomRightCorner(n, n) = m;
810 
811 
812  A.topRightCorner(n, n) = RealMatrixX::Identity(n, n);
813  A.bottomLeftCorner(n, n) = -k;
814  A.bottomRightCorner(n, n) = -c;
815 }
816 
817 
818 
819 
820 
821 void
823 
825  // only non-negative frequencies (>=0) are used, since a time-domain
826  // solver provides complex-conjugate roots.
828 
829  // if the initial scanning has not been done, then do it now
830  const Real tol = 1.0e-5;
831 
832  const unsigned int nvals = _flutter_solutions.begin()->second->n_roots();
833  // make sure that the solution has been generated
834  libmesh_assert(nvals);
835 
836  //
837  // for some cases some roots trail along the g=0 axis
838  // and should not be considered as flutter. These are simply
839  // modes where aerodynamics do not provide any damping.
840  // These modes will not have a damping more than tolerance
841  //
842 
843  std::vector<bool> modes_to_neglect(nvals);
844  std::fill(modes_to_neglect.begin(),
845  modes_to_neglect.end(), false);
846 
847  // look for the max g val for a mode, which will indicate if the
848  // mode is undamped or not
849  for (unsigned int i=0; i<nvals; i++) {
850 
851  std::map<Real, MAST::FlutterSolutionBase*>::const_iterator
852  sol_it = _flutter_solutions.begin(),
853  sol_end = _flutter_solutions.end();
854 
855  Real
856  max_g_val = 0.,
857  max_w_val = -1.e100,
858  g_val = 0.,
859  w_val = 0.;
860 
861 
862  for ( ; sol_it!=sol_end; sol_it++) {
863 
864  g_val = fabs(sol_it->second->get_root(i).root.real());
865  w_val = sol_it->second->get_root(i).root.imag();
866 
867  // maximum damping
868  if (g_val > max_g_val)
869  max_g_val = g_val;
870 
871  if (w_val > max_w_val)
872  max_w_val = w_val;
873  }
874 
875  // check the maximum damping seen for this mode
876  if (max_g_val < tol || max_w_val < 0.)
877  modes_to_neglect[i] = true;
878  }
879 
880  // now look for oscillatory roots crossover points in increasing
881  // order of V
882  for (unsigned int i=0; i<nvals; i++) {
883 
884  std::map<Real, MAST::FlutterSolutionBase*>::const_iterator
885  sol_it = _flutter_solutions.begin(), // first of the pair
886  sol_itp1 = _flutter_solutions.begin(), // first of the pair
887  sol_end = _flutter_solutions.end();
888 
889  if (sol_it == sol_end)
890  return;
891 
892  sol_itp1++; // increment for the next pair of results
893  bool if_process = false;
894 
895  while (sol_itp1 != sol_end) {
896 
897  if_process =
898  (// atleast one |g| > tol
899  (fabs(sol_it->second->ref_val()) > tol ||
900  fabs(sol_itp1->second->ref_val()) > tol) &&
901  // both |g| < max_g
902  //(fabs(sol_it->second->get_root(i).root.real()) < max_allowable_g &&
903  // fabs(sol_itp1->second->get_root(i).root.real()) < max_allowable_g) &&
904  // if the mode has been identified to be trailing along g =0,
905  // neglect it
906  !modes_to_neglect[i]);
907 
908 
909  if (if_process) {
910 
911  // look for the flutter roots
913  *lower = sol_it->second,
914  *upper = sol_itp1->second;
915 
916  if ((lower->get_root(i).root.real() <= 0.) &&
917  (upper->get_root(i).root.real() > 0.)) {
918 
921  cross->crossover_solutions.first = lower; // -ve g
922  cross->crossover_solutions.second = upper; // +ve g
923  cross->root_num = i;
924  std::pair<Real, MAST::FlutterRootCrossoverBase*>
925  val( lower->get_root(i).V, cross);
926  _flutter_crossovers.insert(val);
927  }
928  else if ((lower->get_root(i).root.real() > 0.) &&
929  (upper->get_root(i).root.real() <= 0.)) {
930 
933  cross->crossover_solutions.first = upper; // -ve g
934  cross->crossover_solutions.second = lower; // +ve g
935  cross->root_num = i;
936  std::pair<Real, MAST::FlutterRootCrossoverBase*>
937  val( upper->get_root(i).V, cross);
938  _flutter_crossovers.insert(val);
939  }
940  }
941 
942  // increment the pointers for next pair of roots
943  sol_it++;
944  sol_itp1++;
945  }
946 
947  // now check to see if the root started out as critical at
948  // the lowest V value.
949  sol_it = _flutter_solutions.begin();
950  sol_itp1 = _flutter_solutions.begin();
951  sol_itp1++;
952 
953  if_process =
954  (// atleast one |g| > tol
955  (fabs(sol_it->second->ref_val()) > tol ||
956  fabs(sol_itp1->second->ref_val()) > tol) &&
957  // both |g| < max_g
958  //(fabs(sol_it->second->get_root(i).root.real()) < max_allowable_g &&
959  // fabs(sol_itp1->second->get_root(i).root.real()) < max_allowable_g) &&
960  // if the mode has been identified to be trailing along g =0,
961  // neglect it
962  !modes_to_neglect[i]);
963 
964  Real g_val = sol_it->second->get_root(i).root.real();
965 
966  if (if_process &&
967  g_val > 0 /*&& g_val < max_allowable_g*/) {
968 
971  // here, both roots for crossover are set to be the same
972  // note that this will not work with bisection search, since
973  // it needs a bracket to begin with.
974  // However, the Newton search should be able to find the
975  // critical root location.
976  cross->crossover_solutions.first = sol_it->second;
977  cross->crossover_solutions.second = sol_it->second;
978  cross->root_num = i;
979  std::pair<Real, MAST::FlutterRootCrossoverBase*>
980  val( sol_it->second->get_root(i).V, cross);
981  _flutter_crossovers.insert(val);
982  }
983 
984  }
985 }
986 
987 
988 
989 
990 void
993  const MAST::FunctionBase& f,
994  libMesh::NumericVector<Real>* dXdp,
995  libMesh::NumericVector<Real>* dXdV) {
996 
997 
998 
999  libMesh::out
1000  << " ====================================================" << std::endl
1001  << "Flutter Sensitivity Solution" << std::endl
1002  << " V_ref = " << std::setw(10) << root.V << std::endl;
1003 
1004  Complex
1005  eig = root.root,
1006  deig_dp = 0.,
1007  deig_dV = 0.,
1008  den = 0.;
1009 
1010  // get the sensitivity of the matrices
1011  RealMatrixX
1012  mat_A,
1013  mat_B,
1014  mat_A_sens,
1015  mat_B_sens;
1016 
1017  ComplexVectorX v;
1018 
1019  // initialize the baseline matrices
1020  _initialize_matrices(root.V, mat_A, mat_B);
1021 
1022  // if the sensitivity of the solution was provided, then use that.
1023  // otherwise pass a zero vector
1024  libMesh::NumericVector<Real>* sol_sens = dXdp;
1025  std::unique_ptr<libMesh::NumericVector<Real> > zero_sol_sens;
1026  if (!dXdp) {
1027  zero_sol_sens.reset(_assembly->system().solution->zero_clone().release());
1028  sol_sens = zero_sol_sens.get();
1029  }
1030  else
1031  sol_sens = dXdp;
1032 
1033  // calculate the eigenproblem sensitivity
1035  *sol_sens,
1036  root.V,
1037  mat_A_sens,
1038  mat_B_sens);
1039 
1040 
1041  // the eigenproblem is y^T A x - lambda y^T B x = 0
1042  // therefore, the denominator is obtained from the inner product of
1043  // y^T B x
1044  // sensitivity is
1045  // -dlambda/dp y^T B x = - y^T (dA/dp - lambda dB/dp)
1046  // or
1047  // dlambda/dp = [y^T (dA/dp - lambda dB/dp)]/(y^T B x)
1048 
1049  // now calculate the numerator for sensitivity
1050  // numerator = ( dA/dp - lambda dB/dp)
1051  den = root.eig_vec_left.dot(mat_B*root.eig_vec_right);
1052  deig_dp = root.eig_vec_left.dot((mat_A_sens.cast<Complex>() -
1053  eig*mat_B_sens.cast<Complex>())*root.eig_vec_right)/den;
1054 
1055  // next we need the sensitivity of eigenvalue wrt V
1056  // identify the sensitivity of solution to be used based on the
1057  // function arguments
1058  if (!dXdV) {
1059  sol_sens = zero_sol_sens.get();
1060  }
1061  else
1062  sol_sens = dXdV;
1063 
1065  *sol_sens,
1066  root.V,
1067  mat_A_sens,
1068  mat_B_sens);
1069 
1070 
1071  // now calculate the quotient for sensitivity wrt V
1072  // calculate numerator
1073  deig_dV = root.eig_vec_left.dot((mat_A_sens.cast<Complex>() -
1074  eig*mat_B_sens.cast<Complex>())*root.eig_vec_right)/den;
1075 
1076  // since the constraint that defines flutter speed is that damping = 0,
1077  // Re(lambda) = 0, then the sensitivity of flutter speed is obtained
1078  // from the total derivative of this constraint
1079  // d Re(lambda)/dp + d Re(lambda)/dV dV/dp = 0
1080  // or, dV/dp = -[d Re(lambda)/dp] / [d Re(lambda)/dV]
1081  // finally, the flutter speed sensitivity
1082  root.V_sens = - deig_dp.real() / deig_dV.real();
1083 
1084  // total sensitivity of the eigenvlaue
1085  root.root_sens = deig_dp + deig_dV * root.V_sens;
1086 
1087  root.has_sensitivity_data = true;
1088 
1089  libMesh::out
1090  << "Finished Flutter Sensitivity Solution" << std::endl
1091  << " ====================================================" << std::endl;
1092 
1093 }
virtual std::pair< bool, MAST::FlutterRootBase * > analyze_and_find_critical_root_without_tracking(const Real g_tol, const unsigned int n_iters)
This root starts with the lower velocity and increments the speed till a single unstable root is iden...
const MAST::FlutterRootBase & get_root(const unsigned int i) const
virtual void _identify_crossover_points()
identifies all cross-over and divergence points from analyzed roots
ComplexVectorX eig_vec_left
std::vector< libMesh::NumericVector< Real > * > * _basis_vectors
basis vector used to define the reduced order model
MAST::Parameter * _velocity_param
Parameter that define the velocity.
virtual void print(std::ostream &output)
prints the data and modes from this solution
void clear_base_solution(bool if_sens=false)
Clears the pointer to base solution.
virtual void print(std::ostream &output)=0
prints the data and modes from this solution
unsigned int n_unstable_roots_in_upper_complex_half(Real tol) const
number of unstable roots in this solution.
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 clear()
clears the solution and other data from this solver
virtual void calculate_sensitivity(MAST::FlutterRootBase &root, const MAST::FunctionBase &f, libMesh::NumericVector< Real > *dXdp=nullptr, libMesh::NumericVector< Real > *dXdV=nullptr)
Calculate the sensitivity of the flutter root with respect to the f parameter.
virtual void print_crossover_points()
Prints the crossover points 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
void initialize(MAST::Parameter &velocity_param, Real V_lower, Real V_upper, unsigned int n_V_divs, std::vector< libMesh::NumericVector< Real > * > &basis)
initializes the data structres for a flutter solution.
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 std::unique_ptr< MAST::TimeDomainFlutterSolution > _analyze(const Real v_ref, const MAST::FlutterSolutionBase *prev_sol=nullptr)
performs an eigensolution at the specified reference value, and sort the roots based on the provided ...
libMesh::Real Real
MAST::FlutterSolverBase::SteadySolver * _steady_solver
object provides the steady state solution.
libMesh::Complex Complex
void init(const MAST::TimeDomainFlutterSolver &solver, const Real v_ref, const MAST::LAPACK_DGGEV &eig_sol)
initializes the root
std::multimap< Real, MAST::FlutterRootCrossoverBase * > _flutter_crossovers
the map of flutter crossover points versus average velocity of the two bounding roots ...
void _initialize_matrices(Real U_inf, RealMatrixX &A, RealMatrixX &B)
Assembles the reduced order system structural and aerodynmaic matrices for specified flight velocity ...
std::ofstream * _output
file to which the result will be written
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.
Matrix< Real, Dynamic, Dynamic > RealMatrixX
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.
MAST::FlutterRootBase * get_critical_root(Real tol)
void set_base_solution(const libMesh::NumericVector< Real > &sol, bool if_sens=false)
if the eigenproblem is defined about a non-zero base solution, then this method provides the object w...
virtual unsigned int n_roots_found() const
finds the number of critical points already identified in the procedure.
void initialize(std::vector< libMesh::NumericVector< Real > * > &basis)
initializes the data structres for a flutter solution.
virtual void sort(const MAST::FlutterSolutionBase &sol)
sort this root with respect to the given solution from a previous eigen solution. ...
std::pair< Real, Real > _V_range
range of reference values within which to find flutter roots
std::pair< MAST::FlutterSolutionBase *, MAST::FlutterSolutionBase * > crossover_solutions
virtual const libMesh::NumericVector< Real > & solve()=0
solves for the steady state solution, and
virtual void scan_for_roots()
Scans for flutter roots in the analyzed points, and identified the divergence (if k_red = 0...
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...
MAST::StructuralFluidInteractionAssembly * _assembly
structural assembly that provides the assembly of the system matrices.
unsigned int _n_V_divs
number of division in the reference value range for initial scanning
RealVectorX modal_participation
virtual void clear_solutions()
clears the solutions stored from a previous analysis.
void compute(const RealMatrixX &A, const RealMatrixX &B, bool computeEigenvectors=true)
computes the eigensolution for .
std::map< Real, MAST::FlutterSolutionBase * > _flutter_solutions
map of velocity sorted flutter solutions
ComplexVectorX eig_vec_right
right and left eigenvevtors
const MAST::NonlinearSystem & system() const
virtual void clear()
clears the solution and other data from this solver
void _initialize_matrix_sensitivity_for_param(const MAST::FunctionBase &f, const libMesh::NumericVector< Real > &dXdp, Real U_inf, RealMatrixX &A, RealMatrixX &B)
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...
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