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.ndarray m: model :rtype: scipy.sparse.csc_matrix :return: A(m) .. math:: c(m,u) = A(m)u - q = G\\text{sdiag}(M(mT(m)))Du - q = 0 Where M() is the mass matrix and mT is the model transform. """ if getattr(self, '_A', None) is None: D = self.mesh.faceDiv G = self.mesh.cellGrad self._A = D*self.Msig*G # Remove the null space from the matrix. self._A[0,0] /= self.mesh.vol[0] self._A = self._A.tocsc() return self._A def getRHS(self): # if self.mesh not in self._rhsDict: RHS = np.array([src.eval(self) for src in self.survey.srcList]).T # self._rhsDict[mesh] = RHS # return self._rhsDict[mesh] return RHS def fields(self, m): F = self.fieldsPair(self.mesh, self.survey) self.curModel = m A = self.A self.Ainv = self.Solver(A, **self.solverOpts) RHS = self.getRHS() Phi = self.Ainv * RHS Srcs = self.survey.srcList F[Srcs, 'phi_sol'] = Phi return F def Jvec(self, m, v, f=None): """ :param numpy.array m: model :param numpy.array v: vector to multiply :param Fields f: fields :rtype: numpy.array :return: Jv .. math:: c(m,u) = A(m)u - q = G\\text{sdiag}(M(mT(m)))Du - q = 0 \\nabla_u (A(m)u - q) = A(m) \\nabla_m (A(m)u - q) = G\\text{sdiag}(Du)\\nabla_m(M(mT(m))) Where M() is the mass matrix and mT is the model transform. .. math:: J = - P \left( \\nabla_u c(m, u) \\right)^{-1} \\nabla_m c(m, u) J(v) = - P ( A(m)^{-1} ( G\\text{sdiag}(Du)\\nabla_m(M(mT(m))) v ) ) """ # Set current model; clear dependent property $\mathbf{A(m)}$ self.curModel = m sigma = self.curModel.transform # $\sigma = \mathcal{M}(\m)$ if f is None: # Run forward simulation if $u$ not provided f = self.fields(self.curModel) u = f[self.survey.srcList, 'phi_sol'] D = self.mesh.faceDiv G = self.mesh.cellGrad # Derivative of model transform, $\deriv{\sigma}{\m}$ dsigdm_x_v = self.curModel.transformDeriv * v # Take derivative of $C(m,u)$ w.r.t. $m$ dCdm_x_v = np.empty_like(u) # loop over fields for each source for i in range(self.survey.nSrc): # Derivative of inner product, $\left(\mathbf{M}_{1/\sigma}^f\right)^{-1}$ dAdsig = D * self.dMdsig( G * u[:,i] ) dCdm_x_v[:, i] = dAdsig * dsigdm_x_v # Take derivative of $C(m,u)$ w.r.t. $u$ dA_du = self.A # Solve for $\deriv{u}{m}$ # dCdu_inv = self.Solver(dCdu, **self.solverOpts) if self.Ainv is None: self.Ainv = self.Solver(dA_du, **self.solverOpts) P = self.survey.getP(self.mesh) Jv = - P * mkvc( self.Ainv * dCdm_x_v ) return Jv def Jtvec(self, m, v, f=None): self.curModel = m sigma = self.curModel.transform # $\sigma = \mathcal{M}(\m)$ if f is None: # Run forward simulation if $f$ not provided f = self.fields(self.curModel) u = f[self.survey.srcList, 'phi_sol'] shp = u.shape P = self.survey.getP(self.mesh) PT_x_v = (P.T*v).reshape(shp, order='F') D = self.mesh.faceDiv G = self.mesh.cellGrad dA_du = self.A mT_dm = self.mapping.deriv(m) # We probably always need this due to the linesearch .. (?) self.Ainv = self.Solver(dA_du.T, **self.solverOpts) # if self.Ainv is None: # self.Ainv = self.Solver(dCdu, **self.solverOpts) w = self.Ainv * PT_x_v Jtv = 0 for i, ui in enumerate(u.T): # loop over each column Jtv += self.dMdsig( G * ui ).T * ( D.T * w[:,i] ) Jtv = - mT_dm.T * ( Jtv ) return Jtv