.. _FAQ:

Frequently Asked Questions
==========================

How do I represent an equation in FiPy?
---------------------------------------

As explained in :ref:`chap:Numerics`, the canonical
governing equation that can be solved by :term:`FiPy` for the dependent
:class:`~fipy.variables.cellVariable.CellVariable` :math:`\phi` is

.. math::
   
   \underbrace{
     \frac{\partial (\rho \phi)}{\partial t}
   }_{\text{transient}}
   =
   \underbrace{
     \vphantom{\frac{\partial (\rho \phi)}{\partial t}}
     \nabla \cdot \left( \vec{u} \phi \right)
   }_{\text{convection}}
   +
   \underbrace{
     \vphantom{\frac{\partial (\rho \phi)}{\partial t}}
     \left[ \nabla \cdot \left( \Gamma_i \nabla \right) \right]^n \phi
   }_{\text{diffusion}}
   +
   \underbrace{
     \vphantom{\frac{\partial (\rho \phi)}{\partial t}}
     S_{\phi}
   }_{\text{source}}
   
A physical problem can involve many different coupled
governing equations, one for each variable.  Numerous specific
examples are presented in Part :ref:`part:Examples`, but let us
examine this general expression term-by-term:

How do I represent a transient term :math:`\partial (\rho \phi) / \partial t`?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

>>> TransientTerm(coeff = rho)
    
.. note::      

   We have specified neither the variable :math:`\phi` nor the time
   step.  Both are handled when we actually solve the equation.

How do I represent a convection term :math:`\nabla \cdot \left( \vec{u} \phi \right)`?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. currentmodule:: fipy.terms

>>> <SpecificConvectionTerm>(coeff = u, 
...                          diffusionTerm = diffTerm)
    
where :samp:`{<SpecificConvectionTerm>}` can be any of 
:class:`~centralDiffConvectionTerm.CentralDifferenceConvectionTerm`,
:class:`~exponentialConvectionTerm.ExponentialConvectionTerm`, 
:class:`~hybridConvectionTerm.HybridConvectionTerm`, 
:class:`~powerLawConvectionTerm.PowerLawConvectionTerm`,
:class:`~upwindConvectionTerm.UpwindConvectionTerm`, 
:class:`~explicitUpwindConvectionTerm.ExplicitUpwindConvectionTerm`, or 
:class:`~vanLeerConvectionTerm.VanLeerConvectionTerm`.
The differences between these convection schemes are described
in Section :ref:`sec:NumericalSchemes`. The velocity coefficient 
``u`` must be a rank-1 :class:`~fipy.variables.faceVariable.FaceVariable`, or a 
constant vector in the form of a Python list or tuple, 
*e.g.* ``((1,), (2,))`` for a vector in 2D.

.. note::

   As discussed in :ref:`sec:NumericalSchemes`, the
   convection schemes need to calculate a Peclet number,
   and therefore need to know about any diffusion term
   used in the problem.  It is hoped that this dependency
   can be automated in the future.

How do I represent a diffusion term :math:`\nabla \cdot \left( \Gamma_1 \nabla \phi \right)`?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. currentmodule:: fipy.terms

Either

>>> ImplicitDiffusionTerm(coeff = Gamma1)
    
or 

>>> ExplicitDiffusionTerm(coeff = Gamma1)
    
:class:`~explicitDiffusionTerm.ExplicitDiffusionTerm` is provided only for illustrative purposes.
:class:`~implicitDiffusionTerm.ImplicitDiffusionTerm` is almost 
always preferred (:class:`~diffusionTerm.DiffusionTerm` is a 
synonym for :class:`~implicitDiffusionTerm.ImplicitDiffusionTerm` to reinforce this preference). It is
theoretically possible to create an explicit diffusion term with

>>> (Gamma1 * phi.getFaceGrad()).getDivergence()

Unfortunately, in this form, any boundary conditions on :math:`\phi` will not be
accounted for.

.. _FAQ-higherOrderDiffusion:

How do I represent a term :math:`\nabla^4 \phi` or :math:`\nabla \cdot \left( \Gamma_1 \nabla \left( \nabla\cdot\left(  \Gamma_2 \nabla \phi\right) \right) \right)` such as for  Cahn-Hilliard? 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
>>> ImplicitDiffusionTerm(coeff = (Gamma1, Gamma2))

The number of elements supplied for ``coeff`` determines the
order of the term.

Is there a way to model an anisotropic diffusion process or more generally to represent the diffusion coefficient as a tensor so that the diffusion term takes the form :math:`\partial_i \Gamma_{ij}\partial_j \phi`?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Terms of the form :math:`\partial_i \Gamma_{ij}\partial_j \phi` can
be posed in :term:`FiPy` by using a list, tuple, rank 1 or rank 2
:class:`~fipy.variables.faceVariable.FaceVariable` to represent a vector or tensor diffusion
coefficient.  For example, if we wished to represent a
diffusion term with an anisotropy ratio of 5 aligned along the
x-coordinate axis, we could write the term as,

