/**********
Copyright 1991 Regents of the University of California.  All rights reserved.
Author:	1992 David A. Gates, U. C. Berkeley CAD Group
$Id: mobil.c,v 1.2 2005/05/27 17:52:33 sjborley Exp $
**********/

#include "ngspice.h"
#include "numglobs.h"
#include "numconst.h"
#include "numenum.h"
#include "macros.h"
#include "material.h"

void MOBdefaults(MaterialInfo *info , int carrier, int type, 
                 int concmodel, int fieldmodel )
{
  switch (concmodel) {
    case CT: 
      info->concModel = CT;
      if (carrier == ELEC) {
	info->muMax[ELEC][type] = CT_MUMAX_N;
	info->muMin[ELEC][type] = CT_MUMIN_N;
	info->ntRef[ELEC][type] = CT_NTREF_N;
	info->ntExp[ELEC][type] = CT_NTEXP_N;
      } else {
	info->muMax[HOLE][type] = CT_MUMAX_P;
	info->muMin[HOLE][type] = CT_MUMIN_P;
	info->ntRef[HOLE][type] = CT_NTREF_P;
	info->ntExp[HOLE][type] = CT_NTEXP_P;
      }
      break;
    case AR: 
      info->concModel = AR;
      if (carrier == ELEC) {
	info->muMax[ELEC][type] = AR_MUMAX_N;
	info->muMin[ELEC][type] = AR_MUMIN_N;
	info->ntRef[ELEC][type] = AR_NTREF_N;
	info->ntExp[ELEC][type] = AR_NTEXP_N;
      } else {
	info->muMax[HOLE][type] = AR_MUMAX_P;
	info->muMin[HOLE][type] = AR_MUMIN_P;
	info->ntRef[HOLE][type] = AR_NTREF_P;
	info->ntExp[HOLE][type] = AR_NTEXP_P;
      }
      break;
    case UF: 
      info->concModel = UF;
      if (carrier == ELEC) {
	info->muMax[ELEC][type] = UF_MUMAX_N;
	info->muMin[ELEC][type] = UF_MUMIN_N;
	info->ntRef[ELEC][type] = UF_NTREF_N;
	info->ntExp[ELEC][type] = UF_NTEXP_N;
      } else {
	info->muMax[HOLE][type] = UF_MUMAX_P;
	info->muMin[HOLE][type] = UF_MUMIN_P;
	info->ntRef[HOLE][type] = UF_NTREF_P;
	info->ntExp[HOLE][type] = UF_NTEXP_P;
      }
      break;
    case GA:
      info->concModel = GA;
      if (carrier == ELEC) {
	info->muMax[ELEC][type] = GA_MUMAX_N;
	info->muMin[ELEC][type] = GA_MUMIN_N;
	info->ntRef[ELEC][type] = GA_NTREF_N;
	info->ntExp[ELEC][type] = GA_NTEXP_N;
      } else {
	info->muMax[HOLE][type] = GA_MUMAX_P;
	info->muMin[HOLE][type] = GA_MUMIN_P;
	info->ntRef[HOLE][type] = GA_NTREF_P;
	info->ntExp[HOLE][type] = GA_NTEXP_P;
      }
      break;
    case SG:
    default:
      info->concModel = SG;
      if (carrier == ELEC) {
	info->muMax[ELEC][type] = SG_MUMAX_N;
	info->muMin[ELEC][type] = SG_MUMIN_N;
	info->ntRef[ELEC][type] = SG_NTREF_N;
	info->ntExp[ELEC][type] = SG_NTEXP_N;
      } else {
	info->muMax[HOLE][type] = SG_MUMAX_P;
	info->muMin[HOLE][type] = SG_MUMIN_P;
	info->ntRef[HOLE][type] = SG_NTREF_P;
	info->ntExp[HOLE][type] = SG_NTEXP_P;
      }
      break;
  }
  if (type == MAJOR) {
    switch (fieldmodel) {
      case CT: 
	info->fieldModel = CT;
	if (carrier == ELEC) {
	  info->vSat[ELEC] = CT_VSAT_N;
	} else {
	  info->vSat[HOLE] = CT_VSAT_P;
	}
	break;
      case AR: 
      case UF:
	info->fieldModel = AR;
	if (carrier == ELEC) {
	  info->vSat[ELEC] = AR_VSAT_N;
	} else {
	  info->vSat[HOLE] = AR_VSAT_P;
	}
	break;
      case GA:
	info->fieldModel = GA;
	if (carrier == ELEC) {
	  info->vSat[ELEC] = GA_VSAT_N;
	  info->vWarm[ELEC] = GA_VWARM_N;
	} else {
	  info->vSat[HOLE] = GA_VSAT_P;
	  info->vWarm[HOLE] = GA_VWARM_P;
	}
	break;
      case SG:
      default:
	info->fieldModel = SG;
	if (carrier == ELEC) {
	  info->vSat[ELEC] = SG_VSAT_N;
	  info->vWarm[ELEC] = SG_VWARM_N;
	} else {
	  info->vSat[HOLE] = SG_VSAT_P;
	  info->vWarm[HOLE] = SG_VWARM_P;
	}
	break;
    }
  }
}

