def _establish_models_and_source_grid(epc_file, new_epc_file, source_grid, source_grid_uuid): assert epc_file or source_grid is not None, 'neither epc file name nor source grid supplied' if not epc_file: epc_file = source_grid.model.epc_file assert epc_file, 'unable to ascertain epc filename from grid object' if new_epc_file and epc_file and ( (new_epc_file == epc_file) or (os.path.exists(new_epc_file) and os.path.exists(epc_file) and os.path.samefile(new_epc_file, epc_file))): new_epc_file = None model = None if new_epc_file: log.debug('creating fresh model for refined grid') model = rq.Model(epc_file=new_epc_file, new_epc=True, create_basics=True, create_hdf5_ext=True) if epc_file: model_in = rq.Model(epc_file) if source_grid is None: if source_grid_uuid is None: log.debug('using default source grid from existing epc') source_grid = model_in.grid() else: log.debug( 'selecting source grid from existing epc based on uuid') source_grid = grr.Grid(model_in, uuid=source_grid_uuid) else: if source_grid_uuid is not None: assert bu.matching_uuids(source_grid_uuid, source_grid.uuid) grid_uuid = source_grid.uuid log.debug('reloading source grid from existing epc file') source_grid = grr.Grid(model_in, uuid=grid_uuid) if model is None: model = model_in else: model_in = source_grid.model assert model_in is not None assert model is not None assert source_grid is not None assert source_grid.grid_representation in ['IjkGrid', 'IjkBlockGrid'] return epc_file, model, model_in, source_grid
def example_fine_coarse_model(example_model_and_crs): model, crs = example_model_and_crs coarse_grid = grr.RegularGrid(parent_model=model, origin=(0, 0, 0), extent_kji=(3, 5, 5), crs_uuid=crs.uuid, dxyz=(10, 10, 10)) coarse_grid.cache_all_geometry_arrays() coarse_grid.write_hdf5_from_caches( file=model.h5_file_name(file_must_exist=False), mode='w') coarse_grid.create_xml(ext_uuid=model.h5_uuid(), title='Coarse', write_geometry=True, add_cell_length_properties=True) fine_grid = grr.RegularGrid(parent_model=model, origin=(0, 0, 0), extent_kji=(6, 10, 10), crs_uuid=crs.uuid, dxyz=(5, 5, 5)) fine_grid.cache_all_geometry_arrays() fine_grid.write_hdf5_from_caches( file=model.h5_file_name(file_must_exist=True), mode='a') fine_grid.create_xml(ext_uuid=model.h5_uuid(), title='Fine', write_geometry=True, add_cell_length_properties=True) model.store_epc() model = Model(model.epc_file) coarse = grr.Grid(parent_model=model, uuid=coarse_grid.uuid) fine = grr.Grid(parent_model=model, uuid=fine_grid.uuid) fc = rqfc.FineCoarse(fine_extent_kji=(6, 10, 10), coarse_extent_kji=(3, 5, 5)) fc.set_all_ratios_constant() fc.set_all_proprtions_equal() return model, coarse, fine, fc
def _create_gcs_if_requested(create_gcs, composite_face_set_dict, new_epc_file, grid): if create_gcs and len(composite_face_set_dict) > 0: if new_epc_file is not None: grid_uuid = grid.uuid model = rq.Model(new_epc_file) grid = grr.Grid(model, root=model.root(uuid=grid_uuid), find_properties=False) grid.set_face_set_gcs_list_from_dict( composite_face_set_dict, create_organizing_objects_where_needed=True) combined_gcs = grid.face_set_gcs_list[0] for gcs in grid.face_set_gcs_list[1:]: combined_gcs.append(gcs) combined_gcs.write_hdf5() combined_gcs.create_xml(title='faults added from lines') grid.clear_face_sets() grid.model.store_epc()
def _empty_grid(model, source_grid, is_regular, k0_min, k0_max, zone_count): if is_regular: dxyz_dkji = source_grid.block_dxyz_dkji.copy() dxyz_dkji[0] *= k0_max - k0_min + 1 grid = grr.RegularGrid(model, extent_kji = (1, source_grid.nj, source_grid.ni), dxyz_dkji = dxyz_dkji, origin = source_grid.block_origin, crs_uuid = source_grid.crs_uuid, set_points_cached = False) else: grid = grr.Grid(model) # inherit attributes from source grid grid.grid_representation = 'IjkGrid' grid.extent_kji = np.array((zone_count, source_grid.nj, source_grid.ni), dtype = 'int') grid.nk, grid.nj, grid.ni = zone_count, source_grid.nj, source_grid.ni grid.has_split_coordinate_lines = source_grid.has_split_coordinate_lines grid.crs_uuid = source_grid.crs_uuid grid.k_direction_is_down = source_grid.k_direction_is_down grid.grid_is_right_handed = source_grid.grid_is_right_handed grid.pillar_shape = source_grid.pillar_shape return grid
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
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
def from_unsplit_grid(cls, parent_model, grid_uuid, inherit_properties = True, title = None, extra_metadata = {}, write_active = None): """Creates a new (unstructured) HexaGrid from an existing resqpy unsplit (IJK) Grid without K gaps. arguments: parent_model (model.Model object): the model which this grid is part of grid_uuid (uuid.UUID): the uuid of an IjkGridRepresentation from which the hexa grid will be created inherit_properties (boolean, default True): if True, properties will be created for the new grid title (str, optional): citation title for the new grid extra_metadata (dict, optional): dictionary of extra metadata items to add to the grid write_active (boolean, optional): if True (or None and inactive property is established) then an active cell property is created (in addition to any inherited properties) returns: a newly created HexaGrid object note: this method includes the writing of hdf5 data, creation of xml for the new grid and adding it as a part """ import resqpy.grid as grr # establish existing IJK grid ijk_grid = grr.Grid(parent_model, uuid = grid_uuid, find_properties = inherit_properties) assert ijk_grid is not None assert not ijk_grid.has_split_coordinate_lines, 'IJK grid has split coordinate lines (faults)' assert not ijk_grid.k_gaps, 'IJK grid has K gaps' ijk_grid.cache_all_geometry_arrays() ijk_points = ijk_grid.points_ref(masked = False) if title is None: title = ijk_grid.title # make empty unstructured hexa grid hexa_grid = cls(parent_model, title = title, extra_metadata = extra_metadata) # derive hexa grid attributes from ijk grid hexa_grid.crs_uuid = ijk_grid.crs_uuid hexa_grid.set_cell_count(ijk_grid.cell_count()) if ijk_grid.inactive is not None: hexa_grid.inactive = ijk_grid.inactive.reshape((hexa_grid.cell_count,)) hexa_grid.all_inactive = np.all(hexa_grid.inactive) if hexa_grid.all_inactive: log.warning(f'all cells marked as inactive for unstructured hexa grid {hexa_grid.title}') else: hexa_grid.all_inactive = False # inherit points (nodes) in IJK grid order, ie. K cycling fastest, then I, then J hexa_grid.points_cached = ijk_points.reshape((-1, 3)) # setup faces per cell # ordering of faces (in nodes per face): all K faces, then all J faces, then all I faces # within J faces, ordering is all of J- faces for J = 0 first, then increasing planes in J # similarly for I faces nk_plus_1 = ijk_grid.nk + 1 nj_plus_1 = ijk_grid.nj + 1 ni_plus_1 = ijk_grid.ni + 1 k_face_count = nk_plus_1 * ijk_grid.nj * ijk_grid.ni j_face_count = ijk_grid.nk * nj_plus_1 * ijk_grid.ni i_face_count = ijk_grid.nk * ijk_grid.nj * ni_plus_1 kj_face_count = k_face_count + j_face_count hexa_grid.face_count = k_face_count + j_face_count + i_face_count hexa_grid.faces_per_cell_cl = 6 * (1 + np.arange(hexa_grid.cell_count, dtype = int)) # 6 faces per cell hexa_grid.faces_per_cell = np.empty(6 * hexa_grid.cell_count, dtype = int) arange = np.arange(hexa_grid.cell_count, dtype = int) hexa_grid.faces_per_cell[0::6] = arange # K- faces hexa_grid.faces_per_cell[1::6] = ijk_grid.nj * ijk_grid.ni + arange # K+ faces nki = ijk_grid.nk * ijk_grid.ni nkj = ijk_grid.nk * ijk_grid.nj # todo: vectorise following for loop for cell in range(hexa_grid.cell_count): k, j, i = ijk_grid.denaturalized_cell_index(cell) j_minus_face = k_face_count + nki * j + ijk_grid.ni * k + i hexa_grid.faces_per_cell[6 * cell + 2] = j_minus_face # J- face hexa_grid.faces_per_cell[6 * cell + 3] = j_minus_face + nki # J+ face i_minus_face = kj_face_count + nkj * i + ijk_grid.nj * k + j hexa_grid.faces_per_cell[6 * cell + 4] = i_minus_face # I- face hexa_grid.faces_per_cell[6 * cell + 5] = i_minus_face + nkj # I+ face # setup nodes per face, clockwise when viewed from negative side of face if ijk handedness matches xyz handedness # ordering of nodes in points array is as for the IJK grid hexa_grid.node_count = hexa_grid.points_cached.shape[0] assert hexa_grid.node_count == (ijk_grid.nk + 1) * (ijk_grid.nj + 1) * (ijk_grid.ni + 1) hexa_grid.nodes_per_face_cl = 4 * (1 + np.arange(hexa_grid.face_count, dtype = int)) # 4 nodes per face hexa_grid.nodes_per_face = np.empty(4 * hexa_grid.face_count, dtype = int) # todo: vectorise for loops # K faces face_base = 0 for k in range(nk_plus_1): for j in range(ijk_grid.nj): for i in range(ijk_grid.ni): hexa_grid.nodes_per_face[face_base] = (k * nj_plus_1 + j) * ni_plus_1 + i # ip 0, jp 0 hexa_grid.nodes_per_face[face_base + 1] = (k * nj_plus_1 + j + 1) * ni_plus_1 + i # ip 0, jp 1 hexa_grid.nodes_per_face[face_base + 2] = (k * nj_plus_1 + j + 1) * ni_plus_1 + i + 1 # ip 1, jp 1 hexa_grid.nodes_per_face[face_base + 3] = (k * nj_plus_1 + j) * ni_plus_1 + i + 1 # ip 1, jp 0 face_base += 4 # J faces assert face_base == 4 * k_face_count for j in range(nj_plus_1): for k in range(ijk_grid.nk): for i in range(ijk_grid.ni): hexa_grid.nodes_per_face[face_base] = (k * nj_plus_1 + j) * ni_plus_1 + i # ip 0, kp 0 hexa_grid.nodes_per_face[face_base + 1] = (k * nj_plus_1 + j) * ni_plus_1 + i + 1 # ip 1, kp 0 hexa_grid.nodes_per_face[face_base + 2] = ((k + 1) * nj_plus_1 + j) * ni_plus_1 + i + 1 # ip 1, kp 1 hexa_grid.nodes_per_face[face_base + 3] = ((k + 1) * nj_plus_1 + j) * ni_plus_1 + i # ip 0, kp 1 face_base += 4 # I faces assert face_base == 4 * kj_face_count for i in range(ni_plus_1): for k in range(ijk_grid.nk): for j in range(ijk_grid.nj): hexa_grid.nodes_per_face[face_base] = (k * nj_plus_1 + j) * ni_plus_1 + i # jp 0, kp 0 hexa_grid.nodes_per_face[face_base + 1] = ((k + 1) * nj_plus_1 + j) * ni_plus_1 + i # jp 0, kp 1 hexa_grid.nodes_per_face[face_base + 2] = ((k + 1) * nj_plus_1 + j + 1) * ni_plus_1 + i # jp 1, kp 1 hexa_grid.nodes_per_face[face_base + 3] = (k * nj_plus_1 + j + 1) * ni_plus_1 + i # jp 1, kp 0 face_base += 4 assert face_base == 4 * hexa_grid.face_count # set cell face is right handed # todo: check Energistics documents for meaning of cell face is right handed # here the assumption is clockwise ordering of nodes viewed from within cell means 'right handed' hexa_grid.cell_face_is_right_handed = np.zeros(6 * hexa_grid.cell_count, dtype = bool) # initially set to left handed # if IJK grid's ijk handedness matches the xyz handedness, then set +ve faces to right handed; else -ve faces if ijk_grid.off_handed(): hexa_grid.cell_face_is_right_handed[0::2] = True # negative faces are right handed else: hexa_grid.cell_face_is_right_handed[1::2] = True # positive faces are right handed hexa_grid.write_hdf5(write_active = write_active) hexa_grid.create_xml(write_active = write_active) if inherit_properties: ijk_pc = ijk_grid.extract_property_collection() hexa_pc = rqp.PropertyCollection(support = hexa_grid) for part in ijk_pc.parts(): count = ijk_pc.count_for_part(part) hexa_part_shape = (hexa_grid.cell_count,) if count == 1 else (hexa_grid.cell_count, count) hexa_pc.add_cached_array_to_imported_list( ijk_pc.cached_part_array_ref(part).reshape(hexa_part_shape), 'inherited from grid ' + str(ijk_grid.title), ijk_pc.citation_title_for_part(part), discrete = not ijk_pc.continuous_for_part(part), uom = ijk_pc.uom_for_part(part), time_index = ijk_pc.time_index_for_part(part), null_value = ijk_pc.null_value_for_part(part), property_kind = ijk_pc.property_kind_for_part(part), local_property_kind_uuid = ijk_pc.local_property_kind_uuid(part), facet_type = ijk_pc.facet_type_for_part(part), facet = ijk_pc.facet_for_part(part), realization = ijk_pc.realization_for_part(part), indexable_element = ijk_pc.indexable_for_part(part), count = count, const_value = ijk_pc.constant_value_for_part(part)) # todo: patch min & max values if present in ijk part hexa_pc.write_hdf5_for_imported_list() hexa_pc.create_xml_for_imported_list_and_add_parts_to_model( support_uuid = hexa_grid.uuid, time_series_uuid = ijk_pc.time_series_uuid_for_part(part), string_lookup_uuid = ijk_pc.string_lookup_uuid_for_part(part), extra_metadata = ijk_pc.extra_metadata_for_part(part)) return hexa_grid
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
def s_bend_grid(s_bend_model): nk = 5 nj = 12 ni_tail = 5 ni_bend = 18 ni_half_mid = 2 ni = 2 * (ni_tail + ni_bend + ni_half_mid) total_thickness = 12.0 layer_thickness = total_thickness / float(nk) flat_dx_di = 10.0 horst_dx_dk = 0.25 * flat_dx_di / float(nk) dy_dj = 8.0 top_depth = 100.0 bend_theta_di = maths.pi / float(ni_bend) outer_radius = 2.0 * total_thickness bend_a_centre_xz = (flat_dx_di * float(ni_tail), top_depth + outer_radius) bend_b_centre_xz = (flat_dx_di * float(ni_tail - 2.0 * ni_half_mid), top_depth + 3.0 * outer_radius - total_thickness) points = np.empty((nk + 1, nj + 1, ni + 1, 3)) for k in range(nk + 1): if k == nk // 2 + 1: points[k] = points[k - 1] # pinched out layer else: for i in range(ni + 1): if i < ni_tail + 1: x = flat_dx_di * float(i) z = top_depth + float( k ) * layer_thickness # will introduce a thick layer after pinchout elif i < ni_tail + ni_bend: theta = (i - ni_tail) * bend_theta_di radius = outer_radius - float(k) * layer_thickness x = bend_a_centre_xz[0] + radius * maths.sin(theta) z = bend_a_centre_xz[1] - radius * maths.cos(theta) elif i < ni_tail + ni_bend + 2 * ni_half_mid + 1: x = flat_dx_di * float(ni_tail - (i - (ni_tail + ni_bend))) z = top_depth + 2.0 * outer_radius - float( k) * layer_thickness elif i < ni_tail + 2 * ni_bend + 2 * ni_half_mid: theta = ( i - (ni_tail + ni_bend + 2 * ni_half_mid)) * bend_theta_di radius = outer_radius - float(nk - k) * layer_thickness x = bend_b_centre_xz[0] - radius * maths.sin(theta) z = bend_b_centre_xz[1] - radius * maths.cos(theta) else: x = flat_dx_di * float((i - (ni - ni_tail)) + ni_tail - 2 * ni_half_mid) if i == ni - 1 or i == ni - 4: x += horst_dx_dk * float(k) elif i == ni - 2 or i == ni - 3: x -= horst_dx_dk * float(k) z = top_depth + 4.0 * outer_radius + float( k) * layer_thickness - 2.0 * total_thickness points[k, :, i] = (x, 0.0, z) for j in range(nj + 1): points[:, j, :, 1] = dy_dj * float(j) grid = grr.Grid(s_bend_model) crs = rqc.Crs(s_bend_model) crs_node = crs.create_xml() grid.grid_representation = 'IjkGrid' grid.extent_kji = np.array((nk, nj, ni), dtype='int') grid.nk, grid.nj, grid.ni = nk, nj, ni grid.k_direction_is_down = True # dominant layer direction, or paleo-direction grid.pillar_shape = 'straight' grid.has_split_coordinate_lines = False grid.k_gaps = None grid.crs_uuid = crs.uuid grid.crs_root = crs_node grid.points_cached = points grid.geometry_defined_for_all_pillars_cached = True grid.geometry_defined_for_all_cells_cached = True grid.grid_is_right_handed = crs.is_right_handed_xyz() # grid.write_hdf5_from_caches() # grid.create_xml() return grid
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(epc_file=None, source_grid=None, box=None, box_inactive=None, inherit_properties=False, inherit_realization=None, inherit_all_realizations=False, set_parent_window=None, new_grid_title=None, new_epc_file=None): """Extends an existing model with a new grid extracted as a logical IJK box from the source grid. arguments: epc_file (string): file name to rewrite the model's xml to; if source grid is None, model is loaded from this file source_grid (grid.Grid object, optional): if None, the epc_file is loaded and it should contain one ijk grid object (or one 'ROOT' grid) which is used as the source grid box (numpy int array of shape (2, 3)): the minimum and maximum kji0 indices in the source grid (zero based) to include in the extracted grid; note that cells with index equal to maximum value are included (unlike with python ranges) box_inactive (numpy bool array, optional): if present, shape must match box and values will be or'ed in with the inactive mask inherited from the source grid; if None, inactive mask will be as inherited from source grid inherit_properties (boolean, default False): if True, the new grid will have a copy of any properties associated with the source grid, with values taken from the specified box inherit_realization (int, optional): realization number for which properties will be inherited; ignored if inherit_properties is False inherit_all_realizations (boolean, default False): if True (and inherit_realization is None), properties for all realizations will be inherited; if False, only properties with a realization of None are inherited; ignored if inherit_properties is False or inherit_realization is not None set_parent_window (boolean, optional): if True, the extracted grid has its parent window attribute set; if False, the parent window is not set; if None, the default will be True if new_epc_file is None or False otherwise new_grid_title (string): used as the citation title text for the new grid object new_epc_file (string, optional): if None, the source epc_file is extended with the new grid object; if present, a new epc file (& associated h5 file) is created to contain the extracted grid (& crs) returns: new grid object with extent as implied by the box argument note: the epc file and associated hdf5 file are appended to (extended) with the new grid, unless a new_epc_file is specified, in which case the grid and inherited properties are written there instead """ log.debug('extracting grid for box') assert epc_file or new_epc_file, 'epc file name not specified' if new_epc_file and epc_file and ( (new_epc_file == epc_file) or (os.path.exists(new_epc_file) and os.path.exists(epc_file) and os.path.samefile(new_epc_file, epc_file))): new_epc_file = None if set_parent_window is None: set_parent_window = (new_epc_file is None) model, source_grid = _establish_model_and_source_grid( epc_file, source_grid) assert source_grid.grid_representation in ['IjkGrid', 'IjkBlockGrid'] assert model is not None assert box is not None and box.shape == (2, 3) assert np.all(box[1, :] >= box[0, :]) and np.all( box[0, :] >= 0) and np.all(box[1, :] < source_grid.extent_kji) if source_grid.grid_representation == 'IjkBlockGrid': source_grid.make_regular_points_cached() box_str = bx.string_iijjkk1_for_box_kji0(box) # create a new, empty grid object grid = grr.Grid(model) # inherit attributes from source grid grid.grid_representation = 'IjkGrid' grid.extent_kji = box[1, :] - box[0, :] + 1 if box_inactive is not None: assert box_inactive.shape == tuple(grid.extent_kji) grid.nk, grid.nj, grid.ni = grid.extent_kji[0], grid.extent_kji[ 1], grid.extent_kji[2] grid.k_direction_is_down = source_grid.k_direction_is_down grid.grid_is_right_handed = source_grid.grid_is_right_handed grid.pillar_shape = source_grid.pillar_shape grid.has_split_coordinate_lines = source_grid.has_split_coordinate_lines # inherit the coordinate reference system used by the grid geometry grid.crs_uuid = source_grid.crs_uuid if source_grid.model is not model: model.duplicate_node(source_grid.model.root_for_uuid(grid.crs_uuid), add_as_part=True) grid.crs = rqc.Crs(model, uuid=grid.crs_uuid) # inherit k_gaps for selected layer range _inherit_k_gaps(source_grid, grid, box) # extract inactive cell mask _extract_inactive_cell_mask(source_grid, grid, box_inactive, box) # extract the grid geometry source_grid.cache_all_geometry_arrays() # determine cell geometry is defined if hasattr(source_grid, 'array_cell_geometry_is_defined'): grid.array_cell_geometry_is_defined = _array_box( source_grid.array_cell_geometry_is_defined, box) grid.geometry_defined_for_all_cells_cached = np.all( grid.array_cell_geometry_is_defined) else: grid.geometry_defined_for_all_cells_cached = source_grid.geometry_defined_for_all_cells_cached # copy info for pillar geometry is defined grid.geometry_defined_for_all_pillars_cached = source_grid.geometry_defined_for_all_pillars_cached if hasattr(source_grid, 'array_pillar_geometry_is_defined'): grid.array_pillar_geometry_is_defined = _array_box( source_grid.array_pillar_geometry_is_defined, box) grid.geometry_defined_for_all_pillars_cached = np.all( grid.array_pillar_geometry_is_defined) # get reference to points for source grid geometry source_points = source_grid.points_ref() pillar_box = box.copy() if source_grid.k_gaps: pillar_box[:, 0] = source_grid.k_raw_index_array[pillar_box[:, 0]] pillar_box[ 1, :] += 1 # pillar points have extent one greater than cells, in each axis if not source_grid.has_split_coordinate_lines: log.debug('no split pillars in source grid') grid.points_cached = _array_box( source_points, pillar_box) # should work, ie. preserve xyz axis else: _process_split_pillars(source_grid, grid, box, pillar_box) if set_parent_window: fine_coarse = fc.FineCoarse(grid.extent_kji, grid.extent_kji, within_coarse_box=box) fine_coarse.set_all_ratios_constant() grid.set_parent(source_grid.uuid, True, fine_coarse) collection = _inherit_collection(source_grid, grid, inherit_properties, box, inherit_realization, inherit_all_realizations) if new_grid_title is None or len(new_grid_title) == 0: new_grid_title = 'local grid ' + box_str + ' extracted from ' + str( rqet.citation_title_for_node(source_grid.root)) model.h5_release() if new_epc_file: _write_grid(new_epc_file, grid, property_collection=collection, grid_title=new_grid_title, mode='w') else: ext_uuid, _ = model.h5_uuid_and_path_for_node( rqet.find_nested_tags(source_grid.root, ['Geometry', 'Points']), 'Coordinates') _write_grid(epc_file, grid, ext_uuid=ext_uuid, property_collection=collection, grid_title=new_grid_title, mode='a') return grid
def interpolated_grid(epc_file, grid_a, grid_b, a_to_b_0_to_1=0.5, split_tolerance=0.01, inherit_properties=False, inherit_realization=None, inherit_all_realizations=False, new_grid_title=None, new_epc_file=None): """Extends an existing model with a new grid geometry linearly interpolated between the two source_grids. arguments: epc_file (string): file name to rewrite the model's xml to grid_a, grid_b (grid.Grid objects): a pair of RESQML grid objects representing the end cases, between which the new grid will be interpolated a_to_b_0_to_1 (float, default 0.5): the interpolation factor in the range zero to one; a value of 0.0 will yield a copy of grid a, a value of 1.0 will yield a copy of grid b, intermediate values will yield a grid with all points interpolated split_tolerance (float, default 0.01): maximum offset of corner points for shared point to be generated; units are same as those in grid crs; only relevant if working from corner points, ignored otherwise inherit_properties (boolean, default False): if True, the new grid will have a copy of any properties associated with grid_a inherit_realization (int, optional): realization number for which properties will be inherited; ignored if inherit_properties is False inherit_all_realizations (boolean, default False): if True (and inherit_realization is None), properties for all realizations will be inherited; if False, only properties with a realization of None are inherited; ignored if inherit_properties is False or inherit_realization is not None new_grid_title (string): used as the citation title text for the new grid object new_epc_file (string, optional): if None, the source epc_file is extended with the new grid object; if present, a new epc file (& associated h5 file) is created to contain the interpolated grid (& crs) returns: new grid object (grid.Grid) with geometry interpolated between grid a and grid b notes: the hdf5 file used by the grid_a model is appended to, so it is recommended that the grid_a model's epc is specified as the first argument (unless a new epc file is required, sharing the hdf5 file) """ assert epc_file or new_epc_file, 'epc file name not specified' if new_epc_file and epc_file and ( (new_epc_file == epc_file) or (os.path.exists(new_epc_file) and os.path.exists(epc_file) and os.path.samefile(new_epc_file, epc_file))): new_epc_file = None assert grid_a is not None and grid_b is not None, 'at least one source grid is missing' assert grid_a.grid_representation == 'IjkGrid' and grid_b.grid_representation == 'IjkGrid' assert 0.0 <= a_to_b_0_to_1 <= 1.0, 'interpolation factor outside range 0.0 to 1.0' assert tuple(grid_a.extent_kji) == tuple( grid_b.extent_kji), 'source grids have different extents' assert grid_a.k_direction_is_down == grid_b.k_direction_is_down, 'source grids have different k directions' assert grid_a.grid_is_right_handed == grid_b.grid_is_right_handed, 'source grids have different ijk handedness' assert grid_a.pillar_shape == grid_b.pillar_shape, 'source grids have different resqml pillar shapes' b_weight = a_to_b_0_to_1 a_weight = 1.0 - b_weight model = grid_a.model if not bu.matching_uuids(grid_a.crs_uuid, grid_b.crs_uuid): crs_a = rqc.Crs(grid_a.model, uuid=grid_a.crs_uuid) crs_b = rqc.Crs(grid_b.model, uuid=grid_b.crs_uuid) assert crs_a.is_equivalent(crs_b), \ 'end point grids for interpolation have different coordinate reference systems' log.info('loading geometry for two source grids') grid_a.cache_all_geometry_arrays() grid_b.cache_all_geometry_arrays() assert (grid_a.geometry_defined_for_all_cells() and grid_b.geometry_defined_for_all_cells() ), 'geometry not defined for all cells' # assert grid_a.geometry_defined_for_all_pillars() and grid_b.geometry_defined_for_all_pillars(), \ # 'geometry not defined for all pillars' work_from_pillars = _determine_work_from_pillars(grid_a, grid_b) # create a new, empty grid object grid = grr.Grid(model) # inherit attributes from source grid _inherit_basics(grid, grid_a, grid_b) if work_from_pillars: _interpolate_points_cached_from_pillars(grid, grid_a, grid_b, a_weight, b_weight) else: _interpolate_points_cached_from_cp(grid, grid_a, grid_b, a_weight, b_weight, split_tolerance) collection = _prepare_simple_inheritance(grid, grid_a, inherit_properties, inherit_realization, inherit_all_realizations) if new_grid_title is None or len(new_grid_title) == 0: new_grid_title = 'interpolated between two grids with factor: ' + str( a_to_b_0_to_1) model.h5_release() if new_epc_file: _write_grid(new_epc_file, grid, property_collection=collection, grid_title=new_grid_title, mode='w') else: ext_uuid, _ = model.h5_uuid_and_path_for_node( rqet.find_nested_tags(grid_a.root, ['Geometry', 'Points']), 'Coordinates') _write_grid(epc_file, grid, ext_uuid=ext_uuid, property_collection=collection, grid_title=new_grid_title, mode='a') return grid
def coarsened_grid(epc_file, source_grid, fine_coarse, inherit_properties=False, inherit_realization=None, inherit_all_realizations=False, set_parent_window=None, infill_missing_geometry=True, new_grid_title=None, new_epc_file=None): """Generates a coarsened version of an unsplit source grid, todo: optionally inheriting properties. arguments: epc_file (string): file name to rewrite the model's xml to; if source grid is None, model is loaded from this file source_grid (grid.Grid object, optional): if None, the epc_file is loaded and it should contain one ijk grid object (or one 'ROOT' grid) which is used as the source grid fine_coarse (resqpy.olio.fine_coarse.FineCoarse object): the mapping between cells in the fine (source) and coarse (output) grids inherit_properties (boolean, default False): if True, the new grid will have a copy of any properties associated with the source grid, with values upscaled or sampled inherit_realization (int, optional): realization number for which properties will be inherited; ignored if inherit_properties is False inherit_all_realizations (boolean, default False): if True (and inherit_realization is None), properties for all realizations will be inherited; if False, only properties with a realization of None are inherited; ignored if inherit_properties is False or inherit_realization is not None set_parent_window (boolean or str, optional): if True or 'parent', the coarsened grid has its parent window attribute set; if False, the parent window is not set; if None, the default will be True if new_epc_file is None or False otherwise; if 'grandparent' then an intervening parent window with no refinement or coarsening will be skipped and its box used in the parent window for the new grid, relating directly to the original grid infill_missing_geometry (boolean, default True): if True, an attempt is made to generate grid geometry in the source grid wherever it is undefined; if False, any undefined geometry will result in an assertion failure new_grid_title (string): used as the citation title text for the new grid object new_epc_file (string, optional): if None, the source epc_file is extended with the new grid object; if present, a new epc file (& associated h5 file) is created to contain the refined grid (& crs) returns: new grid object being the coarsened grid; the epc and hdf5 files are written to as an intentional side effect note: this function coarsens an entire grid; to coarsen a local area of a grid, first use the extract_box function and then use this function on the extracted grid; in such a case, using a value of 'grandparent' for the set_parent_window argument will relate the coarsened grid back to the original """ new_epc_file, model, source_grid = _establish_files_and_model( epc_file, new_epc_file, source_grid) if set_parent_window is None: set_parent_window = (new_epc_file is None) assert fine_coarse is not None and isinstance(fine_coarse, fc.FineCoarse) assert not source_grid.has_split_coordinate_lines, 'coarsening only available for unsplit grids: use other functions to heal faults first' if infill_missing_geometry and ( not source_grid.geometry_defined_for_all_cells() or not source_grid.geometry_defined_for_all_pillars()): log.debug('attempting infill of geometry missing in source grid') source_grid.set_geometry_is_defined(treat_as_nan=None, treat_dots_as_nan=True, complete_partial_pillars=True, nullify_partial_pillars=False, complete_all=True) assert source_grid.geometry_defined_for_all_pillars( ), 'coarsening requires geometry to be defined for all pillars' assert source_grid.geometry_defined_for_all_cells( ), 'coarsening requires geometry to be defined for all cells' assert not source_grid.k_gaps, 'coarsening of grids with k gaps not currently supported' assert tuple(fine_coarse.fine_extent_kji) == tuple(source_grid.extent_kji), \ 'fine_coarse mapping fine extent does not match that of source grid' fine_coarse.assert_valid() source_grid.cache_all_geometry_arrays() source_points = source_grid.points_ref().reshape( (source_grid.nk + 1), (source_grid.nj + 1) * (source_grid.ni + 1), 3) # create a new, empty grid object grid = grr.Grid(model) # inherit attributes from source grid grid.grid_representation = 'IjkGrid' grid.extent_kji = fine_coarse.coarse_extent_kji grid.nk, grid.nj, grid.ni = grid.extent_kji[0], grid.extent_kji[ 1], grid.extent_kji[2] grid.k_direction_is_down = source_grid.k_direction_is_down grid.grid_is_right_handed = source_grid.grid_is_right_handed grid.pillar_shape = source_grid.pillar_shape grid.has_split_coordinate_lines = False grid.split_pillars_count = None # inherit the coordinate reference system used by the grid geometry grid.crs_uuid = source_grid.crs_uuid if source_grid.model is not model: model.duplicate_node(source_grid.model.root_for_uuid(grid.crs_uuid), add_as_part=True) grid.crs = rqc.Crs(model, grid.crs_uuid) coarsened_points = np.empty( (grid.nk + 1, (grid.nj + 1) * (grid.ni + 1), 3)) # note: gets reshaped after being populated k_ratio_constant = fine_coarse.constant_ratios[0] if k_ratio_constant: k_indices = None else: k_indices = np.empty(grid.nk + 1, dtype=int) k_indices[0] = 0 for k in range(grid.nk): k_indices[k + 1] = k_indices[k] + fine_coarse.vector_ratios[0][k] assert k_indices[-1] == source_grid.nk for cjp in range(grid.nj + 1): for cji in range(grid.ni + 1): natural_coarse_pillar = cjp * (grid.ni + 1) + cji natural_fine_pillar = fine_coarse.fine_for_coarse_natural_pillar_index( natural_coarse_pillar) if k_ratio_constant: coarsened_points[:, natural_coarse_pillar, :] = source_points[ 0:source_grid.nk + 1:k_ratio_constant, natural_fine_pillar, :] else: coarsened_points[:, natural_coarse_pillar, :] = source_points[ k_indices, natural_fine_pillar, :] grid.points_cached = coarsened_points.reshape( ((grid.nk + 1), (grid.nj + 1), (grid.ni + 1), 3)) grid.geometry_defined_for_all_pillars_cached = True grid.geometry_defined_for_all_cells_cached = True grid.array_cell_geometry_is_defined = np.full(tuple(grid.extent_kji), True, dtype=bool) collection = None if inherit_properties: source_collection = source_grid.extract_property_collection() if source_collection is not None: collection = rqp.GridPropertyCollection() collection.set_grid(grid) collection.extend_imported_list_copying_properties_from_other_grid_collection( source_collection, coarsening=fine_coarse, realization=inherit_realization, copy_all_realizations=inherit_all_realizations) _set_parent_window_in_grid(set_parent_window, source_grid, grid, fine_coarse) # write grid if new_grid_title is None or len(new_grid_title) == 0: new_grid_title = 'grid coarsened from ' + str( rqet.citation_title_for_node(source_grid.root)) model.h5_release() if new_epc_file: _write_grid(new_epc_file, grid, property_collection=collection, grid_title=new_grid_title, mode='w') else: ext_uuid, _ = model.h5_uuid_and_path_for_node( rqet.find_nested_tags(source_grid.root, ['Geometry', 'Points']), 'Coordinates') _write_grid(epc_file, grid, ext_uuid=ext_uuid, property_collection=collection, grid_title=new_grid_title, mode='a') return grid