>>> DiffusionTerm([[[5, 0], [0, 1]]])

which represents :math:`5 \partial_x^2 + \partial_y^2`.  Notice that
the tensor, written in the form of a list, is contained within
a list. This is because the first index of the list refers to
the order of the term not the first index of the tensor (see
the FAQ, :ref:`FAQ-higherOrderDiffusion`). This
notation, although succinct can sometimes be confusing so a
number of cases are interpreted below.

    >>> DiffusionTerm([[5, 1]])

    This represents the same term as the case examined above.
    The vector notation is just a short-hand representation
    for the diagonal of the tensor. Off-diagonals are assumed
    to be zero.

    >>> DiffusionTerm([5, 1])

    This simply represents a fourth order isotropic diffusion
    term of the form :math:`5 \left( \partial_x^2 + \partial_y^2
    \right)^2`.

    >>> DiffusionTerm([[1, 0], [0, 1]])

    Nominally, this should represent a fourth order diffusion
    term of the form :math:`\partial_x^2 \partial_y^2`, but :term:`FiPy`
    does not currently support anisotropy for higher order
    diffusion terms so this may well throw an error or give
    anomalous results.
  
    >>> x, y = mesh.getCellCenters()
    >>> DiffusionTerm([[[x**2, x * y], [-x * y, -y**2]]])

    This represents an anisotropic diffusion coefficient that
    varies spatially so that the term has the form 
    :math:`\partial_x (x^2 \partial_x + x y \partial_y)
    + \partial_y (-x y \partial_x - y^2 \partial_y)
    \equiv x \partial_x - y \partial_y + x^2 \partial_x^2 - y^2
    \partial_y^2`.

    Generally, anisotropy is not conveniently aligned along the coordinate
    axes; in these cases, it is necessary to apply a rotation matrix in
    order to calculate the correct tensor values, see
    :mod:`examples.diffusion.anisotropy` for details.

What if the term isn't one of those?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Any term that cannot be written in one of the previous
forms is considered a source :math:`S_{\phi}`. An explicit
source is written in Python essentially as it appears in
mathematical form, *e.g.*, :math:`3\kappa^2 + b \sin
\theta` would be written

>>> 3 * kappa**2 + b * sin(theta)
    
.. note::

   Functions like :func:`sin` can be obtained from the
   :mod:`fipy.tools.numerix` module.

   .. warning::

      Generally, things will not work as expected if the equivalent
      function is used from the :term:`NumPy` or :term:`SciPy` library.
    
If, however, the source depends on the variable that is being solved for,
it can be advantageous to linearize the source and cast part of it as an
implicit source term, *e.g.*, :math:`3\kappa^2 + \phi \sin \theta`
might be written as

>>> 3 * kappa**2 + ImplicitSourceTerm(coeff=sin(theta))
    
How do I represent a `...` term that *doesn't* involve the dependent variable?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

It is important to realize that, even though an expression may
superficially resemble one of those shown above, if the dependent variable
*for that PDE* does not appear in the appropriate place, then that
term should be treated as a source.

How do I represent a diffusive source?
''''''''''''''''''''''''''''''''''''''

If the governing equation for :math:`\phi` is

.. math::

   \frac{\partial \phi}{\partial t} 
   = \nabla\cdot\left( D_1 \nabla \phi\right)
   + \nabla\cdot\left( D_2 \nabla \xi\right)

then the first term is a :class:`~fipy.terms.transientTerm.TransientTerm` and the second term is a
:class:`~fipy.terms.diffusionTerm.DiffusionTerm`, but the third term is simply an explicit source,
which is written in Python as

>>> (D2 * xi.getFaceGrad()).getDivergence()

.. currentmodule:: fipy.variables.cellVariable

Higher order diffusive sources can be obtained by simply nesting the calls
to :meth:`~CellVariable.getFaceGrad` and 
:meth:`~fipy.variables.faceVariable.FaceVariable.getDivergence`.

.. note::

   We use :meth:`~CellVariable.getFaceGrad`, rather than
   :meth:`~CellVariable.getGrad`, in order to obtain a second-order spatial
   discretization of the diffusion term in :math:`\xi`, consistent with the
   matrix that is formed by
   :class:`~fipy.terms.diffusionTerm.DiffusionTerm` for :math:`\phi`.