void
MOBtempDep (MaterialInfo *info, double temp)
{
  double  relTemp = temp / 300.0;
  double  factor, muMin, mu0;

  /* Modify if necessary. */
  if (TempDepMobility)
  {
  /* Do concentration dependence parameters */
    muMin = info->muMin[ELEC][MAJOR];
    mu0 = info->muMax[ELEC][MAJOR] - muMin;
    factor = pow(relTemp, TD_EXPMUMIN_N);
    muMin *= factor;
    factor = pow(relTemp, TD_EXPMUMAX_N);
    mu0 *= factor;
    info->muMin[ELEC][MAJOR] = muMin;
    info->muMax[ELEC][MAJOR] = mu0 + muMin;
    factor = pow(relTemp, TD_EXPNTREF_N);
    info->ntRef[ELEC][MAJOR] *= factor;
    factor = pow(relTemp, TD_EXPNTEXP_N);
    info->ntExp[ELEC][MAJOR] *= factor;

    muMin = info->muMin[ELEC][MINOR];
    mu0 = info->muMax[ELEC][MINOR] - muMin;
    factor = pow(relTemp, TD_EXPMUMIN_N);
    muMin *= factor;
    factor = pow(relTemp, TD_EXPMUMAX_N);
    mu0 *= factor;
    info->muMin[ELEC][MINOR] = muMin;
    info->muMax[ELEC][MINOR] = mu0 + muMin;
    factor = pow(relTemp, TD_EXPNTREF_N);
    info->ntRef[ELEC][MINOR] *= factor;
    factor = pow(relTemp, TD_EXPNTEXP_N);
    info->ntExp[ELEC][MINOR] *= factor;

    muMin = info->muMin[HOLE][MAJOR];
    mu0 = info->muMax[HOLE][MAJOR] - muMin;
    factor = pow(relTemp, TD_EXPMUMIN_P);
    muMin *= factor;
    factor = pow(relTemp, TD_EXPMUMAX_P);
    mu0 *= factor;
    info->muMin[HOLE][MAJOR] = muMin;
    info->muMax[HOLE][MAJOR] = mu0 + muMin;
    factor = pow(relTemp, TD_EXPNTREF_P);
    info->ntRef[HOLE][MAJOR] *= factor;
    factor = pow(relTemp, TD_EXPNTEXP_P);
    info->ntExp[HOLE][MAJOR] *= factor;

    muMin = info->muMin[HOLE][MINOR];
    mu0 = info->muMax[HOLE][MINOR] - muMin;
    factor = pow(relTemp, TD_EXPMUMIN_P);
    muMin *= factor;
    factor = pow(relTemp, TD_EXPMUMAX_P);
    mu0 *= factor;
    info->muMin[HOLE][MINOR] = muMin;
    info->muMax[HOLE][MINOR] = mu0 + muMin;
    factor = pow(relTemp, TD_EXPNTREF_P);
    info->ntRef[HOLE][MINOR] *= factor;
    factor = pow(relTemp, TD_EXPNTEXP_P);
    info->ntExp[HOLE][MINOR] *= factor;

    /* Modify field dependence parameters */
    /* Assume warm carrier reference velocity has same temperature dep. */
    factor = sqrt( tanh( TD_TREFVS_N / Temp ) );
    info->vSat[ELEC] *= factor;
    info->vWarm[ELEC] *= factor;
    factor = sqrt( tanh( TD_TREFVS_P / Temp ) );
    info->vSat[HOLE] *= factor;
    info->vWarm[HOLE] *= factor;
  }
}

