mirror of
https://github.com/wassname/simpeg.git
synced 2026-06-27 22:23:39 +08:00
ab249d31b3
See the Linear example for updates on how to migrate to this version.
440 lines
12 KiB
Python
440 lines
12 KiB
Python
import Utils, numpy as np, scipy.sparse as sp
|
|
from Tests import checkDerivative
|
|
|
|
class IdentityMap(object):
|
|
"""
|
|
SimPEG Map
|
|
|
|
"""
|
|
|
|
__metaclass__ = Utils.SimPEGMetaClass
|
|
|
|
counter = None #: A SimPEG.Utils.Counter object
|
|
mesh = None #: A SimPEG Mesh
|
|
|
|
def __init__(self, mesh):
|
|
self.mesh = mesh
|
|
|
|
def transform(self, m):
|
|
"""
|
|
:param numpy.array m: model
|
|
:rtype: numpy.array
|
|
:return: transformed model
|
|
|
|
The *transform* changes the model into the physical property.
|
|
|
|
"""
|
|
return m
|
|
|
|
def transformInverse(self, D):
|
|
"""
|
|
:param numpy.array D: physical property
|
|
:rtype: numpy.array
|
|
:return: model
|
|
|
|
The *transformInverse* changes the physical property into the model.
|
|
|
|
.. note:: The *transformInverse* may not be easy to create in general.
|
|
|
|
"""
|
|
raise NotImplementedError('The transformInverse is not implemented.')
|
|
|
|
def transformDeriv(self, m):
|
|
"""
|
|
:param numpy.array m: model
|
|
:rtype: scipy.csr_matrix
|
|
:return: derivative of transformed model
|
|
|
|
The *transform* changes the model into the physical property.
|
|
The *transformDeriv* provides the derivative of the *transform*.
|
|
"""
|
|
return sp.identity(m.size)
|
|
|
|
@property
|
|
def nP(self):
|
|
"""Number of parameters in the model."""
|
|
return self.mesh.nC
|
|
|
|
def example(self):
|
|
return np.random.rand(self.nP)
|
|
|
|
def test(self, m=None, **kwargs):
|
|
print 'Testing the %s Class!' % self.__class__.__name__
|
|
if m is None:
|
|
m = self.example()
|
|
if 'plotIt' not in kwargs:
|
|
kwargs['plotIt'] = False
|
|
return checkDerivative(lambda m : [self.transform(m), self.transformDeriv(m)], m, **kwargs)
|
|
|
|
def _assertMatchesPair(self, pair):
|
|
assert (isinstance(self, pair) or
|
|
isinstance(self, ComboMap) and isinstance(self.maps[0], pair)
|
|
), "Mapping object must be an instance of a %s class."%(pair.__name__)
|
|
|
|
def __mul__(self, val):
|
|
if isinstance(val, ComboMap):
|
|
return ComboMap(self.mesh, [self] + val.maps)
|
|
elif isinstance(val, IdentityMap):
|
|
return ComboMap(self.mesh, [self, val])
|
|
elif type(val) is np.ndarray:
|
|
return self.transform(val)
|
|
|
|
class NonLinearMap(object):
|
|
"""
|
|
SimPEG NonLinearMap
|
|
|
|
"""
|
|
|
|
__metaclass__ = Utils.SimPEGMetaClass
|
|
|
|
counter = None #: A SimPEG.Utils.Counter object
|
|
mesh = None #: A SimPEG Mesh
|
|
|
|
def __init__(self, mesh):
|
|
self.mesh = mesh
|
|
|
|
def transform(self, u, m):
|
|
"""
|
|
:param numpy.array u: fields
|
|
:param numpy.array m: model
|
|
:rtype: numpy.array
|
|
:return: transformed model
|
|
|
|
The *transform* changes the model into the physical property.
|
|
|
|
"""
|
|
return m
|
|
|
|
def transformDerivU(self, u, m):
|
|
"""
|
|
:param numpy.array u: fields
|
|
:param numpy.array m: model
|
|
:rtype: scipy.csr_matrix
|
|
:return: derivative of transformed model
|
|
|
|
The *transform* changes the model into the physical property.
|
|
The *transformDerivU* provides the derivative of the *transform* with respect to the fields.
|
|
"""
|
|
raise NotImplementedError('The transformDerivU is not implemented.')
|
|
|
|
|
|
def transformDerivM(self, u, m):
|
|
"""
|
|
:param numpy.array u: fields
|
|
:param numpy.array m: model
|
|
:rtype: scipy.csr_matrix
|
|
:return: derivative of transformed model
|
|
|
|
The *transform* changes the model into the physical property.
|
|
The *transformDerivU* provides the derivative of the *transform* with respect to the model.
|
|
"""
|
|
raise NotImplementedError('The transformDerivM is not implemented.')
|
|
|
|
@property
|
|
def nP(self):
|
|
"""Number of parameters in the model."""
|
|
return self.mesh.nC
|
|
|
|
def example(self):
|
|
raise NotImplementedError('The example is not implemented.')
|
|
|
|
def test(self, m=None):
|
|
raise NotImplementedError('The test is not implemented.')
|
|
|
|
|
|
class ExpMap(IdentityMap):
|
|
"""SimPEG ExpMap"""
|
|
|
|
def __init__(self, mesh, **kwargs):
|
|
IdentityMap.__init__(self, mesh, **kwargs)
|
|
|
|
def transform(self, m):
|
|
"""
|
|
:param numpy.array m: model
|
|
:rtype: numpy.array
|
|
:return: transformed model
|
|
|
|
The *transform* changes the model into the physical property.
|
|
|
|
A common example of this is to invert for electrical conductivity
|
|
in log space. In this case, your model will be log(sigma) and to
|
|
get back to sigma, you can take the exponential:
|
|
|
|
.. math::
|
|
|
|
m = \log{\sigma}
|
|
|
|
\exp{m} = \exp{\log{\sigma}} = \sigma
|
|
"""
|
|
return np.exp(Utils.mkvc(m))
|
|
|
|
|
|
def transformInverse(self, D):
|
|
"""
|
|
:param numpy.array D: physical property
|
|
:rtype: numpy.array
|
|
:return: model
|
|
|
|
The *transformInverse* changes the physical property into the model.
|
|
|
|
.. math::
|
|
|
|
m = \log{\sigma}
|
|
|
|
"""
|
|
return np.log(Utils.mkvc(D))
|
|
|
|
|
|
def transformDeriv(self, m):
|
|
"""
|
|
:param numpy.array m: model
|
|
:rtype: scipy.csr_matrix
|
|
:return: derivative of transformed model
|
|
|
|
The *transform* changes the model into the physical property.
|
|
The *transformDeriv* provides the derivative of the *transform*.
|
|
|
|
If the model *transform* is:
|
|
|
|
.. math::
|
|
|
|
m = \log{\sigma}
|
|
|
|
\exp{m} = \exp{\log{\sigma}} = \sigma
|
|
|
|
Then the derivative is:
|
|
|
|
.. math::
|
|
|
|
\\frac{\partial \exp{m}}{\partial m} = \\text{sdiag}(\exp{m})
|
|
"""
|
|
return Utils.sdiag(np.exp(Utils.mkvc(m)))
|
|
|
|
class Vertical1DMap(IdentityMap):
|
|
"""Vertical1DMap
|
|
|
|
Given a 1D vector through the last dimension
|
|
of the mesh, this will extend to the full
|
|
model space.
|
|
"""
|
|
|
|
def __init__(self, mesh, **kwargs):
|
|
IdentityMap.__init__(self, mesh, **kwargs)
|
|
|
|
@property
|
|
def nP(self):
|
|
"""Number of model properties.
|
|
|
|
The number of cells in the
|
|
last dimension of the mesh."""
|
|
return self.mesh.vnC[self.mesh.dim-1]
|
|
|
|
def transform(self, m):
|
|
"""
|
|
:param numpy.array m: model
|
|
:rtype: numpy.array
|
|
:return: transformed model
|
|
"""
|
|
repNum = self.mesh.vnC[:self.mesh.dim-1].prod()
|
|
return Utils.mkvc(m).repeat(repNum)
|
|
|
|
def transformDeriv(self, m):
|
|
"""
|
|
:param numpy.array m: model
|
|
:rtype: scipy.csr_matrix
|
|
:return: derivative of transformed model
|
|
"""
|
|
repNum = self.mesh.vnC[:self.mesh.dim-1].prod()
|
|
repVec = sp.csr_matrix(
|
|
(np.ones(repNum),
|
|
(range(repNum), np.zeros(repNum))
|
|
), shape=(repNum, 1))
|
|
return sp.kron(sp.identity(self.nP), repVec)
|
|
|
|
class Mesh2Mesh(IdentityMap):
|
|
"""
|
|
Takes a model on one mesh are translates it to another mesh.
|
|
|
|
.. plot::
|
|
|
|
from SimPEG import *
|
|
M = Mesh.TensorMesh([100,100])
|
|
h1 = Utils.meshTensor([(6,7,-1.5),(6,10),(6,7,1.5)])
|
|
h1 = h1/h1.sum()
|
|
M2 = Mesh.TensorMesh([h1,h1])
|
|
V = Utils.ModelBuilder.randomModel(M.vnC, seed=79, its=50)
|
|
v = Utils.mkvc(V)
|
|
modh = Maps.Mesh2Mesh([M,M2])
|
|
modH = Maps.Mesh2Mesh([M2,M])
|
|
H = modH.transform(v)
|
|
h = modh.transform(H)
|
|
ax = plt.subplot(131)
|
|
M.plotImage(v, ax=ax)
|
|
ax.set_title('Fine Mesh (Original)')
|
|
ax = plt.subplot(132)
|
|
M2.plotImage(H,clim=[0,1],ax=ax)
|
|
ax.set_title('Course Mesh')
|
|
ax = plt.subplot(133)
|
|
M.plotImage(h,clim=[0,1],ax=ax)
|
|
ax.set_title('Fine Mesh (Interpolated)')
|
|
|
|
"""
|
|
|
|
def __init__(self, meshes, **kwargs):
|
|
Utils.setKwargs(self, **kwargs)
|
|
|
|
assert type(meshes) is list, "meshes must be a list of two meshes"
|
|
assert len(meshes) == 2, "meshes must be a list of two meshes"
|
|
assert meshes[0].dim == meshes[1].dim, """The two meshes must be the same dimension"""
|
|
|
|
self.mesh = meshes[0]
|
|
self.mesh2 = meshes[1]
|
|
|
|
self.P = self.mesh2.getInterpolationMat(self.mesh.gridCC,'CC',zerosOutside=True)
|
|
|
|
@property
|
|
def nP(self):
|
|
"""Number of parameters in the model."""
|
|
return self.mesh2.nC
|
|
def transform(self, m):
|
|
return self.P*m
|
|
def transformDeriv(self, m):
|
|
return self.P
|
|
|
|
|
|
class ActiveCells(IdentityMap):
|
|
"""
|
|
Active model parameters.
|
|
|
|
"""
|
|
|
|
indActive = None #: Active Cells
|
|
valInactive = None #: Values of inactive Cells
|
|
nC = None #: Number of cells in the full model
|
|
|
|
def __init__(self, mesh, indActive, valInactive, nC=None):
|
|
self.mesh = mesh
|
|
|
|
self.nC = nC or mesh.nC
|
|
|
|
if indActive.dtype is not bool:
|
|
z = np.zeros(self.nC,dtype=bool)
|
|
z[indActive] = True
|
|
indActive = z
|
|
self.indActive = indActive
|
|
self.indInactive = np.logical_not(indActive)
|
|
if Utils.isScalar(valInactive):
|
|
valInactive = np.ones(self.nC)*float(valInactive)
|
|
|
|
valInactive[self.indActive] = 0
|
|
self.valInactive = valInactive
|
|
|
|
inds = np.nonzero(self.indActive)[0]
|
|
self.P = sp.csr_matrix((np.ones(inds.size),(inds, range(inds.size))), shape=(self.nC, self.nP))
|
|
|
|
@property
|
|
def nP(self):
|
|
"""Number of parameters in the model."""
|
|
return self.indActive.sum()
|
|
|
|
def transform(self, m):
|
|
return self.P*m + self.valInactive
|
|
def transformDeriv(self, m):
|
|
return self.P
|
|
|
|
class ComboMap(IdentityMap):
|
|
"""Combination of various maps."""
|
|
|
|
def __init__(self, mesh, maps, **kwargs):
|
|
IdentityMap.__init__(self, mesh, **kwargs)
|
|
|
|
self.maps = []
|
|
for m in maps:
|
|
if not isinstance(m, IdentityMap):
|
|
self.maps += [m(mesh, **kwargs)]
|
|
else:
|
|
self.maps += [m]
|
|
|
|
@property
|
|
def nP(self):
|
|
"""Number of model properties.
|
|
|
|
The number of cells in the
|
|
last dimension of the mesh."""
|
|
return self.maps[-1].nP
|
|
|
|
def transform(self, m):
|
|
for map_i in reversed(self.maps):
|
|
m = map_i.transform(m)
|
|
return m
|
|
|
|
def transformDeriv(self, m):
|
|
deriv = 1
|
|
mi = m
|
|
for map_i in reversed(self.maps):
|
|
deriv = map_i.transformDeriv(mi) * deriv
|
|
mi = map_i.transform(mi)
|
|
return deriv
|
|
|
|
def __mul__(self, val):
|
|
if isinstance(val, ComboMap):
|
|
return ComboMap(self.mesh, self.maps + val.maps)
|
|
elif isinstance(val, IdentityMap):
|
|
return ComboMap(self.mesh, self.maps + [val])
|
|
elif type(val) is np.ndarray:
|
|
return self.transform(val)
|
|
|
|
class ComplexMap(IdentityMap):
|
|
"""docstring for ComplexMap
|
|
|
|
default nP is nC in the mesh times 2 [real, imag]
|
|
|
|
"""
|
|
def __init__(self, mesh, nP=None):
|
|
IdentityMap.__init__(self, mesh)
|
|
if nP is not None:
|
|
assert nP%2 == 0, 'nP must be even.'
|
|
self._nP = nP or (self.mesh.nC * 2)
|
|
|
|
@property
|
|
def nP(self):
|
|
return self._nP
|
|
|
|
def transform(self, m):
|
|
nC = self.mesh.nC
|
|
return m[:nC] + m[nC:]*1j
|
|
|
|
def transformDeriv(self, m):
|
|
nC = self.nP/2
|
|
shp = (nC, nC*2)
|
|
def fwd(v):
|
|
return v[:nC] + v[nC:]*1j
|
|
def adj(v):
|
|
return np.r_[v.real,v.imag]
|
|
return Utils.SimPEGLinearOperator(shp,fwd,adj)
|
|
|
|
transformInverse = transformDeriv
|
|
|
|
|
|
if __name__ == '__main__':
|
|
from SimPEG import *
|
|
mesh = Mesh.TensorMesh([10,8])
|
|
emap = ExpMap(mesh)
|
|
vmap = Vertical1DMap(mesh)
|
|
|
|
combo = emap*vmap
|
|
print combo
|
|
print combo.maps
|
|
|
|
# combo = ComboMap(mesh, [ExpMap, Vertical1DMap])
|
|
|
|
# combo = ComboMap(mesh, [ExpMap, Vertical1DMap])
|
|
# m = combo.example()
|
|
# print m.shape
|
|
# print combo.test(np.arange(8))
|
|
mesh = Mesh.TensorMesh([10,8])
|
|
mapping = ComplexMap(mesh)
|
|
m = mapping.example()
|
|
print m.shape
|
|
# print mapping.test(m)
|