def test_Horizon(tmp_model): gen = rqo.GeneticBoundaryFeature(tmp_model, kind = 'horizon') hor = rqo.HorizonInterpretation(tmp_model, genetic_boundary_feature = gen, sequence_stratigraphy_surface = 'maximum flooding') gen.create_xml() hor.create_xml() hor2 = rqo.HorizonInterpretation(tmp_model, uuid = hor.uuid) assert hor2.sequence_stratigraphy_surface == hor.sequence_stratigraphy_surface
def _create_feature_and_interpretation(model, feature_type, feature_name): # includes create_xml() for created organisational objects feature_type = feature_type.lower() assert feature_type in ['fault', 'horizon', 'geobody'] if feature_type == 'fault': tbf = rqo.TectonicBoundaryFeature(model, kind='fault', feature_name=feature_name) else: tbf = rqo.GeneticBoundaryFeature(model, kind=feature_type, feature_name=feature_name) tbf.create_xml(reuse=True) if feature_type == 'fault': fi = rqo.FaultInterpretation(model, tectonic_boundary_feature=tbf, is_normal=True) # todo: set is_normal correctly elif feature_type == 'horizon': fi = rqo.HorizonInterpretation(model, genetic_boundary_feature=tbf) # todo: support boundary relation list and sequence stratigraphy surface else: # geobody boundary fi = rqo.GeobodyBoundaryInterpretation( model, genetic_boundary_feature=tbf) fi.create_xml(reuse=True) return fi.uuid
def _add_single_surface(model, surf_file, surface_file_format, surface_role, quad_triangles, crs_uuid, rq_class, hdf5_file, h5_mode, make_horizon_interpretations_and_features, ext_uuid): _, short_name = os.path.split(surf_file) dot = short_name.rfind('.') if dot > 0: short_name = short_name[:dot] log.info('surface ' + short_name + ' processing file: ' + surf_file + ' using format: ' + surface_file_format) if rq_class == 'surface': if surface_file_format == 'GOCAD-Tsurf': surface = rqs.Surface(model, tsurf_file = surf_file, surface_role = surface_role, quad_triangles = quad_triangles) else: surface = rqs.Surface(model, mesh_file = surf_file, mesh_format = surface_file_format, surface_role = surface_role, quad_triangles = quad_triangles) elif rq_class == 'mesh': if surface_file_format == 'GOCAD-Tsurf': log.info(f"Cannot convert a GOCAD-Tsurf to mesh, only to TriangulatedSurface - skipping file {surf_file}") return model else: surface = rqs.Mesh(model, mesh_file = surf_file, mesh_format = surface_file_format, mesh_flavour = 'reg&z', surface_role = surface_role, crs_uuid = crs_uuid) else: log.critical('this is impossible') # NB. surface may be either a Surface object or a Mesh object log.debug('appending to hdf5 file for surface file: ' + surf_file) surface.write_hdf5(hdf5_file, mode = h5_mode) if make_horizon_interpretations_and_features: feature = rqo.GeneticBoundaryFeature(model, kind = 'horizon', feature_name = short_name) feature.create_xml() interp = rqo.HorizonInterpretation(model, genetic_boundary_feature = feature, domain = 'depth') interp_root = interp.create_xml() surface.set_represented_interpretation_root(interp_root) surface.create_xml(ext_uuid, add_as_part = True, add_relationships = True, title = short_name + ' sourced from ' + surf_file, originator = None) return model
def create_interpretation_and_feature(self, kind='horizon', name=None, interp_title_suffix=None, is_normal=True): """Creates xml and objects for a represented interpretaion and interpreted feature, if not already present.""" assert kind in ['horizon', 'fault', 'fracture', 'geobody boundary'] assert name or self.title, 'title missing' if not name: name = self.title if self.rep_int_root is not None: log.debug( f'represented interpretation already exisrs for surface {self.title}' ) return if kind in ['horizon', 'geobody boundary']: feature = rqo.GeneticBoundaryFeature(self.model, kind=kind, feature_name=name) feature.create_xml() if kind == 'horizon': interp = rqo.HorizonInterpretation( self.model, genetic_boundary_feature=feature, domain='depth') else: interp = rqo.GeobodyBoundaryInterpretation( self.model, genetic_boundary_feature=feature, domain='depth') elif kind in ['fault', 'fracture']: feature = rqo.TectonicBoundaryFeature(self.model, kind=kind, feature_name=name) feature.create_xml() interp = rqo.FaultInterpretation( self.model, is_normal=is_normal, tectonic_boundary_feature=feature, domain='depth') # might need more arguments else: log.critical('code failure') interp_root = interp.create_xml(title_suffix=interp_title_suffix) self.rep_int_root = interp_root
def _make_k_gcs_from_cip_list(grid, cip_list, feature_name): # cip (cell index pair) list contains pairs of natural cell indices for which k connection is required # first of pair is layer above (lower k to be precise), second is below (higher k) # called by pinchout_connection_set() and k_gap_connection_set() functions from resqpy.fault._grid_connection_set import GridConnectionSet count = len(cip_list) if count == 0: return None pcs = GridConnectionSet(grid.model) pcs.grid_list = [grid] pcs.count = count pcs.grid_index_pairs = np.zeros((count, 2), dtype=int) pcs.cell_index_pairs = np.array(cip_list, dtype=int) pcs.face_index_pairs = np.zeros((count, 2), dtype=int) # initialize to top faces pcs.face_index_pairs[:, 0] = 1 # bottom face of cells above pinchout pcs.feature_indices = np.zeros( count, dtype=int) # could create seperate features by layer above or below? gbf = rqo.GeneticBoundaryFeature(grid.model, kind='horizon', feature_name=feature_name) gbf_root = gbf.create_xml() fi = rqo.HorizonInterpretation(grid.model, genetic_boundary_feature=gbf) fi_root = fi.create_xml(gbf_root, title_suffix=None) fi_uuid = rqet.uuid_for_part_root(fi_root) pcs.feature_list = [('obj_HorizonInterpretation', fi_uuid, str(feature_name))] return pcs
def test_property_collection(example_model_and_crs): # Create a WellboreMarkerFrame object # Load example model from a fixture model, crs = example_model_and_crs # Create a trajectory well_name = 'Banoffee' elevation = 100 datum = resqpy.well.MdDatum(parent_model=model, crs_uuid=crs.uuid, location=(0, 0, -elevation), md_reference='kelly bushing') mds = np.array([300.0, 310.0, 330.0]) zs = mds - elevation source_dataframe = pd.DataFrame({ 'MD': mds, 'X': [150.0, 165.0, 180.0], 'Y': [240.0, 260.0, 290.0], 'Z': zs, }) trajectory = resqpy.well.Trajectory(parent_model=model, data_frame=source_dataframe, well_name=well_name, md_datum=datum, length_uom='m') trajectory.write_hdf5() trajectory.create_xml() trajectory_uuid = trajectory.uuid # Create features and interpretations horizon_feature_1 = rqo.GeneticBoundaryFeature( parent_model=model, kind='horizon', feature_name='horizon_feature_1') horizon_feature_1.create_xml() horizon_interp_1 = rqo.HorizonInterpretation( parent_model=model, title='horizon_interp_1', genetic_boundary_feature=horizon_feature_1, sequence_stratigraphy_surface='flooding', boundary_relation_list=['conformable']) horizon_interp_1.create_xml() woc_feature_1 = rqo.FluidBoundaryFeature(parent_model=model, kind='water oil contact', feature_name='woc_1') # fluid boundary feature does not have an associated interpretation woc_feature_1.create_xml() fault_feature_1 = rqo.TectonicBoundaryFeature( parent_model=model, kind='fault', feature_name='fault_feature_1') fault_feature_1.create_xml() fault_interp_1 = rqo.FaultInterpretation( parent_model=model, title='fault_interp_1', tectonic_boundary_feature=fault_feature_1, is_normal=True, maximum_throw=15) fault_interp_1.create_xml() df = pd.DataFrame({ 'MD': [400.0, 410.0, 430.0], 'Boundary_Feature_Type': ['horizon', 'water oil contact', 'fault'], 'Marker_Citation_Title': ['marker_horizon_1', 'marker_woc_1', 'marker_fault_1'], 'Interp_Citation_Title': ['horizon_interp_1', None, 'fault_interp_1'], }) # Create a wellbore marker frame from a dataframe wellbore_marker_frame = resqpy.well.WellboreMarkerFrame.from_dataframe( parent_model=model, dataframe=df, trajectory_uuid=trajectory_uuid, title='WBF1', originator='Human', extra_metadata={'target_reservoir': 'treacle'}) wellbore_marker_frame.write_hdf5() wellbore_marker_frame.create_xml() # create a property collection for the wellbore marker frame and add a couple of properties pc = rqp.PropertyCollection(support=wellbore_marker_frame) assert pc is not None node_prop = np.array([123.45, -456.78, 987.65]) pc.add_cached_array_to_imported_list(node_prop, source_info='unit test', keyword='node prop', uom='m', property_kind='length', indexable_element='nodes') interval_prop = np.array([3, 1], dtype=int) pc.add_cached_array_to_imported_list(interval_prop, source_info='unit test', keyword='interval prop', discrete=True, null_value=-1, property_kind='discrete', indexable_element='intervals') pc.write_hdf5_for_imported_list() pc.create_xml_for_imported_list_and_add_parts_to_model() del pc # reload the property collection pc = rqp.PropertyCollection(support=wellbore_marker_frame) node_prop_part = model.part(obj_type='ContinuousProperty', title='node prop') interval_prop_part = model.part(obj_type='DiscreteProperty', title='interval prop') # check the property arrays assert pc is not None assert pc.number_of_parts() == 2 assert node_prop_part is not None assert interval_prop_part is not None assert node_prop_part in pc.parts() assert interval_prop_part in pc.parts() assert_array_almost_equal(pc.cached_part_array_ref(node_prop_part), [123.45, -456.78, 987.65]) int_prop_array = pc.cached_part_array_ref(interval_prop_part) assert int_prop_array.size == 2 assert np.all(int_prop_array == (3, 1))
def test_find_marker_from_index(example_model_and_crs): # --------- Arrange ---------- # Create a WellboreMarkerFrame object in memory # Load example model from a fixture model, crs = example_model_and_crs # Create a trajectory well_name = 'Banoffee' elevation = 100 datum = resqpy.well.MdDatum(parent_model=model, crs_uuid=crs.uuid, location=(0, 0, -elevation), md_reference='kelly bushing') mds = np.array([300.0, 310.0, 330.0]) zs = mds - elevation source_dataframe = pd.DataFrame({ 'MD': mds, 'X': [150.0, 165.0, 180.0], 'Y': [240.0, 260.0, 290.0], 'Z': zs, }) trajectory = resqpy.well.Trajectory(parent_model=model, data_frame=source_dataframe, well_name=well_name, md_datum=datum, length_uom='m') trajectory.write_hdf5() trajectory.create_xml() trajectory_uuid = trajectory.uuid # Create features and interpretations horizon_feature_1 = rqo.GeneticBoundaryFeature( parent_model=model, kind='horizon', feature_name='horizon_feature_1') horizon_feature_1.create_xml() horizon_interp_1 = rqo.HorizonInterpretation( parent_model=model, title='horizon_interp_1', genetic_boundary_feature=horizon_feature_1, sequence_stratigraphy_surface='flooding', boundary_relation_list=['conformable']) horizon_interp_1.create_xml() woc_feature_1 = rqo.FluidBoundaryFeature(parent_model=model, kind='water oil contact', feature_name='woc_1') # fluid boundary feature does not have an associated interpretation woc_feature_1.create_xml() fault_feature_1 = rqo.TectonicBoundaryFeature( parent_model=model, kind='fault', feature_name='fault_feature_1') fault_feature_1.create_xml() fault_interp_1 = rqo.FaultInterpretation( parent_model=model, title='fault_interp_1', tectonic_boundary_feature=fault_feature_1, is_normal=True, maximum_throw=15) fault_interp_1.create_xml() df = pd.DataFrame({ 'MD': [400.0, 410.0, 430.0], 'Boundary_Feature_Type': ['horizon', 'water oil contact', 'fault'], 'Marker_Citation_Title': ['marker_horizon_1', 'marker_woc_1', 'marker_fault_1'], 'Interp_Citation_Title': ['horizon_interp_1', None, 'fault_interp_1'], }) # Create a wellbore marker frame from a dataframe wellbore_marker_frame = resqpy.well.WellboreMarkerFrame.from_dataframe( parent_model=model, dataframe=df, trajectory_uuid=trajectory_uuid, title='WBF1', originator='Human', extra_metadata={'target_reservoir': 'treacle'}) # --------- Act ---------- # Find marker indices based on interpretation uuids found_woc_marker = wellbore_marker_frame.find_marker_from_index(idx=1) index_error_result = wellbore_marker_frame.find_marker_from_index(idx=5) # --------- Assert ---------- assert bu.matching_uuids(wellbore_marker_frame.marker_list[1].uuid, found_woc_marker.uuid) assert index_error_result is None
def test_from_dataframe_and_dataframe(example_model_and_crs): # Test that a WellboreMarkerFrame object can be correctly instantiated from a source dataframe and verify that the # dataframe generated by the dataframe() method matches the source dataframe # --------- Arrange ---------- # Create a WellboreMarkerFrame object in memory # Load example model from a fixture model, crs = example_model_and_crs epc_path = model.epc_file # Create a trajectory well_name = 'Banoffee' elevation = 100 datum = resqpy.well.MdDatum(parent_model=model, crs_uuid=crs.uuid, location=(0, 0, -elevation), md_reference='kelly bushing') mds = np.array([300.0, 310.0, 330.0]) zs = mds - elevation source_dataframe = pd.DataFrame({ 'MD': mds, 'X': [150.0, 165.0, 180.0], 'Y': [240.0, 260.0, 290.0], 'Z': zs, }) trajectory = resqpy.well.Trajectory(parent_model=model, data_frame=source_dataframe, well_name=well_name, md_datum=datum, length_uom='m') trajectory.write_hdf5() trajectory.create_xml() trajectory_uuid = trajectory.uuid # Create features and interpretations horizon_feature_1 = rqo.GeneticBoundaryFeature( parent_model=model, kind='horizon', feature_name='horizon_feature_1') horizon_feature_1.create_xml() horizon_interp_1 = rqo.HorizonInterpretation( parent_model=model, title='horizon_interp_1', genetic_boundary_feature=horizon_feature_1, sequence_stratigraphy_surface='flooding', boundary_relation_list=['conformable']) horizon_interp_1.create_xml() woc_feature_1 = rqo.FluidBoundaryFeature(parent_model=model, kind='water oil contact', feature_name='woc_1') # fluid boundary feature does not have an associated interpretation woc_feature_1.create_xml() fault_feature_1 = rqo.TectonicBoundaryFeature( parent_model=model, kind='fault', feature_name='fault_feature_1') fault_feature_1.create_xml() fault_interp_1 = rqo.FaultInterpretation( parent_model=model, title='fault_interp_1', tectonic_boundary_feature=fault_feature_1, is_normal=True, maximum_throw=15) fault_interp_1.create_xml() df = pd.DataFrame({ 'MD': [400.0, 410.0, 430.0], 'Boundary_Feature_Type': ['horizon', 'water oil contact', 'fault'], 'Marker_Citation_Title': ['marker_horizon_1', 'marker_woc_1', 'marker_fault_1'], 'Interp_Citation_Title': ['horizon_interp_1', None, 'fault_interp_1'], }) # Create a wellbore marker frame from a dataframe wellbore_marker_frame = resqpy.well.WellboreMarkerFrame.from_dataframe( parent_model=model, dataframe=df, trajectory_uuid=trajectory_uuid, title='WBF1', originator='Human', extra_metadata={'target_reservoir': 'treacle'}) # --------- Act ---------- # Save to disk wellbore_marker_frame.write_hdf5() wellbore_marker_frame.create_xml() wmf_uuid = wellbore_marker_frame.uuid # called after create_xml method as it can alter the uuid # get the uuids of each of the markers marker_uuids = [] for marker in wellbore_marker_frame.marker_list: marker_uuids.append(marker.uuid) model.store_epc() model.h5_release() # Clear memory del model, wellbore_marker_frame, datum, trajectory # Reload from disk model2 = Model(epc_file=epc_path) wellbore_marker_frame2 = resqpy.well.WellboreMarkerFrame( parent_model=model2, uuid=wmf_uuid) # Get the uuids of each of the markers marker_uuids2 = [] for marker in wellbore_marker_frame2.marker_list: marker_uuids2.append(marker.uuid) # Create a dataframe from the attributes of the new wellbore marker frame object df2 = wellbore_marker_frame2.dataframe() df2_filtered_cols = df2[[ 'MD', 'Boundary_Feature_Type', 'Marker_Citation_Title', 'Interp_Citation_Title' ]] # --------- Assert ---------- # test that the attributes were reloaded correctly assert bu.matching_uuids(wellbore_marker_frame2.trajectory_uuid, trajectory_uuid) assert wellbore_marker_frame2.node_count == len( wellbore_marker_frame2.node_mds) == len( wellbore_marker_frame2.marker_list) == 3 assert wellbore_marker_frame2.title == 'WBF1' assert wellbore_marker_frame2.originator == 'Human' assert wellbore_marker_frame2.extra_metadata == { 'target_reservoir': 'treacle' } np.testing.assert_almost_equal(wellbore_marker_frame2.node_mds, np.array([400.0, 410.0, 430.0])) for uuid1, uuid2 in zip(marker_uuids, marker_uuids2): assert bu.matching_uuids(uuid1, uuid2) # test that the generated dataframe contains the same data as the original df pd.testing.assert_frame_equal(df, df2_filtered_cols, check_dtype=False)
def test_load_from_xml(example_model_and_crs): # --------- Arrange ---------- model, crs = example_model_and_crs epc_path = model.epc_file elevation = 100 # Create a measured depth datum datum = resqpy.well.MdDatum(parent_model=model, crs_uuid=crs.uuid, location=(0, 0, -elevation), md_reference='kelly bushing') datum.create_xml() mds = np.array([300, 310, 330, 340]) zs = mds - elevation well_name = 'JubJub' source_dataframe = pd.DataFrame({ 'MD': mds, 'X': [1, 2, 3, 4], 'Y': [1, 2, 3, 4], 'Z': zs, 'WELL': ['JubJub', 'JubJub', 'JubJub', 'JubJub'] }) # Create a trajectory from dataframe trajectory = resqpy.well.Trajectory(parent_model=model, data_frame=source_dataframe, well_name=well_name, md_datum=datum, length_uom='m') trajectory.write_hdf5() trajectory.create_xml() trajectory_uuid = trajectory.uuid # Create a wellbore marker frame wellbore_marker_frame = resqpy.well.WellboreMarkerFrame( parent_model=model, trajectory_uuid=trajectory_uuid, title='WMF1', originator='Person1', extra_metadata={'target_reservoir': 'r1'}) wellbore_marker_frame.create_xml() # Create several boundary features and interpretations horizon_feature_1 = rqo.GeneticBoundaryFeature( parent_model=model, kind='horizon', feature_name='horizon_feature_1') horizon_feature_1.create_xml() horizon_interp_1 = rqo.HorizonInterpretation( parent_model=model, title='horizon_interp_1', genetic_boundary_feature=horizon_feature_1, sequence_stratigraphy_surface='flooding', boundary_relation_list=['conformable']) horizon_interp_1.create_xml() horizon_interp_uuid = horizon_interp_1.uuid fluid_contact_feature1 = rqo.FluidBoundaryFeature(parent_model=model, kind="gas oil contact", feature_name='goc_1') fluid_contact_feature1.create_xml() # --------- Act ---------- # Create a wellbore marker object wellbore_marker_1 = resqpy.well.WellboreMarker( parent_model=model, parent_frame=wellbore_marker_frame, marker_index=0, marker_type='horizon', interpretation_uuid=horizon_interp_uuid, title='Horizon1_marker', extra_metadata={'FormationName': 'Banoffee'}) wellbore_marker_1_uuid = wellbore_marker_1.uuid wellbore_marker_2 = resqpy.well.WellboreMarker( parent_model=model, parent_frame=wellbore_marker_frame, marker_index=1, marker_type='gas oil contact', title='GOC_marker') # Create xml for new wellbore markers wbm_node_1 = wellbore_marker_1.create_xml( parent_node=wellbore_marker_frame.root) wbm_node_2 = wellbore_marker_2.create_xml( parent_node=wellbore_marker_frame.root) # Load a new wellbore marker using a marker node wellbore_marker_3 = resqpy.well.WellboreMarker( parent_model=model, parent_frame=wellbore_marker_frame, marker_index=0, marker_node=wbm_node_1) # --------- Assert ---------- assert rqet.find_tag_text(root=wbm_node_1, tag_name='GeologicBoundaryKind') == 'horizon' assert rqet.find_tag_text(root=wbm_node_2, tag_name='FluidContact') == 'gas oil contact' assert wellbore_marker_3 is not None assert wellbore_marker_3.title == 'Horizon1_marker' assert wellbore_marker_3.marker_type == 'horizon' assert bu.matching_uuids(wellbore_marker_3.interpretation_uuid, horizon_interp_uuid) assert wellbore_marker_3.extra_metadata == {'FormationName': 'Banoffee'} assert bu.matching_uuids(wellbore_marker_3.uuid, wellbore_marker_1_uuid)