def test_wellbore_interp_title(tmp_model): # Create a feature and interp objects feature_name = 'well A' well_feature = rqo.WellboreFeature(tmp_model, feature_name = feature_name) well_feature.create_xml() well_interp_1 = rqo.WellboreInterpretation(tmp_model, wellbore_feature = well_feature, is_drilled = True) well_interp_1.create_xml() # Create a duplicate object, loading from XML well_interp_2 = rqo.WellboreInterpretation(tmp_model, uuid = well_interp_1.uuid) # Check feature name is present assert well_interp_1.title == feature_name assert well_interp_2.title == feature_name
def create_feature_and_interpretation(self): """Instantiate new empty WellboreFeature and WellboreInterpretation objects, if a wellboreinterpretation does not already exist. Uses the trajectory citation title as the well name """ log.debug("Creating a new WellboreInterpretation..") log.debug( f"WellboreFeature exists: {self.wellbore_feature is not None}") log.debug( f"WellboreInterpretation exists: {self.wellbore_interpretation is not None}" ) if self.wellbore_interpretation is None: log.info( f"Creating WellboreInterpretation and WellboreFeature with name {self.title}" ) self.wellbore_feature = rqo.WellboreFeature( parent_model=self.model, feature_name=self.title) self.wellbore_interpretation = rqo.WellboreInterpretation( parent_model=self.model, wellbore_feature=self.wellbore_feature) self.feature_and_interpretation_to_be_written = True else: raise ValueError( "Cannot add WellboreFeature, trajectory already has an associated WellboreInterpretation" )
def _load_from_xml(self): """Loads the trajectory object from an xml node (and associated hdf5 data).""" node = self.root assert node is not None self.start_md = float( rqet.node_text(rqet.find_tag(node, 'StartMd')).strip()) self.finish_md = float( rqet.node_text(rqet.find_tag(node, 'FinishMd')).strip()) self.md_uom = rqet.length_units_from_node(rqet.find_tag(node, 'MdUom')) self.md_domain = rqet.node_text(rqet.find_tag(node, 'MdDomain')) geometry_node = rqet.find_tag(node, 'Geometry') self.crs_uuid = bu.uuid_from_string( rqet.find_nested_tags_text(geometry_node, ['LocalCrs', 'UUID'])) self.knot_count = int( rqet.node_text(rqet.find_tag(geometry_node, 'KnotCount')).strip()) self.line_kind_index = int( rqet.node_text(rqet.find_tag(geometry_node, 'LineKindIndex')).strip()) mds_node = rqet.find_tag(geometry_node, 'ControlPointParameters') if mds_node is not None: # not required for vertical or z linear cubic spline load_hdf5_array(self, mds_node, 'measured_depths') control_points_node = rqet.find_tag(geometry_node, 'ControlPoints') load_hdf5_array(self, control_points_node, 'control_points', tag='Coordinates') tangents_node = rqet.find_tag(geometry_node, 'TangentVectors') if tangents_node is not None: load_hdf5_array(self, tangents_node, 'tangent_vectors', tag='Coordinates') relatives_model = self.model # if hdf5_source_model is None else hdf5_source_model # md_datum - separate part, referred to in this tree md_datum_uuid = bu.uuid_from_string( rqet.find_nested_tags_text(node, ['MdDatum', 'UUID'])) assert md_datum_uuid is not None, 'failed to fetch uuid of md datum for trajectory' md_datum_part = relatives_model.part_for_uuid(md_datum_uuid) assert md_datum_part, 'md datum part not found in model' self.md_datum = MdDatum( self.model, uuid=relatives_model.uuid_for_part(md_datum_part)) ds_uuid = bu.uuid_from_string( rqet.find_nested_tags_text(node, ['DeviationSurvey', 'UUID'])) if ds_uuid is not None: # this will probably not work when relatives model is different from self.model ds_uuid = self.model.uuid(obj_type='DeviationSurveyRepresentation', uuid=ds_uuid) # check part is present if ds_uuid is not None: self.deviation_survey = DeviationSurvey(self.model, uuid=ds_uuid) interp_uuid = rqet.find_nested_tags_text( node, ['RepresentedInterpretation', 'UUID']) if interp_uuid is None: self.wellbore_interpretation = None else: self.wellbore_interpretation = rqo.WellboreInterpretation( self.model, uuid=interp_uuid)
def _load_related_wellbore_interp(self): """Return related wellbore interp object from XML if present.""" interp_uuid = rqet.find_nested_tags_text(self.root, ['RepresentedInterpretation', 'UUID']) if interp_uuid is None: represented_interp = None else: represented_interp = rqo.WellboreInterpretation(self.model, uuid = interp_uuid) return represented_interp
def _iter_wellbore_interpretations(model): """Iterable of all WellboreInterpretations associated with the model.""" import resqpy.organize as rqo # imported here for speed, module is not always needed uuids = _uuids(model, obj_type='WellboreInterpretation') if uuids: for uuid in uuids: yield rqo.WellboreInterpretation(model, uuid=uuid)
def create_feature_and_interpretation(self): """Instantiate new empty WellboreFeature and WellboreInterpretation objects, if a wellboreinterpretation does not already exist. Uses the wellboreframe citation title as the well name """ if self.wellbore_interpretation is None: log.info( f"Creating WellboreInterpretation and WellboreFeature with name {self.title}" ) self.wellbore_feature = rqo.WellboreFeature( parent_model=self.model, feature_name=self.title) self.wellbore_interpretation = rqo.WellboreInterpretation( parent_model=self.model, wellbore_feature=self.wellbore_feature) self.feature_and_interpretation_to_be_written = True else: log.info("WellboreInterpretation already exists")
def _load_from_xml(self): """Loads the wellbore frame object from an xml node (and associated hdf5 data).""" # NB: node is the root level xml node, not a node in the md list! node = self.root assert node is not None trajectory_uuid = bu.uuid_from_string( rqet.find_nested_tags_text(node, ['Trajectory', 'UUID'])) assert trajectory_uuid is not None, 'wellbore frame trajectory reference not found in xml' if self.trajectory is None: self.trajectory = resqpy.well.Trajectory(self.model, uuid=trajectory_uuid) else: assert bu.matching_uuids( self.trajectory.uuid, trajectory_uuid), 'wellbore frame trajectory uuid mismatch' self.node_count = rqet.find_tag_int(node, 'NodeCount') assert self.node_count is not None, 'node count not found in xml for wellbore frame' assert self.node_count > 1, 'fewer than 2 nodes for wellbore frame' mds_node = rqet.find_tag(node, 'NodeMd') assert mds_node is not None, 'wellbore frame measured depths hdf5 reference not found in xml' load_hdf5_array(self, mds_node, 'node_mds') assert self.node_mds is not None and self.node_mds.ndim == 1 and self.node_mds.size == self.node_count interp_uuid = rqet.find_nested_tags_text( node, ['RepresentedInterpretation', 'UUID']) if interp_uuid is None: self.wellbore_interpretation = None else: self.wellbore_interpretation = rqo.WellboreInterpretation( self.model, uuid=interp_uuid) # Create well log collection of all log data self.logs = rqp.WellLogCollection(frame=self)
def add_wells_from_ascii_file(model, crs_uuid, trajectory_file, comment_character='#', space_separated_instead_of_csv=False, well_col='WELL', md_col='MD', x_col='X', y_col='Y', z_col='Z', length_uom='m', md_domain=None, drilled=False): """Creates new md datum, trajectory, interpretation and feature objects for each well in an ascii file. arguments: crs_uuid (uuid.UUID): the unique identifier of the coordinate reference system applicable to the x,y,z data; if None, a default crs will be created, making use of the length_uom and z_inc_down arguments trajectory_file (string): the path of the ascii file holding the well trajectory data to be loaded comment_character (string, default '#'): character deemed to introduce a comment in the trajectory file space_separated_instead_of_csv (boolean, default False): if True, the columns in the trajectory file are space separated; if False, comma separated well_col (string, default 'WELL'): the heading for the column containing well names md_col (string, default 'MD'): the heading for the column containing measured depths x_col (string, default 'X'): the heading for the column containing X (usually easting) data y_col (string, default 'Y'): the heading for the column containing Y (usually northing) data z_col (string, default 'Z'): the heading for the column containing Z (depth or elevation) data length_uom (string, default 'm'): the units of measure for the measured depths; should be 'm' or 'ft' md_domain (string, optional): the source of the original deviation data; may be 'logger' or 'driller' drilled (boolean, default False): True should be used for wells that have been drilled; False otherwise (planned, proposed, or a location being studied) z_inc_down (boolean, default True): indicates whether z values increase with depth; only used in the creation of a default coordinate reference system; ignored if crs_uuid is not None returns: tuple of lists of objects: (feature_list, interpretation_list, trajectory_list, md_datum_list), notes: ascii file must be table with first line being column headers, with columns for WELL, MD, X, Y & Z; actual column names can be set with optional arguments; all the objects are added to the model, with array data being written to the hdf5 file for the trajectories; the md_domain and drilled values are stored in the RESQML metadata but are only for human information and do not generally affect computations """ assert md_col and x_col and y_col and z_col md_col = str(md_col) x_col = str(x_col) y_col = str(y_col) z_col = str(z_col) if crs_uuid is None: crs_uuid = model.crs_uuid assert crs_uuid is not None, 'coordinate reference system not found when trying to add wells' try: df = pd.read_csv(trajectory_file, comment=comment_character, delim_whitespace=space_separated_instead_of_csv) if df is None: raise Exception except Exception: log.error('failed to read ascii deviation survey file: ' + str(trajectory_file)) raise if well_col and well_col not in df.columns: log.warning('well column ' + str(well_col) + ' not found in ascii trajectory file: ' + str(trajectory_file)) well_col = None if well_col is None: for col in df.columns: if str(col).upper().startswith('WELL'): well_col = str(col) break else: well_col = str(well_col) assert well_col unique_wells = set(df[well_col]) if len(unique_wells) == 0: log.warning('no well data found in ascii trajectory file: ' + str(trajectory_file)) # note: empty lists will be returned, below feature_list = [] interpretation_list = [] trajectory_list = [] md_datum_list = [] for well_name in unique_wells: log.debug('importing well: ' + str(well_name)) # create single well data frame (assumes measured depths increasing) well_df = df[df[well_col] == well_name] # create a measured depth datum for the well and add as part first_row = well_df.iloc[0] if first_row[md_col] == 0.0: md_datum = MdDatum(model, crs_uuid=crs_uuid, location=(first_row[x_col], first_row[y_col], first_row[z_col])) else: md_datum = MdDatum(model, crs_uuid=crs_uuid, location=(first_row[x_col], first_row[y_col], 0.0)) # sea level datum md_datum.create_xml(title=str(well_name)) md_datum_list.append(md_datum) # create a well feature and add as part feature = rqo.WellboreFeature(model, feature_name=well_name) feature.create_xml() feature_list.append(feature) # create interpretation and add as part interpretation = rqo.WellboreInterpretation(model, is_drilled=drilled, wellbore_feature=feature) interpretation.create_xml(title_suffix=None) interpretation_list.append(interpretation) # create trajectory, write arrays to hdf5 and add as part trajectory = Trajectory(model, md_datum=md_datum, data_frame=well_df, length_uom=length_uom, md_domain=md_domain, represented_interp=interpretation, well_name=well_name) trajectory.write_hdf5() trajectory.create_xml(title=well_name) trajectory_list.append(trajectory) return (feature_list, interpretation_list, trajectory_list, md_datum_list)