Ejemplo n.º 1
0
def set_face_set_gcs_list_from_dict(
        grid,
        face_set_dict=None,
        create_organizing_objects_where_needed=False):
    """Creates a grid connection set for each feature in the face set dictionary, based on kelp list pairs."""

    if face_set_dict is None:
        face_set_dict = grid.face_set_dict
    grid.face_set_gcs_list = []
    for feature, kelp_values in face_set_dict.items():
        gcs = rqf.GridConnectionSet(grid.model, grid=grid)
        if len(kelp_values) == 2:
            kelp_j, kelp_i = kelp_values
            axis = 'K'
        elif len(kelp_values) == 3:
            kelp_j, kelp_i, axis = kelp_values
        else:
            raise ValueError('grid face set dictionary item messed up')
        log.debug(f'creating gcs for: {feature} {axis}')
        gcs.set_pairs_from_kelp(kelp_j,
                                kelp_i,
                                feature,
                                create_organizing_objects_where_needed,
                                axis=axis)
        grid.face_set_gcs_list.append(gcs)
Ejemplo n.º 2
0
def test_two_grid_gcs(tmp_path):
    epc = make_epc_with_abutting_grids(tmp_path)

    # re-open the model and establish the abutting grid connection set
    model = rq.Model(epc)
    gcs_uuid = model.uuid(obj_type='GridConnectionSetRepresentation')
    assert gcs_uuid is not None
    gcs = rqf.GridConnectionSet(model, uuid=gcs_uuid)
    assert gcs is not None
    gcs.cache_arrays()

    # check that attributes have been preserved
    assert gcs.number_of_grids() == 2
    assert len(gcs.grid_list) == 2
    assert not bu.matching_uuids(gcs.grid_list[0].uuid, gcs.grid_list[1].uuid)
    assert gcs.count == 6
    assert gcs.grid_index_pairs.shape == (6, 2)
    assert np.all(gcs.grid_index_pairs[:, 0] == 0)
    assert np.all(gcs.grid_index_pairs[:, 1] == 1)
    assert gcs.face_index_pairs.shape == (6, 2)
    assert np.all(gcs.face_index_pairs[:, 0] == gcs.face_index_map[1, 1])  # J+
    assert np.all(gcs.face_index_pairs[:, 1] == gcs.face_index_map[1, 0])  # J-
    assert tuple(gcs.face_index_inverse_map[gcs.face_index_pairs[0,
                                                                 0]]) == (1, 1)
    assert tuple(gcs.face_index_inverse_map[gcs.face_index_pairs[0,
                                                                 1]]) == (1, 0)
    assert gcs.cell_index_pairs.shape == (6, 2)
    assert np.all(gcs.cell_index_pairs >= 0)
    assert np.all(gcs.cell_index_pairs < 60)
Ejemplo n.º 3
0
def test_two_fault_gcs(tmp_path):
    epc = make_epc_with_gcs(tmp_path)

    # re-open the model and check the gcs
    model = rq.Model(epc)
    gcs_uuid = model.uuid(obj_type='GridConnectionSetRepresentation')
    assert gcs_uuid is not None
    gcs = rqf.GridConnectionSet(model, uuid=gcs_uuid)
    assert gcs is not None
    assert gcs.number_of_features() == 2
    feature_names = gcs.list_of_feature_names()
    assert len(feature_names) == 2
    assert 'F1' in feature_names and 'F2' in feature_names
    fault_names = gcs.list_of_fault_names()
    assert fault_names == feature_names
    for fi in (0, 1):
        assert gcs.feature_name_for_feature_index(fi) in ('F1', 'F2')
    assert gcs.feature_name_for_feature_index(
        0) != gcs.feature_name_for_feature_index(1)
    fi, f_uuid = gcs.feature_index_and_uuid_for_fault_name('F1')
    assert fi is not None and fi in (0, 1)
    assert f_uuid is not None
    assert gcs.fault_name_for_feature_index(fi) == 'F1'
    assert gcs.feature_index_for_cell_face((1, 1, 1), 0, 1) is None
    fi_a = gcs.feature_index_for_cell_face((1, 1, 1), 2, 1)
    assert fi_a in (0, 1)
    fi_b = gcs.feature_index_for_cell_face((1, 1, 1), 1, 1)
    assert fi_b in (0, 1)
    assert fi_a != fi_b
    gcs.rework_face_pairs()
Ejemplo n.º 4
0
def _iter_grid_connection_sets(model):
    """Yields grid connection set objects, one for each gcs in this model."""

    import resqpy.fault as rqf  # imported here for speed, module is not always needed

    gcs_uuids = _uuids(model, obj_type='GridConnectionSetRepresentation')
    for gcs_uuid in gcs_uuids:
        yield rqf.GridConnectionSet(model, uuid=gcs_uuid)
