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