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