Source code for adamspy.adripy.hole

"""Module for working with Adams Drill Hole (.hol) files
"""
import os
import copy
import thornpy
from . import TMPLT_ENV
from .utilities import read_TO_file, get_cdb_path, get_full_path

[docs]class DrillHole(): """An object with all the data necessary to write an Adams Drill hole (.hol) file. Parameters ---------- hole_name : str Name of the hole object Attributes ---------- parameters : dict Dictionary of parameters that make up an Adams Drill hole and would be found in an Adams Drill hole (.str) file. The keys of the dictionary are the parameter names that would be seen in the hole file and the values of the dictionary are the values that would be seen in the hole file. filename : str Name of the file that the hole object was read from or last written to. """ _SCALAR_PARAMETERS = [ 'Hole_Name', 'Length', 'Mass', 'Angle', 'Time' ] _DEFAULT_PARAMETER_SCALARS = { 'File_Type': 'hole', 'File_Version': 1.0, 'Length': 'foot', 'Mass': 'pound_mass', 'Angle': 'degrees', 'Time': 'seconds' } _TABLE_PARAMETERS = [ 'Centerline', 'Diameter', 'Wall_Contact', 'Wall_Friction' ] _DEFAULT_PARAMETER_TABLES = { 'Centerline': ((), (), ()), 'Diameter': ((), ()), 'Wall_Contact': ((-1,), (1e6,), (1e3,)), 'Wall_Friction': ((), (), (), (), ()) } _CDB_TABLE = 'holes.tbl' _EXT = 'hol' def __init__(self, hole_name, **kwargs): self.parameters = kwargs self.parameters['Hole_Name'] = hole_name self._apply_defaults() self.filename = '' def _apply_defaults(self): """ Applies defaults from class variables """ # Applies normal parameter defaults for scalar_parameter, value in self._DEFAULT_PARAMETER_SCALARS.items(): if scalar_parameter not in self.parameters: self.parameters[scalar_parameter] = copy.copy(value) # Applies defaults to all ramp parameters for table_parameter, table in self._DEFAULT_PARAMETER_TABLES.items(): self.parameters[table_parameter] = [list(tup) for tup in table] self.parameters['_' + table_parameter] = zip(*self.parameters[table_parameter])
[docs] @classmethod def read_from_file(cls, filename): """Reads an Adams Drill hole (.hol) file and sets :attr:`DrillHole.parameters` based on data in the file. Parameters ---------- filename : str Filename of an Adams Drill hole (.hol) file. """ # Read the TO data into a dictionary tiem_orbit_data = read_TO_file(get_full_path(filename)) # Create an empty hole object hole = cls('') # Extract the DrillHole parameters from the TO dictionary hole._get_params_from_TO_data(tiem_orbit_data) #pylint: disable=protected-access # Set the filename attribute hole.filename = get_cdb_path(filename) return hole
[docs] def write_to_file(self, directory=None, filename=None, cdb=None): """Writes an Adams Drill hole (.hol) file from the object. Parameters ---------- write_directory : str Directory in which to write the file. Defaults to current working directory. filename : str Name of the file to write. Defaults to self.parameters['Hole_Name'] cdb : str Name of the cdb in which to write the file. This argument overrides the write_directory. Raises ------ ValueError Raised if not all parameters have been defined. """ # Raise an error if the parameters can't be validated if not self.validate(): raise ValueError('The parameters could not be validated.') if directory is not None: # If the write_directory argument is passed if filename is None: # If the filename argument is not passed, set the filename # as the Hole_Name in the file filename = self.parameters['Hole_Name'] else: # If the filename argument is passed, strip the path and the # extension filename = os.path.split(filename)[-1].replace(f'.{self._EXT}','') # Set the filepath to the filename in the given directory filepath = get_full_path(os.path.join(directory, f'{filename}.{self._EXT}')) elif cdb is not None: # If the write_directory argument is not passed, but the cdb # argument is if filename is None: # If the filename argument is not passed, set the filename # as the Hole_Name in the file filename = self.parameters['Hole_Name'] else: # If the filename argument is passed, strip the path and the # extension filename = os.path.split(filename)[-1].replace(f'.{self._EXT}','') # Set the filepath to the file in the cdb filepath = get_full_path(os.path.join(f'<{cdb}>', self._CDB_TABLE, f'{filename}.{self._EXT}')) elif filename is not None: # If the filename argument is given filepath = get_full_path(thornpy.utilities.convert_path(filename)) else: # Raise an error if none of the arguments are provided raise ValueError('Ether directory, filename, or cdb must be passed.') hole_template = TMPLT_ENV.from_string(open(os.path.join(os.path.dirname(__file__), 'templates', f'template.{self._EXT}')).read()) with open(filepath, 'w') as fid: fid.write(hole_template.render(self.parameters)) self.filename = get_cdb_path(filepath)
[docs] def validate(self): """ Determines if all parameters have been set Returns ------- bool `True` if all parameters have been set. Otherwise `False`. """ validated = True # Check that all parameters exist in the self.parameters dictionary for param_name in self._SCALAR_PARAMETERS: if param_name not in self.parameters: validated = False for param_name in self._TABLE_PARAMETERS: if not all([elem for elem in self.parameters[param_name]]): validated = False return validated
[docs] def modify_table(self, param_name, md_start, md_end, new_values): """Modifies the table parameter with key `param_name` in the range defined by `md_start` and `md_end`. Note ---- Use this method to modify the following items in :attr:`DrillHole.parameters`: * Centerline * Diameter * Wall_Contact * Wall_Friction **DO NOT MODIFY OR SET THESE ITEMS DIRECTLY.** They will not write correctly when the :meth:`DrillHole.write_to_file` is called. Note ---- This method does not work on hole tables that use constant property notation (i.e. single row with measured depth equal to -1) Examples -------- Modifying the wall contact parameters between 3000 and 4000 feet. >>> drill_hole.parameters['Wall_Contact'] [[0, 1000, 2000, 3000, 4000], [1.0e5, 1.0e5, 1.0e5, 1.0e6, 1.0e6], [1.0e3, 1.0e3, 1.0e3, 2.0e3, 2.0e3]] >>> md_start = 3000 >>> md_end = 4000 >>> k = 1.0e7 >>> c = 3.0e3 >>> contact = [k, c] >>> drill_hole.modify_table('Wall_Contact', md_start, md_end, contact) >>> drill_hole.parameters['Wall_Contact'] [[0, 1000, 2000, 3000, 4000], [1.0e5, 1.0e5, 1.0e5, 1.0e7, 1.0e7], [1.0e3, 1.0e3, 1.0e3, 3.0e3, 3.0e3]] Modifying the dynamic friction coefficient between 3000 and 4000 feet, but leaving the other friction parameters alone. >>> drill_hole.parameters['Wall_Friction'] [[0, 1000, 2000, 3000, 4000], [0.9, 0.9, 0.9, 0.9, 0.9], [0.15, 0.15, 0.15, 0.15, 0.15], [0.3, 0.3, 0.3, 0.3, 0.3], [0.3, 0.3, 0.3, 0.3, 0.3]] >>> md_start = 3000 >>> md_end = 4000 >>> mu_d = 0.6 >>> friction = [None, None, mu_d, None] >>> drill_hole.modify_table('Wall_Friction', md_start, md_end, friction) [[0, 1000, 2000, 3000, 4000], [0.9, 0.9, 0.9, 0.9, 0.9], [0.15, 0.15, 0.15, 0.15, 0.15], [0.6, 0.6, 0.6, 0.6, 0.6], [0.3, 0.3, 0.3, 0.3, 0.3]] Parameters ---------- param_name : str Name of the parameter to be modified. Must match a key in `DrillHole._TABLE_PARAMETERS` md_start : float Start of measured depth range to be modified. md_end : float End of measured depth range to be modified. new_values : :obj:`list` of :obj:`float`s New parameter values. The order of this list must match the order in which these parameters are listed in the table in the Adams Drill Hole (.hol) file. """ # Raise an error if this isn't an acceptable parameter if param_name not in self._TABLE_PARAMETERS: raise ValueError(f'{param_name} is not a DrillHole table.') # Loop through the rows of the parameter for i, line in enumerate(self.parameters[f'_{param_name}']): # for each row if md_start <= line[0] <= md_end: # if that md for that row is withing the specified range for j, new_value in enumerate(new_values): # For each parameter (e.g. mu_s, v_s, mu_d, v_d) if new_value is not None: # If the new value is given, modify the value in that line self.parameters[param_name][j+1][i] = new_value elif line[0] > md_end: # If passed the specified md range, break the loop break # Re-zip the parameter self.parameters[f'_{param_name}'] = zip(*self.parameters[param_name])
[docs] def set_table(self, param_name, values): """Sets the table parameter with the key `param_name`. Note ---- Use this method to set the following items in :attr:`DrillHole.parameters`: * Centerline * Diameter * Wall_Contact * Wall_Friction **DO NOT MODIFY OR SET THESE ITEMS DIRECTLY.** They will not write correctly when the :meth:`DrillHole.write_to_file` is called. Example ------- >>> md = [0, 1000, 2000, 3000, 4000] >>> mu_s = [0.7, 0.7, 0.7, 0.8, 0.8] >>> v_s = [0.1, 0.1, 0.1, 0.1, 0.1] >>> mu_d = [0.5, 0.5, 0.5, 0.6, 0.6] >>> v_d = [0.2, 0.2, 0.2, 0.2, 0.2] >>> friction = [md, mu_s, v_s, mu_d, v_d] >>> drill_hole.set_table('Wall_Friction', friction) >>> drill_hole.parameters['Wall_Friction'] [[0, 1000, 2000, 3000, 4000], [0.7, 0.7, 0.7, 0.8, 0.8], [0.1, 0.1, 0.1, 0.1, 0.1], [0.5, 0.5, 0.5, 0.6, 0.6], [0.2, 0.2, 0.2, 0.2, 0.2]] Parameters ---------- param_name : str Name of the parameter to be modified. Must match a key in `DrillHole._TABLE_PARAMETERS` values : :obj:`list` of :obj:`list`s Parameter values. The order of this list must match the order in which these parameters are listed in the table in the Adams Drill Hole (.hol) file. """ self.parameters[param_name] = copy.deepcopy(values) self.parameters[f'_{param_name}'] = zip(*self.parameters[param_name])
[docs] def get_tvd(self, md): """Returns the true vertical depth (TVD) corresponding to the provided measured depth (MD). Parameters ---------- md : float Measured depth for which true vertical depth should be returned Returns ------- float True vertical depth corresponding to the given measured depth """ tvd = md return tvd
def _get_params_from_TO_data(self, tiem_orbit_data): """Reads the hole parameters out of a dictoinary of Tiem Orbit data generated by :func:`adamspy.adripy.utilities.read_TO_file` Parameters ---------- tiem_orbit_data : dict Dictionary of Tiem Orbit data Raises ------ ValueError An event parameter could not be found """ for param in self._SCALAR_PARAMETERS: # For each event parameter initialize a found flag found = False for block in tiem_orbit_data: # For each block in the TO file if param.lower() in tiem_orbit_data[block]: # If the parameter is in this block, set the parameter and break the loop self.parameters[param] = tiem_orbit_data[block][param.lower()] found = True break else: # If the parameter is not in this block, find all the sub blocks # and look for the parameter inside each sub block sub_blocks = [header for header in tiem_orbit_data[block] if isinstance(tiem_orbit_data[block][header], dict)] for sub_block in sub_blocks: # For each sub_block in the block if param.lower() in tiem_orbit_data[block][sub_block]: # If the parameter is in the sub block, set the parameter and break the loop self.parameters[param] = tiem_orbit_data[block][sub_block][param.lower()] found = True break if found: break # Raise a value error if the parameter isn't found. if not found: raise ValueError(f'{param} not found!') for param in self._TABLE_PARAMETERS: # For each event parameter initialize a found flag found = False blocks = [header for header in tiem_orbit_data if isinstance(tiem_orbit_data[header], dict)] for block in blocks: # For each block in the TO file, get the sub blocks sub_blocks = [header for header in tiem_orbit_data[block] if isinstance(tiem_orbit_data[block][header], dict)] if param.upper()==block and block=='CENTERLINE': # If the parameter is CENTERLINE and so is the current block self.parameters[param][0] = tiem_orbit_data[block]['north'] self.parameters[param][1] = tiem_orbit_data[block]['east'] self.parameters[param][2] = tiem_orbit_data[block]['true_depth'] found = True break elif param.upper()==block and block=='DIAMETER': # If the parameter is DIAMETER and so is the current block self.parameters[param][0] = tiem_orbit_data[block]['measured_depth'] self.parameters[param][1] = tiem_orbit_data[block]['diameter'] found = True break elif param.upper()==block and block=='WALL_CONTACT': # If the parameter is WALL_CONTACT and so is the current block self.parameters[param][0] = tiem_orbit_data[block]['measured_depth'] self.parameters[param][1] = tiem_orbit_data[block]['k'] self.parameters[param][2] = tiem_orbit_data[block]['c'] found = True break elif param.upper()==block and block=='WALL_FRICTION': # If the parameter is WALL_FRICTION and so is the current block self.parameters[param][0] = tiem_orbit_data[block]['measured_depth'] self.parameters[param][1] = tiem_orbit_data[block]['staticmu'] self.parameters[param][2] = tiem_orbit_data[block]['staticv'] self.parameters[param][3] = tiem_orbit_data[block]['dynamicmu'] self.parameters[param][4] = tiem_orbit_data[block]['dynamicv'] found = True break if not found: raise ValueError(f'{param} not found!') self.parameters['_' + param] = zip(*self.parameters[param])