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