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

QCAD_PoissonSource_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 <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   // Material database
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   // get values from the input .xml and use default values if not provided
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   // find element blocks and voltages applied on them
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   // look through DBCs and pull out any element block names we find (for setting the Fermi level below)
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     // retrieve the nodeset name
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     // obtain the element block associated with this nodeset (should be ohmic, NOT contact on insulator) 
00079     const std::string& ebName = materialDB->getNodeSetParam<std::string>(nsName,"elementBlock","");
00080     
00081     // map the element block to its applied voltage
00082     if (ebName.length() > 0)
00083     {
00084       double dbcValue = dbcPList.get<double>(dbcName); 
00085       mapDBCValue_eb[ebName] = dbcValue;
00086       mapDBCValue_ns[nsName] = dbcValue;
00087       //std::cout << "ebName = " << ebName << ", value = " << mapDBCValue_eb[ebName] << std::endl;  
00088     }
00089   }
00090 
00091   // specific values for "1D MOSCapacitor"
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   // passed down from main list
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   // Defaults
00134   prevDensityMixingFactor = -1.0; //Flag that factor is unset... - sometimes this factor isn't set correctly within Albany framework, so HACK here
00135 
00136   // Add factor as a Sacado-ized parameter
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   // Add parameters from material database as Sacado params
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   //Add Mesh Region Parameters (factors which multiply RHS 
00161   //  of Poisson equation in a given mesh region)
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       // Validate sublist
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       // Create MeshRegion object
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   //Add Point Charges (later add the charge as a Sacado param?)
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       // Validate sublist
00201       psList->sublist(subListName).validateParameters(*ptChargeValidPL,0);
00202 
00203       // Fill PointCharge struct and add to vector (list)
00204       // QCAD::PoissonSource<EvalT, Traits>::PointCharge ptCharge;
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;  // indicates workset & cell are unknown
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     //Add Sacado parameters to set indices of eigenvectors to be multipled together
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; //dummy so all control paths return
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   //validPL->set<double>("Donor Doping", 1e14, "Doping for nsilicon element blocks [cm^-3]");
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   //validPL->set<double>("Donor Activation Energy", 0.045, "Donor activation energy [eV]");
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;          // mesh region scaling factor from element block tests
00381   ScalarT scaleFactor = factor / energy_unit_in_eV; // overall scaling of RHS
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   //mesh region scaling by element block
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   //special case of metals, which always have source == "none", since they have no charge
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   //point charges
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       // do not scale the default device since the DBC is not scaled
00457       poissonSource(cell, qp) = factor*charge;
00458       
00459       // set all states to 0 except electricPotential 
00460       chargeDensity(cell, qp) = 0.0;
00461       electronDensity(cell, qp) = 0.0;  
00462       holeDensity(cell, qp) = 0.0;      
00463       electricPotential(cell, qp) = phi; // no scaling
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   //Note: the moscap1d test structure always outputs values in [eV] (or [V]) and ignores the "Energy Unit In Electron Volts" input parameter
00480   ScalarT temperature = temperatureField(0); //get shared temperature parameter from field
00481 
00482   // Scaling factors
00483   double X0 = length_unit_in_m/1e-2; // length scaling to get to [cm] (structure dimension in [um])
00484   ScalarT V0 = kbBoltz*temperature/1.0; // kb*T/q in [V], scaling for potential        
00485   ScalarT Lambda2 = eps0/(eleQ*X0*X0); // derived scaling factor
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       // Same qPhiRef needs to be used for the entire structure
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); // in [eV]
00503     
00504       ScalarT kbT = kbBoltz*temperature;      // in [eV]
00505       ScalarT Eic = -Eg/2. + 3./4.*kbT*log(mdp/mdn);  // (Ei-Ec) in [eV]
00506       qPhiRef = Chi - Eic;  // (Evac-Ei) in [eV] where Evac = vacuum level
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      // Silicon region
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       // constant prefactor in calculating Nc and Nv in [cm-3]
00540       double NcvFactor = 2.0*pow((kbBoltz*eleQ*m0*Tref)/(2*pi*pow(hbar,2)),3./2.)*1e-6;
00541             // eleQ converts kbBoltz in [eV/K] to [J/K], 1e-6 converts [m-3] to [cm-3]
00542     
00544       ScalarT Nc;  // conduction band effective DOS in [cm-3]
00545       ScalarT Nv;  // valence band effective DOS in [cm-3]
00546       ScalarT Eg;  // band gap at T [K] in [eV]
00547       //ScalarT ni;  // intrinsic carrier concentration in [cm-3]
00548     
00549       Nc = NcvFactor*pow(mdn,1.5)*pow(temperature/Tref,1.5);  // in [cm-3]
00550       Nv = NcvFactor*pow(mdp,1.5)*pow(temperature/Tref,1.5); 
00551       Eg = Eg0-alpha*pow(temperature,2.0)/(beta+temperature); // in [eV]
00552     
00553       ScalarT kbT = kbBoltz*temperature;      // in [eV]
00554       //ni = sqrt(Nc*Nv)*exp(-Eg/(2.0*kbT));    // in [cm-3]
00555     
00556       // argument offset in calculating electron and hole density
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         // retrieve Previous Poisson Potential
00611         ScalarT prevPhi = 0.0, approxEDensity = 0.0;
00612         
00613         if(bUsePredictorCorrector) {
00614           // compute the approximate quantum electron density using predictor-corrector method
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  // otherwise, use the exact quantum density
00620           approxEDensity = eDensityForPoissonSchrodinger(workset, cell, qp, 0.0, false, 0.0, -1.0);
00621 
00622         // compute the exact quantum electron density
00623         ScalarT eDensity = eDensityForPoissonSchrodinger(workset, cell, qp, 0.0, false, 0.0, -1.0);
00624           
00625         // obtain the scaled potential
00626         const ScalarT& unscaled_phi = potential(cell,qp);
00627         ScalarT phi = unscaled_phi / V0; 
00628            
00629         // compute the hole density treated as classical
00630         ScalarT hDensity = Nv*(this->*carrStat)(-phi+hArgOffset); 
00631 
00632         // obtain the ionized dopants
00633         ScalarT ionN  = 0.0;
00634         if (dopantType == "Donor")  // function takes care of sign
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         // the scaled full RHS
00642         ScalarT charge; 
00643         charge = 1.0/Lambda2 * (hDensity- approxEDensity + ionN);
00644         poissonSource(cell, qp) = factor*charge;
00645 
00646         // output states
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)  // include Vxc
00656         {
00657           ScalarT Vxc = computeVxcLDA(relPerm, averagedEffMass, approxEDensity);
00658           conductionBand(cell, qp) = qPhiRef-Chi-phi*V0 +Vxc; // [eV]
00659     electricPotential(cell, qp) = phi*V0 + Vxc - qPhiRef; //add xc correction to electric potential (used in CI delta_ij computation)
00660           // conductionBand(cell, qp) = qPhiRef -Chi -0.5*(phi*V0 +prevPhi) +Vxc; // [eV]
00661         }
00662         else { // not include Vxc
00663           conductionBand(cell, qp) = qPhiRef-Chi-phi*V0; // [eV]
00664     electricPotential(cell, qp) = phi*V0 - qPhiRef;
00665           // conductionBand(cell, qp) = qPhiRef -Chi -0.5*(phi*V0 +prevPhi); // [eV]
00666   }
00667         
00668         valenceBand(cell, qp) = conductionBand(cell,qp)-Eg;
00669 
00670       }
00671 
00673       else
00674       {
00675         // obtain the scaled potential
00676         const ScalarT& unscaled_phi = potential(cell,qp);  //[V]
00677         ScalarT phi = unscaled_phi / V0; 
00678           
00679         // obtain the ionized dopants
00680         ScalarT ionN;
00681         if (dopantType == "Donor")  // function takes care of sign
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         // the scaled full RHS
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         // output states
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; // [eV]
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      } // end of if ( (coord[0] > oxideWidth) ...)
00709 
00710 
00711      // Oxide region
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; // [cm^-3]
00726 
00728       if(quantumRegionSource == "schrodinger")
00729       {
00730         // retrieve Previous Poisson Potential
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           // compute the approximate quantum electron density using predictor-corrector method
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         // compute the exact quantum electron density
00744         ScalarT eDensity = eDensityForPoissonSchrodinger(workset, cell, qp, 0.0, false, 0.0, -1.0);
00745 
00746         // obtain the scaled potential
00747         const ScalarT& unscaled_phi = potential(cell,qp);
00748         ScalarT phi = unscaled_phi / V0; 
00749 
00750         //(No other classical density in insulator)
00751               
00752         // the scaled full RHS
00753         ScalarT charge;
00754         charge = 1.0/Lambda2 * (-approxEDensity + fixedCharge);
00755         poissonSource(cell, qp) = factor*charge;
00756 
00757         // output states
00758         chargeDensity(cell, qp) = -eDensity + fixedCharge; 
00759         electronDensity(cell, qp) = eDensity;  // quantum electrons in an insulator
00760         holeDensity(cell, qp) = 0.0;           // no holes in an insulator
00761         ionizedDopant(cell, qp) = 0.0;
00762         approxQuanEDen(cell,qp) = approxEDensity;
00763         artCBDensity(cell, qp) = eDensity;
00764 
00765         if (bIncludeVxc)  // include Vxc
00766         {
00767           ScalarT Vxc = computeVxcLDA(relPerm, averagedEffMass, approxEDensity);
00768           conductionBand(cell, qp) = qPhiRef-Chi-phi*V0 +Vxc; // [eV]
00769     electricPotential(cell, qp) = phi*V0 + Vxc - qPhiRef; //add xc correction to electric potential (used in CI delta_ij computation)
00770           // conductionBand(cell, qp) = qPhiRef -Chi -0.5*(phi*V0 +prevPhi) +Vxc; // [eV]
00771         }
00772         else  { // not include Vxc
00773           conductionBand(cell, qp) = qPhiRef-Chi-phi*V0; // [eV]
00774     electricPotential(cell, qp) = phi*V0 - qPhiRef;
00775           // conductionBand(cell, qp) = qPhiRef -Chi -0.5*(phi*V0 +prevPhi); // [eV]
00776   }
00777         
00778         valenceBand(cell, qp) = conductionBand(cell,qp)-Eg;
00779       }
00780     
00781       else  // use semiclassical source
00782       {  
00783         const ScalarT& unscaled_phi = potential(cell,qp);
00784         ScalarT phi = unscaled_phi / V0; 
00785           
00786         // the scaled full RHS
00787         ScalarT charge; 
00788         charge = 1.0/Lambda2 * fixedCharge;  // only fixed charge in an insulator
00789         poissonSource(cell, qp) = factor*charge;
00790     
00791         chargeDensity(cell, qp) = fixedCharge; // fixed space charge in an insulator
00792         electronDensity(cell, qp) = 0.0;       // no electrons in an insulator
00793         holeDensity(cell, qp) = 0.0;           // no holes in an insulator
00794         electricPotential(cell, qp) = phi*V0 - qPhiRef;
00795         ionizedDopant(cell, qp) = 0.0;
00796         conductionBand(cell, qp) = qPhiRef-Chi-phi*V0; // [eV]
00797         valenceBand(cell, qp) = conductionBand(cell,qp)-Eg;
00798         approxQuanEDen(cell,qp) = 0.0;
00799         artCBDensity(cell, qp) = 0.0;
00800       }
00801      
00802      } // end of else if ((coord[0] >= 0) ...)
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     }  // end of loop over QPs
00810     
00811   }  // end of loop over cells
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; // length scaling to get to [cm] (structure dimension in [um] usually)
00831   ScalarT temperature = temperatureField(0); //get shared temperature parameter from field
00832   ret.kbT = kbBoltz*temperature / energy_unit_in_eV; // in [myV]
00833 
00835   ret.qPhiRef = getReferencePotential(workset); // in [myV]
00836 
00837   ret.V0 = kbBoltz*temperature / energy_unit_in_eV; // kb*T in desired energy unit ( or kb*T/q in desired voltage unit) [myV]
00838   ret.Lambda2 = eps0/(eleQ*X0*X0);  // derived scaling factor
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"); // in [K]
00846     
00847     ret.Chi = materialDB->getElementBlockParam<double>(workset.EBName,"Electron Affinity") / energy_unit_in_eV; // in [myV]
00848     double Eg0 = materialDB->getElementBlockParam<double>(workset.EBName,"Zero Temperature Band Gap") / energy_unit_in_eV; // in [myV]
00849     double alpha = materialDB->getElementBlockParam<double>(workset.EBName,"Band Gap Alpha Coefficient"); // in [eV]
00850     double beta = materialDB->getElementBlockParam<double>(workset.EBName,"Band Gap Beta Coefficient"); // in [K]
00851     
00853     double NcvFactor = 2.0*pow((kbBoltz*eleQ*m0*Tref)/(2*pi*pow(hbar,2)),3./2.)*1e-6;
00854             // eleQ converts kbBoltz in [eV/K] to [J/K], 1e-6 converts [m-3] to [cm-3]
00855                 
00856     ret.Nc = NcvFactor*pow(mdn,1.5)*pow(temperature/Tref,1.5);  // in [cm-3]
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; // in [myV]
00859     
00860 
00861     //ni = sqrt(Nc*Nv)*exp(-Eg/(2.0*kbT));    // in [cm-3]
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     //  Each element block must have an associated Fermi level, otherwise
00904     //  the Poisson source term cannot be computed.  Since QCAD does not implement
00905     //  drift-diffusion equations, this Fermi level must be specified directly.  This 
00906     //  can be done in several ways:
00907     //   1) a nodeset with a DBC (e.g. a contact) specifies "elementBlock" in its materials db list
00908     //   2) an element block itself specifies "contactNodeset" in its materials db list
00909     //   3) if neither 1) nor 2) hold, a default value of zero is used as the Fermi level.
00910 
00911     ret.fermiE = 0.0;  // default, [myV]
00912     if (mapDBCValue_eb.count(workset.EBName) > 0) 
00913     {
00914       ret.fermiE = -1.0*mapDBCValue_eb[workset.EBName] / energy_unit_in_eV; // [myV] (DBCs are in volts, regardless of desired output energy unit)
00915       // std::cout << "EBName = " << workset.EBName << ", ret.fermiE = " << ret.fermiE << std::endl; 
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; // [myV]
00922     }
00923 
00925     //** Note: doping profile unused currently
00926     ret.fixedChargeType = materialDB->getElementBlockParam<std::string>(workset.EBName,"Dopant Type","None");
00927     ret.fixedChargeConc = 0.0; //only applies to insulators
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; // [myV]
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   } // end "Semiconductor" setup
00958   
00959   else if(mtrlCategory == "Insulator")
00960   {  
00961     ret.Eg = materialDB->getElementBlockParam<double>(workset.EBName,"Band Gap",0.0) / energy_unit_in_eV; // [myV]
00962     ret.Chi = materialDB->getElementBlockParam<double>(workset.EBName,"Electron Affinity",0.0) / energy_unit_in_eV; // [myV]
00963     ret.carrStat = &QCAD::PoissonSource<EvalT,Traits>::computeZeroStat; //always zero  
00964 
00965     //Unused in insulator.  Set as zero
00966     ret.Nc = ret.Nv = 0.0;
00967     ret.hArgOffset = ret.eArgOffset = 0.0;
00968     ret.fermiE = 0.0;
00969     ret.dopingConc = 0.0; //only applies to semiconductors
00970 
00972     if( materialDB->isElementBlockParam(workset.EBName, "Charge Value") ) {
00973       ret.fixedChargeType = "Constant";
00974       ret.fixedChargeConc = materialDB->getElementBlockParam<double>(workset.EBName,"Charge Value");
00975       //std::cout << "DEBUG: applying fixed charge " << ret.fixedChargeConc << " to element block '" << workset.EBName << "'" << std::endl;
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       //std::cout << "DEBUG: applying fixed charge " << ret.fixedChargeConc << " to element block '" << workset.EBName << "' via param" << std::endl;
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   } // end "Insulator" setup
01000 
01001 
01002   else if(mtrlCategory == "Metal")
01003   {  
01004     // Use work function where semiconductor and insulator use electron affinity
01005     ret.Chi = materialDB->getElementBlockParam<double>(workset.EBName,"Work Function") / energy_unit_in_eV; // [myV]
01006     ret.Eg = 0.0;  //no bandgap in metals
01007   } // end "Metal" setup
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   // Previous electron density -- used for daming PS iterations
01016   ret.prevDensityFactor = QCAD::EvaluatorTools<EvalT,Traits>::getDoubleValue( prevDensityMixingFactor );
01017 
01018   //HACK
01019   static double lastNonDefaultFactor = -1.0;
01020   if(ret.prevDensityFactor < 0.0) { //factor was not set by parameters, which is probably an interal Albany bug, so set using last non-default param
01021     ret.prevDensityFactor = lastNonDefaultFactor;
01022     //std::cout << "WARNING: prevDensityMixingFactor was not set via a parameter - setting to last non-default = " << lastNonDefaultFactor << std::endl;
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   //END HACK
01030 
01031   if(ret.prevDensityFactor > 1e-8)
01032     ret.prevDensityArray = (*workset.stateArrayPtr)["PS Previous Electron Density"];
01033 
01034   ret.quantum_edensity_fn = NULL; //default
01035 
01036   //Fill additional members for quantum and coulomb sources
01037   if(sourceName == "schrodinger") {
01038     if(bUsePredictorCorrector) // retrieve Previous Poisson Potential
01039       ret.prevPhiArray = (*workset.stateArrayPtr)["PS Previous Poisson Potential"]; //assumed in [myV]
01040 
01041     ret.quantum_edensity_fn = &QCAD::PoissonSource<EvalT,Traits>::eDensityForPoissonSchrodinger;
01042   }
01043   else if(sourceName == "ci") {
01044     if(bUsePredictorCorrector) // retrieve Previous Poisson Potential
01045       ret.prevPhiArray = (*workset.stateArrayPtr)["PS Previous Poisson Potential"]; //assumed in [myV]
01046 
01047     ret.quantum_edensity_fn = &QCAD::PoissonSource<EvalT,Traits>::eDensityForPoissonCI;
01048   }
01049   else if(sourceName == "coulomb")  {
01050     //RHS == evec[i] * evec[j]
01051     ret.sourceEvec1 = (int)QCAD::EvaluatorTools<EvalT,Traits>::getDoubleValue( sourceEvecInds[0] );
01052     ret.sourceEvec2 = (int)QCAD::EvaluatorTools<EvalT,Traits>::getDoubleValue( sourceEvecInds[1] );
01053     
01054     //int valleyDegeneracyFactor = materialDB->getElementBlockParam<int>(workset.EBName,"Number of conduction band min",2);
01055     // scale so electron density is in [cm^-3] (assume 3D? Suzey?) as expected of RHS of Poisson eqn
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   // -- Semiconductor
01070   const ScalarT& unscaled_phi = potential(cell,qp);  // [myV]
01071   ScalarT phi = unscaled_phi / setup_info.V0; 
01072           
01073   // obtain the ionized dopants (in semiconductor) or fixed charge (in insulator)
01074   ScalarT fixedCharge;
01075   if (setup_info.fixedChargeType == "Donor")  // function takes care of sign
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   // the scaled full RHS
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   // Mix with previous density (to damp S-P iterations)
01090   if(setup_info.prevDensityFactor > 1e-8) { 
01091     ScalarT prevDensity = setup_info.prevDensityArray(cell,qp); 
01092     //eDensity = eDensity * (1 - setup_info.prevDensityFactor) + setup_info.prevDensityFactor * prevDensity;
01093   }
01094 
01095   charge = 1.0/setup_info.Lambda2 * (hDensity - eDensity + fixedCharge);
01096   poissonSource(cell, qp) = scaleFactor*charge;
01097   
01098   //DEBUG
01099   /*if(isnan( QCAD::EvaluatorTools<EvalT,Traits>::getDoubleValue( poissonSource(cell,qp) )))
01100     std::cout << "semicl source("<<cell<<","<<qp<<") is NAN" << std::endl;
01101   if(isinf( QCAD::EvaluatorTools<EvalT,Traits>::getDoubleValue( poissonSource(cell,qp) )))
01102     std::cout << "semicl source("<<cell<<","<<qp<<") is INF -- h=" << hDensity << ", e="<< eDensity << ", i=" << fixedCharge
01103         << ", phi=" << phi << ", eArg=" << setup_info.eArgOffset << ", fermiE=" << setup_info.fermiE 
01104         << ", kbT=" << setup_info.kbT << std::endl;
01105   */
01106   
01107   // output states
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; // [myV]
01112   ionizedDopant(cell, qp) = fixedCharge;
01113   conductionBand(cell, qp) = setup_info.qPhiRef-setup_info.Chi-phi*setup_info.V0; // [myV]
01114   valenceBand(cell, qp) = conductionBand(cell,qp)-setup_info.Eg; // [myV]
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);  //[myV]
01128   ScalarT phi = unscaled_phi / setup_info.V0;  //[unitless]
01129   
01130   // the scaled full RHS
01131   ScalarT charge = 0.0;  // no charge in this RHS mode
01132   poissonSource(cell, qp) = scaleFactor*charge;
01133   
01134   chargeDensity(cell, qp) = 0.0;         // no charge in this RHS mode
01135   electronDensity(cell, qp) = 0.0;       // no electron
01136   holeDensity(cell, qp) = 0.0;           // no holes
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; // [myV]
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   // -- Semiconductor, but see "insulator" comments
01153   ScalarT approxEDensity = 0.0;
01154           
01155   if(bUsePredictorCorrector) {
01156     // compute the approximate quantum electron density using predictor-corrector method
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  // otherwise, use the exact quantum density
01161     approxEDensity = (this->*(setup_info.quantum_edensity_fn))(workset, cell, qp, 0.0, false, setup_info.fermiE, fixedQuantumOcc);
01162 
01163   // compute the exact quantum electron density
01164   ScalarT eDensity = (this->*(setup_info.quantum_edensity_fn))(workset, cell, qp, 0.0, false, setup_info.fermiE, fixedQuantumOcc);
01165 
01166   // Mix with previous density (to damp S-P iterations)
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   // obtain the scaled potential
01174   const ScalarT& unscaled_phi = potential(cell,qp); //[myV]
01175   ScalarT phi = unscaled_phi / setup_info.V0; 
01176            
01177   // compute the hole density treated as classical
01178   ScalarT hDensity = setup_info.Nv*(this->*(setup_info.carrStat))(-phi + setup_info.hArgOffset);
01179 
01180   // obtain the ionized dopants (in semiconductor) or fixed charge (in insulator)
01181   ScalarT fixedCharge  = 0.0;
01182   if (setup_info.fixedChargeType == "Donor")  // function takes care of sign
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   // the scaled full RHS
01192   ScalarT charge; 
01193   charge = 1.0/setup_info.Lambda2*(hDensity- approxEDensity + fixedCharge);
01194   poissonSource(cell, qp) = scaleFactor*charge;
01195 
01196   //DEBUG
01197   /*if(isnan( QCAD::EvaluatorTools<EvalT,Traits>::getDoubleValue( poissonSource(cell,qp) )))
01198     std::cout << "quantum source("<<cell<<","<<qp<<") is NAN" << std::endl;
01199     if(isinf( QCAD::EvaluatorTools<EvalT,Traits>::getDoubleValue( poissonSource(cell,qp) )))
01200     std::cout << "quantum source("<<cell<<","<<qp<<") is INF" << std::endl;
01201   */
01202 
01203   // output states
01204   chargeDensity(cell, qp) = hDensity -eDensity +fixedCharge;
01205   electronDensity(cell, qp) = eDensity;
01206   holeDensity(cell, qp) = hDensity;
01207   //electricPotential(cell, qp) = phi*V0 - qPhiRef; //electric potenial == solution shifted so "reference" == 0
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)  // include Vxc
01213   {
01214     ScalarT Vxc = computeVxcLDA(setup_info.relPerm, setup_info.averagedEffMass, approxEDensity) / energy_unit_in_eV; // [myV]
01215     conductionBand(cell, qp) = setup_info.qPhiRef -setup_info.Chi -phi*setup_info.V0 +Vxc; // [myV]
01216               
01217     // Suzey: need to be discussed (April 06, 2012) ? 
01218     electricPotential(cell, qp) = phi*setup_info.V0 + Vxc- setup_info.qPhiRef; //add xc correction to electric potential (used in CI delta_ij computation)
01219   }
01220   else  // not include Vxc
01221   {
01222     conductionBand(cell, qp) = setup_info.qPhiRef -setup_info.Chi -phi*setup_info.V0; // [myV]
01223     electricPotential(cell, qp) = phi*setup_info.V0 - setup_info.qPhiRef; // [myV]
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   // obtain the scaled potential
01236   const ScalarT& unscaled_phi = potential(cell,qp); //[V]
01237   ScalarT phi = unscaled_phi / setup_info.V0; 
01238 
01239   // the scaled full RHS   note: wavefunctions are assumed normalized by the mass matrix (i.e. integral( Psi_i^2 dR ) == 1)
01240   // Source term is conj(evec_j) * evec_i  
01241   // TODO: double-check this is correct, seeing as eigenvectors are normalized by mass matrix
01242   ScalarT charge;
01243   int i = setup_info.sourceEvec1;
01244   int j = setup_info.sourceEvec2;
01245   if(imagPartOfCoulombSrc) {
01246     if(!bRealEigenvectors) // if eigenvectors are all real, then there is no imaginary part of coulomb source
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   //never include Vxc
01268   conductionBand(cell, qp) = setup_info.qPhiRef -setup_info.Chi -phi*setup_info.V0; // [eV]
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   // obtain the scaled potential
01279   const ScalarT& unscaled_phi = potential(cell,qp); //[myV]
01280   ScalarT phi = unscaled_phi / setup_info.V0; 
01281   MeshScalarT* coord = &coordVec(cell,qp,0);
01282 
01283   // the scaled full RHS   Source term is x^2 + y^2 + z^2 (for debugging purposes)
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; //sign??
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   //never include Vxc
01294   conductionBand(cell, qp) = setup_info.qPhiRef-setup_info.Chi-phi*setup_info.V0; // [myV]
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    // Use the approximate 1/2 FD integral by D. Bednarczyk and J. Bednarczyk, 
01319    // "The approximation of the Fermi-Dirac integral F_{1/2}(x),"
01320    // Physics Letters A, vol.64, no.4, pp.409-410, 1978. The approximation 
01321    // has error < 4e-3 in the entire x range.  
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); // for x<-50, the 1/2 FD integral is well approximated by 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   // fully ionized (create function to use function pointer)
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);  // use Boltzman statistics for large positive x, 
01400     else                           // as the Fermi statistics leads to bad derivative.
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   // Use the predictor-corrector method proposed by A. Trellakis, A. T. Galick,  
01438   // and U. Ravaioli, "Iteration scheme for the solution of the two-dimensional  
01439   // Schrodinger-Poisson equations in quantum structures", J. Appl. Phys. 81, 7880 (1997). 
01440   // If bUsePredCorr = true, use the predictor-corrector approach.
01441   // If bUsePredCorr = false, calculate the exact quantum electron density.
01442   // Fermi-Dirac distribution is used in computing electron density.  
01443   
01444   // unit conversion factor
01445   double eVPerJ = 1.0/eleQ; 
01446   double cm2Perm2 = 1.0e4; 
01447   
01448   // For Delta2-band in Silicon, valley degeneracy factor = 2
01449   int valleyDegeneracyFactor = materialDB->getElementBlockParam<int>(workset.EBName,"Number of conduction band min",2);
01450 
01451   // Scaling factors
01452   double X0 = length_unit_in_m/1e-2; // length scaling to get to [cm] (structure dimension in [um])
01453 
01454   ScalarT temperature = temperatureField(0); //get shared temperature parameter from field
01455   ScalarT kbT_eV = kbBoltz*temperature;  // in [eV]
01456   ScalarT kbT = kbBoltz*temperature / energy_unit_in_eV;  // in [myV]
01457   ScalarT eDensity = 0.0; 
01458 
01459   // assume eigenvalues are in [myV] units (units of energy_unit_in_eV * eV)
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; //apply minus sign (b/c of eigenval convention)
01463 
01464   // determine deltaPhi used in computing quantum electron density
01465   ScalarT deltaPhi = 0.0;  // 0 by default
01466   if (bUsePredCorr)  // true: apply the p-c method
01467   {
01468     const ScalarT& phi = potential(cell,qp); //[myV]
01469     deltaPhi = (phi - prevPhi) / (kbT /1.0);  //[unitless]
01470   }
01471   else
01472     deltaPhi = 0.0;  // false: do not apply the p-c method
01473 
01474   ScalarT eDenPrefactor;
01475   std::vector<ScalarT> occ( eigenvals.size(), 0.0); //occupation of ith eigenstate
01476   
01477   // compute quantum "occupation" according to dimensionality, which is just the coefficient 
01478   //   of each (wavefunction)^2 term in the density, as well as a prefactor.
01479   switch (numDims)
01480   {
01481     case 1: // 1D wavefunction (1D confinement)
01482     {  
01483       // m11 = in-plane effective mass (y-z plane when the 1D wavefunc. is along x)
01484       // For Delta2-band (or valley), m11 is the transverse effective mass (0.19). 
01485       
01486       double m11 = materialDB->getElementBlockParam<double>(workset.EBName,"Transverse Electron Effective Mass");
01487         
01488       // 2D density of states in [#/(eV.cm^2)] where 2D is the unconfined y-z plane
01489       // dos2D below includes the spin degeneracy of 2
01490       double dos2D = m11*m0/(pi*hbar*hbar*eVPerJ*cm2Perm2); 
01491         
01492       // subband-independent prefactor in calculating electron density
01493       // X0 is used to scale wavefunc. squared from [um^-1] or [nm^-1] to [cm^-1]
01494       eDenPrefactor = valleyDegeneracyFactor*dos2D*kbT_eV/X0; 
01495 
01496       // loop over eigenvalues to compute the occupation
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;  // exp(tmpArg) blows up for large positive tmpArg, leading to bad derivative
01503         else
01504           logFunc = log(1.0 + exp(tmpArg));
01505   occ[i] = logFunc;
01506       }
01507       break; 
01508     }  // end of case 1 block
01509 
01510 
01511     case 2: // 2D wavefunction (2D confinement)
01512     {
01513       // mUnconfined = effective mass in the unconfined direction (z dir. when the 2D wavefunc. is in x-y plane)
01514       // For Delta2-band and assume SiO2/Si interface parallel to [100] plane, mUnconfined=0.19. 
01515       double mUnconfined = materialDB->getElementBlockParam<double>(workset.EBName,"Transverse Electron Effective Mass");
01516         
01517       // n1D below is a factor that is part of the line electron density 
01518       // in the unconfined dir. and includes spin degeneracy and in unit of [cm^-1]
01519       ScalarT n1D = sqrt(2.0*mUnconfined*m0*kbT_eV/(pi*hbar*hbar*eVPerJ*cm2Perm2));
01520         
01521       // subband-independent prefactor in calculating electron density
01522       // X0^2 is used to scale wavefunc. squared from [um^-2] or [nm^-2] to [cm^-2]
01523       eDenPrefactor = valleyDegeneracyFactor*n1D/pow(X0,2.);
01524 
01525       // loop over eigenvalues to compute the occupation
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     }  // end of case 2 block    
01533 
01534 
01535     case 3: // 3D wavefunction (3D confinement)
01536     { 
01537       //degeneracy factor
01538       int spinDegeneracyFactor = 2;
01539       double degeneracyFactor = spinDegeneracyFactor * valleyDegeneracyFactor;
01540         
01541       // subband-independent prefactor in calculating electron density
01542       // X0^3 is used to scale wavefunc. squared from [um^-3] or [nm^-3] to [cm^-3]
01543       eDenPrefactor = degeneracyFactor/pow(X0,3.);
01544 
01545       // loop over eigenvalues to compute occupation
01546       for(int i = 0; i < nEigenvectors; i++) 
01547       {
01548         // ScalarT tmpArg = (eigenvals[i]-Ef)/kbT + deltaPhi;
01549         // It is critical to use -deltaPhi for 3D (while +deltaPhi for 1D and 2D) as 
01550         // derived theoretically. +deltaPhi leads to serious oscillations (tested).  
01551         
01552         ScalarT tmpArg = (eigenvals[i]-Ef)/kbT - deltaPhi;
01553         ScalarT fermiFactor; 
01554         
01555         if (tmpArg > MAX_EXPONENT) 
01556           fermiFactor = exp(-tmpArg);  // use Boltzmann statistics for large positive tmpArg,
01557         else                           // as the Fermi statistics leads to bad derivative        
01558           fermiFactor = 1.0/( exp(tmpArg) + 1.0 ); 
01559   occ[i] = fermiFactor;
01560       }
01561       break;
01562     }  // end of case 3 block 
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; //to avoid compiler warning
01568       break; 
01569       
01570   }  // end of switch (numDims) 
01571 
01572   //Enforce a fixed occupation (non-equilibrium) if desired (indicated by fixedOcc > 0)
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   // loop over eigenvalues to compute electron density [cm^-3]
01582   for(int i = 0; i < nEigenvectors; i++) 
01583   {
01584     // note: wavefunctions are assumed normalized here 
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; // in [cm^-3]
01594 
01595   return eDensity; 
01596 }
01597 
01598 
01599 //Similar as eDensityForPoissonSchrodinger but assume |wf|^2 values are given in eigenvector_Re[.] instead of eigenvector
01600 // Note: sum of |wf|^2 values == N where N is the number of electrons in the wave function, not necessarily == 1
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   // Use the predictor-corrector method proposed by A. Trellakis, A. T. Galick,  
01608   // and U. Ravaioli, "Iteration scheme for the solution of the two-dimensional  
01609   // Schrodinger-Poisson equations in quantum structures", J. Appl. Phys. 81, 7880 (1997). 
01610   // If bUsePredCorr = true, use the predictor-corrector approach.
01611   // If bUsePredCorr = false, calculate the exact quantum electron density.
01612   // Fermi-Dirac distribution is used in computing electron density.  
01613   
01614   // unit conversion factor
01615   //double eVPerJ = 1.0/eleQ; 
01616   //double cm2Perm2 = 1.0e4; 
01617 
01618   // For Delta2-band in Silicon, valley degeneracy factor = 2
01619   int valleyDegeneracyFactor = materialDB->getElementBlockParam<int>(workset.EBName,"Number of conduction band min",2);
01620 
01621   // Scaling factors
01622   double X0 = length_unit_in_m/1e-2; // length scaling to get to [cm] (structure dimension in [um])
01623 
01624   ScalarT temperature = temperatureField(0); //get shared temperature parameter from field in [K]
01625   ScalarT kbT = kbBoltz*temperature / energy_unit_in_eV;  // in [myV]
01626   ScalarT eDensity = 0.0; 
01627   //double Ef = 0.0;  //Fermi energy == 0
01628 
01629   const std::vector<double>& neg_eigenvals = *(workset.eigenDataPtr->eigenvalueRe);
01630   std::vector<double> eigenvals( neg_eigenvals );
01631   int nCIEvals = eigenvals.size(); //not necessarily == nEigenvectors, since CI could have not converged as many as requested
01632   int nEvals = std::min(nCIEvals, nEigenvectors); // the number of eigen-pairs to use (we don't gather more than nEigenvectors)
01633 
01634   //I don't believe CI eigenvalues are negated...
01635   //for(unsigned int i=0; i<nEvals; ++i) eigenvals[i] *= -1; //apply minus sign (b/c of eigenval convention)
01636 
01637   //Note: NO predictor corrector method used here yet -- need to understand what's going on better first
01638 
01639   ScalarT eDenPrefactor;
01640   std::vector<ScalarT> occ( nEvals, 0.0); //occupation of ith eigenstate
01641   
01642   // compute quantum electron density according to dimensionality
01643   switch (numDims)
01644   {
01645     //No 1D case -- deal with this later
01646 
01647     case 2: // 2D wavefunction (2D confinement)
01648     {
01649       // ** Assume for now that 2D case means there is confinement along the 3rd dimension such that only a single, completely non-degenerate
01650       //  level exists in the third dimension.  In addition, the CI accounts for spin degeneracy, so there should be no spin degeneracy factors.
01651       //  Together, I think this means n1D == 1.0 below. **
01652 
01653       // mUnconfined = effective mass in the unconfined direction (z dir. when the 2D wavefunc. is in x-y plane)
01654       // For Delta2-band and assume SiO2/Si interface parallel to [100] plane, mUnconfined=0.19. 
01655       //double mUnconfined = materialDB->getElementBlockParam<double>(workset.EBName,"Transverse Electron Effective Mass");
01656         
01657       // n1D below is a factor that is part of the line electron density 
01658       // in the unconfined dir. and includes spin degeneracy and in unit of [cm^-1]
01659       ScalarT n1D = 1.0; //sqrt(2.0*mUnconfined*m0*kbT_eV/(pi*hbar*hbar*eVPerJ*cm2Perm2));
01660         
01661       // subband-independent prefactor in calculating electron density
01662       // X0^2 is used to scale wavefunc. squared from [um^-2] or [nm^-2] to [cm^-2]
01663       // Note: 1/energy_unit_in_eV factor ==> correct [myV] units for LHS of Poisson
01664       eDenPrefactor = valleyDegeneracyFactor*n1D/pow(X0,2.) / energy_unit_in_eV;
01665       
01666       // Get Z = sum( exp(-E_i/kT) )
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     }  // end of case 2 block    
01677 
01678 
01679     case 3: // 3D wavefunction (3D confinement)
01680     { 
01681       //degeneracy factor
01682       double degeneracyFactor = valleyDegeneracyFactor; //CI includes spin degeneracy
01683         
01684       // subband-independent prefactor in calculating electron density
01685       // X0^3 is used to scale wavefunc. squared from [um^-3] or [nm^-3] to [cm^-3]
01686       eDenPrefactor = degeneracyFactor/pow(X0,3.);
01687 
01688       // Get Z = sum( exp(-E_i/kT) )
01689       //ScalarT Z = 0.0;
01690       //for(int i=0; i < nEvals; i++) Z += exp(-eigenvals[i]/kbT);
01691 
01692       for(int i = 0; i < nEvals; i++) 
01693       {
01694         ScalarT oneOverOcc = 0.0;  // get 1/(exp(-eigenvals[i]/kbT) / Z) as sum, then invert (avoids inf issues)
01695   for(int j=0; j < nEvals; j++) oneOverOcc += exp(-(eigenvals[j]-eigenvals[i])/kbT);
01696 
01697   ScalarT wfOcc = 0.0; //exp(-eigenvals[i]/kbT) / Z;
01698   if(!std::isinf(QCAD::EvaluatorTools<EvalT,Traits>::getDoubleValue(oneOverOcc)))
01699     wfOcc = 1/oneOverOcc; //otherwise just leave as zero since denom is infinite
01700   occ[i] = wfOcc;
01701       }
01702       break;
01703     }  // end of case 3 block 
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   }  // end of switch (numDims) 
01711 
01712   //Enforce a fixed occupation (non-equilibrium) if desired (indicated by fixedOcc > 0)
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   // loop over eigenvalues to compute electron density [cm^-3]
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; // in [cm^-3]
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   // Scaling factors
01742   double X0 = length_unit_in_m/1e-2; // length scaling to get to [cm] (structure dimension in [um] usually)
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; //NOTE: this test should be replaced with cell type test
01753     else if(numDims == 3 && numNodes == 8) point_test_fn = &QCAD::PoissonSource<EvalT,Traits>::pointIsInHexahedron;  //NOTE: this test should be replaced with cell type test
01754     else if(numDims == 2)                  point_test_fn = &QCAD::PoissonSource<EvalT,Traits>::pointIsInPolygon;
01755     else                                   point_test_fn = NULL;
01756     
01757     //std::cout << "DEBUG: Looking for point charges in ws " << workset.wsIndex << " - scanned " 
01758     //  << numWorksetsScannedForPtCharges << " worksets so far" << std::endl;
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       //std::cout << "DEBUG: CELL " << cell << "VERTICES = " << std::endl;
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); //equality should always hold in if stmt above
01784     numWorksetsScannedForPtCharges++;
01785   }
01786   
01788   for( std::size_t i=0; i < pointCharges.size(); ++i) {
01789     if( pointCharges[i].iWorkset != (int)workset.wsIndex ) continue; //skips if iWorkset == -1 (not found)
01790     
01791     MeshScalarT cellVol = 0.0, qpChargeDen;
01792     std::size_t cell = pointCharges[i].iCell; // iCell should be valid here since iWorkset is
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)); // 3 was (int)numDims but I think this is wrong (Suzey?); // [cm^-3] value of qps so that integrated charge is correct
01797     //std::cout << "DEBUG: ADDING POINT CHARGE (den=" << qpChargeDen << ", was " << chargeDensity(cell,0) << ") to ws "
01798     //    << workset.wsIndex << ", cell " << cell << std::endl;
01799     for (std::size_t qp=0; qp < numQPs; ++qp) {
01800   ScalarT scaleFactor = 1.0; //TODO: get appropriate scale factor from meshRegions (later?)
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   // Assumes cellVertices contains 4 3D points (length 12)
01815   //  and position is one 3D point (length 3).
01816   // Returns true if position lies within the tetrahedron defined by the 4 points, false otherwise.
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; //last entry in each "4D position" == 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   // Assumes cellVertices contains 8 3D points (length 24) in the order specified by shards::Hexahedron 
01853   //  and position is one 3D point (length 3).
01854   // Returns true if position lies within the hexahedron defined by the 8 points, false otherwise.
01855   
01856   /* From shards::Hexadedron (vertex ordering)
01857          7                    6
01858            o------------------o
01859           /|                 /|
01860          / |                / |
01861         /  |               /  |
01862        /   |              /   |
01863       /    |             /    |
01864      /     |            /     |
01865   4 /      |         5 /      |
01866    o------------------o       |
01867    |       |          |       |
01868    |     3 o----------|-------o 2
01869    |      /           |      /
01870    |     /            |     /
01871    |    /             |    /
01872    |   /              |   /
01873    |  /               |  /
01874    | /                | /
01875    |/                 |/
01876    o------------------o
01877   0                    1
01878 
01879   */
01880   
01881   // Perform the following tests:
01882   // 1) is P on same side of 0123 as 4?
01883   // 2) is P on same side of 4567 as 0?
01884   // 3) is P on same side of 0154 as 2?
01885   // 4) is P on same side of 2376 as 0?
01886   // 5) is P on same side of 0374 as 2?
01887   // 6) is P on same side of 1265 as 0?
01888   // Note: we assume faces are planar and so only need 3 of 4 vertices to determine plane.
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   // Assumes cellVertices contains nVertices 2D points in some order travelling around the polygon,
01916   //  and position is one 2D point (length 2).
01917   // Returns true if position lies within the polygon, false otherwise.
01918   //  Algorithm = ray trace along positive x-axis
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]; // 2 == dimension
01927     const MeshScalarT* pj = &cellVertices[2*j]; // 2 == dimension
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   // Returns the determinant of an NxN matrix mx
01944   // mx is an array of arrays: mx[i][j] gives matrix entry in row i, column j
01945   MeshScalarT det = 0, term;
01946 
01947   //Loop over all permutations and keep track of sign (Dijkstra's algorithm)
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     //inds holds indices of permutation and sign % 2 is sign
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; //we're done
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;  // swap values at positions (i-1) and (j-1)
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;  // swap values at positions (i-1) and (j-1)
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   // 3D only : assumes all arguments are length 3 arrays
01993   MeshScalarT detA, detB;
01994 
01995   const MeshScalarT *mx[3];  // row mx[0] defines the point in question, rows mx[1] and mx[2] define plane
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]; // use plane0 as reference position (subtract off of others)
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]; // replace row 0 with ptB-plane0
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    // Use the approximate -1/2 FD integral by P. Van Halen and D. L. Pulfrey, 
02027    // "Accurate, short series approximations to Fermi-Dirac integrals of order 
02028    // -/2, 1/2, 1, 3/2, 2, 5/2, 3, and 7/2" J. Appl. Phys. 57, 5271 (1985) 
02029    // and its Erratum in J. Appl. Phys. 59, 2264 (1986). The approximation
02030    // has error < 1e-5 in the entire x range.  
02031    
02032    ScalarT fdInt; 
02033    double a1, a2, a3, a4, a5, a6, a7; 
02034    if (x <= 0.)  // eqn.(4) in the reference
02035    {
02036      a1 = 0.999909;  // Table I in Erratum
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.)  // eqn.(6) in Erratum
02046    {
02047      a1 = 1.12837;  // Table II in Erratum
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))  // eqn.(7) in Erratum
02057    {
02058      double a8, a9;
02059      a1 = 0.604856;  // Table III in Erratum
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  // 2.5<x<5, eqn.(7) in Erratum
02071    {
02072      double a8;
02073      a1 = 0.638086;  // Table III in Erratum
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   // Compute the exchange-correlation potential energy within the Local Density
02095   // Approximation. Use the parameterized expression from: 
02096   // [1] L. Hedin and B. I. Lundqvist, J. Phys. C 4, 2064 (1971);
02097   // [2] F. Stern and S. Das Sarma, Phys. Rev. B 30, 840 (1984);
02098   // [3] Dragical Vasileska, "Solving the Effective Mass Schrodinger Equation 
02099   // in State-of-the-Art Devices", http://nanohub.org. 
02100   // Potential in returned in units of eV.
02101   
02102   // first 100.0 converts eps0 from [C/(V.cm)] to [C/(V.m)],
02103   // second 100.0 converts b from [m] to [cm]. 
02104   ScalarT b = (eps0*100.0)*hbar*hbar / (m0*eleQ*eleQ) * 100.0;  // [cm]
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);  // [eV]
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.);  // [unitless]
02118     ScalarT x = rs / 21.0; 
02119     Vxc = -2.0/(pi*alpha*rs) * Ry * (1.0+ 0.7734*x*log(1.+1.0/x)); // [eV]
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     // Get quantities in desired energy (voltage) units, which we denote "[myV]"
02157  
02158     // Same qPhiRef needs to be used for the entire structure
02159     ScalarT temperature = temperatureField(0); //get shared temperature parameter from field
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; // in [myV]
02163     double Eg0 = materialDB->getMaterialParam<double>(refMtrlName,"Zero Temperature Band Gap") / energy_unit_in_eV; // in [myV]
02164     double alpha = materialDB->getMaterialParam<double>(refMtrlName,"Band Gap Alpha Coefficient"); // in [eV/K]
02165     double beta = materialDB->getMaterialParam<double>(refMtrlName,"Band Gap Beta Coefficient");  // in [K]
02166     ScalarT Eg = Eg0-(alpha*pow(temperature,2.0)/(beta+temperature)) / energy_unit_in_eV; // in [myV]
02167   
02168     ScalarT kbT = kbBoltz*temperature / energy_unit_in_eV; // in [myV] (desired voltage unit)
02169     ScalarT Eic = -Eg/2. + 3./4.*kbT*log(mdp/mdn);  // (Ei-Ec) in [myV]
02170     qPhiRef = Chi - Eic;  // (Evac-Ei) in [myV] where Evac = vacuum level
02171   }
02172   else if (category == "Insulator") {
02173     double Chi = materialDB->getMaterialParam<double>(refMtrlName,"Electron Affinity");
02174     qPhiRef = Chi / energy_unit_in_eV; // in [myV]
02175   }
02176   else if (category == "Metal") {
02177     double workFn = materialDB->getMaterialParam<double>(refMtrlName,"Work Function"); 
02178     qPhiRef = workFn / energy_unit_in_eV; // in [myV]
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 // **********************************************************************

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