Exemple #1
0
    def combine_polylines(self, polylines):
        """Combines the isclosed boolean array, coordinates and count data for a list of polyline objects.

        args:
            polylines: list of polyline objects
        """

        self.count_perpol = []
        self.closed_array = []

        for poly in polylines:
            if poly == polylines[0]:
                master_crs = rcrs.Crs(self.model, uuid = poly.crs_uuid)
                self.crs_uuid = poly.crs_uuid
                self.coordinates = poly.coordinates.copy()
            else:
                curr_crs = rcrs.Crs(self.model, uuid = poly.crs_uuid)
                if not curr_crs.is_equivalent(master_crs):
                    shifted = curr_crs.convert_array_to(master_crs, poly.coordinates)
                    self.coordinates = np.concatenate((self.coordinates, shifted))
                else:
                    self.coordinates = np.concatenate((self.coordinates, poly.coordinates))

            self.closed_array.append(poly.isclosed)
            self.count_perpol.append(int(len(poly.coordinates)))

        self.count_perpol = np.array(self.count_perpol)

        assert len(self.closed_array) == len(self.count_perpol)
        assert np.sum(self.count_perpol) == len(self.coordinates)

        self.polys = polylines
Exemple #2
0
def _populate_composite_face_sets_for_polylines(model, grid, polylines,
                                                lines_crs_uuid, grid_crs,
                                                lines_file_list,
                                                full_pillar_list_dict,
                                                composite_face_set_dict):
    lines_crs = None if lines_crs_uuid is None else rqc.Crs(
        model, uuid=lines_crs_uuid)
    if polylines:
        for i, polyline in enumerate(polylines):
            new_line = polyline.coordinates.copy()
            if polyline.crs_uuid is not None and polyline.crs_uuid != lines_crs_uuid:
                lines_crs_uuid = polyline.crs_uuid
                lines_crs = rqc.Crs(model, uuid=lines_crs_uuid)
            if lines_crs:
                lines_crs.convert_array_to(grid_crs, new_line)
            title = polyline.title if polyline.title else 'fault_' + str(i)
            _make_face_sets_for_new_lines([new_line], title, grid,
                                          full_pillar_list_dict,
                                          composite_face_set_dict)
    else:
        for filename in lines_file_list:
            new_lines = sl.read_lines(filename)
            if lines_crs is not None:
                for a in new_lines:
                    lines_crs.convert_array_to(grid_crs, a)
            _, f_name = os.path.split(filename)
            if f_name.lower().endswith('.dat'):
                face_set_id = f_name[:-4]
            else:
                face_set_id = f_name
            _make_face_sets_for_new_lines(new_lines, face_set_id, grid,
                                          full_pillar_list_dict,
                                          composite_face_set_dict)
Exemple #3
0
def test_model_context(tmp_path):

    # Create a new model
    epc_path = str(tmp_path / 'tmp_model.epc')
    model = rq.new_model(epc_path)
    crs = rqc.Crs(parent_model=model, title='kuzcotopia')
    crs_uuid = crs.uuid
    crs.create_xml()
    model.store_epc()
    del crs, model

    # Re-open model in read/write mode
    with rq.ModelContext(epc_path, mode="rw") as model2:

        crs2 = rqc.Crs(model2, uuid=crs_uuid)
        assert len(list(model2.iter_crs())) == 1
        assert crs2.title == 'kuzcotopia'

        # Make a change
        crs2.title = 'wabajam'
        crs2.create_xml(reuse=False)

    # Re-open model in read mode
    with rq.ModelContext(epc_path, mode="r") as model3:

        # Check model has loaded correctly
        assert len(list(model3.iter_crs())) == 1
        crs3 = rqc.Crs(model3, uuid=crs_uuid)
        assert crs3.title == 'wabajam'

    # Overwrite model
    with rq.ModelContext(epc_path, mode="create") as model4:
        # Should be empty
        crs_list = list(model4.iter_crs())
        assert len(crs_list) == 0
Exemple #4
0
    def from_polyset(self, polyset):
        """Instantiates a pointset using points from an input polylineset (PolylineSetRepresentation) object

        arguments:
            polyset (resqpy.lines.PolylineSet object): a polylineset object to generate the pointset from
        """
        master_crs = rcrs.Crs(self.model, uuid = polyset.polys[0].crs_uuid)
        if polyset.polys[0].isclosed and vec.isclose(polyset.polys[0].coordinates[0], polyset.polys[0].coordinates[-1]):
            poly_coords = polyset.polys[0].coordinates[:-1].copy()
        else:
            poly_coords = polyset.polys[0].coordinates.copy()
        for poly in polyset.polys[1:]:
            curr_crs = rcrs.Crs(self.model, uuid = poly.crs_uuid)
            assert master_crs is not None
            assert poly_coords is not None
            if not curr_crs.is_equivalent(master_crs):
                shifted = curr_crs.convert_array_to(master_crs, poly.coordinates)
                poly_coords = concat_polyset_points(poly.isclosed, shifted, poly_coords)
            else:
                poly_coords = concat_polyset_points(poly.isclosed, poly.coordinates, poly_coords)
        self.add_patch(poly_coords)
        if polyset.rep_int_root is not None:
            self.set_represented_interpretation_root(polyset.rep_int_root)
        self.check_crs_match(master_crs.uuid)
        if not self.title:
            self.title = polyset.title
Exemple #5
0
def test_model_copy_all_parts(example_model_with_properties):

    epc = example_model_with_properties.epc_file
    dir = example_model_with_properties.epc_directory
    copied_epc = os.path.join(dir, 'copied.epc')

    # test copying without consolidation
    original = rq.Model(epc)
    assert original is not None
    copied = rq.new_model(copied_epc)
    copied.copy_all_parts_from_other_model(original, consolidate=False)

    assert set(original.uuids()) == set(copied.uuids())
    assert set(original.parts()) == set(copied.parts())

    # test without consolidation of two crs objects
    copied = rq.new_model(copied_epc)
    new_crs = rqc.Crs(copied)
    new_crs.create_xml()

    copied.copy_all_parts_from_other_model(original, consolidate=False)

    assert len(copied.parts()) == len(original.parts()) + 1
    assert set(original.parts()).issubset(set(copied.parts()))
    assert len(copied.parts(obj_type='LocalDepth3dCrs')) == 2

    # test with consolidation of two crs objects
    copied = rq.new_model(copied_epc)
    new_crs = rqc.Crs(copied)
    new_crs.create_xml()

    copied.copy_all_parts_from_other_model(original, consolidate=True)

    assert len(copied.parts()) == len(original.parts())
    assert len(copied.parts(obj_type='LocalDepth3dCrs')) == 1

    crs_uuid = copied.uuid(obj_type='LocalDepth3dCrs')
    assert (bu.matching_uuids(crs_uuid, new_crs.uuid) or bu.matching_uuids(
        crs_uuid, original.uuid(obj_type='LocalDepth3dCrs')))

    # test write and re-load of copied model
    copied.store_epc()
    re_opened = rq.Model(copied_epc)
    assert re_opened is not None

    assert len(copied.parts()) == len(original.parts())

    crs_uuid = re_opened.uuid(obj_type='LocalDepth3dCrs')
    assert (bu.matching_uuids(crs_uuid, new_crs.uuid) or bu.matching_uuids(
        crs_uuid, original.uuid(obj_type='LocalDepth3dCrs')))
Exemple #6
0
def _inherit_basics(grid, grid_a, grid_b):
    grid.grid_representation = 'IjkGrid'
    grid.extent_kji = grid_a.extent_kji.copy()
    grid.nk, grid.nj, grid.ni = grid.extent_kji
    grid.k_direction_is_down = grid_a.k_direction_is_down
    grid.grid_is_right_handed = grid_a.grid_is_right_handed
    grid.pillar_shape = grid_a.pillar_shape
    grid.has_split_coordinate_lines = (grid_a.has_split_coordinate_lines
                                       or grid_b.has_split_coordinate_lines)
    # inherit the coordinate reference system used by the grid geometry
    grid.crs_uuid = grid_a.crs_uuid
    if grid_a.model is not grid.model:
        grid.model.duplicate_node(grid_a.model.root_for_uuid(
            grid_a, grid.crs_uuid),
                                  add_as_part=True)
    grid.crs = rqc.Crs(grid.model, uuid=grid.crs_uuid)

    if grid_a.inactive is None or grid_b.inactive is None:
        grid.inactive = None
    else:
        grid.inactive = np.logical_and(grid_a.inactive, grid_b.inactive)
    grid.geometry_defined_for_all_cells_cached = True
    grid.array_cell_geometry_is_defined = np.ones(tuple(grid.extent_kji),
                                                  dtype=bool)
    grid.geometry_defined_for_all_pillars_cached = True
Exemple #7
0
def test_model(tmp_path):

    epc = os.path.join(tmp_path, 'model.epc')
    model = rq.new_model(epc)
    assert model is not None
    crs = rqc.Crs(model)
    crs_root = crs.create_xml()
    model.store_epc()
    assert os.path.exists(epc)
    md_datum_1 = rqw.MdDatum(model,
                             location=(0.0, 0.0, -50.0),
                             crs_uuid=crs.uuid)
    md_datum_1.create_xml(title='Datum & 1')
    md_datum_2 = rqw.MdDatum(model,
                             location=(3.0, 0.0, -50.0),
                             crs_uuid=crs.uuid)
    md_datum_2.create_xml(title='Datum < 2')
    assert len(model.uuids(obj_type='MdDatum')) == 2
    model.store_epc()

    model = rq.Model(epc)
    assert model is not None
    assert len(model.uuids(obj_type='MdDatum')) == 2
    datum_part_1 = model.part(obj_type='MdDatum', title='1', title_mode='ends')
    datum_part_2 = model.part(obj_type='MdDatum', title='2', title_mode='ends')
    assert datum_part_1 is not None and datum_part_2 is not None and datum_part_1 != datum_part_2
    datum_uuid_1 = rqet.uuid_in_part_name(datum_part_1)
    datum_uuid_2 = rqet.uuid_in_part_name(datum_part_2)
    assert not bu.matching_uuids(datum_uuid_1, datum_uuid_2)
    p1 = model.uuid_part_dict[bu.uuid_as_int(datum_uuid_1)]
    p2 = model.uuid_part_dict[bu.uuid_as_int(datum_uuid_2)]
    assert p1 == datum_part_1 and p2 == datum_part_2
