Exemple #1
0
def _inherit_well(new_epc_file, grid, traj_model, well_name, trajectory,
                  blocked_well, column_ji0, box):
    newer_model = rq.Model(new_epc_file)
    if trajectory is None and blocked_well is None:
        log.info('creating well objects for column')
        box_column_ji0 = (column_ji0[0] - box[0, 1], column_ji0[1] - box[0, 2])
        bw = rqw.BlockedWell(newer_model,
                             grid=grid,
                             column_ji0=box_column_ji0,
                             well_name=well_name,
                             use_face_centres=True)
        bw.write_hdf5(create_for_trajectory_if_needed=True)
        bw.create_xml(create_for_trajectory_if_needed=True, title=well_name)
    elif blocked_well is not None:
        log.info('inheriting trajectory for blocked well'
                 )  # recursively copies referenced parts
        newer_model.copy_part_from_other_model(
            traj_model, traj_model.part(uuid=blocked_well.trajectory.uuid))
    else:
        log.info('inheriting well trajectory'
                 )  # recursively copies referenced parts
        newer_model.copy_part_from_other_model(
            traj_model, traj_model.part(uuid=trajectory.uuid))
    newer_model.h5_release()
    newer_model.store_epc()
Exemple #2
0
def _set_support_uuid_notnone_supportnone(collection, support_uuid, model):
    import resqpy.fault as rqf
    import resqpy.grid as grr
    import resqpy.surface as rqs
    import resqpy.unstructured as rug
    import resqpy.well as rqw

    support_part = model.part_for_uuid(support_uuid)
    assert support_part is not None, 'supporting representation part missing in model'
    collection.support_root = model.root_for_part(support_part)
    support_type = model.type_of_part(support_part)
    assert support_type is not None
    if support_type == 'obj_IjkGridRepresentation':
        collection.support = grr.any_grid(model,
                                          uuid=collection.support_uuid,
                                          find_properties=False)
    elif support_type == 'obj_WellboreFrameRepresentation':
        collection.support = rqw.WellboreFrame(model,
                                               uuid=collection.support_uuid)
    elif support_type == 'obj_BlockedWellboreRepresentation':
        collection.support = rqw.BlockedWell(model,
                                             uuid=collection.support_uuid)
    elif support_type == 'obj_Grid2dRepresentation':
        collection.support = rqs.Mesh(model, uuid=collection.support_uuid)
    elif support_type == 'obj_GridConnectionSetRepresentation':
        collection.support = rqf.GridConnectionSet(
            model, uuid=collection.support_uuid)
    elif support_type == 'obj_TriangulatedSetRepresentation':
        collection.support = rqs.Surface(model, uuid=collection.support_uuid)
    elif support_type == 'obj_UnstructuredGridRepresentation':
        collection.support = rug.UnstructuredGrid(model,
                                                  uuid=collection.support_uuid,
                                                  geometry_required=False,
                                                  find_properties=False)
    elif support_type == 'obj_WellboreMarkerFrameRepresentation':
        collection.support = rqw.WellboreMarkerFrame(
            model, uuid=collection.support_uuid)
    else:
        raise TypeError(
            'unsupported property supporting representation class: ' +
            str(support_type))
Exemple #3
0
def test_s_bend_fn(tmp_path, epc=None):
    if epc is None:
        # use pytest temporary directory fixture
        # https://docs.pytest.org/en/stable/tmpdir.html
        epc = str(os.path.join(tmp_path, f"{bu.new_uuid()}.epc"))

    # create s-bend grid

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

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

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

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

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

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

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

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

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

    grid = grr.Grid(model)

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

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

    grid.points_cached = points

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

    grid.write_hdf5_from_caches()
    grid.create_xml()

    # create a well trajectory and md datum

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

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

    df = df_trajectory(x, y, z)

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

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

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

    trajectory.write_hdf5()
    trajectory.create_xml()

    # add more wells

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

    df = df_trajectory(x, y, z)

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

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

    df = df_trajectory(x, y, z)

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

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

    df = df_trajectory(x, y, z)

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

    # block wells against grid geometry

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

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

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

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

    # derive a faulted version of the grid

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

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

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

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

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

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

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

    faulted_grid.write_hdf5_from_caches()
    faulted_grid.create_xml()

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

    # block wells against faulted grid

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

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

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

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

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

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

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

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

    k_gap_grid.write_hdf5_from_caches()
    k_gap_grid.create_xml()

    k_gap_grid_uuid = k_gap_grid.uuid

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

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

    # block wells against faulted grid with k gap

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

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

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

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

    # store model

    model.store_epc()

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

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

    # clean up
    model.h5_release()
    os.remove(model.h5_file_name())
