Esempio n. 1
0
class ReciprocalPropExample(Props.HasModel):

    sigma = Props.PhysicalProperty("Electrical conductivity (S/m)")

    rho = Props.PhysicalProperty("Electrical resistivity (Ohm m)")

    Props.Reciprocal(sigma, rho)
Esempio n. 2
0
class PetaInvProblem(Problem.BaseProblem):
    surveyPair = Survey.BaseSurvey
    P = None
    J = None
    time = None
    we = None
    ColeCole = "Debye"

    eta, etaMap, etaDeriv = Props.Invertible(
        "Chargeability"
    )
    tau, tauMap, tauDeriv = Props.Invertible(
        "Tau"
    )
    def __init__(self, mesh, **kwargs):
        Problem.BaseProblem.__init__(self, mesh, **kwargs)

    def fields(self, m, f=None):
        self.model = m
        self.J = petaJconvfun(self.eta, self.tau, self.we, self.time, self.P, ColeCole = self.ColeCole)
        return petaconvfun(self.eta, self.tau, self.we, self.time, self.P, ColeCole = self.ColeCole)

    def Jvec(self, m, v, f=None):
        jvec = self.J.dot(v)
        return jvec

    def Jtvec(self, m, v, f=None):
        jtvec = (self.J.T.dot(v))
        return jtvec
Esempio n. 3
0
class Haverkamp_k(BaseHydraulicConductivity):

    Ks, KsMap, KsDeriv = Props.Invertible("Saturated hydraulic conductivity",
                                          default=9.44e-03)

    A, AMap, ADeriv = Props.Invertible("fitting parameter", default=1.175e+06)

    gamma, gammaMap, gammaDeriv = Props.Invertible("fitting parameter",
                                                   default=4.74)

    def _get_params(self):
        return self.Ks, self.A, self.gamma

    def __call__(self, u):
        Ks, A, gamma = self._get_params()
        P_p, P_n = _get_projections(u)  # Compute the positive/negative domains
        f_p = P_p * np.ones(len(u)) * Ks  # ensures scalar Ks works
        f_n = P_n * Ks * A / (A + abs(u)**gamma)
        return f_p + f_n

    def derivU(self, u):
        Ks, A, gamma = self._get_params()
        g = -(Ks * A * gamma * abs(u)**(gamma - 1) * np.sign(u)) / (
            (A + abs(u)**gamma)**2)
        g[u >= 0] = 0
        return Utils.sdiag(g)

    def derivM(self, u):
        return self._derivKs(u) + self._derivA(u) + self._derivGamma(u)

    def _derivKs(self, u):
        if self.KsMap is None:
            return Utils.Zero()

        Ks, A, gamma = self._get_params()
        P_p, P_n = _get_projections(u)  # Compute the positive/negative domains

        dKs_dm_p = P_p * self.KsDeriv
        dKs_dm_n = P_n * Utils.sdiag(A / (A + abs(u)**gamma)) * self.KsDeriv
        return dKs_dm_p + dKs_dm_n

    def _derivA(self, u):
        if self.AMap is None:
            return Utils.Zero()
        Ks, A, gamma = self._get_params()
        ddm = Ks / (A + abs(u)**gamma) - Ks * A / (A + abs(u)**gamma)**2
        ddm[u >= 0] = 0
        dA_dm = Utils.sdiag(ddm) * self.ADeriv
        return dA_dm

    def _derivGamma(self, u):
        if self.gammaMap is None:
            return Utils.Zero()
        Ks, A, gamma = self._get_params()
        ddm = -(A * Ks * np.log(abs(u)) * abs(u)**gamma) / (A +
                                                            abs(u)**gamma)**2
        ddm[u >= 0] = 0
        dGamma_dm = Utils.sdiag(ddm) * self.gammaDeriv
        return dGamma_dm
Esempio n. 4
0
class ReciprocalMappingExample(Props.HasModel):

    sigma, sigmaMap, sigmaDeriv = Props.Invertible(
        "Electrical conductivity (S/m)")

    rho, rhoMap, rhoDeriv = Props.Invertible("Electrical resistivity (Ohm m)")

    Props.Reciprocal(sigma, rho)
Esempio n. 5
0
class ReciprocalExample(Props.HasModel):

    sigma, sigmaMap, sigmaDeriv = Props.Invertible(
        "Electrical conductivity (S/m)")

    rho = Props.PhysicalProperty("Electrical resistivity (Ohm m)")

    Props.Reciprocal(sigma, rho)
Esempio n. 6
0
class ReciprocalPropExampleDefaults(Props.HasModel):

    sigma = Props.PhysicalProperty("Electrical conductivity (S/m)",
                                   default=np.r_[1., 2., 3.])

    rho = Props.PhysicalProperty("Electrical resistivity (Ohm m)")

    Props.Reciprocal(sigma, rho)
Esempio n. 7
0
class ComplicatedInversion(Props.HasModel):

    Ks, KsMap, KsDeriv = Props.Invertible("Saturated hydraulic conductivity",
                                          default=24.96)

    A, AMap, ADeriv = Props.Invertible("fitting parameter", default=1.175e+06)

    gamma, gammaMap, gammaDeriv = Props.Invertible("fitting parameter",
                                                   default=4.74)
Esempio n. 8
0
class SimpleExample(Props.HasModel):

    sigmaMap = Props.Mapping("Mapping to the inversion model.")

    sigma = Props.PhysicalProperty("Electrical conductivity (S/m)",
                                   mapping=sigmaMap)

    sigmaDeriv = Props.Derivative("Derivative of sigma wrt the model.",
                                  physical_property=sigma)
Esempio n. 9
0
class SEInvProblem(Problem.BaseProblem):

    sigmaInf, sigmaInfMap, sigmaInfDeriv = Props.Invertible(
        "Electrical conductivity at infinite frequency (S/m)"
    )

    eta, etaMap, etaDeriv = Props.Invertible(
        "Cole-Cole chargeability (V/V)"
    )

    tau, tauMap, tauDeriv = Props.Invertible(
        "Cole-Cole time constant (s)"
    )

    c, cMap, cDeriv = Props.Invertible(
        "Cole-Cole frequency dependency"
    )

    P = None
    J = None
    time = None

    def __init__(self, mesh, **kwargs):
        Problem.BaseProblem.__init__(self, mesh, **kwargs)

    def fields(self, m=None, f=None):
        if m is not None:
            self.model = m
        self.J = self.get_peta_deriv(self.time)
        return self.get_peta(self.time)

    def Jvec(self, m, v, f=None):
        jvec = self.J.dot(v)
        return jvec

    def Jtvec(self, m, v, f=None):
        jtvec = self.J.T.dot(v)
        return jtvec

    def get_peta(self, time):
        return self.eta*np.exp(-(time/self.tau)**self.c)

    def get_peta_deriv(self, time):
        kerneleta = lambda t, eta, tau, c: np.exp(-(time/tau)**c)
        kerneltau = lambda t, eta, tau, c: (c*eta/tau)*((t/tau)**c)*np.exp(-(t/tau)**c)
        kernelc = lambda t, eta, tau, c: -eta*((t/tau)**c)*np.exp(-(t/tau)**c)*np.log(t/tau)

        tempeta = kerneleta(time, self.eta, self.tau, self.c).reshape([-1,1])
        temptau = kerneltau(time, self.eta, self.tau, self.c).reshape([-1,1])
        tempc = kernelc(time, self.eta, self.tau, self.c).reshape([-1,1])
        J = tempeta * self.etaDeriv + temptau * self.tauDeriv + tempc * self.cDeriv
        return J
Esempio n. 10
0
class BaseSPProblem(BaseDCProblem):

    h, hMap, hDeriv = Props.Invertible("Hydraulic Head (m)")

    q, qMap, qDeriv = Props.Invertible("Streaming current source (A/m^3)")

    jsx, jsxMap, jsxDeriv = Props.Invertible(
        "Streaming current density in x-direction (A/m^2)")

    jsy, jsyMap, jsyDeriv = Props.Invertible(
        "Streaming current density in y-direction (A/m^2)")

    jsz, jszMap, jszDeriv = Props.Invertible(
        "Streaming current density in z-direction (A/m^2)")

    sigma = Props.PhysicalProperty("Electrical conductivity (S/m)")

    rho = Props.PhysicalProperty("Electrical resistivity (Ohm m)")

    Props.Reciprocal(sigma, rho)

    modelType = None
    surveyPair = Survey
    fieldsPair = FieldsDC

    @property
    def deleteTheseOnModelUpdate(self):
        toDelete = []
        return toDelete

    def evalq(self, Qv, vel):
        MfQviI = self.mesh.getFaceInnerProduct(1.0 / Qv, invMat=True)
        Mf = self.mesh.getFaceInnerProduct()
        return self.Div * (Mf * (MfQviI * vel))
Esempio n. 11
0
class Problem3D_e(Problem3DEM_e):

    _solutionType = 'eSolution'
    _formulation = 'EB'
    fieldsPair = Fields3D_e

    sigmaInf, sigmaInfMap, sigmaInfDeriv = Props.Invertible(
        "Electrical conductivity at infinite frequency(S/m)")

    chi = Props.PhysicalProperty("Magnetic susceptibility", default=0.)

    eta, etaMap, etaDeriv = Props.Invertible(
        "Electrical chargeability (V/V), 0 <= eta < 1", default=0.)

    tau, tauMap, tauDeriv = Props.Invertible("Time constant (s)", default=1.)

    c, cMap, cDeriv = Props.Invertible("Frequency Dependency, 0 < c < 1",
                                       default=0.5)

    h, hMap, hDeriv = Props.Invertible("Receiver Height (m), h > 0", )

    def __init__(self, mesh, **kwargs):
        Problem3DEM_e.__init__(self, mesh, **kwargs)

    def MeSigma(self, freq):
        """
            Edge inner product matrix for \\(\\sigma\\). Used in the E-B formulation
        """
        sigma = ColeColePelton(freq, self.sigmaInf, self.eta, self.tau, self.c)
        return self.mesh.getEdgeInnerProduct(sigma)

    def MeSigmaI(self, freq):
        """
            Inverse of the edge inner product matrix for \\(\\sigma\\).
        """
        sigma = ColeColePelton(freq, self.sigmaInf, self.eta, self.tau, self.c)
        return self.mesh.getEdgeInnerProduct(sigma, invMat=True)

    def getA(self, freq):
        """
        System matrix

        .. math ::
            \mathbf{A} = \mathbf{C}^{\\top} \mathbf{M_{\mu^{-1}}^f} \mathbf{C}
            + i \omega \mathbf{M^e_{\sigma}}

        :param float freq: Frequency
        :rtype: scipy.sparse.csr_matrix
        :return: A
        """

        MfMui = self.MfMui
        MeSigma = self.MeSigma(freq)
        C = self.mesh.edgeCurl

        return C.T * MfMui * C + 1j * omega(freq) * MeSigma
Esempio n. 12
0
class Problem3DIP_Linear_singletime(Problem3DIP_Linear):
    peta, petaMap, petaDeriv = Props.Invertible(
        "Peudo-chargeability"
    )

    def forward(self, m, f=None):
        self.model = m
        if self.J is None:
            self.getJ(f=f)

        ntime = len(self.survey.times)
        self.model = m
        return self.J.dot(self.actMap.P.T * self.peta)

    def Jvec(self, m, v, f=None):
        return self.J.dot(self.actMap.P.T * self.petaDeriv * v)

    def Jtvec(self, m, v, f=None):
        return self.petaDeriv.T * (self.actMap.P*(self.J.T.dot(v)))
Esempio n. 13
0
class StraightRayProblem(Problem.LinearProblem):

    slowness, slownessMap, slownessDeriv = Props.Invertible(
        "Slowness model (1/v)")

    @property
    def A(self):
        if getattr(self, '_A', None) is not None:
            return self._A

        self._A = sp.lil_matrix((self.survey.nD, self.mesh.nC))
        row = 0
        for tx in self.survey.txList:
            for rx in tx.rxList:
                for loc_i in range(rx.locs.shape[0]):
                    inds, V = lineintegral(self.mesh, tx.loc,
                                           rx.locs[loc_i, :])
                    self._A[inds * 0 + row, inds] = V
                    row += 1

        return self._A

    def fields(self, m):
        self.model = m
        return self.A * self.slowness

    def Jvec(self, m, v, f=None):
        self.model = m
        # mt = self.model.transformDeriv
        # return self.A * ( mt * v )
        return self.A * self.slownessDeriv * v

    def Jtvec(self, m, v, f=None):
        self.model = m
        # mt = self.model.transformDeriv
        # return mt.T * ( self.A.T * v )
        return self.slownessDeriv.T * self.A.T * v
