/**********
Copyright 1991 Regents of the University of California.  All rights reserved.
Author:	1991 David A. Gates, U. C. Berkeley CAD Group
Modified: 2001 Paolo Nenzi
**********/

#include "ngspice.h"
#include "numcards.h"
#include "numgen.h"
#include "dopdefs.h"
#include "devdefs.h"
#include "sperror.h"
#include "suffix.h"

#define UM_TO_CM 1.0e-4

extern int DOPnewCard(void**,void*);
extern int DOPparam(int,IFvalue*,void*);


IFparm DOPpTable[] = {
  IP("domains",	DOP_DOMAIN,	IF_INTVEC,	"Domain(s) to dope"),
  IP("uniform",	DOP_UNIF,	IF_FLAG,	"Uniform doping"),
  IP("linear",	DOP_LINEAR,	IF_FLAG,	"Linear graded doping"),
  IP("gaussian",DOP_GAUSS,	IF_FLAG,	"Gaussian doping"),
  IP("gdiff",	DOP_GAUSS,	IF_FLAG,	"Gaussian diffusion"),
  IP("gimp",	DOP_GAUSS,	IF_FLAG,	"Gaussian implantation"),
  IP("erfc",	DOP_ERFC,	IF_FLAG,	"Error-function doping"),
  IP("errfc",	DOP_ERFC,	IF_FLAG,	"Error-function doping"),
  IP("exponential",DOP_EXP,	IF_FLAG,	"Exponential doping"),
  IP("suprem3", DOP_SUPREM3,	IF_FLAG,	"Suprem3 doping"),
  IP("ascii",	DOP_ASCII,	IF_FLAG,	"Ascii-input doping"),
  IP("infile",	DOP_INFILE,	IF_STRING,	"Input-file name"),
  IP("lat.rotate",DOP_ROTATE_LAT,IF_FLAG,	"Rotate principal profile"),
  IP("lat.unif",DOP_UNIF_LAT,	IF_FLAG,	"Uniform lateral falloff"),
  IP("lat.linf",DOP_LINEAR_LAT,	IF_FLAG,	"Linear lateral falloff"),
  IP("lat.gauss",DOP_GAUSS_LAT,	IF_FLAG,	"Gaussian lateral falloff"),
  IP("lat.erfc",DOP_ERFC_LAT,	IF_FLAG,	"Erfc lateral falloff"),
  IP("lat.exp", DOP_EXP_LAT,	IF_FLAG,	"Exponential lateral falloff"),
  IP("boron",	DOP_BORON,	IF_FLAG,	"Boron impurities"),
  IP("phosphorus",DOP_PHOSP,	IF_FLAG,	"Phosphorus impurities"),
  IP("arsenic",	DOP_ARSEN,	IF_FLAG,	"Arsenic impurities"),
  IP("antimony",DOP_ANTIM,	IF_FLAG,	"Antimony impurities"),
  IP("p.type",	DOP_P_TYPE,	IF_FLAG,	"P-type impurities"),
  IP("acceptor",DOP_P_TYPE,	IF_FLAG,	"Acceptor impurities"),
  IP("n.type",	DOP_N_TYPE,	IF_FLAG,	"N-type impurities"),
  IP("donor",	DOP_N_TYPE,	IF_FLAG,	"Donor impurities"),
  IP("x.axis",	DOP_X_AXIS,	IF_FLAG,	"X principal axis"),
  IP("y.axis",	DOP_Y_AXIS,	IF_FLAG,	"Y principal axis"),
  IP("x.low", 	DOP_X_LOW,	IF_REAL,	"Low X edge"),
  IP("x.high",	DOP_X_HIGH,	IF_REAL,	"High X edge"),
  IP("y.low",	DOP_Y_LOW,	IF_REAL,	"Low Y edge"),
  IP("y.high",	DOP_Y_HIGH,	IF_REAL,	"High Y edge"),
  IP("conc",	DOP_CONC,	IF_REAL,	"Concentration"),
  IP("peak.conc",DOP_CONC,	IF_REAL,	"Peak concentration"),
  IP("location",DOP_LOCATION,	IF_REAL,	"Peak location"),
  IP("range",   DOP_LOCATION,	IF_REAL,	"Peak location"),
  IP("char.length",DOP_CHAR_LEN,IF_REAL,	"Characteristic length"),
  IP("ratio.lat",DOP_RATIO_LAT,	IF_REAL,	"Lateral ratio")
};

