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 test_is_equivalent_is_tectonic_boundary_one_none(tmp_model): # Arrange fault_interp = rqo.FaultInterpretation( tmp_model, title='tectonic_boundary_test', ) fault_interp.tectonic_boundary_feature = 'something' # Act other = rqo.FaultInterpretation( tmp_model, title='tectonic_boundary_test', ) other.tectonic_boundary_feature = None result = fault_interp.is_equivalent(other=other) # Assert assert result is False
def test_FaultInterp(tmp_model): title = "not my fault" tect_boundary = rqo.TectonicBoundaryFeature(tmp_model, kind = 'fault') fault_interp = rqo.FaultInterpretation( tmp_model, tectonic_boundary_feature = tect_boundary, title = title, domain = "depth", is_normal = True, maximum_throw = 3, ) tect_boundary.create_xml() fault_interp.create_xml() fault_interp_2 = rqo.FaultInterpretation(tmp_model, uuid = fault_interp.uuid) assert fault_interp_2.title == title assert fault_interp_2.maximum_throw == 3
def test_is_equivalent_is_sameUUIDs(tmp_model): # Arrange object_uuid = uuid.new_uuid() fault_interp = rqo.FaultInterpretation( tmp_model, title='test_fault_interpretation', ) fault_interp.uuid = object_uuid # Act other = rqo.FaultInterpretation( tmp_model, title='test_fault_interpretation_other', ) other.uuid = object_uuid result = fault_interp.is_equivalent(other=other) # Assert assert result is True
def test_is_equivalent_is_other(tmp_model): # Arrange fault_interp = rqo.FaultInterpretation( tmp_model, title='test_fault_interpretation', ) # Act other = fault_interp result = fault_interp.is_equivalent(other=other) # Assert assert result is True
def test_is_equivalent_throw_math_is_close(tmp_model): # Arrange fault_interp = rqo.FaultInterpretation( tmp_model, title='test_fault_interpretation', ) fault_interp.maximum_throw = 1 # Act other = rqo.FaultInterpretation( tmp_model, title='test_fault_interpretation', ) other.maximum_throw = 1.002 result = fault_interp.is_equivalent(other=other) # Assert assert result is False
def test_is_equivalent_no_other(tmp_model): #Arrange fault_interp = rqo.FaultInterpretation( tmp_model, title='test_fault_interpretation', domain="depth", is_normal=True, maximum_throw=3, ) #Act result = fault_interp.is_equivalent(other=None) #Assert assert result is False
def test_is_equivalent_non_equivalent_tectonic_boundary(tmp_model): # Arrange tect_boundary = rqo.TectonicBoundaryFeature(tmp_model, kind='fault') fault_interp = rqo.FaultInterpretation( tmp_model, title='test_fault_interpretation', ) fault_interp.tectonic_boundary_feature = tect_boundary # Act tect_boundary_other = rqo.TectonicBoundaryFeature(tmp_model, kind='fracture') other = rqo.FaultInterpretation( tmp_model, title='test_fault_interpretation', ) other.tectonic_boundary_feature = tect_boundary_other result = fault_interp.is_equivalent(other=other) # Assert assert result is False
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 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)