Esempio n. 14
0
class EM1D(Problem.BaseProblem):
    """
    Pseudo analytic solutions for frequency and time domain EM problems
    assumingLayered earth (1D).
    """
    surveyPair = BaseEM1DSurvey
    mapPair = Maps.IdentityMap
    chi = None
    hankel_filter = 'key_101_2009'  # Default: Hankel filter
    hankel_pts_per_dec = None       # Default: Standard DLF
    verbose = False
    fix_Jmatrix = False
    _Jmatrix_sigma = None
    _Jmatrix_height = None
    _pred = None

    sigma, sigmaMap, sigmaDeriv = Props.Invertible(
        "Electrical conductivity at infinite frequency(S/m)"
    )

    chi = Props.PhysicalProperty(
        "Magnetic susceptibility",
        default=0.
    )

    eta, etaMap, etaDeriv = Props.Invertible(
        "Electrical chargeability (V/V), 0 <= eta < 1",
        default=0.
    )

    tau, tauMap, tauDeriv = Props.Invertible(
        "Time constant (s)",
        default=1.
    )

    c, cMap, cDeriv = Props.Invertible(
        "Frequency Dependency, 0 < c < 1",
        default=0.5
    )

    h, hMap, hDeriv = Props.Invertible(
        "Receiver Height (m), h > 0",
    )

    def __init__(self, mesh, **kwargs):
        Problem.BaseProblem.__init__(self, mesh, **kwargs)

        # Check input arguments. If self.hankel_filter is not a valid filter,
        # it will set it to the default (key_201_2009).
        ht, htarg = check_hankel('fht', [self.hankel_filter,
                                         self.hankel_pts_per_dec], 1)

        self.fhtfilt = htarg[0]                 # Store filter
        self.hankel_filter = self.fhtfilt.name  # Store name
        self.hankel_pts_per_dec = htarg[1]      # Store pts_per_dec
        if self.verbose:
            print(">> Use "+self.hankel_filter+" filter for Hankel Transform")

        if self.hankel_pts_per_dec != 0:
            raise NotImplementedError()

    def hz_kernel_vertical_magnetic_dipole(
        self, lamda, f, n_layer, sig, chi, depth, h, z,
        flag, output_type='response'
    ):

        """
            Kernel for vertical magnetic component (Hz) due to
            vertical magnetic diopole (VMD) source in (kx,ky) domain

        """
        u0 = lamda
        coefficient_wavenumber = 1/(4*np.pi)*lamda**3/u0

        n_frequency = self.survey.n_frequency
        n_layer = self.survey.n_layer
        n_filter = self.n_filter

        if output_type == 'sensitivity_sigma':
            drTE = np.zeros([n_layer, n_frequency, n_filter], dtype=np.complex128, order='F')
            rte_fortran.rte_sensitivity(f, lamda, sig, chi, depth, self.survey.half_switch, drTE, n_layer, n_frequency, n_filter)

            kernel = drTE * np.exp(-u0*(z+h)) * coefficient_wavenumber
        else:
            rTE = np.empty([n_frequency, n_filter], dtype=np.complex128, order='F')
            rte_fortran.rte_forward(f, lamda, sig, chi, depth, self.survey.half_switch, rTE, n_layer, n_frequency, n_filter) 

            kernel = rTE * np.exp(-u0*(z+h)) * coefficient_wavenumber
            if output_type == 'sensitivity_height':
                kernel *= -2*u0

        return kernel

        # Note
        # Here only computes secondary field.
        # I am not sure why it does not work if we add primary term.
        # This term can be analytically evaluated, where h = 0.
        #     kernel = (
        #         1./(4*np.pi) *
        #         (np.exp(u0*(z-h))+rTE * np.exp(-u0*(z+h)))*lamda**3/u0
        #     )

    # TODO: make this to take a vector rather than a single frequency
    def hz_kernel_circular_loop(
        self, lamda, f, n_layer, sig, chi, depth, h, z, I, a,
        flag,  output_type='response'
    ):

        """

        Kernel for vertical magnetic component (Hz) at the center
        due to circular loop source in (kx,ky) domain

        .. math::

            H_z = \\frac{Ia}{2} \int_0^{\infty} [e^{-u_0|z+h|} +
            \\r_{TE}e^{u_0|z-h|}]
            \\frac{\lambda^2}{u_0} J_1(\lambda a)] d \lambda

        """

        n_frequency = self.survey.n_frequency
        n_layer = self.survey.n_layer
        n_filter = self.n_filter

        w = 2*np.pi*f
        u0 = lamda
        radius = np.empty([n_frequency, n_filter], order='F')
        radius[:, :] = np.tile(a.reshape([-1, 1]), (1, n_filter))

        coefficient_wavenumber = I*radius*0.5*lamda**2/u0

        if output_type == 'sensitivity_sigma':
            drTE = np.zeros([n_layer, n_frequency, n_filter], dtype=np.complex128, order='F')
            rte_fortran.rte_sensitivity(f, lamda, sig, chi, depth, self.survey.half_switch, drTE, n_layer, n_frequency, n_filter)

            kernel = drTE * np.exp(-u0*(z+h)) * coefficient_wavenumber
        else:
            rTE = np.empty([n_frequency, n_filter], dtype=np.complex128, order='F')
            rte_fortran.rte_forward(f, lamda, sig, chi, depth, self.survey.half_switch, rTE, n_layer, n_frequency, n_filter) 

            if flag == 'secondary':
                kernel = rTE * np.exp(-u0*(z+h)) * coefficient_wavenumber
            else:
                kernel = rTE * (
                    np.exp(-u0*(z+h)) + np.exp(u0*(z-h))
                ) * coefficient_wavenumber

            if output_type == 'sensitivity_height':
                kernel *= -2*u0

        return kernel

    def hz_kernel_horizontal_electric_dipole(
        self, lamda, f, n_layer, sig, chi, depth, h, z,
        flag, output_type='response'
    ):

        """
            Kernel for vertical magnetic field (Hz) due to
            horizontal electric diopole (HED) source in (kx,ky) domain

        """
        n_frequency = self.survey.n_frequency
        n_layer = self.survey.n_layer
        n_filter = self.n_filter

        u0 = lamda
        coefficient_wavenumber = 1/(4*np.pi)*lamda**2/u0

        if output_type == 'sensitivity_sigma':
            drTE = np.zeros([n_layer, n_frequency, n_filter], dtype=np.complex128, order='F')
            rte_fortran.rte_sensitivity(f, lamda, sig, chi, depth, self.survey.half_switch, drTE, n_layer, n_frequency, n_filter)

            kernel = drTE * np.exp(-u0*(z+h)) * coefficient_wavenumber
        else:
            rTE = np.empty([n_frequency, n_filter], dtype=np.complex128, order='F')
            rte_fortran.rte_forward(f, lamda, sig, chi, depth, self.survey.half_switch, rTE, n_layer, n_frequency, n_filter) 

            kernel = rTE * np.exp(-u0*(z+h)) * coefficient_wavenumber
            if output_type == 'sensitivity_height':
                kernel *= -2*u0

        return kernel

    # make it as a property?

    def sigma_cole(self):
        """
        Computes Pelton's Cole-Cole conductivity model
        in frequency domain.

        Parameter
        ---------

        n_filter: int
            the number of filter values
        f: ndarray
            frequency (Hz)

        Return
        ------

        sigma_complex: ndarray (n_layer x n_frequency x n_filter)
            Cole-Cole conductivity values at given frequencies

        """
        n_layer = self.survey.n_layer
        n_frequency = self.survey.n_frequency
        n_filter = self.n_filter
        f = self.survey.frequency

        sigma = np.tile(self.sigma.reshape([-1, 1]), (1, n_frequency))
        if np.isscalar(self.eta):
            eta = self.eta
            tau = self.tau
            c = self.c
        else:
            eta = np.tile(self.eta.reshape([-1, 1]), (1, n_frequency))
            tau = np.tile(self.tau.reshape([-1, 1]), (1, n_frequency))
            c = np.tile(self.c.reshape([-1, 1]), (1, n_frequency))

        w = np.tile(
            2*np.pi*f,
            (n_layer, 1)
        )
        
        sigma_complex = np.empty([n_layer, n_frequency], dtype=np.complex128, order='F')
        sigma_complex[:, :] = (
            sigma -
            sigma*eta/(1+(1-eta)*(1j*w*tau)**c)
        )

        sigma_complex_tensor = np.empty([n_layer, n_frequency, n_filter], dtype=np.complex128, order='F')
        sigma_complex_tensor[:, :, :] = np.tile(sigma_complex.reshape(
            (n_layer, n_frequency, 1)), (1, 1, n_filter)
        )

        return sigma_complex_tensor

    @property
    def n_filter(self):
        """ Length of filter """
        return self.fhtfilt.base.size

    def forward(self, m, output_type='response'):
        """
            Return Bz or dBzdt
        """

        self.model = m

        n_frequency = self.survey.n_frequency
        flag = self.survey.field_type
        n_layer = self.survey.n_layer
        depth = self.survey.depth
        I = self.survey.I
        n_filter = self.n_filter

        # Get lambd and offset, will depend on pts_per_dec
        if self.survey.src_type == "VMD":
            r = self.survey.offset
        else:
            # a is the radius of the loop
            r = self.survey.a * np.ones(n_frequency)

        # Use function from empymod
        # size of lambd is (n_frequency x n_filter)
        lambd = np.empty([self.survey.frequency.size, n_filter], order='F')
        lambd[:, :], _ = get_spline_values(self.fhtfilt, r, self.hankel_pts_per_dec)

        # lambd, _ = get_spline_values(self.fhtfilt, r, self.hankel_pts_per_dec)
        
        # TODO: potentially store
        f = np.empty([self.survey.frequency.size, n_filter], order='F')
        f[:,:] = np.tile(self.survey.frequency.reshape([-1, 1]), (1, n_filter))
        # h is an inversion parameter
        if self.hMap is not None:
            h = self.h
        else:
            h = self.survey.h

        z = h + self.survey.dz

        chi = self.chi

        if np.isscalar(self.chi):
            chi = np.ones_like(self.sigma) * self.chi

        # TODO: potentially store
        sig = self.sigma_cole()

        if output_type == 'response':
            # for simulation
            if self.survey.src_type == 'VMD':
                hz = self.hz_kernel_vertical_magnetic_dipole(
                    lambd, f, n_layer,
                    sig, chi, depth, h, z,
                    flag, output_type=output_type
                )

                # kernels for each bessel function
                # (j0, j1, j2)
                PJ = (hz, None, None)  # PJ0

            elif self.survey.src_type == 'CircularLoop':
                hz = self.hz_kernel_circular_loop(
                    lambd, f, n_layer,
                    sig, chi, depth, h, z, I, r,
                    flag, output_type=output_type
                )

                # kernels for each bessel function
                # (j0, j1, j2)
                PJ = (None, hz, None)  # PJ1

            # TODO: This has not implemented yet!
            elif self.survey.src_type == "piecewise_line":
                # Need to compute y
                hz = self.hz_kernel_horizontal_electric_dipole(
                    lambd, f, n_layer,
                    sig, chi, depth, h, z, I, r,
                    flag, output_type=output_type
                )
                # kernels for each bessel function
                # (j0, j1, j2)
                PJ = (None, hz, None)  # PJ1

            else:
                raise Exception("Src options are only VMD or CircularLoop!!")

        elif output_type == 'sensitivity_sigma':

            # for simulation
            if self.survey.src_type == 'VMD':
                hz = self.hz_kernel_vertical_magnetic_dipole(
                    lambd, f, n_layer,
                    sig, chi, depth, h, z,
                    flag, output_type=output_type
                )

                PJ = (hz, None, None)  # PJ0

            elif self.survey.src_type == 'CircularLoop':

                hz = self.hz_kernel_circular_loop(
                    lambd, f, n_layer,
                    sig, chi, depth, h, z, I, r,
                    flag, output_type=output_type
                )

                PJ = (None, hz, None)  # PJ1

            else:
                raise Exception("Src options are only VMD or CircularLoop!!")

            r = np.tile(r, (n_layer, 1))

        elif output_type == 'sensitivity_height':

            # for simulation
            if self.survey.src_type == 'VMD':
                hz = self.hz_kernel_vertical_magnetic_dipole(
                    lambd, f, n_layer,
                    sig, chi, depth, h, z,
                    flag, output_type=output_type
                )

                PJ = (hz, None, None)  # PJ0

            elif self.survey.src_type == 'CircularLoop':

                hz = self.hz_kernel_circular_loop(
                    lambd, f, n_layer,
                    sig, chi, depth, h, z, I, r,
                    flag, output_type=output_type
                )

                PJ = (None, hz, None)  # PJ1

            else:
                raise Exception("Src options are only VMD or CircularLoop!!")

        # Carry out Hankel DLF
        # ab=66 => 33 (vertical magnetic src and rec)
        # For response
        # HzFHT size = (n_frequency,)
        # For sensitivity
        # HzFHT size = (n_layer, n_frequency)

        HzFHT = dlf(PJ, lambd, r, self.fhtfilt, self.hankel_pts_per_dec,
                    factAng=None, ab=33)

        if output_type == "sensitivity_sigma":
            return HzFHT.T

        return HzFHT

    # @profile
    def fields(self, m):
        f = self.forward(m, output_type='response')
        self.survey._pred = Utils.mkvc(self.survey.projectFields(f))
        return f

    def getJ_height(self, m, f=None):
        """

        """
        if self.hMap is None:
            return Utils.Zero()

        if self._Jmatrix_height is not None:
            return self._Jmatrix_height
        else:

            if self.verbose:
                print (">> Compute J height ")

            dudz = self.forward(m, output_type="sensitivity_height")

            self._Jmatrix_height = (
                self.survey.projectFields(dudz)
            ).reshape([-1, 1])

            return self._Jmatrix_height

    # @profile
    def getJ_sigma(self, m, f=None):

        if self.sigmaMap is None:
            return Utils.Zero()

        if self._Jmatrix_sigma is not None:
            return self._Jmatrix_sigma
        else:

            if self.verbose:
                print (">> Compute J sigma")

            dudsig = self.forward(m, output_type="sensitivity_sigma")

            self._Jmatrix_sigma = self.survey.projectFields(dudsig)
            if self._Jmatrix_sigma.ndim == 1:
                self._Jmatrix_sigma = self._Jmatrix_sigma.reshape([-1, 1])
            return self._Jmatrix_sigma

    def getJ(self, m, f=None):
        return (
            self.getJ_sigma(m, f=f) * self.sigmaDeriv +
            self.getJ_height(m, f=f) * self.hDeriv
        )

    def Jvec(self, m, v, f=None):
        """
            Computing Jacobian^T multiplied by vector.
        """

        J_sigma = self.getJ_sigma(m, f=f)
        J_height = self.getJ_height(m, f=f)
        Jv = np.dot(J_sigma, self.sigmaMap.deriv(m, v))
        if self.hMap is not None:
            Jv += np.dot(J_height, self.hMap.deriv(m, v))
        return Jv

    def Jtvec(self, m, v, f=None):
        """
            Computing Jacobian^T multiplied by vector.
        """

        J_sigma = self.getJ_sigma(m, f=f)
        J_height = self.getJ_height(m, f=f)
        Jtv = self.sigmaDeriv.T*np.dot(J_sigma.T, v)
        if self.hMap is not None:
            Jtv += self.hDeriv.T*np.dot(J_height.T, v)
        return Jtv

    @property
    def deleteTheseOnModelUpdate(self):
        toDelete = []
        if self.fix_Jmatrix is False:
            if self._Jmatrix_sigma is not None:
                toDelete += ['_Jmatrix_sigma']
            if self._Jmatrix_height is not None:
                toDelete += ['_Jmatrix_height']
        return toDelete

    def depth_of_investigation_christiansen_2012(self, std, thres_hold=0.8):
        pred = self.survey._pred.copy()
        delta_d = std * np.log(abs(self.survey.dobs))
        J = self.getJ(self.model)
        J_sum = abs(Utils.sdiag(1/delta_d/pred) * J).sum(axis=0)
        S = np.cumsum(J_sum[::-1])[::-1]
        active = S-thres_hold > 0.
        doi = abs(self.survey.depth[active]).max()
        return doi, active

    def get_threshold(self, uncert):
        _, active = self.depth_of_investigation(uncert)
        JtJdiag = self.get_JtJdiag(uncert)
        delta = JtJdiag[active].min()
        return delta

    def get_JtJdiag(self, uncert):
        J = self.getJ(self.model)
        JtJdiag = (np.power((Utils.sdiag(1./uncert)*J), 2)).sum(axis=0)
        return JtJdiag
Esempio n. 15
0
File: Base.py Progetto: JKutt/PyDev
class BaseEMIPProblem(BaseEMProblem):

    sigmaInf, sigmaInfMap, sigmaInfDeriv = Props.Invertible(
        "Electrical conductivity at infinite frequency (S/m)")

    eta, etaMap, etaDeriv = Props.Invertible("Cole-Cole chargeability (V/V)")

    tau, tauMap, tauDeriv = Props.Invertible("Cole-Cole time constant (s)")

    c, cMap, cDeriv = Props.Invertible("Cole-Cole frequency dependency")

    surveyPair = Survey.BaseSurvey  #: The survey to pair with.
    dataPair = Survey.Data  #: The data to pair with.

    mapPair = Maps.IdentityMap  #: Type of mapping to pair with

    Solver = SimpegSolver  #: Type of solver to pair with
    solverOpts = {}  #: Solver options

    verbose = False

    ####################################################
    # Mass Matrices
    ####################################################
    @property
    def deleteTheseOnModelUpdate(self):
        toDelete = []
        if self.sigmaInfMap is not None:
            toDelete += [
                '_MeSigmaInf', '_MeSigmaInfI', '_MeSigma0', '_MeSigma0I'
            ]

        if hasattr(self, 'muMap') or hasattr(self, 'muiMap'):
            if self.muMap is not None or self.muiMap is not None:
                toDelete += ['_MeMu', '_MeMuI', '_MfMui', '_MfMuiI']
        return toDelete

    ####################################################
    # Electrical Conductivity
    ####################################################
    @property
    def MeSigmaInf(self):
        """
        Edge inner product matrix for \\(\\sigmaInf\\).
        Used in the E-B formulation
        """
        if getattr(self, '_MeSigmaInf', None) is None:
            self._MeSigmaInf = self.mesh.getEdgeInnerProduct(self.sigmaInf)
        return self._MeSigmaInf

    # TODO: This should take a vector
    def MeSigmaInfDeriv(self, u):
        """
        Derivative of MeSigmaInf with respect to the model
        """
        if self.sigmaInfMap is None:
            return Utils.Zero()

        return (self.mesh.getEdgeInnerProductDeriv(self.sigmaInf)(u) *
                self.sigmaInfDeriv)

    @property
    def MeSigmaInfI(self):
        """
        Inverse of the edge inner product matrix for \\(\\sigmaInf\\).
        """
        if getattr(self, '_MeSigmaInfI', None) is None:
            self._MeSigmaInfI = self.mesh.getEdgeInnerProduct(self.sigmaInf,
                                                              invMat=True)
        return self._MeSigmaInfI

    # TODO: This should take a vector
    def MeSigmaInfIDeriv(self, u):
        """
        Derivative of :code:`MeSigmaInfI` with respect to the model
        """
        if self.sigmaInfMap is None:
            return Utils.Zero()

        if len(self.sigmaInf.shape) > 1:
            if self.sigmaInf.shape[1] > self.mesh.dim:
                raise NotImplementedError(
                    "Full anisotropy is not implemented for MeSigmaInfIDeriv.")

        dMeSigmaInfI_dI = -self.MeSigmaInfI**2
        dMe_dsig = self.mesh.getEdgeInnerProductDeriv(self.sigmaInf)(u)
        return dMeSigmaInfI_dI * (dMe_dsig * self.sigmaInfDeriv)

    @property
    def MeSigma0(self):
        """
        Edge inner product matrix for \\(\\sigma0\\).
        Used in the E-B formulation
        """
        if getattr(self, '_MeSigma0', None) is None:
            self._MeSigma0 = self.mesh.getEdgeInnerProduct(self.sigmaInf *
                                                           (1. - self.eta))
        return self._MeSigma0

    @property
    def MeSigma0(self):
        """
        Edge inner product matrix for \\(\\sigma0\\).
        Used in the E-B formulation
        """
        if getattr(self, '_MeSigma0', None) is None:
            self._MeSigma0 = self.mesh.getEdgeInnerProduct(self.sigmaInf *
                                                           (1. - self.eta))