How do I represent a convective source?
'''''''''''''''''''''''''''''''''''''''

The convection of an independent field :math:`\xi` as in

.. math::

   \frac{\partial \phi}{\partial t} 
   = \nabla\cdot
   \left(
       \vec{u} \xi
   \right)

can be rendered as

>>> (u * xi.getArithmeticFaceValue()).getDivergence()

when :math:`\vec{u}` is a rank-1 :class:`~fipy.variables.faceVariable.FaceVariable` (preferred) or as

>>> (u * xi).getDivergence()

if :math:`\vec{u}` is a rank-1 :class:`~fipy.variables.cellVariable.CellVariable`.

How do I represent a transient source?
''''''''''''''''''''''''''''''''''''''

The time-rate-of change of an independent variable :math:`\xi`, such as in

.. math::

   \frac{\partial (\rho_1 \phi)}{\partial t}
   = \frac{\partial (\rho_2 \xi)}{\partial t}

does not have an abstract form in :term:`FiPy` and should be discretized
directly, in the manner of Equation :eq:`num:tra`, as

>>> TransientTerm(coeff = rho1) == rho2 * (xi - xi.getOld()) / timeStep

This technique is used in :mod:`examples.phase.anisotropy`.
    
What if my term involves the dependent variable, but not where FiPy puts it?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
Frequently, viewing the term from a different perspective will allow it to
be cast in one of the canonical forms. For example, the third term in

.. math::

   \frac{\partial \phi}{\partial t} 
   = \nabla\cdot\left( D_1 \nabla \phi\right)
   + \nabla\cdot\left( D_2 \phi \nabla \xi\right)

might be considered as the diffusion of the independent variable :math:`\xi` with
a mobility :math:`D_2\phi` that is a function of the dependent variable :math:`\phi`.
For :term:`FiPy`'s purposes, however, this term represents the convection of
:math:`\phi`, with a velocity :math:`D_2\nabla\xi`, due to the counter-diffusion of
:math:`\xi`, so

>>> diffTerm = DiffusionTerm(coeff = D1)
>>> convTerm = <Specific>ConvectionTerm(coeff = D2 * xi.getFaceGrad(), 
...                                     diffusionTerm = diffTerm)
>>> eq = TransientTerm() == diffTerm + convTerm

What if the coefficient of a term depends on the variable that I'm solving for?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        
A non-linear coefficient, such as the diffusion coefficient in
:math:`\nabla\cdot[\Gamma_1(\phi) \nabla \phi] = \nabla\cdot[\Gamma_0 \phi (1 -
\phi) \nabla\phi]` is not a problem for :term:`FiPy`. Simply write it as it
appears:

>>> diffTerm = DiffusionTerm(coeff = Gamma0 * phi * (1 - phi))

.. note::

   Due to the nonlinearity of the coefficient, it will probably be
   necessary to "sweep" the solution to convergence as discussed in
   :ref:`FAQ-IterationsTimestepsSweeps`.

How can I see what I'm doing?
-----------------------------
   
.. currentmodule:: fipy.viewers

How do I export data?
~~~~~~~~~~~~~~~~~~~~~

The way to save your calculations depends on how you plan to make use of
the data. If you want to save it for "restart" (so that you can continue
or redirect a calculation from some intermediate stage), then you'll want
to "pickle" the :term:`Python` data with the :mod:`dump` module. This is
illustrated in :mod:`examples.phase.anisotropy`,
:mod:`examples.phase.impingement.mesh40x1`,
:mod:`examples.phase.impingement.mesh20x20`, and
:mod:`examples.levelSet.electroChem.howToWriteAScript`.
   
On the other hand, pickled :term:`FiPy` data is of little use to anything
besides :term:`Python` and :term:`FiPy`. If you want to import your calculations into
another piece of software, whether to make publication-quality graphs or
movies, or to perform some analysis, or as input to another stage of a
multiscale model, then you can save your data as an :abbr:`ASCII` text
file of tab-separated-values with a :class:`~tsvViewer.TSVViewer`. This is illustrated
in :mod:`examples.diffusion.circle`.
   
How do I save a plot image?
~~~~~~~~~~~~~~~~~~~~~~~~~~~

Some of the viewers have a button or other mechanism in the user interface
for saving an image file. Also, you can supply an optional keyword
:keyword:`filename` when you tell the viewer to
:meth:`~Viewer.plot`, *e.g.*

>>> viewer.plot(filename="myimage.ext")

which will save a file named :file:`myimage.ext` in your current working
directory. The type of image is determined by the file extension
":file:`.ext`". Different viewers have different capabilities:

:term:`Pygist`
  accepts ":file:`.eps`" (`Encapsulated PostScript
  <http://en.wikipedia.org/wiki/Encapsulated_PostScript>`_) and
  ":file:`.cgm`" (`Computer Graphics Metafile
  <http://xml.coverpages.org/cgm.html>`_).
  
:term:`gnuplot`
  accepts ":file:`.eps`."
  
:term:`Matplotlib`
  accepts ":file:`.eps`," ":file:`.jpg`" (`Joint Photographic Experts
  Group <http://www.jpeg.org/>`_), and ":file:`.png`" (`Portable Network
  Graphics <http://www.w3.org/Graphics/PNG/>`_).
      
  .. attention::

     Actually, :term:`Matplotlib` supports different extensions, depending on
     the chosen `backend
     <http://matplotlib.sourceforge.net/backends.html>`_, but our
     :class:`~matplotlibViewer.matplotlibViewer.MatplotlibViewer` classes don't properly support this yet.

What if I only want the saved file, with no display on screen?
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

To our knowledge, this is only supported by :term:`Matplotlib`, as is explained
in the
`Matplotlib FAQ <http://matplotlib.sourceforge.net/faq/howto_faq.html#generate-images-without-having-a-window-popup>`_. 
Basically, you need to tell :term:`Matplotlib` to use an "image
backend," such as "``Agg``" or "``Cairo``." Backends are discussed at
http://matplotlib.sourceforge.net/backends.html.

How do I make a movie?
~~~~~~~~~~~~~~~~~~~~~~

:term:`FiPy` has no facilities for making movies. You will need to save
individual frames (see the previous question) and then stitch them together
into a movie, using one of a variety of different free, shareware, or
commercial software packages. The guidance in the
`Matplotlib FAQ <http://matplotlib.sourceforge.net/faq/howto_faq.html#make-a-movie>`_ 
should be adaptable to other :class:`~viewer.Viewer`\s.

Why doesn't the :class:`~viewer.Viewer` look the way I want?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

:term:`FiPy`'s viewers are utilitarian. They're designed to let you see
*something* with a minimum of effort. Because different plotting
packages have different capabilities and some are easier to install on some
platforms than on others, we have tried to support a range of :term:`Python`
plotters with a minimal common set of features. Many of these packages are
capable of much more, however. Often, you can invoke the :class:`~viewer.Viewer` you
want, and then issue supplemental commands for the underlying plotting
package. The better option is to make a "subclass" of the :term:`FiPy`
:class:`~viewer.Viewer` that comes closest to producing the image you want. You can
then override just the behavior you wan to change, while letting :term:`FiPy` do
most of the heavy lifting. See :mod:`examples.phase.anisotropy` for an
example of creating a custom :term:`Matplotlib` :class:`~viewer.Viewer` class.

.. _FAQ-IterationsTimestepsSweeps:

Iterations, timesteps, and sweeps? Oh, my!
------------------------------------------

Any non-linear solution of partial differential equations is an
approximation. These approximations benefit from repetetive
solution to achieve the best possible answer. In :term:`FiPy` (and in
many similar PDE solvers), there are three layers of repetition.

iterations
  This is the lowest layer of repetition, which you'll generally need to
  spend the least time thinking about. :term:`FiPy` solves PDEs by
  discretizing them into a set of linear equations in matrix form, as
  explained in :ref:`section:discretization` and
  :ref:`section:linear-equations`. It is not always practical, or even
  possible, to exactly solve these matrix equations on a computer.
  :term:`FiPy` thus employs "iterative solvers", which make successive
  approximations until the linear equations have been satisfactorily
  solved. :term:`FiPy` chooses a default number of iterations and solution
  tolerance, which you will not generally need to change. If you do wish to
  change these defaults, you'll need to create a new
  :class:`~fipy.solvers.solver.Solver` object with the desired number of
  iterations and solution tolerance, *e.g.*

  >>> mySolver = LinearPCGSolver(iterations=1234, tolerance=5e-6) 
  : 
  :
  >>> eq.solve(..., solver=mySolver, ...)

  .. note::

     The older :class:`~fipy.solvers.solver.Solver` :keyword:`steps=` keyword is now deprecated
     in favor of :keyword:`iterations=` to make the role clearer.

  Solver iterations are changed from their defaults in
  :mod:`examples.flow.stokesCavity` and
  :mod:`examples.update0-1to1-0`.

sweeps
  This middle layer of repetition is important
  when a PDE is non-linear (*e.g.*, a diffusivity that
  depends on concentration) or when multiple PDEs are coupled
  (*e.g.*, if solute diffusivity depends on temperature and
  thermal conductivity depends on concentration). Even if the
  :class:`~fipy.solvers.solver.Solver` solves the *linear* approximation of the
  PDE to absolute perfection by performing an infinite number of
  iterations, the solution may still not be a very good
  representation of the actual *non-linear* PDE. If we
  resolve the same equation *at the same point in elapsed
  time*, but use the result of the previous solution instead of 
  the previous timestep, then we can get a refined solution to 
  the *non-linear* PDE in a process known as "sweeping." 

  .. note::

     Despite references to the "previous timestep," sweeping is 
     not limited to time-evolving problems. Nonlinear sets of 
     quasi-static or steady-state PDEs can require sweeping, too.

  We need to distinguish between the value of the variable at
  the last timestep and the value of the variable at the last
  sweep (the last cycle where we tried to solve the
  *current* timestep). This is done by first modifying the
  way the variable is created:

  >>> myVar = CellVariable(..., hasOld=1)

  and then by explicitly moving the current value of the 
  variable into the "old" value only when we want to:

  >>> myVar.updateOld()

  Finally, we will need to repeatedly solve the equation until 
  it gives a stable result. To clearly distinguish that a 
  single cycle will not truly "solve" the equation, we invoke 
  a different method ":meth:`~fipy.terms.term.Term.sweep`:

  >>> for sweep in range(sweeps):
  ...     eq.sweep(var=myVar, ...)

  Even better than sweeping a fixed number of cycles is to do it 
  until the non-linear PDE has been solved satisfactorily:

  >>> while residual > desiredResidual:
  ...     residual = eq.sweep(var=myVar, ...)

  Sweeps are used to achieve better solutions in
  :mod:`examples.diffusion.mesh1D`, 
  :mod:`examples.phase.simple`, 
  :mod:`examples.phase.binary`, and :mod:`examples.flow.stokesCavity`.

timesteps
  This outermost layer of repetition is of most practical interest to
  the user. Understanding the time evolution of a problem is frequently
  the goal of studying a particular set of PDEs. Moreover, even when
  only an equilibrium or steady-state solution is desired, it may not
  be possible to simply solve that directly, due to non-linear coupling
  between equations or to boundary conditions or initial conditions.
  Some types of PDEs have fundamental limits to how large a timestep
  they can take before they become either unstable or inaccurate.

  .. note::

     Stability and accuracy are distinctly different. An unstable 
     solution is often said to "blow up", with radically 
     different values from point to point, often diverging to 
     infinity. An inaccurate solution may look perfectly 
     reasonable, but will disagree significantly from an 
     analytical solution or from a numerical solution obtained by 
     taking either smaller or larger timesteps. 

  For all of these reasons, you will frequently need to advance
  a problem in time and to choose an appropriate interval
  between solutions. This can be simple:

  >>> timeStep = 1.234e-5
  >>> for step in range(steps):
  ...     eq.solve(var=myVar, dt=timeStep, ...)

  or more elaborate:

  >>> timeStep = 1.234e-5
  >>> elapsedTime = 0
  >>> while elapsedTime < totalElapsedTime:
  ...     eq.solve(var=myVar, dt=timeStep, ...)
  ...     elapsedTime += timeStep
  ...     timeStep = SomeFunctionOfVariablesAndTime(myVar1, myVar2, elapedTime)


  A majority of the examples in this manual illustrate time evolving
  behavior. Notably, boundary conditions are made a function of elapsed
  time in :mod:`examples.diffusion.mesh1D`. The timestep is
  chosen based on the expected interfacial velocity in
  :mod:`examples.phase.simple`. The timestep is gradually
  increased as the kinetics slow down in
  :mod:`examples.cahnHilliard.mesh2D`.

Finally, we can (and often do) combine all three layers of repetition:

>>> myVar = CellVariable(..., hasOld=1)
: 
:
>>> mySolver = LinearPCGSolver(iterations=1234, tolerance=5e-6) 
: 
:
>>> while elapsedTime < totalElapsedTime:
...     myVar.updateOld()
...     while residual > desiredResidual:
...         residual = eq.sweep(var=myVar, dt=timeStep, ...)
...     elapsedTime += timeStep

Why the distinction between :class:`~fipy.variables.cellVariable.CellVariable` and :class:`~fipy.variables.faceVariable.FaceVariable` coefficients?  
---------------------------------------------------------------------------------------------------------------------------------------------------

:term:`FiPy` solves field variables on the 
:class:`~fipy.meshes.numMesh.cell.Cell` centers. Transient and
source terms describe the change in the value of a field at the
:class:`~fipy.meshes.numMesh.cellCell` center, and so they take a 
:class:`~fipy.variables.cellVariable.CellVariable` coefficient.
Diffusion and convection terms involve fluxes *between* 
:class:`~fipy.meshes.numMesh.cell.Cell`
centers, and are calculated on the :class:`~fipy.meshes.numMesh.face.Face` 
between two :class:`~fipy.meshes.numMesh.cell.Cell`\s,
and so they take a :class:`~fipy.meshes.numMesh.faceVariable.FaceVariable` coefficient.
    
.. note::
    
   If you supply a :class:`~fipy.meshes.numMesh.cellVariable.CellVariable`
   ``var`` when a :class:`~fipy.meshes.numMesh.faceVariable.FaceVariable`
   is expected, :term:`FiPy` will automatically substitute
   ``var.``:meth:`~fipy.variables.cellVariable.CellVariable.getArithmeticFaceValue`.
   This can have undesirable consequences, however. For one thing, the
   arithmetic face average of a non-linear function is not the same as the
   same non-linear function of the average argument, *e.g.*, for
   :math:`f(x) = x^2`,

   .. math::

      f(\frac{1+2}{2}) = \frac{9}{4} \neq 
      \frac{f(1) + f(2)}{2} = \frac{5}{2} 

   This distinction is not generally important for smoothly
   varying functions, but can dramatically affect the solution
   when sharp changes are present.  Also, for many problems, such
   as a conserved concentration field that cannot be allowed to
   drop below zero, a harmonic average is more appropriate than
   an arithmetic average.
   
   If you experience problems (unstable or wrong results, or 
   excessively small timesteps), you may need to explicitly supply the 
   desired :class:`~fipy.variables.faceVariable.FaceVariable` rather than letting :term:`FiPy` 
   assume one.

How do I represent boundary conditions?
---------------------------------------

What is a :class:`~fipy.boundaryConditions.fixedValue.FixedValue` boundary condition?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

This is simply a Dirichlet boundary condition by another name.

What does the :class:`~fipy.boundaryConditions.fixedFlux.FixedFlux` boundary condition actually represent?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

In :term:`FiPy` a :class:`~fipy.boundaryConditions.fixedFlux.FixedFlux` boundary condition object represents the
quantity 

.. math::

   \Gamma \vec{n} \cdot \nabla \phi - \vec{n} \cdot \vec{u} \phi

on a given boundary edge with :math:`\vec{n}` pointing out of the boundary.
The quantity :math:`\Gamma` represents the diffusion coefficient and :math:`\vec{u}`
represents the convection coefficient for a general convection-diffusion
equation of the type given in Eq. :eq:`num:gen`. See
:mod:`examples.convection.robin` for a usage case.

.. _FAQ-anyBoundaryCondition:

I can't get the :class:`~fipy.boundaryConditions.fixedValue.FixedValue` or :class:`~fipy.boundaryConditions.fixedFlux.FixedFlux` boundary condition objects to work right. What do I do now?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. currentmodule:: fipy.variables.faceVariable

There have been a number of questions on the mailing list about boundary
conditions and from the feedback it is clear that there are some
problematic issues with the design and implementation of the boundary
condition objects. We hope to rectify this in future releases. However, it
is possible to specify almost any boundary condition by using a rank 1
:class:`FaceVariable` to represent the external flux value and apply the
:meth:`~FaceVariable.getDivergence` method to this object and then use it as as a source
term in the given equation. The following code demonstrates how to
implement this technique. First define the coefficients,

>>> convectionCoeff = FaceVariable(..., rank=1)
>>> diffusionCoeff = FaceVariable(...)

where the ``convectionCoeff`` and ``diffusionCoeff`` are defined over
all the faces. We will define a third :class:`FaceVariable` to represent the
boundary source term and then set the values of the coefficients to zero on
the exterior faces.

>>> boundarySource = FaceVariable(..., rank=1)
>>> convectionCoeff.setValue(0, where=mesh.getExteriorFaces())
>>> diffusionCoeff.setValue(0, where=mesh.getExteriorFaces())
>>> boundarySource.setValue(vectorValues, where=mesh.getExteriorFaces())

The ``vectorValues`` quantity can be set to whatever value is required for
the particular boundary condition. The variable ``boundarySource`` could
be a variable that defines a relationship between other variables rather than
a simple container object. To finish off, the :meth:`boundarySource.getDivergence` object
must be added to the regular equation

>>> eqn = (TransientTerm() + ConvectionTerm(convectionSource) 
...        = DiffusionSource(diffusionCoeff) + boundarySource.getDivergence())

No other boundary conditions need to be applied. It may be necessary to reset
or update the values of ``boundarySource``, ``diffusionCoeff`` and
``convectionCoeff`` at each sweep if they are not automatically updated or
if the exterior values need to be reset to zero. For complex boundary
conditions, it is often easier to implement the technique described here
rather than trying to get the 
:class:`~fipy.boundaryConditions.fixedValue.FixedValue` and 
:class:`~fipy.boundaryConditions.fixedFlux.FixedFlux`
boundary conditions to work correctly.

.. _FAQ-inletOutletBoundaryCondition:

How do I apply an outlet or inlet boundary condition?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

There is no good way to do this with the standard boundary conditions in
:term:`FiPy` and thus one needs to use the method suggested above,
see :ref:`FAQ-anyBoundaryCondition`\.
Currently, boundary conditions for the :class:`~fipy.terms.convectionTerm.ConvectionTerm` assume a
:class:`~fipy.boundaryConditions.fixedFlux.FixedFlux` boundary condition with a ``value`` of 0. This is in
fact not the most intuitive default boundary condition, a natural outlet or
inlet boundary condition would in fact be more sensible. In order to apply an
inlet/outlet boundary condition one needs a separate exterior convection
coefficient (velocity vector) to hold the boundary values,

>>> convectionCoeff = FaceVaravble(..., rank=1)
>>> exteriorCoeff = FaceVariable(..., value=0, rank=1)

The ``exteriorCoeff`` can now be given non-zero values on
``inletOutletFaces`` and the ``convectionCoeff`` can be set to zero on
these faces.

>>> exteriorCoeff.setValue(convectionCoeff, where=inletOutletFaces)
>>> convectionCoeff.setValue(0, where=inletOutletFaces)

where the ``inletOutletFaces`` object are the faces over which the
inlet/outlet boundary condition applies. The divergence of the
``exteriorCoeff`` is then included in the equations with an
:class:`~fipy.terms.implicitSourceTerm.ImplicitSourceTerm`. This allows an implicit formulation for outlet
boundary conditions and an explicit formulation for inlet boundary
conditions, consistent with an upwind convection scheme.

>>> eqn = (TransientTerm() + ConvectionTerm(convectionCoeff)
...        + ImplicitSourceTerm(exteriorCoeff.getDivergence()) 
...        == DiffusionTerm(diffusionCoeff))

As in the previous section, the coefficient values may need updating on the
exterior faces between sweeps. See :mod:`examples.convection.source`
for an example of this usage.

How do I apply a fixed gradient?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

In general, it is not currently possible to apply a fixed gradient or Neumann
type boundary condition explicitly. Of course, it is often possible to use
:class:`~fipy.boundaryConditions.fixedValue.FixedValue` or
:class:`~fipy.boundaryConditions.fixedFlux.FixedFlux` boundary conditions to
mimic a fixed gradient condition. In the case when there is no convection, one
can simply use a :class:`~fipy.boundaryConditions.FixedFlux` condition and
multiply through by the diffusion coefficient to create the boundary condition,

>>> FixedFlux(value=gradient * diffusionCoeff, faces=myFaces)

where ``gradient`` is the value of the boundary gradient and
``myFaces`` are the faces over which the boundary condition applies.  If
the equation contains a :class:`~fipy.terms.convectionTerm.ConvectionTerm` and the boundary condition has
a zero gradient then one would use a 
:class:`~fipy.boundaryConditions.fixedValue.FixedValue` boundary condition of
the form

>>> FixedValue(value=phi.getFaceValue(), faces=myFaces)

This is not an "implicit" boundary condition so would in general require
sweeps to reach convergence. See :mod:`examples.convection.source`
for an example of this usage.
In the case of a non-zero gradient one would need to employ the techniques in
both :ref:`FAQ-anyBoundaryCondition`
and :ref:`FAQ-inletOutletBoundaryCondition`
without using either a
:class:`~fipy.boundaryConditions.fixedValue.FixedValue` or a 
:class:`~fipy.boundaryConditions.fixedFlux.FixedFlux` object.

How do I apply spatially varying boundary conditions?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The use of spatial varying boundary conditions is best demonstrated with an
example. Given a 2D equation in the domain :math:`0 < x < 1` and :math:`0 < y < 1` with
boundary conditions,

.. math::

  \phi = \left\{
            \begin{aligned} 
                xy &\quad \text{on $x>1/2$ and $y>1/2$} \\
                \vec{n} \cdot \vec{F} = 0 &\quad \text{elsewhere}
            \end{aligned}
        \right.

where :math:`\vec{F}` represents the flux. The boundary conditions in :term:`FiPy` can
be written with the following code,

>>> x, y = mesh.getFaceCenters()
>>> mask =  ((x < 0.5) | (y < 0.5))
>>> BCs = [FixedFlux(value=0, faces=mesh.getExteriorFaces() & mask),
...        FixedValue(value=x * y, faces=mesh.getExteriorFaces() & ~mask)]

The ``BCs`` list can then be passed to the equation's 
:meth:`~fipy.terms.term.Term.solve` method
when its called,

>>> eqn.solve(..., boundaryConditions=BCs)

Further demonstrations of spatially varying boundary condition can be found
in :mod:`examples.diffusion.mesh20x20`
and :mod:`examples.diffusion.circle`

.. %    http://thread.gmane.org/gmane.comp.python.fipy/726
   %    http://thread.gmane.org/gmane.comp.python.fipy/846

   %    \subsection{Fourth order boundary conditions}

   %    http://thread.gmane.org/gmane.comp.python.fipy/923

   %    \subsection{Periodic boundary conditions}

   %    http://thread.gmane.org/gmane.comp.python.fipy/135

   %    \subsection{Time dependent boundary conditions}

   %    http://thread.gmane.org/gmane.comp.python.fipy/2

   %    \subsection{Internal boundary conditions}

What does this error message mean?
----------------------------------

``ValueError: frames are not aligned``
   This error most likely means that you have provided a
   :class:`~fipy.variables.cellVariable.CellVariable` when :term:`FiPy` was
   expecting a :class:`~fipy.variables.faceVariable.FaceVariable` (or vice
   versa).

``MA.MA.MAError: Cannot automatically convert masked array to Numeric because data is masked in one o more locations.``
  This not-so-helpful error message could mean a number of things, but
  the most likely explanation is that the solution has become unstable
  and is diverging to :math:`\pm\infty`. This can be caused by taking too large
  a timestep or by using explicit terms instead of implicit ones.
      
``repairing catalog by removing key``
  This message (not really an error, but may cause test failures) can
  result when using the :mod:`scipy.weave` package via the
  :option:`--inline` flag. It is due to a bug in :term:`SciPy` that has been
  patched in their source repository:
  http://www.scipy.org/mailinglists/mailman?fn=scipy-dev/2005-June/003010.html.
      
``numerix Numeric 23.6``
  This is neither an error nor a warning. It's just a sloppy 
  message left in :term:`SciPy`:
  http://thread.gmane.org/gmane.comp.python.scientific.user/4349.

.. _FAQ-FlagsAndEnvironmentVariables:

How do I change FiPy's default behavior?
-------------------------------------------

:term:`FiPy` tries to make reasonable choices, based on what 
packages it finds installed, but there may be times that you 
wish to override these behaviors. 

Command-line Flags
~~~~~~~~~~~~~~~~~~

You can add any of the following flags after the name of a 
script you call from the command line

.. cmdoption:: --inline

   Causes many mathematical operations to be performed in C, rather than
   Python, for improved performance. Requires the :mod:`scipy.weave`
   package.

.. cmdoption:: --PySparse

   Forces the use of the :term:`PySparse` solvers. This flag takes precedence
   over the :envvar:`FIPY_SOLVERS` environment variable.

.. cmdoption:: --Trilinos

   Forces the use of the :term:`Trilinos` solvers. This flag takes precedence
   over the :envvar:`FIPY_SOLVERS` environment variable.
    
Environment Variables
~~~~~~~~~~~~~~~~~~~~~

You can set any of the following environment variables in the 
manner appropriate for your shell. If you are not running in 
a shell (*e.g.*, you are invoking :term:`FiPy` scripts from within IPython or 
IDLE), you can set these variables via the 
:const:`os.environ` dictionary, but you must do so before 
importing anything from the :mod:`fipy` package.

.. envvar:: FIPY_DISPLAY_MATRIX

   .. currentmodule:: fipy.terms.term

   If present, causes the graphical display of the solution matrix of each
   equation at each call of :meth:`~Term.solve` or :meth:`~Term.sweep`. If set
   to "``terms``," causes the display of the matrix for each
   :class:`Term` that composes the equation. Requires the :term:`Matplotlib`
   package.

.. envvar:: FIPY_INLINE

   If present, causes many mathematical operations to be performed in C,
   rather than Python. Requires the :mod:`scipy.weave` package.

.. envvar:: FIPY_INLINE_COMMENT

   If present, causes the addition of a comment showing the Python context
   that produced a particular piece of :mod:`scipy.weave` C code. Useful
   for debugging.

.. envvar:: FIPY_SOLVERS

   Forces the use of the specified suite of linear solvers. Valid
   (case-insensitive) choices are "``PySparse``" and
   "``Trilinos``".
    
.. envvar:: FIPY_VIEWER

   Forces the use of the specified viewer. Valid values are any
   :samp:`{<viewer>}` from the
   :samp:`fipy.viewers.{<viewer>}Viewer`
   modules. The special value of ``dummy`` will allow the script
   to run without displaying anything.
   
How can I tell if I'm running in parallel?
------------------------------------------

The easiest way to tell is to run one of the examples, e.g.,::

   $ mpirun -np 2 examples/diffusion/mesh1D.py
   
You should see two viewers open with half the simulation running in one of 
them and half in the other. If this does not look right (e.g., you get two viewers, both 
showing the entire simultion), or if you just want to be sure, you can run 
a diagnostic script::

   $ mpirun -np 3 examples/parallel.py
   
and you should see::

   mpi4py: processor 0 of 3 :: PyTrilinos: processor 0 of 3 :: FiPy: 5 cells on processor 0 of 3
   mpi4py: processor 1 of 3 :: PyTrilinos: processor 1 of 3 :: FiPy: 7 cells on processor 1 of 3
   mpi4py: processor 2 of 3 :: PyTrilinos: processor 2 of 3 :: FiPy: 6 cells on processor 2 of 3

If there is a problem with your parallel environment, it should be clear
that there is either a problem importing one of the required packages or
that there is some problem with the MPI environment. For example::

   mpi4py: processor 2 of 3 :: PyTrilinos: processor 0 of 1 :: FiPy: 10 cells on processor 0 of 1
   [my.machine.com:69815] WARNING: There were 4 Windows created but not freed.
   mpi4py: processor 1 of 3 :: PyTrilinos: processor 0 of 1 :: FiPy: 10 cells on processor 0 of 1
   [my.machine.com:69814] WARNING: There were 4 Windows created but not freed.
   mpi4py: processor 0 of 3 :: PyTrilinos: processor 0 of 1 :: FiPy: 10 cells on processor 0 of 1
   [my.machine.com:69813] WARNING: There were 4 Windows created but not freed.

indicates :ref:`MPI4PY` is properly communicating with MPI and is running
in parallel, but that :ref:`PYTRILINOS` is not, and is running three
separate serial environments. As a result, :term:`FiPy` is limited to three
separate serial operations, too. In this instance, the problem is that
although :ref:`PYTRILINOS` was compiled with MPI enabled, it was compiled
against a different MPI library than is currently available (and which
:ref:`MPI4PY` was compiled against). The solution is to rebuild
:ref:`PYTRILINOS` against the active MPI libraries.

Why don't my scripts work anymore?
----------------------------------

:term:`FiPy` has experienced two major API changes. The steps 
necessary to upgrade older scripts are discussed in 
:ref:`chap:UpdateFiPy`.

What if my question isn't answered here?
----------------------------------------
    
Please post your question to the
mailing list <http://www.ctcms.nist.gov/fipy/mail.html>
or file a Tracker request at <http://matforge.org/fipy/report>.
