Example #1
0
    def draw_figures(self, ds, history, optimiser):

        color_parameter = self.color_parameter
        misfit_cutoff = self.misfit_cutoff
        fontsize = self.font_size
        fontsize_title = self.font_size_title

        nxmax = self.nx
        nymax = self.ny

        problem = history.problem

        for target in problem.targets:
            target.set_dataset(ds)

        target_index = {}
        i = 0
        for target in problem.targets:
            target_index[target] = i, i+target.nmisfits
            i += target.nmisfits

        gms = history.get_sorted_primary_misfits()[::-1]
        models = history.get_sorted_primary_models()[::-1]

        if misfit_cutoff is not None:
            ibest = gms < misfit_cutoff
            gms = gms[ibest]
            models = models[ibest]

        gms = gms[::10]
        models = models[::10]

        nmodels = models.shape[0]
        if color_parameter == 'dist':
            mx = num.mean(models, axis=0)
            cov = num.cov(models.T)
            mdists = core.mahalanobis_distance(models, mx, cov)
            icolor = meta.ordersort(mdists)

        elif color_parameter == 'misfit':
            iorder = num.arange(nmodels)
            icolor = iorder

        elif color_parameter in problem.parameter_names:
            ind = problem.name_to_index(color_parameter)
            icolor = problem.extract(models, ind)

        target_to_results = defaultdict(list)
        all_syn_trs = []

        dtraces = []
        for imodel in range(nmodels):
            model = models[imodel, :]

            source = problem.get_source(model)
            results = problem.evaluate(model)

            dtraces.append([])

            for target, result in zip(problem.targets, results):
                w = target.get_combined_weight()

                if isinstance(result, gf.SeismosizerError) or \
                        not isinstance(target, WaveformMisfitTarget) or \
                        not num.all(num.isfinite(w)):

                    dtraces[-1].extend([None] * target.nmisfits)
                    continue

                itarget, itarget_end = target_index[target]
                assert itarget_end == itarget + 1

                if target.misfit_config.domain == 'cc_max_norm':
                    tref = (
                        result.filtered_obs.tmin + result.filtered_obs.tmax) \
                        * 0.5

                    for tr_filt, tr_proc, tshift in (
                            (result.filtered_obs,
                             result.processed_obs,
                             0.),
                            (result.filtered_syn,
                             result.processed_syn,
                             result.tshift)):

                        norm = num.sum(num.abs(tr_proc.ydata)) \
                            / tr_proc.data_len()
                        tr_filt.ydata /= norm
                        tr_proc.ydata /= norm

                        tr_filt.shift(tshift)
                        tr_proc.shift(tshift)

                    ctr = result.cc
                    ctr.shift(tref)

                    dtrace = ctr

                else:
                    for tr in (
                            result.filtered_obs,
                            result.filtered_syn,
                            result.processed_obs,
                            result.processed_syn):

                        tr.ydata *= w

                    if result.tshift is not None and result.tshift != 0.0:
                        # result.filtered_syn.shift(result.tshift)
                        result.processed_syn.shift(result.tshift)

                    dtrace = make_norm_trace(
                        result.processed_syn, result.processed_obs,
                        problem.norm_exponent)

                target_to_results[target].append(result)

                dtrace.meta = dict(
                    normalisation_family=target.normalisation_family,
                    path=target.path)

                dtraces[-1].append(dtrace)

                result.processed_syn.meta = dict(
                    normalisation_family=target.normalisation_family,
                    path=target.path)

                all_syn_trs.append(result.processed_syn)

        if not all_syn_trs:
            logger.warn('No traces to show!')
            return

        def skey(tr):
            return tr.meta['normalisation_family'], tr.meta['path']

        trace_minmaxs = trace.minmax(all_syn_trs, skey)

        dtraces_all = []
        for dtraces_group in dtraces:
            dtraces_all.extend(dtraces_group)

        dminmaxs = trace.minmax([
            dtrace_ for dtrace_ in dtraces_all if dtrace_ is not None], skey)

        for tr in dtraces_all:
            if tr:
                dmin, dmax = dminmaxs[skey(tr)]
                tr.ydata /= max(abs(dmin), abs(dmax))

        cg_to_targets = meta.gather(
            problem.waveform_targets,
            lambda t: (t.path, t.codes[3]),
            filter=lambda t: t in target_to_results)

        cgs = sorted(cg_to_targets.keys())

        from matplotlib import colors
        cmap = cm.ScalarMappable(
            norm=colors.Normalize(vmin=num.min(icolor), vmax=num.max(icolor)),
            cmap=plt.get_cmap('coolwarm'))

        imodel_to_color = []
        for imodel in range(nmodels):
            imodel_to_color.append(cmap.to_rgba(icolor[imodel]))

        for cg in cgs:
            targets = cg_to_targets[cg]

            frame_to_target, nx, ny, nxx, nyy = layout(
                source, targets, nxmax, nymax)

            figures = {}
            for iy in range(ny):
                for ix in range(nx):
                    if (iy, ix) not in frame_to_target:
                        continue

                    ixx = ix // nxmax
                    iyy = iy // nymax
                    if (iyy, ixx) not in figures:
                        title = '_'.join(x for x in cg if x)
                        item = PlotItem(
                            name='fig_%s_%i_%i' % (title, ixx, iyy))
                        item.attributes['targets'] = []
                        figures[iyy, ixx] = (
                            item, plt.figure(figsize=self.size_inch))

                        figures[iyy, ixx][1].subplots_adjust(
                            left=0.03,
                            right=1.0 - 0.03,
                            bottom=0.03,
                            top=1.0 - 0.06,
                            wspace=0.2,
                            hspace=0.2)

                    item, fig = figures[iyy, ixx]

                    target = frame_to_target[iy, ix]

                    item.attributes['targets'].append(target.string_id())

                    amin, amax = trace_minmaxs[
                        target.normalisation_family, target.path]
                    absmax = max(abs(amin), abs(amax))

                    ny_this = nymax  # min(ny, nymax)
                    nx_this = nxmax  # min(nx, nxmax)
                    i_this = (iy % ny_this) * nx_this + (ix % nx_this) + 1

                    axes2 = fig.add_subplot(ny_this, nx_this, i_this)

                    space = 0.5
                    space_factor = 1.0 + space
                    axes2.set_axis_off()
                    axes2.set_ylim(-1.05 * space_factor, 1.05)

                    axes = axes2.twinx()
                    axes.set_axis_off()

                    if target.misfit_config.domain == 'cc_max_norm':
                        axes.set_ylim(-10. * space_factor, 10.)
                    else:
                        axes.set_ylim(-absmax*1.33 * space_factor, absmax*1.33)

                    itarget, itarget_end = target_index[target]
                    assert itarget_end == itarget + 1

                    for imodel, result in enumerate(target_to_results[target]):

                        syn_color = imodel_to_color[imodel]

                        dtrace = dtraces[imodel][itarget]

                        tap_color_annot = (0.35, 0.35, 0.25)
                        tap_color_edge = (0.85, 0.85, 0.80)
                        tap_color_fill = (0.95, 0.95, 0.90)

                        plot_taper(
                            axes2,
                            result.processed_obs.get_xdata(),
                            result.taper,
                            fc=tap_color_fill, ec=tap_color_edge, alpha=0.2)

                        obs_color = mpl_color('aluminium5')
                        obs_color_light = light(obs_color, 0.5)

                        plot_dtrace(
                            axes2, dtrace, space, 0., 1.,
                            fc='none',
                            ec=syn_color)

                        # plot_trace(
                        #     axes, result.filtered_syn,
                        #     color=syn_color_light, lw=1.0)

                        if imodel == 0:
                            plot_trace(
                                axes, result.filtered_obs,
                                color=obs_color_light, lw=0.75)

                        plot_trace(
                            axes, result.processed_syn,
                            color=syn_color, lw=1.0, alpha=0.3)

                        plot_trace(
                            axes, result.processed_obs,
                            color=obs_color, lw=0.75, alpha=0.3)

                        if imodel != 0:
                            continue
                        xdata = result.filtered_obs.get_xdata()
                        axes.set_xlim(xdata[0], xdata[-1])

                        tmarks = [
                            result.processed_obs.tmin,
                            result.processed_obs.tmax]

                        for tmark in tmarks:
                            axes2.plot(
                                [tmark, tmark], [-0.9, 0.1],
                                color=tap_color_annot)

                        dur = tmarks[1] - tmarks[0]
                        for tmark, text, ha in [
                                (tmarks[0],
                                 '$\\,$ ' + meta.str_duration(
                                    tmarks[0] - source.time),
                                 'left'),
                                (tmarks[1],
                                 '$\\Delta$ ' + meta.str_duration(
                                    dur),
                                 'right')]:

                            axes2.annotate(
                                text,
                                xy=(tmark, -0.9),
                                xycoords='data',
                                xytext=(
                                    fontsize*0.4 * [-1, 1][ha == 'left'],
                                    fontsize*0.2),
                                textcoords='offset points',
                                ha=ha,
                                va='bottom',
                                color=tap_color_annot,
                                fontsize=fontsize)

                        axes2.set_xlim(
                            tmarks[0] - dur*0.1, tmarks[1] + dur*0.1)

                    scale_string = None

                    if target.misfit_config.domain == 'cc_max_norm':
                        scale_string = 'Syn/obs scales differ!'

                    infos = []
                    if scale_string:
                        infos.append(scale_string)

                    if self.nx == 1 and self.ny == 1:
                        infos.append(target.string_id())
                    else:
                        infos.append('.'.join(x for x in target.codes if x))
                    dist = source.distance_to(target)
                    azi = source.azibazi_to(target)[0]
                    infos.append(meta.str_dist(dist))
                    infos.append(u'%.0f\u00B0' % azi)
                    axes2.annotate(
                        '\n'.join(infos),
                        xy=(0., 1.),
                        xycoords='axes fraction',
                        xytext=(2., 2.),
                        textcoords='offset points',
                        ha='left',
                        va='top',
                        fontsize=fontsize,
                        fontstyle='normal')

                    if (self.nx == 1 and self.ny == 1):
                        yield item, fig
                        del figures[iyy, ixx]

            if not (self.nx == 1 and self.ny == 1):
                for (iyy, ixx), (_, fig) in figures.items():
                    title = '.'.join(x for x in cg if x)
                    if len(figures) > 1:
                        title += ' (%i/%i, %i/%i)' % (iyy+1, nyy, ixx+1, nxx)

                    fig.suptitle(title, fontsize=fontsize_title)

            for item, fig in figures.values():
                yield item, fig
