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/SimPEG/DCIP/BaseDC.py b/SimPEG/DCIP/BaseDC.py new file mode 100644 index 00000000..e3d353d7 --- /dev/null +++ b/SimPEG/DCIP/BaseDC.py @@ -0,0 +1,294 @@ +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, u=None): + """ + :param numpy.array m: model + :param numpy.array v: vector to multiply + :param numpy.array u: 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 u is None: + # Run forward simulation if $u$ not provided + u = self.fields(self.curModel)[self.survey.srcList, 'phi_sol'] + else: + u = u[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, u=None): + + self.curModel = m + sigma = self.curModel.transform # $\sigma = \mathcal{M}(\m)$ + if u is None: + # Run forward simulation if $u$ not provided + u = self.fields(self.curModel)[self.survey.srcList, 'phi_sol'] + else: + u = u[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..cec0ea2e --- /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, u=None): + """ + Predicted data. + + .. math:: + d_\\text{pred} = Pu(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, u=None): + return self.forward(v) + + def Jtvec(self, m, v, u=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..90bbbca0 --- /dev/null +++ b/SimPEG/DCIP/DCIPUtils.py @@ -0,0 +1,934 @@ +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): + """ + 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) + + 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 + + # Set depth to 0 for now + z0 = 0. + + # 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 + + # Compute pant leg of apparent rho + if stype == 'pdp': + leg = data * 2*np.pi * MA * ( MA + MN ) / MN + + leg = np.log10(abs(1/leg)) + + elif stype == 'dpdp': + leg = data * 2*np.pi / ( 1/MA - 1/MB - 1/NB + 1/NA ) + + + midx = np.hstack([midx, ( Cmid + Pmid )/2 ]) + midz = np.hstack([midz, -np.abs(Cmid-Pmid)/2 + z0 ]) + rho = np.hstack([rho,leg]) + + + ax = axs + + # 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') + + + plt.imshow(grid_rho.T, extent = (np.min(midx),np.max(midx),np.min(midz),np.max(midz)), origin='lower', alpha=0.8, vmin = np.min(rho), vmax = np.max(rho)) + cbar = plt.colorbar(format = '%.2f',fraction=0.04,orientation="horizontal") + + cmin,cmax = cbar.get_clim() + ticks = np.linspace(cmin,cmax,3) + cbar.set_ticks(ticks) + + # Plot apparent resistivity + plt.scatter(midx,midz,s=50,c=rho.T) + + ax.set_xticklabels([]) + + ax.set_ylabel('Z') + ax.yaxis.tick_right() + ax.yaxis.set_label_position('right') + plt.gca().set_aspect('equal', adjustable='box') + + + return ax + +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 re.match(stype,'dpdp'): +# +# for ii in range(0, int(nstn)-2): +# +# indx = np.min([ii+n+1,nstn]) +# Tx.append(np.c_[M[ii,:],N[ii,:]]) +# Rx.append(np.c_[M[ii+2:indx,:],N[ii+2:indx,:]]) +#============================================================================== + + 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, stype): + """ + 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') + 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("%e " % ii for ii in mkvc(tx[0,:])) + M = M[:,0] + N = N[:,0] + + if stype == 'GENERAL': + + fid.writelines("%e " % ii for ii in mkvc(tx[::2,:])) + M = M[:,0::2] + N = N[:,0::2] + + 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') + + 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)) + + 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') + + count += nD + + fid.close() + +def convertObs_DC3D_to_2D(DCsurvey,lineID): + """ + Read DC survey and data and change + coordinate system to distance along line assuming + all data is acquired along line. + First transmitter pole is assumed to be at the origin + + Assumes flat topo for now... + + Input: + :param Tx, Rx + + Output: + :figure Tx2d, Rx2d + + Edited Feb 17th, 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] + + # 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) + + 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): + """ + Read UBC GIF DCIP 3D observation file and generate arrays for tx-rx location + + Input: + :param fileName, path to the UBC GIF 3D obs file + + Output: + :param rx, tx, d, wd + :return + + Created on Mon December 7th, 2015 + + @author: dominiquef + + """ + + # Load file + obsfile = np.genfromtxt(fileName,delimiter=' \n',dtype=np.str,comments='!') + + # Pre-allocate + srcLists = [] + Rx = [] + d = [] + wd = [] + zflag = True # Flag for z value provided + + # Countdown for number of obs/tx + count = 0 + for ii in range(obsfile.shape[0]): + + if not obsfile[ii]: + continue + + # First line is transmitter with number of receivers + if count==0: + + 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[0:2],np.nan] + zflag = False + + else: + tx = temp[:-1] + + rx = [] + continue + + temp = np.fromstring(obsfile[ii], dtype=float,sep=' ') + + 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[0:2],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 + 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): + """ + 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_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): + print np.asarray(DCsurvey.srcList[ii].loc).shape + 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/EM/FDEM/FDEM.py b/SimPEG/EM/FDEM/FDEM.py index 4b137b2c..cf3dee7f 100644 --- a/SimPEG/EM/FDEM/FDEM.py +++ b/SimPEG/EM/FDEM/FDEM.py @@ -54,8 +54,7 @@ class BaseFDEMProblem(BaseEMProblem): Ainv = self.Solver(A, **self.solverOpts) sol = Ainv * rhs Srcs = self.survey.getSrcByFreq(freq) - ftype = self._fieldType + 'Solution' - F[Srcs, ftype] = sol + F[Srcs, self._solutionType] = sol Ainv.clean() return F @@ -78,30 +77,19 @@ class BaseFDEMProblem(BaseEMProblem): Jv = self.dataPair(self.survey) for freq in self.survey.freqs: - A = self.getA(freq) # + A = self.getA(freq) Ainv = self.Solver(A, **self.solverOpts) for src in self.survey.getSrcByFreq(freq): - ftype = self._fieldType + 'Solution' - u_src = u[src, ftype] - dA_dm = self.getADeriv_m(freq, u_src, v) - dRHS_dm = self.getRHSDeriv_m(freq, src, v) - du_dm = Ainv * ( - dA_dm + dRHS_dm ) + u_src = u[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_duFun = getattr(u, '_%sDeriv_u'%rx.projField, None) - df_dudu_dm = df_duFun(src, du_dm, adjoint=False) - - df_dmFun = getattr(u, '_%sDeriv_m'%rx.projField, None) - df_dm = df_dmFun(src, v, adjoint=False) - - - Df_Dm = np.array(df_dudu_dm + df_dm,dtype=complex) - - P = lambda v: rx.projectFieldsDeriv(src, self.mesh, u, v) # wrt u, also have wrt m - - Jv[src, rx] = P(Df_Dm) - + df_dmFun = getattr(u, '_%sDeriv'%rx.projField, None) + df_dm_v = df_dmFun(src, du_dm_v, v, adjoint=False) + Jv[src, rx] = rx.evalDeriv(src, self.mesh, u, df_dm_v) Ainv.clean() return Utils.mkvc(Jv) @@ -132,32 +120,28 @@ 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 = u[src, self._solutionType] for rx in src.rxList: - PTv = rx.projectFieldsDeriv(src, self.mesh, u, v[src, rx], adjoint=True) # wrt u, need possibility wrt m + PTv = rx.evalDeriv(src, self.mesh, u, v[src, rx], adjoint=True) # wrt u, need possibility wrt m + + df_duTFun = getattr(u, '_%sDeriv'%rx.projField, None) + df_duT, df_dmT = df_duTFun(src, None, PTv, adjoint=True) - df_duTFun = getattr(u, '_%sDeriv_u'%rx.projField, None) - df_duT = df_duTFun(src, PTv, adjoint=True) - ATinvdf_duT = ATinv * df_duT - dA_dmT = self.getADeriv_m(freq, u_src, ATinvdf_duT, adjoint=True) - dRHS_dmT = self.getRHSDeriv_m(freq,src, ATinvdf_duT, adjoint=True) + dA_dmT = self.getADeriv(freq, u_src, ATinvdf_duT, adjoint=True) + dRHS_dmT = self.getRHSDeriv(freq, src, ATinvdf_duT, adjoint=True) du_dmT = -dA_dmT + dRHS_dmT - df_dmFun = getattr(u, '_%sDeriv_m'%rx.projField, None) - dfT_dm = df_dmFun(src, PTv, adjoint=True) + df_dmT = df_dmT + du_dmT - du_dmT += dfT_dm - - # TODO: this should be taken care of by the reciever + # TODO: this should be taken care of by the reciever? real_or_imag = rx.projComp if real_or_imag is 'real': - Jtv += np.array(du_dmT,dtype=complex).real + Jtv += np.array(df_dmT, dtype=complex).real elif real_or_imag is 'imag': - Jtv += - np.array(du_dmT,dtype=complex).real + Jtv += - np.array(df_dmT, dtype=complex).real else: raise Exception('Must be real or imag') @@ -174,10 +158,10 @@ class BaseFDEMProblem(BaseEMProblem): :return: S_m, S_e (nE or nF, nSrc) """ Srcs = self.survey.getSrcByFreq(freq) - if self._eqLocs is 'FE': + 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._eqLocs is 'EF': + 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) @@ -213,9 +197,9 @@ class Problem_e(BaseFDEMProblem): :param SimPEG.Mesh mesh: mesh """ - _fieldType = 'e' - _eqLocs = 'FE' - fieldsPair = Fields_e + _solutionType = 'eSolution' + _formulation = 'EB' + fieldsPair = Fields_e def __init__(self, mesh, **kwargs): BaseFDEMProblem.__init__(self, mesh, **kwargs) @@ -239,7 +223,7 @@ class Problem_e(BaseFDEMProblem): return C.T*MfMui*C + 1j*omega(freq)*MeSigma - def getADeriv_m(self, freq, u, v, adjoint=False): + def getADeriv(self, freq, u, v, adjoint=False): """ Product of the derivative of our system matrix with respect to the model and a vector @@ -280,7 +264,7 @@ class Problem_e(BaseFDEMProblem): return C.T * (MfMui * S_m) -1j * omega(freq) * S_e - def getRHSDeriv_m(self, freq, src, v, adjoint=False): + def getRHSDeriv(self, freq, src, v, adjoint=False): """ Derivative of the right hand side with respect to the model @@ -324,9 +308,9 @@ class Problem_b(BaseFDEMProblem): :param SimPEG.Mesh mesh: mesh """ - _fieldType = 'b' - _eqLocs = 'FE' - fieldsPair = Fields_b + _solutionType = 'bSolution' + _formulation = 'EB' + fieldsPair = Fields_b def __init__(self, mesh, **kwargs): BaseFDEMProblem.__init__(self, mesh, **kwargs) @@ -354,7 +338,7 @@ class Problem_b(BaseFDEMProblem): return MfMui.T*A return A - def getADeriv_m(self, freq, u, v, adjoint=False): + def getADeriv(self, freq, u, v, adjoint=False): """ Product of the derivative of our system matrix with respect to the model and a vector @@ -411,7 +395,7 @@ class Problem_b(BaseFDEMProblem): return RHS - def getRHSDeriv_m(self, freq, src, v, adjoint=False): + def getRHSDeriv(self, freq, src, v, adjoint=False): """ Derivative of the right hand side with respect to the model @@ -472,9 +456,9 @@ class Problem_j(BaseFDEMProblem): :param SimPEG.Mesh mesh: mesh """ - _fieldType = 'j' - _eqLocs = 'EF' - fieldsPair = Fields_j + _solutionType = 'jSolution' + _formulation = 'HJ' + fieldsPair = Fields_j def __init__(self, mesh, **kwargs): BaseFDEMProblem.__init__(self, mesh, **kwargs) @@ -503,7 +487,7 @@ class Problem_j(BaseFDEMProblem): return A - def getADeriv_m(self, freq, u, v, adjoint=False): + def getADeriv(self, freq, u, v, adjoint=False): """ Product of the derivative of our system matrix with respect to the model and a vector @@ -524,16 +508,16 @@ class Problem_j(BaseFDEMProblem): MeMuI = self.MeMuI MfRho = self.MfRho C = self.mesh.edgeCurl - MfRhoDeriv_m = self.MfRhoDeriv(u) + MfRhoDeriv = self.MfRhoDeriv(u) if adjoint: if self._makeASymmetric is True: v = MfRho * v - return MfRhoDeriv_m.T * (C * (MeMuI.T * (C.T * v))) + return MfRhoDeriv.T * (C * (MeMuI.T * (C.T * v))) if self._makeASymmetric is True: - return MfRho.T * (C * ( MeMuI * (C.T * (MfRhoDeriv_m * v) ))) - return C * (MeMuI * (C.T * (MfRhoDeriv_m * v))) + return MfRho.T * (C * ( MeMuI * (C.T * (MfRhoDeriv * v) ))) + return C * (MeMuI * (C.T * (MfRhoDeriv * v))) def getRHS(self, freq): @@ -560,7 +544,7 @@ class Problem_j(BaseFDEMProblem): return RHS - def getRHSDeriv_m(self, freq, src, v, adjoint=False): + def getRHSDeriv(self, freq, src, v, adjoint=False): """ Derivative of the right hand side with respect to the model @@ -610,9 +594,9 @@ class Problem_h(BaseFDEMProblem): :param SimPEG.Mesh mesh: mesh """ - _fieldType = 'h' - _eqLocs = 'EF' - fieldsPair = Fields_h + _solutionType = 'hSolution' + _formulation = 'HJ' + fieldsPair = Fields_h def __init__(self, mesh, **kwargs): BaseFDEMProblem.__init__(self, mesh, **kwargs) @@ -635,7 +619,7 @@ class Problem_h(BaseFDEMProblem): return C.T * (MfRho * C) + 1j*omega(freq)*MeMu - def getADeriv_m(self, freq, u, v, adjoint=False): + def getADeriv(self, freq, u, v, adjoint=False): """ Product of the derivative of our system matrix with respect to the model and a vector @@ -652,11 +636,11 @@ class Problem_h(BaseFDEMProblem): MeMu = self.MeMu C = self.mesh.edgeCurl - MfRhoDeriv_m = self.MfRhoDeriv(C*u) + MfRhoDeriv = self.MfRhoDeriv(C*u) if adjoint: - return MfRhoDeriv_m.T * (C * v) - return C.T * (MfRhoDeriv_m * v) + return MfRhoDeriv.T * (C * v) + return C.T * (MfRhoDeriv * v) def getRHS(self, freq): """ @@ -677,7 +661,7 @@ class Problem_h(BaseFDEMProblem): return S_m + C.T * ( MfRho * S_e ) - def getRHSDeriv_m(self, freq, src, v, adjoint=False): + def getRHSDeriv(self, freq, src, v, adjoint=False): """ Derivative of the right hand side with respect to the model diff --git a/SimPEG/EM/FDEM/FieldsFDEM.py b/SimPEG/EM/FDEM/FieldsFDEM.py index e171a5c5..0f0a4963 100644 --- a/SimPEG/EM/FDEM/FieldsFDEM.py +++ b/SimPEG/EM/FDEM/FieldsFDEM.py @@ -3,7 +3,7 @@ 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): @@ -32,6 +32,134 @@ class Fields(SimPEG.Problem.Fields): knownFields = {} dtype = complex + def _e(self, solution, srcList): + """ + 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: + 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 + + :param numpy.ndarray solution: field we solved for + :param list srcList: list of sources + :rtype: numpy.ndarray + :return: total magnetic flux density + """ + 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 + + :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: + 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 + + :param numpy.ndarray solution: field we solved for + :param list srcList: list of sources + :rtype: numpy.ndarray + :return: total current density + """ + 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) + + def _eDeriv(self, src, du_dm_v, v, adjoint = False): + """ + Total derivative of e with respect to the inversion model. Returns :math:`d\mathbf{e}/d\mathbf{m}` for forward and (:math:`d\mathbf{e}/d\mathbf{u}`, :math:`d\mathb{u}/d\mathbf{m}`) for the adjoint + + :param Src src: sorce + :param numpy.ndarray du_dm_v: derivative of the solution vector with respect to the model times a vector (is None for adjoint) + :param numpy.ndarray v: vector to take sensitivity product with + :param bool adjoint: adjoint? + :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: + 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 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): + """ + Total derivative of b with respect to the inversion model. Returns :math:`d\mathbf{b}/d\mathbf{m}` for forward and (:math:`d\mathbf{b}/d\mathbf{u}`, :math:`d\mathb{u}/d\mathbf{m}`) for the adjoint + + :param Src src: sorce + :param numpy.ndarray du_dm_v: derivative of the solution vector with respect to the model times a vector (is None for adjoint) + :param numpy.ndarray v: vector to take sensitivity product with + :param bool adjoint: adjoint? + :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: + 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 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): + """ + Total derivative of h with respect to the inversion model. Returns :math:`d\mathbf{h}/d\mathbf{m}` for forward and (:math:`d\mathbf{h}/d\mathbf{u}`, :math:`d\mathb{u}/d\mathbf{m}`) for the adjoint + + :param Src src: sorce + :param numpy.ndarray du_dm_v: derivative of the solution vector with respect to the model times a vector (is None for adjoint) + :param numpy.ndarray v: vector to take sensitivity product with + :param bool adjoint: adjoint? + :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: + raise NotImplementedError ('Getting hDerivs from %s is not implemented' %self.knownFields.keys()[0]) + + if adjoint: + return self._hDeriv_u(src, 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): + """ + Total derivative of j with respect to the inversion model. Returns :math:`d\mathbf{j}/d\mathbf{m}` for forward and (:math:`d\mathbf{j}/d\mathbf{u}`, :math:`d\mathb{u}/d\mathbf{m}`) for the adjoint + + :param Src src: sorce + :param numpy.ndarray du_dm_v: derivative of the solution vector with respect to the model times a vector (is None for adjoint) + :param numpy.ndarray v: vector to take sensitivity product with + :param bool adjoint: adjoint? + :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: + 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 np.array(self._jDeriv_u(src, du_dm_v, adjoint) + self._jDeriv_m(src, v, adjoint), dtype = complex) + class Fields_e(Fields): """ Fields object for Problem_e. @@ -47,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): """ @@ -67,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 @@ -82,25 +229,12 @@ class Fields_e(Fields): :rtype: numpy.ndarray :return: secondary electric field """ - return eSolution - def _e(self, eSolution, srcList): - """ - Total electric field is sum of primary and secondary - - :param numpy.ndarray eSolution: field we solved for - :param list srcList: list of sources - :rtype: numpy.ndarray - :return: total electric field - """ - - return self._ePrimary(eSolution,srcList) + self._eSecondary(eSolution,srcList) - def _eDeriv_u(self, src, v, adjoint = False): """ - Derivative of the total electric field with respect to the thing we - solved for + 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 @@ -113,7 +247,7 @@ class Fields_e(Fields): def _eDeriv_m(self, src, v, adjoint = False): """ - Derivative of the total electric field with respect to the inversion model. Here, we assume that the primary does not depend on the model. + 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 @@ -135,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 @@ -159,75 +293,137 @@ class Fields_e(Fields): b[:,i] = b[:,i]+ 1./(1j*omega(src.freq)) * S_m return b - def _bSecondaryDeriv_u(self, src, v, adjoint = False): + def _bDeriv_u(self, src, du_dm_v, adjoint = False): """ - Derivative of the secondary magnetic flux density with respect to the thing we solved for + Derivative of the magnetic flux density with respect to the thing we solved for :param SimPEG.EM.FDEM.Src src: source - :param numpy.ndarray v: vector to take product with - :param bool adjoint: adjoint? - :rtype: numpy.ndarray - :return: product of the derivative of the secondary magnetic flux density with respect to the field we solved for with a vector - """ - - C = self._edgeCurl - if adjoint: - return - 1./(1j*omega(src.freq)) * (C.T * v) - return - 1./(1j*omega(src.freq)) * (C * v) - - def _bSecondaryDeriv_m(self, src, v, adjoint = False): - """ - 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 _b(self, eSolution, srcList): - """ - Total magnetic flux density is sum of primary and secondary - - :param numpy.ndarray eSolution: field we solved for - :param list srcList: list of sources - :rtype: numpy.ndarray - :return: total magnetic flux density - """ - - return self._bPrimary(eSolution, srcList) + self._bSecondary(eSolution, srcList) - - def _bDeriv_u(self, src, v, adjoint=False): - """ - Derivative of the total magnetic flux density with respect to the thing we solved for - - :param SimPEG.EM.FDEM.Src src: source - :param numpy.ndarray v: vector to take product with + :param 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 """ - # Primary does not depend on u - return self._bSecondaryDeriv_u(src, 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): """ - 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) ) ) ) + + + 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 Fields_b(Fields): @@ -246,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): @@ -254,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): """ @@ -269,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 @@ -287,34 +504,23 @@ class Fields_b(Fields): return bSolution - def _b(self, bSolution, srcList): + def _bDeriv_u(self, src, du_dm_v, adjoint=False): """ - Total magnetic flux density is sum of primary and secondary - - :param numpy.ndarray bSolution: field we solved for - :param list srcList: list of sources - :rtype: numpy.ndarray - :return: total magnetic flux density - """ - - return self._bPrimary(bSolution, srcList) + self._bSecondary(bSolution, srcList) - - def _bDeriv_u(self, src, v, adjoint=False): - """ - Derivative of the total magnetic flux density with respect to the thing we - solved for + 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 v: vector to take product with + :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 """ - return Identity()*v + + return Identity()*du_dm_v def _bDeriv_m(self, src, v, adjoint=False): """ - Derivative of the total magnetic flux density with respect to the inversion model. Here, we assume that the primary does not depend on the model. + 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 @@ -336,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 @@ -352,75 +558,16 @@ 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 + e[:,i] = e[:,i] + - S_e - def _eSecondaryDeriv_u(self, src, v, adjoint=False): + return self._MeSigmaI * e + + def _eDeriv_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 * v) ) - else: - return self._MfMui.T * (self._edgeCurl * (self._MeSigmaI.T * 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 - - def _e(self, bSolution, srcList): - """ - Total electric field is sum of primary and secondary - - :param numpy.ndarray eSolution: field we solved for - :param list srcList: list of sources - :rtype: numpy.ndarray - :return: total electric field - """ - - return self._ePrimary(bSolution, srcList) + self._eSecondary(bSolution, srcList) - - def _eDeriv_u(self, src, v, adjoint=False): - """ - 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 v: vector to take product with @@ -429,21 +576,121 @@ class Fields_b(Fields): :return: product of the derivative of the electric field with respect to the field we solved for with a vector """ - return self._eSecondaryDeriv_u(src, v, adjoint) + 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): """ - 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) + + + 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 Fields_j(Fields): @@ -462,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): @@ -470,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): """ @@ -485,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 @@ -515,10 +779,11 @@ class Fields_j(Fields): return self._jPrimary(jSolution, srcList) + self._jSecondary(jSolution, srcList) - def _jDeriv_u(self, src, v, adjoint=False): + + def _jDeriv_u(self, src, du_dm_v, adjoint=False): """ - Derivative of the total current density with respect to the thing we - solved for + 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 @@ -527,11 +792,12 @@ class Fields_j(Fields): :return: product of the derivative of the current density with respect to the field we solved for with a vector """ - return Identity()*v + return Identity()*du_dm_v + def _jDeriv_m(self, src, v, adjoint=False): """ - Derivative of the total current density with respect to the inversion model. Here, we assume that the primary does not depend on the model. + 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 @@ -568,101 +834,158 @@ 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 + h[:,i] = h[:,i] + 1./(1j*omega(src.freq)) * (S_m) + return self._MeMuI * h - def _hSecondaryDeriv_u(self, src, v, adjoint=False): + + def _hDeriv_u(self, src, du_dm_v, adjoint=False): """ - Derivative of the secondary 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 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 * v) ) - elif adjoint: - return -1./(1j*omega(src.freq)) * self._MfRho.T * (self._edgeCurl * ( self._MeMuI.T * v)) - - def _hSecondaryDeriv_m(self, src, v, adjoint=False): - """ - 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 - - - def _h(self, jSolution, srcList): - """ - Total magnetic field is sum of primary and secondary - - :param numpy.ndarray eSolution: field we solved for - :param list srcList: list of sources - :rtype: numpy.ndarray - :return: total magnetic field - """ - - return self._hPrimary(jSolution, srcList) + self._hSecondary(jSolution, srcList) - - def _hDeriv_u(self, src, v, adjoint=False): - """ - Derivative of the total magnetic field with respect to the thing we solved for - - :param SimPEG.EM.FDEM.Src src: source - :param numpy.ndarray v: vector to take product with + :param 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 """ - return self._hSecondaryDeriv_u(src, 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): """ - 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): @@ -680,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): @@ -689,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): """ @@ -720,35 +1062,24 @@ class Fields_h(Fields): return hSolution - def _h(self, hSolution, srcList): - """ - Total magnetic field is sum of primary and secondary - - :param numpy.ndarray hSolution: field we solved for - :param list srcList: list of sources - :rtype: numpy.ndarray - :return: total magnetic field - """ - return self._hPrimary(hSolution, srcList) + self._hSecondary(hSolution, srcList) - - def _hDeriv_u(self, src, v, adjoint=False): + def _hDeriv_u(self, src, du_dm_v, adjoint=False): """ - Derivative of the total magnetic field with respect to the thing we - solved for + 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 v: vector to take product with + :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 """ - return Identity()*v + return Identity()*du_dm_v def _hDeriv_m(self, src, v, adjoint=False): """ - Derivative of the total magnetic field with respect to the inversion model. Here, we assume that the primary does not depend on the model. + 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 @@ -778,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 @@ -792,70 +1123,124 @@ class Fields_h(Fields): j[:,i] = j[:,i]+ -S_e return j - def _jSecondaryDeriv_u(self, src, v, adjoint=False): + def _jDeriv_u(self, src, du_dm_v, adjoint=False): """ - Derivative of the secondary 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 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*v - elif adjoint: - return self._edgeCurl.T*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 _j(self, hSolution, srcList): - """ - Total current density is sum of primary and secondary - - :param numpy.ndarray eSolution: field we solved for - :param list srcList: list of sources - :rtype: numpy.ndarray - :return: total current density - """ - - return self._jPrimary(hSolution, srcList) + self._jSecondary(hSolution, srcList) - - def _jDeriv_u(self, src, v, adjoint=False): - """ - Derivative of the total current density with respect to the thing we solved for - - :param SimPEG.EM.FDEM.Src src: source - :param numpy.ndarray v: vector to take product with + :param 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 """ - return self._jSecondaryDeriv_u(src,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): """ - 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/SrcFDEM.py b/SimPEG/EM/FDEM/SrcFDEM.py index 1213cef3..31e4224f 100644 --- a/SimPEG/EM/FDEM/SrcFDEM.py +++ b/SimPEG/EM/FDEM/SrcFDEM.py @@ -1,7 +1,7 @@ from SimPEG import Survey, Problem, Utils, np, sp from scipy.constants import mu_0 from SimPEG.EM.Utils import * -from SimPEG.Utils import Zero +from SimPEG.Utils import Zero class BaseSrc(Survey.BaseSrc): """ @@ -14,7 +14,7 @@ class BaseSrc(Survey.BaseSrc): def eval(self, prob): """ - Evaluate the source terms. + Evaluate the source terms. - :math:`S_m` : magnetic source term - :math:`S_e` : electric source term @@ -36,12 +36,12 @@ class BaseSrc(Survey.BaseSrc): :param numpy.ndarray v: vector to take product with :param bool adjoint: adjoint? :rtype: (numpy.ndarray, numpy.ndarray) - :return: tuple with magnetic source term and electric source term derivatives times a vector + :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) + if v is not None: + return self.S_mDeriv(prob, v, adjoint), self.S_eDeriv(prob, v, adjoint) else: - return lambda v: self.S_mDeriv(prob,v,adjoint), lambda v: self.S_eDeriv(prob,v,adjoint) + return lambda v: self.S_mDeriv(prob, v, adjoint), lambda v: self.S_eDeriv(prob, v, adjoint) def bPrimary(self, prob): """ @@ -49,7 +49,7 @@ class BaseSrc(Survey.BaseSrc): :param Problem prob: FDEM Problem :rtype: numpy.ndarray - :return: primary magnetic flux density + :return: primary magnetic flux density """ return Zero() @@ -59,7 +59,7 @@ class BaseSrc(Survey.BaseSrc): :param Problem prob: FDEM Problem :rtype: numpy.ndarray - :return: primary magnetic field + :return: primary magnetic field """ return Zero() @@ -69,7 +69,7 @@ class BaseSrc(Survey.BaseSrc): :param Problem prob: FDEM Problem :rtype: numpy.ndarray - :return: primary electric field + :return: primary electric field """ return Zero() @@ -79,13 +79,13 @@ class BaseSrc(Survey.BaseSrc): :param Problem prob: FDEM Problem :rtype: numpy.ndarray - :return: primary current density + :return: primary current density """ return Zero() def S_m(self, prob): """ - Magnetic source term + Magnetic source term :param Problem prob: FDEM Problem :rtype: numpy.ndarray @@ -95,7 +95,7 @@ class BaseSrc(Survey.BaseSrc): def S_e(self, prob): """ - Electric source term + Electric source term :param Problem prob: FDEM Problem :rtype: numpy.ndarray @@ -136,15 +136,26 @@ class RawVec_e(BaseSrc): :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] """ - def __init__(self, rxList, freq, S_e): #, ePrimary=None, bPrimary=None, hPrimary=None, jPrimary=None): - self._S_e = np.array(S_e,dtype=complex) + 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) self.freq = float(freq) + self.integrate = integrate + BaseSrc.__init__(self, rxList) def S_e(self, prob): + """ + Electric source term + :param Problem prob: FDEM Problem + :rtype: numpy.ndarray + :return: electric source term on mesh + """ + if prob._formulation is 'EB' and self.integrate is True: + return prob.Me * self._S_e return self._S_e @@ -155,10 +166,11 @@ class RawVec_m(BaseSrc): :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] """ - 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 @@ -166,12 +178,14 @@ class RawVec_m(BaseSrc): def S_m(self, prob): """ - Magnetic source term + Magnetic source term :param Problem prob: FDEM Problem :rtype: numpy.ndarray :return: magnetic source term on mesh """ + if prob._formulation is 'HJ' and self.integrate is True: + return prob.Me * self._S_m return self._S_m @@ -183,36 +197,51 @@ class RawVec(BaseSrc): :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] """ - 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, integrate=True): + self._S_m = np.array(S_m, dtype=complex) + self._S_e = np.array(S_e, dtype=complex) self.freq = float(freq) self.integrate = integrate BaseSrc.__init__(self, rxList) def S_m(self, prob): - if prob._eqLocs is 'EF' and self.integrate is True: + """ + Magnetic source term + + :param Problem prob: FDEM Problem + :rtype: numpy.ndarray + :return: magnetic source term on mesh + """ + 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): - if prob._eqLocs is 'FE' and self.integrate is True: + """ + Electric source term + + :param Problem prob: FDEM Problem + :rtype: numpy.ndarray + :return: electric source term on mesh + """ + if prob._formulation is 'EB' and self.integrate is True: return prob.Me * self._S_e return self._S_e class MagDipole(BaseSrc): - """ + """ Point magnetic dipole source calculated by taking the curl of a magnetic vector potential. By taking the discrete curl, we ensure that the magnetic - flux density is divergence free (no magnetic monopoles!). + flux density is divergence free (no magnetic monopoles!). This approach uses a primary-secondary in frequency. Here we show the derivation for E-B formulation noting that similar steps are followed for the H-J formulation. - .. math:: + .. math:: \mathbf{C} \mathbf{e} + i \omega \mathbf{b} = \mathbf{s_m} \\\\ {\mathbf{C}^T \mathbf{M_{\mu^{-1}}^f} \mathbf{b} - \mathbf{M_{\sigma}^e} \mathbf{e} = \mathbf{s_e}} @@ -225,17 +254,17 @@ class MagDipole(BaseSrc): and define a zero-frequency primary problem, noting that the source is generated by a divergence free electric current - .. math:: + .. math:: \mathbf{C} \mathbf{e^P} = \mathbf{s_m^P} = 0 \\\\ {\mathbf{C}^T \mathbf{{M_{\mu^{-1}}^f}^P} \mathbf{b^P} - \mathbf{M_{\sigma}^e} \mathbf{e^P} = \mathbf{M^e} \mathbf{s_e^P}} - Since :math:`\mathbf{e^P}` is curl-free, divergence-free, we assume that there is no constant field background, the :math:`\mathbf{e^P} = 0`, so our primary problem is + Since :math:`\mathbf{e^P}` is curl-free, divergence-free, we assume that there is no constant field background, the :math:`\mathbf{e^P} = 0`, so our primary problem is - .. math:: + .. math:: \mathbf{e^P} = 0 \\\\ {\mathbf{C}^T \mathbf{{M_{\mu^{-1}}^f}^P} \mathbf{b^P} = \mathbf{s_e^P}} - Our secondary problem is then + Our secondary problem is then .. math:: \mathbf{C} \mathbf{e^S} + i \omega \mathbf{b^S} = - i \omega \mathbf{b^P} \\\\ @@ -245,15 +274,15 @@ class MagDipole(BaseSrc): :param float freq: frequency :param numpy.ndarray loc: source location (ie: :code:`np.r_[xloc,yloc,zloc]`) :param string orientation: 'X', 'Y', 'Z' - :param float moment: magnetic dipole moment - :param float mu: background magnetic permeability + :param float moment: magnetic dipole moment + :param float mu: background magnetic permeability """ - #TODO: right now, orientation doesn't actually do anything! The methods in SrcUtils should take care of that - 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): 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 @@ -265,17 +294,17 @@ class MagDipole(BaseSrc): :param Problem prob: FDEM problem :rtype: numpy.ndarray - :return: primary magnetic field + :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 @@ -303,10 +332,10 @@ class MagDipole(BaseSrc): :param Problem prob: FDEM problem :rtype: numpy.ndarray - :return: primary magnetic field + :return: primary magnetic field """ b = self.bPrimary(prob) - return h_from_b(prob,b) + return 1./self.mu * b def S_m(self, prob): """ @@ -314,10 +343,12 @@ class MagDipole(BaseSrc): :param Problem prob: FDEM problem :rtype: numpy.ndarray - :return: primary magnetic field + :return: primary magnetic field """ 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): @@ -326,21 +357,21 @@ class MagDipole(BaseSrc): :param Problem prob: FDEM problem :rtype: numpy.ndarray - :return: primary magnetic field + :return: primary magnetic field """ 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) + MMui_s = prob.mesh.getEdgeInnerProduct(mu_s, invMat=True) C = prob.mesh.edgeCurl.T return -C.T * (MMui_s * self.bPrimary(prob)) @@ -353,21 +384,20 @@ class MagDipole_Bfield(BaseSrc): fields from a magnetic dipole. No discrete curl is taken, so the magnetic flux density may not be strictly divergence free. - This approach uses a primary-secondary in frequency in the same fashion as the MagDipole. + This approach uses a primary-secondary in frequency in the same fashion as the MagDipole. :param list rxList: receiver list :param float freq: frequency :param numpy.ndarray loc: source location (ie: :code:`np.r_[xloc,yloc,zloc]`) :param string orientation: 'X', 'Y', 'Z' - :param float moment: magnetic dipole moment - :param float mu: background magnetic permeability + :param float moment: magnetic dipole moment + :param float mu: background magnetic permeability """ - #TODO: right now, orientation doesn't actually do anything! The methods in SrcUtils should take care of that - #TODO: neither does moment def __init__(self, rxList, freq, loc, orientation='Z', moment=1., mu = mu_0): self.freq = float(freq) self.loc = loc + assert orientation in ['X','Y','Z'], "Orientation (right now) doesn't actually do anything! The methods in SrcUtils should take care of this..." self.orientation = orientation self.moment = moment self.mu = mu @@ -379,18 +409,18 @@ class MagDipole_Bfield(BaseSrc): :param Problem prob: FDEM problem :rtype: numpy.ndarray - :return: primary magnetic field + :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 @@ -418,10 +448,10 @@ class MagDipole_Bfield(BaseSrc): :param Problem prob: FDEM problem :rtype: numpy.ndarray - :return: primary magnetic field + :return: primary magnetic field """ b = self.bPrimary(prob) - return h_from_b(prob, b) + return 1/self.mu * b def S_m(self, prob): """ @@ -429,9 +459,11 @@ class MagDipole_Bfield(BaseSrc): :param Problem prob: FDEM problem :rtype: numpy.ndarray - :return: primary magnetic field + :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): @@ -440,20 +472,20 @@ class MagDipole_Bfield(BaseSrc): :param Problem prob: FDEM problem :rtype: numpy.ndarray - :return: primary magnetic field + :return: primary magnetic field """ 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) + MMui_s = prob.mesh.getEdgeInnerProduct(mu_s, invMat=True) C = prob.mesh.edgeCurl.T return -C.T * (MMui_s * self.bPrimary(prob)) @@ -463,22 +495,22 @@ class CircularLoop(BaseSrc): """ Circular loop magnetic source calculated by taking the curl of a magnetic vector potential. By taking the discrete curl, we ensure that the magnetic - flux density is divergence free (no magnetic monopoles!). + flux density is divergence free (no magnetic monopoles!). - This approach uses a primary-secondary in frequency in the same fashion as the MagDipole. + This approach uses a primary-secondary in frequency in the same fashion as the MagDipole. :param list rxList: receiver list :param float freq: frequency :param numpy.ndarray loc: source location (ie: :code:`np.r_[xloc,yloc,zloc]`) :param string orientation: 'X', 'Y', 'Z' - :param float moment: magnetic dipole moment - :param float mu: background magnetic permeability + :param float moment: magnetic dipole moment + :param float mu: background magnetic permeability """ - #TODO: right now, orientation doesn't actually do anything! The methods in SrcUtils should take care of that - def __init__(self, rxList, freq, loc, orientation='Z', radius = 1., mu=mu_0): + def __init__(self, rxList, freq, loc, orientation='Z', radius=1., mu=mu_0): self.freq = float(freq) 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.radius = radius self.mu = mu self.loc = loc @@ -491,17 +523,17 @@ class CircularLoop(BaseSrc): :param Problem prob: FDEM problem :rtype: numpy.ndarray - :return: primary magnetic field + :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 @@ -528,7 +560,7 @@ class CircularLoop(BaseSrc): :param Problem prob: FDEM problem :rtype: numpy.ndarray - :return: primary magnetic field + :return: primary magnetic field """ b = self.bPrimary(prob) return 1./self.mu*b @@ -539,9 +571,11 @@ class CircularLoop(BaseSrc): :param Problem prob: FDEM problem :rtype: numpy.ndarray - :return: primary magnetic field + :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): @@ -550,22 +584,26 @@ class CircularLoop(BaseSrc): :param Problem prob: FDEM problem :rtype: numpy.ndarray - :return: primary magnetic field + :return: primary magnetic field """ 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) + MMui_s = prob.mesh.getEdgeInnerProduct(mu_s, invMat=True) C = prob.mesh.edgeCurl.T return -C.T * (MMui_s * self.bPrimary(prob)) + + diff --git a/SimPEG/EM/FDEM/SurveyFDEM.py b/SimPEG/EM/FDEM/SurveyFDEM.py index 444df88d..ce803ed1 100644 --- a/SimPEG/EM/FDEM/SurveyFDEM.py +++ b/SimPEG/EM/FDEM/SurveyFDEM.py @@ -3,6 +3,7 @@ from SimPEG.EM.Utils import * from scipy.constants import mu_0 from SimPEG.Utils import Zero, Identity import SrcFDEM as Src +from SimPEG import sp #################################################### @@ -18,33 +19,33 @@ class Rx(SimPEG.Survey.BaseRx): """ knownRxTypes = { - 'exr':['e', 'Ex', 'real'], - 'eyr':['e', 'Ey', 'real'], - 'ezr':['e', 'Ez', 'real'], - 'exi':['e', 'Ex', 'imag'], - 'eyi':['e', 'Ey', 'imag'], - 'ezi':['e', 'Ez', 'imag'], + 'exr':['e', 'x', 'real'], + 'eyr':['e', 'y', 'real'], + 'ezr':['e', 'z', 'real'], + 'exi':['e', 'x', 'imag'], + 'eyi':['e', 'y', 'imag'], + 'ezi':['e', 'z', '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'], + 'bxr':['b', 'x', 'real'], + 'byr':['b', 'y', 'real'], + 'bzr':['b', 'z', 'real'], + 'bxi':['b', 'x', 'imag'], + 'byi':['b', 'y', 'imag'], + 'bzi':['b', 'z', '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'], + 'jxr':['j', 'x', 'real'], + 'jyr':['j', 'y', 'real'], + 'jzr':['j', 'z', 'real'], + 'jxi':['j', 'x', 'imag'], + 'jyi':['j', 'y', 'imag'], + 'jzi':['j', 'z', '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'], + 'hxr':['h', 'x', 'real'], + 'hyr':['h', 'y', 'real'], + 'hzr':['h', 'z', 'real'], + 'hxi':['h', 'x', 'imag'], + 'hyi':['h', 'y', 'imag'], + 'hzi':['h', 'z', 'imag'], } radius = None @@ -56,34 +57,37 @@ class Rx(SimPEG.Survey.BaseRx): """Field Type projection (e.g. e b ...)""" return self.knownRxTypes[self.rxType][0] - @property - def projGLoc(self): - """Grid Location projection (e.g. Ex Fy ...)""" - return self.knownRxTypes[self.rxType][1] - @property def projComp(self): """Component projection (real/imag)""" return self.knownRxTypes[self.rxType][2] - def projectFields(self, src, mesh, u): + def projGLoc(self, u): + """Grid Location projection (e.g. Ex Fy ...)""" + return u._GLoc(self.rxType[0]) + self.knownRxTypes[self.rxType][1] + + def eval(self, src, mesh, u): """ Project fields to recievers to get data. :param Source src: FDEM source :param Mesh mesh: mesh used - :param Fields u: fields object + :param Fields f: fields object :rtype: numpy.ndarray :return: fields projected to recievers """ - P = self.getP(mesh) + # projGLoc = u._GLoc(self.knownRxTypes[self.rxType][0]) + # projGLoc += self.knownRxTypes[self.rxType][1] + + P = self.getP(mesh, self.projGLoc(u)) u_part_complex = u[src, self.projField] # get the real or imag component real_or_imag = self.projComp u_part = getattr(u_part_complex, real_or_imag) + return P*u_part - def projectFieldsDeriv(self, src, mesh, u, v, adjoint=False): + def evalDeriv(self, src, mesh, u, v, adjoint=False): """ Derivative of projected fields with respect to the inversion model times a vector. @@ -94,7 +98,8 @@ class Rx(SimPEG.Survey.BaseRx): :rtype: numpy.ndarray :return: fields projected to recievers """ - P = self.getP(mesh) + + P = self.getP(mesh, self.projGLoc(u)) if not adjoint: Pv_complex = P * v @@ -171,7 +176,7 @@ class Survey(SimPEG.Survey.BaseSurvey): assert freq in self._freqDict, "The requested frequency is not in this survey." return self._freqDict[freq] - def projectFields(self, u): + def eval(self, u): """ Project fields to receiver locations :param Fields u: fields object @@ -181,8 +186,9 @@ class Survey(SimPEG.Survey.BaseSurvey): data = SimPEG.Survey.Data(self) for src in self.srcList: for rx in src.rxList: - data[src, rx] = rx.projectFields(src, self.mesh, u) + data[src, rx] = rx.eval(src, self.mesh, u) return data - def projectFieldsDeriv(self, u): - raise Exception('Use Sources to project fields deriv.') + def evalDeriv(self, u): + raise Exception('Use Receivers to project fields deriv.') + diff --git a/SimPEG/EM/TDEM/BaseTDEM.py b/SimPEG/EM/TDEM/BaseTDEM.py index 2efb10ec..0da22072 100644 --- a/SimPEG/EM/TDEM/BaseTDEM.py +++ b/SimPEG/EM/TDEM/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) @@ -128,7 +129,7 @@ class BaseTDEMProblem(BaseTimeProblem, BaseEMProblem): u = self.fields(m) p = self.Gvec(m, v, u) y = self.solveAh(m, p) - Jv = self.survey.projectFieldsDeriv(u, v=y) + Jv = self.survey.evalDeriv(u, v=y) if self.verbose: print '%s\nDone calculating J(v)\n%s'%('*'*50,'*'*50) return - mkvc(Jv) @@ -155,7 +156,7 @@ class BaseTDEMProblem(BaseTimeProblem, BaseEMProblem): if not isinstance(v, self.dataPair): v = self.dataPair(self.survey, v) - p = self.survey.projectFieldsDeriv(u, v=v, adjoint=True) + p = self.survey.evalDeriv(u, v=v, adjoint=True) y = self.solveAht(m, p) w = self.Gtvec(m, y, u) if self.verbose: print '%s\nDone calculating J^T(v)\n%s'%('*'*50,'*'*50) diff --git a/SimPEG/EM/TDEM/SurveyTDEM.py b/SimPEG/EM/TDEM/SurveyTDEM.py index 45348af4..4d04f0ae 100644 --- a/SimPEG/EM/TDEM/SurveyTDEM.py +++ b/SimPEG/EM/TDEM/SurveyTDEM.py @@ -51,12 +51,12 @@ class RxTDEM(Survey.BaseTimeRx): else: return timeMesh.getInterpolationMat(self.times, self.projTLoc) - def projectFields(self, src, mesh, timeMesh, u): + def eval(self, src, mesh, timeMesh, u): P = self.getP(mesh, timeMesh) u_part = Utils.mkvc(u[src, self.projField, :]) return P*u_part - def projectFieldsDeriv(self, src, mesh, timeMesh, u, v, adjoint=False): + def evalDeriv(self, src, mesh, timeMesh, u, v, adjoint=False): P = self.getP(mesh, timeMesh) if not adjoint: @@ -168,27 +168,27 @@ class SurveyTDEM(Survey.BaseSurvey): self.srcList = srcList Survey.BaseSurvey.__init__(self, **kwargs) - def projectFields(self, u): + def eval(self, u): data = Survey.Data(self) for src in self.srcList: for rx in src.rxList: - data[src, rx] = rx.projectFields(src, self.mesh, self.prob.timeMesh, u) + data[src, rx] = rx.eval(src, self.mesh, self.prob.timeMesh, u) return data - def projectFieldsDeriv(self, u, v=None, adjoint=False): + def evalDeriv(self, u, v=None, adjoint=False): assert v is not None, 'v to multiply must be provided.' if not adjoint: data = Survey.Data(self) for src in self.srcList: for rx in src.rxList: - data[src, rx] = rx.projectFieldsDeriv(src, self.mesh, self.prob.timeMesh, u, v) + data[src, rx] = rx.evalDeriv(src, self.mesh, self.prob.timeMesh, u, v) return data else: f = FieldsTDEM(self.mesh, self) for src in self.srcList: for rx in src.rxList: - Ptv = rx.projectFieldsDeriv(src, self.mesh, self.prob.timeMesh, u, v, adjoint=True) + Ptv = rx.evalDeriv(src, self.mesh, self.prob.timeMesh, u, v, adjoint=True) Ptv = Ptv.reshape((-1, self.prob.timeMesh.nN), order='F') if rx.projField not in f: # first time we are projecting f[src, rx.projField, :] = Ptv 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..569f8e6d 100644 --- a/SimPEG/EM/Utils/testingUtils.py +++ b/SimPEG/EM/Utils/testingUtils.py @@ -4,19 +4,28 @@ 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.]) + 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 = EM.FDEM.Rx(XYZ, comp) Src = [] @@ -32,15 +41,15 @@ def getFDEMProblem(fdemType, comp, SrcList, freq, verbose=False): 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. + 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, 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. + 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, S_m, S_e)) if verbose: @@ -70,6 +79,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..2f198238 --- /dev/null +++ b/SimPEG/Examples/DC_Forward_PseudoSection.py @@ -0,0 +1,187 @@ +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', plotIt=True): + """ + DC Forward Simulation + ===================== + + Forward model conductive spheres in a half-space and plot a pseudo-section + + Created by @fourndo on Mon Feb 01 19:28:06 2016 + + """ + + assert stype in ['pdp', 'dpdp'], "Source type (stype) must be pdp or dpdp (pole dipole or dipole dipole)" + + + 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 = 125 + + # Specify the survey type: "pdp" | "dpdp" + + + # 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 differential operators needed for the DC problem + 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 + # [Tx2d, Rx2d] = DC.convertObs_DC3D_to_2D(survey, np.ones(survey.nSrc)) + survey2D = DC.convertObs_DC3D_to_2D(survey, np.ones(survey.nSrc)) + survey2D.dobs =np.hstack(data) + # Here is an example for the first tx-rx array + if plotIt: + import matplotlib.pyplot as plt + fig = plt.figure() + ax = plt.subplot(2,1,1, aspect='equal') + mesh.plotSlice(np.log10(model), ax =ax, normal = 'Y', ind = indy,grid=True) + ax.set_title('E-W section at '+str(mesh.vectorCCy[indy])+' m') + 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]) + + + ax = plt.subplot(2,1,2, aspect='equal') + + # Plot the location of the spheres for reference + circle1=plt.Circle((loc[0,0]-Tx[0][0,0],loc[2,0]),radi[0],color='w',fill=False, lw=3) + circle2=plt.Circle((loc[0,1]-Tx[0][0,0],loc[2,1]),radi[1],color='k',fill=False, lw=3) + ax.add_artist(circle1) + ax.add_artist(circle2) + + # Add the speudo section + DC.plot_pseudoSection(survey2D,ax,stype) + + # 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') + 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 ff87b6a6..e76b2439 100644 --- a/SimPEG/Examples/EM_FDEM_1D_Inversion.py +++ b/SimPEG/Examples/EM_FDEM_1D_Inversion.py @@ -21,8 +21,8 @@ def run(plotIt=True): active = mesh.vectorCCz<0. layer = (mesh.vectorCCz<0.) & (mesh.vectorCCz>=layerz) - actMap = Maps.ActiveCells(mesh, active, np.log(1e-8), nC=mesh.nCz) - mapping = Maps.ExpMap(mesh) * Maps.Vertical1DMap(mesh) * actMap + actMap = Maps.InjectActiveCells(mesh, active, np.log(1e-8), nC=mesh.nCz) + mapping = Maps.ExpMap(mesh) * Maps.SurjectVertical1D(mesh) * actMap sig_half = 2e-2 sig_air = 1e-8 sig_layer = 1e-2 diff --git a/SimPEG/Examples/EM_TDEM_1D_Inversion.py b/SimPEG/Examples/EM_TDEM_1D_Inversion.py index 65ae6669..629811ca 100644 --- a/SimPEG/Examples/EM_TDEM_1D_Inversion.py +++ b/SimPEG/Examples/EM_TDEM_1D_Inversion.py @@ -19,8 +19,8 @@ def run(plotIt=True): active = mesh.vectorCCz<0. layer = (mesh.vectorCCz<0.) & (mesh.vectorCCz>=-100.) - actMap = Maps.ActiveCells(mesh, active, np.log(1e-8), nC=mesh.nCz) - mapping = Maps.ExpMap(mesh) * Maps.Vertical1DMap(mesh) * actMap + actMap = Maps.InjectActiveCells(mesh, active, np.log(1e-8), nC=mesh.nCz) + mapping = Maps.ExpMap(mesh) * Maps.SurjectVertical1D(mesh) * actMap sig_half = 2e-3 sig_air = 1e-8 sig_layer = 1e-3 diff --git a/SimPEG/Examples/Inversion_Linear.py b/SimPEG/Examples/Inversion_Linear.py index a8a0eddc..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 projectFields(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_3D_Foward.py b/SimPEG/Examples/MT_3D_Foward.py index 8b1a0710..da16eeee 100644 --- a/SimPEG/Examples/MT_3D_Foward.py +++ b/SimPEG/Examples/MT_3D_Foward.py @@ -52,7 +52,7 @@ def run(plotIt=True, nFreq=1): # Calculate the data fields = problem.fields(sig) - dataVec = survey.projectFields(fields) + dataVec = survey.eval(fields) # Make the data mtData = MT.Data(survey,dataVec) diff --git a/SimPEG/Examples/__init__.py b/SimPEG/Examples/__init__.py index dd35c0da..cce22296 100644 --- a/SimPEG/Examples/__init__.py +++ b/SimPEG/Examples/__init__.py @@ -1,6 +1,8 @@ # 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_TDEM_1D_Inversion @@ -17,7 +19,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_TDEM_1D_Inversion", "FLOW_Richards_1D_Celia1990", "Forward_BasicDirectCurrent", "Inversion_Linear", "Mesh_Basic_PlotImage", "Mesh_Basic_Types", "Mesh_Operators_CahnHilliard", "Mesh_QuadTree_Creation", "Mesh_QuadTree_FaceDiv", "Mesh_QuadTree_HangingNodes", "Mesh_Tensor_Creation", "MT_1D_ForwardAndInversion", "MT_3D_Foward"] ##### AUTOIMPORTS ##### diff --git a/SimPEG/FLOW/Richards/RichardsProblem.py b/SimPEG/FLOW/Richards/RichardsProblem.py index 7c61c221..4dcabe60 100644 --- a/SimPEG/FLOW/Richards/RichardsProblem.py +++ b/SimPEG/FLOW/Richards/RichardsProblem.py @@ -8,7 +8,7 @@ class RichardsRx(Survey.BaseTimeRx): knownRxTypes = ['saturation','pressureHead'] - def projectFields(self, U, m, mapping, mesh, timeMesh): + def eval(self, U, m, mapping, mesh, timeMesh): if self.rxType == 'pressureHead': u = np.concatenate(U) @@ -17,7 +17,7 @@ class RichardsRx(Survey.BaseTimeRx): return self.getP(mesh, timeMesh) * u - def projectFieldsDeriv(self, U, m, mapping, mesh, timeMesh): + def evalDeriv(self, U, m, mapping, mesh, timeMesh): P = self.getP(mesh, timeMesh) if self.rxType == 'pressureHead': @@ -57,13 +57,13 @@ class RichardsSurvey(Survey.BaseSurvey): 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.projectFields(u, m)) + return Utils.mkvc(self.eval(u, m)) @Utils.requires('prob') - def projectFields(self, U, m): + def eval(self, U, m): Ds = range(len(self.rxList)) for ii, rx in enumerate(self.rxList): - Ds[ii] = rx.projectFields(U, m, + Ds[ii] = rx.eval(U, m, self.prob.mapping, self.prob.mesh, self.prob.timeMesh) @@ -71,11 +71,11 @@ class RichardsSurvey(Survey.BaseSurvey): return np.concatenate(Ds) @Utils.requires('prob') - def projectFieldsDeriv(self, U, m): + def evalDeriv(self, U, m): """The Derivative with respect to the fields.""" Ds = range(len(self.rxList)) for ii, rx in enumerate(self.rxList): - Ds[ii] = rx.projectFieldsDeriv(U, m, + Ds[ii] = rx.evalDeriv(U, m, self.prob.mapping, self.prob.mesh, self.prob.timeMesh) @@ -251,7 +251,7 @@ class RichardsProblem(Problem.BaseTimeProblem): B = np.array(sp.vstack(Bs).todense()) Ainv = self.Solver(A, **self.solverOpts) - P = self.survey.projectFieldsDeriv(u, m) + P = self.survey.evalDeriv(u, m) AinvB = Ainv * B z = np.zeros((self.mesh.nC, B.shape[1])) zAinvB = np.vstack((z, AinvB)) @@ -277,7 +277,7 @@ class RichardsProblem(Problem.BaseTimeProblem): Adiaginv = self.Solver(Adiag, **self.solverOpts) JvC[ii] = Adiaginv * (B*v - Asub*JvC[ii-1]) - P = self.survey.projectFieldsDeriv(u, m) + P = self.survey.evalDeriv(u, m) return P * np.concatenate([np.zeros(self.mesh.nC)] + JvC) @Utils.timeIt @@ -285,7 +285,7 @@ class RichardsProblem(Problem.BaseTimeProblem): if u is None: u = self.field(m) - P = self.survey.projectFieldsDeriv(u, m) + P = self.survey.evalDeriv(u, m) PTv = P.T*v # This is done via backward substitution. diff --git a/SimPEG/MT/BaseMT.py b/SimPEG/MT/BaseMT.py index 664bebcc..36389430 100644 --- a/SimPEG/MT/BaseMT.py +++ b/SimPEG/MT/BaseMT.py @@ -68,7 +68,7 @@ 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.projectFieldsDeriv(src, self.mesh, u, t) # wrt u, we don't have have PDeriv wrt m + PDeriv_u = lambda t: rx.evalDeriv(src, self.mesh, u, t) # wrt u, we don't have have PDeriv wrt m Jv[src, rx] = PDeriv_u(mkvc(du_dm)) dA_duI.clean() # Return the vectorized sensitivities @@ -106,9 +106,9 @@ class BaseMTProblem(BaseFDEMProblem): u_src = u[src, :] for rx in src.rxList: - # Get the adjoint projectFieldsDeriv + # Get the adjoint evalDeriv # PTv needs to be nE, - PTv = rx.projectFieldsDeriv(src, self.mesh, u, mkvc(v[src, rx],2), adjoint=True) # wrt u, need possibility wrt m + PTv = rx.evalDeriv(src, self.mesh, u, mkvc(v[src, rx],2), adjoint=True) # wrt u, need possibility wrt m # Get the dA_duIT = ATinv * PTv dA_dmT = self.getADeriv_m(freq, u_src, mkvc(dA_duIT), adjoint=True) diff --git a/SimPEG/MT/SurveyMT.py b/SimPEG/MT/SurveyMT.py index 91102d47..4e4a8688 100644 --- a/SimPEG/MT/SurveyMT.py +++ b/SimPEG/MT/SurveyMT.py @@ -59,7 +59,7 @@ class Rx(SimPEGsurvey.BaseRx): """Component projection (real/imag)""" return self.knownRxTypes[self.rxType][1] - def projectFields(self, src, mesh, f): + def eval(self, src, mesh, f): ''' Project the fields to natural source data. @@ -139,7 +139,7 @@ class Rx(SimPEGsurvey.BaseRx): # print f_part return f_part - def projectFieldsDeriv(self, src, mesh, f, v, adjoint=False): + def evalDeriv(self, src, mesh, f, v, adjoint=False): """ The derivative of the projection wrt u @@ -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 projectFields(self, u): + def eval(self, u): data = Data(self) for src in self.srcList: sys.stdout.flush() for rx in src.rxList: - data[src, rx] = rx.projectFields(src, self.mesh, u) + data[src, rx] = rx.eval(src, self.mesh, u) return data - def projectFieldsDeriv(self, u): + def evalDeriv(self, u): raise Exception('Use Transmitters to project fields deriv.') ################# diff --git a/SimPEG/Maps.py b/SimPEG/Maps.py index a3c76e6a..5f71d87c 100644 --- a/SimPEG/Maps.py +++ b/SimPEG/Maps.py @@ -4,6 +4,7 @@ from Tests import checkDerivative from PropMaps import PropMap, Property from numpy.polynomial import polynomial from scipy.interpolate import UnivariateSpline +import warnings class IdentityMap(object): """ @@ -296,11 +297,11 @@ class LogMap(IdentityMap): def inverse(self, m): return np.exp(Utils.mkvc(m)) -class FullMap(IdentityMap): +class SurjectFull(IdentityMap): """ - FullMap + SurjectFull - Given a scalar, the FullMap maps the value to the + Given a scalar, the SurjectFull maps the value to the full model space. """ @@ -327,9 +328,15 @@ class FullMap(IdentityMap): """ return np.ones([self.mesh.nC,1]) +class FullMap(SurjectFull): + def __init__(self,mesh,**kwargs): + warnings.warn( + "`FullMap` is deprecated and will be removed in future versions. Use `SurjectFull` instead", + FutureWarning) + SurjectFull.__init__(self,mesh,**kwargs) -class Vertical1DMap(IdentityMap): - """Vertical1DMap +class SurjectVertical1D(IdentityMap): + """SurjectVertical1DMap Given a 1D vector through the last dimension of the mesh, this will extend to the full @@ -369,8 +376,14 @@ class Vertical1DMap(IdentityMap): ), shape=(repNum, 1)) return sp.kron(sp.identity(self.nP), repVec) +class Vertical1DMap(SurjectVertical1D): + def __init__(self,mesh,**kwargs): + warnings.warn( + "`Vertical1DMap` is deprecated and will be removed in future versions. Use `SurjectVertical1D` instead", + FutureWarning) + SurjectVertical1D.__init__(self,mesh,**kwargs) -class Map2Dto3D(IdentityMap): +class Surject2Dto3D(IdentityMap): """Map2Dto3D Given a 2D vector, this will extend to the full @@ -425,6 +438,13 @@ class Map2Dto3D(IdentityMap): ), shape=(nC, nP)) return P +class Map2Dto3D(Surject2Dto3D): + def __init__(self,mesh,**kwargs): + warnings.warn( + "`Map2Dto3D` is deprecated and will be removed in future versions. Use `Surject2Dto3D` instead", + FutureWarning) + Surject2Dto3D.__init__(self,mesh,**kwargs) + class Mesh2Mesh(IdentityMap): """ Takes a model on one mesh are translates it to another mesh. @@ -458,7 +478,7 @@ class Mesh2Mesh(IdentityMap): return self.P -class ActiveCells(IdentityMap): +class InjectActiveCells(IdentityMap): """ Active model parameters. @@ -506,7 +526,14 @@ class ActiveCells(IdentityMap): def deriv(self, m): return self.P -class ActiveCellsTopo(IdentityMap): +class ActiveCells(InjectActiveCells): + def __init__(self, mesh, indActive, valInactive, nC=None): + warnings.warn( + "`ActiveCells` is deprecated and will be removed in future versions. Use `InjectActiveCells` instead", + FutureWarning) + InjectActiveCells.__init__(self, mesh, indActive, valInactive, nC) + +class InjectActiveCellsTopo(IdentityMap): """ Active model parameters. Extend for cells on topography to air cell (only works for tensor mesh) @@ -577,6 +604,12 @@ class ActiveCellsTopo(IdentityMap): def deriv(self, m): return self.P +class ActiveCellsTopo(InjectActiveCellsTopo): + def __init__(self, mesh, indActive, valInactive, nC=None): + warnings.warn( + "`ActiveCellsTopo` is deprecated and will be removed in future versions. Use `InjectActiveCellsTopo` instead", + FutureWarning) + InjectActiveCellsTopo.__init__(self, mesh, indActive, valInactive, nC) class Weighting(IdentityMap): """ diff --git a/SimPEG/Mesh/DiffOperators.py b/SimPEG/Mesh/DiffOperators.py index 00bf0259..793b2581 100644 --- a/SimPEG/Mesh/DiffOperators.py +++ b/SimPEG/Mesh/DiffOperators.py @@ -746,4 +746,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/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/Problem.py b/SimPEG/Problem.py index f24b57de..cd8a4aaa 100644 --- a/SimPEG/Problem.py +++ b/SimPEG/Problem.py @@ -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, u=None): + return self.G.dot(v) + + def Jtvec(self, m, v, u=None): + return self.G.T.dot(v) diff --git a/SimPEG/Regularization.py b/SimPEG/Regularization.py index 677fba5c..5b41b91b 100644 --- a/SimPEG/Regularization.py +++ b/SimPEG/Regularization.py @@ -282,3 +282,243 @@ class Tikhonov(BaseRegularization): out = mD.T * ( self.W.T * r ) return out +# <<<<<<< HEAD + +# class Simple(BaseRegularization): +# """ +# Only for tensor mesh +# """ + +# smoothModel = True #: SMOOTH and SMOOTH_MOD_DIF options +# alpha_s = Utils.dependentProperty('_alpha_s', 1.0, ['_W', '_Ws'], "Smallness weight") +# alpha_x = Utils.dependentProperty('_alpha_x', 1.0, ['_W', '_Wx'], "Weight for the first derivative in the x direction") +# alpha_y = Utils.dependentProperty('_alpha_y', 1.0, ['_W', '_Wy'], "Weight for the first derivative in the y direction") +# alpha_z = Utils.dependentProperty('_alpha_z', 1.0, ['_W', '_Wz'], "Weight for the first derivative in the z direction") +# 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, **kwargs): +# BaseRegularization.__init__(self, mesh, mapping=mapping, **kwargs) + + + +# @property +# def Ws(self): +# """Regularization matrix Ws""" +# if getattr(self,'_Ws', None) is None: +# self._Ws = Utils.sdiag((self.mesh.vol*self.alpha_s)**0.5) +# return self._Ws + +# @property +# def Wx(self): +# """Regularization matrix Wx""" +# if getattr(self, '_Wx', None) is None: +# self._Wx = Utils.sdiag((self.mesh.vol*self.alpha_x)**0.5)*self.mesh.unitCellGradx +# return self._Wx + +# @property +# def Wy(self): +# """Regularization matrix Wy""" +# if getattr(self, '_Wy', None) is None: +# self._Wy = Utils.sdiag((self.mesh.vol*self.alpha_y)**0.5)*self.mesh.unitCellGrady +# return self._Wy + +# @property +# def Wz(self): +# """Regularization matrix Wz""" +# if getattr(self, '_Wz', None) is None: +# self._Wz = Utils.sdiag((self.mesh.vol*self.alpha_z)**0.5)*self.mesh.unitCellGradz +# return self._Wz + +# @property +# def 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 +# 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 +# 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 +# return self._Wzz + +# @property +# def Wsmooth(self): +# """Full smoothness regularization matrix W""" +# if getattr(self, '_Wsmooth', None) is None: +# wlist = (self.Wx, self.Wxx) +# if self.mesh.dim > 1: +# wlist += (self.Wy, self.Wyy) +# if self.mesh.dim > 2: +# wlist += (self.Wz, self.Wzz) +# self._Wsmooth = sp.vstack(wlist) +# return self._Wsmooth + +# @property +# def W(self): +# """Full regularization matrix W""" +# if getattr(self, '_W', None) is None: +# wlist = (self.Ws, self.Wsmooth) +# self._W = sp.vstack(wlist) +# return self._W + +# @Utils.timeIt +# def eval(self, m): +# if self.smoothModel == True: +# r1 = self.Wsmooth * ( self.mapping * (m) ) +# r2 = self.Ws * ( self.mapping * (m - self.mref) ) +# return 0.5*(r1.dot(r1)+r2.dot(r2)) +# elif self.smoothModel == False: +# r = self.W * ( self.mapping * (m - self.mref) ) +# return 0.5*r.dot(r) + + +# @Utils.timeIt +# def evalDeriv(self, m): +# """ + +# The regularization is: + +# .. math:: + +# R(m) = \\frac{1}{2}\mathbf{(m-m_\\text{ref})^\\top W^\\top W(m-m_\\text{ref})} + +# So the derivative is straight forward: + +# .. math:: + +# R(m) = \mathbf{W^\\top W (m-m_\\text{ref})} + +# """ +# if self.smoothModel == True: +# mD1 = self.mapping.deriv(m) +# mD2 = self.mapping.deriv(m - self.mref) +# r1 = self.Wsmooth * ( self.mapping * (m)) +# r2 = self.Ws * ( self.mapping * (m - self.mref) ) +# out1 = mD1.T * ( self.Wsmooth.T * r1 ) +# out2 = mD2.T * ( self.Ws.T * r2 ) +# out = out1+out2 +# elif self.smoothModel == False: +# mD = self.mapping.deriv(m - self.mref) +# r = self.W * ( self.mapping * (m - self.mref) ) +# out = mD.T * ( self.W.T * r ) +# return out + +# class SparseRegularization(Simple): + +# eps = 1e-1 + +# m = None +# gamma = 1. +# p = 0. +# qx = 2. +# qy = 2. +# qz = 2. + +# def __init__(self, mesh, mapping=None, **kwargs): +# Simple.__init__(self, mesh, mapping=mapping, **kwargs) + + +# @property +# def Wsmooth(self): +# """Full smoothness regularization matrix W""" +# if getattr(self, '_Wsmooth', None) is None: +# wlist = (self.Wx, self.Wxx) +# if self.mesh.dim > 1: +# wlist += (self.Wy, self.Wyy) +# if self.mesh.dim > 2: +# wlist += (self.Wz, self.Wzz) +# self._Wsmooth = sp.vstack(wlist) +# return self._Wsmooth + +# @property +# def W(self): +# """Full regularization matrix W""" +# if getattr(self, '_W', None) is None: +# wlist = (self.Ws, self.Wsmooth) +# self._W = sp.vstack(wlist) +# return self._W + +# @property +# def Ws(self): +# """Regularization matrix Ws""" +# if getattr(self, 'm', None) is None: +# self.Rs = Utils.speye(self.mesh.nC) + +# else: +# f_m = self.m +# self.rs = self.R(f_m , self.p, self.eps) +# #print "Min rs: " + str(np.max(self.rs)) + "Max rs: " + str(np.min(self.rs)) +# self.Rs = Utils.sdiag( self.rs ) + +# self._Ws = Utils.sdiag((self.mesh.vol*self.alpha_s*self.gamma)**0.5)*self.Rs + +# return self._Ws + +# @property +# def Wx(self): +# """Regularization matrix Wx""" + +# if getattr(self, 'm', None) is None: +# self.Rx = Utils.speye(self.mesh.unitCellGradx.shape[0]) + +# else: +# f_m = self.mesh.unitCellGradx * self.m +# self.rx = self.R( f_m , self.qx, self.eps) +# self.Rx = Utils.sdiag( self.rx ) + +# if getattr(self, '_Wx', None) is None: +# self._Wx = Utils.sdiag((self.mesh.vol*self.alpha_x*self.gamma)**0.5)*self.Rx*self.mesh.unitCellGradx +# return self._Wx + +# @property +# def Wy(self): +# """Regularization matrix Wy""" + +# if getattr(self, 'm', None) is None: +# self.Ry = Utils.speye(self.mesh.unitCellGrady.shape[0]) + +# else: +# f_m = self.mesh.unitCellGrady * self.m +# self.ry = self.R( f_m , self.qy, self.eps) +# self.Ry = Utils.sdiag( self.ry ) + +# if getattr(self, '_Wy', None) is None: +# self._Wy = Utils.sdiag((self.mesh.vol*self.alpha_y*self.gamma)**0.5)*self.Ry*self.mesh.unitCellGrady +# return self._Wy + +# @property +# def Wz(self): +# """Regularization matrix Wz""" + +# if getattr(self, 'm', None) is None: +# self.Rz = Utils.speye(self.mesh.unitCellGradz.shape[0]) + +# else: +# f_m = self.mesh.unitCellGradz * self.m +# self.rz = self.R( f_m , self.qz, self.eps) +# self.Rz = Utils.sdiag( self.rz ) + +# if getattr(self, '_Wz', None) is None: +# self._Wz = Utils.sdiag((self.mesh.vol*self.alpha_z*self.gamma)**0.5)*self.Rz*self.mesh.unitCellGradz +# return self._Wz + + +# def R(self, f_m , p, dec): + +# eta = (self.eps**(1-p/2.))**0.5 +# r = eta / (f_m**2.+self.eps**2.)**((1-p/2.)/2.) + +# return r +# ======= +# >>>>>>> 834de582844e8e1eac95819fbe03eed55dbeb001 diff --git a/SimPEG/Survey.py b/SimPEG/Survey.py index 47a88ae2..37024028 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 @@ -307,12 +309,12 @@ class BaseSurvey(object): 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.projectFields(u)) + return Utils.mkvc(self.eval(u)) @Utils.count - def projectFields(self, u): - """projectFields(u) + def eval(self, u): + """eval(u) This function projects the fields onto the data space. @@ -320,11 +322,11 @@ class BaseSurvey(object): d_\\text{pred} = \mathbf{P} u(m) """ - raise NotImplemented('projectFields is not yet implemented.') + raise NotImplemented('eval is not yet implemented.') @Utils.count - def projectFieldsDeriv(self, u): - """projectFieldsDeriv(u) + def evalDeriv(self, u): + """evalDeriv(u) This function s the derivative of projects the fields onto the data space. @@ -332,7 +334,7 @@ class BaseSurvey(object): \\frac{\partial d_\\text{pred}}{\partial u} = \mathbf{P} """ - raise NotImplemented('projectFields is not yet implemented.') + raise NotImplemented('eval is not yet implemented.') @Utils.count def residual(self, m, u=None): @@ -375,3 +377,11 @@ class BaseSurvey(object): self.dobs = self.dtrue+noise self.std = self.dobs*0 + std return self.dobs + +class LinearSurvey(BaseSurvey): + def eval(self, u): + return u + + @property + def nD(self): + return self.prob.G.shape[0] diff --git a/SimPEG/Utils/ModelBuilder.py b/SimPEG/Utils/ModelBuilder.py index 52d13820..435856f7 100644 --- a/SimPEG/Utils/ModelBuilder.py +++ b/SimPEG/Utils/ModelBuilder.py @@ -118,6 +118,44 @@ 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 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=[0,1]): """ Define a two layered model. Depth of the first layer must be specified. 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/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..1a500cae --- /dev/null +++ b/docs/examples/DC_Forward_PseudoSection.rst @@ -0,0 +1,28 @@ +.. _examples_DC_Forward_PseudoSection: + +.. --------------------------------- .. +.. .. +.. THIS FILE IS AUTO GENEREATED .. +.. .. +.. SimPEG/Examples/__init__.py .. +.. .. +.. --------------------------------- .. + + +DC Forward Simulation +===================== + +Forward model conductive spheres in a half-space and plot a pseudo-section + +Created by @fourndo on Mon Feb 01 19:28:06 2016 + + + +.. plot:: + + from SimPEG import Examples + Examples.DC_Forward_PseudoSection.run() + +.. literalinclude:: ../../SimPEG/Examples/DC_Forward_PseudoSection.py + :language: python + :linenos: diff --git a/tests/base/test_maps.py b/tests/base/test_maps.py index 623f8715..19cd4e77 100644 --- a/tests/base/test_maps.py +++ b/tests/base/test_maps.py @@ -5,8 +5,8 @@ from scipy.sparse.linalg import dsolve TOL = 1e-14 -MAPS_TO_TEST_2D = ["CircleMap", "ComplexMap", "ExpMap", "IdentityMap", "Vertical1DMap", "Weighting", "FullMap"] -MAPS_TO_TEST_3D = [ "ComplexMap", "ExpMap", "IdentityMap", "Vertical1DMap", "Weighting", "FullMap"] +MAPS_TO_TEST_2D = ["CircleMap", "ComplexMap", "ExpMap", "IdentityMap", "SurjectVertical1D", "Weighting", "SurjectFull","FullMap","Vertical1DMap"] +MAPS_TO_TEST_3D = [ "ComplexMap", "ExpMap", "IdentityMap", "SurjectVertical1D", "Weighting", "SurjectFull","FullMap","Vertical1DMap"] class MapTests(unittest.TestCase): @@ -52,7 +52,7 @@ class MapTests(unittest.TestCase): def test_mapMultiplication(self): M = Mesh.TensorMesh([2,3]) expMap = Maps.ExpMap(M) - vertMap = Maps.Vertical1DMap(M) + vertMap = Maps.SurjectVertical1D(M) combo = expMap*vertMap m = np.arange(3.0) t_true = np.exp(np.r_[0,0,1,1,2,2.]) @@ -83,22 +83,23 @@ class MapTests(unittest.TestCase): def test_activeCells(self): M = Mesh.TensorMesh([2,4],'0C') expMap = Maps.ExpMap(M) - actMap = Maps.ActiveCells(M, M.vectorCCy <=0, 10, nC=M.nCy) - vertMap = Maps.Vertical1DMap(M) - combo = vertMap * actMap - m = np.r_[1,2.] - mod = Models.Model(m,combo) - # import matplotlib.pyplot as plt - # plt.colorbar(M.plotImage(mod.transform)[0]) - # plt.show() - self.assertLess(np.linalg.norm(mod.transform - np.r_[1,1,2,2,10,10,10,10.]), TOL) - self.assertLess((mod.transformDeriv - combo.deriv(m)).toarray().sum(), TOL) + for actMap in [Maps.InjectActiveCells(M, M.vectorCCy <=0, 10, nC=M.nCy), Maps.ActiveCells(M, M.vectorCCy <=0, 10, nC=M.nCy)]: + # actMap = Maps.InjectActiveCells(M, M.vectorCCy <=0, 10, nC=M.nCy) + vertMap = Maps.SurjectVertical1D(M) + combo = vertMap * actMap + m = np.r_[1,2.] + mod = Models.Model(m,combo) + # import matplotlib.pyplot as plt + # plt.colorbar(M.plotImage(mod.transform)[0]) + # plt.show() + self.assertLess(np.linalg.norm(mod.transform - np.r_[1,1,2,2,10,10,10,10.]), TOL) + self.assertLess((mod.transformDeriv - combo.deriv(m)).toarray().sum(), TOL) def test_tripleMultiply(self): M = Mesh.TensorMesh([2,4],'0C') expMap = Maps.ExpMap(M) - vertMap = Maps.Vertical1DMap(M) - actMap = Maps.ActiveCells(M, M.vectorCCy <=0, 10, nC=M.nCy) + vertMap = Maps.SurjectVertical1D(M) + actMap = Maps.InjectActiveCells(M, M.vectorCCy <=0, 10, nC=M.nCy) m = np.r_[1,2.] t_true = np.exp(np.r_[1,1,2,2,10,10,10,10.]) self.assertLess(np.linalg.norm((expMap * vertMap * actMap * m)-t_true,np.inf),TOL) @@ -115,29 +116,33 @@ class MapTests(unittest.TestCase): M2 = Mesh.TensorMesh([2,4]) M3 = Mesh.TensorMesh([3,2,4]) m = np.random.rand(M2.nC) - m2to3 = Maps.Map2Dto3D(M3, normal='X') - m = np.arange(m2to3.nP) - self.assertTrue(m2to3.test()) - self.assertTrue(np.all(Utils.mkvc( (m2to3 * m).reshape(M3.vnC,order='F')[0,:,:] ) == m)) + + for m2to3 in [Maps.Surject2Dto3D(M3, normal='X'), Maps.Map2Dto3D(M3, normal='X')]: + # m2to3 = Maps.Surject2Dto3D(M3, normal='X') + m = np.arange(m2to3.nP) + self.assertTrue(m2to3.test()) + self.assertTrue(np.all(Utils.mkvc( (m2to3 * m).reshape(M3.vnC,order='F')[0,:,:] ) == m)) def test_map2Dto3D_y(self): M2 = Mesh.TensorMesh([3,4]) M3 = Mesh.TensorMesh([3,2,4]) m = np.random.rand(M2.nC) - m2to3 = Maps.Map2Dto3D(M3, normal='Y') - m = np.arange(m2to3.nP) - self.assertTrue(m2to3.test()) - self.assertTrue(np.all(Utils.mkvc( (m2to3 * m).reshape(M3.vnC,order='F')[:,0,:] ) == m)) + for m2to3 in [Maps.Surject2Dto3D(M3, normal='Y'),Maps.Map2Dto3D(M3, normal='Y')]: + # m2to3 = Maps.Surject2Dto3D(M3, normal='Y') + m = np.arange(m2to3.nP) + self.assertTrue(m2to3.test()) + self.assertTrue(np.all(Utils.mkvc( (m2to3 * m).reshape(M3.vnC,order='F')[:,0,:] ) == m)) def test_map2Dto3D_z(self): M2 = Mesh.TensorMesh([3,2]) M3 = Mesh.TensorMesh([3,2,4]) m = np.random.rand(M2.nC) - m2to3 = Maps.Map2Dto3D(M3, normal='Z') - m = np.arange(m2to3.nP) - self.assertTrue(m2to3.test()) - self.assertTrue(np.all(Utils.mkvc( (m2to3 * m).reshape(M3.vnC,order='F')[:,:,0] ) == m)) + for m2to3 in [Maps.Surject2Dto3D(M3, normal='Z'),Maps.Map2Dto3D(M3, normal='Z')]: + # m2to3 = Maps.Surject2Dto3D(M3, normal='Z') + m = np.arange(m2to3.nP) + self.assertTrue(m2to3.test()) + self.assertTrue(np.all(Utils.mkvc( (m2to3 * m).reshape(M3.vnC,order='F')[:,:,0] ) == m)) 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_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..545a5014 --- /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() \ No newline at end of file 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_b_DerivAdjoint.py b/tests/em/tdem/test_TDEM_b_DerivAdjoint.py index 570c808b..30630d8d 100644 --- a/tests/em/tdem/test_TDEM_b_DerivAdjoint.py +++ b/tests/em/tdem/test_TDEM_b_DerivAdjoint.py @@ -18,8 +18,8 @@ class TDEM_bDerivTests(unittest.TestCase): mesh = Mesh.CylMesh([hx,1,hy], '00C') active = mesh.vectorCCz<0. - activeMap = Maps.ActiveCells(mesh, active, np.log(1e-8), nC=mesh.nCz) - mapping = Maps.ExpMap(mesh) * Maps.Vertical1DMap(mesh) * activeMap + activeMap = Maps.InjectActiveCells(mesh, active, np.log(1e-8), nC=mesh.nCz) + mapping = Maps.ExpMap(mesh) * Maps.SurjectVertical1D(mesh) * activeMap rxOffset = 40. rx = EM.TDEM.RxTDEM(np.array([[rxOffset, 0., 0.]]), np.logspace(-4,-3, 20), 'bz') @@ -204,8 +204,8 @@ class TDEM_bDerivTests(unittest.TestCase): d = Survey.Data(survey,v=d_vec) # Check that d.T*Q*f = f.T*Q.T*d - V1 = d_vec.dot(survey.projectFieldsDeriv(None, v=f).tovec()) - V2 = f.tovec().dot(survey.projectFieldsDeriv(None, v=d, adjoint=True).tovec()) + V1 = d_vec.dot(survey.evalDeriv(None, v=f).tovec()) + V2 = f.tovec().dot(survey.evalDeriv(None, v=d, adjoint=True).tovec()) self.assertTrue((V1-V2)/np.abs(V1) < tol) diff --git a/tests/em/tdem/test_TDEM_b_MultiSrc_DerivAdjoint.py b/tests/em/tdem/test_TDEM_b_MultiSrc_DerivAdjoint.py index de261a10..8f3ebcc1 100644 --- a/tests/em/tdem/test_TDEM_b_MultiSrc_DerivAdjoint.py +++ b/tests/em/tdem/test_TDEM_b_MultiSrc_DerivAdjoint.py @@ -17,8 +17,8 @@ class TDEM_bDerivTests(unittest.TestCase): mesh = Mesh.CylMesh([hx,1,hy], '00C') active = mesh.vectorCCz<0. - activeMap = Maps.ActiveCells(mesh, active, np.log(1e-8), nC=mesh.nCz) - mapping = Maps.ExpMap(mesh) * Maps.Vertical1DMap(mesh) * activeMap + activeMap = Maps.InjectActiveCells(mesh, active, np.log(1e-8), nC=mesh.nCz) + mapping = Maps.ExpMap(mesh) * Maps.SurjectVertical1D(mesh) * activeMap rxOffset = 40. rx = EM.TDEM.RxTDEM(np.array([[rxOffset, 0., 0.]]), np.logspace(-4,-3, 20), 'bz') @@ -108,8 +108,8 @@ class TDEM_bDerivTests(unittest.TestCase): d = Survey.Data(survey,v=d_vec) # Check that d.T*Q*f = f.T*Q.T*d - V1 = d_vec.dot(survey.projectFieldsDeriv(None, v=f).tovec()) - V2 = np.sum((f.tovec())*(survey.projectFieldsDeriv(None, v=d, adjoint=True).tovec())) + V1 = d_vec.dot(survey.evalDeriv(None, v=f).tovec()) + V2 = np.sum((f.tovec())*(survey.evalDeriv(None, v=d, adjoint=True).tovec())) self.assertTrue((V1-V2)/np.abs(V1) < 1e-6) diff --git a/tests/em/tdem/test_TDEM_combos.py b/tests/em/tdem/test_TDEM_combos.py index 1f538c3c..926cdf23 100644 --- a/tests/em/tdem/test_TDEM_combos.py +++ b/tests/em/tdem/test_TDEM_combos.py @@ -14,8 +14,8 @@ def getProb(meshType='CYL',rxTypes='bx,bz',nSrc=1): mesh = Mesh.CylMesh([hx,1,hy], '00C') active = mesh.vectorCCz<0. - activeMap = Maps.ActiveCells(mesh, active, np.log(1e-8), nC=mesh.nCz) - mapping = Maps.ExpMap(mesh) * Maps.Vertical1DMap(mesh) * activeMap + activeMap = Maps.InjectActiveCells(mesh, active, np.log(1e-8), nC=mesh.nCz) + mapping = Maps.ExpMap(mesh) * Maps.SurjectVertical1D(mesh) * activeMap rxOffset = 40. diff --git a/tests/em/tdem/test_TDEM_forward_Analytic.py b/tests/em/tdem/test_TDEM_forward_Analytic.py index 06890541..dc3696ef 100644 --- a/tests/em/tdem/test_TDEM_forward_Analytic.py +++ b/tests/em/tdem/test_TDEM_forward_Analytic.py @@ -24,8 +24,8 @@ def halfSpaceProblemAnaDiff(meshType, sig_half=1e-2, rxOffset=50., bounds=[1e-5, mesh = Mesh.TensorMesh([hx,hy,hz], 'CCC') active = mesh.vectorCCz<0. - actMap = Maps.ActiveCells(mesh, active, np.log(1e-8), nC=mesh.nCz) - mapping = Maps.ExpMap(mesh) * Maps.Vertical1DMap(mesh) * actMap + actMap = Maps.InjectActiveCells(mesh, active, np.log(1e-8), nC=mesh.nCz) + mapping = Maps.ExpMap(mesh) * Maps.SurjectVertical1D(mesh) * actMap rx = EM.TDEM.RxTDEM(np.array([[rxOffset, 0., 0.]]), np.logspace(-5,-4, 21), 'bz') src = EM.TDEM.SrcTDEM_VMD_MVP([rx], loc=np.array([0., 0., 0.])) diff --git a/tests/mt/test_Problem1D_againstAnalyticHalfspace.py b/tests/mt/test_Problem1D_againstAnalyticHalfspace.py index 01373395..b023476e 100644 --- a/tests/mt/test_Problem1D_againstAnalyticHalfspace.py +++ b/tests/mt/test_Problem1D_againstAnalyticHalfspace.py @@ -70,7 +70,7 @@ def appRes_TotalFieldNorm(sigmaHalf): fields = problem.fields(sigma) # Project the data - data = survey.projectFields(fields) + data = survey.eval(fields) # Calculate the app res and phs app_r = np.array(getAppResPhs(data))[:,0] @@ -88,7 +88,7 @@ def appPhs_TotalFieldNorm(sigmaHalf): fields = problem.fields(sigma) # Project the data - data = survey.projectFields(fields) + data = survey.eval(fields) # Calculate the app phs app_p = np.array(getAppResPhs(data))[:,1] @@ -106,7 +106,7 @@ def appRes_psFieldNorm(sigmaHalf): fields = problem.fields(sigma) # Project the data - data = survey.projectFields(fields) + data = survey.eval(fields) # Calculate the app res and phs app_r = np.array(getAppResPhs(data))[:,0] @@ -124,7 +124,7 @@ def appPhs_psFieldNorm(sigmaHalf): fields = problem.fields(sigma) # Project the data - data = survey.projectFields(fields) + data = survey.eval(fields) # Calculate the app phs app_p = np.array(getAppResPhs(data))[:,1] diff --git a/tests/mt/test_Problem3D_againstAnalytic.py b/tests/mt/test_Problem3D_againstAnalytic.py index 3eea95ca..f68e515f 100644 --- a/tests/mt/test_Problem3D_againstAnalytic.py +++ b/tests/mt/test_Problem3D_againstAnalytic.py @@ -210,7 +210,7 @@ def DerivProjfieldsTest(inputSetup,comp='All',freq=False): f = problem.fieldsPair(survey.mesh,survey) f[src,'e_pxSolution'] = u[:len(u)/2] f[src,'e_pySolution'] = u[len(u)/2::] - return rx.projectFields(src,survey.mesh,f), lambda t: rx.projectFieldsDeriv(src,survey.mesh,f0,simpeg.mkvc(t,2)) + return rx.eval(src,survey.mesh,f), lambda t: rx.evalDeriv(src,survey.mesh,f0,simpeg.mkvc(t,2)) return simpeg.Tests.checkDerivative(fun, u0, num=3, plotIt=False, eps=FLR)