Beispiel #1
0
    def Jvec(self, m, v, f=None):
        """
        Jvec computes the sensitivity times a vector

        .. math::
            \mathbf{J} \mathbf{v} = \\frac{d\mathbf{P}}{d\mathbf{F}} \left(
            \\frac{d\mathbf{F}}{d\mathbf{u}} \\frac{d\mathbf{u}}{d\mathbf{m}} +
            \\frac{\partial\mathbf{F}}{\partial\mathbf{m}} \\right) \mathbf{v}

        where

        .. math::
            \mathbf{A} \\frac{d\mathbf{u}}{d\mathbf{m}} +
            \\frac{\partial \mathbf{A}(\mathbf{u}, \mathbf{m})}
            {\partial\mathbf{m}} =
            \\frac{d \mathbf{RHS}}{d \mathbf{m}}
        """

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

        ftype = self._fieldType + 'Solution'  # the thing we solved for
        self.model = m

        # mat to store previous time-step's solution deriv times a vector for
        # each source
        # size: nu x nSrc

        # this is a bit silly

        # if self._fieldType is 'b' or self._fieldType is 'j':
        #     ifields = np.zeros((self.mesh.nF, len(Srcs)))
        # elif self._fieldType is 'e' or self._fieldType is 'h':
        #     ifields = np.zeros((self.mesh.nE, len(Srcs)))

        # for i, src in enumerate(self.survey.srcList):
        dun_dm_v = np.hstack([
            Utils.mkvc(
                self.getInitialFieldsDeriv(src, v), 2
            )
            for src in self.survey.srcList
        ]) # can over-write this at each timestep

        # store the field derivs we need to project to calc full deriv
        df_dm_v = Fields_Derivs(self.mesh, self.survey)

        Adiaginv = None

        for tInd, dt in zip(range(self.nT), self.timeSteps):
            # keep factors if dt is the same as previous step b/c A will be the
            # same
            if Adiaginv is not None and (tInd > 0 and dt !=
                                         self.timeSteps[tInd - 1]):
                Adiaginv.clean()
                Adiaginv = None

            if Adiaginv is None:
                A = self.getAdiag(tInd)
                Adiaginv = self.Solver(A, **self.solverOpts)

            Asubdiag = self.getAsubdiag(tInd)

            for i, src in enumerate(self.survey.srcList):

                # here, we are lagging by a timestep, so filling in as we go
                for projField in set([rx.projField for rx in src.rxList]):
                    df_dmFun = getattr(f, '_%sDeriv' % projField, None)
                    # df_dm_v is dense, but we only need the times at
                    # (rx.P.T * ones > 0)
                    # This should be called rx.footprint

                    df_dm_v[src, '{}Deriv'.format(projField), tInd] = df_dmFun(
                        tInd, src, dun_dm_v[:, i], v
                        )

                un_src = f[src, ftype, tInd+1]

                # cell centered on time mesh
                dA_dm_v = self.getAdiagDeriv(tInd, un_src, v)
                # on nodes of time mesh
                dRHS_dm_v = self.getRHSDeriv(tInd+1, src, v)

                dAsubdiag_dm_v = self.getAsubdiagDeriv(
                    tInd, f[src, ftype, tInd], v
                )

                JRHS = dRHS_dm_v - dAsubdiag_dm_v - dA_dm_v

                # step in time and overwrite
                if tInd != len(self.timeSteps+1):
                    dun_dm_v[:, i] = Adiaginv * (
                        JRHS - Asubdiag * dun_dm_v[:, i]
                    )

        Jv = []
        for src in self.survey.srcList:
            for rx in src.rxList:
                Jv.append(
                    rx.evalDeriv(src, self.mesh, self.timeMesh, f, Utils.mkvc(
                            df_dm_v[src, '%sDeriv' % rx.projField, :]
                        )
                    )
                )
        Adiaginv.clean()
        # del df_dm_v, dun_dm_v, Asubdiag
        # return Utils.mkvc(Jv)
        return np.hstack(Jv)
