Files
simpeg/SimPEG/mesh/TensorMesh.py
T

440 lines
17 KiB
Python

import numpy as np
import scipy.sparse as sp
from BaseMesh import BaseMesh
from TensorView import TensorView
from DiffOperators import DiffOperators
from InnerProducts import InnerProducts
from SimPEG.utils import ndgrid, mkvc, spzeros, interpmat
class TensorMesh(BaseMesh, TensorView, DiffOperators, InnerProducts):
"""
TensorMesh is a mesh class that deals with tensor product meshes.
Any Mesh that has a constant width along the entire axis
such that it can defined by a single width vector, called 'h'.
::
hx = np.array([1,1,1])
hy = np.array([1,2])
hz = np.array([1,1,1,1])
mesh = TensorMesh([hx, hy, hz])
Example of a padded tensor mesh:
.. plot:: examples/mesh/plot_TensorMesh.py
"""
_meshType = 'TENSOR'
def __init__(self, h, x0=None):
super(TensorMesh, self).__init__(np.array([x.size for x in h]), x0)
assert len(h) == len(self.x0), "Dimension mismatch. x0 != len(h)"
for i, h_i in enumerate(h):
assert type(h_i) == np.ndarray, ("h[%i] is not a numpy array." % i)
assert len(h_i.shape) == 1, ("h[%i] must be a 1D numpy array." % i)
# Ensure h contains 1D vectors
self._h = [mkvc(x) for x in h]
def __str__(self):
outStr = ' ---- {0:d}-D TensorMesh ---- '.format(self.dim)
def printH(hx, outStr=''):
i = -1
while True:
i = i + 1
if i > hx.size:
break
elif i == hx.size:
break
h = hx[i]
n = 1
for j in range(i+1, hx.size):
if hx[j] == h:
n = n + 1
i = i + 1
else:
break
if n == 1:
outStr = outStr + ' {0:.2f},'.format(h)
else:
outStr = outStr + ' {0:d}*{1:.2f},'.format(n,h)
return outStr[:-1]
if self.dim == 1:
outStr = outStr + '\n x0: {0:.2f}'.format(self.x0[0])
outStr = outStr + '\n nCx: {0:d}'.format(self.nCx)
outStr = outStr + printH(self.hx, outStr='\n hx:')
pass
elif self.dim == 2:
outStr = outStr + '\n x0: {0:.2f}'.format(self.x0[0])
outStr = outStr + '\n y0: {0:.2f}'.format(self.x0[1])
outStr = outStr + '\n nCx: {0:d}'.format(self.nCx)
outStr = outStr + '\n nCy: {0:d}'.format(self.nCy)
outStr = outStr + printH(self.hx, outStr='\n hx:')
outStr = outStr + printH(self.hy, outStr='\n hy:')
elif self.dim == 3:
outStr = outStr + '\n x0: {0:.2f}'.format(self.x0[0])
outStr = outStr + '\n y0: {0:.2f}'.format(self.x0[1])
outStr = outStr + '\n z0: {0:.2f}'.format(self.x0[2])
outStr = outStr + '\n nCx: {0:d}'.format(self.nCx)
outStr = outStr + '\n nCy: {0:d}'.format(self.nCy)
outStr = outStr + '\n nCz: {0:d}'.format(self.nCz)
outStr = outStr + printH(self.hx, outStr='\n hx:')
outStr = outStr + printH(self.hy, outStr='\n hy:')
outStr = outStr + printH(self.hz, outStr='\n hz:')
return outStr
def h():
doc = "h is a list containing the cell widths of the tensor mesh in each dimension."
fget = lambda self: self._h
return locals()
h = property(**h())
def hx():
doc = "Width of cells in the x direction"
fget = lambda self: self._h[0]
return locals()
hx = property(**hx())
def hy():
doc = "Width of cells in the y direction"
fget = lambda self: None if self.dim < 2 else self._h[1]
return locals()
hy = property(**hy())
def hz():
doc = "Width of cells in the z direction"
fget = lambda self: None if self.dim < 3 else self._h[2]
return locals()
hz = property(**hz())
def vectorNx():
doc = "Nodal grid vector (1D) in the x direction."
fget = lambda self: np.r_[0., self.hx.cumsum()] + self.x0[0]
return locals()
vectorNx = property(**vectorNx())
def vectorNy():
doc = "Nodal grid vector (1D) in the y direction."
fget = lambda self: None if self.dim < 2 else np.r_[0., self.hy.cumsum()] + self.x0[1]
return locals()
vectorNy = property(**vectorNy())
def vectorNz():
doc = "Nodal grid vector (1D) in the z direction."
fget = lambda self: None if self.dim < 3 else np.r_[0., self.hz.cumsum()] + self.x0[2]
return locals()
vectorNz = property(**vectorNz())
def vectorCCx():
doc = "Cell-centered grid vector (1D) in the x direction."
fget = lambda self: np.r_[0, self.hx[:-1].cumsum()] + self.hx*0.5 + self.x0[0]
return locals()
vectorCCx = property(**vectorCCx())
def vectorCCy():
doc = "Cell-centered grid vector (1D) in the y direction."
fget = lambda self: None if self.dim < 2 else np.r_[0, self.hy[:-1].cumsum()] + self.hy*0.5 + self.x0[1]
return locals()
vectorCCy = property(**vectorCCy())
def vectorCCz():
doc = "Cell-centered grid vector (1D) in the z direction."
fget = lambda self: None if self.dim < 3 else np.r_[0, self.hz[:-1].cumsum()] + self.hz*0.5 + self.x0[2]
return locals()
vectorCCz = property(**vectorCCz())
def gridCC():
doc = "Cell-centered grid."
def fget(self):
if self._gridCC is None:
self._gridCC = ndgrid([x for x in [self.vectorCCx, self.vectorCCy, self.vectorCCz] if not x is None])
return self._gridCC
return locals()
_gridCC = None # Store grid by default
gridCC = property(**gridCC())
def gridN():
doc = "Nodal grid."
def fget(self):
if self._gridN is None:
self._gridN = ndgrid([x for x in [self.vectorNx, self.vectorNy, self.vectorNz] if not x is None])
return self._gridN
return locals()
_gridN = None # Store grid by default
gridN = property(**gridN())
def gridFx():
doc = "Face staggered grid in the x direction."
def fget(self):
if self._gridFx is None:
self._gridFx = ndgrid([x for x in [self.vectorNx, self.vectorCCy, self.vectorCCz] if not x is None])
return self._gridFx
return locals()
_gridFx = None # Store grid by default
gridFx = property(**gridFx())
def gridFy():
doc = "Face staggered grid in the y direction."
def fget(self):
if self._gridFy is None and self.dim > 1:
self._gridFy = ndgrid([x for x in [self.vectorCCx, self.vectorNy, self.vectorCCz] if not x is None])
return self._gridFy
return locals()
_gridFy = None # Store grid by default
gridFy = property(**gridFy())
def gridFz():
doc = "Face staggered grid in the z direction."
def fget(self):
if self._gridFz is None and self.dim > 2:
self._gridFz = ndgrid([x for x in [self.vectorCCx, self.vectorCCy, self.vectorNz] if not x is None])
return self._gridFz
return locals()
_gridFz = None # Store grid by default
gridFz = property(**gridFz())
def gridEx():
doc = "Edge staggered grid in the x direction."
def fget(self):
if self._gridEx is None:
self._gridEx = ndgrid([x for x in [self.vectorCCx, self.vectorNy, self.vectorNz] if not x is None])
return self._gridEx
return locals()
_gridEx = None # Store grid by default
gridEx = property(**gridEx())
def gridEy():
doc = "Edge staggered grid in the y direction."
def fget(self):
if self._gridEy is None and self.dim > 1:
self._gridEy = ndgrid([x for x in [self.vectorNx, self.vectorCCy, self.vectorNz] if not x is None])
return self._gridEy
return locals()
_gridEy = None # Store grid by default
gridEy = property(**gridEy())
def gridEz():
doc = "Edge staggered grid in the z direction."
def fget(self):
if self._gridEz is None and self.dim > 2:
self._gridEz = ndgrid([x for x in [self.vectorNx, self.vectorNy, self.vectorCCz] if not x is None])
return self._gridEz
return locals()
_gridEz = None # Store grid by default
gridEz = property(**gridEz())
# --------------- Geometries ---------------------
def vol():
doc = "Construct cell volumes of the 3D model as 1d array."
def fget(self):
if(self._vol is None):
vh = self.h
# Compute cell volumes
if(self.dim == 1):
self._vol = mkvc(vh[0])
elif(self.dim == 2):
# Cell sizes in each direction
self._vol = mkvc(np.outer(vh[0], vh[1]))
elif(self.dim == 3):
# Cell sizes in each direction
self._vol = mkvc(np.outer(mkvc(np.outer(vh[0], vh[1])), vh[2]))
return self._vol
return locals()
_vol = None
vol = property(**vol())
def area():
doc = "Construct face areas of the 3D model as 1d array."
def fget(self):
if(self._area is None):
# Ensure that we are working with column vectors
vh = self.h
# The number of cell centers in each direction
n = self.n
# Compute areas of cell faces
if(self.dim == 1):
self._area = np.ones(n[0]+1)
elif(self.dim == 2):
area1 = np.outer(np.ones(n[0]+1), vh[1])
area2 = np.outer(vh[0], np.ones(n[1]+1))
self._area = np.r_[mkvc(area1), mkvc(area2)]
elif(self.dim == 3):
area1 = np.outer(np.ones(n[0]+1), mkvc(np.outer(vh[1], vh[2])))
area2 = np.outer(vh[0], mkvc(np.outer(np.ones(n[1]+1), vh[2])))
area3 = np.outer(vh[0], mkvc(np.outer(vh[1], np.ones(n[2]+1))))
self._area = np.r_[mkvc(area1), mkvc(area2), mkvc(area3)]
return self._area
return locals()
_area = None
area = property(**area())
def edge():
doc = "Construct edge legnths of the 3D model as 1d array."
def fget(self):
if(self._edge is None):
# Ensure that we are working with column vectors
vh = self.h
# The number of cell centers in each direction
n = self.n
# Compute edge lengths
if(self.dim == 1):
self._edge = mkvc(vh[0])
elif(self.dim == 2):
l1 = np.outer(vh[0], np.ones(n[1]+1))
l2 = np.outer(np.ones(n[0]+1), vh[1])
self._edge = np.r_[mkvc(l1), mkvc(l2)]
elif(self.dim == 3):
l1 = np.outer(vh[0], mkvc(np.outer(np.ones(n[1]+1), np.ones(n[2]+1))))
l2 = np.outer(np.ones(n[0]+1), mkvc(np.outer(vh[1], np.ones(n[2]+1))))
l3 = np.outer(np.ones(n[0]+1), mkvc(np.outer(np.ones(n[1]+1), vh[2])))
self._edge = np.r_[mkvc(l1), mkvc(l2), mkvc(l3)]
return self._edge
return locals()
_edge = None
edge = property(**edge())
# --------------- Methods ---------------------
def isInside(self, pts):
"""
Determines if a set of points are inside a mesh.
:param numpy.ndarray pts: Location of points to test
:rtype numpy.ndarray
:return inside, numpy array of booleans
"""
pts = np.atleast_2d(pts)
inside = (pts[:,0] >= self.vectorNx.min()) & (pts[:,0] <= self.vectorNx.max())
if self.dim > 1:
inside = inside & ((pts[:,1] >= self.vectorNy.min()) & (pts[:,1] <= self.vectorNy.max()))
if self.dim > 2:
inside = inside & ((pts[:,2] >= self.vectorNz.min()) & (pts[:,2] <= self.vectorNz.max()))
return inside
def getInterpolationMat(self, loc, locType):
""" Produces interpolation matrix
:param numpy.ndarray loc: Location of points to interpolate to
:param str locType: What to interpolate (see below)
:rtype: scipy.sparse.csr.csr_matrix
:return: M, the interpolation matrix
locType can be::
'ex' -> x-component of field defined on edges
'ey' -> y-component of field defined on edges
'ez' -> z-component of field defined on edges
'fx' -> x-component of field defined on edges
'fy' -> y-component of field defined on edges
'fz' -> z-component of field defined on edges
'n' -> scalar field defined on nodes
'cc' -> scalar field defined on cell centres
"""
loc = np.atleast_2d(loc)
assert np.all(self.isInside(loc)), "Points outside of mesh"
if self.dim == 3:
if locType == 'fx':
Qx = interpmat(self.vectorNx,
self.vectorCCy,
self.vectorCCz,
loc[:,0], loc[:,1], loc[:,2])
Qy = spzeros(loc.shape[0], self.nF[1])
Qz = spzeros(loc.shape[0], self.nF[2])
Q = sp.hstack([Qx, Qy, Qz])
elif locType == 'fy':
Qx = spzeros(loc.shape[0], self.nF[0])
Qy = interpmat(self.vectorCCx,
self.vectorNy,
self.vectorCCz,
loc[:,0], loc[:,1], loc[:,2])
Qz = spzeros(loc.shape[0], self.nF[2])
Q = sp.hstack([Qx, Qy, Qz])
elif locType == 'fz':
Qx = spzeros(loc.shape[0], self.nF[0])
Qy = spzeros(loc.shape[0], self.nF[1])
Qz = interpmat(self.vectorCCx,
self.vectorCCy,
self.vectorNz,
loc[:,0], loc[:,1], loc[:,2])
Q = sp.hstack([Qx, Qy, Qz])
elif locType == 'ex':
Qx = interpmat(self.vectorCCx,
self.vectorNy,
self.vectorNz,
loc[:,0], loc[:,1], loc[:,2])
Qy = spzeros(loc.shape[0], self.nF[1])
Qz = spzeros(loc.shape[0], self.nF[2])
Q = sp.hstack([Qx, Qy, Qz])
elif locType == 'ey':
Qx = spzeros(loc.shape[0], self.nF[0])
Qy = interpmat(self.vectorNx,
self.vectorCCy,
self.vectorNz,
loc[:,0], loc[:,1], loc[:,2])
Qz = spzeros(loc.shape[0], self.nF[2])
Q = sp.hstack([Qx, Qy, Qz])
elif locType == 'ez':
Qx = spzeros(loc.shape[0], self.nF[0])
Qy = spzeros(loc.shape[0], self.nF[1])
Qz = interpmat(self.vectorNx,
self.vectorNy,
self.vectorCCz,
loc[:,0], loc[:,1], loc[:,2])
Q = sp.hstack([Qx, Qy, Qz])
elif locType == 'n':
Q = interpmat(self.vectorNx,
self.vectorNy,
self.vectorNz,
loc[:,0], loc[:,1], loc[:,2])
elif locType == 'cc':
Q = interpmat(self.vectorCCx,
self.vectorCCy,
self.vectorCCz,
loc[:,0], loc[:,1], loc[:,2])
else:
raise NotImplementedError('getInterpolationMat: locType=='+locType)
else:
raise NotImplementedError('getInterpolationMat: dim=='+str(m.dim))
return Q
if __name__ == '__main__':
print('Welcome to tensor mesh!')
testDim = 1
h1 = 0.3*np.ones(7)
h1[0] = 0.5
h1[-1] = 0.6
h2 = .5 * np.ones(4)
h3 = .4 * np.ones(6)
h = [h1, h2, h3]
h = h[:testDim]
M = TensorMesh(h)
xn = M.plotGrid()