mirror of
https://github.com/OPM/ResInsight.git
synced 2026-10-25 06:38:13 -05:00
#8370: Add Python SummaryEnsemble helpers, examples and tests
This commit is contained in:
1 parent
60876e0bdd
commit
d93aa839a6
6 files changed
+255
-8
No files matched your search
@@ -0,0 +1,22 @@
|
||||
###############################################################################
|
||||
# Read ensemble summary vectors for all realizations
|
||||
###############################################################################
|
||||
|
||||
import sys
|
||||
|
||||
import rips
|
||||
|
||||
resinsight = rips.Instance.find()
|
||||
project = resinsight.project
|
||||
|
||||
ensembles = project.summary_ensembles()
|
||||
|
||||
if not ensembles:
|
||||
print("No summary ensemble found in the current project")
|
||||
sys.exit(1)
|
||||
|
||||
ensemble = ensembles[0]
|
||||
|
||||
values = ensemble.vector_values("FOPT", time_resolution="YEAR")
|
||||
for real, data in values.items():
|
||||
print(real, data["values"])
|
||||
+3
-8
@@ -12,19 +12,14 @@ resinsight = rips.Instance.find()
|
||||
|
||||
project = resinsight.project
|
||||
|
||||
# Summary ensembles and summary groups are both represented by
|
||||
# SummaryCaseSubCollection. Use the 'is_ensemble' flag to pick the ensembles.
|
||||
ensembles = [
|
||||
collection
|
||||
for collection in project.descendants(rips.SummaryCaseSubCollection)
|
||||
if collection.is_ensemble
|
||||
]
|
||||
# Get all summary ensembles in the project
|
||||
ensembles = project.summary_ensembles()
|
||||
|
||||
if not ensembles:
|
||||
print("No summary ensemble found in the current project")
|
||||
sys.exit(1)
|
||||
|
||||
summary_plot_collection = project.descendants(rips.SummaryPlotCollection)[0]
|
||||
summary_plot_collection = project.summary_plot_collection()
|
||||
|
||||
for ensemble in ensembles:
|
||||
print("Creating plots for ensemble: " + ensemble.summary_collection_name)
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
###############################################################################
|
||||
# Compare multiple ensembles in the same plot, and add more subplots to an
|
||||
# existing summary plot
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
import sys
|
||||
|
||||
import rips
|
||||
|
||||
resinsight = rips.Instance.find()
|
||||
project = resinsight.project
|
||||
|
||||
ensembles = project.summary_ensembles()
|
||||
|
||||
if len(ensembles) < 2:
|
||||
print("At least two summary ensembles are required")
|
||||
sys.exit(1)
|
||||
|
||||
summary_plot_collection = project.summary_plot_collection()
|
||||
|
||||
# One curve set per ensemble is created in the same subplot
|
||||
plot = summary_plot_collection.new_summary_plot(ensembles=ensembles, address="FOPT")
|
||||
|
||||
# Append more subplots to the multi plot containing the first subplot
|
||||
multi_plot = project.descendants(rips.MultiSummaryPlot)[-1]
|
||||
multi_plot.append_subplot(ensembles=ensembles, address="FWPT")
|
||||
multi_plot.append_subplot(ensembles=ensembles, address="FGPT")
|
||||
|
||||
multi_plot.number_of_columns = 2
|
||||
multi_plot.update()
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
###############################################################################
|
||||
# Create several subplots in the same summary multi plot
|
||||
#
|
||||
# Requires a project with at least one summary case
|
||||
###############################################################################
|
||||
|
||||
import sys
|
||||
|
||||
import rips
|
||||
|
||||
resinsight = rips.Instance.find()
|
||||
project = resinsight.project
|
||||
|
||||
summary_cases = project.descendants(rips.SummaryCase)
|
||||
if not summary_cases:
|
||||
print("At least one summary case is required")
|
||||
sys.exit(1)
|
||||
|
||||
summary_plot_collection = project.summary_plot_collection()
|
||||
|
||||
# Create a multi plot with one subplot for each address
|
||||
multi_plot = summary_plot_collection.new_summary_multi_plot(
|
||||
summary_cases=[summary_cases[0]], addresses=["FOPT", "FWPT", "FGPT"]
|
||||
)
|
||||
|
||||
# Subplots can be added later to the same multi plot
|
||||
multi_plot.append_subplot(summary_cases=[summary_cases[0]], address="FOPR")
|
||||
|
||||
multi_plot.number_of_columns = 2
|
||||
multi_plot.update()
|
||||
|
||||
print("Number of subplots:", len(multi_plot.descendants(rips.SummaryPlot)))
|
||||
@@ -0,0 +1,37 @@
|
||||
"""Convenience methods for working with summary ensembles from Python.
|
||||
|
||||
The methods are added to the ``SummaryEnsemble`` class, which is used
|
||||
for both summary ensembles and summary groups. Use the ``is_ensemble`` flag to
|
||||
pick the ensembles.
|
||||
"""
|
||||
|
||||
from typing import Any, Dict, List
|
||||
|
||||
from .pdmobject import add_method
|
||||
from .resinsight_classes import SummaryEnsemble
|
||||
|
||||
|
||||
@add_method(SummaryEnsemble)
|
||||
def vector_values(
|
||||
self: SummaryEnsemble, address: str, time_resolution: str = "NONE"
|
||||
) -> Dict[int, Dict[str, List[Any]]]:
|
||||
"""Read one summary vector for all realizations in the ensemble.
|
||||
|
||||
Arguments:
|
||||
address (str): Summary vector address, for example "FOPT" or "WOPT:A1"
|
||||
time_resolution (str): "NONE" returns the original time steps of each
|
||||
realization. Use "DAY", "WEEK", "MONTH", "QUARTER", "HALFYEAR" or
|
||||
"YEAR" to resample all realizations to a common time axis.
|
||||
|
||||
Returns:
|
||||
dict: Realization (summary case) id mapped to a dictionary with the
|
||||
keys "time_steps" (seconds since epoch) and "values".
|
||||
"""
|
||||
result: Dict[int, Dict[str, List[Any]]] = {}
|
||||
for summary_case in self.summary_cases():
|
||||
data = summary_case.resample_values(address, time_resolution)
|
||||
result[summary_case.id] = {
|
||||
"time_steps": list(data.time_steps),
|
||||
"values": list(data.values),
|
||||
}
|
||||
return result
|
||||
@@ -0,0 +1,130 @@
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
sys.path.insert(1, os.path.join(sys.path[0], "../../"))
|
||||
import rips
|
||||
|
||||
import dataroot
|
||||
|
||||
SOURCE_DIR = dataroot.PATH + "/flow_diagnostics_test"
|
||||
SUMMARY_FILES = ["SIMPLE_SUMMARY2.SMSPEC", "SIMPLE_SUMMARY2.UNSMRY"]
|
||||
REALIZATION_COUNT = 3
|
||||
|
||||
|
||||
def _create_ensemble_files(root_dir, name):
|
||||
"""Copy the summary files into realization-N/iter-0 folders"""
|
||||
smspec_files = []
|
||||
for real in range(REALIZATION_COUNT):
|
||||
folder = os.path.join(str(root_dir), name, f"realization-{real}", "iter-0")
|
||||
os.makedirs(folder)
|
||||
for file_name in SUMMARY_FILES:
|
||||
shutil.copy(os.path.join(SOURCE_DIR, file_name), folder)
|
||||
smspec_files.append(os.path.abspath(os.path.join(folder, SUMMARY_FILES[0])))
|
||||
return smspec_files
|
||||
|
||||
|
||||
def _import_ensemble(rips_instance, root_dir, name):
|
||||
files = _create_ensemble_files(root_dir, name)
|
||||
return rips_instance.project.import_summary_ensemble(
|
||||
file_names=files, ensemble_name=name
|
||||
)
|
||||
|
||||
|
||||
def test_import_ensemble_and_list_cases(rips_instance, initialize_test, tmp_path):
|
||||
ensemble = _import_ensemble(rips_instance, tmp_path, "ens_a")
|
||||
|
||||
assert ensemble.is_ensemble
|
||||
assert ensemble.summary_collection_name == "ens_a"
|
||||
assert len(ensemble.summary_cases()) == REALIZATION_COUNT
|
||||
|
||||
|
||||
def test_vector_values(rips_instance, initialize_test, tmp_path):
|
||||
ensemble = _import_ensemble(rips_instance, tmp_path, "ens_a")
|
||||
|
||||
values = ensemble.vector_values("FOPT", "NONE")
|
||||
assert len(values) == REALIZATION_COUNT
|
||||
for data in values.values():
|
||||
assert len(data["values"]) == 60
|
||||
assert len(data["time_steps"]) == 60
|
||||
|
||||
|
||||
def test_set_vector_after_project_reload(rips_instance, initialize_test, tmp_path):
|
||||
_import_ensemble(rips_instance, tmp_path, "ens_a")
|
||||
project_path = str(tmp_path / "ensemble.rsp")
|
||||
rips_instance.project.save(project_path)
|
||||
rips_instance.project.close()
|
||||
rips_instance.project.open(project_path)
|
||||
|
||||
ensemble = rips_instance.project.summary_ensembles()[0]
|
||||
summary_case = ensemble.summary_cases()[0]
|
||||
count = len(summary_case.available_time_steps().values)
|
||||
|
||||
# Realization addresses are created lazily after a project is loaded
|
||||
summary_case.set_summary_values("FWCT_N", "", [0.5] * count)
|
||||
with pytest.raises(rips.RipsError):
|
||||
summary_case.set_summary_values("FOPT", "", [0.5] * count)
|
||||
|
||||
|
||||
def test_multiple_ensembles_in_one_subplot(rips_instance, initialize_test, tmp_path):
|
||||
ensemble_a = _import_ensemble(rips_instance, tmp_path, "ens_a")
|
||||
ensemble_b = _import_ensemble(rips_instance, tmp_path, "ens_b")
|
||||
|
||||
collection = rips_instance.project.summary_plot_collection()
|
||||
plot = collection.new_summary_plot(
|
||||
ensembles=[ensemble_a, ensemble_b], address="FOPT"
|
||||
)
|
||||
assert plot is not None
|
||||
|
||||
curve_sets = plot.descendants(rips.EnsembleCurveSet)
|
||||
assert len(curve_sets) == 2
|
||||
assert len(rips_instance.project.descendants(rips.MultiSummaryPlot)) == 1
|
||||
|
||||
|
||||
def test_append_subplot(rips_instance, initialize_test, tmp_path):
|
||||
ensemble = _import_ensemble(rips_instance, tmp_path, "ens_a")
|
||||
|
||||
collection = rips_instance.project.summary_plot_collection()
|
||||
collection.new_summary_plot(ensemble=ensemble, address="FOPT")
|
||||
|
||||
multi_plot = rips_instance.project.descendants(rips.MultiSummaryPlot)[0]
|
||||
assert len(multi_plot.descendants(rips.SummaryPlot)) == 1
|
||||
|
||||
new_plot = multi_plot.append_subplot(ensembles=[ensemble], address="FWPT")
|
||||
assert new_plot is not None
|
||||
assert len(rips_instance.project.descendants(rips.MultiSummaryPlot)) == 1
|
||||
assert len(multi_plot.descendants(rips.SummaryPlot)) == 2
|
||||
|
||||
with pytest.raises(Exception):
|
||||
multi_plot.append_subplot()
|
||||
|
||||
|
||||
def test_new_summary_multi_plot(rips_instance, initialize_test, tmp_path):
|
||||
ensemble = _import_ensemble(rips_instance, tmp_path, "ens_a")
|
||||
|
||||
collection = rips_instance.project.summary_plot_collection()
|
||||
multi_plot = collection.new_summary_multi_plot(
|
||||
ensembles=[ensemble], addresses=["FOPT", "FWPT"]
|
||||
)
|
||||
assert multi_plot is not None
|
||||
assert len(rips_instance.project.descendants(rips.MultiSummaryPlot)) == 1
|
||||
assert len(multi_plot.descendants(rips.SummaryPlot)) == 2
|
||||
|
||||
with pytest.raises(Exception):
|
||||
collection.new_summary_multi_plot(ensembles=[ensemble])
|
||||
|
||||
|
||||
def test_project_summary_ensembles(rips_instance, initialize_test, tmp_path):
|
||||
assert rips_instance.project.summary_ensembles() == []
|
||||
|
||||
ensemble = _import_ensemble(rips_instance, tmp_path, "ens_a")
|
||||
ensembles = rips_instance.project.summary_ensembles()
|
||||
assert len(ensembles) == 1
|
||||
assert ensembles[0].summary_collection_name == ensemble.summary_collection_name
|
||||
|
||||
|
||||
def test_project_summary_plot_collection(rips_instance, initialize_test):
|
||||
collection = rips_instance.project.summary_plot_collection()
|
||||
assert isinstance(collection, rips.SummaryPlotCollection)
|
||||
Reference in new issue
Block a user