1

I would like to solve the transient diffusion equation for two compounds A and B as shown in image. I think the image is a better way to show my problem. Diffusion equations and boundary conditions.

As you can see, the reaction only occurs at the surface and the flux of A is equal to flux of B. So, this two equations are coupled only at surface. The boundary condition is similar to ROBIN boundary condition, explained in Fipy manual. However, the main difference is the existence of the second variable in boundary condition. Does anybody have any idea how to formulate this boundary condition in Fipy? I guess I need to add some extra term to ROBIN boundary condition, but I couldn't figure it out.

I really appreciate your help.

This is the code which solves the mentioned equation with ROBIN boundary condition @ x=0.

-D(dC_A/dx) = -kC_A

-D(dC_B/dx) = -kC_B

In this condition, I can easily use ROBIN boundary condition to solve equations. The results seem reasonable for this boundary condition.

"""
Question for StackOverflow
"""
#%%
from fipy import Variable, FaceVariable, CellVariable, Grid1D, TransientTerm, DiffusionTerm, Viewer, ImplicitSourceTerm
from fipy.tools import numerix



#%%
##### Model parameters

L= 8.4853e-4 # m boundary layer thickness
dx= 1e-8 # mesh size 
nx = int(L/dx)+1 # number of meshes
D = 1e-9 # m^2/s diffusion coefficient
k = 1e-4 # m/s reaction coefficient R = k [c_A],
c_inf =  0. # ROBIN general condition, once can think R = k ([c_A]-[c_inf])
c_init = 1. # Initial concentration of compound A, mol/m^3


#%%
###### Meshing and variable definition

mesh = Grid1D(nx=nx, dx=dx)
c_A = CellVariable(name="c_A", hasOld = True,
                    mesh=mesh,
                    value=c_init)
c_B = CellVariable(name="c_B", hasOld = True,
                    mesh=mesh,
                    value=0.)

#%%
##### Right boundary condition 

valueRight = c_init
c_A.constrain(valueRight, mesh.facesRight)
c_B.constrain(0., mesh.facesRight)

#%%
### ROBIN BC requirements, defining cellDistanceVectors
## This code is for fixing celldistance via this link:
## https://stackoverflow.com/questions/60073399/fipy-problem-with-grid2d-celltofacedistancevectors-gives-error-uniformgrid2d
MA = numerix.MA
tmp = MA.repeat(mesh._faceCenters[..., numerix.NewAxis,:], 2, 1)
cellToFaceDistanceVectors = tmp - numerix.take(mesh._cellCenters, mesh.faceCellIDs, axis=1)
tmp = numerix.take(mesh._cellCenters, mesh.faceCellIDs, axis=1)
tmp = tmp[..., 1,:] - tmp[..., 0,:]
cellDistanceVectors = MA.filled(MA.where(MA.getmaskarray(tmp), cellToFaceDistanceVectors[:, 0], tmp))

#%%
##### Defining mask and Robin BC at left boundary 
mask = mesh.facesLeft
Gamma0 = D
Gamma = FaceVariable(mesh=mesh, value=Gamma0)
Gamma.setValue(0., where=mask)
dPf = FaceVariable(mesh=mesh,
                   value=mesh._faceToCellDistanceRatio * cellDistanceVectors)
n = mesh.faceNormals
a = FaceVariable(mesh=mesh, value=k, rank=1)
b = FaceVariable(mesh=mesh, value=D, rank=0)
g = FaceVariable(mesh=mesh, value= k * c_inf, rank=0)
RobinCoeff = (mask * Gamma0 * n / (-dPf.dot(a)+b))

#%%
#### Making a plot
viewer = Viewer(vars=(c_A, c_B),
                     datamin=-0.2, datamax=c_init * 1.4)
viewer.plot()

#%% Time step and simulation time definition
time = Variable()
t_simulation = 4 # seconds
timeStepDuration = .05
steps = int(t_simulation/timeStepDuration)

#%% PDE Equations
eqcA = (TransientTerm(var=c_A) == DiffusionTerm(var=c_A, coeff=Gamma) + 
            (RobinCoeff * g).divergence 
            - ImplicitSourceTerm(var=c_A, coeff=(RobinCoeff * a.dot(-n)).divergence))

eqcB = (TransientTerm(var=c_B) == DiffusionTerm(var=c_B, coeff=Gamma) -
                (RobinCoeff * g).divergence 
            + ImplicitSourceTerm(var=c_B, coeff=(RobinCoeff * a.dot(-n)).divergence))


#%% A loop for solving PDE equations
while time() <= (t_simulation):
    time.setValue(time() + timeStepDuration)
    c_B.updateOld()
    c_A.updateOld()
    res1=res2 = 1e10
    viewer.plot()
    while (res1 > 1e-6) & (res2 > 1e-6):
        res1 = eqcA.sweep(var=c_A, dt=timeStepDuration)
        res2 = eqcB.sweep(var=c_B, dt=timeStepDuration)
Ali
  • 11
  • 1

1 Answers1

0

It's possible to solve this as a fully implicit system. The code below simplifies the problem to have a unity domain size and diffusion coefficient. k is set to 0.2. It captures the analytical solution quite well with some caveats (see below).

from fipy import (
    CellVariable,
    TransientTerm,
    DiffusionTerm,
    ImplicitSourceTerm,
    Grid1D,
    Viewer,
)

L = 1.0
nx = 1000
dx = L / nx
konstant = 0.2
coeff = 1.0

mesh = Grid1D(nx=nx, dx=dx)

var_a = CellVariable(mesh=mesh, value=1.0, hasOld=True)
var_b = CellVariable(mesh=mesh, value=0.0, hasOld=True)

var_a.constrain(1.0, mesh.facesRight)
var_b.constrain(0.0, mesh.facesRight)

coeff_mask = ~mesh.facesLeft * coeff
boundary_coeff = konstant * (mesh.facesLeft * mesh.faceNormals).divergence

eqn_a = TransientTerm(var=var_a) == DiffusionTerm(
    coeff_mask, var=var_a
) - ImplicitSourceTerm(boundary_coeff, var=var_a) + ImplicitSourceTerm(
    boundary_coeff, var=var_b
)

eqn_b = TransientTerm(var=var_b) == DiffusionTerm(
    coeff_mask, var=var_b
) - ImplicitSourceTerm(boundary_coeff, var=var_b) + ImplicitSourceTerm(
    boundary_coeff, var=var_a
)

eqn = eqn_a & eqn_b

for _ in range(5):
    var_a.updateOld()
    var_b.updateOld()
    eqn.sweep(dt=1e10)

Viewer((var_a, var_b)).plot()

print("var_a[0] (expected):", (1 + konstant) / (1 + 2 * konstant))
print("var_b[0] (expected):", konstant / (1 + 2 * konstant))
print("var_a[0] (actual):", var_a[0])
print("var_b[0] (actual):", var_b[0])

input("wait")

Note the following:

  • As written the boundary condition is only first order accurate, which doesn't really matter for this problem, but might hurt you for in higher dimensions. There might be ways to fix this such as having a small cell near the boundary or adding in an explicit second order correction for the boundary condition.

  • The equations are coupled here. If uncoupled it would probably require loads of iterations to reach equilibrium.

  • It did require a few iterations to reach equilibrium, but it shouldn't. That's probably due to the solver not converging adequately without a few tries. It might be that coupled equations have some bad conditioning.

wd15
  • 1,068
  • 5
  • 8