258 lines
7.8 KiB
C
258 lines
7.8 KiB
C
/**********
|
|
Copyright 1992 Regents of the University of California. All rights reserved.
|
|
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group
|
|
**********/
|
|
|
|
/* Functions to compute device conductances and currents */
|
|
|
|
#include "ngspice/ngspice.h"
|
|
#include "ngspice/numglobs.h"
|
|
#include "ngspice/numenum.h"
|
|
#include "ngspice/onemesh.h"
|
|
#include "ngspice/onedev.h"
|
|
#include "ngspice/macros.h"
|
|
#include "ngspice/spmatrix.h"
|
|
|
|
#include "onedext.h"
|
|
#include "oneddefs.h"
|
|
|
|
void
|
|
NUMDconductance(ONEdevice *pDevice, bool tranAnalysis,
|
|
double *intCoeff, double *gd)
|
|
{
|
|
ONEelem *pElem = pDevice->elemArray[pDevice->numNodes - 1];
|
|
ONEnode *pNode;
|
|
ONEedge *pEdge;
|
|
int index;
|
|
double dPsiDv, dNDv, dPDv, *incVpn;
|
|
|
|
|
|
*gd = 0.0;
|
|
|
|
/* zero the rhs before loading in the new rhs */
|
|
for (index = 1; index <= pDevice->numEqns; index++) {
|
|
pDevice->rhs[index] = 0.0;
|
|
}
|
|
/* compute incremental changes due to N contact */
|
|
pNode = pElem->pLeftNode;
|
|
pDevice->rhs[pNode->psiEqn] = pElem->epsRel * pElem->rDx;
|
|
if (pElem->elemType == SEMICON) {
|
|
pEdge = pElem->pEdge;
|
|
pDevice->rhs[pNode->nEqn] = -pEdge->dJnDpsiP1;
|
|
pDevice->rhs[pNode->pEqn] = -pEdge->dJpDpsiP1;
|
|
}
|
|
incVpn = pDevice->dcDeltaSolution;
|
|
|
|
#ifdef KLU
|
|
SMPsolveKLUforCIDER (pDevice->matrix, pDevice->rhs, incVpn, NULL, NULL) ;
|
|
#else
|
|
SMPsolveForCIDER (pDevice->matrix, pDevice->rhs, incVpn) ;
|
|
#endif
|
|
|
|
pElem = pDevice->elemArray[1];
|
|
pNode = pElem->pRightNode;
|
|
pEdge = pElem->pEdge;
|
|
dPsiDv = incVpn[pNode->psiEqn];
|
|
if (pElem->elemType == SEMICON) {
|
|
dNDv = incVpn[pNode->nEqn];
|
|
dPDv = incVpn[pNode->pEqn];
|
|
*gd += pEdge->dJnDpsiP1 * dPsiDv + pEdge->dJnDnP1 * dNDv +
|
|
pEdge->dJpDpsiP1 * dPsiDv + pEdge->dJpDpP1 * dPDv;
|
|
}
|
|
/* For transient analysis, add the displacement term */
|
|
if (tranAnalysis) {
|
|
*gd -= intCoeff[0] * pElem->epsRel * pElem->rDx * dPsiDv;
|
|
}
|
|
*gd *= -GNorm * pDevice->area;
|
|
|
|
}
|
|
|
|
void
|
|
NBJTconductance(ONEdevice *pDevice, bool tranAnalysis, double *intCoeff,
|
|
double *dIeDVce, double *dIcDVce, double *dIeDVbe, double *dIcDVbe)
|
|
{
|
|
ONEelem *pLastElem = pDevice->elemArray[pDevice->numNodes - 1];
|
|
ONEelem *pBaseElem = pDevice->elemArray[pDevice->baseIndex - 1];
|
|
ONEelem *pElem;
|
|
ONEnode *pNode;
|
|
ONEedge *pEdge;
|
|
int index;
|
|
double dPsiDVce, dPsiDVbe, dNDVce, dNDVbe, dPDVce, dPDVbe;
|
|
double *incVce, *incVbe;
|
|
double nConc, pConc;
|
|
double area = pDevice->area;
|
|
|
|
*dIeDVce = 0.0;
|
|
*dIcDVce = 0.0;
|
|
*dIeDVbe = 0.0;
|
|
*dIcDVbe = 0.0;
|
|
|
|
/* zero the rhs before loading in the new rhs */
|
|
for (index = 1; index <= pDevice->numEqns; index++) {
|
|
pDevice->rhs[index] = 0.0;
|
|
}
|
|
/* store the new rhs for computing CE incremental quantities */
|
|
pNode = pLastElem->pLeftNode;
|
|
pDevice->rhs[pNode->psiEqn] = pLastElem->epsRel * pLastElem->rDx;
|
|
if (pLastElem->elemType == SEMICON) {
|
|
pEdge = pLastElem->pEdge;
|
|
pDevice->rhs[pNode->nEqn] = -pEdge->dJnDpsiP1;
|
|
pDevice->rhs[pNode->pEqn] = -pEdge->dJpDpsiP1;
|
|
}
|
|
incVce = pDevice->dcDeltaSolution;
|
|
|
|
#ifdef KLU
|
|
SMPsolveKLUforCIDER (pDevice->matrix, pDevice->rhs, incVce, NULL, NULL) ;
|
|
#else
|
|
SMPsolveForCIDER (pDevice->matrix, pDevice->rhs, incVce) ;
|
|
#endif
|
|
|
|
/* zero the rhs before loading in the new rhs base contribution */
|
|
for (index = 1; index <= pDevice->numEqns; index++) {
|
|
pDevice->rhs[index] = 0.0;
|
|
}
|
|
pNode = pBaseElem->pRightNode;
|
|
|
|
if (pNode->baseType == N_TYPE) {
|
|
nConc = pDevice->devState0 [pNode->nodeN];
|
|
pDevice->rhs[pNode->nEqn] = nConc * pNode->eg;
|
|
} else if (pNode->baseType == P_TYPE) {
|
|
pConc = pDevice->devState0 [pNode->nodeP];
|
|
pDevice->rhs[pNode->pEqn] = pConc * pNode->eg;
|
|
} else {
|
|
printf("NBJTconductance: unknown base type\n");
|
|
}
|
|
|
|
incVbe = pDevice->copiedSolution;
|
|
|
|
#ifdef KLU
|
|
SMPsolveKLUforCIDER (pDevice->matrix, pDevice->rhs, incVbe, NULL, NULL) ;
|
|
#else
|
|
SMPsolveForCIDER (pDevice->matrix, pDevice->rhs, incVbe) ;
|
|
#endif
|
|
|
|
pElem = pDevice->elemArray[1];/* first element */
|
|
pEdge = pElem->pEdge;
|
|
pNode = pElem->pRightNode;
|
|
|
|
dPsiDVce = incVce[pNode->psiEqn];
|
|
dPsiDVbe = incVbe[pNode->psiEqn];
|
|
if (pElem->elemType == SEMICON) {
|
|
dNDVce = incVce[pNode->nEqn];
|
|
dPDVce = incVce[pNode->pEqn];
|
|
dNDVbe = incVbe[pNode->nEqn];
|
|
dPDVbe = incVbe[pNode->pEqn];
|
|
*dIeDVce += pEdge->dJnDpsiP1 * dPsiDVce + pEdge->dJnDnP1 * dNDVce +
|
|
pEdge->dJpDpsiP1 * dPsiDVce + pEdge->dJpDpP1 * dPDVce;
|
|
*dIeDVbe += pEdge->dJnDpsiP1 * dPsiDVbe + pEdge->dJnDnP1 * dNDVbe +
|
|
pEdge->dJpDpsiP1 * dPsiDVbe + pEdge->dJpDpP1 * dPDVbe;
|
|
}
|
|
/* For transient analysis add the displacement term */
|
|
if (tranAnalysis) {
|
|
*dIeDVce -= intCoeff[0] * pElem->epsRel * dPsiDVce * pElem->rDx;
|
|
*dIeDVbe -= intCoeff[0] * pElem->epsRel * dPsiDVbe * pElem->rDx;
|
|
}
|
|
pElem = pDevice->elemArray[pDevice->numNodes - 1]; /* last element */
|
|
pEdge = pElem->pEdge;
|
|
pNode = pElem->pLeftNode;
|
|
dPsiDVce = incVce[pNode->psiEqn];
|
|
dPsiDVbe = incVbe[pNode->psiEqn];
|
|
if (pElem->elemType == SEMICON) {
|
|
dNDVce = incVce[pNode->nEqn];
|
|
dPDVce = incVce[pNode->pEqn];
|
|
dNDVbe = incVbe[pNode->nEqn];
|
|
dPDVbe = incVbe[pNode->pEqn];
|
|
*dIcDVce += -pEdge->dJnDpsiP1 * dPsiDVce + pEdge->dJnDn * dNDVce +
|
|
-pEdge->dJpDpsiP1 * dPsiDVce + pEdge->dJpDp * dPDVce +
|
|
/* add terms since adjacent to boundary */
|
|
pEdge->dJnDpsiP1 + pEdge->dJpDpsiP1;
|
|
*dIcDVbe += -pEdge->dJnDpsiP1 * dPsiDVbe + pEdge->dJnDn * dNDVbe +
|
|
-pEdge->dJpDpsiP1 * dPsiDVbe + pEdge->dJpDp * dPDVbe;
|
|
}
|
|
if (tranAnalysis) {
|
|
*dIcDVce += intCoeff[0] * pElem->epsRel * (dPsiDVce - 1.0) * pElem->rDx;
|
|
*dIcDVbe += intCoeff[0] * pElem->epsRel * dPsiDVbe * pElem->rDx;
|
|
}
|
|
*dIeDVce *= -GNorm * area;
|
|
*dIcDVce *= -GNorm * area;
|
|
*dIeDVbe *= -GNorm * area;
|
|
*dIcDVbe *= -GNorm * area;
|
|
}
|
|
|
|
void
|
|
NUMDcurrent(ONEdevice *pDevice, bool tranAnalysis, double *intCoeff,
|
|
double *id)
|
|
{
|
|
ONEnode *pNode;
|
|
ONEelem *pElem;
|
|
ONEedge *pEdge;
|
|
double *delta = pDevice->dcDeltaSolution;
|
|
double dPsi, dN, dP;
|
|
|
|
*id = 0.0;
|
|
|
|
pElem = pDevice->elemArray[1];
|
|
pNode = pElem->pRightNode;
|
|
pEdge = pElem->pEdge;
|
|
dPsi = delta[pNode->psiEqn];
|
|
*id = pEdge->jn + pEdge->jp + pElem->epsRel * pEdge->jd;
|
|
if (pElem->elemType == SEMICON) {
|
|
dN = delta[pNode->nEqn];
|
|
dP = delta[pNode->pEqn];
|
|
*id += pEdge->dJnDpsiP1 * dPsi + pEdge->dJnDnP1 * dN +
|
|
pEdge->dJpDpsiP1 * dPsi + pEdge->dJpDpP1 * dP;
|
|
}
|
|
/* for transient analysis add the displacement term */
|
|
if (tranAnalysis) {
|
|
*id -= intCoeff[0] * pElem->epsRel * pElem->rDx * dPsi;
|
|
}
|
|
*id *= JNorm * pDevice->area;
|
|
}
|
|
|
|
void
|
|
NBJTcurrent(ONEdevice *pDevice, bool tranAnalysis, double *intCoeff,
|
|
double *ie, double *ic)
|
|
{
|
|
ONEnode *pNode;
|
|
ONEelem *pElem;
|
|
ONEedge *pEdge;
|
|
double dPsi, dN, dP;
|
|
double *solution;
|
|
|
|
solution = pDevice->dcDeltaSolution;
|
|
|
|
/* first edge for calculating ie */
|
|
pElem = pDevice->elemArray[1];
|
|
pNode = pElem->pRightNode;
|
|
pEdge = pElem->pEdge;
|
|
dPsi = solution[pNode->psiEqn];
|
|
*ie = pEdge->jn + pEdge->jp + pElem->epsRel * pEdge->jd;
|
|
if (pElem->elemType == SEMICON) {
|
|
dN = solution[pNode->nEqn];
|
|
dP = solution[pNode->pEqn];
|
|
*ie += pEdge->dJnDpsiP1 * dPsi + pEdge->dJnDnP1 * dN +
|
|
pEdge->dJpDpsiP1 * dPsi + pEdge->dJpDpP1 * dP;
|
|
}
|
|
/* for transient analysis add the displacement term */
|
|
if (tranAnalysis) {
|
|
*ie -= intCoeff[0] * pElem->epsRel * dPsi * pElem->rDx;
|
|
}
|
|
/* last edge for calculating ic */
|
|
pElem = pDevice->elemArray[pDevice->numNodes - 1];
|
|
pNode = pElem->pLeftNode;
|
|
pEdge = pElem->pEdge;
|
|
dPsi = solution[pNode->psiEqn];
|
|
*ic = pEdge->jn + pEdge->jp + pElem->epsRel * pEdge->jd;
|
|
if (pElem->elemType == SEMICON) {
|
|
dN = solution[pNode->nEqn];
|
|
dP = solution[pNode->pEqn];
|
|
*ic += -pEdge->dJnDpsiP1 * dPsi + pEdge->dJnDn * dN +
|
|
-pEdge->dJpDpsiP1 * dPsi + pEdge->dJpDp * dP;
|
|
}
|
|
if (tranAnalysis) {
|
|
*ic += intCoeff[0] * pElem->epsRel * dPsi * pElem->rDx;
|
|
}
|
|
*ic *= -JNorm * pDevice->area;
|
|
*ie *= -JNorm * pDevice->area;
|
|
}
|