Xyce  6.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
N_DEV_2DPDEInstance.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_2DPDEInstance.C,v $
27 //
28 // Purpose : This file contains a lot of the
29 // implementation of the instance class for the two
30 // dimensional PDE based semiconductor device.
31 //
32 // Functions pertaining to the initial setup are in other
33 // files, as are functions relating to mesh handling and
34 // parameter handling.
35 //
36 // Special Notes :
37 //
38 // Creator : Eric R. Keiter, SNL, Parallel Computational Sciences
39 //
40 // Creation Date : 07/05/03
41 //
42 // Revision Information:
43 // ---------------------
44 //
45 // Revision Number: $Revision: 1.152 $
46 //
47 // Revision Date : $Date: 2014/02/24 23:49:18 $
48 //
49 // Current Owner : $Author: tvrusso $
50 //-------------------------------------------------------------------------
51 #include <Xyce_config.h>
52 
53 // ---------- Standard Includes ----------
54 #ifdef Xyce_DEBUG_DEVICE
55 #include <iostream>
56 #endif
57 
58 // ---------- Xyce Includes ----------
59 #include <N_DEV_2DPDE.h>
60 #include <N_DEV_DeviceOptions.h>
61 #include <N_DEV_DeviceMaster.h>
62 #include <N_DEV_ExternData.h>
63 #include <N_DEV_MatrixLoadData.h>
64 #include <N_DEV_PDE_2DMesh.h>
65 #include <N_DEV_SolverState.h>
66 #include <N_DEV_Message.h>
67 #include <N_DEV_SourceData.h>
68 
69 #include <N_DEV_DiodePDE.h>
70 
71 #include <N_LAS_Vector.h>
72 #include <N_LAS_Matrix.h>
73 #include <N_LAS_System.h>
74 #include <N_LAS_Builder.h>
75 
76 #include <N_UTL_BreakPoint.h>
77 #include <N_UTL_Expression.h>
78 
79 namespace Xyce {
80 namespace Device {
81 namespace TwoDPDE {
82 
83 // note that this macros is only a default - it will probably not be used.
84 // default maximum number of nonzero entries in a matrix row
85 static const int MAX_COLS_PER_ROW = 10;
86 
87 
89 {
90  p.addPar("AREA", 1.0, &TwoDPDE::Instance::area)
91  .setExpressionAccess(TIME_DEP);
92  p.addPar("NA", 1.0e+15, &TwoDPDE::Instance::Na)
93  .setExpressionAccess(TIME_DEP);
94  p.addPar("ND", 1.0e+15, &TwoDPDE::Instance::Nd)
95  .setExpressionAccess(TIME_DEP);
96  p.addPar("WJ", 1.0e-4, &TwoDPDE::Instance::WJ)
97  .setExpressionAccess(TIME_DEP);
98  p.addPar("TEMP", 0.0, &TwoDPDE::Instance::Temp)
99  .setExpressionAccess(TIME_DEP);
100  p.addPar("X0", 1.0e-4, &TwoDPDE::Instance::x0_user)
101  .setExpressionAccess(TIME_DEP);
102  p.addPar("XSTART", 0.0, &TwoDPDE::Instance::xstart)
103  .setExpressionAccess(TIME_DEP);
104  p.addPar("YSTART", 0.0, &TwoDPDE::Instance::ystart)
105  .setExpressionAccess(TIME_DEP);
106  p.addPar("XEND", 0.0, &TwoDPDE::Instance::xend)
107  .setExpressionAccess(TIME_DEP);
108  p.addPar("YEND", 0.0, &TwoDPDE::Instance::yend)
109  .setExpressionAccess(TIME_DEP);
110  p.addPar("PH.A1", 0.0, &TwoDPDE::Instance::photoA1)
111  .setExpressionAccess(TIME_DEP);
112  p.addPar("PH.TSTART", 0.0, &TwoDPDE::Instance::photoTstart)
113  .setExpressionAccess(TIME_DEP);
114  p.addPar("PH.TSTOP", 0.0, &TwoDPDE::Instance::photoTstop)
115  .setExpressionAccess(TIME_DEP);
116  p.addPar("INT", 0.0, &TwoDPDE::Instance::intensity)
117  .setExpressionAccess(TIME_DEP);
118  p.addPar("L", 1.0e-3, &TwoDPDE::Instance::deviceLength)
119  .setExpressionAccess(TIME_DEP);
120  p.addPar("W", 1.0e-3, &TwoDPDE::Instance::deviceWidth)
121  .setExpressionAccess(TIME_DEP);
122  p.addPar("MAXVOLTDELTA", 0.025, &TwoDPDE::Instance::maxVoltDelta)
123  .setExpressionAccess(TIME_DEP);
124  p.addPar("PH.TD", 0.0, &TwoDPDE::Instance::photoTd)
125  .setExpressionAccess(TIME_DEP);
126  p.addPar("PH.TR", 0.0, &TwoDPDE::Instance::photoTr)
127  .setExpressionAccess(TIME_DEP);
128  p.addPar("PH.TF", 0.0, &TwoDPDE::Instance::photoTf)
129  .setExpressionAccess(TIME_DEP);
130  p.addPar("PH.PW", 0.0, &TwoDPDE::Instance::photoPw)
131  .setExpressionAccess(TIME_DEP);
132  p.addPar("PH.PER", 0.0, &TwoDPDE::Instance::photoPer)
133  .setExpressionAccess(TIME_DEP);
134  p.addPar("OUTPUTINTERVAL", 0.0, &TwoDPDE::Instance::outputInterval)
135  .setExpressionAccess(TIME_DEP);
136  p.addPar("PENALTYPREFAC", 1.0, &TwoDPDE::Instance::penaltyPrefac)
137  .setGivenMember(&TwoDPDE::Instance::penaltyPrefacGiven)
138  .setExpressionAccess(TIME_DEP);
139  p.addPar("PENALTYPOW", 2.0, &TwoDPDE::Instance::penaltyPow)
140  .setGivenMember(&TwoDPDE::Instance::penaltyPowGiven)
141  .setExpressionAccess(TIME_DEP);
142  p.addPar("PULSEDATA", 0.0, &TwoDPDE::Instance::PulseData)
143  .setExpressionAccess(TIME_DEP);
144 
145  // Set up map for non-double precision variables:
146  p.addPar("MESHFILE", std::string("internal.msh"), &TwoDPDE::Instance::meshFileName);
147  p.addPar("GRADED", false, &TwoDPDE::Instance::gradedJunctionFlag);
148  p.addPar("MOBMODEL", std::string("ARORA"), &TwoDPDE::Instance::mobModelName);
149  p.addPar("BULKMATERIAL", std::string("SI"), &TwoDPDE::Instance::bulkMaterial);
150 #ifdef Xyce_OXIDE_ENABLED
151  p.addPar("ALLOXIDE", false, false, Pars::NO_DEP, &TwoDPDE::Instance::allOxideFlag);
152 #endif
153  p.addPar("DISPLCUR", false, &TwoDPDE::Instance::displCurrentFlag);
154  p.addPar("PHOTOGEN", false, &TwoDPDE::Instance::photogenOnFlag);
155  p.addPar("PH.TYPE", std::string("UNIFORM"), &TwoDPDE::Instance::photoString);
156  p.addPar("TECPLOTLEVEL", 1, &TwoDPDE::Instance::tecplotLevel);
157  p.addPar("INTERPGRIDSIZE", 20, &TwoDPDE::Instance::interpGridSize);
158  p.addPar("SGPLOTLEVEL", 0, &TwoDPDE::Instance::sgplotLevel);
159  p.addPar("GNUPLOTLEVEL", 0, &TwoDPDE::Instance::gnuplotLevel);
160  p.addPar("TXTDATALEVEL", 1, &TwoDPDE::Instance::txtDataLevel);
161  p.addPar("VOLTLIM", false, &TwoDPDE::Instance::voltLimFlag);
162  p.addPar("USEMATRIXGID", false, &TwoDPDE::Instance::useMatrixGIDFlag);
163  p.addPar("USEVECTORGID", false, &TwoDPDE::Instance::useVectorGIDFlag);
164  p.addPar("CONSTBOUNDARY", false, &TwoDPDE::Instance::constBoundaryFlag);
167  p.addPar("CYL", false, &TwoDPDE::Instance::cylGeomFlag);
168  p.addPar("OUTPUTNLPOISSON", false, &TwoDPDE::Instance::outputNLPoisson);
169  p.addPar("PENALTY", false, &TwoDPDE::Instance::penaltyFlag);
170  p.addPar("TYPE", std::string("PNP"), &TwoDPDE::Instance::deviceType);
171  p.addPar("USEOLDNI", false, &TwoDPDE::Instance::useOldNi)
172  .setGivenMember(&TwoDPDE::Instance::useOldNiGiven)
173  .setUnit(U_LOGIC)
174  .setDescription("Flag for using old (inaccurate) intrinsic carrier calculation.");
175 
179 }
180 
181 // Class Instance
182 
183 //-----------------------------------------------------------------------------
184 // Function : Instance::Instance
185 // Purpose : instance block constructor
186 // Special Notes :
187 // Scope : public
188 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
189 // Creation Date : 11/14/01
190 //-----------------------------------------------------------------------------
192  const Configuration & configuration,
193  const InstanceBlock & IB,
194  Model & Miter,
195  const FactoryBlock &factory_block)
196  : DevicePDEInstance(IB, configuration.getInstanceParameters(), factory_block),
197  model_(Miter),
198  Is(1.0e-14),
199  Id(0.0),
200  Emax(0.0),
201  VminExp(0.0),
202  VmaxExp(0.0),
203  dIVec(),
204  penaltyPrefacGiven(false),
205  penaltyPowGiven(false),
206  LeadCurrent1(0.0),
207  LeadCurrent2(0.0),
208  LeadCurrent3(0.0),
209  LeadCurrent4(0.0),
210  LeadCurrent5(0.0),
211  LeadCurrent6(0.0),
212  LeadCurrent7(0.0),
213  LeadCurrent8(0.0),
214  Na(1.0e15),
215  Nd(1.0e15),
216  Vbi(0.0),
217  //Vbi(Vt*log(Na*Nd/(Ni*Ni))),
218  WJ(1.0e-4),
219  XC(0.0),
220  XL(0.0),
221  XR(0.0),
222  NnMax(1.0e15),
223  NpMax(1.0e15),
224  NnMin(1.0e5), // approx...
225  NpMin(1.0e5),
226  useOldNi(false),
227  useOldNiGiven(false),
228  meshFileName(""),
229  deviceType("PNP"),
230  usingInternalMesh(false),
231 
232  deviceInitialized(false),
233  meshPerturbed (false),
234  dopingPerturbed (false),
235  photogenPerturbed (false),
236 
237  numMeshPointsX(11),
238  numMeshPointsY(11),
239  deviceLength(1.0e-3),
240  deviceWidth(1.0e-3),
241  cylGeomFlag(false),
242  penaltyFlag(false),
243  penaltyPrefac(1.0),
244  penaltyPow(2.0),
245  area(1.0),
246 #ifdef Xyce_OXIDE_ENABLED
247  allOxideFlag(false),
248 #endif
249 
250  gradedJunctionFlag(false),
251  calledBeforeSIGB(false),
252  callsOSG (0),
253  callsOTEC(0),
254  callsOTECvec(0),
255  callsOGNU(0),
256  callsOTXT(0),
257 
258  displCurrentFlag(false),
259  constBoundaryFlag(false),
260  calcConductanceFlag(false),
261  equationSet(0),
262  outputInterval(0.0),
263  outputIndex(0),
264  outputNLPoisson(false),
265  lastOutputTime(-10.0),
266  tecplotLevel(0),
267  sgplotLevel(0),
268  gnuplotLevel(0),
269  txtDataLevel(1),
270  interpGridSize(20),
271  voltLimFlag(false),
272  useMatrixGIDFlag(false),
273  useVectorGIDFlag(false),
274  meshContainerPtr(0),
275  meshCopyContainerPtr(0),
276 
277  xVec(),
278  yVec(),
279  CVec(),
280  minDXVec(),
281  areaVec(),
282  VVec(),
283  nnVec(),
284  npVec(),
285  totSrcVec(),
286  RVec(),
287  SVec(),
288  elecPenalty(),
289  holePenalty(),
290  pdElecPenalty(),
291  pdHolePenalty(),
292  unVec(),
293  upVec(),
294  unE_Vec(),
295  upE_Vec(),
296  tnVec(),
297  tpVec(),
298  EfieldVec(),
299  JnVec(),
300  JpVec(),
301  displPotential(),
302  displCurrent(),
303  outputVec(),
304 
305  dRdpVec(),
306  dRdnVec(),
307  dJndn1Vec(),
308  dJndn2Vec(),
309  dJndV1Vec(),
310  dJndV2Vec(),
311  dJpdn1Vec(),
312  dJpdn2Vec(),
313  dJpdV1Vec(),
314  dJpdV2Vec(),
315  boundarySten(),
316  boundaryStenV(),
317  boundaryStenN(),
318  boundaryStenP(),
319  boundaryTest(),
320  boundaryNeighborSten(),
321  Vrowarray(),
322  Vcolarray(),
323  Nrowarray(),
324  Ncolarray(),
325  Prowarray(),
326  Pcolarray(),
327  vOwnVec(), // on processor tag, for voltage variables.
328  nnOwnVec(), // on processor tag, for elec. density variables.
329  npOwnVec(), // on processor tag, for hole density variables.
330  li_Vrowarray(),
331  li_Nrowarray(),
332  li_Prowarray(),
333  li_VoffsetArray(),
334  li_NoffsetArray(),
335  li_PoffsetArray(),
336  MESHtoLID_V(),
337  MESHtoLID_N(),
338  MESHtoLID_P(),
339  aiEdge(),
340  aiEdge_nf(),
341  iNumPlotEdges(0),
342  iNumPlotEdges_nf(0),
343  tmpBCmap(),
344  labelIndex(),
345  labelNameVector(),
346  labelDIMap(),
347  meshNeighborMultiMap(),
348  electrodeMap(),
349  stateDispl(),
350  stateDispl_owned(),
351  li_stateDispl(),
352 
353  numMeshPoints(0),
354  numInterfaceMeshPoints(0),
355  numMeshEdges (0),
356  numMeshCells (0),
357  numMeshLabels (0),
358  maxColsPerRow(MAX_COLS_PER_ROW),
359  numElectrodes (0),
360  condVec(),
361  capVec(),
362  pdTermsAllocated(false),
363  meshToLID(),
364  jacStamp()
365 {
366 
367 #if 0
368  vector<Param>::const_iterator iter_t;
369  vector<Param>::const_iterator begin_t = parameterVec.begin();
370  vector<Param>::const_iterator end_t = parameterVec.end();
371 
372  for (iter_t = begin_t; iter_t != end_t; ++iter_t)
373  {
374  Xyce::dout() << "Tag: " << iter_t->tag();
375  Xyce::dout() << " Value = " << iter_t->sVal() << " Given: " << iter_t->given() << endl;
376  }
377 #endif
378 
379 
380  // Set params to constant default values:
381  setDefaultParams ();
382 
383  // Set params according to instance line and constant defaults from metadata:
384  setParams (IB.params);
385 
386  // Set any non-constant parameter defaults:
387  if (!given("TEMP"))
388  Temp = getDeviceOptions().temp.getImmutableValue<double>();
389 
390  // Calculate any parameters specified as expressions:
392 
393  // calculate dependent (ie computed) params and check for errors:
394  ExtendedString tmpName = meshFileName;
395  tmpName.toLower();
396  meshFileName = tmpName;
397  tmpName = mobModelName;
398  tmpName.toLower();
399  mobModelName = tmpName;
400  tmpName = bulkMaterial;
401  tmpName.toLower();
402  bulkMaterial = tmpName;
403  if (given("PH.TYPE"))
404  {
405  if (photoString == "UNIFORM")
407  else if (photoString == "PULSE")
409  else
410  {
411  std::string msg = "::: ph.type not recognized.\n";
412  N_ERH_ErrorMgr::report( N_ERH_ErrorMgr::DEV_FATAL_0,msg);
413  }
414  }
415 
416  if ( given("TYPE") )
417  {
418  ExtendedString tmpTypeName = deviceType;
419  tmpTypeName.toUpper ();
420  deviceType = tmpTypeName;
421  }
422 
423 #ifdef Xyce_DEBUG_DEVICE
424  if (getDeviceOptions().debugLevel > 0)
425  Xyce::dout() << "Doing standard initialization of ."<< std::endl;
426 #endif
427  bool bsuccess = setupMesh ();
428  bool bs1 = true;
429 
430  bs1 = doAllocations (); bsuccess = bsuccess && bs1;
431  bs1 = setupDINodes (); bsuccess = bsuccess && bs1;
432  bs1 = setupBCEdgeAreas (); bsuccess = bsuccess && bs1;
433  bs1 = checkForElectrodeOverlap ();bsuccess = bsuccess && bs1;
434  bs1 = setupBoundaryStencil (); bsuccess = bsuccess && bs1;
435  bs1 = setupNumVars (); bsuccess = bsuccess && bs1;
436  bs1 = setupLabelIndex (); bsuccess = bsuccess && bs1;
437  bs1 = setupMinDXVector (); bsuccess = bsuccess && bs1;
438  bs1 = setupJacStamp (); bsuccess = bsuccess && bs1;
439  bs1 = setupMiscConstants (); bsuccess = bsuccess && bs1;
440  bs1 = setupScalingVars (); bsuccess = bsuccess && bs1;
441  bs1 = calcDopingProfile (); bsuccess = bsuccess && bs1;
442  bs1 = setupPhotogen (); bsuccess = bsuccess && bs1;
443 
444  deviceInitialized = true;
445  processParams ();
446 
447  devConMap.resize(numExtVars);
448  for (int i=0 ; i<numExtVars ; ++i)
449  devConMap[i] = i;
450 }
451 
452 
453 //-----------------------------------------------------------------------------
454 // Function : Instance::~Instance
455 // Purpose : destructor
456 // Special Notes :
457 // Scope : public
458 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
459 // Creation Date : 11/14/01
460 //-----------------------------------------------------------------------------
462 {
463 
465  if (meshContainerPtr != 0) delete meshContainerPtr;
466 
467  if (DataSaved_ptr != 0) delete DataSaved_ptr;
468 
469  // Loop over the dopeInfoMap (if it is not empty) and delete its contents.
470  if (!(dopeInfoMap.empty()))
471  {
472  std::map<std::string,DopeInfo *>::iterator iter;
473  std::map<std::string,DopeInfo *>::iterator begin = dopeInfoMap.begin();
474  std::map<std::string,DopeInfo *>::iterator end = dopeInfoMap.end ();
475 
476  for(iter=begin;iter!=end;++iter)
477  {
478  if (iter->second != 0) delete iter->second;
479  }
480  }
481 
482  // Loop over the electrodeMap (if it is not empty) and delete its contents.
483  if (!(electrodeMap.empty()))
484  {
485  std::map<std::string, PDE_2DElectrode * >::iterator iterE;
486  std::map<std::string, PDE_2DElectrode * >::iterator beginE = electrodeMap.begin();
487  std::map<std::string, PDE_2DElectrode * >::iterator endE = electrodeMap.end ();
488 
489  for(iterE=beginE;iterE!=endE;++iterE)
490  {
491  if (iterE->second != 0) delete iterE->second;
492  }
493  }
494 }
495 
496 //-----------------------------------------------------------------------------
497 // Function : Instance::constructComposite
498 // Purpose :
499 // Special Notes :
500 // Scope : public
501 // Creator : Dave Shirley, PSSI
502 // Creation Date : 05/09/05
503 //-----------------------------------------------------------------------------
504 CompositeParam *Instance::constructComposite(const std::string & compositeName, const std::string & paramName)
505 {
506  if (compositeName == "DOPINGPROFILES" || compositeName == "REGION")
507  {
508  DopeInfo *n = new DopeInfo();
509  dopeInfoMap[paramName] = n;
510  return (static_cast<CompositeParam *> (n));
511  }
512  if (compositeName == "NODE")
513  {
514  DeviceInterfaceNode dINode;
515  ExtendedString dIName = paramName;
516  dIName.toUpper ();
517 
518  dINode.eName = dIName;
519  dINode.nName = paramName;
520  dINode.given = true;
521  dINode.index = 0;
522 
523  if (dINode.given) ++numElectrodes;
524  if (dINode.given) dIVec.push_back(dINode);
525 
526  PDE_2DElectrode *n = new PDE_2DElectrode();
527  electrodeMap[paramName] = n;
528  return (static_cast<CompositeParam *> (n));
529  }
530  std::string msg =
531  "Instance::constructComposite: unrecognized composite name: ";
532  msg += compositeName;
533  N_ERH_ErrorMgr::report ( N_ERH_ErrorMgr::DEV_FATAL,msg);
534 
535  return NULL;
536 }
537 
538 // Additional Declarations
539 //
540 //-----------------------------------------------------------------------------
541 // Function : Instance::updateIntermdiateVars
542 // Purpose :
543 // Special Notes :
544 //
545 // Scope : public
546 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
547 // Creation Date : 11/14/01
548 //-----------------------------------------------------------------------------
550 {
551  bool bsuccess = true;
552  bool bs1 = true;
553 
554 #ifdef Xyce_DEBUG_DEVICE
555  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
556  {
557  Xyce::dout() << section_divider << "\n";
558  Xyce::dout() << "updateIntermediateVars. name = " << getName() << std::endl;
559  }
560 #endif
561 
562  // This first call, to setInitialGuess, probably won't do
563  // anything, as this function only does
564  // anything the first time that it is called, and it is usually called
565  // from the time integrator at the very beginning of the run.
566  bs1 = setInitialGuess (); bsuccess = bsuccess && bs1;
567  bs1 = obtainSolution (); bsuccess = bsuccess && bs1;
568  bs1 = calcEfield (); bsuccess = bsuccess && bs1;
569  bs1 = calcMobilities (); bsuccess = bsuccess && bs1;
570  bs1 = calcRecombination (); bsuccess = bsuccess && bs1;
571  bs1 = calcElectronCurrent (); bsuccess = bsuccess && bs1;
572  bs1 = calcHoleCurrent (); bsuccess = bsuccess && bs1;
573  bs1 = calcTerminalCurrents (); bsuccess = bsuccess && bs1;
574  bs1 = calcTerminalCharges (); bsuccess = bsuccess && bs1;
575 
576  if (photogenOnFlag)
577  {
578  bs1 = calcPhotogen ();
579  bsuccess = bsuccess && bs1;
580  }
581 
582  if (penaltyFlag)
583  {
584  bs1 = calcPenalty ();
585  bsuccess = bsuccess && bs1;
586  }
587 
588  sumSources ();
589 
590 #ifdef Xyce_DEBUG_DEVICE
591  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
592  {
593  Xyce::dout() << section_divider << std::endl;
594  }
595 #endif
596 
597  return bs1;
598 }
599 
600 //-----------------------------------------------------------------------------
601 // Function : Instance::calcTerminalCurrents
602 // Purpose : Calculates total device current(s) to be used in the
603 // circuit KCL equations.
604 // Special Notes :
605 // Scope : public
606 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
607 // Creation Date : 11/14/01
608 //-----------------------------------------------------------------------------
610 {
611  bool bsuccess = true;
612 
613 #ifdef Xyce_DEBUG_DEVICE
614  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
615  {
616  Xyce::dout() << section_divider << "\n";
617  Xyce::dout() << "calcTerminalCurrents. name = " << getName() << std::endl;
618  }
619 #endif
620 
621  // loop over the device interface nodes, sum the currents going into each one.
622  std::vector<DeviceInterfaceNode>::iterator firstDI = dIVec.begin ();
623  std::vector<DeviceInterfaceNode>::iterator lastDI = dIVec.end ();
624  std::vector<DeviceInterfaceNode>::iterator iterDI;
625 
626  int ind=1;
627  for (iterDI=firstDI; iterDI!=lastDI; ++iterDI, ++ind)
628  {
629  // loop over the nodes of this device interface node:
630 
631  if ( !( meshContainerPtr->labelEdgeType (iterDI->eName) ) ) continue;
632 
633  mLabel * labelPtr = meshContainerPtr->getLabel(iterDI->eName);
634 
635  std::vector<int>::iterator firstI = labelPtr->mNodeVector.begin();
636  std::vector<int>::iterator lastI = labelPtr->mNodeVector.end ();
637  std::vector<int>::iterator iterI;
638 
639  iterDI->currentSum = 0.0;
640 
641  int nodeIndex;
642  for(nodeIndex=0,iterI=firstI;iterI!=lastI;++iterI,++nodeIndex)
643  {
644 
645  // loop over neighbor nodes/edges to get current sum for this node.
646  mNode * nodePtr = meshContainerPtr->getNode(*iterI);
647 
648  std::vector<EDGEINFO>::iterator firstEI = nodePtr->edgeInfoVector.begin();
649  std::vector<EDGEINFO>::iterator lastEI = nodePtr->edgeInfoVector.end ();
650  std::vector<EDGEINFO>::iterator iterEI;
651 
652  double sum = 0.0; // total current for this node
653 
654 #ifdef Xyce_DEBUG_DEVICE
655  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
656  {
657  Xyce::dout() << " --------------- " << std::endl;
658  Xyce::dout() << "name = " << iterDI->eName;
659  Xyce::dout() << " node = " << *iterI << std::endl;
660  }
661 #endif
662 
663  for (iterEI=firstEI;iterEI!=lastEI;++iterEI)
664  {
665  int iedge = iterEI->iedge;
666  int neighbor = iterEI->inode;
667  mEdge * edgePtr = meshContainerPtr->getEdge(iedge);
668  double ilen = edgePtr->ilen;
669 
670  double sign = (*iterI < neighbor) ? 1.0 : -1.0;
671 
672  sum += (sign*JnVec[iedge] + sign*JpVec[iedge])*ilen;
673  }
674 
675 #ifdef Xyce_DEBUG_DEVICE
677  {
678  Xyce::dout() << " sum*scalingVars.a0 = "<< sum*scalingVars.a0 << std::endl;
679  Xyce::dout() << " sum = " << sum << std::endl;
680  Xyce::dout() << " --------------- " << std::endl;
681  }
682 #endif
683  // total scaled current mult. by total scaled area for this node.
684  sum *= scalingVars.a0;
685 
686  iterDI->currentSum += sum;
687  } // iterI loop.
688 
689  iterDI->currentSum *= scalingVars.J0;
690  if (ind == 1)
691  LeadCurrent1 = iterDI->currentSum;
692  else if (ind == 2)
693  LeadCurrent2 = iterDI->currentSum;
694  else if (ind == 3)
695  LeadCurrent3 = iterDI->currentSum;
696  else if (ind == 4)
697  LeadCurrent4 = iterDI->currentSum;
698  else if (ind == 5)
699  LeadCurrent5 = iterDI->currentSum;
700  else if (ind == 6)
701  LeadCurrent6 = iterDI->currentSum;
702  else if (ind == 7)
703  LeadCurrent7 = iterDI->currentSum;
704  else if (ind == 8)
705  LeadCurrent8 = iterDI->currentSum;
706 
707 #ifdef Xyce_DEBUG_DEVICE
708  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
709  {
710  Xyce::dout().setf(std::ios::scientific);
711  Xyce::dout() << " " << iterDI->eName;
712  Xyce::dout() << " Ickt = " << iterDI->currentSum << std::endl;
713  }
714 #endif
715  } // iterDI loop.
716 
717 
718 #ifdef Xyce_DEBUG_DEVICE
719  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
720  {
721  Xyce::dout() << section_divider << std::endl;
722  }
723 #endif
724 
725  return bsuccess;
726 }
727 
728 //-----------------------------------------------------------------------------
729 // Function : Instance::pdTerminalCurrents
730 //
731 // Purpose : This function calculates partial derivatives associated
732 // with the Jacobian loads for the KCL equation rows.
733 //
734 // Special Notes : Originally, this work was performed in the loadJacDD
735 // function. However, some of this information is also needed
736 // for the decoupled 2-level Newton, to calculate the
737 // terminal conductances.
738 //
739 // To calculate the terminal conductances, the following is
740 // needed for each DeviceInterfaceNode:
741 //
742 // dIdVckt - derivative of terminal current w.r.t. Vckt.
743 // This is the also the Jacobian contribution
744 // for the (KCL row, KCL col) entry of the matrix.
745 //
746 // dFdVckt - (col. vector) derivative of the RHS vector
747 // w.r.t. Vckt. This is a vector quantity, and
748 // corresponds to the (*, Vckt) column of the
749 // PDE matrix sub-block.
750 //
751 // dIdX - (row vector) derivative of the terminal current
752 // w.r.t. the vector of PDE solution variables. (ie not
753 // including Vckt, as that is not part of the PDE
754 // domain). This is a vector quantity.
755 // This corresponds to the (KCL row, *) entry of
756 // the matrix, modulo dIdVckt.
757 //
758 // With the "new" boundary conditions, the dFdVckt vector
759 // should only have one nonzero element, which corresponds
760 // to dIdVckt.
761 //
762 // Scope : public
763 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
764 // Creation Date : 12/01/02
765 //-----------------------------------------------------------------------------
767 {
768  bool bsuccess = true;
769 
770  std::vector<DeviceInterfaceNode>::iterator firstDI = dIVec.begin ();
771  std::vector<DeviceInterfaceNode>::iterator lastDI = dIVec.end ();
772  std::vector<DeviceInterfaceNode>::iterator iterDI;
773 
774  std::string msg;
775  std::string semi(bulkMaterial);
776 
777  int nodeIndex;
778  int iedge = 0;
779  int inodeB = 0;
780  double sign = 1.0;
781  double dJndV = 0.0;
782  double dJpdV = 0.0;
783  double dJndn = 0.0;
784  double dJpdp = 0.0;
785  double coef;
786  double nodeArea,ilen,elen;
787 
788  if (!pdTermsAllocated)
789  {
790  allocatePDTerms ();
791  pdTermsAllocated = true;
792  }
793 
794  // first calculate dIdVckt.----------------------------------------
795  for(iterDI=firstDI;iterDI!=lastDI;++iterDI)
796  {
797  iterDI->dIdVckt = 0.0;
798 
799  // if using the old BC, dIdVckt is just zero.
800 #ifdef Xyce_NEW_BC
801 
802 #ifdef Xyce_DEBUG_DEVICE
803  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
804  {
805  Xyce::dout() << Xyce::subsection_divider << std::endl;
806  }
807 #endif
808 
809  if (iterDI->gid ==-1) continue;
810 
811  mLabel * labelPtr = meshContainerPtr->getLabel(iterDI->eName);
812 
813  // obtain the node indices for the current label, loop over them.
814  std::vector<int>::iterator firstI = labelPtr->mNodeVector.begin();
815  std::vector<int>::iterator lastI = labelPtr->mNodeVector.end ();
816  std::vector<int>::iterator iterI;
817 
818  std::vector<EDGEINFO>::iterator firstEI;
819  std::vector<EDGEINFO>::iterator lastEI;
820  std::vector<EDGEINFO>::iterator iterEI;
821 
822  // for the "new" boundary conditions, the currents coming into
823  // the electrode have a direct dependency on the voltage at
824  // the circuit node, so they all contribute to the dependence
825  // of that KCL equation on that node's voltage. The contributions
826  // here are all the equivalent of those that disappear due to
827  // the removal of the boundary mesh nodes from the system
828  // of equations.
829  for(nodeIndex=0,iterI=firstI;iterI!=lastI;++iterI,++nodeIndex)
830  {
831  mNode * nodePtr = meshContainerPtr->getNode(*iterI);
832  firstEI = nodePtr->edgeInfoVector.begin();
833  lastEI = nodePtr->edgeInfoVector.end ();
834 
835  // do the center point first.
836  coef = 0.0;
837  for (iterEI=firstEI; iterEI!=lastEI; ++iterEI)
838  {
839  iedge = iterEI->iedge;
840  inodeB = iterEI->inode;
841  mEdge * edgePtr = meshContainerPtr->getEdge(iedge);
842  ilen = edgePtr->ilen;
843 
844  // if this is a boundary edge, skip it, because the neighbor point
845  // is not part of the solution vector. Only use
846  // those which extend into the interior.
847  if (boundarySten[inodeB]) continue;
848 
849  sign = (*iterI < inodeB) ? 1.0 : -1.0;
850 
851  if (*iterI<inodeB)
852  {
853  dJndV = dJndV1Vec[iedge];
854  dJpdV = dJpdV1Vec[iedge];
855  }
856  else
857  {
858  dJndV = dJndV2Vec[iedge];
859  dJpdV = dJpdV2Vec[iedge];
860  }
861 
862  coef += (sign* dJndV + sign* dJpdV)*ilen;
863  } // end of nn edge loop
864 
865  double tmpsum = (scalingVars.rV0)*coef*scalingVars.J0*scalingVars.a0;
866 
867  iterDI->dIdVckt += tmpsum;
868 
869 #ifdef Xyce_DEBUG_DEVICE
871  {
872  Xyce::dout().setf(std::ios::left);
873  Xyce::dout() << iterDI->eName<< ":";
874  Xyce::dout() << " KCL pdTerminalCurrent row = " << iterDI->gid;
875  Xyce::dout() << " contrib = " << tmpsum;
876  Xyce::dout() << " dIdVckt = " << iterDI->dIdVckt << std::endl;
877  }
878 #endif
879 
880  } // end of node loop
881 
882 #endif // Xyce_NEW_BC
883 
884  } // end of DI loop
885 #ifdef Xyce_NEW_BC
886 #ifdef Xyce_DEBUG_DEVICE
887  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
888  {
889  Xyce::dout() << Xyce::subsection_divider << std::endl;
890  }
891 #endif
892 #endif // Xyce_NEW_BC
893 
894  // Now calculate dFdVckt.----------------------------------------
895 #ifdef Xyce_NEW_BC
896  for(iterDI=firstDI;iterDI!=lastDI;++iterDI)
897  {
898 
899  // zero out the dFdVckt vector.
900  int itmp=0;
901  int size = iterDI->dFdVckt.size();
902  for (itmp=0;itmp<size;++itmp)
903  {
904  iterDI->dFdVckt[itmp] = 0.0;
905  }
906 
907  int numNeighbor = iterDI->neighborNodes.size();
908 
909  int iNeighbor;
910  int dFdVindex = 0;
911  for (iNeighbor=0;iNeighbor<numNeighbor;++iNeighbor)
912  {
913  int inode = iterDI->neighborNodes[iNeighbor];
914 
915  mNode * nodePtr = meshContainerPtr->getNode(inode);
916  nodeArea = nodePtr->area;
917 
918  double coef = 0.0;
919  int iNN=0;
920  for (iNN=0;iNN<nodePtr->cnode;++iNN)
921  {
922  int inodeB = nodePtr->edgeInfoVector[iNN].inode;
923 
924  // if nodeB is not a boundary node, never mind.
925  if (boundarySten[inodeB]!=1) continue;
926 
927  // if it is a boundary node, but not part of the
928  // current boundary, also never mind.
929  if (labelNameVector[inodeB]!= iterDI->eName) continue;
930 
931  ilen = nodePtr->edgeInfoVector[iNN].ilen;
932  elen = nodePtr->edgeInfoVector[iNN].elen;
933  iedge = nodePtr->edgeInfoVector[iNN].iedge;
934 
935  // poisson equation contribution:
936  coef = ilen/elen;
937  coef *= -scalingVars.L0 * matSupport.getRelPerm(semi)/nodeArea;
938  coef *= scalingVars.rV0;
939 
940  iterDI->dFdVckt[dFdVindex] = coef;
941  ++dFdVindex;
942 
943  // electron equation contribution:
944  double dJdV = 0.0;
945  if (inode>inodeB) { dJdV = dJndV1Vec[iedge]; }
946  else { dJdV = dJndV2Vec[iedge]; }
947 
948  coef = ((inode<inodeB)?1.0:-1.0) * dJdV * ilen/nodeArea;
949  coef *= scalingVars.rV0;
950 
951  iterDI->dFdVckt[dFdVindex] = coef;
952  ++dFdVindex;
953 
954  // hole equation contribution:
955  dJdV = 0.0;
956  if (inode>inodeB) { dJdV = dJpdV1Vec[iedge]; }
957  else { dJdV = dJpdV2Vec[iedge]; }
958 
959  coef = -((inode<inodeB)?1.0:-1.0) * dJdV * ilen/nodeArea;
960  coef *= scalingVars.rV0;
961 
962  iterDI->dFdVckt[dFdVindex] = coef;
963  ++dFdVindex;
964  }
965  }
966 #ifdef Xyce_DEBUG_DEVICE
968  {
969  Xyce::dout() << "-----" << std::endl;
970  Xyce::dout() << "neighbor nodes for boundary: " << iterDI->eName << std::endl;
971  Xyce::dout() << "-----" << std::endl;
972  for (iNeighbor=0;iNeighbor<numNeighbor;++iNeighbor)
973  {
974  int inode = iterDI->neighborNodes[iNeighbor];
975  Xyce::dout() << "\t"<<iNeighbor<<" "<< inode << std::endl;
976  }
977 
978  Xyce::dout() << "-----" << std::endl;
979  Xyce::dout() << "dFdVckt vector for boundary: " << iterDI->eName << std::endl;
980  Xyce::dout() << "-----" << std::endl;
981 
982  iNeighbor=0;
983  int idf = 0;
984  for (iNeighbor=0;iNeighbor<numNeighbor;++iNeighbor)
985  {
986  int inode = iterDI->neighborNodes[iNeighbor];
987  int Vrow = Vrowarray[inode];
988  int Nrow = Nrowarray[inode];
989  int Prow = Prowarray[inode];
990  mNode * nodePtr = meshContainerPtr->getNode(inode);
991 
992  double dfdV = 0.0;
993 
994  int iNN=0;
995  for (iNN=0;iNN<nodePtr->cnode;++iNN)
996  {
997  int inodeB = nodePtr->edgeInfoVector[iNN].inode;
998 
999  // if nodeB is not a boundary node, never mind.
1000  if (boundarySten[inodeB]!=1) continue;
1001 
1002  // if it is a boundary node, but not part of the
1003  // current boundary, also never mind.
1004  if (labelNameVector[inodeB]!= iterDI->eName) continue;
1005 
1006  dfdV = iterDI->dFdVckt[idf];
1007  Xyce::dout() << "\t"<<idf;
1008  Xyce::dout() << " \tv_"<<inode<<"\t"<<Vrow<<"\t"<<dfdV<< std::endl;
1009  ++idf;
1010 
1011  dfdV = iterDI->dFdVckt[idf];
1012  Xyce::dout() << "\t"<<idf;
1013  Xyce::dout() << " \tn_"<<inode<<"\t"<<Nrow<<"\t"<<dfdV<< std::endl;
1014  ++idf;
1015 
1016  dfdV = iterDI->dFdVckt[idf];
1017  Xyce::dout() << "\t"<<idf;
1018  Xyce::dout() << " \tp_"<<inode<<"\t"<<Prow<<"\t"<<dfdV<< std::endl;
1019  ++idf;
1020  }// end of iNN loop
1021  } // end of iNeighbor loop
1022  Xyce::dout() << "-----" << std::endl;
1023  }
1024 #endif
1025  }
1026 #else // old BC not set up yet.
1027 
1028 #endif // Xyce_NEW_BC
1029 
1030  // now do dIdX.-------------------------------------------------
1031  double Vcoef;
1032  double Ncoef;
1033  double Pcoef;
1034  for(iterDI=firstDI;iterDI!=lastDI;++iterDI)
1035  {
1036  mLabel * labelPtr = meshContainerPtr->getLabel(iterDI->eName);
1037 
1038  // zero out the dIdX vector.
1039  int numdIdX = iterDI->dIdX.size ();
1040  for (int j=0;j<numdIdX;++j)
1041  {
1042  iterDI->dIdX[j] = 0.0;
1043  }
1044 
1045  // obtain the node indices for the current label, loop over them.
1046  // for each edge node, add an extra column entry to the colarray.
1047 
1048  std::vector<int>::iterator firstI = labelPtr->mNodeVector.begin();
1049  std::vector<int>::iterator lastI = labelPtr->mNodeVector.end ();
1050  std::vector<int>::iterator iterI;
1051 
1052  std::vector<EDGEINFO>::iterator firstEI;
1053  std::vector<EDGEINFO>::iterator lastEI;
1054  std::vector<EDGEINFO>::iterator iterEI;
1055 
1056  int iVcol = 0;
1057  int iNcol = 0;
1058  int iPcol = 0;
1059  int col1;
1060  int cnt2;
1061  bool bmatch;
1062  for(nodeIndex=0,iterI=firstI;iterI!=lastI;++iterI,++nodeIndex)
1063  {
1064  mNode * nodePtr = meshContainerPtr->getNode(*iterI);
1065  firstEI = nodePtr->edgeInfoVector.begin();
1066  lastEI = nodePtr->edgeInfoVector.end ();
1067 
1068  // do the center point first.
1069  Vcoef = 0.0;
1070  Ncoef = 0.0;
1071  Pcoef = 0.0;
1072  for (iterEI=firstEI; iterEI!=lastEI; ++iterEI)
1073  {
1074  iedge = iterEI->iedge;
1075  inodeB = iterEI->inode;
1076  sign = (*iterI < inodeB) ? 1.0 : -1.0;
1077  mEdge * edgePtr = meshContainerPtr->getEdge(iedge);
1078  ilen = edgePtr->ilen;
1079 
1080  if (*iterI<inodeB)
1081  {
1082  dJndV = dJndV1Vec[iedge];
1083  dJpdV = dJpdV1Vec[iedge];
1084  dJndn = dJndn1Vec[iedge];
1085  dJpdp = dJpdn1Vec[iedge];
1086  }
1087  else
1088  {
1089  dJndV = dJndV2Vec[iedge];
1090  dJpdV = dJpdV2Vec[iedge];
1091  dJndn = dJndn2Vec[iedge];
1092  dJpdp = dJpdn2Vec[iedge];
1093  }
1094 
1095  Vcoef += (sign* dJndV + sign* dJpdV)*ilen;
1096  Ncoef += (sign* dJndn)*ilen;
1097  Pcoef += (sign* dJpdp)*ilen;
1098 
1099  } // end of nn edge loop
1100 
1101  col1 = iterDI->Vcol[iVcol];
1102  if (col1 != -1)
1103  {
1104  // find this column:
1105  bmatch = false;
1106  int size = iterDI->dIdXcols.size();
1107  for (cnt2=0;cnt2<size;++cnt2)
1108  {
1109  if (iterDI->dIdXcols[cnt2] == col1)
1110  { bmatch = true; break; }
1111  }
1112  if (!bmatch)
1113  {
1114  msg = "pdTerminalCurrents: Could not find a column match in dIdXcols";
1115  N_ERH_ErrorMgr::report ( N_ERH_ErrorMgr::DEV_FATAL,msg);
1116  }
1117 
1118  iterDI->dIdX[cnt2] += Vcoef*scalingVars.J0*scalingVars.a0;
1119 
1120  Xyce::dout() << iterDI->eName;
1121  }
1122 
1123  col1 = iterDI->Ncol[iNcol];
1124  if (col1 != -1)
1125  {
1126  // find this column:
1127  bmatch = false;
1128  int size = iterDI->dIdXcols.size();
1129  for (cnt2=0;cnt2<size;++cnt2)
1130  {
1131  if (iterDI->dIdXcols[cnt2] == col1)
1132  { bmatch = true; break; }
1133  }
1134  if (!bmatch)
1135  {
1136  msg = "pdTerminalCurrents: Could not find a column match in dIdXcols";
1137  N_ERH_ErrorMgr::report ( N_ERH_ErrorMgr::DEV_FATAL,msg);
1138  }
1139 
1140  iterDI->dIdX[cnt2] += Ncoef*scalingVars.J0*scalingVars.a0;
1141  }
1142 
1143  col1 = iterDI->Pcol[iPcol];
1144  if (col1 != -1)
1145  {
1146  // find this column:
1147  bmatch = false;
1148  int size = iterDI->dIdXcols.size();
1149  for (cnt2=0;cnt2<size;++cnt2)
1150  {
1151  if (iterDI->dIdXcols[cnt2] == col1)
1152  { bmatch = true; break; }
1153  }
1154  if (!bmatch)
1155  {
1156  msg = "pdTerminalCurrents: Could not find a column match in dIdXcols";
1157  N_ERH_ErrorMgr::report ( N_ERH_ErrorMgr::DEV_FATAL,msg);
1158  }
1159 
1160  iterDI->dIdX[cnt2] += Pcoef*scalingVars.J0*scalingVars.a0;
1161  }
1162  ++iVcol;
1163  ++iNcol;
1164  ++iPcol;
1165 
1166  // loop over the edges connected to the current node,
1167  // and do the neighbor point dependencies.
1168  for (iterEI=firstEI; iterEI!=lastEI; ++iterEI,++iVcol,++iNcol,++iPcol)
1169  {
1170  iedge = iterEI->iedge;
1171  inodeB = iterEI->inode;
1172  sign = (*iterI < inodeB) ? 1.0 : -1.0;
1173  mEdge * edgePtr = meshContainerPtr->getEdge(iedge);
1174  ilen = edgePtr->ilen;
1175 
1176  if (*iterI>inodeB)
1177  {
1178  dJndV = dJndV1Vec[iedge];
1179  dJpdV = dJpdV1Vec[iedge];
1180  dJndn = dJndn1Vec[iedge];
1181  dJpdp = dJpdn1Vec[iedge];
1182  }
1183  else
1184  {
1185  dJndV = dJndV2Vec[iedge];
1186  dJpdV = dJpdV2Vec[iedge];
1187  dJndn = dJndn2Vec[iedge];
1188  dJpdp = dJpdn2Vec[iedge];
1189  }
1190 
1191  Vcoef = (sign* dJndV + sign* dJpdV)*ilen;
1192  Ncoef = (sign* dJndn)*ilen;
1193  Pcoef = (sign* dJpdp)*ilen;
1194 
1195  col1 = iterDI->Vcol[iVcol];
1196  if (col1 != -1)
1197  {
1198  // find this column:
1199  bmatch = false;
1200  int size = iterDI->dIdXcols.size();
1201  for (cnt2=0;cnt2<size;++cnt2)
1202  {
1203  if (iterDI->dIdXcols[cnt2] == col1)
1204  { bmatch = true; break; }
1205  }
1206  if (!bmatch)
1207  {
1208  msg = "pdTerminalCurrents: Could not find a column match in dIdXcols";
1209  N_ERH_ErrorMgr::report ( N_ERH_ErrorMgr::DEV_FATAL,msg);
1210  }
1211 
1212  iterDI->dIdX[cnt2] += Vcoef*scalingVars.J0*scalingVars.a0;
1213  }
1214 
1215  col1 = iterDI->Ncol[iNcol];
1216  if (col1 != -1)
1217  {
1218  // find this column:
1219  bmatch = false;
1220  for (cnt2=0;cnt2<iterDI->dIdXcols.size();++cnt2)
1221  {
1222  if (iterDI->dIdXcols[cnt2] == col1)
1223  { bmatch = true; break; }
1224  }
1225  if (!bmatch)
1226  {
1227  msg = "pdTerminalCurrents: Could not find a column match in dIdXcols";
1228  N_ERH_ErrorMgr::report ( N_ERH_ErrorMgr::DEV_FATAL,msg);
1229  }
1230 
1231  iterDI->dIdX[cnt2] += Ncoef*scalingVars.J0*scalingVars.a0;
1232  }
1233 
1234  col1 = iterDI->Pcol[iPcol];
1235  if (col1 != -1)
1236  {
1237  // find this column:
1238  bmatch = false;
1239  int size = iterDI->dIdXcols.size();
1240  for (cnt2=0;cnt2<size;++cnt2)
1241  {
1242  if (iterDI->dIdXcols[cnt2] == col1)
1243  { bmatch = true; break; }
1244  }
1245  if (!bmatch)
1246  {
1247  msg = "pdTerminalCurrents: Could not find a column match in dIdXcols";
1248  N_ERH_ErrorMgr::report ( N_ERH_ErrorMgr::DEV_FATAL,msg);
1249  }
1250 
1251  iterDI->dIdX[cnt2] += Pcoef*scalingVars.J0*scalingVars.a0;
1252  }
1253  } // end of nn edge loop
1254  } // end of node loop
1255  } // end of DI loop
1256 
1257 #ifdef Xyce_DEBUG_DEVICE
1258  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
1259  {
1260  for(iterDI=firstDI;iterDI!=lastDI;++iterDI)
1261  {
1262  int size = iterDI->dIdXcols.size();
1263  int size2= iterDI->dIdX.size ();
1264  Xyce::dout() << "dIdX for electrode: " << iterDI->eName << std::endl;
1265  for (int ididx=0;ididx<size;++ididx)
1266  {
1267  Xyce::dout() << "\t"<< iterDI->dIdXcols[ididx];
1268  Xyce::dout() << "\t"<< iterDI->dIdX[ididx] << std::endl;
1269  }
1270  }
1271  Xyce::dout() << "Done with Instance::pdTerminalCurrents" << std::endl;
1272  }
1273 #endif
1274 
1275  return bsuccess;
1276 }
1277 
1278 //-----------------------------------------------------------------------------
1279 // Function : Instance::calcTerminalCharges
1280 // Purpose : Calculates total terminal charge for each electrode.
1281 //
1282 // Special Notes : This is used in capacitance extraction calculations.
1283 //
1284 // The charge will be equal to surface integral of the
1285 // normal electric field.
1286 //
1287 // This function is still not quite finished.
1288 //
1289 // Scope : public
1290 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1291 // Creation Date : 05/05/03
1292 //-----------------------------------------------------------------------------
1294 {
1295  bool bsuccess = true;
1296 
1297 #ifdef Xyce_DEBUG_DEVICE
1298  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
1299  {
1300  Xyce::dout() << section_divider << "\n";
1301  Xyce::dout() << "calcTerminalCharges. name = " << getName() << std::endl;
1302  }
1303 #endif
1304 
1305  // loop over the device interface nodes, sum the currents going into each one.
1306  std::vector<DeviceInterfaceNode>::iterator firstDI = dIVec.begin ();
1307  std::vector<DeviceInterfaceNode>::iterator lastDI = dIVec.end ();
1308  std::vector<DeviceInterfaceNode>::iterator iterDI;
1309 
1310  for (iterDI=firstDI; iterDI!=lastDI; ++iterDI)
1311  {
1312  // loop over the nodes of this device interface node:
1313 
1314  if ( !( meshContainerPtr->labelEdgeType (iterDI->eName) ) ) continue;
1315 
1316  mLabel * labelPtr = meshContainerPtr->getLabel(iterDI->eName);
1317 
1318  std::vector<int>::iterator firstI = labelPtr->mNodeVector.begin();
1319  std::vector<int>::iterator lastI = labelPtr->mNodeVector.end ();
1320  std::vector<int>::iterator iterI;
1321 
1322  iterDI->chargeSum = 0.0;
1323 
1324  int nodeIndex;
1325  for(nodeIndex=0,iterI=firstI;iterI!=lastI;++iterI,++nodeIndex)
1326  {
1327 
1328  // loop over neighbor nodes/edges to get charge sum for this node.
1329  mNode * nodePtr = meshContainerPtr->getNode(*iterI);
1330 
1331  std::vector<EDGEINFO>::iterator firstEI = nodePtr->edgeInfoVector.begin();
1332  std::vector<EDGEINFO>::iterator lastEI = nodePtr->edgeInfoVector.end ();
1333  std::vector<EDGEINFO>::iterator iterEI;
1334 
1335  double sum = 0.0; // total charge for this node
1336 
1337 #ifdef Xyce_DEBUG_DEVICE
1338  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
1339  {
1340  Xyce::dout() << " --------------- " << std::endl;
1341  Xyce::dout() << "name = " << iterDI->eName;
1342  Xyce::dout() << " node = " << *iterI << std::endl;
1343  }
1344 #endif
1345 
1346  for (iterEI=firstEI;iterEI!=lastEI;++iterEI)
1347  {
1348  int iedge = iterEI->iedge;
1349  int neighbor = iterEI->inode;
1350 
1351  mEdge * edgePtr = meshContainerPtr->getEdge(iedge);
1352  double ilen = edgePtr->ilen;
1353 
1354  double sign = (*iterI < neighbor) ? +1.0 : -1.0;
1355 
1356  double contrib = sign * eSi * e0 * scalingVars.E0 * EfieldVec[iedge] * ilen;
1357  sum += contrib;
1358 
1359 #ifdef Xyce_DEBUG_DEVICE
1361  {
1362  Xyce::dout() << "neighbor = "<< neighbor;
1363  Xyce::dout() << " Efield = " << EfieldVec[iedge];
1364  Xyce::dout() << " contrib = " << contrib << std::endl;
1365  }
1366 #endif
1367  }
1368 
1369  double tmp = scalingVars.a0;
1370 #ifdef Xyce_DEBUG_DEVICE
1371  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
1372  {
1373  Xyce::dout() << " sum*scalingVars.a0 = "<< sum*tmp << std::endl;
1374  Xyce::dout() << " sum = " << sum << std::endl;
1375  Xyce::dout() << " --------------- " << std::endl;
1376  }
1377 #endif
1378  // total scaled charge mult. by total scaled area for this node.
1379  sum *= tmp;
1380 
1381  iterDI->chargeSum += sum;
1382 
1383  } // iterI loop.
1384 
1385 #ifdef Xyce_DEBUG_DEVICE
1386  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
1387  {
1388  Xyce::dout().setf(std::ios::scientific);
1389  Xyce::dout() << " " << iterDI->eName;
1390  Xyce::dout() << " terminal charge = " << iterDI->chargeSum << std::endl;
1391  }
1392 #endif
1393  } // iterDI loop.
1394 
1395 
1396 #ifdef Xyce_DEBUG_DEVICE
1397  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
1398  {
1399  Xyce::dout() << section_divider << std::endl;
1400  }
1401 #endif
1402 
1403  return bsuccess;
1404 }
1405 
1406 //-----------------------------------------------------------------------------
1407 // Function : Instance::pdTerminalCharges
1408 //
1409 // Purpose : Sets up derivatives used in calculating capacitance.
1410 //
1411 // These quantities are not used for the 2-level Newton,
1412 // but can be used to obtain interesting information. Just
1413 // like the 2-level Newton is used to obtain lumped
1414 // parameter conductances, this stuff is analogously used
1415 // to obtain lumped parameter capacitances.
1416 //
1417 // dQdVckt - derivative of terminal charge w.r.t. Vckt.
1418 // This is analogous to dIdVckt, from pdTerminalCurrents.
1419 //
1420 // dQdX - (row vector) derivative of the terminal charge
1421 // w.r.t. the vector of PDE solution variables. (ie not
1422 // including Vckt, as that is not part of the PDE
1423 // domain). This is a vector quantity.
1424 //
1425 // This is analogous to the quantity dIdX, which is
1426 // calculated in the function pdTerminalCurrents.
1427 //
1428 // Special Notes :
1429 // Scope : public
1430 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1431 // Creation Date : 05/05/03
1432 //-----------------------------------------------------------------------------
1434 {
1435  bool bsuccess = true;
1436 
1437  std::vector<DeviceInterfaceNode>::iterator firstDI = dIVec.begin ();
1438  std::vector<DeviceInterfaceNode>::iterator lastDI = dIVec.end ();
1439  std::vector<DeviceInterfaceNode>::iterator iterDI;
1440 
1441  std::string msg;
1442 
1443  int nodeIndex;
1444  int iedge = 0;
1445  int inodeB = 0;
1446  double sign = 1.0;
1447  double dEdV = 0.0;
1448  double coef;
1449 
1450  if (!pdTermsAllocated)
1451  {
1452  allocatePDTerms ();
1453  pdTermsAllocated = true;
1454  }
1455 
1456  // first calculate dQdVckt.----------------------------------------
1457  for(iterDI=firstDI;iterDI!=lastDI;++iterDI)
1458  {
1459  iterDI->dQdVckt = 0.0;
1460 
1461  // if using the old BC, dQdVckt is just zero.
1462 #ifdef Xyce_NEW_BC
1463 
1464 #ifdef Xyce_DEBUG_DEVICE
1465  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
1466  {
1467  Xyce::dout() << Xyce::subsection_divider << std::endl;
1468  }
1469 #endif
1470 
1471  if (iterDI->gid ==-1) continue;
1472 
1473  mLabel * labelPtr = meshContainerPtr->getLabel(iterDI->eName);
1474 
1475  // obtain the node indices for the current label, loop over them.
1476  std::vector<int>::iterator firstI = labelPtr->mNodeVector.begin();
1477  std::vector<int>::iterator lastI = labelPtr->mNodeVector.end ();
1478  std::vector<int>::iterator iterI;
1479 
1480  std::vector<EDGEINFO>::iterator firstEI;
1481  std::vector<EDGEINFO>::iterator lastEI;
1482  std::vector<EDGEINFO>::iterator iterEI;
1483 
1484  // for the "new" boundary conditions, the currents coming into
1485  // the electrode have a direct dependency on the voltage at
1486  // the circuit node, so they all contribute to the dependence
1487  // of that KCL equation on that node's voltage. The contributions
1488  // here are all the equivalent of those that disappear due to
1489  // the removal of the boundary mesh nodes from the system
1490  // of equations.
1491  for(nodeIndex=0,iterI=firstI;iterI!=lastI;++iterI,++nodeIndex)
1492  {
1493  mNode * nodePtr = meshContainerPtr->getNode(*iterI);
1494  firstEI = nodePtr->edgeInfoVector.begin();
1495  lastEI = nodePtr->edgeInfoVector.end ();
1496 
1497  // do the center point first.
1498  coef = 0.0;
1499  for (iterEI=firstEI; iterEI!=lastEI; ++iterEI)
1500  {
1501  iedge = iterEI->iedge;
1502  inodeB = iterEI->inode;
1503 
1504  mEdge * edgePtr = meshContainerPtr->getEdge(iedge);
1505  double elen = edgePtr->elen;
1506  double ilen = edgePtr->ilen;
1507 
1508  // if this is a boundary edge, skip it, because the neighbor point
1509  // is not part of the solution vector. Only use
1510  // those which extend into the interior.
1511  if (boundarySten[inodeB]) continue;
1512 
1513  //sign = (*iterI < inodeB) ? 1.0 : -1.0;
1514  sign = 1.0;
1515 
1516  dEdV = (1.0/elen);
1517 
1518  coef += sign* dEdV * ilen;
1519  } // end of nn edge loop
1520 
1521  double tmpsum = (scalingVars.rV0)*eSi*e0*coef*scalingVars.E0*scalingVars.a0;
1522 
1523  iterDI->dQdVckt += tmpsum;
1524 
1525 #ifdef Xyce_DEBUG_DEVICE
1527  {
1528  Xyce::dout().setf(std::ios::left);
1529  Xyce::dout() << iterDI->eName<<":";
1530  Xyce::dout() << " KCL pdTerminalCharges row = " << iterDI->gid;
1531  Xyce::dout() << " contrib = " << tmpsum;
1532  Xyce::dout() << " dQdVckt = " << iterDI->dQdVckt << std::endl;
1533  }
1534 #endif
1535 
1536  } // end of node loop
1537 
1538 #endif // Xyce_NEW_BC
1539 
1540  } // end of DI loop
1541 
1542 #ifdef Xyce_NEW_BC
1543 #ifdef Xyce_DEBUG_DEVICE
1544  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
1545  {
1546  Xyce::dout() << Xyce::subsection_divider << std::endl;
1547  }
1548 #endif
1549 #endif // Xyce_NEW_BC
1550 
1551  // now do dQdX.-------------------------------------------------
1552  double Vcoef;
1553  for(iterDI=firstDI;iterDI!=lastDI;++iterDI)
1554  {
1555  mLabel * labelPtr = meshContainerPtr->getLabel(iterDI->eName);
1556 
1557  int numdQdX = iterDI->dQdX.size ();
1558  for (int j=0;j<numdQdX;++j)
1559  {
1560  iterDI->dQdX[j] = 0.0;
1561  }
1562 
1563  // obtain the node indices for the current label, loop over them.
1564  // for each edge node, add an extra column entry to the colarray.
1565 
1566  std::vector<int>::iterator firstI = labelPtr->mNodeVector.begin();
1567  std::vector<int>::iterator lastI = labelPtr->mNodeVector.end ();
1568  std::vector<int>::iterator iterI;
1569 
1570  std::vector<EDGEINFO>::iterator firstEI;
1571  std::vector<EDGEINFO>::iterator lastEI;
1572  std::vector<EDGEINFO>::iterator iterEI;
1573 
1574  int iVcol = 0;
1575  int iNcol = 0;
1576  int iPcol = 0;
1577  int col1;
1578  int cnt2;
1579  bool bmatch;
1580  for(nodeIndex=0,iterI=firstI;iterI!=lastI;++iterI,++nodeIndex)
1581  {
1582  mNode * nodePtr = meshContainerPtr->getNode(*iterI);
1583  firstEI = nodePtr->edgeInfoVector.begin();
1584  lastEI = nodePtr->edgeInfoVector.end ();
1585 
1586  // do the center point first.
1587  Vcoef = 0.0;
1588  for (iterEI=firstEI; iterEI!=lastEI; ++iterEI)
1589  {
1590  iedge = iterEI->iedge;
1591  inodeB = iterEI->inode;
1592  //sign = (*iterI < inodeB) ? 1.0 : -1.0;
1593  sign = 1.0;
1594 
1595  mEdge * edgePtr = meshContainerPtr->getEdge(iedge);
1596  double elen = edgePtr->elen;
1597  double ilen = edgePtr->ilen;
1598 
1599  dEdV = (1.0/elen);
1600  Vcoef += sign* dEdV * ilen;
1601 
1602  } // end of nn edge loop
1603 
1604  col1 = iterDI->Vcol[iVcol];
1605  if (col1 != -1)
1606  {
1607  // find this column:
1608  bmatch = false;
1609  for (cnt2=0;cnt2<iterDI->dIdXcols.size();++cnt2)
1610  {
1611  if (iterDI->dIdXcols[cnt2] == col1)
1612  { bmatch = true; break; }
1613  }
1614  if (!bmatch)
1615  {
1616  msg = "pdTerminalCharges: Could not find a column match in dIdXcols";
1617  N_ERH_ErrorMgr::report ( N_ERH_ErrorMgr::DEV_FATAL,msg);
1618  }
1619  iterDI->dQdX[cnt2] += Vcoef*eSi*e0*scalingVars.E0*scalingVars.a0;
1620  }
1621 
1622  ++iVcol;
1623 
1624  // loop over the edges connected to the current node,
1625  // and do the neighbor point dependencies.
1626  for (iterEI=firstEI; iterEI!=lastEI; ++iterEI,++iVcol,++iNcol,++iPcol)
1627  {
1628  iedge = iterEI->iedge;
1629  inodeB = iterEI->inode;
1630  //sign = (*iterI < inodeB) ? 1.0 : -1.0;
1631  sign = -1.0;
1632 
1633  mEdge * edgePtr = meshContainerPtr->getEdge(iedge);
1634  double elen = edgePtr->elen;
1635  double ilen = edgePtr->ilen;
1636 
1637  dEdV = (1.0/elen);
1638  Vcoef = sign* dEdV * ilen;
1639 
1640  col1 = iterDI->Vcol[iVcol];
1641  if (col1 != -1)
1642  {
1643  // find this column:
1644  bmatch = false;
1645  for (cnt2=0;cnt2<iterDI->dIdXcols.size();++cnt2)
1646  {
1647  if (iterDI->dIdXcols[cnt2] == col1)
1648  { bmatch = true; break; }
1649  }
1650  if (!bmatch)
1651  {
1652  msg = "pdTerminalCharges: Could not find a column match in dIdXcols";
1653  N_ERH_ErrorMgr::report ( N_ERH_ErrorMgr::DEV_FATAL,msg);
1654  }
1655 
1656  iterDI->dQdX[cnt2] += Vcoef*eSi*e0*scalingVars.E0*scalingVars.a0;
1657  }
1658  } // end of nn edge loop
1659  } // end of node loop
1660  } // end of DI loop
1661 
1662 #ifdef Xyce_DEBUG_DEVICE
1663  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
1664  {
1665  for(iterDI=firstDI;iterDI!=lastDI;++iterDI)
1666  {
1667  int size = iterDI->dIdXcols.size();
1668  int size2= iterDI->dQdX.size ();
1669  Xyce::dout() << "dQdX for electrode: " << iterDI->eName << std::endl;
1670  for (int ididx=0;ididx<size;++ididx)
1671  {
1672  Xyce::dout() << "\t"<< iterDI->dIdXcols[ididx];
1673  Xyce::dout() << "\t"<< iterDI->dQdX[ididx] << std::endl;
1674  }
1675  }
1676  Xyce::dout() << "Done with Instance::pdTerminalCharges" << std::endl;
1677  }
1678 #endif
1679 
1680  return bsuccess;
1681 }
1682 
1683 //-----------------------------------------------------------------------------
1684 // Function : Instance::calcDXDV
1685 // Purpose :
1686 // Special Notes :
1687 // Scope : public
1688 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1689 // Creation Date : 12/02/02
1690 //-----------------------------------------------------------------------------
1692 {
1693 
1694  return true;
1695 }
1696 
1697 //-----------------------------------------------------------------------------
1698 // Function : Instance::loadDFDV
1699 // Purpose : Load -dfdv into the RHS vector for the specified
1700 // electrode.
1701 // Special Notes :
1702 // Scope : public
1703 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1704 // Creation Date : 12/02/02
1705 //-----------------------------------------------------------------------------
1706 bool Instance::loadDFDV (int ielectrode, N_LAS_Vector * dfdvPtr)
1707 {
1708  bool bsuccess = true;
1709  bool bs1 = true;
1710  DeviceInterfaceNode & dINode = dIVec[ielectrode];
1711  N_LAS_Vector & dfdv = *dfdvPtr;
1712 
1713  int numNeighbor = dINode.neighborNodes.size();
1714 
1715  double coef;
1716  int iNeighbor;
1717  int dFdVindex = 0;
1718  for (iNeighbor=0;iNeighbor<numNeighbor;++iNeighbor)
1719  {
1720  int inode = dINode.neighborNodes[iNeighbor];
1721  int Vrow = Vrowarray[inode];
1722  int Nrow = Nrowarray[inode];
1723  int Prow = Prowarray[inode];
1724 
1725  mNode * nodePtr = meshContainerPtr->getNode(inode);
1726 
1727  int iNN=0;
1728  for (iNN=0;iNN<nodePtr->cnode;++iNN)
1729  {
1730  int inodeB = nodePtr->edgeInfoVector[iNN].inode;
1731 
1732  // if nodeB is not a boundary node, never mind.
1733  if (boundarySten[inodeB]!=1) continue;
1734 
1735  // if it is a boundary node, but not part of the
1736  // current boundary, also never mind.
1737  if (labelNameVector[inodeB]!= dINode.eName) continue;
1738 
1739  // load V term:
1740  coef = dINode.dFdVckt[dFdVindex];
1741  if( useVectorGIDFlag )
1742  {
1743  if (Vrow != -1)
1744  {
1745  bs1 = dfdv.setElementByGlobalIndex(Vrow, -coef, 0);
1746  bsuccess = bsuccess && bs1;
1747  }
1748  }
1749  else
1750  {
1751  dfdv[Vrow] = -coef;
1752  }
1753 
1754  ++dFdVindex;
1755 
1756  // load N term:
1757  coef = dINode.dFdVckt[dFdVindex];
1758  if( useVectorGIDFlag )
1759  {
1760  if (Nrow != -1)
1761  {
1762  bs1 = dfdv.setElementByGlobalIndex(Nrow, -coef, 0);
1763  bsuccess = bsuccess && bs1;
1764  }
1765  }
1766  else
1767  {
1768  dfdv[Nrow] = -coef;
1769  }
1770  ++dFdVindex;
1771 
1772 
1773  // load P term:
1774  coef = dINode.dFdVckt[dFdVindex];
1775  if( useVectorGIDFlag )
1776  {
1777  if (Prow != -1)
1778  {
1779  bs1 = dfdv.setElementByGlobalIndex(Prow, -coef, 0);
1780  bsuccess = bsuccess && bs1;
1781  }
1782  }
1783  else
1784  {
1785  dfdv[Prow] = -coef;
1786  }
1787  ++dFdVindex;
1788  }
1789  }
1790 
1791  return bsuccess;
1792 }
1793 
1794 //-----------------------------------------------------------------------------
1795 // Function : Instance::calcConductance
1796 //
1797 // Purpose : Calculates device conductances for a single electrode of
1798 // the PDE device. This function is for
1799 // calculating conductances between extern circuit nodes.
1800 //
1801 // The point of calculating these quantities is to provide
1802 // a lumped parameter substitute for the full device, when
1803 // running 2-level Newton.
1804 //
1805 // Special Notes : This function is (ultimately) invoked from the nonlinear
1806 // solver, as that part of the code, when running in
1807 // 2-level mode, knows when this information is needed.
1808 //
1809 // It is assumed that when this function is called, the
1810 // "deltaX" vector contains the information: dXdVckt, where
1811 // X is solution vector variables associated with the PDE
1812 // device, while Vckt is the voltage on the attached
1813 // circuit node. The reason it is in the "deltaX" vector
1814 // is that it was obtained via a linear solver of the
1815 // problem:
1816 //
1817 // dXdVckt = J^-1 . dFdVckt
1818 //
1819 // dFdVckt was calculated previously in pdTerminalCurrents,
1820 // and J is the Jacobian.
1821 //
1822 // 05/06/03: Adapting this function to also calculate terminal
1823 // capacitances. The form of this calculation is exactly
1824 // the same, but instead of using terminal currents to get
1825 // conductances, I'm using terminal charges to get
1826 // capacitances. (dQ/dV instead of dI/dV).
1827 //
1828 //
1829 // Scope : public
1830 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1831 // Creation Date : 11/14/02
1832 //-----------------------------------------------------------------------------
1833 bool Instance::calcConductance (int iElectrode, const N_LAS_Vector * dxdvPtr)
1834 {
1835  bool bsuccess = true;
1836  const N_LAS_Vector & dxdv = *dxdvPtr;
1837 
1838  calcConductanceFlag = true;
1839 
1840 #ifdef Xyce_DEBUG_DEVICE
1841  char filename1[256];
1842 
1843  for (int ich = 0; ich < 256; ++ich)
1844  { filename1[ich] = 0; }
1845 
1846  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
1847  {
1848  Xyce::dout() << section_divider << "\n";
1849  Xyce::dout() << "calcConductances name = " << getName() << std::endl;
1850  Xyce::dout() << "electrode = " << dIVec[iElectrode].eName;
1851  Xyce::dout() << " dIdVckt = " << dIVec[iElectrode].dIdVckt;
1852  Xyce::dout() << std::endl;
1853  Xyce::dout() << std::endl;
1854  }
1855 #endif
1856 
1857  if (!(dIVec[iElectrode].dxdvAllocated))
1858  {
1859  dIVec[iElectrode].dxdvPtr = extData.lasSysPtr->builder().createVector();
1860  dIVec[iElectrode].dxdvAllocated = true;
1861  }
1862 
1863  // A linear solve should have just been performed up in the Newton
1864  // solver. The result of that solve, dxdv was placed in the RHS vector.
1865  // dxdv is needed later, so save a copy.
1866  *(dIVec[iElectrode].dxdvPtr) = *(dxdvPtr);
1867 
1868  // doing the iElectrode Column of the condVec array.
1869  // This should correspond to the dIVec[iElectrode].gid column of the
1870  // Jacobian.
1871 
1872  double Gij = 0.0;
1873  double dIidVj = 0.0;
1874  double dIidVj_chain = 0.0; // from the dot product.
1875 
1876  double Cij = 0.0;
1877  double dQidVj = 0.0;
1878  double dQidVj_chain = 0.0; // from the dot product.
1879 
1880  for (int iEqu=0;iEqu< numElectrodes; ++iEqu)
1881  {
1882  // conductance Gij .
1883  //
1884  // subscript i = variable, which is one of the electrode voltages.
1885  // subscript j = electrode
1886  //
1887  //
1888  // Gij = dIi/dVj_chain + dIi/dVj
1889  //
1890  // = dot( dIi/dX , dX/dVj ) + dIi/dVj
1891  //
1892  // if i != j, then the last term is zero.
1893 
1894  if (iElectrode != iEqu)
1895  {
1896  dIidVj = 0.0;
1897  dQidVj = 0.0;
1898  }
1899  else
1900  {
1901  dIidVj = dIVec[iEqu].dIdVckt;
1902  dQidVj = dIVec[iEqu].dQdVckt;
1903  }
1904 
1905  // load dIdX, dQdX:
1906  extData.tmpdIdXPtr->putScalar (0.0);
1907  extData.tmpdQdXPtr->putScalar (0.0);
1908  int DIDXSize = dIVec[iEqu].dIdX.size();
1909  for (int iDIDX=0;iDIDX<DIDXSize;++iDIDX)
1910  {
1911  int index = dIVec[iEqu].dIdXcols[iDIDX];
1912  double coefI = dIVec[iEqu].dIdX[iDIDX];
1913  double coefQ = dIVec[iEqu].dQdX[iDIDX];
1914 
1915  if( useVectorGIDFlag )
1916  {
1917  if (index != -1)
1918  {
1919  extData.tmpdIdXPtr->setElementByGlobalIndex (index, coefI, 0);
1920  extData.tmpdQdXPtr->setElementByGlobalIndex (index, coefQ, 0);
1921  }
1922  }
1923  else
1924  {
1925  (*(extData.tmpdIdXPtr))[index] = coefI;
1926  (*(extData.tmpdQdXPtr))[index] = coefQ; // CHECK THIS!
1927  } // this is probably wrong.
1928  // I think this DVA
1929  // implementation might break in parallel.
1930  }
1931 
1932 #ifdef Xyce_DEBUG_DEVICE
1933  sprintf(filename1,"dIdX%02d.txt", iEqu);
1934  extData.tmpdIdXPtr->writeToFile(filename1);
1935 
1936  sprintf(filename1,"dQdX%02d.txt", iEqu);
1937  extData.tmpdQdXPtr->writeToFile(filename1);
1938 #endif
1939 
1940  // get dot product of dXdv and dIdX:
1941  dIidVj_chain = dxdv.dotProduct( *(extData.tmpdIdXPtr) );
1942 
1943  // total conductance:
1944  Gij = dIidVj_chain + dIidVj;
1945  condVec[iEqu][iElectrode] = Gij;
1946 
1947 
1948  // get dot product of dXdv and dQdX:
1949  dQidVj_chain = dxdv.dotProduct( *(extData.tmpdQdXPtr) );
1950 
1951  // total capacitance:
1952  Cij = dQidVj_chain + dQidVj;
1953  capVec[iEqu][iElectrode] = Cij;
1954 
1955 #ifdef Xyce_DEBUG_DEVICE
1956  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
1957  {
1958  char outstring[128];
1959  double Itmp = dIVec[iEqu].currentSum;
1960  double Vtmp = dIVec[iEqu].Vckt - dIVec[iElectrode].Vckt;
1961  Vtmp *= scalingVars.V0;
1962  double GV = Gij*Vtmp;
1963  for(int i=0;i<128;++i) outstring[i] = static_cast<char>(0);
1964  sprintf(outstring,
1965  "(%2d,%2d): dotPr=%12.4e G=%12.4e",
1966  iEqu,iElectrode,dIidVj_chain,Gij);
1967  Xyce::dout() << std::string(outstring) << std::endl;
1968 
1969  sprintf(outstring,
1970  "(%2d,%2d): G=%12.4e G*V=%12.4e I=%12.4e V=%12.4e",
1971  iEqu,iElectrode,Gij,GV,Itmp,Vtmp);
1972  Xyce::dout() << std::string(outstring) << std::endl;
1973  }
1974 #endif
1975  }
1976 
1977 #ifdef Xyce_DEBUG_DEVICE
1978  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
1979  {
1980  Xyce::dout() << section_divider << std::endl;
1981  }
1982 #endif
1983 
1984  return bsuccess;
1985 }
1986 
1987 //-----------------------------------------------------------------------------
1988 // Function : Instance::updatePrimaryState
1989 // Purpose :
1990 // Special Notes :
1991 // Scope : public
1992 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1993 // Creation Date : 11/14/01
1994 //-----------------------------------------------------------------------------
1996 {
1997  bool bsuccess = true;
1999  N_LAS_Vector * staVectorPtr = extData.nextStaVectorPtr;
2000 
2001  std::vector<DeviceInterfaceNode>::iterator firstDI = dIVec.begin();
2002  std::vector<DeviceInterfaceNode>::iterator lastDI = dIVec.end ();
2003  std::vector<DeviceInterfaceNode>::iterator iterDI = firstDI;
2004 
2005  for (; iterDI!=lastDI;++iterDI)
2006  {
2007  if( useVectorGIDFlag )
2008  {
2009  if (iterDI->stateC_owned)
2010  {
2011  // place this value for the current sum in the state vector.
2012  (staVectorPtr)->setElementByGlobalIndex( iterDI->stateC,
2013  iterDI->currentSum, 0);
2014  }
2015  }
2016  else
2017  {
2018  (*staVectorPtr)[iterDI->li_stateC] = iterDI->currentSum;
2019  }
2020  }
2021 
2022  // Now store the dielectric displacement in the state vector
2023  // in order to calculate displacement current.
2024  int i;
2025 #ifdef Xyce_OLD_DISPLACEMENT
2026 
2027  for (i = 0; i< numMeshEdges; ++i)
2028  {
2029  double D = eSi * e0 * scalingVars.E0 * EfieldVec[i];
2030 
2031  if( useVectorGIDFlag )
2032  {
2033  if( stateDispl_owned[i] )
2034  {
2035  // place this value for the charge in the state vector.
2036  (staVectorPtr)->setElementByGlobalIndex( stateDispl[i], D, 0);
2037  }
2038  }
2039  else
2040  {
2041  (*staVectorPtr)[li_stateDispl[i]] = D;
2042  }
2043  }
2044 
2045 #else
2046 
2047  // ERK NOTE: as VVEC is already synchronized with the solution vector,
2048  // it is probably unneccessary to also store it in state. Fix later.
2049  for (i = 0; i< numMeshPoints; ++i)
2050  {
2051  if( useVectorGIDFlag )
2052  {
2053  if( stateDispl_owned[i] )
2054  {
2055  // place this value for the charge in the state vector.
2056  (staVectorPtr)->setElementByGlobalIndex( stateDispl[i], (scalingVars.V0*VVec[i]), 0);
2057  }
2058  }
2059  else
2060  {
2061  (*staVectorPtr)[li_stateDispl[i]] = scalingVars.V0 * VVec[i];
2062  }
2063  }
2064 
2065 #endif
2066 
2067 
2068  return bsuccess;
2069 }
2070 
2071 //-----------------------------------------------------------------------------
2072 // Function : Instance::updateSecondaryState
2073 // Purpose :
2074 // Special Notes :
2075 // Scope : public
2076 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
2077 // Creation Date : 11/14/01
2078 //-----------------------------------------------------------------------------
2080 {
2081  bool bsuccess = true;
2082 
2083  // If this is a "ckt-only , don't need to recalculate anything.
2084  if (getSolverState().twoLevelNewtonCouplingMode==OUTER_PROBLEM) return bsuccess;
2085 
2086  N_LAS_Vector * staVectorPtr = extData.nextStaVectorPtr;
2087 
2088  std::vector<DeviceInterfaceNode>::iterator firstDI = dIVec.begin();
2089  std::vector<DeviceInterfaceNode>::iterator lastDI = dIVec.end ();
2090  std::vector<DeviceInterfaceNode>::iterator iterDI = firstDI;
2091 
2092  for (; iterDI!=lastDI;++iterDI)
2093  {
2094  // obtain this value from the state vector.
2095  if( useVectorGIDFlag )
2096  {
2097  iterDI->currentSum =
2098  (staVectorPtr)->getElementByGlobalIndex( iterDI->stateC, 0);
2099  }
2100  else
2101  {
2102  iterDI->currentSum = (*staVectorPtr)[iterDI->li_stateC];
2103  }
2104  }
2105 
2106  // Now get the displacement current:
2107  int i;
2108  double dcmax = 0.0;
2109 
2110 #ifdef Xyce_OLD_DISPLACEMENT
2111  for (i = 0; i< numMeshEdges; ++i)
2112  {
2113  N_LAS_Vector * staDerivPtr = extData.nextStaDerivVectorPtr;
2114  if( useVectorGIDFlag )
2115  {
2116  displCurrent[i] = staDerivPtr->getElementByGlobalIndex(stateDispl[i], 0);
2117  }
2118  else
2119  {
2120  displCurrent[i] = (*staDerivPtr)[li_stateDispl[i]];
2121  }
2122 
2123  if (fabs(displCurrent[i]) > dcmax) dcmax = fabs(displCurrent[i]);
2124  }
2125 
2126 #else
2127 
2128  // first get the "displacement potential"
2129  for (i = 0; i< numMeshPoints; ++i)
2130  {
2131  N_LAS_Vector * staDerivPtr = extData.nextStaDerivVectorPtr;
2132  if( useVectorGIDFlag )
2133  {
2134  displPotential[i] = staDerivPtr->getElementByGlobalIndex(stateDispl[i], 0);
2135  }
2136  else
2137  {
2138  displPotential[i] = (*staDerivPtr)[li_stateDispl[i]];
2139  }
2140 
2141  if (fabs(displCurrent[i]) > dcmax) dcmax = fabs(displCurrent[i]);
2142  }
2143 
2144  // now use it to get the dielectric displacement current. This should look pretty
2145  // similar to the calcEfield function from this point on.
2146  for (i=0;i<numMeshEdges;++i)
2147  {
2148  mEdge * edgePtr = meshContainerPtr->getEdge(i);
2149 
2150  int inodeA = edgePtr->inodeA;
2151  int inodeB = edgePtr->inodeB;
2152  double elen = edgePtr->elen;
2153 
2154  displCurrent[i] = -(displPotential[inodeB] - displPotential[inodeA])/elen;
2156 
2157  if (fabs(displCurrent[i]) > dcmax) dcmax = fabs(displCurrent[i]);
2158  }
2159 
2160 #endif
2161 
2162 #ifdef Xyce_DEBUG_DEVICE
2163  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
2164  Xyce::dout() << " Maximum displacement current: " << dcmax << std::endl;
2165 #endif
2166 
2167  return bsuccess;
2168 }
2169 
2170 //-----------------------------------------------------------------------------
2171 // Function : Instance::setInitialGuess
2172 //
2173 // Purpose : This function sets up a bunch of initial information,
2174 // that only has to be set up once at the beginning of the
2175 // simulation. That includes the
2176 // simple, analytic initial guess, the density boundary
2177 // conditions, carrier lifetimes, scaling variables, etc.
2178 //
2179 // Special Notes : Mobilities are set up here, because originally this code
2180 // only had mobilities that were doping-dependent ONLY.
2181 // Now the code has mobilities that are also dependent on
2182 // carrier density, which can change throughout the
2183 // simulation. So, mobilities are also calculated
2184 // elsewhere, but it didn't hurt to leave them here.
2185 //
2186 // Scope : public
2187 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
2188 // Creation Date : 05/02/03
2189 //-----------------------------------------------------------------------------
2191 {
2192  bool bsuccess = true;
2193  bool bs1 = true;
2194 
2195 #ifdef Xyce_DEBUG_DEVICE
2196  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
2197  {
2198  Xyce::dout() << "Instance::setInitialGuess\n";
2199  }
2200 #endif
2201 
2202  // The stuff inside of the "called before" if statement needs to be here,
2203  // rather than in the instance constructor, because some of these
2204  // functions need for the GID's to have been setup.
2205  if (!calledBeforeSIGB)
2206  {
2207  bs1 = calcDensityBCs (); bsuccess = bsuccess && bs1;
2208  bs1 = calcVequBCs (); bsuccess = bsuccess && bs1;
2209  bs1 = calcInitialGuess (); bsuccess = bsuccess && bs1;
2210  bs1 = calcMobilities (); bsuccess = bsuccess && bs1;
2211  bs1 = calcLifetimes (); bsuccess = bsuccess && bs1;
2212  bs1 = scaleVariables (); bsuccess = bsuccess && bs1;
2213  calledBeforeSIGB = true;
2214 
2215 #ifdef Xyce_DEBUG_DEVICE
2216  // if running with debug turned on, output the initial guess:
2217  if (getDeviceOptions().debugLevel > 3 && getSolverState().debugTimeFlag && tecplotLevel > 0)
2218  {
2219  outputTecplot ();
2220  if (tecplotLevel > 2) outputTecplotVectors ();
2221  }
2222 
2223  if (getDeviceOptions().debugLevel > 3 && getSolverState().debugTimeFlag && sgplotLevel > 0)
2224  {
2225  outputSgplot ();
2226  }
2227 
2228  if (getDeviceOptions().debugLevel > 3 && getSolverState().debugTimeFlag && gnuplotLevel > 0)
2229  {
2230  outputGnuplot ();
2231  }
2232 #endif
2233  } // calledBeforeSIGB
2234 
2235  return (bsuccess);
2236 }
2237 
2238 //-----------------------------------------------------------------------------
2239 // Function : Instance::loadVecNLPoisson
2240 // Purpose :
2241 //
2242 // Special Notes : This function returns the nonlinear poisson rhs load,
2243 // multiplied by a scalar. The scalar generally will
2244 // be 1.0 or -1.0. -1.0 is used for the new-DAE formualtion,
2245 // while 1.0 is the normal thing to do for old-DAE.
2246 //
2247 // Scope : public
2248 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
2249 // Creation Date : 06/24/05
2250 //-----------------------------------------------------------------------------
2251 bool Instance::loadVecNLPoisson (double scalar, N_LAS_Vector * vecPtr)
2252 {
2253  bool bsuccess = true;
2254  bool bs1 = true;
2255  std::string semi(bulkMaterial);
2256  int i;
2257  int Vrow, Nrow, Prow;
2258  double coef, coef2;
2259 #ifndef Xyce_NEW_BC
2260  double vtmp;
2261 #endif
2262  double ilen, elen, nodeArea;
2263  double holeDens;
2264  double elecDens;
2265 
2266  Ut = Vt/scalingVars.V0;
2267 
2268 #ifdef Xyce_DEBUG_DEVICE
2269  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
2270  {
2271  Xyce::dout() << section_divider << "\n";
2272  Xyce::dout() << "Instance::loadVecNLPoisson\n";
2273  Xyce::dout() << " name = " << getName() <<"\n";
2274 
2275  Xyce::dout() << " Vt = " << Vt << "\n";
2276  Xyce::dout() << " Ut = " << Ut << "\n";
2277  Xyce::dout() << " scalingVars.V0 = " << scalingVars.V0 << "\n";
2278 
2279  }
2280 #endif
2281 
2282  // mesh points for the PDE problem:
2283  for (i=0;i<numMeshPoints;++i)
2284  {
2285 #ifdef Xyce_DEBUG_DEVICE
2286  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
2287  {
2288  Xyce::dout() << "--------" << std::endl;
2289  Xyce::dout() << "Mesh Point i = " << i;
2290  Xyce::dout() << " x = " << xVec[i] *scalingVars.x0;
2291  Xyce::dout() << " y = " << yVec[i] *scalingVars.x0 << std::endl;
2292  }
2293 #endif
2294 
2295  if( useVectorGIDFlag )
2296  {
2297  Vrow = Vrowarray[i];
2298  Nrow = Nrowarray[i];
2299  Prow = Prowarray[i];
2300  }
2301  else
2302  {
2303  Vrow = li_Vrowarray[i];
2304  Nrow = li_Nrowarray[i];
2305  Prow = li_Prowarray[i];
2306  }
2307 
2308  // Is this node a node with an explicit boundary condition?
2309  // If so, apply the BC. Otherwise, just load the region operators.
2310 
2311 #ifdef Xyce_NEW_BC
2312  // If we are using the "new" boundary conditions, the are simply
2313  // imposed, rather than being solved, so don't do anything here.
2314  if (boundarySten[i]) continue;
2315 
2316 #else // "old" boundary condition:
2317  bool doneFlag = false;
2318 
2319  if (boundarySten[i]) // do BC load
2320  {
2321  int DIindex = labelDIMap[labelNameVector[i]];
2322  if (dIVec[DIindex].given)
2323  {
2324  vtmp = 0.0;
2325  if (vOwnVec[i] == 1) vtmp = VVec[i];
2326 
2327  int i1 = dIVec[DIindex].meshGlobalToLocal[i];
2328  coef = vtmp - dIVec[DIindex].VbcVec[i1];
2329 
2330 #ifdef Xyce_DEBUG_DEVICE
2331  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
2332  {
2333  Xyce::dout() << "BC load: Vrow = " << Vrow << " coef = " << coef << std::endl;
2334  Xyce::dout() << " vtmp = " << vtmp << std::endl;
2335  Xyce::dout() << " Vckt = " << dIVec[DIindex].Vckt << std::endl;
2336  }
2337 #endif
2338  if( useVectorGIDFlag )
2339  {
2340  bs1 = vecPtr->sumElementByGlobalIndex(Vrow, -scalar*coef, 0);
2341  bsuccess = bsuccess && bs1;
2342  bs1 = vecPtr->sumElementByGlobalIndex(Nrow, 0.0 , 0);
2343  bsuccess = bsuccess && bs1;
2344  bs1 = vecPtr->sumElementByGlobalIndex(Prow, 0.0 , 0);
2345  bsuccess = bsuccess && bs1;
2346  }
2347  else
2348  {
2349  (*vecPtr)[Vrow] += -scalar*coef;
2350  (*vecPtr)[Nrow] += 0.0;
2351  (*vecPtr)[Prow] += 0.0;
2352  }
2353 
2354  doneFlag = true;
2355 
2356  } // inner if
2357  } // outer if
2358 
2359  if (doneFlag) continue;
2360 #endif // Xyce_NEW_BC
2361 
2362  // if load is not done yet, then do an interior point load:
2363  mNode * nodePtr = meshContainerPtr->getNode(i);
2364  nodeArea = nodePtr->area;
2365 #ifdef Xyce_DEBUG_DEVICE
2366  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
2367  {
2368  Xyce::dout() << "--------" << std::endl;
2369  Xyce::dout() << "Interior: mesh point: " << i << " Vrow = " << Vrow;
2370  Xyce::dout() << std::endl;
2371  }
2372 #endif
2373 
2374  coef = 0.0;
2375  for (int iNN=0;iNN<nodePtr->cnode;++iNN)
2376  {
2377  int iedge = nodePtr->edgeInfoVector[iNN].iedge;
2378  int inodeB = nodePtr->edgeInfoVector[iNN].inode;
2379  ilen = nodePtr->edgeInfoVector[iNN].ilen;
2380  elen = nodePtr->edgeInfoVector[iNN].elen;
2381 
2382  double efield_loc = (VVec[i]-VVec[inodeB])/elen;
2383  coef += -efield_loc * ilen ;
2384 #ifdef Xyce_DEBUG_DEVICE
2386  {
2387  Xyce::dout() << "----" << std::endl;
2388  Xyce::dout() << " iedge = " << iedge << std::endl;
2389  Xyce::dout() << " Vlocal = " << VVec[i] << std::endl;
2390  Xyce::dout() << " Vneigh = " << VVec[inodeB] << std::endl;
2391  Xyce::dout() << " efield = " << efield_loc << std::endl;
2392  Xyce::dout() << " elen = " << elen << std::endl;
2393  Xyce::dout() << " ilen = " << ilen << std::endl;
2394  Xyce::dout() << " inodeB = " << inodeB;
2395  Xyce::dout() << " x[b] = " << xVec[inodeB]*scalingVars.x0;
2396  Xyce::dout() << " y[b] = " << yVec[inodeB]*scalingVars.x0 << std::endl;
2397  Xyce::dout() << std::endl;
2398  }
2399 #endif
2400  }
2401  coef *= -scalingVars.L0 * matSupport.getRelPerm(semi)/nodeArea;
2402 
2403  holeDens = getVoltDepHoleDens ( VminExp, VVec[i], NpMax);
2404  elecDens = getVoltDepElecDens ( VmaxExp, VVec[i], NnMax);
2405  coef2 = -(holeDens-elecDens+CVec[i]);
2406 
2407 #ifdef Xyce_OXIDE_ENABLED
2408  if (!allOxideFlag)
2409  {
2410  coef += coef2;
2411  }
2412 #else
2413  coef += coef2;
2414 #endif
2415 
2416 #ifdef Xyce_DEBUG_DEVICE
2418  {
2419  Xyce::dout() << "--------" << std::endl;
2420  Xyce::dout() << " holeD = " << holeDens << std::endl;
2421  Xyce::dout() << " elecD = " << elecDens << std::endl;
2422  Xyce::dout() << " dopeD = " << CVec[i] << std::endl;
2423  Xyce::dout() << " coef2 = " << coef2 << std::endl;
2424  Xyce::dout() << " scalingVars.L0 = " << scalingVars.L0 * matSupport.getRelPerm(semi) << std::endl;
2425  Xyce::dout() << " nodeArea = " << nodeArea << std::endl;
2426  Xyce::dout() << " coef = " << coef << std::endl;
2427  Xyce::dout() << "--------" << std::endl;
2428  }
2429 #endif
2430 
2431  if( useVectorGIDFlag )
2432  {
2433  bs1 = vecPtr->sumElementByGlobalIndex(Vrow, -scalar*coef, 0);
2434  bsuccess = bsuccess && bs1;
2435  bs1 = vecPtr->sumElementByGlobalIndex(Nrow, 0.0 , 0);
2436  bsuccess = bsuccess && bs1;
2437  bs1 = vecPtr->sumElementByGlobalIndex(Prow, 0.0 , 0);
2438  bsuccess = bsuccess && bs1;
2439  }
2440  else
2441  {
2442  (*vecPtr)[Vrow] += -scalar*coef;
2443  (*vecPtr)[Nrow] += 0.0;
2444  (*vecPtr)[Prow] += 0.0;
2445  }
2446 
2447  } // mesh point loop
2448 
2449 #ifdef Xyce_DEBUG_DEVICE
2450  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
2451  Xyce::dout() << section_divider << std::endl;
2452 #endif
2453 
2454  return bsuccess;
2455 }
2456 
2457 //-----------------------------------------------------------------------------
2458 // Function : Instance::loadVecDDForm
2459 // Purpose :
2460 // Special Notes :
2461 // Scope : public
2462 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
2463 // Creation Date : 06/24/05
2464 //-----------------------------------------------------------------------------
2466 (double scalar, double dndtScalar, N_LAS_Vector * vecPtr)
2467 {
2468  bool bsuccess = true;
2469  bool bs1 = true;
2470  std::string semi(bulkMaterial);
2471  int i;
2472  int iNN;
2473  int Vrow, Nrow, Prow;
2474  double coef, coef2;
2475  double ilen, elen, nodeArea;
2476  double holeDens, elecDens;
2477 #ifndef Xyce_NEW_BC
2478  double vtmp;
2479  double ntmp;
2480  double ptmp;
2481 #endif
2482 
2483 #ifdef Xyce_DEBUG_DEVICE
2484  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
2485  {
2486  Xyce::dout() << "\n"<<section_divider << std::endl;
2487  Xyce::dout() << "Instance::loadVecDDForm\n";
2488  Xyce::dout() << " name = " << getName() << "\n";
2489  }
2490 #endif
2491 
2492  // KCL equations for the various connecting terminals:
2493  std::vector<DeviceInterfaceNode>::iterator firstDI = dIVec.begin();
2494  std::vector<DeviceInterfaceNode>::iterator lastDI = dIVec.end ();
2495  std::vector<DeviceInterfaceNode>::iterator iterDI = firstDI;
2496 
2497  // if this is the inner loop of a multilevel Newton solve, don't do the
2498  // KCL-related loads.
2499  if ( !(getSolverState().twoLevelNewtonCouplingMode==INNER_PROBLEM))
2500  {
2501  for (;iterDI!=lastDI;++iterDI)
2502  {
2503  coef = iterDI->currentSum;
2504 
2505  if( useVectorGIDFlag )
2506  {
2507  if (iterDI->gid != -1)
2508  {
2509  bs1 = vecPtr->sumElementByGlobalIndex(iterDI->gid, -scalar*coef, 0);
2510  bsuccess = bsuccess && bs1;
2511  }
2512  }
2513  else
2514  {
2515  (*vecPtr)[iterDI->lid] += -scalar*coef;
2516  }
2517 
2518 #ifdef Xyce_DEBUG_DEVICE
2519  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
2520  {
2521  Xyce::dout() << "KCL for "<< iterDI->eName << ":\n";
2522  Xyce::dout() << " row = " << iterDI->gid << "\n";
2523  Xyce::dout() << "coef = " << coef << "\n";
2524  }
2525 #endif
2526 
2527  }
2528  } // end of twoLevelNewtonCouplingMode if statement.
2529 
2530  // mesh points for the PDE problem:
2531  for (i=0;i<numMeshPoints;++i)
2532  {
2533 #ifdef Xyce_DEBUG_DEVICE
2534  if (getDeviceOptions().debugLevel > 1 && getSolverState().debugTimeFlag)
2535  {
2536  Xyce::dout() << "--------" << std::endl;
2537  Xyce::dout() << "Mesh Point i = " << i;
2538  Xyce::dout() << " x = " << xVec[i]*scalingVars.x0;
2539  Xyce::dout() << " y = " << yVec[i]*scalingVars.x0 << std::endl;
2540  }
2541 #endif
2542 
2543  if( useVectorGIDFlag )
2544  {
2545  Vrow = Vrowarray[i];
2546  Nrow = Nrowarray[i];
2547  Prow = Prowarray[i];
2548  }
2549  else
2550  {
2551  Vrow = li_Vrowarray[i];
2552  Nrow = li_Nrowarray[i];
2553  Prow = li_Prowarray[i];
2554  }
2555 
2556  bool doneFlagV = false;
2557  bool doneFlagN = false;
2558  bool doneFlagP = false;
2559 #ifdef Xyce_NEW_BC
2560  // if doing the "new" boundary conditions, then just skip the load.
2561  // The boundary conditions are just imposed.
2562  // If using this type of BC, Neumann conditions are not an option.
2563  // Only Dirichlet. (not mixed either)
2564  if (boundarySten[i]) continue;
2565 #else
2566  bool doneFlag = false;
2567 
2568  // Is this node a node with an explicit DIRICHLET boundary condition?
2569  // If so, apply the BC. Otherwise, just load the region operators.
2570  // If the BC has been specified to be neumann, then don't do anything
2571  // unusual - the region operator will enforce a neumann condition.
2572 
2573  if (boundarySten[i]) // do BC load
2574  {
2575  int DIindex = labelDIMap[labelNameVector[i]];
2576 
2577  if (dIVec[DIindex].given)
2578  {
2579  int ilocal = dIVec[DIindex].meshGlobalToLocal[i];
2580 
2581  if (dIVec[DIindex].neumannBCFlagV==false)
2582  {
2583  vtmp = 0.0;
2584  if (vOwnVec[i] == 1) vtmp = VVec[i];
2585 
2586  coef = vtmp - dIVec[DIindex].VbcVec[ilocal];
2587 
2588  if( useVectorGIDFlag )
2589  {
2590  bs1 = vecPtr->sumElementByGlobalIndex(Vrow, -scalar*coef, 0);
2591  bsuccess = bsuccess && bs1;
2592  }
2593  else
2594  {
2595  (*vecPtr)[Vrow] += -scalar*coef;
2596  }
2597 
2598 #ifdef Xyce_DEBUG_DEVICE
2599  if (getDeviceOptions().debugLevel > 1 && getSolverState().debugTimeFlag)
2600  {
2601  Xyce::dout() << "BC load: Vrow = " << Vrow;
2602  Xyce::dout() << " coef = " << coef << std::endl;
2603  Xyce::dout() << " vtmp = " << vtmp << std::endl;
2604  Xyce::dout() << " Vckt = " << dIVec[DIindex].Vckt << std::endl;
2605  Xyce::dout() << " Vequ = " << dIVec[DIindex].VequVec[ilocal] << std::endl;
2606  }
2607 #endif
2608  doneFlagV = true;
2609  } // end of neumann if statement.
2610 
2611  if (dIVec[DIindex].neumannBCFlagN==false)
2612  {
2613  ntmp = 0.0;
2614  if (nnOwnVec[i] == 1) ntmp = nnVec[i];
2615  coef = ntmp - (dIVec[DIindex].nnbcVec[ilocal]);
2616 
2617  if( useVectorGIDFlag )
2618  {
2619  bs1 = vecPtr->sumElementByGlobalIndex(Nrow, -scalar*coef , 0);
2620  bsuccess = bsuccess && bs1;
2621  }
2622  else
2623  {
2624  (*vecPtr)[Nrow] += -scalar*coef;
2625  }
2626 
2627 #ifdef Xyce_DEBUG_DEVICE
2628  if (getDeviceOptions().debugLevel > 1 && getSolverState().debugTimeFlag)
2629  {
2630  Xyce::dout() << "BC load: Nrow = ";
2631  Xyce::dout() << Nrow << " coef = " << coef << std::endl;
2632  Xyce::dout() << " ntmp = " << ntmp << std::endl;
2633  Xyce::dout() << " nnbc = " << dIVec[DIindex].nnbcVec[ilocal] << std::endl;
2634  }
2635 #endif
2636  doneFlagN = true;
2637  } // end of neumann if
2638 
2639 
2640  if (dIVec[DIindex].neumannBCFlagP==false)
2641  {
2642  ptmp = 0.0;
2643  if (npOwnVec[i] == 1) ptmp = npVec[i];
2644  coef = ptmp - (dIVec[DIindex].npbcVec[ilocal]);
2645 
2646  if( useVectorGIDFlag )
2647  {
2648  bs1 = vecPtr->sumElementByGlobalIndex(Prow, -scalar*coef , 0);
2649  bsuccess = bsuccess && bs1;
2650  }
2651  else
2652  {
2653  (*vecPtr)[Prow] += -scalar*coef;
2654  }
2655 
2656 #ifdef Xyce_DEBUG_DEVICE
2657  if (getDeviceOptions().debugLevel > 1 && getSolverState().debugTimeFlag)
2658  {
2659  Xyce::dout() << "BC load: Prow = ";
2660  Xyce::dout() << Prow << " coef = " << coef << std::endl;
2661  Xyce::dout() << " ptmp = " << ptmp << std::endl;
2662  Xyce::dout() << " npbc = " << dIVec[DIindex].npbcVec[ilocal] << std::endl;
2663  }
2664 #endif
2665  doneFlagP = true;
2666  } // end of neumann if
2667 
2668  doneFlag = (doneFlagV && doneFlagN && doneFlagP);
2669 
2670  } // inner if (was this DI given by the user in the netlist)
2671  } // outer if (finding the label in the label name vector)
2672 
2673  if (doneFlag) continue;
2674 
2675 #endif // Xyce_NEW_BC
2676 
2677  // Do the interior mesh points, if we've gotten this far:
2678 
2679  mNode * nodePtr = meshContainerPtr->getNode(i);
2680  nodeArea = nodePtr->area;
2681 #ifdef Xyce_DEBUG_DEVICE
2682  if (getDeviceOptions().debugLevel > 1 && getSolverState().debugTimeFlag)
2683  {
2684  Xyce::dout() << "--------" << std::endl;
2685  Xyce::dout() << "Interior: mesh point: " << i << " Vrow = " << Vrow;
2686  Xyce::dout() << std::endl;
2687  }
2688 #endif
2689 
2690  // do poisson's equation first:
2691  if (!doneFlagV)
2692  {
2693 #ifdef Xyce_DEBUG_DEVICE
2694  if (getDeviceOptions().debugLevel > 1 && getSolverState().debugTimeFlag)
2695  {
2696  Xyce::dout() << "--------" << std::endl;
2697  Xyce::dout() << " Poisson equ:";
2698  }
2699 #endif
2700 
2701  coef = 0.0;
2702  for (iNN=0;iNN<nodePtr->cnode;++iNN)
2703  {
2704  int iedge = nodePtr->edgeInfoVector[iNN].iedge;
2705  int inodeB = nodePtr->edgeInfoVector[iNN].inode;
2706  ilen = nodePtr->edgeInfoVector[iNN].ilen;
2707  elen = nodePtr->edgeInfoVector[iNN].elen;
2708 
2709  //coef += ((i<inodeB)?1.0:-1.0) * EfieldVec[iedge] * ilen ;
2710  double efield_loc = (VVec[i]-VVec[inodeB])/elen;
2711  coef += -efield_loc * ilen ;
2712 #ifdef Xyce_DEBUG_DEVICE
2713  if (getDeviceOptions().debugLevel > 1 && getSolverState().debugTimeFlag)
2714  {
2715  Xyce::dout() << "----" << std::endl;
2716  Xyce::dout() << " iedge = " << iedge << std::endl;
2717  Xyce::dout() << " Vlocal = " << VVec[i] << std::endl;
2718  Xyce::dout() << " Vneigh = " << VVec[inodeB] << std::endl;
2719  Xyce::dout() << " efield = " << efield_loc << std::endl;
2720  Xyce::dout() << " elen = " << elen << std::endl;
2721  Xyce::dout() << " ilen = " << ilen << std::endl;
2722  Xyce::dout() << " inodeB = " << inodeB;
2723  Xyce::dout() << " x[b] = " << xVec[inodeB]*scalingVars.x0;
2724  Xyce::dout() << " y[b] = " << yVec[inodeB]*scalingVars.x0 << std::endl;
2725  Xyce::dout() << std::endl;
2726  }
2727 #endif
2728  }
2729  coef *= -scalingVars.L0 * matSupport.getRelPerm(semi)/nodeArea;
2730 
2731  holeDens = npVec[i];
2732  elecDens = nnVec[i];
2733  coef2 = -(holeDens-elecDens+CVec[i]);
2734 
2735 #ifdef Xyce_OXIDE_ENABLED
2736  if (!allOxideFlag)
2737  {
2738  coef += coef2;
2739  }
2740 #else
2741  coef += coef2;
2742 #endif
2743 
2744 #ifdef Xyce_DEBUG_DEVICE
2745  if (getDeviceOptions().debugLevel > 1 && getSolverState().debugTimeFlag)
2746  {
2747  Xyce::dout() << "--------" << std::endl;
2748  Xyce::dout() << " holeD = " << holeDens << std::endl;
2749  Xyce::dout() << " elecD = " << elecDens << std::endl;
2750  Xyce::dout() << " dopeD = " << CVec[i] << std::endl;
2751  Xyce::dout() << " coef2 = " << coef2 << std::endl;
2752  Xyce::dout() << " scalingVars.L0 = " << scalingVars.L0 * matSupport.getRelPerm(semi) << std::endl;
2753  Xyce::dout() << " nodeArea = " << nodeArea << std::endl;
2754  Xyce::dout() << " coef = " << coef << std::endl;
2755  Xyce::dout() << "--------" << std::endl;
2756  }
2757 #endif
2758  if ( getSolverState().chargeHomotopy )
2759  {
2760  coef *= getSolverState().chargeAlpha;
2761  }
2762 
2763  if( useVectorGIDFlag )
2764  {
2765  bs1 = vecPtr->sumElementByGlobalIndex(Vrow, -scalar*coef, 0);
2766  bsuccess = bsuccess && bs1;
2767  }
2768  else
2769  {
2770  (*vecPtr)[Vrow] += -scalar*coef;
2771  }
2772 
2773  doneFlagV = true;
2774 
2775  } // doneFlagV if statement
2776 
2777  // Now do electron continuity
2778  if (!doneFlagN)
2779  {
2780  // get electron time derivative and scale.
2781 
2782 #ifdef Xyce_DEBUG_DEVICE
2783  if (getDeviceOptions().debugLevel > 1 && getSolverState().debugTimeFlag)
2784  {
2785  Xyce::dout() << "--------" << std::endl;
2786  Xyce::dout() << " Electron equ:";
2787  }
2788 #endif
2789 
2790  double dndt = 0.0;
2791 
2792 #if 0
2793  if( useVectorGIDFlag )
2794  {
2795  if ( !((getSolverState().dcopFlag) || Nrowarray[i] == -1) )
2796  {
2797  dndt = (extData.nextSolDerivVectorPtr)->getElementByGlobalIndex
2798  (Nrowarray[i],0);
2799  }
2800  }
2801  else
2802  {
2803  if ( !(getSolverState().dcopFlag) )
2804  dndt = (*extData.nextSolDerivVectorPtr)[li_Nrowarray[i]];
2805  }
2806 
2807  dndt *= scalingVars.t0*dndtScalar; // if new-DAE, dndtScalar is 0.0
2808 #endif
2809 
2810  coef = 0.0;
2811  for (iNN=0;iNN<nodePtr->cnode;++iNN)
2812  {
2813  int iedge = nodePtr->edgeInfoVector[iNN].iedge;
2814  int inodeB = nodePtr->edgeInfoVector[iNN].inode;
2815  ilen = nodePtr->edgeInfoVector[iNN].ilen;
2816  elen = nodePtr->edgeInfoVector[iNN].elen;
2817 
2818  coef += ((i<inodeB)?1.0:-1.0) * JnVec[iedge] * ilen ;
2819 
2820 #ifdef Xyce_DEBUG_DEVICE
2821  if (getDeviceOptions().debugLevel > 1 && getSolverState().debugTimeFlag)
2822  {
2823  Xyce::dout() << "----" << std::endl;
2824  Xyce::dout() << " iedge = " << iedge << std::endl;
2825  Xyce::dout() << " Jn = " << JnVec[iedge] << std::endl;
2826  Xyce::dout() << " nlocal = " << nnVec[i] << std::endl;
2827  Xyce::dout() << " nneigh = " << nnVec[inodeB] << std::endl;
2828  Xyce::dout() << " elen = " << elen << std::endl;
2829  Xyce::dout() << " ilen = " << ilen << std::endl;
2830  Xyce::dout() << " inodeB = " << inodeB;
2831  Xyce::dout() << " x[b] = " << xVec[inodeB]*scalingVars.x0;
2832  Xyce::dout() << " y[b] = " << yVec[inodeB]*scalingVars.x0 << std::endl;
2833  Xyce::dout() << std::endl;
2834  }
2835 #endif
2836  }
2837  coef /= nodeArea;
2838 #if 1
2839  coef += - totSrcVec[i] - dndt;
2840 #else
2841  coef += - RVec[i] - dndt;
2842 #endif
2843 
2844  coef += elecPenalty[i];
2845 
2846 #ifdef Xyce_DEBUG_DEVICE
2847  if (getDeviceOptions().debugLevel > 1 && getSolverState().debugTimeFlag)
2848  {
2849  Xyce::dout() << "--------" << std::endl;
2850  Xyce::dout().setf(std::ios::left);
2851  Xyce::dout() << " row = " << Nrow;
2852  Xyce::dout().setf(std::ios::scientific);
2853  Xyce::dout() << " coef=" << coef;
2854  Xyce::dout() << " nodeArea ="<<nodeArea;
2855  Xyce::dout() << " R[i]="<<RVec[i];
2856  Xyce::dout() << " dndt="<<dndt;
2857  Xyce::dout() << "\n";
2858  Xyce::dout() << "--------" << std::endl;
2859  }
2860 #endif
2861 
2862  if( useVectorGIDFlag )
2863  {
2864  bs1 = vecPtr->setElementByGlobalIndex(Nrow,-scalar*coef,0);
2865  bsuccess = bsuccess && bs1;
2866  }
2867  else
2868  {
2869  (*vecPtr)[Nrow] += -scalar*coef;
2870  }
2871 
2872  doneFlagN = true;
2873 
2874  } // end of doneFlagN if statement.
2875 
2876  // Now do hole continuity
2877  if (!doneFlagP)
2878  {
2879  // get hole time derivative and scale.
2880 #ifdef Xyce_DEBUG_DEVICE
2881  if (getDeviceOptions().debugLevel > 1 && getSolverState().debugTimeFlag)
2882  {
2883  Xyce::dout() << "--------" << std::endl;
2884  Xyce::dout() << " Hole equ:";
2885  }
2886 #endif
2887 
2888  double dpdt = 0.0;
2889 #if 0
2890  if( useVectorGIDFlag )
2891  {
2892  if ( !((getSolverState().dcopFlag) || Prowarray[i] == -1) )
2893  dpdt = (extData.nextSolDerivVectorPtr)->getElementByGlobalIndex
2894  (Prowarray[i],0);
2895  }
2896  else
2897  {
2898  if ( !(getSolverState().dcopFlag) )
2899  dpdt = (*extData.nextSolDerivVectorPtr)[li_Prowarray[i]];
2900  }
2901 
2902  dpdt *= scalingVars.t0*dndtScalar; // if new-DAE, dndtScalar is 0.0.
2903 #endif
2904 
2905  coef = 0.0;
2906  for (iNN=0;iNN<nodePtr->cnode;++iNN)
2907  {
2908  int iedge = nodePtr->edgeInfoVector[iNN].iedge;
2909  int inodeB = nodePtr->edgeInfoVector[iNN].inode;
2910  ilen = nodePtr->edgeInfoVector[iNN].ilen;
2911  elen = nodePtr->edgeInfoVector[iNN].elen;
2912 
2913  coef += ((i<inodeB)?1.0:-1.0) * JpVec[iedge] * ilen ;
2914 
2915 #ifdef Xyce_DEBUG_DEVICE
2916  if (getDeviceOptions().debugLevel > 1 && getSolverState().debugTimeFlag)
2917  {
2918  Xyce::dout() << "----" << std::endl;
2919  Xyce::dout() << " iedge = " << iedge << std::endl;
2920  Xyce::dout() << " Jp = " << JpVec[iedge] << std::endl;
2921  Xyce::dout() << " plocal = " << npVec[i] << std::endl;
2922  Xyce::dout() << " pneigh = " << npVec[inodeB] << std::endl;
2923  Xyce::dout() << " elen = " << elen << std::endl;
2924  Xyce::dout() << " ilen = " << ilen << std::endl;
2925  Xyce::dout() << " inodeB = " << inodeB;
2926  Xyce::dout() << " x[b] = " << xVec[inodeB]*scalingVars.x0;
2927  Xyce::dout() << " y[b] = " << yVec[inodeB]*scalingVars.x0 << std::endl;
2928  Xyce::dout() << std::endl;
2929  }
2930 #endif
2931  }
2932  coef /= -nodeArea;
2933 
2934 #if 1
2935  coef += - totSrcVec[i] - dpdt;
2936 #else
2937  coef += - RVec[i] - dpdt;
2938 #endif
2939  coef += holePenalty[i];
2940 
2941 #ifdef Xyce_DEBUG_DEVICE
2942  if (getDeviceOptions().debugLevel > 1 && getSolverState().debugTimeFlag)
2943  {
2944  Xyce::dout() << "--------" << std::endl;
2945  Xyce::dout().setf(std::ios::left);
2946  Xyce::dout() << " row = " << Prow;
2947  Xyce::dout().setf(std::ios::scientific);
2948  Xyce::dout() << " coef=" << coef;
2949  Xyce::dout() << " nodeArea ="<<nodeArea;
2950  Xyce::dout() << " RVec[i]="<<RVec[i];
2951  Xyce::dout() << " dpdt="<<dpdt;
2952  Xyce::dout() << "\n";
2953  Xyce::dout() << "--------" << std::endl;
2954  }
2955 #endif
2956 
2957  if( useVectorGIDFlag )
2958  {
2959  bs1 = vecPtr->setElementByGlobalIndex(Prow,-scalar*coef,0);
2960  bsuccess = bsuccess && bs1;
2961  }
2962  else
2963  {
2964  (*vecPtr)[Prow] += -scalar*coef;
2965  }
2966 
2967  doneFlagP = true;
2968 
2969  } // end of doneFlagP
2970 
2971  } // ip_iter, row loop...
2972 
2973 #ifdef Xyce_DEBUG_DEVICE
2974  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
2975  Xyce::dout() << section_divider << std::endl;
2976 #endif
2977 
2978  return bsuccess;
2979 }
2980 
2981 //-----------------------------------------------------------------------------
2982 // Function : Instance::loadMatNLPoisson
2983 // Purpose :
2984 // Special Notes :
2985 //
2986 // Scope : private
2987 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
2988 // Creation Date : 06/24/05
2989 //-----------------------------------------------------------------------------
2990 bool Instance::loadMatNLPoisson (N_LAS_Matrix * matPtr)
2991 {
2992  bool bsuccess = true;
2993  bool bs1 = true;
2994 
2995  int Vrow, Nrow, Prow;
2996 
2997 #ifdef Xyce_DEBUG_DEVICE
2998  if (getDeviceOptions().debugLevel > 1 && getSolverState().debugTimeFlag)
2999  {
3000  Xyce::dout() << section_divider << "\n";
3001  Xyce::dout() << "Instance::loadJacNonlinPoisson" << "\n";
3002  Xyce::dout() << " name = " << getName() <<"\n";
3003  Xyce::dout() << "\n";
3004  }
3005 #endif
3006 
3007  int i,j;
3008  int count = 0;
3009  std::string semi(bulkMaterial);
3010  Ut = Vt/scalingVars.V0;
3011  double pre = 1.0/Ut;
3012 
3013  double elecDens;
3014  double holeDens;
3015 
3016  int iNN;
3017 
3018  double coef;
3019  double ilen, elen, nodeArea;
3020 
3021 #ifdef Xyce_DEBUG_DEVICE
3022  double q = charge;
3023  if (getDeviceOptions().debugLevel > 1 && getSolverState().debugTimeFlag)
3024  {
3025  Xyce::dout() << "pre = " << pre << "\n";
3026  Xyce::dout() << "Vt = " << Vt << "\n";
3027  Xyce::dout() << "eps = " << eps << "\n";
3028  Xyce::dout() << "q = " << q << "\n";
3029  Xyce::dout() << "Na = " << Na << "\n";
3030  Xyce::dout() << "Nd = " << Nd << "\n";
3031  Xyce::dout() << "NpMax = " << NpMax << "\n";
3032  Xyce::dout() << "NnMax = " << NnMax << "\n";
3033  }
3034 #endif
3035 
3036  // note: fix the vals and cols arrays later!
3037  int numCol = cols.size();
3038  if (vals.size () < cols.size()) numCol = vals.size();
3039 
3040  // set up some of the partial derivative arrays:
3041  bs1 = pdRecombination (); bsuccess = bsuccess && bs1;
3042  bs1 = pdElectronCurrent (); bsuccess = bsuccess && bs1;
3043  bs1 = pdHoleCurrent (); bsuccess = bsuccess && bs1;
3044 
3045  // Load the jacobian, row by row.
3046  // rows associated with the connecting terminal KCL's:
3047  for (j=0;j<numCol;++j)
3048  {
3049  cols[j] = -1;
3050  vals[j] = 0.0;
3051  }
3052 
3053  std::vector<DeviceInterfaceNode>::iterator firstDI = dIVec.begin ();
3054  std::vector<DeviceInterfaceNode>::iterator lastDI = dIVec.end ();
3055  std::vector<DeviceInterfaceNode>::iterator iterDI;
3056 
3057  // If this PDE device is not coupled (always true for the nonlinear
3058  // poisson) to the ckt, put some 1's on the
3059  // diagonal to be safe. The method by which the matrix is set up
3060  // with 1's may or may not have already loaded these.
3061  vals[0] = 1.0;
3062  for(iterDI=firstDI;iterDI!=lastDI;++iterDI)
3063  {
3064  if (iterDI->gid !=-1)
3065  {
3066  cols[0] = iterDI->gid;
3067  bs1 = matPtr->putRow (iterDI->gid, 1, &vals[0], &cols[0]);
3068  bsuccess = bsuccess && bs1;
3069  }
3070  }
3071 
3072  // rows associated with the PDE mesh:
3073  if( useMatrixGIDFlag )
3074  {
3075  for (i=0;i<numMeshPoints;++i)
3076  {
3077 #ifdef Xyce_DEBUG_DEVICE
3078  if (getDeviceOptions().debugLevel > 2 && getSolverState().debugTimeFlag)
3079  {
3080  Xyce::dout() << "\nmesh point i = " << i << std::endl;
3081  }
3082 #endif
3083  for (j=0;j<numCol;++j)
3084  {
3085  cols[j] = -1;
3086  vals[j] = 0.0;
3087  }
3088 
3089  Vrow = Vrowarray[i];
3090  Nrow = Nrowarray[i];
3091  Prow = Prowarray[i];
3092 
3093 #ifdef Xyce_NEW_BC
3094  if (boundarySten[i]) continue;
3095 #else
3096  bool doneFlag = false;
3097 
3098  // Is this node a node with an explicit boundary condition?
3099  // If so, apply the BC. Otherwise, just load the region operators.
3100 
3101  if (boundarySten[i]) // do BC load
3102  {
3103  int DIindex = labelDIMap[labelNameVector[i]];
3104  if (dIVec[DIindex].given)
3105  {
3106  count = 0;
3107  if (Vrow != -1)
3108  {
3109  ++count;
3110  vals[0] = 1.0;
3111  cols[0] = Vrow;
3112 
3113  if (dIVec[DIindex].gid != -1)
3114  {
3115  ++count;
3116  vals[1] = -scalingVars.rV0;
3117  cols[1] = dIVec[DIindex].gid;
3118  }
3119  }
3120 
3121  if (count > 0)
3122  {
3123  bs1 = matPtr->putRow (Vrow, count, &vals[0], &cols[0]);
3124  bsuccess = bsuccess && bs1;
3125 
3126 #ifdef Xyce_DEBUG_DEVICE
3127  if (getDeviceOptions().debugLevel > 2 && getSolverState().debugTimeFlag)
3128  {
3129  Xyce::dout() << "BC load: Vrow = " << Vrow << std::endl;
3130  for (int eric=0;eric<count;++eric)
3131  {
3132  Xyce::dout() << " cols["<<eric<<"] = " << cols[eric];
3133  Xyce::dout() << " vals["<<eric<<"] = " << vals[eric];
3134  Xyce::dout() << std::endl;
3135  }
3136  }
3137 #endif
3138  }
3139 
3140  if (Nrow != -1)
3141  {
3142  count = 1;
3143  vals[0] = 1.0;
3144  cols[0] = Nrow;
3145  bs1 = matPtr->putRow (Nrow, 1, &vals[0], &cols[0]);
3146  bsuccess = bsuccess && bs1;
3147 
3148 #ifdef Xyce_DEBUG_DEVICE
3149  if (getDeviceOptions().debugLevel > 2 && getSolverState().debugTimeFlag)
3150  {
3151  Xyce::dout() << "BC load: Nrow = " << Nrow;
3152  Xyce::dout() << " coef = " << vals[0] << std::endl;
3153  }
3154 #endif
3155  }
3156 
3157  if (Prow != -1)
3158  {
3159  count = 1;
3160  vals[0] = 1.0;
3161  cols[0] = Prow;
3162 
3163  bs1 = matPtr->putRow (Prow, 1, &vals[0], &cols[0]);
3164  bsuccess = bsuccess && bs1;
3165 
3166 #ifdef Xyce_DEBUG_DEVICE
3167  if (getDeviceOptions().debugLevel > 2 && getSolverState().debugTimeFlag)
3168  {
3169  Xyce::dout() << "BC load: Prow = " << Vrow;
3170  Xyce::dout() << " coef = " << vals[0] << std::endl;
3171  }
3172 #endif
3173  }
3174 
3175  doneFlag = true;
3176 
3177  } // inner if (given if)
3178  } // outer if (name if)
3179 
3180  if (doneFlag) continue;
3181 #endif // Xyce_NEW_BC
3182 
3183  // if interior point:
3184 
3185  // if load is not done yet, then do an interior point load:
3186 #ifdef Xyce_OXIDE_ENABLED
3187  if (!allOxideFlag)
3188  {
3189  holeDens = getVoltDepHoleDens ( VminExp, VVec[i], NpMax);
3190  elecDens = getVoltDepElecDens ( VmaxExp, VVec[i], NnMax);
3191  }
3192 #else
3193  holeDens = getVoltDepHoleDens ( VminExp, VVec[i], NpMax);
3194  elecDens = getVoltDepElecDens ( VmaxExp, VVec[i], NnMax);
3195 #endif
3196 
3197  mNode * nodePtr = meshContainerPtr->getNode(i);
3198  nodeArea = nodePtr->area;
3199 
3200  // center point:
3201  coef = 0.0;
3202  count = 0;
3203  for (iNN=0;iNN<nodePtr->cnode;++iNN)
3204  {
3205  int inodeB = nodePtr->edgeInfoVector[iNN].inode;
3206  ilen = nodePtr->edgeInfoVector[iNN].ilen;
3207  elen = nodePtr->edgeInfoVector[iNN].elen;
3208 
3209  coef += -ilen/elen;
3210  }
3211  coef *= -scalingVars.L0 * matSupport.getRelPerm(semi)/nodeArea;
3212 
3213  // now add the terms associated with the electron and hole densities:
3214 #ifdef Xyce_OXIDE_ENABLED
3215  if (!allOxideFlag)
3216  {
3217  coef += pre*holeDens + pre*elecDens;
3218  }
3219 #else
3220  coef += pre*holeDens + pre*elecDens;
3221 #endif
3222 
3223  if (Vcolarray[i][0] != -1)
3224  {
3225  cols[count] = Vcolarray[i][0];
3226  vals[count] = coef;
3227  ++count;
3228  }
3229  else
3230  {
3231  Xyce::dout() << " center point: i="<<i;
3232  Xyce::dout() << " xVec[i] = " << xVec[i];
3233  Xyce::dout() << " yVec[i] = " << yVec[i];
3234  Xyce::dout() << std::endl;
3235  Xyce::dout() << " label = " << labelNameVector[i] << std::endl;
3236  Xyce::dout() << " boundarySten = " << boundarySten[i] << std::endl;
3237  Xyce::dout() << " Vcolarray[i][0] = ";
3238  Xyce::dout() << Vcolarray[i][0] << std::endl;
3239  }
3240 
3241  // neighbor points:
3242  coef = 0.0;
3243  for (iNN=0;iNN<nodePtr->cnode;++iNN)
3244  {
3245 #ifdef Xyce_DEBUG_DEVICE
3246  if (getDeviceOptions().debugLevel > 2 && getSolverState().debugTimeFlag)
3247  {
3248  Xyce::dout() << "non-center point: i="<<i;
3249  Xyce::dout() << " Vcolarray[i]["<<iNN+1<<"] = ";
3250  Xyce::dout() << Vcolarray[i][iNN+1] << std::endl;
3251  }
3252 #endif
3253  if (Vcolarray[i][iNN+1] == -1) continue;
3254 
3255  int inodeB = nodePtr->edgeInfoVector[iNN].inode;
3256  ilen = nodePtr->edgeInfoVector[iNN].ilen;
3257  elen = nodePtr->edgeInfoVector[iNN].elen;
3258 
3259  coef = ilen/elen;
3260  coef *= -scalingVars.L0 * matSupport.getRelPerm(semi)/nodeArea;
3261 
3262  cols[count] = Vcolarray[i][iNN+1];
3263  vals[count] = coef;
3264  ++count;
3265  }
3266 
3267 #ifdef Xyce_DEBUG_DEVICE
3268  if (getDeviceOptions().debugLevel > 2 && getSolverState().debugTimeFlag)
3269  {
3270  Xyce::dout() << "\n";
3271  Xyce::dout().setf(std::ios::left);
3272  Xyce::dout() << " POISSON LOAD: row = " << Vrow;
3273  Xyce::dout() << " count = " << count << "\n";
3274  Xyce::dout().setf(std::ios::scientific);
3275  for (j=0;j<count;++j)
3276  {
3277  Xyce::dout() << " cols["<<j<<"] = " << cols[j];
3278  Xyce::dout() << " vals["<<j<<"] = " << vals[j];
3279  Xyce::dout() << std::endl;
3280  }
3281  }
3282 #endif
3283 
3284  if (Vrow != -1 && count > 0)
3285  {
3286  bs1 = matPtr->putRow (Vrow, count, &vals[0], &cols[0]);
3287  bsuccess = bsuccess && bs1;
3288  }
3289  else
3290  {
3291  Xyce::dout() << "OOOPS!" << std::endl;
3292  exit(0);
3293  }
3294 
3295 
3296  if (Nrow != -1)
3297  {
3298  vals[0] = 1.0;
3299  cols[0] = Nrow;
3300  bs1 = matPtr->putRow (Nrow, 1, &vals[0], &cols[0]);
3301  bsuccess = bsuccess && bs1;
3302  }
3303 
3304  if (Prow != -1)
3305  {
3306  vals[0] = 1.0;
3307  cols[0] = Prow;
3308  bs1 = matPtr->putRow (Prow, 1, &vals[0], &cols[0]);
3309  bsuccess = bsuccess && bs1;
3310  }
3311 
3312 #ifdef Xyce_DEBUG_DEVICE
3313  if (getDeviceOptions().debugLevel > 2 && getSolverState().debugTimeFlag)
3314  {
3315  Xyce::dout() << subsection_divider << "\n";
3316  }
3317 #endif
3318  } // mesh point for loop
3319  }
3320  else // direct Matrix Access
3321  {
3322  for (i=0;i<numMeshPoints;++i)
3323  {
3324  Vrow = li_Vrowarray[i];
3325  Nrow = li_Nrowarray[i];
3326  Prow = li_Prowarray[i];
3327 
3328  std::vector<int> & Voff = li_VoffsetArray[i];
3329  std::vector<int> & Noff = li_NoffsetArray[i];
3330  std::vector<int> & Poff = li_PoffsetArray[i];
3331 
3332 #ifdef Xyce_NEW_BC
3333  if (boundarySten[i]) continue;
3334 #else
3335  bool doneFlag = false;
3336 
3337  // Is this node a node with an explicit boundary condition?
3338  // If so, apply the BC. Otherwise, just load the region operators.
3339 
3340  if (boundarySten[i]) // do BC load
3341  {
3342  int DIindex = labelDIMap[labelNameVector[i]];
3343  if (dIVec[DIindex].given)
3344  {
3345  // poisson BC
3346  // Assuming the second offset is the (Vrow, ckt_gid) entry.
3347  // CHECK this!
3348  (*matPtr)[Vrow][Voff[0]] = 1.0;
3349  (*matPtr)[Vrow][Voff[1]] = -scalingVars.rV0;
3350 
3351  // electron equation is "off"
3352  (*matPtr)[Nrow][Noff[0]] = 1.0;
3353 
3354  // hole equation is "off"
3355  (*matPtr)[Prow][Poff[0]] = 1.0;
3356 
3357  doneFlag = true;
3358  } // inner if (given if)
3359  } // outer if (name if)
3360 
3361  if (doneFlag) continue;
3362 #endif // Xyce_NEW_BC
3363 
3364  // if interior point:
3365 
3366  // if load is not done yet, then do an interior point load:
3367 #ifdef Xyce_OXIDE_ENABLED
3368  if (!allOxideFlag)
3369  {
3370  holeDens = getVoltDepHoleDens ( VminExp, VVec[i], NpMax);
3371  elecDens = getVoltDepElecDens ( VmaxExp, VVec[i], NnMax);
3372  }
3373 #else
3374  holeDens = getVoltDepHoleDens ( VminExp, VVec[i], NpMax);
3375  elecDens = getVoltDepElecDens ( VmaxExp, VVec[i], NnMax);
3376 #endif
3377 
3378  mNode * nodePtr = meshContainerPtr->getNode(i);
3379  nodeArea = nodePtr->area;
3380 
3381  // center point:
3382  coef = 0.0;
3383  count = 0;
3384  for (iNN=0;iNN<nodePtr->cnode;++iNN)
3385  {
3386  int inodeB = nodePtr->edgeInfoVector[iNN].inode;
3387  ilen = nodePtr->edgeInfoVector[iNN].ilen;
3388  elen = nodePtr->edgeInfoVector[iNN].elen;
3389 
3390  coef += -ilen/elen;
3391  }
3392  coef *= -scalingVars.L0 * matSupport.getRelPerm(semi)/nodeArea;
3393 
3394  // now add the terms associated with the electron and hole densities:
3395 #ifdef Xyce_OXIDE_ENABLED
3396  if (!allOxideFlag)
3397  {
3398  coef += pre*holeDens + pre*elecDens;
3399  }
3400 #else
3401  coef += pre*holeDens + pre*elecDens;
3402 #endif
3403 
3404  (*matPtr)[Vrow][Voff[0]] += coef;
3405 
3406  // neighbor points:
3407  coef = 0.0;
3408  for (iNN=0;iNN<nodePtr->cnode;++iNN)
3409  {
3410  // If the neighbor is a boundary, skip.
3411  // Do I need to do this if statement? CHECK!
3412  if (Vcolarray[i][iNN+1] == -1) continue;
3413 
3414  int inodeB = nodePtr->edgeInfoVector[iNN].inode;
3415  ilen = nodePtr->edgeInfoVector[iNN].ilen;
3416  elen = nodePtr->edgeInfoVector[iNN].elen;
3417 
3418  coef = ilen/elen;
3419  coef *= -scalingVars.L0 * matSupport.getRelPerm(semi)/nodeArea;
3420 
3421  (*matPtr)[Vrow][Voff[iNN+1]] += coef;
3422  }
3423 
3424  // electron equation is "off"
3425  (*matPtr)[Nrow][Noff[0]] = 1.0;
3426 
3427  // hole equation is "off"
3428  (*matPtr)[Prow][Poff[0]] = 1.0;
3429 
3430  } // mesh point loop
3431  } // direct access
3432 
3433 #ifdef Xyce_DEBUG_DEVICE
3434  if (getDeviceOptions().debugLevel > 1 && getSolverState().debugTimeFlag)
3435  {
3436  Xyce::dout() << section_divider << std::endl;
3437  }
3438 #endif
3439 
3440  return bsuccess;
3441 }
3442 
3443 //-----------------------------------------------------------------------------
3444 // Function : Instance::loadMatKCLDDForm
3445 // Purpose :
3446 // Special Notes :
3447 // Scope : private
3448 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
3449 // Creation Date : 06/24/05
3450 //-----------------------------------------------------------------------------
3451 bool Instance::loadMatKCLDDForm (N_LAS_Matrix * matPtr)
3452 {
3453  bool bsuccess = true;
3454  bool bs1 = true;
3455  int j;
3456  int count;
3457 
3458 #ifdef Xyce_DEBUG_DEVICE
3459  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
3460  {
3461  Xyce::dout() << "Starting Instance::loadJacKCLDDFormulation" << std::endl;
3462  }
3463 #endif
3464 
3465  std::vector<DeviceInterfaceNode>::iterator firstDI = dIVec.begin ();
3466  std::vector<DeviceInterfaceNode>::iterator lastDI = dIVec.end ();
3467  std::vector<DeviceInterfaceNode>::iterator iterDI;
3468 
3469  if( useMatrixGIDFlag )
3470  {
3471  int numCol = cols.size();
3472  if (vals.size () < cols.size()) numCol = vals.size();
3473 
3474  // Initialize the cols and vals arrays:
3475  for (j=0;j<numCol;++j)
3476  {
3477  cols[j] = -1;
3478  vals[j] = 0.0;
3479  }
3480 
3481  for(iterDI=firstDI;iterDI!=lastDI;++iterDI)
3482  {
3483  if (iterDI->gid ==-1) continue;
3484  count = 0;
3485  // dependence of electrode current on ckt node voltage.
3486  cols[count] = iterDI->gid;
3487  vals[count] = 0.0; // Setting this to zero is correct for
3488  // the "old" boundary conditions. For the
3489  // the "new" boundary conditions, this will
3490  // eventually be a nonzero number.
3491  count=1;
3492 
3493 #ifdef Xyce_NEW_BC
3494  vals[0] = iterDI->dIdVckt; // calculated in pdTerminalCurrents
3495 
3496 #ifdef Xyce_DEBUG_DEVICE
3497  if (getDeviceOptions().debugLevel > 2 && getSolverState().debugTimeFlag)
3498  {
3499  Xyce::dout() << "\n";
3500  Xyce::dout().setf(std::ios::left);
3501  Xyce::dout() << " KCL new BC LOAD: row = " << iterDI->gid;
3502  Xyce::dout() << " count = " << count;
3503  Xyce::dout() << " name = " << iterDI->eName << "\n";
3504 
3505  Xyce::dout().setf(std::ios::scientific);
3506  Xyce::dout() << " cols[0] = " << cols[0];
3507  Xyce::dout() << " vals[0] = " << vals[0];
3508  Xyce::dout() << std::endl;
3509  }
3510 #endif
3511  bs1 = matPtr->sumIntoRow (iterDI->gid, count, &vals[0], &cols[0]);
3512  bsuccess = bsuccess && bs1;
3513 #endif // Xyce_NEW_BC
3514 
3515  // Now do the dependence of the electrode current on
3516  // all the PDE vars. This was calculated in pdTerminalCurrents.
3517  count = iterDI->dIdXcols.size();
3518 
3519 #ifdef Xyce_DEBUG_DEVICE
3520  if (getDeviceOptions().debugLevel > 2 && getSolverState().debugTimeFlag)
3521  {
3522  Xyce::dout() << "\n";
3523  Xyce::dout().setf(std::ios::left);
3524  Xyce::dout() << " KCL LOAD: row = " << iterDI->gid;
3525  Xyce::dout() << " count = " << count;
3526  Xyce::dout() << " name = " << iterDI->eName << "\n";
3527 
3528  Xyce::dout().setf(std::ios::scientific);
3529  for (j=0;j<count;++j)
3530  {
3531  Xyce::dout() << " cols["<<j<<"] = " << iterDI->dIdXcols[j];
3532  Xyce::dout() << " vals["<<j<<"] = " << iterDI->dIdX[j];
3533  Xyce::dout() << std::endl;
3534  }
3535  }
3536 #endif
3537 
3538  bs1 = matPtr->sumIntoRow
3539  (iterDI->gid, count, &(iterDI->dIdX[0]), &(iterDI->dIdXcols[0]));
3540  bsuccess = bsuccess && bs1;
3541 
3542  } // end of DI loop
3543  }
3544  else // use direct matrix access
3545  {
3546  for(iterDI=firstDI;iterDI!=lastDI;++iterDI)
3547  {
3548  // First, dependence of electrode current on ckt node voltage.
3549  double coef = 0.0; // "old" BC value.
3550  int offset = iterDI->lidOffset;
3551 
3552 #ifdef Xyce_NEW_BC
3553  coef = iterDI->dIdVckt;
3554 #endif // Xyce_NEW_BC
3555 
3556  (*matPtr)[iterDI->lid][offset] += coef;
3557 
3558  // Now do the dependence of the electrode current on
3559  // all the PDE vars. This was calculated in pdTerminalCurrents.
3560  int size = iterDI->dIdXcols.size();
3561  for (int i=0;i<size;++i)
3562  {
3563  coef = iterDI->dIdX[i];
3564  offset = iterDI->dIdXoffset[i];
3565  (*matPtr)[iterDI->lid][offset] += coef;
3566 #ifdef Xyce_DEBUG_DEVICE
3567  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
3568  {
3569  Xyce::dout() << "dIdX["<<i<<"] = " << iterDI->dIdX[i];
3570  Xyce::dout() << " dIdXcols["<<i<<"] = " << iterDI->dIdXcols[i];
3571  Xyce::dout() << " dIdXoffset["<<i<<"] = " << iterDI->dIdXoffset[i];
3572  Xyce::dout() << std::endl;
3573  }
3574 #endif
3575  }
3576  } // end of DI loop
3577  }
3578 
3579 #ifdef Xyce_DEBUG_DEVICE
3580  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
3581  {
3582  Xyce::dout() << "Done with Instance::loadJacKCLDDFormulation" << std::endl;
3583  }
3584 #endif
3585  return bsuccess;
3586 }
3587 
3588 //-----------------------------------------------------------------------------
3589 // Function : Instance::loadMatCktTrivial
3590 // Purpose :
3591 // Special Notes : if this PDE device is not coupled to the ckt, put
3592 // some 1's on the diagonal just to be safe. The method by
3593 // which the matrix is set up with 1's may or may not have
3594 // already loaded these. This may be obsolete later, with
3595 // new petra functionality.
3596 //
3597 // Scope : public
3598 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
3599 // Creation Date : 06/24/05
3600 //-----------------------------------------------------------------------------
3601 bool Instance::loadMatCktTrivial (N_LAS_Matrix * matPtr)
3602 {
3603  bool bsuccess = true;
3604  bool bs1 = true;
3605 
3606  // note: fix the vals and cols arrays later!
3607  int numCol = cols.size();
3608  if (vals.size () < cols.size()) numCol = vals.size();
3609 
3610  std::vector<DeviceInterfaceNode>::iterator firstDI = dIVec.begin ();
3611  std::vector<DeviceInterfaceNode>::iterator lastDI = dIVec.end ();
3612  std::vector<DeviceInterfaceNode>::iterator iterDI;
3613 
3614  if( useMatrixGIDFlag )
3615  {
3616  vals[0] = 1.0;
3617  for(iterDI=firstDI;iterDI!=lastDI;++iterDI)
3618  {
3619  if (iterDI->gid !=-1)
3620  {
3621  cols[0] = iterDI->gid;
3622  bs1 = matPtr->putRow (iterDI->gid, 1, &vals[0], &cols[0]);
3623  bsuccess = bsuccess && bs1;
3624  }
3625  }
3626  }
3627  else // using direct matrix access
3628  {
3629  for(iterDI=firstDI;iterDI!=lastDI;++iterDI)
3630  {
3631  (*matPtr)[iterDI->lid][iterDI->lidOffset] = 1.0;
3632  }
3633  }
3634 
3635  return bsuccess;
3636 }
3637 
3638 //-----------------------------------------------------------------------------
3639 // Function : Instance::loadMatDDForm
3640 // Purpose :
3641 // Special Notes :
3642 // Scope : private
3643 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
3644 // Creation Date : 6/24/05
3645 //-----------------------------------------------------------------------------
3647 (double dndtScalar, N_LAS_Matrix * matPtr)
3648 {
3649  bool bsuccess = true;
3650  bool bs1 = true;
3651 
3652  int Vrow, Nrow, Prow;
3653 
3654 #ifdef Xyce_DEBUG_DEVICE
3655  if (getDeviceOptions().debugLevel > 1 && getSolverState().debugTimeFlag)
3656  {
3657  Xyce::dout() << "\n"<<section_divider << "\n";
3658  Xyce::dout() << "Instance::loadMatDDForm" << "\n";
3659  Xyce::dout() << " name = " << getName() <<"\n";
3660  Xyce::dout() << "\n";
3661  }
3662 #endif
3663 
3664  int i,j;
3665  int iNN;
3666  int count = 0;
3667  int cnt2 = 0;
3668  int iedge = 0;
3669  int inodeB = 0;
3670  bool bmatch;
3671 
3672  double coef;
3673  double ilen, elen, nodeArea;
3674 
3675  // note: fix the vals and cols arrays later!
3676  int numCol = cols.size();
3677  if (vals.size () < cols.size()) numCol = vals.size();
3678 
3679 #ifdef Xyce_DEBUG_DEVICE
3680 #endif
3681 
3682  // obtain partial time derivatives, scale by scalingVars.t0:
3683  // dndtScalar is either 1.0 (old DAE) or 0.0 (new DAE).
3684  double dDNDTdn;
3685  double dDPDTdp;
3686 
3687  if (!(getSolverState().dcopFlag))
3688  {
3689  dDNDTdn = getSolverState().pdt*scalingVars.t0*dndtScalar;
3690  dDPDTdp = getSolverState().pdt*scalingVars.t0*dndtScalar;
3691  }
3692  else
3693  {
3694  dDNDTdn = 0.0;
3695  dDPDTdp = 0.0;
3696  }
3697 
3698  // Initialize the cols and vals arrays:
3699  for (j=0;j<numCol;++j) { cols[j] = -1; vals[j] = 0.0; }
3700 
3701  // load the rows associated with the PDE mesh:
3702  if( useMatrixGIDFlag )
3703  {
3704  for (i=0;i<numMeshPoints;++i)
3705  {
3706  Vrow = Vrowarray[i];
3707  Nrow = Nrowarray[i];
3708  Prow = Prowarray[i];
3709 
3710 #ifdef Xyce_DEBUG_DEVICE
3711  if (getDeviceOptions().debugLevel > 2 && getSolverState().debugTimeFlag)
3712  {
3713  Xyce::dout() << subsection_divider << "\n";
3714  Xyce::dout() << "mesh point i = " << i;
3715  Xyce::dout() << " Vrow = " << Vrow;
3716  Xyce::dout() << " Nrow = " << Nrow;
3717  Xyce::dout() << " Prow = " << Prow;
3718  Xyce::dout() << "\n";
3719  }
3720 #endif
3721 
3722  bool doneFlagV = false;
3723  bool doneFlagN = false;
3724  bool doneFlagP = false;
3725  bool doneFlag = false;
3726 #ifdef Xyce_NEW_BC
3727  if (boundarySten[i]) continue;
3728 #else
3729  // Is this node a node with an explicit boundary condition?
3730  // If so, apply the BC. Otherwise, just load the region operators.
3731 
3732  if (boundarySten[i]) //do BC load
3733  {
3734  int DIindex = labelDIMap[labelNameVector[i]];
3735 
3736  if (dIVec[DIindex].given)
3737  {
3738  if (dIVec[DIindex].neumannBCFlagV==false)
3739  {
3740 #ifdef Xyce_DEBUG_DEVICE
3741  if (getDeviceOptions().debugLevel > 2 && getSolverState().debugTimeFlag)
3742  {
3743  Xyce::dout() << "BC load: Vrow = " << Vrow << std::endl;
3744  }
3745 #endif
3746  count = 0;
3747  if (Vrow != -1)
3748  {
3749  ++count;
3750  vals[0] = 1.0;
3751  cols[0] = Vrow;
3752 
3753  if (dIVec[DIindex].gid != -1)
3754  {
3755  ++count;
3756  vals[1] = -scalingVars.rV0;
3757  cols[1] = dIVec[DIindex].gid;
3758  }
3759  }
3760 
3761  if (count > 0)
3762  {
3763  bs1 = matPtr->putRow (Vrow, count, &vals[0], &cols[0]);
3764  bsuccess = bsuccess && bs1;
3765 
3766 #ifdef Xyce_DEBUG_DEVICE
3767  if (getDeviceOptions().debugLevel > 2 && getSolverState().debugTimeFlag)
3768  {
3769  Xyce::dout() << "BC load: Vrow = " << Vrow << std::endl;
3770  for (int eric=0;eric<count;++eric)
3771  {
3772  Xyce::dout() << " cols["<<eric<<"] = " << cols[eric];
3773  Xyce::dout() << " vals["<<eric<<"] = " << vals[eric];
3774  Xyce::dout() << std::endl;
3775  }
3776  }
3777 #endif
3778 
3779  } // Vrow if statement.
3780  doneFlagV = true;
3781  } // neumann flag if statement.
3782 
3783  if (dIVec[DIindex].neumannBCFlagN==false)
3784  {
3785  if (Nrow != -1)
3786  {
3787  count = 1;
3788  vals[0] = 1.0;
3789  cols[0] = Nrow;
3790  bs1 = matPtr->putRow (Nrow, 1, &vals[0], &cols[0]);
3791  bsuccess = bsuccess && bs1;
3792 
3793 #ifdef Xyce_DEBUG_DEVICE
3794  if (getDeviceOptions().debugLevel > 2 && getSolverState().debugTimeFlag)
3795  {
3796  Xyce::dout() << "BC load: Nrow = " << Nrow << std::endl;
3797  for (int eric=0;eric<count;++eric)
3798  {
3799  Xyce::dout() << " cols["<<eric<<"] = " << cols[eric];
3800  Xyce::dout() << " vals["<<eric<<"] = " << vals[eric];
3801  Xyce::dout() << std::endl;
3802  }
3803  }
3804 #endif
3805  } // Nrow if statement.
3806  doneFlagN = true;
3807  } // neumann flag if statement.
3808 
3809  if (dIVec[DIindex].neumannBCFlagP==false)
3810  {
3811  if (Prow != -1)
3812  {
3813  count = 1;
3814  vals[0] = 1.0;
3815  cols[0] = Prow;
3816  bs1 = matPtr->putRow (Prow, 1, &vals[0], &cols[0]);
3817  bsuccess = bsuccess && bs1;
3818 
3819 #ifdef Xyce_DEBUG_DEVICE
3820  if (getDeviceOptions().debugLevel > 2 && getSolverState().debugTimeFlag)
3821  {
3822  Xyce::dout() << "BC load: Prow = " << Prow << std::endl;
3823  for (int eric=0;eric<count;++eric)
3824  {
3825  Xyce::dout() << " cols["<<eric<<"] = " << cols[eric];
3826  Xyce::dout() << " vals["<<eric<<"] = " << vals[eric];
3827  Xyce::dout() << std::endl;
3828  }
3829  }
3830 #endif
3831  } // Prow if statement.
3832  doneFlagP = true;
3833  } // neumann flag if statement.
3834 
3835  doneFlag = (doneFlagV && doneFlagN && doneFlagP);
3836  } // inner if
3837  } // outer if
3838 
3839  if (doneFlag) continue;
3840 
3841 #endif // Xyce_NEW_BC
3842 
3843  // if load is not done yet, then do an interior point load:
3844  std::string semi(bulkMaterial);
3845  // Poisson's equation:
3846  mNode * nodePtr = meshContainerPtr->getNode(i);
3847  nodeArea = nodePtr->area;
3848 
3849  // center point, with respect to V:
3850  coef = 0.0;
3851  count = 0;
3852  for (j=0;j<numCol;++j) { cols[j] = -1; vals[j] = 0.0; }
3853 
3854  iNN=0;
3855 
3856  if (!doneFlagV)
3857  {
3858  for (iNN=0;iNN<nodePtr->cnode;++iNN)
3859  {
3860  int inodeB = nodePtr->edgeInfoVector[iNN].inode;
3861  ilen = nodePtr->edgeInfoVector[iNN].ilen;
3862  elen = nodePtr->edgeInfoVector[iNN].elen;
3863 
3864  coef += -ilen/elen;
3865  }
3866  coef *= -scalingVars.L0 * matSupport.getRelPerm(semi)/nodeArea;
3867 
3868  if (Vcolarray[i][0] != -1)
3869  {
3870  cols[count] = Vcolarray[i][0];
3871  vals[count] = coef;
3872  ++count;
3873  }
3874 #ifdef Xyce_DEBUG_DEVICE
3875  else
3876  {
3877  Xyce::dout() << " center point: i="<<i;
3878  Xyce::dout() << " Vcolarray[i][0] = " << Vcolarray[i][0] << std::endl;
3879  }
3880 #endif
3881 
3882  // neighbor points, with respect to V:
3883  coef = 0.0;
3884  iNN=0;
3885  for (iNN=0;iNN<nodePtr->cnode;++iNN)
3886  {
3887 #ifdef Xyce_DEBUG_DEVICE
3888  if (getDeviceOptions().debugLevel > 2 && getSolverState().debugTimeFlag)
3889  {
3890  Xyce::dout() << "non-center point: i="<<i;
3891  Xyce::dout() << " Vcolarray[i]["<<iNN+1<<"] = ";
3892  Xyce::dout() << Vcolarray[i][iNN+1] << std::endl;
3893  }
3894 #endif
3895  int tmpCol = Vcolarray[i][iNN+1];
3896 
3897  if (tmpCol == -1) continue;
3898 
3899  int inodeB = nodePtr->edgeInfoVector[iNN].inode;
3900  ilen = nodePtr->edgeInfoVector[iNN].ilen;
3901  elen = nodePtr->edgeInfoVector[iNN].elen;
3902 
3903  coef = ilen/elen;
3904  coef *= -scalingVars.L0 * matSupport.getRelPerm(semi)/nodeArea;
3905 #ifdef Xyce_NEW_BC
3906  coef *= ((boundarySten[inodeB]==1)?(scalingVars.rV0):1.0);
3907 
3908  // check if node == any previous nodes in the cols array
3909  if (boundarySten[inodeB]==1)
3910  {
3911  bmatch = false;
3912  for (cnt2=0;cnt2<count;++cnt2)
3913  {
3914  if (cols[cnt2] == tmpCol)
3915  { bmatch = true; break; }
3916  }
3917  if (!bmatch) { cnt2 = count; ++count;}
3918  }
3919  else
3920  {
3921  cnt2 = count;
3922  ++count;
3923  }
3924 #else
3925  cnt2 = count;
3926  ++count;
3927 #endif // Xyce_NEW_BC
3928  cols[cnt2] = tmpCol;
3929  vals[cnt2] += coef;
3930  }
3931 
3932  // center point, with repsect to electron density:
3933  if (Ncolarray[i][0] != -1)
3934  {
3935  cols[count] = Ncolarray[i][0];
3936  vals[count] = +1.0;
3937  ++count;
3938  }
3939 
3940  // center point, with respect to hole density:
3941  if (Pcolarray[i][0] != -1)
3942  {
3943  cols[count] = Pcolarray[i][0];
3944  vals[count] = -1.0;
3945  ++count;
3946  }
3947 
3948 #ifdef Xyce_DEBUG_DEVICE
3949  if (getDeviceOptions().debugLevel > 2 && getSolverState().debugTimeFlag)
3950  {
3951  Xyce::dout() << "\n";
3952  Xyce::dout().setf(std::ios::left);
3953  Xyce::dout() << " POISSON LOAD: row = " << Vrow;
3954  Xyce::dout() << " count = " << count << "\n";
3955  Xyce::dout().setf(std::ios::scientific);
3956  for (j=0;j<count;++j)
3957  {
3958  Xyce::dout() << " cols["<<j<<"] = " << cols[j];
3959  Xyce::dout() << " vals["<<j<<"] = " << vals[j];
3960  Xyce::dout() << std::endl;
3961  }
3962  }
3963 #endif
3964 
3965  if (Vrow != -1 && count > 0)
3966  {
3967  bs1 = matPtr->sumIntoRow (Vrow, count, &vals[0], &cols[0]);
3968  bsuccess = bsuccess && bs1;
3969  }
3970  else
3971  {
3972  Xyce::dout() << "OOOPS 1!" << std::endl;
3973  exit(0);
3974  }
3975  doneFlagV = true;
3976  } // doneFlagV if statement.
3977 
3978 
3979  // electron continuity row:
3980  if (!doneFlagN)
3981  {
3982  for (j=0;j<numCol;++j) { cols[j] = -1; vals[j] = 0.0; }
3983 
3984  count = 0;
3985  coef = 0.0;
3986 
3987  // center point first: (with respect to nn)
3988  for (iNN=0;iNN<nodePtr->cnode;++iNN)
3989  {
3990  iedge = nodePtr->edgeInfoVector[iNN].iedge;
3991  inodeB = nodePtr->edgeInfoVector[iNN].inode;
3992  ilen = nodePtr->edgeInfoVector[iNN].ilen;
3993  elen = nodePtr->edgeInfoVector[iNN].elen;
3994 
3995  double dJdn = 0.0;
3996  if (i<inodeB)
3997  {
3998  dJdn = dJndn1Vec[iedge];
3999  }
4000  else
4001  {
4002  dJdn = dJndn2Vec[iedge];
4003  }
4004 
4005  coef += ((i<inodeB)?1.0:-1.0) * dJdn * ilen;
4006  }
4007 
4008  coef /= nodeArea;
4009  coef += - dRdnVec[i] - dDNDTdn;
4010 
4011  coef += pdElecPenalty[i];
4012 
4013  vals[count] = coef;
4014  cols[count] = Ncolarray[i][0];
4015  ++count;
4016 
4017  // neighbor points, with respect to nn:
4018  coef = 0.0;
4019  for (iNN=0;iNN<nodePtr->cnode;++iNN)
4020  {
4021  if (Ncolarray[i][iNN+1] == -1) continue;
4022 
4023  iedge = nodePtr->edgeInfoVector[iNN].iedge;
4024  inodeB = nodePtr->edgeInfoVector[iNN].inode;
4025  ilen = nodePtr->edgeInfoVector[iNN].ilen;
4026  elen = nodePtr->edgeInfoVector[iNN].elen;
4027 
4028  double dJdn = 0.0;
4029  if (i>inodeB)
4030  {
4031  dJdn = dJndn1Vec[iedge];
4032  }
4033  else
4034  {
4035  dJdn = dJndn2Vec[iedge];
4036  }
4037 
4038  coef = ((i<inodeB)?1.0:-1.0) * dJdn * ilen/nodeArea;
4039 
4040  vals[count] = coef;
4041  cols[count] = Ncolarray[i][iNN+1];
4042 
4043  ++count;
4044  }
4045 
4046  // center point, with respect to V:
4047  coef = 0.0;
4048  for (iNN=0;iNN<nodePtr->cnode;++iNN)
4049  {
4050  iedge = nodePtr->edgeInfoVector[iNN].iedge;
4051  inodeB = nodePtr->edgeInfoVector[iNN].inode;
4052  ilen = nodePtr->edgeInfoVector[iNN].ilen;
4053  elen = nodePtr->edgeInfoVector[iNN].elen;
4054 
4055  double dJdV = 0.0;
4056  if (i<inodeB)
4057  {
4058  dJdV = dJndV1Vec[iedge];
4059  }
4060  else
4061  {
4062  dJdV = dJndV2Vec[iedge];
4063  }
4064 
4065  coef += ((i<inodeB)?1.0:-1.0) * dJdV * ilen;
4066  }
4067  coef /= nodeArea;
4068 
4069  if (Vcolarray[i][0] != -1)
4070  {
4071  vals[count] = coef;
4072  cols[count] = Vcolarray[i][0];
4073  ++count;
4074  }
4075 
4076  // neighbor points, with respect to V:
4077  coef = 0.0;
4078  for (iNN=0;iNN<nodePtr->cnode;++iNN)
4079  {
4080  int tmpCol = Vcolarray[i][iNN+1];
4081  if (tmpCol == -1) continue;
4082 
4083  iedge = nodePtr->edgeInfoVector[iNN].iedge;
4084  inodeB = nodePtr->edgeInfoVector[iNN].inode;
4085  ilen = nodePtr->edgeInfoVector[iNN].ilen;
4086  elen = nodePtr->edgeInfoVector[iNN].elen;
4087 
4088  double dJdV = 0.0;
4089 
4090  if (i>inodeB)
4091  {
4092  dJdV = dJndV1Vec[iedge];
4093  }
4094  else
4095  {
4096  dJdV = dJndV2Vec[iedge];
4097  }
4098 
4099  coef = ((i<inodeB)?1.0:-1.0) * dJdV * ilen/nodeArea;
4100 #ifdef Xyce_NEW_BC
4101  coef *= ((boundarySten[inodeB]==1)?(scalingVars.rV0):1.0);
4102 
4103  // check if node == any previous nodes in the cols array
4104  if (boundarySten[inodeB]==1)
4105  {
4106  bmatch = false;
4107  for (cnt2=0;cnt2<count;++cnt2)
4108  {
4109  if (cols[cnt2] == tmpCol)
4110  { bmatch = true; break; }
4111  }
4112  if (!bmatch) { cnt2 = count; ++count;}
4113  }
4114  else
4115  {
4116  cnt2 = count;
4117  ++count;
4118  }
4119 #else
4120  cnt2 = count;
4121  ++count;
4122 #endif // Xyce_NEW_BC
4123  vals[cnt2] += coef;
4124  cols[cnt2] = tmpCol;
4125  }
4126 
4127  // center point, with respect to np:
4128  if (Pcolarray[i][0] != -1)
4129  {
4130  vals[count] = -dRdpVec[i];
4131  cols[count] = Pcolarray[i][0];
4132  ++count;
4133  }
4134 
4135 #ifdef Xyce_DEBUG_DEVICE
4136  if (getDeviceOptions().debugLevel > 2 && getSolverState().debugTimeFlag)
4137  {
4138  Xyce::dout() << "\n";
4139  Xyce::dout().setf(std::ios::left);
4140  Xyce::dout() << "ELECTRON LOAD: row = " << Nrow;
4141  Xyce::dout() << " count = " << count<< "\n";
4142  Xyce::dout().setf(std::ios::scientific);
4143  for (j=0;j<count;++j)
4144  {
4145  Xyce::dout() << " cols["<<j<<"] = " << cols[j];
4146  Xyce::dout() << " vals["<<j<<"] = " << vals[j];
4147  Xyce::dout() << std::endl;
4148  }
4149  }
4150 #endif
4151 
4152  if (Nrow != -1 && count > 0)
4153  {
4154  bs1 = matPtr->sumIntoRow (Nrow,count,&vals[0],&cols[0]);
4155  bsuccess = bsuccess && bs1;
4156  }
4157  else
4158  {
4159  Xyce::dout() << "OOOPS 2!" << std::endl;
4160  exit(0);
4161  }
4162 
4163  doneFlagN = true;
4164 
4165  } // doneFlagN if statement.
4166 
4167  // hole continuity row:
4168  if (!doneFlagP)
4169  {
4170  for (j=0;j<numCol;++j) { cols[j] = -1; vals[j] = 0.0; }
4171 
4172  count = 0;
4173  coef = 0.0;
4174 
4175  // center point first: (with respect to np)
4176  for (iNN=0;iNN<nodePtr->cnode;++iNN)
4177  {
4178  iedge = nodePtr->edgeInfoVector[iNN].iedge;
4179  inodeB = nodePtr->edgeInfoVector[iNN].inode;
4180  ilen = nodePtr->edgeInfoVector[iNN].ilen;
4181  elen = nodePtr->edgeInfoVector[iNN].elen;
4182 
4183  double dJdp = 0.0;
4184  if (i<inodeB)
4185  {
4186  dJdp = dJpdn1Vec[iedge];
4187  }
4188  else
4189  {
4190  dJdp = dJpdn2Vec[iedge];
4191  }
4192 
4193  coef += - ((i<inodeB)?1.0:-1.0) * dJdp * ilen;
4194  }
4195 
4196  coef /= nodeArea;
4197  coef += - dRdpVec[i] - dDPDTdp;
4198 
4199  coef += pdHolePenalty[i];
4200 
4201  vals[count] = coef;
4202  cols[count] = Pcolarray[i][0];
4203  ++count;
4204 
4205  // neighbor points, with respect to np:
4206  coef = 0.0;
4207  for (iNN=0;iNN<nodePtr->cnode;++iNN)
4208  {
4209  if (Pcolarray[i][iNN+1] == -1) continue;
4210 
4211  iedge = nodePtr->edgeInfoVector[iNN].iedge;
4212  inodeB = nodePtr->edgeInfoVector[iNN].inode;
4213  ilen = nodePtr->edgeInfoVector[iNN].ilen;
4214  elen = nodePtr->edgeInfoVector[iNN].elen;
4215 
4216  double dJdp = 0.0;
4217  if (i>inodeB)
4218  {
4219  dJdp = dJpdn1Vec[iedge];
4220  }
4221  else
4222  {
4223  dJdp = dJpdn2Vec[iedge];
4224  }
4225 
4226  coef =-((i<inodeB)?1.0:-1.0) * dJdp * ilen/nodeArea;
4227 
4228  vals[count] = coef;
4229  cols[count] = Pcolarray[i][iNN+1];
4230  ++count;
4231  }
4232 
4233  // center point, with respect to V:
4234  coef = 0.0;
4235  for (iNN=0;iNN<nodePtr->cnode;++iNN)
4236  {
4237  iedge = nodePtr->edgeInfoVector[iNN].iedge;
4238  inodeB = nodePtr->edgeInfoVector[iNN].inode;
4239  ilen = nodePtr->edgeInfoVector[iNN].ilen;
4240  elen = nodePtr->edgeInfoVector[iNN].elen;
4241 
4242  double dJdV = 0.0;
4243  if (i<inodeB)
4244  {
4245  dJdV = dJpdV1Vec[iedge];
4246  }
4247  else
4248  {
4249  dJdV = dJpdV2Vec[iedge];
4250  }
4251 
4252  coef += -((i<inodeB)?1.0:-1.0) * dJdV * ilen;
4253  }
4254  coef /= nodeArea;
4255 
4256  if (Vcolarray[i][0] != -1)
4257  {
4258  vals[count] = coef;
4259  cols[count] = Vcolarray[i][0];
4260  ++count;
4261  }
4262 
4263  // neighbor points, with respect to V:
4264  coef = 0.0;
4265  for (iNN=0;iNN<nodePtr->cnode;++iNN)
4266  {
4267  int tmpCol = Vcolarray[i][iNN+1];
4268  if (tmpCol == -1) continue;
4269 
4270  iedge = nodePtr->edgeInfoVector[iNN].iedge;
4271  inodeB = nodePtr->edgeInfoVector[iNN].inode;
4272  ilen = nodePtr->edgeInfoVector[iNN].ilen;
4273  elen = nodePtr->edgeInfoVector[iNN].elen;
4274 
4275  double dJdV = 0.0;
4276 
4277  if (i>inodeB)
4278  {
4279  dJdV = dJpdV1Vec[iedge];
4280  }
4281  else
4282  {
4283  dJdV = dJpdV2Vec[iedge];
4284  }
4285 
4286  coef =-((i<inodeB)?1.0:-1.0) * dJdV * ilen/nodeArea;
4287 #ifdef Xyce_NEW_BC
4288  coef *= ((boundarySten[inodeB]==1)?(scalingVars.rV0):1.0);
4289 
4290  // check if node == any previous nodes in the cols array
4291  if (boundarySten[inodeB]==1)
4292  {
4293  bmatch = false;
4294  for (cnt2=0;cnt2<count;++cnt2)
4295  {
4296  if (cols[cnt2] == tmpCol)
4297  { bmatch = true; break; }
4298  }
4299  if (!bmatch) { cnt2 = count; ++count;}
4300  }
4301  else
4302  {
4303  cnt2 = count;
4304  ++count;
4305  }
4306 #else
4307  cnt2 = count;
4308  ++count;
4309 #endif // Xyce_NEW_BC
4310  vals[cnt2] += coef;
4311  cols[cnt2] = tmpCol;
4312  }
4313 
4314  // center point, with respect to nn:
4315  if (Ncolarray[i][0] != -1)
4316  {
4317  vals[count] = -dRdnVec[i];
4318  cols[count] = Ncolarray[i][0];
4319  ++count;
4320  }
4321 
4322 #ifdef Xyce_DEBUG_DEVICE
4323  if (getDeviceOptions().debugLevel > 2 && getSolverState().debugTimeFlag)
4324  {
4325  Xyce::dout() << "\n";
4326  Xyce::dout().setf(std::ios::left);
4327  Xyce::dout() << " HOLE LOAD: row = " << Prow;
4328  Xyce::dout() << " count = " << count<< "\n";
4329  Xyce::dout().setf(std::ios::scientific);
4330  for (j=0;j<count;++j)
4331  {
4332  Xyce::dout() << " cols["<<j<<"] = " << cols[j];
4333  Xyce::dout() << " vals["<<j<<"] = " << vals[j];
4334  Xyce::dout() << std::endl;
4335  }
4336  }
4337 #endif
4338 
4339  if (Prow != -1 && count > 0)
4340  {
4341  bs1 = matPtr->sumIntoRow (Prow,count,&vals[0],&cols[0]);
4342  bsuccess = bsuccess && bs1;
4343  }
4344  else
4345  {
4346  Xyce::dout() << "OOOPS 3!" << std::endl;
4347  exit(0);
4348  }
4349 
4350  doneFlagP = true;
4351 
4352  } // doneFlagP if statement.
4353 
4354 #ifdef Xyce_DEBUG_DEVICE
4355  if (getDeviceOptions().debugLevel > 2 && getSolverState().debugTimeFlag)
4356  {
4357  Xyce::dout() << subsection_divider << "\n";
4358  }
4359 #endif
4360  } // mesh loop.
4361  }
4362  else // direct matrix access:
4363  {
4364  for (i=0;i<numMeshPoints;++i)
4365  {
4366  int ioffset;
4367 
4368  Vrow = li_Vrowarray[i];
4369  Nrow = li_Nrowarray[i];
4370  Prow = li_Prowarray[i];
4371 
4372  std::vector<int> & Voff = li_VoffsetArray[i];
4373  std::vector<int> & Noff = li_NoffsetArray[i];
4374  std::vector<int> & Poff = li_PoffsetArray[i];
4375 
4376  bool doneFlagV = false;
4377  bool doneFlagN = false;
4378  bool doneFlagP = false;
4379  bool doneFlag = false;
4380 
4381 #ifdef Xyce_DEBUG_DEVICE
4382  if (getDeviceOptions().debugLevel > 2 && getSolverState().debugTimeFlag)
4383  {
4384  Xyce::dout() << "mesh point i = " << i << std::endl;
4385  }
4386 #endif
4387 
4388 #ifdef Xyce_NEW_BC
4389  if (boundarySten[i]) continue;
4390 #else
4391  // Is this node a node with an explicit boundary condition?
4392  // If so, apply the BC. Otherwise, just load the region operators.
4393 
4394  if (boundarySten[i]) //do BC load
4395  {
4396 #ifdef Xyce_DEBUG_DEVICE
4397  if (getDeviceOptions().debugLevel > 2 && getSolverState().debugTimeFlag)
4398  {
4399  Xyce::dout() << " Doing BC load" << std::endl;
4400  }
4401 #endif
4402  int DIindex = labelDIMap[labelNameVector[i]];
4403 
4404  if (dIVec[DIindex].given)
4405  {
4406  if (dIVec[DIindex].neumannBCFlagV==false)
4407  {
4408  // This assumes the first offset is the (Vrow, Vrow) entry,
4409  // and the second is the (Vrow, gid) entry. CHECK!
4410  // This is probably an incorrect assumption...
4411  (*matPtr)[Vrow][Voff[0]] = 1.0;
4412  (*matPtr)[Vrow][Voff[1]] = -scalingVars.rV0;
4413 
4414  doneFlagV = true;
4415  } // neumann flag if statement.
4416 
4417  if (dIVec[DIindex].neumannBCFlagN==false)
4418  {
4419  // This assumes the first offset is the (Nrow, Nrow) entry.
4420  // CHECK!
4421  (*matPtr)[Nrow][Noff[0]] = 1.0;
4422 
4423  doneFlagN = true;
4424  } // neumann flag if statement.
4425 
4426  if (dIVec[DIindex].neumannBCFlagP==false)
4427  {
4428  // This assumes the first offset is the (Prow, Prow) entry.
4429  // CHECK!
4430  (*matPtr)[Prow][Poff[0]] = 1.0;
4431 
4432  doneFlagP = true;
4433  } // neumann flag if statement.
4434 
4435  doneFlag = (doneFlagV && doneFlagN && doneFlagP);
4436  } // inner if
4437  } // outer if
4438 
4439  if (doneFlag) continue;
4440 
4441 #endif // Xyce_NEW_BC
4442 
4443  // if load is not done yet, then do an interior point load:
4444 
4445  // Poisson's equation:
4446  mNode * nodePtr = meshContainerPtr->getNode(i);
4447  nodeArea = nodePtr->area;
4448 
4449  // center point, with respect to V:
4450  coef = 0.0;
4451  iNN=0;
4452 
4453 #ifdef Xyce_DEBUG_DEVICE
4454  if (getDeviceOptions().debugLevel > 2 && getSolverState().debugTimeFlag)
4455  {
4456  Xyce::dout() << " Doing Poisson load" << std::endl;
4457  }
4458 #endif
4459  std::string semi(bulkMaterial);
4460  if (!doneFlagV)
4461  {
4462  for (iNN=0;iNN<nodePtr->cnode;++iNN)
4463  {
4464  int inodeB = nodePtr->edgeInfoVector[iNN].inode;
4465  ilen = nodePtr->edgeInfoVector[iNN].ilen;
4466  elen = nodePtr->edgeInfoVector[iNN].elen;
4467 
4468  coef += -ilen/elen;
4469  }
4470  coef *= -scalingVars.L0 * matSupport.getRelPerm(semi)/nodeArea;
4471 
4472  (*matPtr)[Vrow][Voff[0]] += coef;
4473 
4474  // neighbor points, with respect to V:
4475  coef = 0.0; iNN=0; ioffset=1;
4476  for (iNN=0;iNN<nodePtr->cnode;++iNN)
4477  {
4478  if (Vcolarray[i][iNN+1] == -1) continue;
4479 
4480  int inodeB = nodePtr->edgeInfoVector[iNN].inode;
4481  ilen = nodePtr->edgeInfoVector[iNN].ilen;
4482  elen = nodePtr->edgeInfoVector[iNN].elen;
4483 
4484  coef = ilen/elen;
4485  coef *= -scalingVars.L0 * matSupport.getRelPerm(semi)/nodeArea;
4486 #ifdef Xyce_NEW_BC // again CHECK the offset value.
4487  coef *= ((boundarySten[inodeB]==1)?(scalingVars.rV0):1.0);
4488 #endif // Xyce_NEW_BC
4489  (*matPtr)[Vrow][Voff[ioffset]] += coef;
4490  ++ioffset;
4491  }
4492 
4493  // center point, with repsect to electron density:
4494  (*matPtr)[Vrow][Voff[ioffset]] += 1.0;
4495  ++ioffset;
4496 
4497  // center point, with respect to hole density:
4498  (*matPtr)[Vrow][Voff[ioffset]] += -1.0;
4499  ++ioffset;
4500 
4501  doneFlagV = true;
4502  } // doneFlagV if statement.
4503 
4504  // electron continuity row:
4505 #ifdef Xyce_DEBUG_DEVICE
4506  if (getDeviceOptions().debugLevel > 2 && getSolverState().debugTimeFlag)
4507  {
4508  Xyce::dout() << " Doing electron load" << std::endl;
4509  }
4510 #endif
4511 
4512  if (!doneFlagN)
4513  {
4514  coef = 0.0; ioffset=0;
4515  // center point first: (with respect to nn)
4516  for (iNN=0;iNN<nodePtr->cnode;++iNN)
4517  {
4518  iedge = nodePtr->edgeInfoVector[iNN].iedge;
4519  inodeB = nodePtr->edgeInfoVector[iNN].inode;
4520  ilen = nodePtr->edgeInfoVector[iNN].ilen;
4521  elen = nodePtr->edgeInfoVector[iNN].elen;
4522 
4523  double dJdn = 0.0;
4524  if (i<inodeB)
4525  {
4526  dJdn = dJndn1Vec[iedge];
4527  }
4528  else
4529  {
4530  dJdn = dJndn2Vec[iedge];
4531  }
4532 
4533  coef += ((i<inodeB)?1.0:-1.0) * dJdn * ilen;
4534  }
4535 
4536  coef /= nodeArea;
4537  coef += - dRdnVec[i] - dDNDTdn;
4538  coef += pdElecPenalty[i];
4539 
4540  (*matPtr)[Nrow][Noff[0]] += coef;
4541 
4542  // neighbor points, with respect to nn:
4543  coef = 0.0; ioffset=1;
4544  for (iNN=0;iNN<nodePtr->cnode;++iNN)
4545  {
4546  if (Ncolarray[i][iNN+1] == -1) continue;
4547 
4548  iedge = nodePtr->edgeInfoVector[iNN].iedge;
4549  inodeB = nodePtr->edgeInfoVector[iNN].inode;
4550  ilen = nodePtr->edgeInfoVector[iNN].ilen;
4551  elen = nodePtr->edgeInfoVector[iNN].elen;
4552 
4553  double dJdn = 0.0;
4554  if (i>inodeB)
4555  {
4556  dJdn = dJndn1Vec[iedge];
4557  }
4558  else
4559  {
4560  dJdn = dJndn2Vec[iedge];
4561  }
4562 
4563  coef = ((i<inodeB)?1.0:-1.0) * dJdn * ilen/nodeArea;
4564 
4565  (*matPtr)[Nrow][Noff[ioffset]] += coef;
4566  ++ioffset;
4567  }
4568 
4569  // center point, with respect to V:
4570  coef = 0.0;
4571  for (iNN=0;iNN<nodePtr->cnode;++iNN)
4572  {
4573  iedge = nodePtr->edgeInfoVector[iNN].iedge;
4574  inodeB = nodePtr->edgeInfoVector[iNN].inode;
4575  ilen = nodePtr->edgeInfoVector[iNN].ilen;
4576  elen = nodePtr->edgeInfoVector[iNN].elen;
4577 
4578  double dJdV = 0.0;
4579  if (i<inodeB)
4580  {
4581  dJdV = dJndV1Vec[iedge];
4582  }
4583  else
4584  {
4585  dJdV = dJndV2Vec[iedge];
4586  }
4587 
4588  coef += ((i<inodeB)?1.0:-1.0) * dJdV * ilen;
4589  }
4590  coef /= nodeArea;
4591 
4592  (*matPtr)[Nrow][Noff[ioffset]] += coef;
4593  ++ioffset;
4594 
4595  // neighbor points, with respect to V:
4596  coef = 0.0;
4597  for (iNN=0;iNN<nodePtr->cnode;++iNN)
4598  {
4599  if (Vcolarray[i][iNN+1] == -1) continue;
4600 
4601  iedge = nodePtr->edgeInfoVector[iNN].iedge;
4602  inodeB = nodePtr->edgeInfoVector[iNN].inode;
4603  ilen = nodePtr->edgeInfoVector[iNN].ilen;
4604  elen = nodePtr->edgeInfoVector[iNN].elen;
4605 
4606  double dJdV = 0.0;
4607 
4608  if (i>inodeB)
4609  {
4610  dJdV = dJndV1Vec[iedge];
4611  }
4612  else
4613  {
4614  dJdV = dJndV2Vec[iedge];
4615  }
4616 
4617  coef = ((i<inodeB)?1.0:-1.0) * dJdV * ilen/nodeArea;
4618 #ifdef Xyce_NEW_BC
4619  coef *= ((boundarySten[inodeB]==1)?(scalingVars.rV0):1.0);
4620 #endif // Xyce_NEW_BC
4621  (*matPtr)[Nrow][Noff[ioffset]] += coef;
4622  ++ioffset;
4623  }
4624 
4625  // center point, with respect to np:
4626  (*matPtr)[Nrow][Noff[ioffset]] += -dRdpVec[i];
4627  ++ioffset;
4628 
4629  doneFlagN = true;
4630 
4631  } // doneFlagN if statement.
4632 
4633  // hole continuity row:
4634 #ifdef Xyce_DEBUG_DEVICE
4635  if (getDeviceOptions().debugLevel > 2 && getSolverState().debugTimeFlag)
4636  {
4637  Xyce::dout() << " Doing hole load" << std::endl;
4638  }
4639 #endif
4640  if (!doneFlagP)
4641  {
4642  coef = 0.0; ioffset=0;
4643  // center point first: (with respect to np)
4644  for (iNN=0;iNN<nodePtr->cnode;++iNN)
4645  {
4646  iedge = nodePtr->edgeInfoVector[iNN].iedge;
4647  inodeB = nodePtr->edgeInfoVector[iNN].inode;
4648  ilen = nodePtr->edgeInfoVector[iNN].ilen;
4649  elen = nodePtr->edgeInfoVector[iNN].elen;
4650 
4651  double dJdp = 0.0;
4652  if (i<inodeB)
4653  {
4654  dJdp = dJpdn1Vec[iedge];
4655  }
4656  else
4657  {
4658  dJdp = dJpdn2Vec[iedge];
4659  }
4660 
4661  coef += - ((i<inodeB)?1.0:-1.0) * dJdp * ilen;
4662  }
4663 
4664  coef /= nodeArea;
4665  coef += - dRdpVec[i] - dDPDTdp;
4666  coef += pdHolePenalty[i];
4667 
4668  (*matPtr)[Prow][Poff[0]] += coef;
4669 
4670  // neighbor points, with respect to np:
4671  coef = 0.0; ioffset=1;
4672  for (iNN=0;iNN<nodePtr->cnode;++iNN)
4673  {
4674  if (Pcolarray[i][iNN+1] == -1) continue;
4675 
4676  iedge = nodePtr->edgeInfoVector[iNN].iedge;
4677  inodeB = nodePtr->edgeInfoVector[iNN].inode;
4678  ilen = nodePtr->edgeInfoVector[iNN].ilen;
4679  elen = nodePtr->edgeInfoVector[iNN].elen;
4680 
4681  double dJdp = 0.0;
4682  if (i>inodeB)
4683  {
4684  dJdp = dJpdn1Vec[iedge];
4685  }
4686  else
4687  {
4688  dJdp = dJpdn2Vec[iedge];
4689  }
4690 
4691  coef =-((i<inodeB)?1.0:-1.0) * dJdp * ilen/nodeArea;
4692 
4693  (*matPtr)[Prow][Poff[ioffset]] += coef;
4694  ++ioffset;
4695  }
4696 
4697  // center point, with respect to V:
4698  coef = 0.0;
4699  for (iNN=0;iNN<nodePtr->cnode;++iNN)
4700  {
4701  iedge = nodePtr->edgeInfoVector[iNN].iedge;
4702  inodeB = nodePtr->edgeInfoVector[iNN].inode;
4703  ilen = nodePtr->edgeInfoVector[iNN].ilen;
4704  elen = nodePtr->edgeInfoVector[iNN].elen;
4705 
4706  double dJdV = 0.0;
4707  if (i<inodeB)
4708  {
4709  dJdV = dJpdV1Vec[iedge];
4710  }
4711  else
4712  {
4713  dJdV = dJpdV2Vec[iedge];
4714  }
4715 
4716  coef += -((i<inodeB)?1.0:-1.0) * dJdV * ilen;
4717  }
4718  coef /= nodeArea;
4719 
4720  (*matPtr)[Prow][Poff[ioffset]] += coef;
4721  ++ioffset;
4722 
4723  // neighbor points, with respect to V:
4724  coef = 0.0;
4725  for (iNN=0;iNN<nodePtr->cnode;++iNN)
4726  {
4727  if (Vcolarray[i][iNN+1] == -1) continue;
4728 
4729  iedge = nodePtr->edgeInfoVector[iNN].iedge;
4730  inodeB = nodePtr->edgeInfoVector[iNN].inode;
4731  ilen = nodePtr->edgeInfoVector[iNN].ilen;
4732  elen = nodePtr->edgeInfoVector[iNN].elen;
4733 
4734  double dJdV = 0.0;
4735 
4736  if (i>inodeB)
4737  {
4738  dJdV = dJpdV1Vec[iedge];
4739  }
4740  else
4741  {
4742  dJdV = dJpdV2Vec[iedge];
4743  }
4744 
4745  coef =-((i<inodeB)?1.0:-1.0) * dJdV * ilen/nodeArea;
4746 #ifdef Xyce_NEW_BC // CHECK!
4747  coef *= ((boundarySten[inodeB]==1)?(scalingVars.rV0):1.0);
4748 #endif // Xyce_NEW_BC
4749  (*matPtr)[Prow][Poff[ioffset]] += coef;
4750  ++ioffset;
4751  }
4752 
4753  // center point, with respect to nn:
4754  (*matPtr)[Prow][Poff[ioffset]] += -dRdnVec[i];
4755  ++ioffset;
4756 
4757  doneFlagP = true;
4758 
4759  } // doneFlagP if statement.
4760  } // mesh loop.
4761  }
4762 
4763 #ifdef Xyce_DEBUG_DEVICE
4764  if (getDeviceOptions().debugLevel > 1 && getSolverState().debugTimeFlag)
4765  {
4766  Xyce::dout() << section_divider << "\n";
4767  }
4768 #endif
4769 
4770  return bsuccess;
4771 }
4772 
4773 //-----------------------------------------------------------------------------
4774 // Function : Instance::calcLifetimes
4775 // Purpose : This function calculates the electron and hole lifetimes
4776 // and places them into the tn and tp arrays.
4777 // Special Notes : This function assumes scaling off.
4778 // Scope : public
4779 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
4780 // Creation Date : 11/14/01
4781 //-----------------------------------------------------------------------------
4783 {
4784  bool bsuccess = true;
4785  int i;
4786 
4787 #ifdef Xyce_DEBUG_DEVICE
4788  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
4789  {
4790  Xyce::dout() << section_divider << "\n";
4791  Xyce::dout() << "Instance::calcLifetimes" << "\n";
4792  }
4793 #endif
4794 
4795  for (i=0;i<numMeshPoints;++i)
4796  {
4797  tnVec[i] = matSupport.calcLt (false, fabs(CVec[i]));
4798  tpVec[i] = matSupport.calcLt (true , fabs(CVec[i]));
4799  }
4800 
4801 #ifdef Xyce_DEBUG_DEVICE
4802  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
4803  {
4804  for (i=0;i<numMeshPoints;++i)
4805  {
4806  Xyce::dout() << " tnVec["<<i<<"] = " <<tnVec[i];
4807  Xyce::dout() << " tpVec["<<i<<"] = " <<tpVec[i];
4808  Xyce::dout() << "\n";
4809  }
4810  }
4811 
4812  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
4813  {
4814  Xyce::dout() << section_divider << "\n";
4815  }
4816 #endif
4817 
4818  return bsuccess;
4819 }
4820 
4821 //-----------------------------------------------------------------------------
4822 // Function : Instance::calcMobilities
4823 // Purpose : This function calculates the electron and hole mobilities
4824 // and places them into the un and up arrays.
4825 // Special Notes : This function assumes scaling off.
4826 //
4827 // ERK: 10/25/2012: The mobility functions have been updated to use
4828 // Sacado, and handle field-dependent mobilities. However, that usage
4829 // has not been extended to the 2D devices yet.
4830 //
4831 // Scope : public
4832 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
4833 // Creation Date : 11/14/01
4834 //-----------------------------------------------------------------------------
4836 {
4837  bool bsuccess = true;
4838  int i;
4839 
4840 #ifdef Xyce_DEBUG_DEVICE
4841  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
4842  {
4843  Xyce::dout() << section_divider << "\n";
4844  Xyce::dout() << "Instance::calcMobilities" << "\n";
4845  }
4846 #endif
4847 
4848  MobInfo<double> ci;
4851  for (i=0;i<numMeshPoints;++i)
4852  {
4853  // ci.materialName = labelNameVector[i];
4854  ci.T = Temp; // scaling not worked out for temperature...
4855 
4856  ci.N = fabs(CVec[i]);
4857  ci.N *= ((variablesScaled)?scalingVars.C0:1.0);
4858 
4859  if (ci.N == 0.0) ci.N = 1.0; // avoid nan's.
4860 
4861  ci.p = npVec[i] *((variablesScaled)?scalingVars.C0:1.0);
4862  ci.n = nnVec[i] *((variablesScaled)?scalingVars.C0:1.0);
4863 
4864  // electron mobility:
4865  ci.holeFlag = false;
4866  unVec[i] = matSupport.calcMob(ci);
4867  unVec[i] /= ((variablesScaled)?scalingVars.u0:1.0);
4868 
4869  // hole mobility:
4870  ci.holeFlag = true;
4871  upVec[i] = matSupport.calcMob(ci);
4872  upVec[i] /= ((variablesScaled)?scalingVars.u0:1.0);
4873  }
4874 
4875 #ifdef Xyce_DEBUG_DEVICE
4877  {
4878  Xyce::dout() << "Nodal Mobilities:" << std::endl;
4879  for (i=0;i<numMeshPoints;++i)
4880  {
4881  Xyce::dout() << " unVec["<<i<<"] = " << unVec[i];
4882  Xyce::dout() << " upVec["<<i<<"] = " << upVec[i];
4883  Xyce::dout() << "\n";
4884  }
4885  Xyce::dout() << std::endl;
4886  }
4887 #endif
4888 
4889  for (i=0;i< meshContainerPtr->getNumEdges ();++i)
4890  {
4891  mEdge * edgePtr = meshContainerPtr->getEdge(i);
4892 
4893  int inodeA = edgePtr->inodeA;
4894  int inodeB = edgePtr->inodeB;
4895 
4896  // option 1
4897  if (ci.mobModelName != "carr")
4898  {
4899  unE_Vec[i] = (unVec[inodeA]+unVec[inodeB])/2.0;
4900  upE_Vec[i] = (upVec[inodeA]+upVec[inodeB])/2.0;
4901  }
4902  // option 2
4903  else if(ci.mobModelName == "carr")
4904  {
4905  ci.N = (fabs(CVec[inodeA])+fabs(CVec[inodeB]))*0.5;
4906  ci.N *= ((variablesScaled)?scalingVars.C0:1.0);
4907 
4908  if (ci.N == 0.0) ci.N = 1.0;
4909 
4910  // for the carrier densities, do a "product average".
4911  ci.n = pow((fabs(nnVec[inodeA])*fabs(nnVec[inodeB])),0.5)*
4912  ((variablesScaled)?scalingVars.C0:1.0);
4913 
4914  ci.p = pow((fabs(npVec[inodeA])*fabs(npVec[inodeB])),0.5)*
4915  ((variablesScaled)?scalingVars.C0:1.0);
4916 
4917 #ifdef Xyce_DEBUG_DEVICE
4918  if (ci.n != 0.0 && !(ci.n > 0.0) && !(ci.n < 0.0))
4919  {
4920  Xyce::dout() << "ci.n is nan" << std::endl;
4921  Xyce::dout() << "nnVec[A] = " << nnVec[inodeA];
4922  Xyce::dout() << "nnVec[B] = " << nnVec[inodeB];
4923  Xyce::dout() << std::endl;
4924  exit(0);
4925  }
4926 #endif
4927  //electron mobility
4928  ci.holeFlag = false;
4929  unE_Vec[i] = matSupport.calcMob(ci);
4930  unE_Vec[i] /= ((variablesScaled)?scalingVars.u0:1.0);
4931 
4932  // hole mobility
4933  ci.holeFlag = true;
4934  upE_Vec[i] = matSupport.calcMob(ci);
4935  upE_Vec[i] /= ((variablesScaled)?scalingVars.u0:1.0);
4936  }
4937  }
4938 
4939 #ifdef Xyce_DEBUG_DEVICE
4941  {
4942  Xyce::dout() << "Edge Mobilities" << std::endl;
4943  for (i=0;i<meshContainerPtr->getNumEdges();++i)
4944  {
4945  Xyce::dout() << " unE_Vec["<<i<<"] = " << unE_Vec[i];
4946  Xyce::dout() << " upE_Vec["<<i<<"] = " << upE_Vec[i];
4947  Xyce::dout() << "\n";
4948  }
4949  }
4950 
4951  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
4952  {
4953  Xyce::dout() << section_divider << std::endl;
4954  }
4955 #endif
4956 
4957  return bsuccess;
4958 }
4959 
4960 //-----------------------------------------------------------------------------
4961 // Function : Instance::updateTemperature
4962 //
4963 // Purpose : Re-sets neccessary things for a change in device
4964 // temperature.
4965 //
4966 // Special Notes : This won't quite work yet, because Ni and the bandgap
4967 // functions (up in the material support class) are
4968 // not yet really temperature dependent.
4969 //
4970 // Things that change with temperature:
4971 //
4972 // thermal voltage (Vt)
4973 // scaling variable, scalingVars.V0 = Vt
4974 // intrinsic concentration, Ni.
4975 // bandgap, Eg.
4976 // mobilities (updated in real time, so no need to change here).
4977 // density boundary conditions (depend on Ni)
4978 // Vequ (may depend on Ni and Eg).
4979 // other ???
4980 //
4981 // Scope : public
4982 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
4983 // Creation Date : 11/14/01
4984 //-----------------------------------------------------------------------------
4985 bool Instance::updateTemperature(const double & temp_tmp)
4986 {
4987  bool bsuccess = true;
4988  bool bs1 = true;
4989 
4990  Temp = temp_tmp;
4991 
4992  // first un-scale everything, if neccessary:
4993  if (variablesScaled)
4994  {
4995  bs1 = unScaleVariables (); bsuccess = bsuccess && bs1;
4996  }
4997 
4998  bs1 = setupMiscConstants (); bsuccess = bsuccess && bs1;
4999  bs1 = setupScalingVars (); bsuccess = bsuccess && bs1;
5000 
5001  bs1 = calcDensityBCs (); bsuccess = bsuccess && bs1;
5002  bs1 = calcVequBCs (); bsuccess = bsuccess && bs1;
5003  bs1 = calcMobilities (); bsuccess = bsuccess && bs1;
5004 
5005  if (!variablesScaled)
5006  {
5007  bs1 = scaleVariables ();
5008  }
5009 
5010  return bsuccess;
5011 }
5012 
5013 //-----------------------------------------------------------------------------
5014 // Function : Instance::calcVoltDepDensities
5015 // Purpose : This function calculates electron and hole densities,
5016 // based on the electrostatic potential. It is only to be
5017 // called during the initialization phase.
5018 // Special Notes :
5019 // Scope : public
5020 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
5021 // Creation Date : 11/14/01
5022 //-----------------------------------------------------------------------------
5024 {
5025  bool bsuccess = true;
5026 
5027  int i;
5028  Ut = Vt/scalingVars.V0;
5029 
5030 #ifdef Xyce_DEBUG_DEVICE
5031  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
5032  {
5033  Xyce::dout() << section_divider << "\n";
5034  Xyce::dout() << "Instance::calcVoltDepDensities\n";
5035  }
5036 #endif
5037 
5038  for (i=0;i<numMeshPoints;++i)
5039  {
5042  }
5043 
5044 #ifdef Xyce_DEBUG_DEVICE
5045  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
5046  {
5047  Xyce::dout() << section_divider << "\n";
5048  }
5049 #endif
5050 
5051  return bsuccess;
5052 }
5053 
5054 //-----------------------------------------------------------------------------
5055 // Function : Instance::calcDopingProfile
5056 // Purpose : This function sets up the initial doping profile. It should
5057 // probably only be called once.
5058 // Special Notes :
5059 // Scope : public
5060 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
5061 // Creation Date : 11/14/01
5062 //-----------------------------------------------------------------------------
5064 {
5065  bool bsuccess = true;
5066  int i;
5067 
5068  // set up the initial doping array:
5069 
5070 #ifdef Xyce_DEBUG_DEVICE
5071  if (getDeviceOptions().debugLevel > 0)
5072  {
5073  Xyce::dout() << section_divider << "\n";
5074  Xyce::dout() << "Instance::calcDopingProfile\n";
5075  }
5076 #endif
5077 
5078  // zero out CVec, first:
5079  CVec.resize (numMeshPoints,0.0);
5080  CdonorVec.resize (numMeshPoints,0.0);
5081  CacceptorVec.resize (numMeshPoints,0.0);
5082  //for (int itmp=0;itmp<numMeshPoints;++itmp) CVec[itmp] = 0.0;
5083 
5084 #ifdef Xyce_OXIDE_ENABLED
5085  if (allOxideFlag) return bsuccess;
5086 #endif
5087 
5088  // The doping profile may be contained in the mesh file.
5089  // or it may need to be specified here. First, check the
5090  // mesh container to see if it has this info.
5091 
5092  // use the one owned by the mesh object.
5094  {
5096 
5097  // Obtain Na and Nd:
5098  // Nd = number of donors (maximum + doping)
5099  // Na = number of acceptors (minimum + doping)
5100  double NaTmp = 1.0e+99;
5101  double NdTmp =-1.0e+99;
5102 
5103  for (i=0;i<numMeshPoints;++i)
5104  {
5105  if(NaTmp > CVec[i]) NaTmp = CVec[i];
5106  if(NdTmp < CVec[i]) NdTmp = CVec[i];
5107  }
5108 
5109  Na = fabs(NaTmp);
5110  Nd = fabs(NdTmp);
5111  }
5112  else // allocate, set it up here.
5113  {
5114  // Check which style of doping specification to use. If the dopeInfoMap
5115  // is empty, then assume the old method. If not, use the dopeInfoMap.
5116  if (dopeInfoMap.empty ())
5117  {
5118  // Two electrodes - do a diode sim. This is the same code as
5119  // in the DiodePDE device, mostly, except that the junction is
5120  // perpendicular to the y-direction, rather than the x-direction.
5121  if (numElectrodes == 2)
5122  {
5123  double midpoint;
5124  midpoint = deviceWidth/2.0;
5125  XL = midpoint - WJ/2.0;
5126  XR = midpoint + WJ/2.0;
5127 
5128  // first setup, check the graded junction parameters:
5129  if (gradedJunctionFlag)
5130  {
5131  // if junction width was not specified, set to 1/10 the diode width.
5132  if (!given("WJ")) WJ = 0.1 * deviceWidth;
5133 
5134  midpoint = deviceWidth/2.0;
5135  XL = midpoint - WJ/2.0;
5136  XR = midpoint + WJ/2.0;
5137  }
5138 
5139 #ifdef Xyce_DEBUG_DEVICE
5141  {
5142  Xyce::dout() << "deviceWidth = " << deviceWidth << std::endl;
5143  Xyce::dout() << "midpoint = " << midpoint << std::endl;
5144  Xyce::dout() << "XL = " << XL << std::endl;
5145  Xyce::dout() << "XR = " << XR << std::endl;
5146  Xyce::dout() << "WJ = " << WJ << std::endl;
5147  }
5148 #endif
5149 
5150  for (i=0;i<numMeshPoints;++i)
5151  {
5152  double yloc = yVec[i];
5153 
5154  if (gradedJunctionFlag)
5155  {
5156  if (yloc <= XL) CVec[i] = +Nd;
5157  else if (yloc>XL && yloc<XR) CVec[i] = Nd-(Na+Nd)/(XR-XL)*(yloc-XL);
5158  else CVec[i] = -Na;
5159  }
5160  else
5161  {
5162  if (yloc < deviceWidth /2.0) CVec[i] = Nd;
5163  else CVec[i] = -Na;
5164  }
5165  } // i loop
5166  }
5167  else if (numElectrodes == 3)
5168  {
5169  // Assuming a BJT.
5170  //
5171  // emitter base collector
5172  // A-----B--------C-----D-------------E-----F---------G
5173  // | | / | | | | | |
5174  // |-----|- | | / | | |
5175  // | | | | / | | |
5176  // |-----|--------|-----|- | | |
5177  // | | | | | | |
5178  // | | | | | | |
5179  // | | | | | | |
5180  // H-----I--------J-----K-------------L-----M---------N
5181  //
5182 
5183  // hardwired doping constants for now. Fix this later.
5184  // Assuming a PNP, not NPN.
5185 
5186  double Ne; // emitter doping
5187  double Nb; // base doping
5188  double Nc; // collector doping
5189  //int nx = numMeshPointsX;
5190 
5191  // Wad - total width of the emitter, base,
5192  // and the space in between.
5193  double Wad = 0.4 * deviceLength;
5194 
5195  // We - width of the emittter, by itself.
5196  double We = 0.1 * deviceLength;
5197 
5198  double Re = 0.5 * We; // emitter doping radius - about 1/2 of We.
5199  double Rb = 0.5 * Wad; // base doping radius - about 1/2 of Wad.
5200 
5201  double Ae;
5202  double Ab;
5203 
5204  if (given("TYPE") && deviceType=="NPN") // if npn
5205  {
5206  Ne = 1.00e+19;
5207  Nb = 1.00e+16;
5208  Nc = 1.00e+14;
5209  Ae = log(Ne/Nb)/(Re*Re);
5210  Ab = log(Nb/Nc)/(Rb*Rb);
5211 
5212  for (i=0;i<numMeshPoints;++i)
5213  {
5214  double x = xVec[i];
5215  double y = yVec[i];
5216  y -= deviceWidth;
5217 
5218  CVec[i] =
5219  Nc - // collector
5220  (Nb+Nc)*DopeInfo::ngdep(x,y,2.0*Wad,Ab,Ab) + // base
5221  (Ne+Nb)*DopeInfo::ngdep(x,y,2.0*We ,Ae,Ae); // emitter
5222  }
5223  }
5224  else // if pnp
5225  {
5226  Ne = 1.00e+19;
5227  Nb = 1.00e+16;
5228  Nc = 1.00e+14;
5229  Ae = log(Ne/Nb)/(Re*Re);
5230  Ab = log(Nb/Nc)/(Rb*Rb);
5231 
5232  for (i=0;i<numMeshPoints;++i)
5233  {
5234  double x = xVec[i];
5235  double y = yVec[i];
5236  y -= deviceWidth;
5237 
5238  CVec[i] =
5239  - Nc + // collector
5240  (Nb+Nc)*DopeInfo::ngdep(x,y,2.0*Wad,Ab,Ab) - // base
5241  (Ne+Nb)*DopeInfo::ngdep(x,y,2.0*We ,Ae,Ae); // emitter
5242  }
5243  }
5244 
5245  // For the purposes of the rest of the code getting scaling
5246  // correct, etc., resetting Na, Nd here. This is neccessary,
5247  // since the doping levels are hardwired for this problem.
5248 
5249  // Obtain Na and Nd:
5250  // Nd = number of donors (maximum + doping)
5251  // Na = number of acceptors (minimum + doping)
5252  double NaTmp = 1.0e+99;
5253  double NdTmp =-1.0e+99;
5254 
5255  for (i=0;i<numMeshPoints;++i)
5256  {
5257  if(NaTmp > CVec[i]) NaTmp = CVec[i];
5258  if(NdTmp < CVec[i]) NdTmp = CVec[i];
5259  }
5260 
5261  Na = fabs(NaTmp);
5262  Nd = fabs(NdTmp);
5263  }
5264  else if (numElectrodes == 4)
5265  {
5266 
5267  // Assuming a MOSFET
5268  //
5269  // Source Gate Drain
5270  //
5271  // |--5--| |------------3------------| |--5--|
5272  //
5273  // - C-----D---E-------------------------F---G-----H -
5274  // 6 | | | | |
5275  // - | I-------------------------J | |
5276  // n | | |Noflux
5277  // o | | 2
5278  // f | | |
5279  // l | | |
5280  // u | | |
5281  // x K---------------------------------------------L -
5282  //
5283  // |----------------------1----------------------|
5284  // Bulk
5285 
5286  // hardwired doping constants for now.
5287  // default is NMOS.
5288 
5289  // fix the geometrical stuff later...
5290  double WDEV = deviceLength; // device width
5291 
5292  double WOX = 0.7 * deviceLength; // oxide width
5293 
5294  double NS = 1.0e16; // substrate doping (cm^-3)
5295  double NC = 1.0e19; // contact doping (cm^-3)
5296  double WDIFF = (WDEV-WOX)/2; // diffusion width (cm)
5297  double DDIFF = (0.125)*deviceWidth; // diffusion depth (cm)
5298  double DT = 1.0e-11; // diffusion coef. * time (cm^2)
5299 
5300  for (i=0;i<numMeshPoints;++i)
5301  {
5302  double x = xVec[i];
5303  double y = yVec[i];
5304  y -= deviceWidth;
5305 
5306  CVec[i] =
5307  -NS +
5308  (NC+NS)*DopeInfo::nsdep(x, 2*WDIFF,DT)*DopeInfo::nsdep(y,2*DDIFF,DT) +
5309  (NC+NS)*DopeInfo::nsdep(WDEV-x,2*WDIFF,DT)*DopeInfo::nsdep(y,2*DDIFF,DT) ;
5310  }
5311 
5312  if (given("TYPE") && deviceType=="PMOS") // if pmos, then invert the default.
5313  {
5314  for (i=0;i<numMeshPoints;++i)
5315  {
5316  CVec[i] = -CVec[i];
5317  }
5318  }
5319 
5320  // For the purposes of the rest of the code getting scaling
5321  // correct, etc., resetting Na, Nd here. This is neccessary,
5322  // since the doping levels are hardwired for this problem.
5323 
5324  // Obtain Na and Nd:
5325  // Nd = number of donors (maximum + doping)
5326  // Na = number of acceptors (minimum + doping)
5327  double NaTmp = 1.0e+99;
5328  double NdTmp =-1.0e+99;
5329 
5330  for (i=0;i<numMeshPoints;++i)
5331  {
5332  if(NaTmp > CVec[i]) NaTmp = CVec[i];
5333  if(NdTmp < CVec[i]) NdTmp = CVec[i];
5334  }
5335 
5336  Na = fabs(NaTmp);
5337  Nd = fabs(NdTmp);
5338  }
5339  else
5340  // more than four electrodes? not ready yet...
5341  {
5342  UserFatal(*this) << "Too many electrodes specified. numElectrodes = " << numElectrodes;
5343  }
5344  }
5345  else // use the "new" doping specification:
5346  {
5347  // loop over the dope info map, and sum contributions from each
5348  // doping entity into the total doping array, CVec.
5349  std::map<std::string, DopeInfo *>::iterator iter;
5350  std::map<std::string, DopeInfo *>::iterator start = dopeInfoMap.begin();
5351  std::map<std::string, DopeInfo *>::iterator end = dopeInfoMap.end();
5352 
5353 #ifdef Xyce_DEBUG_DEVICE
5354  if (getDeviceOptions().debugLevel > 0)
5355  {
5356  Xyce::dout() << "dope info map:" << std::endl;
5357  }
5358 #endif
5359  for ( iter = start; iter != end; ++iter )
5360  {
5361  DopeInfo & di = *(iter->second);
5362 
5363 #ifdef Xyce_DEBUG_DEVICE
5364  if (getDeviceOptions().debugLevel > 0)
5365  {
5366  Xyce::dout() << di;
5367  }
5368 #endif
5370 
5371  } // dope info map loop
5372 
5373  Na = 0.0;
5374  Nd = 0.0;
5375  for (i=0;i<numMeshPoints;++i)
5376  {
5377  if (Na > CVec[i]) Na = CVec[i];
5378  if (Nd < CVec[i]) Nd = CVec[i];
5379  }
5380  Na = fabs(Na);
5381  Nd = fabs(Nd);
5382  }
5383  }
5384 
5385 #ifdef Xyce_DEBUG_DEVICE
5386  if (getDeviceOptions().debugLevel > 0)
5387  {
5388  Xyce::dout().width(20);
5389  Xyce::dout().precision(12);
5390  Xyce::dout().setf(std::ios::scientific);
5391  Xyce::dout() << std::endl;
5392  Xyce::dout() << "Na = " << Na << std::endl;
5393  Xyce::dout() << "Nd = " << Nd << std::endl<< std::endl;
5394  }
5395 
5396  if (getDeviceOptions().debugLevel > 0)
5397  {
5398  for (int inode=0;inode<numMeshPoints;++inode)
5399  {
5400  Xyce::dout() << xVec[inode];
5401  Xyce::dout() << " " << yVec[inode];
5402  Xyce::dout() << " CVec["<<inode<<"] = " << CVec[inode] << std::endl;
5403  }
5404  }
5405 #endif
5406 
5407 #ifdef Xyce_DEBUG_DEVICE
5408  if (getDeviceOptions().debugLevel > 0)
5409  {
5410  Xyce::dout() << section_divider << std::endl;
5411  }
5412 #endif
5413 
5414  return bsuccess;
5415 }
5416 
5417 //-----------------------------------------------------------------------------
5418 // Function : Instance::setupMiscConstants
5419 // Purpose :
5420 // Special Notes :
5421 // Scope : public
5422 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
5423 // Creation Date : 07/20/03
5424 //-----------------------------------------------------------------------------
5426 {
5427  if (useOldNi)
5428  {
5429  Ni = matSupport.getNi_old (bulkMaterial, Temp); // this is not accurate
5430  }
5431  else
5432  {
5434  }
5435  Vt = kb*Temp/charge;
5436  return true;
5437 }
5438 
5439 //-----------------------------------------------------------------------------
5440 // Function : Instance::setupScalingVars
5441 // Purpose :
5442 // Special Notes :
5443 // Scope : public
5444 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
5445 // Creation Date : 11/14/01
5446 //-----------------------------------------------------------------------------
5448 {
5449  bool bsuccess = true;
5450 
5451  double maxSize = meshContainerPtr->getMaxSize();
5452 
5453  if (given("X0"))
5455  else
5456  scalingVars.x0 = maxSize; // distance scaling (cm)
5457 
5458  // if the geometry is cylindrical, then scale electrode areas by area.
5459  // if cart., then scale by length.
5462 
5463  scalingVars.T0 = Temp; // temperature scaling (K) (not really used)
5464 
5465  // electrostatic potential scaling (V)
5466  scalingVars.V0 = Vt;
5467  scalingVars.rV0 = 1.0/scalingVars.V0;
5468 
5469  // concentration scaling (cm^-3);
5470  if (Na >= Nd) scalingVars.C0 = Na;
5471  else scalingVars.C0 = Nd;
5472 
5473  scalingVars.D0 = 35.0; // diffusion coefficient scaling (cm^2/s)
5474 
5475  scalingVars.u0 = scalingVars.D0/scalingVars.V0; // mobility coefficient scaling (cm^2/V/s)
5476 
5477  scalingVars.R0 = scalingVars.D0*scalingVars.C0/(scalingVars.x0*scalingVars.x0); // recombination rate scaling (cm^-3/s)
5478  scalingVars.rR0 = 1.0/scalingVars.R0;
5479 
5480  scalingVars.t0 = (scalingVars.x0*scalingVars.x0)/scalingVars.D0; // time scaling (s)
5481 
5482  scalingVars.E0 = scalingVars.V0/scalingVars.x0; // electric field scaling (V/cm)
5483 
5484  scalingVars.F0 = scalingVars.D0*scalingVars.C0/scalingVars.x0; // particle flux scaling (cm^-2/s)
5485 
5486  scalingVars.J0 = charge*scalingVars.D0*scalingVars.C0/scalingVars.x0; // current density scaling (A/cm^2)
5487 
5488  scalingVars.L0 = scalingVars.V0*e0/(charge*scalingVars.x0*scalingVars.x0*scalingVars.C0); // Laplacian scaling constant
5489 
5490 #ifdef Xyce_DEBUG_DEVICE
5491  if (getDeviceOptions().debugLevel > 0)
5492  {
5493  Xyce::dout() << std::endl;
5494  Xyce::dout() << " scalingVars.x0 = " << scalingVars.x0 << "\n";
5495  Xyce::dout() << " scalingVars.a0 = " << scalingVars.a0 << "\n";
5496  Xyce::dout() << " scalingVars.T0 = " << scalingVars.T0 << "\n";
5497  Xyce::dout() << " scalingVars.V0 = " << scalingVars.V0 << "\n";
5498  Xyce::dout() << " scalingVars.C0 = " << scalingVars.C0 << "\n";
5499  Xyce::dout() << " scalingVars.D0 = " << scalingVars.D0 << "\n";
5500  Xyce::dout() << " scalingVars.u0 = " << scalingVars.u0 << "\n";
5501  Xyce::dout() << " scalingVars.R0 = " << scalingVars.R0 << "\n";
5502  Xyce::dout() << " scalingVars.t0 = " << scalingVars.t0 << "\n";
5503  Xyce::dout() << " scalingVars.E0 = " << scalingVars.E0 << "\n";
5504  Xyce::dout() << " scalingVars.J0 = " << scalingVars.J0 << "\n";
5505  Xyce::dout() << " scalingVars.L0 = " << scalingVars.L0 << std::endl;
5506  Xyce::dout() << std::endl;
5507  }
5508 #endif
5509 
5510  return bsuccess;
5511 }
5512 
5513 //-----------------------------------------------------------------------------
5514 // Function : Instance::scaleVariables
5515 //
5516 // Purpose : This function performs scaling on all the relevant variables.
5517 //
5518 // Special Notes : It should only be called at the end of the initial setup.
5519 // Calculations done during the course of the calculation are
5520 // performed with the assumption of scaling.
5521 // Scope : public
5522 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
5523 // Creation Date : 11/14/01
5524 //-----------------------------------------------------------------------------
5526 {
5527  bool bsuccess = true;
5528  int i;
5529  N_LAS_Vector * solVectorPtr = extData.nextSolVectorPtr;
5530 
5531  Na /= scalingVars.C0;
5532  Nd /= scalingVars.C0;
5533  Ni /= scalingVars.C0;
5534  NnMax /= scalingVars.C0;
5535  NpMax /= scalingVars.C0;
5536 
5538 
5541 
5542  Vbi /= scalingVars.V0;
5543 
5544  // scale area, elen and ilen!
5546 
5547  // scale boundary conditions:
5548  std::vector<DeviceInterfaceNode>::iterator firstDI = dIVec.begin();
5549  std::vector<DeviceInterfaceNode>::iterator lastDI = dIVec.end ();
5550  std::vector<DeviceInterfaceNode>::iterator iterDI = firstDI;
5551 
5552  for (;iterDI!=lastDI;++iterDI)
5553  {
5554  for (i=0;i<iterDI->numBoundaryPoints;++i)
5555  {
5556  iterDI->nnbcVec[i] /= scalingVars.C0;
5557  iterDI->npbcVec[i] /= scalingVars.C0;
5558  iterDI->VbcVec [i] /= scalingVars.V0;
5559  iterDI->VequVec[i] /= scalingVars.V0;
5560  }
5561 
5562  // If running with cylindrical coordinates, scale by area (scalingVars.x0*scalingVars.x0).
5563  // If running with cart. coordinates, scale by length (scalingVars.x0).
5564  iterDI->area /= scalingVars.a0;
5565  int size = iterDI->areaVector.size();
5566  for (i = 0; i < size; ++i)
5567  {
5568  iterDI->areaVector[i] /= scalingVars.a0;
5569  }
5570  }
5571 
5572  for (i=0;i<numMeshPoints;++i)
5573  {
5574  nnVec[i] /= scalingVars.C0;
5575  npVec[i] /= scalingVars.C0;
5576  CVec[i] /= scalingVars.C0;
5577  VVec[i] /= scalingVars.V0;
5578  unVec[i] /= scalingVars.u0;
5579  upVec[i] /= scalingVars.u0;
5580  tnVec[i] /= scalingVars.t0;
5581  tpVec[i] /= scalingVars.t0;
5582  xVec[i] /= scalingVars.x0;
5583  yVec[i] /= scalingVars.x0;
5584 
5585 #ifdef Xyce_NEW_BC
5586  if (boundarySten[i]) continue;
5587 #endif // Xyce_NEW_BC
5588 
5589  if( useVectorGIDFlag )
5590  {
5591  if (Vrowarray[i] != -1)
5592  (solVectorPtr)->setElementByGlobalIndex( Vrowarray[i], VVec[i], 0);
5593  if (Nrowarray[i] != -1)
5594  (solVectorPtr)->setElementByGlobalIndex( Nrowarray[i], nnVec[i], 0);
5595  if (Prowarray[i] != -1)
5596  (solVectorPtr)->setElementByGlobalIndex( Prowarray[i], npVec[i], 0);
5597  }
5598  else
5599  {
5600  (*solVectorPtr)[li_Vrowarray[i]] = VVec[i];
5601  (*solVectorPtr)[li_Nrowarray[i]] = nnVec[i];
5602  (*solVectorPtr)[li_Prowarray[i]] = npVec[i];
5603  }
5604  }
5605 
5606  for (i=0;i<meshContainerPtr->getNumEdges();++i)
5607  {
5608  unE_Vec[i] /= scalingVars.u0;
5609  upE_Vec[i] /= scalingVars.u0;
5610  }
5611 
5612 
5613  variablesScaled = true;
5614 
5615  return bsuccess;
5616 
5617 }
5618 
5619 //-----------------------------------------------------------------------------
5620 // Function : Instance::unScaleVariables
5621 //
5622 // Purpose : Reverses the effect of scaleVariables.
5623 //
5624 // Special Notes : It was neccessary to add this after setting sensitivity
5625 // calculations based on the doping.
5626 //
5627 // Scope : public
5628 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
5629 // Creation Date : 04/17/03
5630 //-----------------------------------------------------------------------------
5632 {
5633  bool bsuccess = true;
5634  int i;
5635  N_LAS_Vector * solVectorPtr = extData.nextSolVectorPtr;
5636 
5637  Na *= scalingVars.C0;
5638  Nd *= scalingVars.C0;
5639  Ni *= scalingVars.C0;
5640  NnMax *= scalingVars.C0;
5641  NpMax *= scalingVars.C0;
5642 
5644 
5647 
5648  Vbi *= scalingVars.V0;
5649 
5650  // scale area, elen and ilen!
5651  bsuccess = meshContainerPtr->scaleMesh(1.0/scalingVars.x0);
5652 
5653  // scale boundary conditions:
5654  std::vector<DeviceInterfaceNode>::iterator firstDI = dIVec.begin();
5655  std::vector<DeviceInterfaceNode>::iterator lastDI = dIVec.end ();
5656  std::vector<DeviceInterfaceNode>::iterator iterDI = firstDI;
5657 
5658  for (;iterDI!=lastDI;++iterDI)
5659  {
5660  for (i=0;i<iterDI->numBoundaryPoints;++i)
5661  {
5662  iterDI->nnbcVec[i] *= scalingVars.C0;
5663  iterDI->npbcVec[i] *= scalingVars.C0;
5664  iterDI->VbcVec [i] *= scalingVars.V0;
5665  iterDI->VequVec[i] *= scalingVars.V0;
5666  }
5667 
5668  // If running with cylindrical coordinates, scale by area (scalingVars.x0*scalingVars.x0).
5669  // If running with cart. coordinates, scale by length (scalingVars.x0).
5670  iterDI->area *= scalingVars.a0;
5671  int size = iterDI->areaVector.size();
5672  for (i = 0; i < size; ++i)
5673  {
5674  iterDI->areaVector[i] *= scalingVars.a0;
5675  }
5676  }
5677 
5678  for (i=0;i<numMeshPoints;++i)
5679  {
5680  nnVec[i] *= scalingVars.C0;
5681  npVec[i] *= scalingVars.C0;
5682  CVec[i] *= scalingVars.C0;
5683  VVec[i] *= scalingVars.V0;
5684  unVec[i] *= scalingVars.u0;
5685  upVec[i] *= scalingVars.u0;
5686  tnVec[i] *= scalingVars.t0;
5687  tpVec[i] *= scalingVars.t0;
5688  xVec[i] *= scalingVars.x0;
5689  yVec[i] *= scalingVars.x0;
5690 
5691 #ifdef Xyce_NEW_BC
5692  if (boundarySten[i]) continue;
5693 #endif // Xyce_NEW_BC
5694 
5695  if( useVectorGIDFlag )
5696  {
5697  if (Vrowarray[i] != -1)
5698  (solVectorPtr)->setElementByGlobalIndex( Vrowarray[i], VVec[i], 0);
5699  if (Nrowarray[i] != -1)
5700  (solVectorPtr)->setElementByGlobalIndex( Nrowarray[i], nnVec[i], 0);
5701  if (Prowarray[i] != -1)
5702  (solVectorPtr)->setElementByGlobalIndex( Prowarray[i], npVec[i], 0);
5703  }
5704  else
5705  {
5706  (*solVectorPtr)[li_Vrowarray[i]] = VVec[i];
5707  (*solVectorPtr)[li_Nrowarray[i]] = nnVec[i];
5708  (*solVectorPtr)[li_Prowarray[i]] = npVec[i];
5709  }
5710  }
5711 
5712  for (i=0;i<meshContainerPtr->getNumEdges();++i)
5713  {
5714  unE_Vec[i] *= scalingVars.u0;
5715  upE_Vec[i] *= scalingVars.u0;
5716  }
5717 
5718  variablesScaled = false;
5719 
5720  return bsuccess;
5721 
5722 }
5723 
5724 //-----------------------------------------------------------------------------
5725 // Function : Instance::scaleDopeVariables
5726 //
5727 // Purpose : This function performs scaling, similarly to
5728 // function scaleVariables, but it only modifies variables
5729 // related to the doping. This is only used in the context
5730 // of a sensitivity calculation, in which the sensitivity
5731 // parameters that are being perturbed are doping params.
5732 //
5733 // Special Notes :
5734 //
5735 // Scope : public
5736 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
5737 // Creation Date : 04/28/03
5738 //-----------------------------------------------------------------------------
5740 {
5741  bool bsuccess = true;
5742  int i;
5743 
5744  Na /= scalingVars.C0;
5745  Nd /= scalingVars.C0;
5746  Ni /= scalingVars.C0;
5747  NnMax /= scalingVars.C0;
5748  NpMax /= scalingVars.C0;
5749 
5750  // scale boundary conditions:
5751  std::vector<DeviceInterfaceNode>::iterator firstDI = dIVec.begin();
5752  std::vector<DeviceInterfaceNode>::iterator lastDI = dIVec.end ();
5753  std::vector<DeviceInterfaceNode>::iterator iterDI = firstDI;
5754 
5755  for (;iterDI!=lastDI;++iterDI)
5756  {
5757  for (i=0;i<iterDI->numBoundaryPoints;++i)
5758  {
5759  iterDI->nnbcVec[i] /= scalingVars.C0;
5760  iterDI->npbcVec[i] /= scalingVars.C0;
5761  iterDI->VequVec[i] /= scalingVars.V0;
5762  }
5763  }
5764 
5765  for (i=0;i<numMeshPoints;++i)
5766  {
5767  CVec[i] /= scalingVars.C0;
5768  unVec[i] /= scalingVars.u0;
5769  upVec[i] /= scalingVars.u0;
5770  tnVec[i] /= scalingVars.t0;
5771  tpVec[i] /= scalingVars.t0;
5772  nnVec[i] /= scalingVars.C0;
5773  npVec[i] /= scalingVars.C0;
5774  xVec[i] /= scalingVars.x0;
5775  yVec[i] /= scalingVars.x0;
5776  }
5777 
5778  for (i=0;i<meshContainerPtr->getNumEdges();++i)
5779  {
5780  unE_Vec[i] /= scalingVars.u0;
5781  upE_Vec[i] /= scalingVars.u0;
5782  }
5783 
5784 
5785  variablesScaled = true;
5786 
5787  return bsuccess;
5788 
5789 }
5790 
5791 //-----------------------------------------------------------------------------
5792 // Function : Instance::unScaleDopeVariables
5793 //
5794 // Purpose : Reverses the effect of scaleDopeVariables.
5795 //
5796 // Special Notes : Almost everything needed to use a new, perturbed doping
5797 // profile is just re-generated from scratch, so there
5798 // isn't much to do here.
5799 //
5800 // Scope : public
5801 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
5802 // Creation Date : 04/28/03
5803 //-----------------------------------------------------------------------------
5805 {
5806  bool bsuccess = true;
5807  int i;
5808 
5809  Na *= scalingVars.C0;
5810  Nd *= scalingVars.C0;
5811  Ni *= scalingVars.C0;
5812  NnMax *= scalingVars.C0;
5813  NpMax *= scalingVars.C0;
5814 
5815  for (i=0;i<numMeshPoints;++i)
5816  {
5817  nnVec[i] *= scalingVars.C0;
5818  npVec[i] *= scalingVars.C0;
5819  xVec [i] *= scalingVars.x0;
5820  yVec [i] *= scalingVars.x0;
5821  }
5822 
5823  variablesScaled = false;
5824 
5825  return bsuccess;
5826 
5827 }
5828 
5829 //-----------------------------------------------------------------------------
5830 // Function : Instance::calcInitialGuess
5831 // Purpose : This function calculates the initial e-, h+ densties
5832 // and the intial voltage.
5833 // Special Notes :
5834 // Scope : public
5835 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
5836 // Creation Date : 11/14/01
5837 //-----------------------------------------------------------------------------
5839 {
5840  bool bsuccess = true;
5841  int i;
5842  N_LAS_Vector * solVectorPtr = extData.nextSolVectorPtr;
5843 
5844 #if 0
5845  N_LAS_Vector * solVectorPtr;
5846  N_LAS_Vector * currSolVectorPtr;
5847  N_LAS_Vector * lastSolVectorPtr;
5848 
5849  if (extData.initializeAllFlag == true)
5850  {
5851  solVectorPtr = extData.nextSolVectorPtr;
5852  currSolVectorPtr = extData.currSolVectorPtr;
5853  lastSolVectorPtr = extData.lastSolVectorPtr;
5854  }
5855 #endif
5856 
5857 #ifdef Xyce_DEBUG_DEVICE
5858  if (getDeviceOptions().debugLevel > 0)
5859  {
5860  Xyce::dout() << section_divider << std::endl;
5861  Xyce::dout() << "Instance::calcInitialGuess"<< std::endl;
5862  Xyce::dout() << " name = " << getName() << std::endl;
5863  }
5864 #endif
5865 
5866  // set up an initial guess for nnVec and npVec, based on the doping profile,
5867  // and the equilibrium density expressions. Place these in the
5868  // solution vector.
5869 
5870  double tmp(0.0);
5871  double Ci(0.0);
5872  double Cisq(0.0), Nisq(0.0);
5873 
5874  for (i=0;i<numMeshPoints;++i)
5875  {
5876  Ci = CVec[i];
5877  Cisq = Ci*Ci;
5878  Nisq = Ni*Ni; // Ni is the intrinsic concentration
5879 
5880  // equilibrium electron concentration:
5881  tmp = (fabs(Ci)+sqrt(Cisq+4*Nisq))/2.0;
5882  nnVec[i] = ((Ci>=0)?(tmp):(0.0)) + ((Ci<0)?(Nisq/tmp):(0.0));
5883 
5884  // equilibrium hole concentration:
5885  tmp = (fabs(Ci)+sqrt(Cisq+4*Nisq))/2.0;
5886  npVec[i] = ((Ci<=0)?(tmp):(0.0)) + ((Ci>0)?(Nisq/tmp):(0.0));
5887 
5888 #ifdef Xyce_DEBUG_DEVICE
5889  if (getDeviceOptions().debugLevel > 0)
5890  {
5891  Xyce::dout() << "nnVec[" << i << "] = " << nnVec[i];
5892  Xyce::dout() << " npVec[" << i << "] = " << npVec[i];
5893  Xyce::dout() << std::endl;
5894  }
5895 #endif
5896  }
5897 
5898  for (i=0;i<numMeshPoints;++i)
5899  {
5900  // the doping is n-type.
5901  if (Ci>0)
5902  {
5903  VVec[i] = + Vt * log(nnVec[i]/Ni);
5904  }
5905  // the doping is p-type.
5906  else
5907  {
5908  VVec[i] = - Vt * log(npVec[i]/Ni);
5909  }
5910 
5911 #ifdef Xyce_DEBUG_DEVICE
5912  if (getDeviceOptions().debugLevel > 0)
5913  {
5914  Xyce::dout() << "VVec[" << i << "] = " << VVec[i] << std::endl;
5915  }
5916 #endif
5917  }
5918 
5919  // Scale and offset this initial guess for V so that it matches the
5920  // Vequ boundary conditions.
5921  double VminTmp = +1.0e+99;
5922  double VmaxTmp = -1.0e+99;
5923 
5924  double VminBC = +1.0e+99;
5925  double VmaxBC = -1.0e+99;
5926 
5927  std::vector<DeviceInterfaceNode>::iterator firstDI = dIVec.begin();
5928  std::vector<DeviceInterfaceNode>::iterator lastDI = dIVec.end ();
5929  std::vector<DeviceInterfaceNode>::iterator iterDI;
5930 
5931  for (iterDI=firstDI;iterDI!=lastDI;++iterDI)
5932  {
5933  mLabel * labelPtr = meshContainerPtr->getLabel(iterDI->eName);
5934  std::vector<int>::iterator iterNode = labelPtr->mNodeVector.begin();
5935  std::vector<int>::iterator lastNode = labelPtr->mNodeVector.end ();
5936 
5937  for ( ;iterNode!=lastNode;++iterNode)
5938  {
5939  if (VminTmp > VVec[*iterNode]) VminTmp = VVec[*iterNode];
5940  if (VmaxTmp < VVec[*iterNode]) VmaxTmp = VVec[*iterNode];
5941 
5942  int ilocal = iterDI->meshGlobalToLocal[*iterNode];
5943  if (VminBC > iterDI->VequVec[ilocal]) VminBC = iterDI->VequVec[ilocal];
5944  if (VmaxBC < iterDI->VequVec[ilocal]) VmaxBC = iterDI->VequVec[ilocal];
5945  }
5946  }
5947 
5948  double Voffset = -VminTmp;
5949  double VtotDiff = VmaxTmp-VminTmp;
5950  double VBCDiff = VmaxBC-VminBC;
5951  double Vscale = (VtotDiff!=0.0)?(VBCDiff/VtotDiff):1.0;
5952 
5953 #ifdef Xyce_DEBUG_DEVICE
5954  if (getDeviceOptions().debugLevel > 0)
5955  {
5956  Xyce::dout() << "Voffset = " << Voffset << std::endl;
5957  Xyce::dout() << "VtotDiff = " << VtotDiff << std::endl;
5958  Xyce::dout() << "VBCDiff = " << VBCDiff << std::endl;
5959  Xyce::dout() << "Vscale = " << Vscale << std::endl;
5960  }
5961 #endif
5962 
5963  for (i=0;i<numMeshPoints;++i)
5964  {
5965  VVec[i] += Voffset;
5966  }
5967 
5968  for (i=0;i<numMeshPoints;++i)
5969  {
5970  VVec[i] *= Vscale;
5971  }
5972 
5973  // Place all these initial values into the solution vector.
5974  for (i=0;i<numMeshPoints;++i)
5975  {
5976 
5977 #ifdef Xyce_DEBUG_DEVICE
5978  if (getDeviceOptions().debugLevel > 0)
5979  {
5980  Xyce::dout() << "VVec[" << i << "] = " << VVec[i] << std::endl;
5981  }
5982 #endif
5983 
5984 #ifdef Xyce_NEW_BC
5985  if (boundarySten[i]) continue;
5986 #endif // Xyce_NEW_BC
5987 
5988  if (extData.initializeAllFlag == true)
5989  {
5990  if( useVectorGIDFlag )
5991  {
5992  if (Vrowarray[i] != -1)
5993  (solVectorPtr)->setElementByGlobalIndex( Vrowarray[i], VVec[i], 0);
5994  if (Nrowarray[i] != -1)
5995  (solVectorPtr)->setElementByGlobalIndex( Nrowarray[i], nnVec[i], 0);
5996  if (Prowarray[i] != -1)
5997  (solVectorPtr)->setElementByGlobalIndex( Prowarray[i], npVec[i], 0);
5998 
5999 #if 0
6000  if (Vrowarray[i] != -1)
6001  (currSolVectorPtr)->setElementByGlobalIndex( Vrowarray[i], VVec[i], 0);
6002  if (Nrowarray[i] != -1)
6003  (currSolVectorPtr)->setElementByGlobalIndex( Nrowarray[i], nnVec[i], 0);
6004  if (Prowarray[i] != -1)
6005  (currSolVectorPtr)->setElementByGlobalIndex( Prowarray[i], npVec[i], 0);
6006 
6007  if (Vrowarray[i] != -1)
6008  (lastSolVectorPtr)->setElementByGlobalIndex( Vrowarray[i], VVec[i], 0);
6009  if (Nrowarray[i] != -1)
6010  (lastSolVectorPtr)->setElementByGlobalIndex( Nrowarray[i], nnVec[i], 0);
6011  if (Prowarray[i] != -1)
6012  (lastSolVectorPtr)->setElementByGlobalIndex( Prowarray[i], npVec[i], 0);
6013 #endif
6014  }
6015  else
6016  {
6017  (*solVectorPtr)[li_Vrowarray[i]] = VVec[i];
6018  (*solVectorPtr)[li_Nrowarray[i]] = nnVec[i];
6019  (*solVectorPtr)[li_Prowarray[i]] = npVec[i];
6020 
6021 #if 0
6022  (*currSolVectorPtr)[li_Vrowarray[i]] = VVec[i];
6023  (*currSolVectorPtr)[li_Nrowarray[i]] = nnVec[i];
6024  (*currSolVectorPtr)[li_Prowarray[i]] = npVec[i];
6025 
6026  (*lastSolVectorPtr)[li_Vrowarray[i]] = VVec[i];
6027  (*lastSolVectorPtr)[li_Nrowarray[i]] = nnVec[i];
6028  (*lastSolVectorPtr)[li_Prowarray[i]] = npVec[i];
6029 #endif
6030  }
6031  }
6032  }
6033 
6034  // get the maximum and minimum potentials.
6035  VmaxExp = -1.0e99;
6036  VminExp = +1.0e99;
6037 
6038  for (int j=0;j<numMeshPoints;++j)
6039  {
6040  if (VmaxExp < VVec[j]) VmaxExp = VVec[j];
6041  if (VminExp > VVec[j]) VminExp = VVec[j];
6042  }
6043 
6044 #ifdef Xyce_DEBUG_DEVICE
6045  if (getDeviceOptions().debugLevel > 0)
6046  {
6047  Xyce::dout() << std::endl;
6048  Xyce::dout().setf(std::ios::scientific);
6049  Xyce::dout() << "Vmax = " << VmaxExp << std::endl;
6050  Xyce::dout() << "Vmin = " << VminExp << std::endl;
6051  Xyce::dout() << std::endl;
6052  }
6053 #endif
6054 
6055 #ifdef Xyce_DEBUG_DEVICE
6056  if (getDeviceOptions().debugLevel > 0)
6057  {
6058  Xyce::dout() << section_divider << std::endl;
6059  }
6060 #endif
6061 
6062  return bsuccess;
6063 }
6064 
6065 //-----------------------------------------------------------------------------
6066 // Function : Instance::calcVequBCs
6067 // Purpose : This function sets up the "Vequ" boundary condition
6068 // variables for each electrode.
6069 //
6070 // Special Notes :
6071 //
6072 // Scope : public
6073 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
6074 // Creation Date : 04/28/03
6075 //-----------------------------------------------------------------------------
6077 {
6078  bool bsuccess = true;
6079  int i;
6080 
6081 #ifdef Xyce_DEBUG_DEVICE
6082  if (getDeviceOptions().debugLevel > 0)
6083  {
6084  Xyce::dout() << section_divider << std::endl;
6085  Xyce::dout() << "Instance::calcVequBCs"<< std::endl;
6086  Xyce::dout() << " name = " << getName() << std::endl;
6087  Xyce::dout() << " Na = "<< Na << std::endl;
6088  Xyce::dout() << " Nd = "<< Nd << std::endl;
6089  }
6090 #endif
6091 
6092  double VminBC =+1.0e+99;
6093  double VmaxBC =-1.0e+99;
6094 
6095  std::vector<DeviceInterfaceNode>::iterator firstDI = dIVec.begin();
6096  std::vector<DeviceInterfaceNode>::iterator lastDI = dIVec.end ();
6097  std::vector<DeviceInterfaceNode>::iterator iterDI;
6098 
6099  for (iterDI=firstDI;iterDI!=lastDI;++iterDI)
6100  {
6101 #ifdef Xyce_DEBUG_DEVICE
6102  if (getDeviceOptions().debugLevel > 0)
6103  {
6104  Xyce::dout() << "DI name = " << iterDI->eName << std::endl;
6105  Xyce::dout() << "material = " << iterDI->material << std::endl;
6106  if (iterDI->materialGiven)
6107  {
6108  Xyce::dout() << "material was given" << std::endl;
6109  }
6110  else
6111  {
6112  Xyce::dout() << "material was NOT given" << std::endl;
6113  }
6114 
6115  if (iterDI->oxideBndryFlag)
6116  {
6117  Xyce::dout() << "This is a boundary WITH an oxide." << std::endl;
6118  }
6119  else
6120  {
6121  Xyce::dout() << "This is a boundary WITHOUT an oxide." << std::endl;
6122  }
6123 
6124  Xyce::dout().setf(std::ios::scientific);
6125  }
6126 #endif
6127 
6128  std::string insul = "sio2"; // oxide layers can only be sio2...
6129 
6130  mLabel * labelPtr = meshContainerPtr->getLabel(iterDI->eName);
6131  std::vector<int>::iterator iterNode = labelPtr->mNodeVector.begin();
6132  std::vector<int>::iterator lastNode = labelPtr->mNodeVector.end ();
6133 
6134  for (i=0;i<iterDI->numBoundaryPoints;++i,++iterNode)
6135  {
6136  double Ci = CVec[*iterNode];
6137  double ns = nnVec[*iterNode]; // needed for oxide interface potential
6138  double Cisq = Ci*Ci;
6139  double Nisq = Ni*Ni; // Ni is the intrinsic concentration
6140  double tmp, nnTmp, npTmp;
6141 
6142  // equilibrium electron concentration:
6143  tmp = (fabs(Ci)+sqrt(Cisq+4*Nisq))/2.0;
6144  nnTmp = ((Ci>=0)?(tmp):(0.0)) + ((Ci<0)?(Nisq/tmp):(0.0));
6145 
6146  // equilibrium hole concentration:
6147  tmp = (fabs(Ci)+sqrt(Cisq+4*Nisq))/2.0;
6148  npTmp = ((Ci<=0)?(tmp):(0.0)) + ((Ci>0)?(Nisq/tmp):(0.0));
6149 
6150  ExtendedString mater = iterDI->material;
6151  mater.toLower();
6152 
6153  if (mater=="neutral")
6154  {
6155  // the doping is n-type.
6156  if (Ci>0)
6157  {
6158  iterDI->VequVec[i] = + Vt * log(nnTmp/Ni);
6159  }
6160  // the doping is p-type.
6161  else
6162  {
6163  iterDI->VequVec[i] = - Vt * log(npTmp/Ni);
6164  }
6165  }
6166  else // this electrode has a schottky barrier.
6167  {
6168  // the doping is n-type and a metal contact.
6169  if (Ci>0)
6170  {
6171  iterDI->VequVec[i] = + matSupport.workfunc(iterDI->material)
6174  + 2.0 * Vt * log(nnTmp/Ni);
6175  }
6176  else if (Ci<=0)
6177  {
6178  iterDI->VequVec[i] = + matSupport.workfunc(iterDI->material)
6181  - 2.0 * Vt * log(npTmp/Ni);
6182  }
6183  }
6184 
6185  // if this is a metal-oxide-semiconductor boundary, add an offset due
6186  // to the charge stored in the oxide.
6187  //
6188  // V_cap = Qi/Ci
6189  if (iterDI->oxideBndryFlag)
6190  {
6191  iterDI->VequVec[i] += - iterDI->oxcharge * charge *iterDI->oxthick/
6192  (matSupport.getRelPerm(insul) * e0);
6193  }
6194 
6195  iterDI->VbcVec [i] = 0.0;
6196 
6197  if (VminBC > iterDI->VequVec[i]) VminBC = iterDI->VequVec[i];
6198  if (VmaxBC < iterDI->VequVec[i]) VmaxBC = iterDI->VequVec[i];
6199 
6200 #ifdef Xyce_DEBUG_DEVICE
6201  if (getDeviceOptions().debugLevel > 0)
6202  {
6203  Xyce::dout() << "Vequ["<<i<<"]=" << iterDI->VequVec[i];
6204  Xyce::dout() << std::endl;
6205  }
6206 #endif
6207  }
6208  }
6209 
6210 #ifdef Xyce_DEBUG_DEVICE
6211  if (getDeviceOptions().debugLevel > 0)
6212  {
6213  Xyce::dout() << "After offset:" << std::endl;
6214  }
6215 #endif
6216 
6217  // offset these boundary conditions so that the minimum is zero.
6218  double Voffset = -VminBC;
6219 
6220  for (iterDI=firstDI;iterDI!=lastDI;++iterDI)
6221  {
6222 #ifdef Xyce_DEBUG_DEVICE
6223  if (getDeviceOptions().debugLevel > 0)
6224  {
6225  Xyce::dout() << "DI name = " << iterDI->eName << std::endl;
6226  Xyce::dout().setf(std::ios::scientific);
6227  }
6228 #endif
6229 
6230  for (i=0;i<iterDI->numBoundaryPoints;++i)
6231  {
6232  iterDI->VequVec[i] += Voffset;
6233 #ifdef Xyce_DEBUG_DEVICE
6234  if (getDeviceOptions().debugLevel > 0)
6235  {
6236  Xyce::dout() << "VequVec["<<i<<"] = " << iterDI->VequVec[i] << std::endl;
6237  }
6238 #endif
6239  }
6240  }
6241 
6242 #ifdef Xyce_DEBUG_DEVICE
6243  if (getDeviceOptions().debugLevel > 0)
6244  {
6245  Xyce::dout() << section_divider << std::endl;
6246  }
6247 #endif
6248 
6249  return bsuccess;
6250 }
6251 
6252 //-----------------------------------------------------------------------------
6253 // Function : Instance::calcDensityBCs
6254 // Purpose : This function sets up the boundary condition variables
6255 // for each electrode.
6256 //
6257 // Special Notes : This function is similar to calcBoundaryConditions, but
6258 // this one only calculates BC's on N and P. Since these
6259 // never change, they only need to be calculated once.
6260 //
6261 // Scope : public
6262 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
6263 // Creation Date : 10/20/02
6264 //-----------------------------------------------------------------------------
6266 {
6267  bool bsuccess = true;
6268  std::vector<DeviceInterfaceNode>::iterator firstDI = dIVec.begin();
6269  std::vector<DeviceInterfaceNode>::iterator lastDI = dIVec.end ();
6270  std::vector<DeviceInterfaceNode>::iterator iterDI;
6271 
6272 #ifdef Xyce_DEBUG_DEVICE
6273  if (getDeviceOptions().debugLevel > 0)
6274  {
6275  Xyce::dout() << section_divider<< std::endl;
6276  Xyce::dout() << std::endl << "In Instance::calcDensityBCs" << std::endl;
6277  }
6278 #endif
6279 
6280  NnMax = -1.0e+99;
6281  NpMax = -1.0e+99;
6282 
6283  NnMin = +1.0e+99;
6284  NpMin = +1.0e+99;
6285 
6286  for (iterDI=firstDI;iterDI!=lastDI;++iterDI)
6287  {
6288 #ifdef Xyce_DEBUG_DEVICE
6289  if (getDeviceOptions().debugLevel > 0)
6290  {
6291  Xyce::dout() << iterDI->eName<< ":" << std::endl;
6292  }
6293 #endif
6294 
6295  // This expression is from Selberherr, enforcing thermal
6296  // equilibrium and vanishing space charge at ohmic contacts.
6297  for (int i=0;i<iterDI->numBoundaryPoints;++i)
6298  {
6299  mLabel * labelPtr = meshContainerPtr->getLabel(iterDI->eName);
6300  int nIndex;
6301  if (constBoundaryFlag)
6302  nIndex = labelPtr->mNodeVector[0];
6303  else
6304  nIndex = labelPtr->mNodeVector[i];
6305 
6306  iterDI->nnbcVec[i] =
6307  0.5*(sqrt(CVec[nIndex]*CVec[nIndex]+4*Ni*Ni)+CVec[nIndex]);
6308 
6309  iterDI->npbcVec[i] =
6310  0.5*(sqrt(CVec[nIndex]*CVec[nIndex]+4*Ni*Ni)-CVec[nIndex]);
6311 
6312  if (NnMax < iterDI->nnbcVec[i]) NnMax = iterDI->nnbcVec[i];
6313  if (NpMax < iterDI->npbcVec[i]) NpMax = iterDI->npbcVec[i];
6314 
6315  if (NnMin > iterDI->nnbcVec[i]) NnMin = iterDI->nnbcVec[i];
6316  if (NpMin > iterDI->npbcVec[i]) NpMin = iterDI->npbcVec[i];
6317 
6318 #ifdef Xyce_DEBUG_DEVICE
6319  if (getDeviceOptions().debugLevel > 0)
6320  {
6321  Xyce::dout() << "\tCVec["<<nIndex<<"] = " << CVec[nIndex]<< std::endl;
6322  Xyce::dout() << "\tnnbc["<<i<<"] = " << iterDI->nnbcVec[i] << std::endl;
6323  Xyce::dout() << "\tnpbc["<<i<<"] = " << iterDI->npbcVec[i] << std::endl;
6324  }
6325 #endif
6326  }
6327 
6328  // Set the boundaries to reflect nnbc and npbc. This is neccessary so
6329  // that the correct Vequ is calculated in function calcVequBCs.
6330  mLabel * labelPtr = meshContainerPtr->getLabel(iterDI->eName);
6331  std::vector<int>::iterator iterNode = labelPtr->mNodeVector.begin();
6332  std::vector<int>::iterator lastNode = labelPtr->mNodeVector.end ();
6333 
6334  for ( ;iterNode!=lastNode;++iterNode)
6335  {
6336  int i1 = iterDI->meshGlobalToLocal[*iterNode];
6337  nnVec[*iterNode] = iterDI->nnbcVec[i1]/scalingVars.C0;
6338  npVec[*iterNode] = iterDI->npbcVec[i1]/scalingVars.C0;
6339  }
6340  }
6341 
6342  if (NnMin <= 0) NnMin = 1.56269e-9; // just a guess.
6343  if (NpMin <= 0) NpMin = 1.56269e-9; // just a guess.
6344 
6345 #ifdef Xyce_DEBUG_DEVICE
6346  if (getDeviceOptions().debugLevel > 0)
6347  {
6348  Xyce::dout() << std::endl;
6349  Xyce::dout() << "NnMax = " << NnMax << std::endl;
6350  Xyce::dout() << "NpMax = " << NpMax << std::endl;
6351  Xyce::dout() << "NnMin = " << NnMin << std::endl;
6352  Xyce::dout() << "NpMin = " << NpMin << std::endl;
6353  Xyce::dout() << section_divider<< std::endl;
6354  }
6355 #endif
6356 
6357  return bsuccess;
6358 }
6359 
6360 //-----------------------------------------------------------------------------
6361 // Function : Instance::calcBoundaryConditions
6362 // Purpose : This function sets up the boundary condition variables
6363 // for each electrode. This function only handles the
6364 // voltage BC's. Density BC's are only set up once, in
6365 // function calcDensityBCs.
6366 //
6367 // Special Notes : If a continuation method is being used, the electrode
6368 // voltages are being slowly varried.
6369 //
6370 //
6371 // Scope : public
6372 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
6373 // Creation Date : 10/20/02
6374 //-----------------------------------------------------------------------------
6376 {
6377  bool bsuccess = true;
6378  int i;
6379  std::vector<DeviceInterfaceNode>::iterator firstDI = dIVec.begin();
6380  std::vector<DeviceInterfaceNode>::iterator lastDI = dIVec.end ();
6381  std::vector<DeviceInterfaceNode>::iterator iterDI;
6382 
6383 #ifdef Xyce_DEBUG_DEVICE
6384  if (getDeviceOptions().debugLevel > 0)
6385  {
6386  Xyce::dout() << std::endl << section_divider << std::endl;
6387  Xyce::dout() << std::endl << "In Instance::calcBoundaryConditions" << std::endl;
6388  }
6389 #endif
6390 
6391  for (iterDI=firstDI;iterDI!=lastDI;++iterDI)
6392  {
6393  if (getSolverState().PDEcontinuationFlag)
6394  {
6395  for (i=0;i<iterDI->numBoundaryPoints;++i)
6396  {
6397  iterDI->VbcVec[i] = iterDI->Vckt_ramp + iterDI->VequVec[i];
6398  }
6399  }
6400  else
6401  {
6402  for (i=0;i<iterDI->numBoundaryPoints;++i)
6403  {
6404  iterDI->VbcVec[i] = iterDI->Vckt + iterDI->VequVec[i];
6405  }
6406  }
6407 
6408 #ifdef Xyce_NEW_BC
6409  // if using the "new" boundary conditions, go through the V,n,p
6410  // arrays and impose the electrode boundary values.
6411  mLabel * labelPtr = meshContainerPtr->getLabel(iterDI->eName);
6412  std::vector<int>::iterator iterNode = labelPtr->mNodeVector.begin();
6413  std::vector<int>::iterator lastNode = labelPtr->mNodeVector.end ();
6414 
6415  for ( ;iterNode!=lastNode;++iterNode)
6416  {
6417  int i1 = iterDI->meshGlobalToLocal[*iterNode];
6418  VVec[*iterNode] = iterDI->VbcVec [i1];
6419  nnVec[*iterNode] = iterDI->nnbcVec[i1];
6420  npVec[*iterNode] = iterDI->npbcVec[i1];
6421  }
6422 #endif // Xyce_NEW_BC
6423 
6424 #ifdef Xyce_DEBUG_DEVICE
6425  if (getDeviceOptions().debugLevel > 0)
6426  {
6427  Xyce::dout() << iterDI->eName << ":" << std::endl;
6428  for (i=0;i<iterDI->numBoundaryPoints;++i)
6429  {
6430  Xyce::dout()<<"Vbc["<<i<<"] = "<<iterDI->VbcVec[i]<<", "<<iterDI->VbcVec[i]*scalingVars.V0;
6431  Xyce::dout() << " nnbc["<<i<<"] = "<< iterDI->nnbcVec[i];
6432  Xyce::dout() << ", "<<iterDI->nnbcVec[i]*scalingVars.C0;
6433  Xyce::dout() << " npbc["<<i<<"] = "<< iterDI->npbcVec[i];
6434  Xyce::dout() << ", "<<iterDI->npbcVec[i]*scalingVars.C0;
6435  Xyce::dout() << std::endl;
6436  }
6437  }
6438 #endif
6439  }
6440 
6441 #ifdef Xyce_DEBUG_DEVICE
6442  if (getDeviceOptions().debugLevel > -5 && getSolverState().debugTimeFlag)
6443  {
6444  Xyce::dout() << section_divider << std::endl;
6445  }
6446 #endif
6447 
6448  return bsuccess;
6449 }
6450 
6451 //-----------------------------------------------------------------------------
6452 // Function : Instance::obtainNodeVoltages.
6453 //
6454 // Purpose : This function obtains the nodal voltages from the
6455 // solution vector, which are applied as boundary
6456 // conditions on the electrodes.
6457 //
6458 // Special Notes : This was originally part of function obtainSolution, but
6459 // is needed also by function enableContinuation. So I've
6460 // put it in one place.
6461 //
6462 // If voltage limiting is turned on, this is the function
6463 // in which to apply it.
6464 //
6465 // Scope : public
6466 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
6467 // Creation Date : 12/13/02
6468 //-----------------------------------------------------------------------------
6470 {
6471  bool bsuccess = true;
6472 
6473 #ifdef Xyce_DEBUG_DEVICE
6474  if (getDeviceOptions().debugLevel > 0)
6475  {
6476  Xyce::dout() << std::endl << section_divider << std::endl;
6477  Xyce::dout() << std::endl << "In Instance::obtainNodeVoltages" << std::endl;
6478  }
6479 #endif
6480 
6481  N_LAS_Vector * solVectorPtr = extData.nextSolVectorPtr;
6482 
6483  std::vector<DeviceInterfaceNode>::iterator firstDI = dIVec.begin();
6484  std::vector<DeviceInterfaceNode>::iterator lastDI = dIVec.end ();
6485  std::vector<DeviceInterfaceNode>::iterator iterDI;
6486 
6487  for (iterDI=firstDI;iterDI!=lastDI;++iterDI)
6488  {
6489 
6490  if( useVectorGIDFlag )
6491  {
6492  if (iterDI->gid != -1)
6493  iterDI->Vckt = (solVectorPtr)->getElementByGlobalIndex(iterDI->gid, 0);
6494  else
6495  iterDI->Vckt = 0.0;
6496  }
6497  else
6498  {
6499  iterDI->Vckt = (*solVectorPtr)[iterDI->lid];
6500  }
6501 
6502  iterDI->Vckt /= scalingVars.V0;
6503 
6504 #ifdef Xyce_DEBUG_DEVICE
6506  {
6507  Xyce::dout() << iterDI->eName << " Vckt = " << iterDI->Vckt;
6508  Xyce::dout() << " Vckt*scalingVars.V0 = " << (iterDI->Vckt * scalingVars.V0) << std::endl;
6509  }
6510 #endif
6511  }
6512 
6513 #ifdef Xyce_DEBUG_DEVICE
6515  {
6516  Xyce::dout() << section_divider << std::endl;
6517  }
6518 #endif
6519 
6520  return bsuccess;
6521 }
6522 
6523 //-----------------------------------------------------------------------------
6524 // Function : Instance::applyVoltageLimiting
6525 //
6526 // Purpose : if voltage limiting is turned on, this function
6527 // applies it to the Vckt values.
6528 //
6529 // Special Notes : This is only really set up to work when the 2-level
6530 // Newton is being used.
6531 //
6532 // Scope : public
6533 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
6534 // Creation Date : 12/15/02
6535 //-----------------------------------------------------------------------------
6537 {
6538  bool bsuccess = true;
6539 
6540  std::vector<DeviceInterfaceNode>::iterator firstDI = dIVec.begin();
6541  std::vector<DeviceInterfaceNode>::iterator lastDI = dIVec.end ();
6542  std::vector<DeviceInterfaceNode>::iterator iterDI;
6543 
6544 #ifdef Xyce_DEBUG_DEVICE
6545  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
6546  {
6547  Xyce::dout() << std::endl << section_divider << std::endl;
6548  Xyce::dout() << "device: " << getName() << std::endl;
6549  }
6550 #endif
6551 
6552  for (iterDI=firstDI;iterDI!=lastDI;++iterDI)
6553  {
6554  double v1 = iterDI->Vckt * scalingVars.V0;
6555  double v1_old = iterDI->Vckt_old * scalingVars.V0;
6556 #ifdef Xyce_DEBUG_DEVICE
6557  double v1_orig = v1;
6558 #endif
6559 
6560  double delV1 = v1 - v1_old;
6561 
6562  if ( delV1 > 1.25 ) v1 = v1_old + 1.25;
6563  if ( delV1 < -0.75 ) v1 = v1_old - 0.75;
6564 
6565 #ifdef Xyce_DEBUG_DEVICE
6566  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
6567  {
6568  Xyce::dout() << "electrode = " << iterDI->eName << std::endl;
6569  Xyce::dout() << "v1 = " << v1 << std::endl;
6570  Xyce::dout() << "v1_old = " << v1_old << std::endl;
6571  Xyce::dout() << "v1_orig = " << v1_orig << std::endl;
6572  Xyce::dout() << "v1/scalingVars.V0 = " << v1/scalingVars.V0 << std::endl;
6573  Xyce::dout() << "v1_old/scalingVars.V0 = " << v1_old/scalingVars.V0 << std::endl;
6574  Xyce::dout() << "v1_orig/scalingVars.V0 = " << v1_orig/scalingVars.V0 << std::endl;
6575  Xyce::dout() << std::endl;
6576  }
6577 #endif
6578 
6579  iterDI->Vckt = v1/scalingVars.V0;
6580  iterDI->Vckt_final = v1/scalingVars.V0;
6581  }
6582 
6583 #ifdef Xyce_DEBUG_DEVICE
6584  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
6585  {
6586  Xyce::dout() << section_divider << std::endl;
6587  }
6588 #endif
6589 
6590  return bsuccess;
6591 }
6592 
6593 //-----------------------------------------------------------------------------
6594 // Function : Instance::obtainSolution
6595 // Purpose : This function extracts V, nn, and np from the solution
6596 // vector and copies them into local arrays.
6597 // Special Notes :
6598 // Scope : public
6599 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
6600 // Creation Date : 11/14/01
6601 //-----------------------------------------------------------------------------
6603 {
6604  bool bsuccess = true;
6605  bool bs1 = true;
6606 
6607  N_LAS_Vector * solVectorPtr = extData.nextSolVectorPtr;
6608 
6609 #ifdef Xyce_DEBUG_DEVICE
6610  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
6611  {
6612  Xyce::dout() << section_divider << "\n";
6613  Xyce::dout() << "Instance::obtainSolution\n";
6614  Xyce::dout() << "solVectorPtr = " << solVectorPtr << std::endl;
6615  }
6616 #endif
6617 
6618  bs1 = obtainNodeVoltages ();
6619  bsuccess = bsuccess && bs1;
6620 
6621  // set up the solution array:
6622  int i;
6623  for (i=0;i<numMeshPoints;++i)
6624  {
6625 
6626 #ifdef Xyce_NEW_BC
6627  if (boundarySten[i]) continue;
6628 #endif // Xyce_NEW_BC
6629 
6630  vOwnVec[i] = (Vrowarray[i] != -1);
6631 
6632  if( useVectorGIDFlag )
6633  {
6634  if (Vrowarray[i] != -1)
6635  {
6636  VVec[i] = (solVectorPtr)->getElementByGlobalIndex(Vrowarray[i], 0);
6637  }
6638  }
6639  else
6640  {
6641  VVec[i] = (*solVectorPtr)[li_Vrowarray[i]];
6642  }
6643  }
6644 
6645 #ifdef Xyce_DEBUG_DEVICE
6647  {
6648  for (i=0;i<numMeshPoints;++i)
6649  {
6650  Xyce::dout() << "VVec["<<i<<"]=\t";
6651  Xyce::dout().width(20);
6652  Xyce::dout().precision(12);
6653  Xyce::dout().setf(std::ios::scientific);
6654  Xyce::dout() << VVec[i];
6655  Xyce::dout() << " " << VVec[i]*scalingVars.V0;
6656  Xyce::dout() << "\n";
6657  }
6658  }
6659 #endif
6660 
6661  // If the previous solution is from the nonlinear Poisson solution,
6662  // then calculate what the electron and hole densities must be, and
6663  // place them into the solution vector.
6664 
6665  // If we are past the nonlinear Poisson phase, then simply obtain
6666  // nn and np from the solution vector and move on.
6667 
6668 #ifdef Xyce_DEBUG_DEVICE
6669  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
6670  {
6671  Xyce::dout() << " About to get the density.\n";
6672  if (getSolverState().dcopFlag) Xyce::dout() << "DCOP load" << std::endl;
6673  else Xyce::dout() << "Transient load" << std::endl;
6674  Xyce::dout() << " doubleDCOPStep = " << getSolverState().doubleDCOPStep << "\n";
6675  }
6676 #endif
6677 
6678  if ((getSolverState().dcopFlag) && getSolverState().doubleDCOPStep==0)
6679  {
6681 
6682  for (i=0;i<numMeshPoints;++i)
6683  {
6684 #ifdef Xyce_NEW_BC
6685  if (boundarySten[i]) continue;
6686 #endif // Xyce_NEW_BC
6687 
6688  nnOwnVec[i] = (Nrowarray[i] != -1);
6689  npOwnVec[i] = (Prowarray[i] != -1);
6690 
6691  if( useVectorGIDFlag )
6692  {
6693  if (Nrowarray[i] != -1)
6694  (solVectorPtr)->setElementByGlobalIndex(Nrowarray[i],nnVec[i]);
6695  if (Prowarray[i] != -1)
6696  (solVectorPtr)->setElementByGlobalIndex(Prowarray[i],npVec[i]);
6697  }
6698  else
6699  {
6700  (*solVectorPtr)[li_Nrowarray[i]] = nnVec[i];
6701  (*solVectorPtr)[li_Prowarray[i]] = npVec[i];
6702  }
6703  }
6704  }
6705  else
6706  {
6707 #ifdef Xyce_DEBUG_DEVICE
6709  {
6710  Xyce::dout()<<" Obtaining densities from solution vector (not voltage dep.)\n";
6711  }
6712 #endif
6713  for (i=0;i<numMeshPoints;++i)
6714  {
6715 
6716 #ifdef Xyce_NEW_BC
6717  if (boundarySten[i]) continue;
6718 #endif // Xyce_NEW_BC
6719  nnOwnVec[i] = (Nrowarray[i] != -1);
6720  npOwnVec[i] = (Prowarray[i] != -1);
6721 
6722  if( useVectorGIDFlag )
6723  {
6724  if (Nrowarray[i] != -1)
6725  nnVec[i] = (solVectorPtr)->getElementByGlobalIndex(Nrowarray[i], 0);
6726  }
6727  else
6728  {
6729  nnVec[i] = (*solVectorPtr)[li_Nrowarray[i]];
6730  }
6731 
6732 #ifdef Xyce_PDE_DENSITY_CONSTRAINT
6733  // if the density is less than zero, force to be zero.
6734  if (nnVec[i] < 0.0)
6735  {
6736  nnVec[i] = 0.0;
6737  if( useVectorGIDFlag )
6738  {
6739  if (Nrowarray[i] != -1)
6740  {
6741  (solVectorPtr)->setElementByGlobalIndex(Nrowarray[i], 0.0, 0);
6742  }
6743  }
6744  else
6745  {
6746  (*solVectorPtr)[li_Nrowarray[i]] = 0.0;
6747  }
6748  }
6749 #endif
6750 
6751  if( useVectorGIDFlag )
6752  {
6753  if (Prowarray[i] != -1)
6754  {
6755  npVec[i] = (solVectorPtr)->getElementByGlobalIndex(Prowarray[i], 0);
6756  }
6757  }
6758  else
6759  {
6760  npVec[i] = (*solVectorPtr)[li_Prowarray[i]];
6761  }
6762 
6763 #ifdef Xyce_PDE_DENSITY_CONSTRAINT
6764  // if the density is less than zero, force to be zero.
6765  if (npVec[i] < 0.0)
6766  {
6767  npVec[i] = 0.0;
6768  if( useVectorGIDFlag )
6769  {
6770  if (Prowarray[i] != -1)
6771  (solVectorPtr)->setElementByGlobalIndex(Prowarray[i], 0.0, 0);
6772  }
6773  else
6774  {
6775  (*solVectorPtr)[li_Prowarray[i]] = 0;
6776  }
6777  }
6778 #endif
6779  }
6780  }
6781 
6782 #ifdef Xyce_DEBUG_DEVICE
6784  {
6785  for (i=0;i<numMeshPoints;++i)
6786  {
6787  Xyce::dout() << "nnVec["<<i<<"]=\t";
6788  Xyce::dout().width(14);
6789  Xyce::dout().precision(6);
6790  Xyce::dout().setf(std::ios::scientific);
6791  Xyce::dout() << nnVec[i];
6792  Xyce::dout() << " " << nnVec[i]*scalingVars.C0;
6793 
6794  Xyce::dout() << " npVec["<<i<<"]=\t";
6795  Xyce::dout().width(14);
6796  Xyce::dout().precision(6);
6797  Xyce::dout().setf(std::ios::scientific);
6798  Xyce::dout() << npVec[i];
6799  Xyce::dout() << " " << npVec[i]*scalingVars.C0;
6800 
6801  Xyce::dout() << std::endl;
6802  }
6803  }
6804 #endif
6805 
6806  // now set boundary conditions:
6807  // if the circuit is coupled to the PDE device, then bc's
6808  // must be updated everytime.
6809  //
6810  // If the circuit and PDE device are not coupled, then the
6811  // circuit node voltages can be considered constant, and the
6812  // BC's only need updating at the first Newton step.
6813 
6814  if (!(getSolverState().twoLevelNewtonCouplingMode==INNER_PROBLEM))
6815  {
6816  bs1 = calcBoundaryConditions ();
6817  bsuccess = bsuccess && bs1;
6818  }
6819  else // ... if NOT coupled
6820  {
6821  if (getSolverState().newtonIter == 0)
6822  {
6823  bs1 = calcBoundaryConditions ();
6824  bsuccess = bsuccess && bs1;
6825  }
6826  }
6827 
6828 #ifdef Xyce_DEBUG_DEVICE
6829  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
6830  {
6831  Xyce::dout() << section_divider << std::endl;
6832  }
6833 #endif
6834 
6835  return bsuccess;
6836 }
6837 
6838 //-----------------------------------------------------------------------------
6839 // Function : Instance::calcRecombination
6840 // Purpose :
6841 // Special Notes : This function assumes scaling is turned on.
6842 // Scope : public
6843 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
6844 // Creation Date : 11/14/01
6845 //-----------------------------------------------------------------------------
6847 {
6848  bool bsuccess = true;
6849 
6850  int i;
6851  double Rsrh, Raug;
6852 
6853 #ifdef Xyce_DEBUG_DEVICE
6854  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
6855  {
6856  Xyce::dout() << section_divider << "\n";
6857  Xyce::dout() << "Instance::calcRecombination\n";
6858  Xyce::dout() << "\n";
6859  }
6860 #endif
6861 
6862  for (i=0;i<numMeshPoints;++i)
6863  {
6864  double n = nnVec[i];
6865  double p = npVec[i];
6866  double tn = tnVec[i];
6867  double tp = tpVec[i];
6868 
6869  // assuming Si for now.
6870  Rsrh = matSupport.calcRsrh (bulkMaterial, Ni,n,p,tn,tp);
6871  Raug = matSupport.calcRaug (bulkMaterial, Ni,n,p);
6872 
6873  RVec[i] = (Rsrh + Raug);
6874 
6875 #ifdef Xyce_DEBUG_DEVICE
6877  {
6878  Xyce::dout().precision(4);
6879  Xyce::dout() << " nnVec="<<n<<" npVec="<<p;
6880  Xyce::dout() << " tnVec="<<tn<<" tpVec="<<tp;
6881  Xyce::dout() << " Rsrh="<<Rsrh;
6882  Xyce::dout() << " Raug="<<Raug;
6883  Xyce::dout() << " RVec["<<i<<"]="<<RVec[i];
6884  Xyce::dout() << "\n";
6885  }
6886 
6887  if (getDeviceOptions().debugLevel > 1 && getSolverState().debugTimeFlag)
6888  {
6889  Xyce::dout().precision(4);
6890  Xyce::dout() << " RVec["<<i<<"]="<<RVec[i];
6891  Xyce::dout() << std::endl;
6892  }
6893 #endif
6894  }
6895 
6896 #ifdef Xyce_DEBUG_DEVICE
6897  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
6898  {
6899  Xyce::dout() << section_divider << std::endl;
6900  }
6901 #endif
6902 
6903  return bsuccess;
6904 }
6905 
6906 
6907 //-----------------------------------------------------------------------------
6908 // Function : Instance::setupPhotogen
6909 //
6910 // Purpose : Sets up the photogen source. For now, this is a simple
6911 // uniform source.
6912 //
6913 // Special Notes : Not finished yet. This is an early implementation.
6914 //
6915 // Scope : public
6916 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
6917 // Creation Date : 03/21/03
6918 //-----------------------------------------------------------------------------
6920 {
6921  bool bsuccess = true;
6922  Param ndParam;
6923  Util::Expression expr;
6924 
6925  // for now just supporting "pulse" and "const".
6926  switch (photoType)
6927  {
6928  case _PULSE_DATA:
6929  ndParam.setTag("PULSEDATA");
6930  expr.set("spice_pulse(V1, V2, TD, TR, TF, PW, PER)");
6931  expr.make_constant("V1", 0.0);
6932  expr.make_constant("V2", photoA1);
6933  expr.make_constant("TD", photoTd);
6934  expr.make_constant("TR", photoTr);
6935  expr.make_constant("TF", photoTf);
6936  expr.make_constant("PW", photoPw);
6937  expr.make_constant("PER", photoPer);
6938  ndParam.setVal(expr);
6940  break;
6941 
6942  case _DC_DATA:
6943  ndParam.setTag("PULSEDATA");
6944  expr.set("CONST");
6945  expr.make_constant("CONST", photoA1);
6946  ndParam.setVal(expr);
6948  break;
6949 
6950  default:
6951  break;
6952  }
6953 
6954 
6955  return bsuccess;
6956 }
6957 
6958 
6959 //-----------------------------------------------------------------------------
6960 // Function : Instance::calcPhotogen
6961 //
6962 // Purpose : Calculates a term similar to recombination (in that it
6963 // is used the same way, on the RHS of the continuity
6964 // equations). This is a generation term, rather than a
6965 // recombination term, so it should have the opposite sign
6966 // (+) than that of RVec.
6967 //
6968 // My goal in setting this up is to start mimicing the
6969 // capability of medici/davicini. Most of the user params
6970 // are similar to those of medici, except for there being
6971 // a "ph" precdeding the original name.
6972 //
6973 // For now just doing a uniform source.
6974 //
6975 // Special Notes : Assumes scaling is ON.
6976 //
6977 // Not finished yet. This is an early implementation.
6978 //
6979 // Scope : public
6980 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
6981 // Creation Date : 03/21/03
6982 //-----------------------------------------------------------------------------
6984 {
6985  bool bsuccess = true;
6986  int i;
6987 
6988  double time = getSolverState().currTime;
6989  double val;
6990 
6991 #ifdef Xyce_DEBUG_DEVICE
6992 
6993  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
6994  {
6995  Xyce::dout() << section_divider << "\n";
6996  Xyce::dout() << "Instance::calcPhotogen\n";
6997  Xyce::dout() << "time = " << time;
6998 
6999  if (photogenOnFlag &&
7000  time >= photoTstart &&
7001  time <= photoTstop)
7002  { Xyce::dout() << " photogen is on right now." << std::endl; }
7003  else
7004  { Xyce::dout() << " photogen is off right now." << std::endl; }
7005 
7006  Xyce::dout() << "\n";
7007  }
7008 #endif
7009 
7010  for (i=0;i<numMeshPoints;++i)
7011  {
7012  SVec[i] = 0.0;
7013  }
7014 
7015  if (photogenOnFlag &&
7016  time >= photoTstart &&
7017  time <= photoTstop)
7018  {
7019  if (getSolverState().PDEcontinuationFlag && !(photoContinuationFinished))
7020  {
7021  val = photoA1_ramp;
7022  }
7023  else
7024  {
7025  val = PulseData;
7026  }
7027 
7028  for (i=0;i<numMeshPoints;++i)
7029  {
7030  SVec[i] = (val * scalingVars.rR0);
7031  }
7032 
7033  } // photogenOnFlag, etc.
7034 
7035 #ifdef Xyce_DEBUG_DEVICE
7037  {
7038  Xyce::dout() << "Value of photogen source = " << val << std::endl;
7039  Xyce::dout() << section_divider << std::endl;
7040  }
7041 #endif
7042 
7043  return bsuccess;
7044 }
7045 
7046 //-----------------------------------------------------------------------------
7047 // Function : Instance::calcPenalty
7048 //
7049 // Purpose : Calculates extra penalty method term to be added to the
7050 // continuity equations.
7051 //
7052 // Special Notes : This should represent a smooth function which is a
7053 // function of electron/hole density. If density is
7054 // negative, the penalty function should return a large
7055 // number. If positive, it should return a zero. It needs
7056 // to be differentiable w.r.t. density.
7057 //
7058 // For now, penalty function is:
7059 //
7060 // pf = prefac*pow(dens,N) if dens < 0.0
7061 // = 0.0 if dens >= 0.0
7062 //
7063 // The default value for prefac is scalingVars.C0, and the default
7064 // value for N is 2.0. The user can set both, however.
7065 //
7066 // Scope : public
7067 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
7068 // Creation Date : 04/15/03
7069 //-----------------------------------------------------------------------------
7071 {
7072  bool bsuccess = true;
7073  int i;
7074 
7075 #ifdef Xyce_DEBUG_DEVICE
7076  bool nonZeroFlag = false;
7077 #endif
7078 
7079  double prefac;
7080  double power;
7081 
7082  // prefactor doesn't currently get used, but may later...
7083  if (penaltyPrefacGiven)
7084  {
7085  prefac = penaltyPrefac;
7086  }
7087  else
7088  {
7089  prefac = 1.0;
7090  }
7091 
7092  if (penaltyPowGiven)
7093  {
7094  power = penaltyPow;
7095  }
7096  else
7097  {
7098  power = 2.0;
7099  }
7100 
7101  for (i=0;i<numMeshPoints;++i)
7102  {
7103  double elec = nnVec[i]*scalingVars.C0;
7104  double hole = npVec[i]*scalingVars.C0;
7105 
7106  // electron penalty
7107  double epen = prefac*pow(elec,power);
7108  elecPenalty[i] = (nnVec[i]<0.0)?epen:0.0;
7109 
7110  // hole penalty
7111  double hpen = prefac*pow(hole,power);
7112  holePenalty[i] = (npVec[i]<0.0)?hpen:0.0;
7113 
7114 #ifdef Xyce_DEBUG_DEVICE
7115  if (elecPenalty[i] != 0.0 ||
7116  holePenalty[i] != 0.0)
7117  nonZeroFlag = true;
7118 #endif
7119  }
7120 
7121 #ifdef Xyce_DEBUG_DEVICE
7122  if (getDeviceOptions().debugLevel > 0 && nonZeroFlag)
7123  {
7124  for (i=0;i<numMeshPoints;++i)
7125  {
7126  Xyce::dout() << " e- Pen["<<i<<"] = " << elecPenalty[i];
7127  Xyce::dout() << " e- den["<<i<<"] = " << nnVec[i];
7128 
7129  Xyce::dout() << " h+ Pen["<<i<<"] = " << holePenalty[i];
7130  Xyce::dout() << " h+ den["<<i<<"] = " << npVec[i];
7131  Xyce::dout() << std::endl;
7132  }
7133  }
7134 #endif
7135 
7136  return bsuccess;
7137 }
7138 
7139 //-----------------------------------------------------------------------------
7140 // Function : Instance::pdPenalty
7141 //
7142 // Purpose : Calculates extra penalty method derivative term to be added to the
7143 // continuity equation matrix rows.
7144 //
7145 // Special Notes : This should represent a smooth function which is a
7146 // function of electron/hole density. If density is
7147 // negative, the penalty function should return a large
7148 // number. If positive, it should return a zero. It needs
7149 // to be differentiable w.r.t. density.
7150 //
7151 // For now, penalty function is:
7152 //
7153 // pf = prefac*pow(dens,N) if dens < 0.0
7154 // = 0.0 if dens >= 0.0
7155 //
7156 // The default value for prefac is scalingVars.C0, and the default
7157 // value for N is 2.0. The user can set both, however.
7158 //
7159 // d(pf)
7160 // ------- = N*prefac*pow(dens,N-1) if dens < 0.0
7161 // d(dens)
7162 //
7163 // d(pf)
7164 // ------- = 0.0 if dens >= 0.0
7165 // d(dens)
7166 //
7167 // Scope : public
7168 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
7169 // Creation Date : 04/15/03
7170 //-----------------------------------------------------------------------------
7172 {
7173  bool bsuccess = true;
7174  int i;
7175 
7176  double prefac;
7177  double power;
7178 
7179  // prefactor doesn't currently get used, but may later...
7180  if (penaltyPrefacGiven)
7181  {
7182  prefac = penaltyPrefac;
7183  }
7184  else
7185  {
7186  prefac = 1.0;
7187  }
7188 
7189  if (penaltyPowGiven)
7190  {
7191  power = penaltyPow;
7192  }
7193  else
7194  {
7195  power = 2.0;
7196  }
7197 
7198  double power2 = power - 1.0;
7199 
7200  for (i=0;i<numMeshPoints;++i)
7201  {
7202  double elec = nnVec[i];
7203  double hole = npVec[i];
7204  double Cpow = pow(scalingVars.C0,power);
7205 
7206  // electron penalty
7207  double epen = power*prefac*Cpow*pow(elec,power2);
7208  pdElecPenalty[i] = (nnVec[i]<0.0)?epen:0.0;
7209 
7210  // hole penalty
7211  double hpen = power*prefac*Cpow*pow(hole,power2);
7212  pdHolePenalty[i] = (npVec[i]<0.0)?hpen:0.0;
7213  }
7214 
7215  return bsuccess;
7216 }
7217 
7218 //-----------------------------------------------------------------------------
7219 // Function : Instance::sumSources
7220 //
7221 // Purpose : This function sums in all recombination/generation sources.
7222 // This will probably be refactored later - right now it
7223 // doesn't check what sources are enabled and what aren't -
7224 // it just sums them all in.
7225 //
7226 // Special Notes : Assumes scaling is ON.
7227 // Scope : public
7228 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
7229 // Creation Date : 03/21/03
7230 //-----------------------------------------------------------------------------
7232 {
7233  bool bsuccess = true;
7234  int i;
7235 
7236 #ifdef Xyce_DEBUG_DEVICE
7237 
7238  if (getDeviceOptions().debugLevel > 1 && getSolverState().debugTimeFlag)
7239  {
7240  Xyce::dout() << section_divider << "\n";
7241  Xyce::dout() << "Instance::sumSources\n";
7242  Xyce::dout() << "\n";
7243  }
7244 #endif
7245 
7246  for (i=0;i<numMeshPoints;++i)
7247  {
7248  // Positive R, negative S, because they are assumed to be recombination
7249  // terms. (-totSrcVec) will be the source term.
7250  totSrcVec[i] = RVec[i] - SVec[i];
7251 #ifdef Xyce_DEBUG_DEVICE
7252  if (getDeviceOptions().debugLevel > 1 && getSolverState().debugTimeFlag)
7253  {
7254  Xyce::dout() << "RVec["<<i<<"] = " << RVec[i];
7255  Xyce::dout() << " SVec["<<i<<"] = " << SVec[i];
7256  Xyce::dout() << " totSrcVec["<<i<<"] = " << totSrcVec[i] << "\n";
7257  }
7258 #endif
7259  }
7260 
7261 #ifdef Xyce_DEBUG_DEVICE
7262  if (getDeviceOptions().debugLevel > 1 && getSolverState().debugTimeFlag)
7263  {
7264  Xyce::dout() << section_divider << std::endl;
7265  }
7266 #endif
7267 
7268  return bsuccess;
7269 }
7270 
7271 //-----------------------------------------------------------------------------
7272 // Function : Instance::pdRecombination
7273 // Purpose : This function sets up the arrays of partial derivatives
7274 // associated with the recombination term.
7275 // Special Notes : This function assumes scaling is turned on.
7276 // Scope : public
7277 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
7278 // Creation Date : 11/14/01
7279 //-----------------------------------------------------------------------------
7281 {
7282  bool bsuccess = true;
7283 
7284  int i;
7285 
7286  double dRsrhdn;
7287  double dRsrhdp;
7288  double dRaugdn;
7289  double dRaugdp;
7290 
7291 #if 0
7292  double Cn,Cp;
7293  double pn;
7294  double A1, B1, C1;
7295  double dAdn, dAdp;
7296  double dBdn, dBdp;
7297 #endif
7298 
7299 #ifdef Xyce_DEBUG_DEVICE
7300  if (getDeviceOptions().debugLevel > 1 && getSolverState().debugTimeFlag)
7301  {
7302  Xyce::dout() << section_divider << "\n";
7303  Xyce::dout() << "Instance::pdRecombination\n";
7304  Xyce::dout() << "\n";
7305  }
7306 #endif
7307 
7308  for (i=0;i<numMeshPoints;++i)
7309  {
7310 #if 0
7311  Cn = 2.8e-31;
7312  Cp = 1.2e-31;
7313  pn = Ni*Ni;
7314 
7315  // Rsrh derivatives: checklater.
7316  // (Rsrch = A1*B1)
7317 
7318  double arg = CONSTMAX_EXP_ARG;
7319 
7320  A1 = (nnVec[i]*npVec[i]-pn);
7321  if (A1 >= exp(arg)) A1 = exp(arg);
7322 
7323  dAdn = (npVec[i]);
7324  dAdp = (nnVec[i]);
7325 
7326  C1 = (tpVec[i]*(nnVec[i]+Ni)+tnVec[i]*(npVec[i]+Ni));
7327  if (C1 >= exp(arg)) C1 = exp(arg);
7328 
7329  B1 = 1.0/C1;
7330  dBdn = -1.0/(C1*C1) * tpVec[i];
7331  dBdp = -1.0/(C1*C1) * tnVec[i];
7332 
7333  dRsrhdn = dAdn * B1 + dBdn * A1;
7334  dRsrhdp = dAdp * B1 + dBdp * A1;
7335 
7336 #ifdef Xyce_DEBUG_DEVICE
7338  {
7339  Xyce::dout() << " dRsrhdn = " << dRsrhdn << " dRsrhdp = " << dRsrhdp;
7340  Xyce::dout() << "\n";
7341  Xyce::dout() << " nnVec = " << nnVec[i] << " npVec = " << npVec[i] << "\n";
7342  Xyce::dout() << " tnVec = " << tnVec[i] << " tpVec = " << tpVec[i] << "\n";
7343  Xyce::dout() << " A1 = " << A1 << " B1 = " << B1 << " C1 = " << C1 << "\n";
7344  Xyce::dout() << " dAdn = " << dAdn << " dBdn = " << dBdn << "\n";
7345  Xyce::dout() << " dAdp = " << dAdp << " dBdp = " << dBdp << "\n";
7346  }
7347 #endif
7348 
7349  // Raug derivatives:
7350  // Raug = A1*B1, where A1 is the same as it was for Rsrch).
7351  B1 = (Cn*nnVec[i]+Cp*npVec[i]);
7352  if (B1 >= exp(arg)) B1 = exp(arg);
7353 
7354  dBdn = Cn;
7355  dBdp = Cp;
7356 
7357  dRaugdn = dAdn*B1 + A1*dBdn;
7358  dRaugdp = dAdp*B1 + A1*dBdp;
7359 
7360 #ifdef Xyce_DEBUG_DEVICE
7362  {
7363  Xyce::dout() << " dRaugdn = " << dRaugdn << " dRaugdp = " << dRaugdp <<"\n";
7364  Xyce::dout() << " A1 = " << A1 << " B1 = " << B1 << "\n";
7365  Xyce::dout() << " dAdn = " << dAdn << " dBdn = " << dBdn <<"\n";
7366  Xyce::dout() << " dAdp = " << dAdp << " dBdp = " << dBdp <<"\n";
7367  }
7368 #endif
7369 
7370  dRdnVec[i] = dRsrhdn + dRaugdn;
7371  dRdpVec[i] = dRsrhdp + dRaugdp;
7372 
7373 #else
7374 
7375  double n = nnVec[i];
7376  double p = npVec[i];
7377  double tn = tnVec[i];
7378  double tp = tpVec[i];
7379 
7380  dRsrhdn = matSupport.pdRsrhN(bulkMaterial,Ni,n,p,tn,tp);
7381  dRsrhdp = matSupport.pdRsrhP(bulkMaterial,Ni,n,p,tn,tp);
7382 
7383  dRaugdn = matSupport.pdRaugN(bulkMaterial,Ni,n,p);
7384  dRaugdp = matSupport.pdRaugP(bulkMaterial,Ni,n,p);
7385 
7386 #endif
7387 
7388  dRdnVec[i] = (dRsrhdn + dRaugdn);
7389  dRdpVec[i] = (dRsrhdp + dRaugdp);
7390 
7391 #ifdef Xyce_DEBUG_DEVICE
7393  {
7394  Xyce::dout() << " dRdnVec["<<i<<"] = " << dRdnVec[i];
7395  Xyce::dout() << " dRdpVec["<<i<<"] = " << dRdpVec[i];
7396  Xyce::dout() << "\n";
7397  }
7398 #endif
7399  }
7400 
7401 #ifdef Xyce_DEBUG_DEVICE
7402  if (getDeviceOptions().debugLevel > 1 && getSolverState().debugTimeFlag)
7403  {
7404  Xyce::dout() << section_divider << std::endl;
7405  }
7406 #endif
7407 
7408  return bsuccess;
7409 }
7410 
7411 //-----------------------------------------------------------------------------
7412 // Function : Instance::calcElectronCurrent
7413 // Purpose :
7414 // Special Notes : This function assumes scaling is on.
7415 // Scope : public
7416 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
7417 // Creation Date : 11/14/01
7418 //-----------------------------------------------------------------------------
7420 {
7421  bool bsuccess = true;
7422 
7423  int i;
7424  Ut = Vt/scalingVars.V0;
7425 
7426  double jnMax = 0.0;
7427 
7428 #ifdef Xyce_DEBUG_DEVICE
7429  int iMaxIndex = 0;
7430  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
7431  {
7432  Xyce::dout() << section_divider << "\n";
7433  Xyce::dout() << "Instance::calcElectronCurrent\n";
7434  Xyce::dout() << "\n";
7435  }
7436 #endif
7437 
7438 
7439  for (i=0; i<numMeshEdges; ++i)
7440  {
7441 
7442 #ifdef Xyce_DEBUG_DEVICE
7443  if (getDeviceOptions().debugLevel > 1 && getSolverState().debugTimeFlag)
7444  {
7445  Xyce::dout() << "\n";
7446  Xyce::dout() << "i="<<i<<"\n";
7447  }
7448 #endif
7449 
7450  mEdge * edgePtr = meshContainerPtr->getEdge(i);
7451 
7452  int inodeA = edgePtr->inodeA;
7453  int inodeB = edgePtr->inodeB;
7454  double elen = edgePtr->elen;
7455 
7456  JnVec[i] = Jn(nnVec[inodeA], nnVec[inodeB], EfieldVec[i],
7457  unE_Vec[i], elen );
7458 
7459  if (jnMax < fabs(JnVec[i]) )
7460  {
7461 #ifdef Xyce_DEBUG_DEVICE
7462  iMaxIndex = i;
7463 #endif
7464  jnMax = fabs(JnVec[i]);
7465  }
7466 
7467 #ifdef Xyce_DEBUG_DEVICE
7468  if (getDeviceOptions().debugLevel > 1 && getSolverState().debugTimeFlag)
7469  {
7470  Xyce::dout() << " J*scalingVars.J0="<<JnVec[i]*scalingVars.J0; Xyce::dout() << "\n";
7471  }
7472 #endif
7473 
7474  }
7475 
7476 #ifdef Xyce_DEBUG_DEVICE
7477  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
7478  {
7479  double jScale = 1.0;
7480  double xScale = 1.0;
7481  if (variablesScaled) { jScale = scalingVars.J0; xScale = scalingVars.x0; }
7482 
7483  Xyce::dout().setf(std::ios::scientific);
7484  Xyce::dout() << " Max Electron current = " << jnMax;
7485  Xyce::dout() << " jScale = " << jScale;
7486  Xyce::dout() << "\n";
7487 
7488  mEdge * edgePtr = meshContainerPtr->getEdge(iMaxIndex);
7489  int inodeA = edgePtr->inodeA;
7490  int inodeB = edgePtr->inodeB;
7491 
7492  Xyce::dout() << " max locations A: (x,y) = (" << xVec[inodeA]*xScale<<", ";
7493  Xyce::dout() << yVec[inodeA]*xScale<<")\n";
7494  Xyce::dout() << " max locations B: (x,y) = (" << xVec[inodeB]*xScale<<", ";
7495  Xyce::dout() << yVec[inodeB]*xScale<<")\n";
7496  Xyce::dout() << " nodes: inodeA = "<<inodeA<<" inodeB = " << inodeB<< std::endl;
7497 
7498  Xyce::dout() << " VVec["<<inodeA<<"] = " << VVec[inodeA];
7499  Xyce::dout() << " VVec["<<inodeB<<"] = " << VVec[inodeB] << std::endl;
7500 
7501  Xyce::dout() << " nnVec["<<inodeA<<"] = " << nnVec[inodeA];
7502  Xyce::dout() << " nnVec["<<inodeB<<"] = " << nnVec[inodeB] << std::endl;
7503 
7504  Xyce::dout() << " elen = " << edgePtr->elen << std::endl;
7505  Xyce::dout() << section_divider << std::endl;
7506 
7507  }
7508 #endif
7509 
7510  return bsuccess;
7511 }
7512 
7513 //-----------------------------------------------------------------------------
7514 // Function : Instance::pdElectronCurrent
7515 // Purpose : This function sets up the arrays of partial derivatives
7516 // associated with electron current.
7517 // Special Notes : This function assumes scaling is on.
7518 // Scope : public
7519 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
7520 // Creation Date : 11/14/01
7521 //-----------------------------------------------------------------------------
7523 {
7524  bool bsuccess = true;
7525 
7526  int i;
7527  Ut = Vt/scalingVars.V0;
7528 
7529 #ifdef Xyce_DEBUG_DEVICE
7530  if (getDeviceOptions().debugLevel > 1 && getSolverState().debugTimeFlag)
7531  {
7532  Xyce::dout() << section_divider << "\n";
7533  Xyce::dout() << "Instance::pdElectronCurrent\n";
7534  Xyce::dout() << "\n";
7535  }
7536 #endif
7537 
7538  for (i=0;i<numMeshEdges;++i)
7539  {
7540 
7541 #ifdef Xyce_DEBUG_DEVICE
7542  if (getDeviceOptions().debugLevel > 2 && getSolverState().debugTimeFlag)
7543  {
7544  Xyce::dout() << subsection_divider <<"\n";
7545  Xyce::dout() << "i="<<i;
7546  }
7547 #endif
7548  mEdge * edgePtr = meshContainerPtr->getEdge(i);
7549 
7550  int inodeA = edgePtr->inodeA;
7551  int inodeB = edgePtr->inodeB;
7552  double elen = edgePtr->elen;
7553 
7554  dJndn1Vec[i] = dJndn1(nnVec[inodeA], nnVec[inodeB], EfieldVec[i],
7555  unE_Vec[i], elen);
7556 
7557  dJndn2Vec[i] = dJndn2(nnVec[inodeA], nnVec[inodeB], EfieldVec[i],
7558  unE_Vec[i], elen);
7559 
7560  dJndV1Vec[i] = dJndV1(nnVec[inodeA], nnVec[inodeB], EfieldVec[i],
7561  unE_Vec[i], elen);
7562 
7563  dJndV2Vec[i] = dJndV2(nnVec[inodeA], nnVec[inodeB], EfieldVec[i],
7564  unE_Vec[i], elen);
7565 
7566 #ifdef Xyce_DEBUG_DEVICE
7567  if (getDeviceOptions().debugLevel > 2 && getSolverState().debugTimeFlag)
7568  {
7569  Xyce::dout() << " dJndn1="<<dJndn1Vec[i];
7570  Xyce::dout() << " dJndn2="<<dJndn2Vec[i];
7571  Xyce::dout() << " dJndV1="<<dJndV1Vec[i];
7572  Xyce::dout() << " dJndV2="<<dJndV2Vec[i];
7573  Xyce::dout() << "\n";
7574  }
7575 #endif
7576  }
7577 
7578 #ifdef Xyce_DEBUG_DEVICE
7579  if (getDeviceOptions().debugLevel > 1 && getSolverState().debugTimeFlag)
7580  {
7581  Xyce::dout() << section_divider << std::endl;
7582  }
7583 #endif
7584 
7585  return bsuccess;
7586 }
7587 
7588 //-----------------------------------------------------------------------------
7589 // Function : Instance::calcHoleCurrent
7590 // Purpose : This function assumes scaling is on.
7591 // Special Notes :
7592 // Scope : public
7593 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
7594 // Creation Date : 11/14/01
7595 //-----------------------------------------------------------------------------
7597 {
7598  bool bsuccess = true;
7599  int i;
7600  Ut = Vt/scalingVars.V0;
7601 
7602  double jpMax = 0.0;
7603 
7604 #ifdef Xyce_DEBUG_DEVICE
7605  int iMaxIndex = 0;
7606  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
7607  {
7608  Xyce::dout() << section_divider << "\n";
7609  Xyce::dout() << "Instance::calcHoleCurrent\n";
7610  Xyce::dout() << "\n";
7611  }
7612 #endif
7613 
7614  for (i=0;i<numMeshEdges;++i)
7615  {
7616 
7617 #ifdef Xyce_DEBUG_DEVICE
7618  if (getDeviceOptions().debugLevel > 1 && getSolverState().debugTimeFlag)
7619  {
7620  Xyce::dout() << "\n";
7621  Xyce::dout() << "i="<<i<<"\n";
7622  }
7623 #endif
7624 
7625  mEdge * edgePtr = meshContainerPtr->getEdge(i);
7626 
7627  int inodeA = edgePtr->inodeA;
7628  int inodeB = edgePtr->inodeB;
7629  double elen = edgePtr->elen;
7630 
7631  JpVec[i] = Jp(npVec[inodeA], npVec[inodeB],
7632  EfieldVec[i], upE_Vec[i], elen);
7633 
7634  if (jpMax < fabs(JpVec[i]) )
7635  {
7636 #ifdef Xyce_DEBUG_DEVICE
7637  iMaxIndex = i;
7638 #endif
7639  jpMax = fabs(JpVec[i]);
7640  }
7641 
7642 #ifdef Xyce_DEBUG_DEVICE
7643  if (getDeviceOptions().debugLevel > 1 && getSolverState().debugTimeFlag)
7644  {
7645  Xyce::dout() << " J*scalingVars.J0="<<JpVec[i]*scalingVars.J0; Xyce::dout() <<"\n";
7646  }
7647 #endif
7648 
7649  }
7650 
7651 #ifdef Xyce_DEBUG_DEVICE
7652  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
7653  {
7654  double jScale = 1.0;
7655  double xScale = 1.0;
7656  if (variablesScaled) { jScale = scalingVars.J0; xScale = scalingVars.x0; }
7657 
7658  Xyce::dout().setf(std::ios::scientific);
7659  Xyce::dout() << " Max Hole current = " << jpMax;
7660  Xyce::dout() << " jScale = " << jScale;
7661  Xyce::dout() << "\n";
7662 
7663  mEdge * edgePtr = meshContainerPtr->getEdge(iMaxIndex);
7664  int inodeA = edgePtr->inodeA;
7665  int inodeB = edgePtr->inodeB;
7666 
7667  Xyce::dout() << " max locations A: (x,y) = (" << xVec[inodeA]*xScale<<", ";
7668  Xyce::dout() << yVec[inodeA]*xScale<<")\n";
7669  Xyce::dout() << " max locations B: (x,y) = (" << xVec[inodeB]*xScale<<", ";
7670  Xyce::dout() << yVec[inodeB]*xScale<<")\n";
7671  Xyce::dout() << " nodes: inodeA = "<<inodeA<<" inodeB = " << inodeB<< std::endl;
7672 
7673  Xyce::dout() << " VVec["<<inodeA<<"] = " << VVec[inodeA];
7674  Xyce::dout() << " VVec["<<inodeB<<"] = " << VVec[inodeB] << std::endl;
7675 
7676  Xyce::dout() << " npVec["<<inodeA<<"] = " << npVec[inodeA];
7677  Xyce::dout() << " npVec["<<inodeB<<"] = " << npVec[inodeB] << std::endl;
7678 
7679  Xyce::dout() << " elen = " << edgePtr->elen << std::endl;
7680  Xyce::dout() << section_divider << std::endl;
7681  }
7682 #endif
7683 
7684  return bsuccess;
7685 }
7686 
7687 //-----------------------------------------------------------------------------
7688 // Function : Instance::pdHoleCurrent
7689 // Purpose : This function sets up the arrays of partial derivatives
7690 // associated with the hole current.
7691 // Special Notes : This function assumes scaling is on.
7692 // Scope : public
7693 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
7694 // Creation Date : 11/14/01
7695 //-----------------------------------------------------------------------------
7697 {
7698  bool bsuccess = true;
7699 
7700  int i;
7701  Ut = Vt/scalingVars.V0;
7702 
7703 #ifdef Xyce_DEBUG_DEVICE
7704  if (getDeviceOptions().debugLevel > 1 && getSolverState().debugTimeFlag)
7705  {
7706  Xyce::dout() << section_divider << "\n";
7707  Xyce::dout() << "Instance::pdHoleCurrent\n";
7708  Xyce::dout() << "\n";
7709  }
7710 #endif
7711 
7712 
7713  for (i=0; i<numMeshEdges; ++i)
7714  {
7715 
7716 #ifdef Xyce_DEBUG_DEVICE
7717  if (getDeviceOptions().debugLevel > 2 && getSolverState().debugTimeFlag)
7718  {
7719  Xyce::dout() << subsection_divider <<"\n";
7720  Xyce::dout() << "i="<<i;
7721  Xyce::dout() << "\n";
7722  }
7723 #endif
7724  mEdge * edgePtr = meshContainerPtr->getEdge(i);
7725 
7726  int inodeA = edgePtr->inodeA;
7727  int inodeB = edgePtr->inodeB;
7728  double elen = edgePtr->elen;
7729 
7730  dJpdn1Vec[i] = dJpdn1(npVec[inodeA], npVec[inodeB],
7731  EfieldVec[i], upE_Vec[i], elen );
7732 
7733  dJpdn2Vec[i] = dJpdn2(npVec[inodeA], npVec[inodeB],
7734  EfieldVec[i], upE_Vec[i], elen );
7735 
7736  dJpdV1Vec[i] = dJpdV1(npVec[inodeA], npVec[inodeB],
7737  EfieldVec[i], upE_Vec[i], elen );
7738 
7739  dJpdV2Vec[i] = dJpdV2(npVec[inodeA], npVec[inodeB],
7740  EfieldVec[i], upE_Vec[i], elen );
7741 
7742 #ifdef Xyce_DEBUG_DEVICE
7743  if (getDeviceOptions().debugLevel > 2 && getSolverState().debugTimeFlag)
7744  {
7745  Xyce::dout() << " dJpdn1="<<dJpdn1Vec[i];
7746  Xyce::dout() << " dJpdn2="<<dJpdn2Vec[i];
7747  Xyce::dout() << " dJpdV1="<<dJpdV1Vec[i];
7748  Xyce::dout() << " dJpdV2="<<dJpdV2Vec[i];
7749  Xyce::dout() << "\n" << "\n";
7750  }
7751 #endif
7752 
7753  }
7754 
7755 #ifdef Xyce_DEBUG_DEVICE
7756  if (getDeviceOptions().debugLevel > 1 && getSolverState().debugTimeFlag)
7757  {
7758  Xyce::dout() << section_divider << std::endl;
7759  }
7760 #endif
7761 
7762  return bsuccess;
7763 }
7764 
7765 //-----------------------------------------------------------------------------
7766 // Function : Instance::calcEfield
7767 // Purpose : This function works with or without scaling.
7768 //
7769 // Special Notes : For all "edge" defined vector variables, the gradients
7770 // are based on (nodeB-nodeA).
7771 //
7772 // When used in the the box integration algorithm, the
7773 // "local" node is always node A, and the neighbor node is
7774 // node B.
7775 //
7776 // Scope : public
7777 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
7778 // Creation Date : 11/14/01
7779 //-----------------------------------------------------------------------------
7781 {
7782  bool bsuccess = true;
7783  int i;
7784 
7785 #ifdef Xyce_DEBUG_DEVICE
7786  int iMaxIndex = 0;
7787 
7788  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
7789  {
7790  Xyce::dout() << section_divider << "\n";
7791  Xyce::dout() << "Instance::calcEfield\n";
7792  Xyce::dout() << "\n";
7793  }
7794 #endif
7795 
7796  double absEfield;
7797  Emax = 0.0;
7798 
7799  for (i=0;i<numMeshEdges;++i)
7800  {
7801  mEdge * edgePtr = meshContainerPtr->getEdge(i);
7802 
7803  int inodeA = edgePtr->inodeA;
7804  int inodeB = edgePtr->inodeB;
7805  double elen = edgePtr->elen;
7806 
7807  EfieldVec[i] = -(VVec[inodeB] - VVec[inodeA])/elen;
7808 
7809 #ifdef Xyce_DEBUG_DEVICE
7811  {
7812  Xyce::dout() << " VV[A] = " << VVec[inodeA];
7813  Xyce::dout() << " VV[B] = " << VVec[inodeB];
7814  Xyce::dout() << " elen = " << elen;
7815 
7816  Xyce::dout() << " EfieldVec["<<i<<"] = " << EfieldVec[i];
7817  Xyce::dout() << " E*scalingVars.E0 = " << EfieldVec[i]*scalingVars.E0;
7818  Xyce::dout() << "\n";
7819  }
7820 #endif
7821  if (elen <= 0.00)
7822  {
7823  Xyce::dout() << " edge = " << i;
7824  Xyce::dout() << " elen = " << elen;
7825  Xyce::dout() << " elen less than zero. Exiting" << std::endl;
7826  exit(0);
7827  }
7828 
7829  absEfield = fabs(EfieldVec[i]);
7830  if (absEfield > Emax)
7831  {
7832  Emax = absEfield;
7833 #ifdef Xyce_DEBUG_DEVICE
7834  iMaxIndex = i;
7835 #endif
7836  }
7837  }
7838 
7839  double eScale;
7840  if (variablesScaled) { eScale = scalingVars.E0; }
7841  else { eScale = 1.0; }
7842 
7843  Emax *= eScale;
7844 
7845 #ifdef Xyce_DEBUG_DEVICE
7846  double xScale;
7847  if (variablesScaled) { xScale = scalingVars.x0; }
7848  else { xScale = 1.0; }
7849 
7850  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
7851  {
7852  Xyce::dout().setf(std::ios::scientific);
7853  Xyce::dout() << " Max Efield = " << Emax;
7854  Xyce::dout() << " eScale = " << eScale;
7855  Xyce::dout() << "\n";
7856 
7857  mEdge * edgePtr = meshContainerPtr->getEdge(iMaxIndex);
7858  int inodeA = edgePtr->inodeA;
7859  int inodeB = edgePtr->inodeB;
7860 
7861  Xyce::dout() << " max locations A: (x,y) = (" << xVec[inodeA]*xScale<<", ";
7862  Xyce::dout() << yVec[inodeA]*xScale<<")\n";
7863  Xyce::dout() << " max locations B: (x,y) = (" << xVec[inodeB]*xScale<<", ";
7864  Xyce::dout() << yVec[inodeB]*xScale<<")\n";
7865  Xyce::dout() << " nodes: inodeA = "<<inodeA<<" inodeB = " << inodeB<< std::endl;
7866  Xyce::dout() << " VVec["<<inodeA<<"] = " << VVec[inodeA];
7867  Xyce::dout() << " VVec["<<inodeB<<"] = " << VVec[inodeB] << std::endl;
7868  Xyce::dout() << " elen = " << edgePtr->elen << std::endl;
7869  Xyce::dout() << section_divider << std::endl;
7870  }
7871 #endif
7872 
7873  return bsuccess;
7874 }
7875 
7876 //-----------------------------------------------------------------------------
7877 // Function : Instance::enablePDEContinuation
7878 // Purpose : Sets up the various parameters neccessary for a continuation
7879 // calculation. Mainly, it sets up the voltage step size
7880 // for all the voltage BC's.
7881 //
7882 // Special Notes : This function is called before a Newton loop, or set of
7883 // Newton loops is called. Thus, to have the correct Vckt,
7884 // it is neccessary obtain it from the solution vector
7885 // directly.
7886 //
7887 //
7888 // Scope : public
7889 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
7890 // Creation Date : 10/22/02
7891 //-----------------------------------------------------------------------------
7893 {
7894  bool bnoChange = true;
7895 
7896 #ifdef Xyce_DEBUG_DEVICE
7897  if (getDeviceOptions().debugLevel > -5 && getSolverState().debugTimeFlag)
7898  {
7899  Xyce::dout() << section_divider << "\n";
7900  Xyce::dout() << "Instance::enableContinuation. " << outputName;
7901  Xyce::dout() << std::endl;
7902  }
7903 #endif
7904 
7905  continuationAlpha = 1.0;
7906 
7907  std::vector<DeviceInterfaceNode>::iterator firstDI = dIVec.begin();
7908  std::vector<DeviceInterfaceNode>::iterator lastDI = dIVec.end ();
7909  std::vector<DeviceInterfaceNode>::iterator iterDI;
7910 
7911  // Save the old value, if this function has never been called before.
7912  // The old value really needs to represent what Vckt was the last time
7913  // the PDE problem was solved. The external circuit, which supplies
7914  // Vckt, may have changed a great deal in between PDE solves, when
7915  // running 2level Newton. For that reason, Vckt_old is saved
7916  // at the end of the previous continuation, if there was one.
7918  {
7919  for (iterDI=firstDI;iterDI!=lastDI;++iterDI)
7920  {
7921  iterDI->Vckt_old = iterDI->Vckt;
7922  }
7923  }
7924 
7925  obtainNodeVoltages ();
7926 
7927  for (iterDI=firstDI;iterDI!=lastDI;++iterDI)
7928  {
7929  iterDI->Vckt_final = iterDI->Vckt;
7930  iterDI->Vckt_orig = iterDI->Vckt;
7931  }
7932 
7933  // This (voltlim) is a very new thing. Use carefully...
7934  if (getDeviceOptions().voltageLimiterFlag && voltLimFlag)
7935  {
7937  }
7938 
7939  for (iterDI=firstDI;iterDI!=lastDI;++iterDI)
7940  {
7941  double dV,tmp1V, tmp2V;
7942  tmp1V = iterDI->Vckt_final;
7943  tmp2V = iterDI->Vckt_old;
7944  dV = tmp1V - tmp2V;
7945 
7946  iterDI->Vckt_delta = dV;
7947 
7948  iterDI->Vckt_deltaC = dV/
7949  (static_cast<double>(getSolverState().maxPDEContinuationSteps));
7950 
7951  // if this deltaC is too big, then we need to change the
7952  // number of continuation steps.
7953  double maxDelta = maxVoltDelta;
7954 
7955  if (fabs(iterDI->Vckt_deltaC) > maxDelta)
7956  {
7957  int tmp_steps = static_cast<int>(fabs(dV)/maxDelta) + 1;
7959 
7960  iterDI->Vckt_deltaC = dV/
7961  static_cast<double>(getSolverState().maxPDEContinuationSteps);
7962  }
7963 
7964  if (fabs(dV) > 1.0e-3) bnoChange = false;
7965 
7966 #ifdef Xyce_DEBUG_DEVICE
7968  {
7969  Xyce::dout() << std::endl;
7970  Xyce::dout() << outputName << " ";
7971  Xyce::dout().width(10);
7972  Xyce::dout() << iterDI->eName;
7973  Xyce::dout().width(10); Xyce::dout().precision(2);
7974  Xyce::dout() << ": dV = " << dV;
7975  Xyce::dout() << " Vckt_final = " << iterDI->Vckt_final;
7976  Xyce::dout() << " Vckt_old = " << iterDI->Vckt_old << std::endl;
7977  Xyce::dout() << " delta = " << iterDI->Vckt_delta;
7978  Xyce::dout() << " deltaC = " << iterDI->Vckt_deltaC;
7979  Xyce::dout() << " steps = " << getSolverState().maxPDEContinuationSteps;
7980  Xyce::dout() << std::endl;
7981  }
7982 #endif
7983 
7984  iterDI->Vckt_ramp = iterDI->Vckt_old;
7985  iterDI->Vckt_ramp_old = iterDI->Vckt_old;
7986  }
7987 
7988  // now set up any continuation params associated with photogen.
7989  bool bnoChangePhotogen = true;
7990 
7991  if (photogenOnFlag)
7992  {
7993  std::string msg = "Instance::enablePDEContinuation: PhotogenContinuation not supported";
7994  N_ERH_ErrorMgr::report ( N_ERH_ErrorMgr::DEV_FATAL,msg);
7995  }
7996 
7997  bnoChange = bnoChange && bnoChangePhotogen;
7998 
7999 #ifdef Xyce_DEBUG_DEVICE
8000  if (getDeviceOptions().debugLevel > -5 && getSolverState().debugTimeFlag)
8001  {
8002  if (bnoChange) Xyce::dout() << "bnoChange is TRUE" << std::endl;
8003  else Xyce::dout() << "bnoChange is FALSE" << std::endl;
8004  }
8005 #endif
8006 
8008 
8009 #ifdef Xyce_DEBUG_DEVICE
8010  if (getDeviceOptions().debugLevel > -5 && getSolverState().debugTimeFlag)
8011  {
8012  Xyce::dout() << section_divider << std::endl;
8013  }
8014 #endif
8015 
8016  // if none of the boundary conditions have changed, then
8017  // return a false.
8018  return (!bnoChange);
8019 }
8020 
8021 //-----------------------------------------------------------------------------
8022 // Function : Instance::disablePDEContinuation
8023 //
8024 // Purpose : This function mostly sets up the "old" values of Vckt
8025 // and A1, so that they are correct for the next time the
8026 // continuation loop is enabled.
8027 //
8028 // Special Notes :
8029 // Scope : public
8030 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
8031 // Creation Date : 10/22/02
8032 //-----------------------------------------------------------------------------
8034 {
8035  std::vector<DeviceInterfaceNode>::iterator firstDI = dIVec.begin();
8036  std::vector<DeviceInterfaceNode>::iterator lastDI = dIVec.end ();
8037  std::vector<DeviceInterfaceNode>::iterator iterDI;
8038 
8039  for (iterDI=firstDI;iterDI!=lastDI;++iterDI)
8040  {
8041  iterDI->Vckt_old = iterDI->Vckt_final;
8042  }
8043 
8045 
8046  return true;
8047 }
8048 
8049 //-----------------------------------------------------------------------------
8050 // Function : Instance::setPDEContinuationAlpha
8051 // Purpose :
8052 // Special Notes :
8053 // Scope : public
8054 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
8055 // Creation Date : 07/18/03
8056 //-----------------------------------------------------------------------------
8058 {
8059 #ifdef Xyce_DEBUG_DEVICE
8060  Xyce::dout() << section_divider << std::endl;
8061  Xyce::dout() << "Instance::setPDEContinuationAlpha" << std::endl;
8062 #endif
8063 
8064 
8065  // First do the voltage boundary conditions:
8066  std::vector<DeviceInterfaceNode>::iterator firstDI = dIVec.begin();
8067  std::vector<DeviceInterfaceNode>::iterator lastDI = dIVec.end ();
8068  std::vector<DeviceInterfaceNode>::iterator iterDI;
8069 
8070  for (iterDI=firstDI;iterDI!=lastDI;++iterDI)
8071  {
8072  iterDI->Vckt_ramp = iterDI->Vckt_old + (iterDI->Vckt_delta)*alpha;
8073 
8074  // make sure we haven't gone too far:
8075  if ((iterDI->Vckt_delta > 0 && iterDI->Vckt_ramp > iterDI->Vckt_final) ||
8076  (iterDI->Vckt_delta <= 0 && iterDI->Vckt_ramp <= iterDI->Vckt_final) )
8077  {
8078  iterDI->Vckt_ramp = iterDI->Vckt_final;
8079 
8080 #ifdef Xyce_NEW_PDE_CONTINUATION
8081  // this line allows us to remove "disablePDEContinuation".
8082  iterDI->Vckt_old = iterDI->Vckt_final;
8083 #endif
8084  }
8085 
8086 #ifdef Xyce_DEBUG_DEVICE
8087  if (getDeviceOptions().debugLevel > 0)
8088  {
8089  Xyce::dout() << outputName << " ";
8090  Xyce::dout().width(10);
8091  Xyce::dout() <<iterDI->eName;
8092  Xyce::dout() << "\tVckt_ramp = " << iterDI->Vckt_ramp;
8093  Xyce::dout() << "\tVckt_old = " << iterDI->Vckt_old;
8094  Xyce::dout() << "\talpha = " << alpha;
8095  Xyce::dout() << std::endl;
8096  }
8097 #endif
8098  }
8099 
8100  // now do the photogeneration term, if neccessary:
8102 
8103  // make sure we haven't gone too far:
8104  if ((photoA1_Delta > 0 && photoA1_ramp > photoA1_final) ||
8106  {
8108 
8109 #ifdef Xyce_NEW_PDE_CONTINUATION
8110  // this line enables removal of function disablePDEContinuation.
8112 #endif
8113  }
8114 
8115 #ifdef Xyce_DEBUG_DEVICE
8116  Xyce::dout() << " photoA1_ramp = " << photoA1_ramp << std::endl;
8117  Xyce::dout() << section_divider << std::endl;
8118 #endif
8119 
8120 }
8121 
8122 //-----------------------------------------------------------------------------
8123 // Function : Instance::setPDEContinuationBeta
8124 // Purpose :
8125 // Special Notes :
8126 // Scope : public
8127 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
8128 // Creation Date : 07/18/03
8129 //-----------------------------------------------------------------------------
8131 {
8132 #ifdef Xyce_DEBUG_DEVICE
8133  Xyce::dout() << section_divider << std::endl;
8134  Xyce::dout() << "Instance::setPDEContinuationBeta" << std::endl;
8135 #endif
8136 
8137  // First do the voltage boundary conditions:
8138  std::vector<DeviceInterfaceNode>::iterator firstDI = dIVec.begin();
8139  std::vector<DeviceInterfaceNode>::iterator lastDI = dIVec.end ();
8140  std::vector<DeviceInterfaceNode>::iterator iterDI;
8141 
8142  for (iterDI=firstDI;iterDI!=lastDI;++iterDI)
8143  {
8144  iterDI->Vckt_ramp = iterDI->Vckt*beta;
8145 #ifdef Xyce_DEBUG_DEVICE
8146  Xyce::dout() << " " << iterDI->eName << " Vckt_ramp = " << iterDI->Vckt_ramp << std::endl;
8147 #endif
8148  }
8149 }
8150 
8151 Device *Traits::factory(const Configuration &configuration, const FactoryBlock &factory_block)
8152 {
8153 
8154  Device *device = new DeviceMaster<Traits>(configuration, factory_block, factory_block.solverState_, factory_block.deviceOptions_);
8155 
8156  return device;
8157 }
8158 
8160 {
8162  .registerDevice("pde", 2)
8163  .registerModelType("pde", 2)
8164  .registerModelType("zod", 2);
8165 }
8166 
8167 } // namespace TwoDPDE
8168 } // namespace Device
8169 } // namespace Xyce