mirror of
https://github.com/wassname/simpeg.git
synced 2026-08-12 12:30:37 +08:00
Merge branch 'master' of https://github.com/simpeg/simpegem into em/dev
Conflicts: .coveragerc .gitignore .travis.yml docs/api_Utils.rst docs/conf.py docs/index.rst requirements.txt setup.py
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
from __future__ import division
|
||||
import numpy as np
|
||||
from scipy.constants import mu_0, pi
|
||||
from scipy.special import erf
|
||||
import matplotlib.pyplot as plt
|
||||
from SimPEG import Utils
|
||||
|
||||
|
||||
def hzAnalyticDipoleF(r, freq, sigma, secondary=True, mu=mu_0):
|
||||
"""
|
||||
4.56 in Ward and Hohmann
|
||||
|
||||
.. plot::
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
from SimPEG import EM
|
||||
freq = np.logspace(-1, 6, 61)
|
||||
test = EM.Analytics.FDEM.hzAnalyticDipoleF(100, freq, 0.001, secondary=False)
|
||||
plt.loglog(freq, abs(test.real))
|
||||
plt.loglog(freq, abs(test.imag))
|
||||
plt.title('Response at $r$=100m')
|
||||
plt.xlabel('Frequency')
|
||||
plt.ylabel('Response')
|
||||
plt.legend(('real','imag'))
|
||||
plt.show()
|
||||
|
||||
"""
|
||||
r = np.abs(r)
|
||||
k = np.sqrt(-1j*2.*np.pi*freq*mu*sigma)
|
||||
|
||||
m = 1
|
||||
front = m / (2. * np.pi * (k**2) * (r**5) )
|
||||
back = 9 - ( 9 + 9j * k * r - 4 * (k**2) * (r**2) - 1j * (k**3) * (r**3)) * np.exp(-1j*k*r)
|
||||
hz = front*back
|
||||
|
||||
if secondary:
|
||||
hp =-1/(4*np.pi*r**3)
|
||||
hz = hz-hp
|
||||
|
||||
if hz.ndim == 1:
|
||||
hz = Utils.mkvc(hz,2)
|
||||
|
||||
return hz
|
||||
|
||||
def MagneticDipoleWholeSpace(XYZ, srcLoc, sig, f, moment=1., orientation='X', mu = mu_0):
|
||||
"""
|
||||
Analytical solution for a dipole in a whole-space.
|
||||
|
||||
Equation 2.57 of Ward and Hohmann
|
||||
|
||||
TODOs:
|
||||
- set it up to instead take a mesh & survey
|
||||
- add E-fields
|
||||
- handle multiple frequencies
|
||||
- add divide by zero safety
|
||||
|
||||
|
||||
.. plot::
|
||||
|
||||
from SimPEG import EM
|
||||
import matplotlib.pyplot as plt
|
||||
freqs = np.logspace(-2,5,100)
|
||||
Bx, By, Bz = EM.Analytics.FDEM.AnalyticMagDipoleWholeSpace([0,100,0], [0,0,0], 1e-2, freqs, m=1, orientation='Z')
|
||||
plt.loglog(freqs, np.abs(Bz.real)/mu_0, 'b')
|
||||
plt.loglog(freqs, np.abs(Bz.imag)/mu_0, 'r')
|
||||
plt.legend(('real','imag'))
|
||||
plt.show()
|
||||
|
||||
|
||||
"""
|
||||
|
||||
XYZ = Utils.asArray_N_x_Dim(XYZ, 3)
|
||||
|
||||
dx = XYZ[:,0]-srcLoc[0]
|
||||
dy = XYZ[:,1]-srcLoc[1]
|
||||
dz = XYZ[:,2]-srcLoc[2]
|
||||
|
||||
r = np.sqrt( dx**2. + dy**2. + dz**2.)
|
||||
k = np.sqrt( -1j*2.*np.pi*f*mu*sig )
|
||||
kr = k*r
|
||||
|
||||
front = moment / (4.*pi * r**3.) * np.exp(-1j*kr)
|
||||
mid = -kr**2. + 3.*1j*kr + 3.
|
||||
|
||||
if orientation.upper() == 'X':
|
||||
Hx = front*( (dx/r)**2. * mid + (kr**2. - 1j*kr - 1.) )
|
||||
Hy = front*( (dx*dy/r**2.) * mid )
|
||||
Hz = front*( (dx*dz/r**2.) * mid )
|
||||
|
||||
elif orientation.upper() == 'Y':
|
||||
Hx = front*( (dy*dx/r**2.) * mid )
|
||||
Hy = front*( (dy/r)**2. * mid + (kr**2. - 1j*kr - 1.) )
|
||||
Hz = front*( (dy*dz/r**2.) * mid )
|
||||
|
||||
elif orientation.upper() == 'Z':
|
||||
Hx = front*( (dx*dz/r**2.) * mid )
|
||||
Hy = front*( (dy*dz/r**2.) * mid )
|
||||
Hz = front*( (dz/r)**2. * mid + (kr**2. - 1j*kr - 1.) )
|
||||
|
||||
Bx = mu*Hx
|
||||
By = mu*Hy
|
||||
Bz = mu*Hz
|
||||
|
||||
if Bx.ndim is 1:
|
||||
Bx = Utils.mkvc(Bx,2)
|
||||
|
||||
if By.ndim is 1:
|
||||
By = Utils.mkvc(By,2)
|
||||
|
||||
if Bz.ndim is 1:
|
||||
Bz = Utils.mkvc(Bz,2)
|
||||
|
||||
return Bx, By, Bz
|
||||
|
||||
|
||||
def ElectricDipoleWholeSpace(XYZ, srcLoc, sig, f, current=1., length=1., orientation='X', mu=mu_0):
|
||||
XYZ = Utils.asArray_N_x_Dim(XYZ, 3)
|
||||
|
||||
dx = XYZ[:,0]-srcLoc[0]
|
||||
dy = XYZ[:,1]-srcLoc[1]
|
||||
dz = XYZ[:,2]-srcLoc[2]
|
||||
|
||||
r = np.sqrt( dx**2. + dy**2. + dz**2.)
|
||||
k = np.sqrt( -1j*2.*np.pi*f*mu*sig )
|
||||
kr = k*r
|
||||
|
||||
front = current * length / (4. * np.pi * sig * r**3) * np.exp(-1j*k*r)
|
||||
mid = -k**2 * r**2 + 3*1j*k*r + 3
|
||||
|
||||
# Ex = front*((dx**2 / r**2)*mid + (k**2 * r**2 -1j*k*r))
|
||||
# Ey = front*(dx*dy / r**2)*mid
|
||||
# Ez = front*(dx*dz / r**2)*mid
|
||||
|
||||
if orientation.upper() == 'X':
|
||||
Ex = front*((dx**2 / r**2)*mid + (k**2 * r**2 -1j*k*r-1.))
|
||||
Ey = front*(dx*dy / r**2)*mid
|
||||
Ez = front*(dx*dz / r**2)*mid
|
||||
return Ex, Ey, Ez
|
||||
|
||||
elif orientation.upper() == 'Y':
|
||||
# x--> y, y--> z, z-->x
|
||||
Ey = front*((dy**2 / r**2)*mid + (k**2 * r**2 -1j*k*r-1.))
|
||||
Ez = front*(dy*dz / r**2)*mid
|
||||
Ex = front*(dy*dx / r**2)*mid
|
||||
return Ex, Ey, Ez
|
||||
|
||||
elif orientation.upper() == 'Z':
|
||||
# x --> z, y --> x, z --> y
|
||||
Ez = front*((dz**2 / r**2)*mid + (k**2 * r**2 -1j*k*r-1.))
|
||||
Ex = front*(dz*dx / r**2)*mid
|
||||
Ey = front*(dz*dy / r**2)*mid
|
||||
return Ex, Ey, Ez
|
||||
# return Ey, Ez, Ex
|
||||
@@ -0,0 +1,98 @@
|
||||
from SimPEG import Utils, np
|
||||
from scipy.constants import mu_0, epsilon_0
|
||||
from simpegEM.Utils.EMUtils import k
|
||||
|
||||
def getKc(freq,sigma,a,b,mu=mu_0,eps=epsilon_0):
|
||||
a = float(a)
|
||||
b = float(b)
|
||||
# return 1./(2*np.pi) * np.sqrt(b / a) * np.exp(-1j*k(freq,sigma,mu,eps)*(b-a))
|
||||
return np.sqrt(b / a) * np.exp(-1j*k(freq,sigma,mu,eps)*(b-a))
|
||||
|
||||
def _r2(xyz):
|
||||
return np.sum(xyz**2,1)
|
||||
|
||||
def _getCasingHertzMagDipole(srcloc,obsloc,freq,sigma,a,b,mu=mu_0*np.ones(3),eps=epsilon_0,moment=1.):
|
||||
Kc1 = getKc(freq,sigma[1],a,b,mu[1],eps)
|
||||
|
||||
nobs = obsloc.shape[0]
|
||||
dxyz = obsloc - np.c_[np.ones(nobs)]*np.r_[srcloc]
|
||||
|
||||
r2 = _r2(dxyz[:,:2])
|
||||
sqrtr2z2 = np.sqrt(r2 + dxyz[:,2]**2)
|
||||
k2 = k(freq,sigma[2],mu[2],eps)
|
||||
|
||||
return Kc1 * moment / (4.*np.pi) *np.exp(-1j*k2*sqrtr2z2) / sqrtr2z2
|
||||
|
||||
|
||||
def _getCasingHertzMagDipoleDeriv_r(srcloc,obsloc,freq,sigma,a,b,mu=mu_0*np.ones(3),eps=epsilon_0,moment=1.):
|
||||
HertzZ = _getCasingHertzMagDipole(srcloc,obsloc,freq,sigma,a,b,mu,eps,moment)
|
||||
|
||||
nobs = obsloc.shape[0]
|
||||
dxyz = obsloc - np.c_[np.ones(nobs)]*np.r_[srcloc]
|
||||
|
||||
r2 = _r2(dxyz[:,:2])
|
||||
sqrtr2z2 = np.sqrt(r2 + dxyz[:,2]**2)
|
||||
k2 = k(freq,sigma[2],mu[2],eps)
|
||||
|
||||
return -HertzZ * np.sqrt(r2) / sqrtr2z2 * (1j*k2 + 1./ sqrtr2z2)
|
||||
|
||||
|
||||
def _getCasingHertzMagDipoleDeriv_z(srcloc,obsloc,freq,sigma,a,b,mu=mu_0*np.ones(3),eps=epsilon_0,moment=1.):
|
||||
HertzZ = _getCasingHertzMagDipole(srcloc,obsloc,freq,sigma,a,b,mu,eps,moment)
|
||||
|
||||
nobs = obsloc.shape[0]
|
||||
dxyz = obsloc - np.c_[np.ones(nobs)]*np.r_[srcloc]
|
||||
|
||||
r2z2 = _r2(dxyz)
|
||||
sqrtr2z2 = np.sqrt(r2z2)
|
||||
k2 = k(freq,sigma[2],mu[2],eps)
|
||||
|
||||
return -HertzZ*dxyz[:,2] /sqrtr2z2 * (1j*k2 + 1./sqrtr2z2)
|
||||
|
||||
def _getCasingHertzMagDipole2Deriv_z_r(srcloc,obsloc,freq,sigma,a,b,mu=mu_0*np.ones(3),eps=epsilon_0,moment=1.):
|
||||
HertzZ = _getCasingHertzMagDipole(srcloc,obsloc,freq,sigma,a,b,mu,eps,moment)
|
||||
dHertzZdr = _getCasingHertzMagDipoleDeriv_r(srcloc,obsloc,freq,sigma,a,b,mu,eps,moment)
|
||||
|
||||
nobs = obsloc.shape[0]
|
||||
dxyz = obsloc - np.c_[np.ones(nobs)]*np.r_[srcloc]
|
||||
|
||||
r2 = _r2(dxyz[:,:2])
|
||||
r = np.sqrt(r2)
|
||||
z = dxyz[:,2]
|
||||
sqrtr2z2 = np.sqrt(r2 + z**2)
|
||||
k2 = k(freq,sigma[2],mu[2],eps)
|
||||
|
||||
return dHertzZdr*(-z/sqrtr2z2)*(1j*k2+1./sqrtr2z2) + HertzZ*(z*r/sqrtr2z2**3)*(1j*k2 + 2./sqrtr2z2)
|
||||
|
||||
def _getCasingHertzMagDipole2Deriv_z_z(srcloc,obsloc,freq,sigma,a,b,mu=mu_0*np.ones(3),eps=epsilon_0,moment=1.):
|
||||
HertzZ = _getCasingHertzMagDipole(srcloc,obsloc,freq,sigma,a,b,mu,eps,moment)
|
||||
dHertzZdz = _getCasingHertzMagDipoleDeriv_z(srcloc,obsloc,freq,sigma,a,b,mu,eps,moment)
|
||||
|
||||
nobs = obsloc.shape[0]
|
||||
dxyz = obsloc - np.c_[np.ones(nobs)]*np.r_[srcloc]
|
||||
|
||||
r2 = _r2(dxyz[:,:2])
|
||||
r = np.sqrt(r2)
|
||||
z = dxyz[:,2]
|
||||
sqrtr2z2 = np.sqrt(r2 + z**2)
|
||||
k2 = k(freq,sigma[2],mu[2],eps)
|
||||
|
||||
return (dHertzZdz*z + HertzZ)/sqrtr2z2*(-1j*k2 - 1./sqrtr2z2) + HertzZ*z/sqrtr2z2**3*(1j*k2*z + 2.*z/sqrtr2z2)
|
||||
|
||||
def getCasingEphiMagDipole(srcloc,obsloc,freq,sigma,a,b,mu=mu_0*np.ones(3),eps=epsilon_0,moment=1.):
|
||||
return 1j * omega(freq) * mu * _getCasingHertzMagDipoleDeriv_r(srcloc,obsloc,freq,sigma,a,b,mu,eps,moment)
|
||||
|
||||
def getCasingHrMagDipole(srcloc,obsloc,freq,sigma,a,b,mu=mu_0*np.ones(3),eps=epsilon_0,moment=1.):
|
||||
return _getCasingHertzMagDipole2Deriv_z_r(srcloc,obsloc,freq,sigma,a,b,mu,eps,moment)
|
||||
|
||||
def getCasingHzMagDipole(srcloc,obsloc,freq,sigma,a,b,mu=mu_0*np.ones(3),eps=epsilon_0,moment=1.):
|
||||
d2HertzZdz2 = _getCasingHertzMagDipole2Deriv_z_z(srcloc,obsloc,freq,sigma,a,b,mu,eps,moment)
|
||||
k2 = k(freq,sigma[2],mu[2],eps)
|
||||
HertzZ = _getCasingHertzMagDipole(srcloc,obsloc,freq,sigma,a,b,mu,eps,moment)
|
||||
return d2HertzZdz2 + k2**2 * HertzZ
|
||||
|
||||
def getCasingBrMagDipole(srcloc,obsloc,freq,sigma,a,b,mu=mu_0*np.ones(3),eps=epsilon_0,moment=1.):
|
||||
return mu_0 * getCasingHrMagDipole(srcloc,obsloc,freq,sigma,a,b,mu,eps,moment)
|
||||
|
||||
def getCasingBzMagDipole(srcloc,obsloc,freq,sigma,a,b,mu=mu_0*np.ones(3),eps=epsilon_0,moment=1.):
|
||||
return mu_0 * getCasingHzMagDipole(srcloc,obsloc,freq,sigma,a,b,mu,eps,moment)
|
||||
@@ -0,0 +1,12 @@
|
||||
import numpy as np
|
||||
from scipy.constants import mu_0, pi
|
||||
from scipy.special import erf
|
||||
|
||||
def hzAnalyticDipoleT(r, t, sigma):
|
||||
theta = np.sqrt((sigma*mu_0)/(4*t))
|
||||
tr = theta*r
|
||||
etr = erf(tr)
|
||||
t1 = (9/(2*tr**2) - 1)*etr
|
||||
t2 = (1/np.sqrt(pi))*(9/tr + 4*tr)*np.exp(-tr**2)
|
||||
hz = (t1 - t2)/(4*pi*r**3)
|
||||
return hz
|
||||
@@ -0,0 +1,3 @@
|
||||
from TDEM import hzAnalyticDipoleT
|
||||
from FDEM import hzAnalyticDipoleF
|
||||
from FDEMcasing import *
|
||||
@@ -0,0 +1,186 @@
|
||||
from SimPEG import Survey, Problem, Utils, Models, Maps, PropMaps, np, sp, Solver as SimpegSolver
|
||||
from scipy.constants import mu_0
|
||||
|
||||
class EMPropMap(Maps.PropMap):
|
||||
"""
|
||||
Property Map for EM Problems. The electrical conductivity (\\(\\sigma\\)) is the default inversion property, and the default value of the magnetic permeability is that of free space (\\(\\mu = 4\\pi\\times 10^{-7} \\) H/m)
|
||||
"""
|
||||
|
||||
sigma = Maps.Property("Electrical Conductivity", defaultInvProp = True, propertyLink=('rho',Maps.ReciprocalMap))
|
||||
mu = Maps.Property("Inverse Magnetic Permeability", defaultVal = mu_0, propertyLink=('mui',Maps.ReciprocalMap))
|
||||
|
||||
rho = Maps.Property("Electrical Resistivity", propertyLink=('sigma', Maps.ReciprocalMap))
|
||||
mui = Maps.Property("Inverse Magnetic Permeability", defaultVal = 1./mu_0, propertyLink=('mu', Maps.ReciprocalMap))
|
||||
|
||||
|
||||
class BaseEMProblem(Problem.BaseProblem):
|
||||
|
||||
def __init__(self, mesh, **kwargs):
|
||||
Problem.BaseProblem.__init__(self, mesh, **kwargs)
|
||||
|
||||
|
||||
surveyPair = Survey.BaseSurvey
|
||||
dataPair = Survey.Data
|
||||
|
||||
PropMap = EMPropMap
|
||||
|
||||
Solver = SimpegSolver
|
||||
solverOpts = {}
|
||||
|
||||
verbose = False
|
||||
|
||||
####################################################
|
||||
# Make A Symmetric
|
||||
####################################################
|
||||
@property
|
||||
def _makeASymmetric(self):
|
||||
if getattr(self, '__makeASymmetric', None) is None:
|
||||
self.__makeASymmetric = True
|
||||
return self.__makeASymmetric
|
||||
|
||||
|
||||
####################################################
|
||||
# Mass Matrices
|
||||
####################################################
|
||||
|
||||
@property
|
||||
def deleteTheseOnModelUpdate(self):
|
||||
toDelete = []
|
||||
if self.mapping.sigmaMap is not None or self.mapping.rhoMap is not None:
|
||||
toDelete += ['_MeSigma', '_MeSigmaI','_MfRho','_MfRhoI']
|
||||
if self.mapping.muMap is not None or self.mapping.muiMap is not None:
|
||||
toDelete += ['_MeMu', '_MeMuI','_MfMui','_MfMuiI']
|
||||
return toDelete
|
||||
|
||||
@property
|
||||
def Me(self):
|
||||
"""
|
||||
Edge inner product matrix
|
||||
"""
|
||||
if getattr(self, '_Me', None) is None:
|
||||
self._Me = self.mesh.getEdgeInnerProduct()
|
||||
return self._Me
|
||||
|
||||
@property
|
||||
def Mf(self):
|
||||
"""
|
||||
Face inner product matrix
|
||||
"""
|
||||
if getattr(self, '_Mf', None) is None:
|
||||
self._Mf = self.mesh.getFaceInnerProduct()
|
||||
return self._Mf
|
||||
|
||||
|
||||
# ----- Magnetic Permeability ----- #
|
||||
@property
|
||||
def MfMui(self):
|
||||
"""
|
||||
Face inner product matrix for \\(\\mu^{-1}\\). Used in the E-B formulation
|
||||
"""
|
||||
if getattr(self, '_MfMui', None) is None:
|
||||
self._MfMui = self.mesh.getFaceInnerProduct(self.curModel.mui)
|
||||
return self._MfMui
|
||||
|
||||
@property
|
||||
def MfMuiI(self):
|
||||
"""
|
||||
Inverse of :code:`MfMui`.
|
||||
"""
|
||||
if getattr(self, '_MfMuiI', None) is None:
|
||||
self._MfMuiI = self.mesh.getFaceInnerProduct(self.curModel.mui, invMat=True)
|
||||
return self._MfMuiI
|
||||
|
||||
@property
|
||||
def MeMu(self):
|
||||
"""
|
||||
Edge inner product matrix for \\(\\mu\\). Used in the H-J formulation
|
||||
"""
|
||||
if getattr(self, '_MeMu', None) is None:
|
||||
self._MeMu = self.mesh.getEdgeInnerProduct(self.curModel.mu)
|
||||
return self._MeMu
|
||||
|
||||
@property
|
||||
def MeMuI(self):
|
||||
"""
|
||||
Inverse of :code:`MeMu`
|
||||
"""
|
||||
if getattr(self, '_MeMuI', None) is None:
|
||||
self._MeMuI = self.mesh.getEdgeInnerProduct(self.curModel.mu, invMat=True)
|
||||
return self._MeMuI
|
||||
|
||||
|
||||
# ----- Electrical Conductivity ----- #
|
||||
#TODO: hardcoded to sigma as the model
|
||||
@property
|
||||
def MeSigma(self):
|
||||
"""
|
||||
Edge inner product matrix for \\(\\sigma\\). Used in the E-B formulation
|
||||
"""
|
||||
if getattr(self, '_MeSigma', None) is None:
|
||||
self._MeSigma = self.mesh.getEdgeInnerProduct(self.curModel.sigma)
|
||||
return self._MeSigma
|
||||
|
||||
# TODO: This should take a vector
|
||||
def MeSigmaDeriv(self, u):
|
||||
"""
|
||||
Derivative of MeSigma with respect to the model
|
||||
"""
|
||||
return self.mesh.getEdgeInnerProductDeriv(self.curModel.sigma)(u) * self.curModel.sigmaDeriv
|
||||
|
||||
|
||||
@property
|
||||
def MeSigmaI(self):
|
||||
"""
|
||||
Inverse of the edge inner product matrix for \\(\\sigma\\).
|
||||
"""
|
||||
if getattr(self, '_MeSigmaI', None) is None:
|
||||
self._MeSigmaI = self.mesh.getEdgeInnerProduct(self.curModel.sigma, invMat=True)
|
||||
return self._MeSigmaI
|
||||
|
||||
# TODO: This should take a vector
|
||||
def MeSigmaIDeriv(self, u):
|
||||
"""
|
||||
Derivative of :code:`MeSigma` with respect to the model
|
||||
"""
|
||||
# TODO: only works for diagonal tensors. getEdgeInnerProductDeriv, invMat=True should be implemented in SimPEG
|
||||
|
||||
dMeSigmaI_dI = -self.MeSigmaI**2
|
||||
dMe_dsig = self.mesh.getEdgeInnerProductDeriv(self.curModel.sigma)(u)
|
||||
dsig_dm = self.curModel.sigmaDeriv
|
||||
return dMeSigmaI_dI * ( dMe_dsig * ( dsig_dm))
|
||||
# return self.mesh.getEdgeInnerProductDeriv(self.curModel.sigma, invMat=True)(u)
|
||||
|
||||
|
||||
@property
|
||||
def MfRho(self):
|
||||
"""
|
||||
Face inner product matrix for \\(\\rho\\). Used in the H-J formulation
|
||||
"""
|
||||
if getattr(self, '_MfRho', None) is None:
|
||||
self._MfRho = self.mesh.getFaceInnerProduct(self.curModel.rho)
|
||||
return self._MfRho
|
||||
|
||||
# TODO: This should take a vector
|
||||
def MfRhoDeriv(self,u):
|
||||
"""
|
||||
Derivative of :code:`MfRho` with respect to the model.
|
||||
"""
|
||||
return self.mesh.getFaceInnerProductDeriv(self.curModel.rho)(u) * (-Utils.sdiag(self.curModel.rho**2) * self.curModel.sigmaDeriv)
|
||||
# self.curModel.rhoDeriv
|
||||
|
||||
@property
|
||||
def MfRhoI(self):
|
||||
"""
|
||||
Inverse of :code:`MfRho`
|
||||
"""
|
||||
if getattr(self, '_MfRhoI', None) is None:
|
||||
self._MfRhoI = self.mesh.getFaceInnerProduct(self.curModel.rho, invMat=True)
|
||||
return self._MfRhoI
|
||||
|
||||
# TODO: This isn't going to work yet
|
||||
# TODO: This should take a vector
|
||||
def MfRhoIDeriv(self,u):
|
||||
"""
|
||||
Derivative of :code:`MfRhoI` with respect to the model.
|
||||
"""
|
||||
return self.mesh.getFaceInnerProductDeriv(self.curModel.rho, invMat=True)(u) * self.curModel.rhoDeriv
|
||||
@@ -0,0 +1,97 @@
|
||||
from SimPEG import *
|
||||
import SimPEG.EM as EM
|
||||
from scipy.constants import mu_0
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
def run(plotIt=True):
|
||||
|
||||
cs, ncx, ncz, npad = 5., 25, 15, 15
|
||||
hx = [(cs,ncx), (cs,npad,1.3)]
|
||||
hz = [(cs,npad,-1.3), (cs,ncz), (cs,npad,1.3)]
|
||||
mesh = Mesh.CylMesh([hx,1,hz], '00C')
|
||||
|
||||
active = mesh.vectorCCz<0.
|
||||
layer = (mesh.vectorCCz<0.) & (mesh.vectorCCz>=-100.)
|
||||
actMap = Maps.ActiveCells(mesh, active, np.log(1e-8), nC=mesh.nCz)
|
||||
mapping = Maps.ExpMap(mesh) * Maps.Vertical1DMap(mesh) * actMap
|
||||
sig_half = 2e-3
|
||||
sig_air = 1e-8
|
||||
sig_layer = 1e-3
|
||||
sigma = np.ones(mesh.nCz)*sig_air
|
||||
sigma[active] = sig_half
|
||||
sigma[layer] = sig_layer
|
||||
mtrue = np.log(sigma[active])
|
||||
|
||||
|
||||
if plotIt:
|
||||
fig, ax = plt.subplots(1,1, figsize = (3, 6))
|
||||
plt.semilogx(sigma[active], mesh.vectorCCz[active])
|
||||
ax.set_ylim(-600, 0)
|
||||
ax.set_xlim(1e-4, 1e-2)
|
||||
ax.set_xlabel('Conductivity (S/m)', fontsize = 14)
|
||||
ax.set_ylabel('Depth (m)', fontsize = 14)
|
||||
ax.grid(color='k', alpha=0.5, linestyle='dashed', linewidth=0.5)
|
||||
|
||||
|
||||
rxOffset=1e-3
|
||||
rx = EM.TDEM.RxTDEM(np.array([[rxOffset, 0., 30]]), np.logspace(-5,-3, 31), 'bz')
|
||||
src = EM.TDEM.SrcTDEM_VMD_MVP([rx], np.array([0., 0., 80]))
|
||||
survey = EM.TDEM.SurveyTDEM([src])
|
||||
prb = EM.TDEM.ProblemTDEM_b(mesh, mapping=mapping)
|
||||
|
||||
prb.Solver = SolverLU
|
||||
prb.timeSteps = [(1e-06, 20),(1e-05, 20), (0.0001, 20)]
|
||||
prb.pair(survey)
|
||||
dtrue = survey.dpred(mtrue)
|
||||
|
||||
|
||||
survey.dtrue = dtrue
|
||||
std = 0.05
|
||||
noise = std*abs(survey.dtrue)*np.random.randn(*survey.dtrue.shape)
|
||||
survey.dobs = survey.dtrue+noise
|
||||
survey.std = survey.dobs*0 + std
|
||||
survey.Wd = 1/(abs(survey.dobs)*std)
|
||||
|
||||
if plotIt:
|
||||
fig, ax = plt.subplots(1,1, figsize = (10, 6))
|
||||
ax.loglog(rx.times, dtrue, 'b.-')
|
||||
ax.loglog(rx.times, survey.dobs, 'r.-')
|
||||
ax.legend(('Noisefree', '$d^{obs}$'), fontsize = 16)
|
||||
ax.set_xlabel('Time (s)', fontsize = 14)
|
||||
ax.set_ylabel('$B_z$ (T)', fontsize = 16)
|
||||
ax.set_xlabel('Time (s)', fontsize = 14)
|
||||
ax.grid(color='k', alpha=0.5, linestyle='dashed', linewidth=0.5)
|
||||
|
||||
dmisfit = DataMisfit.l2_DataMisfit(survey)
|
||||
regMesh = Mesh.TensorMesh([mesh.hz[mapping.maps[-1].indActive]])
|
||||
reg = Regularization.Tikhonov(regMesh)
|
||||
opt = Optimization.InexactGaussNewton(maxIter = 5)
|
||||
invProb = InvProblem.BaseInvProblem(dmisfit, reg, opt)
|
||||
# Create an inversion object
|
||||
beta = Directives.BetaSchedule(coolingFactor=5, coolingRate=2)
|
||||
betaest = Directives.BetaEstimate_ByEig(beta0_ratio=1e0)
|
||||
inv = Inversion.BaseInversion(invProb, directiveList=[beta,betaest])
|
||||
m0 = np.log(np.ones(mtrue.size)*sig_half)
|
||||
reg.alpha_s = 1e-2
|
||||
reg.alpha_x = 1.
|
||||
prb.counter = opt.counter = Utils.Counter()
|
||||
opt.LSshorten = 0.5
|
||||
opt.remember('xc')
|
||||
|
||||
mopt = inv.run(m0)
|
||||
|
||||
if plotIt:
|
||||
fig, ax = plt.subplots(1,1, figsize = (3, 6))
|
||||
plt.semilogx(sigma[active], mesh.vectorCCz[active])
|
||||
plt.semilogx(np.exp(mopt), mesh.vectorCCz[active])
|
||||
ax.set_ylim(-600, 0)
|
||||
ax.set_xlim(1e-4, 1e-2)
|
||||
ax.set_xlabel('Conductivity (S/m)', fontsize = 14)
|
||||
ax.set_ylabel('Depth (m)', fontsize = 14)
|
||||
ax.grid(color='k', alpha=0.5, linestyle='dashed', linewidth=0.5)
|
||||
plt.legend(['$\sigma_{true}$', '$\sigma_{pred}$'])
|
||||
plt.show()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
||||
@@ -0,0 +1 @@
|
||||
import CylInversion
|
||||
@@ -0,0 +1,661 @@
|
||||
from SimPEG import Survey, Problem, Utils, np, sp, Solver as SimpegSolver
|
||||
from scipy.constants import mu_0
|
||||
from SurveyFDEM import SurveyFDEM
|
||||
from FieldsFDEM import FieldsFDEM, FieldsFDEM_e, FieldsFDEM_b, FieldsFDEM_h, FieldsFDEM_j
|
||||
from simpegEM.Base import BaseEMProblem
|
||||
from simpegEM.Utils.EMUtils import omega
|
||||
|
||||
|
||||
class BaseFDEMProblem(BaseEMProblem):
|
||||
"""
|
||||
We start by looking at Maxwell's equations in the electric
|
||||
field \\\(\\\mathbf{e}\\\) and the magnetic flux
|
||||
density \\\(\\\mathbf{b}\\\)
|
||||
|
||||
.. 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{M^e} \mathbf{s_e}}
|
||||
|
||||
if using the E-B formulation (:code:`ProblemFDEM_e`
|
||||
or :code:`ProblemFDEM_b`) or the magnetic field
|
||||
\\\(\\\mathbf{h}\\\) and current density \\\(\\\mathbf{j}\\\)
|
||||
|
||||
.. 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} \mathbf{h} - \mathbf{j} = \mathbf{s_e}
|
||||
|
||||
if using the H-J formulation (:code:`ProblemFDEM_j` or :code:`ProblemFDEM_h`).
|
||||
|
||||
The problem performs the elimination so that we are solving the system for \\\(\\\mathbf{e},\\\mathbf{b},\\\mathbf{j} \\\) or \\\(\\\mathbf{h}\\\)
|
||||
"""
|
||||
|
||||
surveyPair = SurveyFDEM
|
||||
fieldsPair = FieldsFDEM
|
||||
|
||||
def fields(self, m=None):
|
||||
"""
|
||||
Solve the forward problem for the fields.
|
||||
"""
|
||||
|
||||
self.curModel = m
|
||||
F = self.fieldsPair(self.mesh, self.survey)
|
||||
|
||||
for freq in self.survey.freqs:
|
||||
A = self.getA(freq)
|
||||
rhs = self.getRHS(freq)
|
||||
Ainv = self.Solver(A, **self.solverOpts)
|
||||
sol = Ainv * rhs
|
||||
Srcs = self.survey.getSrcByFreq(freq)
|
||||
ftype = self._fieldType + 'Solution'
|
||||
F[Srcs, ftype] = sol
|
||||
|
||||
return F
|
||||
|
||||
def Jvec(self, m, v, f=None):
|
||||
"""
|
||||
Sensitivity times a vector
|
||||
"""
|
||||
|
||||
if f is None:
|
||||
f = self.fields(m)
|
||||
|
||||
self.curModel = m
|
||||
|
||||
Jv = self.dataPair(self.survey)
|
||||
|
||||
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):
|
||||
ftype = self._fieldType + 'Solution'
|
||||
u_src = f[src, ftype]
|
||||
dA_dm = self.getADeriv_m(freq, u_src, v)
|
||||
dRHS_dm = self.getRHSDeriv_m(src, v)
|
||||
if dRHS_dm is None:
|
||||
du_dm = dA_duI * ( - dA_dm )
|
||||
else:
|
||||
du_dm = dA_duI * ( - dA_dm + dRHS_dm )
|
||||
for rx in src.rxList:
|
||||
# df_duFun = u.deriv_u(rx.fieldsUsed, m)
|
||||
df_duFun = getattr(f, '_%sDeriv_u'%rx.projField, None)
|
||||
df_du = df_duFun(src, du_dm, adjoint=False)
|
||||
if df_du is not None:
|
||||
du_dm = df_du
|
||||
|
||||
df_dmFun = getattr(f, '_%sDeriv_m'%rx.projField, None)
|
||||
df_dm = df_dmFun(src, v, adjoint=False)
|
||||
if df_dm is not None:
|
||||
du_dm += df_dm
|
||||
|
||||
P = lambda v: rx.projectFieldsDeriv(src, self.mesh, f, v) # wrt u, also have wrt m
|
||||
|
||||
|
||||
Jv[src, rx] = P(du_dm)
|
||||
|
||||
return Utils.mkvc(Jv)
|
||||
|
||||
def Jtvec(self, m, v, f=None):
|
||||
"""
|
||||
Sensitivity transpose times a vector
|
||||
"""
|
||||
|
||||
if f is None:
|
||||
f = 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 = f[src, ftype]
|
||||
|
||||
for rx in src.rxList:
|
||||
PTv = rx.projectFieldsDeriv(src, self.mesh, f, v[src, rx], adjoint=True) # wrt u, need possibility wrt m
|
||||
|
||||
df_duTFun = getattr(f, '_%sDeriv_u'%rx.projField, None)
|
||||
df_duT = df_duTFun(src, PTv, adjoint=True)
|
||||
if df_duT is not None:
|
||||
dA_duIT = ATinv * df_duT
|
||||
else:
|
||||
dA_duIT = ATinv * PTv
|
||||
|
||||
dA_dmT = self.getADeriv_m(freq, u_src, dA_duIT, adjoint=True)
|
||||
|
||||
dRHS_dmT = self.getRHSDeriv_m(src, dA_duIT, adjoint=True)
|
||||
|
||||
if dRHS_dmT is None:
|
||||
du_dmT = - dA_dmT
|
||||
else:
|
||||
du_dmT = -dA_dmT + dRHS_dmT
|
||||
|
||||
df_dmFun = getattr(f, '_%sDeriv_m'%rx.projField, None)
|
||||
dfT_dm = df_dmFun(src, PTv, adjoint=True)
|
||||
if dfT_dm is not None:
|
||||
du_dmT += dfT_dm
|
||||
|
||||
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')
|
||||
|
||||
return Jtv
|
||||
|
||||
def getSourceTerm(self, freq):
|
||||
"""
|
||||
Evaluates the sources for a given frequency and puts them in matrix form
|
||||
|
||||
:param float freq: Frequency
|
||||
:rtype: numpy.ndarray (nE or nF, nSrc)
|
||||
:return: S_m, S_e
|
||||
"""
|
||||
Srcs = self.survey.getSrcByFreq(freq)
|
||||
if self._eqLocs is 'FE':
|
||||
S_m = np.zeros((self.mesh.nF,len(Srcs)), dtype=complex)
|
||||
S_e = np.zeros((self.mesh.nE,len(Srcs)), dtype=complex)
|
||||
elif self._eqLocs is 'EF':
|
||||
S_m = np.zeros((self.mesh.nE,len(Srcs)), dtype=complex)
|
||||
S_e = np.zeros((self.mesh.nF,len(Srcs)), dtype=complex)
|
||||
|
||||
for i, src in enumerate(Srcs):
|
||||
smi, sei = src.eval(self)
|
||||
if smi is not None:
|
||||
S_m[:,i] = Utils.mkvc(smi)
|
||||
if sei is not None:
|
||||
S_e[:,i] = Utils.mkvc(sei)
|
||||
|
||||
return S_m, S_e
|
||||
|
||||
|
||||
##########################################################################################
|
||||
################################ E-B Formulation #########################################
|
||||
##########################################################################################
|
||||
|
||||
class ProblemFDEM_e(BaseFDEMProblem):
|
||||
"""
|
||||
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 ::
|
||||
|
||||
\\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}
|
||||
|
||||
which we solve for \\\(\\\mathbf{e}\\\).
|
||||
"""
|
||||
|
||||
_fieldType = 'e'
|
||||
_eqLocs = 'FE'
|
||||
fieldsPair = FieldsFDEM_e
|
||||
|
||||
def __init__(self, mesh, **kwargs):
|
||||
BaseFDEMProblem.__init__(self, mesh, **kwargs)
|
||||
|
||||
def getA(self, freq):
|
||||
"""
|
||||
.. math ::
|
||||
\mathbf{A} = \mathbf{C}^T \mathbf{M_{\mu^{-1}}^f} \mathbf{C} + i \omega \mathbf{M^e_{\sigma}}
|
||||
|
||||
:param float freq: Frequency
|
||||
:rtype: scipy.sparse.csr_matrix
|
||||
:return: A
|
||||
"""
|
||||
MfMui = self.MfMui
|
||||
MeSigma = self.MeSigma
|
||||
C = self.mesh.edgeCurl
|
||||
|
||||
return C.T*MfMui*C + 1j*omega(freq)*MeSigma
|
||||
|
||||
|
||||
def getADeriv_m(self, freq, u, v, adjoint=False):
|
||||
dsig_dm = self.curModel.sigmaDeriv
|
||||
dMe_dsig = self.MeSigmaDeriv(u)
|
||||
|
||||
if adjoint:
|
||||
return 1j * omega(freq) * ( dMe_dsig.T * v )
|
||||
|
||||
return 1j * omega(freq) * ( dMe_dsig * v )
|
||||
|
||||
def getRHS(self, freq):
|
||||
"""
|
||||
.. math ::
|
||||
\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
|
||||
:rtype: numpy.ndarray (nE, nSrc)
|
||||
:return: RHS
|
||||
"""
|
||||
|
||||
S_m, S_e = self.getSourceTerm(freq)
|
||||
C = self.mesh.edgeCurl
|
||||
MfMui = self.MfMui
|
||||
|
||||
# RHS = C.T * (MfMui * S_m) -1j * omega(freq) * Me * S_e
|
||||
RHS = C.T * (MfMui * S_m) -1j * omega(freq) * S_e
|
||||
|
||||
return RHS
|
||||
|
||||
def getRHSDeriv_m(self, src, v, adjoint=False):
|
||||
C = self.mesh.edgeCurl
|
||||
MfMui = self.MfMui
|
||||
S_mDeriv, S_eDeriv = src.evalDeriv(self, adjoint)
|
||||
|
||||
if adjoint:
|
||||
dRHS = MfMui * (C * v)
|
||||
S_mDerivv = S_mDeriv(dRHS)
|
||||
S_eDerivv = S_eDeriv(v)
|
||||
if S_mDerivv is not None and S_eDerivv is not None:
|
||||
return S_mDerivv - 1j * omega(freq) * S_eDerivv
|
||||
elif S_mDerivv is not None:
|
||||
return S_mDerivv
|
||||
elif S_eDerivv is not None:
|
||||
return - 1j * omega(freq) * S_eDerivv
|
||||
else:
|
||||
return None
|
||||
else:
|
||||
S_mDerivv, S_eDerivv = S_mDeriv(v), S_eDeriv(v)
|
||||
|
||||
if S_mDerivv is not None and S_eDerivv is not None:
|
||||
return C.T * (MfMui * S_mDerivv) -1j * omega(freq) * S_eDerivv
|
||||
elif S_mDerivv is not None:
|
||||
return C.T * (MfMui * S_mDerivv)
|
||||
elif S_eDerivv is not None:
|
||||
return -1j * omega(freq) * S_eDerivv
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
class ProblemFDEM_b(BaseFDEMProblem):
|
||||
"""
|
||||
We eliminate \\\(\\\mathbf{e}\\\) using
|
||||
|
||||
.. math ::
|
||||
|
||||
\mathbf{e} = \mathbf{M^e_{\sigma}}^{-1} \\left(\mathbf{C}^T \mathbf{M_{\mu^{-1}}^f} \mathbf{b} - \mathbf{s_e}\\right)
|
||||
|
||||
and solve for \\\(\\\mathbf{b}\\\) using:
|
||||
|
||||
.. 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}
|
||||
|
||||
.. note ::
|
||||
The inverse problem will not work with full anisotropy
|
||||
"""
|
||||
|
||||
_fieldType = 'b'
|
||||
_eqLocs = 'FE'
|
||||
fieldsPair = FieldsFDEM_b
|
||||
|
||||
def __init__(self, mesh, **kwargs):
|
||||
BaseFDEMProblem.__init__(self, mesh, **kwargs)
|
||||
|
||||
def getA(self, freq):
|
||||
"""
|
||||
.. math ::
|
||||
\mathbf{A} = \mathbf{C} \mathbf{M^e_{\sigma}}^{-1} \mathbf{C}^T \mathbf{M_{\mu^{-1}}^f} + i \omega
|
||||
|
||||
:param float freq: Frequency
|
||||
:rtype: scipy.sparse.csr_matrix
|
||||
:return: A
|
||||
"""
|
||||
|
||||
MfMui = self.MfMui
|
||||
MeSigmaI = self.MeSigmaI
|
||||
C = self.mesh.edgeCurl
|
||||
iomega = 1j * omega(freq) * sp.eye(self.mesh.nF)
|
||||
|
||||
A = C * (MeSigmaI * (C.T * MfMui)) + iomega
|
||||
|
||||
if self._makeASymmetric is True:
|
||||
return MfMui.T*A
|
||||
return A
|
||||
|
||||
def getADeriv_m(self, freq, u, v, adjoint=False):
|
||||
|
||||
MfMui = self.MfMui
|
||||
C = self.mesh.edgeCurl
|
||||
MeSigmaIDeriv = self.MeSigmaIDeriv
|
||||
vec = C.T * (MfMui * u)
|
||||
|
||||
MeSigmaIDeriv = MeSigmaIDeriv(vec)
|
||||
|
||||
if adjoint:
|
||||
if self._makeASymmetric is True:
|
||||
v = MfMui * v
|
||||
return MeSigmaIDeriv.T * (C.T * v)
|
||||
|
||||
if self._makeASymmetric is True:
|
||||
return MfMui.T * ( C * ( MeSigmaIDeriv * v ) )
|
||||
return C * ( MeSigmaIDeriv * v )
|
||||
|
||||
|
||||
def getRHS(self, freq):
|
||||
"""
|
||||
.. math ::
|
||||
\mathbf{RHS} = \mathbf{s_m} + \mathbf{M^e_{\sigma}}^{-1}\mathbf{s_e}
|
||||
|
||||
:param float freq: Frequency
|
||||
:rtype: numpy.ndarray (nE, nSrc)
|
||||
:return: RHS
|
||||
"""
|
||||
|
||||
S_m, S_e = self.getSourceTerm(freq)
|
||||
C = self.mesh.edgeCurl
|
||||
MeSigmaI = self.MeSigmaI
|
||||
# Me = self.Me
|
||||
|
||||
RHS = S_m + C * ( MeSigmaI * S_e )
|
||||
|
||||
if self._makeASymmetric is True:
|
||||
MfMui = self.MfMui
|
||||
return MfMui.T * RHS
|
||||
|
||||
return RHS
|
||||
|
||||
def getRHSDeriv_m(self, src, v, adjoint=False):
|
||||
C = self.mesh.edgeCurl
|
||||
S_m, S_e = src.eval(self)
|
||||
MfMui = self.MfMui
|
||||
# Me = self.Me
|
||||
|
||||
if self._makeASymmetric and adjoint:
|
||||
v = self.MfMui * v
|
||||
|
||||
if S_e is not None:
|
||||
MeSigmaIDeriv = self.MeSigmaIDeriv(S_e)
|
||||
if not adjoint:
|
||||
RHSderiv = C * (MeSigmaIDeriv * v)
|
||||
elif adjoint:
|
||||
RHSderiv = MeSigmaIDeriv.T * (C.T * v)
|
||||
else:
|
||||
RHSderiv = None
|
||||
|
||||
S_mDeriv, S_eDeriv = src.evalDeriv(self, adjoint)
|
||||
S_mDeriv, S_eDeriv = S_mDeriv(v), S_eDeriv(v)
|
||||
if S_mDeriv is not None and S_eDeriv is not None:
|
||||
if not adjoint:
|
||||
SrcDeriv = S_mDeriv + C * (self.MeSigmaI * S_eDeriv)
|
||||
elif adjoint:
|
||||
SrcDeriv = S_mDeriv + Self.MeSigmaI.T * ( C.T * S_eDeriv)
|
||||
elif S_mDeriv is not None:
|
||||
SrcDeriv = S_mDeriv
|
||||
elif S_eDeriv is not None:
|
||||
if not adjoint:
|
||||
SrcDeriv = C * (self.MeSigmaI * S_eDeriv)
|
||||
elif adjoint:
|
||||
SrcDeriv = self.MeSigmaI.T * ( C.T * S_eDeriv)
|
||||
else:
|
||||
SrcDeriv = None
|
||||
|
||||
if RHSderiv is not None and SrcDeriv is not None:
|
||||
RHSderiv += SrcDeriv
|
||||
elif SrcDeriv is not None:
|
||||
RHSderiv = SrcDeriv
|
||||
|
||||
if RHSderiv is not None:
|
||||
if self._makeASymmetric is True and not adjoint:
|
||||
return MfMui.T * RHSderiv
|
||||
|
||||
return RHSderiv
|
||||
|
||||
|
||||
|
||||
##########################################################################################
|
||||
################################ H-J Formulation #########################################
|
||||
##########################################################################################
|
||||
|
||||
|
||||
class ProblemFDEM_j(BaseFDEMProblem):
|
||||
"""
|
||||
We eliminate \\\(\\\mathbf{h}\\\) using
|
||||
|
||||
.. 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)
|
||||
|
||||
and solve for \\\(\\\mathbf{j}\\\) using
|
||||
|
||||
.. 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}
|
||||
|
||||
.. note::
|
||||
This implementation does not yet work with full anisotropy!!
|
||||
|
||||
"""
|
||||
|
||||
_fieldType = 'j'
|
||||
_eqLocs = 'EF'
|
||||
fieldsPair = FieldsFDEM_j
|
||||
|
||||
def __init__(self, mesh, **kwargs):
|
||||
BaseFDEMProblem.__init__(self, mesh, **kwargs)
|
||||
|
||||
def getA(self, freq):
|
||||
"""
|
||||
.. math ::
|
||||
\\mathbf{A} = \\mathbf{C} \\mathbf{M^e_{mu^{-1}}} \\mathbf{C}^T \\mathbf{M^f_{\\sigma^{-1}}} + i\\omega
|
||||
|
||||
:param float freq: Frequency
|
||||
:rtype: scipy.sparse.csr_matrix
|
||||
:return: A
|
||||
"""
|
||||
|
||||
MeMuI = self.MeMuI
|
||||
MfRho = self.MfRho
|
||||
C = self.mesh.edgeCurl
|
||||
iomega = 1j * omega(freq) * sp.eye(self.mesh.nF)
|
||||
|
||||
A = C * MeMuI * C.T * MfRho + iomega
|
||||
|
||||
if self._makeASymmetric is True:
|
||||
return MfRho.T*A
|
||||
return A
|
||||
|
||||
|
||||
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
|
||||
|
||||
.. math ::
|
||||
|
||||
\\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}}
|
||||
&= \\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}}
|
||||
"""
|
||||
|
||||
MeMuI = self.MeMuI
|
||||
MfRho = self.MfRho
|
||||
C = self.mesh.edgeCurl
|
||||
MfRhoDeriv_m = self.MfRhoDeriv(u)
|
||||
|
||||
if adjoint:
|
||||
if self._makeASymmetric is True:
|
||||
v = MfRho * v
|
||||
return MfRhoDeriv_m.T * (C * (MeMuI.T * (C.T * v)))
|
||||
|
||||
if self._makeASymmetric is True:
|
||||
return MfRho.T * (C * ( MeMuI * (C.T * (MfRhoDeriv_m * v) )))
|
||||
return C * (MeMuI * (C.T * (MfRhoDeriv_m * v)))
|
||||
|
||||
|
||||
def getRHS(self, freq):
|
||||
"""
|
||||
.. math ::
|
||||
|
||||
\mathbf{RHS} = \mathbf{C} \mathbf{M_{\mu}^e}^{-1}\mathbf{s_m} -i\omega \mathbf{s_e}
|
||||
:param float freq: Frequency
|
||||
:rtype: numpy.ndarray (nE, nSrc)
|
||||
:return: RHS
|
||||
"""
|
||||
|
||||
S_m, S_e = self.getSourceTerm(freq)
|
||||
C = self.mesh.edgeCurl
|
||||
MeMuI = self.MeMuI
|
||||
|
||||
RHS = C * (MeMuI * S_m) - 1j * omega(freq) * S_e
|
||||
if self._makeASymmetric is True:
|
||||
MfRho = self.MfRho
|
||||
return MfRho.T*RHS
|
||||
|
||||
return RHS
|
||||
|
||||
def getRHSDeriv_m(self, src, v, adjoint=False):
|
||||
C = self.mesh.edgeCurl
|
||||
MeMuI = self.MeMuI
|
||||
S_mDeriv, S_eDeriv = src.evalDeriv(self, adjoint)
|
||||
|
||||
if adjoint:
|
||||
if self._makeASymmetric:
|
||||
MfRho = self.MfRho
|
||||
v = MfRho*v
|
||||
S_mDerivv = S_mDeriv(MeMuI.T * (C.T * v))
|
||||
S_eDerivv = S_eDeriv(v)
|
||||
if S_mDerivv is not None and S_eDerivv is not None:
|
||||
return S_mDerivv - 1j * omega(freq) * S_eDerivv
|
||||
elif S_mDerivv is not None:
|
||||
return S_mDerivv
|
||||
elif S_eDerivv is not None:
|
||||
return - 1j * omega(freq) * S_eDerivv
|
||||
else:
|
||||
return None
|
||||
else:
|
||||
S_mDerivv, S_eDerivv = S_mDeriv(v), S_eDeriv(v)
|
||||
|
||||
if S_mDerivv is not None and S_eDerivv is not None:
|
||||
RHSDeriv = C * (MeMuI * S_mDerivv) - 1j * omega(freq) * S_eDerivv
|
||||
elif S_mDerivv is not None:
|
||||
RHSDeriv = C * (MeMuI * S_mDerivv)
|
||||
elif S_eDerivv is not None:
|
||||
RHSDeriv = - 1j * omega(freq) * S_eDerivv
|
||||
else:
|
||||
return None
|
||||
|
||||
if self._makeASymmetric:
|
||||
MfRho = self.MfRho
|
||||
return MfRho.T * RHSDeriv
|
||||
return RHSDeriv
|
||||
|
||||
|
||||
|
||||
|
||||
class ProblemFDEM_h(BaseFDEMProblem):
|
||||
"""
|
||||
We eliminate \\\(\\\mathbf{j}\\\) using
|
||||
|
||||
.. math ::
|
||||
|
||||
\mathbf{j} = \mathbf{C} \mathbf{h} - \mathbf{s_e}
|
||||
|
||||
and solve for \\\(\\\mathbf{h}\\\) using
|
||||
|
||||
.. 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}
|
||||
|
||||
"""
|
||||
|
||||
_fieldType = 'h'
|
||||
_eqLocs = 'EF'
|
||||
fieldsPair = FieldsFDEM_h
|
||||
|
||||
def __init__(self, mesh, **kwargs):
|
||||
BaseFDEMProblem.__init__(self, mesh, **kwargs)
|
||||
|
||||
def getA(self, freq):
|
||||
"""
|
||||
.. math ::
|
||||
|
||||
\mathbf{A} = \mathbf{C}^T \mathbf{M_{\\rho}^f} \mathbf{C} + i \omega \mathbf{M_{\mu}^e}
|
||||
|
||||
:param float freq: Frequency
|
||||
:rtype: scipy.sparse.csr_matrix
|
||||
:return: A
|
||||
"""
|
||||
|
||||
MeMu = self.MeMu
|
||||
MfRho = self.MfRho
|
||||
C = self.mesh.edgeCurl
|
||||
|
||||
return C.T * (MfRho * C) + 1j*omega(freq)*MeMu
|
||||
|
||||
def getADeriv_m(self, freq, u, v, adjoint=False):
|
||||
|
||||
MeMu = self.MeMu
|
||||
C = self.mesh.edgeCurl
|
||||
MfRhoDeriv_m = self.MfRhoDeriv(C*u)
|
||||
|
||||
if adjoint:
|
||||
return MfRhoDeriv_m.T * (C * v)
|
||||
return C.T * (MfRhoDeriv_m * v)
|
||||
|
||||
def getRHS(self, freq):
|
||||
"""
|
||||
.. math ::
|
||||
|
||||
\mathbf{RHS} = \mathbf{M^e} \mathbf{s_m} + \mathbf{C}^T \mathbf{M_{\\rho}^f} \mathbf{s_e}
|
||||
|
||||
:param float freq: Frequency
|
||||
:rtype: numpy.ndarray (nE, nSrc)
|
||||
:return: RHS
|
||||
"""
|
||||
|
||||
S_m, S_e = self.getSourceTerm(freq)
|
||||
C = self.mesh.edgeCurl
|
||||
MfRho = self.MfRho
|
||||
|
||||
RHS = S_m + C.T * ( MfRho * S_e )
|
||||
|
||||
return RHS
|
||||
|
||||
def getRHSDeriv_m(self, src, v, adjoint=False):
|
||||
_, S_e = src.eval(self)
|
||||
C = self.mesh.edgeCurl
|
||||
MfRho = self.MfRho
|
||||
|
||||
RHSDeriv = None
|
||||
|
||||
if S_e is not None:
|
||||
MfRhoDeriv = self.MfRhoDeriv(S_e)
|
||||
if not adjoint:
|
||||
RHSDeriv = C.T * (MfRhoDeriv * v)
|
||||
elif adjoint:
|
||||
RHSDeriv = MfRhoDeriv.T * (C * v)
|
||||
|
||||
S_mDeriv, S_eDeriv = src.evalDeriv(self, adjoint)
|
||||
|
||||
S_mDeriv = S_mDeriv(v)
|
||||
S_eDeriv = S_eDeriv(v)
|
||||
|
||||
if S_mDeriv is not None:
|
||||
if RHSDeriv is not None:
|
||||
RHSDeriv += S_mDeriv(v)
|
||||
else:
|
||||
RHSDeriv = S_mDeriv(v)
|
||||
if S_eDeriv is not None:
|
||||
if RHSDeriv is not None:
|
||||
RHSDeriv += C.T * (MfRho * S_e)
|
||||
else:
|
||||
RHSDeriv = C.T * (MfRho * S_e)
|
||||
|
||||
return RHSDeriv
|
||||
|
||||
@@ -0,0 +1,374 @@
|
||||
from SimPEG import Survey, Problem, Utils, np, sp
|
||||
from simpegEM.Utils.EMUtils import omega
|
||||
|
||||
|
||||
class FieldsFDEM(Problem.Fields):
|
||||
"""Fancy Field Storage for a FDEM survey."""
|
||||
knownFields = {}
|
||||
dtype = complex
|
||||
|
||||
class FieldsFDEM_e(FieldsFDEM):
|
||||
knownFields = {'eSolution':'E'}
|
||||
aliasFields = {
|
||||
'e' : ['eSolution','E','_e'],
|
||||
'ePrimary' : ['eSolution','E','_ePrimary'],
|
||||
'eSecondary' : ['eSolution','E','_eSecondary'],
|
||||
'b' : ['eSolution','F','_b'],
|
||||
'bPrimary' : ['eSolution','F','_bPrimary'],
|
||||
'bSecondary' : ['eSolution','F','_bSecondary']
|
||||
}
|
||||
|
||||
def __init__(self,mesh,survey,**kwargs):
|
||||
FieldsFDEM.__init__(self,mesh,survey,**kwargs)
|
||||
|
||||
def startup(self):
|
||||
self.prob = self.survey.prob
|
||||
self._edgeCurl = self.survey.prob.mesh.edgeCurl
|
||||
|
||||
def _ePrimary(self, eSolution, srcList):
|
||||
ePrimary = np.zeros_like(eSolution)
|
||||
for i, src in enumerate(srcList):
|
||||
ep = src.ePrimary(self.prob)
|
||||
if ep is not None:
|
||||
ePrimary[:,i] = ep
|
||||
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 None
|
||||
|
||||
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._edgeCurl.shape[0],eSolution.shape[1]],dtype = complex)
|
||||
for i, src in enumerate(srcList):
|
||||
bp = src.bPrimary(self.prob)
|
||||
if bp is not None:
|
||||
bPrimary[:,i] += bp
|
||||
return bPrimary
|
||||
|
||||
def _bSecondary(self, eSolution, srcList):
|
||||
C = self._edgeCurl
|
||||
b = (C * eSolution)
|
||||
for i, src in enumerate(srcList):
|
||||
b[:,i] *= - 1./(1j*omega(src.freq))
|
||||
S_m, _ = src.eval(self.prob)
|
||||
if S_m is not None:
|
||||
b[:,i] += 1./(1j*omega(src.freq)) * S_m
|
||||
return b
|
||||
|
||||
def _bSecondaryDeriv_u(self, src, v, adjoint = False):
|
||||
C = self._edgeCurl
|
||||
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):
|
||||
S_mDeriv, _ = src.evalDeriv(self.prob, adjoint)
|
||||
S_mDeriv = S_mDeriv(v)
|
||||
if S_mDeriv is not None:
|
||||
return 1./(1j * omega(src.freq)) * S_mDeriv
|
||||
return None
|
||||
|
||||
def _b(self, eSolution, srcList):
|
||||
return self._bPrimary(eSolution, srcList) + self._bSecondary(eSolution, srcList)
|
||||
|
||||
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)
|
||||
|
||||
|
||||
class FieldsFDEM_b(FieldsFDEM):
|
||||
knownFields = {'bSolution':'F'}
|
||||
aliasFields = {
|
||||
'b' : ['bSolution','F','_b'],
|
||||
'bPrimary' : ['bSolution','F','_bPrimary'],
|
||||
'bSecondary' : ['bSolution','F','_bSecondary'],
|
||||
'e' : ['bSolution','E','_e'],
|
||||
'ePrimary' : ['bSolution','E','_ePrimary'],
|
||||
'eSecondary' : ['bSolution','E','_eSecondary'],
|
||||
}
|
||||
|
||||
def __init__(self,mesh,survey,**kwargs):
|
||||
FieldsFDEM.__init__(self,mesh,survey,**kwargs)
|
||||
|
||||
def startup(self):
|
||||
self.prob = self.survey.prob
|
||||
self._edgeCurl = self.survey.prob.mesh.edgeCurl
|
||||
self._MeSigmaI = self.survey.prob.MeSigmaI
|
||||
self._MfMui = self.survey.prob.MfMui
|
||||
self._MeSigmaIDeriv = self.survey.prob.MeSigmaIDeriv
|
||||
self._Me = self.survey.prob.Me
|
||||
|
||||
def _bPrimary(self, bSolution, srcList):
|
||||
bPrimary = np.zeros_like(bSolution)
|
||||
for i, src in enumerate(srcList):
|
||||
bp = src.bPrimary(self.prob)
|
||||
if bp is not None:
|
||||
bPrimary[:,i] = bp
|
||||
return bPrimary
|
||||
|
||||
def _bSecondary(self, bSolution, srcList):
|
||||
return bSolution
|
||||
|
||||
def _b(self, bSolution, srcList):
|
||||
return self._bPrimary(bSolution, srcList) + self._bSecondary(bSolution, srcList)
|
||||
|
||||
def _bDeriv_u(self, src, v, adjoint=False):
|
||||
return None
|
||||
|
||||
def _bDeriv_m(self, src, v, adjoint=False):
|
||||
# assuming primary does not depend on the model
|
||||
return None
|
||||
|
||||
def _ePrimary(self, bSolution, srcList):
|
||||
ePrimary = np.zeros([self._edgeCurl.shape[1],bSolution.shape[1]],dtype = complex)
|
||||
for i,src in enumerate(srcList):
|
||||
ep = src.ePrimary(self.prob)
|
||||
if ep is not None:
|
||||
ePrimary[:,i] = ep
|
||||
return ePrimary
|
||||
|
||||
def _eSecondary(self, bSolution, srcList):
|
||||
e = self._MeSigmaI * ( self._edgeCurl.T * ( self._MfMui * bSolution))
|
||||
for i,src in enumerate(srcList):
|
||||
_,S_e = src.eval(self.prob)
|
||||
if S_e is not None:
|
||||
e[:,i] += -self._MeSigmaI * S_e
|
||||
return e
|
||||
|
||||
def _eSecondaryDeriv_u(self, src, v, adjoint=False):
|
||||
if not adjoint:
|
||||
return self._MeSigmaI * ( self._edgeCurl.T * ( self._MfMui * v) )
|
||||
else:
|
||||
return self._MfMui.T * (self._edgeCurl * (self._MeSigmaI.T * v))
|
||||
|
||||
def _eSecondaryDeriv_m(self, src, v, adjoint=False):
|
||||
bSolution = self[[src],'bSolution']
|
||||
_,S_e = src.eval(self.prob)
|
||||
Me = self._Me
|
||||
|
||||
if adjoint:
|
||||
Me = Me.T
|
||||
|
||||
w = self._edgeCurl.T * (self._MfMui * bSolution)
|
||||
if S_e is not None:
|
||||
w += -Utils.mkvc(Me * S_e,2)
|
||||
|
||||
if not adjoint:
|
||||
de_dm = self._MeSigmaIDeriv(w) * v
|
||||
elif adjoint:
|
||||
de_dm = self._MeSigmaIDeriv(w).T * v
|
||||
|
||||
_, S_eDeriv = src.evalDeriv(self.prob, adjoint)
|
||||
Se_Deriv = S_eDeriv(v)
|
||||
|
||||
if Se_Deriv is not None:
|
||||
de_dm += -self._MeSigmaI * Se_Deriv
|
||||
|
||||
return de_dm
|
||||
|
||||
def _e(self, bSolution, srcList):
|
||||
return self._ePrimary(bSolution, srcList) + self._eSecondary(bSolution, srcList)
|
||||
|
||||
def _eDeriv_u(self, src, v, adjoint=False):
|
||||
return self._eSecondaryDeriv_u(src, v, adjoint)
|
||||
|
||||
def _eDeriv_m(self, src, v, adjoint=False):
|
||||
# assuming primary doesn't depend on model
|
||||
return self._eSecondaryDeriv_m(src, v, adjoint)
|
||||
|
||||
|
||||
class FieldsFDEM_j(FieldsFDEM):
|
||||
knownFields = {'jSolution':'F'}
|
||||
aliasFields = {
|
||||
'j' : ['jSolution','F','_j'],
|
||||
'jPrimary' : ['jSolution','F','_jPrimary'],
|
||||
'jSecondary' : ['jSolution','F','_jSecondary'],
|
||||
'h' : ['jSolution','E','_h'],
|
||||
'hPrimary' : ['jSolution','E','_hPrimary'],
|
||||
'hSecondary' : ['jSolution','E','_hSecondary'],
|
||||
}
|
||||
|
||||
def __init__(self,mesh,survey,**kwargs):
|
||||
FieldsFDEM.__init__(self,mesh,survey,**kwargs)
|
||||
|
||||
def startup(self):
|
||||
self.prob = self.survey.prob
|
||||
self._edgeCurl = self.survey.prob.mesh.edgeCurl
|
||||
self._MeMuI = self.survey.prob.MeMuI
|
||||
self._MfRho = self.survey.prob.MfRho
|
||||
self._MfRhoDeriv = self.survey.prob.MfRhoDeriv
|
||||
self._Me = self.survey.prob.Me
|
||||
|
||||
def _jPrimary(self, jSolution, srcList):
|
||||
jPrimary = np.zeros_like(jSolution,dtype = complex)
|
||||
for i, src in enumerate(srcList):
|
||||
jp = src.jPrimary(self.prob)
|
||||
if jp is not None:
|
||||
jPrimary[:,i] += jp
|
||||
return jPrimary
|
||||
|
||||
def _jSecondary(self, jSolution, srcList):
|
||||
return jSolution
|
||||
|
||||
def _j(self, jSolution, srcList):
|
||||
return self._jPrimary(jSolution, srcList) + self._jSecondary(jSolution, srcList)
|
||||
|
||||
def _jDeriv_u(self, src, v, adjoint=False):
|
||||
return None
|
||||
|
||||
def _jDeriv_m(self, src, v, adjoint=False):
|
||||
# assuming primary does not depend on the model
|
||||
return None
|
||||
|
||||
def _hPrimary(self, jSolution, srcList):
|
||||
hPrimary = np.zeros([self._edgeCurl.shape[1],jSolution.shape[1]],dtype = complex)
|
||||
for i, src in enumerate(srcList):
|
||||
hp = src.hPrimary(self.prob)
|
||||
if hp is not None:
|
||||
hPrimary[:,i] = hp
|
||||
return hPrimary
|
||||
|
||||
def _hSecondary(self, jSolution, srcList):
|
||||
h = self._MeMuI * (self._edgeCurl.T * (self._MfRho * jSolution) )
|
||||
for i, src in enumerate(srcList):
|
||||
h[:,i] *= -1./(1j*omega(src.freq))
|
||||
S_m,_ = src.eval(self.prob)
|
||||
if S_m is not None:
|
||||
h[:,i] += 1./(1j*omega(src.freq)) * self._MeMuI * (S_m)
|
||||
return h
|
||||
|
||||
def _hSecondaryDeriv_u(self, src, v, adjoint=False):
|
||||
if not adjoint:
|
||||
return -1./(1j*omega(src.freq)) * self._MeMuI * (self._edgeCurl.T * (self._MfRho * v) )
|
||||
elif adjoint:
|
||||
return -1./(1j*omega(src.freq)) * self._MfRho.T * (self._edgeCurl * ( self._MeMuI.T * v))
|
||||
|
||||
def _hSecondaryDeriv_m(self, src, v, adjoint=False):
|
||||
jSolution = self[[src],'jSolution']
|
||||
MeMuI = self._MeMuI
|
||||
C = self._edgeCurl
|
||||
MfRho = self._MfRho
|
||||
MfRhoDeriv = self._MfRhoDeriv
|
||||
Me = self._Me
|
||||
|
||||
if not adjoint:
|
||||
hDeriv_m = -1./(1j*omega(src.freq)) * MeMuI * (C.T * (MfRhoDeriv(jSolution)*v ) )
|
||||
elif adjoint:
|
||||
hDeriv_m = -1./(1j*omega(src.freq)) * MfRhoDeriv(jSolution).T * ( C * (MeMuI.T * v ) )
|
||||
|
||||
S_mDeriv,_ = src.evalDeriv(self.prob, adjoint)
|
||||
|
||||
if not adjoint:
|
||||
S_mDeriv = S_mDeriv(v)
|
||||
if S_mDeriv is not None:
|
||||
hDeriv_m += 1./(1j*omega(src.freq)) * MeMuI * (Me * S_mDeriv)
|
||||
elif adjoint:
|
||||
S_mDeriv = S_mDeriv(Me.T * (MeMuI.T * v))
|
||||
if S_mDeriv is not None:
|
||||
hDeriv_m += 1./(1j*omega(src.freq)) * S_mDeriv
|
||||
return hDeriv_m
|
||||
|
||||
|
||||
def _h(self, jSolution, srcList):
|
||||
return self._hPrimary(jSolution, srcList) + self._hSecondary(jSolution, srcList)
|
||||
|
||||
def _hDeriv_u(self, src, v, adjoint=False):
|
||||
return self._hSecondaryDeriv_u(src, v, adjoint)
|
||||
|
||||
def _hDeriv_m(self, src, v, adjoint=False):
|
||||
# assuming the primary doesn't depend on the model
|
||||
return self._hSecondaryDeriv_m(src, v, adjoint)
|
||||
|
||||
|
||||
class FieldsFDEM_h(FieldsFDEM):
|
||||
knownFields = {'hSolution':'E'}
|
||||
aliasFields = {
|
||||
'h' : ['hSolution','E','_h'],
|
||||
'hPrimary' : ['hSolution','E','_hPrimary'],
|
||||
'hSecondary' : ['hSolution','E','_hSecondary'],
|
||||
'j' : ['hSolution','F','_j'],
|
||||
'jPrimary' : ['hSolution','F','_jPrimary'],
|
||||
'jSecondary' : ['hSolution','F','_jSecondary']
|
||||
}
|
||||
|
||||
def __init__(self,mesh,survey,**kwargs):
|
||||
FieldsFDEM.__init__(self,mesh,survey,**kwargs)
|
||||
|
||||
def startup(self):
|
||||
self.prob = self.survey.prob
|
||||
self._edgeCurl = self.survey.prob.mesh.edgeCurl
|
||||
self._MeMuI = self.survey.prob.MeMuI
|
||||
self._MfRho = self.survey.prob.MfRho
|
||||
|
||||
def _hPrimary(self, hSolution, srcList):
|
||||
hPrimary = np.zeros_like(hSolution,dtype = complex)
|
||||
for i, src in enumerate(srcList):
|
||||
hp = src.hPrimary(self.prob)
|
||||
if hp is not None:
|
||||
hPrimary[:,i] += hp
|
||||
return hPrimary
|
||||
|
||||
def _hSecondary(self, hSolution, srcList):
|
||||
return hSolution
|
||||
|
||||
def _h(self, hSolution, srcList):
|
||||
return self._hPrimary(hSolution, srcList) + self._hSecondary(hSolution, srcList)
|
||||
|
||||
def _hDeriv_u(self, src, v, adjoint=False):
|
||||
return None
|
||||
|
||||
def _hDeriv_m(self, src, v, adjoint=False):
|
||||
# assuming primary does not depend on the model
|
||||
return None
|
||||
|
||||
def _jPrimary(self, hSolution, srcList):
|
||||
jPrimary = np.zeros([self._edgeCurl.shape[0], hSolution.shape[1]], dtype = complex)
|
||||
for i, src in enumerate(srcList):
|
||||
jp = src.jPrimary(self.prob)
|
||||
if jp is not None:
|
||||
jPrimary[:,i] = jp
|
||||
return jPrimary
|
||||
|
||||
def _jSecondary(self, hSolution, srcList):
|
||||
j = self._edgeCurl*hSolution
|
||||
for i, src in enumerate(srcList):
|
||||
_,S_e = src.eval(self.prob)
|
||||
if S_e is not None:
|
||||
j[:,i] += -S_e
|
||||
return j
|
||||
|
||||
def _jSecondaryDeriv_u(self, src, v, adjoint=False):
|
||||
if not adjoint:
|
||||
return self._edgeCurl*v
|
||||
elif adjoint:
|
||||
return self._edgeCurl.T*v
|
||||
|
||||
def _jSecondaryDeriv_m(self, src, v, adjoint=False):
|
||||
_,S_eDeriv = src.evalDeriv(self.prob, adjoint)
|
||||
S_eDeriv = S_eDeriv(v)
|
||||
if S_eDeriv is not None:
|
||||
return -S_eDeriv
|
||||
return None
|
||||
|
||||
def _j(self, hSolution, srcList):
|
||||
return self._jPrimary(hSolution, srcList) + self._jSecondary(hSolution, srcList)
|
||||
|
||||
def _jDeriv_u(self, src, v, adjoint=False):
|
||||
return self._jSecondaryDeriv_u(src,v,adjoint)
|
||||
|
||||
def _jDeriv_m(self, src, v, adjoint=False):
|
||||
# assuming the primary does not depend on the model
|
||||
return self._jSecondaryDeriv_m(src,v,adjoint)
|
||||
@@ -0,0 +1,491 @@
|
||||
from SimPEG import Survey, Problem, Utils, np, sp
|
||||
from simpegEM.Utils import SrcUtils
|
||||
from simpegEM.Utils.EMUtils import omega, e_from_j, j_from_e, b_from_h, h_from_b
|
||||
from scipy.constants import mu_0
|
||||
|
||||
####################################################
|
||||
# Receivers
|
||||
####################################################
|
||||
|
||||
class RxFDEM(Survey.BaseRx):
|
||||
|
||||
knownRxTypes = {
|
||||
'exr':['e', 'Ex', 'real'],
|
||||
'eyr':['e', 'Ey', 'real'],
|
||||
'ezr':['e', 'Ez', 'real'],
|
||||
'exi':['e', 'Ex', 'imag'],
|
||||
'eyi':['e', 'Ey', 'imag'],
|
||||
'ezi':['e', 'Ez', 'imag'],
|
||||
|
||||
'bxr':['b', 'Fx', 'real'],
|
||||
'byr':['b', 'Fy', 'real'],
|
||||
'bzr':['b', 'Fz', 'real'],
|
||||
'bxi':['b', 'Fx', 'imag'],
|
||||
'byi':['b', 'Fy', 'imag'],
|
||||
'bzi':['b', 'Fz', 'imag'],
|
||||
|
||||
'jxr':['j', 'Fx', 'real'],
|
||||
'jyr':['j', 'Fy', 'real'],
|
||||
'jzr':['j', 'Fz', 'real'],
|
||||
'jxi':['j', 'Fx', 'imag'],
|
||||
'jyi':['j', 'Fy', 'imag'],
|
||||
'jzi':['j', 'Fz', 'imag'],
|
||||
|
||||
'hxr':['h', 'Ex', 'real'],
|
||||
'hyr':['h', 'Ey', 'real'],
|
||||
'hzr':['h', 'Ez', 'real'],
|
||||
'hxi':['h', 'Ex', 'imag'],
|
||||
'hyi':['h', 'Ey', 'imag'],
|
||||
'hzi':['h', 'Ez', 'imag'],
|
||||
}
|
||||
radius = None
|
||||
|
||||
def __init__(self, locs, rxType):
|
||||
Survey.BaseRx.__init__(self, locs, rxType)
|
||||
|
||||
@property
|
||||
def projField(self):
|
||||
"""Field Type projection (e.g. e b ...)"""
|
||||
return self.knownRxTypes[self.rxType][0]
|
||||
|
||||
@property
|
||||
def projGLoc(self):
|
||||
"""Grid Location projection (e.g. Ex Fy ...)"""
|
||||
return self.knownRxTypes[self.rxType][1]
|
||||
|
||||
@property
|
||||
def projComp(self):
|
||||
"""Component projection (real/imag)"""
|
||||
return self.knownRxTypes[self.rxType][2]
|
||||
|
||||
def projectFields(self, src, mesh, u):
|
||||
P = self.getP(mesh)
|
||||
u_part_complex = u[src, self.projField]
|
||||
# get the real or imag component
|
||||
real_or_imag = self.projComp
|
||||
u_part = getattr(u_part_complex, real_or_imag)
|
||||
return P*u_part
|
||||
|
||||
def projectFieldsDeriv(self, src, mesh, u, v, adjoint=False):
|
||||
P = self.getP(mesh)
|
||||
|
||||
if not adjoint:
|
||||
Pv_complex = P * v
|
||||
real_or_imag = self.projComp
|
||||
Pv = getattr(Pv_complex, real_or_imag)
|
||||
elif adjoint:
|
||||
Pv_real = P.T * v
|
||||
|
||||
real_or_imag = self.projComp
|
||||
if real_or_imag == 'imag':
|
||||
Pv = 1j*Pv_real
|
||||
elif real_or_imag == 'real':
|
||||
Pv = Pv_real.astype(complex)
|
||||
else:
|
||||
raise NotImplementedError('must be real or imag')
|
||||
|
||||
return Pv
|
||||
|
||||
|
||||
####################################################
|
||||
# Sources
|
||||
####################################################
|
||||
|
||||
class SrcFDEM(Survey.BaseSrc):
|
||||
freq = None
|
||||
rxPair = RxFDEM
|
||||
integrate = True
|
||||
|
||||
def eval(self, prob):
|
||||
S_m = self.S_m(prob)
|
||||
S_e = self.S_e(prob)
|
||||
return S_m, S_e
|
||||
|
||||
def evalDeriv(self, prob, v, adjoint=False):
|
||||
return lambda v: self.S_mDeriv(prob,v,adjoint), lambda v: self.S_eDeriv(prob,v,adjoint)
|
||||
|
||||
def bPrimary(self, prob):
|
||||
return None
|
||||
|
||||
def hPrimary(self, prob):
|
||||
return None
|
||||
|
||||
def ePrimary(self, prob):
|
||||
return None
|
||||
|
||||
def jPrimary(self, prob):
|
||||
return None
|
||||
|
||||
def S_m(self, prob):
|
||||
return None
|
||||
|
||||
def S_e(self, prob):
|
||||
return None
|
||||
|
||||
def S_mDeriv(self, prob, v, adjoint = False):
|
||||
return None
|
||||
|
||||
def S_eDeriv(self, prob, v, adjoint = False):
|
||||
return None
|
||||
|
||||
|
||||
class SrcFDEM_RawVec_e(SrcFDEM):
|
||||
"""
|
||||
RawVec electric source. It is defined by the user provided vector S_e
|
||||
|
||||
:param numpy.array S_e: electric source term
|
||||
:param float freq: frequency
|
||||
:param rxList: receiver list
|
||||
"""
|
||||
|
||||
def __init__(self, rxList, freq, S_e, ePrimary=None, bPrimary=None, hPrimary=None, jPrimary=None):
|
||||
self._S_e = np.array(S_e,dtype=complex)
|
||||
self._ePrimary = ePrimary
|
||||
self._bPrimary = bPrimary
|
||||
self._hPrimary = hPrimary
|
||||
self._jPrimary = jPrimary
|
||||
self.freq = float(freq)
|
||||
SrcFDEM.__init__(self, rxList)
|
||||
|
||||
def S_e(self, prob):
|
||||
return self._S_e
|
||||
|
||||
def ePrimary(self, prob):
|
||||
return self._ePrimary
|
||||
|
||||
def bPrimary(self, prob):
|
||||
return self._bPrimary
|
||||
|
||||
def hPrimary(self, prob):
|
||||
return self._hPrimary
|
||||
|
||||
def jPrimary(self, prob):
|
||||
return self._jPrimary
|
||||
|
||||
|
||||
class SrcFDEM_RawVec_m(SrcFDEM):
|
||||
"""
|
||||
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 rxList: receiver list
|
||||
"""
|
||||
|
||||
def __init__(self, rxList, freq, S_m, integrate = True, ePrimary=None, bPrimary=None, hPrimary=None, jPrimary=None):
|
||||
self._S_m = np.array(S_m,dtype=complex)
|
||||
self.freq = float(freq)
|
||||
self.integrate = integrate
|
||||
self._ePrimary = np.array(ePrimary,dtype=complex)
|
||||
self._bPrimary = np.array(bPrimary,dtype=complex)
|
||||
self._hPrimary = np.array(hPrimary,dtype=complex)
|
||||
self._jPrimary = np.array(jPrimary,dtype=complex)
|
||||
|
||||
SrcFDEM.__init__(self, rxList)
|
||||
|
||||
def S_m(self, prob):
|
||||
return self._S_m
|
||||
|
||||
def ePrimary(self, prob):
|
||||
return self._ePrimary
|
||||
|
||||
def bPrimary(self, prob):
|
||||
return self._bPrimary
|
||||
|
||||
def hPrimary(self, prob):
|
||||
return self._hPrimary
|
||||
|
||||
def jPrimary(self, prob):
|
||||
return self._jPrimary
|
||||
|
||||
|
||||
class SrcFDEM_RawVec(SrcFDEM):
|
||||
"""
|
||||
RawVec source. It is defined by the user provided vectors S_m, S_e
|
||||
|
||||
:param numpy.array S_m: magnetic source term
|
||||
:param numpy.array S_e: electric source term
|
||||
:param float freq: frequency
|
||||
:param rxList: receiver list
|
||||
"""
|
||||
def __init__(self, rxList, freq, S_m, S_e, integrate = True):
|
||||
self._S_m = np.array(S_m,dtype=complex)
|
||||
self._S_e = np.array(S_e,dtype=complex)
|
||||
self.freq = float(freq)
|
||||
self.integrate = integrate
|
||||
SrcFDEM.__init__(self, rxList)
|
||||
|
||||
def S_m(self, prob):
|
||||
if prob._eqLocs is 'EF' and self.integrate is True:
|
||||
return prob.Me * self._S_m
|
||||
return self._S_m
|
||||
|
||||
def S_e(self, prob):
|
||||
if prob._eqLocs is 'FE' and self.integrate is True:
|
||||
return prob.Me * self._S_e
|
||||
return self._S_e
|
||||
|
||||
|
||||
class SrcFDEM_MagDipole(SrcFDEM):
|
||||
|
||||
#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):
|
||||
self.freq = float(freq)
|
||||
self.loc = loc
|
||||
self.orientation = orientation
|
||||
self.moment = moment
|
||||
self.mu = mu
|
||||
self.integrate = False
|
||||
SrcFDEM.__init__(self, rxList)
|
||||
|
||||
def bPrimary(self, prob):
|
||||
eqLocs = prob._eqLocs
|
||||
|
||||
if eqLocs is 'FE':
|
||||
gridX = prob.mesh.gridEx
|
||||
gridY = prob.mesh.gridEy
|
||||
gridZ = prob.mesh.gridEz
|
||||
C = prob.mesh.edgeCurl
|
||||
|
||||
elif eqLocs is 'EF':
|
||||
gridX = prob.mesh.gridFx
|
||||
gridY = prob.mesh.gridFy
|
||||
gridZ = prob.mesh.gridFz
|
||||
C = prob.mesh.edgeCurl.T
|
||||
|
||||
|
||||
if prob.mesh._meshType is 'CYL':
|
||||
if not prob.mesh.isSymmetric:
|
||||
# TODO ?
|
||||
raise NotImplementedError('Non-symmetric cyl mesh not implemented yet!')
|
||||
a = SrcUtils.MagneticDipoleVectorPotential(self.loc, gridY, 'y', mu=self.mu, moment=self.moment)
|
||||
|
||||
else:
|
||||
srcfct = SrcUtils.MagneticDipoleVectorPotential
|
||||
ax = srcfct(self.loc, gridX, 'x', mu=self.mu, moment=self.moment)
|
||||
ay = srcfct(self.loc, gridY, 'y', mu=self.mu, moment=self.moment)
|
||||
az = srcfct(self.loc, gridZ, 'z', mu=self.mu, moment=self.moment)
|
||||
a = np.concatenate((ax, ay, az))
|
||||
|
||||
return C*a
|
||||
|
||||
def hPrimary(self, prob):
|
||||
b = self.bPrimary(prob)
|
||||
return h_from_b(prob,b)
|
||||
|
||||
def S_m(self, prob):
|
||||
b_p = self.bPrimary(prob)
|
||||
return -1j*omega(self.freq)*b_p
|
||||
|
||||
def S_e(self, prob):
|
||||
if all(np.r_[self.mu] == np.r_[prob.curModel.mu]):
|
||||
return None
|
||||
else:
|
||||
eqLocs = prob._eqLocs
|
||||
|
||||
if eqLocs is 'FE':
|
||||
mui_s = prob.curModel.mui - 1./self.mu
|
||||
MMui_s = prob.mesh.getFaceInnerProduct(mui_s)
|
||||
C = prob.mesh.edgeCurl
|
||||
elif eqLocs is 'EF':
|
||||
mu_s = prob.curModel.mu - self.mu
|
||||
MMui_s = prob.mesh.getEdgeInnerProduct(mu_s,invMat=True)
|
||||
C = prob.mesh.edgeCurl.T
|
||||
|
||||
return -C.T * (MMui_s * self.bPrimary(prob))
|
||||
|
||||
|
||||
class SrcFDEM_MagDipole_Bfield(SrcFDEM):
|
||||
|
||||
#TODO: right now, orientation doesn't actually do anything! The methods in SrcUtils should take care of that
|
||||
#TODO: neither does moment
|
||||
def __init__(self, rxList, freq, loc, orientation='Z', moment=1., mu = mu_0):
|
||||
self.freq = float(freq)
|
||||
self.loc = loc
|
||||
self.orientation = orientation
|
||||
self.moment = moment
|
||||
self.mu = mu
|
||||
SrcFDEM.__init__(self, rxList)
|
||||
|
||||
def bPrimary(self, prob):
|
||||
eqLocs = prob._eqLocs
|
||||
|
||||
if eqLocs is 'FE':
|
||||
gridX = prob.mesh.gridFx
|
||||
gridY = prob.mesh.gridFy
|
||||
gridZ = prob.mesh.gridFz
|
||||
C = prob.mesh.edgeCurl
|
||||
|
||||
elif eqLocs is 'EF':
|
||||
gridX = prob.mesh.gridEx
|
||||
gridY = prob.mesh.gridEy
|
||||
gridZ = prob.mesh.gridEz
|
||||
C = prob.mesh.edgeCurl.T
|
||||
|
||||
srcfct = SrcUtils.MagneticDipoleFields
|
||||
if prob.mesh._meshType is 'CYL':
|
||||
if not prob.mesh.isSymmetric:
|
||||
# TODO ?
|
||||
raise NotImplementedError('Non-symmetric cyl mesh not implemented yet!')
|
||||
bx = srcfct(self.loc, gridX, 'x', mu=self.mu, moment=self.moment)
|
||||
bz = srcfct(self.loc, gridZ, 'z', mu=self.mu, moment=self.moment)
|
||||
b = np.concatenate((bx,bz))
|
||||
else:
|
||||
bx = srcfct(self.loc, gridX, 'x', mu=self.mu, moment=self.moment)
|
||||
by = srcfct(self.loc, gridY, 'y', mu=self.mu, moment=self.moment)
|
||||
bz = srcfct(self.loc, gridZ, 'z', mu=self.mu, moment=self.moment)
|
||||
b = np.concatenate((bx,by,bz))
|
||||
|
||||
return b
|
||||
|
||||
def hPrimary(self, prob):
|
||||
b = self.bPrimary(prob)
|
||||
return h_from_b(prob, b)
|
||||
|
||||
def S_m(self, prob):
|
||||
b = self.bPrimary(prob)
|
||||
return -1j*omega(self.freq)*b
|
||||
|
||||
def S_e(self, prob):
|
||||
if all(np.r_[self.mu] == np.r_[prob.curModel.mu]):
|
||||
return None
|
||||
else:
|
||||
eqLocs = prob._eqLocs
|
||||
|
||||
if eqLocs is 'FE':
|
||||
mui_s = prob.curModel.mui - 1./self.mu
|
||||
MMui_s = prob.mesh.getFaceInnerProduct(mui_s)
|
||||
C = prob.mesh.edgeCurl
|
||||
elif eqLocs is 'EF':
|
||||
mu_s = prob.curModel.mu - self.mu
|
||||
MMui_s = prob.mesh.getEdgeInnerProduct(mu_s,invMat=True)
|
||||
C = prob.mesh.edgeCurl.T
|
||||
|
||||
return -C.T * (MMui_s * self.bPrimary(prob))
|
||||
|
||||
|
||||
class SrcFDEM_CircularLoop(SrcFDEM):
|
||||
|
||||
#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):
|
||||
self.freq = float(freq)
|
||||
self.orientation = orientation
|
||||
self.radius = radius
|
||||
self.mu = mu
|
||||
self.loc = loc
|
||||
self.integrate = False
|
||||
SrcFDEM.__init__(self, rxList)
|
||||
|
||||
def bPrimary(self, prob):
|
||||
eqLocs = prob._eqLocs
|
||||
|
||||
if eqLocs is 'FE':
|
||||
gridX = prob.mesh.gridEx
|
||||
gridY = prob.mesh.gridEy
|
||||
gridZ = prob.mesh.gridEz
|
||||
C = prob.mesh.edgeCurl
|
||||
|
||||
elif eqLocs is 'EF':
|
||||
gridX = prob.mesh.gridFx
|
||||
gridY = prob.mesh.gridFy
|
||||
gridZ = prob.mesh.gridFz
|
||||
C = prob.mesh.edgeCurl.T
|
||||
|
||||
if prob.mesh._meshType is 'CYL':
|
||||
if not prob.mesh.isSymmetric:
|
||||
# TODO ?
|
||||
raise NotImplementedError('Non-symmetric cyl mesh not implemented yet!')
|
||||
a = SrcUtils.MagneticDipoleVectorPotential(self.loc, gridY, 'y', moment=self.radius, mu=self.mu)
|
||||
|
||||
else:
|
||||
srcfct = SrcUtils.MagneticDipoleVectorPotential
|
||||
ax = srcfct(self.loc, gridX, 'x', self.radius, mu=self.mu)
|
||||
ay = srcfct(self.loc, gridY, 'y', self.radius, mu=self.mu)
|
||||
az = srcfct(self.loc, gridZ, 'z', self.radius, mu=self.mu)
|
||||
a = np.concatenate((ax, ay, az))
|
||||
|
||||
return C*a
|
||||
|
||||
def hPrimary(self, prob):
|
||||
b = self.bPrimary(prob)
|
||||
return 1./self.mu*b
|
||||
|
||||
def S_m(self, prob):
|
||||
b = self.bPrimary(prob)
|
||||
return -1j*omega(self.freq)*b
|
||||
|
||||
def S_e(self, prob):
|
||||
if all(np.r_[self.mu] == np.r_[prob.curModel.mu]):
|
||||
return None
|
||||
else:
|
||||
eqLocs = prob._eqLocs
|
||||
|
||||
if eqLocs is 'FE':
|
||||
mui_s = prob.curModel.mui - 1./self.mu
|
||||
MMui_s = prob.mesh.getFaceInnerProduct(mui_s)
|
||||
C = prob.mesh.edgeCurl
|
||||
elif eqLocs is 'EF':
|
||||
mu_s = prob.curModel.mu - self.mu
|
||||
MMui_s = prob.mesh.getEdgeInnerProduct(mu_s,invMat=True)
|
||||
C = prob.mesh.edgeCurl.T
|
||||
|
||||
return -C.T * (MMui_s * self.bPrimary(prob))
|
||||
|
||||
|
||||
####################################################
|
||||
# Survey
|
||||
####################################################
|
||||
|
||||
class SurveyFDEM(Survey.BaseSurvey):
|
||||
"""
|
||||
docstring for SurveyFDEM
|
||||
"""
|
||||
|
||||
srcPair = SrcFDEM
|
||||
|
||||
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)
|
||||
|
||||
@property
|
||||
def nSrcByFreq(self):
|
||||
if getattr(self, '_nSrcByFreq', None) is None:
|
||||
self._nSrcByFreq = {}
|
||||
for freq in self.freqs:
|
||||
self._nSrcByFreq[freq] = len(self.getSrcByFreq(freq))
|
||||
return self._nSrcByFreq
|
||||
|
||||
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 = Survey.Data(self)
|
||||
for src in self.srcList:
|
||||
for rx in src.rxList:
|
||||
data[src, rx] = rx.projectFields(src, self.mesh, u)
|
||||
return data
|
||||
|
||||
def projectFieldsDeriv(self, u):
|
||||
raise Exception('Use Sources to project fields deriv.')
|
||||
@@ -0,0 +1,3 @@
|
||||
from SurveyFDEM import *
|
||||
from FDEM import BaseFDEMProblem, ProblemFDEM_e, ProblemFDEM_b, ProblemFDEM_j, ProblemFDEM_h
|
||||
from FieldsFDEM import *
|
||||
@@ -0,0 +1,155 @@
|
||||
from SimPEG import Solver, Problem
|
||||
from SimPEG.Problem import BaseTimeProblem
|
||||
from simpegEM.Utils import SrcUtils
|
||||
from scipy.constants import mu_0
|
||||
from SimPEG.Utils import sdiag, mkvc
|
||||
from SimPEG import Utils, Mesh
|
||||
from simpegEM.Base import BaseEMProblem
|
||||
import numpy as np
|
||||
|
||||
|
||||
class FieldsTDEM(Problem.TimeFields):
|
||||
"""Fancy Field Storage for a TDEM survey."""
|
||||
knownFields = {'b': 'F', 'e': 'E'}
|
||||
|
||||
def tovec(self):
|
||||
nSrc, nF, nE = self.survey.nSrc, self.mesh.nF, self.mesh.nE
|
||||
u = np.empty((0,nSrc)) #((0,1) if nSrc == 1 else (0, nSrc))
|
||||
|
||||
for i in range(self.survey.prob.nT):
|
||||
if 'b' in self:
|
||||
b = self[:,'b',i+1]
|
||||
else:
|
||||
b = np.zeros((nF,nSrc)) # if nSrc == 1 else (nF, nSrc))
|
||||
|
||||
if 'e' in self:
|
||||
e = self[:,'e',i+1]
|
||||
else:
|
||||
e = np.zeros((nE,nSrc)) # if nSrc == 1 else (nE, nSrc))
|
||||
u = np.concatenate((u, b, e))
|
||||
return Utils.mkvc(u,nSrc)
|
||||
|
||||
|
||||
class BaseTDEMProblem(BaseTimeProblem, BaseEMProblem):
|
||||
"""docstring for ProblemTDEM1D"""
|
||||
def __init__(self, mesh, mapping=None, **kwargs):
|
||||
BaseTimeProblem.__init__(self, mesh, mapping=mapping, **kwargs)
|
||||
|
||||
_FieldsForward_pair = FieldsTDEM #: used for the forward calculation only
|
||||
|
||||
def fields(self, m):
|
||||
if self.verbose: print '%s\nCalculating fields(m)\n%s'%('*'*50,'*'*50)
|
||||
self.curModel = m
|
||||
# Create a fields storage object
|
||||
F = self._FieldsForward_pair(self.mesh, self.survey)
|
||||
for src in self.survey.srcList:
|
||||
# Set the initial conditions
|
||||
F[src,:,0] = src.getInitialFields(self.mesh)
|
||||
F = self.forward(m, self.getRHS, F=F)
|
||||
if self.verbose: print '%s\nDone calculating fields(m)\n%s'%('*'*50,'*'*50)
|
||||
return F
|
||||
|
||||
def forward(self, m, RHS, F=None):
|
||||
self.curModel = m
|
||||
F = F or FieldsTDEM(self.mesh, self.survey)
|
||||
|
||||
dtFact = None
|
||||
Ainv = None
|
||||
for tInd, dt in enumerate(self.timeSteps):
|
||||
if dt != dtFact:
|
||||
dtFact = dt
|
||||
if Ainv is not None:
|
||||
Ainv.clean()
|
||||
A = self.getA(tInd)
|
||||
if self.verbose: print 'Factoring... (dt = %e)'%dt
|
||||
Ainv = self.Solver(A, **self.solverOpts)
|
||||
if self.verbose: print 'Done'
|
||||
rhs = RHS(tInd, F)
|
||||
if self.verbose: print ' Solving... (tInd = %d)'%tInd
|
||||
sol = Ainv * rhs
|
||||
if self.verbose: print ' Done...'
|
||||
if sol.ndim == 1:
|
||||
sol.shape = (sol.size,1)
|
||||
F[:,self.solType,tInd+1] = sol
|
||||
Ainv.clean()
|
||||
return F
|
||||
|
||||
def adjoint(self, m, RHS, F=None):
|
||||
self.curModel = m
|
||||
F = F or FieldsTDEM(self.mesh, self.survey)
|
||||
|
||||
dtFact = None
|
||||
Ainv = None
|
||||
for tInd, dt in reversed(list(enumerate(self.timeSteps))):
|
||||
if dt != dtFact:
|
||||
dtFact = dt
|
||||
if Ainv is not None:
|
||||
Ainv.clean()
|
||||
A = self.getA(tInd)
|
||||
if self.verbose: print 'Factoring (Adjoint)... (dt = %e)'%dt
|
||||
Ainv = self.Solver(A, **self.solverOpts)
|
||||
if self.verbose: print 'Done'
|
||||
rhs = RHS(tInd, F)
|
||||
if self.verbose: print ' Solving (Adjoint)... (tInd = %d)'%tInd
|
||||
sol = Ainv * rhs
|
||||
if self.verbose: print ' Done...'
|
||||
if sol.ndim == 1:
|
||||
sol.shape = (sol.size,1)
|
||||
F[:,self.solType,tInd+1] = sol
|
||||
Ainv.clean()
|
||||
return F
|
||||
|
||||
def Jvec(self, m, v, u=None):
|
||||
"""
|
||||
:param numpy.array m: Conductivity model
|
||||
:param numpy.ndarray v: vector (model object)
|
||||
:param simpegEM.TDEM.FieldsTDEM u: Fields resulting from m
|
||||
:rtype: numpy.ndarray
|
||||
:return: w (data object)
|
||||
|
||||
Multiplying \\\(\\\mathbf{J}\\\) onto a vector can be broken into three steps
|
||||
|
||||
* Compute \\\(\\\\vec{p} = \\\mathbf{G}v\\\)
|
||||
* Solve \\\(\\\hat{\\\mathbf{A}} \\\\vec{y} = \\\\vec{p}\\\)
|
||||
* Compute \\\(\\\\vec{w} = -\\\mathbf{Q} \\\\vec{y}\\\)
|
||||
|
||||
"""
|
||||
if self.verbose: print '%s\nCalculating J(v)\n%s'%('*'*50,'*'*50)
|
||||
self.curModel = m
|
||||
if u is None:
|
||||
u = self.fields(m)
|
||||
p = self.Gvec(m, v, u)
|
||||
y = self.solveAh(m, p)
|
||||
Jv = self.survey.projectFieldsDeriv(u, v=y)
|
||||
if self.verbose: print '%s\nDone calculating J(v)\n%s'%('*'*50,'*'*50)
|
||||
return - mkvc(Jv)
|
||||
|
||||
def Jtvec(self, m, v, u=None):
|
||||
"""
|
||||
:param numpy.array m: Conductivity model
|
||||
:param numpy.ndarray,SimPEG.Survey.Data v: vector (data object)
|
||||
:param simpegEM.TDEM.FieldsTDEM u: Fields resulting from m
|
||||
:rtype: numpy.ndarray
|
||||
:return: w (model object)
|
||||
|
||||
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\\\)
|
||||
|
||||
"""
|
||||
if self.verbose: print '%s\nCalculating J^T(v)\n%s'%('*'*50,'*'*50)
|
||||
self.curModel = m
|
||||
if u is None:
|
||||
u = self.fields(m)
|
||||
|
||||
if not isinstance(v, self.dataPair):
|
||||
v = self.dataPair(self.survey, v)
|
||||
|
||||
p = self.survey.projectFieldsDeriv(u, v=v, adjoint=True)
|
||||
y = self.solveAht(m, p)
|
||||
w = self.Gtvec(m, y, u)
|
||||
if self.verbose: print '%s\nDone calculating J^T(v)\n%s'%('*'*50,'*'*50)
|
||||
return - mkvc(w)
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
from SimPEG import Utils, Survey, np
|
||||
from SimPEG.Survey import BaseSurvey
|
||||
from simpegEM.Utils import SrcUtils
|
||||
from BaseTDEM import FieldsTDEM
|
||||
|
||||
|
||||
class RxTDEM(Survey.BaseTimeRx):
|
||||
|
||||
knownRxTypes = {
|
||||
'ex':['e', 'Ex', 'N'],
|
||||
'ey':['e', 'Ey', 'N'],
|
||||
'ez':['e', 'Ez', 'N'],
|
||||
|
||||
'bx':['b', 'Fx', 'N'],
|
||||
'by':['b', 'Fy', 'N'],
|
||||
'bz':['b', 'Fz', 'N'],
|
||||
|
||||
'dbxdt':['b', 'Fx', 'CC'],
|
||||
'dbydt':['b', 'Fy', 'CC'],
|
||||
'dbzdt':['b', 'Fz', 'CC'],
|
||||
}
|
||||
|
||||
def __init__(self, locs, times, rxType):
|
||||
Survey.BaseTimeRx.__init__(self, locs, times, rxType)
|
||||
|
||||
@property
|
||||
def projField(self):
|
||||
"""Field Type projection (e.g. e b ...)"""
|
||||
return self.knownRxTypes[self.rxType][0]
|
||||
|
||||
@property
|
||||
def projGLoc(self):
|
||||
"""Grid Location projection (e.g. Ex Fy ...)"""
|
||||
return self.knownRxTypes[self.rxType][1]
|
||||
|
||||
@property
|
||||
def projTLoc(self):
|
||||
"""Time Location projection (e.g. CC N)"""
|
||||
return self.knownRxTypes[self.rxType][2]
|
||||
|
||||
def getTimeP(self, timeMesh):
|
||||
"""
|
||||
Returns the time projection matrix.
|
||||
|
||||
.. note::
|
||||
|
||||
This is not stored in memory, but is created on demand.
|
||||
"""
|
||||
if self.rxType in ['dbxdt','dbydt','dbzdt']:
|
||||
return timeMesh.getInterpolationMat(self.times, self.projTLoc)*timeMesh.faceDiv
|
||||
else:
|
||||
return timeMesh.getInterpolationMat(self.times, self.projTLoc)
|
||||
|
||||
def projectFields(self, src, mesh, timeMesh, u):
|
||||
P = self.getP(mesh, timeMesh)
|
||||
u_part = Utils.mkvc(u[src, self.projField, :])
|
||||
return P*u_part
|
||||
|
||||
def projectFieldsDeriv(self, src, mesh, timeMesh, u, v, adjoint=False):
|
||||
P = self.getP(mesh, timeMesh)
|
||||
|
||||
if not adjoint:
|
||||
return P * Utils.mkvc(v[src, self.projField, :])
|
||||
elif adjoint:
|
||||
return P.T * v[src, self]
|
||||
|
||||
|
||||
class SrcTDEM(Survey.BaseSrc):
|
||||
rxPair = RxTDEM
|
||||
radius = None
|
||||
|
||||
def getInitialFields(self, mesh):
|
||||
F0 = getattr(self, '_getInitialFields_' + self.srcType)(mesh)
|
||||
return F0
|
||||
|
||||
def getJs(self, mesh, time):
|
||||
return None
|
||||
|
||||
|
||||
class SrcTDEM_VMD_MVP(SrcTDEM):
|
||||
|
||||
def __init__(self,rxList,loc):
|
||||
self.loc = loc
|
||||
SrcTDEM.__init__(self,rxList)
|
||||
|
||||
def getInitialFields(self, mesh):
|
||||
"""Vertical magnetic dipole, magnetic vector potential"""
|
||||
if mesh._meshType is 'CYL':
|
||||
if mesh.isSymmetric:
|
||||
MVP = SrcUtils.MagneticDipoleVectorPotential(self.loc, mesh, 'Ey')
|
||||
else:
|
||||
raise NotImplementedError('Non-symmetric cyl mesh not implemented yet!')
|
||||
elif mesh._meshType is 'TENSOR':
|
||||
MVP = SrcUtils.MagneticDipoleVectorPotential(self.loc, mesh, ['Ex','Ey','Ez'])
|
||||
else:
|
||||
raise Exception('Unknown mesh for VMD')
|
||||
|
||||
return {"b": mesh.edgeCurl*MVP}
|
||||
|
||||
|
||||
class SrcTDEM_CircularLoop_MVP(SrcTDEM):
|
||||
|
||||
def __init__(self,rxList,loc,radius):
|
||||
self.loc = loc
|
||||
self.radius = radius
|
||||
SrcTDEM.__init__(self,rxList)
|
||||
|
||||
def getInitialFields(self, mesh):
|
||||
"""Circular Loop, magnetic vector potential"""
|
||||
if mesh._meshType is 'CYL':
|
||||
if mesh.isSymmetric:
|
||||
MVP = SrcUtils.MagneticLoopVectorPotential(self.loc, mesh, 'Ey', self.radius)
|
||||
else:
|
||||
raise NotImplementedError('Non-symmetric cyl mesh not implemented yet!')
|
||||
elif mesh._meshType is 'TENSOR':
|
||||
MVP = SrcUtils.MagneticLoopVectorPotential(self.loc, mesh, ['Ex','Ey','Ez'], self.radius)
|
||||
else:
|
||||
raise Exception('Unknown mesh for CircularLoop')
|
||||
|
||||
return {"b": mesh.edgeCurl*MVP}
|
||||
|
||||
|
||||
class SurveyTDEM(Survey.BaseSurvey):
|
||||
"""
|
||||
docstring for SurveyTDEM
|
||||
"""
|
||||
srcPair = SrcTDEM
|
||||
|
||||
def __init__(self, srcList, **kwargs):
|
||||
# Sort these by frequency
|
||||
self.srcList = srcList
|
||||
Survey.BaseSurvey.__init__(self, **kwargs)
|
||||
|
||||
def projectFields(self, u):
|
||||
data = Survey.Data(self)
|
||||
for src in self.srcList:
|
||||
for rx in src.rxList:
|
||||
data[src, rx] = rx.projectFields(src, self.mesh, self.prob.timeMesh, u)
|
||||
return data
|
||||
|
||||
def projectFieldsDeriv(self, u, v=None, adjoint=False):
|
||||
assert v is not None, 'v to multiply must be provided.'
|
||||
|
||||
if not adjoint:
|
||||
data = Survey.Data(self)
|
||||
for src in self.srcList:
|
||||
for rx in src.rxList:
|
||||
data[src, rx] = rx.projectFieldsDeriv(src, self.mesh, self.prob.timeMesh, u, v)
|
||||
return data
|
||||
else:
|
||||
f = FieldsTDEM(self.mesh, self)
|
||||
for src in self.srcList:
|
||||
for rx in src.rxList:
|
||||
Ptv = rx.projectFieldsDeriv(src, self.mesh, self.prob.timeMesh, u, v, adjoint=True)
|
||||
Ptv = Ptv.reshape((-1, self.prob.timeMesh.nN), order='F')
|
||||
if rx.projField not in f: # first time we are projecting
|
||||
f[src, rx.projField, :] = Ptv
|
||||
else: # there are already fields, so let's add to them!
|
||||
f[src, rx.projField, :] += Ptv
|
||||
return f
|
||||
|
||||
|
||||
@@ -0,0 +1,356 @@
|
||||
from BaseTDEM import BaseTDEMProblem, FieldsTDEM
|
||||
from SimPEG.Utils import mkvc, sdiag
|
||||
import numpy as np
|
||||
from SurveyTDEM import SurveyTDEM
|
||||
|
||||
|
||||
class FieldsTDEM_e_from_b(FieldsTDEM):
|
||||
"""Fancy Field Storage for a TDEM survey."""
|
||||
knownFields = {'b': 'F'}
|
||||
aliasFields = {'e': ['b','E','e_from_b']}
|
||||
|
||||
def startup(self):
|
||||
self.MeSigmaI = self.survey.prob.MeSigmaI
|
||||
self.edgeCurlT = self.survey.prob.mesh.edgeCurl.T
|
||||
self.MfMui = self.survey.prob.MfMui
|
||||
|
||||
def e_from_b(self, b, srcInd, timeInd):
|
||||
# TODO: implement non-zero js
|
||||
return self.MeSigmaI*(self.edgeCurlT*(self.MfMui*b))
|
||||
|
||||
class FieldsTDEM_e_from_b_Ah(FieldsTDEM):
|
||||
"""Fancy Field Storage for a TDEM survey.
|
||||
|
||||
This is used when solving Ahat and AhatT
|
||||
"""
|
||||
knownFields = {'b': 'F'}
|
||||
aliasFields = {'e': ['b','E','e_from_b']}
|
||||
p = None
|
||||
|
||||
def startup(self):
|
||||
self.MeSigmaI = self.survey.prob.MeSigmaI
|
||||
self.edgeCurlT = self.survey.prob.mesh.edgeCurl.T
|
||||
self.MfMui = self.survey.prob.MfMui
|
||||
|
||||
def e_from_b(self, y_b, srcInd, tInd):
|
||||
y_e = self.MeSigmaI*(self.edgeCurlT*(self.MfMui*y_b))
|
||||
if 'e' in self.p:
|
||||
y_e = y_e - self.MeSigmaI*self.p[srcInd,'e',tInd]
|
||||
return y_e
|
||||
|
||||
class ProblemTDEM_b(BaseTDEMProblem):
|
||||
"""
|
||||
Time-Domain EM problem - B-formulation
|
||||
|
||||
TDEM_b treats the following discretization of Maxwell's equations
|
||||
|
||||
.. math::
|
||||
\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)}
|
||||
|
||||
with \\\(\\b\\\) defined on cell faces and \\\(\e\\\) defined on edges.
|
||||
"""
|
||||
def __init__(self, mesh, mapping=None, **kwargs):
|
||||
BaseTDEMProblem.__init__(self, mesh, mapping=mapping, **kwargs)
|
||||
|
||||
solType = 'b' #: Type of the solution, in this case the 'b' field
|
||||
|
||||
surveyPair = SurveyTDEM
|
||||
_FieldsForward_pair = FieldsTDEM_e_from_b #: used for the forward calculation only
|
||||
|
||||
####################################################
|
||||
# Internal Methods
|
||||
####################################################
|
||||
|
||||
def getA(self, tInd):
|
||||
"""
|
||||
:param int tInd: Time index
|
||||
:rtype: scipy.sparse.csr_matrix
|
||||
:return: A
|
||||
"""
|
||||
dt = self.timeSteps[tInd]
|
||||
return self.MfMui*self.mesh.edgeCurl*self.MeSigmaI*self.mesh.edgeCurl.T*self.MfMui + (1.0/dt)*self.MfMui
|
||||
|
||||
def getRHS(self, tInd, F):
|
||||
dt = self.timeSteps[tInd]
|
||||
B_n = np.c_[[F[src,'b',tInd] for src in self.survey.srcList]].T
|
||||
if B_n.shape[0] is not 1:
|
||||
raise NotImplementedError('getRHS not implemented for this shape of B_n')
|
||||
RHS = (1.0/dt)*self.MfMui*B_n[0,:,:] #TODO: This is a hack
|
||||
return RHS
|
||||
|
||||
####################################################
|
||||
# Derivatives
|
||||
####################################################
|
||||
|
||||
def Gvec(self, m, vec, u=None):
|
||||
"""
|
||||
:param numpy.array m: Conductivity model
|
||||
:param numpy.array vec: vector (like a model)
|
||||
:param simpegEM.TDEM.FieldsTDEM u: Fields resulting from m
|
||||
:rtype: simpegEM.TDEM.FieldsTDEM
|
||||
:return: f
|
||||
|
||||
Multiply G by a vector
|
||||
"""
|
||||
if u is None:
|
||||
u = self.fields(m)
|
||||
self.curModel = m
|
||||
|
||||
# Note: Fields has shape (nF/E, nSrc, nT+1)
|
||||
# However, p will only really fill (:,:,1:nT+1)
|
||||
# meaning the 'initial fields' are zero (:,:,0)
|
||||
p = FieldsTDEM(self.mesh, self.survey)
|
||||
# 'b' at all times is zero.
|
||||
# However, to save memory we will **not** do:
|
||||
#
|
||||
# p[:, 'b', :] = 0.0
|
||||
|
||||
# fake initial 'e' fields
|
||||
p[:, 'e', 0] = 0.0
|
||||
dMdsig = self.MeSigmaDeriv
|
||||
# self.mesh.getEdgeInnerProductDeriv(self.curModel.transform)
|
||||
# dsigdm_x_v = self.curModel.sigmaDeriv*vec
|
||||
# dsigdm_x_v = self.curModel.transformDeriv*vec
|
||||
for i in range(1,self.nT+1):
|
||||
# TODO: G[1] may be dependent on the model
|
||||
# for a galvanic source (deriv of the dc problem)
|
||||
#
|
||||
# Do multiplication for all src in self.survey.srcList
|
||||
for src in self.survey.srcList:
|
||||
p[src, 'e', i] = - dMdsig(u[src,'e',i]) * vec
|
||||
return p
|
||||
|
||||
def Gtvec(self, m, vec, u=None):
|
||||
"""
|
||||
:param numpy.array m: Conductivity model
|
||||
:param numpy.array vec: vector (like a fields)
|
||||
:param simpegEM.TDEM.FieldsTDEM u: Fields resulting from m
|
||||
:rtype: np.ndarray (like a model)
|
||||
:return: p
|
||||
|
||||
Multiply G.T by a vector
|
||||
"""
|
||||
if u is None:
|
||||
u = self.fields(m)
|
||||
self.curModel = m
|
||||
# dMdsig = self.mesh.getEdgeInnerProductDeriv(self.curModel.transform)
|
||||
# dsigdm = self.curModel.transformDeriv
|
||||
MeSigmaDeriv = self.MeSigmaDeriv
|
||||
|
||||
nSrc = self.survey.nSrc
|
||||
VUs = None
|
||||
# Here we can do internal multiplications of Gt*v and then multiply by MsigDeriv.T in one go.
|
||||
for i in range(1,self.nT+1):
|
||||
vu = None
|
||||
for src in self.survey.srcList:
|
||||
vusrc = MeSigmaDeriv(u[src,'e',i]).T * vec[src,'e',i]
|
||||
vu = vusrc if vu is None else vu + vusrc
|
||||
VUs = vu if VUs is None else VUs + vu
|
||||
# p = -dsigdm.T*VUs
|
||||
return -VUs
|
||||
|
||||
def solveAh(self, m, p):
|
||||
"""
|
||||
:param numpy.array m: Conductivity model
|
||||
:param simpegEM.TDEM.FieldsTDEM p: Fields object
|
||||
:rtype: simpegEM.TDEM.FieldsTDEM
|
||||
:return: y
|
||||
|
||||
Solve the block-matrix system \\\(\\\hat{A} \\\hat{y} = \\\hat{p}\\\):
|
||||
|
||||
.. math::
|
||||
\mathbf{\hat{A}} = \left[
|
||||
\\begin{array}{cccc}
|
||||
A & 0 & & \\\\
|
||||
B & A & & \\\\
|
||||
& \ddots & \ddots & \\\\
|
||||
& & B & A
|
||||
\end{array}
|
||||
\\right] \\\\
|
||||
\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] \\\\
|
||||
"""
|
||||
|
||||
def AhRHS(tInd, y):
|
||||
rhs = self.MfMui*(self.mesh.edgeCurl*(self.MeSigmaI*p[:,'e',tInd+1]))
|
||||
if 'b' in p:
|
||||
rhs = rhs + p[:,'b',tInd+1]
|
||||
if tInd == 0:
|
||||
return rhs
|
||||
dt = self.timeSteps[tInd]
|
||||
return rhs + 1.0/dt*self.MfMui*y[:,'b',tInd]
|
||||
|
||||
F = FieldsTDEM_e_from_b_Ah(self.mesh, self.survey, p=p)
|
||||
|
||||
return self.forward(m, AhRHS, F)
|
||||
|
||||
def solveAht(self, m, p):
|
||||
"""
|
||||
:param numpy.array m: Conductivity model
|
||||
:param simpegEM.TDEM.FieldsTDEM p: Fields object
|
||||
:rtype: simpegEM.TDEM.FieldsTDEM
|
||||
:return: y
|
||||
|
||||
Solve the block-matrix system \\\(\\\hat{A}^\\\\top \\\hat{y} = \\\hat{p}\\\):
|
||||
|
||||
.. math::
|
||||
\mathbf{\hat{A}}^\\top = \left[
|
||||
\\begin{array}{cccc}
|
||||
A & B & & \\\\
|
||||
& \ddots & \ddots & \\\\
|
||||
& & A & B \\\\
|
||||
& & 0 & A
|
||||
\end{array}
|
||||
\\right] \\\\
|
||||
\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] \\\\
|
||||
"""
|
||||
|
||||
# Mini Example:
|
||||
#
|
||||
# nT = 3, len(times) == 4, fields stored in F[:,:,1:4]
|
||||
#
|
||||
# 0 is held for initial conditions (this shifts the storage by +1)
|
||||
# ^
|
||||
# fLoc 0 1 2 3
|
||||
# |-----|-----|-----|
|
||||
# tInd 0 1 2
|
||||
# / ___/
|
||||
# 2 (tInd=2 uses fields 3 and would use 4 but it doesn't exist)
|
||||
# / ___/
|
||||
# 1 (tInd=1 uses fields 2 and 3)
|
||||
|
||||
def AhtRHS(tInd, y):
|
||||
nSrc, nF = self.survey.nSrc, self.mesh.nF
|
||||
rhs = np.zeros((nF,1) if nSrc == 1 else (nF, nSrc))
|
||||
|
||||
if 'e' in p:
|
||||
rhs += self.MfMui*(self.mesh.edgeCurl*(self.MeSigmaI*p[:,'e',tInd+1]))
|
||||
if 'b' in p:
|
||||
rhs += p[:,'b',tInd+1]
|
||||
|
||||
if tInd == self.nT-1:
|
||||
return rhs
|
||||
dt = self.timeSteps[tInd+1]
|
||||
return rhs + 1.0/dt*self.MfMui*y[:,'b',tInd+2]
|
||||
|
||||
F = FieldsTDEM_e_from_b_Ah(self.mesh, self.survey, p=p)
|
||||
|
||||
return self.adjoint(m, AhtRHS, F)
|
||||
|
||||
####################################################
|
||||
# Functions for tests
|
||||
####################################################
|
||||
|
||||
def _AhVec(self, m, vec):
|
||||
"""
|
||||
:param numpy.array m: Conductivity model
|
||||
:param simpegEM.TDEM.FieldsTDEM vec: Fields object
|
||||
:rtype: simpegEM.TDEM.FieldsTDEM
|
||||
:return: f
|
||||
|
||||
Multiply the matrix \\\(\\\hat{A}\\\) by a fields vector where
|
||||
|
||||
.. math::
|
||||
\mathbf{\hat{A}} = \left[
|
||||
\\begin{array}{cccc}
|
||||
A & 0 & & \\\\
|
||||
B & A & & \\\\
|
||||
& \ddots & \ddots & \\\\
|
||||
& & B & A
|
||||
\end{array}
|
||||
\\right] \\\\
|
||||
\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] \\\\
|
||||
"""
|
||||
|
||||
self.curModel = m
|
||||
f = FieldsTDEM(self.mesh, self.survey)
|
||||
for i in range(1,self.nT+1):
|
||||
dt = self.timeSteps[i-1]
|
||||
b = 1.0/dt*self.MfMui*vec[:,'b',i] + self.MfMui*(self.mesh.edgeCurl*vec[:,'e',i])
|
||||
if i > 1:
|
||||
b = b - 1.0/dt*self.MfMui*vec[:,'b',i-1]
|
||||
f[:,'b',i] = b
|
||||
f[:,'e',i] = self.mesh.edgeCurl.T*(self.MfMui*vec[:,'b',i]) - self.MeSigma*vec[:,'e',i]
|
||||
return f
|
||||
|
||||
def _AhtVec(self, m, vec):
|
||||
"""
|
||||
:param numpy.array m: Conductivity model
|
||||
:param simpegEM.TDEM.FieldsTDEM vec: Fields object
|
||||
:rtype: simpegEM.TDEM.FieldsTDEM
|
||||
:return: f
|
||||
|
||||
Multiply the matrix \\\(\\\hat{A}\\\) by a fields vector where
|
||||
|
||||
.. math::
|
||||
\mathbf{\hat{A}}^\\top = \left[
|
||||
\\begin{array}{cccc}
|
||||
A & B & & \\\\
|
||||
& \ddots & \ddots & \\\\
|
||||
& & A & B \\\\
|
||||
& & 0 & A
|
||||
\end{array}
|
||||
\\right] \\\\
|
||||
\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] \\\\
|
||||
"""
|
||||
self.curModel = m
|
||||
f = FieldsTDEM(self.mesh, self.survey)
|
||||
for i in range(self.nT):
|
||||
b = 1.0/self.timeSteps[i]*self.MfMui*vec[:,'b',i+1] + self.MfMui*(self.mesh.edgeCurl*vec[:,'e',i+1])
|
||||
if i < self.nT-1:
|
||||
b = b - 1.0/self.timeSteps[i+1]*self.MfMui*vec[:,'b',i+2]
|
||||
f[:,'b', i+1] = b
|
||||
f[:,'e', i+1] = self.mesh.edgeCurl.T*(self.MfMui*vec[:,'b',i+1]) - self.MeSigma*vec[:,'e',i+1]
|
||||
return f
|
||||
@@ -0,0 +1,3 @@
|
||||
from SurveyTDEM import * #SurveyTDEM, RxTDEM, SrcTDEM
|
||||
from BaseTDEM import BaseTDEMProblem, FieldsTDEM
|
||||
from TDEM_b import ProblemTDEM_b
|
||||
@@ -0,0 +1,49 @@
|
||||
import numpy as np
|
||||
from scipy.constants import mu_0, epsilon_0
|
||||
|
||||
# useful params
|
||||
def omega(freq):
|
||||
"""Angular frequency, omega"""
|
||||
return 2.*np.pi*freq
|
||||
|
||||
def k(freq, sigma, mu=mu_0, eps=epsilon_0):
|
||||
""" Eq 1.47 - 1.49 in Ward and Hohmann """
|
||||
w = omega(freq)
|
||||
alp = w * np.sqrt( mu*eps/2 * ( np.sqrt(1. + (sigma / (eps*w))**2 ) + 1) )
|
||||
beta = w * np.sqrt( mu*eps/2 * ( np.sqrt(1. + (sigma / (eps*w))**2 ) - 1) )
|
||||
return alp - 1j*beta
|
||||
|
||||
# Constitutive relations
|
||||
def e_from_j(prob,j):
|
||||
eqLocs = prob._eqLocs
|
||||
if eqLocs is 'FE':
|
||||
MSigmaI = prob.MeSigmaI
|
||||
elif eqLocs is 'EF':
|
||||
MSigmaI = prob.MfRho
|
||||
return MSigmaI*j
|
||||
|
||||
def j_from_e(prob,e):
|
||||
eqLocs = prob._eqLocs
|
||||
if eqLocs is 'FE':
|
||||
MSigma = prob.MeSigma
|
||||
elif eqLocs is 'EF':
|
||||
MSigma = prob.MfRhoI
|
||||
return MSigma*e
|
||||
|
||||
def b_from_h(prob,h):
|
||||
eqLocs = prob._eqLocs
|
||||
if eqLocs is 'FE':
|
||||
MMu = prob.MfMuiI
|
||||
elif eqLocs is 'EF':
|
||||
MMu = prob.MeMu
|
||||
return MMu*h
|
||||
|
||||
def h_from_b(prob,b):
|
||||
eqLocs = prob._eqLocs
|
||||
if eqLocs is 'FE':
|
||||
MMuI = prob.MfMui
|
||||
elif eqLocs is 'EF':
|
||||
MMuI = prob.MeMuI
|
||||
return MMuI*b
|
||||
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
from SimPEG import *
|
||||
from scipy.special import ellipk, ellipe
|
||||
from scipy.constants import mu_0, pi
|
||||
|
||||
def MagneticDipoleVectorPotential(srcLoc, obsLoc, component, moment=1., dipoleMoment=(0., 0., 1.), mu = mu_0):
|
||||
"""
|
||||
Calculate the vector potential of a set of magnetic dipoles
|
||||
at given locations 'ref. <http://en.wikipedia.org/wiki/Dipole#Magnetic_vector_potential>'
|
||||
|
||||
:param numpy.ndarray srcLoc: Location of the source(s) (x, y, z)
|
||||
:param numpy.ndarray,SimPEG.Mesh obsLoc: Where the potentials will be calculated (x, y, z) or a SimPEG Mesh
|
||||
:param str,list component: The component to calculate - 'x', 'y', or 'z' if an array, or grid type if mesh, can be a list
|
||||
:param numpy.ndarray dipoleMoment: The vector dipole moment
|
||||
:rtype: numpy.ndarray
|
||||
:return: The vector potential each dipole at each observation location
|
||||
"""
|
||||
#TODO: break this out!
|
||||
|
||||
if type(component) in [list, tuple]:
|
||||
out = range(len(component))
|
||||
for i, comp in enumerate(component):
|
||||
out[i] = MagneticDipoleVectorPotential(srcLoc, obsLoc, comp, dipoleMoment=dipoleMoment)
|
||||
return np.concatenate(out)
|
||||
|
||||
if isinstance(obsLoc, Mesh.BaseMesh):
|
||||
mesh = obsLoc
|
||||
assert component in ['Ex','Ey','Ez','Fx','Fy','Fz'], "Components must be in: ['Ex','Ey','Ez','Fx','Fy','Fz']"
|
||||
return MagneticDipoleVectorPotential(srcLoc, getattr(mesh,'grid'+component), component[1], dipoleMoment=dipoleMoment)
|
||||
|
||||
if component == 'x':
|
||||
dimInd = 0
|
||||
elif component == 'y':
|
||||
dimInd = 1
|
||||
elif component == 'z':
|
||||
dimInd = 2
|
||||
else:
|
||||
raise ValueError('Invalid component')
|
||||
|
||||
srcLoc = np.atleast_2d(srcLoc)
|
||||
obsLoc = np.atleast_2d(obsLoc)
|
||||
dipoleMoment = np.atleast_2d(dipoleMoment)
|
||||
|
||||
nEdges = obsLoc.shape[0]
|
||||
nSrc = srcLoc.shape[0]
|
||||
|
||||
m = np.array(dipoleMoment).repeat(nEdges, axis=0)
|
||||
A = np.empty((nEdges, nSrc))
|
||||
for i in range(nSrc):
|
||||
dR = obsLoc - srcLoc[i, np.newaxis].repeat(nEdges, axis=0)
|
||||
mCr = np.cross(m, dR)
|
||||
r = np.sqrt((dR**2).sum(axis=1))
|
||||
A[:, i] = +(mu/(4*pi)) * mCr[:,dimInd]/(r**3)
|
||||
if nSrc == 1:
|
||||
return A.flatten()
|
||||
return A
|
||||
|
||||
|
||||
def MagneticDipoleFields(srcLoc, obsLoc, component, moment=1., mu = mu_0):
|
||||
"""
|
||||
Calculate the vector potential of a set of magnetic dipoles
|
||||
at given locations 'ref. <http://en.wikipedia.org/wiki/Dipole#Magnetic_vector_potential>'
|
||||
|
||||
:param numpy.ndarray srcLoc: Location of the source(s) (x, y, z)
|
||||
:param numpy.ndarray obsLoc: Where the potentials will be calculated (x, y, z)
|
||||
:param str component: The component to calculate - 'x', 'y', or 'z'
|
||||
:param numpy.ndarray moment: The vector dipole moment (vertical)
|
||||
:rtype: numpy.ndarray
|
||||
:return: The vector potential each dipole at each observation location
|
||||
"""
|
||||
|
||||
if component=='x':
|
||||
dimInd = 0
|
||||
elif component=='y':
|
||||
dimInd = 1
|
||||
elif component=='z':
|
||||
dimInd = 2
|
||||
else:
|
||||
raise ValueError('Invalid component')
|
||||
|
||||
srcLoc = np.atleast_2d(srcLoc)
|
||||
obsLoc = np.atleast_2d(obsLoc)
|
||||
moment = np.atleast_2d(moment)
|
||||
|
||||
nFaces = obsLoc.shape[0]
|
||||
nSrc = srcLoc.shape[0]
|
||||
|
||||
m = np.array(moment).repeat(nFaces, axis=0)
|
||||
B = np.empty((nFaces, nSrc))
|
||||
for i in range(nSrc):
|
||||
dR = obsLoc - srcLoc[i, np.newaxis].repeat(nFaces, axis=0)
|
||||
r = np.sqrt((dR**2).sum(axis=1))
|
||||
if dimInd == 0:
|
||||
B[:, i] = +(mu/(4*pi)) /(r**3) * (3*dR[:,2]*dR[:,0]/r**2)
|
||||
elif dimInd == 1:
|
||||
B[:, i] = +(mu/(4*pi)) /(r**3) * (3*dR[:,2]*dR[:,1]/r**2)
|
||||
elif dimInd == 2:
|
||||
B[:, i] = +(mu/(4*pi)) /(r**3) * (3*dR[:,2]**2/r**2-1)
|
||||
else:
|
||||
raise Exception("Not Implemented")
|
||||
if nSrc == 1:
|
||||
return B.flatten()
|
||||
return B
|
||||
|
||||
|
||||
|
||||
def MagneticLoopVectorPotential(srcLoc, obsLoc, component, radius, mu=mu_0):
|
||||
"""
|
||||
Calculate the vector potential of horizontal circular loop
|
||||
at given locations
|
||||
|
||||
:param numpy.ndarray srcLoc: Location of the source(s) (x, y, z)
|
||||
:param numpy.ndarray,SimPEG.Mesh obsLoc: Where the potentials will be calculated (x, y, z) or a SimPEG Mesh
|
||||
:param str,list component: The component to calculate - 'x', 'y', or 'z' if an array, or grid type if mesh, can be a list
|
||||
:param numpy.ndarray I: Input current of the loop
|
||||
:param numpy.ndarray radius: radius of the loop
|
||||
:rtype: numpy.ndarray
|
||||
:return: The vector potential each dipole at each observation location
|
||||
"""
|
||||
|
||||
if type(component) in [list, tuple]:
|
||||
out = range(len(component))
|
||||
for i, comp in enumerate(component):
|
||||
out[i] = MagneticLoopVectorPotential(srcLoc, obsLoc, comp, radius, mu)
|
||||
return np.concatenate(out)
|
||||
|
||||
if isinstance(obsLoc, Mesh.BaseMesh):
|
||||
mesh = obsLoc
|
||||
assert component in ['Ex','Ey','Ez','Fx','Fy','Fz'], "Components must be in: ['Ex','Ey','Ez','Fx','Fy','Fz']"
|
||||
return MagneticLoopVectorPotential(srcLoc, getattr(mesh,'grid'+component), component[1], radius, mu)
|
||||
|
||||
srcLoc = np.atleast_2d(srcLoc)
|
||||
obsLoc = np.atleast_2d(obsLoc)
|
||||
|
||||
n = obsLoc.shape[0]
|
||||
nSrc = srcLoc.shape[0]
|
||||
|
||||
if component=='z':
|
||||
A = np.zeros((n, nSrc))
|
||||
if nSrc ==1:
|
||||
return A.flatten()
|
||||
return A
|
||||
|
||||
else:
|
||||
|
||||
A = np.zeros((n, nSrc))
|
||||
for i in range (nSrc):
|
||||
x = obsLoc[:, 0] - srcLoc[i, 0]
|
||||
y = obsLoc[:, 1] - srcLoc[i, 1]
|
||||
z = obsLoc[:, 2] - srcLoc[i, 2]
|
||||
r = np.sqrt(x**2 + y**2)
|
||||
m = (4 * radius * r) / ((radius + r)**2 + z**2)
|
||||
m[m > 1.] = 1.
|
||||
# m might be slightly larger than 1 due to rounding errors
|
||||
# but ellipke requires 0 <= m <= 1
|
||||
K = ellipk(m)
|
||||
E = ellipe(m)
|
||||
ind = (r > 0) & (m < 1)
|
||||
# % 1/r singular at r = 0 and K(m) singular at m = 1
|
||||
Aphi = np.zeros(n)
|
||||
# % Common factor is (mu * I) / pi with I = 1 and mu = 4e-7 * pi.
|
||||
Aphi[ind] = 4e-7 / np.sqrt(m[ind]) * np.sqrt(radius / r[ind]) *((1. - m[ind] / 2.) * K[ind] - E[ind])
|
||||
if component == 'x':
|
||||
A[ind, i] = Aphi[ind] * (-y[ind] / r[ind] )
|
||||
elif component == 'y':
|
||||
A[ind, i] = Aphi[ind] * ( x[ind] / r[ind] )
|
||||
else:
|
||||
raise ValueError('Invalid component')
|
||||
|
||||
if nSrc == 1:
|
||||
return A.flatten()
|
||||
return A
|
||||
|
||||
if __name__ == '__main__':
|
||||
from SimPEG import Mesh
|
||||
import matplotlib.pyplot as plt
|
||||
cs = 20
|
||||
ncx, ncy, ncz = 41, 41, 40
|
||||
hx = np.ones(ncx)*cs
|
||||
hy = np.ones(ncy)*cs
|
||||
hz = np.ones(ncz)*cs
|
||||
mesh = Mesh.TensorMesh([hx, hy, hz], 'CCC')
|
||||
srcLoc = np.r_[0., 0., 0.]
|
||||
Ax = MagneticLoopVectorPotential(srcLoc, mesh.gridEx, 'x', 200)
|
||||
Ay = MagneticLoopVectorPotential(srcLoc, mesh.gridEy, 'y', 200)
|
||||
Az = MagneticLoopVectorPotential(srcLoc, mesh.gridEz, 'z', 200)
|
||||
A = np.r_[Ax, Ay, Az]
|
||||
B0 = mesh.edgeCurl*A
|
||||
J0 = mesh.edgeCurl.T*B0
|
||||
|
||||
# mesh.plotImage(A, vType = 'Ex')
|
||||
# mesh.plotImage(A, vType = 'Ey')
|
||||
|
||||
mesh.plotImage(B0, vType = 'Fx')
|
||||
mesh.plotImage(B0, vType = 'Fy')
|
||||
mesh.plotImage(B0, vType = 'Fz')
|
||||
|
||||
# # mesh.plotImage(J0, vType = 'Ex')
|
||||
# mesh.plotImage(J0, vType = 'Ey')
|
||||
# mesh.plotImage(J0, vType = 'Ez')
|
||||
|
||||
plt.show()
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# import Sources
|
||||
# import Ana
|
||||
# import Solver
|
||||
import EMUtils
|
||||
import SrcUtils
|
||||
@@ -0,0 +1,6 @@
|
||||
# from EM import *
|
||||
import TDEM
|
||||
import FDEM
|
||||
import Base
|
||||
import Analytics
|
||||
import Utils
|
||||
Reference in New Issue
Block a user