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