renaming to ensure capitals

This commit is contained in:
rowanc1
2014-01-16 13:22:46 -08:00
parent 7432591450
commit fa8a5cd7cb
48 changed files with 0 additions and 0 deletions
+522
View File
@@ -0,0 +1,522 @@
import numpy as np
from SimPEG import Utils
class BaseMesh(object):
"""
BaseMesh does all the counting you don't want to do.
BaseMesh should be inherited by meshes with a regular structure.
:param numpy.array,list n: number of cells in each direction (dim, )
:param numpy.array,list x0: Origin of the mesh (dim, )
"""
def __init__(self, n, x0=None):
# Check inputs
if x0 is None:
x0 = np.zeros(len(n))
if not len(n) == len(x0):
raise Exception("Dimension mismatch. x0 != len(n)")
if len(n) > 3:
raise Exception("Dimensions higher than 3 are not supported.")
# Ensure x0 & n are 1D vectors
self._n = np.array(n, dtype=int).ravel()
self._x0 = np.array(x0).ravel()
self._dim = len(n)
def x0():
doc = """
Origin of the mesh
:rtype: numpy.array (dim, )
:return: x0
"""
fget = lambda self: self._x0
return locals()
x0 = property(**x0())
def r(self, x, xType='CC', outType='CC', format='V'):
"""
Mesh.r is a quick reshape command that will do the best it can at giving you what you want.
For example, you have a face variable, and you want the x component of it reshaped to a 3D matrix.
Mesh.r can fulfil your dreams::
mesh.r(V, 'F', 'Fx', 'M')
| | | { How: 'M' or ['V'] for a matrix (ndgrid style) or a vector (n x dim) }
| | { What you want: ['CC'], 'N', 'F', 'Fx', 'Fy', 'Fz', 'E', 'Ex', 'Ey', or 'Ez' }
| { What is it: ['CC'], 'N', 'F', 'Fx', 'Fy', 'Fz', 'E', 'Ex', 'Ey', or 'Ez' }
{ The input: as a list or ndarray }
For example::
Xex, Yex, Zex = r(mesh.gridEx, 'Ex', 'Ex', 'M') # Separates each component of the Ex grid into 3 matrices
XedgeVector = r(edgeVector, 'E', 'Ex', 'V') # Given an edge vector, this will return just the part on the x edges as a vector
eX, eY, eZ = r(edgeVector, 'E', 'E', 'V') # Separates each component of the edgeVector into 3 vectors
"""
assert (type(x) == list or type(x) == np.ndarray), "x must be either a list or a ndarray"
assert xType in ['CC', 'N', 'F', 'Fx', 'Fy', 'Fz', 'E', 'Ex', 'Ey', 'Ez'], "xType must be either 'CC', 'N', 'F', 'Fx', 'Fy', 'Fz', 'E', 'Ex', 'Ey', or 'Ez'"
assert outType in ['CC', 'N', 'F', 'Fx', 'Fy', 'Fz', 'E', 'Ex', 'Ey', 'Ez'], "outType must be either 'CC', 'N', 'F', Fx', 'Fy', 'Fz', 'E', 'Ex', 'Ey', or 'Ez'"
assert format in ['M', 'V'], "format must be either 'M' or 'V'"
assert outType[:len(xType)] == xType, "You cannot change types when reshaping."
assert xType in outType, 'You cannot change type of components.'
if type(x) == list:
for i, xi in enumerate(x):
assert type(x) == np.ndarray, "x[%i] must be a numpy array" % i
assert xi.size == x[0].size, "Number of elements in list must not change."
x_array = np.ones((x.size, len(x)))
# Unwrap it and put it in a np array
for i, xi in enumerate(x):
x_array[:, i] = Utils.mkvc(xi)
x = x_array
assert type(x) == np.ndarray, "x must be a numpy array"
x = x[:] # make a copy.
xTypeIsFExyz = len(xType) > 1 and xType[0] in ['F', 'E'] and xType[1] in ['x', 'y', 'z']
def outKernal(xx, nn):
"""Returns xx as either a matrix (shape == nn) or a vector."""
if format == 'M':
return xx.reshape(nn, order='F')
elif format == 'V':
return Utils.mkvc(xx)
def switchKernal(xx):
"""Switches over the different options."""
if xType in ['CC', 'N']:
nn = (self.n) if xType == 'CC' else (self.n+1)
assert xx.size == np.prod(nn), "Number of elements must not change."
return outKernal(xx, nn)
elif xType in ['F', 'E']:
# This will only deal with components of fields, not full 'F' or 'E'
xx = Utils.mkvc(xx) # unwrap it in case it is a matrix
nn = self.nFv if xType == 'F' else self.nEv
nn = np.r_[0, nn]
nx = [0, 0, 0]
nx[0] = self.nFx if xType == 'F' else self.nEx
nx[1] = self.nFy if xType == 'F' else self.nEy
nx[2] = self.nFz if xType == 'F' else self.nEz
for dim, dimName in enumerate(['x', 'y', 'z']):
if dimName in outType:
assert self.dim > dim, ("Dimensions of mesh not great enough for %s%s", (xType, dimName))
assert xx.size == np.sum(nn), 'Vector is not the right size.'
start = np.sum(nn[:dim+1])
end = np.sum(nn[:dim+2])
return outKernal(xx[start:end], nx[dim])
elif xTypeIsFExyz:
# This will deal with partial components (x, y or z) lying on edges or faces
if 'x' in xType:
nn = self.nFx if 'F' in xType else self.nEx
elif 'y' in xType:
nn = self.nFy if 'F' in xType else self.nEy
elif 'z' in xType:
nn = self.nFz if 'F' in xType else self.nEz
assert xx.size == np.prod(nn), 'Vector is not the right size.'
return outKernal(xx, nn)
# Check if we are dealing with a vector quantity
isVectorQuantity = len(x.shape) == 2 and x.shape[1] == self.dim
if outType in ['F', 'E']:
assert ~isVectorQuantity, 'Not sure what to do with a vector vector quantity..'
outTypeCopy = outType
out = ()
for ii, dirName in enumerate(['x', 'y', 'z'][:self.dim]):
outType = outTypeCopy + dirName
out += (switchKernal(x),)
return out
elif isVectorQuantity:
out = ()
for ii in range(x.shape[1]):
out += (switchKernal(x[:, ii]),)
return out
else:
return switchKernal(x)
def n():
doc = """
Number of Cells in each dimension (array of integers)
:rtype: numpy.array
:return: n
"""
fget = lambda self: self._n
return locals()
n = property(**n())
def dim():
doc = """
The dimension of the mesh (1, 2, or 3).
:rtype: int
:return: dim
"""
fget = lambda self: self._dim
return locals()
dim = property(**dim())
def nCx():
doc = """
Number of cells in the x direction
:rtype: int
:return: nCx
"""
fget = lambda self: self.n[0]
return locals()
nCx = property(**nCx())
def nCy():
doc = """
Number of cells in the y direction
:rtype: int
:return: nCy or None if dim < 2
"""
def fget(self):
if self.dim > 1:
return self.n[1]
else:
return None
return locals()
nCy = property(**nCy())
def nCz():
doc = """Number of cells in the z direction
:rtype: int
:return: nCz or None if dim < 3
"""
def fget(self):
if self.dim > 2:
return self.n[2]
else:
return None
return locals()
nCz = property(**nCz())
def nC():
doc = """
Total number of cells in the model.
:rtype: int
:return: nC
.. plot::
from SimPEG.mesh import TensorMesh
import numpy as np
TensorMesh([np.ones(n) for n in [2,3]]).plotGrid(centers=True,showIt=True)
"""
fget = lambda self: np.prod(self.n)
return locals()
nC = property(**nC())
def nCv():
doc = """
Total number of cells in each direction
:rtype: numpy.array (dim, )
:return: [nCx, nCy, nCz]
"""
fget = lambda self: np.array([x for x in [self.nCx, self.nCy, self.nCz] if not x is None])
return locals()
nCv = property(**nCv())
def nNx():
doc = """
Number of nodes in the x-direction
:rtype: int
:return: nNx
"""
fget = lambda self: self.nCx + 1
return locals()
nNx = property(**nNx())
def nNy():
doc = """
Number of noes in the y-direction
:rtype: int
:return: nNy or None if dim < 2
"""
def fget(self):
if self.dim > 1:
return self.n[1] + 1
else:
return None
return locals()
nNy = property(**nNy())
def nNz():
doc = """
Number of nodes in the z-direction
:rtype: int
:return: nNz or None if dim < 3
"""
def fget(self):
if self.dim > 2:
return self.n[2] + 1
else:
return None
return locals()
nNz = property(**nNz())
def nN():
doc = """
Total number of nodes
:rtype: int
:return: nN
.. plot::
from SimPEG.mesh import TensorMesh
import numpy as np
TensorMesh([np.ones(n) for n in [2,3]]).plotGrid(nodes=True,showIt=True)
"""
fget = lambda self: np.prod(self.n + 1)
return locals()
nN = property(**nN())
def nNv():
doc = """
Total number of nodes in each direction
:rtype: numpy.array (dim, )
:return: [nNx, nNy, nNz]
"""
fget = lambda self: np.array([x for x in [self.nNx, self.nNy, self.nNz] if not x is None])
return locals()
nNv = property(**nNv())
def nEx():
doc = """
Number of x-edges in each direction
:rtype: numpy.array (dim, )
:return: nEx
"""
fget = lambda self: np.array([x for x in [self.nCx, self.nNy, self.nNz] if not x is None])
return locals()
nEx = property(**nEx())
def nEy():
doc = """
Number of y-edges in each direction
:rtype: numpy.array (dim, )
:return: nEy or None if dim < 2
"""
def fget(self):
if self.dim > 1:
return np.array([x for x in [self.nNx, self.nCy, self.nNz] if not x is None])
else:
return None
return locals()
nEy = property(**nEy())
def nEz():
doc = """
Number of z-edges in each direction
:rtype: numpy.array (dim, )
:return: nEz or None if dim < 3
"""
def fget(self):
if self.dim > 2:
return np.array([x for x in [self.nNx, self.nNy, self.nCz] if not x is None])
else:
return None
return locals()
nEz = property(**nEz())
def nEv():
doc = """
Total number of edges in each direction
:rtype: numpy.array (dim, )
:return: [prod(nEx), prod(nEy), prod(nEz)]
.. plot::
from SimPEG.mesh import TensorMesh
import numpy as np
TensorMesh([np.ones(n) for n in [2,3]]).plotGrid(edges=True,showIt=True)
"""
fget = lambda self: np.array([np.prod(x) for x in [self.nEx, self.nEy, self.nEz] if not x is None])
return locals()
nEv = property(**nEv())
def nE():
doc = """
Total number of edges.
:rtype: int
:return: sum([prod(nEx), prod(nEy), prod(nEz)])
"""
fget = lambda self: np.sum(self.nEv)
return locals()
nE = property(**nE())
def nFx():
doc = """
Number of x-faces in each direction
:rtype: numpy.array (dim, )
:return: nFx
"""
fget = lambda self: np.array([x for x in [self.nNx, self.nCy, self.nCz] if not x is None])
return locals()
nFx = property(**nFx())
def nFy():
doc = """
Number of y-faces in each direction
:rtype: numpy.array (dim, )
:return: nFy or None if dim < 2
"""
def fget(self):
if self.dim > 1:
return np.array([x for x in [self.nCx, self.nNy, self.nCz] if not x is None])
else:
return None
return locals()
nFy = property(**nFy())
def nFz():
doc = """
Number of z-faces in each direction
:rtype: numpy.array (dim, )
:return: nFz or None if dim < 3
"""
def fget(self):
if self.dim > 2:
return np.array([x for x in [self.nCx, self.nCy, self.nNz] if not x is None])
else:
return None
return locals()
nFz = property(**nFz())
def nFv():
doc = """
Total number of faces in each direction
:rtype: numpy.array (dim, )
:return: [prod(nFx), prod(nFy), prod(nFz)]
.. plot::
from SimPEG.mesh import TensorMesh
import numpy as np
TensorMesh([np.ones(n) for n in [2,3]]).plotGrid(faces=True,showIt=True)
"""
fget = lambda self: np.array([np.prod(x) for x in [self.nFx, self.nFy, self.nFz] if not x is None])
return locals()
nFv = property(**nFv())
def nF():
doc = """
Total number of faces.
:rtype: int
:return: sum([prod(nFx), prod(nFy), prod(nFz)])
"""
fget = lambda self: np.sum(self.nFv)
return locals()
nF = property(**nF())
def normals():
doc = """
Face Normals
:rtype: numpy.array (sum(nF), dim)
:return: normals
"""
def fget(self):
if self.dim == 2:
nX = np.c_[np.ones(self.nFv[0]), np.zeros(self.nFv[0])]
nY = np.c_[np.zeros(self.nFv[1]), np.ones(self.nFv[1])]
return np.r_[nX, nY]
elif self.dim == 3:
nX = np.c_[np.ones(self.nFv[0]), np.zeros(self.nFv[0]), np.zeros(self.nFv[0])]
nY = np.c_[np.zeros(self.nFv[1]), np.ones(self.nFv[1]), np.zeros(self.nFv[1])]
nZ = np.c_[np.zeros(self.nFv[2]), np.zeros(self.nFv[2]), np.ones(self.nFv[2])]
return np.r_[nX, nY, nZ]
return locals()
normals = property(**normals())
def tangents():
doc = """
Edge Tangents
:rtype: numpy.array (sum(nE), dim)
:return: normals
"""
def fget(self):
if self.dim == 2:
tX = np.c_[np.ones(self.nEv[0]), np.zeros(self.nEv[0])]
tY = np.c_[np.zeros(self.nEv[1]), np.ones(self.nEv[1])]
return np.r_[tX, tY]
elif self.dim == 3:
tX = np.c_[np.ones(self.nEv[0]), np.zeros(self.nEv[0]), np.zeros(self.nEv[0])]
tY = np.c_[np.zeros(self.nEv[1]), np.ones(self.nEv[1]), np.zeros(self.nEv[1])]
tZ = np.c_[np.zeros(self.nEv[2]), np.zeros(self.nEv[2]), np.ones(self.nEv[2])]
return np.r_[tX, tY, tZ]
return locals()
tangents = property(**tangents())
def projectFaceVector(self, fV):
"""
Given a vector, fV, in cartesian coordinates, this will project it onto the mesh using the normals
:param numpy.array fV: face vector with shape (nF, dim)
:rtype: numpy.array with shape (nF, )
:return: projected face vector
"""
assert type(fV) == np.ndarray, 'fV must be an ndarray'
assert len(fV.shape) == 2 and fV.shape[0] == np.sum(self.nF) and fV.shape[1] == self.dim, 'fV must be an ndarray of shape (nF x dim)'
return np.sum(fV*self.normals, 1)
def projectEdgeVector(self, eV):
"""
Given a vector, eV, in cartesian coordinates, this will project it onto the mesh using the tangents
:param numpy.array eV: edge vector with shape (nE, dim)
:rtype: numpy.array with shape (nE, )
:return: projected edge vector
"""
assert type(eV) == np.ndarray, 'eV must be an ndarray'
assert len(eV.shape) == 2 and eV.shape[0] == np.sum(self.nE) and eV.shape[1] == self.dim, 'eV must be an ndarray of shape (nE x dim)'
return np.sum(eV*self.tangents, 1)
+464
View File
@@ -0,0 +1,464 @@
import numpy as np
import scipy.sparse as sp
from scipy.constants import pi
from SimPEG.Utils import mkvc, ndgrid, sdiag
class Cyl1DMesh(object):
"""
Cyl1DMesh is a mesh class for cylindrically symmetric 1D problems
"""
_meshType = 'CYL1D'
def __init__(self, h, z0=None):
assert len(h) == 2, "len(h) must equal 2"
if z0 is not None:
assert z0.size == 1, "z0.size must equal 1"
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.astype(float)) for x in h]
if z0 is None:
z0 = 0
self._z0 = z0
####################################################
# Mesh properties
####################################################
def h():
doc = "list containing the width of each cell"
def fget(self):
return self._h
return locals()
h = property(**h())
def z0():
doc = "The z-origin"
def fget(self):
return self._z0
return locals()
z0 = property(**z0())
def hr():
doc = "Width of the cells in the r direction"
def fget(self):
return self._h[0]
return locals()
hr = property(**hr())
def hz():
doc = "Width of the cells in the z direction"
def fget(self):
return self._h[1]
return locals()
hz = property(**hz())
####################################################
# Counting
####################################################
def nCx():
doc = "Number of cells in the radial direction"
fget = lambda self: self.hr.size
return locals()
nCx = property(**nCx())
def nCz():
doc = "Number of cells in the z direction"
fget = lambda self: self.hz.size
return locals()
nCz = property(**nCz())
def nC():
doc = "Total number of cells"
fget = lambda self: self.nCx * self.nCz
return locals()
nC = property(**nC())
def nCv():
doc = "Total number of cells in each direction"
fget = lambda self: np.array([self.nCx, self.nCz])
return locals()
nCv = property(**nCv())
def nNr():
doc = "Number of nodes in the radial direction"
fget = lambda self: self.hr.size
return locals()
nNr = property(**nNr())
def nNz():
doc = "Number of nodes in the radial direction"
fget = lambda self: self.hz.size + 1
return locals()
nNz = property(**nNz())
def nN():
doc = "Total number of nodes"
fget = lambda self: self.nNr * self.nNz
return locals()
nN = property(**nN())
def nFr():
doc = "Number of r faces"
fget = lambda self: self.nNr * self.nCz
return locals()
nFr = property(**nFr())
def nFz():
doc = "Number of z faces"
fget = lambda self: self.nNz * self.nCx
return locals()
nFz = property(**nFz())
def nFv():
doc = "Total number of faces in each direction"
fget = lambda self: np.array([self.nFr, self.nFz])
return locals()
nFv = property(**nFv())
def nF():
doc = "Total number of faces"
fget = lambda self: self.nFr + self.nFz
return locals()
nF = property(**nF())
def nE():
doc = "Number of edges"
fget = lambda self: self.nN
return locals()
nE = property(**nE())
####################################################
# Vectors & Grids
####################################################
def vectorNr():
doc = "Nodal grid vector (1D) in the r direction"
fget = lambda self: self.hr.cumsum()
return locals()
vectorNr = property(**vectorNr())
def vectorNz():
doc = "Nodal grid vector (1D) in the z direction"
fget = lambda self: np.r_[0, self.hz.cumsum()] + self._z0
return locals()
vectorNz = property(**vectorNz())
def vectorCCr():
doc = "Cell centered grid vector (1D) in the r direction"
fget = lambda self: np.r_[0, self.hr.cumsum()[1:] - self.hr[1:]/2]
return locals()
vectorCCr = property(**vectorCCr())
def vectorCCz():
doc = "Cell centered grid vector (1D) in the z direction"
fget = lambda self: self.hz.cumsum() - self.hz/2 + self._z0
return locals()
vectorCCz = property(**vectorCCz())
def gridCC():
doc = "Cell-centered grid"
def fget(self):
if self._gridCC is None:
self._gridCC = ndgrid([self.vectorCCr, self.vectorCCz])
return self._gridCC
return locals()
_gridCC = None
gridCC = property(**gridCC())
def gridN():
doc = "Nodal grid"
def fget(self):
if self._gridN is None:
self._gridN = ndgrid([self.vectorNr, self.vectorNz])
return self._gridN
return locals()
_gridN = None
gridN = property(**gridN())
def gridFr():
doc = "r face grid"
def fget(self):
if self._gridFr is None:
self._gridFr = ndgrid([self.vectorNr, self.vectorCCz])
return self._gridFr
return locals()
_gridFr = None
gridFr = property(**gridFr())
def gridFz():
doc = "z face grid"
def fget(self):
if self._gridFz is None:
self._gridFz = ndgrid([self.vectorCCr, self.vectorNz])
return self._gridFz
return locals()
_gridFz = None
gridFz = property(**gridFz())
####################################################
# Geometries
####################################################
def edge():
doc = "Edge lengths"
def fget(self):
if self._edge is None:
self._edge = 2*pi*self.gridN[:,0]
return self._edge
return locals()
_edge = None
edge = property(**edge())
def area():
doc = "Face areas"
def fget(self):
if self._area is None:
areaR = np.kron(self.hz, 2*pi*self.vectorNr)
areaZ = np.kron(np.ones_like(self.vectorNz),pi*(self.vectorNr**2 - np.r_[0, self.vectorNr[:-1]]**2))
self._area = np.r_[areaR, areaZ]
return self._area
return locals()
_area = None
area = property(**area())
def vol():
doc = "Volume of each cell"
def fget(self):
if self._vol is None:
az = pi*(self.vectorNr**2 - np.r_[0, self.vectorNr[:-1]]**2)
self._vol = np.kron(self.hz,az)
return self._vol
return locals()
_vol = None
vol = property(**vol())
####################################################
# Operators
####################################################
def edgeCurl():
doc = "The edgeCurl property."
def fget(self):
if self._edgeCurl is None:
#1D Difference matricies
dr = sp.spdiags((np.ones((self.nCx+1, 1))*[-1, 1]).T, [-1,0], self.nCx, self.nCx, format="csr")
dz = sp.spdiags((np.ones((self.nCz+1, 1))*[-1, 1]).T, [0,1], self.nCz, self.nCz+1, format="csr")
#2D Difference matricies
Dr = sp.kron(sp.eye(self.nNz), dr)
Dz = -sp.kron(dz, sp.eye(self.nCx)) #Not sure about this negative
#Edge curl operator
self._edgeCurl = sp.diags(1/self.area,0)*sp.vstack((Dz, Dr))*sp.diags(self.edge,0)
return self._edgeCurl
return locals()
_edgeCurl = None
edgeCurl = property(**edgeCurl())
def aveE2CC():
doc = "Averaging operator from cell edges to cell centres"
def fget(self):
if self._aveE2CC is None:
az = sp.spdiags(0.5*np.ones((2, self.nNz)), [-1,0], self.nNz, self.nCz, format='csr')
ar = sp.spdiags(0.5*np.ones((2, self.nCx)), [0, 1], self.nCx, self.nCx, format='csr')
ar[0,0] = 1
self._aveE2CC = sp.kron(az, ar).T
return self._aveE2CC
return locals()
_aveE2CC = None
aveE2CC = property(**aveE2CC())
def aveF2CC():
doc = "Averaging operator from cell faces to cell centres"
def fget(self):
if self._aveF2CC is None:
az = sp.spdiags(0.5*np.ones((2, self.nNz)), [-1,0], self.nNz, self.nCz, format='csr')
ar = sp.spdiags(0.5*np.ones((2, self.nCx)), [0, 1], self.nCx, self.nCx, format='csr')
ar[0,0] = 1
Afr = sp.kron(sp.eye(self.nCz),ar)
Afz = sp.kron(az,sp.eye(self.nCx))
self._aveF2CC = sp.vstack((Afr,Afz)).T
return self._aveF2CC
return locals()
_aveF2CC = None
aveF2CC = property(**aveF2CC())
####################################################
# Methods
####################################################
def getMass(self, materialProp=None, loc='e'):
""" Produces mass matricies.
:param None,float,numpy.ndarray materialProp: property to be averaged (see below)
:param str loc: Average to location: 'e'-edges, 'f'-faces
:rtype: scipy.sparse.csr.csr_matrix
:return: M, the mass matrix
materialProp can be::
None -> takes materialProp = 1 (default)
float -> a constant value for entire domain
numpy.ndarray -> if materialProp.size == self.nC
3D property model
if materialProp.size = self.nCz
1D (layered eath) property model
"""
if materialProp is None:
materialProp = np.ones(self.nC)
elif type(materialProp) is float:
materialProp = np.ones(self.nC)*materialProp
elif materialProp.shape == (self.nCz,):
materialProp = materialProp.repeat(self.nCx)
materialProp = mkvc(materialProp)
assert materialProp.shape == (self.nC,), "materialProp incorrect shape"
if loc=='e':
Av = self.aveE2CC
elif loc=='f':
Av = self.aveF2CC
else:
raise ValueError('Invalid loc')
diag = Av.T * (self.vol * mkvc(materialProp))
return sdiag(diag)
def getEdgeMass(self, materialProp=None):
"""mass matrix for products of edge functions w'*M(materialProp)*e"""
return self.getMass(loc='e', materialProp=materialProp)
def getFaceMass(self, materialProp=None):
"""mass matrix for products of face functions w'*M(materialProp)*f"""
return self.getMass(loc='f', materialProp=materialProp)
def getInterpolationMat(self, loc, locType='fz'):
""" Produces intrpolation 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 intrpolation matrix
locType can be::
'fz' -> z-component of field defined on faces
'fr' -> r-component of field defined on faces
'et' -> theta-component of field defined on edges
"""
loc = np.atleast_2d(loc)
assert np.all(loc[:,0]<=self.vectorNr.max()) & \
np.all(loc[:,1]>=self.vectorNz.min()) & \
np.all(loc[:,1]<=self.vectorNz.max()), \
"Points outside of mesh"
if locType=='fz':
Q = sp.lil_matrix((loc.shape[0], self.nF), dtype=float)
for i, iloc in enumerate(loc):
# Point is on a z-interface
if np.any(np.abs(self.vectorNz-iloc[1])<0.001):
dFz = self.gridFz-iloc #Distance to z faces
dFz[dFz[:,0]>0,:] = np.inf #Looking for next face to the left...
indL = np.argmin(np.sum(dFz**2, axis=1)) #Closest one
if self.gridFz[indL,0] == self.vectorCCr.max(): #Point in outer half cell (linear extrapolation)
zFL = self.gridFz[indL,:]
zFLL = self.gridFz[indL-1,:]
Q[i, indL+self.nFr] = (iloc[0] - zFLL[0])/(zFL[0] - zFLL[0])
Q[i, indL+self.nFr-1] = -(iloc[0] - zFL[0])/(zFL[0] - zFLL[0])
else:
zFL = self.gridFz[indL,:]
zFR = self.gridFz[indL+1,:]
Q[i,indL+self.nFr] = (zFR[0] - iloc[0])/(zFR[0] - zFL[0])
Q[i,indL+self.nFr+1] = (iloc[0] - zFL[0])/(zFR[0] - zFL[0])
# Point is in a cell
else:
dFz = self.gridFz-iloc
dFz[dFz>0] = np.inf
dFz = np.sum(dFz**2, axis=1)
indBL = np.argmin(dFz) # Face below and to the left
indAL = indBL + self.nCx # Face above and to the left
zF_BL = self.gridFz[indBL,:]
zF_AL = self.gridFz[indAL,:]
dzB = iloc[1] - zF_BL[1] # z-distance to face below
dzA = zF_AL[1] - iloc[1] # z-distance to face above
if self.gridFz[indBL,0] == self.vectorCCr.max(): #Point in outer half cell (linear extrapolation)
zF_BLL = self.gridFz[indBL-1,:]
zF_ALL = self.gridFz[indAL-1,:]
DZ = zF_AL[1] - zF_BL[1]
DR = zF_AL[0] - zF_ALL[0]
drL = iloc[0] - zF_AL[0]
drLL = iloc[0] - zF_ALL[0]
Q[i, indBL+self.nFr-1] = -(1 - dzB/DZ)*(drL/DR)
Q[i, indBL+self.nFr] = (1 - dzB/DZ)*(drLL/DR)
Q[i, indAL+self.nFr-1] = -(dzB/DZ)*(drL/DR)
Q[i, indAL+self.nFr] = (dzB/DZ)*(drLL/DR)
else:
indBR = indBL+1 # Face below and to the right
indAR = indAL + 1 # Face above and to the right
zF_BR = self.gridFz[indBR,:]
drL = iloc[0] - zF_BL[0] # r-distance to face on left
drR = zF_BR[0] - iloc[0] # r-distance to face on right
drz = (drL + drR)*(dzB + dzA)
Q[i,indBL+self.nFr] = drR*dzA/drz
Q[i,indBR+self.nFr] = drL*dzA/drz
Q[i,indAL+self.nFr] = drR*dzB/drz
Q[i,indAR+self.nFr] = drL*dzB/drz
elif locType=='fr':
raise NotImplementedError('locType==fr')
elif locType=='et':
raise NotImplementedError('locType==et')
else:
raise ValueError('Invalid locType')
return Q.tocsr()
def getNearest(self, loc, locType):
""" Returns the index of the closest face or edge to a given location
:param numpy.ndarray loc: Test point
:param str locType: Type of location desired (see below)
:rtype: int
:return: ind:
locType can be::
'fz' -> location of nearest z-face
'fr' -> location of nearest r-face
'et' -> location of nearest edge
"""
if locType=='et':
dr = self.gridN[:,0] - loc[0]
dz = self.gridN[:,1] - loc[1]
elif locType=='fz':
dr = self.gridFz[:,0] - loc[0]
dz = self.gridFz[:,1] - loc[1]
elif locType=='fr':
dr = self.gridFr[:,0] - loc[0]
dz = self.gridFr[:,1] - loc[1]
else:
raise ValueError('Invalid locType')
R = np.sqrt(dr**2 + dz**2)
ind = np.argmin(R)
return ind
+634
View File
@@ -0,0 +1,634 @@
import numpy as np
from scipy import sparse as sp
from SimPEG.Utils import mkvc, sdiag, speye, kron3, spzeros, ddx, av, avExtrap
def checkBC(bc):
"""
Checks if boundary condition 'bc' is valid.
Each bc must be either 'dirichlet' or 'neumann'
"""
if(type(bc) is str):
bc = [bc, bc]
assert type(bc) is list, 'bc must be a list'
assert len(bc) == 2, 'bc must have two elements'
for bc_i in bc:
assert type(bc_i) is str, "each bc must be a string"
assert bc_i in ['dirichlet', 'neumann'], "each bc must be either, 'dirichlet' or 'neumann'"
return bc
def ddxCellGrad(n, bc):
"""
Create 1D derivative operator from cell-centers to nodes this means we go from n to n+1
For Cell-Centered **Dirichlet**, use a ghost point::
(u_1 - u_g)/hf = grad
u_g u_1 u_2
* | * | * ...
^
0
u_g = - u_1
grad = 2*u1/dx
negitive on the other side.
For Cell-Centered **Neumann**, use a ghost point::
(u_1 - u_g)/hf = 0
u_g u_1 u_2
* | * | * ...
u_g = u_1
grad = 0; put a zero in.
"""
bc = checkBC(bc)
D = sp.spdiags((np.ones((n+1, 1))*[-1, 1]).T, [-1, 0], n+1, n, format="csr")
# Set the first side
if(bc[0] == 'dirichlet'):
D[0, 0] = 2
elif(bc[0] == 'neumann'):
D[0, 0] = 0
# Set the second side
if(bc[1] == 'dirichlet'):
D[-1, -1] = -2
elif(bc[1] == 'neumann'):
D[-1, -1] = 0
return D
def ddxCellGradBC(n, bc):
"""
Create 1D derivative operator from cell-centers to nodes this means we go from n to n+1
For Cell-Centered **Dirichlet**, use a ghost point::
(u_1 - u_g)/hf = grad
u_g u_1 u_2
* | * | * ...
^
u_b
We know the value at the boundary (u_b)::
(u_g+u_1)/2 = u_b (the average)
u_g = 2*u_b - u_1
So plug in to gradient:
(u_1 - (2*u_b - u_1))/hf = grad
2*(u_1-u_b)/hf = grad
Separate, because BC are known (and can move to RHS later)::
( 2/hf )*u_1 + ( -2/hf )*u_b = grad
( ^ ) JUST RETURN THIS
"""
bc = checkBC(bc)
ij = (np.array([0, n]),np.array([0, 1]))
vals = np.zeros(2)
# Set the first side
if(bc[0] == 'dirichlet'):
vals[0] = -2
elif(bc[0] == 'neumann'):
vals[0] = 0
# Set the second side
if(bc[1] == 'dirichlet'):
vals[1] = 2
elif(bc[1] == 'neumann'):
vals[1] = 0
D = sp.csr_matrix((vals, ij), shape=(n+1,2))
return D
class DiffOperators(object):
"""
Class creates the differential operators that you need!
"""
def __init__(self):
raise Exception('DiffOperators is a base class providing differential operators on meshes and cannot run on its own. Inherit to your favorite Mesh class.')
def faceDiv():
doc = "Construct divergence operator (face-stg to cell-centres)."
def fget(self):
if(self._faceDiv is None):
# The number of cell centers in each direction
n = self.n
# Compute faceDivergence operator on faces
if(self.dim == 1):
D = ddx(n[0])
elif(self.dim == 2):
D1 = sp.kron(speye(n[1]), ddx(n[0]))
D2 = sp.kron(ddx(n[1]), speye(n[0]))
D = sp.hstack((D1, D2), format="csr")
elif(self.dim == 3):
D1 = kron3(speye(n[2]), speye(n[1]), ddx(n[0]))
D2 = kron3(speye(n[2]), ddx(n[1]), speye(n[0]))
D3 = kron3(ddx(n[2]), speye(n[1]), speye(n[0]))
D = sp.hstack((D1, D2, D3), format="csr")
# Compute areas of cell faces & volumes
S = self.area
V = self.vol
self._faceDiv = sdiag(1/V)*D*sdiag(S)
return self._faceDiv
return locals()
_faceDiv = None
faceDiv = property(**faceDiv())
def faceDivx():
doc = "Construct divergence operator in the x component (face-stg to cell-centres)."
def fget(self):
if(self._faceDivx is None):
# The number of cell centers in each direction
n = self.n
# Compute faceDivergence operator on faces
if(self.dim == 1):
D1 = ddx(n[0])
elif(self.dim == 2):
D1 = sp.kron(speye(n[1]), ddx(n[0]))
elif(self.dim == 3):
D1 = kron3(speye(n[2]), speye(n[1]), ddx(n[0]))
# Compute areas of cell faces & volumes
S = self.r(self.area, 'F','Fx', 'V')
V = self.vol
self._faceDivx = sdiag(1/V)*D1*sdiag(S)
return self._faceDivx
return locals()
_faceDivx = None
faceDivx = property(**faceDivx())
def faceDivy():
doc = "Construct divergence operator in the y component (face-stg to cell-centres)."
def fget(self):
if(self.dim < 2): return None
if(self._faceDivy is None):
# The number of cell centers in each direction
n = self.n
# Compute faceDivergence operator on faces
if(self.dim == 2):
D2 = sp.kron(ddx(n[1]), speye(n[0]))
elif(self.dim == 3):
D2 = kron3(speye(n[2]), ddx(n[1]), speye(n[0]))
# Compute areas of cell faces & volumes
S = self.r(self.area, 'F','Fy', 'V')
V = self.vol
self._faceDivy = sdiag(1/V)*D2*sdiag(S)
return self._faceDivy
return locals()
_faceDivy = None
faceDivy = property(**faceDivy())
def faceDivz():
doc = "Construct divergence operator in the z component (face-stg to cell-centres)."
def fget(self):
if(self.dim < 3): return None
if(self._faceDivz is None):
# The number of cell centers in each direction
n = self.n
# Compute faceDivergence operator on faces
D3 = kron3(ddx(n[2]), speye(n[1]), speye(n[0]))
# Compute areas of cell faces & volumes
S = self.r(self.area, 'F','Fz', 'V')
V = self.vol
self._faceDivz = sdiag(1/V)*D3*sdiag(S)
return self._faceDivz
return locals()
_faceDivz = None
faceDivz = property(**faceDivz())
def nodalGrad():
doc = "Construct gradient operator (nodes to edges)."
def fget(self):
if(self._nodalGrad is None):
# The number of cell centers in each direction
n = self.n
# Compute divergence operator on faces
if(self.dim == 1):
G = ddx(n[0])
elif(self.dim == 2):
D1 = sp.kron(speye(n[1]+1), ddx(n[0]))
D2 = sp.kron(ddx(n[1]), speye(n[0]+1))
G = sp.vstack((D1, D2), format="csr")
elif(self.dim == 3):
D1 = kron3(speye(n[2]+1), speye(n[1]+1), ddx(n[0]))
D2 = kron3(speye(n[2]+1), ddx(n[1]), speye(n[0]+1))
D3 = kron3(ddx(n[2]), speye(n[1]+1), speye(n[0]+1))
G = sp.vstack((D1, D2, D3), format="csr")
# Compute lengths of cell edges
L = self.edge
self._nodalGrad = sdiag(1/L)*G
return self._nodalGrad
return locals()
_nodalGrad = None
nodalGrad = property(**nodalGrad())
def nodalLaplacian():
doc = "Construct laplacian operator (nodes to edges)."
def fget(self):
if(self._nodalLaplacian is None):
print 'Warning: Laplacian has not been tested rigorously.'
# The number of cell centers in each direction
n = self.n
# Compute divergence operator on faces
if(self.dim == 1):
D1 = sdiag(1./self.hx) * ddx(mesh.nCx)
L = - D1.T*D1
elif(self.dim == 2):
D1 = sdiag(1./self.hx) * ddx(n[0])
D2 = sdiag(1./self.hy) * ddx(n[1])
L1 = sp.kron(speye(n[1]+1), - D1.T * D1)
L2 = sp.kron(- D2.T * D2, speye(n[0]+1))
L = L1 + L2
elif(self.dim == 3):
D1 = sdiag(1./self.hx) * ddx(n[0])
D2 = sdiag(1./self.hy) * ddx(n[1])
D3 = sdiag(1./self.hz) * ddx(n[2])
L1 = kron3(speye(n[2]+1), speye(n[1]+1), - D1.T * D1)
L2 = kron3(speye(n[2]+1), - D2.T * D2, speye(n[0]+1))
L3 = kron3(- D3.T * D3, speye(n[1]+1), speye(n[0]+1))
L = L1 + L2 + L3
self._nodalLaplacian = L
return self._nodalLaplacian
return locals()
_nodalLaplacian = None
nodalLaplacian = property(**nodalLaplacian())
def setCellGradBC(self, BC):
"""
Function that sets the boundary conditions for cell-centred derivative operators.
Examples::
BC = 'neumann' # Neumann in all directions
BC = ['neumann', 'dirichlet', 'neumann'] # 3D, Dirichlet in y Neumann else
BC = [['neumann', 'dirichlet'], 'dirichlet', 'dirichlet'] # 3D, Neumann in x on bottom of domain,
# Dirichlet else
"""
if(type(BC) is str):
BC = [BC for _ in self.n] # Repeat the str self.dim times
elif(type(BC) is list):
assert len(BC) == self.dim, 'BC list must be the size of your mesh'
else:
raise Exception("BC must be a str or a list.")
for i, bc_i in enumerate(BC):
BC[i] = checkBC(bc_i)
# ensure we create a new gradient next time we call it
self._cellGrad = None
self._cellGradBC = None
self._cellGradBC_list = BC
return BC
_cellGradBC_list = 'neumann'
def cellGrad():
doc = "The cell centered Gradient, takes you to cell faces."
def fget(self):
if(self._cellGrad is None):
BC = self.setCellGradBC(self._cellGradBC_list)
n = self.n
if(self.dim == 1):
G = ddxCellGrad(n[0], BC[0])
elif(self.dim == 2):
G1 = sp.kron(speye(n[1]), ddxCellGrad(n[0], BC[0]))
G2 = sp.kron(ddxCellGrad(n[1], BC[1]), speye(n[0]))
G = sp.vstack((G1, G2), format="csr")
elif(self.dim == 3):
G1 = kron3(speye(n[2]), speye(n[1]), ddxCellGrad(n[0], BC[0]))
G2 = kron3(speye(n[2]), ddxCellGrad(n[1], BC[1]), speye(n[0]))
G3 = kron3(ddxCellGrad(n[2], BC[2]), speye(n[1]), speye(n[0]))
G = sp.vstack((G1, G2, G3), format="csr")
# Compute areas of cell faces & volumes
S = self.area
V = self.aveCC2F*self.vol # Average volume between adjacent cells
self._cellGrad = sdiag(S/V)*G
return self._cellGrad
return locals()
_cellGrad = None
cellGrad = property(**cellGrad())
def cellGradBC():
doc = "The cell centered Gradient boundary condition matrix"
def fget(self):
if(self._cellGradBC is None):
BC = self.setCellGradBC(self._cellGradBC_list)
n = self.n
if(self.dim == 1):
G = ddxCellGradBC(n[0], BC[0])
elif(self.dim == 2):
G1 = sp.kron(speye(n[1]), ddxCellGradBC(n[0], BC[0]))
G2 = sp.kron(ddxCellGradBC(n[1], BC[1]), speye(n[0]))
G = sp.block_diag((G1, G2), format="csr")
elif(self.dim == 3):
G1 = kron3(speye(n[2]), speye(n[1]), ddxCellGradBC(n[0], BC[0]))
G2 = kron3(speye(n[2]), ddxCellGradBC(n[1], BC[1]), speye(n[0]))
G3 = kron3(ddxCellGradBC(n[2], BC[2]), speye(n[1]), speye(n[0]))
G = sp.block_diag((G1, G2, G3), format="csr")
# Compute areas of cell faces & volumes
S = self.area
V = self.aveCC2F*self.vol # Average volume between adjacent cells
self._cellGradBC = sdiag(S/V)*G
return self._cellGradBC
return locals()
_cellGradBC = None
cellGradBC = property(**cellGradBC())
def cellGradx():
doc = "Cell centered Gradient in the x dimension. Has neumann boundary conditions."
def fget(self):
if getattr(self, '_cellGradx', None) is None:
BC = ['neumann', 'neumann']
n = self.n
if(self.dim == 1):
G1 = ddxCellGrad(n[0], BC)
elif(self.dim == 2):
G1 = sp.kron(speye(n[1]), ddxCellGrad(n[0], BC))
elif(self.dim == 3):
G1 = kron3(speye(n[2]), speye(n[1]), ddxCellGrad(n[0], BC))
# Compute areas of cell faces & volumes
V = self.aveCC2F*self.vol
L = self.r(self.area/V, 'F','Fx', 'V')
self._cellGradx = sdiag(L)*G1
return self._cellGradx
return locals()
cellGradx = property(**cellGradx())
def cellGrady():
doc = "Cell centered Gradient in the x dimension. Has neumann boundary conditions."
def fget(self):
if self.dim < 2: return None
if getattr(self, '_cellGrady', None) is None:
BC = ['neumann', 'neumann']
n = self.n
if(self.dim == 2):
G2 = sp.kron(ddxCellGrad(n[1], BC), speye(n[0]))
elif(self.dim == 3):
G2 = kron3(speye(n[2]), ddxCellGrad(n[1], BC), speye(n[0]))
# Compute areas of cell faces & volumes
V = self.aveCC2F*self.vol
L = self.r(self.area/V, 'F','Fy', 'V')
self._cellGrady = sdiag(L)*G2
return self._cellGrady
return locals()
cellGrady = property(**cellGrady())
def cellGradz():
doc = "Cell centered Gradient in the x dimension. Has neumann boundary conditions."
def fget(self):
if self.dim < 3: return None
if getattr(self, '_cellGradz', None) is None:
BC = ['neumann', 'neumann']
n = self.n
G3 = kron3(ddxCellGrad(n[2], BC), speye(n[1]), speye(n[0]))
# Compute areas of cell faces & volumes
V = self.aveCC2F*self.vol
L = self.r(self.area/V, 'F','Fz', 'V')
self._cellGradz = sdiag(L)*G3
return self._cellGradz
return locals()
cellGradz = property(**cellGradz())
def edgeCurl():
doc = "Construct the 3D curl operator."
def fget(self):
if(self._edgeCurl is None):
# The number of cell centers in each direction
n1 = self.nCx
n2 = self.nCy
n3 = self.nCz
# Compute lengths of cell edges
L = self.edge
# Compute areas of cell faces
S = self.area
# Compute divergence operator on faces
d1 = ddx(n1)
d2 = ddx(n2)
d3 = ddx(n3)
D32 = kron3(d3, speye(n2), speye(n1+1))
D23 = kron3(speye(n3), d2, speye(n1+1))
D31 = kron3(d3, speye(n2+1), speye(n1))
D13 = kron3(speye(n3), speye(n2+1), d1)
D21 = kron3(speye(n3+1), d2, speye(n1))
D12 = kron3(speye(n3+1), speye(n2), d1)
O1 = spzeros(np.shape(D32)[0], np.shape(D31)[1])
O2 = spzeros(np.shape(D31)[0], np.shape(D32)[1])
O3 = spzeros(np.shape(D21)[0], np.shape(D13)[1])
C = sp.vstack((sp.hstack((O1, -D32, D23)),
sp.hstack((D31, O2, -D13)),
sp.hstack((-D21, D12, O3))), format="csr")
self._edgeCurl = sdiag(1/S)*(C*sdiag(L))
return self._edgeCurl
return locals()
_edgeCurl = None
edgeCurl = property(**edgeCurl())
# --------------- Averaging ---------------------
def aveF2CC():
doc = "Construct the averaging operator on cell faces to cell centers."
def fget(self):
if(self._aveF2CC is None):
n = self.n
if(self.dim == 1):
self._aveF2CC = av(n[0])
elif(self.dim == 2):
self._aveF2CC = (0.5)*sp.hstack((sp.kron(speye(n[1]), av(n[0])),
sp.kron(av(n[1]), speye(n[0]))), format="csr")
elif(self.dim == 3):
self._aveF2CC = (1./3.)*sp.hstack((kron3(speye(n[2]), speye(n[1]), av(n[0])),
kron3(speye(n[2]), av(n[1]), speye(n[0])),
kron3(av(n[2]), speye(n[1]), speye(n[0]))), format="csr")
return self._aveF2CC
return locals()
_aveF2CC = None
aveF2CC = property(**aveF2CC())
def aveCC2F():
doc = "Construct the averaging operator on cell cell centers to faces."
def fget(self):
if(self._aveCC2F is None):
n = self.n
if(self.dim == 1):
self._aveCC2F = avExtrap(n[0])
elif(self.dim == 2):
self._aveCC2F = sp.vstack((sp.kron(speye(n[1]), avExtrap(n[0])),
sp.kron(avExtrap(n[1]), speye(n[0]))), format="csr")
elif(self.dim == 3):
self._aveCC2F = sp.vstack((kron3(speye(n[2]), speye(n[1]), avExtrap(n[0])),
kron3(speye(n[2]), avExtrap(n[1]), speye(n[0])),
kron3(avExtrap(n[2]), speye(n[1]), speye(n[0]))), format="csr")
return self._aveCC2F
return locals()
_aveCC2F = None
aveCC2F = property(**aveCC2F())
def aveE2CC():
doc = "Construct the averaging operator on cell edges to cell centers."
def fget(self):
if(self._aveE2CC is None):
# The number of cell centers in each direction
n = self.n
if(self.dim == 1):
raise Exception('Edge Averaging does not make sense in 1D: Use Identity?')
elif(self.dim == 2):
self._aveE2CC = 0.5*sp.hstack((sp.kron(av(n[1]), speye(n[0])),
sp.kron(speye(n[1]), av(n[0]))), format="csr")
elif(self.dim == 3):
self._aveE2CC = (1./3)*sp.hstack((kron3(av(n[2]), av(n[1]), speye(n[0])),
kron3(av(n[2]), speye(n[1]), av(n[0])),
kron3(speye(n[2]), av(n[1]), av(n[0]))), format="csr")
return self._aveE2CC
return locals()
_aveE2CC = None
aveE2CC = property(**aveE2CC())
def aveN2CC():
doc = "Construct the averaging operator on cell nodes to cell centers."
def fget(self):
if(self._aveN2CC is None):
# The number of cell centers in each direction
n = self.n
if(self.dim == 1):
self._aveN2CC = av(n[0])
elif(self.dim == 2):
self._aveN2CC = sp.kron(av(n[1]), av(n[0])).tocsr()
elif(self.dim == 3):
self._aveN2CC = kron3(av(n[2]), av(n[1]), av(n[0])).tocsr()
return self._aveN2CC
return locals()
_aveN2CC = None
aveN2CC = property(**aveN2CC())
def aveN2E():
doc = "Construct the averaging operator on cell nodes to cell edges, keeping each dimension separate."
def fget(self):
if(self._aveN2E is None):
# The number of cell centers in each direction
n = self.n
if(self.dim == 1):
self._aveN2E = av(n[0])
elif(self.dim == 2):
self._aveN2E = sp.vstack((sp.kron(speye(n[1]+1), av(n[0])),
sp.kron(av(n[1]), speye(n[0]+1))), format="csr")
elif(self.dim == 3):
self._aveN2E = sp.vstack((kron3(speye(n[2]+1), speye(n[1]+1), av(n[0])),
kron3(speye(n[2]+1), av(n[1]), speye(n[0]+1)),
kron3(av(n[2]), speye(n[1]+1), speye(n[0]+1))), format="csr")
return self._aveN2E
return locals()
_aveN2E = None
aveN2E = property(**aveN2E())
def aveN2F():
doc = "Construct the averaging operator on cell nodes to cell faces, keeping each dimension separate."
def fget(self):
if(self._aveN2F is None):
# The number of cell centers in each direction
n = self.n
if(self.dim == 1):
self._aveN2F = av(n[0])
elif(self.dim == 2):
self._aveN2F = sp.vstack((sp.kron(av(n[1]), speye(n[0]+1)),
sp.kron(speye(n[1]+1), av(n[0]))), format="csr")
elif(self.dim == 3):
self._aveN2F = sp.vstack((kron3(av(n[2]), av(n[1]), speye(n[0]+1)),
kron3(av(n[2]), speye(n[1]+1), av(n[0])),
kron3(speye(n[2]+1), av(n[1]), av(n[0]))), format="csr")
return self._aveN2F
return locals()
_aveN2F = None
aveN2F = property(**aveN2F())
# --------------- Methods ---------------------
def getMass(self, materialProp=None, loc='e'):
""" Produces mass matricies.
:param str loc: Average to location: 'e'-edges, 'f'-faces
:param None,float,numpy.ndarray materialProp: property to be averaged (see below)
:rtype: scipy.sparse.csr.csr_matrix
:return: M, the mass matrix
materialProp can be::
None -> takes materialProp = 1 (default)
float -> a constant value for entire domain
numpy.ndarray -> if materialProp.size == self.nC
3D property model
if materialProp.size = self.nCz
1D (layered eath) property model
"""
if materialProp is None:
materialProp = np.ones(self.nC)
elif type(materialProp) is float:
materialProp = np.ones(self.nC)*materialProp
elif materialProp.shape == (self.nCz,):
materialProp = materialProp.repeat(self.nCx*self.nCy)
materialProp = mkvc(materialProp)
assert materialProp.shape == (self.nC,), "materialProp incorrect shape"
if loc=='e':
Av = self.aveE2CC
elif loc=='f':
Av = self.aveF2CC
else:
raise ValueError('Invalid loc')
diag = Av.T * (self.vol * mkvc(materialProp))
return sdiag(diag)
def getEdgeMass(self, materialProp=None):
"""mass matrix for products of edge functions w'*M(materialProp)*e"""
return self.getMass(loc='e', materialProp=materialProp)
def getFaceMass(self, materialProp=None):
"""mass matrix for products of face functions w'*M(materialProp)*f"""
return self.getMass(loc='f', materialProp=materialProp)
def getFaceMassDeriv(self):
Av = self.aveF2CC
return Av.T * sdiag(self.vol)
+554
View File
@@ -0,0 +1,554 @@
from scipy import sparse as sp
from SimPEG.Utils import sub2ind, ndgrid, mkvc, getSubArray, sdiag, inv3X3BlockDiagonal, inv2X2BlockDiagonal
import numpy as np
class InnerProducts(object):
"""
Class creates the inner product matrices that you need!
InnerProducts is a base class providing inner product matrices for meshes and cannot run on its own. Inherit to your favorite Mesh class.
**Example problem for DC resistivity**
.. math::
\sigma^{-1}\mathbf{J} = \\nabla \phi
We can define in weak form by integrating with a general face function F:
.. math::
\int_{\\text{cell}}{\sigma^{-1}\mathbf{J} \cdot \mathbf{F}} = \int_{\\text{cell}}{\\nabla \phi \cdot \mathbf{F}}
\int_{\\text{cell}}{\sigma^{-1}\mathbf{J} \cdot \mathbf{F}} = \int_{\\text{cell}}{(\\nabla \cdot \mathbf{F}) \phi } + \int_{\partial \\text{cell}}{ \phi \mathbf{F} \cdot \mathbf{n}}
We can then discretize for every cell:
.. math::
v_{\\text{cell}} \sigma^{-1} (\mathbf{J}_x \mathbf{F}_x +\mathbf{J}_y \mathbf{F}_y + \mathbf{J}_z \mathbf{F}_z ) = -\phi^{\\top} v_{\\text{cell}} (\mathbf{D}_{\\text{cell}} \mathbf{F}) + \\text{BC}
We can represent this in vector form (again this is for every cell), and will generalize for the case of anisotropic (tensor) sigma.
.. math::
\mathbf{F}_c^{\\top} (\sqrt{v_{\\text{cell}}} \Sigma^{-1} \sqrt{v_{\\text{cell}}}) \mathbf{J}_c = -\phi^{\\top} v_{\\text{cell}}( v_\\text{cell}^{-1} \mathbf{D}_{\\text{cell}} \mathbf{A} \mathbf{F}) + \\text{BC}
We multiply by volume on each side of the tensor conductivity to keep symmetry in the system. Here J_c is the Cartesian J (on the faces) and must be calculated differently depending on the mesh:
.. math::
\mathbf{J}_c = \mathbf{Q}_{(i)}\mathbf{J}_\\text{TENSOR} = \mathbf{N}_{(i)}^{-1}\mathbf{Q}_{(i)}\mathbf{J}_\\text{LOM}
Here the i index refers to where we choose to approximate this integral.
We will approximate this relation at every node of the cell, there are 8 in 3D, using a projection matrix Q_i to pick the appropriate fluxes.
We will then average to the cell center. For the TENSOR mesh, this looks like:
.. math::
\mathbf{F}^{\\top}
{1\over 8}
\left(\sum_{i=1}^8
\mathbf{Q}_{(i)}^{-\\top} \sqrt{v_{\\text{cell}}} \Sigma^{-1} \sqrt{v_{\\text{cell}}} \mathbf{Q}_{(i)}
\\right)
\mathbf{J}
=
-\mathbf{F}^{\\top} \mathbf{A} \mathbf{D}_{\\text{cell}}^{\\top} \phi + \\text{BC}
\mathbf{M}(\Sigma^{-1}) \mathbf{J}
=
-\mathbf{A} \mathbf{D}_{\\text{cell}}^{\\top} \phi + \\text{BC}
\mathbf{M}(\Sigma^{-1}) = {1\over 8}
\left(\sum_{i=1}^8
\mathbf{Q}_{(i)}^{-\\top} \sqrt{v_{\\text{cell}}} \Sigma^{-1} \sqrt{v_{\\text{cell}}} \mathbf{Q}_{(i)}
\\right)
The M is returned if mu is set equal to \Sigma^{-1}.
If requested (returnP=True) the projection matricies are returned as well (ordered by nodes).
Here each P (3*nC, sum(nF)) is a combination of the projection, volume, and any normalization to Cartesian coordinates:
.. math::
\mathbf{P}_{(i)} = \sqrt{ {1\over 8} v_{\\text{cell}}} \overbrace{\mathbf{N}_{(i)}^{-1}}^{\\text{LOM only}} \mathbf{Q}_{(i)}
Note that this is completed for each cell in the mesh at the same time.
"""
def __init__(self):
raise Exception('InnerProducts is a base class providing inner product matrices for meshes and cannot run on its own. Inherit to your favorite Mesh class.')
def getFaceInnerProduct(self, mu=None, returnP=False):
"""Wrapper function,
:py:func:`SimPEG.mesh.InnerProducts.InnerProducts.getFaceInnerProduct`
:py:func:`SimPEG.mesh.InnerProducts.InnerProducts.getFaceInnerProduct2D`
"""
if self.dim == 2:
return getFaceInnerProduct2D(self, mu, returnP)
elif self.dim == 3:
return getFaceInnerProduct(self, mu, returnP)
def getEdgeInnerProduct(self, sigma=None, returnP=False):
"""Wrapper function,
:py:func:`SimPEG.mesh.InnerProducts.InnerProducts.getEdgeInnerProduct`
:py:func:`SimPEG.mesh.InnerProducts.InnerProducts.getEdgeInnerProduct2D`
"""
if self.dim == 2:
return getEdgeInnerProduct2D(self, sigma, returnP)
elif self.dim == 3:
return getEdgeInnerProduct(self, sigma, returnP)
# ------------------------ Geometries ------------------------------
#
#
# node(i,j,k+1) ------ edge2(i,j,k+1) ----- node(i,j+1,k+1)
# / /
# / / |
# edge3(i,j,k) face1(i,j,k) edge3(i,j+1,k)
# / / |
# / / |
# node(i,j,k) ------ edge2(i,j,k) ----- node(i,j+1,k)
# | | |
# | | node(i+1,j+1,k+1)
# | | /
# edge1(i,j,k) face3(i,j,k) edge1(i,j+1,k)
# | | /
# | | /
# | |/
# node(i+1,j,k) ------ edge2(i+1,j,k) ----- node(i+1,j+1,k)
def getFaceInnerProduct(mesh, mu=None, returnP=False):
"""
:param numpy.array mu: material property (tensor properties are possible) at each cell center (nC, (1, 3, or 6))
:param bool returnP: returns the projection matrices
:rtype: scipy.csr_matrix
:return: M, the inner product matrix (sum(nF), sum(nF))
Depending on the number of columns (either 1, 3, or 6) of mu, the material property is interpreted as follows:
.. math::
\\vec{\mu} = \left[\\begin{matrix} \mu_{1} & 0 & 0 \\\\ 0 & \mu_{1} & 0 \\\\ 0 & 0 & \mu_{1} \end{matrix}\\right]
\\vec{\mu} = \left[\\begin{matrix} \mu_{1} & 0 & 0 \\\\ 0 & \mu_{2} & 0 \\\\ 0 & 0 & \mu_{3} \end{matrix}\\right]
\\vec{\mu} = \left[\\begin{matrix} \mu_{1} & \mu_{4} & \mu_{5} \\\\ \mu_{4} & \mu_{2} & \mu_{6} \\\\ \mu_{5} & \mu_{6} & \mu_{3} \end{matrix}\\right]
\mathbf{M}(\\vec{\mu}) = {1\over 8}
\left(\sum_{i=1}^8
\mathbf{J}_c^{-\\top} \sqrt{v_{\\text{cell}}} \\vec{\mu} \sqrt{v_{\\text{cell}}} \mathbf{J}_c
\\right)
If requested (returnP=True) the projection matricies are returned as well (ordered by nodes)::
P = [P000, P001, P010, P011, P100, P101, P110, P111]
Here each P (3*nC, sum(nF)) is a combination of the projection, volume, and any normalization to Cartesian coordinates:
.. math::
\mathbf{P}_{(i)} = \sqrt{ {1\over 8} v_{\\text{cell}}} \overbrace{\mathbf{N}_{(i)}^{-1}}^{\\text{LOM only}} \mathbf{Q}_{(i)}
Note that this is completed for each cell in the mesh at the same time.
"""
if mu is None: # default is ones
mu = np.ones((mesh.nC, 1))
m = np.array([mesh.nCx, mesh.nCy, mesh.nCz])
nc = mesh.nC
i, j, k = np.int64(range(m[0])), np.int64(range(m[1])), np.int64(range(m[2]))
iijjkk = ndgrid(i, j, k)
ii, jj, kk = iijjkk[:, 0], iijjkk[:, 1], iijjkk[:, 2]
if mesh._meshType == 'LOM':
fN1 = mesh.r(mesh.normals, 'F', 'Fx', 'M')
fN2 = mesh.r(mesh.normals, 'F', 'Fy', 'M')
fN3 = mesh.r(mesh.normals, 'F', 'Fz', 'M')
def Pxxx(pos):
ind1 = sub2ind(mesh.nFx, np.c_[ii + pos[0][0], jj + pos[0][1], kk + pos[0][2]])
ind2 = sub2ind(mesh.nFy, np.c_[ii + pos[1][0], jj + pos[1][1], kk + pos[1][2]]) + mesh.nFv[0]
ind3 = sub2ind(mesh.nFz, np.c_[ii + pos[2][0], jj + pos[2][1], kk + pos[2][2]]) + mesh.nFv[0] + mesh.nFv[1]
IND = np.r_[ind1, ind2, ind3].flatten()
PXXX = sp.coo_matrix((np.ones(3*nc), (range(3*nc), IND)), shape=(3*nc, np.sum(mesh.nF))).tocsr()
if mesh._meshType == 'LOM':
I3x3 = inv3X3BlockDiagonal(getSubArray(fN1[0], [i + pos[0][0], j + pos[0][1], k + pos[0][2]]), getSubArray(fN1[1], [i + pos[0][0], j + pos[0][1], k + pos[0][2]]), getSubArray(fN1[2], [i + pos[0][0], j + pos[0][1], k + pos[0][2]]),
getSubArray(fN2[0], [i + pos[1][0], j + pos[1][1], k + pos[1][2]]), getSubArray(fN2[1], [i + pos[1][0], j + pos[1][1], k + pos[1][2]]), getSubArray(fN2[2], [i + pos[1][0], j + pos[1][1], k + pos[1][2]]),
getSubArray(fN3[0], [i + pos[2][0], j + pos[2][1], k + pos[2][2]]), getSubArray(fN3[1], [i + pos[2][0], j + pos[2][1], k + pos[2][2]]), getSubArray(fN3[2], [i + pos[2][0], j + pos[2][1], k + pos[2][2]]))
PXXX = I3x3 * PXXX
return PXXX
# no | node | f1 | f2 | f3
# 000 | i ,j ,k | i , j, k | i, j , k | i, j, k
# 100 | i+1,j ,k | i+1, j, k | i, j , k | i, j, k
# 010 | i ,j+1,k | i , j, k | i, j+1, k | i, j, k
# 110 | i+1,j+1,k | i+1, j, k | i, j+1, k | i, j, k
# 001 | i ,j ,k+1 | i , j, k | i, j , k | i, j, k+1
# 101 | i+1,j ,k+1 | i+1, j, k | i, j , k | i, j, k+1
# 011 | i ,j+1,k+1 | i , j, k | i, j+1, k | i, j, k+1
# 111 | i+1,j+1,k+1 | i+1, j, k | i, j+1, k | i, j, k+1
# Square root of cell volume multiplied by 1/8
v = np.sqrt(0.125*mesh.vol)
V3 = sdiag(np.r_[v, v, v]) # We will multiply on each side to keep symmetry
P000 = V3*Pxxx([[0, 0, 0], [0, 0, 0], [0, 0, 0]])
P100 = V3*Pxxx([[1, 0, 0], [0, 0, 0], [0, 0, 0]])
P010 = V3*Pxxx([[0, 0, 0], [0, 1, 0], [0, 0, 0]])
P110 = V3*Pxxx([[1, 0, 0], [0, 1, 0], [0, 0, 0]])
P001 = V3*Pxxx([[0, 0, 0], [0, 0, 0], [0, 0, 1]])
P101 = V3*Pxxx([[1, 0, 0], [0, 0, 0], [0, 0, 1]])
P011 = V3*Pxxx([[0, 0, 0], [0, 1, 0], [0, 0, 1]])
P111 = V3*Pxxx([[1, 0, 0], [0, 1, 0], [0, 0, 1]])
if mu.size == mesh.nC: # Isotropic!
mu = mkvc(mu) # ensure it is a vector.
Mu = sdiag(np.r_[mu, mu, mu])
elif mu.shape[1] == 3: # Diagonal tensor
Mu = sdiag(np.r_[mu[:, 0], mu[:, 1], mu[:, 2]])
elif mu.shape[1] == 6: # Fully anisotropic
row1 = sp.hstack((sdiag(mu[:, 0]), sdiag(mu[:, 3]), sdiag(mu[:, 4])))
row2 = sp.hstack((sdiag(mu[:, 3]), sdiag(mu[:, 1]), sdiag(mu[:, 5])))
row3 = sp.hstack((sdiag(mu[:, 4]), sdiag(mu[:, 5]), sdiag(mu[:, 2])))
Mu = sp.vstack((row1, row2, row3))
A = P000.T*Mu*P000 + P001.T*Mu*P001 + P010.T*Mu*P010 + P011.T*Mu*P011 + P100.T*Mu*P100 + P101.T*Mu*P101 + P110.T*Mu*P110 + P111.T*Mu*P111
P = [P000, P001, P010, P011, P100, P101, P110, P111]
if returnP:
return A, P
else:
return A
def getFaceInnerProduct2D(mesh, mu=None, returnP=False):
"""
:param numpy.array mu: material property (tensor properties are possible) at each cell center (nC, (1, 2, or 3))
:param bool returnP: returns the projection matrices
:rtype: scipy.csr_matrix
:return: M, the inner product matrix (sum(nF), sum(nF))
Depending on the number of columns (either 1, 2, or 3) of mu, the material property is interpreted as follows:
.. math::
\\vec{\mu} = \left[\\begin{matrix} \mu_{1} & 0 \\\\ 0 & \mu_{1} \end{matrix}\\right]
\\vec{\mu} = \left[\\begin{matrix} \mu_{1} & 0 \\\\ 0 & \mu_{2} \end{matrix}\\right]
\\vec{\mu} = \left[\\begin{matrix} \mu_{1} & \mu_{3} \\\\ \mu_{3} & \mu_{2} \end{matrix}\\right]
.. math::
\mathbf{M}(\\vec{\mu}) = {1\over 4}
\left(\sum_{i=1}^4
\mathbf{J}_c^{-\\top} \sqrt{v_{\\text{cell}}} \\vec{\mu} \sqrt{v_{\\text{cell}}} \mathbf{J}_c
\\right)
If requested (returnP=True) the projection matricies are returned as well (ordered by nodes)::
P = [P00, P10, P01, P11]
Here each P (2*nC, sum(nF)) is a combination of the projection, volume, and any normalization to Cartesian coordinates:
.. math::
\mathbf{P}_{(i)} = \sqrt{ {1\over 4} v_{\\text{cell}}} \overbrace{\mathbf{N}_{(i)}^{-1}}^{\\text{LOM only}} \mathbf{Q}_{(i)}
Note that this is completed for each cell in the mesh at the same time.
"""
if mu is None: # default is ones
mu = np.ones((mesh.nC, 1))
m = np.array([mesh.nCx, mesh.nCy])
nc = mesh.nC
i, j = np.int64(range(m[0])), np.int64(range(m[1]))
iijj = ndgrid(i, j)
ii, jj = iijj[:, 0], iijj[:, 1]
if mesh._meshType == 'LOM':
fN1 = mesh.r(mesh.normals, 'F', 'Fx', 'M')
fN2 = mesh.r(mesh.normals, 'F', 'Fy', 'M')
def Pxx(pos):
ind1 = sub2ind(mesh.nFx, np.c_[ii + pos[0][0], jj + pos[0][1]])
ind2 = sub2ind(mesh.nFy, np.c_[ii + pos[1][0], jj + pos[1][1]]) + mesh.nFv[0]
IND = np.r_[ind1, ind2].flatten()
PXX = sp.coo_matrix((np.ones(2*nc), (range(2*nc), IND)), shape=(2*nc, np.sum(mesh.nF))).tocsr()
if mesh._meshType == 'LOM':
I2x2 = inv2X2BlockDiagonal(getSubArray(fN1[0], [i + pos[0][0], j + pos[0][1]]), getSubArray(fN1[1], [i + pos[0][0], j + pos[0][1]]),
getSubArray(fN2[0], [i + pos[1][0], j + pos[1][1]]), getSubArray(fN2[1], [i + pos[1][0], j + pos[1][1]]))
PXX = I2x2 * PXX
return PXX
# no | node | f1 | f2
# 00 | i ,j | i , j | i, j
# 10 | i+1,j | i+1, j | i, j
# 01 | i ,j+1 | i , j | i, j+1
# 11 | i+1,j+1 | i+1, j | i, j+1
# Square root of cell volume multiplied by 1/4
v = np.sqrt(0.25*mesh.vol)
V2 = sdiag(np.r_[v, v]) # We will multiply on each side to keep symmetry
P00 = V2*Pxx([[0, 0], [0, 0]])
P10 = V2*Pxx([[1, 0], [0, 0]])
P01 = V2*Pxx([[0, 0], [0, 1]])
P11 = V2*Pxx([[1, 0], [0, 1]])
if mu.size == mesh.nC: # Isotropic!
mu = mkvc(mu) # ensure it is a vector.
Mu = sdiag(np.r_[mu, mu])
elif mu.shape[1] == 2: # Diagonal tensor
Mu = sdiag(np.r_[mu[:, 0], mu[:, 1]])
elif mu.shape[1] == 3: # Fully anisotropic
row1 = sp.hstack((sdiag(mu[:, 0]), sdiag(mu[:, 2])))
row2 = sp.hstack((sdiag(mu[:, 2]), sdiag(mu[:, 1])))
Mu = sp.vstack((row1, row2))
A = P00.T*Mu*P00 + P10.T*Mu*P10 + P01.T*Mu*P01 + P11.T*Mu*P11
P = [P00, P10, P01, P11]
if returnP:
return A, P
else:
return A
def getEdgeInnerProduct(mesh, sigma=None, returnP=False):
"""
:param numpy.array sigma: material property (tensor properties are possible) at each cell center (nC, (1, 3, or 6))
:param bool returnP: returns the projection matrices
:rtype: scipy.csr_matrix
:return: M, the inner product matrix (sum(nE), sum(nE))
Depending on the number of columns (either 1, 3, or 6) of sigma, the material property is interpreted as follows:
.. math::
\Sigma = \left[\\begin{matrix} \sigma_{1} & 0 & 0 \\\\ 0 & \sigma_{1} & 0 \\\\ 0 & 0 & \sigma_{1} \end{matrix}\\right]
\Sigma = \left[\\begin{matrix} \sigma_{1} & 0 & 0 \\\\ 0 & \sigma_{2} & 0 \\\\ 0 & 0 & \sigma_{3} \end{matrix}\\right]
\Sigma = \left[\\begin{matrix} \sigma_{1} & \sigma_{4} & \sigma_{5} \\\\ \sigma_{4} & \sigma_{2} & \sigma_{6} \\\\ \sigma_{5} & \sigma_{6} & \sigma_{3} \end{matrix}\\right]
What is returned:
.. math::
\mathbf{M}(\Sigma) = {1\over 8}
\left(\sum_{i=1}^8
\mathbf{J}_c^{-\\top} \sqrt{v_{\\text{cell}}} \Sigma \sqrt{v_{\\text{cell}}} \mathbf{J}_c
\\right)
If requested (returnP=True) the projection matricies are returned as well (ordered by nodes)::
P = [P000, P001, P010, P011, P100, P101, P110, P111]
Here each P (3*nC, sum(nE)) is a combination of the projection, volume, and any normalization to Cartesian coordinates:
.. math::
\mathbf{P}_{(i)} = \sqrt{ {1\over 8} v_{\\text{cell}}} \overbrace{\mathbf{N}_{(i)}^{-1}}^{\\text{LOM only}} \mathbf{Q}_{(i)}
Note that this is completed for each cell in the mesh at the same time.
"""
if sigma is None: # default is ones
sigma = np.ones((mesh.nC, 1))
m = np.array([mesh.nCx, mesh.nCy, mesh.nCz])
nc = mesh.nC
i, j, k = np.int64(range(m[0])), np.int64(range(m[1])), np.int64(range(m[2]))
iijjkk = ndgrid(i, j, k)
ii, jj, kk = iijjkk[:, 0], iijjkk[:, 1], iijjkk[:, 2]
if mesh._meshType == 'LOM':
eT1 = mesh.r(mesh.tangents, 'E', 'Ex', 'M')
eT2 = mesh.r(mesh.tangents, 'E', 'Ey', 'M')
eT3 = mesh.r(mesh.tangents, 'E', 'Ez', 'M')
def Pxxx(pos):
ind1 = sub2ind(mesh.nEx, np.c_[ii + pos[0][0], jj + pos[0][1], kk + pos[0][2]])
ind2 = sub2ind(mesh.nEy, np.c_[ii + pos[1][0], jj + pos[1][1], kk + pos[1][2]]) + mesh.nEv[0]
ind3 = sub2ind(mesh.nEz, np.c_[ii + pos[2][0], jj + pos[2][1], kk + pos[2][2]]) + mesh.nEv[0] + mesh.nEv[1]
IND = np.r_[ind1, ind2, ind3].flatten()
PXXX = sp.coo_matrix((np.ones(3*nc), (range(3*nc), IND)), shape=(3*nc, np.sum(mesh.nE))).tocsr()
if mesh._meshType == 'LOM':
I3x3 = inv3X3BlockDiagonal(getSubArray(eT1[0], [i + pos[0][0], j + pos[0][1], k + pos[0][2]]), getSubArray(eT1[1], [i + pos[0][0], j + pos[0][1], k + pos[0][2]]), getSubArray(eT1[2], [i + pos[0][0], j + pos[0][1], k + pos[0][2]]),
getSubArray(eT2[0], [i + pos[1][0], j + pos[1][1], k + pos[1][2]]), getSubArray(eT2[1], [i + pos[1][0], j + pos[1][1], k + pos[1][2]]), getSubArray(eT2[2], [i + pos[1][0], j + pos[1][1], k + pos[1][2]]),
getSubArray(eT3[0], [i + pos[2][0], j + pos[2][1], k + pos[2][2]]), getSubArray(eT3[1], [i + pos[2][0], j + pos[2][1], k + pos[2][2]]), getSubArray(eT3[2], [i + pos[2][0], j + pos[2][1], k + pos[2][2]]))
PXXX = I3x3 * PXXX
return PXXX
# no | node | e1 | e2 | e3
# 000 | i ,j ,k | i ,j ,k | i ,j ,k | i ,j ,k
# 100 | i+1,j ,k | i ,j ,k | i+1,j ,k | i+1,j ,k
# 010 | i ,j+1,k | i ,j+1,k | i ,j ,k | i ,j+1,k
# 110 | i+1,j+1,k | i ,j+1,k | i+1,j ,k | i+1,j+1,k
# 001 | i ,j ,k+1 | i ,j ,k+1 | i ,j ,k+1 | i ,j ,k
# 101 | i+1,j ,k+1 | i ,j ,k+1 | i+1,j ,k+1 | i+1,j ,k
# 011 | i ,j+1,k+1 | i ,j+1,k+1 | i ,j ,k+1 | i ,j+1,k
# 111 | i+1,j+1,k+1 | i ,j+1,k+1 | i+1,j ,k+1 | i+1,j+1,k
# Square root of cell volume multiplied by 1/8
v = np.sqrt(0.125*mesh.vol)
V3 = sdiag(np.r_[v, v, v]) # We will multiply on each side to keep symmetry
P000 = V3*Pxxx([[0, 0, 0], [0, 0, 0], [0, 0, 0]])
P100 = V3*Pxxx([[0, 0, 0], [1, 0, 0], [1, 0, 0]])
P010 = V3*Pxxx([[0, 1, 0], [0, 0, 0], [0, 1, 0]])
P110 = V3*Pxxx([[0, 1, 0], [1, 0, 0], [1, 1, 0]])
P001 = V3*Pxxx([[0, 0, 1], [0, 0, 1], [0, 0, 0]])
P101 = V3*Pxxx([[0, 0, 1], [1, 0, 1], [1, 0, 0]])
P011 = V3*Pxxx([[0, 1, 1], [0, 0, 1], [0, 1, 0]])
P111 = V3*Pxxx([[0, 1, 1], [1, 0, 1], [1, 1, 0]])
if sigma.size == mesh.nC: # Isotropic!
sigma = mkvc(sigma) # ensure it is a vector.
Sigma = sdiag(np.r_[sigma, sigma, sigma])
elif sigma.shape[1] == 3: # Diagonal tensor
Sigma = sdiag(np.r_[sigma[:, 0], sigma[:, 1], sigma[:, 2]])
elif sigma.shape[1] == 6: # Fully anisotropic
row1 = sp.hstack((sdiag(sigma[:, 0]), sdiag(sigma[:, 3]), sdiag(sigma[:, 4])))
row2 = sp.hstack((sdiag(sigma[:, 3]), sdiag(sigma[:, 1]), sdiag(sigma[:, 5])))
row3 = sp.hstack((sdiag(sigma[:, 4]), sdiag(sigma[:, 5]), sdiag(sigma[:, 2])))
Sigma = sp.vstack((row1, row2, row3))
A = P000.T*Sigma*P000 + P001.T*Sigma*P001 + P010.T*Sigma*P010 + P011.T*Sigma*P011 + P100.T*Sigma*P100 + P101.T*Sigma*P101 + P110.T*Sigma*P110 + P111.T*Sigma*P111
P = [P000, P001, P010, P011, P100, P101, P110, P111]
if returnP:
return A, P
else:
return A
def getEdgeInnerProduct2D(mesh, sigma=None, returnP=False):
"""
:param numpy.array sigma: material property (tensor properties are possible) at each cell center (nC, (1, 2, or 3))
:param bool returnP: returns the projection matrices
:rtype: scipy.csr_matrix
:return: M, the inner product matrix (sum(nE), sum(nE))
Depending on the number of columns (either 1, 2, or 3) of sigma, the material property is interpreted as follows:
.. math::
\Sigma = \left[\\begin{matrix} \sigma_{1} & 0 \\\\ 0 & \sigma_{1} \end{matrix}\\right]
\Sigma = \left[\\begin{matrix} \sigma_{1} & 0 \\\\ 0 & \sigma_{2} \end{matrix}\\right]
\Sigma = \left[\\begin{matrix} \sigma_{1} & \sigma_{3} \\\\ \sigma_{3} & \sigma_{2} \end{matrix}\\right]
.. math::
\mathbf{M}(\Sigma) = {1\over 4}
\left(\sum_{i=1}^4
\mathbf{J}_c^{-\\top} \sqrt{v_{\\text{cell}}} \Sigma \sqrt{v_{\\text{cell}}} \mathbf{J}_c
\\right)
If requested (returnP=True) the projection matricies are returned as well (ordered by nodes)::
P = [P00, P10, P01, P11]
Here each P (2*nC, sum(nE)) is a combination of the projection, volume, and any normalization to Cartesian coordinates:
.. math::
\mathbf{P}_{(i)} = \sqrt{ {1\over 4} v_{\\text{cell}}} \overbrace{\mathbf{N}_{(i)}^{-1}}^{\\text{LOM only}} \mathbf{Q}_{(i)}
Note that this is completed for each cell in the mesh at the same time.
"""
if sigma is None: # default is ones
sigma = np.ones((mesh.nC, 1))
m = np.array([mesh.nCx, mesh.nCy])
nc = mesh.nC
i, j = np.int64(range(m[0])), np.int64(range(m[1]))
iijj = ndgrid(i, j)
ii, jj = iijj[:, 0], iijj[:, 1]
if mesh._meshType == 'LOM':
eT1 = mesh.r(mesh.tangents, 'E', 'Ex', 'M')
eT2 = mesh.r(mesh.tangents, 'E', 'Ey', 'M')
def Pxx(pos):
ind1 = sub2ind(mesh.nEx, np.c_[ii + pos[0][0], jj + pos[0][1]])
ind2 = sub2ind(mesh.nEy, np.c_[ii + pos[1][0], jj + pos[1][1]]) + mesh.nEv[0]
IND = np.r_[ind1, ind2].flatten()
PXX = sp.coo_matrix((np.ones(2*nc), (range(2*nc), IND)), shape=(2*nc, np.sum(mesh.nE))).tocsr()
if mesh._meshType == 'LOM':
I2x2 = inv2X2BlockDiagonal(getSubArray(eT1[0], [i + pos[0][0], j + pos[0][1]]), getSubArray(eT1[1], [i + pos[0][0], j + pos[0][1]]),
getSubArray(eT2[0], [i + pos[1][0], j + pos[1][1]]), getSubArray(eT2[1], [i + pos[1][0], j + pos[1][1]]))
PXX = I2x2 * PXX
return PXX
# no | node | e1 | e2
# 00 | i ,j | i ,j | i ,j
# 10 | i+1,j | i ,j | i+1,j
# 01 | i ,j+1 | i ,j+1 | i ,j
# 11 | i+1,j+1 | i ,j+1 | i+1,j
# Square root of cell volume multiplied by 1/4
v = np.sqrt(0.25*mesh.vol)
V2 = sdiag(np.r_[v, v]) # We will multiply on each side to keep symmetry
P00 = V2*Pxx([[0, 0], [0, 0]])
P10 = V2*Pxx([[0, 0], [1, 0]])
P01 = V2*Pxx([[0, 1], [0, 0]])
P11 = V2*Pxx([[0, 1], [1, 0]])
if sigma.size == mesh.nC: # Isotropic!
sigma = mkvc(sigma) # ensure it is a vector.
Sigma = sdiag(np.r_[sigma, sigma])
elif sigma.shape[1] == 2: # Diagonal tensor
Sigma = sdiag(np.r_[sigma[:, 0], sigma[:, 1]])
elif sigma.shape[1] == 3: # Fully anisotropic
row1 = sp.hstack((sdiag(sigma[:, 0]), sdiag(sigma[:, 2])))
row2 = sp.hstack((sdiag(sigma[:, 2]), sdiag(sigma[:, 1])))
Sigma = sp.vstack((row1, row2))
A = P00.T*Sigma*P00 + P10.T*Sigma*P10 + P01.T*Sigma*P01 + P11.T*Sigma*P11
P = [P00, P10, P01, P11]
if returnP:
return A, P
else:
return A
if __name__ == '__main__':
from TensorMesh import TensorMesh
h = [np.array([1, 2, 3, 4]), np.array([1, 2, 1, 4, 2]), np.array([1, 1, 4, 1])]
mesh = TensorMesh(h)
mu = np.ones((mesh.nC, 6))
A, P = mesh.getFaceInnerProduct(mu, returnP=True)
B, P = mesh.getEdgeInnerProduct(mu, returnP=True)
+340
View File
@@ -0,0 +1,340 @@
from SimPEG import Utils, np
from BaseMesh import BaseMesh
from DiffOperators import DiffOperators
from InnerProducts import InnerProducts
from LomView import LomView
# Some helper functions.
length2D = lambda x: (x[:, 0]**2 + x[:, 1]**2)**0.5
length3D = lambda x: (x[:, 0]**2 + x[:, 1]**2 + x[:, 2]**2)**0.5
normalize2D = lambda x: x/np.kron(np.ones((1, 2)), Utils.mkvc(length2D(x), 2))
normalize3D = lambda x: x/np.kron(np.ones((1, 3)), Utils.mkvc(length3D(x), 2))
class LogicallyOrthogonalMesh(BaseMesh, DiffOperators, InnerProducts, LomView):
"""
LogicallyOrthogonalMesh is a mesh class that deals with logically orthogonal meshes.
Example of a logically orthogonal mesh:
.. plot:: examples/mesh/plot_LogicallyOrthogonalMesh.py
"""
__metaclass__ = Utils.Save.Savable
_meshType = 'LOM'
def __init__(self, nodes):
assert type(nodes) == list, "'nodes' variable must be a list of np.ndarray"
for i, nodes_i in enumerate(nodes):
assert type(nodes_i) == np.ndarray, ("nodes[%i] is not a numpy array." % i)
assert nodes_i.shape == nodes[0].shape, ("nodes[%i] is not the same shape as nodes[0]" % i)
assert len(nodes[0].shape) == len(nodes), "Dimension mismatch"
assert len(nodes[0].shape) > 1, "Not worth using LOM for a 1D mesh."
super(LogicallyOrthogonalMesh, self).__init__(np.array(nodes[0].shape)-1, None)
# Save nodes to private variable _gridN as vectors
self._gridN = np.ones((nodes[0].size, self.dim))
for i, node_i in enumerate(nodes):
self._gridN[:, i] = Utils.mkvc(node_i.astype(float))
def gridCC():
doc = "Cell-centered grid."
def fget(self):
if self._gridCC is None:
self._gridCC = np.concatenate([self.aveN2CC*self.gridN[:,i] for i in range(self.dim)]).reshape((-1,self.dim), order='F')
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:
raise Exception("Someone deleted this. I blame you.")
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:
N = self.r(self.gridN, 'N', 'N', 'M')
if self.dim == 2:
XY = [Utils.mkvc(0.5 * (n[:, :-1] + n[:, 1:])) for n in N]
self._gridFx = np.c_[XY[0], XY[1]]
elif self.dim == 3:
XYZ = [Utils.mkvc(0.25 * (n[:, :-1, :-1] + n[:, :-1, 1:] + n[:, 1:, :-1] + n[:, 1:, 1:])) for n in N]
self._gridFx = np.c_[XYZ[0], XYZ[1], XYZ[2]]
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:
N = self.r(self.gridN, 'N', 'N', 'M')
if self.dim == 2:
XY = [Utils.mkvc(0.5 * (n[:-1, :] + n[1:, :])) for n in N]
self._gridFy = np.c_[XY[0], XY[1]]
elif self.dim == 3:
XYZ = [Utils.mkvc(0.25 * (n[:-1, :, :-1] + n[:-1, :, 1:] + n[1:, :, :-1] + n[1:, :, 1:])) for n in N]
self._gridFy = np.c_[XYZ[0], XYZ[1], XYZ[2]]
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 == 3:
N = self.r(self.gridN, 'N', 'N', 'M')
XYZ = [Utils.mkvc(0.25 * (n[:-1, :-1, :] + n[:-1, 1:, :] + n[1:, :-1, :] + n[1:, 1:, :])) for n in N]
self._gridFz = np.c_[XYZ[0], XYZ[1], XYZ[2]]
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:
N = self.r(self.gridN, 'N', 'N', 'M')
if self.dim == 2:
XY = [Utils.mkvc(0.5 * (n[:-1, :] + n[1:, :])) for n in N]
self._gridEx = np.c_[XY[0], XY[1]]
elif self.dim == 3:
XYZ = [Utils.mkvc(0.5 * (n[:-1, :, :] + n[1:, :, :])) for n in N]
self._gridEx = np.c_[XYZ[0], XYZ[1], XYZ[2]]
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:
N = self.r(self.gridN, 'N', 'N', 'M')
if self.dim == 2:
XY = [Utils.mkvc(0.5 * (n[:, :-1] + n[:, 1:])) for n in N]
self._gridEy = np.c_[XY[0], XY[1]]
elif self.dim == 3:
XYZ = [Utils.mkvc(0.5 * (n[:, :-1, :] + n[:, 1:, :])) for n in N]
self._gridEy = np.c_[XYZ[0], XYZ[1], XYZ[2]]
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 == 3:
N = self.r(self.gridN, 'N', 'N', 'M')
XYZ = [Utils.mkvc(0.5 * (n[:, :, :-1] + n[:, :, 1:])) for n in N]
self._gridEz = np.c_[XYZ[0], XYZ[1], XYZ[2]]
return self._gridEz
return locals()
_gridEz = None # Store grid by default
gridEz = property(**gridEz())
# --------------- Geometries ---------------------
#
#
# ------------------- 2D -------------------------
#
# node(i,j) node(i,j+1)
# A -------------- B
# | |
# | cell(i,j) |
# | I |
# | |
# D -------------- C
# node(i+1,j) node(i+1,j+1)
#
# ------------------- 3D -------------------------
#
#
# node(i,j,k+1) node(i,j+1,k+1)
# E --------------- F
# /| / |
# / | / |
# / | / |
# node(i,j,k) node(i,j+1,k)
# A -------------- B |
# | H ----------|---- G
# | /cell(i,j) | /
# | / I | /
# | / | /
# D -------------- C
# node(i+1,j,k) node(i+1,j+1,k)
def vol():
doc = "Construct cell volumes of the 3D model as 1d array."
def fget(self):
if(self._vol is None):
if self.dim == 2:
A, B, C, D = Utils.indexCube('ABCD', self.n+1)
normal, area = Utils.faceInfo(np.c_[self.gridN, np.zeros((self.nN, 1))], A, B, C, D)
self._vol = area
elif self.dim == 3:
# Each polyhedron can be decomposed into 5 tetrahedrons
# However, this presents a choice so we may as well divide in two ways and average.
A, B, C, D, E, F, G, H = Utils.indexCube('ABCDEFGH', self.n+1)
vol1 = (Utils.volTetra(self.gridN, A, B, D, E) + # cutted edge top
Utils.volTetra(self.gridN, B, E, F, G) + # cutted edge top
Utils.volTetra(self.gridN, B, D, E, G) + # middle
Utils.volTetra(self.gridN, B, C, D, G) + # cutted edge bottom
Utils.volTetra(self.gridN, D, E, G, H)) # cutted edge bottom
vol2 = (Utils.volTetra(self.gridN, A, F, B, C) + # cutted edge top
Utils.volTetra(self.gridN, A, E, F, H) + # cutted edge top
Utils.volTetra(self.gridN, A, H, F, C) + # middle
Utils.volTetra(self.gridN, C, H, D, A) + # cutted edge bottom
Utils.volTetra(self.gridN, C, G, H, F)) # cutted edge bottom
self._vol = (vol1 + vol2)/2
return self._vol
return locals()
_vol = None
vol = property(**vol())
def area():
doc = "Face areas."
def fget(self):
if(self._area is None or self._normals is None):
# Compute areas of cell faces
if(self.dim == 2):
xy = self.gridN
A, B = Utils.indexCube('AB', self.n+1, np.array([self.nNx, self.nCy]))
edge1 = xy[B, :] - xy[A, :]
normal1 = np.c_[edge1[:, 1], -edge1[:, 0]]
area1 = length2D(edge1)
A, D = Utils.indexCube('AD', self.n+1, np.array([self.nCx, self.nNy]))
# Note that we are doing A-D to make sure the normal points the right way.
# Think about it. Look at the picture. Normal points towards C iff you do this.
edge2 = xy[A, :] - xy[D, :]
normal2 = np.c_[edge2[:, 1], -edge2[:, 0]]
area2 = length2D(edge2)
self._area = np.r_[Utils.mkvc(area1), Utils.mkvc(area2)]
self._normals = [normalize2D(normal1), normalize2D(normal2)]
elif(self.dim == 3):
A, E, F, B = Utils.indexCube('AEFB', self.n+1, np.array([self.nNx, self.nCy, self.nCz]))
normal1, area1 = Utils.faceInfo(self.gridN, A, E, F, B, average=False, normalizeNormals=False)
A, D, H, E = Utils.indexCube('ADHE', self.n+1, np.array([self.nCx, self.nNy, self.nCz]))
normal2, area2 = Utils.faceInfo(self.gridN, A, D, H, E, average=False, normalizeNormals=False)
A, B, C, D = Utils.indexCube('ABCD', self.n+1, np.array([self.nCx, self.nCy, self.nNz]))
normal3, area3 = Utils.faceInfo(self.gridN, A, B, C, D, average=False, normalizeNormals=False)
self._area = np.r_[Utils.mkvc(area1), Utils.mkvc(area2), Utils.mkvc(area3)]
self._normals = [normal1, normal2, normal3]
return self._area
return locals()
_area = None
area = property(**area())
def normals():
doc = """Face normals: calling this will average
the computed normals so that there is one
per face. This is especially relevant in
3D, as there are up to 4 different normals
for each face that will be different.
To reshape the normals into a matrix and get the y component::
NyX, NyY, NyZ = M.r(M.normals, 'F', 'Fy', 'M')
"""
def fget(self):
if(self._normals is None):
self.area # calling .area will create the face normals
if self.dim == 2:
return normalize2D(np.r_[self._normals[0], self._normals[1]])
elif self.dim == 3:
normal1 = (self._normals[0][0] + self._normals[0][1] + self._normals[0][2] + self._normals[0][3])/4
normal2 = (self._normals[1][0] + self._normals[1][1] + self._normals[1][2] + self._normals[1][3])/4
normal3 = (self._normals[2][0] + self._normals[2][1] + self._normals[2][2] + self._normals[2][3])/4
return normalize3D(np.r_[normal1, normal2, normal3])
return locals()
_normals = None
normals = property(**normals())
def edge():
doc = "Edge legnths."
def fget(self):
if(self._edge is None or self._tangents is None):
if(self.dim == 2):
xy = self.gridN
A, D = Utils.indexCube('AD', self.n+1, np.array([self.nCx, self.nNy]))
edge1 = xy[D, :] - xy[A, :]
A, B = Utils.indexCube('AB', self.n+1, np.array([self.nNx, self.nCy]))
edge2 = xy[B, :] - xy[A, :]
self._edge = np.r_[Utils.mkvc(length2D(edge1)), Utils.mkvc(length2D(edge2))]
self._tangents = np.r_[edge1, edge2]/np.c_[self._edge, self._edge]
elif(self.dim == 3):
xyz = self.gridN
A, D = Utils.indexCube('AD', self.n+1, np.array([self.nCx, self.nNy, self.nNz]))
edge1 = xyz[D, :] - xyz[A, :]
A, B = Utils.indexCube('AB', self.n+1, np.array([self.nNx, self.nCy, self.nNz]))
edge2 = xyz[B, :] - xyz[A, :]
A, E = Utils.indexCube('AE', self.n+1, np.array([self.nNx, self.nNy, self.nCz]))
edge3 = xyz[E, :] - xyz[A, :]
self._edge = np.r_[Utils.mkvc(length3D(edge1)), Utils.mkvc(length3D(edge2)), Utils.mkvc(length3D(edge3))]
self._tangents = np.r_[edge1, edge2, edge3]/np.c_[self._edge, self._edge, self._edge]
return self._edge
return locals()
_edge = None
edge = property(**edge())
def tangents():
doc = "Edge tangents."
def fget(self):
if(self._tangents is None):
self.edge # calling .edge will create the tangents
return self._tangents
return locals()
_tangents = None
tangents = property(**tangents())
if __name__ == '__main__':
nc = 5
h1 = np.cumsum(np.r_[0, np.ones(nc)/(nc)])
nc = 7
h2 = np.cumsum(np.r_[0, np.ones(nc)/(nc)])
h3 = np.cumsum(np.r_[0, np.ones(nc)/(nc)])
dee3 = True
if dee3:
X, Y, Z = Utils.ndgrid(h1, h2, h3, vector=False)
M = LogicallyOrthogonalMesh([X, Y, Z])
else:
X, Y = Utils.ndgrid(h1, h2, vector=False)
M = LogicallyOrthogonalMesh([X, Y])
print M.r(M.normals, 'F', 'Fx', 'V')
+95
View File
@@ -0,0 +1,95 @@
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
from mpl_toolkits.mplot3d import Axes3D
from SimPEG.Utils import mkvc
class LomView(object):
"""
Provides viewing functions for LogicallyOrthogonalMesh
This class is inherited by LogicallyOrthogonalMesh
"""
def __init__(self):
pass
def plotGrid(self, length=0.05):
"""Plot the nodal, cell-centered and staggered grids for 1,2 and 3 dimensions.
.. plot:: examples/mesh/plot_LogicallyOrthogonalMesh.py
"""
NN = self.r(self.gridN, 'N', 'N', 'M')
if self.dim == 2:
fig = plt.figure(2)
fig.clf()
ax = plt.subplot(111)
X1 = np.c_[mkvc(NN[0][:-1, :]), mkvc(NN[0][1:, :]), mkvc(NN[0][:-1, :])*np.nan].flatten()
Y1 = np.c_[mkvc(NN[1][:-1, :]), mkvc(NN[1][1:, :]), mkvc(NN[1][:-1, :])*np.nan].flatten()
X2 = np.c_[mkvc(NN[0][:, :-1]), mkvc(NN[0][:, 1:]), mkvc(NN[0][:, :-1])*np.nan].flatten()
Y2 = np.c_[mkvc(NN[1][:, :-1]), mkvc(NN[1][:, 1:]), mkvc(NN[1][:, :-1])*np.nan].flatten()
X = np.r_[X1, X2]
Y = np.r_[Y1, Y2]
plt.plot(X, Y)
plt.hold(True)
Nx = self.r(self.normals, 'F', 'Fx', 'V')
Ny = self.r(self.normals, 'F', 'Fy', 'V')
Tx = self.r(self.tangents, 'E', 'Ex', 'V')
Ty = self.r(self.tangents, 'E', 'Ey', 'V')
plt.plot(self.gridN[:, 0], self.gridN[:, 1], 'bo')
nX = np.c_[self.gridFx[:, 0], self.gridFx[:, 0] + Nx[0]*length, self.gridFx[:, 0]*np.nan].flatten()
nY = np.c_[self.gridFx[:, 1], self.gridFx[:, 1] + Nx[1]*length, self.gridFx[:, 1]*np.nan].flatten()
plt.plot(self.gridFx[:, 0], self.gridFx[:, 1], 'rs')
plt.plot(nX, nY, 'r-')
nX = np.c_[self.gridFy[:, 0], self.gridFy[:, 0] + Ny[0]*length, self.gridFy[:, 0]*np.nan].flatten()
nY = np.c_[self.gridFy[:, 1], self.gridFy[:, 1] + Ny[1]*length, self.gridFy[:, 1]*np.nan].flatten()
#plt.plot(self.gridFy[:, 0], self.gridFy[:, 1], 'gs')
plt.plot(nX, nY, 'g-')
tX = np.c_[self.gridEx[:, 0], self.gridEx[:, 0] + Tx[0]*length, self.gridEx[:, 0]*np.nan].flatten()
tY = np.c_[self.gridEx[:, 1], self.gridEx[:, 1] + Tx[1]*length, self.gridEx[:, 1]*np.nan].flatten()
plt.plot(self.gridEx[:, 0], self.gridEx[:, 1], 'r^')
plt.plot(tX, tY, 'r-')
nX = np.c_[self.gridEy[:, 0], self.gridEy[:, 0] + Ty[0]*length, self.gridEy[:, 0]*np.nan].flatten()
nY = np.c_[self.gridEy[:, 1], self.gridEy[:, 1] + Ty[1]*length, self.gridEy[:, 1]*np.nan].flatten()
#plt.plot(self.gridEy[:, 0], self.gridEy[:, 1], 'g^')
plt.plot(nX, nY, 'g-')
plt.axis('equal')
elif self.dim == 3:
fig = plt.figure(3)
fig.clf()
ax = fig.add_subplot(111, projection='3d')
X1 = np.c_[mkvc(NN[0][:-1, :, :]), mkvc(NN[0][1:, :, :]), mkvc(NN[0][:-1, :, :])*np.nan].flatten()
Y1 = np.c_[mkvc(NN[1][:-1, :, :]), mkvc(NN[1][1:, :, :]), mkvc(NN[1][:-1, :, :])*np.nan].flatten()
Z1 = np.c_[mkvc(NN[2][:-1, :, :]), mkvc(NN[2][1:, :, :]), mkvc(NN[2][:-1, :, :])*np.nan].flatten()
X2 = np.c_[mkvc(NN[0][:, :-1, :]), mkvc(NN[0][:, 1:, :]), mkvc(NN[0][:, :-1, :])*np.nan].flatten()
Y2 = np.c_[mkvc(NN[1][:, :-1, :]), mkvc(NN[1][:, 1:, :]), mkvc(NN[1][:, :-1, :])*np.nan].flatten()
Z2 = np.c_[mkvc(NN[2][:, :-1, :]), mkvc(NN[2][:, 1:, :]), mkvc(NN[2][:, :-1, :])*np.nan].flatten()
X3 = np.c_[mkvc(NN[0][:, :, :-1]), mkvc(NN[0][:, :, 1:]), mkvc(NN[0][:, :, :-1])*np.nan].flatten()
Y3 = np.c_[mkvc(NN[1][:, :, :-1]), mkvc(NN[1][:, :, 1:]), mkvc(NN[1][:, :, :-1])*np.nan].flatten()
Z3 = np.c_[mkvc(NN[2][:, :, :-1]), mkvc(NN[2][:, :, 1:]), mkvc(NN[2][:, :, :-1])*np.nan].flatten()
X = np.r_[X1, X2, X3]
Y = np.r_[Y1, Y2, Y3]
Z = np.r_[Z1, Z2, Z3]
plt.plot(X, Y, 'b', zs=Z)
ax.set_zlabel('x3')
ax.grid(True)
ax.hold(False)
ax.set_xlabel('x1')
ax.set_ylabel('x2')
fig.show()
+438
View File
@@ -0,0 +1,438 @@
from SimPEG import Utils, np, sp
from BaseMesh import BaseMesh
from TensorView import TensorView
from DiffOperators import DiffOperators
from InnerProducts import InnerProducts
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::
from SimPEG import mesh, Utils
M = mesh.TensorMesh(Utils.meshTensors(((10,10),(40,10),(10,10)), ((10,10),(20,10),(0,0))))
M.plotGrid()
For a quick tensor mesh on a (10x12x15) unit cube::
mesh = TensorMesh([10, 12, 15])
"""
__metaclass__ = Utils.Save.Savable
_meshType = 'TENSOR'
def __init__(self, h_in, x0=None):
assert type(h_in) is list, 'h_in must be a list'
h = range(len(h_in))
for i, h_i in enumerate(h_in):
if type(h_i) in [int, long, float]:
# This gives you something over the unit cube.
h_i = np.ones(int(h_i))/int(h_i)
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)
h[i] = h_i[:] # make a copy.
BaseMesh.__init__(self, np.array([x.size for x in h]), x0)
assert len(h) == len(self.x0), "Dimension mismatch. x0 != len(h)"
# Ensure h contains 1D vectors
self._h = [Utils.mkvc(x.astype(float)) 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 = Utils.ndgrid(self.getTensor('CC'))
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 = Utils.ndgrid(self.getTensor('N'))
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 = Utils.ndgrid(self.getTensor('Fx'))
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 = Utils.ndgrid(self.getTensor('Fy'))
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 = Utils.ndgrid(self.getTensor('Fz'))
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 = Utils.ndgrid(self.getTensor('Ex'))
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 = Utils.ndgrid(self.getTensor('Ey'))
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 = Utils.ndgrid(self.getTensor('Ez'))
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 = Utils.mkvc(vh[0])
elif(self.dim == 2):
# Cell sizes in each direction
self._vol = Utils.mkvc(np.outer(vh[0], vh[1]))
elif(self.dim == 3):
# Cell sizes in each direction
self._vol = Utils.mkvc(np.outer(Utils.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_[Utils.mkvc(area1), Utils.mkvc(area2)]
elif(self.dim == 3):
area1 = np.outer(np.ones(n[0]+1), Utils.mkvc(np.outer(vh[1], vh[2])))
area2 = np.outer(vh[0], Utils.mkvc(np.outer(np.ones(n[1]+1), vh[2])))
area3 = np.outer(vh[0], Utils.mkvc(np.outer(vh[1], np.ones(n[2]+1))))
self._area = np.r_[Utils.mkvc(area1), Utils.mkvc(area2), Utils.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 = Utils.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_[Utils.mkvc(l1), Utils.mkvc(l2)]
elif(self.dim == 3):
l1 = np.outer(vh[0], Utils.mkvc(np.outer(np.ones(n[1]+1), np.ones(n[2]+1))))
l2 = np.outer(np.ones(n[0]+1), Utils.mkvc(np.outer(vh[1], np.ones(n[2]+1))))
l3 = np.outer(np.ones(n[0]+1), Utils.mkvc(np.outer(np.ones(n[1]+1), vh[2])))
self._edge = np.r_[Utils.mkvc(l1), Utils.mkvc(l2), Utils.mkvc(l3)]
return self._edge
return locals()
_edge = None
edge = property(**edge())
# --------------- Methods ---------------------
def getTensor(self, locType):
""" Returns a tensor list.
:param str locType: What tensor (see below)
:rtype: list
:return: list of the tensors that make up the mesh.
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 faces
'Fy' -> y-component of field defined on faces
'Fz' -> z-component of field defined on faces
'N' -> scalar field defined on nodes
'CC' -> scalar field defined on cell centers
"""
if locType is 'Fx':
ten = [self.vectorNx , self.vectorCCy, self.vectorCCz]
elif locType is 'Fy':
ten = [self.vectorCCx, self.vectorNy , self.vectorCCz]
elif locType is 'Fz':
ten = [self.vectorCCx, self.vectorCCy, self.vectorNz ]
elif locType is 'Ex':
ten = [self.vectorCCx, self.vectorNy , self.vectorNz ]
elif locType is 'Ey':
ten = [self.vectorNx , self.vectorCCy, self.vectorNz ]
elif locType is 'Ez':
ten = [self.vectorNx , self.vectorNy , self.vectorCCz]
elif locType is 'CC':
ten = [self.vectorCCx, self.vectorCCy, self.vectorCCz]
elif locType is 'N':
ten = [self.vectorNx , self.vectorNy , self.vectorNz ]
return [t for t in ten if t is not None]
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 faces
'Fy' -> y-component of field defined on faces
'Fz' -> z-component of field defined on faces
'N' -> scalar field defined on nodes
'CC' -> scalar field defined on cell centers
"""
loc = np.atleast_2d(loc)
assert np.all(self.isInside(loc)), "Points outside of mesh"
ind = 0 if 'x' in locType else 1 if 'y' in locType else 2 if 'z' in locType else -1
if locType in ['Fx','Fy','Fz','Ex','Ey','Ez'] and self.dim >= ind:
nF_nE = self.nFv if 'F' in locType else self.nEv
components = [Utils.spzeros(loc.shape[0], n) for n in nF_nE]
components[ind] = Utils.interpmat(loc, *self.getTensor(locType))
Q = sp.hstack(components)
elif locType in ['CC', 'N']:
Q = Utils.interpmat(loc, *self.getTensor(locType))
else:
raise NotImplementedError('getInterpolationMat: locType=='+locType+' and mesh.dim=='+str(self.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)
print M
xn = M.plotGrid()
+403
View File
@@ -0,0 +1,403 @@
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
from mpl_toolkits.mplot3d import Axes3D
from SimPEG.Utils import mkvc, animate
class TensorView(object):
"""
Provides viewing functions for TensorMesh
This class is inherited by TensorMesh
"""
def __init__(self):
pass
def plotImage(self, I, imageType='CC', figNum=1,ax=None,direction='z',numbering=True,annotationColor='w',showIt=False,clim=None):
"""
Mesh.plotImage(I)
Plots scalar fields on the given mesh.
Input:
:param numpy.array I: scalar field
Optional Input:
:param str imageType: type of image ('CC','N','F','Fx','Fy','Fz','E','Ex','Ey','Ez') or combinations, e.g. ExEy or FxFz
:param int figNum: number of figure to plot to
:param matplotlib.axes.Axes ax: axis to plot to
:param str direction: slice dimensions, 3D only ('x', 'y', 'z')
:param bool numbering: show numbering of slices, 3D only
:param str annotationColor: color of annotation, e.g. 'w', 'k', 'b'
:param bool showIt: call plt.show()
.. plot:: examples/mesh/plot_image_2D.py
:include-source:
.. plot:: examples/mesh/plot_image_3D.py
:include-source:
"""
assert type(I) == np.ndarray, "I must be a numpy array"
assert type(numbering) == bool, "numbering must be a bool"
assert direction in ["x", "y","z"], "direction must be either x,y, or z"
if imageType == 'CC':
assert I.size == self.nC, "Incorrect dimensions for CC."
elif imageType == 'N':
assert I.size == self.nN, "Incorrect dimensions for N."
elif imageType == 'Fx':
if I.size != np.prod(self.nFx): I, fy, fz = self.r(I,'F','F','M')
elif imageType == 'Fy':
if I.size != np.prod(self.nFy): fx, I, fz = self.r(I,'F','F','M')
elif imageType == 'Fz':
if I.size != np.prod(self.nFz): fx, fy, I = self.r(I,'F','F','M')
elif imageType == 'Ex':
if I.size != np.prod(self.nEx): I, ey, ez = self.r(I,'E','E','M')
elif imageType == 'Ey':
if I.size != np.prod(self.nEy): ex, I, ez = self.r(I,'E','E','M')
elif imageType == 'Ez':
if I.size != np.prod(self.nEz): ex, ey, I = self.r(I,'E','E','M')
elif imageType[0] == 'E':
plotAll = len(imageType) == 1
options = {"direction":direction,"numbering":numbering,"annotationColor":annotationColor,"showIt":showIt}
fig = plt.figure(figNum)
# Determine the subplot number: 131, 121
numPlots = 130 if plotAll else len(imageType)/2*10+100
pltNum = 1
ex, ey, ez = self.r(I,'E','E','M')
if plotAll or 'Ex' in imageType:
ax_x = plt.subplot(numPlots+pltNum)
self.plotImage(ex, imageType='Ex', ax=ax_x, **options)
pltNum +=1
if plotAll or 'Ey' in imageType:
ax_y = plt.subplot(numPlots+pltNum)
self.plotImage(ey, imageType='Ey', ax=ax_y, **options)
pltNum +=1
if plotAll or 'Ez' in imageType:
ax_z = plt.subplot(numPlots+pltNum)
self.plotImage(ez, imageType='Ez', ax=ax_z, **options)
pltNum +=1
return
elif imageType[0] == 'F':
plotAll = len(imageType) == 1
options = {"direction":direction,"numbering":numbering,"annotationColor":annotationColor,"showIt":showIt}
fig = plt.figure(figNum)
# Determine the subplot number: 131, 121
numPlots = 130 if plotAll else len(imageType)/2*10+100
pltNum = 1
fxyz = self.r(I,'F','F','M')
if plotAll or 'Fx' in imageType:
ax_x = plt.subplot(numPlots+pltNum)
self.plotImage(fxyz[0], imageType='Fx', ax=ax_x, **options)
pltNum +=1
if plotAll or 'Fy' in imageType:
ax_y = plt.subplot(numPlots+pltNum)
self.plotImage(fxyz[1], imageType='Fy', ax=ax_y, **options)
pltNum +=1
if plotAll or 'Fz' in imageType:
ax_z = plt.subplot(numPlots+pltNum)
self.plotImage(fxyz[2], imageType='Fz', ax=ax_z, **options)
pltNum +=1
return
else:
raise Exception("imageType must be 'CC', 'N','Fx','Fy','Fz','Ex','Ey','Ez'")
if ax is None:
fig = plt.figure(figNum)
fig.clf()
ax = plt.subplot(111)
else:
assert isinstance(ax,matplotlib.axes.Axes), "ax must be an Axes!"
fig = ax.figure
if self.dim == 1:
if imageType == 'CC':
ph = ax.plot(self.vectorCCx, I, '-ro')
elif imageType == 'N':
ph = ax.plot(self.vectorNx, I, '-bs')
ax.set_xlabel("x")
ax.axis('tight')
elif self.dim == 2:
if imageType == 'CC':
C = I[:].reshape(self.n, order='F')
elif imageType == 'N':
C = I[:].reshape(self.n+1, order='F')
C = 0.25*(C[:-1, :-1] + C[1:, :-1] + C[:-1, 1:] + C[1:, 1:])
elif imageType == 'Fx':
C = I[:].reshape(self.nFx, order='F')
C = 0.5*(C[:-1, :] + C[1:, :] )
elif imageType == 'Fy':
C = I[:].reshape(self.nFy, order='F')
C = 0.5*(C[:, :-1] + C[:, 1:] )
elif imageType == 'Ex':
C = I[:].reshape(self.nEx, order='F')
C = 0.5*(C[:,:-1] + C[:,1:] )
elif imageType == 'Ey':
C = I[:].reshape(self.nEy, order='F')
C = 0.5*(C[:-1,:] + C[1:,:] )
if clim is None:
clim = [C.min(),C.max()]
ph = ax.pcolormesh(self.vectorNx, self.vectorNy, C.T, vmin=clim[0], vmax=clim[1])
ax.axis('tight')
ax.set_xlabel("x")
ax.set_ylabel("y")
elif self.dim == 3:
if direction == 'z':
# get copy of image and average to cell-centres is necessary
if imageType == 'CC':
Ic = I[:].reshape(self.n, order='F')
elif imageType == 'N':
Ic = I[:].reshape(self.n+1, order='F')
Ic = .125*(Ic[:-1,:-1,:-1]+Ic[1:,:-1,:-1] + Ic[:-1,1:,:-1]+ Ic[1:,1:,:-1]+ Ic[:-1,:-1,1:]+Ic[1:,:-1,1:] + Ic[:-1,1:,1:]+ Ic[1:,1:,1:] )
elif imageType == 'Fx':
Ic = I[:].reshape(self.nFx, order='F')
Ic = .5*(Ic[:-1,:,:]+Ic[1:,:,:])
elif imageType == 'Fy':
Ic = I[:].reshape(self.nFy, order='F')
Ic = .5*(Ic[:,:-1,:]+Ic[:,1:,:])
elif imageType == 'Fz':
Ic = I[:].reshape(self.nFz, order='F')
Ic = .5*(Ic[:,:,:-1]+Ic[:,:,1:])
elif imageType == 'Ex':
Ic = I[:].reshape(self.nEx, order='F')
Ic = .25*(Ic[:,:-1,:-1]+Ic[:,1:,:-1]+Ic[:,:-1,1:]+Ic[:,1:,:1])
elif imageType == 'Ey':
Ic = I[:].reshape(self.nEy, order='F')
Ic = .25*(Ic[:-1,:,:-1]+Ic[1:,:,:-1]+Ic[:-1,:,1:]+Ic[1:,:,:1])
elif imageType == 'Ez':
Ic = I[:].reshape(self.nEz, order='F')
Ic = .25*(Ic[:-1,:-1,:]+Ic[1:,:-1,:]+Ic[:-1,1:,:]+Ic[1:,:1,:])
# determine number oE slices in x and y dimension
nX = np.ceil(np.sqrt(self.nCz))
nY = np.ceil(self.nCz/nX)
# allocate space for montage
nCx = self.nCx
nCy = self.nCy
C = np.zeros((nX*nCx,nY*nCy))
for iy in range(int(nY)):
for ix in range(int(nX)):
iz = ix + iy*nX
if iz < self.nCz:
C[ix*nCx:(ix+1)*nCx, iy*nCy:(iy+1)*nCy] = Ic[:, :, iz]
else:
C[ix*nCx:(ix+1)*nCx, iy*nCy:(iy+1)*nCy] = np.nan
C = np.ma.masked_where(np.isnan(C), C)
xx = np.r_[0, np.cumsum(np.kron(np.ones((nX, 1)), self.hx).ravel())]
yy = np.r_[0, np.cumsum(np.kron(np.ones((nY, 1)), self.hy).ravel())]
# Plot the mesh
if clim is None:
clim = [C.min(),C.max()]
ph = ax.pcolormesh(xx, yy, C.T, vmin=clim[0], vmax=clim[1])
# Plot the lines
gx = np.arange(nX+1)*(self.vectorNx[-1]-self.x0[0])
gy = np.arange(nY+1)*(self.vectorNy[-1]-self.x0[1])
# Repeat and seperate with NaN
gxX = np.c_[gx, gx, gx+np.nan].ravel()
gxY = np.kron(np.ones((nX+1, 1)), np.array([0, sum(self.hy)*nY, np.nan])).ravel()
gyX = np.kron(np.ones((nY+1, 1)), np.array([0, sum(self.hx)*nX, np.nan])).ravel()
gyY = np.c_[gy, gy, gy+np.nan].ravel()
ax.plot(gxX, gxY, annotationColor+'-', linewidth=2)
ax.plot(gyX, gyY, annotationColor+'-', linewidth=2)
ax.axis('tight')
if numbering:
pad = np.sum(self.hx)*0.04
for iy in range(int(nY)):
for ix in range(int(nX)):
iz = ix + iy*nX
if iz < self.nCz:
ax.text((ix+1)*(self.vectorNx[-1]-self.x0[0])-pad,(iy)*(self.vectorNy[-1]-self.x0[1])+pad,
'#%i'%iz,color=annotationColor,verticalalignment='bottom',horizontalalignment='right',size='x-large')
ax.set_title(imageType)
if showIt: plt.show()
return ph
def plotGrid(self, nodes=False, faces=False, centers=False, edges=False, lines=True, showIt=False):
"""Plot the nodal, cell-centered and staggered grids for 1,2 and 3 dimensions.
:param bool nodes: plot nodes
:param bool faces: plot faces
:param bool centers: plot centers
:param bool edges: plot edges
:param bool lines: plot lines connecting nodes
:param bool showIt: call plt.show()
.. plot:: examples/mesh/plot_grid_2D.py
:include-source:
.. plot:: examples/mesh/plot_grid_3D.py
:include-source:
"""
if self.dim == 1:
fig = plt.figure(1)
fig.clf()
ax = plt.subplot(111)
xn = self.gridN
xc = self.gridCC
ax.hold(True)
ax.plot(xn, np.ones(np.shape(xn)), 'bs')
ax.plot(xc, np.ones(np.shape(xc)), 'ro')
ax.plot(xn, np.ones(np.shape(xn)), 'k--')
ax.grid(True)
ax.hold(False)
ax.set_xlabel('x1')
if showIt: plt.show()
elif self.dim == 2:
fig = plt.figure(2)
fig.clf()
ax = plt.subplot(111)
xn = self.gridN
xc = self.gridCC
xs1 = self.gridFx
xs2 = self.gridFy
ax.hold(True)
if nodes: ax.plot(xn[:, 0], xn[:, 1], 'bs')
if centers: ax.plot(xc[:, 0], xc[:, 1], 'ro')
if faces:
ax.plot(xs1[:, 0], xs1[:, 1], 'g>')
ax.plot(xs2[:, 0], xs2[:, 1], 'g^')
if edges:
ax.plot(self.gridEx[:, 0], self.gridEx[:, 1], 'c>')
ax.plot(self.gridEy[:, 0], self.gridEy[:, 1], 'c^')
# Plot the grid lines
if lines:
NN = self.r(self.gridN, 'N', 'N', 'M')
X1 = np.c_[mkvc(NN[0][0, :]), mkvc(NN[0][self.nCx, :]), mkvc(NN[0][0, :])*np.nan].flatten()
Y1 = np.c_[mkvc(NN[1][0, :]), mkvc(NN[1][self.nCx, :]), mkvc(NN[1][0, :])*np.nan].flatten()
X2 = np.c_[mkvc(NN[0][:, 0]), mkvc(NN[0][:, self.nCy]), mkvc(NN[0][:, 0])*np.nan].flatten()
Y2 = np.c_[mkvc(NN[1][:, 0]), mkvc(NN[1][:, self.nCy]), mkvc(NN[1][:, 0])*np.nan].flatten()
X = np.r_[X1, X2]
Y = np.r_[Y1, Y2]
plt.plot(X, Y)
ax.grid(True)
ax.hold(False)
ax.set_xlabel('x1')
ax.set_ylabel('x2')
if showIt: plt.show()
elif self.dim == 3:
fig = plt.figure(3)
fig.clf()
ax = fig.add_subplot(111, projection='3d')
xn = self.gridN
xc = self.gridCC
xfs1 = self.gridFx
xfs2 = self.gridFy
xfs3 = self.gridFz
xes1 = self.gridEx
xes2 = self.gridEy
xes3 = self.gridEz
ax.hold(True)
if nodes: ax.plot(xn[:, 0], xn[:, 1], 'bs', zs=xn[:, 2])
if centers: ax.plot(xc[:, 0], xc[:, 1], 'ro', zs=xc[:, 2])
if faces:
ax.plot(xfs1[:, 0], xfs1[:, 1], 'g>', zs=xfs1[:, 2])
ax.plot(xfs2[:, 0], xfs2[:, 1], 'g<', zs=xfs2[:, 2])
ax.plot(xfs3[:, 0], xfs3[:, 1], 'g^', zs=xfs3[:, 2])
if edges:
ax.plot(xes1[:, 0], xes1[:, 1], 'k>', zs=xes1[:, 2])
ax.plot(xes2[:, 0], xes2[:, 1], 'k<', zs=xes2[:, 2])
ax.plot(xes3[:, 0], xes3[:, 1], 'k^', zs=xes3[:, 2])
# Plot the grid lines
if lines:
NN = self.r(self.gridN, 'N', 'N', 'M')
X1 = np.c_[mkvc(NN[0][0, :, :]), mkvc(NN[0][self.nCx, :, :]), mkvc(NN[0][0, :, :])*np.nan].flatten()
Y1 = np.c_[mkvc(NN[1][0, :, :]), mkvc(NN[1][self.nCx, :, :]), mkvc(NN[1][0, :, :])*np.nan].flatten()
Z1 = np.c_[mkvc(NN[2][0, :, :]), mkvc(NN[2][self.nCx, :, :]), mkvc(NN[2][0, :, :])*np.nan].flatten()
X2 = np.c_[mkvc(NN[0][:, 0, :]), mkvc(NN[0][:, self.nCy, :]), mkvc(NN[0][:, 0, :])*np.nan].flatten()
Y2 = np.c_[mkvc(NN[1][:, 0, :]), mkvc(NN[1][:, self.nCy, :]), mkvc(NN[1][:, 0, :])*np.nan].flatten()
Z2 = np.c_[mkvc(NN[2][:, 0, :]), mkvc(NN[2][:, self.nCy, :]), mkvc(NN[2][:, 0, :])*np.nan].flatten()
X3 = np.c_[mkvc(NN[0][:, :, 0]), mkvc(NN[0][:, :, self.nCz]), mkvc(NN[0][:, :, 0])*np.nan].flatten()
Y3 = np.c_[mkvc(NN[1][:, :, 0]), mkvc(NN[1][:, :, self.nCz]), mkvc(NN[1][:, :, 0])*np.nan].flatten()
Z3 = np.c_[mkvc(NN[2][:, :, 0]), mkvc(NN[2][:, :, self.nCz]), mkvc(NN[2][:, :, 0])*np.nan].flatten()
X = np.r_[X1, X2, X3]
Y = np.r_[Y1, Y2, Y3]
Z = np.r_[Z1, Z2, Z3]
plt.plot(X, Y, 'b-', zs=Z)
ax.grid(True)
ax.hold(False)
ax.set_xlabel('x1')
ax.set_ylabel('x2')
ax.set_zlabel('x3')
if showIt: plt.show()
def slicer(mesh, var, imageType='CC', normal='z', index=0, ax=None, clim=None):
assert normal in 'xyz', 'normal must be x, y, or z'
if ax is None: ax = plt.subplot(111)
I = mesh.r(var,'CC','CC','M')
axes = [p for p in 'xyz' if p not in normal.lower()]
if normal is 'x': I = I[index,:,:]
if normal is 'y': I = I[:,index,:]
if normal is 'z': I = I[:,:,index]
if clim is None: clim = [I.min(),I.max()]
p = ax.pcolormesh(getattr(mesh,'vectorN'+axes[0]),getattr(mesh,'vectorN'+axes[1]),I.T,vmin=clim[0],vmax=clim[1])
ax.axis('tight')
ax.set_xlabel(axes[0])
ax.set_ylabel(axes[1])
return p
def videoSlicer(mesh,var,imageType='CC',normal='z',figsize=(10,8)):
assert mesh.dim > 2, 'This is for 3D meshes only.'
# First set up the figure, the axis, and the plot element we want to animate
fig = plt.figure(figsize=figsize)
ax = plt.axes()
clim = [var.min(),var.max()]
plt.colorbar(mesh.slicer(var, imageType=imageType, normal=normal, index=0, ax=ax, clim=clim))
tlt = plt.title(normal)
def animateFrame(i):
mesh.slicer(var, imageType=imageType, normal=normal, index=i, ax=ax, clim=clim)
tlt.set_text(normal.upper()+('-Slice: %d, %4.4f' % (i,getattr(mesh,'vectorCC'+normal)[i])))
return animate(fig, animateFrame, frames=mesh.nCv['xyz'.index(normal)])
def video(mesh, var, function, figsize=(10, 8), colorbar=True, skip=1):
"""
Call a function for a list of models to create a video.
::
def function(var, ax, clim, tlt, i):
tlt.set_text('%%d'%%i)
return mesh.plotImage(var, imageType='CC', ax=ax, clim=clim)
mesh.video([model1, model2, ..., modeln],function)
"""
# First set up the figure, the axis, and the plot element we want to animate
fig = plt.figure(figsize=figsize)
ax = plt.axes()
VAR = np.concatenate(var)
clim = [VAR.min(),VAR.max()]
tlt = plt.title('')
if colorbar:
plt.colorbar(function(var[0],ax,clim,tlt,0))
frames = np.arange(0,len(var),skip)
def animateFrame(j):
i = frames[j]
function(var[i],ax,clim,tlt,i)
return animate(fig, animateFrame, frames=len(frames))
+8
View File
@@ -0,0 +1,8 @@
from Cyl1DMesh import Cyl1DMesh
from TensorMesh import TensorMesh
from LogicallyOrthogonalMesh import LogicallyOrthogonalMesh
from BaseMesh import BaseMesh
from TensorView import TensorView
from LomView import LomView
from InnerProducts import InnerProducts
from DiffOperators import DiffOperators