ngspice/src/ciderlib/oned/onemesh.c

379 lines
9.3 KiB
C

/**********
Copyright 1992 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/macros.h"
#include "onedext.h"
#include "oneddefs.h"
extern void CiderLoaded(int);
/* Forward Declarations */
/* static void ONEresetEvalFlag(ONEdevice *); sjb - not used */
void
ONEbuildMesh(ONEdevice *pDevice, ONEcoord *pCoord, ONEdomain *pDomain,
ONEmaterial *pMaterial)
{
int index, i;
int elemType;
double xPos;
ONEcoord *pC;
ONEnode *pNode;
ONEdomain *pD;
ONEelem *pElem;
ONEmaterial *pM;
int poiEqn, numEqn;
ONEedge *pEdge;
ONEnode **nodeArray=NULL;
bool error = FALSE;
/* generate the work array for setting up nodes and elements */
XCALLOC(nodeArray, ONEnode *, 1 + pDevice->numNodes);
for (pC = pCoord; pC != NULL; pC = pC->next) {
xPos = pC->location;
XCALLOC(pNode, ONEnode, 1);
pNode->x = xPos;
pNode->nodeI = pC->number;
nodeArray[pNode->nodeI] = pNode;
}
/* mark the domain info on the nodes */
if (pDomain == NULL) {
fprintf(stderr, "Error: domains not defined for device\n");
exit(-1);
}
for (pD = pDomain; pD != NULL; pD = pD->next) {
for (pM = pMaterial; pM != NULL; pM = pM->next) {
if (pD->material == pM->id) {
break;
}
}
elemType = pM->type;
for (index = pD->ixLo; index <= pD->ixHi; index++) {
pNode = nodeArray[index];
if (!pNode->nodeType) {
pNode->nodeType = elemType;
}
}
}
/*
* check to see if a domain has been defined for all nodes. if not flag an
* error message
*/
for (index = 2; index < pDevice->numNodes; index++) {
pNode = nodeArray[index];
if (!pNode->nodeType) {
fprintf(stderr, "Error: No domain defined for node %d\n", pNode->nodeI);
error = TRUE;
}
}
if (error) {
/* nodes with undefined domains -- exit */
exit(-1);
}
/* mark the first and last nodes to be contact nodes */
nodeArray[1]->nodeType = CONTACT;
nodeArray[pDevice->numNodes]->nodeType = CONTACT;
/* generate the elements and the edges */
for (index = 1; index < pDevice->numNodes; index++) {
XCALLOC(pElem, ONEelem, 1);
XCALLOC(pEdge, ONEedge, 1);
pElem->pEdge = pEdge;
pElem->pLeftNode = nodeArray[index];
pElem->pRightNode = nodeArray[index + 1];
pDevice->elemArray[index] = pElem;
}
/* now setup the nodes to which an element belongs */
for (index = 1; index < pDevice->numNodes; index++) {
pElem = pDevice->elemArray[index];
pElem->pLeftNode->pRightElem = pElem;
pElem->pRightNode->pLeftElem = pElem;
if (index > 1) {
pElem->pLeftElem = pDevice->elemArray[index - 1];
}
if (index < pDevice->numNodes - 1) {
pElem->pRightElem = pDevice->elemArray[index + 1];
}
}
/* mark the domain info on the elements */
for (pD = pDomain; pD != NULL; pD = pD->next) {
for (pM = pMaterial; pM != NULL; pM = pM->next) {
if (pD->material == pM->id) {
break;
}
}
elemType = pM->type;
for (index = pD->ixLo; index < pD->ixHi; index++) {
pElem = pDevice->elemArray[index];
pElem->domain = pD->id;
pElem->elemType = elemType;
pElem->matlInfo = pM;
}
}
/* identify the interface nodes */
for (index = 2; index < pDevice->numNodes; index++) {
pNode = nodeArray[index];
if (pNode->pLeftElem->elemType !=
pNode->pRightElem->elemType) {
/* an interface node */
pNode->nodeType = INTERFACE;
}
}
/* now mark the nodes to be evaluated */
/* all interface nodes marked in silicon elements */
for (index = 1; index < pDevice->numNodes; index++) {
pElem = pDevice->elemArray[index];
pElem->dx = pElem->pRightNode->x - pElem->pLeftNode->x;
for (i = 0; i <= 1; i++) {
pNode = pElem->pNodes[i];
pElem->evalNodes[i] = FALSE;
if (pElem->elemType == INSULATOR) {
if ((!pNode->evaluated) &&
(pNode->nodeType != INTERFACE)) {
/* a non interface node in oxide domain */
pNode->evaluated = TRUE;
pElem->evalNodes[i] = TRUE;
}
}
if (pElem->elemType == SEMICON) {
if (!pNode->evaluated) {
pNode->evaluated = TRUE;
pElem->evalNodes[i] = TRUE;
}
}
}
}
/* set up the equation number for the nodes */
poiEqn = numEqn = 1;
for (index = 1; index < pDevice->numNodes; index++) {
pElem = pDevice->elemArray[index];
for (i = 0; i <= 1; i++) {
if (pElem->evalNodes[i]) {
pNode = pElem->pNodes[i];
if (pNode->nodeType != CONTACT) {
pNode->poiEqn = poiEqn++;
pNode->psiEqn = numEqn;
if (pElem->elemType == INSULATOR) {
numEqn += 1; /* only poisson's equation */
} else {
pNode->nEqn = numEqn + 1;
pNode->pEqn = numEqn + 2;
numEqn += 3;
}
} else { /* this is a contact node */
pNode->poiEqn = 0;
pNode->psiEqn = 0;
pNode->nEqn = 0;
pNode->pEqn = 0;
}
/*
* fprintf(stdout,"NODE: %d %d\n",pNode->nodeI,pNode->poiEqn);
*/
}
}
}
pDevice->dimEquil = poiEqn;
pDevice->dimBias = numEqn;
FREE(nodeArray);
{
CiderLoaded(1);
}
/*
* ONEprnMesh( pDevice );
*/
}
/*
* We have a separate function for this, so that the setup routines can reset
* the state pointers without rebuilding the entire mesh.
*/
void
ONEgetStatePointers(ONEdevice *pDevice, int *numStates)
{
int eIndex, nIndex;
ONEelem *pElem;
ONEnode *pNode;
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) {
pElem = pDevice->elemArray[eIndex];
for (nIndex = 0; nIndex <= 1; nIndex++) {
if (pElem->evalNodes[nIndex]) {
pNode = pElem->pNodes[nIndex];
pNode->nodeState = *numStates;
*numStates += ONEnumNodeStates;
}
}
pElem->pEdge->edgeState = *numStates;
*numStates += ONEnumEdgeStates;
}
}
/* adjust the base contact to be the position of maximum density */
/* index EB and BC are the indexes for the eb, bc junctions */
void
adjustBaseContact(ONEdevice *pDevice, int indexEB, int indexBC)
{
int index, i, newBaseIndex, midPoint;
double maxDensity;
ONEnode *pNode;
ONEelem *pElem;
ONEnode *pBaseNode = pDevice->elemArray[pDevice->baseIndex]->pNodes[0];
/* Initialize the base contact to be the center of the two junctions */
/* This should take care of uniform dopings. */
midPoint = (indexEB + indexBC) / 2;
newBaseIndex = midPoint;
if (pBaseNode->baseType == P_TYPE) {
maxDensity = pDevice->elemArray[midPoint]->pNodes[0]->pConc;
for (index = indexEB; index < indexBC; index++) {
pElem = pDevice->elemArray[index];
for (i = 0; i <= 1; i++) {
pNode = pElem->pNodes[i];
if (pNode->pConc > maxDensity) {
maxDensity = pNode->pConc;
newBaseIndex = index;
}
}
}
} else if (pBaseNode->baseType == N_TYPE) {
maxDensity = pDevice->elemArray[midPoint]->pNodes[0]->nConc;
for (index = indexEB; index < indexBC; index++) {
pElem = pDevice->elemArray[index];
for (i = 0; i <= 1; i++) {
pNode = pElem->pNodes[i];
if (pNode->nConc > maxDensity) {
maxDensity = pNode->nConc;
newBaseIndex = index;
}
}
}
} else {
printf("adjustBaseContact: unknown base type %d\n", pBaseNode->baseType);
}
/* at the conclusion of this loop have the point of max density */
if (pDevice->baseIndex != newBaseIndex) {
/* so change the position */
pNode = pDevice->elemArray[newBaseIndex]->pNodes[0];
pNode->baseType = pBaseNode->baseType;
pNode->vbe = pBaseNode->vbe;
pBaseNode->baseType = FALSE;
pBaseNode->vbe = 0.0;
pDevice->baseIndex = newBaseIndex;
}
}
void
NBJTjunctions(ONEdevice *pDevice, int *indexEB, int *indexBC)
{
int index;
double conc1, conc2;
bool findFirstJunction = TRUE;
bool notFound = TRUE;
for (index = 1; (index < pDevice->numNodes) && (notFound); index++) {
conc1 = pDevice->elemArray[index]->pNodes[0]->netConc;
conc2 = pDevice->elemArray[index]->pNodes[1]->netConc;
if ((conc1 * conc2 < 0.0) && (findFirstJunction)) {
*indexEB = index;
findFirstJunction = FALSE;
} else if ((conc1 * conc2 < 0.0) && (!findFirstJunction)) {
*indexBC = index;
notFound = FALSE;
}
}
if (notFound) {
fprintf(stderr, "BJT: Device does not have two junctions!\n");
exit(-1);
}
}
void
ONEprnMesh(ONEdevice *pDevice)
{
int eIndex, index;
ONEelem *pElem;
ONEnode *pNode;
char *name;
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) {
pElem = pDevice->elemArray[eIndex];
fprintf(stderr, "elem %5d:\n", eIndex);
for (index = 0; index <= 1; index++) {
if (pElem->evalNodes[index]) {
pNode = pElem->pNodes[index];
switch (pNode->nodeType) {
case SEMICON:
name = "semiconductor";
break;
case INSULATOR:
name = "insulator";
break;
case CONTACT:
name = "contact";
break;
case SCHOTTKY:
name = "schottky";
break;
case INTERFACE:
name = "interface";
break;
default:
name = "unknown";
break;
}
fprintf(stderr, "node %5d: %s %5d\n", index, name,
pNode->nodeI);
}
}
}
}
/* sjb - not used
static void
ONEresetEvalFlag(ONEdevice *pDevice)
{
int index, eIndex;
ONEelem *pElem;
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) {
pElem = pDevice->elemArray[eIndex];
for (index = 0; index <= 1; index++) {
pElem->pNodes[index]->evaluated = FALSE;
}
pElem->pEdge->evaluated = FALSE;
}
}
*/