Xyce  6.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
N_ANP_MOR.C
Go to the documentation of this file.
1 //-----------------------------------------------------------------------------
2 // Copyright Notice
3 //
4 // Copyright 2002 Sandia Corporation. Under the terms
5 // of Contract DE-AC04-94AL85000 with Sandia Corporation, the U.S.
6 // Government retains certain rights in this software.
7 //
8 // Xyce(TM) Parallel Electrical Simulator
9 // Copyright (C) 2002-2014 Sandia Corporation
10 //
11 // This program is free software: you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation, either version 3 of the License, or
14 // (at your option) any later version.
15 //
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with this program. If not, see <http://www.gnu.org/licenses/>.
23 //-------------------------------------------------------------------------
24 
25 //-----------------------------------------------------------------------------
26 // Filename : $RCSfile: N_ANP_MOR.C,v $
27 // Purpose : MOR analysis functions.
28 // Special Notes :
29 // Creator : Ting Mei
30 // Creation Date : 7/11
31 //
32 // Revision Information:
33 // ---------------------
34 // Revision Number: $Revision: 1.50.2.1 $
35 // Revision Date : $Date: 2014/08/26 22:31:06 $
36 // Current Owner : $Author: dgbaur $
37 //-----------------------------------------------------------------------------
38 
39 #include <Xyce_config.h>
40 
41 
42 // ---------- Standard Includes ----------
43 #include <iomanip>
44 
45 
46 // ---------- Xyce Includes ----------
47 
48 #include <N_ANP_AnalysisManager.h>
49 #include <N_ANP_MOR.h>
50 #include <N_ANP_Report.h>
51 #include <N_IO_CmdParse.h>
52 #include <N_LAS_BlockMatrix.h>
53 #include <N_LAS_BlockSystemHelpers.h>
54 #include <N_LAS_BlockVector.h>
55 #include <N_LAS_MOROperators.h>
56 #include <N_LAS_Matrix.h>
57 #include <N_LAS_MultiVector.h>
58 #include <N_LAS_System.h>
59 #include <N_LOA_Loader.h>
60 #include <N_NLS_ReturnCodes.h>
61 #include <N_PDS_Comm.h>
62 #include <N_PDS_ParMap.h>
63 #include <N_TIA_DataStore.h>
65 #include <N_TIA_StepErrorControl.h>
66 #include <N_TOP_Topology.h>
67 #include <N_UTL_Misc.h>
68 #include <N_UTL_Timer.h>
69 
70 #ifdef Xyce_PARALLEL_MPI
71 #include <N_PDS_ParComm.h>
72 #include <mpi.h>
73 #else
74 #include <N_PDS_SerialComm.h>
75 #endif
76 
77 // ---------- Other Includes ----------
78 
79 #include <Epetra_CrsMatrix.h>
80 #include <Epetra_Operator.h>
81 #include <Epetra_CrsGraph.h>
82 #include <Epetra_MultiVector.h>
83 #include <Epetra_LinearProblem.h>
84 #include <Amesos.h>
85 
86 #ifdef Xyce_BELOS
87 #include <BelosLinearProblem.hpp>
88 #include <BelosBlockGmresIter.hpp>
89 #include <BelosDGKSOrthoManager.hpp>
90 #include <BelosStatusTestMaxIters.hpp>
91 #include <BelosOutputManager.hpp>
92 #include <BelosEpetraAdapter.hpp>
93 #endif
94 
95 #include <Teuchos_SerialDenseMatrix.hpp>
96 #include <Teuchos_ScalarTraits.hpp>
97 #include <Teuchos_LAPACK.hpp>
98 #include <Teuchos_BLAS.hpp>
99 
100 namespace Xyce {
101 namespace Analysis {
102 
103 //-----------------------------------------------------------------------------
104 // Function : MOR::MOR( AnalysisManager * )
105 // Purpose : constructor
106 // Special Notes :
107 // Scope : public
108 // Creator : Ting Mei
109 // Creation Date : 5/11
110 //-----------------------------------------------------------------------------
111 MOR::MOR( AnalysisManager &analysis_manager ) :
112  AnalysisBase(analysis_manager),
113  dcopFlag_(true),
114  morEvalSize_(0),
115  numPorts_(0),
116  stepMult_(0.0),
117  fStep_(0.0),
118  currentFreq_(0.0),
119  s0_(0.0)
120 {}
121 
122 //-----------------------------------------------------------------------------
123 // Function : MOR::~MOR()
124 // Purpose : destructor
125 // Special Notes :
126 // Scope : public
127 // Creator : Ting Mei
128 // Creation Date : 5/11
129 //-----------------------------------------------------------------------------
131 {
132 }
133 
134 //-----------------------------------------------------------------------------
135 // Function : MOR::setAnalysisParams
136 // Purpose :
137 // Special Notes : These are from the .MOR statement.
138 // Scope : public
139 // Creator : Ting Mei, SNL
140 // Creation Date : 6/11
141 //-----------------------------------------------------------------------------
142 bool MOR::setAnalysisParams(const N_UTL_OptionBlock & paramsBlock)
143 {
144  Util::ParameterList::const_iterator it_tp;
145  Util::ParameterList::const_iterator first = paramsBlock.getParams().begin();
146  Util::ParameterList::const_iterator last = paramsBlock.getParams().end();
147  for (it_tp = first; it_tp != last; ++it_tp)
148  {
149  if (it_tp->uTag() == "SIZE")
150  {
151  tiaParams_.ROMsize = it_tp->getImmutableValue<int>();
152  }
153  else if (it_tp->uTag() == "PORTLIST")
154  {
155  portList_ = it_tp->getValue<std::vector<std::string> >();
156  }
157  }
158 
159 // getDebugLevel() = 1;
160 #ifdef Xyce_DEBUG_ANALYSIS
162  {
163  dout() << std::endl
164  << section_divider << std::endl
165  <<" MOR simulation parameters" << std::endl
166  << " size = " << tiaParams_.ROMsize << std::endl;
167  }
168 #endif
169 
170  return true;
171 }
172 
173 //-----------------------------------------------------------------------------
174 // Function : MOR::run()
175 // Purpose :
176 // Special Notes :
177 // Scope : public
178 // Creator : Ting Mei, SNL
179 // Creation Date : 5/11
180 //-----------------------------------------------------------------------------
181 bool MOR::run()
182 {
183  bool bsuccess = true;
184 
185  bsuccess = bsuccess & init();
186  bsuccess = bsuccess & reduceSystem();
187 
189  {
190  // Evaluate original system
191  bsuccess = bsuccess & evalOrigTransferFunction();
192  }
193 
194  // Reset the output adapter, just in case the reduced system needs to output
195  // its transfer functions.
197 
199  {
200  // Evaluate reduced system
201  bsuccess = bsuccess & evalRedTransferFunction();
202  }
203 
204  // if processing the loop failed,
205  // then skip finish step
206  if( bsuccess )
207  {
208  bsuccess = bsuccess & finish();
209  }
210 
211  return bsuccess;
212 }
213 
214 //-----------------------------------------------------------------------------
215 // Function : MOR::init()
216 // Purpose :
217 // Special Notes :
218 // Scope : public
219 // Creator : Ting Mei, SNL
220 // Creation Date : 5/11
221 //-----------------------------------------------------------------------------
222 bool MOR::init()
223 {
224  bool bsuccess = true;
225 
226  // Compute expansion point in transformed space.
227  s0_ = 2.0 * M_PI * tiaParams_.morExpPoint;
228 
230  {
232  }
233 
234  if(dcopFlag_)
235  {
237  }
238 
239  // Get set to do the operating point.
242 
243  stepNumber = 0;
244  doubleDCOPFlag_ = loader_.getDoubleDCOPFlag ();
246 
247  // set initial guess, if there is one to be set.
248  // this setInitialGuess call is to up an initial guess in the
249  // devices that have them (usually PDE devices). This is different than
250  // the "intializeProblem" call, which sets IC's. (initial conditions are
251  // different than initial guesses.
253 
254  // If available, set initial solution
255  inputOPFlag_ =
258 
259  // Set a constant history for operating point calculation
263 
264  // solving for DC op
265  handlePredictor();
266  loader_.updateSources();
271 
273  {
274  Report::UserError() << "Solving for DC operating point failed, cannot continue MOR analysis";
275  return false;
276  }
277 
278  // Create B matrix stamp
279  std::vector<int> tempVec;
280  loader_.getBMatrixEntriesforMOR(tempVec, bMatPosEntriesVec_);
281 
282  // Determine how many global ports their are performing a sumAll()
283  int hsize = tempVec.size();
284  N_PDS_Comm * pdsComm = (linearSystem_.getPDSManager())->getPDSComm();
285  pdsComm->sumAll( &hsize, &numPorts_, 1 );
286 
287  // Check that the listed ports on the .mor line are the same number as the B matrix entries.
288  if ( numPorts_ != (int)portList_.size() )
289  {
290  Report::UserError() << "Number of specified ports in .MOR line is inconsistent with number of voltage sources";
291  return false;
292  }
293 
294  // Getting the GIDs for the port list in the order specified in the .MOR line
295  // This is used to permute the bMatEntriesVec_ in the order specified by the user.
296  std::vector<int> gidPosEntries( hsize );
297  for (int i=0; i<hsize; ++i)
298  {
299  std::list<int> svGIDList1, dummyList;
300  char type1;
301  analysisManager_.getTopology().getNodeSVarGIDs(NodeID(portList_[i],_VNODE), svGIDList1, dummyList, type1);
302 
303  // Grab the GID for this port.
304  gidPosEntries[i] = svGIDList1.front();
305  }
306 
307  // Use the base map to get the global IDs
308  // Get the parallel maps for the original system
309  N_PDS_Manager &pdsMgrPtr_ = *linearSystem_.getPDSManager();
310  RCP<N_PDS_ParMap> BaseMap_ = rcp(pdsMgrPtr_.getParallelMap( "SOLUTION" ), false);
311 
312  // Find the voltage source corresponding to each port and place the LID in the bMatEntriesVec_.
313  bMatEntriesVec_.resize( hsize );
314  for (int i=0; i<hsize; ++i)
315  {
316  int gid = gidPosEntries[i];
317  bool found = false;
318  for (int j=0; j<hsize; ++j)
319  {
320  if (gid == BaseMap_->localToGlobalIndex(bMatPosEntriesVec_[j]))
321  {
322  bMatEntriesVec_[i] = tempVec[j];
323  found = true;
324  break;
325  }
326  }
327  if (!found)
328  {
329  Report::UserError() << "Did not find voltage source corresponding to port";
330  return false;
331  }
332  }
333 
335  {
336  // Resize transfer function matrices
337  origH_.shape(numPorts_, numPorts_);
338  }
340  {
341  // Resize transfer function matrices
342  redH_.shape(numPorts_, numPorts_);
343  }
344 
345  // Create C and G matrices from DCOP solution
349 
354 
355  loader_.updateState
365  );
366 
367  loader_.loadDAEVectors
384 
389 
392 
393 
394  /// Xyce::dout() << "Branch nodes: " << std::endl;
395  /// for (unsigned int i=0; i < bMatEntriesVec_.size(); ++i)
396  /// {
397  /// Xyce::dout() << "Node " << i << " : " << bMatEntriesVec_[i] << std::endl;
398  /// }
399 
400  /// Xyce::dout() << "Printing GPtr: " << std::endl;
401  /// GPtr_->printPetraObject();
402  /// Xyce::dout() << "Printing CPtr: " << std::endl;
403  /// CPtr_->printPetraObject();
404 
405 
406  // Storage for row extraction
407  int length=1, numEntries=0;
408  std::vector<int> colIndices(length);
409  std::vector<double> coeffs(length);
410 
411  for (unsigned int i=0; i < bMatEntriesVec_.size(); ++i)
412  {
413  // Get the number of non-zero entries in this row.
414  numEntries = GPtr_->getLocalRowLength( bMatEntriesVec_[i] );
415  if ( numEntries != 1 )
416  {
417  Report::UserError0() << "Supposed voltage source row has too many entries, cannot continue MOR analysis";
418  return false;
419  }
420 
421  // Extract out rows of G based on indices in bMatEntriesVec_.
422  GPtr_->getLocalRowCopy(bMatEntriesVec_[i], length, numEntries, &coeffs[0], &colIndices[0]);
423 
424  // If the coefficient for this voltage source is positive, make it negative.
425  if ( coeffs[0] > 0.0 )
426  {
427  coeffs[0] *= -1.0;
428  GPtr_->putLocalRow(bMatEntriesVec_[i], length, &coeffs[0], &colIndices[0]);
429  }
430  }
431 
432 
433  /// Xyce::dout() << "Printing GPtr (after scaling): " << std::endl;
434  /// GPtr_->printPetraObject();
435 
436  return bsuccess;
437 }
438 
439 //-----------------------------------------------------------------------------
440 // Function : MOR::reduceSystem()
441 // Purpose :
442 // Special Notes :
443 // Scope : public
444 // Creator : Heidi Thornquist, SNL
445 // Creation Date : 6/4/2012
446 //-----------------------------------------------------------------------------
448 {
449  // At this time, the reduceSystem() method will compute the reduced-order system using
450  // multi-port PRIMA. This method should allow the user multiple algorithms, defined by
451  // the tiaParams_.morMethod option, which would be best implemented using a factory design.
452 
453  bool bsuccess = true;
454 
455  // Get the parallel maps for the original system
456  N_PDS_Manager &pdsMgrPtr_ = *linearSystem_.getPDSManager();
457 
458  N_PDS_ParMap &BaseMap_ = *pdsMgrPtr_.getParallelMap( "SOLUTION" );
459 
460  // Create matrix for (G + s0 * C)
461  if (s0_ != 0.0)
462  {
463  sCpG_MatrixPtr_ = rcp( new N_LAS_Matrix( &CPtr_->epetraObj(), false ) );
464  sCpG_MatrixPtr_->scale( s0_ );
465  sCpG_MatrixPtr_->add( *GPtr_ );
466  }
467  else
468  {
469  sCpG_MatrixPtr_ = rcp( new N_LAS_Matrix( &GPtr_->epetraObj(), false ) );
470  }
471 
472  // Create multivector for B and R
473  RPtr_ = rcp( new N_LAS_MultiVector( BaseMap_, numPorts_ ) );
474  RPtr_->putScalar( 0.0 );
475  BPtr_ = rcp( new N_LAS_MultiVector( BaseMap_, numPorts_ ) );
476  for (unsigned int j=0; j < bMatEntriesVec_.size(); ++j)
477  {
478  BPtr_->setElementByGlobalIndex( BaseMap_.localToGlobalIndex(bMatEntriesVec_[j]), -1.0, j );
479  }
480 
481  /// Xyce::dout() << "Printing out BPtr" << std::endl;
482  /// BPtr_->epetraObj().Print(Xyce::dout());
483 
484  /// Xyce::dout() << "Printing out sCpG" << std::endl;
485  /// (sCpG_MatrixPtr_->epetraObj()).Print(Xyce::dout());
486 
487  // Create linear problem for (G + s0 * C)
488  origProblem_ = rcp(new Epetra_LinearProblem(&sCpG_MatrixPtr_->epetraObj(), &RPtr_->epetraObj(), &BPtr_->epetraObj() ) );
489 
490  // Create solver object for this linear problem, which will be used to generate the projection basis
491  Amesos amesosFactory;
492  origSolver_ = rcp( amesosFactory.Create( "Klu", *origProblem_ ) );
493 
494  // Solve for R = inv(G + s0*C)*B
495  // Perform symbolic factorization.
496  int linearStatus = origSolver_->SymbolicFactorization();
497  if (linearStatus != 0)
498  {
499  Xyce::dout() << "Amesos symbolic factorization exited with error: " << linearStatus << std::endl;
500  bsuccess = false;
501  }
502 
503  // Perform numeric factorization
504  linearStatus = origSolver_->NumericFactorization();
505  if (linearStatus != 0)
506  {
507  Xyce::dout() << "Amesos numeric factorization exited with error: " << linearStatus << std::endl;
508  bsuccess = false;
509  }
510 
511  // Perform solve for R = inv(G + s0*C)*B
512  linearStatus = origSolver_->Solve();
513  if (linearStatus != 0)
514  {
515  Xyce::dout() << "Amesos solve exited with error: " << linearStatus << std::endl;
516  bsuccess = false;
517  }
518 
519  /// Xyce::dout() << "Printing out R" << std::endl;
520  /// (RPtr_->epetraObj()).Print(Xyce::dout());
521 
522  // Create an Epetra_Operator object to apply the operator inv(G + s0*C)*C
523  RCP<Epetra_Operator> COpPtr_ = rcp( &CPtr_->epetraObj(), false );
524  RCP<N_LAS_AmesosGenOp> AOp = rcp( new N_LAS_AmesosGenOp( origSolver_, COpPtr_ ) );
525 
526  // Check to see if the requested size of the ROM is valid.
527  // An orthogonal basis cannot be generated that is larger than the dimension of the original system
528  int kblock = 0;
529  if (tiaParams_.ROMsize > RPtr_->globalLength())
530  {
531  kblock = (int)(RPtr_->globalLength() / numPorts_);
532  UserWarning(*this) << "Requested reduced-order model dimension is larger than original system dimension, resizing to original system dimension";
533  }
534  else
535  {
536  kblock = (int)(tiaParams_.ROMsize / numPorts_);
537  }
538  int k = kblock * numPorts_;
539 
540  // Resize the projection matrices
541  redG_.shape(k, k);
542  redC_.shape(k, k);
543  redB_.shape(k, numPorts_);
544  redL_.shape(k, numPorts_);
545 
546 #ifdef Xyce_BELOS
547  // ---------------------------------------------------------------------
548  // Now use Belos to compute the basis vectors for K_k(inv(G + s0*C)*C, R)
549  // ---------------------------------------------------------------------
550 
551  // Helpful typedefs for the templates
552  typedef double ST;
553  typedef Epetra_MultiVector MV;
554  typedef Epetra_Operator OP;
555  typedef Belos::MultiVecTraits<ST,MV> MVT;
556 
557  // Output manager.
558  Belos::OutputManager<ST> printer;
559 
560  // Status test.
561  Belos::StatusTestMaxIters<ST, MV, OP> maxIterTest( kblock );
562 
563  // Orthogonalization manager.
564  Belos::DGKSOrthoManager<ST, MV, OP> orthoMgr;
565 
566  // Linear Problem.
567  // Reuse RPtr_. We need the basis vectors for K(inv(G + s0*C)*C, R)
568  N_LAS_MultiVector temp( BaseMap_, numPorts_ );
569  Belos::LinearProblem<ST, MV, OP > problem( AOp,
570  rcp( &temp.epetraObj(), false ),
571  rcp( &RPtr_->epetraObj(), false ));
572  problem.setProblem();
573 
574  // Create parameter list.
575  Teuchos::ParameterList params;
576  params.set("Num Blocks", kblock);
577  params.set("Block Size", numPorts_ );
578 
579  // Create Krylov subspace iteration from Belos
580  Belos::BlockGmresIter<ST, MV, OP> krylovIter( rcp( &problem, false ),
581  rcp( &printer, false ),
582  rcp( &maxIterTest, false ),
583  rcp( &orthoMgr, false ),
584  params );
585 
586  // Get a matrix to hold the orthonormalization coefficients.
587  RCP<Teuchos::SerialDenseMatrix<int,ST> > z
588  = rcp( new Teuchos::SerialDenseMatrix<int,ST>( numPorts_, numPorts_ ) );
589 
590  // Orthonormalize the Krylov kernel vectors V
591  RCP<MV> V = rcp( new Epetra_MultiVector( RPtr_->epetraObj() ) );
592  orthoMgr.normalize( *V, z );
593 
594  // Set the new state and initialize the solver to use R as the kernel vectors.
595  Belos::GmresIterationState<ST,MV> initState;
596  initState.V = V;
597  initState.z = z;
598  initState.curDim = 0;
599  krylovIter.initializeGmres(initState);
600 
601  // Have the solver iterate until the basis size is computed
602  try {
603  krylovIter.iterate();
604  }
605  catch (const Belos::GmresIterationOrthoFailure &e) {
606  // This might happen if the basis size is the same as the operator's dimension.
607  }
608  catch (const std::exception &e) {
609  bsuccess = false; // Anything else is a failure.
610  }
611 
612  // Get the basis vectors back from the iteration object
613  Belos::GmresIterationState<ST,MV> newState = krylovIter.getState();
614 
615  // Create a view into the first k vectors of newState.V
616  std::vector<int> indices( k );
617  for (int i=0; i<k; ++i) { indices[i] = i; }
618  V = rcp( new Epetra_MultiVector( View, *newState.V, &indices[0], k ) );
619 
620  RCP<MV> W = rcp( new Epetra_MultiVector( V->Map(), k ) );
621  W = V;
622 // Xyce::dout() << "Printing out V" << std::endl;
623 // V->Print(Xyce::dout());
624 // Xyce::dout() << "Printing out W" << std::endl;
625 // W->Print(Xyce::dout());
626 
627  // ---------------------------------------------------------------------
628  // Sparsify the reduced system, if requested.
629  // ---------------------------------------------------------------------
631  {
632 
633  N_LAS_MultiVector xyceV( &*V, false );
634  N_LAS_MultiVector temp2( BaseMap_, k );
635 
636  // G * V
637  GPtr_->matvec( false, xyceV, temp2 );
638  // V' * G * V
639  MVT::MvTransMv( 1.0, xyceV.epetraObj(), temp2.epetraObj(), redG_ );
640  //redG_.print( Xyce::dout() );
641 
642  // C * V
643  CPtr_->matvec( false, xyceV, temp2 );
644  // V' * C * V
645  MVT::MvTransMv( 1.0, xyceV.epetraObj(), temp2.epetraObj(), redC_ );
646  //redC_.print( Xyce::dout() );
647 
648  // V' * B
649  MVT::MvTransMv( 1.0, xyceV.epetraObj(), BPtr_->epetraObj(), redB_ );
650  //redB_.print( Xyce::dout() );
651 
652  bsuccess = bsuccess & sparsifyRedSystem_();
653  }
654 
655  // -----------------------------------------------------------------------------
656  // Scale the basis vectors, if requested, before computing the projected system
657  // -----------------------------------------------------------------------------
658 
659  // Create the scaling vector.
660  Teuchos::SerialDenseMatrix<int,ST> Xhatscale( k, 1 );
661 
662  int scaleType = tiaParams_.morScaleType;
663 
664  if ( scaleType == 1 )
665  {
666  for (int i=0; i<k; ++i)
667  {
668  Xhatscale( i, 0 ) = tiaParams_.morScaleFactor;
669  }
670  }
671 
672 // if ( scaleType == 2 )
673 // {
674 
675 // }
676 
677 if ( scaleType == 2 || scaleType == 3 || scaleType ==4)
678  {
679  Epetra_MultiVector Xmag( V->Map(), 1 );
680 
681  if ( scaleType == 2 )
682  Xmag.PutScalar( tiaParams_.morScaleFactor );
683 
684  if ( scaleType == 4)
685  Xmag.PutScalar(1.0);
686 
687  MVT::MvTransMv( 1.0, *V, Xmag, Xhatscale );
688 
689 // Xhatscale.print(Xyce::dout());
690  for (int i=0; i<k; ++i)
691  {
692 
693  if ( scaleType == 2 )
694  {
695  if ( fabs(Xhatscale( i, 0 )) > tiaParams_.morScaleFactor)
696  {
697  Xhatscale( i, 0 ) = tiaParams_.morScaleFactor / fabs( Xhatscale( i, 0 ) );
698  }
699  else
700  {
701  Xhatscale( i, 0 ) = tiaParams_.morScaleFactor1 / fabs( Xhatscale( i, 0 ) );
702  }
703  }
704 
705  if ( scaleType == 3 )
706  Xhatscale( i, 0 ) = tiaParams_.morScaleFactor * fabs( Xhatscale( i, 0 ) );
707 
708  if ( scaleType == 4 )
709  {
710  if ( fabs(Xhatscale( i, 0 )) > 1.0 )
711  {
712  Xhatscale( i, 0 ) = tiaParams_.morScaleFactor / fabs( Xhatscale( i, 0 ) );
713  }
714  else
715  {
716  Xhatscale( i, 0 ) = tiaParams_.morScaleFactor1 / fabs( Xhatscale( i, 0 ) );
717  }
718  }
719 
720 
721  }
722  }
723 
724 // Xhatscale.print(Xyce::dout());
725 
726  // Scale the computed basis vectors before projecting the original system
727  Teuchos::SerialDenseMatrix<int,ST> D(k,k);
728  if ( scaleType != 0 )
729  {
730  RCP<MV> Vtemp = rcp( new Epetra_MultiVector( V->Map(), k ) );
731 // Teuchos::SerialDenseMatrix<int,ST> D(k,k);
732  for (int i=0; i<k; ++i)
733  {
734  if (Xhatscale( i, 0 ) != 0.0)
735  D(i,i) = 1.0/Xhatscale( i, 0 );
736  else
737  D(i,i) = 1.0;
738  }
739  Xyce::dout() << " the scaling matrix " << std::endl;
740 
741 // D.print(Xyce::dout());
742 
743  MVT::MvTimesMatAddMv( 1.0, *V, D, 0.0, *Vtemp );
744  V = Vtemp;
745  }
746 
747 // Xyce::dout() << "Printing out V" << std::endl;
748 // V->Print(Xyce::dout());
749 // Xyce::dout() << "Printing out W" << std::endl;
750 // W->Print(Xyce::dout());
751 
752  // ---------------------------------------------------------------------
753  // Now use the basis vectors for K_k(inv(G + s0*C)*C, R) to compute
754  // the projected system.
755  // ---------------------------------------------------------------------
756  N_LAS_MultiVector xyceV( &*V, false );
757  N_LAS_MultiVector xyceW( &*W, false );
758  N_LAS_MultiVector temp2( BaseMap_, k );
759 
761  {
762  // G * V
763  GPtr_->matvec( false, xyceV, temp2 );
764  // V' * G * V
765  MVT::MvTransMv( 1.0, xyceW.epetraObj(), temp2.epetraObj(), redG_ );
766  //redG_.print( Xyce::dout() );
767 
768  // C * V
769  CPtr_->matvec( false, xyceV, temp2 );
770  // V' * C * V
771  MVT::MvTransMv( 1.0, xyceW.epetraObj(), temp2.epetraObj(), redC_ );
772 
773  // V' * B
774  MVT::MvTransMv( 1.0, xyceW.epetraObj(), BPtr_->epetraObj(), redB_ );
775 
776  MVT::MvTransMv( 1.0, xyceV.epetraObj(), BPtr_->epetraObj(), redL_ );
777  }
778  else
779  {
780  if ( scaleType <= 1 )
781  {
782  redCPtr_->scale(1.0/tiaParams_.morScaleFactor);
783  redGPtr_->scale(1.0/tiaParams_.morScaleFactor);
784 
785  redL_.scale(1.0/tiaParams_.morScaleFactor);
786  }
787  else
788  {
789  Report::UserError0() << "MOR options sparsificationType=1 can only be used with scaletype=1, other scale types have not been supported for sparsification";
790  return false;
791  }
792  }
793 
794  // ---------------------------------------------------------------------
795  // Output the projected system, redG_, redC_, and redB_.
796  // ---------------------------------------------------------------------
797 
799  {
800  // If sparsification was used, write out the N_LAS_Matrix object instead of the dense object.
803  else
805  }
806 
807 #else
808  Report::UserError0() << "Belos is necessary to compute a reduced-order model, please recompile Xyce with Belos enabled";
809  return false;
810 
811 #endif // Xyce_BELOS
812 
813  return bsuccess;
814 }
815 
816 //-----------------------------------------------------------------------------
817 // Function : MOR::evalOrigTransferFunction()
818 // Purpose : Evaluate original transfer function.
819 // Special Notes :
820 // Scope : public
821 // Creator : Heidi Thornquist and Ting Mei, SNL
822 // Creation Date : 5/29/2012
823 //-----------------------------------------------------------------------------
825 {
826  bool bsuccess = true;
827 
829 
830  int currentStep = 0;
831  int finalStep = morEvalSize_;
832 
833  bool stepAttemptStatus;
834 
835  while (currentStep < finalStep)
836  {
837  updateCurrentFreq_(currentStep);
838 
840 
841  stepAttemptStatus = solveOrigLinearSystem_();
842 
843  currentStep++;
844 
845  if (stepAttemptStatus)
846  {
847  processSuccessfulStep(true);
848  }
849  else // stepAttemptStatus (ie do this if the step FAILED)
850  {
852  }
853 
854  }
855 
856  return bsuccess;
857 }
858 
859 //-----------------------------------------------------------------------------
860 // Function : MOR::evalRedTransferFunction()
861 // Purpose : Evaluate reduced transfer function.
862 // Special Notes :
863 // Scope : public
864 // Creator : Heidi Thornquist and Ting Mei, SNL
865 // Creation Date : 5/29/2012
866 //-----------------------------------------------------------------------------
868 {
869  bool bsuccess = true;
870 
872 
873  int currentStep = 0;
874  int finalStep = morEvalSize_;
875 
876  bool stepAttemptStatus;
877 
878  while (currentStep < finalStep)
879  {
880  updateCurrentFreq_(currentStep);
881 
883 
884  stepAttemptStatus = solveRedLinearSystem_();
885 
886  currentStep++;
887 
888  if (stepAttemptStatus)
889  {
890  processSuccessfulStep(false);
891  }
892  else // stepAttemptStatus (ie do this if the step FAILED)
893  {
895  }
896  }
897 
898  return bsuccess;
899 }
900 
901 //-----------------------------------------------------------------------------
902 // Function : MOR::createOrigLinearSystem_()
903 // Purpose :
904 // Special Notes :
905 // Scope : public
906 // Creator : Ting Mei, Heidi Thornquist, SNL
907 // Creation Date : 6/20/2011
908 //-----------------------------------------------------------------------------
910 {
911  bool bsuccess = true;
912 
913  N_PDS_Manager &pdsMgrPtr_ = *linearSystem_.getPDSManager();
914 
915  N_PDS_ParMap &BaseMap_ = *pdsMgrPtr_.getParallelMap("SOLUTION");
916  N_PDS_ParMap &oBaseMap_ = *pdsMgrPtr_.getParallelMap("SOLUTION_OVERLAP_GND");
917  Epetra_CrsGraph &BaseFullGraph_ = *pdsMgrPtr_.getMatrixGraph("JACOBIAN");
918 
919  int numBlocks = 2;
920 
921  std::vector<RCP<N_PDS_ParMap> > blockMaps = createBlockParMaps(numBlocks, BaseMap_, oBaseMap_);
922 
923  // Create a block vector
924  REFBPtr_ = rcp ( new N_LAS_BlockVector ( numBlocks, blockMaps[0], rcp(&BaseMap_, false) ) );
925 
926  std::vector<std::vector<int> > blockPattern(2);
927  blockPattern[0].resize(2);
928  blockPattern[0][0] = 0; blockPattern[0][1] = 1;
929  blockPattern[1].resize(2);
930  blockPattern[1][0] = 0; blockPattern[1][1] = 1;
931 
932  int MaxGID = BaseMap_.maxGlobalEntity();
933  int offset=1;
934  while ( offset <= MaxGID ) offset *= 10;
935 
936  RCP<Epetra_CrsGraph> blockGraph = createBlockGraph( offset, blockPattern, *blockMaps[0], BaseFullGraph_);
937 
938  sCpG_REFMatrixPtr_ = rcp ( new N_LAS_BlockMatrix( numBlocks, offset, blockPattern, *blockGraph, BaseFullGraph_) );
939 
940  // Load diagonal blocks of real equivalent form: (G - s0*C)
941  sCpG_REFMatrixPtr_->put( 0.0 ); // Zero out whole matrix
942 
943  // Add scaled C matrix first if expansion point is not zero.
944  if (s0_ != 0.0)
945  {
946  sCpG_REFMatrixPtr_->block( 0, 0 ).add(*CPtr_);
947  sCpG_REFMatrixPtr_->block( 0, 0 ).scale(-s0_);
948  sCpG_REFMatrixPtr_->block( 1, 1 ).add(*CPtr_);
949  sCpG_REFMatrixPtr_->block( 1, 1 ).scale(-s0_);
950  }
951  // Add G into diagonal block to obtain (G - s0*C)
952  sCpG_REFMatrixPtr_->block( 0, 0 ).add(*GPtr_);
953  sCpG_REFMatrixPtr_->block( 1, 1 ).add(*GPtr_);
954 
955  // Create solver factory
956  Amesos amesosFactory;
957 
958  REFXPtr_ = rcp ( new N_LAS_BlockVector ( numBlocks, blockMaps[0], rcp(&BaseMap_, false) ) );
959  REFXPtr_->putScalar( 0.0 );
960 
961  blockProblem_ = rcp(new Epetra_LinearProblem(&sCpG_REFMatrixPtr_->epetraObj(), &REFXPtr_->epetraObj(), &REFBPtr_->epetraObj() ) );
962 
963  blockSolver_ = rcp( amesosFactory.Create( "Klu", *blockProblem_ ) );
964 
965  int linearStatus = blockSolver_->SymbolicFactorization();
966 
967  if (linearStatus != 0)
968  {
969  Xyce::dout() << "Amesos symbolic factorization exited with error: " << linearStatus;
970  bsuccess = false;
971  }
972 
973  return bsuccess;
974 
975 }
976 
977 //-----------------------------------------------------------------------------
978 // Function : MOR::createRedLinearSystem_()
979 // Purpose :
980 // Special Notes :
981 // Scope : public
982 // Creator : Ting Mei, Heidi Thornquist, SNL
983 // Creation Date : 6/20/2011
984 //-----------------------------------------------------------------------------
986 {
987  // Get the reduced system size.
988  int k = redG_.numRows();
989 
990  // Resize serial dense matrix for real-equivalent form.
991  sCpG_redMatrix_.shape(2*k, 2*k);
992  sCpG_tmpMatrix_.shape(2*k, 2*k);
993  ref_redB_.shape(2*k, numPorts_);
994 
995  // First, load the diagonals.
996  // Get a view of the primary diagonal block, insert (G - s0*C).
997  Teuchos::SerialDenseMatrix<int, double> subMtx( Teuchos::View, sCpG_tmpMatrix_, k, k, 0, 0 );
998  subMtx.assign( redC_ );
999  subMtx.scale( -s0_ );
1000  subMtx += redG_;
1001 
1002  // Get a view of the lower k x k diagonal block, insert (G - s0*C).
1003  Teuchos::SerialDenseMatrix<int, double> subMtx2( Teuchos::View, sCpG_tmpMatrix_, k, k, k, k );
1004  subMtx2.assign( redC_ );
1005  subMtx2.scale( -s0_ );
1006  subMtx2 += redG_;
1007 
1008  return true;
1009 }
1010 
1011 //-----------------------------------------------------------------------------
1012 // Function : MOR::updateOrigLinearSystemFreq_()
1013 // Purpose :
1014 // Special Notes :
1015 // Scope : public
1016 // Creator : Ting Mei, Heidi Thornquist, SNL
1017 // Creation Date : 6/20/2011
1018 //-----------------------------------------------------------------------------
1019 
1021 {
1022  double omega = 2.0 * M_PI * currentFreq_;
1023 
1024  sCpG_REFMatrixPtr_->block( 0, 1).put( 0.0);
1025  sCpG_REFMatrixPtr_->block( 0, 1).add(*CPtr_);
1026  sCpG_REFMatrixPtr_->block( 0, 1).scale(-omega);
1027 
1028  sCpG_REFMatrixPtr_->block(1, 0).put( 0.0);
1029  sCpG_REFMatrixPtr_->block(1, 0).add(*CPtr_);
1030  sCpG_REFMatrixPtr_->block(1, 0).scale(omega);
1031 
1032  return true;
1033 }
1034 
1035 //-----------------------------------------------------------------------------
1036 // Function : MOR::updateRedLinearSystemFreq_()
1037 // Purpose :
1038 // Special Notes :
1039 // Scope : public
1040 // Creator : Ting Mei, Heidi Thornquist, SNL
1041 // Creation Date : 6/20/2011
1042 //-----------------------------------------------------------------------------
1043 
1045 {
1046  int k = redG_.numRows();
1047 
1048  double omega = 2.0 * M_PI * currentFreq_;
1049 
1050  // Reset reduced real equivalent form matrix to sCpG_tmpMatrix_
1051  sCpG_redMatrix_.assign( sCpG_tmpMatrix_ );
1052 
1053  // Insert off diagonals.
1054  Teuchos::SerialDenseMatrix<int, double> subMtx( Teuchos::View, sCpG_redMatrix_, k, k, 0, k );
1055  subMtx.assign( redC_ );
1056  subMtx.scale( -omega );
1057 
1058  Teuchos::SerialDenseMatrix<int, double> subMtx2( Teuchos::View, sCpG_redMatrix_, k, k, k, 0 );
1059  subMtx2.assign( redC_ );
1060  subMtx2.scale( omega );
1061 
1062  return true;
1063 }
1064 
1065 //-----------------------------------------------------------------------------
1066 // Function : MOR::solveLinearSystem_()
1067 // Purpose :
1068 // Special Notes :
1069 // Scope : public
1070 // Creator : Ting Mei, Heidi Thornquist, SNL
1071 // Creation Date : 6/2011
1072 //-----------------------------------------------------------------------------
1073 
1075 {
1076 
1077  bool bsuccess = true;
1078 
1079  // Solve the block problem
1080  int linearStatus = blockSolver_->NumericFactorization();
1081 
1082  if (linearStatus != 0)
1083  {
1084  Xyce::dout() << "Amesos numeric factorization exited with error: " << linearStatus;
1085  bsuccess = false;
1086  }
1087 
1088  // Loop over number of I/O ports here
1089  for (unsigned int j=0; j < bMatEntriesVec_.size(); ++j)
1090  {
1091  REFBPtr_->putScalar( 0.0 );
1092  (REFBPtr_->block( 0 ))[bMatEntriesVec_[j]] = -1.0;
1093 
1094  linearStatus = blockSolver_->Solve();
1095  if (linearStatus != 0)
1096  {
1097  Xyce::dout() << "Amesos solve exited with error: " << linearStatus;
1098  bsuccess = false;
1099  }
1100 
1101  // Compute transfer function entries for all I/O
1102  for (unsigned int i=0; i < bMatEntriesVec_.size(); ++i)
1103  {
1104  // Populate H for all ports in L
1105  // L is the same as B and also a set of canonical basis vectors (e_i), so
1106  // we can pick off the appropriate entries of REFXPtr to place into H.
1107  origH_(i,j) = std::complex<double>(-(REFXPtr_->block( 0 ))[bMatEntriesVec_[i]],
1108  -(REFXPtr_->block( 1 ))[bMatEntriesVec_[i]]);
1109  }
1110  }
1111  return bsuccess;
1112 }
1113 
1114 //-----------------------------------------------------------------------------
1115 // Function : MOR::solveRedLinearSystem_()
1116 // Purpose :
1117 // Special Notes :
1118 // Scope : public
1119 // Creator : Ting Mei, Heidi Thornquist, SNL
1120 // Creation Date : 6/2011
1121 //-----------------------------------------------------------------------------
1122 
1124 {
1125  bool bsuccess = true;
1126 
1127  int k = redG_.numRows();
1128  int numPorts = bMatEntriesVec_.size();
1129  Teuchos::LAPACK<int, double> lapack;
1130 
1131  ref_redB_.putScalar( 0.0 );
1132  Teuchos::SerialDenseMatrix<int, double> tmpRedB( Teuchos::View, ref_redB_, redB_.numRows(), redB_.numCols(), 0, 0 );
1133  tmpRedB.assign( redB_ );
1134 
1135  // First factor matrix using LU.
1136  int info = 0;
1137  std::vector<int> ipiv( sCpG_redMatrix_.numRows() );
1138  lapack.GETRF( sCpG_redMatrix_.numRows(), sCpG_redMatrix_.numCols(), sCpG_redMatrix_.values(),
1139  sCpG_redMatrix_.stride(), &ipiv[0], &info );
1140  if (info != 0)
1141  {
1142  Xyce::dout() << "LAPACK::GETRF: LU factorization failed with error: " << info << std::endl;
1143  bsuccess = false;
1144  }
1145 
1146  // Next solve for all ports at once using LU factors.
1147  const char trans = 'N';
1148  lapack.GETRS( trans, sCpG_redMatrix_.numRows(), ref_redB_.numCols(), sCpG_redMatrix_.values(),
1149  sCpG_redMatrix_.stride(), &ipiv[0], ref_redB_.values(), ref_redB_.stride(), &info );
1150  if (info != 0)
1151  {
1152  Xyce::dout() << "LAPACK::GETRS: LU solve failed with error: " << info << std::endl;
1153  bsuccess = false;
1154  }
1155 
1156  Teuchos::SerialDenseMatrix<int, double> tmpRedImag( numPorts, numPorts ), tmpRedReal( numPorts, numPorts );
1157 
1158  // Compute real part.
1159  tmpRedReal.multiply( Teuchos::TRANS, Teuchos::NO_TRANS, 1.0, redB_, tmpRedB, 0.0 );
1160 
1161  // Compute imaginary part.
1162  Teuchos::SerialDenseMatrix<int, double> tmpRedB2( Teuchos::View, ref_redB_, redB_.numRows(), redB_.numCols(), k, 0 );
1163  tmpRedImag.multiply( Teuchos::TRANS, Teuchos::NO_TRANS, 1.0, redB_, tmpRedB2, 0.0 );
1164 
1165  // Compute transfer function entries for all I/O
1166  for (unsigned int j=0; j < bMatEntriesVec_.size(); ++j)
1167  {
1168  for (unsigned int i=0; i < bMatEntriesVec_.size(); ++i)
1169  {
1170  redH_(i,j) = std::complex<double>(tmpRedReal(i,j), tmpRedImag(i,j));
1171  }
1172  }
1173 
1174  return bsuccess;
1175 }
1176 
1177 //-----------------------------------------------------------------------------
1178 // Function : MOR::sparsifyRedSystem_()
1179 // Purpose : Use techniques to sparsify the projected system.
1180 // Special Notes :
1181 // Scope : public
1182 // Creator : Heidi Thornquist and Ting Mei, SNL
1183 // Creation Date : 8/20/2012
1184 //-----------------------------------------------------------------------------
1186 {
1187  bool bsuccess = true;
1188 
1189  int k = redB_.numRows();
1190 
1191  // Storage vectors
1192  Teuchos::SerialDenseMatrix<int, double> R(redB_);
1193  Teuchos::SerialDenseMatrix<int, double> A(redC_);
1194 
1195  // Factor Ghat.
1196  Teuchos::LAPACK<int, double> lapack;
1197  Teuchos::SerialDenseMatrix<int, double> tmp_Ghat( redG_ );
1198 
1199  int info = 0;
1200  std::vector<int> ipiv( tmp_Ghat.numRows() );
1201  lapack.GETRF( tmp_Ghat.numRows(), tmp_Ghat.numCols(), tmp_Ghat.values(),
1202  tmp_Ghat.stride(), &ipiv[0], &info );
1203  if (info != 0)
1204  {
1205  Xyce::dout() << "LAPACK::GETRF: LU factorization of Ghat failed with error: " << info << std::endl;
1206  bsuccess = false;
1207  }
1208 
1209  // Compute reciprocal condition estimate of Ghat.
1210  const char norm = '1';
1211  double condGhat = 0.0;
1212  std::vector<double> condWork( 4*k );
1213  std::vector<int> condIWork( k );
1214  lapack.GECON( norm, tmp_Ghat.numRows(), tmp_Ghat.values(), tmp_Ghat.stride(), tmp_Ghat.normOne(), &condGhat, &condWork[0], &condIWork[0], &info );
1215  // Xyce::dout() << "Condition estimate for Ghat is : " << 1.0/ condGhat << std::endl;
1216  if (info != 0)
1217  {
1218  Xyce::dout() << "LAPACK::GECON: Computing condition estimate of Ghat failed with error: " << info << std::endl;
1219  bsuccess = false;
1220  }
1221 
1222  // Compute A = inv(Ghat)* Chat
1223  const char trans = 'N';
1224  lapack.GETRS( trans, tmp_Ghat.numRows(), A.numCols(), tmp_Ghat.values(),
1225  tmp_Ghat.stride(), &ipiv[0], A.values(), A.stride(), &info );
1226  if (info != 0)
1227  {
1228  Xyce::dout() << "LAPACK::GETRS: LU solve failed with error: " << info << std::endl;
1229  bsuccess = false;
1230  }
1231  //Xyce::dout() << "A = inv(Ghat)*Chat" << std::endl;
1232  //A.print(Xyce::dout());
1233 
1234  // Compute R = inv(Ghat)* Bhat
1235  lapack.GETRS( trans, tmp_Ghat.numRows(), R.numCols(), tmp_Ghat.values(),
1236  tmp_Ghat.stride(), &ipiv[0], R.values(), R.stride(), &info );
1237  if (info != 0)
1238  {
1239  Xyce::dout() << "LAPACK::GETRS: LU solve failed with error: " << info << std::endl;
1240  bsuccess = false;
1241  }
1242  //Xyce::dout() << "R = inv(Ghat)*Bhat" << std::endl;
1243  //R.print(Xyce::dout());
1244 
1245  // Reduce A to Schur form and compute the eigenvalues.
1246  // Allocate the work space. This space will be used below for calls to:
1247  // * GEES (requires 3*k for real)
1248  // * TREVC (requires 3*k for real)
1249  // Furthermore, GEES requires a real array of length k
1250  //
1251  int lwork = 8*k;
1252  std::vector<double> work( lwork );
1253  std::vector<double> revals( k );
1254  std::vector<double> ievals( k );
1255  Teuchos::SerialDenseMatrix<int, double> Q(k, k);
1256 
1257  // Create diagonal tmpB for the eigenproblem (A, tmpB), beta should return as a vector of ones.
1258  const int ldvl = 1;
1259  double vl[ ldvl ];
1260  std::vector<double> beta( k );
1261  Teuchos::SerialDenseMatrix<int, double> tmpB( k, k );
1262  for (int i=0; i<k; i++)
1263  tmpB(i,i) = 1.0;
1264  lapack.GGEV( 'N', 'V', k, A.values(), A.stride(), tmpB.values(), tmpB.stride(),
1265  &revals[0], &ievals[0], &beta[0], &vl[0], ldvl, Q.values(), Q.stride(),
1266  &work[0], lwork, &info );
1267  if (info != 0)
1268  {
1269  Xyce::dout() << "LAPACK::GGEV: Computing eigenvectors and values of A = inv(Ghat)*Chat failed with error: " << info << std::endl;
1270  bsuccess = false;
1271  }
1272 
1273  // Scale the eigenvectors returned from GGEV to have 2-norm = 1
1274  // They are initially scaled to have inf-norm = 1 from LAPACK
1275  Teuchos::BLAS<int, double> blas;
1276  std::vector<int> rowIndex( k );
1277  int i = 0;
1278 
1279  while( i < k ) {
1280  if ( ievals[i] != 0.0 )
1281  {
1282  rowIndex[i] = 1;
1283  rowIndex[i+1] = -1;
1284 
1285  // Compute the 2-norm of the complex eigenvector
1286  double norm_r = blas.NRM2( k, Q[i], 1 );
1287  double norm_i = blas.NRM2( k, Q[i+1], 1 );
1288  double norm = lapack.LAPY2( norm_r, norm_i );
1289 
1290  // Scale the complex eigenvector
1291  for (int j=0; j<k; j++)
1292  {
1293  Q(j,i) /= norm;
1294  Q(j,i+1) /= norm;
1295  }
1296  i += 2;
1297  }
1298  else
1299  {
1300  rowIndex[i] = 0;
1301 
1302  // Compute the 2-norm of the i-th column
1303  double norm = blas.NRM2( k, Q[i], 1 );
1304 
1305  // Scale the real eigenvector
1306  for (int j=0; j<k; j++)
1307  {
1308  Q(j,i) /= norm;
1309  }
1310  i++;
1311  }
1312  }
1313 
1314  // Xyce::dout() << "Eigenvalues of A: " << std::endl;
1315  // for (int i=0; i<k; ++i)
1316  // Xyce::dout() << revals[i] << "\t" << ievals[i] << "\t" << beta[i] << std::endl;
1317 
1318  // Xyce::dout() << "Eigenvectors of A: " << std::endl;
1319  // Q.print(Xyce::dout());
1320 
1321  // Construct complex eigenvectors
1322  Teuchos::SerialDenseMatrix<int, std::complex<double> > V(k, k);
1323  for (int i=0; i<k; i++)
1324  {
1325  if (rowIndex[i] == 1)
1326  {
1327  for (int j=0; j<k; j++)
1328  {
1329  // Insert both conjugate pairs simultaneously.
1330  V(j,i) = std::complex<double>( Q(j,i), Q(j,i+1) );
1331  V(j,i+1) = std::complex<double>( Q(j,i), -Q(j,i+1) );
1332  }
1333  i++; // Need to increment an extra value for the conjugate pair.
1334  }
1335  else // The eigenvector is real, copy directly
1336  {
1337  for (int j=0; j<k; j++)
1338  {
1339  V(j,i) = std::complex<double>( Q(j,i), 0.0 );
1340  }
1341  }
1342  }
1343 
1344  // Factor V
1345  Teuchos::LAPACK<int, std::complex<double> > lapack_complex;
1346  lapack_complex.GETRF( V.numRows(), V.numCols(), V.values(), V.stride(), &ipiv[0], &info );
1347  if (info != 0)
1348  {
1349  Xyce::dout() << "LAPACK::GETRF: LU factorization of eigenvectors failed with error: " << info << std::endl;
1350  bsuccess = false;
1351  }
1352 
1353  // Compute condition estimate of V.
1354  double condV = 0.0;
1355  std::vector<std::complex<double> > condCWork( 2*k );
1356  lapack_complex.GECON( norm, V.numRows(), V.values(), V.stride(), V.normOne(), &condV, &condCWork[0], &condWork[0], &info );
1357  //Xyce::dout() << "Condition estimate for V is : " << 1.0/condV << std::endl;
1358  if (info != 0)
1359  {
1360  Xyce::dout() << "LAPACK::GECON: Computing condition estimate of V failed with error: " << info << std::endl;
1361  bsuccess = false;
1362  }
1363 
1364  // Compute inv(V)
1365  std::vector<std::complex<double> > cwork( lwork );
1366  lapack_complex.GETRI( V.numRows(), V.values(), V.stride(), &ipiv[0], &cwork[0], lwork, &info );
1367  if (info != 0)
1368  {
1369  Xyce::dout() << "LAPACK::GETRI: Computing inverse of V failed with error: " << info << std::endl;
1370  bsuccess = false;
1371  }
1372 
1373  // Convert R to a complex vector to use the multiply routine for inv(V).
1374  Teuchos::SerialDenseMatrix<int, std::complex<double> > tmpR( k, numPorts_ );
1375  for (int j=0; j<numPorts_; j++)
1376  for (int i=0; i<k; i++)
1377  tmpR(i,j) = std::complex<double>( R(i,j), 0.0 );
1378 
1379  // Compute inv(V) * R
1380  Teuchos::SerialDenseMatrix<int, std::complex<double> > tmp_redB(k ,numPorts_);
1381  tmp_redB.multiply(Teuchos::NO_TRANS, Teuchos::NO_TRANS, 1.0, V, tmpR, 0.0);
1382 
1383  // Generate compressed real storage of V*R and inv(V)
1384  redL_ = redB_; // Store copy of redB_ before destroying it below.
1385  Teuchos::SerialDenseMatrix<int, double> invV(k, k);
1386  for (int i=0; i<k; i++)
1387  {
1388  if (rowIndex[i] == 1) // Complex conjugate pair.
1389  {
1390  for (int j=0; j<k; j++)
1391  {
1392  invV(i, j) = V(i,j).real();
1393  invV(i+1, j) = V(i,j).imag();
1394  }
1395  for (int j=0; j<numPorts_; j++)
1396  {
1397  redB_(i, j) = tmp_redB(i, j).real();
1398  redB_(i+1, j) = tmp_redB(i, j).imag();
1399  }
1400  i++; // Increment by one to skip complex conjugate.
1401  }
1402  else // Eigenvalue is real, so is eigenvector.
1403  {
1404  for (int j=0; j<k; j++)
1405  invV(i, j) = V(i,j).real();
1406  for (int j=0; j<numPorts_; j++)
1407  redB_(i, j) = tmp_redB(i, j).real();
1408  }
1409  }
1410 
1411  // Factor invV.
1412  lapack.GETRF( invV.numRows(), invV.numCols(), invV.values(), invV.stride(), &ipiv[0], &info );
1413  if (info != 0)
1414  {
1415  Xyce::dout() << "LAPACK::GETRF: LU factorization of inv(V) failed with error: " << info << std::endl;
1416  bsuccess = false;
1417  }
1418 
1419  // Compute A = inv(invV)^T*Lhat
1420  const char trans2 = 'T';
1421  lapack.GETRS( trans2, invV.numRows(), redL_.numCols(), invV.values(),
1422  invV.stride(), &ipiv[0], redL_.values(), redL_.stride(), &info );
1423  if (info != 0)
1424  {
1425  Xyce::dout() << "LAPACK::GETRS: LU solve for Lhat failed with error: " << info << std::endl;
1426  bsuccess = false;
1427  }
1428 
1429  // Xyce::dout() << "Bhat : " << std::endl;
1430  // redB_.print(Xyce::dout());
1431  // Xyce::dout() << "Lhat : " << std::endl;
1432  // redL_.print(Xyce::dout());
1433 
1434  // Create redCPtr_ and redGPtr_
1435  // Generate Epetra_Map that puts all the values of the reduced system on one processor.
1436  N_PDS_Manager &pdsMgrPtr_ = *linearSystem_.getPDSManager();
1437  N_PDS_ParMap &BaseMap_ = *pdsMgrPtr_.getParallelMap("SOLUTION");
1438  RCP<Epetra_Map> serialRedMap;
1439  if (BaseMap_.petraMap()->Comm().MyPID() == 0)
1440  serialRedMap = Teuchos::rcp( new Epetra_Map( k, k, 0, BaseMap_.petraMap()->Comm() ) );
1441  else
1442  serialRedMap = Teuchos::rcp( new Epetra_Map( k, 0, 0, BaseMap_.petraMap()->Comm() ) );
1443 
1444  Epetra_CrsMatrix* tmpRedG = new Epetra_CrsMatrix( Copy, *serialRedMap, 1 );
1445  Epetra_CrsMatrix* tmpRedC = new Epetra_CrsMatrix( Copy, *serialRedMap, 2 );
1446 
1447  // Let processor 0 insert entries into tmpRedG and tmpRedC
1448  if (BaseMap_.petraMap()->Comm().MyPID() == 0)
1449  {
1450  std::vector<int> index(2);
1451  std::vector<double> val(2);
1452  for (int i=0; i<k; i++)
1453  {
1454  // Insert G, which is a diagonal of ones.
1455  index[0] = i; val[0] = 1.0;
1456  tmpRedG->InsertGlobalValues( i, 1, &val[0], &index[0] );
1457 
1458  // Insert C, which is block diagonal, where the blocks represent conjugate eigenvalues.
1459  if (rowIndex[i] == 1)
1460  {
1461  index[0] = i; index[1] = i+1;
1462  val[0] = revals[i]; val[1] = -ievals[i];
1463  tmpRedC->InsertGlobalValues( i, 2, &val[0], &index[0] );
1464  }
1465  else if (rowIndex[i] == -1)
1466  {
1467  index[0] = i-1; index[1] = i;
1468  val[0] = ievals[i-1]; val[1] = revals[i-1];
1469  tmpRedC->InsertGlobalValues( i, 2, &val[0], &index[0] );
1470  }
1471  else // Must be real.
1472  {
1473  index[0] = i; val[0] = revals[i];
1474  tmpRedC->InsertGlobalValues( i, 1, &val[0], &index[0] );
1475  }
1476  }
1477  }
1478  // We are done inserting values.
1479  tmpRedC->FillComplete();
1480  tmpRedG->FillComplete();
1481 
1482  // Pass tmpRedG and tmpRedC into redGPtr_ and redGPtr_, respectively, which will manage the memory.
1483  redGPtr_ = rcp( new N_LAS_Matrix( tmpRedG ) );
1484  redCPtr_ = rcp( new N_LAS_Matrix( tmpRedC ) );
1485 
1486  return bsuccess;
1487 }
1488 
1489 //-----------------------------------------------------------------------------
1490 // Function : MOR::processSuccessfulStep()
1491 // Purpose :
1492 // Special Notes :
1493 // Scope : public
1494 // Creator : Ting Mei, SNL
1495 // Creation Date :
1496 //-----------------------------------------------------------------------------
1497 bool MOR::processSuccessfulStep(bool origSys)
1498 {
1499  bool bsuccess = true;
1500 
1501  // Output H.
1502  if (origSys)
1503  {
1505  }
1506  else
1507  {
1509  }
1510 
1511  if ( !firstDoubleDCOPStep_() )
1512  {
1513  stepNumber += 1;
1516  }
1517 
1518  return bsuccess;
1519 }
1520 
1521 
1522 //-----------------------------------------------------------------------------
1523 // Function : MOR::processFailedStep
1524 // Purpose :
1525 // Special Notes :
1526 // Scope : public
1527 // Creator : Ting Mei, SNL
1528 // Creation Date :
1529 //-----------------------------------------------------------------------------
1531 {
1532  bool bsuccess = true;
1533 
1534  stepNumber += 1;
1535  morEvalFailures_.push_back(stepNumber);
1538 
1539  return bsuccess;
1540 }
1541 
1542 //-----------------------------------------------------------------------------
1543 // Function : MOR::finish
1544 // Purpose :
1545 // Special Notes :
1546 // Scope : public
1547 // Creator : Ting Mei, SNL
1548 // Creation Date :
1549 //-----------------------------------------------------------------------------
1551 {
1552  bool bsuccess = true;
1553 
1554 #ifdef Xyce_DEBUG_ANALYSIS
1555  Xyce::dout() << "Calling MOR::finish() outputs!" << std::endl;
1556 #endif
1557 
1558  if (!(morEvalFailures_.empty()))
1559  {
1560  bsuccess = false;
1561  }
1562 
1563  return bsuccess;
1564 }
1565 
1566 
1567 //-----------------------------------------------------------------------------
1568 // Function : MOR::handlePredictor
1569 // Purpose :
1570 // Special Notes :
1571 // Scope : private
1572 // Creator : Eric Keiter, SNL
1573 // Creation Date : 06/24/2013
1574 //-----------------------------------------------------------------------------
1576 {
1580 
1581  // In case this is the upper level of a 2-level sim, tell the
1582  // inner solve to do its prediction:
1583  loader_.startTimeStep ();
1584 
1585  return true;
1586 }
1587 
1588 //-----------------------------------------------------------------------------
1589 // Function : MOR::updateCurrentFreq_(int stepNumber )
1590 // Purpose :
1591 // Special Notes : Used for MOR analysis classes.
1592 // Scope : public
1593 // Creator : Ting Mei, SNL.
1594 // Creation Date :
1595 //-----------------------------------------------------------------------------
1596 bool MOR::updateCurrentFreq_(int stepNumber)
1597 {
1598  double fStart = tiaParams_.morCompFStart;
1599 
1600  if (tiaParams_.type=="LIN")
1601  {
1602  currentFreq_ = fStart + static_cast<double>(stepNumber)*fStep_;
1603  }
1604  else if(tiaParams_.type=="DEC" || tiaParams_.type=="OCT")
1605  {
1606  currentFreq_ = fStart*pow(stepMult_, static_cast<double>(stepNumber) );
1607  }
1608  else
1609  {
1610  Report::DevelFatal().in("MOR::updateCurrentFreq_") << "Unsupported STEP type";
1611  }
1612 
1613  return true;
1614 }
1615 
1616 //-----------------------------------------------------------------------------
1617 // Function : MOR::setupSweepParam_
1618 // Purpose : Processes sweep parameters.
1619 // Special Notes : Used for MOR analysis classes.
1620 // Scope : public
1621 // Creator : Ting Mei, SNL.
1622 // Creation Date :
1623 //-----------------------------------------------------------------------------
1625 {
1626  int fCount = 0;
1627  double fStart = tiaParams_.morCompFStart;
1628  double fStop = tiaParams_.morCompFStop;
1629 
1630 #ifdef Xyce_DEBUG_ANALYSIS
1631  if (analysisManager_.getDebugLevel() > 0)
1632  {
1633  Xyce::dout() << std::endl << std::endl;
1634  Xyce::dout() << section_divider << std::endl;
1635  Xyce::dout() << "MOR::setupSweepParam_" << std::endl;
1636  }
1637 #endif
1638 
1639  if (tiaParams_.morCompType=="LIN")
1640  {
1641  if ( tiaParams_.morCompNP == 1)
1642  fStep_ = 0;
1643  else
1644  fStep_ = (fStop - fStart)/(tiaParams_.morCompNP - 1);
1645 
1646  fCount = tiaParams_.morCompNP;
1647 #ifdef Xyce_DEBUG_ANALYSIS
1648  if (analysisManager_.getDebugLevel() > 0)
1649  {
1650  Xyce::dout() << "fStep = " << fStep_ << std::endl;
1651  }
1652 #endif
1653  }
1654  else if(tiaParams_.morCompType=="DEC")
1655  {
1656  stepMult_ = pow(10,(1/(double)tiaParams_.morCompNP));
1657  fCount = (int)(floor(fabs(log10(fStart) - log10(fStop)) * tiaParams_.morCompNP + 1));
1658 #ifdef Xyce_DEBUG_ANALYSIS
1659  if (analysisManager_.getDebugLevel() > 0)
1660  {
1661  Xyce::dout() << "stepMult_ = " << stepMult_ << std::endl;
1662  }
1663 #endif
1664  }
1665  else if(tiaParams_.morCompType=="OCT")
1666  {
1667  stepMult_ = pow(2,1/(double)(tiaParams_.morCompNP));
1668 
1669  // changed to remove dependence on "log2" function, which apparently
1670  // doesn't exist in the math libraries of FreeBSD or the mingw
1671  // cross-compilation suite. Log_2(x)=log_e(x)/log_e(2.0)
1672  double ln2=log(2.0);
1673  fCount = floor(fabs(log(fStart) - log(fStop))/ln2 * tiaParams_.morCompNP + 1);
1674 #ifdef Xyce_DEBUG_ANALYSIS
1675  if (analysisManager_.getDebugLevel() > 0)
1676  {
1677  Xyce::dout() << "stepMult_ = " << stepMult_ << std::endl;
1678  }
1679 #endif
1680  }
1681  else
1682  {
1683  Report::DevelFatal().in("MOR::setupSweepParam_") << "Unsupported type";
1684  }
1685 
1686  // At this point, pinterval equals the total number of steps
1687  // for the step loop.
1688  return fCount;
1689 }
1690 
1691 } // namespace Analysis
1692 } // namespace Xyce