Example #1
0
def _establish_models_and_source_grid(epc_file, new_epc_file, source_grid,
                                      source_grid_uuid):
    assert epc_file or source_grid is not None, 'neither epc file name nor source grid supplied'
    if not epc_file:
        epc_file = source_grid.model.epc_file
        assert epc_file, 'unable to ascertain epc filename from grid object'
    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 = None
    if new_epc_file:
        log.debug('creating fresh model for refined grid')
        model = rq.Model(epc_file=new_epc_file,
                         new_epc=True,
                         create_basics=True,
                         create_hdf5_ext=True)
    if epc_file:
        model_in = rq.Model(epc_file)
        if source_grid is None:
            if source_grid_uuid is None:
                log.debug('using default source grid from existing epc')
                source_grid = model_in.grid()
            else:
                log.debug(
                    'selecting source grid from existing epc based on uuid')
                source_grid = grr.Grid(model_in, uuid=source_grid_uuid)
        else:
            if source_grid_uuid is not None:
                assert bu.matching_uuids(source_grid_uuid, source_grid.uuid)
            grid_uuid = source_grid.uuid
            log.debug('reloading source grid from existing epc file')
            source_grid = grr.Grid(model_in, uuid=grid_uuid)
        if model is None:
            model = model_in
    else:
        model_in = source_grid.model
    assert model_in is not None
    assert model is not None
    assert source_grid is not None
    assert source_grid.grid_representation in ['IjkGrid', 'IjkBlockGrid']
    return epc_file, model, model_in, source_grid
Example #2
0
def example_fine_coarse_model(example_model_and_crs):
    model, crs = example_model_and_crs

    coarse_grid = grr.RegularGrid(parent_model=model,
                                  origin=(0, 0, 0),
                                  extent_kji=(3, 5, 5),
                                  crs_uuid=crs.uuid,
                                  dxyz=(10, 10, 10))
    coarse_grid.cache_all_geometry_arrays()
    coarse_grid.write_hdf5_from_caches(
        file=model.h5_file_name(file_must_exist=False), mode='w')
    coarse_grid.create_xml(ext_uuid=model.h5_uuid(),
                           title='Coarse',
                           write_geometry=True,
                           add_cell_length_properties=True)

    fine_grid = grr.RegularGrid(parent_model=model,
                                origin=(0, 0, 0),
                                extent_kji=(6, 10, 10),
                                crs_uuid=crs.uuid,
                                dxyz=(5, 5, 5))
    fine_grid.cache_all_geometry_arrays()
    fine_grid.write_hdf5_from_caches(
        file=model.h5_file_name(file_must_exist=True), mode='a')
    fine_grid.create_xml(ext_uuid=model.h5_uuid(),
                         title='Fine',
                         write_geometry=True,
                         add_cell_length_properties=True)

    model.store_epc()
    model = Model(model.epc_file)

    coarse = grr.Grid(parent_model=model, uuid=coarse_grid.uuid)
    fine = grr.Grid(parent_model=model, uuid=fine_grid.uuid)

    fc = rqfc.FineCoarse(fine_extent_kji=(6, 10, 10),
                         coarse_extent_kji=(3, 5, 5))
    fc.set_all_ratios_constant()
    fc.set_all_proprtions_equal()

    return model, coarse, fine, fc
Example #3
0
def _create_gcs_if_requested(create_gcs, composite_face_set_dict, new_epc_file,
                             grid):
    if create_gcs and len(composite_face_set_dict) > 0:
        if new_epc_file is not None:
            grid_uuid = grid.uuid
            model = rq.Model(new_epc_file)
            grid = grr.Grid(model,
                            root=model.root(uuid=grid_uuid),
                            find_properties=False)
        grid.set_face_set_gcs_list_from_dict(
            composite_face_set_dict,
            create_organizing_objects_where_needed=True)
        combined_gcs = grid.face_set_gcs_list[0]
        for gcs in grid.face_set_gcs_list[1:]:
            combined_gcs.append(gcs)
        combined_gcs.write_hdf5()
        combined_gcs.create_xml(title='faults added from lines')
        grid.clear_face_sets()
        grid.model.store_epc()
Example #4
0
def _empty_grid(model, source_grid, is_regular, k0_min, k0_max, zone_count):
    if is_regular:
        dxyz_dkji = source_grid.block_dxyz_dkji.copy()
        dxyz_dkji[0] *= k0_max - k0_min + 1
        grid = grr.RegularGrid(model,
                               extent_kji = (1, source_grid.nj, source_grid.ni),
                               dxyz_dkji = dxyz_dkji,
                               origin = source_grid.block_origin,
                               crs_uuid = source_grid.crs_uuid,
                               set_points_cached = False)
    else:
        grid = grr.Grid(model)
        # inherit attributes from source grid
        grid.grid_representation = 'IjkGrid'
        grid.extent_kji = np.array((zone_count, source_grid.nj, source_grid.ni), dtype = 'int')
        grid.nk, grid.nj, grid.ni = zone_count, source_grid.nj, source_grid.ni
        grid.has_split_coordinate_lines = source_grid.has_split_coordinate_lines
        grid.crs_uuid = source_grid.crs_uuid

    grid.k_direction_is_down = source_grid.k_direction_is_down
    grid.grid_is_right_handed = source_grid.grid_is_right_handed
    grid.pillar_shape = source_grid.pillar_shape
    return grid
Example #5
0
def _refined_unfaulted_grid(model, source_grid, fine_coarse):

    source_points = source_grid.points_ref()
    assert source_points is not None, 'geometry not available for refinement of unfaulted grid'

    # create a new, empty grid object
    grid = grr.Grid(model)

    # inherit attributes from source grid
    grid.grid_representation = 'IjkGrid'
    grid.extent_kji = fine_coarse.fine_extent_kji
    grid.nk, grid.nj, grid.ni = grid.extent_kji[0], grid.extent_kji[
        1], grid.extent_kji[2]
    grid.k_direction_is_down = source_grid.k_direction_is_down
    grid.grid_is_right_handed = source_grid.grid_is_right_handed
    grid.pillar_shape = source_grid.pillar_shape
    grid.has_split_coordinate_lines = source_grid.has_split_coordinate_lines
    grid.split_pillars_count = source_grid.split_pillars_count
    grid.k_gaps = source_grid.k_gaps
    if grid.k_gaps:
        grid.k_gap_after_array = np.zeros((grid.nk - 1, ), dtype=bool)
        grid.k_raw_index_array = np.zeros((grid.nk, ), dtype=int)
        # k gap arrays populated below
    # inherit the coordinate reference system used by the grid geometry
    grid.crs_uuid = source_grid.crs_uuid
    if source_grid.model is not model:
        model.duplicate_node(source_grid.model.root_for_uuid(
            source_grid, grid.crs_uuid),
                             add_as_part=True)
    grid.crs = rqc.Crs(model, uuid=grid.crs_uuid)

    refined_points = np.empty(
        (grid.nk_plus_k_gaps + 1, grid.nj + 1, grid.ni + 1, 3))

    # log.debug(f'source grid: {source_grid.extent_kji}; k gaps: {source_grid.k_gaps}')
    # log.debug(f'refined grid: {grid.extent_kji}; k gaps: {grid.k_gaps}')
    fk0 = 0
    gaps_so_far = 0
    for ck0 in range(fine_coarse.coarse_extent_kji[0] + 1):
        end_k = (ck0 == fine_coarse.coarse_extent_kji[0])
        if end_k:
            k_ratio = 1
            k_interpolation = [0.0]
        else:
            k_ratio = fine_coarse.ratio(0, ck0)
            k_interpolation = fine_coarse.interpolation(0, ck0)
        one_if_gap = 1 if source_grid.k_gaps and ck0 < fine_coarse.coarse_extent_kji[
            0] - 1 and source_grid.k_gap_after_array[ck0] else 0
        for flk0 in range(k_ratio + one_if_gap):
            # log.debug(f'ck0: {ck0}; fk0: {fk0}; flk0: {flk0}; k_ratio: {k_ratio}; one_if_gap: {one_if_gap}; gaps so far: {gaps_so_far}')
            if flk0 < k_ratio:
                k_fraction = k_interpolation[flk0]
            else:
                k_fraction = 1.0
            if grid.k_gaps:
                if end_k:
                    k_plane = source_points[source_grid.k_raw_index_array[ck0 -
                                                                          1] +
                                            1, :, :, :]
                else:
                    k_plane = (
                        k_fraction *
                        source_points[source_grid.k_raw_index_array[ck0] +
                                      1, :, :, :] +
                        (1.0 - k_fraction) * source_points[
                            source_grid.k_raw_index_array[ck0], :, :, :])
                if flk0 == k_ratio:
                    grid.k_gap_after_array[fk0 - 1] = True
                elif fk0 < grid.nk:
                    grid.k_raw_index_array[fk0] = fk0 + gaps_so_far
            else:
                if end_k:
                    k_plane = source_points[ck0, :, :, :]
                else:
                    k_plane = k_fraction * source_points[ck0 + 1, :, :, :] + (
                        1.0 - k_fraction) * source_points[ck0, :, :, :]
            fj0 = 0
            for cj0 in range(fine_coarse.coarse_extent_kji[1] + 1):
                end_j = (cj0 == fine_coarse.coarse_extent_kji[1])
                if end_j:
                    j_ratio = 1
                    j_interpolation = [0.0]
                else:
                    j_ratio = fine_coarse.ratio(1, cj0)
                    j_interpolation = fine_coarse.interpolation(1, cj0)
                for flj0 in range(j_ratio):
                    j_fraction = j_interpolation[flj0]
                    # note: shape of j_line will be different if there are split pillars in play
                    if end_j:
                        j_line = k_plane[cj0, :, :]
                    else:
                        j_line = j_fraction * k_plane[cj0 + 1, :, :] + (
                            1.0 - j_fraction) * k_plane[cj0, :, :]

                    fi0 = 0
                    for ci0 in range(fine_coarse.coarse_extent_kji[2] + 1):
                        end_i = (ci0 == fine_coarse.coarse_extent_kji[2])
                        if end_i:
                            i_ratio = 1
                            i_interpolation = [0.0]
                        else:
                            i_ratio = fine_coarse.ratio(2, ci0)
                            i_interpolation = fine_coarse.interpolation(2, ci0)
                        for fli0 in range(i_ratio):
                            i_fraction = i_interpolation[fli0]
                            if end_i:
                                p = j_line[ci0, :]
                            else:
                                p = i_fraction * j_line[ci0 + 1, :] + (
                                    1.0 - i_fraction) * j_line[ci0, :]

                            refined_points[fk0 + gaps_so_far, fj0, fi0] = p

                            fi0 += 1

                    assert fi0 == fine_coarse.fine_extent_kji[2] + 1

                    fj0 += 1

            assert fj0 == fine_coarse.fine_extent_kji[1] + 1

            if flk0 == k_ratio:
                gaps_so_far += 1
            else:
                fk0 += 1

    assert fk0 == fine_coarse.fine_extent_kji[0] + 1
    assert grid.nk + gaps_so_far == grid.nk_plus_k_gaps

    grid.points_cached = refined_points

    grid.geometry_defined_for_all_pillars_cached = True
    grid.geometry_defined_for_all_cells_cached = True
    grid.array_cell_geometry_is_defined = np.full(tuple(grid.extent_kji),
                                                  True,
                                                  dtype=bool)

    return grid
