mirror of
https://github.com/wassname/simpeg.git
synced 2026-07-05 17:02:21 +08:00
566 lines
25 KiB
Python
566 lines
25 KiB
Python
from SimPEG import Survey, Utils, Problem, Maps, np, sp, mkvc
|
|
from simpegEM.FDEM.SurveyFDEM import SrcFDEM
|
|
from simpegEM.Utils.EMUtils import omega
|
|
from scipy.constants import mu_0
|
|
import sys
|
|
from numpy.lib import recfunctions as recFunc
|
|
from DataMT import DataMT
|
|
from simpegMT.Sources import homo1DModelSource
|
|
#################
|
|
### Receivers ###
|
|
#################
|
|
|
|
class RxMT(Survey.BaseRx):
|
|
|
|
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):
|
|
Survey.BaseRx.__init__(self, locs, rxType)
|
|
|
|
@property
|
|
def projField(self):
|
|
"""
|
|
Field Type projection (e.g. e b ...)
|
|
:param str fracPos: Position of the field in the data ratio
|
|
|
|
"""
|
|
if 'numerator' in fracPos:
|
|
return self.knownRxTypes[self.rxType][0][0]
|
|
elif 'denominator' in fracPos:
|
|
return self.knownRxTypes[self.rxType][1][0]
|
|
else:
|
|
raise Exception('{s} is an unknown option. Use numerator or denominator.')
|
|
|
|
@property
|
|
def projGLoc(self):
|
|
"""
|
|
Grid Location projection (e.g. Ex Fy ...)
|
|
:param str fracPos: Position of the field in the data ratio
|
|
|
|
"""
|
|
if 'numerator' in fracPos:
|
|
return self.knownRxTypes[self.rxType][0][1]
|
|
elif 'denominator' in fracPos:
|
|
return self.knownRxTypes[self.rxType][0][1]
|
|
else:
|
|
raise Exception('{s} is an unknown option. Use numerator or denominator.')
|
|
@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 and return the correct data.
|
|
'''
|
|
|
|
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':
|
|
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 )
|
|
# 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
|
|
# Extract the data
|
|
if real_or_imag == 'imag':
|
|
Pv = 1j*PDeriv_real
|
|
elif real_or_imag == 'real':
|
|
Pv = PDeriv_real.astype(complex)
|
|
|
|
|
|
return Pv
|
|
|
|
|
|
###############
|
|
### Sources ###
|
|
###############
|
|
|
|
class srcMT(SrcFDEM): # Survey.BaseSrc):
|
|
'''
|
|
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)
|
|
rxPair = RxMT
|
|
|
|
|
|
def __init__(self, rxList, freq):
|
|
|
|
self.freq = float(freq)
|
|
Survey.BaseSrc.__init__(self, rxList)
|
|
|
|
# 1D sources
|
|
class srcMT_polxy_1DhomotD(srcMT):
|
|
"""
|
|
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):
|
|
srcMT.__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 srcMT_polxy_1Dprimary(srcMT):
|
|
"""
|
|
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
|
|
srcMT.__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 srcMT_polxy_3Dprimary(srcMT):
|
|
"""
|
|
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
|
|
srcMT.__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
|
|
|
|
##############
|
|
### Survey ###
|
|
##############
|
|
class SurveyMT(Survey.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
|
|
|
|
def __init__(self, srcList, **kwargs):
|
|
# Sort these by frequency
|
|
self.srcList = srcList
|
|
Survey.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 = DataMT(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.')
|
|
|