IFcardInfo DOPinfo = {
  "doping",
  "Add dopant to domains of a device",
  NUMELEMS(DOPpTable),
  DOPpTable,

  DOPnewCard,
  DOPparam,
  NULL
};

int
DOPnewCard(void **inCard, void *inModel)
{
    DOPcard *tmpCard, *newCard;
    GENnumModel *model = (GENnumModel *)inModel;

    newCard = NEW( DOPcard );
    if (!newCard) {
        *inCard = (void *)NULL;
        return(E_NOMEM);
    }
    newCard->DOPnextCard = (DOPcard *)NULL;
    *inCard = (void *)newCard;

    tmpCard = model->GENdopings;
    if (!tmpCard) { /* First in list */
        model->GENdopings = newCard;
    } else {
	/* Go to end of list */
        while (tmpCard->DOPnextCard) tmpCard = tmpCard->DOPnextCard;
	/* And add new card */
	tmpCard->DOPnextCard = newCard;
    }
    return(OK);
}


int
DOPparam(int param, IFvalue *value, void *inCard)
{
    int i;
    DOPcard *card = (DOPcard *)inCard;

    switch (param) {
	case DOP_DOMAIN:
	    if ( !card->DOPdomainsGiven ) {
	      card->DOPnumDomains = value->v.numValue;
	      card->DOPdomains = (int *)tmalloc(value->v.numValue * sizeof(int));
	      for ( i=0; i < card->DOPnumDomains; i++ ) {
		  card->DOPdomains[i] = value->v.vec.iVec[i];
	      }
	      card->DOPdomainsGiven = TRUE;
	    } /* else: do nothing, can only define domains once */
	    break;
	case DOP_ROTATE_LAT:
	    card->DOProtateLat = TRUE;
	    card->DOProtateLatGiven = TRUE;
	    break;
	case DOP_UNIF:
	    if ( !card->DOPprofileTypeGiven ) {
	        card->DOPprofileType = DOP_UNIF;
	        card->DOPprofileTypeGiven = TRUE;
	    }
	    break;
	case DOP_UNIF_LAT:
	    if ( !card->DOPlatProfileTypeGiven ) {
	        card->DOPlatProfileType = DOP_UNIF;
	        card->DOPlatProfileTypeGiven = TRUE;
	    }
	    break;
	case DOP_LINEAR:
	    if ( !card->DOPprofileTypeGiven ) {
	        card->DOPprofileType = DOP_LINEAR;
	        card->DOPprofileTypeGiven = TRUE;
	    }
	    break;
	case DOP_LINEAR_LAT:
	    if ( !card->DOPlatProfileTypeGiven ) {
	        card->DOPlatProfileType = DOP_LINEAR_LAT;
	        card->DOPlatProfileTypeGiven = TRUE;
	    }
	    break;
	case DOP_GAUSS:
	    if ( !card->DOPprofileTypeGiven ) {
	        card->DOPprofileType = DOP_GAUSS;
	        card->DOPprofileTypeGiven = TRUE;
	    }
	    break;
	case DOP_GAUSS_LAT:
	    if ( !card->DOPlatProfileTypeGiven ) {
	        card->DOPlatProfileType = DOP_GAUSS;
	        card->DOPlatProfileTypeGiven = TRUE;
	    }
	    break;
	case DOP_ERFC:
	    if ( !card->DOPprofileTypeGiven ) {
	        card->DOPprofileType = DOP_ERFC;
	        card->DOPprofileTypeGiven = TRUE;
	    }
	    break;
	case DOP_ERFC_LAT:
	    if ( !card->DOPlatProfileTypeGiven ) {
	        card->DOPlatProfileType = DOP_ERFC;
	        card->DOPlatProfileTypeGiven = TRUE;
	    }
	    break;
	case DOP_EXP:
	    if ( !card->DOPprofileTypeGiven ) {
	        card->DOPprofileType = DOP_EXP;
	        card->DOPprofileTypeGiven = TRUE;
	    }
	    break;
	case DOP_EXP_LAT:
	    if ( !card->DOPlatProfileTypeGiven ) {
	        card->DOPlatProfileType = DOP_EXP;
	        card->DOPlatProfileTypeGiven = TRUE;
	    }
	    break;
	case DOP_SUPREM3:
	    if ( !card->DOPprofileTypeGiven ) {
	        card->DOPprofileType = DOP_SUPREM3;
	        card->DOPprofileTypeGiven = TRUE;
	    } else if ( card->DOPprofileType == DOP_ASCII ) {
	        card->DOPprofileType = DOP_SUPASCII;
	    }
	    break;
	case DOP_ASCII:
	    if ( !card->DOPprofileTypeGiven ) {
	        card->DOPprofileType = DOP_ASCII;
	        card->DOPprofileTypeGiven = TRUE;
	    } else if ( card->DOPprofileType == DOP_SUPREM3 ) {
	        card->DOPprofileType = DOP_SUPASCII;
	    }
	    break;
	case DOP_BORON:
	    if ( !card->DOPimpurityTypeGiven ) {
	        card->DOPimpurityType = DOP_BORON;
	        card->DOPimpurityTypeGiven = TRUE;
	    }
	    break;
	case DOP_PHOSP:
	    if ( !card->DOPimpurityTypeGiven ) {
	        card->DOPimpurityType = DOP_PHOSP;
	        card->DOPimpurityTypeGiven = TRUE;
	    }
	    break;
	case DOP_ARSEN:
	    if ( !card->DOPimpurityTypeGiven ) {
	        card->DOPimpurityType = DOP_ARSEN;
	        card->DOPimpurityTypeGiven = TRUE;
	    }
	    break;
	case DOP_ANTIM:
	    if ( !card->DOPimpurityTypeGiven ) {
	        card->DOPimpurityType = DOP_ANTIM;
	        card->DOPimpurityTypeGiven = TRUE;
	    }
	    break;
	case DOP_P_TYPE:
	    if ( !card->DOPimpurityTypeGiven ) {
	        card->DOPimpurityType = DOP_P_TYPE;
	        card->DOPimpurityTypeGiven = TRUE;
	    }
	    break;
	case DOP_N_TYPE:
	    if ( !card->DOPimpurityTypeGiven ) {
	        card->DOPimpurityType = DOP_N_TYPE;
	        card->DOPimpurityTypeGiven = TRUE;
	    }
	    break;
	case DOP_X_AXIS:
	    if ( !card->DOPaxisTypeGiven ) {
	        card->DOPaxisType = DOP_X_AXIS;
	        card->DOPaxisTypeGiven = TRUE;
	    }
	    break;
	case DOP_Y_AXIS:
	    if ( !card->DOPaxisTypeGiven ) {
	        card->DOPaxisType = DOP_Y_AXIS;
	        card->DOPaxisTypeGiven = TRUE;
	    }
	    break;
	case DOP_INFILE:
	    card->DOPinFile = value->sValue;
	    card->DOPinFileGiven = TRUE;
	    break;
	case DOP_X_LOW:
	    card->DOPxLow = value->rValue * UM_TO_CM;
	    card->DOPxLowGiven = TRUE;
	    break;
	case DOP_X_HIGH:
	    card->DOPxHigh = value->rValue * UM_TO_CM;
	    card->DOPxHighGiven = TRUE;
	    break;
	case DOP_Y_LOW:
	    card->DOPyLow = value->rValue * UM_TO_CM;
	    card->DOPyLowGiven = TRUE;
	    break;
	case DOP_Y_HIGH:
	    card->DOPyHigh = value->rValue * UM_TO_CM;
	    card->DOPyHighGiven = TRUE;
	    break;
	case DOP_CONC:
	    card->DOPconc = fabs(value->rValue);
	    card->DOPconcGiven = TRUE;
	    break;
	case DOP_LOCATION:
	    card->DOPlocation = value->rValue * UM_TO_CM;
	    card->DOPlocationGiven = TRUE;
	    break;
	case DOP_CHAR_LEN:
	    card->DOPcharLen = value->rValue * UM_TO_CM;
	    card->DOPcharLenGiven = TRUE;
	    break;
	case DOP_RATIO_LAT:
	    card->DOPratioLat = value->rValue;
	    card->DOPratioLatGiven = TRUE;
	    break;
	default:
	    return(E_BADPARM);
	    break;
    }
    return(OK);
}
