A finite-volume tool for discretizing and solving transient advection–diffusion equations on structured meshes, in Cartesian, cylindrical and radial coordinates. Requires Julia 1.10 or newer.
] add JFVToolJFVTool carries no plotting dependency: it hands you cell values that any plotting
package can draw (the examples use Plots). The older
JFVMvis.jl package, including its
Mayavi-based 3D visualization, is separate and not required.
Formerly
JFVM. This package was called JFVM up to and including its releases in Julia's old METADATA registry. It was renamed to JFVTool for the General registry — to match the rest of the FVTool family, and because a four-letter all-caps name cannot be registered. The code is unchanged: replaceusing JFVMwithusing JFVTool.
You can solve the following PDE (or a subset of it):

with the following boundary conditions:

Believe it or not, the above equations describe the majority of the transport phenomena in chemical and petroleum engineering and similar fields.
This code is a Matlabesque implementation of my Matlab finite volume tool. The code is not in its most beautiful form, but it works if you believe my words. Please remember that the code is written by a chemical/petroleum engineer. Petroleum engineers are known for being simple-minded folks and chemical engineers have only one rule: "any answer is better than no answer". You can expect to easily discretize a linear transient advection-diffusion PDE into the matrix of coefficients and RHS vectors. Domain shape is limited to rectangles, circles (or a section of a circle), cylinders, and soon spheres. The mesh can be uniform or nonuniform:
- Cartesian (1D, 2D, 3D)
- Cylindrical (1D, 2D, 3D)
- Radial (2D r and \theta)
You can have the following boundary conditions or a combination of them on each boundary:
- Dirichlet (constant value)
- Neumann (constant flux)
- Robin (a linear combination of the above)
- Periodic (so funny when visualize)
It is relatively easy to use the code to solve a system of coupled linear PDE's and not too difficult to solve nonlinear PDE's.
Each mesh carries an explicit coordinate-system type, and the discretization
kernels dispatch on it (rather than on the legacy numeric dimension tag). The
constructors are unchanged; the type is selected automatically:
| Constructor | Coordinate system | dimension (legacy) |
|---|---|---|
createMesh1D |
Cartesian1D |
1.0 |
createMeshCylindrical1D |
Cylindrical1D |
1.5 |
createMesh2D |
Cartesian2D |
2.0 |
createMeshCylindrical2D |
Cylindrical2D |
2.5 |
createMeshRadial2D |
Radial2D |
2.8 |
createMesh3D |
Cartesian3D |
3.0 |
createMeshCylindrical3D |
Cylindrical3D |
3.2 |
The coordinate system is available as m.coordinatesystem, with helpers
is_1d(m), is_2d(m), is_3d(m). The numeric m.dimension field and the
coordinatesystem_from_dimension / dimension_from_coordinatesystem bridges are
retained for backward compatibility; prefer dispatching on the coordinate-system
types in new code.
solveLinearPDE and solveLinearPDE! accept a pluggable solver strategy
(default behavior is unchanged):
solveLinearPDE(m, M, RHS) # DirectSolver() — sparse LU via `\`
solveLinearPDE(m, M, RHS; solver = DirectSolver()) # explicit default
# Reuse one factorization across a fixed-matrix transient loop (only RHS changes):
fs = FactorizedSolver(M)
for step in 1:nsteps
phi = solveLinearPDE(m, M, RHS; solver = fs) # M is factored once
# ... update RHS ...
end
solveLinearPDE(m, M, RHS; solver = MUMPSSolver()) # optional MUMPS backend (import MUMPS first)] add JFVToolor, for the development version:
] add https://github.com/FiniteVolumeTransportPhenomena/JFVTool.jlCopy and paste the following to solve a transient diffusion equation:
using JFVTool
Nx = 10
Lx = 1.0
m = createMesh1D(Nx, Lx)
BC = createBC(m) # all-Neumann by default
BC.left.a .= 0.0 # left boundary: Dirichlet c = 1
BC.left.b .= 1.0
BC.left.c .= 1.0
BC.right.a .= 0.0 # right boundary: Dirichlet c = 0
BC.right.b .= 1.0
BC.right.c .= 0.0
c_old = createCellVariable(m, 0.0, BC) # initial condition
D_val = 1.0 # diffusion coefficient
D_cell = createCellVariable(m, D_val) # assigned to the cells
D_face = harmonicMean(D_cell) # averaged onto the faces
N_steps = 20
dt = sqrt(Lx^2 / D_val) / N_steps # time step
M_diff = diffusionTerm(D_face) # matrix for the diffusion term
M_bc, RHS_bc = boundaryConditionTerm(BC) # matrix and RHS for the BCs
for i = 1:5
M_t, RHS_t = transientTerm(c_old, dt, 1.0)
M = M_t - M_diff + M_bc # add up the sparse matrices ...
RHS = RHS_bc + RHS_t # ... and the right-hand sides
global c_old = solveLinearPDE(m, M, RHS)
end
internalCells(c_old) # the solution on the physical cellsChange the mesh line to m = createMesh2D(Nx, Nx, Lx, Lx) and the same code
solves the 2D problem:
The examples/ folder is organised into:
examples/notebooks— Jupyter notebooks, from a first steady problem through source terms, nonlinear coefficients and Cahn–Hilliard,examples/tutorial— the same ground as short.jlscripts,examples/cases— worked engineering problems (heat conduction, plug-flow reactor) that verify themselves against analytical solutions.
See examples/README.md for what each one covers and how
to set up the environment they use.
Elsewhere:
If you have used the code in your research, please cite it as
Ali A Eftekhari. (2017, August 23). JFVM.jl: A Finite Volume Tool for Solving Advection-Diffusion Equations. Zenodo. http://doi.org/10.5281/zenodo.847056
@misc{ali_a_eftekhari_2017_847056,
author = {Ali A Eftekhari},
title = {{JFVM.jl: A Finite Volume Tool for Solving
Advection-Diffusion Equations}},
month = aug,
year = 2017,
doi = {10.5281/zenodo.847056},
url = {https://doi.org/10.5281/zenodo.847056}
}
