You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1052 lines
36 KiB

/**********
Copyright 1991 Regents of the University of California. All rights reserved.
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group
Author: 1991 David A. Gates, U. C. Berkeley CAD Group
$Id$
**********/
#include "ngspice.h"
#include "numglobs.h"
#include "numenum.h"
#include "twomesh.h"
#include "twodev.h"
#include "bool.h"
#include "spmatrix.h"
#include "twoddefs.h"
#include "twodext.h"
#include "cidersupt.h"
#include "../../maths/misc/bernoull.h"
/*
* Functions to setup and solve the continuity equations.
* Both continuity equations are solved.
* Separate functions are used for one continuity equation.
*/
/*
* setup matrix pointers to Jacobian values and
* store direct pointers with the nodes
*/
void
TWO_jacBuild(TWOdevice *pDevice)
{
char *matrix = pDevice->matrix;
double *spGetElement();
TWOelem *pElem;
TWOnode *pNode;
TWOchannel *pCh;
int eIndex, nIndex;
int nextIndex; /* index of node to find next element */
int psiEqn, nEqn, pEqn; /* scratch for deref'd eqn numbers */
int psiEqnTL = 0, nEqnTL = 0, pEqnTL = 0;
int psiEqnTR = 0, nEqnTR = 0, pEqnTR = 0;
int psiEqnBR = 0, nEqnBR = 0, pEqnBR = 0;
int psiEqnBL = 0, nEqnBL = 0, pEqnBL = 0;
int psiEqnInM = 0, psiEqnInP = 0; /* scratch for deref'd surface eqns */
int psiEqnOxM = 0, psiEqnOxP = 0; /* M= more negative, P= more positive */
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
pElem = pDevice->elements[ eIndex ];
/* first the self terms */
for ( nIndex = 0; nIndex <= 3; nIndex++ ) {
pNode = pElem->pNodes[ nIndex ];
/* get poisson-only pointer */
psiEqn = pNode->psiEqn;
pNode->fPsiPsi = spGetElement( matrix, psiEqn, psiEqn );
if ( pElem->elemType == SEMICON ) {
/* get continuity-coupling terms */
nEqn = pNode->nEqn;
pEqn = pNode->pEqn;
/* pointers for additional terms */
pNode->fPsiN = spGetElement( matrix, psiEqn, nEqn );
pNode->fPsiP = spGetElement( matrix, psiEqn, pEqn );
pNode->fNPsi = spGetElement( matrix, nEqn, psiEqn );
pNode->fNN = spGetElement( matrix, nEqn, nEqn );
pNode->fNP = spGetElement( matrix, nEqn, pEqn );
pNode->fPPsi = spGetElement( matrix, pEqn, psiEqn );
pNode->fPN = spGetElement( matrix, pEqn, nEqn );
pNode->fPP = spGetElement( matrix, pEqn, pEqn );
} else {
nEqn = 0;
pEqn = 0;
}
/* save equation indices */
switch ( nIndex ) {
case 0: /* TL Node */
psiEqnTL = psiEqn;
nEqnTL = nEqn;
pEqnTL = pEqn;
break;
case 1: /* TR Node */
psiEqnTR = psiEqn;
nEqnTR = nEqn;
pEqnTR = pEqn;
break;
case 2: /* BR Node */
psiEqnBR = psiEqn;
nEqnBR = nEqn;
pEqnBR = pEqn;
break;
case 3: /* BL Node */
psiEqnBL = psiEqn;
nEqnBL = nEqn;
pEqnBL = pEqn;
break;
default:
break;
}
}
/* now terms to couple to adjacent nodes */
pNode = pElem->pTLNode;
pNode->fPsiPsiiP1 = spGetElement(matrix, psiEqnTL, psiEqnTR );
pNode->fPsiPsijP1 = spGetElement(matrix, psiEqnTL, psiEqnBL );
if ( pElem->elemType == SEMICON ) {
/* continuity equation pointers */
pNode->fNPsiiP1 = spGetElement( matrix, nEqnTL, psiEqnTR );
pNode->fNNiP1 = spGetElement( matrix, nEqnTL, nEqnTR );
pNode->fNPsijP1 = spGetElement( matrix, nEqnTL, psiEqnBL );
pNode->fNNjP1 = spGetElement( matrix, nEqnTL, nEqnBL );
pNode->fPPsiiP1 = spGetElement( matrix, pEqnTL, psiEqnTR );
pNode->fPPiP1 = spGetElement( matrix, pEqnTL, pEqnTR );
pNode->fPPsijP1 = spGetElement( matrix, pEqnTL, psiEqnBL );
pNode->fPPjP1 = spGetElement( matrix, pEqnTL, pEqnBL );
/* Surface Mobility Model depends on diagonal node values */
if ( MobDeriv && SurfaceMobility && pElem->channel ) {
pNode->fNPsiiP1jP1 = spGetElement( matrix, nEqnTL, psiEqnBR );
pNode->fNNiP1jP1 = spGetElement( matrix, nEqnTL, nEqnBR );
pNode->fPPsiiP1jP1 = spGetElement( matrix, pEqnTL, psiEqnBR );
pNode->fPPiP1jP1 = spGetElement( matrix, pEqnTL, pEqnBR );
}
}
pNode = pElem->pTRNode;
pNode->fPsiPsiiM1 = spGetElement(matrix, psiEqnTR, psiEqnTL );
pNode->fPsiPsijP1 = spGetElement(matrix, psiEqnTR, psiEqnBR );
if ( pElem->elemType == SEMICON ) {
/* continuity equation pointers */
pNode->fNPsiiM1 = spGetElement( matrix, nEqnTR, psiEqnTL );
pNode->fNNiM1 = spGetElement( matrix, nEqnTR, nEqnTL );
pNode->fNPsijP1 = spGetElement( matrix, nEqnTR, psiEqnBR );
pNode->fNNjP1 = spGetElement( matrix, nEqnTR, nEqnBR );
pNode->fPPsiiM1 = spGetElement( matrix, pEqnTR, psiEqnTL );
pNode->fPPiM1 = spGetElement( matrix, pEqnTR, pEqnTL );
pNode->fPPsijP1 = spGetElement( matrix, pEqnTR, psiEqnBR );
pNode->fPPjP1 = spGetElement( matrix, pEqnTR, pEqnBR );
/* Surface Mobility Model depends on diagonal node values */
if ( MobDeriv && SurfaceMobility && pElem->channel ) {
pNode->fNPsiiM1jP1 = spGetElement( matrix, nEqnTR, psiEqnBL );
pNode->fNNiM1jP1 = spGetElement( matrix, nEqnTR, nEqnBL );
pNode->fPPsiiM1jP1 = spGetElement( matrix, pEqnTR, psiEqnBL );
pNode->fPPiM1jP1 = spGetElement( matrix, pEqnTR, pEqnBL );
}
}
pNode = pElem->pBRNode;
pNode->fPsiPsiiM1 = spGetElement(matrix, psiEqnBR, psiEqnBL );
pNode->fPsiPsijM1 = spGetElement(matrix, psiEqnBR, psiEqnTR );
if ( pElem->elemType == SEMICON ) {
/* continuity equation pointers */
pNode->fNPsiiM1 = spGetElement( matrix, nEqnBR, psiEqnBL );
pNode->fNNiM1 = spGetElement( matrix, nEqnBR, nEqnBL );
pNode->fNPsijM1 = spGetElement( matrix, nEqnBR, psiEqnTR );
pNode->fNNjM1 = spGetElement( matrix, nEqnBR, nEqnTR );
pNode->fPPsiiM1 = spGetElement( matrix, pEqnBR, psiEqnBL );
pNode->fPPiM1 = spGetElement( matrix, pEqnBR, pEqnBL );
pNode->fPPsijM1 = spGetElement( matrix, pEqnBR, psiEqnTR );
pNode->fPPjM1 = spGetElement( matrix, pEqnBR, pEqnTR );
/* Surface Mobility Model depends on diagonal node values */
if ( MobDeriv && SurfaceMobility && pElem->channel ) {
pNode->fNPsiiM1jM1 = spGetElement( matrix, nEqnBR, psiEqnTL );
pNode->fNNiM1jM1 = spGetElement( matrix, nEqnBR, nEqnTL );
pNode->fPPsiiM1jM1 = spGetElement( matrix, pEqnBR, psiEqnTL );
pNode->fPPiM1jM1 = spGetElement( matrix, pEqnBR, pEqnTL );
}
}
pNode = pElem->pBLNode;
pNode->fPsiPsiiP1 = spGetElement(matrix, psiEqnBL, psiEqnBR );
pNode->fPsiPsijM1 = spGetElement(matrix, psiEqnBL, psiEqnTL );
if ( pElem->elemType == SEMICON ) {
/* continuity equation pointers */
pNode->fNPsiiP1 = spGetElement( matrix, nEqnBL, psiEqnBR );
pNode->fNNiP1 = spGetElement( matrix, nEqnBL, nEqnBR );
pNode->fNPsijM1 = spGetElement( matrix, nEqnBL, psiEqnTL );
pNode->fNNjM1 = spGetElement( matrix, nEqnBL, nEqnTL );
pNode->fPPsiiP1 = spGetElement( matrix, pEqnBL, psiEqnBR );
pNode->fPPiP1 = spGetElement( matrix, pEqnBL, pEqnBR );
pNode->fPPsijM1 = spGetElement( matrix, pEqnBL, psiEqnTL );
pNode->fPPjM1 = spGetElement( matrix, pEqnBL, pEqnTL );
/* Surface Mobility Model depends on diagonal node values */
if ( MobDeriv && SurfaceMobility && pElem->channel ) {
pNode->fNPsiiP1jM1 = spGetElement( matrix, nEqnBL, psiEqnTR );
pNode->fNNiP1jM1 = spGetElement( matrix, nEqnBL, nEqnTR );
pNode->fPPsiiP1jM1 = spGetElement( matrix, pEqnBL, psiEqnTR );
pNode->fPPiP1jM1 = spGetElement( matrix, pEqnBL, pEqnTR );
}
}
}
/*
* Add terms for surface-field of inversion-layer mobility model.
* Elements MUST be made from silicon for this to work.
* No empty elements are allowed.
* Don't need these pointers if SurfaceMobility isn't set.
*/
if ( MobDeriv && SurfaceMobility ) {
for ( pCh = pDevice->pChannel; pCh != NIL(TWOchannel);
pCh = pCh->next ) {
pElem = pCh->pNElem;
switch (pCh->type) {
case 0:
psiEqnInM = pElem->pBLNode->psiEqn;
psiEqnInP = pElem->pBRNode->psiEqn;
psiEqnOxM = pElem->pTLNode->psiEqn;
psiEqnOxP = pElem->pTRNode->psiEqn;
break;
case 1:
psiEqnInM = pElem->pTLNode->psiEqn;
psiEqnInP = pElem->pBLNode->psiEqn;
psiEqnOxM = pElem->pTRNode->psiEqn;
psiEqnOxP = pElem->pBRNode->psiEqn;
break;
case 2:
psiEqnInM = pElem->pTLNode->psiEqn;
psiEqnInP = pElem->pTRNode->psiEqn;
psiEqnOxM = pElem->pBLNode->psiEqn;
psiEqnOxP = pElem->pBRNode->psiEqn;
break;
case 3:
psiEqnInM = pElem->pTRNode->psiEqn;
psiEqnInP = pElem->pBRNode->psiEqn;
psiEqnOxM = pElem->pTLNode->psiEqn;
psiEqnOxP = pElem->pBLNode->psiEqn;
break;
}
pElem = pCh->pSeed;
nextIndex = (pCh->type + 2)%4;
while (pElem && pElem->channel == pCh->id) {
for ( nIndex = 0; nIndex <= 3; nIndex++ ) {
pNode = pElem->pNodes[ nIndex ];
psiEqn = pNode->psiEqn;
nEqn = pNode->nEqn;
pEqn = pNode->pEqn;
if ( pCh->type % 2 == 0 ) { /* Vertical Slice */
if ( nIndex == 0 || nIndex == 3 ) { /* Left Side */
pNode->fNPsiIn = spGetElement( matrix, nEqn, psiEqnInM );
pNode->fNPsiInP1 = spGetElement( matrix, nEqn, psiEqnInP );
pNode->fNPsiOx = spGetElement( matrix, nEqn, psiEqnOxM );
pNode->fNPsiOxP1 = spGetElement( matrix, nEqn, psiEqnOxP );
pNode->fPPsiIn = spGetElement( matrix, pEqn, psiEqnInM );
pNode->fPPsiInP1 = spGetElement( matrix, pEqn, psiEqnInP );
pNode->fPPsiOx = spGetElement( matrix, pEqn, psiEqnOxM );
pNode->fPPsiOxP1 = spGetElement( matrix, pEqn, psiEqnOxP );
} else { /* Right Side */
pNode->fNPsiInM1 = spGetElement( matrix, nEqn, psiEqnInM );
pNode->fNPsiIn = spGetElement( matrix, nEqn, psiEqnInP );
pNode->fNPsiOxM1 = spGetElement( matrix, nEqn, psiEqnOxM );
pNode->fNPsiOx = spGetElement( matrix, nEqn, psiEqnOxP );
pNode->fPPsiInM1 = spGetElement( matrix, pEqn, psiEqnInM );
pNode->fPPsiIn = spGetElement( matrix, pEqn, psiEqnInP );
pNode->fPPsiOxM1 = spGetElement( matrix, pEqn, psiEqnOxM );
pNode->fPPsiOx = spGetElement( matrix, pEqn, psiEqnOxP );
}
} else { /* Horizontal Slice */
if ( nIndex <= 1 ) { /* Top Side */
pNode->fNPsiIn = spGetElement( matrix, nEqn, psiEqnInM );
pNode->fNPsiInP1 = spGetElement( matrix, nEqn, psiEqnInP );
pNode->fNPsiOx = spGetElement( matrix, nEqn, psiEqnOxM );
pNode->fNPsiOxP1 = spGetElement( matrix, nEqn, psiEqnOxP );
pNode->fPPsiIn = spGetElement( matrix, pEqn, psiEqnInM );
pNode->fPPsiInP1 = spGetElement( matrix, pEqn, psiEqnInP );
pNode->fPPsiOx = spGetElement( matrix, pEqn, psiEqnOxM );
pNode->fPPsiOxP1 = spGetElement( matrix, pEqn, psiEqnOxP );
} else { /* Bottom Side */
pNode->fNPsiInM1 = spGetElement( matrix, nEqn, psiEqnInM );
pNode->fNPsiIn = spGetElement( matrix, nEqn, psiEqnInP );
pNode->fNPsiOxM1 = spGetElement( matrix, nEqn, psiEqnOxM );
pNode->fNPsiOx = spGetElement( matrix, nEqn, psiEqnOxP );
pNode->fPPsiInM1 = spGetElement( matrix, pEqn, psiEqnInM );
pNode->fPPsiIn = spGetElement( matrix, pEqn, psiEqnInP );
pNode->fPPsiOxM1 = spGetElement( matrix, pEqn, psiEqnOxM );
pNode->fPPsiOx = spGetElement( matrix, pEqn, psiEqnOxP );
}
}
} /* endfor nIndex */
pElem = pElem->pElems[ nextIndex ];
} /* endwhile pElem */
} /* endfor pCh */
} /* endif SurfaceMobility */
}
/*
* The Jacobian and Rhs are loaded by the following function.
* Inputs are the transient analysis flag and the transient
* information structure
*/
void
TWO_sysLoad(TWOdevice *pDevice, BOOLEAN tranAnalysis, TWOtranInfo *info)
{
TWOelem *pElem;
TWOnode *pNode;
TWOedge *pHEdge, *pVEdge;
TWOedge *pTEdge, *pBEdge, *pLEdge, *pREdge;
TWOchannel *pCh;
int index, eIndex;
int nextIndex; /* index of node to find next element */
double *pRhs = pDevice->rhs;
double dx, dy, dxdy, dyOverDx, dxOverDy;
double ds;
double dPsiT, dPsiB, dPsiL, dPsiR;
double rhsN, rhsP;
double generation, TWOavalanche();
double nConc, pConc;
double perTime = 0.0;
/* first compute the currents and derivatives */
TWO_commonTerms( pDevice, FALSE, tranAnalysis, info );
/* find reciprocal timestep */
if ( tranAnalysis ) {
perTime = info->intCoeff[0];
}
/* zero the rhs vector */
for ( index = 1 ; index <= pDevice->numEqns ; index++ ) {
pRhs[ index ] = 0.0;
}
/* zero the matrix */
spClear( pDevice->matrix );
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
pElem = pDevice->elements[ eIndex ];
dx = 0.5 * pElem->dx;
dy = 0.5 * pElem->dy;
dxdy = dx * dy;
dxOverDy = 0.5 * pElem->epsRel * pElem->dxOverDy;
dyOverDx = 0.5 * pElem->epsRel * pElem->dyOverDx;
pTEdge = pElem->pTopEdge;
pBEdge = pElem->pBotEdge;
pLEdge = pElem->pLeftEdge;
pREdge = pElem->pRightEdge;
dPsiT = pTEdge->dPsi;
dPsiB = pBEdge->dPsi;
dPsiL = pLEdge->dPsi;
dPsiR = pREdge->dPsi;
/* load for all i,j */
for ( index = 0; index <= 3; index++ ) {
pNode = pElem->pNodes[ index ];
if ( pNode->nodeType != CONTACT ) {
*(pNode->fPsiPsi) += dyOverDx + dxOverDy;
if ( index <= 1 ) {
pHEdge = pTEdge;
} else {
pHEdge = pBEdge;
}
if ( index == 0 || index == 3 ) {
pVEdge = pLEdge;
} else {
pVEdge = pREdge;
}
/* Add surface state charges. */
pRhs[ pNode->psiEqn ] += dx * pHEdge->qf;
pRhs[ pNode->psiEqn ] += dy * pVEdge->qf;
if ( pElem->elemType == SEMICON ) {
*(pNode->fPsiN) += dxdy;
*(pNode->fPsiP) -= dxdy;
*(pNode->fNPsi) -= dy * pHEdge->dJnDpsiP1 +
dx * pVEdge->dJnDpsiP1;
*(pNode->fPPsi) -= dy * pHEdge->dJpDpsiP1 +
dx * pVEdge->dJpDpsiP1;
nConc = *(pDevice->devState0 + pNode->nodeN);
pConc = *(pDevice->devState0 + pNode->nodeP);
pRhs[ pNode->psiEqn ] += dxdy * (pNode->netConc + pConc - nConc);
/* Handle generation terms */
*(pNode->fNN) -= dxdy * pNode->dUdN;
*(pNode->fNP) -= dxdy * pNode->dUdP;
*(pNode->fPP) += dxdy * pNode->dUdP;
*(pNode->fPN) += dxdy * pNode->dUdN;
rhsN = - dxdy * pNode->uNet;
rhsP = dxdy * pNode->uNet;
if ( AvalancheGen ) {
generation = TWOavalanche( pElem, pNode );
rhsN += dxdy * generation;
rhsP -= dxdy * generation;
}
pRhs[ pNode->nEqn ] -= rhsN;
pRhs[ pNode->pEqn ] -= rhsP;
/* Handle dXdT continuity terms */
if ( tranAnalysis ) {
*(pNode->fNN) -= dxdy * perTime;
*(pNode->fPP) += dxdy * perTime;
pRhs[ pNode->nEqn ] += dxdy * pNode->dNdT;
pRhs[ pNode->pEqn ] -= dxdy * pNode->dPdT;
}
}
}
}
/* Handle neighbor and edge dependent terms */
pNode = pElem->pTLNode;
if ( pNode->nodeType != CONTACT ) {
pRhs[ pNode->psiEqn ] -= -dyOverDx * dPsiT - dxOverDy * dPsiL;
*(pNode->fPsiPsiiP1) -= dyOverDx;
*(pNode->fPsiPsijP1) -= dxOverDy;
if ( pElem->elemType == SEMICON ) {
pRhs[ pNode->nEqn ] -= dy * pTEdge->jn + dx * pLEdge->jn;
pRhs[ pNode->pEqn ] -= dy * pTEdge->jp + dx * pLEdge->jp;
*(pNode->fNN) += dy * pTEdge->dJnDn + dx * pLEdge->dJnDn;
*(pNode->fPP) += dy * pTEdge->dJpDp + dx * pLEdge->dJpDp;
*(pNode->fNPsiiP1) += dy * pTEdge->dJnDpsiP1;
*(pNode->fNNiP1) += dy * pTEdge->dJnDnP1;
*(pNode->fPPsiiP1) += dy * pTEdge->dJpDpsiP1;
*(pNode->fPPiP1) += dy * pTEdge->dJpDpP1;
*(pNode->fNPsijP1) += dx * pLEdge->dJnDpsiP1;
*(pNode->fNNjP1) += dx * pLEdge->dJnDnP1;
*(pNode->fPPsijP1) += dx * pLEdge->dJpDpsiP1;
*(pNode->fPPjP1) += dx * pLEdge->dJpDpP1;
}
}
pNode = pElem->pTRNode;
if ( pNode->nodeType != CONTACT ) {
pRhs[ pNode->psiEqn ] -= dyOverDx * dPsiT - dxOverDy * dPsiR;
*(pNode->fPsiPsiiM1) -= dyOverDx;
*(pNode->fPsiPsijP1) -= dxOverDy;
if ( pElem->elemType == SEMICON ) {
pRhs[ pNode->nEqn ] -= -dy * pTEdge->jn + dx * pREdge->jn;
pRhs[ pNode->pEqn ] -= -dy * pTEdge->jp + dx * pREdge->jp;
*(pNode->fNN) += -dy * pTEdge->dJnDnP1 + dx * pREdge->dJnDn;
*(pNode->fPP) += -dy * pTEdge->dJpDpP1 + dx * pREdge->dJpDp;
*(pNode->fNPsiiM1) += dy * pTEdge->dJnDpsiP1;
*(pNode->fNNiM1) -= dy * pTEdge->dJnDn;
*(pNode->fPPsiiM1) += dy * pTEdge->dJpDpsiP1;
*(pNode->fPPiM1) -= dy * pTEdge->dJpDp;
*(pNode->fNPsijP1) += dx * pREdge->dJnDpsiP1;
*(pNode->fNNjP1) += dx * pREdge->dJnDnP1;
*(pNode->fPPsijP1) += dx * pREdge->dJpDpsiP1;
*(pNode->fPPjP1) += dx * pREdge->dJpDpP1;
}
}
pNode = pElem->pBRNode;
if ( pNode->nodeType != CONTACT ) {
pRhs[ pNode->psiEqn ] -= dyOverDx * dPsiB + dxOverDy * dPsiR;
*(pNode->fPsiPsiiM1) -= dyOverDx;
*(pNode->fPsiPsijM1) -= dxOverDy;
if ( pElem->elemType == SEMICON ) {
pRhs[ pNode->nEqn ] -= -dy * pBEdge->jn - dx * pREdge->jn;
pRhs[ pNode->pEqn ] -= -dy * pBEdge->jp - dx * pREdge->jp;
*(pNode->fNN) += -dy * pBEdge->dJnDnP1 - dx * pREdge->dJnDnP1;
*(pNode->fPP) += -dy * pBEdge->dJpDpP1 - dx * pREdge->dJpDpP1;
*(pNode->fNPsiiM1) += dy * pBEdge->dJnDpsiP1;
*(pNode->fNNiM1) -= dy * pBEdge->dJnDn;
*(pNode->fPPsiiM1) += dy * pBEdge->dJpDpsiP1;
*(pNode->fPPiM1) -= dy * pBEdge->dJpDp;
*(pNode->fNPsijM1) += dx * pREdge->dJnDpsiP1;
*(pNode->fNNjM1) -= dx * pREdge->dJnDn;
*(pNode->fPPsijM1) += dx * pREdge->dJpDpsiP1;
*(pNode->fPPjM1) -= dx * pREdge->dJpDp;
}
}
pNode = pElem->pBLNode;
if ( pNode->nodeType != CONTACT ) {
pRhs[ pNode->psiEqn ] -= -dyOverDx * dPsiB + dxOverDy * dPsiL;
*(pNode->fPsiPsiiP1) -= dyOverDx;
*(pNode->fPsiPsijM1) -= dxOverDy;
if ( pElem->elemType == SEMICON ) {
pRhs[ pNode->nEqn ] -= dy * pBEdge->jn - dx * pLEdge->jn;
pRhs[ pNode->pEqn ] -= dy * pBEdge->jp - dx * pLEdge->jp;
*(pNode->fNN) += dy * pBEdge->dJnDn - dx * pLEdge->dJnDnP1;
*(pNode->fPP) += dy * pBEdge->dJpDp - dx * pLEdge->dJpDpP1;
*(pNode->fNPsiiP1) += dy * pBEdge->dJnDpsiP1;
*(pNode->fNNiP1) += dy * pBEdge->dJnDnP1;
*(pNode->fPPsiiP1) += dy * pBEdge->dJpDpsiP1;
*(pNode->fPPiP1) += dy * pBEdge->dJpDpP1;
*(pNode->fNPsijM1) += dx * pLEdge->dJnDpsiP1;
*(pNode->fNNjM1) -= dx * pLEdge->dJnDn;
*(pNode->fPPsijM1) += dx * pLEdge->dJpDpsiP1;
*(pNode->fPPjM1) -= dx * pLEdge->dJpDp;
}
}
}
/* Calculate the Inversion-Layer Mobility Dependent Terms in Jac. */
if ( MobDeriv && SurfaceMobility ) {
for ( pCh = pDevice->pChannel; pCh != NIL(TWOchannel);
pCh = pCh->next ) {
/* Find effective height of oxide element at interface. */
if ( pCh->type%2 == 0 ) { /* Vertical slice */
ds = pCh->pNElem->dy / pCh->pNElem->epsRel;
} else { /* Horizontal slice */
ds = pCh->pNElem->dx / pCh->pNElem->epsRel;
}
pElem = pCh->pSeed;
nextIndex = (pCh->type + 2)%4;
while (pElem && pElem->channel == pCh->id) {
TWO_mobDeriv( pElem, pCh->type, ds );
pElem = pElem->pElems[ nextIndex ];
}
} /* endfor pCh != NIL */
} /* endif MobDeriv and SurfaceMobility */
}
/* this function used only for direct method ac analysis
Used to load only the dc Jacobian matrix. Rhs is unaffected
*/
void
TWO_jacLoad(TWOdevice *pDevice)
{
TWOelem *pElem;
TWOnode *pNode;
TWOedge *pHEdge, *pVEdge;
TWOedge *pTEdge, *pBEdge, *pLEdge, *pREdge;
TWOchannel *pCh;
int index, eIndex;
int nextIndex; /* index of node to find next element */
double dx, dy, dxdy, dyOverDx, dxOverDy;
double ds;
void spClear(), TWO_commonTerms();
/* first compute the currents and derivatives */
TWO_commonTerms( pDevice, FALSE, FALSE, NIL(TWOtranInfo) );
/* zero the matrix */
spClear( pDevice->matrix );
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
pElem = pDevice->elements[ eIndex ];
dx = 0.5 * pElem->dx;
dy = 0.5 * pElem->dy;
dxdy = dx * dy;
dxOverDy = 0.5 * pElem->epsRel * pElem->dxOverDy;
dyOverDx = 0.5 * pElem->epsRel * pElem->dyOverDx;
pTEdge = pElem->pTopEdge;
pBEdge = pElem->pBotEdge;
pLEdge = pElem->pLeftEdge;
pREdge = pElem->pRightEdge;
/* load for all i,j */
for ( index = 0; index <= 3; index++ ) {
pNode = pElem->pNodes[ index ];
if ( pNode->nodeType != CONTACT ) {
*(pNode->fPsiPsi) += dyOverDx + dxOverDy;
if ( pElem->elemType == SEMICON ) {
if ( index <= 1 ) {
pHEdge = pTEdge;
} else {
pHEdge = pBEdge;
}
if ( index == 0 || index == 3 ) {
pVEdge = pLEdge;
} else {
pVEdge = pREdge;
}
*(pNode->fPsiN) += dxdy;
*(pNode->fPsiP) -= dxdy;
*(pNode->fNPsi) -= dy * pHEdge->dJnDpsiP1 +
dx * pVEdge->dJnDpsiP1;
*(pNode->fPPsi) -= dy * pHEdge->dJpDpsiP1 +
dx * pVEdge->dJpDpsiP1;
/* Handle generation terms */
*(pNode->fNN) -= dxdy * pNode->dUdN;
*(pNode->fNP) -= dxdy * pNode->dUdP;
*(pNode->fPP) += dxdy * pNode->dUdP;
*(pNode->fPN) += dxdy * pNode->dUdN;
}
}
}
/* Handle neighbor and edge dependent terms */
pNode = pElem->pTLNode;
if ( pNode->nodeType != CONTACT ) {
*(pNode->fPsiPsiiP1) -= dyOverDx;
*(pNode->fPsiPsijP1) -= dxOverDy;
if ( pElem->elemType == SEMICON ) {
*(pNode->fNN) += dy * pTEdge->dJnDn + dx * pLEdge->dJnDn;
*(pNode->fPP) += dy * pTEdge->dJpDp + dx * pLEdge->dJpDp;
*(pNode->fNPsiiP1) += dy * pTEdge->dJnDpsiP1;
*(pNode->fNNiP1) += dy * pTEdge->dJnDnP1;
*(pNode->fPPsiiP1) += dy * pTEdge->dJpDpsiP1;
*(pNode->fPPiP1) += dy * pTEdge->dJpDpP1;
*(pNode->fNPsijP1) += dx * pLEdge->dJnDpsiP1;
*(pNode->fNNjP1) += dx * pLEdge->dJnDnP1;
*(pNode->fPPsijP1) += dx * pLEdge->dJpDpsiP1;
*(pNode->fPPjP1) += dx * pLEdge->dJpDpP1;
}
}
pNode = pElem->pTRNode;
if ( pNode->nodeType != CONTACT ) {
*(pNode->fPsiPsiiM1) -= dyOverDx;
*(pNode->fPsiPsijP1) -= dxOverDy;
if ( pElem->elemType == SEMICON ) {
*(pNode->fNN) += -dy * pTEdge->dJnDnP1 + dx * pREdge->dJnDn;
*(pNode->fPP) += -dy * pTEdge->dJpDpP1 + dx * pREdge->dJpDp;
*(pNode->fNPsiiM1) += dy * pTEdge->dJnDpsiP1;
*(pNode->fNNiM1) -= dy * pTEdge->dJnDn;
*(pNode->fPPsiiM1) += dy * pTEdge->dJpDpsiP1;
*(pNode->fPPiM1) -= dy * pTEdge->dJpDp;
*(pNode->fNPsijP1) += dx * pREdge->dJnDpsiP1;
*(pNode->fNNjP1) += dx * pREdge->dJnDnP1;
*(pNode->fPPsijP1) += dx * pREdge->dJpDpsiP1;
*(pNode->fPPjP1) += dx * pREdge->dJpDpP1;
}
}
pNode = pElem->pBRNode;
if ( pNode->nodeType != CONTACT ) {
*(pNode->fPsiPsiiM1) -= dyOverDx;
*(pNode->fPsiPsijM1) -= dxOverDy;
if ( pElem->elemType == SEMICON ) {
*(pNode->fNN) += -dy * pBEdge->dJnDnP1 - dx * pREdge->dJnDnP1;
*(pNode->fPP) += -dy * pBEdge->dJpDpP1 - dx * pREdge->dJpDpP1;
*(pNode->fNPsiiM1) += dy * pBEdge->dJnDpsiP1;
*(pNode->fNNiM1) -= dy * pBEdge->dJnDn;
*(pNode->fPPsiiM1) += dy * pBEdge->dJpDpsiP1;
*(pNode->fPPiM1) -= dy * pBEdge->dJpDp;
*(pNode->fNPsijM1) += dx * pREdge->dJnDpsiP1;
*(pNode->fNNjM1) -= dx * pREdge->dJnDn;
*(pNode->fPPsijM1) += dx * pREdge->dJpDpsiP1;
*(pNode->fPPjM1) -= dx * pREdge->dJpDp;
}
}
pNode = pElem->pBLNode;
if ( pNode->nodeType != CONTACT ) {
*(pNode->fPsiPsiiP1) -= dyOverDx;
*(pNode->fPsiPsijM1) -= dxOverDy;
if ( pElem->elemType == SEMICON ) {
*(pNode->fNN) += dy * pBEdge->dJnDn - dx * pLEdge->dJnDnP1;
*(pNode->fPP) += dy * pBEdge->dJpDp - dx * pLEdge->dJpDpP1;
*(pNode->fNPsiiP1) += dy * pBEdge->dJnDpsiP1;
*(pNode->fNNiP1) += dy * pBEdge->dJnDnP1;
*(pNode->fPPsiiP1) += dy * pBEdge->dJpDpsiP1;
*(pNode->fPPiP1) += dy * pBEdge->dJpDpP1;
*(pNode->fNPsijM1) += dx * pLEdge->dJnDpsiP1;
*(pNode->fNNjM1) -= dx * pLEdge->dJnDn;
*(pNode->fPPsijM1) += dx * pLEdge->dJpDpsiP1;
*(pNode->fPPjM1) -= dx * pLEdge->dJpDp;
}
}
}
/* Calculate the Inversion-Layer Mobility Dependent Terms in Jac. */
if ( MobDeriv && SurfaceMobility ) {
for ( pCh = pDevice->pChannel; pCh != NIL(TWOchannel);
pCh = pCh->next ) {
/* Find effective height of oxide element at interface. */
if ( pCh->type%2 == 0 ) { /* Vertical slice */
ds = pCh->pNElem->dy / pCh->pNElem->epsRel;
} else { /* Horizontal slice */
ds = pCh->pNElem->dx / pCh->pNElem->epsRel;
}
pElem = pCh->pSeed;
nextIndex = (pCh->type + 2)%4;
while (pElem && pElem->channel == pCh->id) {
TWO_mobDeriv( pElem, pCh->type, ds );
pElem = pElem->pElems[ nextIndex ];
}
} /* endfor pCh != NIL */
} /* endif MobDeriv and SurfaceMobility */
}
/* load only the Rhs vector */
void
TWO_rhsLoad(TWOdevice *pDevice, BOOLEAN tranAnalysis, TWOtranInfo *info)
{
TWOelem *pElem;
TWOnode *pNode;
TWOedge *pHEdge, *pVEdge;
TWOedge *pTEdge, *pBEdge, *pLEdge, *pREdge;
int index, eIndex;
double *pRhs = pDevice->rhs;
double dx, dy, dxdy, dyOverDx, dxOverDy;
double dPsiT, dPsiB, dPsiL, dPsiR;
double rhsN, rhsP;
double generation, TWOavalanche();
double nConc, pConc;
double perTime;
void TWO_commonTerms();
/* first compute the currents */
TWO_commonTerms( pDevice, TRUE, tranAnalysis, info );
/* find reciprocal timestep */
if ( tranAnalysis ) {
perTime = info->intCoeff[0];
}
/* zero the rhs vector */
for ( index = 1 ; index <= pDevice->numEqns ; index++ ) {
pRhs[ index ] = 0.0;
}
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
pElem = pDevice->elements[ eIndex ];
dx = 0.5 * pElem->dx;
dy = 0.5 * pElem->dy;
dxdy = dx * dy;
dxOverDy = 0.5 * pElem->epsRel * pElem->dxOverDy;
dyOverDx = 0.5 * pElem->epsRel * pElem->dyOverDx;
pTEdge = pElem->pTopEdge;
pBEdge = pElem->pBotEdge;
pLEdge = pElem->pLeftEdge;
pREdge = pElem->pRightEdge;
dPsiT = pTEdge->dPsi;
dPsiB = pBEdge->dPsi;
dPsiL = pLEdge->dPsi;
dPsiR = pREdge->dPsi;
/* load for all i,j */
for ( index = 0; index <= 3; index++ ) {
pNode = pElem->pNodes[ index ];
if ( pNode->nodeType != CONTACT ) {
if ( index <= 1 ) {
pHEdge = pTEdge;
} else {
pHEdge = pBEdge;
}
if ( index == 0 || index == 3 ) {
pVEdge = pLEdge;
} else {
pVEdge = pREdge;
}
/* Add surface state charges. */
pRhs[ pNode->psiEqn ] += dx * pHEdge->qf;
pRhs[ pNode->psiEqn ] += dy * pVEdge->qf;
if ( pElem->elemType == SEMICON ) {
nConc = *(pDevice->devState0 + pNode->nodeN);
pConc = *(pDevice->devState0 + pNode->nodeP);
pRhs[ pNode->psiEqn ] += dxdy * (pNode->netConc + pConc - nConc);
/* Handle generation terms */
rhsN = - dxdy * pNode->uNet;
rhsP = dxdy * pNode->uNet;
if ( AvalancheGen ) {
generation = TWOavalanche( pElem, pNode );
rhsN += dxdy * generation;
rhsP -= dxdy * generation;
}
pRhs[ pNode->nEqn ] -= rhsN;
pRhs[ pNode->pEqn ] -= rhsP;
/* Handle dXdT continuity terms */
if ( tranAnalysis ) {
pRhs[ pNode->nEqn ] += dxdy * pNode->dNdT;
pRhs[ pNode->pEqn ] -= dxdy * pNode->dPdT;
}
}
}
}
/* Handle neighbor and edge dependent terms */
pNode = pElem->pTLNode;
if ( pNode->nodeType != CONTACT ) {
pRhs[ pNode->psiEqn ] -= -dyOverDx * dPsiT - dxOverDy * dPsiL;
if ( pElem->elemType == SEMICON ) {
pRhs[ pNode->nEqn ] -= dy * pTEdge->jn + dx * pLEdge->jn;
pRhs[ pNode->pEqn ] -= dy * pTEdge->jp + dx * pLEdge->jp;
}
}
pNode = pElem->pTRNode;
if ( pNode->nodeType != CONTACT ) {
pRhs[ pNode->psiEqn ] -= dyOverDx * dPsiT - dxOverDy * dPsiR;
if ( pElem->elemType == SEMICON ) {
pRhs[ pNode->nEqn ] -= -dy * pTEdge->jn + dx * pREdge->jn;
pRhs[ pNode->pEqn ] -= -dy * pTEdge->jp + dx * pREdge->jp;
}
}
pNode = pElem->pBRNode;
if ( pNode->nodeType != CONTACT ) {
pRhs[ pNode->psiEqn ] -= dyOverDx * dPsiB + dxOverDy * dPsiR;
if ( pElem->elemType == SEMICON ) {
pRhs[ pNode->nEqn ] -= -dy * pBEdge->jn - dx * pREdge->jn;
pRhs[ pNode->pEqn ] -= -dy * pBEdge->jp - dx * pREdge->jp;
}
}
pNode = pElem->pBLNode;
if ( pNode->nodeType != CONTACT ) {
pRhs[ pNode->psiEqn ] -= -dyOverDx * dPsiB + dxOverDy * dPsiL;
if ( pElem->elemType == SEMICON ) {
pRhs[ pNode->nEqn ] -= dy * pBEdge->jn - dx * pLEdge->jn;
pRhs[ pNode->pEqn ] -= dy * pBEdge->jp - dx * pLEdge->jp;
}
}
}
}
/*
* computation of current densities, recombination rates,
* mobilities and their derivatives
*/
void
TWO_commonTerms(TWOdevice *pDevice, BOOLEAN currentOnly,
BOOLEAN tranAnalysis, TWOtranInfo *info)
{
TWOelem *pElem;
TWOedge *pEdge;
TWOnode *pNode;
int index, eIndex;
int nextIndex; /* index of node to find next element */
double psi1, psi2, nC, pC, nP1, pP1;
double dPsiN, dPsiP;
double bPsiN, dbPsiN, bMPsiN, dbMPsiN;
double bPsiP, dbPsiP, bMPsiP, dbMPsiP;
double muN, dMuN, muP, dMuP, rDx, rDy;
double psi, nConc = 0.0, pConc = 0.0;
double cnAug, cpAug;
double eSurf = 0.0; /* For channel mobilities */
double qInt = 0.0;
TWOchannel *pCh;
/* evaluate all node (including recombination) and edge quantities */
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
pElem = pDevice->elements[ eIndex ];
cnAug = pElem->matlInfo->cAug[ELEC];
cpAug = pElem->matlInfo->cAug[HOLE];
for ( index = 0; index <= 3; index++ ) {
if ( pElem->evalNodes[ index ] ) {
pNode = pElem->pNodes[ index ];
if ( pNode->nodeType != CONTACT ) {
psi = pDevice->dcSolution[ pNode->psiEqn ];
if ( pElem->elemType == SEMICON ) {
nConc = pDevice->dcSolution[ pNode->nEqn ];
pConc = pDevice->dcSolution[ pNode->pEqn ];
if ( Srh ) {
recomb(nConc, pConc,
pNode->tn, pNode->tp, cnAug, cpAug, pNode->nie,
&pNode->uNet, &pNode->dUdN, &pNode->dUdP);
} else {
pNode->uNet = 0.0;
pNode->dUdN = 0.0;
pNode->dUdP = 0.0;
}
}
} else {
/* a contact node */
psi = pNode->psi;
if ( pElem->elemType == SEMICON ) {
nConc = pNode->nConc;
pConc = pNode->pConc;
}
}
/* store info in the state tables */
*(pDevice->devState0 + pNode->nodePsi) = psi;
if ( pElem->elemType == SEMICON ) {
*(pDevice->devState0 + pNode->nodeN) = nConc;
*(pDevice->devState0 + pNode->nodeP) = pConc;
if ( tranAnalysis && (pNode->nodeType != CONTACT) ) {
pNode->dNdT = integrate( pDevice->devStates, info, pNode->nodeN );
pNode->dPdT = integrate( pDevice->devStates, info, pNode->nodeP );
}
}
}
}
for ( index = 0; index <= 3; index++ ) {
if ( pElem->evalEdges[ index ] ) {
pEdge = pElem->pEdges[ index ];
pNode = pElem->pNodes[ index ];
if ( pNode->nodeType != CONTACT ) {
psi1 = pDevice->dcSolution[pNode->psiEqn];
} else {
psi1 = pNode->psi;
}
pNode = pElem->pNodes[ (index + 1) % 4 ];
if ( pNode->nodeType != CONTACT ) {
psi2 = pDevice->dcSolution[pNode->psiEqn];
} else {
psi2 = pNode->psi;
}
if ( index <= 1 ) {
pEdge->dPsi = psi2 - psi1;
} else {
pEdge->dPsi = psi1 - psi2;
}
*(pDevice->devState0 + pEdge->edgeDpsi) = pEdge->dPsi;
if ( pElem->elemType == SEMICON ) {
/* Calculate weighted driving forces - wdfn & wdfp for the edge */
dPsiN = pEdge->dPsi + pEdge->dCBand;
dPsiP = pEdge->dPsi - pEdge->dVBand;
bernoulli( dPsiN, &bPsiN, &dbPsiN,
&bMPsiN, &dbMPsiN, !currentOnly );
bernoulli( dPsiP, &bPsiP, &dbPsiP,
&bMPsiP, &dbMPsiP, !currentOnly);
if ( index <= 1 ) {
nC = *(pDevice->devState0 + pElem->pNodes[ index ]->nodeN);
nP1 = *(pDevice->devState0 + pElem->pNodes[ index+1 ]->nodeN);
pC = *(pDevice->devState0 + pElem->pNodes[ index ]->nodeP);
pP1 = *(pDevice->devState0 + pElem->pNodes[ index+1 ]->nodeP);
} else {
nC = *(pDevice->devState0 + pElem->pNodes[(index+1)%4]->nodeN);
nP1 = *(pDevice->devState0 + pElem->pNodes[ index ]->nodeN);
pC = *(pDevice->devState0 + pElem->pNodes[(index+1)%4]->nodeP);
pP1 = *(pDevice->devState0 + pElem->pNodes[ index ]->nodeP);
}
pEdge->wdfn = bPsiN * nP1 - bMPsiN * nC;
pEdge->wdfp = bPsiP * pC - bMPsiP * pP1;
pEdge->jn = 0.0;
pEdge->jp = 0.0;
if ( !currentOnly ) {
pEdge->dWnDpsiP1 = dbPsiN * nP1 - dbMPsiN * nC;
pEdge->dWnDn = - bMPsiN;
pEdge->dWnDnP1 = bPsiN;
pEdge->dWpDpsiP1 = dbPsiP * pC - dbMPsiP * pP1;
pEdge->dWpDp = bPsiP;
pEdge->dWpDpP1 = - bMPsiP;
pEdge->dJnDpsiP1 = 0.0;
pEdge->dJnDn = 0.0;
pEdge->dJnDnP1 = 0.0;
pEdge->dJpDpsiP1 = 0.0;
pEdge->dJpDp = 0.0;
pEdge->dJpDpP1 = 0.0;
}
}
}
}
}
/* DAG: calculate mobilities for channel elems */
if ( SurfaceMobility ) {
for ( pCh = pDevice->pChannel;
pCh != NIL(TWOchannel); pCh = pCh->next ) {
pElem = pCh->pNElem;
switch (pCh->type) {
case 0:
eSurf = - 0.5 * (pElem->pLeftEdge->dPsi + pElem->pRightEdge->dPsi )
* pElem->epsRel / pElem->dy;
qInt = 0.5 * pElem->pBotEdge->qf;
break;
case 1:
eSurf = - 0.5 * (pElem->pTopEdge->dPsi + pElem->pBotEdge->dPsi )
* pElem->epsRel / pElem->dx;
qInt = 0.5 * pElem->pLeftEdge->qf;
break;
case 2:
eSurf = - 0.5 * (pElem->pLeftEdge->dPsi + pElem->pRightEdge->dPsi )
* pElem->epsRel / pElem->dy;
qInt = 0.5 * pElem->pTopEdge->qf;
break;
case 3:
eSurf = - 0.5 * (pElem->pTopEdge->dPsi + pElem->pBotEdge->dPsi )
* pElem->epsRel / pElem->dx;
qInt = 0.5 * pElem->pRightEdge->qf;
break;
}
eSurf += qInt;
pElem = pCh->pSeed;
nextIndex = (pCh->type + 2)%4;
while (pElem && pElem->channel == pCh->id) {
TWO_mobility( pElem, eSurf );
pElem = pElem->pElems[ nextIndex ];
}
} /* endfor pCH != NIL */
} /* endif SurfaceMobility */
/* calculate the current densities assuming mobility value depend on Ex,Ey*/
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
pElem = pDevice->elements[ eIndex ];
rDx = 1.0 / pElem->dx;
rDy = 1.0 / pElem->dy;
for ( index = 0; index <= 3; index++ ) {
pEdge = pElem->pEdges[ index ];
/* calculate conductive currents */
if ( pElem->elemType == SEMICON ) {
/* get mobility for this edge */
if ( !pElem->channel ) {
/* Calculate mobility for non-channel elements */
muN = pElem->mun0;
dMuN = 0.0;
muP = pElem->mup0;
dMuP = 0.0;
dPsiN = pEdge->dPsi + pEdge->dCBand;
dPsiP = pEdge->dPsi - pEdge->dVBand;
if ( index%2 == 0 ) {
MOBfieldDep( pElem->matlInfo, ELEC, - dPsiN * rDx, &muN, &dMuN );
MOBfieldDep( pElem->matlInfo, HOLE, - dPsiP * rDx, &muP, &dMuP );
} else {
MOBfieldDep( pElem->matlInfo, ELEC, - dPsiN * rDy, &muN, &dMuN );
MOBfieldDep( pElem->matlInfo, HOLE, - dPsiP * rDy, &muP, &dMuP );
}
} else {
/* Retrieve previously calculated value. */
muN = pElem->mun;
dMuN = 0.0;
muP = pElem->mup;
dMuP = 0.0;
}
switch ( index ) {
case 0:
muN *= pEdge->kPos * rDx;
dMuN *= pEdge->kPos * rDx * rDx;
muP *= pEdge->kPos * rDx;
dMuP *= pEdge->kPos * rDx * rDx;
break;
case 1:
muN *= pEdge->kNeg * rDy;
dMuN *= pEdge->kNeg * rDy * rDy;
muP *= pEdge->kNeg * rDy;
dMuP *= pEdge->kNeg * rDy * rDy;
break;
case 2:
muN *= pEdge->kNeg * rDx;
dMuN *= pEdge->kNeg * rDx * rDx;
muP *= pEdge->kNeg * rDx;
dMuP *= pEdge->kNeg * rDx * rDx;
break;
case 3:
muN *= pEdge->kPos * rDy;
dMuN *= pEdge->kPos * rDy * rDy;
muP *= pEdge->kPos * rDy;
dMuP *= pEdge->kPos * rDy * rDy;
break;
}
/* Now that the mobility for this edge is known, do current */
pEdge->jn += muN * pEdge->wdfn;
pEdge->jp += muP * pEdge->wdfp;
if ( !currentOnly ) {
pEdge->dJnDpsiP1 += muN * pEdge->dWnDpsiP1;
pEdge->dJnDn += muN * pEdge->dWnDn;
pEdge->dJnDnP1 += muN * pEdge->dWnDnP1;
pEdge->dJpDpsiP1 += muP * pEdge->dWpDpsiP1;
pEdge->dJpDp += muP * pEdge->dWpDp;
pEdge->dJpDpP1 += muP * pEdge->dWpDpP1;
if ( MobDeriv && (!pElem->channel) ) {
pEdge->dJnDpsiP1 -= dMuN * pEdge->wdfn;
pEdge->dJpDpsiP1 -= dMuP * pEdge->wdfp;
}
}
}
/* calculate displacement current only once */
if ( pElem->evalEdges[ index ] ) {
if ( tranAnalysis ) {
if ( index == 0 || index == 2 ) {
/* horizontal edges */
pEdge->jd = -integrate(pDevice->devStates, info,
pEdge->edgeDpsi) * rDx;
} else {
/* vertical edges */
pEdge->jd = -integrate(pDevice->devStates, info,
pEdge->edgeDpsi) * rDy;
}
}
}
}
}
}