1 /**********
2 Copyright 1991 Regents of the University of California.  All rights reserved.
3 Author:	1987 Kartikeya Mayaram, U. C. Berkeley CAD Group
4 Author:	1991 David A. Gates, U. C. Berkeley CAD Group
5 **********/
6 
7 #include "ngspice/ngspice.h"
8 #include "ngspice/numglobs.h"
9 #include "ngspice/numenum.h"
10 #include "ngspice/twomesh.h"
11 #include "ngspice/twodev.h"
12 #include "ngspice/profile.h"
13 #include "ngspice/macros.h"
14 #include "ngspice/bool.h"
15 #include "twoddefs.h"
16 #include "twodext.h"
17 #include "ngspice/cidersupt.h"
18 
19 /* functions in this file are used to calculate the conc */
20 
21 double
TWOdopingValue(DOPprofile * pProfile,DOPtable * pTable,double x,double y)22 TWOdopingValue(DOPprofile *pProfile, DOPtable *pTable, double x,
23                double y)
24 {
25   double argX, argY, argP, argL, value = 0.0;
26 
27   /* Find the appropriate lookup table if necessary */
28   if (pProfile->type == LOOKUP) {
29     while ( pTable != NULL ) {
30       if (pTable->impId == pProfile->IMPID) {
31         /* Found it */
32 	break;
33       } else {
34 	pTable = pTable->next;
35       }
36     }
37     if ( pTable == NULL ) {
38       fprintf( stderr, "Error: unknown impurity profile %d\n",
39 	      ((int)pProfile->IMPID) );
40       controlled_exit(1);
41     }
42   }
43   /* Find distances */
44   if ( pProfile->Y_LOW > y ) {
45     argY = pProfile->Y_LOW - y;
46   } else if ( y > pProfile->Y_HIGH ) {
47     argY = y - pProfile->Y_HIGH;
48   } else {
49     argY = 0.0;
50   }
51   if ( pProfile->X_LOW > x ) {
52     argX = pProfile->X_LOW - x;
53   } else if ( x > pProfile->X_HIGH ) {
54     argX = x - pProfile->X_HIGH;
55   } else {
56     argX = 0.0;
57   }
58 
59   if ( pProfile->DIRECTION == Y ) {
60     argP = argY;
61     argL = argX / pProfile->LAT_RATIO;
62   }
63   else {
64     argP = argX;
65     argL = argY / pProfile->LAT_RATIO;
66   }
67   if ( pProfile->rotate ) {
68     argP = hypot(argP, argL);
69     argL = 0.0;
70   }
71 
72   /* Transform to coordinates of profile peak */
73   argP -= pProfile->LOCATION;
74   argP /= pProfile->CHAR_LENGTH;
75   argL -= pProfile->LOCATION;
76   argL /= pProfile->CHAR_LENGTH;
77 
78   switch (pProfile->type) {
79   case UNIF:
80     if (argP > 0.0) {
81       value = 0.0;
82     } else {
83       value = pProfile->CONC;
84     }
85     break;
86   case LIN:
87     argP = ABS(argP);
88     if (argP > 1.0) {
89       value = 0.0;
90     } else {
91       value = pProfile->CONC * ( 1.0 - argP );
92     }
93     break;
94   case GAUSS:
95     argP *= argP;
96     if ( argP > 80.0 ) {
97       value = 0.0;
98     } else {
99       value = pProfile->PEAK_CONC * exp( -argP );
100     }
101     break;
102   case EXP:
103     argP = ABS(argP);
104     if ( argP > 80.0 ) {
105       value = 0.0;
106     } else {
107       value = pProfile->PEAK_CONC * exp( -argP );
108     }
109     break;
110   case ERRFC:
111     argP = ABS(argP);
112     if ( argP > 10.0 ) {
113       value = 0.0;
114     } else {
115       value = pProfile->PEAK_CONC * erfc( argP );
116     }
117     break;
118   case LOOKUP:
119     argP = ABS(argP);
120     value = lookup( pTable->dopData, argP );
121     break;
122   default:
123     break;
124   }
125   if (!pProfile->rotate) { /* Tensor product in lateral direction */
126     switch (pProfile->latType) {
127     case UNIF:
128       if (argL > 0.0) {
129 	value = 0.0;
130       }
131       break;
132     case LIN:
133       argL = ABS(argL);
134       if (argL > 1.0) {
135 	value = 0.0;
136       } else {
137 	value *= ( 1.0 - argL );
138       }
139       break;
140     case GAUSS:
141       argL *= argL;
142       if ( argL > 80.0 ) {
143 	value = 0.0;
144       } else {
145 	value *= exp( -argL );
146       }
147       break;
148     case EXP:
149       argL = ABS(argL);
150       if ( argL > 80.0 ) {
151 	value = 0.0;
152       } else {
153 	value *= exp( -argL );
154       }
155       break;
156     case ERRFC:
157       argL = ABS(argL);
158       if ( argP > 10.0 ) {
159 	value = 0.0;
160       } else {
161 	value *= erfc( argL );
162       }
163       break;
164     case LOOKUP:
165       argL = ABS(argL);
166       value *= lookup( pTable->dopData, argL ) / lookup( pTable->dopData, 0.0 );
167       break;
168     default:
169       break;
170     }
171   } /* end: not rotated */
172   return( value );
173 }
174 
175 void
TWOsetDoping(TWOdevice * pDevice,DOPprofile * pProfile,DOPtable * pTable)176 TWOsetDoping(TWOdevice *pDevice, DOPprofile *pProfile, DOPtable *pTable)
177 {
178   TWOnode *pNode;
179   TWOelem *pElem;
180   DOPprofile *pP;
181   double conc;
182   int index, eIndex;
183   BOOLEAN dopeMe;
184 
185   /* Clear doping info for all nodes. */
186   for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
187     pElem = pDevice->elements[ eIndex ];
188     for (index = 0; index <= 3; index++) {
189       if (pElem->evalNodes[index]) {
190 	pNode = pElem->pNodes[index];
191 	pNode->na = 0.0;
192 	pNode->nd = 0.0;
193 	pNode->netConc = 0.0;
194 	pNode->totalConc = 0.0;
195       }
196     }
197   }
198   /* Now compute the contribution to the total doping from each profile. */
199   for ( pP = pProfile; pP != NULL; pP = pP->next ) {
200     for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
201       pElem = pDevice->elements[ eIndex ];
202       if ( pElem->elemType == SEMICON ) {
203 	if ( pP->numDomains > 0 ) {
204 	  dopeMe = FALSE;
205 	  for ( index = 0; index < pP->numDomains; index++ ) {
206 	    if ( pElem->domain == pP->domains[ index ] ) {
207 	      dopeMe = TRUE;
208 	      break;
209 	    }
210 	  }
211 	} else { /* domains not given, so dope all */
212 	  dopeMe = TRUE;
213 	}
214 	if ( dopeMe ) {
215 	  for ( index = 0; index <= 3; index++ ) {
216 	    if ( pElem->evalNodes[ index ] ) {
217 	      pNode = pElem->pNodes[ index ];
218 	      conc = TWOdopingValue( pP, pTable,
219 		  pDevice->xScale[ pNode->nodeI ],
220 		  pDevice->yScale[ pNode->nodeJ ] );
221 	      pNode->netConc += conc;
222 	      if ( conc < 0.0 ) {
223 		pNode->totalConc -= conc;
224 		pNode->na -= conc;
225 	      }
226 	      else {
227 		pNode->totalConc += conc;
228 		pNode->nd += conc;
229 	      }
230 	    }
231 	  }
232 	}
233       }
234     }
235   }
236 }
237