Exemple #4
0
def extract_box_for_well(epc_file=None,
                         source_grid=None,
                         min_k0=None,
                         max_k0=None,
                         trajectory_epc=None,
                         trajectory_uuid=None,
                         blocked_well_uuid=None,
                         column_ji0=None,
                         column_xy=None,
                         well_name=None,
                         radius=None,
                         outer_radius=None,
                         active_cells_shape='tube',
                         quad_triangles=True,
                         inherit_properties=False,
                         inherit_realization=None,
                         inherit_all_realizations=False,
                         inherit_well=False,
                         set_parent_window=None,
                         new_grid_title=None,
                         new_epc_file=None):
    """Extends an existing model with a new grid extracted as an IJK box around a well trajectory in the source grid.

    arguments:
       epc_file (string): file name to rewrite the model's xml to; if source grid is None, model is loaded from this file
       source_grid (grid.Grid object, optional): if None, the epc_file is loaded and it should contain one ijk grid object
          (or one 'ROOT' grid) which is used as the source grid
       min_k0, max_k0 (integers, optional): layer range to include; default is full vertical range of source grid
       trajectory_epc (string, optional): the source file for the trajectory or blocked well, if different to that for
          the source grid
       trajectory_uuid (uuid.UUID): the uuid of the trajectory object for the well, if working from a trajectory
       blocked_well_uuid (uuid.UUID): the uuid of the blocked well object, an alternative to working from a trajectory;
          must include blocking against source_grid
       column_ji0 (integer pair, optional): an alternative to providing a trajectory: the column indices of a 'vertical' well
       column_xy (float pair, optional): an alternative to column_ji0: the x, y location used to determine the column
       well_name (string, optional): name to use for column well, ignored if trajectory_uuid is not None
       radius (float, optional): the radius around the wellbore to include in the box; units are those of grid xy values;
          radial distances are applied horizontally regardless of well inclination; if not present, only cells penetrated
          by the trajectory are included
       outer_radius (float, optional): an outer radius around the wellbore, beyond which an inactive cell mask for the
          source_grid will be set to True (inactive); units are those of grid xy values
       active_cells_shape (string, default 'tube'): the logical shape of cells marked as active in the extracted box;
          'tube' results in an active shape with circular cross section in IJ planes, that follows the trajectory; 'prism'
          activates all cells in IJ columns where any cell is within the tube; 'box' leaves the entire IJK cuboid active
       quad_triangles (boolean, default True): if True, cell K faces are treated as 4 triangles (with a common face
          centre point) when computing the intersection of the trajectory with layer interfaces (horizons); if False,
          the K faces are treated as 2 triangles
       inherit_properties (boolean, default False): if True, the new grid will have a copy of any properties associated
          with the source grid, with values taken from the extracted box
       inherit_realization (int, optional): realization number for which properties will be inherited; ignored if
          inherit_properties is False
       inherit_all_realizations (boolean, default False): if True (and inherit_realization is None), properties for all
          realizations will be inherited; if False, only properties with a realization of None are inherited; ignored if
          inherit_properties is False or inherit_realization is not None
       inherit_well (boolean, default False): if True, the new model will have a copy of the well trajectory, its crs (if
          different from that of the grid), and any related wellbore interpretation and feature
       set_parent_window (boolean, optional): if True, the extracted grid has its parent window attribute set; if False,
          the parent window is not set; if None, the default will be True if new_epc_file is None or False otherwise
       new_grid_title (string): used as the citation title text for the new grid object
       new_epc_file (string, optional): if None, the source epc_file is extended with the new grid object; if present,
          a new epc file (& associated h5 file) is created to contain the extracted grid (& crs)

    returns:
       (grid, box) where: grid is the new Grid object with extent as determined by source grid geometry, trajectory and
       radius arguments; and box is a numpy int array of shape (2, 3) with first axis covering min, max and second axis
       covering k,j,i; the box array holds the minimum and maximum indices (zero based) in the source grid that have
       been included in the extraction (nb. maximum indices are included, unlike the usual python protocol)

    notes:
       this function is designed to work fully for vertical and deviated wells; for horizontal wells use blocked well mode;
       the extracted box includes all layers between the specified min and max horizons, even if the trajectory terminates
       above the deeper horizon or does not intersect horizon(s) for other reasons;
       when specifying a column well by providing x,y the IJ column with the centre of the topmost k face closest to the
       given point is selected;
       if an outer_radius is given, a boolean property will be created for the source grid with values set True where the
       centres of the cells are beyond this distance from the well, measured horizontally; if outer_radius and new_epc_file
       are both given, the source grid will be copied to the new epc
    """

    # establish model, source grid, and trajectory model
    new_epc_file, trajectory_epc, model, traj_model, source_grid =  \
        _establish_files_and_models(epc_file, new_epc_file, trajectory_epc, source_grid)

    # ensure geometry exists as points in case of a regular (block) grid
    if source_grid.grid_representation == 'IjkBlockGrid':
        source_grid.make_regular_points_cached()

    # check arguments
    if min_k0 is None:
        min_k0 = 0
    if max_k0 is None:
        max_k0 = source_grid.nk - 1
    assert 0 <= min_k0 <= max_k0 < source_grid.nk
    assert trajectory_uuid is not None or blocked_well_uuid is not None or column_ji0 is not None
    if radius is None:
        radius = 0.0  # cells directly penetrated through a k face will still be included
    assert radius >= 0.0
    if outer_radius is not None:
        assert outer_radius >= radius
    assert active_cells_shape in ['tube', 'prism', 'box']

    # prepare cell centre points for testing inclusion
    centres = source_grid.centre_point(cache_centre_array=True)

    # initialise local variables
    box = None
    trajectory = None
    blocked_well = None
    bw_cells = None
    bw_box = None

    # establish well information, dependent on type of well object
    if trajectory_uuid is not None:
        # prepare a trajectory object
        trajectory_root = traj_model.root(
            obj_type='WellboreTrajectoryRepresentation', uuid=trajectory_uuid)
        assert trajectory_root is not None, 'trajectory object not found for uuid: ' + str(
            trajectory_uuid)
        trajectory = rqw.Trajectory(traj_model, uuid=trajectory_uuid)
        well_name = rqw.well_name(trajectory)
        traj_crs = rqc.Crs(trajectory.model, uuid=trajectory.crs_uuid)
        grid_crs = rqc.Crs(source_grid.model, uuid=source_grid.crs_uuid)
        # modify in-memory trajectory data to be in the same crs as grid
        traj_crs.convert_array_to(
            grid_crs, trajectory.control_points
        )  # trajectory xyz points converted in situ to grid's crs
        trajectory.crs_uuid = source_grid.crs_uuid  # note: tangent vectors might be messed up, if present
        traj_box = np.empty((2, 3))
        traj_box[0] = np.amin(trajectory.control_points, axis=0)
        traj_box[1] = np.amax(trajectory.control_points, axis=0)
        grid_box = source_grid.xyz_box(lazy=False)
        if not bx.boxes_overlap(traj_box, grid_box):
            log.error(
                'no overlap of xyz boxes for trajectory and grid for trajectory uuid: '
                + str(trajectory.uuid))
            return None, None
    elif blocked_well_uuid is not None:
        bw_root = traj_model.root(obj_type='BlockedWellboreRepresentation',
                                  uuid=blocked_well_uuid)
        assert bw_root is not None, 'blocked well object not found for uuid: ' + str(
            blocked_well_uuid)
        blocked_well = rqw.BlockedWell(traj_model, uuid=blocked_well_uuid)
        well_name = rqw.well_name(blocked_well)
        bw_box = blocked_well.box(grid_uuid=source_grid.uuid)
        assert bw_box is not None, 'blocked well does not include cells in source grid'
        assert bw_box[0, 0] <= max_k0 and bw_box[1, 0] >= min_k0,  \
            'blocked well does not include cells in specified layer range'
        bw_cells = blocked_well.cell_indices_for_grid_uuid(source_grid.uuid)
    else:
        column_ji0, well_name = _check_column(source_grid, column_ji0,
                                              column_xy, well_name)

    # create cell mask
    inclusion_mask, outer_inactive_mask = _build_cell_masks(
        source_grid, centres, min_k0, max_k0, trajectory, blocked_well,
        bw_cells, column_ji0, quad_triangles, radius, outer_radius)

    # derive box from inclusion mask
    box = _box_from_inclusion_mask(source_grid, inclusion_mask, min_k0, max_k0)

    # prepare inactive mask to merge in for new grid
    box_inactive = _make_inactive_mask(active_cells_shape, inclusion_mask, box)

    # establish title for the new grid
    new_grid_title = _invent_title(new_grid_title, trajectory, blocked_well,
                                   column_ji0)

    # perform the main grid extraction
    grid = extract_box(epc_file,
                       source_grid=source_grid,
                       box=box,
                       box_inactive=box_inactive,
                       inherit_properties=inherit_properties,
                       inherit_realization=inherit_realization,
                       inherit_all_realizations=inherit_all_realizations,
                       set_parent_window=set_parent_window,
                       new_grid_title=new_grid_title,
                       new_epc_file=new_epc_file)

    # inherit well if requested
    if inherit_well and new_epc_file:
        _inherit_well(new_epc_file, grid, traj_model, well_name, trajectory,
                      blocked_well, column_ji0, box)

    # add mask property for outer radius, if specified
    if outer_radius is not None:
        _add_outer_mask_property(epc_file, new_epc_file, outer_inactive_mask,
                                 source_grid, well_name)

    return grid, box