Ejemplo n.º 5
0
def make_epc_with_gcs(tmp_path):
    epc = os.path.join(tmp_path, 'two_fault.epc')
    model = rq.new_model(epc)

    # create a grid
    g = grr.RegularGrid(model, extent_kji=(5, 4, 3), dxyz=(100.0, 100.0, 10.0))
    g.create_xml()

    # create an empty grid connection set
    gcs = rqf.GridConnectionSet(model, grid=g)

    # prepare two named faults as a dataframe
    data = {
        'name': ['F1', 'F2'],
        'face': ['I+', 'J-'],
        'i1': [1, 0],
        'i2': [1, 2],
        'j1': [0, 2],
        'j2': [3, 2],
        'k1': [0, 0],
        'k2': [4, 4],
        'mult': [0.1, 0.05]
    }
    df = pd.DataFrame(data)

    # set grid connection set from dataframe
    gcs.set_pairs_from_faces_df(df,
                                create_organizing_objects_where_needed=True,
                                create_mult_prop=True,
                                fault_tmult_dict=None,
                                one_based_indexing=False)

    # save the grid connection set
    gcs.write_hdf5()
    gcs.create_xml(title='two fault gcs')
    model.store_epc()

    # add some basic grid properties
    porosity_uuid = rqdm.add_one_grid_property_array(epc,
                                                     np.full(
                                                         g.extent_kji, 0.27),
                                                     property_kind='porosity',
                                                     title='porosity',
                                                     uom='m3/m3')
    assert porosity_uuid is not None
    perm_uuid = rqdm.add_one_grid_property_array(
        epc,
        np.full(g.extent_kji, 152.0),
        property_kind='rock permeability',
        uom='mD',
        facet_type='direction',
        facet='IJK',
        title='permeability')
    assert perm_uuid is not None

    return epc
Ejemplo n.º 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'
Ejemplo n.º 7
0
def make_epc_with_abutting_grids(tmp_path):
    epc = os.path.join(tmp_path, 'abutting_grids.epc')
    model = rq.new_model(epc)

    # create a grid
    g0 = grr.RegularGrid(model,
                         extent_kji=(5, 4, 3),
                         dxyz=(100.0, 100.0, 10.0))
    g0.create_xml()

    g1 = grr.RegularGrid(model,
                         extent_kji=(5, 4, 3),
                         dxyz=(100.0, 100.0, 10.0),
                         origin=(100.0, 400.0, 20.0))
    g1.create_xml()

    # create an empty grid connection set
    gcs = rqf.GridConnectionSet(model, title='abut')

    # populate the grid connection set at low level due to lack of multi-grid methods
    gcs.grid_list = [g0, g1]
    gcs.count = 6
    gcs.grid_index_pairs = np.zeros((6, 2), dtype=int)
    gcs.grid_index_pairs[:, 1] = 1
    gcs.face_index_pairs = np.empty((6, 2), dtype=int)
    gcs.face_index_pairs[:, 0] = gcs.face_index_map[1, 1]  #  J+
    gcs.face_index_pairs[:, 1] = gcs.face_index_map[1, 0]  #  J-
    gcs.cell_index_pairs = np.empty((6, 2), dtype=int)
    cell = 0
    for k in range(3):
        for i in range(2):
            gcs.cell_index_pairs[cell, 0] = g0.natural_cell_index(
                (k + 2, 3, i + 1))
            gcs.cell_index_pairs[cell, 1] = g1.natural_cell_index((k, 0, i))
            cell += 1
    #  leave optional feature list & indices as None

    # save the grid connection set
    gcs.write_hdf5()
    gcs.create_xml()
    model.store_epc()

    return epc