Exemple #8
0
def test_forestry(example_model_with_prop_ts_rels):
    model = example_model_with_prop_ts_rels
    full_parts_list = model.parts()
    dp_parts_list = model.parts(obj_type='DiscreteProperty')
    assert len(dp_parts_list) > 1
    # remove an individual part
    model.remove_part(dp_parts_list[0])
    # corrupt some forest dictionary entries and test tidy up
    for part in dp_parts_list[1:]:
        model.parts_forest[part] = (None, None, None)
    model.tidy_up_forests()
    assert len(model.parts()) + len(dp_parts_list) == len(full_parts_list)
    assert all(p not in model.parts() for p in dp_parts_list)
    # test patch_root_for_part()
    crs_uuid = model.uuid(obj_type='LocalDepth3dCrs')
    crs_part = model.part_for_uuid(crs_uuid)
    assert crs_uuid is not None and crs_part is not None
    crs = rqc.Crs(model, uuid=crs_uuid)
    assert crs is not None
    crs.title = 'relativity'
    crs.originator = 'einstein'
    new_crs_node = crs.create_xml(add_as_part=False, reuse=False)
    rqet.find_tag(new_crs_node, 'VerticalUom').text = 'ft[US]'
    model.patch_root_for_part(crs_part, new_crs_node)
    assert rqet.find_tag_text(model.root(uuid=crs_uuid),
                              'VerticalUom') == 'ft[US]'
    assert model.citation_title_for_part(crs_part) == 'relativity'
    assert model.title(uuid=crs_uuid) == 'relativity'
    assert rqet.find_nested_tags_text(model.root(uuid=crs_uuid),
                                      ['Citation', 'Originator']) == 'einstein'
    # rough test of low level fell_part()
    model.fell_part(crs_part)
    assert len(model.parts()) + len(dp_parts_list) + 1 == len(full_parts_list)
Exemple #9
0
    def _set_mesh_from_df(self):
        """Creates Mesh object; called before writing to hdf5 or creating xml."""
        # note: actual data is stored in related Property if realization number is present, directly in Mesh otherwise

        assert self.n_rows == len(self.df)
        assert self.n_cols == len(self.df.columns)

        if self.mesh is None:
            origin = (0.0, 0.0, 0.0)
            dxyz_dij = np.array([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]])
            crs_uuids = self.model.uuids(obj_type='LocalDepth3dCrs')
            if len(crs_uuids) == 0:
                crs = rqc.Crs(self.model)
                crs.create_xml()
                crs_uuid = crs.uuid
            else:  # use any available crs
                crs_uuid = crs_uuids[0]
            if self.realization is None:
                self.mesh = rqs.Mesh(self.model,
                                     mesh_flavour='reg&z',
                                     ni=self.n_cols,
                                     nj=self.n_rows,
                                     dxyz_dij=dxyz_dij,
                                     origin=origin,
                                     z_values=np.array(self.df),
                                     crs_uuid=crs_uuid)
            else:
                self.mesh = rqs.Mesh(self.model,
                                     mesh_flavour='regular',
                                     ni=self.n_cols,
                                     nj=self.n_rows,
                                     dxyz_dij=dxyz_dij,
                                     origin=origin,
                                     crs_uuid=crs_uuid)
            self.mesh.write_hdf5()
            mesh_root = self.mesh.create_xml(title=self.title)
            rqet.create_metadata_xml(mesh_root, {'dataframe': 'true'})
            if self.realization is not None:
                self.pc = rqp.PropertyCollection()
                self.pc.set_support(support=self.mesh)
                dataframe_pk_uuid = self.model.uuid(obj_type='PropertyKind',
                                                    title='dataframe')
                if dataframe_pk_uuid is None:
                    dataframe_pk = rqp.PropertyKind(self.model,
                                                    title='dataframe',
                                                    example_uom='Euc')
                    dataframe_pk.create_xml()
                    dataframe_pk_uuid = dataframe_pk.uuid
                self.pc.add_cached_array_to_imported_list(
                    np.array(self.df),
                    'dataframe',
                    self.title,
                    uom='Euc',
                    property_kind='dataframe',
                    local_property_kind_uuid=dataframe_pk_uuid,
                    realization=self.realization,
                    indexable_element='nodes')
                self.pc.write_hdf5_for_imported_list()
                self.pc.create_xml_for_imported_list_and_add_parts_to_model()
Exemple #10
0
def _iter_crs(model):
    """Iterable of all CRS objects associated with the model."""

    import resqpy.crs as rqc  # imported here for speed, module is not always needed

    uuids = _uuids(model, obj_type='LocalDepth3dCrs') + _uuids(
        model, obj_type='LocalTime3dCrs')
    if uuids:
        for uuid in uuids:
            yield rqc.Crs(model, uuid=uuid)
Exemple #11
0
def z_inc_down(grid):
    """Return True if z increases downwards in the coordinate reference system used by the grid geometry

    :meta common:
    """

    if grid.crs is None:
        assert grid.crs_uuid is not None
        grid.crs = rqc.Crs(grid.model, uuid=grid.crs_uuid)
    return grid.crs.z_inc_down
Exemple #12
0
def test_crs_reuse():
    model = rq.Model(new_epc = True, create_basics = True)
    crs_a = rqc.Crs(model)
    crs_a.create_xml()
    crs_b = rqc.Crs(model)
    crs_b.create_xml()
    assert len(model.parts(obj_type = 'LocalDepth3dCrs')) == 1
    assert crs_a == crs_b
    assert bu.matching_uuids(crs_a.uuid, crs_b.uuid)
    crs_c = rqc.Crs(model, z_inc_down = False)
    crs_c.create_xml()
    assert len(model.parts(obj_type = 'LocalDepth3dCrs')) == 2
    assert crs_c != crs_a
    assert not bu.matching_uuids(crs_c.uuid, crs_a.uuid)
    crs_d = rqc.Crs(model, z_units = 'ft')
    crs_d.create_xml()
    assert len(model.parts(obj_type = 'LocalDepth3dCrs')) == 3
    crs_e = rqc.Crs(model, z_inc_down = False)
    crs_e.create_xml()
    assert len(model.uuids(obj_type = 'LocalDepth3dCrs')) == 3
    assert crs_e == crs_c
    assert bu.matching_uuids(crs_e.uuid, crs_c.uuid)
    crs_f = rqc.Crs(model)
    crs_f.create_xml(reuse = False)
    assert len(model.parts(obj_type = 'LocalDepth3dCrs')) == 4
    assert crs_f == crs_a
    assert not bu.matching_uuids(crs_f.uuid, crs_a.uuid)
Exemple #13
0
def test_resqml_units(tmp_path, resqml_xy_units, resqml_z_units):
    # Arrange
    current_filename = os.path.split(getsourcefile(lambda: 0))[0]
    base_folder = os.path.dirname(os.path.dirname(current_filename))
    ensemble_dir = f'{base_folder}/test_data/wren'
    epc_file = f'{tmp_path}/test.epc'

    # Act
    import_vdb_ensemble(epc_file,
                        ensemble_dir,
                        resqml_xy_units=resqml_xy_units,
                        resqml_z_units=resqml_z_units)
    model = rq.Model(epc_file)
    crs_uuid = model.uuid(obj_type='LocalDepth3dCrs')
    crs = rqc.Crs(model, uuid=crs_uuid)

    # todo: could check grid point values are being converted
    # Assert
    assert crs.xy_units == resqml_xy_units
    assert crs.z_units == resqml_z_units
Exemple #14
0
    def change_crs(self, required_crs):
        """Changes the crs of the point set, also sets a new uuid if crs changed.

        notes:
           this method is usually used to change the coordinate system for a temporary resqpy object;
           to add as a new part, call write_hdf5() and create_xml() methods
        """

        old_crs = rcrs.Crs(self.model, uuid = self.crs_uuid)
        self.crs_uuid = required_crs.uuid
        if required_crs == old_crs or not self.patch_ref_list:
            log.debug(f'no crs change needed for {self.title}')
            return
        log.debug(f'crs change needed for {self.title} from {old_crs.title} to {required_crs.title}')
        self.load_all_patches()
        self.patch_ref_list = []
        for patch_points in self.patch_array_list:
            required_crs.convert_array_from(old_crs, patch_points)
            self.patch_ref_list.append((None, None, len(patch_points)))
        self.full_array = None  # clear cached full array for point set
        self.uuid = bu.new_uuid()  # hope this doesn't cause problems
Exemple #15
0
def _global_to_local_crs(grid,
                         a,
                         crs_uuid=None,
                         global_xy_units=None,
                         global_z_units=None,
                         global_z_increasing_downward=None):
    """Converts array of points in situ from global coordinate system to established local one."""

    if crs_uuid is None:
        crs_uuid = grid.crs_uuid
        if crs_uuid is None:
            return a

    flat_a = a.reshape((
        -1,
        3))  # flattened view of array a as vector of (x, y, z) points, in situ

    crs = rqc.Crs(grid.model, uuid=crs_uuid)

    if global_xy_units is not None:
        bwam.convert_lengths(flat_a[:, 0], global_xy_units, crs.xy_units)  # x
        bwam.convert_lengths(flat_a[:, 1], global_xy_units, crs.xy_units)  # y
    if global_z_units is not None:
        bwam.convert_lengths(flat_a[:, 2], global_z_units, crs.z_units)  # z

    # This code assumes x, y, z offsets are in local crs units
    flat_a[:, 0] -= crs.x_offset
    flat_a[:, 1] -= crs.y_offset
    flat_a[:, 2] -= crs.z_offset

    # note: here negation is made in local crs; if z_offset is not zero, this might not be what is intended
    if global_z_increasing_downward is not None:
        if global_z_increasing_downward != crs.z_inc_down:
            flat_a[:, 2] = np.negative(flat_a[:, 2])

    if crs.rotated:
        flat_a[:] = vec.rotate_array(crs.reverse_rotation_matrix, flat_a)

    return flat_a.reshape(a.shape)