Exemple #5
0
def add_one_blocked_well_property(epc_file,
                                  a,
                                  property_kind,
                                  blocked_well_uuid,
                                  source_info='imported',
                                  title=None,
                                  discrete=False,
                                  uom=None,
                                  time_index=None,
                                  time_series_uuid=None,
                                  string_lookup_uuid=None,
                                  null_value=None,
                                  indexable_element='cells',
                                  facet_type=None,
                                  facet=None,
                                  realization=None,
                                  local_property_kind_uuid=None,
                                  count_per_element=1,
                                  points=False,
                                  extra_metadata={},
                                  new_epc_file=None):
    """Adds a blocked well property from a numpy array to an existing resqml dataset.

    arguments:
       epc_file (string): file name to load model resqml model from (and rewrite to if new_epc_file is None)
       a (1D numpy array): the blocked well property array to be added to the model
       property_kind (string): the resqml property kind
       blocked_well_uuid (uuid object or string): the uuid of the blocked well to which the property relates
       source_info (string): typically the name of a file from which the array has been read but can be any
          information regarding the source of the data
       title (string): this will be used as the citation title when a part is generated for the array
       discrete (boolean, default False): if True, the array should contain integer (or boolean) data; if False, float
       uom (string, default None): the resqml units of measure for the data; not relevant to discrete data
       time_index (integer, default None): if not None, the time index to be used when creating a part for the array
       time_series_uuid (uuid object or string, default None): required if time_index is not None
       string_lookup_uuid (uuid object or string, optional): required if the array is to be stored as a categorical
          property; set to None for non-categorical discrete data; only relevant if discrete is True
       null_value (int, default None): if present, this is used in the metadata to indicate that this value
          is to be interpreted as a null value wherever it appears in the data (use for discrete data only)
       indexable_element (string, default 'cells'): the indexable element in the supporting representation (the blocked well);
          valid values are 'cells', 'intervals' (which includes unblocked intervals), or 'nodes'
       facet_type (string): resqml facet type, or None
       facet (string): resqml facet, or None
       realization (int): realization number, or None
       local_property_kind_uuid (uuid.UUID or string): uuid of local property kind, or None
       count_per_element (int, default 1): the number of values per indexable element; if greater than one then this
          must be the fastest cycling axis in the cached array, ie last index; if greater than 1 then a must be a 2D array
       points (bool, default False): if True, this is a points property with an extra dimension of extent 3
       extra_metadata (dict, optional): any items in this dictionary are added as extra metadata to the new
          property
       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 blocked well (and dependencies) and
          the new property

    returns:
       uuid.UUID of newly created property object
    """

    if new_epc_file and epc_file and (
        (new_epc_file == epc_file) or
        (os.path.exists(new_epc_file) and os.path.exists(epc_file)
         and os.path.samefile(new_epc_file, epc_file))):
        new_epc_file = None

    # open up model and establish grid object
    model = rq.Model(epc_file)

    blocked_well = rqw.BlockedWell(model, uuid=blocked_well_uuid)
    assert blocked_well is not None, f'no blocked well object found with uuid {blocked_well_uuid}'

    if not discrete:
        string_lookup_uuid = None

    # create an empty property collection and add the new array to its 'imported' list
    bwpc = rqp.PropertyCollection()
    bwpc.set_support(support=blocked_well, model=model)
    bwpc.add_cached_array_to_imported_list(
        a,
        source_info,
        title,
        discrete=discrete,
        uom=uom,
        time_index=time_index,
        null_value=null_value,
        property_kind=property_kind,
        local_property_kind_uuid=local_property_kind_uuid,
        facet_type=facet_type,
        facet=facet,
        realization=realization,
        indexable_element=indexable_element,
        count=count_per_element,
        points=points)
    bwpc.write_hdf5_for_imported_list()
    uuid_list = bwpc.create_xml_for_imported_list_and_add_parts_to_model(
        time_series_uuid=time_series_uuid,
        string_lookup_uuid=string_lookup_uuid,
        property_kind_uuid=local_property_kind_uuid,
        extra_metadata=extra_metadata)
    assert len(uuid_list) == 1
    model.store_epc()
    return uuid_list[0]