Ejemplo n.º 8
0
def _set_support_uuid_notnone_supportnone(collection, support_uuid, model):
    import resqpy.fault as rqf
    import resqpy.grid as grr
    import resqpy.surface as rqs
    import resqpy.unstructured as rug
    import resqpy.well as rqw

    support_part = model.part_for_uuid(support_uuid)
    assert support_part is not None, 'supporting representation part missing in model'
    collection.support_root = model.root_for_part(support_part)
    support_type = model.type_of_part(support_part)
    assert support_type is not None
    if support_type == 'obj_IjkGridRepresentation':
        collection.support = grr.any_grid(model,
                                          uuid=collection.support_uuid,
                                          find_properties=False)
    elif support_type == 'obj_WellboreFrameRepresentation':
        collection.support = rqw.WellboreFrame(model,
                                               uuid=collection.support_uuid)
    elif support_type == 'obj_BlockedWellboreRepresentation':
        collection.support = rqw.BlockedWell(model,
                                             uuid=collection.support_uuid)
    elif support_type == 'obj_Grid2dRepresentation':
        collection.support = rqs.Mesh(model, uuid=collection.support_uuid)
    elif support_type == 'obj_GridConnectionSetRepresentation':
        collection.support = rqf.GridConnectionSet(
            model, uuid=collection.support_uuid)
    elif support_type == 'obj_TriangulatedSetRepresentation':
        collection.support = rqs.Surface(model, uuid=collection.support_uuid)
    elif support_type == 'obj_UnstructuredGridRepresentation':
        collection.support = rug.UnstructuredGrid(model,
                                                  uuid=collection.support_uuid,
                                                  geometry_required=False,
                                                  find_properties=False)
    elif support_type == 'obj_WellboreMarkerFrameRepresentation':
        collection.support = rqw.WellboreMarkerFrame(
            model, uuid=collection.support_uuid)
    else:
        raise TypeError(
            'unsupported property supporting representation class: ' +
            str(support_type))
