diff --git a/.bumpversion.cfg b/.bumpversion.cfg index ea37cced..84469e3f 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,4 +1,4 @@ [bumpversion] -current_version = 0.1.9 +current_version = 0.1.10 files = setup.py SimPEG/__init__.py docs/conf.py diff --git a/.travis.yml b/.travis.yml index 4b5e47ff..a4614a6b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,6 +18,7 @@ env: - TEST_DIR="tests/mesh tests/base tests/utils" - TEST_DIR=tests/em/fdem/inverse/derivs - TEST_DIR=tests/em/tdem + - TEST_DIR=tests/dcip - TEST_DIR=tests/flow - TEST_DIR=tests/mt - TEST_DIR=tests/examples diff --git a/README.rst b/README.rst index 1c645334..c9b30a78 100644 --- a/README.rst +++ b/README.rst @@ -25,6 +25,10 @@ SimPEG :target: https://coveralls.io/r/simpeg/simpeg?branch=master :alt: Coverage status +.. image:: http://img.shields.io/badge/GITTER-JOIN_CHAT-brightgreen.svg?style=flat-square + :alt: gitter chat room at https://gitter.im/simpeg/simpeg + :target: https://gitter.im/simpeg/simpeg + Simulation and Parameter Estimation in Geophysics - A python package for simulation and gradient based parameter estimation in the context of geophysical applications. The vision is to create a package for finite volume simulation with applications to geophysical imaging and subsurface flow. To enable the understanding of the many different components, this package has the following features: diff --git a/SimPEG/DCIP/BaseDC.py b/SimPEG/DCIP/BaseDC.py new file mode 100644 index 00000000..475c621e --- /dev/null +++ b/SimPEG/DCIP/BaseDC.py @@ -0,0 +1,292 @@ +from SimPEG import * + +class FieldsDC_CC(Problem.Fields): + knownFields = {'phi_sol':'CC'} + aliasFields = { + 'phi' : ['phi_sol','CC','_phi'], + 'e' : ['phi_sol','F','_e'], + 'j' : ['phi_sol','F','_j'] + } + + def __init__(self,mesh,survey,**kwargs): + super(FieldsDC_CC, self).__init__(mesh, survey, **kwargs) + + def startup(self): + self._cellGrad = self.survey.prob.mesh.cellGrad + self._Mfinv = self.survey.prob.mesh.getFaceInnerProduct(invMat=True) + + def _phi(self, phi_sol, srcList): + phi = phi_sol + # for i, src in enumerate(srcList): + # phi_p = src.phi_p(self.survey.prob) + # if phi_p is not None: + # phi[:,i] += phi_p + return phi + + def _e(self, phi_sol, srcList): + e = -self._cellGrad*phi_sol + # for i, src in enumerate(srcList): + # e_p = src.e_p(self.survey.prob) + # if e_p is not None: + # e[:,i] += e_p + return e + + def _j(self, phi_sol, srcList): + + j = -self._Mfinv*self.survey.prob.Msig*self._cellGrad*phi_sol + # for i, src in enumerate(srcList): + # j_p = src.j_p(self.survey.prob) + # if j_p is not None: + # j[:,i] += j_p + return j + + + +class SrcDipole(Survey.BaseSrc): + """A dipole source, locA and locB are moved to the closest cell-centers""" + + current = 1 + loc = None + # _rhsDict = None + + def __init__(self, rxList, locA, locB, **kwargs): + self.loc = (locA, locB) + super(SrcDipole, self).__init__(rxList, **kwargs) + + def eval(self, prob): + # Recompute rhs + # if getattr(self, '_rhsDict', None) is None: + # self._rhsDict = {} + # if mesh not in self._rhsDict: + pts = [self.loc[0], self.loc[1]] + inds = Utils.closestPoints(prob.mesh, pts) + q = np.zeros(prob.mesh.nC) + q[inds] = - self.current * ( np.r_[1., -1.] / prob.mesh.vol[inds] ) + # self._rhsDict[mesh] = q + # return self._rhsDict[mesh] + return q + + +class RxDipole(Survey.BaseRx): + """A dipole source, locA and locB are moved to the closest cell-centers""" + def __init__(self, locsM, locsN, **kwargs): + locs = (locsM, locsN) + assert locsM.shape == locsN.shape, 'locs must be the same shape.' + super(RxDipole, self).__init__(locs, 'dipole', storeProjections=False, **kwargs) + + @property + def nD(self): + """Number of data in the receiver.""" + return self.locs[0].shape[0] + + def getP(self, mesh): + P0 = mesh.getInterpolationMat(self.locs[0], self.projGLoc) + P1 = mesh.getInterpolationMat(self.locs[1], self.projGLoc) + return P0 - P1 + + +class SurveyDC(Survey.BaseSurvey): + """ + **SurveyDC** + + Geophysical DC resistivity data. + + """ + uncert = None + def __init__(self, srcList, **kwargs): + self.srcList = srcList + Survey.BaseSurvey.__init__(self, **kwargs) + # self._rhsDict = {} + self._Ps = {} + + def eval(self, u): + """ + Predicted data. + + .. math:: + d_\\text{pred} = Pu(m) + """ + P = self.getP(self.prob.mesh) + return P*mkvc(u[self.srcList, 'phi_sol']) + + def getP(self, mesh): + if mesh in self._Ps: + return self._Ps[mesh] + + P_src = [sp.vstack([rx.getP(mesh) for rx in src.rxList]) for src in self.srcList] + + self._Ps[mesh] = sp.block_diag(P_src) + return self._Ps[mesh] + + +class ProblemDC_CC(Problem.BaseProblem): + """ + **ProblemDC** + + Geophysical DC resistivity problem. + + """ + + surveyPair = SurveyDC + Solver = Solver + fieldsPair = FieldsDC_CC + Ainv = None + + def __init__(self, mesh, **kwargs): + Problem.BaseProblem.__init__(self, mesh) + self.mesh.setCellGradBC('neumann') + Utils.setKwargs(self, **kwargs) + + + deleteTheseOnModelUpdate = ['_A', '_Msig', '_dMdsig'] + + @property + def Msig(self): + if getattr(self, '_Msig', None) is None: + sigma = self.curModel.transform + Av = self.mesh.aveF2CC + self._Msig = Utils.sdiag(1/(self.mesh.dim * Av.T * (1/sigma))) + return self._Msig + + @property + def dMdsig(self): + if getattr(self, '_dMdsig', None) is None: + sigma = self.curModel.transform + Av = self.mesh.aveF2CC + dMdprop = self.mesh.dim * Utils.sdiag(self.Msig.diagonal()**2) * Av.T * Utils.sdiag(1./sigma**2) + self._dMdsig = lambda Gu: Utils.sdiag(Gu) * dMdprop + return self._dMdsig + + @property + def A(self): + """ + Makes the matrix A(m) for the DC resistivity problem. + + :param numpy.array m: model + :rtype: scipy.csc_matrix + :return: A(m) + + .. math:: + c(m,u) = A(m)u - q = G\\text{sdiag}(M(mT(m)))Du - q = 0 + + Where M() is the mass matrix and mT is the model transform. + """ + if getattr(self, '_A', None) is None: + D = self.mesh.faceDiv + G = self.mesh.cellGrad + self._A = D*self.Msig*G + # Remove the null space from the matrix. + self._A[0,0] /= self.mesh.vol[0] + self._A = self._A.tocsc() + return self._A + + def getRHS(self): + # if self.mesh not in self._rhsDict: + RHS = np.array([src.eval(self) for src in self.survey.srcList]).T + # self._rhsDict[mesh] = RHS + # return self._rhsDict[mesh] + return RHS + + def fields(self, m): + + F = self.fieldsPair(self.mesh, self.survey) + self.curModel = m + A = self.A + self.Ainv = self.Solver(A, **self.solverOpts) + RHS = self.getRHS() + Phi = self.Ainv * RHS + Srcs = self.survey.srcList + F[Srcs, 'phi_sol'] = Phi + + return F + + def Jvec(self, m, v, f=None): + """ + :param numpy.array m: model + :param numpy.array v: vector to multiply + :param Fields f: fields + :rtype: numpy.array + :return: Jv + + .. math:: + c(m,u) = A(m)u - q = G\\text{sdiag}(M(mT(m)))Du - q = 0 + + \\nabla_u (A(m)u - q) = A(m) + + \\nabla_m (A(m)u - q) = G\\text{sdiag}(Du)\\nabla_m(M(mT(m))) + + Where M() is the mass matrix and mT is the model transform. + + .. math:: + J = - P \left( \\nabla_u c(m, u) \\right)^{-1} \\nabla_m c(m, u) + + J(v) = - P ( A(m)^{-1} ( G\\text{sdiag}(Du)\\nabla_m(M(mT(m))) v ) ) + """ + # Set current model; clear dependent property $\mathbf{A(m)}$ + self.curModel = m + sigma = self.curModel.transform # $\sigma = \mathcal{M}(\m)$ + if f is None: + # Run forward simulation if $u$ not provided + f = self.fields(self.curModel) + u = f[self.survey.srcList, 'phi_sol'] + + D = self.mesh.faceDiv + G = self.mesh.cellGrad + # Derivative of model transform, $\deriv{\sigma}{\m}$ + dsigdm_x_v = self.curModel.transformDeriv * v + + # Take derivative of $C(m,u)$ w.r.t. $m$ + dCdm_x_v = np.empty_like(u) + # loop over fields for each source + for i in range(self.survey.nSrc): + # Derivative of inner product, $\left(\mathbf{M}_{1/\sigma}^f\right)^{-1}$ + dAdsig = D * self.dMdsig( G * u[:,i] ) + dCdm_x_v[:, i] = dAdsig * dsigdm_x_v + + # Take derivative of $C(m,u)$ w.r.t. $u$ + dA_du = self.A + # Solve for $\deriv{u}{m}$ + # dCdu_inv = self.Solver(dCdu, **self.solverOpts) + if self.Ainv is None: + self.Ainv = self.Solver(dA_du, **self.solverOpts) + + P = self.survey.getP(self.mesh) + Jv = - P * mkvc( self.Ainv * dCdm_x_v ) + return Jv + + def Jtvec(self, m, v, f=None): + + self.curModel = m + sigma = self.curModel.transform # $\sigma = \mathcal{M}(\m)$ + if f is None: + # Run forward simulation if $f$ not provided + f = self.fields(self.curModel) + u = f[self.survey.srcList, 'phi_sol'] + + shp = u.shape + P = self.survey.getP(self.mesh) + PT_x_v = (P.T*v).reshape(shp, order='F') + + D = self.mesh.faceDiv + G = self.mesh.cellGrad + dA_du = self.A + mT_dm = self.mapping.deriv(m) + + # We probably always need this due to the linesearch .. (?) + self.Ainv = self.Solver(dA_du.T, **self.solverOpts) + # if self.Ainv is None: + # self.Ainv = self.Solver(dCdu, **self.solverOpts) + + w = self.Ainv * PT_x_v + + Jtv = 0 + for i, ui in enumerate(u.T): # loop over each column + Jtv += self.dMdsig( G * ui ).T * ( D.T * w[:,i] ) + + Jtv = - mT_dm.T * ( Jtv ) + return Jtv + + + + + diff --git a/SimPEG/DCIP/BaseIP.py b/SimPEG/DCIP/BaseIP.py new file mode 100644 index 00000000..a18b5a47 --- /dev/null +++ b/SimPEG/DCIP/BaseIP.py @@ -0,0 +1,182 @@ +from SimPEG import * +from BaseDC import SurveyDC, FieldsDC_CC + +class SurveyIP(SurveyDC): + """ + **SurveyDC** + + Geophysical DC resistivity data. + + """ + + def __init__(self, srcList, **kwargs): + self.srcList = srcList + Survey.BaseSurvey.__init__(self, **kwargs) + self._Ps = {} + + def dpred(self, m, f=None): + """ + Predicted data. + + .. math:: + d_\\text{pred} = Pf(m) + """ + + return self.prob.forward(m) + + +class ProblemIP(Problem.BaseProblem): + """ + **ProblemIP** + + Geophysical IP resistivity problem. + + """ + + surveyPair = SurveyDC + Solver = Solver + sigma = None + Ainv = None + u = None + + def __init__(self, mesh, **kwargs): + Problem.BaseProblem.__init__(self, mesh) + self.mesh.setCellGradBC('neumann') + Utils.setKwargs(self, **kwargs) + + # deleteTheseOnModelUpdate = ['_A', '_Msig', '_dMdsig'] + + @property + def Msig(self): + if getattr(self, '_Msig', None) is None: + # sigma = self.curModel.transform + sigma = self.sigma + Av = self.mesh.aveF2CC + self._Msig = Utils.sdiag(1/(self.mesh.dim * Av.T * (1/sigma))) + return self._Msig + + @property + def dMdsig(self): + if getattr(self, '_dMdsig', None) is None: + # sigma = self.curModel.transform + sigma = self.sigma + Av = self.mesh.aveF2CC + dMdprop = self.mesh.dim * Utils.sdiag(self.Msig.diagonal()**2) * Av.T * Utils.sdiag(1./sigma**2) + self._dMdsig = lambda Gu: Utils.sdiag(Gu) * dMdprop + return self._dMdsig + + @property + def A(self): + """ + Makes the matrix A(m) for the DC resistivity problem. + + :param numpy.array m: model + :rtype: scipy.csc_matrix + :return: A(m) + + .. math:: + c(m,u) = A(m)u - q = G\\text{sdiag}(M(mT(m)))Du - q = 0 + + Where M() is the mass matrix and mT is the model transform. + """ + if getattr(self, '_A', None) is None: + D = self.mesh.faceDiv + G = self.mesh.cellGrad + self._A = D*self.Msig*G + # Remove the null space from the matrix. + self._A[-1,-1] /= self.mesh.vol[-1] + self._A = self._A.tocsc() + return self._A + + def getRHS(self): + # if self.mesh not in self._rhsDict: + RHS = np.array([src.eval(self) for src in self.survey.srcList]).T + # self._rhsDict[mesh] = RHS + # return self._rhsDict[mesh] + return RHS + + def fields(self, m): + if self.u is None: + A = self.A + if self.Ainv == None: + self.Ainv = self.Solver(A, **self.solverOpts) + Q = self.getRHS() + self.u = self.Ainv * Q + return self.u + + def forward(self, m, u=None): + # Set current model; clear dependent property $\mathbf{A(m)}$ + self.curModel = m + # sigma = self.curModel.transform # $\sigma = \mathcal{M}(\m)$ + sigma = self.sigma + if self.u is None: + # Run forward simulation if $u$ not provided + u = self.fields(sigma) + + shp = (self.mesh.nC, self.survey.nSrc) + u = self.u.reshape(shp, order='F') + + D = self.mesh.faceDiv + G = self.mesh.cellGrad + # Derivative of model transform, $\deriv{\sigma}{\m}$ + # dsigdm_x_v = self.curModel.transformDeriv * v + + dsigdm_x_v = Utils.sdiag(sigma) * self.curModel.transformDeriv * m + + # Take derivative of $C(m,u)$ w.r.t. $m$ + dCdm_x_v = np.empty_like(u) + # loop over fields for each source + for i in range(self.survey.nSrc): + # Derivative of inner product, $\left(\mathbf{M}_{1/\sigma}^f\right)^{-1}$ + dAdsig = D * self.dMdsig( G * u[:,i] ) + dCdm_x_v[:, i] = dAdsig * dsigdm_x_v + + # Take derivative of $C(m,u)$ w.r.t. $u$ + + if self.Ainv == None: + self.Ainv = self.Solver(A, **self.solverOpts) + + # dCdu = self.A + # Solve for $\deriv{u}{m}$ + # dCdu_inv = self.Solver(dCdu, **self.solverOpts) + P = self.survey.getP(self.mesh) + J_x_v = - P * mkvc( self.Ainv * dCdm_x_v ) + return -J_x_v + + def Jvec(self, m, v, f=None): + return self.forward(v) + + def Jtvec(self, m, v, f=None): + + self.curModel = m + # sigma = self.curModel.transform # $\sigma = \mathcal{M}(\m)$ + sigma = self.sigma + if self.u is None: + u = self.fields(sigma) + else: + u = self.u + shp = (self.mesh.nC, self.survey.nSrc) + u = u.reshape(shp, order='F') + P = self.survey.getP(self.mesh) + PT_x_v = (P.T*v).reshape(shp, order='F') + + D = self.mesh.faceDiv + G = self.mesh.cellGrad + A = self.A + mT_dm = Utils.sdiag(sigma)*self.mapping.deriv(m) + # mT_dm = self.mapping.deriv(m) + + # dCdu = A.T + # Ainv = self.Solver(dCdu, **self.solverOpts) + # if self.Ainv == None: + self.Ainv = self.Solver(A.T, **self.solverOpts) + + w = self.Ainv * PT_x_v + + Jtv = 0 + for i, ui in enumerate(u.T): # loop over each column + Jtv += self.dMdsig( G * ui ).T * ( D.T * w[:,i] ) + + Jtv = - mT_dm.T * ( Jtv ) + return -Jtv + diff --git a/SimPEG/DCIP/DCIPUtils.py b/SimPEG/DCIP/DCIPUtils.py new file mode 100644 index 00000000..97c30e89 --- /dev/null +++ b/SimPEG/DCIP/DCIPUtils.py @@ -0,0 +1,1082 @@ +from SimPEG import np +import BaseDC as DC +import BaseDC as IP + +def getActiveindfromTopo(mesh, topo): +# def genActiveindfromTopo(mesh, topo): + """ + Get active indices from topography + """ + from scipy.interpolate import NearestNDInterpolator + if mesh.dim==3: + nCxy = mesh.nCx*mesh.nCy + Zcc = mesh.gridCC[:,2].reshape((nCxy, mesh.nCz), order='F') + Ftopo = NearestNDInterpolator(topo[:,:2], topo[:,2]) + XY = Utils.ndgrid(mesh.vectorCCx, mesh.vectorCCy) + XY.shape + topo = Ftopo(XY) + actind = [] + for ixy in range(nCxy): + actind.append(topo[ixy] <= Zcc[ixy,:]) + else: + raise NotImplementedError("Only 3D is working") + + return Utils.mkvc(np.vstack(actind)) + +def gettopoCC(mesh, airind): +# def gettopoCC(mesh, airind): + """ + Get topography from active indices of mesh. + """ + mesh2D = Mesh.TensorMesh([mesh.hx, mesh.hy], mesh.x0[:2]) + zc = mesh.gridCC[:,2] + AIRIND = airind.reshape((mesh.vnC[0]*mesh.vnC[1],mesh.vnC[2]), order='F') + ZC = zc.reshape((mesh.vnC[0]*mesh.vnC[1], mesh.vnC[2]), order='F') + topo = np.zeros(ZC.shape[0]) + topoCC = np.zeros(ZC.shape[0]) + for i in range(ZC.shape[0]): + ind = np.argmax(ZC[i,:][~AIRIND[i,:]]) + topo[i] = ZC[i,:][~AIRIND[i,:]].max() + mesh.hz[~AIRIND[i,:]][ind]*0.5 + topoCC[i] = ZC[i,:][~AIRIND[i,:]].max() + XY = Utils.ndgrid(mesh.vectorCCx, mesh.vectorCCy) + return mesh2D, topoCC + +def readUBC_DC3Dobstopo(filename,mesh,topo,probType="CC"): + """ + Seogi's personal readObs function. + + """ + text_file = open(filename, "r") + lines = text_file.readlines() + text_file.close() + SRC = [] + DATA = [] + srcLists = [] + isrc = 0 + # airind = getActiveindfromTopo(mesh, topo) + # mesh2D, topoCC = gettopoCC(mesh, airind) + + for line in lines: + if "!" in line.split(): continue + elif line == '\n': continue + elif line == ' \n': continue + temp = map(float, line.split()) + # Read a line for the current electrode + if len(temp) == 5: # SRC: Only X and Y are provided (assume no topography) + #TODO consider topography and assign the closest cell center in the earth + if isrc == 0: + DATA_temp = [] + else: + DATA.append(np.asarray(DATA_temp)) + DATA_temp = [] + indM = Utils.closestPoints(mesh2D, DATA[isrc-1][:,1:3]) + indN = Utils.closestPoints(mesh2D, DATA[isrc-1][:,3:5]) + rx = DCIP.RxDipole(np.c_[DATA[isrc-1][:,1:3], topoCC[indM]], np.c_[DATA[isrc-1][:,3:5], topoCC[indN]]) + temp = np.asarray(temp) + if [SRC[isrc-1][0], SRC[isrc-1][1]] == [SRC[isrc-1][2], SRC[isrc-1][3]]: + indA = Utils.closestPoints(mesh2D, [SRC[isrc-1][0], SRC[isrc-1][1]]) + tx = DCIP.SrcDipole([rx], [SRC[isrc-1][0], SRC[isrc-1][1], topoCC[indA]],[mesh.vectorCCx.max(), mesh.vectorCCy.max(), topoCC[-1]]) + else: + indA = Utils.closestPoints(mesh2D, [SRC[isrc-1][0], SRC[isrc-1][1]]) + indB = Utils.closestPoints(mesh2D, [SRC[isrc-1][2], SRC[isrc-1][3]]) + tx = DCIP.SrcDipole([rx], [SRC[isrc-1][0], SRC[isrc-1][1], topoCC[indA]],[SRC[isrc-1][2], SRC[isrc-1][3], topoCC[indB]]) + srcLists.append(tx) + SRC.append(temp) + isrc += 1 + elif len(temp) == 7: # SRC: X, Y and Z are provided + SRC.append(temp) + isrc += 1 + elif len(temp) == 6: # + DATA_temp.append(np.r_[isrc, np.asarray(temp)]) + elif len(temp) > 7: + DATA_temp.append(np.r_[isrc, np.asarray(temp)]) + + DATA.append(np.asarray(DATA_temp)) + DATA_temp = [] + indM = Utils.closestPoints(mesh2D, DATA[isrc-1][:,1:3]) + indN = Utils.closestPoints(mesh2D, DATA[isrc-1][:,3:5]) + rx = DCIP.RxDipole(np.c_[DATA[isrc-1][:,1:3], topoCC[indM]], np.c_[DATA[isrc-1][:,3:5], topoCC[indN]]) + temp = np.asarray(temp) + if [SRC[isrc-1][0], SRC[isrc-1][1]] == [SRC[isrc-1][2], SRC[isrc-1][3]]: + indA = Utils.closestPoints(mesh2D, [SRC[isrc-1][0], SRC[isrc-1][1]]) + tx = DCIP.SrcDipole([rx], [SRC[isrc-1][0], SRC[isrc-1][1], topoCC[indA]],[mesh.vectorCCx.max(), mesh.vectorCCy.max(), topoCC[-1]]) + else: + indA = Utils.closestPoints(mesh2D, [SRC[isrc-1][0], SRC[isrc-1][1]]) + indB = Utils.closestPoints(mesh2D, [SRC[isrc-1][2], SRC[isrc-1][3]]) + tx = DCIP.SrcDipole([rx], [SRC[isrc-1][0], SRC[isrc-1][1], topoCC[indA]],[SRC[isrc-1][2], SRC[isrc-1][3], topoCC[indB]]) + srcLists.append(tx) + text_file.close() + survey = DCIP.SurveyDC(srcLists) + + # Do we need this? + SRC = np.asarray(SRC) + DATA = np.vstack(DATA) + survey.dobs = np.vstack(DATA)[:,-2] + + + return {'DCsurvey':survey, 'airind':airind, 'topoCC':topoCC, 'SRC':SRC} + +def readUBC_DC2DModel(fileName): + """ + Read UBC GIF 2DTensor model and generate 2D Tensor model in simpeg + + Input: + :param fileName, path to the UBC GIF 2D model file + + Output: + :param SimPEG TensorMesh 2D object + :return + + Created on Thu Nov 12 13:14:10 2015 + + @author: dominiquef + + """ + from SimPEG import np, mkvc + + # Open fileand skip header... assume that we know the mesh already + obsfile = np.genfromtxt(fileName,delimiter=' \n',dtype=np.str,comments='!') + + dim = np.array(obsfile[0].split(),dtype=float) + + temp = np.array(obsfile[1].split(),dtype=float) + + if len(temp) > 1: + model = np.zeros(dim) + + for ii in range(len(obsfile)-1): + mm = np.array(obsfile[ii+1].split(),dtype=float) + model[:,ii] = mm + + model = model[:,::-1] + + else: + + if len(obsfile[1:])==1: + mm = np.array(obsfile[1:].split(),dtype=float) + + else: + mm = np.array(obsfile[1:],dtype=float) + + # Permute the second dimension to flip the order + model = mm.reshape(dim[1],dim[0]) + + model = model[::-1,:] + model = np.transpose(model, (1, 0)) + + model = mkvc(model) + + + return model + +def plot_pseudoSection(DCsurvey, axs, stype='dpdp', dtype="appc", clim=None, cblabel=True, axlabel = True, colorbar = True, contour = None): + """ + Read list of 2D tx-rx location and plot a speudo-section of apparent + resistivity. + + Assumes flat topo for now... + + Input: + :param d2D, z0 + :switch stype -> Either 'pdp' (pole-dipole) | 'dpdp' (dipole-dipole) + :switch dtype=-> Either 'appr' (app. res) | 'appc' (app. con) | 'volt' (potential) + Output: + :figure scatter plot overlayed on image + + Edited Feb 17th, 2016 + + @author: dominiquef + + """ + from SimPEG import np + from scipy.interpolate import griddata + import pylab as plt + + # Pre-allocate + midx = [] + midz = [] + rho = [] + count = 0 # Counter for data + for ii in range(DCsurvey.nSrc): + + Tx = DCsurvey.srcList[ii].loc + Rx = DCsurvey.srcList[ii].rxList[0].locs + + nD = DCsurvey.srcList[ii].rxList[0].nD + + data = DCsurvey.dobs[count:count+nD] + count += nD + + # Get distances between each poles A-B-M-N + MA = np.abs(Tx[0][0] - Rx[0][:,0]) + MB = np.abs(Tx[1][0] - Rx[0][:,0]) + NB = np.abs(Tx[1][0] - Rx[1][:,0]) + NA = np.abs(Tx[0][0] - Rx[1][:,0]) + MN = np.abs(Rx[1][:,0] - Rx[0][:,0]) + + # Create mid-point location + Cmid = (Tx[0][0] + Tx[1][0])/2 + Pmid = (Rx[0][:,0] + Rx[1][:,0])/2 + + # Change output for dtype + if dtype == 'volt': + + rho = np.hstack([rho,data]) + + else: + + # Compute pant leg of apparent rho + if stype == 'pdp': + + leg = data * 2*np.pi * MA * ( MA + MN ) / MN + + elif stype == 'dpdp': + + leg = data * 2*np.pi / ( 1/MA - 1/MB - 1/NB + 1/NA ) + + else: + print """dtype must be 'pdp'(pole-dipole) | 'dpdp' (dipole-dipole) """ + break + + + if dtype == 'appc': + + leg = np.log10(abs(1./leg)) + rho = np.hstack([rho,leg]) + + elif dtype == 'appr': + + leg = np.log10(abs(leg)) + rho = np.hstack([rho,leg]) + + else: + print """dtype must be 'appr' | 'appc' | 'volt' """ + break + + midx = np.hstack([midx, ( Cmid + Pmid )/2 ]) + midz = np.hstack([midz, -np.abs(Cmid-Pmid)/2 + (Tx[0][2] + Tx[1][2])/2 ]) + + # Grid points + grid_x, grid_z = np.mgrid[np.min(midx):np.max(midx), np.min(midz):np.max(midz)] + grid_rho = griddata(np.c_[midx,midz], rho.T, (grid_x, grid_z), method='linear') + + # Scale the color scheme + if clim == None: + vmin, vmax = rho.min(), rho.max() + else: + vmin, vmax = clim[0], clim[1] + + # Plot data + grid_rho = np.ma.masked_where(np.isnan(grid_rho), grid_rho) + ph = plt.pcolormesh(grid_x[:,0],grid_z[0,:],grid_rho.T, vmin = vmin, vmax = vmax) + plt.gca().tick_params(axis='both', which='major', labelsize=8) + + if contour is not None: + plt.contour(grid_x,grid_z,grid_rho,levels = contour,colors = 'r', vmin = vmin, vmax = vmax) + + # Add scatter points + axs.scatter(midx,midz,s=10,c=rho.T, vmin = vmin, vmax = vmax) + + if colorbar: + + if dtype == 'volt': + cbar = plt.colorbar(ph, ax = axs, format="%4.1f",fraction=0.04,orientation="horizontal") + + else: + cbar = plt.colorbar(ph, ax = axs, format="$10^{%.1f}$",fraction=0.04,orientation="horizontal") + + cmin,cmax = cbar.get_clim() + ticks = np.linspace(cmin,cmax,3) + cbar.set_ticks(ticks) + cbar.ax.tick_params(labelsize=10) + + if cblabel: + if dtype == 'appc': + cbar.set_label("App.Cond",size=12) + elif dtype == 'appr': + cbar.set_label("App.Res.",size=12) + elif dtype == 'volt': + cbar.set_label("Potential (V)",size=12) + + + + if not axlabel: + axs.set_xticklabels([]) + axs.set_yticklabels([]) + + plt.gca().set_aspect('equal', adjustable='box') + + + + return ph + +def gen_DCIPsurvey(endl, mesh, stype, a, b, n): + """ + Load in endpoints and survey specifications to generate Tx, Rx location + stations. + + Assumes flat topo for now... + + Input: + :param endl -> input endpoints [x1, y1, z1, x2, y2, z2] + :object mesh -> SimPEG mesh object + :switch stype -> "dpdp" (dipole-dipole) | "pdp" (pole-dipole) | 'gradient' + : param a, n -> pole seperation, number of rx dipoles per tx + + Output: + :param Tx, Rx -> List objects for each tx location + Lines: P1x, P1y, P1z, P2x, P2y, P2z + + Created on Wed December 9th, 2015 + + @author: dominiquef + !! Require clean up to deal with DCsurvey + """ + + from SimPEG import np + + def xy_2_r(x1,x2,y1,y2): + r = np.sqrt( np.sum((x2 - x1)**2 + (y2 - y1)**2) ) + return r + + ## Evenly distribute electrodes and put on surface + # Mesure survey length and direction + dl_len = xy_2_r(endl[0,0],endl[1,0],endl[0,1],endl[1,1]) + + dl_x = ( endl[1,0] - endl[0,0] ) / dl_len + dl_y = ( endl[1,1] - endl[0,1] ) / dl_len + + nstn = np.floor( dl_len / a ) + + # Compute discrete pole location along line + stn_x = endl[0,0] + np.array(range(int(nstn)))*dl_x*a + stn_y = endl[0,1] + np.array(range(int(nstn)))*dl_y*a + + # Create line of P1 locations + M = np.c_[stn_x, stn_y, np.ones(nstn).T*mesh.vectorNz[-1]] + + # Create line of P2 locations + N = np.c_[stn_x+a*dl_x, stn_y+a*dl_y, np.ones(nstn).T*mesh.vectorNz[-1]] + + ## Build list of Tx-Rx locations depending on survey type + # Dipole-dipole: Moving tx with [a] spacing -> [AB a MN1 a MN2 ... a MNn] + # Pole-dipole: Moving pole on one end -> [A a MN1 a MN2 ... MNn a B] + Tx = [] + Rx = [] + SrcList = [] + + + if stype != 'gradient': + + for ii in range(0, int(nstn)-1): + + + if stype == 'dpdp': + tx = np.c_[M[ii,:],N[ii,:]] + elif stype == 'pdp': + tx = np.c_[M[ii,:],M[ii,:]] + + # Rx.append(np.c_[M[ii+1:indx,:],N[ii+1:indx,:]]) + + # Current elctrode seperation + AB = xy_2_r(tx[0,1],endl[1,0],tx[1,1],endl[1,1]) + + # Number of receivers to fit + nstn = np.min([np.floor( (AB - b) / a ) , n]) + + # Check if there is enough space, else break the loop + if nstn <= 0: + continue + + # Compute discrete pole location along line + stn_x = N[ii,0] + dl_x*b + np.array(range(int(nstn)))*dl_x*a + stn_y = N[ii,1] + dl_y*b + np.array(range(int(nstn)))*dl_y*a + + # Create receiver poles + # Create line of P1 locations + P1 = np.c_[stn_x, stn_y, np.ones(nstn).T*mesh.vectorNz[-1]] + + # Create line of P2 locations + P2 = np.c_[stn_x+a*dl_x, stn_y+a*dl_y, np.ones(nstn).T*mesh.vectorNz[-1]] + + Rx.append(np.c_[P1,P2]) + rxClass = DC.RxDipole(P1, P2) + Tx.append(tx) + if stype == 'dpdp': + srcClass = DC.SrcDipole([rxClass], M[ii,:],N[ii,:]) + elif stype == 'pdp': + srcClass = DC.SrcDipole([rxClass], M[ii,:],M[ii,:]) + SrcList.append(srcClass) + + elif stype == 'gradient': + + # Gradient survey only requires Tx at end of line and creates a square + # grid of receivers at in the middle at a pre-set minimum distance + + Tx.append(np.c_[M[0,:],N[-1,:]]) + + # Get the edge limit of survey area + min_x = endl[0,0] + dl_x * b + min_y = endl[0,1] + dl_y * b + + max_x = endl[1,0] - dl_x * b + max_y = endl[1,1] - dl_y * b + + box_l = np.sqrt( (min_x - max_x)**2 + (min_y - max_y)**2 ) + box_w = box_l/2. + + nstn = np.floor( box_l / a ) + + # Compute discrete pole location along line + stn_x = min_x + np.array(range(int(nstn)))*dl_x*a + stn_y = min_y + np.array(range(int(nstn)))*dl_y*a + + # Define number of cross lines + nlin = int(np.floor( box_w / a )) + lind = range(-nlin,nlin+1) + + ngrad = nstn * len(lind) + + rx = np.zeros([ngrad,6]) + for ii in range( len(lind) ): + + # Move line in perpendicular direction by dipole spacing + lxx = stn_x - lind[ii]*a*dl_y + lyy = stn_y + lind[ii]*a*dl_x + + + M = np.c_[ lxx, lyy , np.ones(nstn).T*mesh.vectorNz[-1]] + N = np.c_[ lxx+a*dl_x, lyy+a*dl_y, np.ones(nstn).T*mesh.vectorNz[-1]] + + rx[(ii*nstn):((ii+1)*nstn),:] = np.c_[M,N] + + Rx.append(rx) + rxClass = DC.RxDipole(rx[:,:3], rx[:,3:]) + srcClass = DC.SrcDipole([rxClass], M[0,:], N[-1,:]) + SrcList.append(srcClass) + else: + print """stype must be either 'pdp', 'dpdp' or 'gradient'. """ + + survey = DC.SurveyDC(SrcList) + return survey, Tx, Rx + +def writeUBC_DCobs(fileName, DCsurvey, dtype='3D', stype='SURFACE', iptype = 0): + """ + Write UBC GIF DCIP 2D or 3D observation file + + Input: + :string fileName -> including path where the file is written out + :DCsurvey DC survey class object + :string dtype -> either '2D' | '3D' + :string stype -> either 'SURFACE' | 'GENERAL' + + Output: + :param UBC2D-Data file + :return + + Last edit: February 16th, 2016 + + @author: dominiquef + + """ + from SimPEG import mkvc + + assert (dtype=='2D') | (dtype=='3D'), "Data must be either '2D' | '3D'" + assert (stype=='SURFACE') | (stype=='GENERAL') | (stype=='SIMPLE'), "Data must be either 'SURFACE' | 'GENERAL' | 'SIMPLE'" + + fid = open(fileName,'w') + + + if iptype!=0: + fid.write('IPTYPE=%i\n'%iptype) + + else: + fid.write('! ' + stype + ' FORMAT\n') + + count = 0 + + for ii in range(DCsurvey.nSrc): + + tx = np.c_[DCsurvey.srcList[ii].loc] + + rx = DCsurvey.srcList[ii].rxList[0].locs + + nD = DCsurvey.srcList[ii].nD + + M = rx[0] + N = rx[1] + + # Adapt source-receiver location for dtype and stype + if dtype=='2D': + + if stype == 'SIMPLE': + + #fid.writelines("%e " % ii for ii in mkvc(tx[0,:])) + A = np.repeat(tx[0,0],M.shape[0],axis=0) + B = np.repeat(tx[0,1],M.shape[0],axis=0) + M = M[:,0] + N = N[:,0] + + np.savetxt(fid, np.c_[A, B, M, N , DCsurvey.dobs[count:count+nD], DCsurvey.std[count:count+nD] ], fmt='%e',delimiter=' ',newline='\n') + + + else: + + if stype == 'SURFACE': + + fid.writelines("%f " % ii for ii in mkvc(tx[0,:])) + M = M[:,0] + N = N[:,0] + + if stype == 'GENERAL': + + # Flip sign for z-elevation to depth + tx[2::2,:] = -tx[2::2,:] + + fid.writelines("%e " % ii for ii in mkvc(tx[::2,:])) + M = M[:,0::2] + N = N[:,0::2] + + # Flip sign for z-elevation to depth + M[:,1::2] = -M[:,1::2] + N[:,1::2] = -N[:,1::2] + + fid.write('%i\n'% nD) + np.savetxt(fid, np.c_[ M, N , DCsurvey.dobs[count:count+nD], DCsurvey.std[count:count+nD] ], fmt='%f',delimiter=' ',newline='\n') + + if dtype=='3D': + + if stype == 'SURFACE': + + fid.writelines("%e " % ii for ii in mkvc(tx[0:2,:])) + M = M[:,0:2] + N = N[:,0:2] + + if stype == 'GENERAL': + + fid.writelines("%e " % ii for ii in mkvc(tx[0:3,:])) + + fid.write('%i\n'% nD) + np.savetxt(fid, np.c_[ M, N , DCsurvey.dobs[count:count+nD], DCsurvey.std[count:count+nD] ], fmt='%e',delimiter=' ',newline='\n') + fid.write('\n') + + count += nD + + fid.close() + +def convertObs_DC3D_to_2D(DCsurvey,lineID, flag = 'local'): + """ + Read DC survey and projects the coordinate system + according to the flag = 'Xloc' | 'Yloc' | 'local' (default) + In the 'local' system, station coordinates are referenced + to distance from the first srcLoc[0].loc[0] + + The Z value is preserved, but Y coordinates zeroed. + + Input: + :param survey3D + + Output: + :figure survey2D + + Edited April 6th, 2016 + + @author: dominiquef + + """ + from SimPEG import np + + def stn_id(v0,v1,r): + """ + Compute station ID along line + """ + + dl = int(v0.dot(v1)) * r + + return dl + + srcLists = [] + + srcMat = getSrc_locs(DCsurvey) + + # Find all unique line id + uniqueID = np.unique(lineID) + + for jj in range(len(uniqueID)): + + indx = np.where(lineID==uniqueID[jj])[0] + + # Find origin of survey + r = 1e+8 # Initialize to some large number + + Tx = srcMat[indx] + + x0 = Tx[0][0,0:2] # Define station zero along line + + vecTx, r1 = r_unit(x0,Tx[-1][1,0:2]) + + for ii in range(len(indx)): + + # Get all receivers + Rx = DCsurvey.srcList[indx[ii]].rxList[0].locs + nrx = Rx[0].shape[0] + + if flag == 'local': + # Find A electrode along line + vec, r = r_unit(x0,Tx[ii][0,0:2]) + A = stn_id(vecTx,vec,r) + + # Find B electrode along line + vec, r = r_unit(x0,Tx[ii][1,0:2]) + B = stn_id(vecTx,vec,r) + + M = np.zeros(nrx) + N = np.zeros(nrx) + for kk in range(nrx): + + # Find all M electrodes along line + vec, r = r_unit(x0,Rx[0][kk,0:2]) + M[kk] = stn_id(vecTx,vec,r) + + # Find all N electrodes along line + vec, r = r_unit(x0,Rx[1][kk,0:2]) + N[kk] = stn_id(vecTx,vec,r) + elif flag == 'Yloc': + """ Flip the XY axis locs""" + A = Tx[ii][0,1] + B = Tx[ii][1,1] + M = Rx[0][:,1] + N = Rx[1][:,1] + + elif flag == 'Xloc': + """ Copy the rx-tx locs""" + A = Tx[ii][0,0] + B = Tx[ii][1,0] + M = Rx[0][:,0] + N = Rx[1][:,0] + + Rx = DC.RxDipole(np.c_[M,np.zeros(nrx),Rx[0][:,2]],np.c_[N,np.zeros(nrx),Rx[1][:,2]]) + + srcLists.append( DC.SrcDipole( [Rx], np.asarray([A,0,Tx[ii][0,2]]),np.asarray([B,0,Tx[ii][1,2]]) ) ) + + + DCsurvey2D = DC.SurveyDC(srcLists) + + DCsurvey2D.dobs = np.asarray(DCsurvey.dobs) + DCsurvey2D.std = np.asarray(DCsurvey.std) + + return DCsurvey2D + +def readUBC_DC3Dobs(fileName, dtype = 'DC'): + """ + Read UBC GIF IP 3D observation file and generate survey + + Input: + :param fileName, path to the UBC GIF 3D obs file + + Output: + :param IPsurvey + :return + + @author: dominiquef + + """ + zflag = True # Flag for z value provided + + # Load file + if dtype == 'IP': + obsfile = np.genfromtxt(fileName,delimiter=' \n',dtype=np.str,comments='IPTYPE') + + elif dtype == 'DC': + obsfile = np.genfromtxt(fileName,delimiter=' \n',dtype=np.str,comments='!') + + else: + print "dtype must be 'DC'(default) | 'IP'" + + # Pre-allocate + srcLists = [] + Rx = [] + d = [] + wd = [] + + + # Countdown for number of obs/tx + count = 0 + for ii in range(obsfile.shape[0]): + + # Skip if blank line + if not obsfile[ii]: + continue + + # First line or end of a transmitter block, read transmitter info + if count==0: + # Read the line + temp = (np.fromstring(obsfile[ii], dtype=float, sep=' ').T) + count = int(temp[-1]) + + # Check if z value is provided, if False -> nan + if len(temp)==5: + tx = np.r_[temp[0:2],np.nan,temp[2:4],np.nan] + + zflag = False # Pass on the flag to the receiver loc + + else: + tx = temp[:-1] + + rx = [] + continue + + temp = np.fromstring(obsfile[ii], dtype=float,sep=' ') # Get the string + + # Filter out negative IP +# if temp[-2] < 0: +# count = count -1 +# print "Negative!" +# +# else: + + # If the Z-location is provided, otherwise put nan + if zflag: + + rx.append(temp[:-2]) + # Check if there is data with the location + if len(temp)==8: + d.append(temp[-2]) + wd.append(temp[-1]) + + else: + rx.append(np.r_[temp[0:2],np.nan,temp[2:4],np.nan] ) + # Check if there is data with the location + if len(temp)==6: + d.append(temp[-2]) + wd.append(temp[-1]) + + count = count -1 + + # Reach the end of transmitter block, append the src, rx and continue + if count == 0: + rx = np.asarray(rx) + Rx = DC.RxDipole(rx[:,:3],rx[:,3:]) + srcLists.append( DC.SrcDipole( [Rx], tx[:3],tx[3:]) ) + + # Create survey class + survey = DC.SurveyDC(srcLists) + + survey.dobs = np.asarray(d) + survey.std = np.asarray(wd) + + return {'DCsurvey':survey} + +def readUBC_DC2Dobs(fileName): + """ + ------- NEEDS TO BE UPDATED ------ + Read UBC GIF 2D observation file and generate arrays for tx-rx location + + Input: + :param fileName, path to the UBC GIF 2D model file + + Output: + :param rx, tx + :return + + Created on Thu Nov 12 13:14:10 2015 + + @author: dominiquef + + """ + + from SimPEG import np + + # Load file + obsfile = np.genfromtxt(fileName,delimiter=' \n',dtype=np.str,comments='!') + + # Check first line and figure out if 2D or 3D file format + line = np.array(obsfile[0].split(),dtype=float) + + tx_A = [] + tx_B = [] + rx_M = [] + rx_N = [] + d = [] + wd = [] + + for ii in range(obsfile.shape[0]): + + # If len==3, then simple format where tx-rx is listed on each line + if len(line) == 4: + + temp = np.fromstring(obsfile[ii], dtype=float,sep=' ') + tx_A = np.hstack((tx_A,temp[0])) + tx_B = np.hstack((tx_B,temp[1])) + rx_M = np.hstack((rx_M,temp[2])) + rx_N = np.hstack((rx_N,temp[3])) + + + rx = np.transpose(np.array((rx_M,rx_N))) + tx = np.transpose(np.array((tx_A,tx_B))) + + return tx, rx, d, wd + +def readUBC_DC2Dpre(fileName): + """ + Read UBC GIF DCIP 2D observation file and generate arrays for tx-rx location + + Input: + :param fileName, path to the UBC GIF 3D obs file + + Output: + DCsurvey + :return + + Created on Mon March 9th, 2016 << Doug's 70th Birthday !! >> + + @author: dominiquef + + """ + + # Load file + obsfile = np.genfromtxt(fileName,delimiter=' \n',dtype=np.str,comments='!') + + # Pre-allocate + srcLists = [] + Rx = [] + d = [] + zflag = True # Flag for z value provided + + for ii in range(obsfile.shape[0]): + + if not obsfile[ii]: + continue + + # First line is transmitter with number of receivers + + + temp = (np.fromstring(obsfile[ii], dtype=float,sep=' ').T) + + + # Check if z value is provided, if False -> nan + if len(temp)==5: + tx = np.r_[temp[0],np.nan,np.nan,temp[1],np.nan,np.nan] + zflag = False + + else: + tx = np.r_[temp[0],np.nan,temp[1],temp[2],np.nan,temp[3]] + + + if zflag: + rx = np.c_[temp[4],np.nan,temp[5],temp[6],np.nan,temp[7]] + + + else: + rx = np.c_[temp[2],np.nan,np.nan,temp[3],np.nan,np.nan] + # Check if there is data with the location + + d.append(temp[-1]) + + + Rx = DC.RxDipole(rx[:,:3],rx[:,3:]) + srcLists.append( DC.SrcDipole( [Rx], tx[:3],tx[3:]) ) + + # Create survey class + survey = DC.SurveyDC(srcLists) + + survey.dobs = np.asarray(d) + + return {'DCsurvey':survey} + +def readUBC_DC2DMesh(fileName): + """ + Read UBC GIF 2DTensor mesh and generate 2D Tensor mesh in simpeg + + Input: + :param fileName, path to the UBC GIF mesh file + + Output: + :param SimPEG TensorMesh 2D object + :return + + Created on Thu Nov 12 13:14:10 2015 + + @author: dominiquef + + """ + + from SimPEG import np + # Open file + fopen = open(fileName,'r') + + # Read down the file and unpack dx vector + def unpackdx(fid,nrows): + for ii in range(nrows): + + line = fid.readline() + var = np.array(line.split(),dtype=float) + + if ii==0: + x0= var[0] + xvec = np.ones(int(var[2])) * (var[1] - var[0]) / int(var[2]) + xend = var[1] + + else: + xvec = np.hstack((xvec,np.ones(int(var[1])) * (var[0] - xend) / int(var[1]))) + xend = var[0] + + return x0, xvec + + #%% Start with dx block + # First line specifies the number of rows for x-cells + line = fopen.readline() + nl = np.array(line.split(),dtype=float) + + [x0, dx] = unpackdx(fopen,nl) + + + #%% Move down the file until reaching the z-block + line = fopen.readline() + if not line: + line = fopen.readline() + + #%% End with dz block + # First line specifies the number of rows for z-cells + line = fopen.readline() + nl = np.array(line.split(),dtype=float) + + [z0, dz] = unpackdx(fopen,nl) + + # Flip z0 to be the bottom of the mesh for SimPEG + z0 = z0 - sum(dz) + dz = dz[::-1] + #%% Make the mesh using SimPEG + + from SimPEG import Mesh + tensMsh = Mesh.TensorMesh([dx,dz],(x0, z0)) + return tensMsh + +def xy_2_lineID(DCsurvey): + """ + Read DC survey class and append line ID. + Assumes that the locations are listed in the order + they were collected. May need to generalize for random + point locations, but will be more expensive + + Input: + :param DCdict Vectors of station location + + Output: + :param LineID Vector of integers + :return + + Created on Thu Feb 11, 2015 + + @author: dominiquef + + """ + + # Compute unit vector between two points + nstn = DCsurvey.nSrc + + # Pre-allocate space + lineID = np.zeros(nstn) + + linenum = 0 + indx = 0 + + for ii in range(nstn): + + if ii == 0: + + A = DCsurvey.srcList[ii].loc[0] + B = DCsurvey.srcList[ii].loc[1] + + xout = np.mean([A[0:2],B[0:2]], axis = 0) + + xy0 = A[:2] + xym = xout + + # Deal with replicate pole location + if np.all(xy0==xym): + + xym[0] = xym[0] + 1e-3 + + continue + + A = DCsurvey.srcList[ii].loc[0] + B = DCsurvey.srcList[ii].loc[1] + + xin = np.mean([A[0:2],B[0:2]], axis = 0) + + # Compute vector between neighbours + vec1, r1 = r_unit(xout,xin) + + # Compute vector between current stn and mid-point + vec2, r2 = r_unit(xym,xin) + + # Compute vector between current stn and start line + vec3, r3 = r_unit(xy0,xin) + + # Compute vector between mid-point and start line + vec4, r4 = r_unit(xym,xy0) + + # Compute dot product + ang1 = np.abs(vec1.dot(vec2)) + ang2 = np.abs(vec3.dot(vec4)) + + # If the angles are smaller then 45d, than next point is on a new line + if ((ang1 < np.cos(np.pi/4.)) | (ang2 < np.cos(np.pi/4.))) & (np.all(np.r_[r1,r2,r3,r4] > 0)): + + # Re-initiate start and mid-point location + xy0 = A[:2] + xym = xin + + # Deal with replicate pole location + if np.all(xy0==xym): + + xym[0] = xym[0] + 1e-3 + + linenum += 1 + indx = ii + + else: + xym = np.mean([xy0,xin], axis = 0) + + lineID[ii] = linenum + xout = xin + + return lineID + +def r_unit(p1,p2): + """ + r_unit(x,y) : Function computes the unit vector + between two points with coordinates p1(x1,y1) and p2(x2,y2) + + """ + + assert len(p1)==len(p2), 'locs must be the same shape.' + + dx = [] + for ii in range(len(p1)): + dx.append((p2[ii] - p1[ii])) + + # Compute length of vector + r = np.linalg.norm(np.asarray(dx)) + + + if r!=0: + vec = dx/r + + else: + vec = np.zeros(len(p1)) + + return vec, r + +def getSrc_locs(DCsurvey): + """ + + + """ + + srcMat = np.zeros((DCsurvey.nSrc,2,3)) + for ii in range(DCsurvey.nSrc): + srcMat[ii,:,:] = np.asarray(DCsurvey.srcList[ii].loc) + + return srcMat diff --git a/SimPEG/DCIP/Utils.py b/SimPEG/DCIP/Utils.py new file mode 100644 index 00000000..702a2333 --- /dev/null +++ b/SimPEG/DCIP/Utils.py @@ -0,0 +1,38 @@ +import numpy as np + +def WennerSrcList(nElecs, aSpacing, in2D=False, plotIt=False): + + import SimPEG.DCIP as DC + + elocs = np.arange(0,aSpacing*nElecs,aSpacing) + elocs -= (nElecs*aSpacing - aSpacing)/2 + space = 1 + WENNER = np.zeros((0,),dtype=int) + for ii in range(nElecs): + for jj in range(nElecs): + test = np.r_[jj,jj+space,jj+space*2,jj+space*3] + if np.any(test >= nElecs): + break + WENNER = np.r_[WENNER, test] + space += 1 + WENNER = WENNER.reshape((-1,4)) + + + if plotIt: + for i, s in enumerate('rbkg'): + plt.plot(elocs[WENNER[:,i]],s+'.') + plt.show() + + # Create sources and receivers + i = 0 + if in2D: + getLoc = lambda ii, abmn: np.r_[elocs[WENNER[ii,abmn]],0] + else: + getLoc = lambda ii, abmn: np.r_[elocs[WENNER[ii,abmn]],0, 0] + srcList = [] + for i in range(WENNER.shape[0]): + rx = DC.RxDipole(getLoc(i,1),getLoc(i,2)) + src = DC.SrcDipole([rx], getLoc(i,0),getLoc(i,3)) + srcList += [src] + + return srcList diff --git a/SimPEG/DCIP/__init__.py b/SimPEG/DCIP/__init__.py new file mode 100644 index 00000000..08d1fe12 --- /dev/null +++ b/SimPEG/DCIP/__init__.py @@ -0,0 +1,4 @@ +from BaseDC import * +from BaseIP import * +from DCIPUtils import * +import Utils diff --git a/SimPEG/DataMisfit.py b/SimPEG/DataMisfit.py index 425fe4ce..53728c4e 100644 --- a/SimPEG/DataMisfit.py +++ b/SimPEG/DataMisfit.py @@ -22,11 +22,11 @@ class BaseDataMisfit(object): Utils.setKwargs(self,**kwargs) @Utils.timeIt - def eval(self, m, u=None): - """eval(m, u=None) + def eval(self, m, f=None): + """eval(m, f=None) :param numpy.array m: geophysical model - :param numpy.array u: fields + :param Fields f: fields :rtype: float :return: data misfit @@ -34,11 +34,11 @@ class BaseDataMisfit(object): raise NotImplementedError('This method should be overwritten.') @Utils.timeIt - def evalDeriv(self, m, u=None): - """evalDeriv(m, u=None) + def evalDeriv(self, m, f=None): + """evalDeriv(m, f=None) :param numpy.array m: geophysical model - :param numpy.array u: fields + :param Fields f: fields :rtype: numpy.array :return: data misfit derivative @@ -47,12 +47,12 @@ class BaseDataMisfit(object): @Utils.timeIt - def eval2Deriv(self, m, v, u=None): - """eval2Deriv(m, v, u=None) + def eval2Deriv(self, m, v, f=None): + """eval2Deriv(m, v, f=None) :param numpy.array m: geophysical model :param numpy.array v: vector to multiply - :param numpy.array u: fields + :param Fields f: fields :rtype: numpy.array :return: data misfit derivative @@ -89,7 +89,7 @@ class l2_DataMisfit(BaseDataMisfit): """ if getattr(self, '_Wd', None) is None: - + survey = self.survey if getattr(survey,'std', None) is None: @@ -108,24 +108,20 @@ class l2_DataMisfit(BaseDataMisfit): self._Wd = value @Utils.timeIt - def eval(self, m, u=None): - "eval(m, u=None)" - prob = self.prob - survey = self.survey - R = self.Wd * survey.residual(m, u=u) + def eval(self, m, f=None): + "eval(m, f=None)" + if f is None: f = self.prob.fields(m) + R = self.Wd * self.survey.residual(m, f) return 0.5*np.vdot(R, R) @Utils.timeIt - def evalDeriv(self, m, u=None): - "evalDeriv(m, u=None)" - prob = self.prob - survey = self.survey - if u is None: u = prob.fields(m) - return prob.Jtvec(m, self.Wd * (self.Wd * survey.residual(m, u=u)), u=u) + def evalDeriv(self, m, f=None): + "evalDeriv(m, f=None)" + if f is None: f = self.prob.fields(m) + return self.prob.Jtvec(m, self.Wd * (self.Wd * self.survey.residual(m, f=f)), f=f) @Utils.timeIt - def eval2Deriv(self, m, v, u=None): - "eval2Deriv(m, v, u=None)" - prob = self.prob - if u is None: u = prob.fields(m) - return prob.Jtvec_approx(m, self.Wd * (self.Wd * prob.Jvec_approx(m, v, u=u)), u=u) + def eval2Deriv(self, m, v, f=None): + "eval2Deriv(m, v, f=None)" + if f is None: f = self.prob.fields(m) + return self.prob.Jtvec_approx(m, self.Wd * (self.Wd * self.prob.Jvec_approx(m, v, f=f)), f=f) diff --git a/SimPEG/Directives.py b/SimPEG/Directives.py index 2ed27c20..4fc1ffc5 100644 --- a/SimPEG/Directives.py +++ b/SimPEG/Directives.py @@ -123,10 +123,10 @@ class BetaEstimate_ByEig(InversionDirective): if self.debug: print 'Calculating the beta0 parameter.' m = self.invProb.curModel - u = self.invProb.getFields(m, store=True, deleteWarmstart=False) + f = self.invProb.getFields(m, store=True, deleteWarmstart=False) x0 = np.random.rand(*m.shape) - t = x0.dot(self.dmisfit.eval2Deriv(m,x0,u=u)) + t = x0.dot(self.dmisfit.eval2Deriv(m,x0,f=f)) b = x0.dot(self.reg.eval2Deriv(m, v=x0)) self.beta0 = self.beta0_ratio*(t/b) @@ -216,13 +216,13 @@ class SaveOutputDictEveryIteration(_SaveEveryIteration): # Save the data. ms = self.reg.Ws * ( self.reg.mapping * (self.invProb.curModel - self.reg.mref) ) phi_ms = 0.5*ms.dot(ms) - if self.reg.smoothModel == True: + if self.reg.mrefInSmooth == True: mref = self.reg.mref else: mref = 0 mx = self.reg.Wx * ( self.reg.mapping * (self.invProb.curModel - mref) ) phi_mx = 0.5 * mx.dot(mx) - if self.prob.mesh.dim==2: + if self.prob.mesh.dim >= 2: my = self.reg.Wy * ( self.reg.mapping * (self.invProb.curModel - mref) ) phi_my = 0.5 * my.dot(my) else: @@ -237,40 +237,6 @@ class SaveOutputDictEveryIteration(_SaveEveryIteration): # Save the file as a npz np.savez('{:03d}-{:s}'.format(self.opt.iter,self.fileName), iter=self.opt.iter, beta=self.invProb.beta, phi_d=self.invProb.phi_d, phi_m=self.invProb.phi_m, phi_ms=phi_ms, phi_mx=phi_mx, phi_my=phi_my, phi_mz=phi_mz,f=self.opt.f, m=self.invProb.curModel,dpred=self.invProb.dpred) -class SaveOutputDictEveryIteration(_SaveEveryIteration): - """SaveOutputDictEveryIteration - A directive that saves some relevant information from the inversion run to a numpy .npz dictionary file (see numpy.savez function for further info). - """ - - def initialize(self): - print "SimPEG.SaveOutputDictEveryIteration will save your inversion progress as dictionary: '%s-###.npz'"%self.fileName - - def endIter(self): - # Save the data. - ms = self.reg.Ws * ( self.reg.mapping * (self.invProb.curModel - self.reg.mref) ) - phi_ms = 0.5*ms.dot(ms) - if self.reg.smoothModel == True: - mref = self.reg.mref - else: - mref = 0 - mx = self.reg.Wx * ( self.reg.mapping * (self.invProb.curModel - mref) ) - phi_mx = 0.5 * mx.dot(mx) - if self.prob.mesh.dim==2: - my = self.reg.Wy * ( self.reg.mapping * (self.invProb.curModel - mref) ) - phi_my = 0.5 * my.dot(my) - else: - phi_my = 'NaN' - if self.prob.mesh.dim==3 and 'CYL' not in self.prob.mesh._meshType: - mz = self.reg.Wz * ( self.reg.mapping * (self.invProb.curModel - mref) ) - phi_mz = 0.5 * mz.dot(mz) - else: - phi_mz = 'NaN' - - - # Save the file as a npz - np.savez('{:s}-{:03d}'.format(self.fileName,self.opt.iter), iter=self.opt.iter, beta=self.invProb.beta, phi_d=self.invProb.phi_d, phi_m=self.invProb.phi_m, phi_ms=phi_ms, phi_mx=phi_mx, phi_my=phi_my, phi_mz=phi_mz,f=self.opt.f, m=self.invProb.curModel,dpred=self.invProb.dpred) - - # class UpdateReferenceModel(Parameter): @@ -283,3 +249,125 @@ class SaveOutputDictEveryIteration(_SaveEveryIteration): # mref = self.mref0 # self.m_prev = self.invProb.m_current # return mref + +class Update_IRLS(InversionDirective): + + eps_min = None + factor = None + gamma = None + phi_m_last = None + phi_d_last = None + + + def initialize(self): + + # Scale the regularization for changes in norm + if getattr(self, 'phi_m_last', None) is not None: + + self.reg.curModel = self.invProb.curModel + self.reg.gamma = 1. + phim_new = self.reg.eval(self.invProb.curModel) + self.gamma = self.phi_m_last / phim_new + + self.reg.curModel = self.invProb.curModel + self.reg.gamma = self.gamma + + if getattr(self, 'phi_d_last', None) is None: + self.phi_d_last = self.invProb.phi_d + + def endIter(self): + # Cool the threshold parameter if required + if getattr(self, 'factor', None) is not None: + eps = self.reg.eps / self.factor + + if getattr(self, 'eps_min', None) is not None: + self.reg.eps = np.max([self.eps_min,eps]) + else: + self.reg.eps = eps + + # Get phi_m at the end of current iteration + self.phi_m_last = self.invProb.phi_m_last + + # Update the model used for the IRLS weights + self.reg.curModel = self.invProb.curModel + + # Temporarely set gamma to 1. to get raw phi_m + self.reg.gamma = 1. + + # Compute new model objective function value + phim_new = self.reg.eval(self.invProb.curModel) + + # Update gamma to scale the regularization between IRLS iterations + self.reg.gamma = self.phi_m_last / phim_new + + # Set the weighting matrix to None so that it is recomputed next time + # it is called in the inversion + self.reg._W = None + +class Update_lin_PreCond(InversionDirective): + """ + Create a Jacobi preconditioner for the linear problem + """ + onlyOnStart=False + + def initialize(self): + + if getattr(self.opt, 'approxHinv', None) is None: + # Update the pre-conditioner + diagA = np.sum(self.prob.G**2.,axis=0) + self.invProb.beta*(self.reg.W.T*self.reg.W).diagonal() #* (self.reg.mapping * np.ones(self.reg.curModel.size))**2. + PC = Utils.sdiag((self.prob.mapping.deriv(None).T *diagA)**-1.) + self.opt.approxHinv = PC + + def endIter(self): + # Cool the threshold parameter + if self.onlyOnStart==True: + return + + if getattr(self.opt, 'approxHinv', None) is not None: + # Update the pre-conditioner + diagA = np.sum(self.prob.G**2.,axis=0) + self.invProb.beta*(self.reg.W.T*self.reg.W).diagonal() #* (self.reg.mapping * np.ones(self.reg.curModel.size))**2. + PC = Utils.sdiag((self.prob.mapping.deriv(None).T *diagA)**-1.) + self.opt.approxHinv = PC + + +class Update_Wj(InversionDirective): + """ + Create approx-sensitivity base weighting using the probing method + """ + k = None # Number of probing cycles + itr = None # Iteration number to update Wj, or always update if None + + def endIter(self): + + if self.itr is None or self.itr == self.opt.iter: + + m = self.invProb.curModel + if self.k is None: + self.k = int(self.survey.nD/10) + + def JtJv(v): + + Jv = self.prob.Jvec(m, v) + + return self.prob.Jtvec(m,Jv) + + JtJdiag = Utils.diagEst(JtJv,len(m),k=self.k) + JtJdiag = JtJdiag / max(JtJdiag) + + self.reg.wght = JtJdiag + +class Scale_Beta(InversionDirective): + """ + Instead of a linear cooling schedule, beta is allowed to change based + on the ratio between the target misfit and the current data misfit. The + update is done only if the misfit is outside some threshold bounds. + """ + tol = 0.05 + + def endIter(self): + + # Check if misfit is within the tolerance, otherwise adjust beta + val = self.invProb.phi_d / (self.survey.nD*0.5) + + if np.abs(1.-val) > self.tol: + self.invProb.beta = self.invProb.beta * self.survey.nD*0.5 / self.invProb.phi_d diff --git a/SimPEG/EM/Base.py b/SimPEG/EM/Base.py index 32018f7e..431086a4 100644 --- a/SimPEG/EM/Base.py +++ b/SimPEG/EM/Base.py @@ -2,14 +2,14 @@ from SimPEG import Survey, Problem, Utils, Models, Maps, PropMaps, np, sp, Solve 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)) + rho = Maps.Property("Electrical Resistivity", propertyLink=('sigma', Maps.ReciprocalMap)) mui = Maps.Property("Inverse Magnetic Permeability", defaultVal = 1./mu_0, propertyLink=('mu', Maps.ReciprocalMap)) @@ -21,7 +21,7 @@ class BaseEMProblem(Problem.BaseProblem): surveyPair = Survey.BaseSurvey dataPair = Survey.Data - + PropMap = EMPropMap Solver = SimpegSolver @@ -51,7 +51,7 @@ class BaseEMProblem(Problem.BaseProblem): 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): """ @@ -61,6 +61,15 @@ class BaseEMProblem(Problem.BaseProblem): self._Me = self.mesh.getEdgeInnerProduct() return self._Me + @property + def MeI(self): + """ + Edge inner product matrix + """ + if getattr(self, '_MeI', None) is None: + self._MeI = self.mesh.getEdgeInnerProduct(invMat=True) + return self._MeI + @property def Mf(self): """ @@ -70,8 +79,17 @@ class BaseEMProblem(Problem.BaseProblem): self._Mf = self.mesh.getFaceInnerProduct() return self._Mf + @property + def MfI(self): + """ + Face inner product matrix + """ + if getattr(self, '_MfI', None) is None: + self._MfI = self.mesh.getFaceInnerProduct(invMat=True) + return self._MfI - # ----- Magnetic Permeability ----- # + + # ----- Magnetic Permeability ----- # @property def MfMui(self): """ @@ -109,7 +127,7 @@ class BaseEMProblem(Problem.BaseProblem): return self._MeMuI - # ----- Electrical Conductivity ----- # + # ----- Electrical Conductivity ----- # #TODO: hardcoded to sigma as the model @property def MeSigma(self): @@ -120,18 +138,18 @@ class BaseEMProblem(Problem.BaseProblem): self._MeSigma = self.mesh.getEdgeInnerProduct(self.curModel.sigma) return self._MeSigma - # TODO: This should take a vector + # 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\\). + 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) @@ -140,8 +158,8 @@ class BaseEMProblem(Problem.BaseProblem): # TODO: This should take a vector def MeSigmaIDeriv(self, u): """ - Derivative of :code:`MeSigma` with respect to the model - """ + 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 @@ -163,7 +181,7 @@ class BaseEMProblem(Problem.BaseProblem): # TODO: This should take a vector def MfRhoDeriv(self,u): """ - Derivative of :code:`MfRho` with respect to the model. + 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 @@ -181,6 +199,29 @@ class BaseEMProblem(Problem.BaseProblem): # TODO: This should take a vector def MfRhoIDeriv(self,u): """ - Derivative of :code:`MfRhoI` with respect to the model. + Derivative of :code:`MfRhoI` with respect to the model. """ return self.mesh.getFaceInnerProductDeriv(self.curModel.rho, invMat=True)(u) * self.curModel.rhoDeriv + +class BaseEMSurvey(Survey.BaseSurvey): + + def __init__(self, srcList, **kwargs): + # Sort these by frequency + self.srcList = srcList + Survey.BaseSurvey.__init__(self, **kwargs) + + def eval(self, u): + """ + Project fields to receiver locations + :param Fields u: fields object + :rtype: numpy.ndarray + :return: data + """ + data = Survey.Data(self) + for src in self.srcList: + for rx in src.rxList: + data[src, rx] = rx.eval(src, self.mesh, u) + return data + + def evalDeriv(self, u): + raise Exception('Use Receivers to project fields deriv.') diff --git a/SimPEG/EM/FDEM/FDEM.py b/SimPEG/EM/FDEM/FDEM.py index e8cc9ba2..026230c6 100644 --- a/SimPEG/EM/FDEM/FDEM.py +++ b/SimPEG/EM/FDEM/FDEM.py @@ -1,7 +1,7 @@ from SimPEG import Problem, Utils, np, sp, Solver as SimpegSolver from scipy.constants import mu_0 from SurveyFDEM import Survey as SurveyFDEM -from FieldsFDEM import Fields, Fields_e, Fields_b, Fields_h, Fields_j +from FieldsFDEM import Fields, Fields3D_e, Fields3D_b, Fields3D_h, Fields3D_j from SimPEG.EM.Base import BaseEMProblem from SimPEG.EM.Utils import omega @@ -17,8 +17,8 @@ class BaseFDEMProblem(BaseEMProblem): \mathbf{C} \mathbf{e} + i \omega \mathbf{b} = \mathbf{s_m} \\\\ {\mathbf{C}^{\\top} \mathbf{M_{\mu^{-1}}^f} \mathbf{b} - \mathbf{M_{\sigma}^e} \mathbf{e} = \mathbf{s_e}} - if using the E-B formulation (:code:`Problem_e` - or :code:`Problem_b`). Note that in this case, :math:`\mathbf{s_e}` is an integrated quantity. + if using the E-B formulation (:code:`Problem3D_e` + or :code:`Problem3D_b`). Note that in this case, :math:`\mathbf{s_e}` is an integrated quantity. If we write Maxwell's equations in terms of \\\(\\\mathbf{h}\\\) and current density \\\(\\\mathbf{j}\\\) @@ -28,7 +28,7 @@ class BaseFDEMProblem(BaseEMProblem): \mathbf{C}^{\\top} \mathbf{M_{\\rho}^f} \mathbf{j} + i \omega \mathbf{M_{\mu}^e} \mathbf{h} = \mathbf{s_m} \\\\ \mathbf{C} \mathbf{h} - \mathbf{j} = \mathbf{s_e} - if using the H-J formulation (:code:`Problem_j` or :code:`Problem_h`). Note that here, :math:`\mathbf{s_m}` is an integrated quantity. + if using the H-J formulation (:code:`Problem3D_j` or :code:`Problem3D_h`). Note that here, :math:`\mathbf{s_m}` is an integrated quantity. The problem performs the elimination so that we are solving the system for \\\(\\\mathbf{e},\\\mathbf{b},\\\mathbf{j} \\\) or \\\(\\\mathbf{h}\\\) """ @@ -36,30 +36,29 @@ class BaseFDEMProblem(BaseEMProblem): surveyPair = SurveyFDEM fieldsPair = Fields - def fields(self, m=None): + def fields(self, m): """ Solve the forward problem for the fields. :param numpy.array m: inversion model (nP,) :rtype numpy.array: - :return F: forward solution + :return f: forward solution """ self.curModel = m - F = self.fieldsPair(self.mesh, self.survey) + 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 + u = Ainv * rhs Srcs = self.survey.getSrcByFreq(freq) - ftype = self._fieldType + 'Solution' - F[Srcs, ftype] = sol + f[Srcs, self._solutionType] = u Ainv.clean() - return F + return f - def Jvec(self, m, v, u=None): + def Jvec(self, m, v, f=None): """ Sensitivity times a vector. @@ -70,33 +69,31 @@ class BaseFDEMProblem(BaseEMProblem): :return: Jv (ndata,) """ - if u is None: - u = self.fields(m) + if f is None: + f = self.fields(m) self.curModel = m Jv = self.dataPair(self.survey) for freq in self.survey.freqs: - A = self.getA(freq) # - Ainv = self.Solver(A, **self.solverOpts) + A = self.getA(freq) + Ainv = self.Solver(A, **self.solverOpts) # create the concept of Ainv (actually a solve) for src in self.survey.getSrcByFreq(freq): - ftype = self._fieldType + 'Solution' - u_src = u[src, ftype] + u_src = f[src, self._solutionType] dA_dm_v = self.getADeriv(freq, u_src, v) dRHS_dm_v = self.getRHSDeriv(freq, src, v) du_dm_v = Ainv * ( - dA_dm_v + dRHS_dm_v ) for rx in src.rxList: - df_dmFun = getattr(u, '_%sDeriv'%rx.projField, None) + df_dmFun = getattr(f, '_%sDeriv'%rx.projField, None) df_dm_v = df_dmFun(src, du_dm_v, v, adjoint=False) - df_dm_v = np.array(df_dm_v, dtype=complex) - Jv[src, rx] = rx.evalDeriv(src, self.mesh, u, df_dm_v) + Jv[src, rx] = rx.evalDeriv(src, self.mesh, f, df_dm_v) Ainv.clean() return Utils.mkvc(Jv) - def Jtvec(self, m, v, u=None): + def Jtvec(self, m, v, f=None): """ Sensitivity transpose times a vector @@ -107,8 +104,8 @@ class BaseFDEMProblem(BaseEMProblem): :return: Jv (ndata,) """ - if u is None: - u = self.fields(m) + if f is None: + f = self.fields(m) self.curModel = m @@ -123,13 +120,12 @@ class BaseFDEMProblem(BaseEMProblem): ATinv = self.Solver(AT, **self.solverOpts) for src in self.survey.getSrcByFreq(freq): - ftype = self._fieldType + 'Solution' - u_src = u[src, ftype] + u_src = f[src, self._solutionType] for rx in src.rxList: - PTv = rx.evalDeriv(src, self.mesh, u, v[src, rx], adjoint=True) # wrt u, need possibility wrt m + PTv = rx.evalDeriv(src, self.mesh, f, v[src, rx], adjoint=True) # wrt f, need possibility wrt m - df_duTFun = getattr(u, '_%sDeriv'%rx.projField, None) + df_duTFun = getattr(f, '_%sDeriv'%rx.projField, None) df_duT, df_dmT = df_duTFun(src, None, PTv, adjoint=True) ATinvdf_duT = ATinv * df_duT @@ -138,14 +134,13 @@ class BaseFDEMProblem(BaseEMProblem): dRHS_dmT = self.getRHSDeriv(freq, src, ATinvdf_duT, adjoint=True) du_dmT = -dA_dmT + dRHS_dmT - df_dmT += du_dmT + df_dmT = df_dmT + du_dmT # TODO: this should be taken care of by the reciever? - real_or_imag = rx.projComp - if real_or_imag is 'real': - Jtv += np.array(df_dmT,dtype=complex).real - elif real_or_imag is 'imag': - Jtv += - np.array(df_dmT,dtype=complex).real + if rx.real_or_imag is 'real': + Jtv += np.array(df_dmT, dtype=complex).real + elif rx.real_or_imag is 'imag': + Jtv += - np.array(df_dmT, dtype=complex).real else: raise Exception('Must be real or imag') @@ -159,29 +154,29 @@ class BaseFDEMProblem(BaseEMProblem): :param float freq: Frequency :rtype: (numpy.ndarray, numpy.ndarray) - :return: S_m, S_e (nE or nF, nSrc) + :return: s_m, s_e (nE or nF, nSrc) """ 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) + if self._formulation is 'EB': + s_m = np.zeros((self.mesh.nF,len(Srcs)), dtype=complex) + s_e = np.zeros((self.mesh.nE,len(Srcs)), dtype=complex) + elif self._formulation is 'HJ': + 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) - S_m[:,i] = S_m[:,i] + smi - S_e[:,i] = S_e[:,i] + sei + s_m[:,i] = s_m[:,i] + smi + s_e[:,i] = s_e[:,i] + sei - return S_m, S_e + return s_m, s_e ########################################################################################## ################################ E-B Formulation ######################################### ########################################################################################## -class Problem_e(BaseFDEMProblem): +class Problem3D_e(BaseFDEMProblem): """ By eliminating the magnetic flux density using @@ -201,9 +196,9 @@ class Problem_e(BaseFDEMProblem): :param SimPEG.Mesh mesh: mesh """ - _fieldType = 'e' - _eqLocs = 'FE' - fieldsPair = Fields_e + _solutionType = 'eSolution' + _formulation = 'EB' + fieldsPair = Fields3D_e def __init__(self, mesh, **kwargs): BaseFDEMProblem.__init__(self, mesh, **kwargs) @@ -262,11 +257,11 @@ class Problem_e(BaseFDEMProblem): :return: RHS (nE, nSrc) """ - S_m, S_e = self.getSourceTerm(freq) + s_m, s_e = self.getSourceTerm(freq) C = self.mesh.edgeCurl MfMui = self.MfMui - return C.T * (MfMui * S_m) -1j * omega(freq) * S_e + return C.T * (MfMui * s_m) -1j * omega(freq) * s_e def getRHSDeriv(self, freq, src, v, adjoint=False): """ @@ -282,17 +277,17 @@ class Problem_e(BaseFDEMProblem): C = self.mesh.edgeCurl MfMui = self.MfMui - S_mDeriv, S_eDeriv = src.evalDeriv(self, adjoint=adjoint) + s_mDeriv, s_eDeriv = src.evalDeriv(self, adjoint=adjoint) if adjoint: dRHS = MfMui * (C * v) - return S_mDeriv(dRHS) - 1j * omega(freq) * S_eDeriv(v) + return s_mDeriv(dRHS) - 1j * omega(freq) * s_eDeriv(v) else: - return C.T * (MfMui * S_mDeriv(v)) -1j * omega(freq) * S_eDeriv(v) + return C.T * (MfMui * s_mDeriv(v)) -1j * omega(freq) * s_eDeriv(v) -class Problem_b(BaseFDEMProblem): +class Problem3D_b(BaseFDEMProblem): """ We eliminate :math:`\mathbf{e}` using @@ -312,9 +307,9 @@ class Problem_b(BaseFDEMProblem): :param SimPEG.Mesh mesh: mesh """ - _fieldType = 'b' - _eqLocs = 'FE' - fieldsPair = Fields_b + _solutionType = 'bSolution' + _formulation = 'EB' + fieldsPair = Fields3D_b def __init__(self, mesh, **kwargs): BaseFDEMProblem.__init__(self, mesh, **kwargs) @@ -387,11 +382,11 @@ class Problem_b(BaseFDEMProblem): :return: RHS (nE, nSrc) """ - S_m, S_e = self.getSourceTerm(freq) + s_m, s_e = self.getSourceTerm(freq) C = self.mesh.edgeCurl MeSigmaI = self.MeSigmaI - RHS = S_m + C * ( MeSigmaI * S_e ) + RHS = s_m + C * ( MeSigmaI * s_e ) if self._makeASymmetric is True: MfMui = self.MfMui @@ -412,21 +407,21 @@ class Problem_b(BaseFDEMProblem): """ C = self.mesh.edgeCurl - S_m, S_e = src.eval(self) + s_m, s_e = src.eval(self) MfMui = self.MfMui if self._makeASymmetric and adjoint: v = self.MfMui * v - MeSigmaIDeriv = self.MeSigmaIDeriv(S_e) - S_mDeriv, S_eDeriv = src.evalDeriv(self, adjoint=adjoint) + MeSigmaIDeriv = self.MeSigmaIDeriv(s_e) + s_mDeriv, s_eDeriv = src.evalDeriv(self, adjoint=adjoint) if not adjoint: RHSderiv = C * (MeSigmaIDeriv * v) - SrcDeriv = S_mDeriv(v) + C * (self.MeSigmaI * S_eDeriv(v)) + SrcDeriv = s_mDeriv(v) + C * (self.MeSigmaI * s_eDeriv(v)) elif adjoint: RHSderiv = MeSigmaIDeriv.T * (C.T * v) - SrcDeriv = S_mDeriv(v) + self.MeSigmaI.T * (C.T * S_eDeriv(v)) + SrcDeriv = s_mDeriv(v) + self.MeSigmaI.T * (C.T * s_eDeriv(v)) if self._makeASymmetric is True and not adjoint: return MfMui.T * (SrcDeriv + RHSderiv) @@ -440,7 +435,7 @@ class Problem_b(BaseFDEMProblem): ########################################################################################## -class Problem_j(BaseFDEMProblem): +class Problem3D_j(BaseFDEMProblem): """ We eliminate \\\(\\\mathbf{h}\\\) using @@ -460,9 +455,9 @@ class Problem_j(BaseFDEMProblem): :param SimPEG.Mesh mesh: mesh """ - _fieldType = 'j' - _eqLocs = 'EF' - fieldsPair = Fields_j + _solutionType = 'jSolution' + _formulation = 'HJ' + fieldsPair = Fields3D_j def __init__(self, mesh, **kwargs): BaseFDEMProblem.__init__(self, mesh, **kwargs) @@ -537,11 +532,11 @@ class Problem_j(BaseFDEMProblem): :return: RHS """ - S_m, S_e = self.getSourceTerm(freq) + s_m, s_e = self.getSourceTerm(freq) C = self.mesh.edgeCurl MeMuI = self.MeMuI - RHS = C * (MeMuI * S_m) - 1j * omega(freq) * S_e + RHS = C * (MeMuI * s_m) - 1j * omega(freq) * s_e if self._makeASymmetric is True: MfRho = self.MfRho return MfRho.T*RHS @@ -562,16 +557,16 @@ class Problem_j(BaseFDEMProblem): C = self.mesh.edgeCurl MeMuI = self.MeMuI - S_mDeriv, S_eDeriv = src.evalDeriv(self, adjoint=adjoint) + s_mDeriv, s_eDeriv = src.evalDeriv(self, adjoint=adjoint) if adjoint: if self._makeASymmetric: MfRho = self.MfRho v = MfRho*v - return S_mDeriv(MeMuI.T * (C.T * v)) - 1j * omega(freq) * S_eDeriv(v) + return s_mDeriv(MeMuI.T * (C.T * v)) - 1j * omega(freq) * s_eDeriv(v) else: - RHSDeriv = C * (MeMuI * S_mDeriv(v)) - 1j * omega(freq) * S_eDeriv(v) + RHSDeriv = C * (MeMuI * s_mDeriv(v)) - 1j * omega(freq) * s_eDeriv(v) if self._makeASymmetric: MfRho = self.MfRho @@ -581,7 +576,7 @@ class Problem_j(BaseFDEMProblem): -class Problem_h(BaseFDEMProblem): +class Problem3D_h(BaseFDEMProblem): """ We eliminate \\\(\\\mathbf{j}\\\) using @@ -598,9 +593,9 @@ class Problem_h(BaseFDEMProblem): :param SimPEG.Mesh mesh: mesh """ - _fieldType = 'h' - _eqLocs = 'EF' - fieldsPair = Fields_h + _solutionType = 'hSolution' + _formulation = 'HJ' + fieldsPair = Fields3D_h def __init__(self, mesh, **kwargs): BaseFDEMProblem.__init__(self, mesh, **kwargs) @@ -659,11 +654,11 @@ class Problem_h(BaseFDEMProblem): :return: RHS (nE, nSrc) """ - S_m, S_e = self.getSourceTerm(freq) + s_m, s_e = self.getSourceTerm(freq) C = self.mesh.edgeCurl MfRho = self.MfRho - return S_m + C.T * ( MfRho * S_e ) + return s_m + C.T * ( MfRho * s_e ) def getRHSDeriv(self, freq, src, v, adjoint=False): """ @@ -677,17 +672,17 @@ class Problem_h(BaseFDEMProblem): :return: product of rhs deriv with a vector """ - _, S_e = src.eval(self) + _, s_e = src.eval(self) C = self.mesh.edgeCurl MfRho = self.MfRho - MfRhoDeriv = self.MfRhoDeriv(S_e) + 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=adjoint) + s_mDeriv, s_eDeriv = src.evalDeriv(self, adjoint=adjoint) - return RHSDeriv + S_mDeriv(v) + C.T * (MfRho * S_eDeriv(v)) + return RHSDeriv + s_mDeriv(v) + C.T * (MfRho * s_eDeriv(v)) diff --git a/SimPEG/EM/FDEM/FieldsFDEM.py b/SimPEG/EM/FDEM/FieldsFDEM.py index 8d8c16f4..47f37616 100644 --- a/SimPEG/EM/FDEM/FieldsFDEM.py +++ b/SimPEG/EM/FDEM/FieldsFDEM.py @@ -3,12 +3,12 @@ import scipy.sparse as sp import SimPEG from SimPEG import Utils from SimPEG.EM.Utils import omega -from SimPEG.Utils import Zero, Identity +from SimPEG.Utils import Zero, Identity, sdiag class Fields(SimPEG.Problem.Fields): """ - + Fancy Field Storage for a FDEM survey. Only one field type is stored for each problem, the rest are computed. The fields obejct acts like an array and is indexed by @@ -34,56 +34,56 @@ class Fields(SimPEG.Problem.Fields): def _e(self, solution, srcList): """ - Total electric field is sum of primary and secondary - + Total electric field is sum of primary and secondary + :param numpy.ndarray solution: field we solved for :param list srcList: list of sources :rtype: numpy.ndarray :return: total electric field """ - if getattr(self, '_ePrimary', None) is None or getattr(self, '_eSecondary', None) is None: + if getattr(self, '_ePrimary', None) is None or getattr(self, '_eSecondary', None) is None: raise NotImplementedError ('Getting e from %s is not implemented' %self.knownFields.keys()[0]) return self._ePrimary(solution,srcList) + self._eSecondary(solution,srcList) def _b(self, solution, srcList): """ - Total magnetic flux density is sum of primary and secondary - + Total magnetic flux density is sum of primary and secondary + :param numpy.ndarray solution: field we solved for :param list srcList: list of sources :rtype: numpy.ndarray - :return: total magnetic flux density + :return: total magnetic flux density """ - if getattr(self, '_bPrimary', None) is None or getattr(self, '_bSecondary', None) is None: + if getattr(self, '_bPrimary', None) is None or getattr(self, '_bSecondary', None) is None: raise NotImplementedError ('Getting b from %s is not implemented' %self.knownFields.keys()[0]) return self._bPrimary(solution, srcList) + self._bSecondary(solution, srcList) def _h(self, solution, srcList): """ - Total magnetic field is sum of primary and secondary - + Total magnetic field is sum of primary and secondary + :param numpy.ndarray solution: field we solved for :param list srcList: list of sources :rtype: numpy.ndarray :return: total magnetic field """ - if getattr(self, '_hPrimary', None) is None or getattr(self, '_hSecondary', None) is None: + if getattr(self, '_hPrimary', None) is None or getattr(self, '_hSecondary', None) is None: raise NotImplementedError ('Getting h from %s is not implemented' %self.knownFields.keys()[0]) return self._hPrimary(solution, srcList) + self._hSecondary(solution, srcList) def _j(self, solution, srcList): """ - Total current density is sum of primary and secondary - + Total current density is sum of primary and secondary + :param numpy.ndarray solution: field we solved for :param list srcList: list of sources :rtype: numpy.ndarray - :return: total current density + :return: total current density """ - if getattr(self, '_jPrimary', None) is None or getattr(self, '_jSecondary', None) is None: + if getattr(self, '_jPrimary', None) is None or getattr(self, '_jSecondary', None) is None: raise NotImplementedError ('Getting j from %s is not implemented' %self.knownFields.keys()[0]) return self._jPrimary(solution, srcList) + self._jSecondary(solution, srcList) @@ -99,12 +99,12 @@ class Fields(SimPEG.Problem.Fields): :rtype: numpy.ndarray :return: derivative times a vector (or tuple for adjoint) """ - if getattr(self, '_eDeriv_u', None) is None or getattr(self, '_eDeriv_m', None) is None: + if getattr(self, '_eDeriv_u', None) is None or getattr(self, '_eDeriv_m', None) is None: raise NotImplementedError ('Getting eDerivs from %s is not implemented' %self.knownFields.keys()[0]) if adjoint: return self._eDeriv_u(src, v, adjoint), self._eDeriv_m(src, v, adjoint) - return self._eDeriv_u(src, du_dm_v, adjoint) + self._eDeriv_m(src, v, adjoint) + return np.array(self._eDeriv_u(src, du_dm_v, adjoint) + self._eDeriv_m(src, v, adjoint), dtype = complex) def _bDeriv(self, src, du_dm_v, v, adjoint = False): """ @@ -117,12 +117,12 @@ class Fields(SimPEG.Problem.Fields): :rtype: numpy.ndarray :return: derivative times a vector (or tuple for adjoint) """ - if getattr(self, '_bDeriv_u', None) is None or getattr(self, '_bDeriv_m', None) is None: + if getattr(self, '_bDeriv_u', None) is None or getattr(self, '_bDeriv_m', None) is None: raise NotImplementedError ('Getting bDerivs from %s is not implemented' %self.knownFields.keys()[0]) if adjoint: return self._bDeriv_u(src, v, adjoint), self._bDeriv_m(src, v, adjoint) - return self._bDeriv_u(src, du_dm_v, adjoint) + self._bDeriv_m(src, v, adjoint) + return np.array(self._bDeriv_u(src, du_dm_v, adjoint) + self._bDeriv_m(src, v, adjoint), dtype = complex) def _hDeriv(self, src, du_dm_v, v, adjoint = False): """ @@ -135,12 +135,12 @@ class Fields(SimPEG.Problem.Fields): :rtype: numpy.ndarray :return: derivative times a vector (or tuple for adjoint) """ - if getattr(self, '_hDeriv_u', None) is None or getattr(self, '_hDeriv_m', None) is None: + if getattr(self, '_hDeriv_u', None) is None or getattr(self, '_hDeriv_m', None) is None: raise NotImplementedError ('Getting hDerivs from %s is not implemented' %self.knownFields.keys()[0]) - if adjoint: + if adjoint: return self._hDeriv_u(src, v, adjoint), self._hDeriv_m(src, v, adjoint) - return self._hDeriv_u(src, du_dm_v, adjoint) + self._hDeriv_m(src, v, adjoint) + return np.array(self._hDeriv_u(src, du_dm_v, adjoint) + self._hDeriv_m(src, v, adjoint), dtype = complex) def _jDeriv(self, src, du_dm_v, v, adjoint = False): """ @@ -153,20 +153,19 @@ class Fields(SimPEG.Problem.Fields): :rtype: numpy.ndarray :return: derivative times a vector (or tuple for adjoint) """ - if getattr(self, '_jDeriv_u', None) is None or getattr(self, '_jDeriv_m', None) is None: + if getattr(self, '_jDeriv_u', None) is None or getattr(self, '_jDeriv_m', None) is None: raise NotImplementedError ('Getting jDerivs from %s is not implemented' %self.knownFields.keys()[0]) if adjoint: return self._jDeriv_u(src, v, adjoint), self._jDeriv_m(src, v, adjoint) - return self._jDeriv_u(src, du_dm_v, adjoint) + self._jDeriv_m(src, v, adjoint) + return np.array(self._jDeriv_u(src, du_dm_v, adjoint) + self._jDeriv_m(src, v, adjoint), dtype = complex) - -class Fields_e(Fields): +class Fields3D_e(Fields): """ - Fields object for Problem_e. + Fields object for Problem3D_e. :param Mesh mesh: mesh - :param Survey survey: survey + :param Survey survey: survey """ knownFields = {'eSolution':'E'} @@ -176,15 +175,34 @@ class Fields_e(Fields): 'eSecondary' : ['eSolution','E','_eSecondary'], 'b' : ['eSolution','F','_b'], 'bPrimary' : ['eSolution','F','_bPrimary'], - 'bSecondary' : ['eSolution','F','_bSecondary'] + 'bSecondary' : ['eSolution','F','_bSecondary'], + 'j' : ['eSolution','CCV','_j'], + 'h' : ['eSolution','CCV','_h'], } - def __init__(self,mesh,survey,**kwargs): + def __init__(self, mesh, survey, **kwargs): Fields.__init__(self,mesh,survey,**kwargs) def startup(self): self.prob = self.survey.prob self._edgeCurl = self.survey.prob.mesh.edgeCurl + self._aveE2CCV = self.survey.prob.mesh.aveE2CCV + self._aveF2CCV = self.survey.prob.mesh.aveF2CCV + self._nC = self.survey.prob.mesh.nC + self._MeSigma = self.survey.prob.MeSigma + self._MeSigmaDeriv = self.survey.prob.MeSigmaDeriv + self._MfMui = self.survey.prob.MfMui + + def _GLoc(self, fieldType): + if fieldType == 'e': + return 'E' + elif fieldType == 'b': + return 'F' + elif (fieldType == 'h') or (fieldType == 'j'): + return 'CCV' + else: + raise Exception('Field type must be e, b, h, j') + def _ePrimary(self, eSolution, srcList): """ @@ -196,7 +214,7 @@ class Fields_e(Fields): :return: primary electric field as defined by the sources """ - ePrimary = np.zeros_like(eSolution) + ePrimary = np.zeros([self.prob.mesh.nE,len(srcList)], dtype = complex) for i, src in enumerate(srcList): ep = src.ePrimary(self.prob) ePrimary[:,i] = ePrimary[:,i] + ep @@ -211,15 +229,13 @@ class Fields_e(Fields): :rtype: numpy.ndarray :return: secondary electric field """ - return eSolution - def _eDeriv_u(self, src, v, adjoint = False): """ - Partial derivative of the total electric field with respect to the thing we + Partial derivative of the total electric field with respect to the thing we solved for. - + :param SimPEG.EM.FDEM.Src src: source :param numpy.ndarray v: vector to take product with :param bool adjoint: adjoint? @@ -231,8 +247,8 @@ class Fields_e(Fields): def _eDeriv_m(self, src, v, adjoint = False): """ - Partial derivative of the total electric field with respect to the inversion model. Here, we assume that the primary does not depend on the model. Note that this also includes derivative contributions from the sources. - + Partial derivative of the total electric field with respect to the inversion model. Here, we assume that the primary does not depend on the model. Note that this also includes derivative contributions from the sources. + :param SimPEG.EM.FDEM.Src src: source :param numpy.ndarray v: vector to take product with :param bool adjoint: adjoint? @@ -253,7 +269,7 @@ class Fields_e(Fields): :return: primary magnetic flux density as defined by the sources """ - bPrimary = np.zeros([self._edgeCurl.shape[0],eSolution.shape[1]],dtype = complex) + bPrimary = np.zeros([self._edgeCurl.shape[0],eSolution.shape[1]], dtype = complex) for i, src in enumerate(srcList): bp = src.bPrimary(self.prob) bPrimary[:,i] = bPrimary[:,i] + bp @@ -273,45 +289,14 @@ class Fields_e(Fields): b = (C * eSolution) for i, src in enumerate(srcList): b[:,i] *= - 1./(1j*omega(src.freq)) - S_m, _ = src.eval(self.prob) - b[:,i] = b[:,i]+ 1./(1j*omega(src.freq)) * S_m + s_m, _ = src.eval(self.prob) + b[:,i] = b[:,i]+ 1./(1j*omega(src.freq)) * s_m return b - def _bSecondaryDeriv_u(self, src, du_dm_v, adjoint = False): - """ - Derivative of the secondary magnetic flux density with respect to the thing we solved for - - :param SimPEG.EM.FDEM.Src src: source - :param numpy.ndarray du_dm_v: vector to take product with - :param bool adjoint: adjoint? - :rtype: numpy.ndarray - :return: product of the derivative of the secondary magnetic flux density with respect to the field we solved for with a vector + def _bDeriv_u(self, src, du_dm_v, adjoint = False): """ + Derivative of the magnetic flux density with respect to the thing we solved for - C = self._edgeCurl - if adjoint: - return - 1./(1j*omega(src.freq)) * (C.T * du_dm_v) - return - 1./(1j*omega(src.freq)) * (C * du_dm_v) - - def _bSecondaryDeriv_m(self, src, v, adjoint = False): - """ - Derivative of the secondary magnetic flux density with respect to the inversion model. - - :param SimPEG.EM.FDEM.Src src: source - :param numpy.ndarray v: vector to take product with - :param bool adjoint: adjoint? - :rtype: numpy.ndarray - :return: product of the secondary magnetic flux density derivative with respect to the inversion model with a vector - """ - - S_mDeriv, _ = src.evalDeriv(self.prob, v, adjoint) - return 1./(1j * omega(src.freq)) * S_mDeriv - - - def _bDeriv_u(self, src, du_dm_v, adjoint=False): - """ - Partial derivative of the total magnetic flux density with respect to the thing we solved for - :param SimPEG.EM.FDEM.Src src: source :param numpy.ndarray du_dm_v: vector to take product with :param bool adjoint: adjoint? @@ -319,29 +304,134 @@ class Fields_e(Fields): :return: product of the derivative of the magnetic flux density with respect to the field we solved for with a vector """ - return self._bSecondaryDeriv_u(src, du_dm_v, adjoint) + C = self._edgeCurl + if adjoint: + return - 1./(1j*omega(src.freq)) * (C.T * du_dm_v) + return - 1./(1j*omega(src.freq)) * (C * du_dm_v) - def _bDeriv_m(self, src, v, adjoint=False): + + def _bDeriv_m(self, src, v, adjoint = False): """ - Partial derivative of the total magnetic flux density with respect to the inversion model. - + Derivative of the magnetic flux density with respect to the inversion model. + :param SimPEG.EM.FDEM.Src src: source :param numpy.ndarray v: vector to take product with :param bool adjoint: adjoint? - :rtype: SimPEG.Utils.Zero + :rtype: numpy.ndarray :return: product of the magnetic flux density derivative with respect to the inversion model with a vector """ - # Assuming the primary does not depend on the model - return self._bSecondaryDeriv_m(src, v, adjoint) + s_mDeriv, _ = src.evalDeriv(self.prob, v, adjoint) + return 1./(1j * omega(src.freq)) * s_mDeriv + + def _j(self, eSolution, srcList): + """ + Current density from eSolution + + :param numpy.ndarray eSolution: field we solved for + :param list srcList: list of sources + :rtype: numpy.ndarray + :return: current density + """ + aveE2CCV = self._aveE2CCV + n = int(aveE2CCV.shape[0] / self._nC) # number of components (instead of checking if cyl or not) + VI = sdiag(np.kron(np.ones(n), 1./self.prob.mesh.vol)) + return VI * (aveE2CCV * (self._MeSigma * self._e(eSolution,srcList) ) ) + + def _jDeriv_u(self, src, du_dm_v, adjoint = False): + """ + Derivative of the current density with respect to the thing we solved for + + :param SimPEG.EM.FDEM.Src src: source + :param numpy.ndarray du_dm_v: vector to take product with + :param bool adjoint: adjoint? + :rtype: numpy.ndarray + :return: product of the derivative of the current density with respect to the field we solved for with a vector + """ + n = int(self._aveE2CCV.shape[0] / self._nC) # number of components (instead of checking if cyl or not) + VI = sdiag(np.kron(np.ones(n), 1./self.prob.mesh.vol)) + + if adjoint: + return self._eDeriv_u(src, self._MeSigma.T * (self._aveE2CCV.T * (VI.T * du_dm_v) ), adjoint = adjoint) + return VI * (self._aveE2CCV * (self._MeSigma * (self._eDeriv_u(src, du_dm_v, adjoint=adjoint) ) ) ) -class Fields_b(Fields): + def _jDeriv_m(self, src, v, adjoint = False): + """ + Derivative of the current density with respect to the inversion model. + + :param SimPEG.EM.FDEM.Src src: source + :param numpy.ndarray v: vector to take product with + :param bool adjoint: adjoint? + :rtype: numpy.ndarray + :return: product of the current density derivative with respect to the inversion model with a vector + """ + e = self[src, 'e'] + n = int(self._aveE2CCV.shape[0] / self._nC) #number of components + VI = sdiag(np.kron(np.ones(n), 1./self.prob.mesh.vol)) + + if adjoint: + return self._MeSigmaDeriv(e).T * (self._aveE2CCV.T * (VI.T * v)) + self._eDeriv_m(src, self._aveE2CCV.T * (VI.T * v), adjoint=adjoint) + return VI * (self._aveE2CCV * ( self._eDeriv_m(src, v, adjoint=adjoint) + self._MeSigmaDeriv(e) * v)) + + + + def _h(self, eSolution, srcList): + """ + Magnetic field from eSolution + + :param numpy.ndarray eSolution: field we solved for + :param list srcList: list of sources + :rtype: numpy.ndarray + :return: magnetic field + """ + n = int(self._aveF2CCV.shape[0] / self._nC) # Number of Components + VI = sdiag(np.kron(np.ones(n), 1./self.prob.mesh.vol)) + + return VI * (self._aveF2CCV * (self._MfMui * self._b(eSolution, srcList))) + + def _hDeriv_u(self, src, du_dm_v, adjoint = False): + """ + Derivative of the magnetic field with respect to the thing we solved for + + :param SimPEG.EM.FDEM.Src src: source + :param numpy.ndarray du_dm_v: vector to take product with + :param bool adjoint: adjoint? + :rtype: numpy.ndarray + :return: product of the derivative of the magnetic field with respect to the field we solved for with a vector + """ + n = int(self._aveF2CCV.shape[0] / self._nC) # Number of Components + VI = sdiag(np.kron(np.ones(n), 1./self.prob.mesh.vol)) + if adjoint: + v = self._MfMui.T * (self._aveF2CCV.T * (VI.T * du_dm_v)) + return self._bDeriv_u(src, v, adjoint=adjoint) + return VI * (self._aveF2CCV * (self._MfMui * self._bDeriv_u(src, du_dm_v, adjoint = adjoint))) + + def _hDeriv_m(self, src, v, adjoint = False): + """ + Derivative of the magnetic field with respect to the inversion model. + + :param SimPEG.EM.FDEM.Src src: source + :param numpy.ndarray v: vector to take product with + :param bool adjoint: adjoint? + :rtype: numpy.ndarray + :return: product of the magnetic field derivative with respect to the inversion model with a vector + """ + n = int(self._aveF2CCV.shape[0] / self._nC) # Number of Components + VI = sdiag(np.kron(np.ones(n), 1./self.prob.mesh.vol)) + if adjoint: + v = self._MfMui.T * (self._aveF2CCV.T * (VI.T * v)) + return self._bDeriv_m(src, v, adjoint=adjoint) + return VI * (self._aveF2CCV * (self._MfMui * self._bDeriv_m(src, v, adjoint = adjoint))) + + + +class Fields3D_b(Fields): """ - Fields object for Problem_b. + Fields object for Problem3D_b. :param Mesh mesh: mesh - :param Survey survey: survey + :param Survey survey: survey """ knownFields = {'bSolution':'F'} @@ -352,6 +442,8 @@ class Fields_b(Fields): 'e' : ['bSolution','E','_e'], 'ePrimary' : ['bSolution','E','_ePrimary'], 'eSecondary' : ['bSolution','E','_eSecondary'], + 'j' : ['bSolution','CCV','_j'], + 'h' : ['bSolution','CCV','_h'], } def __init__(self,mesh,survey,**kwargs): @@ -360,10 +452,29 @@ class Fields_b(Fields): def startup(self): self.prob = self.survey.prob self._edgeCurl = self.survey.prob.mesh.edgeCurl + self._MeSigma = self.survey.prob.MeSigma self._MeSigmaI = self.survey.prob.MeSigmaI self._MfMui = self.survey.prob.MfMui + self._MeSigmaDeriv = self.survey.prob.MeSigmaDeriv self._MeSigmaIDeriv = self.survey.prob.MeSigmaIDeriv self._Me = self.survey.prob.Me + self._aveF2CCV = self.survey.prob.mesh.aveF2CCV + self._aveE2CCV = self.survey.prob.mesh.aveE2CCV + self._sigma = self.survey.prob.curModel.sigma + self._mui = self.survey.prob.curModel.mui + self._nC = self.survey.prob.mesh.nC + + + + def _GLoc(self,fieldType): + if fieldType == 'e': + return 'E' + elif fieldType == 'b': + return 'F' + elif (fieldType == 'h') or (fieldType == 'j'): + return'CCV' + else: + raise Exception('Field type must be e, b, h, j') def _bPrimary(self, bSolution, srcList): """ @@ -375,7 +486,7 @@ class Fields_b(Fields): :return: primary electric field as defined by the sources """ - bPrimary = np.zeros_like(bSolution) + bPrimary = np.zeros([self.prob.mesh.nF,len(srcList)], dtype = complex) for i, src in enumerate(srcList): bp = src.bPrimary(self.prob) bPrimary[:,i] = bPrimary[:,i] + bp @@ -395,9 +506,9 @@ class Fields_b(Fields): def _bDeriv_u(self, src, du_dm_v, adjoint=False): """ - Partial derivative of the total magnetic flux density with respect to the thing we + Partial derivative of the total magnetic flux density with respect to the thing we solved for. - + :param SimPEG.EM.FDEM.Src src: source :param numpy.ndarray du_dm_v: vector to take product with :param bool adjoint: adjoint? @@ -409,8 +520,8 @@ class Fields_b(Fields): def _bDeriv_m(self, src, v, adjoint=False): """ - Partial derivative of the total magnetic flux density with respect to the inversion model. Here, we assume that the primary does not depend on the model. Note that this also includes derivative contributions from the sources. - + Partial derivative of the total magnetic flux density with respect to the inversion model. Here, we assume that the primary does not depend on the model. Note that this also includes derivative contributions from the sources. + :param SimPEG.EM.FDEM.Src src: source :param numpy.ndarray v: vector to take product with :param bool adjoint: adjoint? @@ -431,7 +542,7 @@ class Fields_b(Fields): :return: primary electric field as defined by the sources """ - ePrimary = np.zeros([self._edgeCurl.shape[1],bSolution.shape[1]],dtype = complex) + ePrimary = np.zeros([self._edgeCurl.shape[1],bSolution.shape[1]], dtype = complex) for i,src in enumerate(srcList): ep = src.ePrimary(self.prob) ePrimary[:,i] = ePrimary[:,i] + ep @@ -447,94 +558,147 @@ class Fields_b(Fields): :return: secondary electric field """ - e = self._MeSigmaI * ( self._edgeCurl.T * ( self._MfMui * bSolution)) + e = ( self._edgeCurl.T * ( self._MfMui * bSolution)) for i,src in enumerate(srcList): - _,S_e = src.eval(self.prob) - e[:,i] = e[:,i]+ -self._MeSigmaI * S_e - return e + _,s_e = src.eval(self.prob) + e[:,i] = e[:,i] + - s_e - def _eSecondaryDeriv_u(self, src, du_dm_v, adjoint=False): - """ - Derivative of the secondary electric field with respect to the thing we solved for - - :param SimPEG.EM.FDEM.Src src: source - :param numpy.ndarray v: vector to take product with - :param bool adjoint: adjoint? - :rtype: numpy.ndarray - :return: product of the derivative of the secondary electric field with respect to the field we solved for with a vector - """ - - if not adjoint: - return self._MeSigmaI * ( self._edgeCurl.T * ( self._MfMui * du_dm_v) ) - else: - return self._MfMui.T * (self._edgeCurl * (self._MeSigmaI.T * du_dm_v)) - - def _eSecondaryDeriv_m(self, src, v, adjoint=False): - """ - Derivative of the secondary electric field with respect to the inversion model - - :param SimPEG.EM.FDEM.Src src: source - :param numpy.ndarray v: vector to take product with - :param bool adjoint: adjoint? - :rtype: numpy.ndarray - :return: product of the derivative of the secondary electric field with respect to the model with a vector - """ - - bSolution = self[[src],'bSolution'] - _,S_e = src.eval(self.prob) - Me = self._Me - - if adjoint: - Me = Me.T - - w = self._edgeCurl.T * (self._MfMui * bSolution) - w = 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, v, adjoint) - - de_dm = de_dm - self._MeSigmaI * S_eDeriv - - return de_dm + return self._MeSigmaI * e def _eDeriv_u(self, src, du_dm_v, adjoint=False): """ - Partial derivative of the total electric field with respect to the thing we solved for - + Derivative of the electric field with respect to the thing we solved for + :param SimPEG.EM.FDEM.Src src: source - :param numpy.ndarray du_dm_v: vector to take product with + :param numpy.ndarray v: vector to take product with :param bool adjoint: adjoint? :rtype: numpy.ndarray :return: product of the derivative of the electric field with respect to the field we solved for with a vector """ - return self._eSecondaryDeriv_u(src, du_dm_v, adjoint) + if not adjoint: + return self._MeSigmaI * ( self._edgeCurl.T * ( self._MfMui * du_dm_v) ) + return self._MfMui.T * (self._edgeCurl * (self._MeSigmaI.T * du_dm_v)) + def _eDeriv_m(self, src, v, adjoint=False): """ - Partial derivative of the total electric field density with respect to the inversion model. - + Derivative of the electric field with respect to the inversion model + :param SimPEG.EM.FDEM.Src src: source :param numpy.ndarray v: vector to take product with :param bool adjoint: adjoint? :rtype: numpy.ndarray - :return: product of the electric field derivative with respect to the inversion model with a vector + :return: product of the derivative of the electric field with respect to the model with a vector """ - # assuming primary doesn't depend on model - return self._eSecondaryDeriv_m(src, v, adjoint) + bSolution = Utils.mkvc(self[src, 'bSolution']) + _,s_e = src.eval(self.prob) + + w = -s_e + self._edgeCurl.T * (self._MfMui * bSolution) + _, s_eDeriv = src.evalDeriv(self.prob, v, adjoint) -class Fields_j(Fields): + if adjoint: + return self._MeSigmaIDeriv(w).T * v - self._MeSigmaI.T * s_eDeriv + return self._MeSigmaIDeriv(w) * v - self._MeSigmaI * s_eDeriv + + def _j(self, bSolution, srcList): + """ + Secondary current density from bSolution + + :param numpy.ndarray bSolution: field we solved for + :param list srcList: list of sources + :rtype: numpy.ndarray + :return: primary current density + """ + + n = int(self._aveE2CCV.shape[0] / self._nC) # number of components + VI = sdiag(np.kron(np.ones(n), 1./self.prob.mesh.vol)) + + return VI * (self._aveE2CCV * ( self._MeSigma * self._e(bSolution,srcList ) ) ) + + + def _jDeriv_u(self, src, du_dm_v, adjoint=False): + """ + Partial derivative of the current density with respect to the thing we + solved for. + + :param SimPEG.EM.FDEM.Src src: source + :param numpy.ndarray du_dm_v: vector to take product with + :param bool adjoint: adjoint? + :rtype: numpy.ndarray + :return: product of the derivative of the current density with respect to the field we solved for with a vector + """ + n = int(self._aveE2CCV.shape[0] / self._nC) # number of components + VI = sdiag(np.kron(np.ones(n), 1./self.prob.mesh.vol)) + if adjoint: + return self._MfMui.T * ( self._edgeCurl * ( self._aveE2CCV.T * (VI.T * du_dm_v) ) ) + return VI * (self._aveE2CCV * (self._edgeCurl.T * ( self._MfMui * du_dm_v ) ) ) + + + def _jDeriv_m(self, src, v, adjoint=False): + """ + Derivative of the current density with respect to the inversion model + + :param SimPEG.EM.FDEM.Src src: source + :param numpy.ndarray v: vector to take product with + :param bool adjoint: adjoint? + :rtype: numpy.ndarray + :return: product of the derivative of the current density with respect to the model with a vector + """ + return Zero() + + def _h(self, bSolution, srcList): + """ + Magnetic field from bSolution + + :param numpy.ndarray bSolution: field we solved for + :param list srcList: list of sources + :rtype: numpy.ndarray + :return: magnetic field + """ + n = int(self._aveF2CCV.shape[0] / self._nC) #number of components + VI = sdiag(np.kron(np.ones(n), 1./self.prob.mesh.vol)) + return VI * (self._aveF2CCV * (self._MfMui * self._b(bSolution, srcList))) + + def _hDeriv_u(self, src, du_dm_v, adjoint=False): + """ + Partial derivative of the magnetic field with respect to the thing we + solved for. + + :param SimPEG.EM.FDEM.Src src: source + :param numpy.ndarray du_dm_v: vector to take product with + :param bool adjoint: adjoint? + :rtype: numpy.ndarray + :return: product of the derivative of the magnetic field with respect to the field we solved for with a vector + """ + n = int(self._aveF2CCV.shape[0] / self._nC) #number of components + VI = sdiag(np.kron(np.ones(n), 1./self.prob.mesh.vol)) + + if adjoint: + return self._MfMui.T * ( self._aveF2CCV.T * ( VI.T * du_dm_v) ) + return VI * (self._aveF2CCV * (self._MfMui * du_dm_v)) + + def _hDeriv_m(self, src, v, adjoint=False): + """ + Derivative of the magnetic field with respect to the inversion model + + :param SimPEG.EM.FDEM.Src src: source + :param numpy.ndarray v: vector to take product with + :param bool adjoint: adjoint? + :rtype: numpy.ndarray + :return: product of the derivative of the magnetic field with respect to the model with a vector + """ + return Zero() + + +class Fields3D_j(Fields): """ - Fields object for Problem_j. + Fields object for Problem3D_j. :param Mesh mesh: mesh - :param Survey survey: survey + :param Survey survey: survey """ knownFields = {'jSolution':'F'} @@ -545,6 +709,8 @@ class Fields_j(Fields): 'h' : ['jSolution','E','_h'], 'hPrimary' : ['jSolution','E','_hPrimary'], 'hSecondary' : ['jSolution','E','_hSecondary'], + 'e' : ['jSolution','CCV','_e'], + 'b' : ['jSolution','CCV','_b'], } def __init__(self,mesh,survey,**kwargs): @@ -553,10 +719,25 @@ class Fields_j(Fields): def startup(self): self.prob = self.survey.prob self._edgeCurl = self.survey.prob.mesh.edgeCurl + self._MeMu = self.survey.prob.MeMu self._MeMuI = self.survey.prob.MeMuI self._MfRho = self.survey.prob.MfRho self._MfRhoDeriv = self.survey.prob.MfRhoDeriv - self._Me = self.survey.prob.Me + self._rho = self.survey.prob.curModel.rho + self._mu = self.survey.prob.curModel.mui + self._aveF2CCV = self.survey.prob.mesh.aveF2CCV + self._aveE2CCV = self.survey.prob.mesh.aveE2CCV + self._nC = self.survey.prob.mesh.nC + + def _GLoc(self,fieldType): + if fieldType == 'h': + return 'E' + elif fieldType == 'j': + return 'F' + elif (fieldType == 'e') or (fieldType == 'b'): + return 'CCV' + else: + raise Exception('Field type must be e, b, h, j') def _jPrimary(self, jSolution, srcList): """ @@ -568,7 +749,7 @@ class Fields_j(Fields): :return: primary current density as defined by the sources """ - jPrimary = np.zeros_like(jSolution,dtype = complex) + jPrimary = np.zeros_like(jSolution, dtype = complex) for i, src in enumerate(srcList): jp = src.jPrimary(self.prob) jPrimary[:,i] = jPrimary[:,i] + jp @@ -588,12 +769,12 @@ class Fields_j(Fields): def _j(self, jSolution, srcList): """ - Total current density is sum of primary and secondary - + Total current density is sum of primary and secondary + :param numpy.ndarray jSolution: field we solved for :param list srcList: list of sources :rtype: numpy.ndarray - :return: total current density + :return: total current density """ return self._jPrimary(jSolution, srcList) + self._jSecondary(jSolution, srcList) @@ -601,9 +782,9 @@ class Fields_j(Fields): def _jDeriv_u(self, src, du_dm_v, adjoint=False): """ - Partial derivative of the total current density with respect to the thing we + Partial derivative of the total current density with respect to the thing we solved for. - + :param SimPEG.EM.FDEM.Src src: source :param numpy.ndarray v: vector to take product with :param bool adjoint: adjoint? @@ -616,8 +797,8 @@ class Fields_j(Fields): def _jDeriv_m(self, src, v, adjoint=False): """ - Partial derivative of the total current density with respect to the inversion model. Here, we assume that the primary does not depend on the model. Note that this also includes derivative contributions from the sources. - + Partial derivative of the total current density with respect to the inversion model. Here, we assume that the primary does not depend on the model. Note that this also includes derivative contributions from the sources. + :param SimPEG.EM.FDEM.Src src: source :param numpy.ndarray v: vector to take product with :param bool adjoint: adjoint? @@ -653,68 +834,18 @@ class Fields_j(Fields): :return: secondary magnetic field """ - h = self._MeMuI * (self._edgeCurl.T * (self._MfRho * jSolution) ) + h = (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) - h[:,i] = h[:,i]+ 1./(1j*omega(src.freq)) * self._MeMuI * (S_m) - return h - - - def _hSecondaryDeriv_u(self, src, du_dm_v, adjoint=False): - """ - Derivative of the secondary magnetic field with respect to the thing we solved for - - :param SimPEG.EM.FDEM.Src src: source - :param numpy.ndarray du_dm_v: vector to take product with - :param bool adjoint: adjoint? - :rtype: numpy.ndarray - :return: product of the derivative of the secondary magnetic field with respect to the field we solved for with a vector - """ - - if not adjoint: - return -1./(1j*omega(src.freq)) * self._MeMuI * (self._edgeCurl.T * (self._MfRho * du_dm_v) ) - elif adjoint: - return -1./(1j*omega(src.freq)) * self._MfRho.T * (self._edgeCurl * ( self._MeMuI.T * du_dm_v)) - - def _hSecondaryDeriv_m(self, src, v, adjoint=False): - """ - Derivative of the secondary magnetic field with respect to the inversion model - - :param SimPEG.EM.FDEM.Src src: source - :param numpy.ndarray v: vector to take product with - :param bool adjoint: adjoint? - :rtype: numpy.ndarray - :return: product of the derivative of the secondary magnetic field with respect to the model with a vector - """ - - jSolution = self[[src],'jSolution'] - 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 = adjoint) - - if not adjoint: - S_mDeriv = S_mDeriv(v) - hDeriv_m = hDeriv_m + 1./(1j*omega(src.freq)) * MeMuI * (Me * S_mDeriv) - elif adjoint: - S_mDeriv = S_mDeriv(Me.T * (MeMuI.T * v)) - hDeriv_m = hDeriv_m + 1./(1j*omega(src.freq)) * S_mDeriv - return hDeriv_m + s_m,_ = src.eval(self.prob) + h[:,i] = h[:,i] + 1./(1j*omega(src.freq)) * (s_m) + return self._MeMuI * h def _hDeriv_u(self, src, du_dm_v, adjoint=False): """ - Partial derivative of the total magnetic field with respect to the thing we solved for - + Derivative of the magnetic field with respect to the thing we solved for + :param SimPEG.EM.FDEM.Src src: source :param numpy.ndarray du_dm_v: vector to take product with :param bool adjoint: adjoint? @@ -722,29 +853,147 @@ class Fields_j(Fields): :return: product of the derivative of the magnetic field with respect to the field we solved for with a vector """ - return self._hSecondaryDeriv_u(src, du_dm_v, adjoint) + if adjoint: + return -1./(1j*omega(src.freq)) * self._MfRho.T * (self._edgeCurl * ( self._MeMuI.T * du_dm_v)) + return -1./(1j*omega(src.freq)) * self._MeMuI * (self._edgeCurl.T * (self._MfRho * du_dm_v) ) + + def _hDeriv_m(self, src, v, adjoint=False): """ - Partial derivative of the total magnetic field density with respect to the inversion model. - + Derivative of the magnetic field with respect to the inversion model + :param SimPEG.EM.FDEM.Src src: source :param numpy.ndarray v: vector to take product with :param bool adjoint: adjoint? :rtype: numpy.ndarray - :return: product of the magnetic field derivative with respect to the inversion model with a vector + :return: product of the derivative of the magnetic field with respect to the model with a vector """ - # assuming the primary doesn't depend on the model - return self._hSecondaryDeriv_m(src, v, adjoint) + jSolution = Utils.mkvc(self[[src],'jSolution']) + MeMuI = self._MeMuI + C = self._edgeCurl + MfRho = self._MfRho + MfRhoDeriv = self._MfRhoDeriv + s_mDeriv,_ = src.evalDeriv(self.prob, adjoint = adjoint) + + if not adjoint: + hDeriv_m = -1./(1j*omega(src.freq)) * MeMuI * (C.T * (MfRhoDeriv(jSolution)*v ) ) + s_mDeriv = s_mDeriv(v) + hDeriv_m = hDeriv_m + 1./(1j*omega(src.freq)) * MeMuI * ( s_mDeriv) + + elif adjoint: + hDeriv_m = -1./(1j*omega(src.freq)) * MfRhoDeriv(jSolution).T * ( C * (MeMuI.T * v ) ) + + s_mDeriv = s_mDeriv(MeMuI.T * v) + hDeriv_m = hDeriv_m + 1./(1j*omega(src.freq)) * s_mDeriv + + return hDeriv_m + + def _e(self, jSolution, srcList): + """ + Electric field from jSolution + + :param numpy.ndarray hSolution: field we solved for + :param list srcList: list of sources + :rtype: numpy.ndarray + :return: electric field + """ + n = int(self._aveF2CCV.shape[0] / self._nC) # number of components + VI = sdiag(np.kron(np.ones(n), 1./self.prob.mesh.vol)) + return VI * (self._aveF2CCV * (self._MfRho * self._j(jSolution, srcList))) + + def _eDeriv_u(self, src, du_dm_v, adjoint=False): + """ + Derivative of the electric field with respect to the thing we solved for + + :param SimPEG.EM.FDEM.Src src: source + :param numpy.ndarray du_dm_v: vector to take product with + :param bool adjoint: adjoint? + :rtype: numpy.ndarray + :return: product of the derivative of the electric field with respect to the field we solved for with a vector + """ + n = int(self._aveF2CCV.shape[0] / self._nC) # number of components + VI = sdiag(np.kron(np.ones(n), 1./self.prob.mesh.vol)) + if adjoint: + return self._MfRho.T * ( self._aveF2CCV.T * ( VI.T * du_dm_v ) ) + return VI * (self._aveF2CCV * (self._MfRho * du_dm_v)) + + def _eDeriv_m(self, src, v, adjoint=False): + """ + Derivative of the electric field with respect to the inversion model + + :param SimPEG.EM.FDEM.Src src: source + :param numpy.ndarray v: vector to take product with + :param bool adjoint: adjoint? + :rtype: numpy.ndarray + :return: product of the derivative of the electric field with respect to the model with a vector + """ + jSolution = Utils.mkvc(self[src,'jSolution']) + n = int(self._aveF2CCV.shape[0] / self._nC) # number of components + VI = sdiag(np.kron(np.ones(n), 1./self.prob.mesh.vol)) + if adjoint: + return self._MfRhoDeriv(jSolution).T * ( self._aveF2CCV.T * ( VI.T * v ) ) + return VI * (self._aveF2CCV * (self._MfRhoDeriv(jSolution) * v)) + + def _b(self, jSolution, srcList): + """ + Secondary magnetic flux density from jSolution + + :param numpy.ndarray hSolution: field we solved for + :param list srcList: list of sources + :rtype: numpy.ndarray + :return: secondary magnetic flux density + """ + n = int(self._aveE2CCV.shape[0] / self._nC) # number of components + VI = sdiag(np.kron(np.ones(n), 1./self.prob.mesh.vol)) + + return VI * (self._aveE2CCV * ( self._MeMu * self._h(jSolution,srcList)) ) + + def _bDeriv_u(self, src, du_dm_v, adjoint=False): + """ + Derivative of the magnetic flux density with respect to the thing we solved for + + :param SimPEG.EM.FDEM.Src src: source + :param numpy.ndarray du_dm_v: vector to take product with + :param bool adjoint: adjoint? + :rtype: numpy.ndarray + :return: product of the derivative of the magnetic flux density with respect to the field we solved for with a vector + """ + n = int(self._aveF2CCV.shape[0] / self._nC) # number of components + VI = sdiag(np.kron(np.ones(n), 1./self.prob.mesh.vol)) + + if adjoint: + return -1./(1j*omega(src.freq)) * self._MfRho.T * ( self._edgeCurl * ( self._aveE2CCV.T * (VI.T * du_dm_v) ) ) + return -1./(1j*omega(src.freq)) * VI * (self._aveE2CCV * (self._edgeCurl.T * (self._MfRho * du_dm_v))) + + def _bDeriv_m(self, src, v, adjoint=False): + """ + Derivative of the magnetic flux density with respect to the inversion model + + :param SimPEG.EM.FDEM.Src src: source + :param numpy.ndarray v: vector to take product with + :param bool adjoint: adjoint? + :rtype: numpy.ndarray + :return: product of the derivative of the magnetic flux density with respect to the model with a vector + """ + jSolution = self[src,'jSolution'] + n = int(self._aveE2CCV.shape[0] / self._nC) # number of components + VI = sdiag(np.kron(np.ones(n), 1./self.prob.mesh.vol)) + s_mDeriv,_ = src.evalDeriv(self.prob, adjoint = adjoint) + + if adjoint: + v = self._aveE2CCV.T * ( VI.T * v) + return 1./(1j * omega(src.freq)) * ( s_mDeriv(v) - self._MfRhoDeriv(jSolution).T * (self._edgeCurl * v )) + return 1./(1j * omega(src.freq)) * VI * (self._aveE2CCV * ( s_mDeriv(v) - self._edgeCurl.T * ( self._MfRhoDeriv(jSolution) * v ) ) ) -class Fields_h(Fields): +class Fields3D_h(Fields): """ - Fields object for Problem_h. + Fields object for Problem3D_h. :param Mesh mesh: mesh - :param Survey survey: survey + :param Survey survey: survey """ knownFields = {'hSolution':'E'} @@ -754,7 +1003,9 @@ class Fields_h(Fields): 'hSecondary' : ['hSolution','E','_hSecondary'], 'j' : ['hSolution','F','_j'], 'jPrimary' : ['hSolution','F','_jPrimary'], - 'jSecondary' : ['hSolution','F','_jSecondary'] + 'jSecondary' : ['hSolution','F','_jSecondary'], + 'e' : ['hSolution','CCV','_e'], + 'b' : ['hSolution','CCV','_b'], } def __init__(self,mesh,survey,**kwargs): @@ -763,8 +1014,25 @@ class Fields_h(Fields): def startup(self): self.prob = self.survey.prob self._edgeCurl = self.survey.prob.mesh.edgeCurl + self._MeMu = self.survey.prob.MeMu self._MeMuI = self.survey.prob.MeMuI self._MfRho = self.survey.prob.MfRho + self._MfRhoDeriv = self.survey.prob.MfRhoDeriv + self._rho = self.survey.prob.curModel.rho + self._mu = self.survey.prob.curModel.mui + self._aveF2CCV = self.survey.prob.mesh.aveF2CCV + self._aveE2CCV = self.survey.prob.mesh.aveE2CCV + self._nC = self.survey.prob.mesh.nC + + def _GLoc(self,fieldType): + if fieldType == 'h': + return 'E' + elif fieldType == 'j': + return 'F' + elif (fieldType == 'e') or (fieldType == 'b'): + return 'CCV' + else: + raise Exception('Field type must be e, b, h, j') def _hPrimary(self, hSolution, srcList): """ @@ -797,9 +1065,9 @@ class Fields_h(Fields): def _hDeriv_u(self, src, du_dm_v, adjoint=False): """ - Partial derivative of the total magnetic field with respect to the thing we + Partial derivative of the total magnetic field with respect to the thing we solved for. - + :param SimPEG.EM.FDEM.Src src: source :param numpy.ndarray du_dm_v: vector to take product with :param bool adjoint: adjoint? @@ -811,8 +1079,8 @@ class Fields_h(Fields): def _hDeriv_m(self, src, v, adjoint=False): """ - Partial derivative of the total magnetic field with respect to the inversion model. Here, we assume that the primary does not depend on the model. Note that this also includes derivative contributions from the sources. - + Partial derivative of the total magnetic field with respect to the inversion model. Here, we assume that the primary does not depend on the model. Note that this also includes derivative contributions from the sources. + :param SimPEG.EM.FDEM.Src src: source :param numpy.ndarray v: vector to take product with :param bool adjoint: adjoint? @@ -841,7 +1109,7 @@ class Fields_h(Fields): def _jSecondary(self, hSolution, srcList): """ - Secondary current density from eSolution + Secondary current density from hSolution :param numpy.ndarray hSolution: field we solved for :param list srcList: list of sources @@ -851,45 +1119,14 @@ class Fields_h(Fields): j = self._edgeCurl*hSolution for i, src in enumerate(srcList): - _,S_e = src.eval(self.prob) - j[:,i] = j[:,i]+ -S_e + _,s_e = src.eval(self.prob) + j[:,i] = j[:,i]+ -s_e return j - def _jSecondaryDeriv_u(self, src, du_dm_v, adjoint=False): - """ - Derivative of the secondary current density with respect to the thing we solved for - - :param SimPEG.EM.FDEM.Src src: source - :param numpy.ndarray du_dm_v: vector to take product with - :param bool adjoint: adjoint? - :rtype: numpy.ndarray - :return: product of the derivative of the secondary current density with respect to the field we solved for with a vector - """ - - if not adjoint: - return self._edgeCurl*du_dm_v - elif adjoint: - return self._edgeCurl.T*du_dm_v - - def _jSecondaryDeriv_m(self, src, v, adjoint=False): - """ - Derivative of the secondary current density with respect to the inversion model. - - :param SimPEG.EM.FDEM.Src src: source - :param numpy.ndarray v: vector to take product with - :param bool adjoint: adjoint? - :rtype: numpy.ndarray - :return: product of the secondary current density derivative with respect to the inversion model with a vector - """ - - _,S_eDeriv = src.evalDeriv(self.prob, v, adjoint) - return -S_eDeriv - - def _jDeriv_u(self, src, du_dm_v, adjoint=False): """ - Partial derivative of the total current density with respect to the thing we solved for - + Derivative of the current density with respect to the thing we solved for + :param SimPEG.EM.FDEM.Src src: source :param numpy.ndarray du_dm_v: vector to take product with :param bool adjoint: adjoint? @@ -897,18 +1134,113 @@ class Fields_h(Fields): :return: product of the derivative of the current density with respect to the field we solved for with a vector """ - return self._jSecondaryDeriv_u(src,du_dm_v,adjoint) + if not adjoint: + return self._edgeCurl*du_dm_v + elif adjoint: + return self._edgeCurl.T*du_dm_v + def _jDeriv_m(self, src, v, adjoint=False): """ - Partial derivative of the total current density with respect to the inversion model. - + Derivative of the current density with respect to the inversion model. + :param SimPEG.EM.FDEM.Src src: source :param numpy.ndarray v: vector to take product with :param bool adjoint: adjoint? - :rtype: SimPEG.Utils.Zero - :return: product of the current density with respect to the inversion model with a vector + :rtype: numpy.ndarray + :return: product of the current density derivative with respect to the inversion model with a vector """ - # assuming the primary does not depend on the model - return self._jSecondaryDeriv_m(src,v,adjoint) + _,s_eDeriv = src.evalDeriv(self.prob, v, adjoint) + return -s_eDeriv + + def _e(self, hSolution, srcList): + """ + Electric field from hSolution + + :param numpy.ndarray hSolution: field we solved for + :param list srcList: list of sources + :rtype: numpy.ndarray + :return: electric field + """ + n = int(self._aveF2CCV.shape[0] / self._nC) #number of components + VI = sdiag(np.kron(np.ones(n), 1./self.prob.mesh.vol)) + return VI * (self._aveF2CCV * (self._MfRho * self._j(hSolution, srcList))) + + def _eDeriv_u(self, src, du_dm_v, adjoint=False): + """ + Derivative of the electric field with respect to the thing we solved for + + :param SimPEG.EM.FDEM.Src src: source + :param numpy.ndarray du_dm_v: vector to take product with + :param bool adjoint: adjoint? + :rtype: numpy.ndarray + :return: product of the derivative of the electric field with respect to the field we solved for with a vector + """ + n = int(self._aveF2CCV.shape[0] / self._nC) #number of components + VI = sdiag(np.kron(np.ones(n), 1./self.prob.mesh.vol)) + if adjoint: + return self._edgeCurl.T * ( self._MfRho.T * ( self._aveF2CCV.T * ( VI.T * du_dm_v ) ) ) + return VI * (self._aveF2CCV * (self._MfRho * self._edgeCurl * du_dm_v )) + + def _eDeriv_m(self, src, v, adjoint=False): + """ + Derivative of the electric field with respect to the inversion model. + + :param SimPEG.EM.FDEM.Src src: source + :param numpy.ndarray v: vector to take product with + :param bool adjoint: adjoint? + :rtype: numpy.ndarray + :return: product of the electric field derivative with respect to the inversion model with a vector + """ + hSolution = Utils.mkvc(self[src,'hSolution']) + n = int(self._aveF2CCV.shape[0] / self._nC) #number of components + VI = sdiag(np.kron(np.ones(n), 1./self.prob.mesh.vol)) + if adjoint: + return ( self._MfRhoDeriv(self._edgeCurl * hSolution).T * ( self._aveF2CCV.T * (VI.T * v) ) ) + return VI * (self._aveF2CCV * (self._MfRhoDeriv(self._edgeCurl * hSolution) * v )) + + def _b(self, hSolution, srcList): + """ + Magnetic flux density from hSolution + + :param numpy.ndarray hSolution: field we solved for + :param list srcList: list of sources + :rtype: numpy.ndarray + :return: magnetic flux density + """ + h = self._h(hSolution, srcList) + n = int(self._aveE2CCV.shape[0] / self._nC) #number of components + VI = sdiag(np.kron(np.ones(n), 1./self.prob.mesh.vol)) + + return VI * (self._aveE2CCV * (self._MeMu * h)) + + def _bDeriv_u(self, src, du_dm_v, adjoint=False): + """ + Derivative of the magnetic flux density with respect to the thing we solved for + + :param SimPEG.EM.FDEM.Src src: source + :param numpy.ndarray du_dm_v: vector to take product with + :param bool adjoint: adjoint? + :rtype: numpy.ndarray + :return: product of the derivative of the magnetic flux density with respect to the field we solved for with a vector + """ + n = int(self._aveE2CCV.shape[0] / self._nC) #number of components + VI = sdiag(np.kron(np.ones(n), 1./self.prob.mesh.vol)) + if adjoint: + return self._MeMu.T * (self._aveE2CCV.T * ( VI.T * du_dm_v )) + return VI * (self._aveE2CCV * (self._MeMu * du_dm_v)) + + def _bDeriv_m(self, src, v, adjoint=False): + """ + Derivative of the magnetic flux density with respect to the inversion model. + + :param SimPEG.EM.FDEM.Src src: source + :param numpy.ndarray v: vector to take product with + :param bool adjoint: adjoint? + :rtype: numpy.ndarray + :return: product of the magnetic flux density derivative with respect to the inversion model with a vector + """ + return Zero() + + diff --git a/SimPEG/EM/FDEM/RxFDEM.py b/SimPEG/EM/FDEM/RxFDEM.py new file mode 100644 index 00000000..ef58d807 --- /dev/null +++ b/SimPEG/EM/FDEM/RxFDEM.py @@ -0,0 +1,126 @@ +import SimPEG +from SimPEG import sp + +class BaseRx(SimPEG.Survey.BaseRx): + """ + Frequency domain receiver base class + + :param numpy.ndarray locs: receiver locations (ie. :code:`np.r_[x,y,z]`) + :param string orientation: receiver orientation 'x', 'y' or 'z' + :param string real_or_imag: real or imaginary component 'real' or 'imag' + """ + + def __init__(self, locs, orientation=None, real_or_imag=None): + assert(orientation in ['x','y','z']), "Orientation %s not known. Orientation must be in 'x', 'y', 'z'. Arbitrary orientations have not yet been implemented."%orientation + assert(real_or_imag in ['real', 'imag']), "'real_or_imag' must be 'real' or 'imag', not %s"%real_or_imag + + self.projComp = orientation + self.real_or_imag = real_or_imag + + SimPEG.Survey.BaseRx.__init__(self, locs, rxType=None) #TODO: remove rxType from baseRx + + def projGLoc(self, u): + """Grid Location projection (e.g. Ex Fy ...)""" + return u._GLoc(self.projField) + self.projComp + + def eval(self, src, mesh, f): + """ + Project fields to recievers to get data. + + :param Source src: FDEM source + :param Mesh mesh: mesh used + :param Fields f: fields object + :rtype: numpy.ndarray + :return: fields projected to recievers + """ + + P = self.getP(mesh, self.projGLoc(f)) + f_part_complex = f[src, self.projField] + f_part = getattr(f_part_complex, self.real_or_imag) # get the real or imag component + + return P*f_part + + def evalDeriv(self, src, mesh, f, v, adjoint=False): + """ + Derivative of projected fields with respect to the inversion model times a vector. + + :param Source src: FDEM source + :param Mesh mesh: mesh used + :param Fields f: fields object + :param numpy.ndarray v: vector to multiply + :rtype: numpy.ndarray + :return: fields projected to recievers + """ + + P = self.getP(mesh, self.projGLoc(f)) + + if not adjoint: + Pv_complex = P * v + Pv = getattr(Pv_complex, self.real_or_imag) + elif adjoint: + Pv_real = P.T * v + + if self.real_or_imag == 'imag': + Pv = 1j*Pv_real + elif self.real_or_imag == 'real': + Pv = Pv_real.astype(complex) + else: + raise NotImplementedError('must be real or imag') + + return Pv + + +class eField(BaseRx): + """ + Electric field FDEM receiver + + :param numpy.ndarray locs: receiver locations (ie. :code:`np.r_[x,y,z]`) + :param string orientation: receiver orientation 'x', 'y' or 'z' + :param string real_or_imag: real or imaginary component 'real' or 'imag' + """ + + def __init__(self, locs, orientation=None, real_or_imag=None): + self.projField = 'e' + BaseRx.__init__(self, locs, orientation, real_or_imag) + + +class bField(BaseRx): + """ + Magnetic flux FDEM receiver + + :param numpy.ndarray locs: receiver locations (ie. :code:`np.r_[x,y,z]`) + :param string orientation: receiver orientation 'x', 'y' or 'z' + :param string real_or_imag: real or imaginary component 'real' or 'imag' + """ + + def __init__(self, locs, orientation=None, real_or_imag=None): + self.projField = 'b' + BaseRx.__init__(self, locs, orientation, real_or_imag) + + +class hField(BaseRx): + """ + Magnetic field FDEM receiver + + :param numpy.ndarray locs: receiver locations (ie. :code:`np.r_[x,y,z]`) + :param string orientation: receiver orientation 'x', 'y' or 'z' + :param string real_or_imag: real or imaginary component 'real' or 'imag' + """ + + def __init__(self, locs, orientation=None, real_or_imag=None): + self.projField = 'h' + BaseRx.__init__(self, locs, orientation, real_or_imag) + + +class jField(BaseRx): + """ + Current density FDEM receiver + + :param numpy.ndarray locs: receiver locations (ie. :code:`np.r_[x,y,z]`) + :param string orientation: receiver orientation 'x', 'y' or 'z' + :param string real_or_imag: real or imaginary component 'real' or 'imag' + """ + + def __init__(self, locs, orientation=None, real_or_imag=None): + self.projField = 'j' + BaseRx.__init__(self, locs, orientation, real_or_imag) diff --git a/SimPEG/EM/FDEM/SrcFDEM.py b/SimPEG/EM/FDEM/SrcFDEM.py index f524a5f2..160ef5e6 100644 --- a/SimPEG/EM/FDEM/SrcFDEM.py +++ b/SimPEG/EM/FDEM/SrcFDEM.py @@ -9,28 +9,30 @@ class BaseSrc(Survey.BaseSrc): """ freq = None - # rxPair = RxFDEM - integrate = True + integrate = False + + def __init__(self, rxList, **kwargs): + Survey.BaseSrc.__init__(self, rxList, **kwargs) def eval(self, prob): """ Evaluate the source terms. - - :math:`S_m` : magnetic source term - - :math:`S_e` : electric source term + - :math:`s_m` : magnetic source term + - :math:`s_e` : electric source term :param Problem prob: FDEM Problem :rtype: (numpy.ndarray, numpy.ndarray) :return: tuple with magnetic source term and electric source term """ - S_m = self.S_m(prob) - S_e = self.S_e(prob) - return S_m, S_e + s_m = self.s_m(prob) + s_e = self.s_e(prob) + return s_m, s_e def evalDeriv(self, prob, v=None, adjoint=False): """ Derivatives of the source terms with respect to the inversion model - - :code:`S_mDeriv` : derivative of the magnetic source term - - :code:`S_eDeriv` : derivative of the electric source term + - :code:`s_mDeriv` : derivative of the magnetic source term + - :code:`s_eDeriv` : derivative of the electric source term :param Problem prob: FDEM Problem :param numpy.ndarray v: vector to take product with @@ -39,9 +41,9 @@ class BaseSrc(Survey.BaseSrc): :return: tuple with magnetic source term and electric source term derivatives times a vector """ if v is not None: - return self.S_mDeriv(prob, v, adjoint), self.S_eDeriv(prob, v, adjoint) + return self.s_mDeriv(prob, v, adjoint), self.s_eDeriv(prob, v, adjoint) else: - return lambda v: self.S_mDeriv(prob, v, adjoint), lambda v: self.S_eDeriv(prob, v, adjoint) + return lambda v: self.s_mDeriv(prob, v, adjoint), lambda v: self.s_eDeriv(prob, v, adjoint) def bPrimary(self, prob): """ @@ -83,7 +85,7 @@ class BaseSrc(Survey.BaseSrc): """ return Zero() - def S_m(self, prob): + def s_m(self, prob): """ Magnetic source term @@ -93,7 +95,7 @@ class BaseSrc(Survey.BaseSrc): """ return Zero() - def S_e(self, prob): + def s_e(self, prob): """ Electric source term @@ -103,7 +105,7 @@ class BaseSrc(Survey.BaseSrc): """ return Zero() - def S_mDeriv(self, prob, v, adjoint = False): + def s_mDeriv(self, prob, v, adjoint = False): """ Derivative of magnetic source term with respect to the inversion model @@ -116,7 +118,7 @@ class BaseSrc(Survey.BaseSrc): return Zero() - def S_eDeriv(self, prob, v, adjoint = False): + def s_eDeriv(self, prob, v, adjoint = False): """ Derivative of electric source term with respect to the inversion model @@ -131,22 +133,21 @@ class BaseSrc(Survey.BaseSrc): class RawVec_e(BaseSrc): """ - RawVec electric source. It is defined by the user provided vector S_e + RawVec electric source. It is defined by the user provided vector s_e :param list rxList: receiver list :param float freq: frequency - :param numpy.array S_e: electric source term - :param bool integrate: Integrate the source term (multiply by Me) [True] + :param numpy.array s_e: electric source term + :param bool integrate: Integrate the source term (multiply by Me) [False] """ - def __init__(self, rxList, freq, S_e, integrate=True): #, ePrimary=None, bPrimary=None, hPrimary=None, jPrimary=None): - self._S_e = np.array(S_e, dtype=complex) + def __init__(self, rxList, freq, s_e): + self._s_e = np.array(s_e, dtype=complex) self.freq = float(freq) - self.integrate = integrate BaseSrc.__init__(self, rxList) - def S_e(self, prob): + def s_e(self, prob): """ Electric source term @@ -154,29 +155,28 @@ class RawVec_e(BaseSrc): :rtype: numpy.ndarray :return: electric source term on mesh """ - if prob._eqLocs is 'FE' and self.integrate is True: - return prob.Me * self._S_e - return self._S_e + if prob._formulation is 'EB' and self.integrate is True: + return prob.Me * self._s_e + return self._s_e class RawVec_m(BaseSrc): """ - RawVec magnetic source. It is defined by the user provided vector S_m + RawVec magnetic source. It is defined by the user provided vector s_m :param float freq: frequency :param rxList: receiver list - :param numpy.array S_m: magnetic source term - :param bool integrate: Integrate the source term (multiply by Me) [True] + :param numpy.array s_m: magnetic source term + :param bool integrate: Integrate the source term (multiply by Me) [False] """ - def __init__(self, rxList, freq, S_m, integrate=True): #ePrimary=Zero(), bPrimary=Zero(), hPrimary=Zero(), jPrimary=Zero()): - self._S_m = np.array(S_m, dtype=complex) + def __init__(self, rxList, freq, s_m, integrate=True): #ePrimary=Zero(), bPrimary=Zero(), hPrimary=Zero(), jPrimary=Zero()): + self._s_m = np.array(s_m, dtype=complex) self.freq = float(freq) - self.integrate = integrate BaseSrc.__init__(self, rxList) - def S_m(self, prob): + def s_m(self, prob): """ Magnetic source term @@ -184,29 +184,28 @@ class RawVec_m(BaseSrc): :rtype: numpy.ndarray :return: magnetic source term on mesh """ - if prob._eqLocs is 'EF' and self.integrate is True: - return prob.Me * self._S_m - return self._S_m + if prob._formulation is 'HJ' and self.integrate is True: + return prob.Me * self._s_m + return self._s_m class RawVec(BaseSrc): """ - RawVec source. It is defined by the user provided vectors S_m, S_e + RawVec source. It is defined by the user provided vectors s_m, s_e :param rxList: receiver list :param float freq: frequency - :param numpy.array S_m: magnetic source term - :param numpy.array S_e: electric source term - :param bool integrate: Integrate the source term (multiply by Me) [True] + :param numpy.array s_m: magnetic source term + :param numpy.array s_e: electric source term + :param bool integrate: Integrate the source term (multiply by Me) [False] """ - 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) + def __init__(self, rxList, freq, s_m, s_e, **kwargs): + 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 - BaseSrc.__init__(self, rxList) + BaseSrc.__init__(self, rxList, **kwargs) - def S_m(self, prob): + def s_m(self, prob): """ Magnetic source term @@ -214,11 +213,11 @@ class RawVec(BaseSrc): :rtype: numpy.ndarray :return: magnetic source term on mesh """ - if prob._eqLocs is 'EF' and self.integrate is True: - return prob.Me * self._S_m - return self._S_m + if prob._formulation is 'HJ' and self.integrate is True: + return prob.Me * self._s_m + return self._s_m - def S_e(self, prob): + def s_e(self, prob): """ Electric source term @@ -226,9 +225,9 @@ class RawVec(BaseSrc): :rtype: numpy.ndarray :return: electric source term on mesh """ - if prob._eqLocs is 'FE' and self.integrate is True: - return prob.Me * self._S_e - return self._S_e + if prob._formulation is 'EB' and self.integrate is True: + return prob.Me * self._s_e + return self._s_e class MagDipole(BaseSrc): @@ -278,14 +277,13 @@ class MagDipole(BaseSrc): :param float mu: background magnetic permeability """ - def __init__(self, rxList, freq, loc, orientation='Z', moment=1., mu=mu_0): + def __init__(self, rxList, freq, loc, orientation='Z', moment=1., mu=mu_0, **kwargs): self.freq = float(freq) self.loc = loc self.orientation = orientation assert orientation in ['X','Y','Z'], "Orientation (right now) doesn't actually do anything! The methods in SrcUtils should take care of this..." self.moment = moment self.mu = mu - self.integrate = False BaseSrc.__init__(self, rxList) def bPrimary(self, prob): @@ -296,15 +294,15 @@ class MagDipole(BaseSrc): :rtype: numpy.ndarray :return: primary magnetic field """ - eqLocs = prob._eqLocs + formulation = prob._formulation - if eqLocs is 'FE': + if formulation is 'EB': gridX = prob.mesh.gridEx gridY = prob.mesh.gridEy gridZ = prob.mesh.gridEz C = prob.mesh.edgeCurl - elif eqLocs is 'EF': + elif formulation is 'HJ': gridX = prob.mesh.gridFx gridY = prob.mesh.gridFy gridZ = prob.mesh.gridFz @@ -335,9 +333,9 @@ class MagDipole(BaseSrc): :return: primary magnetic field """ b = self.bPrimary(prob) - return h_from_b(prob,b) + return 1./self.mu * b - def S_m(self, prob): + def s_m(self, prob): """ The magnetic source term @@ -347,9 +345,11 @@ class MagDipole(BaseSrc): """ b_p = self.bPrimary(prob) + if prob._formulation is 'HJ': + b_p = prob.Me * b_p return -1j*omega(self.freq)*b_p - def S_e(self, prob): + def s_e(self, prob): """ The electric source term @@ -361,13 +361,13 @@ class MagDipole(BaseSrc): if all(np.r_[self.mu] == np.r_[prob.curModel.mu]): return Zero() else: - eqLocs = prob._eqLocs + formulation = prob._formulation - if eqLocs is 'FE': + if formulation is 'EB': mui_s = prob.curModel.mui - 1./self.mu MMui_s = prob.mesh.getFaceInnerProduct(mui_s) C = prob.mesh.edgeCurl - elif eqLocs is 'EF': + elif formulation is 'HJ': mu_s = prob.curModel.mu - self.mu MMui_s = prob.mesh.getEdgeInnerProduct(mu_s, invMat=True) C = prob.mesh.edgeCurl.T @@ -410,15 +410,15 @@ class MagDipole_Bfield(BaseSrc): :return: primary magnetic field """ - eqLocs = prob._eqLocs + formulation = prob._formulation - if eqLocs is 'FE': + if formulation is 'EB': gridX = prob.mesh.gridFx gridY = prob.mesh.gridFy gridZ = prob.mesh.gridFz C = prob.mesh.edgeCurl - elif eqLocs is 'EF': + elif formulation is 'HJ': gridX = prob.mesh.gridEx gridY = prob.mesh.gridEy gridZ = prob.mesh.gridEz @@ -449,9 +449,9 @@ class MagDipole_Bfield(BaseSrc): :return: primary magnetic field """ b = self.bPrimary(prob) - return h_from_b(prob, b) + return 1/self.mu * b - def S_m(self, prob): + def s_m(self, prob): """ The magnetic source term @@ -460,9 +460,11 @@ class MagDipole_Bfield(BaseSrc): :return: primary magnetic field """ b = self.bPrimary(prob) + if prob._formulation is 'HJ': + b = prob.Me * b return -1j*omega(self.freq)*b - def S_e(self, prob): + def s_e(self, prob): """ The electric source term @@ -473,13 +475,13 @@ class MagDipole_Bfield(BaseSrc): if all(np.r_[self.mu] == np.r_[prob.curModel.mu]): return Zero() else: - eqLocs = prob._eqLocs + formulation = prob._formulation - if eqLocs is 'FE': + if formulation is 'EB': mui_s = prob.curModel.mui - 1./self.mu MMui_s = prob.mesh.getFaceInnerProduct(mui_s) C = prob.mesh.edgeCurl - elif eqLocs is 'EF': + elif formulation is 'HJ': mu_s = prob.curModel.mu - self.mu MMui_s = prob.mesh.getEdgeInnerProduct(mu_s, invMat=True) C = prob.mesh.edgeCurl.T @@ -521,15 +523,15 @@ class CircularLoop(BaseSrc): :rtype: numpy.ndarray :return: primary magnetic field """ - eqLocs = prob._eqLocs + formulation = prob._formulation - if eqLocs is 'FE': + if formulation is 'EB': gridX = prob.mesh.gridEx gridY = prob.mesh.gridEy gridZ = prob.mesh.gridEz C = prob.mesh.edgeCurl - elif eqLocs is 'EF': + elif formulation is 'HJ': gridX = prob.mesh.gridFx gridY = prob.mesh.gridFy gridZ = prob.mesh.gridFz @@ -539,7 +541,8 @@ class CircularLoop(BaseSrc): if not prob.mesh.isSymmetric: # TODO ? raise NotImplementedError('Non-symmetric cyl mesh not implemented yet!') - a = MagneticLoopVectorPotential(self.loc, gridY, 'y', self.radius, mu=self.mu) + a = MagneticLoopVectorPotential(self.loc, gridY, 'y', moment=self.radius, mu=self.mu) + else: srcfct = MagneticLoopVectorPotential ax = srcfct(self.loc, gridX, 'x', self.radius, mu=self.mu) @@ -560,7 +563,7 @@ class CircularLoop(BaseSrc): b = self.bPrimary(prob) return 1./self.mu*b - def S_m(self, prob): + def s_m(self, prob): """ The magnetic source term @@ -569,9 +572,11 @@ class CircularLoop(BaseSrc): :return: primary magnetic field """ b = self.bPrimary(prob) + if prob._formulation is 'HJ': + b = prob.Me * b return -1j*omega(self.freq)*b - def S_e(self, prob): + def s_e(self, prob): """ The electric source term @@ -582,13 +587,15 @@ class CircularLoop(BaseSrc): if all(np.r_[self.mu] == np.r_[prob.curModel.mu]): return Zero() else: - eqLocs = prob._eqLocs + formulation = prob._formulation - if eqLocs is 'FE': + if formulation is 'EB': mui_s = prob.curModel.mui - 1./self.mu MMui_s = prob.mesh.getFaceInnerProduct(mui_s) C = prob.mesh.edgeCurl - elif eqLocs is 'EF': + + + elif formulation is 'HJ': mu_s = prob.curModel.mu - self.mu MMui_s = prob.mesh.getEdgeInnerProduct(mu_s, invMat=True) C = prob.mesh.edgeCurl.T @@ -596,3 +603,5 @@ class CircularLoop(BaseSrc): return -C.T * (MMui_s * self.bPrimary(prob)) + + diff --git a/SimPEG/EM/FDEM/SurveyFDEM.py b/SimPEG/EM/FDEM/SurveyFDEM.py index f1a51b54..46ae2523 100644 --- a/SimPEG/EM/FDEM/SurveyFDEM.py +++ b/SimPEG/EM/FDEM/SurveyFDEM.py @@ -1,123 +1,13 @@ import SimPEG from SimPEG.EM.Utils import * +from SimPEG.EM.Base import BaseEMSurvey from scipy.constants import mu_0 from SimPEG.Utils import Zero, Identity import SrcFDEM as Src +import RxFDEM as Rx +from SimPEG import sp - -#################################################### -# Receivers -#################################################### - -class Rx(SimPEG.Survey.BaseRx): - """ - Frequency domain receivers - - :param numpy.ndarray locs: receiver locations (ie. :code:`np.r_[x,y,z]`) - :param string rxType: reciever type from knownRxTypes - """ - - knownRxTypes = { - '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): - SimPEG.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 eval(self, src, mesh, f): - """ - Project fields to recievers to get data. - - :param Source src: FDEM source - :param Mesh mesh: mesh used - :param Fields f: fields object - :rtype: numpy.ndarray - :return: fields projected to recievers - """ - P = self.getP(mesh) # get interpolation to recievers - u_part_complex = f[src, self.projField] - real_or_imag = self.projComp # get the real or imag component - u_part = getattr(u_part_complex, real_or_imag) - return P*u_part - - def evalDeriv(self, src, mesh, f, v, adjoint=False): - """ - Derivative of projected fields with respect to the inversion model times a vector. - - :param Source src: FDEM source - :param Mesh mesh: mesh used - :param Fields f: fields object - :param numpy.ndarray v: vector to multiply - :rtype: numpy.ndarray - :return: fields projected to recievers - """ - P = self.getP(mesh) - - 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 - - -#################################################### -# Survey -#################################################### - -class Survey(SimPEG.Survey.BaseSurvey): +class Survey(BaseEMSurvey): """ Frequency domain electromagnetic survey @@ -125,12 +15,12 @@ class Survey(SimPEG.Survey.BaseSurvey): """ srcPair = Src.BaseSrc - rxPair = Rx + rxPair = Rx.BaseRx def __init__(self, srcList, **kwargs): # Sort these by frequency self.srcList = srcList - SimPEG.Survey.BaseSurvey.__init__(self, **kwargs) + BaseEMSurvey.__init__(self, srcList, **kwargs) _freqDict = {} for src in srcList: @@ -165,23 +55,8 @@ class Survey(SimPEG.Survey.BaseSurvey): Returns the sources associated with a specific frequency. :param float freq: frequency for which we look up sources :rtype: dictionary - :return: sources at the sepcified frequency + :return: sources at the sepcified frequency """ assert freq in self._freqDict, "The requested frequency is not in this survey." return self._freqDict[freq] - def eval(self, u): - """ - Project fields to receiver locations - :param Fields u: fields object - :rtype: numpy.ndarray - :return: data - """ - data = SimPEG.Survey.Data(self) - for src in self.srcList: - for rx in src.rxList: - data[src, rx] = rx.eval(src, self.mesh, u) - return data - - def evalDeriv(self, u): - raise Exception('Use Receivers to project fields deriv.') diff --git a/SimPEG/EM/FDEM/__init__.py b/SimPEG/EM/FDEM/__init__.py index 978972f5..1701fe3e 100644 --- a/SimPEG/EM/FDEM/__init__.py +++ b/SimPEG/EM/FDEM/__init__.py @@ -1,3 +1,5 @@ -from SurveyFDEM import Rx, Src, Survey -from FDEM import BaseFDEMProblem, Problem_e, Problem_b, Problem_j, Problem_h -from FieldsFDEM import * \ No newline at end of file +from SurveyFDEM import Survey +import SrcFDEM as Src +import RxFDEM as Rx +from FDEM import Problem3D_e, Problem3D_b, Problem3D_j, Problem3D_h +from FieldsFDEM import Fields3D_e, Fields3D_b, Fields3D_j, Fields3D_h diff --git a/SimPEG/EM/TDEM_old/BaseTDEM.py b/SimPEG/EM/TDEM_old/BaseTDEM.py index 1ad44889..15fc19e3 100644 --- a/SimPEG/EM/TDEM_old/BaseTDEM.py +++ b/SimPEG/EM/TDEM_old/BaseTDEM.py @@ -27,6 +27,7 @@ class FieldsTDEM(Problem.TimeFields): else: e = np.zeros((nE,nSrc)) # if nSrc == 1 else (nE, nSrc)) u = np.concatenate((u, b, e)) + return Utils.mkvc(u,nSrc) @@ -107,11 +108,11 @@ class BaseTDEMProblem(BaseTimeProblem, BaseEMProblem): Ainv.clean() return F - def Jvec(self, m, v, u=None): + def Jvec(self, m, v, f=None): """ :param numpy.array m: Conductivity model :param numpy.ndarray v: vector (model object) - :param simpegEM.TDEM.FieldsTDEM u: Fields resulting from m + :param simpegEM.TDEM.FieldsTDEM f: Fields resulting from m :rtype: numpy.ndarray :return: w (data object) @@ -124,15 +125,15 @@ class BaseTDEMProblem(BaseTimeProblem, BaseEMProblem): """ 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) + if f is None: + f = self.fields(m) + p = self.Gvec(m, v, f) y = self.solveAh(m, p) - Jv = self.survey.evalDeriv(u, v=y) + Jv = self.survey.evalDeriv(f, 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): + def Jtvec(self, m, v, f=None): """ :param numpy.array m: Conductivity model :param numpy.ndarray,SimPEG.Survey.Data v: vector (data object) @@ -149,15 +150,15 @@ class BaseTDEMProblem(BaseTimeProblem, BaseEMProblem): """ 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 f is None: + f = self.fields(m) if not isinstance(v, self.dataPair): v = self.dataPair(self.survey, v) - p = self.survey.evalDeriv(u, v=v, adjoint=True) + p = self.survey.evalDeriv(f, v=v, adjoint=True) y = self.solveAht(m, p) - w = self.Gtvec(m, y, u) + w = self.Gtvec(m, y, f) if self.verbose: print '%s\nDone calculating J^T(v)\n%s'%('*'*50,'*'*50) return - mkvc(w) diff --git a/SimPEG/EM/Utils/EMUtils.py b/SimPEG/EM/Utils/EMUtils.py index 4a342acb..e7dbf441 100644 --- a/SimPEG/EM/Utils/EMUtils.py +++ b/SimPEG/EM/Utils/EMUtils.py @@ -13,37 +13,4 @@ def k(freq, sigma, mu=mu_0, eps=epsilon_0): 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 - diff --git a/SimPEG/EM/Utils/__init__.py b/SimPEG/EM/Utils/__init__.py index 18dddde9..ef779042 100644 --- a/SimPEG/EM/Utils/__init__.py +++ b/SimPEG/EM/Utils/__init__.py @@ -1,5 +1,2 @@ -# import Sources -# import Ana -# import Solver -from EMUtils import omega, e_from_j, j_from_e, b_from_h, h_from_b +from EMUtils import omega, k from AnalyticUtils import MagneticDipoleFields, MagneticDipoleVectorPotential, MagneticLoopVectorPotential \ No newline at end of file diff --git a/SimPEG/EM/Utils/testingUtils.py b/SimPEG/EM/Utils/testingUtils.py index 8c703083..c3fc50d2 100644 --- a/SimPEG/EM/Utils/testingUtils.py +++ b/SimPEG/EM/Utils/testingUtils.py @@ -4,63 +4,77 @@ from SimPEG import EM import sys from scipy.constants import mu_0 -def getFDEMProblem(fdemType, comp, SrcList, freq, verbose=False): - cs = 5. - ncx, ncy, ncz = 6, 6, 6 - npad = 3 +FLR = 1e-20 # "zero", so if residual below this --> pass regardless of order +CONDUCTIVITY = 1e1 +MU = mu_0 +freq = 5e-1 + + +def getFDEMProblem(fdemType, comp, SrcList, freq, useMu=False, verbose=False): + cs = 10. + ncx, ncy, ncz = 0, 0, 0 + npad = 8 hx = [(cs,npad,-1.3), (cs,ncx), (cs,npad,1.3)] hy = [(cs,npad,-1.3), (cs,ncy), (cs,npad,1.3)] hz = [(cs,npad,-1.3), (cs,ncz), (cs,npad,1.3)] mesh = Mesh.TensorMesh([hx,hy,hz],['C','C','C']) - mapping = Maps.ExpMap(mesh) + if useMu is True: + mapping = [('sigma', Maps.ExpMap(mesh)), ('mu', Maps.IdentityMap(mesh))] + else: + mapping = Maps.ExpMap(mesh) - x = np.array([np.linspace(-30,-15,3),np.linspace(15,30,3)]) #don't sample right by the source - XYZ = Utils.ndgrid(x,x,np.r_[0.]) - Rx0 = EM.FDEM.Rx(XYZ, comp) + x = np.array([np.linspace(-5.*cs,-2.*cs,3),np.linspace(5.*cs,2.*cs,3)]) + cs/4. #don't sample right by the source, slightly off alignment from either staggered grid + XYZ = Utils.ndgrid(x,x,np.linspace(-2.*cs,2.*cs,5)) + Rx0 = getattr(EM.FDEM.Rx, comp[0] + 'Field') + if comp[2] == 'r': + real_or_imag = 'real' + elif comp[2] == 'i': + real_or_imag = 'imag' + rx0 = Rx0(XYZ, comp[1], 'imag') Src = [] for SrcType in SrcList: if SrcType is 'MagDipole': - Src.append(EM.FDEM.Src.MagDipole([Rx0], freq=freq, loc=np.r_[0.,0.,0.])) + Src.append(EM.FDEM.Src.MagDipole([rx0], freq=freq, loc=np.r_[0.,0.,0.])) elif SrcType is 'MagDipole_Bfield': - Src.append(EM.FDEM.Src.MagDipole_Bfield([Rx0], freq=freq, loc=np.r_[0.,0.,0.])) + Src.append(EM.FDEM.Src.MagDipole_Bfield([rx0], freq=freq, loc=np.r_[0.,0.,0.])) elif SrcType is 'CircularLoop': - Src.append(EM.FDEM.Src.CircularLoop([Rx0], freq=freq, loc=np.r_[0.,0.,0.])) + Src.append(EM.FDEM.Src.CircularLoop([rx0], freq=freq, loc=np.r_[0.,0.,0.])) elif SrcType is 'RawVec': if fdemType is 'e' or fdemType is 'b': S_m = np.zeros(mesh.nF) S_e = np.zeros(mesh.nE) - S_m[Utils.closestPoints(mesh,[0.,0.,0.],'Fz') + np.sum(mesh.vnF[:1])] = 1. - S_e[Utils.closestPoints(mesh,[0.,0.,0.],'Ez') + np.sum(mesh.vnE[:1])] = 1. - Src.append(EM.FDEM.Src.RawVec([Rx0], freq, S_m, S_e)) + S_m[Utils.closestPoints(mesh,[0.,0.,0.],'Fz') + np.sum(mesh.vnF[:1])] = 1e-3 + S_e[Utils.closestPoints(mesh,[0.,0.,0.],'Ez') + np.sum(mesh.vnE[:1])] = 1e-3 + Src.append(EM.FDEM.Src.RawVec([rx0], freq, S_m, mesh.getEdgeInnerProduct()*S_e)) elif fdemType is 'h' or fdemType is 'j': S_m = np.zeros(mesh.nE) S_e = np.zeros(mesh.nF) - S_m[Utils.closestPoints(mesh,[0.,0.,0.],'Ez') + np.sum(mesh.vnE[:1])] = 1. - S_e[Utils.closestPoints(mesh,[0.,0.,0.],'Fz') + np.sum(mesh.vnF[:1])] = 1. - Src.append(EM.FDEM.Src.RawVec([Rx0], freq, S_m, S_e)) + S_m[Utils.closestPoints(mesh,[0.,0.,0.],'Ez') + np.sum(mesh.vnE[:1])] = 1e-3 + S_e[Utils.closestPoints(mesh,[0.,0.,0.],'Fz') + np.sum(mesh.vnF[:1])] = 1e-3 + Src.append(EM.FDEM.Src.RawVec([rx0], freq, mesh.getEdgeInnerProduct()*S_m, S_e)) if verbose: print ' Fetching %s problem' % (fdemType) if fdemType == 'e': survey = EM.FDEM.Survey(Src) - prb = EM.FDEM.Problem_e(mesh, mapping=mapping) + prb = EM.FDEM.Problem3D_e(mesh, mapping=mapping) elif fdemType == 'b': survey = EM.FDEM.Survey(Src) - prb = EM.FDEM.Problem_b(mesh, mapping=mapping) + prb = EM.FDEM.Problem3D_b(mesh, mapping=mapping) elif fdemType == 'j': survey = EM.FDEM.Survey(Src) - prb = EM.FDEM.Problem_j(mesh, mapping=mapping) + prb = EM.FDEM.Problem3D_j(mesh, mapping=mapping) elif fdemType == 'h': survey = EM.FDEM.Survey(Src) - prb = EM.FDEM.Problem_h(mesh, mapping=mapping) + prb = EM.FDEM.Problem3D_h(mesh, mapping=mapping) else: raise NotImplementedError() @@ -70,6 +84,48 @@ def getFDEMProblem(fdemType, comp, SrcList, freq, verbose=False): from pymatsolver import MumpsSolver prb.Solver = MumpsSolver except ImportError, e: - pass + prb.Solver = SolverLU - return prb \ No newline at end of file + return prb + +def crossCheckTest(SrcList, fdemType1, fdemType2, comp, addrandoms = False, useMu=False, TOL=1e-5, verbose=False): + + l2norm = lambda r: np.sqrt(r.dot(r)) + + prb1 = getFDEMProblem(fdemType1, comp, SrcList, freq, useMu, verbose) + mesh = prb1.mesh + print 'Cross Checking Forward: %s, %s formulations - %s' % (fdemType1, fdemType2, comp) + + logsig = np.log(np.ones(mesh.nC)*CONDUCTIVITY) + mu = np.ones(mesh.nC)*MU + + if addrandoms is True: + logsig += np.random.randn(mesh.nC)*np.log(CONDUCTIVITY)*1e-1 + mu += np.random.randn(mesh.nC)*MU*1e-1 + + if useMu is True: + m = np.r_[logsig, mu] + else: + m = logsig + + survey1 = prb1.survey + d1 = survey1.dpred(m) + + if verbose: + print ' Problem 1 solved' + + + prb2 = getFDEMProblem(fdemType2, comp, SrcList, freq, useMu, verbose) + + survey2 = prb2.survey + d2 = survey2.dpred(m) + + if verbose: + print ' Problem 2 solved' + + r = d2-d1 + l2r = l2norm(r) + + tol = np.max([TOL*(10**int(np.log10(0.5* (l2norm(d1) + l2norm(d2)) ))),FLR]) + print l2norm(d1), l2norm(d2), l2r , tol, l2r < tol + return l2r < tol diff --git a/SimPEG/Examples/DC_Analytic_Dipole.py b/SimPEG/Examples/DC_Analytic_Dipole.py new file mode 100644 index 00000000..aed5eed4 --- /dev/null +++ b/SimPEG/Examples/DC_Analytic_Dipole.py @@ -0,0 +1,68 @@ +from SimPEG import * +import SimPEG.DCIP as DC + +def run(plotIt=False): + cs = 25. + hx = [(cs,7, -1.3),(cs,21),(cs,7, 1.3)] + hy = [(cs,7, -1.3),(cs,21),(cs,7, 1.3)] + hz = [(cs,7, -1.3),(cs,20)] + mesh = Mesh.TensorMesh([hx, hy, hz], 'CCN') + sighalf = 1e-2 + sigma = np.ones(mesh.nC)*sighalf + xtemp = np.linspace(-150, 150, 21) + ytemp = np.linspace(-150, 150, 21) + xyz_rxP = Utils.ndgrid(xtemp-10., ytemp, np.r_[0.]) + xyz_rxN = Utils.ndgrid(xtemp+10., ytemp, np.r_[0.]) + xyz_rxM = Utils.ndgrid(xtemp, ytemp, np.r_[0.]) + + # if plotIt: + # fig, ax = plt.subplots(1,1, figsize = (5,5)) + # mesh.plotSlice(sigma, grid=True, ax = ax) + # ax.plot(xyz_rxP[:,0],xyz_rxP[:,1], 'w.') + # ax.plot(xyz_rxN[:,0],xyz_rxN[:,1], 'r.', ms = 3) + + rx = DC.RxDipole(xyz_rxP, xyz_rxN) + src = DC.SrcDipole([rx], [-200, 0, -12.5], [+200, 0, -12.5]) + survey = DC.SurveyDC([src]) + problem = DC.ProblemDC_CC(mesh) + problem.pair(survey) + try: + from pymatsolver import MumpsSolver + problem.Solver = MumpsSolver + except Exception, e: + pass + data = survey.dpred(sigma) + + def DChalf(srclocP, srclocN, rxloc, sigma, I=1.): + rp = (srclocP.reshape([1,-1])).repeat(rxloc.shape[0], axis = 0) + rn = (srclocN.reshape([1,-1])).repeat(rxloc.shape[0], axis = 0) + rP = np.sqrt(((rxloc-rp)**2).sum(axis=1)) + rN = np.sqrt(((rxloc-rn)**2).sum(axis=1)) + return I/(sigma*2.*np.pi)*(1/rP-1/rN) + + data_anaP = DChalf(np.r_[-200, 0, 0.],np.r_[+200, 0, 0.], xyz_rxP, sighalf) + data_anaN = DChalf(np.r_[-200, 0, 0.],np.r_[+200, 0, 0.], xyz_rxN, sighalf) + data_ana = data_anaP-data_anaN + Data_ana = data_ana.reshape((21, 21), order = 'F') + Data = data.reshape((21, 21), order = 'F') + X = xyz_rxM[:,0].reshape((21, 21), order = 'F') + Y = xyz_rxM[:,1].reshape((21, 21), order = 'F') + + if plotIt: + import matplotlib.pyplot as plt + fig, ax = plt.subplots(1,2, figsize = (12, 5)) + vmin = np.r_[data, data_ana].min() + vmax = np.r_[data, data_ana].max() + dat1 = ax[1].contourf(X, Y, Data, 60, vmin = vmin, vmax = vmax) + dat0 = ax[0].contourf(X, Y, Data_ana, 60, vmin = vmin, vmax = vmax) + cb0 = plt.colorbar(dat1, orientation = 'horizontal', ax = ax[0]) + cb1 = plt.colorbar(dat1, orientation = 'horizontal', ax = ax[1]) + ax[1].set_title('Analytic') + ax[0].set_title('Computed') + plt.show() + + return np.linalg.norm(data-data_ana)/np.linalg.norm(data_ana) + + +if __name__ == '__main__': + print run(plotIt=True) diff --git a/SimPEG/Examples/DC_Forward_PseudoSection.py b/SimPEG/Examples/DC_Forward_PseudoSection.py new file mode 100644 index 00000000..53467826 --- /dev/null +++ b/SimPEG/Examples/DC_Forward_PseudoSection.py @@ -0,0 +1,210 @@ +from SimPEG import Mesh, Utils, np, sp +import SimPEG.DCIP as DC +import time + +def run(loc=None, sig=None, radi=None, param=None, stype='dpdp', dtype='appc', plotIt=True): + """ + DC Forward Simulation + ===================== + + Forward model two conductive spheres in a half-space and plot a + pseudo-section. Assumes an infinite line source and measures along the + center of the spheres. + + INPUT: + loc = Location of spheres [[x1,y1,z1],[x2,y2,z2]] + radi = Radius of spheres [r1,r2] + param = Conductivity of background and two spheres [m0,m1,m2] + stype = survey type "pdp" (pole dipole) or "dpdp" (dipole dipole) + dtype = Data type "appr" (app res) | "appc" (app cond) | "volt" (potential) + Created by @fourndo + + """ + + assert stype in ['pdp', 'dpdp'], "Source type (stype) must be pdp or dpdp (pole dipole or dipole dipole)" + assert dtype in ['appr', 'appc', 'volt'], "Data type (dtype) must be appr (app res) or appc (app cond) or volt (potential)" + + if loc is None: + loc = np.c_[[-50.,0.,-50.],[50.,0.,-50.]] + if sig is None: + sig = np.r_[1e-2,1e-1,1e-3] + if radi is None: + radi = np.r_[25.,25.] + if param is None: + param = np.r_[30.,30.,5] + + + # First we need to create a mesh and a model. + # This is our mesh + dx = 5. + + hxind = [(dx,15,-1.3), (dx, 75), (dx,15,1.3)] + hyind = [(dx,15,-1.3), (dx, 10), (dx,15,1.3)] + hzind = [(dx,15,-1.3),(dx, 15)] + + mesh = Mesh.TensorMesh([hxind, hyind, hzind], 'CCN') + + + # Set background conductivity + model = np.ones(mesh.nC) * sig[0] + + # First anomaly + ind = Utils.ModelBuilder.getIndicesSphere(loc[:,0],radi[0],mesh.gridCC) + model[ind] = sig[1] + + # Second anomaly + ind = Utils.ModelBuilder.getIndicesSphere(loc[:,1],radi[1],mesh.gridCC) + model[ind] = sig[2] + + # Get index of the center + indy = int(mesh.nCy/2) + + # Plot the model for reference + # Define core mesh extent + xlim = 200 + zlim = 100 + + # Then specify the end points of the survey. Let's keep it simple for now and survey above the anomalies, top of the mesh + ends = [(-175,0),(175,0)] + ends = np.c_[np.asarray(ends),np.ones(2).T*mesh.vectorNz[-1]] + + # Snap the endpoints to the grid. Easier to create 2D section. + indx = Utils.closestPoints(mesh, ends ) + locs = np.c_[mesh.gridCC[indx,0],mesh.gridCC[indx,1],np.ones(2).T*mesh.vectorNz[-1]] + + # We will handle the geometry of the survey for you and create all the combination of tx-rx along line + # [Tx, Rx] = DC.gen_DCIPsurvey(locs, mesh, stype, param[0], param[1], param[2]) + survey, Tx, Rx = DC.gen_DCIPsurvey(locs, mesh, stype, param[0], param[1], param[2]) + + # Define some global geometry + dl_len = np.sqrt( np.sum((locs[0,:] - locs[1,:])**2) ) + dl_x = ( Tx[-1][0,1] - Tx[0][0,0] ) / dl_len + dl_y = ( Tx[-1][1,1] - Tx[0][1,0] ) / dl_len + #azm = np.arctan(dl_y/dl_x) + + #Set boundary conditions + mesh.setCellGradBC('neumann') + + # Define the linear system needed for the DC problem. We assume an infitite + # line source for simplicity. + Div = mesh.faceDiv + Grad = mesh.cellGrad + Msig = Utils.sdiag(1./(mesh.aveF2CC.T*(1./model))) + + A = Div*Msig*Grad + + # Change one corner to deal with nullspace + A[0,0] = 1 + A = sp.csc_matrix(A) + + # We will solve the system iteratively, so a pre-conditioner is helpful + # This is simply a Jacobi preconditioner (inverse of the main diagonal) + dA = A.diagonal() + P = sp.spdiags(1/dA,0,A.shape[0],A.shape[0]) + + # Now we can solve the system for all the transmitters + # We want to store the data + data = [] + + # There is probably a more elegant way to do this, but we can just for-loop through the transmitters + for ii in range(len(Tx)): + + start_time = time.time() # Let's time the calculations + + #print("Transmitter %i / %i\r" % (ii+1,len(Tx))) + + # Select dipole locations for receiver + rxloc_M = np.asarray(Rx[ii][:,0:3]) + rxloc_N = np.asarray(Rx[ii][:,3:]) + + + # For usual cases "dpdp" or "gradient" + if stype == 'pdp': + # Create an "inifinity" pole + tx = np.squeeze(Tx[ii][:,0:1]) + tinf = tx + np.array([dl_x,dl_y,0])*dl_len*2 + inds = Utils.closestPoints(mesh, np.c_[tx,tinf].T) + RHS = mesh.getInterpolationMat(np.asarray(Tx[ii]).T, 'CC').T*( [-1] / mesh.vol[inds] ) + else: + inds = Utils.closestPoints(mesh, np.asarray(Tx[ii]).T ) + RHS = mesh.getInterpolationMat(np.asarray(Tx[ii]).T, 'CC').T*( [-1,1] / mesh.vol[inds] ) + + # Iterative Solve + Ainvb = sp.linalg.bicgstab(P*A,P*RHS, tol=1e-5) + + # We now have the potential everywhere + phi = Utils.mkvc(Ainvb[0]) + + # Solve for phi on pole locations + P1 = mesh.getInterpolationMat(rxloc_M, 'CC') + P2 = mesh.getInterpolationMat(rxloc_N, 'CC') + + # Compute the potential difference + dtemp = (P1*phi - P2*phi)*np.pi + + data.append( dtemp ) + print '\rTransmitter {0} of {1} -> Time:{2} sec'.format(ii,len(Tx),time.time()- start_time), + + print 'Transmitter {0} of {1}'.format(ii,len(Tx)) + print 'Forward completed' + + # Let's just convert the 3D format into 2D (distance along line) and plot + survey2D = DC.convertObs_DC3D_to_2D(survey, np.ones(survey.nSrc) , 'Xloc') + survey2D.dobs =np.hstack(data) + + if plotIt: + import matplotlib.pyplot as plt + fig = plt.figure(figsize=(7,7)) + ax = plt.subplot(2,1,1, aspect='equal') + # Plot the location of the spheres for reference + circle1=plt.Circle((loc[0,0],loc[2,0]),radi[0],color='w',fill=False, lw=3) + circle2=plt.Circle((loc[0,1],loc[2,1]),radi[1],color='k',fill=False, lw=3) + ax.add_artist(circle1) + ax.add_artist(circle2) + + dat = mesh.plotSlice(np.log10(model), ax =ax, normal = 'Y', + ind = indy,grid=True, clim = np.log10([sig.min(),sig.max()])) + + ax.set_title('3-D model') + plt.gca().set_aspect('equal', adjustable='box') + + plt.scatter(Tx[0][0,:],Tx[0][2,:],s=40,c='g', marker='v') + plt.scatter(Rx[0][:,0::3],Rx[0][:,2::3],s=40,c='y') + plt.xlim([-xlim,xlim]) + plt.ylim([-zlim,mesh.vectorNz[-1]+dx]) + + + pos = ax.get_position() + ax.set_position([pos.x0 , pos.y0 + 0.025 , pos.width, pos.height]) + pos = ax.get_position() + cbarax = fig.add_axes([pos.x0 , pos.y0 + 0.025 , pos.width, pos.height * 0.04]) ## the parameters are the specified position you set + cb = fig.colorbar(dat[0],cax=cbarax, orientation="horizontal", + ax = ax, ticks=np.linspace(np.log10(sig.min()), + np.log10(sig.max()), 3), format="$10^{%.1f}$") + cb.set_label("Conductivity (S/m)",size=12) + cb.ax.tick_params(labelsize=12) + + # Second plot for the predicted apparent resistivity data + ax2 = plt.subplot(2,1,2, aspect='equal') + + # Plot the location of the spheres for reference + circle1=plt.Circle((loc[0,0],loc[2,0]),radi[0],color='w',fill=False, lw=3) + circle2=plt.Circle((loc[0,1],loc[2,1]),radi[1],color='k',fill=False, lw=3) + ax2.add_artist(circle1) + ax2.add_artist(circle2) + + # Add the speudo section + dat = DC.plot_pseudoSection(survey2D,ax2,stype=stype, dtype = dtype) + + # plt.scatter(Tx2d[0][:],Tx[0][2,:],s=40,c='g', marker='v') + # plt.scatter(Rx2d[0][:],Rx[0][:,2::3],s=40,c='y') + # plt.plot(np.r_[Tx2d[0][0],Rx2d[-1][-1,-1]],np.ones(2)*mesh.vectorNz[-1], color='k') + ax2.set_title('Apparent Conductivity data') + + plt.ylim([-zlim,mesh.vectorNz[-1]+dx]) + plt.show() + + return fig, ax + +if __name__ == '__main__': + run() diff --git a/SimPEG/Examples/EM_FDEM_1D_Inversion.py b/SimPEG/Examples/EM_FDEM_1D_Inversion.py index 1556bf63..0a5c59a0 100644 --- a/SimPEG/Examples/EM_FDEM_1D_Inversion.py +++ b/SimPEG/Examples/EM_FDEM_1D_Inversion.py @@ -43,16 +43,15 @@ def run(plotIt=True): rxOffset=10. - bzi = EM.FDEM.Rx(np.array([[rxOffset, 0., 1e-3]]), 'bzi') + bzi = EM.FDEM.Rx.bField(np.array([[rxOffset, 0., 1e-3]]), orientation='z', real_or_imag='imag') freqs = np.logspace(1,3,10) srcLoc = np.array([0., 0., 10.]) - srcList = [] - [srcList.append(EM.FDEM.Src.MagDipole([bzi],freq, srcLoc,orientation='Z')) for freq in freqs] + srcList = [EM.FDEM.Src.MagDipole([bzi],freq, srcLoc,orientation='Z') for freq in freqs] survey = EM.FDEM.Survey(srcList) - prb = EM.FDEM.Problem_b(mesh, mapping=mapping) + prb = EM.FDEM.Problem3D_b(mesh, mapping=mapping) try: from pymatsolver import MumpsSolver diff --git a/SimPEG/Examples/EM_Schenkel_Morrison_Casing.py b/SimPEG/Examples/EM_Schenkel_Morrison_Casing.py new file mode 100644 index 00000000..930b452a --- /dev/null +++ b/SimPEG/Examples/EM_Schenkel_Morrison_Casing.py @@ -0,0 +1,275 @@ +from SimPEG import * +from SimPEG.EM import FDEM, Analytics, mu_0 +import time + +try: + from pymatsolver import MumpsSolver + solver = MumpsSolver +except Exception: + solver = SolverLU + pass + +def run(plotIt=True): + """ + EM: Schenkel and Morrison Casing Model + ====================================== + + Here we create and run a FDEM forward simulation to calculate the vertical + current inside a steel-cased. The model is based on the Schenkel and + Morrison Casing Model, and the results are used in a 2016 SEG abstract by + Yang et al. + + - Schenkel, C.J., and H.F. Morrison, 1990, Effects of well casing on potential field measurements using downhole current sources: Geophysical prospecting, 38, 663-686. + + + The model consists of: + - Air: Conductivity 1e-8 S/m, above z = 0 + - Background: conductivity 1e-2 S/m, below z = 0 + - Casing: conductivity 1e6 S/m + - 300m long + - radius of 0.1m + - thickness of 6e-3m + + Inside the casing, we take the same conductivity as the background. + + We are using an EM code to simulate DC, so we use frequency low enough + that the skin depth inside the casing is longer than the casing length (f + = 1e-6 Hz). The plot produced is of the current inside the casing. + + These results are shown in the SEG abstract by Yang et al., 2016: 3D DC + resistivity modeling of steel casing for reservoir monitoring using + equivalent resistor network. The solver used to produce these results and + achieve the CPU time of ~30s is Mumps, which was installed using pymatsolver_ + + .. _pymatsolver: https://github.com/rowanc1/pymatsolver + + This example is on figshare: https://dx.doi.org/10.6084/m9.figshare.3126961.v1 + + If you would use this example for a code comparison, or build upon it, a + citation would be much appreciated! + + """ + + if plotIt: + import matplotlib.pylab as plt + + # ------------------ MODEL ------------------ + sigmaair = 1e-8 # air + sigmaback = 1e-2 # background + sigmacasing = 1e6 # casing + sigmainside = sigmaback # inside the casing + + + casing_t = 0.006 # 1cm thickness + casing_l = 300 # length of the casing + + casing_r = 0.1 + casing_a = casing_r - casing_t/2. # inner radius + casing_b = casing_r + casing_t/2. # outer radius + casing_z = np.r_[-casing_l,0.] + + + # ------------------ SURVEY PARAMETERS ------------------ + freqs = np.r_[1e-6] #[1e-1, 1, 5] # frequencies + dsz = -300 # down-hole z source location + src_loc = np.r_[0.,0.,dsz] + inf_loc = np.r_[0.,0.,1e4] + + print 'Skin Depth: ', [(500./np.sqrt(sigmaback*_)) for _ in freqs] + + + # ------------------ MESH ------------------ + # fine cells near well bore + csx1, csx2 = 2e-3, 60. + pfx1, pfx2 = 1.3, 1.3 + ncx1 = np.ceil(casing_b/csx1+2) + + # pad nicely to second cell size + npadx1 = np.floor(np.log(csx2/csx1) / np.log(pfx1)) + hx1a,hx1b = Utils.meshTensor([(csx1,ncx1)]),Utils.meshTensor([(csx1,npadx1,pfx1)]) + dx1 = sum(hx1a)+sum(hx1b) + dx1 = np.floor(dx1/csx2) + hx1b *= (dx1*csx2 - sum(hx1a))/sum(hx1b) + + # second chunk of mesh + dx2 = 300. # uniform mesh out to here + ncx2 = np.ceil((dx2 - dx1)/csx2) + npadx2 = 45 + hx2a, hx2b = Utils.meshTensor([(csx2,ncx2)]), Utils.meshTensor([(csx2,npadx2,pfx2)]) + hx = np.hstack([hx1a,hx1b,hx2a,hx2b]) + + # z-direction + csz = 0.05 + nza = 10 + ncz, npadzu, npadzd = np.int(np.ceil(np.diff(casing_z)[0]/csz))+10, 68, 68 # cell size, number of core cells, number of padding cells in the x- direction + hz = Utils.meshTensor([(csz,npadzd,-1.3), (csz,ncz), (csz,npadzu,1.3)]) # vector of cell widths in the z-direction + + # Mesh + mesh = Mesh.CylMesh([hx,1.,hz], [0.,0.,-np.sum(hz[:npadzu+ncz-nza])]) + + print 'Mesh Extent xmax: %f,: zmin: %f, zmax: %f'%(mesh.vectorCCx.max(), mesh.vectorCCz.min(), mesh.vectorCCz.max()) + print 'Number of cells', mesh.nC + + if plotIt is True: + fig, ax = plt.subplots(1, 1, figsize=(6, 4)) + ax.set_title('Simulation Mesh') + mesh.plotGrid(ax=ax) + plt.show() + + # Put the model on the mesh + sigWholespace = sigmaback*np.ones((mesh.nC)) + + sigBack = sigWholespace.copy() + sigBack[mesh.gridCC[:,2] > 0.] = sigmaair + + sigCasing = sigBack.copy() + iCasingZ = (mesh.gridCC[:,2] <= casing_z[1]) & (mesh.gridCC[:,2] >= casing_z[0]) + iCasingX = (mesh.gridCC[:,0] >= casing_a) & (mesh.gridCC[:,0] <= casing_b) + iCasing = iCasingX & iCasingZ + sigCasing[iCasing] = sigmacasing + + + if plotIt is True: + + # plotting parameters + xlim = np.r_[0., 0.2] + zlim = np.r_[-350., 10.] + clim_sig = np.r_[-8,6] + + # plot models + fig, ax = plt.subplots(1,1,figsize=(4,4)) + + f = plt.colorbar(mesh.plotImage(np.log10(sigCasing),ax=ax)[0], ax=ax) + ax.grid(which='both') + ax.set_title('Log_10 (Sigma)') + ax.set_xlim(xlim) + ax.set_ylim(zlim) + f.set_clim(clim_sig) + + plt.show() + + + # -------------- Sources -------------------- + # Define Custom Current Sources + + # surface source + sg_x = np.zeros(mesh.vnF[0],dtype=complex) + sg_y = np.zeros(mesh.vnF[1],dtype=complex) + sg_z = np.zeros(mesh.vnF[2],dtype=complex) + + nza = 2 # put the wire two cells above the surface + ncin = 2 + + # vertically directed wire + sgv_indx = (mesh.gridFz[:,0] > casing_a) & (mesh.gridFz[:,0] < casing_a + csx1) # hook it up to casing at the surface + sgv_indz = (mesh.gridFz[:,2] <= +csz*nza) & (mesh.gridFz[:,2] >= -csz*2) + sgv_ind = sgv_indx & sgv_indz + sg_z[sgv_ind] = -1. + + # horizontally directed wire + sgh_indx = (mesh.gridFx[:,0] > casing_a) & (mesh.gridFx[:,0] <= inf_loc[2]) + sgh_indz = (mesh.gridFx[:,2] > csz*(nza-0.5)) & (mesh.gridFx[:,2] < csz*(nza+0.5)) + sgh_ind = sgh_indx & sgh_indz + sg_x[sgh_ind] = -1. + + sgv2_indx = (mesh.gridFz[:,0] >= mesh.gridFx[sgh_ind,0].max()) & (mesh.gridFz[:,0] <= inf_loc[2]*1.2) # hook it up to casing at the surface + sgv2_indz = (mesh.gridFz[:,2] <= +csz*nza) & (mesh.gridFz[:,2] >= -csz*2) + sgv2_ind = sgv2_indx & sgv2_indz + sg_z[sgv2_ind] = 1. + + # assemble the source + sg = np.hstack([sg_x,sg_y,sg_z]) + sg_p = [FDEM.Src.RawVec_e([],_,sg/mesh.area) for _ in freqs] + + # downhole source + dg_x = np.zeros(mesh.vnF[0],dtype=complex) + dg_y = np.zeros(mesh.vnF[1],dtype=complex) + dg_z = np.zeros(mesh.vnF[2],dtype=complex) + + # vertically directed wire + dgv_indx = (mesh.gridFz[:,0] < csx1) # go through the center of the well + dgv_indz = (mesh.gridFz[:,2] <= +csz*nza) & (mesh.gridFz[:,2] > dsz + csz/2.) + dgv_ind = dgv_indx & dgv_indz + dg_z[dgv_ind] = -1. + + # couple to the casing downhole + dgh_indx = mesh.gridFx[:,0] < casing_a + csx1 + dgh_indz = (mesh.gridFx[:,2] < dsz + csz) & (mesh.gridFx[:,2] >= dsz) + dgh_ind = dgh_indx & dgh_indz + dg_x[dgh_ind] = 1. + + # horizontal part at surface + dgh2_indx = mesh.gridFx[:,0] <= inf_loc[2]*1.2 + dgh2_indz = sgh_indz.copy() + dgh2_ind = dgh2_indx & dgh2_indz + dg_x[dgh2_ind] = -1. + + # vertical part at surface + dgv2_ind = sgv2_ind.copy() + dg_z[dgv2_ind] = 1. + + # assemble the source + dg = np.hstack([dg_x,dg_y,dg_z]) + dg_p = [FDEM.Src.RawVec_e([],_,dg/mesh.area) for _ in freqs] + + # ------------ Problem and Survey --------------- + survey = FDEM.Survey(sg_p + dg_p) + mapping = [('sigma', Maps.IdentityMap(mesh))] + problem = FDEM.Problem3D_h(mesh, mapping=mapping) + problem.pair(survey) + + # ------------- Solve --------------------------- + t0 = time.time() + fieldsCasing = problem.fields(sigCasing) + print 'Time to solve 2 sources', time.time() - t0 + + # Plot current + + # current density + jn0 = fieldsCasing[dg_p,'j'] + jn1 = fieldsCasing[sg_p,'j'] + + # current + in0 = [mesh.area*fieldsCasing[dg_p,'j'][:,i] for i in range(len(freqs))] + in1 = [mesh.area*fieldsCasing[sg_p,'j'][:,i] for i in range(len(freqs))] + + in0 = np.vstack(in0).T + in1 = np.vstack(in1).T + + # integrate to get z-current inside casing + inds_inx = (mesh.gridFz[:,0] >= casing_a) & (mesh.gridFz[:,0] <= casing_b) + inds_inz = (mesh.gridFz[:,2] >= dsz ) & (mesh.gridFz[:,2] <= 0) + inds_fz = inds_inx & inds_inz + + indsx = [False]*mesh.nFx + inds = list(indsx) + list(inds_fz) + + in0_in = in0[np.r_[inds]] + in1_in = in1[np.r_[inds]] + z_in = mesh.gridFz[inds_fz,2] + + in0_in = in0_in.reshape([in0_in.shape[0]/3,3]) + in1_in = in1_in.reshape([in1_in.shape[0]/3,3]) + z_in = z_in.reshape([z_in.shape[0]/3,3]) + + I0 = in0_in.sum(1).real + I1 = in1_in.sum(1).real + z_in = z_in[:,0] + + if plotIt is True: + fig, ax = plt.subplots(1,2,figsize=(12,4)) + + ax[0].plot(z_in,np.absolute(I0), z_in,np.absolute(I1)) + ax[0].legend(['top casing', 'bottom casing'],loc='best') + ax[0].set_title('Magnitude of Vertical Current in Casing') + + ax[1].semilogy(z_in,np.absolute(I0), z_in,np.absolute(I1)) + ax[1].legend(['top casing', 'bottom casing'],loc='best') + ax[1].set_title('Magnitude of Vertical Current in Casing') + ax[1].set_ylim([1e-2, 1.]) + + plt.show() + +if __name__ == '__main__': + run() + diff --git a/SimPEG/Examples/Inversion_IRLS.py b/SimPEG/Examples/Inversion_IRLS.py new file mode 100644 index 00000000..06ef4be4 --- /dev/null +++ b/SimPEG/Examples/Inversion_IRLS.py @@ -0,0 +1,132 @@ +from SimPEG import * + + +def run(N=200, plotIt=True): + """ + Inversion: Linear Problem + ========================= + + Here we go over the basics of creating a linear problem and inversion. + + """ + + + np.random.seed(1) + + std_noise = 1e-2 + + mesh = Mesh.TensorMesh([N]) + + m0 = np.ones(mesh.nC) * 1e-4 + nk = 10 + jk = np.linspace(1.,nk,nk) + p = -2. + q = 1. + + g = lambda k: np.exp(p*jk[k]*mesh.vectorCCx)*np.cos(np.pi*q*jk[k]*mesh.vectorCCx) + + G = np.empty((nk, mesh.nC)) + + for i in range(nk): + G[i,:] = g(i) + + mtrue = np.zeros(mesh.nC) + mtrue[mesh.vectorCCx > 0.3] = 1. + mtrue[mesh.vectorCCx > 0.45] = -0.5 + mtrue[mesh.vectorCCx > 0.6] = 0 + + + prob = Problem.LinearProblem(mesh, G) + survey = Survey.LinearSurvey() + survey.pair(prob) + survey.dobs = prob.fields(mtrue) + std_noise * np.random.randn(nk) + #survey.makeSyntheticData(mtrue, std=std_noise) + + wd = np.ones(nk) * std_noise + + #print survey.std[0] + #M = prob.mesh + # Distance weighting + wr = np.sum(prob.G**2.,axis=0)**0.5 + wr = ( wr/np.max(wr) ) + + reg = Regularization.Simple(mesh) + reg.wght = wr + + dmis = DataMisfit.l2_DataMisfit(survey) + dmis.Wd = 1./wd + + opt = Optimization.ProjectedGNCG(maxIter=30,lower=-2.,upper=2., maxIterCG= 20, tolCG = 1e-4) + invProb = InvProblem.BaseInvProblem(dmis, reg, opt) + invProb.curModel = m0 + + beta = Directives.BetaSchedule(coolingFactor=2, coolingRate=1) + target = Directives.TargetMisfit() + + betaest = Directives.BetaEstimate_ByEig() + inv = Inversion.BaseInversion(invProb, directiveList=[beta, betaest, target]) + + + mrec = inv.run(m0) + ml2 = mrec + print "Final misfit:" + str(invProb.dmisfit.eval(mrec)) + + # Switch regularization to sparse + phim = invProb.phi_m_last + phid = invProb.phi_d + + reg = Regularization.Sparse(mesh) + +#============================================================================== +# fig, axes = plt.subplots(1,2,figsize=(12*1.2,4*1.2)) +# dmdx = reg.mesh.cellDiffxStencil * mrec +# plt.plot(np.sort(dmdx)) +#============================================================================== + + #reg.recModel = mrec + reg.wght = np.ones(mesh.nC) + reg.mref = np.zeros(mesh.nC) + reg.eps_p = 5e-2 + reg.eps_q = 1e-2 + reg.norms = [0., 0., 2., 2.] + reg.wght = wr + + opt = Optimization.ProjectedGNCG(maxIter=10 ,lower=-2.,upper=2., maxIterLS = 20, maxIterCG= 20, tolCG = 1e-3) + invProb = InvProblem.BaseInvProblem(dmis, reg, opt, beta = invProb.beta*2.) + beta = Directives.BetaSchedule(coolingFactor=1, coolingRate=1) + #betaest = Directives.BetaEstimate_ByEig() + target = Directives.TargetMisfit() + IRLS =Directives.Update_IRLS( phi_m_last = phim, phi_d_last = phid ) + + inv = Inversion.BaseInversion(invProb, directiveList=[beta,IRLS]) + + m0 = mrec + + # Run inversion + mrec = inv.run(m0) + + print "Final misfit:" + str(invProb.dmisfit.eval(mrec)) + + + if plotIt: + import matplotlib.pyplot as plt + + fig, axes = plt.subplots(1,2,figsize=(12*1.2,4*1.2)) + for i in range(prob.G.shape[0]): + axes[0].plot(prob.G[i,:]) + axes[0].set_title('Columns of matrix G') + + axes[1].plot(mesh.vectorCCx, mtrue, 'b-') + axes[1].plot(mesh.vectorCCx, ml2, 'r-') + #axes[1].legend(('True Model', 'Recovered Model')) + axes[1].set_ylim(-1.0,1.25) + + axes[1].plot(mesh.vectorCCx, mrec, 'k-',lw = 2) + axes[1].legend(('True Model', 'Smooth l2-l2', + 'Sparse lp:' + str(reg.norms[0]) + ', lqx:' + str(reg.norms[1]) ), fontsize = 12) + plt.show() + + return prob, survey, mesh, mrec + +if __name__ == '__main__': + run() diff --git a/SimPEG/Examples/Inversion_Linear.py b/SimPEG/Examples/Inversion_Linear.py index 6d342a04..47d49c7f 100644 --- a/SimPEG/Examples/Inversion_Linear.py +++ b/SimPEG/Examples/Inversion_Linear.py @@ -10,28 +10,6 @@ def run(N=100, plotIt=True): """ - class LinearSurvey(Survey.BaseSurvey): - def eval(self, u): - return u - - class LinearProblem(Problem.BaseProblem): - - surveyPair = LinearSurvey - - def __init__(self, mesh, G, **kwargs): - Problem.BaseProblem.__init__(self, mesh, **kwargs) - self.G = G - - def fields(self, m, u=None): - return self.G.dot(m) - - def Jvec(self, m, v, u=None): - return self.G.dot(v) - - def Jtvec(self, m, v, u=None): - return self.G.T.dot(v) - - np.random.seed(1) mesh = Mesh.TensorMesh([N]) @@ -53,8 +31,8 @@ def run(N=100, plotIt=True): mtrue[mesh.vectorCCx > 0.45] = -0.5 mtrue[mesh.vectorCCx > 0.6] = 0 - prob = LinearProblem(mesh, G) - survey = LinearSurvey() + prob = Problem.LinearProblem(mesh, G) + survey = Survey.LinearSurvey() survey.pair(prob) survey.makeSyntheticData(mtrue, std=0.01) diff --git a/SimPEG/Examples/MT_1D_ForwardAndInversion.py b/SimPEG/Examples/MT_1D_ForwardAndInversion.py index cf17e77b..69cedd98 100644 --- a/SimPEG/Examples/MT_1D_ForwardAndInversion.py +++ b/SimPEG/Examples/MT_1D_ForwardAndInversion.py @@ -100,7 +100,7 @@ def run(plotIt=True): # Regularization - with a regularization mesh regMesh = simpeg.Mesh.TensorMesh([m1d.hx[problem.mapping.sigmaMap.maps[-1].indActive]],m1d.x0) reg = simpeg.Regularization.Tikhonov(regMesh) - reg.smoothModel = True + reg.mrefInSmooth = True reg.alpha_s = 1e-7 reg.alpha_x = 1. # Inversion problem diff --git a/SimPEG/Examples/__init__.py b/SimPEG/Examples/__init__.py index dd35c0da..c67652a2 100644 --- a/SimPEG/Examples/__init__.py +++ b/SimPEG/Examples/__init__.py @@ -1,11 +1,15 @@ # Run this file to add imports. ##### AUTOIMPORTS ##### +import DC_Analytic_Dipole +import DC_Forward_PseudoSection import EM_FDEM_1D_Inversion import EM_FDEM_Analytic_MagDipoleWholespace +import EM_Schenkel_Morrison_Casing import EM_TDEM_1D_Inversion import FLOW_Richards_1D_Celia1990 import Forward_BasicDirectCurrent +import Inversion_IRLS import Inversion_Linear import Mesh_Basic_PlotImage import Mesh_Basic_Types @@ -17,7 +21,7 @@ import Mesh_Tensor_Creation import MT_1D_ForwardAndInversion import MT_3D_Foward -__examples__ = ["EM_FDEM_1D_Inversion", "EM_FDEM_Analytic_MagDipoleWholespace", "EM_TDEM_1D_Inversion", "FLOW_Richards_1D_Celia1990", "Forward_BasicDirectCurrent", "Inversion_Linear", "Mesh_Basic_PlotImage", "Mesh_Basic_Types", "Mesh_Operators_CahnHilliard", "Mesh_QuadTree_Creation", "Mesh_QuadTree_FaceDiv", "Mesh_QuadTree_HangingNodes", "Mesh_Tensor_Creation", "MT_1D_ForwardAndInversion", "MT_3D_Foward"] +__examples__ = ["DC_Analytic_Dipole", "DC_Forward_PseudoSection", "EM_FDEM_1D_Inversion", "EM_FDEM_Analytic_MagDipoleWholespace", "EM_Schenkel_Morrison_Casing", "EM_TDEM_1D_Inversion", "FLOW_Richards_1D_Celia1990", "Forward_BasicDirectCurrent", "Inversion_IRLS", "Inversion_Linear", "Mesh_Basic_PlotImage", "Mesh_Basic_Types", "Mesh_Operators_CahnHilliard", "Mesh_QuadTree_Creation", "Mesh_QuadTree_FaceDiv", "Mesh_QuadTree_HangingNodes", "Mesh_Tensor_Creation", "MT_1D_ForwardAndInversion", "MT_3D_Foward"] ##### AUTOIMPORTS ##### diff --git a/SimPEG/FLOW/Richards/RichardsProblem.py b/SimPEG/FLOW/Richards/RichardsProblem.py index 4dcabe60..2346f4da 100644 --- a/SimPEG/FLOW/Richards/RichardsProblem.py +++ b/SimPEG/FLOW/Richards/RichardsProblem.py @@ -45,19 +45,19 @@ class RichardsSurvey(Survey.BaseSurvey): @Utils.count @Utils.requires('prob') - def dpred(self, m, u=None): + def dpred(self, m, f=None): """ Create the projected data from a model. - The field, u, (if provided) will be used for the predicted data + The field, f, (if provided) will be used for the predicted data instead of recalculating the fields (which may be expensive!). .. math:: - d_\\text{pred} = P(u(m), m) + d_\\text{pred} = P(f(m), m) Where P is a projection of the fields onto the data space. """ - if u is None: u = self.prob.fields(m) - return Utils.mkvc(self.eval(u, m)) + if f is None: f = self.prob.fields(m) + return Utils.mkvc(self.eval(f, m)) @Utils.requires('prob') def eval(self, U, m): @@ -233,16 +233,16 @@ class RichardsProblem(Problem.BaseTimeProblem): return r, J @Utils.timeIt - def Jfull(self, m, u=None): - if u is None: - u = self.fields(m) + def Jfull(self, m, f=None): + if f is None: + f = self.fields(m) - nn = len(u)-1 + nn = len(f)-1 Asubs, Adiags, Bs = range(nn), range(nn), range(nn) for ii in range(nn): dt = self.timeSteps[ii] - bc = self.getBoundaryConditions(ii, u[ii]) - Asubs[ii], Adiags[ii], Bs[ii] = self.diagsJacobian(m, u[ii], u[ii+1], dt, bc) + bc = self.getBoundaryConditions(ii, f[ii]) + Asubs[ii], Adiags[ii], Bs[ii] = self.diagsJacobian(m, f[ii], f[ii+1], dt, bc) Ad = sp.block_diag(Adiags) zRight = Utils.spzeros((len(Asubs)-1)*Asubs[0].shape[0],Adiags[0].shape[1]) zTop = Utils.spzeros(Adiags[0].shape[0], len(Adiags)*Adiags[0].shape[1]) @@ -251,7 +251,7 @@ class RichardsProblem(Problem.BaseTimeProblem): B = np.array(sp.vstack(Bs).todense()) Ainv = self.Solver(A, **self.solverOpts) - P = self.survey.evalDeriv(u, m) + P = self.survey.evalDeriv(f, m) AinvB = Ainv * B z = np.zeros((self.mesh.nC, B.shape[1])) zAinvB = np.vstack((z, AinvB)) @@ -259,41 +259,41 @@ class RichardsProblem(Problem.BaseTimeProblem): return J @Utils.timeIt - def Jvec(self, m, v, u=None): - if u is None: - u = self.fields(m) + def Jvec(self, m, v, f=None): + if f is None: + f = self.fields(m) - JvC = range(len(u)-1) # Cell to hold each row of the long vector. + JvC = range(len(f)-1) # Cell to hold each row of the long vector. # This is done via forward substitution. - bc = self.getBoundaryConditions(0, u[0]) - temp, Adiag, B = self.diagsJacobian(m, u[0], u[1], self.timeSteps[0], bc) + bc = self.getBoundaryConditions(0, f[0]) + temp, Adiag, B = self.diagsJacobian(m, f[0], f[1], self.timeSteps[0], bc) Adiaginv = self.Solver(Adiag, **self.solverOpts) JvC[0] = Adiaginv * (B*v) - for ii in range(1,len(u)-1): - bc = self.getBoundaryConditions(ii, u[ii]) - Asub, Adiag, B = self.diagsJacobian(m, u[ii], u[ii+1], self.timeSteps[ii], bc) + for ii in range(1,len(f)-1): + bc = self.getBoundaryConditions(ii, f[ii]) + Asub, Adiag, B = self.diagsJacobian(m, f[ii], f[ii+1], self.timeSteps[ii], bc) Adiaginv = self.Solver(Adiag, **self.solverOpts) JvC[ii] = Adiaginv * (B*v - Asub*JvC[ii-1]) - P = self.survey.evalDeriv(u, m) + P = self.survey.evalDeriv(f, m) return P * np.concatenate([np.zeros(self.mesh.nC)] + JvC) @Utils.timeIt - def Jtvec(self, m, v, u=None): - if u is None: - u = self.field(m) + def Jtvec(self, m, v, f=None): + if f is None: + f = self.field(m) - P = self.survey.evalDeriv(u, m) + P = self.survey.evalDeriv(f, m) PTv = P.T*v # This is done via backward substitution. minus = 0 BJtv = 0 - for ii in range(len(u)-1,0,-1): - bc = self.getBoundaryConditions(ii-1, u[ii-1]) - Asub, Adiag, B = self.diagsJacobian(m, u[ii-1], u[ii], self.timeSteps[ii-1], bc) + for ii in range(len(f)-1,0,-1): + bc = self.getBoundaryConditions(ii-1, f[ii-1]) + Asub, Adiag, B = self.diagsJacobian(m, f[ii-1], f[ii], self.timeSteps[ii-1], bc) #select the correct part of v vpart = range((ii)*Adiag.shape[0], (ii+1)*Adiag.shape[0]) AdiaginvT = self.Solver(Adiag.T, **self.solverOpts) diff --git a/SimPEG/InvProblem.py b/SimPEG/InvProblem.py index 0296bf4b..fd6c48c3 100644 --- a/SimPEG/InvProblem.py +++ b/SimPEG/InvProblem.py @@ -82,23 +82,23 @@ class BaseInvProblem(object): self._warmstart = value def getFields(self, m, store=False, deleteWarmstart=True): - u = None + f = None for mtest, u_ofmtest in self.warmstart: if m is mtest: - u = u_ofmtest + f = u_ofmtest if self.debug: print 'InvProb is Warm Starting!' break - if u is None: - u = self.prob.fields(m) + if f is None: + f = self.prob.fields(m) if deleteWarmstart: self.warmstart = [] if store: - self.warmstart += [(m,u)] + self.warmstart += [(m,f)] - return u + return f @Utils.timeIt def evalFunction(self, m, return_g=True, return_H=True): @@ -109,21 +109,21 @@ class BaseInvProblem(object): gc.collect() # Store fields if doing a line-search - u = self.getFields(m, store=(return_g==False and return_H==False)) + f = self.getFields(m, store=(return_g==False and return_H==False)) - phi_d = self.dmisfit.eval(m, u=u) + phi_d = self.dmisfit.eval(m, f=f) phi_m = self.reg.eval(m) - self.dpred = self.survey.dpred(m, u=u) # This is a cheap matrix vector calculation. + self.dpred = self.survey.dpred(m, f=f) # This is a cheap matrix vector calculation. self.phi_d, self.phi_d_last = phi_d, self.phi_d self.phi_m, self.phi_m_last = phi_m, self.phi_m - f = phi_d + self.beta * phi_m + phi = phi_d + self.beta * phi_m - out = (f,) + out = (phi,) if return_g: - phi_dDeriv = self.dmisfit.evalDeriv(m, u=u) + phi_dDeriv = self.dmisfit.evalDeriv(m, f=f) phi_mDeriv = self.reg.evalDeriv(m) g = phi_dDeriv + self.beta * phi_mDeriv @@ -131,7 +131,7 @@ class BaseInvProblem(object): if return_H: def H_fun(v): - phi_d2Deriv = self.dmisfit.eval2Deriv(m, v, u=u) + phi_d2Deriv = self.dmisfit.eval2Deriv(m, v, f=f) phi_m2Deriv = self.reg.eval2Deriv(m, v=v) return phi_d2Deriv + self.beta * phi_m2Deriv diff --git a/SimPEG/Inversion.py b/SimPEG/Inversion.py index 3fe9cfd0..3e92374f 100644 --- a/SimPEG/Inversion.py +++ b/SimPEG/Inversion.py @@ -33,7 +33,9 @@ class BaseInversion(object): self._directiveList = value self._directiveList.inversion = self - def __init__(self, invProb, directiveList=[], **kwargs): + def __init__(self, invProb, directiveList=None, **kwargs): + if directiveList is None: + directiveList = [] self.directiveList = directiveList Utils.setKwargs(self, **kwargs) diff --git a/SimPEG/MT/BaseMT.py b/SimPEG/MT/BaseMT.py index 36389430..c201dfb0 100644 --- a/SimPEG/MT/BaseMT.py +++ b/SimPEG/MT/BaseMT.py @@ -27,7 +27,7 @@ class BaseMTProblem(BaseFDEMProblem): # Might need to add more stuff here. ## NEED to clean up the Jvec and Jtvec to use Zero and Identities for None components. - def Jvec(self, m, v, u=None): + def Jvec(self, m, v, f=None): """ Function to calculate the data sensitivities dD/dm times a vector. @@ -39,8 +39,8 @@ class BaseMTProblem(BaseFDEMProblem): """ # Calculate the fields - if u is None: - u = self.fields(m) + if f is None: + f= self.fields(m) # Set current model self.curModel = m # Initiate the Jv object @@ -56,9 +56,9 @@ class BaseMTProblem(BaseFDEMProblem): # We need fDeriv_m = df/du*du/dm + df/dm # Construct du/dm, it requires a solve # NOTE: need to account for the 2 polarizations in the derivatives. - u_src = u[src,:] + f_src = f[src,:] # dA_dm and dRHS_dm should be of size nE,2, so that we can multiply by dA_duI. The 2 columns are each of the polarizations. - dA_dm = self.getADeriv_m(freq, u_src, v) # Size: nE,2 (u_px,u_py) in the columns. + dA_dm = self.getADeriv_m(freq, f_src, v) # Size: nE,2 (u_px,u_py) in the columns. dRHS_dm = self.getRHSDeriv_m(freq, v) # Size: nE,2 (u_px,u_py) in the columns. if dRHS_dm is None: du_dm = dA_duI * ( -dA_dm ) @@ -68,13 +68,13 @@ class BaseMTProblem(BaseFDEMProblem): for rx in src.rxList: # Get the projection derivative # v should be of size 2*nE (for 2 polarizations) - PDeriv_u = lambda t: rx.evalDeriv(src, self.mesh, u, t) # wrt u, we don't have have PDeriv wrt m + PDeriv_u = lambda t: rx.evalDeriv(src, self.mesh, f, t) # wrt u, we don't have have PDeriv wrt m Jv[src, rx] = PDeriv_u(mkvc(du_dm)) dA_duI.clean() # Return the vectorized sensitivities return mkvc(Jv) - def Jtvec(self, m, v, u=None): + def Jtvec(self, m, v, f=None): """ Function to calculate the transpose of the data sensitivities (dD/dm)^T times a vector. @@ -85,8 +85,8 @@ class BaseMTProblem(BaseFDEMProblem): :return: Data sensitivities wrt m """ - if u is None: - u = self.fields(m) + if f is None: + f = self.fields(m) self.curModel = m @@ -103,15 +103,15 @@ class BaseMTProblem(BaseFDEMProblem): for src in self.survey.getSrcByFreq(freq): ftype = self._fieldType + 'Solution' - u_src = u[src, :] + f_src = f[src, :] for rx in src.rxList: # Get the adjoint evalDeriv # PTv needs to be nE, - PTv = rx.evalDeriv(src, self.mesh, u, mkvc(v[src, rx],2), adjoint=True) # wrt u, need possibility wrt m + PTv = rx.evalDeriv(src, self.mesh, f, mkvc(v[src, rx],2), adjoint=True) # wrt u, need possibility wrt m # Get the dA_duIT = ATinv * PTv - dA_dmT = self.getADeriv_m(freq, u_src, mkvc(dA_duIT), adjoint=True) + dA_dmT = self.getADeriv_m(freq, f_src, mkvc(dA_duIT), adjoint=True) dRHS_dmT = self.getRHSDeriv_m(freq, mkvc(dA_duIT), adjoint=True) # Make du_dmT if dRHS_dmT is None: @@ -129,4 +129,4 @@ class BaseMTProblem(BaseFDEMProblem): raise Exception('Must be real or imag') # Clean the factorization, clear memory. ATinv.clean() - return Jtv \ No newline at end of file + return Jtv diff --git a/SimPEG/MT/SurveyMT.py b/SimPEG/MT/SurveyMT.py index 4e4a8688..0ec91a0e 100644 --- a/SimPEG/MT/SurveyMT.py +++ b/SimPEG/MT/SurveyMT.py @@ -427,15 +427,15 @@ class Survey(SimPEGsurvey.BaseSurvey): assert freq in self._freqDict, "The requested frequency is not in this survey." return self._freqDict[freq] - def eval(self, u): + def eval(self, f): data = Data(self) for src in self.srcList: sys.stdout.flush() for rx in src.rxList: - data[src, rx] = rx.eval(src, self.mesh, u) + data[src, rx] = rx.eval(src, self.mesh, f) return data - def evalDeriv(self, u): + def evalDeriv(self, f): raise Exception('Use Transmitters to project fields deriv.') ################# diff --git a/SimPEG/MT/Utils/ediFilesUtils.py b/SimPEG/MT/Utils/ediFilesUtils.py index 55818a87..d8f5e0ad 100644 --- a/SimPEG/MT/Utils/ediFilesUtils.py +++ b/SimPEG/MT/Utils/ediFilesUtils.py @@ -7,17 +7,16 @@ from SimPEG.MT.Utils.dataUtils import rec2ndarr # Import modules import numpy as np import os, sys, re -try: - import osr -except ImportError as e: - print 'Could not import osr, missing the gdal package' - pass + class EDIimporter: """ A class to import EDIfiles. """ + + + # Define data converters _impUnitEDI2SI = 4*np.pi*1e-4 # Convert Z[mV/km/nT] (as in EDI)to Z[V/A] SI unit _impUnitSI2EDI = 1./_impUnitEDI2SI # ConvertZ[V/A] SI unit to Z[mV/km/nT] (as in EDI) @@ -26,8 +25,8 @@ class EDIimporter: comps = None # Hidden properties - _outEPSG = None - _2out = None + _outEPSG = None # Project info + _2out = None # The projection operator def __init__(self, EDIfilesList, compList=None, outEPSG=None): @@ -113,6 +112,12 @@ class EDIimporter: # nOutData=length(obj.data); # obj.data(nOutData+1:nOutData+length(TEMP.data),:) = TEMP.data; def _transfromPoints(self,longD,latD): + # Import the coordinate projections + try: + import osr + except ImportError as e: + print 'Could not import osr, missing the gdal package\nCan not project coordinates' + raise e # Coordinates convertor if self._2out is None: src = osr.SpatialReference() diff --git a/SimPEG/Maps.py b/SimPEG/Maps.py index 5f71d87c..f17d2314 100644 --- a/SimPEG/Maps.py +++ b/SimPEG/Maps.py @@ -759,15 +759,29 @@ class PolyMap(IdentityMap): m = [\sigma_1, \sigma_2, c] + Can take in an actInd vector to account for topography. + """ - def __init__(self, mesh, order, logSigma=True, normal='X'): + def __init__(self, mesh, order, logSigma=True, normal='X', actInd = None): IdentityMap.__init__(self, mesh) self.logSigma = logSigma self.order = order self.normal = normal + self.actInd = actInd + + if getattr(self, 'actInd', None) is None: + self.actInd = range(self.mesh.nC) + self.nC = self.mesh.nC + + else: + self.nC = len(self.actInd) slope = 1e4 + @property + def shape(self): + return (self.nC, self.nP) + @property def nP(self): if np.isscalar(self.order): @@ -785,8 +799,8 @@ class PolyMap(IdentityMap): sig1, sig2 = np.exp(sig1), np.exp(sig2) #2D if self.mesh.dim == 2: - X = self.mesh.gridCC[:,0] - Y = self.mesh.gridCC[:,1] + X = self.mesh.gridCC[self.actInd,0] + Y = self.mesh.gridCC[self.actInd,1] if self.normal =='X': f = polynomial.polyval(Y, c) - X elif self.normal =='Y': @@ -795,9 +809,9 @@ class PolyMap(IdentityMap): raise(Exception("Input for normal = X or Y or Z")) #3D elif self.mesh.dim == 3: - X = self.mesh.gridCC[:,0] - Y = self.mesh.gridCC[:,1] - Z = self.mesh.gridCC[:,2] + X = self.mesh.gridCC[self.actInd,0] + Y = self.mesh.gridCC[self.actInd,1] + Z = self.mesh.gridCC[self.actInd,2] if self.normal =='X': f = polynomial.polyval2d(Y, Z, c.reshape((self.order[0]+1,self.order[1]+1))) - X elif self.normal =='Y': @@ -806,6 +820,7 @@ class PolyMap(IdentityMap): f = polynomial.polyval2d(X, Y, c.reshape((self.order[0]+1,self.order[1]+1))) - Z else: raise(Exception("Input for normal = X or Y or Z")) + else: raise(Exception("Only supports 2D")) @@ -819,8 +834,8 @@ class PolyMap(IdentityMap): sig1, sig2 = np.exp(sig1), np.exp(sig2) #2D if self.mesh.dim == 2: - X = self.mesh.gridCC[:,0] - Y = self.mesh.gridCC[:,1] + X = self.mesh.gridCC[self.actInd,0] + Y = self.mesh.gridCC[self.actInd,1] if self.normal =='X': f = polynomial.polyval(Y, c) - X @@ -832,9 +847,9 @@ class PolyMap(IdentityMap): raise(Exception("Input for normal = X or Y or Z")) #3D elif self.mesh.dim == 3: - X = self.mesh.gridCC[:,0] - Y = self.mesh.gridCC[:,1] - Z = self.mesh.gridCC[:,2] + X = self.mesh.gridCC[self.actInd,0] + Y = self.mesh.gridCC[self.actInd,1] + Z = self.mesh.gridCC[self.actInd,2] if self.normal =='X': f = polynomial.polyval2d(Y, Z, c.reshape((self.order[0]+1,self.order[1]+1))) - X diff --git a/SimPEG/Mesh/CylMesh.py b/SimPEG/Mesh/CylMesh.py index ecdf36ac..f0b6751e 100644 --- a/SimPEG/Mesh/CylMesh.py +++ b/SimPEG/Mesh/CylMesh.py @@ -330,7 +330,7 @@ class CylMesh(BaseTensorMesh, BaseRectangularMesh, InnerProducts, CylView): raise NotImplementedError('wrapping in the averaging is not yet implemented') return self._aveF2CCV - def getInterpolationMatCartMesh(self, Mrect, locType='CC'): + def getInterpolationMatCartMesh(self, Mrect, locType='CC', locTypeTo=None): """ Takes a cartesian mesh and returns a projection to translate onto the cartesian grid. """ @@ -338,19 +338,22 @@ class CylMesh(BaseTensorMesh, BaseRectangularMesh, InnerProducts, CylView): assert self.isSymmetric, "Currently we have not taken into account other projections for more complicated CylMeshes" + if locTypeTo is None: + locTypeTo = locType + if locType == 'F': # do this three times for each component - X = self.getInterpolationMatCartMesh(Mrect, locType='Fx') - Y = self.getInterpolationMatCartMesh(Mrect, locType='Fy') - Z = self.getInterpolationMatCartMesh(Mrect, locType='Fz') + X = self.getInterpolationMatCartMesh(Mrect, locType='Fx', locTypeTo=locTypeTo+'x') + Y = self.getInterpolationMatCartMesh(Mrect, locType='Fy', locTypeTo=locTypeTo+'y') + Z = self.getInterpolationMatCartMesh(Mrect, locType='Fz', locTypeTo=locTypeTo+'z') return sp.vstack((X,Y,Z)) if locType == 'E': - X = self.getInterpolationMatCartMesh(Mrect, locType='Ex') - Y = self.getInterpolationMatCartMesh(Mrect, locType='Ey') - Z = spzeros(Mrect.nEz, self.nE) + X = self.getInterpolationMatCartMesh(Mrect, locType='Ex', locTypeTo=locTypeTo+'x') + Y = self.getInterpolationMatCartMesh(Mrect, locType='Ey', locTypeTo=locTypeTo+'y') + Z = spzeros(getattr(Mrect, 'n' + locTypeTo + 'z'), self.nE) return sp.vstack((X,Y,Z)) - grid = getattr(Mrect, 'grid' + locType) + grid = getattr(Mrect, 'grid' + locTypeTo) # This is unit circle stuff, 0 to 2*pi, starting at x-axis, rotating counter clockwise in an x-y slice theta = - np.arctan2(grid[:,0] - self.cartesianOrigin[0], grid[:,1] - self.cartesianOrigin[1]) + np.pi/2 theta[theta < 0] += np.pi*2.0 @@ -366,7 +369,7 @@ class CylMesh(BaseTensorMesh, BaseRectangularMesh, InnerProducts, CylView): 'Ex': Mrect.tangents[:Mrect.nEx,:], 'Ey': Mrect.tangents[Mrect.nEx:(Mrect.nEx+Mrect.nEy),:], 'Ez': Mrect.tangents[-Mrect.nEz:,:], - }[locType] + }[locTypeTo] if 'F' in locType: normals = np.c_[np.cos(theta), np.sin(theta), np.zeros(theta.size)] proj = ( normals * dotMe ).sum(axis=1) diff --git a/SimPEG/Mesh/DiffOperators.py b/SimPEG/Mesh/DiffOperators.py index 00bf0259..d0363001 100644 --- a/SimPEG/Mesh/DiffOperators.py +++ b/SimPEG/Mesh/DiffOperators.py @@ -307,24 +307,28 @@ class DiffOperators(object): return BC _cellGradBC_list = 'neumann' + def _cellGradStencil(self): + BC = self.setCellGradBC(self._cellGradBC_list) + n = self.vnC + if(self.dim == 1): + G = ddxCellGrad(n[0], BC[0]) + elif(self.dim == 2): + G1 = sp.kron(speye(n[1]), ddxCellGrad(n[0], BC[0])) + G2 = sp.kron(ddxCellGrad(n[1], BC[1]), speye(n[0])) + G = sp.vstack((G1, G2), format="csr") + elif(self.dim == 3): + G1 = kron3(speye(n[2]), speye(n[1]), ddxCellGrad(n[0], BC[0])) + G2 = kron3(speye(n[2]), ddxCellGrad(n[1], BC[1]), speye(n[0])) + G3 = kron3(ddxCellGrad(n[2], BC[2]), speye(n[1]), speye(n[0])) + G = sp.vstack((G1, G2, G3), format="csr") + return G + def cellGrad(): doc = "The cell centered Gradient, takes you to cell faces." def fget(self): if(self._cellGrad is None): - BC = self.setCellGradBC(self._cellGradBC_list) - n = self.vnC - if(self.dim == 1): - G = ddxCellGrad(n[0], BC[0]) - elif(self.dim == 2): - G1 = sp.kron(speye(n[1]), ddxCellGrad(n[0], BC[0])) - G2 = sp.kron(ddxCellGrad(n[1], BC[1]), speye(n[0])) - G = sp.vstack((G1, G2), format="csr") - elif(self.dim == 3): - G1 = kron3(speye(n[2]), speye(n[1]), ddxCellGrad(n[0], BC[0])) - G2 = kron3(speye(n[2]), ddxCellGrad(n[1], BC[1]), speye(n[0])) - G3 = kron3(ddxCellGrad(n[2], BC[2]), speye(n[1]), speye(n[0])) - G = sp.vstack((G1, G2, G3), format="csr") + G = self._cellGradStencil() # Compute areas of cell faces & volumes S = self.area V = self.aveCC2F*self.vol # Average volume between adjacent cells @@ -361,19 +365,24 @@ class DiffOperators(object): _cellGradBC = None cellGradBC = property(**cellGradBC()) + def _cellGradxStencil(self): + BC = ['neumann', 'neumann'] + n = self.vnC + if(self.dim == 1): + G1 = ddxCellGrad(n[0], BC) + elif(self.dim == 2): + G1 = sp.kron(speye(n[1]), ddxCellGrad(n[0], BC)) + elif(self.dim == 3): + G1 = kron3(speye(n[2]), speye(n[1]), ddxCellGrad(n[0], BC)) + return G1 + + def cellGradx(): doc = "Cell centered Gradient in the x dimension. Has neumann boundary conditions." def fget(self): if getattr(self, '_cellGradx', None) is None: - BC = ['neumann', 'neumann'] - n = self.vnC - if(self.dim == 1): - G1 = ddxCellGrad(n[0], BC) - elif(self.dim == 2): - G1 = sp.kron(speye(n[1]), ddxCellGrad(n[0], BC)) - elif(self.dim == 3): - G1 = kron3(speye(n[2]), speye(n[1]), ddxCellGrad(n[0], BC)) + G1 = self._cellGradxStencil() # Compute areas of cell faces & volumes V = self.aveCC2F*self.vol L = self.r(self.area/V, 'F','Fx', 'V') @@ -382,17 +391,22 @@ class DiffOperators(object): return locals() cellGradx = property(**cellGradx()) + def _cellGradyStencil(self): + if self.dim < 2: return None + BC = ['neumann', 'neumann'] + n = self.vnC + if(self.dim == 2): + G2 = sp.kron(ddxCellGrad(n[1], BC), speye(n[0])) + elif(self.dim == 3): + G2 = kron3(speye(n[2]), ddxCellGrad(n[1], BC), speye(n[0])) + return G2 + def cellGrady(): doc = "Cell centered Gradient in the x dimension. Has neumann boundary conditions." def fget(self): if self.dim < 2: return None if getattr(self, '_cellGrady', None) is None: - BC = ['neumann', 'neumann'] - n = self.vnC - if(self.dim == 2): - G2 = sp.kron(ddxCellGrad(n[1], BC), speye(n[0])) - elif(self.dim == 3): - G2 = kron3(speye(n[2]), ddxCellGrad(n[1], BC), speye(n[0])) + G2 = self._cellGradyStencil() # Compute areas of cell faces & volumes V = self.aveCC2F*self.vol L = self.r(self.area/V, 'F','Fy', 'V') @@ -401,14 +415,19 @@ class DiffOperators(object): return locals() cellGrady = property(**cellGrady()) + def _cellGradzStencil(self): + if self.dim < 3: return None + BC = ['neumann', 'neumann'] + n = self.vnC + G3 = kron3(ddxCellGrad(n[2], BC), speye(n[1]), speye(n[0])) + return G3 + def cellGradz(): doc = "Cell centered Gradient in the x dimension. Has neumann boundary conditions." def fget(self): if self.dim < 3: return None if getattr(self, '_cellGradz', None) is None: - BC = ['neumann', 'neumann'] - n = self.vnC - G3 = kron3(ddxCellGrad(n[2], BC), speye(n[1]), speye(n[0])) + G3 = self._cellGradzStencil() # Compute areas of cell faces & volumes V = self.aveCC2F*self.vol L = self.r(self.area/V, 'F','Fz', 'V') @@ -746,4 +765,3 @@ class DiffOperators(object): kron3(av(n[2]), speye(n[1]+1), av(n[0])), kron3(speye(n[2]+1), av(n[1]), av(n[0]))), format="csr") return self._aveN2F - diff --git a/SimPEG/Mesh/MeshIO.py b/SimPEG/Mesh/MeshIO.py index 7501a66f..1c042237 100644 --- a/SimPEG/Mesh/MeshIO.py +++ b/SimPEG/Mesh/MeshIO.py @@ -21,10 +21,9 @@ class TensorMeshIO(object): if '*' in seg: st = seg sp = seg.split('*') - re = np.array(sp[0],dtype=int)*(' ' + sp[1]) + re = int(sp[0])*(' ' + sp[1]) line = line.replace(st,re.strip()) return np.array(line.split(),dtype=float) - # Read the file as line strings, remove lines with comment = ! msh = np.genfromtxt(fileName,delimiter='\n',dtype=np.str,comments='!') diff --git a/SimPEG/Mesh/TensorMesh.py b/SimPEG/Mesh/TensorMesh.py index 508f015c..1650b5cb 100644 --- a/SimPEG/Mesh/TensorMesh.py +++ b/SimPEG/Mesh/TensorMesh.py @@ -234,6 +234,9 @@ class BaseTensorMesh(BaseMesh): 'Fz' -> z-component of field defined on faces 'N' -> scalar field defined on nodes 'CC' -> scalar field defined on cell centers + 'CCVx' -> x-component of vector field defined on cell centers + 'CCVy' -> y-component of vector field defined on cell centers + 'CCVz' -> z-component of vector field defined on cell centers """ if self._meshType == 'CYL' and self.isSymmetric and locType in ['Ex','Ez','Fy']: raise Exception('Symmetric CylMesh does not support %s interpolation, as this variable does not exist.' % locType) @@ -257,6 +260,16 @@ class BaseTensorMesh(BaseMesh): Q = sp.hstack(components) elif locType in ['CC', 'N']: Q = Utils.interpmat(loc, *self.getTensor(locType)) + elif locType in ['CCVx', 'CCVy', 'CCVz']: + Q = Utils.interpmat(loc, *self.getTensor('CC')) + Z = Utils.spzeros(loc.shape[0],self.nC) + if locType == 'CCVx': + Q = sp.hstack([Q,Z,Z]) + elif locType == 'CCVy': + Q = sp.hstack([Z,Q,Z]) + elif locType == 'CCVz': + Q = sp.hstack([Z,Z,Q]) + else: raise NotImplementedError('getInterpolationMat: locType=='+locType+' and mesh.dim=='+str(self.dim)) diff --git a/SimPEG/Mesh/TreeMesh.py b/SimPEG/Mesh/TreeMesh.py index 02c23dee..2c75fda1 100644 --- a/SimPEG/Mesh/TreeMesh.py +++ b/SimPEG/Mesh/TreeMesh.py @@ -2131,10 +2131,16 @@ class TreeMesh(BaseTensorMesh, InnerProducts, TreeMeshIO): def plotSlice(self, v, vType='CC', normal='Z', ind=None, grid=True, view='real', ax=None, clim=None, showIt=False, - pcolorOpts={}, - streamOpts={'color':'k'}, - gridOpts={'color':'k', 'alpha':0.5}): + pcolorOpts=None, + streamOpts=None, + gridOpts=None): + if pcolorOpts is None: + pcolorOpts = {} + if streamOpts is None: + streamOpts = {'color':'k'} + if gridOpts is None: + gridOpts = {'color':'k', 'alpha':0.5} assert vType in ['CC','F','E'] assert self.dim == 3 diff --git a/SimPEG/Mesh/View.py b/SimPEG/Mesh/View.py index 089d7d9a..8eb22098 100644 --- a/SimPEG/Mesh/View.py +++ b/SimPEG/Mesh/View.py @@ -42,9 +42,9 @@ class TensorView(object): def plotImage(self, v, vType='CC', grid=False, view='real', ax=None, clim=None, showIt=False, - pcolorOpts={}, - streamOpts={'color':'k'}, - gridOpts={'color':'k'}, + pcolorOpts=None, + streamOpts=None, + gridOpts=None, numbering=True, annotationColor='w' ): """ @@ -84,6 +84,12 @@ class TensorView(object): M.plotImage(v, annotationColor='k', showIt=True) """ + if pcolorOpts is None: + pcolorOpts = {} + if streamOpts is None: + streamOpts = {'color':'k'} + if gridOpts is None: + gridOpts = {'color':'k'} if ax is None: fig = plt.figure() @@ -174,9 +180,9 @@ class TensorView(object): def plotSlice(self, v, vType='CC', normal='Z', ind=None, grid=False, view='real', ax=None, clim=None, showIt=False, - pcolorOpts={}, - streamOpts={'color':'k'}, - gridOpts={'color':'k', 'alpha':0.5} + pcolorOpts=None, + streamOpts=None, + gridOpts=None ): """ @@ -197,6 +203,12 @@ class TensorView(object): M.plotSlice(M.cellGrad*b, 'F', view='vec', grid=True, showIt=True, pcolorOpts={'alpha':0.8}) """ + if pcolorOpts is None: + pcolorOpts = {} + if streamOpts is None: + streamOpts = {'color':'k'} + if gridOpts is None: + gridOpts = {'color':'k', 'alpha':0.5} if type(vType) in [list, tuple]: assert ax is None, "cannot specify an axis to plot on with this function." fig, axs = plt.subplots(1,len(vType)) @@ -289,11 +301,17 @@ class TensorView(object): def _plotImage2D(self, v, vType='CC', grid=False, view='real', ax=None, clim=None, showIt=False, - pcolorOpts={}, - streamOpts={'color':'k'}, - gridOpts={'color':'k'} + pcolorOpts=None, + streamOpts=None, + gridOpts=None ): + if pcolorOpts is None: + pcolorOpts = {} + if streamOpts is None: + streamOpts = {'color':'k'} + if gridOpts is None: + gridOpts = {'color':'k'} vTypeOptsCC = ['N','CC','Fx','Fy','Ex','Ey'] vTypeOptsV = ['CCv','F','E'] vTypeOpts = vTypeOptsCC + vTypeOptsV diff --git a/SimPEG/Optimization.py b/SimPEG/Optimization.py index 4f2cb062..77704733 100644 --- a/SimPEG/Optimization.py +++ b/SimPEG/Optimization.py @@ -888,6 +888,8 @@ class ProjectedGNCG(BFGS, Minimize, Remember): maxIterCG = 5 tolCG = 1e-1 + stepOffBoundsFact = 0.1 # perturbation of the inactive set off the bounds + lower = -np.inf upper = np.inf @@ -990,4 +992,20 @@ class ProjectedGNCG(BFGS, Minimize, Remember): cgFlag = 1 # End CG Iterations + # Take a gradient step on the active cells if exist + if temp != self.xc.size: + + rhs_a = (Active) * -self.g + + dm_i = max( abs( delx ) ) + dm_a = max( abs(rhs_a) ) + + # perturb inactive set off of bounds so that they are included in the step + delx = delx + self.stepOffBoundsFact * (rhs_a * dm_i / dm_a) + + + # Only keep gradients going in the right direction on the active set + indx = ((self.xc<=self.lower) & (delx < 0)) | ((self.xc>=self.upper) & (delx > 0)) + delx[indx] = 0. + return delx diff --git a/SimPEG/Problem.py b/SimPEG/Problem.py index f24b57de..f8520c5c 100644 --- a/SimPEG/Problem.py +++ b/SimPEG/Problem.py @@ -88,28 +88,28 @@ class BaseProblem(object): return self.survey is not None @Utils.timeIt - def Jvec(self, m, v, u=None): - """Jvec(m, v, u=None) + def Jvec(self, m, v, f=None): + """Jvec(m, v, f=None) Effect of J(m) on a vector v. :param numpy.array m: model :param numpy.array v: vector to multiply - :param numpy.array u: fields + :param Fields f: fields :rtype: numpy.array :return: Jv """ raise NotImplementedError('J is not yet implemented.') @Utils.timeIt - def Jtvec(self, m, v, u=None): - """Jtvec(m, v, u=None) + def Jtvec(self, m, v, f=None): + """Jtvec(m, v, f=None) Effect of transpose of J(m) on a vector v. :param numpy.array m: model :param numpy.array v: vector to multiply - :param numpy.array u: fields + :param Fields f: fields :rtype: numpy.array :return: JTv """ @@ -117,32 +117,32 @@ class BaseProblem(object): @Utils.timeIt - def Jvec_approx(self, m, v, u=None): - """Jvec_approx(m, v, u=None) + def Jvec_approx(self, m, v, f=None): + """Jvec_approx(m, v, f=None) Approximate effect of J(m) on a vector v :param numpy.array m: model :param numpy.array v: vector to multiply - :param numpy.array u: fields + :param Fields f: fields :rtype: numpy.array :return: approxJv """ - return self.Jvec(m, v, u) + return self.Jvec(m, v, f) @Utils.timeIt - def Jtvec_approx(self, m, v, u=None): - """Jtvec_approx(m, v, u=None) + def Jtvec_approx(self, m, v, f=None): + """Jtvec_approx(m, v, f=None) Approximate effect of transpose of J(m) on a vector v. :param numpy.array m: model :param numpy.array v: vector to multiply - :param numpy.array u: fields + :param Fields f: fields :rtype: numpy.array :return: JTv """ - return self.Jtvec(m, v, u) + return self.Jtvec(m, v, f) def fields(self, m): """ @@ -213,5 +213,20 @@ class BaseTimeProblem(BaseProblem): if hasattr(self, '_timeMesh'): del self._timeMesh +class LinearProblem(BaseProblem): + surveyPair = Survey.LinearSurvey + + def __init__(self, mesh, G, **kwargs): + BaseProblem.__init__(self, mesh, **kwargs) + self.G = G + + def fields(self, m): + return self.G.dot(m) + + def Jvec(self, m, v, f=None): + return self.G.dot(v) + + def Jtvec(self, m, v, f=None): + return self.G.T.dot(v) diff --git a/SimPEG/Regularization.py b/SimPEG/Regularization.py index 677fba5c..fc101a61 100644 --- a/SimPEG/Regularization.py +++ b/SimPEG/Regularization.py @@ -1,5 +1,289 @@ import Utils, Maps, Mesh, numpy as np, scipy.sparse as sp +class RegularizationMesh(object): + """ + **Regularization Mesh** + + This contains the operators used in the regularization. Note that these + are not necessarily true differential operators, but are constructed from + a SimPEG Mesh. + + :param Mesh mesh: problem mesh + :param numpy.array indActive: bool array, size nC, that is True where we have active cells. Used to reduce the operators so we regularize only on active cells + """ + + def __init__(self, mesh, indActive=None): + self.mesh = mesh + assert indActive is None or indActive.dtype == 'bool', 'indActive needs to be None or a bool' + self.indActive = indActive + + @property + def vol(self): + """ + reduced volume vector + :rtype: numpy.array + :return: reduced cell volume + """ + if getattr(self, '_vol', None) is None: + self._vol = self._Pac.T * self.mesh.vol + return self._vol + + @property + def nC(self): + """ + reduced number of cells + :rtype: int + :return: number of cells being regularized + """ + if getattr(self, '_nC', None) is None: + if self.indActive is None: + self._nC = self.mesh.nC + else: + self._nC = sum(self.indActive) + return self._nC + + @property + def dim(self): + """ + dimension of regularization mesh (1D, 2D, 3D) + :rtype: int + :return: dimension + """ + if getattr(self, '_dim', None) is None: + self._dim = self.mesh.dim + return self._dim + + + @property + def _Pac(self): + """ + projection matrix that takes from the reduced space of active cells to full modelling space (ie. nC x nindActive) + :rtype: scipy.sparse.csr_matrix + :return: active cell projection matrix + """ + if getattr(self, '__Pac', None) is None: + if self.indActive is None: + self.__Pac = Utils.speye(self.mesh.nC) + else: + self.__Pac = Utils.speye(self.mesh.nC)[:,self.indActive] + return self.__Pac + + @property + def _Pafx(self): + """ + projection matrix that takes from the reduced space of active x-faces to full modelling space (ie. nFx x nindActive_Fx ) + :rtype: scipy.sparse.csr_matrix + :return: active face-x projection matrix + """ + if getattr(self, '__Pafx', None) is None: + if self.indActive is None: + self.__Pafx = Utils.speye(self.mesh.nFx) + else: + indActive_Fx = (self.mesh.aveFx2CC.T * self.indActive) == 1 + self.__Pafx = Utils.speye(self.mesh.nFx)[:,indActive_Fx] + return self.__Pafx + + @property + def _Pafy(self): + """ + projection matrix that takes from the reduced space of active y-faces to full modelling space (ie. nFy x nindActive_Fy ) + :rtype: scipy.sparse.csr_matrix + :return: active face-y projection matrix + """ + if getattr(self, '__Pafy', None) is None: + if self.indActive is None: + self.__Pafy = Utils.speye(self.mesh.nFy) + else: + indActive_Fy = (self.mesh.aveFy2CC.T * self.indActive) == 1 + self.__Pafy = Utils.speye(self.mesh.nFy)[:,indActive_Fy] + return self.__Pafy + + @property + def _Pafz(self): + """ + projection matrix that takes from the reduced space of active z-faces to full modelling space (ie. nFz x nindActive_Fz ) + :rtype: scipy.sparse.csr_matrix + :return: active face-z projection matrix + """ + if getattr(self, '__Pafz', None) is None: + if self.indActive is None: + self.__Pafz = Utils.speye(self.mesh.nFz) + else: + indActive_Fz = (self.mesh.aveFz2CC.T * self.indActive) == 1 + self.__Pafz = Utils.speye(self.mesh.nFz)[:,indActive_Fz] + return self.__Pafz + + @property + def aveFx2CC(self): + """ + averaging from active cell centers to active x-faces + :rtype: scipy.sparse.csr_matrix + :return: averaging from active cell centers to active x-faces + """ + if getattr(self, '_aveFx2CC', None) is None: + self._aveFx2CC = self._Pac.T * self.mesh.aveFx2CC * self._Pafx + return self._aveFx2CC + + @property + def aveCC2Fx(self): + """ + averaging from active x-faces to active cell centers + :rtype: scipy.sparse.csr_matrix + :return: averaging matrix from active x-faces to active cell centers + """ + if getattr(self, '_aveCC2Fx', None) is None: + self._aveCC2Fx = Utils.sdiag(1./(self.aveFx2CC.T).sum(1)) * self.aveFx2CC.T + return self._aveCC2Fx + + @property + def aveFy2CC(self): + """ + averaging from active cell centers to active y-faces + :rtype: scipy.sparse.csr_matrix + :return: averaging from active cell centers to active y-faces + """ + if getattr(self, '_aveFy2CC', None) is None: + self._aveFy2CC = self._Pac.T * self.mesh.aveFy2CC * self._Pafy + return self._aveFy2CC + + @property + def aveCC2Fy(self): + """ + averaging from active y-faces to active cell centers + :rtype: scipy.sparse.csr_matrix + :return: averaging matrix from active y-faces to active cell centers + """ + if getattr(self, '_aveCC2Fy', None) is None: + self._aveCC2Fy = Utils.sdiag(1./(self.aveFy2CC.T).sum(1)) * self.aveFy2CC.T + return self._aveCC2Fy + + @property + def aveFz2CC(self): + """ + averaging from active cell centers to active z-faces + :rtype: scipy.sparse.csr_matrix + :return: averaging from active cell centers to active z-faces + """ + if getattr(self, '_aveFz2CC', None) is None: + self._aveFz2CC = self._Pac.T * self.mesh.aveFz2CC * self._Pafz + return self._aveFz2CC + + @property + def aveCC2Fz(self): + """ + averaging from active z-faces to active cell centers + :rtype: scipy.sparse.csr_matrix + :return: averaging matrix from active z-faces to active cell centers + """ + if getattr(self, '_aveCC2Fz', None) is None: + self._aveCC2Fz = Utils.sdiag(1./(self.aveFz2CC.T).sum(1)) * self.aveFz2CC.T + return self._aveCC2Fz + + @property + def cellDiffx(self): + """ + cell centered difference in the x-direction + :rtype: scipy.sparse.csr_matrix + :return: differencing matrix for active cells in the x-direction + """ + if getattr(self, '_cellDiffx', None) is None: + self._cellDiffx = self._Pafx.T * self.mesh.cellGradx * self._Pac + return self._cellDiffx + + @property + def cellDiffy(self): + """ + cell centered difference in the y-direction + :rtype: scipy.sparse.csr_matrix + :return: differencing matrix for active cells in the y-direction + """ + if getattr(self, '_cellDiffy', None) is None: + self._cellDiffy = self._Pafy.T * self.mesh.cellGrady * self._Pac + return self._cellDiffy + + @property + def cellDiffz(self): + """ + cell centered difference in the z-direction + :rtype: scipy.sparse.csr_matrix + :return: differencing matrix for active cells in the z-direction + """ + if getattr(self, '_cellDiffz', None) is None: + self._cellDiffz = self._Pafz.T * self.mesh.cellGradz * self._Pac + return self._cellDiffz + + @property + def faceDiffx(self): + """ + x-face differences + :rtype: scipy.sparse.csr_matrix + :return: differencing matrix for active faces in the x-direction + """ + if getattr(self, '_faceDiffx', None) is None: + self._faceDiffx = self._Pac.T * self.mesh.faceDivx * self._Pafx + return self._faceDiffx + + @property + def faceDiffy(self): + """ + y-face differences + :rtype: scipy.sparse.csr_matrix + :return: differencing matrix for active faces in the y-direction + """ + if getattr(self, '_faceDiffy', None) is None: + self._faceDiffy = self._Pac.T * self.mesh.faceDivy * self._Pafy + return self._faceDiffy + + @property + def faceDiffz(self): + """ + z-face differences + :rtype: scipy.sparse.csr_matrix + :return: differencing matrix for active faces in the z-direction + """ + if getattr(self, '_faceDiffz', None) is None: + self._faceDiffz = self._Pac.T * self.mesh.faceDivz * self._Pafz + return self._faceDiffz + + @property + def cellDiffxStencil(self): + """ + cell centered difference stencil (no cell lengths include) in the x-direction + :rtype: scipy.sparse.csr_matrix + :return: differencing matrix for active cells in the x-direction + """ + if getattr(self, '_cellDiffxStencil', None) is None: + + self._cellDiffxStencil = self._Pafx.T * self.mesh._cellGradxStencil() * self._Pac + return self._cellDiffxStencil + + @property + def cellDiffyStencil(self): + """ + cell centered difference stencil (no cell lengths include) in the y-direction + :rtype: scipy.sparse.csr_matrix + :return: differencing matrix for active cells in the y-direction + """ + if self.dim < 2: return None + if getattr(self, '_cellDiffyStencil', None) is None: + + self._cellDiffyStencil = self._Pafy.T * self.mesh._cellGradyStencil() * self._Pac + return self._cellDiffyStencil + + @property + def cellDiffzStencil(self): + """ + cell centered difference stencil (no cell lengths include) in the y-direction + :rtype: scipy.sparse.csr_matrix + :return: differencing matrix for active cells in the y-direction + """ + if self.dim < 3: return None + if getattr(self, '_cellDiffzStencil', None) is None: + + self._cellDiffzStencil = self._Pafz.T * self.mesh._cellGradzStencil() * self._Pac + return self._cellDiffzStencil + + class BaseRegularization(object): """ **Base Regularization Class** @@ -18,12 +302,19 @@ class BaseRegularization(object): mapping = None #: A SimPEG.Map instance. mesh = None #: A SimPEG.Mesh instance. - mref = None #: Reference model. + mref = None #: Reference model. def __init__(self, mesh, mapping=None, indActive=None, **kwargs): Utils.setKwargs(self, **kwargs) - self.mesh = mesh assert isinstance(mesh, Mesh.BaseMesh), "mesh must be a SimPEG.Mesh object." + if indActive is not None and indActive.dtype != 'bool': + tmp = indActive + indActive = np.zeros(mesh.nC, dtype=bool) + indActive[tmp] = True + if indActive is not None and mapping is None: + mapping = Maps.IdentityMap(nP=indActive.nonzero()[0].size) + + self.regmesh = RegularizationMesh(mesh,indActive) self.mapping = mapping or self.mapPair(mesh) self.mapping._assertMatchesPair(self.mapPair) self.indActive = indActive @@ -55,8 +346,7 @@ class BaseRegularization(object): @property def W(self): """Full regularization weighting matrix W.""" - return sp.identity(self.mapping.nP) - + return sp.identity(self.regmesh.nC) @Utils.timeIt def eval(self, m): @@ -87,11 +377,12 @@ class BaseRegularization(object): @Utils.timeIt def eval2Deriv(self, m, v=None): """ + Second derivative - :param numpy.array m: geophysical model - :param numpy.array v: vector to multiply - :rtype: scipy.sparse.csr_matrix or numpy.ndarray - :return: WtW or WtW*v + :param numpy.array m: geophysical model + :param numpy.array v: vector to multiply + :rtype: scipy.sparse.csr_matrix or numpy.ndarray + :return: WtW or WtW*v The regularization is: @@ -112,112 +403,94 @@ class BaseRegularization(object): return mD.T * ( self.W.T * ( self.W * ( mD * v) ) ) - class Tikhonov(BaseRegularization): """ + L2 Tikhonov regularization with both smallness and smoothness (first order + derivative) contributions. + + .. math:: + \phi_m(\mathbf{m}) = \\alpha_s \| W_s (\mathbf{m} - \mathbf{m_{ref}} ) \|^2 + + \\alpha_x \| W_x \\frac{\partial}{\partial x} (\mathbf{m} - \mathbf{m_{ref}} ) \|^2 + + \\alpha_y \| W_y \\frac{\partial}{\partial y} (\mathbf{m} - \mathbf{m_{ref}} ) \|^2 + + \\alpha_z \| W_z \\frac{\partial}{\partial z} (\mathbf{m} - \mathbf{m_{ref}} ) \|^2 + + Note if the key word argument `mrefInSmooth` is False, then mref is not + included in the smoothness contribution. + + :param Mesh mesh: SimPEG mesh + :param Maps mapping: regularization mapping, takes the model from model space to the thing you want to regularize + :param numpy.ndarray indActive: active cell indices for reducing the size of differential operators in the definition of a regularization mesh + :param bool mrefInSmooth: (default = False) put mref in the smoothness component? + :param float alpha_s: (default 1e-6) smallness weight + :param float alpha_x: (default 1) smoothness weight for first derivative in the x-direction + :param float alpha_y: (default 1) smoothness weight for first derivative in the y-direction + :param float alpha_z: (default 1) smoothness weight for first derivative in the z-direction + :param float alpha_xx: (default 1) smoothness weight for second derivative in the x-direction + :param float alpha_yy: (default 1) smoothness weight for second derivative in the y-direction + :param float alpha_zz: (default 1) smoothness weight for second derivative in the z-direction """ - smoothModel = True #: SMOOTH and SMOOTH_MOD_DIF options - alpha_s = Utils.dependentProperty('_alpha_s', 1e-6, ['_W', '_Ws'], "Smallness weight") - alpha_x = Utils.dependentProperty('_alpha_x', 1.0, ['_W', '_Wx'], "Weight for the first derivative in the x direction") - alpha_y = Utils.dependentProperty('_alpha_y', 1.0, ['_W', '_Wy'], "Weight for the first derivative in the y direction") - alpha_z = Utils.dependentProperty('_alpha_z', 1.0, ['_W', '_Wz'], "Weight for the first derivative in the z direction") - alpha_xx = Utils.dependentProperty('_alpha_xx', 0.0, ['_W', '_Wxx'], "Weight for the second derivative in the x direction") - alpha_yy = Utils.dependentProperty('_alpha_yy', 0.0, ['_W', '_Wyy'], "Weight for the second derivative in the y direction") - alpha_zz = Utils.dependentProperty('_alpha_zz', 0.0, ['_W', '_Wzz'], "Weight for the second derivative in the z direction") + mrefInSmooth = False # put mref in the smoothness contribution + alpha_s = Utils.dependentProperty('_alpha_s', 1e-6, ['_W', '_Wsmall'], "Smallness weight") + alpha_x = Utils.dependentProperty('_alpha_x', 1.0, ['_W', '_Wx'], "Weight for the first derivative in the x direction") + alpha_y = Utils.dependentProperty('_alpha_y', 1.0, ['_W', '_Wy'], "Weight for the first derivative in the y direction") + alpha_z = Utils.dependentProperty('_alpha_z', 1.0, ['_W', '_Wz'], "Weight for the first derivative in the z direction") + alpha_xx = Utils.dependentProperty('_alpha_xx', 0.0, ['_W', '_Wxx'], "Weight for the second derivative in the x direction") + alpha_yy = Utils.dependentProperty('_alpha_yy', 0.0, ['_W', '_Wyy'], "Weight for the second derivative in the y direction") + alpha_zz = Utils.dependentProperty('_alpha_zz', 0.0, ['_W', '_Wzz'], "Weight for the second derivative in the z direction") def __init__(self, mesh, mapping=None, indActive = None, **kwargs): - BaseRegularization.__init__(self, mesh, mapping=mapping, **kwargs) - self.indActive = indActive + BaseRegularization.__init__(self, mesh, mapping=mapping, indActive=indActive, **kwargs) @property - def Ws(self): - """Regularization matrix Ws""" - if getattr(self,'_Ws', None) is None: - self._Ws = Utils.sdiag((self.mesh.vol*self.alpha_s)**0.5) - if self.indActive is not None: - Pac = Utils.speye(self.mesh.nC)[:,self.indActive] - self._Ws = Pac.T * self._Ws * Pac - return self._Ws + def Wsmall(self): + """Regularization matrix Wsmall""" + if getattr(self,'_Wsmall', None) is None: + self._Wsmall = Utils.sdiag((self.regmesh.vol*self.alpha_s)**0.5) + return self._Wsmall @property def Wx(self): """Regularization matrix Wx""" if getattr(self, '_Wx', None) is None: - Ave_x_vol = self.mesh.aveF2CC[:,:self.mesh.nFx].T*self.mesh.vol - self._Wx = Utils.sdiag((Ave_x_vol*self.alpha_x)**0.5)*self.mesh.cellGradx - - if self.indActive is not None: - indActive_Fx = (self.mesh.aveFx2CC.T * self.indActive) == 1 - Pac = Utils.speye(self.mesh.nC)[:,self.indActive] - Pafx = Utils.speye(self.mesh.nFx)[:,indActive_Fx] - self._Wx = Pafx.T*self._Wx*Pac - + Ave_x_vol = self.regmesh.aveCC2Fx * self.regmesh.vol + self._Wx = Utils.sdiag((Ave_x_vol*self.alpha_x)**0.5)*self.regmesh.cellDiffx return self._Wx @property def Wy(self): """Regularization matrix Wy""" if getattr(self, '_Wy', None) is None: - Ave_y_vol = self.mesh.aveF2CC[:,self.mesh.nFx:np.sum(self.mesh.vnF[:2])].T*self.mesh.vol - self._Wy = Utils.sdiag((Ave_y_vol*self.alpha_y)**0.5)*self.mesh.cellGrady - - if self.indActive is not None: - indActive_Fy = (self.mesh.aveFy2CC.T * self.indActive) == 1 - Pac = Utils.speye(self.mesh.nC)[:,self.indActive] - Pafy = Utils.speye(self.mesh.nFy)[:,indActive_Fy] - self._Wy = Pafy.T*self._Wy*Pac - + Ave_y_vol = self.regmesh.aveCC2Fy * self.regmesh.vol + self._Wy = Utils.sdiag((Ave_y_vol*self.alpha_y)**0.5)*self.regmesh.cellDiffy return self._Wy @property def Wz(self): """Regularization matrix Wz""" if getattr(self, '_Wz', None) is None: - Ave_z_vol = self.mesh.aveF2CC[:,np.sum(self.mesh.vnF[:2]):].T*self.mesh.vol - self._Wz = Utils.sdiag((Ave_z_vol*self.alpha_z)**0.5)*self.mesh.cellGradz - - if self.indActive is not None: - indActive_Fz = (self.mesh.aveFz2CC.T * self.indActive) == 1 - Pac = Utils.speye(self.mesh.nC)[:,self.indActive] - Pafz = Utils.speye(self.mesh.nFz)[:,indActive_Fz] - self._Wz = Pafz.T*self._Wz*Pac - + Ave_z_vol = self.regmesh.aveCC2Fz * self.regmesh.vol + self._Wz = Utils.sdiag((Ave_z_vol*self.alpha_z)**0.5)*self.regmesh.cellDiffz return self._Wz @property def Wxx(self): """Regularization matrix Wxx""" if getattr(self, '_Wxx', None) is None: - self._Wxx = Utils.sdiag((self.mesh.vol*self.alpha_xx)**0.5)*self.mesh.faceDivx*self.mesh.cellGradx - - if self.indActive is not None: - Pac = Utils.speye(self.mesh.nC)[:,self.indActive] - self._Wxx = Pac.T*self._Wxx*Pac - + self._Wxx = Utils.sdiag((self.regmesh.vol*self.alpha_xx)**0.5)*self.regmesh.faceDiffx*self.regmesh.cellDiffx return self._Wxx @property def Wyy(self): """Regularization matrix Wyy""" if getattr(self, '_Wyy', None) is None: - self._Wyy = Utils.sdiag((self.mesh.vol*self.alpha_yy)**0.5)*self.mesh.faceDivy*self.mesh.cellGrady - - if self.indActive is not None: - Pac = Utils.speye(self.mesh.nC)[:,self.indActive] - self._Wyy = Pac.T*self._Wyy*Pac - + self._Wyy = Utils.sdiag((self.regmesh.vol*self.alpha_yy)**0.5)*self.regmesh.faceDiffy*self.regmesh.cellDiffy return self._Wyy @property def Wzz(self): """Regularization matrix Wzz""" if getattr(self, '_Wzz', None) is None: - self._Wzz = Utils.sdiag((self.mesh.vol*self.alpha_zz)**0.5)*self.mesh.faceDivz*self.mesh.cellGradz - - if self.indActive is not None: - Pac = Utils.speye(self.mesh.nC)[:,self.indActive] - self._Wzz = Pac.T*self._Wzz*Pac - + self._Wzz = Utils.sdiag((self.regmesh.vol*self.alpha_zz)**0.5)*self.regmesh.faceDiffz*self.regmesh.cellDiffz return self._Wzz @property @@ -225,9 +498,9 @@ class Tikhonov(BaseRegularization): """Full smoothness regularization matrix W""" if getattr(self, '_Wsmooth', None) is None: wlist = (self.Wx, self.Wxx) - if self.mesh.dim > 1: + if self.regmesh.dim > 1: wlist += (self.Wy, self.Wyy) - if self.mesh.dim > 2: + if self.regmesh.dim > 2: wlist += (self.Wz, self.Wzz) self._Wsmooth = sp.vstack(wlist) return self._Wsmooth @@ -236,25 +509,44 @@ class Tikhonov(BaseRegularization): def W(self): """Full regularization matrix W""" if getattr(self, '_W', None) is None: - wlist = (self.Ws, self.Wsmooth) + wlist = (self.Wsmall, self.Wsmooth) self._W = sp.vstack(wlist) return self._W @Utils.timeIt - def eval(self, m): - if self.smoothModel == True: - r1 = self.Wsmooth * ( self.mapping * (m) ) - r2 = self.Ws * ( self.mapping * (m - self.mref) ) - return 0.5*(r1.dot(r1)+r2.dot(r2)) - elif self.smoothModel == False: - r = self.W * ( self.mapping * (m - self.mref) ) - return 0.5*r.dot(r) + def _evalSmall(self, m): + r = self.Wsmall * ( self.mapping * (m - self.mref) ) + return 0.5 * r.dot(r) + @Utils.timeIt + def _evalSmooth(self, m): + if self.mrefInSmooth == True: + r = self.Wsmooth * ( self.mapping * (m - self.mref) ) + elif self.mrefInSmooth == False: + r = self.Wsmooth * ( self.mapping * (m) ) + return 0.5 * r.dot(r) + + @Utils.timeIt + def eval(self, m): + return self._evalSmall(m) + self._evalSmooth(m) + + @Utils.timeIt + def _evalSmallDeriv(self,m): + r = self.Wsmall * ( self.mapping * (m - self.mref) ) + return r.T * ( self.Wsmall * self.mapping.deriv(m - self.mref) ) + + @Utils.timeIt + def _evalSmoothDeriv(self,m): + if self.mrefInSmooth == True: + r = self.Wsmooth * ( self.mapping * ( m - self.mref ) ) + return r.T * ( self.Wsmooth * self.mapping.deriv(m - self.mref) ) + elif self.mrefInSmooth == False: + r = self.Wsmooth * ( self.mapping * m ) + return r.T * ( self.Wsmooth * self.mapping.deriv(m) ) @Utils.timeIt def evalDeriv(self, m): """ - The regularization is: .. math:: @@ -268,17 +560,185 @@ class Tikhonov(BaseRegularization): R(m) = \mathbf{W^\\top W (m-m_\\text{ref})} """ - if self.smoothModel == True: - mD1 = self.mapping.deriv(m) - mD2 = self.mapping.deriv(m - self.mref) - r1 = self.Wsmooth * ( self.mapping * (m)) - r2 = self.Ws * ( self.mapping * (m - self.mref) ) - out1 = mD1.T * ( self.Wsmooth.T * r1 ) - out2 = mD2.T * ( self.Ws.T * r2 ) - out = out1+out2 - elif self.smoothModel == False: - mD = self.mapping.deriv(m - self.mref) - r = self.W * ( self.mapping * (m - self.mref) ) - out = mD.T * ( self.W.T * r ) - return out + return self._evalSmallDeriv(m) + self._evalSmoothDeriv(m) + +class Simple(Tikhonov): + """ + Simple regularization that does not include length scales in the derivatives. + """ + + mrefInSmooth = False #: SMOOTH and SMOOTH_MOD_DIF options + alpha_s = Utils.dependentProperty('_alpha_s', 1.0, ['_W', '_Wsmall'], "Smallness weight") + alpha_x = Utils.dependentProperty('_alpha_x', 1.0, ['_W', '_Wx'], "Weight for the first derivative in the x direction") + alpha_y = Utils.dependentProperty('_alpha_y', 1.0, ['_W', '_Wy'], "Weight for the first derivative in the y direction") + alpha_z = Utils.dependentProperty('_alpha_z', 1.0, ['_W', '_Wz'], "Weight for the first derivative in the z direction") + wght = 1. + + def __init__(self, mesh, mapping=None, indActive=None, **kwargs): + BaseRegularization.__init__(self, mesh, mapping=mapping, indActive=indActive, **kwargs) + + if isinstance(self.wght,float): + self.wght = np.ones(self.regmesh.nC) * self.wght + + @property + def Wsmall(self): + """Regularization matrix Wsmall""" + if getattr(self,'_Wsmall', None) is None: + self._Wsmall = Utils.sdiag((self.regmesh.vol*self.alpha_s*self.wght)**0.5) + return self._Wsmall + + @property + def Wx(self): + """Regularization matrix Wx""" + if getattr(self, '_Wx', None) is None: + self._Wx = Utils.sdiag((self.regmesh.aveCC2Fx * self.regmesh.vol*self.alpha_x*(self.regmesh.aveCC2Fx*self.wght))**0.5)*self.regmesh.cellDiffxStencil + return self._Wx + + @property + def Wy(self): + """Regularization matrix Wy""" + if getattr(self, '_Wy', None) is None: + self._Wy = Utils.sdiag((self.regmesh.aveCC2Fy * self.regmesh.vol * self.alpha_y*(self.regmesh.aveCC2Fy*self.wght))**0.5)*self.regmesh.cellDiffyStencil + return self._Wy + + @property + def Wz(self): + """Regularization matrix Wz""" + if getattr(self, '_Wz', None) is None: + self._Wz = Utils.sdiag((self.regmesh.aveCC2Fz * self.regmesh.vol*self.alpha_z*(self.regmesh.aveCC2Fz*self.wght))**0.5)*self.regmesh.cellDiffzStencil + return self._Wz + + @property + def Wsmooth(self): + """Full smoothness regularization matrix W""" + if getattr(self, '_Wsmooth', None) is None: + wlist = (self.Wx,) + if self.regmesh.dim > 1: + wlist += (self.Wy,) + if self.regmesh.dim > 2: + wlist += (self.Wz,) + self._Wsmooth = sp.vstack(wlist) + return self._Wsmooth + + @property + def W(self): + """Full regularization matrix W""" + if getattr(self, '_W', None) is None: + wlist = (self.Wsmall, self.Wsmooth) + self._W = sp.vstack(wlist) + return self._W + + @Utils.timeIt + def _evalSmall(self, m): + r = self.Wsmall * ( self.mapping * (m - self.mref) ) + return 0.5 * r.dot(r) + + @Utils.timeIt + def _evalSmooth(self, m): + if self.mrefInSmooth == True: + r = self.Wsmooth * ( self.mapping * (m - self.mref) ) + elif self.mrefInSmooth == False: + r = self.Wsmooth * ( self.mapping * m) + return 0.5 * r.dot(r) + + +class Sparse(Simple): + + # set default values + eps_p = 1e-1 + eps_q = 1e-1 + curModel = None # use a model to compute the weights + gamma = 1. + norms = [0., 2., 2., 2.] + wght = 1. + + def __init__(self, mesh, mapping=None, indActive=None, **kwargs): + Simple.__init__(self, mesh, mapping=mapping, indActive=indActive, **kwargs) + + if isinstance(self.wght,float): + self.wght = np.ones(self.regmesh.nC) * self.wght + + @property + def Wsmall(self): + """Regularization matrix Wsmall""" + if getattr(self, 'curModel', None) is None: + self.Rs = Utils.speye(self.regmesh.nC) + + else: + f_m = self.curModel - self.reg.mref + self.rs = self.R(f_m , self.eps_p, self.norms[0]) + #print "Min rs: " + str(np.max(self.rs)) + "Max rs: " + str(np.min(self.rs)) + self.Rs = Utils.sdiag( self.rs ) + + return Utils.sdiag((self.regmesh.vol*self.alpha_s*self.gamma*self.wght)**0.5)*self.Rs + + + @property + def Wx(self): + """Regularization matrix Wx""" + + if getattr(self, 'curModel', None) is None: + self.Rx = Utils.speye(self.regmesh.cellDiffxStencil.shape[0]) + + else: + f_m = self.regmesh.cellDiffxStencil * self.curModel + self.rx = self.R( f_m , self.eps_q, self.norms[1]) + self.Rx = Utils.sdiag( self.rx ) + + return Utils.sdiag(( (self.regmesh.aveCC2Fx * self.regmesh.vol) *self.alpha_x*self.gamma*(self.regmesh.aveCC2Fx*self.wght))**0.5)*self.Rx*self.regmesh.cellDiffxStencil + + @property + def Wy(self): + """Regularization matrix Wy""" + + if getattr(self, 'curModel', None) is None: + self.Ry = Utils.speye(self.regmesh.cellDiffyStencil.shape[0]) + + else: + f_m = self.regmesh.cellDiffyStencil * self.curModel + self.ry = self.R( f_m , self.eps_q, self.norms[2]) + self.Ry = Utils.sdiag( self.ry ) + + return Utils.sdiag(((self.regmesh.aveCC2Fy * self.regmesh.vol)*self.alpha_y*self.gamma*(self.regmesh.aveCC2Fy*self.wght))**0.5)*self.Ry*self.regmesh.cellDiffyStencil + + @property + def Wz(self): + """Regularization matrix Wz""" + + if getattr(self, 'curModel', None) is None: + self.Rz = Utils.speye(self.regmesh.cellDiffzStencil.shape[0]) + + else: + f_m = self.regmesh.cellDiffzStencil * self.curModel + self.rz = self.R( f_m , self.eps_q, self.norms[3]) + self.Rz = Utils.sdiag( self.rz ) + + return Utils.sdiag(((self.regmesh.aveCC2Fz * self.regmesh.vol)*self.alpha_z*self.gamma*(self.regmesh.aveCC2Fz*self.wght))**0.5)*self.Rz*self.regmesh.cellDiffzStencil + + @property + def Wsmooth(self): + """Full smoothness regularization matrix W""" + #if getattr(self, '_Wsmooth', None) is None: + wlist = (self.Wx,) + if self.regmesh.dim > 1: + wlist += (self.Wy,) + if self.regmesh.dim > 2: + wlist += (self.Wz,) + #self._Wsmooth = sp.vstack(wlist) + return sp.vstack(wlist) + + @property + def W(self): + """Full regularization matrix W""" + if getattr(self, '_W', None) is None: + wlist = (self.Wsmall, self.Wsmooth) + self._W = sp.vstack(wlist) + return self._W + + def R(self, f_m , eps, exponent): + + eta = (eps**(1.-exponent/2.))**0.5 + r = eta / (f_m**2.+ eps**2.)**((1.-exponent/2.)/2.) + + return r diff --git a/SimPEG/Survey.py b/SimPEG/Survey.py index 1484b6c8..fbc88276 100644 --- a/SimPEG/Survey.py +++ b/SimPEG/Survey.py @@ -1,6 +1,5 @@ import Utils, numpy as np, scipy.sparse as sp, uuid - class BaseRx(object): """SimPEG Receiver Object""" @@ -35,7 +34,7 @@ class BaseRx(object): """Number of data in the receiver.""" return self.locs.shape[0] - def getP(self, mesh): + def getP(self, mesh, projGLoc=None): """ Returns the projection matrices as a list for all components collected by @@ -48,7 +47,10 @@ class BaseRx(object): if mesh in self._Ps: return self._Ps[mesh] - P = mesh.getInterpolationMat(self.locs, self.projGLoc) + if projGLoc is None: + projGLoc = self.projGLoc + + P = mesh.getInterpolationMat(self.locs, projGLoc) if self.storeProjections: self._Ps[mesh] = P return P @@ -293,38 +295,38 @@ class BaseSurvey(object): @Utils.count @Utils.requires('prob') - def dpred(self, m, u=None): - """dpred(m, u=None) + def dpred(self, m, f=None): + """dpred(m, f=None) Create the projected data from a model. - The field, u, (if provided) will be used for the predicted data + The fields, f, (if provided) will be used for the predicted data instead of recalculating the fields (which may be expensive!). .. math:: - d_\\text{pred} = P(u(m)) + d_\\text{pred} = P(f(m)) Where P is a projection of the fields onto the data space. """ - if u is None: u = self.prob.fields(m) - return Utils.mkvc(self.eval(u)) + if f is None: f = self.prob.fields(m) + return Utils.mkvc(self.eval(f)) @Utils.count - def eval(self, u): - """eval(u) + def eval(self, f): + """eval(f) This function projects the fields onto the data space. .. math:: - d_\\text{pred} = \mathbf{P} u(m) + d_\\text{pred} = \mathbf{P} f(m) """ raise NotImplemented('eval is not yet implemented.') @Utils.count - def evalDeriv(self, u): - """evalDeriv(u) + def evalDeriv(self, f): + """evalDeriv(f) This function s the derivative of projects the fields onto the data space. @@ -335,11 +337,11 @@ class BaseSurvey(object): raise NotImplemented('eval is not yet implemented.') @Utils.count - def residual(self, m, u=None): - """residual(m, u=None) + def residual(self, m, f=None): + """residual(m, f=None) :param numpy.array m: geophysical model - :param numpy.array u: fields + :param numpy.array f: fields :rtype: numpy.array :return: data residual @@ -350,14 +352,14 @@ class BaseSurvey(object): \mu_\\text{data} = \mathbf{d}_\\text{pred} - \mathbf{d}_\\text{obs} """ - return Utils.mkvc(self.dpred(m, u=u) - self.dobs) + return Utils.mkvc(self.dpred(m, f=f) - self.dobs) @property def isSynthetic(self): "Check if the data is synthetic." return self.mtrue is not None - def makeSyntheticData(self, m, std=0.05, u=None, force=False): + def makeSyntheticData(self, m, std=0.05, f=None, force=False): """ Make synthetic data given a model, and a standard deviation. @@ -370,8 +372,16 @@ class BaseSurvey(object): if getattr(self, 'dobs', None) is not None and not force: raise Exception('Survey already has dobs. You can use force=True to override this exception.') self.mtrue = m - self.dtrue = self.dpred(m, u=u) + self.dtrue = self.dpred(m, f=f) noise = std*abs(self.dtrue)*np.random.randn(*self.dtrue.shape) self.dobs = self.dtrue+noise self.std = self.dobs*0 + std return self.dobs + +class LinearSurvey(BaseSurvey): + def eval(self, f): + return f + + @property + def nD(self): + return self.prob.G.shape[0] diff --git a/SimPEG/Utils/ModelBuilder.py b/SimPEG/Utils/ModelBuilder.py index 52d13820..1b868fe5 100644 --- a/SimPEG/Utils/ModelBuilder.py +++ b/SimPEG/Utils/ModelBuilder.py @@ -88,12 +88,14 @@ def getIndicesBlock(p0,p1,ccMesh): # Return a tuple return ind -def defineBlock(ccMesh,p0,p1,vals=[0,1]): +def defineBlock(ccMesh,p0,p1,vals=None): """ Build a block with the conductivity specified by condVal. Returns an array. vals[0] conductivity of the block vals[1] conductivity of the ground """ + if vals is None: + vals = [0,1] sigma = np.zeros(ccMesh.shape[0]) + vals[1] ind = getIndicesBlock(p0,p1,ccMesh) @@ -101,7 +103,11 @@ def defineBlock(ccMesh,p0,p1,vals=[0,1]): return mkvc(sigma) -def defineElipse(ccMesh, center=[0,0,0], anisotropy=[1,1,1], slope=10., theta=0.): +def defineElipse(ccMesh, center=None, anisotropy=None, slope=10., theta=0.): + if center is None: + center = [0,0,0] + if anisotropy is None: + anisotropy = [1,1,1] G = ccMesh.copy() dim = ccMesh.shape[1] for i in range(dim): @@ -118,7 +124,45 @@ def defineElipse(ccMesh, center=[0,0,0], anisotropy=[1,1,1], slope=10., theta=0. D = np.sqrt(np.sum(G**2,axis=1)) return -np.arctan((D-1)*slope)*(2./np.pi)/2.+0.5 -def defineTwoLayers(ccMesh,depth,vals=[0,1]): +def getIndicesSphere(center,radius,ccMesh): + """ + Creates a vector containing the sphere indices in the cell centers mesh. + Returns a tuple + + The sphere is defined by the points + + p0, describe the position of the center of the cell + + r, describe the radius of the sphere. + + ccMesh represents the cell-centered mesh + + The points p0 must live in the the same dimensional space as the mesh. + + """ + + # Validation: mesh and point (p0) live in the same dimensional space + dimMesh = np.size(ccMesh[0,:]) + assert len(center) == dimMesh, "Dimension mismatch. len(p0) != dimMesh" + + if dimMesh == 1: + # Define the reference points + + ind = np.abs(center[0] - ccMesh[:,0]) < radius + + elif dimMesh == 2: + # Define the reference points + + ind = np.sqrt( ( center[0] - ccMesh[:,0] )**2 + ( center[1] - ccMesh[:,1] )**2 ) < radius + + elif dimMesh == 3: + # Define the points + ind = np.sqrt( ( center[0] - ccMesh[:,0] )**2 + ( center[1] - ccMesh[:,1] )**2 + ( center[2] - ccMesh[:,2] )**2 ) < radius + + # Return a tuple + return ind + +def defineTwoLayers(ccMesh,depth,vals=None): """ Define a two layered model. Depth of the first layer must be specified. CondVals vector with the conductivity values of the layers. Eg: @@ -129,6 +173,8 @@ def defineTwoLayers(ccMesh,depth,vals=[0,1]): 0 depth zf 1st layer 2nd layer """ + if vals is None: + vals = [0,1] sigma = np.zeros(ccMesh.shape[0]) + vals[1] dim = np.size(ccMesh[0,:]) @@ -214,7 +260,7 @@ def layeredModel(ccMesh, layerTops, layerValues): -def randomModel(shape, seed=None, anisotropy=None, its=100, bounds=[0,1]): +def randomModel(shape, seed=None, anisotropy=None, its=100, bounds=None): """ Create a random model by convolving a kernel with a uniformly distributed model. @@ -238,6 +284,8 @@ def randomModel(shape, seed=None, anisotropy=None, its=100, bounds=[0,1]): """ + if bounds is None: + bounds = [0,1] if seed is None: seed = np.random.randint(1e3) diff --git a/SimPEG/Utils/codeutils.py b/SimPEG/Utils/codeutils.py index bfd00889..6151c540 100644 --- a/SimPEG/Utils/codeutils.py +++ b/SimPEG/Utils/codeutils.py @@ -55,8 +55,10 @@ def hook(obj, method, name=None, overwrite=False, silent=False): print 'Method '+name+' was not overwritten.' -def setKwargs(obj, ignore=[], **kwargs): +def setKwargs(obj, ignore=None, **kwargs): """Sets key word arguments (kwargs) that are present in the object, throw an error if they don't exist.""" + if ignore is None: + ignore = [] for attr in kwargs: if attr in ignore: continue diff --git a/SimPEG/Utils/io_utils.py b/SimPEG/Utils/io_utils.py new file mode 100644 index 00000000..323feb0c --- /dev/null +++ b/SimPEG/Utils/io_utils.py @@ -0,0 +1,137 @@ +from SimPEG import np, Mesh +import time as tm +import vtk, vtk.util.numpy_support as npsup +import re + +def read_GOCAD_ts(tsfile): + """ + + Read GOCAD triangulated surface (*.ts) file + INPUT: + tsfile: Triangulated surface + + OUTPUT: + vrts : Array of vertices in XYZ coordinates [n x 3] + trgl : Array of index for triangles [m x 3]. The order of the vertices + is important and describes the normal + n = cross( (P2 - P1 ) , (P3 - P1) ) + + Author: @fourndo + + + .. note:: + + Remove all attributes from the GoCAD surface before exporting it! + + """ + + + fid = open(tsfile,'r') + line = fid.readline() + + # Skip all the lines until the vertices + while re.match('TFACE',line)==None: + line = fid.readline() + + line = fid.readline() + vrtx = [] + + # Run down all the vertices and save in array + while re.match('VRTX',line): + l_input = re.split('[\s*]',line) + temp = np.array(l_input[2:5]) + vrtx.append(temp.astype(np.float)) + + # Read next line + line = fid.readline() + + vrtx = np.asarray(vrtx) + + # Skip lines to the triangles + while re.match('TRGL',line)==None: + line = fid.readline() + + # Run down the list of triangles + trgl = [] + + # Run down all the vertices and save in array + while re.match('TRGL',line): + l_input = re.split('[\s*]',line) + temp = np.array(l_input[1:4]) + trgl.append(temp.astype(np.int)) + + # Read next line + line = fid.readline() + + trgl = np.asarray(trgl) + + return vrtx, trgl + +def surface2inds(vrtx, trgl, mesh, boundaries=True, internal=True): + """" + Function to read gocad polystructure file and output indexes of mesh with in the structure. + + """ + # Adjust the index + trgl = trgl - 1 + + # Make vtk pts + ptsvtk = vtk.vtkPoints() + ptsvtk.SetData(npsup.numpy_to_vtk(vrtx,deep=1)) + + # Make the polygon connection + polys = vtk.vtkCellArray() + for face in trgl: + poly = vtk.vtkPolygon() + poly.GetPointIds().SetNumberOfIds(len(face)) + for nrv, vert in enumerate(face): + poly.GetPointIds().SetId(nrv,vert) + polys.InsertNextCell(poly) + + # Make the polydata, structure of connections and vrtx + polyData = vtk.vtkPolyData() + polyData.SetPoints(ptsvtk) + polyData.SetPolys(polys) + + # Make implicit func + ImpDistFunc = vtk.vtkImplicitPolyDataDistance() + ImpDistFunc.SetInput(polyData) + + # Convert the mesh + vtkMesh = vtk.vtkRectilinearGrid() + vtkMesh.SetDimensions(mesh.nNx,mesh.nNy,mesh.nNz) + vtkMesh.SetXCoordinates(npsup.numpy_to_vtk(mesh.vectorNx, deep=1)) + vtkMesh.SetYCoordinates(npsup.numpy_to_vtk(mesh.vectorNy, deep=1)) + vtkMesh.SetZCoordinates(npsup.numpy_to_vtk(mesh.vectorNz, deep=1)) + # Add indexes + vtkInd = npsup.numpy_to_vtk(np.arange(mesh.nC), deep=1) + vtkInd.SetName('Index') + vtkMesh.GetCellData().AddArray(vtkInd) + + extractImpDistRectGridFilt = vtk.vtkExtractGeometry() # Object constructor + extractImpDistRectGridFilt.SetImplicitFunction(ImpDistFunc) # + extractImpDistRectGridFilt.SetInputData(vtkMesh) + + if boundaries is True: + extractImpDistRectGridFilt.ExtractBoundaryCellsOn() + + else: + extractImpDistRectGridFilt.ExtractBoundaryCellsOff() + + if internal is True: + extractImpDistRectGridFilt.ExtractInsideOn() + + else: + extractImpDistRectGridFilt.ExtractInsideOff() + + print "Extracting indices from grid..." + # Executing the pipe + extractImpDistRectGridFilt.Update() + + # Get index inside + insideGrid = extractImpDistRectGridFilt.GetOutput() + insideGrid = npsup.vtk_to_numpy(insideGrid.GetCellData().GetArray('Index')) + + + # Return the indexes inside + return insideGrid diff --git a/SimPEG/__init__.py b/SimPEG/__init__.py index cc51fd1f..a1e989b6 100644 --- a/SimPEG/__init__.py +++ b/SimPEG/__init__.py @@ -15,7 +15,7 @@ import Directives import Inversion import Tests -__version__ = '0.1.9' +__version__ = '0.1.10' __author__ = 'Rowan Cockett' __license__ = 'MIT' __copyright__ = 'Copyright 2014 Rowan Cockett' diff --git a/docs/api_DC.rst b/docs/api_DC.rst new file mode 100644 index 00000000..f6c98d09 --- /dev/null +++ b/docs/api_DC.rst @@ -0,0 +1,150 @@ +.. _api_DC: + +.. math:: + + \renewcommand{\div}{\nabla\cdot\,} + \newcommand{\grad}{\vec \nabla} + \newcommand{\curl}{{\vec \nabla}\times\,} + \newcommand{\dcurl}{{\mathbf C}} + \newcommand{\dgrad}{{\mathbf G}} + \newcommand{\Acf}{{\mathbf A_c^f}} + \newcommand{\Ace}{{\mathbf A_c^e}} + \renewcommand{\S}{{\mathbf \Sigma}} + \renewcommand{\Div}{{\mathbf {Div}}} + \renewcommand{\Grad}{{\mathbf {Grad}}} + \newcommand{\St}{{\mathbf \Sigma_\tau}} + \newcommand{\diag}{\mathbf{diag}} + \newcommand{\M}{{\mathbf M}} + \newcommand{\Me}{{\M^e}} + \newcommand{\Mes}[1]{{\M^e_{#1}}} + \newcommand{\be}{\mathbf{e}} + \newcommand{\bj}{\mathbf{j}} + \newcommand{\bphi}{\mathbf{\phi}} + \newcommand{\bq}{\mathbf{q}} + \newcommand{\bJ}{\mathbf{J}} + \newcommand{\bG}{\mathbf{G}} + \newcommand{\bP}{\mathbf{P}} + \newcommand{\bA}{\mathbf{A}} + \newcommand{\bm}{\mathbf{m}} + \newcommand{\B}{\vec{B}} + \newcommand{\D}{\vec{D}} + \renewcommand{\H}{\vec{H}} + \renewcommand {\j} { {\vec j} } + \newcommand {\h} { {\vec h} } + \renewcommand {\b} { {\vec b} } + \newcommand {\e} { {\vec e} } + \newcommand {\c} { {\vec c} } + \renewcommand {\d} { {\vec d} } + \renewcommand {\u} { {\vec u} } + \newcommand{\I}{\vec{I}} + +DC resistivity survey +********************* + +Electrical resistivity of subsurface materials is measured by causing an electrical current to flow in the earth between one pair of electrodes while the voltage across a second pair of electrodes is measured. The result is an "apparent" resistivity which is a value representing the weighted average resistivity over a volume of the earth. Variations in this measurement are caused by variations in the soil, rock, and pore fluid electrical resistivity. Surveys require contact with the ground, so they can be labour intensive. Results are sometimes interpreted directly, but more commonly, 1D, 2D or 3D models are estimated using inversion procedures (`GPG `_). + + +Background +========== + +As direct current (DC) implies, in DC resistivity survey, we assume steady-state. We consider Maxwell's equations in steady state as + +.. math:: + + \curl \frac{1}{\mu} \vec{b} - \j = \j_s \\ + + \curl \e = 0 + +Then by taking \\(\\curl\\) for the first equation, we have + +.. math:: + + - \div\j = q \\ + + +where + +.. math:: + + \div \j_s = q = I(\delta(\vec{r}-\vec{r}_{s+})-\delta(\vec{r}-\vec{r}_{s-})) + +Since \\(\\curl \\e = 0\\), we have + +.. math:: + + \e = \grad \phi + +And by Ohm's law, we have + +.. math:: + + \j = \sigma \grad \phi + +Finally, we can compute the solution of the system: + +.. math:: + + - \div\j = q + + \j = \sigma \grad \phi + + \frac{\partial \phi}{\partial r}\Big|_{\partial \Omega_{BC}} = 0 + + +Discretization +============== + +By using finite volume method (FVM), we discretize our system as + +.. math:: + + -\Div \bj = \bq + + \diag(\Acf^{T}\sigma^{-1}) \bj = \Grad \bphi + +Here boundary condtions are embedded in the discrete differential operators. With some linear algebra we have + +.. math:: + + \bA\bphi = -\bq + +where + +.. math:: + + \bA = \Div (\diag(\Acf^{T}\sigma^{-1}))^{-1} \Grad + +By solving this linear equation, we can compute the solution of \\(\\phi\\). Based on this discretization, we derive sensitivity in discretized space. Sensitivity matrix can be in general can be written as + +.. math :: + + \bJ = -\bP\bA^{-1}\bG + +where + +.. math :: + + \bP: \text{Projection} + + \bJ = \bP\frac{\partial \phi}{\partial \bm} + +Here \\(\\bm\\) indicates model parameters in discretized space. + +Verification +============ + +Comparing to the analytic function: + +.. plot:: + + import simpegDC as DC + DC.Examples.Verification.run(plotIt=True) + +API +=== + +.. automodule:: simpegDC.BaseDC + :show-inheritance: + :members: + :undoc-members: + :inherited-members: diff --git a/docs/conf.py b/docs/conf.py index fee262de..45407435 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -51,9 +51,9 @@ copyright = u'2013, SimPEG Developers' # built documents. # # The short X.Y version. -version = '0.1.9' +version = '0.1.10' # The full version, including alpha/beta/rc tags. -release = '0.1.9' +release = '0.1.10' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/examples/DC_Analytic_Dipole.rst b/docs/examples/DC_Analytic_Dipole.rst new file mode 100644 index 00000000..f3d06058 --- /dev/null +++ b/docs/examples/DC_Analytic_Dipole.rst @@ -0,0 +1,21 @@ +.. _examples_DC_Analytic_Dipole: + +.. --------------------------------- .. +.. .. +.. THIS FILE IS AUTO GENEREATED .. +.. .. +.. SimPEG/Examples/__init__.py .. +.. .. +.. --------------------------------- .. + +DC Analytic Dipole +================== + +.. plot:: + + from SimPEG import Examples + Examples.DC_Analytic_Dipole.run() + +.. literalinclude:: ../../SimPEG/Examples/DC_Analytic_Dipole.py + :language: python + :linenos: diff --git a/docs/examples/DC_Forward_PseudoSection.rst b/docs/examples/DC_Forward_PseudoSection.rst new file mode 100644 index 00000000..29855d4a --- /dev/null +++ b/docs/examples/DC_Forward_PseudoSection.rst @@ -0,0 +1,36 @@ +.. _examples_DC_Forward_PseudoSection: + +.. --------------------------------- .. +.. .. +.. THIS FILE IS AUTO GENEREATED .. +.. .. +.. SimPEG/Examples/__init__.py .. +.. .. +.. --------------------------------- .. + + +DC Forward Simulation +===================== + +Forward model two conductive spheres in a half-space and plot a +pseudo-section. Assumes an infinite line source and measures along the +center of the spheres. + +INPUT: +loc = Location of spheres [[x1,y1,z1],[x2,y2,z2]] +radi = Radius of spheres [r1,r2] +param = Conductivity of background and two spheres [m0,m1,m2] +stype = survey type "pdp" (pole dipole) or "dpdp" (dipole dipole) +dtype = Data type "appr" (app res) | "appc" (app cond) | "volt" (potential) +Created by @fourndo + + + +.. plot:: + + from SimPEG import Examples + Examples.DC_Forward_PseudoSection.run() + +.. literalinclude:: ../../SimPEG/Examples/DC_Forward_PseudoSection.py + :language: python + :linenos: diff --git a/docs/examples/EM_Schenkel_Morrison_Casing.rst b/docs/examples/EM_Schenkel_Morrison_Casing.rst new file mode 100644 index 00000000..55f00168 --- /dev/null +++ b/docs/examples/EM_Schenkel_Morrison_Casing.rst @@ -0,0 +1,58 @@ +.. _examples_EM_Schenkel_Morrison_Casing: + +.. --------------------------------- .. +.. .. +.. THIS FILE IS AUTO GENEREATED .. +.. .. +.. SimPEG/Examples/__init__.py .. +.. .. +.. --------------------------------- .. + + +EM: Schenkel and Morrison Casing Model +====================================== + +Here we create and run a FDEM forward simulation to calculate the vertical +current inside a steel-cased. The model is based on the Schenkel and +Morrison Casing Model, and the results are used in a 2016 SEG abstract by +Yang et al. + +- Schenkel, C.J., and H.F. Morrison, 1990, Effects of well casing on potential field measurements using downhole current sources: Geophysical prospecting, 38, 663-686. + + +The model consists of: +- Air: Conductivity 1e-8 S/m, above z = 0 +- Background: conductivity 1e-2 S/m, below z = 0 +- Casing: conductivity 1e6 S/m + - 300m long + - radius of 0.1m + - thickness of 6e-3m + +Inside the casing, we take the same conductivity as the background. + +We are using an EM code to simulate DC, so we use frequency low enough +that the skin depth inside the casing is longer than the casing length (f += 1e-6 Hz). The plot produced is of the current inside the casing. + +These results are shown in the SEG abstract by Yang et al., 2016: 3D DC +resistivity modeling of steel casing for reservoir monitoring using +equivalent resistor network. The solver used to produce these results and +achieve the CPU time of ~30s is Mumps, which was installed using pymatsolver_ + +.. _pymatsolver: https://github.com/rowanc1/pymatsolver + +This example is on figshare: https://dx.doi.org/10.6084/m9.figshare.3126961.v1 + +If you would use this example for a code comparison, or build upon it, a +citation would be much appreciated! + + + +.. plot:: + + from SimPEG import Examples + Examples.EM_Schenkel_Morrison_Casing.run() + +.. literalinclude:: ../../SimPEG/Examples/EM_Schenkel_Morrison_Casing.py + :language: python + :linenos: diff --git a/docs/examples/Inversion_IRLS.rst b/docs/examples/Inversion_IRLS.rst new file mode 100644 index 00000000..64a072f1 --- /dev/null +++ b/docs/examples/Inversion_IRLS.rst @@ -0,0 +1,26 @@ +.. _examples_Inversion_IRLS: + +.. --------------------------------- .. +.. .. +.. THIS FILE IS AUTO GENEREATED .. +.. .. +.. SimPEG/Examples/__init__.py .. +.. .. +.. --------------------------------- .. + + +Inversion: Linear Problem +========================= + +Here we go over the basics of creating a linear problem and inversion. + + + +.. plot:: + + from SimPEG import Examples + Examples.Inversion_IRLS.run() + +.. literalinclude:: ../../SimPEG/Examples/Inversion_IRLS.py + :language: python + :linenos: diff --git a/setup.py b/setup.py index bcb5b8e3..3383c7f7 100644 --- a/setup.py +++ b/setup.py @@ -77,7 +77,7 @@ with open("README.rst") as f: setup( name = "SimPEG", - version = "0.1.9", + version = "0.1.10", packages = find_packages(), install_requires = ['numpy>=1.7', 'scipy>=0.13', diff --git a/tests/base/test_regularization.py b/tests/base/test_regularization.py index 050c46ac..6dddfb74 100644 --- a/tests/base/test_regularization.py +++ b/tests/base/test_regularization.py @@ -5,6 +5,8 @@ from scipy.sparse.linalg import dsolve import inspect TOL = 1e-20 +testReg = True +testRegMesh = True class RegularizationTests(unittest.TestCase): @@ -16,44 +18,80 @@ class RegularizationTests(unittest.TestCase): mesh3 = Mesh.TensorMesh([hx, hy, hz]) self.meshlist = [mesh1,mesh2, mesh3] - def test_regularization(self): - for R in dir(Regularization): - r = getattr(Regularization, R) - if not inspect.isclass(r): continue - if not issubclass(r, Regularization.BaseRegularization): - continue + if testReg: + def test_regularization(self): + for R in dir(Regularization): + r = getattr(Regularization, R) + if not inspect.isclass(r): continue + if not issubclass(r, Regularization.BaseRegularization): + continue + + for i, mesh in enumerate(self.meshlist): + + print 'Testing %iD'%mesh.dim + + mapping = r.mapPair(mesh) + reg = r(mesh, mapping=mapping) + m = np.random.rand(mapping.nP) + reg.mref = np.ones_like(m)*np.mean(m) + + print 'Check: phi_m (mref) = %f' %reg.eval(reg.mref) + passed = reg.eval(reg.mref) < TOL + self.assertTrue(passed) + + print 'Check:', R + passed = Tests.checkDerivative(lambda m : [reg.eval(m), reg.evalDeriv(m)], m, plotIt=False) + self.assertTrue(passed) + + print 'Check 2 Deriv:', R + passed = Tests.checkDerivative(lambda m : [reg.evalDeriv(m), reg.eval2Deriv(m)], m, plotIt=False) + self.assertTrue(passed) + + def test_regularization_ActiveCells(self): + for R in dir(Regularization): + r = getattr(Regularization, R) + if not inspect.isclass(r): continue + if not issubclass(r, Regularization.BaseRegularization): + continue + + for i, mesh in enumerate(self.meshlist): + + print 'Testing Active Cells %iD'%(mesh.dim) + + if mesh.dim == 1: + indActive = Utils.mkvc(mesh.gridCC <= 0.8) + elif mesh.dim == 2: + indActive = Utils.mkvc(mesh.gridCC[:,-1] <= 2*np.sin(2*np.pi*mesh.gridCC[:,0])+0.5) + elif mesh.dim == 3: + indActive = Utils.mkvc(mesh.gridCC[:,-1] <= 2*np.sin(2*np.pi*mesh.gridCC[:,0])+0.5 * 2*np.sin(2*np.pi*mesh.gridCC[:,1])+0.5) + + for indAct in [indActive, indActive.nonzero()[0]]: # test both bool and integers + reg = r(mesh, indActive=indAct) + m = np.random.rand(mesh.nC)[indAct] + reg.mref = np.ones_like(m)*np.mean(m) + + print 'Check: phi_m (mref) = %f' %reg.eval(reg.mref) + passed = reg.eval(reg.mref) < TOL + self.assertTrue(passed) + + print 'Check:', R + passed = Tests.checkDerivative(lambda m : [reg.eval(m), reg.evalDeriv(m)], m, plotIt=False) + self.assertTrue(passed) + + print 'Check 2 Deriv:', R + passed = Tests.checkDerivative(lambda m : [reg.evalDeriv(m), reg.eval2Deriv(m)], m, plotIt=False) + self.assertTrue(passed) + + if testRegMesh: + def test_regularizationMesh(self): for i, mesh in enumerate(self.meshlist): print 'Testing %iD'%mesh.dim - mapping = r.mapPair(mesh) - reg = r(mesh, mapping=mapping) - m = np.random.rand(mapping.nP) - reg.mref = np.ones_like(m)*np.mean(m) - - print 'Check: phi_m (mref) = %f' %reg.eval(reg.mref) - passed = reg.eval(reg.mref) < TOL - self.assertTrue(passed) - - print 'Check:', R - passed = Tests.checkDerivative(lambda m : [reg.eval(m), reg.evalDeriv(m)], m, plotIt=False) - self.assertTrue(passed) - - print 'Check 2 Deriv:', R - passed = Tests.checkDerivative(lambda m : [reg.evalDeriv(m), reg.eval2Deriv(m)], m, plotIt=False) - self.assertTrue(passed) - - def test_regularization_ActiveCells(self): - for R in dir(Regularization): - r = getattr(Regularization, R) - if not inspect.isclass(r): continue - if not issubclass(r, Regularization.BaseRegularization): - continue - - for i, mesh in enumerate(self.meshlist): - - print 'Testing Active Cells %iD'%(mesh.dim) + # mapping = r.mapPair(mesh) + # reg = r(mesh, mapping=mapping) + # m = np.random.rand(mapping.nP) if mesh.dim == 1: indAct = Utils.mkvc(mesh.gridCC <= 0.8) @@ -62,23 +100,9 @@ class RegularizationTests(unittest.TestCase): elif mesh.dim == 3: indAct = Utils.mkvc(mesh.gridCC[:,-1] <= 2*np.sin(2*np.pi*mesh.gridCC[:,0])+0.5 * 2*np.sin(2*np.pi*mesh.gridCC[:,1])+0.5) - mapping = Maps.IdentityMap(nP=indAct.nonzero()[0].size) + regmesh = Regularization.RegularizationMesh(mesh, indActive=indAct) - reg = r(mesh, mapping=mapping, indActive=indAct) - m = np.random.rand(mesh.nC)[indAct] - reg.mref = np.ones_like(m)*np.mean(m) - - print 'Check: phi_m (mref) = %f' %reg.eval(reg.mref) - passed = reg.eval(reg.mref) < TOL - self.assertTrue(passed) - - print 'Check:', R - passed = Tests.checkDerivative(lambda m : [reg.eval(m), reg.evalDeriv(m)], m, plotIt=False) - self.assertTrue(passed) - - print 'Check 2 Deriv:', R - passed = Tests.checkDerivative(lambda m : [reg.evalDeriv(m), reg.eval2Deriv(m)], m, plotIt=False) - self.assertTrue(passed) + assert (regmesh.vol == mesh.vol[indAct]).all() if __name__ == '__main__': diff --git a/tests/dcip/__init__.py b/tests/dcip/__init__.py new file mode 100644 index 00000000..420388ef --- /dev/null +++ b/tests/dcip/__init__.py @@ -0,0 +1,12 @@ +import os +import glob +import unittest + +if __name__ == '__main__': + test_file_strings = glob.glob('test_*.py') + module_strings = [str[0:len(str)-3] for str in test_file_strings] + suites = [unittest.defaultTestLoader.loadTestsFromName(str) for str + in module_strings] + testSuite = unittest.TestSuite(suites) + + unittest.TextTestRunner(verbosity=2).run(testSuite) diff --git a/tests/dcip/test_forward_DCproblem.py b/tests/dcip/test_forward_DCproblem.py new file mode 100644 index 00000000..a2708936 --- /dev/null +++ b/tests/dcip/test_forward_DCproblem.py @@ -0,0 +1,77 @@ +import unittest +from SimPEG import * +import SimPEG.DCIP as DC + + +class DCProblemTests(unittest.TestCase): + + def setUp(self): + + aSpacing=2.5 + nElecs=10 + + surveySize = nElecs*aSpacing - aSpacing + cs = surveySize/nElecs/4 + + mesh = Mesh.TensorMesh([ + [(cs,10, -1.3),(cs,surveySize/cs),(cs,10, 1.3)], + [(cs,3, -1.3),(cs,3,1.3)], + # [(cs,5, -1.3),(cs,10)] + ],'CN') + + srcList = DC.Utils.WennerSrcList(nElecs, aSpacing, in2D=True) + survey = DC.SurveyDC(srcList) + problem = DC.ProblemDC_CC(mesh) + problem.pair(survey) + + mSynth = np.ones(mesh.nC) + survey.makeSyntheticData(mSynth) + + # Now set up the problem to do some minimization + dmis = DataMisfit.l2_DataMisfit(survey) + reg = Regularization.Tikhonov(mesh) + opt = Optimization.InexactGaussNewton(maxIterLS=20, maxIter=10, tolF=1e-6, tolX=1e-6, tolG=1e-6, maxIterCG=6) + invProb = InvProblem.BaseInvProblem(dmis, reg, opt, beta=1e4) + inv = Inversion.BaseInversion(invProb) + + self.inv = inv + self.reg = reg + self.p = problem + self.mesh = mesh + self.m0 = mSynth + self.survey = survey + self.dmis = dmis + + def test_misfit(self): + derChk = lambda m: [self.survey.dpred(m), lambda mx: self.p.Jvec(self.m0, mx)] + passed = Tests.checkDerivative(derChk, self.m0, plotIt=False) + self.assertTrue(passed) + + def test_adjoint(self): + # Adjoint Test + u = np.random.rand(self.mesh.nC*self.survey.nSrc) + v = np.random.rand(self.mesh.nC) + w = np.random.rand(self.survey.dobs.shape[0]) + wtJv = w.dot(self.p.Jvec(self.m0, v)) + vtJtw = v.dot(self.p.Jtvec(self.m0, w)) + passed = np.abs(wtJv - vtJtw) < 1e-10 + print 'Adjoint Test', np.abs(wtJv - vtJtw), passed + self.assertTrue(passed) + + def test_dataObj(self): + derChk = lambda m: [self.dmis.eval(m), self.dmis.evalDeriv(m)] + passed = Tests.checkDerivative(derChk, self.m0, plotIt=False) + self.assertTrue(passed) + + + def test_massMatrices(self): + Gu = np.random.rand(self.mesh.nF) + def derChk(m): + self.p.curModel = m + return [self.p.Msig * Gu, self.p.dMdsig(Gu)] + passed = Tests.checkDerivative(derChk, self.m0, plotIt=False) + self.assertTrue(passed) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/dcip/test_forward_IPproblem.py b/tests/dcip/test_forward_IPproblem.py new file mode 100644 index 00000000..ff7b1ab6 --- /dev/null +++ b/tests/dcip/test_forward_IPproblem.py @@ -0,0 +1,65 @@ +import unittest +import SimPEG.DCIP as DC +from SimPEG import * + +class IPforwardTests(unittest.TestCase): + + def test_IPforward(self): + + cs = 12.5 + nc = 200/cs+1 + hx = [(cs,7, -1.3),(cs,nc),(cs,7, 1.3)] + hy = [(cs,7, -1.3),(cs,int(nc/2+1)),(cs,7, 1.3)] + hz = [(cs,7, -1.3),(cs,int(nc/2+1))] + mesh = Mesh.TensorMesh([hx, hy, hz], 'CCN') + sighalf = 1e-2 + sigma = np.ones(mesh.nC)*sighalf + p0 = np.r_[-50., 50., -50.] + p1 = np.r_[ 50.,-50., -150.] + blk_ind = Utils.ModelBuilder.getIndicesBlock(p0, p1, mesh.gridCC) + sigma[blk_ind] = 1e-3 + eta = np.zeros_like(sigma) + eta[blk_ind] = 0.1 + sigmaInf = sigma.copy() + sigma0 = sigma*(1-eta) + + nElecs = 11 + x_temp = np.linspace(-100, 100, nElecs) + aSpacing = x_temp[1]-x_temp[0] + y_temp = 0. + xyz = Utils.ndgrid(x_temp, np.r_[y_temp], np.r_[0.]) + srcList = DC.Utils.WennerSrcList(nElecs,aSpacing) + survey = DC.SurveyDC(srcList) + + imap = Maps.IdentityMap(mesh) + problem = DC.ProblemDC_CC(mesh, mapping=imap) + + try: + from pymatsolver import MumpsSolver + solver = MumpsSolver + except ImportError, e: + solver = SolverLU + + problem.Solver = solver + problem.pair(survey) + + phi0 = survey.dpred(sigma0) + phiInf = survey.dpred(sigmaInf) + + phiIP_true = phi0-phiInf + + surveyIP = DC.SurveyIP(srcList) + problemIP = DC.ProblemIP(mesh, sigma=sigma) + problemIP.pair(surveyIP) + + problemIP.Solver = solver + + phiIP_approx = surveyIP.dpred(eta) + + err = np.linalg.norm(phiIP_true-phiIP_approx) / np.linalg.norm(phiIP_true) + + self.assertTrue(err < 0.02) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/dcip/test_sens_IPproblem.py b/tests/dcip/test_sens_IPproblem.py new file mode 100644 index 00000000..9ba18031 --- /dev/null +++ b/tests/dcip/test_sens_IPproblem.py @@ -0,0 +1,82 @@ +import unittest +from SimPEG import * +import SimPEG.DCIP as DC + +class IPProblemTests(unittest.TestCase): + + def setUp(self): + + cs = 12.5 + nc = 500/cs+1 + hx = [(cs,0, -1.3),(cs,nc),(cs,0, 1.3)] + hy = [(cs,0, -1.3),(cs,int(nc/2+1)),(cs,0, 1.3)] + hz = [(cs,0, -1.3),(cs,int(nc/2+1))] + mesh = Mesh.TensorMesh([hx, hy, hz], 'CCN') + sighalf = 1e-2 + sigma = np.ones(mesh.nC)*sighalf + p0 = np.r_[-50., 50., -50.] + p1 = np.r_[ 50.,-50., -150.] + blk_ind = Utils.ModelBuilder.getIndicesBlock(p0, p1, mesh.gridCC) + sigma[blk_ind] = 1e-3 + eta = np.zeros_like(sigma) + eta[blk_ind] = 0.1 + + nElecs = 5 + x_temp = np.linspace(-250, 250, nElecs) + aSpacing = x_temp[1]-x_temp[0] + y_temp = 0. + xyz = Utils.ndgrid(x_temp, np.r_[y_temp], np.r_[0.]) + srcList = DC.Utils.WennerSrcList(nElecs,aSpacing) + survey = DC.SurveyIP(srcList) + imap = Maps.IdentityMap(mesh) + problem = DC.ProblemIP(mesh, sigma=sigma, mapping= imap) + problem.pair(survey) + + try: + from pymatsolver import MumpsSolver + problem.Solver = MumpsSolver + except ImportError, e: + problem.Solver = SolverLU + + mSynth = eta + survey.makeSyntheticData(mSynth) + + # Now set up the problem to do some minimization + dmis = DataMisfit.l2_DataMisfit(survey) + reg = Regularization.Tikhonov(mesh) + opt = Optimization.InexactGaussNewton(maxIterLS=20, maxIter=10, tolF=1e-6, tolX=1e-6, tolG=1e-6, maxIterCG=6) + invProb = InvProblem.BaseInvProblem(dmis, reg, opt, beta=1e4) + inv = Inversion.BaseInversion(invProb) + + self.inv = inv + self.reg = reg + self.p = problem + self.mesh = mesh + self.m0 = mSynth + self.survey = survey + self.dmis = dmis + + def test_misfit(self): + derChk = lambda m: [self.survey.dpred(m), lambda mx: self.p.Jvec(self.m0, mx)] + passed = Tests.checkDerivative(derChk, self.m0*0, plotIt=False) + self.assertTrue(passed) + + def test_adjoint(self): + # Adjoint Test + u = np.random.rand(self.mesh.nC*self.survey.nSrc) + v = np.random.rand(self.mesh.nC) + w = np.random.rand(self.survey.dobs.shape[0]) + wtJv = w.dot(self.p.Jvec(self.m0, v)) + vtJtw = v.dot(self.p.Jtvec(self.m0, w)) + passed = np.abs(wtJv - vtJtw) < 1e-10 + print 'Adjoint Test', np.abs(wtJv - vtJtw), passed + self.assertTrue(passed) + + def test_dataObj(self): + derChk = lambda m: [self.dmis.eval(m), self.dmis.evalDeriv(m)] + passed = Tests.checkDerivative(derChk, self.m0, plotIt=False) + self.assertTrue(passed) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/em/fdem/forward/test_FDEM_analytics.py b/tests/em/fdem/forward/test_FDEM_analytics.py index 9786e7c8..0ea43ca7 100644 --- a/tests/em/fdem/forward/test_FDEM_analytics.py +++ b/tests/em/fdem/forward/test_FDEM_analytics.py @@ -28,12 +28,12 @@ class FDEM_analyticTests(unittest.TestCase): x = np.linspace(-10,10,5) XYZ = Utils.ndgrid(x,np.r_[0],np.r_[0]) - rxList = EM.FDEM.Rx(XYZ, 'exi') + rxList = EM.FDEM.Rx.eField(XYZ, orientation='x', real_or_imag='imag') Src0 = EM.FDEM.Src.MagDipole([rxList],loc=np.r_[0.,0.,0.], freq=freq) survey = EM.FDEM.Survey([Src0]) - prb = EM.FDEM.Problem_b(mesh, mapping=mapping) + prb = EM.FDEM.Problem3D_b(mesh, mapping=mapping) prb.pair(survey) try: @@ -125,8 +125,8 @@ class FDEM_analyticTests(unittest.TestCase): mapping = [('sigma', Maps.IdentityMap(mesh)),('mu', Maps.IdentityMap(mesh))] - prbe = EM.FDEM.Problem_h(mesh, mapping=mapping) - prbm = EM.FDEM.Problem_e(mesh, mapping=mapping) + prbe = EM.FDEM.Problem3D_h(mesh, mapping=mapping) + prbm = EM.FDEM.Problem3D_e(mesh, mapping=mapping) prbe.pair(surveye) # pair problem and survey prbm.pair(surveym) diff --git a/tests/em/fdem/forward/test_FDEM_forward.py b/tests/em/fdem/forward/test_FDEM_forward.py index 437f3708..da446675 100644 --- a/tests/em/fdem/forward/test_FDEM_forward.py +++ b/tests/em/fdem/forward/test_FDEM_forward.py @@ -3,125 +3,75 @@ from SimPEG import * from SimPEG import EM import sys from scipy.constants import mu_0 -from SimPEG.EM.Utils.testingUtils import getFDEMProblem +from SimPEG.EM.Utils.testingUtils import getFDEMProblem, crossCheckTest testEB = True testHJ = True - +testEJ = True +testBH = True verbose = False -TOL = 1e-5 -FLR = 1e-20 # "zero", so if residual below this --> pass regardless of order -CONDUCTIVITY = 1e1 -MU = mu_0 -freq = 1e-1 -addrandoms = True +TOLEBHJ = 1e-5 +TOLEJHB = 1 # averaging and more sensitive to boundary condition violations (ie. the impact of violating the boundary conditions in each case is different.) +#TODO: choose better testing parameters to lower this SrcList = ['RawVec', 'MagDipole_Bfield', 'MagDipole', 'CircularLoop'] -def crossCheckTest(fdemType, comp): - - l2norm = lambda r: np.sqrt(r.dot(r)) - - prb1 = getFDEMProblem(fdemType, comp, SrcList, freq, verbose) - mesh = prb1.mesh - print 'Cross Checking Forward: %s formulation - %s' % (fdemType, comp) - m = np.log(np.ones(mesh.nC)*CONDUCTIVITY) - mu = np.log(np.ones(mesh.nC)*MU) - - if addrandoms is True: - m = m + np.random.randn(mesh.nC)*np.log(CONDUCTIVITY)*1e-1 - mu = mu + np.random.randn(mesh.nC)*MU*1e-1 - - # prb1.PropMap.PropModel.mu = mu - # prb1.PropMap.PropModel.mui = 1./mu - survey1 = prb1.survey - d1 = survey1.dpred(m) - - if verbose: - print ' Problem 1 solved' - - if fdemType == 'e': - prb2 = getFDEMProblem('b', comp, SrcList, freq, verbose) - elif fdemType == 'b': - prb2 = getFDEMProblem('e', comp, SrcList, freq, verbose) - elif fdemType == 'j': - prb2 = getFDEMProblem('h', comp, SrcList, freq, verbose) - elif fdemType == 'h': - prb2 = getFDEMProblem('j', comp, SrcList, freq, verbose) - else: - raise NotImplementedError() - - # prb2.mu = mu - survey2 = prb2.survey - d2 = survey2.dpred(m) - - if verbose: - print ' Problem 2 solved' - - r = d2-d1 - l2r = l2norm(r) - - tol = np.max([TOL*(10**int(np.log10(l2norm(d1)))),FLR]) - print l2norm(d1), l2norm(d2), l2r , tol, l2r < tol - return l2r < tol - - class FDEM_CrossCheck(unittest.TestCase): if testEB: def test_EB_CrossCheck_exr_Eform(self): - self.assertTrue(crossCheckTest('e', 'exr')) + self.assertTrue(crossCheckTest(SrcList, 'e', 'b', 'exr', verbose=verbose)) def test_EB_CrossCheck_eyr_Eform(self): - self.assertTrue(crossCheckTest('e', 'eyr')) + self.assertTrue(crossCheckTest(SrcList, 'e', 'b', 'eyr', verbose=verbose)) def test_EB_CrossCheck_ezr_Eform(self): - self.assertTrue(crossCheckTest('e', 'ezr')) + self.assertTrue(crossCheckTest(SrcList, 'e', 'b', 'ezr', verbose=verbose)) def test_EB_CrossCheck_exi_Eform(self): - self.assertTrue(crossCheckTest('e', 'exi')) + self.assertTrue(crossCheckTest(SrcList, 'e', 'b', 'exi', verbose=verbose)) def test_EB_CrossCheck_eyi_Eform(self): - self.assertTrue(crossCheckTest('e', 'eyi')) + self.assertTrue(crossCheckTest(SrcList, 'e', 'b', 'eyi', verbose=verbose)) def test_EB_CrossCheck_ezi_Eform(self): - self.assertTrue(crossCheckTest('e', 'ezi')) + self.assertTrue(crossCheckTest(SrcList, 'e', 'b', 'ezi', verbose=verbose)) def test_EB_CrossCheck_bxr_Eform(self): - self.assertTrue(crossCheckTest('e', 'bxr')) + self.assertTrue(crossCheckTest(SrcList, 'e', 'b', 'bxr', verbose=verbose)) def test_EB_CrossCheck_byr_Eform(self): - self.assertTrue(crossCheckTest('e', 'byr')) + self.assertTrue(crossCheckTest(SrcList, 'e', 'b', 'byr', verbose=verbose)) def test_EB_CrossCheck_bzr_Eform(self): - self.assertTrue(crossCheckTest('e', 'bzr')) + self.assertTrue(crossCheckTest(SrcList, 'e', 'b', 'bzr', verbose=verbose)) def test_EB_CrossCheck_bxi_Eform(self): - self.assertTrue(crossCheckTest('e', 'bxi')) + self.assertTrue(crossCheckTest(SrcList, 'e', 'b', 'bxi', verbose=verbose)) def test_EB_CrossCheck_byi_Eform(self): - self.assertTrue(crossCheckTest('e', 'byi')) + self.assertTrue(crossCheckTest(SrcList, 'e', 'b', 'byi', verbose=verbose)) def test_EB_CrossCheck_bzi_Eform(self): - self.assertTrue(crossCheckTest('e', 'bzi')) + self.assertTrue(crossCheckTest(SrcList, 'e', 'b', 'bzi', verbose=verbose)) if testHJ: def test_HJ_CrossCheck_jxr_Jform(self): - self.assertTrue(crossCheckTest('j', 'jxr')) + self.assertTrue(crossCheckTest(SrcList, 'j', 'h', 'jxr', verbose=verbose)) def test_HJ_CrossCheck_jyr_Jform(self): - self.assertTrue(crossCheckTest('j', 'jyr')) + self.assertTrue(crossCheckTest(SrcList, 'j', 'h', 'jyr', verbose=verbose)) def test_HJ_CrossCheck_jzr_Jform(self): - self.assertTrue(crossCheckTest('j', 'jzr')) + self.assertTrue(crossCheckTest(SrcList, 'j', 'h', 'jzr', verbose=verbose)) def test_HJ_CrossCheck_jxi_Jform(self): - self.assertTrue(crossCheckTest('j', 'jxi')) + self.assertTrue(crossCheckTest(SrcList, 'j', 'h', 'jxi', verbose=verbose)) def test_HJ_CrossCheck_jyi_Jform(self): - self.assertTrue(crossCheckTest('j', 'jyi')) + self.assertTrue(crossCheckTest(SrcList, 'j', 'h', 'jyi', verbose=verbose)) def test_HJ_CrossCheck_jzi_Jform(self): - self.assertTrue(crossCheckTest('j', 'jzi')) + self.assertTrue(crossCheckTest(SrcList, 'j', 'h', 'jzi', verbose=verbose)) def test_HJ_CrossCheck_hxr_Jform(self): - self.assertTrue(crossCheckTest('j', 'hxr')) + self.assertTrue(crossCheckTest(SrcList, 'j', 'h', 'hxr', verbose=verbose)) def test_HJ_CrossCheck_hyr_Jform(self): - self.assertTrue(crossCheckTest('j', 'hyr')) + self.assertTrue(crossCheckTest(SrcList, 'j', 'h', 'hyr', verbose=verbose)) def test_HJ_CrossCheck_hzr_Jform(self): - self.assertTrue(crossCheckTest('j', 'hzr')) + self.assertTrue(crossCheckTest(SrcList, 'j', 'h', 'hzr', verbose=verbose)) def test_HJ_CrossCheck_hxi_Jform(self): - self.assertTrue(crossCheckTest('j', 'hxi')) + self.assertTrue(crossCheckTest(SrcList, 'j', 'h', 'hxi', verbose=verbose)) def test_HJ_CrossCheck_hyi_Jform(self): - self.assertTrue(crossCheckTest('j', 'hyi')) + self.assertTrue(crossCheckTest(SrcList, 'j', 'h', 'hyi', verbose=verbose)) def test_HJ_CrossCheck_hzi_Jform(self): - self.assertTrue(crossCheckTest('j', 'hzi')) + self.assertTrue(crossCheckTest(SrcList, 'j', 'h', 'hzi', verbose=verbose)) if __name__ == '__main__': unittest.main() \ No newline at end of file diff --git a/tests/em/fdem/forward/test_FDEM_forwardEJHB.py b/tests/em/fdem/forward/test_FDEM_forwardEJHB.py new file mode 100644 index 00000000..e6319dfc --- /dev/null +++ b/tests/em/fdem/forward/test_FDEM_forwardEJHB.py @@ -0,0 +1,125 @@ +import unittest +from SimPEG import * +from SimPEG import EM +import sys +from scipy.constants import mu_0 +from SimPEG.EM.Utils.testingUtils import getFDEMProblem, crossCheckTest + +testEJ = True +testBH = True + +TOLEJHB = 1 # averaging and more sensitive to boundary condition violations (ie. the impact of violating the boundary conditions in each case is different.) +#TODO: choose better testing parameters to lower this + +SrcList = ['RawVec', 'MagDipole', 'MagDipole_Bfield', 'MagDipole', 'CircularLoop'] + + +class FDEM_CrossCheck(unittest.TestCase): + if testEJ: + def test_EJ_CrossCheck_jxr_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'e', 'j', 'jxr', TOL=TOLEJHB)) + def test_EJ_CrossCheck_jyr_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'e', 'j', 'jyr', TOL=TOLEJHB)) + def test_EJ_CrossCheck_jzr_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'e', 'j', 'jzr', TOL=TOLEJHB)) + def test_EJ_CrossCheck_jxi_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'e', 'j', 'jxi', TOL=TOLEJHB)) + def test_EJ_CrossCheck_jyi_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'e', 'j', 'jyi', TOL=TOLEJHB)) + def test_EJ_CrossCheck_jzi_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'e', 'j', 'jzi', TOL=TOLEJHB)) + + def test_EJ_CrossCheck_exr_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'e', 'j', 'exr', TOL=TOLEJHB)) + def test_EJ_CrossCheck_eyr_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'e', 'j', 'eyr', TOL=TOLEJHB)) + def test_EJ_CrossCheck_ezr_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'e', 'j', 'ezr', TOL=TOLEJHB)) + def test_EJ_CrossCheck_exi_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'e', 'j', 'exi', TOL=TOLEJHB)) + def test_EJ_CrossCheck_eyi_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'e', 'j', 'eyi', TOL=TOLEJHB)) + def test_EJ_CrossCheck_ezi_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'e', 'j', 'ezi', TOL=TOLEJHB)) + + def test_EJ_CrossCheck_bxr_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'e', 'j', 'bxr', TOL=TOLEJHB)) + def test_EJ_CrossCheck_byr_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'e', 'j', 'byr', TOL=TOLEJHB)) + def test_EJ_CrossCheck_bzr_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'e', 'j', 'bzr', TOL=TOLEJHB)) + def test_EJ_CrossCheck_bxi_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'e', 'j', 'bxi', TOL=TOLEJHB)) + def test_EJ_CrossCheck_byi_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'e', 'j', 'byi', TOL=TOLEJHB)) + def test_EJ_CrossCheck_bzi_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'e', 'j', 'bzi', TOL=TOLEJHB)) + + def test_EJ_CrossCheck_hxr_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'e', 'j', 'hxr', TOL=TOLEJHB)) + def test_EJ_CrossCheck_hyr_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'e', 'j', 'hyr', TOL=TOLEJHB)) + def test_EJ_CrossCheck_hzr_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'e', 'j', 'hzr', TOL=TOLEJHB)) + def test_EJ_CrossCheck_hxi_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'e', 'j', 'hxi', TOL=TOLEJHB)) + def test_EJ_CrossCheck_hyi_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'e', 'j', 'hyi', TOL=TOLEJHB)) + def test_EJ_CrossCheck_hzi_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'e', 'j', 'hzi', TOL=TOLEJHB)) + + if testBH: + def test_HB_CrossCheck_jxr_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'h', 'b', 'jxr', TOL=TOLEJHB)) + def test_HB_CrossCheck_jyr_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'h', 'b', 'jyr', TOL=TOLEJHB)) + def test_HB_CrossCheck_jzr_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'h', 'b', 'jzr', TOL=TOLEJHB)) + def test_HB_CrossCheck_jxi_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'h', 'b', 'jxi', TOL=TOLEJHB)) + def test_HB_CrossCheck_jyi_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'h', 'b', 'jyi', TOL=TOLEJHB)) + def test_HB_CrossCheck_jzi_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'h', 'b', 'jzi', TOL=TOLEJHB)) + + def test_HB_CrossCheck_exr_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'h', 'b', 'exr', TOL=TOLEJHB)) + def test_HB_CrossCheck_eyr_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'h', 'b', 'eyr', TOL=TOLEJHB)) + def test_HB_CrossCheck_ezr_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'h', 'b', 'ezr', TOL=TOLEJHB)) + def test_HB_CrossCheck_exi_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'h', 'b', 'exi', TOL=TOLEJHB)) + def test_HB_CrossCheck_eyi_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'h', 'b', 'eyi', TOL=TOLEJHB)) + def test_HB_CrossCheck_ezi_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'h', 'b', 'ezi', TOL=TOLEJHB)) + + def test_HB_CrossCheck_bxr_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'h', 'b', 'bxr', TOL=TOLEJHB)) + def test_HB_CrossCheck_byr_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'h', 'b', 'byr', TOL=TOLEJHB)) + def test_HB_CrossCheck_bzr_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'h', 'b', 'bzr', TOL=TOLEJHB)) + def test_HB_CrossCheck_bxi_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'h', 'b', 'bxi', TOL=TOLEJHB)) + def test_HB_CrossCheck_byi_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'h', 'b', 'byi', TOL=TOLEJHB)) + def test_HB_CrossCheck_bzi_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'h', 'b', 'bzi', TOL=TOLEJHB)) + + def test_HB_CrossCheck_hxr_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'h', 'b', 'hxr', TOL=TOLEJHB)) + def test_HB_CrossCheck_hyr_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'h', 'b', 'hyr', TOL=TOLEJHB)) + def test_HB_CrossCheck_hzr_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'h', 'b', 'hzr', TOL=TOLEJHB)) + def test_HB_CrossCheck_hxi_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'h', 'b', 'hxi', TOL=TOLEJHB)) + def test_HB_CrossCheck_hyi_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'h', 'b', 'hyi', TOL=TOLEJHB)) + def test_HB_CrossCheck_hzi_Jform(self): + self.assertTrue(crossCheckTest(SrcList, 'h', 'b', 'hzi', TOL=TOLEJHB)) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/tests/em/fdem/forward/test_FDEM_forwardHB.py b/tests/em/fdem/forward/test_FDEM_forwardHB.py new file mode 100644 index 00000000..8fce615b --- /dev/null +++ b/tests/em/fdem/forward/test_FDEM_forwardHB.py @@ -0,0 +1,128 @@ +import unittest +from SimPEG import * +from SimPEG import EM +import sys +from scipy.constants import mu_0 +from SimPEG.EM.Utils.testingUtils import getFDEMProblem, crossCheckTest + +testEB = True +testHJ = True +testEJ = True +testBH = True +verbose = False + +TOLEJHB = 1 # averaging and more sensitive to boundary condition violations (ie. the impact of violating the boundary conditions in each case is different.) +#TODO: choose better testing parameters to lower this + +SrcList = ['RawVec', 'MagDipole_Bfield', 'MagDipole', 'CircularLoop'] + + +class FDEM_CrossCheck(unittest.TestCase): + if testBH: + def test_BH_CrossCheck_jxr(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'jxr', verbose=verbose, TOL=TOLEJHB)) + def test_BH_CrossCheck_jyr(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'jyr', verbose=verbose, TOL=TOLEJHB)) + def test_BH_CrossCheck_jzr(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'jzr', verbose=verbose, TOL=TOLEJHB)) + def test_BH_CrossCheck_jxi(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'jxi', verbose=verbose, TOL=TOLEJHB)) + def test_BH_CrossCheck_jyi(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'jyi', verbose=verbose, TOL=TOLEJHB)) + def test_BH_CrossCheck_jzi(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'jzi', verbose=verbose, TOL=TOLEJHB)) + + def test_BH_CrossCheck_exr(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'exr', verbose=verbose, TOL=TOLEJHB)) + def test_BH_CrossCheck_eyr(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'eyr', verbose=verbose, TOL=TOLEJHB)) + def test_BH_CrossCheck_ezr(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'ezr', verbose=verbose, TOL=TOLEJHB)) + def test_BH_CrossCheck_exi(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'exi', verbose=verbose, TOL=TOLEJHB)) + def test_BH_CrossCheck_eyi(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'eyi', verbose=verbose, TOL=TOLEJHB)) + def test_BH_CrossCheck_ezi(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'ezi', verbose=verbose, TOL=TOLEJHB)) + + def test_BH_CrossCheck_bxr(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'bxr', verbose=verbose, TOL=TOLEJHB)) + def test_BH_CrossCheck_byr(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'byr', verbose=verbose, TOL=TOLEJHB)) + def test_BH_CrossCheck_bzr(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'bzr', verbose=verbose, TOL=TOLEJHB)) + def test_BH_CrossCheck_bxi(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'bxi', verbose=verbose, TOL=TOLEJHB)) + def test_BH_CrossCheck_byi(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'byi', verbose=verbose, TOL=TOLEJHB)) + def test_BH_CrossCheck_bzi(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'bzi', verbose=verbose, TOL=TOLEJHB)) + + def test_BH_CrossCheck_hxr(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'hxr', verbose=verbose, TOL=TOLEJHB)) + def test_BH_CrossCheck_hyr(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'hyr', verbose=verbose, TOL=TOLEJHB)) + def test_BH_CrossCheck_hzr(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'hzr', verbose=verbose, TOL=TOLEJHB)) + def test_BH_CrossCheck_hxi(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'hxi', verbose=verbose, TOL=TOLEJHB)) + def test_BH_CrossCheck_hyi(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'hyi', verbose=verbose, TOL=TOLEJHB)) + def test_BH_CrossCheck_hzi(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'hzi', verbose=verbose, TOL=TOLEJHB)) + + if testBH: + def test_BH_CrossCheck_jxr(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'jxr', verbose=verbose, TOL=TOLEJHB)) + def test_BH_CrossCheck_jyr(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'jyr', verbose=verbose, TOL=TOLEJHB)) + def test_BH_CrossCheck_jzr(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'jzr', verbose=verbose, TOL=TOLEJHB)) + def test_BH_CrossCheck_jxi(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'jxi', verbose=verbose, TOL=TOLEJHB)) + def test_BH_CrossCheck_jyi(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'jyi', verbose=verbose, TOL=TOLEJHB)) + def test_BH_CrossCheck_jzi(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'jzi', verbose=verbose, TOL=TOLEJHB)) + + def test_BH_CrossCheck_exr(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'exr', verbose=verbose, TOL=TOLEJHB)) + def test_BH_CrossCheck_eyr(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'eyr', verbose=verbose, TOL=TOLEJHB)) + def test_BH_CrossCheck_ezr(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'ezr', verbose=verbose, TOL=TOLEJHB)) + def test_BH_CrossCheck_exi(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'exi', verbose=verbose, TOL=TOLEJHB)) + def test_BH_CrossCheck_eyi(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'eyi', verbose=verbose, TOL=TOLEJHB)) + def test_BH_CrossCheck_ezi(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'ezi', verbose=verbose, TOL=TOLEJHB)) + + def test_BH_CrossCheck_bxr(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'bxr', verbose=verbose, TOL=TOLEJHB)) + def test_BH_CrossCheck_byr(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'byr', verbose=verbose, TOL=TOLEJHB)) + def test_BH_CrossCheck_bzr(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'bzr', verbose=verbose, TOL=TOLEJHB)) + def test_BH_CrossCheck_bxi(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'bxi', verbose=verbose, TOL=TOLEJHB)) + def test_BH_CrossCheck_byi(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'byi', verbose=verbose, TOL=TOLEJHB)) + def test_BH_CrossCheck_bzi(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'bzi', verbose=verbose, TOL=TOLEJHB)) + + def test_BH_CrossCheck_hxr(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'hxr', verbose=verbose, TOL=TOLEJHB)) + def test_BH_CrossCheck_hyr(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'hyr', verbose=verbose, TOL=TOLEJHB)) + def test_BH_CrossCheck_hzr(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'hzr', verbose=verbose, TOL=TOLEJHB)) + def test_BH_CrossCheck_hxi(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'hxi', verbose=verbose, TOL=TOLEJHB)) + def test_BH_CrossCheck_hyi(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'hyi', verbose=verbose, TOL=TOLEJHB)) + def test_BH_CrossCheck_hzi(self): + self.assertTrue(crossCheckTest(SrcList, 'b', 'h', 'hzi', verbose=verbose, TOL=TOLEJHB)) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/em/fdem/inverse/adjoint/test_FDEM_adjoint.py b/tests/em/fdem/inverse/adjoint/test_FDEM_adjointEB.py similarity index 57% rename from tests/em/fdem/inverse/adjoint/test_FDEM_adjoint.py rename to tests/em/fdem/inverse/adjoint/test_FDEM_adjointEB.py index f77f2131..25762368 100644 --- a/tests/em/fdem/inverse/adjoint/test_FDEM_adjoint.py +++ b/tests/em/fdem/inverse/adjoint/test_FDEM_adjointEB.py @@ -5,8 +5,8 @@ import sys from scipy.constants import mu_0 from SimPEG.EM.Utils.testingUtils import getFDEMProblem -testEB = True -testHJ = True +testE = True +testB = True verbose = False @@ -17,10 +17,10 @@ MU = mu_0 freq = 1e-1 addrandoms = True -SrcType = 'RawVec' #or 'MAgDipole_Bfield', 'CircularLoop', 'RawVec' +SrcList = ['RawVec', 'MagDipole'] #or 'MAgDipole_Bfield', 'CircularLoop', 'RawVec' def adjointTest(fdemType, comp): - prb = getFDEMProblem(fdemType, comp, [SrcType], freq) + prb = getFDEMProblem(fdemType, comp, SrcList, freq) print 'Adjoint %s formulation - %s' % (fdemType, comp) m = np.log(np.ones(prb.mapping.nP)*CONDUCTIVITY) @@ -45,7 +45,7 @@ def adjointTest(fdemType, comp): return np.abs(vJw - wJtv) < tol class FDEM_AdjointTests(unittest.TestCase): - if testEB: + if testE: def test_Jtvec_adjointTest_exr_Eform(self): self.assertTrue(adjointTest('e', 'exr')) def test_Jtvec_adjointTest_eyr_Eform(self): @@ -72,6 +72,33 @@ class FDEM_AdjointTests(unittest.TestCase): def test_Jtvec_adjointTest_bzi_Eform(self): self.assertTrue(adjointTest('e', 'bzi')) + def test_Jtvec_adjointTest_jxr_Eform(self): + self.assertTrue(adjointTest('e', 'jxr')) + def test_Jtvec_adjointTest_jyr_Eform(self): + self.assertTrue(adjointTest('e', 'jyr')) + def test_Jtvec_adjointTest_jzr_Eform(self): + self.assertTrue(adjointTest('e', 'jzr')) + def test_Jtvec_adjointTest_jxi_Eform(self): + self.assertTrue(adjointTest('e', 'jxi')) + def test_Jtvec_adjointTest_jyi_Eform(self): + self.assertTrue(adjointTest('e', 'jyi')) + def test_Jtvec_adjointTest_jzi_Eform(self): + self.assertTrue(adjointTest('e', 'jzi')) + + def test_Jtvec_adjointTest_hxr_Eform(self): + self.assertTrue(adjointTest('e', 'hxr')) + def test_Jtvec_adjointTest_hyr_Eform(self): + self.assertTrue(adjointTest('e', 'hyr')) + def test_Jtvec_adjointTest_hzr_Eform(self): + self.assertTrue(adjointTest('e', 'hzr')) + def test_Jtvec_adjointTest_hxi_Eform(self): + self.assertTrue(adjointTest('e', 'hxi')) + def test_Jtvec_adjointTest_hyi_Eform(self): + self.assertTrue(adjointTest('e', 'hyi')) + def test_Jtvec_adjointTest_hzi_Eform(self): + self.assertTrue(adjointTest('e', 'hzi')) + + if testB: def test_Jtvec_adjointTest_exr_Bform(self): self.assertTrue(adjointTest('b', 'exr')) def test_Jtvec_adjointTest_eyr_Bform(self): @@ -84,6 +111,7 @@ class FDEM_AdjointTests(unittest.TestCase): self.assertTrue(adjointTest('b', 'eyi')) def test_Jtvec_adjointTest_ezi_Bform(self): self.assertTrue(adjointTest('b', 'ezi')) + def test_Jtvec_adjointTest_bxr_Bform(self): self.assertTrue(adjointTest('b', 'bxr')) def test_Jtvec_adjointTest_byr_Bform(self): @@ -97,59 +125,31 @@ class FDEM_AdjointTests(unittest.TestCase): def test_Jtvec_adjointTest_bzi_Bform(self): self.assertTrue(adjointTest('b', 'bzi')) + def test_Jtvec_adjointTest_jxr_Bform(self): + self.assertTrue(adjointTest('b', 'jxr')) + def test_Jtvec_adjointTest_jyr_Bform(self): + self.assertTrue(adjointTest('b', 'jyr')) + def test_Jtvec_adjointTest_jzr_Bform(self): + self.assertTrue(adjointTest('b', 'jzr')) + def test_Jtvec_adjointTest_jxi_Bform(self): + self.assertTrue(adjointTest('b', 'jxi')) + def test_Jtvec_adjointTest_jyi_Bform(self): + self.assertTrue(adjointTest('b', 'jyi')) + def test_Jtvec_adjointTest_jzi_Bform(self): + self.assertTrue(adjointTest('b', 'jzi')) - if testHJ: - def test_Jtvec_adjointTest_jxr_Jform(self): - self.assertTrue(adjointTest('j', 'jxr')) - def test_Jtvec_adjointTest_jyr_Jform(self): - self.assertTrue(adjointTest('j', 'jyr')) - def test_Jtvec_adjointTest_jzr_Jform(self): - self.assertTrue(adjointTest('j', 'jzr')) - def test_Jtvec_adjointTest_jxi_Jform(self): - self.assertTrue(adjointTest('j', 'jxi')) - def test_Jtvec_adjointTest_jyi_Jform(self): - self.assertTrue(adjointTest('j', 'jyi')) - def test_Jtvec_adjointTest_jzi_Jform(self): - self.assertTrue(adjointTest('j', 'jzi')) - - def test_Jtvec_adjointTest_hxr_Jform(self): - self.assertTrue(adjointTest('j', 'hxr')) - def test_Jtvec_adjointTest_hyr_Jform(self): - self.assertTrue(adjointTest('j', 'hyr')) - def test_Jtvec_adjointTest_hzr_Jform(self): - self.assertTrue(adjointTest('j', 'hzr')) - def test_Jtvec_adjointTest_hxi_Jform(self): - self.assertTrue(adjointTest('j', 'hxi')) - def test_Jtvec_adjointTest_hyi_Jform(self): - self.assertTrue(adjointTest('j', 'hyi')) - def test_Jtvec_adjointTest_hzi_Jform(self): - self.assertTrue(adjointTest('j', 'hzi')) - - def test_Jtvec_adjointTest_hxr_Hform(self): - self.assertTrue(adjointTest('h', 'hxr')) - def test_Jtvec_adjointTest_hyr_Hform(self): - self.assertTrue(adjointTest('h', 'hyr')) - def test_Jtvec_adjointTest_hzr_Hform(self): - self.assertTrue(adjointTest('h', 'hzr')) - def test_Jtvec_adjointTest_hxi_Hform(self): - self.assertTrue(adjointTest('h', 'hxi')) - def test_Jtvec_adjointTest_hyi_Hform(self): - self.assertTrue(adjointTest('h', 'hyi')) - def test_Jtvec_adjointTest_hzi_Hform(self): - self.assertTrue(adjointTest('h', 'hzi')) - - def test_Jtvec_adjointTest_hxr_Hform(self): - self.assertTrue(adjointTest('h', 'jxr')) - def test_Jtvec_adjointTest_hyr_Hform(self): - self.assertTrue(adjointTest('h', 'jyr')) - def test_Jtvec_adjointTest_hzr_Hform(self): - self.assertTrue(adjointTest('h', 'jzr')) - def test_Jtvec_adjointTest_hxi_Hform(self): - self.assertTrue(adjointTest('h', 'jxi')) - def test_Jtvec_adjointTest_hyi_Hform(self): - self.assertTrue(adjointTest('h', 'jyi')) - def test_Jtvec_adjointTest_hzi_Hform(self): - self.assertTrue(adjointTest('h', 'jzi')) + def test_Jtvec_adjointTest_hxr_Bform(self): + self.assertTrue(adjointTest('b', 'hxr')) + def test_Jtvec_adjointTest_hyr_Bform(self): + self.assertTrue(adjointTest('b', 'hyr')) + def test_Jtvec_adjointTest_hzr_Bform(self): + self.assertTrue(adjointTest('b', 'hzr')) + def test_Jtvec_adjointTest_hxi_Bform(self): + self.assertTrue(adjointTest('b', 'hxi')) + def test_Jtvec_adjointTest_hyi_Bform(self): + self.assertTrue(adjointTest('b', 'hyi')) + def test_Jtvec_adjointTest_hzi_Bform(self): + self.assertTrue(adjointTest('b', 'hzi')) if __name__ == '__main__': diff --git a/tests/em/fdem/inverse/adjoint/test_FDEM_adjointHJ.py b/tests/em/fdem/inverse/adjoint/test_FDEM_adjointHJ.py new file mode 100644 index 00000000..c3fb3d37 --- /dev/null +++ b/tests/em/fdem/inverse/adjoint/test_FDEM_adjointHJ.py @@ -0,0 +1,155 @@ +import unittest +from SimPEG import * +from SimPEG import EM +import sys +from scipy.constants import mu_0 +from SimPEG.EM.Utils.testingUtils import getFDEMProblem + +testJ = True +testH = True + +verbose = False + +TOL = 1e-5 +FLR = 1e-20 # "zero", so if residual below this --> pass regardless of order +CONDUCTIVITY = 1e1 +MU = mu_0 +freq = 1e-1 +addrandoms = True + +SrcList = ['RawVec', 'MagDipole'] #or 'MAgDipole_Bfield', 'CircularLoop', 'RawVec' + +def adjointTest(fdemType, comp): + prb = getFDEMProblem(fdemType, comp, SrcList, freq) + print 'Adjoint %s formulation - %s' % (fdemType, comp) + + m = np.log(np.ones(prb.mapping.nP)*CONDUCTIVITY) + mu = np.ones(prb.mesh.nC)*MU + + if addrandoms is True: + m = m + np.random.randn(prb.mapping.nP)*np.log(CONDUCTIVITY)*1e-1 + mu = mu + np.random.randn(prb.mesh.nC)*MU*1e-1 + + survey = prb.survey + u = prb.fields(m) + + v = np.random.rand(survey.nD) + w = np.random.rand(prb.mesh.nC) + + vJw = v.dot(prb.Jvec(m, w, u)) + wJtv = w.dot(prb.Jtvec(m, v, u)) + tol = np.max([TOL*(10**int(np.log10(np.abs(vJw)))),FLR]) + print vJw, wJtv, vJw - wJtv, tol, np.abs(vJw - wJtv) < tol + return np.abs(vJw - wJtv) < tol + +class FDEM_AdjointTests(unittest.TestCase): + + if testJ: + def test_Jtvec_adjointTest_jxr_Jform(self): + self.assertTrue(adjointTest('j', 'jxr')) + def test_Jtvec_adjointTest_jyr_Jform(self): + self.assertTrue(adjointTest('j', 'jyr')) + def test_Jtvec_adjointTest_jzr_Jform(self): + self.assertTrue(adjointTest('j', 'jzr')) + def test_Jtvec_adjointTest_jxi_Jform(self): + self.assertTrue(adjointTest('j', 'jxi')) + def test_Jtvec_adjointTest_jyi_Jform(self): + self.assertTrue(adjointTest('j', 'jyi')) + def test_Jtvec_adjointTest_jzi_Jform(self): + self.assertTrue(adjointTest('j', 'jzi')) + + def test_Jtvec_adjointTest_hxr_Jform(self): + self.assertTrue(adjointTest('j', 'hxr')) + def test_Jtvec_adjointTest_hyr_Jform(self): + self.assertTrue(adjointTest('j', 'hyr')) + def test_Jtvec_adjointTest_hzr_Jform(self): + self.assertTrue(adjointTest('j', 'hzr')) + def test_Jtvec_adjointTest_hxi_Jform(self): + self.assertTrue(adjointTest('j', 'hxi')) + def test_Jtvec_adjointTest_hyi_Jform(self): + self.assertTrue(adjointTest('j', 'hyi')) + def test_Jtvec_adjointTest_hzi_Jform(self): + self.assertTrue(adjointTest('j', 'hzi')) + + def test_Jtvec_adjointTest_exr_Jform(self): + self.assertTrue(adjointTest('j', 'exr')) + def test_Jtvec_adjointTest_eyr_Jform(self): + self.assertTrue(adjointTest('j', 'eyr')) + def test_Jtvec_adjointTest_ezr_Jform(self): + self.assertTrue(adjointTest('j', 'ezr')) + def test_Jtvec_adjointTest_exi_Jform(self): + self.assertTrue(adjointTest('j', 'exi')) + def test_Jtvec_adjointTest_eyi_Jform(self): + self.assertTrue(adjointTest('j', 'eyi')) + def test_Jtvec_adjointTest_ezi_Jform(self): + self.assertTrue(adjointTest('j', 'ezi')) + + def test_Jtvec_adjointTest_bxr_Jform(self): + self.assertTrue(adjointTest('j', 'bxr')) + def test_Jtvec_adjointTest_byr_Jform(self): + self.assertTrue(adjointTest('j', 'byr')) + def test_Jtvec_adjointTest_bzr_Jform(self): + self.assertTrue(adjointTest('j', 'bzr')) + def test_Jtvec_adjointTest_bxi_Jform(self): + self.assertTrue(adjointTest('j', 'bxi')) + def test_Jtvec_adjointTest_byi_Jform(self): + self.assertTrue(adjointTest('j', 'byi')) + def test_Jtvec_adjointTest_bzi_Jform(self): + self.assertTrue(adjointTest('j', 'bzi')) + + if testH: + def test_Jtvec_adjointTest_hxr_Hform(self): + self.assertTrue(adjointTest('h', 'hxr')) + def test_Jtvec_adjointTest_hyr_Hform(self): + self.assertTrue(adjointTest('h', 'hyr')) + def test_Jtvec_adjointTest_hzr_Hform(self): + self.assertTrue(adjointTest('h', 'hzr')) + def test_Jtvec_adjointTest_hxi_Hform(self): + self.assertTrue(adjointTest('h', 'hxi')) + def test_Jtvec_adjointTest_hyi_Hform(self): + self.assertTrue(adjointTest('h', 'hyi')) + def test_Jtvec_adjointTest_hzi_Hform(self): + self.assertTrue(adjointTest('h', 'hzi')) + + def test_Jtvec_adjointTest_jxr_Hform(self): + self.assertTrue(adjointTest('h', 'jxr')) + def test_Jtvec_adjointTest_jyr_Hform(self): + self.assertTrue(adjointTest('h', 'jyr')) + def test_Jtvec_adjointTest_jzr_Hform(self): + self.assertTrue(adjointTest('h', 'jzr')) + def test_Jtvec_adjointTest_jxi_Hform(self): + self.assertTrue(adjointTest('h', 'jxi')) + def test_Jtvec_adjointTest_jyi_Hform(self): + self.assertTrue(adjointTest('h', 'jyi')) + def test_Jtvec_adjointTest_jzi_Hform(self): + self.assertTrue(adjointTest('h', 'jzi')) + + def test_Jtvec_adjointTest_exr_Hform(self): + self.assertTrue(adjointTest('h', 'exr')) + def test_Jtvec_adjointTest_eyr_Hform(self): + self.assertTrue(adjointTest('h', 'eyr')) + def test_Jtvec_adjointTest_ezr_Hform(self): + self.assertTrue(adjointTest('h', 'ezr')) + def test_Jtvec_adjointTest_exi_Hform(self): + self.assertTrue(adjointTest('h', 'exi')) + def test_Jtvec_adjointTest_eyi_Hform(self): + self.assertTrue(adjointTest('h', 'eyi')) + def test_Jtvec_adjointTest_ezi_Hform(self): + self.assertTrue(adjointTest('h', 'ezi')) + + def test_Jtvec_adjointTest_bxr_Hform(self): + self.assertTrue(adjointTest('h', 'bxr')) + def test_Jtvec_adjointTest_byr_Hform(self): + self.assertTrue(adjointTest('h', 'byr')) + def test_Jtvec_adjointTest_bzr_Hform(self): + self.assertTrue(adjointTest('h', 'bzr')) + def test_Jtvec_adjointTest_bxi_Hform(self): + self.assertTrue(adjointTest('h', 'bxi')) + def test_Jtvec_adjointTest_byi_Hform(self): + self.assertTrue(adjointTest('h', 'byi')) + def test_Jtvec_adjointTest_bzi_Hform(self): + self.assertTrue(adjointTest('h', 'bzi')) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/em/fdem/inverse/derivs/test_FDEM_derivs.py b/tests/em/fdem/inverse/derivs/test_FDEM_derivs.py index d3bcb218..0a2e8b82 100644 --- a/tests/em/fdem/inverse/derivs/test_FDEM_derivs.py +++ b/tests/em/fdem/inverse/derivs/test_FDEM_derivs.py @@ -5,9 +5,11 @@ import sys from scipy.constants import mu_0 from SimPEG.EM.Utils.testingUtils import getFDEMProblem -testDerivs = True -testEB = True -testHJ = True + +testE = True +testB = True +testH = True +testJ = True verbose = False @@ -18,12 +20,12 @@ MU = mu_0 freq = 1e-1 addrandoms = True -SrcType = 'RawVec' #or 'MAgDipole_Bfield', 'CircularLoop', 'RawVec' +SrcType = ['MagDipole', 'RawVec'] #or 'MAgDipole_Bfield', 'CircularLoop', 'RawVec' def derivTest(fdemType, comp): - prb = getFDEMProblem(fdemType, comp, [SrcType], freq) + prb = getFDEMProblem(fdemType, comp, SrcType, freq) print '%s formulation - %s' % (fdemType, comp) x0 = np.log(np.ones(prb.mapping.nP)*CONDUCTIVITY) mu = np.log(np.ones(prb.mesh.nC)*MU) @@ -32,9 +34,6 @@ def derivTest(fdemType, comp): x0 = x0 + np.random.randn(prb.mapping.nP)*np.log(CONDUCTIVITY)*1e-1 mu = mu + np.random.randn(prb.mapping.nP)*MU*1e-1 - # prb.PropMap.PropModel.mu = mu - # prb.PropMap.PropModel.mui = 1./mu - survey = prb.survey def fun(x): return survey.dpred(x), lambda x: prb.Jvec(x0, x) @@ -43,7 +42,7 @@ def derivTest(fdemType, comp): class FDEM_DerivTests(unittest.TestCase): - if testEB: + if testE: def test_Jvec_exr_Eform(self): self.assertTrue(derivTest('e', 'exr')) def test_Jvec_eyr_Eform(self): @@ -70,6 +69,33 @@ class FDEM_DerivTests(unittest.TestCase): def test_Jvec_bzi_Eform(self): self.assertTrue(derivTest('e', 'bzi')) + def test_Jvec_exr_Eform(self): + self.assertTrue(derivTest('e', 'jxr')) + def test_Jvec_eyr_Eform(self): + self.assertTrue(derivTest('e', 'jyr')) + def test_Jvec_ezr_Eform(self): + self.assertTrue(derivTest('e', 'jzr')) + def test_Jvec_exi_Eform(self): + self.assertTrue(derivTest('e', 'jxi')) + def test_Jvec_eyi_Eform(self): + self.assertTrue(derivTest('e', 'jyi')) + def test_Jvec_ezi_Eform(self): + self.assertTrue(derivTest('e', 'jzi')) + + def test_Jvec_bxr_Eform(self): + self.assertTrue(derivTest('e', 'hxr')) + def test_Jvec_byr_Eform(self): + self.assertTrue(derivTest('e', 'hyr')) + def test_Jvec_bzr_Eform(self): + self.assertTrue(derivTest('e', 'hzr')) + def test_Jvec_bxi_Eform(self): + self.assertTrue(derivTest('e', 'hxi')) + def test_Jvec_byi_Eform(self): + self.assertTrue(derivTest('e', 'hyi')) + def test_Jvec_bzi_Eform(self): + self.assertTrue(derivTest('e', 'hzi')) + + if testB: def test_Jvec_exr_Bform(self): self.assertTrue(derivTest('b', 'exr')) def test_Jvec_eyr_Bform(self): @@ -96,7 +122,33 @@ class FDEM_DerivTests(unittest.TestCase): def test_Jvec_bzi_Bform(self): self.assertTrue(derivTest('b', 'bzi')) - if testHJ: + def test_Jvec_jxr_Bform(self): + self.assertTrue(derivTest('b', 'jxr')) + def test_Jvec_jyr_Bform(self): + self.assertTrue(derivTest('b', 'jyr')) + def test_Jvec_jzr_Bform(self): + self.assertTrue(derivTest('b', 'jzr')) + def test_Jvec_jxi_Bform(self): + self.assertTrue(derivTest('b', 'jxi')) + def test_Jvec_jyi_Bform(self): + self.assertTrue(derivTest('b', 'jyi')) + def test_Jvec_jzi_Bform(self): + self.assertTrue(derivTest('b', 'jzi')) + + def test_Jvec_hxr_Bform(self): + self.assertTrue(derivTest('b', 'hxr')) + def test_Jvec_hyr_Bform(self): + self.assertTrue(derivTest('b', 'hyr')) + def test_Jvec_hzr_Bform(self): + self.assertTrue(derivTest('b', 'hzr')) + def test_Jvec_hxi_Bform(self): + self.assertTrue(derivTest('b', 'hxi')) + def test_Jvec_hyi_Bform(self): + self.assertTrue(derivTest('b', 'hyi')) + def test_Jvec_hzi_Bform(self): + self.assertTrue(derivTest('b', 'hzi')) + + if testJ: def test_Jvec_jxr_Jform(self): self.assertTrue(derivTest('j', 'jxr')) def test_Jvec_jyr_Jform(self): @@ -123,6 +175,34 @@ class FDEM_DerivTests(unittest.TestCase): def test_Jvec_hzi_Jform(self): self.assertTrue(derivTest('j', 'hzi')) + def test_Jvec_exr_Jform(self): + self.assertTrue(derivTest('j', 'exr')) + def test_Jvec_eyr_Jform(self): + self.assertTrue(derivTest('j', 'eyr')) + def test_Jvec_ezr_Jform(self): + self.assertTrue(derivTest('j', 'ezr')) + def test_Jvec_exi_Jform(self): + self.assertTrue(derivTest('j', 'exi')) + def test_Jvec_eyi_Jform(self): + self.assertTrue(derivTest('j', 'eyi')) + def test_Jvec_ezi_Jform(self): + self.assertTrue(derivTest('j', 'ezi')) + + def test_Jvec_bxr_Jform(self): + self.assertTrue(derivTest('j', 'bxr')) + def test_Jvec_byr_Jform(self): + self.assertTrue(derivTest('j', 'byr')) + def test_Jvec_bzr_Jform(self): + self.assertTrue(derivTest('j', 'bzr')) + def test_Jvec_bxi_Jform(self): + self.assertTrue(derivTest('j', 'bxi')) + def test_Jvec_byi_Jform(self): + self.assertTrue(derivTest('j', 'byi')) + def test_Jvec_bzi_Jform(self): + self.assertTrue(derivTest('j', 'bzi')) + + + if testH: def test_Jvec_hxr_Hform(self): self.assertTrue(derivTest('h', 'hxr')) def test_Jvec_hyr_Hform(self): @@ -149,6 +229,32 @@ class FDEM_DerivTests(unittest.TestCase): def test_Jvec_hzi_Hform(self): self.assertTrue(derivTest('h', 'jzi')) + def test_Jvec_exr_Hform(self): + self.assertTrue(derivTest('h', 'exr')) + def test_Jvec_eyr_Hform(self): + self.assertTrue(derivTest('h', 'eyr')) + def test_Jvec_ezr_Hform(self): + self.assertTrue(derivTest('h', 'ezr')) + def test_Jvec_exi_Hform(self): + self.assertTrue(derivTest('h', 'exi')) + def test_Jvec_eyi_Hform(self): + self.assertTrue(derivTest('h', 'eyi')) + def test_Jvec_ezi_Hform(self): + self.assertTrue(derivTest('h', 'ezi')) + + def test_Jvec_bxr_Hform(self): + self.assertTrue(derivTest('h', 'bxr')) + def test_Jvec_byr_Hform(self): + self.assertTrue(derivTest('h', 'byr')) + def test_Jvec_bzr_Hform(self): + self.assertTrue(derivTest('h', 'bzr')) + def test_Jvec_bxi_Hform(self): + self.assertTrue(derivTest('h', 'bxi')) + def test_Jvec_byi_Hform(self): + self.assertTrue(derivTest('h', 'byi')) + def test_Jvec_bzi_Hform(self): + self.assertTrue(derivTest('h', 'bzi')) + if __name__ == '__main__': unittest.main() diff --git a/tests/em/tdem/test_TDEM_forward_Analytic.py b/tests/em/tdem/test_TDEM_forward_Analytic.py index 0756d64d..82134928 100644 --- a/tests/em/tdem/test_TDEM_forward_Analytic.py +++ b/tests/em/tdem/test_TDEM_forward_Analytic.py @@ -10,7 +10,9 @@ except ImportError, e: MumpsSolver = SolverLU -def halfSpaceProblemAnaDiff(meshType, sig_half=1e-2, rxOffset=50., bounds=[1e-5,1e-3], showIt=False): +def halfSpaceProblemAnaDiff(meshType, sig_half=1e-2, rxOffset=50., bounds=None, showIt=False): + if bounds is None: + bounds = [1e-5,1e-3] if meshType == 'CYL': cs, ncx, ncz, npad = 5., 30, 10, 15 hx = [(cs,ncx), (cs,npad,1.3)] diff --git a/tests/flow/test_Richards.py b/tests/flow/test_Richards.py index d63a6210..f67ec71d 100644 --- a/tests/flow/test_Richards.py +++ b/tests/flow/test_Richards.py @@ -116,8 +116,8 @@ class RichardsTests1D(unittest.TestCase): v = np.random.rand(self.survey.nD) z = np.random.rand(self.M.nC) Hs = self.prob.fields(self.Ks) - vJz = v.dot(self.prob.Jvec(self.Ks,z,u=Hs)) - zJv = z.dot(self.prob.Jtvec(self.Ks,v,u=Hs)) + vJz = v.dot(self.prob.Jvec(self.Ks,z,f=Hs)) + zJv = z.dot(self.prob.Jtvec(self.Ks,v,f=Hs)) tol = TOL*(10**int(np.log10(np.abs(zJv)))) passed = np.abs(vJz - zJv) < tol print 'Richards Adjoint Test - PressureHead' @@ -188,8 +188,8 @@ class RichardsTests2D(unittest.TestCase): v = np.random.rand(self.survey.nD) z = np.random.rand(self.M.nC) Hs = self.prob.fields(self.Ks) - vJz = v.dot(self.prob.Jvec(self.Ks,z,u=Hs)) - zJv = z.dot(self.prob.Jtvec(self.Ks,v,u=Hs)) + vJz = v.dot(self.prob.Jvec(self.Ks,z,f=Hs)) + zJv = z.dot(self.prob.Jtvec(self.Ks,v,f=Hs)) tol = TOL*(10**int(np.log10(np.abs(zJv)))) passed = np.abs(vJz - zJv) < tol print '2D: Richards Adjoint Test - PressureHead' @@ -260,8 +260,8 @@ class RichardsTests3D(unittest.TestCase): v = np.random.rand(self.survey.nD) z = np.random.rand(self.M.nC) Hs = self.prob.fields(self.Ks) - vJz = v.dot(self.prob.Jvec(self.Ks,z,u=Hs)) - zJv = z.dot(self.prob.Jtvec(self.Ks,v,u=Hs)) + vJz = v.dot(self.prob.Jvec(self.Ks,z,f=Hs)) + zJv = z.dot(self.prob.Jtvec(self.Ks,v,f=Hs)) tol = TOL*(10**int(np.log10(np.abs(zJv)))) passed = np.abs(vJz - zJv) < tol print '3D: Richards Adjoint Test - PressureHead' diff --git a/tests/mesh/test_cylMesh.py b/tests/mesh/test_cylMesh.py index 5a963398..c7cd44ae 100644 --- a/tests/mesh/test_cylMesh.py +++ b/tests/mesh/test_cylMesh.py @@ -146,6 +146,20 @@ class TestCyl2DMesh(unittest.TestCase): assert np.abs(Pr*(Pc2r*mc) - Pc*mc).max() < 1e-3 + def test_getInterpMatCartMesh_Cells2Nodes(self): + + Mr = Mesh.TensorMesh([100,100,2], x0='CC0') + Mc = Mesh.CylMesh([np.ones(10)/5,1,10],x0='0C0',cartesianOrigin=[-0.2,-0.2,0]) + + mc = np.arange(Mc.nC) + xr = np.linspace(0,0.4,50) + xc = np.linspace(0,0.4,50) + 0.2 + Pr = Mr.getInterpolationMat(np.c_[xr,np.ones(50)*-0.2,np.ones(50)*0.5],'N') + Pc = Mc.getInterpolationMat(np.c_[xc,np.zeros(50),np.ones(50)*0.5],'CC') + Pc2r = Mc.getInterpolationMatCartMesh(Mr, 'CC', locTypeTo='N') + + assert np.abs(Pr*(Pc2r*mc) - Pc*mc).max() < 1e-3 + def test_getInterpMatCartMesh_Faces(self): Mr = Mesh.TensorMesh([100,100,2], x0='CC0') @@ -177,6 +191,37 @@ class TestCyl2DMesh(unittest.TestCase): assert np.abs(mag[dist > 0.1].min() - 1) < TOL + def test_getInterpMatCartMesh_Faces2Edges(self): + + Mr = Mesh.TensorMesh([100,100,2], x0='CC0') + Mc = Mesh.CylMesh([np.ones(10)/5,1,10],x0='0C0',cartesianOrigin=[-0.2,-0.2,0]) + + Pf2e = Mc.getInterpolationMatCartMesh(Mr, 'F', locTypeTo='E') + mf = np.ones(Mc.nF) + + ecart = Pf2e * mf + + excc = Mr.aveEx2CC*Mr.r(ecart, 'E', 'Ex') + eycc = Mr.aveEy2CC*Mr.r(ecart, 'E', 'Ey') + ezcc = Mr.r(ecart, 'E', 'Ez') + + indX = Utils.closestPoints(Mr, [0.45, -0.2, 0.5]) + indY = Utils.closestPoints(Mr, [-0.2, 0.45, 0.5]) + + TOL = 1e-2 + assert np.abs(float(excc[indX]) - 1) < TOL + assert np.abs(float(excc[indY]) - 0) < TOL + assert np.abs(float(eycc[indX]) - 0) < TOL + assert np.abs(float(eycc[indY]) - 1) < TOL + assert np.abs((ezcc - 1).sum()) < TOL + + mag = (excc**2 + eycc**2)**0.5 + dist = ((Mr.gridCC[:,0] + 0.2)**2 + (Mr.gridCC[:,1] + 0.2)**2)**0.5 + + assert np.abs(mag[dist > 0.1].max() - 1) < TOL + assert np.abs(mag[dist > 0.1].min() - 1) < TOL + + def test_getInterpMatCartMesh_Edges(self): Mr = Mesh.TensorMesh([100,100,2], x0='CC0') @@ -185,11 +230,42 @@ class TestCyl2DMesh(unittest.TestCase): Pe = Mc.getInterpolationMatCartMesh(Mr, 'E') me = np.ones(Mc.nE) - erect = Pe * me + ecart = Pe * me - excc = Mr.aveEx2CC*Mr.r(erect, 'E', 'Ex') - eycc = Mr.aveEy2CC*Mr.r(erect, 'E', 'Ey') - ezcc = Mr.r(erect, 'E', 'Ez') + excc = Mr.aveEx2CC*Mr.r(ecart, 'E', 'Ex') + eycc = Mr.aveEy2CC*Mr.r(ecart, 'E', 'Ey') + ezcc = Mr.aveEz2CC*Mr.r(ecart, 'E', 'Ez') + + indX = Utils.closestPoints(Mr, [0.45, -0.2, 0.5]) + indY = Utils.closestPoints(Mr, [-0.2, 0.45, 0.5]) + + TOL = 1e-2 + assert np.abs(float(excc[indX]) - 0) < TOL + assert np.abs(float(excc[indY]) + 1) < TOL + assert np.abs(float(eycc[indX]) - 1) < TOL + assert np.abs(float(eycc[indY]) - 0) < TOL + assert np.abs(ezcc.sum()) < TOL + + mag = (excc**2 + eycc**2)**0.5 + dist = ((Mr.gridCC[:,0] + 0.2)**2 + (Mr.gridCC[:,1] + 0.2)**2)**0.5 + + assert np.abs(mag[dist > 0.1].max() - 1) < TOL + assert np.abs(mag[dist > 0.1].min() - 1) < TOL + + + def test_getInterpMatCartMesh_Edges2Faces(self): + + Mr = Mesh.TensorMesh([100,100,2], x0='CC0') + Mc = Mesh.CylMesh([np.ones(10)/5,1,10],x0='0C0',cartesianOrigin=[-0.2,-0.2,0]) + + Pe2f = Mc.getInterpolationMatCartMesh(Mr, 'E', locTypeTo='F') + me = np.ones(Mc.nE) + + frect = Pe2f * me + + excc = Mr.aveFx2CC*Mr.r(frect, 'F', 'Fx') + eycc = Mr.aveFy2CC*Mr.r(frect, 'F', 'Fy') + ezcc = Mr.r(frect, 'F', 'Fz') indX = Utils.closestPoints(Mr, [0.45, -0.2, 0.5]) indY = Utils.closestPoints(Mr, [-0.2, 0.45, 0.5]) diff --git a/tests/mt/test_Problem3D_againstAnalytic.py b/tests/mt/test_Problem3D_againstAnalytic.py index f68e515f..602dbaeb 100644 --- a/tests/mt/test_Problem3D_againstAnalytic.py +++ b/tests/mt/test_Problem3D_againstAnalytic.py @@ -242,9 +242,6 @@ class TestAnalytics(unittest.TestCase): def test_appRes1en3(self):self.assertTrue(appResPhsHalfspace_eFrom_ps_Norm(1e-3)) def test_appPhs1en3(self):self.assertTrue(appResPhsHalfspace_eFrom_ps_Norm(1e-3,False)) - # Do a derivative test - def test_derivProj1(self):self.assertTrue(DerivProjfieldsTest(halfSpace(1e-2))) - # Do a derivative test of Jvec # def test_derivJvec_zxxr(self):self.assertTrue(DerivJvecTest(random(1e-2),'zxxr',.1)) # def test_derivJvec_zxxi(self):self.assertTrue(DerivJvecTest(random(1e-2),'zxxi',.1))