0

Premise

I am trying to solve a set of coupled PDEs that describes the diffusion of charged particles with different diffusion coefficients using FiPy. The ultimate goal is to obtain the concentration profile for both species and the electric field.

The geometry is an infinitely long cylinder with radius R. I want to use a non-uniform grid with more points close to the domain walls.

Charged particles diffuse from the center of the domain (left boundary) to the walls of the domain (right boundary). This translates to a Dirichlet boundary condition (B.C.) at the left boundary where both species concentration = 0, and a Neumann B.C. at the right boundary where species flux are 0 to describe radial symmetry. Because the charged species diffuse at different rates, there is an electric field that arises from the space charge. The electric field accelerates the slower species and decelerates the faster species proportional to the field magnitude.

P is the positively charged species concentration and N is negatively charged charged species concentration. E is the space charge electric field.

Issue

I can't seem to get a sensible solution from my code and I think it may be related on how I casted the gradient/divergence terms as a ConvectionTerm:

from fipy import *
import scipy.constants as constant
from fipy.tools import numerix
import numpy as np

## Defining physical constants
pi = constant.pi
m_argon = 6.6335e-26 # kg
k_b = constant.k # J/K
e_0 = constant.epsilon_0 # F/m
q_e = constant.elementary_charge # C
m_e = constant.electron_mass # kg
planck = constant.h

def char_diff_length(L,R):
    """Characteristic diffusion length in a cylinder.
    Used for determining the ambipolar diffusion coefficient.
    ref: https://doi.org/10.6028/jres.095.035"""
    a = (pi/L)**2
    b = (2.405/R)**2
    c = (a+b)**(1/2)
    return c

def L_Debye(ne,Te):
    """Electron Debye screening length given in m.
    ne is in #/m3, Te is in K."""
    if ne < 3.3e-5:
        ne = 3.3e-5
    return (((e_0*k_b*Te)/(ne*q_e**2)))**(1/2)

## Setting system parameters
# Operation parameters
Pressure = 1.e5 # ambient pressure Pa
T_g = 400. # background gas temperature K
n_g = Pressure/k_b/T_g # gas number density #/m3
Q_std = 300. # standard volumetric flowrate in sccm
T_e_0 = 11. # plasma temperature ratio T_e/T_g here assumed to be T_e = 0.5 eV and T_g = 500 K
n_e_0 = 1.e20 # electron density in bulk plasma #/m3

# Geometric parameters
R_b = 1.e-3 # radius cylinder m
L = 1.e-1 # length of cylinder m

# Transport parameters
D_ion = 4.16e-6 #m2/s ion diffusion, obtained from https://doi.org/10.1007/s12127-020-00258-z
mu_ion = D_ion*q_e/k_b/T_g # ion electrical mobility using Einstein relation

D_e = 100.68122*D_ion #m2/s electron diffusion
mu_e = D_e*q_e/k_b/T_g # electron electrical mobility using Einstein relation

Lambda = char_diff_length(L,R_b)
debyelength_e = L_Debye(n_e_0,T_g)
gamma = (Lambda/debyelength_e)**2
delta = D_ion/D_e



def d_j(rb,n): #sets the desired spatial steps for mesh
    dj = np.zeros(n)
    for j in range(n):
        dj[j] = 2*rb*(1 - j/n)/n
    return dj

#Initializing mesh
dj = d_j(1.,100) # 100 points
mesh = CylindricalGrid1D(dr = dj)

#Declaring cell variables
N = CellVariable(mesh=mesh, value = 1., hasOld = True, name = "electron density")
P = CellVariable(mesh=mesh, value = 1., hasOld = True, name = "ion density")
H = CellVariable(mesh=mesh, value = 0., hasOld = True, name = "electric field")

#Setting boundary conditions
N.constrain(0.,mesh.facesRight) # electron density = 0 at walls
P.constrain(0.,mesh.facesRight)# ion density = 0 at walls

H.constrain(0.,mesh.facesLeft) # electric field = 0 in the center
N.faceGrad.constrain([0.],mesh.facesLeft) # flux of electron = 0 in the center
P.faceGrad.constrain([0.],mesh.facesLeft) # flux of ion = 0 in the center

if __name__ == '__main__':
    viewer = Viewer(vars=(P,N))
    viewer.plot()

eqn1 = (TransientTerm(var=P) == DiffusionTerm(coeff=delta,var=P) 
                                - ConvectionTerm(coeff=[H.cellVolumeAverage,],var=P) 
                                - ConvectionTerm(coeff=[P.cellVolumeAverage,],var=H))
eqn2 = (TransientTerm(var=N) == DiffusionTerm(var=N) 
                                + (1/delta)*(ConvectionTerm(coeff=[H.cellVolumeAverage,],var=N)
                                            +ConvectionTerm(coeff=[N.cellVolumeAverage,],var=H)))
eqn3 = (TransientTerm(var=H) == gamma*(ConvectionTerm(coeff=[delta**2,],var=P) 
                                       - ConvectionTerm(coeff=[delta,],var=N) 
                                       - H*(delta*P.cellVolumeAverage + N.cellVolumeAverage)))
P.setValue(1.)
N.setValue(1.)
H.setValue(0.)
eqn1d = eqn1 & eqn2 & eqn3



timesteps = 1e-5
steps = 100
for i in range(steps):
    P.updateOld()
    N.updateOld()
    H.updateOld()
    res = 1e10
    sweep = 0
    while res > 1e-3 and sweep < 20:
        res = eqn1d.sweep(dt=timesteps)
        sweep += 1
    if __name__ == '__main__':
        viewer.plot()