Example #2
0
    def draw_figure(self, ds, history, tpath):
        problem = history.problem
        color_parameter = self.color_parameter
        misfit_cutoff = self.misfit_cutoff
        fontsize = self.font_size
        targets = [
            t for t in problem.targets
            if isinstance(t, PhaseRatioTarget) and t.path == tpath
        ]

        gms = problem.combine_misfits(history.misfits)
        isort = num.argsort(gms)[::-1]
        gms = gms[isort]
        models = history.models[isort, :]

        if misfit_cutoff is not None:
            ibest = gms < misfit_cutoff
            gms = gms[ibest]
            models = models[ibest]

        gms = gms[::self.istride_ensemble]
        models = models[::self.istride_ensemble]

        nmodels = models.shape[0]
        if color_parameter == 'dist':
            mx = num.mean(models, axis=0)
            cov = num.cov(models.T)
            mdists = core.mahalanobis_distance(models, mx, cov)
            icolor = meta.ordersort(mdists)

        elif color_parameter == 'misfit':
            iorder = num.arange(nmodels)
            icolor = iorder

        elif color_parameter in problem.parameter_names:
            ind = problem.name_to_index(color_parameter)
            icolor = problem.extract(models, ind)

        from matplotlib import colors
        cmap = cm.ScalarMappable(norm=colors.Normalize(vmin=num.min(icolor),
                                                       vmax=num.max(icolor)),
                                 cmap=plt.get_cmap('coolwarm'))

        imodel_to_color = []
        for imodel in range(nmodels):
            imodel_to_color.append(cmap.to_rgba(icolor[imodel]))

        data = []
        for imodel in range(nmodels):
            model = models[imodel, :]

            # source = problem.get_source(model)
            results = problem.evaluate(model, targets=targets)

            for target, result in zip(targets, results):
                if isinstance(result, gf.SeismosizerError):
                    continue

                if not isinstance(target, PhaseRatioTarget):
                    continue

                a_obs = result.a_obs
                b_obs = result.b_obs
                a_syn = result.a_syn
                b_syn = result.b_syn

                r_obs = a_obs / (a_obs + b_obs)
                r_syn = a_syn / (a_syn + b_syn)

                data.append(('.'.join(target.codes), imodel, r_obs, r_syn))

        fontsize = self.font_size

        item = PlotItem(name='fig_%s' % tpath)

        item.attributes['targets'] = [t.string_id() for t in targets]

        fig = plt.figure(figsize=self.size_inch)

        labelpos = mpl_margins(fig,
                               nw=1,
                               nh=1,
                               left=7.,
                               right=1.,
                               bottom=10.,
                               top=3,
                               units=fontsize)

        axes = fig.add_subplot(1, 1, 1)
        labelpos(axes, 2.5, 2.0)

        labels = sorted(set(x[0] for x in data))

        ntargets = len(labels)
        string_id_to_itarget = dict((x, i) for (i, x) in enumerate(labels))

        itargets = num.array([string_id_to_itarget[x[0]] for x in data])

        imodels = num.array([x[1] for x in data], dtype=num.int).T
        r_obs, r_syn = num.array([x[2:] for x in data]).T

        r_obs_median = num.zeros(ntargets)
        for itarget in range(ntargets):
            r_obs_median[itarget] = num.median(r_obs[itargets == itarget])

        iorder = meta.ordersort(r_obs_median)

        for imodel in range(nmodels):
            mask = imodels == imodel
            axes.plot(iorder[itargets[mask]],
                      r_obs[mask],
                      '_',
                      ms=20.,
                      zorder=-10,
                      alpha=0.5,
                      color='black')
            axes.plot(iorder[itargets[mask]],
                      r_syn[mask],
                      '_',
                      ms=10.,
                      alpha=0.5,
                      color=imodel_to_color[imodel])

        axes.set_yscale('log')
        axes.set_ylabel('Ratio')

        axes.set_xticks(iorder[num.arange(ntargets)])
        axes.set_xticklabels(labels, rotation='vertical')

        fig.suptitle(tpath, fontsize=self.font_size_title)

        yield item, fig
