diff --git a/SimPEG/EM/FDEM/FieldsFDEM.py b/SimPEG/EM/FDEM/FieldsFDEM.py index 8f6fafe9..9f54855c 100644 --- a/SimPEG/EM/FDEM/FieldsFDEM.py +++ b/SimPEG/EM/FDEM/FieldsFDEM.py @@ -7,11 +7,39 @@ from SimPEG.Utils import Zero, Identity class Fields(SimPEG.Problem.Fields): - """Fancy Field Storage for a FDEM survey.""" + """ + + Fancy Field Storage for a FDEM survey. Only one field type is stored for + each problem, the rest are computed. The fields obejct acts like an array and is indexed by + + .. code-block:: python + + f = problem.fields(m) + e = f[srcList,'e'] + b = f[srcList,'b'] + + If accessing all sources for a given field, use the :code:`:` + + .. code-block:: python + + f = problem.fields(m) + e = f[:,'e'] + b = f[:,'b'] + + The array returned will be size (nE or nF, nSrcs :math:`\\times` nFrequencies) + """ + knownFields = {} dtype = complex class Fields_e(Fields): + """ + Fields object for Problem_e. + + :param Mesh mesh: mesh + :param Survey survey: survey + """ + knownFields = {'eSolution':'E'} aliasFields = { 'e' : ['eSolution','E','_e'], @@ -30,6 +58,15 @@ class Fields_e(Fields): self._edgeCurl = self.survey.prob.mesh.edgeCurl def _ePrimary(self, eSolution, srcList): + """ + Primary electric field from source + + :param numpy.ndarray eSolution: field we solved for + :param list srcList: list of sources + :rtype: numpy.ndarray + :return: primary electric field as defined by the sources + """ + ePrimary = np.zeros_like(eSolution) for i, src in enumerate(srcList): ep = src.ePrimary(self.prob) @@ -37,19 +74,67 @@ class Fields_e(Fields): return ePrimary def _eSecondary(self, eSolution, srcList): + """ + Secondary electric field is the thing we solved for + + :param numpy.ndarray eSolution: field we solved for + :param list srcList: list of sources + :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 + + :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 field we solved for with a vector + """ + return Identity()*v 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. + + :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 electric field derivative with respect to the inversion model with a vector + """ + # assuming primary does not depend on the model return Zero() def _bPrimary(self, eSolution, srcList): + """ + Primary magnetic flux density from source + + :param numpy.ndarray eSolution: field we solved for + :param list srcList: list of sources + :rtype: numpy.ndarray + :return: primary magnetic flux density as defined by the sources + """ + bPrimary = np.zeros([self._edgeCurl.shape[0],eSolution.shape[1]],dtype = complex) for i, src in enumerate(srcList): bp = src.bPrimary(self.prob) @@ -57,6 +142,15 @@ class Fields_e(Fields): return bPrimary def _bSecondary(self, eSolution, srcList): + """ + Secondary magnetic flux density from eSolution + + :param numpy.ndarray eSolution: field we solved for + :param list srcList: list of sources + :rtype: numpy.ndarray + :return: secondary magnetic flux density + """ + C = self._edgeCurl b = (C * eSolution) for i, src in enumerate(srcList): @@ -66,29 +160,85 @@ class Fields_e(Fields): return b def _bSecondaryDeriv_u(self, src, v, adjoint = False): + """ + Derivative of the secondary magnetic flux density with respect to the thing we solved for + + :param SimPEG.EM.FDEM.Src src: source + :param numpy.ndarray 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, adjoint) S_mDeriv = S_mDeriv(v) 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 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) def _bDeriv_m(self, src, v, adjoint=False): + """ + Derivative of the total 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 + :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) class Fields_b(Fields): + """ + Fields object for Problem_b. + + :param Mesh mesh: mesh + :param Survey survey: survey + """ + knownFields = {'bSolution':'F'} aliasFields = { 'b' : ['bSolution','F','_b'], @@ -111,6 +261,15 @@ class Fields_b(Fields): self._Me = self.survey.prob.Me def _bPrimary(self, bSolution, srcList): + """ + Primary magnetic flux density from source + + :param numpy.ndarray bSolution: field we solved for + :param list srcList: list of sources + :rtype: numpy.ndarray + :return: primary electric field as defined by the sources + """ + bPrimary = np.zeros_like(bSolution) for i, src in enumerate(srcList): bp = src.bPrimary(self.prob) @@ -118,19 +277,66 @@ class Fields_b(Fields): return bPrimary def _bSecondary(self, bSolution, srcList): + """ + Secondary magnetic flux density is the thing we solved for + + :param numpy.ndarray bSolution: field we solved for + :param list srcList: list of sources + :rtype: numpy.ndarray + :return: secondary magnetic flux density + """ + return bSolution def _b(self, bSolution, srcList): + """ + 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 + + :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 field we solved for with a vector + """ return Identity()*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. + + :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 magnetic flux density derivative with respect to the inversion model with a vector + """ + # assuming primary does not depend on the model return Zero() def _ePrimary(self, bSolution, srcList): + """ + Primary electric field from source + + :param numpy.ndarray bSolution: field we solved for + :param list srcList: list of sources + :rtype: numpy.ndarray + :return: primary electric field as defined by the sources + """ + ePrimary = np.zeros([self._edgeCurl.shape[1],bSolution.shape[1]],dtype = complex) for i,src in enumerate(srcList): ep = src.ePrimary(self.prob) @@ -138,6 +344,15 @@ class Fields_b(Fields): return ePrimary def _eSecondary(self, bSolution, srcList): + """ + Secondary electric field from bSolution + + :param numpy.ndarray bSolution: field we solved for + :param list srcList: list of sources + :rtype: numpy.ndarray + :return: secondary electric field + """ + e = self._MeSigmaI * ( self._edgeCurl.T * ( self._MfMui * bSolution)) for i,src in enumerate(srcList): _,S_e = src.eval(self.prob) @@ -145,12 +360,32 @@ class Fields_b(Fields): return e def _eSecondaryDeriv_u(self, src, 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 @@ -174,17 +409,53 @@ class Fields_b(Fields): 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 + + :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 field we solved for with a vector + """ + return self._eSecondaryDeriv_u(src, v, adjoint) def _eDeriv_m(self, src, v, adjoint=False): + """ + Derivative of the total electric field 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 electric field derivative with respect to the inversion model with a vector + """ + # assuming primary doesn't depend on model return self._eSecondaryDeriv_m(src, v, adjoint) class Fields_j(Fields): + """ + Fields object for Problem_j. + + :param Mesh mesh: mesh + :param Survey survey: survey + """ + knownFields = {'jSolution':'F'} aliasFields = { 'j' : ['jSolution','F','_j'], @@ -207,6 +478,15 @@ class Fields_j(Fields): self._Me = self.survey.prob.Me def _jPrimary(self, jSolution, srcList): + """ + Primary current density from source + + :param numpy.ndarray jSolution: field we solved for + :param list srcList: list of sources + :rtype: numpy.ndarray + :return: primary current density as defined by the sources + """ + jPrimary = np.zeros_like(jSolution,dtype = complex) for i, src in enumerate(srcList): jp = src.jPrimary(self.prob) @@ -214,19 +494,66 @@ class Fields_j(Fields): return jPrimary def _jSecondary(self, jSolution, srcList): + """ + Secondary current density is the thing we solved for + + :param numpy.ndarray jSolution: field we solved for + :param list srcList: list of sources + :rtype: numpy.ndarray + :return: secondary current density + """ + return jSolution def _j(self, jSolution, srcList): + """ + Total current density is sum of primary and secondary + + :param numpy.ndarray jSolution: field we solved for + :param list srcList: list of sources + :rtype: numpy.ndarray + :return: total current density + """ + return self._jPrimary(jSolution, srcList) + self._jSecondary(jSolution, 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 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 Identity()*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. + + :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 derivative with respect to the inversion model with a vector + """ # assuming primary does not depend on the model return Zero() def _hPrimary(self, jSolution, srcList): + """ + Primary magnetic field from source + + :param numpy.ndarray hSolution: field we solved for + :param list srcList: list of sources + :rtype: numpy.ndarray + :return: primary magnetic field as defined by the sources + """ + hPrimary = np.zeros([self._edgeCurl.shape[1],jSolution.shape[1]],dtype = complex) for i, src in enumerate(srcList): hp = src.hPrimary(self.prob) @@ -234,6 +561,15 @@ class Fields_j(Fields): return hPrimary def _hSecondary(self, jSolution, srcList): + """ + Secondary magnetic field from bSolution + + :param numpy.ndarray jSolution: field we solved for + :param list srcList: list of sources + :rtype: numpy.ndarray + :return: secondary magnetic field + """ + h = self._MeMuI * (self._edgeCurl.T * (self._MfRho * jSolution) ) for i, src in enumerate(srcList): h[:,i] *= -1./(1j*omega(src.freq)) @@ -242,12 +578,32 @@ class Fields_j(Fields): return h def _hSecondaryDeriv_u(self, src, v, adjoint=False): + """ + Derivative of the secondary magnetic field with respect to the thing we solved for + + :param SimPEG.EM.FDEM.Src src: source + :param numpy.ndarray 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 @@ -272,17 +628,53 @@ class Fields_j(Fields): 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 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) def _hDeriv_m(self, src, v, adjoint=False): + """ + Derivative of the total magnetic field 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 field derivative with respect to the inversion model with a vector + """ + # assuming the primary doesn't depend on the model return self._hSecondaryDeriv_m(src, v, adjoint) class Fields_h(Fields): + """ + Fields object for Problem_h. + + :param Mesh mesh: mesh + :param Survey survey: survey + """ + knownFields = {'hSolution':'E'} aliasFields = { 'h' : ['hSolution','E','_h'], @@ -303,6 +695,15 @@ class Fields_h(Fields): self._MfRho = self.survey.prob.MfRho def _hPrimary(self, hSolution, srcList): + """ + Primary magnetic field from source + + :param numpy.ndarray eSolution: field we solved for + :param list srcList: list of sources + :rtype: numpy.ndarray + :return: primary magnetic field as defined by the sources + """ + hPrimary = np.zeros_like(hSolution,dtype = complex) for i, src in enumerate(srcList): hp = src.hPrimary(self.prob) @@ -310,19 +711,67 @@ class Fields_h(Fields): return hPrimary def _hSecondary(self, hSolution, srcList): + """ + Secondary magnetic field is the thing we solved for + + :param numpy.ndarray hSolution: field we solved for + :param list srcList: list of sources + :rtype: numpy.ndarray + :return: secondary magnetic field + """ + 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): + """ + 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 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 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. + + :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 magnetic field derivative with respect to the inversion model with a vector + """ + # assuming primary does not depend on the model return Zero() def _jPrimary(self, hSolution, srcList): + """ + Primary current density from source + + :param numpy.ndarray hSolution: field we solved for + :param list srcList: list of sources + :rtype: numpy.ndarray + :return: primary current density as defined by the sources + """ + jPrimary = np.zeros([self._edgeCurl.shape[0], hSolution.shape[1]], dtype = complex) for i, src in enumerate(srcList): jp = src.jPrimary(self.prob) @@ -330,6 +779,15 @@ class Fields_h(Fields): return jPrimary def _jSecondary(self, hSolution, srcList): + """ + Secondary current density from eSolution + + :param numpy.ndarray hSolution: field we solved for + :param list srcList: list of sources + :rtype: numpy.ndarray + :return: secondary current density + """ + j = self._edgeCurl*hSolution for i, src in enumerate(srcList): _,S_e = src.eval(self.prob) @@ -337,22 +795,70 @@ class Fields_h(Fields): return j def _jSecondaryDeriv_u(self, src, v, adjoint=False): + """ + Derivative of the secondary current density with respect to the thing we solved for + + :param SimPEG.EM.FDEM.Src src: source + :param numpy.ndarray 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, adjoint) S_eDeriv = S_eDeriv(v) 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 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) def _jDeriv_m(self, src, v, adjoint=False): + """ + Derivative of the total 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 + """ + # assuming the primary does not depend on the model return self._jSecondaryDeriv_m(src,v,adjoint)