Five Spot Pattern Single Phase#
Author: Zakariya Abugrin | Date: May 2025
Introduction#
This tutorial shows how to create a simple regular cartesian grid with a single phase fluid and a five-spot wellsβ pattern. The user can use this tutorial to test the change in pressure distribution based on the rock (i.e. grid) and fluid properties or using a different wellsβ pattern.
*the code used to generate this gif image is shown in Save simulation run as gif section below.
Import reservoirflow#
We start with importing reservoirflow as rf. The abbreviation rf refers to reservoirflow where all modules under this library can be accessed. rf is also used throughout the API documentation. We recommend our users to stick with this convention.
1import reservoirflow as rf
2
3print(rf.__version__)
0.1.0b3
1static = True
2notebook = True
Both
staticandnotebookare set toTrueto produce images for this notebook. For an interactive visualization in your local machine, set both arguments toFalse. To allow for interactive visualizations in Colab, set onlystatic=Falseand keepnotebook=True.
1rf.utils.pyvista.set_mode("dark")
By default,
"dark"mode is used with a black background. You can change to"light"mode usingrf.utils.pyvista.set_mode()function.
Define a regular cartesian grid#
We start with defining the grid model which represents the rock geometry and properties using grids module. Currently, only RegularCartesian grid models are supported. In the near future, Radial and IrregularCartesian models will be added.
1grid = rf.grids.RegularCartesian(
2 nx=7,
3 ny=7,
4 nz=3,
5 dx=200,
6 dy=200,
7 dz=40,
8 phi=0.30,
9 kx=1,
10 ky=1,
11 kz=1,
12 comp=1e-6,
13 unit="field",
14)
Note
Note that field units are used in this tutorial. For more information about selecting a unit system or what units or factors are used for each property, see Units & Factors.
The grid object can be shown using show() method. Argument label can be used to show cells id or other properties. For more information, check RegularCartesian.
1grid.show(
2 label="id",
3 boundary=True,
4 static=static,
5 notebook=notebook,
6)
Note
Cells are given an id based on natural order starting form the bottom left corner, see grid.cells_id. Cells id increase in x from left to right, in y from front to back, and in z from bottom to top. Boundaries are also counted. You can use the cell id to access its properties, or add a well in that location as shown in Add wells section below.
Define a single phase compressible fluid#
After defining the grid model, we can use the fluids module to define a fluid. A single phase fluid can be defined using SinglePhase class which accepts arguments such s viscosity (mu), formation volume factor (B), density (rho), compressibility (comp) etc. For more information, check SinglePhase.
1fluid = rf.fluids.SinglePhase(
2 mu=0.5,
3 B=1,
4 rho=50,
5 comp=1e-5,
6 unit="field",
7)
Create a reservoir simulation model#
To construct a reservoir simulation model, both fluid and grid objects are required as input arguments in models module. In addition, you can also define the initial reservoir pressure (pi), time step duration (dt), start date etc. For more information, check models. Currently, only BlackOil models are supported. In the future, more advanced models will be added such as Compositional or Thermal.
1model = rf.models.BlackOil(
2 grid=grid,
3 fluid=fluid,
4 pi=3000,
5 dt=1,
6 start_date="01.01.2000",
7 unit="field",
8)
Define boundary conditions#
If you start the simulation run without defining a boundary condition, then all boundaries are set to zero flow rate. To define a boundary condition, you can use set_boundaries() method where you specify the boundary condition as dictionary where keys are cells id and values are tuple of (βcondβ, value). For example, a constant zero rate boundary condition in cell_id=0 can be set as model.set_boundaries({0: ("rate", 0)}).
Below we set all the boundaries cells to zero rate (default behavior).
1model.set_boundaries({i: ("rate", 0) for i in grid.get_boundaries("id", fmt="array")})
Add wells#
To make a five spot pattern, single cell wells are used to add wells to specific cells as following:
Injection wells#
4 injectors: 182, 186, 218, 222
1for cell_id in [182, 186, 218, 222]:
2 model.set_well(cell_id=cell_id, pwf=3500, s=0, r=3.5)
Note
Since the bottom hole flowing pressure (BHFP or pwf) is higher than the initial reservoir pressure, then these wells will have an injection rates (i.e. a positive flow rate means additional fluid is injected into the reservoir). This might not be the expected behavior in reservoir simulation and this might be reversed in the future where injection rates will be expressed as a negative rate (depends on the usersβ feedback). Feel free to comment on this.
Production well#
1 producer: 202
1model.set_well(
2 cell_id=202,
3 pwf=1000,
4 s=0,
5 r=3.5,
6)
Note
In this case, pwf is lower than the initial reservoir pressure, which will lead to a production rates (i.e. a negative flow rate means a fluid is produced from the reservoir).
Compile the model#
Before you can run the model, you need to compile a solution for it. By compiling a solution, you actually decide the solution you want to use for your model. Interestingly, reservoirflow provides multiple solutions for the same model based on your configuration.
Hint
Compiling solutions is the most interesting idea introduced in reservoirflow which allows to solve the same model using different solutions so we can compare them with each other and/or combine them together.
Currently, a numerical solution based on Finite-Difference-Method (FDM) is available. You can read more about the available solution in the solutions. Below, we compile our model using a numerical solution using FDM.
1model.compile(stype="numerical", method="FDM", sparse=False)
[info] FDM was assigned as model.solution.
Attention
A model can not be run until it is complied. Methods such as solve() and run() will be functioning only after the model is compiled.
Run the model with 40 time steps#
To run the simulation model, use run() method as shown below. Here, you can set the number of time steps based on nsteps argument. Note that each time step will have a time duration as defined by dt argument when the model was constructed.
There are two modes available run the model based on vectorize argument, True means vectorized and False means symbolized. In addition, you can also select a solver which can be direct, iterative, or neurical. Developing solvers based on neural-networks is also a new idea introduced by reservoirflow.
1model.run(
2 nsteps=40,
3 vectorize=True,
4 isolver=None,
5)
[info] Simulation run started: 40 timesteps.
[info] Simulation run of 40 steps finished in 0.13 seconds.
[info] Material Balance Error: 1.3280299082651936e-10.
Visualize the simulation run in 3D show#
You can interactively show the simulation run in a 3D show using show() method. There is a lot of functionality here. For interactive visualization, set static and notebook to False. For more information, check the BlackOil.show().
Note
Here, boundaries are also added (boundary=True) but they appear in a gray color. That is because boundaries are defined as a zero flow rate where pressures are undefined (i.e. np.nan). If boundary conditions were set to constant pressures, values will appear based on the defined color map.
1model.show(
2 prop="pressures",
3 boundary=True,
4 cmap="Blues",
5 static=static,
6 notebook=notebook,
7)
Tip
Use the color maps names as str with cmap argument. Matplotlib color maps are supported, for more color maps, check Choosing Colormaps in Matplotlib. In the above image, cmap="Blues" was used. In Save simulation run as gif section below, cmap="tab10" was used instead.
Show results as a pandas DataFrame#
The results of the Simulation run can be accessed as a pandas DateFrame using get_df() method. You can select columns and scale values. For more information, check BlackOil.get_df().
1df = model.get_df(columns=["time", "date", "wells"], units=True).round()
2df
| Time [days] | Date [d.m.y] | Qw182 [stb/day] | Qw186 [stb/day] | Qw218 [stb/day] | Qw222 [stb/day] | Qw202 [stb/day] | Pwf182 [psia] | Pwf186 [psia] | Pwf218 [psia] | Pwf222 [psia] | Pwf202 [psia] | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Step | ||||||||||||
| 0 | 0 | 01.01.2000 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 3000.0 | 3000.0 | 3000.0 | 3000.0 | 3000.0 |
| 1 | 1 | 02.01.2000 | 55.0 | 55.0 | 55.0 | 55.0 | -222.0 | 3500.0 | 3500.0 | 3500.0 | 3500.0 | 1000.0 |
| 2 | 2 | 03.01.2000 | 54.0 | 54.0 | 54.0 | 54.0 | -217.0 | 3500.0 | 3500.0 | 3500.0 | 3500.0 | 1000.0 |
| 3 | 3 | 04.01.2000 | 53.0 | 53.0 | 53.0 | 53.0 | -213.0 | 3500.0 | 3500.0 | 3500.0 | 3500.0 | 1000.0 |
| 4 | 4 | 05.01.2000 | 53.0 | 53.0 | 53.0 | 53.0 | -210.0 | 3500.0 | 3500.0 | 3500.0 | 3500.0 | 1000.0 |
| 5 | 5 | 06.01.2000 | 52.0 | 52.0 | 52.0 | 52.0 | -208.0 | 3500.0 | 3500.0 | 3500.0 | 3500.0 | 1000.0 |
| 6 | 6 | 07.01.2000 | 52.0 | 52.0 | 52.0 | 52.0 | -207.0 | 3500.0 | 3500.0 | 3500.0 | 3500.0 | 1000.0 |
| 7 | 7 | 08.01.2000 | 51.0 | 51.0 | 51.0 | 51.0 | -205.0 | 3500.0 | 3500.0 | 3500.0 | 3500.0 | 1000.0 |
| 8 | 8 | 09.01.2000 | 51.0 | 51.0 | 51.0 | 51.0 | -204.0 | 3500.0 | 3500.0 | 3500.0 | 3500.0 | 1000.0 |
| 9 | 9 | 10.01.2000 | 51.0 | 51.0 | 51.0 | 51.0 | -203.0 | 3500.0 | 3500.0 | 3500.0 | 3500.0 | 1000.0 |
| 10 | 10 | 11.01.2000 | 51.0 | 51.0 | 51.0 | 51.0 | -203.0 | 3500.0 | 3500.0 | 3500.0 | 3500.0 | 1000.0 |
| 11 | 11 | 12.01.2000 | 50.0 | 50.0 | 50.0 | 50.0 | -202.0 | 3500.0 | 3500.0 | 3500.0 | 3500.0 | 1000.0 |
| 12 | 12 | 13.01.2000 | 50.0 | 50.0 | 50.0 | 50.0 | -201.0 | 3500.0 | 3500.0 | 3500.0 | 3500.0 | 1000.0 |
| 13 | 13 | 14.01.2000 | 50.0 | 50.0 | 50.0 | 50.0 | -201.0 | 3500.0 | 3500.0 | 3500.0 | 3500.0 | 1000.0 |
| 14 | 14 | 15.01.2000 | 50.0 | 50.0 | 50.0 | 50.0 | -200.0 | 3500.0 | 3500.0 | 3500.0 | 3500.0 | 1000.0 |
| 15 | 15 | 16.01.2000 | 50.0 | 50.0 | 50.0 | 50.0 | -200.0 | 3500.0 | 3500.0 | 3500.0 | 3500.0 | 1000.0 |
| 16 | 16 | 17.01.2000 | 50.0 | 50.0 | 50.0 | 50.0 | -200.0 | 3500.0 | 3500.0 | 3500.0 | 3500.0 | 1000.0 |
| 17 | 17 | 18.01.2000 | 50.0 | 50.0 | 50.0 | 50.0 | -199.0 | 3500.0 | 3500.0 | 3500.0 | 3500.0 | 1000.0 |
| 18 | 18 | 19.01.2000 | 50.0 | 50.0 | 50.0 | 50.0 | -199.0 | 3500.0 | 3500.0 | 3500.0 | 3500.0 | 1000.0 |
| 19 | 19 | 20.01.2000 | 50.0 | 50.0 | 50.0 | 50.0 | -199.0 | 3500.0 | 3500.0 | 3500.0 | 3500.0 | 1000.0 |
| 20 | 20 | 21.01.2000 | 50.0 | 50.0 | 50.0 | 50.0 | -199.0 | 3500.0 | 3500.0 | 3500.0 | 3500.0 | 1000.0 |
| 21 | 21 | 22.01.2000 | 50.0 | 50.0 | 50.0 | 50.0 | -198.0 | 3500.0 | 3500.0 | 3500.0 | 3500.0 | 1000.0 |
| 22 | 22 | 23.01.2000 | 50.0 | 50.0 | 50.0 | 50.0 | -198.0 | 3500.0 | 3500.0 | 3500.0 | 3500.0 | 1000.0 |
| 23 | 23 | 24.01.2000 | 50.0 | 50.0 | 50.0 | 50.0 | -198.0 | 3500.0 | 3500.0 | 3500.0 | 3500.0 | 1000.0 |
| 24 | 24 | 25.01.2000 | 50.0 | 50.0 | 50.0 | 50.0 | -198.0 | 3500.0 | 3500.0 | 3500.0 | 3500.0 | 1000.0 |
| 25 | 25 | 26.01.2000 | 50.0 | 50.0 | 50.0 | 50.0 | -198.0 | 3500.0 | 3500.0 | 3500.0 | 3500.0 | 1000.0 |
| 26 | 26 | 27.01.2000 | 50.0 | 50.0 | 50.0 | 50.0 | -198.0 | 3500.0 | 3500.0 | 3500.0 | 3500.0 | 1000.0 |
| 27 | 27 | 28.01.2000 | 50.0 | 50.0 | 50.0 | 50.0 | -198.0 | 3500.0 | 3500.0 | 3500.0 | 3500.0 | 1000.0 |
| 28 | 28 | 29.01.2000 | 49.0 | 49.0 | 49.0 | 49.0 | -197.0 | 3500.0 | 3500.0 | 3500.0 | 3500.0 | 1000.0 |
| 29 | 29 | 30.01.2000 | 49.0 | 49.0 | 49.0 | 49.0 | -197.0 | 3500.0 | 3500.0 | 3500.0 | 3500.0 | 1000.0 |
| 30 | 30 | 31.01.2000 | 49.0 | 49.0 | 49.0 | 49.0 | -197.0 | 3500.0 | 3500.0 | 3500.0 | 3500.0 | 1000.0 |
| 31 | 31 | 01.02.2000 | 49.0 | 49.0 | 49.0 | 49.0 | -197.0 | 3500.0 | 3500.0 | 3500.0 | 3500.0 | 1000.0 |
| 32 | 32 | 02.02.2000 | 49.0 | 49.0 | 49.0 | 49.0 | -197.0 | 3500.0 | 3500.0 | 3500.0 | 3500.0 | 1000.0 |
| 33 | 33 | 03.02.2000 | 49.0 | 49.0 | 49.0 | 49.0 | -197.0 | 3500.0 | 3500.0 | 3500.0 | 3500.0 | 1000.0 |
| 34 | 34 | 04.02.2000 | 49.0 | 49.0 | 49.0 | 49.0 | -197.0 | 3500.0 | 3500.0 | 3500.0 | 3500.0 | 1000.0 |
| 35 | 35 | 05.02.2000 | 49.0 | 49.0 | 49.0 | 49.0 | -197.0 | 3500.0 | 3500.0 | 3500.0 | 3500.0 | 1000.0 |
| 36 | 36 | 06.02.2000 | 49.0 | 49.0 | 49.0 | 49.0 | -197.0 | 3500.0 | 3500.0 | 3500.0 | 3500.0 | 1000.0 |
| 37 | 37 | 07.02.2000 | 49.0 | 49.0 | 49.0 | 49.0 | -197.0 | 3500.0 | 3500.0 | 3500.0 | 3500.0 | 1000.0 |
| 38 | 38 | 08.02.2000 | 49.0 | 49.0 | 49.0 | 49.0 | -197.0 | 3500.0 | 3500.0 | 3500.0 | 3500.0 | 1000.0 |
| 39 | 39 | 09.02.2000 | 49.0 | 49.0 | 49.0 | 49.0 | -197.0 | 3500.0 | 3500.0 | 3500.0 | 3500.0 | 1000.0 |
| 40 | 40 | 10.02.2000 | 49.0 | 49.0 | 49.0 | 49.0 | -197.0 | 3500.0 | 3500.0 | 3500.0 | 3500.0 | 1000.0 |
Save simulation run as gif#
You can save gif movie of the simulation run using save_gif() method. For more information, check the BlackOil.save_gif().
1if not static:
2 model.save_gif(
3 prop="pressures",
4 boundary=False,
5 cmap="tab10",
6 file_name="attachments/grid_animated.gif",
7 window_size=rf.utils.pyvista.get_window_size("hd"),
8 )
Tip
To save a gif movie of the simulation run, set static=False or just remove the first line and unindent.
Comments π¬#
Feel free to make a comment, ask a question, or share your opinion about this specific content. Please keep in mind the Commenting Guidelines β.