Example #1
0
    def showFit(self, ax=None, **kwargs):
        """Show the last inversion data and response."""
        ax = self.showData(data=self.inv.dataVals,
                           error=self.inv.errorVals,
                           label='Data',
                           ax=ax,
                           **kwargs)
        ax = self.showData(data=self.inv.response,
                           label='Response',
                           ax=ax,
                           **kwargs)

        if not kwargs.pop('hideFittingAnnotation', False):
            ax.text(0.99,
                    0.005,
                    r"rrms: {0}, $\chi^2$: {1}".format(
                        pf(self.fw.inv.relrms()), pf(self.fw.inv.chi2())),
                    transform=ax.transAxes,
                    horizontalalignment='right',
                    verticalalignment='bottom',
                    fontsize=8)

        if not kwargs.pop('hideLegend', False):
            ax.legend()

        return ax
Example #2
0
    def showProgress(self, style='all'):
        r"""Called if showProgress=True is set for the inversion run.

        TODO
            *Discuss .. its a useful function but breaks a little
                the FrameWork work only concept.
        """
        if self.axs is None:
            axs = None
            if style == 'all' or style == True:
                fig, axs = pg.plt.subplots(1, 2)
            elif style == 'Model':
                fig, axs = pg.plt.subplots(1, 1)
            self.axs = axs
        ax = self.axs

        if style == 'Model':
            for other_ax in ax.figure.axes:
                # pg._y(type(other_ax).mro())
                if type(other_ax).mro()[0] == type(ax):
                    # only clear Axes not Colorbars
                    other_ax.clear()

            self.fop.drawModel(ax, self.inv.model())
        else:
            # for other_ax in ax[0].figure.axes:
            #     other_ax.clear()
            for _ax in self.axs:
                _ax.clear()
                try:
                    pg.viewer.mpl.twin(_ax).clear()
                except:
                    pass

            self.fop.drawModel(ax[0], self.inv.model(), label='Model')
            self.fop.drawData(ax[1],
                              self._dataVals,
                              self._errorVals,
                              label='Data')
            self.fop.drawData(ax[1], self.inv.response(), label='Response')

            ax[1].text(0.99,
                       0.005,
                       "Iter: {0}, rrms: {1}%, $\chi^2$: {2}".format(
                           self.inv.iter(), pf(self.inv.relrms()),
                           pf(self.inv.chi2())),
                       transform=ax[1].transAxes,
                       horizontalalignment='right',
                       verticalalignment='bottom',
                       fontsize=8)

            ax[1].figure.tight_layout()
        pg.plt.pause(0.05)
