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