Source code for resqpy.olio.read_nexus_fault

"""read_nexus_fault.py: functions for reading Nexus fault definition data from an ascii file."""

# Nexus is a registered trademark of the Halliburton Company

import logging

log = logging.getLogger(__name__)

import os
import re
import unicodedata
import numpy as np
import pandas as pd


[docs]def load_nexus_fault_mult_table_from_list(file_as_list): """Reads a Nexus (!) format list of file contents containing one or more MULT keywords and returns a dataframe with the MULT rows.""" def is_number(s): try: float(s) return True except ValueError: pass try: unicodedata.numeric(s) return True except (TypeError, ValueError): pass return False dfs = [] num_tables = 0 ISTABLE = False ISRECORD = False name = face = None grid = 'ROOT' ## nexus default grid face_dict = {'TX': 'I', 'TY': 'J', 'TZ': 'K', 'TI': 'I', 'TJ': 'J', 'TK': 'K'} chunks = [] for line in file_as_list: if len(line.strip()): if (not line.strip()[0] == '!') & (not line.strip()[0] == 'C'): line = line.partition('!')[0] # removing trailing comments # line = line.partition('C')[0] # removing trailing comments tokens = line.split() if ISTABLE: if is_number(tokens[0]): ISRECORD = True if ISRECORD and (not is_number(tokens[0])): data = chunks[0:] d_elems = np.array([np.array(data[i].split()) for i in range(len(data))]) # fill empty elements with zero lens = np.array([len(i) for i in d_elems]) # Mask of valid places in each row mask = np.arange(lens.max()) < lens[:, None] # Setup output array and put elements from data into masked positions outdata = np.zeros(mask.shape, dtype = d_elems.dtype) outdata[mask] = np.concatenate(d_elems) df = pd.DataFrame(outdata) for column in df.columns: df[column] = pd.to_numeric(df[column], errors = 'ignore') df.columns = ['i1', 'i2', 'j1', 'j2', 'k1', 'k2', 'mult'] df['grid'] = grid df['name'] = name df['face'] = face dfs.append(df) num_tables += 1 ISTABLE = False ISRECORD = False chunks = [] if ISTABLE: if re.match("(.*)GRID(.*)", tokens[0]): if len(tokens) > 0: grid = tokens[1] elif re.match("(.*)FNAME(.*)", tokens[0]): if len(tokens) > 0: name = tokens[1] else: if re.match(r"^MULT$", tokens[0]): ISTABLE = False ISRECORD = False chunks = [] else: chunks.append(line.strip()) if re.match(r"^MULT$", tokens[0]): if len(tokens) > 0: face = face_dict[tokens[1]] if 'MINUS' in tokens: face += '-' # indicates data apply to 'negative' faces of specified cells grid = 'ROOT' # nexus default name = 'NONE' ISTABLE = True else: if ISTABLE: if ISRECORD: data = chunks[0:] d_elems = np.array([np.array(data[i].split()) for i in range(len(data))]) # fill empty elements with zero lens = np.array([len(i) for i in d_elems]) # Mask of valid places in each row mask = np.arange(lens.max()) < lens[:, None] # Setup output array and put elements from data into masked positions outdata = np.zeros(mask.shape, dtype = d_elems.dtype) outdata[mask] = np.concatenate(d_elems) df = pd.DataFrame(outdata) for column in df.columns: df[column] = pd.to_numeric(df[column], errors = 'ignore') df.columns = ['i1', 'i2', 'j1', 'j2', 'k1', 'k2', 'mult'] df['grid'] = grid df['name'] = name df['face'] = face dfs.append(df) num_tables += 1 ISTABLE = False ISRECORD = False chunks = [] if not dfs: return pd.DataFrame() fault_df = pd.concat(dfs).reset_index(drop = True) convert_dict = {'i1': int, 'i2': int, 'j1': int, 'j2': int, 'k1': int, 'k2': int, 'mult': float} fault_df = fault_df.astype(convert_dict) return fault_df
[docs]def load_nexus_fault_mult_table(file_name): """Reads a Nexus (!) format file containing one or more MULT keywords and returns a dataframe with the MULT rows.""" with open(file_name) as f: file_as_list = f.readlines() return load_nexus_fault_mult_table_from_list(file_as_list)