mirror of
https://github.com/OPM/ResInsight.git
synced 2026-10-31 17:17:50 -05:00
RimWellPathCollection_importWellLogFiles imports LAS files given explicitly and/or all LAS files in a folder. RicfImportWellLogFiles forwards to it. Project.import_well_paths() and Project.import_well_log_files() in Python now call the object methods on the well path collection. Their signatures and return values are unchanged, so no deprecation.
612 lines
17 KiB
Python
612 lines
17 KiB
Python
# pylint: disable=too-many-arguments
|
|
# pylint: disable=no-member
|
|
"""
|
|
The ResInsight project module
|
|
"""
|
|
|
|
import grpc
|
|
import uuid
|
|
import warnings
|
|
|
|
from .case import Case
|
|
from .gridcasegroup import GridCaseGroup
|
|
from .pdmobject import add_method, add_static_method
|
|
from .plot import Plot as Plot
|
|
from .view import View
|
|
|
|
import Commands_pb2
|
|
from Definitions_pb2 import Empty
|
|
import Project_pb2_grpc
|
|
import KeyValueStore_pb2_grpc
|
|
import KeyValueStore_pb2
|
|
|
|
from .exception import RipsError
|
|
from .resinsight_classes import (
|
|
ColorLegendCollection,
|
|
FractureTemplate,
|
|
PlotWindow,
|
|
Project,
|
|
Reservoir,
|
|
SnapshotContentType,
|
|
SnapshotFileFormat,
|
|
SummaryCase,
|
|
WellPath,
|
|
)
|
|
|
|
from typing import Optional, List
|
|
|
|
|
|
@add_method(Project)
|
|
def __custom_init__(self, pb2_object, channel: grpc.Channel) -> None:
|
|
self._project_stub = Project_pb2_grpc.ProjectStub(self._channel)
|
|
self.__key_value_store_stub = KeyValueStore_pb2_grpc.KeyValueStoreStub(
|
|
self._channel
|
|
)
|
|
|
|
# Public properties
|
|
self.chunk_size = 8160
|
|
|
|
|
|
@add_static_method(Project)
|
|
def create(channel: grpc.Channel) -> Project:
|
|
project_stub = Project_pb2_grpc.ProjectStub(channel)
|
|
pb2_object = project_stub.GetPdmObject(Empty())
|
|
return Project(pb2_object, channel)
|
|
|
|
|
|
@add_method(Project)
|
|
def open(self, path):
|
|
"""Open a new project from the given path
|
|
|
|
Arguments:
|
|
path(str): path to project file
|
|
|
|
"""
|
|
self._execute_command(openProject=Commands_pb2.FilePathRequest(path=path))
|
|
return self
|
|
|
|
|
|
@add_method(Project)
|
|
def save(self, path=""):
|
|
"""Save the project to the existing project file, or to a new file
|
|
|
|
Arguments:
|
|
path(str): File path to the file to save the project to. If empty, saves to the active project file
|
|
"""
|
|
self._execute_command(saveProject=Commands_pb2.SaveProjectRequest(filePath=path))
|
|
return self
|
|
|
|
|
|
@add_method(Project)
|
|
def close(self) -> None:
|
|
"""Close the current project (and open new blank project)"""
|
|
self._execute_command(closeProject=Empty())
|
|
|
|
|
|
@add_method(Project)
|
|
def selected_cases(self) -> List[Case]:
|
|
"""Get a list of all grid cases selected in the project tree
|
|
|
|
Returns:
|
|
A list of :class:`rips.generated.generated_classes.Case`
|
|
"""
|
|
case_infos = self._project_stub.GetSelectedCases(Empty())
|
|
cases = []
|
|
for case_info in case_infos.data:
|
|
cases.append(self.case(case_info.id))
|
|
return cases
|
|
|
|
|
|
@add_method(Project)
|
|
def cases(self: Project) -> List[Reservoir]:
|
|
"""Get a list of all grid cases in the project
|
|
|
|
Returns:
|
|
A list of :class:`rips.generated.generated_classes.Case`
|
|
"""
|
|
return self.descendants(Reservoir)
|
|
|
|
|
|
@add_method(Project)
|
|
def case(self: Project, case_id: int) -> Optional[Reservoir]:
|
|
"""Get a specific grid case from the provided case Id
|
|
|
|
Arguments:
|
|
id(int): case id
|
|
Returns:
|
|
:class:`rips.generated.resinsight_classes.Case`
|
|
"""
|
|
allCases = self.cases()
|
|
for case in allCases:
|
|
if case.id == case_id:
|
|
return case
|
|
return None
|
|
|
|
|
|
@add_method(Project)
|
|
def replace_source_cases(self, grid_list_file, case_group_id=0):
|
|
"""Replace all source grid cases within a case group
|
|
|
|
Deprecated: use GridCaseGroup.replace_source_cases(grid_files=[...]) on the group object instead,
|
|
which takes the grid file paths directly.
|
|
|
|
Arguments:
|
|
grid_list_file (str): path to file containing a list of cases
|
|
case_group_id (int): id of the case group to replace
|
|
"""
|
|
warnings.warn(
|
|
"Project.replace_source_cases() is deprecated, use GridCaseGroup.replace_source_cases(grid_files=[...]) instead",
|
|
DeprecationWarning,
|
|
stacklevel=3,
|
|
)
|
|
return self._execute_command(
|
|
replaceSourceCases=Commands_pb2.ReplaceSourceCasesRequest(
|
|
gridListFile=grid_list_file, caseGroupId=case_group_id
|
|
)
|
|
)
|
|
|
|
|
|
@add_method(Project)
|
|
def summary_cases(self):
|
|
"""Get a list of all summary cases in the Project
|
|
|
|
Returns: A list of :class:`rips.generated.resinsight_classes.SummaryCase`
|
|
"""
|
|
return self.descendants(SummaryCase)
|
|
|
|
|
|
@add_method(Project)
|
|
def color_legend_collection(self: Project) -> Optional[ColorLegendCollection]:
|
|
"""Get the project's color legend collection
|
|
|
|
Returns:
|
|
:class:`rips.generated.generated_classes.ColorLegendCollection`
|
|
"""
|
|
collections = self.descendants(ColorLegendCollection)
|
|
return collections[0] if collections else None
|
|
|
|
|
|
@add_method(Project)
|
|
def views(self):
|
|
"""Get a list of views belonging to a project"""
|
|
return self.descendants(View)
|
|
|
|
|
|
@add_method(Project)
|
|
def view(self, view_id):
|
|
"""Get a particular view belonging to a case by providing view id
|
|
|
|
Arguments:
|
|
view_id(int): view id
|
|
Returns:
|
|
:class:`rips.generated.generated_classes.View`
|
|
"""
|
|
views = self.views()
|
|
for view_object in views:
|
|
if view_object.id == view_id:
|
|
return view_object
|
|
return None
|
|
|
|
|
|
@add_method(Project)
|
|
def plots(self):
|
|
"""Get a list of all plots belonging to a project
|
|
|
|
Returns:
|
|
List of :class:`rips.generated.generated_classes.Plot`
|
|
"""
|
|
resinsight_classes = self.descendants(PlotWindow)
|
|
plot_list = []
|
|
for pdm_object in resinsight_classes:
|
|
if pdm_object.id != -1:
|
|
plot_list.append(pdm_object)
|
|
return plot_list
|
|
|
|
|
|
@add_method(Project)
|
|
def plot(self, view_id):
|
|
"""Get a particular plot by providing view id
|
|
|
|
Arguments:
|
|
view_id(int): view id
|
|
|
|
Returns:
|
|
:class:`rips.generated.generated_classes.Plot`
|
|
"""
|
|
plots = self.plots()
|
|
for plot_object in plots:
|
|
if plot_object.id == view_id:
|
|
return plot_object
|
|
return None
|
|
|
|
|
|
@add_method(Project)
|
|
def grid_case_groups(self):
|
|
"""Get a list of all grid case groups in the project
|
|
|
|
Returns:
|
|
List of :class:`rips.generated.generated_classes.GridCaseGroup`
|
|
|
|
"""
|
|
case_groups = self.descendants(GridCaseGroup)
|
|
return case_groups
|
|
|
|
|
|
@add_method(Project)
|
|
def grid_case_group(self, group_id):
|
|
"""Get a particular grid case group belonging to a project
|
|
|
|
Arguments:
|
|
groupId(int): group id
|
|
|
|
Returns:
|
|
:class:`rips.generated.generated_classes.GridCaseGroup`
|
|
"""
|
|
case_groups = self.grid_case_groups()
|
|
for case_group in case_groups:
|
|
if case_group.group_id == group_id:
|
|
return case_group
|
|
return None
|
|
|
|
|
|
@add_method(Project)
|
|
def export_multi_case_snapshots(self, grid_list_file):
|
|
"""Export snapshots for a set of cases
|
|
|
|
Arguments:
|
|
grid_list_file (str): Path to a file containing a list of grids to export snapshot for
|
|
"""
|
|
return self._execute_command(
|
|
exportMultiCaseSnapshot=Commands_pb2.ExportMultiCaseRequest(
|
|
gridListFile=grid_list_file
|
|
)
|
|
)
|
|
|
|
|
|
@add_method(Project)
|
|
def export_snapshots(
|
|
self,
|
|
snapshot_type="ALL",
|
|
prefix="",
|
|
plot_format="PNG",
|
|
width=-1,
|
|
height=-1,
|
|
content_type=None,
|
|
export_folder="",
|
|
plot_file_format=None,
|
|
):
|
|
"""Export all snapshots of a given type
|
|
|
|
Arguments:
|
|
snapshot_type (str): Deprecated, use content_type. Enum string ('ALL', 'VIEWS' or 'PLOTS')
|
|
prefix (str): Exported file name prefix
|
|
plot_format(str): Deprecated, use plot_file_format. Enum string, 'PNG' or 'PDF'
|
|
width (int): The width of the exported snapshots. By default will use the existing size.
|
|
height (int): The height of the exported snapshots. By default will use the existing size.
|
|
content_type (SnapshotContentType): 'ALL', 'VIEWS' or 'PLOTS'
|
|
export_folder (str): The path to export to. By default will use the 'snapshots' folder next to the project file.
|
|
plot_file_format (SnapshotFileFormat): 'PNG' or 'PDF'
|
|
"""
|
|
if snapshot_type != "ALL":
|
|
warnings.warn(
|
|
"Project.export_snapshots(snapshot_type=...) is deprecated, use content_type=...",
|
|
DeprecationWarning,
|
|
stacklevel=3,
|
|
)
|
|
if plot_format != "PNG":
|
|
warnings.warn(
|
|
"Project.export_snapshots(plot_format=...) is deprecated, use plot_file_format=...",
|
|
DeprecationWarning,
|
|
stacklevel=3,
|
|
)
|
|
if content_type is None:
|
|
content_type = SnapshotContentType(snapshot_type)
|
|
if plot_file_format is None:
|
|
plot_file_format = SnapshotFileFormat(plot_format)
|
|
|
|
self._call_pdm_method_void(
|
|
"exportSnapshots",
|
|
content_type=content_type,
|
|
export_folder=export_folder,
|
|
prefix=prefix,
|
|
width=width,
|
|
height=height,
|
|
plot_file_format=plot_file_format,
|
|
)
|
|
|
|
|
|
@add_method(Project)
|
|
def export_well_paths(self, well_paths=None, md_step_size=5.0):
|
|
"""Export a set of well paths
|
|
|
|
Arguments:
|
|
well_paths(list): List of strings of well paths. If none, export all.
|
|
md_step_size(double): resolution of the exported well path
|
|
"""
|
|
if well_paths is None:
|
|
well_paths = []
|
|
elif isinstance(well_paths, str):
|
|
well_paths = [well_paths]
|
|
return self._execute_command(
|
|
exportWellPaths=Commands_pb2.ExportWellPathRequest(
|
|
wellPathNames=well_paths, mdStepSize=md_step_size
|
|
)
|
|
)
|
|
|
|
|
|
def _fracture_template_by_id(project, template_id):
|
|
for template in project.descendants(FractureTemplate):
|
|
if template.id == template_id:
|
|
return template
|
|
raise RipsError(f"Could not find fracture template with ID {template_id}")
|
|
|
|
|
|
@add_method(Project)
|
|
def scale_fracture_template(
|
|
self, template_id, half_length, height, d_factor, conductivity
|
|
):
|
|
"""Scale fracture template parameters
|
|
|
|
Deprecated: use FractureTemplate.set_scale_factors(...) on the template object instead.
|
|
|
|
Arguments:
|
|
template_id(int): ID of fracture template
|
|
half_length (double): Half Length scale factor
|
|
height (double): Height scale factor
|
|
d_factor (double): D-factor scale factor
|
|
conductivity (double): Conductivity scale factor
|
|
"""
|
|
warnings.warn(
|
|
"Project.scale_fracture_template(template_id=...) is deprecated, use FractureTemplate.set_scale_factors() instead",
|
|
DeprecationWarning,
|
|
stacklevel=3,
|
|
)
|
|
template = _fracture_template_by_id(self, template_id)
|
|
template.set_scale_factors(
|
|
half_length=half_length,
|
|
height=height,
|
|
d_factor=d_factor,
|
|
conductivity=conductivity,
|
|
)
|
|
|
|
|
|
@add_method(Project)
|
|
def set_fracture_containment(self, template_id, top_layer, base_layer):
|
|
"""Set fracture template containment parameters
|
|
|
|
Deprecated: use FractureTemplate.set_containment(...) on the template object instead.
|
|
|
|
Arguments:
|
|
template_id(int): ID of fracture template
|
|
top_layer (int): Top layer containment
|
|
base_layer (int): Base layer containment
|
|
"""
|
|
warnings.warn(
|
|
"Project.set_fracture_containment(template_id=...) is deprecated, use FractureTemplate.set_containment() instead",
|
|
DeprecationWarning,
|
|
stacklevel=3,
|
|
)
|
|
template = _fracture_template_by_id(self, template_id)
|
|
template.set_containment(top_layer=top_layer, base_layer=base_layer)
|
|
|
|
|
|
@add_method(Project)
|
|
def import_well_paths(self, well_path_files=None, well_path_folder=""):
|
|
"""Import well paths into project
|
|
|
|
Arguments:
|
|
well_path_files(list): list of file paths to import
|
|
well_path_folder(str): A folder path containing files to import
|
|
|
|
Returns:
|
|
List of :class:`rips.generated.generated_classes.WellPath`
|
|
"""
|
|
if well_path_files is None:
|
|
well_path_files = []
|
|
|
|
names = self.well_path_collection().import_well_paths(
|
|
well_path_files=well_path_files, well_path_folder=well_path_folder
|
|
)
|
|
well_paths = []
|
|
for well_path_name in names.values:
|
|
well_paths.append(self.well_path_by_name(well_path_name))
|
|
return well_paths
|
|
|
|
|
|
@add_method(Project)
|
|
def well_paths(self):
|
|
"""Get a list of all well paths in the project
|
|
|
|
Returns:
|
|
List of :class:`rips.generated.generated_classes.WellPath`
|
|
"""
|
|
return self.descendants(WellPath)
|
|
|
|
|
|
@add_method(Project)
|
|
def well_path_by_name(self, well_path_name):
|
|
"""Get a specific well path by name from the project
|
|
|
|
Returns:
|
|
:class:`rips.generated.generated_classes.WellPath`
|
|
"""
|
|
all_well_paths = self.well_paths()
|
|
for well_path in all_well_paths:
|
|
if well_path.name == well_path_name:
|
|
return well_path
|
|
return None
|
|
|
|
|
|
@add_method(Project)
|
|
def import_well_log_files(self, well_log_files=None, well_log_folder=""):
|
|
"""Import well log files into project
|
|
|
|
Arguments:
|
|
well_log_files(list): list of file paths to import
|
|
well_log_folder(str): A folder path containing files to import
|
|
|
|
Returns:
|
|
A list of well path names (strings)
|
|
"""
|
|
if well_log_files is None:
|
|
well_log_files = []
|
|
|
|
names = self.well_path_collection().import_well_log_files(
|
|
well_log_files=well_log_files, well_log_folder=well_log_folder
|
|
)
|
|
return list(names.values)
|
|
|
|
|
|
@add_method(Project)
|
|
def import_formation_names(self, formation_files=None):
|
|
"""Import formation names into project
|
|
|
|
Arguments:
|
|
formation_files(list): list of files to import
|
|
|
|
"""
|
|
if formation_files is None:
|
|
formation_files = []
|
|
elif isinstance(formation_files, str):
|
|
formation_files = [formation_files]
|
|
|
|
self._execute_command(
|
|
importFormationNames=Commands_pb2.ImportFormationNamesRequest(
|
|
formationFiles=formation_files, applyToCaseId=-1
|
|
)
|
|
)
|
|
|
|
|
|
@add_method(Project)
|
|
def create_corner_point_grid(
|
|
self,
|
|
name: str,
|
|
nx: int,
|
|
ny: int,
|
|
nz: int,
|
|
coord: List[float],
|
|
zcorn: List[float],
|
|
actnum: List[int],
|
|
):
|
|
"""Creates a corner point grid from given parameters.
|
|
|
|
Arguments:
|
|
name(str): Name of the grid.
|
|
nx(int): Number of cells in x direction
|
|
ny(int): Number of cells in y direction
|
|
nz(int): Number of cells in z direction
|
|
coord(list[float]): Coordinate lines as COORD keyword in Eclipse.
|
|
Each coordinate line is defined by two points (top and bottom).
|
|
Size: (nx+1) * (ny+1) * 2 * 3. Points are ordered as in Eclipse.
|
|
zcorn(list[float]): Corner depths as defined by the Eclipse keyword ZCORN.
|
|
Size: nx * ny * nz * 8
|
|
actnum(list[int]): Active cell info: cells with values > 0 are active.
|
|
Size: nx * ny * nz
|
|
"""
|
|
|
|
# Generate unique keys for three arrays
|
|
coord_key = "{}_{}".format(uuid.uuid4(), "coord")
|
|
zcorn_key = "{}_{}".format(uuid.uuid4(), "zcorn")
|
|
actnum_key = "{}_{}".format(uuid.uuid4(), "actnum")
|
|
|
|
self.set_key_values(coord_key, coord)
|
|
self.set_key_values(zcorn_key, zcorn)
|
|
self.set_key_values(actnum_key, actnum)
|
|
return self.create_grid_from_key_values(
|
|
name=name,
|
|
nx=nx,
|
|
ny=ny,
|
|
nz=nz,
|
|
coord_key=coord_key,
|
|
zcorn_key=zcorn_key,
|
|
actnum_key=actnum_key,
|
|
)
|
|
|
|
|
|
@add_method(Project)
|
|
def set_key_values(self, key, values):
|
|
"""Sets values for a given key in the key-value store.
|
|
|
|
Arguments:
|
|
key(str): The key (should be unique).
|
|
values(list): a list of double precision floating point numbers
|
|
"""
|
|
request_iterator = self.__generate_key_value_store_input_chunks(values, key)
|
|
reply = self.__key_value_store_stub.SetValue(request_iterator)
|
|
if reply.accepted_value_count < len(values):
|
|
raise IndexError
|
|
|
|
|
|
@add_method(Project)
|
|
def __generate_key_value_store_input_chunks(self, array, name):
|
|
index = -1
|
|
while index < len(array):
|
|
chunk = KeyValueStore_pb2.KeyValueStoreInputChunk()
|
|
if index == -1:
|
|
parameters = KeyValueStore_pb2.KeyValueInputParameters(
|
|
name=name, num_elements=len(array)
|
|
)
|
|
chunk.parameters.CopyFrom(parameters)
|
|
index += 1
|
|
else:
|
|
actual_chunk_size = min(len(array) - index + 1, self.chunk_size)
|
|
chunk.values.CopyFrom(
|
|
KeyValueStore_pb2.KeyValueStoreChunk(
|
|
values=array[index : index + actual_chunk_size]
|
|
)
|
|
)
|
|
index += actual_chunk_size
|
|
|
|
yield chunk
|
|
# Final empty message to signal completion
|
|
chunk = KeyValueStore_pb2.KeyValueStoreInputChunk()
|
|
yield chunk
|
|
|
|
|
|
@add_method(Project)
|
|
def key_values_async(
|
|
self,
|
|
key,
|
|
):
|
|
"""Get float values for a given key. Async: returns an iterator.
|
|
|
|
Arguments:
|
|
key(str): The key to get.
|
|
|
|
Returns:
|
|
An iterator to a chunk object containing an array of float values
|
|
Loop through the chunks and then the values within the chunk to get all values.
|
|
"""
|
|
request = KeyValueStore_pb2.KeyValueStoreOutputRequest(name=key)
|
|
for chunk in self.__key_value_store_stub.GetValue(request):
|
|
yield chunk
|
|
|
|
|
|
@add_method(Project)
|
|
def key_values(self, key):
|
|
"""Get float values for a given key. Synchronous: returns a list. `
|
|
|
|
Arguments:
|
|
key(str): The key to get.
|
|
|
|
Returns:
|
|
A list of float values
|
|
"""
|
|
all_values = []
|
|
generator = self.key_values_async(key)
|
|
for chunk in generator:
|
|
for value in chunk.values:
|
|
all_values.append(value)
|
|
return all_values
|
|
|
|
|
|
@add_method(Project)
|
|
def remove_key_values(self, key):
|
|
"""Remove the values for a given key in the key-value store.
|
|
|
|
Arguments:
|
|
key(str): The key.
|
|
"""
|
|
request = KeyValueStore_pb2.KeyValueStoreRemoveRequest(name=key)
|
|
self.__key_value_store_stub.RemoveValue(request)
|