Esempio n. 16
0
class BaseIPProblem(BaseEMProblem):

    sigma = Props.PhysicalProperty("Electrical conductivity (S/m)")

    rho = Props.PhysicalProperty("Electrical resistivity (Ohm m)")

    Props.Reciprocal(sigma, rho)

    eta, etaMap, etaDeriv = Props.Invertible("Electrical Chargeability")

    surveyPair = Survey
    fieldsPair = FieldsDC
    Ainv = None
    _f = None
    storeJ = False
    _Jmatrix = None
    sign = None

    def fields(self, m):
        if self.verbose is True:
            print(">> Compute fields")

        if self._f is None:
            self._f = self.fieldsPair(self.mesh, self.survey)
            if self.Ainv is None:
                A = self.getA()
                self.Ainv = self.Solver(A, **self.solverOpts)
            RHS = self.getRHS()
            u = self.Ainv * RHS
            Srcs = self.survey.srcList
            self._f[Srcs, self._solutionType] = u
        return self._f

    def getJ(self, m, f=None):
        """
            Generate Full sensitivity matrix
        """
        self.model = m

        if self.verbose:
            print("Calculating J and storing")

        if self._Jmatrix is not None:
            return self._Jmatrix
        else:

            if f is None:
                f = self.fields(m)
            self._Jmatrix = (self._Jtvec(m, v=None, f=f)).T

            # delete fields after computing sensitivity
            del f
            # Not sure why this is a problem
            # if self._f is not None:
            #     del self._f
            # clean all factorization
            if self.Ainv is not None:
                self.Ainv.clean()

        return self._Jmatrix

    def Jvec(self, m, v, f=None):

        self.model = m

        # When sensitivity matrix J is stored
        if self.storeJ:
            J = self.getJ(m, f=f)
            Jv = Utils.mkvc(np.dot(J, v))
            return self.sign * Jv

        else:

            if f is None:
                f = self.fields(m)

            Jv = []

            for src in self.survey.srcList:
                u_src = f[src, self._solutionType]  # solution vector
                dA_dm_v = self.getADeriv(u_src.flatten(), v, adjoint=False)
                dRHS_dm_v = self.getRHSDeriv(src, v)
                du_dm_v = self.Ainv * (-dA_dm_v + dRHS_dm_v)

                for rx in src.rxList:
                    df_dmFun = getattr(f, '_{0!s}Deriv'.format(rx.projField),
                                       None)
                    df_dm_v = df_dmFun(src, du_dm_v, v, adjoint=False)
                    Jv.append(rx.evalDeriv(src, self.mesh, f, df_dm_v))

            # Conductivity (d u / d log sigma) - EB form
            # Resistivity (d u / d log rho) - HJ form
            return self.sign * np.hstack(Jv)

    def Jtvec(self, m, v, f=None):
        """
            Compute adjoint sensitivity matrix (J^T) and vector (v) product.

        """

        # When sensitivity matrix J is stored
        if self.storeJ:
            J = self.getJ(m, f=f)
            Jtv = Utils.mkvc(np.dot(J.T, v))
            return self.sign * Jtv

        else:
            self.model = m

            if f is None:
                f = self.fields(m)
            return self._Jtvec(m, v=v, f=f)

    def _Jtvec(self, m, v=None, f=None):
        """
            Compute adjoint sensitivity matrix (J^T) and vector (v) product.
            Full J matrix can be computed by inputing v=None
        """

        if v is not None:
            # Ensure v is a data object.
            if not isinstance(v, self.dataPair):
                v = self.dataPair(self.survey, v)
            Jtv = np.zeros(m.size)
        else:
            # This is for forming full sensitivity matrix
            Jtv = np.zeros((self.model.size, self.survey.nD), order='F')
            istrt = int(0)
            iend = int(0)

        for isrc, src in enumerate(self.survey.srcList):
            u_src = f[src, self._solutionType]
            if self.storeJ:
                # TODO: use logging package
                sys.stdout.write(("\r %d / %d") % (isrc + 1, self.survey.nSrc))
                sys.stdout.flush()

            for rx in src.rxList:
                if v is not None:
                    PTv = rx.evalDeriv(
                        src, self.mesh, f, v[src, rx],
                        adjoint=True)  # wrt f, need possibility wrt m
                    df_duTFun = getattr(f, '_{0!s}Deriv'.format(rx.projField),
                                        None)
                    df_duT, df_dmT = df_duTFun(src, None, PTv, adjoint=True)
                    ATinvdf_duT = self.Ainv * df_duT
                    dA_dmT = self.getADeriv(u_src.flatten(),
                                            ATinvdf_duT,
                                            adjoint=True)
                    dRHS_dmT = self.getRHSDeriv(src, ATinvdf_duT, adjoint=True)
                    du_dmT = -dA_dmT + dRHS_dmT
                    Jtv += (df_dmT + du_dmT).astype(float)
                else:
                    P = rx.getP(self.mesh, rx.projGLoc(f)).toarray()
                    ATinvdf_duT = self.Ainv * (P.T)
                    dA_dmT = self.getADeriv(u_src, ATinvdf_duT, adjoint=True)

                    iend = istrt + rx.nD
                    if rx.nD == 1:
                        Jtv[:, istrt] = dA_dmT
                    else:
                        Jtv[:, istrt:iend] = dA_dmT
                    istrt += rx.nD

        # Conductivity ((d u / d log sigma).T) - EB form
        # Resistivity ((d u / d log rho).T) - HJ form

        if v is not None:
            return self.sign * Utils.mkvc(Jtv)
        else:
            return Jtv
        return

    def getSourceTerm(self):
        """
        takes concept of source and turns it into a matrix
        """
        """
        Evaluates the sources, and puts them in matrix form

        :rtype: (numpy.ndarray, numpy.ndarray)
        :return: q (nC or nN, nSrc)
        """

        Srcs = self.survey.srcList

        if self._formulation == 'EB':
            n = self.mesh.nN
            # return NotImplementedError

        elif self._formulation == 'HJ':
            n = self.mesh.nC

        q = np.zeros((n, len(Srcs)))

        for i, src in enumerate(Srcs):
            q[:, i] = src.eval(self)
        return q

    def delete_these_for_sensitivity(self):
        del self._Jmatrix, self._MfRhoI, self._MeSigma

    @property
    def deleteTheseOnModelUpdate(self):
        toDelete = []
        return toDelete

    @property
    def MfRhoDerivMat(self):
        """
        Derivative of MfRho with respect to the model
        """
        if getattr(self, '_MfRhoDerivMat', None) is None:
            drho_dlogrho = Utils.sdiag(self.rho) * self.etaDeriv
            self._MfRhoDerivMat = self.mesh.getFaceInnerProductDeriv(
                np.ones(self.mesh.nC))(np.ones(self.mesh.nF)) * drho_dlogrho
        return self._MfRhoDerivMat

    def MfRhoIDeriv(self, u, v, adjoint=False):
        """
            Derivative of :code:`MfRhoI` with respect to the model.
        """
        dMfRhoI_dI = -self.MfRhoI**2
        if self.storeInnerProduct:
            if adjoint:
                return self.MfRhoDerivMat.T * (Utils.sdiag(u) *
                                               (dMfRhoI_dI.T * v))
            else:
                return dMfRhoI_dI * (Utils.sdiag(u) * (self.MfRhoDerivMat * v))
        else:
            dMf_drho = self.mesh.getFaceInnerProductDeriv(self.rho)(u)
            drho_dlogrho = Utils.sdiag(self.rho) * self.etaDeriv
            if adjoint:
                return drho_dlogrho.T * (dMf_drho.T * (dMfRhoI_dI.T * v))
            else:
                return dMfRhoI_dI * (dMf_drho * (drho_dlogrho * v))

    @property
    def MeSigmaDerivMat(self):
        """
        Derivative of MeSigma with respect to the model
        """

        if getattr(self, '_MeSigmaDerivMat', None) is None:
            dsigma_dlogsigma = Utils.sdiag(self.sigma) * self.etaDeriv
            self._MeSigmaDerivMat = self.mesh.getEdgeInnerProductDeriv(
                np.ones(self.mesh.nC))(np.ones(
                    self.mesh.nE)) * dsigma_dlogsigma
        return self._MeSigmaDerivMat

    # TODO: This should take a vector
    def MeSigmaDeriv(self, u, v, adjoint=False):
        """
        Derivative of MeSigma with respect to the model times a vector (u)
        """
        if self.storeInnerProduct:
            if adjoint:
                return self.MeSigmaDerivMat.T * (Utils.sdiag(u) * v)
            else:
                return Utils.sdiag(u) * (self.MeSigmaDerivMat * v)
        else:
            dsigma_dlogsigma = Utils.sdiag(self.sigma) * self.etaDeriv
            if adjoint:
                return (
                    dsigma_dlogsigma.T *
                    (self.mesh.getEdgeInnerProductDeriv(self.sigma)(u).T * v))
            else:
                return (self.mesh.getEdgeInnerProductDeriv(self.sigma)(u) *
                        (dsigma_dlogsigma * v))
Esempio n. 17
0
class BaseFDEMProblem(BaseEMProblem):
    """
        We start by looking at Maxwell's equations in the electric
        field \\\(\\\mathbf{e}\\\) and the magnetic flux
        density \\\(\\\mathbf{b}\\\)

        .. math ::

            \mathbf{C} \mathbf{e} + i \omega \mathbf{b} = \mathbf{s_m} \\\\
            {\mathbf{C}^{\\top} \mathbf{M_{\mu^{-1}}^f} \mathbf{b} -
            \mathbf{M_{\sigma}^e} \mathbf{e} = \mathbf{s_e}}

        if using the E-B formulation (:code:`Problem3D_e`
        or :code:`Problem3D_b`). Note that in this case,
        :math:`\mathbf{s_e}` is an integrated quantity.

        If we write Maxwell's equations in terms of
        \\\(\\\mathbf{h}\\\) and current density \\\(\\\mathbf{j}\\\)

        .. math ::

            \mathbf{C}^{\\top} \mathbf{M_{\\rho}^f} \mathbf{j} +
            i \omega \mathbf{M_{\mu}^e} \mathbf{h} = \mathbf{s_m} \\\\
            \mathbf{C} \mathbf{h} - \mathbf{j} = \mathbf{s_e}

        if using the H-J formulation (:code:`Problem3D_j` or
        :code:`Problem3D_h`). Note that here, :math:`\mathbf{s_m}` is an
        integrated quantity.

        The problem performs the elimination so that we are solving the system
        for \\\(\\\mathbf{e},\\\mathbf{b},\\\mathbf{j} \\\) or
        \\\(\\\mathbf{h}\\\)

    """

    surveyPair = SurveyFDEM
    fieldsPair = FieldsFDEM

    mu, muMap, muDeriv = Props.Invertible("Magnetic Permeability (H/m)",
                                          default=mu_0)

    mui, muiMap, muiDeriv = Props.Invertible(
        "Inverse Magnetic Permeability (m/H)")

    Props.Reciprocal(mu, mui)

    def fields(self, m=None):
        """
        Solve the forward problem for the fields.

        :param numpy.ndarray m: inversion model (nP,)
        :rtype: numpy.ndarray
        :return f: forward solution
        """

        if m is not None:
            self.model = m

        f = self.fieldsPair(self.mesh, self.survey)

        for freq in self.survey.freqs:
            A = self.getA(freq)
            rhs = self.getRHS(freq)
            Ainv = self.Solver(A, **self.solverOpts)
            u = Ainv * rhs
            Srcs = self.survey.getSrcByFreq(freq)
            f[Srcs, self._solutionType] = u
            Ainv.clean()
        return f

    def Jvec(self, m, v, f=None):
        """
        Sensitivity times a vector.

        :param numpy.ndarray m: inversion model (nP,)
        :param numpy.ndarray v: vector which we take sensitivity product with
            (nP,)
        :param SimPEG.EM.FDEM.FieldsFDEM.FieldsFDEM u: fields object
        :rtype: numpy.ndarray
        :return: Jv (ndata,)
        """

        if f is None:
            f = self.fields(m)

        self.model = m

        # Jv = self.dataPair(self.survey)
        Jv = []

        for freq in self.survey.freqs:
            A = self.getA(freq)
            # create the concept of Ainv (actually a solve)
            Ainv = self.Solver(A, **self.solverOpts)

            for src in self.survey.getSrcByFreq(freq):
                u_src = f[src, self._solutionType]
                dA_dm_v = self.getADeriv(freq, u_src, v, adjoint=False)
                dRHS_dm_v = self.getRHSDeriv(freq, src, v)
                du_dm_v = Ainv * (-dA_dm_v + dRHS_dm_v)

                for rx in src.rxList:
                    Jv.append(
                        rx.evalDeriv(src, self.mesh, f, du_dm_v=du_dm_v, v=v))
            Ainv.clean()
        return np.hstack(Jv)

    def Jtvec(self, m, v, f=None):
        """
        Sensitivity transpose times a vector

        :param numpy.ndarray m: inversion model (nP,)
        :param numpy.ndarray v: vector which we take adjoint product with (nP,)
        :param SimPEG.EM.FDEM.FieldsFDEM.FieldsFDEM u: fields object
        :rtype: numpy.ndarray
        :return: Jv (ndata,)
        """

        if f is None:
            f = self.fields(m)

        self.model = m

        # Ensure v is a data object.
        if not isinstance(v, self.dataPair):
            v = self.dataPair(self.survey, v)

        Jtv = np.zeros(m.size)

        for freq in self.survey.freqs:
            AT = self.getA(freq).T
            ATinv = self.Solver(AT, **self.solverOpts)

            for src in self.survey.getSrcByFreq(freq):
                u_src = f[src, self._solutionType]

                for rx in src.rxList:
                    df_duT, df_dmT = rx.evalDeriv(src,
                                                  self.mesh,
                                                  f,
                                                  v=v[src, rx],
                                                  adjoint=True)

                    ATinvdf_duT = ATinv * df_duT

                    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_dmT = df_dmT + du_dmT

                    # TODO: this should be taken care of by the reciever?
                    if rx.component is 'real':
                        Jtv += np.array(df_dmT, dtype=complex).real
                    elif rx.component is 'imag':
                        Jtv += -np.array(df_dmT, dtype=complex).real
                    else:
                        raise Exception('Must be real or imag')

            ATinv.clean()

        return Utils.mkvc(Jtv)

    def getSourceTerm(self, freq):
        """
        Evaluates the sources for a given frequency and puts them in matrix
        form

        :param float freq: Frequency
        :rtype: tuple
        :return: (s_m, s_e) (nE or nF, nSrc)
        """
        Srcs = self.survey.getSrcByFreq(freq)
        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._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)

        for i, src in enumerate(Srcs):
            smi, sei = src.eval(self)

            s_m[:, i] = s_m[:, i] + smi
            s_e[:, i] = s_e[:, i] + sei

        return s_m, s_e
