00001
00002
00003
00004
00005
00006
00007 #include <fstream>
00008 #include "Teuchos_TestForException.hpp"
00009 #include "Phalanx_DataLayout.hpp"
00010 #include "Sacado_ParameterRegistration.hpp"
00011
00012 const int MAX_MESH_REGIONS = 30;
00013 const int MAX_POINT_CHARGES = 10;
00014
00015 template<typename EvalT, typename Traits>
00016 QCAD::PoissonSource<EvalT, Traits>::
00017 PoissonSource(Teuchos::ParameterList& p,
00018 const Teuchos::RCP<Albany::Layouts>& dl) :
00019 coordVec(p.get<std::string>("Coordinate Vector Name"), dl->qp_vector),
00020 coordVecAtVertices(p.get<std::string>("Coordinate Vector Name"), dl->vertices_vector),
00021 weights("Weights", dl->qp_scalar),
00022 potential(p.get<std::string>("Variable Name"), dl->qp_scalar),
00023 temperatureField(p.get<std::string>("Temperature Name"), dl->shared_param),
00024 poissonSource(p.get<std::string>("Source Name"), dl->qp_scalar),
00025 chargeDensity("Charge Density",dl->qp_scalar),
00026 electronDensity("Electron Density",dl->qp_scalar),
00027 artCBDensity("Artificial Conduction Band Density",dl->qp_scalar),
00028 holeDensity("Hole Density",dl->qp_scalar),
00029 electricPotential("Electric Potential",dl->qp_scalar),
00030 ionizedDopant("Ionized Dopant",dl->qp_scalar),
00031 conductionBand("Conduction Band",dl->qp_scalar),
00032 valenceBand("Valence Band",dl->qp_scalar),
00033 approxQuanEDen("Approx Quantum EDensity",dl->qp_scalar)
00034 {
00035
00036 materialDB = p.get< Teuchos::RCP<QCAD::MaterialDatabase> >("MaterialDB");
00037
00038 Teuchos::ParameterList* psList = p.get<Teuchos::ParameterList*>("Parameter List");
00039
00040 Teuchos::RCP<const Teuchos::ParameterList> reflist =
00041 this->getValidPoissonSourceParameters();
00042 psList->validateParameters(*reflist,0);
00043
00044 std::vector<PHX::DataLayout::size_type> dims;
00045 dl->qp_vector->dimensions(dims);
00046 numQPs = dims[1];
00047 numDims = dims[2];
00048 numNodes = dl->node_scalar->dimension(1);
00049
00050
00051 factor = psList->get("Factor", 1.0);
00052 device = psList->get("Device", "defaultdevice");
00053 nonQuantumRegionSource = psList->get("Non Quantum Region Source", "semiclassical");
00054 quantumRegionSource = psList->get("Quantum Region Source", "semiclassical");
00055 imagPartOfCoulombSrc = psList->get<bool>("Imaginary Part of Coulomb Source", false);
00056 carrierStatistics = psList->get("Carrier Statistics", "Boltzmann Statistics");
00057 incompIonization = psList->get("Incomplete Ionization", "False");
00058 bUsePredictorCorrector = psList->get<bool>("Use predictor-corrector method",false);
00059 bIncludeVxc = psList->get<bool>("Include exchange-correlation potential",false);
00060 fixedQuantumOcc = psList->get<double>("Fixed Quantum Occupation",-1.0);
00061
00062
00063 std::string preName = "DBC on NS ";
00064 std::string postName = " for DOF Phi";
00065 std::size_t preLen = preName.length();
00066 std::size_t postLen = postName.length();
00067
00068
00069 const Teuchos::ParameterList& dbcPList = *(p.get<Teuchos::ParameterList*>("Dirichlet BCs ParameterList"));
00070 for (Teuchos::ParameterList::ConstIterator model_it = dbcPList.begin();
00071 model_it != dbcPList.end(); ++model_it)
00072 {
00073
00074 const std::string& dbcName = model_it->first;
00075 std::size_t nsNameLen = dbcName.length() - preLen - postLen;
00076 std::string nsName = dbcName.substr(preLen, nsNameLen);
00077
00078
00079 const std::string& ebName = materialDB->getNodeSetParam<std::string>(nsName,"elementBlock","");
00080
00081
00082 if (ebName.length() > 0)
00083 {
00084 double dbcValue = dbcPList.get<double>(dbcName);
00085 mapDBCValue_eb[ebName] = dbcValue;
00086 mapDBCValue_ns[nsName] = dbcValue;
00087
00088 }
00089 }
00090
00091
00092 if (device == "1D MOSCapacitor")
00093 {
00094 oxideWidth = psList->get("Oxide Width", 0.);
00095 siliconWidth = psList->get("Silicon Width", 0.);
00096 dopingAcceptor = psList->get("Acceptor Doping", 1e14);
00097 acceptorActE = psList->get("Acceptor Activation Energy", 0.045);
00098 }
00099
00100
00101 length_unit_in_m = p.get<double>("Length unit in m");
00102 energy_unit_in_eV = p.get<double>("Energy unit in eV");
00103
00104 if(quantumRegionSource == "schrodinger" ||
00105 quantumRegionSource == "coulomb" ||
00106 quantumRegionSource == "ci") {
00107 std::string evecFieldRoot = p.get<std::string>("Eigenvector field name root");
00108 nEigenvectors = psList->get<int>("Eigenvectors to Import");
00109 bRealEigenvectors = psList->get<bool>("Eigenvectors are Real", false);
00110
00111 char buf[200];
00112
00113 eigenvector_Re.resize(nEigenvectors);
00114 for (int k = 0; k < nEigenvectors; ++k) {
00115 sprintf(buf, "%s_Re%d", evecFieldRoot.c_str(), k);
00116 PHX::MDField<ScalarT,Cell,QuadPoint> fr(buf,dl->qp_scalar);
00117 eigenvector_Re[k] = fr; this->addDependentField(eigenvector_Re[k]);
00118 }
00119
00120 if(!bRealEigenvectors) {
00121 eigenvector_Im.resize(nEigenvectors);
00122 for (int k = 0; k < nEigenvectors; ++k) {
00123 sprintf(buf, "%s_Im%d", evecFieldRoot.c_str(), k);
00124 PHX::MDField<ScalarT,Cell,QuadPoint> fi(buf,dl->qp_scalar);
00125 eigenvector_Im[k] = fi; this->addDependentField(eigenvector_Im[k]);
00126 }
00127 }
00128 }
00129 else {
00130 nEigenvectors = 0;
00131 }
00132
00133
00134 prevDensityMixingFactor = -1.0;
00135
00136
00137 Teuchos::RCP<ParamLib> paramLib =
00138 p.get< Teuchos::RCP<ParamLib> >("Parameter Library", Teuchos::null);
00139 new Sacado::ParameterRegistration<EvalT, SPL_Traits>(
00140 "Poisson Source Factor", this, paramLib);
00141
00142
00143 std::vector<std::string> dopingParamNames = materialDB->getAllMatchingParams<std::string>("Doping Parameter Name");
00144 std::vector<std::string> chargeParamNames = materialDB->getAllMatchingParams<std::string>("Charge Parameter Name");
00145
00146 std::vector<std::string>::iterator s;
00147 for(s = dopingParamNames.begin(); s != dopingParamNames.end(); s++) {
00148 if( psList->isParameter(*s) ) {
00149 materialParams[*s] = psList->get<double>(*s);
00150 new Sacado::ParameterRegistration<EvalT, SPL_Traits>(*s, this, paramLib);
00151 }
00152 }
00153 for(s = chargeParamNames.begin(); s != chargeParamNames.end(); s++) {
00154 if( psList->isParameter(*s) ) {
00155 materialParams[*s] = psList->get<double>(*s);
00156 new Sacado::ParameterRegistration<EvalT, SPL_Traits>(*s, this, paramLib);
00157 }
00158 }
00159
00160
00161
00162 for(int i=0; i<MAX_MESH_REGIONS; i++) {
00163 std::string subListName = Albany::strint("Mesh Region",i);
00164 if( psList->isSublist(subListName) ) {
00165 std::string factorName = Albany::strint("Mesh Region Factor",i);
00166 new Sacado::ParameterRegistration<EvalT, SPL_Traits>(factorName, this, paramLib);
00167
00168
00169 Teuchos::RCP<const Teuchos::ParameterList> regionreflist =
00170 QCAD::MeshRegion<EvalT, Traits>::getValidParameters();
00171 Teuchos::ParameterList refsublist(*regionreflist);
00172 refsublist.set<double>("Factor Value",1.0,"Initial value of the factor corresponding to this mesh region");
00173 psList->sublist(subListName).validateParameters(refsublist,0);
00174
00175
00176 Teuchos::RCP<QCAD::MeshRegion<EvalT, Traits> > region =
00177 Teuchos::rcp( new QCAD::MeshRegion<EvalT, Traits>(p.get<std::string>("Coordinate Vector Name"),
00178 "Weights",psList->sublist(subListName),materialDB,dl) );
00179
00180 ScalarT value = psList->sublist(subListName).get<double>("Factor Value",1.0);
00181 meshRegionList.push_back(region);
00182 meshRegionFactors.push_back( value );
00183 }
00184 else break;
00185 }
00186
00187
00188 numWorksetsScannedForPtCharges = 0;
00189 Teuchos::RCP<Teuchos::ParameterList> ptChargeValidPL =
00190 rcp(new Teuchos::ParameterList("Valid Point Charge Params"));
00191 ptChargeValidPL->set<double>("X", 0.0, "x-coordinate of point charge");
00192 ptChargeValidPL->set<double>("Y", 0.0, "y-coordinate of point charge");
00193 ptChargeValidPL->set<double>("Z", 0.0, "z-coordinate of point charge");
00194 ptChargeValidPL->set<double>("Charge", 1.0, "Amount of charge in units of the elementary charge (default = +1)");
00195
00196 for(int i=0; i<MAX_POINT_CHARGES; i++) {
00197 std::string subListName = Albany::strint("Point Charge",i);
00198 if( psList->isSublist(subListName) ) {
00199
00200
00201 psList->sublist(subListName).validateParameters(*ptChargeValidPL,0);
00202
00203
00204
00205 PointCharge ptCharge;
00206 ptCharge.position[0] = psList->sublist(subListName).get<double>("X",0.0);
00207 ptCharge.position[1] = psList->sublist(subListName).get<double>("Y",0.0);
00208 ptCharge.position[2] = psList->sublist(subListName).get<double>("Z",0.0);
00209 ptCharge.charge = psList->sublist(subListName).get<double>("Charge",+1.0);
00210 ptCharge.iWorkset = ptCharge.iCell = -1;
00211
00212 pointCharges.push_back(ptCharge);
00213 }
00214 else break;
00215 }
00216
00217
00218 if(quantumRegionSource == "schrodinger") {
00219 new Sacado::ParameterRegistration<EvalT, SPL_Traits>("Previous Quantum Density Mixing Factor", this, paramLib);
00220 }
00221 else if(quantumRegionSource == "coulomb") {
00222
00223 new Sacado::ParameterRegistration<EvalT, SPL_Traits>("Source Eigenvector 1", this, paramLib);
00224 new Sacado::ParameterRegistration<EvalT, SPL_Traits>("Source Eigenvector 2", this, paramLib);
00225 }
00226
00227 this->addDependentField(potential);
00228 this->addDependentField(coordVec);
00229 this->addDependentField(coordVecAtVertices);
00230 this->addDependentField(weights);
00231 this->addDependentField(temperatureField);
00232
00233 typename std::vector< Teuchos::RCP<MeshRegion<EvalT, Traits> > >::iterator it;
00234 for(it = meshRegionList.begin(); it != meshRegionList.end(); it++)
00235 (*it)->addDependentFields(this);
00236
00237 this->addEvaluatedField(poissonSource);
00238 this->addEvaluatedField(chargeDensity);
00239 this->addEvaluatedField(electronDensity);
00240 this->addEvaluatedField(artCBDensity);
00241 this->addEvaluatedField(holeDensity);
00242 this->addEvaluatedField(electricPotential);
00243 this->addEvaluatedField(ionizedDopant);
00244 this->addEvaluatedField(conductionBand);
00245 this->addEvaluatedField(valenceBand);
00246 this->addEvaluatedField(approxQuanEDen);
00247
00248 this->setName("Poisson Source"+PHX::TypeString<EvalT>::value);
00249 }
00250
00251
00252 template<typename EvalT, typename Traits>
00253 void QCAD::PoissonSource<EvalT, Traits>::
00254 postRegistrationSetup(typename Traits::SetupData d,
00255 PHX::FieldManager<Traits>& fm)
00256 {
00257 this->utils.setFieldData(poissonSource,fm);
00258 this->utils.setFieldData(potential,fm);
00259 this->utils.setFieldData(coordVec,fm);
00260 this->utils.setFieldData(coordVecAtVertices,fm);
00261 this->utils.setFieldData(weights,fm);
00262 this->utils.setFieldData(temperatureField,fm);
00263
00264 this->utils.setFieldData(chargeDensity,fm);
00265 this->utils.setFieldData(electronDensity,fm);
00266 this->utils.setFieldData(artCBDensity,fm);
00267 this->utils.setFieldData(holeDensity,fm);
00268 this->utils.setFieldData(electricPotential,fm);
00269
00270 this->utils.setFieldData(ionizedDopant,fm);
00271 this->utils.setFieldData(conductionBand,fm);
00272 this->utils.setFieldData(valenceBand,fm);
00273 this->utils.setFieldData(approxQuanEDen,fm);
00274
00275 for (int k = 0; k < nEigenvectors; ++k)
00276 this->utils.setFieldData(eigenvector_Re[k],fm);
00277
00278 if(!bRealEigenvectors) {
00279 for (int k = 0; k < nEigenvectors; ++k)
00280 this->utils.setFieldData(eigenvector_Im[k],fm);
00281 }
00282
00283 typename std::vector< Teuchos::RCP<MeshRegion<EvalT, Traits> > >::iterator it;
00284 for(it = meshRegionList.begin(); it != meshRegionList.end(); it++)
00285 (*it)->postRegistrationSetup(fm);
00286
00287 }
00288
00289
00290 template<typename EvalT, typename Traits>
00291 void QCAD::PoissonSource<EvalT, Traits>::
00292 evaluateFields(typename Traits::EvalData workset)
00293 {
00294 if (device == "elementblocks") evaluateFields_elementblocks(workset);
00295
00296 else if (device == "1D MOSCapacitor") evaluateFields_moscap1d(workset);
00297
00299 else evaluateFields_default(workset);
00300 }
00301
00302
00303 template<typename EvalT,typename Traits>
00304 typename QCAD::PoissonSource<EvalT,Traits>::ScalarT&
00305 QCAD::PoissonSource<EvalT,Traits>::getValue(const std::string &n)
00306 {
00307 if(n == "Poisson Source Factor") return factor;
00308 else if( materialParams.find(n) != materialParams.end() ) return materialParams[n];
00309 else if( n == "Source Eigenvector 1") return sourceEvecInds[0];
00310 else if( n == "Source Eigenvector 2") return sourceEvecInds[1];
00311 else if( n == "Previous Quantum Density Mixing Factor") return prevDensityMixingFactor;
00312 else {
00313 int nRegions = meshRegionFactors.size();
00314 for(int i=0; i<nRegions; i++)
00315 if( n == Albany::strint("Mesh Region Factor",i) ) return meshRegionFactors[i];
00316
00317 TEUCHOS_TEST_FOR_EXCEPT(true);
00318 return factor;
00319 }
00320 }
00321
00322
00323 template<typename EvalT,typename Traits>
00324 Teuchos::RCP<const Teuchos::ParameterList>
00325 QCAD::PoissonSource<EvalT,Traits>::getValidPoissonSourceParameters() const
00326 {
00327 Teuchos::RCP<Teuchos::ParameterList> validPL =
00328 rcp(new Teuchos::ParameterList("Valid Poisson Problem Params"));;
00329
00330 validPL->set<double>("Factor", 1.0, "Constant multiplier in source term");
00331 validPL->set<std::string>("Device", "defaultdevice", "Switch between different device models");
00332 validPL->set<std::string>("Non Quantum Region Source", "semiclassical", "Source type for non-quantum regions");
00333 validPL->set<std::string>("Quantum Region Source", "semiclassical", "Source type for quantum regions");
00334 validPL->set<bool>("Imaginary Part Of Coulomb Source",false,"Whether to use imag or real part of coulomb quantum region source");
00335
00336 validPL->set<double>("Acceptor Doping", 1e14, "Doping for psilicon element blocks [cm^-3]");
00337 validPL->set<std::string>("Carrier Statistics", "Boltzmann Statistics", "Carrier statistics");
00338 validPL->set<std::string>("Incomplete Ionization", "False", "Partial ionization of dopants");
00339
00340 validPL->set<double>("Acceptor Activation Energy", 0.045, "Acceptor activation energy [eV]");
00341 validPL->set<int>("Eigenvectors to Import", 0, "Number of eigenvectors to take from eigendata information");
00342 validPL->set<bool>("Eigenvectors are Real", false, "Whether eigenvectors contain imaginary parts (which should be imported)");
00343 validPL->set<bool>("Use predictor-corrector method",false, "Enable use of predictor-corrector method for S-P iterations");
00344 validPL->set<bool>("Include exchange-correlation potential",false, "Include the exchange correlation term in the output potential state");
00345 validPL->set<bool>("Imaginary Part of Coulomb Source",false,"When 'Quantum Region Source' equals 'coulomb', whether to use imaginary or real part as source term.");
00346 validPL->set<double>("Fixed Quantum Occupation",-1.0, "The fixed number of quantum orbitals (one orbital == spin * valley degeneracy e-) to fill (non-equilibrium).");
00347
00348 validPL->set<double>("Oxide Width", 0., "Oxide width for 1D MOSCapacitor device");
00349 validPL->set<double>("Silicon Width", 0., "Silicon width for 1D MOSCapacitor device");
00350
00351 std::vector<std::string> dopingParamNames = materialDB->getAllMatchingParams<std::string>("Doping Parameter Name");
00352 std::vector<std::string> chargeParamNames = materialDB->getAllMatchingParams<std::string>("Charge Parameter Name");
00353 std::vector<std::string>::iterator s;
00354 for(s = dopingParamNames.begin(); s != dopingParamNames.end(); s++)
00355 validPL->set<double>( *s, 0.0, "Doping Parameter [cm^-3]");
00356 for(s = chargeParamNames.begin(); s != chargeParamNames.end(); s++)
00357 validPL->set<double>( *s, 0.0, "Charge Parameter [cm^-3]");
00358
00359 for(int i=0; i<MAX_MESH_REGIONS; i++) {
00360 std::string subListName = Albany::strint("Mesh Region",i);
00361 validPL->sublist(subListName, false, "Sublist defining a mesh region");
00362 }
00363
00364 for(int i=0; i<MAX_POINT_CHARGES; i++) {
00365 std::string subListName = Albany::strint("Point Charge",i);
00366 validPL->sublist(subListName, false, "Sublist defining a point charge");
00367 }
00368
00369 return validPL;
00370 }
00371
00372
00373
00374 template<typename EvalT, typename Traits>
00375 void QCAD::PoissonSource<EvalT, Traits>::
00376 evaluateFields_elementblocks(typename Traits::EvalData workset)
00377 {
00378 using std::string;
00379
00380 ScalarT mrsFromEBTest = 1.0;
00381 ScalarT scaleFactor = factor / energy_unit_in_eV;
00382
00383 bool isQuantum = materialDB->getElementBlockParam<bool>(workset.EBName,"quantum",false);
00384 string matrlCategory = materialDB->getElementBlockParam<string>(workset.EBName,"Category");
00385 string sourceName = isQuantum ? quantumRegionSource : nonQuantumRegionSource;
00386
00387
00388
00389 std::size_t nRegions = meshRegionList.size();
00390 std::vector<bool> bEBInRegion(nRegions,false);
00391 for(std::size_t i=0; i<nRegions; i++) {
00392 if(meshRegionList[i]->elementBlockIsInRegion(workset.EBName)) {
00393 mrsFromEBTest *= meshRegionFactors[i];
00394 bEBInRegion[i] = true;
00395 }
00396 }
00397
00399 void (QCAD::PoissonSource<EvalT,Traits>::*sourceCalc) (const typename Traits::EvalData workset, std::size_t cell, std::size_t qp,
00400 const ScalarT& scaleFactor, const PoissonSourceSetupInfo& setup_info);
00401
00402
00403 if(matrlCategory == "Metal") sourceName = "none";
00404
00405 if(sourceName == "semiclassical") sourceCalc = &QCAD::PoissonSource<EvalT,Traits>::source_semiclassical;
00406 else if(sourceName == "none") sourceCalc = &QCAD::PoissonSource<EvalT,Traits>::source_none;
00407 else if(sourceName == "schrodinger") sourceCalc = &QCAD::PoissonSource<EvalT,Traits>::source_quantum;
00408 else if(sourceName == "ci") sourceCalc = &QCAD::PoissonSource<EvalT,Traits>::source_quantum;
00409 else if(sourceName == "coulomb") sourceCalc = &QCAD::PoissonSource<EvalT,Traits>::source_coulomb;
00410 else if(sourceName == "testcoulomb") sourceCalc = &QCAD::PoissonSource<EvalT,Traits>::source_testcoulomb;
00411 else {
00412 TEUCHOS_TEST_FOR_EXCEPTION (true, Teuchos::Exceptions::InvalidParameter,
00413 std::endl << "Error! Unknown source name: " << sourceName << "!"<< std::endl);
00414 }
00415
00416 PoissonSourceSetupInfo setup_info = source_setup(sourceName, matrlCategory, workset);
00417 for (std::size_t cell=0; cell < workset.numCells; ++cell)
00418 {
00419 scaleFactor = getCellScaleFactor(cell, bEBInRegion, mrsFromEBTest*factor / energy_unit_in_eV);
00420 for (std::size_t qp=0; qp < numQPs; ++qp)
00421 (this->*sourceCalc)(workset, cell, qp, scaleFactor, setup_info);
00422 }
00423
00424
00425 if(pointCharges.size() > 0)
00426 source_pointcharges(workset);
00427
00428 }
00429
00430
00431
00432 template<typename EvalT, typename Traits>
00433 void QCAD::PoissonSource<EvalT, Traits>::
00434 evaluateFields_default(typename Traits::EvalData workset)
00435 {
00436 MeshScalarT* coord;
00437 ScalarT charge;
00438
00439 for (std::size_t cell=0; cell < workset.numCells; ++cell)
00440 {
00441 for (std::size_t qp=0; qp < numQPs; ++qp)
00442 {
00443 coord = &coordVec(cell,qp,0);
00444 const ScalarT& phi = potential(cell,qp);
00445
00446 switch (numDims) {
00447 case 2:
00448 if (coord[1]<0.8) charge = (coord[1]*coord[1]);
00449 else charge = 3.0;
00450 charge *= (1.0 + exp(-phi));
00451 chargeDensity(cell, qp) = charge;
00452 break;
00453 default: TEUCHOS_TEST_FOR_EXCEPT(true);
00454 }
00455
00456
00457 poissonSource(cell, qp) = factor*charge;
00458
00459
00460 chargeDensity(cell, qp) = 0.0;
00461 electronDensity(cell, qp) = 0.0;
00462 holeDensity(cell, qp) = 0.0;
00463 electricPotential(cell, qp) = phi;
00464 ionizedDopant(cell, qp) = 0.0;
00465 conductionBand(cell, qp) = 0.0;
00466 valenceBand(cell, qp) = 0.01;
00467 approxQuanEDen(cell,qp) = 0.0;
00468 artCBDensity(cell, qp) = 0.0;
00469 }
00470 }
00471 }
00472
00473
00474
00475 template<typename EvalT, typename Traits>
00476 void QCAD::PoissonSource<EvalT, Traits>::
00477 evaluateFields_moscap1d(typename Traits::EvalData workset)
00478 {
00479
00480 ScalarT temperature = temperatureField(0);
00481
00482
00483 double X0 = length_unit_in_m/1e-2;
00484 ScalarT V0 = kbBoltz*temperature/1.0;
00485 ScalarT Lambda2 = eps0/(eleQ*X0*X0);
00486
00488 ScalarT qPhiRef;
00489 {
00490 std::string refMtrlName, category;
00491 refMtrlName = materialDB->getParam<std::string>("Reference Material");
00492 category = materialDB->getMaterialParam<std::string>(refMtrlName,"Category");
00493 if (category == "Semiconductor")
00494 {
00495
00496 double mdn = materialDB->getMaterialParam<double>(refMtrlName,"Electron DOS Effective Mass");
00497 double mdp = materialDB->getMaterialParam<double>(refMtrlName,"Hole DOS Effective Mass");
00498 double Chi = materialDB->getMaterialParam<double>(refMtrlName,"Electron Affinity");
00499 double Eg0 = materialDB->getMaterialParam<double>(refMtrlName,"Zero Temperature Band Gap");
00500 double alpha = materialDB->getMaterialParam<double>(refMtrlName,"Band Gap Alpha Coefficient");
00501 double beta = materialDB->getMaterialParam<double>(refMtrlName,"Band Gap Beta Coefficient");
00502 ScalarT Eg = Eg0-alpha*pow(temperature,2.0)/(beta+temperature);
00503
00504 ScalarT kbT = kbBoltz*temperature;
00505 ScalarT Eic = -Eg/2. + 3./4.*kbT*log(mdp/mdn);
00506 qPhiRef = Chi - Eic;
00507 }
00508 else
00509 {
00510 TEUCHOS_TEST_FOR_EXCEPTION (true, Teuchos::Exceptions::InvalidParameter, std::endl
00511 << "Error! Invalid category " << category
00512 << " for reference material !" << std::endl);
00513 }
00514 }
00515
00516 MeshScalarT* coord;
00517
00518 for (std::size_t cell = 0; cell < workset.numCells; ++cell)
00519 {
00520 for (std::size_t qp = 0; qp < numQPs; ++qp)
00521 {
00522 coord = &coordVec(cell,qp,0);
00523
00524
00525 if ( (coord[0] > oxideWidth) && (coord[0] <= (oxideWidth + siliconWidth)) )
00526 {
00527 const std::string matName = "Silicon";
00528
00530 double mdn = materialDB->getMaterialParam<double>(matName,"Electron DOS Effective Mass");
00531 double mdp = materialDB->getMaterialParam<double>(matName,"Hole DOS Effective Mass");
00532 double Tref = materialDB->getMaterialParam<double>(matName,"Reference Temperature");
00533
00534 double Chi = materialDB->getMaterialParam<double>(matName,"Electron Affinity");
00535 double Eg0 = materialDB->getMaterialParam<double>(matName,"Zero Temperature Band Gap");
00536 double alpha = materialDB->getMaterialParam<double>(matName,"Band Gap Alpha Coefficient");
00537 double beta = materialDB->getMaterialParam<double>(matName,"Band Gap Beta Coefficient");
00538
00539
00540 double NcvFactor = 2.0*pow((kbBoltz*eleQ*m0*Tref)/(2*pi*pow(hbar,2)),3./2.)*1e-6;
00541
00542
00544 ScalarT Nc;
00545 ScalarT Nv;
00546 ScalarT Eg;
00547
00548
00549 Nc = NcvFactor*pow(mdn,1.5)*pow(temperature/Tref,1.5);
00550 Nv = NcvFactor*pow(mdp,1.5)*pow(temperature/Tref,1.5);
00551 Eg = Eg0-alpha*pow(temperature,2.0)/(beta+temperature);
00552
00553 ScalarT kbT = kbBoltz*temperature;
00554
00555
00556
00557 ScalarT eArgOffset = (-qPhiRef+Chi)/kbT;
00558 ScalarT hArgOffset = (qPhiRef-Chi-Eg)/kbT;
00559
00561 double ml = materialDB->getMaterialParam<double>(matName,"Longitudinal Electron Effective Mass");
00562 double mt = materialDB->getMaterialParam<double>(matName,"Transverse Electron Effective Mass");
00563 double invEffMass = (2.0/mt + 1.0/ml) / 3.0;
00564 double averagedEffMass = 1.0 / invEffMass;
00565 double relPerm = materialDB->getMaterialParam<double>(matName,"Permittivity");
00566
00568 ScalarT (QCAD::PoissonSource<EvalT,Traits>::*carrStat) (const ScalarT);
00569
00570 if (carrierStatistics == "Boltzmann Statistics")
00571 carrStat = &QCAD::PoissonSource<EvalT,Traits>::computeMBStat;
00572
00573 else if (carrierStatistics == "Fermi-Dirac Statistics")
00574 carrStat = &QCAD::PoissonSource<EvalT,Traits>::computeFDIntOneHalf;
00575
00576 else if (carrierStatistics == "0-K Fermi-Dirac Statistics")
00577 carrStat = &QCAD::PoissonSource<EvalT,Traits>::computeZeroKFDInt;
00578
00579 else TEUCHOS_TEST_FOR_EXCEPTION (true, Teuchos::Exceptions::InvalidParameter,
00580 std::endl << "Error! Unknown carrier statistics ! " << std::endl);
00581
00583 ScalarT (QCAD::PoissonSource<EvalT,Traits>::*ionDopant) (const std::string, const ScalarT&);
00584
00585 if (incompIonization == "False")
00586 ionDopant = &QCAD::PoissonSource<EvalT,Traits>::fullDopants;
00587
00588 else if (incompIonization == "True")
00589 ionDopant = &QCAD::PoissonSource<EvalT,Traits>::ionizedDopants;
00590
00591 else TEUCHOS_TEST_FOR_EXCEPTION (true, Teuchos::Exceptions::InvalidParameter,
00592 std::endl << "Error! Invalid incomplete ionization option ! " << std::endl);
00593
00595 const std::string dopantType = "Acceptor";
00596 ScalarT inArg, dopingConc, dopantActE;
00597 dopingConc = dopingAcceptor;
00598 dopantActE = acceptorActE;
00599
00600 if(dopantType == "Donor")
00601 inArg = eArgOffset + dopantActE/kbT;
00602 else if(dopantType == "Acceptor")
00603 inArg = hArgOffset + dopantActE/kbT;
00604 else TEUCHOS_TEST_FOR_EXCEPTION (true, Teuchos::Exceptions::InvalidParameter,
00605 std::endl << "Error! Unknown dopant type " << dopantType << "!"<< std::endl);
00606
00608 if(quantumRegionSource == "schrodinger")
00609 {
00610
00611 ScalarT prevPhi = 0.0, approxEDensity = 0.0;
00612
00613 if(bUsePredictorCorrector) {
00614
00615 Albany::MDArray prevPhiArray = (*workset.stateArrayPtr)["PS Previous Poisson Potential"];
00616 prevPhi = prevPhiArray(cell,qp);
00617 approxEDensity = eDensityForPoissonSchrodinger(workset, cell, qp, prevPhi, true, 0.0, -1.0);
00618 }
00619 else
00620 approxEDensity = eDensityForPoissonSchrodinger(workset, cell, qp, 0.0, false, 0.0, -1.0);
00621
00622
00623 ScalarT eDensity = eDensityForPoissonSchrodinger(workset, cell, qp, 0.0, false, 0.0, -1.0);
00624
00625
00626 const ScalarT& unscaled_phi = potential(cell,qp);
00627 ScalarT phi = unscaled_phi / V0;
00628
00629
00630 ScalarT hDensity = Nv*(this->*carrStat)(-phi+hArgOffset);
00631
00632
00633 ScalarT ionN = 0.0;
00634 if (dopantType == "Donor")
00635 ionN = (this->*ionDopant)(dopantType,phi+inArg)*dopingConc;
00636 else if (dopantType == "Acceptor")
00637 ionN = (this->*ionDopant)(dopantType,-phi+inArg)*dopingConc;
00638 else
00639 ionN = 0.0;
00640
00641
00642 ScalarT charge;
00643 charge = 1.0/Lambda2 * (hDensity- approxEDensity + ionN);
00644 poissonSource(cell, qp) = factor*charge;
00645
00646
00647 chargeDensity(cell, qp) = hDensity -eDensity +ionN;
00648 electronDensity(cell, qp) = eDensity;
00649 holeDensity(cell, qp) = hDensity;
00650 electricPotential(cell, qp) = phi*V0 - qPhiRef;
00651 ionizedDopant(cell, qp) = ionN;
00652 approxQuanEDen(cell,qp) = approxEDensity;
00653 artCBDensity(cell, qp) = ( eDensity > 1e-6 ? eDensity : -Nc*(this->*carrStat)( -(phi+eArgOffset) ));
00654
00655 if (bIncludeVxc)
00656 {
00657 ScalarT Vxc = computeVxcLDA(relPerm, averagedEffMass, approxEDensity);
00658 conductionBand(cell, qp) = qPhiRef-Chi-phi*V0 +Vxc;
00659 electricPotential(cell, qp) = phi*V0 + Vxc - qPhiRef;
00660
00661 }
00662 else {
00663 conductionBand(cell, qp) = qPhiRef-Chi-phi*V0;
00664 electricPotential(cell, qp) = phi*V0 - qPhiRef;
00665
00666 }
00667
00668 valenceBand(cell, qp) = conductionBand(cell,qp)-Eg;
00669
00670 }
00671
00673 else
00674 {
00675
00676 const ScalarT& unscaled_phi = potential(cell,qp);
00677 ScalarT phi = unscaled_phi / V0;
00678
00679
00680 ScalarT ionN;
00681 if (dopantType == "Donor")
00682 ionN = (this->*ionDopant)(dopantType,phi+inArg)*dopingConc;
00683 else if (dopantType == "Acceptor")
00684 ionN = (this->*ionDopant)(dopantType,-phi+inArg)*dopingConc;
00685 else
00686 ionN = 0.0;
00687
00688
00689 ScalarT charge, eDensity, hDensity;
00690 eDensity = Nc*(this->*carrStat)(phi+eArgOffset);
00691 hDensity = Nv*(this->*carrStat)(-phi+hArgOffset);
00692 charge = 1.0/Lambda2 * (hDensity - eDensity + ionN);
00693 poissonSource(cell, qp) = factor*charge;
00694
00695
00696 chargeDensity(cell, qp) = charge*Lambda2;
00697 electronDensity(cell, qp) = eDensity;
00698 holeDensity(cell, qp) = hDensity;
00699 electricPotential(cell, qp) = phi*V0 - qPhiRef;
00700 ionizedDopant(cell, qp) = ionN;
00701 conductionBand(cell, qp) = qPhiRef-Chi-phi*V0;
00702 valenceBand(cell, qp) = conductionBand(cell,qp)-Eg;
00703 approxQuanEDen(cell,qp) = 0.0;
00704 artCBDensity(cell, qp) = ( eDensity > 1e-6 ? eDensity
00705 : -Nc*(this->*carrStat)( -(phi+eArgOffset) ));
00706 }
00707
00708 }
00709
00710
00711
00712 else if ((coord[0] >= 0) && (coord[0] <= oxideWidth))
00713 {
00714 const std::string matName = "SiliconDioxide" ;
00715 double Eg = materialDB->getMaterialParam<double>(matName,"Band Gap",0.0);
00716 double Chi = materialDB->getMaterialParam<double>(matName,"Electron Affinity",0.0);
00717
00719 double ml = materialDB->getMaterialParam<double>(matName,"Longitudinal Electron Effective Mass");
00720 double mt = materialDB->getMaterialParam<double>(matName,"Transverse Electron Effective Mass");
00721 double invEffMass = (2.0/mt + 1.0/ml) / 3.0;
00722 double averagedEffMass = 1.0 / invEffMass;
00723 double relPerm = materialDB->getMaterialParam<double>(matName,"Permittivity");
00724
00725 ScalarT fixedCharge = 0.0;
00726
00728 if(quantumRegionSource == "schrodinger")
00729 {
00730
00731 ScalarT prevPhi = 0.0, approxEDensity = 0.0;
00732
00733 if (bUsePredictorCorrector) {
00734 Albany::MDArray prevPhiArray = (*workset.stateArrayPtr)["PS Previous Poisson Potential"];
00735 prevPhi = prevPhiArray(cell,qp);
00736
00737
00738 approxEDensity = eDensityForPoissonSchrodinger(workset, cell, qp, prevPhi, true, 0.0, -1.0);
00739 }
00740 else
00741 approxEDensity = eDensityForPoissonSchrodinger(workset, cell, qp, 0.0, false, 0.0, -1.0);
00742
00743
00744 ScalarT eDensity = eDensityForPoissonSchrodinger(workset, cell, qp, 0.0, false, 0.0, -1.0);
00745
00746
00747 const ScalarT& unscaled_phi = potential(cell,qp);
00748 ScalarT phi = unscaled_phi / V0;
00749
00750
00751
00752
00753 ScalarT charge;
00754 charge = 1.0/Lambda2 * (-approxEDensity + fixedCharge);
00755 poissonSource(cell, qp) = factor*charge;
00756
00757
00758 chargeDensity(cell, qp) = -eDensity + fixedCharge;
00759 electronDensity(cell, qp) = eDensity;
00760 holeDensity(cell, qp) = 0.0;
00761 ionizedDopant(cell, qp) = 0.0;
00762 approxQuanEDen(cell,qp) = approxEDensity;
00763 artCBDensity(cell, qp) = eDensity;
00764
00765 if (bIncludeVxc)
00766 {
00767 ScalarT Vxc = computeVxcLDA(relPerm, averagedEffMass, approxEDensity);
00768 conductionBand(cell, qp) = qPhiRef-Chi-phi*V0 +Vxc;
00769 electricPotential(cell, qp) = phi*V0 + Vxc - qPhiRef;
00770
00771 }
00772 else {
00773 conductionBand(cell, qp) = qPhiRef-Chi-phi*V0;
00774 electricPotential(cell, qp) = phi*V0 - qPhiRef;
00775
00776 }
00777
00778 valenceBand(cell, qp) = conductionBand(cell,qp)-Eg;
00779 }
00780
00781 else
00782 {
00783 const ScalarT& unscaled_phi = potential(cell,qp);
00784 ScalarT phi = unscaled_phi / V0;
00785
00786
00787 ScalarT charge;
00788 charge = 1.0/Lambda2 * fixedCharge;
00789 poissonSource(cell, qp) = factor*charge;
00790
00791 chargeDensity(cell, qp) = fixedCharge;
00792 electronDensity(cell, qp) = 0.0;
00793 holeDensity(cell, qp) = 0.0;
00794 electricPotential(cell, qp) = phi*V0 - qPhiRef;
00795 ionizedDopant(cell, qp) = 0.0;
00796 conductionBand(cell, qp) = qPhiRef-Chi-phi*V0;
00797 valenceBand(cell, qp) = conductionBand(cell,qp)-Eg;
00798 approxQuanEDen(cell,qp) = 0.0;
00799 artCBDensity(cell, qp) = 0.0;
00800 }
00801
00802 }
00803
00804 else
00805 TEUCHOS_TEST_FOR_EXCEPTION (true, Teuchos::Exceptions::InvalidParameter,
00806 std::endl << "Error! x-coord:" << coord[0] << "is outside the oxideWidth" <<
00807 " + siliconWidth range: " << oxideWidth + siliconWidth << "!"<< std::endl);
00808
00809 }
00810
00811 }
00812
00813 }
00814
00815
00816
00817
00818
00820
00821
00822
00823 template<typename EvalT, typename Traits>
00824 typename QCAD::PoissonSource<EvalT,Traits>::PoissonSourceSetupInfo
00825 QCAD::PoissonSource<EvalT, Traits>::source_setup(const std::string& sourceName, const std::string& mtrlCategory,
00826 const typename Traits::EvalData workset)
00827 {
00828 PoissonSourceSetupInfo ret;
00829
00830 double X0 = length_unit_in_m/1e-2;
00831 ScalarT temperature = temperatureField(0);
00832 ret.kbT = kbBoltz*temperature / energy_unit_in_eV;
00833
00835 ret.qPhiRef = getReferencePotential(workset);
00836
00837 ret.V0 = kbBoltz*temperature / energy_unit_in_eV;
00838 ret.Lambda2 = eps0/(eleQ*X0*X0);
00839
00840 if(mtrlCategory == "Semiconductor") {
00841
00843 double mdn = materialDB->getElementBlockParam<double>(workset.EBName,"Electron DOS Effective Mass");
00844 double mdp = materialDB->getElementBlockParam<double>(workset.EBName,"Hole DOS Effective Mass");
00845 double Tref = materialDB->getElementBlockParam<double>(workset.EBName,"Reference Temperature");
00846
00847 ret.Chi = materialDB->getElementBlockParam<double>(workset.EBName,"Electron Affinity") / energy_unit_in_eV;
00848 double Eg0 = materialDB->getElementBlockParam<double>(workset.EBName,"Zero Temperature Band Gap") / energy_unit_in_eV;
00849 double alpha = materialDB->getElementBlockParam<double>(workset.EBName,"Band Gap Alpha Coefficient");
00850 double beta = materialDB->getElementBlockParam<double>(workset.EBName,"Band Gap Beta Coefficient");
00851
00853 double NcvFactor = 2.0*pow((kbBoltz*eleQ*m0*Tref)/(2*pi*pow(hbar,2)),3./2.)*1e-6;
00854
00855
00856 ret.Nc = NcvFactor*pow(mdn,1.5)*pow(temperature/Tref,1.5);
00857 ret.Nv = NcvFactor*pow(mdp,1.5)*pow(temperature/Tref,1.5);
00858 ret.Eg = Eg0 - (alpha*pow(temperature,2.0)/(beta+temperature)) / energy_unit_in_eV;
00859
00860
00861
00862
00864 const std::string& condBandMin = materialDB->getElementBlockParam<std::string>(workset.EBName,"Conduction Band Minimum");
00865 double ml = materialDB->getElementBlockParam<double>(workset.EBName,"Longitudinal Electron Effective Mass");
00866 double mt = materialDB->getElementBlockParam<double>(workset.EBName,"Transverse Electron Effective Mass");
00867 if ((condBandMin == "Gamma Valley") && (abs(ml-mt) > 1e-10))
00868 TEUCHOS_TEST_FOR_EXCEPTION (true, std::logic_error, "Gamma Valley's longitudinal and "
00869 << "transverse electron effective mass must be equal ! "
00870 << "Please check the values in materials.xml" << std::endl);
00871
00872 double invEffMass = (2.0/mt + 1.0/ml) / 3.0;
00873 ret.averagedEffMass = 1.0 / invEffMass;
00874 ret.relPerm = materialDB->getElementBlockParam<double>(workset.EBName,"Permittivity");
00875
00877 ret.eArgOffset = (-ret.qPhiRef+ret.Chi)/ret.kbT;
00878 ret.hArgOffset = (ret.qPhiRef-ret.Chi-ret.Eg)/ret.kbT;
00879
00880 if (carrierStatistics == "Boltzmann Statistics")
00881 ret.carrStat = &QCAD::PoissonSource<EvalT,Traits>::computeMBStat;
00882
00883 else if (carrierStatistics == "Fermi-Dirac Statistics")
00884 ret.carrStat = &QCAD::PoissonSource<EvalT,Traits>::computeFDIntOneHalf;
00885
00886 else if (carrierStatistics == "0-K Fermi-Dirac Statistics")
00887 ret.carrStat = &QCAD::PoissonSource<EvalT,Traits>::computeZeroKFDInt;
00888
00889 else TEUCHOS_TEST_FOR_EXCEPTION (true, Teuchos::Exceptions::InvalidParameter,
00890 std::endl << "Error! Unknown carrier statistics ! " << std::endl);
00891
00892 if (incompIonization == "False")
00893 ret.ionDopant = &QCAD::PoissonSource<EvalT,Traits>::fullDopants;
00894
00895 else if (incompIonization == "True")
00896 ret.ionDopant = &QCAD::PoissonSource<EvalT,Traits>::ionizedDopants;
00897
00898 else TEUCHOS_TEST_FOR_EXCEPTION (true, Teuchos::Exceptions::InvalidParameter,
00899 std::endl << "Error! Invalid incomplete ionization option ! " << std::endl);
00900
00902
00903
00904
00905
00906
00907
00908
00909
00910
00911 ret.fermiE = 0.0;
00912 if (mapDBCValue_eb.count(workset.EBName) > 0)
00913 {
00914 ret.fermiE = -1.0*mapDBCValue_eb[workset.EBName] / energy_unit_in_eV;
00915
00916 }
00917 else if(materialDB->isElementBlockParam(workset.EBName, "contactNodeset"))
00918 {
00919 std::string nsName = materialDB->getElementBlockParam<std::string>(workset.EBName, "contactNodeset");
00920 if(mapDBCValue_ns.count(nsName) > 0)
00921 ret.fermiE = -1.0*mapDBCValue_ns[nsName] / energy_unit_in_eV;
00922 }
00923
00925
00926 ret.fixedChargeType = materialDB->getElementBlockParam<std::string>(workset.EBName,"Dopant Type","None");
00927 ret.fixedChargeConc = 0.0;
00928 std::string dopingProfile;
00929
00930 if(ret.fixedChargeType != "None") {
00931 double dopantActE;
00932 dopingProfile = materialDB->getElementBlockParam<std::string>(workset.EBName,"Doping Profile","Constant");
00933 dopantActE = materialDB->getElementBlockParam<double>(workset.EBName,"Dopant Activation Energy",0.045) / energy_unit_in_eV;
00934
00935 if( materialDB->isElementBlockParam(workset.EBName, "Doping Value") )
00936 ret.dopingConc = materialDB->getElementBlockParam<double>(workset.EBName,"Doping Value");
00937 else if( materialDB->isElementBlockParam(workset.EBName, "Doping Parameter Name") ) {
00938 double scl = materialDB->getElementBlockParam<double>(workset.EBName,"Doping Parameter Scaling", 1.0);
00939 ret.dopingConc = materialParams[ materialDB->getElementBlockParam<std::string>(workset.EBName,"Doping Parameter Name") ] * scl;
00940 }
00941 else TEUCHOS_TEST_FOR_EXCEPTION (true, Teuchos::Exceptions::InvalidParameter,
00942 std::endl << "Error! Unknown dopant concentration for " << workset.EBName << "!"<< std::endl);
00943
00944 if(ret.fixedChargeType == "Donor")
00945 ret.inArg = ret.eArgOffset + dopantActE/ret.kbT + ret.fermiE/ret.kbT;
00946 else if(ret.fixedChargeType == "Acceptor")
00947 ret.inArg = ret.hArgOffset + dopantActE/ret.kbT - ret.fermiE/ret.kbT;
00948 else TEUCHOS_TEST_FOR_EXCEPTION (true, Teuchos::Exceptions::InvalidParameter,
00949 std::endl << "Error! Unknown dopant type " << ret.fixedChargeType << "!"<< std::endl);
00950 }
00951 else {
00952 dopingProfile = "Constant";
00953 ret.dopingConc = 0.0;
00954 ret.inArg = 0.0;
00955 }
00956
00957 }
00958
00959 else if(mtrlCategory == "Insulator")
00960 {
00961 ret.Eg = materialDB->getElementBlockParam<double>(workset.EBName,"Band Gap",0.0) / energy_unit_in_eV;
00962 ret.Chi = materialDB->getElementBlockParam<double>(workset.EBName,"Electron Affinity",0.0) / energy_unit_in_eV;
00963 ret.carrStat = &QCAD::PoissonSource<EvalT,Traits>::computeZeroStat;
00964
00965
00966 ret.Nc = ret.Nv = 0.0;
00967 ret.hArgOffset = ret.eArgOffset = 0.0;
00968 ret.fermiE = 0.0;
00969 ret.dopingConc = 0.0;
00970
00972 if( materialDB->isElementBlockParam(workset.EBName, "Charge Value") ) {
00973 ret.fixedChargeType = "Constant";
00974 ret.fixedChargeConc = materialDB->getElementBlockParam<double>(workset.EBName,"Charge Value");
00975
00976 }
00977 else if( materialDB->isElementBlockParam(workset.EBName, "Charge Parameter Name") ) {
00978 double scl = materialDB->getElementBlockParam<double>(workset.EBName,"Charge Parameter Scaling", 1.0);
00979 ret.fixedChargeType = "Constant";
00980 ret.fixedChargeConc = materialParams[ materialDB->getElementBlockParam<std::string>(workset.EBName,"Charge Parameter Name") ] * scl;
00981
00982 }
00983 else {
00984 ret.fixedChargeType = "None";
00985 ret.fixedChargeConc = 0.0;
00986 }
00987
00989 double ml = materialDB->getElementBlockParam<double>(workset.EBName,"Longitudinal Electron Effective Mass");
00990 double mt = materialDB->getElementBlockParam<double>(workset.EBName,"Transverse Electron Effective Mass");
00991 if (abs(ml-mt) > 1e-10)
00992 TEUCHOS_TEST_FOR_EXCEPTION (true, std::logic_error, "Insulator's longitudinal and "
00993 << "transverse electron effective mass must be equal ! "
00994 << "Please check the values in materials.xml" << std::endl);
00995
00996 double invEffMass = (2.0/mt + 1.0/ml) / 3.0;
00997 ret.averagedEffMass = 1.0 / invEffMass;
00998 ret.relPerm = materialDB->getElementBlockParam<double>(workset.EBName,"Permittivity");
00999 }
01000
01001
01002 else if(mtrlCategory == "Metal")
01003 {
01004
01005 ret.Chi = materialDB->getElementBlockParam<double>(workset.EBName,"Work Function") / energy_unit_in_eV;
01006 ret.Eg = 0.0;
01007 }
01008
01009 else {
01010 TEUCHOS_TEST_FOR_EXCEPTION (true, Teuchos::Exceptions::InvalidParameter,
01011 std::endl << "Error! Unknown material category "
01012 << mtrlCategory << "!" << std::endl);
01013 }
01014
01015
01016 ret.prevDensityFactor = QCAD::EvaluatorTools<EvalT,Traits>::getDoubleValue( prevDensityMixingFactor );
01017
01018
01019 static double lastNonDefaultFactor = -1.0;
01020 if(ret.prevDensityFactor < 0.0) {
01021 ret.prevDensityFactor = lastNonDefaultFactor;
01022
01023 }
01024 if(ret.prevDensityFactor != lastNonDefaultFactor) {
01025 std::cout << "DEBUG: setup prevDensityMixingFactor = " << ret.prevDensityFactor
01026 << " (lastDef = " << lastNonDefaultFactor << ", scalarT = " << prevDensityMixingFactor << ")" << std::endl;
01027 lastNonDefaultFactor = ret.prevDensityFactor;
01028 }
01029
01030
01031 if(ret.prevDensityFactor > 1e-8)
01032 ret.prevDensityArray = (*workset.stateArrayPtr)["PS Previous Electron Density"];
01033
01034 ret.quantum_edensity_fn = NULL;
01035
01036
01037 if(sourceName == "schrodinger") {
01038 if(bUsePredictorCorrector)
01039 ret.prevPhiArray = (*workset.stateArrayPtr)["PS Previous Poisson Potential"];
01040
01041 ret.quantum_edensity_fn = &QCAD::PoissonSource<EvalT,Traits>::eDensityForPoissonSchrodinger;
01042 }
01043 else if(sourceName == "ci") {
01044 if(bUsePredictorCorrector)
01045 ret.prevPhiArray = (*workset.stateArrayPtr)["PS Previous Poisson Potential"];
01046
01047 ret.quantum_edensity_fn = &QCAD::PoissonSource<EvalT,Traits>::eDensityForPoissonCI;
01048 }
01049 else if(sourceName == "coulomb") {
01050
01051 ret.sourceEvec1 = (int)QCAD::EvaluatorTools<EvalT,Traits>::getDoubleValue( sourceEvecInds[0] );
01052 ret.sourceEvec2 = (int)QCAD::EvaluatorTools<EvalT,Traits>::getDoubleValue( sourceEvecInds[1] );
01053
01054
01055
01056 ret.coulombPrefactor = 1.0/pow(X0,(int)numDims);
01057 }
01058
01059 return ret;
01060 }
01061
01062
01063
01064 template<typename EvalT, typename Traits>
01065 void QCAD::PoissonSource<EvalT, Traits>::
01066 source_semiclassical(const typename Traits::EvalData workset, std::size_t cell, std::size_t qp, const ScalarT& scaleFactor,
01067 const PoissonSourceSetupInfo& setup_info)
01068 {
01069
01070 const ScalarT& unscaled_phi = potential(cell,qp);
01071 ScalarT phi = unscaled_phi / setup_info.V0;
01072
01073
01074 ScalarT fixedCharge;
01075 if (setup_info.fixedChargeType == "Donor")
01076 fixedCharge = (this->*(setup_info.ionDopant))("Donor",phi + setup_info.inArg)*setup_info.dopingConc;
01077 else if (setup_info.fixedChargeType == "Acceptor")
01078 fixedCharge = (this->*(setup_info.ionDopant))("Acceptor",-phi + setup_info.inArg)*setup_info.dopingConc;
01079 else if (setup_info.fixedChargeType == "Constant")
01080 fixedCharge = setup_info.fixedChargeConc;
01081 else
01082 fixedCharge = 0.0;
01083
01084
01085 ScalarT charge, eDensity, hDensity;
01086 eDensity = setup_info.Nc*(this->*(setup_info.carrStat))(phi + setup_info.eArgOffset + setup_info.fermiE/setup_info.kbT);
01087 hDensity = setup_info.Nv*(this->*(setup_info.carrStat))(-phi + setup_info.hArgOffset - setup_info.fermiE/setup_info.kbT);
01088
01089
01090 if(setup_info.prevDensityFactor > 1e-8) {
01091 ScalarT prevDensity = setup_info.prevDensityArray(cell,qp);
01092
01093 }
01094
01095 charge = 1.0/setup_info.Lambda2 * (hDensity - eDensity + fixedCharge);
01096 poissonSource(cell, qp) = scaleFactor*charge;
01097
01098
01099
01100
01101
01102
01103
01104
01105
01106
01107
01108 chargeDensity(cell, qp) = hDensity - eDensity + fixedCharge;
01109 electronDensity(cell, qp) = eDensity;
01110 holeDensity(cell, qp) = hDensity;
01111 electricPotential(cell, qp) = phi*setup_info.V0 - setup_info.qPhiRef;
01112 ionizedDopant(cell, qp) = fixedCharge;
01113 conductionBand(cell, qp) = setup_info.qPhiRef-setup_info.Chi-phi*setup_info.V0;
01114 valenceBand(cell, qp) = conductionBand(cell,qp)-setup_info.Eg;
01115 approxQuanEDen(cell,qp) = 0.0;
01116 artCBDensity(cell, qp) = ( eDensity > 1e-6 ? eDensity
01117 : -setup_info.Nc*(this->*(setup_info.carrStat))( -(phi+setup_info.eArgOffset) ));
01118 }
01119
01120
01121
01122 template<typename EvalT, typename Traits>
01123 void QCAD::PoissonSource<EvalT, Traits>::
01124 source_none(const typename Traits::EvalData workset, std::size_t cell, std::size_t qp, const ScalarT& scaleFactor,
01125 const PoissonSourceSetupInfo& setup_info)
01126 {
01127 const ScalarT& unscaled_phi = potential(cell,qp);
01128 ScalarT phi = unscaled_phi / setup_info.V0;
01129
01130
01131 ScalarT charge = 0.0;
01132 poissonSource(cell, qp) = scaleFactor*charge;
01133
01134 chargeDensity(cell, qp) = 0.0;
01135 electronDensity(cell, qp) = 0.0;
01136 holeDensity(cell, qp) = 0.0;
01137 electricPotential(cell, qp) = phi*setup_info.V0 - setup_info.qPhiRef;
01138 ionizedDopant(cell, qp) = 0.0;
01139 conductionBand(cell, qp) = setup_info.qPhiRef-setup_info.Chi-phi*setup_info.V0;
01140 valenceBand(cell, qp) = conductionBand(cell,qp)-setup_info.Eg;
01141 approxQuanEDen(cell,qp) = 0.0;
01142 artCBDensity(cell, qp) = 0.0;
01143 }
01144
01145
01146
01147 template<typename EvalT, typename Traits>
01148 void QCAD::PoissonSource<EvalT, Traits>::
01149 source_quantum(const typename Traits::EvalData workset, std::size_t cell, std::size_t qp, const ScalarT& scaleFactor,
01150 const PoissonSourceSetupInfo& setup_info)
01151 {
01152
01153 ScalarT approxEDensity = 0.0;
01154
01155 if(bUsePredictorCorrector) {
01156
01157 ScalarT prevPhi = setup_info.prevPhiArray(cell,qp);
01158 approxEDensity = (this->*(setup_info.quantum_edensity_fn))(workset, cell, qp, prevPhi, true, setup_info.fermiE, fixedQuantumOcc);
01159 }
01160 else
01161 approxEDensity = (this->*(setup_info.quantum_edensity_fn))(workset, cell, qp, 0.0, false, setup_info.fermiE, fixedQuantumOcc);
01162
01163
01164 ScalarT eDensity = (this->*(setup_info.quantum_edensity_fn))(workset, cell, qp, 0.0, false, setup_info.fermiE, fixedQuantumOcc);
01165
01166
01167 if(setup_info.prevDensityFactor > 1e-8) {
01168 ScalarT prevDensity = setup_info.prevDensityArray(cell,qp);
01169 approxEDensity = approxEDensity * (1.0 - setup_info.prevDensityFactor) + setup_info.prevDensityFactor * prevDensity;
01170 eDensity = eDensity * (1.0 - setup_info.prevDensityFactor) + setup_info.prevDensityFactor * prevDensity;
01171 }
01172
01173
01174 const ScalarT& unscaled_phi = potential(cell,qp);
01175 ScalarT phi = unscaled_phi / setup_info.V0;
01176
01177
01178 ScalarT hDensity = setup_info.Nv*(this->*(setup_info.carrStat))(-phi + setup_info.hArgOffset);
01179
01180
01181 ScalarT fixedCharge = 0.0;
01182 if (setup_info.fixedChargeType == "Donor")
01183 fixedCharge = (this->*(setup_info.ionDopant))("Donor",phi + setup_info.inArg)*setup_info.dopingConc;
01184 else if (setup_info.fixedChargeType == "Acceptor")
01185 fixedCharge = (this->*(setup_info.ionDopant))("Acceptor",-phi + setup_info.inArg)*setup_info.dopingConc;
01186 else if (setup_info.fixedChargeType == "Constant")
01187 fixedCharge = setup_info.fixedChargeConc;
01188 else
01189 fixedCharge = 0.0;
01190
01191
01192 ScalarT charge;
01193 charge = 1.0/setup_info.Lambda2*(hDensity- approxEDensity + fixedCharge);
01194 poissonSource(cell, qp) = scaleFactor*charge;
01195
01196
01197
01198
01199
01200
01201
01202
01203
01204 chargeDensity(cell, qp) = hDensity -eDensity +fixedCharge;
01205 electronDensity(cell, qp) = eDensity;
01206 holeDensity(cell, qp) = hDensity;
01207
01208 ionizedDopant(cell, qp) = fixedCharge;
01209 approxQuanEDen(cell,qp) = approxEDensity;
01210 artCBDensity(cell, qp) = ( eDensity > 1e-6 ? eDensity : -setup_info.Nc*(this->*(setup_info.carrStat))( -(phi+setup_info.eArgOffset) ));
01211
01212 if (bIncludeVxc)
01213 {
01214 ScalarT Vxc = computeVxcLDA(setup_info.relPerm, setup_info.averagedEffMass, approxEDensity) / energy_unit_in_eV;
01215 conductionBand(cell, qp) = setup_info.qPhiRef -setup_info.Chi -phi*setup_info.V0 +Vxc;
01216
01217
01218 electricPotential(cell, qp) = phi*setup_info.V0 + Vxc- setup_info.qPhiRef;
01219 }
01220 else
01221 {
01222 conductionBand(cell, qp) = setup_info.qPhiRef -setup_info.Chi -phi*setup_info.V0;
01223 electricPotential(cell, qp) = phi*setup_info.V0 - setup_info.qPhiRef;
01224 }
01225 valenceBand(cell, qp) = conductionBand(cell,qp)-setup_info.Eg;
01226 }
01227
01228
01229
01230 template<typename EvalT, typename Traits>
01231 void QCAD::PoissonSource<EvalT, Traits>::
01232 source_coulomb(const typename Traits::EvalData workset, std::size_t cell, std::size_t qp, const ScalarT& scaleFactor,
01233 const PoissonSourceSetupInfo& setup_info)
01234 {
01235
01236 const ScalarT& unscaled_phi = potential(cell,qp);
01237 ScalarT phi = unscaled_phi / setup_info.V0;
01238
01239
01240
01241
01242 ScalarT charge;
01243 int i = setup_info.sourceEvec1;
01244 int j = setup_info.sourceEvec2;
01245 if(imagPartOfCoulombSrc) {
01246 if(!bRealEigenvectors)
01247 charge = - setup_info.coulombPrefactor * ( eigenvector_Re[i](cell,qp) * eigenvector_Im[j](cell,qp) -
01248 eigenvector_Im[i](cell,qp) * eigenvector_Re[j](cell,qp));
01249 else charge = 0.0;
01250 }
01251 else {
01252 if(bRealEigenvectors)
01253 charge = - setup_info.coulombPrefactor * ( eigenvector_Re[i](cell,qp) * eigenvector_Re[j](cell,qp) );
01254 else
01255 charge = - setup_info.coulombPrefactor * ( eigenvector_Re[i](cell,qp) * eigenvector_Re[j](cell,qp) +
01256 eigenvector_Im[i](cell,qp) * eigenvector_Im[j](cell,qp));
01257 }
01258
01259
01260 poissonSource(cell, qp) = scaleFactor * 1.0/setup_info.Lambda2 * charge;
01261
01262 chargeDensity(cell, qp) = charge;
01263 electronDensity(cell, qp) = charge;
01264 holeDensity(cell, qp) = 0.0;
01265 electricPotential(cell, qp) = phi*setup_info.V0 - setup_info.qPhiRef;
01266
01267
01268 conductionBand(cell, qp) = setup_info.qPhiRef -setup_info.Chi -phi*setup_info.V0;
01269 valenceBand(cell, qp) = conductionBand(cell,qp)-setup_info.Eg;
01270 }
01271
01272
01273 template<typename EvalT, typename Traits>
01274 void QCAD::PoissonSource<EvalT, Traits>::
01275 source_testcoulomb(const typename Traits::EvalData workset, std::size_t cell, std::size_t qp, const ScalarT& scaleFactor,
01276 const PoissonSourceSetupInfo& setup_info)
01277 {
01278
01279 const ScalarT& unscaled_phi = potential(cell,qp);
01280 ScalarT phi = unscaled_phi / setup_info.V0;
01281 MeshScalarT* coord = &coordVec(cell,qp,0);
01282
01283
01284 ScalarT charge = setup_info.coulombPrefactor * ( exp(-(coord[0]*coord[0] + coord[1]*coord[1] + coord[2]*coord[2])));
01285
01286 poissonSource(cell, qp) = scaleFactor * 1.0/setup_info.Lambda2 * charge;
01287
01288 chargeDensity(cell, qp) = charge;
01289 electronDensity(cell, qp) = charge;
01290 holeDensity(cell, qp) = 0.0;
01291 electricPotential(cell, qp) = phi*setup_info.V0 - setup_info.qPhiRef;
01292
01293
01294 conductionBand(cell, qp) = setup_info.qPhiRef-setup_info.Chi-phi*setup_info.V0;
01295 valenceBand(cell, qp) = conductionBand(cell,qp)-setup_info.Eg;
01296 }
01297
01298
01299
01300
01302
01303
01304
01305 template<typename EvalT,typename Traits>
01306 inline typename QCAD::PoissonSource<EvalT,Traits>::ScalarT
01307 QCAD::PoissonSource<EvalT,Traits>::computeMBStat(const ScalarT x)
01308 {
01309 return exp(x);
01310 }
01311
01312
01313
01314 template<typename EvalT,typename Traits>
01315 inline typename QCAD::PoissonSource<EvalT,Traits>::ScalarT
01316 QCAD::PoissonSource<EvalT,Traits>::computeFDIntOneHalf(const ScalarT x)
01317 {
01318
01319
01320
01321
01322
01323 ScalarT fdInt;
01324 if (x >= -50.0)
01325 {
01326 fdInt = pow(x,4.) + 50. + 33.6*x*(1.-0.68*exp(-0.17*pow((x+1.),2.0)));
01327 fdInt = pow((exp(-x) + (3./4.*sqrt(pi)) * pow(fdInt, -3./8.)),-1.0);
01328 }
01329 else
01330 fdInt = exp(x);
01331
01332 return fdInt;
01333 }
01334
01335
01336
01337 template<typename EvalT,typename Traits>
01338 inline typename QCAD::PoissonSource<EvalT,Traits>::ScalarT
01339 QCAD::PoissonSource<EvalT,Traits>::computeZeroKFDInt(const ScalarT x)
01340 {
01341 ScalarT zeroKFDInt;
01342 if (x > 0.0)
01343 zeroKFDInt = 4./3./sqrt(pi)*pow(x, 3./2.);
01344 else
01345 zeroKFDInt = 0.0;
01346
01347 return zeroKFDInt;
01348 }
01349
01350
01351 template<typename EvalT,typename Traits>
01352 inline typename QCAD::PoissonSource<EvalT,Traits>::ScalarT
01353 QCAD::PoissonSource<EvalT,Traits>::computeZeroStat(const ScalarT x)
01354 {
01355 return 0;
01356 }
01357
01358
01359
01360
01362
01363
01364
01365 template<typename EvalT,typename Traits>
01366 inline typename QCAD::PoissonSource<EvalT,Traits>::ScalarT
01367 QCAD::PoissonSource<EvalT,Traits>::fullDopants(const std::string dopType, const ScalarT &x)
01368 {
01369 ScalarT ionDopants;
01370
01371
01372 if (dopType == "Donor")
01373 ionDopants = 1.0;
01374 else if (dopType == "Acceptor")
01375 ionDopants = -1.0;
01376 else if (dopType == "None")
01377 ionDopants = 0.0;
01378 else
01379 {
01380 ionDopants = 0.0;
01381 TEUCHOS_TEST_FOR_EXCEPTION (true, Teuchos::Exceptions::InvalidParameter,
01382 std::endl << "Error! Unknown dopant type " << dopType << "!"<< std::endl);
01383 }
01384
01385 return ionDopants;
01386 }
01387
01388
01389
01390 template<typename EvalT,typename Traits>
01391 typename QCAD::PoissonSource<EvalT,Traits>::ScalarT
01392 QCAD::PoissonSource<EvalT,Traits>::ionizedDopants(const std::string dopType, const ScalarT &x)
01393 {
01394 ScalarT ionDopants;
01395
01396 if (dopType == "Donor")
01397 {
01398 if (x > MAX_EXPONENT)
01399 ionDopants = 0.5 * exp(-x);
01400 else
01401 ionDopants = 1.0 / (1. + 2.*exp(x));
01402 }
01403
01404 else if (dopType == "Acceptor")
01405 {
01406 if (x > MAX_EXPONENT)
01407 ionDopants = -0.25 * exp(-x);
01408 else
01409 ionDopants = -1.0 / (1. + 4.*exp(x));
01410 }
01411
01412 else if (dopType == "None")
01413 ionDopants = 0.0;
01414 else
01415 {
01416 ionDopants = 0.0;
01417 TEUCHOS_TEST_FOR_EXCEPTION (true, Teuchos::Exceptions::InvalidParameter,
01418 std::endl << "Error! Unknown dopant type " << dopType << "!"<< std::endl);
01419 }
01420
01421 return ionDopants;
01422 }
01423
01424
01425
01426
01428
01429
01430
01431 template<typename EvalT, typename Traits>
01432 typename QCAD::PoissonSource<EvalT,Traits>::ScalarT
01433 QCAD::PoissonSource<EvalT,Traits>::eDensityForPoissonSchrodinger
01434 (typename Traits::EvalData workset, std::size_t cell, std::size_t qp,
01435 const ScalarT prevPhi, const bool bUsePredCorr, const double Ef, const double fixedOcc)
01436 {
01437
01438
01439
01440
01441
01442
01443
01444
01445 double eVPerJ = 1.0/eleQ;
01446 double cm2Perm2 = 1.0e4;
01447
01448
01449 int valleyDegeneracyFactor = materialDB->getElementBlockParam<int>(workset.EBName,"Number of conduction band min",2);
01450
01451
01452 double X0 = length_unit_in_m/1e-2;
01453
01454 ScalarT temperature = temperatureField(0);
01455 ScalarT kbT_eV = kbBoltz*temperature;
01456 ScalarT kbT = kbBoltz*temperature / energy_unit_in_eV;
01457 ScalarT eDensity = 0.0;
01458
01459
01460 const std::vector<double>& neg_eigenvals = *(workset.eigenDataPtr->eigenvalueRe);
01461 std::vector<double> eigenvals( neg_eigenvals );
01462 for(unsigned int i=0; i<eigenvals.size(); ++i) eigenvals[i] *= -1;
01463
01464
01465 ScalarT deltaPhi = 0.0;
01466 if (bUsePredCorr)
01467 {
01468 const ScalarT& phi = potential(cell,qp);
01469 deltaPhi = (phi - prevPhi) / (kbT /1.0);
01470 }
01471 else
01472 deltaPhi = 0.0;
01473
01474 ScalarT eDenPrefactor;
01475 std::vector<ScalarT> occ( eigenvals.size(), 0.0);
01476
01477
01478
01479 switch (numDims)
01480 {
01481 case 1:
01482 {
01483
01484
01485
01486 double m11 = materialDB->getElementBlockParam<double>(workset.EBName,"Transverse Electron Effective Mass");
01487
01488
01489
01490 double dos2D = m11*m0/(pi*hbar*hbar*eVPerJ*cm2Perm2);
01491
01492
01493
01494 eDenPrefactor = valleyDegeneracyFactor*dos2D*kbT_eV/X0;
01495
01496
01497 for(int i = 0; i < nEigenvectors; i++)
01498 {
01499 ScalarT tmpArg = (Ef-eigenvals[i])/kbT + deltaPhi;
01500 ScalarT logFunc;
01501 if (tmpArg > MAX_EXPONENT)
01502 logFunc = tmpArg;
01503 else
01504 logFunc = log(1.0 + exp(tmpArg));
01505 occ[i] = logFunc;
01506 }
01507 break;
01508 }
01509
01510
01511 case 2:
01512 {
01513
01514
01515 double mUnconfined = materialDB->getElementBlockParam<double>(workset.EBName,"Transverse Electron Effective Mass");
01516
01517
01518
01519 ScalarT n1D = sqrt(2.0*mUnconfined*m0*kbT_eV/(pi*hbar*hbar*eVPerJ*cm2Perm2));
01520
01521
01522
01523 eDenPrefactor = valleyDegeneracyFactor*n1D/pow(X0,2.);
01524
01525
01526 for(int i=0; i < nEigenvectors; i++)
01527 {
01528 ScalarT inArg = (Ef-eigenvals[i])/kbT + deltaPhi;
01529 occ[i] = computeFDIntMinusOneHalf(inArg);
01530 }
01531 break;
01532 }
01533
01534
01535 case 3:
01536 {
01537
01538 int spinDegeneracyFactor = 2;
01539 double degeneracyFactor = spinDegeneracyFactor * valleyDegeneracyFactor;
01540
01541
01542
01543 eDenPrefactor = degeneracyFactor/pow(X0,3.);
01544
01545
01546 for(int i = 0; i < nEigenvectors; i++)
01547 {
01548
01549
01550
01551
01552 ScalarT tmpArg = (eigenvals[i]-Ef)/kbT - deltaPhi;
01553 ScalarT fermiFactor;
01554
01555 if (tmpArg > MAX_EXPONENT)
01556 fermiFactor = exp(-tmpArg);
01557 else
01558 fermiFactor = 1.0/( exp(tmpArg) + 1.0 );
01559 occ[i] = fermiFactor;
01560 }
01561 break;
01562 }
01563
01564 default:
01565 TEUCHOS_TEST_FOR_EXCEPTION (true, Teuchos::Exceptions::InvalidParameter,
01566 std::endl << "Error! Invalid number of dimensions " << numDims << "!"<< std::endl);
01567 eDenPrefactor = 0.0;
01568 break;
01569
01570 }
01571
01572
01573 if(fixedOcc > -1e-6) {
01574 double occLeft = fixedOcc;
01575 for(int i = 0; i < nEigenvectors; i++) {
01576 occ[i] = std::min(1.0, occLeft);
01577 occLeft -= QCAD::EvaluatorTools<EvalT,Traits>::getDoubleValue(occ[i]);
01578 }
01579 }
01580
01581
01582 for(int i = 0; i < nEigenvectors; i++)
01583 {
01584
01585 ScalarT wfSquared;
01586 if(bRealEigenvectors)
01587 wfSquared = ( eigenvector_Re[i](cell,qp)*eigenvector_Re[i](cell,qp) );
01588 else
01589 wfSquared = ( eigenvector_Re[i](cell,qp)*eigenvector_Re[i](cell,qp) +
01590 eigenvector_Im[i](cell,qp)*eigenvector_Im[i](cell,qp) );
01591 eDensity += wfSquared*occ[i];
01592 }
01593 eDensity = eDenPrefactor*eDensity;
01594
01595 return eDensity;
01596 }
01597
01598
01599
01600
01601 template<typename EvalT, typename Traits>
01602 typename QCAD::PoissonSource<EvalT,Traits>::ScalarT
01603 QCAD::PoissonSource<EvalT,Traits>::eDensityForPoissonCI
01604 (typename Traits::EvalData workset, std::size_t cell, std::size_t qp,
01605 const ScalarT prevPhi, const bool bUsePredCorr, const double Ef, const double fixedOcc)
01606 {
01607
01608
01609
01610
01611
01612
01613
01614
01615
01616
01617
01618
01619 int valleyDegeneracyFactor = materialDB->getElementBlockParam<int>(workset.EBName,"Number of conduction band min",2);
01620
01621
01622 double X0 = length_unit_in_m/1e-2;
01623
01624 ScalarT temperature = temperatureField(0);
01625 ScalarT kbT = kbBoltz*temperature / energy_unit_in_eV;
01626 ScalarT eDensity = 0.0;
01627
01628
01629 const std::vector<double>& neg_eigenvals = *(workset.eigenDataPtr->eigenvalueRe);
01630 std::vector<double> eigenvals( neg_eigenvals );
01631 int nCIEvals = eigenvals.size();
01632 int nEvals = std::min(nCIEvals, nEigenvectors);
01633
01634
01635
01636
01637
01638
01639 ScalarT eDenPrefactor;
01640 std::vector<ScalarT> occ( nEvals, 0.0);
01641
01642
01643 switch (numDims)
01644 {
01645
01646
01647 case 2:
01648 {
01649
01650
01651
01652
01653
01654
01655
01656
01657
01658
01659 ScalarT n1D = 1.0;
01660
01661
01662
01663
01664 eDenPrefactor = valleyDegeneracyFactor*n1D/pow(X0,2.) / energy_unit_in_eV;
01665
01666
01667 ScalarT Z = 0.0;
01668 for(int i=0; i < nEvals; i++) Z += exp(-eigenvals[i]/kbT);
01669
01670 for(int i=0; i < nEvals; i++)
01671 {
01672 ScalarT wfOcc = exp(-eigenvals[i]/kbT) / Z;
01673 occ[i] = wfOcc;
01674 }
01675 break;
01676 }
01677
01678
01679 case 3:
01680 {
01681
01682 double degeneracyFactor = valleyDegeneracyFactor;
01683
01684
01685
01686 eDenPrefactor = degeneracyFactor/pow(X0,3.);
01687
01688
01689
01690
01691
01692 for(int i = 0; i < nEvals; i++)
01693 {
01694 ScalarT oneOverOcc = 0.0;
01695 for(int j=0; j < nEvals; j++) oneOverOcc += exp(-(eigenvals[j]-eigenvals[i])/kbT);
01696
01697 ScalarT wfOcc = 0.0;
01698 if(!std::isinf(QCAD::EvaluatorTools<EvalT,Traits>::getDoubleValue(oneOverOcc)))
01699 wfOcc = 1/oneOverOcc;
01700 occ[i] = wfOcc;
01701 }
01702 break;
01703 }
01704
01705 default:
01706 TEUCHOS_TEST_FOR_EXCEPTION (true, Teuchos::Exceptions::InvalidParameter,
01707 std::endl << "Error! Invalid number of dimensions " << numDims << "!"<< std::endl);
01708 break;
01709
01710 }
01711
01712
01713 if(fixedOcc > -1e-6) {
01714 double occLeft = fixedOcc;
01715 for(int i = 0; i < nEigenvectors; i++) {
01716 occ[i] = std::min(1.0, occLeft);
01717 occLeft -= QCAD::EvaluatorTools<EvalT,Traits>::getDoubleValue(occ[i]);
01718 }
01719 }
01720
01721
01722 for(int i = 0; i < nEvals; i++)
01723 {
01724 ScalarT wfSquared = ( eigenvector_Re[i](cell,qp) );
01725 eDensity += wfSquared * occ[i];
01726 }
01727 eDensity = eDenPrefactor*eDensity;
01728
01729 return eDensity;
01730 }
01731
01732
01733
01734
01735
01737
01738 template<typename EvalT, typename Traits>
01739 void QCAD::PoissonSource<EvalT,Traits>::source_pointcharges(typename Traits::EvalData workset)
01740 {
01741
01742 double X0 = length_unit_in_m/1e-2;
01743 ScalarT Lambda2 = eps0/(eleQ*X0*X0);
01744
01746 if(numWorksetsScannedForPtCharges <= workset.wsIndex) {
01747 TEUCHOS_TEST_FOR_EXCEPTION ( !(numDims == 2 || ((numNodes == 4 || numNodes == 8) && numDims == 3)), Teuchos::Exceptions::InvalidParameter,
01748 std::endl << "Error! Point charges are only supported for TET4 and HEX8 meshes in 3D currently." << std::endl);
01749
01751 bool (QCAD::PoissonSource<EvalT,Traits>::*point_test_fn) (const MeshScalarT*, const MeshScalarT*, int);
01752 if(numDims == 3 && numNodes == 4) point_test_fn = &QCAD::PoissonSource<EvalT,Traits>::pointIsInTetrahedron;
01753 else if(numDims == 3 && numNodes == 8) point_test_fn = &QCAD::PoissonSource<EvalT,Traits>::pointIsInHexahedron;
01754 else if(numDims == 2) point_test_fn = &QCAD::PoissonSource<EvalT,Traits>::pointIsInPolygon;
01755 else point_test_fn = NULL;
01756
01757
01758
01759 MeshScalarT* cellVertices = new MeshScalarT[numNodes*numDims];
01760 for (std::size_t cell=0; cell < workset.numCells; ++cell) {
01761 for( std::size_t node=0; node<numNodes; ++node ) {
01762 for( std::size_t k=0; k<numDims; ++k )
01763 cellVertices[node*numDims+k] = coordVecAtVertices(cell,node,k);
01764 }
01765
01766 for( std::size_t i=0; i < pointCharges.size(); ++i) {
01767 if( (this->*point_test_fn)(cellVertices, pointCharges[i].position, numNodes) ) {
01768 std::cout << "DEBUG: FOUND POINT CHARGE in ws " << workset.wsIndex << ", cell " << cell << std::endl;
01769
01770 for(std::size_t v=0; v<numNodes; v++) {
01771 std::cout << " ( ";
01772 for(std::size_t d=0; d<numDims; d++) std::cout << cellVertices[v*numDims] << " ";
01773 std::cout << ")" << std::endl;
01774 }
01775
01776 pointCharges[i].iWorkset = workset.wsIndex;
01777 pointCharges[i].iCell = cell;
01778 }
01779 }
01780 }
01781 delete [] cellVertices;
01782
01783 assert(workset.wsIndex == numWorksetsScannedForPtCharges);
01784 numWorksetsScannedForPtCharges++;
01785 }
01786
01788 for( std::size_t i=0; i < pointCharges.size(); ++i) {
01789 if( pointCharges[i].iWorkset != (int)workset.wsIndex ) continue;
01790
01791 MeshScalarT cellVol = 0.0, qpChargeDen;
01792 std::size_t cell = pointCharges[i].iCell;
01793 for (std::size_t qp=0; qp < numQPs; ++qp)
01794 cellVol += weights(cell,qp);
01795
01796 qpChargeDen = pointCharges[i].charge / (cellVol*pow(X0,3));
01797
01798
01799 for (std::size_t qp=0; qp < numQPs; ++qp) {
01800 ScalarT scaleFactor = 1.0;
01801 poissonSource(cell, qp) += 1.0/Lambda2 * scaleFactor * qpChargeDen;
01802 chargeDensity(cell, qp) += qpChargeDen;
01803 }
01804 }
01805 }
01806
01807
01808
01809
01810 template<typename EvalT, typename Traits>
01811 bool QCAD::PoissonSource<EvalT,Traits>::
01812 pointIsInTetrahedron(const MeshScalarT* cellVertices, const MeshScalarT* position, int nVertices)
01813 {
01814
01815
01816
01817
01818 MeshScalarT v1[4], v2[4], v3[4], v4[4], p[4];
01819 for(int i=0; i<3; i++) {
01820 v1[i] = cellVertices[i];
01821 v2[i] = cellVertices[3+i];
01822 v3[i] = cellVertices[6+i];
01823 v4[i] = cellVertices[9+i];
01824 p[i] = position[i];
01825 }
01826 v1[3] = v2[3] = v3[3] = v4[3] = p[3] = 1;
01827
01828 const MeshScalarT *mx[4], *refMx[4];
01829 MeshScalarT refDet, det;
01830
01831 refMx[0] = mx[0] = v1; refMx[1] = mx[1] = v2;
01832 refMx[2] = mx[2] = v3; refMx[3] = mx[3] = v4;
01833 refDet = determinant(refMx, 4);
01834
01835 for(int i=0; i < 4; i++) {
01836 mx[i] = p; det = determinant(mx, 4); mx[i] = refMx[i];
01837 if( (det < 0 && refDet > 0) || (det > 0 && refDet < 0) )
01838 return false;
01839 }
01840
01841 return true;
01842 }
01843
01844
01845
01846
01847
01848 template<typename EvalT, typename Traits>
01849 bool QCAD::PoissonSource<EvalT,Traits>::
01850 pointIsInHexahedron(const MeshScalarT* cellVertices, const MeshScalarT* position, int nVertices)
01851 {
01852
01853
01854
01855
01856
01857
01858
01859
01860
01861
01862
01863
01864
01865
01866
01867
01868
01869
01870
01871
01872
01873
01874
01875
01876
01877
01878
01879
01880
01881
01882
01883
01884
01885
01886
01887
01888
01889
01890 const MeshScalarT *v0 = cellVertices;
01891 const MeshScalarT *v1 = cellVertices + 3;
01892 const MeshScalarT *v2 = cellVertices + 6;
01893 const MeshScalarT *v3 = cellVertices + 9;
01894 const MeshScalarT *v4 = cellVertices + 12;
01895 const MeshScalarT *v5 = cellVertices + 15;
01896 const MeshScalarT *v6 = cellVertices + 18;
01897 const MeshScalarT *v7 = cellVertices + 21;
01898
01899 if(!sameSideOfPlane( v0, v1, v2, v4, position)) return false;
01900 if(!sameSideOfPlane( v4, v5, v6, v0, position)) return false;
01901 if(!sameSideOfPlane( v0, v1, v5, v2, position)) return false;
01902 if(!sameSideOfPlane( v2, v3, v7, v0, position)) return false;
01903 if(!sameSideOfPlane( v0, v3, v7, v2, position)) return false;
01904 if(!sameSideOfPlane( v1, v2, v6, v0, position)) return false;
01905
01906 return true;
01907 }
01908
01909
01910
01911 template<typename EvalT, typename Traits>
01912 bool QCAD::PoissonSource<EvalT,Traits>::
01913 pointIsInPolygon(const MeshScalarT* cellVertices, const MeshScalarT* position, int nVertices)
01914 {
01915
01916
01917
01918
01919
01920 bool c = false;
01921 int n = nVertices;
01922 MeshScalarT x=position[0], y=position[1];
01923 const int X=0,Y=1;
01924
01925 for (int i = 0, j = n-1; i < n; j = i++) {
01926 const MeshScalarT* pi = &cellVertices[2*i];
01927 const MeshScalarT* pj = &cellVertices[2*j];
01928 if ((((pi[Y] <= y) && (y < pj[Y])) ||
01929 ((pj[Y] <= y) && (y < pi[Y]))) &&
01930 (x < (pj[X] - pi[X]) * (y - pi[Y]) / (pj[Y] - pi[Y]) + pi[X]))
01931 c = !c;
01932 }
01933 return c;
01934 }
01935
01936
01937
01938 template<typename EvalT, typename Traits>
01939 typename QCAD::PoissonSource<EvalT,Traits>::MeshScalarT
01940 QCAD::PoissonSource<EvalT,Traits>::
01941 determinant(const MeshScalarT** mx, int N)
01942 {
01943
01944
01945 MeshScalarT det = 0, term;
01946
01947
01948 int t, sign = 0;
01949 int* inds = new int[N];
01950 for(int i=0; i<N; i++) inds[i] = i;
01951
01952 while(true) {
01953
01954 term = 1;
01955 for(int i=0; i<N; i++)
01956 term = term * mx[i][inds[i]];
01957 det += ((sign % 2) ? -1 : 1) * term;
01958
01959 int i = N - 1;
01960 while (i > 0 && inds[i-1] >= inds[i])
01961 i = i-1;
01962
01963 if(i == 0) break;
01964
01965 int j = N;
01966 while (inds[j-1] <= inds[i-1])
01967 j = j-1;
01968
01969 t = inds[i-1]; inds[i-1] = inds[j-1]; inds[j-1] = t;
01970 sign++;
01971
01972 i++; j = N;
01973 while (i < j) {
01974 t = inds[i-1]; inds[i-1] = inds[j-1]; inds[j-1] = t;
01975 sign++;
01976 i++;
01977 j--;
01978 }
01979 }
01980
01981 delete [] inds;
01982 return det;
01983 }
01984
01985
01986
01987 template<typename EvalT, typename Traits>
01988 bool QCAD::PoissonSource<EvalT,Traits>::
01989 sameSideOfPlane(const MeshScalarT* plane0, const MeshScalarT* plane1, const MeshScalarT* plane2,
01990 const MeshScalarT* ptA, const MeshScalarT* ptB)
01991 {
01992
01993 MeshScalarT detA, detB;
01994
01995 const MeshScalarT *mx[3];
01996 MeshScalarT row0[3], row1[3], row2[3]; mx[0] = row0; mx[1] = row1; mx[2] = row2;
01997
01998 for(int i=0; i<3; i++) {
01999 row0[i] = ptA[i] - plane0[i];
02000 row1[i] = plane1[i] - plane0[i];
02001 row2[i] = plane2[i] - plane0[i];
02002 }
02003 detA = determinant(mx, 3);
02004
02005 for(int i=0; i<3; i++)
02006 row0[i] = ptB[i] - plane0[i];
02007
02008 detB = determinant(mx, 3);
02009
02010 if( (detA < 0 && detB > 0) || (detA > 0 && detB < 0) )
02011 return false;
02012 return true;
02013 }
02014
02015
02016
02017
02019
02020
02021
02022 template<typename EvalT,typename Traits>
02023 inline typename QCAD::PoissonSource<EvalT,Traits>::ScalarT
02024 QCAD::PoissonSource<EvalT,Traits>::computeFDIntMinusOneHalf(const ScalarT x)
02025 {
02026
02027
02028
02029
02030
02031
02032 ScalarT fdInt;
02033 double a1, a2, a3, a4, a5, a6, a7;
02034 if (x <= 0.)
02035 {
02036 a1 = 0.999909;
02037 a2 = 0.706781;
02038 a3 = 0.572752;
02039 a4 = 0.466318;
02040 a5 = 0.324511;
02041 a6 = 0.152889;
02042 a7 = 0.033673;
02043 fdInt = a1*exp(x)-a2*exp(2.*x)+a3*exp(3.*x)-a4*exp(4.*x)+a5*exp(5.*x)-a6*exp(6.*x)+a7*exp(7.*x);
02044 }
02045 else if (x >= 5.)
02046 {
02047 a1 = 1.12837;
02048 a2 = -0.470698;
02049 a3 = -0.453108;
02050 a4 = -228.975;
02051 a5 = 8303.50;
02052 a6 = -118124;
02053 a7 = 632895;
02054 fdInt = sqrt(x)*(a1+ a2/pow(x,2.)+ a3/pow(x,4.)+ a4/pow(x,6.)+ a5/pow(x,8.)+ a6/pow(x,10.)+ a7/pow(x,12.));
02055 }
02056 else if ((x > 0.) && (x <= 2.5))
02057 {
02058 double a8, a9;
02059 a1 = 0.604856;
02060 a2 = 0.380080;
02061 a3 = 0.059320;
02062 a4 = -0.014526;
02063 a5 = -0.004222;
02064 a6 = 0.001335;
02065 a7 = 0.000291;
02066 a8 = -0.000159;
02067 a9 = 0.000018;
02068 fdInt = a1+ a2*x+ a3*pow(x,2.)+ a4*pow(x,3.)+ a5*pow(x,4.)+ a6*pow(x,5.)+ a7*pow(x,6.)+ a8*pow(x,7.)+ a9*pow(x,8.);
02069 }
02070 else
02071 {
02072 double a8;
02073 a1 = 0.638086;
02074 a2 = 0.292266;
02075 a3 = 0.159486;
02076 a4 = -0.077691;
02077 a5 = 0.018650;
02078 a6 = -0.002736;
02079 a7 = 0.000249;
02080 a8 = -0.000013;
02081 fdInt = a1+ a2*x+ a3*pow(x,2.)+ a4*pow(x,3.)+ a5*pow(x,4.)+ a6*pow(x,5.)+ a7*pow(x,6.)+ a8*pow(x,7.);
02082 }
02083
02084 return fdInt;
02085 }
02086
02087
02088
02089 template<typename EvalT, typename Traits>
02090 typename QCAD::PoissonSource<EvalT,Traits>::ScalarT
02091 QCAD::PoissonSource<EvalT,Traits>::computeVxcLDA (const double & relPerm,
02092 const double & effMass, const ScalarT& eDensity)
02093 {
02094
02095
02096
02097
02098
02099
02100
02101
02102
02103
02104 ScalarT b = (eps0*100.0)*hbar*hbar / (m0*eleQ*eleQ) * 100.0;
02105 b = b * (4.0*pi*relPerm/effMass);
02106
02107 double eVPerJ = 1.0/eleQ;
02108 ScalarT Ry = eleQ*eleQ / (eps0*b) * eVPerJ / (8.*pi*relPerm);
02109
02110 double alpha = pow(4.0/9.0/pi, 1./3.);
02111 ScalarT Vxc;
02112
02113 if (eDensity <= 1.0)
02114 Vxc = 0.0;
02115 else
02116 {
02117 ScalarT rs = pow(4.0*pi*eDensity*pow(b,3.0)/3.0, -1./3.);
02118 ScalarT x = rs / 21.0;
02119 Vxc = -2.0/(pi*alpha*rs) * Ry * (1.0+ 0.7734*x*log(1.+1.0/x));
02120 }
02121
02122 return Vxc;
02123 }
02124
02125
02126 template<typename EvalT, typename Traits>
02127 typename QCAD::PoissonSource<EvalT,Traits>::ScalarT
02128 QCAD::PoissonSource<EvalT, Traits>::getCellScaleFactor(std::size_t cell, const std::vector<bool>& bEBInRegion, ScalarT init_factor)
02129 {
02130 ScalarT ret = init_factor;
02131 std::size_t nRegions = meshRegionList.size();
02132 if(nRegions > 0) {
02133 for(std::size_t i=0; i<nRegions; i++) {
02134 if(!bEBInRegion[i] && meshRegionList[i]->cellIsInRegion(cell)) {
02135 ret *= meshRegionFactors[i];
02136 }
02137 }
02138 }
02139 return ret;
02140 }
02141
02142
02143
02144 template<typename EvalT, typename Traits>
02145 typename QCAD::PoissonSource<EvalT,Traits>::ScalarT
02146 QCAD::PoissonSource<EvalT, Traits>::getReferencePotential(typename Traits::EvalData workset)
02147 {
02149 ScalarT qPhiRef;
02150
02151 std::string refMtrlName, category;
02152 refMtrlName = materialDB->getParam<std::string>("Reference Material");
02153 category = materialDB->getMaterialParam<std::string>(refMtrlName,"Category");
02154 if (category == "Semiconductor") {
02155
02156
02157
02158
02159 ScalarT temperature = temperatureField(0);
02160 double mdn = materialDB->getMaterialParam<double>(refMtrlName,"Electron DOS Effective Mass");
02161 double mdp = materialDB->getMaterialParam<double>(refMtrlName,"Hole DOS Effective Mass");
02162 double Chi = materialDB->getMaterialParam<double>(refMtrlName,"Electron Affinity") / energy_unit_in_eV;
02163 double Eg0 = materialDB->getMaterialParam<double>(refMtrlName,"Zero Temperature Band Gap") / energy_unit_in_eV;
02164 double alpha = materialDB->getMaterialParam<double>(refMtrlName,"Band Gap Alpha Coefficient");
02165 double beta = materialDB->getMaterialParam<double>(refMtrlName,"Band Gap Beta Coefficient");
02166 ScalarT Eg = Eg0-(alpha*pow(temperature,2.0)/(beta+temperature)) / energy_unit_in_eV;
02167
02168 ScalarT kbT = kbBoltz*temperature / energy_unit_in_eV;
02169 ScalarT Eic = -Eg/2. + 3./4.*kbT*log(mdp/mdn);
02170 qPhiRef = Chi - Eic;
02171 }
02172 else if (category == "Insulator") {
02173 double Chi = materialDB->getMaterialParam<double>(refMtrlName,"Electron Affinity");
02174 qPhiRef = Chi / energy_unit_in_eV;
02175 }
02176 else if (category == "Metal") {
02177 double workFn = materialDB->getMaterialParam<double>(refMtrlName,"Work Function");
02178 qPhiRef = workFn / energy_unit_in_eV;
02179 }
02180 else {
02181 TEUCHOS_TEST_FOR_EXCEPTION (true, Teuchos::Exceptions::InvalidParameter, std::endl
02182 << "Error! Invalid category " << category
02183 << " for reference material !" << std::endl);
02184 }
02185
02186 return qPhiRef;
02187 }
02188
02189
02190