Xyce  6.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
N_DEV_DevicePDEInstance.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_DevicePDEInstance.C,v $
27 //
28 // Purpose : This file contains functions that are common to both
29 // the 1D and 2D PDE devices. Theoretically, they could
30 // be common to 3D devices as well, but those don't exist
31 // in this code.
32 //
33 // Special Notes :
34 //
35 // Creator : Eric R. Keiter, SNL, Parallel Computational Sciences
36 //
37 // Creation Date : 05/23/00
38 //
39 // Revision Information:
40 // ---------------------
41 //
42 // Revision Number: $Revision: 1.82 $
43 //
44 // Revision Date : $Date: 2014/02/24 23:49:18 $
45 //
46 // Current Owner : $Author: tvrusso $
47 //-------------------------------------------------------------------------
48 
49 #include <Xyce_config.h>
50 
51 #include <iostream>
52 
53 #include <N_DEV_DeviceInstance.h>
55 #include <N_DEV_DeviceMaster.h>
56 #include <N_DEV_DeviceOptions.h>
57 #include <N_DEV_SolverState.h>
58 #include <N_DEV_SourceData.h>
59 
60 #include <N_UTL_Expression.h>
61 
62 namespace Xyce {
63 namespace Device {
64 
65 // Mathematical functions and derivatives.
66 // Most of these were lifted from the SG Framework (sgnam.cpp).
67 
68 //-----------------------------------------------------------------------------
69 // Function : DevicePDEInstance::DevicePDEInstance
70 // Purpose : constructor.
71 // Special Notes :
72 // Scope : public
73 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
74 // Creation Date : 04/01/03
75 //-----------------------------------------------------------------------------
77  const InstanceBlock & IB,
78  ParametricData<void> & parametric_data,
79  const FactoryBlock & factory_block)
80  : DeviceInstance(IB, parametric_data, factory_block),
81  Temp(getDeviceOptions().temp.getImmutableValue<double>()),
82  charge(1.602176565e-19),
83  kb (1.3806488e-23), // boltzmann's constant
84  Vt(kb*Temp/charge),
85  Ut(Vt),
86  e0(8.854187817e-14),
87  eSi(11.8),
88  eSiO2(3.9),
89  eps(eSi*e0),
90  Ni(1.25e10),
91  h_planck (6.62606957e-34), // Planck's constant (in J-s)
92  e_mass (9.10938291e-31), // e- mass in kg.
93  // photogen section
94  photogenOnFlag(false),
95  xstart(0.0),
96  ystart(0.0),
97  xend (0.0),
98  yend (0.0),
99  intensity (0.0),
100  photoA1 (0.0),
101  photoTstart (0.0),
102  photoTstop (1.0e+100), // just something really big...
103  photoTd(0.0),
104  photoTr(0.0),
105  photoTf(0.0),
106  photoPw(0.0),
107  photoPer(0.0),
108  lastPeriodIndex(-1),
109  photoType(_DC_DATA),
110  Data_ptr(NULL),
111  DataSaved_ptr(NULL),
112  photoA1_old(0.0),
113  photoA1_final(0.0),
114  photoA1_orig(0.0),
115  photoA1_ramp(0.0),
116  photoA1_ramp_old(0.0),
117  photoA1_Delta(0.0),
118  photoA1_DeltaC(0.0),
119  maxPhotoDelta(1.0e+21),
120  photoContinuationFinished (false),
121  // end of photogen section
122 
123  maxVoltDelta(3.0*Vt), // 3 * thermal voltage.
124  enableContinuationCalled(false),
125  continuationAlpha (1.0),
126  mobModelName("carr"),
127  fieldDependentMobility(false),
128  fieldDependentMobilityGiven(false),
129  bulkMaterial("si"),
130  sensOn (false),
131  sensProcess (false),
132  meshSensMod (false),
133  dopingSensMod (false),
134  photogenSensMod (false),
135  variablesScaled(false),
136  x0_user(0.0),
137  C0_user(0.0),
138  t0_user(0.0),
139  outputName("")
140 {
141  setupOutputName ();
142 }
143 
144 //-----------------------------------------------------------------------------
145 // Function : DevicePDEInstance::aux1
146 // Purpose : This is the first of two "auxilliary" functions, neccessary
147 // for the Scharfetter-Gummel approximation.
148 //
149 // Special Notes : To avoid under and over-flows this function is defined
150 // by equivalent or approximate functions depending upon the
151 // value of the argument.
152 //
153 // x
154 // Aux1(x) = -------
155 // sinh(x)
156 // Scope : public
157 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
158 // Creation Date : 4/15/01
159 //-----------------------------------------------------------------------------
160 double DevicePDEInstance::aux1(double x)
161 {
162  if (x < -bernSupport.bp0_MISC) x = -bernSupport.bp0_MISC;
163  else if (x > bernSupport.bp0_MISC) x = bernSupport.bp0_MISC;
164 
165  if (x <= bernSupport.bp0_AUX1)
166  {
167  return(x / sinh(x));
168  }
169  else if (x <= bernSupport.bp1_AUX1)
170  {
171  return(1 - x*x/6.0*(1.0 - 7.0*x*x/60.0));
172  }
173  else
174  {
175  return(x / sinh(x));
176  }
177 }
178 
179 //-----------------------------------------------------------------------------
180 // Function : DevicePDEInstance::pd1aux1
181 // Purpose : This function returns the total derivative of the aux1
182 // function.
183 //
184 // Special Notes :
185 // To avoid under and overflows this function is defined by equivalent or
186 // approximate functions depending upon the value of the argument.
187 //
188 // d sinh(x) - x*cosh(x)
189 // --Aux1(x) = -------------------
190 // dx (sinh(x))^2
191 //
192 // Scope : public
193 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
194 // Creation Date : 4/22/01
195 //-----------------------------------------------------------------------------
197 {
198  double y;
199 
200 #if 0
201  if (x < -bernSupport.bp0_MISC) x = -bernSupport.bp0_MISC;
202  else if (x > bernSupport.bp0_MISC) x = bernSupport.bp0_MISC;
203 #else
204  if (x < -700.0) x = -700.0;
205  else if (x > 700.0) x = 700.0;
206 #endif
207 
208  if (x <= bernSupport.bp0_DAUX1)
209  {
210  y = sinh(x);
211  return((y - x*cosh(x))/(y*y));
212  }
213  else if (x <= bernSupport.bp1_DAUX1)
214  {
215  return(-x/3.0*(1.0 - 7.0*x*x/30.0));
216  }
217  else
218  {
219  y = sinh(x);
220  return((y - x*cosh(x))/(y*y));
221  }
222 
223 } // pd1aux1
224 
225 
226 //-----------------------------------------------------------------------------
227 // Function : DevicePDEInstance::aux2
228 // Purpose : This is the second of two "auxilliary" functions, neccesary
229 // for the Scharfetter-Gummel approximation.
230 //
231 // Special Notes : To avoid under and over-flows this function is defined
232 // by equivalent or approximate functions depending upon
233 // the value of the argument.
234 //
235 // 1
236 // Aux2(x) = -------
237 // 1 + e^x
238 // Scope : public
239 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
240 // Creation Date : 4/15/01
241 //-----------------------------------------------------------------------------
242 double DevicePDEInstance::aux2(double x)
243 {
244  if (x <= bernSupport.bp0_AUX2)
245  {
246  return(1.0);
247  }
248  else if (x <= bernSupport.bp1_AUX2)
249  {
250  return(1.0 / (1.0 + exp(x)));
251  }
252  else if (x <= bernSupport.bp2_AUX2)
253  {
254  return(exp(-x));
255  }
256  else
257  {
258  return(0.0);
259  }
260 }
261 
262 //-----------------------------------------------------------------------------
263 // Function : DevicePDEInstance::pd1aux2
264 // Purpose : This function returns the total derivative of the aux2
265 // function.
266 //
267 // Special Notes : To avoid under and overflows this function is defined
268 // by equivalent or approximate functions depending upon
269 // the value of the argument.
270 //
271 // d - e^x
272 // --Aux2(x) = -----------
273 // dx (1 + e^x)^2
274 //
275 // Scope : public
276 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
277 // Creation Date : 4/22/01
278 //-----------------------------------------------------------------------------
280 {
281  double y,z;
282 
283  if (x <= bernSupport.bp0_DAUX2)
284  {
285  return(0.0);
286  }
287  else if (x <= bernSupport.bp1_DAUX2)
288  {
289  return(-exp(x));
290  }
291  else if (x <= bernSupport.bp2_DAUX2)
292  {
293  y = exp(x); z = y + 1.0; return(-y/(z*z));
294  }
295  else if (x <= bernSupport.bp3_DAUX2)
296  {
297  return(-exp(-x));
298  }
299  else
300  {
301  return(0.0);
302  }
303 
304 } // pd1aux2
305 
306 //-----------------------------------------------------------------------------
307 // Function : DevicePDEInstance::Jn
308 // Purpose : Electron current density between two points.
309 // Special Notes : Version for passing mobility along edge.
310 // Scope : public
311 // Creator : Deborah Fixel, SNL, Parallel Computational Sciences
312 // Creation Date : 7/07/03
313 //-----------------------------------------------------------------------------
315 (double n1, double n2, double E, double u, double h)
316 {
317  double MU = u;
318  double dV = E*h/(2.0*Ut);
319  double n = n1*aux2(dV)+n2*aux2(-dV);
320  double dndx = aux1(-dV)*(n2-n1)/h;
321  double J = MU*((n*E)+(Ut*dndx));
322 
323 #ifdef Xyce_DEBUG_DEVICE
324  if (getDeviceOptions().debugLevel > 1 && getSolverState().debugTimeFlag)
325  {
326  Xyce::dout().width(24);
327  Xyce::dout().precision(16);
328  Xyce::dout().setf(std::ios::scientific);
329 
330  Xyce::dout() << std::endl;
331  Xyce::dout() << " MU = "<< MU << std::endl;
332  Xyce::dout() << " n = "<< n << " dndx = "<< dndx <<" E = "<< E << std::endl;
333  Xyce::dout() << " dV = "<< dV << " Ut = "<< Ut <<" Jn = "<< J << std::endl;
334  Xyce::dout() << " n1 = "<< n1 << " n2 = "<< n2 <<" (n2-n1) = "<<(n2-n1)<< std::endl;
335  Xyce::dout() << " h = "<< h << std::endl;
336  Xyce::dout() << " aux1(-dV) = " << aux1(-dV) << std::endl;
337  Xyce::dout() << " aux2( dV) = " << aux2( dV) << std::endl;
338  Xyce::dout() << " aux2(-dV) = " << aux2( dV) << std::endl;
339  Xyce::dout() << std::endl;
340  }
341 #endif
342 
343  return J;
344 }
345 
346 //-----------------------------------------------------------------------------
347 // Function : DevicePDEInstance::dJndV1
348 //
349 // Purpose : This function returns the derivative of electron current
350 // density between two points in space, with
351 // respect to the voltage at node 1.
352 //
353 // Special Notes : This version passes mobility along an edge.
354 // Scope : public
355 // Creator : Deborah Fixel, SNL, Parallel Computational Sciences
356 // Creation Date : 7/07/03
357 //-----------------------------------------------------------------------------
359 (double n1, double n2, double E, double u, double h)
360 {
361  double MU = u;
362  double dV = E*h/(2.0*Ut);
363  double n = n1*aux2(dV)+n2*aux2(-dV);
364  double dEdv1 = 1.0/h;
365  double ddVdv1 = 1.0/(2.0*Ut);
366  double dNdv1 = n1*( ddVdv1*pd1aux2( dV) ) + n2*(-ddVdv1*pd1aux2(-dV) );
367  double dDNDXdv1 = (n2-n1)/h * (-ddVdv1) * pd1aux1(-dV);
368  double dJdv1 = MU*(dNdv1*E + n*dEdv1 + Ut*dDNDXdv1);
369  return dJdv1;
370 }
371 
372 //-----------------------------------------------------------------------------
373 // Function : DevicePDEInstance::dJndV2
374 //
375 // Purpose : This function returns the derivative of electron current
376 // density between two points in space, with
377 // respect to the voltage at node 2.
378 //
379 // Special Notes : This version passes mobility along an edge.
380 // Scope : public
381 // Creator : Deborah Fixel, SNL, Parallel Computational Sciences
382 // Creation Date : 7/07/03
383 //-----------------------------------------------------------------------------
385 (double n1, double n2, double E, double u, double h)
386 {
387  double MU = u;
388  double dV = E*h/(2.0*Ut);
389  double n = n1*aux2(dV)+n2*aux2(-dV);
390  double dEdv2 = -1.0/h;
391  double ddVdv2 = -1.0/(2.0*Ut);
392  double dNdv2 = n1*( ddVdv2*pd1aux2( dV) ) + n2*(-ddVdv2*pd1aux2(-dV) );
393  double dDNDXdv2 = (n2-n1)/h * (-ddVdv2) * pd1aux1(-dV);
394  double dJdv2 = MU*(dNdv2*E + n*dEdv2 + Ut*dDNDXdv2);
395  return dJdv2;
396 }
397 
398 //-----------------------------------------------------------------------------
399 // Function : DevicePDEInstance::dJndn1
400 //
401 // Purpose : This function returns the derivative of electron current
402 // density between two points in space, with
403 // respect to the electron density at node 1.
404 //
405 // Special Notes : This version passes mobility along an edge.
406 // Scope : public
407 // Creator : Deborah Fixel, SNL, Parallel Computational Sciences
408 // Creation Date : 7/07/03
409 //-----------------------------------------------------------------------------
411 (double n1, double n2, double E, double u, double h)
412 {
413  double MU = u;
414  double dV = E*h/(2.0*Ut);
415  double dNdn1 = aux2( dV);
416  double dDNDXdn1 = -aux1(-dV)/h;
417  double dJdn1 = MU*(dNdn1*E + Ut*dDNDXdn1);
418  return dJdn1;
419 }
420 
421 //-----------------------------------------------------------------------------
422 // Function : DevicePDEInstance::dJndn2
423 //
424 // Purpose : This function returns the derivative of electron current
425 // density between two points in space, with
426 // respect to the electron density at node 1.
427 //
428 // Special Notes : This version passes mobility along an edge.
429 // Scope : public
430 // Creator : Deborah Fixel, SNL, Parallel Computational Sciences
431 // Creation Date : 7/07/03
432 //-----------------------------------------------------------------------------
434 (double n1, double n2, double E, double u, double h)
435 {
436  double MU = u;
437  double dV = E*h/(2.0*Ut);
438  double dNdn2 = aux2(-dV);
439  double dDNDXdn2 = aux1(-dV)/h;
440  double dJdn2 = MU*(dNdn2*E + Ut*dDNDXdn2);
441  return dJdn2;
442 }
443 
444 //-----------------------------------------------------------------------------
445 // Function : DevicePDEInstance::Jp
446 // Purpose : Hole current density between two points.
447 // Special Notes : This version passes mobility along an edge.
448 // Scope : public
449 // Creator : Deborah Fixel, SNL, Parallel Computational Sciences
450 // Creation Date : 7/07/03
451 //-----------------------------------------------------------------------------
453 (double p1, double p2, double E, double u, double h)
454 {
455  double MU = u;
456  double dV = E*h/(2.0*Ut);
457  double p = p1*aux2(-dV)+p2*aux2(dV);
458  double dpdx = aux1(-dV)*(p2-p1)/h;
459  double J = MU*(p*E-Ut*dpdx);
460 
461 #ifdef Xyce_DEBUG_DEVICE
462  if (getDeviceOptions().debugLevel > 1 && getSolverState().debugTimeFlag)
463  {
464  Xyce::dout().width(16);
465  Xyce::dout().precision(8);
466  Xyce::dout().setf(std::ios::scientific);
467 
468  Xyce::dout() << std::endl;
469  Xyce::dout() << " MU = "<< MU << std::endl;
470  Xyce::dout() << " p = "<< p << " dpdx = "<<dpdx<<" E = "<<E <<std::endl;
471  Xyce::dout() << " dV = "<< dV << " Ut = "<<Ut<<" Jpx = "<<J<<std::endl;
472  Xyce::dout() << " p1 = "<< p1 << " p2 = "<<p2<<" (p2-p1) = "<<(p2-p1)<<std::endl;
473  Xyce::dout() << " h = "<< h << std::endl;
474  Xyce::dout() << " aux1(-dV) = " << aux1(-dV) << std::endl;
475  Xyce::dout() << " aux2( dV) = " << aux2( dV) << std::endl;
476  Xyce::dout() << " aux2(-dV) = " << aux2( dV) << std::endl;
477  Xyce::dout() << std::endl;
478  }
479 #endif
480 
481  return J;
482 }
483 
484 //-----------------------------------------------------------------------------
485 // Function : DevicePDEInstance::dJpdV1
486 //
487 // Purpose : This function returns the derivative of hole current
488 // density between two points in space, with
489 // respect to the voltage at node 1.
490 //
491 // Special Notes : This function passes mobility along an edge.
492 // Scope : public
493 // Creator : Deborah Fixel, SNL, Parallel Computational Sciences
494 // Creation Date : 7/07/03
495 //-----------------------------------------------------------------------------
497 (double p1, double p2, double E, double u, double h)
498 {
499  double MU = u;
500  double dV = E*h/(2.0*Ut);
501  double p = p1*aux2(-dV)+p2*aux2(dV);
502  double dEdv1 = 1.0/h;
503  double ddVdv1 = 1.0/(2.0*Ut);
504  double dNdv1 = p1*(-ddVdv1*pd1aux2(-dV) ) + p2*(ddVdv1*pd1aux2(dV));
505  double dDNDXdv1 = (p2-p1)/h * (-ddVdv1) * pd1aux1(-dV);
506  double dJdv1 = MU*(dNdv1*E + p*dEdv1 - Ut*dDNDXdv1);
507  return dJdv1;
508 }
509 
510 
511 //-----------------------------------------------------------------------------
512 // Function : DevicePDEInstance::dJpdV2
513 //
514 // Purpose : This function returns the derivative of hole current
515 // density between two points in space, with
516 // respect to the voltage at node 2.
517 //
518 // Special Notes : This function passes mobility along an edge.
519 // Scope : public
520 // Creator : Deborah Fixel, SNL, Parallel Computational Sciences
521 // Creation Date : 7/07/03
522 //-----------------------------------------------------------------------------
524 (double p1, double p2, double E, double u, double h)
525 {
526  double MU = u;
527  double dV = E*h/(2.0*Ut);
528  double p = p1*aux2(-dV)+p2*aux2(dV);
529  double dEdv2 = -1.0/h;
530  double ddVdv2 = -1.0/(2.0*Ut);
531  double dNdv2 = p1*(-ddVdv2*pd1aux2(-dV) ) + p2*(ddVdv2*pd1aux2(dV));
532  double dDNDXdv2 = (p2-p1)/h * (-ddVdv2) * pd1aux1(-dV);
533  double dJdv2 = MU*(dNdv2*E + p*dEdv2 - Ut*dDNDXdv2);
534  return dJdv2;
535 }
536 
537 //-----------------------------------------------------------------------------
538 // Function : DevicePDEInstance::dJpdn1
539 //
540 // Purpose : This function returns the derivative of hole current
541 // density between two points in space, with
542 // respect to the hole density at node 1.
543 //
544 // Special Notes : Maybe I should have called this "dJpdp1"?
545 // : This version passes mobility along an edge.
546 // Scope : public
547 // Creator : Deborah Fixel, SNL, Parallel Computational Sciences
548 // Creation Date : 7/07/03
549 //-----------------------------------------------------------------------------
551 (double p1, double p2, double E, double u, double h)
552 {
553  double MU = u;
554  double dV = E*h/(2.0*Ut);
555  double dNdp1 = aux2(-dV);
556  double dDNDXdp1 = -aux1(-dV)/h;
557  double dJdn1 = MU*(dNdp1*E - Ut*dDNDXdp1);
558  return dJdn1;
559 }
560 
561 //-----------------------------------------------------------------------------
562 // Function : DevicePDEInstance::dJpdn2
563 //
564 // Purpose : This function returns the derivative of hole current
565 // density between two points in space, with
566 // respect to the hole density at node 2.
567 //
568 // Special Notes : This version passes mobility along an edge.
569 // Scope : public
570 // Creator : Deborah Fixel, SNL, Parallel Computational Sciences
571 // Creation Date : 7/07/03
572 //-----------------------------------------------------------------------------
574 (double p1, double p2, double E, double u, double h)
575 {
576  double MU = u;
577  double dV = E*h/(2.0*Ut);
578  double dNdp2 = aux2( dV);
579  double dDNDXdp2 = aux1(-dV)/h;
580  double dJdn2 = MU*(dNdp2*E - Ut*dDNDXdp2);
581  return dJdn2;
582 }
583 
584 // ERK Note: the "qdep" versions of these functions are intended to address
585 // charge-dependent (or current-dependent) mobilities, and also to be useful
586 // for defect carriers (i.e. carriers that have more than a single charge).
587 
588 //-----------------------------------------------------------------------------
589 // Function : DevicePDEInstance::J_qdep
590 // Purpose : Current density between two points.
591 // Special Notes :
592 // Scope : public
593 // Creator : Eric R. Keiter, SNL
594 // Creation Date : 11/20/10
595 //-----------------------------------------------------------------------------
597 (double n1, double n2, double E, double u, double h, int z)
598 {
599  double charge_number = static_cast<double>(z);
600  double MU = u;
601  double dV = -E*h/(2.0*Ut);
602  double n = charge_number*(n1*aux2(charge_number*dV)+n2*aux2(-charge_number*dV));
603  double dndx = aux1(-charge_number*dV)*(n2-n1)/h;
604  double J = MU*((n*E)-Ut*(dndx));
605 
606  return J;
607 }
608 
609 //-----------------------------------------------------------------------------
610 // Function : DevicePDEInstance::nMidpoint
611 // Purpose : Carrier density between two points.
612 // Special Notes :
613 // Scope : public
614 // Creator : Eric R. Keiter, SNL
615 // Creation Date : 10/30/12
616 //-----------------------------------------------------------------------------
618 (pdeFadType & n1, pdeFadType & n2, pdeFadType & E, double h, int z)
619 {
620  double charge_number = static_cast<double>(z);
621  pdeFadType dV = -E*h/(2.0*Ut);
622  pdeFadType arg1 = charge_number*dV;
623  pdeFadType arg2 = -charge_number*dV;
624  pdeFadType A1 = aux2(arg1);
625  pdeFadType A2 = aux2(arg2);
626  pdeFadType n = charge_number*(n1*A1+n2*A2);
627  return n;
628 }
629 
630 //-----------------------------------------------------------------------------
631 // Function : DevicePDEInstance::dJdV1_qdep
632 // Purpose : This function returns the derivative of current
633 // density between two points in space, with
634 // respect to the voltage at node 1.
635 // Special Notes :
636 // Scope : public
637 // Creator : Eric R. Keiter, SNL
638 // Creation Date : 11/20/10
639 //-----------------------------------------------------------------------------
641 (double n1, double n2, double E, double u, double h, int z)
642 {
643  double charge_number = static_cast<double>(z);
644  double MU = u;
645  double dV = -E*h/(2.0*Ut);
646  double n = charge_number*(n1*aux2(charge_number*dV)+n2*aux2(-charge_number*dV));
647  double dEdv1 = 1.0/h;
648  double ddVdv1 = -1.0/(2.0*Ut);
649  double dNdv1 = charge_number*(n1*charge_number*ddVdv1*pd1aux2(charge_number*dV)
650  -n2*charge_number*ddVdv1*pd1aux2(-charge_number*dV));
651  double dDNDXdv1 = (n2-n1)/h * (-charge_number*ddVdv1) * pd1aux1(-charge_number*dV);
652  double dJdv1 = MU*((dNdv1*E + n*dEdv1) - Ut*dDNDXdv1);
653 
654  return dJdv1;
655 }
656 
657 //-----------------------------------------------------------------------------
658 // Function : DevicePDEInstance::dJdV2_qdep
659 // Purpose : This function returns the derivative of current
660 // density between two points in space, with
661 // respect to the voltage at node 2.
662 // Special Notes :
663 // Scope : public
664 // Creator : Eric R. Keiter, SNL
665 // Creation Date : 11/20/10
666 //-----------------------------------------------------------------------------
668 (double n1, double n2, double E, double u, double h, int z)
669 {
670  double charge_number = static_cast<double>(z);
671  double MU = u;
672  double dV = -E*h/(2.0*Ut);
673  double n = charge_number*(n1*aux2(charge_number*dV)+n2*aux2(-charge_number*dV));
674  double dEdv2 = -1.0/h;
675  double ddVdv2 = 1.0/(2.0*Ut);
676  double dNdv2 = charge_number*(n1*charge_number*ddVdv2*pd1aux2(charge_number*dV)
677  -n2*charge_number*ddVdv2*pd1aux2(-charge_number*dV));
678  double dDNDXdv2 = (n2-n1)/h * (-charge_number*ddVdv2) * pd1aux1(-charge_number*dV);
679  double dJdv2 = MU*((dNdv2*E + n*dEdv2) - Ut*dDNDXdv2);
680 
681  return dJdv2;
682 }
683 
684 //-----------------------------------------------------------------------------
685 // Function : DevicePDEInstance::dJdV1_qdep
686 // Purpose : This function returns the derivative of current
687 // density between two points in space, with
688 // respect to the voltage at node 1.
689 // Special Notes : This version is used when mu is voltage-dependent.
690 // Scope : public
691 // Creator : Eric R. Keiter, SNL
692 // Creation Date : 10/27/12
693 //-----------------------------------------------------------------------------
695 (double n1, double n2, double E, const pdeFadType & u, double h, int z)
696 {
697  double dJdv1=dJdV1_qdep (n1, n2, E, u.val(), h, z);
698  double dudv1=u.dx(0);
699 
700  // add in chain rule from mobility derivative.
701  if (dudv1 != 0.0)
702  {
703  double charge_number = static_cast<double>(z);
704  double dV = -E*h/(2.0*Ut);
705  double n = charge_number*(n1*aux2(charge_number*dV)+n2*aux2(-charge_number*dV));
706  double dndx = aux1(-charge_number*dV)*(n2-n1)/h;
707  double dJ = dudv1*((n*E)-Ut*(dndx));
708  dJdv1 += dJ;
709  }
710 
711  return dJdv1;
712 }
713 
714 //-----------------------------------------------------------------------------
715 // Function : DevicePDEInstance::dJdV2_qdep
716 // Purpose : This function returns the derivative of current
717 // density between two points in space, with
718 // respect to the voltage at node 2.
719 // Special Notes : This version is used when mu is voltage-dependent.
720 // Scope : public
721 // Creator : Eric R. Keiter, SNL
722 // Creation Date : 10/27/12
723 //-----------------------------------------------------------------------------
725 (double n1, double n2, double E, const pdeFadType & u, double h, int z)
726 {
727  double dJdv2=dJdV2_qdep (n1, n2, E, u.val(), h, z);
728  double dudv2=u.dx(1);
729 
730  // add in chain rule from mobility derivative.
731  if (dudv2 != 0.0)
732  {
733  double charge_number = static_cast<double>(z);
734  double dV = -E*h/(2.0*Ut);
735  double n = charge_number*(n1*aux2(charge_number*dV)+n2*aux2(-charge_number*dV));
736  double dndx = aux1(-charge_number*dV)*(n2-n1)/h;
737  double dJ = dudv2*((n*E)-Ut*(dndx));
738  dJdv2 += dJ;
739  }
740 
741  return dJdv2;
742 }
743 
744 //-----------------------------------------------------------------------------
745 // Function : DevicePDEInstance::dJdn1_qdep
746 // Purpose : This function returns the derivative of current
747 // density between two points in space, with
748 // respect to the density at node 1.
749 // Special Notes :
750 // Scope : public
751 // Creator : Eric R. Keiter, SNL
752 // Creation Date : 11/20/10
753 //-----------------------------------------------------------------------------
755 (double n1, double n2, double E, double u, double h, int z)
756 {
757  double charge_number = static_cast<double>(z);
758  double MU = u;
759  double dV = -E*h/(2.0*Ut);
760  double dNdn1 = charge_number*aux2(charge_number*dV);
761  double dDNDXdn1 = -aux1(-charge_number*dV)/h;
762  double dJdn1 = MU*(dNdn1*E - Ut*dDNDXdn1);
763 
764  return dJdn1;
765 }
766 
767 //-----------------------------------------------------------------------------
768 // Function : DevicePDEInstance::dJdn2_qdep
769 // Purpose : This function returns the derivative of current
770 // density between two points in space, with
771 // respect to the electron density at node 1.
772 // Special Notes :
773 // Scope : public
774 // Creator : Eric R. Keiter, SNL
775 // Creation Date : 11/20/10
776 //-----------------------------------------------------------------------------
778 (double n1, double n2, double E, double u, double h, int z)
779 {
780  double charge_number = static_cast<double>(z);
781  double MU = u;
782  double dV = -E*h/(2.0*Ut);
783  double dNdn2 = charge_number*aux2(-charge_number*dV);
784  double dDNDXdn2 = aux1(-charge_number*dV)/h;
785  double dJdn2 = MU*(dNdn2*E - Ut*dDNDXdn2);
786 
787  return dJdn2;
788 }
789 
790 //
791 //-----------------------------------------------------------------------------
792 // Function : DevicePDEInstance::dJdn1_qdep
793 // Purpose : This function returns the derivative of current
794 // density between two points in space, with
795 // respect to the density at node 1.
796 //
797 // Special Notes : This version assumes that the mobility is dependent on n1
798 // as well.
799 //
800 // Scope : public
801 // Creator : Eric R. Keiter, SNL
802 // Creation Date : 10/29/12
803 //-----------------------------------------------------------------------------
805 (double n1, double n2, double E, const pdeFadType & u, double h, int z)
806 {
807  double dJdn1=dJdn1_qdep (n1, n2, E, u.val(), h, z);
808  double dudn1=0.0;
809  if (z < 0) dudn1=u.dx(2);
810  else dudn1=u.dx(4);
811 
812  if (dudn1 !=0.0)
813  {
814  double charge_number = static_cast<double>(z);
815  double dV = -E*h/(2.0*Ut);
816  double n = charge_number*(n1*aux2(charge_number*dV)+n2*aux2(-charge_number*dV));
817  double dndx = aux1(-charge_number*dV)*(n2-n1)/h;
818 
819  dJdn1 += dudn1*((n*E)-Ut*(dndx));
820  }
821 
822  return dJdn1;
823 }
824 
825 //-----------------------------------------------------------------------------
826 // Function : DevicePDEInstance::dJdn2_qdep
827 // Purpose : This function returns the derivative of current
828 // density between two points in space, with
829 // respect to the electron density at node 1.
830 //
831 // Special Notes : This version assumes that the mobility is dependent on n2
832 // as well.
833 //
834 // Scope : public
835 // Creator : Eric R. Keiter, SNL
836 // Creation Date : 10/29/12
837 //-----------------------------------------------------------------------------
839 (double n1, double n2, double E, const pdeFadType & u, double h, int z)
840 {
841  double dJdn2=dJdn2_qdep (n1, n2, E, u.val(), h, z);
842  double dudn2=0.0;
843  if (z < 0) dudn2=u.dx(3);
844  else dudn2=u.dx(5);
845 
846  if (dudn2 !=0.0)
847  {
848  double charge_number = static_cast<double>(z);
849  double dV = -E*h/(2.0*Ut);
850  double n = charge_number*(n1*aux2(charge_number*dV)+n2*aux2(-charge_number*dV));
851  double dndx = aux1(-charge_number*dV)*(n2-n1)/h;
852 
853  dJdn2 += dudn2*((n*E)-Ut*(dndx));
854  }
855 
856  return dJdn2;
857 }
858 //
859 //-----------------------------------------------------------------------------
860 // Function : DevicePDEInstance::dJdp1_qdep
861 // Purpose : This function returns the derivative of current
862 // density between two points in space, with
863 // respect to the density at node 1.
864 //
865 // Special Notes : This version assumes that the mobility is dependent on p1
866 // as well. In this context "p" is the density of the other
867 // carrier. The only dependence will be via a carrier-dependent
868 // mobility. This is a little confusing. If "J" in this
869 // function is a hole current, then "p" refers to
870 // electrons.
871 //
872 // Scope : public
873 // Creator : Eric R. Keiter, SNL
874 // Creation Date : 10/29/12
875 //-----------------------------------------------------------------------------
877 (double n1, double n2, double E, const pdeFadType & u, double h, int z)
878 {
879  double dJdp1=0.0;
880  double dudp1=0.0;
881  if (z < 0) dudp1=u.dx(4);
882  else dudp1=u.dx(2);
883 
884  if (dudp1 != 0.0)
885  {
886  double charge_number = static_cast<double>(z);
887  double dV = -E*h/(2.0*Ut);
888  double n = charge_number*(n1*aux2(charge_number*dV)+n2*aux2(-charge_number*dV));
889  double dndx = aux1(-charge_number*dV)*(n2-n1)/h;
890 
891  dJdp1 += dudp1*((n*E)-Ut*(dndx));
892  }
893 
894  return dJdp1;
895 }
896 
897 //-----------------------------------------------------------------------------
898 // Function : DevicePDEInstance::dJdp2_qdep
899 // Purpose : This function returns the derivative of current
900 // density between two points in space, with
901 // respect to the electron density at node 1.
902 //
903 // Special Notes : This version assumes that the mobility is dependent on p2
904 // as well. In this context "p" is the density of the other
905 // carrier. The only dependence will be via a carrier-dependent
906 // mobility. This is a little confusing. If "J" in this
907 // function is a hole current, then "p" refers to
908 // electrons.
909 //
910 // Scope : public
911 // Creator : Eric R. Keiter, SNL
912 // Creation Date : 10/29/12
913 //-----------------------------------------------------------------------------
915 (double n1, double n2, double E, const pdeFadType & u, double h, int z)
916 {
917  double dJdp2=0.0;
918  double dudp2=0.0;
919  if (z < 0) dudp2=u.dx(5);
920  else dudp2=u.dx(3);
921 
922  if (dudp2 != 0.0)
923  {
924  double charge_number = static_cast<double>(z);
925  double dV = -E*h/(2.0*Ut);
926  double n = charge_number*(n1*aux2(charge_number*dV)+n2*aux2(-charge_number*dV));
927  double dndx = aux1(-charge_number*dV)*(n2-n1)/h;
928  dJdp2 += dudp2*((n*E)-Ut*(dndx));
929  }
930 
931  return dJdp2;
932 }
933 
934 
935 
936 
937 //-----------------------------------------------------------------------------
938 // Function : DevicePDEInstance::dJdbm1_qdep
939 // Purpose : This function returns the derivative of current
940 // density between two points in space, with
941 // respect to the dopants at node 1.
942 // Special Notes : This version is used when mu is dopant-dependent and dopants are variable
943 // Scope : public
944 // Creator : Lawrence C Musson, SNL
945 // Creation Date : 10/24/13
946 //-----------------------------------------------------------------------------
948 (double n1, double n2, double E, const pdeFadType & u, double h, int z)
949 {
950  double dJdbm1 = 0.0;
951  double dudbm1=u.dx(6);
952 
953  // add in chain rule from mobility derivative.
954  if (dudbm1 != 0.0)
955  {
956  double charge_number = static_cast<double>(z);
957  double dV = -E*h/(2.0*Ut);
958  double n = charge_number*(n1*aux2(charge_number*dV)+n2*aux2(-charge_number*dV));
959  double dndx = aux1(-charge_number*dV)*(n2-n1)/h;
960  double dJ = dudbm1*((n*E)-Ut*(dndx));
961  dJdbm1 += dJ;
962  }
963 
964  return dJdbm1;
965 }
966 
967 
968 //-----------------------------------------------------------------------------
969 // Function : DevicePDEInstance::dJdbm2_qdep
970 // Purpose : This function returns the derivative of current
971 // density between two points in space, with
972 // respect to the dopants at node 1.
973 // Special Notes : This version is used when mu is dopant-dependent and dopants are variable
974 // Scope : public
975 // Creator : Lawrence C Musson, SNL
976 // Creation Date : 10/24/13
977 //-----------------------------------------------------------------------------
979 (double n1, double n2, double E, const pdeFadType & u, double h, int z)
980 {
981  double dJdbm2=0.0;
982  double dudbm2=u.dx(8);
983 
984  // add in chain rule from mobility derivative.
985  if (dudbm2 != 0.0)
986  {
987  double charge_number = static_cast<double>(z);
988  double dV = -E*h/(2.0*Ut);
989  double n = charge_number*(n1*aux2(charge_number*dV)+n2*aux2(-charge_number*dV));
990  double dndx = aux1(-charge_number*dV)*(n2-n1)/h;
991  double dJ = dudbm2*((n*E)-Ut*(dndx));
992  dJdbm2 += dJ;
993  }
994 
995  return dJdbm2;
996 }
997 
998 
999 //-----------------------------------------------------------------------------
1000 // Function : DevicePDEInstance::dJdpp1_qdep
1001 // Purpose : This function returns the derivative of current
1002 // density between two points in space, with
1003 // respect to the dopants at node 1.
1004 // Special Notes : This version is used when mu is dopant-dependent and dopants are variable
1005 // Scope : public
1006 // Creator : Lawrence C Musson, SNL
1007 // Creation Date : 10/24/13
1008 //-----------------------------------------------------------------------------
1010 (double n1, double n2, double E, const pdeFadType & u, double h, int z)
1011 {
1012  double dJdpp1=0.0;
1013  double dudpp1=u.dx(7);
1014 
1015  // add in chain rule from mobility derivative.
1016  if (dudpp1 != 0.0)
1017  {
1018  double charge_number = static_cast<double>(z);
1019  double dV = -E*h/(2.0*Ut);
1020  double n = charge_number*(n1*aux2(charge_number*dV)+n2*aux2(-charge_number*dV));
1021  double dndx = aux1(-charge_number*dV)*(n2-n1)/h;
1022  double dJ = dudpp1*((n*E)-Ut*(dndx));
1023  dJdpp1 += dJ;
1024  }
1025 
1026  return dJdpp1;
1027 }
1028 
1029 
1030 //-----------------------------------------------------------------------------
1031 // Function : DevicePDEInstance::dJdpp2_qdep
1032 // Purpose : This function returns the derivative of current
1033 // density between two points in space, with
1034 // respect to the dopants at node 1.
1035 // Special Notes : This version is used when mu is dopant-dependent and dopants are variable
1036 // Scope : public
1037 // Creator : Lawrence C Musson, SNL
1038 // Creation Date : 10/24/13
1039 //-----------------------------------------------------------------------------
1041 (double n1, double n2, double E, const pdeFadType & u, double h, int z)
1042 {
1043  double dJdpp2=0.0;
1044  double dudpp2=u.dx(9);
1045 
1046  // add in chain rule from mobility derivative.
1047  if (dudpp2 != 0.0)
1048  {
1049  double charge_number = static_cast<double>(z);
1050  double dV = -E*h/(2.0*Ut);
1051  double n = charge_number*(n1*aux2(charge_number*dV)+n2*aux2(-charge_number*dV));
1052  double dndx = aux1(-charge_number*dV)*(n2-n1)/h;
1053  double dJ = dudpp2*((n*E)-Ut*(dndx));
1054  dJdpp2 += dJ;
1055  }
1056 
1057  return dJdpp2;
1058 }
1059 
1060 
1061 
1062 
1063 
1064 //
1065 //
1066 #if 0
1067 // ----------------------------------------------------------------------------
1068 // Function : DevicePDEInstance::nsdep
1069 // Purpose : This function returns an approximate deposition profile
1070 // of a step implant driven in an inert environment.
1071 // Special Notes :
1072 // 1 W/2 + x W/2 - x
1073 // nsdep(x,W,Dt) = - (erf(---------) + erf(----------))
1074 // 2 2*sqrt(Dt) 2*sqrt(Dt)
1075 //
1076 //
1077 // Scope : public
1078 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1079 // Creation Date : 4/25/02
1080 // ----------------------------------------------------------------------------
1081 double DevicePDEInstance::nsdep (double x, double W, double Dt)
1082 {
1083  double D = 2.0 * sqrt(Dt);
1084  double Wh = W / 2.0;
1085  return 0.5 * (erf((Wh + x)/D) + erf((Wh - x)/D));
1086 }
1087 
1088 // ----------------------------------------------------------------------------
1089 // Function : DevicePDEInstance::ngdep
1090 //
1091 // Purpose : This function returns an approximate Gaussian deposition.
1092 //
1093 // Adapted from a similar function in SGF.
1094 //
1095 // Special Notes : This function is a little flakey, in that it assumes
1096 // that we're using a cylindrical geomtry
1097 // (x = radius, y = height.) It also assumes that the (0,0)
1098 // origin is in the upper-left-hand corner of the mesh.
1099 //
1100 // So, y=0.0 is the upper surface of the device, and the
1101 // implant is coming from the top (above y=0.0). Hence,
1102 // the (y>0) conditional.
1103 //
1104 // Also, the width parameter (W), is set up to be a
1105 // diameter about x=0, which is the reason for the 0.5*W -
1106 // half of this diameter will impact this radius.
1107 //
1108 // The implant is completely flat and constant in the
1109 // x-direction, as long as fabs(x) is less than W/2.0.
1110 // Beyond W/2.0, the gaussian profile kicks in.
1111 //
1112 // The parameters ax and ay are scaling parameters, and
1113 // correspond to how much you want the doping to vary with
1114 // space. A typical value for either can be set up as:
1115 //
1116 // Ax = ln (Nhi / Nlo)/(Rx*Rx)
1117 //
1118 // where:
1119 //
1120 // Nhi = the max. level of doping
1121 // Nlo = the min. level of doping
1122 // Rx = distance over which this doping should vary.
1123 //
1124 // Nhi/Nlo = 10^N, where N = # of orders of magnitude
1125 // that should vary between x=0 and x=Rx.
1126 //
1127 // Scope : public
1128 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1129 // Creation Date : 4/25/02
1130 // ----------------------------------------------------------------------------
1131 double DevicePDEInstance::ngdep
1132 (double x, double y, double W, double ax, double ay)
1133 {
1134  double xprime = fabs(x) - (0.5 * W);
1135  return ((xprime <= 0.0) ? 1.0 : exp(-ax*xprime*xprime))*
1136  ((y > 0.0) ? 0.0 : exp(-ay*y*y));
1137 }
1138 
1139 // ----------------------------------------------------------------------------
1140 // Function : DevicePDEInstance::ngdep2
1141 //
1142 // Purpose : This function returns an approximate Gaussian deposition.
1143 //
1144 // Special Notes : This function is a modification of the original ngdep
1145 // (see above), and I designed it to address some of the
1146 // peculiarities of ngdep.
1147 //
1148 // (1) I've gotten rid of the width, W. I'm just
1149 // going to assume that whoever is calling this function
1150 // can set that(the constant region) up on their own.
1151 //
1152 // (2) I've removed the conditionals cause things to
1153 // be set to zero, or one, or whatever, if you are on one
1154 // side or another of the suface. I'm assuming that
1155 // whoever calls this function can do that themselves, if
1156 // they need to.
1157 //
1158 // (3) I've removed the stuff that sets the retVal to zero
1159 // for y>0. Again, this is the user's problem.
1160 //
1161 // ax and ay mean the same as they did for the original
1162 // ngdep. (see above).
1163 //
1164 // It is possible to use this for the 1D case, pretty
1165 // easily. Set the xflag to false, hold y fixed at zero,
1166 // and have x correspond to the 1D mesh locations. (or, set
1167 // xflag to true, hold x fixed at zero, and let y
1168 // correspond to 1D mesh locations - either way).
1169 //
1170 // Scope : public
1171 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1172 // Creation Date : 03/28/03
1173 // ----------------------------------------------------------------------------
1174 double DevicePDEInstance::ngdep2
1175 (double x, double y, double ax, double ay)
1176 {
1177  double retVal = exp(-ax*x*x)* exp(-ay*y*y);
1178  return retVal;
1179 }
1180 #endif
1181 
1182 // ----------------------------------------------------------------------------
1183 // Function : DevicePDEInstance::erf
1184 // Purpose : This function returns the error function.
1185 // Special Notes :
1186 // Scope : public
1187 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1188 // Creation Date : 4/25/02
1189 // ----------------------------------------------------------------------------
1190 double DevicePDEInstance::erf(double x)
1191 {
1192  double t1 = 1.0 / (1.0 + 0.3275911 * fabs(x));
1193  double t2 = t1 * t1;
1194  double t3 = t2 * t1;
1195  double t4 = t3 * t1;
1196  double t5 = t4 * t1;
1197  double result = 1.0 - (0.254829592*t1 - 0.284496736*t2 + 1.421413741*t3 -
1198  1.453152027*t4 + 1.061405429*t5) * exp(-x*x);
1199  return (x < 0.0) ? -result : result;
1200 }
1201 
1202 // ----------------------------------------------------------------------------
1203 // Function : DevicePDEInstance::pd1erf
1204 // Purpose : This function returns the derivative of the error
1205 // function with respect to the first variable.
1206 // Special Notes :
1207 // Scope : public
1208 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1209 // Creation Date : 4/25/02
1210 // ----------------------------------------------------------------------------
1212 {
1213  double pi = M_PI;
1214  return 2.0 / sqrt(pi) * exp(-x*x);
1215 }
1216 
1217 // ----------------------------------------------------------------------------
1218 // Function : DevicePDEInstance::setupOutputName
1219 //
1220 // Purpose : This function takes the device instance name and creates
1221 // an appropriate "outputName" to be used for file outputs.
1222 //
1223 // At this point PDE devices are all specified as "Y" devices,
1224 // meaning that the device instance name will almost always
1225 // start with "Y%PDE%". Left unchanged, this has been
1226 // resulting in (for example) tecplot files named thing like,
1227 // "Y%PDE%DIODE1.dat". Obviously, the Y&PDE& prefix is
1228 // not needed, so this function removes it, if it exists.
1229 //
1230 // Special Notes :
1231 // Scope : public
1232 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1233 // Creation Date : 04/03/05
1234 // ----------------------------------------------------------------------------
1236 {
1237 
1238  std::string pdeString("Y%PDE%");
1239  std::string neutString("Y%NEUTRON%");
1240  std::string::size_type pos1 = getName().find(pdeString);
1241  std::string::size_type pos2 = getName().find(neutString);
1242 
1243  if (pos1 != std::string::npos)
1244  {
1245  std::string tmp1 = "";
1246  if (pos1 > 0) tmp1 = getName().substr(0,pos1);
1247  std::string tmp2 = getName().substr(pos1+6, getName().length()-1);
1248  outputName = tmp1 + tmp2;
1249  }
1250  else if (pos2 != std::string::npos)
1251  {
1252  std::string tmp1 = "";
1253  if (pos2 > 0) tmp1 = getName().substr(0,pos2);
1254  std::string tmp2 = getName().substr(pos2+10, getName().length()-1);
1255  outputName = tmp1 + tmp2;
1256  }
1257  else
1258  {
1259  outputName = getName();
1260  }
1261 
1262  // Tecplot doesn't like file names with the character, ":", so change all
1263  // colons to underscores. I personally don't like "%" characters in
1264  // filenames, so remove those as well.
1265  for (int i=0;i<outputName.size();++i)
1266  {
1267  if (outputName[i]==':') outputName[i] = '_';
1268  if (outputName[i]=='%') outputName[i] = '_';
1269  }
1270 
1271 #ifdef Xyce_DEBUG_DEVICE
1273  {
1274  Xyce::dout() << "outputName = "<< outputName << std::endl;
1275  }
1276 #endif
1277 
1278 }
1279 
1280 } // namespace Device
1281 } // namespace Xyce