Ejemplo n.º 9
0
def fault_throw_scaling(epc_file,
                        source_grid=None,
                        scaling_factor=None,
                        connection_set=None,
                        scaling_dict=None,
                        ref_k0=0,
                        ref_k_faces='top',
                        cell_range=0,
                        offset_decay=0.5,
                        store_displacement=False,
                        inherit_properties=False,
                        inherit_realization=None,
                        inherit_all_realizations=False,
                        inherit_gcs=True,
                        new_grid_title=None,
                        new_epc_file=None):
    """Extends epc with a new grid with fault throws multiplied by scaling factors.

    arguments:
       epc_file (string): file name to rewrite the model's xml to; if source grid is None, model is loaded from this file
       source_grid (grid.Grid object, optional): if None, the epc_file is loaded and it should contain one ijk grid object
          (or one 'ROOT' grid) which is used as the source grid
       scaling_factor (float, optional): if present, the default scaling factor to apply to split pillars which do not
          appear in any of the faults in the scaling dictionary; if None, such pillars are left unchanged
       connection_set (fault.GridConnectionSet object): the connection set with associated fault feature list, used to
          identify which faces (and hence pillars) belong to which named fault
       scaling_dict (dictionary mapping string to float): the scaling factor to apply to each named fault; any faults not
          included in the dictionary will be left unadjusted (unless a default scaling factor is given as scaling_factor arg)
       ref_k0 (integer, default 0): the reference layer (zero based) to use when determining the pre-existing throws
       ref_k_faces (string, default 'top'): 'top' or 'base' identifying which bounding interface to use as the reference
       cell_range (integer, default 0): the number of cells away from faults which will have depths adjusted to spatially
          smooth the effect of the throw scaling (ie. reduce sudden changes in gradient due to the scaling)
       offset_decay (float, default 0.5): DEPRECATED; ignored
       store_displacement (boolean, default False): if True, 3 grid property parts are created, one each for x, y, & z
          displacement of cells' centres brought about by the fault throw scaling
       inherit_properties (boolean, default False): if True, the new grid will have a copy of any properties associated
          with the source grid
       inherit_realization (int, optional): realization number for which properties will be inherited; ignored if
          inherit_properties is False
       inherit_all_realizations (boolean, default False): if True (and inherit_realization is None), properties for all
          realizations will be inherited; if False, only properties with a realization of None are inherited; ignored if
          inherit_properties is False or inherit_realization is not None
       inherit_gcs (boolean, default True): if True, any grid connection set objects related to the source grid will be
          inherited by the modified grid
       new_grid_title (string): used as the citation title text for the new grid object
       new_epc_file (string, optional): if None, the source epc_file is extended with the new grid object; if present,
          a new epc file (& associated h5 file) is created to contain the derived grid (& crs)

    returns:
       new grid (grid.Grid object), with fault throws scaled according to values in the scaling dictionary

    notes:
       grid points are moved along pillar lines;
       stretch is towards or away from mid-point of throw;
       same shift is applied to all layers along pillar;
       pillar lines assumed to be straight;
       the offset decay argument might be changed in a future version to give improved smoothing;
       if a large fault is represented by a series of parallel minor faults 'stepping' down, each minor fault will have the
       scaling factor applied independently, leading to some unrealistic results
    """

    assert epc_file or new_epc_file, 'epc file name not specified'
    if new_epc_file and epc_file and (
        (new_epc_file == epc_file) or
        (os.path.exists(new_epc_file) and os.path.exists(epc_file)
         and os.path.samefile(new_epc_file, epc_file))):
        new_epc_file = None
    model, source_grid = _establish_model_and_source_grid(
        epc_file, source_grid)
    assert source_grid.grid_representation == 'IjkGrid'
    assert model is not None

    assert source_grid.has_split_coordinate_lines, 'cannot scale fault throws in unfaulted grid'
    assert scaling_factor is not None or (connection_set is not None
                                          and scaling_dict is not None)

    if ref_k_faces == 'base':
        ref_k0 += 1
    assert ref_k0 >= 0 and ref_k0 <= source_grid.nk, 'reference layer out of range'

    # take a copy of the grid
    log.debug('copying grid')
    grid = copy_grid(source_grid, model)
    grid.cache_all_geometry_arrays()  # probably already cached anyway

    # todo: handle pillars with no geometry defined, and cells without geometry defined
    assert grid.geometry_defined_for_all_pillars(
    ), 'not all pillars have defined geometry'

    primaries = (grid.nj + 1) * (grid.ni + 1)
    offsets = np.zeros(grid.points_cached.shape[1:])

    if scaling_factor is not None:  # apply global scaling to throws
        _set_offsets_based_on_scaling_factor(grid, scaling_factor, offsets,
                                             ref_k0, primaries)

    if connection_set is not None and scaling_dict is not None:  # overwrite any global offsets with named fault throw adjustments
        _set_offsets_based_on_scaling_dict(grid, connection_set, scaling_dict,
                                           offsets, ref_k0)

    # initialise flag array for adjustments
    adjusted = np.zeros((primaries, ), dtype=bool)

    # insert adjusted throws to all layers of split pillars
    grid.points_cached[:, grid.split_pillar_indices_cached, :] += offsets[
        grid.split_pillar_indices_cached, :].reshape(1, -1, 3)
    adjusted[grid.split_pillar_indices_cached] = True
    grid.points_cached[:, primaries:, :] += offsets[primaries:, :].reshape(
        1, -1, 3)

    # iteratively look for pillars neighbouring adjusted pillars, adjusting by a decayed amount
    adjusted = adjusted.reshape((grid.nj + 1, grid.ni + 1))
    while cell_range > 0:
        newly_adjusted = _neighbourly_adjustment(grid, offsets, adjusted,
                                                 cell_range)
        adjusted = np.logical_or(adjusted, newly_adjusted)
        cell_range -= 1

    # check cell edge relative directions (in x,y) to ensure geometry is still coherent
    log.debug('checking grid geometry coherence')
    grid.check_top_and_base_cell_edge_directions()

    # build cell displacement property array(s)
    if store_displacement:
        log.debug('generating cell displacement property arrays')
        displacement_collection = _displacement_properties(grid, source_grid)
    else:
        displacement_collection = None

    collection = _prepare_simple_inheritance(grid, source_grid,
                                             inherit_properties,
                                             inherit_realization,
                                             inherit_all_realizations)
    if collection is None:
        collection = displacement_collection
    elif displacement_collection is not None:
        collection.inherit_imported_list_from_other_collection(
            displacement_collection, copy_cached_arrays=False)

    if new_grid_title is None or len(new_grid_title) == 0:
        new_grid_title = 'grid with fault throws scaled by ' + str(scaling_factor) + ' from ' +  \
                         str(rqet.citation_title_for_node(source_grid.root))

    gcs_list = []
    if inherit_gcs:
        gcs_uuids = model.uuids(obj_type='GridConnectionSetRepresentation',
                                related_uuid=source_grid.uuid)
        for gcs_uuid in gcs_uuids:
            gcs = rqf.GridConnectionSet(model, uuid=gcs_uuid)
            gcs.cache_arrays()
            gcs_list.append((gcs, gcs.title))
        log.debug(f'{len(gcs_list)} grid connection sets to be inherited')

    # write model
    model.h5_release()
    if new_epc_file:
        _write_grid(new_epc_file,
                    grid,
                    property_collection=collection,
                    grid_title=new_grid_title,
                    mode='w')
        epc_file = new_epc_file
    else:
        ext_uuid, _ = model.h5_uuid_and_path_for_node(
            rqet.find_nested_tags(source_grid.root, ['Geometry', 'Points']),
            'Coordinates')
        _write_grid(epc_file,
                    grid,
                    ext_uuid=ext_uuid,
                    property_collection=collection,
                    grid_title=new_grid_title,
                    mode='a')

    if len(gcs_list):
        log.debug(
            f'inheriting grid connection sets related to source grid: {source_grid.uuid}'
        )
        _inherit_gcs_list(epc_file, gcs_list, source_grid, grid)

    return grid