Example #3
0
    def run(self, dataVals, errorVals, **kwargs):
        """Run inversion.

        The inversion will always start from the starting model taken from
        the forward operator.
        If you want to run the inversion from a specified prior model,
        e.g., from a other run, set this model as starting model to the FOP
        (fop.setStartModel).
        Any self.inv.setModel() settings will be overwritten.

        Parameters
        ----------
        dataVals : iterable
            Data values
        errorVals : iterable
            Relative error values. dv / v

        Keyword Arguments
        -----------------
        maxIter : int
            Overwrite class settings for maximal iterations number.
        dPhi : float [1]
            Overwrite class settings for delta data phi aborting criteria.
            Default is 1%
        """
        self.reset()
        if self.isFrameWork:
            pg.critical('in use?')
            return self._inv.run(dataVals, errorVals, **kwargs)

        if self.fop is None:
            raise Exception(
                "Need a valid forward operator for the inversion run.")

        maxIter = kwargs.pop('maxIter', self.maxIter)
        minDPhi = kwargs.pop('dPhi', self.minDPhi)

        self.verbose = kwargs.pop('verbose', self.verbose)
        self.debug = kwargs.pop('debug', self.debug)
        self.robustData = kwargs.pop('robustData', False)

        lam = kwargs.pop('lam', 20)

        progress = kwargs.pop('progress', None)
        showProgress = kwargs.pop('showProgress', False)

        self.inv.setTransModel(self.fop.modelTrans)

        self.dataVals = dataVals
        self.errorVals = errorVals

        sm = kwargs.pop('startModel', None)
        if sm is not None:
            self.startModel = sm

        self.inv.setData(self._dataVals)
        self.inv.setRelativeError(self._errorVals)
        self.inv.setLambda(lam)

        # temporary set max iter to one for the initial run call
        maxIterTmp = self.maxIter
        self.maxIter = 1

        if self.verbose:
            pg.info('Starting inversion.')
            print("fop:", self.inv.fop())
            if isinstance(self.dataTrans, pg.trans.TransCumulative):
                print("Data transformation (cumulative):")
                for i in range(self.dataTrans.size()):
                    print("\t", i, self.dataTrans.at(i))
            else:
                print("Data transformation:", self.dataTrans)
            if isinstance(self.modelTrans, pg.trans.TransCumulative):
                print("Model transformation (cumulative):")
                for i in range(self.modelTrans.size()):
                    if i < 10:
                        print("\t", i, self.modelTrans.at(i))
                    else:
                        print(".", end='')
            else:
                print("Model transformation:", self.modelTrans)

            print("min/max (data): {0}/{1}".format(pf(min(self._dataVals)),
                                                   pf(max(self._dataVals))))
            print("min/max (error): {0}%/{1}%".format(
                pf(100 * min(self._errorVals)),
                pf(100 * max(self._errorVals))))
            print("min/max (start model): {0}/{1}".format(
                pf(min(self.startModel)), pf(max(self.startModel))))

        ### To ensure reproduceability of the run() call inv.start() will
        ### reset self.inv.model() to fop.startModel().
        self.fop.setStartModel(self.startModel)
        self.inv.setReferenceModel(self.startModel)

        if self.verbose:
            print("-" * 80)
        if self._preStep and callable(self._preStep):
            self._preStep(0, self)

        self.inv.start()
        self.maxIter = maxIterTmp

        if self._postStep and callable(self._postStep):
            self._postStep(0, self)

        if showProgress:
            self.showProgress(showProgress)

        lastPhi = self.phi()
        self.chi2History = [self.chi2()]
        self.modelHistory = [self.startModel]

        for i in range(1, maxIter):

            if self._preStep and callable(self._preStep):
                self._preStep(i, self)

            if self.verbose:
                print("-" * 80)
                print("inv.iter", i + 1, "... ", end='')

            try:
                self.inv.oneStep()
            except RuntimeError as e:
                print(e)
                pg.error('One step failed. '
                         'Aborting and going back to last model')

            if np.isnan(self.model).any():
                print(model)
                pg.critical('invalid model')

            resp = self.inv.response()
            chi2 = self.inv.chi2()

            self.chi2History.append(chi2)
            self.modelHistory.append(self.model)

            if showProgress:
                self.showProgress(showProgress)

            if self._postStep and callable(self._postStep):
                self._postStep(i, self)

            ### we need to check  the following before oder after chi2 calc??
            self.inv.setLambda(self.inv.getLambda() * self.inv.lambdaFactor())

            if self.robustData:
                self.inv.robustWeighting()

            if self.inv.blockyModel():
                self.inv.constrainBlocky()

            phi = self.phi()
            dPhi = phi / lastPhi

            if self.verbose:
                print("chiĀ² = {0} (dPhi = {1}%) lam: {2}".format(
                    round(chi2, 2), round((1 - dPhi) * 100, 2),
                    self.inv.getLambda()))

            if chi2 <= 1 and self.stopAtChi1:
                print("\n")
                if self.verbose:
                    pg.boxprint("Abort criterion reached: chiĀ² <= 1 (%.2f)" %
                                chi2)
                break

            if (dPhi > (1.0 - minDPhi / 100.0)) and i > 2:
                # if dPhi < -minDPhi:
                if self.verbose:
                    pg.boxprint(
                        "Abort criteria reached: dPhi = {0} (< {1}%)".format(
                            round((1 - dPhi) * 100, 2), minDPhi))
                break

            lastPhi = phi

        ### will never work as expected until we unpack kwargs .. any idea for
        # better strategy?
        # if len(kwargs.keys()) > 0:
        #     print("Warning! unused keyword arguments", kwargs)

        self.model = self.inv.model()
        return self.model
Example #4
0
    def showProgress(self, style='all'):
        r"""Show the inversion progress after every iteration.

        Can show models if `drawModel` method exists. The default fallback is
        plotting the :math:`\chi^2` fit as a function of iterations. Called if
        `showProgress=True` is set for the inversion run.
        """

        if self.fop.drawModel is None:
            style = 'convergence'

        if self.axs is None:
            axs = None
            if style == 'all' or style is True:
                fig, axs = pg.plt.subplots(1, 2)
            else:
                fig, axs = pg.plt.subplots(1, 1)
            self.axs = axs
        ax = self.axs

        if style == 'Model':
            for other_ax in ax.figure.axes:
                # pg._y(type(other_ax).mro())
                if type(other_ax).mro()[0] == type(ax):
                    # only clear Axes not Colorbars
                    other_ax.clear()

            self.fop.drawModel(ax, self.inv.model())
        elif style == 'all':
            # for other_ax in ax[0].figure.axes:
            #     other_ax.clear()
            for _ax in self.axs:
                _ax.clear()
                try:
                    pg.viewer.mpl.twin(_ax).clear()
                except Exception:
                    pass

            self.fop.drawModel(ax[0], self.inv.model(),
                               label='Model')
            self.fop.drawData(ax[1], self._dataVals, self._errorVals,
                              label='Data')
            self.fop.drawData(ax[1], self.inv.response(),
                              label='Response')

            ax[1].text(
                0.99, 0.005, r"Iter: {0}, rrms: {1}%, $\chi^2$: {2}".format(
                    self.inv.iter(), pf(self.inv.relrms()),
                    pf(self.inv.chi2())),
                transform=ax[1].transAxes,
                horizontalalignment='right',
                verticalalignment='bottom',
                fontsize=8)

            ax[1].figure.tight_layout()

        elif style == 'convergence':
            ax.semilogy(self.inv.iter(), self.inv.chi2(), "ro")
            if self.inv.iter() == 1:
                ax.set_xlabel("Iteration")
                ax.set_ylabel("$\chi^2$")
                ax.autoscale(tight=True)
                ax.axhline(y=1, ls="--")

        pg.plt.pause(0.05)