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