text: import geoapps.simpegPF as spf
  from geoapps.simpegPF.Utils import Zero, closestPoints, mkvc
  import numpy as np
  
  
  class BaseSrc(spf.Survey.BaseSrc):
  
      current = 1.0
      loc = None
  
      def __init__(self, rxList, **kwargs):
          spf.Survey.BaseSrc.__init__(self, rxList, **kwargs)
  
      def eval(self, prob):
          raise NotImplementedError
  
      def evalDeriv(self, prob):
          return Zero()
  
      @property
      def nD(self):
          """Number of data"""
          return self.vnD.sum()
  
      @property
      def vnD(self):
          """Vector number of data"""
          return np.array([rx.nD * len(rx.times) for rx in self.rxList])
  
  
  class Dipole(BaseSrc):
      def __init__(self, rxList, locA, locB, **kwargs):
          assert locA.shape == locB.shape, "Shape of locA and locB should be the same"
          self.loc = [locA, locB]
          BaseSrc.__init__(self, rxList, **kwargs)
  
      def eval(self, prob):
          if prob._formulation == "HJ":
              inds = closestPoints(prob.mesh, self.loc, gridLoc="CC")
              q = np.zeros(prob.mesh.nC)
              q[inds] = self.current * np.r_[1.0, -1.0]
          elif prob._formulation == "EB":
              qa = prob.mesh.getInterpolationMat(self.loc[0], locType="N").todense()
              qb = -prob.mesh.getInterpolationMat(self.loc[1], locType="N").todense()
              q = self.current * mkvc(qa + qb)
          return q
  
  
  class Pole(BaseSrc):
      def __init__(self, rxList, loc, **kwargs):
          BaseSrc.__init__(self, rxList, loc=loc, **kwargs)
  
      def eval(self, prob):
          if prob._formulation == "HJ":
              inds = closestPoints(prob.mesh, self.loc)
              q = np.zeros(prob.mesh.nC)
              q[inds] = self.current * np.r_[1.0]
          elif prob._formulation == "EB":
              q = prob.mesh.getInterpolationMat(self.loc, locType="N").todense()
              q = self.current * mkvc(q)
          return q
  