\documentclass[article,nojss]{jss}
\DeclareGraphicsExtensions{.pdf,.eps,.jpeg}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Add-on packages and fonts
\usepackage{graphicx}
\usepackage{amsmath}
\newcommand{\noun}[1]{\textsc{#1}}
%% Bold symbol macro for standard LaTeX users
\providecommand{\boldsymbol}[1]{\mbox{\boldmath $#1$}}
%% Because html converters don't know tabularnewline
\providecommand{\tabularnewline}{\\}
\usepackage{array} % tabel commands
\setlength{\extrarowheight}{0.1cm}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% User specified LaTeX commands.
\newcommand{\rt}{\textbf{\textsf{ReacTran }}}
\newcommand{\rs}{\textbf{\textsf{rootSolve }}}
\newcommand{\R}{\proglang{R }}
\newcommand{\ds}{\textbf{\textsf{deSolve }}}
\newcommand{\dd}{\textbf{\textsf{ddesolve }}}
\newcommand{\rb}[1]{\raisebox{1.5ex}{#1}}
\title{Solving partial differential equations, using \R package \rt}
\Plaintitle{Solving partial differential equations using R package ReacTran}
\Keywords{Partial Differential Equations, hyperbolic, parabolic,
elliptic, \R}
\Plainkeywords{Partial Differential Equations, hyperbolic, parabolic,
elliptic, R}
\author{Karline Soetaert and Filip Meysman\\
Royal Netherlands Institute of Sea Research (NIOZ)\\
Yerseke\\
The Netherlands
}
\Plainauthor{Karline Soetaert and Filip Meysman}
\Abstract{
\R-package \rt \citep{ReacTran_paper} contains functions to solve reactive-transport equations, as used e.g. in the environmental sciences.
Essentially, it
\begin{enumerate}
\item Provides functions that subdivide the spatial extent into a number of discrete grid cells.
\item Approximates the advective-diffusive transport term by finite differences or finite volumes.
\end{enumerate}
The main package vignette \citep{ReacTran-vignette} explains how \rt can be used to model reaction-transport phenomena.
However, the functions from \rt can be use to solve more general types of partial differential equations ($\leq$ order 2).
In this vignette, show how the package can be used to solve partial differential equations of the parabolic, hyperbolic and elliptic type, providing one example each.
}
%% The address of (at least) one author should be given
%% in the following format:
\Address{
Karline Soetaert\\
Royal Netherlands Institute of Sea Research (NIOZ)\\
4401 NT Yerseke, Netherlands\\
E-mail: \email{karline.soetaert@nioz.nl}\\
URL: \url{http://www.nioz.nl}\\
\\
Filip Meysman\\
Royal Netherlands Institute of Sea Research (NIOZ)\\
4401 NT Yerseke, Netherlands\\
E-mail: \email{filip.meysman@nioz.nl}\\
URL: \url{http://www.nioz.nl}\\
}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% R/Sweave specific LaTeX commands.
%% need no \usepackage{Sweave}
%\VignetteIndexEntry{Solving parabolic, hyperbolic and elliptic partial differential equations in R}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Begin of the document
\begin{document}
\SweaveOpts{engine=R,eps=FALSE}
\SweaveOpts{keep.source=TRUE}
<>=
library(ReacTran)
options(prompt = " ")
@
\maketitle
\section{Partial differential equations}
In \textbf{partial differential equations }(PDE), the function has several
independent variables (e.g. time and depth) and contains their partial
derivatives.
A first step to solve partial differential equations (PDE), is to discretise one or more of the independent variables.
Usually, the independent variable ``time'' is not discretised, while other variables (e.g. spatial axes) are discetised, so that a set of ODE is obtained, which can be solved with appropriate initial values solvers from package \ds \citep{deSolve}.
This technique, the method-of-lines, applies to hyperbolic and parabolic PDEs.
For time-invariant problems, usually all independent variables are discretised, and the resulting algebraic equations solved with root-solving functions from package \rs
\citep{rootSolve}.
Functions \code{tran.1D}, \code{tran.2D}, and \code{tran.3D} from
\R package \pkg{ReacTran} implement finite difference approximations of the
general diffusive-advective transport equation, which for 1-D is:
\begin{eqnarray*}
-\frac{1}{A_x \xi_x}\cdot (\frac{\partial}{\partial x}
A_x \cdot (- D \cdot \frac{\partial \xi_x C}{\partial x})
- \frac{\partial}{\partial x}
(A_x \cdot v \cdot \xi_x C))
\end{eqnarray*}
Here $D$ is the "diffusion coefficient", $v$ is the "advection rate", and $A_x$ and $\xi$ are the surface area and volume fraction respectively.
Assuming that $A$, $\xi$, $D$ and $v$ are constant along $x$, we can rewrite this in a more general form:
\begin{eqnarray*}
D \frac{\partial^2C}{\partial x^2} - u \frac{\partial C}{\partial x}
\end{eqnarray*}
In which the first term is a second-order, the second term a first-order derivative.
\R-function \code{tran.1D} is defined as (simplified):
\begin{verbatim}
tran.1D <- function (C, C.up = C[1], C.down = C[length(C)], flux.up = NULL,
flux.down = NULL, a.bl.up = NULL, a.bl.down = NULL, D = 0,
v = 0, AFDW = 1, VF = 1, A = 1, dx, ...)
\end{verbatim}
where \code{C.up} and \code{C.down} are the upstream and downstream boundary values, \code{flux.up} and \code{flux.down} are the upstream and downstream fluxes, \code{v} and \code{D} are the advection and diffusion coefficient respectively, \code{A} is the surface area, \code{x} contains the grid, and \code{VF} is the volume fraction ($\xi$).
For the other arguments, see the help file of \code{tran.1D}.
A suitable \emph{grid} can be generated with functions \code{setup.grid.1D} and
\code{setup.grid.2D} (there is no corresponding 3D function), while a \emph{property} can be added to this grid using functions \code{setup.prop.1D}, and \code{setup.prop.2D}.
These latter two functions are useful to have the variable surface areas, volume fractions, advection and diffusion rates being defined at the correct places of the grid.
These functions are defined as (simplified):
\begin{verbatim}
setup.grid.1D <- function(x.up = 0, x.down = NULL, L = NULL, N = NULL,
dx.1 = NULL, ...)
setup.grid.2D <- function(x.grid = NULL, y.grid = NULL)
setup.prop.1D <- function (func = NULL, value = NULL, xy = NULL,
interpolate = "spline", grid, ...)
setup.prop.2D <- function (func = NULL, value = NULL, grid, y.func = func,
y.value = value, ...)
\end{verbatim}
\clearpage
\section{A parabolic PDE}
As an example of the parabolic type, consider the 1-D diffusion-reaction model, in spherical,
cylindrical and cartesian coordinates, defined for $r$ in $[0, 10]$:
\begin{eqnarray*}
\frac{\partial C}{\partial t} &=& \frac{1}{r^2}\cdot \frac{\partial}{\partial r}
\left(r^2 \cdot D \cdot \frac{\partial C}{\partial r}\right) - Q \\
\frac{\partial C}{\partial t} &=& \frac{1}{r}\cdot \frac{\partial}{\partial r}
\left(r \cdot D \cdot \frac{\partial C}{\partial r}\right) - Q \\
\frac{\partial C}{\partial t} &=& \frac{\partial}{\partial r}
\left(D \cdot \frac{\partial C}{\partial r}\right) - Q
\end{eqnarray*}
with $t$ the time, $r$ the (radial) distance from the origin,
$Q$, the consumption rate, and with boundary conditions
(values at the model edges):
\begin{eqnarray*}
\frac{\partial C}{\partial r}_{r=0} &=& 0 \\
C_{r=10} &=& Cext
\end{eqnarray*}
To solve this model in \R{}, first the 1-D model \code{Grid} is
defined; it divides 10 cm (\code{L}) into 1000 equally-sized boxes (\code{N}).
<<>>=
Grid <- setup.grid.1D(N = 1000, L = 10)
@
Next the properties $r$ and $r^2$ are defined on this grid:
\SweaveOpts{keep.source=TRUE}
<<>>=
r <- setup.prop.1D(grid = Grid, func = function(r) r)
r2 <- setup.prop.1D(grid = Grid, func = function(r) r^2)
@
\SweaveOpts{keep.source=FALSE}
The model equation includes a transport term, approximated by
\pkg{ReacTran} function \code{tran.1D} and a consumption term
(\code{Q}); only the downstream boundary condition, prescribed as a
concentration (\code{C.down}) needs to be specified, as the
zero-gradient at the upstream boundary is the default:
<<>>=
library(ReacTran)
pde1D <-function(t, C, parms, A = 1) {
tran <- tran.1D (C = C, A = A, D = D, C.down = Cext, dx = Grid)$dC
list(tran - Q) # the return value: rate of change
}
@
The model parameters are defined:
<<>>=
D <- 1 # diffusion constant
Q <- 1 # uptake rate
Cext <- 20
@
\subsection{Steady-state solution}
In a first application, the model is solved to \emph{steady-state},
which retrieves the condition where the concentrations are invariant,
e.g. for the cylindrical coordinate case:
\[
0 = \frac{1}{r^2}\cdot \frac{\partial}{\partial r}
\left(r^2 \cdot D \cdot \frac{\partial C}{\partial r}\right) - Q
\]
In \R, steady-state conditions can be estimated using functions from
package \rs which implement amongst others a Newton-Raphson algorithm
\citep{Press92}. For 1-dimensional models, \code{steady.1D} should be
used. The initial ``guess'' of the steady-state solution (\code{y})
is unimportant; here we take simply \code{N} random
numbers. Argument \code{nspec = 1} informs the solver that only one component
is described.
Although a system of 1000 equations needs to be solved, this takes only a
fraction of a second:
\SweaveOpts{keep.source=TRUE}
<<>>=
library(rootSolve)
Cartesian <- steady.1D(y = runif(Grid$N),
func = pde1D, parms = NULL, nspec = 1, A = 1)
Cylindrical <- steady.1D(y = runif(Grid$N),
func = pde1D, parms = NULL, nspec = 1, A = r)
@
<<>>=
print(system.time(
Spherical <- steady.1D(y = runif(Grid$N),
func = pde1D, parms = NULL, nspec = 1, A = r2)
))
@
The values of the state-variables (\code{y}) are plotted against the
radial distance, in the middle of the grid cells (\code{Grid\$x.mid}).
We use \code{rootSolve}'s plot method to do so. This function accepts
several steady-state outputs at once:
<