Example #6
0
def copy_grid(source_grid, target_model = None, copy_crs = True):
    """Creates a copy of the IJK grid object in the target model (usually prior to modifying points in situ).

    note:
       this function is not usually called directly by application code; it does not write to the hdf5
       file nor create xml for the copied grid;
       the copy will be a resqpy Grid even if the source grid is a RegularGrid
    """

    assert source_grid.grid_representation in ['IjkGrid', 'IjkBlockGrid']

    model = source_grid.model
    if target_model is None:
        target_model = model
    if target_model is model:
        copy_crs = False

    # if the source grid is a RegularGrid, ensure that it has explicit points
    if source_grid.grid_representation == 'IjkBlockGrid':
        source_grid.make_regular_points_cached()

    # create empty grid object (with new uuid)
    grid = grr.Grid(target_model)

    # inherit attributes from source grid
    grid.grid_representation = 'IjkGrid'
    grid.extent_kji = np.array(source_grid.extent_kji, dtype = 'int')
    grid.nk, grid.nj, grid.ni = source_grid.nk, source_grid.nj, source_grid.ni
    grid.k_direction_is_down = source_grid.k_direction_is_down
    grid.grid_is_right_handed = source_grid.grid_is_right_handed
    grid.pillar_shape = source_grid.pillar_shape
    grid.has_split_coordinate_lines = source_grid.has_split_coordinate_lines
    grid.k_gaps = source_grid.k_gaps
    if grid.k_gaps:
        grid.k_gap_after_array = source_grid.k_gap_after_array.copy()
        grid.k_raw_index_array = source_grid.k_raw_index_array.copy()

    # inherit a copy of the coordinate reference system used by the grid geometry
    grid.crs_uuid = source_grid.crs_uuid
    if target_model is source_grid.model:
        grid.crs = rqc.Crs(model, uuid = grid.crs_uuid)
    elif copy_crs and source_grid.crs_uuid is not None:
        model.duplicate_node(source_grid.model.root_for_uuid(source_grid.crs_uuid), add_as_part = True)
    else:
        grid.crs = None

    # inherit a copy of the inactive cell mask
    if source_grid.inactive is None:
        grid.inactive = None
    else:
        grid.inactive = source_grid.inactive.copy()
    grid.active_property_uuid = source_grid.active_property_uuid

    # take a copy of the grid geometry
    source_grid.cache_all_geometry_arrays()
    grid.geometry_defined_for_all_pillars_cached = source_grid.geometry_defined_for_all_pillars_cached
    if hasattr(source_grid, 'array_pillar_geometry_is_defined'):
        grid.array_pillar_geometry_is_defined = source_grid.array_pillar_geometry_is_defined.copy()
    if hasattr(source_grid, 'array_cell_geometry_is_defined'):
        grid.array_cell_geometry_is_defined = source_grid.array_cell_geometry_is_defined.copy()
    grid.geometry_defined_for_all_cells_cached = source_grid.geometry_defined_for_all_cells_cached
    grid.points_cached = source_grid.points_cached.copy()
    if grid.has_split_coordinate_lines:
        source_grid.create_column_pillar_mapping()
        grid.split_pillar_indices_cached = source_grid.split_pillar_indices_cached.copy()
        grid.cols_for_split_pillars = source_grid.cols_for_split_pillars.copy()
        grid.cols_for_split_pillars_cl = source_grid.cols_for_split_pillars_cl.copy()
        grid.split_pillars_count = source_grid.split_pillars_count
        grid.pillars_for_column = source_grid.pillars_for_column.copy()

    return grid
