PyFVTool discretizes and numerically solves the conservative form of transient convection-diffusion-reaction equations with variable velocity field/diffusion coefficients and source terms. PyFVTool uses the finite volume method (FVM) to do this.
The partial differential equations that can be solved numerically with PyFVTool have the general form
\underbrace{\alpha\frac{\partial\phi}{\partial t}}_{\textrm{Transient term}}+\underbrace{\nabla \cdot \left(\mathbf{u}\phi\right)}_{\text{Advection term}}+\underbrace{\nabla \cdot (-\mathcal{D}\nabla\phi)}_{\text{Diffusion term}}+\underbrace{\beta\phi}_{\text{Linear source term}}+\underbrace{\gamma}_{\text{Constant source term}}=0
with the following general form of boundary conditions (specified by constants a
, b
, and c
):
a\nabla\phi \cdot \mathbf{e}+b\phi=c
An important feature of PyFVTool is that it is 'pure scientific Python' (i.e. it needs only Python and the standard scientific computing libraries numpy
, scipy
and matplotlib
to run). Further optional dependencies may appear in the future, e.g., for increasing the computational speed via optimised numerical libraries, but these will remain optional.
PyFVTool is a Python implementation of A. A. Eftekhari's Matlab/Octave FVM solver FVTool. It was strongly inspired by FiPy, but it has only a fraction of FiPy's features. Boundary conditions, however, are much easier (and arguably more consistently) implemented in PyFVTool.
PyFVTool is limited to calculations on structured meshes (regular grids). It is oriented to calculation of heat and mass transport phenomena (diffusion-advection-reaction) for the frequent cases where the flow velocity field is already known (or where flow is absent). It is not particularly suited for fluid dynamics (solving Navier-Stokes), which requires implementation of further numerical schemes on top of the current PyFVTool (simulkade knows how). For fluid dynamics, other specialized finite-volume codes exist.
The finite-volume discretization schemes in PyFVTool include:
PyFVTool is under active development. Several test cases have been validated to match analytical solutions. More validation is under way, in particular through the use of this toolbox in ongoing research projects. There is not much documentation for the code yet (help wanted!) but the example
and example-notebooks
folders are the best places to start. The latter folder contains annotated Jupyter Notebooks. From the examples, it is easy to understand how to set up finite-volume solvers for heat and mass transfer.
For now, install PyFVTool directly from the GitHub repository using pip
. You will need Python 3.9
or higher and numpy
, scipy
, and matplotlib
:
pip install git+https://github.com/FiniteVolumeTransportPhenomena/PyFVTool.git
If you'd like to use PyFVTool in Google Colab, you can enter the following in the first cell of a Colab Notebook:
!pip install git+https://github.com/FiniteVolumeTransportPhenomena/PyFVTool.git
This will install PyFVTool in the current Colab instance, and make it available for import in the Notebook.
It is convenient to use the Anaconda/miniconda Python distributions and set up a specific environment for PyFVTool (we'll call the environment pyfvtool_user
).
This requires three commands to be launched from the command-line prompt.
conda create --name pyfvtool_user numpy scipy matplotlib spyder jupyterlab tqdm
conda activate pyfvtool_user
pip install git+https://github.com/FiniteVolumeTransportPhenomena/PyFVTool.git
Of course, do not forget to conda activate pyfvtool_user
the environment every time you run Python code that uses PyFVTool.
If you would like to work on the source code, it is possible to install a development version using pip
. See CONTRIBUTING.md
Here is a simple example of a 1D transient diffusion equation:
import pyfvtool as pf
# Solving a 1D diffusion equation with a fixed concentration
# at the left boundary and a closed boundary on the right side
# Calculation parameters
Nx = 20 # number of finite volume cells
Lx = 1.0 # [m] length of the domain
c_left = 1.0 # left boundary concentration
c_init = 0.0 # initial concentration
D_val = 1e-5 # diffusion coefficient (gas phase)
t_simulation = 7200.0 # [s] simulation time
dt = 60.0 # [s] time step
Nskip = 10 # plot every Nskip-th profile
# Define mesh
mesh = pf.Grid1D(Nx, Lx)
# Create a cell variable with initial concentration
# By default, 'no flux' boundary conditions are applied
c = pf.CellVariable(mesh, c_init)
# Switch the left boundary to Dirichlet: fixed concentration
c.BCs.left.a[:] = 0.0
c.BCs.left.b[:] = 1.0
c.BCs.left.c[:] = c_left
c.apply_BCs()
# Assign diffusivity to cells
D_cell = pf.CellVariable(mesh, D_val)
D_face = pf.geometricMean(D_cell) # average value of diffusivity at the interfaces between cells
# Time loop
t = 0
nplot = 0
while t<t_simulation:
# Compose discretized terms for matrix equation
eqnterms = [ pf.transientTerm(c, dt, 1.0),
-pf.diffusionTerm(D_face)]
# Solve PDE
pf.solvePDE(c, eqnterms)
t+=dt
if (nplot % Nskip == 0):
pf.visualizeCells(c)
nplot+=1