Beispiel #2
0
    def Jtvec(self, m, v, f=None):

        """
        Jvec computes the adjoint of the sensitivity times a vector

        .. math::
            \mathbf{J}^\\top \mathbf{v} =  \left(
            \\frac{d\mathbf{u}}{d\mathbf{m}} ^ \\top
            \\frac{d\mathbf{F}}{d\mathbf{u}} ^ \\top  +
            \\frac{\partial\mathbf{F}}{\partial\mathbf{m}} ^ \\top \\right)
            \\frac{d\mathbf{P}}{d\mathbf{F}} ^ \\top \mathbf{v}

        where

        .. math::
            \\frac{d\mathbf{u}}{d\mathbf{m}} ^\\top \mathbf{A}^\\top  +
            \\frac{d\mathbf{A}(\mathbf{u})}{d\mathbf{m}} ^ \\top =
            \\frac{d \mathbf{RHS}}{d \mathbf{m}} ^ \\top
        """

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

        self.model = m
        ftype = self._fieldType + 'Solution'  # the thing we solved for

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

        df_duT_v = Fields_Derivs(self.mesh, self.survey)

        # same size as fields at a single timestep
        ATinv_df_duT_v = np.zeros(
            (
                len(self.survey.srcList),
                len(f[self.survey.srcList[0], ftype, 0])
            ),
            dtype=float
        )
        JTv = np.zeros(m.shape, dtype=float)

        # Loop over sources and receivers to create a fields object:
        # PT_v, df_duT_v, df_dmT_v
        # initialize storage for PT_v (don't need to preserve over sources)
        PT_v = Fields_Derivs(self.mesh, self.survey)
        for src in self.survey.srcList:
            # Looping over initializing field class is appending memory!
            # PT_v = Fields_Derivs(self.mesh, self.survey) # initialize storage
            # #for PT_v (don't need to preserve over sources)
            # initialize size
            df_duT_v[src, '{}Deriv'.format(self._fieldType), :] = (
                np.zeros_like(f[src, self._fieldType, :])
            )

            for rx in src.rxList:
                PT_v[src, '{}Deriv'.format(rx.projField), :] = rx.evalDeriv(
                    src, self.mesh, self.timeMesh, f, Utils.mkvc(v[src, rx]),
                    adjoint=True
                ) # this is +=

                # PT_v = np.reshape(curPT_v,(len(curPT_v)/self.timeMesh.nN,
                # self.timeMesh.nN), order='F')
                df_duTFun = getattr(f, '_{}Deriv'.format(rx.projField), None)

                for tInd in range(self.nT+1):
                    cur = df_duTFun(
                        tInd, src, None, Utils.mkvc(
                            PT_v[src, '{}Deriv'.format(rx.projField), tInd]
                        ),
                        adjoint=True
                    )

                    df_duT_v[src, '{}Deriv'.format(self._fieldType), tInd] = (
                        df_duT_v[src, '{}Deriv'.format(self._fieldType), tInd] +
                        Utils.mkvc(cur[0], 2))
                    JTv = cur[1] + JTv

        del PT_v # no longer need this

        AdiagTinv = None

        # Do the back-solve through time
        # if the previous timestep is the same: no need to refactor the matrix
        # for tInd, dt in zip(range(self.nT), self.timeSteps):

        for tInd in reversed(range(self.nT)):
            # tInd = tIndP - 1
            if AdiagTinv is not None and (
                tInd <= self.nT and
                self.timeSteps[tInd] != self.timeSteps[tInd+1]
            ):
                AdiagTinv.clean()
                AdiagTinv = None

            # refactor if we need to
            if AdiagTinv is None:  # and tInd > -1:
                Adiag = self.getAdiag(tInd)
                AdiagTinv = self.Solver(Adiag.T, **self.solverOpts)

            if tInd < self.nT - 1:
                Asubdiag = self.getAsubdiag(tInd+1)

            for isrc, src in enumerate(self.survey.srcList):

                # solve against df_duT_v
                if tInd >= self.nT-1:
                    # last timestep (first to be solved)
                    ATinv_df_duT_v[isrc, :] = AdiagTinv * df_duT_v[
                        src, '{}Deriv'.format(self._fieldType), tInd+1]
                elif tInd > -1:
                    ATinv_df_duT_v[isrc, :] = AdiagTinv * (
                        Utils.mkvc(df_duT_v[
                            src, '{}Deriv'.format(self._fieldType), tInd+1
                        ]
                    ) - Asubdiag.T * Utils.mkvc(ATinv_df_duT_v[isrc, :]))

                if tInd < self.nT:
                    dAsubdiagT_dm_v = self.getAsubdiagDeriv(
                        tInd, f[src, ftype, tInd], ATinv_df_duT_v[isrc, :],
                        adjoint=True)
                else:
                    dAsubdiagT_dm_v = Utils.Zero()

                dRHST_dm_v = self.getRHSDeriv(
                        tInd+1, src, ATinv_df_duT_v[isrc, :], adjoint=True
                        )  # on nodes of time mesh

                un_src = f[src, ftype, tInd+1]
                # cell centered on time mesh
                dAT_dm_v = self.getAdiagDeriv(
                    tInd, un_src, ATinv_df_duT_v[isrc, :], adjoint=True
                )

                JTv = JTv + Utils.mkvc(
                    -dAT_dm_v - dAsubdiagT_dm_v + dRHST_dm_v
                )

        # del df_duT_v, ATinv_df_duT_v, A, Asubdiag
        if AdiagTinv is not None:
            AdiagTinv.clean()

        return Utils.mkvc(JTv).astype(float)