Skip to content

Commit 9a43518

Browse files
authored
Merge pull request #37 from panasun1994/parallel-improvement
Parallel improvement (switch back to 4-argument input).
2 parents 2e182d9 + ce5173c commit 9a43518

1 file changed

Lines changed: 82 additions & 30 deletions

File tree

‎python/sphinx_docs/docs/parallel-in-python.rst‎

Lines changed: 82 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -35,46 +35,71 @@ Running in parallel needs the following in addition:
3535
An example script for a parallel run
3636
------------------------------------
3737

38-
.. code-block:: python
38+
The example builds the simulator from objects parsed in Python, instead of from
39+
the file name, so that the script can change well controls during the run
40+
through the ``Schedule`` object it passes in, for example with
41+
``schedule.shut_well("PROD", step)``. With the filename constructor the
42+
simulator builds its own ``Schedule``, which the script cannot reach. Passing
43+
``None`` for the ``EclipseState`` is what makes this work in parallel (see
44+
"Constructing the simulator" below). This is a workaround until the simulator
45+
offers a supported way to reach its own ``Schedule``. In a parallel run, make
46+
every such change on every rank: each rank holds its own ``Schedule``, and a
47+
change made only under ``if RANK == 0:`` is silently ignored.
3948

40-
from opm.simulators import BlackOilSimulator
49+
.. code-block:: python
4150
42-
# mpi4py owns MPI_Init/MPI_Finalize; importing it initializes MPI for the
43-
# whole process, including the simulator underneath.
44-
from mpi4py import MPI
51+
# Importing mpi4py initializes MPI for the whole process,
52+
# including the simulator underneath.
53+
from mpi4py import MPI
54+
from opm.simulators import BlackOilSimulator
55+
from opm.io.parser import Parser
56+
from opm.io.ecl_state import EclipseState
57+
from opm.io.schedule import Schedule
58+
from opm.io.summary import SummaryConfig
4559
46-
COMM = MPI.COMM_WORLD
47-
RANK = COMM.Get_rank()
60+
CASE = "SPE1CASE1.DATA"
4861
49-
CASE = "SPE1CASE1.DATA"
62+
COMM = MPI.COMM_WORLD
63+
RANK = COMM.Get_rank()
5064
5165
52-
def main():
53-
sim = BlackOilSimulator(filename=CASE)
66+
def main():
67+
deck = Parser().parse(CASE)
68+
state = EclipseState(deck) # needed to build the Schedule only
69+
schedule = Schedule(deck, state)
70+
summary_config = SummaryConfig(deck, state, schedule)
5471
55-
# init=False: MPI is already initialized by mpi4py.
56-
# finalize=False: keep MPI alive until the script exits.
57-
sim.setup_mpi(init=False, finalize=False)
72+
# Compared with the serial example in "Run OPM Flow from Python", the only
73+
# change is None in place of `state`: in parallel the simulator must build
74+
# the EclipseState itself (see "Constructing the simulator" below).
75+
sim = BlackOilSimulator(deck, None, schedule, summary_config)
76+
# init=False: MPI is already initialized by mpi4py.
77+
# finalize=False: keep MPI alive until the script exits.
78+
sim.setup_mpi(init=False, finalize=False)
5879
59-
sim.step_init()
80+
sim.step_init()
6081
61-
sim.step()
82+
# Change well controls through the Schedule passed to the constructor.
83+
# Every rank must make the same call: a change made only under
84+
# `if RANK == 0:` is silently ignored.
85+
schedule.shut_well("PROD", 3)
6286
63-
# The grid is distributed, so each rank sees only its own cells
64-
# (owned + overlap).
65-
poro = sim.get_porosity()
66-
sim.set_porosity(poro * 0.95)
87+
# The grid is distributed, so each rank sees only its own cells
88+
# (owned + overlap).
89+
poro = sim.get_porosity()
90+
sim.set_porosity(poro * 0.95)
6791
68-
sim.step()
92+
while not sim.check_simulation_finished():
93+
sim.step()
6994
70-
sim.step_cleanup()
95+
sim.step_cleanup()
7196
72-
if RANK == 0:
73-
print("done -- results written to SPE1CASE1.PRT", flush=True)
97+
if RANK == 0:
98+
print("done -- results written to SPE1CASE1.PRT", flush=True)
7499
75100
76-
if __name__ == "__main__":
77-
main()
101+
if __name__ == "__main__":
102+
main()
78103
79104
80105
Run it with:
@@ -89,6 +114,29 @@ and confirm the rank count in the print file:
89114
90115
grep "Number of MPI processes" SPE1CASE1.PRT
91116
117+
To check that the well was shut, read the oil rate of ``PROD`` back from the
118+
summary file once the run has finished:
119+
120+
.. code-block:: python
121+
122+
from opm.io.ecl import ESmry
123+
124+
smry = ESmry("SPE1CASE1.SMSPEC")
125+
print("TIME (days):", [round(float(t), 1) for t in smry["TIME"]])
126+
print("WOPR:PROD: ", [round(v) for v in smry["WOPR:PROD"]])
127+
128+
The output depends on the deck. With the ten-year ``SPE1CASE1.DATA`` from
129+
opm-tests, the lists are long, and the rate is 20000 until day 90 and 0 from
130+
then on. With the ten-day version used by the opm-simulators Python tests
131+
(`python/test_data/SPE1CASE1a
132+
<https://github.com/OPM/opm-simulators/tree/master/python/test_data/SPE1CASE1a>`_),
133+
the rate is 20000 until day 3 and 0 from then on:
134+
135+
.. code-block:: text
136+
137+
TIME (days): [1.0, 1.6, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]
138+
WOPR:PROD: [20000, 20000, 20000, 20000, 0, 0, 0, 0, 0, 0, 0]
139+
92140
93141
Good to know
94142
------------
@@ -124,13 +172,17 @@ Constructing the simulator
124172

125173
.. warning::
126174

127-
In parallel, only the **filename constructor** works:
175+
In parallel, the ``EclipseState`` argument of the four-argument
176+
constructor must be ``None``:
128177

129178
.. code-block:: python
130179
131-
sim = BlackOilSimulator(filename="SPE1CASE1.DATA")
180+
sim = BlackOilSimulator(deck, None, schedule, summary_config)
132181
133-
The four-argument form documented for serial runs —
134-
``BlackOilSimulator(deck, state, schedule, summary_config)`` — cannot run on
135-
more than one rank. It aborts with
182+
Passing an ``EclipseState`` object fails on more than one rank with
136183
``Parallel simulator setup is incorrect as it does not use ParallelEclipseState``.
184+
With ``None``, the simulator builds the parallel state itself from the
185+
DATA file. The filename constructor, ``BlackOilSimulator(filename=...)``,
186+
also works in parallel, but then the script has no access to the
187+
``Schedule`` the simulator uses, so well controls cannot be changed from
188+
Python.

0 commit comments

Comments
 (0)