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

QCAD_SchrodingerResid_Def.hpp

Go to the documentation of this file.
00001 //*****************************************************************//
00002 //    Albany 2.0:  Copyright 2012 Sandia Corporation               //
00003 //    This Software is released under the BSD license detailed     //
00004 //    in the file "license.txt" in the top-level Albany directory  //
00005 //*****************************************************************//
00006 
00007 #include "Teuchos_TestForException.hpp"
00008 #include "Phalanx_DataLayout.hpp"
00009 
00010 #include "Intrepid_FunctionSpaceTools.hpp"
00011 
00012 
00013 //**********************************************************************
00014 template<typename EvalT, typename Traits>
00015 QCAD::SchrodingerResid<EvalT, Traits>::
00016 SchrodingerResid(const Teuchos::ParameterList& p,
00017                  const Teuchos::RCP<Albany::Layouts>& dl) :
00018   wBF         (p.get<std::string>  ("Weighted BF Name"), dl->node_qp_scalar),
00019   psi         (p.get<std::string>  ("QP Variable Name"), dl->qp_scalar),
00020   psiDot      (p.get<std::string>  ("QP Time Derivative Variable Name"), dl->qp_scalar),
00021   wGradBF     (p.get<std::string>  ("Weighted Gradient BF Name"), dl->node_qp_gradient),
00022   psiGrad     (p.get<std::string>  ("Gradient QP Variable Name"), dl->qp_gradient),
00023   V           (p.get<std::string>  ("Potential Name"), dl->qp_scalar),
00024   coordVec    (p.get<std::string>  ("QP Coordinate Vector Name"), dl->qp_gradient),
00025   havePotential (p.get<bool>("Have Potential")),
00026   psiResidual (p.get<std::string>  ("Residual Name"), dl->node_scalar)
00027 {
00028   // obtain Finite Wall potential parameters
00029   Teuchos::ParameterList* psList = p.get<Teuchos::ParameterList*>("Parameter List");
00030   potentialType = psList->get<std::string>("Type", "defaultType");
00031   barrEffMass = psList->get<double>("Barrier Effective Mass", 0.0);
00032   barrWidth = psList->get<double>("Barrier Width", 0.0);
00033   wellEffMass = psList->get<double>("Well Effective Mass", 0.0);
00034   wellWidth = psList->get<double>("Well Width", 0.0);
00035 
00036   // obtain Oxide and Silicon Width for 1D MOSCapacitor
00037   oxideWidth = psList->get<double>("Oxide Width", 0.0);
00038   siliconWidth = psList->get<double>("Silicon Width", 0.0); 
00039   
00040   enableTransient = true; //always true - problem doesn't make sense otherwise
00041   energy_unit_in_eV = p.get<double>("Energy unit in eV");
00042   length_unit_in_m = p.get<double>("Length unit in m");
00043   bOnlyInQuantumBlocks = p.get<bool>("Only solve in quantum blocks");
00044 
00045   // calculate hbar^2/2m0 so kinetic energy has specified units (EnergyUnitInEV)
00046   const double hbar = 1.0546e-34;   // Planck constant [J s]
00047   const double evPerJ = 6.2415e18;  // eV per Joule (eV/J)
00048   const double emass = 9.1094e-31;  // Electron mass [kg]
00049   hbar2_over_2m0 = 0.5*pow(hbar,2)*evPerJ /(emass *energy_unit_in_eV *pow(length_unit_in_m,2));
00050 
00051   // Material database
00052   materialDB = p.get< Teuchos::RCP<QCAD::MaterialDatabase> >("MaterialDB");
00053 
00054   std::vector<PHX::DataLayout::size_type> dims;
00055   dl->qp_gradient->dimensions(dims);
00056   numQPs  = dims[1];
00057   numDims = dims[2];
00058 
00059   // Allocate workspace
00060   psiGradWithMass.resize(dims[0], numQPs, numDims);
00061   psiV.resize(dims[0], numQPs);
00062   V_barrier.resize(dims[0], numQPs);
00063 
00064   this->addDependentField(wBF);
00065   this->addDependentField(psi);
00066   this->addDependentField(psiDot);
00067   this->addDependentField(psiGrad);
00068   this->addDependentField(wGradBF);
00069   this->addDependentField(coordVec);
00070   if (havePotential) this->addDependentField(V);
00071 
00072   this->addEvaluatedField(psiResidual);
00073   
00074   this->setName("SchrodingerResid"+PHX::TypeString<EvalT>::value);
00075 }
00076 
00077 
00078 //**********************************************************************
00079 template<typename EvalT, typename Traits>
00080 void QCAD::SchrodingerResid<EvalT, Traits>::
00081 postRegistrationSetup(typename Traits::SetupData d,
00082                       PHX::FieldManager<Traits>& fm)
00083 {
00084   this->utils.setFieldData(wBF,fm);
00085   this->utils.setFieldData(psi,fm);
00086   this->utils.setFieldData(psiDot,fm);
00087   this->utils.setFieldData(psiGrad,fm);
00088   this->utils.setFieldData(wGradBF,fm);
00089   this->utils.setFieldData(coordVec,fm);
00090   if (havePotential)  this->utils.setFieldData(V,fm);
00091 
00092   this->utils.setFieldData(psiResidual,fm);
00093 }
00094 
00095 
00096 //**********************************************************************
00097 template<typename EvalT, typename Traits>
00098 void QCAD::SchrodingerResid<EvalT, Traits>::
00099 evaluateFields(typename Traits::EvalData workset)
00100 {
00101   typedef Intrepid::FunctionSpaceTools FST;
00102   bool bValidRegion = true;
00103   double invEffMass = 1.0; 
00104 
00105   if(bOnlyInQuantumBlocks)
00106     bValidRegion = materialDB->getElementBlockParam<bool>(workset.EBName,"quantum",false);
00107   
00108   // Put loops inside the if branches for speed
00109   if(bValidRegion)
00110   {
00111     if ( potentialType == "From State" || potentialType == "String Formula" || potentialType == "From Aux Data Vector")
00112     {
00113       if ( (numDims == 1) && (oxideWidth > 0.0) )   // 1D MOSCapacitor 
00114       {
00115         for (std::size_t cell = 0; cell < workset.numCells; ++cell) 
00116         { 
00117           for (std::size_t qp = 0; qp < numQPs; ++qp) 
00118           {
00119             invEffMass = hbar2_over_2m0 * getInvEffMass1DMosCap(&coordVec(cell,qp,0));
00120             for (std::size_t dim = 0; dim < numDims; ++dim)
00121               psiGradWithMass(cell,qp,dim) = invEffMass * psiGrad(cell,qp,dim);
00122           }  
00123         }    
00124       }
00125       
00126       // Effective mass depends on the wafer orientation and growth direction.
00127       // Assume SiO2/Si interface is parallel to the [100] plane and consider only the 
00128       // Delta2 Valleys whose principal axis is perpendicular to the SiO2/Si interface. 
00129       // (need to include Delta4-band for high temperatures > 50 K).
00130 
00131       else  // General case
00132       {
00133         double ml, mt;
00134         
00135         const std::string& matrlCategory = materialDB->getElementBlockParam<std::string>(workset.EBName,"Category","");
00136 
00137         // obtain ml and mt
00138         if (matrlCategory == "Semiconductor") 
00139         {
00140           const std::string& condBandMinVal = materialDB->getElementBlockParam<std::string>(workset.EBName,"Conduction Band Minimum");
00141           ml = materialDB->getElementBlockParam<double>(workset.EBName,"Longitudinal Electron Effective Mass");
00142           mt = materialDB->getElementBlockParam<double>(workset.EBName,"Transverse Electron Effective Mass");
00143     
00144           if ((condBandMinVal == "Gamma Valley") && (fabs(ml-mt) > 1e-10))
00145           {
00146             TEUCHOS_TEST_FOR_EXCEPTION (true, std::logic_error, "Gamma Valley's longitudinal and "
00147               << "transverse electron effective mass must be equal ! "
00148               << "Please check the values in materials.xml" << std::endl);
00149           }
00150         }      
00151 
00152         else if (matrlCategory == "Insulator")
00153         {
00154           ml = materialDB->getElementBlockParam<double>(workset.EBName,"Longitudinal Electron Effective Mass");
00155           mt = materialDB->getElementBlockParam<double>(workset.EBName,"Transverse Electron Effective Mass");
00156           if (fabs(ml-mt) > 1e-10) 
00157           {
00158             TEUCHOS_TEST_FOR_EXCEPTION (true, std::logic_error, "Insulator's longitudinal and "
00159               << "transverse electron effective mass must be equal ! "
00160               << "Please check the values in materials.xml" << std::endl);
00161     }
00162   }
00163 
00164   else {
00165     // Default releative effective masses == 1.0 if matrl category is not recognized.
00166     // Perhaps we should throw an error here instead?
00167     ml = mt = 1.0;
00168   }
00169         
00170         // calculate psiGradWithMass (good for diagonal effective mass tensor !)
00171         for (std::size_t cell = 0; cell < workset.numCells; ++cell) 
00172         { 
00173           for (std::size_t qp = 0; qp < numQPs; ++qp) 
00174           {
00175             for (std::size_t dim = 0; dim < numDims; ++dim)
00176             {
00177               if (dim == numDims-1)
00178                 invEffMass = hbar2_over_2m0 / ml;
00179               else
00180                 invEffMass = hbar2_over_2m0 / mt;
00181               psiGradWithMass(cell,qp,dim) = invEffMass * psiGrad(cell,qp,dim);
00182             }  
00183           }  
00184         }    
00185                
00186       }  // end of General case
00187       
00188     }  // end of if ( potentialType == "From State" || ... )
00189 
00190 
00191     // For potentialType == Finite Wall 
00192     else if ( potentialType == "Finite Wall" ) 
00193     {
00194       for (std::size_t cell = 0; cell < workset.numCells; ++cell) 
00195       { 
00196         for (std::size_t qp = 0; qp < numQPs; ++qp) 
00197         {
00198           invEffMass = hbar2_over_2m0 * getInvEffMassFiniteWall(&coordVec(cell,qp,0));
00199           for (std::size_t dim = 0; dim < numDims; ++dim)
00200             psiGradWithMass(cell,qp,dim) = invEffMass * psiGrad(cell,qp,dim);
00201         }  
00202       }    
00203     }  
00204     
00205     // For other potentialType
00206     else
00207     {
00208       for (std::size_t cell = 0; cell < workset.numCells; ++cell) 
00209         for (std::size_t qp = 0; qp < numQPs; ++qp) 
00210           for (std::size_t dim = 0; dim < numDims; ++dim)
00211             psiGradWithMass(cell,qp,dim) = hbar2_over_2m0 * psiGrad(cell,qp,dim);
00212     }    
00213 
00214     //Kinetic term: add integral( hbar^2/2m * Grad(psi) * Grad(BF)dV ) to residual
00215     FST::integrate<ScalarT>(psiResidual, psiGradWithMass, wGradBF, Intrepid::COMP_CPP, false); // "false" overwrites
00216   
00217     //Potential term: add integral( psi * V * BF dV ) to residual
00218     if (havePotential) {
00219       FST::scalarMultiplyDataData<ScalarT> (psiV, V, psi);
00220       FST::integrate<ScalarT>(psiResidual, psiV, wBF, Intrepid::COMP_CPP, true); // "true" sums into
00221     }
00222 
00223     //**Note: I think this should always be used with enableTransient = True
00224     //psiDot term (to use loca): add integral( psi_dot * BF dV ) to residual
00225     if (workset.transientTerms && enableTransient) 
00226       FST::integrate<ScalarT>(psiResidual, psiDot, wBF, Intrepid::COMP_CPP, true); // "true" sums into
00227       
00228   }  // end of if(bValidRegion)
00229   
00230   else 
00231   { 
00232     // Invalid region (don't perform calc here - evectors should all be zero)
00233     // So, set psiDot term to zero and set psi term (H) = Identity
00234 
00235     //This doesn't work - results in NaN error
00236     /*for (std::size_t cell=0; cell < workset.numCells; ++cell) 
00237     {
00238       for (std::size_t qp=0; qp < numQPs; ++qp)
00239         psiResidual(cell, qp) = 1.0*psi(cell,qp);
00240     }*/
00241 
00242     //Potential term: add integral( psi * V * BF dV ) to residual
00243     if (havePotential) 
00244     {
00245       for (std::size_t cell = 0; cell < workset.numCells; ++cell)
00246         for (std::size_t qp = 0; qp < numQPs; ++qp)
00247           V_barrier(cell,qp) = 100.0;
00248           
00249       FST::scalarMultiplyDataData<ScalarT> (psiV, V_barrier, psi);
00250       // FST::scalarMultiplyDataData<ScalarT> (psiV, V, psi);
00251       FST::integrate<ScalarT>(psiResidual, psiV, wBF, Intrepid::COMP_CPP, false); // "false" overwrites
00252     }
00253 
00254 
00255     //POSSIBLE NEW - same as valid region but use a very large potential...
00256     /*for (std::size_t cell = 0; cell < workset.numCells; ++cell)  {
00257       for (std::size_t qp = 0; qp < numQPs; ++qp) {
00258 
00259   V_barrier(cell,qp) = 1e+10;
00260 
00261   for (std::size_t dim = 0; dim < numDims; ++dim)
00262     psiGradWithMass(cell,qp,dim) = hbar2_over_2m0 * psiGrad(cell,qp,dim);
00263       }
00264     }
00265 
00266     //Kinetic term: add integral( hbar^2/2m * Grad(psi) * Grad(BF)dV ) to residual
00267     FST::integrate<ScalarT>(psiResidual, psiGradWithMass, wGradBF, Intrepid::COMP_CPP, false); // "false" overwrites
00268   
00269     //Potential term: add integral( psi * V * BF dV ) to residual
00270     FST::scalarMultiplyDataData<ScalarT> (psiV, V_barrier, psi);
00271     //FST::scalarMultiplyDataData<ScalarT> (psiV, V, psi);
00272     FST::integrate<ScalarT>(psiResidual, psiV, wBF, Intrepid::COMP_CPP, true); // "true" sums into
00273 
00274     // **Note: I think this should always be used with enableTransient = True
00275     //psiDot term (to use loca): add integral( psi_dot * BF dV ) to residual
00276     if (workset.transientTerms && enableTransient) 
00277       FST::integrate<ScalarT>(psiResidual, psiDot, wBF, Intrepid::COMP_CPP, true); // "true" sums into
00278     */
00279   }
00280 }
00281 
00282 
00283 // **********************************************************************
00284 template<typename EvalT, typename Traits> double
00285 QCAD::SchrodingerResid<EvalT, Traits>::getInvEffMassFiniteWall(const MeshScalarT* coord)
00286 {
00287   double effMass; 
00288   switch (numDims) 
00289   {
00290     case 1:  // 1D
00291     {
00292       if ( (coord[0] >= barrWidth) && (coord[0] <= (barrWidth+wellWidth)) )
00293         effMass = wellEffMass;  // well
00294       else
00295         effMass = barrEffMass;  // barrier
00296       break;  
00297     }
00298     case 2:  // 2D
00299     {
00300       if ( (coord[0] >= barrWidth) && (coord[0] <= (barrWidth+wellWidth)) &&
00301          (coord[1] >= barrWidth) && (coord[1] <= (barrWidth+wellWidth)) )
00302         effMass = wellEffMass;  
00303       else
00304         effMass = barrEffMass;
00305       break;   
00306     }
00307     case 3:  // 3D
00308     {
00309       if ( (coord[0] >= barrWidth) && (coord[0] <= (barrWidth+wellWidth)) &&
00310          (coord[1] >= barrWidth) && (coord[1] <= (barrWidth+wellWidth)) && 
00311          (coord[2] >= barrWidth) && (coord[2] <= (barrWidth+wellWidth)) )
00312          
00313         effMass = wellEffMass;   
00314       else
00315         effMass = barrEffMass;  
00316       break;    
00317     }
00318     default:
00319     {
00320       effMass = 0.0; // should never get here (suppresses uninitialized warning)
00321       TEUCHOS_TEST_FOR_EXCEPT( effMass == 0 );
00322     }
00323   }  
00324     
00325   return 1.0/effMass;
00326 }
00327 
00328 
00329 // **********************************************************************
00330 template<typename EvalT, typename Traits> double
00331 QCAD::SchrodingerResid<EvalT, Traits>::getInvEffMass1DMosCap(const MeshScalarT* coord)
00332 {
00333   double effMass; 
00334   
00335   // Oxide region
00336   if ((coord[0] >= 0) && (coord[0] <= oxideWidth))  
00337     effMass = materialDB->getMaterialParam<double>("SiliconDioxide","Longitudinal Electron Effective Mass",1.0);
00338     
00339   // Silicon region
00340   else if ((coord[0] > oxideWidth) && (coord[0] <= oxideWidth+siliconWidth))  
00341     effMass = materialDB->getMaterialParam<double>("Silicon","Longitudinal Electron Effective Mass",1.0);
00342    
00343   else
00344   {
00345    TEUCHOS_TEST_FOR_EXCEPTION (true, Teuchos::Exceptions::InvalidParameter,
00346        std::endl << "Error!  x-coord:" << coord[0] << "is outside the oxideWidth" << 
00347        " + siliconWidth range: " << oxideWidth + siliconWidth << "!"<< std::endl);
00348   }
00349   
00350   return 1.0/effMass;
00351 }
00352 
00353 
00354 // **********************************************************************

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