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()
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))
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())
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
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]