Esempio n. 18
0
class Vangenuchten_k(BaseHydraulicConductivity):

    Ks, KsMap, KsDeriv = Props.Invertible("Saturated hydraulic conductivity",
                                          default=24.96)

    I, IMap, IDeriv = Props.Invertible("", default=0.5)

    n, nMap, nDeriv = Props.Invertible(
        "measure of the pore-size distribution, >1", default=1.56)

    alpha, alphaMap, alphaDeriv = Props.Invertible(
        "related to the inverse of the air entry suction [L-1], >0",
        default=0.036)

    def _get_params(self):
        alpha = self.alpha
        I = self.I
        n = self.n
        Ks = self.Ks
        m = 1.0 - 1.0 / n
        return Ks, alpha, I, n, m

    def __call__(self, u):
        Ks, alpha, I, n, m = self._get_params()

        P_p, P_n = _get_projections(u)  # Compute the positive/negative domains
        theta_e = 1.0 / ((1.0 + abs(alpha * u)**n)**m)
        f_p = P_p * np.ones(len(u)) * Ks  # ensures scalar Ks works
        f_n = P_n * Ks * theta_e**I * ((1.0 -
                                        (1.0 - theta_e**(1.0 / m))**m)**2)
        return f_p + f_n

    def derivM(self, u):
        """derivative with respect to m

        .. code::

            import sympy as sy

            alpha, u, n, I, Ks, theta_r, theta_s = sy.symbols(
                'alpha u n I Ks theta_r theta_s', real=True
            )

            m = 1.0 - 1.0/n
            theta_e = 1.0 / ((1.0 + sy.functions.Abs(alpha * u) ** n) ** m)

            f_n = Ks * theta_e ** I * (
                (1.0 - (1.0 - theta_e ** (1.0 / m)) ** m) ** 2
            )

            f_n = (
                (
                    theta_s - theta_r
                ) /
                (
                    (1.0 + abs(alpha * u)**n) ** (1.0 - 1.0 / n)
                ) +
                theta_r
            )
        """
        return (self._derivKs(u) + self._derivI(u) + self._derivN(u) +
                self._derivAlpha(u))

    def _derivKs(self, u):
        if self.KsMap is None:
            return Utils.Zero()

        Ks, alpha, I, n, m = self._get_params()
        P_p, P_n = _get_projections(u)  # Compute the positive/negative domains
        theta_e = 1.0 / ((1.0 + abs(alpha * u)**n)**m)
        dKs_dm_p = P_p * self.KsDeriv
        dKs_dm_n = P_n * Utils.sdiag(theta_e**I * (
            (1.0 - (1.0 - theta_e**(1.0 / m))**m)**2)) * self.KsDeriv
        return dKs_dm_p + dKs_dm_n

    def _derivAlpha(self, u):
        if self.alphaMap is None:
            return Utils.Zero()
        Ks, alpha, I, n, m = self._get_params()
        ddm = I * u * n * Ks * abs(alpha * u)**(n - 1) * np.sign(alpha * u) * (
            1.0 / n - 1
        ) * ((abs(alpha * u)**n + 1)**(1.0 / n - 1))**(I - 1) * (
            (1 - 1.0 /
             ((abs(alpha * u)**n + 1)**(1.0 / n - 1))**(1.0 /
                                                        (1.0 / n - 1)))**
            (1 - 1.0 / n) - 1)**2 * (abs(alpha * u)**n + 1)**(1.0 / n - 2) - (
                2 * u * n * Ks * abs(alpha * u)**(n - 1) * np.sign(alpha * u) *
                (1.0 / n - 1) * ((abs(alpha * u)**n + 1)**(1.0 / n - 1))**I *
                ((1 - 1.0 / ((abs(alpha * u)**n + 1)**(1.0 / n - 1))**
                  (1.0 / (1.0 / n - 1)))**(1 - 1.0 / n) - 1) *
                (abs(alpha * u)**n + 1)**(1.0 / n - 2)) / (
                    ((abs(alpha * u)**n + 1)**
                     (1.0 / n - 1))**(1.0 / (1.0 / n - 1) + 1) *
                    (1 - 1.0 / ((abs(alpha * u)**n + 1)**(1.0 / n - 1))**
                     (1.0 / (1.0 / n - 1)))**(1.0 / n))
        ddm[u >= 0] = 0
        dA = Utils.sdiag(ddm) * self.alphaDeriv
        return dA

    def _derivN(self, u):
        if self.nMap is None:
            return Utils.Zero()
        Ks, alpha, I, n, m = self._get_params()
        ddm = 1.0 * I * Ks * (
            1.0 * (abs(alpha * u)**n + 1.0)**(-1.0 + 1.0 / n))**I * (
                (-1.0 + 1.0 / n) * np.log(abs(alpha * u)) * abs(alpha * u)**n /
                (abs(alpha * u)**n + 1.0) -
                1.0 * np.log(abs(alpha * u)**n + 1.0) / n**2) * (
                    -(-(1.0 * (abs(alpha * u)**n + 1.0)**(-1.0 + 1.0 / n))**
                      (1.0 / (1.0 - 1.0 / n)) + 1.0)**(1.0 - 1.0 / n) + 1.0
                )**2 * (abs(alpha * u)**n + 1.0)**(-1.0 + 1.0 / n) * (
                    abs(alpha * u)**n + 1.0)**(1.0 - 1.0 / n) - 2 * Ks * (
                        1.0 *
                        (abs(alpha * u)**n + 1.0)**(-1.0 + 1.0 / n))**I * (
                            -(1.0 *
                              (abs(alpha * u)**n + 1.0)**(-1.0 + 1.0 / n))**
                            (1.0 / (1.0 - 1.0 / n)) + 1.0)**(1.0 - 1.0 / n) * (
                                -(1.0 * (abs(alpha * u)**n + 1.0)**
                                  (-1.0 + 1.0 / n))**(1.0 / (1.0 - 1.0 / n)) *
                                (1.0 - 1.0 / n) *
                                (1.0 *
                                 ((-1.0 + 1.0 / n) * np.log(abs(alpha * u)) *
                                  abs(alpha * u)**n /
                                  (abs(alpha * u)**n + 1.0) - 1.0 *
                                  np.log(abs(alpha * u)**n + 1.0) / n**2) *
                                 (abs(alpha * u)**n + 1.0)**(-1.0 + 1.0 / n) *
                                 (abs(alpha * u)**n + 1.0)**(1.0 - 1.0 / n) /
                                 (1.0 - 1.0 / n) -
                                 1.0 * np.log(1.0 * (abs(alpha * u)**n + 1.0)**
                                              (-1.0 + 1.0 / n)) /
                                 (n**2 * (1.0 - 1.0 / n)**2)) /
                                (-(1.0 * (abs(alpha * u)**n + 1.0)**
                                   (-1.0 + 1.0 / n))**(1.0 /
                                                       (1.0 - 1.0 / n)) + 1.0)
                                + 1.0 * np.log(-(1.0 *
                                                 (abs(alpha * u)**n + 1.0)**
                                                 (-1.0 + 1.0 / n))**
                                               (1.0 / (1.0 - 1.0 / n)) + 1.0) /
                                n**2) * (-(-(1.0 * (abs(alpha * u)**n + 1.0)**
                                             (-1.0 + 1.0 / n))**
                                           (1.0 / (1.0 - 1.0 / n)) + 1.0)**
                                         (1.0 - 1.0 / n) + 1.0)
        ddm[u >= 0] = 0
        dn = Utils.sdiag(ddm) * self.nDeriv
        return dn

    def _derivI(self, u):
        if self.IMap is None:
            return Utils.Zero()
        Ks, alpha, I, n, m = self._get_params()
        ddm = Ks * (1.0 * (abs(alpha * u)**n + 1.0)**(-1.0 + 1.0 / n))**I * (
            -(-(1.0 * (abs(alpha * u)**n + 1.0)**(-1.0 + 1.0 / n))**
              (1.0 / (1.0 - 1.0 / n)) + 1.0)**(1.0 - 1.0 / n) +
            1.0)**2 * np.log(1.0 * (abs(alpha * u)**n + 1.0)**(-1.0 + 1.0 / n))
        ddm[u >= 0] = 0
        dI = Utils.sdiag(ddm) * self.IDeriv
        return dI

    def derivU(self, u):
        Ks, alpha, I, n, m = self._get_params()
        ddm = I * alpha * n * Ks * abs(alpha * u)**(n - 1.0) * np.sign(
            alpha * u) * (1.0 / n - 1.0) * (
                (abs(alpha * u)**n + 1)**(1.0 / n - 1))**(I - 1) * (
                    (1 - 1.0 / ((abs(alpha * u)**n + 1)**(1.0 / n - 1))**
                     (1.0 / (1.0 / n - 1)))**(1 - 1.0 / n) -
                    1)**2 * (abs(alpha * u)**n + 1)**(1.0 / n - 2) - (
                        2 * alpha * n * Ks * abs(alpha * u)**
                        (n - 1) * np.sign(alpha * u) * (1.0 / n - 1) *
                        ((abs(alpha * u)**n + 1)**(1.0 / n - 1))**I *
                        ((1 - 1.0 / ((abs(alpha * u)**n + 1)**(1.0 / n - 1))**
                          (1.0 / (1.0 / n - 1)))**(1 - 1.0 / n) - 1) *
                        (abs(alpha * u)**n + 1)**(1.0 / n - 2)) / (
                            ((abs(alpha * u)**n + 1)**
                             (1.0 / n - 1))**(1.0 / (1.0 / n - 1) + 1) *
                            (1 - 1.0 /
                             ((abs(alpha * u)**n + 1)**(1.0 / n - 1))**
                             (1.0 / (1.0 / n - 1)))**(1.0 / n))
        ddm[u >= 0] = 0
        g = Utils.sdiag(ddm)
        return g
Esempio n. 19
0
class ShortcutExample(Props.HasModel):

    sigma, sigmaMap, sigmaDeriv = Props.Invertible(
        "Electrical conductivity (S/m)")
Esempio n. 20
0
class GravityIntegral(Problem.LinearProblem):

    rho, rhoMap, rhoDeriv = Props.Invertible(
        "Specific density (g/cc)",
        default=1.
    )

    # surveyPair = Survey.LinearSurvey
    forwardOnly = False  # Is TRUE, forward matrix not stored to memory
    actInd = None  #: Active cell indices provided
    rx_type = 'z'
    silent = False
    memory_saving_mode = False
    parallelized = False
    n_cpu = None
    progress_index = -1
    gtgdiag = None

    aa = []

    def __init__(self, mesh, **kwargs):
        Problem.BaseProblem.__init__(self, mesh, **kwargs)

    def fields(self, m):
        self.model = self.rhoMap*m

        if self.forwardOnly:

            # Compute the linear operation without forming the full dense G
            fields = self.Intrgl_Fwr_Op()

            return mkvc(fields)

        else:
            vec = np.dot(self.G, (self.model).astype(np.float32))

            return vec.astype(np.float64)

    def mapping(self):
        """
            Return rhoMap
        """
        return self.rhoMap

    def getJtJdiag(self, m, W=None):
        """
            Return the diagonal of JtJ
        """

        if self.gtgdiag is None:

            if W is None:
                w = np.ones(self.G.shape[1])
            else:
                w = W.diagonal()

            dmudm = self.rhoMap.deriv(m)
            self.gtgdiag = np.zeros(dmudm.shape[1])

            for ii in range(self.G.shape[0]):

                self.gtgdiag += (w[ii]*self.G[ii, :]*dmudm)**2.

        return self.gtgdiag

    def getJ(self, m, f=None):
        """
            Sensitivity matrix
        """
        return self.G

    def Jvec(self, m, v, f=None):
        dmudm = self.rhoMap.deriv(m)
        return self.G.dot(dmudm*v)

    def Jtvec(self, m, v, f=None):
        dmudm = self.rhoMap.deriv(m)
        return dmudm.T * (self.G.T.dot(v))

    @property
    def G(self):
        if not self.ispaired:
            raise Exception('Need to pair!')

        if getattr(self, '_G', None) is None:
            print("Begin linear forward calculation: " + self.rx_type)
            start = time.time()
            self._G = self.Intrgl_Fwr_Op()
            print("Linear forward calculation ended in: " + str(time.time()-start) + " sec")
        return self._G

    def Intrgl_Fwr_Op(self, m=None, rx_type='z'):

        """

        Gravity forward operator in integral form

        flag        = 'z' | 'xyz'

        Return
        _G        = Linear forward modeling operation

        Created on March, 15th 2016

        @author: dominiquef

         """

        if m is not None:
            self.model = self.rhoMap*m

        if getattr(self, 'actInd', None) is not None:

            if self.actInd.dtype == 'bool':
                inds = np.asarray([inds for inds,
                                  elem in enumerate(self.actInd, 1)
                                  if elem], dtype=int) - 1
            else:
                inds = self.actInd

        else:

            inds = np.asarray(range(self.mesh.nC))

        self.nC = len(inds)

        # Create active cell projector
        P = sp.sparse.csr_matrix(
            (np.ones(self.nC), (inds, range(self.nC))),
            shape=(self.mesh.nC, self.nC)
        )

        # Create vectors of nodal location
        # (lower and upper corners for each cell)
        if isinstance(self.mesh, Mesh.TreeMesh):
            # Get upper and lower corners of each cell
            bsw = (self.mesh.gridCC -
                   np.kron(self.mesh.vol.T**(1/3)/2,
                           np.ones(3)).reshape((self.mesh.nC, 3)))
            tne = (self.mesh.gridCC +
                   np.kron(self.mesh.vol.T**(1/3)/2,
                           np.ones(3)).reshape((self.mesh.nC, 3)))

            xn1, xn2 = bsw[:, 0], tne[:, 0]
            yn1, yn2 = bsw[:, 1], tne[:, 1]
            zn1, zn2 = bsw[:, 2], tne[:, 2]

        else:

            xn = self.mesh.vectorNx
            yn = self.mesh.vectorNy
            zn = self.mesh.vectorNz

            yn2, xn2, zn2 = np.meshgrid(yn[1:], xn[1:], zn[1:])
            yn1, xn1, zn1 = np.meshgrid(yn[:-1], xn[:-1], zn[:-1])

        self.Yn = P.T*np.c_[Utils.mkvc(yn1), Utils.mkvc(yn2)]
        self.Xn = P.T*np.c_[Utils.mkvc(xn1), Utils.mkvc(xn2)]
        self.Zn = P.T*np.c_[Utils.mkvc(zn1), Utils.mkvc(zn2)]

        self.rxLoc = self.survey.srcField.rxList[0].locs
        self.nD = int(self.rxLoc.shape[0])

        # if self.n_cpu is None:
        #     self.n_cpu = multiprocessing.cpu_count()

        # Switch to determine if the process has to be run in parallel
        job = Forward(
                rxLoc=self.rxLoc, Xn=self.Xn, Yn=self.Yn, Zn=self.Zn,
                n_cpu=self.n_cpu, forwardOnly=self.forwardOnly,
                model=self.model, rx_type=self.rx_type,
                parallelized=self.parallelized
                )

        G = job.calculate()

        return G

    @property
    def mapPair(self):
        """
            Call for general mapping of the problem
        """
        return self.rhoMap
Esempio n. 21
0
class BaseIPProblem(BaseEMProblem):

    sigma = Props.PhysicalProperty("Electrical conductivity (S/m)")

    rho = Props.PhysicalProperty("Electrical resistivity (Ohm m)")

    Props.Reciprocal(sigma, rho)

    eta, etaMap, etaDeriv = Props.Invertible("Electrical Chargeability")

    surveyPair = Survey
    fieldsPair = FieldsDC
    Ainv = None
    f = None
    Ainv = None

    def fields(self, m):
        if m is not None:
            self.model = m
        if self.f is None:
            self.f = self.fieldsPair(self.mesh, self.survey)
            if self.Ainv is None:
                A = self.getA()
                self.Ainv = self.Solver(A, **self.solverOpts)
            RHS = self.getRHS()
            u = self.Ainv * RHS
            Srcs = self.survey.srcList
            self.f[Srcs, self._solutionType] = u
        return self.f

    def Jvec(self, m, v, f=None):

        if f is None:
            f = self.fields(m)

        self.model = m

        Jv = []
        A = self.getA()

        for src in self.survey.srcList:
            u_src = f[src, self._solutionType]  # solution vector
            dA_dm_v = self.getADeriv(u_src, v)
            dRHS_dm_v = self.getRHSDeriv(src, v)
            du_dm_v = self.Ainv * (-dA_dm_v + dRHS_dm_v)

            for rx in src.rxList:
                df_dmFun = getattr(f, '_{0!s}Deriv'.format(rx.projField), None)
                df_dm_v = df_dmFun(src, du_dm_v, v, adjoint=False)
                # Jv[src, rx] = rx.evalDeriv(src, self.mesh, f, df_dm_v)
                Jv.append(rx.evalDeriv(src, self.mesh, f, df_dm_v))
        # Conductivity (d u / d log sigma)
        if self._formulation == 'EB':
            # return -Utils.mkvc(Jv)
            return -np.hstack(Jv)
        # Conductivity (d u / d log rho)
        if self._formulation == 'HJ':
            # return Utils.mkvc(Jv)
            return np.hstack(Jv)

    def Jtvec(self, m, v, f=None):
        if f is None:
            f = self.fields(m)

        self.model = m

        # Ensure v is a data object.
        if not isinstance(v, self.dataPair):
            v = self.dataPair(self.survey, v)

        Jtv = np.zeros(m.size)
        AT = self.getA()

        for src in self.survey.srcList:
            u_src = f[src, self._solutionType]
            for rx in src.rxList:
                PTv = rx.evalDeriv(
                    src, self.mesh, f, v[src, rx],
                    adjoint=True)  # wrt f, need possibility wrt m
                df_duTFun = getattr(f, '_{0!s}Deriv'.format(rx.projField),
                                    None)
                df_duT, df_dmT = df_duTFun(src, None, PTv, adjoint=True)
                ATinvdf_duT = self.Ainv * df_duT
                dA_dmT = self.getADeriv(u_src, ATinvdf_duT, adjoint=True)
                dRHS_dmT = self.getRHSDeriv(src, ATinvdf_duT, adjoint=True)
                du_dmT = -dA_dmT + dRHS_dmT
                Jtv += (df_dmT + du_dmT).astype(float)
        # Conductivity ((d u / d log sigma).T)
        if self._formulation == 'EB':
            return -Utils.mkvc(Jtv)
        # Conductivity ((d u / d log rho).T)
        if self._formulation == 'HJ':
            return Utils.mkvc(Jtv)

    def getSourceTerm(self):
        """
        takes concept of source and turns it into a matrix
        """
        """
        Evaluates the sources, and puts them in matrix form

        :rtype: (numpy.ndarray, numpy.ndarray)
        :return: q (nC or nN, nSrc)
        """

        Srcs = self.survey.srcList

        if self._formulation == 'EB':
            n = self.mesh.nN
            # return NotImplementedError

        elif self._formulation == 'HJ':
            n = self.mesh.nC

        q = np.zeros((n, len(Srcs)))

        for i, src in enumerate(Srcs):
            q[:, i] = src.eval(self)
        return q

    @property
    def deleteTheseOnModelUpdate(self):
        toDelete = []
        return toDelete

    # assume log rho or log cond
    @property
    def MeSigma(self):
        """
            Edge inner product matrix for \\(\\sigma\\).
            Used in the E-B formulation
        """
        if getattr(self, '_MeSigma', None) is None:
            self._MeSigma = self.mesh.getEdgeInnerProduct(self.sigma)
        return self._MeSigma

    @property
    def MfRhoI(self):
        """
            Inverse of :code:`MfRho`
        """
        if getattr(self, '_MfRhoI', None) is None:
            self._MfRhoI = self.mesh.getFaceInnerProduct(self.rho, invMat=True)
        return self._MfRhoI

    def MfRhoIDeriv(self, u):
        """
            Derivative of :code:`MfRhoI` with respect to the model.
        """

        dMfRhoI_dI = -self.MfRhoI**2
        dMf_drho = self.mesh.getFaceInnerProductDeriv(self.rho)(u)
        drho_dlogrho = Utils.sdiag(self.rho) * self.etaDeriv
        return dMfRhoI_dI * (dMf_drho * drho_dlogrho)

    # TODO: This should take a vector
    def MeSigmaDeriv(self, u):
        """
            Derivative of MeSigma with respect to the model
        """
        dsigma_dlogsigma = Utils.sdiag(self.sigma) * self.etaDeriv
        return self.mesh.getEdgeInnerProductDeriv(
            self.sigma)(u) * dsigma_dlogsigma
