Search code examples
pdefipy

FiPy: Can I directly change faceVariables depending on neighboring cells?


I am working with a biological model of the distribution of microbial biomass (b1) on a 2D grid. From the biomass a protein (p1) is produced. The biomass diffuses over the grid, while the protein does not. Only if a certain amount of protein is produced (p > p_lim), the biomass is supposed to diffuse. I try to implement this by using a dummy cell variable z multiplied with the diffusion coefficient and setting it from 0 to 1 only in cells where p > p_lim.

The condition works fine and when the critical amount of p is reached in a cell, z is set to 1, and diffusion happens. However, the diffusion still does not work with the rate I would like, because to calculate diffusion, the face variable, not the value of the cell itself is used. The faces of z are always a mean of the cell with z=1 and its neighboring cells with z=0. I I, however, would like the diffusion to work at its original rate even if the neighbouring cell is still at p < p_lim.

So, my question is: Can i somehow access a faceVariable and change it? For example, set a face to 1 if any neigboring cell has reached p1 > p_lim? I guess this is not a proper mathematical thing to do, but I couldn't think of another way to simulate this problem.

I will show a very reduced form of my model below. In any case, I thank you very much for your time!

##### produce mesh

nx= 5.
ny= nx
dx = 1.
dy = dx
L = nx*dx
mesh = Grid2D(nx=nx,ny=ny,dx=dx,dy=dy)


#parameters
h1 = 0.5 # production rate of p
Db = 10. # diffusion coeff of b
p_lim=0.1 


# cell variables
z = CellVariable(name="z",mesh=mesh,value=0.)

b1 = CellVariable(name="b1",mesh=mesh,hasOld=True,value=0.)

p1= CellVariable(name="p1",mesh=mesh,hasOld=True,value=0.)


# equations
eqb1 = (TransientTerm(var=b1)== DiffusionTerm(var=b1,coeff=Db*z.arithmeticFaceValue)-ImplicitSourceTerm(var=b1,coeff=h1))
eqp1 = (TransientTerm(var=p1)==ImplicitSourceTerm(var=b1,coeff=h1)) 

# set b1 to 10. in the center of the grid
b1.setValue(10.,where=((x>2.)&(x<3.)&(y>2.)&(y<3.)))
         
vi=Viewer(vars=(b1,p1),FIPY_VIEWER="matplotlib")


eq = eqb1 & eqp1

from builtins import range
for t in range(10):
    b1.updateOld()
    p1.updateOld()
    z.setValue(z + 0.1,where=((p1>=p_lim) & (z < 1.)))
    
    eq.solve(dt=0.1)
     
    vi.plot()

Solution

  • In addition to .arithmeticFaceValue, FiPy provides other interpolators between cell and face values, such as .harmonicFaceValue and .minmodFaceValue.

    These properties are implemented using subclasses of _CellToFaceVariable, specifically _ArithmeticCellToFaceVariable, _HarmonicCellToFaceVariable, and _MinmodCellToFaceVariable.

    You can also make a custom interpolator by subclassing _CellToFaceVariable. Two such examples are _LevelSetDiffusionVariable and ScharfetterGummelFaceVariable (neither is well documented, I'm afraid).

    You need to override the _calc_() method to provide your custom calculation. This method takes three arguments:

    • alpha: an array of the ratio (0-1) of the distance from the face to the cell on one side, relative to the distance from distance from the cell on the other side to the cell on the first side
    • id1: an array of indices of the cells on one side of the face
    • id2: an array of indices of the cells on the other side of the face

    Note: You can ignore any clause if inline.doInline: and look at the _calc_() method defined under the else: clause.