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.
679 lines
20 KiB
679 lines
20 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
|
|
**********/
|
|
|
|
/*
|
|
* Functions for projecting the next solution point for use with the modified
|
|
* two-level Newton scheme
|
|
*/
|
|
|
|
#include "ngspice/ngspice.h"
|
|
#include "ngspice/numglobs.h"
|
|
#include "ngspice/numenum.h"
|
|
#include "ngspice/twomesh.h"
|
|
#include "ngspice/twodev.h"
|
|
#include "ngspice/bool.h"
|
|
#include "ngspice/spmatrix.h"
|
|
#include "twoddefs.h"
|
|
#include "twodext.h"
|
|
#include "ngspice/cidersupt.h"
|
|
|
|
|
|
/* Forward Declarations */
|
|
|
|
|
|
void NUMD2project(TWOdevice *pDevice, double delV)
|
|
{
|
|
TWOnode *pNode;
|
|
TWOelem *pElem;
|
|
int index, eIndex, numContactNodes;
|
|
TWOcontact *pContact = pDevice->pLastContact;
|
|
double *incVpn, *solution = pDevice->dcSolution;
|
|
double delPsi, delN, delP, newN, newP;
|
|
|
|
delV = -delV / VNorm;
|
|
/* update the boundary condition for the last contact */
|
|
numContactNodes = pContact->numNodes;
|
|
for ( index = 0; index < numContactNodes; index++ ) {
|
|
pNode = pContact->pNodes[ index ];
|
|
pNode->psi += delV;
|
|
}
|
|
|
|
/*
|
|
* store the new rhs for computing the incremental quantities
|
|
* with the second to last node. solve the system of equations
|
|
*/
|
|
|
|
if ( ABS(delV) < MIN_DELV ) {
|
|
TWOstoreInitialGuess( pDevice );
|
|
return;
|
|
}
|
|
incVpn = pDevice->dcDeltaSolution;
|
|
storeNewRhs( pDevice, pContact );
|
|
spSolve( pDevice->matrix, pDevice->rhs, incVpn, NULL, NULL );
|
|
|
|
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
|
|
pElem = pDevice->elements[ eIndex ];
|
|
for ( index = 0; index <= 3; index++ ) {
|
|
if ( pElem->evalNodes[ index ] ) {
|
|
pNode = pElem->pNodes[ index ];
|
|
if ( pNode->nodeType != CONTACT ) {
|
|
delPsi = incVpn[ pNode->psiEqn ] * delV;
|
|
solution[ pNode->psiEqn ] = pNode->psi + delPsi;
|
|
if ( pElem->elemType == SEMICON
|
|
&& (!OneCarrier || (OneCarrier == N_TYPE)) ) {
|
|
delN = incVpn[ pNode->nEqn ] * delV;
|
|
newN = pNode->nConc + delN;
|
|
if ( newN <= 0.0 ) {
|
|
solution[ pNode->nEqn ] = guessNewConc( pNode->nConc, delN );
|
|
}
|
|
else {
|
|
solution[ pNode->nEqn ] = newN;
|
|
}
|
|
}
|
|
if ( pElem->elemType == SEMICON
|
|
&& (!OneCarrier || (OneCarrier == P_TYPE)) ) {
|
|
delP = incVpn[ pNode->pEqn ] * delV;
|
|
newP = pNode->pConc + delP;
|
|
if ( newP <= 0.0 ) {
|
|
solution[ pNode->pEqn ] = guessNewConc( pNode->pConc, delP );
|
|
}
|
|
else {
|
|
solution[ pNode->pEqn ] = newP;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void NBJT2project(TWOdevice *pDevice, double delVce, double delVbe)
|
|
{
|
|
TWOnode *pNode;
|
|
TWOelem *pElem;
|
|
int index, eIndex, numContactNodes;
|
|
TWOcontact *pColContact = pDevice->pFirstContact;
|
|
TWOcontact *pBaseContact = pDevice->pFirstContact->next;
|
|
double *incVce, *incVbe, *solution = pDevice->dcSolution;
|
|
double delPsi, delN, delP, newN, newP;
|
|
double nConc, pConc;
|
|
|
|
/* Normalize the voltages for calculations. */
|
|
if ( delVce != 0.0 ) {
|
|
delVce = delVce / VNorm;
|
|
numContactNodes = pColContact->numNodes;
|
|
for ( index = 0; index < numContactNodes; index++ ) {
|
|
pNode = pColContact->pNodes[ index ];
|
|
pNode->psi += delVce;
|
|
}
|
|
}
|
|
if ( delVbe != 0.0 ) {
|
|
delVbe = delVbe / VNorm;
|
|
numContactNodes = pBaseContact->numNodes;
|
|
for ( index = 0; index < numContactNodes; index++ ) {
|
|
pNode = pBaseContact->pNodes[ index ];
|
|
pNode->psi += delVbe;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* store the new rhs for computing the incremental quantities
|
|
* incVce (dcDeltaSolution) and incVbe (copiedSolution) are used to
|
|
* store the incremental quantities associated with Vce and Vbe
|
|
*/
|
|
|
|
/* set incVce = dcDeltaSolution; incVbe = copiedSolution */
|
|
|
|
if ( ABS( delVce ) > MIN_DELV ) {
|
|
incVce = pDevice->dcDeltaSolution;
|
|
storeNewRhs( pDevice, pColContact );
|
|
spSolve( pDevice->matrix, pDevice->rhs, incVce, NULL, NULL);
|
|
|
|
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
|
|
pElem = pDevice->elements[ eIndex ];
|
|
for ( index = 0; index <= 3; index++ ) {
|
|
if ( pElem->evalNodes[ index ] ) {
|
|
pNode = pElem->pNodes[ index ];
|
|
if ( pNode->nodeType != CONTACT ) {
|
|
delPsi = incVce[ pNode->psiEqn ] * delVce;
|
|
solution[ pNode->psiEqn ] = pNode->psi + delPsi;
|
|
if ( pElem->elemType == SEMICON
|
|
&& (!OneCarrier || (OneCarrier == N_TYPE)) ) {
|
|
delN = incVce[ pNode->nEqn ] * delVce;
|
|
newN = pNode->nConc + delN;
|
|
if ( newN <= 0.0 ) {
|
|
solution[ pNode->nEqn ] = guessNewConc( pNode->nConc, delN );
|
|
}
|
|
else {
|
|
solution[ pNode->nEqn ] = newN;
|
|
}
|
|
}
|
|
if ( pElem->elemType == SEMICON
|
|
&& (!OneCarrier | (OneCarrier == P_TYPE)) ) {
|
|
delP = incVce[ pNode->pEqn ] * delVce;
|
|
newP = pNode->pConc + delP;
|
|
if ( newP <= 0.0 ) {
|
|
solution[ pNode->pEqn ] = guessNewConc( pNode->pConc, delP );
|
|
}
|
|
else {
|
|
solution[ pNode->pEqn ] = newP;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
TWOstoreInitialGuess( pDevice );
|
|
}
|
|
|
|
if ( ABS( delVbe ) > MIN_DELV ) {
|
|
incVbe = pDevice->copiedSolution;
|
|
storeNewRhs( pDevice, pBaseContact );
|
|
spSolve( pDevice->matrix, pDevice->rhs, incVbe, NULL, NULL);
|
|
|
|
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
|
|
pElem = pDevice->elements[ eIndex ];
|
|
for ( index = 0; index <= 3; index++ ) {
|
|
if ( pElem->evalNodes[ index ] ) {
|
|
pNode = pElem->pNodes[ index ];
|
|
if ( pNode->nodeType != CONTACT ) {
|
|
delPsi = incVbe[ pNode->psiEqn ] * delVbe;
|
|
solution[ pNode->psiEqn ] += delPsi;
|
|
if ( pElem->elemType == SEMICON
|
|
&& (!OneCarrier || (OneCarrier == N_TYPE)) ) {
|
|
delN = incVbe[ pNode->nEqn ] * delVbe;
|
|
nConc = solution[ pNode->nEqn ];
|
|
newN = nConc + delN;
|
|
if ( newN <= 0.0 ) {
|
|
solution[ pNode->nEqn ] = guessNewConc( nConc, delN );
|
|
}
|
|
else {
|
|
solution[ pNode->nEqn ] = newN;
|
|
}
|
|
}
|
|
if ( pElem->elemType == SEMICON
|
|
&& (!OneCarrier || (OneCarrier == P_TYPE)) ) {
|
|
delP = incVbe[ pNode->pEqn ] * delVbe;
|
|
pConc = solution[ pNode->pEqn ];
|
|
newP = pConc + delP;
|
|
if ( newP <= 0.0 ) {
|
|
solution[ pNode->pEqn ] = guessNewConc( pConc, delP );
|
|
}
|
|
else {
|
|
solution[ pNode->pEqn ] = newP;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void NUMOSproject(TWOdevice *pDevice, double delVdb, double delVsb,
|
|
double delVgb)
|
|
{
|
|
TWOnode *pNode;
|
|
TWOelem *pElem;
|
|
int index, eIndex, numContactNodes;
|
|
TWOcontact *pDContact = pDevice->pFirstContact;
|
|
TWOcontact *pGContact = pDevice->pFirstContact->next;
|
|
TWOcontact *pSContact = pDevice->pFirstContact->next->next;
|
|
double *incVdb, *incVsb, *incVgb, *solution = pDevice->dcSolution;
|
|
double delPsi, delN, delP, newN, newP;
|
|
double nConc, pConc;
|
|
|
|
/* normalize the voltages for calculations */
|
|
if ( delVdb != 0.0 ) {
|
|
delVdb = delVdb / VNorm;
|
|
numContactNodes = pDContact->numNodes;
|
|
for ( index = 0; index < numContactNodes; index++ ) {
|
|
pNode = pDContact->pNodes[ index ];
|
|
pNode->psi += delVdb;
|
|
}
|
|
}
|
|
if ( delVsb != 0.0 ) {
|
|
delVsb = delVsb / VNorm;
|
|
numContactNodes = pSContact->numNodes;
|
|
for ( index = 0; index < numContactNodes; index++ ) {
|
|
pNode = pSContact->pNodes[ index ];
|
|
pNode->psi += delVsb;
|
|
}
|
|
}
|
|
if ( delVgb != 0.0 ) {
|
|
delVgb = delVgb / VNorm;
|
|
numContactNodes = pGContact->numNodes;
|
|
for ( index = 0; index < numContactNodes; index++ ) {
|
|
pNode = pGContact->pNodes[ index ];
|
|
pNode->psi += delVgb;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* store the new rhs for computing the incremental quantities
|
|
* incVdb (dcDeltaSolution), incVsb, incVgb
|
|
*/
|
|
|
|
if ( ABS( delVdb ) > MIN_DELV ) {
|
|
|
|
incVdb = pDevice->dcDeltaSolution;
|
|
storeNewRhs( pDevice, pDContact );
|
|
spSolve( pDevice->matrix, pDevice->rhs, incVdb, NULL, NULL);
|
|
|
|
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
|
|
pElem = pDevice->elements[ eIndex ];
|
|
for ( index = 0; index <= 3; index++ ) {
|
|
if ( pElem->evalNodes[ index ] ) {
|
|
pNode = pElem->pNodes[ index ];
|
|
if ( pNode->nodeType != CONTACT ) {
|
|
delPsi = incVdb[ pNode->psiEqn ] * delVdb;
|
|
solution[ pNode->psiEqn ] = pNode->psi + delPsi;
|
|
if ( pElem->elemType == SEMICON
|
|
&& (!OneCarrier || (OneCarrier == N_TYPE)) ) {
|
|
delN = incVdb[ pNode->nEqn ] * delVdb;
|
|
newN = pNode->nConc + delN;
|
|
if ( newN <= 0.0 ) {
|
|
solution[ pNode->nEqn ] = guessNewConc( pNode->nConc, delN );
|
|
}
|
|
else {
|
|
solution[ pNode->nEqn ] = newN;
|
|
}
|
|
}
|
|
if ( pElem->elemType == SEMICON
|
|
&& (!OneCarrier || (OneCarrier == P_TYPE)) ) {
|
|
delP = incVdb[ pNode->pEqn ] * delVdb;
|
|
newP = pNode->pConc + delP;
|
|
if ( newP <= 0.0 ) {
|
|
solution[ pNode->pEqn ] = guessNewConc( pNode->pConc, delP );
|
|
}
|
|
else {
|
|
solution[ pNode->pEqn ] = newP;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
TWOstoreInitialGuess( pDevice );
|
|
}
|
|
|
|
if ( ABS( delVsb ) > MIN_DELV ) {
|
|
|
|
incVsb = pDevice->dcDeltaSolution;
|
|
storeNewRhs( pDevice, pSContact );
|
|
spSolve( pDevice->matrix, pDevice->rhs, incVsb, NULL, NULL);
|
|
|
|
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
|
|
pElem = pDevice->elements[ eIndex ];
|
|
for ( index = 0; index <= 3; index++ ) {
|
|
if ( pElem->evalNodes[ index ] ) {
|
|
pNode = pElem->pNodes[ index ];
|
|
if ( pNode->nodeType != CONTACT ) {
|
|
delPsi = incVsb[ pNode->psiEqn ] * delVsb;
|
|
solution[ pNode->psiEqn ] += delPsi;
|
|
if ( pElem->elemType == SEMICON
|
|
&& (!OneCarrier || (OneCarrier == N_TYPE)) ) {
|
|
delN = incVsb[ pNode->nEqn ] * delVsb;
|
|
nConc = solution[ pNode->nEqn ];
|
|
newN = nConc + delN;
|
|
if ( newN <= 0.0 ) {
|
|
solution[ pNode->nEqn ] = guessNewConc( nConc, delN );
|
|
}
|
|
else {
|
|
solution[ pNode->nEqn ] = newN;
|
|
}
|
|
}
|
|
if ( pElem->elemType == SEMICON
|
|
&& (!OneCarrier || (OneCarrier == P_TYPE)) ) {
|
|
delP = incVsb[ pNode->pEqn ] * delVsb;
|
|
pConc = solution[ pNode->pEqn ];
|
|
newP = pConc + delP;
|
|
if ( newP <= 0.0 ) {
|
|
solution[ pNode->pEqn ] = guessNewConc( pConc, delP );
|
|
}
|
|
else {
|
|
solution[ pNode->pEqn ] = newP;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( ABS( delVgb ) > MIN_DELV ) {
|
|
|
|
incVgb = pDevice->dcDeltaSolution;
|
|
storeNewRhs( pDevice, pGContact );
|
|
spSolve( pDevice->matrix, pDevice->rhs, incVgb, NULL, NULL);
|
|
|
|
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
|
|
pElem = pDevice->elements[ eIndex ];
|
|
for ( index = 0; index <= 3; index++ ) {
|
|
if ( pElem->evalNodes[ index ] ) {
|
|
pNode = pElem->pNodes[ index ];
|
|
if ( pNode->nodeType != CONTACT ) {
|
|
delPsi = incVgb[ pNode->psiEqn ] * delVgb;
|
|
solution[ pNode->psiEqn ] += delPsi;
|
|
if ( pElem->elemType == SEMICON
|
|
&& (!OneCarrier || (OneCarrier == N_TYPE)) ) {
|
|
delN = incVgb[ pNode->nEqn ] * delVgb;
|
|
nConc = solution[ pNode->nEqn ];
|
|
newN = nConc + delN;
|
|
if ( newN <= 0.0 ) {
|
|
solution[ pNode->nEqn ] = guessNewConc( nConc, delN );
|
|
}
|
|
else {
|
|
solution[ pNode->nEqn ] = newN;
|
|
}
|
|
}
|
|
if ( pElem->elemType == SEMICON
|
|
&& (!OneCarrier || (OneCarrier == P_TYPE)) ) {
|
|
delP = incVgb[ pNode->pEqn ] * delVgb;
|
|
pConc = solution[ pNode->pEqn ];
|
|
newP = pConc + delP;
|
|
if ( newP <= 0.0 ) {
|
|
solution[ pNode->pEqn ] = guessNewConc( pConc, delP );
|
|
}
|
|
else {
|
|
solution[ pNode->pEqn ] = newP;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* functions to update the solution for the full-LU and
|
|
modified two-level Newton methods
|
|
*/
|
|
|
|
void NUMD2update(TWOdevice *pDevice, double delV, BOOLEAN updateBoundary)
|
|
{
|
|
TWOnode *pNode;
|
|
TWOelem *pElem;
|
|
int index, eIndex, numContactNodes;
|
|
TWOcontact *pContact = pDevice->pLastContact;
|
|
double delPsi, delN, delP, *incVpn, *solution = pDevice->dcSolution;
|
|
|
|
delV = -delV / VNorm;
|
|
if ( updateBoundary ) {
|
|
/* update the boundary condition for the last contact */
|
|
numContactNodes = pContact->numNodes;
|
|
for ( index = 0; index < numContactNodes; index++ ) {
|
|
pNode = pContact->pNodes[ index ];
|
|
pNode->psi += delV;
|
|
}
|
|
}
|
|
|
|
/* the equations have been solved while computing the conductances */
|
|
/* solution is in dcDeltaSolution, so use it */
|
|
|
|
incVpn = pDevice->dcDeltaSolution;
|
|
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
|
|
pElem = pDevice->elements[ eIndex ];
|
|
for ( index = 0; index <= 3; index++ ) {
|
|
if ( pElem->evalNodes[ index ] ) {
|
|
pNode = pElem->pNodes[ index ];
|
|
if ( pNode->nodeType != CONTACT ) {
|
|
delPsi = incVpn[ pNode->psiEqn ] * delV;
|
|
solution[ pNode->psiEqn ] = pNode->psi + delPsi;
|
|
if ( pElem->elemType == SEMICON
|
|
&& (!OneCarrier || (OneCarrier == N_TYPE)) ) {
|
|
delN = incVpn[ pNode->nEqn ] * delV;
|
|
solution[ pNode->nEqn ] = pNode->nConc + delN;
|
|
}
|
|
if ( pElem->elemType == SEMICON
|
|
&& (!OneCarrier || (OneCarrier == P_TYPE)) ) {
|
|
delP = incVpn[ pNode->pEqn ] * delV;
|
|
solution[ pNode->pEqn ] = pNode->pConc + delP;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void NBJT2update(TWOdevice *pDevice, double delVce, double delVbe,
|
|
BOOLEAN updateBoundary)
|
|
{
|
|
TWOnode *pNode;
|
|
TWOelem *pElem;
|
|
int index, eIndex, numContactNodes;
|
|
TWOcontact *pColContact = pDevice->pFirstContact;
|
|
TWOcontact *pBaseContact = pDevice->pFirstContact->next;
|
|
double delPsi, delN, delP, *incVce, *incVbe, *solution = pDevice->dcSolution;
|
|
|
|
/* normalize the voltages for calculations */
|
|
|
|
if ( delVce != 0.0 ) {
|
|
delVce = delVce / VNorm;
|
|
if ( updateBoundary ) {
|
|
numContactNodes = pColContact->numNodes;
|
|
for ( index = 0; index < numContactNodes; index++ ) {
|
|
pNode = pColContact->pNodes[ index ];
|
|
pNode->psi += delVce;
|
|
}
|
|
}
|
|
}
|
|
if ( delVbe != 0.0 ) {
|
|
delVbe = delVbe / VNorm;
|
|
if ( updateBoundary ) {
|
|
numContactNodes = pBaseContact->numNodes;
|
|
for ( index = 0; index < numContactNodes; index++ ) {
|
|
pNode = pBaseContact->pNodes[ index ];
|
|
pNode->psi += delVbe;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* use solution from computeConductance to do update */
|
|
/* set incVce = dcDeltaSolution; incVbe = copiedSolution */
|
|
|
|
incVce = pDevice->dcDeltaSolution;
|
|
incVbe = pDevice->copiedSolution;
|
|
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
|
|
pElem = pDevice->elements[ eIndex ];
|
|
for ( index = 0; index <= 3; index++ ) {
|
|
if ( pElem->evalNodes[ index ] ) {
|
|
pNode = pElem->pNodes[ index ];
|
|
if ( pNode->nodeType != CONTACT ) {
|
|
delPsi = (incVce[ pNode->psiEqn ] * delVce
|
|
+ incVbe[ pNode->psiEqn ] * delVbe);
|
|
solution[ pNode->psiEqn ] = pNode->psi + delPsi;
|
|
if ( pElem->elemType == SEMICON
|
|
&& (!OneCarrier || (OneCarrier == N_TYPE)) ) {
|
|
delN = (incVce[ pNode->nEqn ] * delVce
|
|
+ incVbe[ pNode->nEqn ] * delVbe);
|
|
solution[ pNode->nEqn ] = pNode->nConc + delN;
|
|
}
|
|
if ( pElem->elemType == SEMICON
|
|
&& (!OneCarrier || (OneCarrier == P_TYPE)) ) {
|
|
delP = (incVce[ pNode->pEqn ] * delVce
|
|
+ incVbe[ pNode->pEqn ] * delVbe);
|
|
solution[ pNode->pEqn ] = pNode->pConc + delP;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void NUMOSupdate(TWOdevice *pDevice, double delVdb, double delVsb,
|
|
double delVgb, BOOLEAN updateBoundary)
|
|
{
|
|
TWOnode *pNode;
|
|
TWOelem *pElem;
|
|
int index, eIndex, numContactNodes;
|
|
TWOcontact *pDContact = pDevice->pFirstContact;
|
|
TWOcontact *pGContact = pDevice->pFirstContact->next;
|
|
TWOcontact *pSContact = pDevice->pFirstContact->next->next;
|
|
double delPsi, delN, delP;
|
|
double *incVdb, *incVsb, *incVgb, *solution = pDevice->dcSolution;
|
|
|
|
/* normalize the voltages for calculations */
|
|
if ( delVdb != 0.0 ) {
|
|
delVdb = delVdb / VNorm;
|
|
if ( updateBoundary ) {
|
|
numContactNodes = pDContact->numNodes;
|
|
for ( index = 0; index < numContactNodes; index++ ) {
|
|
pNode = pDContact->pNodes[ index ];
|
|
pNode->psi += delVdb;
|
|
}
|
|
}
|
|
}
|
|
if ( delVsb != 0.0 ) {
|
|
delVsb = delVsb / VNorm;
|
|
if ( updateBoundary ) {
|
|
numContactNodes = pSContact->numNodes;
|
|
for ( index = 0; index < numContactNodes; index++ ) {
|
|
pNode = pSContact->pNodes[ index ];
|
|
pNode->psi += delVsb;
|
|
}
|
|
}
|
|
}
|
|
if ( delVgb != 0.0 ) {
|
|
delVgb = delVgb / VNorm;
|
|
if ( updateBoundary ) {
|
|
numContactNodes = pGContact->numNodes;
|
|
for ( index = 0; index < numContactNodes; index++ ) {
|
|
pNode = pGContact->pNodes[ index ];
|
|
pNode->psi += delVgb;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* use solution from computeConductance to do update */
|
|
|
|
incVdb = pDevice->dcDeltaSolution;
|
|
incVsb = pDevice->copiedSolution;
|
|
incVgb = pDevice->rhsImag;
|
|
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
|
|
pElem = pDevice->elements[ eIndex ];
|
|
for ( index = 0; index <= 3; index++ ) {
|
|
if ( pElem->evalNodes[ index ] ) {
|
|
pNode = pElem->pNodes[ index ];
|
|
if ( pNode->nodeType != CONTACT ) {
|
|
delPsi = (incVdb[ pNode->psiEqn ] * delVdb
|
|
+ incVsb[ pNode->psiEqn ] * delVsb
|
|
+ incVgb[ pNode->psiEqn ] * delVgb);
|
|
solution[ pNode->psiEqn ] = pNode->psi + delPsi;
|
|
if ( pElem->elemType == SEMICON
|
|
&& (!OneCarrier || (OneCarrier == N_TYPE)) ) {
|
|
delN = (incVdb[ pNode->nEqn ] * delVdb
|
|
+ incVsb[ pNode->nEqn ] * delVsb
|
|
+ incVgb[ pNode->nEqn ] * delVgb);
|
|
solution[ pNode->nEqn ] = pNode->nConc + delN;
|
|
}
|
|
if ( pElem->elemType == SEMICON
|
|
&& (!OneCarrier || (OneCarrier == P_TYPE)) ) {
|
|
delP = (incVdb[ pNode->pEqn ] * delVdb
|
|
+ incVsb[ pNode->pEqn ] * delVsb
|
|
+ incVgb[ pNode->pEqn ] * delVgb);
|
|
solution[ pNode->pEqn ] = pNode->pConc + delP;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void storeNewRhs(TWOdevice *pDevice, TWOcontact *pContact)
|
|
{
|
|
int index, i, numContactNodes;
|
|
TWOelem *pElem;
|
|
TWOnode *pNode, *pHNode = NULL, *pVNode = NULL;
|
|
TWOedge *pHEdge = NULL, *pVEdge = NULL;
|
|
double *rhs = pDevice->rhs;
|
|
|
|
/* zero the rhs before loading in the new rhs */
|
|
for ( index = 1; index <= pDevice->numEqns ; index++ ) {
|
|
rhs[ index ] = 0.0;
|
|
}
|
|
|
|
numContactNodes = pContact->numNodes;
|
|
for ( index = 0; index < numContactNodes; index++ ) {
|
|
pNode = pContact->pNodes[ index ];
|
|
for ( i = 0; i <= 3; i++ ) {
|
|
pElem = pNode->pElems[ i ];
|
|
if ( pElem != NULL) {
|
|
/* found an element to which this node belongs */
|
|
switch ( i ) {
|
|
case 0:
|
|
/* the TL element of this node */
|
|
pHNode = pElem->pBLNode;
|
|
pVNode = pElem->pTRNode;
|
|
pHEdge = pElem->pBotEdge;
|
|
pVEdge = pElem->pRightEdge;
|
|
break;
|
|
case 1:
|
|
/* the TR element */
|
|
pHNode = pElem->pBRNode;
|
|
pVNode = pElem->pTLNode;
|
|
pHEdge = pElem->pBotEdge;
|
|
pVEdge = pElem->pLeftEdge;
|
|
break;
|
|
case 2:
|
|
/* the BR element */
|
|
pHNode = pElem->pTRNode;
|
|
pVNode = pElem->pBLNode;
|
|
pHEdge = pElem->pTopEdge;
|
|
pVEdge = pElem->pLeftEdge;
|
|
break;
|
|
case 3:
|
|
/* the BL element */
|
|
pHNode = pElem->pTLNode;
|
|
pVNode = pElem->pBRNode;
|
|
pHEdge = pElem->pTopEdge;
|
|
pVEdge = pElem->pRightEdge;
|
|
break;
|
|
default:
|
|
printf( "storeNewRhs: shouldn't be here\n");
|
|
break;
|
|
}
|
|
if ( pHNode->nodeType != CONTACT ) {
|
|
/* contribution to the x nodes */
|
|
rhs[ pHNode->psiEqn ] += pElem->epsRel * 0.5 * pElem->dyOverDx;
|
|
if ( pElem->elemType == SEMICON ) {
|
|
if ( !OneCarrier ) {
|
|
rhs[ pHNode->nEqn ] -= 0.5 * pElem->dy * pHEdge->dJnDpsiP1;
|
|
rhs[ pHNode->pEqn ] -= 0.5 * pElem->dy * pHEdge->dJpDpsiP1;
|
|
} else if ( OneCarrier == N_TYPE ) {
|
|
rhs[ pHNode->nEqn ] -= 0.5 * pElem->dy * pHEdge->dJnDpsiP1;
|
|
} else if ( OneCarrier == P_TYPE ) {
|
|
rhs[ pHNode->pEqn ] -= 0.5 * pElem->dy * pHEdge->dJpDpsiP1;
|
|
}
|
|
}
|
|
}
|
|
if ( pVNode->nodeType != CONTACT ) {
|
|
/* contribution to the y nodes */
|
|
rhs[ pVNode->psiEqn ] += pElem->epsRel * 0.5 * pElem->dxOverDy;
|
|
if ( pElem->elemType == SEMICON ) {
|
|
if ( !OneCarrier ) {
|
|
rhs[ pVNode->nEqn ] -= 0.5 * pElem->dx * pVEdge->dJnDpsiP1;
|
|
rhs[ pVNode->pEqn ] -= 0.5 * pElem->dx * pVEdge->dJpDpsiP1;
|
|
} else if ( OneCarrier == N_TYPE ) {
|
|
rhs[ pVNode->nEqn ] -= 0.5 * pElem->dx * pVEdge->dJnDpsiP1;
|
|
} else if ( OneCarrier == P_TYPE ) {
|
|
rhs[ pVNode->pEqn ] -= 0.5 * pElem->dx * pVEdge->dJpDpsiP1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|