Xyce  6.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
N_DEV_Diode.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_Diode.C,v $
27 //
28 // Purpose :
29 //
30 // Special Notes :
31 //
32 // Creator : Eric R. Keiter, SNL, Parallel Computational Sciences
33 //
34 // Creation Date : 02/28/00
35 //
36 // Revision Information:
37 // ---------------------
38 //
39 // Revision Number: $Revision: 1.304 $
40 //
41 // Revision Date : $Date: 2014/08/07 21:23:15 $
42 //
43 // Current Owner : $Author: erkeite $
44 //-------------------------------------------------------------------------
45 #include <Xyce_config.h>
46 
47 // ---------- Standard Includes ----------
48 #ifdef HAVE_CMATH
49 #include <cmath>
50 #else
51 #include <math.h>
52 #endif
53 
54 // ---------- Xyce Includes ----------
55 #include <N_DEV_Const.h>
56 #include <N_DEV_DeviceOptions.h>
57 #include <N_DEV_Diode.h>
58 #include <N_DEV_ExternData.h>
59 #include <N_DEV_MatrixLoadData.h>
60 #include <N_DEV_SolverState.h>
61 #include <N_DEV_Message.h>
62 #include <N_ERH_ErrorMgr.h>
63 
64 #include <N_LAS_Matrix.h>
65 #include <N_LAS_Vector.h>
66 
67 namespace Xyce {
68 namespace Device {
69 namespace Diode {
70 
72 {
73  p.addPar ("AREA", 1.0, &Diode::Instance::Area)
74  .setCategory(CAT_GEOMETRY)
75  .setDescription("Area scaling value (scales IS, ISR, IKF, RS, CJ0, and IBV)");
76 
77  p.addPar ("IC", 0.0, &Diode::Instance::InitCond)
78  .setGivenMember(&Diode::Instance::InitCondGiven)
79  .setCategory(CAT_NONE);
80 
81  p.addPar ("TEMP", 0.0, &Diode::Instance::Temp)
82  .setExpressionAccess(ParameterType::TIME_DEP)
83  .setDescription("Device temperature");
84 
85  p.addPar ("OFF", 0, &Diode::Instance::off)
86  .setUnit(U_LOGIC)
87  .setCategory(CAT_CONTROL)
88  .setDescription("Initial voltage drop across device set to zero");
89  p.addPar ("LAMBERTW", 0, &Diode::Instance::lambertWFlag)
90  .setUnit(U_LOGIC)
91  .setCategory(CAT_CONTROL)
92  .setDescription("Option to solve diode equations with the Lambert-W function");
93 }
94 
96 {
97  p.addPar ("IS", 1.0e-14, &Diode::Model::IS)
98  .setUnit(U_AMP)
99  .setCategory(CAT_CURRENT)
100  .setDescription("Saturation current")
101  .setAnalyticSensitivityAvailable(true)
102  .setSensitivityFunctor(&diodeSens);
103 
104  // synonym for IS
105  p.addPar ("JS", 1.0e-14, &Diode::Model::IS)
106  .setUnit(U_AMP)
107  .setCategory(CAT_CURRENT)
108  .setDescription("Saturation current")
109  .setAnalyticSensitivityAvailable(true)
110  .setSensitivityFunctor(&diodeSens);
111 
112  p.addPar ("RS", 0.0, &Diode::Model::RS)
113  .setExpressionAccess(ParameterType::MIN_RES)
114  .setUnit(U_OHM)
115  .setCategory(CAT_RES)
116  .setDescription("Parasitic resistance")
117  .setAnalyticSensitivityAvailable(true)
118  .setSensitivityFunctor(&diodeSens);
119 
120  p.addPar ("N", 1.0, &Diode::Model::N)
121  .setCategory(CAT_PROCESS)
122  .setOriginalValueStored(true)
123  .setDescription("Emission coefficient")
124  .setAnalyticSensitivityAvailable(true)
125  .setSensitivityFunctor(&diodeSens);
126 
127  p.addPar ("ISR", 0.0, &Diode::Model::ISR)
128  .setUnit(U_AMP)
129  .setCategory(CAT_CURRENT)
130  .setDescription("Recombination current parameter (level 2)")
131  .setAnalyticSensitivityAvailable(true)
132  .setSensitivityFunctor(&diodeSens);
133 
134  p.addPar ("NR", 2.0, &Diode::Model::NR)
135  .setCategory(CAT_NONE)
136  .setDescription("Emission coefficient for ISR (level 2)")
137  .setAnalyticSensitivityAvailable(true)
138  .setSensitivityFunctor(&diodeSens);
139 
140  p.addPar ("IKF", 0.0, &Diode::Model::IKF)
141  .setUnit(U_AMP)
142  .setCategory(CAT_CURRENT)
143  .setDescription("High-injection \"knee\" current (level 2)")
144  .setAnalyticSensitivityAvailable(true)
145  .setSensitivityFunctor(&diodeSens);
146 
147  p.addPar ("TT", 0.0, &Diode::Model::TT)
148  .setUnit(U_SECOND)
149  .setCategory(CAT_PROCESS)
150  .setDescription("Transit time")
151  .setAnalyticSensitivityAvailable(true)
152  .setSensitivityFunctor(&diodeSens);
153 
154  p.addPar ("CJO", 0.0, &Diode::Model::CJO)
155  .setExpressionAccess(ParameterType::MIN_CAP)
156  .setUnit(U_FARAD)
157  .setCategory(CAT_CAP)
158  .setDescription("Zero-bias p-n depletion capacitance")
159  .setAnalyticSensitivityAvailable(true)
160  .setSensitivityFunctor(&diodeSens);
161 
162  // synonyms for CJO
163  p.addPar ("CJ", 0.0, &Diode::Model::CJO)
164  .setExpressionAccess(ParameterType::MIN_CAP)
165  .setUnit(U_FARAD)
166  .setCategory(CAT_CAP)
167  .setDescription("Zero-bias p-n depletion capacitance")
168  .setAnalyticSensitivityAvailable(true)
169  .setSensitivityFunctor(&diodeSens);
170 
171  p.addPar ("CJ0", 0.0, &Diode::Model::CJO)
172  .setExpressionAccess(ParameterType::MIN_CAP)
173  .setUnit(U_FARAD)
174  .setCategory(CAT_CAP)
175  .setDescription("Zero-bias p-n depletion capacitance")
176  .setAnalyticSensitivityAvailable(true)
177  .setSensitivityFunctor(&diodeSens);
178 
179  p.addPar ("VJ", 1.0, &Diode::Model::VJ)
180  .setUnit(U_VOLT)
181  .setCategory(CAT_VOLT)
182  .setDescription("Potential for p-n junction")
183  .setAnalyticSensitivityAvailable(true)
184  .setSensitivityFunctor(&diodeSens);
185 
186  p.addPar ("M", 0.5, &Diode::Model::M)
187  .setCategory(CAT_PROCESS)
188  .setDescription("Grading parameter for p-n junction")
189  .setAnalyticSensitivityAvailable(true)
190  .setSensitivityFunctor(&diodeSens);
191 
192  p.addPar ("EG", 1.11, &Diode::Model::EG)
193  .setUnit(U_EV)
194  .setCategory(CAT_PROCESS)
195  .setDescription("Bandgap voltage (barrier height)")
196  .setAnalyticSensitivityAvailable(true)
197  .setSensitivityFunctor(&diodeSens);
198 
199  p.addPar ("XTI", 3.0, &Diode::Model::XTI)
200  .setCategory(CAT_TEMP)
201  .setDescription("IS temperature exponent")
202  .setAnalyticSensitivityAvailable(true)
203  .setSensitivityFunctor(&diodeSens);
204 
205  p.addPar ("TIKF", 0.0, &Diode::Model::TIKF)
206  .setUnit(U_DEGCM1)
207  .setCategory(CAT_TEMP)
208  .setDescription("IKF temperature coefficient (linear) (level 2)")
209  .setAnalyticSensitivityAvailable(true)
210  .setSensitivityFunctor(&diodeSens);
211 ////
212  p.addPar ("TBV1", 0.0, &Diode::Model::TBV1)
213  .setUnit(U_DEGCM1)
214  .setCategory(CAT_TEMP)
215  .setDescription("BV temperature coefficient (linear) (level 2)");
216 
217  p.addPar ("TBV2", 0.0, &Diode::Model::TBV2)
218  .setUnit(U_DEGCM2)
219  .setCategory(CAT_TEMP)
220  .setDescription("BV temperature coefficient (quadratic) (level 2)");
221 
222  p.addPar ("TRS1", 0.0, &Diode::Model::TRS1)
223  .setUnit(U_DEGCM1)
224  .setCategory(CAT_TEMP)
225  .setDescription("RS temperature coefficient (linear) (level 2)");
226 
227  p.addPar ("TRS2", 0.0, &Diode::Model::TRS2)
228  .setUnit(U_DEGCM2)
229  .setCategory(CAT_TEMP)
230  .setDescription("RS temperature coefficient (quadratic) (level 2)");
231 ////
232  p.addPar ("FC", 0.5, &Diode::Model::FC)
233  .setCategory(CAT_CAP)
234  .setDescription("Forward-bias depletion capacitance coefficient")
235  .setAnalyticSensitivityAvailable(true)
236  .setSensitivityFunctor(&diodeSens);
237 
238  p.addPar ("BV", 1E99, &Diode::Model::BV)
239  .setGivenMember(&Diode::Model::BVGiven)
240  .setUnit(U_VOLT)
241  .setCategory(CAT_VOLT)
242  .setDescription("Reverse breakdown \"knee\" voltage")
243  .setAnalyticSensitivityAvailable(true)
244  .setSensitivityFunctor(&diodeSens);
245 
246  // synonym for BV
247  p.addPar ("VB", 1E99, &Diode::Model::BV)
248  .setGivenMember(&Diode::Model::BVGiven)
249  .setUnit(U_VOLT)
250  .setCategory(CAT_VOLT)
251  .setDescription("Reverse breakdown \"knee\" voltage")
252  .setAnalyticSensitivityAvailable(true)
253  .setSensitivityFunctor(&diodeSens);
254 
255  p.addPar ("IBV", 1.0e-3, &Diode::Model::IBV)
256  .setUnit(U_AMP)
257  .setCategory(CAT_CURRENT)
258  .setDescription("Reverse breakdown \"knee\" current")
259  .setAnalyticSensitivityAvailable(true)
260  .setSensitivityFunctor(&diodeSens);
261 
262  p.addPar ("IRF", 1.0, &Diode::Model::IRF)
263  .setCategory(CAT_CURRENT)
264  .setDescription("Reverse current fitting factor")
265  .setAnalyticSensitivityAvailable(true)
266  .setSensitivityFunctor(&diodeSens);
267 
268  p.addPar ("NBV", 1.0, &Diode::Model::NBV)
269  .setCategory(CAT_PROCESS)
270  .setDescription("Reverse breakdown ideality factor (level 2)")
271  .setAnalyticSensitivityAvailable(true)
272  .setSensitivityFunctor(&diodeSens);
273 
274  p.addPar ("IBVL", 0.0, &Diode::Model::IBVL)
275  .setUnit(U_AMP)
276  .setCategory(CAT_CURRENT)
277  .setDescription("Low-level reverse breakdown \"knee\" current (level 2)")
278  .setAnalyticSensitivityAvailable(true)
279  .setSensitivityFunctor(&diodeSens);
280 
281  p.addPar ("NBVL", 1.0, &Diode::Model::NBVL)
282  .setCategory(CAT_PROCESS)
283  .setDescription("Low-level reverse breakdown ideality factor (level 2)")
284  .setAnalyticSensitivityAvailable(true)
285  .setSensitivityFunctor(&diodeSens);
286 
287  p.addPar ("TNOM", 0.0, &Diode::Model::TNOM)
288  .setCategory(CAT_NONE)
289  .setDescription("")
290  .setAnalyticSensitivityAvailable(true)
291  .setSensitivityFunctor(&diodeSens);
292 
293  p.addPar ("KF", 0.0, &Diode::Model::KF)
294  .setCategory(CAT_FLICKER)
295  .setDescription("Flicker noise coefficient")
296  .setAnalyticSensitivityAvailable(true)
297  .setSensitivityFunctor(&diodeSens);
298 
299  p.addPar ("AF", 1.0, &Diode::Model::AF)
300  .setCategory(CAT_FLICKER)
301  .setDescription("Flicker noise exponent")
302  .setAnalyticSensitivityAvailable(true)
303  .setSensitivityFunctor(&diodeSens);
304 }
305 
306 
307 
308 std::vector< std::vector<int> > Instance::jacStamp_RS;
309 std::vector< std::vector<int> > Instance::jacStamp;
310 
311 std::vector<int> Instance::jacMap_RS;
312 std::vector<int> Instance::jacMap;
313 
314 std::vector< std::vector<int> > Instance::jacMap2_RS;
315 std::vector< std::vector<int> > Instance::jacMap2;
316 
317 //-----------------------------------------------------------------------------
318 // Function : Instance::processParams
319 // Purpose :
320 // Special Notes :
321 // Scope : public
322 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
323 // Creation Date : 6/03/02
324 //-----------------------------------------------------------------------------
326 {
328  return true;
329 }
330 
331 //-----------------------------------------------------------------------------
332 // Function : Instance::Instance
333 // Purpose : "instance block" constructor
334 // Special Notes :
335 // Scope : public
336 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
337 // Creation Date : 3/16/00
338 //-----------------------------------------------------------------------------
340  const Configuration & configuration,
341  const InstanceBlock & instance_block,
342  Model & model,
343  const FactoryBlock & factory_block)
344  : DeviceInstance(instance_block, configuration.getInstanceParameters(), factory_block),
345  model_(model),
346  off(0),
347  Area(1.0),
348  InitCond(0.0),
349  Temp(getDeviceOptions().temp.getImmutableValue<double>()),
350  lambertWFlag(0),
351  InitCondGiven(false),
352  tJctPot(0.0),
353  tJctCap(0.0),
354  tDepCap(0.0),
355  tSatCur(0.0),
356  tVcrit(0.0),
357  tF1(0.0),
358  tBrkdwnV(0.0),
359  tSatCurR(0.0),
360  tIKF(0.0),
361  tRS(0.0),
362  tIRF(1.0),
363  Id(0.0),
364  Gd(0.0),
365  Cd(0.0),
366  Gcd(0.0),
367  Icd(0.0),
368  Qd(0.0),
369  Gspr(0.0),
370  Vpp(0.0),
371  Vp(0.0),
372  Vn(0.0),
373  Vc(0.0),
374  Vd(0.0),
375  Vd_old(0.0),
376  Vd_orig(0.0),
377  newtonIterOld(0),
378  li_Pos(-1),
379  li_Neg(-1),
380  li_Pri(-1),
381  APosEquPosNodeOffset(-1),
382  APosEquPriNodeOffset(-1),
383  ANegEquNegNodeOffset(-1),
384  ANegEquPriNodeOffset(-1),
385  APriEquPosNodeOffset(-1),
386  APriEquNegNodeOffset(-1),
387  APriEquPriNodeOffset(-1),
389  fPosEquPosNodePtr(0),
390  fPosEquPriNodePtr(0),
391  fNegEquNegNodePtr(0),
392  fNegEquPriNodePtr(0),
393  fPriEquPosNodePtr(0),
394  fPriEquNegNodePtr(0),
395  fPriEquPriNodePtr(0),
396  qPosEquPosNodePtr(0),
397  qPosEquPriNodePtr(0),
398  qNegEquNegNodePtr(0),
399  qNegEquPriNodePtr(0),
400  qPriEquPosNodePtr(0),
401  qPriEquNegNodePtr(0),
402  qPriEquPriNodePtr(0),
403 #endif
404  li_storevd(-1),
405  li_store_dev_i(-1)
406 {
407  numIntVars = 1;
408  numExtVars = 2;
409  numStateVars = 0;
410  setNumStoreVars(1);
411  numLeadCurrentStoreVars = 1; // lead current DEV_I
412 
413  if( jacStamp.empty() )
414  {
415  jacStamp_RS.resize(3);
416  jacStamp_RS[0].resize(2);
417  jacStamp_RS[0][0]=0;
418  jacStamp_RS[0][1]=2;
419  jacStamp_RS[1].resize(2);
420  jacStamp_RS[1][0]=1;
421  jacStamp_RS[1][1]=2;
422  jacStamp_RS[2].resize(3);
423  jacStamp_RS[2][0]=0;
424  jacStamp_RS[2][1]=1;
425  jacStamp_RS[2][2]=2;
426 
427  jacMap_RS.clear();
429  jacStamp, jacMap, jacMap2, 2, 0, 3);
430 
431  }
432 
433  // Set params to constant default values:
434  setDefaultParams ();
435 
436  // Set params according to instance line and constant defaults from metadata:
437  setParams (instance_block.params);
438 
439  // Set any non-constant parameter defaults:
440  if (!given("LAMBERTW"))
442  if (!given("TEMP"))
443  Temp = getDeviceOptions().temp.getImmutableValue<double>();
444  if ( (model_.RS == 0.0) || (lambertWFlag == 1) )
445  numIntVars = 0;
446  if ( model_.CJO == 0.0 )
447  numStateVars = 1;
448  if ( (model_.RS == 0.0) && (lambertWFlag == 1) )
449  model_.RS =1.0e-12;
450 
451  // Calculate any parameters specified as expressions:
453 
454  // calculate dependent (ie computed) params and check for errors:
455  processParams ();
456 }
457 
458 //-----------------------------------------------------------------------------
459 // Function : Instance::~Instance
460 // Purpose : destructor
461 // Special Notes :
462 // Scope : public
463 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
464 // Creation Date : 3/16/00
465 //-----------------------------------------------------------------------------
467 {
468 }
469 
470 // Additional Declarations
471 //-----------------------------------------------------------------------------
472 // Function : Instance::registerLIDs
473 // Purpose : function for registering, and setting up, local ID's.
474 // Special Notes :
475 // Scope : public
476 // Creator : Robert Hoekstra, SNL, Parallel Computational Sciences
477 // Creation Date : 6/20/02
478 //-----------------------------------------------------------------------------
479 void Instance::registerLIDs( const std::vector<int> & intLIDVecRef,
480  const std::vector<int> & extLIDVecRef)
481 {
482  AssertLIDs(intLIDVecRef.size() == numIntVars);
483  AssertLIDs(extLIDVecRef.size() == numExtVars);
484 
485 #ifdef Xyce_DEBUG_DEVICE
486  if (getDeviceOptions().debugLevel > 0)
487  {
488  Xyce::dout() << std::endl << section_divider << std::endl;
489  Xyce::dout() << "In Instance::register LIDs\n\n";
490  Xyce::dout() << "name = " << getName() << std::endl;
491  Xyce::dout() << "number of internal variables: " << numIntVars << std::endl;
492  Xyce::dout() << "number of external variables: " << numExtVars << std::endl;
493  }
494 #endif
495 
496  // copy over the global ID lists.
497  intLIDVec = intLIDVecRef;
498  extLIDVec = extLIDVecRef;
499 
500  // Now use these lists to obtain the indices into the linear algebra
501  // entities. This assumes an order.
502 
503  li_Pos = extLIDVec[0];
504  li_Neg = extLIDVec[1];
505 
506  //Setup of Pri node indices
507  //If RS=0, Pri=Pos Node
508  //--------------------------
509  if( model_.RS && (lambertWFlag != 1) )
510  li_Pri = intLIDVec[0];
511  else
512  li_Pri = li_Pos;
513 
514 #ifdef Xyce_DEBUG_DEVICE
515  if (getDeviceOptions().debugLevel > 0)
516  {
517  Xyce::dout() << "\nSolution and RHS variables:\n";
518  Xyce::dout() << "\nli_Pos = ";
519  Xyce::dout().width(4);
520  Xyce::dout() << li_Pos << std::endl;
521 
522  Xyce::dout() << "\nli_Neg = ";
523  Xyce::dout().width(4);
524  Xyce::dout() << li_Neg << std::endl;
525 
526  Xyce::dout() << "\nli_Pri = ";
527  Xyce::dout().width(4);
528  Xyce::dout() << li_Pri << std::endl;
529 
530  }
531 #endif
532 
533 
534 
535 #ifdef Xyce_DEBUG_DEVICE
536  if (getDeviceOptions().debugLevel > 0)
537  {
538  Xyce::dout() << "\nEnd of Instance::register LIDs\n";
539  Xyce::dout() << section_divider << std::endl;
540  }
541 #endif
542 }
543 
544 //-----------------------------------------------------------------------------
545 // Function : Instance::getIntNameMap
546 // Purpose :
547 // Special Notes :
548 // Scope : public
549 // Creator : Eric R. Keiter, SNL, Parallel Computational Sciences
550 // Creation Date : 05/13/05
551 //-----------------------------------------------------------------------------
552 std::map<int,std::string> & Instance::getIntNameMap ()
553 {
554  // set up the internal name map, if it hasn't been already.
555  if (intNameMap.empty ())
556  {
557  std::string tmpstr;
558  if ( li_Pos != li_Pri )
559  {
560  intNameMap[ li_Pri ] = spiceInternalName(getName(), "internal");
561  }
562  }
563 
564  return intNameMap;
565 }
566 
567 //-----------------------------------------------------------------------------
568 // Function : Instance::registerStateLIDs
569 // Purpose :
570 // Special Notes :
571 // Scope : public
572 // Creator : Robert Hoekstra, SNL, Parallel Computational Sciences
573 // Creation Date : 6/20/02
574 //-----------------------------------------------------------------------------
575 void Instance::registerStateLIDs( const std::vector<int> & staLIDVecRef )
576 {
577  AssertLIDs(staLIDVecRef.size() == numStateVars);
578 
579  // copy over the global ID lists:
580  staLIDVec = staLIDVecRef;
581 }
582 
583 //-----------------------------------------------------------------------------
584 // Function : Instance::registerStoreLIDs
585 // Purpose :
586 // Special Notes :
587 // Scope : public
588 // Creator : Eric Keiter
589 // Creation Date :
590 //-----------------------------------------------------------------------------
592  const std::vector<int> & stoLIDVecRef )
593 {
594  AssertLIDs(stoLIDVecRef.size() == getNumStoreVars());
595 
596 // copy over the global ID lists:
597  stoLIDVec = stoLIDVecRef;
598  li_storevd = stoLIDVec[0];
599  if( loadLeadCurrent )
600  {
601  li_store_dev_i = stoLIDVec[1];
602  }
603 
604 #ifdef Xyce_DEBUG_DEVICE
605  if (getDeviceOptions().debugLevel > 0)
606  {
607  Xyce::dout() << "li_storevd = " << li_storevd;
608  }
609 #endif
610 
611 }
612 
613 //-----------------------------------------------------------------------------
614 // Function : N_DEV_DiodeInstance::getStoreNameMap
615 // Purpose :
616 // Special Notes :
617 // Scope : public
618 // Creator : Richard Schiek, Electrical Systems Modeling
619 // Creation Date : 03/27/2013
620 //-----------------------------------------------------------------------------
621 std::map<int, std::string> & N_DEV_DiodeInstance::getStoreNameMap ()
622 {
623  // set up the internal name map, if it hasn't been already.
624  if( storeNameMap.empty () )
625  {
626  storeNameMap[ li_storevd ] = spiceStoreName(getName(), "vd");
627  if( loadLeadCurrent )
628  {
629  storeNameMap[ li_store_dev_i ] = spiceStoreName(getName(), "DEV_I");
630  }
631  }
632  return storeNameMap;
633 }
634 
635 
636 //-----------------------------------------------------------------------------
637 // Function : N_DEV_DiodeInstance::jacobianStamp
638 // Purpose :
639 // Special Notes :
640 // Scope : public
641 // Creator : Robert Hoekstra, SNL, Parallel Computational Sciences
642 // Creation Date : 9/2/02
643 //-----------------------------------------------------------------------------
644 const std::vector< std::vector<int> > & Instance::jacobianStamp() const
645 {
646  if( model_.RS && (lambertWFlag != 1) )
647  return jacStamp_RS;
648  else
649  return jacStamp;
650 }
651 
652 //-----------------------------------------------------------------------------
653 // Function : Instance::registerJacLIDs
654 // Purpose :
655 // Special Notes :
656 // Scope : public
657 // Creator : Robert Hoekstra, SNL, Parallel Computational Sciences
658 // Creation Date : 9/2/02
659 //-----------------------------------------------------------------------------
660 void Instance::registerJacLIDs( const std::vector< std::vector<int> > & jacLIDVec )
661 {
662  DeviceInstance::registerJacLIDs( jacLIDVec );
663  std::vector<int> map;
664  std::vector< std::vector<int> > map2;
665 
666  if( model_.RS && (lambertWFlag != 1) )
667  {
668  map = jacMap_RS;
669  map2 = jacMap2_RS;
670  }
671  else
672  {
673  map = jacMap;
674  map2 = jacMap2;
675  }
676 
677  APosEquPosNodeOffset = jacLIDVec[map[0]][map2[0][0]];
678  APosEquPriNodeOffset = jacLIDVec[map[0]][map2[0][1]];
679 
680  ANegEquNegNodeOffset = jacLIDVec[map[1]][map2[1][0]];
681  ANegEquPriNodeOffset = jacLIDVec[map[1]][map2[1][1]];
682 
683  APriEquPosNodeOffset = jacLIDVec[map[2]][map2[2][0]];
684  APriEquNegNodeOffset = jacLIDVec[map[2]][map2[2][1]];
685  APriEquPriNodeOffset = jacLIDVec[map[2]][map2[2][2]];
686 
687 }
688 
689 //-----------------------------------------------------------------------------
690 // Function : Instance::setupPointers
691 // Purpose :
692 // Special Notes :
693 // Scope : public
694 // Creator : Eric Keiter, SNL
695 // Creation Date : 11/30/08
696 //-----------------------------------------------------------------------------
698 {
699 #ifndef Xyce_NONPOINTER_MATRIX_LOAD
700  N_LAS_Matrix & dFdx = *(extData.dFdxMatrixPtr);
701  N_LAS_Matrix & dQdx = *(extData.dQdxMatrixPtr);
702 
710 
718 #endif
719 }
720 
721 //-----------------------------------------------------------------------------
722 // Function : Instance::loadDAEQVector
723 //
724 // Purpose : Loads the Q-vector contributions for a single
725 // diode instance.
726 //
727 // Special Notes : The "Q" vector is part of a standard DAE formalism in
728 // which the system of equations is represented as:
729 //
730 // f(x) = dQ(x)/dt + F(x) - B(t) = 0
731 //
732 // Scope : public
733 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
734 // Creation Date : 01/26/03
735 //-----------------------------------------------------------------------------
737 {
738  // note: the only capacitor goes from the negative to the
739  // positive-prime node, so there is not contribution in this
740  // function to the positive node.
741 
742  // load in the KCL for the negative node:
743  double coef = Qd;
744  (extData.daeQVectorRawPtr)[li_Neg] -= coef;
745 
746  // load in the KCL for the positive prime node:
747  coef *= -1.0; // -Qd
748  (extData.daeQVectorRawPtr)[li_Pri] -= coef;
749 
750  // load the voltage limiter vector.
752  {
753  double Vd_diff = Vd - Vd_orig;
754  double Cd_Jdxp = 0.0;
755  Cd_Jdxp = -( Cd ) * Vd_diff;
756 
757  // Load the dQdxdVp vector
758  (extData.dQdxdVpVectorRawPtr)[li_Neg] += Cd_Jdxp;
759  (extData.dQdxdVpVectorRawPtr)[li_Pri] -= Cd_Jdxp;
760  }
761 
762  if( loadLeadCurrent && (model_.CJO != 0.0))
763  {
765  }
766 
767  return true;
768 }
769 
770 //-----------------------------------------------------------------------------
771 // Function : Instance::loadDAEFVector
772 //
773 // Purpose : Loads the F-vector contributions for a single
774 // diode instance.
775 //
776 // Special Notes :
777 //
778 // Scope : public
779 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
780 // Creation Date : 01/26/03
781 //-----------------------------------------------------------------------------
783 {
784  // 3f5 compatible currents
785  // Including derivation of Vd_diff and Limiting Correction
786  //---------------------------------------------------------
787  double Ir = Gspr * (Vp - Vpp);
788 
789  // load in the KCL for the positive node:
790  double coef = -Ir;
791  (extData.daeFVectorRawPtr)[li_Pos] -= coef;
792 
793  // load in the KCL for the negative node:
794  coef = Id;
795  (extData.daeFVectorRawPtr)[li_Neg] -= coef;
796 
797  // load in the KCL for the positive prime node:
798  coef *= -1;
799  coef += Ir;
800  (extData.daeFVectorRawPtr)[li_Pri] -= coef;
801 
802  // load the voltage limiter vector.
804  {
805  double Vd_diff = Vd - Vd_orig;
806  double Gd_Jdxp = 0.0;
807  Gd_Jdxp = -( Gd ) * Vd_diff;
808 
809  // Load the dFdxdVp vector
810  (extData.dFdxdVpVectorRawPtr)[li_Neg] += Gd_Jdxp;
811  (extData.dFdxdVpVectorRawPtr)[li_Pri] -= Gd_Jdxp;
812  }
813 
814  if( loadLeadCurrent )
815  {
817  }
818 
819  return true;
820 }
821 
822 //-----------------------------------------------------------------------------
823 // Function : Instance::loadDAEdQdx
824 //
825 // Purpose : Loads the Q-vector contributions for a single
826 // diode instance.
827 //
828 // Special Notes : The "Q" vector is part of a standard DAE formalism in
829 // which the system of equations is represented as:
830 //
831 // f(x) = dQ(x)/dt + F(x) - B(t) = 0
832 //
833 // Scope : public
834 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
835 // Creation Date : 03/05/04
836 //-----------------------------------------------------------------------------
838 {
839  N_LAS_Matrix & dQdx = *(extData.dQdxMatrixPtr);
840 
841 // New Spice3f5 matched conductivities
842 // Only major difference in loads is support for Pos=Pri node when RS=0
843 // For this DAE dQdx load, the capacitance contribution (Gcd) is the
844 // only part used.
845 //---------------------------------------------------------------------
846 
847  dQdx[li_Neg][ANegEquNegNodeOffset] += Cd;
848  dQdx[li_Neg][ANegEquPriNodeOffset] -= Cd;
849 
850  dQdx[li_Pri][APriEquNegNodeOffset] -= Cd;
851  dQdx[li_Pri][APriEquPriNodeOffset] += Cd;
852 
853  return true;
854 }
855 
856 //-----------------------------------------------------------------------------
857 // Function : Instance::loadDAEdFdx ()
858 //
859 // Purpose : Loads the F-vector contributions for a single
860 // diode instance.
861 //
862 // Special Notes :
863 //
864 // Scope : public
865 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
866 // Creation Date : 03/05/04
867 //-----------------------------------------------------------------------------
869 {
870  N_LAS_Matrix & dFdx = *(extData.dFdxMatrixPtr);
871 
872 // New Spice3f5 matched conductivities
873 // Only major difference in loads is support for Pos=Pri node when RS=0
874 // For this DAE dFdx load, the capacitance contribution (Gcd) is removed.
875 //---------------------------------------------------------------------
876 
877  dFdx[li_Pos][APosEquPosNodeOffset] += Gspr;
878  dFdx[li_Pos][APosEquPriNodeOffset] -= Gspr;
879 
880  dFdx[li_Neg][ANegEquNegNodeOffset] += Gd;
881  dFdx[li_Neg][ANegEquPriNodeOffset] -= Gd;
882 
883  dFdx[li_Pri][APriEquPosNodeOffset] -= Gspr;
884  dFdx[li_Pri][APriEquNegNodeOffset] -= Gd;
885  dFdx[li_Pri][APriEquPriNodeOffset] += Gspr + Gd;
886 
887  return true;
888 }
889 
890 //-----------------------------------------------------------------------------
891 // Function : Instance::updatePrimaryState
892 // Purpose : update primary state for one diode instance
893 // Special Notes :
894 // Scope : public
895 // Creator : Tom Russo, Component Information and Models
896 // Creation Date : 1/10/01
897 //-----------------------------------------------------------------------------
899 {
900  double * stoVec = extData.nextStoVectorRawPtr;
901  stoVec[li_storevd] = Vd;
902 
903  //Qd - capacitor charge generated in updateIntermediateVars
904  bool bsuccess = updateIntermediateVars ();
905 
906  return bsuccess;
907 }
908 
909 //-----------------------------------------------------------------------------
910 // Function : Instance::updateIntermediateVars
911 // Purpose : update intermediate variables for one diode instance
912 // Special Notes :
913 // Scope : public
914 // Creator : Tom Russo, Component Information and Models
915 // Creation Date : 1/10/01
916 //-----------------------------------------------------------------------------
918 {
919  bool bsuccess = true;
920 
921  double * solVec = extData.nextSolVectorRawPtr;
922 
923  //diode parameters
924  double M; // grading parameter
925  double BV; // breakdown voltage
926  double IBV; // reverse breakdown current
927  double NBV; // reverse breakdown ideality factor
928  double IBVL; // low-level reverse breakdown current
929  double NBVL; // low-level reverse breakdown ideality factor
930  double N; // non-ideality factor.
931  double NR; // emission coeff. for ISR.
932  double TT; // transit time.
933  double F2; // capacitive polynomial factor
934  double F3; // capacitive polynomial factor
935  double Inorm; // normal diffusion current
936  double Irec; // recombination current
937  double Kgen; // generation factor
938  double Khi; // high-injection factor
939  double Gd1, DKgen;
940  double Gd2, DKhi;
941 
942  //Model Diode parameters
943  M = model_.M;
944  BV = model_.BV;
945  IBV = model_.IBV;
946  NBV = model_.NBV;
947  IBVL = model_.IBVL;
948  NBVL = model_.NBVL;
949  N = model_.N;
950  NR = model_.NR;
951  TT = model_.TT;
952  F2 = model_.F2;
953  F3 = model_.F3;
954 
955  // obtain voltage drop accross the capacitor:
956  Vp = Vn = Vpp = 0.0;
957  Vpp = solVec[li_Pri];
958  Vn = solVec[li_Neg];
959  Vp = solVec[li_Pos];
960 
961  // Junction Voltage
962  Vd = Vpp - Vn;
963 
964  double Isat = tSatCur * Area;
965  double IsatR = tSatCurR * Area;
966  double Vt = CONSTKoverQ * Temp;
967  double Vte = N * Vt;
968  double VteR = NR * Vt;
969 
970  Gspr = tCOND * Area;
971 
972  Vd_orig = Vd;
973  origFlag = true;
974 
975  // Setup initial junction conditions if UIC enabled
976  //------------------------------------------------
977  if (getSolverState().newtonIter == 0)
978  {
979  newtonIterOld = 0;
980  //Vd_old = Vd;
981  if (getSolverState().initJctFlag && getDeviceOptions().voltageLimiterFlag)
982  {
983  if (InitCondGiven)
984  {
985  Vd = InitCond;
986  origFlag = false;
987  }
988  else if (off)
989  {
990  Vd = 0.0;
991  origFlag = false;
992  }
993  else
994  {
995  if (getSolverState().inputOPFlag)
996  {
997  N_LAS_Vector * flagSolVectorPtr = extData.flagSolVectorPtr;
998  if ((*flagSolVectorPtr)[li_Pos] == 0 ||
999  (*flagSolVectorPtr)[li_Neg] == 0 ||
1000  (*flagSolVectorPtr)[li_Pri] == 0)
1001  {
1002  Vd=tVcrit;
1003  origFlag = false;
1004  }
1005  }
1006  else
1007  {
1008  Vd=tVcrit;
1009  origFlag = false;
1010  }
1011  }
1012  }
1013 
1014  // assume there is no history -- then check if the
1015  // state vector can overwrite this
1016  Vd_old = Vd;
1017 
1018  if (!(getSolverState().dcopFlag)||(getSolverState().locaEnabledFlag && getSolverState().dcopFlag))
1019  {
1021  }
1022  }
1023  else // just do this whenever it isn't the first iteration:
1024  {
1026  }
1027 
1028  // Voltage limiting based on mode of diode
1029  //---------------------------------------
1031  {
1032  int ichk = 0;
1033 
1034  if (getSolverState().newtonIter >= 0)
1035  {
1036  //Test if breakdown voltage given or not
1037  if (model_.BVGiven && (Vd < Xycemin(0.0, -BV + 10.0 * Vte)))
1038  {
1039  double Vdtmp = -( BV + Vd );
1040  Vdtmp = devSupport.pnjlim(Vdtmp, -(Vd_old+BV), Vte, tVcrit, &ichk);
1041  Vd = -(Vdtmp + BV);
1042  }
1043  else
1044  Vd = devSupport.pnjlim(Vd, Vd_old, Vte, tVcrit, &ichk);
1045 
1046  if (ichk) origFlag = false;
1047  }
1048  }
1049 
1050  // update the "old" newton iteration number.
1051  if (getSolverState().newtonIter != 0 && getSolverState().newtonIter != newtonIterOld)
1052  {
1054  }
1055 
1056 #ifdef Xyce_DEBUG_DEVICE
1057  if (getDeviceOptions().debugLevel>0 && getSolverState().debugTimeFlag)
1058  {
1059  Xyce::dout() << Xyce::section_divider << std::endl;
1060  Xyce::dout() << "Instance::updateIntermediateVars " << getName()<<std::endl;
1061  }
1062 #endif
1063 
1064  // Current and Conductivity
1065  //----------------------------------------------
1066 
1067  int level = model_.getLevel();
1068  if(level == 1)
1069  {
1070  // Using LambertW function
1071  if (lambertWFlag == 1)
1072  {
1073  double RS = model_.RS;
1074  if (Vd >= -3.0 * Vte)
1075  {
1076  lambertWCurrent(Isat, Vte, RS);
1077  }
1078  // linear reverse bias
1079  else if ( !tBrkdwnV || (Vd >= -tBrkdwnV) )
1080  {
1081  lambertWLinearReverseBias(Isat, Vte, RS);
1082  }
1083  // reverse breakdown
1084  else
1085  {
1086  lambertWBreakdownCurrent(Isat, Vte, RS);
1087  }
1088 
1089  double Vrs;
1090  Vrs = (Id + Icd)*RS;
1091  Vc = Vd - Vrs;
1092  }
1093  else if (lambertWFlag == 2)
1094  {
1095  if (Vd >= -3.0 * Vte)
1096  {
1097  lambertWCurrent(Isat, Vte, 1.0e-15);
1098  }
1099  // linear reverse bias
1100  else if ( !tBrkdwnV || (Vd >= -tBrkdwnV) )
1101  {
1102  lambertWLinearReverseBias(Isat, Vte, 1.0e-15);
1103  }
1104  // reverse breakdown
1105  else
1106  {
1107  lambertWBreakdownCurrent(Isat, Vte, 1.0e-15);
1108  }
1109  Vc = Vd;
1110  }
1111 
1112  // Normal exponential
1113  else
1114  {
1115  // Adjustment for linear portion of reverse current
1116  double IRfactor;
1117  //if(Vd >= 0) IRfactor = 1.0; //error? shouldn't this be Vd>=-3*Vte????
1118  if(Vd >= -3.0 * Vte) IRfactor = 1.0; //changing this to be consistent
1119  else IRfactor = tIRF; //with model in reference guide.
1120  Isat *= IRfactor;
1121 
1122  if (Vd >= -3.0 * Vte)
1123  {
1124  double arg1 = Vd / Vte;
1125  arg1 = Xycemin(CONSTMAX_EXP_ARG, arg1);
1126  double evd = exp(arg1);
1127 
1128  Id = Isat * (evd - 1.0) + getDeviceOptions().gmin * Vd;
1129  Gd = Isat * evd / Vte + getDeviceOptions().gmin;
1130 #ifdef Xyce_DEBUG_DEVICE
1131  if (getDeviceOptions().debugLevel>0 && getSolverState().debugTimeFlag)
1132  {
1133  Xyce::dout() << "Normal exponential regime." << std::endl;
1134  Xyce::dout() << " Vd = " << Vd << std::endl;
1135  Xyce::dout() << " Vte = " << Vte << std::endl;
1136  Xyce::dout() << " Id = " << Id << std::endl;
1137  Xyce::dout() << " Gd = " << Gd << std::endl;
1138  }
1139 #endif
1140 
1141  }
1142 
1143  // Linear reverse bias
1144  else if(!tBrkdwnV || (Vd >= -tBrkdwnV))
1145  {
1146  double arg = 3.0 * Vte / (Vd * CONSTe);
1147  arg = arg * arg * arg;
1148  Id = -Isat * (1.0 + arg) + getDeviceOptions().gmin * Vd;
1149  Gd = Isat * 3.0 * arg / Vd + getDeviceOptions().gmin;
1150 #ifdef Xyce_DEBUG_DEVICE
1151  if (getDeviceOptions().debugLevel>0 && getSolverState().debugTimeFlag)
1152  {
1153  Xyce::dout() << "Linear reverse bias regime." << std::endl;
1154  Xyce::dout() << " Vd = " << Vd << std::endl;
1155  Xyce::dout() << " tBrkdwnV = " << tBrkdwnV << std::endl;
1156  Xyce::dout() << " Id = " << Id << std::endl;
1157  Xyce::dout() << " Gd = " << Gd << std::endl;
1158  }
1159 #endif
1160  }
1161 
1162  // Reverse breakdown
1163  else
1164  {
1165  double arg1 = -(tBrkdwnV + Vd) / Vte;
1166  arg1 = Xycemin(CONSTMAX_EXP_ARG, arg1);
1167  double evrev = exp(arg1);
1168 
1169 #ifdef Xyce_BREAKDOWN_ORIGINAL
1170  Id = -Isat * evrev + getDeviceOptions().gmin * Vd;
1171  Gd = Isat * evrev / Vte + getDeviceOptions().gmin;
1172 #else
1173  //added by K. Santarelli 9/18/07 to account for change in tBrkdwnV
1174  //calculation.
1175  double arg2=3.0*Vte/(CONSTe*tBrkdwnV);
1176  arg2=arg2*arg2*arg2;
1177  double Isat_tBrkdwnV=Isat*(1-arg2);
1178  Id = -Isat_tBrkdwnV * evrev + getDeviceOptions().gmin * Vd;
1179  Gd = Isat_tBrkdwnV * evrev / Vte + getDeviceOptions().gmin;
1180 #endif
1181 
1182 #ifdef Xyce_DEBUG_DEVICE
1183  if (getDeviceOptions().debugLevel>0 && getSolverState().debugTimeFlag)
1184  {
1185  Xyce::dout() << "Reverse breakdown regime." << std::endl;
1186  Xyce::dout() << " Vd = " << Vd << std::endl;
1187  Xyce::dout() << " tBrkdwnV = " << tBrkdwnV << std::endl;
1188  Xyce::dout() << " Id = " << Id << std::endl;
1189  Xyce::dout() << " Gd = " << Gd << std::endl;
1190  }
1191 #endif
1192  }
1193  Vc = Vd;
1194  }
1195 
1196  }
1197  else if(level == 2)
1198  {
1199 #ifdef Xyce_DEBUG_DEVICE
1200  if (getDeviceOptions().debugLevel>0 && getSolverState().debugTimeFlag)
1201  {
1202  Xyce::dout() << " Level 2 diode code " << std::endl;
1203  }
1204 #endif
1205  // Adjustment for linear portion of reverse current
1206  double IRfactor;
1207  //if(Vd >= 0) IRfactor = 1.0; //error? Shouldn't this be Vd >= -3*Vte?
1208  if(Vd >= -3.0*Vte) IRfactor=1.0; //changing it to be consistent with model
1209  else IRfactor = tIRF; //in the reference manual.
1210  Isat *= IRfactor;
1211 
1212  if (Vd >= -3.0 * Vte)
1213  {
1214  double arg1 = Vd / Vte;
1215  arg1 = Xycemin(CONSTMAX_EXP_ARG, arg1);
1216  double evd = exp(arg1);
1217  Inorm = Isat * (evd - 1.0) + getDeviceOptions().gmin * Vd;
1218  Gd1 = Isat*evd/Vte + getDeviceOptions().gmin;
1219 
1220  arg1 = Vd / VteR;
1221  arg1 = Xycemin(CONSTMAX_EXP_ARG, arg1);
1222  evd = exp(arg1);
1223  Irec = IsatR * (evd - 1.0);
1224  Gd2 = IsatR*evd/VteR;
1225 
1226  Khi = 1;
1227  DKhi = 0;
1228  if(tIKF > 0)
1229  {
1230  Khi = sqrt(tIKF/(tIKF+Inorm));
1231  DKhi = 0.5*Khi*Gd1/(tIKF+Inorm);
1232  }
1233  Kgen = 0;
1234  DKgen = 0;
1235  if(Irec != 0)
1236  {
1237  Kgen = sqrt( pow(((1-Vd/tJctPot)*(1-Vd/tJctPot) + 0.005),M) );
1238  DKgen = -M*(1-Vd/tJctPot)/(tJctPot*Kgen);
1239  }
1240 
1241  Id = Inorm*Khi + Irec*Kgen;
1242  Gd = Gd1*Khi + Inorm*DKhi + Gd2*Kgen + Irec*DKgen;
1243 #ifdef Xyce_DEBUG_DEVICE
1244  if (getDeviceOptions().debugLevel>0 && getSolverState().debugTimeFlag)
1245  {
1246  Xyce::dout() << "L2 Normal exponential regime." << std::endl;
1247  Xyce::dout() << " Vd = " << Vd << std::endl;
1248  Xyce::dout() << " Vte = " << Vte << std::endl;
1249  Xyce::dout() << " Id = " << Id << std::endl;
1250  Xyce::dout() << " Irec= " << Irec << std::endl;
1251  Xyce::dout() << " Gd = " << Gd << std::endl;
1252  Xyce::dout() << " Gd1 = " << Gd1 << std::endl;
1253  Xyce::dout() << " Gd2 = " << Gd2 << std::endl;
1254  Xyce::dout() << " Khi = " << Khi << std::endl;
1255  Xyce::dout() << " Kgen=" << Kgen << std::endl;
1256  Xyce::dout() << "DKgen=" <<DKgen << std::endl;
1257  }
1258 #endif
1259  }
1260 
1261  // Linear reverse bias
1262  else if(!tBrkdwnV || (Vd >= -tBrkdwnV))
1263  {
1264  double arg = 3.0 * Vte / (Vd * CONSTe);
1265  arg = arg * arg * arg;
1266  Id = -Isat * (1.0 + arg) + getDeviceOptions().gmin * Vd;
1267  Gd = Isat * 3.0 * arg / Vd + getDeviceOptions().gmin;
1268 #ifdef Xyce_DEBUG_DEVICE
1269  if (getDeviceOptions().debugLevel>0 && getSolverState().debugTimeFlag)
1270  {
1271  Xyce::dout() << "L2 Linear reverse bias regime." << std::endl;
1272  Xyce::dout() << " Vd = " << Vd << std::endl;
1273  Xyce::dout() << " tBrkdwnV = " << tBrkdwnV << std::endl;
1274  Xyce::dout() << " Id = " << Id << std::endl;
1275  Xyce::dout() << " Gd = " << Gd << std::endl;
1276  }
1277 #endif
1278  }
1279 
1280  // Reverse breakdown
1281  else
1282  {
1283  double arg1 = -(tBrkdwnV + Vd) / Vte;
1284  arg1 = Xycemin(CONSTMAX_EXP_ARG, arg1);
1285  double evrev = exp(arg1);
1286 
1287 #ifdef Xyce_BREAKDOWN_ORIGINAL
1288  Id = -Isat * evrev + getDeviceOptions().gmin * Vd;
1289  Gd = Isat * evrev / Vte + getDeviceOptions().gmin;
1290 #else
1291  //added 9/18/07 by K. Santarelli to account for change in tBrkdwnV
1292  //calculation.
1293  double arg2=3.0*Vte/(CONSTe*tBrkdwnV);
1294  arg2=arg2*arg2*arg2;
1295  double Isat_tBrkdwnV=Isat*(1-arg2);
1296  Id = -Isat_tBrkdwnV * evrev + getDeviceOptions().gmin * Vd;
1297  Gd = Isat_tBrkdwnV * evrev / Vte + getDeviceOptions().gmin;
1298 #endif
1299 #ifdef Xyce_DEBUG_DEVICE
1300  if (getDeviceOptions().debugLevel>0 && getSolverState().debugTimeFlag)
1301  {
1302  Xyce::dout() << "L2 Reverse breakdown regime." << std::endl;
1303  Xyce::dout() << " Vd = " << Vd << std::endl;
1304  Xyce::dout() << " tBrkdwnV = " << tBrkdwnV << std::endl;
1305  Xyce::dout() << " Id = " << Id << std::endl;
1306  Xyce::dout() << " Gd = " << Gd << std::endl;
1307  }
1308 #endif
1309  }
1310  Vc = Vd;
1311 
1312  } // level
1313 
1314  // Only compute if Capacitance is non-zero
1315  //---------------------------------------
1316  if (tJctCap != 0.0)
1317  {
1318  // Charge Storage
1319  double Czero = tJctCap * Area;
1320  if (Vc < tDepCap)
1321  {
1322  //double arg = 1.0 - Vd/VJ;
1323  double arg = 1.0 - Vc / tJctPot;
1324  double arg1 = -M * log(arg);
1325  arg1 = Xycemin(CONSTMAX_EXP_ARG, arg1);
1326  double sarg = exp(arg1);
1327 
1328  //Qd = TT*Id + VJ*Czero*(1.0-arg*sarg)/(1.0-M);
1329  Qd = TT * Id + tJctPot * Czero * (1.0 - arg * sarg) / (1.0 - M);
1330  Cd = TT * Gd + Czero * sarg;
1331  }
1332  else
1333  {
1334  double Czof2 = Czero / F2;
1335  double MotJctPot = M / tJctPot;
1336 
1337  //Qd = TT*Id + Czero*tF1 + Czof2*(F3*(Vd-tDepCap)+(M/(VJ+VJ))
1338  Qd = TT * Id + Czero * tF1 +
1339  Czof2 * (F3 * (Vc - tDepCap) + (0.5 * MotJctPot) *
1340  (Vc * Vc - tDepCap * tDepCap));
1341  //Cd = TT*Gd + Czof2*(F3+M*Vd/VJ);
1342  Cd = TT * Gd + Czof2 * (F3 + MotJctPot * Vc);
1343  }
1344 #ifdef Xyce_DEBUG_DEVICE
1346  {
1347  Xyce::dout() << "Qd = " << Qd << std::endl;
1348  Xyce::dout() << "Cd = " << Qd << std::endl;
1349  }
1350 #endif
1351  }
1352  else
1353  {
1354  Qd = 0.0;
1355  Cd = 0.0;
1356  }
1357 
1358 #ifdef Xyce_DEBUG_DEVICE
1359  if (getDeviceOptions().debugLevel>0 && getSolverState().debugTimeFlag)
1360  {
1361  Xyce::dout() << Xyce::section_divider << std::endl;
1362  }
1363 #endif
1364 
1365  return bsuccess;
1366 }
1367 
1368 //-----------------------------------------------------------------------------
1369 // Function : Instance::updateTemperature
1370 // Purpose : update intermediate variables for one diode instance
1371 // Special Notes :
1372 // Scope : public
1373 // Creator : Tom Russo, Component Information and Models
1374 // Creation Date : 1/10/01
1375 //-----------------------------------------------------------------------------
1376 bool Instance::updateTemperature( const double & temp )
1377 {
1378 
1379  double vtnom = CONSTKoverQ * model_.TNOM;
1380 
1381  double xfc = log( 1.0 - model_.FC );
1382 
1383  if( temp != -999.0 ) Temp = temp;
1384  double TNOM = model_.TNOM;
1385 
1386  double vt = CONSTKoverQ * Temp;
1387  double fact2 = Temp / CONSTREFTEMP;
1388  double egfet = CONSTEg0 - (CONSTalphaEg*Temp*Temp)/(Temp+CONSTbetaEg);
1389  double arg = -egfet/(2.0*CONSTboltz*Temp) +
1391  double pbfact = -2.0*vt*(1.5*log(fact2)+CONSTQ*arg);
1392  double egfet1 = CONSTEg0 - (CONSTalphaEg*model_.TNOM*model_.TNOM)/
1394  double arg1 = -egfet1/(2.0*CONSTboltz*model_.TNOM) +
1396  double fact1 = model_.TNOM/CONSTREFTEMP;
1397  double pbfact1 = -2.0*vtnom*(1.5*log(fact1)+CONSTQ*arg1);
1398 
1399  double pbo = (model_.VJ-pbfact1)/fact1;
1400  double gmaold = (model_.VJ-pbo)/pbo;
1401 
1402  tJctCap = model_.CJO/
1403  (1.0+model_.M*(4.0e-4*(model_.TNOM-CONSTREFTEMP) -gmaold));
1404 
1405  tJctPot = pbfact+fact2*pbo;
1406 
1407  double gmanew = (tJctPot-pbo)/pbo;
1408 
1409  tJctCap *= 1.0 + model_.M*(4.0e-4*(Temp-CONSTREFTEMP)-gmanew);
1410 
1411  tSatCur = model_.IS*exp(((Temp/model_.TNOM)-1.0)*
1412  model_.EG/(model_.N*vt)+
1413  model_.XTI/model_.N*log(Temp/model_.TNOM));
1414 
1415  tF1 = tJctPot*(1.0-exp((1.0-model_.M)*xfc))/(1.0-model_.M);
1416 
1418 
1419  double vte = model_.N*vt;
1420  double tempBV;
1421 
1422  tVcrit = vte*log(vte/(CONSTroot2*tSatCur));
1423  tRS = model_.RS;
1424  tCOND = model_.COND;
1425  tIRF = model_.IRF*pow(fact2,1.6);
1426 
1427  int level = model_.getLevel();
1428  if(level == 2) // this section is PSPICE compatible
1429  {
1430  tSatCurR = model_.ISR*exp((Temp/TNOM - 1.0)*
1431  model_.EG/(model_.NR*vt)+
1432  model_.XTI/model_.NR*log(Temp/TNOM));
1433 
1434  tIKF = model_.IKF*(1 + model_.TIKF*(Temp-TNOM));
1435 
1436  tempBV = model_.BV*(1 + (Temp-TNOM)*
1437  ( model_.TBV1 + model_.TBV2*(Temp-TNOM) ));
1438 
1439  tRS = model_.RS*(1 + (Temp-TNOM)*
1440  ( model_.TRS1 + model_.TRS2*(Temp-TNOM) ));
1441 
1442  tCOND = 0.0;
1443  if(tRS != 0.0) tCOND = 1.0/tRS;
1444 
1445  tJctPot = (model_.VJ - egfet1)*fact2 - 3*vt*log(fact2) + egfet;
1446 
1447  tJctCap = model_.CJO/(1.0 +
1448  model_.M*(4.0e-4*(Temp-TNOM) + (1-tJctPot/model_.VJ)));
1449  }
1450  else
1451  {
1452  tempBV=model_.BV;
1453  }
1454 
1455 #ifdef Xyce_BREAKDOWN_ORIGINAL
1456  //Changed 9/18/07, K. R. Santarelli. This is the original (broken) version
1457  //of the breakdown voltage computation. It has two known issues:
1458  //
1459  //1. It assumes N (the emission coefficient) is always 1 (division by
1460  // vt instead of vte).
1461  //2. While the for loop terminates due to the fact that it's implemented via
1462  // a counter, the value of xbv does not converge in many cases, so the
1463  // value of xbv upon terminating is often garbage.
1464  //
1465  //For consistency with old, existing simulations, this version of the
1466  //breakdown voltage calculation (along with the appropriate code for
1467  //computing the breakdown current in the level 1 and level 2 models---see
1468  //the appropriate sections of
1469  //Instance::updateIntermediateVars) can be invoked by
1470  //specifiying the CPPFLAG "-DXyce_BREAKDOWN_ORIGINAL" when creating a Xyce
1471  //Build. The default, however, is the code which follows #else.
1472 
1473  double reltol = 1.0e-3;
1474  if( model_.BVGiven )
1475  {
1476  double IRfactor = tIRF;
1477  double cbv = model_.IBV;
1478  double xbv, xcbv;
1479  if( cbv < IRfactor*tSatCur*tempBV/vt )
1480  {
1481  cbv = IRfactor*tSatCur*tempBV/vt;
1482  xbv = tempBV;
1483  }
1484  else
1485  {
1486  double tol = reltol*cbv;
1487  xbv = tempBV-vt*log(1.0+cbv/(IRfactor*tSatCur));
1488  for( int i = 0; i < 25; ++i )
1489  {
1490  xbv = tempBV-vt*log(cbv/(IRfactor*tSatCur)+1.0-xbv/vt);
1491  xcbv = IRfactor*tSatCur*(exp((tempBV-xbv)/vt)-1.0+xbv/vt);
1492  if(fabs(xcbv-cbv)<=tol) break;
1493  }
1494  }
1495  tBrkdwnV = xbv;
1496  }
1497 #else
1498  if( model_.BVGiven)
1499  {
1500  double IRFactor=tIRF;
1501  double cbv = model_.IBV;
1502  double xbv;
1503  double cthreshlow; //lower threshold for IBV
1504  double cthreshhigh; //high threshold for IBV
1505  int iter_count;
1506  const int ITER_COUNT_MAX=8;
1507  double arg2;
1508 
1509  double arg1=3.0*vte/(CONSTe*tempBV);
1510  arg1=arg1*arg1*arg1;
1511  cthreshlow=tSatCur*IRFactor*(1-arg1);
1512  cthreshhigh=tSatCur*IRFactor*(1-1.0/(CONSTe*CONSTe*CONSTe)) *
1513  exp(-1.0*(3.0*vte-tempBV)/vte);
1514 
1515  if(cbv >= cthreshhigh)
1516  { //if IBV is too high, tBrkdwnV will go below 3NVt.
1517  tBrkdwnV=3.0*vte; //Clip tBrkdwnV to 3*N*Vt in this case (and hence
1518  } //clip IBV to cthreshhigh).
1519 
1520 
1521  else if(cbv <= cthreshlow)
1522  { //if IBV is too low, tBrkdwnV will go above
1523  tBrkdwnV=tempBV; //BV. Clip tBrkdwnV to BV in this case (and
1524  } //hence clip IBV to cthreshlow).
1525 
1526 
1527  //If IBV is in an acceptable range, perform a Picard iteration to find
1528  //tBrkdwnV, starting with an initial guess of tBrkdwnV=tempBV, and
1529  //running through the iteration ITER_COUNT_MAX times.
1530 
1531  else
1532  {
1533  xbv=tempBV;
1534  for(iter_count=0; iter_count < ITER_COUNT_MAX; iter_count++)
1535  {
1536  arg2=3.0*vte/(CONSTe*xbv);
1537  arg2=arg2*arg2*arg2;
1538  xbv=tempBV-vte*log(cbv/(tSatCur*IRFactor))+vte *
1539  log(1-arg2);
1540  }
1541  tBrkdwnV=xbv;
1542  }
1543 
1544 //Note that, not only is tBrkdwnV adjusted using this method, but the effective
1545 //value of Is (tSatCur) is adjusted as well. The code used to use Is before,
1546 //but now it will use Is*IRFactor*(1-(3*N*Vt/(e*tBrkdwnV))^3) instead. This is
1547 //reflected in changes made to Instance::updateIntermediateVars
1548 //(for "normal" level 1 and 2 diode models) for the reverse breakdown region.
1549 //Changes have not been made to the associated LambertW functions as of yet
1550 //since there seem to be other issues involved with those functions independent
1551 // of this change.
1552  }
1553 #endif
1554 
1555 
1556 #ifdef Xyce_DEBUG_DEVICE
1557  if (getDeviceOptions().debugLevel>0 && getSolverState().debugTimeFlag)
1558  {
1559  Xyce::dout() << Xyce::section_divider << std::endl;
1560  Xyce::dout() << "Instance::UpdateTemperature" << getName() <<std::endl;
1561  Xyce::dout() << " IS = " << model_.IS << std::endl;
1562  Xyce::dout() << " vtnom = " << vtnom << std::endl;
1563  Xyce::dout() << " xfc = " << xfc << std::endl;
1564  Xyce::dout() << " TNOM = " << TNOM << std::endl;
1565  Xyce::dout() << " vt = " << vt << std::endl;
1566  Xyce::dout() << " fact2 = " << fact2 << std::endl;
1567  Xyce::dout() << " egfet = " << egfet << std::endl;
1568  Xyce::dout() << " arg = " << arg << std::endl;
1569  Xyce::dout() << " pbfact = " << pbfact << std::endl;
1570  Xyce::dout() << " egfet1 = " << egfet1 << std::endl;
1571  Xyce::dout() << " arg1 = " << arg1 << std::endl;
1572  Xyce::dout() << " fact1 = " << fact1 << std::endl;
1573  Xyce::dout() << " pbfact1 = " << pbfact1 << std::endl;
1574  Xyce::dout() << " pbo = " << pbo << std::endl;
1575  Xyce::dout() << " gmaold = " << gmaold << std::endl;
1576  Xyce::dout() << " gmanew = " << gmanew << std::endl;
1577  Xyce::dout() << " tJctCap = " << tJctCap << std::endl;
1578  Xyce::dout() << " tJctPot = " << tJctPot << std::endl;
1579  Xyce::dout() << " tSatCur = " << tSatCur << std::endl;
1580  Xyce::dout() << " tF1 = " << tF1 << std::endl;
1581  Xyce::dout() << " tDepCap = " << tDepCap << std::endl;
1582  Xyce::dout() << " vte = " << vte << std::endl;
1583  Xyce::dout() << " tempBV = " << tempBV << std::endl;
1584  Xyce::dout() << " tVcrit = " << tVcrit << std::endl;
1585  Xyce::dout() << " tRS = " << tRS << std::endl;
1586  Xyce::dout() << " tCOND = " << tCOND << std::endl;
1587  Xyce::dout() << " tIRF = " << tIRF << std::endl;
1588  Xyce::dout() << " tBrkdwnV= " << tBrkdwnV << std::endl;
1589  }
1590 #endif
1591 
1592  return true;
1593 }
1594 
1595 
1596 //-----------------------------------------------------------------------------
1597 // Function : Instance::lambertWCurrent
1598 // Purpose : Determine the diode current using Lambert-W function
1599 // Special Notes :
1600 // Scope : public
1601 // Creator : Nick Johnson, Summer Intern
1602 // Creation Date : 7/5/02
1603 //-----------------------------------------------------------------------------
1604 bool Instance::lambertWCurrent(double Isat, double Vte, double RS)
1605 {
1606  // with capacitor current and using LambertW accros whole model
1607  /* if (Cd != 0.0 && Icd != 0 && (lambertWFlag == 1))
1608  {
1609  double AA = Vte*(1.0 + RS*getSolverState().pdt*Cd);
1610  double XX = Icd - getSolverState().pdt*Qd;
1611  double arg1 = (Vd + RS*Isat - RS*XX)/AA;
1612  arg1 = Xycemin(CONSTMAX_EXP_ARG, arg1);
1613  double evd = exp(arg1);
1614  double lambWArg = Isat*RS/AA * evd;
1615  double lambWReturn;
1616  int ierr;
1617  double lambWError;
1618  devSupport.lambertw(lambWArg, lambWReturn, ierr, lambWError);
1619 
1620  #ifdef Xyce_DEBUG_DEVICE
1621  if (getDeviceOptions().debugLevel > 0)
1622  {
1623  if (ierr == 0)
1624  Xyce::dout() << "Safe LambertW return" << std::endl;
1625  else if (ierr == 1)
1626  Xyce::dout() << "LambertW argument not in domain" << std::endl;
1627  else
1628  Xyce::dout() << "Arithmetic problems with LambertW" << std::endl;
1629  }
1630  #endif
1631 
1632  double prefac = AA/RS;
1633  Id = -Isat + prefac*lambWReturn;
1634  Gd = lambWReturn / ((1 + lambWReturn)*RS);
1635 
1636  #ifdef Xyce_DEBUG_DEVICE
1637  if (getDeviceOptions().debugLevel > 0)
1638  {
1639  Xyce::dout() << "lambWArg = " << lambWArg << std::endl;
1640  Xyce::dout() << "lambWError = " << lambWError << std::endl;
1641  Xyce::dout() << "Id = " << Id << std::endl;
1642  Xyce::dout() << "Gd = " << Gd << std::endl;
1643  Xyce::dout() << "Icd = " << Icd << std::endl;
1644  Xyce::dout() << "Cd = " << Cd << std::endl;
1645  Xyce::dout() << "Qd = " << Qd << std::endl;
1646  Xyce::dout() << "AA = " << AA << std::endl;
1647  Xyce::dout() << "XX = " << XX << std::endl;
1648  Xyce::dout() << "Using lambertwCurrent w/ capacitance" << std::endl;
1649  }
1650  #endif
1651  }
1652 
1653  // when capacitor current=0, there is no capacitance, or using LambertW
1654  // only across the diode element
1655  else
1656  { */
1657  double arg1 = (Vd + Isat*RS)/Vte;
1658  arg1 = Xycemin(CONSTMAX_EXP_ARG, arg1);
1659  double evd = exp(arg1);
1660  double lambWArg = Isat*RS*evd/Vte;
1661  double lambWReturn;
1662  int ierr;
1663  double lambWError;
1664  devSupport.lambertw(lambWArg, lambWReturn, ierr, lambWError);
1665 
1666  Id = -Isat+Vte*(lambWReturn)/RS;
1667  Gd = lambWReturn / ((1 + lambWReturn)*RS);
1668 
1669  // }
1670 
1671  return true;
1672 }
1673 
1674 //-----------------------------------------------------------------------------
1675 // Function : Instance::lambertWLinearReverseBias
1676 // Purpose : Function to maintain continuity between forward bias and
1677 // breakdown voltages of diode
1678 // Special Notes :
1679 // Scope : public
1680 // Creator : Nick Johnson, Summer Intern
1681 // Creation Date : 7/30/02
1682 //-----------------------------------------------------------------------------
1683 bool Instance::lambertWLinearReverseBias(double Isat, double Vte, double RS)
1684 {
1685  double FF1 = (Vd + tBrkdwnV)/(-3.0 * Vte + tBrkdwnV);
1686  double FF2 = (1/2 - FF1)*(-2);
1687  double arg = Vte/RS;
1688  double arg1 = (Isat/arg - 3);
1689  double arg2 = FF1*arg1;
1690  arg1 = Xycemin(CONSTMAX_EXP_ARG, arg2);
1691  double evd = exp(arg2);
1692  double lambWArg = Isat*evd/arg;
1693  double lambWReturn;
1694  int ierr;
1695  double lambWError;
1696  devSupport.lambertw(lambWArg, lambWReturn, ierr, lambWError);
1697 
1698 #ifdef Xyce_DEBUG_DEVICE
1699  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
1700  {
1701  if (ierr == 0)
1702  Xyce::dout() << "Safe LambertW return" << std::endl;
1703  else if (ierr == 1)
1704  Xyce::dout() << "LambertW argument not in domain" << std::endl;
1705  else
1706  Xyce::dout() << "Arithmetic problems with LambertW" << std::endl;
1707  }
1708 #endif
1709 
1710  Id = -Isat*FF1 + FF2*lambWReturn*arg;
1711 
1712  double GdFF1 = 1/(-3.0*Vte + tBrkdwnV);
1713  double GdFF2 = 2 * GdFF1;
1714  double GdW = arg*lambWReturn*GdFF1*arg1/(1 + lambWReturn);
1715  Gd = -Isat*GdFF1 + GdFF2*arg*lambWReturn + FF2*GdW;
1716 
1717 #ifdef Xyce_DEBUG_DEVICE
1719  {
1720  Xyce::dout() << "lambWArg = " << lambWArg << std::endl;
1721  Xyce::dout() << "lambWError = " << lambWError << std::endl;
1722  Xyce::dout() << "lambWReturn= " << lambWReturn << std::endl;
1723  Xyce::dout() << "Id = " << Id << std::endl;
1724  Xyce::dout() << "Gd = " << Gd << std::endl;
1725  Xyce::dout() << "Using lambertwReverseBias" << std::endl;
1726  }
1727 #endif
1728 
1729  return true;
1730 }
1731 
1732 //-----------------------------------------------------------------------------
1733 // Function : Instance::lambertWBreakdownCurrent
1734 // Purpose : Determine the diode breakdown current using Lambert-W function
1735 // Special Notes :
1736 // Scope : public
1737 // Creator : Nick Johnson, Summer Intern
1738 // Creation Date : 7/11/02
1739 //-----------------------------------------------------------------------------
1740 bool Instance::lambertWBreakdownCurrent(double Isat, double Vte, double RS)
1741 {
1742  // with capacitor current and applying LambertW accros whole diode model
1743  /* if (Cd != 0.0 && Icd != 0 && (lambertWFlag == 1))
1744  {
1745  double AA = Vte*(1 + RS*getSolverState().pdt*Cd);
1746  double XX = Icd - getSolverState().pdt*Qd;
1747  double arg1 = (RS*XX - Vd)/AA - tBrkdwnV/Vte;
1748  arg1 = Xycemin(CONSTMAX_EXP_ARG, arg1);
1749  double evd = exp(arg1);
1750  double lambWArg = Isat*RS*evd/AA;
1751  double lambWReturn;
1752  int ierr;
1753  double lambWError;
1754  devSupport.lambertw(lambWArg, lambWReturn, ierr, lambWError);
1755 
1756  #ifdef Xyce_DEBUG_DEVICE
1757  if (getDeviceOptions().debugLevel > 0)
1758  {
1759  if (ierr == 0)
1760  Xyce::dout() << "Safe LambertW return" << std::endl;
1761  else if (ierr == 1)
1762  Xyce::dout() << "LambertW argument not in domain" << std::endl;
1763  else
1764  Xyce::dout() << "Arithmetic problems with LambertW" << std::endl;
1765  }
1766  #endif
1767 
1768  Id = -AA*lambWReturn/RS;
1769  Gd = (lambWReturn / (1 + lambWReturn)) * (1/RS);
1770 
1771  #ifdef Xyce_DEBUG_DEVICE
1772  if (getDeviceOptions().debugLevel > 0)
1773  {
1774  Xyce::dout() << "lambWArg = " << lambWArg << std::endl;
1775  Xyce::dout() << "lambWError = " << lambWError << std::endl;
1776  Xyce::dout() << "Id = " << Id << std::endl;
1777  Xyce::dout() << "Gd = " << Gd << std::endl;
1778  Xyce::dout() << "Icd = " << Icd << std::endl;
1779  Xyce::dout() << "Cd = " << Cd << std::endl;
1780  Xyce::dout() << "Qd = " << Qd << std::endl;
1781  Xyce::dout() << "AA = " << AA << std::endl;
1782  Xyce::dout() << "XX = " << XX << std::endl;
1783  Xyce::dout() << "Using lambertwBreakdown w/ capacitance" << std::endl;
1784  }
1785  #endif
1786  }
1787 
1788  // without capacitor current or applying lambertW just accros diode element
1789  else
1790  {*/
1791  double arg1 = (-Vd - tBrkdwnV)/ Vte;
1792  arg1 = Xycemin(CONSTMAX_EXP_ARG, arg1);
1793  double evd = exp(arg1);
1794  double lambWArg = Isat*RS*evd/Vte;
1795  double lambWReturn;
1796  int ierr;
1797  double lambWError;
1798  devSupport.lambertw(lambWArg, lambWReturn, ierr, lambWError);
1799 
1800  Id = -Vte*lambWReturn/RS;
1801  Gd = lambWReturn / ((1 + lambWReturn)*RS);
1802 
1803  // }
1804 
1805  return true;
1806 }
1807 
1808 //-----------------------------------------------------------------------------
1809 // Function : Model::processParams
1810 // Purpose :
1811 // Special Notes :
1812 // Scope : public
1813 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1814 // Creation Date : 6/03/02
1815 //-----------------------------------------------------------------------------
1817 {
1818  //limit grading coeff
1819  if( M > 0.9 ) M = 0.9;
1820 
1821  //limit activation energy
1822  if( EG < 0.1 ) EG = 0.1;
1823 
1824  //limit depl cap coeff
1825  if( FC > 0.95 ) FC = 0.95;
1826 
1827  if( RS==0.0 )
1828  COND = 0.0;
1829  else
1830  COND = 1.0/RS;
1831 
1832  double xfc = log(1.0-FC);
1833  F2 = exp((1.0+M)*xfc);
1834  F3 = 1.0-FC*(1.0+M);
1835 
1836  return true;
1837 }
1838 
1839 //----------------------------------------------------------------------------
1840 // Function : Model::processInstanceParams
1841 // Purpose :
1842 // Special Notes :
1843 // Scope : public
1844 // Creator : Dave Shirely, PSSI
1845 // Creation Date : 03/23/06
1846 //----------------------------------------------------------------------------
1848 {
1849 
1850  std::vector<Instance*>::iterator iter;
1851  std::vector<Instance*>::iterator first = instanceContainer.begin();
1852  std::vector<Instance*>::iterator last = instanceContainer.end();
1853 
1854  for (iter=first; iter!=last; ++iter)
1855  {
1856  (*iter)->processParams();
1857  }
1858 
1859  return true;
1860 }
1861 
1862 //-----------------------------------------------------------------------------
1863 // Function : Model::Model
1864 // Purpose : model block constructor
1865 // Special Notes :
1866 // Scope : public
1867 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1868 // Creation Date : 3/16/00
1869 //-----------------------------------------------------------------------------
1871  const Configuration & configuration,
1872  const ModelBlock & MB,
1873  const FactoryBlock & factory_block)
1874  : DeviceModel(MB, configuration.getModelParameters(), factory_block),
1875  IS(1.0e-14),
1876  RS(0.0),
1877  COND(0.0),
1878  N(1.0),
1879  ISR(0.0),
1880  NR(2.0),
1881  IKF(0.0),
1882  TT(0.0),
1883  CJO(0.0),
1884  VJ(1.0),
1885  M(0.5),
1886  EG(1.11),
1887  XTI(3.0),
1888  TIKF(0.0),
1889  TBV1(0.0),
1890  TBV2(0.0),
1891  TRS1(0.0),
1892  TRS2(0.0),
1893  FC(0.5),
1894  BV(1e99),
1895  IBV(1.0e-10),
1896  IRF(1.0),
1897  NBV(1.0),
1898  IBVL(0.0),
1899  NBVL(1.0),
1900  F2(0.0),
1901  F3(0.0),
1902  TNOM(27),
1903  KF(0.0),
1904  AF(1.0),
1905  BVGiven(false)
1906 {
1907 
1908  // Set params to constant default values:
1909  setDefaultParams ();
1910 
1911  // Set params according to .model line and constant defaults from metadata:
1912  setModParams (MB.params);
1913 
1914  // Set any non-constant parameter defaults:
1915  if (!given("TNOM"))
1917 
1918  // Calculate any parameters specified as expressions:
1920 
1921  // calculate dependent (ie computed) params and check for errors:
1922 
1923  // Note: Level 2 only params are: ISR, NR, IKF, NBV, IBVL, NBVL, TIKF, TBV1, TBV2, TRS1, TRS2
1924  if (getLevel() == 1)
1925  {
1926  std::string bad_parameters;
1927 
1928  if (given("ISR"))
1929  bad_parameters += " ISR";
1930  if (given("NR"))
1931  bad_parameters += " NR";
1932  if (given("IKF"))
1933  bad_parameters += " IKF";
1934  if (given("NBV"))
1935  bad_parameters += " NBV";
1936  if (given("IBVL"))
1937  bad_parameters += " IBVL";
1938  if (given("NBVL"))
1939  bad_parameters += " NBVL";
1940  if (given("TIKF"))
1941  bad_parameters += " TIKF";
1942  if (given("TBV1"))
1943  bad_parameters += " TBV1";
1944  if (given("TBV2"))
1945  bad_parameters += " TBV2";
1946  if (given("TRS1"))
1947  bad_parameters += " TRS1";
1948  if (given("TRS2"))
1949  bad_parameters += " TRS2";
1950  if (!bad_parameters.empty())
1951  {
1952  UserError0(*this) << "Illegal parameter(s) given for level 1 diode:" << bad_parameters;
1953  }
1954  }
1955 
1956  processParams ();
1957 }
1958 
1959 
1960 //-----------------------------------------------------------------------------
1961 // Function : Model::~Model
1962 // Purpose : destructor
1963 // Special Notes :
1964 // Scope : public
1965 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1966 // Creation Date : 3/16/00
1967 //-----------------------------------------------------------------------------
1969 {
1970  std::vector<Instance*>::iterator iterI;
1971  std::vector<Instance*>::iterator firstI = instanceContainer.begin ();
1972  std::vector<Instance*>::iterator lastI = instanceContainer.end ();
1973 
1974  // loop over instances:
1975  for (iterI = firstI; iterI != lastI; ++iterI)
1976  {
1977  delete (*iterI);
1978  }
1979 }
1980 
1981 //-----------------------------------------------------------------------------
1982 // Function : Model::printOutInstances
1983 // Purpose : debugging tool.
1984 // Special Notes :
1985 // Scope : public
1986 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1987 // Creation Date : 4/03/00
1988 //-----------------------------------------------------------------------------
1989 
1990 std::ostream &Model::printOutInstances(std::ostream &os) const
1991 {
1992  std::vector<Instance*>::const_iterator iter;
1993  std::vector<Instance*>::const_iterator first = instanceContainer.begin();
1994  std::vector<Instance*>::const_iterator last = instanceContainer.end();
1995 
1996  int i;
1997  os << std::endl;
1998  os << " name model name Parameters" << std::endl;
1999  for (i=0, iter=first; iter!=last; ++iter, ++i)
2000  {
2001  os << " " << i << ": " << (*iter)->getName() << " ";
2002  os << getName();
2003 
2004  os << std::endl;
2005  os << "AREA = " << (*iter)->Area << std::endl;
2006  os << " IC = " << (*iter)->InitCond << std::endl;
2007  os << "TEMP = " << (*iter)->Temp << std::endl;
2008  os << " off = " << (*iter)->off << std::endl;
2009 
2010  os << std::endl;
2011  }
2012 
2013  os << std::endl;
2014 
2015  return os;
2016 }
2017 
2018 //-----------------------------------------------------------------------------
2019 // Function : Model::forEachInstance
2020 // Purpose :
2021 // Special Notes :
2022 // Scope : public
2023 // Creator : David Baur
2024 // Creation Date : 2/4/2014
2025 //-----------------------------------------------------------------------------
2026 /// Apply a device instance "op" to all instances associated with this
2027 /// model
2028 ///
2029 /// @param[in] op Operator to apply to all instances.
2030 ///
2031 ///
2032 void Model::forEachInstance(DeviceInstanceOp &op) const /* override */
2033 {
2034  for (std::vector<Instance *>::const_iterator it = instanceContainer.begin(); it != instanceContainer.end(); ++it)
2035  op(*it);
2036 }
2037 
2038 
2039 //-----------------------------------------------------------------------------
2040 // Diode Master functions:
2041 //-----------------------------------------------------------------------------
2042 
2043 //-----------------------------------------------------------------------------
2044 // Function : Master::updateState
2045 // Purpose :
2046 // Special Notes :
2047 // Scope : public
2048 // Creator : Eric Keiter, SNL
2049 // Creation Date : 11/26/08
2050 //-----------------------------------------------------------------------------
2051 bool Master::updateState (double * solVec, double * staVec, double * stoVec)
2052 {
2053  bool bsuccess = true;
2054 
2055  for (InstanceVector::const_iterator it = getInstanceBegin(); it != getInstanceEnd(); ++it)
2056  {
2057  Instance & di = *(*it);
2058 
2059  // save voltage drops
2060  double * stoVec = di.extData.nextStoVectorRawPtr;
2061  stoVec[di.li_storevd] = di.Vd;
2062 
2063  bool btmp = di.updateIntermediateVars ();
2064  bsuccess = bsuccess && btmp;
2065  }
2066 
2067  return bsuccess;
2068 }
2069 
2070 //-----------------------------------------------------------------------------
2071 // Function : Master::loadDAEVectors
2072 // Purpose :
2073 // Special Notes :
2074 // Scope : public
2075 // Creator : Eric Keiter, SNL
2076 // Creation Date : 11/26/08
2077 //-----------------------------------------------------------------------------
2078 bool Master::loadDAEVectors (double * solVec, double * fVec, double *qVec, double * bVec, double * storeLeadF, double * storeLeadQ)
2079 {
2080  for (InstanceVector::const_iterator it = getInstanceBegin(); it != getInstanceEnd(); ++it)
2081  {
2082  Instance & di = *(*it);
2083  // load F:
2084  double Ir = di.Gspr * (di.Vp - di.Vpp);
2085 
2086  fVec[di.li_Pos] -= -Ir;
2087  fVec[di.li_Neg] -= di.Id;
2088  fVec[di.li_Pri] -= (-di.Id + Ir);
2089 
2090  // load Q:
2091  qVec[di.li_Neg] -= di.Qd;
2092  qVec[di.li_Pri] -= -di.Qd;
2093 
2094  // voltage limiter vectors.
2096  {
2097  double Vd_diff = di.Vd - di.Vd_orig;
2098  double Cd_Jdxp = -( di.Cd ) * Vd_diff;
2099  double Gd_Jdxp = -( di.Gd ) * Vd_diff;
2100 
2101  double * dFdxdVp = di.extData.dFdxdVpVectorRawPtr;
2102  // dFdxdVp vector
2103  dFdxdVp[di.li_Neg] += Gd_Jdxp;
2104  dFdxdVp[di.li_Pri] -= Gd_Jdxp;
2105 
2106  double * dQdxdVp = di.extData.dQdxdVpVectorRawPtr;
2107  // dQdxdVp vector
2108  dQdxdVp[di.li_Neg] += Cd_Jdxp;
2109  dQdxdVp[di.li_Pri] -= Cd_Jdxp;
2110  }
2111 
2112  if( di.loadLeadCurrent )
2113  {
2114  storeLeadF[di.li_store_dev_i] = di.Id;
2115  if (di.model_.CJO != 0.0)
2116  {
2117  storeLeadQ[di.li_store_dev_i] = di.Qd;
2118  }
2119  }
2120  }
2121  return true;
2122 }
2123 
2124 //-----------------------------------------------------------------------------
2125 // Function : Master::loadDAEMatrices
2126 // Purpose :
2127 // Special Notes :
2128 // Scope : public
2129 // Creator : Eric Keiter, SNL
2130 // Creation Date : 11/26/08
2131 //-----------------------------------------------------------------------------
2132 bool Master::loadDAEMatrices (N_LAS_Matrix & dFdx, N_LAS_Matrix & dQdx)
2133 {
2134  for (InstanceVector::const_iterator it = getInstanceBegin(); it != getInstanceEnd(); ++it)
2135  {
2136  Instance & di = *(*it);
2137 
2138 #ifndef Xyce_NONPOINTER_MATRIX_LOAD
2139  // F-matrix:
2140  *di.fPosEquPosNodePtr += di.Gspr;
2141  *di.fPosEquPriNodePtr -= di.Gspr;
2142  *di.fNegEquNegNodePtr += di.Gd;
2143  *di.fNegEquPriNodePtr -= di.Gd;
2144  *di.fPriEquPosNodePtr -= di.Gspr;
2145  *di.fPriEquNegNodePtr -= di.Gd;
2146  *di.fPriEquPriNodePtr += di.Gspr + di.Gd;
2147 
2148  // Q-matrix:
2149  *di.qNegEquNegNodePtr += di.Cd;
2150  *di.qNegEquPriNodePtr -= di.Cd;
2151  *di.qPriEquNegNodePtr -= di.Cd;
2152  *di.qPriEquPriNodePtr += di.Cd;
2153 #else
2154  // F-matrix:
2155  dFdx[di.li_Pos][di.APosEquPosNodeOffset] += di.Gspr;
2156  dFdx[di.li_Pos][di.APosEquPriNodeOffset] -= di.Gspr;
2157  dFdx[di.li_Neg][di.ANegEquNegNodeOffset] += di.Gd;
2158  dFdx[di.li_Neg][di.ANegEquPriNodeOffset] -= di.Gd;
2159  dFdx[di.li_Pri][di.APriEquPosNodeOffset] -= di.Gspr;
2160  dFdx[di.li_Pri][di.APriEquNegNodeOffset] -= di.Gd;
2161  dFdx[di.li_Pri][di.APriEquPriNodeOffset] += di.Gspr + di.Gd;
2162 
2163  // Q-matrix:
2164  dQdx[di.li_Neg][di.ANegEquNegNodeOffset] += di.Cd;
2165  dQdx[di.li_Neg][di.ANegEquPriNodeOffset] -= di.Cd;
2166  dQdx[di.li_Pri][di.APriEquNegNodeOffset] -= di.Cd;
2167  dQdx[di.li_Pri][di.APriEquPriNodeOffset] += di.Cd;
2168 #endif
2169  }
2170  return true;
2171 }
2172 
2173 Device *Traits::factory(const Configuration &configuration, const FactoryBlock &factory_block)
2174 {
2175 
2176  return new Master(configuration, factory_block, factory_block.solverState_, factory_block.deviceOptions_);
2177 }
2178 
2180 {
2182  .registerDevice("d", 1)
2183  .registerDevice("d", 2)
2184  .registerModelType("d", 1)
2185  .registerModelType("d", 2);
2186 }
2187 
2188 //-----------------------------------------------------------------------------
2189 // sensitivity related code.
2190 //
2191 // This is not the final implementation, as it is too klunky. But, it works
2192 // for now as a preliminary experiment.
2193 
2194 
2195 //-----------------------------------------------------------------------------
2196 // Function : processParams
2197 // Purpose : patterned after the model version of this function.
2198 // Special Notes :
2199 // Scope : public
2200 // Creator :
2201 // Creation Date :
2202 //-----------------------------------------------------------------------------
2203 template <typename ScalarT>
2205  ScalarT & M,
2206  ScalarT & EG,
2207  ScalarT & FC,
2208  const ScalarT & RS,
2209  ScalarT & COND,
2210  ScalarT & F2,
2211  ScalarT & F3
2212  )
2213 {
2214  //limit grading coeff
2215  if( M > 0.9 ) M = 0.9;
2216 
2217  //limit activation energy
2218  if( EG < 0.1 ) EG = 0.1;
2219 
2220  //limit depl cap coeff
2221  if( FC > 0.95 ) FC = 0.95;
2222 
2223  if( RS==0.0 )
2224  COND = 0.0;
2225  else
2226  COND = 1.0/RS;
2227 
2228  ScalarT xfc = log(1.0-FC);
2229  F2 = exp((1.0+M)*xfc);
2230  F3 = 1.0-FC*(1.0+M);
2231 
2232  return true;
2233 }
2234 
2235 //-----------------------------------------------------------------------------
2236 // Function : updateTemperature
2237 // Purpose :
2238 // Special Notes :
2239 // Scope : public
2240 // Creator :
2241 // Creation Date :
2242 //-----------------------------------------------------------------------------
2243 template <typename ScalarT>
2244 bool updateTemperature
2245  (
2246  const double & temp,
2247 
2248  // instance variables/params:
2249  ScalarT & Temp,
2250  ScalarT & tJctCap,
2251  ScalarT & tJctPot,
2252  ScalarT & tDepCap,
2253  ScalarT & tF1,
2254  ScalarT & tSatCur,
2255  ScalarT & tSatCurR,
2256  ScalarT & tVcrit,
2257  ScalarT & tRS,
2258  ScalarT & tCOND,
2259  ScalarT & tIRF,
2260  ScalarT & tIKF,
2261  ScalarT & tBrkdwnV,
2262 
2263  // model variables/params:
2264  const ScalarT & TNOM,
2265  const ScalarT & VJ,
2266  const ScalarT & CJO,
2267  const ScalarT & M,
2268  const ScalarT & N,
2269  const ScalarT & IS,
2270  const ScalarT & EG,
2271  const ScalarT & XTI,
2272  const ScalarT & RS,
2273  const ScalarT & COND,
2274  const ScalarT & IRF,
2275  const ScalarT & NR,
2276  const ScalarT & IKF,
2277  const ScalarT & TIKF,
2278  const ScalarT & ISR,
2279  const ScalarT & IBV,
2280  const ScalarT & BV,
2281 
2282  const bool & BVGiven,
2283 
2284  const ScalarT & TBV1,
2285  const ScalarT & TBV2,
2286  const ScalarT & TRS1,
2287  const ScalarT & TRS2,
2288  const ScalarT & FC,
2289 
2290  const int level
2291 
2292  )
2293 {
2294  ScalarT KoverQ = static_cast<ScalarT>(CONSTKoverQ);
2295  ScalarT REFTEMP = static_cast<ScalarT>(CONSTREFTEMP);
2296  ScalarT Eg0 = static_cast<ScalarT>(CONSTEg0);
2297  ScalarT alphaEg = static_cast<ScalarT>(CONSTalphaEg);
2298  ScalarT betaEg = static_cast<ScalarT>(CONSTbetaEg);
2299  ScalarT boltz = static_cast<ScalarT>(CONSTboltz);
2300  ScalarT Q = static_cast<ScalarT>(CONSTQ);
2301  ScalarT Eg300 = static_cast<ScalarT>(CONSTEg300);
2302  ScalarT root2 = static_cast<ScalarT>(CONSTroot2);
2303  ScalarT e = static_cast<ScalarT>(CONSTe);
2304 
2305  ScalarT vtnom = KoverQ * TNOM;
2306 
2307  ScalarT xfc = log( 1.0 - FC );
2308 
2309  if( temp != -999.0 ) Temp = temp;
2310  //double TNOM = TNOM;
2311 
2312  ScalarT vt = KoverQ * Temp;
2313  ScalarT fact2 = Temp / REFTEMP;
2314  ScalarT egfet = Eg0 - (alphaEg*Temp*Temp)/(Temp+betaEg);
2315  ScalarT arg = -egfet/(2.0*boltz*Temp) +
2316  Eg300/(boltz*(REFTEMP+REFTEMP));
2317  ScalarT pbfact = -2.0*vt*(1.5*log(fact2)+Q*arg);
2318  ScalarT egfet1 = Eg0 - (alphaEg*TNOM*TNOM)/
2319  (TNOM+betaEg);
2320  ScalarT arg1 = -egfet1/(2.0*boltz*TNOM) +
2321  Eg300/(2.0*boltz*REFTEMP);
2322  ScalarT fact1 = TNOM/REFTEMP;
2323  ScalarT pbfact1 = -2.0*vtnom*(1.5*log(fact1)+Q*arg1);
2324 
2325  ScalarT pbo = (VJ-pbfact1)/fact1;
2326  ScalarT gmaold = (VJ-pbo)/pbo;
2327 
2328  tJctCap = CJO/
2329  (1.0+M*(4.0e-4*(TNOM-REFTEMP) -gmaold));
2330 
2331  tJctPot = pbfact+fact2*pbo;
2332 
2333  ScalarT gmanew = (tJctPot-pbo)/pbo;
2334 
2335  tJctCap *= 1.0 + M*(4.0e-4*(Temp-REFTEMP)-gmanew);
2336 
2337  tSatCur = IS*exp(((Temp/TNOM)-1.0)*
2338  EG/(N*vt)+
2339  XTI/N*log(Temp/TNOM));
2340 
2341  tF1 = tJctPot*(1.0-exp((1.0-M)*xfc))/(1.0-M);
2342 
2343  tDepCap = FC*tJctPot;
2344 
2345  ScalarT vte = N*vt;
2346  ScalarT tempBV;
2347 
2348  tVcrit = vte*log(vte/(root2*tSatCur));
2349  tRS = RS;
2350  tCOND = COND;
2351  tIRF = IRF*pow(fact2,1.6);
2352 
2353  //int level = getLevel();
2354  if(level == 2) // this section is PSPICE compatible
2355  {
2356  tSatCurR = ISR*exp((Temp/TNOM - 1.0)*
2357  EG/(NR*vt)+
2358  XTI/NR*log(Temp/TNOM));
2359 
2360  tIKF = IKF*(1 + TIKF*(Temp-TNOM));
2361 
2362  tempBV = BV*(1 + (Temp-TNOM)*
2363  ( TBV1 + TBV2*(Temp-TNOM) ));
2364 
2365  tRS = RS*(1 + (Temp-TNOM)*
2366  ( TRS1 + TRS2*(Temp-TNOM) ));
2367 
2368  tCOND = 0.0;
2369  if(tRS != 0.0) tCOND = 1.0/tRS;
2370 
2371  tJctPot = (VJ - egfet1)*fact2 - 3*vt*log(fact2) + egfet;
2372 
2373  tJctCap = CJO/(1.0 +
2374  M*(4.0e-4*(Temp-TNOM) + (1-tJctPot/VJ)));
2375  }
2376  else
2377  {
2378  tempBV=BV;
2379  }
2380 
2381 #ifdef Xyce_BREAKDOWN_ORIGINAL
2382  //Changed 9/18/07, K. R. Santarelli. This is the original (broken) version
2383  //of the breakdown voltage computation. It has two known issues:
2384  //
2385  //1. It assumes N (the emission coefficient) is always 1 (division by
2386  // vt instead of vte).
2387  //2. While the for loop terminates due to the fact that it's implemented via
2388  // a counter, the value of xbv does not converge in many cases, so the
2389  // value of xbv upon terminating is often garbage.
2390  //
2391  //For consistency with old, existing simulations, this version of the
2392  //breakdown voltage calculation (along with the appropriate code for
2393  //computing the breakdown current in the level 1 and level 2 models---see
2394  //the appropriate sections of
2395  //Instance::updateIntermediateVars) can be invoked by
2396  //specifiying the CPPFLAG "-DXyce_BREAKDOWN_ORIGINAL" when creating a Xyce
2397  //Build. The default, however, is the code which follows #else.
2398 
2399  double reltol = 1.0e-3;
2400  if( BVGiven )
2401  {
2402  double IRfactor = tIRF;
2403  double cbv = IBV;
2404  double xbv, xcbv;
2405  if( cbv < IRfactor*tSatCur*tempBV/vt )
2406  {
2407  cbv = IRfactor*tSatCur*tempBV/vt;
2408  xbv = tempBV;
2409  }
2410  else
2411  {
2412  double tol = reltol*cbv;
2413  xbv = tempBV-vt*log(1.0+cbv/(IRfactor*tSatCur));
2414  for( int i = 0; i < 25; ++i )
2415  {
2416  xbv = tempBV-vt*log(cbv/(IRfactor*tSatCur)+1.0-xbv/vt);
2417  xcbv = IRfactor*tSatCur*(exp((tempBV-xbv)/vt)-1.0+xbv/vt);
2418  if(fabs(xcbv-cbv)<=tol) break;
2419  }
2420  }
2421  tBrkdwnV = xbv;
2422  }
2423 #else
2424  if( BVGiven)
2425  {
2426  ScalarT IRFactor=tIRF;
2427  ScalarT cbv = IBV;
2428  ScalarT xbv;
2429  ScalarT cthreshlow; //lower threshold for IBV
2430  ScalarT cthreshhigh; //high threshold for IBV
2431  int iter_count;
2432  const int ITER_COUNT_MAX=8;
2433  ScalarT arg2;
2434 
2435  ScalarT arg1=3.0*vte/(e*tempBV);
2436  arg1=arg1*arg1*arg1;
2437  cthreshlow=tSatCur*IRFactor*(1-arg1);
2438  cthreshhigh=tSatCur*IRFactor*(1-1.0/(e*e*e)) *
2439  exp(-1.0*(3.0*vte-tempBV)/vte);
2440 
2441  if(cbv >= cthreshhigh)
2442  { //if IBV is too high, tBrkdwnV will go below 3NVt.
2443  tBrkdwnV=3.0*vte; //Clip tBrkdwnV to 3*N*Vt in this case (and hence
2444  } //clip IBV to cthreshhigh).
2445 
2446 
2447  else if(cbv <= cthreshlow)
2448  { //if IBV is too low, tBrkdwnV will go above
2449  tBrkdwnV=tempBV; //BV. Clip tBrkdwnV to BV in this case (and
2450  } //hence clip IBV to cthreshlow).
2451 
2452 
2453  //If IBV is in an acceptable range, perform a Picard iteration to find
2454  //tBrkdwnV, starting with an initial guess of tBrkdwnV=tempBV, and
2455  //running through the iteration ITER_COUNT_MAX times.
2456 
2457  else
2458  {
2459  xbv=tempBV;
2460  for(iter_count=0; iter_count < ITER_COUNT_MAX; iter_count++)
2461  {
2462  arg2=3.0*vte/(e*xbv);
2463  arg2=arg2*arg2*arg2;
2464  xbv=tempBV-vte*log(cbv/(tSatCur*IRFactor))+vte *
2465  log(1-arg2);
2466  }
2467  tBrkdwnV=xbv;
2468  }
2469 
2470 //Note that, not only is tBrkdwnV adjusted using this method, but the effective
2471 //value of Is (tSatCur) is adjusted as well. The code used to use Is before,
2472 //but now it will use Is*IRFactor*(1-(3*N*Vt/(e*tBrkdwnV))^3) instead. This is
2473 //reflected in changes made to Instance::updateIntermediateVars
2474 //(for "normal" level 1 and 2 diode models) for the reverse breakdown region.
2475 //Changes have not been made to the associated LambertW functions as of yet
2476 //since there seem to be other issues involved with those functions independent
2477 // of this change.
2478  }
2479 #endif
2480 
2481  return true;
2482 }
2483 
2484 
2485 //-----------------------------------------------------------------------------
2486 // Function : updateIntermediateVars
2487 // Purpose : update intermediate variables for one diode instance
2488 // Special Notes :
2489 // Scope : public
2490 // Creator :
2491 // Creation Date :
2492 //-----------------------------------------------------------------------------
2493 template <typename ScalarT>
2495 
2496  // inputs:
2497  const ScalarT & Vp,
2498  const ScalarT & Vpp,
2499  const ScalarT & Vn,
2500  const ScalarT & Vd,
2501 
2502  // instance params:
2503  const ScalarT & Temp,
2504  const ScalarT & tJctCap,
2505  const ScalarT & tJctPot,
2506  const ScalarT & tDepCap,
2507  const ScalarT & tF1,
2508  const ScalarT & tSatCur,
2509  const ScalarT & tSatCurR,
2510  const ScalarT & tVcrit,
2511  const ScalarT & tRS,
2512  const ScalarT & tCOND,
2513  const ScalarT & tIRF,
2514  const ScalarT & tIKF,
2515  const ScalarT & tBrkdwnV,
2516 
2517  // instance variables:
2518  const ScalarT & Area,
2519  const int & lambertWFlag,
2520  const double & gmin,
2521 
2522  // model params:
2523  const ScalarT M , // grading parameter
2524  const ScalarT BV , // breakdown voltage
2525  const ScalarT IBV , // reverse breakdown current
2526  const ScalarT NBV , // reverse breakdown ideality factor
2527  const ScalarT IBVL, // low-level reverse breakdown current
2528  const ScalarT NBVL, // low-level reverse breakdown ideality factor
2529  const ScalarT N , // non-ideality factor.
2530  const ScalarT NR , // emission coeff. for ISR.
2531  const ScalarT TT , // transit time.
2532  const ScalarT F2 , // capacitive polynomial factor
2533  const ScalarT F3 , // capacitive polynomial factor
2534 
2535  const int level,
2536 
2537  // outputs:
2538  ScalarT & Id,
2539  ScalarT & Gd,
2540  ScalarT & Qd,
2541  ScalarT & Cd,
2542  ScalarT & Gspr
2543 
2544  )
2545 {
2546  bool bsuccess = true;
2547 
2548 
2549 
2550  ScalarT Inorm; // normal diffusion current
2551  ScalarT Irec; // recombination current
2552  ScalarT Kgen; // generation factor
2553  ScalarT Khi; // high-injection factor
2554  ScalarT Gd1, DKgen;
2555  ScalarT Gd2, DKhi;
2556 
2557  ScalarT Vc;
2558 
2559  ScalarT Icd = 0.0;// NOT USED, must be deprecated.
2560 
2561  ScalarT Isat = tSatCur * Area;
2562  ScalarT IsatR = tSatCurR * Area;
2563  ScalarT KoverQ = static_cast<ScalarT>(CONSTKoverQ);
2564  ScalarT Vt = KoverQ * Temp;
2565  ScalarT Vte = N * Vt;
2566  ScalarT VteR = NR * Vt;
2567 
2568  Gspr = tCOND * Area;
2569 
2570  // Current and Conductivity
2571  //----------------------------------------------
2572 
2573  if(level == 1)
2574  {
2575 #if 0
2576  // Using LambertW function
2577  if (lambertWFlag == 1)
2578  {
2579  if (Vd >= -3.0 * Vte)
2580  {
2581  lambertWCurrent(Isat, Vte, tRS);
2582  }
2583  // linear reverse bias
2584  else if ( !tBrkdwnV || (Vd >= -tBrkdwnV) )
2585  {
2586  lambertWLinearReverseBias(Isat, Vte, tRS);
2587  }
2588  // reverse breakdown
2589  else
2590  {
2591  lambertWBreakdownCurrent(Isat, Vte, tRS);
2592  }
2593 
2594  ScalarT Vrs;
2595  Vrs = (Id + Icd)*tRS;
2596  Vc = Vd - Vrs;
2597  }
2598  else if (lambertWFlag == 2)
2599  {
2600  if (Vd >= -3.0 * Vte)
2601  {
2602  lambertWCurrent(Isat, Vte, 1.0e-15);
2603  }
2604  // linear reverse bias
2605  else if ( !tBrkdwnV || (Vd >= -tBrkdwnV) )
2606  {
2607  lambertWLinearReverseBias(Isat, Vte, 1.0e-15);
2608  }
2609  // reverse breakdown
2610  else
2611  {
2612  lambertWBreakdownCurrent(Isat, Vte, 1.0e-15);
2613  }
2614  Vc = Vd;
2615  }
2616 
2617  // Normal exponential
2618  else
2619 #endif
2620  {
2621  // Adjustment for linear portion of reverse current
2622  ScalarT IRfactor;
2623  //if(Vd >= 0) IRfactor = 1.0; //error? shouldn't this be Vd>=-3*Vte????
2624  if(Vd >= -3.0 * Vte) IRfactor = 1.0; //changing this to be consistent
2625  else IRfactor = tIRF; //with model in reference guide.
2626  Isat *= IRfactor;
2627 
2628  if (Vd >= -3.0 * Vte)
2629  {
2630  ScalarT arg1 = Vd / Vte;
2631  arg1 = Xycemin(static_cast<fadType>(CONSTMAX_EXP_ARG), arg1);
2632  ScalarT evd = exp(arg1);
2633 
2634  Id = Isat * (evd - 1.0) + gmin * Vd;
2635  Gd = Isat * evd / Vte + gmin;
2636  }
2637  // Linear reverse bias
2638  else if(!tBrkdwnV || (Vd >= -tBrkdwnV))
2639  {
2640  ScalarT arg = 3.0 * Vte / (Vd * CONSTe);
2641  arg = arg * arg * arg;
2642  Id = -Isat * (1.0 + arg) + gmin * Vd;
2643  Gd = Isat * 3.0 * arg / Vd + gmin;
2644  }
2645  // Reverse breakdown
2646  else
2647  {
2648  ScalarT arg1 = -(tBrkdwnV + Vd) / Vte;
2649  arg1 = Xycemin(static_cast<fadType>(CONSTMAX_EXP_ARG), arg1);
2650  ScalarT evrev = exp(arg1);
2651 
2652 #ifdef Xyce_BREAKDOWN_ORIGINAL
2653  Id = -Isat * evrev + gmin * Vd;
2654  Gd = Isat * evrev / Vte + gmin;
2655 #else
2656  //added by K. Santarelli 9/18/07 to account for change in tBrkdwnV
2657  //calculation.
2658  ScalarT arg2=3.0*Vte/(CONSTe*tBrkdwnV);
2659  arg2=arg2*arg2*arg2;
2660  ScalarT Isat_tBrkdwnV=Isat*(1-arg2);
2661  Id = -Isat_tBrkdwnV * evrev + gmin * Vd;
2662  Gd = Isat_tBrkdwnV * evrev / Vte + gmin;
2663 #endif
2664  }
2665  Vc = Vd;
2666  }
2667  }
2668  else if(level == 2)
2669  {
2670  // Adjustment for linear portion of reverse current
2671  ScalarT IRfactor;
2672  //if(Vd >= 0) IRfactor = 1.0; //error? Shouldn't this be Vd >= -3*Vte?
2673  if(Vd >= -3.0*Vte) IRfactor=1.0; //changing it to be consistent with model
2674  else IRfactor = tIRF; //in the reference manual.
2675  Isat *= IRfactor;
2676 
2677  if (Vd >= -3.0 * Vte)
2678  {
2679  ScalarT arg1 = Vd / Vte;
2680  arg1 = Xycemin(static_cast<fadType>(CONSTMAX_EXP_ARG), arg1);
2681  ScalarT evd = exp(arg1);
2682  Inorm = Isat * (evd - 1.0) + gmin * Vd;
2683  Gd1 = Isat*evd/Vte + gmin;
2684 
2685  arg1 = Vd / VteR;
2686  arg1 = Xycemin(static_cast<fadType>(CONSTMAX_EXP_ARG), arg1);
2687  evd = exp(arg1);
2688  Irec = IsatR * (evd - 1.0);
2689  Gd2 = IsatR*evd/VteR;
2690 
2691  Khi = 1;
2692  DKhi = 0;
2693  if(tIKF > 0)
2694  {
2695  Khi = sqrt(tIKF/(tIKF+Inorm));
2696  DKhi = 0.5*Khi*Gd1/(tIKF+Inorm);
2697  }
2698  Kgen = 0;
2699  DKgen = 0;
2700  if(Irec != 0)
2701  {
2702  Kgen = sqrt( pow(((1-Vd/tJctPot)*(1-Vd/tJctPot) + 0.005),M) );
2703  DKgen = -M*(1-Vd/tJctPot)/(tJctPot*Kgen);
2704  }
2705 
2706  Id = Inorm*Khi + Irec*Kgen;
2707  Gd = Gd1*Khi + Inorm*DKhi + Gd2*Kgen + Irec*DKgen;
2708  }
2709  // Linear reverse bias
2710  else if(!tBrkdwnV || (Vd >= -tBrkdwnV))
2711  {
2712  ScalarT arg = 3.0 * Vte / (Vd * CONSTe);
2713  arg = arg * arg * arg;
2714  Id = -Isat * (1.0 + arg) + gmin * Vd;
2715  Gd = Isat * 3.0 * arg / Vd + gmin;
2716  }
2717  // Reverse breakdown
2718  else
2719  {
2720  ScalarT arg1 = -(tBrkdwnV + Vd) / Vte;
2721  arg1 = Xycemin(static_cast<fadType>(CONSTMAX_EXP_ARG), arg1);
2722  ScalarT evrev = exp(arg1);
2723 
2724 #ifdef Xyce_BREAKDOWN_ORIGINAL
2725  Id = -Isat * evrev + gmin * Vd;
2726  Gd = Isat * evrev / Vte + gmin;
2727 #else
2728  //added 9/18/07 by K. Santarelli to account for change in tBrkdwnV
2729  //calculation.
2730  ScalarT arg2=3.0*Vte/(CONSTe*tBrkdwnV);
2731  arg2=arg2*arg2*arg2;
2732  ScalarT Isat_tBrkdwnV=Isat*(1-arg2);
2733  Id = -Isat_tBrkdwnV * evrev + gmin * Vd;
2734  Gd = Isat_tBrkdwnV * evrev / Vte + gmin;
2735 #endif
2736  }
2737  Vc = Vd;
2738  } // level
2739 
2740  // Only compute if Capacitance is non-zero
2741  //---------------------------------------
2742  if (tJctCap != 0.0)
2743  {
2744  // Charge Storage
2745  ScalarT Czero = tJctCap * Area;
2746  if (Vc < tDepCap)
2747  {
2748  //ScalarT arg = 1.0 - Vd/VJ;
2749  ScalarT arg = 1.0 - Vc / tJctPot;
2750  ScalarT arg1 = -M * log(arg);
2751  arg1 = Xycemin(static_cast<fadType>(CONSTMAX_EXP_ARG), arg1);
2752  ScalarT sarg = exp(arg1);
2753 
2754  //Qd = TT*Id + VJ*Czero*(1.0-arg*sarg)/(1.0-M);
2755  Qd = TT * Id + tJctPot * Czero * (1.0 - arg * sarg) / (1.0 - M);
2756  Cd = TT * Gd + Czero * sarg;
2757  }
2758  else
2759  {
2760  ScalarT Czof2 = Czero / F2;
2761  ScalarT MotJctPot = M / tJctPot;
2762 
2763  //Qd = TT*Id + Czero*tF1 + Czof2*(F3*(Vd-tDepCap)+(M/(VJ+VJ))
2764  Qd = TT * Id + Czero * tF1 +
2765  Czof2 * (F3 * (Vc - tDepCap) + (0.5 * MotJctPot) *
2766  (Vc * Vc - tDepCap * tDepCap));
2767  //Cd = TT*Gd + Czof2*(F3+M*Vd/VJ);
2768  Cd = TT * Gd + Czof2 * (F3 + MotJctPot * Vc);
2769  }
2770  }
2771  else
2772  {
2773  Qd = 0.0;
2774  Cd = 0.0;
2775  }
2776 
2777  return bsuccess;
2778 }
2779 
2780 //-----------------------------------------------------------------------------
2781 // Function : applyLimiters
2782 // Purpose :
2783 // Special Notes :
2784 // Scope : public
2785 // Creator :
2786 // Creation Date :
2787 //-----------------------------------------------------------------------------
2788 template <typename ScalarT>
2789 bool applyLimiters
2790 (
2791  N_DEV_DeviceSupport & devSupport,
2792 
2793  // inputs:
2794  const ScalarT & Vp,
2795  const ScalarT & Vpp,
2796  const ScalarT & Vn,
2797 
2798  // parameters:
2799  const ScalarT & tVcrit,
2800  const ScalarT & Vte,
2801  const ScalarT & BV,
2802 
2803  // output and book-keeping
2804  ScalarT & Vd,
2805  ScalarT & Vd_orig,
2806  ScalarT & Vd_old,
2807 
2808  const ScalarT & currVd_old, // (extData.currStoVectorRawPtr)[li_storevd];
2809  const ScalarT & nextVd_old, // (extData.nextStoVectorRawPtr)[li_storevd];
2810 
2811  const double InitCond,
2812  const bool InitCondGiven,
2813  const bool BVGiven,
2814  const int off, // instance variable
2815  int & newtonIterOld, // instance variable
2816  bool & origFlag, // instance variable
2817  const bool dotICapplies, // check all the "flagSol". if any == 1, then true
2818 
2819  // solver state variables:
2820  const int & newtonIter,
2821  const bool initJctFlag,
2822  const bool voltageLimiterFlag,
2823  const bool dcopFlag,
2824  const bool locaEnabledFlag
2825  )
2826 {
2827  // Junction Voltage
2828  Vd = Vpp - Vn;
2829  Vd_orig = Vd;
2830  origFlag = true;
2831 
2832  // Setup initial junction conditions if UIC enabled
2833  //------------------------------------------------
2834  if (newtonIter == 0)
2835  {
2836  newtonIterOld = 0;
2837  if (initJctFlag && voltageLimiterFlag && !dotICapplies)
2838  {
2839  if (InitCondGiven)
2840  {
2841  Vd = InitCond;
2842  origFlag = false;
2843  }
2844  else if (off)
2845  {
2846  Vd = 0.0;
2847  origFlag = false;
2848  }
2849  else
2850  {
2851  Vd=tVcrit;
2852  origFlag = false;
2853  }
2854  }
2855 
2856  // assume there is no history -- then check if the
2857  // state vector can overwrite this
2858  Vd_old = Vd;
2859 
2860  if (!(dcopFlag)|| (locaEnabledFlag && dcopFlag))
2861  {
2862  Vd_old = currVd_old;
2863  }
2864  }
2865  else // just do this whenever it isn't the first iteration:
2866  {
2867  Vd_old = nextVd_old;
2868  }
2869 
2870  // Voltage limiting based on mode of diode
2871  //---------------------------------------
2872  if (voltageLimiterFlag)
2873  {
2874  int ichk = 0;
2875 
2876  if (newtonIter >= 0)
2877  {
2878  //Test if breakdown voltage given or not
2879  if (BVGiven && (Vd < Xycemin(0.0, -BV + 10.0 * Vte)))
2880  {
2881  double Vdtmp = -( BV + Vd );
2882  Vdtmp = devSupport.pnjlim(Vdtmp, -(Vd_old+BV), Vte, tVcrit, &ichk);
2883  Vd = -(Vdtmp + BV);
2884  }
2885  else
2886  Vd = devSupport.pnjlim(Vd, Vd_old, Vte, tVcrit, &ichk);
2887 
2888  if (ichk) origFlag = false;
2889  }
2890  }
2891 
2892  // update the "old" newton iteration number.
2893  if (newtonIter != 0 && newtonIter != newtonIterOld)
2894  {
2895  newtonIterOld = newtonIter;
2896  }
2897 }
2898 
2899 //-----------------------------------------------------------------------------
2900 // Function : diodeSensitivity::operator
2901 // Purpose : produces df/dp and dq/dp, where p= any diode parameter.
2902 // Special Notes :
2903 // Scope : public
2904 // Creator : Eric Keiter, SNL
2905 // Creation Date : 7/18/2014
2906 //-----------------------------------------------------------------------------
2908  const ParameterBase &entity,
2909  const std::string &name,
2910  std::vector<double> & dfdp,
2911  std::vector<double> & dqdp,
2912  std::vector<double> & dbdp,
2913  std::vector<int> & Findices,
2914  std::vector<int> & Qindices,
2915  std::vector<int> & Bindices
2916  ) const
2917 {
2918  const ParameterBase * e1 = &entity;
2919  const Model & mod = *(dynamic_cast<const Model *> (e1));
2920 
2921  int sizeInstance = mod.instanceContainer.size();
2922 
2923  dfdp.resize(3*sizeInstance);
2924  dqdp.resize(3*sizeInstance);
2925  Findices.resize(3*sizeInstance);
2926  Qindices.resize(3*sizeInstance);
2927 
2928  // model params:
2929  fadType TNOM = mod.TNOM;
2930  fadType VJ = mod.VJ;
2931  fadType CJO = mod.CJO;
2932  fadType M = mod.M;
2933  fadType N = mod.N;
2934  fadType IS = mod.IS;
2935  fadType EG = mod.EG;
2936  fadType XTI = mod.XTI;
2937  fadType RS = mod.RS;
2938  fadType COND = mod.COND;
2939  fadType IRF = mod.IRF;
2940  fadType NR = mod.NR;
2941  fadType IKF = mod.IKF;
2942  fadType TIKF = mod.TIKF;
2943  fadType ISR = mod.ISR;
2944  fadType IBV = mod.IBV;
2945  fadType IBVL = mod.IBVL;
2946  fadType NBV = mod.NBV;
2947  fadType NBVL = mod.NBVL;
2948  fadType BV = mod.BV;
2949  fadType TT = mod.TT;
2950  fadType F2 = mod.F2;
2951  fadType F3 = mod.F3;
2952 
2953  fadType TBV1 = mod.TBV1;
2954  fadType TBV2 = mod.TBV2;
2955  fadType TRS1 = mod.TRS1;
2956  fadType TRS2 = mod.TRS2;
2957  fadType FC = mod.FC;
2958  fadType KF = mod.KF;
2959  fadType AF = mod.AF;
2960 
2961  std::string paramName = ExtendedString( name ).toUpper();
2962 
2963  if (paramName=="VJ") { VJ.diff(0,1); }
2964  else if (paramName=="CJO") { CJO.diff(0,1); }
2965  else if (paramName=="CJ") { CJO.diff(0,1); } // CJ is a synonym for CJO
2966  else if (paramName=="CJ0") { CJO.diff(0,1); } // CJ0 is a synonym for CJO
2967  else if (paramName=="M") { M.diff(0,1); }
2968  else if (paramName=="N") { N.diff(0,1); }
2969  else if (paramName=="IS") { IS.diff(0,1); }
2970  else if (paramName=="JS") { IS.diff(0,1); } // JS is a synonym for IS
2971  else if (paramName=="EG") { EG.diff(0,1); }
2972  else if (paramName=="XTI") { XTI.diff(0,1); }
2973  else if (paramName=="RS") { RS.diff(0,1); }
2974  else if (paramName=="COND") { COND.diff(0,1); }
2975  else if (paramName=="IRF") { IRF.diff(0,1); }
2976  else if (paramName=="NR") { NR.diff(0,1); }
2977  else if (paramName=="IKF") { IKF.diff(0,1); }
2978  else if (paramName=="TIKF") { TIKF.diff(0,1); }
2979  else if (paramName=="ISR") { ISR.diff(0,1); }
2980  else if (paramName=="IBV") { IBV.diff(0,1); }
2981  else if (paramName=="IBVL") { IBVL.diff(0,1); }
2982  else if (paramName=="NBV") { NBV.diff(0,1); }
2983  else if (paramName=="NBVL") { NBVL.diff(0,1); }
2984  else if (paramName=="BV") { BV.diff(0,1); }
2985  else if (paramName=="VB") { BV.diff(0,1); } // VB is a synonym for BV
2986  else if (paramName=="TT") { TT.diff(0,1); }
2987  else if (paramName=="FC") { FC.diff(0,1); }
2988  else if (paramName=="KF") { KF.diff(0,1); }
2989  else if (paramName=="AF") { AF.diff(0,1); }
2990  else if (paramName=="TNOM") { TNOM.diff(0,1); }
2991 
2992  else if (paramName=="TBV1") { TBV1.diff(0,1); }
2993  else if (paramName=="TBV2") { TBV2.diff(0,1); }
2994  else if (paramName=="TRS1") { TRS1.diff(0,1); }
2995  else if (paramName=="TRS2") { TRS2.diff(0,1); }
2996 
2997  processParams( M, EG, FC, RS, COND, F2, F3);
2998 
2999  int inst=0;
3000 
3001  for (std::vector<Instance *>::const_iterator in = mod.instanceContainer.begin();
3002  in != mod.instanceContainer.end(); ++in, ++inst)
3003  {
3004  double * solVec = (*in)->extData.nextSolVectorRawPtr;
3005 
3006  fadType Vpp = solVec[(*in)->li_Pri];
3007  fadType Vn = solVec[(*in)->li_Neg];
3008  fadType Vp = solVec[(*in)->li_Pos];
3009  fadType Vd = Vpp - Vn;
3010 
3011  // instance variables:
3012  fadType tJctCap = 0.0;
3013  fadType tJctPot = 0.0;
3014  fadType tDepCap = 0.0;
3015  fadType tF1 = 0.0;
3016  fadType tSatCur = 0.0;
3017  fadType tSatCurR = 0.0;
3018  fadType tVcrit = 0.0;
3019  fadType tRS = 0.0;
3020  fadType tCOND = 0.0;
3021  fadType tIRF = 0.0;
3022  fadType tIKF = 0.0;
3023  fadType tBrkdwnV = 0.0;
3024 
3025  // instance parameters:
3026  fadType Temp = (*in)->Temp;
3027  fadType Area = (*in)->Area;
3028 
3030  (*in)->Temp,
3031  Temp, tJctCap, tJctPot, tDepCap, tF1, tSatCur, tSatCurR,
3032  tVcrit, tRS, tCOND, tIRF, tIKF, tBrkdwnV,
3033  TNOM, VJ, CJO, M, N, IS, EG, XTI, RS, COND, IRF,
3034  NR, IKF, TIKF, ISR, IBV, BV,
3035  mod.BVGiven,
3036  TBV1, TBV2, TRS1, TRS2, FC,
3037  mod.getLevel()
3038  );
3039 
3040  fadType Id = 0.0;
3041  fadType Gd = 0.0;
3042  fadType Qd = 0.0;
3043  fadType Cd = 0.0;
3044  fadType Gspr = 0.0;
3045 
3047  // inputs:
3048  Vp, Vpp, Vn, Vd,
3049  // instance params:
3050  Temp, tJctCap, tJctPot, tDepCap, tF1,
3051  tSatCur, tSatCurR, tVcrit, tRS, tCOND,
3052  tIRF, tIKF, tBrkdwnV,
3053  // instance variables:
3054  Area, (*in)->lambertWFlag, (*in)->getDeviceOptions().gmin,
3055  // model params:
3056  M, BV, IBV, NBV, IBVL, NBVL, N, NR, TT, F2, F3,
3057  mod.getLevel(),
3058  // outputs:
3059  Id, Gd, Qd, Cd, Gspr
3060  );
3061 
3062  int iPos=0+inst*3;
3063  int iNeg=1+inst*3;
3064  int iPri=2+inst*3;
3065 
3066  fadType Ir = Gspr * (Vp - Vpp);
3067  dfdp[iPos] -= -Ir.dx(0);
3068  dfdp[iNeg] -= Id.dx(0);
3069  dfdp[iPri] -= (-Id.dx(0) + Ir.dx(0));
3070 
3071  dqdp[iPos] = 0.0;
3072  dqdp[iNeg] -= Qd.dx(0);
3073  dqdp[iPri] -= -Qd.dx(0);
3074 
3075  Findices[iPos] = (*in)->li_Pos;
3076  Findices[iNeg] = (*in)->li_Neg;
3077  Findices[iPri] = (*in)->li_Pri;
3078 
3079  Qindices[iPos] = (*in)->li_Pos;
3080  Qindices[iNeg] = (*in)->li_Neg;
3081  Qindices[iPri] = (*in)->li_Pri;
3082  }
3083 }
3084 
3085 } // namespace Diode
3086 } // namespace Device
3087 } // namespace Xyce