def _srcFct(self, obsLoc, coordinates="cartesian"): if getattr(self, '_dipole', None) is None: self._dipole = MagneticDipoleWholeSpace( mu=self.mu, orientation=self.orientation, location=self.loc, moment=self.moment ) return self._dipole.vector_potential(obsLoc, coordinates=coordinates)
def _srcFct(self, obsLoc, coordinates="cartesian"): if getattr(self, "_dipole", None) is None: self._dipole = MagneticDipoleWholeSpace( mu=self.mu, orientation=self.orientation, location=self.location, moment=self.moment, ) return self._dipole.magnetic_flux_density(obsLoc, coordinates=coordinates)
def ana_sol(XYZ): return MagneticDipoleWholeSpace( location=src.location, moment=1.0, orientation=src.orientation, mu=src.mu, ).magnetic_flux_density(XYZ)
class MagDipole(BaseTDEMSrc): moment = properties.Float("dipole moment of the transmitter", default=1.0, min=0.0) mu = properties.Float("permeability of the background", default=mu_0, min=0.0) orientation = properties.Vector3("orientation of the source", default="Z", length=1.0, required=True) location = LocationVector("location of the source", default=np.r_[0.0, 0.0, 0.0], shape=(3, )) loc = deprecate_property(location, "loc", new_name="location", removal_version="0.15.0") def __init__(self, receiver_list=None, **kwargs): kwargs.pop("srcType", None) BaseTDEMSrc.__init__(self, receiver_list=receiver_list, srcType="inductive", **kwargs) def _srcFct(self, obsLoc, coordinates="cartesian"): if getattr(self, "_dipole", None) is None: self._dipole = MagneticDipoleWholeSpace( mu=self.mu, orientation=self.orientation, location=self.loc, moment=self.moment, ) return self._dipole.vector_potential(obsLoc, coordinates=coordinates) def _aSrc(self, prob): coordinates = "cartesian" if prob._formulation == "EB": gridX = prob.mesh.gridEx gridY = prob.mesh.gridEy gridZ = prob.mesh.gridEz elif prob._formulation == "HJ": gridX = prob.mesh.gridFx gridY = prob.mesh.gridFy gridZ = prob.mesh.gridFz if prob.mesh._meshType == "CYL": coordinates = "cylindrical" if prob.mesh.isSymmetric: return self._srcFct(gridY)[:, 1] ax = self._srcFct(gridX, coordinates)[:, 0] ay = self._srcFct(gridY, coordinates)[:, 1] az = self._srcFct(gridZ, coordinates)[:, 2] a = np.concatenate((ax, ay, az)) return a def _getAmagnetostatic(self, prob): if prob._formulation == "EB": return prob.mesh.faceDiv * prob.MfMuiI * prob.mesh.faceDiv.T else: raise NotImplementedError( "Solving the magnetostatic problem for the initial fields " "when a permeable model is considered has not yet been " "implemented for the HJ formulation. " "See: https://github.com/simpeg/simpeg/issues/680") def _rhs_magnetostatic(self, prob): if getattr(self, "_hp", None) is None: if prob._formulation == "EB": bp = prob.mesh.edgeCurl * self._aSrc(prob) self._MfMuip = prob.mesh.getFaceInnerProduct(1.0 / self.mu) self._MfMuipI = prob.mesh.getFaceInnerProduct(1.0 / self.mu, invMat=True) self._hp = self._MfMuip * bp else: raise NotImplementedError( "Solving the magnetostatic problem for the initial fields " "when a permeable model is considered has not yet been " "implemented for the HJ formulation. " "See: https://github.com/simpeg/simpeg/issues/680") if prob._formulation == "EB": return -prob.mesh.faceDiv * ( (prob.MfMuiI - self._MfMuipI) * self._hp) else: raise NotImplementedError( "Solving the magnetostatic problem for the initial fields " "when a permeable model is considered has not yet been " "implemented for the HJ formulation. " "See: https://github.com/simpeg/simpeg/issues/680") def _phiSrc(self, prob): Ainv = prob.Solver(self._getAmagnetostatic(prob)) # todo: store these rhs = self._rhs_magnetostatic(prob) Ainv.clean() return Ainv * rhs def _bSrc(self, prob): if prob._formulation == "EB": C = prob.mesh.edgeCurl elif prob._formulation == "HJ": C = prob.mesh.edgeCurl.T return C * self._aSrc(prob) def bInitial(self, prob): if self.waveform.hasInitialFields is False: return Zero() if np.all(prob.mu == self.mu): return self._bSrc(prob) else: if prob._formulation == "EB": hs = prob.mesh.faceDiv.T * self._phiSrc(prob) ht = self._hp + hs return prob.MfMuiI * ht else: raise NotImplementedError def hInitial(self, prob): if self.waveform.hasInitialFields is False: return Zero() # if prob._formulation == 'EB': # return prob.MfMui * self.bInitial(prob) # elif prob._formulation == 'HJ': # return prob.MeMuI * self.bInitial(prob) return 1.0 / self.mu * self.bInitial(prob) def s_m(self, prob, time): if self.waveform.hasInitialFields is False: return Zero() return Zero() def s_e(self, prob, time): C = prob.mesh.edgeCurl b = self._bSrc(prob) if prob._formulation == "EB": MfMui = prob.mesh.getFaceInnerProduct(1.0 / self.mu) if self.waveform.hasInitialFields is True and time < prob.time_steps[ 1]: if prob._fieldType == "b": return Zero() elif prob._fieldType == "e": # Compute s_e from vector potential return C.T * (MfMui * b) else: return C.T * (MfMui * b) * self.waveform.eval(time) elif prob._formulation == "HJ": h = 1.0 / self.mu * b if self.waveform.hasInitialFields is True and time < prob.time_steps[ 1]: if prob._fieldType == "h": return Zero() elif prob._fieldType == "j": # Compute s_e from vector potential return C * h else: return C * h * self.waveform.eval(time)
class MagDipole_Bfield(MagDipole): """ Point magnetic dipole source calculated with the analytic solution for the 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. :param list receiver_list: 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 """ def __init__(self, receiver_list=None, frequency=None, location=None, **kwargs): super(MagDipole_Bfield, self).__init__(receiver_list=receiver_list, frequency=frequency, location=location, **kwargs) def _srcFct(self, obsLoc, coordinates="cartesian"): if getattr(self, "_dipole", None) is None: self._dipole = MagneticDipoleWholeSpace( mu=self.mu, orientation=self.orientation, location=self.location, moment=self.moment, ) return self._dipole.magnetic_flux_density(obsLoc, coordinates=coordinates) def bPrimary(self, simulation): """ The primary magnetic flux density from the analytic solution for magnetic fields from a dipole :param BaseFDEMSimulation simulation: FDEM simulation :rtype: numpy.ndarray :return: primary magnetic field """ formulation = simulation._formulation coordinates = "cartesian" if formulation == "EB": gridX = simulation.mesh.gridFx gridY = simulation.mesh.gridFy gridZ = simulation.mesh.gridFz elif formulation == "HJ": gridX = simulation.mesh.gridEx gridY = simulation.mesh.gridEy gridZ = simulation.mesh.gridEz if simulation.mesh._meshType == "CYL": coordinates = "cylindrical" if simulation.mesh.isSymmetric: bx = self._srcFct(gridX)[:, 0] bz = self._srcFct(gridZ)[:, 2] b = np.concatenate((bx, bz)) else: bx = self._srcFct(gridX, coordinates=coordinates)[:, 0] by = self._srcFct(gridY, coordinates=coordinates)[:, 1] bz = self._srcFct(gridZ, coordinates=coordinates)[:, 2] b = np.concatenate((bx, by, bz)) return mkvc(b)
class MagDipole(BaseFDEMSrc): """ 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!). 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:: \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}} We split up the fields and :math:`\mu^{-1}` into primary (:math:`\mathbf{P}`) and secondary (:math:`\mathbf{S}`) components - :math:`\mathbf{e} = \mathbf{e^P} + \mathbf{e^S}` - :math:`\mathbf{b} = \mathbf{b^P} + \mathbf{b^S}` - :math:`\\boldsymbol{\mu}^{\mathbf{-1}} = \\boldsymbol{\mu}^{\mathbf{-1}^\mathbf{P}} + \\boldsymbol{\mu}^{\mathbf{-1}^\mathbf{S}}` and define a zero-frequency primary simulation, noting that the source is generated by a divergence free electric current .. 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 .. 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 .. math:: \mathbf{C} \mathbf{e^S} + i \omega \mathbf{b^S} = - i \omega \mathbf{b^P} \\\\ {\mathbf{C}^T \mathbf{M_{\mu^{-1}}^f} \mathbf{b^S} - \mathbf{M_{\sigma}^e} \mathbf{e^S} = -\mathbf{C}^T \mathbf{{M_{\mu^{-1}}^f}^S} \mathbf{b^P}} :param list receiver_list: receiver list :param float freq: frequency :param numpy.ndarray location: 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 """ moment = properties.Float("dipole moment of the transmitter", default=1.0, min=0.0) mu = properties.Float("permeability of the background", default=mu_0, min=0.0) orientation = properties.Vector3("orientation of the source", default="Z", length=1.0, required=True) location = LocationVector("location of the source", default=np.r_[0.0, 0.0, 0.0], shape=(3, )) loc = deprecate_property(location, "loc", new_name="location", removal_version="0.15.0") def __init__(self, receiver_list=None, frequency=None, location=None, **kwargs): super(MagDipole, self).__init__(receiver_list, frequency=frequency, **kwargs) if location is not None: self.location = location def _srcFct(self, obsLoc, coordinates="cartesian"): if getattr(self, "_dipole", None) is None: self._dipole = MagneticDipoleWholeSpace( mu=self.mu, orientation=self.orientation, location=self.location, moment=self.moment, ) return self._dipole.vector_potential(obsLoc, coordinates=coordinates) def bPrimary(self, simulation): """ The primary magnetic flux density from a magnetic vector potential :param BaseFDEMSimulation simulation: FDEM simulation :rtype: numpy.ndarray :return: primary magnetic field """ formulation = simulation._formulation coordinates = "cartesian" if formulation == "EB": gridX = simulation.mesh.gridEx gridY = simulation.mesh.gridEy gridZ = simulation.mesh.gridEz C = simulation.mesh.edgeCurl elif formulation == "HJ": gridX = simulation.mesh.gridFx gridY = simulation.mesh.gridFy gridZ = simulation.mesh.gridFz C = simulation.mesh.edgeCurl.T if simulation.mesh._meshType == "CYL": coordinates = "cylindrical" if simulation.mesh.isSymmetric is True: if not (np.linalg.norm(self.orientation - np.r_[0.0, 0.0, 1.0]) < 1e-6): raise AssertionError( "for cylindrical symmetry, the dipole must be oriented" " in the Z direction") a = self._srcFct(gridY)[:, 1] return C * a ax = self._srcFct(gridX, coordinates)[:, 0] ay = self._srcFct(gridY, coordinates)[:, 1] az = self._srcFct(gridZ, coordinates)[:, 2] a = np.concatenate((ax, ay, az)) return C * a def hPrimary(self, simulation): """ The primary magnetic field from a magnetic vector potential :param BaseFDEMSimulation simulation: FDEM simulation :rtype: numpy.ndarray :return: primary magnetic field """ b = self.bPrimary(simulation) return 1.0 / self.mu * b def s_m(self, simulation): """ The magnetic source term :param BaseFDEMSimulation simulation: FDEM simulation :rtype: numpy.ndarray :return: primary magnetic field """ b_p = self.bPrimary(simulation) if simulation._formulation == "HJ": b_p = simulation.Me * b_p return -1j * omega(self.frequency) * b_p def s_e(self, simulation): """ The electric source term :param BaseFDEMSimulation simulation: FDEM simulation :rtype: numpy.ndarray :return: primary magnetic field """ if all(np.r_[self.mu] == np.r_[simulation.mu]): return Zero() else: formulation = simulation._formulation if formulation == "EB": mui_s = simulation.mui - 1.0 / self.mu MMui_s = simulation.mesh.getFaceInnerProduct(mui_s) C = simulation.mesh.edgeCurl elif formulation == "HJ": mu_s = simulation.mu - self.mu MMui_s = simulation.mesh.getEdgeInnerProduct(mu_s, invMat=True) C = simulation.mesh.edgeCurl.T return -C.T * (MMui_s * self.bPrimary(simulation)) def s_eDeriv(self, simulation, v, adjoint=False): if not hasattr(simulation, "muMap") or not hasattr( simulation, "muiMap"): return Zero() else: formulation = simulation._formulation if formulation == "EB": mui_s = simulation.mui - 1.0 / self.mu MMui_sDeriv = (simulation.mesh.getFaceInnerProductDeriv(mui_s)( self.bPrimary(simulation)) * simulation.muiDeriv) C = simulation.mesh.edgeCurl if adjoint: return -MMui_sDeriv.T * (C * v) return -C.T * (MMui_sDeriv * v) elif formulation == "HJ": return Zero() # raise NotImplementedError mu_s = simulation.mu - self.mu MMui_s = simulation.mesh.getEdgeInnerProduct(mu_s, invMat=True) C = simulation.mesh.edgeCurl.T return -C.T * (MMui_s * self.bPrimary(simulation))
class MagDipole(BaseTDEMSrc): moment = properties.Float( "dipole moment of the transmitter", default=1., min=0. ) mu = properties.Float( "permeability of the background", default=mu_0, min=0. ) orientation = properties.Vector3( "orientation of the source", default='Z', length=1., required=True ) loc = LocationVector( "location of the source", default=np.r_[0.,0.,0.], shape=(3,) ) def __init__(self, rxList, **kwargs): BaseTDEMSrc.__init__(self, rxList, srcType="inductive", **kwargs) def _srcFct(self, obsLoc, coordinates="cartesian"): if getattr(self, '_dipole', None) is None: self._dipole = MagneticDipoleWholeSpace( mu=self.mu, orientation=self.orientation, location=self.loc, moment=self.moment ) return self._dipole.vector_potential(obsLoc, coordinates=coordinates) def _aSrc(self, prob): coordinates = "cartesian" if prob._formulation == 'EB': gridX = prob.mesh.gridEx gridY = prob.mesh.gridEy gridZ = prob.mesh.gridEz elif prob._formulation == 'HJ': gridX = prob.mesh.gridFx gridY = prob.mesh.gridFy gridZ = prob.mesh.gridFz if prob.mesh._meshType is 'CYL': coordinates = "cylindrical" if prob.mesh.isSymmetric: return self._srcFct(gridY)[:, 1] ax = self._srcFct(gridX, coordinates)[:, 0] ay = self._srcFct(gridY, coordinates)[:, 1] az = self._srcFct(gridZ, coordinates)[:, 2] a = np.concatenate((ax, ay, az)) return a def _getAmagnetostatic(self, prob): if prob._formulation == 'EB': return prob.mesh.faceDiv * prob.MfMuiI * prob.mesh.faceDiv.T else: raise NotImplementedError( "Solving the magnetostatic problem for the initial fields " "when a permeable model is considered has not yet been " "implemented for the HJ formulation. " "See: https://github.com/simpeg/simpeg/issues/680" ) def _rhs_magnetostatic(self, prob): if getattr(self, '_hp', None) is None: if prob._formulation == 'EB': bp = prob.mesh.edgeCurl * self._aSrc(prob) self._MfMuip = prob.mesh.getFaceInnerProduct(1./self.mu) self._MfMuipI = prob.mesh.getFaceInnerProduct( 1./self.mu, invMat=True ) self._hp = self._MfMuip * bp else: raise NotImplementedError( "Solving the magnetostatic problem for the initial fields " "when a permeable model is considered has not yet been " "implemented for the HJ formulation. " "See: https://github.com/simpeg/simpeg/issues/680" ) if prob._formulation == 'EB': return -prob.mesh.faceDiv * ( (prob.MfMuiI - self._MfMuipI) * self._hp ) else: raise NotImplementedError( "Solving the magnetostatic problem for the initial fields " "when a permeable model is considered has not yet been " "implemented for the HJ formulation. " "See: https://github.com/simpeg/simpeg/issues/680" ) def _phiSrc(self, prob): Ainv = prob.Solver(self._getAmagnetostatic(prob)) # todo: store these rhs = self._rhs_magnetostatic(prob) Ainv.clean() return Ainv * rhs def _bSrc(self, prob): if prob._formulation == 'EB': C = prob.mesh.edgeCurl elif prob._formulation == 'HJ': C = prob.mesh.edgeCurl.T return C*self._aSrc(prob) def bInitial(self, prob): if self.waveform.hasInitialFields is False: return Zero() if np.all(prob.mu == self.mu): return self._bSrc(prob) else: if prob._formulation == 'EB': hs = prob.mesh.faceDiv.T * self._phiSrc(prob) ht = self._hp + hs return prob.MfMuiI * ht else: raise NotImplementedError def hInitial(self, prob): if self.waveform.hasInitialFields is False: return Zero() # if prob._formulation == 'EB': # return prob.MfMui * self.bInitial(prob) # elif prob._formulation == 'HJ': # return prob.MeMuI * self.bInitial(prob) return 1./self.mu * self.bInitial(prob) def s_m(self, prob, time): if self.waveform.hasInitialFields is False: return Zero() return Zero() def s_e(self, prob, time): C = prob.mesh.edgeCurl b = self._bSrc(prob) if prob._formulation == 'EB': MfMui = prob.mesh.getFaceInnerProduct(1./self.mu) if self.waveform.hasInitialFields is True and time < prob.timeSteps[1]: if prob._fieldType == 'b': return Zero() elif prob._fieldType == 'e': # Compute s_e from vector potential return C.T * (MfMui * b) else: return C.T * (MfMui * b) * self.waveform.eval(time) elif prob._formulation == 'HJ': h = 1./self.mu * b if self.waveform.hasInitialFields is True and time < prob.timeSteps[1]: if prob._fieldType == 'h': return Zero() elif prob._fieldType == 'j': # Compute s_e from vector potential return C * h else: return C * h * self.waveform.eval(time)