Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions Source/FerroX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,9 @@ AMREX_GPU_MANAGED amrex::GpuArray<amrex::Real, AMREX_SPACEDIM> FerroX::FE_hi;
AMREX_GPU_MANAGED amrex::GpuArray<amrex::Real, AMREX_SPACEDIM> FerroX::SC_hi;
AMREX_GPU_MANAGED amrex::GpuArray<amrex::Real, AMREX_SPACEDIM> FerroX::Channel_hi;
AMREX_GPU_MANAGED amrex::GpuArray<amrex::Real, AMREX_SPACEDIM> FerroX::Channel_lo;
AMREX_GPU_MANAGED amrex::GpuArray<amrex::Real, AMREX_SPACEDIM> FerroX::Metal_hi;
AMREX_GPU_MANAGED amrex::GpuArray<amrex::Real, AMREX_SPACEDIM> FerroX::Metal_lo;


// material parameters
AMREX_GPU_MANAGED amrex::Real FerroX::epsilon_0;
Expand All @@ -184,6 +187,9 @@ AMREX_GPU_MANAGED amrex::Real FerroX::epsilonX_fe_tphase;
AMREX_GPU_MANAGED amrex::Real FerroX::epsilonZ_fe;
AMREX_GPU_MANAGED amrex::Real FerroX::epsilon_de;
AMREX_GPU_MANAGED amrex::Real FerroX::epsilon_si;
AMREX_GPU_MANAGED amrex::Real FerroX::epsilon_metal;
AMREX_GPU_MANAGED amrex::Real FerroX::metal_screening_length;
AMREX_GPU_MANAGED amrex::Real FerroX::metal_thickness;
AMREX_GPU_MANAGED amrex::Real FerroX::alpha; // alpha = 2*alpha_1
AMREX_GPU_MANAGED amrex::Real FerroX::beta; // beta = 4*alpha_11
AMREX_GPU_MANAGED amrex::Real FerroX::gamma; // gamma = 6*alpha_111
Expand Down Expand Up @@ -284,6 +290,16 @@ void InitializeFerroXNamespace(const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM
pp.get("epsilonZ_fe",epsilonZ_fe);// epsilon_r for FE
pp.get("epsilon_de",epsilon_de);// epsilon_r for DE
pp.get("epsilon_si",epsilon_si);// epsilon_r for SC

epsilon_metal = 1.;
pp.query("epsilon_metal",epsilon_metal);// epsilon_r for metal

metal_screening_length = 1.e5;
pp.query("metal_screening_length",metal_screening_length);// metal_screening_length

metal_thickness = 0.;
pp.query("metal_thickness",metal_thickness);// metal_thickness

pp.get("alpha",alpha);
pp.get("beta",beta);
pp.get("gamma",FerroX::gamma);
Expand Down Expand Up @@ -415,6 +431,18 @@ void InitializeFerroXNamespace(const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM
}
}

if (pp.queryarr("Metal_lo",temp)) {
for (int i=0; i<AMREX_SPACEDIM; ++i) {
Metal_lo[i] = temp[i];
}
}

if (pp.queryarr("Metal_hi",temp)) {
for (int i=0; i<AMREX_SPACEDIM; ++i) {
Metal_hi[i] = temp[i];
}
}

