Source code for deepfield.field.rock

"""Rock component."""
import warnings
import numpy as np
import matplotlib.pyplot as plt

from .base_spatial import SpatialComponent
from .decorators import apply_to_each_input, state_check, ndim_check
from .plot_utils import show_slice_static, show_slice_interactive
from .utils import rolling_window, get_single_path
from .parse_utils import read_ecl_bin


[docs] class Rock(SpatialComponent): """Rock component of geological model.""" def _load_ecl_binary(self, path_to_results, attrs, basename, logger=None): path = get_single_path(path_to_results, basename + '.INIT', logger) if path is None: return sections = read_ecl_bin(path, attrs, logger=logger) for k in ['PORO', 'PERMX', 'PERMY', 'PERMZ', "KRW"]: if (k in attrs) and (k in sections): setattr(self, k, sections[k]) self.state.binary_attributes.append(k) @apply_to_each_input def _to_spatial(self, attr, inplace=True): """Spatial order 'F' transformations.""" dimens = self.field.grid.dimens return self.reshape(attr=attr, newshape=dimens, order='F', inplace=inplace) def _make_data_dump(self, attr, fmt=None, float_dtype=None, **kwargs): """Prepare data for dump.""" if fmt.upper() != 'HDF5': return super()._make_data_dump(attr, fmt=fmt, **kwargs) data = self.ravel(attr=attr, inplace=False) return data if float_dtype is None else data.astype(float_dtype)
[docs] @apply_to_each_input def pad_na(self, attr, fill_na=0., inplace=True): """Add dummy cells into the rock vector in the positions of non-active cells if necessary. Parameters ---------- attr: str, array-like Attributes to be padded with non-active cells. actnum: array-like of type bool Vector representing a mask of active and non-active cells. fill_na: float Value to be used as filler. inplace: bool Modify сomponent inplace. Returns ------- output : component if inplace else padded attribute. """ data = getattr(self, attr) if np.prod(data.shape) == np.prod(self.field.grid.dimens): return self if inplace else data actnum = self.field.grid.actnum if data.ndim > 1: raise ValueError('Data should be raveled before padding.') padded_data = np.full(shape=(actnum.size,), fill_value=fill_na, dtype=float) padded_data[actnum.ravel(order='F')] = data if inplace: setattr(self, attr, padded_data) return self return padded_data
[docs] @apply_to_each_input def strip_na(self, attr, inplace=True): """Remove non-active cells from the rock vector. Parameters ---------- attr: str, array-like Attributes to be stripped inplace: bool Modify сomponent inplace. Returns ------- output : component if inplace else stripped attribute. """ if self.state.spatial and inplace: raise ValueError('Inplace is not allowed in spatial state.') data = self.ravel(attr, inplace=False) actnum = self.field.grid.actnum if data.size == np.sum(actnum): return self if inplace else data stripped_data = data[actnum.ravel(order='F')] if inplace: setattr(self, attr, stripped_data) return self return stripped_data
[docs] def show_histogram(self, attr, **kwargs): """Show properties distribution. Parameters ---------- attr : str Attribute to compute the histogram. kwargs : misc Any additional named arguments to ``plt.hist``. Returns ------- plot : Histogram plot. """ data = getattr(self, attr) try: actnum = self.field.grid.actnum data = data * actnum except AttributeError: pass plt.hist(data.ravel(), **kwargs) plt.show() return self
[docs] @state_check(lambda state: state.spatial) @ndim_check(3) def show_slice(self, attr, i=None, j=None, k=None, figsize=None, **kwargs): """Visualize slices of 3D array. If no slice is specified, all 3 slices will be shown with interactive slider widgets. Parameters ---------- attr : str Attribute to show. i : int or None, optional Slice along x-axis to show. j : int or None, optional Slice along y-axis to show. k : int or None, optional Slice along z-axis to show. figsize : array-like, optional Output plot size. kwargs : dict, optional Additional keyword arguments for plot. """ data = getattr(self, attr) try: actnum = self.field.grid.actnum data = data * actnum except AttributeError: pass if np.all([i is None, j is None, k is None]): show_slice_interactive(self, attr, figsize=figsize, **kwargs) else: show_slice_static(self, attr, i=i, j=j, k=k, figsize=figsize, **kwargs) return self
[docs] @state_check(lambda state: state.spatial) @apply_to_each_input def upscale(self, attr, factors, weights=None): """Upscale properties. Parameters ---------- attr : str, list of str Attributes to be upscaled. factors : tuple, int Scale factors along each axis. If int, factors are the same for each axis. weights : ndarray, same shape as `attr`, optional Cell weights. Returns ------- out : ndarray Upscaled properties. """ factors = np.atleast_1d(factors) if factors.size == 1: factors = np.repeat(factors, 3) data = getattr(self, attr) volumes = self.field.grid.cell_volumes binned_weights = rolling_window(volumes, factors) if weights is not None: binned_weights *= rolling_window(weights, factors) out = rolling_window(data, factors) * binned_weights binned_weights = np.sum(binned_weights, axis=(-3, -2, -1)) zero_mask = np.isclose(binned_weights, 0) with warnings.catch_warnings(): warnings.simplefilter("ignore") out = np.sum(out, axis=(-3, -2, -1)) / binned_weights out[zero_mask] = 0 return out