1 Introduction

The study of flow and transport phenomena in sedimentary rocks is important in a range of scientific and engineering applications, such as water management, contaminant transport (Sahimi 2011), oil & gas recovery (Lake 1996) and geological carbon storage (Szulczewski et al. 2012; Popova et al. 2012). With the advent of X-ray microtomography, high-resolution images of the pore space of sedimentary rocks (a few microns voxel size) have been made available. These three-dimensional images can be used to model flow and transport properties (Blunt et al. 2013), as well as electric and elastic properties (Saenger 2008; Arns et al. 2002) in a wide range of rocks, from relatively homogeneous sandstones to heterogeneous carbonates (Bijeljic et al. 2011). The availability of high-resolution tomographic images of rock cores makes it possible to study in detail flow and transport phenomena at the pore scale. Moreover, it creates an impetus for the development and adaptation of traditional fluid simulation techniques.

The Lagrangian approach provides a convenient framework to study transport phenomena in porous media. Unlike Eulerian methods, Lagrangian methods do not suffer from numerical dispersion and, for advection-dominated problems, deliver a far superior computational performance (e.g. Batycky et al. 1997; Datta-Gupta and King 2007). Streamline tracing is a Lagrangian method where solute particles are followed along streamlines traced in the simulation domain. In a steady flow field, streamlines are the solutions of:

$$\begin{aligned} \frac{\text {d}\,\mathbf {x}}{\text {d} \tau } = \mathbf {V}(\mathbf {x}) \end{aligned}$$
(1)

where \(\mathbf {V}(\mathbf {x})\) is the velocity field previously calculated on a computational grid and \(\tau \) is the time of flight (tof), the time that a particle following the streamline needs to travel a given distance. The solution of the Navier–Stokes equations is a necessary input for all streamline tracing algorithms at the pore scale. At the field scale the prerequisite is the solution of the pressure equation with the assumption of a Darcy regime between adjacent grid cells, from which the velocity field is then recovered.

The Pollock algorithm (Pollock 1988) is widely used to trace streamlines in field-scale models, where the average flow is described by Darcy’s law, in both hydrocarbon reservoirs (Batycky et al. 1997; Datta-Gupta and King 2007) and aquifers (Donato et al. 2003, Obi and Blunt 2004). It calculates the velocities through a linear interpolation of the (staggered) grid velocities in each coordinate direction independently. In doing so, Eq. (1) has an analytical solution for each grid element, and these solutions are then connected to trace the streamlines for the whole domain. While originally developed for rectangular grids, Pollock’s algorithm was later adapted for unstructured grids (Prevost et al. 2002; Matringe et al. 2006).

However, despite its broad range of applications, the Pollock method cannot be immediately applied at the pore scale. It does not obey the more strict no-slip boundary conditions necessary when simulating fluid flow and transport on segmented micro-computed tomography (micro-CT) images consisting of void and solid voxels. Pore-space images inevitably have to make a trade-off between resolution (having many image voxels to describe each pore) and the overall size of the system, to capture the connectivity and heterogeneity of the sample. As a result, generally only a few voxels span the smallest pores in the image. As we show later in Sect. 4, the application of the Pollock algorithm, with the wrong boundary condition at the solid interface, leads to significant errors, particularly when the distance between solid boundaries is a small number of voxels.

In this paper we propose an extension of the Pollock method where both the normal and tangential components of the interpolated velocity field are zero at the pore-solid walls, thus respecting the no-flow boundary condition. Our main result is a semi-analytical streamline tracing algorithm capable of simulating transport properties directly in three-dimensional pore-space images. Mostaghimi et al. (2012) attempted to solve the same problem, but for transport simulations only calculated the velocity at a point in the pore space and then took a small time step with this fixed velocity. Here we trace streamlines semi-analytically, allowing the very rapid and accurate prediction of advection-dominated transport.

