Understand Compiling#
Author: Zakariya Abugrin | Date: May 2025
Introduction#
In this tutorial, we see how a model can be compiled to calculate different types of solutions such as: analytical
, numerical
, and neurical
which is one of the main research topics in reservoirflow
.
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.
\
The following points are important to understand the compiling concept in reservoirflow
:
Every model from
models
module has acompile()
method which can be used to select a solution.Every model come with an empty dictionary of solutions and compilers accessed by
model.solutions
andmodel.compilers
.Every model must be compiled before the solution can be computed.
After compiling a model, a new attribute called
model.solution
is added.Every model can be solved using different solutions where
model.set_solution()
can be used to switch to previous solutions.Solutions can be computed using
model.solution.solve()
for a single time step andmodel.solution.run()
for multiple time steps.Solution functions are mapped directly to the corresponding model and can be accessed directly using
model.solve()
andmodel.run()
.Specifically,
neurical
solutions (i.e. based on neural networks) havemodel.solution.fit()
to train based on physics-loss andmodel.solution.predict()
.Specifically,
neurical
solution functions are mapped directly to the corresponding model and can be accessed directly usingmodel.train()
andmodel.predict()
.
Now, letβs see how we can apply this concept.
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
1# Check what is available in solutions:
2dir(rf.solutions)
['Compiler',
'Solution',
'__all__',
'__builtins__',
'__cached__',
'__doc__',
'__file__',
'__loader__',
'__name__',
'__package__',
'__path__',
'__spec__',
'compiler',
'solution']
Warning
Modules under solutions
are not loaded by default in reservoirflow
. As a result, rf.solutions.numerical.FDM
will fail. As can be seen below, only classes Compiler
and Solution
are loaded by default.
1# rf.solutions.numerical.fdm.FDM # this will not work
2# rf.solutions.numerical.FDM # this will not work
3
4# But, we can load these modules or classes explicitly:
5# from reservoirflow.solutions import numerical
6# from reservoirflow.solutions.numerical import FDM
Note
By default, modules numerical
, analytical
, and neurical
are not available under solutions
.
Tip
Specific solutions modules such as rf.solutions.numerical
will only be available once a solution for that module (e.g. rf.solutions.numerical.FDM
) was used to compile a model. Of course, this does not prevent loading these modules explicitly (e.g. from reservoirflow.solutions import numerical
, or from reservoirflow.solutions.numerical import FDM
).
Build a model#
A reservoir simulation model requires two objects: Grid
and Fluid
. The function below create_model()
starts by creating these objects which are used to initiate a Model
object using BlackOil
class.
1def create_model():
2 # Grid:
3 grid = rf.grids.RegularCartesian(
4 nx=4,
5 ny=1,
6 nz=1,
7 dx=300,
8 dy=350,
9 dz=40,
10 phi=0.27,
11 kx=270,
12 dtype="double",
13 )
14
15 # Fluid:
16 fluid = rf.fluids.SinglePhase(mu=0.5, B=1, dtype="double")
17
18 # Model:
19 model = rf.models.BlackOil(
20 grid,
21 fluid,
22 pi=4000,
23 dt=1,
24 start_date="10.10.2018",
25 verbose=False,
26 dtype="double",
27 )
28
29 # Production well:
30 model.set_well(cell_id=4, q=-600, s=1.5, r=3.5)
31
32 # Boundaries:
33 model.set_boundaries({0: ("pressure", 4000), 5: ("rate", 0)})
34
35 return model
36
37
38model = create_model()
Simulation Run#
To perform the simulation run, method model.run() can be used. The code below performs a simulation run for nsteps=10
(i.e. number of steps) and using isolver=cgs
:
1model.solutions
{}
1model.solve() # same as: model.run()
The model is not compiled. Use model.compile() to add solve() and run() methods.
1help(model.solve) # same as: help(model.run)
Help on method solve in module reservoirflow.models.model:
solve(**kwargs) method of reservoirflow.models.black_oil.BlackOil instance
Solve a single timestep.
.. attention::
This method is not available until the model is compiled
using ``model.compile()``.
Once the model is compiled, the documentation of the assigned
solution can be accessed using one of the following methods:
>>> help(model.solve) # or help(model.solution.solve)
>>> print(model.solve.__doc__) # or print(model.solution.solve.__doc__)
1model.compile(stype="numerical", method="FDM", sparse=False)
2# model.compile(stype="analytical", method="1D1P", sparse=False)
[info] FDM was assigned as model.solution.
1model.compiler
rf.solutions.Compiler(model='BlackOil', stype='numerical', method='FDM', sparse=False)
1print(model.run.__doc__)
Perform a simulation run for nsteps.
Parameters
----------
nsteps : int, optional
_description_
threading : bool, optional
_description_
check_MB : bool, optional
_description_
isolver : str, optional
iterative solver for sparse matrices. Available solvers are
["bicg", "bicgstab", "cg", "cgs", "gmres", "lgmres",
"minres", "qmr", "gcrotmk", "tfqmr"].
If None, direct solver is used. Only relevant when argument
sparse=True. Direct solver is recommended for more accurate
calculations. To improve performance, "cgs" is recommended
to increase performance while option "minres" is not recommended due to
high MB error. For more information check [1][2].
References
----------
- SciPy: `Solving Linear Problems <https://docs.scipy.org/doc/scipy/reference/sparse.linalg.html#solving-linear-problems>`_.
- SciPy: `Iterative Solvers <https://scipy-lectures.org/advanced/scipy_sparse/solvers.html#iterative-solvers>`_.
1help(model.run)
Help on method run in module reservoirflow.solutions.numerical.fdm:
run(nsteps=10, threading=True, vectorize=True, check_MB=True, print_arrays=False, isolver=None) method of reservoirflow.solutions.numerical.fdm.FDM instance
Perform a simulation run for nsteps.
Parameters
----------
nsteps : int, optional
_description_
threading : bool, optional
_description_
check_MB : bool, optional
_description_
isolver : str, optional
iterative solver for sparse matrices. Available solvers are
["bicg", "bicgstab", "cg", "cgs", "gmres", "lgmres",
"minres", "qmr", "gcrotmk", "tfqmr"].
If None, direct solver is used. Only relevant when argument
sparse=True. Direct solver is recommended for more accurate
calculations. To improve performance, "cgs" is recommended
to increase performance while option "minres" is not recommended due to
high MB error. For more information check [1][2].
References
----------
- SciPy: `Solving Linear Problems <https://docs.scipy.org/doc/scipy/reference/sparse.linalg.html#solving-linear-problems>`_.
- SciPy: `Iterative Solvers <https://scipy-lectures.org/advanced/scipy_sparse/solvers.html#iterative-solvers>`_.
1# print(rf.solutions.numerical.fdm.FDM.run.__doc__)
2# print(rf.solutions.numerical.FDM.run.__doc__)
3# print(model.solution.run.__doc__)
4print(model.run.__doc__)
Perform a simulation run for nsteps.
Parameters
----------
nsteps : int, optional
_description_
threading : bool, optional
_description_
check_MB : bool, optional
_description_
isolver : str, optional
iterative solver for sparse matrices. Available solvers are
["bicg", "bicgstab", "cg", "cgs", "gmres", "lgmres",
"minres", "qmr", "gcrotmk", "tfqmr"].
If None, direct solver is used. Only relevant when argument
sparse=True. Direct solver is recommended for more accurate
calculations. To improve performance, "cgs" is recommended
to increase performance while option "minres" is not recommended due to
high MB error. For more information check [1][2].
References
----------
- SciPy: `Solving Linear Problems <https://docs.scipy.org/doc/scipy/reference/sparse.linalg.html#solving-linear-problems>`_.
- SciPy: `Iterative Solvers <https://scipy-lectures.org/advanced/scipy_sparse/solvers.html#iterative-solvers>`_.
1dir(rf.solutions)
['Compiler',
'Solution',
'__all__',
'__builtins__',
'__cached__',
'__doc__',
'__file__',
'__loader__',
'__name__',
'__package__',
'__path__',
'__spec__',
'compiler',
'numerical',
'solution']
1dir(rf.solutions.numerical)
['FDM',
'FEM',
'FVM',
'__all__',
'__builtins__',
'__cached__',
'__doc__',
'__file__',
'__loader__',
'__name__',
'__package__',
'__path__',
'__spec__',
'fdm',
'fem',
'fvm',
'solvers']
1print(
2 " stype:",
3 model.compiler.stype,
4 "\n",
5 "method:",
6 model.compiler.method,
7 # "\n",
8 # "mode:",
9 # model.compiler.mode,
10 # "\n",
11 # "solver:",
12 # model.compiler.solver,
13 "\n",
14 "solution:",
15 # model.compiler.solution,
16 model.compiler.model.solution,
17 model.compiler.model.name,
18 "\n",
19 model.solutions,
20)
stype: numerical
method: FDM
solution: <reservoirflow.solutions.numerical.fdm.FDM object at 0x7f5b37118150> BlackOil
{'BlackOil-numerical-FDM-False': <reservoirflow.solutions.numerical.fdm.FDM object at 0x7f5b37118150>}
1model.run(
2 nsteps=10,
3 vectorize=True,
4 threading=True,
5 isolver="cgs",
6)
[info] Simulation run started: 10 timesteps.
[info] Simulation run of 10 steps finished in 0.04 seconds.
[info] Material Balance Error: 1.693933882052079e-11.
1model.get_df()
Time | Q0 | Q4 | P0 | P1 | P2 | P3 | P4 | Qw4 | Pwf4 | |
---|---|---|---|---|---|---|---|---|---|---|
Step | ||||||||||
0 | 0 | 0.0 | 0.0 | 4000.0 | 4000.000000 | 4000.000000 | 4000.000000 | 4000.000000 | 0.0 | 4000.000000 |
1 | 1 | 600.0 | -600.0 | 4000.0 | 3989.436768 | 3968.310305 | 3947.183842 | 3926.057379 | -600.0 | 3898.992647 |
2 | 2 | 600.0 | -600.0 | 4000.0 | 3989.436768 | 3968.310305 | 3947.183842 | 3926.057379 | -600.0 | 3898.992647 |
3 | 3 | 600.0 | -600.0 | 4000.0 | 3989.436768 | 3968.310305 | 3947.183842 | 3926.057379 | -600.0 | 3898.992647 |
4 | 4 | 600.0 | -600.0 | 4000.0 | 3989.436768 | 3968.310305 | 3947.183842 | 3926.057379 | -600.0 | 3898.992647 |
5 | 5 | 600.0 | -600.0 | 4000.0 | 3989.436768 | 3968.310305 | 3947.183842 | 3926.057379 | -600.0 | 3898.992647 |
6 | 6 | 600.0 | -600.0 | 4000.0 | 3989.436768 | 3968.310305 | 3947.183842 | 3926.057379 | -600.0 | 3898.992647 |
7 | 7 | 600.0 | -600.0 | 4000.0 | 3989.436768 | 3968.310305 | 3947.183842 | 3926.057379 | -600.0 | 3898.992647 |
8 | 8 | 600.0 | -600.0 | 4000.0 | 3989.436768 | 3968.310305 | 3947.183842 | 3926.057379 | -600.0 | 3898.992647 |
9 | 9 | 600.0 | -600.0 | 4000.0 | 3989.436768 | 3968.310305 | 3947.183842 | 3926.057379 | -600.0 | 3898.992647 |
10 | 10 | 600.0 | -600.0 | 4000.0 | 3989.436768 | 3968.310305 | 3947.183842 | 3926.057379 | -600.0 | 3898.992647 |
1model.solution.get_cells_eq()
{np.int64(1): (defaultdict(int, {p1: 85.2012000000000, p2: -28.4004000000000}),
227203.200000000),
np.int64(2): (defaultdict(int,
{p1: 28.4004000000000,
p3: 28.4004000000000,
p2: -56.8008000000000}),
0.0),
np.int64(3): (defaultdict(int,
{p2: 28.4004000000000,
p4: 28.4004000000000,
p3: -56.8008000000000}),
0.0),
np.int64(4): (defaultdict(int, {p3: 28.4004000000000, p4: -28.4004000000000}),
600.000000000000)}
1model.solution.get_matrices_symb(True)
(array([[ 85.2012, -28.4004, 0. , 0. ],
[ 28.4004, -56.8008, 28.4004, 0. ],
[ 0. , 28.4004, -56.8008, 28.4004],
[ 0. , 0. , 28.4004, -28.4004]]),
array([[227203.2],
[ 0. ],
[ 0. ],
[ 600. ]]))
1model.solution.get_matrices_vect(True)
(array([[-85.2012, 28.4004, 0. , 0. ],
[ 28.4004, -56.8008, 28.4004, 0. ],
[ 0. , 28.4004, -56.8008, 28.4004],
[ 0. , 0. , 28.4004, -28.4004]]),
array([[-227203.2],
[ 0. ],
[ 0. ],
[ 600. ]]))
1model.solve(print_arrays=True)
step: 10
[[ 85.2012 -28.4004 0. 0. ]
[ 28.4004 -56.8008 28.4004 0. ]
[ 0. 28.4004 -56.8008 28.4004]
[ 0. 0. 28.4004 -28.4004]
[-85.2012 28.4004 0. 0. ]
[ 28.4004 -56.8008 28.4004 0. ]
[ 0. 28.4004 -56.8008 28.4004]
[ 0. 0. 28.4004 -28.4004]
[ 0. 0. 0. 0. ]
[ 0. 0. 0. 0. ]
[ 0. 0. 0. 0. ]
[ 0. 0. 0. 0. ]]
[[ 227203.2 -227203.2 0. ]
[ 0. 0. 0. ]
[ 0. 0. 0. ]
[ 600. 600. 0. ]]
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 β.