ngspice/src/ciderlib/input/dopset.c

379 lines
9.8 KiB
C

/**********
Copyright 1991 Regents of the University of California. All rights reserved.
Author: 1991 David A. Gates, U. C. Berkeley CAD Group
Modifed: 2001 Paolo Nenzi
**********/
#include "ngspice/carddefs.h"
#include "ngspice/cidersupt.h"
#include "ngspice/cktdefs.h"
#include "ngspice/ciderinp.h"
#include "ngspice/cpextern.h"
#include "ngspice/dopdefs.h"
#include "ngspice/meshext.h"
#include "ngspice/ngspice.h"
#include "ngspice/numenum.h"
#include "ngspice/profile.h"
#include "ngspice/gendev.h"
#include "ngspice/sperror.h"
#include "ngspice/suffix.h"
/*
* Name: DOPcheck
* Purpose: checks a list of DOPcards for input errors
* Formals: cardList: the list to check
* Returns: OK/E_PRIVATE
* Users: numerical device setup routines
* Calls: error message handler
*/
int
DOPcheck(DOPcard *cardList, MESHcoord *xMeshList, MESHcoord *yMeshList)
{
DOPcard *card;
int cardNum = 0;
int error = OK;
for ( card = cardList; card != NULL; card = card->DOPnextCard ) {
cardNum++;
if (!card->DOPdomainsGiven) {
card->DOPnumDomains = 0;
card->DOPdomains = NULL;
}
if (!card->DOPprofileTypeGiven) {
SPfrontEnd->IFerrorf( ERR_WARNING, "doping card %d does not specify profile type", cardNum );
error = E_PRIVATE;
} else switch (card->DOPprofileType) {
case DOP_UNIF:
if (!card->DOPconcGiven) {
SPfrontEnd->IFerrorf( ERR_WARNING, "doping card %d needs conc of uniform distribution", cardNum );
error = E_PRIVATE;
}
break;
case DOP_LINEAR:
if (!card->DOPconcGiven) {
SPfrontEnd->IFerrorf( ERR_WARNING, "doping card %d needs peak conc of linear distribution", cardNum );
error = E_PRIVATE;
}
break;
case DOP_GAUSS:
if (!card->DOPconcGiven) {
SPfrontEnd->IFerrorf( ERR_WARNING, "doping card %d needs peak conc of gaussian distribution", cardNum );
error = E_PRIVATE;
}
break;
case DOP_ERFC:
if (!card->DOPconcGiven) {
SPfrontEnd->IFerrorf( ERR_WARNING, "doping card %d needs peak conc of error-function distribution", cardNum );
error = E_PRIVATE;
}
break;
case DOP_EXP:
if (!card->DOPconcGiven) {
SPfrontEnd->IFerrorf( ERR_WARNING, "doping card %d needs peak conc of exponential distribution", cardNum );
error = E_PRIVATE;
}
break;
case DOP_SUPREM3:
case DOP_SUPASCII:
if (!card->DOPinFileGiven) {
SPfrontEnd->IFerrorf( ERR_WARNING, "doping card %d needs input-file name of suprem3 data", cardNum );
error = E_PRIVATE;
}
break;
case DOP_ASCII:
if (!card->DOPinFileGiven) {
SPfrontEnd->IFerrorf( ERR_WARNING, "doping card %d needs input-file name of ascii data", cardNum );
error = E_PRIVATE;
}
break;
default:
SPfrontEnd->IFerrorf( ERR_FATAL, "doping card %d has unrecognized profile type", cardNum );
error = E_NOTFOUND;
break;
}
if (!card->DOProtateLatGiven) {
card->DOProtateLat = FALSE;
}
if (!card->DOPlatProfileTypeGiven || card->DOProtateLat) {
card->DOPlatProfileType = card->DOPprofileType;
}
if (!card->DOPratioLatGiven) {
card->DOPratioLat = 1.0;
}
if (!card->DOPcharLenGiven) {
card->DOPcharLen = 1.0e-4; /* 1um in centimeters */
}
if (!card->DOPlocationGiven) {
card->DOPlocation = 0.0;
}
if (!card->DOPimpurityTypeGiven) {
card->DOPimpurityType = IMP_N_TYPE;
} else switch (card->DOPimpurityType) {
case DOP_BORON:
card->DOPimpurityType = IMP_BORON;
break;
case DOP_PHOSP:
card->DOPimpurityType = IMP_PHOSPHORUS;
break;
case DOP_ARSEN:
card->DOPimpurityType = IMP_ARSENIC;
break;
case DOP_ANTIM:
card->DOPimpurityType = IMP_ANTIMONY;
break;
case DOP_N_TYPE:
card->DOPimpurityType = IMP_N_TYPE;
break;
case DOP_P_TYPE:
card->DOPimpurityType = IMP_P_TYPE;
break;
default:
break;
}
if (!card->DOPaxisTypeGiven) {
if ( xMeshList && yMeshList ) { /* both lists are non-empty */
card->DOPaxisType = DOP_Y_AXIS;
} else if ( xMeshList ) { /* x-mesh list is non-empty */
card->DOPaxisType = DOP_X_AXIS;
} else if ( yMeshList ) { /* y-mesh list is non-empty */
card->DOPaxisType = DOP_Y_AXIS;
}
}
/* Return now if anything has failed */
if (error) return(error);
}
return(OK);
}
/*
* Name: DOPsetup
* Purpose: convert a list of DOPcard's to DOPprofile's
* Formals: cardList: list of cards to setup
* profileList: returns the list of DOPprofile's
* xMeshList: list of coordinates in the x mesh
* yMeshList: list of coordinates in the y mesh
* Returns: OK/E_PRIVATE
* Users: numerical devices
* Calls: DOPcheck
*/
int
DOPsetup(DOPcard *cardList, DOPprofile **profileList, DOPtable **tableList,
MESHcoord *xMeshList, MESHcoord *yMeshList)
{
DOPcard *card;
DOPprofile *newProfile = NULL, *endProfile;
int impurityId = 0;
double xMin, xMax, yMin, yMax;
double sign;
int error, xProfUnif, yProfUnif;
/* Initialize list of profiles */
*profileList = endProfile = NULL;
/* Check the card list */
if ((error = DOPcheck( cardList, xMeshList, yMeshList )) != 0) return( error );
/* Find the limits on locations */
MESHlBounds( xMeshList, &xMin, &xMax );
MESHlBounds( yMeshList, &yMin, &yMax );
for ( card = cardList; card != NULL; card = card->DOPnextCard ) {
if (*profileList == NULL) {
RALLOC( newProfile, DOPprofile, 1 );
*profileList = newProfile;
}
else {
RALLOC( newProfile->next, DOPprofile, 1 );
newProfile = newProfile->next;
}
newProfile->next = NULL;
newProfile->numDomains = card->DOPnumDomains;
if ( newProfile->numDomains > 0 ) {
int i;
RALLOC( newProfile->domains, int, newProfile->numDomains );
for ( i=0; i < newProfile->numDomains; i++ ) {
newProfile->domains[i] = card->DOPdomains[i];
}
}
else {
newProfile->domains = NULL;
}
if ( card->DOPimpurityType == IMP_P_TYPE ) {
sign = -1.0;
}
else {
sign = 1.0;
}
switch( card->DOPprofileType ) {
case DOP_UNIF:
newProfile->type = UNIF;
newProfile->CONC = sign * card->DOPconc;
break;
case DOP_LINEAR:
newProfile->type = LIN;
newProfile->CONC = sign * card->DOPconc;
break;
case DOP_GAUSS:
newProfile->type = GAUSS;
newProfile->CONC = sign * card->DOPconc;
break;
case DOP_ERFC:
newProfile->type = ERRFC;
newProfile->CONC = sign * card->DOPconc;
break;
case DOP_EXP:
newProfile->type = EXP;
newProfile->CONC = sign * card->DOPconc;
break;
case DOP_SUPREM3:
newProfile->type = LOOKUP;
if (readSupremData( card->DOPinFile, 0, card->DOPimpurityType,
tableList) != 0) {
(void) fprintf(cp_err, "Doping setup failed.\n");
return -1;
}
newProfile->IMPID = ++impurityId;
break;
case DOP_SUPASCII:
newProfile->type = LOOKUP;
if (readSupremData( card->DOPinFile, 1, card->DOPimpurityType,
tableList) != 0) {
(void) fprintf(cp_err, "Doping setup failed.\n");
return -1;
}
newProfile->IMPID = ++impurityId;
break;
case DOP_ASCII:
newProfile->type = LOOKUP;
if (readAsciiData(card->DOPinFile, card->DOPimpurityType,
tableList) != 0) {
(void) fprintf(cp_err, "Doping setup failed.\n");
return -1;
}
newProfile->IMPID = ++impurityId;
break;
default:
break;
}
switch( card->DOPlatProfileType ) {
case DOP_UNIF:
newProfile->latType = UNIF;
break;
case DOP_LINEAR:
newProfile->latType = LIN;
break;
case DOP_GAUSS:
newProfile->latType = GAUSS;
break;
case DOP_ERFC:
newProfile->latType = ERRFC;
break;
case DOP_EXP:
newProfile->latType = EXP;
break;
case DOP_SUPREM3:
case DOP_SUPASCII:
newProfile->latType = LOOKUP;
break;
case DOP_ASCII:
newProfile->latType = LOOKUP;
break;
default:
break;
}
newProfile->rotate = card->DOProtateLat;
newProfile->LOCATION = card->DOPlocation;
newProfile->CHAR_LENGTH = card->DOPcharLen;
newProfile->LAT_RATIO = card->DOPratioLat;
xProfUnif = yProfUnif = FALSE;
if (card->DOPaxisType == DOP_X_AXIS) {
newProfile->DIRECTION = X;
if (newProfile->type == UNIF) xProfUnif = TRUE;
if (newProfile->latType == UNIF) yProfUnif = TRUE;
}
else {
newProfile->DIRECTION = Y;
if (newProfile->type == UNIF) yProfUnif = TRUE;
if (newProfile->latType == UNIF) xProfUnif = TRUE;
}
/* Fill in x coordinates. Use defaults if necessary */
if (card->DOPxLowGiven && card->DOPxHighGiven) {
newProfile->X_LOW = card->DOPxLow;
newProfile->X_HIGH = card->DOPxHigh;
}
else if (card->DOPxLowGiven) {
newProfile->X_LOW = card->DOPxLow;
if (xProfUnif) {
newProfile->X_HIGH = xMax;
}
else {
newProfile->X_HIGH = newProfile->X_LOW;
}
}
else if (card->DOPxHighGiven) {
newProfile->X_HIGH = card->DOPxHigh;
if (xProfUnif) {
newProfile->X_LOW = xMin;
}
else {
newProfile->X_LOW = newProfile->X_HIGH;
}
}
else {
if (xProfUnif) {
newProfile->X_LOW = xMin;
newProfile->X_HIGH = xMax;
}
else {
newProfile->X_LOW = 0.5 * (xMin + xMax);
newProfile->X_HIGH = 0.5 * (xMin + xMax);
}
}
/* Fill in y coordinates. Use defaults if necessary */
if (card->DOPyLowGiven && card->DOPyHighGiven) {
newProfile->Y_LOW = card->DOPyLow;
newProfile->Y_HIGH = card->DOPyHigh;
}
else if (card->DOPyLowGiven) {
newProfile->Y_LOW = card->DOPyLow;
if (yProfUnif) {
newProfile->Y_HIGH = yMax;
}
else {
newProfile->Y_HIGH = newProfile->Y_LOW;
}
}
else if (card->DOPyHighGiven) {
newProfile->Y_HIGH = card->DOPyHigh;
if (xProfUnif) {
newProfile->Y_LOW = yMin;
}
else {
newProfile->Y_LOW = newProfile->Y_HIGH;
}
}
else {
if (yProfUnif) {
newProfile->Y_LOW = yMin;
newProfile->Y_HIGH = yMax;
}
else {
newProfile->Y_LOW = 0.5 * (yMin + yMax);
newProfile->Y_HIGH = 0.5 * (yMin + yMax);
}
}
}
return( OK );
}