Xyce  6.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
N_DEV_DiodePDEInstance.C
Go to the documentation of this file.
1 //-----------------------------------------------------------------------------
2 // Copyright Notice
3 //
4 // Copyright 2002 Sandia Corporation. Under the terms
5 // of Contract DE-AC04-94AL85000 with Sandia Corporation, the U.S.
6 // Government retains certain rights in this software.
7 //
8 // Xyce(TM) Parallel Electrical Simulator
9 // Copyright (C) 2002-2014 Sandia Corporation
10 //
11 // This program is free software: you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation, either version 3 of the License, or
14 // (at your option) any later version.
15 //
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with this program. If not, see <http://www.gnu.org/licenses/>.
23 //
24 //-----------------------------------------------------------------------------
25 
26 //-------------------------------------------------------------------------
27 // Filename : $RCSfile: N_DEV_DiodePDEInstance.C,v $
28 //
29 // Purpose : One dimensional PDE device, instance class
30 // implementation.
31 //
32 // Special Notes :
33 //
34 // Creator : Eric R. Keiter, SNL, Parallel Computational Sciences
35 //
36 // Creation Date : 07/06/03
37 //
38 // Revision Information:
39 // ---------------------
40 //
41 // Revision Number: $Revision: 1.130 $
42 //
43 // Revision Date : $Date: 2014/08/07 23:08:53 $
44 //
45 // Current Owner : $Author: dgbaur $
46 //-------------------------------------------------------------------------
47 #include <Xyce_config.h>
48 
49 // ---------- Standard Includes ----------
50 #include <N_UTL_Misc.h>
51 #ifdef Xyce_DEBUG_DEVICE
52 #include <iostream>
53 #endif
54 
55 #ifdef HAVE_CMATH
56 #include <cmath>
57 #else
58 #include <math.h>
59 #endif
60 
61 #ifdef HAVE_CSTDIO
62 #include <cstdio>
63 #else
64 #include <stdio.h>
65 #endif
66 
67 #include <N_DEV_fwd.h>
68 
69 #include <N_DEV_DeviceOptions.h>
70 #include <N_DEV_DeviceMaster.h>
71 #include <N_DEV_DiodePDE.h>
72 #include <N_DEV_ExternData.h>
73 #include <N_DEV_MatrixLoadData.h>
74 #include <N_DEV_RegionData.h>
75 #include <N_DEV_SolverState.h>
76 #include <N_DEV_Message.h>
77 
78 #include <N_LAS_Vector.h>
79 #include <N_LAS_Matrix.h>
80 #include <N_LAS_System.h>
81 #include <N_LAS_Builder.h>
82 
83 namespace Xyce {
84 namespace Device {
85 namespace DiodePDE {
86 
87 // default number of mesh points:
88 static const int NUM_MESH_POINTS = 11;
89 
90 // default maximum number of nonzero entries in a matrix row
91 static const int MAX_COLS_PER_ROW = 40;
92 
93 
95 {
96  p.addPar("ANODE.BC", 0.5, &DiodePDE::Instance::anodebc);
97  p.addPar("CATHODE.BC", 0.0, &DiodePDE::Instance::cathodebc);
98  p.addPar("EMITTER.BC", 0.5, &DiodePDE::Instance::emitterbc);
99  p.addPar("COLLECTOR.BC", 0.0, &DiodePDE::Instance::collectorbc);
100  p.addPar("BASE.BC", 0.0, &DiodePDE::Instance::basebc);
101  p.addPar("BASE.LOC", 0.5e-3, &DiodePDE::Instance::baseLocation)
102  .setGivenMember(&DiodePDE::Instance::baseLocationGiven);
103  p.addPar("AREA", 1.0, &DiodePDE::Instance::area);
104 
105  // user-specified scaling vars:
106  p.addPar("X0", 1.0e-7, &DiodePDE::Instance::x0_user)
107  .setExpressionAccess(ParameterType::TIME_DEP);
108  p.addPar("C0", 1.0e+15, &DiodePDE::Instance::C0_user)
109  .setExpressionAccess(ParameterType::TIME_DEP)
110  .setDescription("Density scalar; adjust to mitigate convergence problems.");
111  p.addPar("t0", 1.0e-6, &DiodePDE::Instance::t0_user)
112  .setExpressionAccess(ParameterType::TIME_DEP);
113  p.addPar("SCALEDENSITYTOMAXDOPING", true, &DiodePDE::Instance::scaleDensityToMaxDoping_)
114  .setUnit(U_LOGIC)
115  .setDescription("If set the density will be scaled by a fraction of the maximum doping.");
116  p.addPar("DENSITYSCALARFRACTION", 1.0e-1, &DiodePDE::Instance::densityScalarFraction_)
117  .setUnit(U_LOGIC)
118  .setDescription("Fraction of the maximum doping by which density will be scaled.");
119  p.addPar("NA", 1.0e+15, &DiodePDE::Instance::Na);
120  p.addPar("ND", 1.0e+15, &DiodePDE::Instance::Nd);
121  p.addPar("WJ", 1.0e-4, &DiodePDE::Instance::WJ);
122  p.addPar("TEMP", 300.15, &DiodePDE::Instance::Temp)
123  .setDescription("Temperature");
124  p.addPar("ANODE.AREA", 0.0, &DiodePDE::Instance::anodeArea);
125  p.addPar("EMITTER.AREA", 0.0, &DiodePDE::Instance::emitterArea);
126  p.addPar("CATHODE.AREA", 0.0, &DiodePDE::Instance::cathodeArea);
127  p.addPar("COLLECTOR.AREA", 0.0, &DiodePDE::Instance::collectorArea);
128  p.addPar("BASE.AREA", 0.0, &DiodePDE::Instance::baseArea);
129  p.addPar("L", 1.0e-3, &DiodePDE::Instance::length)
130  .setGivenMember(&DiodePDE::Instance::lengthGiven)
131  .setDescription("Device width.");
132  p.addPar("W", 1.0e-3, &DiodePDE::Instance::width)
133  .setGivenMember(&DiodePDE::Instance::widthGiven)
134  .setDescription("Device width.");
135  p.addPar("MAXVOLTDELTA", 0.025, &DiodePDE::Instance::maxVoltDelta);
136  p.addPar("OUTPUTINTERVAL", 0.0, &DiodePDE::Instance::outputInterval)
138  .setDescription("Time interval for tecplot output (if enabled).");
139  p.addPar("basex", 0.5e-3, &DiodePDE::Instance::basex);
140 
141 #ifdef Xyce_DEBUG_DEVICE
142  p.addPar("ANODEINDEX", 1, &DiodePDE::Instance::anodeIndex_user)
143  .setGivenMember(&DiodePDE::Instance::anodeIndex_userGiven);
144  p.addPar("CATHODEINDEX", 0, &DiodePDE::Instance::cathodeIndex_user)
145  .setGivenMember(&DiodePDE::Instance::cathodeIndex_userGiven);
146 #endif
147 
148  // Neutron stuff:
149  p.addPar("JUNCTIONAREA", 1.0e-4, &DiodePDE::Instance::junctionArea)
150  .setDescription("Cross sectional area used to compute recombination integral.");
151 
152  // Set up map for non-double precision variables:
153  p.addPar("GRADED", false, &DiodePDE::Instance::gradedJunctionFlag);
154  p.addPar("BJTENABLE", false, &DiodePDE::Instance::bjtEnableFlag);
155  p.addPar("MOBMODEL", std::string("ARORA"), &DiodePDE::Instance::mobModelName);
158  .setUnit(U_LOGIC)
159  .setDescription("If true, use field dependent mobility.");
160  p.addPar("BULKMATERIAL", std::string("SI"), &DiodePDE::Instance::bulkMaterial);
161  p.addPar("OFFSETOUTPUTVOLTAGE", true, &DiodePDE::Instance::useVoltageOutputOffset_);
162  p.addPar("FIRSTELECTRODEOFFSET", false, &DiodePDE::Instance::offsetWithFirstElectrode_);
163  p.addPar("DISPLCUR", false, &DiodePDE::Instance::displCurrentFlag);
164  p.addPar("OUTPUTNLPOISSON", false, &DiodePDE::Instance::outputNLPoisson);
165  p.addPar("OUTPUTREGION", 1, &DiodePDE::Instance::outputRegion);
166  p.addPar("MASKVARSTIA", false, &DiodePDE::Instance::maskVarsTIAFlag_)
167  .setUnit(U_LOGIC)
168  .setDescription("If set to true, then some variables are excluded from the time integration error control calculation.");
170  .setUnit(U_LOGIC)
171  .setDescription("Flag to turn on/off Auger recombination");
173  .setUnit(U_LOGIC)
174  .setDescription("Flag to turn on/off Shockley-Reed-Hall recombination.");
175 
176  p.addPar ("FERMIDIRAC", false, false, NO_DEP, &DiodePDE::Instance::fermiDiracFlag,
177  NULL, U_NONE, CAT_UNKNOWN, "Use Fermi-Dirac statistics.");
178  p.addPar("THERMIONICEMISSION", false, &DiodePDE::Instance::thermionicEmissionFlag);
179  p.addPar("TUNNELING", std::string("none"), &DiodePDE::Instance::tunnelingModelName);
180 
181  p.addPar("GNUPLOTLEVEL", 1, &DiodePDE::Instance::gnuplotLevel);
182  p.addPar("TECPLOTLEVEL", 1, &DiodePDE::Instance::tecplotLevel);
183  p.addPar("SGPLOTLEVEL", 0, &DiodePDE::Instance::sgplotLevel);
184  p.addPar("VOLTLIM", false, &DiodePDE::Instance::voltLimFlag);
185  p.addPar("MESHFILE", std::string("internal.msh"), &DiodePDE::Instance::meshFileName);
186 
187 // Doping file params:
188  p.addPar("DOPING_FILE", std::string("NOFILE"), &DiodePDE::Instance::dopingFileName)
189  .setDescription("File containing doping profile.");
190  p.addPar("PDOPE_FILE", std::string("NOFILE"), &DiodePDE::Instance::pdopeFileName);
191  p.addPar("NDOPE_FILE", std::string("NOFILE"), &DiodePDE::Instance::ndopeFileName);
192  p.addPar("NX", 11, &DiodePDE::Instance::NX)
193  .setGivenMember(&DiodePDE::Instance::NXGiven)
194  .setDescription("Number of mesh points (if a single material).");
195 
196  p.addPar("DIRICHLETBC", false, &DiodePDE::Instance::dirichletBCFlag)
197  .setUnit(U_LOGIC)
198  .setDescription("Flag for using Dirichlet boundary conditions for defects.");
199  p.addPar("USEOLDNI", false, &DiodePDE::Instance::useOldNi)
200  .setGivenMember(&DiodePDE::Instance::useOldNiGiven)
201  .setDescription("Flag for using old(inaccurate) intrinsic carrier calculation.");
202 
206 
208 }
209 
210 
211 //-----------------------------------------------------------------------------
212 // Function : Instance::processParams
213 //
214 // Purpose : This function contains much of the initialization for
215 // the Instance class. Most of this was
216 // originally in the constructor.
217 //
218 // Special Notes :
219 //
220 //
221 // Scope : public
222 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
223 // Creation Date : 6/03/02
224 //-----------------------------------------------------------------------------
226 {
228  return true;
229 }
230 
231 //-----------------------------------------------------------------------------
232 // Function : Instance::Instance
233 // Purpose : instance block constructor
234 // Special Notes :
235 // Scope : public
236 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
237 // Creation Date : 6/29/00
238 //-----------------------------------------------------------------------------
240  const Configuration & configuration,
241  const InstanceBlock & IB,
242  Model & model,
243  const FactoryBlock &factory_block)
244  : DevicePDEInstance(IB, configuration.getInstanceParameters(), factory_block),
245  model_(model),
246  NX(NUM_MESH_POINTS),
247  LX(NX-1),
248  NXGiven(false),
249  maxColsPerRow(MAX_COLS_PER_ROW),
250  numElectrodes(2),
251  NUMRC(NX*3),
252  indicesSetup_(false),
253  includeBaseNode_(false),
254  useElectrodeSpec_(false),
255  maskVarsTIAFlag_(false),
256  scaleDensityToMaxDoping_(true),
257  densityScalarFraction_(1.0e-1),
258  useVoltageOutputOffset_(true),
259  offsetWithFirstElectrode_(false),
260  VoltageOffset_(0.0),
261  useLayerCompositeDoping_(false),
262  Na(1.0e15),
263  Nd(1.0e15),
264  NnMax(1.0e15),
265  NpMax(1.0e15),
266  NnMin(1.0e5), // approx...
267  NpMin(1.0e5),
268  WJ(1.0e-4),
269  XC(0.0),
270  XL(0.0),
271  XR(0.0),
272  Emax(0.0),
273  VminExp(0.0),
274  VmaxExp(0.0),
275  diodeCap(0.0),
276  junctionArea(1.0e-5),
277  meshFileName(""),
278  useOldNi(false),
279  useOldNiGiven(false),
280  dopingFileName("NOFILE"),
281  ndopeFileName("NOFILE"),
282  pdopeFileName("NOFILE"),
283  width(1.0e-3),
284  length(1.0e-3),
285  widthGiven(false),
286  lengthGiven(false),
287  basex(0.5e-3),
288  area(1.0),
289  anodebc(0.0),
290  cathodebc(0.0),
291 
292  emitterbc(0.0),
293  collectorbc(0.0),
294  basebc(0.0),
295 
296  anodeArea(0.0),
297  cathodeArea(0.0),
298 
299  emitterArea(0.0),
300  collectorArea(0.0),
301  baseArea(0.0),
302 
303  baseLocation(0.5e-3),
304  baseLocationGiven(false),
305 
306  gradedJunctionFlag(false),
307  bjtEnableFlag(false),
308  displCurrentFlag(false),
309  calledBeforeUIVB(false),
310  callsOTEC(0),
311  callsOSG(0),
312  equationSet(0),
313  lastOutputTime(-10.0),
314  outputInterval(0.0),
315  outputIntervalGiven(false),
316  outputIndex(0),
317  outputNLPoisson(false),
318  outputRegion(0),
319  tecplotLevel(0),
320  sgplotLevel(0),
321  voltLimFlag(false),
322  includeAugerRecomb(true),
323  includeSRHRecomb(true),
324  fermiDiracFlag(false),
325  thermionicEmissionFlag(false),
326  tunnelingModelName("none"),
327 
328 #ifdef Xyce_DEBUG_DEVICE
329  anodeIndex_user(1),
330  anodeIndex_userGiven(false),
331  cathodeIndex_user(0),
332  cathodeIndex_userGiven(false),
333 #endif
334 
335  maxVoltDelta(0.025), // thermal voltage.
336  enableContinuationCalled(false),
337  dirichletBCFlag(false),
338  columnReorderingFlag(false),
339  layerCompositeSpecified(false)
340 {
341  bcVec.clear();
342 
343  // these 4 mesh things change later.
345  numExtVars = 2;
346  if (IB.numExtVars != 0)
347  {
348  numExtVars = IB.numExtVars;
349  }
350  numStateVars = 2;
351 
352  if (numExtVars < 3)
353  {
354  includeBaseNode_ = false;
355  }
356  else if (numExtVars == 3)
357  {
358  includeBaseNode_ = true;
359  }
360  else if (numExtVars > 3)
361  {
362  UserFatal(*this) << "Too many external nodes are set! Set no more than 3.";
363  }
364 
365  // Set params to constant default values:
366  setDefaultParams ();
367 
368  // Set params according to instance line and constant defaults from metadata:
369  setParams (IB.params);
370 
371  // check doping files...
372  if ( given("PDOPE_FILE") && !given("NDOPE_FILE") )
373  {
374  std::string msg = "Ndope file specified with no Pdope file. Exiting.";
375  N_ERH_ErrorMgr::report(N_ERH_ErrorMgr::DEV_FATAL, msg);
376  }
377 
378  if ( !given("PDOPE_FILE") && given("NDOPE_FILE") )
379  {
380  std::string msg = "Pdope file specified with no Ndope file. Exiting.";
381  N_ERH_ErrorMgr::report(N_ERH_ErrorMgr::DEV_FATAL, msg);
382  }
383 
384  // Set any non-constant parameter defaults:
385  if (!given("TEMP"))
386  Temp = getDeviceOptions().temp.getImmutableValue<double>();
387 
388  if (given("MESHFILE"))
389  {
390  std::string msg = "Instance constructor."
391  "mesh file was specified. The 1D device doesn't need a mesh file."
392  " Either add a model statement of level=2, or get rid of the mesh"
393  " file specification.";
394  N_ERH_ErrorMgr::report( N_ERH_ErrorMgr::DEV_FATAL_0,msg);
395  }
396 
397  if (lengthGiven && !widthGiven)
398  {
399  width = length;
400  }
401 
402  if (given("GNUPLOTLEVEL") && !given("TECPLOTLEVEL"))
403  {
405  }
406 
407  // Calculate any parameters specified as expressions:
409 
410  processParams ();
411 
412  // calculate dependent (ie computed) params and check for errors:
413  ExtendedString tmpName = mobModelName;
414  tmpName.toLower();
415  mobModelName = tmpName;
416 
417  bool bsuccess = true;
418  bool bs1 = true;
419 
420  bs1 = setupDefaultLayer (); bsuccess = bsuccess && bs1;
421  bs1 = setupNumVars (); bsuccess = bsuccess && bs1;
422  bs1 = doAllocations (); bsuccess = bsuccess && bs1;
423  bs1 = setupMesh (); bsuccess = bsuccess && bs1;
424  bs1 = setupMaterialArrays ();bsuccess = bsuccess && bs1;
425  bs1 = setupNodes (); bsuccess = bsuccess && bs1;
426  bs1 = setupDopingProfile (); bsuccess = bsuccess && bs1;
427  bs1 = setupMiscConstants (); bsuccess = bsuccess && bs1;
428  bs1 = setupScalingVars (); bsuccess = bsuccess && bs1;
429 
430  bs1 = setupJacStamp (); bsuccess = bsuccess && bs1;
431  bs1 = cleanupJacStamp (); bsuccess = bsuccess && bs1;
432 
433  if (!given("AREA")) area = 1.0;
434 }
435 
436 //-----------------------------------------------------------------------------
437 // Function : Instance::~Instance
438 // Purpose : destructor
439 // Special Notes :
440 // Scope : public
441 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
442 // Creation Date : 6/29/00
443 //-----------------------------------------------------------------------------
445 {
446  for (std::map<std::string, DopeInfo *>::iterator it = dopeInfoMap.begin();
447  it != dopeInfoMap.end(); ++it)
448  {
449  delete (*it).second;
450  }
451 
452  for (std::map<std::string, PDE_1DElectrode *>::iterator it = electrodeMap.begin();
453  it != electrodeMap.end(); ++it)
454  {
455  delete (*it).second;
456  }
457 
458  int size = materialVec.size();
459  for (int i=0;i<size;++i)
460  {
461  MaterialLayer *matPtr = materialVec[i];
462  if (matPtr != 0)
463  {
464  delete matPtr;
465  materialVec[i] = 0;
466  }
467  }
468  materialVec.clear();
469 }
470 
471 //-----------------------------------------------------------------------------
472 // Function : Instance::constructComposite
473 // Purpose :
474 // Special Notes :
475 // Scope : public
476 // Creator : Dave Shirley, PSSI
477 // Creation Date : 05/14/05
478 //-----------------------------------------------------------------------------
480 Instance::constructComposite(const std::string & compositeName, const std::string & paramName)
481 {
482  if (compositeName == "DOPINGPROFILES" || compositeName == "REGION")
483  {
484  DopeInfo *n = new DopeInfo();
485  dopeInfoMap[paramName] = n;
486  return static_cast<CompositeParam *> (n);
487  }
488  else if (compositeName == "NODE" || compositeName == "ELECTRODE")
489  {
490  bcData bc;
491  ExtendedString electrodeName = paramName;
492  electrodeName.toUpper ();
493 
494  bc.eName = electrodeName;
495  bc.nName = paramName;
496  bc.given = true;
497  bc.index = 0;
498 
499  if (electrodeName =="ANODE")
500  {
501  bc.meshIndex = 0;
502  bc.neighborNode = 1;
503  }
504  else
505  {
506  bc.meshIndex = NUM_MESH_POINTS-1;
508  }
509 
510  if (bc.given) ++numElectrodes;
511  if (bc.given) bcVec.push_back(bc);
512 
513  PDE_1DElectrode *n = new PDE_1DElectrode();
514  electrodeMap[paramName] = n;
515  return static_cast<CompositeParam *> (n);
516  }
517  else if (compositeName == "LAYER")
518  {
520  MaterialLayer *matPtr = new MaterialLayer();
521  materialVec.push_back(matPtr);
522  return (static_cast<CompositeParam *> (matPtr));
523  }
524  else
525  {
526  std::string msg =
527  "Instance::constructComposite: unrecognized composite name: ";
528  msg += compositeName;
529  N_ERH_ErrorMgr::report ( N_ERH_ErrorMgr::DEV_FATAL,msg);
530  }
531 
532  return NULL;
533 }
534 
535 // Additional Declarations
536 
537 //-----------------------------------------------------------------------------
538 // Function : Instance::doAllocations
539 // Purpose :
540 // Special Notes :
541 // Scope : public
542 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
543 // Creation Date : 10/29/10
544 //-----------------------------------------------------------------------------
546 {
547  // Set up a bunch of mesh-based arrays:
548  dxVec.resize (NX,0.0);
549  xVec.resize (NX,0.0);
550  CVec.resize (NX,0.0);
551  CdonorVec.resize (NX,0.0);
552  CacceptorVec.resize (NX,0.0);
553  VVec.resize (NX,0.0);
554  ExVec.resize (NX,0.0);
555  JnxVec.resize (NX,0.0);
556  JpxVec.resize (NX,0.0);
557  RVec.resize (NX,0.0);
558  SVec.resize (NX,0.0);
559  nnVec.resize (NX,0.0);
560  npVec.resize (NX,0.0);
561 
562  NcVec.resize (NX,0.0);
563  NvVec.resize (NX,0.0);
564  EcVec.resize (NX,0.0);
565  EvVec.resize (NX,0.0);
566  EcEffVec.resize (NX,0.0);
567  EvEffVec.resize (NX,0.0);
568  bgnCVec.resize (NX,0.0);
569  bgnVVec.resize (NX,0.0);
570  NiVec.resize (NX,0.0);
571  NiEffVec.resize (NX,0.0);
572  EiVec.resize (NX,0.0);
573  EiEffVec.resize (NX,0.0);
574  EfVec.resize (NX,0.0);
575  EfEffVec.resize (NX,0.0);
576  relPermVec.resize (NX,0.0);
577  bulkMaterialVec.resize(NX);
578 
579  dRdpVec.resize (NX,0.0);
580  dRdnVec.resize (NX,0.0);
581 
582  dJndn1Vec.resize (NX,0.0);
583  dJndn2Vec.resize (NX,0.0);
584  dJndV1Vec.resize (NX,0.0);
585  dJndV2Vec.resize (NX,0.0);
586  dJndp1Vec.resize (NX,0.0);
587  dJndp2Vec.resize (NX,0.0);
588 
589  dJpdn1Vec.resize (NX,0.0);
590  dJpdn2Vec.resize (NX,0.0);
591  dJpdV1Vec.resize (NX,0.0);
592  dJpdV2Vec.resize (NX,0.0);
593  dJpdp1Vec.resize (NX,0.0);
594  dJpdp2Vec.resize (NX,0.0);
595 
596  tnVec.resize(NX,0.0);
597  tpVec.resize(NX,0.0);
598  unE_Vec.resize (NX-1,0.0);
599  upE_Vec.resize (NX-1,0.0);
600 
601  // indexing arrays, local. jacStamp is resized elsewhere
602  li_Vrowarray.resize(NX,0);
603  li_Nrowarray.resize(NX,0);
604  li_Prowarray.resize(NX,0);
605 
606  // displacement current stuff
607  stateDispl.resize(NX,0);
608  stateDispl_owned.resize(NX,0);
609  displCurrent.resize(NX,0.0);
610  li_stateDispl.resize(NX,0);
611 
612  // set up the boundary stencil:
613  boundarySten.resize(NX,0);
614  edgeBoundarySten.resize(NX,0);
615  internalBoundarySten.resize(NX,0);
616  heterojunctionSten.resize(NX,0);
617  matIndex.resize(NX,0);
618 
619  // these will always be set.
620  edgeBoundarySten[0]=1;
621  edgeBoundarySten[LX]=1;
622  boundarySten[0]=1;
623  boundarySten[LX]=1;
624 
625  return true;
626 }
627 
628 //-----------------------------------------------------------------------------
629 // Function : Instance::setupNodes
630 //
631 // Purpose : This sets up the bcVec container. bcVec is a vector of
632 // bcData classes, which contain bondary condition
633 // related data.
634 //
635 // The key issues for a boundary are:
636 // - determine circuit node
637 // - determine mesh boundary location
638 //
639 // Special Notes :
640 // Scope : public
641 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
642 // Creation Date : 10/29/10
643 //-----------------------------------------------------------------------------
645 {
646  // If the user did not use the ELECTRODE/NODE vector-composite
647  // specification, then set up a default (implicit) set of electrodes.
648  //
649  // If 2 terminals, assume a diode, with a specification consistent with the
650  // SPICE diode noder order: D cathode anode
651  //
652  // If 3 terminals, assume a BJT with the node order being the same as
653  // the SPICE Gummel-Poon specification: Q col bas emit
654  //
655  if ( bcVec.empty() )
656  {
657  bcVec.clear();
658  bcVec.resize(numExtVars);
659 
660  useElectrodeSpec_ = false;
661 
662  if (includeBaseNode_)
663  {
664  // collector:
665  int collectorIndex=0;
666  bcIndexMap["collector"] = collectorIndex;
667  bcVec[collectorIndex].eName = "collector";
668  bcVec[collectorIndex].Vequ = collectorbc;
669  bcVec[collectorIndex].VequGiven = given("COLLECTOR.BC");
670  bcVec[collectorIndex].area = collectorArea;
671  bcVec[collectorIndex].areaGiven = given("COLLECTOR.AREA");
672  bcVec[collectorIndex].meshIndex = LX;
673  bcVec[collectorIndex].neighborNode = LX-1;
674  if (!given("COLLECTOR.AREA")) bcVec[collectorIndex].area = area;
675 
676  // base:
677  int baseIndex=1;
678  bcIndexMap["base"] = baseIndex;
679  bcVec[baseIndex].eName = "base";
680  bool found=false;
681  int bIndex=0;
682  double minDelta = length;
683  for (int i=0;i<NX;++i)
684  {
685  double deltaX=fabs(baseLocation-xVec[i]);
686  if (deltaX < minDelta)
687  {
688  bIndex=i;
689  minDelta=deltaX;
690  }
691  }
692 
693  bcVec[baseIndex].Vequ = basebc;
694  bcVec[baseIndex].VequGiven = given("BASE.BC");
695  bcVec[baseIndex].area = baseArea;
696  bcVec[baseIndex].areaGiven = given("BASE.AREA");
697  //bcVec[baseIndex].meshIndex = static_cast<int> (LX/2);
698  bcVec[baseIndex].meshIndex = bIndex;
699  bcVec[baseIndex].neighborNode = bcVec[baseIndex].meshIndex-1;
700  if (!given("BASE.AREA")) bcVec[baseIndex].area = area;
701 
702  // emitter:
703  int emitterIndex=2;
704  bcIndexMap["emitter"] = emitterIndex;
705  bcVec[emitterIndex].eName = "emitter";
706  bcVec[emitterIndex].Vequ = emitterbc;
707  bcVec[emitterIndex].VequGiven = given("EMITTER.BC");
708  bcVec[emitterIndex].area = emitterArea;
709  bcVec[emitterIndex].areaGiven = given("EMITTER.AREA");
710  bcVec[emitterIndex].meshIndex = 0;
711  bcVec[emitterIndex].neighborNode = 1;
712  if (!given("EMITTER.AREA")) bcVec[emitterIndex].area = area;
713  }
714  else
715  {
716  // anode:
717  int anodeIndex=1;
718  bcIndexMap["anode"] = anodeIndex;
719  bcVec[anodeIndex].eName = "anode";
720  bcVec[anodeIndex].Vequ = anodebc;
721  bcVec[anodeIndex].VequGiven = given("ANODE.BC");
722  bcVec[anodeIndex].area = anodeArea;
723  bcVec[anodeIndex].areaGiven = given("ANODE.AREA");
724  bcVec[anodeIndex].meshIndex = 0;
725  bcVec[anodeIndex].neighborNode = 1;
726  if (!given("ANODE.AREA")) bcVec[anodeIndex].area = area;
727 
728  // cathode:
729  int cathodeIndex=0;
730  bcIndexMap["cathode"] = cathodeIndex;
731  bcVec[cathodeIndex].eName = "cathode";
732  bcVec[cathodeIndex].Vequ = cathodebc;
733  bcVec[cathodeIndex].VequGiven = given("CATHODE.BC");
734  bcVec[cathodeIndex].area = cathodeArea;
735  bcVec[cathodeIndex].areaGiven = given("CATHODE.AREA");
736  bcVec[cathodeIndex].meshIndex = LX;
737  bcVec[cathodeIndex].neighborNode = LX-1;
738  if (!given("CATHODE.AREA")) bcVec[cathodeIndex].area = area;
739  }
740  }
741  else // user did use the ELECTRODE/NODE specification.
742  {
743  useElectrodeSpec_ = true;
744 
745  std::vector<int> tmpMeshSten(NX,0);
746 
747  for (int iBC=0;iBC<bcVec.size();++iBC)
748  {
749  PDE_1DElectrode & electrode = *(electrodeMap[bcVec[iBC].nName]);
750 
751  bcIndexMap[ bcVec[iBC].eName ] = iBC;
752 
753  if (electrode.sideGiven)
754  {
755  ExtendedString side = electrode.side;
756  side.toLower();
757  if (side == "left")
758  {
759  bcVec[iBC].meshIndex = 0;
760  bcVec[iBC].neighborNode = 1;
761  tmpMeshSten[0] = 1;
762  }
763  else if (side == "right")
764  {
765  bcVec[iBC].meshIndex = LX;
766  bcVec[iBC].neighborNode = LX-1;
767  tmpMeshSten[LX] = 1;
768  }
769  else if (side == "middle" || side == "mid")
770  {
771 
772  double location = electrode.location;
773  bool found=false;
774  int bIndex=0;
775  double minDelta = length;
776  for (int imesh=0;imesh<NX;++imesh)
777  {
778  double deltaX=fabs(location-xVec[imesh]);
779  if (deltaX < minDelta)
780  {
781  bIndex=imesh;
782  minDelta=deltaX;
783  }
784  }
785 
786  bcVec[iBC].meshIndex = bIndex;
787  bcVec[iBC].neighborNode = bIndex-1;
788  // assuming current coming from the emitter direciton
789 
790  // check to make sure that bIndex isn't already used.
791  if (tmpMeshSten[bIndex] == 1)
792  {
793  std::string msg = "Instance::setupNodes. Failed to find mesh index for " + bcVec[iBC].eName;
794  N_ERH_ErrorMgr::report( N_ERH_ErrorMgr::DEV_FATAL_0,msg);
795  }
796  }
797  else
798  {
799  std::string msg = "Instance::setupNodes. unrecognized side specified.";
800  N_ERH_ErrorMgr::report( N_ERH_ErrorMgr::DEV_FATAL_0,msg);
801  }
802  }
803  else
804  {
805  //std::string msg = "Instance::setupNodes. side NOT specified.";
806  //N_ERH_ErrorMgr::report( N_ERH_ErrorMgr::DEV_FATAL_0,msg);
807  }
808 
809  bcVec[iBC].areaGiven = electrode.areaGiven;
810  if (electrode.areaGiven)
811  {
812  bcVec[iBC].area = electrode.area;
813  }
814  }
815  }
816 
817  indicesSetup_=true;
818 
819 #ifdef Xyce_DEBUG_DEVICE
820  if (getDeviceOptions().debugLevel > 0)
821  {
822  Xyce::dout() << " area = " << area << std::endl;
823  Xyce::dout() << " areaGiven = " << given("AREA") << std::endl;
824  int isize=bcVec.size();
825  for (int i=0;i<isize;++i)
826  {
827  Xyce::dout() << " bcVec["<<i<<"].area = " << bcVec[i].area << std::endl;
828  Xyce::dout() << " bcVec["<<i<<"].areaGiven = " << bcVec[i].areaGiven << std::endl;
829  Xyce::dout() << " bcVec["<<i<<"].meshIndex = " << bcVec[i].meshIndex << std::endl;
830  }
831  }
832 #endif
833 
834  int colmax = maxColsPerRow;
835  int bcSize=bcVec.size();
836  for (int i=0;i<bcSize;++i)
837  {
838  bcVec[i].colArray.resize(colmax,-1);
839  bcVec[i].dIdXcols.resize(colmax,-1);
840  bcVec[i].dIdX.resize(colmax,-1);
841  bcVec[i].dFdVckt.resize(colmax,0.0);
842  }
843 
844  // allocate conductance array:
845  numElectrodes = bcVec.size(); // should be n x n,
846  // where n=number of terminals.
847  condVec.resize(numElectrodes);
848  for (int iE=0;iE<numElectrodes;++iE)
849  {
850  condVec[iE].resize(numElectrodes,0.0);
851  }
852 
853 
854  // initialize the boundary stencils.
855  // Note: two of the points will be at meshIndex=0 and meshIndex=LX. If there
856  // is a 3rd terminal (for the base of a BJT) it will be somewhere in the middle.
857 
858  for (int i=0;i<bcSize;++i)
859  {
860  int meshIndex=bcVec[i].meshIndex;
861 
862  if (meshIndex==0 || meshIndex==LX)
863  {
864  edgeBoundarySten[meshIndex]=1;
865  }
866  else
867  {
868  internalBoundarySten[meshIndex]=1;
869  }
870  boundarySten[meshIndex]=1;
871  }
872 
873  return true;
874 }
875 
876 //-----------------------------------------------------------------------------
877 // Function : Instance::setupNumVars
878 //
879 // Purpose : mostly sets up numIntVars. numExtVars was set earlier
880 // and is easy.
881 //
882 // Special Notes :
883 // Scope : public
884 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
885 // Creation Date : 02/11/03
886 //-----------------------------------------------------------------------------
888 {
889  // Determine the proper size of NX, LX, and get the givens.
890  bool compositeGiven=true;
892  {
893  int matVecSize=materialVec.size();
894  NX=0;
895  width=0.0;
896  for (int i=0;i<matVecSize;++i)
897  {
898  MaterialLayer & matLay = *(materialVec[i]);
899  NX += matLay.NX;
900  width += matLay.width;
901 
902  matLay.LX = matLay.NX-1;
903  }
904 
905  LX = NX-1;
906  }
907 
908  if (NXGiven)
909  {
910  LX = NX-1;
911  numIntVars = 3*NX;
912  numStateVars = numExtVars + NX - 1; // the NX-1 is for the displacement current.
914  }
915  else
916  {
917  std::string msg = "Instance constructor."
918  " NX parameter was not specified.";
919  N_ERH_ErrorMgr::report( N_ERH_ErrorMgr::DEV_FATAL_0,msg);
920  }
921 
922  return true;
923 }
924 
925 //-----------------------------------------------------------------------------
926 // Function : Instance::setupJacStamp
927 // Purpose :
928 // Special Notes :
929 // Scope : public
930 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
931 // Creation Date : 02/11/03
932 //-----------------------------------------------------------------------------
934 {
935  int iMeshNode;
936  int numVars = 3;
937  int Voffset = 0;
938  int Noffset = 1;
939  int Poffset = 2;
940  int baseIndex = 0;
941  int baseIndex_m1 = 0;
942  int baseIndex_p1 = 0;
943 
944  int Vindex = 0;
945  int Nindex = 0;
946  int Pindex = 0;
947 
948  // "minus one" indices
949  int Vindex_m1 = 0;
950  int Nindex_m1 = 0;
951  int Pindex_m1 = 0;
952 
953  // "plus one" indices
954  int Vindex_p1 = 0;
955  int Nindex_p1 = 0;
956  int Pindex_p1 = 0;
957 
958  int extVarOffset = numExtVars;
959  int iBC;
960 
961  int jacSize = numIntVars + extVarOffset;
962 
963  meshToLID.clear(); meshToLID.resize(NX,-1);
964  jacStamp.clear(); jacStamp.resize(jacSize);
965 
966  // set up the meshToLID converter first.
967  int bcSize=bcVec.size();
968  int lid=0;
969  for (int iBC=0;iBC<bcSize;++iBC)
970  {
971  int meshIndex=bcVec[iBC].meshIndex;
972  meshToLID[meshIndex] = lid;
973  lid++;
974  }
975 
976  for (int i=0;i<NX;++i)
977  {
978  if (boundarySten[i]==1) continue;
979  meshToLID[i] = lid;
980  lid++;
981  }
982 
983  // external vars (from circuit) first:
984  // coupled mode==1 is handled in the function augJacStampRxnChem, not here.
985  for (iBC=0;iBC<bcVec.size();++iBC)
986  {
987  iMeshNode = bcVec[iBC].meshIndex;
988 
989  if (edgeBoundarySten[iMeshNode]!=1 && internalBoundarySten[iMeshNode]!=1)
990  {
991  std::string msg = "Instance::setupJacStamp:";
992  msg += "Boundary point not in the stencil.";
993  N_ERH_ErrorMgr::report( N_ERH_ErrorMgr::DEV_FATAL,msg);
994  }
995 
996  int iNN = bcVec[iBC].neighborNode;
997  baseIndex = numVars*meshToLID[iMeshNode ] + extVarOffset;
998  Vindex = baseIndex + Voffset;
999  Nindex = baseIndex + Noffset;
1000  Pindex = baseIndex + Poffset;
1001 
1002  if (iMeshNode > iNN) // i=0, or right-looking
1003  {
1004  baseIndex_m1 = numVars*meshToLID[iMeshNode-1] + extVarOffset;
1005 
1006  Vindex_m1 = baseIndex_m1 + Voffset;
1007  Nindex_m1 = baseIndex_m1 + Noffset;
1008  Pindex_m1 = baseIndex_m1 + Poffset;
1009 
1010  int col=0;
1011  jacStamp[iBC].resize(7,-1);
1012  jacStamp[iBC][col++] = iBC;
1013  jacStamp[iBC][col++] = Vindex;
1014  jacStamp[iBC][col++] = Vindex_m1;
1015  jacStamp[iBC][col++] = Nindex;
1016  jacStamp[iBC][col++] = Nindex_m1;
1017  jacStamp[iBC][col++] = Pindex;
1018  jacStamp[iBC][col++] = Pindex_m1;
1019  }
1020  else // i=LX, or left-looking
1021  {
1022  baseIndex_p1 = numVars*meshToLID[iMeshNode+1] + extVarOffset;
1023 
1024  Vindex_p1 = baseIndex_p1 + Voffset;
1025  Nindex_p1 = baseIndex_p1 + Noffset;
1026  Pindex_p1 = baseIndex_p1 + Poffset;
1027 
1028  int col=0;
1029  jacStamp[iBC].resize(7,-1);
1030  jacStamp[iBC][col++] = iBC;
1031  jacStamp[iBC][col++] = Vindex;
1032  jacStamp[iBC][col++] = Vindex_p1;
1033  jacStamp[iBC][col++] = Nindex;
1034  jacStamp[iBC][col++] = Nindex_p1;
1035  jacStamp[iBC][col++] = Pindex;
1036  jacStamp[iBC][col++] = Pindex_p1;
1037  }
1038  }
1039 
1040  // Variables associated with the mesh.
1041  // First do the mesh points that are boundary condition points.
1042  // Do the anode (BC) mesh point:
1043 
1044  for (iBC=0;iBC<bcVec.size();++iBC)
1045  {
1046  iMeshNode = bcVec[iBC].meshIndex;
1047  int iNN = bcVec[iBC].neighborNode;
1048  int NodeIndex = bcIndexMap[bcVec[iBC].eName]; // iBC?
1049 
1050  baseIndex = numVars*meshToLID[iMeshNode ] + extVarOffset;
1051  Vindex = baseIndex + Voffset;
1052  Nindex = baseIndex + Noffset;
1053  Pindex = baseIndex + Poffset;
1054 
1055  jacStamp[Vindex].resize(3,-1);
1056  jacStamp[Nindex].resize(4,-1);
1057  jacStamp[Pindex].resize(4,-1);
1058 
1059  if (edgeBoundarySten[iMeshNode]==1)
1060  {
1061  if (iMeshNode < iNN) // i=0
1062  {
1063  baseIndex_p1 = numVars*meshToLID[iMeshNode+1] + extVarOffset;
1064  Vindex_p1 = baseIndex_p1 + Voffset;
1065  Nindex_p1 = baseIndex_p1 + Noffset;
1066  Pindex_p1 = baseIndex_p1 + Poffset;
1067 
1068  int col=0;
1069  jacStamp[Vindex][col++] = NodeIndex;
1070  jacStamp[Vindex][col++] = Vindex;
1071  jacStamp[Vindex][col++] = Vindex_p1;
1072 
1073  col=0;
1074  jacStamp[Nindex][col++] = Nindex;
1075  jacStamp[Nindex][col++] = Nindex_p1;
1076  jacStamp[Nindex][col++] = Pindex;
1077  jacStamp[Nindex][col++] = Pindex_p1;
1078 
1079  col=0;
1080  jacStamp[Pindex][col++] = Pindex;
1081  jacStamp[Pindex][col++] = Pindex_p1;
1082  jacStamp[Pindex][col++] = Nindex;
1083  jacStamp[Pindex][col++] = Nindex_p1;
1084  }
1085  else // i=LX
1086  {
1087  baseIndex_m1 = numVars*meshToLID[iMeshNode-1] + extVarOffset;
1088  Vindex_m1 = baseIndex_m1 + Voffset;
1089  Nindex_m1 = baseIndex_m1 + Noffset;
1090  Pindex_m1 = baseIndex_m1 + Poffset;
1091 
1092  int col=0;
1093  jacStamp[Vindex][col++] = Vindex_m1;
1094  jacStamp[Vindex][col++] = Vindex;
1095  jacStamp[Vindex][col++] = NodeIndex;
1096 
1097  col=0;
1098  jacStamp[Nindex][col++] = Nindex_m1;
1099  jacStamp[Nindex][col++] = Nindex;
1100  jacStamp[Nindex][col++] = Pindex_m1;
1101  jacStamp[Nindex][col++] = Pindex;
1102 
1103  col=0;
1104  jacStamp[Pindex][col++] = Pindex_m1;
1105  jacStamp[Pindex][col++] = Pindex;
1106  jacStamp[Pindex][col++] = Nindex_m1;
1107  jacStamp[Pindex][col++] = Nindex;
1108  }
1109  }
1110  else if (internalBoundarySten[iMeshNode]==1) // probably base node
1111  {
1112  // base node applies a BC to the potential and majority carrier.
1113  // The minority carrier does not get a BC, and is treated like an
1114  // internal point. So the stamp here is the same as an interior
1115  // point plus a little extra. Not all of these will be used.
1116 
1117  baseIndex_m1 = numVars*meshToLID[iMeshNode-1] + extVarOffset;
1118  baseIndex = numVars*meshToLID[iMeshNode ] + extVarOffset;
1119  baseIndex_p1 = numVars*meshToLID[iMeshNode+1] + extVarOffset;
1120 
1121  Vindex_m1 = baseIndex_m1 + Voffset;
1122  Nindex_m1 = baseIndex_m1 + Noffset;
1123  Pindex_m1 = baseIndex_m1 + Poffset;
1124  Vindex = baseIndex + Voffset;
1125  Nindex = baseIndex + Noffset;
1126  Pindex = baseIndex + Poffset;
1127  Vindex_p1 = baseIndex_p1 + Voffset;
1128  Nindex_p1 = baseIndex_p1 + Noffset;
1129  Pindex_p1 = baseIndex_p1 + Poffset;
1130 
1131  // voltage col arrays:
1132  int col=0;
1133  jacStamp[Vindex].resize(6,-1);
1134  jacStamp[Vindex][col++] = NodeIndex;
1135  jacStamp[Vindex][col++] = Vindex_m1;
1136  jacStamp[Vindex][col++] = Vindex;
1137  jacStamp[Vindex][col++] = Vindex_p1;
1138  jacStamp[Vindex][col++] = Nindex;
1139  jacStamp[Vindex][col++] = Pindex;
1140 
1141  // electron col arrays:
1142  col=0;
1143  jacStamp[Nindex].resize(9,-1);
1144  jacStamp[Nindex][col++] = Nindex_m1;
1145  jacStamp[Nindex][col++] = Nindex;
1146  jacStamp[Nindex][col++] = Nindex_p1;
1147  jacStamp[Nindex][col++] = Vindex_m1;
1148  jacStamp[Nindex][col++] = Vindex;
1149  jacStamp[Nindex][col++] = Vindex_p1;
1150  jacStamp[Nindex][col++] = Pindex_m1;
1151  jacStamp[Nindex][col++] = Pindex;
1152  jacStamp[Nindex][col++] = Pindex_p1;
1153 
1154  // hole col arrays:
1155  col=0;
1156  jacStamp[Pindex].resize(9,-1);
1157  jacStamp[Pindex][col++] = Pindex_m1;
1158  jacStamp[Pindex][col++] = Pindex;
1159  jacStamp[Pindex][col++] = Pindex_p1;
1160  jacStamp[Pindex][col++] = Vindex_m1;
1161  jacStamp[Pindex][col++] = Vindex;
1162  jacStamp[Pindex][col++] = Vindex_p1;
1163  jacStamp[Pindex][col++] = Nindex_m1;
1164  jacStamp[Pindex][col++] = Nindex;
1165  jacStamp[Pindex][col++] = Nindex_p1;
1166 
1167  }
1168  else// not a boundary. oops!
1169  {
1170  std::string msg = "Instance::setupJacStamp:";
1171  msg += "Boundary point not in the stencil.";
1172  N_ERH_ErrorMgr::report( N_ERH_ErrorMgr::DEV_FATAL,msg);
1173  }
1174  }
1175 
1176  // Now do the non-BC mesh points.
1177  for (iMeshNode=0;iMeshNode<NX;++iMeshNode)
1178  {
1179  if (boundarySten[iMeshNode]==1) continue;
1180 
1181  baseIndex_m1 = numVars*meshToLID[iMeshNode-1] + extVarOffset;
1182  baseIndex = numVars*meshToLID[iMeshNode ] + extVarOffset;
1183  baseIndex_p1 = numVars*meshToLID[iMeshNode+1] + extVarOffset;
1184 
1185  Vindex_m1 = baseIndex_m1 + Voffset;
1186  Nindex_m1 = baseIndex_m1 + Noffset;
1187  Pindex_m1 = baseIndex_m1 + Poffset;
1188  Vindex = baseIndex + Voffset;
1189  Nindex = baseIndex + Noffset;
1190  Pindex = baseIndex + Poffset;
1191  Vindex_p1 = baseIndex_p1 + Voffset;
1192  Nindex_p1 = baseIndex_p1 + Noffset;
1193  Pindex_p1 = baseIndex_p1 + Poffset;
1194 
1195  // voltage col arrays:
1196  int col=0;
1197  jacStamp[Vindex].resize(5,-1);
1198  jacStamp[Vindex][col++] = Vindex_m1;
1199  jacStamp[Vindex][col++] = Vindex;
1200  jacStamp[Vindex][col++] = Vindex_p1;
1201  jacStamp[Vindex][col++] = Nindex;
1202  jacStamp[Vindex][col++] = Pindex;
1203 
1204  // electron col arrays:
1205  col=0;
1206  jacStamp[Nindex].resize(9,-1);
1207  jacStamp[Nindex][col++] = Nindex_m1;
1208  jacStamp[Nindex][col++] = Nindex;
1209  jacStamp[Nindex][col++] = Nindex_p1;
1210  jacStamp[Nindex][col++] = Vindex_m1;
1211  jacStamp[Nindex][col++] = Vindex;
1212  jacStamp[Nindex][col++] = Vindex_p1;
1213  jacStamp[Nindex][col++] = Pindex_m1;
1214  jacStamp[Nindex][col++] = Pindex;
1215  jacStamp[Nindex][col++] = Pindex_p1;
1216 
1217  // hole col arrays:
1218  col=0;
1219  jacStamp[Pindex].resize(9,-1);
1220  jacStamp[Pindex][col++] = Pindex_m1;
1221  jacStamp[Pindex][col++] = Pindex;
1222  jacStamp[Pindex][col++] = Pindex_p1;
1223  jacStamp[Pindex][col++] = Vindex_m1;
1224  jacStamp[Pindex][col++] = Vindex;
1225  jacStamp[Pindex][col++] = Vindex_p1;
1226  jacStamp[Pindex][col++] = Nindex_m1;
1227  jacStamp[Pindex][col++] = Nindex;
1228  jacStamp[Pindex][col++] = Nindex_p1;
1229  }
1230 
1231 #ifdef Xyce_DEBUG_DEVICE
1232  if (getDeviceOptions().debugLevel > 1)
1233  {
1234  // dump the jacStamp to stdout:
1235  int jacSize = jacStamp.size ();
1236  Xyce::dout() << "jacStamp size = " << jacSize << std::endl;
1237 
1238  for(int i=0;i<jacSize;++i)
1239  {
1240  int colSize = jacStamp[i].size();
1241  for (int j=0;j<colSize;++j)
1242  {
1243  Xyce::dout() << " jacStamp["<<i<<"]["<<j<<"] = " << jacStamp[i][j] << std::endl;
1244  }
1245  }
1246  }
1247 #endif
1248 
1249  return true;
1250 }
1251 
1252 //-----------------------------------------------------------------------------
1253 // Function : Instance::cleanupJacStamp
1254 // Purpose :
1255 // Special Notes :
1256 // Scope : public
1257 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1258 // Creation Date : 11/12/10
1259 //-----------------------------------------------------------------------------
1261 {
1262 #if 1
1263  // set up normal jacMap for when all resistances nonzero
1264  // If nothing is remapped, this amounts to a null operation when the
1265  // map is used later. The maps become important when we start
1266  // remapping nodes because of zero lead resistances
1267  jacMap.clear();
1268  jacMap2.clear();
1269  jacMap.resize(jacStamp.size());
1270  jacMap2.resize(jacStamp.size());
1271 
1272  int mapSize = jacMap.size();
1273  for (int i=0;i<mapSize;++i)
1274  {
1275  jacMap[i]=i;
1276  jacMap2[i].resize(jacStamp[i].size());
1277  for (int j=0;j<jacStamp[i].size();++j)
1278  {
1279  jacMap2[i][j] = j;
1280  }
1281  }
1282 
1283  // Now fix the ordering of the columns in the jacStamp. If the columns in each row
1284  // are not in ascending order, then the jacStampMap calls below (for removing
1285  // variables) will not work correctly.
1286  //
1287  // NOTE: This is probably not safe to do, for the PDE devices, so column reordering is off
1288  // by default.
1290  {
1291  std::vector< std::vector<int> > tempStamp_eric;
1292  std::vector< std::vector<int> > tempMap2_eric;
1293  jacStampMap_fixOrder(jacStamp, jacMap2, tempStamp_eric, tempMap2_eric);
1294  jacStamp = tempStamp_eric;
1295  jacMap2 = tempMap2_eric;
1296  }
1297 
1298 #endif // if 1
1299 
1300  return true;
1301 }
1302 
1303 //-----------------------------------------------------------------------------
1304 // Function : Instance::getIntNameMap
1305 // Purpose :
1306 // Special Notes :
1307 // Scope : public
1308 // Creator : Eric R. Keiter, SNL, Parallel Computational Sciences
1309 // Creation Date : 05/13/05
1310 //-----------------------------------------------------------------------------
1311 std::map<int,std::string> & Instance::getIntNameMap ()
1312 {
1313  // set up the internal name map, if it hasn't been already.
1314  if (intNameMap.empty ())
1315  {
1316  int itmp;
1317  char tmpchar[128];
1318 
1319  for (itmp=0;itmp<128;++itmp) tmpchar[itmp] = 0;
1320 
1321  // set up the name map for this device.
1322  for (itmp=0;itmp<NX;++itmp)
1323  {
1324  if (li_Vrowarray[itmp] != -1)
1325  {
1326  std::ostringstream oss;
1327  oss << getName() << "_V_" << itmp;
1328  intNameMap[li_Vrowarray[itmp]] = oss.str();
1329  }
1330 
1331  if (li_Nrowarray[itmp] != -1)
1332  {
1333  std::ostringstream oss;
1334  oss << getName() << "_N_" << itmp;
1335  intNameMap[li_Nrowarray[itmp]] = oss.str();
1336  }
1337 
1338  if (li_Prowarray[itmp] != -1)
1339  {
1340  std::ostringstream oss;
1341  oss << getName() << "_P_" << itmp;
1342  intNameMap[li_Prowarray[itmp]] = oss.str();
1343  }
1344  }
1345  }
1346 
1347  return intNameMap;
1348 }
1349 
1350 //-----------------------------------------------------------------------------
1351 // Function : DiodeDPEInstance::registerLIDs
1352 // Purpose :
1353 // Special Notes :
1354 // Scope : public
1355 // Creator : Eric R. Keiter, SNL, Parallel Computational Sciences
1356 // Creation Date : 09/18/02
1357 //-----------------------------------------------------------------------------
1358 void Instance::registerLIDs( const std::vector<int> & intLIDVecRef,
1359  const std::vector<int> & extLIDVecRef)
1360 {
1361  AssertLIDs(intLIDVecRef.size() == numIntVars);
1362  AssertLIDs(extLIDVecRef.size() == numExtVars);
1363 
1364 #ifdef Xyce_DEBUG_DEVICE
1365  if (getDeviceOptions().debugLevel > 0)
1366  {
1367  Xyce::dout() << section_divider << std::endl;
1368  Xyce::dout() << "Instance::registerLIDs:\n";
1369  Xyce::dout() << " name = " << getName() << std::endl;
1370  Xyce::dout() << " numInt = " << numIntVars << std::endl;
1371  Xyce::dout() << " numEXt = " << numExtVars << std::endl;
1372  Xyce::dout() << " NX = " << NX << std::endl;
1373  }
1374 #endif
1375 
1376  // Copy over the local ID lists:
1377  intLIDVec = intLIDVecRef;
1378  extLIDVec = extLIDVecRef;
1379 
1380  for (int iBC=0;iBC<bcVec.size();++iBC)
1381  {
1382  bcVec[iBC].lid = extLIDVec[iBC];
1383  }
1384 
1385  // First do the boundary condition mesh points. There will be imposed
1386  // boundary conditions at each boundary
1387  // for potential, hole density and electron density.
1388 
1389  // The electrostatic potential, from the perspective of the device
1390  // simulation, is not neccessarily the same as the voltage used in the
1391  // circuit part of the code. Also, obviously, the densities are not used by
1392  // the circuit sim., so these boundary conditions are considered internal
1393  // variables.
1394 
1395  int meshIndex = 0;
1396  int intIndex = 0;
1397 
1398  for (int iBC=0;iBC<bcVec.size();++iBC)
1399  {
1400  meshIndex = bcVec[iBC].meshIndex;
1401  li_Vrowarray[meshIndex] = intLIDVec[intIndex++];
1402  li_Nrowarray[meshIndex] = intLIDVec[intIndex++];
1403  li_Prowarray[meshIndex] = intLIDVec[intIndex++];
1404  }
1405 
1406  // now do the interior points. These will be blocked (V,N,P) together.
1407  //meshIndex=1;
1408  //while (meshIndex < LX)
1409  for (meshIndex=0;meshIndex<NX;++meshIndex)
1410  {
1411  if (boundarySten[meshIndex]==1) continue;
1412 
1413  li_Vrowarray[meshIndex] = intLIDVec[intIndex++];
1414  li_Nrowarray[meshIndex] = intLIDVec[intIndex++];
1415  li_Prowarray[meshIndex] = intLIDVec[intIndex++];
1416  }
1417 
1418 #ifdef Xyce_DEBUG_DEVICE
1419 
1420  if (getDeviceOptions().debugLevel > 0 )
1421  {
1422  Xyce::dout() << "\n solution indices:\n";
1423 
1424  for (int i=0;i<NX;++i)
1425  {
1426  Xyce::dout() << " li_Vrowarray["<<i<<"] = " << li_Vrowarray[i];
1427  Xyce::dout() << "\tli_Nrowarray["<<i<<"] = " << li_Nrowarray[i];
1428  Xyce::dout() << "\tli_Prowarray["<<i<<"] = " << li_Prowarray[i] << std::endl;
1429  }
1430  Xyce::dout() << section_divider << std::endl;
1431  }
1432 
1433 #endif
1434 }
1435 
1436 //-----------------------------------------------------------------------------
1437 // Function : Instance::registerStateLIDs
1438 // Purpose :
1439 // Special Notes :
1440 // Scope : public
1441 // Creator : Eric R. Keiter, SNL, Parallel Computational Sciences
1442 // Creation Date : 09/18/02
1443 //-----------------------------------------------------------------------------
1444 void Instance::registerStateLIDs( const std::vector<int> & staLIDVecRef)
1445 {
1446  AssertLIDs(staLIDVecRef.size() == numStateVars);
1447 
1448 #ifdef Xyce_DEBUG_DEVICE
1449  if (getDeviceOptions().debugLevel > 0)
1450  {
1451  Xyce::dout() << std::endl;
1452  Xyce::dout() << section_divider << std::endl;
1453  Xyce::dout() << " In Instance::registerStateLIDs\n\n";
1454  Xyce::dout() << " name = " << getName() << std::endl;
1455  Xyce::dout() << " Number of State LIDs: " << numStateVars << std::endl;
1456  }
1457 #endif
1458 
1459  // Copy over the local ID lists:
1460  staLIDVec = staLIDVecRef;
1461 
1462  int i;
1463  int j;
1464  for (i=0;i<bcVec.size();++i)
1465  {
1466  bcVec[i].li_stateC = staLIDVec[i];
1467  }
1468 
1469  for (i=0,j=2;i<NX-1;++i,++j)
1470  {
1471  li_stateDispl[i] = staLIDVec[j];
1472  }
1473 
1474 #ifdef Xyce_DEBUG_DEVICE
1475  if (getDeviceOptions().debugLevel > 0)
1476  {
1477  Xyce::dout() << " State indices:" << std::endl;
1478  Xyce::dout() << std::endl;
1479  for (i=0;i<bcVec.size();++i)
1480  {
1481  Xyce::dout() << "bcVec["<<i<<"].li_stateC = "<<bcVec[i].li_stateC<< std::endl;
1482  }
1483  Xyce::dout() << std::endl;
1484 
1485  Xyce::dout() << " Displacement current state variable local indices:" << std::endl;
1486  for (i=0;i<NX-1;++i)
1487  {
1488  Xyce::dout() << " li_stateDispl["<<i<<"] = " << li_stateDispl[i] << std::endl;
1489  }
1490  Xyce::dout() << section_divider << std::endl;
1491  }
1492 #endif
1493 }
1494 
1495 //-----------------------------------------------------------------------------
1496 // Function : Instance::jacobianStamp
1497 // Purpose :
1498 // Special Notes :
1499 // Scope : public
1500 // Creator : Eric R. Keiter, Dept. 9233
1501 // Creation Date : 02/11/03
1502 //-----------------------------------------------------------------------------
1503 const std::vector< std::vector<int> > & Instance::jacobianStamp() const
1504 {
1505  return jacStamp;
1506 }
1507 
1508 //-----------------------------------------------------------------------------
1509 // Function : Instance::registerJacLIDs
1510 // Purpose :
1511 // Special Notes :
1512 // Scope : public
1513 // Creator : Eric R. Keiter, Dept. 9233
1514 // Creation Date : 02/12/03
1515 //-----------------------------------------------------------------------------
1517 ( const std::vector< std::vector<int> > & jacLIDVec )
1518 {
1519  DeviceInstance::registerJacLIDs ( jacLIDVec );
1520 
1521  int iMeshNode;
1522  int numVars = 3;
1523  int baseIndex;
1524  int Vindex;
1525  int Nindex;
1526  int Pindex;
1527  int Voffset = 0;
1528  int Noffset = 1;
1529  int Poffset = 2;
1530 #ifdef Xyce_DEBUG_DEVICE
1531  int i,j;
1532 #endif
1533 
1534  int extVarOffset = numExtVars;
1535 
1536 #ifdef Xyce_DEBUG_DEVICE
1537  if (getDeviceOptions().debugLevel > 0)
1538  {
1539  Xyce::dout() << section_divider << std::endl;
1540  Xyce::dout() << "Instance::registerJacLIDs" << std::endl;
1541 
1542  int jacLIDSize = jacLIDVec.size();
1543  Xyce::dout() << "jacLIDSize = " << jacLIDSize << std::endl;
1544  for (i=0;i<jacLIDSize;++i)
1545  {
1546  int jacLIDcolSize = jacLIDVec[i].size();
1547  Xyce::dout() << std::endl;
1548  Xyce::dout() << "jacLIDVec["<<i<<"].size = " << jacLIDcolSize << std::endl;
1549  for (j=0;j<jacLIDcolSize;++j)
1550  {
1551  Xyce::dout() << "jacLIDVec["<<i<<"]["<<j<<"] = ";
1552  Xyce::dout() << jacLIDVec[i][j] << std::endl;
1553  }
1554  }
1555  }
1556 #endif
1557 
1558  li_Vcolarray.resize(NX);
1559  li_Ncolarray.resize(NX);
1560  li_Pcolarray.resize(NX);
1561 
1562 
1563  for (int iBC=0;iBC<bcVec.size();++iBC)
1564  {
1565  int i1=0;
1566  int numCols = jacLIDVec[iBC].size();
1567  bcVec[iBC].li_colArray.resize(numCols,-1);
1568 
1569  for (i1=0;i1<numCols;++i1)
1570  {
1571  bcVec[iBC].li_colArray[i1] = jacLIDVec[iBC][i1];
1572  }
1573 
1574  int iMeshNode = bcVec[iBC].meshIndex;
1575 
1576  bcVec[iBC].lidOffset = jacLIDVec[iBC][0];
1577 
1578 #ifdef Xyce_DEBUG_DEVICE
1579  if (getDeviceOptions().debugLevel > 0)
1580  {
1581  Xyce::dout() << std::endl;
1582  for(i=0;i<bcVec[iBC].li_colArray.size();++i)
1583  {
1584  Xyce::dout() << bcVec[iBC].eName << ": li_colArray["<<i<<"] = "<<bcVec[iBC].li_colArray[i]<< std::endl;
1585  }
1586  Xyce::dout() << std::endl;
1587  }
1588 #endif
1589  }
1590 
1591  // Do the non-BC mesh points.
1592  for (iMeshNode=0;iMeshNode<NX;++iMeshNode)
1593  {
1594  if (boundarySten[iMeshNode]==1) continue;
1595 
1596  baseIndex = numVars*meshToLID[iMeshNode ] + extVarOffset;
1597 
1598  Vindex = baseIndex + Voffset;
1599  Nindex = baseIndex + Noffset;
1600  Pindex = baseIndex + Poffset;
1601 
1602  // voltage col arrays:
1603  int i1=0;
1604  int j1=0;
1605  li_Vcolarray[iMeshNode].resize(5,-1);
1606  li_Vcolarray[iMeshNode][i1++] = jacLIDVec[Vindex][j1++];
1607  li_Vcolarray[iMeshNode][i1++] = jacLIDVec[Vindex][j1++];
1608  li_Vcolarray[iMeshNode][i1++] = jacLIDVec[Vindex][j1++];
1609  li_Vcolarray[iMeshNode][i1++] = jacLIDVec[Vindex][j1++];
1610  li_Vcolarray[iMeshNode][i1++] = jacLIDVec[Vindex][j1++];
1611 
1612  // electron col arrays:
1613  i1=0; j1=0;
1614  li_Ncolarray[iMeshNode].resize(9,-1);
1615  li_Ncolarray[iMeshNode][i1++] = jacLIDVec[Nindex][j1++];
1616  li_Ncolarray[iMeshNode][i1++] = jacLIDVec[Nindex][j1++];
1617  li_Ncolarray[iMeshNode][i1++] = jacLIDVec[Nindex][j1++];
1618  li_Ncolarray[iMeshNode][i1++] = jacLIDVec[Nindex][j1++];
1619  li_Ncolarray[iMeshNode][i1++] = jacLIDVec[Nindex][j1++];
1620  li_Ncolarray[iMeshNode][i1++] = jacLIDVec[Nindex][j1++];
1621  li_Ncolarray[iMeshNode][i1++] = jacLIDVec[Nindex][j1++];
1622  li_Ncolarray[iMeshNode][i1++] = jacLIDVec[Nindex][j1++];
1623  li_Ncolarray[iMeshNode][i1++] = jacLIDVec[Nindex][j1++];
1624 
1625 
1626  // hole col arrays:
1627  i1=0; j1=0;
1628  li_Pcolarray[iMeshNode].resize(9,-1);
1629  li_Pcolarray[iMeshNode][i1++] = jacLIDVec[Pindex][j1++];
1630  li_Pcolarray[iMeshNode][i1++] = jacLIDVec[Pindex][j1++];
1631  li_Pcolarray[iMeshNode][i1++] = jacLIDVec[Pindex][j1++];
1632  li_Pcolarray[iMeshNode][i1++] = jacLIDVec[Pindex][j1++];
1633  li_Pcolarray[iMeshNode][i1++] = jacLIDVec[Pindex][j1++];
1634  li_Pcolarray[iMeshNode][i1++] = jacLIDVec[Pindex][j1++];
1635  li_Pcolarray[iMeshNode][i1++] = jacLIDVec[Pindex][j1++];
1636  li_Pcolarray[iMeshNode][i1++] = jacLIDVec[Pindex][j1++];
1637  li_Pcolarray[iMeshNode][i1++] = jacLIDVec[Pindex][j1++];
1638 
1639 #ifdef Xyce_DEBUG_DEVICE
1640  if (getDeviceOptions().debugLevel > 0)
1641  {
1642  Xyce::dout() << std::endl;
1643  Xyce::dout() << "registerJacLIDs: iMeshNode = " << iMeshNode << std::endl;
1644  Xyce::dout() << "jacLIDVec[Vindex].size = " << jacLIDVec[Vindex].size()<<std::endl;
1645  Xyce::dout() << "jacLIDVec[Nindex].size = " << jacLIDVec[Nindex].size()<<std::endl;
1646  Xyce::dout() << "jacLIDVec[Pindex].size = " << jacLIDVec[Pindex].size()<<std::endl;
1647 
1648  for (i=0;i<5;++i)
1649  {
1650  Xyce::dout() << " li_Vcolarray["<<iMeshNode<<"]["<<i<<"] = ";
1651  Xyce::dout() << li_Vcolarray[iMeshNode][i] << std::endl;
1652  }
1653  Xyce::dout() << std::endl;
1654  for (i=0;i<7;++i)
1655  {
1656  Xyce::dout() << " li_Ncolarray["<<iMeshNode<<"]["<<i<<"] = ";
1657  Xyce::dout() << li_Ncolarray[iMeshNode][i] << std::endl;
1658  }
1659  Xyce::dout() << std::endl;
1660  for (i=0;i<7;++i)
1661  {
1662  Xyce::dout() << " li_Pcolarray["<<iMeshNode<<"]["<<i<<"] = ";
1663  Xyce::dout() << li_Pcolarray[iMeshNode][i] << std::endl;
1664  }
1665  Xyce::dout() << std::endl;
1666  }
1667 #endif
1668  }
1669 
1670  // Do the BC mesh points. These are actually "first" in the LID array.
1671  for (int iBC=0;iBC<bcVec.size();++iBC)
1672  {
1673  iMeshNode = bcVec[iBC].meshIndex;
1674  int iNN = bcVec[iBC].neighborNode;
1675 
1676  baseIndex = numVars*meshToLID[iMeshNode ] + extVarOffset;
1677  Vindex = baseIndex + Voffset;
1678  Nindex = baseIndex + Noffset;
1679  Pindex = baseIndex + Poffset;
1680 
1681  if (edgeBoundarySten[iMeshNode]==1)
1682  {
1683  int i1=0;
1684  int j1=0;
1685  li_Vcolarray[iMeshNode].resize(3,-1);
1686  li_Vcolarray[iMeshNode][i1++] = jacLIDVec[Vindex][j1++];
1687  li_Vcolarray[iMeshNode][i1++] = jacLIDVec[Vindex][j1++];
1688  li_Vcolarray[iMeshNode][i1++] = jacLIDVec[Vindex][j1++];
1689 
1690  // The stamp for N and P is dependent on the direction.
1691  if (iMeshNode < iNN)// i=0
1692  {
1693  i1=0; j1=0;
1694  li_Ncolarray[iMeshNode].resize(3,-1);
1695  li_Ncolarray[iMeshNode][i1++] = -1;
1696  li_Ncolarray[iMeshNode][i1++] = jacLIDVec[Nindex][j1++];
1697  li_Ncolarray[iMeshNode][i1++] = jacLIDVec[Nindex][j1++];
1698 
1699  i1=0; j1=0;
1700  li_Pcolarray[iMeshNode].resize(3,-1);
1701  li_Pcolarray[iMeshNode][i1++] = -1;
1702  li_Pcolarray[iMeshNode][i1++] = jacLIDVec[Pindex][j1++];
1703  li_Pcolarray[iMeshNode][i1++] = jacLIDVec[Pindex][j1++];
1704  }
1705  else
1706  {
1707  i1=0; j1=0;
1708  li_Ncolarray[iMeshNode].resize(3,-1);
1709  li_Ncolarray[iMeshNode][i1++] = jacLIDVec[Nindex][j1++];
1710  li_Ncolarray[iMeshNode][i1++] = jacLIDVec[Nindex][j1++];
1711 
1712  i1=0; j1=0;
1713  li_Pcolarray[iMeshNode].resize(3,-1);
1714  li_Pcolarray[iMeshNode][i1++] = jacLIDVec[Pindex][j1++];
1715  li_Pcolarray[iMeshNode][i1++] = jacLIDVec[Pindex][j1++];
1716  }
1717  }
1718  else if (internalBoundarySten[iMeshNode]==1) // probably base node
1719  {
1720  // voltage col arrays:
1721  int i1=0;
1722  int j1=0;
1723  li_Vcolarray[iMeshNode].resize(6,-1);
1724  li_Vcolarray[iMeshNode][i1++] = jacLIDVec[Vindex][j1++];
1725  li_Vcolarray[iMeshNode][i1++] = jacLIDVec[Vindex][j1++];
1726  li_Vcolarray[iMeshNode][i1++] = jacLIDVec[Vindex][j1++];
1727  li_Vcolarray[iMeshNode][i1++] = jacLIDVec[Vindex][j1++];
1728  li_Vcolarray[iMeshNode][i1++] = jacLIDVec[Vindex][j1++];
1729  li_Vcolarray[iMeshNode][i1++] = jacLIDVec[Vindex][j1++];
1730 
1731  // electron col arrays:
1732  i1=0; j1=0;
1733  li_Ncolarray[iMeshNode].resize(9,-1);
1734  li_Ncolarray[iMeshNode][i1++] = jacLIDVec[Nindex][j1++];
1735  li_Ncolarray[iMeshNode][i1++] = jacLIDVec[Nindex][j1++];
1736  li_Ncolarray[iMeshNode][i1++] = jacLIDVec[Nindex][j1++];
1737  li_Ncolarray[iMeshNode][i1++] = jacLIDVec[Nindex][j1++];
1738  li_Ncolarray[iMeshNode][i1++] = jacLIDVec[Nindex][j1++];
1739  li_Ncolarray[iMeshNode][i1++] = jacLIDVec[Nindex][j1++];
1740  li_Ncolarray[iMeshNode][i1++] = jacLIDVec[Nindex][j1++];
1741  li_Ncolarray[iMeshNode][i1++] = jacLIDVec[Nindex][j1++];
1742  li_Ncolarray[iMeshNode][i1++] = jacLIDVec[Nindex][j1++];
1743 
1744  // hole col arrays:
1745  i1=0; j1=0;
1746  li_Pcolarray[iMeshNode].resize(9,-1);
1747  li_Pcolarray[iMeshNode][i1++] = jacLIDVec[Pindex][j1++];
1748  li_Pcolarray[iMeshNode][i1++] = jacLIDVec[Pindex][j1++];
1749  li_Pcolarray[iMeshNode][i1++] = jacLIDVec[Pindex][j1++];
1750  li_Pcolarray[iMeshNode][i1++] = jacLIDVec[Pindex][j1++];
1751  li_Pcolarray[iMeshNode][i1++] = jacLIDVec[Pindex][j1++];
1752  li_Pcolarray[iMeshNode][i1++] = jacLIDVec[Pindex][j1++];
1753  li_Pcolarray[iMeshNode][i1++] = jacLIDVec[Pindex][j1++];
1754  li_Pcolarray[iMeshNode][i1++] = jacLIDVec[Pindex][j1++];
1755  li_Pcolarray[iMeshNode][i1++] = jacLIDVec[Pindex][j1++];
1756  }
1757  else// not a boundary. oops!
1758  {
1759  std::string msg = "Instance::registerJacLIDs:";
1760  msg += "Boundary point not in the stencil.";
1761  N_ERH_ErrorMgr::report( N_ERH_ErrorMgr::DEV_FATAL,msg);
1762  }
1763 
1764 #ifdef Xyce_DEBUG_DEVICE
1765  if (getDeviceOptions().debugLevel > 0)
1766  {
1767  Xyce::dout() << std::endl;
1768  Xyce::dout() << "registerJacLIDs: ("<<bcVec[iBC].eName<<") iMeshNode = ";
1769  Xyce::dout() << iMeshNode << std::endl;
1770  for (i=0;i<3;++i)
1771  {
1772  Xyce::dout() << " li_Vcolarray["<<iMeshNode<<"]["<<i<<"] = ";
1773  Xyce::dout() << li_Vcolarray[iMeshNode][i] << std::endl;
1774  }
1775  Xyce::dout() << std::endl;
1776  for (i=0;i<3;++i)
1777  {
1778  Xyce::dout() << " li_Ncolarray["<<iMeshNode<<"]["<<i<<"] = ";
1779  Xyce::dout() << li_Ncolarray[iMeshNode][i] << std::endl;
1780  }
1781  Xyce::dout() << std::endl;
1782  for (i=0;i<3;++i)
1783  {
1784  Xyce::dout() << " li_Pcolarray["<<iMeshNode<<"]["<<i<<"] = ";
1785  Xyce::dout() << li_Pcolarray[iMeshNode][i] << std::endl;
1786  }
1787  Xyce::dout() << std::endl;
1788  }
1789 #endif
1790  }
1791 
1792  return;
1793 }
1794 
1795 //-----------------------------------------------------------------------------
1796 // Function : Instance::setupPointers()
1797 //
1798 // Purpose : Sets up raw pointers for optimized matrix loads.
1799 //
1800 // Special Notes :
1801 //
1802 // Scope : public
1803 // Creator : Eric R. Keiter, SNL
1804 // Creation Date : 05/18/10
1805 //-----------------------------------------------------------------------------
1807 {
1808  N_LAS_Matrix & dFdx = *(extData.dFdxMatrixPtr);
1809  N_LAS_Matrix & dQdx = *(extData.dQdxMatrixPtr);
1810 
1811  fVmatPtr.resize(NX);
1812  fNmatPtr.resize(NX);
1813  fPmatPtr.resize(NX);
1814  qVmatPtr.resize(NX);
1815  qNmatPtr.resize(NX);
1816  qPmatPtr.resize(NX);
1817 
1818  for (int i=0;i<NX;++i)
1819  {
1820  int Vrow = li_Vrowarray[i];
1821  int Nrow = li_Nrowarray[i];
1822  int Prow = li_Prowarray[i];
1823 
1824  int vSize = li_Vcolarray[i].size();
1825  fVmatPtr[i].resize(vSize);
1826  qVmatPtr[i].resize(vSize);
1827  for (int j=0;j<vSize;++j)
1828  {
1829  fVmatPtr[i][j] = &(dFdx[Vrow][li_Vcolarray[i][j]]);
1830  qVmatPtr[i][j] = &(dQdx[Vrow][li_Vcolarray[i][j]]);
1831  }
1832 
1833  int nSize = li_Ncolarray[i].size();
1834  fNmatPtr[i].resize(nSize);
1835  qNmatPtr[i].resize(nSize);
1836  for (int j=0;j<nSize;++j)
1837  {
1838  fNmatPtr[i][j] = &(dFdx[Nrow][li_Ncolarray[i][j]]);
1839  qNmatPtr[i][j] = &(dQdx[Nrow][li_Ncolarray[i][j]]);
1840  }
1841 
1842  int pSize = li_Pcolarray[i].size();
1843  fPmatPtr[i].resize(pSize);
1844  qPmatPtr[i].resize(pSize);
1845  for (int j=0;j<pSize;++j)
1846  {
1847  fPmatPtr[i][j] = &(dFdx[Prow][li_Pcolarray[i][j]]);
1848  qPmatPtr[i][j] = &(dQdx[Prow][li_Pcolarray[i][j]]);
1849  }
1850  }
1851 }
1852 
1853 //-----------------------------------------------------------------------------
1854 // Function : Instance::updateIntermdiateVars
1855 // Purpose :
1856 // Special Notes :
1857 //
1858 // Scope : public
1859 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1860 // Creation Date : 6/29/00
1861 //-----------------------------------------------------------------------------
1863 {
1864  bool bsuccess = true;
1865  bool bs1 = true;
1866 
1867 #ifdef Xyce_DEBUG_DEVICE
1868  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
1869  {
1870  Xyce::dout() << section_divider << std::endl;
1871  Xyce::dout() << "updateIntermediateVars. name = " << getName() << std::endl;
1872  }
1873 #endif
1874 
1875  bs1 = obtainSolution (); bsuccess = bsuccess && bs1;
1876  bs1 = calcEfield (); bsuccess = bsuccess && bs1;
1877  bs1 = calcMobilities (); bsuccess = bsuccess && bs1;
1878  bs1 = calcRecombination (); bsuccess = bsuccess && bs1;
1879  bs1 = calcElectronCurrent (); bsuccess = bsuccess && bs1;
1880  bs1 = calcHoleCurrent (); bsuccess = bsuccess && bs1;
1881 
1882 #ifdef Xyce_DEBUG_DEVICE
1883  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
1884  {
1885  Xyce::dout() << section_divider << std::endl;
1886  }
1887 #endif
1888 
1889  return bsuccess;
1890 }
1891 
1892 //-----------------------------------------------------------------------------
1893 // Function : Instance::calcTerminalCurrents
1894 // Purpose : Calculates total diode current(s) to be used in the
1895 // circuit KCL equations.
1896 //
1897 // Special Notes : Two options:
1898 //
1899 // 1) use the fluxes from the PDE calculation
1900 // 2) use the integrated emission/capture rates from the rxn network.
1901 //
1902 // Scope : public
1903 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1904 // Creation Date : 5/04/01
1905 //-----------------------------------------------------------------------------
1907 {
1908  bool bsuccess = true;
1909  double & J0 = scalingVars.J0;
1910  double & a0 = scalingVars.a0;
1911 
1912  // Calculate the diode current using DD fluxes.
1913  int iBC;
1914  for (iBC=0;iBC<bcVec.size();++iBC)
1915  {
1916  int index = bcVec[iBC].meshIndex;
1917  int iNN=bcVec[iBC].neighborNode;
1918  double & area = bcVec[iBC].area;
1919  double A0=J0*a0*area;
1920 
1921  double sign = ((iNN > index)?1.0:-1.0);
1922  int edgeIndex= ((iNN > index)?index:iNN);
1923 
1924  bcVec[iBC].elecCurrent = sign*JnxVec[edgeIndex]*A0;
1925  bcVec[iBC].holeCurrent = sign*JpxVec[edgeIndex]*A0;
1926 
1927  if (edgeBoundarySten[index]==1)
1928  {
1929  bcVec[iBC].currentSum = bcVec[iBC].elecCurrent + bcVec[iBC].holeCurrent;
1930  }
1931  else if (internalBoundarySten[index]==1)
1932  {
1933  std::string & type = bcVec[iBC].type;
1934 
1935  // only majority carrier goes to the boundary
1936  if (type=="ntype")
1937  {
1938  bcVec[iBC].currentSum = bcVec[iBC].elecCurrent;
1939  }
1940  else if (type=="ptype")
1941  {
1942  bcVec[iBC].currentSum = bcVec[iBC].holeCurrent;
1943  }
1944  else // oops.
1945  {
1946  std::string msg = "Instance::calcTerminalCurrents";
1947  msg += "Unrecognized type on boundary.";
1948  N_ERH_ErrorMgr::report(N_ERH_ErrorMgr::DEV_FATAL, msg);
1949  }
1950  }
1951  else
1952  {
1953  std::string msg = "Instance::calcTerminalCurrents";
1954  msg += "Unrecognized boundary.";
1955  N_ERH_ErrorMgr::report(N_ERH_ErrorMgr::DEV_FATAL, msg);
1956  }
1957 
1958  if (displCurrentFlag)
1959  {
1960  bcVec[iBC].currentSum += bcVec[iBC].displCurrent;
1961  }
1962  }
1963 
1964 #ifdef Xyce_DEBUG_DEVICE
1965  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
1966  {
1967  Xyce::dout() << Xyce::subsection_divider << std::endl
1968  << "Calculated currents, etc., coming from the DD calculation:" << std::endl
1969  << " scalingVars.J0 = " << J0<<std::endl
1970  << " scalingVars.a0 = " << a0<<std::endl
1971  << Xyce::subsection_divider << std::endl;
1972  for (int iBC=0;iBC<bcVec.size();++iBC)
1973  {
1974  Xyce::dout() << bcVec[iBC];
1975  }
1976  Xyce::dout() << Xyce::subsection_divider << std::endl;
1977  }
1978 #endif
1979 
1980  return bsuccess;
1981 }
1982 
1983 //-----------------------------------------------------------------------------
1984 // Function : Instance::pdTerminalCurrents
1985 //
1986 // Purpose : This function calculates partial derivatives associated
1987 // with the Jacobian loads for the KCL equation rows.
1988 //
1989 // Special Notes : Originally, this work was performed in the loadJacDD
1990 // function. However, some of this information is also needed
1991 // for the decoupled 2-level Newton, to calculate the
1992 // terminal conductances.
1993 //
1994 // To calculate the terminal conductances, the following is
1995 // needed for each electrode.
1996 //
1997 // dIdVckt - derivative of terminal current w.r.t. Vckt.
1998 // This is the also the Jacobian contribution
1999 // for the (KCL row, KCL col) entry of the matrix.
2000 //
2001 // dFdVckt - derivative of the RHS vector w.r.t. Vckt.
2002 // This is a vector quantity, and corresponds to
2003 // the (*, Vckt) column of the PDE matrix
2004 // sub-block.
2005 //
2006 // dIdX - derivative of the terminal current w.r.t. the
2007 // vector of PDE solution variables. (ie not
2008 // including Vckt, as that is not part of the PDE
2009 // domain). This is a vector quantity.
2010 // This corresponds to the (KCL row, *) entry of
2011 // the matrix, modulo dIdVckt.
2012 //
2013 // With the "new" boundary conditions, the dFdVckt vector
2014 // should only have one nonzero element, which corresponds
2015 // to dIdVckt.
2016 //
2017 // Scope : public
2018 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
2019 // Creation Date : 04/03/03
2020 //-----------------------------------------------------------------------------
2022 {
2023  std::string msg;
2024 
2025  double & J0 = scalingVars.J0;
2026  double & a0 = scalingVars.a0;
2027 
2028  // first calculate dIdVckt.----------------------------------------
2029  int iBC;
2030  int bcSize = bcVec.size();
2031  for (iBC=0; iBC < bcSize; ++iBC)
2032  {
2033  bcVec[iBC].dIdVckt = 0.0;
2034  } // end if BC loop
2035 
2036  // Now calculate dFdVckt.----------------------------------------
2037  // dFdVckt is a column of the matrix, which corresponds to the variable
2038  // Vckt, but only includes rows for internal PDE device variables.
2039  for (iBC=0;iBC<bcVec.size();++iBC)
2040  {
2041  int iN = bcVec[iBC].meshIndex;
2042  int iNN = bcVec[iBC].neighborNode;
2043  int dFdVcktIndex = 0;
2044 
2045  // poisson term:
2046  bcVec[iBC].dFdVckt[dFdVcktIndex] = -scalingVars.rV0;
2047  }
2048 
2049  // now do dIdX.-------------------------------------------------
2050  // This is a kludge that I pretty much just copied over from
2051  // function loadJacKCLDDFormulation.
2052  // dIdX is pretty much just a KCL row from the matrix, but with only the
2053  // terms which pertain to internal PDE device variables included.
2054  // anode:
2055  int liOffIndex;
2056  int i;
2057  int count;
2058 
2059  for (iBC=0;iBC<bcSize;++iBC)
2060  {
2061  liOffIndex = 1;
2062  i=bcVec[iBC].meshIndex; //i is the mesh point
2063 
2064  if (edgeBoundarySten[i]!=1 && internalBoundarySten[i]!=1)
2065  {
2066  std::string msg = "Instance::pdTerminalCurrents";
2067  msg += "Unrecognized boundary.";
2068  N_ERH_ErrorMgr::report(N_ERH_ErrorMgr::DEV_FATAL, msg);
2069  }
2070 
2071  int iNN=bcVec[iBC].neighborNode;
2072  double & area = bcVec[iBC].area;
2073  double A0=J0*a0*area;
2074  std::vector<int> & colA = bcVec[iBC].li_colArray;
2075 
2076  double sign = ((iNN > i)?1.0:-1.0);
2077  double dJndV = 0.0;
2078  double dJpdV = 0.0;
2079  double dJndn = 0.0;
2080  double dJpdp = 0.0;
2081 
2082  double dJndV_nn = 0.0;
2083  double dJpdV_nn = 0.0;
2084  double dJndn_nn = 0.0;
2085  double dJpdp_nn = 0.0;
2086 
2087  // if looking right, then edge=i, if looking left, then edge=i-1.
2088  int edgeIndex=i;
2089 
2090  if (i<iNN)
2091  {
2092  edgeIndex=i;
2093  dJndV = dJndV1Vec[edgeIndex];
2094  dJpdV = dJpdV1Vec[edgeIndex];
2095  dJndn = dJndn1Vec[edgeIndex];
2096  dJpdp = dJpdp1Vec[edgeIndex];
2097 
2098  dJndV_nn = dJndV2Vec[edgeIndex];
2099  dJpdV_nn = dJpdV2Vec[edgeIndex];
2100  dJndn_nn = dJndn2Vec[edgeIndex];
2101  dJpdp_nn = dJpdp2Vec[edgeIndex];
2102  }
2103  else
2104  {
2105  edgeIndex=iNN;
2106  dJndV = dJndV2Vec[edgeIndex];
2107  dJpdV = dJpdV2Vec[edgeIndex];
2108  dJndn = dJndn2Vec[edgeIndex];
2109  dJpdp = dJpdp2Vec[edgeIndex];
2110 
2111  dJndV_nn = dJndV1Vec[edgeIndex];
2112  dJpdV_nn = dJpdV1Vec[edgeIndex];
2113  dJndn_nn = dJndn1Vec[edgeIndex];
2114  dJpdp_nn = dJpdp1Vec[edgeIndex];
2115  }
2116 
2117  // center (boundary) point:
2118  double Vcoef = (sign* dJndV + sign* dJpdV)*A0;
2119  double Ncoef = (sign* dJndn)*A0;
2120  double Pcoef = (sign* dJpdp)*A0;
2121 
2122  // neighbor point:
2123  double Vcoef_nn = (sign* dJndV_nn + sign* dJpdV_nn)*A0;
2124  double Ncoef_nn = (sign* dJndn_nn)*A0;
2125  double Pcoef_nn = (sign* dJpdp_nn)*A0;
2126 
2127  if (internalBoundarySten[i]==1)
2128  {
2129  std::string & type = bcVec[iBC].type;
2130 
2131  // only majority carrier goes to the boundary
2132  if (type=="ntype")
2133  {
2134  Pcoef=0.0;
2135  Pcoef_nn=0.0;
2136  }
2137  else if (type=="ptype")
2138  {
2139  Ncoef=0.0;
2140  Ncoef_nn=0.0;
2141  }
2142  else // oops.
2143  {
2144  std::string msg = "Instance::pdTerminalCurrents";
2145  msg += "Unrecognized type on boundary.";
2146  N_ERH_ErrorMgr::report(N_ERH_ErrorMgr::DEV_FATAL, msg);
2147  }
2148  }
2149 
2150  // nn=i+1 for right-looking or nn=i-1 for left-looking
2151 
2152  count = 0;
2153 
2154  liOffIndex=1;
2155  // derivative w.r.t. V[i]:
2156  if (bcVec[iBC].li_colArray[liOffIndex] != -1)
2157  {
2158  bcVec[iBC].dIdXcols[count] = bcVec[iBC].li_colArray[liOffIndex];
2159  bcVec[iBC].dIdX[count] = Vcoef;
2160  ++count;
2161  }
2162  ++liOffIndex;
2163 
2164  // derivative w.r.t. V[nn].
2165  if (bcVec[iBC].li_colArray[liOffIndex] != -1)
2166  {
2167  bcVec[iBC].dIdXcols[count] = bcVec[iBC].li_colArray[liOffIndex];
2168  bcVec[iBC].dIdX[count] = Vcoef_nn;
2169  ++count;
2170  }
2171  ++liOffIndex;
2172 
2173  // derivative w.r.t. n[i]:
2174  if (bcVec[iBC].li_colArray[liOffIndex] != -1)
2175  {
2176  bcVec[iBC].dIdXcols[count] = bcVec[iBC].li_colArray[liOffIndex];
2177  bcVec[iBC].dIdX[count] = Ncoef;
2178  ++count;
2179  }
2180  ++liOffIndex;
2181 
2182  // derivative w.r.t. n[nn].
2183  if (bcVec[iBC].li_colArray[liOffIndex] != -1)
2184  {
2185  bcVec[iBC].dIdXcols[count] = bcVec[iBC].li_colArray[liOffIndex];
2186  bcVec[iBC].dIdX[count] = Ncoef_nn;
2187  ++count;
2188  }
2189  ++liOffIndex;
2190 
2191  // derivative w.r.t. p[i]:
2192  if (bcVec[iBC].li_colArray[liOffIndex] != -1)
2193  {
2194  bcVec[iBC].dIdXcols[count] = bcVec[iBC].li_colArray[liOffIndex];
2195  bcVec[iBC].dIdX[count] = Pcoef;
2196  ++count;
2197  }
2198  ++liOffIndex;
2199 
2200  // derivative w.r.t. p[nn].
2201  if (bcVec[iBC].li_colArray[liOffIndex] != -1)
2202  {
2203  bcVec[iBC].dIdXcols[count] = bcVec[iBC].li_colArray[liOffIndex];
2204  bcVec[iBC].dIdX[count] = Pcoef_nn;
2205  ++count;
2206  }
2207  ++liOffIndex;
2208  }
2209 
2210  return true;
2211 }
2212 
2213 //-----------------------------------------------------------------------------
2214 // Function : Instance::calcDXDV
2215 // Purpose :
2216 // Special Notes :
2217 // Scope : public
2218 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
2219 // Creation Date : 04/07/03
2220 //-----------------------------------------------------------------------------
2222 {
2223 
2224  return true;
2225 }
2226 
2227 
2228 //-----------------------------------------------------------------------------
2229 // Function : Instance::loadDFDV
2230 // Purpose : Load -dfdv into the RHS vector for the specified
2231 // electrode.
2232 // Special Notes :
2233 // Scope : public
2234 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
2235 // Creation Date : 04/07/03
2236 //-----------------------------------------------------------------------------
2237 bool Instance::loadDFDV (int ielectrode, N_LAS_Vector * dfdvPtr)
2238 {
2239  bool bsuccess = true;
2240  bool bs1 = true;
2241  N_LAS_Vector & dfdv = *(dfdvPtr);
2242 
2243  bcData & bc = bcVec[ielectrode];
2244 
2245  double coef;
2246  int dFdVindex = 0;
2247 
2248  int inode = bc.neighborNode;
2249 
2250  int Vrow = li_Vrowarray[inode];
2251  int Nrow = li_Nrowarray[inode];
2252  int Prow = li_Prowarray[inode];
2253 
2254  // load V term:
2255  coef = bc.dFdVckt[dFdVindex];
2256  dfdv[Vrow] = -coef;
2257 
2258  ++dFdVindex;
2259 
2260  return bsuccess;
2261 }
2262 
2263 //-----------------------------------------------------------------------------
2264 // Function : Instance::calcConductance
2265 //
2266 // Purpose : Calculates device conductances for a single electrode of
2267 // the PDE device. This function is for
2268 // calculating conductances between extern circuit nodes.
2269 //
2270 // The point of calculating these quantities is to provide
2271 // a lumped parameter substitute for the full device, when
2272 // running 2-level Newton.
2273 //
2274 // Special Notes : This function is (ultimately) invoked from the nonlinear
2275 // solver, as that part of the code, when running in
2276 // 2-level mode, knows when this information is needed.
2277 //
2278 // It is assumed that when this function is called, the
2279 // "deltaX" vector contains the information: dXdVckt, where
2280 // X is solution vector variables associated with the PDE
2281 // device, while Vckt is the voltage on the attached
2282 // circuit node. The reason it is in the "deltaX" vector
2283 // is that it was obtained via a linear solver of the
2284 // problem:
2285 //
2286 // dXdVckt = J^-1 . dFdVckt
2287 //
2288 // dFdVckt was calculated previously in pdTerminalCurrents,
2289 // and J is the Jacobian.
2290 //
2291 //
2292 // Scope : public
2293 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
2294 // Creation Date : 04/07/03
2295 //-----------------------------------------------------------------------------
2296 bool Instance::calcConductance (int iElectrode, const N_LAS_Vector * dxdvPtr)
2297 {
2298  bool bsuccess = true;
2299  const N_LAS_Vector & dxdv = *dxdvPtr;
2300 
2301 #ifdef Xyce_DEBUG_DEVICE
2302  char filename1[256];
2303 
2304  for (int ich = 0; ich < 256; ++ich)
2305  { filename1[ich] = 0; }
2306 
2307  if (getDeviceOptions().debugLevel > -1 && getSolverState().debugTimeFlag)
2308  {
2309  Xyce::dout() << section_divider << "\n";
2310  Xyce::dout() << "calcConductances name = " << getName() << std::endl;
2311  Xyce::dout() << "electrode = " << bcVec[iElectrode].eName;
2312  Xyce::dout() << " dIdVckt = " << bcVec[iElectrode].dIdVckt;
2313  Xyce::dout() << std::endl;
2314  Xyce::dout() << std::endl;
2315  }
2316 #endif
2317 
2318  if (!(bcVec[iElectrode].dxdvAllocated))
2319  {
2320  bcVec[iElectrode].dxdvPtr = extData.lasSysPtr->builder().createVector();
2321  bcVec[iElectrode].dxdvAllocated = true;
2322  }
2323 
2324  // A linear solve should have just been performed up in the Newton
2325  // solver. The result of that solve, dxdv was placed in the RHS vector.
2326  // dxdv is needed later, so save a copy.
2327  *(bcVec[iElectrode].dxdvPtr) = *(dxdvPtr);
2328 
2329  // doing the iElectrode Column of the condVec array.
2330  // This should correspond to the bcVec[iElectrode].gid column of the
2331  // Jacobian.
2332 
2333  double Gij = 0.0;
2334  double dIidVj = 0.0;
2335  double dIidVj_chain = 0.0; // from the dot product.
2336 
2337  for (int iEqu=0;iEqu< numElectrodes; ++iEqu)
2338  {
2339  // conductance Gij .
2340  //
2341  // subscript i = variable, which is one of the electrode voltages.
2342  // subscript j = electrode
2343  //
2344  //
2345  // Gij = dIi/dVj_chain + dIi/dVj
2346  //
2347  // = dot( dIi/dX , dX/dVj ) + dIi/dVj
2348  //
2349  // if i != j, then the last term is zero.
2350 
2351  if (iElectrode != iEqu) dIidVj = 0.0;
2352  else dIidVj = bcVec[iEqu].dIdVckt;
2353 
2354  // load dIdX:
2355  extData.tmpdIdXPtr->putScalar (0.0);
2356  int DIDXSize = bcVec[iEqu].dIdX.size();
2357  for (int iDIDX=0;iDIDX<DIDXSize;++iDIDX)
2358  {
2359  int index = bcVec[iEqu].dIdXcols[iDIDX];
2360  double coef = bcVec[iEqu].dIdX[iDIDX];
2361 
2362  if (index < 0) continue;
2363 
2364  (*(extData.tmpdIdXPtr))[index] = coef;
2365  }
2366 
2367 #ifdef Xyce_DEBUG_DEVICE
2368  sprintf(filename1,"dIdX%02d.txt", iEqu);
2369  extData.tmpdIdXPtr->writeToFile(filename1);
2370 #endif
2371 
2372  // get dot product:
2373  dIidVj_chain = dxdv.dotProduct( *(extData.tmpdIdXPtr) );
2374 
2375  Gij = dIidVj_chain + dIidVj;
2376 
2377  condVec[iEqu][iElectrode] = Gij;
2378 
2379 #ifdef Xyce_DEBUG_DEVICE
2380  if (getDeviceOptions().debugLevel > -5 && getSolverState().debugTimeFlag)
2381  {
2382  char outstring[128];
2383  double Itmp = bcVec[iEqu].currentSum;
2384  double Vtmp = bcVec[iEqu].Vckt - bcVec[iElectrode].Vckt;
2385  Vtmp *= scalingVars.V0;
2386  double GV = Gij*Vtmp;
2387  for(int i=0;i<128;++i) outstring[i] = static_cast<char>(0);
2388  sprintf(outstring,
2389  "(%2d,%2d): dotPr=%12.4e G=%12.4e",
2390  iEqu,iElectrode,dIidVj_chain,Gij);
2391  Xyce::dout() << std::string(outstring) << std::endl;
2392 
2393  sprintf(outstring,
2394  "(%2d,%2d): G=%12.4e G*V=%12.4e I=%12.4e V=%12.4e",
2395  iEqu,iElectrode,Gij,GV,Itmp,Vtmp);
2396  Xyce::dout() << std::string(outstring) << std::endl;
2397  }
2398 #endif
2399  }
2400 
2401 #ifdef Xyce_DEBUG_DEVICE
2402  if (getDeviceOptions().debugLevel > -1 && getSolverState().debugTimeFlag)
2403  {
2404  Xyce::dout() << section_divider << std::endl;
2405  }
2406 #endif
2407 
2408  return bsuccess;
2409 }
2410 
2411 
2412 //-----------------------------------------------------------------------------
2413 // Function : Instance::updatePrimaryState
2414 // Purpose :
2415 // Special Notes :
2416 // Scope : public
2417 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
2418 // Creation Date : 4/18/01
2419 //-----------------------------------------------------------------------------
2421 {
2422  bool bsuccess = true;
2424  N_LAS_Vector & staVector = *(extData.nextStaVectorPtr);
2425 
2426  for (int iBC=0;iBC<bcVec.size();++iBC)
2427  {
2428  int li_state=bcVec[iBC].li_stateC;
2429  staVector[li_state] = bcVec[iBC].currentSum;
2430  }
2431 
2432  // Now store the dielectric displacement in the state vector for
2433  // displacement current.
2434  int i;
2435  for (i = 0; i< NX-1; ++i)
2436  {
2437  double D = eSi * e0 * scalingVars.E0 * ExVec[i];
2438  staVector[li_stateDispl[i]] = D;
2439  }
2440 
2441  return bsuccess;
2442 }
2443 
2444 //-----------------------------------------------------------------------------
2445 // Function : Instance::updateSecondaryState
2446 // Purpose :
2447 // Special Notes :
2448 // Scope : public
2449 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
2450 // Creation Date : 4/18/01
2451 //-----------------------------------------------------------------------------
2453 {
2454  bool bsuccess = true;
2455  N_LAS_Vector & staVector = *(extData.nextStaVectorPtr);
2456  N_LAS_Vector & staDeriv = *(extData.nextStaDerivVectorPtr);
2457 
2458  // Now get displacement current.
2459  for( int i = 0; i< NX-1; ++i)
2460  {
2461  displCurrent[i] = staDeriv[li_stateDispl[i]];
2462  }
2463 
2465 
2467 
2468  return bsuccess;
2469 }
2470 
2471 //-----------------------------------------------------------------------------
2472 // Function : Instance::loadDeviceMask
2473 //
2474 // Purpose : Loads the zero elements of the device mask
2475 //
2476 // Special Notes : elements of the error vector associated with zero
2477 // elements of the mask will not be included in weighted
2478 // norms by the time integrator.
2479 //
2480 // For this device, the electrostatic potential is
2481 // optionally excluded from time integration norm
2482 // calculations.
2483 //
2484 // Scope : public
2485 // Creator : Eric Keiter, SNL, Electrical and Microsystems Modeling
2486 // Creation Date : 11/07/10
2487 //-----------------------------------------------------------------------------
2489 {
2490  if (maskVarsTIAFlag_)
2491  {
2492  N_LAS_Vector * maskVectorPtr = extData.deviceMaskVectorPtr;
2493 
2494  for (int i=0;i<NX;++i)
2495  {
2496  int Vrow = li_Vrowarray[i];
2497  (*maskVectorPtr)[Vrow] = 0.0;
2498  (*maskVectorPtr)[Vrow] = 0.0;
2499  }
2500  }
2501 
2502  return true;
2503 }
2504 
2505 
2506 //-----------------------------------------------------------------------------
2507 // Function : Instance::setInitialGuess
2508 // Purpose :
2509 // Special Notes :
2510 // Scope : public
2511 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
2512 // Creation Date : 5/02/03
2513 //-----------------------------------------------------------------------------
2515 {
2516  bool bsuccess = true;
2517  bool bs1 = true;
2518 
2519  if (variablesScaled)
2520  {
2521  bs1 = unScaleVariables (); bsuccess = bsuccess && bs1;
2522  }
2523  bs1 = calcDensityBCs (); bsuccess = bsuccess && bs1;
2524  bs1 = calcVequBCs (); bsuccess = bsuccess && bs1;
2525  bs1 = calcInitialGuess (); bsuccess = bsuccess && bs1;
2526  bs1 = calcMobilities (); bsuccess = bsuccess && bs1;
2527  bs1 = calcLifetimes (); bsuccess = bsuccess && bs1;
2528  bs1 = scaleVariables (); bsuccess = bsuccess && bs1;
2529 
2530  return bsuccess;
2531 }
2532 
2533 //-----------------------------------------------------------------------------
2534 // Function : Instance::loadVecNLPoisson
2535 // Purpose :
2536 // Special Notes :
2537 //
2538 // Scope : public
2539 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
2540 // Creation Date : 5/25/05
2541 //-----------------------------------------------------------------------------
2542 bool Instance::loadVecNLPoisson (double * rhs)
2543 {
2544  bool bsuccess = true;
2545  int i(-1);
2546  int Vrow(-1), Nrow(-1), Prow(-1);
2547  double coef(0);
2548  double coef2(0);
2549 
2550  Ut = Vt/scalingVars.V0;
2551 
2552  // KCL equations for the two connecting terminals:
2553  // For the NL poisson, there is no coupling to the circuit,
2554  // so nothing to do here.
2555 
2556 
2557  // boundary conditions:
2558  for (int iBC=0;iBC<bcVec.size();++iBC)
2559  {
2560  int i = bcVec[iBC].meshIndex;
2561  Vrow = li_Vrowarray[i];
2562  Nrow = li_Nrowarray[i];
2563  Prow = li_Prowarray[i];
2564 
2565  // no coupling to ckt, so effectively Vckt is hardwired to zero.
2566  //rhs[Vrow] += VVec[i] - (bcVec[iBC].Vckt + bcVec[iBC].Vequ);
2567  rhs[Vrow] += VVec[i] - (bcVec[iBC].Vequ);
2568  rhs[Nrow] = 0.0;
2569  rhs[Prow] = 0.0;
2570  }
2571 
2572  // mesh points associated with heterojunction boundaries:
2573  int hetSize=heterojunctionBCs.size();
2574  for (int ihet=0;ihet<hetSize;++ihet)
2575  {
2576  int i1=heterojunctionBCs[ihet].first;
2577  int i2=heterojunctionBCs[ihet].second;
2578 
2579  // recall ExVec[i] = -(VVec[i+1] - VVec[i])/dxVec[i];
2580  coef = (relPermVec[i1-1]*ExVec[i1-1]-relPermVec[i2]*ExVec[i2]);
2581  Vrow = li_Vrowarray[i1];
2582  Nrow = li_Nrowarray[i1];
2583  Prow = li_Prowarray[i1];
2584  rhs[Vrow] += coef;
2585  rhs[Nrow] = 0.0;
2586  rhs[Prow] = 0.0;
2587 
2588  // only need 1 equation to enforce this.
2589  Vrow = li_Vrowarray[i2];
2590  Nrow = li_Nrowarray[i2];
2591  Prow = li_Prowarray[i2];
2592  rhs[Vrow] = 0.0;
2593  rhs[Nrow] = 0.0;
2594  rhs[Prow] = 0.0;
2595  }
2596 
2597  // mesh points for the PDE problem:
2598  for (i=0;i<NX;++i)
2599  {
2600  if (boundarySten[i]==1) continue;
2601  if ( heterojunctionSten[i]!=0 ) continue;
2602 
2603  Vrow = li_Vrowarray[i];
2604  Nrow = li_Nrowarray[i];
2605  Prow = li_Prowarray[i];
2606 
2607  double aveDx = 0.5*(dxVec[i-1] + dxVec[i]);
2608  coef = (relPermVec[i]*ExVec[i]-relPermVec[i-1]*ExVec[i-1])/aveDx;
2609  coef *= scalingVars.L0;
2610 
2611  double holeDens = getVoltDepHoleDens ( VminExp, VVec[i], Na);
2612  double elecDens = getVoltDepElecDens ( VmaxExp, VVec[i], Nd);
2613  coef2 = -(holeDens-elecDens+CVec[i]);
2614 
2615  coef += coef2;
2616  rhs[Vrow] += coef;
2617 
2618  // Now do electron, hole continuity
2619  rhs[Nrow] = 0.0;
2620  rhs[Prow] = 0.0;
2621  } // row loop...
2622 
2623  return bsuccess;
2624 }
2625 
2626 //-----------------------------------------------------------------------------
2627 // Function : Instance::loadVecDDForm
2628 // Purpose :
2629 // Special Notes :
2630 // Scope : public
2631 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
2632 // Creation Date : 05/26/05
2633 //-----------------------------------------------------------------------------
2634 bool Instance::loadVecDDForm (double * rhs)
2635 {
2636  bool bsuccess = true;
2637  int i;
2638  int Vrow, Nrow, Prow;
2639  double coef, coef2;
2640 
2641  // KCL equations for the two connecting terminals:
2642  // if this is the inner loop of a multilevel Newton solve, don't do the
2643  // KCL-related loads.
2644  if ( !(getSolverState().twoLevelNewtonCouplingMode==Nonlinear::INNER_PROBLEM))
2645  {
2646  for (int iBC=0;iBC<bcVec.size();++iBC)
2647  {
2648  rhs[bcVec[iBC].lid] += bcVec[iBC].currentSum;
2649  }
2650  } // end of twoLevelNewtonCouplingMode if statement.
2651 
2652  // mesh points for the PDE problem:
2653 
2654  // boundary conditions:
2655  // all of these take a Dirchlet BC on voltage.
2656  for (int iBC=0;iBC<bcVec.size();++iBC)
2657  {
2658  int i = bcVec[iBC].meshIndex;
2659  Vrow = li_Vrowarray[i];
2660  Nrow = li_Nrowarray[i];
2661  Prow = li_Prowarray[i];
2662 
2663  rhs[Vrow] += VVec[i]-bcVec[iBC].Vbc;
2664 
2665  if (edgeBoundarySten[i])
2666  {
2667  rhs[Nrow] = nnVec[i]-bcVec[iBC].nnbc;
2668  rhs[Prow] = npVec[i]-bcVec[iBC].npbc;
2669  }
2670  else if (internalBoundarySten[i])
2671  {
2672  std::string & type = bcVec[iBC].type;
2673  double aveDx = 0.5*(dxVec[i-1] + dxVec[i]);
2674 
2675  if (type=="ntype") // boundary condition on e-, let h+ flow
2676  {
2677  rhs[Nrow] = nnVec[i]-bcVec[iBC].nnbc;
2678  rhs[Prow] = -(JpxVec[i]-JpxVec[i-1])/aveDx - RVec[i];
2679  }
2680  else if (type=="ptype") // boundary condition on h+, let e- flow
2681  {
2682  rhs[Nrow] = (JnxVec[i]-JnxVec[i-1])/aveDx - RVec[i];
2683  rhs[Prow] = npVec[i]-bcVec[iBC].npbc;
2684  }
2685  else
2686  {
2687  std::string msg = "Instance::loadVecDDForm";
2688  msg += "Unrecognized type on boundary.";
2689  N_ERH_ErrorMgr::report(N_ERH_ErrorMgr::DEV_FATAL, msg);
2690  }
2691  }
2692  else
2693  {
2694  std::string msg = "Instance::loadVecDDForm";
2695  msg += "Unrecognized stencil on boundary.";
2696  N_ERH_ErrorMgr::report(N_ERH_ErrorMgr::DEV_FATAL, msg);
2697  }
2698  }
2699 
2700  // interior mesh points:
2701  for (i=0;i<NX;++i)
2702  {
2703  if (boundarySten[i]==1) continue;
2704  if ( heterojunctionSten[i]!=0 ) continue;
2705 
2706  ExtendedString semi = bulkMaterial; semi.toLower();
2707 
2708  Vrow = li_Vrowarray[i];
2709  Nrow = li_Nrowarray[i];
2710  Prow = li_Prowarray[i];
2711 
2712  double aveDx = 0.5*(dxVec[i-1] + dxVec[i]);
2713  coef = (relPermVec[i]*ExVec[i]-relPermVec[i-1]*ExVec[i-1])/aveDx;
2714  coef *= scalingVars.L0;
2715  coef2 = -(npVec[i]-nnVec[i]+CVec[i]);
2716 
2717  coef += coef2;
2718  rhs[Vrow] += coef;
2719 
2720  // Now do electron continuity
2721  // get electron time derivative and scale.
2722  rhs[Nrow] = (JnxVec[i]-JnxVec[i-1])/aveDx - RVec[i];
2723 
2724  // Now do hole continuity
2725  // get hole time derivative and scale.
2726  rhs[Prow] = -(JpxVec[i]-JpxVec[i-1])/aveDx - RVec[i];
2727 
2728  } // row loop...
2729 
2730  return bsuccess;
2731 }
2732 
2733 //-----------------------------------------------------------------------------
2734 // Function : Instance::getInstanceBreakPoints
2735 // Purpose : This function adds break points to a vector of breakpoints.
2736 // Special Notes :
2737 // Scope : public
2738 // Creator : Eric R. Keiter, SNL, Electrical and Microsystems Modeling
2739 // Creation Date : 02/09/08
2740 //-----------------------------------------------------------------------------
2742  std::vector<N_UTL_BreakPoint> &breakPointTimes)
2743 {
2744  return true;
2745 }
2746 
2747 //-----------------------------------------------------------------------------
2748 // Function : Instance::loadMatNLPoisson
2749 // Purpose : This function performs an analytic Jacobian matrix load for
2750 // the diode-pde class, for the case of solving a nonlinear
2751 // poisson equation.
2752 // Special Notes :
2753 //
2754 // Scope : private
2755 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
2756 // Creation Date : 4/18/01
2757 //-----------------------------------------------------------------------------
2758 bool Instance::loadMatNLPoisson (N_LAS_Matrix & mat)
2759 {
2760  bool bsuccess = true;
2761  int Vrow, Nrow, Prow;
2762  int i,j;
2763 
2764  Ut = Vt/scalingVars.V0;
2765  double rUt = 1.0/Ut;
2766 
2767  double elecDens, holeDens;
2768  double dx1, dx2;
2769  int iBC;
2770 
2771  // Load the jacobian, row by row.
2772 
2773  // For the NL poisson option, device is not coupled to the ckt,
2774  // so put 1's on the diagonal.
2775  for (iBC=0;iBC<bcVec.size();++iBC)
2776  {
2777  mat[bcVec[iBC].lid][bcVec[iBC].lidOffset] = 1.0;
2778  }
2779 
2780  // boundary conditions on the mesh:
2781  for (int iBC=0;iBC<bcVec.size();++iBC)
2782  {
2783  int i = bcVec[iBC].meshIndex;
2784  Vrow = li_Vrowarray[i];
2785  Nrow = li_Nrowarray[i];
2786  Prow = li_Prowarray[i];
2787 
2788  int offset1 = li_Vcolarray[i][0];
2789  int offset2 = li_Vcolarray[i][1];
2790  int offset3 = li_Vcolarray[i][2];
2791 
2792  if (i==0)
2793  {
2794  mat[Vrow][offset1] = 0.0;
2795  mat[Vrow][offset2] = 1.0;
2796  mat[Vrow][offset3] = 0.0;
2797  }
2798  else if (i==LX)
2799  {
2800  mat[Vrow][offset1] = 0.0;
2801  mat[Vrow][offset2] = 1.0;
2802  mat[Vrow][offset3] = 0.0;
2803  }
2804  else if (internalBoundarySten[i]==1)
2805  {
2806  mat[Vrow][offset1] = 0.0;
2807  mat[Vrow][offset2] = 0.0;
2808  mat[Vrow][offset3] = 1.0;
2809  }
2810 
2811  mat[Nrow][li_Ncolarray[i][1]] = 1.0;
2812  mat[Prow][li_Pcolarray[i][1]] = 1.0;
2813  }
2814 
2815 
2816  // mesh points associated with heterojunction boundaries:
2817  int hetSize=heterojunctionBCs.size();
2818  for (int ihet=0;ihet<hetSize;++ihet)
2819  {
2820  int i1=heterojunctionBCs[ihet].first;
2821  int i2=heterojunctionBCs[ihet].second;
2822 
2823  // recall ExVec[i] = -(VVec[i+1] - VVec[i])/dxVec[i];
2824  //coef = (relPermVec[i1-1]*ExVec[i1-1]-relPermVec[i2]*ExVec[i2]);
2825  Vrow = li_Vrowarray[i1];
2826  Nrow = li_Nrowarray[i1];
2827  Prow = li_Prowarray[i1];
2828 
2829  int offset1 = li_Vcolarray[i1][0];
2830  int offset2 = li_Vcolarray[i1][1];
2831  int offset3 = li_Vcolarray[i1][2];
2832  //int offset4 = li_Vcolarray[i][3]; // not available yet
2833 
2834  dx1 = dxVec[i1-1];
2835  dx2 = dxVec[i2];
2836 
2837  double perm1=relPermVec[i1-1];
2838  double perm2=relPermVec[i2];
2839 
2840 #if 0
2841  Xyce::dout() << "ihet="<<ihet<< " perm1/dx1="<<perm1/dx1<<" perm2/dx2="<<perm2/dx2;
2842  Xyce::dout() << " Vrow="<<Vrow<<" Nrow="<<Nrow<<" Prow="<<Prow<<std::endl;
2843 #endif
2844 
2845  mat[Vrow][offset1] = perm1/dx1;
2846  mat[Vrow][offset2] = -perm1/dx1;
2847  mat[Vrow][offset3] = -perm2/dx2;
2848  //mat[Vrow][offset4] = perm2/dx2; // not available yet
2849 
2850  mat[Nrow][li_Ncolarray[i1][1]] = 1.0;
2851  mat[Prow][li_Pcolarray[i1][1]] = 1.0;
2852 
2853  // only need 1 equation to enforce this.
2854  Vrow = li_Vrowarray[i2];
2855  Nrow = li_Nrowarray[i2];
2856  Prow = li_Prowarray[i2];
2857  mat[Vrow][li_Vcolarray[i2][1]] = 1.0;
2858  mat[Nrow][li_Ncolarray[i2][1]] = 1.0;
2859  mat[Prow][li_Pcolarray[i2][1]] = 1.0;
2860  }
2861 
2862  // rows associated with the PDE mesh:
2863  for (i=0;i<NX;++i)
2864  {
2865  if (boundarySten[i]==1) continue;
2866  if ( heterojunctionSten[i]!=0 ) continue;
2867 
2868  ExtendedString semi = bulkMaterial; semi.toLower();
2869 
2870  holeDens = getVoltDepHoleDens ( VminExp , VVec[i], Na);
2871  elecDens = getVoltDepElecDens ( VmaxExp , VVec[i], Nd);
2872 
2873  dx1 = dxVec[i-1];
2874  dx2 = dxVec[i];
2875  double L0 = scalingVars.L0 * MaterialSupport::getRelPerm(semi);
2876  // double L0 = scalingVars.L0 * relPermVec[i];
2877 
2878  int Vrow = li_Vrowarray[i];
2879  int Nrow = li_Nrowarray[i];
2880  int Prow = li_Prowarray[i];
2881 
2882  int offset1 = li_Vcolarray[i][0];
2883  int offset2 = li_Vcolarray[i][1];
2884  int offset3 = li_Vcolarray[i][2];
2885 
2886  mat[Vrow][offset1] = -L0/(dx1*dx2);
2887  mat[Vrow][offset2] = 2.0*L0/(dx1*dx2) + rUt*holeDens + rUt*elecDens;
2888  mat[Vrow][offset3] = -L0/(dx1*dx2);
2889 
2890  mat[Nrow][li_Ncolarray[i][1]] = 1.0;
2891  mat[Prow][li_Pcolarray[i][1]] = 1.0;
2892  }
2893 
2894  return bsuccess;
2895 }
2896 
2897 //-----------------------------------------------------------------------------
2898 // Function : Instance::loadMatKCLDDForm
2899 // Purpose : Loads drift-diffusion-KCL equations into a matrix.
2900 // Special Notes : This function is used for both old and new DAE.
2901 // Scope : private
2902 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
2903 // Creation Date : 05/25/05
2904 //-----------------------------------------------------------------------------
2905 bool Instance::loadMatKCLDDForm (N_LAS_Matrix & mat)
2906 {
2907  int Vrow;
2908  int iBC;
2909 
2910  double & J0 = scalingVars.J0;
2911  double & a0 = scalingVars.a0;
2912 
2913  int i,j;
2914  int liOffIndex;
2915  int count = 0;
2916 
2917  // rows associated with the connecting terminal KCL's:
2918  int li_row; // circuit node lid
2919 
2920  for (iBC=0;iBC<bcVec.size();++iBC)
2921  {
2922  liOffIndex = 1;
2923  i=bcVec[iBC].meshIndex; //i is the mesh point
2924 
2925  if (edgeBoundarySten[i]!=1 && internalBoundarySten[i]!=1)
2926  {
2927  std::string msg = "Instance::loadMatKCLForm";
2928  msg += "Unrecognized boundary.";
2929  N_ERH_ErrorMgr::report(N_ERH_ErrorMgr::DEV_FATAL, msg);
2930  }
2931 
2932  int iNN=bcVec[iBC].neighborNode;
2933  li_row = bcVec[iBC].lid;
2934  double & area = bcVec[iBC].area;
2935  double A0=J0*a0*area;
2936  std::vector<int> & colA = bcVec[iBC].li_colArray;
2937 
2938  double sign = ((iNN > i)?1.0:-1.0);
2939  double dJndV = 0.0;
2940  double dJpdV = 0.0;
2941  double dJndn = 0.0;
2942  double dJndp = 0.0;
2943  double dJpdp = 0.0;
2944  double dJpdn = 0.0;
2945 
2946  double dJndV_nn = 0.0;
2947  double dJpdV_nn = 0.0;
2948  double dJndn_nn = 0.0;
2949  double dJndp_nn = 0.0;
2950  double dJpdp_nn = 0.0;
2951  double dJpdn_nn = 0.0;
2952 
2953  // if looking right, then edge=i, if looking left, then edge=i-1.
2954  int edgeIndex=i;
2955 
2956  if (i<iNN)
2957  {
2958  edgeIndex=i;
2959  dJndV = dJndV1Vec[edgeIndex];
2960  dJpdV = dJpdV1Vec[edgeIndex];
2961  dJndn = dJndn1Vec[edgeIndex];
2962  dJndp = dJndp1Vec[edgeIndex];
2963  dJpdp = dJpdp1Vec[edgeIndex];
2964  dJpdn = dJpdn1Vec[edgeIndex];
2965 
2966  dJndV_nn = dJndV2Vec[edgeIndex];
2967  dJpdV_nn = dJpdV2Vec[edgeIndex];
2968  dJndn_nn = dJndn2Vec[edgeIndex];
2969  dJndp_nn = dJndp2Vec[edgeIndex];
2970  dJpdp_nn = dJpdp2Vec[edgeIndex];
2971  dJpdn_nn = dJpdn2Vec[edgeIndex];
2972  }
2973  else
2974  {
2975  edgeIndex=iNN;
2976  dJndV = dJndV2Vec[edgeIndex];
2977  dJpdV = dJpdV2Vec[edgeIndex];
2978  dJndn = dJndn2Vec[edgeIndex];
2979  dJndp = dJndp2Vec[edgeIndex];
2980  dJpdp = dJpdp2Vec[edgeIndex];
2981  dJpdn = dJpdn2Vec[edgeIndex];
2982 
2983  dJndV_nn = dJndV1Vec[edgeIndex];
2984  dJpdV_nn = dJpdV1Vec[edgeIndex];
2985  dJndn_nn = dJndn1Vec[edgeIndex];
2986  dJndp_nn = dJndp1Vec[edgeIndex];
2987  dJpdp_nn = dJpdp1Vec[edgeIndex];
2988  dJpdn_nn = dJpdn1Vec[edgeIndex];
2989  }
2990 
2991  // center (boundary) point:
2992  double Vcoef = sign*(dJndV + dJpdV)*A0;
2993  double Ncoef = sign*(dJndn + dJpdn)*A0;
2994  double Pcoef = sign*(dJndp + dJpdp)*A0;
2995 
2996  // neighbor point:
2997  double Vcoef_nn = sign*(dJndV_nn + dJpdV_nn)*A0;
2998  double Ncoef_nn = sign*(dJndn_nn + dJpdn_nn)*A0;
2999  double Pcoef_nn = sign*(dJndp_nn + dJpdp_nn)*A0;
3000 
3001  if (internalBoundarySten[i]==1)
3002  {
3003  std::string & type = bcVec[iBC].type;
3004 
3005  // only majority carrier goes to the boundary
3006  if (type=="ntype")
3007  {
3008  Pcoef=0.0;
3009  Pcoef_nn=0.0;
3010  }
3011  else if (type=="ptype")
3012  {
3013  Ncoef=0.0;
3014  Ncoef_nn=0.0;
3015  }
3016  else // oops.
3017  {
3018  std::string msg = "Instance::loadMatKCLForm";
3019  msg += "Unrecognized type on boundary.";
3020  N_ERH_ErrorMgr::report(N_ERH_ErrorMgr::DEV_FATAL, msg);
3021  }
3022  }
3023 
3024  // nn=i+1 for right-looking or nn=i-1 for left-looking
3025  // derivative w.r.t. V[i]:
3026  mat[li_row][colA[liOffIndex++]] += Vcoef;
3027 
3028  // derivative w.r.t. V[nn].
3029  mat[li_row][colA[liOffIndex++]] += Vcoef_nn;
3030 
3031  // derivative w.r.t. n[i]:
3032  mat[li_row][colA[liOffIndex++]] += Ncoef;
3033 
3034  // derivative w.r.t. n[nn].
3035  mat[li_row][colA[liOffIndex++]] += Ncoef_nn;
3036 
3037  // derivative w.r.t. p[i]:
3038  mat[li_row][colA[liOffIndex++]] += Pcoef;
3039 
3040  // derivative w.r.t. p[nn].
3041  mat[li_row][colA[liOffIndex++]] += Pcoef_nn;
3042  }
3043 
3044  return true;
3045 }
3046 
3047 //-----------------------------------------------------------------------------
3048 // Function : Instance::loadMatCktTrivial
3049 // Purpose : This function handles rows associated with the connecting
3050 // terminal KCL's:
3051 // Special Notes :
3052 // Scope : private
3053 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
3054 // Creation Date : 5/29/05
3055 //-----------------------------------------------------------------------------
3056 bool Instance::loadMatCktTrivial (N_LAS_Matrix & mat)
3057 {
3058  bool bsuccess = true;
3059  bool bs1 = true;
3060  int j;
3061  int iBC;
3062 
3063  for (iBC=0;iBC<bcVec.size();++iBC)
3064  {
3065  mat[bcVec[iBC].lid][bcVec[iBC].lidOffset] = 1.0;
3066  }
3067 
3068  return bsuccess;
3069 }
3070 
3071 //-----------------------------------------------------------------------------
3072 // Function : Instance::loadMatDDForm
3073 // Purpose :
3074 // Special Notes :
3075 // Scope : private
3076 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
3077 // Creation Date : 5/25/05
3078 //-----------------------------------------------------------------------------
3079 bool Instance::loadMatDDForm (N_LAS_Matrix & mat)
3080 {
3081  bool bsuccess = true;
3082  bool bs1 = true;
3083 
3084  int Vrow, Nrow, Prow;
3085 
3086  int i,j;
3087  double coef;
3088 
3089  // set up some of the partial derivative arrays:
3090  bs1 = pdRecombination (); bsuccess = bsuccess && bs1;
3091  bs1 = pdElectronCurrent (); bsuccess = bsuccess && bs1;
3092  bs1 = pdHoleCurrent (); bsuccess = bsuccess && bs1;
3093  bs1 = pdTerminalCurrents (); bsuccess = bsuccess && bs1;
3094 
3095 #ifdef Xyce_DEBUG_DEVICE
3096  if (getDeviceOptions().debugLevel > -10 && getSolverState().debugTimeFlag)
3097  {
3098  for (int i=1;i<LX;++i)
3099  {
3100  double aveDx = 0.5*(dxVec[i-1]+dxVec[i]);
3101  Xyce::dout()<<"\t" <<dJndp1Vec[i]/aveDx<<"\t"<<dJndp2Vec[i]/aveDx
3102  <<"\t" <<dJpdn1Vec[i]/aveDx<<"\t"<<dJpdn2Vec[i]/aveDx<<std::endl;
3103  }
3104  }
3105 #endif
3106 
3107  if ( !(getSolverState().twoLevelNewtonCouplingMode==Nonlinear::INNER_PROBLEM))
3108  {
3109  bs1 = loadMatKCLDDForm ( mat );
3110  bsuccess = bsuccess && bs1;
3111  }
3112  else
3113  {
3114  bs1 = loadMatCktTrivial ( mat );
3115  bsuccess = bsuccess && bs1;
3116  }
3117 
3118  // boundary conditions on the mesh:
3119  for (int iBC=0;iBC<bcVec.size();++iBC)
3120  {
3121  int i = bcVec[iBC].meshIndex;
3122  Vrow = li_Vrowarray[i];
3123  Nrow = li_Nrowarray[i];
3124  Prow = li_Prowarray[i];
3125 
3126  if (i==0)
3127  {
3128  mat[Vrow][li_Vcolarray[i][0]] = -scalingVars.rV0;
3129  mat[Vrow][li_Vcolarray[i][1]] = 1.0;
3130  mat[Vrow][li_Vcolarray[i][2]] = 0.0;
3131 
3132  mat[Nrow][li_Ncolarray[i][1]] = +1.0;
3133  mat[Prow][li_Pcolarray[i][1]] = 1.0;
3134  }
3135  else if (i==LX)
3136  {
3137  mat[Vrow][li_Vcolarray[i][0]] = 0.0;
3138  mat[Vrow][li_Vcolarray[i][1]] = 1.0;
3139  mat[Vrow][li_Vcolarray[i][2]] = -scalingVars.rV0;
3140 
3141  mat[Nrow][li_Ncolarray[i][1]] = +1.0;
3142  mat[Prow][li_Pcolarray[i][1]] = 1.0;
3143  }
3144  else if (internalBoundarySten[i]==1)
3145  {
3146  mat[Vrow][li_Vcolarray[i][0]] = -scalingVars.rV0;
3147  mat[Vrow][li_Vcolarray[i][2]] = 1.0;
3148 
3149  std::string & type = bcVec[iBC].type;
3150 
3151  if (type=="ntype") // boundary condition on e-, let h+ flow
3152  {
3153  mat[Nrow][li_Ncolarray[i][1]] = +1.0;
3154 
3155  double aveDx = 0.5*(dxVec[i-1]+dxVec[i]);
3156 
3157  // derivative w.r.t. npVec[i-1]:
3158  //mat[Prow][li_Pcolarray[i][0]] = dJpdn1Vec[i-1]/aveDx;
3159  mat[Prow][li_Pcolarray[i][0]] = dJpdp1Vec[i-1]/aveDx;
3160 
3161  // derivative w.r.t. npVec[i ]:
3162  mat[Prow][li_Pcolarray[i][1]] =
3163  -(dJpdp1Vec[i] - dJpdp2Vec[i-1])/aveDx - dRdpVec[i];
3164 
3165  // derivative w.r.t. npVec[i+1]:
3166  mat[Prow][li_Pcolarray[i][2]] = -dJpdp2Vec[i]/aveDx;
3167 
3168  // derivative w.r.t. VVec[i-1]:
3169  mat[Prow][li_Pcolarray[i][3]] = (dJpdV1Vec[i-1]/aveDx);
3170 
3171  // derivative w.r.t. VVec[i ]:
3172  mat[Prow][li_Pcolarray[i][4]] =
3173  -(dJpdV1Vec[i] - dJpdV2Vec[i-1])/aveDx;
3174 
3175  // derivative w.r.t. VVec[i+1]:
3176  mat[Prow][li_Pcolarray[i][5]] = (-dJpdV2Vec[i]/aveDx);
3177 
3178  // derivative w.r.t. nnVec[i ]:
3179  //mat[Prow][li_Pcolarray[i][6]] = -dRdnVec[i];
3180 
3181  // derivative w.r.t. nnVec[i-1]:
3182  mat[Prow][li_Pcolarray[i][6]] = dJpdn1Vec[i-1]/aveDx;
3183 
3184  // derivative w.r.t. nnVec[i ]:
3185  mat[Prow][li_Pcolarray[i][7]] =
3186  -(dJpdn1Vec[i] - dJpdn2Vec[i-1])/aveDx -dRdnVec[i];
3187 
3188  // derivative w.r.t. nnVec[i+1]:
3189  mat[Prow][li_Pcolarray[i][8]] = -dJpdn2Vec[i]/aveDx;
3190  }
3191  else if (type=="ptype") // boundary condition on h+, let e- flow
3192  {
3193  mat[Prow][li_Pcolarray[i][1]] = 1.0;
3194 
3195  double aveDx = 0.5*(dxVec[i-1]+dxVec[i]);
3196 
3197  // derivative w.r.t. nnVec[i-1]:
3198  mat[Nrow][li_Ncolarray[i][0]] = -dJndn1Vec[i-1]/aveDx;
3199 
3200  // derivative w.r.t. nnVec[i ]:
3201  mat[Nrow][li_Ncolarray[i][1]] =
3202  (dJndn1Vec[i] - dJndn2Vec[i-1])/aveDx - dRdnVec[i];
3203 
3204  // derivative w.r.t. nnVec[i+1]:
3205  mat[Nrow][li_Ncolarray[i][2]] = dJndn2Vec[i]/aveDx;
3206 
3207  // derivative w.r.t. VVec[i-1]:
3208  mat[Nrow][li_Ncolarray[i][3]] = (-dJndV1Vec[i-1]/aveDx);
3209 
3210  // derivative w.r.t. VVec[i ]:
3211  mat[Nrow][li_Ncolarray[i][4]] =
3212  (dJndV1Vec[i] - dJndV2Vec[i-1])/aveDx;
3213 
3214  // derivative w.r.t. VVec[i+1]:
3215  mat[Nrow][li_Ncolarray[i][5]] = (dJndV2Vec[i]/aveDx);
3216 
3217  // derivative w.r.t. npVec[i-1]:
3218  mat[Nrow][li_Ncolarray[i][6]] = dJndp1Vec[i-1]/aveDx;
3219 
3220  // derivative w.r.t. npVec[i ]:
3221  mat[Nrow][li_Ncolarray[i][7]] =
3222  -(dJndp1Vec[i] - dJndp2Vec[i-1])/aveDx - dRdpVec[i];
3223 
3224  // derivative w.r.t. npVec[i+1]:
3225  mat[Nrow][li_Ncolarray[i][8]] = -dJndp2Vec[i]/aveDx;
3226  }
3227  else
3228  {
3229  std::string msg = "Instance::loadMatDDForm";
3230  msg += "Unrecognized type on boundary.";
3231  N_ERH_ErrorMgr::report(N_ERH_ErrorMgr::DEV_FATAL, msg);
3232  }
3233 
3234  }
3235  }
3236 
3237  // load the rows associated with the PDE mesh:
3238  for (i=0;i<NX;++i)
3239  {
3240  if (boundarySten[i]==1) continue;
3241  if ( heterojunctionSten[i]!=0 ) continue;
3242 
3243  ExtendedString semi = bulkMaterial; semi.toLower();
3244 
3245  Vrow = li_Vrowarray[i];
3246  Nrow = li_Nrowarray[i];
3247  Prow = li_Prowarray[i];
3248 
3249  // poisson equation row -------------------------------------
3250  double dx1 = dxVec[i-1];
3251  double dx2 = dxVec[i];
3252 
3253  double L0 = scalingVars.L0 * MaterialSupport::getRelPerm(semi);
3254  //double L0 = scalingVars.L0 * relPermVec[i];
3255 
3256  // del^2 V elements:
3257  *(fVmatPtr[i][0])=(-L0/(dx1*dx2));
3258  *(fVmatPtr[i][1])=(2.0*L0/(dx1*dx2));
3259  *(fVmatPtr[i][2])=(-L0/(dx1*dx2));
3260 
3261  //double dfdn = q/eps;
3262  // electron density dependency:
3263  *(fVmatPtr[i][3]) = +1.0;
3264 
3265  // hole density dependency:
3266  *(fVmatPtr[i][4]) = -1.0;
3267 
3268  // electron continuity row -------------------------------------
3269  double aveDx = 0.5*(dxVec[i-1]+dxVec[i]);
3270 
3271  // derivative w.r.t. nnVec[i-1]:
3272  *(fNmatPtr[i][0]) = -dJndn1Vec[i-1]/aveDx;
3273 
3274  // derivative w.r.t. nnVec[i ]:
3275  *(fNmatPtr[i][1]) =
3276  (dJndn1Vec[i] - dJndn2Vec[i-1])/aveDx - dRdnVec[i];
3277 
3278 
3279  // derivative w.r.t. nnVec[i+1]:
3280  *(fNmatPtr[i][2]) = dJndn2Vec[i]/aveDx;
3281 
3282  // derivative w.r.t. VVec[i-1]:
3283  *(fNmatPtr[i][3]) = (-dJndV1Vec[i-1]/aveDx);
3284 
3285  // derivative w.r.t. VVec[i ]:
3286  *(fNmatPtr[i][4]) =
3287  (dJndV1Vec[i] - dJndV2Vec[i-1])/aveDx;
3288 
3289  // derivative w.r.t. VVec[i+1]:
3290  *(fNmatPtr[i][5]) = (dJndV2Vec[i]/aveDx);
3291 
3292  // derivative w.r.t. npVec[i-1]:
3293  *(fNmatPtr[i][6]) = -dJndp1Vec[i-1]/aveDx;
3294 
3295  // derivative w.r.t. npVec[i ]:
3296  *(fNmatPtr[i][7]) =
3297  (dJndp1Vec[i] - dJndp2Vec[i-1])/aveDx -dRdpVec[i];
3298 
3299  // derivative w.r.t. npVec[i+1]:
3300  *(fNmatPtr[i][8]) = dJndp2Vec[i]/aveDx;
3301 
3302  // hole continuity row -------------------------------------
3303 
3304  // derivative w.r.t. npVec[i-1]:
3305  *(fPmatPtr[i][0]) = dJpdp1Vec[i-1]/aveDx;
3306 
3307  // derivative w.r.t. npVec[i ]:
3308  *(fPmatPtr[i][1]) =
3309  -(dJpdp1Vec[i] - dJpdp2Vec[i-1])/aveDx - dRdpVec[i];
3310 
3311  // derivative w.r.t. npVec[i+1]:
3312  *(fPmatPtr[i][2]) = -dJpdp2Vec[i]/aveDx;
3313 
3314  // derivative w.r.t. VVec[i-1]:
3315  *(fPmatPtr[i][3]) = (dJpdV1Vec[i-1]/aveDx);
3316 
3317  // derivative w.r.t. VVec[i ]:
3318  *(fPmatPtr[i][4]) =
3319  -(dJpdV1Vec[i] - dJpdV2Vec[i-1])/aveDx;
3320 
3321  // derivative w.r.t. VVec[i+1]:
3322  *(fPmatPtr[i][5]) = (-dJpdV2Vec[i]/aveDx);
3323 
3324  // derivative w.r.t. nnVec[i-1]:
3325  *(fPmatPtr[i][6]) = -dJpdn1Vec[i-1]/aveDx;
3326 
3327  // derivative w.r.t. nnVec[i ]:
3328  *(fPmatPtr[i][7]) =
3329  (dJpdn1Vec[i] - dJpdn2Vec[i-1])/aveDx -dRdnVec[i];
3330 
3331  // derivative w.r.t. nnVec[i+1]:
3332  *(fPmatPtr[i][8]) = dJpdn2Vec[i]/aveDx;
3333  }
3334 
3335  return bsuccess;
3336 }
3337 
3338 //-----------------------------------------------------------------------------
3339 // Function : Instance::calcLifetimes
3340 // Purpose : This function calculates the electron and hole lifetimes
3341 // and places them into the tn and tp arrays.
3342 // Special Notes : This function assumes scaling off.
3343 // Scope : public
3344 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
3345 // Creation Date : 4/13/01
3346 //-----------------------------------------------------------------------------
3348 {
3349  for (int i=0;i<NX;++i)
3350  {
3351  tnVec[i] = MaterialSupport::calcLt (false, fabs(CVec[i]));
3352  tpVec[i] = MaterialSupport::calcLt (true , fabs(CVec[i]));
3353  }
3354  return true;
3355 }
3356 
3357 //-----------------------------------------------------------------------------
3358 // Function : Instance::calcMobilities
3359 // Purpose : This function calculates the electron and hole mobilities
3360 // and places them into the un and up arrays.
3361 //
3362 // Special Notes : The mobility functions assume that scaling is off, so
3363 // this function unscales and rescales things as needed.
3364 //
3365 // Scope : public
3366 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
3367 // Creation Date : 4/13/01
3368 //-----------------------------------------------------------------------------
3370 {
3371  bool bsuccess = true;
3372  int i;
3373 
3378 
3379  // Now do edge mobilities:
3380  for (i=0;i<LX;++i)
3381  {
3382  // possibly these doping related quantities should be determined
3383  // using splines instead.
3384  mi.N = (fabs(CVec[i+1])+fabs(CVec[i]))*0.5;
3385  mi.N *= ((variablesScaled)?scalingVars.C0:1.0);
3386 
3387  mi.Na = (fabs(CacceptorVec[i])+fabs(CacceptorVec[i+1]))*0.5
3388  *((variablesScaled)?scalingVars.C0:1.0);
3389 
3390  mi.Nd = (fabs(CdonorVec[i])+fabs(CdonorVec[i+1]))*0.5
3391  *((variablesScaled)?scalingVars.C0:1.0);
3392 
3393  if (mi.N == 0.0) mi.N = 1.0;
3394 
3395  pdeFadType v1=VVec[i];
3396  pdeFadType v2=VVec[i+1];
3397  pdeFadType n1=nnVec[i];
3398  pdeFadType n2=nnVec[i+1];
3399  pdeFadType p1=npVec[i];
3400  pdeFadType p2=npVec[i+1];
3401 
3402  v1.diff(0,6);
3403  v2.diff(1,6);
3404  n1.diff(2,6);
3405  n2.diff(3,6);
3406  p1.diff(4,6);
3407  p2.diff(5,6);
3408 
3409  pdeFadType Efield = (-(v2-v1)/dxVec[i]);
3410  // this is the most consistent, as it relies on the SG approximation for the midpoint density.
3411  mi.n = fabs(nMidpoint(n1,n2,Efield,dxVec[i],-1))*((variablesScaled)?scalingVars.C0:1.0);
3412  mi.p = fabs(nMidpoint(p1,p2,Efield,dxVec[i],+1))*((variablesScaled)?scalingVars.C0:1.0);
3413  mi.epar = fabs(Efield)*((variablesScaled)?scalingVars.E0:1.0);
3414 
3415  //electron mobility
3416  mi.holeFlag = false;
3418  unE_Vec[i] /= ((variablesScaled)?scalingVars.u0:1.0);
3419 
3420  // hole mobility
3421  mi.holeFlag = true;
3423  upE_Vec[i] /= ((variablesScaled)?scalingVars.u0:1.0);
3424 
3425 #ifdef Xyce_DEBUG_DEVICE
3427  {
3428  Xyce::dout() << "\tunE["<<i<<"]="<<unE_Vec[i];
3429  Xyce::dout() << "\tupE["<<i<<"]="<<upE_Vec[i];
3430  Xyce::dout() << std::endl;
3431  }
3432 #endif
3433  }
3434 
3435  return bsuccess;
3436 }
3437 
3438 //-----------------------------------------------------------------------------
3439 // Function : Instance::updateTemperature
3440 // Purpose :
3441 //
3442 // Special Notes : This won't quite work yet, because Ni and the bandgap
3443 // functions (up in the material support class) are
3444 // not yet really temperature dependent.
3445 //
3446 // Things that change with temperature:
3447 //
3448 // thermal voltage (Vt)
3449 // scaling variable, scalingVars.V0 = Vt
3450 // intrinsic concentration, Ni.
3451 // bandgap, Eg.
3452 // mobilities (updated in real time, so no need to change here).
3453 // density boundary conditions (depend on Ni)
3454 // Vequ (may depend on Ni and Eg).
3455 // defect reactions
3456 // other??
3457 //
3458 // Scope : public
3459 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
3460 // Creation Date : 4/13/01
3461 //-----------------------------------------------------------------------------
3462 bool Instance::updateTemperature(const double & temp_tmp)
3463 {
3464  bool bsuccess = true;
3465  bool bs1 = true;
3466 
3467  if (indicesSetup_) // instance block constructor sets this flag,
3468  // but default constructor does not. If it is set,
3469  // then the device is ready to process this function,
3470  // but not otherwise.
3471  {
3472  Temp = temp_tmp;
3473 
3474  // first un-scale everything, if neccessary:
3475  if (variablesScaled)
3476  {
3477  bs1 = unScaleVariables (); bsuccess = bsuccess && bs1;
3478  }
3479 
3480  bs1 = setupMiscConstants (); bsuccess = bsuccess && bs1;
3481  bs1 = setupScalingVars (); bsuccess = bsuccess && bs1;
3482 
3483  bs1 = calcDensityBCs (); bsuccess = bsuccess && bs1;
3484  bs1 = calcVequBCs (); bsuccess = bsuccess && bs1;
3485  bs1 = calcMobilities (); bsuccess = bsuccess && bs1;
3486 
3487  if (!variablesScaled)
3488  {
3489  bs1 = scaleVariables (); bsuccess = bsuccess && bs1;
3490  }
3491  }
3492 
3493  return bsuccess;
3494 }
3495 
3496 //-----------------------------------------------------------------------------
3497 // Function : Instance::calcVoltDepDensities
3498 // Purpose : This function calculates electron and hole densities,
3499 // based on the electrostatic potential. It is only to be
3500 // called during the initialization phase.
3501 // Special Notes :
3502 // Scope : public
3503 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
3504 // Creation Date : 4/13/01
3505 //-----------------------------------------------------------------------------
3507 {
3508  Ut = Vt/scalingVars.V0;
3509 
3510  for (int i=0;i<NX;++i)
3511  {
3512  npVec[i] = getVoltDepHoleDens(VminExp , VVec[i], Na);
3513  nnVec[i] = getVoltDepElecDens(VmaxExp , VVec[i], Nd);
3514  }
3515  return true;
3516 }
3517 
3518 //-----------------------------------------------------------------------------
3519 // Function : Instance::setupDopingProfile
3520 // Purpose :
3521 // Special Notes :
3522 // Scope : public
3523 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
3524 // Creation Date : 11/28/07
3525 //-----------------------------------------------------------------------------
3527 {
3528  bool bsuccess (false);
3529  bool fromFile (false);
3530  int i;
3531 
3533  {
3534  return true;
3535  }
3536 
3537 #ifdef Xyce_DEBUG_DEVICE
3538  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
3539  {
3540  Xyce::dout() << section_divider << std::endl;
3541  Xyce::dout() << "Instance::setupDopingProfile\n";
3542  }
3543 #endif
3544 
3545  if ( dopingFileName != "NOFILE" )
3546  {
3551  devSupport );
3552 
3553  xloc_pdope_vec.clear();
3554  xloc_pdope_vec.resize( xloc_ndope_vec.size(), 0.0);
3556  bsuccess=true;
3557  fromFile=true;
3558  }
3559  else if ( ( ( ndopeFileName != "NOFILE" ) && ( pdopeFileName != "NOFILE") ) )
3560  {
3565 
3566  bsuccess=true;
3567  fromFile=true;
3568  }
3569  else
3570  {
3571  bsuccess = calcDopingProfile ();
3572  }
3573 
3574 
3575  // use the N and P dopants to create the C vector.
3576  if (fromFile)
3577  {
3578  Na = 0.0;
3579  Nd = 0.0;
3580  for (i=0;i<NX;++i)
3581  {
3582  double xtmp = xVec[i];
3583  double ndopeDopeValue(0.0), pdopeDopeValue(0.0);
3585  ndope_vec, y2_ndope_vec, xtmp, ndopeDopeValue);
3587  pdope_vec, y2_pdope_vec, xtmp, pdopeDopeValue);
3588  CVec[i] = ndopeDopeValue-pdopeDopeValue;
3589 
3590  if (Na > CVec[i]) Na = CVec[i];
3591  if (Nd < CVec[i]) Nd = CVec[i];
3592  }
3593  Na = fabs(Na);
3594  Nd = fabs(Nd);
3595  }
3596 
3597  // now that we have the C vector, loop over the boundary
3598  // conditions and determine if n-type or p-type.
3599 
3600  int iBC;
3601  for (iBC=0;iBC<bcVec.size();++iBC)
3602  {
3603  int i = bcVec[iBC].meshIndex;
3604  if (CVec[i] > 0.0)
3605  {
3606  bcVec[iBC].type = "ntype";
3607  }
3608  else
3609  {
3610  bcVec[iBC].type = "ptype";
3611  }
3612  }
3613 
3614 #ifdef Xyce_DEBUG_DEVICE
3616  {
3617  Xyce::dout() << "Na = " << Na << std::endl;
3618  Xyce::dout() << "Nd = " << Nd << std::endl;
3619  for (i=0;i<NX;++i)
3620  {
3621  Xyce::dout() << "x[" << i << "] = " << xVec[i] << "\t";
3622  Xyce::dout() << "C[" << i << "] = " << CVec[i] << std::endl;
3623  }
3624 
3625  Xyce::dout() << section_divider << std::endl;
3626  }
3627 #endif
3628 
3629  return bsuccess;
3630 }
3631 
3632 //-----------------------------------------------------------------------------
3633 // Function : Instance::calcDopingProfile
3634 //
3635 // Purpose : This function sets up the initial doping profile.
3636 //
3637 // Special Notes : 03/31/03. This function is being modified to handle a
3638 // more general doping specification. The old way of
3639 // specifying doping, which assumes a PN junction, with Na
3640 // and Nd may eventually be phased out. For now, both
3641 // methods of specification are supported, with the new
3642 // style over-riding the old, in the event that both are
3643 // specified.
3644 //
3645 // Scope : public
3646 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
3647 // Creation Date : 4/13/01
3648 //-----------------------------------------------------------------------------
3650 {
3651  bool bsuccess = true;
3652  double midpoint;
3653  int i;
3654 
3655  // Check which style of doping specification to use. If the dopeInfoMap
3656  // is empty, then assume the old method. If not, use the dopeInfoMap.
3657 
3658  if (dopeInfoMap.empty ())
3659  {
3660  // first setup, check the graded junction parameters:
3661  if (gradedJunctionFlag)
3662  {
3663  // if junction width was not specified, set to 1/10 the diode width.
3664  if (!given("WJ"))
3665  WJ = 0.1 * width;
3666 
3667  midpoint = width/2.0;
3668  XL = midpoint - WJ/2.0;
3669  XR = midpoint + WJ/2.0;
3670  }
3671 
3672  for (i=0;i<NX;++i)
3673  {
3674  if (gradedJunctionFlag)
3675  {
3676  if (xVec[i] <= XL) CVec[i] = +Nd;
3677  else if (xVec[i]>XL && xVec[i]<XR)
3678  CVec[i] = Nd-(Na+Nd)/(XR-XL)*(xVec[i]-XL);
3679  else CVec[i] = -Na;
3680  }
3681  else
3682  {
3683  if (xVec[i] < xVec[LX]/2.0)
3684  {
3685  CVec[i] = Nd;
3686  }
3687  else
3688  {
3689  CVec[i] = -Na;
3690  }
3691  }
3692  }
3693  }
3694  else
3695  {
3696  // loop over the dope info map, and sum contributions from each
3697  // doping entity into the total doping array, CVec.
3698  std::map<std::string, DopeInfo *>::iterator iter;
3699  std::map<std::string, DopeInfo *>::iterator start = dopeInfoMap.begin();
3700  std::map<std::string, DopeInfo *>::iterator end = dopeInfoMap.end();
3701 
3702  for ( iter = start; iter != end; ++iter )
3703  {
3704  DopeInfo & di = *(iter->second);
3706  }
3707 
3708  Na = 0.0;
3709  Nd = 0.0;
3710  for (i=0;i<NX;++i)
3711  {
3712  if (Na > CVec[i]) Na = CVec[i];
3713  if (Nd < CVec[i]) Nd = CVec[i];
3714  }
3715  Na = fabs(Na);
3716  Nd = fabs(Nd);
3717 
3718  } // if statement
3719 
3720  if (Na == 0.0 || Nd == 0.0)
3721  {
3722  UserError(*this) << "Mistake in doping. Na=" << Na << " %12.4e Nd=" << Nd;
3723  bsuccess = false;
3724  }
3725 
3726  return bsuccess;
3727 }
3728 
3729 //-----------------------------------------------------------------------------
3730 // Function : Instance::setupDefaultLayer
3731 // Purpose :
3732 // Special Notes :
3733 // Scope : public
3734 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
3735 // Creation Date : 4/8/2014
3736 //-----------------------------------------------------------------------------
3738 {
3739  // if the layer specification was not used, then create one.
3741  {
3742  MaterialLayer *matPtr = new MaterialLayer(bulkMaterial);
3743 
3744  matPtr->materialGiven=true;
3745  matPtr->name="FULLDOMAIN";
3746  matPtr->nameGiven=true;
3747  matPtr->begin=0;
3748  matPtr->end=NX;
3749  matPtr->NX=NX;
3750  matPtr->NXGiven=NXGiven;
3751  matPtr->width=width;
3752  matPtr->widthGiven=widthGiven;
3753 
3754  matPtr->processParams();
3755 
3756  materialVec.resize(1, matPtr);
3759  }
3760  else
3761  {
3763  }
3764 
3765  return true;
3766 }
3767 
3768 //-----------------------------------------------------------------------------
3769 // Function : Instance::setupMesh
3770 // Purpose : This function sets up the mesh. Should only be called once.
3771 // Special Notes :
3772 // Scope : public
3773 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
3774 // Creation Date : 4/13/01
3775 //-----------------------------------------------------------------------------
3777 {
3779  {
3780  int matVecSize=materialVec.size();
3781  int totalMeshIndex = 1;
3782  for (int imat=0;imat<matVecSize;++imat)
3783  {
3784  MaterialLayer & matLay = *(materialVec[imat]);
3785  Xyce::dout() << "name="<<matLay.name <<" NX="<<matLay.NX<<" LX="<<matLay.LX <<std::endl;
3786 
3787  matLay.LX = matLay.NX-1;
3788  matLay.begin = totalMeshIndex-1;
3789  matLay.end = matLay.begin + matLay.LX;
3790 
3791  heterojunctionSten[matLay.begin] = 1;
3792  heterojunctionSten[matLay.end] = 1;
3793 
3794  double dx = matLay.width /(static_cast<double>(matLay.LX));
3795 
3796  if (imat>0)
3797  {
3798  MaterialLayer & matLayPrev = *(materialVec[imat-1]);
3799  xVec[matLay.begin] = xVec[matLayPrev.end];
3800  Xyce::dout() << "Setting xVec["<<matLay.begin<<"] to "<<xVec[matLay.begin] <<"."<<std::endl;
3801 
3802  std::pair<int,int> hetPair = std::make_pair (matLayPrev.end, matLay.begin);
3803  heterojunctionBCs.push_back(hetPair);
3804  }
3805 
3806  double base=xVec[matLay.begin];
3807  int iloc=0;
3808  for (int ix=matLay.begin;ix<=matLay.end;++ix,++totalMeshIndex,++iloc)
3809  {
3810  double extra=static_cast<double>(iloc)*dx;
3811  xVec[ix] = base + extra;
3812  matIndex[ix] = imat;
3813  }
3814  for (int ix=matLay.begin;ix<matLay.end;++ix)
3815  {
3816  dxVec[ix] = xVec[ix+1]-xVec[ix];
3817  }
3818  }
3819  dxVec[LX] = dxVec[LX-1];
3820 
3821  // un-set the heterojunction stencil at the boundaries, as these are more
3822  // appropriately covered by the boundarySten stencil.
3823  heterojunctionSten[0] = 0;
3824  heterojunctionSten[LX] = 0;
3825  }
3826  else
3827  {
3828  // set up an evenly spaced mesh:
3829  double dx_tmp = width/(static_cast<double>(LX));
3830 
3831  for (int i=0;i<NX;++i)
3832  {
3833  xVec[i] = static_cast<double>(i)*dx_tmp;
3834  }
3835 
3836  for (int i=0;i<LX;++i)
3837  {
3838  dxVec[i] = xVec[i+1]-xVec[i];
3839  }
3840  dxVec[LX] = dxVec[LX-1];
3841  }
3842 
3843 #ifdef Xyce_DEBUG_DEVICE
3844  if (getDeviceOptions().debugLevel > 0)
3845  {
3846  for (int i=0;i<NX;++i)
3847  {
3848  Xyce::dout() << "x["<<i<<"] = " << xVec[i];
3849  Xyce::dout() << "\tdx["<<i<<"] = " << dxVec[i];
3850  Xyce::dout() << std::endl;
3851  }
3852 
3853  Xyce::dout() << "heterojunction boundary points:" <<std::endl;
3854  for (int i=0;i<heterojunctionBCs.size();++i)
3855  {
3856  Xyce::dout() << "("
3857  << heterojunctionBCs[i].first << ","
3858  << heterojunctionBCs[i].second << ")" <<std::endl;
3859  }
3860  }
3861 #endif
3862 
3863  return true;
3864 }
3865 
3866 //-----------------------------------------------------------------------------
3867 // Function : Instance::setupMaterialArrays
3868 // Purpose : sets up material arrays that are owned by the instance.
3869 //
3870 // Special Notes : If it is temperature-dependent, it is in the instance.
3871 // (temperature is an instance variable). If not, it
3872 // is in the model.
3873 //
3874 // This function calculates the density of states arrays.
3875 // For reference, see p32-33 of "Fundamentals of III-V Devices"
3876 // by William Liu.
3877 //
3878 // Equation 1-23 from Liu (conduction band DOS):
3879 //
3880 // N_c = 2 \left(\frac{2 \pi k T}{h^2}\right)^{3/2} m_{de}^{*{3/2}}
3881 //
3882 // where m_{de} is the DOS effective mass for electrons.
3883 //
3884 // Equation 1-27 from Liu (valance band DOS):
3885 //
3886 // N_v = 2 \left(\frac{2 \pi k T}{h^2}\right)^{3/2} (m_{lh}^{*{3/2}} + m_{hh}^{*{3/2}})
3887 //
3888 // where m_{lh} is the DOS effective mass for light holes
3889 // where m_{hh} is the DOS effective mass for light holes
3890 //
3891 // According to Liu, the valance band DOS is dependent on two different
3892 // degenerate energy bands, each of which has its own hole effective mass.
3893 //
3894 // Scope : public
3895 // Creation Date :
3896 // Creator : Eric R. Keiter, SNL
3897 //-----------------------------------------------------------------------------
3899 {
3900  double dnbnd0 = 2.0*M_PI*e_mass*kb*Temp/pow(h_planck,2.0);
3901  dnbnd0 = 2.0*pow(dnbnd0,1.5)/1.0e6;
3902  double kbq = 8.6173324e-5; // boltzmann's constant in eV K^-1
3903 
3904 
3905  int size = materialVec.size();
3906  for (int im=0;im<size; ++im)
3907  {
3908  MaterialLayer & matLayer = *(materialVec[im]);
3909 
3910  int begin = matLayer.begin;
3911  int end = matLayer.end;
3912 
3913  double Ec = matLayer.Ec;
3914  double Ev = matLayer.Ev;
3915  double bg = fabs(Ec - Ev);
3916 
3917  double Nc = dnbnd0*(matLayer.dnco);
3918  double Nv = dnbnd0*(matLayer.dnva);
3919 
3920  double EcEff = matLayer.Ec-matLayer.narco;
3921  double EvEff = matLayer.Ev+matLayer.narva;
3922  double bgEff = fabs(EcEff-EvEff);
3923 
3924  // Should this Ni include band-gap narrowing correction? Generally YES.
3925  double Ni = sqrt( Nc * Nv ) * exp (-bg/(2.0*kbq*Temp));
3926  double NiEff = sqrt( Nc * Nv ) * exp (-bgEff/(2.0*kbq*Temp));
3927 
3928  matLayer.Ni = Ni;
3929  matLayer.NiEff = NiEff;
3930  matLayer.bg = bg;
3931  matLayer.bgEff = bgEff;
3932 
3933 #ifdef Xyce_DEBUG_DEVICE
3934  if (getDeviceOptions().debugLevel > -2)
3935  {
3936  Xyce::dout() << "layer="<<matLayer.name<< "\t"<<matLayer.material;
3937  Xyce::dout() << "\tNc="<<Nc<<"\tNv="<<Nv<<"\tbg="<<bg<<"\tNi="<<Ni<<std::endl;
3938  }
3939 #endif
3940 
3941  for (int i=begin;i<=end;++i)
3942  {
3943  relPermVec[i] = matLayer.diel;
3944  EcVec[i] = matLayer.Ec;
3945  EvVec[i] = matLayer.Ev;
3946  EcEffVec[i] = matLayer.EcEff;
3947  EvEffVec[i] = matLayer.EvEff;
3948 
3949  bgnCVec[i] = matLayer.narco;
3950  bgnVVec[i] = matLayer.narva;
3951 
3953  {
3954  CdonorVec[i] = matLayer.Cdonor;
3955  CacceptorVec[i] = matLayer.Cacceptor;
3956  CVec[i] = matLayer.Cdonor - matLayer.Cacceptor;
3957  }
3958 
3959  NiVec[i] = matLayer.Ni;
3960  NiEffVec[i] = matLayer.NiEff;
3961 
3962  EiVec[i] = 0.5*(Ec+Ev)+0.5*kb*Temp*log(Nv/Nc);
3963  EiEffVec[i] = 0.5*(EcEff+EvEff)+0.5*kb*Temp*log(Nv/Nc);
3964  EfVec[i] = 1.0;
3965  EfEffVec[i] = 1.0;
3966 
3967  bulkMaterialVec[i] = matLayer.name;
3968  }
3969  }
3970 
3972  {
3973  Na = 0.0;
3974  Nd = 0.0;
3975  for (int i=0;i<NX;++i)
3976  {
3977  if (Na > CVec[i]) Na = CVec[i];
3978  if (Nd < CVec[i]) Nd = CVec[i];
3979  }
3980  Na = fabs(Na);
3981  Nd = fabs(Nd);
3982  }
3983 
3984 #ifdef Xyce_DEBUG_DEVICE
3985  if (getDeviceOptions().debugLevel > -2)
3986  {
3987  for (int im=0;im<size; ++im)
3988  {
3989  MaterialLayer & matLayer = *(materialVec[im]);
3990 
3991  int begin = matLayer.begin;
3992  int end = matLayer.end;
3993 
3994  Xyce::dout() << matLayer.name << "\tbegin="<<begin<<"\tend="<<end<<std::endl;
3995  }
3996 
3997  for (int i=0;i<NiVec.size();++i)
3998  {
3999  Xyce::dout() << i <<"\t"
4000  << EcVec[i] << "\t"
4001  << EvVec[i] << "\t"
4002  << bgnCVec[i] << "\t"
4003  << bgnVVec[i] << "\t"
4004  << NcVec[i] << "\t"
4005  << NvVec[i] << "\t"
4006  << NiVec[i] << "\n";
4007  }
4008  Xyce::dout() << std::endl;
4009  }
4010 #endif
4011 
4012  return true;
4013 }
4014 
4015 //-----------------------------------------------------------------------------
4016 // Function : Instance::setupMiscConstants
4017 // Purpose :
4018 // Special Notes :
4019 // Scope : public
4020 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
4021 // Creation Date : 07/20/03
4022 //-----------------------------------------------------------------------------
4024 {
4025  if (useOldNi)
4026  {
4027  Ni = MaterialSupport::getNi_old (bulkMaterial, Temp); // this is not accurate
4028  }
4029  else
4030  {
4032  }
4033  Vt = kb*Temp/charge;
4034  return true;
4035 }
4036 
4037 //-----------------------------------------------------------------------------
4038 // Function : Instance::setupScalingVars
4039 // Purpose : This function sets up scaling variables.
4040 // Special Notes :
4041 // Scope : public
4042 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
4043 // Creation Date : 4/28/01
4044 //-----------------------------------------------------------------------------
4046 {
4047  bool bsuccess = true;
4048 
4049  // just to make sure:
4050  Vt = kb*Temp/charge;
4051 
4052  if (given("X0"))
4053  {
4055  }
4056  else
4057  {
4058  scalingVars.x0 = width;// distance scaling (cm)
4059  }
4060 
4061  // For the 1D device, cross-sectional area is kind of a weird concept.
4062  // For the equations within the device, area really doesn't factor into
4063  // the discretization. It is only important at the electrodes, for
4064  // calculating the current coming out of the device.
4066 
4067  scalingVars.T0 = Temp;// temperature scaling (K) (not really used)
4068 
4069  // electrostatic potential scaling (V)
4070  scalingVars.V0 = Vt;
4071  scalingVars.rV0 = 1.0/scalingVars.V0;
4072 
4073  // concentration scaling (cm^-3);
4074  if (given("C0"))
4075  {
4077  }
4078  else if (scaleDensityToMaxDoping_) // this is the default
4079  {
4080  if (Na >= Nd) scalingVars.C0 = Na;
4081  else scalingVars.C0 = Nd;
4082 
4083  scalingVars.C0 *= densityScalarFraction_; // 1.0e-2 by default
4084  }
4085  else
4086  {
4087  scalingVars.C0 = 1.0e+17;
4088  }
4089 
4090  if (given("t0"))
4091  {
4094  }
4095  else
4096  {
4097  // diffusion coefficient scaling (cm^2/s)
4098  scalingVars.D0 = 35.0;
4099 
4100  // time scaling (s)
4102  }
4103 
4104  // mobility coefficient scaling (cm^2/V/s)
4106 
4107  // recombination rate scaling (cm^-3/s)
4109  scalingVars.rR0 = 1.0/scalingVars.R0;
4110 
4111  // electric field scaling (V/cm)
4113 
4114  // particle flux scaling (cm^-2/s)
4116 
4117  // current density scaling (A/cm^2)
4119 
4120  // Laplacian scaling constant
4121  //scalingVars.L0 = scalingVars.V0*eps/(charge*scalingVars.x0*scalingVars.x0*scalingVars.C0);
4122  scalingVars.L0 = scalingVars.V0*e0/(charge*scalingVars.x0*scalingVars.x0*scalingVars.C0); // Laplacian scaling constant
4123 
4124  // rate constant scaling. k0 = 1/(C0*t0) = cm^3/sec
4126  scalingVars.rt0 = 1.0/scalingVars.t0;
4127  scalingVars.k0 = 1.0/scalingVars.rk0;
4128 
4129 #ifdef Xyce_DEBUG_DEVICE
4130  if (getDeviceOptions().debugLevel > -2)
4131  {
4132  Xyce::dout() << "scalingVars.x0 = " << scalingVars.x0 << std::endl;
4133  Xyce::dout() << "scalingVars.a0 = " << scalingVars.a0 << std::endl;
4134  Xyce::dout() << "scalingVars.T0 = " << scalingVars.T0 << std::endl;
4135  Xyce::dout() << "scalingVars.V0 = " << scalingVars.V0 << std::endl;
4136  Xyce::dout() << "scalingVars.C0 = " << scalingVars.C0 << std::endl;
4137  Xyce::dout() << "scalingVars.D0 = " << scalingVars.D0 << std::endl;
4138  Xyce::dout() << "scalingVars.u0 = " << scalingVars.u0 << std::endl;
4139  Xyce::dout() << "scalingVars.R0 = " << scalingVars.R0 << std::endl;
4140  Xyce::dout() << "scalingVars.t0 = " << scalingVars.t0 << std::endl;
4141  Xyce::dout() << "scalingVars.E0 = " << scalingVars.E0 << std::endl;
4142  Xyce::dout() << "scalingVars.F0 = " << scalingVars.F0 << std::endl;
4143  Xyce::dout() << "scalingVars.J0 = " << scalingVars.J0 << std::endl;
4144  Xyce::dout() << "scalingVars.L0 = " << scalingVars.L0 << std::endl;
4145  }
4146 #endif
4147 
4148  return bsuccess;
4149 }
4150 
4151 //-----------------------------------------------------------------------------
4152 // Function : Instance::scaleVariables
4153 //
4154 // Purpose : This function performs scaling on all the relevant variables.
4155 //
4156 // Special Notes : It should only be called at the end of the initial setup.
4157 // Calculations done during the course of the calculation are
4158 // performed with the assumption of scaling.
4159 // Scope : public
4160 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
4161 // Creation Date : 4/28/01
4162 //-----------------------------------------------------------------------------
4164 {
4165  N_LAS_Vector * solVectorPtr = extData.nextSolVectorPtr;
4166 
4167  Na /= scalingVars.C0;
4168  Nd /= scalingVars.C0;
4169  Ni /= scalingVars.C0;
4170 
4171  int i;
4172  for (i=0;i<bcVec.size();++i)
4173  {
4174  bcVec[i].Vbc /= scalingVars.V0;
4175  bcVec[i].Vequ /= scalingVars.V0;
4176  bcVec[i].nnbc /= scalingVars.C0;
4177  bcVec[i].npbc /= scalingVars.C0;
4178 
4179  bcVec[i].area /= scalingVars.a0;
4180  }
4181  area /= scalingVars.a0;
4182 
4183  VminExp /= scalingVars.V0;
4184  VmaxExp /= scalingVars.V0;
4185 
4187 
4188  for (i=0;i<NX;++i)
4189  {
4190  nnVec[i] /= scalingVars.C0;
4191  npVec[i] /= scalingVars.C0;
4192  CVec[i] /= scalingVars.C0;
4193  CdonorVec[i] /= scalingVars.C0;
4194  CacceptorVec[i] /= scalingVars.C0;
4195  VVec[i] /= scalingVars.V0;
4196  tnVec[i] /= scalingVars.t0;
4197  tpVec[i] /= scalingVars.t0;
4198  xVec[i] /= scalingVars.x0;
4199  dxVec[i] /= scalingVars.x0;
4200 
4201  (*solVectorPtr)[li_Vrowarray[i]] = VVec[i];
4202  (*solVectorPtr)[li_Nrowarray[i]] = nnVec[i];
4203  (*solVectorPtr)[li_Prowarray[i]] = npVec[i];
4204  }
4205 
4206  variablesScaled = true;
4207 
4208  return true;
4209 
4210 }
4211 
4212 //-----------------------------------------------------------------------------
4213 // Function : Instance::unScaleVariables
4214 // Purpose : This function is the inverse of scaleVariables.
4215 // Special Notes :
4216 // Scope : public
4217 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
4218 // Creation Date : 7/20/03
4219 //-----------------------------------------------------------------------------
4221 {
4222  N_LAS_Vector * solVectorPtr = extData.nextSolVectorPtr;
4223 
4224  Na *= scalingVars.C0;
4225  Nd *= scalingVars.C0;
4226  Ni *= scalingVars.C0;
4227 
4228  int i;
4229  for (i=0;i<bcVec.size();++i)
4230  {
4231  bcVec[i].Vbc *= scalingVars.V0;
4232  bcVec[i].Vequ *= scalingVars.V0;
4233  bcVec[i].nnbc *= scalingVars.C0;
4234  bcVec[i].npbc *= scalingVars.C0;
4235 
4236  bcVec[i].area *= scalingVars.a0;
4237  }
4238  area *= scalingVars.a0;
4239 
4240  VminExp *= scalingVars.V0;
4241  VmaxExp *= scalingVars.V0;
4242 
4244 
4245  for (i=0;i<NX;++i)
4246  {
4247  nnVec[i] *= scalingVars.C0;
4248  npVec[i] *= scalingVars.C0;
4249  CVec[i] *= scalingVars.C0;
4250  CdonorVec[i] *= scalingVars.C0;
4251  CacceptorVec[i] *= scalingVars.C0;
4252  VVec[i] *= scalingVars.V0;
4253  tnVec[i] *= scalingVars.t0;
4254  tpVec[i] *= scalingVars.t0;
4255  xVec[i] *= scalingVars.x0;
4256  dxVec[i] *= scalingVars.x0;
4257 
4258  (*solVectorPtr)[li_Vrowarray[i]] = VVec[i];
4259  (*solVectorPtr)[li_Nrowarray[i]] = nnVec[i];
4260  (*solVectorPtr)[li_Prowarray[i]] = npVec[i];
4261  }
4262 
4263  variablesScaled = false;
4264 
4265  return true;
4266 }
4267 
4268 //-----------------------------------------------------------------------------
4269 // Function : Instance::calcInitialGuess
4270 // Purpose : This function calculates the initial e-, h+ densties
4271 // and the intial voltage.
4272 // Special Notes :
4273 // Scope : public
4274 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
4275 // Creation Date : 4/13/01
4276 //-----------------------------------------------------------------------------
4278 {
4279  bool bsuccess = true;
4280  N_LAS_Vector * solVectorPtr = extData.nextSolVectorPtr;
4281 
4282  // set up an initial guess for nn and np, based on the doping profile,
4283  // and the equilibrium density expressions. Place these in the
4284  // solution vector.
4285  if (!fermiDiracFlag)
4286  {
4287  double tmp;
4288  double Ci;
4289  double Cisq, Nisq;
4290  for (int i=0;i<NX;++i)
4291  {
4292  Ci = CVec[i];
4293  Cisq = Ci*Ci;
4294  Nisq = Ni*Ni; // Ni is the intrinsic concentration
4295 
4296  // equilibrium electron concentration:
4297  tmp = (fabs(Ci)+sqrt(Cisq+4*Nisq))/2.0;
4298  nnVec[i] = ((Ci>=0)?(tmp):(0.0)) + ((Ci<0)?(Nisq/tmp):(0.0));
4299 
4300  // equilibrium hole concentration:
4301  tmp = (fabs(Ci)+sqrt(Cisq+4*Nisq))/2.0;
4302  npVec[i] = ((Ci<=0)?(tmp):(0.0)) + ((Ci>0)?(Nisq/tmp):(0.0));
4303  }
4304  }
4305  else
4306  {
4307  for (int i=0;i<NX;++i)
4308  {
4309  // using Fermi-Dirac statistics
4310  double kbq_ = 8.6173324e-5; // boltzmann's constant in eV K^-1
4311  double dope_us_ = CVec[i];
4312  double temp_us_ = Temp;
4313 
4314  double Nc = NcVec[i];
4315  double Nv = NvVec[i];
4316 
4317  double cond_band = EcVec[i];
4318  double vale_band = EvVec[i];
4319  double bandgap = cond_band-vale_band;
4320 
4321  // N-type
4322  if (dope_us_ >= 0.0)
4323  {
4324  // Assume n approximately equal to Nd
4325  nnVec[i] = CdonorVec[i];
4326 
4327  // Get Ef - Ec from the inverse fermi function
4328  double ef_m_ec_ = kbq_*temp_us_* fdinvObj (dope_us_/Nc);
4329 
4330  // BGN value. FIX THIS!
4331  double bgn_ = 3.23e-8 * std::pow(dope_us_, 1.0/3.0);
4332 
4333  // Calculate Ef - Ev via (Ef - Ec + Eg) = (Ef - Ec + Ec - Ev) = (Ef - Ev)
4334  double ef_m_ev_ = ef_m_ec_ + bandgap-bgn_;
4335 
4336  // Calculate the minority carrier concentration using Boltzmann-style approx.
4337  npVec[i] = Nv*std::exp(-ef_m_ev_/(kbq_*temp_us_));
4338  }
4339  // P-type
4340  else
4341  {
4342  dope_us_ = std::fabs(dope_us_);
4343  npVec[i] = CacceptorVec[i];
4344 
4345  // Get Ev - Ef from the inverse fermi function
4346  double ev_m_ef_ = kbq_*temp_us_* fdinvObj (dope_us_/Nv);
4347 
4348  // BGN value
4349  double bgn_ = 2.55e-8 * std::pow(dope_us_, 1.0/3.0);
4350 
4351  // Calculate Ec - Ef via (Ev - Ef + Eg) = (Ev - Ef + Ec - Ev) = (Ec - Ef)
4352  double ec_m_ef_ = ev_m_ef_ + (bandgap-bgn_);
4353 
4354  // Calculate the minority carrier concentration using Boltzmann-style approx.
4355  nnVec[i] = Nc*std::exp(-ec_m_ef_/(kbq_*temp_us_));
4356  }
4357  }
4358  }
4359 
4360  // set up initial guess for V, place in solution vector
4361  double Vmax = -1.0e99;
4362  double Vmin = +1.0e99;
4363  for (int i=0;i<NX;++i)
4364  {
4365  // the doping is n-type.
4366  if (nnVec[i]>=npVec[i])
4367  {
4368  VVec[i] = + Vt * log(nnVec[i]/Ni);
4369  }
4370  // the doping is p-type.
4371  else
4372  {
4373  VVec[i] = - Vt * log(npVec[i]/Ni);
4374  }
4375 
4376  if (Vmax < VVec[i]) Vmax = VVec[i];
4377  if (Vmin > VVec[i]) Vmin = VVec[i];
4378  }
4379 
4380  // get the maximum and minimum potentials.
4381  VmaxExp = -1.0e99;
4382  VminExp = +1.0e99;
4383 
4384  for (int i=0;i<NX;++i)
4385  {
4386  if (VmaxExp < VVec[i]) VmaxExp = VVec[i];
4387  if (VminExp > VVec[i]) VminExp = VVec[i];
4388  }
4389 
4390  for (int i=0;i<NX;++i)
4391  {
4392  (*solVectorPtr)[li_Vrowarray[i]] = VVec[i];
4393  (*solVectorPtr)[li_Nrowarray[i]] = nnVec[i];
4394  (*solVectorPtr)[li_Prowarray[i]] = npVec[i];
4395  }
4396 
4397  return bsuccess;
4398 }
4399 
4400 //-----------------------------------------------------------------------------
4401 // Function : Instance::calcVequBCs
4402 // Purpose :
4403 // Special Notes :
4404 // Scope : public
4405 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
4406 // Creation Date : 07/17/03
4407 //-----------------------------------------------------------------------------
4409 {
4410  bool bsuccess = true;
4411  Vt = kb*Temp/charge;
4412 
4413  double VminBC =+1.0e+99;
4414  double VmaxBC =-1.0e+99;
4415 
4416  int bcSize=bcVec.size();
4417  for (int i=0;i<bcSize;++i)
4418  {
4419  int mIndex = bcVec[i].meshIndex;
4420  double Ci = CVec[mIndex];
4421  double Cisq = Ci*Ci;
4422  double Nisq = Ni*Ni; // Ni is the intrinsic concentration
4423  double tmp, nnTmp, npTmp;
4424 
4425  // equilibrium electron concentration:
4426  tmp = (fabs(Ci)+sqrt(Cisq+4*Nisq))/2.0;
4427  nnTmp = ((Ci>=0)?(tmp):(0.0)) + ((Ci<0)?(Nisq/tmp):(0.0));
4428 
4429  // equilibrium hole concentration:
4430  tmp = (fabs(Ci)+sqrt(Cisq+4*Nisq))/2.0;
4431  npTmp = ((Ci<=0)?(tmp):(0.0)) + ((Ci>0)?(Nisq/tmp):(0.0));
4432 
4433  //ExtendedString bulkMat = bulkMaterialVec[mIndex];
4434  ExtendedString bulkMat = bulkMaterial;
4435  bulkMat.toLower();
4436  ExtendedString mater = bcVec[i].material;
4437  mater.toLower();
4438 
4439  if (bcVec[i].VequGiven != 1)
4440  {
4441  if (mater=="neutral")
4442  {
4443  // the doping is n-type.
4444  if (Ci>0)
4445  {
4446  bcVec[i].Vequ = + Vt * log(nnTmp/Ni);
4447  }
4448  else // the doping is p-type.
4449  {
4450  bcVec[i].Vequ = - Vt * log(npTmp/Ni);
4451  }
4452  }
4453  else // this electrode is a schottky barrier.
4454  {
4455  // the doping is n-type.
4456  if (Ci>0)
4457  {
4458  bcVec[i].Vequ = + MaterialSupport::workfunc(mater)
4459  - MaterialSupport::affin(bulkMat)
4460  - 0.5 * MaterialSupport::bandgap(bulkMat, Temp)
4461  + 2.0 * Vt * log(nnTmp/Ni);
4462  }
4463  else // the doping is p-type.
4464  {
4465  bcVec[i].Vequ = + MaterialSupport::workfunc(mater)
4466  - MaterialSupport::affin(bulkMat)
4467  - 0.5 * MaterialSupport::bandgap(bulkMat, Temp)
4468  - 2.0 * Vt * log(npTmp/Ni);
4469  }
4470  }
4471  }
4472 
4473  if (VminBC > bcVec[i].Vequ) VminBC = bcVec[i].Vequ;
4474  if (VmaxBC < bcVec[i].Vequ) VmaxBC = bcVec[i].Vequ;
4475  }
4476 
4477  VoltageOffset_ = -VminBC;
4478 
4479  return bsuccess;
4480 }
4481 
4482 //-----------------------------------------------------------------------------
4483 // Function : Instance::calcDensityBCs
4484 // Purpose : This function sets up the boundary condition variables
4485 // for each electrode.
4486 //
4487 // Special Notes : This function is similar to calcBoundaryConditions, but
4488 // this one only calculates BC's on N and P. Since these
4489 // never change, they only need to be calculated once.
4490 //
4491 // Scope : public
4492 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
4493 // Creation Date : 04/03/03
4494 //-----------------------------------------------------------------------------
4496 {
4497  bool bsuccess = true;
4498 
4499  NnMax = -1.0e+99;
4500  NpMax = -1.0e+99;
4501 
4502  NnMin = +1.0e+99;
4503  NpMin = +1.0e+99;
4504 
4505  // This density boundary condition is from Selberherr,
4506  // enforcing thermal equilibrium and
4507  // vanishing space charge at ohmic contacts
4508  int iBC;
4509  for (iBC=0;iBC<bcVec.size();++iBC)
4510  {
4511  int i1 = bcVec[iBC].meshIndex;
4512  bcVec[iBC].nnbc = 0.5*(sqrt(CVec[i1]*CVec[i1]+4*Ni*Ni)+CVec[i1]);
4513  bcVec[iBC].npbc = 0.5*(sqrt(CVec[i1]*CVec[i1]+4*Ni*Ni)-CVec[i1]);
4514 
4515  if (NnMax < bcVec[iBC].nnbc) NnMax = bcVec[iBC].nnbc;
4516  if (NpMax < bcVec[iBC].npbc) NpMax = bcVec[iBC].npbc;
4517  }
4518 
4519  if (NnMin <= 0) NnMin = 1.56269e-9; // just a guess.
4520  if (NpMin <= 0) NpMin = 1.56269e-9; // just a guess.
4521 
4522  return bsuccess;
4523 }
4524 
4525 //-----------------------------------------------------------------------------
4526 // Function : Instance::calcBoundaryConditions
4527 // Purpose : This function sets up the boundary condition variables
4528 // for each electrode.
4529 //
4530 // Special Notes : If a continuation method is being used, a good parameter
4531 // to vary is the voltage boundary condition on the
4532 // electrodes.
4533 //
4534 // Scope : public
4535 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
4536 // Creation Date : 10/20/02
4537 //-----------------------------------------------------------------------------
4539 {
4540  bool bsuccess = true;
4541  int iBC;
4542 
4543  int bcSize=bcVec.size();
4544  if (getSolverState().PDEcontinuationFlag)
4545  {
4546  for (iBC=0;iBC<bcSize;++iBC)
4547  {
4548  bcVec[iBC].Vbc = bcVec[iBC].Vckt_ramp + bcVec[iBC].Vequ;
4549  }
4550  }
4551  else
4552  {
4553  for (iBC=0;iBC<bcSize;++iBC)
4554  {
4555  bcVec[iBC].Vbc = (bcVec[iBC].Vckt + bcVec[iBC].Vequ);
4556  }
4557  }
4558 
4559  return bsuccess;
4560 }
4561 
4562 //-----------------------------------------------------------------------------
4563 // Function : Instance::obtainNodeVoltages.
4564 //
4565 // Purpose : This function obtains the nodal voltages from the
4566 // solution vector, which are applied as boundary
4567 // conditions on the electrodes.
4568 //
4569 // Special Notes : This was originally part of function obtainSolution, but
4570 // is needed also by function enableContinuation. So I've
4571 // put it in one place.
4572 //
4573 // If voltage limiting is turned on, this is the function
4574 // in which to apply it.
4575 //
4576 // Scope : public
4577 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
4578 // Creation Date : 12/13/02
4579 //-----------------------------------------------------------------------------
4581 {
4582  N_LAS_Vector * solVectorPtr = extData.nextSolVectorPtr;
4583 
4584  int iBC;
4585  for (iBC=0;iBC<bcVec.size();++iBC)
4586  {
4587  bcVec[iBC].Vckt = (*solVectorPtr)[bcVec[iBC].lid];
4588  bcVec[iBC].Vckt /= scalingVars.V0;
4589  }
4590  return true;
4591 }
4592 
4593 //-----------------------------------------------------------------------------
4594 // Function : Instance::applyVoltageLimiting
4595 //
4596 // Purpose : if voltage limiting is turned on, this function
4597 // applies it to the Vckt values.
4598 //
4599 // Special Notes : This is only really set up to work when the 2-level
4600 // Newton is being used.
4601 //
4602 // For now, this is just a test capability.
4603 //
4604 // Scope : public
4605 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
4606 // Creation Date : 12/15/02
4607 //-----------------------------------------------------------------------------
4609 {
4610  for (int iBC=0;iBC<bcVec.size();++iBC)
4611  {
4612  double v1 = bcVec[iBC].Vckt * scalingVars.V0;
4613  double v1_old = bcVec[iBC].Vckt_old * scalingVars.V0;
4614  double delV1 = v1 - v1_old;
4615 
4616  if ( delV1 > 1.25 ) v1 = v1_old + 1.25;
4617 
4618  if ( delV1 < -0.75) v1 = v1_old - 0.75;
4619 
4620  bcVec[iBC].Vckt = v1/scalingVars.V0;
4621  bcVec[iBC].Vckt_final = v1/scalingVars.V0;
4622  }
4623 
4624  return true;
4625 }
4626 
4627 //-----------------------------------------------------------------------------
4628 // Function : Instance::obtainSolution
4629 // Purpose : This function extracts V, nn, and np from the solution
4630 // vector and copies them into local arrays.
4631 // Special Notes :
4632 // Scope : public
4633 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
4634 // Creation Date : 4/13/01
4635 //-----------------------------------------------------------------------------
4637 {
4638  bool bsuccess = true;
4639  bool bs1 = true;
4640  N_LAS_Vector * solVectorPtr = extData.nextSolVectorPtr;
4641 
4642  // First get the two circuit node voltages:
4643  bsuccess = obtainNodeVoltages ();
4644 
4645  // set up the V solution array:
4646  int i;
4647  for (i=0;i<NX;++i)
4648  {
4649  VVec[i] = (*solVectorPtr)[li_Vrowarray[i]];
4650  }
4651 
4652  // If the previous solution is from the nonlinear Poisson solution,
4653  // then calculate what the electron and hole densities must be, and
4654  // place them into the solution vector.
4655 
4656  // If we are past the nonlinear Poisson phase, then simply obtain
4657  // nn and np from the solution vector and move on.
4658 
4659  if (getSolverState().dcopFlag && getSolverState().doubleDCOPStep==0)
4660  {
4662 
4663  for (i=0;i<NX;++i)
4664  {
4665  (*solVectorPtr)[li_Nrowarray[i]] = nnVec[i];
4666  (*solVectorPtr)[li_Prowarray[i]] = npVec[i];
4667  }
4668  }
4669  else
4670  {
4671  for (i=0;i<NX;++i)
4672  {
4673  nnVec[i] = (*solVectorPtr)[li_Nrowarray[i]];
4674 
4675 #ifdef Xyce_PDE_DENSITY_CONSTRAINT
4676  if (nnVec[i] < 0.0) nnVec[i] = 0.0;
4677 #endif
4678 
4679  npVec[i] = (*solVectorPtr)[li_Prowarray[i]];
4680 
4681 #ifdef Xyce_PDE_DENSITY_CONSTRAINT
4682  if (npVec[i] < 0.0) npVec[i] = 0.0;
4683 #endif
4684  }
4685 
4686  // now set boundary conditions:
4687  // if the circuit is coupled to the PDE device, then bc's
4688  // must be updated everytime.
4689  //
4690  // If the circuit and PDE device are not coupled, then the
4691  // circuit node voltages can be considered constant, and the
4692  // BC's only need updating at the first Newton step.
4693  if ( !(getSolverState().twoLevelNewtonCouplingMode==Nonlinear::INNER_PROBLEM))
4694  {
4695  bs1 = calcBoundaryConditions (); bsuccess = bsuccess && bs1;
4696  }
4697  else
4698  {
4699  if (getSolverState().newtonIter == 0)
4700  {
4701  bs1 = calcBoundaryConditions (); bsuccess = bsuccess && bs1;
4702  }
4703  }
4704  }
4705 
4706  return bsuccess;
4707 }
4708 
4709 //-----------------------------------------------------------------------------
4710 // Function : Instance::outputPlotFiles
4711 // Purpose :
4712 // Special Notes :
4713 // Scope : public
4714 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
4715 // Creation Date : 7/22/03
4716 //-----------------------------------------------------------------------------
4718 {
4719  bool bsuccess = true;
4720  bool bs1 = true;
4721  bool skipOutput = false;
4722 
4723  // usually, don't bother outputting nonlinear Poisson result.
4724  if (equationSet == 0 && !(outputNLPoisson)) return bsuccess;
4725 
4726  // If using output interval, check if enough time has passed to do
4727  // another output. (only applies for transient - not DCOP).
4728  if ( !(getSolverState().dcopFlag) &&
4729  !(getSolverState().forceFinalOutput) &&
4731  {
4732  double outMult = static_cast<double> (outputIndex);
4733  double nextOutputTime = outMult * outputInterval;
4734 
4735  if (nextOutputTime > getSolverState().currTime)
4736  {
4737  skipOutput = true;
4738  }
4739  }
4740 
4741  // If this is a "forced" final output, make sure that it didn't already output.
4742  // This can happen if the output interval is an exact multiple of the
4743  // total simulation time.
4745  getSolverState().currTime==lastOutputTime) skipOutput=true;
4746 
4747  if (skipOutput) return bsuccess;
4748  ++outputIndex;
4750 
4751 #ifdef Xyce_DEBUG_DEVICE
4752  Xyce::dout() << std::endl << "Doing an output at time = " << getSolverState().currTime << std::endl;
4753 #endif
4754 
4755  if (tecplotLevel > 0) {bs1 = outputTecplot (); bsuccess = bsuccess && bs1;}
4756  if (sgplotLevel > 0) {bs1 = outputSgplot (); bsuccess = bsuccess && bs1;}
4757 
4758  return bsuccess;
4759 }
4760 
4761 //-----------------------------------------------------------------------------
4762 // Function : Instance::outputTecplot
4763 // Purpose : This function outputs a file which is easily plottable
4764 // by tecplot. Simply run tecplot "filename.dat" <return>
4765 //
4766 //
4767 // Special Notes : This file can also be plotted using gnuplot. If the
4768 // name of the file is "Z1DIODE_000.dat", plot inside of
4769 // gnuplot using:
4770 //
4771 // plot "Z1DIODE_000.dat" using 1:3 w l
4772 //
4773 // or, if you want a log plot, for the doping:
4774 //
4775 // plot "Z1DIODE_000.dat" using 1:(log($6)) w l
4776 //
4777 // The "$" and both pairs of parens are needed for some
4778 // reason.
4779 //
4780 // Special Notes : If tecplot level is set to 1, then output each dataset
4781 // in a separate file. If not, then append to a single file.
4782 //
4783 // Scope : public
4784 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
4785 // Creation Date : 4/13/01
4786 //-----------------------------------------------------------------------------
4788 {
4789  int i;
4790  char filename[32]; for(i=0;i<32;++i) filename[i] = static_cast<char>(0);
4791 
4792  if (tecplotLevel == 1)
4793  {
4794  sprintf(filename,"%s_%03d.dat",outputName.c_str(),callsOTEC);
4795  }
4796  else
4797  {
4798  sprintf(filename,"%s.dat",outputName.c_str());
4799  }
4800 
4801  double time = getSolverState().currTime;
4802  FILE *fp1;
4803 
4804  if (tecplotLevel == 1)
4805  {
4806  fp1 = fopen(filename,"w");
4807  }
4808  else
4809  {
4810  if (callsOTEC <= 0)
4811  {
4812  fp1 = fopen(filename,"w");
4813  }
4814  else
4815  {
4816  fp1 = fopen(filename,"a");
4817  }
4818  }
4819 
4820  if (tecplotLevel == 1)
4821  {
4822  if (equationSet == 0)
4823  {
4824  fprintf(fp1,
4825  " TITLE = \"Spatially Dependent data for PDE diode: %s time = %20.12e seconds. equation set = nonlinear Poisson\",\n",
4826  outputName.c_str(),time);
4827  }
4828  else
4829  {
4830  fprintf(fp1,
4831  " TITLE = \"Spatially Dependent data for PDE diode: %s time = %20.12e seconds. equation set = drift diffusion\",\n",
4832  outputName.c_str(),time);
4833  }
4834  }
4835  else
4836  {
4837  if (callsOTEC <= 0)
4838  {
4839  fprintf(fp1,
4840  " TITLE = \"Spatially Dependent data for PDE diode: %s time = %20.12e seconds.\",\n",
4841  outputName.c_str(),time);
4842  }
4843  }
4844 
4845  int rSize=0;
4846  int cSize=0;
4847 
4848  if (callsOTEC <= 0 || tecplotLevel == 1)
4849  {
4850  fprintf(fp1,"%s","\tVARIABLES = \"X \",\n");
4851 
4852  fprintf(fp1,"%s","\t \"V \",\n");
4853  fprintf(fp1,"%s","\t \"nn (electron dens.) \",\n");
4854  fprintf(fp1,"%s","\t \"np (hole dens.) \",\n");
4855  fprintf(fp1,"%s","\t \"nn*np (carrier product) \",\n");
4856  fprintf(fp1,"%s","\t \"Dopant density \",\n");
4857  fprintf(fp1,"%s","\t \"fabs(Dopant density)\",\n");
4858  fprintf(fp1,"%s","\t \"electron lifetime \",\n");
4859  fprintf(fp1,"%s","\t \"hole lifetime \",\n");
4860  //fprintf(fp1,"%s","\t \"electron mobility \",\n");
4861  //fprintf(fp1,"%s","\t \"hole mobility \",\n");
4862  fprintf(fp1,"%s","\t \"Jn \",\n");
4863  fprintf(fp1,"%s","\t \"Jp \",\n");
4864  fprintf(fp1,"%s","\t \"R \",\n");
4865  fprintf(fp1,"%s","\t \"Ex \",\n");
4866  fprintf(fp1,"%s","\t \"Idispl \", \n");
4867 
4868  fprintf(fp1,"%s","\t \"Conduction Band, uncorrected \", \n");
4869  fprintf(fp1,"%s","\t \"Valance Band, uncorrected \", \n");
4870 
4871  fprintf(fp1,"%s","\t \"Band-gap narrowing, Conduction Band \", \n");
4872  fprintf(fp1,"%s","\t \"Band-gap narrowing, Valance Band \", \n");
4873 
4874  fprintf(fp1,"%s","\t \"Conduction Band, corrected for BGN \", \n");
4875  fprintf(fp1,"%s","\t \"Valance Band, corrected for BGN \", \n");
4876  fprintf(fp1,"%s","\t \"Fermi Level\", \n");
4877 
4878  fprintf(fp1,"%s","\t \"conduction band DOS\", \n");
4879  fprintf(fp1,"%s","\t \"valance band DOS\", \n");
4880 
4881  fprintf(fp1,"\t \"n0, Fermi-Dirac \",\n");
4882  fprintf(fp1,"\t \"p0, Fermi-Dirac \",\n");
4883  fprintf(fp1,"\t \"n0, Boltzmann\",\n");
4884  fprintf(fp1,"\t \"p0, Boltzmann\",\n");
4885  fprintf(fp1,"\t \"np0 Fermi-Dirac\",\n");
4886  fprintf(fp1,"\t \"Ni^2 (Boltzmann np0)\",\n");
4887  fprintf(fp1,"%s","\t \"Ni (intrinsic concentration) \", \n");
4888  }
4889 
4890  fprintf(fp1,"\tZONE F=POINT,I=%d", NX);
4891 
4892  if (getSolverState().dcopFlag)
4893  {
4894  fprintf(fp1," T = \"DCOP step = %d\" \n", callsOTEC);
4895  }
4896  else
4897  {
4898  fprintf(fp1," T = \"time step = %d time = %20.12e\" AUXDATA time = \"%20.12e seconds\" \n", callsOTEC , time, time);
4899  }
4900 
4901  double vcorrection = 0.0;
4903  {
4904  if (offsetWithFirstElectrode_) // not the default. This is here to match Wampler's 1D code.
4905  {
4906  vcorrection = -VVec[0]*scalingVars.V0;
4907  }
4908  else
4909  {
4910  vcorrection = VoltageOffset_;
4911  }
4912  }
4913 
4914  for (i=0;i<NX;++i)
4915  {
4916  fprintf(fp1," %20.12e",xVec[i]*scalingVars.x0);
4917  fprintf(fp1," %20.12e", (VVec[i]*scalingVars.V0 + vcorrection) );
4918  fprintf(fp1," %20.12e",nnVec[i]*scalingVars.C0);
4919  fprintf(fp1,"%s","\n");
4920  fprintf(fp1," %20.12e",npVec[i]*scalingVars.C0);
4921  fprintf(fp1," %20.12e",nnVec[i]*scalingVars.C0*npVec[i]*scalingVars.C0);
4922  fprintf(fp1," %20.12e",CVec[i]*scalingVars.C0);
4923  fprintf(fp1," %20.12e",fabs(CVec[i]*scalingVars.C0));
4924  fprintf(fp1,"%s","\n");
4925  fprintf(fp1," %20.12e",tnVec[i]*scalingVars.t0);
4926  fprintf(fp1," %20.12e",tpVec[i]*scalingVars.t0);
4927  fprintf(fp1,"%s","\n");
4928  fprintf(fp1," %20.12e",JnxVec[i]*scalingVars.J0);
4929  fprintf(fp1," %20.12e",JpxVec[i]*scalingVars.J0);
4930  fprintf(fp1,"%s","\n");
4931  fprintf(fp1," %20.12e",RVec[i]*scalingVars.R0);
4932  fprintf(fp1," %20.12e",ExVec[i]*scalingVars.E0);
4933  fprintf(fp1," %20.12e",displCurrent[i]*scalingVars.J0);
4934  fprintf(fp1,"%s","\n");
4935 
4936  fprintf(fp1," %20.12e", EcVec[i]);
4937  fprintf(fp1," %20.12e", EvVec[i]);
4938  fprintf(fp1," %20.12e", bgnCVec[i]);
4939  fprintf(fp1," %20.12e", bgnVVec[i]);
4940 
4941  double con = EcVec[i]-bgnCVec[i];
4942  double val = EvVec[i]+bgnVVec[i];
4943  fprintf(fp1," %20.12e", con);
4944  fprintf(fp1," %20.12e", val);
4945 
4946  fprintf(fp1,"%s","\n");
4947 
4948  fprintf(fp1," %20.12e", EfVec[i]);
4949  fprintf(fp1," %20.12e", NcVec[i]);
4950  fprintf(fp1," %20.12e", NvVec[i]);
4951 
4952  double Ni=NiVec[i];
4953  double n0,p0;
4954  n0_and_p0(
4955  (nnVec[i]*scalingVars.C0), (npVec[i]*scalingVars.C0),
4956  Ni, con, val, NcVec[i], NvVec[i], Temp, n0, p0);
4957 
4958  fprintf(fp1," %20.12e",n0);
4959  fprintf(fp1," %20.12e",p0);
4960 
4961  if (CdonorVec[i] > CacceptorVec[i])
4962  {
4963  n0 = (CdonorVec[i]-CacceptorVec[i]); p0=1.0;
4964  if (n0 != 0.0) { p0 = Ni*Ni/n0; }
4965  }
4966  else
4967  {
4968  p0 = (CacceptorVec[i]-CdonorVec[i]); n0=1.0;
4969  if (p0 != 0.0) { n0 = Ni*Ni/p0; }
4970  }
4971  fprintf(fp1," %20.12e",n0);
4972  fprintf(fp1," %20.12e",p0);
4973 
4974  double np0 = np0_calculation(
4975  (nnVec[i]*scalingVars.C0), (npVec[i]*scalingVars.C0),
4976  Ni, con, val, NcVec[i], NvVec[i], Temp);
4977 
4978  fprintf(fp1," %20.12e",np0);
4979  fprintf(fp1," %20.12e",Ni*Ni);
4980  fprintf(fp1," %20.12e",Ni);
4981  fprintf(fp1,"\n");
4982 
4983  fprintf(fp1,"%s","\n");
4984  }
4985 
4986  ++callsOTEC;
4987  fclose(fp1);
4988 
4989  return true;
4990 }
4991 
4992 //-----------------------------------------------------------------------------
4993 // Function : Instance::outputSgplot
4994 // Purpose : Outputs data in a format readable by the simgen plotting
4995 // tools.
4996 //
4997 // Special Notes : Despite the name, the output file should be plotted using
4998 // the program oneplot(a 1D plotter), not sgplot
4999 // (a 2d plotter).
5000 //
5001 // Scope : public
5002 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
5003 // Creation Date : 4/13/01
5004 //-----------------------------------------------------------------------------
5006 {
5007  int i;
5008  char fileName[32];
5009 
5010  static const int LEN_IDENT2 = 31;
5011 
5012  for (i = 0 ; i < 32; ++i)
5013  fileName[i] = static_cast<char>(0);
5014 
5015  sprintf(fileName,"%s_%03d.res",outputName.c_str(),callsOSG);
5016  ++callsOSG;
5017 
5018  FILE * handle1 = fopen(fileName, "w");
5019 
5020  UINT numArrays = 3;
5021  double timeVar = 0.0;
5022 
5023  UINT inx = NX;
5024 
5025  char title[64];
5026  sprintf(title, "%s", "Xyce diodePDE 1D output");
5027 
5028  fwrite(&inx , sizeof( UINT), 1, handle1); // array size.
5029  fwrite(&numArrays, sizeof( UINT), 1, handle1); // number of arrays, besides x.
5030  fwrite( title , sizeof( char),64, handle1); // title
5031  fwrite(&timeVar , sizeof(double), 1, handle1); // time.
5032 
5033  char names[3][LEN_IDENT2];
5034  sprintf(names[0], "%s", "V");
5035  sprintf(names[1], "%s", "Ne");
5036  sprintf(names[2], "%s", "Np");
5037 
5038  // output the variable names, other than x:
5039  for(i=0;i<numArrays;++i)
5040  {
5041  fwrite(names[i], sizeof(char),(LEN_IDENT2), handle1);
5042  }
5043 
5044  double vcorrection = 0.0;
5046  {
5047  if (offsetWithFirstElectrode_) // not the default. This is here to match Wampler's 1D code.
5048  {
5049  vcorrection = -VVec[0]*scalingVars.V0;
5050  }
5051  else
5052  {
5053  vcorrection = VoltageOffset_;
5054  }
5055  }
5056 
5057  for (i=0;i<inx;++i)
5058  {
5059  xVec[i] *= scalingVars.x0;
5060  VVec[i] *= scalingVars.V0 + vcorrection;
5061  nnVec[i] *= scalingVars.C0;
5062  npVec[i] *= scalingVars.C0;
5063  }
5064 
5065  // output x-axis:
5066  fwrite( &xVec[0], sizeof(double),inx , handle1 );
5067 
5068  // output V
5069  fwrite( &VVec[0], sizeof(double),inx , handle1 );
5070 
5071  // output nn
5072  fwrite( &nnVec[0], sizeof(double),inx , handle1 );
5073 
5074  // output np
5075  fwrite( &npVec[0], sizeof(double),inx , handle1 );
5076 
5077  for (i=0;i<inx;++i)
5078  {
5079  xVec[i] /= scalingVars.x0;
5080  VVec[i] /= scalingVars.V0;
5081  nnVec[i] /= scalingVars.C0;
5082  npVec[i] /= scalingVars.C0;
5083  }
5084 
5085  fclose(handle1);
5086  return true;
5087 }
5088 
5089 //-----------------------------------------------------------------------------
5090 // Function : Instance::calcRecombination
5091 // Purpose :
5092 // Special Notes : This function assumes scaling is turned on.
5093 // Scope : public
5094 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
5095 // Creation Date : 4/15/01
5096 //-----------------------------------------------------------------------------
5098 {
5099  if (!includeAugerRecomb && !includeSRHRecomb) return true;
5100 
5101  for (int i=0;i<NX;++i)
5102  {
5103  double Rsrh=0.0;
5104  double Raug=0.0;
5105 
5106  double n = nnVec[i];
5107  double p = npVec[i];
5108  double tn = tnVec[i];
5109  double tp = tpVec[i];
5110 
5111  if (includeSRHRecomb)
5112  {
5113  Rsrh = MaterialSupport::calcRsrh (bulkMaterial, Ni,n,p,tn,tp);
5114  }
5115 
5116  if (includeAugerRecomb)
5117  {
5119  }
5120 
5121  RVec[i] = (Rsrh + Raug);
5122  }
5123 
5124  return true;
5125 }
5126 
5127 //-----------------------------------------------------------------------------
5128 // Function : Instance::pdRecombination
5129 // Purpose : This function sets up the arrays of partial derivatives
5130 // associated with the recombination term.
5131 // Special Notes : This function assumes scaling is turned on.
5132 // Scope : public
5133 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
5134 // Creation Date : 4/22/01
5135 //-----------------------------------------------------------------------------
5137 {
5138  if (!includeAugerRecomb && !includeSRHRecomb) return true;
5139 
5140  int i;
5141  double A, B, C;
5142  double dAdn, dAdp;
5143  double dBdn, dBdp;
5144 
5145  for (i=0;i<NX;++i)
5146  {
5147  double dRsrhdn=0.0;
5148  double dRsrhdp=0.0;
5149  double dRaugdn=0.0;
5150  double dRaugdp=0.0;
5151 
5152  // Rsrh derivatives: checklater.
5153  // (Rsrch = A*B)
5154 
5155  double n = nnVec[i];
5156  double p = npVec[i];
5157  double tn = tnVec[i];
5158  double tp = tpVec[i];
5159 
5160  if (includeSRHRecomb)
5161  {
5162  dRsrhdn = MaterialSupport::pdRsrhN(bulkMaterial,Ni,n,p,tn,tp);
5163  dRsrhdp = MaterialSupport::pdRsrhP(bulkMaterial,Ni,n,p,tn,tp);
5164  }
5165 
5166  if (includeAugerRecomb)
5167  {
5168  dRaugdn = MaterialSupport::pdRaugN(bulkMaterial,Ni,n,p);
5169  dRaugdp = MaterialSupport::pdRaugP(bulkMaterial,Ni,n,p);
5170  }
5171 
5172  dRdnVec[i] = dRsrhdn + dRaugdn;
5173  dRdpVec[i] = dRsrhdp + dRaugdp;
5174  }
5175 
5176  return true;
5177 }
5178 
5179 //-----------------------------------------------------------------------------
5180 // Function : Instance::calcElectronCurrent
5181 // Purpose :
5182 // Special Notes : This function assumes scaling is on.
5183 // Scope : public
5184 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
5185 // Creation Date : 4/15/01
5186 //-----------------------------------------------------------------------------
5188 {
5189  Ut = Vt/scalingVars.V0;
5190  for (int i=0;i<LX;++i)
5191  {
5192  if (i>0 && i< LX && heterojunctionSten[i]!=0 && heterojunctionSten[i+1]!=0 )
5193  {
5194  JnxVec[i] = JnxVec[i-1]; // kludge for now
5195  }
5196  else
5197  {
5198  JnxVec[i] =
5199  -J_qdep(nnVec[i], nnVec[i+1], ExVec[i], unE_Vec[i], dxVec[i],-1);
5200  }
5201  }
5202  JnxVec[LX] = JnxVec[LX-1];
5203 
5204  return true;
5205 }
5206 
5207 //-----------------------------------------------------------------------------
5208 // Function : Instance::pdElectronCurrent
5209 // Purpose : This function sets up the arrays of partial derivatives
5210 // associated with electron current.
5211 // Special Notes : This function assumes scaling is on.
5212 // Scope : public
5213 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
5214 // Creation Date : 4/22/01
5215 //-----------------------------------------------------------------------------
5217 {
5218  Ut = Vt/scalingVars.V0;
5219 
5220  for (int i=0;i<LX;++i)
5221  {
5222  dJndn1Vec[i] =
5223  -dJdn1_qdep(nnVec[i], nnVec[i+1], ExVec[i], unE_Vec[i], dxVec[i],-1);
5224 
5225  dJndn2Vec[i] =
5226  -dJdn2_qdep(nnVec[i], nnVec[i+1], ExVec[i], unE_Vec[i], dxVec[i],-1);
5227 
5228  dJndV1Vec[i] =
5229  -dJdV1_qdep(nnVec[i], nnVec[i+1], ExVec[i], unE_Vec[i], dxVec[i],-1);
5230 
5231  dJndV2Vec[i] =
5232  -dJdV2_qdep(nnVec[i], nnVec[i+1], ExVec[i], unE_Vec[i], dxVec[i],-1);
5233 
5234  dJndp1Vec[i] =
5235  -dJdp1_qdep(nnVec[i], nnVec[i+1], ExVec[i], unE_Vec[i], dxVec[i],-1);
5236 
5237  dJndp2Vec[i] =
5238  -dJdp2_qdep(nnVec[i], nnVec[i+1], ExVec[i], unE_Vec[i], dxVec[i],-1);
5239  }
5240 
5241  dJndn1Vec[LX] = dJndn1Vec[LX-1];
5242  dJndn2Vec[LX] = dJndn2Vec[LX-1];
5243  dJndV1Vec[LX] = dJndV1Vec[LX-1];
5244  dJndV2Vec[LX] = dJndV2Vec[LX-1];
5245  dJndp1Vec[LX] = dJndp1Vec[LX-1];
5246  dJndp2Vec[LX] = dJndp2Vec[LX-1];
5247 
5248  return true;
5249 }
5250 
5251 //-----------------------------------------------------------------------------
5252 // Function : Instance::calcHoleCurrent
5253 // Purpose : This function assumes scaling is on.
5254 // Special Notes :
5255 // Scope : public
5256 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
5257 // Creation Date : 4/15/01
5258 //-----------------------------------------------------------------------------
5260 {
5261  Ut = Vt/scalingVars.V0;
5262 
5263  for (int i=0;i<LX;++i)
5264  {
5265  if (i>0 && i< LX && heterojunctionSten[i]!=0 && heterojunctionSten[i+1]!=0 )
5266  {
5267  JpxVec[i] = JpxVec[i-1]; //kludge for now
5268  }
5269  else
5270  {
5271  JpxVec[i] =
5272  J_qdep(npVec[i], npVec[i+1], ExVec[i], upE_Vec[i], dxVec[i],+1);
5273  }
5274  }
5275 
5276  JpxVec[LX] = JpxVec[LX-1];
5277 
5278  return true;
5279 }
5280 
5281 //-----------------------------------------------------------------------------
5282 // Function : Instance::pdHoleCurrent
5283 // Purpose : This function sets up the arrays of partial derivatives
5284 // associated with the hole current.
5285 // Special Notes : This function assumes scaling is on.
5286 // Scope : public
5287 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
5288 // Creation Date : 4/22/01
5289 //-----------------------------------------------------------------------------
5291 {
5292  Ut = Vt/scalingVars.V0;
5293 
5294  for (int i=0;i<LX;++i)
5295  {
5296  dJpdp1Vec[i] =
5297  dJdn1_qdep(npVec[i], npVec[i+1], ExVec[i], upE_Vec[i], dxVec[i],+1);
5298 
5299  dJpdp2Vec[i] =
5300  dJdn2_qdep(npVec[i], npVec[i+1], ExVec[i], upE_Vec[i], dxVec[i],+1);
5301 
5302  dJpdV1Vec[i] =
5303  dJdV1_qdep(npVec[i], npVec[i+1], ExVec[i], upE_Vec[i], dxVec[i],+1);
5304 
5305  dJpdV2Vec[i] =
5306  dJdV2_qdep(npVec[i], npVec[i+1], ExVec[i], upE_Vec[i], dxVec[i],+1);
5307 
5308  dJpdn1Vec[i] =
5309  -dJdp1_qdep(npVec[i], npVec[i+1], ExVec[i], upE_Vec[i], dxVec[i],+1);
5310 
5311  dJpdn2Vec[i] =
5312  -dJdp2_qdep(npVec[i], npVec[i+1], ExVec[i], upE_Vec[i], dxVec[i],+1);
5313  }
5314 
5315  dJpdn1Vec[LX] = dJpdn1Vec[LX-1];
5316  dJpdn2Vec[LX] = dJpdn2Vec[LX-1];
5317  dJpdV1Vec[LX] = dJpdV1Vec[LX-1];
5318  dJpdV2Vec[LX] = dJpdV2Vec[LX-1];
5319  dJpdn1Vec[LX] = dJpdn1Vec[LX-1];
5320  dJpdn2Vec[LX] = dJpdn2Vec[LX-1];
5321 
5322  return true;
5323 }
5324 
5325 //-----------------------------------------------------------------------------
5326 // Function : Instance::calcEfield
5327 // Purpose : This function works with or without scaling.
5328 // Special Notes :
5329 // Scope : public
5330 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
5331 // Creation Date : 4/15/01
5332 //-----------------------------------------------------------------------------
5334 {
5335  double absEx;
5336  Emax = 0.0;
5337 
5338  for (int i=0;i<LX;++i)
5339  {
5340  if (i>0 && i< LX && heterojunctionSten[i]!=0 && heterojunctionSten[i+1]!=0 )
5341  {
5342  ExVec[i] = ExVec[i-1]; // kludge for now (to avoid nan's or inf's in the output)
5343  }
5344  else
5345  {
5346  ExVec[i] = -(VVec[i+1] - VVec[i])/dxVec[i];
5347  }
5348 
5349  absEx = fabs(ExVec[i]);
5350  if (absEx > Emax) Emax = absEx;
5351  }
5352  Emax *= scalingVars.E0;
5353 
5354  ExVec[LX] = ExVec[LX-1];
5355 
5356  return true;
5357 }
5358 
5359 //-----------------------------------------------------------------------------
5360 // Function : Instance::enablePDEContinuation
5361 // Purpose : Sets up the various parameters neccessary for a continuation
5362 // calculation. Mainly, it sets up the voltage step size
5363 // for all the voltage BC's.
5364 // Special Notes :
5365 //
5366 // Scope : public
5367 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
5368 // Creation Date : 10/22/02
5369 //-----------------------------------------------------------------------------
5371 {
5372  bool bnoChange = true;
5373  int iBC;
5374  int bcSize=bcVec.size();
5375 
5376  continuationAlpha = 1.0;
5377 
5379  {
5380  for (iBC=0;iBC<bcSize;++iBC)
5381  {
5382  bcVec[iBC].Vckt_old = bcVec[iBC].Vckt;
5383  }
5384  }
5385 
5386  obtainNodeVoltages ();
5387 
5388  for (iBC=0;iBC<bcSize;++iBC)
5389  {
5390  bcVec[iBC].Vckt_final = bcVec[iBC].Vckt;
5391  bcVec[iBC].Vckt_orig = bcVec[iBC].Vckt;
5392  }
5393 
5394  // This (voltlim) is a very new thing. Use carefully...
5395  if (getDeviceOptions().voltageLimiterFlag && voltLimFlag)
5396  {
5398  }
5399 
5400  for (iBC=0;iBC<bcSize;++iBC)
5401  {
5402  double dV,tmp1V, tmp2V;
5403  tmp1V = bcVec[iBC].Vckt_final;
5404  tmp2V = bcVec[iBC].Vckt_old;
5405  dV = tmp1V - tmp2V;
5406 
5407  bcVec[iBC].Vckt_delta = dV;
5408 
5409  bcVec[iBC].Vckt_deltaC = dV/
5410  (static_cast<double>(getSolverState().maxPDEContinuationSteps));
5411 
5412  // if this deltaC is too big, then we need to change the
5413  // number of continuation steps.
5414  double maxDelta = maxVoltDelta;
5415 
5416  if (fabs(bcVec[iBC].Vckt_deltaC) > maxDelta)
5417  {
5418  int tmp_steps = static_cast<int>(fabs(dV)/maxDelta) + 1;
5420 
5421  bcVec[iBC].Vckt_deltaC = dV/
5422  static_cast<double>(getSolverState().maxPDEContinuationSteps);
5423  }
5424 
5425  if (fabs(dV) > 1.0e-3) bnoChange = false;
5426 
5427  bcVec[iBC].Vckt_ramp = bcVec[iBC].Vckt_old;
5428  bcVec[iBC].Vckt_ramp_old = bcVec[iBC].Vckt_old;
5429  }
5430 
5431 #if 0
5432  // now set up any continuation params associated with photogen.
5433  bool bnoChangePhotogen;
5434 
5435  bnoChangePhotogen = enablePhotogenContinuation ();
5436 
5437  bnoChange = bnoChange && bnoChangePhotogen;
5438 #endif
5439 
5441 
5442  // if none of the boundary conditions have changed, then
5443  // return a false.
5444  return (!bnoChange);
5445 }
5446 
5447 //-----------------------------------------------------------------------------
5448 // Function : Instance::disablePDEContinuation
5449 // Purpose :
5450 // Special Notes :
5451 // Scope : public
5452 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
5453 // Creation Date : 10/22/02
5454 //-----------------------------------------------------------------------------
5456 {
5457  int iBC;
5458 
5459  int bcSize=bcVec.size();
5460  for (iBC=0;iBC<bcSize;++iBC)
5461  {
5462  bcVec[iBC].Vckt_old = bcVec[iBC].Vckt_final;
5463  }
5464 
5465 #if 0
5467 #endif
5468 
5469  return true;
5470 }
5471 
5472 //-----------------------------------------------------------------------------
5473 // Function : Instance::setPDEContinuationAlpha
5474 // Purpose :
5475 // Special Notes :
5476 // Scope : public
5477 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
5478 // Creation Date : 07/18/03
5479 //-----------------------------------------------------------------------------
5481 {
5482 #ifdef Xyce_DEBUG_DEVICE
5483  Xyce::dout() << section_divider << std::endl;
5484  Xyce::dout() << "Instance::setPDEContinuationAlpha" << std::endl;
5485 #endif
5486 
5487 
5488  // First do the voltage boundary conditions:
5489  int bcSize=bcVec.size();
5490  for (int iBC=0;iBC<bcSize;++iBC)
5491  {
5492  bcVec[iBC].Vckt_ramp = bcVec[iBC].Vckt_old + (bcVec[iBC].Vckt_delta)*alpha;
5493 
5494  // make sure we haven't gone too far:
5495  if ((bcVec[iBC].Vckt_delta > 0 && bcVec[iBC].Vckt_ramp > bcVec[iBC].Vckt_final) ||
5496  (bcVec[iBC].Vckt_delta <= 0 && bcVec[iBC].Vckt_ramp <= bcVec[iBC].Vckt_final) )
5497  {
5498  bcVec[iBC].Vckt_ramp = bcVec[iBC].Vckt_final;
5499  }
5500 
5501 #ifdef Xyce_DEBUG_DEVICE
5502  Xyce::dout() << " " << bcVec[iBC].eName << " Vckt_ramp = " << bcVec[iBC].Vckt_ramp << std::endl;
5503 #endif
5504  }
5505 
5506 #if 0
5507  // now do the photogeneration term, if neccessary:
5509 
5510  // make sure we haven't gone too far:
5511  if ((photoA1_Delta > 0 && photoA1_ramp > photoA1_final) ||
5513  {
5515  }
5516 
5517 #ifdef Xyce_DEBUG_DEVICE
5518  Xyce::dout() << " photoA1_ramp = " << photoA1_ramp << std::endl;
5519  Xyce::dout() << section_divider << std::endl;
5520 #endif
5521 #endif
5522 }
5523 
5524 Device *Traits::factory(const Configuration &configuration, const FactoryBlock &factory_block)
5525 {
5526 
5527  Device *device = new DeviceMaster<Traits>(configuration, factory_block, factory_block.solverState_, factory_block.deviceOptions_);
5528 
5529  return device;
5530 }
5531 
5533 {
5535  .registerDevice("pde", 1)
5536  .registerModelType("pde", 1)
5537  .registerModelType("zod", 1);
5538 }
5539 
5540 } // namespace DiodePDE
5541 } // namespace Device
5542 } // namespace Xyce