Ejemplo n.º 10
0
def test_gcs_property_inheritance(tmp_path):
    epc = os.path.join(tmp_path, 'gcs_prop_inherit.epc')

    model = rq.Model(epc,
                     new_epc=True,
                     create_basics=True,
                     create_hdf5_ext=True)

    # create a grid
    g = grr.RegularGrid(model, extent_kji=(5, 3, 3), dxyz=(10.0, 10.0, 1.0))
    g.write_hdf5()
    g.create_xml(title='unsplit grid')

    # define an L shaped (in plan view) fault
    j_faces = np.zeros((g.nk, g.nj - 1, g.ni), dtype=bool)
    j_faces[:, 0, 1:] = True
    i_faces = np.zeros((g.nk, g.nj, g.ni - 1), dtype=bool)
    i_faces[:, 1:, 0] = True
    gcs = rqf.GridConnectionSet(
        model,
        grid=g,
        j_faces=j_faces,
        i_faces=i_faces,
        feature_name='L fault',
        create_organizing_objects_where_needed=True,
        create_transmissibility_multiplier_property=False)
    # check that connection set has the right number of cell face pairs
    assert gcs.count == g.nk * ((g.nj - 1) + (g.ni - 1))

    #  create a transmissibility multiplier property
    tm = np.arange(gcs.count).astype(float)
    if gcs.property_collection is None:
        gcs.property_collection = rqp.PropertyCollection()
        gcs.property_collection.set_support(support=gcs)
    pc = gcs.property_collection
    pc.add_cached_array_to_imported_list(
        tm,
        'unit test',
        'TMULT',
        uom='Euc',  # actually a ratio of transmissibilities
        property_kind='transmissibility multiplier',
        local_property_kind_uuid=None,
        realization=None,
        indexable_element='faces')
    # write gcs which should also write property collection and create a local property kind
    gcs.write_hdf5()
    gcs.create_xml(write_new_properties=True)

    # check that a local property kind has materialised
    pk_uuid = model.uuid(obj_type='PropertyKind',
                         title='transmissibility multiplier')
    assert pk_uuid is not None

    # check that we can create a surface object for the gcs
    surf = gcs.surface()
    assert surf is not None
    t, _ = surf.triangles_and_points()
    assert t.shape == (2 * gcs.count, 3)

    # create a derived grid connection set using a layer range
    thin_gcs, thin_indices = gcs.filtered_by_layer_range(min_k0=1,
                                                         max_k0=3,
                                                         return_indices=True)
    assert thin_gcs is not None and thin_indices is not None
    assert thin_gcs.count == 3 * ((g.nj - 1) + (g.ni - 1))
    # inherit the transmissibility multiplier property
    thin_gcs.inherit_properties_for_selected_indices(gcs, thin_indices)
    thin_gcs.write_hdf5()
    thin_gcs.create_xml()  # by default will include write of new properties

    # check that the inheritance has worked
    assert thin_gcs.property_collection is not None and thin_gcs.property_collection.number_of_parts(
    ) > 0
    thin_pc = thin_gcs.property_collection
    tm_part = thin_pc.singleton(property_kind='transmissibility multiplier')
    assert tm_part is not None
    thin_tm = thin_pc.cached_part_array_ref(tm_part)
    assert thin_tm is not None and thin_tm.ndim == 1
    assert thin_tm.size == thin_gcs.count
    assert_array_almost_equal(thin_tm, tm[thin_indices])

    # check that get_combined...() method can execute using property collection
    b_a, i_a, f_a = gcs.get_combined_fault_mask_index_value_arrays(
        min_k=1, max_k=3, property_name='Transmissibility multiplier', ref_k=2)
    assert b_a is not None and i_a is not None and f_a is not None
    # check that transmissibility multiplier values have been sampled correctly from property array
    assert f_a.shape == (g.nj, g.ni, 2, 2)
    assert np.count_nonzero(
        np.isnan(f_a)) == 4 * g.nj * g.ni - 2 * ((g.nj - 1) + (g.ni - 1))
    assert np.nanmax(f_a) > np.nanmin(f_a)
    restore = np.seterr(all='ignore')
    assert np.all(np.logical_or(np.isnan(f_a), f_a >= np.nanmin(thin_tm)))
    assert np.all(np.logical_or(np.isnan(f_a), f_a <= np.nanmax(thin_tm)))
    np.seterr(**restore)
