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.83 $
43 //
44 // Revision Date : $Date: 2014/05/13 14:50:41 $
45 //
46 // Current Owner : $Author: dgbaur $
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(setupOutputName(getName()))
140  {}
141 
142 //-----------------------------------------------------------------------------
143 // Function : DevicePDEInstance::aux1
144 // Purpose : This is the first of two "auxilliary" functions, neccessary
145 // for the Scharfetter-Gummel approximation.
146 //
147 // Special Notes : To avoid under and over-flows this function is defined
148 // by equivalent or approximate functions depending upon the
149 // value of the argument.
150 //
151 // x
152 // Aux1(x) = -------
153 // sinh(x)
154 // Scope : public
155 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
156 // Creation Date : 4/15/01
157 //-----------------------------------------------------------------------------
158 double DevicePDEInstance::aux1(double x)
159 {
160  if (x < -bernSupport.bp0_MISC) x = -bernSupport.bp0_MISC;
161  else if (x > bernSupport.bp0_MISC) x = bernSupport.bp0_MISC;
162 
163  if (x <= bernSupport.bp0_AUX1)
164  {
165  return(x / sinh(x));
166  }
167  else if (x <= bernSupport.bp1_AUX1)
168  {
169  return(1 - x*x/6.0*(1.0 - 7.0*x*x/60.0));
170  }
171  else
172  {
173  return(x / sinh(x));
174  }
175 }
176 
177 //-----------------------------------------------------------------------------
178 // Function : DevicePDEInstance::pd1aux1
179 // Purpose : This function returns the total derivative of the aux1
180 // function.
181 //
182 // Special Notes :
183 // To avoid under and overflows this function is defined by equivalent or
184 // approximate functions depending upon the value of the argument.
185 //
186 // d sinh(x) - x*cosh(x)
187 // --Aux1(x) = -------------------
188 // dx (sinh(x))^2
189 //
190 // Scope : public
191 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
192 // Creation Date : 4/22/01
193 //-----------------------------------------------------------------------------
195 {
196  double y;
197 
198 #if 0
199  if (x < -bernSupport.bp0_MISC) x = -bernSupport.bp0_MISC;
200  else if (x > bernSupport.bp0_MISC) x = bernSupport.bp0_MISC;
201 #else
202  if (x < -700.0) x = -700.0;
203  else if (x > 700.0) x = 700.0;
204 #endif
205 
206  if (x <= bernSupport.bp0_DAUX1)
207  {
208  y = sinh(x);
209  return((y - x*cosh(x))/(y*y));
210  }
211  else if (x <= bernSupport.bp1_DAUX1)
212  {
213  return(-x/3.0*(1.0 - 7.0*x*x/30.0));
214  }
215  else
216  {
217  y = sinh(x);
218  return((y - x*cosh(x))/(y*y));
219  }
220 
221 } // pd1aux1
222 
223 
224 //-----------------------------------------------------------------------------
225 // Function : DevicePDEInstance::aux2
226 // Purpose : This is the second of two "auxilliary" functions, neccesary
227 // for the Scharfetter-Gummel approximation.
228 //
229 // Special Notes : To avoid under and over-flows this function is defined
230 // by equivalent or approximate functions depending upon
231 // the value of the argument.
232 //
233 // 1
234 // Aux2(x) = -------
235 // 1 + e^x
236 // Scope : public
237 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
238 // Creation Date : 4/15/01
239 //-----------------------------------------------------------------------------
240 double DevicePDEInstance::aux2(double x)
241 {
242  if (x <= bernSupport.bp0_AUX2)
243  {
244  return(1.0);
245  }
246  else if (x <= bernSupport.bp1_AUX2)
247  {
248  return(1.0 / (1.0 + exp(x)));
249  }
250  else if (x <= bernSupport.bp2_AUX2)
251  {
252  return(exp(-x));
253  }
254  else
255  {
256  return(0.0);
257  }
258 }
259 
260 //-----------------------------------------------------------------------------
261 // Function : DevicePDEInstance::pd1aux2
262 // Purpose : This function returns the total derivative of the aux2
263 // function.
264 //
265 // Special Notes : To avoid under and overflows this function is defined
266 // by equivalent or approximate functions depending upon
267 // the value of the argument.
268 //
269 // d - e^x
270 // --Aux2(x) = -----------
271 // dx (1 + e^x)^2
272 //
273 // Scope : public
274 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
275 // Creation Date : 4/22/01
276 //-----------------------------------------------------------------------------
278 {
279  double y,z;
280 
281  if (x <= bernSupport.bp0_DAUX2)
282  {
283  return(0.0);
284  }
285  else if (x <= bernSupport.bp1_DAUX2)
286  {
287  return(-exp(x));
288  }
289  else if (x <= bernSupport.bp2_DAUX2)
290  {
291  y = exp(x); z = y + 1.0; return(-y/(z*z));
292  }
293  else if (x <= bernSupport.bp3_DAUX2)
294  {
295  return(-exp(-x));
296  }
297  else
298  {
299  return(0.0);
300  }
301 
302 } // pd1aux2
303 
304 //-----------------------------------------------------------------------------
305 // Function : DevicePDEInstance::Jn
306 // Purpose : Electron current density between two points.
307 // Special Notes : Version for passing mobility along edge.
308 // Scope : public
309 // Creator : Deborah Fixel, SNL, Parallel Computational Sciences
310 // Creation Date : 7/07/03
311 //-----------------------------------------------------------------------------
313 (double n1, double n2, double E, double u, double h)
314 {
315  double MU = u;
316  double dV = E*h/(2.0*Ut);
317  double n = n1*aux2(dV)+n2*aux2(-dV);
318  double dndx = aux1(-dV)*(n2-n1)/h;
319  double J = MU*((n*E)+(Ut*dndx));
320 
321 #ifdef Xyce_DEBUG_DEVICE
322  if (getDeviceOptions().debugLevel > 1 && getSolverState().debugTimeFlag)
323  {
324  Xyce::dout().width(24);
325  Xyce::dout().precision(16);
326  Xyce::dout().setf(std::ios::scientific);
327 
328  Xyce::dout() << std::endl;
329  Xyce::dout() << " MU = "<< MU << std::endl;
330  Xyce::dout() << " n = "<< n << " dndx = "<< dndx <<" E = "<< E << std::endl;
331  Xyce::dout() << " dV = "<< dV << " Ut = "<< Ut <<" Jn = "<< J << std::endl;
332  Xyce::dout() << " n1 = "<< n1 << " n2 = "<< n2 <<" (n2-n1) = "<<(n2-n1)<< std::endl;
333  Xyce::dout() << " h = "<< h << std::endl;
334  Xyce::dout() << " aux1(-dV) = " << aux1(-dV) << std::endl;
335  Xyce::dout() << " aux2( dV) = " << aux2( dV) << std::endl;
336  Xyce::dout() << " aux2(-dV) = " << aux2( dV) << std::endl;
337  Xyce::dout() << std::endl;
338  }
339 #endif
340 
341  return J;
342 }
343 
344 //-----------------------------------------------------------------------------
345 // Function : DevicePDEInstance::dJndV1
346 //
347 // Purpose : This function returns the derivative of electron current
348 // density between two points in space, with
349 // respect to the voltage at node 1.
350 //
351 // Special Notes : This version passes mobility along an edge.
352 // Scope : public
353 // Creator : Deborah Fixel, SNL, Parallel Computational Sciences
354 // Creation Date : 7/07/03
355 //-----------------------------------------------------------------------------
357 (double n1, double n2, double E, double u, double h)
358 {
359  double MU = u;
360  double dV = E*h/(2.0*Ut);
361  double n = n1*aux2(dV)+n2*aux2(-dV);
362  double dEdv1 = 1.0/h;
363  double ddVdv1 = 1.0/(2.0*Ut);
364  double dNdv1 = n1*( ddVdv1*pd1aux2( dV) ) + n2*(-ddVdv1*pd1aux2(-dV) );
365  double dDNDXdv1 = (n2-n1)/h * (-ddVdv1) * pd1aux1(-dV);
366  double dJdv1 = MU*(dNdv1*E + n*dEdv1 + Ut*dDNDXdv1);
367  return dJdv1;
368 }
369 
370 //-----------------------------------------------------------------------------
371 // Function : DevicePDEInstance::dJndV2
372 //
373 // Purpose : This function returns the derivative of electron current
374 // density between two points in space, with
375 // respect to the voltage at node 2.
376 //
377 // Special Notes : This version passes mobility along an edge.
378 // Scope : public
379 // Creator : Deborah Fixel, SNL, Parallel Computational Sciences
380 // Creation Date : 7/07/03
381 //-----------------------------------------------------------------------------
383 (double n1, double n2, double E, double u, double h)
384 {
385  double MU = u;
386  double dV = E*h/(2.0*Ut);
387  double n = n1*aux2(dV)+n2*aux2(-dV);
388  double dEdv2 = -1.0/h;
389  double ddVdv2 = -1.0/(2.0*Ut);
390  double dNdv2 = n1*( ddVdv2*pd1aux2( dV) ) + n2*(-ddVdv2*pd1aux2(-dV) );
391  double dDNDXdv2 = (n2-n1)/h * (-ddVdv2) * pd1aux1(-dV);
392  double dJdv2 = MU*(dNdv2*E + n*dEdv2 + Ut*dDNDXdv2);
393  return dJdv2;
394 }
395 
396 //-----------------------------------------------------------------------------
397 // Function : DevicePDEInstance::dJndn1
398 //
399 // Purpose : This function returns the derivative of electron current
400 // density between two points in space, with
401 // respect to the electron density at node 1.
402 //
403 // Special Notes : This version passes mobility along an edge.
404 // Scope : public
405 // Creator : Deborah Fixel, SNL, Parallel Computational Sciences
406 // Creation Date : 7/07/03
407 //-----------------------------------------------------------------------------
409 (double n1, double n2, double E, double u, double h)
410 {
411  double MU = u;
412  double dV = E*h/(2.0*Ut);
413  double dNdn1 = aux2( dV);
414  double dDNDXdn1 = -aux1(-dV)/h;
415  double dJdn1 = MU*(dNdn1*E + Ut*dDNDXdn1);
416  return dJdn1;
417 }
418 
419 //-----------------------------------------------------------------------------
420 // Function : DevicePDEInstance::dJndn2
421 //
422 // Purpose : This function returns the derivative of electron current
423 // density between two points in space, with
424 // respect to the electron density at node 1.
425 //
426 // Special Notes : This version passes mobility along an edge.
427 // Scope : public
428 // Creator : Deborah Fixel, SNL, Parallel Computational Sciences
429 // Creation Date : 7/07/03
430 //-----------------------------------------------------------------------------
432 (double n1, double n2, double E, double u, double h)
433 {
434  double MU = u;
435  double dV = E*h/(2.0*Ut);
436  double dNdn2 = aux2(-dV);
437  double dDNDXdn2 = aux1(-dV)/h;
438  double dJdn2 = MU*(dNdn2*E + Ut*dDNDXdn2);
439  return dJdn2;
440 }
441 
442 //-----------------------------------------------------------------------------
443 // Function : DevicePDEInstance::Jp
444 // Purpose : Hole current density between two points.
445 // Special Notes : This version passes mobility along an edge.
446 // Scope : public
447 // Creator : Deborah Fixel, SNL, Parallel Computational Sciences
448 // Creation Date : 7/07/03
449 //-----------------------------------------------------------------------------
451 (double p1, double p2, double E, double u, double h)
452 {
453  double MU = u;
454  double dV = E*h/(2.0*Ut);
455  double p = p1*aux2(-dV)+p2*aux2(dV);
456  double dpdx = aux1(-dV)*(p2-p1)/h;
457  double J = MU*(p*E-Ut*dpdx);
458 
459 #ifdef Xyce_DEBUG_DEVICE
460  if (getDeviceOptions().debugLevel > 1 && getSolverState().debugTimeFlag)
461  {
462  Xyce::dout().width(16);
463  Xyce::dout().precision(8);
464  Xyce::dout().setf(std::ios::scientific);
465 
466  Xyce::dout() << std::endl;
467  Xyce::dout() << " MU = "<< MU << std::endl;
468  Xyce::dout() << " p = "<< p << " dpdx = "<<dpdx<<" E = "<<E <<std::endl;
469  Xyce::dout() << " dV = "<< dV << " Ut = "<<Ut<<" Jpx = "<<J<<std::endl;
470  Xyce::dout() << " p1 = "<< p1 << " p2 = "<<p2<<" (p2-p1) = "<<(p2-p1)<<std::endl;
471  Xyce::dout() << " h = "<< h << std::endl;
472  Xyce::dout() << " aux1(-dV) = " << aux1(-dV) << std::endl;
473  Xyce::dout() << " aux2( dV) = " << aux2( dV) << std::endl;
474  Xyce::dout() << " aux2(-dV) = " << aux2( dV) << std::endl;
475  Xyce::dout() << std::endl;
476  }
477 #endif
478 
479  return J;
480 }
481 
482 //-----------------------------------------------------------------------------
483 // Function : DevicePDEInstance::dJpdV1
484 //
485 // Purpose : This function returns the derivative of hole current
486 // density between two points in space, with
487 // respect to the voltage at node 1.
488 //
489 // Special Notes : This function passes mobility along an edge.
490 // Scope : public
491 // Creator : Deborah Fixel, SNL, Parallel Computational Sciences
492 // Creation Date : 7/07/03
493 //-----------------------------------------------------------------------------
495 (double p1, double p2, double E, double u, double h)
496 {
497  double MU = u;
498  double dV = E*h/(2.0*Ut);
499  double p = p1*aux2(-dV)+p2*aux2(dV);
500  double dEdv1 = 1.0/h;
501  double ddVdv1 = 1.0/(2.0*Ut);
502  double dNdv1 = p1*(-ddVdv1*pd1aux2(-dV) ) + p2*(ddVdv1*pd1aux2(dV));
503  double dDNDXdv1 = (p2-p1)/h * (-ddVdv1) * pd1aux1(-dV);
504  double dJdv1 = MU*(dNdv1*E + p*dEdv1 - Ut*dDNDXdv1);
505  return dJdv1;
506 }
507 
508 
509 //-----------------------------------------------------------------------------
510 // Function : DevicePDEInstance::dJpdV2
511 //
512 // Purpose : This function returns the derivative of hole current
513 // density between two points in space, with
514 // respect to the voltage at node 2.
515 //
516 // Special Notes : This function passes mobility along an edge.
517 // Scope : public
518 // Creator : Deborah Fixel, SNL, Parallel Computational Sciences
519 // Creation Date : 7/07/03
520 //-----------------------------------------------------------------------------
522 (double p1, double p2, double E, double u, double h)
523 {
524  double MU = u;
525  double dV = E*h/(2.0*Ut);
526  double p = p1*aux2(-dV)+p2*aux2(dV);
527  double dEdv2 = -1.0/h;
528  double ddVdv2 = -1.0/(2.0*Ut);
529  double dNdv2 = p1*(-ddVdv2*pd1aux2(-dV) ) + p2*(ddVdv2*pd1aux2(dV));
530  double dDNDXdv2 = (p2-p1)/h * (-ddVdv2) * pd1aux1(-dV);
531  double dJdv2 = MU*(dNdv2*E + p*dEdv2 - Ut*dDNDXdv2);
532  return dJdv2;
533 }
534 
535 //-----------------------------------------------------------------------------
536 // Function : DevicePDEInstance::dJpdn1
537 //
538 // Purpose : This function returns the derivative of hole current
539 // density between two points in space, with
540 // respect to the hole density at node 1.
541 //
542 // Special Notes : Maybe I should have called this "dJpdp1"?
543 // : This version passes mobility along an edge.
544 // Scope : public
545 // Creator : Deborah Fixel, SNL, Parallel Computational Sciences
546 // Creation Date : 7/07/03
547 //-----------------------------------------------------------------------------
549 (double p1, double p2, double E, double u, double h)
550 {
551  double MU = u;
552  double dV = E*h/(2.0*Ut);
553  double dNdp1 = aux2(-dV);
554  double dDNDXdp1 = -aux1(-dV)/h;
555  double dJdn1 = MU*(dNdp1*E - Ut*dDNDXdp1);
556  return dJdn1;
557 }
558 
559 //-----------------------------------------------------------------------------
560 // Function : DevicePDEInstance::dJpdn2
561 //
562 // Purpose : This function returns the derivative of hole current
563 // density between two points in space, with
564 // respect to the hole density at node 2.
565 //
566 // Special Notes : This version passes mobility along an edge.
567 // Scope : public
568 // Creator : Deborah Fixel, SNL, Parallel Computational Sciences
569 // Creation Date : 7/07/03
570 //-----------------------------------------------------------------------------
572 (double p1, double p2, double E, double u, double h)
573 {
574  double MU = u;
575  double dV = E*h/(2.0*Ut);
576  double dNdp2 = aux2( dV);
577  double dDNDXdp2 = aux1(-dV)/h;
578  double dJdn2 = MU*(dNdp2*E - Ut*dDNDXdp2);
579  return dJdn2;
580 }
581 
582 // ERK Note: the "qdep" versions of these functions are intended to address
583 // charge-dependent (or current-dependent) mobilities, and also to be useful
584 // for defect carriers (i.e. carriers that have more than a single charge).
585 
586 //-----------------------------------------------------------------------------
587 // Function : DevicePDEInstance::J_qdep
588 // Purpose : Current density between two points.
589 // Special Notes :
590 // Scope : public
591 // Creator : Eric R. Keiter, SNL
592 // Creation Date : 11/20/10
593 //-----------------------------------------------------------------------------
595 (double n1, double n2, double E, double u, double h, int z)
596 {
597  double charge_number = static_cast<double>(z);
598  double MU = u;
599  double dV = -E*h/(2.0*Ut);
600  double n = charge_number*(n1*aux2(charge_number*dV)+n2*aux2(-charge_number*dV));
601  double dndx = aux1(-charge_number*dV)*(n2-n1)/h;
602  double J = MU*((n*E)-Ut*(dndx));
603 
604  return J;
605 }
606 
607 //-----------------------------------------------------------------------------
608 // Function : DevicePDEInstance::nMidpoint
609 // Purpose : Carrier density between two points.
610 // Special Notes :
611 // Scope : public
612 // Creator : Eric R. Keiter, SNL
613 // Creation Date : 10/30/12
614 //-----------------------------------------------------------------------------
616 (pdeFadType & n1, pdeFadType & n2, pdeFadType & E, double h, int z)
617 {
618  double charge_number = static_cast<double>(z);
619  pdeFadType dV = -E*h/(2.0*Ut);
620  pdeFadType arg1 = charge_number*dV;
621  pdeFadType arg2 = -charge_number*dV;
622  pdeFadType A1 = aux2(arg1);
623  pdeFadType A2 = aux2(arg2);
624  pdeFadType n = charge_number*(n1*A1+n2*A2);
625  return n;
626 }
627 
628 //-----------------------------------------------------------------------------
629 // Function : DevicePDEInstance::dJdV1_qdep
630 // Purpose : This function returns the derivative of current
631 // density between two points in space, with
632 // respect to the voltage at node 1.
633 // Special Notes :
634 // Scope : public
635 // Creator : Eric R. Keiter, SNL
636 // Creation Date : 11/20/10
637 //-----------------------------------------------------------------------------
639 (double n1, double n2, double E, double u, double h, int z)
640 {
641  double charge_number = static_cast<double>(z);
642  double MU = u;
643  double dV = -E*h/(2.0*Ut);
644  double n = charge_number*(n1*aux2(charge_number*dV)+n2*aux2(-charge_number*dV));
645  double dEdv1 = 1.0/h;
646  double ddVdv1 = -1.0/(2.0*Ut);
647  double dNdv1 = charge_number*(n1*charge_number*ddVdv1*pd1aux2(charge_number*dV)
648  -n2*charge_number*ddVdv1*pd1aux2(-charge_number*dV));
649  double dDNDXdv1 = (n2-n1)/h * (-charge_number*ddVdv1) * pd1aux1(-charge_number*dV);
650  double dJdv1 = MU*((dNdv1*E + n*dEdv1) - Ut*dDNDXdv1);
651 
652  return dJdv1;
653 }
654 
655 //-----------------------------------------------------------------------------
656 // Function : DevicePDEInstance::dJdV2_qdep
657 // Purpose : This function returns the derivative of current
658 // density between two points in space, with
659 // respect to the voltage at node 2.
660 // Special Notes :
661 // Scope : public
662 // Creator : Eric R. Keiter, SNL
663 // Creation Date : 11/20/10
664 //-----------------------------------------------------------------------------
666 (double n1, double n2, double E, double u, double h, int z)
667 {
668  double charge_number = static_cast<double>(z);
669  double MU = u;
670  double dV = -E*h/(2.0*Ut);
671  double n = charge_number*(n1*aux2(charge_number*dV)+n2*aux2(-charge_number*dV));
672  double dEdv2 = -1.0/h;
673  double ddVdv2 = 1.0/(2.0*Ut);
674  double dNdv2 = charge_number*(n1*charge_number*ddVdv2*pd1aux2(charge_number*dV)
675  -n2*charge_number*ddVdv2*pd1aux2(-charge_number*dV));
676  double dDNDXdv2 = (n2-n1)/h * (-charge_number*ddVdv2) * pd1aux1(-charge_number*dV);
677  double dJdv2 = MU*((dNdv2*E + n*dEdv2) - Ut*dDNDXdv2);
678 
679  return dJdv2;
680 }
681 
682 //-----------------------------------------------------------------------------
683 // Function : DevicePDEInstance::dJdV1_qdep
684 // Purpose : This function returns the derivative of current
685 // density between two points in space, with
686 // respect to the voltage at node 1.
687 // Special Notes : This version is used when mu is voltage-dependent.
688 // Scope : public
689 // Creator : Eric R. Keiter, SNL
690 // Creation Date : 10/27/12
691 //-----------------------------------------------------------------------------
693 (double n1, double n2, double E, const pdeFadType & u, double h, int z)
694 {
695  double dJdv1=dJdV1_qdep (n1, n2, E, u.val(), h, z);
696  double dudv1=u.dx(0);
697 
698  // add in chain rule from mobility derivative.
699  if (dudv1 != 0.0)
700  {
701  double charge_number = static_cast<double>(z);
702  double dV = -E*h/(2.0*Ut);
703  double n = charge_number*(n1*aux2(charge_number*dV)+n2*aux2(-charge_number*dV));
704  double dndx = aux1(-charge_number*dV)*(n2-n1)/h;
705  double dJ = dudv1*((n*E)-Ut*(dndx));
706  dJdv1 += dJ;
707  }
708 
709  return dJdv1;
710 }
711 
712 //-----------------------------------------------------------------------------
713 // Function : DevicePDEInstance::dJdV2_qdep
714 // Purpose : This function returns the derivative of current
715 // density between two points in space, with
716 // respect to the voltage at node 2.
717 // Special Notes : This version is used when mu is voltage-dependent.
718 // Scope : public
719 // Creator : Eric R. Keiter, SNL
720 // Creation Date : 10/27/12
721 //-----------------------------------------------------------------------------
723 (double n1, double n2, double E, const pdeFadType & u, double h, int z)
724 {
725  double dJdv2=dJdV2_qdep (n1, n2, E, u.val(), h, z);
726  double dudv2=u.dx(1);
727 
728  // add in chain rule from mobility derivative.
729  if (dudv2 != 0.0)
730  {
731  double charge_number = static_cast<double>(z);
732  double dV = -E*h/(2.0*Ut);
733  double n = charge_number*(n1*aux2(charge_number*dV)+n2*aux2(-charge_number*dV));
734  double dndx = aux1(-charge_number*dV)*(n2-n1)/h;
735  double dJ = dudv2*((n*E)-Ut*(dndx));
736  dJdv2 += dJ;
737  }
738 
739  return dJdv2;
740 }
741 
742 //-----------------------------------------------------------------------------
743 // Function : DevicePDEInstance::dJdn1_qdep
744 // Purpose : This function returns the derivative of current
745 // density between two points in space, with
746 // respect to the density at node 1.
747 // Special Notes :
748 // Scope : public
749 // Creator : Eric R. Keiter, SNL
750 // Creation Date : 11/20/10
751 //-----------------------------------------------------------------------------
753 (double n1, double n2, double E, double u, double h, int z)
754 {
755  double charge_number = static_cast<double>(z);
756  double MU = u;
757  double dV = -E*h/(2.0*Ut);
758  double dNdn1 = charge_number*aux2(charge_number*dV);
759  double dDNDXdn1 = -aux1(-charge_number*dV)/h;
760  double dJdn1 = MU*(dNdn1*E - Ut*dDNDXdn1);
761 
762  return dJdn1;
763 }
764 
765 //-----------------------------------------------------------------------------
766 // Function : DevicePDEInstance::dJdn2_qdep
767 // Purpose : This function returns the derivative of current
768 // density between two points in space, with
769 // respect to the electron density at node 1.
770 // Special Notes :
771 // Scope : public
772 // Creator : Eric R. Keiter, SNL
773 // Creation Date : 11/20/10
774 //-----------------------------------------------------------------------------
776 (double n1, double n2, double E, double u, double h, int z)
777 {
778  double charge_number = static_cast<double>(z);
779  double MU = u;
780  double dV = -E*h/(2.0*Ut);
781  double dNdn2 = charge_number*aux2(-charge_number*dV);
782  double dDNDXdn2 = aux1(-charge_number*dV)/h;
783  double dJdn2 = MU*(dNdn2*E - Ut*dDNDXdn2);
784 
785  return dJdn2;
786 }
787 
788 //
789 //-----------------------------------------------------------------------------
790 // Function : DevicePDEInstance::dJdn1_qdep
791 // Purpose : This function returns the derivative of current
792 // density between two points in space, with
793 // respect to the density at node 1.
794 //
795 // Special Notes : This version assumes that the mobility is dependent on n1
796 // as well.
797 //
798 // Scope : public
799 // Creator : Eric R. Keiter, SNL
800 // Creation Date : 10/29/12
801 //-----------------------------------------------------------------------------
803 (double n1, double n2, double E, const pdeFadType & u, double h, int z)
804 {
805  double dJdn1=dJdn1_qdep (n1, n2, E, u.val(), h, z);
806  double dudn1=0.0;
807  if (z < 0) dudn1=u.dx(2);
808  else dudn1=u.dx(4);
809 
810  if (dudn1 !=0.0)
811  {
812  double charge_number = static_cast<double>(z);
813  double dV = -E*h/(2.0*Ut);
814  double n = charge_number*(n1*aux2(charge_number*dV)+n2*aux2(-charge_number*dV));
815  double dndx = aux1(-charge_number*dV)*(n2-n1)/h;
816 
817  dJdn1 += dudn1*((n*E)-Ut*(dndx));
818  }
819 
820  return dJdn1;
821 }
822 
823 //-----------------------------------------------------------------------------
824 // Function : DevicePDEInstance::dJdn2_qdep
825 // Purpose : This function returns the derivative of current
826 // density between two points in space, with
827 // respect to the electron density at node 1.
828 //
829 // Special Notes : This version assumes that the mobility is dependent on n2
830 // as well.
831 //
832 // Scope : public
833 // Creator : Eric R. Keiter, SNL
834 // Creation Date : 10/29/12
835 //-----------------------------------------------------------------------------
837 (double n1, double n2, double E, const pdeFadType & u, double h, int z)
838 {
839  double dJdn2=dJdn2_qdep (n1, n2, E, u.val(), h, z);
840  double dudn2=0.0;
841  if (z < 0) dudn2=u.dx(3);
842  else dudn2=u.dx(5);
843 
844  if (dudn2 !=0.0)
845  {
846  double charge_number = static_cast<double>(z);
847  double dV = -E*h/(2.0*Ut);
848  double n = charge_number*(n1*aux2(charge_number*dV)+n2*aux2(-charge_number*dV));
849  double dndx = aux1(-charge_number*dV)*(n2-n1)/h;
850 
851  dJdn2 += dudn2*((n*E)-Ut*(dndx));
852  }
853 
854  return dJdn2;
855 }
856 //
857 //-----------------------------------------------------------------------------
858 // Function : DevicePDEInstance::dJdp1_qdep
859 // Purpose : This function returns the derivative of current
860 // density between two points in space, with
861 // respect to the density at node 1.
862 //
863 // Special Notes : This version assumes that the mobility is dependent on p1
864 // as well. In this context "p" is the density of the other
865 // carrier. The only dependence will be via a carrier-dependent
866 // mobility. This is a little confusing. If "J" in this
867 // function is a hole current, then "p" refers to
868 // electrons.
869 //
870 // Scope : public
871 // Creator : Eric R. Keiter, SNL
872 // Creation Date : 10/29/12
873 //-----------------------------------------------------------------------------
875 (double n1, double n2, double E, const pdeFadType & u, double h, int z)
876 {
877  double dJdp1=0.0;
878  double dudp1=0.0;
879  if (z < 0) dudp1=u.dx(4);
880  else dudp1=u.dx(2);
881 
882  if (dudp1 != 0.0)
883  {
884  double charge_number = static_cast<double>(z);
885  double dV = -E*h/(2.0*Ut);
886  double n = charge_number*(n1*aux2(charge_number*dV)+n2*aux2(-charge_number*dV));
887  double dndx = aux1(-charge_number*dV)*(n2-n1)/h;
888 
889  dJdp1 += dudp1*((n*E)-Ut*(dndx));
890  }
891 
892  return dJdp1;
893 }
894 
895 //-----------------------------------------------------------------------------
896 // Function : DevicePDEInstance::dJdp2_qdep
897 // Purpose : This function returns the derivative of current
898 // density between two points in space, with
899 // respect to the electron density at node 1.
900 //
901 // Special Notes : This version assumes that the mobility is dependent on p2
902 // as well. In this context "p" is the density of the other
903 // carrier. The only dependence will be via a carrier-dependent
904 // mobility. This is a little confusing. If "J" in this
905 // function is a hole current, then "p" refers to
906 // electrons.
907 //
908 // Scope : public
909 // Creator : Eric R. Keiter, SNL
910 // Creation Date : 10/29/12
911 //-----------------------------------------------------------------------------
913 (double n1, double n2, double E, const pdeFadType & u, double h, int z)
914 {
915  double dJdp2=0.0;
916  double dudp2=0.0;
917  if (z < 0) dudp2=u.dx(5);
918  else dudp2=u.dx(3);
919 
920  if (dudp2 != 0.0)
921  {
922  double charge_number = static_cast<double>(z);
923  double dV = -E*h/(2.0*Ut);
924  double n = charge_number*(n1*aux2(charge_number*dV)+n2*aux2(-charge_number*dV));
925  double dndx = aux1(-charge_number*dV)*(n2-n1)/h;
926  dJdp2 += dudp2*((n*E)-Ut*(dndx));
927  }
928 
929  return dJdp2;
930 }
931 
932 
933 
934 
935 //-----------------------------------------------------------------------------
936 // Function : DevicePDEInstance::dJdbm1_qdep
937 // Purpose : This function returns the derivative of current
938 // density between two points in space, with
939 // respect to the dopants at node 1.
940 // Special Notes : This version is used when mu is dopant-dependent and dopants are variable
941 // Scope : public
942 // Creator : Lawrence C Musson, SNL
943 // Creation Date : 10/24/13
944 //-----------------------------------------------------------------------------
946 (double n1, double n2, double E, const pdeFadType & u, double h, int z)
947 {
948  double dJdbm1 = 0.0;
949  double dudbm1=u.dx(6);
950 
951  // add in chain rule from mobility derivative.
952  if (dudbm1 != 0.0)
953  {
954  double charge_number = static_cast<double>(z);
955  double dV = -E*h/(2.0*Ut);
956  double n = charge_number*(n1*aux2(charge_number*dV)+n2*aux2(-charge_number*dV));
957  double dndx = aux1(-charge_number*dV)*(n2-n1)/h;
958  double dJ = dudbm1*((n*E)-Ut*(dndx));
959  dJdbm1 += dJ;
960  }
961 
962  return dJdbm1;
963 }
964 
965 
966 //-----------------------------------------------------------------------------
967 // Function : DevicePDEInstance::dJdbm2_qdep
968 // Purpose : This function returns the derivative of current
969 // density between two points in space, with
970 // respect to the dopants at node 1.
971 // Special Notes : This version is used when mu is dopant-dependent and dopants are variable
972 // Scope : public
973 // Creator : Lawrence C Musson, SNL
974 // Creation Date : 10/24/13
975 //-----------------------------------------------------------------------------
977 (double n1, double n2, double E, const pdeFadType & u, double h, int z)
978 {
979  double dJdbm2=0.0;
980  double dudbm2=u.dx(8);
981 
982  // add in chain rule from mobility derivative.
983  if (dudbm2 != 0.0)
984  {
985  double charge_number = static_cast<double>(z);
986  double dV = -E*h/(2.0*Ut);
987  double n = charge_number*(n1*aux2(charge_number*dV)+n2*aux2(-charge_number*dV));
988  double dndx = aux1(-charge_number*dV)*(n2-n1)/h;
989  double dJ = dudbm2*((n*E)-Ut*(dndx));
990  dJdbm2 += dJ;
991  }
992 
993  return dJdbm2;
994 }
995 
996 
997 //-----------------------------------------------------------------------------
998 // Function : DevicePDEInstance::dJdpp1_qdep
999 // Purpose : This function returns the derivative of current
1000 // density between two points in space, with
1001 // respect to the dopants at node 1.
1002 // Special Notes : This version is used when mu is dopant-dependent and dopants are variable
1003 // Scope : public
1004 // Creator : Lawrence C Musson, SNL
1005 // Creation Date : 10/24/13
1006 //-----------------------------------------------------------------------------
1008 (double n1, double n2, double E, const pdeFadType & u, double h, int z)
1009 {
1010  double dJdpp1=0.0;
1011  double dudpp1=u.dx(7);
1012 
1013  // add in chain rule from mobility derivative.
1014  if (dudpp1 != 0.0)
1015  {
1016  double charge_number = static_cast<double>(z);
1017  double dV = -E*h/(2.0*Ut);
1018  double n = charge_number*(n1*aux2(charge_number*dV)+n2*aux2(-charge_number*dV));
1019  double dndx = aux1(-charge_number*dV)*(n2-n1)/h;
1020  double dJ = dudpp1*((n*E)-Ut*(dndx));
1021  dJdpp1 += dJ;
1022  }
1023 
1024  return dJdpp1;
1025 }
1026 
1027 
1028 //-----------------------------------------------------------------------------
1029 // Function : DevicePDEInstance::dJdpp2_qdep
1030 // Purpose : This function returns the derivative of current
1031 // density between two points in space, with
1032 // respect to the dopants at node 1.
1033 // Special Notes : This version is used when mu is dopant-dependent and dopants are variable
1034 // Scope : public
1035 // Creator : Lawrence C Musson, SNL
1036 // Creation Date : 10/24/13
1037 //-----------------------------------------------------------------------------
1039 (double n1, double n2, double E, const pdeFadType & u, double h, int z)
1040 {
1041  double dJdpp2=0.0;
1042  double dudpp2=u.dx(9);
1043 
1044  // add in chain rule from mobility derivative.
1045  if (dudpp2 != 0.0)
1046  {
1047  double charge_number = static_cast<double>(z);
1048  double dV = -E*h/(2.0*Ut);
1049  double n = charge_number*(n1*aux2(charge_number*dV)+n2*aux2(-charge_number*dV));
1050  double dndx = aux1(-charge_number*dV)*(n2-n1)/h;
1051  double dJ = dudpp2*((n*E)-Ut*(dndx));
1052  dJdpp2 += dJ;
1053  }
1054 
1055  return dJdpp2;
1056 }
1057 
1058 
1059 
1060 
1061 
1062 //
1063 //
1064 #if 0
1065 // ----------------------------------------------------------------------------
1066 // Function : DevicePDEInstance::nsdep
1067 // Purpose : This function returns an approximate deposition profile
1068 // of a step implant driven in an inert environment.
1069 // Special Notes :
1070 // 1 W/2 + x W/2 - x
1071 // nsdep(x,W,Dt) = - (erf(---------) + erf(----------))
1072 // 2 2*sqrt(Dt) 2*sqrt(Dt)
1073 //
1074 //
1075 // Scope : public
1076 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1077 // Creation Date : 4/25/02
1078 // ----------------------------------------------------------------------------
1079 double DevicePDEInstance::nsdep (double x, double W, double Dt)
1080 {
1081  double D = 2.0 * sqrt(Dt);
1082  double Wh = W / 2.0;
1083  return 0.5 * (erf((Wh + x)/D) + erf((Wh - x)/D));
1084 }
1085 
1086 // ----------------------------------------------------------------------------
1087 // Function : DevicePDEInstance::ngdep
1088 //
1089 // Purpose : This function returns an approximate Gaussian deposition.
1090 //
1091 // Adapted from a similar function in SGF.
1092 //
1093 // Special Notes : This function is a little flakey, in that it assumes
1094 // that we're using a cylindrical geomtry
1095 // (x = radius, y = height.) It also assumes that the (0,0)
1096 // origin is in the upper-left-hand corner of the mesh.
1097 //
1098 // So, y=0.0 is the upper surface of the device, and the
1099 // implant is coming from the top (above y=0.0). Hence,
1100 // the (y>0) conditional.
1101 //
1102 // Also, the width parameter (W), is set up to be a
1103 // diameter about x=0, which is the reason for the 0.5*W -
1104 // half of this diameter will impact this radius.
1105 //
1106 // The implant is completely flat and constant in the
1107 // x-direction, as long as fabs(x) is less than W/2.0.
1108 // Beyond W/2.0, the gaussian profile kicks in.
1109 //
1110 // The parameters ax and ay are scaling parameters, and
1111 // correspond to how much you want the doping to vary with
1112 // space. A typical value for either can be set up as:
1113 //
1114 // Ax = ln (Nhi / Nlo)/(Rx*Rx)
1115 //
1116 // where:
1117 //
1118 // Nhi = the max. level of doping
1119 // Nlo = the min. level of doping
1120 // Rx = distance over which this doping should vary.
1121 //
1122 // Nhi/Nlo = 10^N, where N = # of orders of magnitude
1123 // that should vary between x=0 and x=Rx.
1124 //
1125 // Scope : public
1126 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1127 // Creation Date : 4/25/02
1128 // ----------------------------------------------------------------------------
1129 double DevicePDEInstance::ngdep
1130 (double x, double y, double W, double ax, double ay)
1131 {
1132  double xprime = fabs(x) - (0.5 * W);
1133  return ((xprime <= 0.0) ? 1.0 : exp(-ax*xprime*xprime))*
1134  ((y > 0.0) ? 0.0 : exp(-ay*y*y));
1135 }
1136 
1137 // ----------------------------------------------------------------------------
1138 // Function : DevicePDEInstance::ngdep2
1139 //
1140 // Purpose : This function returns an approximate Gaussian deposition.
1141 //
1142 // Special Notes : This function is a modification of the original ngdep
1143 // (see above), and I designed it to address some of the
1144 // peculiarities of ngdep.
1145 //
1146 // (1) I've gotten rid of the width, W. I'm just
1147 // going to assume that whoever is calling this function
1148 // can set that(the constant region) up on their own.
1149 //
1150 // (2) I've removed the conditionals cause things to
1151 // be set to zero, or one, or whatever, if you are on one
1152 // side or another of the suface. I'm assuming that
1153 // whoever calls this function can do that themselves, if
1154 // they need to.
1155 //
1156 // (3) I've removed the stuff that sets the retVal to zero
1157 // for y>0. Again, this is the user's problem.
1158 //
1159 // ax and ay mean the same as they did for the original
1160 // ngdep. (see above).
1161 //
1162 // It is possible to use this for the 1D case, pretty
1163 // easily. Set the xflag to false, hold y fixed at zero,
1164 // and have x correspond to the 1D mesh locations. (or, set
1165 // xflag to true, hold x fixed at zero, and let y
1166 // correspond to 1D mesh locations - either way).
1167 //
1168 // Scope : public
1169 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1170 // Creation Date : 03/28/03
1171 // ----------------------------------------------------------------------------
1172 double DevicePDEInstance::ngdep2
1173 (double x, double y, double ax, double ay)
1174 {
1175  double retVal = exp(-ax*x*x)* exp(-ay*y*y);
1176  return retVal;
1177 }
1178 #endif
1179 
1180 // ----------------------------------------------------------------------------
1181 // Function : DevicePDEInstance::erf
1182 // Purpose : This function returns the error function.
1183 // Special Notes :
1184 // Scope : public
1185 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1186 // Creation Date : 4/25/02
1187 // ----------------------------------------------------------------------------
1188 double DevicePDEInstance::erf(double x)
1189 {
1190  double t1 = 1.0 / (1.0 + 0.3275911 * fabs(x));
1191  double t2 = t1 * t1;
1192  double t3 = t2 * t1;
1193  double t4 = t3 * t1;
1194  double t5 = t4 * t1;
1195  double result = 1.0 - (0.254829592*t1 - 0.284496736*t2 + 1.421413741*t3 -
1196  1.453152027*t4 + 1.061405429*t5) * exp(-x*x);
1197  return (x < 0.0) ? -result : result;
1198 }
1199 
1200 // ----------------------------------------------------------------------------
1201 // Function : DevicePDEInstance::pd1erf
1202 // Purpose : This function returns the derivative of the error
1203 // function with respect to the first variable.
1204 // Special Notes :
1205 // Scope : public
1206 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1207 // Creation Date : 4/25/02
1208 // ----------------------------------------------------------------------------
1210 {
1211  double pi = M_PI;
1212  return 2.0 / sqrt(pi) * exp(-x*x);
1213 }
1214 
1215 } // namespace Device
1216 } // namespace Xyce