Example #7
0
    def from_unsplit_grid(cls,
                          parent_model,
                          grid_uuid,
                          inherit_properties = True,
                          title = None,
                          extra_metadata = {},
                          write_active = None):
        """Creates a new (unstructured) HexaGrid from an existing resqpy unsplit (IJK) Grid without K gaps.

        arguments:
           parent_model (model.Model object): the model which this grid is part of
           grid_uuid (uuid.UUID): the uuid of an IjkGridRepresentation from which the hexa grid will be created
           inherit_properties (boolean, default True): if True, properties will be created for the new grid
           title (str, optional): citation title for the new grid
           extra_metadata (dict, optional): dictionary of extra metadata items to add to the grid
           write_active (boolean, optional): if True (or None and inactive property is established) then an
              active cell property is created (in addition to any inherited properties)

        returns:
           a newly created HexaGrid object

        note:
           this method includes the writing of hdf5 data, creation of xml for the new grid and adding it as a part
        """

        import resqpy.grid as grr

        # establish existing IJK grid
        ijk_grid = grr.Grid(parent_model, uuid = grid_uuid, find_properties = inherit_properties)
        assert ijk_grid is not None
        assert not ijk_grid.has_split_coordinate_lines, 'IJK grid has split coordinate lines (faults)'
        assert not ijk_grid.k_gaps, 'IJK grid has K gaps'
        ijk_grid.cache_all_geometry_arrays()
        ijk_points = ijk_grid.points_ref(masked = False)
        if title is None:
            title = ijk_grid.title

        # make empty unstructured hexa grid
        hexa_grid = cls(parent_model, title = title, extra_metadata = extra_metadata)

        # derive hexa grid attributes from ijk grid
        hexa_grid.crs_uuid = ijk_grid.crs_uuid
        hexa_grid.set_cell_count(ijk_grid.cell_count())
        if ijk_grid.inactive is not None:
            hexa_grid.inactive = ijk_grid.inactive.reshape((hexa_grid.cell_count,))
            hexa_grid.all_inactive = np.all(hexa_grid.inactive)
            if hexa_grid.all_inactive:
                log.warning(f'all cells marked as inactive for unstructured hexa grid {hexa_grid.title}')
        else:
            hexa_grid.all_inactive = False

        # inherit points (nodes) in IJK grid order, ie. K cycling fastest, then I, then J
        hexa_grid.points_cached = ijk_points.reshape((-1, 3))

        # setup faces per cell
        # ordering of faces (in nodes per face): all K faces, then all J faces, then all I faces
        # within J faces, ordering is all of J- faces for J = 0 first, then increasing planes in J
        # similarly for I faces
        nk_plus_1 = ijk_grid.nk + 1
        nj_plus_1 = ijk_grid.nj + 1
        ni_plus_1 = ijk_grid.ni + 1
        k_face_count = nk_plus_1 * ijk_grid.nj * ijk_grid.ni
        j_face_count = ijk_grid.nk * nj_plus_1 * ijk_grid.ni
        i_face_count = ijk_grid.nk * ijk_grid.nj * ni_plus_1
        kj_face_count = k_face_count + j_face_count
        hexa_grid.face_count = k_face_count + j_face_count + i_face_count
        hexa_grid.faces_per_cell_cl = 6 * (1 + np.arange(hexa_grid.cell_count, dtype = int))  # 6 faces per cell
        hexa_grid.faces_per_cell = np.empty(6 * hexa_grid.cell_count, dtype = int)
        arange = np.arange(hexa_grid.cell_count, dtype = int)
        hexa_grid.faces_per_cell[0::6] = arange  # K- faces
        hexa_grid.faces_per_cell[1::6] = ijk_grid.nj * ijk_grid.ni + arange  # K+ faces
        nki = ijk_grid.nk * ijk_grid.ni
        nkj = ijk_grid.nk * ijk_grid.nj
        # todo: vectorise following for loop
        for cell in range(hexa_grid.cell_count):
            k, j, i = ijk_grid.denaturalized_cell_index(cell)
            j_minus_face = k_face_count + nki * j + ijk_grid.ni * k + i
            hexa_grid.faces_per_cell[6 * cell + 2] = j_minus_face  # J- face
            hexa_grid.faces_per_cell[6 * cell + 3] = j_minus_face + nki  # J+ face
            i_minus_face = kj_face_count + nkj * i + ijk_grid.nj * k + j
            hexa_grid.faces_per_cell[6 * cell + 4] = i_minus_face  # I- face
            hexa_grid.faces_per_cell[6 * cell + 5] = i_minus_face + nkj  # I+ face

        # setup nodes per face, clockwise when viewed from negative side of face if ijk handedness matches xyz handedness
        # ordering of nodes in points array is as for the IJK grid
        hexa_grid.node_count = hexa_grid.points_cached.shape[0]
        assert hexa_grid.node_count == (ijk_grid.nk + 1) * (ijk_grid.nj + 1) * (ijk_grid.ni + 1)
        hexa_grid.nodes_per_face_cl = 4 * (1 + np.arange(hexa_grid.face_count, dtype = int))  # 4 nodes per face
        hexa_grid.nodes_per_face = np.empty(4 * hexa_grid.face_count, dtype = int)
        # todo: vectorise for loops
        # K faces
        face_base = 0
        for k in range(nk_plus_1):
            for j in range(ijk_grid.nj):
                for i in range(ijk_grid.ni):
                    hexa_grid.nodes_per_face[face_base] = (k * nj_plus_1 + j) * ni_plus_1 + i  # ip 0, jp 0
                    hexa_grid.nodes_per_face[face_base + 1] = (k * nj_plus_1 + j + 1) * ni_plus_1 + i  # ip 0, jp 1
                    hexa_grid.nodes_per_face[face_base + 2] = (k * nj_plus_1 + j + 1) * ni_plus_1 + i + 1  # ip 1, jp 1
                    hexa_grid.nodes_per_face[face_base + 3] = (k * nj_plus_1 + j) * ni_plus_1 + i + 1  # ip 1, jp 0
                    face_base += 4
        # J faces
        assert face_base == 4 * k_face_count
        for j in range(nj_plus_1):
            for k in range(ijk_grid.nk):
                for i in range(ijk_grid.ni):
                    hexa_grid.nodes_per_face[face_base] = (k * nj_plus_1 + j) * ni_plus_1 + i  # ip 0, kp 0
                    hexa_grid.nodes_per_face[face_base + 1] = (k * nj_plus_1 + j) * ni_plus_1 + i + 1  # ip 1, kp 0
                    hexa_grid.nodes_per_face[face_base +
                                             2] = ((k + 1) * nj_plus_1 + j) * ni_plus_1 + i + 1  # ip 1, kp 1
                    hexa_grid.nodes_per_face[face_base + 3] = ((k + 1) * nj_plus_1 + j) * ni_plus_1 + i  # ip 0, kp 1
                    face_base += 4
        # I faces
        assert face_base == 4 * kj_face_count
        for i in range(ni_plus_1):
            for k in range(ijk_grid.nk):
                for j in range(ijk_grid.nj):
                    hexa_grid.nodes_per_face[face_base] = (k * nj_plus_1 + j) * ni_plus_1 + i  # jp 0, kp 0
                    hexa_grid.nodes_per_face[face_base + 1] = ((k + 1) * nj_plus_1 + j) * ni_plus_1 + i  # jp 0, kp 1
                    hexa_grid.nodes_per_face[face_base +
                                             2] = ((k + 1) * nj_plus_1 + j + 1) * ni_plus_1 + i  # jp 1, kp 1
                    hexa_grid.nodes_per_face[face_base + 3] = (k * nj_plus_1 + j + 1) * ni_plus_1 + i  # jp 1, kp 0
                    face_base += 4
        assert face_base == 4 * hexa_grid.face_count

        # set cell face is right handed
        # todo: check Energistics documents for meaning of cell face is right handed
        # here the assumption is clockwise ordering of nodes viewed from within cell means 'right handed'
        hexa_grid.cell_face_is_right_handed = np.zeros(6 * hexa_grid.cell_count,
                                                       dtype = bool)  # initially set to left handed
        # if IJK grid's ijk handedness matches the xyz handedness, then set +ve faces to right handed; else -ve faces
        if ijk_grid.off_handed():
            hexa_grid.cell_face_is_right_handed[0::2] = True  # negative faces are right handed
        else:
            hexa_grid.cell_face_is_right_handed[1::2] = True  # positive faces are right handed

        hexa_grid.write_hdf5(write_active = write_active)
        hexa_grid.create_xml(write_active = write_active)

        if inherit_properties:
            ijk_pc = ijk_grid.extract_property_collection()
            hexa_pc = rqp.PropertyCollection(support = hexa_grid)
            for part in ijk_pc.parts():
                count = ijk_pc.count_for_part(part)
                hexa_part_shape = (hexa_grid.cell_count,) if count == 1 else (hexa_grid.cell_count, count)
                hexa_pc.add_cached_array_to_imported_list(
                    ijk_pc.cached_part_array_ref(part).reshape(hexa_part_shape),
                    'inherited from grid ' + str(ijk_grid.title),
                    ijk_pc.citation_title_for_part(part),
                    discrete = not ijk_pc.continuous_for_part(part),
                    uom = ijk_pc.uom_for_part(part),
                    time_index = ijk_pc.time_index_for_part(part),
                    null_value = ijk_pc.null_value_for_part(part),
                    property_kind = ijk_pc.property_kind_for_part(part),
                    local_property_kind_uuid = ijk_pc.local_property_kind_uuid(part),
                    facet_type = ijk_pc.facet_type_for_part(part),
                    facet = ijk_pc.facet_for_part(part),
                    realization = ijk_pc.realization_for_part(part),
                    indexable_element = ijk_pc.indexable_for_part(part),
                    count = count,
                    const_value = ijk_pc.constant_value_for_part(part))
                # todo: patch min & max values if present in ijk part
                hexa_pc.write_hdf5_for_imported_list()
                hexa_pc.create_xml_for_imported_list_and_add_parts_to_model(
                    support_uuid = hexa_grid.uuid,
                    time_series_uuid = ijk_pc.time_series_uuid_for_part(part),
                    string_lookup_uuid = ijk_pc.string_lookup_uuid_for_part(part),
                    extra_metadata = ijk_pc.extra_metadata_for_part(part))

        return hexa_grid
