Xyce  6.1
N_NLS_NOX_XyceTests.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_NOX_XyceTests.C,v $
27 //
28 // Purpose : Status test.
29 //
30 // Special Notes :
31 //
32 // Creator : Roger Pawlowski, SNL 9233
33 //
34 // Creation Date : 04/15/03
35 //
36 // Revision Information:
37 // ---------------------
38 //
39 // Revision Number: $Revision: 1.60 $
40 //
41 // Revision Date : $Date: 2015/09/02 20:23:46 $
42 //
43 // Current Owner : $Author: tvrusso $
44 //-------------------------------------------------------------------------
45 
46 #include <Xyce_config.h>
47 
48 
49 // ---------- Standard Includes ----------
50 
51 // ---------- Xyce Includes ----------
52 #include "N_NLS_NOX_Group.h"
53 #include "N_NLS_NOX_XyceTests.h"
54 #include "N_NLS_NOX_Vector.h"
55 #include "N_LAS_Vector.h"
57 #include "NOX.H"
58 #include "NOX_Solver_LineSearchBased.H"
59 
60 #include <N_UTL_MachDepParams.h>
61 
62 // ---------- Namespaces ----------
63 
64 namespace Xyce {
65 namespace Nonlinear {
66 namespace N_NLS_NOX {
67 
68 // ---------- Code ----------
69 
70 //-----------------------------------------------------------------------------
71 // Function : XyceTests::XyceTests
72 // Purpose : constructor
73 // Special Notes :
74 // Scope : public
75 // Creator :
76 // Creation Date :
77 //-----------------------------------------------------------------------------
79  Parallel::Machine comm,
80  bool isTransient,
81  double normF,
82  double machPrec,
83  Linear::Vector ** currSolVectorPtrPtr,
84  double epsilon_a,
85  double epsilon_r,
86  double tol,
87  int maxIters,
88  double convRate,
89  double relConvRate,
90  double maxConvRate,
91  double stagnationTol,
92  int maxBadSteps,
93  int checkDeviceConvergence,
94  double smallUpdateTol,
96  bool maskingFlag,
97  Linear::Vector * maskVectorPtr)
98  : comm_(comm),
99  status_(NOX::StatusTest::Unconverged),
100  returnTest_(0),
101  isTransient_(isTransient),
102  niters_(-1),
103  maxNormFindex_(-1),
104  maxNormF_(0.0),
105  requestedMaxNormF_(normF),
106  requestedMachPrecTol_(machPrec),
107  oldTimeStepVectorPtrPtr_(currSolVectorPtrPtr),
108  weightsVectorPtr_(0),
109  updateVectorPtr_(0),
110  tmpVectorPtr_(0),
111  epsilon_a_(epsilon_a),
112  epsilon_r_(epsilon_r),
113  tol_(tol),
114  weightedUpdate_(0.0),
115  maxIters_(maxIters),
116  requestedConvRate_(convRate),
117  currentConvRate_(1.0),
118  requestedRelativeConvRate_(relConvRate),
119  currentRelativeConvRate_(1.0),
120  normResidualInit_(1.0),
121  maxConvRate_(maxConvRate),
122  lastIteration_(-1),
123  badStepCount_(0),
124  minConvRate_(1.0),
125  stagnationTol_(stagnationTol),
126  maxBadSteps_(maxBadSteps),
127  xyceReturnCode_(0),
128  smallUpdateTol_(smallUpdateTol),
129  checkDeviceConvergence_(checkDeviceConvergence),
130  loaderPtr_(loader),
131  allDevicesConverged_(false),
132  innerDevicesConverged_(false),
133  maskingFlag_(NLS_MASKED_WRMS_NORMS && maskingFlag ),
134  deviceMaskFlag_( true ),
135  weightMaskVectorPtr_( maskVectorPtr)
136 {
137 
138 }
139 
140 //-----------------------------------------------------------------------------
141 // Function : XyceTests::XyceTests
142 // Purpose : destructor
143 // Special Notes :
144 // Scope : public
145 // Creator :
146 // Creation Date :
147 //-----------------------------------------------------------------------------
149 {
150  if( weightsVectorPtr_ != 0 )
151  {
152  delete weightsVectorPtr_;
153  delete updateVectorPtr_;
154  delete tmpVectorPtr_;
155  }
156 }
157 
158 
159 //-----------------------------------------------------------------------------
160 // Function : XyceTests::checkStatus
161 // Purpose : main testing function for this class
162 // Special Notes :
163 // Scope : public
164 // Creator :
165 // Creation Date :
166 //-----------------------------------------------------------------------------
167 NOX::StatusTest::StatusType
169  const NOX::Solver::Generic& problem,
170  NOX::StatusTest::CheckType checkType)
171 {
172  status_ = NOX::StatusTest::Unconverged;
173  xyceReturnCode_ = 0;
174  niters_ = problem.getNumIterations();
175 
176  returnTest_ = 0;
177 
178  // Get the current and previous solutions
179  const Linear::Vector& x = (dynamic_cast<const Vector&>
180  (problem.getSolutionGroup().getX())).getNativeVectorRef();
181  const Linear::Vector& oldX = (dynamic_cast<const Vector&>
182  (problem.getPreviousSolutionGroup().getX())).getNativeVectorRef();
183 
184  // Test0 - NaN/Inf checker
185  NOX::StatusTest::StatusType check = finiteTest_.checkStatus(problem, checkType);
186  if (check == NOX::StatusTest::Failed)
187  {
188  status_ = check;
189  returnTest_ = 0;
190  xyceReturnCode_ = retCodes_.nanFail; // default: -6
191  return status_;
192  }
193 
194  // This test is for 2-level solves only.
195  // If the inner solve failed, then the whole thing needs to fail.
196  // If 2-level is not being used, this test doesn't do anything.
199  {
200  status_ = NOX::StatusTest::Failed;
201  returnTest_ = 9;
202  xyceReturnCode_ = retCodes_.innerSolveFailed; // default: -5
203  return status_;
204  }
205 
206  // Test #9 (-9): if linear solver failed, reject.
207  const N_NLS_NOX::Group & grp =
208  (dynamic_cast<const N_NLS_NOX::Group &>(problem.getSolutionGroup()));
209 
210  bool linearSolverStatus = grp.linearSolverStatus();
211  if (!linearSolverStatus)
212  {
213  status_ = NOX::StatusTest::Failed;
214  returnTest_ = 9;
216  return status_;
217  }
218 
219  // Test 8 - Devices need to satisfy their own convergence criteria
221  {
224  {
225  status_ = NOX::StatusTest::Unconverged;
226  returnTest_ = 8;
227  xyceReturnCode_ = 0;
228  return status_;
229  }
230  }
231 
232  // Compute a few norms needed by various tests.
233  maxNormF_ = problem.getSolutionGroup().getF()
234  .norm(NOX::Abstract::Vector::MaxNorm);
235 
236  const Linear::Vector& F = (dynamic_cast<const Vector&>
237  (problem.getSolutionGroup().getF())).getNativeVectorRef();
238 
239  std::vector<int> index(1, -1);
240  F.infNormIndex( &index[0] );
241  maxNormFindex_ = index[0];
242 
243 #if 0
244  // ERK. This has been removed as part of bug #414 (SON). If its removal
245  // causes problems then it could (maybe) be restored). But it would need
246  // to be restored to a point prior to the linear solver failure test, above.
247  //
248  //Test 1 - Make sure the residual isn't too small, hardwired tolerances
249  if ((maxNormF_ < requestedMaxNormF_) &&
251  {
252  status_ = NOX::StatusTest::Converged;
253  returnTest_ = 1;
254  xyceReturnCode_ = retCodes_.normTooSmall; // default: 1
255  return status_;
256  }
257 #endif
258 
259  // Test 2 - Normal convergence based on rhs residual (2a) and
260  // update norm (2b).
261 
262  // Copy into local reference
263  Linear::Vector& oldTimeStepX = **oldTimeStepVectorPtrPtr_;
264 
265  // Allocate space if necessary
266  if (weightsVectorPtr_ == 0)
267  {
268  weightsVectorPtr_ = new Linear::Vector(x);
269  // when we create weightsVectorPtr_ from the latest solution, there
270  // is a chance that one of the values will be zero. If this isn't
271  // the DC op step or the first iteration of a time step, then
272  // we'll end up dividing by zero in the wMaxNorm function below.
273  // So to be safe we'll just add epsilon_a_ on the when we create
274  // this vector.
275  for (int i=0; i< x.localLength() ; ++i )
276  {
277  (*weightsVectorPtr_)[i] += epsilon_a_;
278  }
279  updateVectorPtr_ = new Linear::Vector(x);
280  tmpVectorPtr_ = new Linear::Vector(x);
281  }
282 
283  // Local references
284  Linear::Vector& weights = *weightsVectorPtr_;
285 
286  // Compute local portion of weights vector
287  // Weights are recomputed at each nonlinear iteration of a DC Op calc
288  // but only at the beginning of a transient nonlinear solve.
289  if ((!isTransient_) || (niters_ == 0))
290  {
291  int length = x.localLength();
292  for (int i = 0; i < length; ++i )
293  {
294  //update[i] = x[i] - oldX[i];
295  weights[i] =
296  epsilon_r_ * std::max(fabs(x[i]), fabs(oldTimeStepX[i])) + epsilon_a_;
297  if (NLS_MASKED_WRMS_NORMS && maskingFlag_ && deviceMaskFlag_ && ((*weightMaskVectorPtr_)[i] == 0.0) )
298  {
299  weights[i] = Util::MachineDependentParams::MachineBig();
300  }
301  }
302  }
303 
304  if (niters_ < 1)
305  {
306  weightedUpdate_ = 1.0;
307  }
308  else
309  {
310  // Next compute the update
311  updateVectorPtr_->update(1.0, x, -1.0, oldX, 0.0);
312 
313  // Compute final result
314 #ifdef Xyce_SPICE_NORMS
315  updateVectorPtr_->wMaxNorm(weights,*tmpVectorPtr_,&weightedUpdate_);
316 #else
317  updateVectorPtr_->wRMSNorm(weights,&weightedUpdate_);
318 #endif
319 
320  // RPP: If a line search is being used, we must account for any
321  // damping of the step length. Otherwise delta X could be small due
322  // the line search and not due to being close to a solution.
323  const NOX::Solver::LineSearchBased* test = 0;
324  test = dynamic_cast<const NOX::Solver::LineSearchBased*>(&problem);
325  if (test != 0)
326  {
327  weightedUpdate_ = weightedUpdate_/(test->getStepSize());
328  }
329 
331  {
332  status_ = NOX::StatusTest::Converged;
333  returnTest_ = 2;
335  return status_;
336  }
337  }
338 
339  // Test 3 - Near Convergence - Hit max iterations but residual
340  // and convergence rate indicate we may be near a converged solution.
341  // Therefore, let the time stepper decide whether or not the step is ok.
342  // Transient mode ONLY!
343  // NOTE: Convergence rates are based on the 2-Norm, not max norm!
344  if (niters_ > 0)
345  {
346  // ||F(x_current)|| / ||F(x_previous)||
347  currentConvRate_ = (problem.getSolutionGroup().getNormF()) /
348  (problem.getPreviousSolutionGroup().getNormF());
349 
350  // ||F(x)|| / ||F(x_init)||
351  currentRelativeConvRate_ = (problem.getSolutionGroup().getNormF()) /
353  }
354  else
355  {
356  currentConvRate_ = 1.0;
358  }
359 
360  if (isTransient_)
361  {
362  if (niters_ == 0)
363  {
364  normResidualInit_ = problem.getSolutionGroup().getNormF();
365  }
366 
367  // Test only if we hit the max number of iterations
368  if (niters_ >= maxIters_)
369  {
372  {
373  status_ = NOX::StatusTest::Converged;
374  xyceReturnCode_ = retCodes_.nearConvergence; // default: 3
375 
376  if (xyceReturnCode_ < 0)
377  status_ = NOX::StatusTest::Failed;
378  else
379  status_ = NOX::StatusTest::Converged;
380  }
381  else
382  {
383  status_ = NOX::StatusTest::Failed;
384  xyceReturnCode_ = retCodes_.tooManySteps; // default: -1
385  }
386 
387  returnTest_ = 3;
388  return status_;
389  }
390  } // end test 3
391 
392  // Test 4 - Update is too small
393  if ((niters_ > 0) && (weightedUpdate_ < smallUpdateTol_) && (niters_ < maxIters_))
394  {
395  if (isTransient_) // Let the time integrator determine convergence (+4)
396  {
397  xyceReturnCode_ = retCodes_.smallUpdate; // default: 4
398  status_ = NOX::StatusTest::Failed;
399  }
400  else
401  {
402  xyceReturnCode_ = 0; // neither pass nor fail.
403  status_ = NOX::StatusTest::Unconverged;
404  }
405 
406  returnTest_ = 4;
407 
408  return status_;
409  }
410 
411  // Test 5 - Max nonlinear iterations (if transient, this will be checked
412  // in the NearConvergence test (#3)
413  if ((!isTransient_) && (niters_ >= maxIters_))
414  {
415  status_ = NOX::StatusTest::Failed;
416  returnTest_ = 5;
417  xyceReturnCode_ = retCodes_.tooManySteps; // default: -1
418  return status_;
419  }
420 
421  // Test 6 - update is too big
423  {
424  status_ = NOX::StatusTest::Failed;
425  returnTest_ = 6;
426  xyceReturnCode_ = retCodes_.updateTooBig; // default: -2
427  return status_;
428  }
429 
430  // Test 7 - Stall in the convergence rate. Transient mode ONLY!
431  if (isTransient_)
432  {
433  // First time through we don't do anything but reset the counters
434  if (niters_ == 0)
435  {
436  badStepCount_ = 0;
437  lastIteration_ = 0;
438  //minConvRate = 1.0; // Don't reset this. Xyce solver never does.
439  }
440 
441  // Make sure we have not already counted the last nonlinear iteration.
442  // This protects against multiple calls to checkStatus() in between
443  // nonlinear iterations.
444  bool isCounted = false;
445  if (niters_ == lastIteration_)
446  {
447  isCounted = true;
448  }
449  else
450  {
452  }
453 
454  // Set counter appropriately
455  if (!isCounted)
456  {
457  if (fabs(currentConvRate_ - 1.0) <= stagnationTol_)
458  {
459  if ((badStepCount_ == 0) || (currentConvRate_ < minConvRate_))
460  {
462  }
463  ++badStepCount_ ;
464  }
465  else
466  {
467  badStepCount_ = 0;
468  }
469  }
470 
471  if (badStepCount_ >= maxBadSteps_)
472  {
473  if ((currentRelativeConvRate_ <= 0.9) && (minConvRate_ <= 1.0))
474  {
475  status_ = NOX::StatusTest::Converged;
476  returnTest_ = 7;
477  xyceReturnCode_ = retCodes_.nearConvergence; // default: 3
478  // note - I'm not sure if this is
479  // really a near convergece test - but 3 is the code for it...
480 
481  if (xyceReturnCode_ < 0)
482  status_ = NOX::StatusTest::Failed;
483  else
484  status_ = NOX::StatusTest::Converged;
485  }
486  else
487  {
488  status_ = NOX::StatusTest::Failed;
489  returnTest_ = 7;
490  xyceReturnCode_ = retCodes_.stalled; // default: -3
491  }
492  }
493  }
494 
495  return status_;
496 }
497 
498 //-----------------------------------------------------------------------------
499 // Function : XyceTests::print
500 // Purpose : verbose output
501 // Special Notes :
502 // Scope : public
503 // Creator :
504 // Creation Date :
505 //-----------------------------------------------------------------------------
506 std::ostream& XyceTests::print(std::ostream& stream, int indent) const
507 {
508  // precision
509  int p = 5;
510 
511  for (int j = 0; j < indent; ++j )
512  stream << ' ';
513  stream << status_ << "by Test #" << returnTest_ << "\n";
514 
515  indent += 4;
516 
517  //for (int j = 0; j < indent; ++j )
518  // stream << ' ';
519  finiteTest_.print(stream, indent);
520 
522  for (int j = 0; j < indent; ++j )
523  stream << ' ';
524  stream << "8. Devices are Converged: ";
526  stream << "true" << "\n";
527  else
528  stream << "false" << "\n";
529  }
530 
531  for (int j = 0; j < indent; ++j )
532  stream << ' ';
533  stream << "1. Inf-Norm F too small" << "\n";
534 
535  for (int j = 0; j < indent; ++j )
536  stream << ' ';
537  stream << " Machine Precision: " << NOX::Utils::sciformat(maxNormF_, p)
538  << " < " << NOX::Utils::sciformat(requestedMachPrecTol_, p) << "\n";
539 
540  for (int j = 0; j < indent; ++j )
541  stream << ' ';
542  stream << " Requested Tolerance: " << NOX::Utils::sciformat(maxNormF_, p)
543  << " < " << NOX::Utils::sciformat(requestedMaxNormF_, p) << "\n";
544 
545  for (int j = 0; j < indent; ++j )
546  stream << ' ';
547  stream << "2. Normal Convergence" << "\n";
548 
549  for (int j = 0; j < indent; ++j )
550  stream << ' ';
551  stream << " Inf-Norm F: " << NOX::Utils::sciformat(maxNormF_, p)
552  << " < " << NOX::Utils::sciformat(requestedMaxNormF_, p) << "\n";
553 
554  for (int j = 0; j < indent; ++j )
555  stream << ' ';
556  stream << " Weighted Update: " << NOX::Utils::sciformat(weightedUpdate_, p)
557  << " < " << NOX::Utils::sciformat(tol_, p) << "\n";
558 
559  for (int j = 0; j < indent; ++j )
560  stream << ' ';
561  stream << "3. Near Convergence" << "\n";
562 
563  for (int j = 0; j < indent; ++j )
564  stream << ' ';
565  stream << " Max Iters: " << niters_
566  << " < " << maxIters_ << "\n";
567 
568  for (int j = 0; j < indent; ++j )
569  stream << ' ';
570  stream << " Convergence Rate: "
571  << NOX::Utils::sciformat(currentConvRate_, p)
572  << " < " << NOX::Utils::sciformat(requestedConvRate_, p) << "\n";
573 
574  for (int j = 0; j < indent; ++j )
575  stream << ' ';
576  stream << " Relative Convergence Rate: "
577  << NOX::Utils::sciformat(currentRelativeConvRate_, p)
578  << " < " << NOX::Utils::sciformat(requestedRelativeConvRate_, p)
579  << "\n";
580 
581  for (int j = 0; j < indent; ++j )
582  stream << ' ';
583  stream << "4. Small Weighted Update: "
584  << NOX::Utils::sciformat(weightedUpdate_, p)
585  << " < " << NOX::Utils::sciformat(smallUpdateTol_, p) << "\n";
586 
587  if (!isTransient_) {
588 
589  for (int j = 0; j < indent; ++j )
590  stream << ' ';
591  stream << "5. Maximum Iterations: "
592  << niters_
593  << " < " << maxIters_ << "\n";
594 
595  for (int j = 0; j < indent; ++j )
596  stream << ' ';
597  stream << "6. Large Conv Rate: "
598  << NOX::Utils::sciformat(currentConvRate_, p)
599  << " < " << NOX::Utils::sciformat(maxConvRate_, p) << "\n";
600  }
601 
602  for (int j = 0; j < indent; ++j )
603  stream << ' ';
604  stream << "7. Stagnation " << "\n";
605 
606  for (int j = 0; j < indent; ++j )
607  stream << ' ';
608  stream << " Bad Step Count: "
609  << badStepCount_ << " < " << maxBadSteps_ << "\n";
610 
611  for (int j = 0; j < indent; ++j )
612  stream << ' ';
613  stream << " Stagnation Tolerance: "
614  << NOX::Utils::sciformat(fabs(currentConvRate_ - 1.0), p)
615  << " < " << NOX::Utils::sciformat(stagnationTol_, p) << "\n";
616 
617  for (int j = 0; j < indent; ++j )
618  stream << ' ';
619  stream << "9. Linear solver failed." << "\n";
620 
621  stream << std::endl;
622  return stream;
623 }
624 
625 //-----------------------------------------------------------------------------
626 // Function : XyceTests::getXyceReturnCode
627 // Purpose : verbose output
628 // Special Notes :
629 // Scope : public
630 // Creator :
631 // Creation Date :
632 //-----------------------------------------------------------------------------
634 {
635  return xyceReturnCode_;
636 }
637 
638 //-----------------------------------------------------------------------------
639 // Function : XyceTests::getMaxNormF
640 // Purpose : verbose output
641 // Special Notes :
642 // Scope : public
643 // Creator :
644 // Creation Date :
645 //-----------------------------------------------------------------------------
647 {
648  return maxNormF_;
649 }
650 
651 //-----------------------------------------------------------------------------
652 // Function : XyceTests::getMaxNormFindex
653 // Purpose : verbose output
654 // Special Notes :
655 // Scope : public
656 // Creator :
657 // Creation Date :
658 //-----------------------------------------------------------------------------
660 {
661  return maxNormFindex_;
662 }
663 
664 }}}
Pure virtual class to augment a linear system.
Xyce::Loader::NonlinearEquationLoader * loaderPtr_
NOX::StatusTest::FiniteValue finiteTest_
Xyce::Linear::Vector ** oldTimeStepVectorPtrPtr_
Xyce::Nonlinear::ReturnCodes retCodes_
XyceTests(Parallel::Machine comm, bool isTransient, double normF, double machPrec, Xyce::Linear::Vector **currSolVectorPtrPtr, double epsilon_a, double epsilon_r, double tol, int maxIters, double convRate, double relConvRate, double maxConvRate, double stagnationTol, int maxBadSteps, int checkDeviceConvergence, double smallUpdateTol, Xyce::Loader::NonlinearEquationLoader *loader, bool maskingFlag, Xyce::Linear::Vector *maskVectorPtr)
std::ostream & print(std::ostream &stream, int indent=0) const
NOX::StatusTest::StatusType checkStatus(const NOX::Solver::Generic &problem, NOX::StatusTest::CheckType checkType)
Definition: N_NLS_fwd.h:108