Exemple #16
0
    def change_crs(self, required_crs):
        """Changes the crs of the surface, also sets a new uuid if crs changed.

        note:
           this method is usually used to change the coordinate system for a temporary resqpy object;
           to add as a new part, call write_hdf5() and create_xml() methods
        """

        old_crs = rqc.Crs(self.model, uuid=self.crs_uuid)
        self.crs_uuid = required_crs.uuid
        if required_crs == old_crs or not self.patch_list:
            log.debug(f'no crs change needed for {self.title}')
            return
        log.debug(
            f'crs change needed for {self.title} from {old_crs.title} to {required_crs.title}'
        )
        for patch in self.patch_list:
            patch.triangles_and_points()
            required_crs.convert_array_from(old_crs, patch.points)
            patch.crs_uuid = self.crs_uuid
        self.triangles = None  # clear cached arrays for surface
        self.points = None
        self.uuid = bu.new_uuid()  # hope this doesn't cause problems
        assert self.root is None
Exemple #17
0
def test_vertical_prism_grid_from_seed_points_and_surfaces(tmp_path):

    seed(23487656)  # to ensure test reproducibility

    epc = os.path.join(tmp_path, 'voronoi_prism_grid.epc')
    model = rq.new_model(epc)
    crs = rqc.Crs(model)
    crs.create_xml()

    # define a boundary polyline:
    b_count = 7
    boundary_points = np.empty((b_count, 3))
    radius = 1000.0
    for i in range(b_count):
        theta = -vec.radians_from_degrees(i * 360.0 / b_count)
        boundary_points[i] = (2.0 * radius * maths.cos(theta),
                              radius * maths.sin(theta), 0.0)
    boundary = rql.Polyline(model,
                            set_coord=boundary_points,
                            set_bool=True,
                            set_crs=crs.uuid,
                            title='rough ellipse')
    boundary.write_hdf5()
    boundary.create_xml()

    # derive a larger area of interest
    aoi = rql.Polyline.from_scaled_polyline(boundary,
                                            1.1,
                                            title='area of interest')
    aoi.write_hdf5()
    aoi.create_xml()
    min_xy = np.min(aoi.coordinates[:, :2], axis=0) - 50.0
    max_xy = np.max(aoi.coordinates[:, :2], axis=0) + 50.0

    print(f'***** min max xy aoi+ : {min_xy} {max_xy}')  # debug

    # create some seed points within boundary
    seed_count = 5
    seeds = rqs.PointSet(model,
                         crs_uuid=crs.uuid,
                         polyline=boundary,
                         random_point_count=seed_count,
                         title='seeds')
    seeds.write_hdf5()
    seeds.create_xml()
    seeds_xy = seeds.single_patch_array_ref(0)

    for seed_xy in seeds_xy:
        assert aoi.point_is_inside_xy(
            seed_xy), f'seed point {seed_xy} outwith aoi'

    print(
        f'***** min max xy seeds : {np.min(seeds_xy, axis = 0)} {np.max(seeds_xy, axis = 0)}'
    )  # debug

    # create some horizon surfaces
    ni, nj = 21, 11
    lattice = rqs.Mesh(model,
                       crs_uuid=crs.uuid,
                       mesh_flavour='regular',
                       ni=ni,
                       nj=nj,
                       origin=(min_xy[0], min_xy[1], 0.0),
                       dxyz_dij=np.array([[
                           (max_xy[0] - min_xy[0]) / (ni - 1), 0.0, 0.0
                       ], [0.0, (max_xy[1] - min_xy[1]) / (nj - 1), 0.0]]))
    lattice.write_hdf5()
    lattice.create_xml()
    horizons = []
    for i in range(4):
        horizon_depths = 1000.0 + 100.0 * i + 20.0 * (np.random.random(
            (nj, ni)) - 0.5)
        horizon_mesh = rqs.Mesh(model,
                                crs_uuid=crs.uuid,
                                mesh_flavour='ref&z',
                                ni=ni,
                                nj=nj,
                                z_values=horizon_depths,
                                z_supporting_mesh_uuid=lattice.uuid,
                                title='h' + str(i))
        horizon_mesh.write_hdf5()
        horizon_mesh.create_xml()
        horizon_surface = rqs.Surface(model,
                                      crs_uuid=crs.uuid,
                                      mesh=horizon_mesh,
                                      quad_triangles=True,
                                      title=horizon_mesh.title)
        horizon_surface.write_hdf5()
        horizon_surface.create_xml()
        horizons.append(horizon_surface)

    # create a re-triangulated Voronoi vertical prism grid
    grid = rug.VerticalPrismGrid.from_seed_points_and_surfaces(
        model, seeds_xy, horizons, aoi, title="giant's causeway")
    assert grid is not None
    grid.write_hdf5()
    grid.create_xml()

    # check cell thicknesses are in expected range
    thick = grid.thickness()
    assert np.all(thick >= 80.0)
    assert np.all(thick <= 120.0)

    model.store_epc()
Exemple #18
0
def test_vertical_prism_grid_from_surfaces(tmp_path):

    epc = os.path.join(tmp_path, 'vertical_prism.epc')
    model = rq.new_model(epc)
    crs = rqc.Crs(model)
    crs.create_xml()

    # create a point set representing a pentagon with a centre node
    pentagon_points = np.array([[-100.0, -200.0, 1050.0],
                                [-200.0, 0.0, 1050.0], [0.0, 200.0, 1025.0],
                                [200.0, 0.0, 975.0], [100.0, -200.0, 999.0],
                                [0.0, 0.0, 1000.0]])
    pentagon = rqs.PointSet(model,
                            points_array=pentagon_points,
                            crs_uuid=crs.uuid,
                            title='pentagon')
    pentagon.write_hdf5()
    pentagon.create_xml()

    # create a surface from the point set (will make a Delauney triangulation)
    top_surf = rqs.Surface(model, point_set=pentagon, title='top surface')
    top_surf.write_hdf5()
    top_surf.create_xml()
    surf_list = [top_surf]

    # check the pentagon surface
    pentagon_triangles, pentagon_points = top_surf.triangles_and_points()
    assert pentagon_points.shape == (6, 3)
    assert pentagon_triangles.shape == (5, 3)

    # create a couple of horizontal surfaces at greater depths
    boundary = np.array([[-300.0, -300.0, 0.0], [300.0, 300.0, 0.0]])
    for depth in (1100.0, 1200.0):
        base = rqs.Surface(model)
        base.set_to_horizontal_plane(depth, boundary)
        base.write_hdf5()
        base.create_xml()
        surf_list.append(base)

    # now build a vertical prism grid from the surfaces
    grid = rug.VerticalPrismGrid.from_surfaces(model,
                                               surf_list,
                                               title='the pentagon')
    grid.write_hdf5()
    grid.create_xml()

    model.store_epc()

    # re-open model

    model = rq.Model(epc)
    assert model is not None

    # find grid by title
    grid_uuid = model.uuid(obj_type='UnstructuredGridRepresentation',
                           title='the pentagon')
    assert grid_uuid is not None

    # re-instantiate the grid
    grid = rug.VerticalPrismGrid(model, uuid=grid_uuid)
    assert grid is not None
    assert grid.nk == 2
    assert grid.cell_count == 10
    assert grid.node_count == 18
    assert grid.face_count == 35

    # create a very similar grid using explicit triangulation arguments

    # make the same Delauney triangulation
    triangles = triangulation.dt(pentagon_points, algorithm="scipy")
    assert triangles.ndim == 2 and triangles.shape[1] == 3

    # slightly shrink pentagon points to be within area of surfaces
    for i in range(len(pentagon_points)):
        if pentagon_points[i, 0] < 0.0:
            pentagon_points[i, 0] += 1.0
        elif pentagon_points[i, 0] > 0.0:
            pentagon_points[i, 0] -= 1.0
        if pentagon_points[i, 1] < 0.0:
            pentagon_points[i, 1] += 1.0
        elif pentagon_points[i, 1] > 0.0:
            pentagon_points[i, 1] -= 1.0

    # load the surfaces
    surf_uuids = model.uuids(obj_type='TriangulatedSetRepresentation',
                             sort_by='oldest')
    surf_list = []
    for surf_uuid in surf_uuids:
        surf_list.append(rqs.Surface(model, uuid=surf_uuid))

    # create a new vertical prism grid using the explicit triangulation arguments
    similar = rug.VerticalPrismGrid.from_surfaces(
        model,
        surf_list,
        column_points=pentagon_points,
        column_triangles=triangles,
        title='similar pentagon')

    # check similarity
    for attr in ('cell_shape', 'nk', 'cell_count', 'node_count', 'face_count'):
        assert getattr(grid, attr) == getattr(similar, attr)
    # for index_attr in ('nodes_per_face', 'nodes_per_face_cl', 'faces_per_cell', 'faces_per_cell_cl'):
    for i, (index_attr, index_attr_cl) in enumerate([
        ('nodes_per_face', 'nodes_per_face_cl'),
        ('faces_per_cell', 'faces_per_cell_cl')
    ]):
        ga_cl = getattr(grid, index_attr_cl)
        sa_cl = getattr(similar, index_attr_cl)
        assert np.all(ga_cl == sa_cl)
        ga = getattr(grid, index_attr)
        sa = getattr(similar, index_attr)
        ip = 0 if i == 0 else ga_cl[i - 1]
        assert set(ga[ip:ga_cl[i]]) == set(sa[ip:ga_cl[i]])
    assert_allclose(grid.points_ref(), similar.points_ref(), atol=2.0)

    # check that isotropic horizontal permeability is preserved
    permeability = 250.0
    primary_k = np.full((grid.cell_count, ), permeability)
    orthogonal_k = primary_k.copy()
    triple_k = grid.triple_horizontal_permeability(primary_k, orthogonal_k,
                                                   37.0)
    assert triple_k.shape == (grid.cell_count, 3)
    assert_array_almost_equal(triple_k, permeability)
    azimuth = np.linspace(0.0, 360.0, num=grid.cell_count)
    triple_k = grid.triple_horizontal_permeability(primary_k, orthogonal_k,
                                                   azimuth)
    assert triple_k.shape == (grid.cell_count, 3)
    assert_array_almost_equal(triple_k, permeability)

    # check that anisotropic horizontal permeability is correctly bounded
    orthogonal_k *= 0.1
    triple_k = grid.triple_horizontal_permeability(primary_k, orthogonal_k,
                                                   azimuth)
    assert triple_k.shape == (grid.cell_count, 3)
    assert np.all(triple_k <= permeability)
    assert np.all(triple_k >= permeability * 0.1)
    assert np.min(triple_k) < permeability / 2.0
    assert np.max(triple_k) > permeability / 2.0

    # set up some properties
    pc = grid.property_collection
    assert pc is not None
    pc.add_cached_array_to_imported_list(cached_array=None,
                                         source_info='unit test',
                                         keyword='NETGRS',
                                         property_kind='net to gross ratio',
                                         discrete=False,
                                         uom='m3/m3',
                                         indexable_element='cells',
                                         const_value=0.75)
    pc.add_cached_array_to_imported_list(cached_array=None,
                                         source_info='unit test',
                                         keyword='PERMK',
                                         property_kind='permeability rock',
                                         facet_type='direction',
                                         facet='K',
                                         discrete=False,
                                         uom='mD',
                                         indexable_element='cells',
                                         const_value=10.0)
    pc.add_cached_array_to_imported_list(cached_array=None,
                                         source_info='unit test',
                                         keyword='PERM',
                                         property_kind='permeability rock',
                                         facet_type='direction',
                                         facet='primary',
                                         discrete=False,
                                         uom='mD',
                                         indexable_element='cells',
                                         const_value=100.0)
    pc.add_cached_array_to_imported_list(cached_array=None,
                                         source_info='unit test',
                                         keyword='PERM',
                                         property_kind='permeability rock',
                                         facet_type='direction',
                                         facet='orthogonal',
                                         discrete=False,
                                         uom='mD',
                                         indexable_element='cells',
                                         const_value=20.0)
    x_min, x_max = grid.xyz_box()[:, 0]
    relative_x = (grid.centre_point()[:, 0] - x_min) * (x_max - x_min)
    azi = relative_x * 90.0 + 45.0
    pc.add_cached_array_to_imported_list(
        cached_array=azi,
        source_info='unit test',
        keyword='primary permeability azimuth',
        property_kind='plane angle',
        facet_type='direction',
        facet='primary',
        discrete=False,
        uom='dega',
        indexable_element='cells')
    pc.write_hdf5_for_imported_list()
    pc.create_xml_for_imported_list_and_add_parts_to_model()

    model.store_epc()

    # test that half cell transmissibilities can be computed
    half_t = grid.half_cell_transmissibility()
    assert np.all(half_t > 0.0)

    # add the half cell transmissibility array as a property
    pc.add_cached_array_to_imported_list(cached_array=half_t.flatten(),
                                         source_info='unit test',
                                         keyword='half transmissibility',
                                         property_kind='transmissibility',
                                         discrete=False,
                                         count=1,
                                         indexable_element='faces per cell')
    pc.write_hdf5_for_imported_list()
    pc.create_xml_for_imported_list_and_add_parts_to_model(
        extra_metadata={'uom': 'm3.cP/(d.kPa)'})

    model.store_epc()
