mirror of
https://github.com/wassname/simpeg.git
synced 2026-08-12 12:30:37 +08:00
Merge branch 'dcip/dev' of https://github.com/simpeg/simpeg into dcip/dev
This commit is contained in:
@@ -20,6 +20,7 @@ env:
|
|||||||
- TEST_DIR=tests/em/tdem
|
- TEST_DIR=tests/em/tdem
|
||||||
- TEST_DIR=tests/dcip
|
- TEST_DIR=tests/dcip
|
||||||
- TEST_DIR=tests/flow
|
- TEST_DIR=tests/flow
|
||||||
|
- TEST_DIR=tests/mt
|
||||||
- TEST_DIR=tests/examples
|
- TEST_DIR=tests/examples
|
||||||
- TEST_DIR=tests/em/fdem/inverse/adjoint
|
- TEST_DIR=tests/em/fdem/inverse/adjoint
|
||||||
- TEST_DIR=tests/em/fdem/forward
|
- TEST_DIR=tests/em/fdem/forward
|
||||||
@@ -55,3 +56,5 @@ notifications:
|
|||||||
email:
|
email:
|
||||||
- rowanc1@gmail.com
|
- rowanc1@gmail.com
|
||||||
- lindseyheagy@gmail.com
|
- lindseyheagy@gmail.com
|
||||||
|
- gkrosen@gmail.com
|
||||||
|
- sgkang09@gmail.com
|
||||||
|
|||||||
+39
-43
@@ -237,53 +237,49 @@ class SaveOutputDictEveryIteration(_SaveEveryIteration):
|
|||||||
# Save the file as a npz
|
# Save the file as a npz
|
||||||
np.savez('{:03d}-{:s}'.format(self.opt.iter,self.fileName), iter=self.opt.iter, beta=self.invProb.beta, phi_d=self.invProb.phi_d, phi_m=self.invProb.phi_m, phi_ms=phi_ms, phi_mx=phi_mx, phi_my=phi_my, phi_mz=phi_mz,f=self.opt.f, m=self.invProb.curModel,dpred=self.invProb.dpred)
|
np.savez('{:03d}-{:s}'.format(self.opt.iter,self.fileName), iter=self.opt.iter, beta=self.invProb.beta, phi_d=self.invProb.phi_d, phi_m=self.invProb.phi_m, phi_ms=phi_ms, phi_mx=phi_mx, phi_my=phi_my, phi_mz=phi_mz,f=self.opt.f, m=self.invProb.curModel,dpred=self.invProb.dpred)
|
||||||
|
|
||||||
|
class SaveOutputDictEveryIteration(_SaveEveryIteration):
|
||||||
|
"""SaveOutputDictEveryIteration
|
||||||
|
A directive that saves some relevant information from the inversion run to a numpy .npz dictionary file (see numpy.savez function for further info).
|
||||||
|
"""
|
||||||
|
|
||||||
|
def initialize(self):
|
||||||
|
print "SimPEG.SaveOutputDictEveryIteration will save your inversion progress as dictionary: '%s-###.npz'"%self.fileName
|
||||||
|
|
||||||
|
def endIter(self):
|
||||||
|
# Save the data.
|
||||||
|
ms = self.reg.Ws * ( self.reg.mapping * (self.invProb.curModel - self.reg.mref) )
|
||||||
|
phi_ms = 0.5*ms.dot(ms)
|
||||||
|
if self.reg.smoothModel == True:
|
||||||
|
mref = self.reg.mref
|
||||||
|
else:
|
||||||
|
mref = 0
|
||||||
|
mx = self.reg.Wx * ( self.reg.mapping * (self.invProb.curModel - mref) )
|
||||||
|
phi_mx = 0.5 * mx.dot(mx)
|
||||||
|
if self.prob.mesh.dim==2:
|
||||||
|
my = self.reg.Wy * ( self.reg.mapping * (self.invProb.curModel - mref) )
|
||||||
|
phi_my = 0.5 * my.dot(my)
|
||||||
|
else:
|
||||||
|
phi_my = 'NaN'
|
||||||
|
if self.prob.mesh.dim==3 and 'CYL' not in self.prob.mesh._meshType:
|
||||||
|
mz = self.reg.Wz * ( self.reg.mapping * (self.invProb.curModel - mref) )
|
||||||
|
phi_mz = 0.5 * mz.dot(mz)
|
||||||
|
else:
|
||||||
|
phi_mz = 'NaN'
|
||||||
|
|
||||||
|
|
||||||
class update_IRLS(InversionDirective):
|
# Save the file as a npz
|
||||||
|
np.savez('{:s}-{:03d}'.format(self.fileName,self.opt.iter), iter=self.opt.iter, beta=self.invProb.beta, phi_d=self.invProb.phi_d, phi_m=self.invProb.phi_m, phi_ms=phi_ms, phi_mx=phi_mx, phi_my=phi_my, phi_mz=phi_mz,f=self.opt.f, m=self.invProb.curModel,dpred=self.invProb.dpred)
|
||||||
m = None
|
|
||||||
eps_min = None
|
|
||||||
factor = None
|
|
||||||
gamma = None
|
|
||||||
phi_m_last = None
|
|
||||||
|
|
||||||
def initialize(self):
|
|
||||||
|
|
||||||
# Scale the regularization for changes in norm
|
|
||||||
if getattr(self, 'phi_m_last', None) is not None:
|
|
||||||
self.reg.gamma = 1.
|
|
||||||
phim_new = self.reg.eval(self.invProb.curModel)
|
|
||||||
self.gamma = self.phi_m_last / phim_new
|
|
||||||
|
|
||||||
self.reg.gamma = self.gamma
|
|
||||||
|
|
||||||
def endIter(self):
|
|
||||||
# Cool the threshold parameter
|
|
||||||
if getattr(self, 'factor', None) is not None:
|
|
||||||
eps = self.reg.eps / self.factor
|
|
||||||
|
|
||||||
if getattr(self, 'eps_min', None) is not None:
|
|
||||||
self.reg.eps = np.max([self.eps_min,eps])
|
|
||||||
else:
|
|
||||||
self.reg.eps = eps
|
|
||||||
|
|
||||||
|
|
||||||
# Update the model used for the IRLS weights
|
|
||||||
if getattr(self, 'm', None) is None:
|
|
||||||
self.reg.m = self.invProb.curModel
|
|
||||||
|
|
||||||
# Update the pre-conditioner
|
# class UpdateReferenceModel(Parameter):
|
||||||
diagA = np.sum(self.prob.G**2.,axis=0) + self.invProb.beta*(self.reg.W.T*self.reg.W).diagonal() * (self.reg.mapping * np.ones(self.prob.mesh.nC))**2.
|
|
||||||
PC = Utils.sdiag(diagA**-1.)
|
|
||||||
|
|
||||||
self.opt.approxHinv = PC
|
# mref0 = None
|
||||||
|
|
||||||
phim_new = self.reg.eval(self.invProb.curModel)
|
# def nextIter(self):
|
||||||
self.reg.gamma = self.reg.gamma * self.invProb.phi_m_last / phim_new
|
# mref = getattr(self, 'm_prev', None)
|
||||||
|
# if mref is None:
|
||||||
#==============================================================================
|
# if self.debug: print 'UpdateReferenceModel is using mref0'
|
||||||
# import pylab as plt
|
# mref = self.mref0
|
||||||
# plt.figure()
|
# self.m_prev = self.invProb.m_current
|
||||||
# ax = plt.subplot(221)
|
# return mref
|
||||||
# self.prob.mesh.plotSlice(self.invProb.curModel, ax = ax, normal = 'Z', ind=-5, clim = (0, 0.005))
|
|
||||||
#==============================================================================
|
|
||||||
|
|||||||
+229
-97
@@ -15,18 +15,20 @@ class BaseFDEMProblem(BaseEMProblem):
|
|||||||
.. math ::
|
.. math ::
|
||||||
|
|
||||||
\mathbf{C} \mathbf{e} + i \omega \mathbf{b} = \mathbf{s_m} \\\\
|
\mathbf{C} \mathbf{e} + i \omega \mathbf{b} = \mathbf{s_m} \\\\
|
||||||
{\mathbf{C}^T \mathbf{M_{\mu^{-1}}^f} \mathbf{b} - \mathbf{M_{\sigma}^e} \mathbf{e} = \mathbf{M^e} \mathbf{s_e}}
|
{\mathbf{C}^{\\top} \mathbf{M_{\mu^{-1}}^f} \mathbf{b} - \mathbf{M_{\sigma}^e} \mathbf{e} = \mathbf{s_e}}
|
||||||
|
|
||||||
if using the E-B formulation (:code:`Problem_e`
|
if using the E-B formulation (:code:`Problem_e`
|
||||||
or :code:`Problem_b`) or the magnetic field
|
or :code:`Problem_b`). Note that in this case, :math:`\mathbf{s_e}` is an integrated quantity.
|
||||||
|
|
||||||
|
If we write Maxwell's equations in terms of
|
||||||
\\\(\\\mathbf{h}\\\) and current density \\\(\\\mathbf{j}\\\)
|
\\\(\\\mathbf{h}\\\) and current density \\\(\\\mathbf{j}\\\)
|
||||||
|
|
||||||
.. math ::
|
.. math ::
|
||||||
|
|
||||||
\mathbf{C}^T \mathbf{M_{\\rho}^f} \mathbf{j} + i \omega \mathbf{M_{\mu}^e} \mathbf{h} = \mathbf{M^e} \mathbf{s_m} \\\\
|
\mathbf{C}^{\\top} \mathbf{M_{\\rho}^f} \mathbf{j} + i \omega \mathbf{M_{\mu}^e} \mathbf{h} = \mathbf{s_m} \\\\
|
||||||
\mathbf{C} \mathbf{h} - \mathbf{j} = \mathbf{s_e}
|
\mathbf{C} \mathbf{h} - \mathbf{j} = \mathbf{s_e}
|
||||||
|
|
||||||
if using the H-J formulation (:code:`Problem_j` or :code:`Problem_h`).
|
if using the H-J formulation (:code:`Problem_j` or :code:`Problem_h`). Note that here, :math:`\mathbf{s_m}` is an integrated quantity.
|
||||||
|
|
||||||
The problem performs the elimination so that we are solving the system for \\\(\\\mathbf{e},\\\mathbf{b},\\\mathbf{j} \\\) or \\\(\\\mathbf{h}\\\)
|
The problem performs the elimination so that we are solving the system for \\\(\\\mathbf{e},\\\mathbf{b},\\\mathbf{j} \\\) or \\\(\\\mathbf{h}\\\)
|
||||||
"""
|
"""
|
||||||
@@ -36,7 +38,11 @@ class BaseFDEMProblem(BaseEMProblem):
|
|||||||
|
|
||||||
def fields(self, m=None):
|
def fields(self, m=None):
|
||||||
"""
|
"""
|
||||||
Solve the forward problem for the fields.
|
Solve the forward problem for the fields.
|
||||||
|
|
||||||
|
:param numpy.array m: inversion model (nP,)
|
||||||
|
:rtype numpy.array:
|
||||||
|
:return F: forward solution
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.curModel = m
|
self.curModel = m
|
||||||
@@ -55,7 +61,13 @@ class BaseFDEMProblem(BaseEMProblem):
|
|||||||
|
|
||||||
def Jvec(self, m, v, u=None):
|
def Jvec(self, m, v, u=None):
|
||||||
"""
|
"""
|
||||||
Sensitivity times a vector
|
Sensitivity times a vector.
|
||||||
|
|
||||||
|
:param numpy.array m: inversion model (nP,)
|
||||||
|
:param numpy.array v: vector which we take sensitivity product with (nP,)
|
||||||
|
:param SimPEG.EM.FDEM.Fields u: fields object
|
||||||
|
:rtype numpy.array:
|
||||||
|
:return: Jv (ndata,)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if u is None:
|
if u is None:
|
||||||
@@ -83,6 +95,7 @@ class BaseFDEMProblem(BaseEMProblem):
|
|||||||
df_dmFun = getattr(u, '_%sDeriv_m'%rx.projField, None)
|
df_dmFun = getattr(u, '_%sDeriv_m'%rx.projField, None)
|
||||||
df_dm = df_dmFun(src, v, adjoint=False)
|
df_dm = df_dmFun(src, v, adjoint=False)
|
||||||
|
|
||||||
|
|
||||||
Df_Dm = np.array(df_dudu_dm + df_dm,dtype=complex)
|
Df_Dm = np.array(df_dudu_dm + df_dm,dtype=complex)
|
||||||
|
|
||||||
P = lambda v: rx.projectFieldsDeriv(src, self.mesh, u, v) # wrt u, also have wrt m
|
P = lambda v: rx.projectFieldsDeriv(src, self.mesh, u, v) # wrt u, also have wrt m
|
||||||
@@ -94,7 +107,13 @@ class BaseFDEMProblem(BaseEMProblem):
|
|||||||
|
|
||||||
def Jtvec(self, m, v, u=None):
|
def Jtvec(self, m, v, u=None):
|
||||||
"""
|
"""
|
||||||
Sensitivity transpose times a vector
|
Sensitivity transpose times a vector
|
||||||
|
|
||||||
|
:param numpy.array m: inversion model (nP,)
|
||||||
|
:param numpy.array v: vector which we take adjoint product with (nP,)
|
||||||
|
:param SimPEG.EM.FDEM.Fields u: fields object
|
||||||
|
:rtype numpy.array:
|
||||||
|
:return: Jv (ndata,)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if u is None:
|
if u is None:
|
||||||
@@ -133,6 +152,7 @@ class BaseFDEMProblem(BaseEMProblem):
|
|||||||
|
|
||||||
du_dmT += dfT_dm
|
du_dmT += dfT_dm
|
||||||
|
|
||||||
|
# TODO: this should be taken care of by the reciever
|
||||||
real_or_imag = rx.projComp
|
real_or_imag = rx.projComp
|
||||||
if real_or_imag is 'real':
|
if real_or_imag is 'real':
|
||||||
Jtv += np.array(du_dmT,dtype=complex).real
|
Jtv += np.array(du_dmT,dtype=complex).real
|
||||||
@@ -147,11 +167,11 @@ class BaseFDEMProblem(BaseEMProblem):
|
|||||||
|
|
||||||
def getSourceTerm(self, freq):
|
def getSourceTerm(self, freq):
|
||||||
"""
|
"""
|
||||||
Evaluates the sources for a given frequency and puts them in matrix form
|
Evaluates the sources for a given frequency and puts them in matrix form
|
||||||
|
|
||||||
:param float freq: Frequency
|
:param float freq: Frequency
|
||||||
:rtype: numpy.ndarray (nE or nF, nSrc)
|
:rtype: (numpy.ndarray, numpy.ndarray)
|
||||||
:return: S_m, S_e
|
:return: S_m, S_e (nE or nF, nSrc)
|
||||||
"""
|
"""
|
||||||
Srcs = self.survey.getSrcByFreq(freq)
|
Srcs = self.survey.getSrcByFreq(freq)
|
||||||
if self._eqLocs is 'FE':
|
if self._eqLocs is 'FE':
|
||||||
@@ -175,20 +195,22 @@ class BaseFDEMProblem(BaseEMProblem):
|
|||||||
|
|
||||||
class Problem_e(BaseFDEMProblem):
|
class Problem_e(BaseFDEMProblem):
|
||||||
"""
|
"""
|
||||||
By eliminating the magnetic flux density using
|
By eliminating the magnetic flux density using
|
||||||
|
|
||||||
.. math ::
|
|
||||||
|
|
||||||
\mathbf{b} = \\frac{1}{i \omega}\\left(-\mathbf{C} \mathbf{e} + \mathbf{s_m}\\right)
|
|
||||||
|
|
||||||
|
|
||||||
we can write Maxwell's equations as a second order system in \\\(\\\mathbf{e}\\\) only:
|
|
||||||
|
|
||||||
.. math ::
|
.. math ::
|
||||||
|
|
||||||
\\left(\mathbf{C}^T \mathbf{M_{\mu^{-1}}^f} \mathbf{C}+ i \omega \mathbf{M^e_{\sigma}} \\right)\mathbf{e} = \mathbf{C}^T \mathbf{M_{\mu^{-1}}^f}\mathbf{s_m} -i\omega\mathbf{M^e}\mathbf{s_e}
|
\mathbf{b} = \\frac{1}{i \omega}\\left(-\mathbf{C} \mathbf{e} + \mathbf{s_m}\\right)
|
||||||
|
|
||||||
which we solve for \\\(\\\mathbf{e}\\\).
|
|
||||||
|
we can write Maxwell's equations as a second order system in \\\(\\\mathbf{e}\\\) only:
|
||||||
|
|
||||||
|
.. math ::
|
||||||
|
|
||||||
|
\\left(\mathbf{C}^{\\top} \mathbf{M_{\mu^{-1}}^f} \mathbf{C}+ i \omega \mathbf{M^e_{\sigma}} \\right)\mathbf{e} = \mathbf{C}^{\\top} \mathbf{M_{\mu^{-1}}^f}\mathbf{s_m} -i\omega\mathbf{M^e}\mathbf{s_e}
|
||||||
|
|
||||||
|
which we solve for :math:`\mathbf{e}`.
|
||||||
|
|
||||||
|
:param SimPEG.Mesh mesh: mesh
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_fieldType = 'e'
|
_fieldType = 'e'
|
||||||
@@ -200,13 +222,16 @@ class Problem_e(BaseFDEMProblem):
|
|||||||
|
|
||||||
def getA(self, freq):
|
def getA(self, freq):
|
||||||
"""
|
"""
|
||||||
.. math ::
|
System matrix
|
||||||
\mathbf{A} = \mathbf{C}^T \mathbf{M_{\mu^{-1}}^f} \mathbf{C} + i \omega \mathbf{M^e_{\sigma}}
|
|
||||||
|
|
||||||
:param float freq: Frequency
|
.. math ::
|
||||||
:rtype: scipy.sparse.csr_matrix
|
\mathbf{A} = \mathbf{C}^{\\top} \mathbf{M_{\mu^{-1}}^f} \mathbf{C} + i \omega \mathbf{M^e_{\sigma}}
|
||||||
:return: A
|
|
||||||
|
:param float freq: Frequency
|
||||||
|
:rtype: scipy.sparse.csr_matrix
|
||||||
|
:return: A
|
||||||
"""
|
"""
|
||||||
|
|
||||||
MfMui = self.MfMui
|
MfMui = self.MfMui
|
||||||
MeSigma = self.MeSigma
|
MeSigma = self.MeSigma
|
||||||
C = self.mesh.edgeCurl
|
C = self.mesh.edgeCurl
|
||||||
@@ -215,6 +240,20 @@ class Problem_e(BaseFDEMProblem):
|
|||||||
|
|
||||||
|
|
||||||
def getADeriv_m(self, freq, u, v, adjoint=False):
|
def getADeriv_m(self, freq, u, v, adjoint=False):
|
||||||
|
"""
|
||||||
|
Product of the derivative of our system matrix with respect to the model and a vector
|
||||||
|
|
||||||
|
.. math ::
|
||||||
|
\\frac{\mathbf{A}(\mathbf{m}) \mathbf{v}}{d \mathbf{m}} = i \omega \\frac{d \mathbf{M^e_{\sigma}}\mathbf{v} }{d\mathbf{m}}
|
||||||
|
|
||||||
|
:param float freq: frequency
|
||||||
|
:param numpy.ndarray u: solution vector (nE,)
|
||||||
|
:param numpy.ndarray v: vector to take prodct with (nP,) or (nD,) for adjoint
|
||||||
|
:param bool adjoint: adjoint?
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: derivative of the system matrix times a vector (nP,) or adjoint (nD,)
|
||||||
|
"""
|
||||||
|
|
||||||
dsig_dm = self.curModel.sigmaDeriv
|
dsig_dm = self.curModel.sigmaDeriv
|
||||||
dMe_dsig = self.MeSigmaDeriv(u)
|
dMe_dsig = self.MeSigmaDeriv(u)
|
||||||
|
|
||||||
@@ -225,26 +264,37 @@ class Problem_e(BaseFDEMProblem):
|
|||||||
|
|
||||||
def getRHS(self, freq):
|
def getRHS(self, freq):
|
||||||
"""
|
"""
|
||||||
.. math ::
|
Right hand side for the system
|
||||||
\mathbf{RHS} = \mathbf{C}^T \mathbf{M_{\mu^{-1}}^f}\mathbf{s_m} -i\omega\mathbf{M_e}\mathbf{s_e}
|
|
||||||
|
|
||||||
:param float freq: Frequency
|
.. math ::
|
||||||
:rtype: numpy.ndarray (nE, nSrc)
|
\mathbf{RHS} = \mathbf{C}^{\\top} \mathbf{M_{\mu^{-1}}^f}\mathbf{s_m} -i\omega\mathbf{M_e}\mathbf{s_e}
|
||||||
:return: RHS
|
|
||||||
|
:param float freq: Frequency
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: RHS (nE, nSrc)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
S_m, S_e = self.getSourceTerm(freq)
|
S_m, S_e = self.getSourceTerm(freq)
|
||||||
C = self.mesh.edgeCurl
|
C = self.mesh.edgeCurl
|
||||||
MfMui = self.MfMui
|
MfMui = self.MfMui
|
||||||
|
|
||||||
RHS = C.T * (MfMui * S_m) -1j * omega(freq) * S_e
|
return C.T * (MfMui * S_m) -1j * omega(freq) * S_e
|
||||||
|
|
||||||
return RHS
|
|
||||||
|
|
||||||
def getRHSDeriv_m(self, freq, src, v, adjoint=False):
|
def getRHSDeriv_m(self, freq, src, v, adjoint=False):
|
||||||
|
"""
|
||||||
|
Derivative of the right hand side with respect to the model
|
||||||
|
|
||||||
|
:param float freq: frequency
|
||||||
|
:param SimPEG.EM.FDEM.Src src: FDEM source
|
||||||
|
:param numpy.ndarray v: vector to take product with
|
||||||
|
:param bool adjoint: adjoint?
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: product of rhs deriv with a vector
|
||||||
|
"""
|
||||||
|
|
||||||
C = self.mesh.edgeCurl
|
C = self.mesh.edgeCurl
|
||||||
MfMui = self.MfMui
|
MfMui = self.MfMui
|
||||||
S_mDeriv, S_eDeriv = src.evalDeriv(self, adjoint)
|
S_mDeriv, S_eDeriv = src.evalDeriv(self, adjoint=adjoint)
|
||||||
|
|
||||||
if adjoint:
|
if adjoint:
|
||||||
dRHS = MfMui * (C * v)
|
dRHS = MfMui * (C * v)
|
||||||
@@ -256,20 +306,22 @@ class Problem_e(BaseFDEMProblem):
|
|||||||
|
|
||||||
class Problem_b(BaseFDEMProblem):
|
class Problem_b(BaseFDEMProblem):
|
||||||
"""
|
"""
|
||||||
We eliminate \\\(\\\mathbf{e}\\\) using
|
We eliminate :math:`\mathbf{e}` using
|
||||||
|
|
||||||
.. math ::
|
.. math ::
|
||||||
|
|
||||||
\mathbf{e} = \mathbf{M^e_{\sigma}}^{-1} \\left(\mathbf{C}^T \mathbf{M_{\mu^{-1}}^f} \mathbf{b} - \mathbf{s_e}\\right)
|
\mathbf{e} = \mathbf{M^e_{\sigma}}^{-1} \\left(\mathbf{C}^{\\top} \mathbf{M_{\mu^{-1}}^f} \mathbf{b} - \mathbf{s_e}\\right)
|
||||||
|
|
||||||
and solve for \\\(\\\mathbf{b}\\\) using:
|
and solve for :math:`\mathbf{b}` using:
|
||||||
|
|
||||||
.. math ::
|
.. math ::
|
||||||
|
|
||||||
\\left(\mathbf{C} \mathbf{M^e_{\sigma}}^{-1} \mathbf{C}^T \mathbf{M_{\mu^{-1}}^f} + i \omega \\right)\mathbf{b} = \mathbf{s_m} + \mathbf{M^e_{\sigma}}^{-1}\mathbf{M^e}\mathbf{s_e}
|
\\left(\mathbf{C} \mathbf{M^e_{\sigma}}^{-1} \mathbf{C}^{\\top} \mathbf{M_{\mu^{-1}}^f} + i \omega \\right)\mathbf{b} = \mathbf{s_m} + \mathbf{M^e_{\sigma}}^{-1}\mathbf{M^e}\mathbf{s_e}
|
||||||
|
|
||||||
.. note ::
|
.. note ::
|
||||||
The inverse problem will not work with full anisotropy
|
The inverse problem will not work with full anisotropy
|
||||||
|
|
||||||
|
:param SimPEG.Mesh mesh: mesh
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_fieldType = 'b'
|
_fieldType = 'b'
|
||||||
@@ -281,12 +333,14 @@ class Problem_b(BaseFDEMProblem):
|
|||||||
|
|
||||||
def getA(self, freq):
|
def getA(self, freq):
|
||||||
"""
|
"""
|
||||||
.. math ::
|
System matrix
|
||||||
\mathbf{A} = \mathbf{C} \mathbf{M^e_{\sigma}}^{-1} \mathbf{C}^T \mathbf{M_{\mu^{-1}}^f} + i \omega
|
|
||||||
|
|
||||||
:param float freq: Frequency
|
.. math ::
|
||||||
:rtype: scipy.sparse.csr_matrix
|
\mathbf{A} = \mathbf{C} \mathbf{M^e_{\sigma}}^{-1} \mathbf{C}^{\\top} \mathbf{M_{\mu^{-1}}^f} + i \omega
|
||||||
:return: A
|
|
||||||
|
:param float freq: Frequency
|
||||||
|
:rtype: scipy.sparse.csr_matrix
|
||||||
|
:return: A
|
||||||
"""
|
"""
|
||||||
|
|
||||||
MfMui = self.MfMui
|
MfMui = self.MfMui
|
||||||
@@ -302,6 +356,20 @@ class Problem_b(BaseFDEMProblem):
|
|||||||
|
|
||||||
def getADeriv_m(self, freq, u, v, adjoint=False):
|
def getADeriv_m(self, freq, u, v, adjoint=False):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Product of the derivative of our system matrix with respect to the model and a vector
|
||||||
|
|
||||||
|
.. math ::
|
||||||
|
\\frac{\mathbf{A}(\mathbf{m}) \mathbf{v}}{d \mathbf{m}} = \mathbf{C} \\frac{\mathbf{M^e_{\sigma}} \mathbf{v}}{d\mathbf{m}}
|
||||||
|
|
||||||
|
:param float freq: frequency
|
||||||
|
:param numpy.ndarray u: solution vector (nF,)
|
||||||
|
:param numpy.ndarray v: vector to take prodct with (nP,) or (nD,) for adjoint
|
||||||
|
:param bool adjoint: adjoint?
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: derivative of the system matrix times a vector (nP,) or adjoint (nD,)
|
||||||
|
"""
|
||||||
|
|
||||||
MfMui = self.MfMui
|
MfMui = self.MfMui
|
||||||
C = self.mesh.edgeCurl
|
C = self.mesh.edgeCurl
|
||||||
MeSigmaIDeriv = self.MeSigmaIDeriv
|
MeSigmaIDeriv = self.MeSigmaIDeriv
|
||||||
@@ -321,12 +389,14 @@ class Problem_b(BaseFDEMProblem):
|
|||||||
|
|
||||||
def getRHS(self, freq):
|
def getRHS(self, freq):
|
||||||
"""
|
"""
|
||||||
.. math ::
|
Right hand side for the system
|
||||||
\mathbf{RHS} = \mathbf{s_m} + \mathbf{M^e_{\sigma}}^{-1}\mathbf{s_e}
|
|
||||||
|
|
||||||
:param float freq: Frequency
|
.. math ::
|
||||||
:rtype: numpy.ndarray (nE, nSrc)
|
\mathbf{RHS} = \mathbf{s_m} + \mathbf{M^e_{\sigma}}^{-1}\mathbf{s_e}
|
||||||
:return: RHS
|
|
||||||
|
:param float freq: Frequency
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: RHS (nE, nSrc)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
S_m, S_e = self.getSourceTerm(freq)
|
S_m, S_e = self.getSourceTerm(freq)
|
||||||
@@ -342,6 +412,17 @@ class Problem_b(BaseFDEMProblem):
|
|||||||
return RHS
|
return RHS
|
||||||
|
|
||||||
def getRHSDeriv_m(self, freq, src, v, adjoint=False):
|
def getRHSDeriv_m(self, freq, src, v, adjoint=False):
|
||||||
|
"""
|
||||||
|
Derivative of the right hand side with respect to the model
|
||||||
|
|
||||||
|
:param float freq: frequency
|
||||||
|
:param SimPEG.EM.FDEM.Src src: FDEM source
|
||||||
|
:param numpy.ndarray v: vector to take product with
|
||||||
|
:param bool adjoint: adjoint?
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: product of rhs deriv with a vector
|
||||||
|
"""
|
||||||
|
|
||||||
C = self.mesh.edgeCurl
|
C = self.mesh.edgeCurl
|
||||||
S_m, S_e = src.eval(self)
|
S_m, S_e = src.eval(self)
|
||||||
MfMui = self.MfMui
|
MfMui = self.MfMui
|
||||||
@@ -350,7 +431,7 @@ class Problem_b(BaseFDEMProblem):
|
|||||||
v = self.MfMui * v
|
v = self.MfMui * v
|
||||||
|
|
||||||
MeSigmaIDeriv = self.MeSigmaIDeriv(S_e)
|
MeSigmaIDeriv = self.MeSigmaIDeriv(S_e)
|
||||||
S_mDeriv, S_eDeriv = src.evalDeriv(self, adjoint)
|
S_mDeriv, S_eDeriv = src.evalDeriv(self, adjoint=adjoint)
|
||||||
|
|
||||||
if not adjoint:
|
if not adjoint:
|
||||||
RHSderiv = C * (MeSigmaIDeriv * v)
|
RHSderiv = C * (MeSigmaIDeriv * v)
|
||||||
@@ -373,21 +454,22 @@ class Problem_b(BaseFDEMProblem):
|
|||||||
|
|
||||||
class Problem_j(BaseFDEMProblem):
|
class Problem_j(BaseFDEMProblem):
|
||||||
"""
|
"""
|
||||||
We eliminate \\\(\\\mathbf{h}\\\) using
|
We eliminate \\\(\\\mathbf{h}\\\) using
|
||||||
|
|
||||||
.. math ::
|
.. math ::
|
||||||
|
|
||||||
\mathbf{h} = \\frac{1}{i \omega} \mathbf{M_{\mu}^e}^{-1} \\left(-\mathbf{C}^T \mathbf{M_{\\rho}^f} \mathbf{j} + \mathbf{M^e} \mathbf{s_m} \\right)
|
\mathbf{h} = \\frac{1}{i \omega} \mathbf{M_{\mu}^e}^{-1} \\left(-\mathbf{C}^{\\top} \mathbf{M_{\\rho}^f} \mathbf{j} + \mathbf{M^e} \mathbf{s_m} \\right)
|
||||||
|
|
||||||
and solve for \\\(\\\mathbf{j}\\\) using
|
and solve for \\\(\\\mathbf{j}\\\) using
|
||||||
|
|
||||||
.. math ::
|
.. math ::
|
||||||
|
|
||||||
\\left(\mathbf{C} \mathbf{M_{\mu}^e}^{-1} \mathbf{C}^T \mathbf{M_{\\rho}^f} + i \omega\\right)\mathbf{j} = \mathbf{C} \mathbf{M_{\mu}^e}^{-1} \mathbf{M^e} \mathbf{s_m} -i\omega\mathbf{s_e}
|
\\left(\mathbf{C} \mathbf{M_{\mu}^e}^{-1} \mathbf{C}^{\\top} \mathbf{M_{\\rho}^f} + i \omega\\right)\mathbf{j} = \mathbf{C} \mathbf{M_{\mu}^e}^{-1} \mathbf{M^e} \mathbf{s_m} -i\omega\mathbf{s_e}
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
This implementation does not yet work with full anisotropy!!
|
This implementation does not yet work with full anisotropy!!
|
||||||
|
|
||||||
|
:param SimPEG.Mesh mesh: mesh
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_fieldType = 'j'
|
_fieldType = 'j'
|
||||||
@@ -399,12 +481,14 @@ class Problem_j(BaseFDEMProblem):
|
|||||||
|
|
||||||
def getA(self, freq):
|
def getA(self, freq):
|
||||||
"""
|
"""
|
||||||
.. math ::
|
System matrix
|
||||||
\\mathbf{A} = \\mathbf{C} \\mathbf{M^e_{mu^{-1}}} \\mathbf{C}^T \\mathbf{M^f_{\\sigma^{-1}}} + i\\omega
|
|
||||||
|
|
||||||
:param float freq: Frequency
|
.. math ::
|
||||||
:rtype: scipy.sparse.csr_matrix
|
\\mathbf{A} = \\mathbf{C} \\mathbf{M^e_{\\mu^{-1}}} \\mathbf{C}^{\\top} \\mathbf{M^f_{\\sigma^{-1}}} + i\\omega
|
||||||
:return: A
|
|
||||||
|
:param float freq: Frequency
|
||||||
|
:rtype: scipy.sparse.csr_matrix
|
||||||
|
:return: A
|
||||||
"""
|
"""
|
||||||
|
|
||||||
MeMuI = self.MeMuI
|
MeMuI = self.MeMuI
|
||||||
@@ -421,12 +505,20 @@ class Problem_j(BaseFDEMProblem):
|
|||||||
|
|
||||||
def getADeriv_m(self, freq, u, v, adjoint=False):
|
def getADeriv_m(self, freq, u, v, adjoint=False):
|
||||||
"""
|
"""
|
||||||
In this case, we assume that electrical conductivity, \\\(\\\sigma\\\) is the physical property of interest (i.e. \\\(\\\sigma\\\) = model.transform). Then we want
|
Product of the derivative of our system matrix with respect to the model and a vector
|
||||||
|
|
||||||
.. math ::
|
In this case, we assume that electrical conductivity, :math:`\sigma` is the physical property of interest (i.e. :math:`\sigma` = model.transform). Then we want
|
||||||
|
|
||||||
\\frac{\mathbf{A(\sigma)} \mathbf{v}}{d \\mathbf{m}} &= \\mathbf{C} \\mathbf{M^e_{mu^{-1}}} \\mathbf{C^T} \\frac{d \\mathbf{M^f_{\\sigma^{-1}}}}{d \\mathbf{m}}
|
.. math ::
|
||||||
&= \\mathbf{C} \\mathbf{M^e_{mu}^{-1}} \\mathbf{C^T} \\frac{d \\mathbf{M^f_{\\sigma^{-1}}}}{d \\mathbf{\\sigma^{-1}}} \\frac{d \\mathbf{\\sigma^{-1}}}{d \\mathbf{\\sigma}} \\frac{d \\mathbf{\\sigma}}{d \\mathbf{m}}
|
|
||||||
|
\\frac{\mathbf{A(\sigma)} \mathbf{v}}{d \mathbf{m}} = \mathbf{C} \mathbf{M^e_{mu^{-1}}} \mathbf{C^{\\top}} \\frac{d \mathbf{M^f_{\sigma^{-1}}}\mathbf{v} }{d \mathbf{m}}
|
||||||
|
|
||||||
|
:param float freq: frequency
|
||||||
|
:param numpy.ndarray u: solution vector (nF,)
|
||||||
|
:param numpy.ndarray v: vector to take prodct with (nP,) or (nD,) for adjoint
|
||||||
|
:param bool adjoint: adjoint?
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: derivative of the system matrix times a vector (nP,) or adjoint (nD,)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
MeMuI = self.MeMuI
|
MeMuI = self.MeMuI
|
||||||
@@ -446,12 +538,15 @@ class Problem_j(BaseFDEMProblem):
|
|||||||
|
|
||||||
def getRHS(self, freq):
|
def getRHS(self, freq):
|
||||||
"""
|
"""
|
||||||
.. math ::
|
Right hand side for the system
|
||||||
|
|
||||||
\mathbf{RHS} = \mathbf{C} \mathbf{M_{\mu}^e}^{-1}\mathbf{s_m} -i\omega \mathbf{s_e}
|
.. math ::
|
||||||
:param float freq: Frequency
|
|
||||||
:rtype: numpy.ndarray (nE, nSrc)
|
\mathbf{RHS} = \mathbf{C} \mathbf{M_{\mu}^e}^{-1}\mathbf{s_m} -i\omega \mathbf{s_e}
|
||||||
:return: RHS
|
|
||||||
|
:param float freq: Frequency
|
||||||
|
:rtype: numpy.ndarray (nE, nSrc)
|
||||||
|
:return: RHS
|
||||||
"""
|
"""
|
||||||
|
|
||||||
S_m, S_e = self.getSourceTerm(freq)
|
S_m, S_e = self.getSourceTerm(freq)
|
||||||
@@ -466,9 +561,20 @@ class Problem_j(BaseFDEMProblem):
|
|||||||
return RHS
|
return RHS
|
||||||
|
|
||||||
def getRHSDeriv_m(self, freq, src, v, adjoint=False):
|
def getRHSDeriv_m(self, freq, src, v, adjoint=False):
|
||||||
|
"""
|
||||||
|
Derivative of the right hand side with respect to the model
|
||||||
|
|
||||||
|
:param float freq: frequency
|
||||||
|
:param SimPEG.EM.FDEM.Src src: FDEM source
|
||||||
|
:param numpy.ndarray v: vector to take product with
|
||||||
|
:param bool adjoint: adjoint?
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: product of rhs deriv with a vector
|
||||||
|
"""
|
||||||
|
|
||||||
C = self.mesh.edgeCurl
|
C = self.mesh.edgeCurl
|
||||||
MeMuI = self.MeMuI
|
MeMuI = self.MeMuI
|
||||||
S_mDeriv, S_eDeriv = src.evalDeriv(self, adjoint)
|
S_mDeriv, S_eDeriv = src.evalDeriv(self, adjoint=adjoint)
|
||||||
|
|
||||||
if adjoint:
|
if adjoint:
|
||||||
if self._makeASymmetric:
|
if self._makeASymmetric:
|
||||||
@@ -489,18 +595,19 @@ class Problem_j(BaseFDEMProblem):
|
|||||||
|
|
||||||
class Problem_h(BaseFDEMProblem):
|
class Problem_h(BaseFDEMProblem):
|
||||||
"""
|
"""
|
||||||
We eliminate \\\(\\\mathbf{j}\\\) using
|
We eliminate \\\(\\\mathbf{j}\\\) using
|
||||||
|
|
||||||
.. math ::
|
.. math ::
|
||||||
|
|
||||||
\mathbf{j} = \mathbf{C} \mathbf{h} - \mathbf{s_e}
|
\mathbf{j} = \mathbf{C} \mathbf{h} - \mathbf{s_e}
|
||||||
|
|
||||||
and solve for \\\(\\\mathbf{h}\\\) using
|
and solve for \\\(\\\mathbf{h}\\\) using
|
||||||
|
|
||||||
.. math ::
|
.. math ::
|
||||||
|
|
||||||
\\left(\mathbf{C}^T \mathbf{M_{\\rho}^f} \mathbf{C} + i \omega \mathbf{M_{\mu}^e}\\right) \mathbf{h} = \mathbf{M^e} \mathbf{s_m} + \mathbf{C}^T \mathbf{M_{\\rho}^f} \mathbf{s_e}
|
\\left(\mathbf{C}^{\\top} \mathbf{M_{\\rho}^f} \mathbf{C} + i \omega \mathbf{M_{\mu}^e}\\right) \mathbf{h} = \mathbf{M^e} \mathbf{s_m} + \mathbf{C}^{\\top} \mathbf{M_{\\rho}^f} \mathbf{s_e}
|
||||||
|
|
||||||
|
:param SimPEG.Mesh mesh: mesh
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_fieldType = 'h'
|
_fieldType = 'h'
|
||||||
@@ -512,13 +619,14 @@ class Problem_h(BaseFDEMProblem):
|
|||||||
|
|
||||||
def getA(self, freq):
|
def getA(self, freq):
|
||||||
"""
|
"""
|
||||||
.. math ::
|
System matrix
|
||||||
|
|
||||||
\mathbf{A} = \mathbf{C}^T \mathbf{M_{\\rho}^f} \mathbf{C} + i \omega \mathbf{M_{\mu}^e}
|
.. math::
|
||||||
|
\mathbf{A} = \mathbf{C}^{\\top} \mathbf{M_{\\rho}^f} \mathbf{C} + i \omega \mathbf{M_{\mu}^e}
|
||||||
|
|
||||||
:param float freq: Frequency
|
:param float freq: Frequency
|
||||||
:rtype: scipy.sparse.csr_matrix
|
:rtype: scipy.sparse.csr_matrix
|
||||||
:return: A
|
:return: A
|
||||||
"""
|
"""
|
||||||
|
|
||||||
MeMu = self.MeMu
|
MeMu = self.MeMu
|
||||||
@@ -528,6 +636,19 @@ class Problem_h(BaseFDEMProblem):
|
|||||||
return C.T * (MfRho * C) + 1j*omega(freq)*MeMu
|
return C.T * (MfRho * C) + 1j*omega(freq)*MeMu
|
||||||
|
|
||||||
def getADeriv_m(self, freq, u, v, adjoint=False):
|
def getADeriv_m(self, freq, u, v, adjoint=False):
|
||||||
|
"""
|
||||||
|
Product of the derivative of our system matrix with respect to the model and a vector
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
\\frac{\mathbf{A}(\mathbf{m}) \mathbf{v}}{d \mathbf{m}} = \mathbf{C}^{\\top}\\frac{d \mathbf{M^f_{\\rho}}\mathbf{v} }{d\mathbf{m}}
|
||||||
|
|
||||||
|
:param float freq: frequency
|
||||||
|
:param numpy.ndarray u: solution vector (nE,)
|
||||||
|
:param numpy.ndarray v: vector to take prodct with (nP,) or (nD,) for adjoint
|
||||||
|
:param bool adjoint: adjoint?
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: derivative of the system matrix times a vector (nP,) or adjoint (nD,)
|
||||||
|
"""
|
||||||
|
|
||||||
MeMu = self.MeMu
|
MeMu = self.MeMu
|
||||||
C = self.mesh.edgeCurl
|
C = self.mesh.edgeCurl
|
||||||
@@ -539,24 +660,35 @@ class Problem_h(BaseFDEMProblem):
|
|||||||
|
|
||||||
def getRHS(self, freq):
|
def getRHS(self, freq):
|
||||||
"""
|
"""
|
||||||
.. math ::
|
Right hand side for the system
|
||||||
|
|
||||||
\mathbf{RHS} = \mathbf{M^e} \mathbf{s_m} + \mathbf{C}^T \mathbf{M_{\\rho}^f} \mathbf{s_e}
|
.. math ::
|
||||||
|
|
||||||
:param float freq: Frequency
|
\mathbf{RHS} = \mathbf{M^e} \mathbf{s_m} + \mathbf{C}^{\\top} \mathbf{M_{\\rho}^f} \mathbf{s_e}
|
||||||
:rtype: numpy.ndarray (nE, nSrc)
|
|
||||||
:return: RHS
|
:param float freq: Frequency
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: RHS (nE, nSrc)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
S_m, S_e = self.getSourceTerm(freq)
|
S_m, S_e = self.getSourceTerm(freq)
|
||||||
C = self.mesh.edgeCurl
|
C = self.mesh.edgeCurl
|
||||||
MfRho = self.MfRho
|
MfRho = self.MfRho
|
||||||
|
|
||||||
RHS = S_m + C.T * ( MfRho * S_e )
|
return S_m + C.T * ( MfRho * S_e )
|
||||||
|
|
||||||
return RHS
|
|
||||||
|
|
||||||
def getRHSDeriv_m(self, freq, src, v, adjoint=False):
|
def getRHSDeriv_m(self, freq, src, v, adjoint=False):
|
||||||
|
"""
|
||||||
|
Derivative of the right hand side with respect to the model
|
||||||
|
|
||||||
|
:param float freq: frequency
|
||||||
|
:param SimPEG.EM.FDEM.Src src: FDEM source
|
||||||
|
:param numpy.ndarray v: vector to take product with
|
||||||
|
:param bool adjoint: adjoint?
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: product of rhs deriv with a vector
|
||||||
|
"""
|
||||||
|
|
||||||
_, S_e = src.eval(self)
|
_, S_e = src.eval(self)
|
||||||
C = self.mesh.edgeCurl
|
C = self.mesh.edgeCurl
|
||||||
MfRho = self.MfRho
|
MfRho = self.MfRho
|
||||||
@@ -567,7 +699,7 @@ class Problem_h(BaseFDEMProblem):
|
|||||||
elif adjoint:
|
elif adjoint:
|
||||||
RHSDeriv = MfRhoDeriv.T * (C * v)
|
RHSDeriv = MfRhoDeriv.T * (C * v)
|
||||||
|
|
||||||
S_mDeriv, S_eDeriv = src.evalDeriv(self, adjoint)
|
S_mDeriv, S_eDeriv = src.evalDeriv(self, adjoint=adjoint)
|
||||||
|
|
||||||
return RHSDeriv + S_mDeriv(v) + C.T * (MfRho * S_eDeriv(v))
|
return RHSDeriv + S_mDeriv(v) + C.T * (MfRho * S_eDeriv(v))
|
||||||
|
|
||||||
|
|||||||
@@ -7,11 +7,39 @@ from SimPEG.Utils import Zero, Identity
|
|||||||
|
|
||||||
|
|
||||||
class Fields(SimPEG.Problem.Fields):
|
class Fields(SimPEG.Problem.Fields):
|
||||||
"""Fancy Field Storage for a FDEM survey."""
|
"""
|
||||||
|
|
||||||
|
Fancy Field Storage for a FDEM survey. Only one field type is stored for
|
||||||
|
each problem, the rest are computed. The fields obejct acts like an array and is indexed by
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
f = problem.fields(m)
|
||||||
|
e = f[srcList,'e']
|
||||||
|
b = f[srcList,'b']
|
||||||
|
|
||||||
|
If accessing all sources for a given field, use the :code:`:`
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
f = problem.fields(m)
|
||||||
|
e = f[:,'e']
|
||||||
|
b = f[:,'b']
|
||||||
|
|
||||||
|
The array returned will be size (nE or nF, nSrcs :math:`\\times` nFrequencies)
|
||||||
|
"""
|
||||||
|
|
||||||
knownFields = {}
|
knownFields = {}
|
||||||
dtype = complex
|
dtype = complex
|
||||||
|
|
||||||
class Fields_e(Fields):
|
class Fields_e(Fields):
|
||||||
|
"""
|
||||||
|
Fields object for Problem_e.
|
||||||
|
|
||||||
|
:param Mesh mesh: mesh
|
||||||
|
:param Survey survey: survey
|
||||||
|
"""
|
||||||
|
|
||||||
knownFields = {'eSolution':'E'}
|
knownFields = {'eSolution':'E'}
|
||||||
aliasFields = {
|
aliasFields = {
|
||||||
'e' : ['eSolution','E','_e'],
|
'e' : ['eSolution','E','_e'],
|
||||||
@@ -30,6 +58,15 @@ class Fields_e(Fields):
|
|||||||
self._edgeCurl = self.survey.prob.mesh.edgeCurl
|
self._edgeCurl = self.survey.prob.mesh.edgeCurl
|
||||||
|
|
||||||
def _ePrimary(self, eSolution, srcList):
|
def _ePrimary(self, eSolution, srcList):
|
||||||
|
"""
|
||||||
|
Primary electric field from source
|
||||||
|
|
||||||
|
:param numpy.ndarray eSolution: field we solved for
|
||||||
|
:param list srcList: list of sources
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: primary electric field as defined by the sources
|
||||||
|
"""
|
||||||
|
|
||||||
ePrimary = np.zeros_like(eSolution)
|
ePrimary = np.zeros_like(eSolution)
|
||||||
for i, src in enumerate(srcList):
|
for i, src in enumerate(srcList):
|
||||||
ep = src.ePrimary(self.prob)
|
ep = src.ePrimary(self.prob)
|
||||||
@@ -37,19 +74,67 @@ class Fields_e(Fields):
|
|||||||
return ePrimary
|
return ePrimary
|
||||||
|
|
||||||
def _eSecondary(self, eSolution, srcList):
|
def _eSecondary(self, eSolution, srcList):
|
||||||
|
"""
|
||||||
|
Secondary electric field is the thing we solved for
|
||||||
|
|
||||||
|
:param numpy.ndarray eSolution: field we solved for
|
||||||
|
:param list srcList: list of sources
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: secondary electric field
|
||||||
|
"""
|
||||||
|
|
||||||
return eSolution
|
return eSolution
|
||||||
|
|
||||||
def _e(self, eSolution, srcList):
|
def _e(self, eSolution, srcList):
|
||||||
|
"""
|
||||||
|
Total electric field is sum of primary and secondary
|
||||||
|
|
||||||
|
:param numpy.ndarray eSolution: field we solved for
|
||||||
|
:param list srcList: list of sources
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: total electric field
|
||||||
|
"""
|
||||||
|
|
||||||
return self._ePrimary(eSolution,srcList) + self._eSecondary(eSolution,srcList)
|
return self._ePrimary(eSolution,srcList) + self._eSecondary(eSolution,srcList)
|
||||||
|
|
||||||
def _eDeriv_u(self, src, v, adjoint = False):
|
def _eDeriv_u(self, src, v, adjoint = False):
|
||||||
|
"""
|
||||||
|
Derivative of the total electric field with respect to the thing we
|
||||||
|
solved for
|
||||||
|
|
||||||
|
:param SimPEG.EM.FDEM.Src src: source
|
||||||
|
:param numpy.ndarray v: vector to take product with
|
||||||
|
:param bool adjoint: adjoint?
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: product of the derivative of the electric field with respect to the field we solved for with a vector
|
||||||
|
"""
|
||||||
|
|
||||||
return Identity()*v
|
return Identity()*v
|
||||||
|
|
||||||
def _eDeriv_m(self, src, v, adjoint = False):
|
def _eDeriv_m(self, src, v, adjoint = False):
|
||||||
|
"""
|
||||||
|
Derivative of the total electric field with respect to the inversion model. Here, we assume that the primary does not depend on the model.
|
||||||
|
|
||||||
|
:param SimPEG.EM.FDEM.Src src: source
|
||||||
|
:param numpy.ndarray v: vector to take product with
|
||||||
|
:param bool adjoint: adjoint?
|
||||||
|
:rtype: SimPEG.Utils.Zero
|
||||||
|
:return: product of the electric field derivative with respect to the inversion model with a vector
|
||||||
|
"""
|
||||||
|
|
||||||
# assuming primary does not depend on the model
|
# assuming primary does not depend on the model
|
||||||
return Zero()
|
return Zero()
|
||||||
|
|
||||||
def _bPrimary(self, eSolution, srcList):
|
def _bPrimary(self, eSolution, srcList):
|
||||||
|
"""
|
||||||
|
Primary magnetic flux density from source
|
||||||
|
|
||||||
|
:param numpy.ndarray eSolution: field we solved for
|
||||||
|
:param list srcList: list of sources
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: primary magnetic flux density as defined by the sources
|
||||||
|
"""
|
||||||
|
|
||||||
bPrimary = np.zeros([self._edgeCurl.shape[0],eSolution.shape[1]],dtype = complex)
|
bPrimary = np.zeros([self._edgeCurl.shape[0],eSolution.shape[1]],dtype = complex)
|
||||||
for i, src in enumerate(srcList):
|
for i, src in enumerate(srcList):
|
||||||
bp = src.bPrimary(self.prob)
|
bp = src.bPrimary(self.prob)
|
||||||
@@ -57,6 +142,15 @@ class Fields_e(Fields):
|
|||||||
return bPrimary
|
return bPrimary
|
||||||
|
|
||||||
def _bSecondary(self, eSolution, srcList):
|
def _bSecondary(self, eSolution, srcList):
|
||||||
|
"""
|
||||||
|
Secondary magnetic flux density from eSolution
|
||||||
|
|
||||||
|
:param numpy.ndarray eSolution: field we solved for
|
||||||
|
:param list srcList: list of sources
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: secondary magnetic flux density
|
||||||
|
"""
|
||||||
|
|
||||||
C = self._edgeCurl
|
C = self._edgeCurl
|
||||||
b = (C * eSolution)
|
b = (C * eSolution)
|
||||||
for i, src in enumerate(srcList):
|
for i, src in enumerate(srcList):
|
||||||
@@ -66,29 +160,84 @@ class Fields_e(Fields):
|
|||||||
return b
|
return b
|
||||||
|
|
||||||
def _bSecondaryDeriv_u(self, src, v, adjoint = False):
|
def _bSecondaryDeriv_u(self, src, v, adjoint = False):
|
||||||
|
"""
|
||||||
|
Derivative of the secondary magnetic flux density with respect to the thing we solved for
|
||||||
|
|
||||||
|
:param SimPEG.EM.FDEM.Src src: source
|
||||||
|
:param numpy.ndarray v: vector to take product with
|
||||||
|
:param bool adjoint: adjoint?
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: product of the derivative of the secondary magnetic flux density with respect to the field we solved for with a vector
|
||||||
|
"""
|
||||||
|
|
||||||
C = self._edgeCurl
|
C = self._edgeCurl
|
||||||
if adjoint:
|
if adjoint:
|
||||||
return - 1./(1j*omega(src.freq)) * (C.T * v)
|
return - 1./(1j*omega(src.freq)) * (C.T * v)
|
||||||
return - 1./(1j*omega(src.freq)) * (C * v)
|
return - 1./(1j*omega(src.freq)) * (C * v)
|
||||||
|
|
||||||
def _bSecondaryDeriv_m(self, src, v, adjoint = False):
|
def _bSecondaryDeriv_m(self, src, v, adjoint = False):
|
||||||
S_mDeriv, _ = src.evalDeriv(self.prob, adjoint)
|
"""
|
||||||
S_mDeriv = S_mDeriv(v)
|
Derivative of the secondary magnetic flux density with respect to the inversion model.
|
||||||
|
|
||||||
|
:param SimPEG.EM.FDEM.Src src: source
|
||||||
|
:param numpy.ndarray v: vector to take product with
|
||||||
|
:param bool adjoint: adjoint?
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: product of the secondary magnetic flux density derivative with respect to the inversion model with a vector
|
||||||
|
"""
|
||||||
|
|
||||||
|
S_mDeriv, _ = src.evalDeriv(self.prob, v, adjoint)
|
||||||
return 1./(1j * omega(src.freq)) * S_mDeriv
|
return 1./(1j * omega(src.freq)) * S_mDeriv
|
||||||
|
|
||||||
def _b(self, eSolution, srcList):
|
def _b(self, eSolution, srcList):
|
||||||
|
"""
|
||||||
|
Total magnetic flux density is sum of primary and secondary
|
||||||
|
|
||||||
|
:param numpy.ndarray eSolution: field we solved for
|
||||||
|
:param list srcList: list of sources
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: total magnetic flux density
|
||||||
|
"""
|
||||||
|
|
||||||
return self._bPrimary(eSolution, srcList) + self._bSecondary(eSolution, srcList)
|
return self._bPrimary(eSolution, srcList) + self._bSecondary(eSolution, srcList)
|
||||||
|
|
||||||
def _bDeriv_u(self, src, v, adjoint=False):
|
def _bDeriv_u(self, src, v, adjoint=False):
|
||||||
|
"""
|
||||||
|
Derivative of the total magnetic flux density with respect to the thing we solved for
|
||||||
|
|
||||||
|
:param SimPEG.EM.FDEM.Src src: source
|
||||||
|
:param numpy.ndarray v: vector to take product with
|
||||||
|
:param bool adjoint: adjoint?
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: product of the derivative of the magnetic flux density with respect to the field we solved for with a vector
|
||||||
|
"""
|
||||||
|
|
||||||
# Primary does not depend on u
|
# Primary does not depend on u
|
||||||
return self._bSecondaryDeriv_u(src, v, adjoint)
|
return self._bSecondaryDeriv_u(src, v, adjoint)
|
||||||
|
|
||||||
def _bDeriv_m(self, src, v, adjoint=False):
|
def _bDeriv_m(self, src, v, adjoint=False):
|
||||||
|
"""
|
||||||
|
Derivative of the total magnetic flux density with respect to the inversion model.
|
||||||
|
|
||||||
|
:param SimPEG.EM.FDEM.Src src: source
|
||||||
|
:param numpy.ndarray v: vector to take product with
|
||||||
|
:param bool adjoint: adjoint?
|
||||||
|
:rtype: SimPEG.Utils.Zero
|
||||||
|
:return: product of the magnetic flux density derivative with respect to the inversion model with a vector
|
||||||
|
"""
|
||||||
|
|
||||||
# Assuming the primary does not depend on the model
|
# Assuming the primary does not depend on the model
|
||||||
return self._bSecondaryDeriv_m(src, v, adjoint)
|
return self._bSecondaryDeriv_m(src, v, adjoint)
|
||||||
|
|
||||||
|
|
||||||
class Fields_b(Fields):
|
class Fields_b(Fields):
|
||||||
|
"""
|
||||||
|
Fields object for Problem_b.
|
||||||
|
|
||||||
|
:param Mesh mesh: mesh
|
||||||
|
:param Survey survey: survey
|
||||||
|
"""
|
||||||
|
|
||||||
knownFields = {'bSolution':'F'}
|
knownFields = {'bSolution':'F'}
|
||||||
aliasFields = {
|
aliasFields = {
|
||||||
'b' : ['bSolution','F','_b'],
|
'b' : ['bSolution','F','_b'],
|
||||||
@@ -111,6 +260,15 @@ class Fields_b(Fields):
|
|||||||
self._Me = self.survey.prob.Me
|
self._Me = self.survey.prob.Me
|
||||||
|
|
||||||
def _bPrimary(self, bSolution, srcList):
|
def _bPrimary(self, bSolution, srcList):
|
||||||
|
"""
|
||||||
|
Primary magnetic flux density from source
|
||||||
|
|
||||||
|
:param numpy.ndarray bSolution: field we solved for
|
||||||
|
:param list srcList: list of sources
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: primary electric field as defined by the sources
|
||||||
|
"""
|
||||||
|
|
||||||
bPrimary = np.zeros_like(bSolution)
|
bPrimary = np.zeros_like(bSolution)
|
||||||
for i, src in enumerate(srcList):
|
for i, src in enumerate(srcList):
|
||||||
bp = src.bPrimary(self.prob)
|
bp = src.bPrimary(self.prob)
|
||||||
@@ -118,19 +276,66 @@ class Fields_b(Fields):
|
|||||||
return bPrimary
|
return bPrimary
|
||||||
|
|
||||||
def _bSecondary(self, bSolution, srcList):
|
def _bSecondary(self, bSolution, srcList):
|
||||||
|
"""
|
||||||
|
Secondary magnetic flux density is the thing we solved for
|
||||||
|
|
||||||
|
:param numpy.ndarray bSolution: field we solved for
|
||||||
|
:param list srcList: list of sources
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: secondary magnetic flux density
|
||||||
|
"""
|
||||||
|
|
||||||
return bSolution
|
return bSolution
|
||||||
|
|
||||||
def _b(self, bSolution, srcList):
|
def _b(self, bSolution, srcList):
|
||||||
|
"""
|
||||||
|
Total magnetic flux density is sum of primary and secondary
|
||||||
|
|
||||||
|
:param numpy.ndarray bSolution: field we solved for
|
||||||
|
:param list srcList: list of sources
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: total magnetic flux density
|
||||||
|
"""
|
||||||
|
|
||||||
return self._bPrimary(bSolution, srcList) + self._bSecondary(bSolution, srcList)
|
return self._bPrimary(bSolution, srcList) + self._bSecondary(bSolution, srcList)
|
||||||
|
|
||||||
def _bDeriv_u(self, src, v, adjoint=False):
|
def _bDeriv_u(self, src, v, adjoint=False):
|
||||||
|
"""
|
||||||
|
Derivative of the total magnetic flux density with respect to the thing we
|
||||||
|
solved for
|
||||||
|
|
||||||
|
:param SimPEG.EM.FDEM.Src src: source
|
||||||
|
:param numpy.ndarray v: vector to take product with
|
||||||
|
:param bool adjoint: adjoint?
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: product of the derivative of the magnetic flux density with respect to the field we solved for with a vector
|
||||||
|
"""
|
||||||
return Identity()*v
|
return Identity()*v
|
||||||
|
|
||||||
def _bDeriv_m(self, src, v, adjoint=False):
|
def _bDeriv_m(self, src, v, adjoint=False):
|
||||||
|
"""
|
||||||
|
Derivative of the total magnetic flux density with respect to the inversion model. Here, we assume that the primary does not depend on the model.
|
||||||
|
|
||||||
|
:param SimPEG.EM.FDEM.Src src: source
|
||||||
|
:param numpy.ndarray v: vector to take product with
|
||||||
|
:param bool adjoint: adjoint?
|
||||||
|
:rtype: SimPEG.Utils.Zero
|
||||||
|
:return: product of the magnetic flux density derivative with respect to the inversion model with a vector
|
||||||
|
"""
|
||||||
|
|
||||||
# assuming primary does not depend on the model
|
# assuming primary does not depend on the model
|
||||||
return Zero()
|
return Zero()
|
||||||
|
|
||||||
def _ePrimary(self, bSolution, srcList):
|
def _ePrimary(self, bSolution, srcList):
|
||||||
|
"""
|
||||||
|
Primary electric field from source
|
||||||
|
|
||||||
|
:param numpy.ndarray bSolution: field we solved for
|
||||||
|
:param list srcList: list of sources
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: primary electric field as defined by the sources
|
||||||
|
"""
|
||||||
|
|
||||||
ePrimary = np.zeros([self._edgeCurl.shape[1],bSolution.shape[1]],dtype = complex)
|
ePrimary = np.zeros([self._edgeCurl.shape[1],bSolution.shape[1]],dtype = complex)
|
||||||
for i,src in enumerate(srcList):
|
for i,src in enumerate(srcList):
|
||||||
ep = src.ePrimary(self.prob)
|
ep = src.ePrimary(self.prob)
|
||||||
@@ -138,6 +343,15 @@ class Fields_b(Fields):
|
|||||||
return ePrimary
|
return ePrimary
|
||||||
|
|
||||||
def _eSecondary(self, bSolution, srcList):
|
def _eSecondary(self, bSolution, srcList):
|
||||||
|
"""
|
||||||
|
Secondary electric field from bSolution
|
||||||
|
|
||||||
|
:param numpy.ndarray bSolution: field we solved for
|
||||||
|
:param list srcList: list of sources
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: secondary electric field
|
||||||
|
"""
|
||||||
|
|
||||||
e = self._MeSigmaI * ( self._edgeCurl.T * ( self._MfMui * bSolution))
|
e = self._MeSigmaI * ( self._edgeCurl.T * ( self._MfMui * bSolution))
|
||||||
for i,src in enumerate(srcList):
|
for i,src in enumerate(srcList):
|
||||||
_,S_e = src.eval(self.prob)
|
_,S_e = src.eval(self.prob)
|
||||||
@@ -145,12 +359,32 @@ class Fields_b(Fields):
|
|||||||
return e
|
return e
|
||||||
|
|
||||||
def _eSecondaryDeriv_u(self, src, v, adjoint=False):
|
def _eSecondaryDeriv_u(self, src, v, adjoint=False):
|
||||||
|
"""
|
||||||
|
Derivative of the secondary electric field with respect to the thing we solved for
|
||||||
|
|
||||||
|
:param SimPEG.EM.FDEM.Src src: source
|
||||||
|
:param numpy.ndarray v: vector to take product with
|
||||||
|
:param bool adjoint: adjoint?
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: product of the derivative of the secondary electric field with respect to the field we solved for with a vector
|
||||||
|
"""
|
||||||
|
|
||||||
if not adjoint:
|
if not adjoint:
|
||||||
return self._MeSigmaI * ( self._edgeCurl.T * ( self._MfMui * v) )
|
return self._MeSigmaI * ( self._edgeCurl.T * ( self._MfMui * v) )
|
||||||
else:
|
else:
|
||||||
return self._MfMui.T * (self._edgeCurl * (self._MeSigmaI.T * v))
|
return self._MfMui.T * (self._edgeCurl * (self._MeSigmaI.T * v))
|
||||||
|
|
||||||
def _eSecondaryDeriv_m(self, src, v, adjoint=False):
|
def _eSecondaryDeriv_m(self, src, v, adjoint=False):
|
||||||
|
"""
|
||||||
|
Derivative of the secondary electric field with respect to the inversion model
|
||||||
|
|
||||||
|
:param SimPEG.EM.FDEM.Src src: source
|
||||||
|
:param numpy.ndarray v: vector to take product with
|
||||||
|
:param bool adjoint: adjoint?
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: product of the derivative of the secondary electric field with respect to the model with a vector
|
||||||
|
"""
|
||||||
|
|
||||||
bSolution = self[[src],'bSolution']
|
bSolution = self[[src],'bSolution']
|
||||||
_,S_e = src.eval(self.prob)
|
_,S_e = src.eval(self.prob)
|
||||||
Me = self._Me
|
Me = self._Me
|
||||||
@@ -166,25 +400,60 @@ class Fields_b(Fields):
|
|||||||
elif adjoint:
|
elif adjoint:
|
||||||
de_dm = self._MeSigmaIDeriv(w).T * v
|
de_dm = self._MeSigmaIDeriv(w).T * v
|
||||||
|
|
||||||
_, S_eDeriv = src.evalDeriv(self.prob, adjoint)
|
_, S_eDeriv = src.evalDeriv(self.prob, v, adjoint)
|
||||||
Se_Deriv = S_eDeriv(v)
|
|
||||||
|
|
||||||
de_dm = de_dm - self._MeSigmaI * Se_Deriv
|
de_dm = de_dm - self._MeSigmaI * S_eDeriv
|
||||||
|
|
||||||
return de_dm
|
return de_dm
|
||||||
|
|
||||||
def _e(self, bSolution, srcList):
|
def _e(self, bSolution, srcList):
|
||||||
|
"""
|
||||||
|
Total electric field is sum of primary and secondary
|
||||||
|
|
||||||
|
:param numpy.ndarray eSolution: field we solved for
|
||||||
|
:param list srcList: list of sources
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: total electric field
|
||||||
|
"""
|
||||||
|
|
||||||
return self._ePrimary(bSolution, srcList) + self._eSecondary(bSolution, srcList)
|
return self._ePrimary(bSolution, srcList) + self._eSecondary(bSolution, srcList)
|
||||||
|
|
||||||
def _eDeriv_u(self, src, v, adjoint=False):
|
def _eDeriv_u(self, src, v, adjoint=False):
|
||||||
|
"""
|
||||||
|
Derivative of the total electric field with respect to the thing we solved for
|
||||||
|
|
||||||
|
:param SimPEG.EM.FDEM.Src src: source
|
||||||
|
:param numpy.ndarray v: vector to take product with
|
||||||
|
:param bool adjoint: adjoint?
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: product of the derivative of the electric field with respect to the field we solved for with a vector
|
||||||
|
"""
|
||||||
|
|
||||||
return self._eSecondaryDeriv_u(src, v, adjoint)
|
return self._eSecondaryDeriv_u(src, v, adjoint)
|
||||||
|
|
||||||
def _eDeriv_m(self, src, v, adjoint=False):
|
def _eDeriv_m(self, src, v, adjoint=False):
|
||||||
|
"""
|
||||||
|
Derivative of the total electric field density with respect to the inversion model.
|
||||||
|
|
||||||
|
:param SimPEG.EM.FDEM.Src src: source
|
||||||
|
:param numpy.ndarray v: vector to take product with
|
||||||
|
:param bool adjoint: adjoint?
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: product of the electric field derivative with respect to the inversion model with a vector
|
||||||
|
"""
|
||||||
|
|
||||||
# assuming primary doesn't depend on model
|
# assuming primary doesn't depend on model
|
||||||
return self._eSecondaryDeriv_m(src, v, adjoint)
|
return self._eSecondaryDeriv_m(src, v, adjoint)
|
||||||
|
|
||||||
|
|
||||||
class Fields_j(Fields):
|
class Fields_j(Fields):
|
||||||
|
"""
|
||||||
|
Fields object for Problem_j.
|
||||||
|
|
||||||
|
:param Mesh mesh: mesh
|
||||||
|
:param Survey survey: survey
|
||||||
|
"""
|
||||||
|
|
||||||
knownFields = {'jSolution':'F'}
|
knownFields = {'jSolution':'F'}
|
||||||
aliasFields = {
|
aliasFields = {
|
||||||
'j' : ['jSolution','F','_j'],
|
'j' : ['jSolution','F','_j'],
|
||||||
@@ -207,6 +476,15 @@ class Fields_j(Fields):
|
|||||||
self._Me = self.survey.prob.Me
|
self._Me = self.survey.prob.Me
|
||||||
|
|
||||||
def _jPrimary(self, jSolution, srcList):
|
def _jPrimary(self, jSolution, srcList):
|
||||||
|
"""
|
||||||
|
Primary current density from source
|
||||||
|
|
||||||
|
:param numpy.ndarray jSolution: field we solved for
|
||||||
|
:param list srcList: list of sources
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: primary current density as defined by the sources
|
||||||
|
"""
|
||||||
|
|
||||||
jPrimary = np.zeros_like(jSolution,dtype = complex)
|
jPrimary = np.zeros_like(jSolution,dtype = complex)
|
||||||
for i, src in enumerate(srcList):
|
for i, src in enumerate(srcList):
|
||||||
jp = src.jPrimary(self.prob)
|
jp = src.jPrimary(self.prob)
|
||||||
@@ -214,19 +492,66 @@ class Fields_j(Fields):
|
|||||||
return jPrimary
|
return jPrimary
|
||||||
|
|
||||||
def _jSecondary(self, jSolution, srcList):
|
def _jSecondary(self, jSolution, srcList):
|
||||||
|
"""
|
||||||
|
Secondary current density is the thing we solved for
|
||||||
|
|
||||||
|
:param numpy.ndarray jSolution: field we solved for
|
||||||
|
:param list srcList: list of sources
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: secondary current density
|
||||||
|
"""
|
||||||
|
|
||||||
return jSolution
|
return jSolution
|
||||||
|
|
||||||
def _j(self, jSolution, srcList):
|
def _j(self, jSolution, srcList):
|
||||||
|
"""
|
||||||
|
Total current density is sum of primary and secondary
|
||||||
|
|
||||||
|
:param numpy.ndarray jSolution: field we solved for
|
||||||
|
:param list srcList: list of sources
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: total current density
|
||||||
|
"""
|
||||||
|
|
||||||
return self._jPrimary(jSolution, srcList) + self._jSecondary(jSolution, srcList)
|
return self._jPrimary(jSolution, srcList) + self._jSecondary(jSolution, srcList)
|
||||||
|
|
||||||
def _jDeriv_u(self, src, v, adjoint=False):
|
def _jDeriv_u(self, src, v, adjoint=False):
|
||||||
|
"""
|
||||||
|
Derivative of the total current density with respect to the thing we
|
||||||
|
solved for
|
||||||
|
|
||||||
|
:param SimPEG.EM.FDEM.Src src: source
|
||||||
|
:param numpy.ndarray v: vector to take product with
|
||||||
|
:param bool adjoint: adjoint?
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: product of the derivative of the current density with respect to the field we solved for with a vector
|
||||||
|
"""
|
||||||
|
|
||||||
return Identity()*v
|
return Identity()*v
|
||||||
|
|
||||||
def _jDeriv_m(self, src, v, adjoint=False):
|
def _jDeriv_m(self, src, v, adjoint=False):
|
||||||
|
"""
|
||||||
|
Derivative of the total current density with respect to the inversion model. Here, we assume that the primary does not depend on the model.
|
||||||
|
|
||||||
|
:param SimPEG.EM.FDEM.Src src: source
|
||||||
|
:param numpy.ndarray v: vector to take product with
|
||||||
|
:param bool adjoint: adjoint?
|
||||||
|
:rtype: SimPEG.Utils.Zero
|
||||||
|
:return: product of the current density derivative with respect to the inversion model with a vector
|
||||||
|
"""
|
||||||
# assuming primary does not depend on the model
|
# assuming primary does not depend on the model
|
||||||
return Zero()
|
return Zero()
|
||||||
|
|
||||||
def _hPrimary(self, jSolution, srcList):
|
def _hPrimary(self, jSolution, srcList):
|
||||||
|
"""
|
||||||
|
Primary magnetic field from source
|
||||||
|
|
||||||
|
:param numpy.ndarray hSolution: field we solved for
|
||||||
|
:param list srcList: list of sources
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: primary magnetic field as defined by the sources
|
||||||
|
"""
|
||||||
|
|
||||||
hPrimary = np.zeros([self._edgeCurl.shape[1],jSolution.shape[1]],dtype = complex)
|
hPrimary = np.zeros([self._edgeCurl.shape[1],jSolution.shape[1]],dtype = complex)
|
||||||
for i, src in enumerate(srcList):
|
for i, src in enumerate(srcList):
|
||||||
hp = src.hPrimary(self.prob)
|
hp = src.hPrimary(self.prob)
|
||||||
@@ -234,6 +559,15 @@ class Fields_j(Fields):
|
|||||||
return hPrimary
|
return hPrimary
|
||||||
|
|
||||||
def _hSecondary(self, jSolution, srcList):
|
def _hSecondary(self, jSolution, srcList):
|
||||||
|
"""
|
||||||
|
Secondary magnetic field from bSolution
|
||||||
|
|
||||||
|
:param numpy.ndarray jSolution: field we solved for
|
||||||
|
:param list srcList: list of sources
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: secondary magnetic field
|
||||||
|
"""
|
||||||
|
|
||||||
h = self._MeMuI * (self._edgeCurl.T * (self._MfRho * jSolution) )
|
h = self._MeMuI * (self._edgeCurl.T * (self._MfRho * jSolution) )
|
||||||
for i, src in enumerate(srcList):
|
for i, src in enumerate(srcList):
|
||||||
h[:,i] *= -1./(1j*omega(src.freq))
|
h[:,i] *= -1./(1j*omega(src.freq))
|
||||||
@@ -242,12 +576,32 @@ class Fields_j(Fields):
|
|||||||
return h
|
return h
|
||||||
|
|
||||||
def _hSecondaryDeriv_u(self, src, v, adjoint=False):
|
def _hSecondaryDeriv_u(self, src, v, adjoint=False):
|
||||||
|
"""
|
||||||
|
Derivative of the secondary magnetic field with respect to the thing we solved for
|
||||||
|
|
||||||
|
:param SimPEG.EM.FDEM.Src src: source
|
||||||
|
:param numpy.ndarray v: vector to take product with
|
||||||
|
:param bool adjoint: adjoint?
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: product of the derivative of the secondary magnetic field with respect to the field we solved for with a vector
|
||||||
|
"""
|
||||||
|
|
||||||
if not adjoint:
|
if not adjoint:
|
||||||
return -1./(1j*omega(src.freq)) * self._MeMuI * (self._edgeCurl.T * (self._MfRho * v) )
|
return -1./(1j*omega(src.freq)) * self._MeMuI * (self._edgeCurl.T * (self._MfRho * v) )
|
||||||
elif adjoint:
|
elif adjoint:
|
||||||
return -1./(1j*omega(src.freq)) * self._MfRho.T * (self._edgeCurl * ( self._MeMuI.T * v))
|
return -1./(1j*omega(src.freq)) * self._MfRho.T * (self._edgeCurl * ( self._MeMuI.T * v))
|
||||||
|
|
||||||
def _hSecondaryDeriv_m(self, src, v, adjoint=False):
|
def _hSecondaryDeriv_m(self, src, v, adjoint=False):
|
||||||
|
"""
|
||||||
|
Derivative of the secondary magnetic field with respect to the inversion model
|
||||||
|
|
||||||
|
:param SimPEG.EM.FDEM.Src src: source
|
||||||
|
:param numpy.ndarray v: vector to take product with
|
||||||
|
:param bool adjoint: adjoint?
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: product of the derivative of the secondary magnetic field with respect to the model with a vector
|
||||||
|
"""
|
||||||
|
|
||||||
jSolution = self[[src],'jSolution']
|
jSolution = self[[src],'jSolution']
|
||||||
MeMuI = self._MeMuI
|
MeMuI = self._MeMuI
|
||||||
C = self._edgeCurl
|
C = self._edgeCurl
|
||||||
@@ -260,7 +614,7 @@ class Fields_j(Fields):
|
|||||||
elif adjoint:
|
elif adjoint:
|
||||||
hDeriv_m = -1./(1j*omega(src.freq)) * MfRhoDeriv(jSolution).T * ( C * (MeMuI.T * v ) )
|
hDeriv_m = -1./(1j*omega(src.freq)) * MfRhoDeriv(jSolution).T * ( C * (MeMuI.T * v ) )
|
||||||
|
|
||||||
S_mDeriv,_ = src.evalDeriv(self.prob, adjoint)
|
S_mDeriv,_ = src.evalDeriv(self.prob, adjoint = adjoint)
|
||||||
|
|
||||||
if not adjoint:
|
if not adjoint:
|
||||||
S_mDeriv = S_mDeriv(v)
|
S_mDeriv = S_mDeriv(v)
|
||||||
@@ -272,17 +626,53 @@ class Fields_j(Fields):
|
|||||||
|
|
||||||
|
|
||||||
def _h(self, jSolution, srcList):
|
def _h(self, jSolution, srcList):
|
||||||
|
"""
|
||||||
|
Total magnetic field is sum of primary and secondary
|
||||||
|
|
||||||
|
:param numpy.ndarray eSolution: field we solved for
|
||||||
|
:param list srcList: list of sources
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: total magnetic field
|
||||||
|
"""
|
||||||
|
|
||||||
return self._hPrimary(jSolution, srcList) + self._hSecondary(jSolution, srcList)
|
return self._hPrimary(jSolution, srcList) + self._hSecondary(jSolution, srcList)
|
||||||
|
|
||||||
def _hDeriv_u(self, src, v, adjoint=False):
|
def _hDeriv_u(self, src, v, adjoint=False):
|
||||||
|
"""
|
||||||
|
Derivative of the total magnetic field with respect to the thing we solved for
|
||||||
|
|
||||||
|
:param SimPEG.EM.FDEM.Src src: source
|
||||||
|
:param numpy.ndarray v: vector to take product with
|
||||||
|
:param bool adjoint: adjoint?
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: product of the derivative of the magnetic field with respect to the field we solved for with a vector
|
||||||
|
"""
|
||||||
|
|
||||||
return self._hSecondaryDeriv_u(src, v, adjoint)
|
return self._hSecondaryDeriv_u(src, v, adjoint)
|
||||||
|
|
||||||
def _hDeriv_m(self, src, v, adjoint=False):
|
def _hDeriv_m(self, src, v, adjoint=False):
|
||||||
|
"""
|
||||||
|
Derivative of the total magnetic field density with respect to the inversion model.
|
||||||
|
|
||||||
|
:param SimPEG.EM.FDEM.Src src: source
|
||||||
|
:param numpy.ndarray v: vector to take product with
|
||||||
|
:param bool adjoint: adjoint?
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: product of the magnetic field derivative with respect to the inversion model with a vector
|
||||||
|
"""
|
||||||
|
|
||||||
# assuming the primary doesn't depend on the model
|
# assuming the primary doesn't depend on the model
|
||||||
return self._hSecondaryDeriv_m(src, v, adjoint)
|
return self._hSecondaryDeriv_m(src, v, adjoint)
|
||||||
|
|
||||||
|
|
||||||
class Fields_h(Fields):
|
class Fields_h(Fields):
|
||||||
|
"""
|
||||||
|
Fields object for Problem_h.
|
||||||
|
|
||||||
|
:param Mesh mesh: mesh
|
||||||
|
:param Survey survey: survey
|
||||||
|
"""
|
||||||
|
|
||||||
knownFields = {'hSolution':'E'}
|
knownFields = {'hSolution':'E'}
|
||||||
aliasFields = {
|
aliasFields = {
|
||||||
'h' : ['hSolution','E','_h'],
|
'h' : ['hSolution','E','_h'],
|
||||||
@@ -303,6 +693,15 @@ class Fields_h(Fields):
|
|||||||
self._MfRho = self.survey.prob.MfRho
|
self._MfRho = self.survey.prob.MfRho
|
||||||
|
|
||||||
def _hPrimary(self, hSolution, srcList):
|
def _hPrimary(self, hSolution, srcList):
|
||||||
|
"""
|
||||||
|
Primary magnetic field from source
|
||||||
|
|
||||||
|
:param numpy.ndarray eSolution: field we solved for
|
||||||
|
:param list srcList: list of sources
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: primary magnetic field as defined by the sources
|
||||||
|
"""
|
||||||
|
|
||||||
hPrimary = np.zeros_like(hSolution,dtype = complex)
|
hPrimary = np.zeros_like(hSolution,dtype = complex)
|
||||||
for i, src in enumerate(srcList):
|
for i, src in enumerate(srcList):
|
||||||
hp = src.hPrimary(self.prob)
|
hp = src.hPrimary(self.prob)
|
||||||
@@ -310,19 +709,67 @@ class Fields_h(Fields):
|
|||||||
return hPrimary
|
return hPrimary
|
||||||
|
|
||||||
def _hSecondary(self, hSolution, srcList):
|
def _hSecondary(self, hSolution, srcList):
|
||||||
|
"""
|
||||||
|
Secondary magnetic field is the thing we solved for
|
||||||
|
|
||||||
|
:param numpy.ndarray hSolution: field we solved for
|
||||||
|
:param list srcList: list of sources
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: secondary magnetic field
|
||||||
|
"""
|
||||||
|
|
||||||
return hSolution
|
return hSolution
|
||||||
|
|
||||||
def _h(self, hSolution, srcList):
|
def _h(self, hSolution, srcList):
|
||||||
|
"""
|
||||||
|
Total magnetic field is sum of primary and secondary
|
||||||
|
|
||||||
|
:param numpy.ndarray hSolution: field we solved for
|
||||||
|
:param list srcList: list of sources
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: total magnetic field
|
||||||
|
"""
|
||||||
|
|
||||||
return self._hPrimary(hSolution, srcList) + self._hSecondary(hSolution, srcList)
|
return self._hPrimary(hSolution, srcList) + self._hSecondary(hSolution, srcList)
|
||||||
|
|
||||||
def _hDeriv_u(self, src, v, adjoint=False):
|
def _hDeriv_u(self, src, v, adjoint=False):
|
||||||
|
"""
|
||||||
|
Derivative of the total magnetic field with respect to the thing we
|
||||||
|
solved for
|
||||||
|
|
||||||
|
:param SimPEG.EM.FDEM.Src src: source
|
||||||
|
:param numpy.ndarray v: vector to take product with
|
||||||
|
:param bool adjoint: adjoint?
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: product of the derivative of the magnetic field with respect to the field we solved for with a vector
|
||||||
|
"""
|
||||||
|
|
||||||
return Identity()*v
|
return Identity()*v
|
||||||
|
|
||||||
def _hDeriv_m(self, src, v, adjoint=False):
|
def _hDeriv_m(self, src, v, adjoint=False):
|
||||||
|
"""
|
||||||
|
Derivative of the total magnetic field with respect to the inversion model. Here, we assume that the primary does not depend on the model.
|
||||||
|
|
||||||
|
:param SimPEG.EM.FDEM.Src src: source
|
||||||
|
:param numpy.ndarray v: vector to take product with
|
||||||
|
:param bool adjoint: adjoint?
|
||||||
|
:rtype: SimPEG.Utils.Zero
|
||||||
|
:return: product of the magnetic field derivative with respect to the inversion model with a vector
|
||||||
|
"""
|
||||||
|
|
||||||
# assuming primary does not depend on the model
|
# assuming primary does not depend on the model
|
||||||
return Zero()
|
return Zero()
|
||||||
|
|
||||||
def _jPrimary(self, hSolution, srcList):
|
def _jPrimary(self, hSolution, srcList):
|
||||||
|
"""
|
||||||
|
Primary current density from source
|
||||||
|
|
||||||
|
:param numpy.ndarray hSolution: field we solved for
|
||||||
|
:param list srcList: list of sources
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: primary current density as defined by the sources
|
||||||
|
"""
|
||||||
|
|
||||||
jPrimary = np.zeros([self._edgeCurl.shape[0], hSolution.shape[1]], dtype = complex)
|
jPrimary = np.zeros([self._edgeCurl.shape[0], hSolution.shape[1]], dtype = complex)
|
||||||
for i, src in enumerate(srcList):
|
for i, src in enumerate(srcList):
|
||||||
jp = src.jPrimary(self.prob)
|
jp = src.jPrimary(self.prob)
|
||||||
@@ -330,6 +777,15 @@ class Fields_h(Fields):
|
|||||||
return jPrimary
|
return jPrimary
|
||||||
|
|
||||||
def _jSecondary(self, hSolution, srcList):
|
def _jSecondary(self, hSolution, srcList):
|
||||||
|
"""
|
||||||
|
Secondary current density from eSolution
|
||||||
|
|
||||||
|
:param numpy.ndarray hSolution: field we solved for
|
||||||
|
:param list srcList: list of sources
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: secondary current density
|
||||||
|
"""
|
||||||
|
|
||||||
j = self._edgeCurl*hSolution
|
j = self._edgeCurl*hSolution
|
||||||
for i, src in enumerate(srcList):
|
for i, src in enumerate(srcList):
|
||||||
_,S_e = src.eval(self.prob)
|
_,S_e = src.eval(self.prob)
|
||||||
@@ -337,22 +793,69 @@ class Fields_h(Fields):
|
|||||||
return j
|
return j
|
||||||
|
|
||||||
def _jSecondaryDeriv_u(self, src, v, adjoint=False):
|
def _jSecondaryDeriv_u(self, src, v, adjoint=False):
|
||||||
|
"""
|
||||||
|
Derivative of the secondary current density with respect to the thing we solved for
|
||||||
|
|
||||||
|
:param SimPEG.EM.FDEM.Src src: source
|
||||||
|
:param numpy.ndarray v: vector to take product with
|
||||||
|
:param bool adjoint: adjoint?
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: product of the derivative of the secondary current density with respect to the field we solved for with a vector
|
||||||
|
"""
|
||||||
|
|
||||||
if not adjoint:
|
if not adjoint:
|
||||||
return self._edgeCurl*v
|
return self._edgeCurl*v
|
||||||
elif adjoint:
|
elif adjoint:
|
||||||
return self._edgeCurl.T*v
|
return self._edgeCurl.T*v
|
||||||
|
|
||||||
def _jSecondaryDeriv_m(self, src, v, adjoint=False):
|
def _jSecondaryDeriv_m(self, src, v, adjoint=False):
|
||||||
_,S_eDeriv = src.evalDeriv(self.prob, adjoint)
|
"""
|
||||||
S_eDeriv = S_eDeriv(v)
|
Derivative of the secondary current density with respect to the inversion model.
|
||||||
|
|
||||||
|
:param SimPEG.EM.FDEM.Src src: source
|
||||||
|
:param numpy.ndarray v: vector to take product with
|
||||||
|
:param bool adjoint: adjoint?
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: product of the secondary current density derivative with respect to the inversion model with a vector
|
||||||
|
"""
|
||||||
|
|
||||||
|
_,S_eDeriv = src.evalDeriv(self.prob, v, adjoint)
|
||||||
return -S_eDeriv
|
return -S_eDeriv
|
||||||
|
|
||||||
def _j(self, hSolution, srcList):
|
def _j(self, hSolution, srcList):
|
||||||
|
"""
|
||||||
|
Total current density is sum of primary and secondary
|
||||||
|
|
||||||
|
:param numpy.ndarray eSolution: field we solved for
|
||||||
|
:param list srcList: list of sources
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: total current density
|
||||||
|
"""
|
||||||
|
|
||||||
return self._jPrimary(hSolution, srcList) + self._jSecondary(hSolution, srcList)
|
return self._jPrimary(hSolution, srcList) + self._jSecondary(hSolution, srcList)
|
||||||
|
|
||||||
def _jDeriv_u(self, src, v, adjoint=False):
|
def _jDeriv_u(self, src, v, adjoint=False):
|
||||||
|
"""
|
||||||
|
Derivative of the total current density with respect to the thing we solved for
|
||||||
|
|
||||||
|
:param SimPEG.EM.FDEM.Src src: source
|
||||||
|
:param numpy.ndarray v: vector to take product with
|
||||||
|
:param bool adjoint: adjoint?
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: product of the derivative of the current density with respect to the field we solved for with a vector
|
||||||
|
"""
|
||||||
return self._jSecondaryDeriv_u(src,v,adjoint)
|
return self._jSecondaryDeriv_u(src,v,adjoint)
|
||||||
|
|
||||||
def _jDeriv_m(self, src, v, adjoint=False):
|
def _jDeriv_m(self, src, v, adjoint=False):
|
||||||
|
"""
|
||||||
|
Derivative of the total current density with respect to the inversion model.
|
||||||
|
|
||||||
|
:param SimPEG.EM.FDEM.Src src: source
|
||||||
|
:param numpy.ndarray v: vector to take product with
|
||||||
|
:param bool adjoint: adjoint?
|
||||||
|
:rtype: SimPEG.Utils.Zero
|
||||||
|
:return: product of the current density with respect to the inversion model with a vector
|
||||||
|
"""
|
||||||
|
|
||||||
# assuming the primary does not depend on the model
|
# assuming the primary does not depend on the model
|
||||||
return self._jSecondaryDeriv_m(src,v,adjoint)
|
return self._jSecondaryDeriv_m(src,v,adjoint)
|
||||||
|
|||||||
+273
-18
@@ -2,54 +2,140 @@ from SimPEG import Survey, Problem, Utils, np, sp
|
|||||||
from scipy.constants import mu_0
|
from scipy.constants import mu_0
|
||||||
from SimPEG.EM.Utils import *
|
from SimPEG.EM.Utils import *
|
||||||
from SimPEG.Utils import Zero
|
from SimPEG.Utils import Zero
|
||||||
# from SurveyFDEM import Rx
|
|
||||||
|
|
||||||
|
|
||||||
class BaseSrc(Survey.BaseSrc):
|
class BaseSrc(Survey.BaseSrc):
|
||||||
|
"""
|
||||||
|
Base source class for FDEM Survey
|
||||||
|
"""
|
||||||
|
|
||||||
freq = None
|
freq = None
|
||||||
# rxPair = Rx
|
# rxPair = RxFDEM
|
||||||
integrate = True
|
integrate = True
|
||||||
|
|
||||||
def eval(self, prob):
|
def eval(self, prob):
|
||||||
|
"""
|
||||||
|
Evaluate the source terms.
|
||||||
|
- :math:`S_m` : magnetic source term
|
||||||
|
- :math:`S_e` : electric source term
|
||||||
|
|
||||||
|
:param Problem prob: FDEM Problem
|
||||||
|
:rtype: (numpy.ndarray, numpy.ndarray)
|
||||||
|
:return: tuple with magnetic source term and electric source term
|
||||||
|
"""
|
||||||
S_m = self.S_m(prob)
|
S_m = self.S_m(prob)
|
||||||
S_e = self.S_e(prob)
|
S_e = self.S_e(prob)
|
||||||
return S_m, S_e
|
return S_m, S_e
|
||||||
|
|
||||||
def evalDeriv(self, prob, v, adjoint=False):
|
def evalDeriv(self, prob, v=None, adjoint=False):
|
||||||
return lambda v: self.S_mDeriv(prob,v,adjoint), lambda v: self.S_eDeriv(prob,v,adjoint)
|
"""
|
||||||
|
Derivatives of the source terms with respect to the inversion model
|
||||||
|
- :code:`S_mDeriv` : derivative of the magnetic source term
|
||||||
|
- :code:`S_eDeriv` : derivative of the electric source term
|
||||||
|
|
||||||
|
:param Problem prob: FDEM Problem
|
||||||
|
:param numpy.ndarray v: vector to take product with
|
||||||
|
:param bool adjoint: adjoint?
|
||||||
|
:rtype: (numpy.ndarray, numpy.ndarray)
|
||||||
|
:return: tuple with magnetic source term and electric source term derivatives times a vector
|
||||||
|
"""
|
||||||
|
if v is not None:
|
||||||
|
return self.S_mDeriv(prob,v,adjoint), self.S_eDeriv(prob,v,adjoint)
|
||||||
|
else:
|
||||||
|
return lambda v: self.S_mDeriv(prob,v,adjoint), lambda v: self.S_eDeriv(prob,v,adjoint)
|
||||||
|
|
||||||
def bPrimary(self, prob):
|
def bPrimary(self, prob):
|
||||||
|
"""
|
||||||
|
Primary magnetic flux density
|
||||||
|
|
||||||
|
:param Problem prob: FDEM Problem
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: primary magnetic flux density
|
||||||
|
"""
|
||||||
return Zero()
|
return Zero()
|
||||||
|
|
||||||
def hPrimary(self, prob):
|
def hPrimary(self, prob):
|
||||||
|
"""
|
||||||
|
Primary magnetic field
|
||||||
|
|
||||||
|
:param Problem prob: FDEM Problem
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: primary magnetic field
|
||||||
|
"""
|
||||||
return Zero()
|
return Zero()
|
||||||
|
|
||||||
def ePrimary(self, prob):
|
def ePrimary(self, prob):
|
||||||
|
"""
|
||||||
|
Primary electric field
|
||||||
|
|
||||||
|
:param Problem prob: FDEM Problem
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: primary electric field
|
||||||
|
"""
|
||||||
return Zero()
|
return Zero()
|
||||||
|
|
||||||
def jPrimary(self, prob):
|
def jPrimary(self, prob):
|
||||||
|
"""
|
||||||
|
Primary current density
|
||||||
|
|
||||||
|
:param Problem prob: FDEM Problem
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: primary current density
|
||||||
|
"""
|
||||||
return Zero()
|
return Zero()
|
||||||
|
|
||||||
def S_m(self, prob):
|
def S_m(self, prob):
|
||||||
|
"""
|
||||||
|
Magnetic source term
|
||||||
|
|
||||||
|
:param Problem prob: FDEM Problem
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: magnetic source term on mesh
|
||||||
|
"""
|
||||||
return Zero()
|
return Zero()
|
||||||
|
|
||||||
def S_e(self, prob):
|
def S_e(self, prob):
|
||||||
|
"""
|
||||||
|
Electric source term
|
||||||
|
|
||||||
|
:param Problem prob: FDEM Problem
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: electric source term on mesh
|
||||||
|
"""
|
||||||
return Zero()
|
return Zero()
|
||||||
|
|
||||||
def S_mDeriv(self, prob, v, adjoint = False):
|
def S_mDeriv(self, prob, v, adjoint = False):
|
||||||
|
"""
|
||||||
|
Derivative of magnetic source term with respect to the inversion model
|
||||||
|
|
||||||
|
:param Problem prob: FDEM Problem
|
||||||
|
:param numpy.ndarray v: vector to take product with
|
||||||
|
:param bool adjoint: adjoint?
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: product of magnetic source term derivative with a vector
|
||||||
|
"""
|
||||||
|
|
||||||
return Zero()
|
return Zero()
|
||||||
|
|
||||||
def S_eDeriv(self, prob, v, adjoint = False):
|
def S_eDeriv(self, prob, v, adjoint = False):
|
||||||
|
"""
|
||||||
|
Derivative of electric source term with respect to the inversion model
|
||||||
|
|
||||||
|
:param Problem prob: FDEM Problem
|
||||||
|
:param numpy.ndarray v: vector to take product with
|
||||||
|
:param bool adjoint: adjoint?
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: product of electric source term derivative with a vector
|
||||||
|
"""
|
||||||
return Zero()
|
return Zero()
|
||||||
|
|
||||||
|
|
||||||
class RawVec_e(BaseSrc):
|
class RawVec_e(BaseSrc):
|
||||||
"""
|
"""
|
||||||
RawVec electric source. It is defined by the user provided vector S_e
|
RawVec electric source. It is defined by the user provided vector S_e
|
||||||
|
|
||||||
:param numpy.array S_e: electric source term
|
:param list rxList: receiver list
|
||||||
:param float freq: frequency
|
:param float freq: frequency
|
||||||
:param rxList: receiver list
|
:param numpy.array S_e: electric source term
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, rxList, freq, S_e): #, ePrimary=None, bPrimary=None, hPrimary=None, jPrimary=None):
|
def __init__(self, rxList, freq, S_e): #, ePrimary=None, bPrimary=None, hPrimary=None, jPrimary=None):
|
||||||
@@ -58,16 +144,17 @@ class RawVec_e(BaseSrc):
|
|||||||
BaseSrc.__init__(self, rxList)
|
BaseSrc.__init__(self, rxList)
|
||||||
|
|
||||||
def S_e(self, prob):
|
def S_e(self, prob):
|
||||||
|
|
||||||
return self._S_e
|
return self._S_e
|
||||||
|
|
||||||
|
|
||||||
class RawVec_m(BaseSrc):
|
class RawVec_m(BaseSrc):
|
||||||
"""
|
"""
|
||||||
RawVec magnetic source. It is defined by the user provided vector S_m
|
RawVec magnetic source. It is defined by the user provided vector S_m
|
||||||
|
|
||||||
:param numpy.array S_m: magnetic source term
|
:param float freq: frequency
|
||||||
:param float freq: frequency
|
:param rxList: receiver list
|
||||||
:param rxList: receiver list
|
:param numpy.array S_m: magnetic source term
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, rxList, freq, S_m, integrate = True): #ePrimary=Zero(), bPrimary=Zero(), hPrimary=Zero(), jPrimary=Zero()):
|
def __init__(self, rxList, freq, S_m, integrate = True): #ePrimary=Zero(), bPrimary=Zero(), hPrimary=Zero(), jPrimary=Zero()):
|
||||||
@@ -78,17 +165,24 @@ class RawVec_m(BaseSrc):
|
|||||||
BaseSrc.__init__(self, rxList)
|
BaseSrc.__init__(self, rxList)
|
||||||
|
|
||||||
def S_m(self, prob):
|
def S_m(self, prob):
|
||||||
|
"""
|
||||||
|
Magnetic source term
|
||||||
|
|
||||||
|
:param Problem prob: FDEM Problem
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: magnetic source term on mesh
|
||||||
|
"""
|
||||||
return self._S_m
|
return self._S_m
|
||||||
|
|
||||||
|
|
||||||
class RawVec(BaseSrc):
|
class RawVec(BaseSrc):
|
||||||
"""
|
"""
|
||||||
RawVec source. It is defined by the user provided vectors S_m, S_e
|
RawVec source. It is defined by the user provided vectors S_m, S_e
|
||||||
|
|
||||||
:param numpy.array S_m: magnetic source term
|
:param rxList: receiver list
|
||||||
:param numpy.array S_e: electric source term
|
:param float freq: frequency
|
||||||
:param float freq: frequency
|
:param numpy.array S_m: magnetic source term
|
||||||
:param rxList: receiver list
|
:param numpy.array S_e: electric source term
|
||||||
"""
|
"""
|
||||||
def __init__(self, rxList, freq, S_m, S_e, integrate = True):
|
def __init__(self, rxList, freq, S_m, S_e, integrate = True):
|
||||||
self._S_m = np.array(S_m,dtype=complex)
|
self._S_m = np.array(S_m,dtype=complex)
|
||||||
@@ -109,6 +203,51 @@ class RawVec(BaseSrc):
|
|||||||
|
|
||||||
|
|
||||||
class MagDipole(BaseSrc):
|
class MagDipole(BaseSrc):
|
||||||
|
"""
|
||||||
|
Point magnetic dipole source calculated by taking the curl of a magnetic
|
||||||
|
vector potential. By taking the discrete curl, we ensure that the magnetic
|
||||||
|
flux density is divergence free (no magnetic monopoles!).
|
||||||
|
|
||||||
|
This approach uses a primary-secondary in frequency. Here we show the
|
||||||
|
derivation for E-B formulation noting that similar steps are followed for
|
||||||
|
the H-J formulation.
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
\mathbf{C} \mathbf{e} + i \omega \mathbf{b} = \mathbf{s_m} \\\\
|
||||||
|
{\mathbf{C}^T \mathbf{M_{\mu^{-1}}^f} \mathbf{b} - \mathbf{M_{\sigma}^e} \mathbf{e} = \mathbf{s_e}}
|
||||||
|
|
||||||
|
We split up the fields and :math:`\mu^{-1}` into primary (:math:`\mathbf{P}`) and secondary (:math:`\mathbf{S}`) components
|
||||||
|
|
||||||
|
- :math:`\mathbf{e} = \mathbf{e^P} + \mathbf{e^S}`
|
||||||
|
- :math:`\mathbf{b} = \mathbf{b^P} + \mathbf{b^S}`
|
||||||
|
- :math:`\\boldsymbol{\mu}^{\mathbf{-1}} = \\boldsymbol{\mu}^{\mathbf{-1}^\mathbf{P}} + \\boldsymbol{\mu}^{\mathbf{-1}^\mathbf{S}}`
|
||||||
|
|
||||||
|
and define a zero-frequency primary problem, noting that the source is
|
||||||
|
generated by a divergence free electric current
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
\mathbf{C} \mathbf{e^P} = \mathbf{s_m^P} = 0 \\\\
|
||||||
|
{\mathbf{C}^T \mathbf{{M_{\mu^{-1}}^f}^P} \mathbf{b^P} - \mathbf{M_{\sigma}^e} \mathbf{e^P} = \mathbf{M^e} \mathbf{s_e^P}}
|
||||||
|
|
||||||
|
Since :math:`\mathbf{e^P}` is curl-free, divergence-free, we assume that there is no constant field background, the :math:`\mathbf{e^P} = 0`, so our primary problem is
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
\mathbf{e^P} = 0 \\\\
|
||||||
|
{\mathbf{C}^T \mathbf{{M_{\mu^{-1}}^f}^P} \mathbf{b^P} = \mathbf{s_e^P}}
|
||||||
|
|
||||||
|
Our secondary problem is then
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
\mathbf{C} \mathbf{e^S} + i \omega \mathbf{b^S} = - i \omega \mathbf{b^P} \\\\
|
||||||
|
{\mathbf{C}^T \mathbf{M_{\mu^{-1}}^f} \mathbf{b^S} - \mathbf{M_{\sigma}^e} \mathbf{e^S} = -\mathbf{C}^T \mathbf{{M_{\mu^{-1}}^f}^S} \mathbf{b^P}}
|
||||||
|
|
||||||
|
:param list rxList: receiver list
|
||||||
|
:param float freq: frequency
|
||||||
|
:param numpy.ndarray loc: source location (ie: :code:`np.r_[xloc,yloc,zloc]`)
|
||||||
|
:param string orientation: 'X', 'Y', 'Z'
|
||||||
|
:param float moment: magnetic dipole moment
|
||||||
|
:param float mu: background magnetic permeability
|
||||||
|
"""
|
||||||
|
|
||||||
#TODO: right now, orientation doesn't actually do anything! The methods in SrcUtils should take care of that
|
#TODO: right now, orientation doesn't actually do anything! The methods in SrcUtils should take care of that
|
||||||
def __init__(self, rxList, freq, loc, orientation='Z', moment=1., mu = mu_0):
|
def __init__(self, rxList, freq, loc, orientation='Z', moment=1., mu = mu_0):
|
||||||
@@ -121,6 +260,13 @@ class MagDipole(BaseSrc):
|
|||||||
BaseSrc.__init__(self, rxList)
|
BaseSrc.__init__(self, rxList)
|
||||||
|
|
||||||
def bPrimary(self, prob):
|
def bPrimary(self, prob):
|
||||||
|
"""
|
||||||
|
The primary magnetic flux density from a magnetic vector potential
|
||||||
|
|
||||||
|
:param Problem prob: FDEM problem
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: primary magnetic field
|
||||||
|
"""
|
||||||
eqLocs = prob._eqLocs
|
eqLocs = prob._eqLocs
|
||||||
|
|
||||||
if eqLocs is 'FE':
|
if eqLocs is 'FE':
|
||||||
@@ -152,14 +298,37 @@ class MagDipole(BaseSrc):
|
|||||||
return C*a
|
return C*a
|
||||||
|
|
||||||
def hPrimary(self, prob):
|
def hPrimary(self, prob):
|
||||||
|
"""
|
||||||
|
The primary magnetic field from a magnetic vector potential
|
||||||
|
|
||||||
|
:param Problem prob: FDEM problem
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: primary magnetic field
|
||||||
|
"""
|
||||||
b = self.bPrimary(prob)
|
b = self.bPrimary(prob)
|
||||||
return h_from_b(prob,b)
|
return h_from_b(prob,b)
|
||||||
|
|
||||||
def S_m(self, prob):
|
def S_m(self, prob):
|
||||||
|
"""
|
||||||
|
The magnetic source term
|
||||||
|
|
||||||
|
:param Problem prob: FDEM problem
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: primary magnetic field
|
||||||
|
"""
|
||||||
|
|
||||||
b_p = self.bPrimary(prob)
|
b_p = self.bPrimary(prob)
|
||||||
return -1j*omega(self.freq)*b_p
|
return -1j*omega(self.freq)*b_p
|
||||||
|
|
||||||
def S_e(self, prob):
|
def S_e(self, prob):
|
||||||
|
"""
|
||||||
|
The electric source term
|
||||||
|
|
||||||
|
:param Problem prob: FDEM problem
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: primary magnetic field
|
||||||
|
"""
|
||||||
|
|
||||||
if all(np.r_[self.mu] == np.r_[prob.curModel.mu]):
|
if all(np.r_[self.mu] == np.r_[prob.curModel.mu]):
|
||||||
return Zero()
|
return Zero()
|
||||||
else:
|
else:
|
||||||
@@ -179,6 +348,21 @@ class MagDipole(BaseSrc):
|
|||||||
|
|
||||||
class MagDipole_Bfield(BaseSrc):
|
class MagDipole_Bfield(BaseSrc):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Point magnetic dipole source calculated with the analytic solution for the
|
||||||
|
fields from a magnetic dipole. No discrete curl is taken, so the magnetic
|
||||||
|
flux density may not be strictly divergence free.
|
||||||
|
|
||||||
|
This approach uses a primary-secondary in frequency in the same fashion as the MagDipole.
|
||||||
|
|
||||||
|
:param list rxList: receiver list
|
||||||
|
:param float freq: frequency
|
||||||
|
:param numpy.ndarray loc: source location (ie: :code:`np.r_[xloc,yloc,zloc]`)
|
||||||
|
:param string orientation: 'X', 'Y', 'Z'
|
||||||
|
:param float moment: magnetic dipole moment
|
||||||
|
:param float mu: background magnetic permeability
|
||||||
|
"""
|
||||||
|
|
||||||
#TODO: right now, orientation doesn't actually do anything! The methods in SrcUtils should take care of that
|
#TODO: right now, orientation doesn't actually do anything! The methods in SrcUtils should take care of that
|
||||||
#TODO: neither does moment
|
#TODO: neither does moment
|
||||||
def __init__(self, rxList, freq, loc, orientation='Z', moment=1., mu = mu_0):
|
def __init__(self, rxList, freq, loc, orientation='Z', moment=1., mu = mu_0):
|
||||||
@@ -190,6 +374,14 @@ class MagDipole_Bfield(BaseSrc):
|
|||||||
BaseSrc.__init__(self, rxList)
|
BaseSrc.__init__(self, rxList)
|
||||||
|
|
||||||
def bPrimary(self, prob):
|
def bPrimary(self, prob):
|
||||||
|
"""
|
||||||
|
The primary magnetic flux density from the analytic solution for magnetic fields from a dipole
|
||||||
|
|
||||||
|
:param Problem prob: FDEM problem
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: primary magnetic field
|
||||||
|
"""
|
||||||
|
|
||||||
eqLocs = prob._eqLocs
|
eqLocs = prob._eqLocs
|
||||||
|
|
||||||
if eqLocs is 'FE':
|
if eqLocs is 'FE':
|
||||||
@@ -221,14 +413,35 @@ class MagDipole_Bfield(BaseSrc):
|
|||||||
return b
|
return b
|
||||||
|
|
||||||
def hPrimary(self, prob):
|
def hPrimary(self, prob):
|
||||||
|
"""
|
||||||
|
The primary magnetic field from a magnetic vector potential
|
||||||
|
|
||||||
|
:param Problem prob: FDEM problem
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: primary magnetic field
|
||||||
|
"""
|
||||||
b = self.bPrimary(prob)
|
b = self.bPrimary(prob)
|
||||||
return h_from_b(prob, b)
|
return h_from_b(prob, b)
|
||||||
|
|
||||||
def S_m(self, prob):
|
def S_m(self, prob):
|
||||||
|
"""
|
||||||
|
The magnetic source term
|
||||||
|
|
||||||
|
:param Problem prob: FDEM problem
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: primary magnetic field
|
||||||
|
"""
|
||||||
b = self.bPrimary(prob)
|
b = self.bPrimary(prob)
|
||||||
return -1j*omega(self.freq)*b
|
return -1j*omega(self.freq)*b
|
||||||
|
|
||||||
def S_e(self, prob):
|
def S_e(self, prob):
|
||||||
|
"""
|
||||||
|
The electric source term
|
||||||
|
|
||||||
|
:param Problem prob: FDEM problem
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: primary magnetic field
|
||||||
|
"""
|
||||||
if all(np.r_[self.mu] == np.r_[prob.curModel.mu]):
|
if all(np.r_[self.mu] == np.r_[prob.curModel.mu]):
|
||||||
return Zero()
|
return Zero()
|
||||||
else:
|
else:
|
||||||
@@ -247,6 +460,20 @@ class MagDipole_Bfield(BaseSrc):
|
|||||||
|
|
||||||
|
|
||||||
class CircularLoop(BaseSrc):
|
class CircularLoop(BaseSrc):
|
||||||
|
"""
|
||||||
|
Circular loop magnetic source calculated by taking the curl of a magnetic
|
||||||
|
vector potential. By taking the discrete curl, we ensure that the magnetic
|
||||||
|
flux density is divergence free (no magnetic monopoles!).
|
||||||
|
|
||||||
|
This approach uses a primary-secondary in frequency in the same fashion as the MagDipole.
|
||||||
|
|
||||||
|
:param list rxList: receiver list
|
||||||
|
:param float freq: frequency
|
||||||
|
:param numpy.ndarray loc: source location (ie: :code:`np.r_[xloc,yloc,zloc]`)
|
||||||
|
:param string orientation: 'X', 'Y', 'Z'
|
||||||
|
:param float moment: magnetic dipole moment
|
||||||
|
:param float mu: background magnetic permeability
|
||||||
|
"""
|
||||||
|
|
||||||
#TODO: right now, orientation doesn't actually do anything! The methods in SrcUtils should take care of that
|
#TODO: right now, orientation doesn't actually do anything! The methods in SrcUtils should take care of that
|
||||||
def __init__(self, rxList, freq, loc, orientation='Z', radius = 1., mu=mu_0):
|
def __init__(self, rxList, freq, loc, orientation='Z', radius = 1., mu=mu_0):
|
||||||
@@ -259,6 +486,13 @@ class CircularLoop(BaseSrc):
|
|||||||
BaseSrc.__init__(self, rxList)
|
BaseSrc.__init__(self, rxList)
|
||||||
|
|
||||||
def bPrimary(self, prob):
|
def bPrimary(self, prob):
|
||||||
|
"""
|
||||||
|
The primary magnetic flux density from a magnetic vector potential
|
||||||
|
|
||||||
|
:param Problem prob: FDEM problem
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: primary magnetic field
|
||||||
|
"""
|
||||||
eqLocs = prob._eqLocs
|
eqLocs = prob._eqLocs
|
||||||
|
|
||||||
if eqLocs is 'FE':
|
if eqLocs is 'FE':
|
||||||
@@ -289,14 +523,35 @@ class CircularLoop(BaseSrc):
|
|||||||
return C*a
|
return C*a
|
||||||
|
|
||||||
def hPrimary(self, prob):
|
def hPrimary(self, prob):
|
||||||
|
"""
|
||||||
|
The primary magnetic field from a magnetic vector potential
|
||||||
|
|
||||||
|
:param Problem prob: FDEM problem
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: primary magnetic field
|
||||||
|
"""
|
||||||
b = self.bPrimary(prob)
|
b = self.bPrimary(prob)
|
||||||
return 1./self.mu*b
|
return 1./self.mu*b
|
||||||
|
|
||||||
def S_m(self, prob):
|
def S_m(self, prob):
|
||||||
|
"""
|
||||||
|
The magnetic source term
|
||||||
|
|
||||||
|
:param Problem prob: FDEM problem
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: primary magnetic field
|
||||||
|
"""
|
||||||
b = self.bPrimary(prob)
|
b = self.bPrimary(prob)
|
||||||
return -1j*omega(self.freq)*b
|
return -1j*omega(self.freq)*b
|
||||||
|
|
||||||
def S_e(self, prob):
|
def S_e(self, prob):
|
||||||
|
"""
|
||||||
|
The electric source term
|
||||||
|
|
||||||
|
:param Problem prob: FDEM problem
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: primary magnetic field
|
||||||
|
"""
|
||||||
if all(np.r_[self.mu] == np.r_[prob.curModel.mu]):
|
if all(np.r_[self.mu] == np.r_[prob.curModel.mu]):
|
||||||
return Zero()
|
return Zero()
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -10,6 +10,12 @@ import SrcFDEM as Src
|
|||||||
####################################################
|
####################################################
|
||||||
|
|
||||||
class Rx(SimPEG.Survey.BaseRx):
|
class Rx(SimPEG.Survey.BaseRx):
|
||||||
|
"""
|
||||||
|
Frequency domain receivers
|
||||||
|
|
||||||
|
:param numpy.ndarray locs: receiver locations (ie. :code:`np.r_[x,y,z]`)
|
||||||
|
:param string rxType: reciever type from knownRxTypes
|
||||||
|
"""
|
||||||
|
|
||||||
knownRxTypes = {
|
knownRxTypes = {
|
||||||
'exr':['e', 'Ex', 'real'],
|
'exr':['e', 'Ex', 'real'],
|
||||||
@@ -60,15 +66,33 @@ class Rx(SimPEG.Survey.BaseRx):
|
|||||||
"""Component projection (real/imag)"""
|
"""Component projection (real/imag)"""
|
||||||
return self.knownRxTypes[self.rxType][2]
|
return self.knownRxTypes[self.rxType][2]
|
||||||
|
|
||||||
def projectFields(self, src, mesh, u):
|
def projectFields(self, src, mesh, f):
|
||||||
P = self.getP(mesh)
|
"""
|
||||||
u_part_complex = u[src, self.projField]
|
Project fields to recievers to get data.
|
||||||
# get the real or imag component
|
|
||||||
real_or_imag = self.projComp
|
:param Source src: FDEM source
|
||||||
|
:param Mesh mesh: mesh used
|
||||||
|
:param Fields f: fields object
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: fields projected to recievers
|
||||||
|
"""
|
||||||
|
P = self.getP(mesh) # get interpolation to recievers
|
||||||
|
u_part_complex = f[src, self.projField]
|
||||||
|
real_or_imag = self.projComp # get the real or imag component
|
||||||
u_part = getattr(u_part_complex, real_or_imag)
|
u_part = getattr(u_part_complex, real_or_imag)
|
||||||
return P*u_part
|
return P*u_part
|
||||||
|
|
||||||
def projectFieldsDeriv(self, src, mesh, u, v, adjoint=False):
|
def projectFieldsDeriv(self, src, mesh, f, v, adjoint=False):
|
||||||
|
"""
|
||||||
|
Derivative of projected fields with respect to the inversion model times a vector.
|
||||||
|
|
||||||
|
:param Source src: FDEM source
|
||||||
|
:param Mesh mesh: mesh used
|
||||||
|
:param Fields f: fields object
|
||||||
|
:param numpy.ndarray v: vector to multiply
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: fields projected to recievers
|
||||||
|
"""
|
||||||
P = self.getP(mesh)
|
P = self.getP(mesh)
|
||||||
|
|
||||||
if not adjoint:
|
if not adjoint:
|
||||||
@@ -95,10 +119,13 @@ class Rx(SimPEG.Survey.BaseRx):
|
|||||||
|
|
||||||
class Survey(SimPEG.Survey.BaseSurvey):
|
class Survey(SimPEG.Survey.BaseSurvey):
|
||||||
"""
|
"""
|
||||||
docstring for SurveyFDEM
|
Frequency domain electromagnetic survey
|
||||||
|
|
||||||
|
:param list srcList: list of FDEM sources used in the survey
|
||||||
"""
|
"""
|
||||||
|
|
||||||
srcPair = Src.BaseSrc
|
srcPair = Src.BaseSrc
|
||||||
|
rxPaair = Rx
|
||||||
|
|
||||||
def __init__(self, srcList, **kwargs):
|
def __init__(self, srcList, **kwargs):
|
||||||
# Sort these by frequency
|
# Sort these by frequency
|
||||||
@@ -126,6 +153,7 @@ class Survey(SimPEG.Survey.BaseSurvey):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def nSrcByFreq(self):
|
def nSrcByFreq(self):
|
||||||
|
"""Number of sources at each frequency"""
|
||||||
if getattr(self, '_nSrcByFreq', None) is None:
|
if getattr(self, '_nSrcByFreq', None) is None:
|
||||||
self._nSrcByFreq = {}
|
self._nSrcByFreq = {}
|
||||||
for freq in self.freqs:
|
for freq in self.freqs:
|
||||||
@@ -133,11 +161,22 @@ class Survey(SimPEG.Survey.BaseSurvey):
|
|||||||
return self._nSrcByFreq
|
return self._nSrcByFreq
|
||||||
|
|
||||||
def getSrcByFreq(self, freq):
|
def getSrcByFreq(self, freq):
|
||||||
"""Returns the sources associated with a specific frequency."""
|
"""
|
||||||
|
Returns the sources associated with a specific frequency.
|
||||||
|
:param float freq: frequency for which we look up sources
|
||||||
|
:rtype: dictionary
|
||||||
|
:return: sources at the sepcified frequency
|
||||||
|
"""
|
||||||
assert freq in self._freqDict, "The requested frequency is not in this survey."
|
assert freq in self._freqDict, "The requested frequency is not in this survey."
|
||||||
return self._freqDict[freq]
|
return self._freqDict[freq]
|
||||||
|
|
||||||
def projectFields(self, u):
|
def projectFields(self, u):
|
||||||
|
"""
|
||||||
|
Project fields to receiver locations
|
||||||
|
:param Fields u: fields object
|
||||||
|
:rtype: numpy.ndarray
|
||||||
|
:return: data
|
||||||
|
"""
|
||||||
data = SimPEG.Survey.Data(self)
|
data = SimPEG.Survey.Data(self)
|
||||||
for src in self.srcList:
|
for src in self.srcList:
|
||||||
for rx in src.rxList:
|
for rx in src.rxList:
|
||||||
@@ -145,4 +184,4 @@ class Survey(SimPEG.Survey.BaseSurvey):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
def projectFieldsDeriv(self, u):
|
def projectFieldsDeriv(self, u):
|
||||||
raise Exception('Use Sources to project fields deriv.')
|
raise Exception('Use Receivers to project fields deriv.')
|
||||||
|
|||||||
@@ -79,12 +79,32 @@ class SrcTDEM(Survey.BaseSrc):
|
|||||||
|
|
||||||
class SrcTDEM_VMD_MVP(SrcTDEM):
|
class SrcTDEM_VMD_MVP(SrcTDEM):
|
||||||
|
|
||||||
def __init__(self,rxList,loc):
|
def __init__(self,rxList,loc,waveformType="STEPOFF"):
|
||||||
self.loc = loc
|
self.loc = loc
|
||||||
|
self.waveformType = waveformType
|
||||||
SrcTDEM.__init__(self,rxList)
|
SrcTDEM.__init__(self,rxList)
|
||||||
|
|
||||||
def getInitialFields(self, mesh):
|
def getInitialFields(self, mesh):
|
||||||
"""Vertical magnetic dipole, magnetic vector potential"""
|
"""Vertical magnetic dipole, magnetic vector potential"""
|
||||||
|
if self.waveformType == "STEPOFF":
|
||||||
|
print ">> Step waveform: Non-zero initial condition"
|
||||||
|
if mesh._meshType is 'CYL':
|
||||||
|
if mesh.isSymmetric:
|
||||||
|
MVP = MagneticDipoleVectorPotential(self.loc, mesh, 'Ey')
|
||||||
|
else:
|
||||||
|
raise NotImplementedError('Non-symmetric cyl mesh not implemented yet!')
|
||||||
|
elif mesh._meshType is 'TENSOR':
|
||||||
|
MVP = MagneticDipoleVectorPotential(self.loc, mesh, ['Ex','Ey','Ez'])
|
||||||
|
else:
|
||||||
|
raise Exception('Unknown mesh for VMD')
|
||||||
|
return {"b": mesh.edgeCurl*MVP}
|
||||||
|
elif self.waveformType == "GENERAL":
|
||||||
|
print ">> General waveform: Zero initial condition"
|
||||||
|
return {"b": np.zeros(mesh.nF)}
|
||||||
|
else:
|
||||||
|
raise NotImplementedError("Only use STEPOFF or GENERAL")
|
||||||
|
|
||||||
|
def getMeS(self, mesh, MfMui):
|
||||||
if mesh._meshType is 'CYL':
|
if mesh._meshType is 'CYL':
|
||||||
if mesh.isSymmetric:
|
if mesh.isSymmetric:
|
||||||
MVP = MagneticDipoleVectorPotential(self.loc, mesh, 'Ey')
|
MVP = MagneticDipoleVectorPotential(self.loc, mesh, 'Ey')
|
||||||
@@ -94,12 +114,11 @@ class SrcTDEM_VMD_MVP(SrcTDEM):
|
|||||||
MVP = MagneticDipoleVectorPotential(self.loc, mesh, ['Ex','Ey','Ez'])
|
MVP = MagneticDipoleVectorPotential(self.loc, mesh, ['Ex','Ey','Ez'])
|
||||||
else:
|
else:
|
||||||
raise Exception('Unknown mesh for VMD')
|
raise Exception('Unknown mesh for VMD')
|
||||||
|
return mesh.edgeCurl.T*MfMui*mesh.edgeCurl*MVP
|
||||||
return {"b": mesh.edgeCurl*MVP}
|
|
||||||
|
|
||||||
|
|
||||||
class SrcTDEM_CircularLoop_MVP(SrcTDEM):
|
class SrcTDEM_CircularLoop_MVP(SrcTDEM):
|
||||||
def __init__(self,rxList,loc,radius,waveformType):
|
def __init__(self,rxList,loc,radius,waveformType="STEPOFF"):
|
||||||
self.loc = loc
|
self.loc = loc
|
||||||
self.radius = radius
|
self.radius = radius
|
||||||
self.waveformType = waveformType
|
self.waveformType = waveformType
|
||||||
|
|||||||
@@ -0,0 +1,129 @@
|
|||||||
|
import SimPEG as simpeg
|
||||||
|
import numpy as np
|
||||||
|
import SimPEG.MT as MT
|
||||||
|
from scipy.constants import mu_0
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
def run(plotIt=True):
|
||||||
|
"""
|
||||||
|
MT: 1D: Inversion
|
||||||
|
=======================
|
||||||
|
|
||||||
|
Forward model 1D MT data.
|
||||||
|
Setup and run a MT 1D inversion.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
## Setup the forward modeling
|
||||||
|
# Setting up 1D mesh and conductivity models to forward model data.
|
||||||
|
# Frequency
|
||||||
|
nFreq = 31
|
||||||
|
freqs = np.logspace(3,-3,nFreq)
|
||||||
|
# Set mesh parameters
|
||||||
|
ct = 20
|
||||||
|
air = simpeg.Utils.meshTensor([(ct,16,1.4)])
|
||||||
|
core = np.concatenate( ( np.kron(simpeg.Utils.meshTensor([(ct,10,-1.3)]),np.ones((5,))) , simpeg.Utils.meshTensor([(ct,5)]) ) )
|
||||||
|
bot = simpeg.Utils.meshTensor([(core[0],10,-1.4)])
|
||||||
|
x0 = -np.array([np.sum(np.concatenate((core,bot)))])
|
||||||
|
# Make the model
|
||||||
|
m1d = simpeg.Mesh.TensorMesh([np.concatenate((bot,core,air))], x0=x0)
|
||||||
|
|
||||||
|
# Setup model varibles
|
||||||
|
active = m1d.vectorCCx<0.
|
||||||
|
layer1 = (m1d.vectorCCx<-500.) & (m1d.vectorCCx>=-800.)
|
||||||
|
layer2 = (m1d.vectorCCx<-3500.) & (m1d.vectorCCx>=-5000.)
|
||||||
|
# Set the conductivity values
|
||||||
|
sig_half = 2e-3
|
||||||
|
sig_air = 1e-8
|
||||||
|
sig_layer1 = .2
|
||||||
|
sig_layer2 = .2
|
||||||
|
# Make the true model
|
||||||
|
sigma_true = np.ones(m1d.nCx)*sig_air
|
||||||
|
sigma_true[active] = sig_half
|
||||||
|
sigma_true[layer1] = sig_layer1
|
||||||
|
sigma_true[layer2] = sig_layer2
|
||||||
|
# Extract the model
|
||||||
|
m_true = np.log(sigma_true[active])
|
||||||
|
# Make the background model
|
||||||
|
sigma_0 = np.ones(m1d.nCx)*sig_air
|
||||||
|
sigma_0[active] = sig_half
|
||||||
|
m_0 = np.log(sigma_0[active])
|
||||||
|
|
||||||
|
# Set the mapping
|
||||||
|
actMap = simpeg.Maps.ActiveCells(m1d, active, np.log(1e-8), nC=m1d.nCx)
|
||||||
|
mappingExpAct = simpeg.Maps.ExpMap(m1d) * actMap
|
||||||
|
|
||||||
|
## Setup the layout of the survey, set the sources and the connected receivers
|
||||||
|
# Receivers
|
||||||
|
rxList = []
|
||||||
|
for rxType in ['z1dr','z1di']:
|
||||||
|
rxList.append(MT.Rx(simpeg.mkvc(np.array([0.0]),2).T,rxType))
|
||||||
|
# Source list
|
||||||
|
srcList =[]
|
||||||
|
for freq in freqs:
|
||||||
|
srcList.append(MT.SrcMT.polxy_1Dprimary(rxList,freq))
|
||||||
|
# Make the survey
|
||||||
|
survey = MT.Survey(srcList)
|
||||||
|
survey.mtrue = m_true
|
||||||
|
|
||||||
|
## Set the problem
|
||||||
|
problem = MT.Problem1D.eForm_psField(m1d,sigmaPrimary=sigma_0,mapping=mappingExpAct)
|
||||||
|
problem.pair(survey)
|
||||||
|
|
||||||
|
## Forward model data
|
||||||
|
# Project the data
|
||||||
|
survey.dtrue = survey.dpred(m_true)
|
||||||
|
survey.dobs = survey.dtrue + 0.025*abs(survey.dtrue)*np.random.randn(*survey.dtrue.shape)
|
||||||
|
|
||||||
|
if plotIt:
|
||||||
|
fig = MT.Utils.dataUtils.plotMT1DModelData(problem)
|
||||||
|
fig.suptitle('Target - smooth true')
|
||||||
|
|
||||||
|
|
||||||
|
# Assign uncertainties
|
||||||
|
std = 0.05 # 5% std
|
||||||
|
survey.std = np.abs(survey.dobs*std)
|
||||||
|
# Assign the data weight
|
||||||
|
Wd = 1./survey.std
|
||||||
|
|
||||||
|
## Setup the inversion proceedure
|
||||||
|
# Define a counter
|
||||||
|
C = simpeg.Utils.Counter()
|
||||||
|
# Set the optimization
|
||||||
|
opt = simpeg.Optimization.InexactGaussNewton(maxIter = 30)
|
||||||
|
opt.counter = C
|
||||||
|
opt.LSshorten = 0.5
|
||||||
|
opt.remember('xc')
|
||||||
|
# Data misfit
|
||||||
|
dmis = simpeg.DataMisfit.l2_DataMisfit(survey)
|
||||||
|
dmis.Wd = Wd
|
||||||
|
# Regularization - with a regularization mesh
|
||||||
|
regMesh = simpeg.Mesh.TensorMesh([m1d.hx[problem.mapping.sigmaMap.maps[-1].indActive]],m1d.x0)
|
||||||
|
reg = simpeg.Regularization.Tikhonov(regMesh)
|
||||||
|
reg.smoothModel = True
|
||||||
|
reg.alpha_s = 1e-7
|
||||||
|
reg.alpha_x = 1.
|
||||||
|
# Inversion problem
|
||||||
|
invProb = simpeg.InvProblem.BaseInvProblem(dmis, reg, opt)
|
||||||
|
invProb.counter = C
|
||||||
|
# Beta cooling
|
||||||
|
beta = simpeg.Directives.BetaSchedule()
|
||||||
|
beta.coolingRate = 4
|
||||||
|
betaest = simpeg.Directives.BetaEstimate_ByEig(beta0_ratio=0.75)
|
||||||
|
targmis = simpeg.Directives.TargetMisfit()
|
||||||
|
targmis.target = survey.nD
|
||||||
|
saveModel = simpeg.Directives.SaveModelEveryIteration()
|
||||||
|
saveModel.fileName = 'Inversion_TargMisEqnD_smoothTrue'
|
||||||
|
# Create an inversion object
|
||||||
|
inv = simpeg.Inversion.BaseInversion(invProb, directiveList=[beta,betaest,targmis])
|
||||||
|
|
||||||
|
## Run the inversion
|
||||||
|
mopt = inv.run(m_0)
|
||||||
|
|
||||||
|
if plotIt:
|
||||||
|
fig = MT.Utils.dataUtils.plotMT1DModelData(problem,[mopt])
|
||||||
|
fig.suptitle('Target - smooth true')
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
run()
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
# Test script to use SimPEG.MT platform to forward model synthetic data.
|
||||||
|
|
||||||
|
# Import
|
||||||
|
import SimPEG as simpeg
|
||||||
|
from SimPEG import MT
|
||||||
|
import numpy as np
|
||||||
|
try:
|
||||||
|
from pymatsolver import MumpsSolver as Solver
|
||||||
|
except:
|
||||||
|
from SimPEG import Solver
|
||||||
|
|
||||||
|
def run(plotIt=True, nFreq=1):
|
||||||
|
"""
|
||||||
|
MT: 3D: Forward
|
||||||
|
=======================
|
||||||
|
|
||||||
|
Forward model 3D MT data.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Make a mesh
|
||||||
|
M = simpeg.Mesh.TensorMesh([[(100,5,-1.5),(100.,10),(100,5,1.5)],[(100,5,-1.5),(100.,10),(100,5,1.5)],[(100,5,1.6),(100.,10),(100,3,2)]], x0=['C','C',-3529.5360])
|
||||||
|
# Setup the model
|
||||||
|
conds = [1e-2,1]
|
||||||
|
sig = simpeg.Utils.ModelBuilder.defineBlock(M.gridCC,[-1000,-1000,-400],[1000,1000,-200],conds)
|
||||||
|
sig[M.gridCC[:,2]>0] = 1e-8
|
||||||
|
sig[M.gridCC[:,2]<-600] = 1e-1
|
||||||
|
sigBG = np.zeros(M.nC) + conds[0]
|
||||||
|
sigBG[M.gridCC[:,2]>0] = 1e-8
|
||||||
|
|
||||||
|
## Setup the the survey object
|
||||||
|
# Receiver locations
|
||||||
|
rx_x, rx_y = np.meshgrid(np.arange(-500,501,50),np.arange(-500,501,50))
|
||||||
|
rx_loc = np.hstack((simpeg.Utils.mkvc(rx_x,2),simpeg.Utils.mkvc(rx_y,2),np.zeros((np.prod(rx_x.shape),1))))
|
||||||
|
# Make a receiver list
|
||||||
|
rxList = []
|
||||||
|
for loc in rx_loc:
|
||||||
|
# NOTE: loc has to be a (1,3) np.ndarray otherwise errors accure
|
||||||
|
for rxType in ['zxxr','zxxi','zxyr','zxyi','zyxr','zyxi','zyyr','zyyi','tzxr','tzxi','tzyr','tzyi']:
|
||||||
|
rxList.append(MT.Rx(simpeg.mkvc(loc,2).T,rxType))
|
||||||
|
# Source list
|
||||||
|
srcList =[]
|
||||||
|
for freq in np.logspace(3,-3,nFreq):
|
||||||
|
srcList.append(MT.SrcMT.polxy_1Dprimary(rxList,freq))
|
||||||
|
# Survey MT
|
||||||
|
survey = MT.Survey(srcList)
|
||||||
|
|
||||||
|
## Setup the problem object
|
||||||
|
problem = MT.Problem3D.eForm_ps(M, sigmaPrimary=sigBG)
|
||||||
|
problem.pair(survey)
|
||||||
|
problem.Solver = Solver
|
||||||
|
|
||||||
|
# Calculate the data
|
||||||
|
fields = problem.fields(sig)
|
||||||
|
dataVec = survey.projectFields(fields)
|
||||||
|
|
||||||
|
# Make the data
|
||||||
|
mtData = MT.Data(survey,dataVec)
|
||||||
|
# Add plots
|
||||||
|
if plotIt:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
run()
|
||||||
@@ -16,8 +16,10 @@ import Mesh_QuadTree_Creation
|
|||||||
import Mesh_QuadTree_FaceDiv
|
import Mesh_QuadTree_FaceDiv
|
||||||
import Mesh_QuadTree_HangingNodes
|
import Mesh_QuadTree_HangingNodes
|
||||||
import Mesh_Tensor_Creation
|
import Mesh_Tensor_Creation
|
||||||
|
import MT_1D_ForwardAndInversion
|
||||||
|
import MT_3D_Foward
|
||||||
|
|
||||||
__examples__ = ["DC_Analytic_Dipole", "DC_Forward_PseudoSection", "EM_FDEM_1D_Inversion", "EM_FDEM_Analytic_MagDipoleWholespace", "EM_TDEM_1D_Inversion", "FLOW_Richards_1D_Celia1990", "Forward_BasicDirectCurrent", "Inversion_Linear", "Mesh_Basic_PlotImage", "Mesh_Basic_Types", "Mesh_Operators_CahnHilliard", "Mesh_QuadTree_Creation", "Mesh_QuadTree_FaceDiv", "Mesh_QuadTree_HangingNodes", "Mesh_Tensor_Creation"]
|
__examples__ = ["DC_Analytic_Dipole", "DC_Forward_PseudoSection", "EM_FDEM_1D_Inversion", "EM_FDEM_Analytic_MagDipoleWholespace", "EM_TDEM_1D_Inversion", "FLOW_Richards_1D_Celia1990", "Forward_BasicDirectCurrent", "Inversion_Linear", "Mesh_Basic_PlotImage", "Mesh_Basic_Types", "Mesh_Operators_CahnHilliard", "Mesh_QuadTree_Creation", "Mesh_QuadTree_FaceDiv", "Mesh_QuadTree_HangingNodes", "Mesh_Tensor_Creation", "MT_1D_ForwardAndInversion", "MT_3D_Foward"]
|
||||||
|
|
||||||
##### AUTOIMPORTS #####
|
##### AUTOIMPORTS #####
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,132 @@
|
|||||||
|
from SimPEG import SolverLU as SimpegSolver, PropMaps, Utils, mkvc, sp, np
|
||||||
|
from SimPEG.EM.FDEM.FDEM import BaseFDEMProblem
|
||||||
|
from SurveyMT import Survey, Data
|
||||||
|
from FieldsMT import BaseMTFields
|
||||||
|
|
||||||
|
|
||||||
|
class BaseMTProblem(BaseFDEMProblem):
|
||||||
|
"""
|
||||||
|
Base class for all Natural source problems.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, mesh, **kwargs):
|
||||||
|
BaseFDEMProblem.__init__(self, mesh, **kwargs)
|
||||||
|
Utils.setKwargs(self, **kwargs)
|
||||||
|
# Set the default pairs of the problem
|
||||||
|
surveyPair = Survey
|
||||||
|
dataPair = Data
|
||||||
|
fieldsPair = BaseMTFields
|
||||||
|
|
||||||
|
# Set the solver
|
||||||
|
Solver = SimpegSolver
|
||||||
|
solverOpts = {}
|
||||||
|
|
||||||
|
verbose = False
|
||||||
|
# Notes:
|
||||||
|
# Use the forward and devs from BaseFDEMProblem
|
||||||
|
# Might need to add more stuff here.
|
||||||
|
|
||||||
|
## NEED to clean up the Jvec and Jtvec to use Zero and Identities for None components.
|
||||||
|
def Jvec(self, m, v, u=None):
|
||||||
|
"""
|
||||||
|
Function to calculate the data sensitivities dD/dm times a vector.
|
||||||
|
|
||||||
|
:param numpy.ndarray m (nC, 1) - conductive model
|
||||||
|
:param numpy.ndarray v (nC, 1) - random vector
|
||||||
|
:param MTfields object (optional) - MT fields object, if not given it is calculated
|
||||||
|
:rtype: MTdata object
|
||||||
|
:return: Data sensitivities wrt m
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Calculate the fields
|
||||||
|
if u is None:
|
||||||
|
u = self.fields(m)
|
||||||
|
# Set current model
|
||||||
|
self.curModel = m
|
||||||
|
# Initiate the Jv object
|
||||||
|
Jv = self.dataPair(self.survey)
|
||||||
|
|
||||||
|
# Loop all the frequenies
|
||||||
|
for freq in self.survey.freqs:
|
||||||
|
dA_du = self.getA(freq) #
|
||||||
|
|
||||||
|
dA_duI = self.Solver(dA_du, **self.solverOpts)
|
||||||
|
|
||||||
|
for src in self.survey.getSrcByFreq(freq):
|
||||||
|
# We need fDeriv_m = df/du*du/dm + df/dm
|
||||||
|
# Construct du/dm, it requires a solve
|
||||||
|
# NOTE: need to account for the 2 polarizations in the derivatives.
|
||||||
|
u_src = u[src,:]
|
||||||
|
# dA_dm and dRHS_dm should be of size nE,2, so that we can multiply by dA_duI. The 2 columns are each of the polarizations.
|
||||||
|
dA_dm = self.getADeriv_m(freq, u_src, v) # Size: nE,2 (u_px,u_py) in the columns.
|
||||||
|
dRHS_dm = self.getRHSDeriv_m(freq, v) # Size: nE,2 (u_px,u_py) in the columns.
|
||||||
|
if dRHS_dm is None:
|
||||||
|
du_dm = dA_duI * ( -dA_dm )
|
||||||
|
else:
|
||||||
|
du_dm = dA_duI * ( -dA_dm + dRHS_dm )
|
||||||
|
# Calculate the projection derivatives
|
||||||
|
for rx in src.rxList:
|
||||||
|
# Get the projection derivative
|
||||||
|
# v should be of size 2*nE (for 2 polarizations)
|
||||||
|
PDeriv_u = lambda t: rx.projectFieldsDeriv(src, self.mesh, u, t) # wrt u, we don't have have PDeriv wrt m
|
||||||
|
Jv[src, rx] = PDeriv_u(mkvc(du_dm))
|
||||||
|
dA_duI.clean()
|
||||||
|
# Return the vectorized sensitivities
|
||||||
|
return mkvc(Jv)
|
||||||
|
|
||||||
|
def Jtvec(self, m, v, u=None):
|
||||||
|
"""
|
||||||
|
Function to calculate the transpose of the data sensitivities (dD/dm)^T times a vector.
|
||||||
|
|
||||||
|
:param numpy.ndarray m (nC, 1) - conductive model
|
||||||
|
:param numpy.ndarray v (nD, 1) - vector
|
||||||
|
:param MTfields object u (optional) - MT fields object, if not given it is calculated
|
||||||
|
:rtype: MTdata object
|
||||||
|
:return: Data sensitivities wrt m
|
||||||
|
"""
|
||||||
|
|
||||||
|
if u is None:
|
||||||
|
u = self.fields(m)
|
||||||
|
|
||||||
|
self.curModel = m
|
||||||
|
|
||||||
|
# Ensure v is a data object.
|
||||||
|
if not isinstance(v, self.dataPair):
|
||||||
|
v = self.dataPair(self.survey, v)
|
||||||
|
|
||||||
|
Jtv = np.zeros(m.size)
|
||||||
|
|
||||||
|
for freq in self.survey.freqs:
|
||||||
|
AT = self.getA(freq).T
|
||||||
|
|
||||||
|
ATinv = self.Solver(AT, **self.solverOpts)
|
||||||
|
|
||||||
|
for src in self.survey.getSrcByFreq(freq):
|
||||||
|
ftype = self._fieldType + 'Solution'
|
||||||
|
u_src = u[src, :]
|
||||||
|
|
||||||
|
for rx in src.rxList:
|
||||||
|
# Get the adjoint projectFieldsDeriv
|
||||||
|
# PTv needs to be nE,
|
||||||
|
PTv = rx.projectFieldsDeriv(src, self.mesh, u, mkvc(v[src, rx],2), adjoint=True) # wrt u, need possibility wrt m
|
||||||
|
# Get the
|
||||||
|
dA_duIT = ATinv * PTv
|
||||||
|
dA_dmT = self.getADeriv_m(freq, u_src, mkvc(dA_duIT), adjoint=True)
|
||||||
|
dRHS_dmT = self.getRHSDeriv_m(freq, mkvc(dA_duIT), adjoint=True)
|
||||||
|
# Make du_dmT
|
||||||
|
if dRHS_dmT is None:
|
||||||
|
du_dmT = -dA_dmT
|
||||||
|
else:
|
||||||
|
du_dmT = -dA_dmT + dRHS_dmT
|
||||||
|
# Select the correct component
|
||||||
|
# du_dmT needs to be of size nC,
|
||||||
|
real_or_imag = rx.projComp
|
||||||
|
if real_or_imag == 'real':
|
||||||
|
Jtv += du_dmT.real
|
||||||
|
elif real_or_imag == 'imag':
|
||||||
|
Jtv += -du_dmT.real
|
||||||
|
else:
|
||||||
|
raise Exception('Must be real or imag')
|
||||||
|
# Clean the factorization, clear memory.
|
||||||
|
ATinv.clean()
|
||||||
|
return Jtv
|
||||||
@@ -0,0 +1,351 @@
|
|||||||
|
from SimPEG import Survey, Utils, Problem, np, sp, mkvc
|
||||||
|
from scipy.constants import mu_0
|
||||||
|
import sys
|
||||||
|
from numpy.lib import recfunctions as recFunc
|
||||||
|
from SimPEG.EM.Utils import omega
|
||||||
|
|
||||||
|
##############
|
||||||
|
### Fields ###
|
||||||
|
##############
|
||||||
|
class BaseMTFields(Problem.Fields):
|
||||||
|
"""Field Storage for a MT survey."""
|
||||||
|
knownFields = {}
|
||||||
|
dtype = complex
|
||||||
|
|
||||||
|
|
||||||
|
class Fields1D_e(BaseMTFields):
|
||||||
|
"""
|
||||||
|
Fields storage for the 1D MT solution.
|
||||||
|
"""
|
||||||
|
knownFields = {'e_1dSolution':'F'}
|
||||||
|
aliasFields = {
|
||||||
|
'e_1d' : ['e_1dSolution','F','_e'],
|
||||||
|
'e_1dPrimary' : ['e_1dSolution','F','_ePrimary'],
|
||||||
|
'e_1dSecondary' : ['e_1dSolution','F','_eSecondary'],
|
||||||
|
'b_1d' : ['e_1dSolution','E','_b'],
|
||||||
|
'b_1dPrimary' : ['e_1dSolution','E','_bPrimary'],
|
||||||
|
'b_1dSecondary' : ['e_1dSolution','E','_bSecondary']
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self,mesh,survey,**kwargs):
|
||||||
|
BaseMTFields.__init__(self,mesh,survey,**kwargs)
|
||||||
|
|
||||||
|
def _ePrimary(self, eSolution, srcList):
|
||||||
|
ePrimary = np.zeros_like(eSolution)
|
||||||
|
for i, src in enumerate(srcList):
|
||||||
|
ep = src.ePrimary(self.survey.prob)
|
||||||
|
if ep is not None:
|
||||||
|
ePrimary[:,i] = ep[:,-1]
|
||||||
|
return ePrimary
|
||||||
|
|
||||||
|
def _eSecondary(self, eSolution, srcList):
|
||||||
|
return eSolution
|
||||||
|
|
||||||
|
def _e(self, eSolution, srcList):
|
||||||
|
return self._ePrimary(eSolution,srcList) + self._eSecondary(eSolution,srcList)
|
||||||
|
|
||||||
|
def _eDeriv_u(self, src, v, adjoint = False):
|
||||||
|
return v
|
||||||
|
|
||||||
|
def _eDeriv_m(self, src, v, adjoint = False):
|
||||||
|
# assuming primary does not depend on the model
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _bPrimary(self, eSolution, srcList):
|
||||||
|
bPrimary = np.zeros([self.survey.mesh.nE,eSolution.shape[1]], dtype = complex)
|
||||||
|
for i, src in enumerate(srcList):
|
||||||
|
bp = src.bPrimary(self.survey.prob)
|
||||||
|
if bp is not None:
|
||||||
|
bPrimary[:,i] += bp[:,-1]
|
||||||
|
return bPrimary
|
||||||
|
|
||||||
|
def _bSecondary(self, eSolution, srcList):
|
||||||
|
C = self.mesh.nodalGrad
|
||||||
|
b = (C * eSolution)
|
||||||
|
for i, src in enumerate(srcList):
|
||||||
|
b[:,i] *= - 1./(1j*omega(src.freq))
|
||||||
|
# There is no magnetic source in the MT problem
|
||||||
|
# S_m, _ = src.eval(self.survey.prob)
|
||||||
|
# if S_m is not None:
|
||||||
|
# b[:,i] += 1./(1j*omega(src.freq)) * S_m
|
||||||
|
return b
|
||||||
|
|
||||||
|
def _b(self, eSolution, srcList):
|
||||||
|
return self._bPrimary(eSolution, srcList) + self._bSecondary(eSolution, srcList)
|
||||||
|
|
||||||
|
def _bSecondaryDeriv_u(self, src, v, adjoint = False):
|
||||||
|
C = self.mesh.nodalGrad
|
||||||
|
if adjoint:
|
||||||
|
return - 1./(1j*omega(src.freq)) * (C.T * v)
|
||||||
|
return - 1./(1j*omega(src.freq)) * (C * v)
|
||||||
|
|
||||||
|
def _bSecondaryDeriv_m(self, src, v, adjoint = False):
|
||||||
|
# Doesn't depend on m
|
||||||
|
# _, S_eDeriv = src.evalDeriv(self.survey.prob, adjoint)
|
||||||
|
# S_eDeriv = S_eDeriv(v)
|
||||||
|
# if S_eDeriv is not None:
|
||||||
|
# return 1./(1j * omega(src.freq)) * S_eDeriv
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _bDeriv_u(self, src, v, adjoint=False):
|
||||||
|
# Primary does not depend on u
|
||||||
|
return self._bSecondaryDeriv_u(src, v, adjoint)
|
||||||
|
|
||||||
|
def _bDeriv_m(self, src, v, adjoint=False):
|
||||||
|
# Assuming the primary does not depend on the model
|
||||||
|
return self._bSecondaryDeriv_m(src, v, adjoint)
|
||||||
|
|
||||||
|
def _fDeriv_u(self, src, v, adjoint=False):
|
||||||
|
"""
|
||||||
|
Derivative of the fields object wrt u.
|
||||||
|
|
||||||
|
:param MTsrc src: MT source
|
||||||
|
:param numpy.ndarray v: random vector of f_sol.size
|
||||||
|
This function stacks the fields derivatives appropriately
|
||||||
|
|
||||||
|
return a vector of size (nreEle+nrbEle)
|
||||||
|
"""
|
||||||
|
|
||||||
|
de_du = v #Utils.spdiag(np.ones((self.nF,)))
|
||||||
|
db_du = self._bDeriv_u(src, v, adjoint)
|
||||||
|
# Return the stack
|
||||||
|
# This doesn't work...
|
||||||
|
return np.vstack((de_du,db_du))
|
||||||
|
|
||||||
|
def _fDeriv_m(self, src, v, adjoint=False):
|
||||||
|
"""
|
||||||
|
Derivative of the fields object wrt m.
|
||||||
|
|
||||||
|
This function stacks the fields derivatives appropriately
|
||||||
|
"""
|
||||||
|
return None
|
||||||
|
|
||||||
|
class Fields3D_e(BaseMTFields):
|
||||||
|
"""
|
||||||
|
Fields storage for the 3D MT solution. Labels polarizations by px and py.
|
||||||
|
|
||||||
|
:param SimPEG object mesh: The solution mesh
|
||||||
|
:param SimPEG object survey: A survey object
|
||||||
|
"""
|
||||||
|
# Define the known the alias fields
|
||||||
|
# Assume that the solution of e on the E.
|
||||||
|
## NOTE: Need to make this more general, to allow for other solutions formats.
|
||||||
|
knownFields = {'e_pxSolution':'E','e_pySolution':'E'}
|
||||||
|
aliasFields = {
|
||||||
|
'e_px' : ['e_pxSolution','E','_e_px'],
|
||||||
|
'e_pxPrimary' : ['e_pxSolution','E','_e_pxPrimary'],
|
||||||
|
'e_pxSecondary' : ['e_pxSolution','E','_e_pxSecondary'],
|
||||||
|
'e_py' : ['e_pySolution','E','_e_py'],
|
||||||
|
'e_pyPrimary' : ['e_pySolution','E','_e_pyPrimary'],
|
||||||
|
'e_pySecondary' : ['e_pySolution','E','_e_pySecondary'],
|
||||||
|
'b_px' : ['e_pxSolution','F','_b_px'],
|
||||||
|
'b_pxPrimary' : ['e_pxSolution','F','_b_pxPrimary'],
|
||||||
|
'b_pxSecondary' : ['e_pxSolution','F','_b_pxSecondary'],
|
||||||
|
'b_py' : ['e_pySolution','F','_b_py'],
|
||||||
|
'b_pyPrimary' : ['e_pySolution','F','_b_pyPrimary'],
|
||||||
|
'b_pySecondary' : ['e_pySolution','F','_b_pySecondary']
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self,mesh,survey,**kwargs):
|
||||||
|
BaseMTFields.__init__(self,mesh,survey,**kwargs)
|
||||||
|
|
||||||
|
def _e_pxPrimary(self, e_pxSolution, srcList):
|
||||||
|
e_pxPrimary = np.zeros_like(e_pxSolution)
|
||||||
|
for i, src in enumerate(srcList):
|
||||||
|
ep = src.ePrimary(self.survey.prob)
|
||||||
|
if ep is not None:
|
||||||
|
e_pxPrimary[:,i] = ep[:,0]
|
||||||
|
return e_pxPrimary
|
||||||
|
|
||||||
|
def _e_pyPrimary(self, e_pySolution, srcList):
|
||||||
|
e_pyPrimary = np.zeros_like(e_pySolution)
|
||||||
|
for i, src in enumerate(srcList):
|
||||||
|
ep = src.ePrimary(self.survey.prob)
|
||||||
|
if ep is not None:
|
||||||
|
e_pyPrimary[:,i] = ep[:,1]
|
||||||
|
return e_pyPrimary
|
||||||
|
|
||||||
|
def _e_pxSecondary(self, e_pxSolution, srcList):
|
||||||
|
return e_pxSolution
|
||||||
|
|
||||||
|
def _e_pySecondary(self, e_pySolution, srcList):
|
||||||
|
return e_pySolution
|
||||||
|
|
||||||
|
def _e_px(self, e_pxSolution, srcList):
|
||||||
|
return self._e_pxPrimary(e_pxSolution,srcList) + self._e_pxSecondary(e_pxSolution,srcList)
|
||||||
|
|
||||||
|
def _e_py(self, e_pySolution, srcList):
|
||||||
|
return self._e_pyPrimary(e_pySolution,srcList) + self._e_pySecondary(e_pySolution,srcList)
|
||||||
|
|
||||||
|
#NOTE: For e_p?Deriv_u,
|
||||||
|
# v has to be u(2*nE) long for the not adjoint and nE long for adjoint.
|
||||||
|
# Returns nE long for not adjoint and 2*nE long for adjoint
|
||||||
|
def _e_pxDeriv_u(self, src, v, adjoint = False):
|
||||||
|
'''
|
||||||
|
Takes the derivative of e_px wrt u
|
||||||
|
'''
|
||||||
|
if adjoint:
|
||||||
|
# adjoint: returns a 2*nE long vector with zero's for py
|
||||||
|
return np.vstack((v,np.zeros_like(v)))
|
||||||
|
# Not adjoint: return only the px part of the vector
|
||||||
|
return v[:len(v)/2]
|
||||||
|
|
||||||
|
def _e_pyDeriv_u(self, src, v, adjoint = False):
|
||||||
|
'''
|
||||||
|
Takes the derivative of e_py wrt u
|
||||||
|
'''
|
||||||
|
if adjoint:
|
||||||
|
# adjoint: returns a 2*nE long vector with zero's for px
|
||||||
|
return np.vstack((np.zeros_like(v),v))
|
||||||
|
# Not adjoint: return only the px part of the vector
|
||||||
|
return v[len(v)/2::]
|
||||||
|
|
||||||
|
def _e_pxDeriv_m(self, src, v, adjoint = False):
|
||||||
|
# assuming primary does not depend on the model
|
||||||
|
return None
|
||||||
|
def _e_pyDeriv_m(self, src, v, adjoint = False):
|
||||||
|
# assuming primary does not depend on the model
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _b_pxPrimary(self, e_pxSolution, srcList):
|
||||||
|
b_pxPrimary = np.zeros([self.survey.mesh.nF,e_pxSolution.shape[1]], dtype = complex)
|
||||||
|
for i, src in enumerate(srcList):
|
||||||
|
bp = src.bPrimary(self.survey.prob)
|
||||||
|
if bp is not None:
|
||||||
|
b_pxPrimary[:,i] += bp[:,0]
|
||||||
|
return b_pxPrimary
|
||||||
|
|
||||||
|
def _b_pyPrimary(self, e_pySolution, srcList):
|
||||||
|
b_pyPrimary = np.zeros([self.survey.mesh.nF,e_pySolution.shape[1]], dtype = complex)
|
||||||
|
for i, src in enumerate(srcList):
|
||||||
|
bp = src.bPrimary(self.survey.prob)
|
||||||
|
if bp is not None:
|
||||||
|
b_pyPrimary[:,i] += bp[:,1]
|
||||||
|
return b_pyPrimary
|
||||||
|
|
||||||
|
def _b_pxSecondary(self, e_pxSolution, srcList):
|
||||||
|
C = self.mesh.edgeCurl
|
||||||
|
b = (C * e_pxSolution)
|
||||||
|
for i, src in enumerate(srcList):
|
||||||
|
b[:,i] *= - 1./(1j*omega(src.freq))
|
||||||
|
# There is no magnetic source in the MT problem
|
||||||
|
# S_m, _ = src.eval(self.survey.prob)
|
||||||
|
# if S_m is not None:
|
||||||
|
# b[:,i] += 1./(1j*omega(src.freq)) * S_m
|
||||||
|
return b
|
||||||
|
|
||||||
|
def _b_pySecondary(self, e_pySolution, srcList):
|
||||||
|
C = self.mesh.edgeCurl
|
||||||
|
b = (C * e_pySolution)
|
||||||
|
for i, src in enumerate(srcList):
|
||||||
|
b[:,i] *= - 1./(1j*omega(src.freq))
|
||||||
|
# There is no magnetic source in the MT problem
|
||||||
|
# S_m, _ = src.eval(self.survey.prob)
|
||||||
|
# if S_m is not None:
|
||||||
|
# b[:,i] += 1./(1j*omega(src.freq)) * S_m
|
||||||
|
return b
|
||||||
|
|
||||||
|
def _b_px(self, eSolution, srcList):
|
||||||
|
return self._b_pxPrimary(eSolution, srcList) + self._b_pxSecondary(eSolution, srcList)
|
||||||
|
|
||||||
|
def _b_py(self, eSolution, srcList):
|
||||||
|
return self._b_pyPrimary(eSolution, srcList) + self._b_pySecondary(eSolution, srcList)
|
||||||
|
|
||||||
|
# NOTE: v needs to be length 2*nE to account for both polarizations
|
||||||
|
def _b_pxSecondaryDeriv_u(self, src, v, adjoint = False):
|
||||||
|
# C = sp.kron(self.mesh.edgeCurl,[[1,0],[0,0]])
|
||||||
|
C = sp.hstack((self.mesh.edgeCurl,Utils.spzeros(self.mesh.nF,self.mesh.nE))) # This works for adjoint = None
|
||||||
|
if adjoint:
|
||||||
|
return - 1./(1j*omega(src.freq)) * (C.T * v)
|
||||||
|
return - 1./(1j*omega(src.freq)) * (C * v)
|
||||||
|
|
||||||
|
def _b_pySecondaryDeriv_u(self, src, v, adjoint = False):
|
||||||
|
# C = sp.kron(self.mesh.edgeCurl,[[0,0],[0,1]])
|
||||||
|
C = sp.hstack((Utils.spzeros(self.mesh.nF,self.mesh.nE),self.mesh.edgeCurl)) # This works for adjoint = None
|
||||||
|
if adjoint:
|
||||||
|
return - 1./(1j*omega(src.freq)) * (C.T * v)
|
||||||
|
return - 1./(1j*omega(src.freq)) * (C * v)
|
||||||
|
|
||||||
|
def _b_pxSecondaryDeriv_m(self, src, v, adjoint = False):
|
||||||
|
# Doesn't depend on m
|
||||||
|
# _, S_eDeriv = src.evalDeriv(self.survey.prob, adjoint)
|
||||||
|
# S_eDeriv = S_eDeriv(v)
|
||||||
|
# if S_eDeriv is not None:
|
||||||
|
# return 1./(1j * omega(src.freq)) * S_eDeriv
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _b_pySecondaryDeriv_m(self, src, v, adjoint = False):
|
||||||
|
# Doesn't depend on m
|
||||||
|
# _, S_eDeriv = src.evalDeriv(self.survey.prob, adjoint)
|
||||||
|
# S_eDeriv = S_eDeriv(v)
|
||||||
|
# if S_eDeriv is not None:
|
||||||
|
# return 1./(1j * omega(src.freq)) * S_eDeriv
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _b_pxDeriv_u(self, src, v, adjoint=False):
|
||||||
|
# Primary does not depend on u
|
||||||
|
return self._b_pxSecondaryDeriv_u(src, v, adjoint)
|
||||||
|
|
||||||
|
def _b_pyDeriv_u(self, src, v, adjoint=False):
|
||||||
|
# Primary does not depend on u
|
||||||
|
return self._b_pySecondaryDeriv_u(src, v, adjoint)
|
||||||
|
|
||||||
|
def _b_pxDeriv_m(self, src, v, adjoint=False):
|
||||||
|
# Assuming the primary does not depend on the model
|
||||||
|
return self._b_pxSecondaryDeriv_m(src, v, adjoint)
|
||||||
|
|
||||||
|
def _b_pyDeriv_m(self, src, v, adjoint=False):
|
||||||
|
# Assuming the primary does not depend on the model
|
||||||
|
return self._b_pySecondaryDeriv_m(src, v, adjoint)
|
||||||
|
|
||||||
|
def _f_pxDeriv_u(self, src, v, adjoint=False):
|
||||||
|
"""
|
||||||
|
Derivative of the fields object wrt u.
|
||||||
|
|
||||||
|
:param MTsrc src: MT source
|
||||||
|
:param numpy.ndarray v: random vector of f_sol.size
|
||||||
|
This function stacks the fields derivatives appropriately
|
||||||
|
|
||||||
|
return a vector of size (nreEle+nrbEle)
|
||||||
|
"""
|
||||||
|
|
||||||
|
de_du = v #Utils.spdiag(np.ones((self.nF,)))
|
||||||
|
db_du = self._b_pxDeriv_u(src, v, adjoint)
|
||||||
|
# Return the stack
|
||||||
|
# This doesn't work...
|
||||||
|
return np.vstack((de_du,db_du))
|
||||||
|
|
||||||
|
def _f_pyDeriv_u(self, src, v, adjoint=False):
|
||||||
|
"""
|
||||||
|
Derivative of the fields object wrt u.
|
||||||
|
|
||||||
|
:param MTsrc src: MT source
|
||||||
|
:param numpy.ndarray v: random vector of f_sol.size
|
||||||
|
This function stacks the fields derivatives appropriately
|
||||||
|
|
||||||
|
return a vector of size (nreEle+nrbEle)
|
||||||
|
"""
|
||||||
|
|
||||||
|
de_du = v #Utils.spdiag(np.ones((self.nF,)))
|
||||||
|
db_du = self._b_pyDeriv_u(src, v, adjoint)
|
||||||
|
# Return the stack
|
||||||
|
# This doesn't work...
|
||||||
|
return np.vstack((de_du,db_du))
|
||||||
|
|
||||||
|
def _f_pxDeriv_m(self, src, v, adjoint=False):
|
||||||
|
"""
|
||||||
|
Derivative of the fields object wrt m.
|
||||||
|
|
||||||
|
This function stacks the fields derivatives appropriately
|
||||||
|
"""
|
||||||
|
# The fields have no dependance to the model.
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _f_pyDeriv_m(self, src, v, adjoint=False):
|
||||||
|
"""
|
||||||
|
Derivative of the fields object wrt m.
|
||||||
|
|
||||||
|
This function stacks the fields derivatives appropriately
|
||||||
|
"""
|
||||||
|
# The fields have no dependance to the model.
|
||||||
|
return None
|
||||||
@@ -0,0 +1,291 @@
|
|||||||
|
from SimPEG.EM.Utils import omega
|
||||||
|
from SimPEG import mkvc
|
||||||
|
from scipy.constants import mu_0
|
||||||
|
from SimPEG.MT.BaseMT import BaseMTProblem
|
||||||
|
from SimPEG.MT.SurveyMT import Survey, Data
|
||||||
|
from SimPEG.MT.FieldsMT import Fields1D_e
|
||||||
|
from SimPEG.MT.Utils.MT1Danalytic import getEHfields
|
||||||
|
import numpy as np
|
||||||
|
import multiprocessing, sys, time
|
||||||
|
|
||||||
|
|
||||||
|
class eForm_psField(BaseMTProblem):
|
||||||
|
"""
|
||||||
|
A MT problem soving a e formulation and primary/secondary fields decomposion.
|
||||||
|
|
||||||
|
By eliminating the magnetic flux density using
|
||||||
|
|
||||||
|
.. math ::
|
||||||
|
|
||||||
|
\mathbf{b} = \\frac{1}{i \omega}\\left(-\mathbf{C} \mathbf{e} \\right)
|
||||||
|
|
||||||
|
|
||||||
|
we can write Maxwell's equations as a second order system in \\\(\\\mathbf{e}\\\) only:
|
||||||
|
|
||||||
|
.. math ::
|
||||||
|
\\left(\mathbf{C}^T \mathbf{M^e_{\mu^{-1}}} \mathbf{C} + i \omega \mathbf{M^f_\sigma}] \mathbf{e}_{s} =& i \omega \mathbf{M^f_{\delta \sigma}} \mathbf{e}_{p}
|
||||||
|
which we solve for \\\(\\\mathbf{e_s}\\\). The total field \\\mathbf{e}\\ = \\\mathbf{e_p}\\ + \\\mathbf{e_s}\\.
|
||||||
|
|
||||||
|
The primary field is estimated from a background model (commonly half space ).
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
# From FDEMproblem: Used to project the fields. Currently not used for MTproblem.
|
||||||
|
_fieldType = 'e_1d'
|
||||||
|
_eqLocs = 'EF'
|
||||||
|
_sigmaPrimary = None
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, mesh, **kwargs):
|
||||||
|
BaseMTProblem.__init__(self, mesh, **kwargs)
|
||||||
|
self.fieldsPair = Fields1D_e
|
||||||
|
# self._sigmaPrimary = sigmaPrimary
|
||||||
|
@property
|
||||||
|
def MeMui(self):
|
||||||
|
"""
|
||||||
|
Edge inner product matrix
|
||||||
|
"""
|
||||||
|
if getattr(self, '_MeMui', None) is None:
|
||||||
|
self._MeMui = self.mesh.getEdgeInnerProduct(1.0/mu_0)
|
||||||
|
return self._MeMui
|
||||||
|
|
||||||
|
@property
|
||||||
|
def MfSigma(self):
|
||||||
|
"""
|
||||||
|
Edge inner product matrix
|
||||||
|
"""
|
||||||
|
if getattr(self, '_MfSigma', None) is None:
|
||||||
|
self._MfSigma = self.mesh.getFaceInnerProduct(self.curModel.sigma)
|
||||||
|
return self._MfSigma
|
||||||
|
|
||||||
|
@property
|
||||||
|
def sigmaPrimary(self):
|
||||||
|
"""
|
||||||
|
A background model, use for the calculation of the primary fields.
|
||||||
|
|
||||||
|
"""
|
||||||
|
return self._sigmaPrimary
|
||||||
|
|
||||||
|
@sigmaPrimary.setter
|
||||||
|
def sigmaPrimary(self, val):
|
||||||
|
# Note: TODO add logic for val, make sure it is the correct size.
|
||||||
|
self._sigmaPrimary = val
|
||||||
|
|
||||||
|
def getA(self, freq):
|
||||||
|
"""
|
||||||
|
Function to get the A matrix.
|
||||||
|
|
||||||
|
:param float freq: Frequency
|
||||||
|
:rtype: scipy.sparse.csr_matrix
|
||||||
|
:return: A
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Note: need to use the code above since in the 1D problem I want
|
||||||
|
# e to live on Faces(nodes) and h on edges(cells). Might need to rethink this
|
||||||
|
# Possible that _fieldType and _eqLocs can fix this
|
||||||
|
MeMui = self.MeMui
|
||||||
|
MfSigma = self.MfSigma
|
||||||
|
C = self.mesh.nodalGrad
|
||||||
|
# Make A
|
||||||
|
A = C.T*MeMui*C + 1j*omega(freq)*MfSigma
|
||||||
|
# Either return full or only the inner part of A
|
||||||
|
return A
|
||||||
|
|
||||||
|
def getADeriv_m(self, freq, u, v, adjoint=False):
|
||||||
|
"""
|
||||||
|
The derivative of A wrt sigma
|
||||||
|
"""
|
||||||
|
|
||||||
|
dsig_dm = self.curModel.sigmaDeriv
|
||||||
|
MeMui = self.MeMui
|
||||||
|
#
|
||||||
|
u_src = u['e_1dSolution']
|
||||||
|
dMfSigma_dm = self.mesh.getFaceInnerProductDeriv(self.curModel.sigma)(u_src) * self.curModel.sigmaDeriv
|
||||||
|
if adjoint:
|
||||||
|
return 1j * omega(freq) * ( dMfSigma_dm.T * v )
|
||||||
|
# Note: output has to be nN/nF, not nC/nE.
|
||||||
|
# v should be nC
|
||||||
|
return 1j * omega(freq) * ( dMfSigma_dm * v )
|
||||||
|
|
||||||
|
def getRHS(self, freq):
|
||||||
|
"""
|
||||||
|
Function to return the right hand side for the system.
|
||||||
|
:param float freq: Frequency
|
||||||
|
:rtype: numpy.ndarray (nF, 1), numpy.ndarray (nF, 1)
|
||||||
|
:return: RHS for 1 polarizations, primary fields
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Get sources for the frequncy(polarizations)
|
||||||
|
Src = self.survey.getSrcByFreq(freq)[0]
|
||||||
|
S_e = Src.S_e(self)
|
||||||
|
return -1j * omega(freq) * S_e
|
||||||
|
|
||||||
|
def getRHSDeriv_m(self, freq, v, adjoint=False):
|
||||||
|
"""
|
||||||
|
The derivative of the RHS wrt sigma
|
||||||
|
"""
|
||||||
|
|
||||||
|
Src = self.survey.getSrcByFreq(freq)[0]
|
||||||
|
S_eDeriv = Src.S_eDeriv_m(self, v, adjoint)
|
||||||
|
return -1j * omega(freq) * S_eDeriv
|
||||||
|
|
||||||
|
def fields(self, m):
|
||||||
|
'''
|
||||||
|
Function to calculate all the fields for the model m.
|
||||||
|
|
||||||
|
:param np.ndarray (nC,) m: Conductivity model
|
||||||
|
'''
|
||||||
|
# Set the current model
|
||||||
|
self.curModel = m
|
||||||
|
|
||||||
|
F = Fields1D_e(self.mesh, self.survey)
|
||||||
|
for freq in self.survey.freqs:
|
||||||
|
if self.verbose:
|
||||||
|
startTime = time.time()
|
||||||
|
print 'Starting work for {:.3e}'.format(freq)
|
||||||
|
sys.stdout.flush()
|
||||||
|
A = self.getA(freq)
|
||||||
|
rhs = self.getRHS(freq)
|
||||||
|
Ainv = self.Solver(A, **self.solverOpts)
|
||||||
|
e_s = Ainv * rhs
|
||||||
|
|
||||||
|
# Store the fields
|
||||||
|
Src = self.survey.getSrcByFreq(freq)[0]
|
||||||
|
# NOTE: only store the e_solution(secondary), all other components calculated in the fields object
|
||||||
|
F[Src, 'e_1dSolution'] = e_s[:,-1] # Only storing the yx polarization as 1d
|
||||||
|
|
||||||
|
# Note curl e = -iwb so b = -curl e /iw
|
||||||
|
# b = -( self.mesh.nodalGrad * e )/( 1j*omega(freq) )
|
||||||
|
# F[Src, 'b_1d'] = b[:,1]
|
||||||
|
if self.verbose:
|
||||||
|
print 'Ran for {:f} seconds'.format(time.time()-startTime)
|
||||||
|
sys.stdout.flush()
|
||||||
|
return F
|
||||||
|
|
||||||
|
# Note this is not fully functional.
|
||||||
|
# Missing:
|
||||||
|
# Fields class corresponding to the fields
|
||||||
|
# Update Jvec and Jtvec to include all the derivatives components
|
||||||
|
# Other things ...
|
||||||
|
class eForm_TotalField(BaseMTProblem):
|
||||||
|
"""
|
||||||
|
A MT problem solving a e formulation and a Total bondary domain decompostion.
|
||||||
|
|
||||||
|
Solves the equation:
|
||||||
|
|
||||||
|
Math:
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# From FDEMproblem: Used to project the fields. Currently not used for MTproblem.
|
||||||
|
_fieldType = 'e'
|
||||||
|
_eqLocs = 'EF'
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, mesh, **kwargs):
|
||||||
|
BaseMTProblem.__init__(self, mesh, **kwargs)
|
||||||
|
@property
|
||||||
|
def MeMui(self):
|
||||||
|
"""
|
||||||
|
Edge inner product matrix
|
||||||
|
"""
|
||||||
|
if getattr(self, '_MeMui', None) is None:
|
||||||
|
self._MeMui = self.mesh.getEdgeInnerProduct(1.0/mu_0)
|
||||||
|
return self._MeMui
|
||||||
|
|
||||||
|
@property
|
||||||
|
def MfSigma(self):
|
||||||
|
"""
|
||||||
|
Edge inner product matrix
|
||||||
|
"""
|
||||||
|
if getattr(self, '_MfSigma', None) is None:
|
||||||
|
self._MfSigma = self.mesh.getFaceInnerProduct(self.curModel.sigma)
|
||||||
|
return self._MfSigma
|
||||||
|
|
||||||
|
def getA(self, freq, full=False):
|
||||||
|
"""
|
||||||
|
Function to get the A matrix.
|
||||||
|
|
||||||
|
:param float freq: Frequency
|
||||||
|
:param logic full: Return full A or the inner part
|
||||||
|
:rtype: scipy.sparse.csr_matrix
|
||||||
|
:return: A
|
||||||
|
"""
|
||||||
|
|
||||||
|
MeMui = self.MeMui
|
||||||
|
MfSigma = self.MfSigma
|
||||||
|
# Note: need to use the code above since in the 1D problem I want
|
||||||
|
# e to live on Faces(nodes) and h on edges(cells). Might need to rethink this
|
||||||
|
# Possible that _fieldType and _eqLocs can fix this
|
||||||
|
# MeMui = self.MfMui
|
||||||
|
# MfSigma = self.MfSigma
|
||||||
|
C = self.mesh.nodalGrad
|
||||||
|
# Make A
|
||||||
|
A = C.T*MeMui*C + 1j*omega(freq)*MfSigma
|
||||||
|
# Either return full or only the inner part of A
|
||||||
|
if full:
|
||||||
|
return A
|
||||||
|
else:
|
||||||
|
return A[1:-1,1:-1]
|
||||||
|
|
||||||
|
def getADeriv_m(self, freq, u, v, adjoint=False):
|
||||||
|
raise NotImplementedError('getADeriv is not implemented')
|
||||||
|
|
||||||
|
def getRHS(self, freq):
|
||||||
|
"""
|
||||||
|
Function to return the right hand side for the system.
|
||||||
|
:param float freq: Frequency
|
||||||
|
:rtype: numpy.ndarray (nE, 2), numpy.ndarray (nE, 2)
|
||||||
|
:return: RHS for both polarizations, primary fields
|
||||||
|
"""
|
||||||
|
# Get sources for the frequency
|
||||||
|
# NOTE: Need to use the source information, doesn't really apply in 1D
|
||||||
|
src = self.survey.getSrcByFreq(freq)
|
||||||
|
# Get the full A
|
||||||
|
A = self.getA(freq,full=True)
|
||||||
|
# Define the outer part of the solution matrix
|
||||||
|
Aio = A[1:-1,[0,-1]]
|
||||||
|
Ed, Eu, Hd, Hu = getEHfields(self.mesh,self.curModel.sigma,freq,self.mesh.vectorNx)
|
||||||
|
Etot = (Ed + Eu)
|
||||||
|
sourceAmp = 1.0
|
||||||
|
Etot = ((Etot/Etot[-1])*sourceAmp) # Scale the fields to be equal to sourceAmp at the top
|
||||||
|
## Note: The analytic solution is derived with e^iwt
|
||||||
|
eBC = np.r_[Etot[0],Etot[-1]]
|
||||||
|
# The right hand side
|
||||||
|
|
||||||
|
return -Aio*eBC, eBC
|
||||||
|
|
||||||
|
def getRHSderiv_m(self, freq, backSigma, u, v, adjoint=False):
|
||||||
|
raise NotImplementedError('getRHSDeriv not implemented yet')
|
||||||
|
return None
|
||||||
|
|
||||||
|
def fields(self, m):
|
||||||
|
'''
|
||||||
|
Function to calculate all the fields for the model m.
|
||||||
|
|
||||||
|
:param np.ndarray (nC,) m: Conductivity model
|
||||||
|
:param np.ndarray (nC,) m_back: Background conductivity model
|
||||||
|
'''
|
||||||
|
self.curModel = m
|
||||||
|
# RHS, CalcFields = self.getRHS(freq,m_back), self.calcFields
|
||||||
|
|
||||||
|
F = Fields1D_e(self.mesh, self.survey)
|
||||||
|
for freq in self.survey.freqs:
|
||||||
|
if self.verbose:
|
||||||
|
startTime = time.time()
|
||||||
|
print 'Starting work for {:.3e}'.format(freq)
|
||||||
|
sys.stdout.flush()
|
||||||
|
A = self.getA(freq)
|
||||||
|
rhs, e_o = self.getRHS(freq)
|
||||||
|
Ainv = self.Solver(A, **self.solverOpts)
|
||||||
|
e_i = Ainv * rhs
|
||||||
|
e = mkvc(np.r_[e_o[0], e_i, e_o[1]],2)
|
||||||
|
# Store the fields
|
||||||
|
Src = self.survey.getSrcByFreq(freq)
|
||||||
|
# NOTE: only store e fields
|
||||||
|
F[Src, 'e_1dSolution'] = e[:,0]
|
||||||
|
if self.verbose:
|
||||||
|
print 'Ran for {:f} seconds'.format(time.time()-startTime)
|
||||||
|
sys.stdout.flush()
|
||||||
|
return F
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
from Probs import eForm_TotalField, eForm_psField
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pass
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
from SimPEG import Survey, Problem, Utils, Models, np, sp, mkvc, SolverLU as SimpegSolver
|
||||||
|
from SimPEG.EM.Utils import omega
|
||||||
|
from scipy.constants import mu_0
|
||||||
|
from SimPEG.MT.BaseMT import BaseMTProblem
|
||||||
|
from SimPEG.MT.SurveyMT import Survey, Data
|
||||||
|
from SimPEG.MT.FieldsMT import Fields3D_e
|
||||||
|
import multiprocessing, sys, time
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class eForm_ps(BaseMTProblem):
|
||||||
|
"""
|
||||||
|
A MT problem solving a e formulation and a primary/secondary fields decompostion.
|
||||||
|
|
||||||
|
By eliminating the magnetic flux density using
|
||||||
|
|
||||||
|
.. math ::
|
||||||
|
|
||||||
|
\mathbf{b} = \\frac{1}{i \omega}\\left(-\mathbf{C} \mathbf{e} \\right)
|
||||||
|
|
||||||
|
|
||||||
|
we can write Maxwell's equations as a second order system in \\\(\\\mathbf{e}\\\) only:
|
||||||
|
|
||||||
|
.. math ::
|
||||||
|
\\left(\mathbf{C}^T \mathbf{M^f_{\mu^{-1}}} \mathbf{C} + i \omega \mathbf{M^e_\sigma}] \mathbf{e}_{s} =& i \omega \mathbf{M^e_{\delta \sigma}} \mathbf{e}_{p}
|
||||||
|
which we solve for \\\(\\\mathbf{e_s}\\\). The total field \\\mathbf{e}\\ = \\\mathbf{e_p}\\ + \\\mathbf{e_s}\\.
|
||||||
|
|
||||||
|
The primary field is estimated from a background model (commonly as a 1D model).
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# From FDEMproblem: Used to project the fields. Currently not used for MTproblem.
|
||||||
|
_fieldType = 'e'
|
||||||
|
_eqLocs = 'FE'
|
||||||
|
fieldsPair = Fields3D_e
|
||||||
|
_sigmaPrimary = None
|
||||||
|
|
||||||
|
def __init__(self, mesh, **kwargs):
|
||||||
|
BaseMTProblem.__init__(self, mesh, **kwargs)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def sigmaPrimary(self):
|
||||||
|
"""
|
||||||
|
A background model, use for the calculation of the primary fields.
|
||||||
|
|
||||||
|
"""
|
||||||
|
return self._sigmaPrimary
|
||||||
|
@sigmaPrimary.setter
|
||||||
|
def sigmaPrimary(self, val):
|
||||||
|
# Note: TODO add logic for val, make sure it is the correct size.
|
||||||
|
self._sigmaPrimary = val
|
||||||
|
|
||||||
|
def getA(self, freq):
|
||||||
|
"""
|
||||||
|
Function to get the A system.
|
||||||
|
|
||||||
|
:param float freq: Frequency
|
||||||
|
:rtype: scipy.sparse.csr_matrix
|
||||||
|
:return: A
|
||||||
|
"""
|
||||||
|
Mmui = self.MfMui
|
||||||
|
Msig = self.MeSigma
|
||||||
|
C = self.mesh.edgeCurl
|
||||||
|
|
||||||
|
return C.T*Mmui*C + 1j*omega(freq)*Msig
|
||||||
|
|
||||||
|
def getADeriv_m(self, freq, u, v, adjoint=False):
|
||||||
|
"""
|
||||||
|
Calculate the derivative of A wrt m.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# This considers both polarizations and returns a nE,2 matrix for each polarization
|
||||||
|
if adjoint:
|
||||||
|
dMe_dsigV = sp.hstack(( self.MeSigmaDeriv( u['e_pxSolution'] ).T, self.MeSigmaDeriv(u['e_pySolution'] ).T ))*v
|
||||||
|
else:
|
||||||
|
# Need a nE,2 matrix to be returned
|
||||||
|
dMe_dsigV = np.hstack(( mkvc(self.MeSigmaDeriv( u['e_pxSolution'] )*v,2), mkvc( self.MeSigmaDeriv(u['e_pySolution'] )*v,2) ))
|
||||||
|
return 1j * omega(freq) * dMe_dsigV
|
||||||
|
|
||||||
|
|
||||||
|
def getRHS(self, freq):
|
||||||
|
"""
|
||||||
|
Function to return the right hand side for the system.
|
||||||
|
|
||||||
|
:param float freq: Frequency
|
||||||
|
:rtype: numpy.ndarray (nE, 2), numpy.ndarray (nE, 2)
|
||||||
|
:return: RHS for both polarizations, primary fields
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Get sources for the frequncy(polarizations)
|
||||||
|
Src = self.survey.getSrcByFreq(freq)[0]
|
||||||
|
S_e = Src.S_e(self)
|
||||||
|
return -1j * omega(freq) * S_e
|
||||||
|
|
||||||
|
def getRHSDeriv_m(self, freq, v, adjoint=False):
|
||||||
|
"""
|
||||||
|
The derivative of the RHS with respect to sigma
|
||||||
|
"""
|
||||||
|
|
||||||
|
Src = self.survey.getSrcByFreq(freq)[0]
|
||||||
|
S_eDeriv = Src.S_eDeriv_m(self, v, adjoint)
|
||||||
|
return -1j * omega(freq) * S_eDeriv
|
||||||
|
|
||||||
|
def fields(self, m):
|
||||||
|
'''
|
||||||
|
Function to calculate all the fields for the model m.
|
||||||
|
|
||||||
|
:param np.ndarray (nC,) m: Conductivity model
|
||||||
|
'''
|
||||||
|
# Set the current model
|
||||||
|
self.curModel = m
|
||||||
|
|
||||||
|
F = Fields3D_e(self.mesh, self.survey)
|
||||||
|
for freq in self.survey.freqs:
|
||||||
|
if self.verbose:
|
||||||
|
startTime = time.time()
|
||||||
|
print 'Starting work for {:.3e}'.format(freq)
|
||||||
|
sys.stdout.flush()
|
||||||
|
A = self.getA(freq)
|
||||||
|
rhs = self.getRHS(freq)
|
||||||
|
# Solve the system
|
||||||
|
Ainv = self.Solver(A, **self.solverOpts)
|
||||||
|
e_s = Ainv * rhs
|
||||||
|
|
||||||
|
# Store the fields
|
||||||
|
Src = self.survey.getSrcByFreq(freq)[0]
|
||||||
|
# Store the fieldss
|
||||||
|
F[Src, 'e_pxSolution'] = e_s[:,0]
|
||||||
|
F[Src, 'e_pySolution'] = e_s[:,1]
|
||||||
|
# Note curl e = -iwb so b = -curl/iw
|
||||||
|
|
||||||
|
if self.verbose:
|
||||||
|
print 'Ran for {:f} seconds'.format(time.time()-startTime)
|
||||||
|
sys.stdout.flush()
|
||||||
|
Ainv.clean()
|
||||||
|
return F
|
||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
from Probs import eForm_ps
|
||||||
@@ -0,0 +1,206 @@
|
|||||||
|
from SimPEG import Utils, Problem, Maps, np, sp, mkvc
|
||||||
|
from SimPEG.EM.FDEM.SrcFDEM import BaseSrc as FDEMBaseSrc
|
||||||
|
from SimPEG.EM.Utils import omega
|
||||||
|
from scipy.constants import mu_0
|
||||||
|
from numpy.lib import recfunctions as recFunc
|
||||||
|
from Utils.sourceUtils import homo1DModelSource
|
||||||
|
from Utils import rec2ndarr
|
||||||
|
import sys
|
||||||
|
|
||||||
|
#################
|
||||||
|
### Sources ###
|
||||||
|
#################
|
||||||
|
|
||||||
|
class BaseMTSrc(FDEMBaseSrc):
|
||||||
|
'''
|
||||||
|
Sources for the MT problem.
|
||||||
|
Use the SimPEG BaseSrc, since the source fields share properties with the transmitters.
|
||||||
|
|
||||||
|
:param float freq: The frequency of the source
|
||||||
|
:param list rxList: A list of receivers associated with the source
|
||||||
|
'''
|
||||||
|
|
||||||
|
freq = None #: Frequency (float)
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, rxList, freq):
|
||||||
|
|
||||||
|
self.freq = float(freq)
|
||||||
|
FDEMBaseSrc.__init__(self, rxList)
|
||||||
|
|
||||||
|
# 1D sources
|
||||||
|
class polxy_1DhomotD(BaseMTSrc):
|
||||||
|
"""
|
||||||
|
MT source for both polarizations (x and y) for the total Domain.
|
||||||
|
|
||||||
|
It calculates fields calculated based on conditions on the boundary of the domain.
|
||||||
|
"""
|
||||||
|
def __init__(self, rxList, freq):
|
||||||
|
BaseMTSrc.__init__(self, rxList, freq)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: need to add the primary fields calc and source terms into the problem.
|
||||||
|
|
||||||
|
# Need to implement such that it works for all dims.
|
||||||
|
class polxy_1Dprimary(BaseMTSrc):
|
||||||
|
"""
|
||||||
|
MT source for both polarizations (x and y) given a 1D primary models.
|
||||||
|
It assigns fields calculated from the 1D model as fields in the full space of the problem.
|
||||||
|
"""
|
||||||
|
def __init__(self, rxList, freq):
|
||||||
|
# assert mkvc(self.mesh.hz.shape,1) == mkvc(sigma1d.shape,1),'The number of values in the 1D background model does not match the number of vertical cells (hz).'
|
||||||
|
self.sigma1d = None
|
||||||
|
BaseMTSrc.__init__(self, rxList, freq)
|
||||||
|
# Hidden property of the ePrimary
|
||||||
|
self._ePrimary = None
|
||||||
|
|
||||||
|
def ePrimary(self,problem):
|
||||||
|
# Get primary fields for both polarizations
|
||||||
|
if self.sigma1d is None:
|
||||||
|
# Set the sigma1d as the 1st column in the background model
|
||||||
|
if len(problem._sigmaPrimary) == problem.mesh.nC:
|
||||||
|
if problem.mesh.dim == 1:
|
||||||
|
self.sigma1d = problem.mesh.r(problem._sigmaPrimary,'CC','CC','M')[:]
|
||||||
|
elif problem.mesh.dim == 3:
|
||||||
|
self.sigma1d = problem.mesh.r(problem._sigmaPrimary,'CC','CC','M')[0,0,:]
|
||||||
|
# Or as the 1D model that matches the vertical cell number
|
||||||
|
elif len(problem._sigmaPrimary) == problem.mesh.nCz:
|
||||||
|
self.sigma1d = problem._sigmaPrimary
|
||||||
|
|
||||||
|
if self._ePrimary is None:
|
||||||
|
self._ePrimary = homo1DModelSource(problem.mesh,self.freq,self.sigma1d)
|
||||||
|
return self._ePrimary
|
||||||
|
|
||||||
|
def bPrimary(self,problem):
|
||||||
|
# Project ePrimary to bPrimary
|
||||||
|
# Satisfies the primary(background) field conditions
|
||||||
|
if problem.mesh.dim == 1:
|
||||||
|
C = problem.mesh.nodalGrad
|
||||||
|
elif problem.mesh.dim == 3:
|
||||||
|
C = problem.mesh.edgeCurl
|
||||||
|
bBG_bp = (- C * self.ePrimary(problem) )*(1/( 1j*omega(self.freq) ))
|
||||||
|
return bBG_bp
|
||||||
|
|
||||||
|
def S_e(self,problem):
|
||||||
|
"""
|
||||||
|
Get the electrical field source
|
||||||
|
"""
|
||||||
|
e_p = self.ePrimary(problem)
|
||||||
|
Map_sigma_p = Maps.Vertical1DMap(problem.mesh)
|
||||||
|
sigma_p = Map_sigma_p._transform(self.sigma1d)
|
||||||
|
# Make mass matrix
|
||||||
|
# Note: M(sig) - M(sig_p) = M(sig - sig_p)
|
||||||
|
# Need to deal with the edge/face discrepencies between 1d/2d/3d
|
||||||
|
if problem.mesh.dim == 1:
|
||||||
|
Mesigma = problem.mesh.getFaceInnerProduct(problem.curModel.sigma)
|
||||||
|
Mesigma_p = problem.mesh.getFaceInnerProduct(sigma_p)
|
||||||
|
if problem.mesh.dim == 2:
|
||||||
|
pass
|
||||||
|
if problem.mesh.dim == 3:
|
||||||
|
Mesigma = problem.MeSigma
|
||||||
|
Mesigma_p = problem.mesh.getEdgeInnerProduct(sigma_p)
|
||||||
|
return (Mesigma - Mesigma_p) * e_p
|
||||||
|
|
||||||
|
def S_eDeriv_m(self, problem, v, adjoint = False):
|
||||||
|
'''
|
||||||
|
Get the derivative of S_e wrt to sigma (m)
|
||||||
|
'''
|
||||||
|
# Need to deal with
|
||||||
|
if problem.mesh.dim == 1:
|
||||||
|
# Need to use the faceInnerProduct
|
||||||
|
MsigmaDeriv = problem.mesh.getFaceInnerProductDeriv(problem.curModel.sigma)(self.ePrimary(problem)[:,1]) * problem.curModel.sigmaDeriv
|
||||||
|
# MsigmaDeriv = ( MsigmaDeriv * MsigmaDeriv.T)**2
|
||||||
|
if problem.mesh.dim == 2:
|
||||||
|
pass
|
||||||
|
if problem.mesh.dim == 3:
|
||||||
|
# Need to take the derivative of both u_px and u_py
|
||||||
|
ePri = self.ePrimary(problem)
|
||||||
|
# MsigmaDeriv = problem.MeSigmaDeriv(ePri[:,0]) + problem.MeSigmaDeriv(ePri[:,1])
|
||||||
|
# MsigmaDeriv = problem.MeSigmaDeriv(np.sum(ePri,axis=1))
|
||||||
|
if adjoint:
|
||||||
|
return sp.hstack(( problem.MeSigmaDeriv(ePri[:,0]).T, problem.MeSigmaDeriv(ePri[:,1]).T ))*v
|
||||||
|
else:
|
||||||
|
return np.hstack(( mkvc(problem.MeSigmaDeriv(ePri[:,0]) * v,2), mkvc(problem.MeSigmaDeriv(ePri[:,1])*v,2) ))
|
||||||
|
if adjoint:
|
||||||
|
#
|
||||||
|
return MsigmaDeriv.T * v
|
||||||
|
else:
|
||||||
|
# v should be nC size
|
||||||
|
return MsigmaDeriv * v
|
||||||
|
|
||||||
|
class polxy_3Dprimary(BaseMTSrc):
|
||||||
|
"""
|
||||||
|
MT source for both polarizations (x and y) given a 3D primary model. It assigns fields calculated from the 1D model
|
||||||
|
as fields in the full space of the problem.
|
||||||
|
"""
|
||||||
|
def __init__(self, rxList, freq):
|
||||||
|
# assert mkvc(self.mesh.hz.shape,1) == mkvc(sigma1d.shape,1),'The number of values in the 1D background model does not match the number of vertical cells (hz).'
|
||||||
|
self.sigmaPrimary = None
|
||||||
|
BaseMTSrc.__init__(self, rxList, freq)
|
||||||
|
# Hidden property of the ePrimary
|
||||||
|
self._ePrimary = None
|
||||||
|
|
||||||
|
def ePrimary(self,problem):
|
||||||
|
# Get primary fields for both polarizations
|
||||||
|
self.sigmaPrimary = problem._sigmaPrimary
|
||||||
|
|
||||||
|
if self._ePrimary is None:
|
||||||
|
self._ePrimary = homo3DModelSource(problem.mesh,self.sigmaPrimary,self.freq)
|
||||||
|
return self._ePrimary
|
||||||
|
|
||||||
|
def bPrimary(self,problem):
|
||||||
|
# Project ePrimary to bPrimary
|
||||||
|
# Satisfies the primary(background) field conditions
|
||||||
|
if problem.mesh.dim == 1:
|
||||||
|
C = problem.mesh.nodalGrad
|
||||||
|
elif problem.mesh.dim == 3:
|
||||||
|
C = problem.mesh.edgeCurl
|
||||||
|
bBG_bp = (- C * self.ePrimary(problem) )*(1/( 1j*omega(self.freq) ))
|
||||||
|
return bBG_bp
|
||||||
|
|
||||||
|
def S_e(self,problem):
|
||||||
|
"""
|
||||||
|
Get the electrical field source
|
||||||
|
"""
|
||||||
|
e_p = self.ePrimary(problem)
|
||||||
|
Map_sigma_p = Maps.Vertical1DMap(problem.mesh)
|
||||||
|
sigma_p = Map_sigma_p._transform(self.sigma1d)
|
||||||
|
# Make mass matrix
|
||||||
|
# Note: M(sig) - M(sig_p) = M(sig - sig_p)
|
||||||
|
# Need to deal with the edge/face discrepencies between 1d/2d/3d
|
||||||
|
if problem.mesh.dim == 1:
|
||||||
|
Mesigma = problem.mesh.getFaceInnerProduct(problem.curModel.sigma)
|
||||||
|
Mesigma_p = problem.mesh.getFaceInnerProduct(sigma_p)
|
||||||
|
if problem.mesh.dim == 2:
|
||||||
|
pass
|
||||||
|
if problem.mesh.dim == 3:
|
||||||
|
Mesigma = problem.MeSigma
|
||||||
|
Mesigma_p = problem.mesh.getEdgeInnerProduct(sigma_p)
|
||||||
|
return (Mesigma - Mesigma_p) * e_p
|
||||||
|
|
||||||
|
def S_eDeriv_m(self, problem, v, adjoint = False):
|
||||||
|
'''
|
||||||
|
Get the derivative of S_e wrt to sigma (m)
|
||||||
|
'''
|
||||||
|
# Need to deal with
|
||||||
|
if problem.mesh.dim == 1:
|
||||||
|
# Need to use the faceInnerProduct
|
||||||
|
MsigmaDeriv = problem.mesh.getFaceInnerProductDeriv(problem.curModel.sigma)(self.ePrimary(problem)[:,1]) * problem.curModel.sigmaDeriv
|
||||||
|
# MsigmaDeriv = ( MsigmaDeriv * MsigmaDeriv.T)**2
|
||||||
|
if problem.mesh.dim == 2:
|
||||||
|
pass
|
||||||
|
if problem.mesh.dim == 3:
|
||||||
|
# Need to take the derivative of both u_px and u_py
|
||||||
|
ePri = self.ePrimary(problem)
|
||||||
|
# MsigmaDeriv = problem.MeSigmaDeriv(ePri[:,0]) + problem.MeSigmaDeriv(ePri[:,1])
|
||||||
|
# MsigmaDeriv = problem.MeSigmaDeriv(np.sum(ePri,axis=1))
|
||||||
|
if adjoint:
|
||||||
|
return sp.hstack(( problem.MeSigmaDeriv(ePri[:,0]).T, problem.MeSigmaDeriv(ePri[:,1]).T ))*v
|
||||||
|
else:
|
||||||
|
return np.hstack(( mkvc(problem.MeSigmaDeriv(ePri[:,0]) * v,2), mkvc(problem.MeSigmaDeriv(ePri[:,1])*v,2) ))
|
||||||
|
if adjoint:
|
||||||
|
#
|
||||||
|
return MsigmaDeriv.T * v
|
||||||
|
else:
|
||||||
|
# v should be nC size
|
||||||
|
return MsigmaDeriv * v
|
||||||
@@ -0,0 +1,562 @@
|
|||||||
|
from SimPEG import Survey as SimPEGsurvey, Utils, Problem, Maps, np, sp, mkvc
|
||||||
|
from SimPEG.EM.FDEM.SrcFDEM import BaseSrc as FDEMBaseSrc
|
||||||
|
from SimPEG.EM.Utils import omega
|
||||||
|
from scipy.constants import mu_0
|
||||||
|
from numpy.lib import recfunctions as recFunc
|
||||||
|
from Utils import rec2ndarr
|
||||||
|
import SrcMT
|
||||||
|
import sys
|
||||||
|
|
||||||
|
#################
|
||||||
|
### Receivers ###
|
||||||
|
#################
|
||||||
|
class Rx(SimPEGsurvey.BaseRx):
|
||||||
|
"""
|
||||||
|
Class that defines natural source receivers.
|
||||||
|
|
||||||
|
See knownRxTypes for types of allowed receivers.
|
||||||
|
|
||||||
|
:param ndArray locs: Locations of the receivers
|
||||||
|
:param str rxType: The type of receiver
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
knownRxTypes = {
|
||||||
|
# 3D impedance
|
||||||
|
'zxxr':['Z3D', 'real'],
|
||||||
|
'zxyr':['Z3D', 'real'],
|
||||||
|
'zyxr':['Z3D', 'real'],
|
||||||
|
'zyyr':['Z3D', 'real'],
|
||||||
|
'zxxi':['Z3D', 'imag'],
|
||||||
|
'zxyi':['Z3D', 'imag'],
|
||||||
|
'zyxi':['Z3D', 'imag'],
|
||||||
|
'zyyi':['Z3D', 'imag'],
|
||||||
|
# 2D impedance
|
||||||
|
# TODO:
|
||||||
|
# 1D impedance
|
||||||
|
'z1dr':['Z1D', 'real'],
|
||||||
|
'z1di':['Z1D', 'imag'],
|
||||||
|
# Tipper
|
||||||
|
'tzxr':['T3D','real'],
|
||||||
|
'tzxi':['T3D','imag'],
|
||||||
|
'tzyr':['T3D','real'],
|
||||||
|
'tzyi':['T3D','imag']
|
||||||
|
}
|
||||||
|
# TODO: Have locs as single or double coordinates for both or numerator and denominator separately, respectively.
|
||||||
|
def __init__(self, locs, rxType):
|
||||||
|
SimPEGsurvey.BaseRx.__init__(self, locs, rxType)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def projType(self):
|
||||||
|
"""
|
||||||
|
Receiver type for projection.
|
||||||
|
|
||||||
|
"""
|
||||||
|
return self.knownRxTypes[self.rxType][0]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def projComp(self):
|
||||||
|
"""Component projection (real/imag)"""
|
||||||
|
return self.knownRxTypes[self.rxType][1]
|
||||||
|
|
||||||
|
def projectFields(self, src, mesh, f):
|
||||||
|
'''
|
||||||
|
Project the fields to natural source data.
|
||||||
|
|
||||||
|
:param SrcMT src: The source of the fields to project
|
||||||
|
:param SimPEG.Mesh mesh:
|
||||||
|
:param FieldsMT f: Natural source fields object to project
|
||||||
|
'''
|
||||||
|
|
||||||
|
## NOTE: Assumes that e is on t
|
||||||
|
if self.projType is 'Z1D':
|
||||||
|
Pex = mesh.getInterpolationMat(self.locs[:,-1],'Fx')
|
||||||
|
Pbx = mesh.getInterpolationMat(self.locs[:,-1],'Ex')
|
||||||
|
ex = Pex*mkvc(f[src,'e_1d'],2)
|
||||||
|
bx = Pbx*mkvc(f[src,'b_1d'],2)/mu_0
|
||||||
|
# Note: Has a minus sign in front, to comply with quadrant calculations.
|
||||||
|
# Can be derived from zyx case for the 3D case.
|
||||||
|
f_part_complex = -ex/bx
|
||||||
|
# elif self.projType is 'Z2D':
|
||||||
|
elif self.projType is 'Z3D':
|
||||||
|
## NOTE: Assumes that e is on edges and b on the faces. Need to generalize that or use a prop of fields to determine that.
|
||||||
|
if self.locs.ndim == 3:
|
||||||
|
eFLocs = self.locs[:,:,0]
|
||||||
|
bFLocs = self.locs[:,:,1]
|
||||||
|
else:
|
||||||
|
eFLocs = self.locs
|
||||||
|
bFLocs = self.locs
|
||||||
|
# Get the projection
|
||||||
|
Pex = mesh.getInterpolationMat(eFLocs,'Ex')
|
||||||
|
Pey = mesh.getInterpolationMat(eFLocs,'Ey')
|
||||||
|
Pbx = mesh.getInterpolationMat(bFLocs,'Fx')
|
||||||
|
Pby = mesh.getInterpolationMat(bFLocs,'Fy')
|
||||||
|
# Get the fields at location
|
||||||
|
# px: x-polaration and py: y-polaration.
|
||||||
|
ex_px = Pex*f[src,'e_px']
|
||||||
|
ey_px = Pey*f[src,'e_px']
|
||||||
|
ex_py = Pex*f[src,'e_py']
|
||||||
|
ey_py = Pey*f[src,'e_py']
|
||||||
|
hx_px = Pbx*f[src,'b_px']/mu_0
|
||||||
|
hy_px = Pby*f[src,'b_px']/mu_0
|
||||||
|
hx_py = Pbx*f[src,'b_py']/mu_0
|
||||||
|
hy_py = Pby*f[src,'b_py']/mu_0
|
||||||
|
# Make the complex data
|
||||||
|
if 'zxx' in self.rxType:
|
||||||
|
f_part_complex = ( ex_px*hy_py - ex_py*hy_px)/(hx_px*hy_py - hx_py*hy_px)
|
||||||
|
elif 'zxy' in self.rxType:
|
||||||
|
f_part_complex = (-ex_px*hx_py + ex_py*hx_px)/(hx_px*hy_py - hx_py*hy_px)
|
||||||
|
elif 'zyx' in self.rxType:
|
||||||
|
f_part_complex = ( ey_px*hy_py - ey_py*hy_px)/(hx_px*hy_py - hx_py*hy_px)
|
||||||
|
elif 'zyy' in self.rxType:
|
||||||
|
f_part_complex = (-ey_px*hx_py + ey_py*hx_px)/(hx_px*hy_py - hx_py*hy_px)
|
||||||
|
elif self.projType is 'T3D':
|
||||||
|
if self.locs.ndim == 3:
|
||||||
|
horLoc = self.locs[:,:,0]
|
||||||
|
vertLoc = self.locs[:,:,1]
|
||||||
|
else:
|
||||||
|
horLoc = self.locs
|
||||||
|
vertLoc = self.locs
|
||||||
|
Pbx = mesh.getInterpolationMat(horLoc,'Fx')
|
||||||
|
Pby = mesh.getInterpolationMat(horLoc,'Fy')
|
||||||
|
Pbz = mesh.getInterpolationMat(vertLoc,'Fz')
|
||||||
|
bx_px = Pbx*f[src,'b_px']
|
||||||
|
by_px = Pby*f[src,'b_px']
|
||||||
|
bz_px = Pbz*f[src,'b_px']
|
||||||
|
bx_py = Pbx*f[src,'b_py']
|
||||||
|
by_py = Pby*f[src,'b_py']
|
||||||
|
bz_py = Pbz*f[src,'b_py']
|
||||||
|
if 'tzx' in self.rxType:
|
||||||
|
f_part_complex = (- by_px*bz_py + by_py*bz_px)/(bx_px*by_py - bx_py*by_px)
|
||||||
|
if 'tzy' in self.rxType:
|
||||||
|
f_part_complex = ( bx_px*bz_py - bx_py*bz_px)/(bx_px*by_py - bx_py*by_px)
|
||||||
|
|
||||||
|
else:
|
||||||
|
NotImplementedError('Projection of {:s} receiver type is not implemented.'.format(self.rxType))
|
||||||
|
# Get the real or imag component
|
||||||
|
real_or_imag = self.projComp
|
||||||
|
f_part = getattr(f_part_complex, real_or_imag)
|
||||||
|
# print f_part
|
||||||
|
return f_part
|
||||||
|
|
||||||
|
def projectFieldsDeriv(self, src, mesh, f, v, adjoint=False):
|
||||||
|
"""
|
||||||
|
The derivative of the projection wrt u
|
||||||
|
|
||||||
|
:param MTsrc src: MT source
|
||||||
|
:param TensorMesh mesh: Mesh defining the topology of the problem
|
||||||
|
:param MTfields f: MT fields object of the source
|
||||||
|
:param numpy.ndarray v: Random vector of size
|
||||||
|
"""
|
||||||
|
|
||||||
|
real_or_imag = self.projComp
|
||||||
|
|
||||||
|
if not adjoint:
|
||||||
|
if self.projType is 'Z1D':
|
||||||
|
Pex = mesh.getInterpolationMat(self.locs[:,-1],'Fx')
|
||||||
|
Pbx = mesh.getInterpolationMat(self.locs[:,-1],'Ex')
|
||||||
|
# ex = Pex*mkvc(f[src,'e_1d'],2)
|
||||||
|
# bx = Pbx*mkvc(f[src,'b_1d'],2)/mu_0
|
||||||
|
dP_de = -mkvc(Utils.sdiag(1./(Pbx*mkvc(f[src,'b_1d'],2)/mu_0))*(Pex*v),2)
|
||||||
|
dP_db = mkvc( Utils.sdiag(Pex*mkvc(f[src,'e_1d'],2))*(Utils.sdiag(1./(Pbx*mkvc(f[src,'b_1d'],2)/mu_0)).T*Utils.sdiag(1./(Pbx*mkvc(f[src,'b_1d'],2)/mu_0)))*(Pbx*f._bDeriv_u(src,v)/mu_0),2)
|
||||||
|
PDeriv_complex = np.sum(np.hstack((dP_de,dP_db)),1)
|
||||||
|
elif self.projType is 'Z2D':
|
||||||
|
raise NotImplementedError('Has not been implement for 2D impedance tensor')
|
||||||
|
elif self.projType is 'Z3D':
|
||||||
|
if self.locs.ndim == 3:
|
||||||
|
eFLocs = self.locs[:,:,0]
|
||||||
|
bFLocs = self.locs[:,:,1]
|
||||||
|
else:
|
||||||
|
eFLocs = self.locs
|
||||||
|
bFLocs = self.locs
|
||||||
|
# Get the projection
|
||||||
|
Pex = mesh.getInterpolationMat(eFLocs,'Ex')
|
||||||
|
Pey = mesh.getInterpolationMat(eFLocs,'Ey')
|
||||||
|
Pbx = mesh.getInterpolationMat(bFLocs,'Fx')
|
||||||
|
Pby = mesh.getInterpolationMat(bFLocs,'Fy')
|
||||||
|
# Get the fields at location
|
||||||
|
# px: x-polaration and py: y-polaration.
|
||||||
|
ex_px = Pex*f[src,'e_px']
|
||||||
|
ey_px = Pey*f[src,'e_px']
|
||||||
|
ex_py = Pex*f[src,'e_py']
|
||||||
|
ey_py = Pey*f[src,'e_py']
|
||||||
|
hx_px = Pbx*f[src,'b_px']/mu_0
|
||||||
|
hy_px = Pby*f[src,'b_px']/mu_0
|
||||||
|
hx_py = Pbx*f[src,'b_py']/mu_0
|
||||||
|
hy_py = Pby*f[src,'b_py']/mu_0
|
||||||
|
# Derivatives as lambda functions
|
||||||
|
# The size of the diratives should be nD,nU
|
||||||
|
ex_px_u = lambda vec: Pex*f._e_pxDeriv_u(src,vec)
|
||||||
|
ey_px_u = lambda vec: Pey*f._e_pxDeriv_u(src,vec)
|
||||||
|
ex_py_u = lambda vec: Pex*f._e_pyDeriv_u(src,vec)
|
||||||
|
ey_py_u = lambda vec: Pey*f._e_pyDeriv_u(src,vec)
|
||||||
|
# NOTE: Think b_p?Deriv_u should return a 2*nF size matrix
|
||||||
|
hx_px_u = lambda vec: Pbx*f._b_pxDeriv_u(src,vec)/mu_0
|
||||||
|
hy_px_u = lambda vec: Pby*f._b_pxDeriv_u(src,vec)/mu_0
|
||||||
|
hx_py_u = lambda vec: Pbx*f._b_pyDeriv_u(src,vec)/mu_0
|
||||||
|
hy_py_u = lambda vec: Pby*f._b_pyDeriv_u(src,vec)/mu_0
|
||||||
|
# Update the input vector
|
||||||
|
sDiag = lambda t: Utils.sdiag(mkvc(t,2))
|
||||||
|
# Define the components of the derivative
|
||||||
|
Hd = sDiag(1./(sDiag(hx_px)*hy_py - sDiag(hx_py)*hy_px))
|
||||||
|
Hd_uV = sDiag(hy_py)*hx_px_u(v) + sDiag(hx_px)*hy_py_u(v) - sDiag(hx_py)*hy_px_u(v) - sDiag(hy_px)*hx_py_u(v)
|
||||||
|
# Calculate components
|
||||||
|
if 'zxx' in self.rxType:
|
||||||
|
Zij = sDiag(Hd*( sDiag(ex_px)*hy_py - sDiag(ex_py)*hy_px ))
|
||||||
|
ZijN_uV = sDiag(hy_py)*ex_px_u(v) + sDiag(ex_px)*hy_py_u(v) - sDiag(ex_py)*hy_px_u(v) - sDiag(hy_px)*ex_py_u(v)
|
||||||
|
elif 'zxy' in self.rxType:
|
||||||
|
Zij = sDiag(Hd*(-sDiag(ex_px)*hx_py + sDiag(ex_py)*hx_px ))
|
||||||
|
ZijN_uV = -sDiag(hx_py)*ex_px_u(v) - sDiag(ex_px)*hx_py_u(v) + sDiag(ex_py)*hx_px_u(v) + sDiag(hx_px)*ex_py_u(v)
|
||||||
|
elif 'zyx' in self.rxType:
|
||||||
|
Zij = sDiag(Hd*( sDiag(ey_px)*hy_py - sDiag(ey_py)*hy_px ))
|
||||||
|
ZijN_uV = sDiag(hy_py)*ey_px_u(v) + sDiag(ey_px)*hy_py_u(v) - sDiag(ey_py)*hy_px_u(v) - sDiag(hy_px)*ey_py_u(v)
|
||||||
|
elif 'zyy' in self.rxType:
|
||||||
|
Zij = sDiag(Hd*(-sDiag(ey_px)*hx_py + sDiag(ey_py)*hx_px ))
|
||||||
|
ZijN_uV = -sDiag(hx_py)*ey_px_u(v) - sDiag(ey_px)*hx_py_u(v) + sDiag(ey_py)*hx_px_u(v) + sDiag(hx_px)*ey_py_u(v)
|
||||||
|
|
||||||
|
# Calculate the complex derivative
|
||||||
|
PDeriv_complex = Hd * (ZijN_uV - Zij * Hd_uV )
|
||||||
|
elif self.projType is 'T3D':
|
||||||
|
if self.locs.ndim == 3:
|
||||||
|
eFLocs = self.locs[:,:,0]
|
||||||
|
bFLocs = self.locs[:,:,1]
|
||||||
|
else:
|
||||||
|
eFLocs = self.locs
|
||||||
|
bFLocs = self.locs
|
||||||
|
# Get the projection
|
||||||
|
Pbx = mesh.getInterpolationMat(bFLocs,'Fx')
|
||||||
|
Pby = mesh.getInterpolationMat(bFLocs,'Fy')
|
||||||
|
Pbz = mesh.getInterpolationMat(bFLocs,'Fz')
|
||||||
|
|
||||||
|
# Get the fields at location
|
||||||
|
# px: x-polaration and py: y-polaration.
|
||||||
|
bx_px = Pbx*f[src,'b_px']
|
||||||
|
by_px = Pby*f[src,'b_px']
|
||||||
|
bz_px = Pbz*f[src,'b_px']
|
||||||
|
bx_py = Pbx*f[src,'b_py']
|
||||||
|
by_py = Pby*f[src,'b_py']
|
||||||
|
bz_py = Pbz*f[src,'b_py']
|
||||||
|
# Derivatives as lambda functions
|
||||||
|
# NOTE: Think b_p?Deriv_u should return a 2*nF size matrix
|
||||||
|
bx_px_u = lambda vec: Pbx*f._b_pxDeriv_u(src,vec)
|
||||||
|
by_px_u = lambda vec: Pby*f._b_pxDeriv_u(src,vec)
|
||||||
|
bz_px_u = lambda vec: Pbz*f._b_pxDeriv_u(src,vec)
|
||||||
|
bx_py_u = lambda vec: Pbx*f._b_pyDeriv_u(src,vec)
|
||||||
|
by_py_u = lambda vec: Pby*f._b_pyDeriv_u(src,vec)
|
||||||
|
bz_py_u = lambda vec: Pbz*f._b_pyDeriv_u(src,vec)
|
||||||
|
# Update the input vector
|
||||||
|
sDiag = lambda t: Utils.sdiag(mkvc(t,2))
|
||||||
|
# Define the components of the derivative
|
||||||
|
Hd = sDiag(1./(sDiag(bx_px)*by_py - sDiag(bx_py)*by_px))
|
||||||
|
Hd_uV = sDiag(by_py)*bx_px_u(v) + sDiag(bx_px)*by_py_u(v) - sDiag(bx_py)*by_px_u(v) - sDiag(by_px)*bx_py_u(v)
|
||||||
|
if 'tzx' in self.rxType:
|
||||||
|
Tij = sDiag(Hd*( - sDiag(by_px)*bz_py + sDiag(by_py)*bz_px ))
|
||||||
|
TijN_uV = -sDiag(by_px)*bz_py_u(v) - sDiag(bz_py)*by_px_u(v) + sDiag(by_py)*bz_px_u(v) + sDiag(bz_px)*by_py_u(v)
|
||||||
|
elif 'tzy' in self.rxType:
|
||||||
|
Tij = sDiag(Hd*( sDiag(bx_px)*bz_py - sDiag(bx_py)*bz_px ))
|
||||||
|
TijN_uV = sDiag(bz_py)*bx_px_u(v) + sDiag(bx_px)*bz_py_u(v) - sDiag(bx_py)*bz_px_u(v) - sDiag(bz_px)*bx_py_u(v)
|
||||||
|
# Calculate the complex derivative
|
||||||
|
PDeriv_complex = Hd * (TijN_uV - Tij * Hd_uV )
|
||||||
|
|
||||||
|
# Extract the real number for the real/imag components.
|
||||||
|
Pv = np.array(getattr(PDeriv_complex, real_or_imag))
|
||||||
|
elif adjoint:
|
||||||
|
# Note: The v vector is real and the return should be complex
|
||||||
|
if self.projType is 'Z1D':
|
||||||
|
Pex = mesh.getInterpolationMat(self.locs[:,-1],'Fx')
|
||||||
|
Pbx = mesh.getInterpolationMat(self.locs[:,-1],'Ex')
|
||||||
|
# ex = Pex*mkvc(f[src,'e_1d'],2)
|
||||||
|
# bx = Pbx*mkvc(f[src,'b_1d'],2)/mu_0
|
||||||
|
dP_deTv = -mkvc(Pex.T*Utils.sdiag(1./(Pbx*mkvc(f[src,'b_1d'],2)/mu_0)).T*v,2)
|
||||||
|
db_duv = Pbx.T/mu_0*Utils.sdiag(1./(Pbx*mkvc(f[src,'b_1d'],2)/mu_0))*(Utils.sdiag(1./(Pbx*mkvc(f[src,'b_1d'],2)/mu_0))).T*Utils.sdiag(Pex*mkvc(f[src,'e_1d'],2)).T*v
|
||||||
|
dP_dbTv = mkvc(f._bDeriv_u(src,db_duv,adjoint=True),2)
|
||||||
|
PDeriv_real = np.sum(np.hstack((dP_deTv,dP_dbTv)),1)
|
||||||
|
elif self.projType is 'Z2D':
|
||||||
|
raise NotImplementedError('Has not be implement for 2D impedance tensor')
|
||||||
|
elif self.projType is 'Z3D':
|
||||||
|
if self.locs.ndim == 3:
|
||||||
|
eFLocs = self.locs[:,:,0]
|
||||||
|
bFLocs = self.locs[:,:,1]
|
||||||
|
else:
|
||||||
|
eFLocs = self.locs
|
||||||
|
bFLocs = self.locs
|
||||||
|
# Get the projection
|
||||||
|
Pex = mesh.getInterpolationMat(eFLocs,'Ex')
|
||||||
|
Pey = mesh.getInterpolationMat(eFLocs,'Ey')
|
||||||
|
Pbx = mesh.getInterpolationMat(bFLocs,'Fx')
|
||||||
|
Pby = mesh.getInterpolationMat(bFLocs,'Fy')
|
||||||
|
# Get the fields at location
|
||||||
|
# px: x-polaration and py: y-polaration.
|
||||||
|
aex_px = mkvc(mkvc(f[src,'e_px'],2).T*Pex.T)
|
||||||
|
aey_px = mkvc(mkvc(f[src,'e_px'],2).T*Pey.T)
|
||||||
|
aex_py = mkvc(mkvc(f[src,'e_py'],2).T*Pex.T)
|
||||||
|
aey_py = mkvc(mkvc(f[src,'e_py'],2).T*Pey.T)
|
||||||
|
ahx_px = mkvc(mkvc(f[src,'b_px'],2).T/mu_0*Pbx.T)
|
||||||
|
ahy_px = mkvc(mkvc(f[src,'b_px'],2).T/mu_0*Pby.T)
|
||||||
|
ahx_py = mkvc(mkvc(f[src,'b_py'],2).T/mu_0*Pbx.T)
|
||||||
|
ahy_py = mkvc(mkvc(f[src,'b_py'],2).T/mu_0*Pby.T)
|
||||||
|
# Derivatives as lambda functions
|
||||||
|
aex_px_u = lambda vec: f._e_pxDeriv_u(src,Pex.T*vec,adjoint=True)
|
||||||
|
aey_px_u = lambda vec: f._e_pxDeriv_u(src,Pey.T*vec,adjoint=True)
|
||||||
|
aex_py_u = lambda vec: f._e_pyDeriv_u(src,Pex.T*vec,adjoint=True)
|
||||||
|
aey_py_u = lambda vec: f._e_pyDeriv_u(src,Pey.T*vec,adjoint=True)
|
||||||
|
ahx_px_u = lambda vec: f._b_pxDeriv_u(src,Pbx.T*vec,adjoint=True)/mu_0
|
||||||
|
ahy_px_u = lambda vec: f._b_pxDeriv_u(src,Pby.T*vec,adjoint=True)/mu_0
|
||||||
|
ahx_py_u = lambda vec: f._b_pyDeriv_u(src,Pbx.T*vec,adjoint=True)/mu_0
|
||||||
|
ahy_py_u = lambda vec: f._b_pyDeriv_u(src,Pby.T*vec,adjoint=True)/mu_0
|
||||||
|
|
||||||
|
# Update the input vector
|
||||||
|
# Define shortcuts
|
||||||
|
sDiag = lambda t: Utils.sdiag(mkvc(t,2))
|
||||||
|
sVec = lambda t: Utils.sp.csr_matrix(mkvc(t,2))
|
||||||
|
# Define the components of the derivative
|
||||||
|
aHd = sDiag(1./(sDiag(ahx_px)*ahy_py - sDiag(ahx_py)*ahy_px))
|
||||||
|
aHd_uV = lambda x: ahx_px_u(sDiag(ahy_py)*x) + ahx_px_u(sDiag(ahy_py)*x) - ahy_px_u(sDiag(ahx_py)*x) - ahx_py_u(sDiag(ahy_px)*x)
|
||||||
|
# Need to fix this to reflect the adjoint
|
||||||
|
if 'zxx' in self.rxType:
|
||||||
|
Zij = sDiag(aHd*( sDiag(ahy_py)*aex_px - sDiag(ahy_px)*aex_py))
|
||||||
|
ZijN_uV = lambda x: aex_px_u(sDiag(ahy_py)*x) + ahy_py_u(sDiag(aex_px)*x) - ahy_px_u(sDiag(aex_py)*x) - aex_py_u(sDiag(ahy_px)*x)
|
||||||
|
elif 'zxy' in self.rxType:
|
||||||
|
Zij = sDiag(aHd*(-sDiag(ahx_py)*aex_px + sDiag(ahx_px)*aex_py))
|
||||||
|
ZijN_uV = lambda x:-aex_px_u(sDiag(ahx_py)*x) - ahx_py_u(sDiag(aex_px)*x) + ahx_px_u(sDiag(aex_py)*x) + aex_py_u(sDiag(ahx_px)*x)
|
||||||
|
elif 'zyx' in self.rxType:
|
||||||
|
Zij = sDiag(aHd*( sDiag(ahy_py)*aey_px - sDiag(ahy_px)*aey_py))
|
||||||
|
ZijN_uV = lambda x: aey_px_u(sDiag(ahy_py)*x) + ahy_py_u(sDiag(aey_px)*x) - ahy_px_u(sDiag(aey_py)*x) - aey_py_u(sDiag(ahy_px)*x)
|
||||||
|
elif 'zyy' in self.rxType:
|
||||||
|
Zij = sDiag(aHd*(-sDiag(ahx_py)*aey_px + sDiag(ahx_px)*aey_py))
|
||||||
|
ZijN_uV = lambda x:-aey_px_u(sDiag(ahx_py)*x) - ahx_py_u(sDiag(aey_px)*x) + ahx_px_u(sDiag(aey_py)*x) + aey_py_u(sDiag(ahx_px)*x)
|
||||||
|
|
||||||
|
# Calculate the complex derivative
|
||||||
|
PDeriv_real = ZijN_uV(aHd*v) - aHd_uV(Zij.T*aHd*v)#
|
||||||
|
# NOTE: Need to reshape the output to go from 2*nU array to a (nU,2) matrix for each polarization
|
||||||
|
# PDeriv_real = np.hstack((mkvc(PDeriv_real[:len(PDeriv_real)/2],2),mkvc(PDeriv_real[len(PDeriv_real)/2::],2)))
|
||||||
|
PDeriv_real = PDeriv_real.reshape((2,mesh.nE)).T
|
||||||
|
|
||||||
|
elif self.projType is 'T3D':
|
||||||
|
if self.locs.ndim == 3:
|
||||||
|
bFLocs = self.locs[:,:,1]
|
||||||
|
else:
|
||||||
|
bFLocs = self.locs
|
||||||
|
# Get the projection
|
||||||
|
Pbx = mesh.getInterpolationMat(bFLocs,'Fx')
|
||||||
|
Pby = mesh.getInterpolationMat(bFLocs,'Fy')
|
||||||
|
Pbz = mesh.getInterpolationMat(bFLocs,'Fz')
|
||||||
|
# Get the fields at location
|
||||||
|
# px: x-polaration and py: y-polaration.
|
||||||
|
abx_px = mkvc(mkvc(f[src,'b_px'],2).T*Pbx.T)
|
||||||
|
aby_px = mkvc(mkvc(f[src,'b_px'],2).T*Pby.T)
|
||||||
|
abz_px = mkvc(mkvc(f[src,'b_px'],2).T*Pbz.T)
|
||||||
|
abx_py = mkvc(mkvc(f[src,'b_py'],2).T*Pbx.T)
|
||||||
|
aby_py = mkvc(mkvc(f[src,'b_py'],2).T*Pby.T)
|
||||||
|
abz_py = mkvc(mkvc(f[src,'b_py'],2).T*Pbz.T)
|
||||||
|
# Derivatives as lambda functions
|
||||||
|
abx_px_u = lambda vec: f._b_pxDeriv_u(src,Pbx.T*vec,adjoint=True)
|
||||||
|
aby_px_u = lambda vec: f._b_pxDeriv_u(src,Pby.T*vec,adjoint=True)
|
||||||
|
abz_px_u = lambda vec: f._b_pxDeriv_u(src,Pbz.T*vec,adjoint=True)
|
||||||
|
abx_py_u = lambda vec: f._b_pyDeriv_u(src,Pbx.T*vec,adjoint=True)
|
||||||
|
aby_py_u = lambda vec: f._b_pyDeriv_u(src,Pby.T*vec,adjoint=True)
|
||||||
|
abz_py_u = lambda vec: f._b_pyDeriv_u(src,Pbz.T*vec,adjoint=True)
|
||||||
|
|
||||||
|
# Update the input vector
|
||||||
|
# Define shortcuts
|
||||||
|
sDiag = lambda t: Utils.sdiag(mkvc(t,2))
|
||||||
|
sVec = lambda t: Utils.sp.csr_matrix(mkvc(t,2))
|
||||||
|
# Define the components of the derivative
|
||||||
|
aHd = sDiag(1./(sDiag(abx_px)*aby_py - sDiag(abx_py)*aby_px))
|
||||||
|
aHd_uV = lambda x: abx_px_u(sDiag(aby_py)*x) + abx_px_u(sDiag(aby_py)*x) - aby_px_u(sDiag(abx_py)*x) - abx_py_u(sDiag(aby_px)*x)
|
||||||
|
# Need to fix this to reflect the adjoint
|
||||||
|
if 'tzx' in self.rxType:
|
||||||
|
Tij = sDiag(aHd*( -sDiag(abz_py)*aby_px + sDiag(abz_px)*aby_py))
|
||||||
|
TijN_uV = lambda x: -abz_py_u(sDiag(aby_px)*x) - aby_px_u(sDiag(abz_py)*x) + aby_py_u(sDiag(abz_px)*x) + abz_px_u(sDiag(aby_py)*x)
|
||||||
|
elif 'tzy' in self.rxType:
|
||||||
|
Tij = sDiag(aHd*( sDiag(abz_py)*abx_px - sDiag(abz_px)*abx_py))
|
||||||
|
TijN_uV = lambda x: abx_px_u(sDiag(abz_py)*x) + abz_py_u(sDiag(abx_px)*x) - abx_py_u(sDiag(abz_px)*x) - abz_px_u(sDiag(abx_py)*x)
|
||||||
|
# Calculate the complex derivative
|
||||||
|
PDeriv_real = TijN_uV(aHd*v) - aHd_uV(Tij.T*aHd*v)#
|
||||||
|
# NOTE: Need to reshape the output to go from 2*nU array to a (nU,2) matrix for each polarization
|
||||||
|
# PDeriv_real = np.hstack((mkvc(PDeriv_real[:len(PDeriv_real)/2],2),mkvc(PDeriv_real[len(PDeriv_real)/2::],2)))
|
||||||
|
PDeriv_real = PDeriv_real.reshape((2,mesh.nE)).T
|
||||||
|
# Extract the data
|
||||||
|
if real_or_imag == 'imag':
|
||||||
|
Pv = 1j*PDeriv_real
|
||||||
|
elif real_or_imag == 'real':
|
||||||
|
Pv = PDeriv_real.astype(complex)
|
||||||
|
|
||||||
|
|
||||||
|
return Pv
|
||||||
|
|
||||||
|
#################
|
||||||
|
### Survey ###
|
||||||
|
#################
|
||||||
|
class Survey(SimPEGsurvey.BaseSurvey):
|
||||||
|
"""
|
||||||
|
Survey class for MT. Contains all the sources associated with the survey.
|
||||||
|
|
||||||
|
:param list srcList: List of sources associated with the survey
|
||||||
|
|
||||||
|
"""
|
||||||
|
srcPair = SrcMT.BaseMTSrc
|
||||||
|
|
||||||
|
def __init__(self, srcList, **kwargs):
|
||||||
|
# Sort these by frequency
|
||||||
|
self.srcList = srcList
|
||||||
|
SimPEGsurvey.BaseSurvey.__init__(self, **kwargs)
|
||||||
|
|
||||||
|
_freqDict = {}
|
||||||
|
for src in srcList:
|
||||||
|
if src.freq not in _freqDict:
|
||||||
|
_freqDict[src.freq] = []
|
||||||
|
_freqDict[src.freq] += [src]
|
||||||
|
|
||||||
|
self._freqDict = _freqDict
|
||||||
|
self._freqs = sorted([f for f in self._freqDict])
|
||||||
|
|
||||||
|
@property
|
||||||
|
def freqs(self):
|
||||||
|
"""Frequencies"""
|
||||||
|
return self._freqs
|
||||||
|
|
||||||
|
@property
|
||||||
|
def nFreq(self):
|
||||||
|
"""Number of frequencies"""
|
||||||
|
return len(self._freqDict)
|
||||||
|
|
||||||
|
# TODO: Rename to getSources
|
||||||
|
def getSrcByFreq(self, freq):
|
||||||
|
"""Returns the sources associated with a specific frequency."""
|
||||||
|
assert freq in self._freqDict, "The requested frequency is not in this survey."
|
||||||
|
return self._freqDict[freq]
|
||||||
|
|
||||||
|
def projectFields(self, u):
|
||||||
|
data = Data(self)
|
||||||
|
for src in self.srcList:
|
||||||
|
sys.stdout.flush()
|
||||||
|
for rx in src.rxList:
|
||||||
|
data[src, rx] = rx.projectFields(src, self.mesh, u)
|
||||||
|
return data
|
||||||
|
|
||||||
|
def projectFieldsDeriv(self, u):
|
||||||
|
raise Exception('Use Transmitters to project fields deriv.')
|
||||||
|
|
||||||
|
#################
|
||||||
|
### Data ###
|
||||||
|
#################
|
||||||
|
class Data(SimPEGsurvey.Data):
|
||||||
|
'''
|
||||||
|
Data class for MTdata. Stores the data vector indexed by the survey.
|
||||||
|
|
||||||
|
:param SimPEG survey object survey:
|
||||||
|
:param v vector of the data in order matching of the survey
|
||||||
|
|
||||||
|
|
||||||
|
'''
|
||||||
|
def __init__(self, survey, v=None):
|
||||||
|
# Pass the variables to the "parent" method
|
||||||
|
SimPEGsurvey.Data.__init__(self, survey, v)
|
||||||
|
|
||||||
|
# # Import data
|
||||||
|
# @classmethod
|
||||||
|
# def fromEDIFiles():
|
||||||
|
# pass
|
||||||
|
|
||||||
|
def toRecArray(self,returnType='RealImag'):
|
||||||
|
'''
|
||||||
|
Function that returns a numpy.recarray for a SimpegMT impedance data object.
|
||||||
|
|
||||||
|
:param str returnType: Switches between returning a rec array where the impedance is split to real and imaginary ('RealImag') or is a complex ('Complex')
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
# Define the record fields
|
||||||
|
dtRI = [('freq',float),('x',float),('y',float),('z',float),('zxxr',float),('zxxi',float),('zxyr',float),('zxyi',float),
|
||||||
|
('zyxr',float),('zyxi',float),('zyyr',float),('zyyi',float),('tzxr',float),('tzxi',float),('tzyr',float),('tzyi',float)]
|
||||||
|
dtCP = [('freq',float),('x',float),('y',float),('z',float),('zxx',complex),('zxy',complex),('zyx',complex),('zyy',complex),('tzx',complex),('tzy',complex)]
|
||||||
|
impList = ['zxxr','zxxi','zxyr','zxyi','zyxr','zyxi','zyyr','zyyi']
|
||||||
|
for src in self.survey.srcList:
|
||||||
|
# Temp array for all the receivers of the source.
|
||||||
|
# Note: needs to be written more generally, using diffterent rxTypes and not all the data at the locaitons
|
||||||
|
# Assume the same locs for all RX
|
||||||
|
locs = src.rxList[0].locs
|
||||||
|
if locs.shape[1] == 1:
|
||||||
|
locs = np.hstack((np.array([[0.0,0.0]]),locs))
|
||||||
|
elif locs.shape[1] == 2:
|
||||||
|
locs = np.hstack((np.array([[0.0]]),locs))
|
||||||
|
tArrRec = np.concatenate((src.freq*np.ones((locs.shape[0],1)),locs,np.nan*np.ones((locs.shape[0],12))),axis=1).view(dtRI)
|
||||||
|
# np.array([(src.freq,rx.locs[0,0],rx.locs[0,1],rx.locs[0,2],np.nan ,np.nan ,np.nan ,np.nan ,np.nan ,np.nan ,np.nan ,np.nan ) for rx in src.rxList],dtype=dtRI)
|
||||||
|
# Get the type and the value for the DataMT object as a list
|
||||||
|
typeList = [[rx.rxType.replace('z1d','zyx'),self[src,rx]] for rx in src.rxList]
|
||||||
|
# Insert the values to the temp array
|
||||||
|
for nr,(key,val) in enumerate(typeList):
|
||||||
|
tArrRec[key] = mkvc(val,2)
|
||||||
|
# Masked array
|
||||||
|
mArrRec = np.ma.MaskedArray(rec2ndarr(tArrRec),mask=np.isnan(rec2ndarr(tArrRec))).view(dtype=tArrRec.dtype)
|
||||||
|
# Unique freq and loc of the masked array
|
||||||
|
uniFLmarr = np.unique(mArrRec[['freq','x','y','z']]).copy()
|
||||||
|
|
||||||
|
try:
|
||||||
|
outTemp = recFunc.stack_arrays((outTemp,mArrRec))
|
||||||
|
#outTemp = np.concatenate((outTemp,dataBlock),axis=0)
|
||||||
|
except NameError as e:
|
||||||
|
outTemp = mArrRec
|
||||||
|
|
||||||
|
if 'RealImag' in returnType:
|
||||||
|
outArr = outTemp
|
||||||
|
elif 'Complex' in returnType:
|
||||||
|
# Add the real and imaginary to a complex number
|
||||||
|
outArr = np.empty(outTemp.shape,dtype=dtCP)
|
||||||
|
for comp in ['freq','x','y','z']:
|
||||||
|
outArr[comp] = outTemp[comp].copy()
|
||||||
|
for comp in ['zxx','zxy','zyx','zyy','tzx','tzy']:
|
||||||
|
outArr[comp] = outTemp[comp+'r'].copy() + 1j*outTemp[comp+'i'].copy()
|
||||||
|
else:
|
||||||
|
raise NotImplementedError('{:s} is not implemented, as to be RealImag or Complex.')
|
||||||
|
|
||||||
|
# Return
|
||||||
|
return outArr
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def fromRecArray(cls, recArray, srcType='primary'):
|
||||||
|
"""
|
||||||
|
Class method that reads in a numpy record array to MTdata object.
|
||||||
|
|
||||||
|
Only imports the impedance data.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if srcType=='primary':
|
||||||
|
src = SrcMT.polxy_1Dprimary
|
||||||
|
elif srcType=='total':
|
||||||
|
src = SrcMT.polxy_1DhomotD
|
||||||
|
else:
|
||||||
|
raise NotImplementedError('{:s} is not a valid source type for MTdata')
|
||||||
|
|
||||||
|
# Find all the frequencies in recArray
|
||||||
|
uniFreq = np.unique(recArray['freq'])
|
||||||
|
srcList = []
|
||||||
|
dataList = []
|
||||||
|
for freq in uniFreq:
|
||||||
|
# Initiate rxList
|
||||||
|
rxList = []
|
||||||
|
# Find that data for freq
|
||||||
|
dFreq = recArray[recArray['freq'] == freq].copy()
|
||||||
|
# Find the impedance rxTypes in the recArray.
|
||||||
|
rxTypes = [ comp for comp in recArray.dtype.names if (len(comp)==4 or len(comp)==3) and 'z' in comp]
|
||||||
|
for rxType in rxTypes:
|
||||||
|
# Find index of not nan values in rxType
|
||||||
|
notNaNind = ~np.isnan(dFreq[rxType])
|
||||||
|
if np.any(notNaNind): # Make sure that there is any data to add.
|
||||||
|
locs = rec2ndarr(dFreq[['x','y','z']][notNaNind].copy())
|
||||||
|
if dFreq[rxType].dtype.name in 'complex128':
|
||||||
|
rxList.append(Rx(locs,rxType+'r'))
|
||||||
|
dataList.append(dFreq[rxType][notNaNind].real.copy())
|
||||||
|
rxList.append(Rx(locs,rxType+'i'))
|
||||||
|
dataList.append(dFreq[rxType][notNaNind].imag.copy())
|
||||||
|
else:
|
||||||
|
rxList.append(Rx(locs,rxType))
|
||||||
|
dataList.append(dFreq[rxType][notNaNind].copy())
|
||||||
|
srcList.append(src(rxList,freq))
|
||||||
|
|
||||||
|
# Make a survey
|
||||||
|
survey = Survey(srcList)
|
||||||
|
dataVec = np.hstack(dataList)
|
||||||
|
return cls(survey,dataVec)
|
||||||
|
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
# Analytic solution of EM fields due to a plane wave
|
||||||
|
|
||||||
|
import numpy as np, SimPEG as simpeg
|
||||||
|
from scipy.constants import mu_0, epsilon_0 as eps_0
|
||||||
|
|
||||||
|
def getEHfields(m1d,sigma,freq,zd,scaleUD=True):
|
||||||
|
'''Analytic solution for MT 1D layered earth. Returns E and H fields.
|
||||||
|
|
||||||
|
:param SimPEG.mesh, object m1d: Mesh object with the 1D spatial information.
|
||||||
|
:param numpy.array, vector sigma: Physical property of conductivity corresponding with the mesh.
|
||||||
|
:param float, freq: Frequency to calculate data at.
|
||||||
|
:param numpy array, vector zd: location to calculate EH fields at
|
||||||
|
:param bollean, scaleUD: scales the output to be 1 at the top, increases numeracal stability.
|
||||||
|
|
||||||
|
Assumes a halfspace with the same conductive as the last cell below.
|
||||||
|
|
||||||
|
'''
|
||||||
|
# Note add an error check for the mesh and sigma are the same size.
|
||||||
|
|
||||||
|
# Constants: Assume constant
|
||||||
|
mu = mu_0*np.ones((m1d.nC+1))
|
||||||
|
eps = eps_0*np.ones((m1d.nC+1))
|
||||||
|
# Angular freq
|
||||||
|
w = 2*np.pi*freq
|
||||||
|
# Add the halfspace value to the property
|
||||||
|
sig = np.concatenate((np.array([sigma[0]]),sigma))
|
||||||
|
# Calculate the wave number
|
||||||
|
k = np.sqrt(eps*mu*w**2-1j*mu*sig*w)
|
||||||
|
|
||||||
|
# Initiate the propagation matrix, in the order down up.
|
||||||
|
UDp = np.zeros((2,m1d.nC+1),dtype=complex)
|
||||||
|
UDp[1,0] = 1. # Set the wave amplitude as 1 into the half-space at the bottom of the mesh
|
||||||
|
# Loop over all the layers, starting at the bottom layer
|
||||||
|
for lnr, h in enumerate(m1d.hx): # lnr-number of layer, h-thickness of the layer
|
||||||
|
# Calculate
|
||||||
|
yp1 = k[lnr]/(w*mu[lnr]) # Admittance of the layer below the current layer
|
||||||
|
zp = (w*mu[lnr+1])/k[lnr+1] # Impedance in the current layer
|
||||||
|
# Build the propagation matrix
|
||||||
|
|
||||||
|
# Convert fields to down/up going components in layer below current layer
|
||||||
|
Pj1 = np.array([[1,1],[yp1,-yp1]])
|
||||||
|
# Convert fields to down/up going components in current layer
|
||||||
|
Pjinv = 1./2*np.array([[1,zp],[1,-zp]])
|
||||||
|
# Propagate down and up components through the current layer
|
||||||
|
elamh = np.array([[np.exp(-1j*k[lnr+1]*h),0],[0,np.exp(1j*k[lnr+1]*h)]])
|
||||||
|
|
||||||
|
# The down and up component in current layer.
|
||||||
|
UDp[:,lnr+1] = elamh.dot(Pjinv.dot(Pj1)).dot(UDp[:,lnr])
|
||||||
|
|
||||||
|
if scaleUD:
|
||||||
|
UDp[:,lnr+1::-1] = UDp[:,lnr+1::-1]/UDp[1,lnr+1]
|
||||||
|
|
||||||
|
# Calculate the fields
|
||||||
|
Ed = np.empty((zd.size,),dtype=complex)
|
||||||
|
Eu = np.empty((zd.size,),dtype=complex)
|
||||||
|
Hd = np.empty((zd.size,),dtype=complex)
|
||||||
|
Hu = np.empty((zd.size,),dtype=complex)
|
||||||
|
|
||||||
|
# Loop over the layers and calculate the fields
|
||||||
|
# In the halfspace below the mesh
|
||||||
|
dup = m1d.vectorNx[0]
|
||||||
|
dind = dup >= zd
|
||||||
|
Ed[dind] = UDp[1,0]*np.exp(-1j*k[0]*(dup-zd[dind]))
|
||||||
|
Eu[dind] = UDp[0,0]*np.exp(1j*k[0]*(dup-zd[dind]))
|
||||||
|
Hd[dind] = (k[0]/(w*mu[0]))*UDp[1,0]*np.exp(-1j*k[0]*(dup-zd[dind]))
|
||||||
|
Hu[dind] = -(k[0]/(w*mu[0]))*UDp[0,0]*np.exp(1j*k[0]*(dup-zd[dind]))
|
||||||
|
for ki,mui,epsi,dlow,dup,Up,Dp in zip(k[1::],mu[1::],eps[1::],m1d.vectorNx[:-1],m1d.vectorNx[1::],UDp[0,1::],UDp[1,1::]):
|
||||||
|
dind = np.logical_and(dup >= zd, zd > dlow)
|
||||||
|
Ed[dind] = Dp*np.exp(-1j*ki*(dup-zd[dind]))
|
||||||
|
Eu[dind] = Up*np.exp(1j*ki*(dup-zd[dind]))
|
||||||
|
Hd[dind] = (ki/(w*mui))*Dp*np.exp(-1j*ki*(dup-zd[dind]))
|
||||||
|
Hu[dind] = -(ki/(w*mui))*Up*np.exp(1j*ki*(dup-zd[dind]))
|
||||||
|
|
||||||
|
# Return return the fields
|
||||||
|
return Ed, Eu, Hd, Hu
|
||||||
|
|
||||||
|
def getImpedance(m1d,sigma,freq):
|
||||||
|
"""Analytic solution for MT 1D layered earth. Returns the impedance at the surface.
|
||||||
|
|
||||||
|
:param SimPEG.mesh, object m1d: Mesh object with the 1D spatial information.
|
||||||
|
:param numpy.array, vector sigma: Physical property corresponding with the mesh.
|
||||||
|
:param numpy.array, vector freq: Frequencies to calculate data at.
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Initiate the impedances
|
||||||
|
Z1d = np.empty(len(freq) , dtype='complex')
|
||||||
|
h = m1d.hx #vectorNx[:-1]
|
||||||
|
# Start the process
|
||||||
|
for nrFr, fr in enumerate(freq):
|
||||||
|
om = 2*np.pi*fr
|
||||||
|
Zall = np.empty(len(h)+1,dtype='complex')
|
||||||
|
# Calculate the impedance for the bottom layer
|
||||||
|
Zall[0] = (mu_0*om)/np.sqrt(mu_0*eps_0*(om)**2 - 1j*mu_0*sigma[0]*om)
|
||||||
|
|
||||||
|
for nr,hi in enumerate(h):
|
||||||
|
# Calculate the wave number
|
||||||
|
# print nr,sigma[nr]
|
||||||
|
k = np.sqrt(mu_0*eps_0*om**2 - 1j*mu_0*sigma[nr]*om)
|
||||||
|
Z = (mu_0*om)/k
|
||||||
|
|
||||||
|
Zall[nr+1] = Z *((Zall[nr] + Z*np.tanh(1j*k*hi))/(Z + Zall[nr]*np.tanh(1j*k*hi)))
|
||||||
|
|
||||||
|
#pdb.set_trace()
|
||||||
|
Z1d[nrFr] = Zall[-1]
|
||||||
|
|
||||||
|
return Z1d
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import numpy as np, SimPEG as simpeg
|
||||||
|
from MT1Danalytic import getEHfields
|
||||||
|
from scipy.constants import mu_0
|
||||||
|
|
||||||
|
def get1DEfields(m1d,sigma,freq,sourceAmp=1.0):
|
||||||
|
"""Function to get 1D electrical fields"""
|
||||||
|
|
||||||
|
# Get the gradient
|
||||||
|
G = m1d.nodalGrad
|
||||||
|
# Mass matrices
|
||||||
|
# Magnetic permeability
|
||||||
|
Mmu = simpeg.Utils.sdiag(m1d.vol*(1.0/mu_0))
|
||||||
|
# Conductivity
|
||||||
|
Msig = m1d.getFaceInnerProduct(sigma)
|
||||||
|
# Set up the solution matrix
|
||||||
|
A = G.T*Mmu*G + 1j*2.*np.pi*freq*Msig
|
||||||
|
# Define the inner part of the solution matrix
|
||||||
|
Aii = A[1:-1,1:-1]
|
||||||
|
# Define the outer part of the solution matrix
|
||||||
|
Aio = A[1:-1,[0,-1]]
|
||||||
|
|
||||||
|
# Set the boundary conditions
|
||||||
|
Ed, Eu, Hd, Hu = getEHfields(m1d,sigma,freq,m1d.vectorNx)
|
||||||
|
Etot = (Ed + Eu)
|
||||||
|
if sourceAmp is not None:
|
||||||
|
Etot = ((Etot/Etot[-1])*sourceAmp) # Scale the fields to be equal to sourceAmp at the top
|
||||||
|
## Note: The analytic solution is derived with e^iwt
|
||||||
|
bc = np.r_[Etot[0],Etot[-1]]
|
||||||
|
# The right hand side
|
||||||
|
rhs = Aio*bc
|
||||||
|
# Solve the system
|
||||||
|
Aii_inv = simpeg.Solver(Aii)
|
||||||
|
eii = Aii_inv*rhs
|
||||||
|
# Assign the boundary conditions
|
||||||
|
e = np.r_[bc[0],eii,bc[1]]
|
||||||
|
# Return the electrical fields
|
||||||
|
return e
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
|
||||||
|
hz = [(100.,18)]
|
||||||
|
M = simpeg.Mesh.TensorMesh([hz],'C')
|
||||||
|
sig = np.zeros(M.nC) + 1e-8
|
||||||
|
sig[M.vectorCCx<=0] = sigHalf
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
from MT1Dsolutions import * # Add the names of the functions
|
||||||
|
from MT1Danalytic import *
|
||||||
|
from dataUtils import *
|
||||||
|
from ediFilesUtils import *
|
||||||
@@ -0,0 +1,245 @@
|
|||||||
|
# Utils used for the data,
|
||||||
|
import numpy as np, matplotlib.pyplot as plt, sys
|
||||||
|
import SimPEG as simpeg
|
||||||
|
import numpy.lib.recfunctions as recFunc
|
||||||
|
from scipy.constants import mu_0
|
||||||
|
from scipy import interpolate as sciint
|
||||||
|
|
||||||
|
def getAppRes(MTdata):
|
||||||
|
# Make impedance
|
||||||
|
zList = []
|
||||||
|
for src in MTdata.survey.srcList:
|
||||||
|
zc = [src.freq]
|
||||||
|
for rx in src.rxList:
|
||||||
|
if 'i' in rx.rxType:
|
||||||
|
m=1j
|
||||||
|
else:
|
||||||
|
m = 1
|
||||||
|
zc.append(m*MTdata[src,rx])
|
||||||
|
zList.append(zc)
|
||||||
|
return [appResPhs(zList[i][0],np.sum(zList[i][1:3])) for i in np.arange(len(zList))]
|
||||||
|
|
||||||
|
def rotateData(MTdata,rotAngle):
|
||||||
|
'''
|
||||||
|
Function that rotates clockwist by rotAngle (- negative for a counter-clockwise rotation)
|
||||||
|
'''
|
||||||
|
recData = MTdata.toRecArray('Complex')
|
||||||
|
impData = rec2ndarr(recData[['zxx','zxy','zyx','zyy']],complex)
|
||||||
|
# Make the rotation matrix
|
||||||
|
# c,s,zxx,zxy,zyx,zyy = sympy.symbols('c,s,zxx,zxy,zyx,zyy')
|
||||||
|
# rotM = sympy.Matrix([[c,-s],[s, c]])
|
||||||
|
# zM = sympy.Matrix([[zxx,zxy],[zyx,zyy]])
|
||||||
|
# rotM*zM*rotM.T
|
||||||
|
# [c*(c*zxx - s*zyx) - s*(c*zxy - s*zyy), c*(c*zxy - s*zyy) + s*(c*zxx - s*zyx)],
|
||||||
|
# [c*(c*zyx + s*zxx) - s*(c*zyy + s*zxy), c*(c*zyy + s*zxy) + s*(c*zyx + s*zxx)]])
|
||||||
|
s = np.sin(-np.deg2rad(rotAngle))
|
||||||
|
c = np.cos(-np.deg2rad(rotAngle))
|
||||||
|
rotMat = np.array([[c,-s],[s,c]])
|
||||||
|
rotData = (rotMat.dot(impData.reshape(-1,2,2).dot(rotMat.T))).transpose(1,0,2).reshape(-1,4)
|
||||||
|
outRec = recData.copy()
|
||||||
|
for nr,comp in enumerate(['zxx','zxy','zyx','zyy']):
|
||||||
|
outRec[comp] = rotData[:,nr]
|
||||||
|
|
||||||
|
from SimPEG import MT
|
||||||
|
return MT.Data.fromRecArray(outRec)
|
||||||
|
|
||||||
|
|
||||||
|
def appResPhs(freq,z):
|
||||||
|
app_res = ((1./(8e-7*np.pi**2))/freq)*np.abs(z)**2
|
||||||
|
app_phs = np.arctan2(z.imag,z.real)*(180/np.pi)
|
||||||
|
return app_res, app_phs
|
||||||
|
|
||||||
|
def skindepth(rho,freq):
|
||||||
|
''' Function to calculate the skindepth of EM waves'''
|
||||||
|
return np.sqrt( (rho*((1/(freq * mu_0 * np.pi )))))
|
||||||
|
|
||||||
|
def rec2ndarr(x,dt=float):
|
||||||
|
return x.view((dt, len(x.dtype.names)))
|
||||||
|
|
||||||
|
def makeAnalyticSolution(mesh,model,elev,freqs):
|
||||||
|
from SimPEG import MT
|
||||||
|
data1D = []
|
||||||
|
for freq in freqs:
|
||||||
|
anaEd, anaEu, anaHd, anaHu = MT.Utils.MT1Danalytic.getEHfields(mesh,model,freq,elev)
|
||||||
|
anaE = anaEd+anaEu
|
||||||
|
anaH = anaHd+anaHu
|
||||||
|
|
||||||
|
anaZ = anaE/anaH
|
||||||
|
# Add to the list
|
||||||
|
data1D.append((freq,0,0,elev,anaZ[0]))
|
||||||
|
dataRec = np.array(data1D,dtype=[('freq',float),('x',float),('y',float),('z',float),('zyx',complex)])
|
||||||
|
return dataRec
|
||||||
|
|
||||||
|
def plotMT1DModelData(problem,models,symList=None):
|
||||||
|
from SimPEG import MT
|
||||||
|
# Setup the figure
|
||||||
|
fontSize = 15
|
||||||
|
|
||||||
|
fig = plt.figure(figsize=[9,7])
|
||||||
|
axM = fig.add_axes([0.075,.1,.25,.875])
|
||||||
|
axM.set_xlabel('Resistivity [Ohm*m]',fontsize=fontSize)
|
||||||
|
axM.set_xlim(1e-1,1e5)
|
||||||
|
axM.set_ylim(-10000,5000)
|
||||||
|
axM.set_ylabel('Depth [km]',fontsize=fontSize)
|
||||||
|
axR = fig.add_axes([0.42,.575,.5,.4])
|
||||||
|
axR.set_xscale('log')
|
||||||
|
axR.set_yscale('log')
|
||||||
|
axR.invert_xaxis()
|
||||||
|
# axR.set_xlabel('Frequency [Hz]')
|
||||||
|
axR.set_ylabel('Apparent resistivity [Ohm m]',fontsize=fontSize)
|
||||||
|
|
||||||
|
axP = fig.add_axes([0.42,.1,.5,.4])
|
||||||
|
axP.set_xscale('log')
|
||||||
|
axP.invert_xaxis()
|
||||||
|
axP.set_ylim(0,90)
|
||||||
|
axP.set_xlabel('Frequency [Hz]',fontsize=fontSize)
|
||||||
|
axP.set_ylabel('Apparent phase [deg]',fontsize=fontSize)
|
||||||
|
|
||||||
|
# if not symList:
|
||||||
|
# symList = ['x']*len(models)
|
||||||
|
import plotDataTypes as pDt
|
||||||
|
# Loop through the models.
|
||||||
|
modelList = [problem.survey.mtrue]
|
||||||
|
modelList.extend(models)
|
||||||
|
if False:
|
||||||
|
modelList = [problem.mapping.sigmaMap*mod for mod in modelList]
|
||||||
|
for nr, model in enumerate(modelList):
|
||||||
|
# Calculate the data
|
||||||
|
if nr==0:
|
||||||
|
data1D = problem.dataPair(problem.survey,problem.survey.dobs).toRecArray('Complex')
|
||||||
|
else:
|
||||||
|
data1D = problem.dataPair(problem.survey,problem.survey.dpred(model)).toRecArray('Complex')
|
||||||
|
# Plot the data and the model
|
||||||
|
colRat = nr/((len(modelList)-1.999)*1.)
|
||||||
|
if colRat > 1.:
|
||||||
|
col = 'k'
|
||||||
|
else:
|
||||||
|
col = plt.cm.seismic(1-colRat)
|
||||||
|
# The model - make the pts to plot
|
||||||
|
meshPts = np.concatenate((problem.mesh.gridN[0:1],np.kron(problem.mesh.gridN[1::],np.ones(2))[:-1]))
|
||||||
|
modelPts = np.kron(1./(problem.mapping.sigmaMap*model),np.ones(2,))
|
||||||
|
axM.semilogx(modelPts,meshPts,color=col)
|
||||||
|
|
||||||
|
## Data
|
||||||
|
# Appres
|
||||||
|
pDt.plotIsoStaImpedance(axR,np.array([0,0]),data1D,'zyx','res',pColor=col)
|
||||||
|
# Appphs
|
||||||
|
pDt.plotIsoStaImpedance(axP,np.array([0,0]),data1D,'zyx','phs',pColor=col)
|
||||||
|
try:
|
||||||
|
allData = np.concatenate((allData,simpeg.mkvc(data1D['zyx'],2)),1)
|
||||||
|
except:
|
||||||
|
allData = simpeg.mkvc(data1D['zyx'],2)
|
||||||
|
freq = simpeg.mkvc(data1D['freq'],2)
|
||||||
|
res, phs = appResPhs(freq,allData)
|
||||||
|
|
||||||
|
stdCol = 'gray'
|
||||||
|
axRtw = axR.twinx()
|
||||||
|
axRtw.set_ylabel('Std of log10',color=stdCol)
|
||||||
|
[(t.set_color(stdCol), t.set_rotation(-45)) for t in axRtw.get_yticklabels()]
|
||||||
|
axPtw = axP.twinx()
|
||||||
|
axPtw.set_ylabel('Std ',color=stdCol)
|
||||||
|
[t.set_color(stdCol) for t in axPtw.get_yticklabels()]
|
||||||
|
axRtw.plot(freq, np.std(np.log10(res),1),'--',color=stdCol)
|
||||||
|
axPtw.plot(freq, np.std(phs,1),'--',color=stdCol)
|
||||||
|
|
||||||
|
# Fix labels and ticks
|
||||||
|
|
||||||
|
yMtick = [l/1000 for l in axM.get_yticks().tolist()]
|
||||||
|
axM.set_yticklabels(yMtick)
|
||||||
|
[ l.set_rotation(90) for l in axM.get_yticklabels()]
|
||||||
|
[ l.set_rotation(90) for l in axR.get_yticklabels()]
|
||||||
|
[(t.set_color(stdCol), t.set_rotation(-45)) for t in axRtw.get_yticklabels()]
|
||||||
|
[t.set_color(stdCol) for t in axPtw.get_yticklabels()]
|
||||||
|
for ax in [axM,axR,axP]:
|
||||||
|
ax.xaxis.set_tick_params(labelsize=fontSize)
|
||||||
|
ax.yaxis.set_tick_params(labelsize=fontSize)
|
||||||
|
return fig
|
||||||
|
|
||||||
|
def printTime():
|
||||||
|
import time
|
||||||
|
print time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.localtime())
|
||||||
|
|
||||||
|
def convert3Dto1Dobject(MTdata,rxType3D='zyx'):
|
||||||
|
from SimPEG import MT
|
||||||
|
# Find the unique locations
|
||||||
|
# Need to find the locations
|
||||||
|
recDataTemp = MTdata.toRecArray()
|
||||||
|
# Check if survey.std has been assigned.
|
||||||
|
## NEED TO: write this...
|
||||||
|
# Calculte and add the DET of the tensor to the recArray
|
||||||
|
if 'det' in rxType3D:
|
||||||
|
Zon = (recDataTemp['zxxr']+1j*recDataTemp['zxxi'])*(recDataTemp['zyyr']+1j*recDataTemp['zyyi'])
|
||||||
|
Zoff = (recDataTemp['zxyr']+1j*recDataTemp['zxyi'])*(recDataTemp['zyxr']+1j*recDataTemp['zyxi'])
|
||||||
|
det = np.sqrt(Zon.data - Zoff.data)
|
||||||
|
recData = recFunc.append_fields(recDataTemp,['zdetr','zdeti'],[det.real,det.imag] )
|
||||||
|
else:
|
||||||
|
recData = recDataTemp
|
||||||
|
|
||||||
|
uniLocs = rec2ndarr(np.unique(recData[['x','y','z']])).data
|
||||||
|
mtData1DList = []
|
||||||
|
if 'zxy' in rxType3D:
|
||||||
|
corr = -1 # Shift the data to comply with the quadtrature of the 1d problem
|
||||||
|
else:
|
||||||
|
corr = 1
|
||||||
|
for loc in uniLocs:
|
||||||
|
# Make the receiver list
|
||||||
|
rx1DList = []
|
||||||
|
for rxType in ['z1dr','z1di']:
|
||||||
|
rx1DList.append(MT.Rx(simpeg.mkvc(loc,2).T,rxType))
|
||||||
|
# Source list
|
||||||
|
locrecData = recData[np.sqrt(np.sum( (rec2ndarr(recData[['x','y','z']]).data - loc )**2,axis=1)) < 1e-5]
|
||||||
|
dat1DList = []
|
||||||
|
src1DList = []
|
||||||
|
for freq in locrecData['freq']:
|
||||||
|
src1DList.append(MT.SrcMT.src_polxy_1Dprimary(rx1DList,freq))
|
||||||
|
for comp in ['r','i']:
|
||||||
|
dat1DList.append( corr * locrecData[rxType3D+comp][locrecData['freq']== freq].data )
|
||||||
|
|
||||||
|
# Make the survey
|
||||||
|
sur1D = MT.Survey(src1DList)
|
||||||
|
|
||||||
|
# Make the data
|
||||||
|
dataVec = np.hstack(dat1DList)
|
||||||
|
dat1D = MT.Data(sur1D,dataVec)
|
||||||
|
sur1D.dobs = dataVec
|
||||||
|
# Need to take MTdata.survey.std and split it as well.
|
||||||
|
std=0.05
|
||||||
|
sur1D.std = np.abs(sur1D.dobs*std) #+ 0.01*np.linalg.norm(sur1D.dobs)
|
||||||
|
mtData1DList.append(dat1D)
|
||||||
|
|
||||||
|
# Return the the list of data.
|
||||||
|
return mtData1DList
|
||||||
|
|
||||||
|
def resampleMTdataAtFreq(MTdata,freqs):
|
||||||
|
"""
|
||||||
|
Function to resample MTdata at set of frequencies
|
||||||
|
|
||||||
|
"""
|
||||||
|
from SimPEG import MT
|
||||||
|
# Make a rec array
|
||||||
|
MTrec = MTdata.toRecArray().data
|
||||||
|
|
||||||
|
# Find unique locations
|
||||||
|
uniLoc = np.unique(MTrec[['x','y','z']])
|
||||||
|
uniFreq = MTdata.survey.freqs
|
||||||
|
# Get the comps
|
||||||
|
dNames = MTrec.dtype
|
||||||
|
|
||||||
|
# Loop over all the locations and interpolate
|
||||||
|
for loc in uniLoc:
|
||||||
|
# Find the index of the station
|
||||||
|
ind = np.sqrt(np.sum((rec2ndarr(MTrec[['x','y','z']]) - rec2ndarr(loc))**2,axis=1)) < 1. # Find dist of 1 m accuracy
|
||||||
|
# Make a temporary recArray and interpolate all the components
|
||||||
|
tArrRec = np.concatenate((simpeg.mkvc(freqs,2),np.ones((len(freqs),1))*rec2ndarr(loc),np.nan*np.ones((len(freqs),12))),axis=1).view(dNames)
|
||||||
|
for comp in ['zxxr','zxxi','zxyr','zxyi','zyxr','zyxi','zyyr','zyyi','tzxr','tzxi','tzyr','tzyi']:
|
||||||
|
int1d = sciint.interp1d(MTrec[ind]['freq'],MTrec[ind][comp],bounds_error=False)
|
||||||
|
tArrRec[comp] = simpeg.mkvc(int1d(freqs),2)
|
||||||
|
|
||||||
|
# Join together
|
||||||
|
try:
|
||||||
|
outRecArr = recFunc.stack_arrays((outRecArr,tArrRec))
|
||||||
|
except NameError as e:
|
||||||
|
outRecArr = tArrRec
|
||||||
|
|
||||||
|
# Make the MTdata and return
|
||||||
|
return MT.Data.fromRecArray(outRecArr)
|
||||||
@@ -0,0 +1,175 @@
|
|||||||
|
# Functions to import and export MT EDI files.
|
||||||
|
from SimPEG import mkvc
|
||||||
|
from scipy.constants import mu_0
|
||||||
|
from numpy.lib import recfunctions as recFunc
|
||||||
|
from SimPEG.MT.Utils.dataUtils import rec2ndarr
|
||||||
|
|
||||||
|
# Import modules
|
||||||
|
import numpy as np
|
||||||
|
import os, sys, re
|
||||||
|
try:
|
||||||
|
import osr
|
||||||
|
except ImportError as e:
|
||||||
|
print 'Could not import osr, missing the gdal package'
|
||||||
|
pass
|
||||||
|
|
||||||
|
class EDIimporter:
|
||||||
|
"""
|
||||||
|
A class to import EDIfiles.
|
||||||
|
|
||||||
|
"""
|
||||||
|
_impUnitEDI2SI = 4*np.pi*1e-4 # Convert Z[mV/km/nT] (as in EDI)to Z[V/A] SI unit
|
||||||
|
_impUnitSI2EDI = 1./_impUnitEDI2SI # ConvertZ[V/A] SI unit to Z[mV/km/nT] (as in EDI)
|
||||||
|
|
||||||
|
# Properties
|
||||||
|
filesList = None
|
||||||
|
comps = None
|
||||||
|
|
||||||
|
# Hidden properties
|
||||||
|
_outEPSG = None
|
||||||
|
_2out = None
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, EDIfilesList, compList=None, outEPSG=None):
|
||||||
|
|
||||||
|
# Set the fileList
|
||||||
|
self.filesList = EDIfilesList
|
||||||
|
# Set the components to import
|
||||||
|
if compList is None:
|
||||||
|
self.comps = ['ZXXR','ZXYR','ZYXR','ZYYR','ZXXI','ZXYI','ZYXI','ZYYI','ZXX.VAR','ZXY.VAR','ZYX.VAR','ZYY.VAR']
|
||||||
|
else:
|
||||||
|
self.comps = compList
|
||||||
|
if outEPSG is not None:
|
||||||
|
self._outEPSG = outEPSG
|
||||||
|
|
||||||
|
def __call__(self,comps=None):
|
||||||
|
|
||||||
|
if comps is None:
|
||||||
|
return self._data
|
||||||
|
|
||||||
|
return self._data[comps]
|
||||||
|
|
||||||
|
def importFiles(self):
|
||||||
|
"""
|
||||||
|
Function to import EDI files into a object.
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Constants that are needed for convertion of units
|
||||||
|
|
||||||
|
# Temp lists
|
||||||
|
tmpStaList = []
|
||||||
|
|
||||||
|
tmpCompList = ['freq','x','y','z']
|
||||||
|
tmpCompList.extend(self.comps)
|
||||||
|
# Make the outarray
|
||||||
|
dtRI = [(compS.lower().replace('.',''),float) for compS in tmpCompList]
|
||||||
|
# Loop through all the files
|
||||||
|
for nrEDI, EDIfile in enumerate(self.filesList):
|
||||||
|
# Read the file into a list of the lines
|
||||||
|
with open(EDIfile,'r') as fid:
|
||||||
|
EDIlines = fid.readlines()
|
||||||
|
# Find the location
|
||||||
|
latD, longD, elevM = _findLatLong(EDIlines)
|
||||||
|
# Transfrom coordinates
|
||||||
|
transCoord = self._transfromPoints(longD,latD)
|
||||||
|
# Extract the name of the file (station)
|
||||||
|
EDIname = EDIfile.split(os.sep)[-1].split('.')[0]
|
||||||
|
# Arrange the data
|
||||||
|
staList = [EDIname, EDIfile, transCoord[0], transCoord[1], elevM[0]]
|
||||||
|
# Add to the station list
|
||||||
|
tmpStaList.extend(staList)
|
||||||
|
|
||||||
|
# Read the frequency data
|
||||||
|
freq = _findEDIcomp('>FREQ',EDIlines)
|
||||||
|
# Make the temporary rec array.
|
||||||
|
tArrRec = ( np.nan*np.ones( (len(freq),len(dtRI)) ) ).view(dtRI) #np.concatenate((freq*np.ones((locs.shape[0],1)),locs,np.nan*np.ones((locs.shape[0],8))),axis=1).view(dtRI)
|
||||||
|
# Add data to the array
|
||||||
|
tArrRec['freq'] = mkvc(freq,2)
|
||||||
|
tArrRec['x'] = mkvc(np.ones((len(freq),1))*transCoord[0],2)
|
||||||
|
tArrRec['y'] = mkvc(np.ones((len(freq),1))*transCoord[1],2)
|
||||||
|
tArrRec['z'] = mkvc(np.ones((len(freq),1))*elevM[0],2)
|
||||||
|
for comp in self.comps:
|
||||||
|
# Deal with converting units of the impedance tensor
|
||||||
|
if 'Z' in comp:
|
||||||
|
unitConvert = self._impUnitEDI2SI
|
||||||
|
else:
|
||||||
|
unitConvert = 1
|
||||||
|
# Rotate the data since EDI x is *north, y *east but Simpeg uses x *east, y *north (* means internal reference frame)
|
||||||
|
key = [comp.lower().replace('.','').replace(s,t) for s,t in [['xx','yy'],['xy','yx'],['yx','xy'],['yy','xx']] if s in comp.lower()][0]
|
||||||
|
tArrRec[key] = mkvc(unitConvert*_findEDIcomp('>'+comp,EDIlines),2)
|
||||||
|
# Make a masked array
|
||||||
|
mArrRec = np.ma.MaskedArray(rec2ndarr(tArrRec),mask=np.isnan(rec2ndarr(tArrRec))).view(dtype=tArrRec.dtype)
|
||||||
|
try:
|
||||||
|
outTemp = recFunc.stack_arrays((outTemp,mArrRec))
|
||||||
|
except NameError as e:
|
||||||
|
outTemp = mArrRec
|
||||||
|
|
||||||
|
# Assign the data
|
||||||
|
self._data = outTemp
|
||||||
|
|
||||||
|
# % Assign the data to the obj
|
||||||
|
# nOutData=length(obj.data);
|
||||||
|
# obj.data(nOutData+1:nOutData+length(TEMP.data),:) = TEMP.data;
|
||||||
|
def _transfromPoints(self,longD,latD):
|
||||||
|
# Coordinates convertor
|
||||||
|
if self._2out is None:
|
||||||
|
src = osr.SpatialReference()
|
||||||
|
src.ImportFromEPSG(4326)
|
||||||
|
out = osr.SpatialReference()
|
||||||
|
if self._outEPSG is None:
|
||||||
|
# Find the UTM EPSG number
|
||||||
|
Nnr = 700 if latD < 0.0 else 600
|
||||||
|
utmZ = int(1+(longD+180.0)/6.0)
|
||||||
|
self._outEPSG = 32000 + Nnr + utmZ
|
||||||
|
out.ImportFromEPSG(self._outEPSG)
|
||||||
|
self._2out = osr.CoordinateTransformation(src,out)
|
||||||
|
# Return the transfrom
|
||||||
|
return self._2out.TransformPoint(longD,latD)
|
||||||
|
|
||||||
|
# Hidden functions
|
||||||
|
def _findLatLong(fileLines):
|
||||||
|
latDMS = np.array(fileLines[_findLine('LAT=',fileLines)[0]].split('=')[1].split()[0].split(':'),float)
|
||||||
|
longDMS = np.array(fileLines[_findLine('LONG=',fileLines)[0]].split('=')[1].split()[0].split(':'),float)
|
||||||
|
elevM = np.array([fileLines[_findLine('ELEV=',fileLines)[0]].split('=')[1].split()[0]],float)
|
||||||
|
# Convert to D.ddddd values
|
||||||
|
latS = np.sign(latDMS[0])
|
||||||
|
longS = np.sign(longDMS[0])
|
||||||
|
latD = latDMS[0] + latS*latDMS[1]/60 + latS*latDMS[2]/3600
|
||||||
|
longD = longDMS[0] + longS*longDMS[1]/60 + longS*longDMS[2]/3600
|
||||||
|
return latD, longD, elevM
|
||||||
|
|
||||||
|
def _findLine(comp,fileLines):
|
||||||
|
""" Find a line number in the file"""
|
||||||
|
# Line counter
|
||||||
|
c = 0
|
||||||
|
# List of indices for found lines
|
||||||
|
found = []
|
||||||
|
# Loop through all the lines
|
||||||
|
for line in fileLines:
|
||||||
|
if comp in line:
|
||||||
|
# Append if found
|
||||||
|
found.append(c)
|
||||||
|
# Increse the counter
|
||||||
|
c += 1
|
||||||
|
# Return the found indices
|
||||||
|
return found
|
||||||
|
|
||||||
|
def _findEDIcomp(comp,fileLines,dt=float):
|
||||||
|
"""
|
||||||
|
Extract the data vector.
|
||||||
|
|
||||||
|
Returns a list of the data.
|
||||||
|
"""
|
||||||
|
# Find the data
|
||||||
|
headLine, indHead = [(st,nr) for nr,st in enumerate(fileLines) if re.search(comp,st)][0]
|
||||||
|
# Extract the data
|
||||||
|
nrVec = int(headLine.split()[-1])
|
||||||
|
c = 0
|
||||||
|
dataList = []
|
||||||
|
while c < nrVec:
|
||||||
|
indHead += 1
|
||||||
|
dataList.extend(fileLines[indHead].split())
|
||||||
|
c = len(dataList)
|
||||||
|
return np.array(dataList,dt)
|
||||||
@@ -0,0 +1,416 @@
|
|||||||
|
from matplotlib import pyplot as plt, colors, numpy as np
|
||||||
|
|
||||||
|
|
||||||
|
def rec2nd(structArray):
|
||||||
|
""" Converts a structured/record array to ndarray to do operations on."""
|
||||||
|
return structArray.view((np.float,len(structArray.dtype.names)))
|
||||||
|
|
||||||
|
def plotIsoFreqNSimpedance(ax,freq,array,flag,par='abs',colorbar=True,colorNorm='SymLog',cLevel=True,contour=True):
|
||||||
|
|
||||||
|
indUniFreq = np.where(freq==array['freq'])
|
||||||
|
|
||||||
|
|
||||||
|
x, y = array['x'][indUniFreq],array['y'][indUniFreq]
|
||||||
|
if par == 'abs':
|
||||||
|
zPlot = np.abs(array[flag][indUniFreq])
|
||||||
|
cmap = plt.get_cmap('OrRd_r')#seismic')
|
||||||
|
level = np.logspace(0,-5,31)
|
||||||
|
clevel = np.logspace(0,-4,5)
|
||||||
|
plotNorm = colors.LogNorm()
|
||||||
|
elif par == 'real':
|
||||||
|
zPlot = np.real(array[flag][indUniFreq])
|
||||||
|
cmap = plt.get_cmap('RdYlBu')
|
||||||
|
if cLevel:
|
||||||
|
level = np.concatenate((-np.logspace(0,-10,31),np.logspace(-10,0,31)))
|
||||||
|
clevel = np.concatenate((-np.logspace(0,-8,5),np.logspace(-8,0,5)))
|
||||||
|
else:
|
||||||
|
level = np.linspace(zPlot.min(),zPlot.max(),100)
|
||||||
|
clevel = np.linspace(zPlot.min(),zPlot.max(),10)
|
||||||
|
if colorNorm=='SymLog':
|
||||||
|
plotNorm = colors.SymLogNorm(1e-10,linscale=2)
|
||||||
|
else:
|
||||||
|
plotNorm = colors.Normalize()
|
||||||
|
elif par == 'imag':
|
||||||
|
zPlot = np.imag(array[flag][indUniFreq])
|
||||||
|
cmap = plt.get_cmap('RdYlBu')
|
||||||
|
level = np.concatenate((-np.logspace(0,-10,31),np.logspace(-10,0,31)))
|
||||||
|
clevel = np.concatenate((-np.logspace(0,-8,5),np.logspace(-8,0,5)))
|
||||||
|
plotNorm = colors.SymLogNorm(1e-10,linscale=2)
|
||||||
|
if cLevel:
|
||||||
|
level = np.concatenate((-np.logspace(0,-10,31),np.logspace(-10,0,31)))
|
||||||
|
clevel = np.concatenate((-np.logspace(0,-8,5),np.logspace(-8,0,5)))
|
||||||
|
else:
|
||||||
|
level = np.linspace(zPlot.min(),zPlot.max(),100)
|
||||||
|
clevel = np.linspace(zPlot.min(),zPlot.max(),10)
|
||||||
|
if colorNorm=='SymLog':
|
||||||
|
plotNorm = colors.SymLogNorm(1e-10,linscale=2)
|
||||||
|
elif colorNorm=='Lin':
|
||||||
|
plotNorm = colors.Normalize()
|
||||||
|
if contour:
|
||||||
|
cs = ax.tricontourf(x,y,zPlot,levels=level,cmap=cmap,norm=plotNorm)#,extend='both')
|
||||||
|
else:
|
||||||
|
uniX,uniY = np.unique(x),np.unique(y)
|
||||||
|
X,Y = np.meshgrid(np.append(uniX-25,uniX[-1]+25),np.append(uniY-25,uniY[-1]+25))
|
||||||
|
cs = ax.pcolor(X,Y,np.reshape(zPlot,(len(uniY),len(uniX))),cmap=cmap,norm=plotNorm)
|
||||||
|
if colorbar:
|
||||||
|
plt.colorbar(cs,cax=ax.cax,ticks=clevel,format='%1.2e')
|
||||||
|
ax.set_title(flag+' '+par,fontsize=8)
|
||||||
|
return cs
|
||||||
|
|
||||||
|
def plotIsoFreqNSDiff(ax,freq,arrayList,flag,par='abs',colorbar=True,cLevel=True,mask=None,contourLine=True,useLog=False):
|
||||||
|
|
||||||
|
indUniFreq0 = np.where(freq==arrayList[0]['freq'])
|
||||||
|
indUniFreq1 = np.where(freq==arrayList[1]['freq'])
|
||||||
|
seicmap = plt.get_cmap('RdYlBu')#seismic')
|
||||||
|
x, y = arrayList[0]['x'][indUniFreq0],arrayList[0]['y'][indUniFreq0]
|
||||||
|
if par == 'abs':
|
||||||
|
if useLog:
|
||||||
|
zPlot = (np.log10(np.abs(arrayList[0][flag][indUniFreq0])) - np.log10(np.abs(arrayList[1][flag][indUniFreq1])))/np.log10(np.abs(arrayList[1][flag][indUniFreq1]))
|
||||||
|
else:
|
||||||
|
zPlot = (np.abs(arrayList[0][flag][indUniFreq0]) - np.abs(arrayList[1][flag][indUniFreq1]))/np.abs(arrayList[1][flag][indUniFreq1])
|
||||||
|
if mask:
|
||||||
|
maskInd = np.logical_or(np.abs(arrayList[0][flag][indUniFreq0])< 1e-3,np.abs(arrayList[1][flag][indUniFreq1]) < 1e-3)
|
||||||
|
zPlot = np.ma.array(zPlot)
|
||||||
|
zPlot[maskInd] = mask
|
||||||
|
if cLevel:
|
||||||
|
level = np.arange(-200,201,10)
|
||||||
|
clevel = np.arange(-200,201,25)
|
||||||
|
else:
|
||||||
|
level = np.linspace(zPlot.min(),zPlot.max(),100)
|
||||||
|
clevel = np.linspace(zPlot.min(),zPlot.max(),10)
|
||||||
|
elif par == 'real':
|
||||||
|
if useLog:
|
||||||
|
zPlot = (np.log10(np.real(arrayList[0][flag][indUniFreq0])) -np.log10(np.real(arrayList[1][flag][indUniFreq1])))/np.log10(np.abs((np.real(arrayList[1][flag][indUniFreq1]))))
|
||||||
|
else:
|
||||||
|
zPlot = (np.real(arrayList[0][flag][indUniFreq0]) -np.real(arrayList[1][flag][indUniFreq1]))/np.abs((np.real(arrayList[1][flag][indUniFreq1])))
|
||||||
|
if mask:
|
||||||
|
maskInd = np.logical_or(np.abs(np.real(arrayList[0][flag][indUniFreq0])) < 1e-3,np.abs(np.real(arrayList[1][flag][indUniFreq1])) < 1e-3)
|
||||||
|
zPlot = np.ma.array(zPlot)
|
||||||
|
zPlot[maskInd] = mask
|
||||||
|
if cLevel:
|
||||||
|
level = np.arange(-200,201,10)
|
||||||
|
clevel = np.arange(-200,201,25)
|
||||||
|
else:
|
||||||
|
level = np.linspace(zPlot.min(),zPlot.max(),100)
|
||||||
|
clevel = np.linspace(zPlot.min(),zPlot.max(),10)
|
||||||
|
elif par == 'imag':
|
||||||
|
if useLog:
|
||||||
|
zPlot = (np.log10(np.imag(arrayList[0][flag][indUniFreq0])) -np.log10(np.imag(arrayList[1][flag][indUniFreq1])))/np.log10(np.abs((np.imag(arrayList[1][flag][indUniFreq1]))))
|
||||||
|
else:
|
||||||
|
zPlot = (np.imag(arrayList[0][flag][indUniFreq0]) -np.imag(arrayList[1][flag][indUniFreq1]))/np.abs((np.imag(arrayList[1][flag][indUniFreq1])))
|
||||||
|
if mask:
|
||||||
|
maskInd = np.logical_or(np.abs(np.imag(arrayList[0][flag][indUniFreq0])) < 1e-3,np.abs(np.imag(arrayList[1][flag][indUniFreq1])) < 1e-3)
|
||||||
|
zPlot = np.ma.array(zPlot)
|
||||||
|
zPlot[maskInd] = mask
|
||||||
|
if cLevel:
|
||||||
|
level = np.arange(-200,201,10)
|
||||||
|
clevel = np.arange(-200,201,25)
|
||||||
|
else:
|
||||||
|
level = np.linspace(zPlot.min(),zPlot.max(),100)
|
||||||
|
clevel = np.linspace(zPlot.min(),zPlot.max(),10)
|
||||||
|
cs = ax.tricontourf(x,y,zPlot*100,levels=level*100,cmap=seicmap,extend='both') #,norm=colors.SymLogNorm(1e-2,linscale=2))
|
||||||
|
if contourLine:
|
||||||
|
csl = ax.tricontour(x,y,zPlot*100,levels=clevel*100,colors='k')
|
||||||
|
plt.clabel(csl, fontsize=7, inline=1,fmt='%1.1e',inline_spacing=10)
|
||||||
|
if colorbar:
|
||||||
|
cb = plt.colorbar(cs,cax=ax.cax,ticks=clevel*100,format='%1.1e')
|
||||||
|
for t in cb.ax.get_yticklabels():
|
||||||
|
t.set_rotation(60)
|
||||||
|
t.set_fontsize(8)
|
||||||
|
|
||||||
|
ax.set_title(flag+' '+par,fontsize=8)
|
||||||
|
|
||||||
|
def plotIsoFreqNStipper(ax,freq,array,flag,par='abs',colorbar=True,colorNorm='SymLog',cLevel=True,contour=True):
|
||||||
|
|
||||||
|
indUniFreq = np.where(freq==array['freq'])
|
||||||
|
|
||||||
|
x, y = array['x'][indUniFreq],array['y'][indUniFreq]
|
||||||
|
if par == 'abs':
|
||||||
|
cmap = plt.get_cmap('OrRd_r')#seismic')
|
||||||
|
zPlot = np.abs(array[flag][indUniFreq])
|
||||||
|
if cLevel:
|
||||||
|
level = np.logspace(-4,0,33)
|
||||||
|
clevel = np.logspace(-4,0,5)
|
||||||
|
else:
|
||||||
|
level = np.linspace(zPlot.min(),zPlot.max(),100)
|
||||||
|
clevel = np.linspace(zPlot.min(),zPlot.max(),10)
|
||||||
|
if colorNorm=='SymLog':
|
||||||
|
plotNorm = colors.LogNorm()
|
||||||
|
else:
|
||||||
|
plotNorm = colors.Normalize()
|
||||||
|
elif par == 'real':
|
||||||
|
cmap = plt.get_cmap('RdYlBu')
|
||||||
|
zPlot = np.real(array[flag][indUniFreq])
|
||||||
|
if cLevel:
|
||||||
|
level = np.concatenate((-np.logspace(0,-4,33),np.logspace(-4,0,33)))
|
||||||
|
clevel = np.concatenate((-np.logspace(0,-4,5),np.logspace(-4,0,5)))
|
||||||
|
else:
|
||||||
|
level = np.linspace(zPlot.min(),zPlot.max(),100)
|
||||||
|
clevel = np.linspace(zPlot.min(),zPlot.max(),10)
|
||||||
|
if colorNorm=='SymLog':
|
||||||
|
plotNorm = colors.SymLogNorm(1e-4,linscale=2)
|
||||||
|
else:
|
||||||
|
plotNorm = colors.Normalize()
|
||||||
|
elif par == 'imag':
|
||||||
|
cmap = plt.get_cmap('RdYlBu')
|
||||||
|
zPlot = np.imag(array[flag][indUniFreq])
|
||||||
|
if cLevel:
|
||||||
|
level = np.concatenate((-np.logspace(0,-4,33),np.logspace(-4,0,33)))
|
||||||
|
clevel = np.concatenate((-np.logspace(0,-4,5),np.logspace(-4,0,5)))
|
||||||
|
else:
|
||||||
|
level = np.linspace(zPlot.min(),zPlot.max(),100)
|
||||||
|
clevel = np.linspace(zPlot.min(),zPlot.max(),10)
|
||||||
|
if colorNorm=='SymLog':
|
||||||
|
plotNorm = colors.SymLogNorm(1e-4,linscale=2)
|
||||||
|
else:
|
||||||
|
plotNorm = colors.Normalize()
|
||||||
|
if contour:
|
||||||
|
cs = ax.tricontourf(x,y,zPlot,levels=level,cmap=cmap,norm=plotNorm)#,extend='both')
|
||||||
|
else:
|
||||||
|
uniX,uniY = np.unique(x),np.unique(y)
|
||||||
|
X,Y = np.meshgrid(np.append(uniX-25,uniX[-1]+25),np.append(uniY-25,uniY[-1]+25))
|
||||||
|
cs = ax.pcolor(X,Y,np.reshape(zPlot,(len(uniY),len(uniX))),levels=level,cmap=cmap,norm=plotNorm,edgecolors='k', linewidths=0.5)
|
||||||
|
if colorbar:
|
||||||
|
plt.colorbar(cs,cax=ax.cax,ticks=clevel,format='%1.2e')
|
||||||
|
ax.set_title(flag+' '+par,fontsize=8)
|
||||||
|
|
||||||
|
def plotIsoStaImpedance(ax,loc,array,flag,par='abs',pSym='s',pColor=None):
|
||||||
|
|
||||||
|
appResFact = 1/(8*np.pi**2*10**(-7))
|
||||||
|
treshold = 1.0 # 1 meter
|
||||||
|
indUniSta = np.sqrt(np.sum((rec2nd(array[['x','y']])-loc)**2,axis=1)) < treshold
|
||||||
|
freq = array['freq'][indUniSta]
|
||||||
|
|
||||||
|
if par == 'abs':
|
||||||
|
zPlot = np.abs(array[flag][indUniSta])
|
||||||
|
elif par == 'real':
|
||||||
|
zPlot = np.real(array[flag][indUniSta])
|
||||||
|
elif par == 'imag':
|
||||||
|
zPlot = np.imag(array[flag][indUniSta])
|
||||||
|
elif par == 'res':
|
||||||
|
zPlot = (appResFact/freq)*np.abs(array[flag][indUniSta])**2
|
||||||
|
elif par == 'phs':
|
||||||
|
zPlot = np.arctan2(array[flag][indUniSta].imag,array[flag][indUniSta].real)*(180/np.pi)
|
||||||
|
|
||||||
|
if not pColor:
|
||||||
|
if 'xx' in flag:
|
||||||
|
lab = 'XX'
|
||||||
|
pColor = 'g'
|
||||||
|
elif 'xy' in flag:
|
||||||
|
lab = 'XY'
|
||||||
|
pColor = 'r'
|
||||||
|
elif 'yx' in flag:
|
||||||
|
lab = 'YX'
|
||||||
|
pColor = 'b'
|
||||||
|
elif 'yy' in flag:
|
||||||
|
lab = 'YY'
|
||||||
|
pColor = 'y'
|
||||||
|
|
||||||
|
ax.plot(freq,zPlot,color=pColor,marker=pSym,label=flag)
|
||||||
|
|
||||||
|
|
||||||
|
def plotPsudoSectNSimpedance(ax,sectDict,array,flag,par='abs',colorbar=True,colorNorm='None',cLevel=None,contour=True):
|
||||||
|
|
||||||
|
indSect = np.where(sectDict.values()[0]==array[sectDict.keys()[0]])
|
||||||
|
|
||||||
|
# Define the plot axes
|
||||||
|
if 'x' in sectDict.keys()[0]:
|
||||||
|
x = array['y'][indSect]
|
||||||
|
else:
|
||||||
|
x = array['x'][indSect]
|
||||||
|
y = array['freq'][indSect]
|
||||||
|
|
||||||
|
if par == 'abs':
|
||||||
|
zPlot = np.abs(array[flag][indSect])
|
||||||
|
cmap = plt.get_cmap('OrRd_r')#seismic')
|
||||||
|
if cLevel:
|
||||||
|
level = np.logspace(0,-5,31,endpoint=True)
|
||||||
|
clevel = np.logspace(0,-4,5,endpoint=True)
|
||||||
|
else:
|
||||||
|
level = np.linspace(zPlot.min(),zPlot.max(),100,endpoint=True)
|
||||||
|
clevel = np.linspace(zPlot.min(),zPlot.max(),10,endpoint=True)
|
||||||
|
|
||||||
|
elif par == 'ares':
|
||||||
|
zPlot = np.abs(array[flag][indSect])**2/(8*np.pi**2*10**(-7)*array['freq'][indSect])
|
||||||
|
cmap = plt.get_cmap('RdYlBu')#seismic)
|
||||||
|
if cLevel:
|
||||||
|
zMax = np.log10(cLevel[1])
|
||||||
|
zMin = np.log10(cLevel[0])
|
||||||
|
else:
|
||||||
|
zMax = (np.ceil(np.log10(np.abs(zPlot).max())))
|
||||||
|
zMin = (np.floor(np.log10(np.abs(zPlot).min())))
|
||||||
|
level = np.logspace(zMin,zMax,(zMax-zMin)*8+1,endpoint=True)
|
||||||
|
clevel = np.logspace(zMin,zMax,(zMax-zMin)*2+1,endpoint=True)
|
||||||
|
plotNorm = colors.LogNorm()
|
||||||
|
|
||||||
|
elif par == 'aphs':
|
||||||
|
zPlot = np.arctan2(array[flag][indSect].imag,array[flag][indSect].real)*(180/np.pi)
|
||||||
|
cmap = plt.get_cmap('RdYlBu')#seismic)
|
||||||
|
if cLevel:
|
||||||
|
zMax = cLevel[1]
|
||||||
|
zMin = cLevel[0]
|
||||||
|
else:
|
||||||
|
zMax = (np.ceil(zPlot).max())
|
||||||
|
zMin = (np.floor(zPlot).min())
|
||||||
|
level = np.arange(zMin,zMax+.1,1)
|
||||||
|
clevel = np.arange(zMin,zMax+.1,10)
|
||||||
|
plotNorm = colors.Normalize()
|
||||||
|
|
||||||
|
elif par == 'real':
|
||||||
|
zPlot = np.real(array[flag][indSect])
|
||||||
|
cmap = plt.get_cmap('Spectral') #('RdYlBu')
|
||||||
|
if cLevel:
|
||||||
|
zMax = np.log10(cLevel[1])
|
||||||
|
zMin = np.log10(cLevel[0])
|
||||||
|
else:
|
||||||
|
zMax = (np.ceil(np.log10(np.abs(zPlot).max())))
|
||||||
|
zMin = (np.floor(np.log10(np.abs(zPlot).min())))
|
||||||
|
level = np.concatenate((-np.logspace(zMax,zMin-.125,(zMax-zMin)*8+1,endpoint=True),np.logspace(zMin-.125,zMax,(zMax-zMin)*8+1,endpoint=True)))
|
||||||
|
clevel = np.concatenate((-np.logspace(zMax,zMin,(zMax-zMin)*1+1,endpoint=True),np.logspace(zMin,zMax,(zMax-zMin)*1+1,endpoint=True)))
|
||||||
|
plotNorm = colors.SymLogNorm(np.abs(level).min(),linscale=0.1)
|
||||||
|
elif par == 'imag':
|
||||||
|
zPlot = np.imag(array[flag][indSect])
|
||||||
|
cmap = plt.get_cmap('Spectral') #('RdYlBu')
|
||||||
|
|
||||||
|
if cLevel:
|
||||||
|
zMax = np.log10(cLevel[1])
|
||||||
|
zMin = np.log10(cLevel[0])
|
||||||
|
else:
|
||||||
|
zMax = (np.ceil(np.log10(np.abs(zPlot).max())))
|
||||||
|
zMin = (np.floor(np.log10(np.abs(zPlot).min())))
|
||||||
|
level = np.concatenate((-np.logspace(zMax,zMin-.125,(zMax-zMin)*8+1,endpoint=True),np.logspace(zMin-.125,zMax,(zMax-zMin)*8+1,endpoint=True)))
|
||||||
|
clevel = np.concatenate((-np.logspace(zMax,zMin,(zMax-zMin)*1+1,endpoint=True),np.logspace(zMin,zMax,(zMax-zMin)*1+1,endpoint=True)))
|
||||||
|
plotNorm = colors.SymLogNorm(np.abs(level).min(),linscale=0.1)
|
||||||
|
|
||||||
|
if colorNorm=='SymLog':
|
||||||
|
plotNorm = colors.SymLogNorm(np.abs(level).min(),linscale=0.1)
|
||||||
|
elif colorNorm=='Lin':
|
||||||
|
plotNorm = colors.Normalize()
|
||||||
|
elif colorNorm=='Log':
|
||||||
|
plotNorm = colors.LogNorm()
|
||||||
|
if contour:
|
||||||
|
cs = ax.tricontourf(x,y,zPlot,levels=level,cmap=cmap,norm=plotNorm)#,extend='both')
|
||||||
|
else:
|
||||||
|
uniX,uniY = np.unique(x),np.unique(y)
|
||||||
|
X,Y = np.meshgrid(np.append(uniX-25,uniX[-1]+25),np.append(uniY-25,uniY[-1]+25))
|
||||||
|
cs = ax.pcolor(X,Y,np.reshape(zPlot,(len(uniY),len(uniX))),cmap=cmap,norm=plotNorm)
|
||||||
|
if colorbar:
|
||||||
|
csB = plt.colorbar(cs,cax=ax.cax,ticks=clevel,format='%1.2e')
|
||||||
|
# csB.on_mappable_changed(cs)
|
||||||
|
ax.set_title(flag+' '+par,fontsize=8)
|
||||||
|
return cs, csB
|
||||||
|
return cs,None
|
||||||
|
|
||||||
|
|
||||||
|
def plotPsudoSectNSDiff(ax,sectDict,arrayList,flag,par='abs',colorbar=True,colorNorm='SymLog',cLevel=None,contour=True,mask=None,useLog=False):
|
||||||
|
|
||||||
|
def sortInArr(arr):
|
||||||
|
return np.sort(arr,order=['freq','x','y','z'])
|
||||||
|
# Find the index for the slice
|
||||||
|
indSect0 = np.where(sectDict.values()[0]==arrayList[0][sectDict.keys()[0]])
|
||||||
|
indSect1 = np.where(sectDict.values()[0]==arrayList[1][sectDict.keys()[0]])
|
||||||
|
# Extract and sort the mats
|
||||||
|
arr0 = sortInArr(arrayList[0][indSect0])
|
||||||
|
arr1 = sortInArr(arrayList[1][indSect1])
|
||||||
|
|
||||||
|
# Define the plot axes
|
||||||
|
if 'x' in sectDict.keys()[0]:
|
||||||
|
x0 = arr0['y']
|
||||||
|
x1 = arr1['y']
|
||||||
|
else:
|
||||||
|
x0 = arr0['x']
|
||||||
|
x1 = arr1['x']
|
||||||
|
y0 = arr0['freq']
|
||||||
|
y1 = arr1['freq']
|
||||||
|
|
||||||
|
|
||||||
|
if par == 'abs':
|
||||||
|
if useLog:
|
||||||
|
zPlot = (np.log10(np.abs(arr0[flag])) - np.log10(np.abs(arr1[flag])))/np.log10(np.abs(arr1[flag]))
|
||||||
|
else:
|
||||||
|
zPlot = (np.abs(arr0[flag]) - np.abs(arr1[flag]))/np.abs(arr1[flag])
|
||||||
|
if mask:
|
||||||
|
maskInd = np.logical_or(np.abs(arr0[flag])< 1e-3,np.abs(arr1[flag]) < 1e-3)
|
||||||
|
zPlot = np.ma.array(zPlot)
|
||||||
|
zPlot[maskInd] = mask
|
||||||
|
cmap = plt.get_cmap('RdYlBu')#seismic)
|
||||||
|
elif par == 'ares':
|
||||||
|
arF = 1/(8*np.pi**2*10**(-7))
|
||||||
|
if useLog:
|
||||||
|
zPlot = (np.log10((arF/arr0['freq'])*np.abs(arr0[flag])**2) - np.log10((arF/arr1['freq'])*np.abs(arr1[flag])**2))/np.log10((arF/arr1['freq'])*np.abs(arr1[flag])**2)
|
||||||
|
else:
|
||||||
|
zPlot = ((arF/arr0['freq'])*np.abs(arr0[flag])**2 - (arF/arr1['freq'])*np.abs(arr1[flag])**2)/((arF/arr1['freq'])*np.abs(arr1[flag])**2)
|
||||||
|
if mask:
|
||||||
|
maskInd = np.logical_or(np.abs(arr0[flag])< 1e-3,np.abs(arr1[flag]) < 1e-3)
|
||||||
|
zPlot = np.ma.array(zPlot)
|
||||||
|
zPlot[maskInd] = mask
|
||||||
|
cmap = plt.get_cmap('Spectral')#seismic)
|
||||||
|
|
||||||
|
elif par == 'aphs':
|
||||||
|
if useLog:
|
||||||
|
zPlot = (np.log10(np.arctan2(arr0[flag].imag,arr0[flag].real)*(180/np.pi)) - np.log10(np.arctan2(arr1[flag].imag,arr1[flag].real)*(180/np.pi)) )/np.log10(np.arctan2(arr1[flag].imag,arr1[flag].real)*(180/np.pi))
|
||||||
|
else:
|
||||||
|
zPlot = ( np.arctan2(arr0[flag].imag,arr0[flag].real)*(180/np.pi) - np.arctan2(arr1[flag].imag,arr1[flag].real)*(180/np.pi) )/(np.arctan2(arr1[flag].imag,arr1[flag].real)*(180/np.pi))
|
||||||
|
if mask:
|
||||||
|
maskInd = np.logical_or(np.abs(arr0[flag])< 1e-3,np.abs(arr1[flag]) < 1e-3)
|
||||||
|
zPlot = np.ma.array(zPlot)
|
||||||
|
zPlot[maskInd] = mask
|
||||||
|
cmap = plt.get_cmap('Spectral')#seismic)
|
||||||
|
elif par == 'real':
|
||||||
|
if useLog:
|
||||||
|
zPlot = (np.log10(arr0[flag].real) - np.log10(arr1[flag].real))/np.log10(arr1[flag].real)
|
||||||
|
else:
|
||||||
|
zPlot = (arr0[flag].real - arr1[flag].real)/arr1[flag].real
|
||||||
|
if mask:
|
||||||
|
maskInd = np.logical_or(arr0[flag].real< 1e-3,arr1[flag].real < 1e-3)
|
||||||
|
zPlot = np.ma.array(zPlot)
|
||||||
|
zPlot[maskInd] = mask
|
||||||
|
cmap = plt.get_cmap('Spectral') #('Spectral')
|
||||||
|
|
||||||
|
elif par == 'imag':
|
||||||
|
if useLog:
|
||||||
|
zPlot = (np.log10(arr0[flag].imag) - np.log10(arr1[flag].imag))/np.log10(arr1[flag].imag)
|
||||||
|
else:
|
||||||
|
zPlot = (arr0[flag].imag - arr1[flag].imag)/arr1[flag].imag
|
||||||
|
if mask:
|
||||||
|
maskInd = np.logical_or(arr0[flag].imag< 1e-3,arr1[flag].imag < 1e-3)
|
||||||
|
zPlot = np.ma.array(zPlot)
|
||||||
|
zPlot[maskInd] = mask
|
||||||
|
cmap = plt.get_cmap('Spectral') #('RdYlBu')
|
||||||
|
|
||||||
|
if cLevel:
|
||||||
|
zMax = np.log10(cLevel[1])
|
||||||
|
zMin = np.log10(cLevel[0])
|
||||||
|
else:
|
||||||
|
zMax = (np.ceil(np.log10(np.abs(zPlot).max())))
|
||||||
|
zMin = (np.floor(np.log10(np.abs(zPlot).min())))
|
||||||
|
|
||||||
|
|
||||||
|
if colorNorm=='SymLog':
|
||||||
|
level = np.concatenate((-np.logspace(zMax,zMin-.125,(zMax-zMin)*8+1,endpoint=True),np.logspace(zMin-.125,zMax,(zMax-zMin)*8+1,endpoint=True)))
|
||||||
|
clevel = np.concatenate((-np.logspace(zMax,zMin,(zMax-zMin)*1+1,endpoint=True),np.logspace(zMin,zMax,(zMax-zMin)*1+1,endpoint=True)))
|
||||||
|
plotNorm = colors.SymLogNorm(np.abs(level).min(),linscale=0.1)
|
||||||
|
elif colorNorm=='Lin':
|
||||||
|
if cLevel:
|
||||||
|
level = np.arange(cLevel[0],cLevel[1]+.1,(cLevel[1] - cLevel[0])/50.)
|
||||||
|
clevel = np.arange(cLevel[0],cLevel[1]+.1,(cLevel[1] - cLevel[0])/10.)
|
||||||
|
else:
|
||||||
|
level = np.arange(zPlot.min(),zPlot.max(),(zPlot.max() - zPlot.min())/50.)
|
||||||
|
clevel = np.arange(zPlot.min(),zPlot.max(),(zPlot.max() - zPlot.min())/10.)
|
||||||
|
plotNorm = colors.Normalize()
|
||||||
|
elif colorNorm=='Log':
|
||||||
|
level = np.logspace(zMin-.125,zMax,(zMax-zMin)*8+1,endpoint=True)
|
||||||
|
clevel = np.logspace(zMin,zMax,(zMax-zMin)*2+1,endpoint=True)
|
||||||
|
plotNorm = colors.LogNorm()
|
||||||
|
if contour:
|
||||||
|
cs = ax.tricontourf(x0,y0,zPlot*100,levels=level*100,cmap=cmap,norm=plotNorm,extend='both')#,extend='both')
|
||||||
|
else:
|
||||||
|
uniX,uniY = np.unique(x0),np.unique(y0)
|
||||||
|
X,Y = np.meshgrid(np.append(uniX-25,uniX[-1]+25),np.append(uniY-25,uniY[-1]+25))
|
||||||
|
cs = ax.pcolor(X,Y,np.reshape(zPlot,(len(uniY),len(uniX))),cmap=cmap,norm=plotNorm)
|
||||||
|
if colorbar:
|
||||||
|
csB = plt.colorbar(cs,cax=ax.cax,ticks=clevel*100,format='%1.2e')
|
||||||
|
# csB.on_mappable_changed(cs)
|
||||||
|
ax.set_title(flag+' '+par + ' diff',fontsize=8)
|
||||||
|
return cs, csB
|
||||||
|
return cs,None
|
||||||
@@ -0,0 +1,178 @@
|
|||||||
|
import SimPEG as simpeg, numpy as np
|
||||||
|
|
||||||
|
def homo1DModelSource(mesh,freq,sigma_1d):
|
||||||
|
'''
|
||||||
|
Function that calculates and return background fields
|
||||||
|
|
||||||
|
:param Simpeg mesh object mesh: Holds information on the discretization
|
||||||
|
:param float freq: The frequency to solve at
|
||||||
|
:param np.array sigma_1d: Background model of conductivity to base the calculations on, 1d model.
|
||||||
|
:rtype: numpy.ndarray (mesh.nE,2)
|
||||||
|
:return: eBG_bp, E fields for the background model at both polarizations.
|
||||||
|
|
||||||
|
'''
|
||||||
|
# import
|
||||||
|
from SimPEG.MT.Utils import get1DEfields
|
||||||
|
# Get a 1d solution for a halfspace background
|
||||||
|
if mesh.dim == 1:
|
||||||
|
mesh1d = mesh
|
||||||
|
elif mesh.dim == 2:
|
||||||
|
mesh1d = simpeg.Mesh.TensorMesh([mesh.hy],np.array([mesh.x0[1]]))
|
||||||
|
elif mesh.dim == 3:
|
||||||
|
mesh1d = simpeg.Mesh.TensorMesh([mesh.hz],np.array([mesh.x0[2]]))
|
||||||
|
|
||||||
|
# # Note: Everything is using e^iwt
|
||||||
|
e0_1d = get1DEfields(mesh1d,sigma_1d,freq)
|
||||||
|
if mesh.dim == 1:
|
||||||
|
eBG_px = simpeg.mkvc(e0_1d,2)
|
||||||
|
eBG_py = -simpeg.mkvc(e0_1d,2) # added a minus to make the results in the correct quadrents.
|
||||||
|
elif mesh.dim == 2:
|
||||||
|
ex_px = np.zeros(mesh.vnEx,dtype=complex)
|
||||||
|
ey_px = np.zeros((mesh.nEy,1),dtype=complex)
|
||||||
|
for i in np.arange(mesh.vnEx[0]):
|
||||||
|
ex_px[i,:] = -e0_1d
|
||||||
|
eBG_px = np.vstack((simpeg.Utils.mkvc(ex_px,2),ey_px))
|
||||||
|
# Setup y (north) polarization (_py)
|
||||||
|
ex_py = np.zeros((mesh.nEx,1), dtype='complex128')
|
||||||
|
ey_py = np.zeros(mesh.vnEy, dtype='complex128')
|
||||||
|
# Assign the source to ey_py
|
||||||
|
for i in np.arange(mesh.vnEy[0]):
|
||||||
|
ey_py[i,:] = e0_1d
|
||||||
|
# ey_py[1:-1,1:-1,1:-1] = 0
|
||||||
|
eBG_py = np.vstack((ex_py,simpeg.Utils.mkvc(ey_py,2),ez_py))
|
||||||
|
elif mesh.dim == 3:
|
||||||
|
# Setup x (east) polarization (_x)
|
||||||
|
ex_px = np.zeros(mesh.vnEx,dtype=complex)
|
||||||
|
ey_px = np.zeros((mesh.nEy,1),dtype=complex)
|
||||||
|
ez_px = np.zeros((mesh.nEz,1),dtype=complex)
|
||||||
|
# Assign the source to ex_x
|
||||||
|
for i in np.arange(mesh.vnEx[0]):
|
||||||
|
for j in np.arange(mesh.vnEx[1]):
|
||||||
|
ex_px[i,j,:] = -e0_1d
|
||||||
|
eBG_px = np.vstack((simpeg.Utils.mkvc(ex_px,2),ey_px,ez_px))
|
||||||
|
# Setup y (north) polarization (_py)
|
||||||
|
ex_py = np.zeros((mesh.nEx,1), dtype='complex128')
|
||||||
|
ey_py = np.zeros(mesh.vnEy, dtype='complex128')
|
||||||
|
ez_py = np.zeros((mesh.nEz,1), dtype='complex128')
|
||||||
|
# Assign the source to ey_py
|
||||||
|
for i in np.arange(mesh.vnEy[0]):
|
||||||
|
for j in np.arange(mesh.vnEy[1]):
|
||||||
|
ey_py[i,j,:] = e0_1d
|
||||||
|
# ey_py[1:-1,1:-1,1:-1] = 0
|
||||||
|
eBG_py = np.vstack((ex_py,simpeg.Utils.mkvc(ey_py,2),ez_py))
|
||||||
|
|
||||||
|
# Return the electric fields
|
||||||
|
eBG_bp = np.hstack((eBG_px,eBG_py))
|
||||||
|
return eBG_bp
|
||||||
|
|
||||||
|
def analytic1DModelSource(mesh,freq,sigma_1d):
|
||||||
|
'''
|
||||||
|
Function that calculates and return background fields
|
||||||
|
|
||||||
|
:param Simpeg mesh object mesh: Holds information on the discretization
|
||||||
|
:param float freq: The frequency to solve at
|
||||||
|
:param np.array sigma_1d: Background model of conductivity to base the calculations on, 1d model.
|
||||||
|
:rtype: numpy.ndarray (mesh.nE,2)
|
||||||
|
:return: eBG_bp, E fields for the background model at both polarizations.
|
||||||
|
|
||||||
|
'''
|
||||||
|
# import
|
||||||
|
from SimPEG.MT.Utils import getEHfields
|
||||||
|
# Get a 1d solution for a halfspace background
|
||||||
|
if mesh.dim == 1:
|
||||||
|
mesh1d = mesh
|
||||||
|
elif mesh.dim == 2:
|
||||||
|
mesh1d = simpeg.Mesh.TensorMesh([mesh.hy],np.array([mesh.x0[1]]))
|
||||||
|
elif mesh.dim == 3:
|
||||||
|
mesh1d = simpeg.Mesh.TensorMesh([mesh.hz],np.array([mesh.x0[2]]))
|
||||||
|
|
||||||
|
# # Note: Everything is using e^iwt
|
||||||
|
Eu, Ed, _, _ = getEHfields(mesh1d,sigma_1d,freq,mesh.vectorNz)
|
||||||
|
# Make the fields into a dictionary of location and the fields
|
||||||
|
e0_1d = Eu+Ed
|
||||||
|
E1dFieldDict = dict(zip(mesh.vectorNz,e0_1d))
|
||||||
|
if mesh.dim == 1:
|
||||||
|
eBG_px = simpeg.mkvc(e0_1d,2)
|
||||||
|
eBG_py = -simpeg.mkvc(e0_1d,2) # added a minus to make the results in the correct quadrents.
|
||||||
|
elif mesh.dim == 2:
|
||||||
|
ex_px = np.zeros(mesh.vnEx,dtype=complex)
|
||||||
|
ey_px = np.zeros((mesh.nEy,1),dtype=complex)
|
||||||
|
for i in np.arange(mesh.vnEx[0]):
|
||||||
|
ex_px[i,:] = -e0_1d
|
||||||
|
eBG_px = np.vstack((simpeg.Utils.mkvc(ex_px,2),ey_px))
|
||||||
|
# Setup y (north) polarization (_py)
|
||||||
|
ex_py = np.zeros((mesh.nEx,1), dtype='complex128')
|
||||||
|
ey_py = np.zeros(mesh.vnEy, dtype='complex128')
|
||||||
|
# Assign the source to ey_py
|
||||||
|
for i in np.arange(mesh.vnEy[0]):
|
||||||
|
ey_py[i,:] = e0_1d
|
||||||
|
# ey_py[1:-1,1:-1,1:-1] = 0
|
||||||
|
eBG_py = np.vstack((ex_py,simpeg.Utils.mkvc(ey_py,2),ez_py))
|
||||||
|
elif mesh.dim == 3:
|
||||||
|
# Setup x (east) polarization (_x)
|
||||||
|
ex_px = -np.array([E1dFieldDict[i] for i in mesh.gridEx[:,2]]).reshape(-1,1)
|
||||||
|
ey_px = np.zeros((mesh.nEy,1),dtype=complex)
|
||||||
|
ez_px = np.zeros((mesh.nEz,1),dtype=complex)
|
||||||
|
# Construct the full fields
|
||||||
|
eBG_px = np.vstack((ex_px,ey_px,ez_px))
|
||||||
|
# Setup y (north) polarization (_py)
|
||||||
|
ex_py = np.zeros((mesh.nEx,1), dtype='complex128')
|
||||||
|
ey_py = np.array([E1dFieldDict[i] for i in mesh.gridEy[:,2]]).reshape(-1,1)
|
||||||
|
ez_py = np.zeros((mesh.nEz,1), dtype='complex128')
|
||||||
|
# Construct the full fields
|
||||||
|
eBG_py = np.vstack((ex_py,simpeg.Utils.mkvc(ey_py,2),ez_py))
|
||||||
|
|
||||||
|
# Return the electric fields
|
||||||
|
eBG_bp = np.hstack((eBG_px,eBG_py))
|
||||||
|
return eBG_bp
|
||||||
|
|
||||||
|
# def homo3DModelSource(mesh,model,freq):
|
||||||
|
# '''
|
||||||
|
# Function that estimates 1D analytic background fields from a 3D model.
|
||||||
|
|
||||||
|
# :param Simpeg mesh object mesh: Holds information on the discretization
|
||||||
|
# :param float freq: The frequency to solve at
|
||||||
|
# :param np.array sigma_1d: Background model of conductivity to base the calculations on, 1d model.
|
||||||
|
# :rtype: numpy.ndarray (mesh.nE,2)
|
||||||
|
# :return: eBG_bp, E fields for the background model at both polarizations.
|
||||||
|
|
||||||
|
# '''
|
||||||
|
|
||||||
|
# if mesh.dim < 3:
|
||||||
|
# raise IOError('Input mesh has to have 3 dimensions.')
|
||||||
|
|
||||||
|
|
||||||
|
# # Get the locations
|
||||||
|
# a = mesh.gridCC[:,0:2].copy()
|
||||||
|
# unixy = np.unique(a.view(a.dtype.descr * a.shape[1])).view(float).reshape(-1,2)
|
||||||
|
# uniz = np.unique(mesh.gridCC[:,2])
|
||||||
|
# # # Note: Everything is using e^iwt
|
||||||
|
# # Need to loop thourgh the xy locations, assess the model and calculate the fields at the phusdo cell centers.
|
||||||
|
# # Then interpolate the cc fields to the edges.
|
||||||
|
|
||||||
|
# e0_1d = get1DEfields(mesh1d,sigma_1d,freq)
|
||||||
|
|
||||||
|
# elif mesh.dim == 3:
|
||||||
|
# # Setup x (east) polarization (_x)
|
||||||
|
# ex_px = np.zeros(mesh.vnEx,dtype=complex)
|
||||||
|
# ey_px = np.zeros((mesh.nEy,1),dtype=complex)
|
||||||
|
# ez_px = np.zeros((mesh.nEz,1),dtype=complex)
|
||||||
|
# # Assign the source to ex_x
|
||||||
|
# for i in np.arange(mesh.vnEx[0]):
|
||||||
|
# for j in np.arange(mesh.vnEx[1]):
|
||||||
|
# ex_px[i,j,:] = -e0_1d
|
||||||
|
# eBG_px = np.vstack((simpeg.Utils.mkvc(ex_px,2),ey_px,ez_px))
|
||||||
|
# # Setup y (north) polarization (_py)
|
||||||
|
# ex_py = np.zeros((mesh.nEx,1), dtype='complex128')
|
||||||
|
# ey_py = np.zeros(mesh.vnEy, dtype='complex128')
|
||||||
|
# ez_py = np.zeros((mesh.nEz,1), dtype='complex128')
|
||||||
|
# # Assign the source to ey_py
|
||||||
|
# for i in np.arange(mesh.vnEy[0]):
|
||||||
|
# for j in np.arange(mesh.vnEy[1]):
|
||||||
|
# ey_py[i,j,:] = e0_1d
|
||||||
|
# # ey_py[1:-1,1:-1,1:-1] = 0
|
||||||
|
# eBG_py = np.vstack((ex_py,simpeg.Utils.mkvc(ey_py,2),ez_py))
|
||||||
|
|
||||||
|
# # Return the electric fields
|
||||||
|
# eBG_bp = np.hstack((eBG_px,eBG_py))
|
||||||
|
# return eBG_bp
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import SimPEG as simpeg, numpy as np
|
||||||
|
|
||||||
|
def homo1DModelSource(mesh,freq,m_back):
|
||||||
|
'''
|
||||||
|
Function that calculates and return background fields for a 3D mesh and model.
|
||||||
|
The calculuations use 1D field solution for a vertical slice throught model (south-western most column),
|
||||||
|
which is assigned at the fields everywhere for the respective polarizations.2
|
||||||
|
|
||||||
|
:param Simpeg mesh object mesh: Holds information on the discretization
|
||||||
|
:param float freq: The frequency to solve at
|
||||||
|
:param np.array m_back: Background model of conductivity to base the calculations on.
|
||||||
|
:rtype: numpy.ndarray (mesh.nE,2)
|
||||||
|
:return: eBG_bp, E fields for the background model at both polarizations.
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
# import
|
||||||
|
from SimPEG.MT.Utils import get1DEfields
|
||||||
|
# Get a 1d solution for a halfspace background
|
||||||
|
mesh1d = simpeg.Mesh.TensorMesh([mesh.hz],np.array([mesh.x0[2]]))
|
||||||
|
# Note: Everything is using e^iwt
|
||||||
|
e0_1d = get1DEfields(mesh1d,mesh.r(m_back,'CC','CC','M')[0,0,:],freq)
|
||||||
|
# Setup x (east) polarization (_x)
|
||||||
|
ex_px = np.zeros(mesh.vnEx,dtype=complex)
|
||||||
|
ey_px = np.zeros((mesh.nEy,1),dtype=complex)
|
||||||
|
ez_px = np.zeros((mesh.nEz,1),dtype=complex)
|
||||||
|
# Assign the source to ex_x
|
||||||
|
for i in np.arange(mesh.vnEx[0]):
|
||||||
|
for j in np.arange(mesh.vnEx[1]):
|
||||||
|
ex_px[i,j,:] = -e0_1d
|
||||||
|
eBG_px = np.vstack((simpeg.Utils.mkvc(ex_px,2),ey_px,ez_px))
|
||||||
|
# Setup y (north) polarization (_py)
|
||||||
|
ex_py = np.zeros((mesh.nEx,1), dtype='complex128')
|
||||||
|
ey_py = np.zeros(mesh.vnEy, dtype='complex128')
|
||||||
|
ez_py = np.zeros((mesh.nEz,1), dtype='complex128')
|
||||||
|
# Assign the source to ey_py
|
||||||
|
|
||||||
|
for i in np.arange(mesh.vnEy[0]):
|
||||||
|
for j in np.arange(mesh.vnEy[1]):
|
||||||
|
ey_py[i,j,:] = e0_1d
|
||||||
|
# ey_py[1:-1,1:-1,1:-1] = 0
|
||||||
|
eBG_py = np.vstack((ex_py,simpeg.Utils.mkvc(ey_py,2),ez_py))
|
||||||
|
|
||||||
|
# Return the electric fields
|
||||||
|
eBG_bp = np.hstack((eBG_px,eBG_py))
|
||||||
|
return eBG_bp
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import Utils
|
||||||
|
from SurveyMT import Rx, Survey, Data
|
||||||
|
from FieldsMT import Fields1D_e, Fields3D_e
|
||||||
|
import Problem1D, Problem2D, Problem3D
|
||||||
|
import SrcMT
|
||||||
@@ -566,57 +566,6 @@ class DiffOperators(object):
|
|||||||
return Pbc, Pin, Pout
|
return Pbc, Pin, Pout
|
||||||
|
|
||||||
|
|
||||||
def unitCellGradx():
|
|
||||||
doc = """Cell centered Gradient in the x dimension used for
|
|
||||||
regularization. The gradient operator is square (nC-by-nC)"""
|
|
||||||
def fget(self):
|
|
||||||
if self.dim < 3: return None
|
|
||||||
if getattr(self, '_unitCellGradx', None) is None:
|
|
||||||
|
|
||||||
n = self.vnC
|
|
||||||
gx = ddx(n[0]-1)
|
|
||||||
gx_square = sp.vstack((gx,gx[-1,:]*-1), format="csr")
|
|
||||||
|
|
||||||
self._unitCellGradx = kron3(speye(n[2]), speye(n[1]), gx_square)
|
|
||||||
|
|
||||||
return self._unitCellGradx
|
|
||||||
return locals()
|
|
||||||
unitCellGradx = property(**unitCellGradx())
|
|
||||||
|
|
||||||
def unitCellGrady():
|
|
||||||
doc = """Cell centered Gradient in they dimension used for
|
|
||||||
regularization. The gradient operator is square (nC-by-nC)"""
|
|
||||||
def fget(self):
|
|
||||||
if self.dim < 3: return None
|
|
||||||
if getattr(self, '_unitCellGrady', None) is None:
|
|
||||||
|
|
||||||
n = self.vnC
|
|
||||||
gy = ddx(n[1]-1)
|
|
||||||
gy_square = sp.vstack((gy,gy[-1,:]*-1), format="csr")
|
|
||||||
|
|
||||||
self._unitCellGrady = kron3(speye(n[2]), gy_square, speye(n[0]))
|
|
||||||
|
|
||||||
return self._unitCellGrady
|
|
||||||
return locals()
|
|
||||||
unitCellGrady = property(**unitCellGrady())
|
|
||||||
|
|
||||||
def unitCellGradz():
|
|
||||||
doc = """Cell centered Gradient in they dimension used for
|
|
||||||
regularization. The gradient operator is square (nC-by-nC)"""
|
|
||||||
def fget(self):
|
|
||||||
if self.dim < 3: return None
|
|
||||||
if getattr(self, '_unitCellGradz', None) is None:
|
|
||||||
|
|
||||||
n = self.vnC
|
|
||||||
gz = ddx(n[2]-1)
|
|
||||||
gz_square = sp.vstack((gz,gz[-1,:]*-1), format="csr")
|
|
||||||
|
|
||||||
self._unitCellGradz = kron3( gz_square , speye(n[1]), speye(n[0]))
|
|
||||||
|
|
||||||
return self._unitCellGradz
|
|
||||||
return locals()
|
|
||||||
unitCellGradz = property(**unitCellGradz())
|
|
||||||
|
|
||||||
# --------------- Averaging ---------------------
|
# --------------- Averaging ---------------------
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -797,4 +746,3 @@ class DiffOperators(object):
|
|||||||
kron3(av(n[2]), speye(n[1]+1), av(n[0])),
|
kron3(av(n[2]), speye(n[1]+1), av(n[0])),
|
||||||
kron3(speye(n[2]+1), av(n[1]), av(n[0]))), format="csr")
|
kron3(speye(n[2]+1), av(n[1]), av(n[0]))), format="csr")
|
||||||
return self._aveN2F
|
return self._aveN2F
|
||||||
|
|
||||||
|
|||||||
@@ -990,19 +990,4 @@ class ProjectedGNCG(BFGS, Minimize, Remember):
|
|||||||
cgFlag = 1
|
cgFlag = 1
|
||||||
# End CG Iterations
|
# End CG Iterations
|
||||||
|
|
||||||
# Take a gradient step on the active cells if exist
|
|
||||||
if temp != self.xc.size:
|
|
||||||
|
|
||||||
rhs_a = (Active) * -self.g
|
|
||||||
|
|
||||||
dm_i = max( abs( delx ) )
|
|
||||||
dm_a = max( abs(rhs_a) )
|
|
||||||
|
|
||||||
delx = delx + rhs_a * dm_i / dm_a /10.
|
|
||||||
|
|
||||||
# Only keep gradients going in the right direction on the active set
|
|
||||||
indx = ((self.xc<=self.lower) & (delx < 0)) | ((self.xc>=self.upper) & (delx > 0))
|
|
||||||
delx[indx] = 0.
|
|
||||||
|
|
||||||
return delx
|
return delx
|
||||||
|
|
||||||
|
|||||||
@@ -281,212 +281,3 @@ class Tikhonov(BaseRegularization):
|
|||||||
r = self.W * ( self.mapping * (m - self.mref) )
|
r = self.W * ( self.mapping * (m - self.mref) )
|
||||||
out = mD.T * ( self.W.T * r )
|
out = mD.T * ( self.W.T * r )
|
||||||
return out
|
return out
|
||||||
|
|
||||||
class Simple(BaseRegularization):
|
|
||||||
"""
|
|
||||||
Only for tensor mesh
|
|
||||||
"""
|
|
||||||
|
|
||||||
smoothModel = True #: SMOOTH and SMOOTH_MOD_DIF options
|
|
||||||
alpha_s = Utils.dependentProperty('_alpha_s', 1.0, ['_W', '_Ws'], "Smallness weight")
|
|
||||||
alpha_x = Utils.dependentProperty('_alpha_x', 1.0, ['_W', '_Wx'], "Weight for the first derivative in the x direction")
|
|
||||||
alpha_y = Utils.dependentProperty('_alpha_y', 1.0, ['_W', '_Wy'], "Weight for the first derivative in the y direction")
|
|
||||||
alpha_z = Utils.dependentProperty('_alpha_z', 1.0, ['_W', '_Wz'], "Weight for the first derivative in the z direction")
|
|
||||||
|
|
||||||
def __init__(self, mesh, mapping=None, **kwargs):
|
|
||||||
BaseRegularization.__init__(self, mesh, mapping=mapping, **kwargs)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def Ws(self):
|
|
||||||
"""Regularization matrix Ws"""
|
|
||||||
if getattr(self,'_Ws', None) is None:
|
|
||||||
self._Ws = Utils.sdiag((self.mesh.vol*self.alpha_s)**0.5)
|
|
||||||
return self._Ws
|
|
||||||
|
|
||||||
@property
|
|
||||||
def Wx(self):
|
|
||||||
"""Regularization matrix Wx"""
|
|
||||||
if getattr(self, '_Wx', None) is None:
|
|
||||||
self._Wx = Utils.sdiag((self.mesh.vol*self.alpha_x)**0.5)*self.mesh.unitCellGradx
|
|
||||||
return self._Wx
|
|
||||||
|
|
||||||
@property
|
|
||||||
def Wy(self):
|
|
||||||
"""Regularization matrix Wy"""
|
|
||||||
if getattr(self, '_Wy', None) is None:
|
|
||||||
self._Wy = Utils.sdiag((self.mesh.vol*self.alpha_y)**0.5)*self.mesh.unitCellGrady
|
|
||||||
return self._Wy
|
|
||||||
|
|
||||||
@property
|
|
||||||
def Wz(self):
|
|
||||||
"""Regularization matrix Wz"""
|
|
||||||
if getattr(self, '_Wz', None) is None:
|
|
||||||
self._Wz = Utils.sdiag((self.mesh.vol*self.alpha_z)**0.5)*self.mesh.unitCellGradz
|
|
||||||
return self._Wz
|
|
||||||
|
|
||||||
@property
|
|
||||||
def Wsmooth(self):
|
|
||||||
"""Full smoothness regularization matrix W"""
|
|
||||||
if getattr(self, '_Wsmooth', None) is None:
|
|
||||||
wlist = (self.Wx,)
|
|
||||||
if self.mesh.dim > 1:
|
|
||||||
wlist += (self.Wy,)
|
|
||||||
if self.mesh.dim > 2:
|
|
||||||
wlist += (self.Wz,)
|
|
||||||
self._Wsmooth = sp.vstack(wlist)
|
|
||||||
return self._Wsmooth
|
|
||||||
|
|
||||||
@property
|
|
||||||
def W(self):
|
|
||||||
"""Full regularization matrix W"""
|
|
||||||
if getattr(self, '_W', None) is None:
|
|
||||||
wlist = (self.Ws, self.Wsmooth)
|
|
||||||
self._W = sp.vstack(wlist)
|
|
||||||
return self._W
|
|
||||||
|
|
||||||
@Utils.timeIt
|
|
||||||
def eval(self, m):
|
|
||||||
if self.smoothModel == True:
|
|
||||||
r1 = self.Wsmooth * ( self.mapping * (m) )
|
|
||||||
r2 = self.Ws * ( self.mapping * (m - self.mref) )
|
|
||||||
return 0.5*(r1.dot(r1)+r2.dot(r2))
|
|
||||||
elif self.smoothModel == False:
|
|
||||||
r = self.W * ( self.mapping * (m - self.mref) )
|
|
||||||
return 0.5*r.dot(r)
|
|
||||||
|
|
||||||
|
|
||||||
@Utils.timeIt
|
|
||||||
def evalDeriv(self, m):
|
|
||||||
"""
|
|
||||||
|
|
||||||
The regularization is:
|
|
||||||
|
|
||||||
.. math::
|
|
||||||
|
|
||||||
R(m) = \\frac{1}{2}\mathbf{(m-m_\\text{ref})^\\top W^\\top W(m-m_\\text{ref})}
|
|
||||||
|
|
||||||
So the derivative is straight forward:
|
|
||||||
|
|
||||||
.. math::
|
|
||||||
|
|
||||||
R(m) = \mathbf{W^\\top W (m-m_\\text{ref})}
|
|
||||||
|
|
||||||
"""
|
|
||||||
if self.smoothModel == True:
|
|
||||||
mD1 = self.mapping.deriv(m)
|
|
||||||
mD2 = self.mapping.deriv(m - self.mref)
|
|
||||||
r1 = self.Wsmooth * ( self.mapping * (m))
|
|
||||||
r2 = self.Ws * ( self.mapping * (m - self.mref) )
|
|
||||||
out1 = mD1.T * ( self.Wsmooth.T * r1 )
|
|
||||||
out2 = mD2.T * ( self.Ws.T * r2 )
|
|
||||||
out = out1+out2
|
|
||||||
elif self.smoothModel == False:
|
|
||||||
mD = self.mapping.deriv(m - self.mref)
|
|
||||||
r = self.W * ( self.mapping * (m - self.mref) )
|
|
||||||
out = mD.T * ( self.W.T * r )
|
|
||||||
return out
|
|
||||||
|
|
||||||
class SparseRegularization(Simple):
|
|
||||||
|
|
||||||
eps = 1e-1
|
|
||||||
m = None
|
|
||||||
gamma = 1.
|
|
||||||
p = 0.
|
|
||||||
qx = 2.
|
|
||||||
qy = 2.
|
|
||||||
qz = 2.
|
|
||||||
|
|
||||||
def __init__(self, mesh, mapping=None, **kwargs):
|
|
||||||
Simple.__init__(self, mesh, mapping=mapping, **kwargs)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def Wsmooth(self):
|
|
||||||
"""Full smoothness regularization matrix W"""
|
|
||||||
if getattr(self, '_Wsmooth', None) is None:
|
|
||||||
wlist = (self.Wx, self.Wxx)
|
|
||||||
if self.mesh.dim > 1:
|
|
||||||
wlist += (self.Wy, self.Wyy)
|
|
||||||
if self.mesh.dim > 2:
|
|
||||||
wlist += (self.Wz, self.Wzz)
|
|
||||||
self._Wsmooth = sp.vstack(wlist)
|
|
||||||
return self._Wsmooth
|
|
||||||
|
|
||||||
@property
|
|
||||||
def W(self):
|
|
||||||
"""Full regularization matrix W"""
|
|
||||||
if getattr(self, '_W', None) is None:
|
|
||||||
wlist = (self.Ws, self.Wsmooth)
|
|
||||||
self._W = sp.vstack(wlist)
|
|
||||||
return self._W
|
|
||||||
|
|
||||||
@property
|
|
||||||
def Ws(self):
|
|
||||||
"""Regularization matrix Ws"""
|
|
||||||
if getattr(self, 'm', None) is None:
|
|
||||||
self.Rs = Utils.speye(self.mesh.nC)
|
|
||||||
|
|
||||||
else:
|
|
||||||
f_m = self.m
|
|
||||||
self.rs = self.R(f_m , self.p, self.eps)
|
|
||||||
#print "Min rs: " + str(np.max(self.rs)) + "Max rs: " + str(np.min(self.rs))
|
|
||||||
self.Rs = Utils.sdiag( self.rs )
|
|
||||||
|
|
||||||
self._Ws = Utils.sdiag((self.mesh.vol*self.alpha_s*self.gamma)**0.5)*self.Rs
|
|
||||||
|
|
||||||
return self._Ws
|
|
||||||
|
|
||||||
@property
|
|
||||||
def Wx(self):
|
|
||||||
"""Regularization matrix Wx"""
|
|
||||||
|
|
||||||
if getattr(self, 'm', None) is None:
|
|
||||||
self.Rx = Utils.speye(self.mesh.unitCellGradx.shape[0])
|
|
||||||
|
|
||||||
else:
|
|
||||||
f_m = self.mesh.unitCellGradx * self.m
|
|
||||||
self.rx = self.R( f_m , self.qx, self.eps)
|
|
||||||
self.Rx = Utils.sdiag( self.rx )
|
|
||||||
|
|
||||||
if getattr(self, '_Wx', None) is None:
|
|
||||||
self._Wx = Utils.sdiag((self.mesh.vol*self.alpha_x*self.gamma)**0.5)*self.Rx*self.mesh.unitCellGradx
|
|
||||||
return self._Wx
|
|
||||||
|
|
||||||
@property
|
|
||||||
def Wy(self):
|
|
||||||
"""Regularization matrix Wy"""
|
|
||||||
|
|
||||||
if getattr(self, 'm', None) is None:
|
|
||||||
self.Ry = Utils.speye(self.mesh.unitCellGrady.shape[0])
|
|
||||||
|
|
||||||
else:
|
|
||||||
f_m = self.mesh.unitCellGrady * self.m
|
|
||||||
self.ry = self.R( f_m , self.qy, self.eps)
|
|
||||||
self.Ry = Utils.sdiag( self.ry )
|
|
||||||
|
|
||||||
if getattr(self, '_Wy', None) is None:
|
|
||||||
self._Wy = Utils.sdiag((self.mesh.vol*self.alpha_y*self.gamma)**0.5)*self.Ry*self.mesh.unitCellGrady
|
|
||||||
return self._Wy
|
|
||||||
|
|
||||||
@property
|
|
||||||
def Wz(self):
|
|
||||||
"""Regularization matrix Wz"""
|
|
||||||
|
|
||||||
if getattr(self, 'm', None) is None:
|
|
||||||
self.Rz = Utils.speye(self.mesh.unitCellGradz.shape[0])
|
|
||||||
|
|
||||||
else:
|
|
||||||
f_m = self.mesh.unitCellGradz * self.m
|
|
||||||
self.rz = self.R( f_m , self.qz, self.eps)
|
|
||||||
self.Rz = Utils.sdiag( self.rz )
|
|
||||||
|
|
||||||
if getattr(self, '_Wz', None) is None:
|
|
||||||
self._Wz = Utils.sdiag((self.mesh.vol*self.alpha_z*self.gamma)**0.5)*self.Rz*self.mesh.unitCellGradz
|
|
||||||
return self._Wz
|
|
||||||
|
|
||||||
|
|
||||||
def R(self, f_m , p, dec):
|
|
||||||
|
|
||||||
eta = (self.eps**(1-p/2.))**0.5
|
|
||||||
r = eta / (f_m**2.+self.eps**2.)**((1-p/2.)/2.)
|
|
||||||
|
|
||||||
return r
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import numpy as np
|
|||||||
import scipy.sparse as sp
|
import scipy.sparse as sp
|
||||||
from codeutils import isScalar
|
from codeutils import isScalar
|
||||||
|
|
||||||
|
|
||||||
def mkvc(x, numDims=1):
|
def mkvc(x, numDims=1):
|
||||||
"""Creates a vector with the number of dimension specified
|
"""Creates a vector with the number of dimension specified
|
||||||
|
|
||||||
@@ -41,7 +40,7 @@ def mkvc(x, numDims=1):
|
|||||||
def sdiag(h):
|
def sdiag(h):
|
||||||
"""Sparse diagonal matrix"""
|
"""Sparse diagonal matrix"""
|
||||||
if isinstance(h, Zero):
|
if isinstance(h, Zero):
|
||||||
return h
|
return Zero()
|
||||||
|
|
||||||
return sp.spdiags(mkvc(h), 0, h.size, h.size, format="csr")
|
return sp.spdiags(mkvc(h), 0, h.size, h.size, format="csr")
|
||||||
|
|
||||||
|
|||||||
+62
-42
@@ -19,14 +19,14 @@ Electromagnetic phenomena are governed by Maxwell's equations. They describe the
|
|||||||
|
|
||||||
Fourier Transform Convention
|
Fourier Transform Convention
|
||||||
----------------------------
|
----------------------------
|
||||||
In order to examine Maxwell's equations in the frequency domain, we must first define our choice of harmonic time-dependence by choosing a Fourier transform convention. We use the \\(e^{i \\omega t} \\) convention, so we define our Fourier Transform pair as
|
In order to examine Maxwell's equations in the frequency domain, we must first define our choice of harmonic time-dependence by choosing a Fourier transform convention. We use the :math:`e^{i \omega t}` convention, so we define our Fourier Transform pair as
|
||||||
|
|
||||||
.. math ::
|
.. math ::
|
||||||
F(\omega) = \int_{-\infty}^{\infty} f(t) e^{- i \omega t} dt \\
|
F(\omega) = \int_{-\infty}^{\infty} f(t) e^{- i \omega t} dt \\
|
||||||
|
|
||||||
f(t) = \frac{1}{2\pi}\int_{-\infty}^{\infty} F(\omega) e^{i \omega t} d \omega
|
f(t) = \frac{1}{2\pi}\int_{-\infty}^{\infty} F(\omega) e^{i \omega t} d \omega
|
||||||
|
|
||||||
where \\(\\omega\\) is angular frequency, \\(t\\) is time, \\(F(\\omega)\\) is the function defined in the frequency domain and \\(f(t)\\) is the function defined in the time domain.
|
where :math:`\omega` is angular frequency, :math:`t` is time, :math:`F(\omega)` is the function defined in the frequency domain and :math:`f(t)` is the function defined in the time domain.
|
||||||
|
|
||||||
|
|
||||||
Maxwell's Equations
|
Maxwell's Equations
|
||||||
@@ -34,44 +34,46 @@ Maxwell's Equations
|
|||||||
In the frequency domain, Maxwell's equations are given by
|
In the frequency domain, Maxwell's equations are given by
|
||||||
|
|
||||||
.. math ::
|
.. math ::
|
||||||
\curl \vec{E} = - i \omega \vec{B} \\
|
\curl \vec{E} + i \omega \vec{B} = \vec{S_m}\\
|
||||||
|
|
||||||
\curl \vec{H} = \vec{J} + i \omega \vec{D} + \vec{S} \\
|
\curl \vec{H} - \vec{J} - i \omega \vec{D} = \vec{S_e} \\
|
||||||
|
|
||||||
\div \vec{B} = 0 \\
|
\div \vec{B} = 0 \\
|
||||||
|
|
||||||
\div \vec{D} = \rho_f
|
\div \vec{D} = \rho_f
|
||||||
|
|
||||||
where:
|
where:
|
||||||
|
|
||||||
- \\(\\vec{E}\\) : electric field (\\(V/m\\))
|
- :math:`\vec{E}` : electric field (:math:`V/m` )
|
||||||
- \\(\\vec{H}\\) : magnetic field (\\(A/m\\))
|
- :math:`\vec{H}` : magnetic field (:math:`A/m` )
|
||||||
- \\(\\vec{B}\\) : magnetic flux density (\\(Wb/m^2\\))
|
- :math:`\vec{B}` : magnetic flux density (:math:`Wb/m^2` )
|
||||||
- \\(\\vec{D}\\) : electric displacement / electric flux density (\\(C/m^2\\))
|
- :math:`\vec{D}` : electric displacement / electric flux density (:math:`C/m^2` )
|
||||||
- \\(\\vec{J}\\) : electric current density (\\(A/m^2\\))
|
- :math:`\vec{J}` : electric current density (:math:`A/m^2` )
|
||||||
- \\(\\rho_f\\) : free charge density
|
- :math:`\vec{S_m}` : magnetic source term (:math:`V/m^2` )
|
||||||
|
- :math:`\vec{S_e}` : electric source term (:math:`A/m^2` )
|
||||||
|
- :math:`\rho_f` : free charge density (:math:`\Omega m` )
|
||||||
|
|
||||||
The source term is \\(\\vec{S}\\)
|
|
||||||
|
|
||||||
|
|
||||||
Constitutive Relations
|
Constitutive Relations
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
The fields and fluxes are related through the constitutive relations. At each frequency, they are given by
|
The fields and fluxes are related through the constitutive relations. At each frequency, they are given by
|
||||||
|
|
||||||
.. math ::
|
.. math ::
|
||||||
\vec{J} = \sigma \vec{E} \\
|
\vec{J} = \sigma \vec{E} \\
|
||||||
|
|
||||||
\vec{B} = \mu \vec{H} \\
|
\vec{B} = \mu \vec{H} \\
|
||||||
|
|
||||||
\vec{D} = \varepsilon \vec{E}
|
\vec{D} = \varepsilon \vec{E}
|
||||||
|
|
||||||
where:
|
where:
|
||||||
|
|
||||||
- \\(\\sigma\\) : electrical conductivity \\(S/m\\)
|
- :math:`\sigma` : electrical conductivity (:math:`S/m`)
|
||||||
- \\(\\mu\\) : magnetic permeability \\(H/m\\)
|
- :math:`\mu` : magnetic permeability (:math:`H/m`)
|
||||||
- \\(\\varepsilon\\) : dielectric permittivity \\(F/m\\)
|
- :math:`\varepsilon` : dielectric permittivity (:math:`F/m`)
|
||||||
|
|
||||||
\\(\\sigma\\), \\(\\mu\\), \\(\\varepsilon\\) are physical properties which depend on the material. \\(\\sigma\\) describes how easily electric current passes through a material, \\(\\mu\\) describes how easily a material is magnetized, and \\(\\varepsilon\\) describes how easily a material is electrically polarized. In most geophysical applications of EM, \\(\\sigma\\) is the the primary physical property of interest, and \\(\\mu\\), \\(\\varepsilon\\) are assumed to have their free-space values \\(\\mu_0 = 4\\pi \\times 10^{-7} H/m \\), \\(\\varepsilon_0 = 8.85 \\times 10^{-12} F/m\\)
|
:math:`\sigma`, :math:`\mu`, :math:`\varepsilon` are physical properties which depend on the material. :math:`\sigma` describes how easily electric current passes through a material, :math:`\mu` describes how easily a material is magnetized, and :math:`\varepsilon` describes how easily a material is electrically polarized. In most geophysical applications of EM, :math:`\sigma` is the the primary physical property of interest, and :math:`\mu`, :math:`\varepsilon` are assumed to have their free-space values :math:`\mu_0 = 4\pi \times 10^{-7} H/m` , :math:`\varepsilon_0 = 8.85 \times 10^{-12} F/m`
|
||||||
|
|
||||||
|
|
||||||
Quasi-static Approximation
|
Quasi-static Approximation
|
||||||
@@ -80,8 +82,8 @@ Quasi-static Approximation
|
|||||||
For the frequency range typical of most geophysical surveys, the contribution of the electric displacement is negligible compared to the electric current density. In this case, we use the Quasi-static approximation and assume that this term can be neglected, giving
|
For the frequency range typical of most geophysical surveys, the contribution of the electric displacement is negligible compared to the electric current density. In this case, we use the Quasi-static approximation and assume that this term can be neglected, giving
|
||||||
|
|
||||||
.. math ::
|
.. math ::
|
||||||
\nabla \times \vec{E} = -i \omega \vec{B} \\
|
\nabla \times \vec{E} + i \omega \vec{B} = \vec{S_m} \\
|
||||||
\nabla \times \vec{H} = \vec{J} + \vec{S}
|
\nabla \times \vec{H} - \vec{J} = \vec{S_e}
|
||||||
|
|
||||||
|
|
||||||
Implementation in SimPEG.EM
|
Implementation in SimPEG.EM
|
||||||
@@ -90,14 +92,14 @@ Implementation in SimPEG.EM
|
|||||||
We consider two formulations in SimPEG.EM, both first-order and both in terms of one field and one flux. We allow for the definition of magnetic and electric sources (see for example: Ward and Hohmann, starting on page 144). The E-B formulation is in terms of the electric field and the magnetic flux:
|
We consider two formulations in SimPEG.EM, both first-order and both in terms of one field and one flux. We allow for the definition of magnetic and electric sources (see for example: Ward and Hohmann, starting on page 144). The E-B formulation is in terms of the electric field and the magnetic flux:
|
||||||
|
|
||||||
.. math ::
|
.. math ::
|
||||||
\nabla \times \vec{E} + i \omega \vec{B} = \vec{S}_m \\
|
\nabla \times \vec{E} + i \omega \vec{B} = \vec{S}_m \\
|
||||||
\nabla \times \mu^{-1} \vec{B} - \sigma \vec{E} = \vec{S}_e
|
\nabla \times \mu^{-1} \vec{B} - \sigma \vec{E} = \vec{S}_e
|
||||||
|
|
||||||
The H-J formulation is in terms of the current density and the magnetic field:
|
The H-J formulation is in terms of the current density and the magnetic field:
|
||||||
|
|
||||||
.. math ::
|
.. math ::
|
||||||
\nabla \times \sigma^{-1} \vec{J} + i \omega \mu \vec{H} = \vec{S}_m \\
|
\nabla \times \sigma^{-1} \vec{J} + i \omega \mu \vec{H} = \vec{S}_m \\
|
||||||
\nabla \times \vec{H} - \vec{J} = \vec{S}_e
|
\nabla \times \vec{H} - \vec{J} = \vec{S}_e
|
||||||
|
|
||||||
|
|
||||||
Discretizing
|
Discretizing
|
||||||
@@ -106,34 +108,34 @@ For both formulations, we use a finite volume discretization
|
|||||||
and discretize fields on cell edges, fluxes on cell faces and
|
and discretize fields on cell edges, fluxes on cell faces and
|
||||||
physical properties in cell centers. This is particularly
|
physical properties in cell centers. This is particularly
|
||||||
important when using symmetry to reduce the dimensionality of a problem
|
important when using symmetry to reduce the dimensionality of a problem
|
||||||
(for instance on a 2D CylMesh, there are \\(r\\), \\(z\\) faces and \\(\\theta\\) edges)
|
(for instance on a 2D CylMesh, there are :math:`r`, :math:`z` faces and :math:`\theta` edges)
|
||||||
|
|
||||||
.. figure:: ../images/finitevolrealestate.png
|
.. figure:: ../images/finitevolrealestate.png
|
||||||
:align: center
|
:align: center
|
||||||
:scale: 60 %
|
:scale: 60 %
|
||||||
|
|
||||||
For the two formulations, the discretization of the physical properties, fields and fluxes are summarized below.
|
For the two formulations, the discretization of the physical properties, fields and fluxes are summarized below.
|
||||||
|
|
||||||
.. figure:: ../images/ebjhdiscretizations.png
|
.. figure:: ../images/ebjhdiscretizations.png
|
||||||
:align: center
|
:align: center
|
||||||
:scale: 60 %
|
:scale: 60 %
|
||||||
|
|
||||||
Note that resistivity is the inverse of conductivity, \\(\\rho = \\sigma^{-1}\\).
|
Note that resistivity is the inverse of conductivity, :math:`\rho = \sigma^{-1}`.
|
||||||
|
|
||||||
|
|
||||||
E-B Formulation:
|
E-B Formulation
|
||||||
****************
|
---------------
|
||||||
|
|
||||||
.. math ::
|
.. math ::
|
||||||
\mathbf{C} \mathbf{e} + i \omega \mathbf{b} = \mathbf{s_m} \\
|
\mathbf{C} \mathbf{e} + i \omega \mathbf{b} = \mathbf{s_m} \\
|
||||||
\mathbf{C^T} \mathbf{M^f_{\mu^{-1}}} \mathbf{b} - \mathbf{M^e_\sigma} \mathbf{e} = \mathbf{M^e} \mathbf{s_e}
|
\mathbf{C^T} \mathbf{M^f_{\mu^{-1}}} \mathbf{b} - \mathbf{M^e_\sigma} \mathbf{e} = \mathbf{M^e} \mathbf{s_e}
|
||||||
|
|
||||||
H-J Formulation:
|
H-J Formulation
|
||||||
****************
|
---------------
|
||||||
|
|
||||||
.. math ::
|
.. math ::
|
||||||
\mathbf{C^T} \mathbf{M^f_\rho} \mathbf{j} + i \omega \mathbf{M^e_\mu} \mathbf{h} = \mathbf{M^e} \mathbf{s_m} \\
|
\mathbf{C^T} \mathbf{M^f_\rho} \mathbf{j} + i \omega \mathbf{M^e_\mu} \mathbf{h} = \mathbf{M^e} \mathbf{s_m} \\
|
||||||
\mathbf{C} \mathbf{h} - \mathbf{j} = \mathbf{s_e}
|
\mathbf{C} \mathbf{h} - \mathbf{j} = \mathbf{s_e}
|
||||||
|
|
||||||
|
|
||||||
.. Forward Problem
|
.. Forward Problem
|
||||||
@@ -144,6 +146,10 @@ H-J Formulation:
|
|||||||
|
|
||||||
API
|
API
|
||||||
===
|
===
|
||||||
|
|
||||||
|
FDEM Problem
|
||||||
|
------------
|
||||||
|
|
||||||
.. automodule:: SimPEG.EM.FDEM.FDEM
|
.. automodule:: SimPEG.EM.FDEM.FDEM
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
:members:
|
:members:
|
||||||
@@ -157,3 +163,17 @@ FDEM Survey
|
|||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
|
|
||||||
|
.. automodule:: SimPEG.EM.FDEM.SrcFDEM
|
||||||
|
:show-inheritance:
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
|
||||||
|
FDEM Fields
|
||||||
|
-----------
|
||||||
|
|
||||||
|
.. automodule:: SimPEG.EM.FDEM.FieldsFDEM
|
||||||
|
:show-inheritance:
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,305 @@
|
|||||||
\newcommand{\I}{\vec{I}}
|
\newcommand{\I}{\vec{I}}
|
||||||
|
|
||||||
|
|
||||||
|
Time Domain Electromagnetics
|
||||||
|
****************************
|
||||||
|
|
||||||
|
.. _api_TDEM_derivation:
|
||||||
|
|
||||||
|
Time-Domain EM Derivation
|
||||||
|
=========================
|
||||||
|
|
||||||
|
The following shows the derivation for the TDEM problem. We use the b-formulation below.
|
||||||
|
(More to come soon..!)
|
||||||
|
|
||||||
|
|
||||||
|
Sensitivity Calculation
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
\begin{align}
|
||||||
|
\dcurl \e^{(t+1)} + \frac{\b^{(t+1)} - \b^{(t)}}{\delta t} = 0 \\
|
||||||
|
\dcurl^\top \MfMui \b^{(t+1)} - \MeSig \e^{(t+1)} = \Me \j_s^{(t+1)}
|
||||||
|
\end{align}
|
||||||
|
|
||||||
|
Using Gauss-Newton to solve the inverse problem requires the ability to calculate the product of the
|
||||||
|
Jacobian and a vector, as well as the transpose of the Jacobian times a vector.
|
||||||
|
The above system can be rewritten as:
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
\begin{align}
|
||||||
|
\mathbf{A} \u^{(t+1)} + \mathbf{B} \u^{(t)}= \s^{(t+1)}
|
||||||
|
\end{align}
|
||||||
|
|
||||||
|
where
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
\begin{align}
|
||||||
|
\mathbf{A} =
|
||||||
|
\left[
|
||||||
|
\begin{array}{cc}
|
||||||
|
\frac{1}{\delta t} \MfMui & \MfMui\dcurl \\
|
||||||
|
\dcurl^\top \MfMui & -\MeSig
|
||||||
|
\end{array}
|
||||||
|
\right] \\
|
||||||
|
\mathbf{B} =
|
||||||
|
\left[
|
||||||
|
\begin{array}{cc}
|
||||||
|
-\frac{1}{\delta t} \MfMui & 0 \\
|
||||||
|
0 & 0
|
||||||
|
\end{array}
|
||||||
|
\right] \\
|
||||||
|
\u^{(k)} = \left[
|
||||||
|
\begin{array}{c}
|
||||||
|
\b^{(k)}\\
|
||||||
|
\e^{(k)}
|
||||||
|
\end{array}
|
||||||
|
\right] \\
|
||||||
|
\s^{(k)} = \left[
|
||||||
|
\begin{array}{c}
|
||||||
|
0\\
|
||||||
|
\Me \j^{(k)}_s
|
||||||
|
\end{array}
|
||||||
|
\right]
|
||||||
|
\end{align}
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Here we have multiplied through by \\(\\MfMui\\) to make A and B symmetric!
|
||||||
|
|
||||||
|
The entire time dependent system can be written in a single matrix expression
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
\begin{align}
|
||||||
|
\hat{\mathbf{A}} \hat{u} = \hat{s}
|
||||||
|
\end{align}
|
||||||
|
|
||||||
|
where
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
\begin{align}
|
||||||
|
\mathbf{\hat{A}} = \left[
|
||||||
|
\begin{array}{cccc}
|
||||||
|
A & 0 & & \\
|
||||||
|
B & A & & \\
|
||||||
|
& \ddots & \ddots & \\
|
||||||
|
& & B & A
|
||||||
|
\end{array}
|
||||||
|
\right] \\
|
||||||
|
\hat{u} = \left[
|
||||||
|
\begin{array}{c}
|
||||||
|
\u^{(1)} \\
|
||||||
|
\u^{(2)} \\
|
||||||
|
\vdots \\
|
||||||
|
\u^{(N)}
|
||||||
|
\end{array} \right]\\
|
||||||
|
\hat{s} = \left[
|
||||||
|
\begin{array}{c}
|
||||||
|
\s^{(1)} - \mathbf{B} \u^{(0)} \\
|
||||||
|
\s^{(2)} \\
|
||||||
|
\vdots \\
|
||||||
|
\s^{(N)}
|
||||||
|
\end{array}
|
||||||
|
\right]
|
||||||
|
\end{align}
|
||||||
|
|
||||||
|
For the fields \\(\\u\\), the measured data is given by
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
\begin{align}
|
||||||
|
\vec{d} = \mathbf{Q} \u
|
||||||
|
\end{align}
|
||||||
|
|
||||||
|
The sensitivity matrix **J** is then defined as
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
\begin{align}
|
||||||
|
\mathbf{J} = \mathbf{Q} \frac{\partial \u}{\partial \sigma}
|
||||||
|
\end{align}
|
||||||
|
|
||||||
|
|
||||||
|
Defining the function \\(\\c(m,\\u)\\) to be
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
\begin{align}
|
||||||
|
\vec{c}(m,\u) = \hat{\mathbf{A}} \vec{u} - \vec{q} = \vec{0}
|
||||||
|
\end{align}
|
||||||
|
|
||||||
|
then
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
\begin{align}
|
||||||
|
\frac{\partial \vec{c}}{\partial m} \partial m
|
||||||
|
+ \frac{\partial \vec{c}}{\partial \u} \partial \vec{u} = 0
|
||||||
|
\end{align}
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
\begin{align}
|
||||||
|
\frac{\partial \vec{u}}{\partial m} = -\left(\frac{\partial \vec{c}}{\partial \u} \right)^{-1} \frac{\partial \vec{c}}{\partial m}
|
||||||
|
\end{align}
|
||||||
|
|
||||||
|
|
||||||
|
Differentiating, we find that
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
\begin{align}
|
||||||
|
\frac{\partial \vec{c}}{\partial \hat{u}} = \hat{\mathbf{A}}
|
||||||
|
\end{align}
|
||||||
|
|
||||||
|
and
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
\begin{align}
|
||||||
|
\frac{\partial \vec{c}}{\partial \sigma} = \mathbf{G}_\sigma =
|
||||||
|
\left[
|
||||||
|
\begin{array}{c}
|
||||||
|
g_\sigma^{(1)}\\
|
||||||
|
g_\sigma^{(2)}\\
|
||||||
|
\vdots \\
|
||||||
|
g_\sigma^{(N)}
|
||||||
|
\end{array}
|
||||||
|
\right]
|
||||||
|
\end{align}
|
||||||
|
|
||||||
|
with
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
\begin{align}
|
||||||
|
g_\sigma^{(n)} =
|
||||||
|
\left[
|
||||||
|
\begin{array}{c}
|
||||||
|
\mathbf{0} \\
|
||||||
|
- \diag{\e^{(n)}} \Ace \diag{\vec{V}}
|
||||||
|
\end{array}
|
||||||
|
\right]
|
||||||
|
\end{align}
|
||||||
|
|
||||||
|
|
||||||
|
Implementing **J** times a vector
|
||||||
|
---------------------------------
|
||||||
|
|
||||||
|
Multiplying **J** onto a vector can be broken into three steps
|
||||||
|
|
||||||
|
|
||||||
|
* Compute \\(\\vec{p} = \\mathbf{G}m\\)
|
||||||
|
* Solve \\(\\hat{\\mathbf{A}} \\vec{y} = \\vec{p}\\)
|
||||||
|
* Compute \\(\\vec{w} = -\\mathbf{Q} \\vec{y}\\)
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
\begin{align}
|
||||||
|
\vec{p}^{(n)} = \left[
|
||||||
|
\begin{array}{c}
|
||||||
|
\vec{p}_b^{(n)} \\
|
||||||
|
\vec{p}_e^{(n)}
|
||||||
|
\end{array}
|
||||||
|
\right] \\
|
||||||
|
\vec{p}_b^{(n)} = 0 \\
|
||||||
|
\vec{p}_e^{(n)} = - \diag{\e^{(n)}} \Ace \diag{V} m
|
||||||
|
\end{align}
|
||||||
|
|
||||||
|
|
||||||
|
For all time steps:
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
\begin{align}
|
||||||
|
\frac{1}{\delta t} \MfMui\vec{y}_{b}^{(t+1)} + \MfMui\dcurl \vec{y}_{e}^{(t+1)}
|
||||||
|
- \frac{1}{\delta t} \MfMui \vec{y}_{b}^{(t)}
|
||||||
|
= \vec{p}_b^{(t+1)} \\
|
||||||
|
\dcurl^\top \MfMui \vec{y}_b^{(t+1)} - \MeSig \vec{y}_e^{(t+1)} = \vec{p}_e^{(t+1)}
|
||||||
|
\end{align}
|
||||||
|
|
||||||
|
and
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
\begin{align}
|
||||||
|
\left( \MfMui \dcurl \MeSig^{-1} \dcurl^\top \MfMui + \frac{1}{\delta t} \MfMui \right) \vec{y}_{b}^{(t+1)} =
|
||||||
|
\frac{1}{\delta t} \MfMui \vec{y}_b^{(t)}
|
||||||
|
+ \MfMui \dcurl \MeSig^{-1} \vec{p}_e^{(t+1)} + \vec{p}_b^{(t+1)} \\
|
||||||
|
\vec{y}_e^{(t+1)} = \MeSig^{-1} \dcurl^\top \MfMui \vec{y}_b^{(t+1)} - \MeSig^{-1} \vec{p}_e^{(t+1)}
|
||||||
|
\end{align}
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
For the first time step, \\\(t=0\\\), the term: \\\(\\frac{1}{\\delta t} \\MfMui \\vec{y}_b^{(0)}\\\) is zero.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Implementing **J** transpose times a vector
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
Multiplying \\(\\mathbf{J}^\\top\\) onto a vector can be broken into three steps
|
||||||
|
|
||||||
|
|
||||||
|
* Compute \\(\\vec{p} = \\mathbf{Q}^\\top \\vec{v}\\)
|
||||||
|
* Solve \\(\\hat{\\mathbf{A}}^\\top \\vec{y} = \\vec{p}\\)
|
||||||
|
* Compute \\(\\vec{w} = -\\mathbf{G}^\\top y\\)
|
||||||
|
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
\mathbf{\hat{A}}^\top = \left[
|
||||||
|
\begin{array}{cccc}
|
||||||
|
A & B & & \\
|
||||||
|
& \ddots & \ddots & \\
|
||||||
|
& & A & B \\
|
||||||
|
& & 0 & A
|
||||||
|
\end{array}
|
||||||
|
\right]
|
||||||
|
|
||||||
|
For the all time-steps (going backwards in time):
|
||||||
|
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
A \vec{y}^{(t)} + B \vec{y}^{(t+1)} = \vec{p}^{(t)}
|
||||||
|
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
\begin{align}
|
||||||
|
\frac{1}{\delta t} \MfMui\vec{y}_{b}^{(t)} + \MfMui\dcurl \vec{y}_{e}^{(t)}
|
||||||
|
- \frac{1}{\delta t} \MfMui \vec{y}_{b}^{(t+1)}
|
||||||
|
= \vec{p}_b^{(t)} \\
|
||||||
|
\dcurl^\top \MfMui \vec{y}_b^{(t)} - \MeSig \vec{y}_e^{(t)} = \vec{p}_e^{(t)}
|
||||||
|
\end{align}
|
||||||
|
|
||||||
|
and
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
\begin{align}
|
||||||
|
\left( \MfMui \dcurl \MeSig^{-1} \dcurl^\top \MfMui + \frac{1}{\delta t} \MfMui \right) \vec{y}_{b}^{(t)} =
|
||||||
|
\frac{1}{\delta t} \MfMui \vec{y}_b^{(t+1)}
|
||||||
|
+ \MfMui \dcurl \MeSig^{-1} \vec{p}_e^{(t)} + \vec{p}_b^{(t)} \\
|
||||||
|
\vec{y}_e^{(t)} = \MeSig^{-1} \dcurl^\top \MfMui \vec{y}_b^{(t)} - \MeSig^{-1} \vec{p}_e^{(t)}
|
||||||
|
\end{align}
|
||||||
|
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
For the last time step, \\\(t=N\\\), the term: \\\(\\frac{1}{\\delta t} \\MfMui \\vec{y}_b^{(N+1)}\\\) is zero.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
TDEM - B formulation
|
TDEM - B formulation
|
||||||
====================
|
====================
|
||||||
|
|
||||||
|
|||||||
@@ -1,341 +0,0 @@
|
|||||||
.. _api_TDEM_derivation:
|
|
||||||
|
|
||||||
|
|
||||||
.. math::
|
|
||||||
|
|
||||||
\renewcommand{\div}{\nabla\cdot\,}
|
|
||||||
\newcommand{\grad}{\vec \nabla}
|
|
||||||
\newcommand{\curl}{{\vec \nabla}\times\,}
|
|
||||||
\newcommand {\J}{{\vec J}}
|
|
||||||
\renewcommand{\H}{{\vec H}}
|
|
||||||
\newcommand {\E}{{\vec E}}
|
|
||||||
\newcommand{\dcurl}{{\mathbf C}}
|
|
||||||
\newcommand{\dgrad}{{\mathbf G}}
|
|
||||||
\newcommand{\Acf}{{\mathbf A_c^f}}
|
|
||||||
\newcommand{\Ace}{{\mathbf A_c^e}}
|
|
||||||
\renewcommand{\S}{{\mathbf \Sigma}}
|
|
||||||
\newcommand{\St}{{\mathbf \Sigma_\tau}}
|
|
||||||
\newcommand{\T}{{\mathbf T}}
|
|
||||||
\newcommand{\Tt}{{\mathbf T_\tau}}
|
|
||||||
\newcommand{\diag}[1]{\,{\sf diag}\left( #1 \right)}
|
|
||||||
\newcommand{\M}{{\mathbf M}}
|
|
||||||
\newcommand{\MfMui}{{\M^f_{\mu^{-1}}}}
|
|
||||||
\newcommand{\MeSig}{{\M^e_\sigma}}
|
|
||||||
\newcommand{\MeSigInf}{{\M^e_{\sigma_\infty}}}
|
|
||||||
\newcommand{\MeSigO}{{\M^e_{\sigma_0}}}
|
|
||||||
\newcommand{\Me}{{\M^e}}
|
|
||||||
\newcommand{\Mes}[1]{{\M^e_{#1}}}
|
|
||||||
\newcommand{\Mee}{{\M^e_e}}
|
|
||||||
\newcommand{\Mej}{{\M^e_j}}
|
|
||||||
\newcommand{\BigO}[1]{\mathcal{O}\bigl(#1\bigr)}
|
|
||||||
\newcommand{\bE}{\mathbf{E}}
|
|
||||||
\newcommand{\bH}{\mathbf{H}}
|
|
||||||
\newcommand{\B}{\vec{B}}
|
|
||||||
\newcommand{\D}{\vec{D}}
|
|
||||||
\renewcommand{\H}{\vec{H}}
|
|
||||||
\newcommand{\s}{\vec{s}}
|
|
||||||
\newcommand{\bfJ}{\bf{J}}
|
|
||||||
\newcommand{\vecm}{\vec m}
|
|
||||||
\renewcommand{\Re}{\mathsf{Re}}
|
|
||||||
\renewcommand{\Im}{\mathsf{Im}}
|
|
||||||
\renewcommand {\j} { {\vec j} }
|
|
||||||
\newcommand {\h} { {\vec h} }
|
|
||||||
\renewcommand {\b} { {\vec b} }
|
|
||||||
\newcommand {\e} { {\vec e} }
|
|
||||||
\newcommand {\c} { {\vec c} }
|
|
||||||
\renewcommand {\d} { {\vec d} }
|
|
||||||
\renewcommand {\u} { {\vec u} }
|
|
||||||
\newcommand{\I}{\vec{I}}
|
|
||||||
|
|
||||||
|
|
||||||
Time-Domain EM Derivation
|
|
||||||
*************************
|
|
||||||
|
|
||||||
The following shows the derivation for the TDEM problem. We use the b-formulation below.
|
|
||||||
(More to come soon..!)
|
|
||||||
|
|
||||||
|
|
||||||
Sensitivity Calculation
|
|
||||||
=======================
|
|
||||||
|
|
||||||
.. math::
|
|
||||||
|
|
||||||
\begin{align}
|
|
||||||
\dcurl \e^{(t+1)} + \frac{\b^{(t+1)} - \b^{(t)}}{\delta t} = 0 \\
|
|
||||||
\dcurl^\top \MfMui \b^{(t+1)} - \MeSig \e^{(t+1)} = \Me \j_s^{(t+1)}
|
|
||||||
\end{align}
|
|
||||||
|
|
||||||
Using Gauss-Newton to solve the inverse problem requires the ability to calculate the product of the
|
|
||||||
Jacobian and a vector, as well as the transpose of the Jacobian times a vector.
|
|
||||||
The above system can be rewritten as:
|
|
||||||
|
|
||||||
.. math::
|
|
||||||
|
|
||||||
\begin{align}
|
|
||||||
\mathbf{A} \u^{(t+1)} + \mathbf{B} \u^{(t)}= \s^{(t+1)}
|
|
||||||
\end{align}
|
|
||||||
|
|
||||||
where
|
|
||||||
|
|
||||||
.. math::
|
|
||||||
|
|
||||||
\begin{align}
|
|
||||||
\mathbf{A} =
|
|
||||||
\left[
|
|
||||||
\begin{array}{cc}
|
|
||||||
\frac{1}{\delta t} \MfMui & \MfMui\dcurl \\
|
|
||||||
\dcurl^\top \MfMui & -\MeSig
|
|
||||||
\end{array}
|
|
||||||
\right] \\
|
|
||||||
\mathbf{B} =
|
|
||||||
\left[
|
|
||||||
\begin{array}{cc}
|
|
||||||
-\frac{1}{\delta t} \MfMui & 0 \\
|
|
||||||
0 & 0
|
|
||||||
\end{array}
|
|
||||||
\right] \\
|
|
||||||
\u^{(k)} = \left[
|
|
||||||
\begin{array}{c}
|
|
||||||
\b^{(k)}\\
|
|
||||||
\e^{(k)}
|
|
||||||
\end{array}
|
|
||||||
\right] \\
|
|
||||||
\s^{(k)} = \left[
|
|
||||||
\begin{array}{c}
|
|
||||||
0\\
|
|
||||||
\Me \j^{(k)}_s
|
|
||||||
\end{array}
|
|
||||||
\right]
|
|
||||||
\end{align}
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
Here we have multiplied through by \\(\\MfMui\\) to make A and B symmetric!
|
|
||||||
|
|
||||||
The entire time dependent system can be written in a single matrix expression
|
|
||||||
|
|
||||||
.. math::
|
|
||||||
|
|
||||||
\begin{align}
|
|
||||||
\hat{\mathbf{A}} \hat{u} = \hat{s}
|
|
||||||
\end{align}
|
|
||||||
|
|
||||||
where
|
|
||||||
|
|
||||||
.. math::
|
|
||||||
|
|
||||||
\begin{align}
|
|
||||||
\mathbf{\hat{A}} = \left[
|
|
||||||
\begin{array}{cccc}
|
|
||||||
A & 0 & & \\
|
|
||||||
B & A & & \\
|
|
||||||
& \ddots & \ddots & \\
|
|
||||||
& & B & A
|
|
||||||
\end{array}
|
|
||||||
\right] \\
|
|
||||||
\hat{u} = \left[
|
|
||||||
\begin{array}{c}
|
|
||||||
\u^{(1)} \\
|
|
||||||
\u^{(2)} \\
|
|
||||||
\vdots \\
|
|
||||||
\u^{(N)}
|
|
||||||
\end{array} \right]\\
|
|
||||||
\hat{s} = \left[
|
|
||||||
\begin{array}{c}
|
|
||||||
\s^{(1)} - \mathbf{B} \u^{(0)} \\
|
|
||||||
\s^{(2)} \\
|
|
||||||
\vdots \\
|
|
||||||
\s^{(N)}
|
|
||||||
\end{array}
|
|
||||||
\right]
|
|
||||||
\end{align}
|
|
||||||
|
|
||||||
For the fields \\(\\u\\), the measured data is given by
|
|
||||||
|
|
||||||
.. math::
|
|
||||||
|
|
||||||
\begin{align}
|
|
||||||
\vec{d} = \mathbf{Q} \u
|
|
||||||
\end{align}
|
|
||||||
|
|
||||||
The sensitivity matrix **J** is then defined as
|
|
||||||
|
|
||||||
.. math::
|
|
||||||
|
|
||||||
\begin{align}
|
|
||||||
\mathbf{J} = \mathbf{Q} \frac{\partial \u}{\partial \sigma}
|
|
||||||
\end{align}
|
|
||||||
|
|
||||||
|
|
||||||
Defining the function \\(\\c(m,\\u)\\) to be
|
|
||||||
|
|
||||||
.. math::
|
|
||||||
|
|
||||||
\begin{align}
|
|
||||||
\vec{c}(m,\u) = \hat{\mathbf{A}} \vec{u} - \vec{q} = \vec{0}
|
|
||||||
\end{align}
|
|
||||||
|
|
||||||
then
|
|
||||||
|
|
||||||
.. math::
|
|
||||||
|
|
||||||
\begin{align}
|
|
||||||
\frac{\partial \vec{c}}{\partial m} \partial m
|
|
||||||
+ \frac{\partial \vec{c}}{\partial \u} \partial \vec{u} = 0
|
|
||||||
\end{align}
|
|
||||||
|
|
||||||
or
|
|
||||||
|
|
||||||
.. math::
|
|
||||||
|
|
||||||
\begin{align}
|
|
||||||
\frac{\partial \vec{u}}{\partial m} = -\left(\frac{\partial \vec{c}}{\partial \u} \right)^{-1} \frac{\partial \vec{c}}{\partial m}
|
|
||||||
\end{align}
|
|
||||||
|
|
||||||
|
|
||||||
Differentiating, we find that
|
|
||||||
|
|
||||||
.. math::
|
|
||||||
|
|
||||||
\begin{align}
|
|
||||||
\frac{\partial \vec{c}}{\partial \hat{u}} = \hat{\mathbf{A}}
|
|
||||||
\end{align}
|
|
||||||
|
|
||||||
and
|
|
||||||
|
|
||||||
.. math::
|
|
||||||
|
|
||||||
\begin{align}
|
|
||||||
\frac{\partial \vec{c}}{\partial \sigma} = \mathbf{G}_\sigma =
|
|
||||||
\left[
|
|
||||||
\begin{array}{c}
|
|
||||||
g_\sigma^{(1)}\\
|
|
||||||
g_\sigma^{(2)}\\
|
|
||||||
\vdots \\
|
|
||||||
g_\sigma^{(N)}
|
|
||||||
\end{array}
|
|
||||||
\right]
|
|
||||||
\end{align}
|
|
||||||
|
|
||||||
with
|
|
||||||
|
|
||||||
.. math::
|
|
||||||
|
|
||||||
\begin{align}
|
|
||||||
g_\sigma^{(n)} =
|
|
||||||
\left[
|
|
||||||
\begin{array}{c}
|
|
||||||
\mathbf{0} \\
|
|
||||||
- \diag{\e^{(n)}} \Ace \diag{\vec{V}}
|
|
||||||
\end{array}
|
|
||||||
\right]
|
|
||||||
\end{align}
|
|
||||||
|
|
||||||
|
|
||||||
Implementing **J** times a vector
|
|
||||||
=================================
|
|
||||||
|
|
||||||
Multiplying **J** onto a vector can be broken into three steps
|
|
||||||
|
|
||||||
|
|
||||||
* Compute \\(\\vec{p} = \\mathbf{G}m\\)
|
|
||||||
* Solve \\(\\hat{\\mathbf{A}} \\vec{y} = \\vec{p}\\)
|
|
||||||
* Compute \\(\\vec{w} = -\\mathbf{Q} \\vec{y}\\)
|
|
||||||
|
|
||||||
.. math::
|
|
||||||
|
|
||||||
\begin{align}
|
|
||||||
\vec{p}^{(n)} = \left[
|
|
||||||
\begin{array}{c}
|
|
||||||
\vec{p}_b^{(n)} \\
|
|
||||||
\vec{p}_e^{(n)}
|
|
||||||
\end{array}
|
|
||||||
\right] \\
|
|
||||||
\vec{p}_b^{(n)} = 0 \\
|
|
||||||
\vec{p}_e^{(n)} = - \diag{\e^{(n)}} \Ace \diag{V} m
|
|
||||||
\end{align}
|
|
||||||
|
|
||||||
|
|
||||||
For all time steps:
|
|
||||||
|
|
||||||
.. math::
|
|
||||||
|
|
||||||
\begin{align}
|
|
||||||
\frac{1}{\delta t} \MfMui\vec{y}_{b}^{(t+1)} + \MfMui\dcurl \vec{y}_{e}^{(t+1)}
|
|
||||||
- \frac{1}{\delta t} \MfMui \vec{y}_{b}^{(t)}
|
|
||||||
= \vec{p}_b^{(t+1)} \\
|
|
||||||
\dcurl^\top \MfMui \vec{y}_b^{(t+1)} - \MeSig \vec{y}_e^{(t+1)} = \vec{p}_e^{(t+1)}
|
|
||||||
\end{align}
|
|
||||||
|
|
||||||
and
|
|
||||||
|
|
||||||
.. math::
|
|
||||||
|
|
||||||
\begin{align}
|
|
||||||
\left( \MfMui \dcurl \MeSig^{-1} \dcurl^\top \MfMui + \frac{1}{\delta t} \MfMui \right) \vec{y}_{b}^{(t+1)} =
|
|
||||||
\frac{1}{\delta t} \MfMui \vec{y}_b^{(t)}
|
|
||||||
+ \MfMui \dcurl \MeSig^{-1} \vec{p}_e^{(t+1)} + \vec{p}_b^{(t+1)} \\
|
|
||||||
\vec{y}_e^{(t+1)} = \MeSig^{-1} \dcurl^\top \MfMui \vec{y}_b^{(t+1)} - \MeSig^{-1} \vec{p}_e^{(t+1)}
|
|
||||||
\end{align}
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
For the first time step, \\\(t=0\\\), the term: \\\(\\frac{1}{\\delta t} \\MfMui \\vec{y}_b^{(0)}\\\) is zero.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Implementing **J** transpose times a vector
|
|
||||||
===========================================
|
|
||||||
|
|
||||||
Multiplying \\(\\mathbf{J}^\\top\\) onto a vector can be broken into three steps
|
|
||||||
|
|
||||||
|
|
||||||
* Compute \\(\\vec{p} = \\mathbf{Q}^\\top \\vec{v}\\)
|
|
||||||
* Solve \\(\\hat{\\mathbf{A}}^\\top \\vec{y} = \\vec{p}\\)
|
|
||||||
* Compute \\(\\vec{w} = -\\mathbf{G}^\\top y\\)
|
|
||||||
|
|
||||||
|
|
||||||
.. math::
|
|
||||||
|
|
||||||
\mathbf{\hat{A}}^\top = \left[
|
|
||||||
\begin{array}{cccc}
|
|
||||||
A & B & & \\
|
|
||||||
& \ddots & \ddots & \\
|
|
||||||
& & A & B \\
|
|
||||||
& & 0 & A
|
|
||||||
\end{array}
|
|
||||||
\right]
|
|
||||||
|
|
||||||
For the all time-steps (going backwards in time):
|
|
||||||
|
|
||||||
|
|
||||||
.. math::
|
|
||||||
|
|
||||||
A \vec{y}^{(t)} + B \vec{y}^{(t+1)} = \vec{p}^{(t)}
|
|
||||||
|
|
||||||
|
|
||||||
.. math::
|
|
||||||
|
|
||||||
\begin{align}
|
|
||||||
\frac{1}{\delta t} \MfMui\vec{y}_{b}^{(t)} + \MfMui\dcurl \vec{y}_{e}^{(t)}
|
|
||||||
- \frac{1}{\delta t} \MfMui \vec{y}_{b}^{(t+1)}
|
|
||||||
= \vec{p}_b^{(t)} \\
|
|
||||||
\dcurl^\top \MfMui \vec{y}_b^{(t)} - \MeSig \vec{y}_e^{(t)} = \vec{p}_e^{(t)}
|
|
||||||
\end{align}
|
|
||||||
|
|
||||||
and
|
|
||||||
|
|
||||||
.. math::
|
|
||||||
|
|
||||||
\begin{align}
|
|
||||||
\left( \MfMui \dcurl \MeSig^{-1} \dcurl^\top \MfMui + \frac{1}{\delta t} \MfMui \right) \vec{y}_{b}^{(t)} =
|
|
||||||
\frac{1}{\delta t} \MfMui \vec{y}_b^{(t+1)}
|
|
||||||
+ \MfMui \dcurl \MeSig^{-1} \vec{p}_e^{(t)} + \vec{p}_b^{(t)} \\
|
|
||||||
\vec{y}_e^{(t)} = \MeSig^{-1} \dcurl^\top \MfMui \vec{y}_b^{(t)} - \MeSig^{-1} \vec{p}_e^{(t)}
|
|
||||||
\end{align}
|
|
||||||
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
For the last time step, \\\(t=N\\\), the term: \\\(\\frac{1}{\\delta t} \\MfMui \\vec{y}_b^{(N+1)}\\\) is zero.
|
|
||||||
+10
-9
@@ -4,6 +4,16 @@ simpegEM Utilities
|
|||||||
SimPEG for EM provides a few EM specific utility codes,
|
SimPEG for EM provides a few EM specific utility codes,
|
||||||
sources, and analytic functions.
|
sources, and analytic functions.
|
||||||
|
|
||||||
|
Utilities for Electromagnetics
|
||||||
|
==============================
|
||||||
|
|
||||||
|
.. automodule:: SimPEG.EM.Utils
|
||||||
|
:show-inheritance:
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:inherited-members:
|
||||||
|
|
||||||
|
|
||||||
Analytic Functions - Time
|
Analytic Functions - Time
|
||||||
=========================
|
=========================
|
||||||
|
|
||||||
@@ -22,12 +32,3 @@ Analytic Functions - Frequency
|
|||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:inherited-members:
|
:inherited-members:
|
||||||
|
|
||||||
|
|
||||||
Sources
|
|
||||||
=======
|
|
||||||
|
|
||||||
.. autoclass:: SimPEG.EM.FDEM.SrcFDEM.MagDipole
|
|
||||||
:show-inheritance:
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
||||||
|
|||||||
+8
-26
@@ -5,40 +5,22 @@ Electromagnetics
|
|||||||
`SimPEG.EM` uses SimPEG as the framework for the forward and inverse
|
`SimPEG.EM` uses SimPEG as the framework for the forward and inverse
|
||||||
electromagnetics geophysical problems.
|
electromagnetics geophysical problems.
|
||||||
|
|
||||||
Time Domian Electromagnetics
|
To solve for predicted data, we follow the framework shown below. The model is
|
||||||
----------------------------
|
what we invert for. This is mapped to a physical property on the simulation
|
||||||
|
mesh. A source which is used to excite the system is specified. Having a model
|
||||||
.. toctree::
|
and a source, we can solve Maxwell's equations for fields. We sample these
|
||||||
:maxdepth: 2
|
fields with recievers to give us predicted data.
|
||||||
|
|
||||||
api_TDEM_derivation
|
|
||||||
|
|
||||||
|
|
||||||
Code for Time Domian Electromagnetics
|
.. image:: ../images/simpegEM_noMath.png
|
||||||
-------------------------------------
|
:scale: 50%
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 2
|
|
||||||
|
|
||||||
api_TDEM
|
|
||||||
|
|
||||||
Frequency Domian Electromagnetics
|
|
||||||
---------------------------------
|
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
|
||||||
api_FDEM
|
api_FDEM
|
||||||
|
api_TDEM
|
||||||
|
|
||||||
Utility Codes
|
|
||||||
-------------
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 2
|
|
||||||
|
|
||||||
api_Utils
|
api_Utils
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
.. _examples_MT_1D_ForwardAndInversion:
|
||||||
|
|
||||||
|
.. --------------------------------- ..
|
||||||
|
.. ..
|
||||||
|
.. THIS FILE IS AUTO GENEREATED ..
|
||||||
|
.. ..
|
||||||
|
.. SimPEG/Examples/__init__.py ..
|
||||||
|
.. ..
|
||||||
|
.. --------------------------------- ..
|
||||||
|
|
||||||
|
|
||||||
|
MT: 1D: Inversion
|
||||||
|
=======================
|
||||||
|
|
||||||
|
Forward model 1D MT data.
|
||||||
|
Setup and run a MT 1D inversion.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.. plot::
|
||||||
|
|
||||||
|
from SimPEG import Examples
|
||||||
|
Examples.MT_1D_ForwardAndInversion.run()
|
||||||
|
|
||||||
|
.. literalinclude:: ../../SimPEG/Examples/MT_1D_ForwardAndInversion.py
|
||||||
|
:language: python
|
||||||
|
:linenos:
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
.. _examples_MT_3D_Foward:
|
||||||
|
|
||||||
|
.. --------------------------------- ..
|
||||||
|
.. ..
|
||||||
|
.. THIS FILE IS AUTO GENEREATED ..
|
||||||
|
.. ..
|
||||||
|
.. SimPEG/Examples/__init__.py ..
|
||||||
|
.. ..
|
||||||
|
.. --------------------------------- ..
|
||||||
|
|
||||||
|
|
||||||
|
MT: 3D: Forward
|
||||||
|
=======================
|
||||||
|
|
||||||
|
Forward model 3D MT data.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.. plot::
|
||||||
|
|
||||||
|
from SimPEG import Examples
|
||||||
|
Examples.MT_3D_Foward.run()
|
||||||
|
|
||||||
|
.. literalinclude:: ../../SimPEG/Examples/MT_3D_Foward.py
|
||||||
|
:language: python
|
||||||
|
:linenos:
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 56 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 113 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 75 KiB |
+1
-3
@@ -49,10 +49,8 @@ Examples
|
|||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
|
||||||
api_Examples
|
api_Examples
|
||||||
|
|
||||||
|
|
||||||
Packages
|
Packages
|
||||||
********
|
********
|
||||||
|
|
||||||
@@ -60,9 +58,9 @@ Packages
|
|||||||
:maxdepth: 3
|
:maxdepth: 3
|
||||||
|
|
||||||
em/index
|
em/index
|
||||||
|
mt/index
|
||||||
flow/index
|
flow/index
|
||||||
|
|
||||||
|
|
||||||
Finite Volume
|
Finite Volume
|
||||||
*************
|
*************
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
Magnetotellurics
|
||||||
|
****************
|
||||||
|
|
||||||
|
SimPEG (Simulation and Parameter Estimation in Geophysics) is a python
|
||||||
|
package for simulation and gradient based parameter estimation in the
|
||||||
|
context of geoscience applications.
|
||||||
|
|
||||||
|
simpegMT uses SimPEG as the framework for the forward and inverse
|
||||||
|
magnetotellurics geophysical problems.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Problem
|
||||||
|
=======
|
||||||
|
|
||||||
|
.. autoclass:: SimPEG.MT.BaseMT.BaseMTProblem
|
||||||
|
:show-inheritance:
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
@@ -1,8 +1,28 @@
|
|||||||
import unittest
|
import unittest
|
||||||
import sys
|
import sys
|
||||||
|
import os
|
||||||
from SimPEG import Examples
|
from SimPEG import Examples
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
class compareInitFiles(unittest.TestCase):
|
||||||
|
def test_compareInitFiles(self):
|
||||||
|
print 'Checking that __init__.py up-to-date in SimPEG/Examples'
|
||||||
|
fName = os.path.abspath(__file__)
|
||||||
|
ExamplesDir = os.path.sep.join(fName.split(os.path.sep)[:-3] + ['SimPEG', 'Examples'])
|
||||||
|
|
||||||
|
files = os.listdir(ExamplesDir)
|
||||||
|
|
||||||
|
pyfiles = []
|
||||||
|
[pyfiles.append(py.rstrip('.py')) for py in files if py.endswith('.py') and py != '__init__.py']
|
||||||
|
|
||||||
|
setdiff = set(pyfiles) - set(Examples.__examples__)
|
||||||
|
|
||||||
|
print ' Any missing files? ', setdiff
|
||||||
|
|
||||||
|
didpass = (setdiff == set())
|
||||||
|
|
||||||
|
self.assertTrue(didpass, "Examples not up to date, run 'python __init__.py' from SimPEG/Examples to update")
|
||||||
|
|
||||||
def get(test):
|
def get(test):
|
||||||
def test_func(self):
|
def test_func(self):
|
||||||
print '\nTesting %s.run(plotIt=False)\n'%test
|
print '\nTesting %s.run(plotIt=False)\n'%test
|
||||||
@@ -10,11 +30,11 @@ def get(test):
|
|||||||
self.assertTrue(True)
|
self.assertTrue(True)
|
||||||
return test_func
|
return test_func
|
||||||
attrs = dict()
|
attrs = dict()
|
||||||
|
|
||||||
for test in Examples.__examples__:
|
for test in Examples.__examples__:
|
||||||
attrs['test_'+test] = get(test)
|
attrs['test_'+test] = get(test)
|
||||||
|
|
||||||
TestExamples = type('TestExamples', (unittest.TestCase,), attrs)
|
TestExamples = type('TestExamples', (unittest.TestCase,), attrs)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import os
|
||||||
|
import glob
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
test_file_strings = glob.glob('test_*.py')
|
||||||
|
module_strings = [str[0:len(str)-3] for str in test_file_strings]
|
||||||
|
suites = [unittest.defaultTestLoader.loadTestsFromName(str) for str
|
||||||
|
in module_strings]
|
||||||
|
testSuite = unittest.TestSuite(suites)
|
||||||
|
|
||||||
|
unittest.TextTestRunner(verbosity=2).run(testSuite)
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import unittest
|
||||||
|
from SimPEG import *
|
||||||
|
from SimPEG import MT
|
||||||
|
|
||||||
|
TOL = 1e-6
|
||||||
|
|
||||||
|
def appResPhs(freq,z):
|
||||||
|
app_res = ((1./(8e-7*np.pi**2))/freq)*np.abs(z)**2
|
||||||
|
app_phs = np.arctan2(-z.imag,z.real)*(180/np.pi)
|
||||||
|
return app_res, app_phs
|
||||||
|
|
||||||
|
def appResNorm(sigmaHalf):
|
||||||
|
nFreq = 26
|
||||||
|
|
||||||
|
m1d = Mesh.TensorMesh([[(100,5,1.5),(100.,10),(100,5,1.5)]], x0=['C'])
|
||||||
|
sigma = np.zeros(m1d.nC) + sigmaHalf
|
||||||
|
sigma[m1d.gridCC[:]>200] = 1e-8
|
||||||
|
|
||||||
|
# Calculate the analytic fields
|
||||||
|
freqs = np.logspace(4,-4,nFreq)
|
||||||
|
Z = []
|
||||||
|
for freq in freqs:
|
||||||
|
Ed, Eu, Hd, Hu = MT.Utils.getEHfields(m1d,sigma,freq,np.array([200]))
|
||||||
|
Z.append((Ed + Eu)/(Hd + Hu))
|
||||||
|
|
||||||
|
Zarr = np.concatenate(Z)
|
||||||
|
|
||||||
|
app_r, app_p = appResPhs(freqs,Zarr)
|
||||||
|
|
||||||
|
return np.linalg.norm(np.abs(app_r - np.ones(nFreq)/sigmaHalf)) / np.log10(sigmaHalf)
|
||||||
|
|
||||||
|
|
||||||
|
class TestAnalytics(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
pass
|
||||||
|
def test_appRes2en1(self):self.assertLess(appResNorm(2e-1), TOL)
|
||||||
|
def test_appRes2en2(self):self.assertLess(appResNorm(2e-2), TOL)
|
||||||
|
def test_appRes2en3(self):self.assertLess(appResNorm(2e-3), TOL)
|
||||||
|
def test_appRes2en4(self):self.assertLess(appResNorm(2e-4), TOL)
|
||||||
|
def test_appRes2en5(self):self.assertLess(appResNorm(2e-5), TOL)
|
||||||
|
def test_appRes2en6(self):self.assertLess(appResNorm(2e-6), TOL)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
import unittest
|
||||||
|
import SimPEG as simpeg
|
||||||
|
from SimPEG import MT
|
||||||
|
from SimPEG.Utils import meshTensor
|
||||||
|
import numpy as np
|
||||||
|
# Define the tolerances
|
||||||
|
TOLr = 5e-2
|
||||||
|
TOLp = 5e-2
|
||||||
|
|
||||||
|
|
||||||
|
def setupSurvey(sigmaHalf,tD=True):
|
||||||
|
|
||||||
|
# Frequency
|
||||||
|
nFreq = 33
|
||||||
|
freqs = np.logspace(3,-3,nFreq)
|
||||||
|
# Make the mesh
|
||||||
|
ct = 5
|
||||||
|
air = meshTensor([(ct,25,1.3)])
|
||||||
|
# coreT0 = meshTensor([(ct,15,1.2)])
|
||||||
|
# coreT1 = np.kron(meshTensor([(coreT0[-1],15,1.3)]),np.ones((7,)))
|
||||||
|
core = np.concatenate( ( np.kron(meshTensor([(ct,15,-1.2)]),np.ones((10,))) , meshTensor([(ct,20)]) ) )
|
||||||
|
bot = meshTensor([(core[0],10,-1.3)])
|
||||||
|
x0 = -np.array([np.sum(np.concatenate((core,bot)))])
|
||||||
|
m1d = simpeg.Mesh.TensorMesh([np.concatenate((bot,core,air))], x0=x0)
|
||||||
|
# Make the model
|
||||||
|
sigma = np.zeros(m1d.nC) + sigmaHalf
|
||||||
|
sigma[m1d.gridCC > 0 ] = 1e-8
|
||||||
|
|
||||||
|
rxList = []
|
||||||
|
for rxType in ['z1dr','z1di']:
|
||||||
|
rxList.append(MT.Rx(simpeg.mkvc(np.array([0.0]),2).T,rxType))
|
||||||
|
# Source list
|
||||||
|
srcList =[]
|
||||||
|
if tD:
|
||||||
|
for freq in freqs:
|
||||||
|
srcList.append(MT.SrcMT.polxy_1DhomotD(rxList,freq))
|
||||||
|
else:
|
||||||
|
for freq in freqs:
|
||||||
|
srcList.append(MT.SrcMT.polxy_1Dprimary(rxList,freq))
|
||||||
|
|
||||||
|
survey = MT.Survey(srcList)
|
||||||
|
return survey, sigma, m1d
|
||||||
|
|
||||||
|
def getAppResPhs(MTdata):
|
||||||
|
# Make impedance
|
||||||
|
def appResPhs(freq,z):
|
||||||
|
app_res = ((1./(8e-7*np.pi**2))/freq)*np.abs(z)**2
|
||||||
|
app_phs = np.arctan2(z.imag,z.real)*(180/np.pi)
|
||||||
|
return app_res, app_phs
|
||||||
|
zList = []
|
||||||
|
for src in MTdata.survey.srcList:
|
||||||
|
zc = [src.freq]
|
||||||
|
for rx in src.rxList:
|
||||||
|
if 'i' in rx.rxType:
|
||||||
|
m=1j
|
||||||
|
else:
|
||||||
|
m = 1
|
||||||
|
zc.append(m*MTdata[src,rx])
|
||||||
|
zList.append(zc)
|
||||||
|
return [appResPhs(zList[i][0],np.sum(zList[i][1:3])) for i in np.arange(len(zList))]
|
||||||
|
|
||||||
|
def appRes_TotalFieldNorm(sigmaHalf):
|
||||||
|
|
||||||
|
# Make the survey
|
||||||
|
survey, sigma, mesh = setupSurvey(sigmaHalf)
|
||||||
|
problem = MT.Problem1D.eForm_TotalField(mesh)
|
||||||
|
problem.pair(survey)
|
||||||
|
|
||||||
|
# Get the fields
|
||||||
|
fields = problem.fields(sigma)
|
||||||
|
|
||||||
|
# Project the data
|
||||||
|
data = survey.projectFields(fields)
|
||||||
|
|
||||||
|
# Calculate the app res and phs
|
||||||
|
app_r = np.array(getAppResPhs(data))[:,0]
|
||||||
|
|
||||||
|
return np.linalg.norm(np.abs(app_r - np.ones(survey.nFreq)/sigmaHalf)*sigmaHalf)
|
||||||
|
|
||||||
|
def appPhs_TotalFieldNorm(sigmaHalf):
|
||||||
|
|
||||||
|
# Make the survey
|
||||||
|
survey, sigma, mesh = setupSurvey(sigmaHalf)
|
||||||
|
problem = MT.Problem1D.eForm_TotalField(mesh)
|
||||||
|
problem.pair(survey)
|
||||||
|
|
||||||
|
# Get the fields
|
||||||
|
fields = problem.fields(sigma)
|
||||||
|
|
||||||
|
# Project the data
|
||||||
|
data = survey.projectFields(fields)
|
||||||
|
|
||||||
|
# Calculate the app phs
|
||||||
|
app_p = np.array(getAppResPhs(data))[:,1]
|
||||||
|
|
||||||
|
return np.linalg.norm(np.abs(app_p - np.ones(survey.nFreq)*45)/ 45)
|
||||||
|
|
||||||
|
def appRes_psFieldNorm(sigmaHalf):
|
||||||
|
|
||||||
|
# Make the survey
|
||||||
|
survey, sigma, mesh = setupSurvey(sigmaHalf,False)
|
||||||
|
problem = MT.Problem1D.eForm_psField(mesh, sigmaPrimary = sigma)
|
||||||
|
problem.pair(survey)
|
||||||
|
|
||||||
|
# Get the fields
|
||||||
|
fields = problem.fields(sigma)
|
||||||
|
|
||||||
|
# Project the data
|
||||||
|
data = survey.projectFields(fields)
|
||||||
|
|
||||||
|
# Calculate the app res and phs
|
||||||
|
app_r = np.array(getAppResPhs(data))[:,0]
|
||||||
|
|
||||||
|
return np.linalg.norm(np.abs(app_r - np.ones(survey.nFreq)/sigmaHalf)*sigmaHalf)
|
||||||
|
|
||||||
|
def appPhs_psFieldNorm(sigmaHalf):
|
||||||
|
|
||||||
|
# Make the survey
|
||||||
|
survey, sigma, mesh = setupSurvey(sigmaHalf,False)
|
||||||
|
problem = MT.Problem1D.eForm_psField(mesh, sigmaPrimary = sigma)
|
||||||
|
problem.pair(survey)
|
||||||
|
|
||||||
|
# Get the fields
|
||||||
|
fields = problem.fields(sigma)
|
||||||
|
|
||||||
|
# Project the data
|
||||||
|
data = survey.projectFields(fields)
|
||||||
|
|
||||||
|
# Calculate the app phs
|
||||||
|
app_p = np.array(getAppResPhs(data))[:,1]
|
||||||
|
|
||||||
|
return np.linalg.norm(np.abs(app_p - np.ones(survey.nFreq)*45)/ 45)
|
||||||
|
|
||||||
|
class TestAnalytics(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
pass
|
||||||
|
# Total Fields
|
||||||
|
# def test_appRes2en1(self):self.assertLess(appRes_TotalFieldNorm(2e-1), TOLr)
|
||||||
|
# def test_appPhs2en1(self):self.assertLess(appPhs_TotalFieldNorm(2e-1), TOLp)
|
||||||
|
|
||||||
|
# def test_appRes2en2(self):self.assertLess(appRes_TotalFieldNorm(2e-2), TOLr)
|
||||||
|
# def test_appPhs2en2(self):self.assertLess(appPhs_TotalFieldNorm(2e-2), TOLp)
|
||||||
|
|
||||||
|
# def test_appRes2en3(self):self.assertLess(appRes_TotalFieldNorm(2e-3), TOLr)
|
||||||
|
# def test_appPhs2en3(self):self.assertLess(appPhs_TotalFieldNorm(2e-3), TOLp)
|
||||||
|
|
||||||
|
# def test_appRes2en4(self):self.assertLess(appRes_TotalFieldNorm(2e-4), TOLr)
|
||||||
|
# def test_appPhs2en4(self):self.assertLess(appPhs_TotalFieldNorm(2e-4), TOLp)
|
||||||
|
|
||||||
|
# def test_appRes2en5(self):self.assertLess(appRes_TotalFieldNorm(2e-5), TOLr)
|
||||||
|
# def test_appPhs2en5(self):self.assertLess(appPhs_TotalFieldNorm(2e-5), TOLp)
|
||||||
|
|
||||||
|
# def test_appRes2en6(self):self.assertLess(appRes_TotalFieldNorm(2e-6), TOLr)
|
||||||
|
# def test_appPhs2en6(self):self.assertLess(appPhs_TotalFieldNorm(2e-6), TOLp)
|
||||||
|
|
||||||
|
# Primary/secondary
|
||||||
|
def test_appRes2en2_ps(self):self.assertLess(appRes_psFieldNorm(2e-2), TOLr)
|
||||||
|
def test_appPhs2en2_ps(self):self.assertLess(appPhs_psFieldNorm(2e-2), TOLp)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
import unittest
|
||||||
|
import SimPEG as simpeg
|
||||||
|
from SimPEG import MT
|
||||||
|
from SimPEG.Utils import meshTensor
|
||||||
|
import numpy as np
|
||||||
|
# Define the tolerances
|
||||||
|
TOLr = 5e-2
|
||||||
|
TOLp = 5e-2
|
||||||
|
|
||||||
|
|
||||||
|
def setupSurvey(sigmaHalf,tD=True):
|
||||||
|
|
||||||
|
# Frequency
|
||||||
|
nFreq = 33
|
||||||
|
freqs = np.logspace(3,-3,nFreq)
|
||||||
|
# Make the mesh
|
||||||
|
ct = 5
|
||||||
|
air = meshTensor([(ct,25,1.3)])
|
||||||
|
# coreT0 = meshTensor([(ct,15,1.2)])
|
||||||
|
# coreT1 = np.kron(meshTensor([(coreT0[-1],15,1.3)]),np.ones((7,)))
|
||||||
|
core = np.concatenate( ( np.kron(meshTensor([(ct,15,-1.2)]),np.ones((10,))) , meshTensor([(ct,20)]) ) )
|
||||||
|
bot = meshTensor([(core[0],15,-1.3)])
|
||||||
|
x0 = -np.array([np.sum(np.concatenate((core,bot)))])
|
||||||
|
m1d = simpeg.Mesh.TensorMesh([np.concatenate((bot,core,air))], x0=x0)
|
||||||
|
# Make the model
|
||||||
|
sigma = np.zeros(m1d.nC) + sigmaHalf
|
||||||
|
sigma[m1d.gridCC > 0 ] = 1e-8
|
||||||
|
sigmaBack = sigma.copy()
|
||||||
|
# Add structure
|
||||||
|
shallow = (m1d.gridCC < -200) * (m1d.gridCC > -600)
|
||||||
|
deep = (m1d.gridCC < -3000) * (m1d.gridCC > -5000)
|
||||||
|
sigma[shallow] = 1
|
||||||
|
sigma[deep] = 0.1
|
||||||
|
|
||||||
|
rxList = []
|
||||||
|
for rxType in ['z1dr','z1di']:
|
||||||
|
rxList.append(MT.Rx(simpeg.mkvc(np.array([0.0]),2).T,rxType))
|
||||||
|
# Source list
|
||||||
|
srcList =[]
|
||||||
|
if tD:
|
||||||
|
for freq in freqs:
|
||||||
|
srcList.append(MT.SrcMT.polxy_1DhomotD(rxList,freq))
|
||||||
|
else:
|
||||||
|
for freq in freqs:
|
||||||
|
srcList.append(MT.SrcMT.polxy_1Dprimary(rxList,freq))
|
||||||
|
|
||||||
|
survey = MT.Survey(srcList)
|
||||||
|
return survey, sigma, m1d
|
||||||
|
|
||||||
|
def getAppResPhs(MTdata):
|
||||||
|
# Make impedance
|
||||||
|
def appResPhs(freq,z):
|
||||||
|
app_res = ((1./(8e-7*np.pi**2))/freq)*np.abs(z)**2
|
||||||
|
app_phs = np.arctan2(z.imag,z.real)*(180/np.pi)
|
||||||
|
return app_res, app_phs
|
||||||
|
zList = []
|
||||||
|
for src in MTdata.survey.srcList:
|
||||||
|
zc = [src.freq]
|
||||||
|
for rx in src.rxList:
|
||||||
|
if 'i' in rx.rxType:
|
||||||
|
m=1j
|
||||||
|
else:
|
||||||
|
m = 1
|
||||||
|
zc.append(m*MTdata[src,rx])
|
||||||
|
zList.append(zc)
|
||||||
|
return [appResPhs(zList[i][0],np.sum(zList[i][1:3])) for i in np.arange(len(zList))]
|
||||||
|
|
||||||
|
def calculateAnalyticSolution(srcList,mesh,model):
|
||||||
|
surveyAna = MT.Survey(srcList)
|
||||||
|
data1D = MT.Data(surveyAna)
|
||||||
|
for src in surveyAna.srcList:
|
||||||
|
elev = src.rxList[0].locs[0]
|
||||||
|
anaEd, anaEu, anaHd, anaHu = MT.Utils.MT1Danalytic.getEHfields(mesh,model,src.freq,elev)
|
||||||
|
anaE = anaEd+anaEu
|
||||||
|
anaH = anaHd+anaHu
|
||||||
|
# Scale the solution
|
||||||
|
# anaE = (anaEtemp/anaEtemp[-1])#.conj()
|
||||||
|
# anaH = (anaHtemp/anaEtemp[-1])#.conj()
|
||||||
|
anaZ = anaE/anaH
|
||||||
|
for rx in src.rxList:
|
||||||
|
data1D[src,rx] = getattr(anaZ, rx.projComp)
|
||||||
|
return data1D
|
||||||
|
|
||||||
|
def dataMis_AnalyticTotalDomain(sigmaHalf):
|
||||||
|
|
||||||
|
# Make the survey
|
||||||
|
|
||||||
|
# Total domain solution
|
||||||
|
surveyTD, sigma, mesh = setupSurvey(sigmaHalf)
|
||||||
|
problemTD = MT.Problem1D.eForm_TotalField(mesh)
|
||||||
|
problemTD.pair(surveyTD)
|
||||||
|
# Analytic data
|
||||||
|
dataAnaObj = calculateAnalyticSolution(surveyTD.srcList,mesh,sigma)
|
||||||
|
# dataTDObj = MT.DataMT.DataMT(surveyTD, surveyTD.dpred(sigma))
|
||||||
|
dataTD = surveyTD.dpred(sigma)
|
||||||
|
dataAna = simpeg.mkvc(dataAnaObj)
|
||||||
|
return np.all((dataTD - dataAna)/dataAna < 2.)
|
||||||
|
# surveyTD.dtrue = -simpeg.mkvc(dataAna,2)
|
||||||
|
# surveyTD.dobs = -simpeg.mkvc(dataAna,2)
|
||||||
|
# surveyTD.Wd = np.ones(surveyTD.dtrue.shape) #/(np.abs(surveyTD.dtrue)*0.01)
|
||||||
|
# # Setup the data misfit
|
||||||
|
# dmis = simpeg.DataMisfit.l2_DataMisfit(surveyTD)
|
||||||
|
# dmis.Wd = surveyTD.Wd
|
||||||
|
# return dmis.eval(sigma)
|
||||||
|
|
||||||
|
|
||||||
|
def dataMis_AnalyticPrimarySecondary(sigmaHalf):
|
||||||
|
|
||||||
|
# Make the survey
|
||||||
|
# Primary secondary
|
||||||
|
surveyPS, sigmaPS, mesh = setupSurvey(sigmaHalf,tD=False)
|
||||||
|
problemPS = MT.Problem1D.eForm_psField(mesh)
|
||||||
|
problemPS.sigmaPrimary = sigmaPS
|
||||||
|
problemPS.pair(surveyPS)
|
||||||
|
# Analytic data
|
||||||
|
dataAnaObj = calculateAnalyticSolution(surveyPS.srcList,mesh,sigmaPS)
|
||||||
|
|
||||||
|
dataPS = surveyPS.dpred(sigmaPS)
|
||||||
|
dataAna = simpeg.mkvc(dataAnaObj)
|
||||||
|
return np.all((dataPS - dataAna)/dataAna < 2.)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class TestNumericVsAnalytics(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
pass
|
||||||
|
# Total Fields
|
||||||
|
# def test_appRes2en2(self):self.assertTrue(dataMis_AnalyticTotalDomain(2e-2))
|
||||||
|
|
||||||
|
# Primary/secondary
|
||||||
|
def test_appRes2en2_ps(self):self.assertTrue(dataMis_AnalyticPrimarySecondary(2e-2))
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
@@ -0,0 +1,271 @@
|
|||||||
|
# Test functions
|
||||||
|
from glob import glob
|
||||||
|
import numpy as np, sys, os, time, scipy, subprocess
|
||||||
|
import SimPEG as simpeg
|
||||||
|
import unittest
|
||||||
|
from SimPEG import MT
|
||||||
|
from SimPEG.Utils import meshTensor
|
||||||
|
from scipy.constants import mu_0
|
||||||
|
|
||||||
|
TOLr = 5e-2
|
||||||
|
TOL = 1e-4
|
||||||
|
FLR = 1e-20 # "zero", so if residual below this --> pass regardless of order
|
||||||
|
CONDUCTIVITY = 1e1
|
||||||
|
MU = mu_0
|
||||||
|
freq = [1e-1, 2e-1]
|
||||||
|
addrandoms = True
|
||||||
|
|
||||||
|
|
||||||
|
def getInputs():
|
||||||
|
"""
|
||||||
|
Function that returns Mesh, freqs, rx_loc, elev.
|
||||||
|
"""
|
||||||
|
# Make a mesh
|
||||||
|
# M = simpeg.Mesh.TensorMesh([[(100,5,-1.5),(100.,10),(100,5,1.5)],[(100,5,-1.5),(100.,10),(100,5,1.5)],[(100,5,1.6),(100.,10),(100,3,2)]], x0=['C','C',-3529.5360])
|
||||||
|
# M = simpeg.Mesh.TensorMesh([[(1000,6,-1.5),(1000.,6),(1000,6,1.5)],[(1000,6,-1.5),(1000.,2),(1000,6,1.5)],[(1000,6,-1.3),(1000.,6),(1000,6,1.3)]], x0=['C','C','C'])# Setup the model
|
||||||
|
M = simpeg.Mesh.TensorMesh([[(1000,6,-1.5),(1000.,4),(1000,6,1.5)],[(1000,6,-1.5),(1000.,4),(1000,6,1.5)],[(500,8,-1.3),(500.,8),(500,8,1.3)]], x0=['C','C','C'])# Setup the model
|
||||||
|
# Set the frequencies
|
||||||
|
freqs = np.logspace(1,-3,5)
|
||||||
|
elev = 0
|
||||||
|
|
||||||
|
## Setup the the survey object
|
||||||
|
# Receiver locations
|
||||||
|
rx_x, rx_y = np.meshgrid(np.arange(-1000,1001,500),np.arange(-1000,1001,500))
|
||||||
|
rx_loc = np.hstack((simpeg.Utils.mkvc(rx_x,2),simpeg.Utils.mkvc(rx_y,2),elev+np.zeros((np.prod(rx_x.shape),1))))
|
||||||
|
|
||||||
|
return M, freqs, rx_loc, elev
|
||||||
|
|
||||||
|
def random(conds):
|
||||||
|
''' Returns a halfspace model based on the inputs'''
|
||||||
|
M, freqs, rx_loc, elev = getInputs()
|
||||||
|
|
||||||
|
# Backround
|
||||||
|
sigBG = np.ones(M.nC)*conds
|
||||||
|
# Add randomness to the model (10% of the value).
|
||||||
|
sig = np.exp( np.log(sigBG) + np.random.randn(M.nC)*(conds)*1e-1 )
|
||||||
|
|
||||||
|
return (M, freqs, sig, sigBG, rx_loc)
|
||||||
|
|
||||||
|
def halfSpace(conds):
|
||||||
|
''' Returns a halfspace model based on the inputs'''
|
||||||
|
M, freqs, rx_loc, elev = getInputs()
|
||||||
|
|
||||||
|
# Model
|
||||||
|
ccM = M.gridCC
|
||||||
|
# conds = [1e-2]
|
||||||
|
groundInd = ccM[:,2] < elev
|
||||||
|
sig = np.zeros(M.nC) + 1e-8
|
||||||
|
sig[groundInd] = conds
|
||||||
|
# Set the background, not the same as the model
|
||||||
|
sigBG = np.zeros(M.nC) + 1e-8
|
||||||
|
sigBG[groundInd] = conds
|
||||||
|
|
||||||
|
return (M, freqs, sig, sigBG, rx_loc)
|
||||||
|
|
||||||
|
def blockInhalfSpace(conds):
|
||||||
|
''' Returns a halfspace model based on the inputs'''
|
||||||
|
M, freqs, rx_loc, elev = getInputs()
|
||||||
|
|
||||||
|
# Model
|
||||||
|
ccM = M.gridCC
|
||||||
|
# conds = [1e-2]
|
||||||
|
groundInd = ccM[:,2] < elev
|
||||||
|
sig = simpeg.Utils.ModelBuilder.defineBlock(M.gridCC,np.array([-1000,-1000,-1500]),np.array([1000,1000,-1000]),conds)
|
||||||
|
sig[~groundInd] = 1e-8
|
||||||
|
# Set the background, not the same as the model
|
||||||
|
sigBG = np.zeros(M.nC) + 1e-8
|
||||||
|
sigBG[groundInd] = conds[1]
|
||||||
|
|
||||||
|
return (M, freqs, sig, sigBG, rx_loc)
|
||||||
|
|
||||||
|
def twoLayer(conds):
|
||||||
|
''' Returns a 2 layer model based on the conductivity values given'''
|
||||||
|
M, freqs, rx_loc, elev = getInputs()
|
||||||
|
|
||||||
|
# Model
|
||||||
|
ccM = M.gridCC
|
||||||
|
groundInd = ccM[:,2] < elev
|
||||||
|
botInd = ccM[:,2] < -3000
|
||||||
|
sig = np.zeros(M.nC) + 1e-8
|
||||||
|
sig[groundInd] = conds[1]
|
||||||
|
sig[botInd] = conds[0]
|
||||||
|
# Set the background, not the same as the model
|
||||||
|
sigBG = np.zeros(M.nC) + 1e-8
|
||||||
|
sigBG[groundInd] = conds[1]
|
||||||
|
|
||||||
|
|
||||||
|
return (M, freqs, sig, sigBG, rx_loc)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def setupSimpegMTfwd_eForm_ps(inputSetup,comp='Imp',singleFreq=False,expMap=True):
|
||||||
|
M,freqs,sig,sigBG,rx_loc = inputSetup
|
||||||
|
# Make a receiver list
|
||||||
|
rxList = []
|
||||||
|
if comp == 'All':
|
||||||
|
for rxType in ['zxxr','zxxi','zxyr','zxyi','zyxr','zyxi','zyyr','zyyi','tzxr','tzxi','tzyr','tzyi']:
|
||||||
|
rxList.append(MT.Rx(rx_loc,rxType))
|
||||||
|
elif comp == 'Imp':
|
||||||
|
for rxType in ['zxxr','zxxi','zxyr','zxyi','zyxr','zyxi','zyyr','zyyi']:
|
||||||
|
rxList.append(MT.Rx(rx_loc,rxType))
|
||||||
|
elif comp == 'Tip':
|
||||||
|
for rxType in ['tzxr','tzxi','tzyr','tzyi']:
|
||||||
|
rxList.append(MT.Rx(rx_loc,rxType))
|
||||||
|
else:
|
||||||
|
rxList.append(MT.Rx(rx_loc,comp))
|
||||||
|
# Source list
|
||||||
|
srcList =[]
|
||||||
|
|
||||||
|
if singleFreq:
|
||||||
|
srcList.append(MT.SrcMT.polxy_1Dprimary(rxList,singleFreq))
|
||||||
|
else:
|
||||||
|
for freq in freqs:
|
||||||
|
srcList.append(MT.SrcMT.polxy_1Dprimary(rxList,freq))
|
||||||
|
# Survey MT
|
||||||
|
survey = MT.Survey(srcList)
|
||||||
|
|
||||||
|
## Setup the problem object
|
||||||
|
sigma1d = M.r(sigBG,'CC','CC','M')[0,0,:]
|
||||||
|
if expMap:
|
||||||
|
problem = MT.Problem3D.eForm_ps(M,sigmaPrimary= np.log(sigma1d) )
|
||||||
|
problem.mapping = simpeg.Maps.ExpMap(problem.mesh)
|
||||||
|
problem.curModel = np.log(sig)
|
||||||
|
else:
|
||||||
|
problem = MT.Problem3D.eForm_ps(M,sigmaPrimary= sigma1d)
|
||||||
|
problem.curModel = sig
|
||||||
|
problem.pair(survey)
|
||||||
|
problem.verbose = False
|
||||||
|
try:
|
||||||
|
from pymatsolver import MumpsSolver
|
||||||
|
problem.Solver = MumpsSolver
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return (survey, problem)
|
||||||
|
|
||||||
|
def getAppResPhs(MTdata):
|
||||||
|
# Make impedance
|
||||||
|
def appResPhs(freq,z):
|
||||||
|
app_res = ((1./(8e-7*np.pi**2))/freq)*np.abs(z)**2
|
||||||
|
app_phs = np.arctan2(z.imag,z.real)*(180/np.pi)
|
||||||
|
return app_res, app_phs
|
||||||
|
recData = MTdata.toRecArray('Complex')
|
||||||
|
return appResPhs(recData['freq'],recData['zxy']), appResPhs(recData['freq'],recData['zyx'])
|
||||||
|
|
||||||
|
def JvecAdjointTest(inputSetup,comp='All',freq=False):
|
||||||
|
(M, freqs, sig, sigBG, rx_loc) = inputSetup
|
||||||
|
survey, problem = setupSimpegMTfwd_eForm_ps(inputSetup,comp='All',singleFreq=freq)
|
||||||
|
print 'Adjoint test of eForm primary/secondary for {:s} comp at {:s}\n'.format(comp,str(survey.freqs))
|
||||||
|
|
||||||
|
m = sig
|
||||||
|
u = problem.fields(m)
|
||||||
|
|
||||||
|
v = np.random.rand(survey.nD,)
|
||||||
|
# print problem.PropMap.PropModel.nP
|
||||||
|
w = np.random.rand(problem.mesh.nC,)
|
||||||
|
|
||||||
|
vJw = v.ravel().dot(problem.Jvec(m, w, u))
|
||||||
|
wJtv = w.ravel().dot(problem.Jtvec(m, v, u))
|
||||||
|
tol = np.max([TOL*(10**int(np.log10(np.abs(vJw)))),FLR])
|
||||||
|
print ' vJw wJtv vJw - wJtv tol abs(vJw - wJtv) < tol'
|
||||||
|
print vJw, wJtv, vJw - wJtv, tol, np.abs(vJw - wJtv) < tol
|
||||||
|
return np.abs(vJw - wJtv) < tol
|
||||||
|
|
||||||
|
# Test the Jvec derivative
|
||||||
|
def DerivJvecTest(inputSetup,comp='All',freq=False,expMap=True):
|
||||||
|
(M, freqs, sig, sigBG, rx_loc) = inputSetup
|
||||||
|
survey, problem = setupSimpegMTfwd_eForm_ps(inputSetup,comp=comp,singleFreq=freq,expMap=expMap)
|
||||||
|
print 'Derivative test of Jvec for eForm primary/secondary for {:s} comp at {:s}\n'.format(comp,survey.freqs)
|
||||||
|
# problem.mapping = simpeg.Maps.ExpMap(problem.mesh)
|
||||||
|
# problem.sigmaPrimary = np.log(sigBG)
|
||||||
|
x0 = np.log(sigBG)
|
||||||
|
# cond = sig[0]
|
||||||
|
# x0 = np.log(np.ones(problem.mesh.nC)*cond)
|
||||||
|
# problem.sigmaPrimary = x0
|
||||||
|
# if True:
|
||||||
|
# x0 = x0 + np.random.randn(problem.mesh.nC)*cond*1e-1
|
||||||
|
survey = problem.survey
|
||||||
|
def fun(x):
|
||||||
|
return survey.dpred(x), lambda x: problem.Jvec(x0, x)
|
||||||
|
return simpeg.Tests.checkDerivative(fun, x0, num=3, plotIt=False, eps=FLR)
|
||||||
|
|
||||||
|
def DerivProjfieldsTest(inputSetup,comp='All',freq=False):
|
||||||
|
|
||||||
|
survey, problem = setupSimpegMTfwd_eForm_ps(inputSetup,comp,freq)
|
||||||
|
print 'Derivative test of data projection for eFormulation primary/secondary\n\n'
|
||||||
|
# problem.mapping = simpeg.Maps.ExpMap(problem.mesh)
|
||||||
|
# Initate things for the derivs Test
|
||||||
|
src = survey.srcList[0]
|
||||||
|
rx = src.rxList[0]
|
||||||
|
|
||||||
|
u0x = np.random.randn(survey.mesh.nE)+np.random.randn(survey.mesh.nE)*1j
|
||||||
|
u0y = np.random.randn(survey.mesh.nE)+np.random.randn(survey.mesh.nE)*1j
|
||||||
|
u0 = np.vstack((simpeg.mkvc(u0x,2),simpeg.mkvc(u0y,2)))
|
||||||
|
f0 = problem.fieldsPair(survey.mesh,survey)
|
||||||
|
# u0 = np.hstack((simpeg.mkvc(u0_px,2),simpeg.mkvc(u0_py,2)))
|
||||||
|
f0[src,'e_pxSolution'] = u0[:len(u0)/2]#u0x
|
||||||
|
f0[src,'e_pySolution'] = u0[len(u0)/2::]#u0y
|
||||||
|
|
||||||
|
def fun(u):
|
||||||
|
f = problem.fieldsPair(survey.mesh,survey)
|
||||||
|
f[src,'e_pxSolution'] = u[:len(u)/2]
|
||||||
|
f[src,'e_pySolution'] = u[len(u)/2::]
|
||||||
|
return rx.projectFields(src,survey.mesh,f), lambda t: rx.projectFieldsDeriv(src,survey.mesh,f0,simpeg.mkvc(t,2))
|
||||||
|
|
||||||
|
return simpeg.Tests.checkDerivative(fun, u0, num=3, plotIt=False, eps=FLR)
|
||||||
|
|
||||||
|
def appResPhsHalfspace_eFrom_ps_Norm(sigmaHalf,appR=True,expMap=False):
|
||||||
|
if appR:
|
||||||
|
label = 'resistivity'
|
||||||
|
else:
|
||||||
|
label = 'phase'
|
||||||
|
# Make the survey and the problem
|
||||||
|
survey, problem = setupSimpegMTfwd_eForm_ps(halfSpace(sigmaHalf),expMap=expMap)
|
||||||
|
print 'Apperent {:s} test of eFormulation primary/secondary at {:g}\n\n'.format(label,sigmaHalf)
|
||||||
|
|
||||||
|
data = problem.dataPair(survey,survey.dpred(problem.curModel))
|
||||||
|
# Calculate the app phs
|
||||||
|
app_rpxy, app_rpyx = np.array(getAppResPhs(data))
|
||||||
|
if appR:
|
||||||
|
return np.all(np.abs(app_rpxy[0,:] - 1./sigmaHalf) * sigmaHalf < .4)
|
||||||
|
else:
|
||||||
|
return np.all(np.abs(app_rpxy[1,:] + 135) / 135 < .4)
|
||||||
|
|
||||||
|
class TestAnalytics(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
pass
|
||||||
|
# # Test apparent resistivity and phase
|
||||||
|
def test_appRes1en2(self):self.assertTrue(appResPhsHalfspace_eFrom_ps_Norm(1e-2))
|
||||||
|
def test_appPhs1en2(self):self.assertTrue(appResPhsHalfspace_eFrom_ps_Norm(1e-2,False))
|
||||||
|
|
||||||
|
def test_appRes1en3(self):self.assertTrue(appResPhsHalfspace_eFrom_ps_Norm(1e-3))
|
||||||
|
def test_appPhs1en3(self):self.assertTrue(appResPhsHalfspace_eFrom_ps_Norm(1e-3,False))
|
||||||
|
|
||||||
|
# Do a derivative test
|
||||||
|
def test_derivProj1(self):self.assertTrue(DerivProjfieldsTest(halfSpace(1e-2)))
|
||||||
|
|
||||||
|
# Do a derivative test of Jvec
|
||||||
|
# def test_derivJvec_zxxr(self):self.assertTrue(DerivJvecTest(random(1e-2),'zxxr',.1))
|
||||||
|
# def test_derivJvec_zxxi(self):self.assertTrue(DerivJvecTest(random(1e-2),'zxxi',.1))
|
||||||
|
# def test_derivJvec_zxyr(self):self.assertTrue(DerivJvecTest(random(1e-2),'zxyr',.1))
|
||||||
|
# def test_derivJvec_zxyi(self):self.assertTrue(DerivJvecTest(random(1e-2),'zxyi',.1))
|
||||||
|
# def test_derivJvec_zyxr(self):self.assertTrue(DerivJvecTest(random(1e-2),'zyxr',.1))
|
||||||
|
# def test_derivJvec_zyxi(self):self.assertTrue(DerivJvecTest(random(1e-2),'zyxi',.1))
|
||||||
|
# def test_derivJvec_zyyr(self):self.assertTrue(DerivJvecTest(random(1e-2),'zyyr',.1))
|
||||||
|
# def test_derivJvec_zyyi(self):self.assertTrue(DerivJvecTest(random(1e-2),'zyyi',.1))
|
||||||
|
def test_derivJvec_All(self):self.assertTrue(DerivJvecTest(random(1e-2),'All',.1))
|
||||||
|
|
||||||
|
# Test the adjoint of Jvec and Jtvec
|
||||||
|
# def test_JvecAdjoint_zxxr(self):self.assertTrue(JvecAdjointTest(random(1e-2),'zxxr',.1))
|
||||||
|
# def test_JvecAdjoint_zxxi(self):self.assertTrue(JvecAdjointTest(random(1e-2),'zxxi',.1))
|
||||||
|
# def test_JvecAdjoint_zxyr(self):self.assertTrue(JvecAdjointTest(random(1e-2),'zxyr',.1))
|
||||||
|
# def test_JvecAdjoint_zxyi(self):self.assertTrue(JvecAdjointTest(random(1e-2),'zxyi',.1))
|
||||||
|
# def test_JvecAdjoint_zyxr(self):self.assertTrue(JvecAdjointTest(random(1e-2),'zyxr',.1))
|
||||||
|
# def test_JvecAdjoint_zyxi(self):self.assertTrue(JvecAdjointTest(random(1e-2),'zyxi',.1))
|
||||||
|
# def test_JvecAdjoint_zyyr(self):self.assertTrue(JvecAdjointTest(random(1e-2),'zyyr',.1))
|
||||||
|
# def test_JvecAdjoint_zyyi(self):self.assertTrue(JvecAdjointTest(random(1e-2),'zyyi',.1))
|
||||||
|
def test_JvecAdjoint_All(self):self.assertTrue(JvecAdjointTest(random(1e-2),'All',.1))
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
Reference in New Issue
Block a user