Source code for wtools.mesh

"""This provides a class for discretizing data in a convienant way that makes
sense for our spatially referenced data/models.
"""

__all__ = [
    'Grid',
]

__displayname__ = 'Mesh Tools'

import numpy as np
import pandas as pd
import properties
import discretize

from .plots import display
from .fileio import GridFileIO


def get_data_range(data):
    """Get the data range for a given ndarray"""
    dmin = np.nanmin(data)
    dmax = np.nanmax(data)
    return (dmin, dmax)



[docs]class Grid(discretize.TensorMesh, GridFileIO): """ A data structure to store a model space discretization and different attributes of that model space. Example: >>> import wtools >>> import numpy as np >>> models = { 'rand': np.random.random(1000).reshape((10,10,10)), 'spatial': np.arange(1000).reshape((10,10,10)), } >>> grid = wtools.Grid(models=models) >>> grid.validate() # Make sure the data object was created successfully True Note: See Jupyter notebooks under the ``examples`` directory """ def __init__(self, h=None, x0=(0.,0.,0.), models=None, **kwargs): if models is not None: self.models = models if h is None: h = [] shp = list(models.values())[0].shape # Now create tensors if not present if len(shp) > 0: h.append(np.ones(shp[0])) if len(shp) > 1: h.append(np.ones(shp[1])) if len(shp) > 2: h.append(np.ones(shp[2])) discretize.TensorMesh.__init__(self, h=h, x0=x0, **kwargs) models = properties.Dictionary( 'The volumetric data as a 3D NumPy arrays in <X,Y,Z> or <i,j,k> ' + 'coordinates. Each key value pair represents a different model for ' + 'the gridded model space. Keys will be treated as the string name of ' + 'the model.', key_prop=properties.String('Model name'), value_prop=properties.Array( 'The volumetric data as a 3D NumPy array in <X,Y,Z> or <i,j,k> coordinates.', shape=('*','*','*')), required=False ) @properties.validator def _validate_models(self): # Check the models if self.models is not None: shp = list(self.models.values())[0].shape for k, d in self.models.items(): if d.shape != shp: raise RuntimeError('Validation Failed: dimesnion mismatch between models.') return True @property def keys(self): """List of the string names for each of the models""" return list(self.models.keys()) @property def shape(self): """3D shape of the grid (number of cells in all three directions)""" return ( self.nCx, self.nCy, self.nCz) @property def bounds(self): """The bounds of the grid""" grid = self.gridN try: x0, x1 = np.min(grid[:,0]), np.max(grid[:,0]) except: x0, x1 = 0., 0. try: y0, y1 = np.min(grid[:,1]), np.max(grid[:,1]) except: y0, y1 = 0., 0. try: z0, z1 = np.min(grid[:,2]), np.max(grid[:,2]) except: z0, z1 = 0., 0. return (x0,x1, y0,y1, z0,z1)
[docs] def get_data_range(self, key): """Get the data range for a given model""" data = self.models[key] return get_data_range(data)
[docs] def equal(self, other): """Compare this Grid to another Grid""" return properties.equal(self, other)
def __str__(self): """Print this onject as a human readable string""" self.validate() fmt = ["<%s instance>" % (self.__class__.__name__)] fmt.append(" Shape: {}".format(self.shape)) fmt.append(" Origin: {}".format(tuple(self.x0))) bds = self.bounds fmt.append(" X Bounds: {}".format((bds[0], bds[1]))) fmt.append(" Y Bounds: {}".format((bds[2], bds[3]))) fmt.append(" Z Bounds: {}".format((bds[4], bds[5]))) if self.models is not None: fmt.append(" Models: ({})".format(len(self.models.keys()))) for key, val in self.models.items(): dl, dh = self.get_data_range(key) fmt.append(" '{}' ({}): ({:.3e}, {:.3e})".format(key, val.dtype, dl, dh)) return '\n'.join(fmt) def __repr__(self): return self.__str__() def _repr_html_(self): self.validate() fmt = "" if self.models is not None: fmt += "<table>" fmt += "<tr><th>Grid Attributes</th><th>Models</th></tr>" fmt += "<tr><td>" fmt += "\n" fmt += "<table>\n" fmt += "<tr><th>Attribute</th><th>Values</th></tr>\n" row = "<tr><td>{}</td><td>{}</td></tr>\n" fmt += row.format("Shape", self.shape) fmt += row.format('Origin', tuple(self.x0)) bds = self.bounds fmt += row.format("X Bounds", (bds[0], bds[1])) fmt += row.format("Y Bounds", (bds[2], bds[3])) fmt += row.format("Z Bounds", (bds[4], bds[5])) num = 0 if self.models is not None: num = len(self.models.keys()) fmt += row.format("Models", num) fmt += "</table>\n" fmt += "\n" if self.models is not None: fmt += "</td><td>" fmt += "\n" fmt += "<table>\n" row = "<tr><th>{}</th><th>{}</th><th>{}</th><th>{}</th></tr>\n" fmt += row.format("Name", "Type", "Min", "Max") row = "<tr><td>{}</td><td>{}</td><td>{:.3e}</td><td>{:.3e}</td></tr>\n" for key, val in self.models.items(): dl, dh = self.get_data_range(key) fmt += row.format(key, val.dtype, dl, dh) fmt += "</table>\n" fmt += "\n" fmt += "</td></tr> </table>" return fmt def __getitem__(self, key): """Get a model of this grid by its string name""" return self.models[key]
[docs] def to_data_frame(self, order='C'): """Returns the models in this Grid to a Pandas DataFrame with all arrays flattened in the specified order. A header attribute is added to the DataFrame to specified the grid extents. Much metadata is lost in this conversion. """ self.validate() tits = self.models.keys() data = {k: v.flatten(order=order) for k, v in self.models.items()} df = pd.DataFrame.from_dict(data) df.header = '{} {} {}'.format(self.nCx, self.nCy, self.nCz) return df
[docs] def plot_3d_slicer(self, key, **kwargs): model = self.models[key] return discretize.TensorMesh.plot_3d_slicer(self, model, **kwargs)
[docs] def plotSlice(self, key, **kwargs): """Plots a 2D slice of the mesh Args: key (str): the model name to plot Note: See the `discretize code docs`_ for more details. .. _discretize code docs: http://discretize.simpeg.xyz/en/latest/content/mesh_tensor.html?highlight=plotSlice#discretize.View.TensorView.plotSlice """ return discretize.TensorMesh.plotSlice(self, v=self.models[key], **kwargs)
@property def models_flat(self): """Returns flattened model dictionary in Fortran ordering""" return {k:v.flatten(order='F') for k,v in self.models.items()}
[docs] def toVTK(self): return discretize.TensorMesh.toVTK(self, models=self.models_flat)
[docs] def writeVTK(self): return discretize.TensorMesh.writeVTK(self, models=self.models_flat)