Xyce  6.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
N_DEV_NumericalJacobian.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_DEV_NumericalJacobian.C,v $
27 //
28 // Purpose :
29 //
30 // Special Notes :
31 //
32 // Creator : Eric Keiter, SNL, Electrical and Microsystems Modeling
33 //
34 // Creation Date : 04/30/02
35 //
36 // Revision Information:
37 // ---------------------
38 //
39 // Revision Number: $Revision: 1.68 $
40 //
41 // Revision Date : $Date: 2014/05/13 14:50:38 $
42 //
43 // Current Owner : $Author: dgbaur $
44 //-------------------------------------------------------------------------
45 
46 #include <Xyce_config.h>
47 
48 
49 // ---------- Standard Includes ----------
50 #include <N_UTL_Misc.h>
51 #include <string>
52 #include <iostream>
53 
54 #ifdef HAVE_CSTDIO
55 #include <cstdio>
56 #else
57 #include <stdio.h>
58 #endif
59 
60 
61 #include <sstream>
62 // ---------- Xyce Includes ----------
64 #include <N_DEV_MatrixLoadData.h>
65 #include <N_DEV_DeviceOptions.h>
66 #include <N_DEV_SolverState.h>
67 #include <N_DEV_ExternData.h>
68 #include <N_DEV_DeviceInstance.h>
69 #include <N_DEV_DeviceMgr.h>
70 
71 #include <N_ERH_ErrorMgr.h>
72 
73 #include <N_LAS_Vector.h>
74 #include <N_LAS_Matrix.h>
75 
76 // ---------- Static Initializations ----------
77 
78 
79 namespace Xyce {
80 namespace Device {
81 
82 //-----------------------------------------------------------------------------
83 // Function : NumericalJacobian::NumericalJacobian
84 // Purpose : block constructor
85 // Special Notes :
86 // Scope : public
87 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
88 // Creation Date : 4/30/02
89 //-----------------------------------------------------------------------------
91  MatrixLoadData & mlData1,
92  const SolverState &ss1,
93  const ExternData &ed1,
94  const DeviceOptions & do1)
95  : mlData(mlData1),
96  cols(mlData1.cols),
97  vals(mlData1.vals),
98  Qvals(mlData1.Qvals),
99  val_local(mlData1.val_local),
100  Qval_local(mlData1.Qval_local),
101  col_local(mlData1.col_local),
102  row_local(mlData1.row_local),
103  internalFlag(mlData1.internalFlag),
104  solState (ss1),
105  extData (ed1),
106  devOptions(do1),
107  maxCols(10) // guess
108 {}
109 
110 //-----------------------------------------------------------------------------
111 // Function : NumericalJacobian::NumericalJacobian
112 // Purpose : copy constructor
113 // Special Notes :
114 // Scope : public
115 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
116 // Creation Date : 4/30/02
117 //-----------------------------------------------------------------------------
119  : mlData(right.mlData),
120  cols(right.cols),
121  vals(right.vals),
122  Qvals(right.Qvals),
123  val_local(right.val_local),
124  Qval_local(right.Qval_local),
125  col_local(right.col_local),
126  row_local(right.row_local),
127  internalFlag(right.internalFlag),
128  solState (right.solState),
129  extData (right.extData),
130  devOptions(right.devOptions),
131  maxCols(right.maxCols)
132 {
133 
134 }
135 
136 //-----------------------------------------------------------------------------
137 // Function : NumericalJacobian::~NumericalJacobian
138 // Purpose : destructor
139 // Special Notes :
140 // Scope : public
141 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
142 // Creation Date : 3/30/00
143 //-----------------------------------------------------------------------------
145 {
146 
147 }
148 
149 //-----------------------------------------------------------------------------
150 // Function : NumericalJacobian::testDAEMatrices
151 //
152 // Purpose : Performs a numerical jacobian test on the dFdx and dQdx
153 // matrices.
154 //
155 // Special Notes : This is the main numerical jacoabian test. There is
156 // another numerical jacobian test, which pre-dates this
157 // one, but is not recommended. The older one is handled
158 // by the functions preceding this one in this file.
159 //
160 // The old one attempt to run the code using a purely
161 // numerical jacobian. It does not work with votlage limiting.
162 //
163 // The new one (controlled from this function) operates along
164 // side the analytic jacobian, but does not replace it, as
165 // the solvers will still use the analytic jacobian. This
166 // function will test, on a device-by-device basis, the device
167 // contributions to the analytic jaocian. That way,
168 // it is possible to test a single device (rather than the whole
169 // problem), and test it at a precise point during the simulation.
170 //
171 // Scope : public
172 // Creator : Eric Keiter, SNL, Electrical and Microsystems Modeling
173 // Creation Date : 12/15/06
174 //-----------------------------------------------------------------------------
175 bool NumericalJacobian::testDAEMatrices( DeviceInstance & instance, const std::vector<std::string> & nameVec)
176 {
177 
178  // Set up various references to indexing arrays
179  const std::vector<int> & devLIDs = instance.getDevLIDs();
180  const std::vector<int> & devStateLIDs = instance.getStaLIDVec();
181  const std::vector< std::vector<int> > & devJacLIDs = instance.getDevJacLIDs();
182  const std::vector< std::vector<int> > & jacStamp = instance.jacobianStamp();
183 
184 
185  // Set up references to temporary data structures.
186  std::vector< std::vector<double> > & numJacF = mlData.numJac;
187  std::vector< std::vector<double> > & saveJacF = mlData.saveJac;
188  std::vector< std::vector<double> > & devJacF = mlData.devJac;
189  std::vector< std::vector<double> > & diffJacF = mlData.diffJac;
190  std::vector< std::vector<double> > & relJacF = mlData.relJac;
191 
192  std::vector< std::vector<double> > & numJacQ = mlData.numJacQ;
193  std::vector< std::vector<double> > & saveJacQ = mlData.saveJacQ;
194  std::vector< std::vector<double> > & devJacQ = mlData.devJacQ;
195  std::vector< std::vector<double> > & diffJacQ = mlData.diffJacQ;
196  std::vector< std::vector<double> > & relJacQ = mlData.relJacQ;
197 
198  std::vector< std::vector<int> > & statusF = mlData.status;
199  std::vector< std::vector<int> > & statusQ = mlData.statusQ;
200  std::vector< std::vector<int> > & stencil = mlData.stencil;
201 
202  std::vector<double> & saveF = mlData.saveRHS;
203  std::vector<double> & pertF = mlData.pertRHS;
204  std::vector<double> & origF = mlData.origRHS;
205  std::vector<double> & saveQ = mlData.saveQ;
206  std::vector<double> & pertQ = mlData.pertQ;
207  std::vector<double> & origQ = mlData.origQ;
208 
209  std::vector<double> & saveSoln = mlData.saveSoln;
210  std::vector<double> & pertSoln = mlData.pertSoln;
211  std::vector<double> & saveCurrSoln = mlData.saveCurrSoln;
212 
213  std::vector<double> & saveLastState = mlData.saveLastState;
214  std::vector<double> & saveCurrState = mlData.saveCurrState;
215  std::vector<double> & saveNextState = mlData.saveNextState;
216  std::vector<double> & saveStateDerivs = mlData.saveStateDerivs;
217 
218  // set up references to epetra objects.
219  N_LAS_Vector & Fvec = (*extData.daeFVectorPtr);
220  N_LAS_Vector & Qvec = (*extData.daeQVectorPtr);
221 
222  N_LAS_Vector & currSol = (*extData.currSolVectorPtr);
223  N_LAS_Vector & nextSol = (*extData.nextSolVectorPtr);
224 
225  N_LAS_Vector & lastSta = (*extData.lastStaVectorPtr);
226  N_LAS_Vector & currSta = (*extData.currStaVectorPtr);
227  N_LAS_Vector & nextSta = (*extData.nextStaVectorPtr);
228  N_LAS_Vector & nextStaDeriv = (*extData.nextStaDerivVectorPtr);
229 
230  N_LAS_Matrix & dQdxMat = (*extData.dQdxMatrixPtr);
231  N_LAS_Matrix & dFdxMat = (*extData.dFdxMatrixPtr);
232 
233  int numRows, numCols;
234  numCols = devLIDs.size();
235  numRows = jacStamp.size();
236 
237  int testSize= (numRows>numCols)?numRows:numCols;
238  if (testSize > saveF.size())
239  {
240  mlData.resizeTestJacSolData(testSize);
241  mlData.resizeTestJacQData(testSize);
242  }
243 
244  int numState = devStateLIDs.size();
245  if (numState > saveCurrState.size())
246  {
247  mlData.resizeTestJacStateData(numState);
248  }
249 
250  int i, j, jCol;
251 
252  if(devJacLIDs.empty())
253  {
254 #ifdef Xyce_DEBUG_DEVICE
255  Report::UserWarning() << instance.getName() << " does not have jacLIDs available";
256 #endif
257 
258  return true;
259  }
260 
261  if (instance.getOrigFlag() && numRows > 0 && numRows == jacStamp.size())
262  {
263 
264  // Zero out all the mlData structures
265  saveF.assign(saveF.size(),0.0);
266  pertF.assign(pertF.size(),0.0);
267  origF.assign(origF.size(),0.0);
268 
269  saveQ.assign(saveQ.size(),0.0);
270  pertQ.assign(pertQ.size(),0.0);
271  origQ.assign(origQ.size(),0.0);
272 
273  saveSoln.assign(saveSoln.size(),0.0);
274  pertSoln.assign(pertSoln.size(),0.0);
275  saveCurrSoln.assign(saveCurrSoln.size(),0.0);
276 
277  saveLastState.assign(saveLastState.size(),0.0);
278  saveCurrState.assign(saveCurrState.size(),0.0);
279  saveNextState.assign(saveNextState.size(),0.0);
280  saveStateDerivs.assign(saveStateDerivs.size(),0.0);
281  for (i=0;i<numJacF.size();++i)
282  {
283  numJacF[i].assign(numJacF[i].size(),0.0);
284  saveJacF[i].assign(saveJacF[i].size(),0.0);
285  devJacF[i].assign(devJacF[i].size(),0.0);
286  diffJacF[i].assign(diffJacF[i].size(),0.0);
287  statusF[i].assign(statusF[i].size(),-1);
288  stencil[i].assign(stencil[i].size(),0);
289 
290  numJacQ[i].assign(numJacQ[i].size(),0.0);
291  saveJacQ[i].assign(saveJacQ[i].size(),0.0);
292  devJacQ[i].assign(devJacQ[i].size(),0.0);
293  diffJacQ[i].assign(diffJacQ[i].size(),0.0);
294  statusQ[i].assign(statusQ[i].size(),-1);
295  }
296 
297  // Save Soln, RHS, and State for this device
298  bool origFlag = instance.getOrigFlag();
299  //for (i=0 ; i<numRows ; ++i)
300  int tmpSize= (numRows>numCols)?numRows:numCols;
301  double sqrtEta=devOptions.testJac_SqrtEta;
302  for (i=0 ; i<tmpSize; ++i)
303  {
304  saveF[i] = Fvec[devLIDs[i]];
305  saveQ[i] = Qvec[devLIDs[i]];
306 
307  saveSoln[i] = nextSol[devLIDs[i]];
308  saveCurrSoln[i] = currSol[devLIDs[i]];
309  pertSoln[i] = sqrtEta * (1.0 + fabs(saveSoln[i]));
310  }
311 
312  for (i=0 ; i<numState ; ++i)
313  {
314  saveLastState[i] = lastSta[devStateLIDs[i]];
315  saveCurrState[i] = currSta[devStateLIDs[i]];
316  saveNextState[i] = nextSta[devStateLIDs[i]];
317  saveStateDerivs[i] = nextStaDeriv[devStateLIDs[i]];
318  }
319 
320  // Save the original matrix for later:
321  for (i=0 ; i<numRows ; ++i)
322  {
323  jCol = devJacLIDs[i].size();
324  for (j=0 ; j<jCol ; ++j)
325  {
326  double valF = dFdxMat[devLIDs[i]][devJacLIDs[i][j]];
327  saveJacF[i][j] = valF;
328  double valQ = dQdxMat[devLIDs[i]][devJacLIDs[i][j]];
329  saveJacQ[i][j] = valQ;
330  }
331  }
332 
333  // Zeroing needs to be done after all saved values are
334  // recorded because there can be multiple references
335  // to the same element
336  for (i=0 ; i<numRows ; ++i)
337  {
338  jCol = devJacLIDs[i].size();
339  for (j=0 ; j<jCol ; ++j)
340  {
341  dFdxMat[devLIDs[i]][devJacLIDs[i][j]] = 0;
342  dQdxMat[devLIDs[i]][devJacLIDs[i][j]] = 0;
343  }
344  }
345 
346  // Now that the original load has been zeroed out, re-load the
347  // analytic contributions, to get the contributions from *just* this
348  // device.
349  instance.loadDAEdQdx ();
350  instance.loadDAEdFdx ();
351 
352  for (i=0 ; i<numRows ; ++i)
353  {
354  devJacF[i].assign(devJacF[i].size(),0.0);
355  devJacQ[i].assign(devJacQ[i].size(),0.0);
356  stencil[i].assign(stencil[i].size(),0);
357 
358  jCol = devJacLIDs[i].size();
359  for (j=0 ; j<jCol ; ++j)
360  {
361  double valF = dFdxMat[devLIDs[i]][devJacLIDs[i][j]];
362  devJacF[i][jacStamp[i][j]] = valF;
363  double valQ = dQdxMat[devLIDs[i]][devJacLIDs[i][j]];
364  devJacQ[i][jacStamp[i][j]] = valQ;
365  stencil[i][jacStamp[i][j]] = 1;
366  }
367  }
368 
369  // zero out the RHS, and re-load, so that we have only the
370  // elements from one device present.
371  for (i=0 ; i<numRows ; ++i)
372  {
373  Fvec[devLIDs[i]] = 0.0;
374  Qvec[devLIDs[i]] = 0.0;
375  }
376  // re-load for just this instance:
377  loadLocalDAEVectors (instance);
378 
379  // Save RHS for just this instance:
380  for (i=0 ; i<numRows ; ++i)
381  {
382  origF[i] = Fvec[devLIDs[i]];
383  origQ[i] = Qvec[devLIDs[i]];
384  }
385 
386  for (i=0 ; i<numRows ; ++i)
387  {
388  jCol = devJacLIDs[i].size();
389  for (j=0 ; j<jCol ; ++j)
390  {
391  statusF[i][jacStamp[i][j]] = -1;
392  statusQ[i][jacStamp[i][j]] = -1;
393  }
394  }
395 
396  // This test, of the merged numRows/numCols, should only be needed 1 time.
397  // Leaving this off by default for now. For reasons, see the header
398  // for the mergeTest function.
399  if (false)
400  {
401  mergeTest (instance, nameVec);
402  }
403 
404  // These are the tolerances for determining jacobian agreement
405  // relT: similar to reltol, fractional error that is allowed
406  // absT: similar to abstol, error that is allowed for small derivatives
407  double relTol=devOptions.testJac_relTol;
408  double absTol=devOptions.testJac_absTol;
409 
410  double ndFdx, adFdx, ddFdx, relError_dFdx;
411  double ndQdx, adQdx, ddQdx, relError_dQdx;
412 
413  bool failedTest = false;
414  for (i=0 ; i<numCols ; ++i)
415  {
416  // Don't bother perturbing gnd.
417  if (nameVec[devLIDs[i]] == "gnd") continue;
418 
419  for (j=0 ; j<numRows ; ++j)
420  {
421  nextSol[devLIDs[j]] = saveSoln[j];
422  currSol[devLIDs[j]] = saveCurrSoln[j];
423  Fvec[devLIDs[j]] = 0.0;
424  Qvec[devLIDs[j]] = 0.0;
425  }
426 
427  for (j=0 ; j<numState ; ++j)
428  {
429  lastSta[devStateLIDs[j]] = saveLastState[j];
430  currSta[devStateLIDs[j]] = saveCurrState[j];
431  nextSta[devStateLIDs[j]] = saveNextState[j];
432  nextStaDeriv[devStateLIDs[j]] = saveStateDerivs[j];
433  }
434 
435  // Perturb the solution.
436  double dX = pertSoln[i];
437  nextSol[devLIDs[i]] += dX;
438 
439  // Re-load the F,Q vectors:
440  loadLocalDAEVectors (instance);
441 
442  for (j=0 ; j<numRows ; ++j)
443  {
444  pertF[j] = Fvec[devLIDs[j]];
445  pertQ[j] = Qvec[devLIDs[j]];
446  }
447 
448 #ifdef Xyce_DEBUG_TESTJAC
449  testDebugHead (instance, nameVec, i, dX);
450 #endif
451 
452  for (j=0 ; j<numRows ; ++j)
453  {
454  // Don't bother with the gnd row.
455  if (nameVec[devLIDs[j]] == "gnd") continue;
456 
457  // if this derivative is not loaded analytically, don't bother
458  // with it.
459  if (stencil[j][i]!=1) continue;
460 
461  double dF = (pertF[j]-origF[j]);
462  double dQ = (pertQ[j]-origQ[j]);
463  numJacF[j][i] = dF/dX;
464  numJacQ[j][i] = dQ/dX;
465 
466  ndFdx = numJacF[j][i];
467  adFdx = devJacF[j][i];
468  ddFdx = fabs(adFdx-ndFdx);
469  relError_dFdx = ddFdx/(relTol*fabs(ndFdx)+absTol);
470 
471  diffJacF[j][i] = ddFdx;
472  relJacF[j][i] = relError_dFdx;
473 
474  ndQdx = numJacQ[j][i];
475  adQdx = devJacQ[j][i];
476  ddQdx = fabs(adQdx-ndQdx);
477  relError_dQdx = ddQdx/(relTol*fabs(ndQdx)+absTol);
478 
479  diffJacQ[j][i] = ddQdx;
480  relJacQ[j][i] = relError_dQdx;
481 
482 #ifdef Xyce_DEBUG_TESTJAC
483  if (isnan(numJacF[j][i]))
484  {
485  testDebugOut (instance, nameVec, i, j);
486  }
487 #endif
488  // if the device is a Inductor, and IC= has been specified,
489  // then skip this term as it is a special case.
490  ExtendedString varNameI(nameVec[devLIDs[i]]); varNameI.toUpper();
491  ExtendedString varNameJ(nameVec[devLIDs[j]]); varNameJ.toUpper();
492  if ( ((solState.dcopFlag) && varNameI[0]=='L' && varNameJ[0]=='L') )
493  {
494  // For the inductor branch current, the matrix element will be
495  // there whether IC= was specified or not.
496  if (adFdx == 1 && ndFdx == 0)
497  {
498  statusF[j][i] = 3;
499  statusQ[j][i] = 3;
500  }
501  }
502  // if the device is a capacitor, and it has a branch current,
503  // that means that IC= has been used, thus it is a "special case"
504  // that should be skipped. The branch current will only be there
505  // for IC=.
506  else if((!(solState.dcopFlag) && varNameI[0]=='C') && varNameJ[0]=='C')
507  {
508  statusF[j][i] = 3;
509  statusQ[j][i] = 3;
510  }
511 
512  if ( statusF[j][i] != 3 )
513  {
514  if (relError_dFdx > 1.0) // failure
515  {
516  statusF[j][i] = -2;
517  failedTest = true;
518  }
519  else // success
520  {
521  statusF[j][i] = 1;
522  }
523 
524  if (relError_dQdx > 1.0) // failure
525  {
526  statusQ[j][i] = -2;
527  failedTest = true;
528  }
529  else // success
530  {
531  statusQ[j][i] = 1;
532  }
533  }
534  }
535 
536 #ifdef Xyce_DEBUG_TESTJAC
537  testDebugTail (instance, nameVec);
538 #endif
539  }
540 
541 #ifdef Xyce_DEBUG_DEVICE
542  // Output Jacobians Differences.
543  // If debug enabled, always output.
544  // If not, only output for failures.
545  printJacobian_ (dout(), instance, nameVec, failedTest);
546 #else
547  if (failedTest)
548  printJacobian_ (lout(), instance, nameVec, failedTest);
549 #endif
550 
551  // Restore jacobian, RHS for this device
552  instance.setOrigFlag(origFlag);
553  tmpSize= (numRows>numCols)?numRows:numCols;
554  for (i=0 ; i<tmpSize; ++i)
555  {
556  Fvec[devLIDs[i]] = saveF[i];
557  Qvec[devLIDs[i]] = saveQ[i];
558  nextSol[devLIDs[i]] = saveSoln[i];
559  currSol[devLIDs[i]] = saveCurrSoln[i];
560  }
561  for (i=0 ; i<numState ; ++i)
562  {
563  lastSta[devStateLIDs[i]] = saveLastState[i];
564  currSta[devStateLIDs[i]] = saveCurrState[i];
565  nextSta[devStateLIDs[i]] = saveNextState[i];
566  nextStaDeriv[devStateLIDs[i]] = saveStateDerivs[i];
567  }
568 
569 
570  for (i=0 ; i<numRows ; ++i)
571  {
572  jCol = devJacLIDs[i].size();
573  for (j=0 ; j<jCol ; ++j)
574  {
575  dFdxMat[devLIDs[i]][devJacLIDs[i][j]] = saveJacF[i][j];
576  dQdxMat[devLIDs[i]][devJacLIDs[i][j]] = saveJacQ[i][j];
577  }
578  }
579  }
580 
581  return true;
582 }
583 
584 //-----------------------------------------------------------------------------
585 // Function : NumericalJacobian::loadLocalDAEVectors
586 // Purpose :
587 // Special Notes : Note this function ignores the B-vector.
588 // Scope : public
589 // Creator : Eric Keiter, SNL, Electrical and Microsystems Modeling
590 // Creation Date : 12/15/06
591 //-----------------------------------------------------------------------------
593 {
594  N_LAS_Vector & currSta = (*extData.currStaVectorPtr);
595  N_LAS_Vector & nextSta = (*extData.nextStaVectorPtr);
596  N_LAS_Vector & nextStaDeriv = (*extData.nextStaDerivVectorPtr);
597  N_LAS_Vector & nextSol = (*extData.nextSolVectorPtr);
598 
599  const std::vector<int> & devStateLIDs = instance.getStaLIDVec();
600  int numState = devStateLIDs.size();
601 
602  instance.updateDependentParameters(nextSol); // this line necessary for expressions
603  instance.updatePrimaryState ();
604 
605  // Assume backward euler integration, so that the time integrator
606  // accessors are not needed.
607  //anaIntPtr_->updateDivDiffs(devLIDs, devStateLIDs);
608  //anaIntPtr_->updateDerivs(devLIDs, devStateLIDs);
609  for (int j=0 ; j<numState ; ++j)
610  {
611  nextStaDeriv[devStateLIDs[j]] =
612  solState.pdt * (nextSta[devStateLIDs[j]]-currSta[devStateLIDs[j]]);
613  }
614 
615  instance.updateSecondaryState ();
616  instance.loadDAEQVector ();
617  instance.loadDAEFVector ();
618 
619  return;
620 }
621 
622 //-----------------------------------------------------------------------------
623 // Function : NumericalJacobian::printJacobian_
624 // Purpose :
625 // Special Notes :
626 // Scope : public
627 // Creator : Eric Keiter, SNL, Electrical and Microsystems Modeling
628 // Creation Date : 12/12/06
629 //-----------------------------------------------------------------------------
631  std::ostream & os,
632  DeviceInstance & instance,
633  const std::vector<std::string> & nameVec,
634  bool failed)
635 {
636  bool NAflag = false;
637  const std::vector<int> & devLIDs = instance.getDevLIDs();
638 
639  // These curly brackets are for scoping only.
640  {
641  const std::vector< std::vector<double> > & numJac = mlData.numJac;
642  const std::vector< std::vector<double> > & anaJac = mlData.devJac;
643  const std::vector< std::vector<double> > & diffJac = mlData.diffJac;
644  const std::vector< std::vector<double> > & relJac = mlData.relJac;
645  const std::vector< std::vector<int> > & stencil = mlData.stencil;
646  const std::vector< std::vector<int> > & status = mlData.status;
647 
648 
649  os << Xyce::section_divider << std::endl;
650  os << "dFdx matrix for " << instance.getName();
651 
652  if (failed)
653  {
654  os << ": JACOBIAN TEST FAILURE";
655  }
656  else
657  {
658  os << ": JACOBIAN TEST SUCCESS";
659  }
660  os << " at time = " << solState.currTime;
661  os << " at Niter = " << solState.newtonIter;
662  os << std::endl;
663  os << " Numerical Analytic absDiff relative Error Status Names (row, col)"<<std::endl;
664 
665  int i,j;
666  int numCols = devLIDs.size();
667  int numRows = (status.size()<numCols)?status.size():numCols;
668 
669  for (i = 0; i < numRows; ++i)
670  {
671  if (nameVec[devLIDs[i]] == "gnd") continue;
672 
673  for (j = 0; j < numCols; ++j)
674  {
675  if (nameVec[devLIDs[j]] == "gnd") continue;
676 
677  // if this variable has not been tested for any reason, skip.
678  if (status[i][j]==-1) continue;
679 
680  // if this derivative is not loaded analytically, skip.
681  if (stencil[i][j]!=1) continue;
682 
683  // Note: JT=jacobian test is there to make this easy to grep.
684  static char tmpChar[128];
685  static char prefix[4];
686 
687  sprintf(prefix,"%s","FT:");
688 
689  if (status[i][j]==-2)
690  {
691  sprintf(tmpChar,"%s %12.4e %12.4e %12.4e %12.4e fail",prefix,
692  numJac[i][j], anaJac[i][j], diffJac[i][j], relJac[i][j]);
693  }
694  else if (status[i][j]==3)
695  {
696  NAflag = true;
697  sprintf(tmpChar,"%s %12.4e %12.4e %12.4e %12.4e NA ",prefix,
698  numJac[i][j], anaJac[i][j], diffJac[i][j], relJac[i][j]);
699  }
700  else
701  {
702  sprintf(tmpChar,"%s %12.4e %12.4e %12.4e %12.4e ",prefix,
703  numJac[i][j], anaJac[i][j], diffJac[i][j], relJac[i][j]);
704  }
705 
706  os << std::string(tmpChar);
707 
708  os << " ("<< nameVec[devLIDs[i]]
709  << ", " << nameVec[devLIDs[j]]
710  << ") "
711  << " row,col=[ " << i << ", " << j << "]" << std::endl;
712 
713 
714  }
715  }
716  }
717 
718  const std::vector< std::vector<double> > & numJac = mlData.numJacQ;
719  const std::vector< std::vector<double> > & anaJac = mlData.devJacQ;
720  const std::vector< std::vector<double> > & diffJac = mlData.diffJacQ;
721  const std::vector< std::vector<double> > & relJac = mlData.relJacQ;
722  const std::vector< std::vector<int> > & stencil = mlData.stencil;
723  const std::vector< std::vector<int> > & status = mlData.statusQ;
724 
725  os << "dQdx matrix for " << instance.getName();
726 
727  if (failed)
728  {
729  os << ": JACOBIAN TEST FAILURE";
730  }
731  else
732  {
733  os << ": JACOBIAN TEST SUCCESS";
734  }
735  os << " at time = " << solState.currTime;
736  os << std::endl;
737  os << " Numerical Analytic absDiff relative Error Status Names (row, col)"<<std::endl;
738 
739  int i,j;
740  int numCols = devLIDs.size();
741  int numRows = (status.size()<numCols)?status.size():numCols;
742 
743  for (i = 0; i < numRows; ++i)
744  {
745  if (nameVec[devLIDs[i]] == "gnd") continue;
746 
747  for (j = 0; j < numCols; ++j)
748  {
749  if (nameVec[devLIDs[j]] == "gnd") continue;
750 
751  // if this variable has not been tested for any reason, skip.
752  if (status[i][j]==-1) continue;
753 
754  // if this derivative is not loaded analytically, skip.
755  if (stencil[i][j]!=1) continue;
756 
757  // Note: QT=jacobian test is there to make this easy to grep.
758  static char tmpChar[128];
759  if (status[i][j]==-2)
760  {
761  sprintf(tmpChar,"QT: %12.4e %12.4e %12.4e %12.4e fail",
762  numJac[i][j], anaJac[i][j], diffJac[i][j], relJac[i][j]);
763  }
764  else if (status[i][j]==3)
765  {
766  NAflag = true;
767  sprintf(tmpChar,"QT: %12.4e %12.4e %12.4e %12.4e NA ",
768  numJac[i][j], anaJac[i][j], diffJac[i][j], relJac[i][j]);
769  }
770  else
771  {
772  sprintf(tmpChar,"QT: %12.4e %12.4e %12.4e %12.4e ",
773  numJac[i][j], anaJac[i][j], diffJac[i][j], relJac[i][j]);
774  }
775 
776  os << std::string(tmpChar);
777 
778  os << " ("<< nameVec[devLIDs[i]]
779  << ", " << nameVec[devLIDs[j]]
780  << ") "
781  << " row,col=[ " << i << ", " << j << "]" << std::endl;
782 
783  }
784  }
785 
786  if(NAflag)
787  os << " Note: NA = untestable special case, such as IC=, etc." << std::endl;
788  os << Xyce::section_divider << std::endl;
789 
790  if (failed)
791  {
792  if (!(devOptions.testJacWarn))
793  {
794  Report::UserError() << "Numerical Jacobian test failure" << std::endl
795  << "If you want this failure to be a warning, rather than an error, "
796  << "run with .options device testjacwarn=1 in the netlist.";
797  }
798  else
799  {
800  Report::UserWarning() << "Numerical Jacobian test failure";
801  }
802  }
803 }
804 
805 //-----------------------------------------------------------------------------
806 // Function : NumericalJacobian::mergeTest
807 //
808 // Purpose : ERK: This function tests merged rows and cols, to make sure
809 // that they are actually equivalent. This is adapted from
810 // a version of this diagnostic by Dave Shirley.
811 //
812 // Another purpose of this test is to avoid testing the same
813 // element (numerical vs. analytic) more than once. I've
814 // chosen to use a different simpler approach for this, using
815 // a matrix stencil. The nice thing about stencils is that
816 // they don't require very many if-statements.
817 //
818 // With the stencil implemented, the only purpose for this test
819 // is essentially to test that the merged rows and cols are
820 // in fact correctly merged. I think that is a low-probability
821 // bug, so for now I'm not calling this test function, in
822 // the interests of speed.
823 //
824 // Finally, I'm leaving this test out, because it relies on
825 // "==" style comparisons of floating point numbers, which can
826 // be dangerous and easy to break.
827 //
828 // However, for historical reasons I'm leaving it in the code.
829 //
830 // Special Notes :
831 // Scope : public
832 // Creator : Eric Keiter, SNL, Electrical and Microsystems Modeling
833 // Creation Date : 12/14/06
834 //-----------------------------------------------------------------------------
835 void NumericalJacobian::mergeTest( DeviceInstance & instance, const std::vector<std::string> & nameVec)
836 {
837  int i,j,k;
838  const std::vector<int> & devLIDs = instance.getDevLIDs();
839  const std::vector< std::vector<double> > & devJac = mlData.devJac;
840  std::vector< std::vector<int> > & status = mlData.status;
841  int numRows, numCols;
842  numRows = devLIDs.size();
843  numCols = numRows;
844 
845  // insure that for any instance this is only checked once.
846  if (!(instance.getMergeRowColChecked()))
847  {
848  for (i=1 ; i<numRows ; ++i)
849  {
850  for (j=0 ; j<i ; ++j)
851  {
852  if (devLIDs[i] == devLIDs[j])
853  {
854  for (k=0 ; k<numCols ; ++k)
855  {
856  if (devJac[i][k] != devJac[j][k])
857  {
858  Report::UserWarning() << "In device " << instance.getName() << " different non-zero values in row merge";
859  }
860  status[i][k] = 2;
861  }
862  for (k=0 ; k<numRows ; ++k)
863  {
864  if (devJac[k][i] != devJac[k][j])
865  {
866  Report::UserWarning() << "In device " << instance.getName() << " different non-zero values in column merge";
867  }
868  status[k][i] = 2;
869  }
870  }
871  }
872  }
873  instance.setMergeRowColChecked(true);
874  }
875 
876 }
877 
878 //-----------------------------------------------------------------------------
879 // Function : NumericalJacobian::testDebugHead
880 // Purpose :
881 // Special Notes :
882 // Scope : public
883 // Creator : Eric Keiter, SNL, Electrical and Microsystems Modeling
884 // Creation Date : 12/14/06
885 //-----------------------------------------------------------------------------
886 void NumericalJacobian::testDebugHead( DeviceInstance & instance, const std::vector<std::string> & nameVec, int i, double dX)
887 {
888  const std::vector<int> & devLIDs = instance.getDevLIDs();
889 
890  Xyce::dout() << Xyce::section_divider<<std::endl;
891  Xyce::dout() << "Perturbing (LID="<<devLIDs[i]<<") " << nameVec[devLIDs[i]] << " by " << dX << std::endl;
892 }
893 
894 //-----------------------------------------------------------------------------
895 // Function : NumericalJacobian::testDebugOut
896 // Purpose :
897 // Special Notes :
898 // Scope : public
899 // Creator : Eric Keiter, SNL, Electrical and Microsystems Modeling
900 // Creation Date : 12/14/06
901 //-----------------------------------------------------------------------------
902 void NumericalJacobian::testDebugOut( DeviceInstance & instance, const std::vector<std::string> & nameVec, int i, int j)
903 {
904  const std::vector<int> & devLIDs = instance.getDevLIDs();
905  const std::vector<double> & pertRHS = mlData.pertRHS;
906  const std::vector<double> & origRHS = mlData.origRHS;
907 
908  const std::vector< std::vector<double> > & numJac = mlData.numJac;
909  const std::vector< std::vector<double> > & relJac = mlData.relJac;
910 
911  Xyce::dout().width(15); Xyce::dout().precision(7); Xyce::dout().setf(std::ios::scientific);
912  Xyce::dout() << "dFdX: ";
913  Xyce::dout() << " (" << devLIDs[j] << ", " << devLIDs[i] << ") ";
914  Xyce::dout() << numJac[j][i];
915  Xyce::dout() << " Forig = " << origRHS[j];
916  Xyce::dout() << " Fperturb = " << pertRHS[j];
917  double dF = -(pertRHS[j]-origRHS[j]);
918  Xyce::dout() << " dF = " << dF;
919  Xyce::dout() << " (" << nameVec[devLIDs[j]] << ", " << nameVec[devLIDs[i]] << ") ";
920  Xyce::dout() << std::endl;
921  Xyce::dout() << " relative error = " << relJac[j][i] << std::endl;
922 }
923 
924 //-----------------------------------------------------------------------------
925 // Function : NumericalJacobian::testDebugTail
926 // Purpose :
927 // Special Notes :
928 // Scope : public
929 // Creator : Eric Keiter, SNL, Electrical and Microsystems Modeling
930 // Creation Date : 12/14/06
931 //-----------------------------------------------------------------------------
932 void NumericalJacobian::testDebugTail( DeviceInstance & instance, const std::vector<std::string> & nameVec)
933 {
934  Xyce::dout() << Xyce::section_divider<<std::endl;
935 }
936 
937 } // namespace Device
938 } // namespace Xyce