Beispiel #1
0
    def __init__(self,
                 model,
                 uuid=None,
                 title=None,
                 originator=None,
                 extra_metadata=None):
        """Load an existing resqml object, or create new.

        Args:
            model (resqpy.model.Model): Parent model
            uuid (str, optional): Load from existing uuid (if given), else create new.
            title (str, optional): Citation title
            originator (str, optional): Creator of object. By default, uses user id.
        """
        self.model = model
        self.title = title  #: Citation title
        self.originator = originator  #: Creator of object. By default, user id.
        self.extra_metadata = {}
        if extra_metadata:
            self.extra_metadata = extra_metadata
            self._standardise_extra_metadata(
            )  # has side effect of making a copy

        if uuid is None:
            self.uuid = bu.new_uuid()  #: Unique identifier
        else:
            self.uuid = uuid
            root_node = self.root
            citation_node = rqet.find_tag(root_node, 'Citation')
            if citation_node is not None:
                self.title = rqet.find_tag_text(citation_node, 'Title')
                self.originator = rqet.find_tag_text(citation_node,
                                                     'Originator')
            self.extra_metadata = rqet.load_metadata_from_xml(root_node)
            self._load_from_xml()
Beispiel #2
0
def grid_flavour(grid_root):
    """Returns a string indicating type of grid geometry, currently 'IjkGrid' or 'IjkBlockGrid'."""

    if grid_root is None:
        return None
    em = rqet.load_metadata_from_xml(grid_root)
    flavour = em.get('grid_flavour')
    if flavour is None:
        node_type = rqet.node_type(grid_root, strip_obj = True)
        if node_type == 'IjkGridRepresentation':
            if rqet.find_tag(grid_root, 'Geometry') is not None:
                flavour = 'IjkGrid'
            else:
                flavour = 'IjkBlockGrid'  # this might cause issues
        elif node_type == 'UnstructuredGridRepresentation':
            cell_shape = rqet.find_nested_tags_text(grid_root, ['Geometry', 'CellShape'])
            if cell_shape is None or cell_shape == 'polyhedral':
                flavour = 'UnstructuredGrid'
            elif cell_shape == 'tetrahedral':
                flavour = 'TetraGrid'
            elif cell_shape == 'hexahedral':
                flavour = 'HexaGrid'
            elif cell_shape == 'pyramidal':
                flavour = 'PyramidGrid'
            elif cell_shape == 'prism':
                flavour = 'PrismGrid'
    return flavour
Beispiel #3
0
    def _load_from_xml(self):

        assert self.root is not None  # polyline xml node is specified
        poly_root = self.root

        self.title = rqet.citation_title_for_node(poly_root)

        self.extra_metadata = rqet.load_metadata_from_xml(self.root)

        self.isclosed = rqet.bool_from_text(
            rqet.node_text(rqet.find_tag(poly_root, 'IsClosed')))
        assert self.isclosed is not None  # Required field

        patch_node = rqet.find_tag(poly_root, 'NodePatch')
        assert patch_node is not None  # Required field

        geometry_node = rqet.find_tag(patch_node, 'Geometry')
        assert geometry_node is not None  # Required field

        self.crs_uuid = bu.uuid_from_string(
            rqet.find_nested_tags_text(geometry_node, ['LocalCrs', 'UUID']))
        assert self.crs_uuid is not None  # Required field

        points_node = rqet.find_tag(geometry_node, 'Points')
        assert points_node is not None  # Required field
        load_hdf5_array(self, points_node, 'coordinates', tag='Coordinates')

        self.nodepatch = (rqet.find_tag_int(patch_node, 'PatchIndex'),
                          rqet.find_tag_int(patch_node, 'Count'))
        assert not any(
            map(lambda x: x is None,
                self.nodepatch))  # Required fields - assert neither are None

        self.rep_int_root = self.model.referenced_node(
            rqet.find_tag(poly_root, 'RepresentedInterpretation'))
Beispiel #4
0
def equivalent_extra_metadata(a, b):
    """Returns True if the two objects have identical extra metadata"""
    a_has = hasattr(a, 'extra_metadata')
    b_has = hasattr(b, 'extra_metadata')
    if a_has:
        a_em = a.extra_metadata
        a_has = len(a_em) > 0
    else:
        a_em = rqet.load_metadata_from_xml(a.root)
        a_has = a_em is not None and len(a_em) > 0
    if b_has:
        b_em = b.extra_metadata
        b_has = len(b_em) > 0
    else:
        b_em = rqet.load_metadata_from_xml(b.root)
        b_has = b_em is not None and len(b_em) > 0
    if a_has != b_has:
        return False
    if not a_has:
        return True
    return a_em == b_em
