diff --git a/src/fmu/sumo/explorer/objects/__init__.py b/src/fmu/sumo/explorer/objects/__init__.py index 42c32626..33cd6861 100644 --- a/src/fmu/sumo/explorer/objects/__init__.py +++ b/src/fmu/sumo/explorer/objects/__init__.py @@ -10,6 +10,8 @@ from fmu.sumo.explorer.objects.surface import Surface from fmu.sumo.explorer.objects.polygons import Polygons from fmu.sumo.explorer.objects.table import Table +from fmu.sumo.explorer.objects.cpgrid import CPGrid +from fmu.sumo.explorer.objects.cpgrid_property import CPGridProperty from fmu.sumo.explorer.objects.iteration import Iteration from fmu.sumo.explorer.objects.iterations import Iterations from fmu.sumo.explorer.objects.realization import Realization diff --git a/src/fmu/sumo/explorer/objects/_search_context.py b/src/fmu/sumo/explorer/objects/_search_context.py index fb749263..5afe0afd 100644 --- a/src/fmu/sumo/explorer/objects/_search_context.py +++ b/src/fmu/sumo/explorer/objects/_search_context.py @@ -324,10 +324,12 @@ def _to_sumo(self, obj, blob=None): "polygons": objects.Polygons, "surface": objects.Surface, "table": objects.Table, + "cpgrid": objects.CPGrid, + "cpgrid_property": objects.CPGridProperty }.get(cls) if constructor is None: - warnings.warn(f"No constructor for class {cls}") - constructor = objects.Child + warnings.warn(f"No constructor for class {cls}") + constructor = objects.Child return constructor(self._sumo, obj, blob) def __len__(self): @@ -557,7 +559,7 @@ def _maybe_prefetch(self, index): uuid = self._hits[index] if self._cache.has(uuid): return - uuids = self._hits[index : min(index + 100, len(self._hits))] + uuids = self._hits[index:min(index + 100, len(self._hits))] uuids = [uuid for uuid in uuids if not self._cache.has(uuid)] hits = self.__search_all( {"ids": {"values": uuids}}, @@ -575,7 +577,7 @@ async def _maybe_prefetch_async(self, index): uuid = self._hits[index] if self._cache.has(uuid): return - uuids = self._hits[index : min(index + 100, len(self._hits))] + uuids = self._hits[index:min(index + 100, len(self._hits))] uuids = [uuid for uuid in uuids if not self._cache.has(uuid)] hits = await self.__search_all_async( {"ids": {"values": uuids}}, @@ -809,17 +811,38 @@ def all(self): @property def cases(self): """Cases from current selection.""" - return objects.Cases(self) + uuids = self._get_field_values("fmu.case.uuid.keyword") + return objects.Cases(self, uuids) + + @property + async def cases_async(self): + """Cases from current selection.""" + uuids = await self._get_field_values_async("fmu.case.uuid.keyword") + return objects.Cases(self, uuids) @property def iterations(self): """Iterations from current selection.""" - return objects.Iterations(self) + uuids = self._get_field_values("fmu.iteration.uuid.keyword") + return objects.Iterations(self, uuids) + + @property + async def iterations_async(self): + """Iterations from current selection.""" + uuids = await self._get_field_values_async("fmu.iteration.uuid.keyword") + return objects.Iterations(self, uuids) @property def realizations(self): """Realizations from current selection.""" - return objects.Realizations(self) + uuids = self._get_field_values("fmu.realization.uuid.keyword") + return objects.Realizations(self, uuids) + + @property + async def realizations_async(self): + """Realizations from current selection.""" + uuids = await self._get_field_values_async("fmu.realization.uuid.keyword") + return objects.Realizations(self, uuids) @property def template_paths(sc): @@ -958,6 +981,14 @@ def polygons(self): def dictionaries(self): return self._context_for_class("dictionary") + @property + def grids(self): + return self._context_for_class("cpgrid") + + @property + def grid_properties(self): + return self._context_for_class("cpgrid_property") + def _get_object_by_class_and_uuid(self, cls, uuid): obj = self.get_object(uuid) if obj.metadata["class"] != cls: diff --git a/src/fmu/sumo/explorer/objects/cases.py b/src/fmu/sumo/explorer/objects/cases.py index e607aec5..a7d20c9e 100644 --- a/src/fmu/sumo/explorer/objects/cases.py +++ b/src/fmu/sumo/explorer/objects/cases.py @@ -3,42 +3,11 @@ from fmu.sumo.explorer.objects._search_context import SearchContext class Cases(SearchContext): - def __init__(self, sc): - super().__init__(sc._sumo, sc._must, sc._must_not) + def __init__(self, sc, uuids): + super().__init__(sc._sumo, must=[{"terms": {"fmu.case.uuid.keyword": uuids}}]) + self._hits = uuids return - def __len__(self): - if self._length is None: - if self._hits is None: - self._hits = self._search_all(select=False) - pass - self._length = len(self._hits) - pass - return self._length - - async def length_async(self): - if self._length is None: - if self._hits is None: - self._hits = self._search_all(select=False) - pass - self._length = len(self._hits) - pass - return self._length - - def _search_all(self, select=False): - uuids = self._get_field_values("fmu.case.uuid.keyword") - if select is False: - return uuids - # ELSE - return SearchContext(must=[{"ids": {"values": uuids}}])._search_all(select=select) - - async def _search_all_async(self, select=False): - uuids = await self._get_field_values_async("fmu.case.uuid.keyword") - if select is False: - return uuids - # ELSE - return await SearchContext(must=[{"ids": {"values": uuids}}])._search_all_async(select=select) - def _maybe_prefetch(self, index): return diff --git a/src/fmu/sumo/explorer/objects/cpgrid.py b/src/fmu/sumo/explorer/objects/cpgrid.py new file mode 100644 index 00000000..fceb291f --- /dev/null +++ b/src/fmu/sumo/explorer/objects/cpgrid.py @@ -0,0 +1,46 @@ +"""Module containing class for cpgrid""" + +from typing import Dict +from sumo.wrapper import SumoClient +from fmu.sumo.explorer.objects._child import Child + +class CPGrid(Child): + """Class representing a cpgrid object in Sumo.""" + + def __init__(self, sumo: SumoClient, metadata: Dict, blob=None) -> None: + """ + Args: + sumo (SumoClient): connection to Sumo + metadata (dict): dictionary metadata + blob: data object + """ + super().__init__(sumo, metadata, blob) + + def to_cpgrid(self): + """Get cpgrid object as a Grid + Returns: + Grid: A Grid object + """ + try: + from xtgeo import grid_from_file + except ModuleNotFoundError: + raise RuntimeError("Unable to import xtgeo; probably not installed.") + try: + return grid_from_file(self.blob) + except TypeError as type_err: + raise TypeError(f"Unknown format: {self.format}") from type_err + + async def to_cpgrid_async(self): + """Get cpgrid object as a Grid + Returns: + Grid: A Grid object + """ + try: + from xtgeo import grid_from_file + except ModuleNotFoundError: + raise RuntimeError("Unable to import xtgeo; probably not installed.") + + try: + return grid_from_file(await self.blob_async) + except TypeError as type_err: + raise TypeError(f"Unknown format: {self.format}") from type_err diff --git a/src/fmu/sumo/explorer/objects/cpgrid_property.py b/src/fmu/sumo/explorer/objects/cpgrid_property.py new file mode 100644 index 00000000..b146aa94 --- /dev/null +++ b/src/fmu/sumo/explorer/objects/cpgrid_property.py @@ -0,0 +1,46 @@ +"""Module containing class for cpgrid_property""" + +from typing import Dict +from sumo.wrapper import SumoClient +from fmu.sumo.explorer.objects._child import Child + +class CPGridProperty(Child): + """Class representing a cpgrid_property object in Sumo.""" + + def __init__(self, sumo: SumoClient, metadata: Dict, blob=None) -> None: + """ + Args: + sumo (SumoClient): connection to Sumo + metadata (dict): dictionary metadata + blob: data object + """ + super().__init__(sumo, metadata, blob) + + def to_cpgrid_property(self): + """Get cpgrid_property object as a GridProperty + Returns: + GridProperty: A GridProperty object + """ + try: + from xtgeo import gridproperty_from_file + except ModuleNotFoundError: + raise RuntimeError("Unable to import xtgeo; probably not installed.") + try: + return gridproperty_from_file(self.blob) + except TypeError as type_err: + raise TypeError(f"Unknown format: {self.format}") from type_err + + async def to_cpgrid_property_async(self): + """Get cpgrid_property object as a GridProperty + Returns: + GridProperty: A GridProperty object + """ + try: + from xtgeo import gridproperty_from_file + except ModuleNotFoundError: + raise RuntimeError("Unable to import xtgeo; probably not installed.") + + try: + return gridproperty_from_file(await self.blob_async) + except TypeError as type_err: + raise TypeError(f"Unknown format: {self.format}") from type_err diff --git a/src/fmu/sumo/explorer/objects/iterations.py b/src/fmu/sumo/explorer/objects/iterations.py index fecb0d9f..dd942d94 100644 --- a/src/fmu/sumo/explorer/objects/iterations.py +++ b/src/fmu/sumo/explorer/objects/iterations.py @@ -4,34 +4,11 @@ from fmu.sumo.explorer.objects._search_context import SearchContext class Iterations(SearchContext): - def __init__(self, _search_context): - super().__init__(_search_context._sumo, _search_context._must, _search_context._must_not) + def __init__(self, sc, uuids): + super().__init__(sc._sumo, must=[{"terms": {"fmu.iteration.uuid.keyword": uuids}}]) + self._hits = uuids return - def __len__(self): - if self._length is None: - if self._hits is None: - self._hits = self._search_all(select=False) - pass - self._length = len(self._hits) - pass - return self._length - - async def length_async(self): - if self._length is None: - if self._hits is None: - self._hits = self._search_all(select=False) - pass - self._length = len(self._hits) - pass - return self._length - - def _search_all(self, select=False): - return self._get_field_values("fmu.iteration.uuid.keyword") - - async def _search_all_async(self, select=False): - return await self._get_field_values_async("fmu.iteration.uuid.keyword") - def _maybe_prefetch(self, index): return diff --git a/src/fmu/sumo/explorer/objects/realizations.py b/src/fmu/sumo/explorer/objects/realizations.py index b328cddb..eea2ad81 100644 --- a/src/fmu/sumo/explorer/objects/realizations.py +++ b/src/fmu/sumo/explorer/objects/realizations.py @@ -4,33 +4,11 @@ from fmu.sumo.explorer.objects._search_context import SearchContext class Realizations(SearchContext): - def __init__(self, sc): - super().__init__(sc._sumo, sc._must, sc._must_not) + def __init__(self, sc, uuids): + super().__init__(sc._sumo, must=[{"terms": {"fmu.realization.uuid.keyword": uuids}}]) + self._hits = uuids return - def __len__(self): - if self._length is None: - if self._hits is None: - self._hits = self._search_all(select=False) - pass - self._length = len(self._hits) - pass - return self._length - - async def length_async(self): - if self._length is None: - if self._hits is None: - self._hits = self._search_all(select=False) - pass - self._length = len(self._hits) - pass - return self._length - - def _search_all(self, select=False): - return self._get_field_values("fmu.realization.uuid.keyword") - - async def _search_all_async(self, select=False): - return await self._get_field_values_async("fmu.realization.uuid.keyword") def _maybe_prefetch(self, index): return diff --git a/tests/test_explorer.py b/tests/test_explorer.py index 9b9fda67..cd921df8 100644 --- a/tests/test_explorer.py +++ b/tests/test_explorer.py @@ -327,4 +327,23 @@ def test_seismic_case_by_uuid(explorer: Explorer, seismic_case_uuid: str): assert "Trace" in channel_list assert "SEGYTraceHeader" in channel_list +def test_grids_and_properties(explorer: Explorer): + cases_with_grids = explorer.grids.cases.filter(status="keep").cases + cases_with_gridprops = explorer.grid_properties.cases.filter(status="keep").cases + cgs=set([case.uuid for case in cases_with_grids]) + cgps=set([case.uuid for case in cases_with_gridprops]) + assert cgs==cgps + case=cases_with_grids[0] + grids=case.grids + gridprops=case.grid_properties + xtgrid=grids[0].to_cpgrid() + gridspec=grids[0].metadata["data"]["spec"] + assert xtgrid.nlay == gridspec["nlay"] + assert xtgrid.nrow == gridspec["nrow"] + assert xtgrid.ncol == gridspec["ncol"] + xtgridprop=gridprops[0].to_cpgrid_property() + gridpropspec = gridprops[0].metadata["data"]["spec"] + assert xtgridprop.nlay == gridpropspec["nlay"] + assert xtgridprop.nrow == gridpropspec["nrow"] + assert xtgridprop.ncol == gridpropspec["ncol"]