Example #8
0
def s_bend_k_gap_grid(s_bend_model):
    nk = 5
    nj = 12
    ni_tail = 5
    ni_bend = 18
    ni_half_mid = 2
    ni = 2 * (ni_tail + ni_bend + ni_half_mid)

    total_thickness = 12.0
    layer_thickness = total_thickness / float(nk)
    flat_dx_di = 10.0
    horst_dx_dk = 0.25 * flat_dx_di / float(nk)
    horst_dz = 1.73 * layer_thickness
    horst_half_dx = horst_dx_dk * horst_dz / layer_thickness
    dy_dj = 8.0
    top_depth = 100.0

    bend_theta_di = maths.pi / float(ni_bend)
    outer_radius = 2.0 * total_thickness

    bend_a_centre_xz = (flat_dx_di * float(ni_tail), top_depth + outer_radius)
    bend_b_centre_xz = (flat_dx_di * float(ni_tail - 2.0 * ni_half_mid),
                        top_depth + 3.0 * outer_radius - total_thickness)

    points = np.empty((nk + 1, nj + 1, ni + 1, 3))

    for k in range(nk + 1):
        if k == nk // 2 + 1:
            points[k] = points[k - 1]  # pinched out layer
        else:
            for i in range(ni + 1):
                if i < ni_tail + 1:
                    x = flat_dx_di * float(i)
                    z = top_depth + float(
                        k
                    ) * layer_thickness  # will introduce a thick layer after pinchout
                elif i < ni_tail + ni_bend:
                    theta = (i - ni_tail) * bend_theta_di
                    radius = outer_radius - float(k) * layer_thickness
                    x = bend_a_centre_xz[0] + radius * maths.sin(theta)
                    z = bend_a_centre_xz[1] - radius * maths.cos(theta)
                elif i < ni_tail + ni_bend + 2 * ni_half_mid + 1:
                    x = flat_dx_di * float(ni_tail - (i - (ni_tail + ni_bend)))
                    z = top_depth + 2.0 * outer_radius - float(
                        k) * layer_thickness
                elif i < ni_tail + 2 * ni_bend + 2 * ni_half_mid:
                    theta = (
                        i -
                        (ni_tail + ni_bend + 2 * ni_half_mid)) * bend_theta_di
                    radius = outer_radius - float(nk - k) * layer_thickness
                    x = bend_b_centre_xz[0] - radius * maths.sin(theta)
                    z = bend_b_centre_xz[1] - radius * maths.cos(theta)
                else:
                    x = flat_dx_di * float((i - (ni - ni_tail)) + ni_tail -
                                           2 * ni_half_mid)
                    if i == ni - 1 or i == ni - 4:
                        x += horst_dx_dk * float(k)
                    elif i == ni - 2 or i == ni - 3:
                        x -= horst_dx_dk * float(k)
                    z = top_depth + 4.0 * outer_radius + float(
                        k) * layer_thickness - 2.0 * total_thickness
                points[k, :, i] = (x, 0.0, z)

    for j in range(nj + 1):
        points[:, j, :, 1] = dy_dj * float(j)

    grid = grr.Grid(s_bend_model)

    crs = rqc.Crs(s_bend_model)
    crs_node = crs.create_xml()

    grid.grid_representation = 'IjkGrid'
    grid.extent_kji = np.array((nk, nj, ni), dtype='int')
    grid.nk, grid.nj, grid.ni = nk, nj, ni
    grid.k_direction_is_down = True  # dominant layer direction, or paleo-direction
    grid.pillar_shape = 'straight'
    grid.has_split_coordinate_lines = False
    grid.k_gaps = None
    grid.crs_uuid = crs.uuid
    grid.crs_root = crs_node

    grid.points_cached = points

    grid.geometry_defined_for_all_pillars_cached = True
    grid.geometry_defined_for_all_cells_cached = True
    grid.grid_is_right_handed = crs.is_right_handed_xyz()
    cp = grid.corner_points(cache_cp_array=True).copy()

    bend_theta_di = maths.pi / float(ni_bend)

    # IK plane faults
    cp[:, 3:, :, :, :, :, :] += (flat_dx_di * 0.7, 0.0, layer_thickness * 1.3)
    cp[:, 5:, :, :, :, :, :] += (flat_dx_di * 0.4, 0.0, layer_thickness * 0.9)
    cp[:, 8:, :, :, :, :, :] += (flat_dx_di * 0.3, 0.0, layer_thickness * 0.6)

    # JK plane faults
    cp[:, :, ni_tail + ni_bend // 2:, :, :, :,
       0] -= flat_dx_di * 0.57  # horizontal break mid top bend
    cp[:, :, ni_tail + ni_bend + ni_half_mid:, :, :, :,
       2] += layer_thickness * 1.27  # vertical break in mid section

    # zig-zag fault
    j_step = nj // (ni_tail - 2)
    for i in range(ni_tail - 1):
        j_start = i * j_step
        if j_start >= nj:
            break
        cp[:, j_start:, i, :, :, :, 2] += 1.1 * total_thickness

    # JK horst blocks
    cp[:, :, ni - 4, :, :, :, :] -= (horst_half_dx, 0.0, horst_dz)
    cp[:, :, ni - 3:, :, :, :, 0] -= 2.0 * horst_half_dx
    cp[:, :, ni - 2, :, :, :, :] += (-horst_half_dx, 0.0, horst_dz)
    cp[:, :, ni - 1:, :, :, :, 0] -= 2.0 * horst_half_dx

    # JK horst block mid lower bend
    bend_horst_dz = horst_dz * maths.tan(bend_theta_di)
    cp[:, :, ni - (ni_tail + ni_bend // 2 + 1):ni -
       (ni_tail + ni_bend // 2 - 1), :, :, :, :] -= (horst_dz, 0.0,
                                                     bend_horst_dz)
    cp[:, :, ni - (ni_tail + ni_bend // 2 - 1):, :, :, :,
       2] -= 2.0 * bend_horst_dz

    k_gap_grid = rqi.grid_from_cp(
        s_bend_model,
        cp,
        crs.uuid,
        max_z_void=0.01,
        split_pillars=True,
        split_tolerance=0.01,
        ijk_handedness='right' if grid.grid_is_right_handed else 'left',
        known_to_be_straight=True)

    # convert second layer to a K gap
    k_gap_grid.nk -= 1
    k_gap_grid.extent_kji[0] = k_gap_grid.nk
    k_gap_grid.k_gaps = 1
    k_gap_grid.k_gap_after_array = np.zeros(k_gap_grid.nk - 1, dtype=bool)
    k_gap_grid.k_gap_after_array[0] = True
    k_gap_grid.k_raw_index_array = np.zeros(k_gap_grid.nk, dtype=int)
    for k in range(1, k_gap_grid.nk):
        k_gap_grid.k_raw_index_array[k] = k + 1

    # clear some attributes which may no longer be valid
    k_gap_grid.pinchout = None
    k_gap_grid.inactive = None
    k_gap_grid.grid_skin = None
    if hasattr(k_gap_grid, 'array_thickness'):
        delattr(k_gap_grid, 'array_thickness')

    # k_gap_grid.write_hdf5_from_caches()
    # k_gap_grid.create_xml()
    return k_gap_grid
Example #9
0
def s_bend_grid(s_bend_model):
    nk = 5
    nj = 12
    ni_tail = 5
    ni_bend = 18
    ni_half_mid = 2
    ni = 2 * (ni_tail + ni_bend + ni_half_mid)

    total_thickness = 12.0
    layer_thickness = total_thickness / float(nk)
    flat_dx_di = 10.0
    horst_dx_dk = 0.25 * flat_dx_di / float(nk)
    dy_dj = 8.0
    top_depth = 100.0

    bend_theta_di = maths.pi / float(ni_bend)
    outer_radius = 2.0 * total_thickness

    bend_a_centre_xz = (flat_dx_di * float(ni_tail), top_depth + outer_radius)
    bend_b_centre_xz = (flat_dx_di * float(ni_tail - 2.0 * ni_half_mid),
                        top_depth + 3.0 * outer_radius - total_thickness)

    points = np.empty((nk + 1, nj + 1, ni + 1, 3))

    for k in range(nk + 1):
        if k == nk // 2 + 1:
            points[k] = points[k - 1]  # pinched out layer
        else:
            for i in range(ni + 1):
                if i < ni_tail + 1:
                    x = flat_dx_di * float(i)
                    z = top_depth + float(
                        k
                    ) * layer_thickness  # will introduce a thick layer after pinchout
                elif i < ni_tail + ni_bend:
                    theta = (i - ni_tail) * bend_theta_di
                    radius = outer_radius - float(k) * layer_thickness
                    x = bend_a_centre_xz[0] + radius * maths.sin(theta)
                    z = bend_a_centre_xz[1] - radius * maths.cos(theta)
                elif i < ni_tail + ni_bend + 2 * ni_half_mid + 1:
                    x = flat_dx_di * float(ni_tail - (i - (ni_tail + ni_bend)))
                    z = top_depth + 2.0 * outer_radius - float(
                        k) * layer_thickness
                elif i < ni_tail + 2 * ni_bend + 2 * ni_half_mid:
                    theta = (
                        i -
                        (ni_tail + ni_bend + 2 * ni_half_mid)) * bend_theta_di
                    radius = outer_radius - float(nk - k) * layer_thickness
                    x = bend_b_centre_xz[0] - radius * maths.sin(theta)
                    z = bend_b_centre_xz[1] - radius * maths.cos(theta)
                else:
                    x = flat_dx_di * float((i - (ni - ni_tail)) + ni_tail -
                                           2 * ni_half_mid)
                    if i == ni - 1 or i == ni - 4:
                        x += horst_dx_dk * float(k)
                    elif i == ni - 2 or i == ni - 3:
                        x -= horst_dx_dk * float(k)
                    z = top_depth + 4.0 * outer_radius + float(
                        k) * layer_thickness - 2.0 * total_thickness
                points[k, :, i] = (x, 0.0, z)

    for j in range(nj + 1):
        points[:, j, :, 1] = dy_dj * float(j)

    grid = grr.Grid(s_bend_model)

    crs = rqc.Crs(s_bend_model)
    crs_node = crs.create_xml()

    grid.grid_representation = 'IjkGrid'
    grid.extent_kji = np.array((nk, nj, ni), dtype='int')
    grid.nk, grid.nj, grid.ni = nk, nj, ni
    grid.k_direction_is_down = True  # dominant layer direction, or paleo-direction
    grid.pillar_shape = 'straight'
    grid.has_split_coordinate_lines = False
    grid.k_gaps = None
    grid.crs_uuid = crs.uuid
    grid.crs_root = crs_node

    grid.points_cached = points

    grid.geometry_defined_for_all_pillars_cached = True
    grid.geometry_defined_for_all_cells_cached = True
    grid.grid_is_right_handed = crs.is_right_handed_xyz()

    # grid.write_hdf5_from_caches()
    # grid.create_xml()
    return grid
Example #10
0
def test_s_bend_fn(tmp_path, epc=None):
    if epc is None:
        # use pytest temporary directory fixture
        # https://docs.pytest.org/en/stable/tmpdir.html
        epc = str(os.path.join(tmp_path, f"{bu.new_uuid()}.epc"))

    # create s-bend grid

    nk = 5
    nj = 12
    ni_tail = 5
    ni_bend = 18
    ni_half_mid = 2
    ni = 2 * (ni_tail + ni_bend + ni_half_mid)

    total_thickness = 12.0
    layer_thickness = total_thickness / float(nk)
    flat_dx_di = 10.0
    horst_dx_dk = 0.25 * flat_dx_di / float(nk)
    horst_dz = 1.73 * layer_thickness
    horst_half_dx = horst_dx_dk * horst_dz / layer_thickness
    dy_dj = 8.0
    top_depth = 100.0

    assert ni_bend % 2 == 0, 'ni_bend must be even for horizontal faulting'
    assert ni_tail >= 5, 'ni_tail must be at least 5 for horst blocks'

    bend_theta_di = maths.pi / float(ni_bend)
    outer_radius = 2.0 * total_thickness

    bend_a_centre_xz = (flat_dx_di * float(ni_tail), top_depth + outer_radius)
    bend_b_centre_xz = (flat_dx_di * float(ni_tail - 2.0 * ni_half_mid),
                        top_depth + 3.0 * outer_radius - total_thickness)

    points = np.empty((nk + 1, nj + 1, ni + 1, 3))

    for k in range(nk + 1):
        if k == nk // 2 + 1:
            points[k] = points[k - 1]  # pinched out layer
        else:
            for i in range(ni + 1):
                if i < ni_tail + 1:
                    x = flat_dx_di * float(i)
                    z = top_depth + float(
                        k
                    ) * layer_thickness  # will introduce a thick layer after pinchout
                elif i < ni_tail + ni_bend:
                    theta = (i - ni_tail) * bend_theta_di
                    radius = outer_radius - float(k) * layer_thickness
                    x = bend_a_centre_xz[0] + radius * maths.sin(theta)
                    z = bend_a_centre_xz[1] - radius * maths.cos(theta)
                elif i < ni_tail + ni_bend + 2 * ni_half_mid + 1:
                    x = flat_dx_di * float(ni_tail - (i - (ni_tail + ni_bend)))
                    z = top_depth + 2.0 * outer_radius - float(
                        k) * layer_thickness
                elif i < ni_tail + 2 * ni_bend + 2 * ni_half_mid:
                    theta = (
                        i -
                        (ni_tail + ni_bend + 2 * ni_half_mid)) * bend_theta_di
                    radius = outer_radius - float(nk - k) * layer_thickness
                    x = bend_b_centre_xz[0] - radius * maths.sin(theta)
                    z = bend_b_centre_xz[1] - radius * maths.cos(theta)
                else:
                    x = flat_dx_di * float((i - (ni - ni_tail)) + ni_tail -
                                           2 * ni_half_mid)
                    if i == ni - 1 or i == ni - 4:
                        x += horst_dx_dk * float(k)
                    elif i == ni - 2 or i == ni - 3:
                        x -= horst_dx_dk * float(k)
                    z = top_depth + 4.0 * outer_radius + float(
                        k) * layer_thickness - 2.0 * total_thickness
                points[k, :, i] = (x, 0.0, z)

    for j in range(nj + 1):
        points[:, j, :, 1] = dy_dj * float(j)

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

    grid = grr.Grid(model)

    crs = rqc.Crs(model)
    crs_node = crs.create_xml()
    if model.crs_uuid is None:
        model.crs_uuid = crs.crs_uuid

    grid.grid_representation = 'IjkGrid'
    grid.extent_kji = np.array((nk, nj, ni), dtype='int')
    grid.nk, grid.nj, grid.ni = nk, nj, ni
    grid.k_direction_is_down = True  # dominant layer direction, or paleo-direction
    grid.pillar_shape = 'straight'
    grid.has_split_coordinate_lines = False
    grid.k_gaps = None
    grid.crs_uuid = crs.uuid
    grid.crs = crs

    grid.points_cached = points

    grid.geometry_defined_for_all_pillars_cached = True
    grid.geometry_defined_for_all_cells_cached = True
    grid.grid_is_right_handed = crs.is_right_handed_xyz()

    grid.write_hdf5_from_caches()
    grid.create_xml()

    # create a well trajectory and md datum

    def df_trajectory(x, y, z):
        N = len(x)
        assert len(y) == N and len(z) == N
        df = pd.DataFrame(columns=['MD', 'X', 'Y', 'Z'])
        md = np.zeros(N)
        for n in range(N - 1):
            md[n + 1] = md[n] + vec.naive_length(
                (x[n + 1] - x[n], y[n + 1] - y[n], z[n + 1] - z[n]))
        df.MD = md
        df.X = x
        df.Y = y
        df.Z = z
        return df

    x = np.array([
        0.0, flat_dx_di * float(ni_tail) + outer_radius,
        flat_dx_di * (float(ni_tail) - 0.5), 0.0, -outer_radius
    ])
    y = np.array([
        0.0, dy_dj * 0.5, dy_dj * float(nj) / 2.0, dy_dj * (float(nj) - 0.5),
        dy_dj * float(nj)
    ])
    z = np.array([
        0.0, top_depth - total_thickness,
        top_depth + 2.0 * outer_radius - total_thickness / 2.0,
        top_depth + 3.0 * outer_radius - total_thickness,
        top_depth + 4.0 * outer_radius
    ])

    df = df_trajectory(x, y, z)

    datum = rqw.MdDatum(model, crs_uuid=crs.uuid, location=(x[0], y[0], z[0]))
    datum.create_xml()

    trajectory = rqw.Trajectory(model,
                                md_datum=datum,
                                data_frame=df,
                                length_uom='m',
                                well_name='ANGLED_WELL')

    assert bu.matching_uuids(trajectory.crs_uuid, crs.uuid)

    trajectory.write_hdf5()
    trajectory.create_xml()

    # add more wells

    x = np.array([
        0.0, flat_dx_di * float(ni_tail),
        flat_dx_di * 2.0 * float(ni_tail - ni_half_mid) + outer_radius,
        -outer_radius
    ])
    y = np.array([0.0, dy_dj * float(nj) * 0.59, dy_dj * 0.67, dy_dj * 0.5])
    z = np.array([
        0.0, top_depth - total_thickness,
        top_depth + 4.0 * outer_radius - 1.7 * total_thickness,
        top_depth + 4.0 * outer_radius - 1.7 * total_thickness
    ])

    df = df_trajectory(x, y, z)

    traj_2 = rqw.Trajectory(model,
                            md_datum=datum,
                            data_frame=df,
                            length_uom='m',
                            well_name='HORST_WELL')
    traj_2.write_hdf5()
    traj_2.create_xml()
    traj_2.control_points

    x = np.array([0.0, 0.0, 0.0])
    y = np.array([0.0, dy_dj * float(nj) * 0.53, dy_dj * float(nj) * 0.53])
    z = np.array(
        [0.0, top_depth - total_thickness, top_depth + 4.0 * outer_radius])

    df = df_trajectory(x, y, z)

    traj_3 = rqw.Trajectory(model,
                            md_datum=datum,
                            data_frame=df,
                            length_uom='m',
                            well_name='VERTICAL_WELL')
    traj_3.write_hdf5()
    traj_3.create_xml()
    traj_3.control_points

    n_x = flat_dx_di * float(ni_tail) * 0.48
    n_y = dy_dj * float(nj) / 9.1
    o_y = -dy_dj * 0.45
    nd_x = n_y / 3.0
    x = np.array([
        0.0, n_x, n_x, n_x + nd_x, n_x + 2.0 * nd_x, n_x + 3.0 * nd_x,
        n_x + 4.0 * nd_x, n_x + 5.0 * nd_x, n_x + 6.0 * nd_x, n_x + 7.0 * nd_x,
        n_x + 8.0 * nd_x, n_x + 8.0 * nd_x
    ])
    y = np.array([
        0.0, o_y, o_y + n_y, o_y + 2.0 * n_y, o_y + 3.0 * n_y, o_y + 4.0 * n_y,
        o_y + 5.0 * n_y, o_y + 6.0 * n_y, o_y + 7.0 * n_y, o_y + 8.0 * n_y,
        o_y + 9.0 * n_y, o_y + 10.0 * n_y
    ])
    n_z1 = top_depth + total_thickness * 0.82
    n_z2 = top_depth - total_thickness * 0.17
    z = np.array([
        0.0, n_z1, n_z1, n_z2, n_z2, n_z1, n_z1, n_z2, n_z2, n_z1, n_z1, n_z2
    ])

    df = df_trajectory(x, y, z)

    traj_4 = rqw.Trajectory(model,
                            md_datum=datum,
                            data_frame=df,
                            length_uom='m',
                            well_name='NESSIE_WELL')
    traj_4.write_hdf5()
    traj_4.create_xml()
    traj_4.control_points

    # block wells against grid geometry

    log.info('unfaulted grid blocking of well ' +
             str(rqw.well_name(trajectory)))
    bw = rqw.BlockedWell(model, grid=grid, trajectory=trajectory)
    bw.write_hdf5()
    bw.create_xml()
    assert bw.cell_count == 19
    assert len(bw.cell_indices) == bw.cell_count
    np.testing.assert_array_equal(
        bw.cell_indices,
        np.array([
            108, 708, 709, 1909, 1959, 2559, 2673, 2073, 2123, 923, 924, 974,
            374, 587, 588, 1188, 2388, 2389, 2989
        ]))

    log.info('unfaulted grid blocking of well ' + str(rqw.well_name(traj_2)))
    bw_2 = rqw.BlockedWell(model, grid=grid, trajectory=traj_2)
    bw_2.write_hdf5()
    bw_2.create_xml()
    assert bw_2.cell_count == 33
    assert len(bw_2.cell_indices) == bw_2.cell_count
    np.testing.assert_array_equal(
        bw_2.cell_indices,
        np.array([
            306, 256, 856, 857, 2057, 2058, 2059, 2659, 2660, 2610, 2611, 2612,
            2613, 2614, 2014, 2015, 2016, 1966, 766, 767, 167, 649, 648, 647,
            646, 645, 644, 643, 642, 1842, 1841, 1840, 2440
        ]))

    log.info('unfaulted grid blocking of well ' + str(rqw.well_name(traj_3)))
    bw_3 = rqw.BlockedWell(model, grid=grid, trajectory=traj_3)
    bw_3.write_hdf5()
    bw_3.create_xml()
    assert bw_3.cell_count == 18
    assert len(bw_3.cell_indices) == bw_3.cell_count
    np.testing.assert_array_equal(
        bw_3.cell_indices,
        np.array([
            300, 900, 2100, 2700, 2729, 2129, 2130, 930, 931, 331, 332, 339,
            340, 940, 941, 2141, 2142, 2742
        ]))

    log.info('unfaulted grid blocking of well ' + str(rqw.well_name(traj_4)))
    bw_4 = rqw.BlockedWell(model, grid=grid, trajectory=traj_4)
    bw_4.write_hdf5()
    bw_4.create_xml()
    assert bw_4.cell_count == 26
    assert len(bw_4.cell_indices) == bw_4.cell_count
    np.testing.assert_array_equal(
        bw_4.cell_indices,
        np.array([
            2402, 1802, 1852, 652, 52, 153, 753, 803, 2003, 2603, 2653, 2703,
            2103, 903, 904, 304, 354, 454, 1054, 2254, 2304, 2904, 2905, 2955,
            2355, 1155
        ]))

    # derive a faulted version of the grid

    cp = grid.corner_points(cache_cp_array=True).copy()

    # IK plane faults
    cp[:, 3:, :, :, :, :, :] += (flat_dx_di * 0.7, 0.0, layer_thickness * 1.3)
    cp[:, 5:, :, :, :, :, :] += (flat_dx_di * 0.4, 0.0, layer_thickness * 0.9)
    cp[:, 8:, :, :, :, :, :] += (flat_dx_di * 0.3, 0.0, layer_thickness * 0.6)

    # JK plane faults
    cp[:, :, ni_tail + ni_bend // 2:, :, :, :,
       0] -= flat_dx_di * 0.57  # horizontal break mid top bend
    cp[:, :, ni_tail + ni_bend + ni_half_mid:, :, :, :,
       2] += layer_thickness * 1.27  # vertical break in mid section

    # zig-zag fault
    j_step = nj // (ni_tail - 2)
    for i in range(ni_tail - 1):
        j_start = i * j_step
        if j_start >= nj:
            break
        cp[:, j_start:, i, :, :, :, 2] += 1.1 * total_thickness

    # JK horst blocks
    cp[:, :, ni - 4, :, :, :, :] -= (horst_half_dx, 0.0, horst_dz)
    cp[:, :, ni - 3:, :, :, :, 0] -= 2.0 * horst_half_dx
    cp[:, :, ni - 2, :, :, :, :] += (-horst_half_dx, 0.0, horst_dz)
    cp[:, :, ni - 1:, :, :, :, 0] -= 2.0 * horst_half_dx

    # JK horst block mid lower bend
    bend_horst_dz = horst_dz * maths.tan(bend_theta_di)
    cp[:, :, ni - (ni_tail + ni_bend // 2 + 1):ni -
       (ni_tail + ni_bend // 2 - 1), :, :, :, :] -= (horst_dz, 0.0,
                                                     bend_horst_dz)
    cp[:, :, ni - (ni_tail + ni_bend // 2 - 1):, :, :, :,
       2] -= 2.0 * bend_horst_dz

    faulted_grid = rqi.grid_from_cp(
        model,
        cp,
        crs.uuid,
        max_z_void=0.01,
        split_pillars=True,
        split_tolerance=0.01,
        ijk_handedness='right' if grid.grid_is_right_handed else 'left',
        known_to_be_straight=True)

    faulted_grid.write_hdf5_from_caches()
    faulted_grid.create_xml()

    # create some grid connection sets
    f_gcs, _ = faulted_grid.fault_connection_set(add_to_model=True)
    assert f_gcs is not None and f_gcs.count > 0
    p_gcs, _ = faulted_grid.pinchout_connection_set(add_to_model=True)
    assert p_gcs is not None and p_gcs.count > 0

    # block wells against faulted grid

    log.info('faulted grid blocking of well ' + str(rqw.well_name(trajectory)))
    fbw = rqw.BlockedWell(model, grid=faulted_grid, trajectory=trajectory)
    fbw.write_hdf5()
    fbw.create_xml()
    assert fbw.cell_count == 32
    assert len(fbw.cell_indices) == fbw.cell_count
    np.testing.assert_array_equal(
        fbw.cell_indices,
        np.array([
            108, 708, 709, 1909, 1956, 2556, 2673, 2674, 2724, 2124, 2174,
            2775, 2175, 2225, 2226, 2276, 2277, 1077, 1078, 1079, 1129, 1130,
            1131, 2331, 2332, 2382, 2383, 2384, 2984, 2385, 2386, 2986
        ]))

    log.info('faulted grid blocking of well ' + str(rqw.well_name(traj_2)))
    fbw_2 = rqw.BlockedWell(model, grid=faulted_grid, trajectory=traj_2)
    fbw_2.write_hdf5()
    fbw_2.create_xml()
    assert fbw_2.cell_count == 26
    assert len(fbw_2.cell_indices) == fbw_2.cell_count
    np.testing.assert_array_equal(
        fbw_2.cell_indices,
        np.array([
            254, 854, 2054, 2654, 2655, 2614, 2014, 2015, 1965, 1966, 766, 767,
            167, 49, 47, 1846, 45, 44, 43, 643, 642, 641, 1841, 1840, 2440,
            2439
        ]))

    log.info('faulted grid blocking of well ' + str(rqw.well_name(traj_3)))
    fbw_3 = rqw.BlockedWell(model, grid=faulted_grid, trajectory=traj_3)
    fbw_3.write_hdf5()
    fbw_3.create_xml()
    assert fbw_3.cell_count == 14
    assert len(fbw_3.cell_indices) == fbw_3.cell_count
    np.testing.assert_array_equal(
        fbw_3.cell_indices,
        np.array([
            2730, 2731, 2131, 2132, 2133, 933, 934, 937, 938, 2138, 2139, 2140,
            2740, 2741
        ]))

    log.info('faulted grid blocking of well ' + str(rqw.well_name(traj_4)))
    fbw_4 = rqw.BlockedWell(model, grid=faulted_grid, trajectory=traj_4)
    fbw_4.write_hdf5()
    fbw_4.create_xml()
    assert fbw_4.cell_count == 16
    assert len(fbw_4.cell_indices) == fbw_4.cell_count
    np.testing.assert_array_equal(
        fbw_4.cell_indices,
        np.array([
            2402, 1802, 1852, 652, 52, 202, 802, 2002, 852, 902, 302, 453, 503,
            1103, 1153, 553
        ]))

    # create a version of the faulted grid with a k gap

    k_gap_grid = rqi.grid_from_cp(
        model,
        cp,
        crs.uuid,
        max_z_void=0.01,
        split_pillars=True,
        split_tolerance=0.01,
        ijk_handedness='right' if grid.grid_is_right_handed else 'left',
        known_to_be_straight=True)

    # convert second layer to a K gap
    k_gap_grid.nk -= 1
    k_gap_grid.extent_kji[0] = k_gap_grid.nk
    k_gap_grid.k_gaps = 1
    k_gap_grid.k_gap_after_array = np.zeros(k_gap_grid.nk - 1, dtype=bool)
    k_gap_grid.k_gap_after_array[0] = True
    k_gap_grid.k_raw_index_array = np.zeros(k_gap_grid.nk, dtype=int)
    for k in range(1, k_gap_grid.nk):
        k_gap_grid.k_raw_index_array[k] = k + 1

    # clear some attributes which may no longer be valid
    k_gap_grid.pinchout = None
    k_gap_grid.inactive = None
    k_gap_grid.grid_skin = None
    if hasattr(k_gap_grid, 'array_thickness'):
        delattr(k_gap_grid, 'array_thickness')

    k_gap_grid.write_hdf5_from_caches()
    k_gap_grid.create_xml()

    k_gap_grid_uuid = k_gap_grid.uuid

    # reload k gap grid object to ensure it is properly initialised

    k_gap_grid = None
    k_gap_grid = grr.Grid(model, uuid=k_gap_grid_uuid)

    # block wells against faulted grid with k gap

    log.info('k gap grid blocking of well ' + str(rqw.well_name(trajectory)))
    try:
        gbw = rqw.BlockedWell(model, grid=k_gap_grid, trajectory=trajectory)
        gbw.write_hdf5()
        gbw.create_xml()
        assert gbw.cell_count == 24
    except Exception:
        log.exception('failed to block well against k gap grid')

    log.info('k gap grid blocking of well ' + str(rqw.well_name(traj_2)))
    try:
        gbw_2 = rqw.BlockedWell(model, grid=k_gap_grid, trajectory=traj_2)
        gbw_2.write_hdf5()
        gbw_2.create_xml()
        assert gbw_2.cell_count == 20
    except Exception:
        log.exception('failed to block well against k gap grid')

    log.info('k gap grid blocking of well ' + str(rqw.well_name(traj_3)))
    try:
        gbw_3 = rqw.BlockedWell(model, grid=k_gap_grid, trajectory=traj_3)
        gbw_3.write_hdf5()
        gbw_3.create_xml()
        assert gbw_3.cell_count == 10
    except Exception:
        log.exception('failed to block well against k gap grid')

    log.info('k gap grid blocking of well ' + str(rqw.well_name(traj_4)))
    try:
        gbw_4 = rqw.BlockedWell(model, grid=k_gap_grid, trajectory=traj_4)
        gbw_4.write_hdf5()
        gbw_4.create_xml()
        assert gbw_4.cell_count == 10
    except Exception:
        log.exception('failed to block well against k gap grid')

    # store model

    model.store_epc()

    assert k_gap_grid.k_gaps
    assert len(k_gap_grid.k_raw_index_array) == k_gap_grid.nk
    assert len(k_gap_grid.k_gap_after_array) == k_gap_grid.nk - 1
    assert k_gap_grid.pinched_out((1, 0, 2))

    # check gcs iterator
    gcs_list = list(model.iter_grid_connection_sets())
    assert len(gcs_list) == 2
    assert not bu.matching_uuids(gcs_list[0].uuid, gcs_list[1].uuid)

    # clean up
    model.h5_release()
    os.remove(model.h5_file_name())
Example #11
0
def extract_box(epc_file=None,
                source_grid=None,
                box=None,
                box_inactive=None,
                inherit_properties=False,
                inherit_realization=None,
                inherit_all_realizations=False,
                set_parent_window=None,
                new_grid_title=None,
                new_epc_file=None):
    """Extends an existing model with a new grid extracted as a logical IJK box from the source grid.

    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
       box (numpy int array of shape (2, 3)): the minimum and maximum kji0 indices in the source grid (zero based) to include
          in the extracted grid; note that cells with index equal to maximum value are included (unlike with python ranges)
       box_inactive (numpy bool array, optional): if present, shape must match box and values will be or'ed in with the
          inactive mask inherited from the source grid; if None, inactive mask will be as inherited from source grid
       inherit_properties (boolean, default False): if True, the new grid will have a copy of any properties associated
          with the source grid, with values taken from the specified box
       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
       set_parent_window (boolean, optional): if True, the extracted grid has its parent window attribute set; if False,
          the parent window is not set; if None, the default will be True if new_epc_file is None or False otherwise
       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 extracted grid (& crs)

    returns:
       new grid object with extent as implied by the box argument

    note:
       the epc file and associated hdf5 file are appended to (extended) with the new grid, unless a new_epc_file is specified,
       in which case the grid and inherited properties are written there instead
    """

    log.debug('extracting grid for box')

    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
    if set_parent_window is None:
        set_parent_window = (new_epc_file is None)
    model, source_grid = _establish_model_and_source_grid(
        epc_file, source_grid)
    assert source_grid.grid_representation in ['IjkGrid', 'IjkBlockGrid']
    assert model is not None
    assert box is not None and box.shape == (2, 3)
    assert np.all(box[1, :] >= box[0, :]) and np.all(
        box[0, :] >= 0) and np.all(box[1, :] < source_grid.extent_kji)

    if source_grid.grid_representation == 'IjkBlockGrid':
        source_grid.make_regular_points_cached()

    box_str = bx.string_iijjkk1_for_box_kji0(box)

    # create a new, empty grid object
    grid = grr.Grid(model)

    # inherit attributes from source grid
    grid.grid_representation = 'IjkGrid'
    grid.extent_kji = box[1, :] - box[0, :] + 1
    if box_inactive is not None:
        assert box_inactive.shape == tuple(grid.extent_kji)
    grid.nk, grid.nj, grid.ni = grid.extent_kji[0], grid.extent_kji[
        1], grid.extent_kji[2]
    grid.k_direction_is_down = source_grid.k_direction_is_down
    grid.grid_is_right_handed = source_grid.grid_is_right_handed
    grid.pillar_shape = source_grid.pillar_shape
    grid.has_split_coordinate_lines = source_grid.has_split_coordinate_lines
    # inherit the coordinate reference system used by the grid geometry
    grid.crs_uuid = source_grid.crs_uuid
    if source_grid.model is not model:
        model.duplicate_node(source_grid.model.root_for_uuid(grid.crs_uuid),
                             add_as_part=True)
    grid.crs = rqc.Crs(model, uuid=grid.crs_uuid)

    # inherit k_gaps for selected layer range
    _inherit_k_gaps(source_grid, grid, box)

    # extract inactive cell mask
    _extract_inactive_cell_mask(source_grid, grid, box_inactive, box)

    # extract the grid geometry
    source_grid.cache_all_geometry_arrays()

    # determine cell geometry is defined
    if hasattr(source_grid, 'array_cell_geometry_is_defined'):
        grid.array_cell_geometry_is_defined = _array_box(
            source_grid.array_cell_geometry_is_defined, box)
        grid.geometry_defined_for_all_cells_cached = np.all(
            grid.array_cell_geometry_is_defined)
    else:
        grid.geometry_defined_for_all_cells_cached = source_grid.geometry_defined_for_all_cells_cached

    # copy info for pillar geometry is defined
    grid.geometry_defined_for_all_pillars_cached = source_grid.geometry_defined_for_all_pillars_cached
    if hasattr(source_grid, 'array_pillar_geometry_is_defined'):
        grid.array_pillar_geometry_is_defined = _array_box(
            source_grid.array_pillar_geometry_is_defined, box)
        grid.geometry_defined_for_all_pillars_cached = np.all(
            grid.array_pillar_geometry_is_defined)

    # get reference to points for source grid geometry
    source_points = source_grid.points_ref()

    pillar_box = box.copy()
    if source_grid.k_gaps:
        pillar_box[:, 0] = source_grid.k_raw_index_array[pillar_box[:, 0]]
    pillar_box[
        1, :] += 1  # pillar points have extent one greater than cells, in each axis

    if not source_grid.has_split_coordinate_lines:
        log.debug('no split pillars in source grid')
        grid.points_cached = _array_box(
            source_points, pillar_box)  # should work, ie. preserve xyz axis
    else:
        _process_split_pillars(source_grid, grid, box, pillar_box)

    if set_parent_window:
        fine_coarse = fc.FineCoarse(grid.extent_kji,
                                    grid.extent_kji,
                                    within_coarse_box=box)
        fine_coarse.set_all_ratios_constant()
        grid.set_parent(source_grid.uuid, True, fine_coarse)

    collection = _inherit_collection(source_grid, grid, inherit_properties,
                                     box, inherit_realization,
                                     inherit_all_realizations)

    if new_grid_title is None or len(new_grid_title) == 0:
        new_grid_title = 'local grid ' + box_str + ' extracted from ' + str(
            rqet.citation_title_for_node(source_grid.root))

    model.h5_release()
    if new_epc_file:
        _write_grid(new_epc_file,
                    grid,
                    property_collection=collection,
                    grid_title=new_grid_title,
                    mode='w')
    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')

    return grid
Example #12
0
def interpolated_grid(epc_file,
                      grid_a,
                      grid_b,
                      a_to_b_0_to_1=0.5,
                      split_tolerance=0.01,
                      inherit_properties=False,
                      inherit_realization=None,
                      inherit_all_realizations=False,
                      new_grid_title=None,
                      new_epc_file=None):
    """Extends an existing model with a new grid geometry linearly interpolated between the two source_grids.

    arguments:
       epc_file (string): file name to rewrite the model's xml to
       grid_a, grid_b (grid.Grid objects): a pair of RESQML grid objects representing the end cases, between
          which the new grid will be interpolated
       a_to_b_0_to_1 (float, default 0.5): the interpolation factor in the range zero to one; a value of 0.0 will yield
          a copy of grid a, a value of 1.0 will yield a copy of grid b, intermediate values will yield a grid with all
          points interpolated
       split_tolerance (float, default 0.01): maximum offset of corner points for shared point to be generated; units
          are same as those in grid crs; only relevant if working from corner points, ignored otherwise
       inherit_properties (boolean, default False): if True, the new grid will have a copy of any properties associated
          with grid_a
       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
       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 interpolated grid (& crs)

    returns:
       new grid object (grid.Grid) with geometry interpolated between grid a and grid b

    notes:
       the hdf5 file used by the grid_a model is appended to, so it is recommended that the grid_a model's epc is specified
       as the first argument (unless a new epc file is required, sharing the hdf5 file)
    """

    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
    assert grid_a is not None and grid_b is not None, 'at least one source grid is missing'
    assert grid_a.grid_representation == 'IjkGrid' and grid_b.grid_representation == 'IjkGrid'
    assert 0.0 <= a_to_b_0_to_1 <= 1.0, 'interpolation factor outside range 0.0 to 1.0'
    assert tuple(grid_a.extent_kji) == tuple(
        grid_b.extent_kji), 'source grids have different extents'
    assert grid_a.k_direction_is_down == grid_b.k_direction_is_down, 'source grids have different k directions'
    assert grid_a.grid_is_right_handed == grid_b.grid_is_right_handed, 'source grids have different ijk handedness'
    assert grid_a.pillar_shape == grid_b.pillar_shape, 'source grids have different resqml pillar shapes'

    b_weight = a_to_b_0_to_1
    a_weight = 1.0 - b_weight

    model = grid_a.model

    if not bu.matching_uuids(grid_a.crs_uuid, grid_b.crs_uuid):
        crs_a = rqc.Crs(grid_a.model, uuid=grid_a.crs_uuid)
        crs_b = rqc.Crs(grid_b.model, uuid=grid_b.crs_uuid)
        assert crs_a.is_equivalent(crs_b),  \
            'end point grids for interpolation have different coordinate reference systems'

    log.info('loading geometry for two source grids')
    grid_a.cache_all_geometry_arrays()
    grid_b.cache_all_geometry_arrays()

    assert (grid_a.geometry_defined_for_all_cells()
            and grid_b.geometry_defined_for_all_cells()
            ), 'geometry not defined for all cells'
    # assert grid_a.geometry_defined_for_all_pillars() and grid_b.geometry_defined_for_all_pillars(),  \
    #     'geometry not defined for all pillars'

    work_from_pillars = _determine_work_from_pillars(grid_a, grid_b)

    # create a new, empty grid object
    grid = grr.Grid(model)

    # inherit attributes from source grid
    _inherit_basics(grid, grid_a, grid_b)

    if work_from_pillars:
        _interpolate_points_cached_from_pillars(grid, grid_a, grid_b, a_weight,
                                                b_weight)
    else:
        _interpolate_points_cached_from_cp(grid, grid_a, grid_b, a_weight,
                                           b_weight, split_tolerance)

    collection = _prepare_simple_inheritance(grid, grid_a, inherit_properties,
                                             inherit_realization,
                                             inherit_all_realizations)

    if new_grid_title is None or len(new_grid_title) == 0:
        new_grid_title = 'interpolated between two grids with factor: ' + str(
            a_to_b_0_to_1)

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

    return grid
Example #13
0
def coarsened_grid(epc_file,
                   source_grid,
                   fine_coarse,
                   inherit_properties=False,
                   inherit_realization=None,
                   inherit_all_realizations=False,
                   set_parent_window=None,
                   infill_missing_geometry=True,
                   new_grid_title=None,
                   new_epc_file=None):
    """Generates a coarsened version of an unsplit source grid, todo: optionally inheriting properties.

    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
       fine_coarse (resqpy.olio.fine_coarse.FineCoarse object): the mapping between cells in the fine (source) and
          coarse (output) grids
       inherit_properties (boolean, default False): if True, the new grid will have a copy of any properties associated
          with the source grid, with values upscaled or sampled
       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
       set_parent_window (boolean or str, optional): if True or 'parent', the coarsened grid has its parent window attribute
          set; if False, the parent window is not set; if None, the default will be True if new_epc_file is None or False
          otherwise; if 'grandparent' then an intervening parent window with no refinement or coarsening will be skipped
          and its box used in the parent window for the new grid, relating directly to the original grid
       infill_missing_geometry (boolean, default True): if True, an attempt is made to generate grid geometry in the
          source grid wherever it is undefined; if False, any undefined geometry will result in an assertion failure
       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 refined grid (& crs)

    returns:
       new grid object being the coarsened grid; the epc and hdf5 files are written to as an intentional side effect

    note:
       this function coarsens an entire grid; to coarsen a local area of a grid, first use the extract_box function
       and then use this function on the extracted grid; in such a case, using a value of 'grandparent' for the
       set_parent_window argument will relate the coarsened grid back to the original
    """

    new_epc_file, model, source_grid = _establish_files_and_model(
        epc_file, new_epc_file, source_grid)

    if set_parent_window is None:
        set_parent_window = (new_epc_file is None)
    assert fine_coarse is not None and isinstance(fine_coarse, fc.FineCoarse)

    assert not source_grid.has_split_coordinate_lines, 'coarsening only available for unsplit grids: use other functions to heal faults first'

    if infill_missing_geometry and (
            not source_grid.geometry_defined_for_all_cells()
            or not source_grid.geometry_defined_for_all_pillars()):
        log.debug('attempting infill of geometry missing in source grid')
        source_grid.set_geometry_is_defined(treat_as_nan=None,
                                            treat_dots_as_nan=True,
                                            complete_partial_pillars=True,
                                            nullify_partial_pillars=False,
                                            complete_all=True)

    assert source_grid.geometry_defined_for_all_pillars(
    ), 'coarsening requires geometry to be defined for all pillars'
    assert source_grid.geometry_defined_for_all_cells(
    ), 'coarsening requires geometry to be defined for all cells'
    assert not source_grid.k_gaps, 'coarsening of grids with k gaps not currently supported'

    assert tuple(fine_coarse.fine_extent_kji) == tuple(source_grid.extent_kji),  \
           'fine_coarse mapping fine extent does not match that of source grid'
    fine_coarse.assert_valid()

    source_grid.cache_all_geometry_arrays()
    source_points = source_grid.points_ref().reshape(
        (source_grid.nk + 1), (source_grid.nj + 1) * (source_grid.ni + 1), 3)

    # create a new, empty grid object
    grid = grr.Grid(model)

    # inherit attributes from source grid
    grid.grid_representation = 'IjkGrid'
    grid.extent_kji = fine_coarse.coarse_extent_kji
    grid.nk, grid.nj, grid.ni = grid.extent_kji[0], grid.extent_kji[
        1], grid.extent_kji[2]
    grid.k_direction_is_down = source_grid.k_direction_is_down
    grid.grid_is_right_handed = source_grid.grid_is_right_handed
    grid.pillar_shape = source_grid.pillar_shape
    grid.has_split_coordinate_lines = False
    grid.split_pillars_count = None
    # inherit the coordinate reference system used by the grid geometry
    grid.crs_uuid = source_grid.crs_uuid
    if source_grid.model is not model:
        model.duplicate_node(source_grid.model.root_for_uuid(grid.crs_uuid),
                             add_as_part=True)
    grid.crs = rqc.Crs(model, grid.crs_uuid)

    coarsened_points = np.empty(
        (grid.nk + 1, (grid.nj + 1) * (grid.ni + 1),
         3))  # note: gets reshaped after being populated

    k_ratio_constant = fine_coarse.constant_ratios[0]
    if k_ratio_constant:
        k_indices = None
    else:
        k_indices = np.empty(grid.nk + 1, dtype=int)
        k_indices[0] = 0
        for k in range(grid.nk):
            k_indices[k + 1] = k_indices[k] + fine_coarse.vector_ratios[0][k]
        assert k_indices[-1] == source_grid.nk

    for cjp in range(grid.nj + 1):
        for cji in range(grid.ni + 1):
            natural_coarse_pillar = cjp * (grid.ni + 1) + cji
            natural_fine_pillar = fine_coarse.fine_for_coarse_natural_pillar_index(
                natural_coarse_pillar)
            if k_ratio_constant:
                coarsened_points[:, natural_coarse_pillar, :] = source_points[
                    0:source_grid.nk + 1:k_ratio_constant,
                    natural_fine_pillar, :]
            else:
                coarsened_points[:, natural_coarse_pillar, :] = source_points[
                    k_indices, natural_fine_pillar, :]

    grid.points_cached = coarsened_points.reshape(
        ((grid.nk + 1), (grid.nj + 1), (grid.ni + 1), 3))

    grid.geometry_defined_for_all_pillars_cached = True
    grid.geometry_defined_for_all_cells_cached = True
    grid.array_cell_geometry_is_defined = np.full(tuple(grid.extent_kji),
                                                  True,
                                                  dtype=bool)

    collection = None
    if inherit_properties:
        source_collection = source_grid.extract_property_collection()
        if source_collection is not None:
            collection = rqp.GridPropertyCollection()
            collection.set_grid(grid)
            collection.extend_imported_list_copying_properties_from_other_grid_collection(
                source_collection,
                coarsening=fine_coarse,
                realization=inherit_realization,
                copy_all_realizations=inherit_all_realizations)

    _set_parent_window_in_grid(set_parent_window, source_grid, grid,
                               fine_coarse)

    # write grid
    if new_grid_title is None or len(new_grid_title) == 0:
        new_grid_title = 'grid coarsened from ' + str(
            rqet.citation_title_for_node(source_grid.root))

    model.h5_release()
    if new_epc_file:
        _write_grid(new_epc_file,
                    grid,
                    property_collection=collection,
                    grid_title=new_grid_title,
                    mode='w')
    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')

    return grid