// ATC Headers #include "ExtrinsicModelDriftDiffusion.h" #include "ATC_Error.h" #include "FieldEulerIntegrator.h" #include "ATC_Coupling.h" #include "LammpsInterface.h" #include "PrescribedDataManager.h" #include "PhysicsModel.h" #include "LinearSolver.h" #include "PoissonSolver.h" #include "SchrodingerSolver.h" // timer #include "Utility.h" #include using ATC_Utility::to_string; using std::cout; using std::string; using std::set; using std::pair; using std::vector; namespace ATC { //-------------------------------------------------------- //-------------------------------------------------------- // Class ExtrinsicModelDriftDiffusion //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- ExtrinsicModelDriftDiffusion::ExtrinsicModelDriftDiffusion (ExtrinsicModelManager * modelManager, ExtrinsicModelType modelType, string matFileName) : ExtrinsicModelTwoTemperature(modelManager,modelType,matFileName), continuityIntegrator_(NULL), poissonSolverType_(DIRECT), // ITERATIVE | DIRECT poissonSolver_(NULL), baseSize_(0), electronDensityEqn_(ELECTRON_CONTINUITY), fluxUpdateFreq_(1), schrodingerSolverType_(DIRECT), // ITERATIVE | DIRECT schrodingerSolver_(NULL), schrodingerPoissonMgr_(), schrodingerPoissonSolver_(NULL), maxConsistencyIter_(0), maxConstraintIter_(1), safe_dEf_(0.1), Ef_shift_(0.0), oneD_(false), oneDcoor_(0), oneDconserve_(0) { // delete base class's version of the physics model if (physicsModel_) delete physicsModel_; if (modelType == DRIFT_DIFFUSION_EQUILIBRIUM) { physicsModel_ = new PhysicsModelDriftDiffusionEquilibrium(matFileName); electronDensityEqn_ = ELECTRON_EQUILIBRIUM; } else if (modelType == DRIFT_DIFFUSION_SCHRODINGER) { physicsModel_ = new PhysicsModelDriftDiffusionSchrodinger(matFileName); electronDensityEqn_ = ELECTRON_SCHRODINGER; maxConsistencyIter_ = 1; } else if (modelType == DRIFT_DIFFUSION_SCHRODINGER_SLICE) { physicsModel_ = new PhysicsModelDriftDiffusionSchrodingerSlice(matFileName); electronDensityEqn_ = ELECTRON_SCHRODINGER; maxConsistencyIter_ = 1; } else { physicsModel_ = new PhysicsModelDriftDiffusion(matFileName); } atc_->useConsistentMassMatrix_(ELECTRON_DENSITY) = true; rhsMaskIntrinsic_(ELECTRON_TEMPERATURE,SOURCE) = true; //atc_->fieldMask_(ELECTRON_TEMPERATURE,EXTRINSIC_SOURCE) = true; } //-------------------------------------------------------- // Destructor //-------------------------------------------------------- ExtrinsicModelDriftDiffusion::~ExtrinsicModelDriftDiffusion() { if(continuityIntegrator_) delete continuityIntegrator_; if(poissonSolver_) delete poissonSolver_; if(schrodingerSolver_) delete schrodingerSolver_; if(schrodingerPoissonSolver_) delete schrodingerPoissonSolver_; } //-------------------------------------------------------- // modify //-------------------------------------------------------- bool ExtrinsicModelDriftDiffusion::modify(int narg, char **arg) { bool match = false; if (!match) { match = ExtrinsicModelTwoTemperature::modify(narg, arg); } return match; } //-------------------------------------------------------- // initialize //-------------------------------------------------------- void ExtrinsicModelDriftDiffusion::initialize() { // xTTM sets rhsMaskIntrinsic_ ExtrinsicModelTwoTemperature::initialize(); nNodes_ = atc_->num_nodes(); rhs_[ELECTRON_DENSITY].reset(nNodes_,1); rhs_[ELECTRIC_POTENTIAL].reset(nNodes_,1); // set up electron continuity integrator Array2D rhsMask(NUM_TOTAL_FIELDS,NUM_FLUX); rhsMask = false; for (int i = 0; i < NUM_FLUX; i++) { rhsMask(ELECTRON_DENSITY,i) = atc_->fieldMask_(ELECTRON_DENSITY,i); } // need to create the bcs for the solver to configure properly atc_->set_fixed_nodes(); if (continuityIntegrator_) delete continuityIntegrator_; if (electronTimeIntegration_ == TimeIntegrator::IMPLICIT) { continuityIntegrator_ = new FieldImplicitEulerIntegrator(ELECTRON_DENSITY, physicsModel_, atc_->feEngine_, atc_, rhsMask); } else { continuityIntegrator_ = new FieldExplicitEulerIntegrator(ELECTRON_DENSITY, physicsModel_, atc_->feEngine_, atc_, rhsMask); } atc_->compute_mass_matrix(ELECTRON_DENSITY,physicsModel_); //(atc_->consistentMassMats_[ELECTRON_DENSITY].quantity()).print("PHYS MASS MAT"); //DENS_MAT temp = atc_->consistentMassInverse_ - atc_->consistentMassMatInv_[ELECTRON_DENSITY]; //temp.print("DIFF In MATS"); // set up poisson solver rhsMask = false; for (int i = 0; i < NUM_FLUX; i++) { rhsMask(ELECTRIC_POTENTIAL,i) = atc_->fieldMask_(ELECTRIC_POTENTIAL,i); } int type = ATC::LinearSolver::ITERATIVE_SOLVE_SYMMETRIC; if (poissonSolverType_ == DIRECT) { type = ATC::LinearSolver::DIRECT_SOLVE; } if (poissonSolver_) delete poissonSolver_; poissonSolver_ = new PoissonSolver(ELECTRIC_POTENTIAL, physicsModel_, atc_->feEngine_, atc_->prescribedDataMgr_, atc_, rhsMask,type, true); poissonSolver_->initialize(); // set up schrodinger solver if ( electronDensityEqn_ == ELECTRON_SCHRODINGER ) { if ( schrodingerSolver_ ) delete schrodingerSolver_; if ( oneD_ ) { EfHistory_.reset(oneDslices_.size(),2); schrodingerSolver_ = new SliceSchrodingerSolver(ELECTRON_DENSITY, physicsModel_, atc_->feEngine_, atc_->prescribedDataMgr_, atc_, oneDslices_,oneDdxs_); } else { schrodingerSolver_ = new SchrodingerSolver(ELECTRON_DENSITY, physicsModel_, atc_->feEngine_, atc_->prescribedDataMgr_, atc_); } schrodingerSolver_->initialize(); if ( schrodingerPoissonSolver_ ) delete schrodingerPoissonSolver_; schrodingerPoissonSolver_ = schrodingerPoissonMgr_.initialize( atc_, schrodingerSolver_, poissonSolver_, physicsModel_); } if (electronDensityEqn_ == ELECTRON_SCHRODINGER && !(atc_->is_initialized())) { ((atc_->fields())[ELECTRON_WAVEFUNCTION].set_quantity()).reset(nNodes_,1); ((atc_->fields())[ELECTRON_WAVEFUNCTIONS].set_quantity()).reset(nNodes_,nNodes_); ((atc_->fields())[ELECTRON_WAVEFUNCTION_ENERGIES].set_quantity()).reset(nNodes_,1); } #if 0 cout << " RHS MASK\n"; print_mask(rhsMask); #endif } //-------------------------------------------------------- // pre initial integration //-------------------------------------------------------- void ExtrinsicModelDriftDiffusion::pre_init_integrate() { double dt = atc_->lammpsInterface_->dt(); double time = atc_->time(); int step = atc_->step(); if (step % fluxUpdateFreq_ != 0) return; // set Dirchlet data atc_->set_fixed_nodes(); // set Neumann data (atc does not set these until post_final) atc_->set_sources(); // subcyle integration of fast electron variable/s double idt = dt/nsubcycle_; for (int i = 0; i < nsubcycle_ ; ++i) { if (electronDensityEqn_ == ELECTRON_CONTINUITY) { // update continuity eqn if (! atc_->prescribedDataMgr_->all_fixed(ELECTRON_DENSITY) ) continuityIntegrator_->update(idt,time,atc_->fields_,rhs_); atc_->set_fixed_nodes(); // solve poisson eqn for electric potential if (! atc_->prescribedDataMgr_->all_fixed(ELECTRIC_POTENTIAL) ) poissonSolver_->solve(atc_->fields(),rhs_); } else if (electronDensityEqn_ == ELECTRON_SCHRODINGER) { if ( (! atc_->prescribedDataMgr_->all_fixed(ELECTRON_DENSITY) ) || (! atc_->prescribedDataMgr_->all_fixed(ELECTRIC_POTENTIAL) ) ) schrodingerPoissonSolver_->solve(rhs_,fluxes_); } // update electron temperature if (! atc_->prescribedDataMgr_->all_fixed(ELECTRON_TEMPERATURE) && temperatureIntegrator_ ) { #ifdef ATC_VERBOSE ATC::LammpsInterface::instance()->stream_msg_once("start temperature integration...",true,false); #endif temperatureIntegrator_->update(idt,time,atc_->fields_,rhs_); #ifdef ATC_VERBOSE ATC::LammpsInterface::instance()->stream_msg_once(" done",false,true); #endif } atc_->set_fixed_nodes(); } } //-------------------------------------------------------- // set coupling source terms //------------------------------------------------------- void ExtrinsicModelDriftDiffusion::set_sources(FIELDS & fields, FIELDS & sources) { atc_->evaluate_rhs_integral(rhsMaskIntrinsic_, fields, sources, atc_->source_integration(), physicsModel_); } //-------------------------------------------------------- // output //-------------------------------------------------------- void ExtrinsicModelDriftDiffusion::output(OUTPUT_LIST & outputData) { #ifdef ATC_VERBOSE // ATC::LammpsInterface::instance()->print_msg_once("start output",true,false); #endif ExtrinsicModelTwoTemperature::output(outputData); // fields outputData["dot_electron_density"] = & (atc_->dot_field(ELECTRON_DENSITY)).set_quantity(); DENS_MAT & JE = rhs_[ELECTRON_TEMPERATURE].set_quantity(); double totalJouleHeating =JE.sum(); outputData["joule_heating"] = & JE; atc_->feEngine_->add_global("total_joule_heating",totalJouleHeating); Array2D rhsMask(NUM_FIELDS,NUM_FLUX); rhsMask = false; rhsMask(ELECTRON_DENSITY,FLUX) = true; rhsMask(ELECTRIC_POTENTIAL,FLUX) = true; atc_->compute_flux(rhsMask,atc_->fields_,fluxes_,physicsModel_); //(fluxes_[ELECTRON_DENSITY][0]).print("J_x"); outputData["electron_flux_x"] = & fluxes_[ELECTRON_DENSITY][0]; outputData["electron_flux_y"] = & fluxes_[ELECTRON_DENSITY][1]; outputData["electron_flux_z"] = & fluxes_[ELECTRON_DENSITY][2]; outputData["electric_field_x"] = & fluxes_[ELECTRIC_POTENTIAL][0]; outputData["electric_field_y"] = & fluxes_[ELECTRIC_POTENTIAL][1]; outputData["electric_field_z"] = & fluxes_[ELECTRIC_POTENTIAL][2]; if (electronDensityEqn_ == ELECTRON_SCHRODINGER ) { SPAR_MAT K; Array2D rhsMask(NUM_FIELDS,NUM_FLUX); rhsMask = false; rhsMask(ELECTRON_WAVEFUNCTION,FLUX) = true; pair row_col(ELECTRON_WAVEFUNCTION, ELECTRON_WAVEFUNCTION); atc_->feEngine_->compute_tangent_matrix( rhsMask, row_col, atc_->fields(), physicsModel_, atc_->element_to_material_map(), K); phiTotal_.reset(K.nRows(),1); const DIAG_MAT & inv_dV = (atc_->invNodeVolumes_).quantity(); for (int i = 0; i < K.nRows() ; i++) { phiTotal_(i,0) = 0.0; for (int j = 0; j < K.nCols() ; j++) { phiTotal_(i,0) += K(i,j); } phiTotal_(i,0) *= inv_dV(i,i); } outputData["V_total"] = & phiTotal_; } // globals double nSum = ((atc_->field(ELECTRON_DENSITY)).quantity()).col_sum(); atc_->feEngine_->add_global("total_electron_density",nSum); #ifdef ATC_VERBOSE // ATC::LammpsInterface::instance()->print_msg_once("... done",false,true); #endif } //-------------------------------------------------------- // size_vector //-------------------------------------------------------- int ExtrinsicModelDriftDiffusion::size_vector(int intrinsicSize) { int xSize = ExtrinsicModelTwoTemperature::size_vector(intrinsicSize); baseSize_ = intrinsicSize + xSize; xSize += 2; return xSize; } //-------------------------------------------------------- // compute_vector //-------------------------------------------------------- bool ExtrinsicModelDriftDiffusion::compute_vector(int n, double & value) { // output[1] = total electron density // output[2] = total joule heating bool match = ExtrinsicModelTwoTemperature::compute_vector(n,value); if (match) return match; if (n == baseSize_) { double nSum = ((atc_->field(ELECTRON_DENSITY)).quantity()).col_sum(); value = nSum; return true; } if (n == baseSize_+1) { DENS_MAT & JE = rhs_[ELECTRON_TEMPERATURE].set_quantity(); double totalJouleHeating =JE.sum(); value = totalJouleHeating; return true; } return false; } //-------------------------------------------------------- //-------------------------------------------------------- // Class ExtrinsicModelDriftDiffusionConvection //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- ExtrinsicModelDriftDiffusionConvection::ExtrinsicModelDriftDiffusionConvection (ExtrinsicModelManager * modelManager, ExtrinsicModelType modelType, string matFileName) : ExtrinsicModelDriftDiffusion(modelManager,modelType,matFileName), cddmPoissonSolver_(NULL), baseSize_(0) { // delete base class's version of the physics model if (physicsModel_) delete physicsModel_; if (modelType == CONVECTIVE_DRIFT_DIFFUSION_SCHRODINGER) { physicsModel_ = new PhysicsModelDriftDiffusionConvectionSchrodinger(matFileName); electronDensityEqn_ = ELECTRON_SCHRODINGER; } else { physicsModel_ = new PhysicsModelDriftDiffusionConvection(matFileName); } atc_->useConsistentMassMatrix_(ELECTRON_VELOCITY) = false; atc_->useConsistentMassMatrix_(ELECTRON_TEMPERATURE) = false; } //-------------------------------------------------------- // Destructor //-------------------------------------------------------- ExtrinsicModelDriftDiffusionConvection::~ExtrinsicModelDriftDiffusionConvection() { if (cddmPoissonSolver_) delete cddmPoissonSolver_; for (vector::const_iterator iter=velocitySolvers_.begin(); iter != velocitySolvers_.end(); iter++) if (*iter) delete *iter; } //-------------------------------------------------------- // initialize //-------------------------------------------------------- void ExtrinsicModelDriftDiffusionConvection::initialize() { ExtrinsicModelDriftDiffusion::initialize(); nNodes_ = atc_->num_nodes(); nsd_ = atc_->nsd(); rhs_[ELECTRON_VELOCITY].reset(nNodes_,nsd_); atc_->set_fixed_nodes(); // needed to correctly set BC data // initialize Poisson solver if (cddmPoissonSolver_) delete cddmPoissonSolver_; Array2D rhsMask(NUM_FIELDS,NUM_FLUX); rhsMask = false; rhsMask(ELECTRIC_POTENTIAL,FLUX) = true; pair row_col(ELECTRIC_POTENTIAL,ELECTRIC_POTENTIAL); SPAR_MAT stiffness; (atc_->feEngine_)->compute_tangent_matrix(rhsMask,row_col, atc_->fields(), physicsModel_, atc_->element_to_material_map(), stiffness); const BC_SET & bcs = (atc_->prescribedDataMgr_->bcs(ELECTRIC_POTENTIAL))[0]; cddmPoissonSolver_ = new LinearSolver(stiffness, bcs, poissonSolverType_, -1, true); // initialize velocity solver const BCS & velocityBcs = atc_->prescribedDataMgr_->bcs(ELECTRON_VELOCITY); DENS_MAT velocityRhs(nNodes_,nsd_); atc_->compute_mass_matrix(ELECTRON_VELOCITY,physicsModel_); SPAR_MAT & velocityMassMat = (atc_->consistentMassMats_[ELECTRON_VELOCITY]).set_quantity(); for (int i = 0; i < nsd_; i++ ) { LinearSolver * myVelocitySolver = new LinearSolver(velocityMassMat, velocityBcs[i], LinearSolver::AUTO_SOLVE, -1, true); velocitySolvers_.push_back(myVelocitySolver); } } //-------------------------------------------------------- // pre initial integration //-------------------------------------------------------- void ExtrinsicModelDriftDiffusionConvection::pre_init_integrate() { double dt = atc_->lammpsInterface_->dt(); double time = atc_->time(); int step = atc_->step(); if (step % fluxUpdateFreq_ != 0) return; // set Dirchlet data atc_->set_fixed_nodes(); // set Neumann data (atc does not set these until post_final) atc_->set_sources(); // subcyle integration of fast electron variable/s double idt = dt/nsubcycle_; for (int i = 0; i < nsubcycle_ ; ++i) { // update electron temperature mass matrix atc_->compute_mass_matrix(ELECTRON_VELOCITY,physicsModel_); // update electron velocity if (!(atc_->prescribedDataMgr_)->all_fixed(ELECTRON_VELOCITY)) { //const BCS & bcs // = atc_->prescribedDataMgr_->bcs(ELECTRON_VELOCITY); Array2D rhsMask(NUM_FIELDS,NUM_FLUX); rhsMask = false; rhsMask(ELECTRON_VELOCITY,SOURCE) = atc_->fieldMask_(ELECTRON_VELOCITY,SOURCE); rhsMask(ELECTRON_VELOCITY,FLUX) = atc_->fieldMask_(ELECTRON_VELOCITY,FLUX); rhsMask(ELECTRON_VELOCITY,OPEN_SOURCE) = atc_->fieldMask_(ELECTRON_VELOCITY,OPEN_SOURCE); FIELDS rhs; rhs[ELECTRON_VELOCITY].reset(nNodes_,nsd_); atc_->compute_rhs_vector(rhsMask, atc_->fields_, rhs, atc_->source_integration(), physicsModel_); const DENS_MAT & velocityRhs = rhs[ELECTRON_VELOCITY].quantity(); // add a solver for electron momentum DENS_MAT & velocity = (atc_->field(ELECTRON_VELOCITY)).set_quantity(); for (int j = 0; j < nsd_; ++j) { if (! atc_->prescribedDataMgr_->all_fixed(ELECTRON_VELOCITY,j) ) { if (atc_->useConsistentMassMatrix_(ELECTRON_VELOCITY)) { //#ifdef ATC_PRINT_DEBUGGING const SPAR_MAT & velocityMassMat = (atc_->consistentMassMats_[ELECTRON_VELOCITY]).quantity(); velocityMassMat.print("VMASS"); //#endif CLON_VEC v = column(velocity,j); const CLON_VEC r = column(velocityRhs,j); (velocitySolvers_[j])->solve(v,r); } else { //velocityRhs.print("VRHS"); //const DIAG_MAT & velocityMassMat = (atc_->massMats_[ELECTRON_VELOCITY]).quantity(); //velocityMassMat.print("MASS"); atc_->apply_inverse_mass_matrix(velocityRhs,velocity,ELECTRON_VELOCITY); } } } } //atc_->set_fixed_nodes(); if (electronDensityEqn_ == ELECTRON_CONTINUITY) { // update continuity eqn Array2D rhsMask(NUM_FIELDS,NUM_FLUX); rhsMask = false; rhsMask(ELECTRON_DENSITY,FLUX) = atc_->fieldMask_(ELECTRON_DENSITY,FLUX); rhsMask(ELECTRON_DENSITY,SOURCE) = atc_->fieldMask_(ELECTRON_DENSITY,SOURCE); rhsMask(ELECTRON_DENSITY,PRESCRIBED_SOURCE) = atc_->fieldMask_(ELECTRON_DENSITY,PRESCRIBED_SOURCE); FIELDS rhs; rhs[ELECTRON_DENSITY].reset(nNodes_,1); atc_->compute_rhs_vector(rhsMask, atc_->fields_, rhs, atc_->source_integration(), physicsModel_); if (! atc_->prescribedDataMgr_->all_fixed(ELECTRON_DENSITY) ) continuityIntegrator_->update(idt,time,atc_->fields_,rhs); atc_->set_fixed_nodes(); // solve poisson eqn for electric potential if (! atc_->prescribedDataMgr_->all_fixed(ELECTRIC_POTENTIAL) ) { //poissonSolver_->solve(atc_->fields_,rhs_); rhsMask = false; rhsMask(ELECTRIC_POTENTIAL,SOURCE) = atc_->fieldMask_(ELECTRIC_POTENTIAL,SOURCE); rhsMask(ELECTRIC_POTENTIAL,PRESCRIBED_SOURCE) = atc_->fieldMask_(ELECTRIC_POTENTIAL,PRESCRIBED_SOURCE); // FIELDS rhs; rhs[ELECTRIC_POTENTIAL].reset(nNodes_,1); atc_->compute_rhs_vector(rhsMask, atc_->fields_, rhs, atc_->source_integration(), physicsModel_); CLON_VEC x =column((atc_->field(ELECTRIC_POTENTIAL)).set_quantity(),0); const CLON_VEC r =column(rhs[ELECTRIC_POTENTIAL].quantity(),0); cddmPoissonSolver_->solve(x,r); } } else if (electronDensityEqn_ == ELECTRON_SCHRODINGER) { schrodingerPoissonSolver_->solve(rhs_,fluxes_); } atc_->set_fixed_nodes(); // update electron temperature mass matrix atc_->compute_mass_matrix(ELECTRON_TEMPERATURE,physicsModel_); // update electron temperature if (! atc_->prescribedDataMgr_->all_fixed(ELECTRON_TEMPERATURE) ) { //if (atc_->useConsistentMassMatrix_(ELECTRON_TEMPERATURE)) { temperatureIntegrator_->update(idt,time,atc_->fields_,rhs_); //} //else { // lumped mass matrix //} } atc_->set_fixed_nodes(); } } //-------------------------------------------------------- // output //-------------------------------------------------------- void ExtrinsicModelDriftDiffusionConvection::output(OUTPUT_LIST & outputData) { ExtrinsicModelDriftDiffusion::output(outputData); //FIELD jouleHeating(atc_->num_nodes(),1); //set_kinetic_energy_source(atc_->fields(),jouleHeating); outputData["joule_heating"] = & (atc_->extrinsic_source(TEMPERATURE)).set_quantity(); // globals DENS_MAT nodalKineticEnergy; compute_nodal_kinetic_energy(nodalKineticEnergy); double kineticEnergy = nodalKineticEnergy.sum(); atc_->feEngine_->add_global("total_electron_kinetic_energy",kineticEnergy); } //-------------------------------------------------------- // size_vector //-------------------------------------------------------- int ExtrinsicModelDriftDiffusionConvection::size_vector(int intrinsicSize) { int xSize = ExtrinsicModelDriftDiffusion::size_vector(intrinsicSize); baseSize_ = intrinsicSize + xSize; xSize += 1; return xSize; } //-------------------------------------------------------- // compute_vector //-------------------------------------------------------- bool ExtrinsicModelDriftDiffusionConvection::compute_vector(int n, double & value) { // output[1] = total electron kinetic energy bool match = ExtrinsicModelDriftDiffusion::compute_vector(n,value); if (match) return match; if (n == baseSize_) { DENS_MAT nodalKineticEnergy; compute_nodal_kinetic_energy(nodalKineticEnergy); value = nodalKineticEnergy.sum(); return true; } return false; } //-------------------------------------------------------- // compute_kinetic_energy //-------------------------------------------------------- void ExtrinsicModelDriftDiffusionConvection::compute_nodal_kinetic_energy(DENS_MAT & kineticEnergy) { DENS_MAT & velocity((atc_->field(ELECTRON_VELOCITY)).set_quantity()); SPAR_MAT & velocityMassMat = (atc_->consistentMassMats_[ELECTRON_VELOCITY]).set_quantity(); kineticEnergy.reset(nNodes_,1); for (int j = 0; j < nsd_; j++) { CLON_VEC myVelocity(velocity,CLONE_COL,j); DENS_MAT velocityMat(nNodes_,1); for (int i = 0; i < nNodes_; i++) velocityMat(i,0) = myVelocity(i); kineticEnergy += velocityMat.mult_by_element(myVelocity); } kineticEnergy = 0.5*velocityMassMat*kineticEnergy; } };