The paper is organized as follows: we start with a brief description of the Pollock algorithm; then we proceed explaining why and how it has to be adapted for use at the pore scale and present our methodology. We explore the differences between the original method and our improved version for flow between parallel plates, whose analytical solution is known. Next we exemplify the application of the new transport method using the geometry and velocity fields of two micro-CT images of rock samples: a carbonate and a sandstone. We show how the results of time-of-flight distributions and breakthrough curves (BTCs, concentration of solute particles versus time) calculated with our method differ qualitatively from the ones obtained using the standard Pollock algorithm. In particular, the BTCs have the long tails and power law like behaviour characteristic of non-Fickian transport (Haggerty et al. 2000). We conclude by highlighting the most important steps of the method and explaining how to incorporate diffusion.

2 Flow Simulation and Streamline Tracing in Micro-CT Images

2.1 Flow Model

Incompressible flow in a porous medium is described by the Navier–Stokes equation:

$$\begin{aligned} \rho \left( \frac{\partial \mathbf {V}}{\partial t} + \mathbf {V}.\nabla \mathbf {V}\right) = -\nabla P +\mu \nabla ^{2}\mathbf {V} \end{aligned}$$
(2)

subject to the incompressibility condition \(\nabla . \mathbf {V} = 0 \), where \(\mu \) is the fluid viscosity, \(\rho \) the fluid density, \(\mathbf {V}\) the velocity, and P the pressure. Throughout our simulations we make \(\mu = 0.001 \, \text {Pa\,s}\) and \(\rho = 1000\,\text {kg/}\text {m}^3\), the values for water. Finite-difference (volume) and lattice-Boltzmann are the most common methods to solve for incompressible flow at the pore scale (Manwart et al. 2002). We use the finite-volume method implemented in OpenFoam (an open source library http://www.openfoam.org) and described in Raeini et al. (2012), Bijeljic et al. (2013) to solve the Navier–Stokes equation with constant pressure boundary conditions at inlet and outlet. No flow on the pore–solid boundaries is imposed. The resulting velocity field is used as input to the streamline tracing algorithms.

2.2 Streamline Tracing—Pollock’s Algorithm

A streamline is a line whose tangential vector is instantaneously parallel to the velocity field at every point of the flow domain. Pollock’s algorithm (Pollock 1988) is a semi-analytical approach that calculates the voxel time of flight (the amount of time a tracer particle takes to cross the voxel) as well as particle entry and exit positions assuming that the velocity in any point inside a grid cell is a linear interpolation of the face velocities. This method was developed for large-scale flows governed by Darcy’s law. Here we are studying a different problem: pore-scale dynamics described by the Navier–Stokes equation. Hence, we will need to adapt and extend the methodology to account for the different boundary conditions encountered.

The computational domain is a cubic lattice where voxels assume binary values, representing a pore or a solid. The velocity field is represented in a staggered grid so that its components are located at each face of the voxel and denoted by \(u_1\) and \(u_2\) (faces perpendicular to the x-direction), \(v_1\) and \(v_2\) (perpendicular to the y-direction) and \(w_1\) and \(w_2\) (perpendicular to the z-direction), see Fig. 1. The velocity field at a point with coordinates (xyz) inside a grid block can be obtained through a linear interpolation of the face velocities:

$$\begin{aligned}&V_x(x,y,z) = u_1 + \frac{\varDelta u}{\varDelta x}(x-x_1) \end{aligned}$$
(3a)
$$\begin{aligned}&V_y(x,y,z)= v_1 + \frac{\varDelta v}{\varDelta y}(y-y_1) \end{aligned}$$
(3b)
$$\begin{aligned}&V_z(x,y,z) = w_1 +\frac{\varDelta w}{\varDelta z}(z-z_1) \end{aligned}$$
(3c)

where \(x_1\), \(y_1\) and \(z_1\) are the coordinates of the corner voxel marked in Fig. 1, and \(\varDelta x\), \(\varDelta y\) and \(\varDelta z\) are the linear dimensions of the grid block while \(\varDelta u = u_2-u_1\), \(\varDelta v = v_2 -v_1\) and \(\varDelta w = w_2 -w_1\). For a particle with initial position \((x_p,y_p,z_p)\) and using the interpolated velocity, it is possible to calculate the time required to leave the voxel through either one of its faces:

$$\begin{aligned}&\tau _x =\frac{\varDelta x}{\varDelta u}\ln \left[ \frac{u_2\varDelta x}{u_1\varDelta x + \varDelta u(x_p -x_1)}\right] \end{aligned}$$
(4a)
$$\begin{aligned}&\tau _y = \frac{\varDelta y}{\varDelta v}\ln \left[ \frac{v_2\varDelta y}{v_1\varDelta y +\varDelta v(y_p -y_1)}\right] \end{aligned}$$
(4b)
$$\begin{aligned}&\tau _z = \frac{\varDelta z}{\varDelta w}\ln \left[ \frac{w_2\varDelta z}{w_1\varDelta z + \varDelta w(z_p -z_1)}\right] \end{aligned}$$
(4c)

and the particle will leave through the face with the smallest travel time:

$$\begin{aligned} \tau _{e} = \text {min}(\tau _x,\tau _y,\tau _z) \end{aligned}$$
(5)

The exit coordinates are obtained inverting Eq. 4:

$$\begin{aligned}&x_e = x_1 -\frac{u_1\varDelta x}{\varDelta u}+ \left[ \frac{u_1\varDelta x}{\varDelta u} + (x_p-x_1)\right] \exp \left( \frac{\varDelta u\tau _e}{\varDelta x}\right) \end{aligned}$$
(6a)
$$\begin{aligned}&y_e =y_1 -\frac{v_1\varDelta y}{\varDelta v}+ \left[ \frac{v_1\varDelta y}{\varDelta v} + (y_p -y_1)\right] \exp \left( \frac{\varDelta v\tau _e}{\varDelta y}\right) \end{aligned}$$
(6b)
$$\begin{aligned}&z_e = z_1 -\frac{w_1\varDelta z}{\varDelta w}+ \left[ \frac{w_1\varDelta z}{\varDelta w} + (z_p-z_1)\right] \exp \left( \frac{\varDelta w\tau _e}{\varDelta z}\right) \end{aligned}$$
(6c)

Streamlines are then traced concatenating a series of line segments crossing the voxels inside the pore space: the point where a segment leaves a voxel is the entrance point for the next one.

Fig. 1
figure 1

Voxel representing a void voxel and its velocity components. In the staggered grid the velocities are assigned to the faces of the voxels

3 Semi-analytical Streamline Tracing in Pore-scale Images

Pore-scale images segregate pores and solids; each voxel is either one or the other, while in field-scale models the grid cells are assigned a porosity (fraction of void space) and a permeability. The assumption that velocities can be calculated via linear interpolations inside the voxels does not apply to pore-scale models. These velocities do not obey the strict no-flow condition at the solid walls, meaning that their tangential component is not zero. To address this problem we modify the original Pollock algorithm postulating analytical expressions for the velocities where both the normal and tangential components are zero at the pore-solid walls. These new velocities are then integrated to obtain the particle trajectories, and the streamlines are traced in the usual way: adding streamline increments through each voxel. A complication is that now the pore-space geometry is the crucial factor, requiring that each pore geometry must be treated separately. In a cubic lattice, there are sixty-three possible configurations for a voxel with solid boundaries—six for one solid, fifteen for two solids, twenty for three solids and fifteen for four solids, plus the degenerate cases (the velocity is everywhere zero) with five solid boundaries and the case of an isolated pore (six boundaries). Each of the non-degenerate geometries will have a particular choice of velocity that has to be integrated and solved for the exit times and positions, just like in the original Pollock method.

There are fifty-six cases of potential interest plus the case without any solid boundary—treated with the standard Pollock algorithm. We note that the number of cases of interest can be reduced by analysing the velocity field of the rock under study. In the images of real rocks that we study (see examples in Sect. 5), the voxels with three and four solid boundaries contribute with less than 5 % of the pore space and 0.3 % of the flux, having negligible impact on the total flow across the sample, see Table 1. Although we derived the full semi-analytical expressions for the time of flight and exit coordinates for these blocks, in the results presented later we use the standard Pollock method in these cases for simplicity.

Table 1 Percentage of the total pore space and to the total flux across the samples (Bentheimer and Ketton) of the voxels with different numbers of solid neighbours

Our approach is an extension of the Pollock method, where the components of the velocity vector vary linearly with coordinate direction between average values at each voxel face. We adapt this to allow the components of the velocity to vary linearly or quadratically with each direction, subject to the following three constraints. Firstly, the velocity field everywhere is divergence free (incompressible flow). Secondly, the no-flow boundary conditions at the solid are strictly obeyed. Thirdly, the average velocity normal to a face is the same as computed from the numerical solution of the Navier–Stokes equations. This ensures conservation of volume. In principle there is also a pressure field within each grid block that is consistent with this velocity and the Navier–Stokes equations. However, as this pressure is not used in the simulations we do not need to compute it. These constraints are sufficient to define the velocity field uniquely for every arrangement of solid boundaries. The time of flight can then be computed analytically from the known velocity field.

To demonstrate our method we show the derivation of the analytical expressions for the case of voxels with one solid neighbour. The velocities, transit times and exit points for the cases with two, three and four solid boundaries are shown in the appendices.

3.1 One Solid Boundary

Given a pore voxel in lattice location (ijk), there are six possible positions for the neighbouring solid: \((i+1,j,k)\), \((i-1,j,k)\), \((i,j+1,k)\), \((i,j-1,k)\), \((i,j,k+1)\) or \((i,j,k-1)\). We show the derivation of the case where the limiting solid voxel is in the position \((i+1,j,k)\), Fig. 2. The other five cases with one solid boundary can be derived in the same way.

Fig. 2
figure 2

Pore voxel limited by a solid voxel in lattice position \((i+1,j,k)\)

As in the original Pollock method, the time of flight must be calculated in each direction according to the velocity distribution inside the pore. It is necessary to define a velocity field satisfying the no-slip boundary condition at the pore-solid wall and the conservation of flux.

Assuming that the tangential component y varies linearly with the distance from the solid wall and requiring that it takes its average values \(v_1\) and \(v_2\) at the cell faces, we can write:

$$\begin{aligned} V_y =\frac{\alpha }{\varDelta x}(x_2 -x)\left[ v_1 + \frac{\varDelta v}{\varDelta y}(y-y_1)\right] \end{aligned}$$
(7)

then, the average velocity when \(y=y_1\) is:

$$\begin{aligned} \overline{V_y} = \frac{1}{\varDelta x \varDelta z}\int _{z_1}^{z_2}\!\int _{x_1}^{x_2} \alpha v_1 \frac{x_2 -x}{\varDelta x} \, \mathrm {d}x \,\mathrm {d}z\, = \frac{\alpha v_1}{2} \end{aligned}$$
(8)

From the condition \(\overline{V_y} = v_1\), we have \( \alpha =2\). \(V_z\) can be calculated in an analogous way. The component x of the velocity is obtained applying the condition \(\nabla . \mathbf {V} = 0 \):

$$\begin{aligned} \frac{\partial \, V_x}{\partial x} = -\frac{2\varDelta v (x_2 -x)}{\varDelta x \varDelta y }-\frac{2\varDelta w (x_2 -x)}{\varDelta x \varDelta z } \end{aligned}$$
(9)

together with the conservation of flux (note that \(u_2=0\), so \(\varDelta u = -u_1\)):

$$\begin{aligned} \frac{-u_1}{\varDelta x} + \frac{\varDelta v}{\varDelta y} + \frac{\varDelta w}{\varDelta z}=0 \end{aligned}$$
(10)

which results in:

$$\begin{aligned} \frac{\partial \, V_x}{\partial x} =\frac{-2u_1}{\varDelta x^2}(x_2 -x) \end{aligned}$$
(11)

Finally, the velocity field is:

$$\begin{aligned}&V_x = \frac{u_1}{\varDelta x^2}(x_2-x)^2 \end{aligned}$$
(12a)
$$\begin{aligned}&V_y = \frac{2v_1}{\varDelta x}(x_2 -x) + \frac{2\varDelta v}{\varDelta x \varDelta y}(x_2 -x )(y-y_1) \end{aligned}$$
(12b)
$$\begin{aligned}&V_z= \frac{2w_1}{\varDelta x}(x_2 -x) + \frac{2\varDelta w}{\varDelta x \varDelta z}(x_2 -x )(z-z_1) \end{aligned}$$
(12c)

The tof in the x-direction (\(\tau _x\)) is defined by the integral:

$$\begin{aligned} \tau _x = \int _{x_p}^{x_1} \frac{\mathrm {d}x}{V_x} = \frac{\varDelta x^2}{u_1}\left[ \frac{1}{\varDelta x} - \frac{1}{x_2 - x_p}\right] \end{aligned}$$
(13)

The tof in the y direction is the time spent to travel to a point with vertical coordinate \(y_2\) along a streamline passing through \((x_p,y_p)\):

$$\begin{aligned} \tau _y = \int _{y_p}^{y_2} \frac{\mathrm {d}y}{V_y (x,y)} \end{aligned}$$
(14)

To eliminate the dependency on x in Eq. (14), we make use of the fact that the slope of the streamline is given by the ratio of the velocity components and write:

$$\begin{aligned} \frac{\mathrm {d}y}{\mathrm {d}x} = \frac{V_y(x,y,z)}{V_x(x,y,z)} \end{aligned}$$
(15)

Using Eq. (12a) and (12b) and integrating Eq.  (15) from \((x_p,y_p)\) to (xy) yield:

$$\begin{aligned}&\left[ \frac{ x_2 -x_p}{x_2 -x}\right] ^{\frac{2\varDelta v \varDelta x}{u_1\varDelta y}} = \frac{ v_1 \varDelta y + \varDelta v(y-y_1)}{v_1\varDelta y + \varDelta v(y_p-y_1)} \end{aligned}$$
(16)
$$\begin{aligned}&(x_2-x) = (x_2 -x_p)\left[ \frac{v_1\varDelta y + \varDelta v (y_p-y_1)}{v_1\varDelta y + \varDelta v (y-y_1)}\right] ^{\frac{u_1\varDelta y }{2 \varDelta v \varDelta x}} \end{aligned}$$
(17)

Substituting Eq. (17) into Eq. (12b) and solving the integral in Eq. (14), we have the time of flight:

$$\begin{aligned} \tau _y = \frac{\varDelta x ^2}{(x_2-x_p)u_1}\left\{ \left[ \frac{v_2\varDelta y}{v_1\varDelta y +\varDelta v(y_p-y_1)}\right] ^{\frac{u_1\varDelta y}{2\varDelta x\varDelta v}} -1\right\} \end{aligned}$$
(18)

Following the derivation outlined above, \(\tau _z\) is readily calculated:

$$\begin{aligned} \tau _z = \frac{\varDelta x ^2}{(x_2-x_p)u_1}\left\{ \left[ \frac{w_2\varDelta z}{w_1\varDelta z +\varDelta w(z_p-z_1)}\right] ^{\frac{u_1 \varDelta z}{2 \varDelta x \varDelta w}} -1\right\} \end{aligned}$$
(19)

Now using Eqs. (13), (18) and (19) and remembering that the exit time is defined as \(\tau _e = \text {min} (\tau _x,\tau _y,\tau _z)\), the exit locations are calculated:

$$\begin{aligned}&\displaystyle x_e = x_2 -\left[ \frac{u_1 \tau _e}{\varDelta x^2} + \frac{1}{\varDelta x_{2p}}\right] ^{-1} \end{aligned}$$
(20a)
$$\begin{aligned}&\displaystyle y_e= y_1 - \frac{v_1\varDelta y}{\varDelta v} + \frac{v_1\varDelta y +\varDelta v\varDelta y_{p1}}{\varDelta v}\left[ \frac{\tau _e u_1\varDelta x_{2p}}{\varDelta x^2} +1\right] ^{\frac{2\varDelta x\varDelta v}{u_1 \varDelta y}} \end{aligned}$$
(20b)
$$\begin{aligned}&\displaystyle z_e = z_1 - \frac{w_1\varDelta z}{\varDelta w} + \frac{w_1\varDelta z +\varDelta w\varDelta z_{p1}}{\varDelta w}\left[ \frac{\tau _e u_1\varDelta x_{2p}}{\varDelta x^2} +1\right] ^{\frac{2\varDelta x\varDelta w}{u_1 \varDelta z}} \end{aligned}$$
(20c)

where we define \(\varDelta x_{2p}= x_2 -x_p\), \(\varDelta y_{p1}= y_p -y_1\) and \(\varDelta z_{p1}= z_p -z_1\).

4 Flow Between Two Parallel Plates

To validate the method and stress the differences between the original and the extended versions of the Pollock algorithm, we consider the flow of an incompressible fluid between two infinite parallel plates separated by a distance \(L_y\), Fig. 3. The analytical solution with no-slip boundary conditions predicts a parabolic velocity profile in the axial direction (Pozrikidis 2011):

$$\begin{aligned} V_x = -\frac{\nabla P}{2\mu }y(L_y - y) \end{aligned}$$
(21)
Fig. 3
figure 3

Streamline tracing algorithm and its prediction of transport are validated using the analytical solution for the flow of an incompressible fluid between two infinite parallel plates shown schematically here. We study how accurately transport is simulated for different numbers of voxels used to resolve the pore space between the two plates

We place \(10^3\) particles uniformly along the y-direction and trace streamlines assuming that the velocity is previously solved in a square grid with an error-free numerical method that finds the average fluxes at the faces of the grid blocks. In Fig. 4 we plot the particle velocities calculated with our method and the standard Pollock approach for one, two and four grid cells across the plates. In this case, the velocity is in the x-direction only and varies with y, Fig. 3. The Pollock interpolation therefore assumes a fixed velocity in each voxel. Our new method reduces to the same approximation when there are no solid boundaries, but allows a linear and a quadratic variation in velocity away from a solid boundary for voxels with one or two solid boundaries, respectively. This captures more accurately the smallest flow speeds next to the solids. The case of one grid cell corresponds to the two-dimensional version of the case discussed in the “Appendix 1: Two Solids in the Same Direction”—a voxel with two opposing solid boundaries—and the interpolated velocity using our method is an exact match to the analytical solution. With two cells, our method now assumes a piece-wise linear velocity profile, for voxels with one solid boundary, as described by Eq. 12b, where the second term on the right-hand side is zero. While this is a poorer representation of the analytical solution, it is still far superior to the Pollock approach, which assumes that the velocity is constant within each cell. As the number of cells increases, both our approach and that of Pollock converge to the correct solution; however, the Pollock method always over-states the velocity in the cell closest to the solid boundary.

Fig. 4
figure 4

Particle velocities with one (a), two (b) and four (c) grid cells between the plates. The analytical solution is shown in blue dots, the standard Pollock method in the solid black line, and our modified approach in red dashed line. With one grid cell our modified method is an exact match to the analytical solution while the linear interpolation from the original method completely fails to reproduce the expected parabolic profile. With two and four grid cells our extended method also produces better estimates than the original method

Figure 5 shows the BTCs corresponding to the cases depicted in Fig. 4. We assume an initial delta-function pulse concentration, and by definition a particle moving at the average velocity breaks through at unit normalized time. We ignore molecular diffusion and study transport by advection only. As expected, with only one grid cell our method reproduces exactly the analytical curve, which the original Pollock completely fails to reproduce: with a constant velocity, there is no dispersion of concentration and the BTC is a delta function. With two and four grid cells, even if our proposed method is not able to reproduce exactly the results, it converges to the true solution for late-time arrivals, determined by the particles close to the walls, while the original method fails to capture the characteristics of the analytical solution. When the number of grid cells is high enough, both methods match the exact solution, Fig. 6. However, this requires a large number of voxels across each pore, which is rarely—if ever—achieved in pore-scale images and numerical models.

Fig. 5
figure 5

Breakthrough curves with one (a), two (b) and four (c) grid cells between the plates. Unit time is defined as the break-through time of the particles moving at the average flow speed. The analytical solution is the dotted blue line, the standard Pollock is the black solid line, and our method is the red dashed line. Using one grid cell the modified method is an exact match to the analytical solution. With a constant velocity (see Fig. 4), the Pollock method assumes transport with no dispersion of the original delta-function concentration profile in the absence of molecular diffusion. With two and four grid cells our extended method also yields better results than the original method and correctly captures the late-time tail of the distribution. The Pollock method, assuming piece-wise constant velocities, again fails to reproduce the wide distribution of travel times

Fig. 6
figure 6

Increasing the number of grid cells across the plates (fifty in this example) both methods converge to the analytical solution for the breakthrough curve. The analytical solution is shown in the solid line, the standard Pollock in the green dots, and our method in the dashed red line. Here, for a fair comparison, we have binned the concentration to construct a smoothed histogram of the concentration

As the preceding results suggest, our method is suitable to simulate transport in the relatively coarse grids typical of pore-scale imaging applications—where individual pores are resolved with only a few voxels. Real sedimentary rocks have a typical pore throat size of few microns (Nelson 2009). Since the tomographic images have resolutions of the same order of magnitude, this means the pore throats must be represented with just a few grid cells. Computing flow on a refined grid, to remove this restriction, rapidly leads to prohibitive simulation times (Blunt et al. 2013). Inevitably, the trade-off between system size and resolution means that for realistic systems some pores will be only one or two voxels across; it is necessary that a numerical model represents transport through these pores with reasonable accuracy. The Pollock method fails to reproduce the slow flows near the solid, while our modified method gives a much better representation of the long-time tail of the transport.

5 Streamline Tracing in Images of Sedimentary Rocks

Next we show how streamline tracing can be used to study transport properties in real rocks: a sandstone and a carbonate. The sandstone is a Bentheimer with image size of \(1000^3\) voxels, voxel size of \(3.0\,\upmu \text {m}\), porosity of \(21.6\,\%\) and the image-calculated permeability of \(3 \times 10^{-12}\,\text {m}^2\). The carbonate is a Ketton oolite with dimensions \(911\times 902\times 922\), voxel size of \(3.8\,\upmu \text {m}\), porosity of \(13.5\,\%\) and image-calculated permeability of \(13\times 10^{-12}\,\text {m}^2\) , Fig. 7. Defining the characteristic length as \(\pi V/S\), where V is the volume of the porous medium and S is the area of the pore–solid interface (Mostaghimi et al. 2012), we calculate Reynolds numbers in our flow simulations of \(10^{-3}\) and \(10^{-2}\) for the sandstone and the carbonate, respectively.

Fig. 7
figure 7

Orthogonal slices of three-dimensional segmented micro-CT images of a Bentheimer sandstone (a) and a Ketton carbonate (b). We launch streamlines according to a flow weighted rule at the inlet and trace until the outlet face. Flow in the Bentheimer sandstone is relatively homogeneous (c), while in the Ketton carbonate it tends to be concentrated in high-velocity channels (d). Colour in the streamlines is indicative of advective travel time (slow paths in light blue, fast paths in red), distances in \(\upmu \text {m}\)

The flow field is calculated using a finite-volume method (Raeini et al. 2012), as mentioned previously. Tracer particles are injected using a flow weighted rule at the inlet and tracked until they reach the outlet. In both images we inject \(5.0{\,\times \,}10^{4}\) particles and impose constant pressure at the inlet and outlet faces (perpendicular to the x-direction) and no flux in the lateral (y and z) boundaries. In addition to the voxels with no solid boundaries, the ones with four and five boundaries are also treated with the standard Pollock method, as they have negligible impact on the total flux across the sample, as shown in Table 1.

Distributions of the time of flight can be used to characterize the degree of heterogeneity of the rock (Bijeljic et al. 2011). In addition, BTCs also provide useful insights into the nature of the transport in porous media and the presence of a heavy tail being indicative of anomalous (non-Fickian) transport that cannot be adequately described by the classical advection-diffusion equation (Berkowitz et al. 2000).

In each image we trace streamlines using both the standard Pollock method for every voxel and our modified version (in the voxels with one, two and three solid neighbours). We compute the distribution of voxel transit times—\(\psi (\tau )\)—as a function of the dimensionless time \(\tau = t/\tau _{avg}\), where \(\tau _{avg} = \varDelta x/u_{avg}\) and \(u_{avg}\) is the average flow velocity, see Fig. 8. The standard Pollock method gives lower voxel transit times in the slow regions, typically the ones close to the solid interface, compared with our new tracing method. Based on the results of the previous section, we suggest that ignoring the no-slip boundary conditions at the solid walls tends to overestimate the speed of the slowest particles and truncate \(\psi (\tau )\) for long \(\tau \). We also note that both distributions are almost identical for short times, as it would be expected since the “free” pores, which tend to have the fastest advective travel times, are treated the in same way.

Fig. 8
figure 8

Distribution of voxel transit times against dimensionless time \(\tau \) in a Bentheimer sandstone (top) and in a Ketton carbonate (bottom) obtained using both the standard Pollock algorithm (dashed line) and our modified version (solid line). Because it ignores the decay of the tangential velocities in the voxels neighbouring the solid phase, the standard Pollock method tends to underestimate the transit times in such slower voxels

One of the analytical frameworks to describe anomalous transport in porous media is the continuous time random walk (CTRW) approach, see Berkowitz et al. (2006) and references therein. Under its assumptions it can be shown that asymptotically when \(\psi (t) \sim t^{-1-\beta }\), then the breakthrough concentration scales as \(C(t) \sim t^{-1-\beta }\). The exponent \(\beta \) is related to the degree of heterogeneity of the system and controls the nature of dispersion (e.g. Fickian or non-Fickian). When \(\beta >2\) the first and second moments of the transit time distribution are finite and the system displays asymptotic Fickian behaviour. To calculate \(\beta \) from the breakthrough curves, we first define \(\alpha = 1+\beta \), meaning that \(C(t) \sim t^{-\alpha }\). Next, using a maximum-likelihood method (Clauset et al. 2009), we compute \(\alpha \) in the tails of the breakthrough curves obtained by both the standard Pollock method and by our modified version, Fig. 9. Simulating BTCs with the Pollock method gives us values of \(\beta \) of 2.6 and 1.9 for Bentheimer and Ketton, respectively, which would lead us to infer Fickian (or almost-Fickian) behaviour, contradicting several earlier experimental and modelling results on transport in natural porous media (e.g. Sahimi 2011; Bijeljic et al. 2011; Berkowitz et al. 2000; Becker and Shapiro 2000; Silliman and Simpson 1987; Bijeljic et al. 2013). However, using our new method we obtain \(\beta = 1.2\) and \(\beta = 0.9\), respectively, indicating the expected non-Fickian transport behaviour.

Fig. 9
figure 9

Breakthrough curves (concentration as a function of time) in a Bentheimer sandstone (top) and in a Ketton carbonate (bottom) simulated with both the standard Pollock algorithm (dashed line) and our modified version of the algorithm (solid line). Using our method we have longer tails and smaller exponents (\(\alpha = 1+\beta \)) for the power law describing the late-time behaviour. Under the CTRW approach, the standard Pollock simulation predicts Fickian behaviour (\(\beta >2\)) for the Bentheimer sandstone, while our approach predicts non-Fickian behaviour (\(\beta <2\))

6 Conclusions

We have presented a semi-analytical streamline tracing method to simulate pore-scale transport in heterogeneous porous media. The method is a development of a well established algorithm that has been in use for field-scale (Darcy law) flow simulation for many years. Our new methodology is applied to flow controlled by the Navier–Stokes equation at the pore scale and represents a very efficient way of obtaining transport properties. The simulation time was only a few minutes using a standard desktop computer, even for billion-cell models. We apply this new approach to obtain the time-of-flight distributions and breakthrough curves directly in voxelized micro-CT images of two sedimentary rocks, a Bentheimer sandstone and a Ketton carbonate, and compare to the ones obtained with the original Pollock method. The tails of the distributions are significantly different, which may give misleading indications about the nature of the transport (Fickian or non-Fickian) in the rocks. In particular, depending on the method, the Bentheimer sandstone would be characterized as Fickian (using the standard version) or non-Fickian (using the modified version).

The use of analytically traced streamlines eliminates the numerical stability constraints associated with the size of the time step, an obvious advantage over particle tracking methods, such as the Euler or Runge–Kutta methods (Oliveira and Baptista 1998).

Diffusion processes can be readily treated by implementing a random walk in the particle tracers (Tompson and Gelhar 1990; Sàlles et al. 1993). The advantage of our algorithm lies in that the time step would be constrained by the diffusive step only, allowing for larger time steps and a consequent saving in computational time.

The method can also be used to study the origin of anomalous transport at the pore scale (Kang et al. 2014) and the scaling behaviour of Lagrangian velocities (Siena et al. 2014).