nabz
  • 11
  • 1
  • welcome to [Stack Overflow.](https://stackoverflow.com/ "Stack Overflow")! Your question seems to be too broad for this site. In order that we may help you, we need a reproducible data set, minimal code that generates the error/problem, what you are getting and what you expect. Read [Where to Start](https://softwareengineering.meta.stackexchange.com/questions/6366/where-to-start/6367#6367), and [Minimal Reproducible Example](https://stackoverflow.com/help/minimal-reproducible-example "Minimal Reproducible Example") then edit your post. – itprorh66 Mar 30 '21 at 19:15
  • 1
    As author of the software in question, I disagree. This question provides a fully worked example and an adequate description of what they're looking for. @itprorh66: StackOverflow has a well-deserved reputation for being unwelcoming; knock it off. – jeguyer Mar 30 '21 at 20:09
  • What is the error you get? What is the non sensible result ? – TheTechRobo the Nerd Mar 31 '21 at 01:51
  • It wasn't an error. The solver ran but the solution did not make physical sense. – nabz Mar 31 '21 at 02:27

2 Answers2

0

Electric field is a vector, not a scalar.

H = CellVariable(rank=1, mesh=mesh, value = 0., hasOld = True, name = "electric field")

Correcting that should make converting the terms to FiPy clearer:

There's no reason to run the chain rule on the last term of eq1 or eq2; they're already in the canonical form for a FiPy ConvectionTerm. After the chain rule, they become, e.g., , neither of which is a form that FiPy likes. You could write those last two terms as explicit sources, but you shouldn't.

eqn1 = (TransientTerm(var=P) == DiffusionTerm(coeff=delta, var=P) 
                                - ConvectionTerm(coeff=H, var=P))
eqn2 = (TransientTerm(var=N) == DiffusionTerm(var=N) 
                                + (1/delta)*ConvectionTerm(coeff=H, var=N))

I don't really understand eq3. It looks sort of like an integration of the continuity equation? I don't see it on a quick scan of the Phelps paper you cite. Regardless, it's not in a form that FiPy is amenable to; you can write it, but it won't solve well. The terms on the right aren't ConvectionTerms, they're just gradients.

If you're going to be allowing charge separation and worrying about the Debye length, I think you should be solving Poisson's equation. Can you share where this equation comes from? We might be able to put it in a form that FiPy will be happier with.

jeguyer
  • 2,379
  • 1
  • 11
  • 15
  • Hi Jonathan, thanks for your response. I will follow your advice to keep the terms pre-chain rule. – nabz Mar 31 '21 at 00:12
0

eq3 is a modified Poisson's equation. I tried to follow the procedure outlined by Freeman where a time derivative is performed on the Poisson equation to substitute the species continuity equations. Freeman solved these equations using the Gear package which I can only assume is a package on Fortran. I followed his steps out of naivite because I am out of my depth with numerical methods.

I will try solving again with the Poisson equation in its standard form.

edit: I have changed the electric field H to a rank 1 tensor and I have modified eq3 as well as slightly changed the definition of gamma. Everything else is unchanged.

H = CellVariable(rank = 1, mesh=mesh, value = 0., hasOld = True, name = "electric field")

charlength = char_diff_length(L,R_b)
debyelength_e = L_Debye(n_e_0,T_g)
gamma = (debyelength_e/charlength)**2
delta = D_ion/D_e

eqn1 = (TransientTerm(var=P) == DiffusionTerm(coeff=delta,var=P) 
                                - ConvectionTerm(coeff=H,var=P))
eqn2 = (TransientTerm(var=N) == DiffusionTerm(var=N) 
                                + (1/delta)*ConvectionTerm(coeff=H,var=N))
eqn3 = (ConvectionTerm(coeff = gamma/delta, var=H) == ImplicitSourceTerm(var=P) 
                                - ImplicitSourceTerm(var=N))
P.setValue(1.)
N.setValue(1.)
H.setValue(0.)
eqn1d = eqn1 & eqn2 & eqn3

timesteps = 1e-8
steps = 100
for i in range(steps):
    P.updateOld()
    N.updateOld()
    H.updateOld()
    res = 1e10
    sweep = 0
    while res > 1e-3 and sweep < 20:
        res = eqn1d.sweep(dt=timesteps)
        sweep += 1
    if __name__ == '__main__':
        viewer.plot()

It does not give me the same errors as before which is some indication of progress. However, it is spitting out a new error:

ValueError: all the input arrays must have same number of dimensions, but the array at index 0 has 1 dimension(s) and the array at index 2 has 2 dimension(s)

nabz
  • 11
  • 1
  • I see. I guess I see some utility in Freeman's approach, given the desire to use an ODE solver almost fifty years ago. These days, solving Poisson's equation is not hard. Moreover, any transient version of Poisson's equation operates at the speed of light. I've never it necessary to use anything but the quasistatic version except for things like GHz or THz semiconductor devices. – jeguyer Mar 31 '21 at 13:58
  • I recommend solving Poisson's equation. I recommend solving for potential (scalar) and not electric field (vector). $\vec{E} = -\nabla V$. This puts Poisson's equation in the form of a 2nd order PDE, which is well-suited for FiPy. – jeguyer Mar 31 '21 at 14:00
  • For further discussion, I encourage you to use our [mailing list](https://www.ctcms.nist.gov/fipy/documentation/MAIL.html) or [GitHub issue tracker](https://github.com/usnistgov/fipy/issues). StackOverflow is not well-suited to back-and-forth discussion and troubleshooting. – jeguyer Mar 31 '21 at 14:01
  • I didn't register until now that both of your references are for plasmas. I don't know anything about plasmas, but a transient Poisson equation may be appropriate in your case if you're trying to capture the dynamics of the excitation field, but I'd still start with the electrostatic version. – jeguyer Mar 31 '21 at 14:29
  • thanks, I will follow up for further discussion on the mailing list – nabz Mar 31 '21 at 14:40