This is a Python implementation of A. A. Eftekhari's Matlab/Octave FVM solver FVTool. Inspired by FiPy, it has only a fraction of FiPy's features. Boundary conditions, however, are much easier (and arguably more consistent) to implement in PyFVTool.
PyFVTool can discretize and solve the conservative form of transient convection-diffusion-reaction equation(s) with variable velocity field/diffusion coefficients and source terms:
with the following general form of boundary conditions (specified by constants a
, b
, and c
):
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 include:
- 1D, 2D and 3D Cartesian and cylindrical grids
- Second order (central difference) diffusion terms
- Second order (central difference), first order (upwind), and total variation diminishing (TVD) for advection terms
- Constant and linear source terms
- Backward and forward Euler for transient terms
- Dirichlet, Neumann, Robin, and periodic boundary conditions
- (Relatively) easy linearization of nonlinear PDEs
- Averaging methods (linear, arithmetic, geometric, harmonic, upwind, TVD)
- Divergence and gradient terms
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.
The code is under active development. Preliminary simulations match analytical solutions. More validation is under way, and the use of this PyFVTool toolbox in ongoing research projects will further consolidate the code and verify its validity. There is not much documentation for the code yet (help wanted!) but the example folder is the best place to start. If you know the topic, there is a good chance you will never need any documentations.
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
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
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
m1 = pf.Grid1D(Nx, Lx) # mesh object
bc = pf.BoundaryConditions(m1) # Neumann boundary condition by default
# switch the left boundary to Dirichlet: fixed concentration
bc.left.a[:] = 0.0
bc.left.b[:] = 1.0
bc.left.c[:] = c_left
# create a cell variable with initial concentration
c = pf.CellVariable(m1, c_init, bc)
# assign diffusivity to cells
D_cell = pf.CellVariable(m1, 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
bcterm = pf.boundaryConditionsTerm(bc)
eqn = [ pf.transientTerm(c, dt, 1.0),
-pf.diffusionTerm(D_face)]
# solve PDE
pf.solvePDE(c,
bcterm,
eqn)
t+=dt
if (nplot % Nskip == 0):
pf.visualizeCells(c)
nplot+=1