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