void
MOBconcDep (MaterialInfo *info, double conc, double *pMun, double *pMup)
{
  double  s;

  /* We have to check sign of conc even when concentration dependence
   * is not used because it affects whether carriers are majority or
   * minority carriers. Ideally, the minority/majority carrier models
   * should agree at 0.0 concentration, but often they'll be inconsistent.
   */

  if (conc >= 0.0)
  {				/* N type */
    if (ConcDepMobility)
    {
      switch (info->concModel)
      {
	case CT: 
	case AR: 
	case UF: 
	case GA:
	  *pMun = info->muMin[ELEC][MAJOR] +
	    (info->muMax[ELEC][MAJOR] - info->muMin[ELEC][MAJOR]) /
	    (1.0 + pow(conc / info->ntRef[ELEC][MAJOR],
		info->ntExp[ELEC][MAJOR]));

	  *pMup = info->muMin[HOLE][MINOR] +
	    (info->muMax[HOLE][MINOR] - info->muMin[HOLE][MINOR]) /
	    (1.0 + pow(conc / info->ntRef[HOLE][MINOR],
		info->ntExp[HOLE][MINOR]));
	  break;
	case SG: 
	default: 
	  s = info->muMax[ELEC][MAJOR] / info->muMin[ELEC][MAJOR];
	  s = pow(s, 1.0 / info->ntExp[ELEC][MAJOR]) - 1;
	  *pMun = info->muMax[ELEC][MAJOR] /
	    pow(1.0 + conc / (conc / s + info->ntRef[ELEC][MAJOR]),
	      info->ntExp[ELEC][MAJOR]);

	  s = info->muMax[HOLE][MINOR] / info->muMin[HOLE][MINOR];
	  s = pow(s, 1.0 / info->ntExp[HOLE][MINOR]) - 1;
	  *pMup = info->muMax[HOLE][MINOR] /
	    pow(1.0 + conc / (conc / s + info->ntRef[HOLE][MINOR]),
	      info->ntExp[HOLE][MINOR]);
	  break;
      }
    }
    else
    {
      *pMun = info->muMax[ELEC][MAJOR];
      *pMup = info->muMax[HOLE][MINOR];
    }
  }
  else
  {				/* P type */
    if (ConcDepMobility)
    {
      conc = -conc;		/* Take absolute value. */
      switch (info->concModel)
      {
	case CT: 
	case AR: 
	case UF: 
	case GA:
	  *pMun = info->muMin[ELEC][MINOR] +
	    (info->muMax[ELEC][MINOR] - info->muMin[ELEC][MINOR]) /
	    (1.0 + pow(conc / info->ntRef[ELEC][MINOR],
		info->ntExp[ELEC][MINOR]));

	  *pMup = info->muMin[HOLE][MAJOR] +
	    (info->muMax[HOLE][MAJOR] - info->muMin[HOLE][MAJOR]) /
	    (1.0 + pow(conc / info->ntRef[HOLE][MAJOR],
		info->ntExp[HOLE][MAJOR]));
	  break;
	case SG: 
	default: 
	  s = info->muMax[ELEC][MINOR] / info->muMin[ELEC][MINOR];
	  s = pow(s, 1.0 / info->ntExp[ELEC][MINOR]) - 1;
	  *pMun = info->muMax[ELEC][MINOR] /
	    pow(1.0 + conc / (conc / s + info->ntRef[ELEC][MINOR]),
	      info->ntExp[ELEC][MINOR]);

	  s = info->muMax[HOLE][MAJOR] / info->muMin[HOLE][MAJOR];
	  s = pow(s, 1.0 / info->ntExp[HOLE][MAJOR]) - 1;
	  *pMup = info->muMax[HOLE][MAJOR] /
	    pow(1.0 + conc / (conc / s + info->ntRef[HOLE][MAJOR]),
	      info->ntExp[HOLE][MAJOR]);
	  break;
      }
    }
    else
    {
      *pMun = info->muMax[ELEC][MINOR];
      *pMup = info->muMax[HOLE][MAJOR];
    }
  }
  return;
}