Ejemplo n.º 11
0
def test_feature_inheritance(tmp_path):
    epc = make_epc_with_gcs(tmp_path)

    # introduce a split version of the grid
    model = rq.Model(epc)
    simple_gcs_uuid = model.uuid(obj_type='GridConnectionSetRepresentation')
    assert simple_gcs_uuid is not None
    crs_uuid = model.uuid(obj_type='LocalDepth3dCrs')
    assert crs_uuid is not None
    line_a = rql.Polyline(model,
                          set_bool=False,
                          set_crs=crs_uuid,
                          title='line a',
                          set_coord=np.array([[200.0, -50.0, 0.0],
                                              [200.0, 450.0, 0.0]]))
    line_b = rql.Polyline(model,
                          set_bool=False,
                          set_crs=crs_uuid,
                          title='line b',
                          set_coord=np.array([[-50.0, 200.0, 0.0],
                                              [350.0, 200.0, 0.0]]))
    fault_lines = rql.PolylineSet(model,
                                  polylines=[line_a, line_b],
                                  title='fault lines')
    assert fault_lines is not None
    fault_lines.write_hdf5()
    fault_lines.create_xml()
    model.store_epc()
    model.h5_release()

    i_grid = rqdm.add_faults(epc,
                             polylines=fault_lines,
                             source_grid=None,
                             inherit_properties=True,
                             new_grid_title='interim',
                             create_gcs=False)
    assert i_grid is not None

    # increase the throw on the faults
    rqdm.global_fault_throw_scaling(epc,
                                    source_grid=i_grid,
                                    scaling_factor=10.0,
                                    cell_range=1,
                                    inherit_properties=True,
                                    new_grid_title='faulted')

    # re-open the model and load the faulted grid
    model = rq.Model(epc)
    grid = model.grid(title='faulted')
    assert grid is not None

    # establish the original simple gcs (even though it relates to a different grid?)
    simple_gcs = rqf.GridConnectionSet(model, uuid=simple_gcs_uuid)

    # derive a grid connection set from fault juxtaposition, inheriting features from simple gcs
    juxta_gcs, tr = grid.fault_connection_set(compute_transmissibility=True,
                                              add_to_model=True,
                                              inherit_features_from=simple_gcs,
                                              title='juxtaposed')
    assert juxta_gcs is not None
    assert tr is not None
    assert tr.ndim == 1 and len(tr) == juxta_gcs.count

    # check that features have been inherited correctly
    assert juxta_gcs.number_of_features() == 2
    feature_names = juxta_gcs.list_of_feature_names()
    for fi in range(2):
        if 'F1' in feature_names[fi]:
            axis = 2
        else:
            assert 'F2' in feature_names[fi]
            axis = 1
        cfp_lists = juxta_gcs.list_of_cell_face_pairs_for_feature_index(fi)
        assert len(cfp_lists) == 2 and len(cfp_lists[0]) == len(
            cfp_lists[1]) and len(cfp_lists[1]) > 0
        for fip in cfp_lists[1]:
            assert fip.shape == (2, 2)
            assert np.all(fip[:, 0] == axis)
            assert np.all(fip[0, 1] == 1 - fip[1, 1])

    # check that transmissibility property has been created okay
    tr_uuid = model.uuid(obj_type='ContinuousProperty',
                         related_uuid=juxta_gcs.uuid)
    assert tr_uuid is not None
    trp = rqp.Property(model, uuid=tr_uuid)
    assert trp is not None
    assert trp.is_continuous()
    assert trp.property_kind() == 'transmissibility'
    assert_array_almost_equal(tr, trp.array_ref())