Example #3
0
    def draw_figures(self, history, optimiser):

        color_parameter = self.color_parameter
        exclude = self.exclude
        include = self.include
        nsubplots = self.nsubplots
        figsize = self.size_inch
        ibootstrap = 0 if self.ibootstrap is None else self.ibootstrap
        misfit_cutoff = self.misfit_cutoff
        show_ellipses = self.show_ellipses
        msize = 1.5
        cmap = 'coolwarm'

        problem = history.problem
        if not problem:
            return []

        models = history.models

        exclude = list(exclude)
        bounds = problem.get_combined_bounds()
        for ipar in range(problem.ncombined):
            par = problem.combined[ipar]
            lo, hi = bounds[ipar]
            if lo == hi:
                exclude.append(par.name)

        xref = problem.get_reference_model()

        isort = history.get_sorted_misfits_idx(chain=ibootstrap)[::-1]
        models = history.get_sorted_models(chain=ibootstrap)[::-1]
        nmodels = history.nmodels

        gms = history.get_sorted_misfits(chain=ibootstrap)[::-1]
        if misfit_cutoff is not None:
            ibest = gms < misfit_cutoff
            gms = gms[ibest]
            models = models[ibest]

        kwargs = {}

        if color_parameter == 'dist':
            mx = num.mean(models, axis=0)
            cov = num.cov(models.T)
            mdists = core.mahalanobis_distance(models, mx, cov)
            icolor = meta.ordersort(mdists)

        elif color_parameter == 'misfit':
            iorder = num.arange(nmodels)
            icolor = iorder

        elif color_parameter in problem.parameter_names:
            ind = problem.name_to_index(color_parameter)
            icolor = problem.extract(models, ind)

        elif color_parameter in history.attribute_names:
            icolor = history.get_attribute(color_parameter)[isort]
            icolor_need = num.unique(icolor)

            colors = []
            for i in range(icolor_need[-1]+1):
                colors.append(mpl_graph_color(i))

            cmap = mcolors.ListedColormap(colors)
            cmap.set_under(mpl_color('aluminium3'))
            kwargs.update(dict(vmin=0, vmax=icolor_need[-1]))
        else:
            raise meta.GrondError(
                'Invalid color_parameter: %s' % color_parameter)

        smap = {}
        iselected = 0
        for ipar in range(problem.ncombined):
            par = problem.combined[ipar]
            if exclude and par.name in exclude or \
                    include and par.name not in include:
                continue

            smap[iselected] = ipar
            iselected += 1

        nselected = iselected

        if nselected < 2:
            logger.warn('Cannot draw joinpar figures with less than two '
                        'parameters selected.')
            return []

        nfig = (nselected - 2) // nsubplots + 1

        figs = []
        for ifig in range(nfig):
            figs_row = []
            for jfig in range(nfig):
                if ifig >= jfig:
                    item = PlotItem(name='fig_%i_%i' % (ifig, jfig))
                    item.attributes['parameters'] = []
                    figs_row.append((item, plt.figure(figsize=figsize)))
                else:
                    figs_row.append(None)

            figs.append(figs_row)

        for iselected in range(nselected):
            ipar = smap[iselected]
            ypar = problem.combined[ipar]
            for jselected in range(iselected):
                jpar = smap[jselected]
                xpar = problem.combined[jpar]

                ixg = (iselected - 1)
                iyg = jselected

                ix = ixg % nsubplots
                iy = iyg % nsubplots

                ifig = ixg // nsubplots
                jfig = iyg // nsubplots

                aind = (nsubplots, nsubplots, (ix * nsubplots) + iy + 1)

                item, fig = figs[ifig][jfig]

                tlist = item.attributes['parameters']
                if xpar.name not in tlist:
                    tlist.append(xpar.name)
                if ypar.name not in tlist:
                    tlist.append(ypar.name)

                axes = fig.add_subplot(*aind)

                axes.axvline(0., color=mpl_color('aluminium3'), lw=0.5)
                axes.axhline(0., color=mpl_color('aluminium3'), lw=0.5)
                for spine in axes.spines.values():
                    spine.set_edgecolor(mpl_color('aluminium5'))
                    spine.set_linewidth(0.5)

                xmin, xmax = fixlim(*xpar.scaled(bounds[jpar]))
                ymin, ymax = fixlim(*ypar.scaled(bounds[ipar]))

                if ix == 0 or jselected + 1 == iselected:
                    for (xpos, xoff, x) in [
                            (0.0, 10., xmin),
                            (1.0, -10., xmax)]:

                        axes.annotate(
                            '%.3g%s' % (x, xpar.get_unit_suffix()),
                            xy=(xpos, 1.05),
                            xycoords='axes fraction',
                            xytext=(xoff, 5.),
                            textcoords='offset points',
                            verticalalignment='bottom',
                            horizontalalignment='left',
                            rotation=45.)

                if iy == nsubplots - 1 or jselected + 1 == iselected:
                    for (ypos, yoff, y) in [
                            (0., 10., ymin),
                            (1.0, -10., ymax)]:

                        axes.annotate(
                            '%.3g%s' % (y, ypar.get_unit_suffix()),
                            xy=(1.0, ypos),
                            xycoords='axes fraction',
                            xytext=(5., yoff),
                            textcoords='offset points',
                            verticalalignment='bottom',
                            horizontalalignment='left',
                            rotation=45.)

                axes.set_xlim(xmin, xmax)
                axes.set_ylim(ymin, ymax)

                if not self.show_ticks:
                    axes.get_xaxis().set_ticks([])
                    axes.get_yaxis().set_ticks([])
                else:
                    axes.tick_params(length=4, which='both')
                    axes.get_yaxis().set_ticklabels([])
                    axes.get_xaxis().set_ticklabels([])

                if iselected == nselected - 1 or ix == nsubplots - 1:
                    axes.annotate(
                        xpar.get_label(with_unit=False),
                        xy=(0.5, -0.05),
                        xycoords='axes fraction',
                        verticalalignment='top',
                        horizontalalignment='right',
                        rotation=45.)

                if iy == 0:
                    axes.annotate(
                        ypar.get_label(with_unit=False),
                        xy=(-0.05, 0.5),
                        xycoords='axes fraction',
                        verticalalignment='top',
                        horizontalalignment='right',
                        rotation=45.)

                fx = problem.extract(models, jpar)
                fy = problem.extract(models, ipar)

                axes.scatter(
                    xpar.scaled(fx),
                    ypar.scaled(fy),
                    c=icolor,
                    s=msize, alpha=0.5, cmap=cmap, edgecolors='none', **kwargs)

                if show_ellipses:
                    cov = num.cov((xpar.scaled(fx), ypar.scaled(fy)))
                    evals, evecs = eigh_sorted(cov)
                    evals = num.sqrt(evals)
                    ell = patches.Ellipse(
                        xy=(
                            num.mean(xpar.scaled(fx)),
                            num.mean(ypar.scaled(fy))),
                        width=evals[0] * 2,
                        height=evals[1] * 2,
                        angle=num.rad2deg(
                            num.arctan2(evecs[1][0], evecs[0][0])))

                    ell.set_facecolor('none')
                    axes.add_artist(ell)

                if self.show_reference:
                    fx = problem.extract(xref, jpar)
                    fy = problem.extract(xref, ipar)

                    ref_color = mpl_color('aluminium6')
                    ref_color_light = 'none'
                    axes.plot(
                        xpar.scaled(fx), ypar.scaled(fy), 's',
                        mew=1.5, ms=5, mfc=ref_color_light, mec=ref_color)

        figs_flat = []
        for figs_row in figs:
            figs_flat.extend(
                item_fig for item_fig in figs_row if item_fig is not None)

        return figs_flat