Esempio n. 22
0
class DebyeDecProblem(Problem.BaseProblem):

    sigmaInf, sigmaInfMap, sigmaInfDeriv = Props.Invertible(
        "Scalar, Conductivity at infinite frequency (S/m)"
    )
    eta, etaMap, etaDeriv = Props.Invertible(
        "Array, Chargeability (V/V)"
    )

    tau = Props.PhysicalProperty(
        "Array, Time constant (s)",
        default=0.1
    )

    nfreq = None
    ntau = None
    G = None
    frequency = None
    tau = None
    f = None
    InvertOnlyEta = False

    def __init__(self, mesh, **kwargs):
        Problem.BaseProblem.__init__(self, mesh, **kwargs)
        self.omega = 2*np.pi*self.frequency
        self.nfreq = self.frequency.size
        self.tau = self.mesh.gridN
        self.ntau = self.mesh.nN
        if self.sigmaInfMap == None:
            self.InvertOnlyEta = True
            print ("Assume sigmaInf is known")

    @property
    def X(self):
        """
        Denominator in Cole-Cole model
        """
        if getattr(self, '_X', None) is None:
            X = np.zeros((self.nfreq, self.ntau), dtype=complex)
            for itau in range(self.ntau):
                X[:,itau] = 1./(1.+(1.-self.eta[itau])*(1j*self.omega*self.tau[itau]))
            self._X = X
        return self._X


    def fields(self, m=None):
        if m is not None:
            self._X = None
            self.model = m
        f = self.sigmaInf - self.sigmaInf*(np.dot(self.X, self.eta))
        self.f = f
        return f

    def get_petaImpulse(self, time, m):
        etas = m
        taus = self.tau
        b = -1. / ((1.-etas)*taus)
        a = -etas*b
        t_temp = np.atleast_2d(time).T
        temp = np.exp(np.dot(t_temp, np.atleast_2d(b)))
        out = np.dot(temp, a)

        return out

    def get_petaStepon(self, time, m):
        etas = m
        taus = self.tau
        b = -1. / ((1.-etas)*taus)
        t_temp = np.atleast_2d(time).T
        temp = np.exp(np.dot(t_temp, np.atleast_2d(b)))
        out = np.dot(temp, etas)
        return out

    def get_Expb(self, time, m):
        etas = m
        taus = self.tau
        b = -1. / ((1.-etas)*taus)
        e = etas / b
        t_temp = np.atleast_2d(time).T
        temp = 1.-np.exp(np.dot(t_temp, np.atleast_2d(b)))
        out = np.dot(temp, e)
        return out

    def dsig_dm(self, v, adjoint=False):

        if not adjoint:

            deta_dm_v = self.etaDeriv*v
            if self.InvertOnlyEta:
                return self.dsig_deta(deta_dm_v)
            else:
                dsigmaInf_dm_v = self.sigmaInfDeriv*v
                return self.dsig_dsigmaInf(dsigmaInf_dm_v) + self.dsig_deta(deta_dm_v)

        elif adjoint:

            dsig_detaT_v = self.dsig_deta(v, adjoint=adjoint)
            dsig_dm_v = self.etaDeriv.T * dsig_detaT_v

            if not self.InvertOnlyEta:

                dsig_dsigmaInfT_v = self.dsig_dsigmaInf(v, adjoint=adjoint)
                dsig_dm_v += self.sigmaInfDeriv.T * dsig_dsigmaInfT_v

            return dsig_dm_v

    def dsig_deta(self, v, adjoint=False):
        """
            NxM matrix vec
            I*eta*omega*tau/(I*omega*tau*(-eta + 1) + 1)**2 + 1/(I*omega*tau*(-eta + 1) + 1)
        """
        if not adjoint:
            dsig_deta_v = -self.sigmaInf*np.dot(self.X, v)
            temp_v = Utils.sdiag(self.tau*self.eta) * v
            dsig_deta_v -= np.dot(Utils.sdiag(self.sigmaInf*1j*self.omega)*self.X**2, temp_v)
            return dsig_deta_v

        elif adjoint:
            dsig_detaT_v = -self.sigmaInf*np.dot(self.X.conj().T, v)
            tempa_v = Utils.sdiag(self.sigmaInf*1j*self.omega).conj() * v
            temp_v = np.dot((self.X**2).conj().T, tempa_v)
            dsig_detaT_v -= Utils.sdiag(self.tau*self.eta) * temp_v
            return dsig_detaT_v.real

    def dsig_dsigmaInf(self, v, adjoint=False):
        """
            Nx1 matrix vec
        """
        if not adjoint:
            dsig_dsigmaInf_v = (1.-np.dot(self.X, self.eta)) * v
            return dsig_dsigmaInf_v

        elif adjoint:
            e = np.ones_like(v)
            dsig_dsigmaInfT_v = np.dot(e, v) - np.dot(self.eta, np.dot(self.X.conj().T, v))
            return np.r_[dsig_dsigmaInfT_v.real]

    def Jvec(self, m, v, f=None):
        if f is None:
            f = self.fields(m=m)
        dsig_dm_v = self.dsig_dm(v)
        Jv = self.survey.evalDeriv(dsig_dm_v, f, adjoint=False)
        return Jv

    def Jtvec(self, m, v, f=None):
        if f is None:
            f = self.fields(m=m)
        dP_dsigT_v = self.survey.evalDeriv(v, f, adjoint=True)
        Jtv = self.dsig_dm(dP_dsigT_v, adjoint=True)
        return Jtv
Esempio n. 23
0
class BaseIPProblem_2D(BaseDCProblem_2D):

    sigma = Props.PhysicalProperty(
        "Electrical conductivity (S/m)"
    )

    rho = Props.PhysicalProperty(
        "Electrical resistivity (Ohm m)"
    )

    Props.Reciprocal(sigma, rho)

    eta, etaMap, etaDeriv = Props.Invertible(
        "Electrical Chargeability (V/V)"
    )

    surveyPair = Survey
    fieldsPair = Fields_ky
    _Jmatrix = None
    _f = None
    sign = None

    def fields(self, m):
        if self.verbose:
            print(">> Compute DC fields")

        if self._f is None:
            self._f = self.fieldsPair(self.mesh, self.survey)
            Srcs = self.survey.srcList
            for iky in range(self.nky):
                ky = self.kys[iky]
                A = self.getA(ky)
                self.Ainv[iky] = self.Solver(A, **self.solverOpts)
                RHS = self.getRHS(ky)
                u = self.Ainv[iky] * RHS
                self._f[Srcs, self._solutionType, iky] = u
        return self._f

    def Jvec(self, m, v, f=None):
        self.model = m
        J = self.getJ(m, f=f)
        Jv = J.dot(v)
        return self.sign * Jv

    def Jtvec(self, m, v, f=None):
        self.model = m
        J = self.getJ(m, f=f)
        Jtv = J.T.dot(v)
        return self.sign * Jtv

    @property
    def deleteTheseOnModelUpdate(self):
        toDelete = []
        return toDelete

    @property
    def MeSigmaDerivMat(self):
        """
        Derivative of MeSigma with respect to the model
        """
        if getattr(self, '_MeSigmaDerivMat', None) is None:
            dsigma_dlogsigma = Utils.sdiag(self.sigma)*self.etaDeriv
            self._MeSigmaDerivMat = self.mesh.getEdgeInnerProductDeriv(
                np.ones(self.mesh.nC)
            )(np.ones(self.mesh.nE)) * dsigma_dlogsigma
        return self._MeSigmaDerivMat

    # TODO: This should take a vector
    def MeSigmaDeriv(self, u, v, adjoint=False):
        """
        Derivative of MeSigma with respect to the model times a vector (u)
        """
        if self.storeInnerProduct:
            if adjoint:
                return self.MeSigmaDerivMat.T * (Utils.sdiag(u)*v)
            else:
                return Utils.sdiag(u)*(self.MeSigmaDerivMat * v)
        else:
            dsigma_dlogsigma = Utils.sdiag(self.sigma)*self.etaDeriv
            if adjoint:
                return (
                    dsigma_dlogsigma.T * (
                        self.mesh.getEdgeInnerProductDeriv(self.sigma)(u).T * v
                    )
                )
            else:
                return (
                    self.mesh.getEdgeInnerProductDeriv(self.sigma)(u) *
                    (dsigma_dlogsigma * v)
                )

    @property
    def MccRhoiDerivMat(self):
        """
            Derivative of MccRho with respect to the model
        """
        if getattr(self, '_MccRhoiDerivMat', None) is None:
            rho = self.rho
            vol = self.mesh.vol
            drho_dlogrho = Utils.sdiag(rho)*self.etaDeriv
            self._MccRhoiDerivMat = (
                Utils.sdiag(vol*(-1./rho**2))*drho_dlogrho
            )
        return self._MccRhoiDerivMat

    def MccRhoiDeriv(self, u, v, adjoint=False):
        """
            Derivative of :code:`MccRhoi` with respect to the model.
        """
        if len(self.rho.shape) > 1:
            if self.rho.shape[1] > self.mesh.dim:
                raise NotImplementedError(
                    "Full anisotropy is not implemented for MccRhoiDeriv."
                )
        if self.storeInnerProduct:
            if adjoint:
                return self.MccRhoiDerivMat.T * (Utils.sdiag(u) * v)
            else:
                return Utils.sdiag(u) * (self.MccRhoiDerivMat * v)
        else:
            vol = self.mesh.vol
            rho = self.rho
            drho_dlogrho = Utils.sdiag(rho)*self.etaDeriv
            if adjoint:
                return drho_dlogrho.T * (Utils.sdia(u*vol*(-1./rho**2)) * v)
            else:
                return Utils.sdiag(u*vol*(-1./rho**2))*(drho_dlogrho * v)

    @property
    def MnSigmaDerivMat(self):
        """
            Derivative of MnSigma with respect to the model
        """
        if getattr(self, '_MnSigmaDerivMat', None) is None:
            sigma = self.sigma
            vol = self.mesh.vol
            dsigma_dlogsigma = Utils.sdiag(sigma)*self.etaDeriv
            self._MnSigmaDerivMat = (
                self.mesh.aveN2CC.T * Utils.sdiag(vol) * dsigma_dlogsigma
                )
        return self._MnSigmaDerivMat

    def MnSigmaDeriv(self, u, v, adjoint=False):
        """
            Derivative of MnSigma with respect to the model times a vector (u)
        """
        if self.storeInnerProduct:
            if adjoint:
                return self.MnSigmaDerivMat.T * (Utils.sdiag(u)*v)
            else:
                return Utils.sdiag(u)*(self.MnSigmaDerivMat * v)
        else:
            sigma = self.sigma
            vol = self.mesh.vol
            dsigma_dlogsigma = Utils.sdiag(sigma)*self.etaDeriv
            if adjoint:
                return dsigma_dlogsigma.T * (vol * (self.mesh.aveN2CC * (u*v)))
            else:
                dsig_dm_v = dsigma_dlogsigma * v
                return (
                    u * (self.mesh.aveN2CC.T * (vol * dsig_dm_v))
                )
