Xyce  6.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
N_DEV_RxnSet.C
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // Copyright Notice
3 //
4 // Copyright 2002 Sandia Corporation. Under the terms
5 // of Contract DE-AC04-94AL85000 with Sandia Corporation, the U.S.
6 // Government retains certain rights in this software.
7 //
8 // Xyce(TM) Parallel Electrical Simulator
9 // Copyright (C) 2002-2014 Sandia Corporation
10 //
11 // This program is free software: you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation, either version 3 of the License, or
14 // (at your option) any later version.
15 //
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with this program. If not, see <http://www.gnu.org/licenses/>.
23 // ----------------------------------------------------------------------------
24 
25 //-------------------------------------------------------------------------
26 // Filename : $RCSfile: N_DEV_RxnSet.C,v $
27 //
28 // Purpose :
29 //
30 // Special Notes :
31 //
32 // Creator : Eric R. Keiter, SNL
33 //
34 // Creation Date : 02/09/08
35 //
36 // Revision Information:
37 // ---------------------
38 //
39 // Revision Number: $Revision: 1.40.2.1 $
40 // Revision Date : $Date: 2014/02/25 22:30:29 $
41 //
42 // Current Owner : $Author: dgbaur $
43 //-------------------------------------------------------------------------
44 
45 #include <Xyce_config.h>
46 
47 
48 // ---------- Standard Includes ----------
49 
50 #include <N_UTL_Misc.h>
51 #include <iostream>
52 
53 #ifdef HAVE_CMATH
54 #include <cmath>
55 #else
56 #include <math.h>
57 #endif
58 
59 #ifdef Xyce_DEBUG_DEVICE
60 #ifdef HAVE_CSTDIO
61 #include <cstdio>
62 #else
63 #include <stdio.h>
64 #endif
65 #endif
66 
67 
68 // ---------- Xyce Includes ----------
69 #include <N_DEV_Const.h>
70 #include <N_DEV_DeviceOptions.h>
71 #include <N_DEV_DeviceMaster.h>
72 #include <N_DEV_ExternData.h>
73 #include <N_DEV_MatrixLoadData.h>
74 #include <N_DEV_Message.h>
75 #include <N_DEV_Region.h>
76 #include <N_DEV_RegionData.h>
77 #include <N_DEV_SolverState.h>
78 #include <N_DEV_SpecieSource.h>
79 
80 #include <N_LAS_Matrix.h>
81 #include <N_LAS_MultiVector.h>
82 #include <N_LAS_Vector.h>
83 
84 #include <N_DEV_RxnSet.h>
85 #include <N_DEV_ReactionNetwork.h>
86 
87 #include <N_UTL_Expression.h>
88 
89 namespace Xyce {
90 namespace Device {
91 
92 
93 namespace RxnSet {
94 
95 
97 {
98 p.addPar ("TEMP", 0.0, false, ParameterType::TIME_DEP, &RxnSet::Instance::TEMP, NULL, STANDARD, CAT_NONE, "");
99 
100  // user-specified scaling vars:
101  p.addPar ("X0", 1.0e-7, false, ParameterType::NO_DEP, &RxnSet::Instance::x0_user, NULL, U_NONE, CAT_NONE, "Length Scalar.");
102  p.addPar ("C0", 1.0e+12, false, ParameterType::NO_DEP, &RxnSet::Instance::C0_user, NULL, U_NONE, CAT_NONE, "Concentration Scalar.");
103  p.addPar ("t0", 1.0e-6, false, ParameterType::NO_DEP, &RxnSet::Instance::t0_user, NULL, U_NONE, CAT_NONE, "Time Scalar.");
104  p.addPar ("outputXscalar", 1.0, false, ParameterType::NO_DEP, &RxnSet::Instance::outputXscalar, NULL, U_NONE, CAT_NONE, "Scalar for X axis in tecplot file (default unit is cm)");
105  p.addPar ("OUTPUTINTERVAL", 0.0, false, ParameterType::NO_DEP, &RxnSet::Instance::outputInterval, NULL, U_NONE, CAT_NONE, "Output Interval(sec)");
106 
107  // Set up map for non-double precision variables:
108 
109  p.addPar ("COPYRXN", false, false, ParameterType::NO_DEP, &RxnSet::Instance::reactionFileCopyFlag, NULL, U_LOGIC, CAT_NONE, "Flag for processing chemistry file only once, then copied to other mesh points.");
110  p.addPar ("SCALERXN", true, false, ParameterType::NO_DEP, &RxnSet::Instance::useScaledVariablesFlag, NULL, U_LOGIC, CAT_NONE, "Flag for applying scaling to the reaction equations.");
111  p.addPar ("DIFFUSION", false, false, ParameterType::NO_DEP, &RxnSet::Instance::diffusionFlag, &RxnSet::Instance::diffusionFlagGiven, U_LOGIC, CAT_NONE, "Flag for enabling lattice defect diffusion.");
112  p.addPar ("TRANSPORT", false, false, ParameterType::NO_DEP, &RxnSet::Instance::transportFlag, &RxnSet::Instance::transportFlagGiven, U_LOGIC, CAT_NONE, "Flag for enabling lattice defect diffusion. Identical to DIFFUSION flag, above. Do not set both!");
113  p.addPar ("EXCLUDENOSOURCE", true, false, ParameterType::NO_DEP, &RxnSet::Instance::excludeNoSourceRegionsFlag, &RxnSet::Instance::excludeNoSourceRegionsFlagGiven, U_LOGIC, CAT_NONE, "Flag for excluding regions that are outside of source region from computing defect reaction equations. This is a speed optimization. Turning it on will NOT change the answer");
114  p.addPar ("COLUMNREORDER", false, false, ParameterType::NO_DEP, &RxnSet::Instance::columnReorderingFlag, NULL, U_LOGIC, CAT_NONE, "Debug Flag for turning on/off column reordering.");
115  p.addPar ("OUTPUTREGION", 1, &RxnSet::Instance::outputRegion);
116  p.addPar ("TECPLOTLEVEL", 0, &RxnSet::Instance::tecplotLevel)
117  .setDescription("Integer number to determine type of tecplot output. 0=no output. 1=single time-dependent file, with each time step in a different zone.");
118  p.addPar ("DIRICHLETBC", false, false, ParameterType::NO_DEP, &RxnSet::Instance::dirichletBCFlag, NULL, U_LOGIC, CAT_NONE, "Flag for using Dirichlet boundary conditions.");
119 }
120 
122 {
123  p.addPar ("TNOM", 0.0, false, ParameterType::NO_DEP, &RxnSet::Model::TNOM, NULL, U_DEGC, CAT_UNKNOWN,
124  "Parameter measurement temperature");
125 
126  // rxn stuff:
127 
128  p.addPar ("XLO", 1.0e-5, false, ParameterType::NO_DEP, &RxnSet::Model::xlo, NULL, U_CM, CAT_UNKNOWN, "Left edge of integration volume.");
129  p.addPar ("XHI", 3.0e-4, false, ParameterType::NO_DEP, &RxnSet::Model::xhi, NULL, U_CM, CAT_UNKNOWN, "Right edge of integration volume");
130  p.addPar ("XLO_SOURCE",1.0e-5, false, ParameterType::NO_DEP, &RxnSet::Model::xlo_source, &RxnSet::Model::xlo_sourceGiven, U_CM, CAT_UNKNOWN, "Left edge of source region");
131  p.addPar ("XHI_SOURCE",3.0e-4, false, ParameterType::NO_DEP, &RxnSet::Model::xhi_source, &RxnSet::Model::xhi_sourceGiven, U_CM, CAT_UNKNOWN, "Right edge of source region");
132  p.addPar ("MASTERSOURCE", 0.0, false, ParameterType::TIME_DEP, &RxnSet::Model::masterSource, NULL, STANDARD, CAT_NONE, "");
133  p.addPar ("REACTION_FILE",std::string("NOFILE"),false,ParameterType::NO_DEP, &RxnSet::Model::rxnFileName, NULL, U_NONE, CAT_NONE, "");
134  p.addPar ("NUMBER_REGIONS", 0, false, ParameterType::NO_DEP, &RxnSet::Model::userNumRegions, NULL, U_NONE, CAT_NONE, "Number of mesh points.");
135 
137 
141 }
142 
143 
144 //-----------------------------------------------------------------------------
145 // Function : Instance::processParams
146 // Purpose :
147 // Special Notes :
148 // Scope : public
149 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
150 // Creation Date : 02/09/08
151 //-----------------------------------------------------------------------------
153 {
155  return true;
156 }
157 
158 //----------------------------------------------------------------------------
159 // Function : Instance::Instance
160 // Purpose : instance block constructor
161 // Special Notes :
162 // Scope : public
163 // Creator : Eric R. Keiter
164 // Creation Date : 02/09/08
165 //----------------------------------------------------------------------------
166 
168  const Configuration & configuration,
169  const InstanceBlock & IB,
170  Model & it_MB,
171  const FactoryBlock & factory_block)
172  : DevicePDEInstance(IB,configuration.getInstanceParameters(), factory_block),
173  model_(it_MB),
174  reactionFileCopyFlag(false),
175  haveAnyReactions(false),
176  useScaledVariablesFlag(true),
177  useDopingArrayData(false),
178  outputInterval(0.0),
179  outputIndex(0),
180  lastOutputTime(-10.0),
181  outputRegion(0),
182  tecplotLevel(0),
183  callsOTEC(0),
184  callsOTECcarrier(0),
185  TEMP(300.0),
186  newtonIterOld (0),
187 
188  li_Pos(-1),
189  li_Neg(-1),
190 
191  outputXscalar(1.0),
192  excludeNoSourceRegionsFlag(true),
193  excludeNoSourceRegionsFlagGiven(false),
194  transportFlagGiven(false),
195  transportFlag(false),
196  diffusionFlagGiven(false),
197  diffusionFlag(false),
198  dirichletBCFlag(false),
199  columnReorderingFlag(false),
200  xloIndex(-1),
201  xhiIndex(-1)
202 {
203  numIntVars = 0;
204  numExtVars = 2;
205  numStateVars = 10;
206 
207  devConMap.resize(2);
208  devConMap[0] = 1;
209  devConMap[1] = 1;
210 
211  // Set params to constant default values:
212  setDefaultParams ();
213 
214  // Set params according to instance line and constant defaults from metadata:
215  setParams (IB.params);
216 
217  // Set any non-constant parameter defaults:
218  if (!given("TEMP"))
219  TEMP = getDeviceOptions().temp.getImmutableValue<double>();
220 
221 
223  {
225  }
226 
227  if (diffusionFlagGiven && transportFlagGiven) // both given
228  {
229  std::string msg = "Both transportFlag and diffusionFlag set in " + getName() + ". Using transportFlag";
230  N_ERH_ErrorMgr::report(N_ERH_ErrorMgr::DEV_FATAL, msg);
231  }
232 
233  // Calculate any parameters specified as expressions:
235 
236  setupScalingVars ();
237 
238  // Handle the reactions.
239  setupMeshUniform ();
240  allocateRegions ();
241  scaleMesh ();
242 
243  // Do the rest of the defect chemistry setup, which is independent of whether
244  // or not the model or instance spefication has been used.
246 
247  int numRegions=regVec.size();
248 
249 #ifdef Xyce_DEBUG_DEVICE
250  if (getDeviceOptions().debugLevel > 0)
251  {
252  std::vector<RegionData*> & rdVec1 = model_.regionDataVec;
253  if (!(rdVec1.empty()))
254  {
255  Xyce::dout() << "Model Region Data vector:" << std::endl;
256  for (int ireg=0;ireg<numRegions;++ireg)
257  {
258  RegionData & rd = *(rdVec1[ireg]);
259  Xyce::dout() << ireg << ": "<< rd;
260  }
261  }
262  }
263 #endif
264 
265  // calculate dependent (ie computed) params:
266  processParams ();
267  setupFluxVec ();
268  setupJacStamp ();
269 }
270 
271 //-----------------------------------------------------------------------------
272 // Function : Instance::setupMeshUniform
273 // Purpose : Sets up xVec and dxVec (unscaled).
274 // Special Notes :
275 // Scope : public
276 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
277 // Creation Date : 10/27/08
278 //-----------------------------------------------------------------------------
280 {
281  if ( model_.userNumRegions > 0)
282  {
283  int size = model_.userNumRegions;
284  double xmin = model_.xlo;
285  double xmax = model_.xhi;
286 
287  double dn = static_cast<double>(size-1);
288  double dx = (xmax-xmin)/dn;
289  double xtmp = xmin;
290 
291  xVec.resize(size,0.0);
292  dxVec.resize(size,0.0);
293 
294  xloStencilVec.resize(size,0);
295  xhiStencilVec.resize(size,0);
296 
297  int i=0;
298  for (; i<size;++i)
299  {
300  xVec[i] = xtmp;
301  xtmp += dx;
302  }
303 
304  for (i=0;i<size-1;++i)
305  {
306  dxVec[i] = xVec[i+1]-xVec[i];
307  }
308 
309  if (size-2 >= 0)
310  {
311  dxVec[size-1] = dxVec[size-2];
312  }
313 
314  // set up the edge indices
315  xloIndex=0;
316  xhiIndex=size-1;
317  }
318  else
319  {
320  // no-op
321  }
322 }
323 
324 //-----------------------------------------------------------------------------
325 // Function : Instance::allocateRegions
326 // Purpose :
327 // Special Notes :
328 // Scope : public
329 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
330 // Creation Date : 11/03/08
331 //-----------------------------------------------------------------------------
333 {
334  std::map<std::string, DopeInfo *> diMap = model_.dopeInfoMap;
335 
336  if ( model_.userNumRegions > 0 && model_.rxnFileName != "NOFILE" )
337  {
338  //setupDopingArrays();
339  useDopingArrayData = true;
340 
341  // Now set up corresponding region Data classes, to match the region classes.
342  // mesh stuff will be fixed later also:
343  int numReg = model_.userNumRegions;
344 
345  std::vector<RegionData*> * rdVecPtr(0);
346  rdVecPtr = &(model_.regionDataVec);
347  // if (*rdVecPtr) is not empty, then the contents came from the input file and
348  // should be deleted, as this specification overrides.
349  (*rdVecPtr).clear();
350  int iReg=0;
351  for (iReg=0;iReg!=numReg;++iReg)
352  {
353  RegionData * regDataPtr = new RegionData ();
354  double xtmp = xVec[iReg];
355 
356  std::vector<char> tmpname;
357  tmpname.resize( getName().size()+10 );
358  sprintf( &(tmpname[0]), "%s_%03d",getName().c_str(),iReg);
359  std::string regName(&(tmpname[0]));
360 
361  std::vector<char> tmpname2;
362  tmpname2.resize( outputName.size()+10 );
363  sprintf( &(tmpname2[0]), "%s_%03d",outputName.c_str(),iReg);
364  std::string outRegName(&(tmpname2[0]));
365 
366  regDataPtr->xloc = xtmp;
367  regDataPtr->name = regName;
368  regDataPtr->outName = outRegName;
369  regDataPtr->reactionFile = model_.rxnFileName;
370 
371  (*rdVecPtr).push_back(regDataPtr);
372  }
373 
375  {
376  // Allocate a single reaction network class, and use it to parse the reactions
377  // file. This reaction network will then be copied into each reaction region
378  // as it is constructed.
379  ReactionNetwork tmpReactions1;
380  ReactionNetwork tmpReactions2;
381 
382  tmpReactions1.setApplySources(true);
384 
385  tmpReactions2.setApplySources(false);
387 
388  // Now that the region data class have been set up, allocate the regions.
389  for (iReg=0;iReg!=numReg;++iReg)
390  {
391  Region * regPtr;
392  bool sourceOn(true);
393  sourceOn = (xVec[iReg] >= model_.xlo_source) &&
394  (xVec[iReg] <= model_.xhi_source);
395 
396  if (sourceOn)
397  {
398  regPtr = new Region
399  ( *((*rdVecPtr)[iReg]),
401  getSolverState(),
402  tmpReactions1);
403  }
404  else
405  {
406  // if no source and transport is off, then no point in
407  // solving reaction network at this mesh cell.
408  if (transportFlag)
409  {
410  regPtr = new Region
411  ( *((*rdVecPtr)[iReg]),
413  getSolverState(),
414  tmpReactions2);
415  }
416  else
417  {
418  ((*rdVecPtr)[iReg])->doNothing=true;
419 
420  regPtr = new Region
421  ( *((*rdVecPtr)[iReg]),
423  getSolverState(),
424  tmpReactions2);
425  }
426  }
427  regVec.push_back(regPtr);
428  }
429  }
430  else
431  {
432  // Now that the region data class have been set up, allocate the regions.
433  for (iReg=0;iReg!=numReg;++iReg)
434  {
435  bool sourceOn(true);
436  sourceOn = (xVec[iReg] >= model_.xlo_source) &&
437  (xVec[iReg] <= model_.xhi_source);
438 
440  {
441  if (!transportFlag && !sourceOn)
442  {
443  ((*rdVecPtr)[iReg])->doNothing=true;
444  }
445  }
446 
447  Region * regPtr = new Region
448  ( *((*rdVecPtr)[iReg]),
450  getSolverState(), sourceOn);
451 
452  regVec.push_back(regPtr);
453  }
454  }
455  }
456 }
457 
458 //-----------------------------------------------------------------------------
459 // Function : Instance::setupScalingVars
460 // Purpose :
461 // Special Notes :
462 // Scope : public
463 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
464 // Creation Date : 03/04/08
465 //-----------------------------------------------------------------------------
467 {
468  scalingVars.t0 = 1.0e-6; // time scaling (s)
469  scalingVars.C0 = 1.0e+12; // concentration scaling (cm^-3);
470  scalingVars.x0 = 1.0e-7; // distance scaling (cm)
471 
472  // time scaling (s)
473  if (given("t0"))
474  {
476  }
477 
478  // concentration scaling (cm^-3);
479  if (given("C0"))
480  {
482  }
483 
484  // distance scaling (cm)
485  if (given("X0"))
486  {
488  }
489 
490  double rx0 = 1.0/scalingVars.x0;
491 
492  // area scaling (cm^2)
494 
495  // diffusion coefficient scaling (cm^2/s)
496  //scalingVars.D0 = scalingVars.t0*rx0*rx0;
498 
499  // recombination rate scaling (cm^-3/s)
500  //scalingVars.R0 = scalingVars.D0*scalingVars.C0/(scalingVars.x0*scalingVars.x0);
502 
504 
505  // rate constant scaling. k0 = 1/(C0*t0) = cm^3/sec
509 }
510 
511 //-----------------------------------------------------------------------------
512 // Function : Instance::initializeChemistry
513 //
514 // Purpose : This function takes care of some of the initial setup for
515 // defect chemistry. It is independent of the model or
516 // instance specification for the chemistry, and thus gets
517 // its own function. The other functions are
518 // setupUserSpecifiedRegions (for instance based).
519 //
520 // Special Notes :
521 // Scope : public
522 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
523 // Creation Date : 03/02/08
524 //-----------------------------------------------------------------------------
526 {
527 #ifdef Xyce_DEBUG_DEVICE
528  if (getDeviceOptions().debugLevel > 0)
529  {
530  // debug outputs. Check what is in the defect map.
531  std::map<std::string, SpecieSource *>::iterator itSource = model_.defectSourceMap.begin();
532  std::map<std::string, SpecieSource *>::iterator itSourceEnd = model_.defectSourceMap.end ();
533 
534  Xyce::dout() << Xyce::section_divider << std::endl;
535  Xyce::dout() << "Instance::initializeChemistry ():" << std::endl;
536  for (; itSource!=itSourceEnd; ++itSource)
537  {
538  std::string speciesName (itSource->first);
539  Xyce::dout() << "speciesName = " << speciesName << std::endl;
540  }
541  Xyce::dout() << Xyce::section_divider << std::endl;
542  }
543 #endif
544 
545  std::vector<RegionData*> * rdVecPtr(0);
546  rdVecPtr = &(model_.regionDataVec);
547 
548  int numRegions=regVec.size();
549  for (int ireg=0;ireg<numRegions;++ireg)
550  {
551  regVec[ireg]->initializeReactionNetwork(scalingVars);
552 
553  if (useDopingArrayData)
554  {
555  std::map<std::string, DopeInfo *> diMap = model_.dopeInfoMap;
556  if (!(diMap.empty()))
557  {
558  // Push in some initial values for various species.
559  // This includes doping species like boron- (BM) as well as E and H.
560  std::map<std::string, DopeInfo *>::iterator iter;
561  std::map<std::string, DopeInfo *>::iterator start = diMap.begin();
562  std::map<std::string, DopeInfo *>::iterator end = diMap.end();
563 
564  for ( iter = start; iter != end; ++iter )
565  {
566  DopeInfo & di = *(iter->second);
567 
568  bool reactantExist = false;
569  reactantExist = regVec[ireg]->reactantExist(di.speciesName);
570 
571  if (reactantExist)
572  {
573  double dopeValue = di.splintDopeVec[ireg];
574  regVec[ireg]->setInitialCondition(di.speciesName, dopeValue);
575  }
576  }
577  }
578  else
579  {
580 #if 0
581  // This is the original doping/carrier intialization.
582  // It is hardwired to boron and phosphorus, so
583  // it should be phased out and/or deprecated.
584 
585  if (regVec[ireg]->reactantExist("BM"))
586  {
587  regVec[ireg]->setInitialCondition("BM", (*rdVecPtr)[ireg]->Boron_Concentration);
588  }
589 
590  if (regVec[ireg]->reactantExist("PP"))
591  {
592  regVec[ireg]->setInitialCondition("PP", (*rdVecPtr)[ireg]->Phosphorus_Concentration);
593  }
594 #endif
595  }
596 
597  if (model_.given("MASTERSOURCE"))
598  {
599  bool sourceOn(true);
600  double xlos = model_.xlo_source *(useScaledVariablesFlag?(1.0/scalingVars.x0):1.0);
601  double xhis = model_.xhi_source *(useScaledVariablesFlag?(1.0/scalingVars.x0):1.0);
602 
603  sourceOn = (xVec[ireg] >= xlos) && (xVec[ireg] <= xhis);
604 
605  if (sourceOn)
606  {
607  std::map<std::string,SpecieSource *>::iterator itSource = model_.defectSourceMap.begin();
608  std::map<std::string,SpecieSource *>::iterator itSourceEnd = model_.defectSourceMap.end ();
609 
610  for (; itSource!=itSourceEnd; ++itSource)
611  {
612  std::string speciesName (itSource->first);
613  regVec[ireg]->addMasterSource(speciesName);
614  }
615  }
616  }
617 
619  {
620  regVec[ireg]->scaleVariables ();
621  }
622  }
623  }
624 }
625 
626 //-----------------------------------------------------------------------------
627 // Function : Instance::setupJacStamp
628 // Purpose :
629 // Special Notes :
630 //
631 //
632 // Scope : public
633 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
634 // Creation Date : 02/09/08
635 //-----------------------------------------------------------------------------
637 {
638  // Now it's time to do the jacobian stamp.
639  // Set up full jacstamp
640  int localPosIndex = 0;
641  int localNegIndex = 1;
642  int indexCounter=localNegIndex+1;
643 
644  jacStamp.resize(2);
645 
646  jacStamp[localPosIndex].resize(2); // Base'
647  jacStamp[localPosIndex][localPosIndex] = localPosIndex; // B'-B' (0)
648  jacStamp[localPosIndex][localNegIndex] = localNegIndex; // B'-E' (1)
649 
650  jacStamp[localNegIndex].resize(2); // Emitter'
651  jacStamp[localNegIndex][localPosIndex] = localPosIndex; // E'-B' (0)
652  jacStamp[localNegIndex][localNegIndex] = localNegIndex; // E'-E' (1)
653 
654  // Rxn model blocks are next.
655  regLastIndexVec.resize(regVec.size(), -1); // index at the end of the stamp.
656  regFirstReactantIndexVec.resize(regVec.size(), -1); // index of first reactant
657  regNumSpecieVec.resize(regVec.size(), 0);
658 
659  int ireg=0;
660 
661  // Call each region to have it augment its share of the jacstamp.
662  int numRegions=regVec.size();
663  for (ireg=0;ireg<numRegions;++ireg)
664  {
665  int concentrationSize = regVec[ireg]->getNumSpecies();
666  if (concentrationSize != 0)
667  {
668  // communicate down to the region the voltage inputs indices.
669  std::vector<int> voltageNodeColDep(2,-1);
670  voltageNodeColDep[0]=localPosIndex; // pos (previously base) voltage
671  voltageNodeColDep[1]=localNegIndex; // neg (perviously emitter) voltage
672 
673  int firstReactant=-1;
674  int lastIndex=-1;
675  regVec[ireg]->setupJacStamp(jacStamp,voltageNodeColDep, firstReactant, lastIndex);
676  regFirstReactantIndexVec[ireg] = firstReactant;
677  regLastIndexVec[ireg] = lastIndex;
678 
679  numIntVars += regVec[ireg]->getNumIntVars();
680  numStateVars += concentrationSize;
681  }
682  }
683 
684  // Do diffusion. for all mobile species.
685  int numSpecies = thVec.size();
686  int isp=0;
687 
688  for (ireg=0;ireg<numRegions;++ireg)
689  {
690  if (!(regVec[ireg]->getDoNothingFlag()) )
691  {
692  regNumSpecieVec[ireg] = numSpecies;
693  }
694  else
695  {
696  regNumSpecieVec[ireg] = 0;
697  }
698 #ifdef Xyce_DEBUG_DEVICE
699 
700  Xyce::dout() << regVec[ireg]->getName ()
701  << " numSpecies = " << regNumSpecieVec[ireg] << std::endl;
702 #endif
703  }
704 
705  if (transportFlag && numRegions > 1)
706  {
707  for (isp=0;isp<numSpecies;++isp)
708  {
709  if (!(thVec[isp].transportFlag)) continue;
710 
711  // If this is the base or emitter, then add a single column to the stamp.
712  // Otherwise, if this is the BE point (in the middle) then add 2 columns,
713  // for 2 neighbors.
714  for (ireg=0;ireg<numRegions;++ireg)
715  {
716  int row = regFirstReactantIndexVec[ireg] + isp;
717  if (row < 0) continue;
718 
719  int rowSize = jacStamp[row].size();
720  if (ireg==0)
721  {
722  int col = regFirstReactantIndexVec[1] + isp;
723  jacStamp[row].resize(rowSize+1);
724  jacStamp[row][rowSize] = col;
725  }
726  else if (ireg==numRegions-1)
727  {
728  int col = regFirstReactantIndexVec[numRegions-2] + isp;
729  jacStamp[row].resize(rowSize+1);
730  jacStamp[row][rowSize] = col;
731  }
732  else
733  {
734  int col1 = regFirstReactantIndexVec[ireg+1] + isp;
735  int col2 = regFirstReactantIndexVec[ireg-1] + isp;
736  jacStamp[row].resize(rowSize+2);
737  jacStamp[row][rowSize ] = col1;
738  jacStamp[row][rowSize+1] = col2;
739  }
740  }
741  }
742  }
743 
744  // Just put in a dense-ish row for these terms for now.
745  // These represent the dependence of generation-recombination
746  // currents, calculated from regions, on the species
747  // densities of those same regions.
748  {
749  //
750  // do the base-prime row and the emit-prime row.
751  int posRow = 0;
752  int posSize = jacStamp[posRow].size();
753  int negRow = 1;
754  int negSize = jacStamp[negRow].size();
755  for (ireg=0;ireg<numRegions;++ireg)
756  {
757  int posIndex = regFirstReactantIndexVec[ireg];
758  int numSpecie = regNumSpecieVec[ireg];
759 
760  if (posIndex != -1)
761  {
762  int posRowSize = jacStamp[posRow].size();
763  jacStamp[posRow].resize(posRowSize+numSpecie);
764 
765  int negRowSize = jacStamp[negRow].size();
766  jacStamp[negRow].resize(negRowSize+numSpecie);
767 
768  for (isp=0;isp<numSpecie;++isp)
769  {
770  int icol = posRowSize+isp;
771  jacStamp[posRow][icol] = posIndex+isp ;
772 
773  icol = negRowSize+isp;
774  jacStamp[negRow][icol] = posIndex+isp ;
775  }
776  }
777  }
778  }
779 
780  // set up normal jacMap for when all resistances nonzero
781  // If nothing is remapped, this amounts to a null operation when the
782  // map is used later. The maps become important when we start
783  // remapping nodes because of zero lead resistances
784  jacMap.clear();
785  jacMap2.clear();
786  jacMap.resize(jacStamp.size());
787  jacMap2.resize(jacStamp.size());
788 
789  // This is only done once per model, and size is small --- not gonna
790  // bother with temporary vars just to keep the .size() call out of the
791  // loops
792 
793  int mapSize = jacMap.size();
794  for (int i=0;i<mapSize;++i)
795  {
796  jacMap[i]=i;
797  jacMap2[i].resize(jacStamp[i].size());
798  for (int j=0;j<jacStamp[i].size();++j)
799  {
800  jacMap2[i][j] = j;
801  }
802  }
803 
804  // Now fix the ordering of the columns in the jacStamp. If the columns in each row
805  // are not in ascending order, then the jacStampMap calls below (for removing
806  // variables) will not work correctly.
807  //
808  // ERK: 01/13/10: this flag doesn't appear to be needed for this model,
809  // as this model doesn't contain any optional prime nodes. This block
810  // of code was copied over from another model, which *does* need it.
811  // As such, the columnReorderFlag is "false" by default, whereas it is
812  // "true" in the orginal device from which this was copied.
814  {
815  std::vector< std::vector<int> > tempStamp_eric;
816  std::vector< std::vector<int> > tempMap2_eric;
817  jacStampMap_fixOrder(jacStamp, jacMap2, tempStamp_eric, tempMap2_eric);
818  jacStamp = tempStamp_eric;
819  jacMap2 = tempMap2_eric;
820  }
821 
822  // Now we have to selectively map away bits and pieces of the
823  // jacobian stamp based on absent resistances
824 #ifdef Xyce_DEBUG_DEVICE
825  if (getDeviceOptions().debugLevel > -50 && getSolverState().debugTimeFlag)
826  {
827  Xyce::dout() << "jacStamp Before removing terminal nodes:"<<std::endl;
829  Xyce::dout() << "jacMap2 Before removing terminal nodes:"<<std::endl;
831  }
832 #endif
833 }
834 
835 //-----------------------------------------------------------------------------
836 // Function : Instance::scaleMesh
837 // Purpose :
838 // Special Notes : This should be called after the doping-profile based
839 // regions are set up.
840 //
841 // Scope : public
842 // Creator : Eric Keiter, SNL
843 // Creation Date : 10/27/08
844 //-----------------------------------------------------------------------------
846 {
847  std::vector<RegionData*> * rdVecPtr(0);
848  rdVecPtr = &(model_.regionDataVec);
849  int i=0;
850  int size = (*rdVecPtr).size();
852  {
853  for (i=0;i<size;++i)
854  {
855  xVec[i] *= (1.0/scalingVars.x0);
856  }
857  }
858 
859  for (i=0;i<size-1;++i)
860  {
861  dxVec[i] = xVec[i+1]-xVec[i];
862  }
863 
864  if (size-2 >= 0)
865  {
866  dxVec[size-1] = dxVec[size-2];
867  }
868 
869 #ifdef Xyce_DEBUG_DEVICE
870  if (getDeviceOptions().debugLevel > 1)
871  {
872  for (i=0;i<size;++i)
873  {
874  Xyce::dout() << "Scaled Mesh: xVec["<<i<<"] = " << xVec[i] << std::endl;
875  }
876  Xyce::dout() << std::endl;
877  for (i=0;i<size-1;++i)
878  {
879  Xyce::dout() << "Scaled Mesh: dxVec["<<i<<"] = " << dxVec[i] << std::endl;
880  }
881  Xyce::dout() << std::endl;
882  }
883 #endif
884 
885 }
886 
887 //-----------------------------------------------------------------------------
888 // Function : Instance::setupFluxVec
889 // Purpose :
890 // Special Notes :
891 // Scope : public
892 // Creator : Eric Keiter, SNL
893 // Creation Date : 10/27/08
894 //-----------------------------------------------------------------------------
896 {
897  std::vector<RegionData*> * rdVecPtr(0);
898  rdVecPtr = &(model_.regionDataVec);
899  if ( !((*rdVecPtr).empty()) )
900  {
901  // do all the mobile species
902  int numSpecies = thVec.size();
903  int isp=0;
904  int size = (*rdVecPtr).size();
905  for (isp=0;isp<numSpecies;++isp)
906  {
907  thVec[isp].fluxVec.resize(size-1,0.0);
908  }
909  }
910 }
911 
912 //-----------------------------------------------------------------------------
913 // Function : Instance::outputPlotFiles
914 // Purpose :
915 // Special Notes :
916 // Scope : public
917 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
918 // Creation Date : 02/09/08
919 //-----------------------------------------------------------------------------
921 {
922  bool bsuccess = true;
923  bool bs1 = true;
924  bool skipOutput = false;
925 
926  // If using output interval, check if enough time has passed to do
927  // another output. (only applies for transient - not DCOP).
928  if ( !(getSolverState().dcopFlag) && !(getSolverState().forceFinalOutput) && given("OUTPUTINTERVAL") )
929  {
930  double outMult = static_cast<double> (outputIndex);
931  double nextOutputTime = outMult * outputInterval;
932 
933  if (nextOutputTime > getSolverState().currTime)
934  {
935  skipOutput = true;
936  }
937  }
938 
939  // If this is a "forced" final output, make sure that it didn't already output.
940  // This can happen if the output interval is an exact multiple of the
941  // total simulation time.
943  getSolverState().currTime==lastOutputTime) skipOutput=true;
944 
945  if (skipOutput) return bsuccess;
946  ++outputIndex;
948 
949 
950  // output individual tecplot files for each region.
951  // These are time-dependent files, with no spatial depedence.
952  int numRegions = regVec.size();
953  if ( (tecplotLevel==2 && numRegions > 1) || (tecplotLevel==1 && numRegions==1) )
954  {
955  for (int ireg=0;ireg<numRegions;++ireg)
956  {
957  if (regVec[ireg]->haveAnyReactions())
958  {
959  bs1 = regVec[ireg]->outputTecplot ();
960  bsuccess = bsuccess && bs1;
961  }
962  }
963  }
964 
965  // output a single, spatially-dependent file(s).
966  // Don't bother if there's only one (or less) regions.
967  if (tecplotLevel==1 && numRegions > 1)
968  {
969  bs1 = outputTecplot ();
970  bsuccess = bsuccess && bs1;
971  }
972  else if (tecplotLevel==3 && numRegions > 1)
973  {
974  bs1 = output2DTecplot ();
975  bsuccess = bsuccess && bs1;
976  }
977 
978  if (tecplotLevel > 0 && given("OUTPUTREGION"))
979  {
980  int numRegions = regVec.size();
981  if (outputRegion < numRegions && outputRegion >= 0)
982  {
984  {
985  bs1 = regVec[outputRegion]->outputTecplot ();
986  bsuccess = bsuccess && bs1;
987  }
988  }
989  }
990 
991  return bsuccess;
992 }
993 
994 //-----------------------------------------------------------------------------
995 // Function : Instance::outputTecplot
996 // Purpose :
997 // Special Notes :
998 // Scope : public
999 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1000 // Creation Date : 02/09/08
1001 //-----------------------------------------------------------------------------
1003 {
1004  bool bsuccess = true;
1005 
1006  int i(0);
1007  int NX = regVec.size();
1008 
1009  char filename[256];
1010  for(i=0;i<256;++i) filename[i] = static_cast<char>(0);
1011 
1012  sprintf(filename,"%s.dat",outputName.c_str());
1013  double time = getSolverState().currTime;
1014  FILE *fp1(NULL);
1015 
1016 #ifdef Xyce_DEBUG_DEVICE
1017  if (getDeviceOptions().debugLevel > 0)
1018  {
1019  Xyce::dout() << " Instance::outputTecplot. filename = " << std::string(filename) <<std::endl;
1020  }
1021 #endif
1022 
1023  if (callsOTEC <= 0)
1024  {
1025  fp1 = fopen(filename,"w");
1026  fprintf(fp1,
1027  " TITLE = \"Spatially Dependent defect data for compact rxn device: %s time = %20.12e seconds.\",\n",
1028  outputName.c_str(),time);
1029  }
1030  else
1031  {
1032  fp1 = fopen(filename,"a");
1033  }
1034 
1035  int rSize=(regVec[0])->getNumSpecies();
1036  int cSize=(regVec[0])->getNumConstants();
1037 
1038  if (callsOTEC <= 0)
1039  {
1040  fprintf(fp1,"%s","\tVARIABLES = \"X \",\n");
1041 
1042  Region & reg = (*(regVec[0]));
1043  for (int iconst=0;iconst<cSize;++iconst)
1044  {
1045  fprintf(fp1, "\t \"%s \",\n" , ( reg.getConstantsName(iconst)).c_str());
1046  }
1047 
1048  for (int ispec=0;ispec<rSize;++ispec)
1049  {
1050  fprintf(fp1,"\t \"%s \",\n", ( reg.getSpeciesName(ispec)).c_str());
1051  }
1052  }
1053 
1054  fprintf(fp1,"\tDATASETAUXDATA %s\n", tecplotTimeDateStamp().c_str() );
1055  fprintf(fp1,"\tZONE F=POINT,I=%d", NX);
1056 
1057  if (getSolverState().dcopFlag)
1058  {
1059  fprintf(fp1," T = \"DCOP step = %d\" \n", callsOTEC);
1060  }
1061  else
1062  {
1063  fprintf(fp1," T = \"time step = %d, time=%20.12e\" AUXDATA time = \"%20.12e seconds\" \n", callsOTEC , time, time);
1064  }
1065 
1066  std::vector<RegionData*> * rdVecPtr(0);
1067  rdVecPtr = &(model_.regionDataVec);
1068 
1069  if (NX <= 1)
1070  {
1071  fprintf(fp1," %20.12e", time);
1072  }
1073 
1074  int numprint=0;
1075  for (i=0;i<NX;++i)
1076  {
1077  double val(0.0);
1078  val = ((*rdVecPtr)[i])->xloc * outputXscalar;
1079  fprintf(fp1," %20.12e", val);
1080 
1081  Region & reg = (*(regVec[i]));
1082 
1083  for (int iconst=0;iconst<cSize;++iconst)
1084  {
1085  val = reg.getConstantsVal(iconst) ;
1086  fprintf(fp1," %20.12e", val);
1087 
1088  if (numprint >= 6)
1089  {
1090  fprintf(fp1,"%s","\n"); numprint = 0;
1091  }
1092  else
1093  {
1094  numprint++;
1095  }
1096  }
1097 
1098  for (int ispec=0;ispec<rSize;++ispec)
1099  {
1100  val = 0.0;
1101  if (reg.haveAnyReactions())
1102  {
1103  val = reg.getSpeciesVal(ispec);
1104  }
1105  fprintf(fp1," %20.12e", val);
1106 
1107  if (numprint >= 6)
1108  {
1109  fprintf(fp1,"%s","\n"); numprint = 0;
1110  }
1111  else
1112  {
1113  numprint++;
1114  }
1115  }
1116  }
1117 
1118  fprintf(fp1,"%s","\n");
1119 
1120  ++callsOTEC;
1121  fclose(fp1);
1122 
1123  return bsuccess;
1124 }
1125 
1126 //-----------------------------------------------------------------------------
1127 // Function : Instance::output2DTecplot
1128 // Purpose :
1129 // Special Notes :
1130 // Scope : public
1131 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1132 // Creation Date : 10/14/09
1133 //-----------------------------------------------------------------------------
1135 {
1136  bool bsuccess = true;
1137 
1138  int i(0);
1139  int NX = regVec.size();
1140 
1141  char filename[256];
1142  for(i=0;i<256;++i) filename[i] = static_cast<char>(0);
1143 
1144  sprintf(filename,"%s.dat",outputName.c_str());
1145  double time = getSolverState().currTime;
1146  FILE *fp1(NULL);
1147 
1148  if (callsOTEC <= 0)
1149  {
1150  fp1 = fopen(filename,"w");
1151  fprintf(fp1,
1152  " TITLE = \"Spatially Dependent defect data for compact rxn device: %s time = %20.12e seconds.\",\n",
1153  outputName.c_str(),time);
1154  }
1155  else
1156  {
1157  fp1 = fopen(filename,"a");
1158  }
1159 
1160  int rSize=(regVec[0])->getNumSpecies();
1161  int cSize=(regVec[0])->getNumConstants();
1162 
1163  if (callsOTEC <= 0)
1164  {
1165  fprintf(fp1,"%s","\tVARIABLES = \"X \",\n");
1166  fprintf(fp1,"%s","\t \"Time(sec) \",\n");
1167 
1168  Region & reg = (*(regVec[0]));
1169  for (int iconst=0;iconst<cSize;++iconst)
1170  {
1171  fprintf(fp1, "\t \"%s \",\n" , ( reg.getConstantsName(iconst)).c_str());
1172  }
1173 
1174  for (int ispec=0;ispec<rSize;++ispec)
1175  {
1176  fprintf(fp1,"\t \"%s \",\n", ( reg.getSpeciesName(ispec)).c_str());
1177  }
1178 
1179  fprintf(fp1,"\tZONE F=POINT,I=%d", NX);
1180  }
1181 
1182  fprintf(fp1,"%s"," \n");
1183 
1184  std::vector<RegionData*> * rdVecPtr(0);
1185  rdVecPtr = &(model_.regionDataVec);
1186 
1187  if (NX <= 1)
1188  {
1189  fprintf(fp1," %20.12e", time);
1190  }
1191 
1192  int numprint=0;
1193  for (i=0;i<NX;++i)
1194  {
1195  double val(0.0);
1196  val = ((*rdVecPtr)[i])->xloc;
1197  fprintf(fp1," %20.12e", val);
1198 
1199  fprintf(fp1," %20.12e", time);
1200 
1201  Region & reg = (*(regVec[i]));
1202 
1203  for (int iconst=0;iconst<cSize;++iconst)
1204  {
1205  val = reg.getConstantsVal(iconst) ;
1206  fprintf(fp1," %20.12e", val);
1207 
1208  if (numprint >= 6)
1209  {
1210  fprintf(fp1,"%s","\n"); numprint = 0;
1211  }
1212  else
1213  {
1214  numprint++;
1215  }
1216  }
1217 
1218  for (int ispec=0;ispec<rSize;++ispec)
1219  {
1220  val = reg.getSpeciesVal(ispec);
1221  fprintf(fp1," %20.12e", val);
1222 
1223  if (numprint >= 6)
1224  {
1225  fprintf(fp1,"%s","\n"); numprint = 0;
1226  }
1227  else
1228  {
1229  numprint++;
1230  }
1231  }
1232  }
1233 
1234  fprintf(fp1,"%s","\n");
1235 
1236  ++callsOTEC;
1237  fclose(fp1);
1238 
1239  return bsuccess;
1240 }
1241 
1242 //-----------------------------------------------------------------------------
1243 // Function : Instance::outputCarrierDensities
1244 // Purpose :
1245 // Special Notes :
1246 // Scope : public
1247 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1248 // Creation Date : 02/09/08
1249 //-----------------------------------------------------------------------------
1251 {
1252  bool bsuccess = true;
1253 
1254  int i(0);
1255  int NX = regVec.size();
1256  char filename[256]; for(i=0;i<256;++i) filename[i] = static_cast<char>(0);
1257  sprintf(filename,"%scarrier.dat",outputName.c_str());
1258 
1259  FILE *fp1(NULL);
1260  fp1 = fopen(filename,"w");
1261  int cSize=(regVec[0])->getNumConstants();
1262 
1263  std::vector<RegionData*> * rdVecPtr(0);
1264  rdVecPtr = &(model_.regionDataVec);
1265 
1266  for (i=0;i<NX;++i)
1267  {
1268  double val(0.0);
1269  val = ((*rdVecPtr)[i])->xloc;
1270  fprintf(fp1," %20.12e", val);
1271 
1272  Region & reg = (*(regVec[i]));
1273 
1274  for (int iconst=0;iconst<cSize;++iconst)
1275  {
1276  val = reg.getConstantsVal(iconst) ;
1277  fprintf(fp1," %20.12e", val);
1278  }
1279  fprintf(fp1,"%s","\n");
1280  }
1281 
1282  ++callsOTECcarrier;
1283  fclose(fp1);
1284 
1285  return bsuccess;
1286 }
1287 
1288 //-----------------------------------------------------------------------------
1289 // Function : Instance::~Instance
1290 // Purpose : destructor
1291 // Special Notes :
1292 // Scope : public
1293 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1294 // Creation Date : 02/09/08
1295 //-----------------------------------------------------------------------------
1297 {
1298  if (!(regVec.empty()))
1299  {
1300  int size = regVec.size();
1301  int i=0;
1302  for (i=0;i<size;++i)
1303  {
1304  if (regVec[i] != 0)
1305  {
1306  delete regVec[i];
1307  regVec[i] = 0;
1308  }
1309  }
1310  }
1311 
1312  // Loop over the dopeInfoMap (if it is not empty) and delete its contents.
1313  if (!(dopeInfoMap.empty()))
1314  {
1315  std::map<std::string,DopeInfo *>::iterator iter;
1316  std::map<std::string,DopeInfo *>::iterator begin = dopeInfoMap.begin();
1317  std::map<std::string,DopeInfo *>::iterator end = dopeInfoMap.end ();
1318 
1319  for(iter=begin;iter!=end;++iter)
1320  {
1321  if (iter->second != 0) delete iter->second;
1322  }
1323  }
1324 
1325  regVec.clear();
1326  dopeInfoMap.clear();
1327 }
1328 
1329 //-----------------------------------------------------------------------------
1330 // Function : Instance::registerLIDs
1331 // Purpose :
1332 // Special Notes :
1333 // Scope : public
1334 // Creator : Eric R. Keiter, SNL
1335 // Creation Date : 02/09/08
1336 //-----------------------------------------------------------------------------
1337 void Instance::registerLIDs( const std::vector<int> & intLIDVecRef,
1338  const std::vector<int> & extLIDVecRef )
1339 {
1340  AssertLIDs(intLIDVecRef.size() == numIntVars);
1341  AssertLIDs(extLIDVecRef.size() == numExtVars);
1342 
1343 #ifdef Xyce_DEBUG_DEVICE
1344  if (getDeviceOptions().debugLevel > 0)
1345  {
1346  Xyce::dout() << section_divider << std::endl;
1347  Xyce::dout() << " Instance::registerLIDs" <<std::endl;
1348  Xyce::dout() << " name = " << getName() << std::endl;
1349  Xyce::dout() << " number of internal variables: " << numIntVars << std::endl;
1350  Xyce::dout() << " number of external variables: " << numExtVars << std::endl;
1351  Xyce::dout() << " numIntVars = " << numIntVars << std::endl;
1352  Xyce::dout() << " numExtVars = " << numExtVars << std::endl;
1353  }
1354 #endif
1355 
1356  // Copy over the global ID lists.
1357  intLIDVec = intLIDVecRef;
1358  extLIDVec = extLIDVecRef;
1359 
1360 #ifdef Xyce_DEBUG_DEVICE
1361  if (getDeviceOptions().debugLevel > 0)
1362  {
1363  Xyce::dout() << " Internal LID List" << std::endl;
1364  for( int i = 0; i < intLIDVec.size(); ++i )
1365  Xyce::dout() << " " << intLIDVec[i] << std::endl;
1366  Xyce::dout() << " External LID List" << std::endl;
1367  for( int i = 0; i < extLIDVec.size(); ++i )
1368  Xyce::dout() << " " << extLIDVec[i] << std::endl;
1369  }
1370 #endif
1371 
1372  // Use these lists to obtain the indices into the linear algebra entities.
1373  // First do external variables:
1374  int extIndex = 0;
1375  int intIndex = 0;
1376 
1377  li_Pos = extLIDVec[extIndex++];
1378  li_Neg = extLIDVec[extIndex++];
1379 
1380 #ifdef Xyce_DEBUG_DEVICE
1381  if (getDeviceOptions().debugLevel > 0)
1382  {
1383  Xyce::dout() << " li_Pos = " << li_Pos << std::endl;
1384  Xyce::dout() << " li_Neg = " << li_Neg << std::endl;
1385  }
1386 #endif
1387 
1388  // Register the LIDs for each reaction region.
1389  int numRegions = regVec.size();
1390  for (int ireg=0;ireg<numRegions;++ireg)
1391  {
1392  regVec[ireg]->registerLIDs(intLIDVec, extLIDVec,intIndex);
1393  }
1394 
1395  // Do diffusion. for all mobile species.
1396  int i=0;
1397  int ispec=0;
1398  int numSpecies = thVec.size();
1399  int size = regVec.size();
1400 
1401  if (transportFlag)
1402  {
1403  for (ispec=0;ispec<numSpecies;++ispec)
1404  {
1405  std::string speciesName = regVec[0]->getSpeciesName(ispec);
1406  thVec[ispec].specie_id.resize(size,-1);
1407  for (i=0;i<size;++i)
1408  {
1409  thVec[ispec].specie_id[i] = (regVec[i])->getSpeciesLID (speciesName);
1410  }
1411  }
1412  }
1413 
1414 #ifdef Xyce_DEBUG_DEVICE
1415  if (getDeviceOptions().debugLevel > 0)
1416  {
1417  Xyce::dout() << section_divider << std::endl;
1418  }
1419 #endif
1420 
1421 }
1422 
1423 //-----------------------------------------------------------------------------
1424 // Function : Instance::getIntNameMap
1425 // Purpose :
1426 // Special Notes :
1427 // Scope : public
1428 // Creator : Eric R. Keiter, SNL, Parallel Computational Sciences
1429 // Creation Date : 02/09/08
1430 //-----------------------------------------------------------------------------
1431 std::map<int,std::string> & Instance::getIntNameMap ()
1432 {
1433  // set up the internal name map, if it hasn't been already.
1434  if (intNameMap.empty ())
1435  {
1436  // If we have internal nodes, then we need to do this for debugging
1437  // set up the internal name map:
1438  int numRegions = regVec.size();
1439  for (int ireg=0;ireg<numRegions;++ireg)
1440  {
1441  regVec[ireg]->augmentNameMap(intNameMap, *this);
1442  }
1443  }
1444  return intNameMap;
1445 }
1446 
1447 
1448 //-----------------------------------------------------------------------------
1449 // Function : Instance::registerStateLIDs
1450 // Purpose :
1451 // Special Notes :
1452 // Scope : public
1453 // Creator : Eric R. Keiter, SNL
1454 // Creation Date : 02/09/08
1455 //-----------------------------------------------------------------------------
1456 void Instance::registerStateLIDs( const std::vector<int> & staLIDVecRef )
1457 {
1458  AssertLIDs(staLIDVecRef.size() == numStateVars);
1459 
1460  // Copy over the global ID lists:
1461  staLIDVec = staLIDVecRef;
1462 
1463  int i=0;
1464 
1465  int numRegions = regVec.size();
1466  for (int ireg=0;ireg<numRegions;++ireg)
1467  {
1468  regVec[ireg]->registerStateLIDs(staLIDVec,i);
1469  }
1470 }
1471 
1472 //-----------------------------------------------------------------------------
1473 // Function : Instance::getDepSolnVars
1474 // Purpose :
1475 // Special Notes :
1476 // Scope : public
1477 // Creator : Rob Hoekstra, SNL, Parallel Computational Sciences
1478 // Creation Date : 06/06/01
1479 //-----------------------------------------------------------------------------
1480 const std::vector<std::string> & Instance::getDepSolnVars()
1481 {
1483 }
1484 
1485 //-----------------------------------------------------------------------------
1486 // Function : Instance::jacobianStamp
1487 // Purpose :
1488 // Special Notes :
1489 // Scope : public
1490 // Creator : Eric R. Keiter, SNL
1491 // Creation Date : 02/09/08
1492 //-----------------------------------------------------------------------------
1493 const std::vector< std::vector<int> > & Instance::jacobianStamp() const
1494 {
1495  return jacStamp;
1496 }
1497 
1498 //-----------------------------------------------------------------------------
1499 // Function : Instance::registerJacLIDs
1500 // Purpose :
1501 // Special Notes :
1502 // Scope : public
1503 // Creator : Eric R. Keiter, SNL
1504 // Creation Date : 02/09/08
1505 //-----------------------------------------------------------------------------
1506 void Instance::registerJacLIDs( const std::vector< std::vector<int> > & jacLIDVec )
1507 {
1508  DeviceInstance::registerJacLIDs( jacLIDVec );
1509 
1510  std::vector<int> &map=jacMap;
1511  std::vector< std::vector<int> > &map2=jacMap2;
1512  int posI = 0;
1513  int negI = 1;
1514 
1515  APosEquPosNodeOffset = jacLIDVec[map[posI]][map2[posI][posI]];
1516  APosEquNegNodeOffset = jacLIDVec[map[posI]][map2[posI][negI]];
1517 
1518  ANegEquPosNodeOffset = jacLIDVec[map[negI]][map2[negI][posI]];
1519  ANegEquNegNodeOffset = jacLIDVec[map[negI]][map2[negI][negI]];
1520 
1521  // Do the reaction species block
1522  int numRegions = regVec.size();
1523  for (int ireg=0;ireg<numRegions;++ireg)
1524  {
1525  regVec[ireg]->registerJacLIDs(jacLIDVec, jacMap, jacMap2);
1526  }
1527 
1528 
1529 #if 0
1530  // For now diffusion can only be set up in the jacobian matrix
1531  // via setRow function calls.
1532 
1533  // If this is the base or emitter, then add a single column to the stamp.
1534  // Otherwise, if this is the BE point (in the middle) then add 2 columns,
1535  // for 2 neighbors.
1536  for (int ireg=0;ireg<numRegions;++ireg)
1537  {
1538  int row = regPosIndexVec[ireg] + regV0subIndexVec[ireg];
1539  if (row < 0) continue;
1540 
1541  int rowSize = jacStamp[row].size();
1542  if (ireg==0)
1543  {
1544  int col = regPosIndexVec[1] + regV0subIndexVec[1];
1545  jacStamp[row].resize(rowSize+1);
1546  jacStamp[row][rowSize] = col;
1547  }
1548  else if (ireg==numRegions-1)
1549  {
1550  int col = regPosIndexVec[numRegions-2] + regV0subIndexVec[numRegions-2];
1551  jacStamp[row].resize(rowSize+1);
1552  jacStamp[row][rowSize] = col;
1553  }
1554  else
1555  {
1556  int col1 = regPosIndexVec[ireg+1] + regV0subIndexVec[ireg+1];
1557  int col2 = regPosIndexVec[ireg-1] + regV0subIndexVec[ireg-1];
1558  jacStamp[row].resize(rowSize+2);
1559  jacStamp[row][rowSize+1] = col1;
1560  jacStamp[row][rowSize+2] = col2;
1561  }
1562  }
1563 #endif
1564 
1565  for (int iReg=0;iReg<numRegions;++iReg)
1566  {
1567  if (regVec[iReg]->haveAnyReactions())
1568  {
1569  haveAnyReactions=true;
1570  }
1571  }
1572 
1573 }
1574 
1575 //-----------------------------------------------------------------------------
1576 // Function : Instance::setupPointers()
1577 //
1578 // Purpose : Sets up raw pointers for optimized matrix loads.
1579 //
1580 // Special Notes : ERK: This function is primarily concerned with determining
1581 // matrix pointers associated with coupling the reaction model
1582 // to the terminal nodes. I've had a lot of difficulty
1583 // getting the LID offsets correct for those terms. To preserve
1584 // load speed, I've decided to go directly to raw pointers
1585 // for those terms.
1586 //
1587 // Scope : public
1588 // Creator : Eric R. Keiter, SNL
1589 // Creation Date : 05/18/10
1590 //-----------------------------------------------------------------------------
1592 {
1593  int numRegions = regVec.size();
1594 
1595  N_LAS_Matrix & dFdxMat = *(extData.dFdxMatrixPtr);
1596 
1597  APosEqu_SpeciesPtr.resize(numRegions);
1598  ANegEqu_SpeciesPtr.resize(numRegions);
1599 
1600  APosEqu_ConstPtr.resize(numRegions);
1601  ANegEqu_ConstPtr.resize(numRegions);
1602 
1603  for (int ireg=0;ireg<numRegions;++ireg)
1604  {
1605  N_LAS_Matrix & dFdx = *(extData.dFdxMatrixPtr);
1606  N_LAS_Matrix & dQdx = *(extData.dQdxMatrixPtr);
1607  regVec[ireg]->setupPointers (dFdx,dQdx);
1608  }
1609 
1610 }
1611 
1612 //-----------------------------------------------------------------------------
1613 // Function : Instance::updateTemperature
1614 // Purpose :
1615 // Special Notes :
1616 // Scope : public
1617 // Creator : Eric R. Keiter, SNL
1618 // Creation Date : 02/09/08
1619 //-----------------------------------------------------------------------------
1620 bool Instance::updateTemperature( const double & temp )
1621 {
1622 
1623 #ifdef Xyce_DEBUG_DEVICE
1624  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
1625  {
1626  Xyce::dout() << "Start Instance::updateTemperature" << std::endl;
1627  Xyce::dout() << "temp = "<<temp << std::endl;
1628  }
1629 #endif
1630  if( temp != -999.0 ) TEMP = temp;
1631 
1633  {
1634  // some params may have changed during interpolation
1636  }
1637 
1638  //Generation of temperature based factors
1639  double TNOM = model_.TNOM;
1640 
1641  // update the rate constants for the defect reactions
1642  int numRegions = regVec.size();
1643  for (int i=0;i<numRegions;++i)
1644  {
1645  regVec[i]->setRateConstants(TEMP);
1646  }
1647 
1648  // all Species diffusion:
1649  if (thVec.empty() && !(regVec.empty()) )
1650  {
1651  int numSpecies = regVec[0]->getNumSpecies();
1652  thVec.reserve(numSpecies);
1653  for (int ispec=0;ispec<numSpecies;++ispec)
1654  {
1655  std::string speciesName = regVec[0]->getSpeciesName(ispec);
1656 
1657  double Dtmp = (regVec[0])->getDiffusionCoefficient (ispec,TEMP);
1658 
1659  thVec.push_back(N_DEV_TransportHelper(Dtmp,speciesName));
1660 
1661 #ifdef Xyce_DEBUG_DEVICE
1662  if (getDeviceOptions().debugLevel > 0)
1663  {
1665  {
1666  Xyce::dout() << "Vacancy Diffusion: scaling=TRUE ";
1667  Xyce::dout() << "D_"<<speciesName<<" = " << Dtmp << std::endl;
1668  }
1669  else
1670  {
1671  Xyce::dout() << "Vacancy Diffusion: scaling=FALSE ";
1672  Xyce::dout() << "D_"<<speciesName<<" = " << Dtmp << std::endl;
1673  }
1674  }
1675 #endif
1676  }
1677 
1678 #ifdef Xyce_DEBUG_DEVICE
1679  if (getDeviceOptions().debugLevel > 0)
1680  {
1681  for (int ispec=0;ispec<numSpecies;++ispec)
1682  {
1683  Xyce::dout() << "Vacancy Diffusion: D_" << thVec[ispec].name << " transportFlag = ";
1684  if (thVec[ispec].transportFlag) Xyce::dout() << "TRUE";
1685  else Xyce::dout() << "FALSE";
1686  Xyce::dout() << std::endl;
1687  }
1688  }
1689 #endif
1690 
1691  }
1692 
1693  return true;
1694 }
1695 
1696 //-----------------------------------------------------------------------------
1697 // Function : Instance::loadDeviceMask
1698 //
1699 // Purpose : Loads the zero elements of the device mask
1700 //
1701 // Special Notes : elements of the error vector associated with zero
1702 // elements of the mask will not be included in weighted
1703 // norms by the time integrator.
1704 //
1705 // Scope : public
1706 // Creator : Eric R. Keiter, SNL, Electrical and Microsystems Modeling
1707 // Creation Date : 02/09/08
1708 //-----------------------------------------------------------------------------
1710 {
1711  bool returnVal=false;
1712  N_LAS_Vector & maskVector = *(extData.deviceMaskVectorPtr);
1713 
1714  int numRegions = regVec.size();
1715  if (numRegions > 0)
1716  {
1717  for (int ireg=0;ireg<numRegions;++ireg)
1718  {
1719  bool tmpval = regVec[ireg]->loadDeviceMask (maskVector);
1720 
1721  // if any of these return true, then set return val to true.
1722  if (tmpval) returnVal = true;
1723  }
1724  }
1725 
1726  return (returnVal);
1727 }
1728 
1729 //-----------------------------------------------------------------------------
1730 // Function : Instance::loadDAEQVector
1731 //
1732 // Purpose : Loads the Q-vector contributions for a single
1733 // instance.
1734 //
1735 // Special Notes : The "Q" vector is part of a standard DAE formalism in
1736 // which the system of equations is represented as:
1737 //
1738 // f(x) = dQ(x)/dt + F(x) - B(t) = 0
1739 //
1740 // Scope : public
1741 // Creator : Eric R. Keiter, SNL
1742 // Creation Date : 02/09/08
1743 //-----------------------------------------------------------------------------
1745 {
1746  double * daeQVec = extData.daeQVectorRawPtr;
1747  double * dQdxdVp = extData.dQdxdVpVectorRawPtr;
1748 
1749  // Do the reaction terms. There will be no voltage limiting
1750  // contributions here.
1751  int numRegions = regVec.size();
1752  for (int ireg=0;ireg<numRegions;++ireg)
1753  {
1754  regVec[ireg]->loadDAEQVector (daeQVec);
1755  }
1756 
1757  return true;
1758 }
1759 
1760 //-----------------------------------------------------------------------------
1761 // Function : Instance::loadDAEFVector
1762 //
1763 // Purpose : Loads the F-vector contributions for a single
1764 // instance.
1765 //
1766 // Special Notes :
1767 //
1768 // Scope : public
1769 // Creator : Eric R. Keiter, SNL
1770 // Creation Date : 02/09/08
1771 //-----------------------------------------------------------------------------
1773 {
1774  double * daeFVec = extData.daeFVectorRawPtr;
1775  double * dFdxdVp = extData.dFdxdVpVectorRawPtr;
1776  double * solVector = extData.nextSolVectorRawPtr;
1777 
1778  double vbe_diff = 0.0;
1779 
1780  // Now do the reaction terms. There will be no voltage limiting
1781  // contributions here.
1782  int numRegions = regVec.size();
1783  for (int ireg=0;ireg<numRegions;++ireg)
1784  {
1785  // Internal reaction terms are handled by the region class.
1786  regVec[ireg]->loadDAEFVector (daeFVec);
1787  regVec[ireg]->loadDAEdFdxdV(dFdxdVp, vbe_diff);
1788  }
1789 
1790  if (transportFlag)
1791  {
1792  if (!getSolverState().dcopFlag)
1793  {
1794  int numSpecies = thVec.size();
1795  int isp=0;
1796  for (isp=0;isp<numSpecies;++isp)
1797  {
1798  if (!(thVec[isp].transportFlag)) continue;
1799 
1800  int i=0;
1801  int size = regVec.size();
1802 
1803  std::vector<int> & specie_id = thVec[isp].specie_id;
1804 
1805  double dx1 = dxVec[0];
1806  double xloVal = (thVec[isp].fluxVec[0]-thVec[isp].flux_bc1)/dx1;
1807  daeFVec[specie_id[0]] += xloVal;
1808 
1809  for (i=1;i<size-1;++i)
1810  {
1811  double fluxDif = (thVec[isp].fluxVec[i]-thVec[isp].fluxVec[i-1]);
1812  double aveDx = (dxVec[i-1]+dxVec[i])*0.5;
1813  daeFVec[specie_id[i]] += (fluxDif)/aveDx;
1814  }
1815 
1816  double dx2 = dxVec[size-2];
1817  double xhiVal = (thVec[isp].flux_bc2-thVec[isp].fluxVec[size-2])/dx2;
1818  daeFVec[specie_id[size-1]] += xhiVal;
1819  } // end of species loop.
1820  }
1821  }
1822 
1823  return true;
1824 }
1825 
1826 //-----------------------------------------------------------------------------
1827 // Function : Instance::loadDAEdQdx
1828 //
1829 // Purpose : Loads the Q-vector contributions for a single
1830 // instance.
1831 //
1832 // Special Notes : The "Q" vector is part of a standard DAE formalism in
1833 // which the system of equations is represented as:
1834 //
1835 // f(x) = dQ(x)/dt + F(x) - B(t) = 0
1836 //
1837 // Scope : public
1838 // Creator : Eric R. Keiter, SNL
1839 // Creation Date : 02/09/08
1840 //-----------------------------------------------------------------------------
1842 {
1843  bool bsuccess = true;
1844  bool tmpBool = true;
1845  N_LAS_Matrix & dQdxMat = *(extData.dQdxMatrixPtr);
1846 
1847 #ifdef Xyce_DEBUG_DEVICE
1848  char tmpstr[256];
1849 
1851  {
1852  Xyce::dout() << subsection_divider << std::endl;
1853  Xyce::dout() << "Rxn dQdx load:" << std::endl;
1854  Xyce::dout() << " name = " << getName() << std::endl;
1855  }
1856 #endif
1857 
1858  tmpBool = loadQMatrix (dQdxMat);
1859  bsuccess = bsuccess && tmpBool;
1860 
1861 #ifdef Xyce_DEBUG_DEVICE
1862  if (getDeviceOptions().debugLevel > 1 && getSolverState().debugTimeFlag)
1863  Xyce::dout() << subsection_divider << std::endl;
1864 #endif
1865 
1866  return bsuccess;
1867 }
1868 
1869 //-----------------------------------------------------------------------------
1870 // Function : Instance::loadQMatrix
1871 //
1872 // Purpose : Loads the Q-vector contributions for a single
1873 // instance.
1874 //
1875 // Special Notes : See the special notes for loadDAEdQdxMatrix
1876 //
1877 // Scope : public
1878 // Creator : Eric R. Keiter, SNL
1879 // Creation Date : 10/19/08
1880 //-----------------------------------------------------------------------------
1881 bool Instance::loadQMatrix (N_LAS_Matrix & dQdxMat)
1882 {
1883  bool bsuccess = true;
1884 
1885  // Finally, we need to add in the block of jacobian entries for the
1886  // reactions
1887  int numRegions = regVec.size();
1888  for (int ireg=0;ireg<numRegions;++ireg)
1889  {
1890  regVec[ireg]->loadDAEdQdx (dQdxMat);
1891  }
1892  return bsuccess;
1893 }
1894 
1895 //-----------------------------------------------------------------------------
1896 // Function : Instance::loadDAEdFdx ()
1897 //
1898 // Purpose : Loads the F-vector contributions for a single
1899 // instance.
1900 //
1901 // Special Notes : This is an algebraic constaint.
1902 //
1903 // Scope : public
1904 // Creator : Eric R. Keiter, SNL
1905 // Creation Date : 02/09/08
1906 //-----------------------------------------------------------------------------
1908 {
1909  bool bsuccess = true;
1910  bool tmpBool = true;
1911 
1912  N_LAS_Matrix & dFdxMat = *(extData.dFdxMatrixPtr);
1913 
1914 #ifdef Xyce_DEBUG_DEVICE
1915  char tmpstr[256];
1916 
1918  {
1919  Xyce::dout() << subsection_divider << std::endl;
1920  Xyce::dout() << "Rxn dFdx load:" << std::endl;
1921  Xyce::dout() << " name = " << getName() << std::endl;
1922  }
1923 #endif
1924 
1925  tmpBool = loadFMatrix (dFdxMat);
1926  bsuccess = bsuccess && tmpBool;
1927 
1928 #ifdef Xyce_DEBUG_DEVICE
1929  if (getDeviceOptions().debugLevel > 1 && getSolverState().debugTimeFlag)
1930  {
1931  Xyce::dout() << subsection_divider << std::endl;
1932  }
1933 #endif
1934 
1935  return bsuccess;
1936 }
1937 
1938 //-----------------------------------------------------------------------------
1939 // Function : Instance::loadFMatrix
1940 //
1941 // Purpose : Loads the F-vector contributions for a single
1942 // instance.
1943 //
1944 // Special Notes : See the special notes for loadDAEdFdxMatrix
1945 //
1946 // Scope : public
1947 // Creator : Eric R. Keiter, SNL
1948 // Creation Date : 10/19/08
1949 //-----------------------------------------------------------------------------
1950 bool Instance::loadFMatrix (N_LAS_Matrix & dFdxMat)
1951 {
1952  bool bsuccess=true;
1953 
1954  // Finally, we need to add in the block of jacobian entries for the
1955  // reactions
1956  int numRegions = regVec.size();
1957  int cSize(0);
1958  int rSize(0);
1959  if (numRegions > 0)
1960  {
1961  cSize = (regVec[0])->getNumConstants();
1962  rSize = (regVec[0])->getNumSpecies();
1963  for (int ireg=0;ireg<numRegions;++ireg)
1964  {
1965  regVec[ireg]->loadDAEdFdx(dFdxMat);
1966  }
1967  if (cols.size() < rSize) cols.resize(rSize,0);
1968  if (vals.size() < cols.size()) vals.resize(cols.size(),0.0);
1969  }
1970 
1971  // This set of loads should be updated to use the bracket operators.
1972  if (transportFlag)
1973  {
1974  if (!getSolverState().dcopFlag)
1975  {
1976  int numSpecies = thVec.size();
1977  int isp=0;
1978  for (isp=0;isp<numSpecies;++isp)
1979  {
1980  if (!(thVec[isp].transportFlag)) continue;
1981 
1982  int i=0;
1983  int size = regVec.size();
1984 
1985  double DiffC = thVec[isp].D_specie;
1986  std::vector<int> & specie_id = thVec[isp].specie_id;
1987 
1988  for (i=0;i<size;++i)
1989  {
1990  int row = specie_id[i];
1991  if (i==0)
1992  {
1993  double aveDx = (dxVec[i]);
1994  double coef = DiffC/(aveDx*dxVec[i]);
1995  int count = 2;
1996 
1997  cols[0] = specie_id[i ]; vals[0] = thVec[isp].bcScale1 * coef;
1998  cols[1] = specie_id[i+1]; vals[1] =-coef;
1999 
2000  bool bs1 = dFdxMat.sumIntoLocalRow (row, count, &vals[0], &cols[0]);
2001  }
2002  else if (i==(size-1))
2003  {
2004  double aveDx = (dxVec[i-1]);
2005  double coef = DiffC/(aveDx*dxVec[i-1]);
2006  int count = 2;
2007  cols[0] = specie_id[i-1]; vals[0] =-coef;
2008  cols[1] = specie_id[i ]; vals[1] = thVec[isp].bcScale2 * coef;
2009 
2010  bool bs1 = dFdxMat.sumIntoLocalRow (row, count, &vals[0], &cols[0]);
2011  }
2012  else
2013  {
2014  double aveDx = 0.5*(dxVec[i-1] + dxVec[i]);
2015  double coef1 = DiffC/(aveDx*dxVec[i-1]);
2016  double coef2 = DiffC/(aveDx*dxVec[i ]);
2017  double coefSum = coef1+coef2;
2018  int count = 3;
2019 
2020  cols[0] = specie_id[i-1]; vals[0] =-coef1;
2021  cols[1] = specie_id[i ]; vals[1] = coefSum;
2022  cols[2] = specie_id[i+1]; vals[2] =-coef2;
2023 
2024  bool bs1 = dFdxMat.sumIntoLocalRow (row, count, &vals[0], &cols[0]);
2025  }
2026  } // for loop over regions
2027  } // for loop over species.
2028  }
2029  }
2030 
2031  return bsuccess;
2032 }
2033 
2034 //-----------------------------------------------------------------------------
2035 // Function : Instance::updatePrimaryState
2036 // Purpose :
2037 // Special Notes :
2038 // Scope : public
2039 // Creator : Eric R. Keiter, SNL
2040 // Creation Date : 02/09/08
2041 //-----------------------------------------------------------------------------
2043 {
2044  bool bsuccess = true;
2045 
2046 #ifdef Xyce_DEBUG_DEVICE
2047 
2048  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
2049  {
2050  Xyce::dout() << subsection_divider << std::endl;
2051  Xyce::dout() << " Start Instance::updatePrimaryState\n";
2052  Xyce::dout() << " name = " << getName() << std::endl;
2053  }
2054 #endif
2055 
2056  // Do the bulk of the work in updateIntermediateVars:
2058 
2059  // From this point onward, the function does nothing if running MPDE.
2060  double * staVector = extData.nextStaVectorRawPtr;
2061  double * currStaVector = extData.currStaVectorRawPtr;
2062 
2063  // We need time derivatives of concentrations from time integrator to
2064  // calculate RHS for old DAE, so save the concentrations in state.
2065  int numRegions = regVec.size();
2066  for (int ireg=0;ireg<numRegions;++ireg)
2067  {
2068  if (regVec[ireg]->haveAnyReactions())
2069  {
2070  int rSize = regVec[ireg]->getNumSpecies();
2071  for (int i=0;i<rSize;++i)
2072  {
2073  staVector[regVec[ireg]->getStateConcentrationLID(i)] = regVec[ireg]->getStateConcentration(i);
2074  }
2075  }
2076  }
2077 
2078  // if this is the first newton step of the first time step
2079  // of the transient simulation, we need to enforce that the
2080  // time derivatives w.r.t. charge are zero. This is to maintain 3f5
2081  // compatibility. ERK.
2082 
2083  if (!(getSolverState().dcopFlag) && (getSolverState().initTranFlag) && getSolverState().newtonIter==0)
2084  {
2085  // Also need to force these derivatives to be zero
2086  numRegions = regVec.size();
2087  for (int ireg=0;ireg<numRegions;++ireg)
2088  {
2089  if (regVec[ireg]->haveAnyReactions())
2090  {
2091  int rSize = regVec[ireg]->getNumSpecies();
2092  for (int i=0;i<rSize;++i)
2093  {
2094  currStaVector[regVec[ireg]->getStateConcentrationLID(i)] = regVec[ireg]->getStateConcentration(i);
2095  }
2096  }
2097  }
2098  }
2099 
2100  return bsuccess;
2101 }
2102 
2103 //-----------------------------------------------------------------------------
2104 // Function : Instance::updateSecondaryState
2105 // Purpose :
2106 // Special Notes :
2107 // Scope : public
2108 // Creator : Eric R. Keiter, SNL
2109 // Creation Date : 02/09/08
2110 //-----------------------------------------------------------------------------
2112 {
2113  double * staDeriv = extData.nextStaDerivVectorRawPtr;
2114 
2115 #ifdef Xyce_DEBUG_DEVICE
2116 
2117  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
2118  {
2119  Xyce::dout() << subsection_divider << std::endl;
2120  Xyce::dout() << " Start Instance::updateSecondaryState\n";
2121  Xyce::dout() << " name = " << getName() << std::endl;
2122  }
2123 #endif
2124 
2125  // We need time derivatives of concentrations from time integrator
2126  int numRegions = regVec.size();
2127  for (int ireg=0;ireg<numRegions;++ireg)
2128  {
2129  regVec[ireg]->updateSecondaryState (staDeriv);
2130  }
2131 
2132  return true;
2133 }
2134 
2135 //-----------------------------------------------------------------------------
2136 // Function : Instance::updateIntermediateVars
2137 // Purpose :
2138 // Special Notes :
2139 // Scope : public
2140 // Creator : Eric R. Keiter
2141 // Creation Date : 02/09/08
2142 //-----------------------------------------------------------------------------
2144 {
2145  double * solVec = extData.nextSolVectorRawPtr;
2146  double * oldSolVec = extData.currSolVectorRawPtr;
2147 
2148  double * staVec = extData.nextStaVectorRawPtr;
2149  double * currStaVec = extData.currStaVectorRawPtr;
2150 
2151 #ifdef Xyce_DEBUG_DEVICE
2152  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
2153  {
2154  Xyce::dout() << subsection_divider << std::endl;
2155  Xyce::dout() << " In Instance::updateIntermediateVars" << std::endl;
2156  Xyce::dout() << " name = " << getName() << std::endl;
2157  }
2158 #endif
2159 
2160  // Reset this to show we're starting from the originals
2161  origFlag = true;
2162 
2163  // update the "old" newton iteration number.
2164  if (getSolverState().newtonIter != 0 && getSolverState().newtonIter != newtonIterOld)
2165  {
2167  }
2168 
2169  double time=getSolverState().currTime;
2170 
2171  // For reaction kinetics, all we have to do here is copy the concentrations
2172  // out of the solution vector and into the temp storage
2173  int numRegions = regVec.size();
2174  for (int ireg=0;ireg<numRegions;++ireg)
2175  {
2176  regVec[ireg]->setMasterSourceValue(model_.masterSource);
2177  regVec[ireg]->updateIntermediateVars (solVec, oldSolVec, time);
2178  }
2179 
2180 
2181  int numSpecies = thVec.size();
2182  int isp=0;
2183  if (transportFlag)
2184  {
2185  for (isp=0;isp<numSpecies;++isp)
2186  {
2187  if (!(thVec[isp].transportFlag)) continue;
2188 
2189  int i=0;
2190  int size = thVec[isp].fluxVec.size(); // = numRegions-1
2191  double DiffC = thVec[isp].D_specie;
2192  for (i=0;i<size;++i)
2193  {
2194  double n2 = solVec[thVec[isp].specie_id[i ]];
2195  double n1 = solVec[thVec[isp].specie_id[i+1]];
2196 
2197  thVec[isp].fluxVec[i] = DiffC*(n2-n1)/dxVec[i];
2198  }
2199 
2200  // this imposes a direchlet BC (n=0) condition on all species at the ends.
2201  if (dirichletBCFlag)
2202  {
2203  double n1(0.0); // i+1
2204  double n2(0.0); // i
2205 
2206  n1 = solVec[thVec[isp].specie_id[0]];
2207  n2 = 0.0; // i=-1
2208  thVec[isp].flux_bc1 = DiffC*(n2-n1)/dxVec[0];
2209 
2210  n1 = 0.0; // i=max. Remember "size" here is # fluxes, not # regions (=fluxes+1).
2211  n2 = solVec[thVec[isp].specie_id[size]];
2212  thVec[isp].flux_bc2 = DiffC*(n2-n1)/dxVec[size];
2213 
2214  thVec[isp].bcScale1=2.0;
2215  thVec[isp].bcScale2=2.0;
2216  }
2217  else
2218  {
2219  thVec[isp].flux_bc1 = 0.0;
2220  thVec[isp].flux_bc2 = 0.0;
2221  }
2222  }
2223  }
2224 
2225  return true;
2226 }
2227 
2228 
2229 //-----------------------------------------------------------------------------
2230 // Function : Instance::getInstanceBreakPoints
2231 // Purpose : This function adds break points to a vector of breakpoints.
2232 //
2233 // It does not bother to check them in any way, or put them
2234 // in order. It only adds them in.
2235 //
2236 // Special Notes : Breakpoints other than those from the photocurrent
2237 // in this device are all generated by the reaction network
2238 //
2239 // Scope : public
2240 // Creator : Eric R. Keiter, SNL, Electrical and Microsystems Modeling
2241 // Creation Date : 02/09/08
2242 //-----------------------------------------------------------------------------
2244  std::vector<N_UTL_BreakPoint> &breakPointTimes)
2245 {
2246  int numRegions = regVec.size();
2247  double junk;
2248  for (int ireg=0;ireg<numRegions;++ireg)
2249  {
2250  regVec[ireg]->setSimTime(getSolverState().currTime);
2251  junk=regVec[ireg]->getBreakTime();
2252  breakPointTimes.push_back(junk);
2253  }
2254  return true;
2255 }
2256 
2257 //-----------------------------------------------------------------------------
2258 // Function : Model::constructComposite
2259 // Purpose :
2260 // Special Notes :
2261 // Scope : public
2262 // Creator : Eric Keiter, SNL
2263 // Creation Date : 02/09/08
2264 //-----------------------------------------------------------------------------
2265 CompositeParam *Model::constructComposite(const std::string & cName, const std::string & pName)
2266 {
2267 #ifdef Xyce_DEBUG_DEVICE
2268  //cout << "Model::constructComposite name = " << name << " cName = " << cName <<std::endl;
2269 #endif
2270 
2271  if (cName == "DOPINGPROFILES" || cName == "REGION")
2272  {
2273  DopeInfo *doping = new DopeInfo();
2274  dopeInfoMap[pName] = doping;
2275  return (static_cast<CompositeParam *> (doping));
2276  }
2277  else if (cName == "SOURCELIST")
2278  {
2279  SpecieSource *sources = new SpecieSource();
2280  defectSourceMap[pName] = sources;
2281  return (static_cast<CompositeParam *> (sources));
2282  }
2283  else
2284  {
2285  std::string msg =
2286  "Model::constructComposite: unrecognized composite name: ";
2287  msg += cName;
2288  N_ERH_ErrorMgr::report ( N_ERH_ErrorMgr::DEV_FATAL,msg);
2289  }
2290 
2291  return NULL;
2292 }
2293 
2294 //-----------------------------------------------------------------------------
2295 // Function : Model::processParams
2296 // Purpose :
2297 // Special Notes :
2298 // Scope : public
2299 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
2300 // Creation Date : 02/09/08
2301 //-----------------------------------------------------------------------------
2303 {
2304  return true;
2305 }
2306 
2307 //----------------------------------------------------------------------------
2308 // Function : Model::processInstanceParams
2309 // Purpose :
2310 // Special Notes :
2311 // Scope : public
2312 // Creator : Eric R. Keiter, SNL
2313 // Creation Date : 02/09/08
2314 //----------------------------------------------------------------------------
2316 {
2317 
2318  std::vector<Instance*>::iterator iter;
2319  std::vector<Instance*>::iterator first = instanceContainer.begin();
2320  std::vector<Instance*>::iterator last = instanceContainer.end();
2321 
2322  for (iter=first; iter!=last; ++iter)
2323  {
2324  (*iter)->processParams();
2325  }
2326 
2327  return true;
2328 }
2329 
2330 //-----------------------------------------------------------------------------
2331 // Function : Model::Model
2332 // Purpose : modelblock constructor
2333 // Special Notes :
2334 // Scope : public
2335 // Creator :
2336 // Creation Date : 02/09/08
2337 //-----------------------------------------------------------------------------
2339  const Configuration & configuration,
2340  const ModelBlock & MB,
2341  const FactoryBlock & factory_block)
2342  : DevicePDEModel(MB,configuration.getModelParameters(), factory_block),
2343  TNOM(300.0),
2344 
2345  userNumRegions(0),
2346  rxnFileName("NOFILE"),
2347  xlo(1.0e-5),
2348  xhi(3.0e-4),
2349 
2350  xlo_source(1.0e-5),
2351  xhi_source(3.0e-4),
2352  xlo_sourceGiven(false),
2353  xhi_sourceGiven(false),
2354 
2355  masterSource(0.0)
2356 {
2357 
2358  // Set params to constant default values:
2359  setDefaultParams ();
2360 
2361  // Set params according to .model line and constant defaults from metadata:
2362  setModParams (MB.params);
2363 
2364  // Set any non-constant parameter defaults:
2365  if (!given("TNOM"))
2367 
2368  // if the specification makes no sense, then turn it off:
2369  if (xlo_source >= xhi_source)
2370  {
2371  xlo_sourceGiven = false;
2372  xhi_sourceGiven = false;
2373  UserWarning(*this) << "XLO_SOURCE >= XHI_SOURCE. Ignoring, and using a spatially uniform source";
2374  }
2375 
2376  // If the source region was not specified(or turned off), make sure it
2377  // extends through the entire integration volume:
2378  if (!xlo_sourceGiven)
2379  {
2380  xlo_source = xlo;
2381  }
2382 
2383  if (!xhi_sourceGiven)
2384  {
2385  xhi_source = xhi;
2386  }
2387 
2388  // Calculate any parameters specified as expressions:
2389 
2391 
2392  // calculate dependent (ie computed) params and check for errors:
2393 
2394  processParams ();
2395 }
2396 
2397 //-----------------------------------------------------------------------------
2398 // Function : Model::~Model
2399 // Purpose : destructor
2400 // Special Notes :
2401 // Scope : public
2402 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
2403 // Creation Date : 02/09/08
2404 //-----------------------------------------------------------------------------
2406 {
2407  std::vector<Instance*>::iterator iter;
2408  std::vector<Instance*>::iterator first = instanceContainer.begin();
2409  std::vector<Instance*>::iterator last = instanceContainer.end();
2410 
2411  for (iter=first; iter!=last; ++iter)
2412  {
2413  delete (*iter);
2414  }
2415 
2416  if (!(regionDataVec.empty()))
2417  {
2418  int size = regionDataVec.size();
2419  int i=0;
2420  for (i=0;i<size;++i)
2421  {
2422  if (regionDataVec[i] != 0)
2423  {
2424  delete regionDataVec[i];
2425  regionDataVec[i] = 0;
2426  }
2427  }
2428  }
2429 
2430  // Loop over the dopeInfoMap (if it is not empty) and delete its contents.
2431  if (!(dopeInfoMap.empty()))
2432  {
2433  std::map<std::string,DopeInfo *>::iterator iter;
2434  std::map<std::string,DopeInfo *>::iterator begin = dopeInfoMap.begin();
2435  std::map<std::string,DopeInfo *>::iterator end = dopeInfoMap.end ();
2436 
2437  for(iter=begin;iter!=end;++iter)
2438  {
2439  if (iter->second != 0) delete iter->second;
2440  }
2441  }
2442 
2443  // Do the same for the defect source map.
2444  if (!(defectSourceMap.empty()))
2445  {
2446  std::map<std::string,SpecieSource *>::iterator iter;
2447  std::map<std::string,SpecieSource *>::iterator begin = defectSourceMap.begin();
2448  std::map<std::string,SpecieSource *>::iterator end = defectSourceMap.end ();
2449 
2450  for(iter=begin;iter!=end;++iter)
2451  {
2452  if (iter->second != 0)
2453  {
2454  delete iter->second;
2455  iter->second=0;
2456  }
2457  }
2458  defectSourceMap.clear();
2459  }
2460 
2461 }
2462 
2463 //-----------------------------------------------------------------------------
2464 // Function : Model::printOutInstances
2465 // Purpose : debugging tool.
2466 // Special Notes :
2467 // Scope : public
2468 // Creator :
2469 // Creation Date : 02/09/08
2470 //-----------------------------------------------------------------------------
2471 std::ostream &Model::printOutInstances(std::ostream &os) const
2472 {
2473  std::vector<Instance*>::const_iterator iter;
2474  std::vector<Instance*>::const_iterator first = instanceContainer.begin();
2475  std::vector<Instance*>::const_iterator last = instanceContainer.end();
2476 
2477  int i;
2478  os << " name model name Parameters" << std::endl;
2479  for (i = 0, iter = first; iter != last; ++iter, ++i)
2480  {
2481  os << " " << i << ": " << (*iter)->getName() << " ";
2482  os << getName();
2483 
2484  os << std::endl;
2485  os << " TEMP = " << (*iter)->TEMP << std::endl;
2486 
2487  os << std::endl;
2488  }
2489 
2490  os << std::endl;
2491 
2492  return os;
2493 }
2494 
2495 void Model::forEachInstance(DeviceInstanceOp &op) const /* override */ {
2496  for (std::vector<Instance *>::const_iterator it = instanceContainer.begin(); it != instanceContainer.end(); ++it)
2497  op(*it);
2498 }
2499 
2500 
2501 Device *Traits::factory(const Configuration &configuration, const FactoryBlock &factory_block)
2502 {
2503 
2504  return new DeviceMaster<Traits>(configuration, factory_block, factory_block.solverState_, factory_block.deviceOptions_);
2505 }
2506 
2508 {
2510  .registerDevice("rxn", 1)
2511  .registerModelType("rxn", 1);
2512 }
2513 
2514 } // namespace RxnSet
2515 } // namespace Device
2516 } // namespace Xyce