Example #4
0
    def draw_figures(self, ds, history):

        color_parameter = self.color_parameter
        misfit_cutoff = self.misfit_cutoff
        fontsize = self.font_size
        fontsize_title = self.font_size_title

        problem = history.problem

        for target in problem.targets:
            target.set_dataset(ds)

        target_index = dict(
            (target, i) for (i, target) in enumerate(problem.targets))

        gms = problem.combine_misfits(history.misfits)
        isort = num.argsort(gms)[::-1]
        gms = gms[isort]
        models = history.models[isort, :]

        if misfit_cutoff is not None:
            ibest = gms < misfit_cutoff
            gms = gms[ibest]
            models = models[ibest]

        gms = gms[::10]
        models = models[::10]

        nmodels = models.shape[0]
        if color_parameter == 'dist':
            mx = num.mean(models, axis=0)
            cov = num.cov(models.T)
            mdists = core.mahalanobis_distance(models, mx, cov)
            icolor = meta.ordersort(mdists)

        elif color_parameter == 'misfit':
            iorder = num.arange(nmodels)
            icolor = iorder

        elif color_parameter in problem.parameter_names:
            ind = problem.name_to_index(color_parameter)
            icolor = problem.extract(models, ind)

        target_to_results = defaultdict(list)
        all_syn_trs = []

        dtraces = []
        for imodel in range(nmodels):
            model = models[imodel, :]

            source = problem.get_source(model)
            results = problem.evaluate(model)

            dtraces.append([])

            for target, result in zip(problem.targets, results):
                if isinstance(result, gf.SeismosizerError):
                    dtraces[-1].append(None)
                    continue

                if not isinstance(target, WaveformMisfitTarget):
                    dtraces[-1].append(None)
                    continue

                itarget = target_index[target]
                w = target.get_combined_weight()

                if target.misfit_config.domain == 'cc_max_norm':
                    tref = (
                        result.filtered_obs.tmin + result.filtered_obs.tmax) \
                        * 0.5

                    for tr_filt, tr_proc, tshift in ((result.filtered_obs,
                                                      result.processed_obs,
                                                      0.),
                                                     (result.filtered_syn,
                                                      result.processed_syn,
                                                      result.tshift)):

                        norm = num.sum(num.abs(tr_proc.ydata)) \
                            / tr_proc.data_len()
                        tr_filt.ydata /= norm
                        tr_proc.ydata /= norm

                        tr_filt.shift(tshift)
                        tr_proc.shift(tshift)

                    ctr = result.cc
                    ctr.shift(tref)

                    dtrace = ctr

                else:
                    for tr in (result.filtered_obs, result.filtered_syn,
                               result.processed_obs, result.processed_syn):

                        tr.ydata *= w

                    if result.tshift is not None and result.tshift != 0.0:
                        # result.filtered_syn.shift(result.tshift)
                        result.processed_syn.shift(result.tshift)

                    dtrace = make_norm_trace(result.processed_syn,
                                             result.processed_obs,
                                             problem.norm_exponent)

                target_to_results[target].append(result)

                dtrace.meta = dict(
                    normalisation_family=target.normalisation_family,
                    path=target.path)

                dtraces[-1].append(dtrace)

                result.processed_syn.meta = dict(
                    normalisation_family=target.normalisation_family,
                    path=target.path)

                all_syn_trs.append(result.processed_syn)

        if not all_syn_trs:
            logger.warn('no traces to show')
            return []

        def skey(tr):
            return tr.meta['normalisation_family'], tr.meta['path']

        trace_minmaxs = trace.minmax(all_syn_trs, skey)

        dtraces_all = []
        for dtraces_group in dtraces:
            dtraces_all.extend(dtraces_group)

        dminmaxs = trace.minmax(
            [dtrace_ for dtrace_ in dtraces_all if dtrace_ is not None], skey)

        for tr in dtraces_all:
            if tr:
                dmin, dmax = dminmaxs[skey(tr)]
                tr.ydata /= max(abs(dmin), abs(dmax))

        cg_to_targets = meta.gather(problem.waveform_targets,
                                    lambda t: (t.path, t.codes[3]),
                                    filter=lambda t: t in target_to_results)

        cgs = sorted(cg_to_targets.keys())

        from matplotlib import colors
        cmap = cm.ScalarMappable(norm=colors.Normalize(vmin=num.min(icolor),
                                                       vmax=num.max(icolor)),
                                 cmap=plt.get_cmap('coolwarm'))

        imodel_to_color = []
        for imodel in range(nmodels):
            imodel_to_color.append(cmap.to_rgba(icolor[imodel]))

        figs = []
        for cg in cgs:
            targets = cg_to_targets[cg]
            nframes = len(targets)

            nx = int(math.ceil(math.sqrt(nframes)))
            ny = (nframes - 1) // nx + 1

            nxmax = 4
            nymax = 4

            nxx = (nx - 1) // nxmax + 1
            nyy = (ny - 1) // nymax + 1

            # nz = nxx * nyy

            xs = num.arange(nx) / ((max(2, nx) - 1.0) / 2.)
            ys = num.arange(ny) / ((max(2, ny) - 1.0) / 2.)

            xs -= num.mean(xs)
            ys -= num.mean(ys)

            fxs = num.tile(xs, ny)
            fys = num.repeat(ys, nx)

            data = []

            for target in targets:
                azi = source.azibazi_to(target)[0]
                dist = source.distance_to(target)
                x = dist * num.sin(num.deg2rad(azi))
                y = dist * num.cos(num.deg2rad(azi))
                data.append((x, y, dist))

            gxs, gys, dists = num.array(data, dtype=num.float).T

            iorder = num.argsort(dists)

            gxs = gxs[iorder]
            gys = gys[iorder]
            targets_sorted = [targets[ii] for ii in iorder]

            gxs -= num.mean(gxs)
            gys -= num.mean(gys)

            gmax = max(num.max(num.abs(gys)), num.max(num.abs(gxs)))
            if gmax == 0.:
                gmax = 1.

            gxs /= gmax
            gys /= gmax

            dists = num.sqrt((fxs[num.newaxis, :] - gxs[:, num.newaxis])**2 +
                             (fys[num.newaxis, :] - gys[:, num.newaxis])**2)

            distmax = num.max(dists)

            availmask = num.ones(dists.shape[1], dtype=num.bool)
            frame_to_target = {}
            for itarget, target in enumerate(targets_sorted):
                iframe = num.argmin(
                    num.where(availmask, dists[itarget], distmax + 1.))
                availmask[iframe] = False
                iy, ix = num.unravel_index(iframe, (ny, nx))
                frame_to_target[iy, ix] = target

            figures = {}
            for iy in range(ny):
                for ix in range(nx):
                    if (iy, ix) not in frame_to_target:
                        continue

                    ixx = ix // nxmax
                    iyy = iy // nymax
                    if (iyy, ixx) not in figures:
                        title = '_'.join(x for x in cg if x)
                        item = PlotItem(name='fig_%s_%i_%i' %
                                        (title, ixx, iyy))
                        item.attributes['targets'] = []
                        figures[iyy,
                                ixx] = (item,
                                        plt.figure(figsize=self.size_inch))

                        figures[iyy, ixx][1].subplots_adjust(left=0.03,
                                                             right=1.0 - 0.03,
                                                             bottom=0.03,
                                                             top=1.0 - 0.06,
                                                             wspace=0.2,
                                                             hspace=0.2)

                        figs.append(figures[iyy, ixx])

                    item, fig = figures[iyy, ixx]

                    target = frame_to_target[iy, ix]

                    item.attributes['targets'].append(target.string_id())

                    amin, amax = trace_minmaxs[target.normalisation_family,
                                               target.path]
                    absmax = max(abs(amin), abs(amax))

                    ny_this = nymax  # min(ny, nymax)
                    nx_this = nxmax  # min(nx, nxmax)
                    i_this = (iy % ny_this) * nx_this + (ix % nx_this) + 1

                    axes2 = fig.add_subplot(ny_this, nx_this, i_this)

                    space = 0.5
                    space_factor = 1.0 + space
                    axes2.set_axis_off()
                    axes2.set_ylim(-1.05 * space_factor, 1.05)

                    axes = axes2.twinx()
                    axes.set_axis_off()

                    if target.misfit_config.domain == 'cc_max_norm':
                        axes.set_ylim(-10. * space_factor, 10.)
                    else:
                        axes.set_ylim(-absmax * 1.33 * space_factor,
                                      absmax * 1.33)

                    itarget = target_index[target]
                    for imodel, result in enumerate(target_to_results[target]):

                        syn_color = imodel_to_color[imodel]

                        dtrace = dtraces[imodel][itarget]

                        tap_color_annot = (0.35, 0.35, 0.25)
                        tap_color_edge = (0.85, 0.85, 0.80)
                        tap_color_fill = (0.95, 0.95, 0.90)

                        plot_taper(axes2,
                                   result.processed_obs.get_xdata(),
                                   result.taper,
                                   fc=tap_color_fill,
                                   ec=tap_color_edge,
                                   alpha=0.2)

                        obs_color = mpl_color('aluminium5')
                        obs_color_light = light(obs_color, 0.5)

                        plot_dtrace(axes2,
                                    dtrace,
                                    space,
                                    0.,
                                    1.,
                                    fc='none',
                                    ec=syn_color)

                        # plot_trace(
                        #     axes, result.filtered_syn,
                        #     color=syn_color_light, lw=1.0)

                        if imodel == 0:
                            plot_trace(axes,
                                       result.filtered_obs,
                                       color=obs_color_light,
                                       lw=0.75)

                        plot_trace(axes,
                                   result.processed_syn,
                                   color=syn_color,
                                   lw=1.0,
                                   alpha=0.3)

                        plot_trace(axes,
                                   result.processed_obs,
                                   color=obs_color,
                                   lw=0.75,
                                   alpha=0.3)

                        if imodel != 0:
                            continue
                        xdata = result.filtered_obs.get_xdata()
                        axes.set_xlim(xdata[0], xdata[-1])

                        tmarks = [
                            result.processed_obs.tmin,
                            result.processed_obs.tmax
                        ]

                        for tmark in tmarks:
                            axes2.plot([tmark, tmark], [-0.9, 0.1],
                                       color=tap_color_annot)

                        for tmark, text, ha in [
                            (tmarks[0], '$\\,$ ' +
                             meta.str_duration(tmarks[0] - source.time),
                             'right'),
                            (tmarks[1], '$\\Delta$ ' +
                             meta.str_duration(tmarks[1] - tmarks[0]), 'left')
                        ]:

                            axes2.annotate(
                                text,
                                xy=(tmark, -0.9),
                                xycoords='data',
                                xytext=(fontsize * 0.4 * [-1, 1][ha == 'left'],
                                        fontsize * 0.2),
                                textcoords='offset points',
                                ha=ha,
                                va='bottom',
                                color=tap_color_annot,
                                fontsize=fontsize)

                    scale_string = None

                    if target.misfit_config.domain == 'cc_max_norm':
                        scale_string = 'Syn/obs scales differ!'

                    infos = []
                    if scale_string:
                        infos.append(scale_string)

                    infos.append('.'.join(x for x in target.codes if x))
                    dist = source.distance_to(target)
                    azi = source.azibazi_to(target)[0]
                    infos.append(meta.str_dist(dist))
                    infos.append(u'%.0f\u00B0' % azi)
                    axes2.annotate('\n'.join(infos),
                                   xy=(0., 1.),
                                   xycoords='axes fraction',
                                   xytext=(2., 2.),
                                   textcoords='offset points',
                                   ha='left',
                                   va='top',
                                   fontsize=fontsize,
                                   fontstyle='normal')

            for (iyy, ixx), (_, fig) in figures.items():
                title = '.'.join(x for x in cg if x)
                if len(figures) > 1:
                    title += ' (%i/%i, %i/%i)' % (iyy + 1, nyy, ixx + 1, nxx)

                fig.suptitle(title, fontsize=fontsize_title)

        return figs