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