Esempio n. 24
0
class GlobalEM1DProblem(Problem.BaseProblem):
    """
        The GlobalProblem allows you to run a whole bunch of SubProblems,
        potentially in parallel, potentially of different meshes.
        This is handy for working with lots of sources,
    """
    sigma, sigmaMap, sigmaDeriv = Props.Invertible(
        "Electrical conductivity (S/m)")

    h, hMap, hDeriv = Props.Invertible("Receiver Height (m), h > 0", )

    chi = Props.PhysicalProperty("Magnetic susceptibility (H/m)", )

    eta = Props.PhysicalProperty(
        "Electrical chargeability (V/V), 0 <= eta < 1")

    tau = Props.PhysicalProperty("Time constant (s)")

    c = Props.PhysicalProperty("Frequency Dependency, 0 < c < 1")

    _Jmatrix_sigma = None
    _Jmatrix_height = None
    run_simulation = None
    n_cpu = None
    hz = None
    parallel = False
    parallel_jvec_jtvec = False
    verbose = False
    fix_Jmatrix = False
    invert_height = None

    def __init__(self, mesh, **kwargs):
        Utils.setKwargs(self, **kwargs)
        self.mesh = mesh
        if PARALLEL:
            if self.parallel:
                print(">> Use multiprocessing for parallelization")
                if self.n_cpu is None:
                    self.n_cpu = multiprocessing.cpu_count()
                print((">> n_cpu: %i") % (self.n_cpu))
            else:
                print(">> Serial version is used")
        else:
            print(">> Serial version is used")
        if self.hz is None:
            raise Exception("Input vertical thickness hz !")
        if self.hMap is None:
            self.invert_height = False
        else:
            self.invert_height = True

    @property
    def n_layer(self):
        return self.hz.size

    @property
    def n_sounding(self):
        return self.survey.n_sounding

    @property
    def rx_locations(self):
        return self.survey.rx_locations

    @property
    def src_locations(self):
        return self.survey.src_locations

    @property
    def data_index(self):
        return self.survey.data_index

    @property
    def topo(self):
        return self.survey.topo

    @property
    def offset(self):
        return self.survey.offset

    @property
    def a(self):
        return self.survey.a

    @property
    def I(self):
        return self.survey.I

    @property
    def field_type(self):
        return self.survey.field_type

    @property
    def rx_type(self):
        return self.survey.rx_type

    @property
    def src_type(self):
        return self.survey.src_type

    @property
    def half_switch(self):
        return self.survey.half_switch

    @property
    def Sigma(self):
        if getattr(self, '_Sigma', None) is None:
            # Ordering: first z then x
            self._Sigma = self.sigma.reshape((self.n_sounding, self.n_layer))
        return self._Sigma

    @property
    def Chi(self):
        if getattr(self, '_Chi', None) is None:
            # Ordering: first z then x
            if self.chi is None:
                self._Chi = np.zeros((self.n_sounding, self.n_layer),
                                     dtype=float,
                                     order='C')
            else:
                self._Chi = self.chi.reshape((self.n_sounding, self.n_layer))
        return self._Chi

    @property
    def Eta(self):
        if getattr(self, '_Eta', None) is None:
            # Ordering: first z then x
            if self.eta is None:
                self._Eta = np.zeros((self.n_sounding, self.n_layer),
                                     dtype=float,
                                     order='C')
            else:
                self._Eta = self.eta.reshape((self.n_sounding, self.n_layer))
        return self._Eta

    @property
    def Tau(self):
        if getattr(self, '_Tau', None) is None:
            # Ordering: first z then x
            if self.tau is None:
                self._Tau = 1e-3 * np.ones(
                    (self.n_sounding, self.n_layer), dtype=float, order='C')
            else:
                self._Tau = self.tau.reshape((self.n_sounding, self.n_layer))
        return self._Tau

    @property
    def C(self):
        if getattr(self, '_C', None) is None:
            # Ordering: first z then x
            if self.c is None:
                self._C = np.ones((self.n_sounding, self.n_layer),
                                  dtype=float,
                                  order='C')
            else:
                self._C = self.c.reshape((self.n_sounding, self.n_layer))
        return self._C

    @property
    def H(self):
        if self.hMap is None:
            return np.ones(self.n_sounding)
        else:
            return self.h

    def fields(self, m):
        if self.verbose:
            print("Compute fields")
        self.survey._pred = self.forward(m)
        return []

    def forward(self, m):
        self.model = m

        if self.verbose:
            print(">> Compute response")

        if self.parallel:
            pool = Pool(self.n_cpu)
            # This assumes the same # of layer for each of soundings
            result = pool.map(self.run_simulation, [
                self.input_args(i, jac_switch='forward')
                for i in range(self.n_sounding)
            ])
            pool.close()
            pool.join()
        else:
            result = [
                self.run_simulation(self.input_args(i, jac_switch='forward'))
                for i in range(self.n_sounding)
            ]
        return np.hstack(result)

    def getJ_sigma(self, m):
        """
             Compute d F / d sigma
        """
        if self._Jmatrix_sigma is not None:
            return self._Jmatrix_sigma
        if self.verbose:
            print(">> Compute J sigma")
        self.model = m
        if self.parallel:
            pool = Pool(self.n_cpu)
            self._Jmatrix_sigma = pool.map(self.run_simulation, [
                self.input_args(i, jac_switch='sensitivity_sigma')
                for i in range(self.n_sounding)
            ])
            pool.close()
            pool.join()
            if self.parallel_jvec_jtvec is False:
                self._Jmatrix_sigma = sp.block_diag(
                    self._Jmatrix_sigma).tocsr()
        else:
            # _Jmatrix_sigma is block diagnoal matrix (sparse)
            self._Jmatrix_sigma = sp.block_diag([
                self.run_simulation(
                    self.input_args(i, jac_switch='sensitivity_sigma'))
                for i in range(self.n_sounding)
            ]).tocsr()
        return self._Jmatrix_sigma

    def getJ_height(self, m):
        """
             Compute d F / d height
        """
        if self.hMap is None:
            return Utils.Zero()

        if self._Jmatrix_height is not None:
            return self._Jmatrix_height
        if self.verbose:
            print(">> Compute J height")

        self.model = m

        if self.parallel:
            pool = Pool(self.n_cpu)
            self._Jmatrix_height = pool.map(self.run_simulation, [
                self.input_args(i, jac_switch="sensitivity_height")
                for i in range(self.n_sounding)
            ])
            pool.close()
            pool.join()
            if self.parallel_jvec_jtvec is False:
                self._Jmatrix_height = sp.block_diag(
                    self._Jmatrix_height).tocsr()
        else:
            self._Jmatrix_height = sp.block_diag([
                self.run_simulation(
                    self.input_args(i, jac_switch='sensitivity_height'))
                for i in range(self.n_sounding)
            ]).tocsr()
        return self._Jmatrix_height

    def Jvec(self, m, v, f=None):
        J_sigma = self.getJ_sigma(m)
        J_height = self.getJ_height(m)
        # This is deprecated at the moment
        # if self.parallel and self.parallel_jvec_jtvec:
        #     # Extra division of sigma is because:
        #     # J_sigma = dF/dlog(sigma)
        #     # And here sigmaMap also includes ExpMap
        #     v_sigma = Utils.sdiag(1./self.sigma) * self.sigmaMap.deriv(m, v)
        #     V_sigma = v_sigma.reshape((self.n_sounding, self.n_layer))

        #     pool = Pool(self.n_cpu)
        #     Jv = np.hstack(
        #         pool.map(
        #             dot,
        #             [(J_sigma[i], V_sigma[i, :]) for i in range(self.n_sounding)]
        #         )
        #     )
        #     if self.hMap is not None:
        #         v_height = self.hMap.deriv(m, v)
        #         V_height = v_height.reshape((self.n_sounding, self.n_layer))
        #         Jv += np.hstack(
        #             pool.map(
        #                 dot,
        #                 [(J_height[i], V_height[i, :]) for i in range(self.n_sounding)]
        #             )
        #         )
        #     pool.close()
        #     pool.join()
        # else:
        Jv = J_sigma * (Utils.sdiag(1. / self.sigma) * (self.sigmaDeriv * v))
        if self.hMap is not None:
            Jv += J_height * (self.hDeriv * v)
        return Jv

    def Jtvec(self, m, v, f=None):
        J_sigma = self.getJ_sigma(m)
        J_height = self.getJ_height(m)
        # This is deprecated at the moment
        # if self.parallel and self.parallel_jvec_jtvec:
        #     pool = Pool(self.n_cpu)
        #     Jtv = np.hstack(
        #         pool.map(
        #             dot,
        #             [(J_sigma[i].T, v[self.data_index[i]]) for i in range(self.n_sounding)]
        #         )
        #     )
        #     if self.hMap is not None:
        #         Jtv_height = np.hstack(
        #             pool.map(
        #                 dot,
        #                 [(J_sigma[i].T, v[self.data_index[i]]) for i in range(self.n_sounding)]
        #             )
        #         )
        #         # This assumes certain order for model, m = (sigma, height)
        #         Jtv = np.hstack((Jtv, Jtv_height))
        #     pool.close()
        #     pool.join()
        #     return Jtv
        # else:
        # Extra division of sigma is because:
        # J_sigma = dF/dlog(sigma)
        # And here sigmaMap also includes ExpMap
        Jtv = self.sigmaDeriv.T * (Utils.sdiag(1. / self.sigma) *
                                   (J_sigma.T * v))
        if self.hMap is not None:
            Jtv += self.hDeriv.T * (J_height.T * v)
        return Jtv

    @property
    def deleteTheseOnModelUpdate(self):
        toDelete = []
        if self.sigmaMap is not None:
            toDelete += ['_Sigma']
        if self.fix_Jmatrix is False:
            if self._Jmatrix_sigma is not None:
                toDelete += ['_Jmatrix_sigma']
            if self._Jmatrix_height is not None:
                toDelete += ['_Jmatrix_height']
        return toDelete
Esempio n. 25
0
class Vangenuchten_theta(BaseWaterRetention):

    theta_r, theta_rMap, theta_rDeriv = Props.Invertible(
        "residual water content [L3L-3]", default=0.078)

    theta_s, theta_sMap, theta_sDeriv = Props.Invertible(
        "saturated water content [L3L-3]", default=0.430)

    n, nMap, nDeriv = Props.Invertible(
        "measure of the pore-size distribution, >1", default=1.56)

    alpha, alphaMap, alphaDeriv = Props.Invertible(
        "related to the inverse of the air entry suction [L-1], >0",
        default=0.036)

    def _get_params(self):
        return self.theta_r, self.theta_s, self.alpha, self.n

    def __call__(self, u):
        theta_r, theta_s, alpha, n = self._get_params()
        f = ((theta_s - theta_r) /
             ((1.0 + abs(alpha * u)**n)**(1.0 - 1.0 / n)) + theta_r)
        if np.isscalar(theta_s):
            f[u >= 0] = theta_s
        else:
            f[u >= 0] = theta_s[u >= 0]

        return f

    def derivM(self, u):
        """derivative with respect to m

        .. code::

            import sympy as sy

            alpha, u, n, I, Ks, theta_r, theta_s = sy.symbols(
                'alpha u n I Ks theta_r theta_s', real=True
            )

            m = 1.0 - 1.0/n
            theta_e = 1.0 / ((1.0 + sy.functions.Abs(alpha * u) ** n) ** m)

            f_n = (
                (
                    theta_s - theta_r
                ) /
                (
                    (1.0 + abs(alpha * u)**n) ** (1.0 - 1.0 / n)
                ) +
                theta_r
            )
        """
        return (self._derivTheta_r(u) + self._derivTheta_s(u) +
                self._derivN(u) + self._derivAlpha(u))

    def _derivTheta_r(self, u):
        if self.theta_rMap is None:
            return Utils.Zero()
        theta_r, theta_s, alpha, n = self._get_params()
        ddm = -(abs(alpha * u)**n + 1.0)**(-1.0 + 1.0 / n) + 1
        ddm[u >= 0] = 0
        dT = Utils.sdiag(ddm) * self.theta_rDeriv
        return dT

    def _derivTheta_s(self, u):
        if self.theta_sMap is None:
            return Utils.Zero()
        theta_r, theta_s, alpha, n = self._get_params()
        P_p, P_n = _get_projections(u)  # Compute the positive/negative domains
        dT_p = P_p * self.theta_sDeriv
        dT_n = P_n * Utils.sdiag(
            (abs(alpha * u)**n + 1.0)**(-1.0 + 1.0 / n)) * self.theta_sDeriv
        return dT_p + dT_n

    def _derivN(self, u):
        if self.nMap is None:
            return Utils.Zero()
        theta_r, theta_s, alpha, n = self._get_params()
        ddm = (-theta_r + theta_s) * (
            (-1.0 + 1.0 / n) * np.log(abs(alpha * u)) * abs(alpha * u)**n /
            (abs(alpha * u)**n + 1.0) - 1.0 * np.log(abs(alpha * u)**n + 1.0) /
            n**2) * (abs(alpha * u)**n + 1.0)**(-1.0 + 1.0 / n)
        ddm[u >= 0] = 0
        dN = Utils.sdiag(ddm) * self.nDeriv
        return dN

    def _derivAlpha(self, u):
        if self.alphaMap is None:
            return Utils.Zero()
        theta_r, theta_s, alpha, n = self._get_params()
        ddm = n * u * (-1.0 + 1.0 / n) * (-theta_r + theta_s) * (
            abs(alpha * u)**n + 1.0)**(-1.0 + 1.0 / n) * abs(
                alpha * u)**n * np.sign(alpha * u) / (
                    (abs(alpha * u)**n + 1.0) * abs(alpha * u))
        ddm[u >= 0] = 0
        dA = Utils.sdiag(ddm) * self.alphaDeriv
        return dA

    def derivU(self, u):
        theta_r, theta_s, alpha, n = self._get_params()
        g = -alpha * n * abs(alpha * u)**(n - 1) * np.sign(
            alpha * u) * (1. / n - 1) * (theta_r - theta_s) * (
                abs(alpha * u)**n + 1)**(1. / n - 2)
        g[u >= 0] = 0
        g = Utils.sdiag(g)
        return g
Esempio n. 26
0
class BaseEMProblem(Problem.BaseProblem):

    sigma, sigmaMap, sigmaDeriv = Props.Invertible(
        "Electrical conductivity (S/m)")

    rho, rhoMap, rhoDeriv = Props.Invertible("Electrical resistivity (Ohm m)")

    Props.Reciprocal(sigma, rho)

    mu = Props.PhysicalProperty("Magnetic Permeability (H/m)", default=mu_0)
    mui = Props.PhysicalProperty("Inverse Magnetic Permeability (m/H)")

    Props.Reciprocal(mu, mui)

    surveyPair = Survey.BaseSurvey  #: The survey to pair with.
    dataPair = Survey.Data  #: The data to pair with.

    mapPair = Maps.IdentityMap  #: Type of mapping to pair with

    Solver = SimpegSolver  #: Type of solver to pair with
    solverOpts = {}  #: Solver options

    verbose = False

    ####################################################
    # Make A Symmetric
    ####################################################
    @property
    def _makeASymmetric(self):
        if getattr(self, '__makeASymmetric', None) is None:
            self.__makeASymmetric = True
        return self.__makeASymmetric

    ####################################################
    # Mass Matrices
    ####################################################
    @property
    def _clear_on_mu_update(self):
        return ['_MeMu', '_MeMuI', '_MfMui', '_MfMuiI']

    @property
    def _clear_on_sigma_update(self):
        return ['_MeSigma', '_MeSigmaI', '_MfRho', '_MfRhoI']

    @property
    def deleteTheseOnModelUpdate(self):
        toDelete = []
        if self.sigmaMap is not None or self.rhoMap is not None:
            toDelete += self._clear_on_sigma_update

        if hasattr(self, 'muMap') or hasattr(self, 'muiMap'):
            if self.muMap is not None or self.muiMap is not None:
                toDelete += self._clear_on_mu_update
        return toDelete

    @properties.observer('mu')
    def _clear_mu_mats_on_mu_update(self, change):
        if change['previous'] is change['value']:
            return
        if (isinstance(change['previous'], np.ndarray)
                and isinstance(change['value'], np.ndarray)
                and np.allclose(change['previous'], change['value'])):
            return
        for mat in self._clear_on_mu_update:
            if hasattr(self, mat):
                delattr(self, mat)

    @properties.observer('mui')
    def _clear_mu_mats_on_mui_update(self, change):
        if change['previous'] is change['value']:
            return
        if (isinstance(change['previous'], np.ndarray)
                and isinstance(change['value'], np.ndarray)
                and np.allclose(change['previous'], change['value'])):
            return
        for mat in self._clear_on_mu_update:
            if hasattr(self, mat):
                delattr(self, mat)

    @properties.observer('sigma')
    def _clear_sigma_mats_on_sigma_update(self, change):
        if change['previous'] is change['value']:
            return
        if (isinstance(change['previous'], np.ndarray)
                and isinstance(change['value'], np.ndarray)
                and np.allclose(change['previous'], change['value'])):
            return
        for mat in self._clear_on_sigma_update:
            if hasattr(self, mat):
                delattr(self, mat)

    @properties.observer('rho')
    def _clear_sigma_mats_on_rho_update(self, change):
        if change['previous'] is change['value']:
            return
        if (isinstance(change['previous'], np.ndarray)
                and isinstance(change['value'], np.ndarray)
                and np.allclose(change['previous'], change['value'])):
            return
        for mat in self._clear_on_sigma_update:
            if hasattr(self, mat):
                delattr(self, mat)

    @property
    def Me(self):
        """
            Edge inner product matrix
        """
        if getattr(self, '_Me', None) is None:
            self._Me = self.mesh.getEdgeInnerProduct()
        return self._Me

    @property
    def MeI(self):
        """
            Edge inner product matrix
        """
        if getattr(self, '_MeI', None) is None:
            self._MeI = self.mesh.getEdgeInnerProduct(invMat=True)
        return self._MeI

    @property
    def Mf(self):
        """
            Face inner product matrix
        """
        if getattr(self, '_Mf', None) is None:
            self._Mf = self.mesh.getFaceInnerProduct()
        return self._Mf

    @property
    def MfI(self):
        """
            Face inner product matrix
        """
        if getattr(self, '_MfI', None) is None:
            self._MfI = self.mesh.getFaceInnerProduct(invMat=True)
        return self._MfI

    @property
    def Vol(self):
        if getattr(self, '_Vol', None) is None:
            self._Vol = Utils.sdiag(self.mesh.vol)
        return self._Vol

    ####################################################
    # Magnetic Permeability
    ####################################################
    @property
    def MfMui(self):
        """
        Face inner product matrix for \\(\\mu^{-1}\\).
        Used in the E-B formulation
        """
        if getattr(self, '_MfMui', None) is None:
            self._MfMui = self.mesh.getFaceInnerProduct(self.mui)
        return self._MfMui

    def MfMuiDeriv(self, u):
        """
        Derivative of :code:`MfMui` with respect to the model.
        """
        if self.muiMap is None:
            return Utils.Zero()

        return (self.mesh.getFaceInnerProductDeriv(self.mui)(u) *
                self.muiDeriv)

    @property
    def MfMuiI(self):
        """
        Inverse of :code:`MfMui`.
        """
        if getattr(self, '_MfMuiI', None) is None:
            self._MfMuiI = self.mesh.getFaceInnerProduct(self.mui, invMat=True)
        return self._MfMuiI

    # TODO: This should take a vector
    def MfMuiIDeriv(self, u):
        """
        Derivative of :code:`MfMui` with respect to the model
        """

        if self.muiMap is None:
            return Utils.Zero()

        if len(self.mui.shape) > 1:
            if self.mui.shape[1] > self.mesh.dim:
                raise NotImplementedError(
                    "Full anisotropy is not implemented for MfMuiIDeriv.")

        dMfMuiI_dI = -self.MfMuiI**2
        dMf_dmui = self.mesh.getEdgeInnerProductDeriv(self.mui)(u)
        return dMfMuiI_dI * (dMf_dmui * self.muiDeriv)

    @property
    def MeMu(self):
        """
        Edge inner product matrix for \\(\\mu\\).
        Used in the H-J formulation
        """
        if getattr(self, '_MeMu', None) is None:
            self._MeMu = self.mesh.getEdgeInnerProduct(self.mu)
        return self._MeMu

    def MeMuDeriv(self, u):
        """
        Derivative of :code:`MeMu` with respect to the model.
        """
        if self.muMap is None:
            return Utils.Zero()

        return (self.mesh.getEdgeInnerProductDeriv(self.mu)(u) * self.muDeriv)

    @property
    def MeMuI(self):
        """
            Inverse of :code:`MeMu`
        """
        if getattr(self, '_MeMuI', None) is None:
            self._MeMuI = self.mesh.getEdgeInnerProduct(self.mu, invMat=True)
        return self._MeMuI

    # TODO: This should take a vector
    def MeMuIDeriv(self, u):
        """
        Derivative of :code:`MeMuI` with respect to the model
        """

        if self.muMap is None:
            return Utils.Zero()

        if len(self.mu.shape) > 1:
            if self.mu.shape[1] > self.mesh.dim:
                raise NotImplementedError(
                    "Full anisotropy is not implemented for MeMuIDeriv.")

        dMeMuI_dI = -self.MeMuI**2
        dMe_dmu = self.mesh.getEdgeInnerProductDeriv(self.mu)(u)
        return dMeMuI_dI * (dMe_dmu * self.muDeriv)

    ####################################################
    # Electrical Conductivity
    ####################################################
    @property
    def MeSigma(self):
        """
        Edge inner product matrix for \\(\\sigma\\).
        Used in the E-B formulation
        """
        if getattr(self, '_MeSigma', None) is None:
            self._MeSigma = self.mesh.getEdgeInnerProduct(self.sigma)
        return self._MeSigma

    # TODO: This should take a vector
    def MeSigmaDeriv(self, u):
        """
        Derivative of MeSigma with respect to the model
        """
        if self.sigmaMap is None:
            return Utils.Zero()

        return (self.mesh.getEdgeInnerProductDeriv(self.sigma)(u) *
                self.sigmaDeriv)

    @property
    def MeSigmaI(self):
        """
        Inverse of the edge inner product matrix for \\(\\sigma\\).
        """
        if getattr(self, '_MeSigmaI', None) is None:
            self._MeSigmaI = self.mesh.getEdgeInnerProduct(self.sigma,
                                                           invMat=True)
        return self._MeSigmaI

    # TODO: This should take a vector
    def MeSigmaIDeriv(self, u):
        """
        Derivative of :code:`MeSigmaI` with respect to the model
        """
        if self.sigmaMap is None:
            return Utils.Zero()

        if len(self.sigma.shape) > 1:
            if self.sigma.shape[1] > self.mesh.dim:
                raise NotImplementedError(
                    "Full anisotropy is not implemented for MeSigmaIDeriv.")

        dMeSigmaI_dI = -self.MeSigmaI**2
        dMe_dsig = self.mesh.getEdgeInnerProductDeriv(self.sigma)(u)
        return dMeSigmaI_dI * (dMe_dsig * self.sigmaDeriv)

    @property
    def MfRho(self):
        """
        Face inner product matrix for \\(\\rho\\). Used in the H-J
        formulation
        """
        if getattr(self, '_MfRho', None) is None:
            self._MfRho = self.mesh.getFaceInnerProduct(self.rho)
        return self._MfRho

    # TODO: This should take a vector
    def MfRhoDeriv(self, u):
        """
        Derivative of :code:`MfRho` with respect to the model.
        """
        if self.rhoMap is None:
            return Utils.Zero()

        return (self.mesh.getFaceInnerProductDeriv(self.rho)(u) *
                self.rhoDeriv)

    @property
    def MfRhoI(self):
        """
        Inverse of :code:`MfRho`
        """
        if getattr(self, '_MfRhoI', None) is None:
            self._MfRhoI = self.mesh.getFaceInnerProduct(self.rho, invMat=True)
        return self._MfRhoI

    # TODO: This should take a vector
    def MfRhoIDeriv(self, u):
        """
            Derivative of :code:`MfRhoI` with respect to the model.
        """
        if self.rhoMap is None:
            return Utils.Zero()

        if len(self.rho.shape) > 1:
            if self.rho.shape[1] > self.mesh.dim:
                raise NotImplementedError(
                    "Full anisotropy is not implemented for MfRhoIDeriv.")

        dMfRhoI_dI = -self.MfRhoI**2
        dMf_drho = self.mesh.getFaceInnerProductDeriv(self.rho)(u)
        return dMfRhoI_dI * (dMf_drho * self.rhoDeriv)
