from __future__ import print_function
__all__ = [
'MeshSpecifications',
'RockType',
'USNT',
]
__displayname__ = 'Specifications'
import numpy as np
import pandas as pd
import properties
import copy
import warnings
import time
import discretize
class MaterialComponent(properties.HasProperties):
"""Defines the extent of a material component in the grid"""
i = properties.Array('Range of element indices in the X-direction',
shape=(2,),
dtype=int,)
j = properties.Array('Range of element indices in the Y-direction',
shape=(2,),
dtype=int,)
k = properties.Array('Range of element indices in the Z-direction',
shape=(2,),
dtype=int,)
[docs]class MeshSpecifications(properties.HasProperties):
"""specifies the mesh geometry, element material types and names as well as
dual permeability parameters and radiation parameters.
"""
coord = properties.String(
'specifies the type of mesh that will be generated',
change_case='lower',
)
@properties.validator
def _validate_mesh_type(self):
"""Check mesh type is one of two valid options: 'rect' or 'cylind'"""
if self.coord not in ['rect', 'cylind']:
raise properties.ValidationError("Mesh type ('{}') not a valid mesh type. Only 'rect' and 'cylind' are suppurted".format(self.coord))
return True
down = properties.Array(
'Vector orientation of down direction and in the direction of ' +
'the gravity vector.',
dtype=float,
shape=(3,))
dx = properties.Array(
"widths of the mesh in the X-dimension",
dtype=float,
shape=("*",))
dy = properties.Array(
"widths of the mesh in the Y-dimension",
dtype=float,
shape=("*",))
dz = properties.Array(
"widths of the mesh in the Z-dimension",
dtype=float,
shape=("*",))
mat = properties.Dictionary(
'The materials int the model space.',
key_prop=properties.String('The element name prefix', change_case='lower'),
value_prop=properties.Dictionary('The material types',
key_prop=properties.String('The material type. Must have a corresponding entry in the material type the ``rocktab``.'),
value_prop=properties.List('ther material components', MaterialComponent)
)
)
@property
def nx(self):
return len(self.dx)
@property
def ny(self):
return len(self.dy)
@property
def nz(self):
return len(self.dz)
@property
def nC(self):
return (self.nx * self.ny * self.nz)
@property
def shape(self):
return (self.nx, self.ny, self.nz)
# Optional
#anistotrpoic
#wrap_around
#radcon
@staticmethod
def __pasrseCellList(line):
line_list = []
for seg in line:
if '*' in seg:
sp = seg.split('*')
seg_arr = np.ones((int(sp[0]),), dtype=float) * float(sp[1])
else:
seg_arr = np.array([float(seg)], dtype=float)
line_list.append(seg_arr)
return np.concatenate(line_list)
@classmethod
def _create(cls, values, validate=False):
if not isinstance(values, dict):
raise RuntimeError('Input values must be a dictionary')
start_time = time.time()
#print('Creating Mesh Specs...', end='\r')
props = cls()
mat = values.pop('mat')
save = copy.deepcopy(mat)
for k, v in values.items():
if k in cls._props:
# If the value is simple, set it!
if isinstance(cls._props[k], properties.String):
p = props._props.get(k)
props._set(k, p.from_json(v))
else:
# anything that isnt a mat is a list of floats
# make sure to fix any repetitve values us '*' notations
vals = MeshSpecifications.__pasrseCellList(v)
props._set(k, vals)
else:
#warnings.warn("({}:{}) property is not valid.".format(k, v))
pass
# Now handle the material matrix since all other parameters are set
for pref, comp in mat.items():
for name, indices in comp.items():
for idx, ind in enumerate(indices):
ind = [i.replace('nx', '%d'%(props.nx+1)) for i in ind]
ind = [i.replace('ny', '%d'%(props.ny+1)) for i in ind]
ind = [i.replace('nz', '%d'%(props.nz+1)) for i in ind]
# Now shift the indices by one because someone chose +1 indexing :(
ind = [int(i)-1 for i in ind]
indices[idx] = MaterialComponent(i=ind[0:2],j=ind[2:4],k=ind[4:6])
mat[pref][name] = indices
props._set('mat', mat)
# Reutrn the object
values['mat'] = save
#print('Created Mesh Specs in {} seconds.'.format(time.time() - start_time))
if validate:
start_time = time.time()
print('Validating Mesh Specs...', end='\r')
props.validate()
print('Validated Mesh Specs in {} seconds.'.format(time.time() - start_time))
return props
@property
def definitions(self):
"""Gets the ``mat_type`` definitions as integers to be matched with any
given rocktab file via the lookup table."""
mod = np.empty(self.shape, dtype=int)
lootbl = self.lookup_table.set_index('material')
for el_pref in self.mat.keys():
for mat_type in self.mat[el_pref].keys():
for mc in self.mat[el_pref][mat_type]:
mod[mc.i[0]:mc.i[1]+1,mc.j[0]:mc.j[1]+1,mc.k[0]:mc.k[1]+1] = lootbl.loc[mat_type]['id']
return mod.flatten(order='f')
@property
def injector(self):
mod = np.full(self.shape, False, dtype=bool)
for mat_type in self.mat['wb1'].keys():
for mc in self.mat['wb1'][mat_type]:
mod[mc.i[0]:mc.i[1]+1,mc.j[0]:mc.j[1]+1,mc.k[0]:mc.k[1]+1] = True
return mod.flatten(order='f')
@property
def materials(self):
mats = []
for el_pref in self.mat.keys():
for mat_type in self.mat[el_pref].keys():
mats.append(mat_type)
return list(set(mats))
@property
def lookup_table(self):
mats = self.materials
df = pd.DataFrame(data=mats, columns=['material'])
df['id'] = pd.factorize(df['material'])[0]
return df
[docs] def toTensorMesh(self):
return discretize.TensorMesh(h=[self.dx, self.dy, self.dz])
class Param(properties.HasProperties):
name = properties.String('The parameter name')
value = properties.Float('The parameter value')
class EqnParams(properties.HasProperties):
phase = properties.String('The phase')
equation = properties.String('Equation to use')
params = properties.List('The parameters', Param)
[docs]class RockType(properties.HasProperties):
mat_type = properties.String('The rock type name corresponding to ``MaterialComponent.mat_type``.')
Kd = properties.List('Dimensionless solid sorption coefficient of the component', Param)
KdFactor = properties.List('Dimensionless solid sorption coefficient of the component', Param)
tort = properties.List('The Tortuosity parameters per phase.', EqnParams)
pc = properties.List('The pressure parameters per phase.', EqnParams)
kr = properties.List('The relative permeability/saturation per phase.', EqnParams)
K0 = properties.Float('The Kx value')
K1 = properties.Float('The Kx value')
K2 = properties.Float('The Kx value')
porosity = properties.Float('The fractional porosity', min=0., max=1.)
solid_density = properties.Float('The solid density value.')
#tcond
#Cp
@classmethod
def _create(cls, mat_type, values, validate=True):
if not isinstance(values, dict):
raise RuntimeError('Input values must be a dictionary')
props = cls()
props.mat_type = mat_type
for k, v in values.items():
if k in cls._props:
# If the value is simple, set it!
if not isinstance(cls._props[k], properties.List):
p = props._props.get(k)
props._set(k, p.from_json(v))
elif k in ['Kd', 'KdFactor']:
# v is a dictionary of name value pairs of params
props._set(k, [Param(name=n, value=float(p)) for n,p in v.items()])
elif k in ['tort', 'pc', 'kr']:
phases = []
for phase, eqns in v.items():
opt = eqns.pop('option')
params = [Param(name=n, value=float(p)) for n,p in eqns.items()]
phases.append(EqnParams(phase=phase, equation=opt, params=params))
props._set(k, phases)
else:
#warnings.warn("({}:{}) property is not valid.".format(k, v))
pass
else:
print('WARN: {} not a property of this class.'.format(k))
if validate: props.validate()
return props
[docs]class USNT(MeshSpecifications):
"""The base object to instantiate."""
rocktab = properties.Dictionary('Porous medium properties', key_prop=properties.String('The material type name'), value_prop=RockType)
@property
def attributes(self):
atts = []
for k,v in RockType._props.items():
if isinstance(v, properties.Float):
atts.append(k)
return atts
[docs] def model(self, attribute):
"""Gets a rocktab attribute as a NumPy array ready for discretize or PVGeo"""
mod = np.empty(self.shape)
mod[:] = np.nan
for el_pref in self.mat.keys():
for mat_type in self.mat[el_pref].keys():
for mc in self.mat[el_pref][mat_type]:
mod[mc.i[0]:mc.i[1]+1,mc.j[0]:mc.j[1]+1,mc.k[0]:mc.k[1]+1] = self.rocktab[mat_type]._get(attribute)
return mod.flatten(order='f')
[docs] def allModels(self, dataframe=True):
"""Returns all attributes in a Pandas DataFrame"""
df = pd.DataFrame()
for key in self.attributes:
df[key] = self.model(key)
if dataframe:
return df
return df.to_dict()
[docs] def saveLithLookupTable(self, filename):
atts = ['K0', 'K1', 'K2', 'porosity', 'solid_density']
lootbl = self.lookup_table.set_index('material')
for at in atts:
lootbl[at] = np.full(len(lootbl), np.nan)
for k in lootbl.index:
lootbl.loc[k,at] = self.rocktab[k]._get(at)
lootbl = lootbl.reset_index()
lootbl = lootbl.set_index('id')
return lootbl.to_csv(filename, index_label='Index')