00001
00002
00003
00004
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
00021
00022 if(this->inputConditions == "robin") {
00023
00024
00025 user_value = PHAL::NeumannBase<EvalT,Traits>::robin_vals[0];
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
00034
00035
00036
00037
00038 temperature = p.get<double>("Temperature");
00039
00040
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
00050 const double kbBoltz = 8.617343e-05;
00051 kbT = kbBoltz*temperature;
00052 V0 = kbBoltz*temperature/1.0;
00053
00054
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);
00069 ScalarT Eic = -Eg/2. + 3./4.*kbT*log(mdp/mdn);
00070 qPhiRef = Chi - Eic;
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;
00105
00106 bcValue = (user_value - offsetDueToWorkFunc) / energy_unit_in_eV;
00107 }
00108
00110 else {
00111
00112
00113 const double kbBoltz = 8.617343e-05;
00114 const double eleQ = 1.602176487e-19;
00115 const double m0 = 9.10938215e-31;
00116 const double hbar = 1.054571628e-34;
00117 const double pi = 3.141592654;
00118
00119
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
00143 double NcvFactor = 2.0*pow((kbBoltz*eleQ*m0*Tref)/(2*pi*pow(hbar,2)),3./2.)*1.e-6;
00144
00145
00146
00147 ScalarT Nc;
00148 ScalarT Nv;
00149 ScalarT Eg;
00150
00151 Nc = NcvFactor*pow(mdn,1.5)*pow(temperature/Tref,1.5);
00152 Nv = NcvFactor*pow(mdp,1.5)*pow(temperature/Tref,1.5);
00153 Eg = Eg0-alpha*pow(temperature,2.0)/(beta+temperature);
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
00163 if (dopantType == "None")
00164 {
00165
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;
00168 }
00169
00170 else
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
00195
00196
00197 else
00198 builtinPotential = potentialForMBIncomplIon(Nc,Nv,Eg,Chi,dopantType,dopingConc,dopantActE);
00199
00200 bcValue = (user_value + builtinPotential) / energy_unit_in_eV;
00201 }
00202 }
00203 }
00204
00206
00207 else
00208 bcValue = user_value / energy_unit_in_eV;
00209
00211 PHAL::NeumannBase<EvalT,Traits>::robin_vals[0] = bcValue;
00212
00214 PHAL::Neumann<EvalT,Traits>::evaluateFields(neumannWorkset);
00215
00216 std::string sideSetName = PHAL::NeumannBase<EvalT,Traits>::sideSetID;
00217
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
00227
00228
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
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
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
00309 const double MAX_EXPONENT = 100.0;
00310 ScalarT builtinPotential;
00311
00312
00313 if(dopType == "Donor")
00314 {
00315 ScalarT offset = (-dopantActE +qPhiRef -Chi) /1.0;
00316 if (dopantActE/kbT > MAX_EXPONENT)
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
00328 else if(dopType == "Acceptor")
00329 {
00330 ScalarT offset = (dopantActE +qPhiRef -Chi -Eg) /1.0;
00331 if (dopantActE/kbT > MAX_EXPONENT)
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
00361 if(dopType == "Donor")
00362 {
00363 ScalarT invFDInt = inverseFDIntOneHalf(dopingConc/Nc);
00364 builtinPotential = (qPhiRef-Chi)/1.0 + V0*invFDInt;
00365 }
00366
00367
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
00393 if(dopType == "Donor")
00394 {
00395 if (dopingConc < Nc)
00396 builtinPotential = potentialForFDComplIon(Nc,Nv,Eg,Chi,dopType,dopingConc);
00397 else
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
00405 else if(dopType == "Acceptor")
00406 {
00407 if (dopingConc < Nv)
00408 builtinPotential = potentialForFDComplIon(Nc,Nv,Eg,Chi,dopType,dopingConc);
00409 else
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 }