• Main Page
  • Related Pages
  • Namespaces
  • Classes
  • Files
  • File List
  • File Members

QCAD_PoissonNeumann_Def.hpp

Go to the documentation of this file.
00001 //*****************************************************************//
00002 //    Albany 2.0:  Copyright 2012 Sandia Corporation               //
00003 //    This Software is released under the BSD license detailed     //
00004 //    in the file "license.txt" in the top-level Albany directory  //
00005 //*****************************************************************//
00006 
00007 #include "Teuchos_TestForException.hpp"
00008 #include "Phalanx_DataLayout.hpp"
00009 #include "Sacado_ParameterRegistration.hpp"
00010 #include "Albany_AbstractDiscretization.hpp"
00011 
00012 
00013 namespace QCAD {
00014 
00015 template<typename EvalT,typename Traits>
00016 PoissonNeumann<EvalT, Traits>::
00017 PoissonNeumann(Teuchos::ParameterList& p) :
00018   PHAL::Neumann<EvalT,Traits>(p)
00019 {
00020   // We only need to do extra work in the case of robin bc's,
00021   //  when there is a builtin offset producing a shift from the user's value
00022   if(this->inputConditions == "robin") {  //CHANGED from dirichlet case
00023     
00024     // get parameters from ParameterList
00025     user_value = PHAL::NeumannBase<EvalT,Traits>::robin_vals[0]; // dof_value for robin condition  //CHANGED from dirichlet case
00026     materialDB = p.get< Teuchos::RCP<QCAD::MaterialDatabase> >("MaterialDB");
00027     energy_unit_in_eV = p.get<double>("Energy unit in eV");
00028     
00029     Teuchos::ParameterList* psList = p.get<Teuchos::ParameterList*>("Poisson Source Parameter List");
00030     
00031     carrierStatistics = psList->get("Carrier Statistics", "Boltzmann Statistics");
00032     incompIonization = psList->get("Incomplete Ionization", "False");
00033     //dopingDonor = psList->get("Donor Doping", 1e14);
00034     //dopingAcceptor = psList->get("Acceptor Doping", 1e14);
00035     //donorActE = psList->get("Donor Activation Energy", 0.040);
00036     //acceptorActE = psList->get("Acceptor Activation Energy", 0.045);
00037     
00038     temperature = p.get<double>("Temperature"); //To be replaced by SharedParameter evaluator access
00039     
00040     // obtain material or eb name for a given nodeset 
00041     std::string sideSetName = PHAL::NeumannBase<EvalT,Traits>::sideSetID;
00042     material = materialDB->getSideSetParam<std::string>(sideSetName,"material","");
00043     if (material.length() == 0) {
00044       ebName = materialDB->getSideSetParam<std::string>(sideSetName,"elementBlock","");
00045       if (ebName.length() > 0)
00046         material = materialDB->getElementBlockParam<std::string>(ebName,"material","");
00047     }
00048     
00049     // private scaling parameter (note: kbT is used in calculating qPhiRef below)
00050     const double kbBoltz = 8.617343e-05; //[eV/K]
00051     kbT = kbBoltz*temperature;
00052     V0 = kbBoltz*temperature/1.0; // kb*T/q in [V], scaling for potential
00053     
00054     // obtain qPhiRef (energy reference for heterogeneous structures)
00055     if ( (material.length() > 0) || (ebName.length() > 0) )
00056     {
00057       std::string refMtrlName, category;
00058       refMtrlName = materialDB->getParam<std::string>("Reference Material");
00059       category = materialDB->getMaterialParam<std::string>(refMtrlName,"Category");
00060       if (category == "Semiconductor") {
00061         double mdn = materialDB->getMaterialParam<double>(refMtrlName,"Electron DOS Effective Mass");
00062         double mdp = materialDB->getMaterialParam<double>(refMtrlName,"Hole DOS Effective Mass");
00063         double Chi = materialDB->getMaterialParam<double>(refMtrlName,"Electron Affinity");
00064         double Eg0 = materialDB->getMaterialParam<double>(refMtrlName,"Zero Temperature Band Gap");
00065         double alpha = materialDB->getMaterialParam<double>(refMtrlName,"Band Gap Alpha Coefficient");
00066         double beta = materialDB->getMaterialParam<double>(refMtrlName,"Band Gap Beta Coefficient");
00067         
00068         ScalarT Eg = Eg0-alpha*pow(temperature,2.0)/(beta+temperature); // in [eV]
00069         ScalarT Eic = -Eg/2. + 3./4.*kbT*log(mdp/mdn);  // (Ei-Ec) in [eV]
00070         qPhiRef = Chi - Eic;  // (Evac-Ei) in [eV] where Evac = vacuum level
00071       }
00072       else if (category == "Insulator") {
00073         double Chi = materialDB->getMaterialParam<double>(refMtrlName,"Electron Affinity");
00074         qPhiRef = Chi;
00075       }
00076       else if (category == "Metal") {
00077         double workFn = materialDB->getMaterialParam<double>(refMtrlName,"Work Function");
00078         qPhiRef = workFn;
00079       }
00080       else {
00081         TEUCHOS_TEST_FOR_EXCEPTION (true, Teuchos::Exceptions::InvalidParameter, std::endl 
00082             << "Error!  Invalid category " << category 
00083             << " for reference material !" << std::endl);
00084       }
00085     }
00086   }
00087 }
00088 
00089 
00090 // ****************************************************************************
00091 template<typename EvalT,typename Traits>
00092 void PoissonNeumann<EvalT, Traits>::
00093 evaluateFields(typename Traits::EvalData neumannWorkset)
00094 {
00095   ScalarT bcValue = 0.0; 
00096 
00097   if (material.length() > 0)  
00098   {
00099     std::string category = materialDB->getMaterialParam<std::string>(material,"Category");
00100 
00102     if (category != "Semiconductor") {
00103       double metalWorkFunc = materialDB->getMaterialParam<double>(material,"Work Function");
00104       ScalarT offsetDueToWorkFunc = (metalWorkFunc-qPhiRef)/1.0;  // 1.0 converts from [eV] to [V]
00105 
00106       bcValue = (user_value - offsetDueToWorkFunc) / energy_unit_in_eV;  // [myV]
00107     }
00108   
00110     else {
00111 
00112       // Universal constants
00113       const double kbBoltz = 8.617343e-05;  // [eV/K]
00114       const double eleQ = 1.602176487e-19;  // [C]
00115       const double m0 = 9.10938215e-31;     // [kg]
00116       const double hbar = 1.054571628e-34;  // [J.s]
00117       const double pi = 3.141592654;
00118 
00119   // Temperature-independent material parameters
00120       double mdn, mdp, Tref, Eg0, alpha, beta, Chi;
00121       if (ebName.length() > 0)  {
00122   mdn = materialDB->getElementBlockParam<double>(ebName,"Electron DOS Effective Mass");
00123   mdp = materialDB->getElementBlockParam<double>(ebName,"Hole DOS Effective Mass");
00124   Tref = materialDB->getElementBlockParam<double>(ebName,"Reference Temperature");
00125   
00126   Eg0 = materialDB->getElementBlockParam<double>(ebName,"Zero Temperature Band Gap");
00127   alpha = materialDB->getElementBlockParam<double>(ebName,"Band Gap Alpha Coefficient");
00128   beta = materialDB->getElementBlockParam<double>(ebName,"Band Gap Beta Coefficient");
00129   Chi = materialDB->getElementBlockParam<double>(ebName,"Electron Affinity");
00130       }
00131       else {
00132   mdn = materialDB->getMaterialParam<double>(material,"Electron DOS Effective Mass");
00133   mdp = materialDB->getMaterialParam<double>(material,"Hole DOS Effective Mass");
00134   Tref = materialDB->getMaterialParam<double>(material,"Reference Temperature");
00135   
00136   Eg0 = materialDB->getMaterialParam<double>(material,"Zero Temperature Band Gap");
00137   alpha = materialDB->getMaterialParam<double>(material,"Band Gap Alpha Coefficient");
00138   beta = materialDB->getMaterialParam<double>(material,"Band Gap Beta Coefficient");
00139   Chi = materialDB->getMaterialParam<double>(material,"Electron Affinity");
00140       }
00141 
00142       // Constant prefactor in calculating Nc and Nv in [cm-3]
00143       double NcvFactor = 2.0*pow((kbBoltz*eleQ*m0*Tref)/(2*pi*pow(hbar,2)),3./2.)*1.e-6;
00144           // eleQ converts kbBoltz in [eV/K] to [J/K], 1e-6 converts [m-3] to [cm-3]
00145     
00146       // Strong temperature-dependent material parameters
00147       ScalarT Nc;  // conduction band effective DOS in [cm-3]
00148       ScalarT Nv;  // valence band effective DOS in [cm-3]
00149       ScalarT Eg;  // band gap at T [K] in [eV]
00150     
00151       Nc = NcvFactor*pow(mdn,1.5)*pow(temperature/Tref,1.5);  // in [cm-3]
00152       Nv = NcvFactor*pow(mdp,1.5)*pow(temperature/Tref,1.5); 
00153       Eg = Eg0-alpha*pow(temperature,2.0)/(beta+temperature); // in [eV]
00154     
00155       ScalarT builtinPotential = 0.0; 
00156       std::string dopantType;
00157       if (ebName.length() > 0)
00158   dopantType = materialDB->getElementBlockParam<std::string>(ebName,"Dopant Type","None");
00159       else
00160   dopantType = materialDB->getMaterialParam<std::string>(material,"Dopant Type","None");
00161 
00162       // Intrinsic semiconductor (no doping)
00163       if (dopantType == "None")  
00164       {
00165   // apply charge neutrality (p=n) and MB statistics
00166   builtinPotential = (qPhiRef-Chi-0.5*Eg)/1.0 + 0.5*kbT*log(Nv/Nc)/1.0;
00167   bcValue = (user_value + builtinPotential) / energy_unit_in_eV;  // [myV]
00168       }
00169     
00170       else // Extrinsic semiconductor (doped)
00171       {
00172   double dopingConc, dopantActE;
00173   if (ebName.length() > 0) {
00174     dopingConc = materialDB->getElementBlockParam<double>(ebName,"Doping Value");
00175     dopantActE = materialDB->getElementBlockParam<double>(ebName,"Dopant Activation Energy", 0.045);
00176   }
00177   else {
00178     dopingConc = materialDB->getMaterialParam<double>(material,"Doping Value");
00179     dopantActE = materialDB->getMaterialParam<double>(material,"Dopant Activation Energy", 0.045);
00180   }
00181 
00182   if ((carrierStatistics=="Boltzmann Statistics") && (incompIonization=="False"))
00183     builtinPotential = potentialForMBComplIon(Nc,Nv,Eg,Chi,dopantType,dopingConc);
00184     
00185   else if ((carrierStatistics=="Boltzmann Statistics") && (incompIonization=="True"))
00186     builtinPotential = potentialForMBIncomplIon(Nc,Nv,Eg,Chi,dopantType,dopingConc,dopantActE);
00187 
00188   else if ((carrierStatistics=="Fermi-Dirac Statistics") && (incompIonization=="False"))
00189     builtinPotential = potentialForFDComplIon(Nc,Nv,Eg,Chi,dopantType,dopingConc);
00190   
00191   else if ((carrierStatistics=="0-K Fermi-Dirac Statistics") && (incompIonization=="False"))
00192     builtinPotential = potentialForZeroKFDComplIon(Nc,Nv,Eg,Chi,dopantType,dopingConc);
00193     
00194   // For cases of FD and 0-K FD with incompIonization==True, one needs to 
00195   // numerically solve a non-trivial equation. Since when incompIonization==True
00196   // is enabled, the MB almost always holds, so use potentialForMBIncomplIon.
00197   else  
00198     builtinPotential = potentialForMBIncomplIon(Nc,Nv,Eg,Chi,dopantType,dopingConc,dopantActE);
00199         
00200   bcValue = (user_value + builtinPotential) / energy_unit_in_eV;  // [myV]
00201       }
00202     }
00203   } // end of if (material.length() > 0)
00204     
00206   //   (NBCs are always specified in volts by the user)
00207   else
00208     bcValue = user_value / energy_unit_in_eV;  // [myV]
00209 
00211   PHAL::NeumannBase<EvalT,Traits>::robin_vals[0] = bcValue;  //CHANGED from dirichlet case
00212     
00214   PHAL::Neumann<EvalT,Traits>::evaluateFields(neumannWorkset);  //CHANGED from dirichlet case
00215 
00216   std::string sideSetName = PHAL::NeumannBase<EvalT,Traits>::sideSetID;
00217   //std::cout << "sideSetName = " << sideSetName << ", bcValue = " << bcValue << std::endl;   
00218 }
00219 
00220 
00221 // *****************************************************************************
00222 template<typename EvalT,typename Traits>
00223 inline typename QCAD::PoissonNeumann<EvalT,Traits>::ScalarT
00224 QCAD::PoissonNeumann<EvalT,Traits>::inverseFDIntOneHalf(const ScalarT x)
00225 {
00226   // Use the approximate inverse of 1/2 FD integral by N. G. Nillsson, 
00227   // "An Accurate Approximation of the Generalized Einstein Relation for 
00228   // Degenerate Semiconductors," physica status solidi (a) 19, K75 (1973).
00229   
00230   const double pi = 3.1415926536;
00231   const double a = 0.24;
00232   const double b = 1.08; 
00233 
00234   ScalarT nu = pow(3./4.*sqrt(pi)*x, 2./3.);
00235   ScalarT invFDInt = 0.0; 
00236   
00237   if (x > 0.)
00238     invFDInt = log(x)/(1.-pow(x,2.)) + nu/(1.+pow(a+b*nu,-2.));
00239   else
00240     TEUCHOS_TEST_FOR_EXCEPTION (true, Teuchos::Exceptions::InvalidParameter, std::endl 
00241       << "Error! x in inverseFDIntOneHalf(x) must be greater than 0 " << std::endl);
00242 
00243   return invFDInt;
00244 }
00245 
00246 
00247 // *****************************************************************************
00248 template<typename EvalT,typename Traits>
00249 typename QCAD::PoissonNeumann<EvalT,Traits>::ScalarT
00250 QCAD::PoissonNeumann<EvalT,Traits>::potentialForMBComplIon(const ScalarT &Nc, 
00251    const ScalarT &Nv, const ScalarT &Eg, const double &Chi, const std::string &dopType, const double &dopingConc)
00252 {
00253   ScalarT Cn = Nc*exp((-qPhiRef+Chi)/kbT); 
00254   ScalarT Cp = Nv*exp((qPhiRef-Chi-Eg)/kbT);
00255   ScalarT builtinPotential = 0.0; 
00256   
00257   // for high-T, include n and p in charge neutrality: p=n+Na or p+Nd=n
00258   if ((Cn > 1.e-5) && (Cp > 1.e-5))
00259   {
00260     double signedDopingConc;
00261     if(dopType == "Donor") 
00262       signedDopingConc = dopingConc;
00263     else if(dopType == "Acceptor") 
00264       signedDopingConc = -dopingConc;
00265     else 
00266     {
00267       TEUCHOS_TEST_FOR_EXCEPTION (true, Teuchos::Exceptions::InvalidParameter, std::endl 
00268         << "Error!  Unknown dopant type " << dopType << "!"<< std::endl);
00269     }
00270     ScalarT tmp1 = signedDopingConc/(2.0*Cn);
00271     ScalarT tmp2 = tmp1 + sqrt(pow(tmp1,2.0) + Cp/Cn);
00272     if (tmp2 <= 0.0)
00273     {
00274       if(dopType == "Donor") 
00275         builtinPotential = (qPhiRef-Chi)/1.0 + V0*log(dopingConc/Nc);  
00276       else if(dopType == "Acceptor") 
00277         builtinPotential = (qPhiRef-Chi-Eg)/1.0 - V0*log(dopingConc/Nv);  
00278     }
00279     else
00280       builtinPotential = V0*log(tmp2); 
00281   }
00282   
00283   // for low-T (where Cn=0, Cp=0), consider only p=Na or n=Nd
00284   else
00285   {
00286     if(dopType == "Donor") 
00287       builtinPotential = (qPhiRef-Chi)/1.0 + V0*log(dopingConc/Nc);  
00288     else if(dopType == "Acceptor") 
00289       builtinPotential = (qPhiRef-Chi-Eg)/1.0 - V0*log(dopingConc/Nv);  
00290     else 
00291     {
00292       TEUCHOS_TEST_FOR_EXCEPTION (true, Teuchos::Exceptions::InvalidParameter, std::endl 
00293         << "Error!  Unknown dopant type " << dopType << "!"<< std::endl);
00294     }
00295   }
00296   
00297   return builtinPotential;
00298 }
00299 
00300 
00301 // *****************************************************************************
00302 template<typename EvalT,typename Traits>
00303 typename QCAD::PoissonNeumann<EvalT,Traits>::ScalarT
00304 QCAD::PoissonNeumann<EvalT,Traits>::potentialForMBIncomplIon(const ScalarT &Nc, 
00305       const ScalarT &Nv, const ScalarT &Eg, const double &Chi, const std::string &dopType, 
00306       const double &dopingConc, const double &dopantActE )
00307 {
00308   // maximum allowed exponent in an exponential function (unitless)
00309   const double MAX_EXPONENT = 100.0; 
00310   ScalarT builtinPotential;
00311   
00312   // assume n = Nd+ to have an analytical expression (neglect p)  
00313   if(dopType == "Donor") 
00314   {
00315     ScalarT offset = (-dopantActE +qPhiRef -Chi) /1.0;
00316     if (dopantActE/kbT > MAX_EXPONENT)  // exp(dopantActE/kbT) -> +infinity for very small T
00317     {
00318       builtinPotential = offset +V0 *(0.5*log(dopingConc/(2.*Nc)) + dopantActE/(2.*kbT) );
00319     }
00320     else
00321     { 
00322       ScalarT tmp = -1./4.+1./4.*sqrt(1.+8.*dopingConc/Nc*exp(dopantActE/kbT));
00323       builtinPotential = offset + V0*log(tmp);
00324     }  
00325   }
00326   
00327   // assume p = Na- to have an analytical expression (neglect n)
00328   else if(dopType == "Acceptor") 
00329   {
00330     ScalarT offset = (dopantActE +qPhiRef -Chi -Eg) /1.0; 
00331     if (dopantActE/kbT > MAX_EXPONENT)  // exp(dopantActE/kbT) -> +infinity for very small T
00332     {
00333       builtinPotential = offset -V0 *(0.5*log(dopingConc/(4.*Nv)) + dopantActE/(2.*kbT) );
00334     }
00335     else
00336     {
00337       ScalarT tmp = -1./8.+1./8.*sqrt(1.+16.*dopingConc/Nv*exp(dopantActE/kbT));
00338       builtinPotential = offset - V0*log(tmp);
00339     }
00340   }
00341 
00342   else 
00343   {
00344     TEUCHOS_TEST_FOR_EXCEPTION (true, Teuchos::Exceptions::InvalidParameter, std::endl 
00345       << "Error!  Unknown dopant type " << dopType << "!"<< std::endl);
00346   }
00347 
00348   return builtinPotential;
00349 }
00350 
00351 
00352 // *****************************************************************************
00353 template<typename EvalT,typename Traits>
00354 typename QCAD::PoissonNeumann<EvalT,Traits>::ScalarT
00355 QCAD::PoissonNeumann<EvalT,Traits>::potentialForFDComplIon(const ScalarT &Nc, 
00356     const ScalarT &Nv, const ScalarT &Eg, const double &Chi, const std::string &dopType, const double &dopingConc)
00357 {
00358   ScalarT builtinPotential;
00359     
00360   // assume n = Nd to have an analytical expression (neglect p)
00361   if(dopType == "Donor") 
00362   {
00363     ScalarT invFDInt = inverseFDIntOneHalf(dopingConc/Nc);
00364     builtinPotential = (qPhiRef-Chi)/1.0 + V0*invFDInt;
00365   }
00366   
00367   // assume p = Na to have an analytical expression (neglect n)
00368   else if(dopType == "Acceptor") 
00369   {
00370     ScalarT invFDInt = inverseFDIntOneHalf(dopingConc/Nv);
00371     builtinPotential = (qPhiRef-Chi-Eg)/1.0 - V0*invFDInt;
00372   }
00373   else {
00374     TEUCHOS_TEST_FOR_EXCEPTION (true, Teuchos::Exceptions::InvalidParameter, std::endl 
00375       << "Error!  Unknown dopant type " << dopType << "!"<< std::endl);
00376   }
00377     
00378   return builtinPotential;
00379 }
00380 
00381 
00382 // *****************************************************************************
00383 template<typename EvalT,typename Traits>
00384 typename QCAD::PoissonNeumann<EvalT,Traits>::ScalarT
00385 QCAD::PoissonNeumann<EvalT,Traits>::potentialForZeroKFDComplIon(const ScalarT &Nc, 
00386     const ScalarT &Nv, const ScalarT &Eg, const double &Chi, const std::string &dopType, const double &dopingConc)
00387 {
00388   const double pi = 3.1415926536;
00389 
00390   ScalarT builtinPotential;
00391     
00392   // assume n = Nd to have an analytical expression (neglect p)
00393   if(dopType == "Donor") 
00394   {
00395     if (dopingConc < Nc)  // Fermi level is below conduction band (due to doping)
00396       builtinPotential = potentialForFDComplIon(Nc,Nv,Eg,Chi,dopType,dopingConc);
00397     else  // Fermi level is in conduction band (degenerate)
00398     { 
00399       ScalarT invFDInt = pow(3./4.*sqrt(pi)*(dopingConc/Nc),2./3.);
00400       builtinPotential = (qPhiRef-Chi)/1.0+ V0*invFDInt;
00401     }
00402   }
00403   
00404   // assume p = Na to have an analytical expression (neglect n)
00405   else if(dopType == "Acceptor") 
00406   {
00407     if (dopingConc < Nv)  // Fermi level is above valence band (due to doping) 
00408       builtinPotential = potentialForFDComplIon(Nc,Nv,Eg,Chi,dopType,dopingConc); 
00409     else  // Fermi level is in valence band (degenerate)
00410     {  
00411       ScalarT invFDInt = pow(3./4.*sqrt(pi)*(dopingConc/Nv),2./3.);
00412       builtinPotential = (qPhiRef-Chi-Eg)/1.0- V0*invFDInt;
00413     }
00414   }
00415   
00416   else 
00417   {
00418     TEUCHOS_TEST_FOR_EXCEPTION (true, Teuchos::Exceptions::InvalidParameter, std::endl 
00419       << "Error!  Unknown dopant type " << dopType << "!"<< std::endl);
00420   }
00421     
00422   return builtinPotential;
00423 }
00424 
00425 
00426 
00427 // *****************************************************************************
00428 template<typename EvalT,typename Traits>
00429 typename QCAD::PoissonNeumann<EvalT,Traits>::ScalarT&
00430 QCAD::PoissonNeumann<EvalT,Traits>::getValue(const std::string &n) 
00431 {
00432   std::stringstream ss; ss << PHAL::NeumannBase<EvalT, Traits>::name << "[0]";
00433   if (n == ss.str())  return user_value;
00434   return PHAL::Neumann<EvalT, Traits>::getValue(n);
00435 }
00436 
00437 
00438 }  //end of QCAD namespace

Generated on Wed Mar 26 2014 18:36:43 for Albany: a Trilinos-based PDE code by  doxygen 1.7.1