Esempio n. 27
0
class Haverkamp_theta(BaseWaterRetention):

    theta_r, theta_rMap, theta_rDeriv = Props.Invertible(
        "residual water content [L3L-3]", default=0.075)

    theta_s, theta_sMap, theta_sDeriv = Props.Invertible(
        "saturated water content [L3L-3]", default=0.287)

    alpha, alphaMap, alphaDeriv = Props.Invertible("", default=1.611e+06)

    beta, betaMap, betaDeriv = Props.Invertible("", default=3.96)

    def _get_params(self):
        return self.theta_r, self.theta_s, self.alpha, self.beta

    def __call__(self, u):
        theta_r, theta_s, alpha, beta = self._get_params()

        f = (alpha * (theta_s - theta_r) / (alpha + abs(u)**beta) + theta_r)

        if np.isscalar(theta_s):
            f[u >= 0] = theta_s
        else:
            f[u >= 0] = theta_s[u >= 0]
        return f

    def derivM(self, u):
        """derivative with respect to m

        .. code::

            import sympy as sy

            alpha, u, beta, theta_r, theta_s = sy.symbols(
                'alpha u beta theta_r theta_s', real=True
            )

            f_n = (
                alpha *
                (theta_s - theta_r) /
                (alpha + abs(u)**beta) +
                theta_r
            )
        """
        return (self._derivTheta_r(u) + self._derivTheta_s(u) +
                self._derivAlpha(u) + self._derivBeta(u))

    def _derivTheta_r(self, u):
        if self.theta_rMap is None:
            return Utils.Zero()
        theta_r, theta_s, alpha, beta = self._get_params()
        ddm = -alpha / (alpha + abs(u)**beta) + 1
        ddm[u >= 0] = 0
        dT = Utils.sdiag(ddm) * self.theta_rDeriv
        return dT

    def _derivTheta_s(self, u):
        if self.theta_sMap is None:
            return Utils.Zero()
        theta_r, theta_s, alpha, beta = self._get_params()
        P_p, P_n = _get_projections(u)  # Compute the positive/negative domains
        dT_p = P_p * self.theta_sDeriv
        dT_n = P_n * Utils.sdiag(alpha /
                                 (alpha + abs(u)**beta)) * self.theta_sDeriv
        return dT_p + dT_n

    def _derivAlpha(self, u):
        if self.alphaMap is None:
            return Utils.Zero()
        theta_r, theta_s, alpha, beta = self._get_params()
        ddm = -alpha * (-theta_r + theta_s) / (alpha + abs(u)**beta)**2 + (
            -theta_r + theta_s) / (alpha + abs(u)**beta)
        ddm[u >= 0] = 0
        dA = Utils.sdiag(ddm) * self.alphaDeriv
        return dA

    def _derivBeta(self, u):
        if self.betaMap is None:
            return Utils.Zero()
        theta_r, theta_s, alpha, beta = self._get_params()
        ddm = -alpha * (-theta_r + theta_s) * np.log(
            abs(u)) * abs(u)**beta / (alpha + abs(u)**beta)**2
        ddm[u >= 0] = 0
        dN = Utils.sdiag(ddm) * self.betaDeriv
        return dN

    def derivU(self, u):
        theta_r, theta_s, alpha, beta = self._get_params()

        g = (alpha * ((theta_s - theta_r) / (alpha + abs(u)**beta)**2) *
             (-beta * abs(u)**(beta - 1) * np.sign(u)))
        g[u >= 0] = 0
        g = Utils.sdiag(g)
        return g
Esempio n. 28
0
class BaseSIPProblem(BaseEMProblem):

    eta, etaMap, etaDeriv = Props.Invertible("Electrical Chargeability")

    tau, tauMap, tauDeriv = Props.Invertible("time constant", default=0.1)

    taui, tauiMap, tauiDeriv = Props.Invertible("inverse time constant")

    Props.Reciprocal(tau, taui)

    c, cMap, cDeriv = Props.Invertible("frequency dependency", default=1.)

    surveyPair = Survey
    fieldsPair = FieldsDC
    dataPair = Data
    Ainv = None
    sigma = None
    rho = None
    f = None
    Ainv = None

    def DebyeTime(self, t):
        peta = self.eta * np.exp(-self.taui * t)
        return peta

    def EtaDeriv(self, t, v, adjoint=False):
        v = np.array(v, dtype=float)
        if adjoint:
            return self.etaDeriv.T * (np.exp(-self.taui * t) * v)
        else:
            return np.exp(-self.taui * t) * (self.etaDeriv * v)

    def TauiDeriv(self, t, v, adjoint=False):
        v = np.array(v, dtype=float)
        if adjoint:
            return -self.tauiDeriv.T * (self.eta * t * np.exp(-self.taui * t) *
                                        v)
        else:
            return -self.eta * t * np.exp(
                -self.taui * t) * (self.tauiDeriv * v)

    def fields(self, m):
        self.model = m
        if self.f is None:
            self.f = self.fieldsPair(self.mesh, self.survey)
            if self.Ainv is None:
                A = self.getA()
                self.Ainv = self.Solver(A, **self.solverOpts)
            RHS = self.getRHS()
            u = self.Ainv * RHS
            Srcs = self.survey.srcList
            self.f[Srcs, self._solutionType] = u
        return self.f

    def forward(self, m, f=None):

        if f is None:
            f = self.fields(m)

        self.model = m
        Jv = []

        # A = self.getA()
        for tind in range(len(self.survey.times)):
            # Pseudo-chareability
            t = self.survey.times[tind]
            v = self.DebyeTime(t)
            for src in self.survey.srcList:
                u_src = f[src, self._solutionType]  # solution vector
                dA_dm_v = self.getADeriv(u_src, v)
                dRHS_dm_v = self.getRHSDeriv(src, v)
                du_dm_v = self.Ainv * (-dA_dm_v + dRHS_dm_v)
                for rx in src.rxList:
                    timeindex = rx.getTimeP(self.survey.times)
                    if timeindex[tind]:
                        df_dmFun = getattr(f,
                                           '_{0!s}Deriv'.format(rx.projField),
                                           None)
                        df_dm_v = df_dmFun(src, du_dm_v, v, adjoint=False)
                        Jv.append(rx.evalDeriv(src, self.mesh, f, df_dm_v))
                        # Jv[src, rx, t] = rx.evalDeriv(src, self.mesh, f, df_dm_v)

        # Conductivity (d u / d log sigma)
        if self._formulation == 'EB':
            # return -Utils.mkvc(Jv)
            return -np.hstack(Jv)
        # Resistivity (d u / d log rho)
        if self._formulation == 'HJ':
            # return Utils.mkvc(Jv)
            return np.hstack(Jv)

    def Jvec(self, m, v, f=None):

        if f is None:
            f = self.fields(m)

        self.model = m
        Jv = []
        # A = self.getA()
        JvAll = []

        for tind in range(len(self.survey.times)):
            t = self.survey.times[tind]
            v0 = self.EtaDeriv(t, v)
            v1 = self.TauiDeriv(t, v)
            for src in self.survey.srcList:
                u_src = f[src, self._solutionType]  # solution vector
                dA_dm_v0 = self.getADeriv(u_src, v0)
                dRHS_dm_v0 = self.getRHSDeriv(src, v0)
                du_dm_v0 = self.Ainv * (-dA_dm_v0 + dRHS_dm_v0)
                dA_dm_v1 = self.getADeriv(u_src, v1)
                dRHS_dm_v1 = self.getRHSDeriv(src, v1)
                du_dm_v1 = self.Ainv * (-dA_dm_v1 + dRHS_dm_v1)
                for rx in src.rxList:
                    timeindex = rx.getTimeP(self.survey.times)
                    if timeindex[tind]:
                        df_dmFun = getattr(f,
                                           '_{0!s}Deriv'.format(rx.projField),
                                           None)
                        df_dm_v0 = df_dmFun(src, du_dm_v0, v0, adjoint=False)
                        df_dm_v1 = df_dmFun(src, du_dm_v1, v1, adjoint=False)
                        # Jv[src, rx, t] = rx.evalDeriv(src, self.mesh, f, df_dm_v0)
                        # Jv[src, rx, t] += rx.evalDeriv(src, self.mesh, f, df_dm_v1)
                        Jv.append(
                            rx.evalDeriv(src, self.mesh, f, df_dm_v0) +
                            rx.evalDeriv(src, self.mesh, f, df_dm_v1))

        # Conductivity (d u / d log sigma)
        if self._formulation == 'EB':
            # return -Jv.tovec()
            return -np.hstack(Jv)
        # Resistivity (d u / d log rho)
        if self._formulation == 'HJ':
            # return Jv.tovec()
            return np.hstack(Jv)

    def Jtvec(self, m, v, f=None):
        if f is None:
            f = self.fields(m)

        self.model = m

        # Ensure v is a data object.
        if not isinstance(v, self.dataPair):
            v = self.dataPair(self.survey, v)

        Jtv = np.zeros(m.size)

        for tind in range(len(self.survey.times)):
            t = self.survey.times[tind]
            for src in self.survey.srcList:
                u_src = f[src, self._solutionType]
                for rx in src.rxList:
                    timeindex = rx.getTimeP(self.survey.times)
                    if timeindex[tind]:
                        PTv = rx.evalDeriv(
                            src, self.mesh, f, v[src, rx, t],
                            adjoint=True)  # wrt f, need possibility wrt m
                        df_duTFun = getattr(f,
                                            '_{0!s}Deriv'.format(rx.projField),
                                            None)
                        df_duT, df_dmT = df_duTFun(src,
                                                   None,
                                                   PTv,
                                                   adjoint=True)
                        ATinvdf_duT = self.Ainv * df_duT
                        dA_dmT = self.getADeriv(u_src,
                                                ATinvdf_duT,
                                                adjoint=True)
                        dRHS_dmT = self.getRHSDeriv(src,
                                                    ATinvdf_duT,
                                                    adjoint=True)
                        du_dmT = -dA_dmT + dRHS_dmT
                        Jtv += self.EtaDeriv(
                            self.survey.times[tind], du_dmT,
                            adjoint=True) + self.TauiDeriv(
                                self.survey.times[tind], du_dmT, adjoint=True)

        # Conductivity ((d u / d log sigma).T)
        if self._formulation == 'EB':
            return -Jtv
        # Conductivity ((d u / d log rho).T)
        if self._formulation == 'HJ':
            return Jtv

    def getSourceTerm(self):
        """
        takes concept of source and turns it into a matrix
        """
        """
        Evaluates the sources, and puts them in matrix form

        :rtype: (numpy.ndarray, numpy.ndarray)
        :return: q (nC or nN, nSrc)
        """

        Srcs = self.survey.srcList

        if self._formulation == 'EB':
            n = self.mesh.nN
            # return NotImplementedError

        elif self._formulation == 'HJ':
            n = self.mesh.nC

        q = np.zeros((n, len(Srcs)))

        for i, src in enumerate(Srcs):
            q[:, i] = src.eval(self)
        return q

    @property
    def deleteTheseOnModelUpdate(self):
        toDelete = []
        return toDelete

    # assume log rho or log cond
    @property
    def MeSigma(self):
        """
            Edge inner product matrix for \\(\\sigma\\).
            Used in the E-B formulation
        """
        if getattr(self, '_MeSigma', None) is None:
            self._MeSigma = self.mesh.getEdgeInnerProduct(self.sigma)
        return self._MeSigma

    @property
    def MfRhoI(self):
        """
            Inverse of :code:`MfRho`
        """
        if getattr(self, '_MfRhoI', None) is None:
            self._MfRhoI = self.mesh.getFaceInnerProduct(self.rho, invMat=True)
        return self._MfRhoI

    def MfRhoIDeriv(self, u):
        """
            Derivative of :code:`MfRhoI` with respect to the model.
        """

        dMfRhoI_dI = -self.MfRhoI**2
        dMf_drho = self.mesh.getFaceInnerProductDeriv(self.rho)(u)
        drho_dlogrho = Utils.sdiag(self.rho)
        return dMfRhoI_dI * (dMf_drho * drho_dlogrho)

    # TODO: This should take a vector
    def MeSigmaDeriv(self, u):
        """
            Derivative of MeSigma with respect to the model
        """
        dsigma_dlogsigma = Utils.sdiag(self.sigma)
        return self.mesh.getEdgeInnerProductDeriv(
            self.sigma)(u) * dsigma_dlogsigma
