From charlesreid1

No edit summary
No edit summary
Line 6: Line 6:


<math>
<math>
\dfrac{\partial C}{\partial t} = \nabla \cdot \left( D \nabla C \right)
\dfrac{\partial X_i}{\partial t} = \nabla \cdot \left( D_i \nabla X_i \right)
</math>
</math>


subject to boundary conditions:


<syntaxhighlight lang="python">
<math>
from fipy import *
X_i ( x=0 ) = 1
from numpy import *
X_i ( x=1 ) = 0
import Cantera
</math>
import matplotlib.pylab as plt


"""
The mixture being used is argon in oxygen. Although the diffusion coefficient is a very weak function of composition, and therefore the assumption
1D Diffusion:
Cantera Example with Variable Diffusivity


Charles Reid
<math>
December 2013
D_i \neq D_i(X_i)
</math>


Solve a 1D diffusion problem in which  
would normally apply, this example illustrates the sweep solution technique (in which the diffusion coefficient is a function of the composition) anyway.
two species diffuse into each other.


Diffusion coefficients vary with composition.
=The Script=


Species:  
Begin with import statements:
    Ar
    O2


BCs:
<source lang="python">
    Ar(x=0) = 1
from fipy import *
    Ar(x=1) = 0
from numpy import *
    O2(x=0) = 0
import Cantera
    O2(x=1) = 1
import matplotlib.pylab as plt
 
</source>
See also:
    http://www.ctcms.nist.gov/fipy/examples/diffusion/generated/examples.diffusion.mesh1D.html
"""


Next, create a plot, which we'll use to visualize our results:


<source>
fig = plt.figure()
fig = plt.figure()
ax = fig.add_subplot(111)
ax = fig.add_subplot(111)
ax.set_title('Ar')
ax.set_title('Ar')
</source>