if (pp.queryarr("t_phase_lo",temp)) {
for (int i=0; i<AMREX_SPACEDIM; ++i) {
t_phase_lo[i] = temp[i];
Expand Down
6 changes: 6 additions & 0 deletions Source/FerroX_namespace.H
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ namespace FerroX {
extern AMREX_GPU_MANAGED amrex::GpuArray<amrex::Real, AMREX_SPACEDIM> SC_hi;
extern AMREX_GPU_MANAGED amrex::GpuArray<amrex::Real, AMREX_SPACEDIM> Channel_hi;
extern AMREX_GPU_MANAGED amrex::GpuArray<amrex::Real, AMREX_SPACEDIM> Channel_lo;
extern AMREX_GPU_MANAGED amrex::GpuArray<amrex::Real, AMREX_SPACEDIM> Metal_hi;
extern AMREX_GPU_MANAGED amrex::GpuArray<amrex::Real, AMREX_SPACEDIM> Metal_lo;


// material parameters
extern AMREX_GPU_MANAGED amrex::Real epsilon_0;
Expand All @@ -23,6 +26,9 @@ namespace FerroX {
extern AMREX_GPU_MANAGED amrex::Real epsilonZ_fe;
extern AMREX_GPU_MANAGED amrex::Real epsilon_de;
extern AMREX_GPU_MANAGED amrex::Real epsilon_si;
extern AMREX_GPU_MANAGED amrex::Real epsilon_metal;
extern AMREX_GPU_MANAGED amrex::Real metal_screening_length;
extern AMREX_GPU_MANAGED amrex::Real metal_thickness;
extern AMREX_GPU_MANAGED amrex::Real alpha; // alpha = 2*alpha_1
extern AMREX_GPU_MANAGED amrex::Real beta; // beta = 4*alpha_11
extern AMREX_GPU_MANAGED amrex::Real gamma; // gamma = 6*alpha_111
Expand Down
12 changes: 11 additions & 1 deletion Source/Solver/ChargeDensity.H
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,19 @@
using namespace amrex;
using namespace FerroX;


void ComputeRho(MultiFab& PoissonPhi,
Array<MultiFab, AMREX_SPACEDIM> &P_old,
MultiFab& rho,
MultiFab& e_den,
MultiFab& p_den,
const MultiFab& MaterialMask);
const MultiFab& MaterialMask,
const Geometry& geom,
const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& prob_lo,
const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& prob_hi);

void Compute_P_Sum(const std::array<MultiFab, AMREX_SPACEDIM>& P, Real& sum);

void Compute_P_index_Sum(const MultiFab& MaterialMask, Real& count);

void Compute_P_av(const std::array<MultiFab, AMREX_SPACEDIM>& P, Real& sum, const MultiFab& MaterialMask, Real& count, Real &P_av_z);
191 changes: 159 additions & 32 deletions Source/Solver/ChargeDensity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,51 @@

// Compute rho in SC region for given phi
void ComputeRho(MultiFab& PoissonPhi,
Array<MultiFab, AMREX_SPACEDIM> &P_old,
MultiFab& rho,
MultiFab& e_den,
MultiFab& p_den,
const MultiFab& MaterialMask)
const MultiFab& MaterialMask,
const Geometry& geom,
const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& prob_lo,
const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& prob_hi)
{
// Calculate average Pr. We take the average only over the FE region
Real average_P_r = 0.;
Real total_P_r = 0.;
Real FE_index_counter = 0.;

Compute_P_av(P_old, total_P_r, MaterialMask, FE_index_counter, average_P_r);

//Calculate integrated electrode charge (Qe) based on eq 13 of https://pubs.aip.org/aip/jap/article/44/8/3379/6486/Depolarization-fields-in-thin-ferroelectric-films
Real FE_thickness = FE_hi[2] - FE_lo[2];
Real coth = (exp(2.*metal_thickness/metal_screening_length) + 1.0) / (exp(2.*metal_thickness/metal_screening_length) - 1.0);
Real csch = (2.*exp(metal_thickness/metal_screening_length)) / (exp(2.*metal_thickness/metal_screening_length) - 1.0);
Real numerator = 0.5 * FE_thickness * average_P_r / epsilonX_fe;
Real denominator = metal_screening_length/epsilon_de*(coth - csch) + FE_thickness/(2.*epsilonX_fe);
Real Qe = -numerator/denominator;

amrex::Print() << "ChargeDen average_P_r = " << average_P_r << "\n";
amrex::Print() << "ChargeDen numerator = " << numerator << "\n";
amrex::Print() << "ChargeDen denominator = " << denominator << "\n";
amrex::Print() << "ChargeDen Qe = " << Qe << "\n";

// loop over boxes
for (MFIter mfi(PoissonPhi); mfi.isValid(); ++mfi)
{
const Box& bx = mfi.validbox();

// extract dx from the geometry object
GpuArray<Real,AMREX_SPACEDIM> dx = geom.CellSizeArray();

// Calculate charge density from Phi, Nc, Nv, Ec, and Ev
MultiFab acceptor_den(rho.boxArray(), rho.DistributionMap(), 1, 0);
MultiFab donor_den(rho.boxArray(), rho.DistributionMap(), 1, 0);

const Array4<Real> &pOld_p = P_old[0].array(mfi);
const Array4<Real> &pOld_q = P_old[1].array(mfi);
const Array4<Real> &pOld_r = P_old[2].array(mfi);

const Array4<Real>& hole_den_arr = p_den.array(mfi);
const Array4<Real>& e_den_arr = e_den.array(mfi);
const Array4<Real>& charge_den_arr = rho.array(mfi);
Expand All @@ -24,45 +55,70 @@ void ComputeRho(MultiFab& PoissonPhi,
const Array4<Real>& donor_den_arr = donor_den.array(mfi);
const Array4<Real const>& mask = MaterialMask.array(mfi);


amrex::ParallelFor( bx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
{

if (mask(i,j,k) >= 2.0) {
Real z = prob_lo[2] + (k+0.5) * dx[2];
Real z_metal = 0.;

if(use_Fermi_Dirac == 1){
//Fermi-Dirac
Real eta_n = q*(phi(i,j,k) - Ec)/(kb*T);
Real nu_n = std::pow(eta_n, 4.0) + 50.0 + 33.6 * eta_n * (1 - 0.68 * exp(-0.17 * std::pow((eta_n + 1), 2)));
Real xi_n = 3.0 * sqrt(3.14)/(4.0 * std::pow(nu_n, 3/8));
Real FD_half_n = std::pow(exp(-eta_n) + xi_n, -1.0);

e_den_arr(i,j,k) = 2.0/sqrt(3.14)*Nc*FD_half_n;

Real eta_p = q*(Ev - phi(i,j,k))/(kb*T);
Real nu_p = std::pow(eta_p, 4.0) + 50.0 + 33.6 * eta_p * (1 - 0.68 * exp(-0.17 * std::pow((eta_p + 1), 2)));
Real xi_p = 3.0 * sqrt(3.14)/(4.0 * std::pow(nu_p, 3/8));
Real FD_half_p = std::pow(exp(-eta_p) + xi_p, -1.0);
if (mask(i,j,k) >= 2.0) {

hole_den_arr(i,j,k) = 2.0/sqrt(3.14)*Nv*FD_half_p;
} else {
//Maxwell-Boltzmann
Real n_0 = intrinsic_carrier_concentration;
Real p_0 = intrinsic_carrier_concentration;
hole_den_arr(i,j,k) = n_0*exp(-(q*phi(i,j,k))/(kb*T));
e_den_arr(i,j,k) = p_0*exp(q*phi(i,j,k)/(kb*T));
}

//If in channel, set acceptor doping, else (Source/Drain) set donor doping
if (mask(i,j,k) == 3.0) {
if (mask(i,j,k) == 4.0) { //Metal

//if(z <= FE_lo[2]){
// z_metal = std::abs(FE_lo[2] - (k + 0.5) * dx[2]);
//} else if (z >= FE_hi[2]){
// z_metal = std::abs((k + 0.5) * dx[2] - FE_hi[2]);
//}
// // amrex::Print() << "Qe = " << Qe << "\n";
//charge_den_arr(i,j,k) = Qe/metal_screening_length*exp(-z_metal/metal_screening_length);

//Treat Metal as SC
Real n_0 = intrinsic_carrier_concentration;
Real p_0 = intrinsic_carrier_concentration;
hole_den_arr(i,j,k) = n_0*exp(-(q*phi(i,j,k))/(kb*T));
e_den_arr(i,j,k) = p_0*exp(q*phi(i,j,k)/(kb*T));
acceptor_den_arr(i,j,k) = acceptor_doping;
donor_den_arr(i,j,k) = 0.0;
} else { // Source / Drain
acceptor_den_arr(i,j,k) = 0.0;
donor_den_arr(i,j,k) = donor_doping;
}
charge_den_arr(i,j,k) = q*(hole_den_arr(i,j,k) - e_den_arr(i,j,k) - acceptor_den_arr(i,j,k) + donor_den_arr(i,j,k));

} else {

charge_den_arr(i,j,k) = q*(hole_den_arr(i,j,k) - e_den_arr(i,j,k) - acceptor_den_arr(i,j,k) + donor_den_arr(i,j,k));
if(use_Fermi_Dirac == 1){
//Fermi-Dirac
Real eta_n = q*(phi(i,j,k) - Ec)/(kb*T);
Real nu_n = std::pow(eta_n, 4.0) + 50.0 + 33.6 * eta_n * (1 - 0.68 * exp(-0.17 * std::pow((eta_n + 1), 2)));
Real xi_n = 3.0 * sqrt(3.14)/(4.0 * std::pow(nu_n, 3/8));
Real FD_half_n = std::pow(exp(-eta_n) + xi_n, -1.0);

e_den_arr(i,j,k) = 2.0/sqrt(3.14)*Nc*FD_half_n;

Real eta_p = q*(Ev - phi(i,j,k))/(kb*T);
Real nu_p = std::pow(eta_p, 4.0) + 50.0 + 33.6 * eta_p * (1 - 0.68 * exp(-0.17 * std::pow((eta_p + 1), 2)));
Real xi_p = 3.0 * sqrt(3.14)/(4.0 * std::pow(nu_p, 3/8));
Real FD_half_p = std::pow(exp(-eta_p) + xi_p, -1.0);

hole_den_arr(i,j,k) = 2.0/sqrt(3.14)*Nv*FD_half_p;
} else {
//Maxwell-Boltzmann
Real n_0 = intrinsic_carrier_concentration;
Real p_0 = intrinsic_carrier_concentration;
hole_den_arr(i,j,k) = n_0*exp(-(q*phi(i,j,k))/(kb*T));
e_den_arr(i,j,k) = p_0*exp(q*phi(i,j,k)/(kb*T));
}

//If in channel, set acceptor doping, else (Source/Drain) set donor doping
if (mask(i,j,k) == 3.0) {
acceptor_den_arr(i,j,k) = acceptor_doping;
donor_den_arr(i,j,k) = 0.0;
} else if (mask(i,j,k) == 2.0){ // Source / Drain
acceptor_den_arr(i,j,k) = 0.0;
donor_den_arr(i,j,k) = donor_doping;
}

charge_den_arr(i,j,k) = q*(hole_den_arr(i,j,k) - e_den_arr(i,j,k) - acceptor_den_arr(i,j,k) + donor_den_arr(i,j,k));
}

} else {

Expand All @@ -73,3 +129,74 @@ void ComputeRho(MultiFab& PoissonPhi,
}
}

void Compute_P_Sum(const std::array<MultiFab, AMREX_SPACEDIM>& P, Real& sum)
{

// Initialize to zero
sum = 0.;

ReduceOps<ReduceOpSum> reduce_op;

ReduceData<Real> reduce_data(reduce_op);
using ReduceTuple = typename decltype(reduce_data)::Type;

for (MFIter mfi(P[2],TilingIfNotGPU()); mfi.isValid(); ++mfi)
{
const Box& bx = mfi.tilebox();
const Box& bx_grid = mfi.validbox();

auto const& fab = P[2].array(mfi);

reduce_op.eval(bx, reduce_data,
[=] AMREX_GPU_DEVICE (int i, int j, int k) -> ReduceTuple
{
return {fab(i,j,k)};
});
}

sum = amrex::get<0>(reduce_data.value());
ParallelDescriptor::ReduceRealSum(sum);
}


void Compute_P_index_Sum(const MultiFab& MaterialMask, Real& count)
{

// Initialize to zero
count = 0.;

ReduceOps<ReduceOpSum> reduce_op;

ReduceData<Real> reduce_data(reduce_op);
using ReduceTuple = typename decltype(reduce_data)::Type;

for (MFIter mfi(MaterialMask, TilingIfNotGPU()); mfi.isValid(); ++mfi)
{
const Box& bx = mfi.tilebox();
const Box& bx_grid = mfi.validbox();

auto const& fab = MaterialMask.array(mfi);

reduce_op.eval(bx, reduce_data,
[=] AMREX_GPU_DEVICE (int i, int j, int k) -> ReduceTuple
{
if(fab(i,j,k) == 0.) {
return {1.};
} else {
return {0.};
}

});
}

count = amrex::get<0>(reduce_data.value());
ParallelDescriptor::ReduceRealSum(count);
}

void Compute_P_av(const std::array<MultiFab, AMREX_SPACEDIM>& P, Real& sum, const MultiFab& MaterialMask, Real& count, Real& P_av_z)
{
Compute_P_Sum(P, sum);
Compute_P_index_Sum(MaterialMask, count);
P_av_z = sum/count;
}

10 changes: 6 additions & 4 deletions Source/Solver/ElectrostaticSolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ void dF_dPhi(MultiFab& alpha_cc,
PoissonPhi_plus_delta.plus(delta, 0, 1, 0);

// Calculate rho from Phi in SC region
ComputeRho(PoissonPhi_plus_delta, rho, e_den, p_den, MaterialMask);

ComputeRho(PoissonPhi_plus_delta, P_old, rho, e_den, p_den, MaterialMask, geom, prob_lo, prob_hi);
//Compute RHS of Poisson equation
ComputePoissonRHS(PoissonRHS_phi_plus_delta, P_old, rho, MaterialMask, angle_alpha, angle_beta, angle_theta, geom);

Expand Down Expand Up @@ -231,7 +231,7 @@ void InitializePermittivity(std::array<std::array<amrex::LinOpBCType,AMREX_SPACE

Real x = prob_lo[0] + (i+0.5) * dx[0];
Real y = prob_lo[1] + (j+0.5) * dx[1];
Real z = prob_lo[1] + (k+0.5) * dx[2];
Real z = prob_lo[2] + (k+0.5) * dx[2];

if(mask(i,j,k) == 0.0) {
beta(i,j,k) = epsilonX_fe * epsilon_0; //FE layer
Expand All @@ -242,8 +242,10 @@ void InitializePermittivity(std::array<std::array<amrex::LinOpBCType,AMREX_SPACE
}
} else if(mask(i,j,k) == 1.0) {
beta(i,j,k) = epsilon_de * epsilon_0; //DE layer
} else if (mask(i,j,k) >= 2.0){
} else if (mask(i,j,k) == 2.0 || mask(i,j,k) == 3.0){
beta(i,j,k) = epsilon_si * epsilon_0; //SC layer
} else if (mask(i,j,k) == 4.0){
beta(i,j,k) = epsilon_metal * epsilon_0; //Metal layer
} else {
beta(i,j,k) = epsilon_de * epsilon_0; //Spacer is same as DE
}
Expand Down
Loading