⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 onesolve.c

📁 spice中支持多层次元件模型仿真的可单独运行的插件源码
💻 C
📖 第 1 页 / 共 3 页
字号:
/**********Copyright 1991 Regents of the University of California.  All rights reserved.Author:	1987 Kartikeya Mayaram, U. C. Berkeley CAD GroupAuthor:	1991 David A. Gates, U. C. Berkeley CAD Group**********//* * Functions needed to calculate solutions for 1D devices. */#include <math.h>#include "numglobs.h"#include "numenum.h"#include "nummacs.h"#include "onedev.h"#include "onemesh.h"#include "spmatrix.h"#include "ifsim.h"extern IFfrontEnd *SPfrontEnd;/* External Declarations */extern void ONEQjacBuild(), ONEQcommonTerms(), ONEQrhsLoad(), ONEQsysLoad();extern void ONE_jacBuild(), ONE_commonTerms(), ONE_rhsLoad(), ONE_sysLoad();extern void ONE_jacLoad();extern BOOLEAN foundError();extern double oneNorm(), l2Norm(), maxNorm(), predict();extern void computePredCoeff();/* Forward Declarations */void ONEdcSolve();BOOLEAN ONEdeltaConverged();int ONEnewDelta();void ONEstoreNeutralGuess();void ONEstoreEquilibGuess();void ONEstoreInitialGuess();double ONEnuNorm();void ONEjacCheck();/* Globally available Debugging Flags lurking here */BOOLEAN ONEdcDebug = TRUE;BOOLEAN ONEtranDebug = TRUE;BOOLEAN ONEjacDebug = FALSE;/* The iteration driving loop and convergence check */voidONEdcSolve(pDevice, iterationLimit, newSolver, tranAnalysis, info)  ONEdevice *pDevice;  int iterationLimit;  BOOLEAN newSolver;  BOOLEAN tranAnalysis;  ONEtranInfo *info;{  ONEnode *pNode;  ONEelem *pElem;  int index, eIndex, error;  int timesConverged = 0, negConc = FALSE;  int size = pDevice->numEqns;  BOOLEAN quitLoop;  BOOLEAN debug = FALSE;  double *rhs = pDevice->rhs;  double *intermediate = pDevice->copiedSolution;  double *solution = pDevice->dcSolution;  double *delta = pDevice->dcDeltaSolution;  double poissNorm, contNorm;  double startTime, totalStartTime;  double totalTime, loadTime, factorTime, solveTime, updateTime, checkTime;  double orderTime = 0.0;  quitLoop = FALSE;  debug = (NOT tranAnalysis AND ONEdcDebug) OR(tranAnalysis AND ONEtranDebug);  pDevice->iterationNumber = 0;  pDevice->converged = FALSE;  totalTime = loadTime = factorTime = solveTime = updateTime = checkTime = 0.0;  totalStartTime = SPfrontEnd->IFseconds();  if (debug) {    if (pDevice->poissonOnly) {      fprintf(stdout, "Equilibrium Solution:\n");    } else {      fprintf(stdout, "Bias Solution:\n");    }    fprintf(stdout, "Iteration  RHS Norm\n");  }  while (NOT(pDevice->converged OR pDevice->iterationNumber > iterationLimit	  OR quitLoop)) {    pDevice->iterationNumber++;    if ((NOT pDevice->poissonOnly) AND(iterationLimit > 0)	AND(NOT tranAnalysis)) {      ONEjacCheck(pDevice, tranAnalysis, info);    }    /* LOAD */    startTime = SPfrontEnd->IFseconds();    if (pDevice->poissonOnly) {      ONEQsysLoad(pDevice);    } else {      ONE_sysLoad(pDevice, tranAnalysis, info);    }    pDevice->rhsNorm = maxNorm(rhs, size);    loadTime += SPfrontEnd->IFseconds() - startTime;    if (debug) {      fprintf(stdout, "%7d   %11.4e%s\n",	  pDevice->iterationNumber - 1, pDevice->rhsNorm,	  negConc ? "   negative conc encountered" : "");      negConc = FALSE;    }    /* FACTOR */    startTime = SPfrontEnd->IFseconds();    error = spFactor(pDevice->matrix);    factorTime += SPfrontEnd->IFseconds() - startTime;    if (newSolver) {      if (pDevice->iterationNumber == 1) {	orderTime = factorTime;      } else if (pDevice->iterationNumber == 2) {	orderTime -= factorTime - orderTime;	factorTime -= orderTime;	if (pDevice->poissonOnly) {	  pDevice->pStats->orderTime[STAT_SETUP] += orderTime;	} else {	  pDevice->pStats->orderTime[STAT_DC] += orderTime;	}	newSolver = FALSE;      }    }    if (foundError(error)) {      if (error == spSINGULAR) {	int badRow, badCol;	spWhereSingular(pDevice->matrix, &badRow, &badCol);	printf("*****  singular at (%d,%d)\n", badRow, badCol);      }      /* Should probably try to recover now, but we'll punt instead. */      exit(-1);    }    /* SOLVE */    startTime = SPfrontEnd->IFseconds();    spSolve(pDevice->matrix, rhs, delta, NIL(spREAL), NIL(spREAL));    solveTime += SPfrontEnd->IFseconds() - startTime;    /* UPDATE */    startTime = SPfrontEnd->IFseconds();    /*     * Use norm reducing Newton method only for DC bias solutions. Since norm     * reducing can get trapped by numerical errors, turn it off when we are     * somewhat close to the solution.     */    if ((NOT pDevice->poissonOnly) AND(iterationLimit > 0)	AND(NOT tranAnalysis) AND(pDevice->rhsNorm > 1e-6)) {      error = ONEnewDelta(pDevice, tranAnalysis, info);      if (error) {	pDevice->converged = FALSE;	quitLoop = TRUE;	updateTime += SPfrontEnd->IFseconds() - startTime;	continue;      }    }    for (index = 1; index <= size; index++) {      solution[index] += delta[index];    }    updateTime += SPfrontEnd->IFseconds() - startTime;    /* CHECK CONVERGENCE */    startTime = SPfrontEnd->IFseconds();    /* Check if updates have gotten sufficiently small. */    if (pDevice->iterationNumber ISNOT 1) {      /*       * pDevice->converged = ONEdeltaConverged(pDevice);       */      pDevice->converged = ONEpsiDeltaConverged(pDevice, &negConc);    }    /* Check if the rhs residual is smaller than abstol. */    if (pDevice->converged AND(NOT pDevice->poissonOnly)	AND(NOT tranAnalysis)) {      ONE_rhsLoad(pDevice, tranAnalysis, info);      pDevice->rhsNorm = maxNorm(rhs, size);      if (pDevice->rhsNorm > pDevice->abstol) {	pDevice->converged = FALSE;      }      if ((++timesConverged >= 2)	  AND(pDevice->rhsNorm < 1e3 * pDevice->abstol)) {	pDevice->converged = TRUE;      } else if (timesConverged >= 5) {	pDevice->converged = FALSE;	quitLoop = TRUE;      }    } else if (pDevice->converged AND pDevice->poissonOnly) {      ONEQrhsLoad(pDevice);      pDevice->rhsNorm = maxNorm(rhs, size);      if (pDevice->rhsNorm > pDevice->abstol) {	pDevice->converged = FALSE;      }      if (++timesConverged >= 5) {	pDevice->converged = TRUE;      }    }    /* Check if any of the carrier concentrations are negative. */    if (pDevice->converged AND(NOT pDevice->poissonOnly)) {      /* Clear garbage entry since carrier-free elements reference it */      solution[0] = 0.0;      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 (solution[pNode->nEqn] < 0.0) {	      pDevice->converged = FALSE;	      negConc = TRUE;	      if (tranAnalysis) {		quitLoop = TRUE;	      } else {		solution[pNode->nEqn] = 0.0;	      }	    }	    if (solution[pNode->pEqn] < 0.0) {	      pDevice->converged = FALSE;	      negConc = TRUE;	      if (tranAnalysis) {		quitLoop = TRUE;	      } else {		solution[pNode->pEqn] = 0.0;	      }	    }	  }	}      }      /* Set to a consistent state if negative conc was encountered. */      if (NOT pDevice->converged) {	ONE_rhsLoad(pDevice, tranAnalysis, info);	pDevice->rhsNorm = maxNorm(rhs, size);      }    }    checkTime += SPfrontEnd->IFseconds() - startTime;  }  totalTime += SPfrontEnd->IFseconds() - totalStartTime;  if (tranAnalysis) {    pDevice->pStats->loadTime[STAT_TRAN] += loadTime;    pDevice->pStats->factorTime[STAT_TRAN] += factorTime;    pDevice->pStats->solveTime[STAT_TRAN] += solveTime;    pDevice->pStats->updateTime[STAT_TRAN] += updateTime;    pDevice->pStats->checkTime[STAT_TRAN] += checkTime;    pDevice->pStats->numIters[STAT_TRAN] += pDevice->iterationNumber;  } else if (pDevice->poissonOnly) {    pDevice->pStats->loadTime[STAT_SETUP] += loadTime;    pDevice->pStats->factorTime[STAT_SETUP] += factorTime;    pDevice->pStats->solveTime[STAT_SETUP] += solveTime;    pDevice->pStats->updateTime[STAT_SETUP] += updateTime;    pDevice->pStats->checkTime[STAT_SETUP] += checkTime;    pDevice->pStats->numIters[STAT_SETUP] += pDevice->iterationNumber;  } else {    pDevice->pStats->loadTime[STAT_DC] += loadTime;    pDevice->pStats->factorTime[STAT_DC] += factorTime;    pDevice->pStats->solveTime[STAT_DC] += solveTime;    pDevice->pStats->updateTime[STAT_DC] += updateTime;    pDevice->pStats->checkTime[STAT_DC] += checkTime;    pDevice->pStats->numIters[STAT_DC] += pDevice->iterationNumber;  }  if (debug) {    if (NOT tranAnalysis) {      pDevice->rhsNorm = maxNorm(rhs, size);      fprintf(stdout, "%7d   %11.4e%s\n",	pDevice->iterationNumber, pDevice->rhsNorm,	negConc ? "   negative conc in solution" : "");    }    if (pDevice->converged) {      if (NOT pDevice->poissonOnly) {	rhs[0] = 0.0;	poissNorm = contNorm = 0.0;	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];	      poissNorm = MAX(poissNorm, ABS(rhs[pNode->psiEqn]));	      contNorm = MAX(contNorm, ABS(rhs[pNode->nEqn]));	      contNorm = MAX(contNorm, ABS(rhs[pNode->pEqn]));	    }	  }	}	fprintf(stdout,	    "Residual: %11.4e C/um^2 poisson, %11.4e A/um^2 continuity\n",	    poissNorm * EpsNorm * ENorm * 1e-8,	    contNorm * JNorm * 1e-8);      } else {	fprintf(stdout, "Residual: %11.4e C/um^2 poisson\n",	    pDevice->rhsNorm * EpsNorm * ENorm * 1e-8);      }    }  }}/* * A function that checks convergence based on the convergence of the quasi * Fermi levels. In theory, this should work better than the one currently * being used since we are always looking at potentials: (psi, phin, phip). */BOOLEANONEpsiDeltaConverged(pDevice, pNegConc)  ONEdevice *pDevice;  int *pNegConc;{  int index, nIndex, eIndex;  ONEnode *pNode;  ONEelem *pElem;  double xOld, xNew, xDelta, tol;  double psi, newPsi, nConc, pConc, newN, newP;  double phiN, phiP, newPhiN, newPhiP;  BOOLEAN converged = TRUE;  /* Equilibrium solution. */  if (pDevice->poissonOnly) {    for (index = 1; index <= pDevice->numEqns; index++) {      xOld = pDevice->dcSolution[index];      xDelta = pDevice->dcDeltaSolution[index];      xNew = xOld + xDelta;      tol = pDevice->abstol + pDevice->reltol * MAX(ABS(xOld), ABS(xNew));      if (ABS(xDelta) > tol) {	converged = FALSE;	goto done;      }    }    return (converged);  }  /* Bias solution. Check convergence on psi, phin, phip. */  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];	if (pNode->nodeType ISNOT CONTACT) {	  /* check convergence on psi */	  xOld = pDevice->dcSolution[pNode->psiEqn];	  xDelta = pDevice->dcDeltaSolution[pNode->psiEqn];	  xNew = xOld + xDelta;	  tol = pDevice->abstol +	      pDevice->reltol * MAX(ABS(xOld), ABS(xNew));	  if (ABS(xDelta) > tol) {	    converged = FALSE;	    goto done;	  }	}	/* check convergence on phin and phip */	if (pElem->elemType IS SEMICON AND pNode->nodeType ISNOT CONTACT) {	  psi = pDevice->dcSolution[pNode->psiEqn];	  nConc = pDevice->dcSolution[pNode->nEqn];	  pConc = pDevice->dcSolution[pNode->pEqn];	  newPsi = psi + pDevice->dcDeltaSolution[pNode->psiEqn];	  newN = nConc + pDevice->dcDeltaSolution[pNode->nEqn];	  newP = pConc + pDevice->dcDeltaSolution[pNode->pEqn];	  if (newN <= 0.0 OR newP <= 0.0) {	    *pNegConc = TRUE;	    converged = FALSE;	    goto done;	  }	  phiN = psi - log(nConc / pNode->nie);	  phiP = psi + log(pConc / pNode->nie);	  newPhiN = newPsi - log(newN / pNode->nie);	  newPhiP = newPsi + log(newP / pNode->nie);	  tol = pDevice->abstol + pDevice->reltol * MAX(ABS(phiN), ABS(newPhiN));	  if (ABS(newPhiN - phiN) > tol) {	    converged = FALSE;	    goto done;	  }	  tol = pDevice->abstol + pDevice->reltol * MAX(ABS(phiP), ABS(newPhiP));

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -