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:
Rowan Cockett
2015-11-04 09:59:11 -08:00
37 changed files with 5141 additions and 0 deletions
+153
View File
@@ -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
+98
View File
@@ -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)
+12
View File
@@ -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
+3
View File
@@ -0,0 +1,3 @@
from TDEM import hzAnalyticDipoleT
from FDEM import hzAnalyticDipoleF
from FDEMcasing import *
+186
View File
@@ -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
+97
View File
@@ -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()
+1
View File
@@ -0,0 +1 @@
import CylInversion
+661
View File
@@ -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
+374
View File
@@ -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)
+491
View File
@@ -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.')
+3
View File
@@ -0,0 +1,3 @@
from SurveyFDEM import *
from FDEM import BaseFDEMProblem, ProblemFDEM_e, ProblemFDEM_b, ProblemFDEM_j, ProblemFDEM_h
from FieldsFDEM import *
+155
View File
@@ -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)
+162
View File
@@ -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
+356
View File
@@ -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
+3
View File
@@ -0,0 +1,3 @@
from SurveyTDEM import * #SurveyTDEM, RxTDEM, SrcTDEM
from BaseTDEM import BaseTDEMProblem, FieldsTDEM
from TDEM_b import ProblemTDEM_b
+49
View File
@@ -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
+203
View File
@@ -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()
+5
View File
@@ -0,0 +1,5 @@
# import Sources
# import Ana
# import Solver
import EMUtils
import SrcUtils
+6
View File
@@ -0,0 +1,6 @@
# from EM import *
import TDEM
import FDEM
import Base
import Analytics
import Utils