Copyright 1991 Regents of the University of California. All rights reserved.
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group
**********/
#include "ngspice/ngspice.h"
#include "ngspice/numglobs.h"
#include "ngspice/numenum.h"
#include "ngspice/onemesh.h"
#include "ngspice/onedev.h"
#include "ngspice/spmatrix.h"
#include "ngspice/macros.h"
#include "onedext.h"
#include "oneddefs.h"
#include "ngspice/cidersupt.h"
#include "../../maths/misc/bernoull.h"
#ifdef KLU
#include "ngspice/klu-binding.h"
#endif
void
ONE_jacBuild(ONEdevice *pDevice)
{
SMPmatrix *matrix = pDevice->matrix;
ONEelem *pElem;
ONEnode *pNode;
int index, eIndex;
int psiEqn, nEqn, pEqn;
int psiEqnL=0, nEqnL=0, pEqnL=0;
int psiEqnR=0, nEqnR=0, pEqnR=0;
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) {
pElem = pDevice->elemArray[eIndex];
for (index = 0; index <= 1; index++) {
pNode = pElem->pNodes[index];
psiEqn = pNode->psiEqn;
#ifdef KLU
pNode->fPsiPsi = SMPmakeEltKLUforCIDER (matrix, psiEqn, psiEqn) ;
pNode->fPsiPsiBinding = NULL ;
#else
pNode->fPsiPsi = SMPmakeElt(matrix, psiEqn, psiEqn);
#endif
if (pElem->elemType == SEMICON) {
nEqn = pNode->nEqn;
pEqn = pNode->pEqn;
#ifdef KLU
pNode->fPsiN = SMPmakeEltKLUforCIDER (matrix, psiEqn, nEqn) ;
pNode->fPsiNBinding = NULL ;
#else
pNode->fPsiN = SMPmakeElt(matrix, psiEqn, nEqn);
#endif
#ifdef KLU
pNode->fPsiP = SMPmakeEltKLUforCIDER (matrix, psiEqn, pEqn) ;
pNode->fPsiPBinding = NULL ;
#else
pNode->fPsiP = SMPmakeElt(matrix, psiEqn, pEqn);
#endif
#ifdef KLU
pNode->fNPsi = SMPmakeEltKLUforCIDER (matrix, nEqn, psiEqn) ;
pNode->fNPsiBinding = NULL ;
#else
pNode->fNPsi = SMPmakeElt(matrix, nEqn, psiEqn);
#endif
#ifdef KLU
pNode->fNN = SMPmakeEltKLUforCIDER (matrix, nEqn, nEqn) ;
pNode->fNNBinding = NULL ;
#else
pNode->fNN = SMPmakeElt(matrix, nEqn, nEqn);
#endif
#ifdef KLU
pNode->fNP = SMPmakeEltKLUforCIDER (matrix, nEqn, pEqn) ;
pNode->fNPBinding = NULL ;
#else
pNode->fNP = SMPmakeElt(matrix, nEqn, pEqn);
#endif
#ifdef KLU
pNode->fPPsi = SMPmakeEltKLUforCIDER (matrix, pEqn, psiEqn) ;
pNode->fPPsiBinding = NULL ;
#else
pNode->fPPsi = SMPmakeElt(matrix, pEqn, psiEqn);
#endif
#ifdef KLU
pNode->fPP = SMPmakeEltKLUforCIDER (matrix, pEqn, pEqn) ;
pNode->fPPBinding = NULL ;
#else
pNode->fPP = SMPmakeElt(matrix, pEqn, pEqn);
#endif
#ifdef KLU
pNode->fPN = SMPmakeEltKLUforCIDER (matrix, pEqn, nEqn) ;
pNode->fPNBinding = NULL ;
#else
pNode->fPN = SMPmakeElt(matrix, pEqn, nEqn);
#endif
} else {
nEqn = 0;
pEqn = 0;
}
if (index == 0) {
psiEqnL = psiEqn;
nEqnL = nEqn;
pEqnL = pEqn;
} else {
psiEqnR = psiEqn;
nEqnR = nEqn;
pEqnR = pEqn;
}
}
pNode = pElem->pLeftNode;
#ifdef KLU
pNode->fPsiPsiiP1 = SMPmakeEltKLUforCIDER (matrix, psiEqnL, psiEqnR) ;
pNode->fPsiPsiiP1Binding = NULL ;
#else
pNode->fPsiPsiiP1 = SMPmakeElt(matrix, psiEqnL, psiEqnR);
#endif
if (pElem->elemType == SEMICON) {
#ifdef KLU
pNode->fNPsiiP1 = SMPmakeEltKLUforCIDER (matrix, nEqnL, psiEqnR) ;
pNode->fNPsiiP1Binding = NULL ;
#else
pNode->fNPsiiP1 = SMPmakeElt(matrix, nEqnL, psiEqnR);
#endif
#ifdef KLU
pNode->fNNiP1 = SMPmakeEltKLUforCIDER (matrix, nEqnL, nEqnR) ;
pNode->fNNiP1Binding = NULL ;
#else
pNode->fNNiP1 = SMPmakeElt(matrix, nEqnL, nEqnR);
#endif
#ifdef KLU
pNode->fPPsiiP1 = SMPmakeEltKLUforCIDER (matrix, pEqnL, psiEqnR) ;
pNode->fPPsiiP1Binding = NULL ;
#else
pNode->fPPsiiP1 = SMPmakeElt(matrix, pEqnL, psiEqnR);
#endif
#ifdef KLU
pNode->fPPiP1 = SMPmakeEltKLUforCIDER (matrix, pEqnL, pEqnR) ;
pNode->fPPiP1Binding = NULL ;
#else
pNode->fPPiP1 = SMPmakeElt(matrix, pEqnL, pEqnR);
#endif
if (AvalancheGen) {
#ifdef KLU
pNode->fNPiP1 = SMPmakeEltKLUforCIDER (matrix, nEqnL, pEqnR) ;
pNode->fNPiP1Binding = NULL ;
#else
pNode->fNPiP1 = SMPmakeElt(matrix, nEqnL, pEqnR);
#endif
#ifdef KLU
pNode->fPNiP1 = SMPmakeEltKLUforCIDER (matrix, pEqnL, nEqnR) ;
pNode->fPNiP1Binding = NULL ;
#else
pNode->fPNiP1 = SMPmakeElt(matrix, pEqnL, nEqnR);
#endif
}
}
pNode = pElem->pRightNode;
#ifdef KLU
pNode->fPsiPsiiM1 = SMPmakeEltKLUforCIDER (matrix, psiEqnR, psiEqnL) ;
pNode->fPsiPsiiM1Binding = NULL ;
#else
pNode->fPsiPsiiM1 = SMPmakeElt(matrix, psiEqnR, psiEqnL);
#endif
if (pElem->elemType == SEMICON) {
#ifdef KLU
pNode->fNPsiiM1 = SMPmakeEltKLUforCIDER (matrix, nEqnR, psiEqnL) ;
pNode->fNPsiiM1Binding = NULL ;
#else
pNode->fNPsiiM1 = SMPmakeElt(matrix, nEqnR, psiEqnL);
#endif
#ifdef KLU
pNode->fNNiM1 = SMPmakeEltKLUforCIDER (matrix, nEqnR, nEqnL) ;
pNode->fNNiM1Binding = NULL ;
#else
pNode->fNNiM1 = SMPmakeElt(matrix, nEqnR, nEqnL);
#endif
#ifdef KLU
pNode->fPPsiiM1 = SMPmakeEltKLUforCIDER (matrix, pEqnR, psiEqnL) ;
pNode->fPPsiiM1Binding = NULL ;
#else
pNode->fPPsiiM1 = SMPmakeElt(matrix, pEqnR, psiEqnL);
#endif
#ifdef KLU
pNode->fPPiM1 = SMPmakeEltKLUforCIDER (matrix, pEqnR, pEqnL) ;
pNode->fPPiM1Binding = NULL ;
#else
pNode->fPPiM1 = SMPmakeElt(matrix, pEqnR, pEqnL);
#endif
if (AvalancheGen) {
#ifdef KLU
pNode->fNPiM1 = SMPmakeEltKLUforCIDER (matrix, nEqnR, pEqnL) ;
pNode->fNPiM1Binding = NULL ;
#else
pNode->fNPiM1 = SMPmakeElt(matrix, nEqnR, pEqnL);
#endif
#ifdef KLU
pNode->fPNiM1 = SMPmakeEltKLUforCIDER (matrix, pEqnR, nEqnL) ;
pNode->fPNiM1Binding = NULL ;
#else
pNode->fPNiM1 = SMPmakeElt(matrix, pEqnR, nEqnL);
#endif
}
}
}
}
#ifdef KLU
void
ONEbindCSC (ONEdevice *pDevice)
{
ONEelem *pElem;
ONEnode *pNode;
int index, eIndex;
int psiEqn, nEqn, pEqn;
int psiEqnL=0, nEqnL=0, pEqnL=0;
int psiEqnR=0, nEqnR=0, pEqnR=0;
BindElementKLUforCIDER i, *matched, *BindStruct, *BindStructCSC ;
size_t nz ;
BindStruct = pDevice->matrix->SMPkluMatrix->KLUmatrixBindStructForCIDER ;
nz = pDevice->matrix->SMPkluMatrix->KLUmatrixNZ ;
BindStructCSC = (BindElementKLUforCIDER *) malloc (nz * sizeof (BindElementKLUforCIDER)) ;
for (index = 0 ; index < (int)nz ; index++) {
BindStructCSC [index] = BindStruct [index] ;
}
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) {
pElem = pDevice->elemArray[eIndex];
for (index = 0; index <= 1; index++) {
pNode = pElem->pNodes[index];
psiEqn = pNode->psiEqn;
CREATE_KLU_BINDING_TABLE_CIDER(fPsiPsi, fPsiPsiBinding, psiEqn, psiEqn) ;
if (pElem->elemType == SEMICON) {
nEqn = pNode->nEqn;
pEqn = pNode->pEqn;
CREATE_KLU_BINDING_TABLE_CIDER(fPsiN, fPsiNBinding, psiEqn, nEqn) ;
CREATE_KLU_BINDING_TABLE_CIDER(fPsiP, fPsiPBinding, psiEqn, pEqn) ;
CREATE_KLU_BINDING_TABLE_CIDER(fNPsi, fNPsiBinding, nEqn, psiEqn) ;
CREATE_KLU_BINDING_TABLE_CIDER(fNN, fNNBinding, nEqn, nEqn) ;
CREATE_KLU_BINDING_TABLE_CIDER(fNP, fNPBinding, nEqn, pEqn) ;
CREATE_KLU_BINDING_TABLE_CIDER(fPPsi, fPPsiBinding, pEqn, psiEqn) ;
CREATE_KLU_BINDING_TABLE_CIDER(fPP, fPPBinding, pEqn, pEqn) ;
CREATE_KLU_BINDING_TABLE_CIDER(fPN, fPNBinding, pEqn, nEqn) ;
} else {
nEqn = 0;
pEqn = 0;
}
if (index == 0) {
psiEqnL = psiEqn;
nEqnL = nEqn;
pEqnL = pEqn;
} else {
psiEqnR = psiEqn;
nEqnR = nEqn;
pEqnR = pEqn;
}
}
pNode = pElem->pLeftNode;
CREATE_KLU_BINDING_TABLE_CIDER(fPsiPsiiP1, fPsiPsiiP1Binding, psiEqnL, psiEqnR) ;
if (pElem->elemType == SEMICON) {
CREATE_KLU_BINDING_TABLE_CIDER(fNPsiiP1, fNPsiiP1Binding, nEqnL, psiEqnR) ;
CREATE_KLU_BINDING_TABLE_CIDER(fNNiP1, fNNiP1Binding, nEqnL, nEqnR) ;
CREATE_KLU_BINDING_TABLE_CIDER(fPPsiiP1, fPPsiiP1Binding, pEqnL, psiEqnR) ;
CREATE_KLU_BINDING_TABLE_CIDER(fPPiP1, fPPiP1Binding, pEqnL, pEqnR) ;
if (AvalancheGen) {
CREATE_KLU_BINDING_TABLE_CIDER(fNPiP1, fNPiP1Binding, nEqnL, pEqnR) ;
CREATE_KLU_BINDING_TABLE_CIDER(fPNiP1, fPNiP1Binding, pEqnL, nEqnR) ;
}
}
pNode = pElem->pRightNode;
CREATE_KLU_BINDING_TABLE_CIDER(fPsiPsiiM1, fPsiPsiiM1Binding, psiEqnR, psiEqnL) ;
if (pElem->elemType == SEMICON) {
CREATE_KLU_BINDING_TABLE_CIDER(fNPsiiM1, fNPsiiM1Binding, nEqnR, psiEqnL) ;
CREATE_KLU_BINDING_TABLE_CIDER(fNNiM1, fNNiM1Binding, nEqnR, nEqnL) ;
CREATE_KLU_BINDING_TABLE_CIDER(fPPsiiM1, fPPsiiM1Binding, pEqnR, psiEqnL) ;
CREATE_KLU_BINDING_TABLE_CIDER(fPPiM1, fPPiM1Binding, pEqnR, pEqnL) ;
if (AvalancheGen) {
CREATE_KLU_BINDING_TABLE_CIDER(fNPiM1, fNPiM1Binding, nEqnR, pEqnL) ;
CREATE_KLU_BINDING_TABLE_CIDER(fPNiM1, fPNiM1Binding, pEqnR, nEqnL) ;
}
}
}
free (BindStructCSC) ;
}
#endif
void
ONE_sysLoad(ONEdevice *pDevice, bool tranAnalysis,
ONEtranInfo *info)
{
ONEelem *pElem;
ONEnode *pNode;
ONEedge *pEdge;
int index, eIndex;
double *pRhs = pDevice->rhs;
double dx, rDx, dPsi;
double generation;
double perTime = 0.0;
double fNd, fNa, fdNd, fdNa;
double netConc, dNd, dNa, psi, nConc, pConc;
ONE_commonTerms(pDevice, FALSE, tranAnalysis, info);
if (tranAnalysis) {
perTime = info->intCoeff[0];
}
for (index = 1; index <= pDevice->numEqns; index++) {
pRhs[index] = 0.0;
}
#ifdef KLU
if (pDevice->matrix->CKTkluMODE) {
SMPclearKLUforCIDER (pDevice->matrix) ;
} else {
#endif
SMPclear(pDevice->matrix);
#ifdef KLU
}
#endif
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) {
pElem = pDevice->elemArray[eIndex];
dx = 0.5 * pElem->dx;
rDx = pElem->epsRel * pElem->rDx;
for (index = 0; index <= 1; index++) {
pNode = pElem->pNodes[index];
if (pNode->nodeType != CONTACT) {
*(pNode->fPsiPsi) += rDx;
pRhs[pNode->psiEqn] += pNode->qf;
if (pElem->elemType == SEMICON) {
pEdge = pElem->pEdge;
netConc = pNode->netConc;
dNd = 0.0;
dNa = 0.0;
psi = pDevice->devState0 [pNode->nodePsi];
nConc = pDevice->devState0 [pNode->nodeN];
pConc = pDevice->devState0 [pNode->nodeP];
if (FreezeOut) {
ONE_freezeOut(pNode, nConc, pConc, &fNd, &fNa, &fdNd, &fdNa);
netConc = pNode->nd * fNd - pNode->na * fNa;
dNd = pNode->nd * fdNd;
dNa = pNode->na * fdNa;
}
*(pNode->fPsiN) += dx * (1.0 - dNd);
*(pNode->fPsiP) -= dx * (1.0 - dNa);
*(pNode->fNPsi) -= pEdge->dJnDpsiP1;
*(pNode->fPPsi) -= pEdge->dJpDpsiP1;
pRhs[pNode->psiEqn] += dx * (netConc + pConc - nConc);
*(pNode->fNN) -= dx * pNode->dUdN;
*(pNode->fNP) -= dx * pNode->dUdP;
*(pNode->fPP) += dx * pNode->dUdP;
*(pNode->fPN) += dx * pNode->dUdN;
pRhs[pNode->nEqn] -= -dx * pNode->uNet;
pRhs[pNode->pEqn] -= dx * pNode->uNet;
if (tranAnalysis) {
*(pNode->fNN) -= dx * perTime;
*(pNode->fPP) += dx * perTime;
pRhs[pNode->nEqn] += dx * pNode->dNdT;
pRhs[pNode->pEqn] -= dx * pNode->dPdT;
}
if (pNode->baseType == N_TYPE) {
pRhs[pNode->nEqn] += 0.5 * pNode->eg * nConc *
(pNode->vbe - psi + log(nConc / pNode->nie));
*(pNode->fNPsi) += 0.5 * pNode->eg * nConc;
*(pNode->fNN) -= 0.5 * pNode->eg *
(pNode->vbe - psi + log(nConc / pNode->nie) + 1.0);
} else if (pNode->baseType == P_TYPE) {
pRhs[pNode->pEqn] += 0.5 * pNode->eg * pConc *
(pNode->vbe - psi - log(pConc / pNode->nie));
*(pNode->fPPsi) += 0.5 * pNode->eg * pConc;
*(pNode->fPP) -= 0.5 * pNode->eg *
(pNode->vbe - psi - log(pConc / pNode->nie) - 1.0);
}
}
}
}
pEdge = pElem->pEdge;
dPsi = pEdge->dPsi;
pNode = pElem->pLeftNode;
if (pNode->nodeType != CONTACT) {
pRhs[pNode->psiEqn] += rDx * dPsi;
*(pNode->fPsiPsiiP1) -= rDx;
if (pElem->elemType == SEMICON) {
pRhs[pNode->nEqn] -= pEdge->jn;
pRhs[pNode->pEqn] -= pEdge->jp;
*(pNode->fNN) += pEdge->dJnDn;
*(pNode->fPP) += pEdge->dJpDp;
*(pNode->fNPsiiP1) += pEdge->dJnDpsiP1;
*(pNode->fNNiP1) += pEdge->dJnDnP1;
*(pNode->fPPsiiP1) += pEdge->dJpDpsiP1;
*(pNode->fPPiP1) += pEdge->dJpDpP1;
}
}
pNode = pElem->pRightNode;
if (pNode->nodeType != CONTACT) {
pRhs[pNode->psiEqn] -= rDx * dPsi;
*(pNode->fPsiPsiiM1) -= rDx;
if (pElem->elemType == SEMICON) {
pRhs[pNode->nEqn] += pEdge->jn;
pRhs[pNode->pEqn] += pEdge->jp;
*(pNode->fNN) -= pEdge->dJnDnP1;
*(pNode->fPP) -= pEdge->dJpDpP1;
*(pNode->fNPsiiM1) += pEdge->dJnDpsiP1;
*(pNode->fNNiM1) -= pEdge->dJnDn;
*(pNode->fPPsiiM1) += pEdge->dJpDpsiP1;
*(pNode->fPPiM1) -= pEdge->dJpDp;
}
}
}
if (AvalancheGen) {
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) {
pElem = pDevice->elemArray[eIndex];
for (index = 0; index <= 1; index++) {
if (pElem->evalNodes[index]) {
pNode = pElem->pNodes[index];
if ((pNode->nodeType != CONTACT) && (pElem->elemType == SEMICON)) {
generation = ONEavalanche(FALSE, pDevice, pNode);
pRhs[pNode->nEqn] -= generation;
pRhs[pNode->pEqn] += generation;
}
}
}
}
}
}
void
ONE_jacLoad(ONEdevice *pDevice)
{
ONEelem *pElem;
ONEnode *pNode, *pNode1;
ONEedge *pEdge;
int index, eIndex;
double dx, rDx, dPsi;
double generation;
double fNd, fNa, fdNd, fdNa;
double dNd, dNa, psi, nConc, pConc;
ONE_commonTerms(pDevice, FALSE, FALSE, NULL);
#ifdef KLU
if (pDevice->matrix->CKTkluMODE) {
SMPclearKLUforCIDER (pDevice->matrix) ;
} else {
#endif
SMPclear(pDevice->matrix);
#ifdef KLU
}
#endif
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) {
pElem = pDevice->elemArray[eIndex];
dx = 0.5 * pElem->dx;
rDx = pElem->epsRel * pElem->rDx;
for (index = 0; index <= 1; index++) {
pNode = pElem->pNodes[index];
if (pNode->nodeType != CONTACT) {
*(pNode->fPsiPsi) += rDx;
if (pElem->elemType == SEMICON) {
pEdge = pElem->pEdge;
dNd = 0.0;
dNa = 0.0;
psi = pDevice->devState0 [pNode->nodePsi];
nConc = pDevice->devState0 [pNode->nodeN];
pConc = pDevice->devState0 [pNode->nodeP];
if (FreezeOut) {
ONE_freezeOut(pNode, nConc, pConc, &fNd, &fNa, &fdNd, &fdNa);
dNd = pNode->nd * fdNd;
dNa = pNode->na * fdNa;
}
*(pNode->fPsiN) += dx * (1.0 - dNd);
*(pNode->fPsiP) -= dx * (1.0 - dNa);
*(pNode->fNPsi) -= pEdge->dJnDpsiP1;
*(pNode->fPPsi) -= pEdge->dJpDpsiP1;
if (pNode->baseType == N_TYPE) {
*(pNode->fNPsi) += 0.5 * nConc * pNode->eg;
*(pNode->fNN) -= 0.5 * pNode->eg
* (pNode->vbe - psi + log(nConc / pNode->nie) + 1.0);
}
if (pNode->baseType == P_TYPE) {
*(pNode->fPPsi) += 0.5 * pConc * pNode->eg;
*(pNode->fPP) -= 0.5 * pNode->eg
* (pNode->vbe - psi - log(pConc / pNode->nie) - 1.0);
}
}
}
}
pNode = pElem->pLeftNode;
if (pNode->nodeType != CONTACT) {
pEdge = pElem->pEdge;
dPsi = pEdge->dPsi;
if (pElem->elemType == SEMICON) {
*(pNode->fNN) += pEdge->dJnDn - dx * pNode->dUdN;
*(pNode->fNP) -= dx * pNode->dUdP;
*(pNode->fPP) += pEdge->dJpDp + dx * pNode->dUdP;
*(pNode->fPN) += dx * pNode->dUdN;
}
pNode1 = pElem->pRightNode;
if (pNode1->nodeType != CONTACT) {
*(pNode->fPsiPsiiP1) -= rDx;
if (pElem->elemType == SEMICON) {
*(pNode->fNPsiiP1) += pEdge->dJnDpsiP1;
*(pNode->fNNiP1) += pEdge->dJnDnP1;
*(pNode->fPPsiiP1) += pEdge->dJpDpsiP1;
*(pNode->fPPiP1) += pEdge->dJpDpP1;
}
}
}
pNode = pElem->pRightNode;
if (pNode->nodeType != CONTACT) {
pEdge = pElem->pEdge;
dPsi = pEdge->dPsi;
if (pElem->elemType == SEMICON) {
*(pNode->fNN) += -pEdge->dJnDnP1 - dx * pNode->dUdN;
*(pNode->fNP) -= dx * pNode->dUdP;
*(pNode->fPP) += -pEdge->dJpDpP1 + dx * pNode->dUdP;
*(pNode->fPN) += dx * pNode->dUdN;
}
pNode1 = pElem->pLeftNode;
if (pNode1->nodeType != CONTACT) {
*(pNode->fPsiPsiiM1) -= rDx;
if (pElem->elemType == SEMICON) {
*(pNode->fNPsiiM1) += pEdge->dJnDpsiP1;
*(pNode->fNNiM1) -= pEdge->dJnDn;
*(pNode->fPPsiiM1) += pEdge->dJpDpsiP1;
*(pNode->fPPiM1) -= pEdge->dJpDp;
}
}
}
}
if (AvalancheGen) {
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) {
pElem = pDevice->elemArray[eIndex];
for (index = 0; index <= 1; index++) {
if (pElem->evalNodes[index]) {
pNode = pElem->pNodes[index];
if ((pNode->nodeType != CONTACT) && (pElem->elemType == SEMICON)) {
generation = ONEavalanche(FALSE, pDevice, pNode);
}
}
}
}
}
}
void
ONE_rhsLoad(ONEdevice *pDevice, bool tranAnalysis,
ONEtranInfo *info)
{
ONEelem *pElem;
ONEnode *pNode;
ONEedge *pEdge;
int index, eIndex;
double *pRhs = pDevice->rhs;
double dx, rDx, dPsi;
double generation;
double perTime;
double fNd, fNa, fdNd, fdNa;
double netConc, dNd, dNa, psi, nConc, pConc;
ONE_commonTerms(pDevice, FALSE, tranAnalysis, info);
if (tranAnalysis) {
perTime = info->intCoeff[0];
}
for (index = 1; index <= pDevice->numEqns; index++) {
pRhs[index] = 0.0;
}
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) {
pElem = pDevice->elemArray[eIndex];
dx = 0.5 * pElem->dx;
rDx = pElem->epsRel * pElem->rDx;
for (index = 0; index <= 1; index++) {
pNode = pElem->pNodes[index];
if (pNode->nodeType != CONTACT) {
pRhs[pNode->psiEqn] += pNode->qf;
if (pElem->elemType == SEMICON) {
pEdge = pElem->pEdge;
netConc = pNode->netConc;
dNd = 0.0;
dNa = 0.0;
psi = pDevice->devState0 [pNode->nodePsi];
nConc = pDevice->devState0 [pNode->nodeN];
pConc = pDevice->devState0 [pNode->nodeP];
if (FreezeOut) {
ONE_freezeOut(pNode, nConc, pConc, &fNd, &fNa, &fdNd, &fdNa);
netConc = pNode->nd * fNd - pNode->na * fNa;
dNd = pNode->nd * fdNd;
dNa = pNode->na * fdNa;
}
pRhs[pNode->psiEqn] += dx * (netConc + pConc - nConc);
pRhs[pNode->nEqn] -= -dx * pNode->uNet;
pRhs[pNode->pEqn] -= dx * pNode->uNet;
if (tranAnalysis) {
pRhs[pNode->nEqn] += dx * pNode->dNdT;
pRhs[pNode->pEqn] -= dx * pNode->dPdT;
}
if (pNode->baseType == N_TYPE) {
pRhs[pNode->nEqn] += 0.5 * pNode->eg * nConc *
(pNode->vbe - psi + log(nConc / pNode->nie));
} else if (pNode->baseType == P_TYPE) {
pRhs[pNode->pEqn] += 0.5 * pNode->eg * pConc *
(pNode->vbe - psi - log(pConc / pNode->nie));
}
}
}
}
pEdge = pElem->pEdge;
dPsi = pEdge->dPsi;
pNode = pElem->pLeftNode;
if (pNode->nodeType != CONTACT) {
pRhs[pNode->psiEqn] += rDx * dPsi;
if (pElem->elemType == SEMICON) {
pRhs[pNode->nEqn] -= pEdge->jn;
pRhs[pNode->pEqn] -= pEdge->jp;
}
}
pNode = pElem->pRightNode;
if (pNode->nodeType != CONTACT) {
pRhs[pNode->psiEqn] -= rDx * dPsi;
if (pElem->elemType == SEMICON) {
pRhs[pNode->nEqn] += pEdge->jn;
pRhs[pNode->pEqn] += pEdge->jp;
}
}
}
if (AvalancheGen) {
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) {
pElem = pDevice->elemArray[eIndex];
for (index = 0; index <= 1; index++) {
if (pElem->evalNodes[index]) {
pNode = pElem->pNodes[index];
if ((pNode->nodeType != CONTACT) && (pElem->elemType == SEMICON)) {
generation = ONEavalanche(TRUE, pDevice, pNode);
pRhs[pNode->nEqn] -= generation;
pRhs[pNode->pEqn] += generation;
}
}
}
}
}
}
void
ONE_commonTerms(ONEdevice *pDevice, bool currentOnly,
bool tranAnalysis, ONEtranInfo *info)
{
ONEelem *pElem;
ONEedge *pEdge;
ONEnode *pNode;
int index, eIndex;
double psi1, psi2, psi, nConc=0.0, pConc=0.0, nC, pC, nP1, pP1;
double dPsiN, dPsiP;
double bPsiN, dbPsiN, bMPsiN, dbMPsiN;
double bPsiP, dbPsiP, bMPsiP, dbMPsiP;
double mun, dMun, mup, dMup;
double conc1, conc2;
double cnAug, cpAug;
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) {
pElem = pDevice->elemArray[eIndex];
cnAug = pElem->matlInfo->cAug[ELEC];
cpAug = pElem->matlInfo->cAug[HOLE];
for (index = 0; index <= 1; 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;
}
if (pNode->baseType == P_TYPE && pConc <= 0.0) {
pConc = pNode->na;
} else if (pNode->baseType == N_TYPE && nConc <= 0.0) {
nConc = pNode->nd;
}
}
} else {
psi = pNode->psi;
if (pElem->elemType == SEMICON) {
nConc = pNode->nConc;
pConc = pNode->pConc;
}
}
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);
}
}
}
}
pEdge = pElem->pEdge;
pNode = pElem->pLeftNode;
if (pNode->nodeType != CONTACT) {
psi1 = pDevice->dcSolution[pNode->psiEqn];
} else {
psi1 = pNode->psi;
}
pNode = pElem->pRightNode;
if (pNode->nodeType != CONTACT) {
psi2 = pDevice->dcSolution[pNode->psiEqn];
} else {
psi2 = pNode->psi;
}
pEdge->dPsi = psi2 - psi1;
pDevice->devState0 [pEdge->edgeDpsi] = pEdge->dPsi;
}
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) {
pElem = pDevice->elemArray[eIndex];
pEdge = pElem->pEdge;
if (pElem->elemType == SEMICON) {
dPsiN = pEdge->dPsi + pEdge->dCBand;
dPsiP = pEdge->dPsi - pEdge->dVBand;
bernoulli(dPsiN, &bPsiN, &dbPsiN, &bMPsiN, &dbMPsiN, !currentOnly);
bernoulli(dPsiP, &bPsiP, &dbPsiP, &bMPsiP, &dbMPsiP, !currentOnly);
nC = pDevice->devState0 [pElem->pLeftNode->nodeN];
nP1 = pDevice->devState0 [pElem->pRightNode->nodeN];
pC = pDevice->devState0 [pElem->pLeftNode->nodeP];
pP1 = pDevice->devState0 [pElem->pRightNode->nodeP];
conc1 = pElem->pLeftNode->totalConc;
conc2 = pElem->pRightNode->totalConc;
pEdge->jn = (bPsiN * nP1 - bMPsiN * nC);
pEdge->jp = (bPsiP * pC - bMPsiP * pP1);
mun = pEdge->mun;
dMun = 0.0;
mup = pEdge->mup;
dMup = 0.0;
MOBfieldDep(pElem->matlInfo, ELEC, dPsiN * pElem->rDx, &mun, &dMun);
MOBfieldDep(pElem->matlInfo, HOLE, dPsiP * pElem->rDx, &mup, &dMup);
mun *= pElem->rDx;
dMun *= pElem->rDx * pElem->rDx;
mup *= pElem->rDx;
dMup *= pElem->rDx * pElem->rDx;
* The base continuity equation makes use of mu/dx in eg. The base
* length has already been calculated and converted to normalized,
* reciprocal form during setup. The name should be changed, but that's
* a big hassle.
*/
for (index = 0; index <= 1; index++) {
if (pElem->evalNodes[index]) {
pNode = pElem->pNodes[index];
if (pNode->baseType == N_TYPE) {
pNode->eg = pEdge->mun * pDevice->baseLength;
} else if (pNode->baseType == P_TYPE) {
pNode->eg = pEdge->mup * pDevice->baseLength;
}
}
}
pEdge->jn *= mun;
pEdge->jp *= mup;
if (!currentOnly) {
if (dMun == 0.0) {
pEdge->dJnDpsiP1 = mun * (dbPsiN * nP1 - dbMPsiN * nC);
} else {
pEdge->dJnDpsiP1 = dMun * (bPsiN * nP1 - bMPsiN * nC)
+ mun * (dbPsiN * nP1 - dbMPsiN * nC);
}
pEdge->dJnDn = -mun * bMPsiN;
pEdge->dJnDnP1 = mun * bPsiN;
if (dMup == 0.0) {
pEdge->dJpDpsiP1 = mup * (dbPsiP * pC - dbMPsiP * pP1);
} else {
pEdge->dJpDpsiP1 = dMup * (bPsiP * pC - bMPsiP * pP1)
+ mup * (dbPsiP * pC - dbMPsiP * pP1);
}
pEdge->dJpDp = mup * bPsiP;
pEdge->dJpDpP1 = -mup * bMPsiP;
}
}
if (tranAnalysis) {
pEdge->jd = -integrate(pDevice->devStates, info,
pEdge->edgeDpsi) * pElem->rDx;
}
}
}