void
MOBfieldDep (MaterialInfo *info, int carrier, double field, double *pMu,
             double *pDMu)
{
  double  eLateral, mu;
  double  sgnL;
  double  temp1, temp2, temp3, temp4, temp5, temp6;
  double  dMuDEl;		/* Lateral Field Derivative */

  /* Quick check to make sure we really belong here. */
  if (!FieldDepMobility) /* XXX Global */
    return;

  sgnL = SGN (field);
  eLateral = ABS (field);
  mu = *pMu;			/* Grab temp. and conc.-dep. mobility */

  
  if (carrier == ELEC)
  {
    switch (info->fieldModel)
    {
      case CT: 
      case AR: 
      case UF: 
	temp1 = mu / info->vSat[ELEC];
	temp2 = temp1 * eLateral;
	temp3 = 1.0 / (1.0 + temp2 * temp2);
	mu *= sqrt(temp3);
	dMuDEl = -sgnL * mu * temp3 * temp2 * temp1;
	
	
	break;
      case GA:
	temp1 = info->vSat[ELEC] / info->vWarm[ELEC]; /* Vsat / Vwarm */
	temp2 = mu / info->vWarm[ELEC];
	temp3 = temp2 * eLateral; /* Vdrift / Vwarm */
	temp4 = temp3 * temp3 * temp3;
	temp5 = 1.0  +  temp1 * temp4; 
	temp6 = 1.0 / (1.0 + temp3 * temp4);
	mu *= temp5 * temp6;
	dMuDEl = - sgnL * mu * temp2 *
	    (4.0 * temp4 * temp6 - 3.0 * temp1 * temp3 * temp3 / temp5 ); 
	
	/*
	dMuDEl = 0.0; 
	    */
	break;
      case SG: 
      default: 
	temp1 = mu / info->vSat[ELEC];
	temp2 = temp1 * eLateral;/* Vdrift / Vsat */
	temp3 = mu / info->vWarm[ELEC];
	temp4 = temp3 * eLateral;/* Vdrift / Vwarm */
	temp5 = temp4 / (temp4 + SG_FIT_N);
	temp6 = 1.0 / (1.0 + temp4 * temp5 + temp2 * temp2);
	mu *= sqrt(temp6);
	dMuDEl = -sgnL * 0.5 * mu * temp6 *
	  (temp5 * (2.0 - temp5) * temp3 + (2.0 * temp2 * temp1));
	  
	  
	break;
    }
  }
  else
  {				/* Hole Mobility */
    switch (info->fieldModel)
    {
      case CT: 
      case AR: 
      case UF: 
	temp1 = mu / info->vSat[HOLE];
	temp2 = temp1 * eLateral;
	temp3 = 1.0 / (1.0 + temp2);
	mu *= temp3;
	dMuDEl = -sgnL * mu * temp3 * temp1;
	
	break;
      case GA:
	temp1 = info->vSat[HOLE] / info->vWarm[HOLE]; /* Vsat / Vwarm */
	temp2 = mu / info->vWarm[HOLE];
	temp3 = temp2 * eLateral; /* Vdrift / Vwarm */
	temp4 = temp3 * temp3 * temp3;
	temp5 = 1.0 + temp1 * temp4;
	temp6 = 1.0 / (1.0 + temp3 * temp4);
	mu *= temp5 * temp6;
	dMuDEl = - sgnL * mu * temp2 *
	    (4.0 * temp4 * temp6 - 3.0 * temp1 * temp3 * temp3 / temp5 ); 
	
	
	/*
	dMuDEl = 0.0;
	    */
	break;
      case SG: 
      default: 
	temp1 = mu / info->vSat[HOLE];
	temp2 = temp1 * eLateral;/* Vdrift / Vsat */
	temp3 = mu / info->vWarm[HOLE];
	temp4 = temp3 * eLateral;/* Vdrift / Vwarm */
	temp5 = temp4 / (temp4 + SG_FIT_P);
	temp6 = 1.0 / (1.0 + temp4 * temp5 + temp2 * temp2);
	mu *= sqrt(temp6);
	dMuDEl = -sgnL * 0.5 * mu * temp6 *
	  (temp5 * (2.0 - temp5) * temp3 + (2.0 * temp2 * temp1));
	  
	  
	break;
    }
  }

  *pMu = mu;
  *pDMu = dMuDEl;

  return;
}
