627 lines
16 KiB
C
627 lines
16 KiB
C
/**********
|
|
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
|
|
**********/
|
|
|
|
#include "ngspice/ngspice.h"
|
|
#include "ngspice/numglobs.h"
|
|
#include "ngspice/numconst.h"
|
|
#include "ngspice/numenum.h"
|
|
#include "ngspice/twomesh.h"
|
|
#include "ngspice/twodev.h"
|
|
#include "ngspice/bool.h"
|
|
#include "twoddefs.h"
|
|
#include "twodext.h"
|
|
|
|
|
|
|
|
static void doMobCoeffs(TWOelem *, int);
|
|
static void resetEvalFlag(TWOdevice *pDevice);
|
|
|
|
|
|
void
|
|
TWObuildMesh(TWOdevice *pDevice, TWOdomain *pDomain,
|
|
TWOelectrode *pElectrode, TWOmaterial *pMaterial)
|
|
{
|
|
int xIndex, yIndex, eIndex, index;
|
|
int elemType;
|
|
TWOelem *pElem, *pElem1;
|
|
TWOnode *pNode, *pNode1, *pNextHNode, *pNextVNode, *pNextDNode;
|
|
TWOnode ***nodeArray = NULL;
|
|
TWOedge *pEdge;
|
|
TWOedge ***edgeArrayH = NULL, ***edgeArrayV = NULL;
|
|
TWOdomain *pD;
|
|
TWOelectrode *pE;
|
|
TWOmaterial *pM;
|
|
BOOLEAN interiorNode;
|
|
int poiEqn, numEqn, numElem, numNodes, numEdges;
|
|
int numXNodes = pDevice->numXNodes;
|
|
int numYNodes = pDevice->numYNodes;
|
|
double *xScale = pDevice->xScale;
|
|
double *yScale = pDevice->yScale;
|
|
#ifdef NOTDEF
|
|
FILE *meshFile;
|
|
#endif
|
|
|
|
/* Generate work arrays. */
|
|
XCALLOC(nodeArray, TWOnode **, 1 + numXNodes);
|
|
for (xIndex = 1; xIndex <= numXNodes; xIndex++) {
|
|
XCALLOC(nodeArray[xIndex], TWOnode *, 1 + numYNodes);
|
|
}
|
|
|
|
/* Generate the nodes. */
|
|
for (xIndex = 1; xIndex <= numXNodes; xIndex++) {
|
|
for (yIndex = 1; yIndex <= numYNodes; yIndex++) {
|
|
XCALLOC(pNode, TWOnode, 1);
|
|
pNode->nodeI = xIndex;
|
|
pNode->nodeJ = yIndex;
|
|
pNode->poiEqn = 0;
|
|
nodeArray[xIndex][yIndex] = pNode;
|
|
}
|
|
}
|
|
|
|
/* Mark the semiconductor/insulator domains. */
|
|
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 (xIndex = pD->ixLo; xIndex <= pD->ixHi; xIndex++) {
|
|
for (yIndex = pD->iyLo; yIndex <= pD->iyHi; yIndex++) {
|
|
pNode = nodeArray[xIndex][yIndex];
|
|
pNode->nodeType = elemType;
|
|
}
|
|
}
|
|
}
|
|
/* Now mark all the metallic domains */
|
|
for (pE = pElectrode; pE != NULL; pE = pE->next) {
|
|
for (xIndex = pE->ixLo; xIndex <= pE->ixHi; xIndex++) {
|
|
for (yIndex = pE->iyLo; yIndex <= pE->iyHi; yIndex++) {
|
|
pNode = nodeArray[xIndex][yIndex];
|
|
pNode->nodeType = CONTACT;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* Now mark as NULL any node in the interior of an electrode, i.e. none of
|
|
* its neighbors is a different material.
|
|
*/
|
|
for (xIndex = 1; xIndex <= numXNodes; xIndex++) {
|
|
for (yIndex = 1; yIndex <= numYNodes; yIndex++) {
|
|
pNode = nodeArray[xIndex][yIndex];
|
|
if (pNode->nodeType == CONTACT) {
|
|
interiorNode = TRUE;
|
|
if (xIndex > 1) {
|
|
pNode1 = nodeArray[xIndex - 1][yIndex];
|
|
if (pNode1->nodeType != 0
|
|
&& pNode1->nodeType != CONTACT) {
|
|
interiorNode = FALSE;
|
|
}
|
|
}
|
|
if (interiorNode && xIndex < numXNodes) {
|
|
pNode1 = nodeArray[xIndex + 1][yIndex];
|
|
if (pNode1->nodeType != 0
|
|
&& pNode1->nodeType != CONTACT) {
|
|
interiorNode = FALSE;
|
|
}
|
|
}
|
|
if (interiorNode && yIndex > 1) {
|
|
pNode1 = nodeArray[xIndex][yIndex - 1];
|
|
if (pNode1->nodeType != 0
|
|
&& pNode1->nodeType != CONTACT) {
|
|
interiorNode = FALSE;
|
|
}
|
|
}
|
|
if (interiorNode && yIndex < numYNodes) {
|
|
pNode1 = nodeArray[xIndex][yIndex + 1];
|
|
if (pNode1->nodeType != 0
|
|
&& pNode1->nodeType != CONTACT) {
|
|
interiorNode = FALSE;
|
|
}
|
|
}
|
|
if (interiorNode) {
|
|
pNode->nodeType = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Delete nodes that do not belong to any domain. */
|
|
numNodes = 0;
|
|
for (yIndex = 1; yIndex <= numYNodes; yIndex++) {
|
|
for (xIndex = 1; xIndex <= numXNodes; xIndex++) {
|
|
pNode = nodeArray[xIndex][yIndex];
|
|
if (pNode->nodeType == 0) {
|
|
/* This node doesn't belong to a domain so delete it. */
|
|
nodeArray[xIndex][yIndex] = NULL;
|
|
FREE(pNode);
|
|
} else {
|
|
numNodes++;
|
|
}
|
|
}
|
|
}
|
|
pDevice->numNodes = numNodes;
|
|
|
|
/* Now relabel any remaining nodes that are part of electrodes. */
|
|
setupContacts(pDevice, pElectrode, nodeArray);
|
|
|
|
/* Done with the nodes. Now construct the elements and the edges. */
|
|
numEdges = 0;
|
|
|
|
/* Generate the horizontal edges and store in a work array. */
|
|
XCALLOC(edgeArrayH, TWOedge **, numXNodes);
|
|
for (xIndex = 1; xIndex < numXNodes; xIndex++) {
|
|
XCALLOC(edgeArrayH[xIndex], TWOedge *, 1 + numYNodes);
|
|
}
|
|
for (yIndex = 1; yIndex <= numYNodes; yIndex++) {
|
|
for (xIndex = 1; xIndex < numXNodes; xIndex++) {
|
|
pNode = nodeArray[xIndex][yIndex];
|
|
pNextHNode = nodeArray[xIndex + 1][yIndex];
|
|
if (pNode != NULL &&
|
|
pNextHNode != NULL) {
|
|
XCALLOC(pEdge, TWOedge, 1);
|
|
numEdges++;
|
|
edgeArrayH[xIndex][yIndex] = pEdge;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Generate the vertical edges and store in a work array. */
|
|
XCALLOC(edgeArrayV, TWOedge **, 1 + numXNodes);
|
|
for (xIndex = 1; xIndex <= numXNodes; xIndex++) {
|
|
XCALLOC(edgeArrayV[xIndex], TWOedge *, numYNodes);
|
|
}
|
|
for (xIndex = 1; xIndex <= numXNodes; xIndex++) {
|
|
for (yIndex = 1; yIndex < numYNodes; yIndex++) {
|
|
pNode = nodeArray[xIndex][yIndex];
|
|
pNextVNode = nodeArray[xIndex][yIndex + 1];
|
|
if (pNode != NULL &&
|
|
pNextVNode != NULL) {
|
|
XCALLOC(pEdge, TWOedge, 1);
|
|
numEdges++;
|
|
edgeArrayV[xIndex][yIndex] = pEdge;
|
|
}
|
|
}
|
|
}
|
|
pDevice->numEdges = numEdges;
|
|
|
|
/* Now construct and count the elements and store the node/edge pointers. */
|
|
numElem = 1;
|
|
for (yIndex = 1; yIndex < numYNodes; yIndex++) {
|
|
for (xIndex = 1; xIndex < numXNodes; xIndex++) {
|
|
pNode = nodeArray[xIndex][yIndex];
|
|
pNextHNode = nodeArray[xIndex + 1][yIndex];
|
|
pNextVNode = nodeArray[xIndex][yIndex + 1];
|
|
pNextDNode = nodeArray[xIndex + 1][yIndex + 1];
|
|
if (pNode != NULL &&
|
|
pNextHNode != NULL &&
|
|
pNextVNode != NULL &&
|
|
pNextDNode != NULL) {
|
|
numElem++;
|
|
XCALLOC(pElem, TWOelem, 1);
|
|
pElem->pTLNode = pNode;
|
|
pElem->pTRNode = pNextHNode;
|
|
pElem->pBLNode = pNextVNode;
|
|
pElem->pBRNode = pNextDNode;
|
|
pElem->pTopEdge = edgeArrayH[xIndex][yIndex];
|
|
pElem->pBotEdge = edgeArrayH[xIndex][yIndex + 1];
|
|
pElem->pLeftEdge = edgeArrayV[xIndex][yIndex];
|
|
pElem->pRightEdge = edgeArrayV[xIndex + 1][yIndex];
|
|
pDevice->elemArray[xIndex][yIndex] = pElem;
|
|
} else {
|
|
pDevice->elemArray[xIndex][yIndex] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Create and pack the 1D element array. */
|
|
pDevice->numElems = numElem - 1;
|
|
XCALLOC(pDevice->elements, TWOelem *, 1 + numElem);
|
|
numElem = 1;
|
|
for (yIndex = 1; yIndex < numYNodes; yIndex++) {
|
|
for (xIndex = 1; xIndex < numXNodes; xIndex++) {
|
|
pElem = pDevice->elemArray[xIndex][yIndex];
|
|
if (pElem != NULL) {
|
|
pDevice->elements[numElem++] = pElem;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Now create back links from elements to nodes. */
|
|
for (yIndex = 1; yIndex < numYNodes; yIndex++) {
|
|
for (xIndex = 1; xIndex < numXNodes; xIndex++) {
|
|
pElem = pDevice->elemArray[xIndex][yIndex];
|
|
if (pElem != NULL) {
|
|
pElem->pTLNode->pBRElem = pElem;
|
|
pElem->pTRNode->pBLElem = pElem;
|
|
pElem->pBLNode->pTRElem = pElem;
|
|
pElem->pBRNode->pTLElem = pElem;
|
|
if (xIndex > 1) {
|
|
pElem->pLeftElem = pDevice->elemArray[xIndex-1][yIndex];
|
|
}
|
|
if (xIndex < numXNodes - 1) {
|
|
pElem->pRightElem = pDevice->elemArray[xIndex+1][yIndex];
|
|
}
|
|
if (yIndex > 1) {
|
|
pElem->pTopElem = pDevice->elemArray[xIndex][yIndex-1];
|
|
}
|
|
if (yIndex < numYNodes - 1) {
|
|
pElem->pBotElem = pDevice->elemArray[xIndex][yIndex+1];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Establish the element types using domain info. */
|
|
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 (yIndex = pD->iyLo; yIndex < pD->iyHi; yIndex++) {
|
|
for (xIndex = pD->ixLo; xIndex < pD->ixHi; xIndex++) {
|
|
pElem = pDevice->elemArray[xIndex][yIndex];
|
|
if (pElem != NULL) {
|
|
pElem->domain = pD->id;
|
|
pElem->elemType = elemType;
|
|
pElem->matlInfo = pM;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Establish the edge types. */
|
|
for (yIndex = 1; yIndex < numYNodes; yIndex++) {
|
|
for (xIndex = 1; xIndex < numXNodes; xIndex++) {
|
|
pElem = pDevice->elemArray[xIndex][yIndex];
|
|
if (pElem != NULL) {
|
|
for (index = 0; index <= 3; index++) {
|
|
pEdge = pElem->pEdges[index];
|
|
pNode = pElem->pNodes[index];
|
|
pNode1 = pElem->pNodes[(index + 1) % 4];
|
|
pElem1 = pNode1->pElems[index];
|
|
|
|
if (pNode->nodeType == CONTACT &&
|
|
pNode1->nodeType == CONTACT) {
|
|
/* Contact edge */
|
|
pEdge->edgeType = CONTACT;
|
|
} else if (pNode->nodeType == SCHOTTKY &&
|
|
pNode1->nodeType == SCHOTTKY) {
|
|
/* Schottky edge */
|
|
pEdge->edgeType = SCHOTTKY;
|
|
} else if (pElem1 == NULL) {
|
|
/* Neumann edge */
|
|
pEdge->edgeType = pElem->elemType;
|
|
} else if (pElem->elemType != pElem1->elemType) {
|
|
/* Interface edge */
|
|
pEdge->edgeType = INTERFACE;
|
|
} else { /* Ignore heterojnxns for now */
|
|
/* Heterojunction or Homojunction edge */
|
|
pEdge->edgeType = pElem->elemType;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
resetEvalFlag(pDevice);
|
|
/* Set evaluation flags on nodes and edges. */
|
|
/* Assign the dx and dy terms in the elements while we're at it. */
|
|
for (yIndex = 1; yIndex < numYNodes; yIndex++) {
|
|
for (xIndex = 1; xIndex < numXNodes; xIndex++) {
|
|
pElem = pDevice->elemArray[xIndex][yIndex];
|
|
if (pElem != NULL) {
|
|
pElem->dx = xScale[xIndex + 1] - xScale[xIndex];
|
|
pElem->dy = yScale[yIndex + 1] - yScale[yIndex];
|
|
pElem->dxOverDy = pElem->dx / pElem->dy;
|
|
pElem->dyOverDx = pElem->dy / pElem->dx;
|
|
|
|
/*
|
|
* Semiconductor elements take precedence over Insulator elements, so
|
|
* set them up first.
|
|
*/
|
|
for (index = 0; index <= 3; index++) {
|
|
if (pElem->elemType == SEMICON) {
|
|
pNode = pElem->pNodes[index];
|
|
if (!pNode->evaluated) {
|
|
pNode->evaluated = TRUE;
|
|
pElem->evalNodes[index] = TRUE;
|
|
} else {
|
|
pElem->evalNodes[index] = FALSE;
|
|
}
|
|
pEdge = pElem->pEdges[index];
|
|
if (!pEdge->evaluated) {
|
|
pEdge->evaluated = TRUE;
|
|
pElem->evalEdges[index] = TRUE;
|
|
} else {
|
|
pElem->evalEdges[index] = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Do a second setup pass for Insulator elements */
|
|
/* Do mobility coefficients now, because we set up dx and dy
|
|
* in the previous pass
|
|
*/
|
|
for (yIndex = 1; yIndex < numYNodes; yIndex++) {
|
|
for (xIndex = 1; xIndex < numXNodes; xIndex++) {
|
|
pElem = pDevice->elemArray[xIndex][yIndex];
|
|
if (pElem != NULL) {
|
|
pElem->direction = 0;
|
|
pElem->channel = 0;
|
|
pElem->surface = FALSE;
|
|
for (index = 0; index <= 3; index++) {
|
|
if (pElem->elemType == SEMICON) {
|
|
doMobCoeffs( pElem, index );
|
|
} else if (pElem->elemType == INSULATOR) {
|
|
pNode = pElem->pNodes[index];
|
|
if (!pNode->evaluated) {
|
|
pNode->evaluated = TRUE;
|
|
pElem->evalNodes[index] = TRUE;
|
|
} else {
|
|
pElem->evalNodes[index] = FALSE;
|
|
}
|
|
pEdge = pElem->pEdges[index];
|
|
if (!pEdge->evaluated) {
|
|
pEdge->evaluated = TRUE;
|
|
pElem->evalEdges[index] = TRUE;
|
|
} else {
|
|
pElem->evalEdges[index] = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Set up the equation numbers for nodes. */
|
|
poiEqn = numEqn = 1;
|
|
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) {
|
|
/* First assign potential equation numbers */
|
|
if (pNode->nodeType != SCHOTTKY) {
|
|
pNode->poiEqn = poiEqn++;
|
|
pNode->psiEqn = numEqn++;
|
|
}
|
|
/* Now assign carrier-concentration equation numbers */
|
|
if (pElem->elemType == INSULATOR) {
|
|
pNode->nEqn = 0;
|
|
pNode->pEqn = 0;
|
|
} else {
|
|
if (OneCarrier) {
|
|
/* n and p get same number */
|
|
pNode->nEqn = numEqn;
|
|
pNode->pEqn = numEqn++;
|
|
} else {
|
|
pNode->nEqn = numEqn++;
|
|
pNode->pEqn = numEqn++;
|
|
}
|
|
}
|
|
} else { /* This is a contact node. */
|
|
pNode->poiEqn = 0;
|
|
pNode->psiEqn = 0;
|
|
pNode->nEqn = 0;
|
|
pNode->pEqn = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
pDevice->dimEquil = poiEqn;
|
|
pDevice->dimBias = numEqn;
|
|
|
|
/* Open and Print Mesh Output File for Debugging */
|
|
/* Nuked from release version */
|
|
#ifdef NOTDEF
|
|
if (!(meshFile = fopen("mesh.out", "w"))) {
|
|
perror("mesh.out");
|
|
exit(-1);
|
|
}
|
|
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];
|
|
fprintf(meshFile, "node: %5d %5d %5d %5d\n", pNode->nodeI,
|
|
pNode->nodeJ, pNode->poiEqn, pNode->psiEqn);
|
|
}
|
|
}
|
|
}
|
|
fflush(meshFile);
|
|
fclose(meshFile);
|
|
#endif
|
|
|
|
/* Delete work arrays. */
|
|
for (xIndex = 1; xIndex <= numXNodes; xIndex++) {
|
|
FREE(nodeArray[xIndex]);
|
|
FREE(edgeArrayV[xIndex]);
|
|
}
|
|
for (xIndex = 1; xIndex < numXNodes; xIndex++) {
|
|
FREE(edgeArrayH[xIndex]);
|
|
}
|
|
FREE(nodeArray);
|
|
FREE(edgeArrayV);
|
|
FREE(edgeArrayH);
|
|
|
|
/*
|
|
* TWOprnMesh( pDevice );
|
|
*/
|
|
}
|
|
|
|
void
|
|
TWOprnMesh(TWOdevice *pDevice)
|
|
{
|
|
int eIndex, index;
|
|
TWOelem *pElem;
|
|
TWOnode *pNode;
|
|
TWOedge *pEdge;
|
|
char *name;
|
|
|
|
for (eIndex = 1; eIndex <= pDevice->numElems; eIndex++) {
|
|
pElem = pDevice->elements[eIndex];
|
|
fprintf(stderr, "elem %5d:\n", eIndex);
|
|
for (index = 0; index <= 3; 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 %5d\n", index, name,
|
|
pNode->nodeI, pNode->nodeJ);
|
|
}
|
|
if (pElem->evalEdges[index]) {
|
|
pEdge = pElem->pEdges[index];
|
|
switch (pEdge->edgeType) {
|
|
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, "edge %5d: %s\n", index, name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* We have a separate function for this, so that the setup routines can
|
|
* reset the state pointers without rebuilding the entire mesh.
|
|
*/
|
|
void
|
|
TWOgetStatePointers(TWOdevice *pDevice, int *numStates)
|
|
{
|
|
int eIndex, index;
|
|
TWOelem *pElem;
|
|
TWOnode *pNode;
|
|
TWOedge *pEdge;
|
|
|
|
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];
|
|
pNode->nodeState = *numStates;
|
|
*numStates += TWOnumNodeStates;
|
|
}
|
|
if (pElem->evalEdges[index]) {
|
|
pEdge = pElem->pEdges[index];
|
|
pEdge->edgeState = *numStates;
|
|
*numStates += TWOnumEdgeStates;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This function computes the percentages of the total semiconductor
|
|
* width of an edge on the negative and positive sides of the edge
|
|
*/
|
|
static void
|
|
doMobCoeffs(TWOelem *pElem, int index)
|
|
{
|
|
TWOelem *pNElem;
|
|
TWOedge *pEdge;
|
|
double dl1 = 0.0, dl2 = 0.0;
|
|
|
|
pNElem = pElem->pElems[ index ];
|
|
pEdge = pElem->pEdges[ index ];
|
|
|
|
/* If neighbor is not SEMICON, assign and return */
|
|
if ( (pNElem == NULL) || (pNElem->elemType == INSULATOR) ) {
|
|
if ( index == 0 || index == 3 ) {
|
|
pEdge->kNeg = 0.0;
|
|
pEdge->kPos = 1.0;
|
|
} else {
|
|
pEdge->kNeg = 1.0;
|
|
pEdge->kPos = 0.0;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* Find appropriate dimensions of the elements */
|
|
switch ( index ) {
|
|
case 0:
|
|
dl1 = pNElem->dy;
|
|
dl2 = pElem->dy;
|
|
break;
|
|
case 1:
|
|
dl1 = pElem->dx;
|
|
dl2 = pNElem->dx;
|
|
break;
|
|
case 2:
|
|
dl1 = pElem->dy;
|
|
dl2 = pNElem->dy;
|
|
break;
|
|
case 3:
|
|
dl1 = pNElem->dx;
|
|
dl2 = pElem->dx;
|
|
break;
|
|
}
|
|
|
|
/* Assign coefficients */
|
|
pEdge->kNeg = dl1 / (dl1 + dl2);
|
|
pEdge->kPos = dl2 / (dl1 + dl2);
|
|
|
|
}
|
|
|
|
static void
|
|
resetEvalFlag(TWOdevice *pDevice)
|
|
{
|
|
int index, eIndex;
|
|
TWOelem *pElem;
|
|
|
|
for (eIndex = 1; eIndex <= pDevice->numElems; eIndex++) {
|
|
pElem = pDevice->elements[eIndex];
|
|
for (index = 0; index <= 3; index++) {
|
|
pElem->pNodes[index]->evaluated = FALSE;
|
|
pElem->pEdges[index]->evaluated = FALSE;
|
|
}
|
|
}
|
|
}
|