Computing the diffusion coefficient using Cantera proceeds as follows:
* Pass in a Fipy CellVariable object
* Create a CellVariable to contain diffusion coefficient values
* Looping over each cell:
** Translate the value of the Fipy CellVariable at a given cell into a thermochemical state for a Cantera phase object (we want to use a single Cantera phase object, so we don't have to keep loading our CTI/XML mechanism file into memory)
** Compute the value of the diffusion coefficient
** Populate the diffusion CellVariable
* Return the diffusion CellVariable


We'll define this in a function, which we can then pass when specifying the diffusion coefficient for the Fipy equation.
<source>
######################################  
######################################  
# Diffusion coefficient function
# Diffusion coefficient function
Line 65: Line 72:


     return D
     return D
</source>


We also want to define a single Cantera phase, which we'll use in the <code>get_diffusion_coeff()</code> function:


<source>
######################################  
######################################  
# Gas object  
# Gas object  
Line 83: Line 93:
ari = a.speciesIndex(arlab)
ari = a.speciesIndex(arlab)
o2i = a.speciesIndex(o2lab)
o2i = a.speciesIndex(o2lab)
</pre>


Now we define Fipy equations/grid:
<pre>
# First, define the mesh
# First, define the mesh
nx = 100
nx = 100
Line 105: Line 119:
print "Nsteps =",steps
print "Nsteps =",steps
t = timestepDuration * steps
t = timestepDuration * steps


# Now define your sweep variables
# Now define your sweep variables
arvar = [CellVariable(name=arlab, mesh=mesh, value=arRight, hasOld=1)]
</source>
At this point, we can define the Fipy equation that we're solving, which is, as given above,
<math>
\dfrac{\partial X_i}{\partial t} = \nabla \cdot \left( D \nabla X_i \right)
</math>


arvar = [CellVariable(name=arlab, mesh=mesh, value=arRight, hasOld=1)]
and the diffusion coefficient is a function of <math>X_i</math>. This is subject to the boundary conditions set above (1 on the left side of the domain, 0 on the right side of the domain).


<source>
# Define the equation being solved
# Define the equation being solved
eq = TransientTerm() == DiffusionTerm(coeff=get_diffusion_coeff(arvar[0]))
eq = TransientTerm() == DiffusionTerm(coeff=get_diffusion_coeff(arvar[0]))
Line 116: Line 140:
arvar[0].constrain(arLeft, mesh.facesLeft)
arvar[0].constrain(arLeft, mesh.facesLeft)
arvar[0].constrain(arRight, mesh.facesRight)
arvar[0].constrain(arRight, mesh.facesRight)
</source>


Now we timestep through the solutions, and plot the solution every 50 timesteps. At each timestep, we are performing 5 sweeps (that is, we're performing 5 iterations per timestep). Again, our diffusion coefficient is not a strong function of composition, because Ar and O2 are so similar in shape/properties, so even 5 sweeps is probably unnecessary. Again, this is purely for illustrative purposes.
<source>
# Timestep through solutions
# Timestep through solutions
arvar[0].setValue(arRight)
arvar[0].setValue(arRight)
Line 133: Line 161:
         plt.show()
         plt.show()
         plt.draw()
         plt.draw()
</syntaxhighlight>
</source>
 
=Result=
 
This will result in the following plot:
 
[[Image:OneVarCanteraVariableD.png]]
 
=Download The Script=
 
Download the script here: [[File:OneVarCanteraVariableD.py]]
 
=References=
 
Fipy documentation covering 1D diffusion problems: http://www.ctcms.nist.gov/fipy/examples/diffusion/generated/examples.diffusion.mesh1D.html


[[Category:Cantera]]
[[Category:Cantera]]
[[Category:Fipy]]
[[Category:Fipy]]

Revision as of 23:58, 19 December 2013

Overview

This example illustrates how to solve a simple, time-dependent 1D diffusion problem using Fipy with diffusion coefficeints computed by Cantera.

The equation being solved is:

$ \dfrac{\partial X_i}{\partial t} = \nabla \cdot \left( D_i \nabla X_i \right) $

subject to boundary conditions:

$ X_i ( x=0 ) = 1 X_i ( x=1 ) = 0 $

The mixture being used is argon in oxygen. Although the diffusion coefficient is a very weak function of composition, and therefore the assumption

$ D_i \neq D_i(X_i) $

would normally apply, this example illustrates the sweep solution technique (in which the diffusion coefficient is a function of the composition) anyway.

The Script

Begin with import statements:

from fipy import *
from numpy import *
import Cantera
import matplotlib.pylab as plt

Next, create a plot, which we'll use to visualize our results:

fig = plt.figure()
ax = fig.add_subplot(111)
ax.set_title('Ar')

Computing the diffusion coefficient using Cantera proceeds as follows:

  • Pass in a Fipy CellVariable object
  • Create a CellVariable to contain diffusion coefficient values
  • Looping over each cell:
    • Translate the value of the Fipy CellVariable at a given cell into a thermochemical state for a Cantera phase object (we want to use a single Cantera phase object, so we don't have to keep loading our CTI/XML mechanism file into memory)
    • Compute the value of the diffusion coefficient
    • Populate the diffusion CellVariable
  • Return the diffusion CellVariable

We'll define this in a function, which we can then pass when specifying the diffusion coefficient for the Fipy equation.

###################################### 
# Diffusion coefficient function
def get_diffusion_coeff( phi ):

    D = CellVariable(name="DiffusionCoeff",mesh=mesh)

    for i in range(phi.shape[0]):
        var = phi.value[i]
        X0 = "AR:%0.8f, O2:%0.8f"%(var,1-var)
        a.set(X=X0)

        # mixture-averaged diffusion coefficients
        Dcoeff = a.mixDiffCoeffs()[ari]

        D.put(i,Dcoeff)

    return D

We also want to define a single Cantera phase, which we'll use in the get_diffusion_coeff() function:

###################################### 
# Gas object 
#
# This object will be used to evaluate all thermodynamic, kinetic,
# and transport properties
#
rxnmech    =  'h2o2.cti'       # reaction mechanism file
mix        =  'ohmech'         # gas mixture model
comp       =  'O2:1.0, AR:1.0' # gas composition
a = Cantera.IdealGasMix(rxnmech, mix)
a.set(T = 298.15, P = Cantera.OneAtm, X = comp)

arlab = 'AR'
o2lab = 'O2'
ari = a.speciesIndex(arlab)
o2i = a.speciesIndex(o2lab)
</pre>

Now we define Fipy equations/grid:

<pre>
# First, define the mesh
nx = 100
dx = 0.01
L = nx * dx
xs = linspace(0,L,nx)
mesh = Grid1D(nx=nx,dx=dx)

# Define boundary values 
arLeft  = 1.
arRight = 0.

# Define timestep-related parameters
# Courant stability constraint for timestep
D0 = 1.0e-5
safetyFactor = 0.9
timestepDuration = safetyFactor * dx**2 / (2 * D0)

# Simulation duration
steps = 500
print "Nsteps =",steps
t = timestepDuration * steps


# Now define your sweep variables
arvar = [CellVariable(name=arlab, mesh=mesh, value=arRight, hasOld=1)]

At this point, we can define the Fipy equation that we're solving, which is, as given above,

$ \dfrac{\partial X_i}{\partial t} = \nabla \cdot \left( D \nabla X_i \right) $

and the diffusion coefficient is a function of $ X_i $. This is subject to the boundary conditions set above (1 on the left side of the domain, 0 on the right side of the domain).

# Define the equation being solved
eq = TransientTerm() == DiffusionTerm(coeff=get_diffusion_coeff(arvar[0]))

# Set the boundary conditions
arvar[0].constrain(arLeft, mesh.facesLeft)
arvar[0].constrain(arRight, mesh.facesRight)

Now we timestep through the solutions, and plot the solution every 50 timesteps. At each timestep, we are performing 5 sweeps (that is, we're performing 5 iterations per timestep). Again, our diffusion coefficient is not a strong function of composition, because Ar and O2 are so similar in shape/properties, so even 5 sweeps is probably unnecessary. Again, this is purely for illustrative purposes.

# Timestep through solutions
arvar[0].setValue(arRight)
for step in range(steps):
    # only move forward in time once per time step
    arvar[0].updateOld()
    
    # but "sweep" many times per time step
    for sweep in range(5):
        res = eq.sweep(var=arvar[0],
                       dt=timestepDuration)

    if step%50==0:
        ax.plot(xs,arvar[0].value)
        ax.hold(True)
        plt.show()
        plt.draw()

Result

This will result in the following plot:

OneVarCanteraVariableD.png

Download The Script

Download the script here: File:OneVarCanteraVariableD.py

References

Fipy documentation covering 1D diffusion problems: http://www.ctcms.nist.gov/fipy/examples/diffusion/generated/examples.diffusion.mesh1D.html