Beispiel #5
0
def _filtered_by_extra(model, parts_list, extra):
    filtered_list = []
    for part in parts_list:
        part_extra = rqet.load_metadata_from_xml(_root_for_part(model, part))
        if not part_extra:
            continue
        match = True
        for key, value in extra.items():
            if key not in part_extra or part_extra[key] != value:
                match = False
                break
        if match:
            filtered_list.append(part)
    return filtered_list
Beispiel #6
0
def test_add_connection_set_and_tmults(example_model_with_properties,
                                       test_data_path, inc_list, tmult_dict,
                                       expected_mult):
    model = example_model_with_properties

    inc_list = [os.path.join(test_data_path, inc) for inc in inc_list]

    gcs_uuid = rqf.add_connection_set_and_tmults(model, inc_list, tmult_dict)

    assert gcs_uuid is not None, 'Grid connection set not generated'

    reload_model = rq.Model(epc_file=model.epc_file)

    faults = reload_model.parts_list_of_type('obj_FaultInterpretation')
    assert len(faults) == len(expected_mult.keys()), \
        f'Expected a {len(expected_mult.keys())} faults, found {len(faults)}'
    for fault in faults:
        metadata = rqet.load_metadata_from_xml(
            reload_model.root_for_part(fault))
        title = reload_model.citation_title_for_part(fault)
        expected_str = str(float(expected_mult[title]))
        assert metadata["Transmissibility multiplier"] == expected_str, \
            f'Expected mult for fault {title} to be {expected_str}, found {metadata["Transmissibility multiplier"]}'

    # check that a transmissibility multiplier property has been created
    gcs = rqf.GridConnectionSet(reload_model,
                                uuid=gcs_uuid,
                                find_properties=True)
    assert gcs is not None
    pc = gcs.property_collection
    assert pc is not None and pc.number_of_parts() > 0
    part = pc.singleton(property_kind='transmissibility multiplier')
    assert part is not None
    # check property values are in expected set
    a = pc.cached_part_array_ref(part)
    assert a is not None and a.ndim == 1
    expect = [x for x in expected_mult.values()]
    assert all([v in expect for v in a])
    #  see if a local property kind has been set up correctly
    pku = pc.local_property_kind_uuid(part)
    assert pku is not None
    pk = rqp.PropertyKind(reload_model, uuid=pku)
    assert pk is not None
    assert pk.title == 'transmissibility multiplier'
Beispiel #7
0
    def _load_from_xml(self, marker_node):
        """Load attributes from xml.

        This is invoked as part of the init method when an existing uuid is given.

        Returns:
           [bool]: True if successful
        """

        assert marker_node is not None

        # Load XML data
        uuid_str = marker_node.attrib.get('uuid')
        if uuid_str:
            self.uuid = bu.uuid_from_string(uuid_str)
        citation_tag = rqet.find_nested_tags(root=marker_node,
                                             tag_list=['Citation'])
        assert citation_tag is not None
        self.title = rqet.find_tag_text(root=citation_tag, tag_name='Title')
        self.originator = rqet.find_tag_text(root=citation_tag,
                                             tag_name='Originator')

        self.marker_type = None
        for boundary_feature_type in [
                'GeologicBoundaryKind', 'FluidMarker', 'FluidContact'
        ]:
            found_tag_text = rqet.find_tag_text(root=marker_node,
                                                tag_name=boundary_feature_type)
            if found_tag_text is not None:
                self.marker_type = found_tag_text
                break
        assert self.marker_type is not None

        self.interpretation_uuid = bu.uuid_from_string(
            rqet.find_nested_tags_text(root=marker_node,
                                       tag_list=['Interpretation', 'UUID']))
        self.extra_metadata = rqet.load_metadata_from_xml(node=marker_node)

        return True