Exemple #19
0
def test_tetra_grid(tmp_path):

    epc = os.path.join(tmp_path, 'tetra_test.epc')
    model = rq.new_model(epc)
    crs = rqc.Crs(model)
    crs.create_xml()

    # create an empty TetraGrid
    tetra = rug.TetraGrid(model, title='star')
    assert tetra.cell_shape == 'tetrahedral'

    # hand craft all attribute data
    tetra.crs_uuid = model.uuid(obj_type='LocalDepth3dCrs')
    assert tetra.crs_uuid is not None
    assert bu.matching_uuids(tetra.crs_uuid, crs.uuid)
    tetra.set_cell_count(5)
    # faces
    tetra.face_count = 16
    tetra.faces_per_cell_cl = np.arange(4, 4 * 5 + 1, 4, dtype=int)
    tetra.faces_per_cell = np.empty(20, dtype=int)
    tetra.faces_per_cell[:4] = (0, 1, 2, 3)  # cell 0
    tetra.faces_per_cell[4:8] = (0, 4, 5, 6)  # cell 1
    tetra.faces_per_cell[8:12] = (1, 7, 8, 9)  # cell 2
    tetra.faces_per_cell[12:16] = (2, 10, 11, 12)  # cell 3
    tetra.faces_per_cell[16:] = (3, 13, 14, 15)  # cell 4
    # nodes
    tetra.node_count = 8
    tetra.nodes_per_face_cl = np.arange(3, 3 * 16 + 1, 3, dtype=int)
    tetra.nodes_per_face = np.empty(48, dtype=int)
    # internal faces (cell 0)
    tetra.nodes_per_face[:3] = (0, 1, 2)  # face 0
    tetra.nodes_per_face[3:6] = (0, 3, 1)  # face 1
    tetra.nodes_per_face[6:9] = (1, 3, 2)  # face 2
    tetra.nodes_per_face[9:12] = (2, 3, 0)  # face 3
    # external faces (cell 1)
    tetra.nodes_per_face[12:15] = (0, 1, 4)  # face 4
    tetra.nodes_per_face[15:18] = (1, 2, 4)  # face 5
    tetra.nodes_per_face[18:21] = (2, 0, 4)  # face 6
    # external faces (cell 2)
    tetra.nodes_per_face[21:24] = (0, 3, 5)  # face 7
    tetra.nodes_per_face[24:27] = (3, 1, 5)  # face 8
    tetra.nodes_per_face[27:30] = (1, 0, 5)  # face 9
    # external faces (cell 3)
    tetra.nodes_per_face[30:33] = (1, 3, 6)  # face 10
    tetra.nodes_per_face[33:36] = (3, 2, 6)  # face 11
    tetra.nodes_per_face[36:39] = (2, 1, 6)  # face 12
    # external faces (cell 4)
    tetra.nodes_per_face[39:42] = (2, 3, 7)  # face 10
    tetra.nodes_per_face[42:45] = (3, 0, 7)  # face 11
    tetra.nodes_per_face[45:] = (0, 2, 7)  # face 12
    # face handedness
    tetra.cell_face_is_right_handed = np.zeros(
        20, dtype=bool)  # False for all faces for external cells (1 to 4)
    tetra.cell_face_is_right_handed[:
                                    4] = True  # True for all faces of internal cell (0)
    # points
    tetra.points_cached = np.zeros((8, 3))
    # internal cell (0) points
    half_edge = 36.152
    one_over_root_two = 1.0 / maths.sqrt(2.0)
    tetra.points_cached[0] = (-half_edge, 0.0, -half_edge * one_over_root_two)
    tetra.points_cached[1] = (half_edge, 0.0, -half_edge * one_over_root_two)
    tetra.points_cached[2] = (0.0, half_edge, half_edge * one_over_root_two)
    tetra.points_cached[3] = (0.0, -half_edge, half_edge * one_over_root_two)
    # project remaining nodes outwards
    for fi, o_node in enumerate((3, 2, 0, 1)):
        fc = tetra.face_centre_point(fi)
        tetra.points_cached[4 + fi] = fc - (tetra.points_cached[o_node] - fc)

    # basic validity check
    tetra.check_tetra()

    # write arrays, create xml and store model
    tetra.write_hdf5()
    tetra.create_xml()
    model.store_epc()

    # re-open model and establish grid
    model = rq.Model(epc)
    assert model is not None
    tetra_uuid = model.uuid(obj_type='UnstructuredGridRepresentation',
                            title='star')
    assert tetra_uuid is not None
    tetra = rug.TetraGrid(model, uuid=tetra_uuid)
    assert tetra is not None
    # perform basic checks
    assert tetra.cell_count == 5
    assert tetra.cell_shape == 'tetrahedral'
    tetra.check_tetra()

    # test volume calculation
    expected_cell_volume = ((2.0 * half_edge)**3) / (6.0 * maths.sqrt(2.0))
    for cell in range(tetra.cell_count):
        assert maths.isclose(tetra.volume(cell),
                             expected_cell_volume,
                             rel_tol=1.0e-3)
    assert maths.isclose(tetra.grid_volume(), 5.0 * expected_cell_volume)

    # test face area
    expected_area = maths.sqrt(3.0 * half_edge * (half_edge**3))
    area = tetra.area_of_face(0)
    assert maths.isclose(area, expected_area, rel_tol=1.0e-3)

    # test internal / external face lists
    assert np.all(tetra.external_face_indices() == np.arange(4, 16, dtype=int))
    inactive_mask = np.zeros(5, dtype=bool)
    assert np.all(
        tetra.external_face_indices_for_masked_cells(inactive_mask) ==
        tetra.external_face_indices())
    assert np.all(
        tetra.internal_face_indices_for_masked_cells(inactive_mask) ==
        np.arange(4, dtype=int))
    # mask out central cell
    inactive_mask[0] = True
    assert len(tetra.external_face_indices_for_masked_cells(
        inactive_mask)) == tetra.face_count
    assert len(
        tetra.internal_face_indices_for_masked_cells(inactive_mask)) == 0
