Source code for pygeopressure.basic.well

# -*- coding: utf-8 -*-
"""
a Well class utilizing pandas DataFrame and hdf5 storage

Created on Tue Dec 27 2016
"""
from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

from builtins import zip, open, bytes, str

__author__ = "yuhao"

import json
from collections import OrderedDict

import numpy as np
import pandas as pd

from pygeopressure.pressure.hydrostatic import hydrostatic_pressure
from pygeopressure.pressure.eaton import eaton
from pygeopressure.pressure.bowers import bowers_varu
from pygeopressure.pressure.multivariate import pressure_multivariate
from pygeopressure.velocity.extrapolate import normal
from .well_log import Log
from .well_storage import WellStorage


[docs]class Well(object): """ A class representing a well with information and log curve data. """
[docs] def __init__(self, json_file, hdf_path=None): """ Parameters ---------- json_file : str path to parameter file hdf_path : str, optional path to hdf5 file used to override the one written in json_file """ self.json_file = json_file self.hdf_file = None self.well_name = None self.loc = None self.kelly_bushing = None self.water_depth = None self.total_depth = None # self.trajectory = None self.data_frame = None self.params = None self.in_hdf = False self._parse_json() if hdf_path: self.hdf_file = hdf_path self._read_hdf()
def __str__(self): return "Well-{}".format(self.well_name) def _parse_json(self): try: with open(self.json_file) as fin: self.params = json.load(fin, object_pairs_hook=OrderedDict) self.hdf_file = self.params['hdf_file'] self.well_name = self.params['well_name'] self.loc = self.params['loc'] self.kelly_bushing = self.params['KB'] self.water_depth = self.params['WD'] self.total_depth = self.params['TD'] except KeyError as inst: print(inst) def _read_hdf(self): try: storage = WellStorage(self.hdf_file) self.data_frame = storage.get_well_data( self.well_name.lower().replace('-', '_')) self.in_hdf = True except Exception as inst: print(inst) @property def depth(self): """ depth values of the well Returns ------- numpy.ndarray """ if self.data_frame is not None: return np.around(self.data_frame['Depth(m)'].values, decimals=1) else: raise Exception("No dataframe found.") @property def logs(self): """ logs stored in this well Returns ------- list """ if self.data_frame is not None: temp = [item.strip(')').split('(')[0] \ for item in self.data_frame.keys()] temp.remove('Depth') return temp else: return [] @property def unit_dict(self): """ properties and their units """ temp_dict = { item.strip(')').split('(')[0]: item.strip(')').split('(')[-1] \ for item in self.data_frame.keys()} return {key: '' if temp_dict[key] == key else temp_dict[key] \ for key in temp_dict.keys()} @property def hydrostatic(self): """ Hydrostatic Pressure Returns ------- numpy.ndarray """ try: # temp_log = self.get_log('Overburden_Pressure') return hydrostatic_pressure( self.depth, kelly_bushing=self.kelly_bushing, depth_w=self.water_depth) except Exception as ex: print(ex.message) # print("No 'Overburden_Pressure' log found.")
[docs] def hydro_log(self): """ Returns ------- Log Hydrostatic Pressure """ hydro_log = Log() hydro_log.depth = np.array(self.depth) hydro_log.data = self.hydrostatic hydro_log.name = '{}_hydro'.format(self.well_name) hydro_log.descr = "Hydrostatic_Pressure" hydro_log.units = "MPa" return hydro_log
@property def lithostatic(self): """ Overburden Pressure (Lithostatic) Returns ------- numpy.ndarray """ try: temp_log = self.get_log('Overburden_Pressure') return np.array(temp_log.data) except KeyError: print("No 'Overburden_Pressure' log found.") @property def normal_velocity(self): """ Normal Velocity calculated using NCT stored in well Returns ------- numpy.ndarray """ try: a = self.params['nct']['a'] b = self.params['nct']['b'] # temp_log = self.get_log('Overburden_Pressure') return normal(x=self.depth, a=a, b=b) except KeyError: print("No 'Overburden_Pressure' log found.")
[docs] def get_log(self, logs, ref=None): """ Retreive one or several logs in well Parameters ---------- logs : str or list str names of logs to be retrieved ref : {'sea', 'kb'} depth reference, 'sea' references to sea level, 'kb' references to Kelly Bushing Returns ------- Log one or a list of Log objects """ log_list = list() output_list = list() if isinstance(logs, (bytes, str)): log_list.append(logs) elif isinstance(logs, list): log_list = logs for name in log_list: new_log = Log() new_log.name = name.lower()[:3] + '_' + \ self.well_name.lower().replace('-', '_') new_log.units = self.unit_dict[name] new_log.descr = name new_log.depth = np.array(self.data_frame['Depth(m)'].values) new_log.data = np.array(self.data_frame[ '{}({})'.format(name, self.unit_dict[name])].values) if ref == 'sea': shift = int(self.kelly_bushing // 0.1) shift_data = np.full_like(new_log.data, np.nan, dtype=np.double) shift_data[:-shift] = new_log.data[shift:] new_log.data = shift_data output_list.append(new_log) if isinstance(logs, (bytes, str)): return output_list[0] else: return output_list
[docs] def add_log(self, log, name=None, unit=None): """ Add new Log to current well Parameters ---------- log : Log log to be added name : str, optional name for the newly added log, None, use log.name unit : str, optional unit for the newly added log, None, use log.unit """ log_name = log.descr.replace(' ', '_') log_unit = log.units if name is not None: log_name = name if unit is not None: log_unit = unit if log_name not in self.logs: temp_dataframe = pd.DataFrame( data={ 'Depth(m)':log.depth, '{}({})'.format( log_name, log_unit): log.data}) self.data_frame = self.data_frame.join( temp_dataframe.set_index("Depth(m)"), on="Depth(m)") else: raise Warning("{} already exists in well {}".format( log_name, self.well_name))
[docs] def drop_log(self, log_name): """ delete a Log in current Well Parameters ---------- log_name : str name of the log to be deleted """ if log_name in self.logs: log = self.get_log(log_name) col = "{}({})".format(log.descr.replace(' ', '_'), log.units) self.data_frame = self.data_frame.drop(col, 1) else: print("no log named {}".format(log_name))
[docs] def rename_log(self, log_name, new_log_name): """ Parameters ---------- log_name : str log name to be replaced new_log_name : str """ if log_name in self.logs: log = self.get_log(log_name) old_str = "{}({})".format(log.descr.replace(' ', '_'), log.units) new_str = "{}({})".format(new_log_name.replace(' ', '_'), log.units) self.data_frame = self.data_frame.rename(index=str, columns={old_str: new_str})
[docs] def update_log(self, log_name, log): """ Update well log already in current well with a new Log Parameters ---------- log_name : str name of the log to be replaced in current well log : Log Log to replace """ old_log = self.get_log(log_name) if old_log.depth == log.depth: self.data_frame["{}({})".format( old_log.descr.replace(' ', '_'), old_log.units)] = log.data else: raise Warning("Mismatch")
[docs] def to_las(self, file_path, logs_to_export=None, full_las=False, null_value=1e30): """ Export logs to LAS or pseudo-LAS file Parameters ---------- file_path : str output file path logs_to_export : list of str Log names to be exported, None export all logs full_las : bool True, export LAS header; False export only data hence psuedo-LAS null_value : scalar Null Value representation in output file. """ if logs_to_export is None: logs_to_export = self.logs keys_to_export = ["Depth(m)"] for log_name in logs_to_export: log = self.get_log(log_name) keys_to_export.append( "{}({})".format(log.descr.replace(' ', '_'), log.units)) self.data_frame.to_csv( file_path, sep="\t".encode('utf-8'), na_rep="{}".format(null_value), columns=keys_to_export, index=False)
[docs] def save_well_logs(self): """ Save current well logs to file """ try: storage = WellStorage(self.hdf_file) storage.update_well(self.well_name, self.data_frame) except Exception as inst: print(inst)
# Measured Presure ----------
[docs] def get_pressure(self, pres_key, ref=None, hydrodynamic=0, coef=False): """ Get Pressure Values or Pressure Coefficients Parameters ---------- pres_key : str Pressure data name ref : {'sea', 'kb'} depth reference, 'sea' references to sea level, 'kb' references to Kelly Bushing hydrodynamic : float return Pressure at depth deeper than this value coef : bool True - get pressure coefficient else get pressure value Returns ------- Log Log object containing Pressure or Pressure coefficients """ pres_to_get = None try: pres_to_get = self.params[pres_key] except KeyError: print("{}: Cannot find {}".format(self.well_name, pres_key)) return Log() hydro = hydrostatic_pressure(self.depth, kelly_bushing=self.kelly_bushing, depth_w=self.water_depth) depth = pres_to_get["depth"] coefficients = pres_to_get["coef"] pres = pres_to_get["data"] obp_depth = self.depth # obp_log.depth output_depth = np.array(depth) if coef is True: if not coefficients: # get pressure coefficients but no coefficients stored output_data = list() for dp, pr in zip(depth, pres): idx = np.searchsorted(self.depth, dp) output_data.append(pr / hydro[idx]) output_data = np.array(output_data) else: output_data = np.array(coefficients) elif coef is False: if pres: # get pressure values and values stored coefficients = pres hydro = np.ones(hydro.shape) output_data = list() # ouput_depth = list() for dp, co in zip(depth, coefficients): idx = np.searchsorted(obp_depth, dp) output_data.append(hydro[idx] * co) # ouput_depth.append(dp) output_data = np.array(output_data) else: raise Exception() mask = output_depth > hydrodynamic output_depth = output_depth[mask] output_data = output_data[mask] log = Log() if ref == 'sea': log.depth = output_depth - self.kelly_bushing else: log.depth = output_depth log.data = np.round(output_data, 4) return log
[docs] def get_pressure_normal(self): """ return pressure points within normally pressured zone. Returns ------- Log Log object containing normally pressured measurements """ # obp_log = self.get_log("Overburden_Pressure") hydro = hydrostatic_pressure(self.depth, kelly_bushing=self.kelly_bushing, depth_w=self.water_depth) depth = self.params["MP"] obp_depth = self.depth pres_data = list() for dp in depth: idx = np.searchsorted(obp_depth, dp) pres_data.append(hydro[idx]) log = Log() log.depth = depth log.data = pres_data return log
# Prediction ------------
[docs] def eaton(self, vel_log, obp_log=None, n=None, a=None, b=None): """ Predict pore pressure using Eaton method Parameters ---------- vel_log : Log velocity log obp_log : Log overburden pressure log n : scalar Eaton exponent Returns ------- Log a Log object containing calculated pressure. """ if isinstance(vel_log, (bytes, str)): vel_log.get_log(vel_log) velocity = np.array(vel_log.data) if obp_log is None: obp = self.lithostatic else: if isinstance(obp_log, (bytes, str)): obp_log.get_log(obp_log) obp = np.array(obp_log.data) try: n = self.params['n'] if n is None else n except KeyError: n = 3 if a is None or b is None: a = self.params['nct']['a'] b = self.params['nct']['b'] # log = Log() # log.depth = self.depth pres_eaton = eaton(hydrostatic=self.hydrostatic, lithostatic=obp, n=n, v=velocity, vn=self.normal_velocity) # log.name = "pressure_eaton_{}".format( # self.well_name.lower().replace('-', '_')) # log.descr = "Pressure_Eaton" # log.units = "MPa" # log.prop_type = "PRE" log = Log.from_scratch( self.depth, pres_eaton, "pressure_eaton_{}".format(self.well_name.lower().replace('-', '_')), descr = "Pressure_Eaton", units = "MPa", prop_type = "PRE") return log
[docs] def bowers(self, vel_log, obp_log=None, a=None, b=None, u=None, vmax=None, start_depth=None, buf=20, end_depth=None, end_buffer=10): """ Predict pore pressure using Eaton method Parameters ---------- vel_log : Log velocity log obp_log : Log overburden pressure log a, b, u : float bowers model coefficients Returns ------- Log a Log object containing calculated pressure. """ if isinstance(vel_log, (bytes, str)): vel_log.get_log(vel_log) velocity = np.array(vel_log.data) if obp_log is None: obp = self.lithostatic else: if isinstance(obp_log, (bytes, str)): obp_log.get_log(obp_log) obp = np.array(obp_log.data) try: a = self.params['bowers']['A'] if a is None else a b = self.params['bowers']['B'] if b is None else b if not start_depth: start_depth = self.params['bowers']["start_depth"] if not end_depth: end_depth = self.params['bowers']["end_depth"] vmax = self.params['bowers']["vmax"] if vmax is None else vmax u = self.params['bowers']["U"] if u is None else u except KeyError as e: raise KeyError("Missing parameter: {}".format(e.args[0])) log = Log() log.depth = self.depth log.data = bowers_varu( velocity, obp, u, start_idx=vel_log.get_depth_idx(start_depth), a=a, b=b, vmax=vmax, buf=buf, end_idx=vel_log.get_depth_idx(end_depth), end_buffer=end_buffer) log.name = "pressure_bowers_{}".format( self.well_name.lower().replace('-', '_')) log.descr = "Pressure_Bowers" log.units = "MPa" log.prop_type = "PRE" return log
[docs] def multivariate(self, vel_log, por_log, vsh_log, obp_log=None, a0=None, a1=None, a2=None, a3=None, b=None): if isinstance(vel_log, (bytes, str)): vel_log.get_log(vel_log) vel = np.array(vel_log.data) if isinstance(por_log, (bytes, str)): por_log.get_log(por_log) phi = np.array(por_log.data) if isinstance(vsh_log, (bytes, str)): vsh_log.get_log(vsh_log) vsh = np.array(vsh_log.data) if isinstance(vsh_log, (bytes, str)): vsh_log.get_log(vsh_log) vsh = np.array(vsh_log.data) if obp_log is None: obp = self.lithostatic else: if isinstance(obp_log, (bytes, str)): obp_log.get_log(obp_log) obp = np.array(obp_log.data) try: a0 = self.params['multivariate']['a0'] if a0 is None else a0 a1 = self.params['multivariate']['a1'] if a1 is None else a1 a2 = self.params['multivariate']['a2'] if a2 is None else a2 a3 = self.params['multivariate']['a3'] if a3 is None else a3 b = self.params['multivariate']['B'] if b is None else b except KeyError as e: raise KeyError("Missing parameter: {}".format(e.args[0])) log = Log() log.depth = self.depth log.data = pressure_multivariate( obp, vel, phi, vsh, a0, a1, a2, a3, b, 1, 4600, start_idx=3000, end_idx=None) log.name = "pressure_mutlivariate_{}".format( self.well_name.lower().replace('-', '_')) log.descr = "Pressure_Multivariate" log.units = "MPa" log.prop_type = "PRE" return log
[docs] def plot_horizons(self, ax, color_dict=None): """ Plot horizons stored in well """ horizon_dict = None try: color_dict = self.params['color_dict'] except: pass try: horizon_dict = self.params['horizon'] except KeyError: print("no horizons") for key in horizon_dict.keys(): try: color = color_dict[key] except KeyError: color = 'black' except TypeError: color = 'black' ax.axhline(y=horizon_dict[key], color=color, linewidth=0.5, zorder=0) import matplotlib.transforms as transforms trans = transforms.blended_transform_factory( ax.transAxes, ax.transData) ax.text( s=key, x=0.8, y=horizon_dict[key], color=color, transform=trans, size=9) ax.figure.canvas.draw()
[docs] def save_params(self): """ Save edited parameters to well information file """ try: with open(self.json_file, "w") as fl: json.dump(self.params, fl, indent=4) except KeyError as inst: print(inst)