Ejemplo n.º 12
0
def test_pinchout_and_k_gap_gcs(tmp_path):
    epc = os.path.join(tmp_path, 'gcs_pinchout_k_gap.epc')
    model = rq.new_model(epc)

    # create a grid
    g = grr.RegularGrid(model,
                        extent_kji=(5, 5, 5),
                        dxyz=(100.0, 100.0, 10.0),
                        as_irregular_grid=True)
    # patch points to generate a pinchout
    p = g.points_cached
    assert p.shape == (6, 6, 6, 3)
    p[2, :3, :3] = p[1, :3, :3]
    # convert one layer to a K gap with pinchout
    p[4, 3:, 3:] = p[3, 3:, 3:]
    g.nk -= 1
    g.extent_kji = np.array((g.nk, g.nj, g.ni), dtype=int)
    g.k_gaps = 1
    g.k_gap_after_array = np.zeros(g.nk - 1, dtype=bool)
    g.k_gap_after_array[2] = True
    g._set_k_raw_index_array()
    g.write_hdf5()
    g.create_xml(title='pinchout k gap grid')
    model.store_epc()

    # reload the grid
    model = rq.Model(epc)
    grid = model.grid()
    assert grid is not None
    assert grid.k_gaps == 1
    assert tuple(grid.extent_kji) == (4, 5, 5)

    # create a pinchout connection set
    po_gcs = rqf.pinchout_connection_set(grid)
    assert po_gcs is not None
    po_gcs.write_hdf5()
    po_gcs.create_xml()
    po_uuid = po_gcs.uuid

    # create a K gap connection set
    kg_gcs = rqf.k_gap_connection_set(grid)
    assert kg_gcs is not None
    kg_gcs.write_hdf5()
    kg_gcs.create_xml()
    kg_uuid = kg_gcs.uuid

    model.store_epc()

    # re-open the model and load the connection sets
    model = rq.Model(epc)
    po_gcs = rqf.GridConnectionSet(model, uuid=po_uuid)
    assert po_gcs is not None
    po_gcs.cache_arrays()
    kg_gcs = rqf.GridConnectionSet(model, uuid=kg_uuid)
    assert kg_gcs is not None
    kg_gcs.cache_arrays()

    # check face pairs in the pinchout connection set
    assert po_gcs.count == 4
    assert po_gcs.cell_index_pairs.shape == (4, 2)
    assert po_gcs.face_index_pairs.shape == (4, 2)
    assert np.all(
        po_gcs.cell_index_pairs[:, 0] != po_gcs.cell_index_pairs[:, 1])
    assert np.all(
        po_gcs.face_index_pairs[:, 0] != po_gcs.cell_index_pairs[:, 1])
    assert np.all(
        np.logical_or(po_gcs.face_index_pairs == 0,
                      po_gcs.face_index_pairs == 1))
    for cell in po_gcs.cell_index_pairs.flatten():
        assert cell in [0, 1, 5, 6, 50, 51, 55, 56]
    assert np.all(
        np.abs(po_gcs.cell_index_pairs[:, 1] -
               po_gcs.cell_index_pairs[:, 0]) == 50)

    # check face pairs in K gap connection set
    assert kg_gcs.count == 4
    assert kg_gcs.cell_index_pairs.shape == (4, 2)
    assert kg_gcs.face_index_pairs.shape == (4, 2)
    assert np.all(
        kg_gcs.cell_index_pairs[:, 0] != kg_gcs.cell_index_pairs[:, 1])
    assert np.all(
        kg_gcs.face_index_pairs[:, 0] != kg_gcs.cell_index_pairs[:, 1])
    assert np.all(
        np.logical_or(kg_gcs.face_index_pairs == 0,
                      kg_gcs.face_index_pairs == 1))
    for cell in kg_gcs.cell_index_pairs.flatten():
        assert cell in [74, 73, 69, 68, 99, 98, 94, 93]
    assert np.all(
        np.abs(kg_gcs.cell_index_pairs[:, 1] -
               kg_gcs.cell_index_pairs[:, 0]) == 25)

    # test compact indices method
    ci = po_gcs.compact_indices()
    assert ci.shape == (4, 2)
    for cf in ci.flatten():
        assert cf in [1, 7, 31, 37, 300, 306, 330, 336]
    assert np.all(np.abs(ci[:, 1] - ci[:, 0]) == 299)

    # test write simulator method
    files = ('fault_ff.dat', 'fault_tf.dat', 'fault_ft.dat')
    both_sides = (False, True, False)
    minus = (False, False, True)
    for filename, inc_both_sides, use_minus in zip(files, both_sides, minus):
        dat_path = os.path.join(tmp_path, filename)
        po_gcs.write_simulator(dat_path,
                               include_both_sides=inc_both_sides,
                               use_minus=use_minus)
        assert os.path.exists(dat_path)