Beispiel #8
0
    def __init__(self,
                 model,
                 uuid = None,
                 df = None,
                 uom_list = None,
                 realization = None,
                 phase_combo = None,
                 low_sal = False,
                 table_index = None,
                 title = 'relperm_table',
                 column_lookup_uuid = None,
                 uom_lookup_uuid = None):
        """Create a new RelPerm object from either a previously stored object or a pandas dataframe.

        arguments:
           phase_combo (str, optional): the combination of phases whose relative permeability behaviour is described.
              Options include 'water-oil', 'gas-oil' and 'gas-water'
           low_sal (boolean, optional): if True, indicates that the water-oil table contains the low-salinity data for
              relative permeability and capillary pressure
           table_index (int, optional): the index of the relative permeability
           table when multiple relative permeability tables are present. Note, indices should start at 1.

        note:
           see DataFrame class docstring for details of other arguments
        """

        # check that either a uuid OR dataframe has been provided
        if df is None and uuid is None:
            raise ValueError('either a uuid or a dataframe must be provided')

        # check extra_metadata for arguments if uuid is provided
        if uuid is not None:
            model_root = model.root(uuid = uuid)
            uuid_metadata_dict = rqet.load_metadata_from_xml(model_root)
            self.phase_combo = uuid_metadata_dict['phase_combo']
            self.low_sal = uuid_metadata_dict['low_sal']
            self.table_index = uuid_metadata_dict['table_index']

        else:
            # check that 'phase_combo' parameter is valid
            self.phase_combo = phase_combo
            self.low_sal = low_sal
            self.table_index = table_index

            df.columns = [x.capitalize() for x in df.columns]
            if 'Pc' in df.columns and df.columns[-1] != 'Pc':
                raise ValueError('Pc', 'capillary pressure data should be in the last column of the dataframe')

            processed_phase_combo_checks = {
                ('oil', 'water'): self.__water_oil_error_check,
                ('gas', 'oil'): self.__gas_oil_error_check,
                ('gas', 'water'): self.__gas_water_error_check,
                ('None',): self.__no_phase_combo_error_check
            }

            processed_phase_combo = tuple(sorted(set([x.strip() for x in str(self.phase_combo).split('-')])))
            if processed_phase_combo not in processed_phase_combo_checks.keys():
                raise ValueError('invalid phase_combo provided')
            # check that table_index is >= 1
            if table_index is not None and table_index < 1:
                raise ValueError('table_index cannot be less than 1')
            # check that the column names and order are as expected
            processed_phase_combo_checks.get(processed_phase_combo)(df)
            # ensure that missing capillary pressure values are stored as np.nan
            if 'Pc' in df.columns:
                df['Pc'].replace('None', np.nan, inplace = True)
            # convert all values in the dataframe to numeric type
            df[df.columns] = df[df.columns].apply(pd.to_numeric, errors = 'coerce')
            # ensure that no other column besides Pc has missing values
            cols_no_pc = [x for x in df.columns if 'Pc' != x]
            for col in cols_no_pc:
                if (df[col].isnull().sum() > 0) or ('None' in list(df[col])):
                    raise Exception(f'missing values found in {col} column')

            # check that Sw, Kr and Pc values are monotonic and that the Sw and Kr values are within the range 0-1
            sat_col = [x for x in df.columns if x[0] == 'S'][0]
            relp_corr_col = [x for x in df.columns if x == 'Kr' + sat_col[-1]][0]
            relp_opp_col = [x for x in df.columns if (x[0:2] == 'Kr') & (x[-1] != sat_col[-1])][0]
            if not (df[sat_col].is_monotonic and df[relp_corr_col].is_monotonic and df[
                relp_opp_col].is_monotonic_decreasing) and not \
                    (df[sat_col].is_monotonic_decreasing and df[relp_corr_col].is_monotonic_decreasing and
                     df[relp_opp_col].is_monotonic):
                raise ValueError(f'{sat_col, relp_corr_col, relp_opp_col} combo is not monotonic')
            if 'Pc' in df.columns:
                if not df['Pc'].dropna().is_monotonic and not df['Pc'].dropna().is_monotonic_decreasing:
                    raise ValueError('Pc values are not monotonic')
            for col in ['Sw', 'Sg', 'So', 'Krw', 'Krg', 'Kro']:
                if col in df.columns:
                    if df[col].min() < 0 or df[col].max() > 1:
                        raise ValueError(f'{col} is not within the range 0-1')

        super().__init__(model,
                         uuid = uuid,
                         support_root = None,
                         df = df,
                         uom_list = uom_list,
                         realization = realization,
                         title = title,
                         column_lookup_uuid = column_lookup_uuid,
                         uom_lookup_uuid = uom_lookup_uuid,
                         extra_metadata = {
                             'relperm_table': 'true',
                             'phase_combo': self.phase_combo,
                             'low_sal': str(self.low_sal).lower(),
                             'table_index': str(self.table_index)
                         })