Esempio n. 29
0
class EM1D(Problem.BaseProblem):
    """
    Pseudo analytic solutions for frequency and time domain EM problems
    assumingLayered earth (1D).
    """
    surveyPair = BaseEM1DSurvey
    mapPair = Maps.IdentityMap
    WT1 = None
    WT0 = None
    YBASE = None
    chi = None
    jacSwitch = True
    filter_type = 'key_101'
    verbose = False
    fix_Jmatrix = False
    _Jmatrix_sigma = None
    _Jmatrix_height = None
    _pred = None

    sigma, sigmaMap, sigmaDeriv = Props.Invertible(
        "Electrical conductivity at infinite frequency(S/m)")

    chi = Props.PhysicalProperty("Magnetic susceptibility", default=0.)

    eta, etaMap, etaDeriv = Props.Invertible(
        "Electrical chargeability (V/V), 0 <= eta < 1", default=0.)

    tau, tauMap, tauDeriv = Props.Invertible("Time constant (s)", default=1.)

    c, cMap, cDeriv = Props.Invertible("Frequency Dependency, 0 < c < 1",
                                       default=0.5)

    h, hMap, hDeriv = Props.Invertible("Receiver Height (m), h > 0", )

    def __init__(self, mesh, **kwargs):
        Problem.BaseProblem.__init__(self, mesh, **kwargs)

        if self.filter_type == 'key_201':
            if self.verbose:
                print(">> Use Key 201 filter for Hankel Tranform")
            fht = filters.key_201_2009()
            self.WT0 = np.empty(201, complex)
            self.WT1 = np.empty(201, complex)
            self.YBASE = np.empty(201, complex)
            self.WT0 = fht.j0
            self.WT1 = fht.j1
            self.YBASE = fht.base
        elif self.filter_type == 'key_101':
            if self.verbose:
                print(">> Use Key 101 filter for Hankel Tranform")
            fht = filters.key_101_2009()
            self.WT0 = np.empty(101, complex)
            self.WT1 = np.empty(101, complex)
            self.YBASE = np.empty(101, complex)
            self.WT0 = fht.j0
            self.WT1 = fht.j1
            self.YBASE = fht.base
        elif self.filter_type == 'anderson_801':
            if self.verbose:
                print(">> Use Anderson 801 filter for Hankel Tranform")
            fht = filters.anderson_801_1982()
            self.WT0 = np.empty(801, complex)
            self.WT1 = np.empty(801, complex)
            self.YBASE = np.empty(801, complex)
            self.WT0 = fht.j0
            self.WT1 = fht.j1
            self.YBASE = fht.base
        else:
            raise NotImplementedError()

    def hz_kernel_vertical_magnetic_dipole(self,
                                           lamda,
                                           f,
                                           n_layer,
                                           sig,
                                           chi,
                                           depth,
                                           h,
                                           z,
                                           flag,
                                           output_type='response'):
        """
            Kernel for vertical magnetic component (Hz) due to
            vertical magnetic diopole (VMD) source in (kx,ky) domain

        """
        u0 = lamda
        rTE = np.zeros(lamda.size, dtype=complex)
        coefficient_wavenumber = 1 / (4 * np.pi) * lamda**3 / u0

        if output_type == 'sensitivity_sigma':
            drTE = np.zeros((n_layer, lamda.size), dtype=complex)
            drTE = rTEfunjac(n_layer, f, lamda, sig, chi, depth,
                             self.survey.half_switch)
            kernel = drTE * np.exp(-u0 * (z + h)) * coefficient_wavenumber
        else:
            rTE = rTEfunfwd(n_layer, f, lamda, sig, chi, depth,
                            self.survey.half_switch)
            kernel = rTE * np.exp(-u0 * (z + h)) * coefficient_wavenumber
            if output_type == 'sensitivity_height':
                kernel *= -2 * u0

        return kernel

        # Note
        # Here only computes secondary field.
        # I am not sure why it does not work if we add primary term.
        # This term can be analytically evaluated, where h = 0.
        #     kernel = (
        #         1./(4*np.pi) *
        #         (np.exp(u0*(z-h))+rTE * np.exp(-u0*(z+h)))*lamda**3/u0
        #     )

    def hz_kernel_circular_loop(self,
                                lamda,
                                f,
                                n_layer,
                                sig,
                                chi,
                                depth,
                                h,
                                z,
                                I,
                                a,
                                flag,
                                output_type='response'):
        """

        Kernel for vertical magnetic component (Hz) at the center
        due to circular loop source in (kx,ky) domain

        .. math::

            H_z = \\frac{Ia}{2} \int_0^{\infty} [e^{-u_0|z+h|} + \\r_{TE}e^{u_0|z-h|}] \\frac{\lambda^2}{u_0} J_1(\lambda a)] d \lambda

        """

        w = 2 * np.pi * f
        rTE = np.empty(lamda.size, dtype=complex)
        u0 = lamda
        coefficient_wavenumber = I * a * 0.5 * lamda**2 / u0

        if output_type == 'sensitivity_sigma':
            drTE = np.empty((n_layer, lamda.size), dtype=complex)
            drTE = rTEfunjac(n_layer, f, lamda, sig, chi, depth,
                             self.survey.half_switch)
            kernel = drTE * np.exp(-u0 * (z + h)) * coefficient_wavenumber
        else:
            rTE = rTEfunfwd(n_layer, f, lamda, sig, chi, depth,
                            self.survey.half_switch)

            if flag == 'secondary':
                kernel = rTE * np.exp(-u0 * (z + h)) * coefficient_wavenumber
            else:
                kernel = rTE * (np.exp(-u0 * (z + h)) +
                                np.exp(u0 * (z - h))) * coefficient_wavenumber

            if output_type == 'sensitivity_height':
                kernel *= -2 * u0

        return kernel

    def hz_kernel_horizontal_electric_dipole(self,
                                             lamda,
                                             f,
                                             n_layer,
                                             sig,
                                             chi,
                                             depth,
                                             h,
                                             z,
                                             flag,
                                             output_type='response'):
        """
            Kernel for vertical magnetic field (Hz) due to
            horizontal electric diopole (HED) source in (kx,ky) domain

        """
        u0 = lamda
        rTE = np.zeros(lamda.size, dtype=complex)
        coefficient_wavenumber = 1 / (4 * np.pi) * lamda**2 / u0

        if output_type == 'sensitivity_sigma':
            drTE = np.zeros((n_layer, lamda.size), dtype=complex)
            drTE = rTEfunjac(n_layer, f, lamda, sig, chi, depth,
                             self.survey.half_switch)
            kernel = drTE * np.exp(-u0 * (z + h)) * coefficient_wavenumber
        else:
            rTE = rTEfunfwd(n_layer, f, lamda, sig, chi, depth,
                            self.survey.half_switch)
            kernel = rTE * np.exp(-u0 * (z + h)) * coefficient_wavenumber
            if output_type == 'sensitivity_height':
                kernel *= -2 * u0

        return kernel

    def sigma_cole(self, f):
        """
        Computes Pelton's Cole-Cole conductivity model
        in frequency domain.

        Parameter
        ---------

        f: ndarray
            frequency (Hz)

        Return
        ------

        sigma_complex: ndarray
            Cole-Cole conductivity values at given frequencies.

        """
        w = 2 * np.pi * f
        sigma_complex = (self.sigma - self.sigma * self.eta /
                         (1 + (1 - self.eta) * (1j * w * self.tau)**self.c))
        return sigma_complex

    def forward(self, m, output_type='response'):
        """
            Return Bz or dBzdt
        """

        f = self.survey.frequency
        n_frequency = self.survey.n_frequency
        flag = self.survey.field_type
        r = self.survey.offset

        self.model = m

        n_layer = self.survey.n_layer
        depth = self.survey.depth
        nfilt = self.YBASE.size

        # h is an inversion parameter
        if self.hMap is not None:
            h = self.h
        else:
            h = self.survey.h
        z = h + self.survey.dz
        HzFHT = np.empty(n_frequency, dtype=complex)
        chi = self.chi

        if np.isscalar(self.chi):
            chi = np.ones_like(self.sigma) * self.chi

        if output_type == 'response':
            if self.verbose:
                print('>> Compute response')

            # for simulation
            hz = np.empty(nfilt, complex)
            if self.survey.src_type == 'VMD':
                r = self.survey.offset
                for ifreq in range(n_frequency):
                    sig = self.sigma_cole(f[ifreq])
                    hz = self.hz_kernel_vertical_magnetic_dipole(
                        self.YBASE / r[ifreq],
                        f[ifreq],
                        n_layer,
                        sig,
                        chi,
                        depth,
                        h,
                        z,
                        flag,
                        output_type=output_type)
                    HzFHT[ifreq] = np.dot(hz, self.WT0) / r[ifreq]

            elif self.survey.src_type == 'CircularLoop':
                I = self.survey.I
                a = self.survey.a
                for ifreq in range(n_frequency):
                    sig = self.sigma_cole(f[ifreq])
                    hz = self.hz_kernel_circular_loop(self.YBASE / a,
                                                      f[ifreq],
                                                      n_layer,
                                                      sig,
                                                      chi,
                                                      depth,
                                                      h,
                                                      z,
                                                      I,
                                                      a,
                                                      flag,
                                                      output_type=output_type)
                    HzFHT[ifreq] = np.dot(hz, self.WT1) / a

            elif self.survey.src_type == "piecewise_line":
                for ifreq in range(n_frequency):
                    sig = self.sigma_cole(f[ifreq])
                    # Need to compute y
                    hz = self.hz_kernel_horizontal_electric_dipole(
                        self.YBASE / r[ifreq] * y,
                        f[ifreq],
                        n_layer,
                        sig,
                        chi,
                        depth,
                        h,
                        z,
                        I,
                        a,
                        flag,
                        output_type=output_type)
                    HzFHT[ifreq] = np.dot(hz, self.WT1) / a
            else:
                raise Exception("Src options are only VMD or CircularLoop!!")

            return HzFHT

        elif output_type == 'sensitivity_sigma':

            dHzFHT_dsig = np.empty((n_frequency, n_layer), dtype=complex)
            dhz = np.empty((nfilt, n_layer), complex)
            if self.survey.src_type == 'VMD':
                r = self.survey.offset
                for ifreq in range(n_frequency):
                    sig = self.sigma_cole(f[ifreq])
                    dhz = self.hz_kernel_vertical_magnetic_dipole(
                        self.YBASE / r[ifreq],
                        f[ifreq],
                        n_layer,
                        sig,
                        chi,
                        depth,
                        h,
                        z,
                        flag,
                        output_type=output_type)
                    dHzFHT_dsig[ifreq, :] = np.dot(dhz, self.WT0) / r[ifreq]
            elif self.survey.src_type == 'CircularLoop':
                I = self.survey.I
                a = self.survey.a
                for ifreq in range(n_frequency):
                    sig = self.sigma_cole(f[ifreq])
                    dhz = self.hz_kernel_circular_loop(self.YBASE / a,
                                                       f[ifreq],
                                                       n_layer,
                                                       sig,
                                                       chi,
                                                       depth,
                                                       h,
                                                       z,
                                                       I,
                                                       a,
                                                       flag,
                                                       output_type=output_type)
                    dHzFHT_dsig[ifreq, :] = np.dot(dhz, self.WT1) / a
            else:
                raise Exception("Src options are only VMD or CircularLoop!!")

            return dHzFHT_dsig

        elif output_type == 'sensitivity_height':
            dHzFHT_dh = np.empty((n_frequency, 1), dtype=complex)
            dhz = np.empty(nfilt, complex)
            if self.survey.src_type == 'VMD':
                r = self.survey.offset
                for ifreq in range(n_frequency):
                    sig = self.sigma_cole(f[ifreq])
                    dhz = self.hz_kernel_vertical_magnetic_dipole(
                        self.YBASE / r[ifreq],
                        f[ifreq],
                        n_layer,
                        sig,
                        chi,
                        depth,
                        h,
                        z,
                        flag,
                        output_type=output_type)
                    dHzFHT_dh[ifreq] = np.dot(dhz, self.WT0) / r[ifreq]

            elif self.survey.src_type == 'CircularLoop':
                I = self.survey.I
                a = self.survey.a
                for ifreq in range(n_frequency):
                    sig = self.sigma_cole(f[ifreq])
                    dhz = self.hz_kernel_circular_loop(self.YBASE / a,
                                                       f[ifreq],
                                                       n_layer,
                                                       sig,
                                                       chi,
                                                       depth,
                                                       h,
                                                       z,
                                                       I,
                                                       a,
                                                       flag,
                                                       output_type=output_type)
                    dHzFHT_dh[ifreq] = np.dot(dhz, self.WT1) / a
            else:
                raise Exception("Src options are only VMD or CircularLoop!!")

            return dHzFHT_dh

    # @profile
    def fields(self, m):
        f = self.forward(m, output_type='response')
        self.survey._pred = Utils.mkvc(self.survey.projectFields(f))
        return f

    def getJ_height(self, m, f=None):
        """

        """
        if self.hMap is None:
            return Utils.Zero()

        if self._Jmatrix_height is not None:
            return self._Jmatrix_height
        else:

            if self.verbose:
                print(">> Compute J height ")

            dudz = self.forward(m, output_type="sensitivity_height")

            self._Jmatrix_height = (self.survey.projectFields(dudz)).reshape(
                [-1, 1])

            return self._Jmatrix_height

    # @profile
    def getJ_sigma(self, m, f=None):

        if self.sigmaMap is None:
            return Utils.Zero()

        if self._Jmatrix_sigma is not None:
            return self._Jmatrix_sigma
        else:

            if self.verbose:
                print(">> Compute J sigma")

            dudsig = self.forward(m, output_type="sensitivity_sigma")

            self._Jmatrix_sigma = self.survey.projectFields(dudsig)
            if self._Jmatrix_sigma.ndim == 1:
                self._Jmatrix_sigma = self._Jmatrix_sigma.reshape([-1, 1])
            return self._Jmatrix_sigma

    def getJ(self, m, f=None):
        return (self.getJ_sigma(m, f=f) * self.sigmaDeriv +
                self.getJ_height(m, f=f) * self.hDeriv)

    def Jvec(self, m, v, f=None):
        """
            Computing Jacobian^T multiplied by vector.
        """

        J_sigma = self.getJ_sigma(m, f=f)
        J_height = self.getJ_height(m, f=f)

        if self.hMap is None:
            Jv = np.dot(J_sigma, self.sigmaMap.deriv(m, v))
        else:
            Jv = np.dot(J_sigma, self.sigmaMap.deriv(m, v))
            Jv += np.dot(J_height, self.hMap.deriv(m, v))
        return Jv

    def Jtvec(self, m, v, f=None):
        """
            Computing Jacobian^T multiplied by vector.
        """

        J_sigma = self.getJ_sigma(m, f=f)
        J_height = self.getJ_height(m, f=f)
        if self.hMap is None:
            Jtv = self.sigmaMap.deriv(m, np.dot(J_sigma.T, v))
        else:
            Jtv = (self.sigmaDeriv.T * np.dot(J_sigma.T, v))
            Jtv += self.hDeriv.T * np.dot(J_height.T, v)
        return Jtv

    @property
    def deleteTheseOnModelUpdate(self):
        toDelete = []
        if self.fix_Jmatrix is False:
            if self._Jmatrix_sigma is not None:
                toDelete += ['_Jmatrix_sigma']
            if self._Jmatrix_height is not None:
                toDelete += ['_Jmatrix_height']
        return toDelete

    def depth_of_investigation(self, uncert, thres_hold=0.8):
        thres_hold = 0.8
        J = self.getJ(self.model)
        S = np.cumsum(abs(np.dot(J.T, 1. / uncert))[::-1])[::-1]
        active = S - 0.8 > 0.
        doi = abs(self.survey.depth[active]).max()
        return doi, active

    def get_threshold(self, uncert):
        _, active = self.depth_of_investigation(uncert)
        JtJdiag = self.get_JtJdiag(uncert)
        delta = JtJdiag[active].min()
        return delta

    def get_JtJdiag(self, uncert):
        J = self.getJ(self.model)
        JtJdiag = ((Utils.sdiag(1. / uncert) * J)**2).sum(axis=0)
        return JtJdiag
Esempio n. 30
0
class Problem3D_Diff(Problem.BaseProblem):
    """
        Gravity in differential equations!
    """

    _depreciate_main_map = 'rhoMap'

    rho, rhoMap, rhoDeriv = Props.Invertible(
        "Specific density (g/cc)",
        default=1.
    )

    solver = None

    def __init__(self, mesh, **kwargs):
        Problem.BaseProblem.__init__(self, mesh, **kwargs)

        self.mesh.setCellGradBC('dirichlet')

        self._Div = self.mesh.cellGrad

    @property
    def MfI(self): return self._MfI

    @property
    def Mfi(self): return self._Mfi

    def makeMassMatrices(self, m):
        self.model = m
        self._Mfi = self.mesh.getFaceInnerProduct()
        self._MfI = Utils.sdiag(1. / self._Mfi.diagonal())

    def getRHS(self, m):
        """


        """

        Mc = Utils.sdiag(self.mesh.vol)

        self.model = m
        rho = self.rho

        return Mc * rho

    def getA(self, m):
        """
        GetA creates and returns the A matrix for the Gravity nodal problem

        The A matrix has the form:

        .. math ::

            \mathbf{A} =  \Div(\MfMui)^{-1}\Div^{T}

        """
        return -self._Div.T * self.Mfi * self._Div

    def fields(self, m):
        """
            Return magnetic potential (u) and flux (B)
            u: defined on the cell nodes [nC x 1]
            gField: defined on the cell faces [nF x 1]

            After we compute u, then we update B.

            .. math ::

                \mathbf{B}_s = (\MfMui)^{-1}\mathbf{M}^f_{\mu_0^{-1}}\mathbf{B}_0-\mathbf{B}_0 -(\MfMui)^{-1}\Div^T \mathbf{u}

        """
        from scipy.constants import G as NewtG

        self.makeMassMatrices(m)
        A = self.getA(m)
        RHS = self.getRHS(m)

        if self.solver is None:
            m1 = sp.linalg.interface.aslinearoperator(
                Utils.sdiag(1 / A.diagonal())
            )
            u, info = sp.linalg.bicgstab(A, RHS, tol=1e-6, maxiter=1000, M=m1)

        else:
            print("Solving with Paradiso")
            Ainv = self.solver(A)
            u = Ainv * RHS

        gField = 4. * np.pi * NewtG * 1e+8 * self._Div * u

        return {'G': gField, 'u': u}