Xyce  6.1
N_NLS_Sensitivity.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-2015 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_NLS_Sensitivity.C,v $
27 //
28 // Purpose : Body for the sensitivity class.
29 //
30 // Special Notes :
31 //
32 // Creator : Eric R. Keiter, SNL, Parallel Computational Sciences
33 //
34 // Creation Date : 10/30/02
35 //
36 // Revision Information:
37 // ---------------------
38 //
39 // Revision Number: $Revision: 1.125.2.1 $
40 //
41 // Revision Date : $Date $
42 //
43 // Current Owner : $Author: tvrusso $
44 //-------------------------------------------------------------------------
45 
46 #include <Xyce_config.h>
47 
48 
49 // ---------- Standard Includes ----------
50 
51 #include <algorithm>
52 #include <sstream>
53 #include <stdexcept>
54 
55 // ---------- Xyce Includes ----------
56 
57 #include <N_ANP_AnalysisManager.h>
58 #include <N_ERH_ErrorMgr.h>
59 #include <N_LAS_Builder.h>
60 #include <N_LAS_Matrix.h>
61 #include <N_LAS_Problem.h>
62 #include <N_LAS_Solver.h>
63 #include <N_LAS_System.h>
64 #include <N_LAS_Vector.h>
66 #include <N_NLS_Manager.h>
67 #include <N_NLS_Sensitivity.h>
68 #include <N_PDS_Comm.h>
69 #include <N_PDS_MPI.h>
70 #include <N_PDS_Manager.h>
71 #include <N_PDS_Serial.h>
72 #include <N_TIA_DataStore.h>
73 #include <N_TOP_Topology.h>
74 #include <N_UTL_Algorithm.h>
75 #include <N_UTL_Diagnostic.h>
76 #include <N_UTL_Expression.h>
77 #include <N_UTL_ExtendedString.h>
78 #include <N_UTL_FeatureTest.h>
79 #include <N_UTL_OptionBlock.h>
80 
81 // ---------- Static Declarations ----------
82 
83 namespace Xyce {
84 namespace Nonlinear {
85 
86 //-----------------------------------------------------------------------------
87 // Function : Sensitivity::Sensitivity
88 // Purpose : constructor
89 // Special Notes :
90 // Scope : public
91 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
92 // Creation Date : 10/30/02
93 //-----------------------------------------------------------------------------
95  NonLinearSolver & nls,
96  Topo::Topology & topTmp,
97  const IO::CmdParse & cp)
98  : NonLinearSolver(cp),
99  debugLevel_(1),
100  solutionSize_(0),
101  solveDirectFlag_(false),
102  solveAdjointFlag_(true),
103  outputScaledFlag_(false),
104  outputUnscaledFlag_(true),
105  maxParamStringSize_(0),
106  stdOutputFlag_(true),
107  fileOutputFlag_(false),
108  dakotaFileOutputFlag_(false),
109  forceFD_(false),
110  numSolves_(0),
111  difference(SENS_FWD),
112  objFuncGiven_(false),
113  objFuncGIDsetup_(false),
114  expNumVars_(0),
115  expVal_(0.0),
116  objFuncString_(""),
117  curValue_(0.0),
118  objFuncEval_(0.0),
119  dOdp_(0.0),
120  sqrtEta_(1.0e-8),
121  sqrtEtaGiven_(false),
122  reuseFactors_(true),
123  dOdXVectorPtr_(0),
124  lambdaVectorPtr_(0),
125  savedRHSVectorPtr_(0),
126  savedNewtonVectorPtr_(0),
127 
128  origFVectorPtr_(0),
129  pertFVectorPtr_(0),
130  origQVectorPtr_(0),
131  pertQVectorPtr_(0),
132  origBVectorPtr_(0),
133  pertBVectorPtr_(0),
134 
135  nls_(nls),
136  top_(topTmp),
137  expPtr_(0),
138  numSensParams_(0)
139 {
140  // if the base nonlinear solver class had a copy constructor, I could use
141  // that here... maybe I'll set that up later. ERK 11/15/02.
146 
152 
153  dOdXVectorPtr_ = lasSysPtr_->builder().createVector();
154  savedRHSVectorPtr_ = lasSysPtr_->builder().createVector();
155  savedNewtonVectorPtr_ = lasSysPtr_->builder().createVector();
156 
157  origFVectorPtr_ = lasSysPtr_->builder().createVector();
158  pertFVectorPtr_ = lasSysPtr_->builder().createVector();
159 
160  origQVectorPtr_ = lasSysPtr_->builder().createVector();
161  pertQVectorPtr_ = lasSysPtr_->builder().createVector();
162 
163  origBVectorPtr_ = lasSysPtr_->builder().createVector();
164  pertBVectorPtr_ = lasSysPtr_->builder().createVector();
165 
166  lambdaVectorPtr_ = lasSysPtr_->builder().createVector();
167 
168  solutionSize_ = lasSysPtr_->getSolutionSize();
169 }
170 
171 //-----------------------------------------------------------------------------
172 // Function : Sensitivity::~Sensitivity
173 // Purpose : destructor
174 // Special Notes :
175 // Scope : public
176 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
177 // Creation Date : 10/30/02
178 //-----------------------------------------------------------------------------
180 {
181  delete dOdXVectorPtr_;
182  dOdXVectorPtr_ = 0;
183 
184  delete savedRHSVectorPtr_;
185  savedRHSVectorPtr_ = 0;
186 
187  delete savedNewtonVectorPtr_;
189 
190  delete origFVectorPtr_;
191  origFVectorPtr_ = 0;
192 
193  delete pertFVectorPtr_;
194  pertFVectorPtr_ = 0;
195 
196  delete origQVectorPtr_;
197  origQVectorPtr_ = 0;
198 
199  delete pertQVectorPtr_;
200  pertQVectorPtr_ = 0;
201 
202 
203  delete origBVectorPtr_;
204  origBVectorPtr_ = 0;
205 
206  delete pertBVectorPtr_;
207  pertBVectorPtr_ = 0;
208 
209  delete lambdaVectorPtr_;
210  lambdaVectorPtr_ = 0;
211 
212  if (expPtr_)
213  {
214  delete expPtr_;
215  expPtr_ = 0;
216  }
217 
218  // For all the stuff that is to be deleted in the nonlinear solver
219  // base class destructor, just set those pointers to zero because
220  // they will have been deleted already.
221  //
222  // This is the consequence of having the sensitivity class derived
223  // off of the nonlinear solver base class, but having it use all
224  // the same linear solver objects, etc., as the nonlinear solver used
225  // to solve the problem.
226  NewtonVectorPtr_ = 0;
227  gradVectorPtr_ = 0;
228  solWtVectorPtr_ = 0;
230  lasSolverPtr_ = 0;
231 }
232 
233 
234 
235 //-----------------------------------------------------------------------------
236 // Function : Sensitivity::stdOutput
237 // Purpose :
238 // Special Notes :
239 // Scope : public
240 // Creator : Eric Keiter, SNL
241 // Creation Date : 2/18/2014
242 //-----------------------------------------------------------------------------
244  std::string idString,
245  std::vector<double> & paramVals,
246  std::vector<double> & sensitivities,
247  std::vector<double> & scaled_sensitivities)
248 {
249  // Send the sensitivity information to the screen:
250 #ifdef Xyce_PARALLEL_MPI
251  N_PDS_Comm *pdsCommPtr = pdsMgrPtr_->getPDSComm();
252  int myPID = pdsCommPtr->procID();
253  if (myPID==0)
254 #endif
255  {
256  Xyce::dout() << "\n"<<idString << " Sensitivities of objective function:"
257  << objFuncString_ << " = " << objFuncEval_ << std::endl;
258 
259  Xyce::dout() << std::setw(maxParamStringSize_)<<"Name";
260  Xyce::dout() << "\t"<<std::setw(13)<<"Value";
261  Xyce::dout() << "\t"<<std::setw(13)<<"Sensitivity";
262  Xyce::dout() << "\t"<<std::setw(13)<<"Normalized"<<std::endl;
263 
264  for (int iparam=0; iparam< numSensParams_; ++iparam)
265  {
266  Xyce::dout() << std::setw(maxParamStringSize_)<<paramNameVec_[iparam];
267 
268  Xyce::dout() << "\t" << std::setw(13)<< std::scientific<< std::setprecision(4)
269  << paramVals[iparam];
270 
271  Xyce::dout() << "\t" << std::setw(13)<< std::scientific<< std::setprecision(4)
272  << sensitivities[iparam];
273 
274  Xyce::dout() << "\t" << std::setw(13)<< std::scientific<< std::setprecision(4)
275  << scaled_sensitivities[iparam] << std::endl;
276  }
277  }
278 #ifdef Xyce_PARALLEL_MPI
279  pdsCommPtr->barrier();
280 #endif
281 }
282 
283 //-----------------------------------------------------------------------------
284 // Function : Sensitivity::fileOutput
285 // Purpose : Dump sensitivity information out to a file.
286 // Special Notes :
287 // Scope : public
288 // Creator : Eric Keiter, SNL
289 // Creation Date : 2/18/2014
290 //-----------------------------------------------------------------------------
292  std::string idString,
293  std::vector<double> & paramVals,
294  std::vector<double> & sensitivities,
295  std::vector<double> & scaled_sensitivities)
296 {
297 
298 #ifdef Xyce_PARALLEL_MPI
299  N_PDS_Comm *pdsCommPtr = pdsMgrPtr_->getPDSComm();
300  int myPID = pdsCommPtr->procID();
301  if (myPID==0)
302 #endif
303  {
304  std::ostringstream numSolvesOStr;
305  numSolvesOStr << numSolves_;
306  std::string dodpFileName = netlistFilename_ + numSolvesOStr.str() + "_dodp" + idString +".txt";
307  FILE *fp = fopen(dodpFileName.c_str(),"w");
308  for (int iparam=0;iparam< numSensParams_; ++iparam)
309  {
310  fprintf(fp,"\t%16.8e\n", sensitivities[iparam]);
311  }
312  fclose(fp);
313  }
314 #ifdef Xyce_PARALLEL_MPI
315  pdsCommPtr->barrier();
316 #endif
317 }
318 
319 //-----------------------------------------------------------------------------
320 // Function : Sensitivity::dakOutput
321 // Purpose : Dump sensitivity information out to a dakota-style file.
322 // Special Notes :
323 // Scope : public
324 // Creator : Eric Keiter, SNL
325 // Creation Date : 2/19/2014
326 //-----------------------------------------------------------------------------
328  std::string idString,
329  std::vector<double> & paramVals,
330  std::vector<double> & sensitivities,
331  std::vector<double> & scaled_sensitivities)
332 {
333 #ifdef Xyce_PARALLEL_MPI
334  N_PDS_Comm *pdsCommPtr = pdsMgrPtr_->getPDSComm();
335  int myPID = pdsCommPtr->procID();
336  if (myPID==0)
337 #endif
338  {
339  // write a file format that can be used by dakota :
340  // Note that currently, this will simply overwrite the same
341  // file every time this function is called.
342  std::string dakotaFileName = netlistFilename_ + "_dodp" + idString + "_all.txt";
343  FILE *fp2 = fopen(dakotaFileName.c_str(),"w");
344  fprintf(fp2,"%16.8e", objFuncEval_ );
345  fprintf(fp2,"%s","\n[\n");
346  for (int iparam=0;iparam< numSensParams_; ++iparam)
347  {
348  fprintf(fp2,"\t%16.8e\n", sensitivities[iparam]);
349  }
350  fprintf(fp2,"%s","]\n");
351  fclose(fp2);
352  }
353 #ifdef Xyce_PARALLEL_MPI
354  pdsCommPtr->barrier();
355 #endif
356 }
357 
358 
359 //-----------------------------------------------------------------------------
360 // Function : Sensitivity::icSensitivity
361 // Purpose : This function is called when there is a NOOP or UIC. The
362 // dqdp vector needs to be set up for the history to be correct.
363 // Special Notes :
364 // Scope : public
365 // Creator : Eric Keiter, SNL
366 // Creation Date :
367 //-----------------------------------------------------------------------------
369  std::vector<double> & objectiveVec,
370  std::vector<double> & dOdpVec,
371  std::vector<double> & dOdpAdjVec,
372  std::vector<double> & scaled_dOdpVec,
373  std::vector<double> & scaled_dOdpAdjVec)
374 {
375  if (!solveDirectFlag_ && !solveAdjointFlag_) return 1;
376 
377  TimeIntg::DataStore & ds = *dsPtr_;
378 
379  ds.dOdpVec_.clear();
380  ds.dOdpAdjVec_.clear();
381 
382  ds.scaled_dOdpVec_.clear();
383  ds.scaled_dOdpAdjVec_.clear();
384 
385  // first get the derivatives of the RHS vector w.r.t. the
386  // user-specified optimization parameters.
388 
390 
391  objectiveVec.clear();
392  objectiveVec.push_back(objFuncEval_);
393  ds.objectiveVec_.clear();
394  ds.objectiveVec_.push_back(objFuncEval_);
395 
396  if (solveDirectFlag_)
397  {
398  ds.dOdpVec_.resize(numSensParams_,0.0);
399  ds.scaled_dOdpVec_.resize(numSensParams_,0.0);
400 
402  {
403  dOdpVec = ds.dOdpVec_;
404  }
405 
406  if (outputScaledFlag_)
407  {
408  scaled_dOdpVec = ds.scaled_dOdpVec_;
409  }
410 
411  if (stdOutputFlag_)
412  {
413  stdOutput(std::string("Direct"), ds.paramOrigVals_, ds.dOdpVec_, ds.scaled_dOdpVec_);
414  }
415  }
416 
417  if (solveAdjointFlag_)
418  {
419  ds.dOdpAdjVec_.resize(numSensParams_,0.0);
420  ds.scaled_dOdpAdjVec_.resize(numSensParams_,0.0);
421 
423  {
424  dOdpAdjVec = ds.dOdpAdjVec_;
425  }
426 
427  if (outputScaledFlag_)
428  {
429  scaled_dOdpAdjVec = ds.scaled_dOdpAdjVec_;
430  }
431  if (stdOutputFlag_)
432  {
433  stdOutput(std::string("Adjoint"), ds.paramOrigVals_, ds.dOdpAdjVec_, ds.scaled_dOdpAdjVec_);
434  }
435  }
436 
437  return true;
438 }
439 
440 //-----------------------------------------------------------------------------
441 // Function : Sensitivity::solve
442 // Purpose :
443 // Special Notes :
444 // Scope : public
445 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
446 // Creation Date : 11/21/02
447 //-----------------------------------------------------------------------------
449  std::vector<double> & objectiveVec,
450  std::vector<double> & dOdpVec,
451  std::vector<double> & dOdpAdjVec,
452  std::vector<double> & scaled_dOdpVec,
453  std::vector<double> & scaled_dOdpAdjVec)
454 {
455  Stats::StatTop _solveStat("Sensistivity Solve");
456  Stats::TimeBlock _solveTimer(_solveStat);
457 
458  if (!solveDirectFlag_ && !solveAdjointFlag_) return 1;
459 
460  TimeIntg::DataStore & ds = *dsPtr_;
461 
462  ds.dOdpVec_.clear();
463  ds.dOdpAdjVec_.clear();
464 
465  ds.scaled_dOdpVec_.clear();
466  ds.scaled_dOdpAdjVec_.clear();
467 
468  // It may now be neccessary to re-load the jacobian and rhs vectors.
469  // It is necccessary, for example, if the two-level Newton solver is the
470  // solver being used.
472 
473  // first get the derivatives of the RHS vector w.r.t. the
474  // user-specified optimization parameters.
476 
478 
479  objectiveVec.clear();
480  objectiveVec.push_back(objFuncEval_);
481  ds.objectiveVec_.clear();
482  ds.objectiveVec_.push_back(objFuncEval_);
483 
484  if (solveDirectFlag_)
485  {
486  solveDirect ();
487 
489  {
490  dOdpVec = ds.dOdpVec_;
491  }
492 
493  if (outputScaledFlag_)
494  {
495  scaled_dOdpVec = ds.scaled_dOdpVec_;
496  }
497  }
498 
499  if (solveAdjointFlag_)
500  {
501  solveAdjoint ();
503  {
504  dOdpAdjVec = ds.dOdpAdjVec_;
505  }
506 
507  if (outputScaledFlag_)
508  {
509  scaled_dOdpAdjVec = ds.scaled_dOdpAdjVec_;
510  }
511  }
512 
513  numSolves_++;
514 
515  return 1;
516 }
517 
518 //-----------------------------------------------------------------------------
519 // Function : Sensitivity::solveDirect
520 //
521 // Purpose : This function calculates the direct sensitivities for
522 // the user specified parameters.
523 //
524 // The ultimate goal of this function is to obtain dO/dp,
525 // where O is the objective function, and p is a
526 // user-defined optimization parameter.
527 //
528 // This is a bit confusing because the DAKOTA folks use
529 // a different naming convention than I have tended to
530 // use. In Dakota's documentation, dO/dp would be referred
531 // to as df/du. In Xyce, f is already considered to be the
532 // right-hand-side residual vector. For clarity, this is
533 // the key between my notation and DAKOTA's:
534 // DAK Xyce
535 // f O
536 // y x
537 // u p
538 // c f
539 //
540 // To obtain dOdp, the device manager is first called, and
541 // told to calculate dfdp, which is the derivative of the
542 // residual vector w.r.t. user-defined params. It does
543 // this by numerical differentiation. Then, after that,
544 // this function solves the equation:
545 //
546 // J dx/dp = df/dp -> dx/dp = J^-1 df/dp
547 //
548 // for each p.
549 //
550 // J is the jacobian matrix, so J=df/dx. (dc/dy)
551 //
552 // After performing these linear solves, dO/dp is to be
553 // obtained using the chain rule by:
554 //
555 // dO/dp = - dO/dx * dx/dp + dO/dp
556 //
557 // The O in the dO/dp on the left side of this equation
558 // should have a hat over it "^", to indicate that it is
559 // different than the O on the right hand side.
560 //
561 // Note, this method is best if you have lots of objective
562 // functions, and a small number of parameters. For adjoint
563 // calculations it is the other way around.
564 //
565 // 11/19/02.
566 // It is assumed (for now) that dO/dp on the right hand side
567 // is zero, i.e., there is no direct
568 // dependence on p by O. So, for example, if a user
569 // defined parameter to be used is the length of a MOSFET,
570 // the MOSFET length will NOT appear in the analytical
571 // expression for O.
572 //
573 //
574 // Special Notes :
575 // Scope : public
576 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
577 // Creation Date : 10/31/02
578 //-----------------------------------------------------------------------------
580 {
581  Stats::StatTop _solveDirectStat("Solve Direct");
582  Stats::TimeBlock _solveDirectTimer(_solveDirectStat);
583 
584  if (DEBUG_NONLINEAR && isActive(Diag::SENS_NONLINEAR_SOLVER))
585  Xyce::dout() << std::endl
586  << "In Sensitivity::solveDirect" << std::endl;
587 
588  TimeIntg::DataStore & ds = *dsPtr_;
589 
590  int iparam;
591  std::vector<Linear::Vector *> & dXdpPtrVector_ = ds.nextDXdpPtrVector;
592  std::vector<Linear::Vector *> & currdXdpPtrVector_ = ds.currDXdpPtrVector;
593  std::vector<Linear::Vector *> & sensRHSPtrVector = ds.sensRHSPtrVector;
594 
595  std::vector<Linear::Vector *> & DQdxDXdpPtrVector_ = ds.nextDQdxDXdpPtrVector;
596 
597  // first save a copy of the rhs and newton vectors, in case we want them later.
598  savedRHSVectorPtr_->update(1.0, *(rhsVectorPtr_), 0.0);
599  savedNewtonVectorPtr_->update(1.0, *(NewtonVectorPtr_), 0.0);
600 
601  // via the loader, setup all the sensitivity residuals.
603 
604  if (DEBUG_NONLINEAR && isActive(Diag::SENS_NONLINEAR_SOLVER))
605  {
606  std::string matrixFile = netlistFilename_ + "_directMatrix.txt";
607  jacobianMatrixPtr_->writeToFile(const_cast<char *>(matrixFile.c_str()));
608  }
609 
610  // Now solve the series of linear systems to get dXdp.
611  for (iparam=0; iparam< numSensParams_; ++iparam)
612  {
613  // copy the sensitivity residual into the rhs vector location,
614  // as that is the RHS vector that the linear solver expects.
615  rhsVectorPtr_->update(1.0, *(sensRHSPtrVector[iparam]), 0.0);
616 
618 
619  // copy the result of the linear solve into the dxdp data structure.
620  (dXdpPtrVector_[iparam])->update(1.0, *(NewtonVectorPtr_), 0.0);
621 
622  // do debug output.
623  if (DEBUG_NONLINEAR && isActive(Diag::SENS_NONLINEAR_SOLVER))
624  {
625  Xyce::dout() << "iparam="<<iparam << "\t" << paramNameVec_[iparam] <<std::endl;
626  for (int k = 0; k < solutionSize_; ++k)
627  {
628  Xyce::dout() << "dXdp[" << std::setw(3) << k << "] = "<< std::setw(15)<< std::scientific
629  << std::setprecision(8)<< (*(dXdpPtrVector_[iparam]))[k]
630  <<std::endl;
631  }
632 
633  std::ostringstream filename;
634  filename << netlistFilename_ << "_dxdp";
635  filename << std::setw(3) << std::setfill('0') << iparam;
636  filename << ".txt";
637  dXdpPtrVector_[iparam]->writeToFile(const_cast<char *>(filename.str().c_str()));
638  }
639 
640  // Now store the DQdx*dXdp matvec
641  Linear::Matrix & dQdx = *(ds.dQdxMatrixPtr);
642  bool Transpose = false;
643  (DQdxDXdpPtrVector_[iparam])->putScalar(0.0);
644  dQdx.matvec( Transpose , *(dXdpPtrVector_[iparam]), *(DQdxDXdpPtrVector_[iparam]) );
645 
646  }// end of param for loop
647 
648  // Now update the time derivative of the matvec (this is needed for trap).
650 
651  // Restore the RHS and Newton vectors.
652  rhsVectorPtr_->update(1.0, *(savedRHSVectorPtr_),0.0);
653  NewtonVectorPtr_->update(1.0, *(savedNewtonVectorPtr_),0.0);
654 
655  // Now get the final dOdp's (one for each param).
656  for (iparam=0; iparam< numSensParams_; ++iparam)
657  {
658  double tmp = dOdXVectorPtr_->dotProduct( (*(dXdpPtrVector_[iparam])) );
659  tmp += dOdp_;
660 
661  ds.dOdpVec_.push_back(tmp);
662 
663  // get scaled value. dO/dp*(p/100)
664  double normalize = ds.paramOrigVals_[iparam]/100.0;
665  tmp *= normalize;
666  ds.scaled_dOdpVec_.push_back(tmp);
667  }
668 
669  if (stdOutputFlag_)
670  {
671  stdOutput(std::string("Direct"), ds.paramOrigVals_, ds.dOdpVec_, ds.scaled_dOdpVec_);
672  }
673 
674  if (fileOutputFlag_)
675  {
676  fileOutput(std::string("Direct"), ds.paramOrigVals_, ds.dOdpVec_, ds.scaled_dOdpVec_);
677  }
678 
680  {
681  dakOutput(std::string("Direct"), ds.paramOrigVals_, ds.dOdpVec_, ds.scaled_dOdpVec_);
682  }
683 
684  return 1;
685 }
686 
687 //-----------------------------------------------------------------------------
688 // Function : Sensitivity::calcObjFuncDerivs
689 //
690 // Purpose : This function assumes an objective function, O, and
691 // uses it to calculate other stuff, including dO'/du,
692 // dO/dy, dO/du.
693 //
694 // The objective function simply assumes that you are
695 // trying to match a solution variable with a
696 // user-specified number. (objective=1)
697 //
698 // Or that you are trying to match your final solution
699 // with a specified solution. (objective=2)
700 //
701 // Special Notes : This is mostly for testing purposes. Usually, the
702 // objective function would be set and managed from
703 // the DAKOTA side.
704 //
705 // Scope : public
706 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
707 // Creation Date : 11/15/02
708 //-----------------------------------------------------------------------------
710 {
711  bool bsuccess = true;
712  int i;
713 
714  dOdXVectorPtr_->putScalar(0.0);
715 
716  // first check if the user specified a function via the expressions
717  // class:
718  int found(0);
719  int found2(0);
720  bool foundLocal(false);
721  bool foundLocal2(false);
722 
723  N_PDS_Comm & comm = *(pdsMgrPtr_->getPDSComm());
724  int myPID = comm.procID();
725 
726  if (objFuncGiven_)
727  {
728  if (!objFuncGIDsetup_)
729  {
730  // set up the gid's:
731  expVarGIDs_.resize( expNumVars_, -1 );
732  for (i = 0; i < expNumVars_; ++i)
733  {
734  std::vector<int> svGIDList1, dummyList;
735  char type1;
736  foundLocal = top_.getNodeSVarGIDs(NodeID(expVarNames_[i], Xyce::_VNODE), svGIDList1, dummyList, type1);
737  found = static_cast<int>(foundLocal);
738  Xyce::Parallel::AllReduce(comm.comm(), MPI_LOR, &found, 1);
739 
740  foundLocal2 = false;
741  if (!found)// if looking for this as a voltage node failed, try a "device" (i.e. current) node.
742  {
743  foundLocal2 = top_.getNodeSVarGIDs(NodeID(expVarNames_[i], Xyce::_DNODE), svGIDList1, dummyList, type1);
744  }
745  found2 = static_cast<int>(foundLocal2);
746  Xyce::Parallel::AllReduce(comm.comm(), MPI_LOR, &found2, 1);
747 
748  if (!found && !found2)
749  {
750  static std::string tmp = "objective function variable not found!\n";
751  N_ERH_ErrorMgr::report( N_ERH_ErrorMgr::USR_FATAL, tmp);
752  }
753 
754  if (found || found2)
755  {
756  int tmpGID=-1;
757  if(svGIDList1.size()==1)
758  {
759  tmpGID = svGIDList1.front();
760  }
761  expVarGIDs_[i] = tmpGID;
762  }
763  }
764  objFuncGIDsetup_ = true;
765  }
766 
767  // obtain the expression variable values. It will only grab this value if it is owned on this processor.
768  expVarVals_.resize (expNumVars_, 0.0);
769  expVarDerivs_.resize (expNumVars_, 0.0);
770 
771  comm.barrier();
772  for (i = 0; i < expNumVars_; ++i)
773  {
774  int tmpGID=expVarGIDs_[i];
775  double tmpVal=0.0;
776  int root=-1;
777  if (tmpGID >= 0)
778  {
779  tmpVal = (*nextSolVectorPtrPtr_)->getElementByGlobalIndex(tmpGID, 0);
780  root = myPID;
781  }
782  Xyce::Parallel::AllReduce(comm.comm(), MPI_MAX, &root, 1);
783 
784  comm.bcast( &tmpVal, 1, root );
785 
786  expVarVals_[i] = tmpVal;
787  }
788  comm.barrier();
789 
790  //get expression value and partial derivatives
791  expPtr_->evaluate( expVal_, expVarDerivs_, expVarVals_ );
793  dOdXVectorPtr_->putScalar(0.0);
794  for (i=0;i<expNumVars_;++i)
795  {
796  int tmpGID = expVarGIDs_[i];
797  double tmpDODX = expVarDerivs_[i];
798 
799  if (DEBUG_NONLINEAR && isActive(Diag::SENS_NONLINEAR_SOLVER))
800  {
801  Xyce::dout() << "i="<<i<<" gid = " << tmpGID << " dodx = "<< tmpDODX << std::endl;
802  }
803 
804  if (tmpGID >= 0)
805  {
806  dOdXVectorPtr_->setElementByGlobalIndex(tmpGID, tmpDODX, 0);
807  }
808  }
809 
810  // Assuming this is zero:
811  dOdp_ = 0.0;
812  }
813  else // give a warning.
814  {
815  Report::UserWarning0() << "Objective function was not specified";
816  }
817 
818  dOdXVectorPtr_->fillComplete();
819 
820  if (DEBUG_NONLINEAR && isActive(Diag::SENS_NONLINEAR_SOLVER))
821  {
822  std::string filename = netlistFilename_ + "_dodx.txt";
823  dOdXVectorPtr_->writeToFile(const_cast<char *>(filename.c_str()));
824  }
825 
826  return bsuccess;
827 }
828 
829 //-----------------------------------------------------------------------------
830 // Function : Sensitivity::solveAdjoint
831 // Purpose : Solves first for the vector, lambda, of adjoint variables.
832 // Afterwards, it solves for dO/dp.
833 // Special Notes :
834 // Scope : public
835 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
836 // Creation Date : 11/20/02
837 //-----------------------------------------------------------------------------
839 {
840  Stats::StatTop _solveAdjointStat("Solve Adjoint");
841  Stats::TimeBlock _solveAdjointTimer(_solveAdjointStat);
842 
843  if (DEBUG_NONLINEAR && isActive(Diag::SENS_NONLINEAR_SOLVER))
844  {
845  Xyce::dout() << std::endl;
846  Xyce::dout() << "In Sensitivity::solveAdjoint" << std::endl;
847  }
848 
849  TimeIntg::DataStore & ds = *dsPtr_;
850 
851  std::vector<Linear::Vector *> & dfdpPtrVector_ = ds.nextDfdpPtrVector;
852  std::vector<Linear::Vector *> & dbdpPtrVector_ = ds.nextDbdpPtrVector;
853  std::vector<Linear::Vector *> & sensRHSPtrVector = ds.sensRHSPtrVector;
854 
855  // first save a copy of the rhs vector, in case we want it later.
856  savedRHSVectorPtr_->update(1.0, *(rhsVectorPtr_),0.0);
857  savedNewtonVectorPtr_->update(1.0, *(NewtonVectorPtr_),0.0);
858 
859  bool useTran = jacobianMatrixPtr_->useTranspose ();
860  if (DEBUG_NONLINEAR && isActive(Diag::SENS_NONLINEAR_SOLVER))
861  {
862  std::string matrixFile = netlistFilename_ + "_adjointMatrix.txt";
863  jacobianMatrixPtr_->writeToFile(const_cast<char *>(matrixFile.c_str()));
864  }
865 
866  // copy the current dOdx vector into the rhs vector data structure.
867  rhsVectorPtr_->update(1.0, *(dOdXVectorPtr_),0.0);
868 
869  int status = lasSolverPtr_->solveTranspose(reuseFactors_);
870  if (status!=0)
871  {
872  std::string msg("Sensitivity::solveAdjoint. Solver failed\n");
873  N_ERH_ErrorMgr::report (N_ERH_ErrorMgr::DEV_FATAL, msg);
874  }
875 
876  // allocate the dxdp vector for this param, and
877  // copy the resulting deltax vector into the dxdp data structure.
878  lambdaVectorPtr_->update(1.0, *(NewtonVectorPtr_),0.0);
879 
880  if (DEBUG_NONLINEAR && isActive(Diag::SENS_NONLINEAR_SOLVER))
881  {
882  std::string filename = netlistFilename_ + "_lambda.txt";
883  lambdaVectorPtr_->writeToFile(const_cast<char *>(filename.c_str()));
884  }
885 
886  // Now that we have lambda, get the dOdp's by doing dot products of
887  // lambda * df/dp.
888 
889  // do the final dot products, one for each param.
890  for (int iparam=0; iparam< numSensParams_; ++iparam)
891  {
892  // assuming that this is steady-state, then we need to sum together dfdp and -dbdp.
893  Linear::Vector & RHSVec = *(sensRHSPtrVector[iparam]);
894  Linear::Vector & dfdpVec = *(dfdpPtrVector_[iparam]);
895  Linear::Vector & dbdpVec = *(dbdpPtrVector_[iparam]);
896  RHSVec.linearCombo(0.0,RHSVec,+1.0,dfdpVec);
897  RHSVec.linearCombo(1.0,RHSVec,-1.0,dbdpVec);
898 
899  double tmp = -1.0 * lambdaVectorPtr_->dotProduct(RHSVec);
900  ds.dOdpAdjVec_.push_back(tmp);
901 
902  // get scaled value. dO/dp*(p/100)
903  double normalize = ds.paramOrigVals_[iparam]/100.0;
904  tmp *= normalize;
905  ds.scaled_dOdpAdjVec_.push_back(tmp);
906  }
907 
908  // restore the useTranspose flag to the original setting. (false)
909  jacobianMatrixPtr_->setUseTranspose (useTran);
910 
911  // Restore the RHS and Newton vectors.
912  rhsVectorPtr_->update(1.0, *(savedRHSVectorPtr_),0.0);
913  NewtonVectorPtr_->update(1.0, *(savedNewtonVectorPtr_),0.0);
914 
915  // set the sensitivity information to the screen:
916  if (stdOutputFlag_)
917  {
918  stdOutput(std::string("Adjoint"), ds.paramOrigVals_, ds.dOdpAdjVec_, ds.scaled_dOdpAdjVec_);
919  }
920  if (fileOutputFlag_)
921  {
922  fileOutput(std::string("Adjoint"), ds.paramOrigVals_, ds.dOdpAdjVec_, ds.scaled_dOdpAdjVec_);
923  }
924 
926  {
927  dakOutput(std::string("Adjoint"), ds.paramOrigVals_, ds.dOdpAdjVec_, ds.scaled_dOdpAdjVec_);
928  }
929 
930  return 1;
931 }
932 
933 //-----------------------------------------------------------------------------
934 // Function : Sensitivity::loadSensitivityResiduals
935 //
936 // Purpose : This function is to be called after a calculation has
937 // converged. It computes the sensitivies of different components
938 // of the residual. Recall the DAE form of the residual:
939 //
940 // F = dq/dt + f - b
941 //
942 // This function sets up the following derivatives:
943 //
944 // df/dp, dq/dp, db/dp where p is the parameter.
945 //
946 // Which can ultimately be assembled to give dF/dp:
947 //
948 // d(dq/dp)
949 // dF/dp = ------- + df/dp - db/dp
950 // dt
951 //
952 // Special Notes :
953 // Scope : public
954 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
955 // Creation Date : 7/15/02
956 //-----------------------------------------------------------------------------
958 {
959  int iparam;
960  std::string msg;
961  Linear::System & lasSys_ = (*lasSysPtr_);
962 
963  TimeIntg::DataStore & ds = *dsPtr_;
964 
965  std::vector<Linear::Vector *> & dfdpPtrVector_ = ds.nextDfdpPtrVector;
966  std::vector<Linear::Vector *> & dqdpPtrVector_ = ds.nextDqdpPtrVector;
967  std::vector<Linear::Vector *> & dbdpPtrVector_ = ds.nextDbdpPtrVector;
968 
969  ds.paramOrigVals_.clear();
970 
971  // it is necessary to load the Jacobian here to make sure we have the most
972  // up-to-date matrix. The Jacobian is not loaded for the final
973  // evaluation of the residual in the Newton solve.
975 
976  // Loop over the vector of parameters. For each parameter, find the
977  // device entity (a model or an instance) which corresponds to it, and
978  // perform the finite difference calculation.
979  std::vector<std::string>::iterator firstParam = paramNameVec_.begin ();
980  std::vector<std::string>::iterator lastParam = paramNameVec_.end ();
981  std::vector<std::string>::iterator iterParam;
982  for ( iterParam=firstParam, iparam=0;
983  iterParam!=lastParam; ++iterParam, ++iparam )
984  {
985  std::string paramName(*iterParam);
986 
987  // get the original value of this parameter, to be used for scaling and/or
988  // numerical derivatives later.
989  double paramOrig = 0.0;
990  bool found = loaderPtr_->getParamAndReduce(paramName, paramOrig);
991  if (!found)
992  {
993  std::string msg("Sensitivity::loadSensitivityResiduals: cannot find parameter ");
994  msg += paramName;
995  N_ERH_ErrorMgr::report (N_ERH_ErrorMgr::DEV_FATAL, msg);
996  }
997  ds.paramOrigVals_.push_back(paramOrig);
998 
999  // check if derivative is available analytically. If not, take FD.
1000  bool analyticAvailable = false;
1001  if (!forceFD_)
1002  {
1003  analyticAvailable = loaderPtr_->analyticSensitivitiesAvailable (paramName);
1004  }
1005  if (analyticAvailable)
1006  {
1007  std::vector<double> dfdpVec;
1008  std::vector<double> dqdpVec;
1009  std::vector<double> dbdpVec;
1010 
1011  std::vector<int> FindicesVec;
1012  std::vector<int> QindicesVec;
1013  std::vector<int> BindicesVec;
1014 
1016  dfdpVec,dqdpVec,dbdpVec,
1017  FindicesVec, QindicesVec, BindicesVec);
1018 
1019  dfdpPtrVector_[iparam]->putScalar(0.0);
1020  dqdpPtrVector_[iparam]->putScalar(0.0);
1021  dbdpPtrVector_[iparam]->putScalar(0.0);
1022 
1023  int Fsize=FindicesVec.size();
1024  for (int i=0;i<Fsize;++i)
1025  {
1026  Linear::Vector & dfdpRef = *(dfdpPtrVector_[iparam]);
1027  dfdpRef[FindicesVec[i]] += dfdpVec[i];
1028  }
1029 
1030  int Qsize=QindicesVec.size();
1031  for (int i=0;i<Qsize;++i)
1032  {
1033  Linear::Vector & dqdpRef = *(dqdpPtrVector_[iparam]);
1034  dqdpRef[QindicesVec[i]] += dqdpVec[i];
1035  }
1036 
1037  int Bsize=BindicesVec.size();
1038  for (int i=0;i<Bsize;++i)
1039  {
1040  Linear::Vector & dbdpRef = *(dbdpPtrVector_[iparam]);
1041  dbdpRef[BindicesVec[i]] += dbdpVec[i];
1042  }
1043 
1044  dfdpPtrVector_[iparam]->fillComplete();
1045  dqdpPtrVector_[iparam]->fillComplete();
1046  dbdpPtrVector_[iparam]->fillComplete();
1047 
1048  if (DEBUG_NONLINEAR && isActive(Diag::SENS_NONLINEAR_SOLVER))
1049  {
1050  Xyce::dout() << *iterParam << ": ";
1051  Xyce::dout().setf(std::ios::scientific);
1052  Xyce::dout() << std::endl;
1053 
1054  for (int k1 = 0; k1 < solutionSize_; ++k1)
1055  {
1056  Xyce::dout()
1057  <<"dfdp["<<std::setw(3)<<k1<<"]= "<<std::setw(15)<<std::scientific<<std::setprecision(8)<<(*(dfdpPtrVector_[iparam]))[k1]
1058  <<" dqdp["<<std::setw(3)<<k1<<"]= "<<std::setw(15)<<std::scientific<<std::setprecision(8)<<(*(dqdpPtrVector_[iparam]))[k1]
1059  <<" dbdp["<<std::setw(3)<<k1<<"]= "<<std::setw(15)<<std::scientific<<std::setprecision(8)<<(*(dbdpPtrVector_[iparam]))[k1]
1060  <<std::endl;
1061  }
1062  }
1063  }
1064  else
1065  {
1066  if (DEBUG_NONLINEAR && isActive(Diag::SENS_NONLINEAR_SOLVER))
1067  {
1068  Xyce::dout() << std::endl << " Calculating numerical df/dp, dq/dp and db/dp for: ";
1069  Xyce::dout() << *iterParam << std::endl;
1070  }
1071 
1072  // save a copy of the DAE vectors
1073  origFVectorPtr_->update(1.0, *(ds.daeFVectorPtr), 0.0);
1074  origQVectorPtr_->update(1.0, *(ds.daeQVectorPtr), 0.0);
1075  origBVectorPtr_->update(1.0, *(ds.daeBVectorPtr), 0.0);
1076 
1077  // now perturb the value of this parameter.
1078  double dp = sqrtEta_ * (1.0 + fabs(paramOrig));
1079  double paramPerturbed = paramOrig;
1080 
1081  if (difference==SENS_FWD)
1082  {
1083  paramPerturbed += dp;
1084  }
1085  else if (difference==SENS_REV)
1086  {
1087  paramPerturbed -= dp;
1088  }
1089  else if (difference==SENS_CNT)
1090  {
1091  static std::string tmp = "difference=central not supported.\n";
1092  N_ERH_ErrorMgr::report( N_ERH_ErrorMgr::USR_FATAL_0, tmp);
1093  }
1094  else
1095  {
1096  static std::string tmp = "difference not recognized!\n";
1097  N_ERH_ErrorMgr::report( N_ERH_ErrorMgr::USR_FATAL_0, tmp);
1098  }
1099 
1100  if (DEBUG_NONLINEAR && isActive(Diag::SENS_NONLINEAR_SOLVER))
1101  {
1102  Xyce::dout() << std::setw(maxParamStringSize_)<< *iterParam
1103  << " dp = " << std::setw(11)<< std::scientific<< std::setprecision(4) << dp
1104  << " original value = " << std::setw(16)<< std::scientific<< std::setprecision(9) << paramOrig
1105  << " modified value = " << std::setw(16)<< std::scientific<< std::setprecision(9) << paramPerturbed
1106  <<std::endl;
1107  }
1108 
1109  loaderPtr_->setParam (paramName, paramPerturbed);
1110 
1111  // Now that the parameter has been perturbed,
1112  // calculate the numerical derivative.
1113 
1114  // Load F,Q and B.
1115  loaderPtr_->loadRHS();
1116 
1117  // save the perturbed DAE vectors
1118  pertFVectorPtr_->update(1.0, *(ds.daeFVectorPtr), 0.0);
1119  pertQVectorPtr_->update(1.0, *(ds.daeQVectorPtr), 0.0);
1120  pertBVectorPtr_->update(1.0, *(ds.daeBVectorPtr), 0.0);
1121 
1122  // calculate the df/dp vector.
1123  double rdp=1/dp;
1124  dfdpPtrVector_[iparam]->putScalar(0.0);
1125  dfdpPtrVector_[iparam]->addVec (+1.0, *(pertFVectorPtr_)); //+Fperturb
1126  dfdpPtrVector_[iparam]->addVec (-1.0, *(origFVectorPtr_)); //-Forig
1127  dfdpPtrVector_[iparam]->scale(rdp);
1128 
1129  // calculate the dq/dp vector.
1130  dqdpPtrVector_[iparam]->putScalar(0.0);
1131  dqdpPtrVector_[iparam]->addVec (+1.0, *(pertQVectorPtr_)); //+Fperturb
1132  dqdpPtrVector_[iparam]->addVec (-1.0, *(origQVectorPtr_)); //-Forig
1133  dqdpPtrVector_[iparam]->scale(rdp);
1134 
1135  // calculate the db/dp vector.
1136  dbdpPtrVector_[iparam]->putScalar(0.0);
1137  dbdpPtrVector_[iparam]->addVec (+1.0, *(pertBVectorPtr_)); //+Fperturb
1138  dbdpPtrVector_[iparam]->addVec (-1.0, *(origBVectorPtr_)); //-Forig
1139  dbdpPtrVector_[iparam]->scale(rdp);
1140 
1141  if (DEBUG_NONLINEAR && isActive(Diag::SENS_NONLINEAR_SOLVER))
1142  {
1143  Xyce::dout() << *iterParam << ": ";
1144  Xyce::dout().width(15); Xyce::dout().precision(7); Xyce::dout().setf(std::ios::scientific);
1145  Xyce::dout() << "deviceSens_dp = " << dp << std::endl;
1146 
1147  for (int k1 = 0; k1 < solutionSize_; ++k1)
1148  {
1149 
1150  Xyce::dout()
1151  <<"fpert["<<std::setw(3)<<k1<<"]= "<<std::setw(15)<<std::scientific<<std::setprecision(8)<<(*(pertFVectorPtr_))[k1]
1152  <<" forig["<<std::setw(3)<<k1<<"]= "<<std::setw(15)<<std::scientific<<std::setprecision(8)<<(*(origFVectorPtr_))[k1]
1153  <<" dfdp ["<<std::setw(3)<<k1<<"]= "<<std::setw(15)<<std::scientific<<std::setprecision(8)<<(*(dfdpPtrVector_[iparam]))[k1]
1154  <<std::endl;
1155  }
1156 
1157  Xyce::dout() << std::endl;
1158  for (int k1 = 0; k1 < solutionSize_; ++k1)
1159  {
1160  Xyce::dout()
1161  <<"qpert["<<std::setw(3)<<k1<<"]= "<<std::setw(15)<<std::scientific<<std::setprecision(8)<<(*(pertQVectorPtr_))[k1]
1162  <<" qorig["<<std::setw(3)<<k1<<"]= "<<std::setw(15)<<std::scientific<<std::setprecision(8)<<(*(origQVectorPtr_))[k1]
1163  <<" dqdp ["<<std::setw(3)<<k1<<"]= "<<std::setw(15)<<std::scientific<<std::setprecision(8)<<(*(dqdpPtrVector_[iparam]))[k1]
1164  <<std::endl;
1165  }
1166 
1167  Xyce::dout() << std::endl ;
1168  for (int k1 = 0; k1 < solutionSize_; ++k1)
1169  {
1170  Xyce::dout()
1171  <<"bpert["<<std::setw(3)<<k1<<"]= "<<std::setw(15)<<std::scientific<<std::setprecision(8)<<(*(pertBVectorPtr_))[k1]
1172  <<" borig["<<std::setw(3)<<k1<<"]= "<<std::setw(15)<<std::scientific<<std::setprecision(8)<<(*(origBVectorPtr_))[k1]
1173  <<" dbdp ["<<std::setw(3)<<k1<<"]= "<<std::setw(15)<<std::scientific<<std::setprecision(8)<<(*(dbdpPtrVector_[iparam]))[k1]
1174  <<std::endl;
1175 
1176  }
1177 
1178  std::ostringstream filename;
1179  filename << netlistFilename_ << "_dfdp";
1180  filename << std::setw(3) << std::setfill('0') << iparam;
1181  filename << ".txt";
1182  dfdpPtrVector_[iparam]->writeToFile(const_cast<char *>(filename.str().c_str()));
1183 
1184  filename.str("");
1185  filename << netlistFilename_ << "_fpert";
1186  filename << std::setw(3) << std::setfill('0') << iparam;
1187  filename << ".txt";
1188  pertFVectorPtr_->writeToFile(const_cast<char *>(filename.str().c_str()));
1189 
1190  filename.str("");
1191  filename << netlistFilename_ << "_dqdp";
1192  filename << std::setw(3) << std::setfill('0') << iparam;
1193  filename << ".txt";
1194  dqdpPtrVector_[iparam]->writeToFile(const_cast<char *>(filename.str().c_str()));
1195 
1196  filename.str("");
1197  filename << netlistFilename_ << "_qpert";
1198  filename << std::setw(3) << std::setfill('0') << iparam;
1199  filename << ".txt";
1200  pertQVectorPtr_->writeToFile(const_cast<char *>(filename.str().c_str()));
1201 
1202  filename.str("");
1203  filename << netlistFilename_ << "_dbdp";
1204  filename << std::setw(3) << std::setfill('0') << iparam;
1205  filename << ".txt";
1206  dbdpPtrVector_[iparam]->writeToFile(const_cast<char *>(filename.str().c_str()));
1207 
1208  filename.str("");
1209  filename << netlistFilename_ << "_bpert";
1210  filename << std::setw(3) << std::setfill('0') << iparam;
1211  filename << ".txt";
1212  pertBVectorPtr_->writeToFile(const_cast<char *>(filename.str().c_str()));
1213  }
1214 
1215  // now reset the parameter and rhs to previous values.
1216  loaderPtr_->setParam (paramName, paramOrig);
1217 
1218  ds.daeFVectorPtr->update(1.0, *(origFVectorPtr_), 0.0);
1219  ds.daeQVectorPtr->update(1.0, *(origQVectorPtr_), 0.0);
1220  ds.daeBVectorPtr->update(1.0, *(origBVectorPtr_), 0.0);
1221  }
1222  }
1223 
1224  return true;
1225 }
1226 
1227 //-----------------------------------------------------------------------------
1228 // Function : Sensitivity::setOptions
1229 //
1230 // Purpose : This function processes the .SENS line
1231 //
1232 // Special Notes :
1233 // Scope : public
1234 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1235 // Creation Date : 11/15/02
1236 //-----------------------------------------------------------------------------
1237 bool Sensitivity::setOptions(const Util::OptionBlock& OB)
1238 {
1239  bool bsuccess = true;
1240  Util::ParamList::const_iterator iter = OB.begin();
1241  Util::ParamList::const_iterator end = OB.end();
1242 
1243  numSensParams_ = 0;
1244  for ( ; iter != end; ++ iter)
1245  {
1246  if (iter->uTag() == "OBJFUNC")
1247  {
1248  objFuncString_ = iter->stringValue();
1249  expPtr_ = new Util::Expression(iter->stringValue());
1250  objFuncGiven_ = true;
1251  }
1252  else if ( std::string( iter->uTag() ,0,5) == "PARAM") // this is a vector
1253  {
1254  ExtendedString tag = iter->stringValue();
1255  tag.toUpper();
1256  // set up the initial skeleton of the maps:
1257  ++numSensParams_;
1258  paramNameVec_.push_back(tag);
1259  int sz = tag.size();
1260  if (sz > maxParamStringSize_)
1261  {
1262  maxParamStringSize_ = sz;
1263  }
1264  }
1265  else
1266  {
1267  Xyce::Report::UserWarning() << iter->uTag()
1268  << " is not a recognized sensitivity solver option.\n" << std::endl;
1269  }
1270  }
1271 
1272  // parse the expression now, so if there are any errors, they will come
1273  // up early in the simulation.
1274  if (objFuncGiven_)
1275  {
1276  // setup the names:
1277  expVarNames_.clear();
1278 
1279  std::vector<std::string> nodes;
1280  expPtr_->get_names(XEXP_NODE, nodes);
1281  std::vector<std::string> instances;
1282  expPtr_->get_names(XEXP_INSTANCE, instances);
1283 
1284  // Make the current (instance) strings all upper case.
1285  // The topology directory apparently requires this.
1286  std::vector<std::string>::iterator iter;
1287  for (iter=instances.begin();iter!=instances.end();++iter)
1288  {
1289  ExtendedString tmpString = *iter;
1290  tmpString.toUpper ();
1291  *iter = tmpString;
1292  }
1293 
1294  expVarNames_.insert(expVarNames_.end(), nodes.begin(), nodes.end());
1295  expVarNames_.insert(expVarNames_.end(), instances.begin(), instances.end());
1296 
1297  // Order the names in the expression so that it agrees with the order
1298  // in expVarNames.
1299  if ( !expVarNames_.empty() )
1300  {
1301  expPtr_->order_names( expVarNames_ );
1302  }
1303 
1304  expNumVars_ = expVarNames_.size();
1305  }
1306 
1307  if (DEBUG_NONLINEAR && isActive(Diag::SENS_NONLINEAR_SOLVER))
1308  {
1309  std::vector<std::string>::iterator iter;
1310  std::vector<std::string>::iterator begin = paramNameVec_.begin();
1311  std::vector<std::string>::iterator end = paramNameVec_.end ();
1312 
1313  for (iter=begin;iter!=end;++iter)
1314  {
1315  Xyce::dout() << *iter<<std::endl;
1316  }
1317  }
1318 
1320 
1321  return bsuccess;
1322 }
1323 
1324 //-----------------------------------------------------------------------------
1325 // Function : Sensitivity::setSensitivityOptions
1326 // Purpose : This function processes the .options SENSITIVITY line
1327 // Special Notes :
1328 // Scope : public
1329 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1330 // Creation Date : 06/20/2013
1331 //-----------------------------------------------------------------------------
1332 bool Sensitivity::setSensitivityOptions(const Util::OptionBlock &OB)
1333 {
1334  bool bsuccess = true;
1335  Util::ParamList::const_iterator it = OB.begin();
1336  Util::ParamList::const_iterator end = OB.end();
1337  for ( ; it != end; ++ it)
1338  {
1339  if ((*it).uTag() == "ADJOINT")
1340  {
1342  static_cast<bool>((*it).getImmutableValue<bool>());
1343  }
1344  else if ((*it).uTag() == "DIRECT")
1345  {
1346  solveDirectFlag_ =
1347  static_cast<bool>((*it).getImmutableValue<bool>());
1348  }
1349  else if ((*it).uTag() == "OUTPUTSCALED")
1350  {
1352  static_cast<bool>((*it).getImmutableValue<bool>());
1353  }
1354  else if ((*it).uTag() == "OUTPUTUNSCALED")
1355  {
1357  static_cast<bool>((*it).getImmutableValue<bool>());
1358  }
1359  else if ((*it).uTag() == "STDOUTPUT")
1360  {
1361  stdOutputFlag_ =
1362  static_cast<bool>((*it).getImmutableValue<bool>());
1363  }
1364  else if ((*it).uTag() == "DAKOTAFILE")
1365  {
1367  static_cast<bool>((*it).getImmutableValue<bool>());
1368  }
1369  else if ((*it).uTag() == "DIAGNOSTICFILE")
1370  {
1371  fileOutputFlag_ =
1372  static_cast<bool>((*it).getImmutableValue<bool>());
1373  }
1374  else if ((*it).uTag() == "FORCEFD")
1375  {
1376  forceFD_ =
1377  static_cast<bool>((*it).getImmutableValue<bool>());
1378  }
1379  else if ((*it).uTag() == "DIFFERENCE")
1380  {
1381  ExtendedString sval=(*it).stringValue();
1382  sval.toUpper();
1383  if(sval=="FORWARD")
1384  {
1386  }
1387  else if(sval=="REVERSE")
1388  {
1390  }
1391  else if(sval=="CENTRAL")
1392  {
1394  static std::string tmp = "difference=central not supported.\n";
1395  N_ERH_ErrorMgr::report( N_ERH_ErrorMgr::USR_FATAL_0, tmp);
1396  }
1397  else
1398  {
1399  static std::string tmp = "difference not recognized!\n";
1400  N_ERH_ErrorMgr::report( N_ERH_ErrorMgr::USR_FATAL_0, tmp);
1401  }
1402  }
1403  else if ((*it).uTag() == "SQRTETA")
1404  {
1405  sqrtEta_ = (*it).getImmutableValue<double>();
1406  sqrtEtaGiven_ = true;
1407  }
1408  else if ((*it).uTag() == "REUSEFACTORS")
1409  {
1410  reuseFactors_ = (*it).getImmutableValue<double>();
1411  }
1412  else if (DEBUG_NONLINEAR && (*it).uTag() == "DEBUGLEVEL")
1413  {
1414  debugLevel_ = (*it).getImmutableValue<int>();
1415  Xyce::setNonlinearSensitivityDebugLevel(debugLevel_);
1416  }
1417  else
1418  {
1419  Xyce::Report::UserWarning() << (*it).uTag()
1420  << " is not a recognized sensitivity solver option.\n" << std::endl;
1421  }
1422  }
1423 
1424  return true;
1425 }
1426 
1427 //-----------------------------------------------------------------------------
1428 // Function : Sensitivity::setTranOptions
1429 //
1430 // Purpose : Not used yet. Same as setOptions, but for transient
1431 // mode, if and when the sensitivity stuff is ever set
1432 // up to work in transient.
1433 //
1434 // Special Notes :
1435 // Scope : public
1436 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1437 // Creation Date : 10/31/02
1438 //-----------------------------------------------------------------------------
1439 bool Sensitivity::setTranOptions(const Util::OptionBlock& OB)
1440 {
1441  bool bsuccess = true;
1442  return bsuccess;
1443 }
1444 
1445 //-----------------------------------------------------------------------------
1446 // Function : Sensitivity::setHBOptions
1447 // Purpose :
1448 // Special Notes :
1449 // Scope : public
1450 // Creator :
1451 // Creation Date :
1452 //-----------------------------------------------------------------------------
1453 bool Sensitivity::setHBOptions(const Util::OptionBlock& OB)
1454 {
1455  bool bsuccess = true;
1456  return bsuccess;
1457 }
1458 
1459 //-----------------------------------------------------------------------------
1460 // Function : Sensitivity::getMaxNormF
1461 // Purpose :
1462 // Special Notes :
1463 // Scope : public
1464 // Creator : Rich Schiek, Electrical and MEMS Modeling
1465 // Creation Date : 9/28/2009
1466 //-----------------------------------------------------------------------------
1468 {
1469  return nls_.getMaxNormF();
1470 }
1471 
1472 //-----------------------------------------------------------------------------
1473 // Function : Sensitivity::getMaxNormFindex
1474 // Purpose :
1475 // Special Notes :
1476 // Scope : public
1477 // Creator : Rich Schiek, Electrical and MEMS Modeling
1478 // Creation Date : 9/28/2009
1479 //-----------------------------------------------------------------------------
1481 {
1482  return nls_.getMaxNormFindex ();
1483 }
1484 
1485 } // namespace Nonlinear
1486 } // namespace Xyce
std::vector< double > paramOrigVals_
virtual int getMaxNormFindex() const =0
bool getParamAndReduce(const std::string &name, double &val) const
std::vector< Linear::Vector * > nextDqdpPtrVector
void dakOutput(std::string idString, std::vector< double > &paramVals, std::vector< double > &sensitivities, std::vector< double > &scaled_sensitivities)
Pure virtual class to augment a linear system.
std::vector< double > dOdpVec_
std::vector< double > scaled_dOdpVec_
void allocateSensitivityArrays(Linear::Builder &builder, int numParams)
std::vector< Linear::Vector * > nextDXdpPtrVector
std::vector< double > scaled_dOdpAdjVec_
int solve(NonLinearSolver *nlsTmpPtr=NULL)
std::vector< Linear::Vector * > nextDbdpPtrVector
std::vector< Linear::Vector * > nextDfdpPtrVector
Linear::Vector * savedNewtonVectorPtr_
std::vector< double > objectiveVec_
std::vector< Linear::Vector * > currDXdpPtrVector
void stdOutput(std::string idString, std::vector< double > &paramVals, std::vector< double > &sensitivities, std::vector< double > &scaled_sensitivities)
bool setParam(std::string &name, double val, bool overrideOriginal=false)
Linear::Vector * daeQVectorPtr
std::vector< double > expVarVals_
const Analysis::AnalysisManager & getAnalysisManager() const
void fileOutput(std::string idString, std::vector< double > &paramVals, std::vector< double > &sensitivities, std::vector< double > &scaled_sensitivities)
Sensitivity(NonLinearSolver &nls_, Topo::Topology &top_, const IO::CmdParse &cp)
Linear::Vector * daeFVectorPtr
bool setTranOptions(const Util::OptionBlock &OB)
bool icSensitivity(std::vector< double > &objectiveVec, std::vector< double > &dOdpVec, std::vector< double > &dOdpAdjVec, std::vector< double > &scaled_dOdpVec, std::vector< double > &scaled_dOdpAdjVec)
void getAnalyticSensitivities(std::string &name, std::vector< double > &dfdpVec, std::vector< double > &dqdpVec, std::vector< double > &dbdpVec, std::vector< int > &FindicesVec, std::vector< int > &QindicesVec, std::vector< int > &BindicesVec) const
bool setOptions(const Util::OptionBlock &OB)
std::vector< std::string > expVarNames_
bool setHBOptions(const Util::OptionBlock &OB)
bool setSensitivityOptions(const Util::OptionBlock &OB)
std::vector< double > dOdpAdjVec_
bool registerAnalysisManager(Analysis::AnalysisManager *tmp_anaIntPtr)
std::vector< double > expVarDerivs_
Loader::NonlinearEquationLoader * loaderPtr_
std::vector< Linear::Vector * > sensRHSPtrVector
Linear::Matrix * dQdxMatrixPtr
Linear::Vector * daeBVectorPtr
std::vector< Linear::Vector * > nextDQdxDXdpPtrVector
virtual double getMaxNormF() const =0
std::vector< std::string > paramNameVec_