Exemple #20
0
def test_hexa_grid_from_grid(example_model_with_properties):

    model = example_model_with_properties

    ijk_grid_uuid = model.uuid(obj_type='IjkGridRepresentation')
    assert ijk_grid_uuid is not None

    # create an unstructured grid with hexahedral cells from an unsplit IJK grid (includes write_hdf5 and create_xml)
    hexa = rug.HexaGrid.from_unsplit_grid(model,
                                          ijk_grid_uuid,
                                          inherit_properties=True,
                                          title='HEXA')

    hexa_uuid = hexa.uuid

    epc = model.epc_file
    assert epc

    model.store_epc()

    # re-open model and check hexa grid

    model = rq.Model(epc)

    unstructured_uuids = model.uuids(obj_type='UnstructuredGridRepresentation')

    assert unstructured_uuids is not None and len(unstructured_uuids) == 1

    hexa_grid = grr.any_grid(model, uuid=unstructured_uuids[0])
    assert isinstance(hexa_grid, rug.HexaGrid)

    assert hexa_grid.cell_shape == 'hexahedral'

    hexa_grid.check_indices()
    hexa_grid.check_hexahedral()

    # instantiate ijk grid and compare hexa grid with it
    ijk_grid = grr.any_grid(model, uuid=ijk_grid_uuid)
    assert ijk_grid is not None
    assert not np.any(np.isnan(ijk_grid.points_ref()))

    assert hexa_grid.cell_count == ijk_grid.cell_count()
    assert hexa_grid.active_cell_count() == hexa_grid.cell_count
    assert hexa_grid.node_count == (ijk_grid.nk + 1) * (ijk_grid.nj +
                                                        1) * (ijk_grid.ni + 1)
    assert hexa_grid.face_count == (
        (ijk_grid.nk + 1) * ijk_grid.nj * ijk_grid.ni + ijk_grid.nk *
        (ijk_grid.nj + 1) * ijk_grid.ni + ijk_grid.nk * ijk_grid.nj *
        (ijk_grid.ni + 1))

    assert bu.matching_uuids(hexa_grid.extract_crs_uuid(), ijk_grid.crs_uuid)
    assert hexa_grid.crs_is_right_handed == rqc.Crs(
        model, uuid=ijk_grid.crs_uuid).is_right_handed_xyz()

    # points arrays should be identical for the two grids
    assert not np.any(np.isnan(hexa_grid.points_ref()))
    assert_array_almost_equal(
        hexa_grid.points_ref(),
        ijk_grid.points_ref(masked=False).reshape((-1, 3)))

    # compare centre points of cells (not sure if these would be coincident for irregular shaped cells)
    # note that the centre_point() method exercises several other methods
    hexa_centres = hexa_grid.centre_point()
    ijk_centres = ijk_grid.centre_point()
    assert_array_almost_equal(hexa_centres,
                              ijk_centres.reshape((-1, 3)),
                              decimal=3)

    # check that face and node indices are in range
    hexa_grid.cache_all_geometry_arrays()
    assert np.all(0 <= hexa_grid.faces_per_cell)
    assert np.all(hexa_grid.faces_per_cell < hexa_grid.face_count)
    assert np.all(0 <= hexa_grid.nodes_per_face)
    assert np.all(hexa_grid.nodes_per_face < hexa_grid.node_count)
    assert len(hexa_grid.faces_per_cell_cl) == hexa_grid.cell_count
    assert len(hexa_grid.nodes_per_face_cl) == hexa_grid.face_count

    # check distinct nodes for first cell
    cell_nodes = hexa_grid.distinct_node_indices_for_cell(
        0)  # is sorted by node index
    assert len(cell_nodes) == 8
    ni1_nj1 = (ijk_grid.ni + 1) * (ijk_grid.nj + 1)
    expected_nodes = np.array(
        (0, 1, ijk_grid.ni + 1, ijk_grid.ni + 2, ni1_nj1, ni1_nj1 + 1,
         ni1_nj1 + ijk_grid.ni + 1, ni1_nj1 + ijk_grid.ni + 2),
        dtype=int)
    assert np.all(cell_nodes == expected_nodes)

    # check that some simple convenience methods work okay
    assert hexa_grid.face_count_for_cell(0) == 6
    assert hexa_grid.max_face_count_for_any_cell() == 6
    assert hexa_grid.max_node_count_for_any_face() == 4

    # check that correct number of edges is found for a face
    edges = hexa_grid.edges_for_face(hexa_grid.face_count //
                                     2)  # arbitrary face in middle
    assert edges.shape == (4, 2)
    edges = hexa_grid.edges_for_face_with_node_indices_ordered_within_pairs(
        hexa_grid.face_count // 2)
    assert edges.shape == (4, 2)
    for a, b in edges:  # check node within pair ordering
        assert a < b

    # compare corner points for first cell with those for ijk grid cell
    cp = hexa_grid.corner_points(0)
    assert cp.shape == (8, 3)
    assert_array_almost_equal(
        cp.reshape((2, 2, 2, 3)),
        ijk_grid.corner_points(cell_kji0=(0, 0, 0), cache_resqml_array=False))

    # have a look at handedness of cell faces
    assert len(hexa_grid.cell_face_is_right_handed) == 6 * hexa_grid.cell_count
    # following assertion only applies to HexaGrid built from_unsplit_grid()
    assert np.count_nonzero(
        hexa_grid.cell_face_is_right_handed
    ) == 3 * hexa_grid.cell_count  # half are right handed

    # compare cell volumes for first cell
    hexa_vol = hexa_grid.volume(0)
    ijk_vol = ijk_grid.volume(cell_kji0=0,
                              cache_resqml_array=False,
                              cache_volume_array=False)
    assert maths.isclose(hexa_vol, ijk_vol)
    assert maths.isclose(hexa_vol, 1.0, rel_tol=1.0e-3)

    # check face normal for first face (K- face of first cell)
    assert_array_almost_equal(hexa_grid.face_normal(0), (0.0, 0.0, -1.0))

    # check that planar approximation of last face is about the same as the original
    fi = hexa_grid.face_count - 1
    assert_array_almost_equal(
        hexa_grid.planar_face_points(fi),
        hexa_grid.points_cached[hexa_grid.node_indices_for_face(fi)],
        decimal=3)

    # construct a Delauney triangulation of the last face
    triangulated = hexa_grid.face_triangulation(fi, local_nodes=True)
    assert triangulated.shape == (2, 3)
    assert np.all(triangulated.flatten() < 4)
    assert np.all(np.unique(triangulated) == (0, 1, 2, 3))
    # also test the triangulation using the global node indices, for all the faces of the last cell
    for face_index in hexa_grid.face_indices_for_cell(hexa_grid.cell_count -
                                                      1):
        triangulated = hexa_grid.face_triangulation(face_index,
                                                    local_nodes=False)
        assert triangulated.shape == (2, 3)

    # check the area of a middle face (the example model has unit area faces)
    assert maths.isclose(hexa_grid.area_of_face(hexa_grid.face_count // 2),
                         1.0,
                         rel_tol=1.0e-3)

    # compare properties
    ijk_pc = ijk_grid.extract_property_collection()
    hexa_pc = hexa_grid.extract_property_collection()
    assert ijk_pc is not None and hexa_pc is not None
    assert ijk_pc.number_of_parts() <= hexa_pc.number_of_parts(
    )  # hexa grid has extra active array in this test
    for ijk_part in ijk_pc.parts():
        p_title = ijk_pc.citation_title_for_part(ijk_part)
        hexa_part = hexa_pc.singleton(citation_title=p_title)
        assert hexa_part is not None
        assert hexa_part != ijk_part  # properties are different objects with distinct uuids
        assert hexa_pc.continuous_for_part(
            hexa_part) == ijk_pc.continuous_for_part(ijk_part)
        ijk_array = ijk_pc.cached_part_array_ref(ijk_part)
        hexa_array = hexa_pc.cached_part_array_ref(hexa_part)
        assert ijk_array is not None and hexa_array is not None
        if hexa_pc.continuous_for_part(hexa_part):
            assert_array_almost_equal(hexa_array.flatten(),
                                      ijk_array.flatten())
        else:
            assert np.all(hexa_array.flatten() == ijk_array.flatten())

    # test TetraGrid.from_unstructured_cell is as expected for a hexahedral cell
    tetra = rug.TetraGrid.from_unstructured_cell(hexa_grid,
                                                 hexa_grid.cell_count // 2)
    tetra.check_indices()
    tetra.check_tetra()
    assert tetra.cell_count == 12  # 2 tetrahedra per hexa face
    assert tetra.node_count == 9  # 8 nodes of hexa cell plus centre point
    assert tetra.face_count == 6 * 2 + 12 + 6
    # check total volume of tetra grid version of cell
    assert maths.isclose(tetra.grid_volume(), 1.0, rel_tol=1.0e-3)
Exemple #21
0
def add_wells_from_ascii_file(epc_file,
                              crs_uuid,
                              trajectory_file,
                              comment_character='#',
                              space_separated_instead_of_csv=False,
                              well_col='WELL',
                              md_col='MD',
                              x_col='X',
                              y_col='Y',
                              z_col='Z',
                              length_uom='m',
                              md_domain=None,
                              drilled=False,
                              z_inc_down=True,
                              new_epc_file=None):
    """Adds new md datum, trajectory, interpretation and feature objects for each well in a tabular ascii file..

    arguments:
       epc_file (string): file name to load model resqml model from (and rewrite to if new_epc_file is None)
       crs_uuid (uuid.UUID): the unique identifier of the coordinate reference system applicable to the x,y,z data;
          if None, a default crs will be created, making use of the length_uom and z_inc_down arguments
       trajectory_file (string): the path of the ascii file holding the well trajectory data to be loaded
       comment_character (string, default '#'): character deemed to introduce a comment in the trajectory file
       space_separated_instead_of_csv (boolean, default False): if True, the columns in the trajectory file are space
          separated; if False, comma separated
       well_col (string, default 'WELL'): the heading for the column containing well names
       md_col (string, default 'MD'): the heading for the column containing measured depths
       x_col (string, default 'X'): the heading for the column containing X (usually easting) data
       y_col (string, default 'Y'): the heading for the column containing Y (usually northing) data
       z_col (string, default 'Z'): the heading for the column containing Z (depth or elevation) data
       length_uom (string, default 'm'): the units of measure for the measured depths; should be 'm' or 'ft'
       md_domain (string, optional): the source of the original deviation data; may be 'logger' or 'driller'
       drilled (boolean, default False): True should be used for wells that have been drilled; False otherwise (planned,
          proposed, or a location being studied)
       z_inc_down (boolean, default True): indicates whether z values increase with depth; only used in the creation
          of a default coordinate reference system; ignored if crs_uuid is not None
       new_epc_file (string, optional): if None, the source epc_file is extended with the new property object; if present,
          a new epc file (& associated h5 file) is created to contain a copy of the grid and the new property

    returns:
       int: the number of wells added

    notes:
       ascii file must be table with first line being column headers, with columns for WELL, MD, X, Y & Z;
       actual column names can be set with optional arguments;
       all the objects are added to the model, with array data being written to the hdf5 file for the trajectories;
       the md_domain and drilled values are stored in the RESQML metadata but are only for human information and do not
       generally affect computations
    """

    assert trajectory_file and os.path.exists(trajectory_file)
    if md_domain:
        assert md_domain in ['driller', 'logger']

    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

    # open up model
    if new_epc_file:
        model = rq.Model(new_epc_file, copy_from=epc_file)
    else:
        model = rq.Model(epc_file)

    # sort out the coordinate reference system
    if crs_uuid is None:
        crs_uuid = model.crs_uuid
    if crs_uuid is None:
        if z_inc_down is None:
            z_inc_down = True
        crs = rqc.Crs(model,
                      xy_units=length_uom,
                      z_units=length_uom,
                      z_inc_down=z_inc_down)
        crs.create_xml()
        crs_uuid = crs.uuid

    # add all the well related objects to the model, based on data in the ascii file
    (feature_list, interpretation_list, trajectory_list, md_datum_list) =  \
       rqw.add_wells_from_ascii_file(model, crs_uuid, trajectory_file, comment_character = comment_character,
                                     space_separated_instead_of_csv = space_separated_instead_of_csv,
                                     well_col = well_col, md_col = md_col, x_col = x_col, y_col = y_col, z_col = z_col,
                                     length_uom = length_uom, md_domain = md_domain, drilled = drilled)

    assert len(feature_list) == len(interpretation_list) == len(
        trajectory_list) == len(md_datum_list)
    count = len(feature_list)

    log.info('features, interpretations, trajectories and md data added for ' +
             str(count) + ' well' + _pl(count))

    # write or re-write model
    model.h5_release()
    model.store_epc()

    return count
Exemple #22
0
def test_crs(tmp_path):
    # create some coordinate reference systems
    model = rq.new_model(os.path.join(tmp_path, 'crs_test.epc'))
    crs_default = rqc.Crs(model)
    assert crs_default.null_transform
    crs_m = rqc.Crs(model, xy_units = 'm', z_units = 'm')
    crs_ft = rqc.Crs(model, xy_units = 'ft', z_units = 'ft')
    crs_mixed = rqc.Crs(model, xy_units = 'm', z_units = 'ft')
    crs_offset = rqc.Crs(model, xy_units = 'm', z_units = 'm', x_offset = 100.0, y_offset = -100.0, z_offset = -50.0)
    assert not crs_offset.null_transform
    crs_elevation = rqc.Crs(model, z_inc_down = False)
    crs_rotate = rqc.Crs(model, rotation = maths.pi / 2.0, rotation_units = 'rad')
    crs_south = rqc.Crs(model, axis_order = 'southing westing')
    crs_time_s = rqc.Crs(model, xy_units = 'm', time_units = 's')
    crs_time_ms = rqc.Crs(model, xy_units = 'm', time_units = 'ms')
    for crs_time in [crs_time_s, crs_time_ms]:
        assert crs_time.resqml_type == 'LocalTime3dCrs'

    # check that distincitveness is recognised
    assert crs_default.is_equivalent(crs_m)
    assert not crs_m.is_equivalent(crs_ft)
    assert not crs_mixed.is_equivalent(crs_m)
    assert not crs_m.is_equivalent(crs_offset)
    assert not crs_m.is_equivalent(crs_elevation)
    assert not crs_m.is_equivalent(crs_rotate)
    assert not crs_m.is_equivalent(crs_south)
    assert not crs_time_s.is_equivalent(crs_time_ms)
    for depth_crs in [crs_default, crs_m, crs_ft, crs_mixed, crs_offset, crs_elevation, crs_rotate, crs_south]:
        assert depth_crs.resqml_type == 'LocalDepth3dCrs'
        assert not crs_time_s == depth_crs
        assert not crs_time_ms == depth_crs

    # check handedness
    assert not crs_m.is_right_handed_xy()
    assert not crs_m.is_right_handed_xyz()
    assert not crs_elevation.is_right_handed_xy()
    assert crs_elevation.is_right_handed_xyz()
    assert crs_south.is_right_handed_xy()
    assert crs_south.is_right_handed_xyz()

    # create some xml
    for crs in [
            crs_default, crs_m, crs_ft, crs_mixed, crs_offset, crs_elevation, crs_rotate, crs_south, crs_time_s,
            crs_time_ms
    ]:
        crs.create_xml()
    model.store_epc()
    # check re-use of equivalent crs'es
    assert bu.matching_uuids(crs_default.uuid, crs_m.uuid)

    # test conversion
    ft_to_m = 0.3048

    a = np.empty((10, 3))
    a[:, 0] = np.random.random(10) * 5.0e5
    a[:, 1] = np.random.random(10) * 10.0e5
    a[:, 2] = np.random.random(10) * 4.0e3

    b = a.copy()
    crs_m.convert_array_from(crs_default, a)
    assert np.max(np.abs(b - a)) < 1.0e-6
    a[:] = b
    crs_m.convert_array_to(crs_m, a)
    assert np.all(a == b)
    crs_ft.convert_array_from(crs_m, a)
    assert np.max(np.abs(b - a * ft_to_m)) < 1.0e-6
    crs_ft.convert_array_to(crs_m, a)
    assert np.max(np.abs(b - a)) < 1.0e-6
    a[:] = b
    crs_m.local_to_global_array(a)
    assert np.max(np.abs(b - a)) < 1.0e-6
    a[:] = b
    crs_offset.global_to_local_array(a)
    a[:, 0] += 100.0
    a[:, 1] -= 100.0
    a[:, 2] -= 50.0
    assert_array_almost_equal(a, b)

    # test single point conversion
    p = (456.78, 678.90, -1234.56)
    assert_array_almost_equal(p, crs_offset.global_to_local(crs_offset.local_to_global(p)))
    p_ft = crs_m.convert_to(crs_ft, np.array(p))
    assert_array_almost_equal(p, crs_m.convert_from(crs_ft, p_ft))

    #  test time conversion
    pt = (123456.0, 234567.0, 1983.0)
    pt_s = np.array(crs_time_ms.convert_to(crs_time_s, pt))
    pt_s[2] *= 1000.0  # convert from seconds back to milliseconds
    assert_array_almost_equal(pt, pt_s)

    # todo: test rotation
    p = (0.00, 234.00, 5678.90)
    pr = crs_rotate.local_to_global(p)
    assert_array_almost_equal(pr, (234.00, 0.00, 5678.90))
    assert_array_almost_equal(crs_rotate.global_to_local(pr), p)
Exemple #23
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
Exemple #24
0
    def equivalent_uuid_for_part(self,
                                 part,
                                 immigrant_model=None,
                                 ignore_identical_part=False):
        """Returns uuid of an equivalent part in resident model, or None if no equivalent found."""

        #      log.debug('Looking for equivalent uuid for: ' + str(part))
        if not part:
            return None
        if immigrant_model is None:
            immigrant_model = self.model
        immigrant_uuid = rqet.uuid_in_part_name(part)
        #      log.debug('   immigrant uuid: ' + str(immigrant_uuid))
        if immigrant_uuid in self.map:
            #         log.debug('   known to be equivalent to: ' + str(self.map[immigrant_uuid]))
            return self.map[immigrant_uuid]
        obj_type = immigrant_model.type_of_part(part, strip_obj=True)
        if obj_type is None or obj_type not in consolidatable_list:
            return None
        #      log.debug('   object type is consolidatable')
        resident_uuids = self.model.uuids(obj_type=obj_type)
        if resident_uuids is None or len(resident_uuids) == 0:
            #         log.debug('   no resident parts found of type: ' + str(obj_type))
            return None
#      log.debug(f'   {len(resident_uuids)} resident parts of same class')
        if not ignore_identical_part:
            for resident_uuid in resident_uuids:
                if bu.matching_uuids(resident_uuid, immigrant_uuid):
                    #               log.debug('   uuid already resident: ' + str(resident_uuid))
                    return resident_uuid


#      log.debug('   preparing immigrant object')
        if obj_type.endswith('Interpretation') or obj_type.endswith('Feature'):
            immigrant_obj = rqo.__dict__[obj_type](immigrant_model,
                                                   uuid=immigrant_uuid)
        elif obj_type.endswith('Crs'):
            immigrant_obj = rqc.Crs(immigrant_model, uuid=immigrant_uuid)
        elif obj_type == 'TimeSeries':
            immigrant_obj = rqt.TimeSeries(immigrant_model,
                                           uuid=immigrant_uuid)
        elif obj_type == 'StringTableLookup':
            immigrant_obj = rqp.StringLookup(immigrant_model,
                                             uuid=immigrant_uuid)
        elif obj_type == 'PropertyKind':
            immigrant_obj = rqp.PropertyKind(immigrant_model,
                                             uuid=immigrant_uuid)
        else:
            raise Exception('code failure')
        assert immigrant_obj is not None
        for resident_uuid in resident_uuids:
            #         log.debug('   considering resident: ' + str(resident_uuid))
            if ignore_identical_part and bu.matching_uuids(
                    resident_uuid, immigrant_uuid):
                continue
            if obj_type.endswith('Interpretation') or obj_type.endswith(
                    'Feature'):
                resident_obj = rqo.__dict__[obj_type](self.model,
                                                      uuid=resident_uuid)
            elif obj_type.endswith('Crs'):
                resident_obj = rqc.Crs(self.model, uuid=resident_uuid)
            elif obj_type == 'TimeSeries':
                resident_obj = rqt.TimeSeries(self.model, uuid=resident_uuid)
            elif obj_type == 'StringTableLookup':
                resident_obj = rqp.StringLookup(self.model, uuid=resident_uuid)
            elif obj_type == 'PropertyKind':
                resident_obj = rqp.PropertyKind(self.model, uuid=resident_uuid)
            else:
                raise Exception('code failure')
            assert resident_obj is not None
            #         log.debug('   comparing with: ' + str(resident_obj.uuid))
            if immigrant_obj == resident_obj:  # note: == operator overloaded with equivalence method for these classes
                while resident_uuid in self.map:
                    #               log.debug('   following equivalence for: ' + str(resident_uuid))
                    resident_uuid = self.map[resident_uuid]
                self.map[immigrant_uuid] = resident_uuid
                #            log.debug('   new equivalence found with: ' + str(resident_uuid))
                return resident_uuid
        return None
Exemple #25
0
def local_depth_adjustment(epc_file,
                           source_grid,
                           centre_x,
                           centre_y,
                           radius,
                           centre_shift,
                           use_local_coords,
                           decay_shape = 'quadratic',
                           ref_k0 = 0,
                           store_displacement = False,
                           inherit_properties = False,
                           inherit_realization = None,
                           inherit_all_realizations = False,
                           new_grid_title = None,
                           new_epc_file = None):
    """Applies a local depth adjustment to the grid, adding as a new grid part in the model.

    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): a multi-layer RESQML grid object; 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
       centre_x, centre_y (floats): the centre of the depth adjustment, corresponding to the location of maximum change
          in depth; crs is implicitly that of the grid but see also use_local_coords argument
       radius (float): the radius of adjustment of depths; units are implicitly xy (projected) units of grid crs
       centre_shift (float): the maximum vertical depth adjustment; units are implicily z (vertical) units of grid crs;
          use positive value to increase depth, negative to make shallower
       use_local_coords (boolean): if True, centre_x & centre_y are taken to be in the local coordinates of the grid's
          crs; otherwise the global coordinates
       decay_shape (string): 'linear' yields a cone shaped change in depth values; 'quadratic' (the default) yields a
          bell shaped change
       ref_k0 (integer, default 0): the layer in the grid to use as reference for determining the distance of a pillar
          from the centre of the depth adjustment; the corners of the top face of the reference layer are used
       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 local depth shift
       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
       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 adjusted grid (& crs)

    returns:
       new grid object which is a copy of the source grid with the local depth adjustment applied
    """

    log.info('adjusting depth')
    log.debug('centre x: {0:3.1f}; y: {1:3.1f}'.format(centre_x, centre_y))
    if use_local_coords:
        log.debug('centre x & y interpreted in local crs')
    log.debug('radius of influence: {0:3.1f}'.format(radius))
    log.debug('depth shift at centre: {0:5.3f}'.format(centre_shift))
    log.debug('decay shape: ' + decay_shape)
    log.debug('reference layer (k0 protocol): ' + str(ref_k0))

    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

    # take a copy of the grid
    grid = copy_grid(source_grid, model, copy_crs = True)

    # if not use_local_coords, convert centre_x & y into local_coords
    if grid.crs is None:
        grid.crs = rqc.Crs(model, uuid = grid.crs_uuid)
    if not use_local_coords:
        rotation = grid.crs.rotation
        if rotation > 0.001:
            log.error('unable to account for rotation in crs: use local coordinates')
            return
        centre_x -= grid.crs.x_offset
        centre_y -= grid.crs.y_offset
    z_inc_down = grid.crs.z_inc_down

    if not z_inc_down:
        centre_shift = -centre_shift

    # cache geometry in memory; needed prior to writing new coherent set of arrays to hdf5
    grid.cache_all_geometry_arrays()
    if grid.has_split_coordinate_lines:
        reshaped_points = grid.points_cached.copy()
    else:
        nkp1, njp1, nip1, xyz = grid.points_cached.shape
        reshaped_points = grid.points_cached.copy().reshape((nkp1, njp1 * nip1, xyz))
    assert reshaped_points.ndim == 3 and reshaped_points.shape[2] == 3
    assert ref_k0 >= 0 and ref_k0 < reshaped_points.shape[0]

    log.debug('reshaped_points.shape: ' + str(reshaped_points.shape))

    log.debug('min z before depth adjustment: ' + str(np.nanmin(reshaped_points[:, :, 2])))

    # for each pillar, find x, y for k = reference_layer_k0
    pillars_adjusted = 0

    # todo: replace with numpy array operations
    radius_sqr = radius * radius
    for pillar in range(reshaped_points.shape[1]):
        x, y, z = tuple(reshaped_points[ref_k0, pillar, :])
        # find distance of this pillar from the centre
        dx = centre_x - x
        dy = centre_y - y
        distance_sqr = (dx * dx) + (dy * dy)
        # if this pillar is beyond radius of influence, no action needed
        if distance_sqr > radius_sqr:
            continue
        distance = maths.sqrt(distance_sqr)
        # compute decayed shift as function of distance
        shift = _decayed_shift(centre_shift, distance, radius, decay_shape)
        # adjust depth values for pillar in cached array
        log.debug('adjusting pillar number {0} at x: {1:3.1f}, y: {2:3.1f}, distance: {3:3.1f} by {4:5.3f}'.format(
            pillar, x, y, distance, shift))
        reshaped_points[:, pillar, 2] += shift
        pillars_adjusted += 1

    # if no pillars adjusted: warn and return
    if pillars_adjusted == 0:
        log.warning('no pillars adjusted')
        return

    log.debug('min z after depth adjustment: ' + str(np.nanmin(reshaped_points[:, :, 2])))
    if grid.has_split_coordinate_lines:
        grid.points_cached[:] = reshaped_points
    else:
        grid.points_cached[:] = reshaped_points.reshape((nkp1, njp1, nip1, xyz))


#   model.copy_part(old_uuid, grid.uuid, change_hdf5_refs = True)   # copies the xml, substituting the new uuid in the root node (and in hdf5 refs)
    log.info(str(pillars_adjusted) + ' pillars adjusted')

    # 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 derived from {0} with local depth shift of {1:3.1f} applied'.format(
            str(rqet.citation_title_for_node(source_grid.root)), centre_shift)

    # 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')
    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
Exemple #26
0
    def __init__(self,
                 parent_model,
                 uuid=None,
                 extent_kji=None,
                 dxyz=None,
                 dxyz_dkji=None,
                 origin=(0.0, 0.0, 0.0),
                 crs_uuid=None,
                 use_vertical=False,
                 mesh=None,
                 mesh_dz_dk=1.0,
                 set_points_cached=False,
                 as_irregular_grid=False,
                 find_properties=True,
                 title=None,
                 originator=None,
                 extra_metadata={}):
        """Creates a regular grid object based on dxyz, or derived from a Mesh object.

        arguments:
           parent_model (model.Model object): the model to which the new grid will be assigned
           uuid (optional): the uuid for an existing grid part; if present, the RegularGrid object is
              based on existing data or a mix of that data and other arguments where present
           extent_kji (triple positive integers, optional): the number of cells in the grid (nk, nj, ni);
              required unless uuid is present
           dxyz (triple float, optional): use when the I,J,K axes align with the x,y,z axes (possible with inverted
              directions); the size of each cell (dx, dy, dz); values may be negative
           dxyz_dkji (numpy float array of shape (3, 3), optional): how x,y,z values increase with each step in each
              direction K,J,I; first index is KJI, second index is xyz; only one of dxyz, dxyz_dkji and mesh should be
              present; NB axis ordering is different to that used in Mesh class for dxyz_dij
           origin (triple float, default (0.0, 0.0, 0.0)): the location in the local coordinate space of the crs of
              the 'first' corner point of the grid
           crs_uuid (uuid.UUID, optional): the uuid of the coordinate reference system for the grid
           use_vertical (boolean, default False): if True and the pillars of the regular grid are vertical then a
              pillar shape of 'vertical' is used; if False (or the pillars are not vertical), then a pillar shape of
              'straight' is used
           mesh (surface.Mesh object, optional): if present, the I,J layout of the grid is based on the mesh, which
              must be regular, and the K cell size is given by the mesh_dz_dk argument; if present, then dxyz and
              dxyz_dkji must be None
           mesh_dz_dk (float, default 1.0): the size of cells in the K axis, which is aligned with the z axis, when
              starting from a mesh; ignored if mesh is None
           set_points_cached (boolean, default False): if True, an explicit geometry is created for the regular grid
              in the form of the cached points array; will be treated as True if as_irregular_grid is True
           as_irregular_grid (boolean, default False): if True, the grid is setup such that it will appear as a Grid
              object when next loaded from disc
           find_properties (boolean, default True): if True and uuid is not None, a grid property collection is
              instantiated as an attribute, holding properties for which this grid is the supporting representation
           title (str, optional): citation title for new grid; ignored if loading from xml
           originator (str, optional): name of person creating the grid; defaults to login id;
              ignored if loading from xml
           extra_metadata (dict, optional): dictionary of extra metadata items to add to the grid;
              ignored if loading from xml

        returns:
           a newly created RegularGrid object with inheritance from the Grid class

        notes:
           the RESQML standard allows for regular grid geometry pillars to be stored as parametric lines
           but that is not yet supported by this code base; however, constant dx, dy, dz arrays are supported;
           alternatively, regular meshes (Grid2d) may be stored in parameterized form and used to generate a
           regular grid here;
           if uuid, dxyz, dxyz_dkji and mesh arguments are all None then unit cube cells aligned with
           the x,y,z axes will be generated;
           to store the geometry explicitly set as_irregular_grid True and use the following methods:
           make_regular_points_cached(), write_hdf5(), create_xml(..., write_geometry = True);
           otherwise, avoid write_hdf5() and call create_xml(..., write_geometry = False);
           if geometry is not stored explicitly, the uuid of the crs is stored as extra metadata
           if origin is not triple zero, a new crs will be created with the origin moved appropriately

        :meta common:
        """

        if as_irregular_grid:
            set_points_cached = True
            self.is_aligned = False
        else:
            self.is_aligned = None  #: boolean indicating alignment of IJK axes with +/- xyz respectively

        if uuid is None:
            assert extent_kji is not None and len(extent_kji) == 3
            super().__init__(parent_model,
                             title=title,
                             originator=originator,
                             extra_metadata=extra_metadata)
            self.grid_representation = 'IjkGrid' if as_irregular_grid else 'IjkBlockGrid'
            self.extent_kji = np.array(extent_kji).copy()
            self.nk, self.nj, self.ni = self.extent_kji
            self.k_direction_is_down = True  # assumed direction
            self.grid_is_right_handed = False  # todo: work it out from dxyz_dkji and crs xyz handedness
            self.has_split_coordinate_lines = False
            self.k_gaps = None
            self.k_gap_after_array = None
            self.k_raw_index_array = None
            self.inactive = None
            self.all_inactive = None
            self.geometry_defined_for_all_cells_cached = True
            self.geometry_defined_for_all_pillars_cached = True
            self.array_cell_geometry_is_defined = np.full(tuple(
                self.extent_kji),
                                                          True,
                                                          dtype=bool)
        else:
            assert bu.is_uuid(uuid)
            assert is_regular_grid(parent_model.root_for_uuid(uuid))
            super().__init__(parent_model,
                             uuid=uuid,
                             find_properties=find_properties,
                             geometry_required=False,
                             title=title,
                             originator=originator,
                             extra_metadata=extra_metadata)
            self.grid_representation = 'IjkBlockGrid'
            if dxyz is None and dxyz_dkji is None:
                # find cell length properties and populate dxyz from those values
                assert self.property_collection is not None
                dxi_part = self.property_collection.singleton(
                    property_kind='cell length',
                    facet_type='direction',
                    facet='I')
                dyj_part = self.property_collection.singleton(
                    property_kind='cell length',
                    facet_type='direction',
                    facet='J')
                dzk_part = self.property_collection.singleton(
                    property_kind='cell length',
                    facet_type='direction',
                    facet='K')
                if dxi_part is not None and dyj_part is not None and dzk_part is not None:
                    dxi = self.property_collection.constant_value_for_part(
                        dxi_part)
                    dyj = self.property_collection.constant_value_for_part(
                        dyj_part)
                    dzk = self.property_collection.constant_value_for_part(
                        dzk_part)
                    if dxi is not None and dyj is not None and dzk is not None:
                        dxyz = (float(dxi), float(dyj), float(dzk))

        if mesh is not None:
            assert mesh.flavour == 'regular'
            assert dxyz is None and dxyz_dkji is None
            origin = mesh.regular_origin
            dxyz_dkji = np.empty((3, 3))
            dxyz_dkji[0, :] = mesh_dz_dk
            dxyz_dkji[1, :] = mesh.regular_dxyz_dij[1]  # J axis
            dxyz_dkji[2, :] = mesh.regular_dxyz_dij[0]  # I axis
            if crs_uuid is None:
                crs_uuid = mesh.crs_uuid
            else:
                assert bu.matching_uuids(crs_uuid, mesh.crs_uuid)

        assert dxyz is None or dxyz_dkji is None
        if dxyz is None and dxyz_dkji is None:
            dxyz = (1.0, 1.0, 1.0)
        if dxyz_dkji is None:
            dxyz_dkji = np.array([[0.0, 0.0, dxyz[2]], [0.0, dxyz[1], 0.0],
                                  [dxyz[0], 0.0, 0.0]])
        self.block_origin = np.array(
            origin).copy() if uuid is None else np.zeros(3)
        self.block_dxyz_dkji = np.array(dxyz_dkji).copy()
        if self.is_aligned is None:
            self._set_is_aligned()
        if use_vertical and dxyz_dkji[0][0] == 0.0 and dxyz_dkji[0][
                1] == 0.0:  # ie. no x,y change with k
            self.pillar_shape = 'vertical'
        else:
            self.pillar_shape = 'straight'

        if set_points_cached:
            self.make_regular_points_cached()

        new_crs = None
        shift_origin = np.any(
            origin != 0.0) and uuid is None and not as_irregular_grid
        if crs_uuid is None and self.extra_metadata is not None:
            crs_uuid = bu.uuid_from_string(self.extra_metadata.get('crs uuid'))
            shift_origin = shift_origin and crs_uuid is None
        if crs_uuid is None:
            crs_uuid = parent_model.crs_uuid
        if crs_uuid is None:
            new_crs = rqc.Crs(parent_model,
                              x_offset=origin[0],
                              y_offset=origin[1],
                              z_offset=origin[2])
            shift_origin = False
            new_crs.create_xml(reuse=True)
            crs_uuid = new_crs.uuid
        if shift_origin:
            new_crs = rqc.Crs(parent_model, uuid=crs_uuid)
            new_crs.uuid = bu.new_uuid()
            new_crs.x_offset += origin[0]
            new_crs.y_offset += origin[1]
            new_crs.z_offset += origin[2]
            new_crs.create_xml(reuse=True)
            crs_uuid = new_crs.uuid
        self.crs_uuid = crs_uuid
        self.crs = rqc.Crs(parent_model,
                           uuid=crs_uuid) if new_crs is None else new_crs

        if self.uuid is None:
            self.uuid = bu.new_uuid()
Exemple #27
0
def triangulated_polygons(p, v, centres=None):
    """Returns triangulation of polygons using centres as extra points.

    arguments:
       p (2D numpy float array): points used as vertices of polygons
       v (list of list of ints): ordered indices into p for each polygon
       centres (2D numpy float array, optional): the points to use as the centre for each polygon

    returns:
       points, triangles where: points is a copy of p extended with the centre points of polygons;
       and triangles is a numpy int array of shape (N, 3) being the triangulation of points, where N is
       equal to the overall length of v

    notes:
       if no centres are provided, balanced centre points are computed for the polygons;
       the polygons must be convex (at least from the perspective of the centre points);
       the clockwise/anti-clockwise order of the triangle edges will match that of the polygon;
       the centre point is the first point in each triangle;
       the order of triangles will match the order of vertices in a flattened view of list v;
       p and centres may have a shape of 2 or 3 in the second dimension (xy or xyz data);
       p & v could be the values (c, v) returned by the voronoi() function, in which case the
       original seed points p passed into voronoi() can be passed as centres here
    """

    assert p.ndim == 2 and p.shape[1] in [2, 3]
    assert len(v) > 0
    if centres is not None:
        assert centres.ndim == 2
        assert len(centres) == len(v)
        assert centres.shape[1] == p.shape[1]

    model = rq.Model(
        create_basics=True)  # temporary object for handling Polylines
    crs = rqc.Crs(model)

    points = np.zeros(
        (len(p) + len(v),
         p.shape[1]))  # polygon nodes, to be extended with centre points
    points[:len(p)] = p
    if centres is not None:
        points[len(p):] = centres

    t_count = sum([len(x) for x in v])
    triangles = np.empty((t_count, 3), dtype=int)
    t_index = 0

    for cell, poly_vertices in enumerate(v):
        # add polygon centre points to points array, if not already provided
        centre_i = len(p) + cell
        if centres is None:
            polygon = rql.Polyline(model,
                                   set_coord=p[np.array(poly_vertices,
                                                        dtype=int)],
                                   set_bool=True,
                                   set_crs=crs.uuid,
                                   title='v cell')
            poly_centre = polygon.balanced_centre()
            points[centre_i] = poly_centre[:p.shape[1]]
        # and populate triangles for this polygon
        for ti in range(len(poly_vertices)):
            triangles[t_index] = (centre_i, poly_vertices[ti],
                                  poly_vertices[(ti + 1) % len(poly_vertices)])
            t_index += 1
    assert t_index == t_count

    return points, triangles
Exemple #28
0
def drape_to_surface(epc_file,
                     source_grid=None,
                     surface=None,
                     scaling_factor=None,
                     ref_k0=0,
                     ref_k_faces='top',
                     quad_triangles=True,
                     border=None,
                     store_displacement=False,
                     inherit_properties=False,
                     inherit_realization=None,
                     inherit_all_realizations=False,
                     new_grid_title=None,
                     new_epc_file=None):
    """Return a new grid with geometry draped to a surface.
    
    Extend a resqml model with a new grid where the reference layer boundary of the source grid has been re-draped
    to a surface.

    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
       surface (surface.Surface object, optional): the surface to drape the grid to; if None, a surface is generated from
          the reference layer boundary (which can then be scaled with the scaling_factor)
       scaling_factor (float, optional): if not None, prior to draping, the surface is stretched vertically by this factor,
          away from a horizontal plane located at the surface's shallowest depth
       ref_k0 (integer, default 0): the reference layer (zero based) to drape to the surface
       ref_k_faces (string, default 'top'): 'top' or 'base' identifying which bounding interface to use as the reference
       quad_triangles (boolean, default True): if True and surface is None, each cell face in the reference boundary layer
          is represented by 4 triangles (with a common vertex at the face centre) in the generated surface; if False,
          only 2 trianges are used for each cell face (which gives a non-unique solution)
       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): the factor to reduce depth shifts by with each cell step away from faults (used
          in conjunction with cell_range)
       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 local depth shift
       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
       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 draped grid (& crs)

    returns:
       new grid (grid.Grid object), with geometry draped to surface

    notes:
       at least one of a surface or a scaling factor must be given;
       if no surface is given, one is created from the fault-healed grid points for the reference layer interface;
       if a scaling factor other than 1.0 is given, the surface is flexed vertically, relative to its shallowest point;
       layer thicknesses measured along pillars are maintained; cell volumes may change;
       the coordinate reference systems for the surface and the grid are assumed to be the same;
       this function currently uses an exhaustive, computationally and memory intensive algorithm;
       setting quad_triangles argument to False should give a factor of 2 speed up and reduction in memory requirement;
       the epc file and associated hdf5 file are appended to (extended) with the new grid, as a side effect of this function
    """

    log.info('draping grid to surface')

    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 ref_k0 >= 0 and ref_k0 < source_grid.nk
    assert ref_k_faces in ['top', 'base']
    assert surface is not None or (scaling_factor is not None
                                   and scaling_factor != 1.0)

    if surface is None:
        surface = rgs.generate_untorn_surface_for_layer_interface(
            source_grid,
            k0=ref_k0,
            ref_k_faces=ref_k_faces,
            quad_triangles=quad_triangles,
            border=border)

    if scaling_factor is not None and scaling_factor != 1.0:
        scaled_surf = copy.deepcopy(surface)
        scaled_surf.vertical_rescale_points(scaling_factor=scaling_factor)
        surface = scaled_surf

    # check that surface and grid use same crs; if not, convert to same
    surface_crs = rqc.Crs(surface.model, surface.crs_uuid)
    if source_grid.crs is None:
        source_grid.crs = rqc.Crs(source_grid.model, uuid=source_grid.crs_uuid)
    if surface_crs != source_grid.crs:
        surface.triangles_and_points()
        surface_crs.convert_array_to(source_grid.crs, surface.points)

    # 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'
    assert grid.geometry_defined_for_all_cells(
    ), 'not all cells have defined geometry'

    # fetch unsplit equivalent of grid points
    log.debug('fetching unsplit equivalent grid points')
    unsplit_points = grid.unsplit_points_ref(cache_array=True)

    # assume pillars to be straight lines based on top and base points
    log.debug('setting up pillar sample points and directional vectors')
    line_p = unsplit_points[0, :, :, :].reshape((-1, 3))
    line_v = unsplit_points[-1, :, :, :].reshape((-1, 3)) - line_p
    if ref_k_faces == 'base':
        ref_k0 += 1

    # access triangulated surface as triangle node indices into array of points
    log.debug('fetching surface points and triangle corner indices')
    t, p = surface.triangles_and_points(
    )  # will pick up cached crs converted copy if appropriate

    # compute intersections of all pillars with all triangles (sparse array returned with NaN for no intersection)
    log.debug('computing intersections of all pillars with all triangles')
    intersects = meet.line_set_triangles_intersects(line_p, line_v, p[t])

    # reduce to a single intersection point per pillar; todo: flag multiple intersections with a warning
    log.debug(
        'selecting last intersection for each pillar (there should be only one intersection anyway)'
    )
    picks = meet.last_intersects(intersects)

    # count the number of pillars with no intersection at surface (indicated by triple nan)
    log.debug(
        'counting number of pillars which fail to intersect with surface')
    failures = np.count_nonzero(np.isnan(picks)) // 3
    log.info('number of pillars which do not intersect with surface: ' +
             str(failures))
    assert failures == 0, 'cannot proceed as some pillars do not intersect with surface'

    # compute a translation vector per pillar
    log.debug('computing translation vectors for pillars')
    translate = picks - unsplit_points[ref_k0, :, :, :].reshape((-1, 3))

    # shift all points by translation vectors
    _shift_by_translation_vectors(grid, translate)

    # 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 flexed from ' + str(
            rqet.citation_title_for_node(source_grid.root))

    # 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')
    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
Exemple #29
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
Exemple #30
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