Ejemplo n.º 13
0
def grid_columns_property_from_gcs_property(model,
                                            gcs_property_uuid,
                                            null_value=np.nan,
                                            title=None,
                                            multiple_handling='default'):
    """Derives a new grid columns property (map) from a single-grid gcs property using values for k faces.

    arguments:
        model (Model): the model in which the existing objects are to be found and the new property added
        gcs_property_uuid (UUID): the uuid of the existing grid connection set property
        null_value (float or int, default NaN): the value to use in columns where no K faces are present in the gcs
        title (str, optional): the title for the new grid property; defaults to that of the gcs property
        multiple_handling (str, default 'mean'): one of 'default', 'mean', 'min', 'max', 'min_k', 'max_k', 'exception';
            determines how a value is generated when more than one K face is present in the gcs for a column

    returns:
        uuid of the newly created Property (RESQML ContinuousProperty, DiscreteProperty, CategoricalProperty or
        PointsProperty)

    notes:
        the grid connection set which is the support for gcs_property must involve only one grid;
        the resulting columns grid property is of the same class as the original gcs property;
        the write_hdf() and create_xml() methods are called by this function, for the new property,
        which is added to the model;
        the default multiple handling mode is mean for continuous data, any for discrete (inc categorical);
        in the case of discrete (including categorical) data, a null_value of NaN will be changed to -1
    """
    assert multiple_handling in [
        'default', 'mean', 'min', 'max', 'any', 'exception'
    ]
    assert gcs_property_uuid is not None and bu.is_uuid(gcs_property_uuid)
    gcs_property = rqp.Property(model, uuid=gcs_property_uuid)
    assert gcs_property is not None
    support_uuid = gcs_property.collection.support_uuid
    assert support_uuid is not None
    assert model.type_of_uuid(
        support_uuid, strip_obj=True) == 'GridConnectionSetRepresentation'
    gcs = rqf.GridConnectionSet(model, uuid=support_uuid)
    gcs_prop_array = gcs_property.array_ref()
    if gcs_property.is_continuous():
        dtype = float
        if multiple_handling == 'default':
            multiple_handling = 'mean'
    else:
        dtype = int
        if null_value == np.nan:
            null_value = -1
        elif type(null_value) is float:
            null_value = int(null_value)
        if multiple_handling == 'default':
            multiple_handling = 'any'
        assert multiple_handling != 'mean', 'mean specified as multiple handling for non-continuous property'
    assert gcs.number_of_grids(
    ) == 1, 'only single grid gcs supported for grid columns property derivation'
    grid = gcs.grid_list[0]
    if gcs_property.is_points():
        map_shape = (grid.nj, grid.ni, 3)
    else:
        map_shape = (grid.nj, grid.ni)
    assert gcs_property.count() == 1
    map = np.full(map_shape, null_value, dtype=dtype)
    count_per_col = np.zeros((grid.nj, grid.ni), dtype=int)
    cells_and_faces = gcs.list_of_cell_face_pairs_for_feature_index(None)
    assert cells_and_faces is not None and len(cells_and_faces) == 2
    cell_pairs, face_pairs = cells_and_faces
    if cell_pairs is None or len(cell_pairs) == 0:
        log.warning(
            'no faces found for grid connection set property {gcs_property.title}'
        )
        return None
    assert len(cell_pairs) == len(face_pairs)
    assert len(cell_pairs) == len(gcs_prop_array)
    for index in range(len(cell_pairs)):
        if face_pairs[index, 0, 0] != 0 or face_pairs[index, 1, 0] != 0:
            continue  # not a K face
        col_j, col_i = cell_pairs[index, 0,
                                  1:]  # assume paired cell is in same column!
        if count_per_col[col_j, col_i] == 0 or multiple_handling == 'any':
            map[col_j, col_i] = gcs_prop_array[index]
        elif multiple_handling == 'mean':
            map[col_j,
                col_i] = (((map[col_j, col_i] * count_per_col[col_j, col_i]) +
                           gcs_prop_array[index]) /
                          float(count_per_col[col_j, col_i] + 1))
        elif multiple_handling == 'min':
            if gcs_prop_array[index] < map[col_j, col_i]:
                map[col_j, col_i] = gcs_prop_array[index]
        elif multiple_handling == 'max':
            if gcs_prop_array[index] > map[col_j, col_i]:
                map[col_j, col_i] = gcs_prop_array[index]
        else:
            raise ValueError(
                'multiple grid connection set K faces found for column')
        count_per_col[col_j, col_i] += 1
    # create an empty PropertyCollection and add the map data as a new property
    time_index = gcs_property.time_index()
    pc = rqp.PropertyCollection()
    pc.set_support(support=grid)
    pc.add_cached_array_to_imported_list(
        map,
        source_info=f'{gcs.title} property {gcs_property.title} map',
        keyword=title if title else gcs_property.title,
        discrete=not gcs_property.is_continuous(),
        uom=gcs_property.uom(),
        time_index=time_index,
        null_value=None if gcs_property.is_continuous() else null_value,
        property_kind=gcs_property.property_kind(),
        local_property_kind_uuid=gcs_property.local_property_kind_uuid(),
        facet_type=gcs_property.facet_type(),
        facet=gcs_property.facet(),
        realization=None,  # do we want to preserve the realisation number?
        indexable_element='columns',
        points=gcs_property.is_points())
    time_series_uuid = pc.write_hdf5_for_imported_list()
    string_lookup_uuid = gcs_property.string_lookup_uuid()
    time_series_uuid = None if time_index is None else gcs_property.time_series_uuid(
    )
    new_uuids = pc.create_xml_for_imported_list_and_add_parts_to_model(
        time_series_uuid=time_series_uuid,
        string_lookup_uuid=string_lookup_uuid)
    assert len(new_uuids) == 1
    return new_uuids[0]