Exemple #1
0
class Map(Object):
    lat = Float.T(optional=True)
    lon = Float.T(optional=True)
    radius = Float.T(optional=True)
    width = Float.T(default=20.)
    height = Float.T(default=14.)
    margins = List.T(Float.T())
    illuminate = Bool.T(default=True)
    skip_feature_factor = Float.T(default=0.02)
    show_grid = Bool.T(default=False)
    show_topo = Bool.T(default=True)
    show_topo_scale = Bool.T(default=False)
    show_center_mark = Bool.T(default=False)
    show_rivers = Bool.T(default=True)
    illuminate_factor_land = Float.T(default=0.5)
    illuminate_factor_ocean = Float.T(default=0.25)
    color_wet = Tuple.T(3, Int.T(), default=(216, 242, 254))
    color_dry = Tuple.T(3, Int.T(), default=(172, 208, 165))
    topo_resolution_min = Float.T(
        default=40., help='minimum resolution of topography [dpi]')
    topo_resolution_max = Float.T(
        default=200., help='maximum resolution of topography [dpi]')
    replace_topo_color_only = FloatTile.T(
        optional=True,
        help='replace topo color while keeping topographic shading')
    topo_cpt_wet = String.T(default='light_sea')
    topo_cpt_dry = String.T(default='light_land')
    axes_layout = String.T(optional=True)
    custom_cities = List.T(City.T())
    gmt_config = Dict.T(String.T(), String.T())
    comment = String.T(optional=True)

    def __init__(self, **kwargs):
        Object.__init__(self, **kwargs)
        self._gmt = None
        self._scaler = None
        self._widget = None
        self._corners = None
        self._wesn = None
        self._minarea = None
        self._coastline_resolution = None
        self._rivers = None
        self._dems = None
        self._have_topo_land = None
        self._have_topo_ocean = None
        self._jxyr = None
        self._prep_topo_have = None
        self._labels = []

    def save(self,
             outpath,
             resolution=75.,
             oversample=2.,
             size=None,
             width=None,
             height=None):
        '''Save the image.

        Save the image to *outpath*. The format is determined by the filename
        extension. Formats are handled as follows: ``'.eps'`` and ``'.ps'``
        produce EPS and PS, respectively, directly with GMT. If the file name
        ends with ``'.pdf'``, GMT output is fed through ``gmtpy-epstopdf`` to
        create a PDF file. For any other filename extension, output is first
        converted to PDF with ``gmtpy-epstopdf``, then with ``pdftocairo`` to
        PNG with a resolution oversampled by the factor *oversample* and
        finally the PNG is downsampled and converted to the target format with
        ``convert``. The resolution of rasterized target image can be
        controlled either by *resolution* in DPI or by specifying *width* or
        *height* or *size*, where the latter fits the image into a square with
        given side length.'''

        gmt = self.gmt
        self.draw_labels()
        self.draw_axes()
        if self.show_topo and self.show_topo_scale:
            self._draw_topo_scale()

        gmt = self._gmt
        if outpath.endswith('.eps'):
            tmppath = gmt.tempfilename() + '.eps'
        elif outpath.endswith('.ps'):
            tmppath = gmt.tempfilename() + '.ps'
        else:
            tmppath = gmt.tempfilename() + '.pdf'

        gmt.save(tmppath)

        if any(outpath.endswith(x) for x in ('.eps', '.ps', '.pdf')):
            shutil.copy(tmppath, outpath)
        else:
            convert_graph(tmppath,
                          outpath,
                          resolution=resolution,
                          oversample=oversample,
                          size=size,
                          width=width,
                          height=height)

    @property
    def scaler(self):
        if self._scaler is None:
            self._setup_geometry()

        return self._scaler

    @property
    def wesn(self):
        if self._wesn is None:
            self._setup_geometry()

        return self._wesn

    @property
    def widget(self):
        if self._widget is None:
            self._setup()

        return self._widget

    @property
    def layout(self):
        if self._layout is None:
            self._setup()

        return self._layout

    @property
    def jxyr(self):
        if self._jxyr is None:
            self._setup()

        return self._jxyr

    @property
    def pxyr(self):
        if self._pxyr is None:
            self._setup()

        return self._pxyr

    @property
    def gmt(self):
        if self._gmt is None:
            self._setup()

        if self._have_topo_ocean is None:
            self._draw_background()

        return self._gmt

    def _setup(self):
        if not self._widget:
            self._setup_geometry()

        self._setup_lod()
        self._setup_gmt()

    def _setup_geometry(self):
        wpage, hpage = self.width, self.height
        ml, mr, mt, mb = self._expand_margins()
        wpage -= ml + mr
        hpage -= mt + mb

        wreg = self.radius * 2.0
        hreg = self.radius * 2.0
        if wpage >= hpage:
            wreg *= wpage / hpage
        else:
            hreg *= hpage / wpage

        self._corners = corners(self.lon, self.lat, wreg, hreg)
        west, east, south, north = extent(self.lon, self.lat, wreg, hreg, 10)

        x, y, z = ((west, east), (south, north), (-6000., 4500.))

        xax = gmtpy.Ax(mode='min-max', approx_ticks=4.)
        yax = gmtpy.Ax(mode='min-max', approx_ticks=4.)
        zax = gmtpy.Ax(mode='min-max',
                       inc=1000.,
                       label='Height',
                       scaled_unit='km',
                       scaled_unit_factor=0.001)

        scaler = gmtpy.ScaleGuru(data_tuples=[(x, y, z)], axes=(xax, yax, zax))

        par = scaler.get_params()

        west = par['xmin']
        east = par['xmax']
        south = par['ymin']
        north = par['ymax']

        self._wesn = west, east, south, north
        self._scaler = scaler

    def _setup_lod(self):
        w, e, s, n = self._wesn
        if self.radius > 1500. * km:
            coastline_resolution = 'i'
            rivers = False
        else:
            coastline_resolution = 'f'
            rivers = True

        self._minarea = (self.skip_feature_factor * self.radius / km)**2

        self._coastline_resolution = coastline_resolution
        self._rivers = rivers

        self._prep_topo_have = {}
        self._dems = {}

        cm2inch = gmtpy.cm / gmtpy.inch

        dmin = 2.0 * self.radius * m2d / (self.topo_resolution_max *
                                          (self.height * cm2inch))
        dmax = 2.0 * self.radius * m2d / (self.topo_resolution_min *
                                          (self.height * cm2inch))

        for k in ['ocean', 'land']:
            self._dems[k] = topo.select_dem_names(k, dmin, dmax, self._wesn)
            if self._dems[k]:
                logger.debug('using topography dataset %s for %s' %
                             (','.join(self._dems[k]), k))

    def _expand_margins(self):
        if len(self.margins) == 0 or len(self.margins) > 4:
            ml = mr = mt = mb = 2.0
        elif len(self.margins) == 1:
            ml = mr = mt = mb = self.margins[0]
        elif len(self.margins) == 2:
            ml = mr = self.margins[0]
            mt = mb = self.margins[1]
        elif len(self.margins) == 4:
            ml, mr, mt, mb = self.margins

        return ml, mr, mt, mb

    def _setup_gmt(self):
        w, h = self.width, self.height
        scaler = self._scaler

        gmtconf = dict(TICK_PEN='1.25p',
                       TICK_LENGTH='0.2c',
                       ANNOT_FONT_PRIMARY='1',
                       ANNOT_FONT_SIZE_PRIMARY='12p',
                       LABEL_FONT='1',
                       LABEL_FONT_SIZE='12p',
                       CHAR_ENCODING='ISOLatin1+',
                       BASEMAP_TYPE='fancy',
                       PLOT_DEGREE_FORMAT='D',
                       PAPER_MEDIA='Custom_%ix%i' %
                       (w * gmtpy.cm, h * gmtpy.cm),
                       GRID_PEN_PRIMARY='thinnest/0/50/0',
                       DOTS_PR_INCH='1200',
                       OBLIQUE_ANNOTATION='6')

        gmtconf.update(
            (k.upper(), v) for (k, v) in self.gmt_config.iteritems())

        gmt = gmtpy.GMT(config=gmtconf)

        layout = gmt.default_layout()

        layout.set_fixed_margins(*[x * cm for x in self._expand_margins()])

        widget = layout.get_widget()
        # widget['J'] = ('-JT%g/%g' % (self.lon, self.lat)) + '/%(width)gp'
        # widget['J'] = ('-JA%g/%g' % (self.lon, self.lat)) + '/%(width)gp'
        widget['P'] = widget['J']
        widget['J'] = ('-JA%g/%g' % (self.lon, self.lat)) + '/%(width_m)gm'
        # widget['J'] = ('-JE%g/%g' % (self.lon, self.lat)) + '/%(width)gp'
        # scaler['R'] = '-R%(xmin)g/%(xmax)g/%(ymin)g/%(ymax)g'
        scaler['R'] = '-R%g/%g/%g/%gr' % self._corners

        aspect = gmtpy.aspect_for_projection(*(widget.J() + scaler.R()))
        widget.set_aspect(aspect)

        self._gmt = gmt
        self._layout = layout
        self._widget = widget
        self._jxyr = self._widget.JXY() + self._scaler.R()
        self._pxyr = self._widget.PXY() + [
            '-R%g/%g/%g/%g' % (0, widget.width(), 0, widget.height())
        ]
        self._have_drawn_axes = False
        self._have_drawn_labels = False

    def _draw_background(self):
        self._have_topo_land = False
        self._have_topo_ocean = False
        if self.show_topo:
            self._have_topo = self._draw_topo()

        self._draw_basefeatures()

    def _get_topo_tile(self, k):
        t = None
        demname = None
        for dem in self._dems[k]:
            t = topo.get(dem, self._wesn)
            demname = dem
            if t is not None:
                break

        if not t:
            raise NoTopo()

        return t, demname

    def _prep_topo(self, k):
        gmt = self._gmt
        t, demname = self._get_topo_tile(k)

        if demname not in self._prep_topo_have:

            grdfile = gmt.tempfilename()
            gmtpy.savegrd(t.x(),
                          t.y(),
                          t.data,
                          filename=grdfile,
                          naming='lonlat')

            if self.illuminate:
                if k == 'ocean':
                    factor = self.illuminate_factor_ocean
                else:
                    factor = self.illuminate_factor_land

                ilumfn = gmt.tempfilename()
                gmt.grdgradient(grdfile,
                                N='e%g' % factor,
                                A=-45,
                                G=ilumfn,
                                out_discard=True)

                ilumargs = ['-I%s' % ilumfn]
            else:
                ilumargs = []

            if self.replace_topo_color_only:
                t2 = self.replace_topo_color_only
                grdfile2 = gmt.tempfilename()

                gmtpy.savegrd(t2.x(),
                              t2.y(),
                              t2.data,
                              filename=grdfile2,
                              naming='lonlat')

                gmt.grdsample(grdfile2,
                              G=grdfile,
                              Q='l',
                              I='%g/%g' % (t.dx, t.dy),
                              R=grdfile,
                              out_discard=True)

                gmt.grdmath(grdfile,
                            '0.0',
                            'AND',
                            '=',
                            grdfile2,
                            out_discard=True)

                grdfile = grdfile2

            self._prep_topo_have[demname] = grdfile, ilumargs

        return self._prep_topo_have[demname]

    def _draw_topo(self):
        widget = self._widget
        scaler = self._scaler
        gmt = self._gmt
        cres = self._coastline_resolution
        minarea = self._minarea

        JXY = widget.JXY()
        R = scaler.R()

        try:
            grdfile, ilumargs = self._prep_topo('ocean')
            gmt.pscoast(D=cres, S='c', A=minarea, *(JXY + R))
            gmt.grdimage(grdfile,
                         C=topo.cpt(self.topo_cpt_wet),
                         *(ilumargs + JXY + R))
            gmt.pscoast(Q=True, *(JXY + R))
            self._have_topo_ocean = True
        except NoTopo:
            self._have_topo_ocean = False

        try:
            grdfile, ilumargs = self._prep_topo('land')
            gmt.pscoast(D=cres, G='c', A=minarea, *(JXY + R))
            gmt.grdimage(grdfile,
                         C=topo.cpt(self.topo_cpt_dry),
                         *(ilumargs + JXY + R))
            gmt.pscoast(Q=True, *(JXY + R))
            self._have_topo_land = True
        except NoTopo:
            self._have_topo_land = False

    def _draw_topo_scale(self, label='Elevation [km]'):
        dry = read_cpt(topo.cpt(self.topo_cpt_dry))
        wet = read_cpt(topo.cpt(self.topo_cpt_wet))
        combi = cpt_merge_wet_dry(wet, dry)
        for level in combi.levels:
            level.vmin /= km
            level.vmax /= km

        topo_cpt = self.gmt.tempfilename()
        write_cpt(combi, topo_cpt)

        (w, h), (xo, yo) = self.widget.get_size()
        self.gmt.psscale(
            D='%gp/%gp/%gp/%gph' %
            (xo + 0.5 * w, yo - 2.0 * gmtpy.cm, w, 0.5 * gmtpy.cm),
            C=topo_cpt,
            B='1:%s:' % label)

    def _draw_basefeatures(self):
        gmt = self._gmt
        cres = self._coastline_resolution
        rivers = self._rivers
        minarea = self._minarea

        color_wet = self.color_wet
        color_dry = self.color_dry

        if self.show_rivers and rivers:
            rivers = ['-Ir/0.25p,%s' % gmtpy.color(self.color_wet)]
        else:
            rivers = []

        fill = {}
        if not self._have_topo_land:
            fill['G'] = color_dry

        if not self._have_topo_ocean:
            fill['S'] = color_wet

        gmt.pscoast(D=cres,
                    W='thinnest,%s' %
                    gmtpy.color(darken(gmtpy.color_tup(color_dry))),
                    A=minarea,
                    *(rivers + self._jxyr),
                    **fill)

    def _draw_axes(self):
        gmt = self._gmt
        scaler = self._scaler
        widget = self._widget

        if self.axes_layout is None:
            if self.lat > 0.0:
                axes_layout = 'WSen'
            else:
                axes_layout = 'WseN'
        else:
            axes_layout = self.axes_layout

        scale_km = gmtpy.nice_value(self.radius / 5.) / 1000.

        if self.show_center_mark:
            gmt.psxy(in_rows=[[self.lon, self.lat]],
                     S='c20p',
                     W='2p,black',
                     *self._jxyr)

        if self.show_grid:
            btmpl = ('%(xinc)gg%(xinc)g:%(xlabel)s:/'
                     '%(yinc)gg%(yinc)g:%(ylabel)s:')
        else:
            btmpl = '%(xinc)g:%(xlabel)s:/%(yinc)g:%(ylabel)s:'

        gmt.psbasemap(B=(btmpl % scaler.get_params()) + axes_layout,
                      L=('x%gp/%gp/%g/%g/%gk' %
                         (6. / 7 * widget.width(), widget.height() / 7.,
                          self.lon, self.lat, scale_km)),
                      *self._jxyr)

        if self.comment:
            fontsize = self.gmt.to_points(
                self.gmt.gmt_config['LABEL_FONT_SIZE'])

            _, east, south, _ = self._wesn
            gmt.pstext(in_rows=[[1, 0, fontsize, 0, 0, 'BR', self.comment]],
                       N=True,
                       R=(0, 1, 0, 1),
                       D='%gp/%gp' % (-fontsize * 0.2, fontsize * 0.3),
                       *widget.PXY())

    def draw_axes(self):
        if not self._have_drawn_axes:
            self._draw_axes()
            self._have_drawn_axes = True

    def _have_coastlines(self):
        gmt = self._gmt
        cres = self._coastline_resolution
        minarea = self._minarea

        checkfile = gmt.tempfilename()

        gmt.pscoast(M=True,
                    D=cres,
                    W='thinnest/black',
                    A=minarea,
                    out_filename=checkfile,
                    *self._jxyr)

        with open(checkfile, 'r') as f:
            for line in f:
                ls = line.strip()
                if ls.startswith('#') or ls.startswith('>') or ls == '':
                    continue
                plon, plat = [float(x) for x in ls.split()]
                if point_in_region((plon, plat), self._wesn):
                    return True

        return False

    def project(self, lats, lons):
        onepoint = False
        if isinstance(lats, float) and isinstance(lons, float):
            lats = [lats]
            lons = [lons]
            onepoint = True

        j, _, _, r = self.jxyr
        (xo, yo) = self.widget.get_size()[1]
        f = StringIO()
        self.gmt.mapproject(j, r, in_columns=(lons, lats), out_stream=f, D='p')
        f.seek(0)
        data = num.loadtxt(f, ndmin=2)
        xs, ys = data.T
        if onepoint:
            xs = xs[0]
            ys = ys[0]
        return xs, ys

    def _draw_labels(self):
        if self._labels:
            fontsize = self.gmt.to_points(
                self.gmt.gmt_config['LABEL_FONT_SIZE'])

            n = len(self._labels)

            lons, lats, texts, sx, sy, styles = zip(*self._labels)

            sx = num.array(sx, dtype=num.float)
            sy = num.array(sy, dtype=num.float)

            j, _, _, r = self.jxyr
            f = StringIO()
            self.gmt.mapproject(j,
                                r,
                                in_columns=(lons, lats),
                                out_stream=f,
                                D='p')
            f.seek(0)
            data = num.loadtxt(f, ndmin=2)
            xs, ys = data.T
            dxs = num.zeros(n)
            dys = num.zeros(n)

            g = gmtpy.GMT()
            g.psbasemap('-G0', finish=True, *(j, r))
            l, b, r, t = g.bbox()
            h = (t - b)
            w = (r - l)

            for i in xrange(n):
                g = gmtpy.GMT()
                g.pstext(in_rows=[[0, 0, fontsize, 0, 1, 'BL', texts[i]]],
                         finish=True,
                         R=(0, 1, 0, 1),
                         J='x10p',
                         N=True,
                         **styles[i])

                fn = g.tempfilename()
                g.save(fn)

                (_, stderr) = Popen([
                    'gs', '-q', '-dNOPAUSE', '-dBATCH', '-r720',
                    '-sDEVICE=bbox', fn
                ],
                                    stderr=PIPE).communicate()

                dx, dy = None, None
                for line in stderr.splitlines():
                    if line.startswith('%%HiResBoundingBox:'):
                        l, b, r, t = [float(x) for x in line.split()[-4:]]
                        dx, dy = r - l, t - b

                dxs[i] = dx
                dys[i] = dy

            la = num.logical_and
            anchors_ok = (
                la(xs + sx + dxs < w, ys + sy + dys < h),
                la(xs - sx - dxs > 0., ys - sy - dys > 0.),
                la(xs + sx + dxs < w, ys - sy - dys > 0.),
                la(xs - sx - dxs > 0., ys + sy + dys < h),
            )

            arects = [(xs, ys, xs + sx + dxs, ys + sy + dys),
                      (xs - sx - dxs, ys - sy - dys, xs, ys),
                      (xs, ys - sy - dys, xs + sx + dxs, ys),
                      (xs - sx - dxs, ys, xs, ys + sy + dys)]

            def no_points_in_rect(xs, ys, xmin, ymin, xmax, ymax):
                xx = not num.any(
                    la(la(xmin < xs, xs < xmax), la(ymin < ys, ys < ymax)))
                return xx

            for i in xrange(n):
                for ianch in xrange(4):
                    anchors_ok[ianch][i] &= no_points_in_rect(
                        xs, ys, *[xxx[i] for xxx in arects[ianch]])

            anchor_choices = []
            anchor_take = []
            for i in xrange(n):
                choices = [
                    ianch for ianch in xrange(4) if anchors_ok[ianch][i]
                ]
                anchor_choices.append(choices)
                if choices:
                    anchor_take.append(choices[0])
                else:
                    anchor_take.append(None)

            def roverlaps(a, b):
                return (a[0] < b[2] and b[0] < a[2] and a[1] < b[3]
                        and b[1] < a[3])

            def cost(anchor_take):
                noverlaps = 0
                for i in xrange(n):
                    for j in xrange(n):
                        if i != j:
                            i_take = anchor_take[i]
                            j_take = anchor_take[j]
                            if i_take is None or j_take is None:
                                continue
                            r_i = [xxx[i] for xxx in arects[i_take]]
                            r_j = [xxx[j] for xxx in arects[j_take]]
                            if roverlaps(r_i, r_j):
                                noverlaps += 1

                return noverlaps

            cur_cost = cost(anchor_take)
            imax = 30
            while cur_cost != 0 and imax > 0:
                for i in xrange(n):
                    for t in anchor_choices[i]:
                        anchor_take_new = list(anchor_take)
                        anchor_take_new[i] = t
                        new_cost = cost(anchor_take_new)
                        if new_cost < cur_cost:
                            anchor_take = anchor_take_new
                            cur_cost = new_cost

                imax -= 1

            while cur_cost != 0:
                for i in xrange(n):
                    anchor_take_new = list(anchor_take)
                    anchor_take_new[i] = None
                    new_cost = cost(anchor_take_new)
                    if new_cost < cur_cost:
                        anchor_take = anchor_take_new
                        cur_cost = new_cost
                        break

            anchor_strs = ['BL', 'TR', 'TL', 'BR']

            for i in xrange(n):
                ianchor = anchor_take[i]
                if ianchor is not None:
                    anchor = anchor_strs[ianchor]
                    yoff = [-sy[i], sy[i]][anchor[0] == 'B']
                    xoff = [-sx[i], sx[i]][anchor[1] == 'L']
                    row = (lons[i], lats[i], fontsize, 0, 1, anchor, texts[i])
                    self.gmt.pstext(in_rows=[row],
                                    D='%gp/%gp' % (xoff, yoff),
                                    *self.jxyr,
                                    **styles[i])

    def draw_labels(self):
        if not self._have_drawn_labels:
            self._draw_labels()
            self._have_drawn_labels = True

    def add_label(self, lat, lon, text, offset_x=5., offset_y=5., style={}):
        self._labels.append((lon, lat, text, offset_x, offset_y, style))

    def cities_in_region(self):
        from pyrocko import geonames
        cities = list(
            geonames.load('cities1000.zip',
                          'cities1000.txt',
                          region=self.wesn,
                          minpop=0))

        cities.extend(self.custom_cities)
        cities.sort(key=lambda x: x.population)

        return cities

    def draw_cities(
        self,
        exact=None,
        include=[],
        exclude=['City of London'],  # duplicate entry in geonames
        nmax_soft=10,
        psxy_style=dict(S='s5p', G='black')):

        cities = self.cities_in_region()

        if exact is not None:
            cities = [c for c in cities if c.name in exact]
            minpop = None
        else:
            cities = [c for c in cities if c.name not in exclude]
            minpop = 10**3
            for minpop_new in [1e3, 3e3, 1e4, 3e4, 1e5, 3e5, 1e6, 3e6, 1e7]:
                cities_new = [c for c in cities if c.population > minpop_new]

                if len(cities_new) == 0 or (len(cities_new) < 3
                                            and len(cities) < nmax_soft * 2):
                    break

                cities = cities_new
                minpop = minpop_new
                if len(cities) <= nmax_soft:
                    break

        if cities:
            lats = [c.lat for c in cities]
            lons = [c.lon for c in cities]

            self.gmt.psxy(in_columns=(lons, lats), *self.jxyr, **psxy_style)

            for c in cities:
                try:
                    text = c.name.encode('iso-8859-1')
                except UnicodeEncodeError:
                    text = c.asciiname

                self.add_label(c.lat, c.lon, text)

        self._cities_minpop = minpop
class PlotSettings(Object):
    trace_filename = String.T(
        help="filename of beam or trace to use for "
        "plotting, incl. path.",
        optional=True,
    )
    station_filename = String.T(
        help="filename containing station meta "
        "information related to *trace_filename*.",
        optional=True,
    )
    event_filename = String.T(
        help="filename containing event information "
        "including the expected moment tensor.",
        default="event.pf",
    )
    store_id = String.T(help="Store ID to use for generating the synthetic "
                        "traces.",
                        optional=True)
    store_superdirs = List.T(String.T(), optional=True)
    depth = Float.T(help="Depth [km] where to put the trace.", default=10.0)
    depths = String.T(
        help="Synthetic source depths [km]. start:stop:delta. "
        "default: 0:15:1",
        optional=True,
        default="0:15:1",
    )
    filters = List.T(
        FrequencyResponse.T(help="List of filters used to filter "
                            "the traces"))
    zoom = List.T(
        Float.T(),
        help="Window to visualize with reference to the P "
        "phase onset [s].",
        default=[-7, 15],
    )
    correction = Float.T(help="time shift, to move beam trace.", default=0.0)
    normalize = Bool.T(help="normalize by maximum amplitude", default=True)
    save_as = String.T(default="depth_%(array-id)s.png",
                       help="filename of output figure")
    force_nearest_neighbor = Bool.T(help="Handles OutOfBounds exceptions. "
                                    "applies only laterally!",
                                    default=False)
    auto_caption = Bool.T(
        help="Add a caption giving basic information to the figure.",
        default=False)
    title = String.T(default="%(array-id)s - %(event_name)s",
                     help="Add default title.")
    quantity = String.T(
        default="velocity",
        help="velocity-> differentiate synthetic."
        "displacement-> integrate recorded",
    )
    gain = Float.T(default=1.0, help="Gain factor")
    gain_record = Float.T(default=1.0, help="Gain factor")
    color = String.T(help="Trace color", default="blue")

    def update_from_args(self, args):
        kwargs = {}
        try:
            hp, lp = args.filter.split(":")
            filters = [
                ButterworthResponse(corner=float(lp), order=4, type="low"),
                ButterworthResponse(corner=float(hp), order=4, type="high"),
            ]
            kwargs.update({"filters": filters})
        except:
            pass

        kwargs.update(self.process_arglist(args))
        for arg, v in kwargs.items():
            setattr(self, arg, v)

    @classmethod
    def from_argument_parser(cls, args):
        try:
            hp, lp = args.filter.split(":")
        except AttributeError:
            hp, lp = (0.7, 4.5)
        filters = [
            ButterworthResponse(corner=float(lp), order=4, type="low"),
            ButterworthResponse(corner=float(hp), order=4, type="high"),
        ]
        kwargs = cls.process_arglist(args)
        return cls(filters=filters, **kwargs)

    def do_filter(self, tr):
        for f in self.filters:
            tr = tr.transfer(transfer_function=f,
                             tfade=10,
                             cut_off_fading=False)
        return tr

    @staticmethod
    def process_arglist(args):
        kwargs = {}
        for arg in arglist:
            try:
                val = getattr(args, arg)
                if arg == "zoom" and val:
                    val = val.split(":")
                    val = map(float, val)
                if val:
                    kwargs.update({arg: val})
            except AttributeError:
                logger.debug("%s not defined" % arg)
                continue

        return kwargs
Exemple #3
0
class InSARGenerator(TargetGenerator):
    # https://sentinel.esa.int/web/sentinel/user-guides/sentinel-1-sar/acquisition-modes/interferometric-wide-swath
    store_id = String.T(default=DEFAULT_STORE_ID,
                        help='Store ID for these stations.')

    inclination = Float.T(
        default=98.2,
        help='Inclination of the satellite orbit towards equatorial plane'
        ' in [deg]. Defaults to Sentinel-1 (98.1 deg).')
    apogee = Float.T(default=693. * km,
                     help='Apogee of the satellite in [m]. '
                     'Defaults to Sentinel-1 (693 km).')
    swath_width = Float.T(
        default=250 * km,
        help='Swath width in [m]. '
        'Defaults to Sentinel-1 Interfeometric Wide Swath Mode (IW).'
        ' (IW; 250 km).')
    track_length = Float.T(default=150 * km,
                           help='Track length in [m]. Defaults to 200 km.')
    incident_angle = Float.T(
        default=29.1,
        help='Near range incident angle in [deg]. Defaults to 29.1 deg;'
        ' Sentinel IW mode (29.1 - 46.0 deg).')
    resolution = Tuple.T(default=(250, 250),
                         help='Resolution of raster in east x north [px].')
    mask_water = Bool.T(default=True, help='Mask out water bodies.')
    noise_generator = NoiseGenerator.T(
        default=AtmosphericNoiseGenerator.D(),
        help='Add atmospheric noise model after Hansen, 2001.')

    def get_scene_patches(self):
        lat_center, lon_center = self.get_center_latlon()

        scene_patches = []
        for direction in ('Ascending', 'Descending'):
            patch = ScenePatch(lat_center=lat_center,
                               lon_center=lon_center,
                               time_master=0,
                               time_slave=0,
                               inclination=self.inclination,
                               apogee=self.apogee,
                               swath_width=self.swath_width,
                               track_length=self.track_length,
                               incident_angle=self.incident_angle,
                               resolution=self.resolution,
                               orbital_node=direction,
                               mask_water=self.mask_water)
            scene_patches.append(patch)

        return scene_patches

    def get_targets(self):
        targets = [s.get_target() for s in self.get_scene_patches()]

        for t in targets:
            t.store_id = self.store_id

        return targets

    def get_insar_scenes(self, engine, sources, tmin=None, tmax=None):
        logger.debug('Forward modelling InSAR displacement...')

        scenario_tmin, scenario_tmax = self.get_time_range(sources)

        try:
            resp = engine.process(sources, self.get_targets(), nthreads=0)
        except gf.meta.OutOfBounds as e:
            logger.warning('Could not calculate InSAR displacements'
                           ' - the GF store\'s extend is too small!')
            return []

        scenes = [res.scene for res in resp.static_results()]

        tmin, tmax = self.get_time_range(sources)
        for sc in scenes:
            sc.meta.time_master = float(tmin)
            sc.meta.time_slave = float(tmax)

        scenes_asc = [
            sc for sc in scenes if sc.config.meta.orbital_node == 'Ascending'
        ]
        scenes_dsc = [
            sc for sc in scenes if sc.config.meta.orbital_node == 'Descending'
        ]

        def stack_scenes(scenes):
            base = scenes[0]
            for sc in scenes[1:]:
                base += sc
            return base

        scene_asc = stack_scenes(scenes_asc)
        scene_dsc = stack_scenes(scenes_dsc)

        if self.noise_generator:
            scene_asc.displacement += self.noise_generator.get_noise(scene_asc)
            scene_dsc.displacement += self.noise_generator.get_noise(scene_dsc)

        return scene_asc, scene_dsc

    def dump_data(self,
                  engine,
                  sources,
                  path,
                  tmin=None,
                  tmax=None,
                  overwrite=False):
        path_insar = op.join(path, 'insar')
        util.ensuredir(path_insar)

        tmin, tmax = self.get_time_range(sources)
        tts = util.time_to_str

        fn_tpl = op.join(
            path_insar,
            'insar-scene-{orbital_node}_%s_%s' % (tts(tmin), tts(tmax)))

        def scene_fn(track):
            return fn_tpl.format(orbital_node=track.lower())

        for track in ('ascending', 'descending'):
            fn = '%s.yml' % scene_fn(track)

            if op.exists(fn) and not overwrite:
                logger.debug('Scene exists: %s' % fn)
                continue

            scenes = self.get_insar_scenes(engine, sources, tmin, tmax)
            for sc in scenes:
                fn = scene_fn(sc.config.meta.orbital_node)
                logger.debug('Writing %s' % fn)
                sc.save('%s.npz' % fn)

        return [path_insar]

    def add_map_artists(self, engine, sources, automap):
        pass
Exemple #4
0
class LocationGenerator(Generator):

    avoid_water = Bool.T(
        default=True,
        help='Set whether wet areas should be avoided.')
    center_lat = Float.T(
        optional=True,
        help='Center latitude for the random locations in [deg].')
    center_lon = Float.T(
        optional=True,
        help='Center longitude for the random locations in [deg].')
    radius = Float.T(
        optional=True,
        help='Radius for distribution of random locations [m].')
    ntries = Int.T(
        default=500,
        help='Maximum number of tries to find a location satisifying all '
             'given constraints')

    def __init__(self, **kwargs):
        Generator.__init__(self, **kwargs)
        self._center_latlon = None

    def clear(self):
        Generator.clear(self)
        self._center_latlon = None

    def get_center_latlon(self):
        assert (self.center_lat is None) == (self.center_lon is None)

        if self._center_latlon is None:

            if self.center_lat is not None and self.center_lon is not None:
                self._center_latlon = self.center_lat, self.center_lon

            else:
                if self._parent:
                    self._center_latlon = self._parent.get_center_latlon()
                else:
                    rstate = self.get_rstate(0)
                    lat = random_lat(rstate)
                    lon = rstate.uniform(-180., 180.)
                    self._center_latlon = lat, lon

        return self._center_latlon

    def get_radius(self):
        if self.radius is not None:
            return self.radius
        elif self._parent is not None:
            return self._parent.get_radius()
        else:
            return None

    def get_latlon(self, i):
        rstate = self.get_rstate(i)
        for itry in range(self.ntries):
            radius = self.get_radius()
            if radius is None:
                lat = random_lat(rstate)
                lon = rstate.uniform(-180., 180.)
            else:
                lat_center, lon_center = self.get_center_latlon()
                while True:
                    north = rstate.uniform(-radius, radius)
                    east = rstate.uniform(-radius, radius)
                    if math.sqrt(north**2 + east**2) <= radius:
                        break

                lat, lon = od.ne_to_latlon(lat_center, lon_center, north, east)

            if not self.avoid_water or is_on_land(lat, lon):
                return lat, lon

        if self.avoid_water:
            sadd = ' (avoiding water)'

        raise ScenarioError('could not generate location%s' % sadd)
Exemple #5
0
class PhaseRatioTargetGroup(TargetGroup):
    '''
    Generate targets to compare ratios or log ratios of two seismic phases.

      misfit = | a_obs / (a_obs + b_obs)  - a_syn / (a_syn + b_syn) |

    or

      misfit = | log(a_obs / (a_obs + b_obs) + waterlevel)  -
                 log(a_syn / (a_syn + b_syn) + waterlevel) |
    '''

    distance_min = Float.T(optional=True)
    distance_max = Float.T(optional=True)
    distance_3d_min = Float.T(optional=True)
    distance_3d_max = Float.T(optional=True)
    depth_min = Float.T(optional=True)
    depth_max = Float.T(optional=True)
    measure_a = fm.FeatureMeasure.T()
    measure_b = fm.FeatureMeasure.T()
    interpolation = gf.InterpolationMethod.T()
    store_id = gf.StringID.T(optional=True)

    fit_log_ratio = Bool.T(
        default=True,
        help='if true, compare synthetic and observed log ratios')

    fit_log_ratio_waterlevel = Float.T(
        default=0.01,
        help='waterlevel added to both ratios when comparing on logarithmic '
        'scale, to avoid log(0)')

    def get_targets(self, ds, event, default_path):
        logger.debug('Selecting phase ratio targets...')
        origin = event
        targets = []

        for st in ds.get_stations():
            blacklisted = False
            for measure in [self.measure_a, self.measure_b]:
                for cha in measure.channels:
                    if ds.is_blacklisted((st.nsl() + (cha, ))):
                        blacklisted = True

            target = PhaseRatioTarget(
                codes=st.nsl(),
                lat=st.lat,
                lon=st.lon,
                depth=st.depth,
                interpolation=self.interpolation,
                store_id=self.store_id,
                measure_a=self.measure_a,
                measure_b=self.measure_b,
                manual_weight=self.weight,
                normalisation_family=self.normalisation_family,
                path=self.path or default_path,
                backazimuth=0.0,
                fit_log_ratio=self.fit_log_ratio,
                fit_log_ratio_waterlevel=self.fit_log_ratio_waterlevel)

            if blacklisted:
                log_exclude(target, 'blacklisted')
                continue

            if self.distance_min is not None and \
               target.distance_to(origin) < self.distance_min:
                log_exclude(target, 'distance < distance_min')
                continue

            if self.distance_max is not None and \
               target.distance_to(origin) > self.distance_max:
                log_exclude(target, 'distance > distance_max')
                continue

            if self.distance_3d_min is not None and \
               target.distance_3d_to(origin) < self.distance_3d_min:
                log_exclude(target, 'distance_3d < distance_3d_min')
                continue

            if self.distance_3d_max is not None and \
               target.distance_3d_to(origin) > self.distance_3d_max:
                log_exclude(target, 'distance_3d > distance_3d_max')
                continue

            if self.depth_min is not None and \
               target.depth < self.depth_min:
                log_exclude(target, 'depth < depth_min')
                continue

            if self.depth_max is not None and \
               target.depth > self.depth_max:
                log_exclude(target, 'depth > depth_max')
                continue

            bazi, _ = target.azibazi_to(origin)
            target.backazimuth = bazi
            target.set_dataset(ds)
            targets.append(target)

        return targets
Exemple #6
0
class HistogramPlot(PlotConfig):
    '''
    Histograms or Gaussian kernel densities (default) of all parameters
    (marginal distributions of model parameters).

    The histograms (by default shown as Gaussian kernel densities) show (red
    curved solid line) the distributions of the parameters (marginals) along
    with some characteristics: The red solid vertical line gives the median of
    the distribution and the dashed red vertical line the mean value. Dark gray
    vertical lines show reference values (given in the event.txt file). The
    overlapping red-shaded areas show the 68% confidence intervals (innermost
    area), the 90% confidence intervals (middle area) and the minimum and
    maximum values (widest area). The plot ranges are defined by the given
    parameter bounds and show the model space. Well resolved model parameters
    show peaked distributions.
    '''

    name = 'histogram'
    size_cm = Tuple.T(2, Float.T(), default=(12.5, 7.5))
    exclude = List.T(String.T())
    include = List.T(String.T())
    method = StringChoice.T(choices=['gaussian_kde', 'histogram'],
                            default='gaussian_kde')
    show_reference = Bool.T(default=True)

    def make(self, environ):
        cm = environ.get_plot_collection_manager()
        history = environ.get_history(subset='harvest')

        mpl_init(fontsize=self.font_size)
        cm.create_group_mpl(self,
                            self.draw_figures(history),
                            title=u'Histogram',
                            section='solution',
                            feather_icon='bar-chart-2',
                            description=u'''
Distribution of the problem's parameters.

The histograms are shown either as Gaussian kernel densities (red curved solid
line) or as bar plots the distributions of the parameters (marginals) along
with some characteristics:

The red solid vertical line gives the median of the distribution and the dashed
red vertical line the mean value. Dark gray vertical lines show reference
parameter values if given in the event.txt file. The overlapping red-shaded
areas show the 68% confidence intervals (innermost area), the 90% confidence
intervals (middle area) and the minimum and maximum values (widest area). The
plot ranges are defined by the given parameter bounds and show the model
space.
''')

    def draw_figures(self, history):

        import scipy.stats
        from grond.core import make_stats

        exclude = self.exclude
        include = self.include
        figsize = self.size_inch
        fontsize = self.font_size
        method = self.method
        ref_color = mpl_color('aluminium6')
        stats_color = mpl_color('scarletred2')
        bar_color = mpl_color('scarletred1')
        stats_color3 = mpl_color('scarletred3')

        problem = history.problem
        misfits = history.misfits

        models = history.models

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

        xref = problem.get_reference_model(expand=True)

        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
        del iselected

        pnames = [
            problem.combined[smap[iselected]].name
            for iselected in range(nselected)
        ]

        rstats = make_stats(problem, models, misfits, pnames=pnames)

        for iselected in range(nselected):
            ipar = smap[iselected]
            par = problem.combined[ipar]
            vs = problem.extract(models, ipar)
            vmin, vmax = bounds[ipar]

            fig = plt.figure(figsize=figsize)
            labelpos = mpl_margins(fig,
                                   nw=1,
                                   nh=1,
                                   w=7.,
                                   bottom=5.,
                                   top=1,
                                   units=fontsize)

            axes = fig.add_subplot(1, 1, 1)
            labelpos(axes, 2.5, 2.0)
            axes.set_xlabel(par.get_label())
            axes.set_ylabel('PDF')
            axes.set_xlim(*fixlim(*par.scaled((vmin, vmax))))

            if method == 'gaussian_kde':
                try:
                    kde = scipy.stats.gaussian_kde(vs)
                except Exception:
                    logger.warn(
                        'Cannot create plot histogram with gaussian_kde: '
                        'possibly all samples have the same value.')
                    continue

                vps = num.linspace(vmin, vmax, 600)
                pps = kde(vps)

                axes.plot(par.scaled(vps),
                          par.inv_scaled(pps),
                          color=stats_color)

            elif method == 'histogram':
                pps, edges = num.histogram(vs,
                                           bins=num.linspace(vmin,
                                                             vmax,
                                                             num=40),
                                           density=True)
                vps = 0.5 * (edges[:-1] + edges[1:])

                axes.bar(par.scaled(vps),
                         par.inv_scaled(pps),
                         par.scaled(2. * (vps - edges[:-1])),
                         color=bar_color)

            pstats = rstats.parameter_stats_list[iselected]

            axes.axvspan(par.scaled(pstats.minimum),
                         par.scaled(pstats.maximum),
                         color=stats_color,
                         alpha=0.1)
            axes.axvspan(par.scaled(pstats.percentile16),
                         par.scaled(pstats.percentile84),
                         color=stats_color,
                         alpha=0.1)
            axes.axvspan(par.scaled(pstats.percentile5),
                         par.scaled(pstats.percentile95),
                         color=stats_color,
                         alpha=0.1)

            axes.axvline(par.scaled(pstats.median),
                         color=stats_color3,
                         alpha=0.5)
            axes.axvline(par.scaled(pstats.mean),
                         color=stats_color3,
                         ls=':',
                         alpha=0.5)

            if self.show_reference:
                axes.axvline(par.scaled(problem.extract(xref, ipar)),
                             color=ref_color)

            item = PlotItem(name=par.name)
            item.attributes['parameters'] = [par.name]
            yield item, fig
Exemple #7
0
class MTDecompositionPlot(PlotConfig):
    '''
    Moment tensor decomposition plot.
    '''

    name = 'mt_decomposition'
    size_cm = Tuple.T(2, Float.T(), default=(15., 5.))
    cluster_attribute = meta.StringID.T(
        optional=True, help='name of attribute to use as cluster IDs')
    show_reference = Bool.T(default=True)

    def make(self, environ):
        cm = environ.get_plot_collection_manager()
        history = environ.get_history(subset='harvest')
        mpl_init(fontsize=self.font_size)
        cm.create_group_mpl(self,
                            self.draw_figures(history),
                            title=u'MT Decomposition',
                            section='solution',
                            feather_icon='sun',
                            description=u'''
Moment tensor decomposition of the best-fitting solution into isotropic,
deviatoric and best double couple components.

Shown are the ensemble best, the ensemble mean%s and, if available, a reference
mechanism. The symbol size indicates the relative strength of the components.
The inversion result is consistent and stable if ensemble mean and ensemble
best have similar symbol size and patterns.
''' % (', cluster results' if self.cluster_attribute else ''))

    def draw_figures(self, history):

        fontsize = self.font_size

        fig = plt.figure(figsize=self.size_inch)
        axes = fig.add_subplot(1, 1, 1, aspect=1.0)
        fig.subplots_adjust(left=0., right=1., bottom=0., top=1.)

        problem = history.problem
        models = history.models

        if models.size == 0:
            logger.warn('Empty models vector.')
            return []

        # gms = problem.combine_misfits(history.misfits)
        # isort = num.argsort(gms)
        # iorder = num.empty_like(isort)
        # iorder[isort] = num.arange(iorder.size)[::-1]

        mean_source = stats.get_mean_source(problem, history.models)

        best_source = stats.get_best_source(problem, history.models,
                                            history.misfits)

        ref_source = problem.base_source

        nlines_max = int(round(self.size_cm[1] / 5. * 4. - 1.0))

        if self.cluster_attribute:
            cluster_sources = history.mean_sources_by_cluster(
                self.cluster_attribute)
        else:
            cluster_sources = []

        def get_deco(source):
            mt = source.pyrocko_moment_tensor()
            return mt.standard_decomposition()

        lines = []
        lines.append(
            ('Ensemble best', get_deco(best_source), mpl_color('aluminium5')))

        lines.append(
            ('Ensemble mean', get_deco(mean_source), mpl_color('aluminium5')))

        for (icluster, perc, source) in cluster_sources:
            if len(lines) < nlines_max - int(self.show_reference):
                lines.append((cluster_label(icluster, perc), get_deco(source),
                              cluster_color(icluster)))
            else:
                logger.warn(
                    'Skipping display of cluster %i because figure height is '
                    'too small. Figure height should be at least %g cm.' %
                    (icluster,
                     (3 + len(cluster_sources) + int(self.show_reference)) *
                     5 / 4.))

        if self.show_reference:
            lines.append(
                ('Reference', get_deco(ref_source), mpl_color('aluminium3')))

        moment_full_max = max(deco[-1][0] for (_, deco, _) in lines)

        for xpos, label in [(0., 'Full'), (2., 'Isotropic'),
                            (4., 'Deviatoric'), (6., 'CLVD'), (8., 'DC')]:

            axes.annotate(label,
                          xy=(1 + xpos, nlines_max),
                          xycoords='data',
                          xytext=(0., 0.),
                          textcoords='offset points',
                          ha='center',
                          va='center',
                          color='black',
                          fontsize=fontsize)

        for i, (label, deco, color_t) in enumerate(lines):
            ypos = nlines_max - i - 1.0

            [(moment_iso, ratio_iso, m_iso), (moment_dc, ratio_dc, m_dc),
             (moment_clvd, ratio_clvd, m_clvd),
             (moment_devi, ratio_devi, m_devi),
             (moment_full, ratio_full, m_full)] = deco

            size0 = moment_full / moment_full_max

            axes.annotate(label,
                          xy=(-2., ypos),
                          xycoords='data',
                          xytext=(0., 0.),
                          textcoords='offset points',
                          ha='left',
                          va='center',
                          color='black',
                          fontsize=fontsize)

            for xpos, mt_part, ratio, ops in [(0., m_full, ratio_full, '-'),
                                              (2., m_iso, ratio_iso, '='),
                                              (4., m_devi, ratio_devi, '='),
                                              (6., m_clvd, ratio_clvd, '+'),
                                              (8., m_dc, ratio_dc, None)]:

                if ratio > 1e-4:
                    try:
                        beachball.plot_beachball_mpl(
                            mt_part,
                            axes,
                            beachball_type='full',
                            position=(1. + xpos, ypos),
                            size=0.9 * size0 * math.sqrt(ratio),
                            size_units='data',
                            color_t=color_t,
                            linewidth=1.0)

                    except beachball.BeachballError as e:
                        logger.warn(str(e))

                        axes.annotate('ERROR',
                                      xy=(1. + xpos, ypos),
                                      ha='center',
                                      va='center',
                                      color='red',
                                      fontsize=fontsize)

                else:
                    axes.annotate('N/A',
                                  xy=(1. + xpos, ypos),
                                  ha='center',
                                  va='center',
                                  color='black',
                                  fontsize=fontsize)

                if ops is not None:
                    axes.annotate(ops,
                                  xy=(2. + xpos, ypos),
                                  ha='center',
                                  va='center',
                                  color='black',
                                  fontsize=fontsize)

        axes.axison = False
        axes.set_xlim(-2.25, 9.75)
        axes.set_ylim(-0.5, nlines_max + 0.5)

        item = PlotItem(name='main')
        return [[item, fig]]
Exemple #8
0
class OkadaSegment(OkadaSource):
    enabled = Bool.T(default=True, optional=True)
Exemple #9
0
class ReportInfo(Object):
    title = Unicode.T(optional=True)
    description = Unicode.T(optional=True)
    version_info = info.VersionInfo.T()
    have_archive = Bool.T(optional=True)
Exemple #10
0
class PinkyConfig(Object):
    '''Configuration of data IO and data preprocessing'''

    blacklist = List.T(String.T(),
                       help='List blacklist patterns (may contain wild cards')

    stack_channels = Bool.T(
        default=False,
        help='If *True* stack abs. amplitudes of all channels of a station')

    sample_length = Float.T(optional=True,
                            help='Length in seconds. Not needed \
        when using TFRecordData')

    data_generator = DataGeneratorBase.T()
    evaluation_data_generator = DataGeneratorBase.T(optional=True)
    prediction_data_generator = DataGeneratorBase.T(optional=True)

    normalization = Normalization.T(default=Normalization(), optional=True)

    absolute = Bool.T(help='Use absolute amplitudes', default=False)

    imputation = Imputation.T(optional=True, help='How to mask and fill gaps')

    reference_station = String.T(
        optional=True,
        help='define the stations used as a reference location as NSL string')

    fn_stations = String.T(optional=True, help='')

    reference_target = Target.T(optional=True)

    n_classes = Int.T(default=3)

    # Not implemented for DataGeneratorBase
    highpass = Float.T(optional=True, help='Highpass filter corner frequency')
    lowpass = Float.T(optional=True, help='Lowpass filter corner frequency')

    highpass_order = Int.T(default=4, optional=True)
    lowpass_order = Int.T(default=4, optional=True)

    normalize_labels = Bool.T(default=True, help='Normalize labels by std')

    tpad = Float.T(default=0.,
                   help='padding between p phase onset and data chunk start')

    t_translation_max = Float.T(
        default=0.,
        help='Augment data by uniformly shifting examples in time limited by '
        'this parameters. This will increase *tpad*')

    deltat_want = Float.T(
        optional=True,
        help='If set, down or upsample traces to this sampling rate.')

    # These value or not meant to be modified. If they are set in a
    # configuration this happened automatically to port values accross
    # configurations.
    # _label_scale = num.ones(3, dtype=num.float32, help='(Don\'t modify)')
    # _label_median = num.ones(3, dtype=num.float32, help='(Don\'t modify)')
    _channels = List.T(Tuple.T(4, String.T()),
                       optional=True,
                       help='(Don\'t modify)')
    _n_samples = Int.T(optional=True, help='(Don\'t modify)')

    def __init__(self, *args, **kwargs):
        super(PinkyConfig, self).__init__(*args, **kwargs)
        stations = load_stations(self.fn_stations)
        self.targets = stations_to_targets(stations)

        if not self.reference_target:
            targets_by_code = {'.'.join(t.codes[:3]): t for t in self.targets}
            self.reference_target = targets_by_code[self.reference_station]

    def setup(self):

        self.data_generator.set_config(self)
        if self.normalize_labels:
            # To not break the label normalization, the data_generator used
            # for training is required in any case at the moment!
            # Better store normalization data during training to recycle at
            # prediction time.
            self._label_median = num.median(num.array(
                list(self.data_generator.iter_labels())),
                                            axis=0)

            self._label_scale = num.mean(
                num.std(num.array(list(self.data_generator.iter_labels())),
                        axis=0))

        if self.evaluation_data_generator:
            self.evaluation_data_generator.set_config(self)

        if self.prediction_data_generator:
            self.prediction_data_generator.set_config(self)

        self.set_n_samples()

        if self.stack_channels:

            self.data_generator = ChannelStackGenerator.from_generator(
                generator=self.data_generator)
            if self.evaluation_data_generator:
                self.evaluation_data_generator = ChannelStackGenerator.from_generator(
                    generator=self.evaluation_data_generator)
            if self.prediction_data_generator:
                self.prediction_data_generator = ChannelStackGenerator.from_generator(
                    generator=self.prediction_data_generator)

        # self.data_generator.setup()
        # self.evaluation_data_generator.setup()
        # if self.prediction_data_generator:
        #     self.prediction_data_generator.setup()

    def set_n_samples(self):
        '''Set number of sampes (n_samples) from first example of data
        generator. Note that this assumes that the evaluation data generator
        contains identical shaped examples.'''
        example, _ = next(self.data_generator.generate())
        self._n_samples = example.shape[1]
        assert (example.shape == self.tensor_shape)

    @property
    def effective_deltat(self):
        if self.deltat_want is None:
            return (self.sample_length + self.tpad) / self._n_samples
        else:
            return self.deltat_want

    @property
    def effective_tpad(self):
        tpad = self.tpad + self.t_translation_max
        if self.highpass is not None:
            tpad += 0.5 / self.highpass

        return tpad

    def normalize_label(self, label):
        '''label has to be a numpy array'''
        return (label - self._label_median) / self._label_scale

    def denormalize_label(self, label):
        '''label has to be a numpy array'''
        return (label * self._label_scale) + self._label_median

    @property
    def channels(self):
        return self._channels

    @channels.setter
    def channels(self, v):
        if self._channels:
            logger.warn('Setting channels although channels have been \
                    assigned before')
        self._channels = v

    @property
    def n_channels(self):
        return len(self._channels)

    @property
    def output_shapes(self):
        '''Return a tuple containing the shape of feature arrays and number of
        labels.
        '''
        return (self.tensor_shape, self.n_classes)

    @property
    def tensor_shape(self):
        return (self.n_channels, self._n_samples)
Exemple #11
0
class WaveformGenerator(TargetGenerator):

    station_generator = StationGenerator.T(
        default=RandomStationGenerator.D(),
        help='The StationGenerator for creating the stations.')

    noise_generator = WaveformNoiseGenerator.T(
        default=WhiteNoiseGenerator.D(),
        help='Add Synthetic noise on the waveforms.')

    store_id = gf.StringID.T(
        default=DEFAULT_STORE_ID,
        help='The GF store to use for forward-calculations.')

    seismogram_quantity = StringChoice.T(
        choices=['displacement', 'velocity', 'acceleration', 'counts'],
        default='displacement')

    vmin_cut = Float.T(
        default=2000.,
        help='Minimum velocity to seismic velicty to consider in the model.')
    vmax_cut = Float.T(
        default=8000.,
        help='Maximum velocity to seismic velicty to consider in the model.')

    fmin = Float.T(
        default=0.01,
        help='Minimum frequency/wavelength to resolve in the'
             ' synthetic waveforms.')

    tabulated_phases = List.T(
        gf.meta.TPDef.T(), optional=True,
        help='Define seismic phases to be calculated.')

    tabulated_phases_from_store = Bool.T(
        default=False,
        help='Calculate seismic phase arrivals for all travel-time tables '
             'defined in GF store.')

    tabulated_phases_noise_scale = Float.T(
        default=0.0,
        help='Standard deviation of normally distributed noise added to '
             'calculated phase arrivals.')

    taper = trace.Taper.T(
        optional=True,
        help='Time domain taper applied to synthetic waveforms.')

    compensate_synthetic_offsets = Bool.T(
        default=False,
        help='Center synthetic trace amplitudes using mean of waveform tips.')

    tinc = Float.T(
        optional=True,
        help='Time increment of waveforms.')

    continuous = Bool.T(
        default=True,
        help='Only produce traces that intersect with events.')

    def __init__(self, *args, **kwargs):
        super(WaveformGenerator, self).__init__(*args, **kwargs)
        self._targets = []
        self._piles = {}

    def _get_pile(self, path):
        apath = op.abspath(path)
        assert op.isdir(apath)

        if apath not in self._piles:
            fns = util.select_files(
                [apath], show_progress=False)

            p = pile.Pile()
            if fns:
                p.load_files(fns, fileformat='mseed', show_progress=False)

            self._piles[apath] = p

        return self._piles[apath]

    def get_stations(self):
        return self.station_generator.get_stations()

    def get_targets(self):
        if self._targets:
            return self._targets

        for station in self.get_stations():
            channel_data = []
            channels = station.get_channels()
            if channels:
                for channel in channels:
                    channel_data.append([
                        channel.name,
                        channel.azimuth,
                        channel.dip])

            else:
                for c_name in ['BHZ', 'BHE', 'BHN']:
                    channel_data.append([
                        c_name,
                        model.guess_azimuth_from_name(c_name),
                        model.guess_dip_from_name(c_name)])

            for c_name, c_azi, c_dip in channel_data:

                target = gf.Target(
                    codes=(
                        station.network,
                        station.station,
                        station.location,
                        c_name),
                    quantity='displacement',
                    lat=station.lat,
                    lon=station.lon,
                    north_shift=station.north_shift,
                    east_shift=station.east_shift,
                    depth=station.depth,
                    store_id=self.store_id,
                    optimization='enable',
                    interpolation='nearest_neighbor',
                    azimuth=c_azi,
                    dip=c_dip)

                self._targets.append(target)

        return self._targets

    def get_time_range(self, sources):
        dmin, dmax = self.station_generator.get_distance_range(sources)

        times = num.array([source.time for source in sources],
                          dtype=num.float)

        tmin_events = num.min(times)
        tmax_events = num.max(times)

        tmin = tmin_events + dmin / self.vmax_cut - 10.0 / self.fmin
        tmax = tmax_events + dmax / self.vmin_cut + 10.0 / self.fmin

        return tmin, tmax

    def get_codes_to_deltat(self, engine, sources):
        deltats = {}

        targets = self.get_targets()
        for source in sources:
            for target in targets:
                deltats[target.codes] = engine.get_store(
                    target.store_id).config.deltat

        return deltats

    def get_useful_time_increment(self, engine, sources):
        _, dmax = self.station_generator.get_distance_range(sources)
        tinc = dmax / self.vmin_cut + 2.0 / self.fmin

        deltats = set(self.get_codes_to_deltat(engine, sources).values())
        deltat = reduce(util.lcm, deltats)
        tinc = int(round(tinc / deltat)) * deltat
        return tinc

    def get_relevant_sources(self, sources, tmin, tmax):
        dmin, dmax = self.station_generator.get_distance_range(sources)
        trange = tmax - tmin
        tmax_pad = trange + tmax + dmin / self.vmax_cut
        tmin_pad = tmin - (dmax / self.vmin_cut + trange)

        return [s for s in sources if s.time < tmax_pad and s.time > tmin_pad]

    def get_waveforms(self, engine, sources, tmin, tmax):

        sources_relevant = self.get_relevant_sources(sources, tmin, tmax)
        if not (self.continuous or sources_relevant):
            return []

        trs = {}
        tts = util.time_to_str

        for nslc, deltat in self.get_codes_to_deltat(engine, sources).items():
            tr_tmin = int(round(tmin / deltat)) * deltat
            tr_tmax = (int(round(tmax / deltat))-1) * deltat
            nsamples = int(round((tr_tmax - tr_tmin) / deltat)) + 1

            tr = trace.Trace(
                *nslc,
                tmin=tr_tmin,
                ydata=num.zeros(nsamples),
                deltat=deltat)

            self.noise_generator.add_noise(tr)

            trs[nslc] = tr

        logger.debug('Forward modelling waveforms between %s - %s...'
                     % (tts(tmin, format='%Y-%m-%d_%H-%M-%S'),
                        tts(tmax, format='%Y-%m-%d_%H-%M-%S')))

        if not sources_relevant:
            return list(trs.values())

        targets = self.get_targets()
        response = engine.process(sources_relevant, targets)
        for source, target, res in response.iter_results(
                get='results'):

            if isinstance(res, gf.SeismosizerError):
                logger.warning(
                    'Out of bounds! \nTarget: %s\nSource: %s\n' % (
                        '.'.join(target.codes)), source)
                continue

            tr = res.trace.pyrocko_trace()

            candidate = trs[target.codes]
            if not candidate.overlaps(tr.tmin, tr.tmax):
                continue

            if self.compensate_synthetic_offsets:
                tr.ydata -= (num.mean(tr.ydata[-3:-1]) +
                             num.mean(tr.ydata[1:3])) / 2.

            if self.taper:
                tr.taper(self.taper)

            resp = self.get_transfer_function(target.codes)
            if resp:
                tr = tr.transfer(transfer_function=resp)

            candidate.add(tr)
            trs[target.codes] = candidate

        return list(trs.values())

    def get_onsets(self, engine, sources, *args, **kwargs):

        targets = {t.codes[:3]: t for t in self.get_targets()}

        markers = []
        for source in sources:
            ev = source.pyrocko_event()
            markers.append(EventMarker(ev))
            for nsl, target in targets.items():
                store = engine.get_store(target.store_id)
                if self.tabulated_phases:
                    tabulated_phases = self.tabulated_phases

                elif self.tabulated_phases_from_store:
                    tabulated_phases = store.config.tabulated_phases
                else:
                    tabulated_phases = []

                for phase in tabulated_phases:
                    t = store.t(phase.id, source, target)
                    if not t:
                        continue

                    noise_scale = self.tabulated_phases_noise_scale
                    if noise_scale != 0.0:
                        t += num.random.normal(scale=noise_scale)

                    t += source.time
                    markers.append(
                        PhaseMarker(
                            phasename=phase.id,
                            tmin=t,
                            tmax=t,
                            event=source.pyrocko_event(),
                            nslc_ids=(nsl+('*',),)
                            )
                        )
        return markers

    def get_transfer_function(self, codes):
        if self.seismogram_quantity == 'displacement':
            return None
        elif self.seismogram_quantity == 'velocity':
            return trace.DifferentiationResponse(1)
        elif self.seismogram_quantity == 'acceleration':
            return trace.DifferentiationResponse(2)
        elif self.seismogram_quantity == 'counts':
            raise NotImplementedError()

    def ensure_data(self, engine, sources, path, tmin=None, tmax=None):
        self.ensure_waveforms(engine, sources, path, tmin, tmax)
        self.ensure_responses(path)

    def ensure_waveforms(self, engine, sources, path, tmin=None, tmax=None):

        path_waveforms = op.join(path, 'waveforms')
        util.ensuredir(path_waveforms)

        p = self._get_pile(path_waveforms)

        nslc_ids = set(target.codes for target in self.get_targets())

        def have_waveforms(tmin, tmax):
            trs_have = p.all(
                tmin=tmin, tmax=tmax,
                load_data=False, degap=False,
                trace_selector=lambda tr: tr.nslc_id in nslc_ids)

            return any(tr.data_len() > 0 for tr in trs_have)

        def add_files(paths):
            p.load_files(paths, fileformat='mseed', show_progress=False)

        path_traces = op.join(
            path_waveforms,
            '%(wmin_year)s',
            '%(wmin_month)s',
            '%(wmin_day)s',
            'waveform_%(network)s_%(station)s_' +
            '%(location)s_%(channel)s_%(tmin)s_%(tmax)s.mseed')

        tmin_all, tmax_all = self.get_time_range(sources)
        tmin = tmin if tmin is not None else tmin_all
        tmax = tmax if tmax is not None else tmax_all
        tts = util.time_to_str

        tinc = self.tinc or self.get_useful_time_increment(engine, sources)
        tmin = math.floor(tmin / tinc) * tinc
        tmax = math.ceil(tmax / tinc) * tinc

        nwin = int(round((tmax - tmin) / tinc))

        pbar = None
        for iwin in range(nwin):
            tmin_win = tmin + iwin*tinc
            tmax_win = tmin + (iwin+1)*tinc

            if have_waveforms(tmin_win, tmax_win):
                continue

            if pbar is None:
                pbar = util.progressbar('Generating waveforms', (nwin-iwin))

            pbar.update(iwin)

            trs = self.get_waveforms(engine, sources, tmin_win, tmax_win)

            try:
                wpaths = io.save(
                    trs, path_traces,
                    additional=dict(
                        wmin_year=tts(tmin_win, format='%Y'),
                        wmin_month=tts(tmin_win, format='%m'),
                        wmin_day=tts(tmin_win, format='%d'),
                        wmin=tts(tmin_win, format='%Y-%m-%d_%H-%M-%S'),
                        wmax_year=tts(tmax_win, format='%Y'),
                        wmax_month=tts(tmax_win, format='%m'),
                        wmax_day=tts(tmax_win, format='%d'),
                        wmax=tts(tmax_win, format='%Y-%m-%d_%H-%M-%S')))

                for wpath in wpaths:
                    logger.debug('Generated file: %s' % wpath)

                add_files(wpaths)

            except FileSaveError as e:
                raise ScenarioError(str(e))

        if pbar is not None:
            pbar.finish()

    def ensure_responses(self, path):
        from pyrocko.io import stationxml

        path_responses = op.join(path, 'meta')
        util.ensuredir(path_responses)

        fn_stationxml = op.join(path_responses, 'stations.xml')
        if op.exists(fn_stationxml):
            return

        logger.debug('Writing waveform meta information to StationXML...')

        stations = self.station_generator.get_stations()
        sxml = stationxml.FDSNStationXML.from_pyrocko_stations(stations)

        sunit = {
            'displacement': 'M',
            'velocity': 'M/S',
            'acceleration': 'M/S**2',
            'counts': 'COUNTS'}[self.seismogram_quantity]

        response = stationxml.Response(
            instrument_sensitivity=stationxml.Sensitivity(
                value=1.,
                frequency=1.,
                input_units=stationxml.Units(sunit),
                output_units=stationxml.Units('COUNTS')),
            stage_list=[])

        for net, station, channel in sxml.iter_network_station_channels():
            channel.response = response

        sxml.dump_xml(filename=fn_stationxml)

    def add_map_artists(self, engine, sources, automap):
        automap.add_stations(self.get_stations())
Exemple #12
0
class SourceGenerator(LocationGenerator):

    nevents = Int.T(default=2)
    avoid_water = Bool.T(
        default=False, help='Avoid sources offshore under the ocean / lakes.')

    time_min = Timestamp.T(default=util.str_to_time('2017-01-01 00:00:00'))
    time_max = Timestamp.T(default=util.str_to_time('2017-01-03 00:00:00'))

    magnitude_min = Float.T(default=4.0, help='minimum moment magnitude')
    magnitude_max = Float.T(
        optional=True,
        help='if set, maximum moment magnitude for a uniform distribution. '
        'If set to ``None``, magnitudes are drawn using a '
        'Gutenberg-Richter distribution, see :gattr:`b_value`.')
    b_value = Float.T(
        optional=True,
        help='b-value for Gutenberg-Richter magnitude distribution. If unset, '
        'a value of 1 is assumed.')

    def __init__(self, *args, **kwargs):
        super(SourceGenerator, self).__init__(*args, **kwargs)
        if self.b_value is not None and self.magnitude_max is not None:
            raise ScenarioError(
                '%s: b_value and magnitude_max are mutually exclusive.' %
                self.__class__.__name__)

    def draw_magnitude(self, rstate):
        if self.b_value is None and self.magnitude_max is None:
            b_value = 1.0
        else:
            b_value = self.b_value

        if b_value is None:
            return rstate.uniform(self.magnitude_min, self.magnitude_max)
        else:
            return moment_tensor.rand_to_gutenberg_richter(
                rstate.rand(), b_value, magnitude_min=self.magnitude_min)

    def get_sources(self):
        sources = []
        for ievent in range(self.nevents):
            src = self.get_source(ievent)
            src.name = 'scenario_ev%03d' % (ievent + 1)
            sources.append(src)

        return sources

    def ensure_data(self, path):
        fn_sources = op.join(path, 'sources.yml')
        if not op.exists(fn_sources):
            with open(fn_sources, 'w') as f:
                for src in self.get_sources():
                    f.write(src.dump())

        fn_events = op.join(path, 'events.txt')
        if not op.exists(fn_events):
            with open(fn_events, 'w') as f:
                for isrc, src in enumerate(self.get_sources()):
                    f.write(src.pyrocko_event().dump())

    def add_map_artists(self, automap):
        pass
Exemple #13
0
class DatasetConfig(HasPaths):

    stations_path = Path.T(optional=True)
    stations_stationxml_paths = List.T(Path.T(), optional=True)
    events_path = Path.T(optional=True)
    waveform_paths = List.T(Path.T(), optional=True)
    clippings_path = Path.T(optional=True)
    responses_sacpz_path = Path.T(optional=True)
    responses_stationxml_paths = List.T(Path.T(), optional=True)
    station_corrections_path = Path.T(optional=True)
    apply_correction_factors = Bool.T(optional=True,
                                      default=True)
    apply_correction_delays = Bool.T(optional=True,
                                     default=True)
    extend_incomplete = Bool.T(default=False)
    picks_paths = List.T(Path.T())
    blacklist_paths = List.T(Path.T())
    blacklist = List.T(
        String.T(),
        help='stations/components to be excluded according to their STA, '
             'NET.STA, NET.STA.LOC, or NET.STA.LOC.CHA codes.')
    whitelist_paths = List.T(Path.T())
    whitelist = List.T(
        String.T(),
        optional=True,
        help='if not None, list of stations/components to include according '
             'to their STA, NET.STA, NET.STA.LOC, or NET.STA.LOC.CHA codes. '
             'Note: ''when whitelisting on channel level, both, the raw and '
             'the processed channel codes have to be listed.')

    def __init__(self, *args, **kwargs):
        HasPaths.__init__(self, *args, **kwargs)
        self._ds = {}

    def get_event_names(self):
        def extra(path):
            return expand_template(path, dict(
                event_name='*'))

        def fp(path):
            return self.expand_path(path, extra=extra)

        events = []
        for fn in glob.glob(fp(self.events_path)):
            events.extend(cached_load_events(fn))

        event_names = [ev.name for ev in events]
        return event_names

    def get_dataset(self, event_name):
        if event_name not in self._ds:
            def extra(path):
                return expand_template(path, dict(
                    event_name=event_name))

            def fp(path):
                return self.expand_path(path, extra=extra)

            ds = Dataset(event_name)
            ds.add_stations(
                pyrocko_stations_filename=fp(self.stations_path),
                stationxml_filenames=fp(self.stations_stationxml_paths))

            ds.add_events(filename=fp(self.events_path))

            if self.waveform_paths:
                ds.add_waveforms(paths=fp(self.waveform_paths))

            if self.clippings_path:
                ds.add_clippings(markers_filename=fp(self.clippings_path))

            if self.responses_sacpz_path:
                ds.add_responses(
                    sacpz_dirname=fp(self.responses_sacpz_path))

            if self.responses_stationxml_paths:
                ds.add_responses(
                    stationxml_filenames=fp(self.responses_stationxml_paths))

            if self.station_corrections_path:
                ds.add_station_corrections(
                    filename=fp(self.station_corrections_path))

            ds.apply_correction_factors = self.apply_correction_factors
            ds.apply_correction_delays = self.apply_correction_delays
            ds.extend_incomplete = self.extend_incomplete

            for picks_path in self.picks_paths:
                ds.add_picks(
                    filename=fp(picks_path))

            ds.add_blacklist(self.blacklist)
            ds.add_blacklist(filenames=fp(self.blacklist_paths))
            if self.whitelist:
                ds.add_whitelist(self.whitelist)
            if self.whitelist_paths:
                ds.add_whitelist(filenames=fp(self.whitelist_paths))

            self._ds[event_name] = ds

        return self._ds[event_name]
class SyntheticTest(Object):
    inject_solution = Bool.T(default=False)
    respect_data_availability = Bool.T(default=False)
    real_noise_scale = Float.T(default=0.0)
    white_noise_scale = Float.T(default=0.0)
    relative_white_noise_scale = Float.T(default=0.0)
    random_response_scale = Float.T(default=0.0)
    real_noise_toffset = Float.T(default=-3600.)
    random_seed = Int.T(optional=True)
    x = Dict.T(String.T(), Float.T())

    def __init__(self, **kwargs):
        Object.__init__(self, **kwargs)
        self._problem = None
        self._synthetics = None

    def set_problem(self, problem):
        self._problem = problem
        self._synthetics = None

    def get_problem(self):
        if self._problem is None:
            raise SyntheticWaveformNotAvailable(
                'SyntheticTest.set_problem() has not been called yet')

        return self._problem

    def get_x(self):
        problem = self.get_problem()
        if self.x:
            x = problem.preconstrain(
                problem.get_parameter_array(self.x))

        else:
            x = problem.preconstrain(
                problem.pack(
                    problem.base_source))

        return x

    def get_synthetics(self):
        problem = self.get_problem()
        if self._synthetics is None:
            x = self.get_x()
            results = problem.forward(x)
            synthetics = {}
            for iresult, result in enumerate(results):
                tr = result.trace.pyrocko_trace()
                tfade = tr.tmax - tr.tmin
                tr_orig = tr.copy()
                tr.extend(tr.tmin - tfade, tr.tmax + tfade)
                rstate = num.random.RandomState(
                    (self.random_seed or 0) + iresult)

                if self.random_response_scale != 0:
                    tf = RandomResponse(scale=self.random_response_scale)
                    tf.set_random_state(rstate)
                    tr = tr.transfer(
                        tfade=tfade,
                        transfer_function=tf)

                if self.white_noise_scale != 0.0:
                    u = rstate.normal(
                        scale=self.white_noise_scale,
                        size=tr.data_len())

                    tr.ydata += u

                if self.relative_white_noise_scale != 0.0:
                    u = rstate.normal(
                        scale=self.relative_white_noise_scale * num.std(
                            tr_orig.ydata),
                        size=tr.data_len())

                    tr.ydata += u

                synthetics[result.trace.codes] = tr

            self._synthetics = synthetics

        return self._synthetics

    def get_waveform(self, nslc, tmin, tmax, tfade=0., freqlimits=None):
        synthetics = self.get_synthetics()
        if nslc not in synthetics:
            s = 'no synthetics for %s available' % '.'.join(nslc)
            logger.warn(s)
            from grond.dataset import NotFound
            raise NotFound(s)

        tr = synthetics[nslc]
        tr.extend(tmin - tfade * 2.0, tmax + tfade * 2.0)

        tr = tr.transfer(
            tfade=tfade,
            freqlimits=freqlimits)

        tr.chop(tmin, tmax)
        return tr
Exemple #15
0
class Model(Object):
    '''Defines your neural network and the basic training strategy.'''

    name = String.T(
        default='unnamed',
        help='Used to identify the model and runs in summy and checkpoint \
            directory')

    config = PinkyConfig.T(help='')
    learning_rate = Float.T(default=1e-3)
    dropout_rate = Float.T(default=0.01)
    batch_size = Int.T(default=10)
    n_epochs = Int.T(default=1)
    max_steps = Int.T(default=5000)
    outdir = String.T(default=tempfile.mkdtemp(prefix='pinky-'))
    summary_outdir = String.T(default='summary')
    summary_nth_step = Int.T(default=1)

    shuffle_size = Int.T(optional=True,
                         help='if set, shuffle examples at given buffer size.')

    tf.logging.set_verbosity(tf.logging.INFO)
    force_dropout = Bool.T(optional=True)
    layers = List.T(Layer.T(), help='A list of `Layer` instances.')

    def __init__(self, tf_config=None, **kwargs):
        ''' '''
        super().__init__(**kwargs)

        if not tf_config:
            tf_config = tf.ConfigProto()
            tf_config.gpu_options.allow_growth = True

        self.sess = tf.Session(config=tf_config)
        self.debug = logger.getEffectiveLevel() == logging.DEBUG
        self.est = None
        self.prefix = None
        self.tinc_detect = 1.

    def set_tfconfig(self, tf_config):
        self.sess.close()
        self.sess = tf.Session(config=tf_config)

    def enable_debugger(self):
        '''wrap session to enable breaking into debugger.'''
        from tensorflow.python import debug as tf_debug

        # Attach debugger
        self.sess = tf_debug.LocalCLIDebugWrapperSession(self.sess)

        # Attach Tensorboard debugger
        # self.sess = tf_debug.TensorBoardDebugWrapperSession(
        #         self.sess, "localhost:8080")

    def extend_path(self, p):
        '''Append subdirectory to path `p` named by the model.'''
        if self.prefix:
            return os.path.join(self.prefix, p, self.name)
        return os.path.join(p, self.name)

    def get_summary_outdir(self):
        '''Return the directory where to store the summary.'''
        d = self.extend_path(self.summary_outdir)
        ensure_dir(d)
        return d

    def get_outdir(self):
        '''Return the directory where to store the model.'''
        return self.extend_path(self.outdir)

    def get_plot_path(self):
        '''Return the directory where to store figures.'''
        d = os.path.join(self.get_summary_outdir(), 'plots')
        ensure_dir(d)
        return d

    def clear(self):
        '''Delete summary and model directories.'''
        delete_if_exists(self.get_summary_outdir())
        self.clear_model()

    def denormalize_location(self, items):
        '''Convert normalized carthesian location to true location.'''
        return self.config.denormalize_label(items)

    def clear_model(self):
        '''model directories.'''
        delete_if_exists(self.get_outdir())

    def generate_eval_dataset(self):
        '''Generator of evaluation dataset.'''
        return self.config.evaluation_data_generator.get_dataset().batch(
            self.batch_size)

    def generate_eval_dataset_3(self):
        '''Generator of evaluation dataset.'''
        return self.config.evaluation_data_generator.get_dataset().batch(
            self.batch_size).repeat(3)

    def generate_predict_dataset(self):
        '''Generator of prediction dataset.'''
        d = self.config.prediction_data_generator
        if not d:
            raise Exception(
                '\nNo prediction data generator defined in config!')
        return d.get_dataset().batch(self.batch_size)

    def generate_dataset(self):
        '''Generator of training dataset.'''
        dataset = self.config.data_generator.get_dataset()
        if self.shuffle_size is not None:
            dataset = dataset.shuffle(buffer_size=self.shuffle_size)
        return dataset.repeat(count=self.n_epochs).batch(self.batch_size)

    def generate_detect_dataset(self):
        '''Generator of prediction dataset.'''
        d = self.config.prediction_data_generator
        if not d:
            raise Exception(
                '\nNo prediction data generator defined in config!')
        return d.get_chunked_dataset(tinc=self.tinc_detect).prefetch(100)

    def model(self, features, labels, mode):
        '''Setup the model, summaries and run training or evaluation.'''
        training = bool(mode == tf.estimator.ModeKeys.TRAIN)

        if self.debug:
            view = features[:3]
            view = tf.expand_dims(view, -1)
            tf.summary.image('input', view)

        with tf.name_scope('input'):
            input = tf.reshape(
                features, [-1, *self.config.data_generator.tensor_shape, 1])

        input = tf.layers.batch_normalization(input, training=training)
        level = tf.zeros([1], name='level')

        for layer in self.layers:
            logger.debug('chain in layer: %s' % layer)
            input, level = layer.chain(input=input,
                                       level=level,
                                       training=training or self.force_dropout,
                                       dropout_rate=self.dropout_rate)

        # Final layer
        predictions = tf.layers.dense(input,
                                      self.config.n_classes,
                                      name='output',
                                      activation=None)

        if mode == tf.estimator.ModeKeys.PREDICT:
            return tf.estimator.EstimatorSpec(
                mode,
                predictions={
                    'predictions': self.denormalize_location(predictions),
                    'level': level
                })

        # do not denormalize labels and predictions for loss
        loss = tf.losses.mean_squared_error(labels, predictions)

        # transform to carthesian coordiantes
        labels = self.denormalize_location(labels)
        predictions = self.denormalize_location(predictions)

        predictions = tf.transpose(predictions)
        labels = tf.transpose(labels)
        errors = predictions - labels
        abs_errors = tf.abs(errors)
        variable_summaries(errors[0], 'error_abs_x')
        variable_summaries(errors[1], 'error_abs_y')
        variable_summaries(errors[2], 'error_abs_z')

        loss_carthesian = tf.sqrt(
            tf.reduce_sum(errors**2, axis=0, keepdims=False))

        variable_summaries(loss_carthesian, 'training_loss')
        loss_ = tf.reduce_mean(loss_carthesian)
        tf.summary.scalar('mean_loss_cart', loss_)
        tf.summary.scalar('loss_normalized', loss)

        if mode == tf.estimator.ModeKeys.TRAIN:
            optimizer = tf.train.AdamOptimizer(self.learning_rate)
            update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
            with tf.control_dependencies(update_ops):
                train_op = optimizer.minimize(
                    loss=loss, global_step=tf.train.get_global_step())

            logging_hook = tf.train.LoggingTensorHook(
                {
                    "loss": loss,
                    "step": tf.train.get_global_step()
                },
                every_n_iter=10)

            return tf.estimator.EstimatorSpec(
                mode=mode,
                loss=loss,
                train_op=train_op,
                evaluation_hooks=[
                    self.get_summary_hook('eval'),
                ],
                training_hooks=[self.get_summary_hook('train'), logging_hook])

        elif mode == tf.estimator.ModeKeys.EVAL:
            metrics = {
                'rmse_eval':
                tf.metrics.root_mean_squared_error(labels=labels,
                                                   predictions=predictions,
                                                   name='rmse_eval'),
                'mse_eval':
                tf.metrics.mean_squared_error(labels=labels,
                                              predictions=predictions,
                                              name='mse_eval'),
                'mae_eval':
                tf.metrics.mean_absolute_error(labels=labels,
                                               predictions=predictions,
                                               name='mae_eval')
            }

            with tf.name_scope('eval'):
                for k, v in metrics.items():
                    tf.summary.scalar(k, v[1])
            return tf.estimator.EstimatorSpec(
                mode=mode,
                loss=loss,
                eval_metric_ops=metrics,
                evaluation_hooks=[
                    self.get_summary_hook('eval'),
                ])

    def get_summary_hook(self, subdir=''):
        '''Return a summary hook storing summaries at *subdir*.'''
        return tf.train.SummarySaverHook(
            self.summary_nth_step,
            output_dir=pjoin(self.get_summary_outdir(), subdir),
            scaffold=tf.train.Scaffold(summary_op=tf.summary.merge_all()))

    def train_and_evaluate(self):
        '''Execute training and evaluation and visualize kernels as well as
        activation maps at the end of all epochs.'''
        self.save_model_in_summary()
        with self.sess as default:
            self.est = tf.estimator.Estimator(model_fn=self.model,
                                              model_dir=self.get_outdir())

            train_spec = tf.estimator.TrainSpec(input_fn=self.generate_dataset,
                                                max_steps=self.max_steps)

            eval_spec = tf.estimator.EvalSpec(
                input_fn=self.generate_eval_dataset, steps=None)

            result = tf.estimator.train_and_evaluate(self.est, train_spec,
                                                     eval_spec)

        self.save_kernels()
        self.save_activation_maps()

        return result

    def predict(self):
        '''Predict locations prediction data generator and store results
        as a list of carthesian coordinates.'''
        import time
        import csv
        with self.sess as default:
            self.est = tf.estimator.Estimator(model_fn=self.model,
                                              model_dir=self.get_outdir())
            predictions = []
            for i, p in enumerate(
                    self.est.predict(input_fn=self.generate_predict_dataset,
                                     yield_single_examples=False)):

                if not i:
                    tstart = time.time()

                predictions.extend(p['predictions'])

        fn_out = 'predictions.csv'
        with open(fn_out, 'w') as f:
            w = csv.writer(f, delimiter=' ')
            for p in predictions:
                w.writerow(p)

        logger.info('This took %1.1f seconds ' % (time.time() - tstart))
        logger.info('Saved locations in %s' % fn_out)

    def detect(self, tinc=None, detector_threshold=1.8):
        '''Detect events

        Summarizes the energy of layers in your network that have their
        *is_detector* flag set to *True*.

        :param tinc: time increment to step through the dataset.
        :param detector_threshold: triggers a detection when summed energy
            exceeds this value.
        '''
        tpeaksearch = 5.
        self.tinc_detect = tinc or 1.0
        fn_detector_trace = 'detector.mseed'
        fn_detections = 'detections.pf'

        with self.sess as default:
            self.est = tf.estimator.Estimator(model_fn=self.model,
                                              model_dir=self.get_outdir())
            detector_level = []

            for ip, p in enumerate(
                    self.est.predict(input_fn=self.generate_detect_dataset,
                                     yield_single_examples=True)):
                detector_level.append(p['level'])

            tr = trace.Trace(
                tmin=self.config.prediction_data_generator.tstart_data,
                ydata=num.array(detector_level),
                deltat=self.tinc_detect)

            tpeaks, apeaks = tr.peaks(detector_threshold, tpeaksearch)
            logger.info('Fount %i detections' % len(tpeaks))

            markers = []
            for (tpeak, apeak) in zip(tpeaks, apeaks):
                markers.append(
                    EventMarker(pmodel.Event(time=tpeak, name=str(apeak))))

            logger.info('Saving detections: %s' % fn_detections)
            Marker.save_markers(markers, fn_detections)

            logger.info('Saving detector level: %s' % fn_detector_trace)
            io.save([tr], fn_detector_trace)

    def evaluate_errors(self, n_predict=100, force_dropout=0.333):
        '''Repeatedly run the prediction for *n_predict* times where the first
        run is without and the subsequent are with enforced dropout rate
        *force_dropout*.

        This is allows to evaluate location accuracy without a given
        reference catatog of known event locations. It is basically a jacknife
        applied to the neural network layers.
        '''
        logger.debug('evaluation...')
        self.dropout_rate = force_dropout
        with self.sess as default:
            self.est = tf.estimator.Estimator(model_fn=self.model,
                                              model_dir=self.get_outdir())
            labels = [
                l for _, l in self.config.evaluation_data_generator.generate()
            ]
            labels = self.denormalize_location(num.array(labels))

            all_predictions = []

            for n in range(n_predict):
                predictions = []
                for p in self.est.predict(input_fn=self.generate_eval_dataset,
                                          yield_single_examples=False):

                    predictions.append(p['predictions'])
                # predictions = self.denormalize_location(num.array(predictions))
                all_predictions.append(predictions)

                # activate dropout after first iteration. hence, first are
                # 'correct locations'
                self.force_dropout = True

            all_predictions = num.array(all_predictions)

            save_name = pjoin(self.get_plot_path(), 'errors')

            plot.evaluate_errors(all_predictions, labels, name=save_name)

    def evaluate(self, annotate=False):
        ''' Run the evaluation and visualize the results.

        :param annotate: label all events
        '''
        logger.debug('evaluation...')

        with self.sess as default:
            self.est = tf.estimator.Estimator(model_fn=self.model,
                                              model_dir=self.get_outdir())
            labels = [
                l for _, l in self.config.evaluation_data_generator.generate()
            ]
            predictions = []
            for p in self.est.predict(input_fn=self.generate_eval_dataset,
                                      yield_single_examples=False):

                predictions.extend(p['predictions'])

        save_name = pjoin(self.get_plot_path(), 'mislocation')
        predictions = num.array(predictions)
        labels = self.denormalize_location(num.array(labels))

        text_labels = None
        if annotate:
            text_labels = self.config.evaluation_data_generator.text_labels

        plot.plot_predictions_and_labels(predictions,
                                         labels,
                                         name=save_name,
                                         text_labels=text_labels)

        save_name = pjoin(self.get_plot_path(), 'mislocation_hists')
        plot.mislocation_hist(predictions, labels, name=save_name)

        save_name = pjoin(self.get_plot_path(), 'mislocation_snrs')
        snrs = self.config.evaluation_data_generator.snrs(split_factor=0.8)
        plot.mislocation_vs_snr(snrs, predictions, labels, name=save_name)

        save_name = pjoin(self.get_plot_path(), 'mislocation_vs_gaps')
        gaps = self.config.evaluation_data_generator.gaps()

        plot.mislocation_vs_gaps(predictions, labels, gaps, name=save_name)

    def evaluate_station_dropout(self, start=0., stop=0.8, inc=0.1):
        '''
        Predict for a range of station dropouts and visualize the results.
        '''
        with self.sess as default:
            self.est = tf.estimator.Estimator(model_fn=self.model,
                                              model_dir=self.get_outdir())
            labels = [
                l for _, l in self.config.evaluation_data_generator.generate()
            ]
            sdropouts = num.linspace(start, stop, inc)
            results = {}
            for sdropout in sdropouts:
                predictions = []
                for p in self.est.predict(input_fn=self.generate_eval_dataset,
                                          yield_single_examples=False):

                    predictions.extend(p['predictions'])
                results[sdropout] = predictions

        plot.mislocation_vs_gaps_many(
            results,
            labels,
            self.config.evaluation_data_generator.gaps(),
            name=save_name)

    def train_multi_gpu(self, params=None):
        ''' Use multiple GPUs for training.  Buggy...'''
        params = params or {}
        self.training_hooks = []
        # saver_hook = tf.train.CheckpointSaverHook()
        # summary_hook = tf.train.SummarySaverHook()
        with tf.train.MonitoredSession(
                # session_creator=tf.train.ChiefSessionCreator(),
                # hooks=[summary_hook]) as sess:
                # hooks=[saver_hook, summary_hook]) as sess:
        ) as sess:
            distribution = tf.contrib.distribute.MirroredStrategy(num_gpus=2)
            run_config = tf.estimator.RunConfig(train_distribute=distribution)
            est = tf.estimator.Estimator(
                model_fn=self.model,
                model_dir=self.outdir,
                # params=params,
                config=run_config)

            est.train(input_fn=self.generate_dataset)
            logger.info('====== start evaluation')
            return est.evaluate(input_fn=self.generate_eval_dataset)

    def save_kernels(self, index=0):
        '''save weight kernels of all layers (at index=`index`).'''
        save_path = pjoin(self.get_summary_outdir(), 'kernels')
        ensure_dir(save_path)
        logger.info('Storing weight matrices at %s' % save_path)
        for layer in self.layers:
            layer.visualize_kernel(self.est, save_path=save_path)

    def save_activation_maps(self, index=0):
        '''Visualizes all activation maps at given *index* of all layers.'''
        save_path = pjoin(self.get_summary_outdir(), 'kernels')
        ensure_dir(save_path)
        for layer in self.layers:
            plot.getActivations(layer, stimuli)

    def save_model_in_summary(self):
        '''Dump neural network configuration to summary directory as YAML.'''
        self.regularize()
        self.dump(filename=pjoin(self.get_summary_outdir(), 'model.config'))
Exemple #16
0
class GNSSTargetMisfitPlot(PlotConfig):
    ''' Maps showing horizontal surface displacements
        of a GNSS campaign and model '''

    name = 'gnss'

    size_cm = Tuple.T(2,
                      Float.T(),
                      default=(30., 30.),
                      help='width and length of the figure in cm')
    show_topo = Bool.T(default=False, help='show topography')
    show_grid = Bool.T(default=True, help='show the lat/lon grid')
    show_rivers = Bool.T(default=True, help='show rivers on the map')
    radius = Float.T(optional=True,
                     help='radius of the map around campaign center lat/lon')

    def make(self, environ):
        cm = environ.get_plot_collection_manager()
        history = environ.get_history(subset='harvest')
        optimiser = environ.get_optimiser()
        ds = environ.get_dataset()

        environ.setup_modelling()

        cm.create_group_automap(self,
                                self.draw_gnss_fits(ds, history, optimiser),
                                title=u'GNSS Displacements',
                                section='fits',
                                feather_icon='map',
                                description=u'''
Maps showing station positions and statiom names of the GNSS targets.

Arrows the observed surface displacements (black arrows) and synthetic
displacements (red arrows). The top plot shows the horizontal displacements and
the bottom plot the vertical displacements. The grey filled box shows the
surface projection of the modelled source, with the thick-lined edge marking
the upper fault edge.
''')

    def draw_gnss_fits(self, ds, history, optimiser, vertical=False):
        problem = history.problem

        gnss_targets = problem.gnss_targets
        for target in gnss_targets:
            target.set_dataset(ds)

        xbest = history.get_best_model()
        source = history.get_best_source()

        results = problem.evaluate(xbest,
                                   result_mode='full',
                                   targets=gnss_targets)

        def plot_gnss(gnss_target, result, ifig, vertical=False):
            campaign = gnss_target.campaign
            item = PlotItem(
                name='fig_%i' % ifig,
                attributes={'targets': gnss_target.path},
                title=u'Static GNSS Surface Displacements - Campaign %s' %
                campaign.name,
                description=u'''
Static surface displacement from GNSS campaign %s (black vectors) and
displacements derived from best model (red).
''' % campaign.name)

            event = source.pyrocko_event()
            locations = campaign.stations + [event]

            lat, lon = od.geographic_midpoint_locations(locations)

            if self.radius is None:
                coords = num.array([loc.effective_latlon for loc in locations])
                radius = od.distance_accurate50m_numpy(lat[num.newaxis],
                                                       lon[num.newaxis],
                                                       coords[:, 0].max(),
                                                       coords[:, 1]).max()
                radius *= 1.1

            if radius < 30. * km:
                logger.warn('Radius of GNSS campaign %s too small, defaulting'
                            ' to 30 km' % campaign.name)
                radius = 30 * km

            model_camp = gnss.GNSSCampaign(stations=copy.deepcopy(
                campaign.stations),
                                           name='grond model')
            for ista, sta in enumerate(model_camp.stations):
                sta.north.shift = result.statics_syn['displacement.n'][ista]
                sta.north.sigma = 0.

                sta.east.shift = result.statics_syn['displacement.e'][ista]
                sta.east.sigma = 0.

                if sta.up:
                    sta.up.shift = -result.statics_syn['displacement.d'][ista]
                    sta.up.sigma = 0.

            m = automap.Map(width=self.size_cm[0],
                            height=self.size_cm[1],
                            lat=lat,
                            lon=lon,
                            radius=radius,
                            show_topo=self.show_topo,
                            show_grid=self.show_grid,
                            show_rivers=self.show_rivers,
                            color_wet=(216, 242, 254),
                            color_dry=(238, 236, 230))

            all_stations = campaign.stations + model_camp.stations
            offset_scale = num.zeros(len(all_stations))

            for ista, sta in enumerate(all_stations):
                for comp in sta.components.values():
                    offset_scale[ista] += comp.shift
            offset_scale = num.sqrt(offset_scale**2).max()

            m.add_gnss_campaign(campaign,
                                psxy_style={
                                    'G': 'black',
                                    'W': '0.8p,black',
                                },
                                offset_scale=offset_scale,
                                vertical=vertical)

            m.add_gnss_campaign(model_camp,
                                psxy_style={
                                    'G': 'red',
                                    'W': '0.8p,red',
                                    't': 30,
                                },
                                offset_scale=offset_scale,
                                vertical=vertical,
                                labels=False)

            if isinstance(problem, CMTProblem):
                from pyrocko import moment_tensor
                from pyrocko.plot import gmtpy

                mt = event.moment_tensor.m_up_south_east()
                ev_lat, ev_lon = event.effective_latlon

                xx = num.trace(mt) / 3.
                mc = num.matrix([[xx, 0., 0.], [0., xx, 0.], [0., 0., xx]])
                mc = mt - mc
                mc = mc / event.moment_tensor.scalar_moment() * \
                    moment_tensor.magnitude_to_moment(5.0)
                m6 = tuple(moment_tensor.to6(mc))
                symbol_size = 20.
                m.gmt.psmeca(S='%s%g' % ('d', symbol_size / gmtpy.cm),
                             in_rows=[(ev_lon, ev_lat, 10) + m6 + (1, 0, 0)],
                             M=True,
                             *m.jxyr)

            elif isinstance(problem, RectangularProblem):
                m.gmt.psxy(in_rows=source.outline(cs='lonlat'),
                           L='+p2p,black',
                           W='1p,black',
                           G='black',
                           t=60,
                           *m.jxyr)

            elif isinstance(problem, VLVDProblem):
                ev_lat, ev_lon = event.effective_latlon
                dV = abs(source.volume_change)
                sphere_radius = num.cbrt(dV / (4. / 3. * num.pi))

                volcanic_circle = [ev_lon, ev_lat, '%fe' % sphere_radius]
                m.gmt.psxy(S='E-',
                           in_rows=[volcanic_circle],
                           W='1p,black',
                           G='orange3',
                           *m.jxyr)

            return (item, m)

        ifig = 0
        for vertical in (False, True):
            for gnss_target, result in zip(problem.gnss_targets, results):
                yield plot_gnss(gnss_target, result, ifig, vertical)
                ifig += 1
Exemple #17
0
class CNNLayer(Layer):
    '''2D CNN layer'''
    kernel_width = Int.T()
    kernel_height = Int.T(
        optional=True,
        help='If this parameter is not set use *N* channels as height.')

    pool_width = Int.T(optional=True,
                       help='If not set pool_height=kernel_height')

    pool_height = Int.T(optional=True,
                        help='If not set pool_height=kernel_height')

    dilation_rate = Int.T(default=0)

    strides = Tuple.T(2, Int.T(), default=(1, 1))

    is_detector = Bool.T(
        default=False,
        help='If *True* this layer\'s activations contributes to detector.')

    def __init__(self, *args, **kwargs):
        Layer.__init__(self, *args, **kwargs)

        if not self.pool_width:
            logger.info(
                'Pool width is undefined. Setting equal to kernel width')
            self.pool_width = self.kernel_width

        if not self.pool_height:
            logger.info(
                'Pool heigth is undefined. Setting equal to kernel height')
            self.pool_height = self.kernel_height

    def chain(self, input, level, training=False, dropout_rate=0.):
        '''
        :param level: detection level
        '''
        _, n_channels, n_samples, _ = input.shape

        logger.debug('input shape %s' % input.shape)
        kernel_height = self.kernel_height or n_channels

        kwargs = {}
        if self.dilation_rate:
            kwargs.update({'dilation_rate': self.dilation_rate})

        inpower = tf.sqrt(tf.reduce_sum(tf.square(input)))

        input = tf.layers.conv2d(inputs=input,
                                 filters=self.n_filters,
                                 kernel_size=(kernel_height,
                                              self.kernel_width),
                                 activation=self.get_activation(),
                                 strides=self.strides,
                                 name=self.name,
                                 **kwargs)

        input = tf.layers.max_pooling2d(input,
                                        pool_size=(self.pool_width,
                                                   self.pool_height),
                                        strides=(1, 1),
                                        name=self.name + 'maxpool')

        if logger.getEffectiveLevel() == logging.DEBUG:
            tf.summary.image(
                'post-%s' % self.name,
                tf.split(input, num_or_size_splits=self.n_filters, axis=-1)[0])

        if self.is_detector:
            batch_mean = tf.reduce_mean(input,
                                        reduction_indices=(1, 2, 3),
                                        keepdims=True)

            stdev = tf.sqrt(
                tf.reduce_mean(tf.square(input - batch_mean),
                               reduction_indices=(1, 2, 3)))

            level += (tf.sqrt(tf.reduce_sum(tf.square(input))) / inpower)

        input = tf.layers.dropout(input, rate=dropout_rate, training=training)

        return tf.layers.batch_normalization(input, training=training), level

    def visualize_kernel(self, estimator, index=0, save_path=None, **kwargs):
        save_name = pjoin(save_path, 'kernel-%s' % self.name)
        weights = estimator.get_variable_value('%s/kernel' % self.name)
        plot.show_kernels(weights[:, :, index, ...], name=save_name)
class PlotSettings(Object):
    trace_filename = String.T(help='filename of beam or trace to use for '
                              'plotting, incl. path.',
                              optional=True)
    station_filename = String.T(help='filename containing station meta '
                                'information related to *trace_filename*.',
                                optional=True)
    event_filename = String.T(help='filename containing event information '
                              'including the expected moment tensor.',
                              default='event.pf')
    store_id = String.T(help='Store ID to use for generating the synthetic '
                        'traces.',
                        optional=True)
    store_superdirs = List.T(String.T(), optional=True)
    depth = Float.T(help='Depth [km] where to put the trace.', default=10.)
    depths = String.T(help='Synthetic source depths [km]. start:stop:delta. '
                      'default: 0:15:1',
                      optional=True,
                      default='0:15:1')
    filters = List.T(
        FrequencyResponse.T(help='List of filters used to filter '
                            'the traces'))
    zoom = List.T(Float.T(),
                  help='Window to visualize with reference to the P '
                  'phase onset [s].',
                  default=[-7, 15])
    correction = Float.T(help='time shift, to move beam trace.', default=0.)
    normalize = Bool.T(help='normalize by maximum amplitude', default=True)
    save_as = String.T(default='depth_%(array-id)s.png',
                       help='filename of output figure')
    force_nearest_neighbor = Bool.T(help='Handles OutOfBounds exceptions. '
                                    'applies only laterally!',
                                    default=False)
    auto_caption = Bool.T(
        help='Add a caption giving basic information to the figure.',
        default=False)
    title = String.T(default='%(array-id)s - %(event_name)s',
                     help='Add default title.')
    quantity = String.T(default='velocity',
                        help='velocity-> differentiate synthetic.'
                        'displacement-> integrate recorded')
    gain = Float.T(default=1., help='Gain factor')
    gain_record = Float.T(default=1., help='Gain factor')
    color = String.T(help='Trace color', default='blue')

    def update_from_args(self, args):
        kwargs = {}
        try:
            hp, lp = args.filter.split(':')
            filters = [
                ButterworthResponse(corner=float(lp), order=4, type='low'),
                ButterworthResponse(corner=float(hp), order=4, type='high')
            ]
            kwargs.update({'filters': filters})
        except:
            pass

        kwargs.update(self.process_arglist(args))
        for arg, v in kwargs.items():
            setattr(self, arg, v)

    @classmethod
    def from_argument_parser(cls, args):
        try:
            hp, lp = args.filter.split(':')
        except AttributeError:
            hp, lp = (0.7, 4.5)
        filters = [
            ButterworthResponse(corner=float(lp), order=4, type='low'),
            ButterworthResponse(corner=float(hp), order=4, type='high')
        ]
        kwargs = cls.process_arglist(args)
        return cls(filters=filters, **kwargs)

    def do_filter(self, tr):
        for f in self.filters:
            tr = tr.transfer(transfer_function=f,
                             tfade=10,
                             cut_off_fading=False)
        return tr

    @staticmethod
    def process_arglist(args):
        kwargs = {}
        for arg in arglist:
            try:
                val = getattr(args, arg)
                if arg == 'zoom' and val:
                    val = val.split(':')
                    val = map(float, val)
                if val:
                    kwargs.update({arg: val})
            except AttributeError:
                logger.debug('%s not defined' % arg)
                continue

        return kwargs
Exemple #19
0
class JointparPlot(PlotConfig):
    '''
    Source problem parameter's tradeoff plots.
    '''

    name = 'jointpar'
    size_cm = Tuple.T(2, Float.T(), default=(20., 20.))
    misfit_cutoff = Float.T(optional=True)
    ibootstrap = Int.T(optional=True)
    color_parameter = String.T(default='misfit')
    exclude = List.T(String.T())
    include = List.T(String.T())
    show_ellipses = Bool.T(default=False)
    nsubplots = Int.T(default=6)
    show_reference = Bool.T(default=True)

    def make(self, environ):
        cm = environ.get_plot_collection_manager()
        history = environ.get_history(subset='harvest')
        optimiser = environ.get_optimiser()
        sref = 'Dark gray boxes mark reference solution.' \
            if self.show_reference else ''

        mpl_init(fontsize=self.font_size)
        cm.create_group_mpl(self,
                            self.draw_figures(history, optimiser),
                            title=u'Jointpar Plot',
                            section='solution',
                            feather_icon='crosshair',
                            description=u'''
Source problem parameter's scatter plots, to evaluate the resolution of source
parameters and possible trade-offs between pairs of model parameters.

A subset of model solutions (from harvest) is shown in two dimensions for all
possible parameter pairs as points. The point color indicates the misfit for
the model solution with cold colors (blue) for high misfit models and warm
colors (red) for low misfit models. The plot ranges are defined by the given
parameter bounds and shows the model space of the optimsation. %s''' % sref)

    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 = 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(expand=True)

        if ibootstrap is not None:
            gms = history.bootstrap_misfits[:, ibootstrap]
        else:
            gms = problem.combine_misfits(history.misfits)

        isort = num.argsort(gms)[::-1]

        gms = gms[isort]
        models = models[isort, :]

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

        nmodels = models.shape[0]
        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)

                axes.get_xaxis().set_ticks([])
                axes.get_yaxis().set_ticks([])

                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
Exemple #20
0
class PalantiriWeightConfig(Object):
    '''Configuration of data IO and data preprocessing'''

    shift_max = Float.T(default=4.,
                        optional=True,
                        help='Shift in s to consider for corrections')

    weight_by_azimuth = Bool.T(default=True,
                               optional=True,
                               help='Weight in 12 azimuthal blocks.')

    bootstrap_array_weights = Bool.T(
        default=True,
        optional=True,
        help='If true bootstraps the array weights.')

    n_bootstrap = Int.T(default=1, optional=True, help='Number of bootstraps.')

    correct_shifts_empirical_run = Bool.T(default=False,
                                          optional=True,
                                          help='')

    correct_shifts = Bool.T(
        default=False,
        optional=True,
        help=
        'If true shifts waveforms to expected traveltimes using the center and velocity model '
    )

    correct_shifts_empirical = Bool.T(default=False, optional=True, help='')

    correct_shifts_empirical_manual = Bool.T(default=False,
                                             optional=True,
                                             help='')

    correct_shifts_empirical_manual_station_wise = Bool.T(default=False,
                                                          optional=True,
                                                          help='')

    combine_all = Bool.T(
        default=True,
        optional=True,
        help='if True combines the semblance of all arrays by multiplication.')

    norm_all = Bool.T(
        default=True,
        optional=True,
        help='if True all semblances are normalized between 0 and 1.')

    weight_by_noise = Bool.T(
        default=False,
        optional=True,
        help='If true the array weight is determined by preevent noise')

    shift_by_phase_onset = Bool.T(
        default=False,
        optional=True,
        help=
        'If true the waveforms will be shifted to expected arrivals. Use only for tests.'
    )

    shift_by_phase_pws = Bool.T(
        default=False,
        optional=True,
        help='If true use the phase weighted stacking.')

    shift_by_phase_cc = Bool.T(default=False,
                               optional=True,
                               help='If true use corelation based stacking.')
Exemple #21
0
class HudsonPlot(PlotConfig):
    '''
    Illustration of the solution distribution of decomposed moment tensor.
    '''

    name = 'hudson'
    size_cm = Tuple.T(2, Float.T(), default=(17.5, 17.5 * (3. / 4.)))
    beachball_type = StringChoice.T(choices=['full', 'deviatoric', 'dc'],
                                    default='dc')
    show_reference = Bool.T(default=True)

    def make(self, environ):
        cm = environ.get_plot_collection_manager()
        history = environ.get_history(subset='harvest')
        mpl_init(fontsize=self.font_size)
        cm.create_group_mpl(self,
                            self.draw_figures(history),
                            title=u'Hudson Plot',
                            section='solution',
                            feather_icon='box',
                            description=u'''
Hudson's source type plot with the ensemble of bootstrap solutions.

For about 10% of the solutions (randomly chosen), the focal mechanism is
depicted, others are represented as dots. The square marks the global best
fitting solution.
''')

    def draw_figures(self, history):

        color = 'black'
        fontsize = self.font_size
        markersize = fontsize * 1.5
        markersize_small = markersize * 0.2
        beachballsize = markersize
        beachballsize_small = beachballsize * 0.5
        beachball_type = self.beachball_type

        problem = history.problem
        mean_source = stats.get_mean_source(problem, history.models)
        best_source = stats.get_best_source(problem, history.models,
                                            history.misfits)

        fig = plt.figure(figsize=self.size_inch)
        axes = fig.add_subplot(1, 1, 1)

        data = []
        for ix, x in enumerate(history.models):
            source = problem.get_source(x)
            mt = source.pyrocko_moment_tensor()
            u, v = hudson.project(mt)

            if random.random() < 0.1:
                try:
                    beachball.plot_beachball_mpl(mt,
                                                 axes,
                                                 beachball_type=beachball_type,
                                                 position=(u, v),
                                                 size=beachballsize_small,
                                                 color_t=color,
                                                 alpha=0.5,
                                                 zorder=1,
                                                 linewidth=0.25)
                except beachball.BeachballError as e:
                    logger.warn(str(e))

            else:
                data.append((u, v))

        if data:
            u, v = num.array(data).T
            axes.plot(u,
                      v,
                      'o',
                      color=color,
                      ms=markersize_small,
                      mec='none',
                      mew=0,
                      alpha=0.25,
                      zorder=0)

        hudson.draw_axes(axes)

        mt = mean_source.pyrocko_moment_tensor()
        u, v = hudson.project(mt)

        try:
            beachball.plot_beachball_mpl(mt,
                                         axes,
                                         beachball_type=beachball_type,
                                         position=(u, v),
                                         size=beachballsize,
                                         color_t=color,
                                         zorder=2,
                                         linewidth=0.5)
        except beachball.BeachballError as e:
            logger.warn(str(e))

        mt = best_source.pyrocko_moment_tensor()
        u, v = hudson.project(mt)

        axes.plot(u,
                  v,
                  's',
                  markersize=markersize,
                  mew=1,
                  mec='black',
                  mfc='none',
                  zorder=-2)

        if self.show_reference:
            mt = problem.base_source.pyrocko_moment_tensor()
            u, v = hudson.project(mt)

            try:
                beachball.plot_beachball_mpl(mt,
                                             axes,
                                             beachball_type=beachball_type,
                                             position=(u, v),
                                             size=beachballsize,
                                             color_t='red',
                                             zorder=2,
                                             linewidth=0.5)
            except beachball.BeachballError as e:
                logger.warn(str(e))

        item = PlotItem(name='main')
        return [[item, fig]]
Exemple #22
0
class PolygonMaskConfig(PluginConfig):
    polygons = Dict.T(optional=True, default={})
    applied = Bool.T(default=True)
Exemple #23
0
class PhaseRatioTarget(gf.Location, MisfitTarget):
    '''
    Target to compare ratios or log ratios of two seismic phases.

      misfit = | a_obs / (a_obs + b_obs)  - a_syn / (a_syn + b_syn) |

    '''

    codes = Tuple.T(3, String.T(), help='network, station, location codes.')

    store_id = gf.StringID.T(
        help='ID of Green\'s function store to use for the computation.')

    backazimuth = Float.T(optional=True)

    interpolation = gf.InterpolationMethod.T()

    measure_a = fm.FeatureMeasure.T()
    measure_b = fm.FeatureMeasure.T()

    fit_log_ratio = Bool.T(
        default=True,
        help='if true, compare synthetic and observed log ratios')

    fit_log_ratio_waterlevel = Float.T(
        default=0.01,
        help='waterlevel added to both ratios when comparing on logarithmic '
        'scale, to avoid log(0)')

    can_bootstrap_weights = True

    def __init__(self, **kwargs):
        gf.Location.__init__(self, **kwargs)
        MisfitTarget.__init__(self, **kwargs)

    @classmethod
    def get_plot_classes(cls):
        from . import plot
        plots = super(PhaseRatioTarget, cls).get_plot_classes()
        plots.extend(plot.get_plot_classes())
        return plots

    def string_id(self):
        return '.'.join(x for x in (self.path, ) + self.codes)

    def get_plain_targets(self, engine, source):
        return self.prepare_modelling(engine, source, None)

    def prepare_modelling(self, engine, source, targets):
        modelling_targets = []
        for measure in [self.measure_a, self.measure_b]:
            modelling_targets.extend(
                measure.get_modelling_targets(self.codes, self.lat, self.lon,
                                              self.depth, self.store_id,
                                              self.backazimuth))

        return modelling_targets

    def finalize_modelling(self, engine, source, modelling_targets,
                           modelling_results):

        ds = self.get_dataset()

        try:
            imt = 0
            amps = []
            for measure in [self.measure_a, self.measure_b]:
                nmt_this = measure.get_nmodelling_targets()
                amp_obs, _ = measure.evaluate(engine,
                                              source,
                                              modelling_targets[imt:imt +
                                                                nmt_this],
                                              dataset=ds)

                amp_syn, _ = measure.evaluate(
                    engine,
                    source,
                    modelling_targets[imt:imt + nmt_this],
                    trs=[
                        r.trace.pyrocko_trace()
                        for r in modelling_results[imt:imt + nmt_this]
                    ])

                amps.append((amp_obs, amp_syn))

                imt += nmt_this

            (a_obs, a_syn), (b_obs, b_syn) = amps

            eps = self.fit_log_ratio_waterlevel
            if self.fit_log_ratio:
                res_a = num.log(a_obs / (a_obs + b_obs) + eps) \
                    - num.log(a_syn / (a_syn + b_syn) + eps)
            else:
                res_a = a_obs / (a_obs + b_obs) - a_syn / (a_syn + b_syn)

            misfit = num.abs(res_a)
            norm = 1.0

            result = PhaseRatioResult(misfits=num.array([[misfit, norm]],
                                                        dtype=num.float),
                                      a_obs=a_obs,
                                      b_obs=b_obs,
                                      a_syn=a_syn,
                                      b_syn=b_syn)

            return result

        except dataset.NotFound as e:
            logger.debug(str(e))
            return gf.SeismosizerError('no waveform data, %s' % str(e))
Exemple #24
0
class DatasetConfig(HasPaths):
    ''' Configuration for a Grond `Dataset`  object. '''

    stations_path = Path.T(
        optional=True,
        help='List of files with station coordinates in Pyrocko format.')
    stations_stationxml_paths = List.T(
        Path.T(),
        optional=True,
        help='List of files with station coordinates in StationXML format.')
    events_path = Path.T(
        optional=True,
        help='File with hypocenter information and possibly'
             ' reference solution')
    waveform_paths = List.T(
        Path.T(),
        optional=True,
        help='List of directories with raw waveform data')
    clippings_path = Path.T(
        optional=True)
    responses_sacpz_path = Path.T(
        optional=True,
        help='List of SACPZ response files for restitution of'
             ' the raw waveform data.')
    responses_stationxml_paths = List.T(
        Path.T(),
        optional=True,
        help='List of StationXML response files for restitution of'
             ' the raw waveform data.')
    station_corrections_path = Path.T(
        optional=True,
        help='File containing station correction informations.')
    apply_correction_factors = Bool.T(
        optional=True,
        default=True,
        help='Apply correction factors from station corrections.')
    apply_correction_delays = Bool.T(
        optional=True,
        default=True,
        help='Apply correction delays from station corrections.')
    apply_displaced_sampling_workaround = Bool.T(
        optional=True,
        default=False,
        help='Work around displaced sampling issues.')
    extend_incomplete = Bool.T(
        default=False,
        help='Extend incomplete seismic traces.')
    picks_paths = List.T(
        Path.T())
    blacklist_paths = List.T(
        Path.T(),
        help='List of text files with blacklisted stations.')
    blacklist = List.T(
        String.T(),
        help='Stations/components to be excluded according to their STA, '
             'NET.STA, NET.STA.LOC, or NET.STA.LOC.CHA codes.')
    whitelist_paths = List.T(
        Path.T(),
        help='List of text files with whitelisted stations.')
    whitelist = List.T(
        String.T(),
        optional=True,
        help='If not None, list of stations/components to include according '
             'to their STA, NET.STA, NET.STA.LOC, or NET.STA.LOC.CHA codes. '
             'Note: ''when whitelisting on channel level, both, the raw and '
             'the processed channel codes have to be listed.')
    synthetic_test = SyntheticTest.T(
        optional=True)

    kite_scene_paths = List.T(
        Path.T(),
        optional=True,
        help='List of directories for the InSAR scenes.')

    gnss_campaign_paths = List.T(
        Path.T(),
        optional=True,
        help='List of directories for the GNSS campaign data.')

    def __init__(self, *args, **kwargs):
        HasPaths.__init__(self, *args, **kwargs)
        self._ds = {}

    def get_event_names(self):
        logger.info('Loading events ...')

        def extra(path):
            return expand_template(path, dict(
                event_name='*'))

        def fp(path):
            return self.expand_path(path, extra=extra)

        def check_events(events, fn):
            for ev in events:
                if not ev.name:
                    logger.warning('Event in %s has no name!', fn)
                    return
                if not ev.lat or not ev.lon:
                    logger.warning('Event %s has inconsistent coordinates!',
                                   ev.name)
                if not ev.depth:
                    logger.warning('Event %s has no depth!', ev.name)
                if not ev.time:
                    logger.warning('Event %s has no time!', ev.name)

        events = []
        events_path = fp(self.events_path)
        fns = glob.glob(events_path)
        if not fns:
            raise DatasetError('No event files matching "%s".' % events_path)

        for fn in fns:
            logger.debug('Loading from file %s' % fn)
            ev = model.load_events(filename=fn)
            check_events(ev, fn)

            events.extend(ev)

        event_names = [ev_.name for ev_ in events]
        event_names.sort()
        return event_names

    def get_dataset(self, event_name):
        if event_name not in self._ds:
            def extra(path):
                return expand_template(path, dict(
                    event_name=event_name))

            def fp(path):
                p = self.expand_path(path, extra=extra)
                if p is None:
                    return None

                if isinstance(p, list):
                    for path in p:
                        if not op.exists(path):
                            logger.warn('Path %s does not exist.' % path)
                else:
                    if not op.exists(p):
                        logger.warn('Path %s does not exist.' % p)

                return p

            ds = Dataset(event_name)
            try:
                ds.add_events(filename=fp(self.events_path))

                ds.add_stations(
                    pyrocko_stations_filename=fp(self.stations_path),
                    stationxml_filenames=fp(self.stations_stationxml_paths))

                if self.waveform_paths:
                    ds.add_waveforms(paths=fp(self.waveform_paths))

                if self.kite_scene_paths:
                    ds.add_kite_scenes(paths=fp(self.kite_scene_paths))

                if self.gnss_campaign_paths:
                    ds.add_gnss_campaigns(paths=fp(self.gnss_campaign_paths))

                if self.clippings_path:
                    ds.add_clippings(markers_filename=fp(self.clippings_path))

                if self.responses_sacpz_path:
                    ds.add_responses(
                        sacpz_dirname=fp(self.responses_sacpz_path))

                if self.responses_stationxml_paths:
                    ds.add_responses(
                        stationxml_filenames=fp(
                            self.responses_stationxml_paths))

                if self.station_corrections_path:
                    ds.add_station_corrections(
                        filename=fp(self.station_corrections_path))

                ds.apply_correction_factors = self.apply_correction_factors
                ds.apply_correction_delays = self.apply_correction_delays
                ds.apply_displaced_sampling_workaround = \
                    self.apply_displaced_sampling_workaround
                ds.extend_incomplete = self.extend_incomplete

                for picks_path in self.picks_paths:
                    ds.add_picks(
                        filename=fp(picks_path))

                ds.add_blacklist(self.blacklist)
                ds.add_blacklist(filenames=fp(self.blacklist_paths))
                if self.whitelist:
                    ds.add_whitelist(self.whitelist)
                if self.whitelist_paths:
                    ds.add_whitelist(filenames=fp(self.whitelist_paths))

                ds.set_synthetic_test(copy.deepcopy(self.synthetic_test))
                self._ds[event_name] = ds
            except (FileLoadError, OSError) as e:
                raise DatasetError(str(e))

        return self._ds[event_name]
Exemple #25
0
class SatelliteTargetDisplacement(PlotConfig):
    ''' Maps showing surface displacements from satellite and modelled data '''

    name = 'satellite'
    dpi = Int.T(
        default=250)
    size_cm = Tuple.T(
        2, Float.T(),
        default=(22., 10.))
    colormap = String.T(
        default='RdBu',
        help='Colormap for the surface displacements')
    relative_coordinates = Bool.T(
        default=False,
        help='Show relative coordinates, initial location centered at 0N, 0E')

    def make(self, environ):
        cm = environ.get_plot_collection_manager()
        history = environ.get_history()
        optimiser = environ.get_optimiser()
        ds = environ.get_dataset()

        environ.setup_modelling()

        cm.create_group_mpl(
            self,
            self.draw_static_fits(ds, history, optimiser),
            title=u'Satellite Surface Displacements',
            section='fits',
            feather_icon='navigation',
            description=u' Maps showing subsampled surface displacements as '   
                        u' observed, modelled and the residual (observed minus'  
                        u' modelled).\n The displacement values predicted by' 
                        u' the orbit-ambiguity ramps are added to the modelled' 
                        u' displacements (middle panels). The color shows the' 
                        u' LOS displacement values associated with, and the' 
                        u' extent of, every quadtree box. The light grey dots' 
                        u' show the focal point of pixels combined in the' 
                        u' quadtree box. This point corresponds to the' 
                        u' position of the modelled data point.\n The large dark' 
                        u' grey dot shows the reference source position. The' 
                        u' grey filled box shows the surface projection of the' 
                        u' modelled source, with the thick-lined edge marking' 
                        u' the upper fault edge. '
                        u' Complete data extent is shown.')

    def draw_static_fits(self, ds, history, optimiser, closeup=False):
        from pyrocko.orthodrome import latlon_to_ne_numpy
        problem = history.problem

        sat_targets = problem.satellite_targets
        for target in sat_targets:
            target.set_dataset(ds)

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

        source = problem.get_source(xbest)
        results = problem.evaluate(xbest, targets=sat_targets)

        def initAxes(ax, scene, title, last_axes=False):
            ax.set_title(title)
            ax.tick_params(length=2)

            if scene.frame.isMeter():
                ax.set_xlabel('Easting [km]')
                scale_x = {'scale': 1./km}
                scale_y = {'scale': 1./km}
                if not self.relative_coordinates:
                    import utm
                    utm_E, utm_N, utm_zone, utm_zone_letter =\
                        utm.from_latlon(source.lat, source.lon)
                    scale_x['offset'] = utm_E
                    scale_y['offset'] = utm_N

                    if last_axes:
                        ax.text(0.975, 0.025,
                                'UTM Zone %d%s' % (utm_zone, utm_zone_letter),
                                va='bottom', ha='right',
                                fontsize=8, alpha=.7,
                                transform=ax.transAxes)

            elif scene.frame.isDegree():
                ax.set_xlabel('Lon [°]')
                scale_x = {'scale': 1.}
                scale_y = {'scale': 1.}
                if not self.relative_coordinates:
                    scale_x['offset'] = source.lon
                    scale_y['offset'] = source.lat

            scale_axes(ax.get_xaxis(), **scale_x)
            scale_axes(ax.get_yaxis(), **scale_y)
            ax.set_aspect('equal')

        def drawSource(ax, scene):
            if scene.frame.isMeter():
                fn, fe = source.outline(cs='xy').T
            elif scene.frame.isDegree():
                fn, fe = source.outline(cs='latlon').T
                fn -= source.lat
                fe -= source.lon

            # source is centered
            ax.scatter(0., 0., color='black', s=3, alpha=.5, marker='o')
            ax.fill(fe, fn,
                    edgecolor=(0., 0., 0.),
                    facecolor=(.5, .5, .5), alpha=0.7)
            ax.plot(fe[0:2], fn[0:2], 'k', linewidth=1.3)

        def mapDisplacementGrid(displacements, scene):
            arr = num.full_like(scene.displacement, fill_value=num.nan)
            qt = scene.quadtree

            for syn_v, l in zip(displacements, qt.leaves):
                arr[l._slice_rows, l._slice_cols] = syn_v

            arr[scene.displacement_mask] = num.nan
            return arr

        def drawLeaves(ax, scene, offset_e=0, offset_n=0):
            rects = scene.quadtree.getMPLRectangles()
            for r in rects:
                r.set_edgecolor((.4, .4, .4))
                r.set_linewidth(.5)
                r.set_facecolor('none')
                r.set_x(r.get_x() - offset_e)
                r.set_y(r.get_y() - offset_n)
            map(ax.add_artist, rects)

            ax.scatter(scene.quadtree.leaf_coordinates[:, 0] - offset_e,
                       scene.quadtree.leaf_coordinates[:, 1] - offset_n,
                       s=.25, c='black', alpha=.1)

        def addArrow(ax, scene):
            phi = num.nanmean(scene.phi)
            los_dx = num.cos(phi + num.pi) * .0625
            los_dy = num.sin(phi + num.pi) * .0625

            az_dx = num.cos(phi - num.pi/2) * .125
            az_dy = num.sin(phi - num.pi/2) * .125

            anchor_x = .9 if los_dx < 0 else .1
            anchor_y = .85 if los_dx < 0 else .975

            az_arrow = patches.FancyArrow(
                x=anchor_x-az_dx, y=anchor_y-az_dy,
                dx=az_dx, dy=az_dy,
                head_width=.025,
                alpha=.5, fc='k',
                head_starts_at_zero=False,
                length_includes_head=True,
                transform=ax.transAxes)

            los_arrow = patches.FancyArrow(
                x=anchor_x-az_dx/2, y=anchor_y-az_dy/2,
                dx=los_dx, dy=los_dy,
                head_width=.02,
                alpha=.5, fc='k',
                head_starts_at_zero=False,
                length_includes_head=True,
                transform=ax.transAxes)

            ax.add_artist(az_arrow)
            ax.add_artist(los_arrow)

        urE, urN, llE, llN = (0., 0., 0., 0.)
        for target in sat_targets:

            if target.scene.frame.isMeter():
                off_n, off_e = map(float, latlon_to_ne_numpy(
                    target.scene.frame.llLat, target.scene.frame.llLon,
                    source.lat, source.lon))
            if target.scene.frame.isDegree():
                off_n = source.lat - target.scene.frame.llLat
                off_e = source.lon - target.scene.frame.llLon

            turE, turN, tllE, tllN = zip(
                *[(l.gridE.max()-off_e,
                   l.gridN.max()-off_n,
                   l.gridE.min()-off_e,
                   l.gridN.min()-off_n)
                  for l in target.scene.quadtree.leaves])

            turE, turN = map(max, (turE, turN))
            tllE, tllN = map(min, (tllE, tllN))
            urE, urN = map(max, ((turE, urE), (urN, turN)))
            llE, llN = map(min, ((tllE, llE), (llN, tllN)))

        def generate_plot(sat_target, result, ifig):

            scene = sat_target.scene

            fig = plt.figure()
            fig.set_size_inches(*self.size_inch)
            gs = gridspec.GridSpec(
                2, 3,
                wspace=.05, hspace=.2,
                left=.1, right=.975, top=.95,
                height_ratios=[12, 1])

            item = PlotItem(
                name='fig_%i' % ifig,
                attributes={'targets': [sat_target.path]},
                title=u'Satellite Surface Displacements - %s'
                      % scene.meta.scene_title,
                description=u'''Surface displacements derived from
satellite data, Scene {meta.scene_title} (id: {meta.scene_id}).
 (Left) the input data, (center) the
modelled data and (right) the model residual.'''.format(meta=scene.meta))

            stat_obs = result.statics_obs
            stat_syn = result.statics_syn['displacement.los']
            res = stat_obs - stat_syn

            if scene.frame.isMeter():
                offset_n, offset_e = map(float, latlon_to_ne_numpy(
                    scene.frame.llLat, scene.frame.llLon,
                    source.lat, source.lon))
            elif scene.frame.isDegree():
                offset_n = source.lat - scene.frame.llLat
                offset_e = source.lon - scene.frame.llLon

            im_extent = (scene.frame.E.min() - offset_e,
                         scene.frame.E.max() - offset_e,
                         scene.frame.N.min() - offset_n,
                         scene.frame.N.max() - offset_n)

            abs_displ = num.abs([stat_obs.min(), stat_obs.max(),
                                 stat_syn.min(), stat_syn.max(),
                                 res.min(), res.max()]).max()

            cmw = cm.ScalarMappable(cmap=self.colormap)
            cmw.set_clim(vmin=-abs_displ, vmax=abs_displ)
            cmw.set_array(stat_obs)

            axes = [fig.add_subplot(gs[0, 0]),
                    fig.add_subplot(gs[0, 1]),
                    fig.add_subplot(gs[0, 2])]

            ax = axes[0]
            ax.imshow(mapDisplacementGrid(stat_obs, scene),
                      extent=im_extent, cmap=self.colormap,
                      vmin=-abs_displ, vmax=abs_displ,
                      origin='lower')
            drawLeaves(ax, scene, offset_e, offset_n)
            drawSource(ax, scene)
            addArrow(ax, scene)
            initAxes(ax, scene, 'Observed')

            ax.text(.025, .025, 'Scene ID: %s' % scene.meta.scene_id,
                    fontsize=8, alpha=.7,
                    va='bottom', transform=ax.transAxes)
            if scene.frame.isDegree():
                ax.set_ylabel('Lat [°]')
            elif scene.frame.isMeter():
                ax.set_ylabel('Northing [km]')

            ax = axes[1]
            ax.imshow(mapDisplacementGrid(stat_syn, scene),
                      extent=im_extent, cmap=self.colormap,
                      vmin=-abs_displ, vmax=abs_displ,
                      origin='lower')
            drawLeaves(ax, scene, offset_e, offset_n)
            drawSource(ax, scene)
            addArrow(ax, scene)
            initAxes(ax, scene, 'Model')
            ax.get_yaxis().set_visible(False)

            ax = axes[2]
            ax.imshow(mapDisplacementGrid(res, scene),
                      extent=im_extent, cmap=self.colormap,
                      vmin=-abs_displ, vmax=abs_displ,
                      origin='lower')
            drawLeaves(ax, scene, offset_e, offset_n)
            drawSource(ax, scene)
            addArrow(ax, scene)
            initAxes(ax, scene, 'Residual', last_axes=True)
            ax.get_yaxis().set_visible(False)

            for ax in axes:
                ax.set_xlim(llE, urE)
                ax.set_ylim(llN, urN)

            if closeup:
                if scene.frame.isMeter():
                    fn, fe = source.outline(cs='xy').T
                elif scene.frame.isDegree():
                    fn, fe = source.outline(cs='latlon').T
                    fn -= source.lat
                    fe -= source.lon

                off_n = (fn[0] + fn[1]) / 2
                off_e = (fe[0] + fe[1]) / 2

                fault_size = 2*num.sqrt(max(abs(fn-off_n))**2
                                        + max(abs(fe-off_e))**2)
                fault_size *= self.map_scale

                for ax in axes:
                    ax.set_xlim(-fault_size/2 + off_e, fault_size/2 + off_e)
                    ax.set_ylim(-fault_size/2 + off_n, fault_size/2 + off_n)

            cax = fig.add_subplot(gs[1, :])
            cbar = fig.colorbar(cmw, cax=cax, orientation='horizontal',
                                use_gridspec=True)
            
            cbar.set_label('LOS Displacement [m]')

            return (item, fig)

        for ifig, (sat_target, result) in enumerate(zip(sat_targets, results)):
            yield generate_plot(sat_target, result, ifig)
Exemple #26
0
class AutoScaler(Object):
    '''
    Tunable 1D autoscaling based on data range.

    Instances of this class may be used to determine nice minima, maxima and
    increments for ax annotations, as well as suitable common exponents for
    notation.

    The autoscaling process is guided by the following public attributes:
    '''

    approx_ticks = Float.T(
        default=7.0,
        help='Approximate number of increment steps (tickmarks) to generate.')

    mode = AutoScaleMode.T(default='auto',
                           help='''Mode of operation for auto-scaling.''')

    exp = Int.T(
        optional=True,
        help='If defined, override automatically determined exponent for '
        'notation by the given value.')

    snap = Bool.T(
        default=False,
        help='If set to True, snap output range to multiples of increment. '
        'This parameter has no effect, if mode is set to ``\'off\'``.')

    inc = Float.T(
        optional=True,
        help='If defined, override automatically determined tick increment by '
        'the given value.')

    space = Float.T(
        default=0.0,
        help='Add some padding to the range. The value given, is the fraction '
        'by which the output range is increased on each side. If mode is '
        '``\'0-max\'`` or ``\'min-0\'``, the end at zero is kept fixed '
        'at zero. This parameter has no effect if mode is set to '
        '``\'off\'``.')

    exp_factor = Int.T(
        default=3,
        help='Exponent of notation is chosen to be a multiple of this value.')

    no_exp_interval = Tuple.T(
        2,
        Int.T(),
        default=(-3, 5),
        help='Range of exponent, for which no exponential notation is a'
        'allowed.')

    def __init__(self,
                 approx_ticks=7.0,
                 mode='auto',
                 exp=None,
                 snap=False,
                 inc=None,
                 space=0.0,
                 exp_factor=3,
                 no_exp_interval=(-3, 5)):
        '''
        Create new AutoScaler instance.

        The parameters are described in the AutoScaler documentation.
        '''

        Object.__init__(self,
                        approx_ticks=approx_ticks,
                        mode=mode,
                        exp=exp,
                        snap=snap,
                        inc=inc,
                        space=space,
                        exp_factor=exp_factor,
                        no_exp_interval=no_exp_interval)

    def make_scale(self, data_range, override_mode=None):
        '''
        Get nice minimum, maximum and increment for given data range.

        Returns ``(minimum, maximum, increment)`` or ``(maximum, minimum,
        -increment)``, depending on whether data_range is ``(data_min,
        data_max)`` or ``(data_max, data_min)``. If ``override_mode`` is
        defined, the mode attribute is temporarily overridden by the given
        value.
        '''

        data_min = min(data_range)
        data_max = max(data_range)

        is_reverse = (data_range[0] > data_range[1])

        a = self.mode
        if self.mode == 'auto':
            a = self.guess_autoscale_mode(data_min, data_max)

        if override_mode is not None:
            a = override_mode

        mi, ma = 0, 0
        if a == 'off':
            mi, ma = data_min, data_max
        elif a == '0-max':
            mi = 0.0
            if data_max > 0.0:
                ma = data_max
            else:
                ma = 1.0
        elif a == 'min-0':
            ma = 0.0
            if data_min < 0.0:
                mi = data_min
            else:
                mi = -1.0
        elif a == 'min-max':
            mi, ma = data_min, data_max
        elif a == 'symmetric':
            m = max(abs(data_min), abs(data_max))
            mi = -m
            ma = m

        nmi = mi
        if (mi != 0. or a == 'min-max') and a != 'off':
            nmi = mi - self.space * (ma - mi)

        nma = ma
        if (ma != 0. or a == 'min-max') and a != 'off':
            nma = ma + self.space * (ma - mi)

        mi, ma = nmi, nma

        if mi == ma and a != 'off':
            mi -= 1.0
            ma += 1.0

        # make nice tick increment
        if self.inc is not None:
            inc = self.inc
        else:
            if self.approx_ticks > 0.:
                inc = nice_value((ma - mi) / self.approx_ticks)
            else:
                inc = nice_value((ma - mi) * 10.)

        if inc == 0.0:
            inc = 1.0

        # snap min and max to ticks if this is wanted
        if self.snap and a != 'off':
            ma = inc * math.ceil(ma / inc)
            mi = inc * math.floor(mi / inc)

        if is_reverse:
            return ma, mi, -inc
        else:
            return mi, ma, inc

    def make_exp(self, x):
        '''Get nice exponent for notation of ``x``.

        For ax annotations, give tick increment as ``x``.'''

        if self.exp is not None:
            return self.exp

        x = abs(x)
        if x == 0.0:
            return 0

        if 10**self.no_exp_interval[0] <= x <= 10**self.no_exp_interval[1]:
            return 0

        return math.floor(math.log10(x) / self.exp_factor) * self.exp_factor

    def guess_autoscale_mode(self, data_min, data_max):
        '''Guess mode of operation, based on data range.

        Used to map ``'auto'`` mode to ``'0-max'``, ``'min-0'``, ``'min-max'``
        or ``'symmetric'``.'''

        a = 'min-max'
        if data_min >= 0.0:
            if data_min < data_max / 2.:
                a = '0-max'
            else:
                a = 'min-max'
        if data_max <= 0.0:
            if data_max > data_min / 2.:
                a = 'min-0'
            else:
                a = 'min-max'
        if data_min < 0.0 and data_max > 0.0:
            if abs((abs(data_max) - abs(data_min)) /
                   (abs(data_max) + abs(data_min))) < 0.5:
                a = 'symmetric'
            else:
                a = 'min-max'
        return a
Exemple #27
0
class ScenePatch(Object):
    lat_center = Float.T(help='Center latitude anchor.')
    lon_center = Float.T(help='center longitude anchor.')
    time_master = Timestamp.T(help='Timestamp of the master.')
    time_slave = Timestamp.T(help='Timestamp of the slave.')
    inclination = Float.T(
        help='Orbital inclination towards the equatorial plane [deg].')
    apogee = Float.T(help='Apogee of the satellite in [m].')
    swath_width = Float.T(default=250 * km, help='Swath width in [m].')
    track_length = Float.T(help='Track length in [m].')
    incident_angle = Float.T(help='Ground incident angle in [deg].')
    resolution = Tuple.T(help='Resolution of raster in east x north [px].')
    orbital_node = StringChoice.T(['Ascending', 'Descending'],
                                  help='Orbit heading.')
    mask_water = Bool.T(default=True, help='Mask water bodies.')

    class SatelliteGeneratorTarget(gf.SatelliteTarget):
        def __init__(self, scene_patch, *args, **kwargs):
            gf.SatelliteTarget.__init__(self, *args, **kwargs)

            self.scene_patch = scene_patch

        def post_process(self, *args, **kwargs):
            resp = gf.SatelliteTarget.post_process(self, *args, **kwargs)

            from kite import Scene
            from kite.scene import SceneConfig, FrameConfig, Meta

            patch = self.scene_patch

            grid, _ = patch.get_grid()

            displacement = num.empty_like(grid)
            displacement.fill(num.nan)
            displacement[patch.get_mask()] = resp.result['displacement.los']

            theta, phi = patch.get_incident_angles()

            llLat, llLon = patch.get_ll_anchor()
            urLat, urLon = patch.get_ur_anchor()
            dLon = num.abs(llLon - urLon) / patch.resolution[0]
            dLat = num.abs(llLat - urLat) / patch.resolution[1]

            scene_config = SceneConfig(meta=Meta(
                scene_title='Pyrocko Scenario Generator - {orbit} ({time})'.
                format(orbit=self.scene_patch.orbital_node,
                       time=datetime.now()),
                orbital_node=patch.orbital_node,
                scene_id='pyrocko.scenario-%s' % self.scene_patch.orbital_node,
                satellite_name='Sentinel-1 (Scenario)'),
                                       frame=FrameConfig(llLat=float(llLat),
                                                         llLon=float(llLon),
                                                         dN=float(dLat),
                                                         dE=float(dLon),
                                                         spacing='degree'))

            scene = Scene(displacement=displacement,
                          theta=theta,
                          phi=phi,
                          config=scene_config)

            resp.scene = scene

            return resp

    def __init__(self, *args, **kwargs):
        Object.__init__(self, *args, **kwargs)
        self._mask_water = None

    @property
    def width(self):
        track_shift = num.abs(
            num.cos(self.inclination * d2r) * self.track_length)
        return self.swath_width + track_shift

    def get_ll_anchor(self):
        return od.ne_to_latlon(self.lat_center, self.lon_center,
                               -self.track_length / 2, -self.width / 2)

    def get_ur_anchor(self):
        return od.ne_to_latlon(self.lat_center, self.lon_center,
                               self.track_length / 2, self.width / 2)

    def get_ul_anchor(self):
        return od.ne_to_latlon(self.lat_center, self.lon_center,
                               self.track_length / 2, -self.width / 2)

    def get_lr_anchor(self):
        return od.ne_to_latlon(self.lat_center, self.lon_center,
                               -self.track_length / 2, self.width / 2)

    def get_corner_coordinates(self):
        inc = self.inclination

        llLat, llLon = self.get_ll_anchor()
        urLat, urLon = self.get_ur_anchor()

        if self.orbital_node == 'Ascending':

            ulLat, ulLon = od.ne_to_latlon(
                self.lat_center, self.lon_center, self.track_length / 2,
                -num.tan(inc * d2r) * self.width / 2)
            lrLat, lrLon = od.ne_to_latlon(self.lat_center, self.lon_center,
                                           -self.track_length / 2,
                                           num.tan(inc * d2r) * self.width / 2)

        elif self.orbital_node == 'Descending':
            urLat, urLon = od.ne_to_latlon(self.lat_center, self.lon_center,
                                           self.track_length / 2,
                                           num.tan(inc * d2r) * self.width / 2)
            llLat, llLon = od.ne_to_latlon(
                self.lat_center, self.lon_center, -self.track_length / 2,
                -num.tan(inc * d2r) * self.width / 2)

        return ((llLat, llLon), (ulLat, ulLon), (urLat, urLon), (lrLat, lrLon))

    def get_grid(self):
        '''Return relative positions of scatterer.

        :param track: Acquisition track, from `'asc'` or `'dsc'`.
        :type track: string
        '''
        easts = num.linspace(0, self.width, self.resolution[0])
        norths = num.linspace(0, self.track_length, self.resolution[1])

        return num.meshgrid(easts, norths)

    def get_mask_track(self):
        east_shifts, north_shifts = self.get_grid()
        norths = north_shifts[:, 0]
        track = num.abs(num.cos(self.inclination * d2r)) * norths

        track_mask = num.logical_and(
            east_shifts > track[:, num.newaxis], east_shifts <
            (track + self.swath_width)[:, num.newaxis])

        if self.orbital_node == 'Ascending':
            track_mask = num.fliplr(track_mask)

        return track_mask

    def get_mask_water(self):
        if self._mask_water is None:
            east_shifts, north_shifts = self.get_grid()

            east_shifts -= east_shifts[0, -1] / 2
            north_shifts -= north_shifts[-1, -1] / 2

            latlon = od.ne_to_latlon(self.lat_center, self.lon_center,
                                     north_shifts.ravel(), east_shifts.ravel())
            points = num.array(latlon).T
            self._mask_water = get_gsshg().get_land_mask(points)\
                .reshape(*east_shifts.shape)

        return self._mask_water

    def get_mask(self):
        mask_track = self.get_mask_track()
        if self.mask_water:
            mask_water = self.get_mask_water()
            return num.logical_and(mask_track, mask_water)
        return mask_track

    def get_incident_angles(self):
        # theta: elevation angle towards satellite from horizon in radians.
        # phi:  Horizontal angle towards satellite' :abbr:`line of sight (LOS)`
        #       in [rad] from East.
        east_shifts, _ = self.get_grid()

        phi = num.empty_like(east_shifts)
        theta = num.empty_like(east_shifts)

        east_shifts += num.tan(self.incident_angle * d2r) * self.apogee
        theta = num.arctan(east_shifts / self.apogee)

        if self.orbital_node == 'Ascending':
            phi.fill(self.inclination * d2r + num.pi / 2)
        elif self.orbital_node == 'Descending':
            phi.fill(2 * num.pi - (self.inclination * d2r + 3 / 2 * num.pi))
            theta = num.fliplr(theta)
        else:
            raise AttributeError('Orbital node %s not defined!' %
                                 self.orbital_node)

        return theta, phi

    def get_target(self):
        gE, gN = self.get_grid()
        mask = self.get_mask()

        east_shifts = gE[mask].ravel()
        north_shifts = gN[mask].ravel()
        llLat, llLon = self.get_ll_anchor()

        ncoords = east_shifts.size

        theta, phi = self.get_incident_angles()

        theta = theta[mask].ravel()
        phi = phi[mask].ravel()

        if ncoords == 0:
            logger.warning('InSAR taget has no valid points,'
                           ' maybe it\'s all water?')
            return

        return self.SatelliteGeneratorTarget(scene_patch=self,
                                             lats=num.full(ncoords,
                                                           fill_value=llLat),
                                             lons=num.full(ncoords,
                                                           fill_value=llLon),
                                             east_shifts=east_shifts,
                                             north_shifts=north_shifts,
                                             theta=theta,
                                             phi=phi)
Exemple #28
0
class ProblemConfig(Object):
    """
    Config for optimization problem to setup.
    """
    mode = StringChoice.T(
        choices=['geometry', 'ffi', 'interseismic'],
        default='geometry',
        help='Problem to solve: "geometry", "ffi",'
        ' "interseismic"',
    )
    mode_config = ModeConfig.T(
        optional=True, help='Global optimization mode specific parameters.')
    source_type = StringChoice.T(
        default='RectangularSource',
        choices=source_names,
        help='Source type to optimize for. Options: %s' %
        (', '.join(name for name in source_names)))
    stf_type = StringChoice.T(
        default='HalfSinusoid',
        choices=stf_names,
        help='Source time function type to use. Options: %s' %
        (', '.join(name for name in stf_names)))
    decimation_factors = Dict.T(
        default=None,
        optional=True,
        help='Determines the reduction of discretization of an extended'
        ' source.')
    n_sources = Int.T(default=1, help='Number of Sub-sources to solve for')
    datatypes = List.T(default=['geodetic'])
    dataset_specific_residual_noise_estimation = Bool.T(
        default=False,
        help='If set, for EACH DATASET specific hyperparameter estimation.'
        'For seismic data: n_hypers = nstations * nchannels.'
        'For geodetic data: n_hypers = nimages (SAR) or '
        'nstations * ncomponents (GPS).'
        'If false one hyperparameter for each DATATYPE and '
        'displacement COMPONENT.')
    hyperparameters = Dict.T(
        default=OrderedDict(),
        help='Hyperparameters to estimate the noise in different'
        ' types of datatypes.')
    priors = Dict.T(default=OrderedDict(),
                    help='Priors of the variables in question.')
    hierarchicals = Dict.T(
        default=OrderedDict(),
        help='Hierarchical parameters that affect the posterior'
        ' likelihood, but do not affect the forward problem.'
        ' Implemented: Temporal station corrections, orbital'
        ' ramp estimation')

    def __init__(self, **kwargs):

        mode = 'mode'
        mode_config = 'mode_config'
        if mode in kwargs:
            omode = kwargs[mode]

            if omode == 'ffi':
                if mode_config not in kwargs:
                    kwargs[mode_config] = FFIConfig()

        Object.__init__(self, **kwargs)

    def init_vars(self, variables=None, nvars=None):
        """
        Initiate priors based on the problem mode and datatypes.

        Parameters
        ----------
        variables : list
            of str of variable names to initialise
        """
        if variables is None:
            variables = self.select_variables()

        self.priors = OrderedDict()

        for variable in variables:

            if nvars is None:
                if variable in block_vars:
                    nvars = 1
                else:
                    nvars = self.n_sources

            lower = default_bounds[variable][0]
            upper = default_bounds[variable][1]
            self.priors[variable] = \
                Parameter(
                    name=variable,
                    lower=num.ones(
                        nvars,
                        dtype=tconfig.floatX) * lower,
                    upper=num.ones(
                        nvars,
                        dtype=tconfig.floatX) * upper,
                    testvalue=num.ones(
                        nvars,
                        dtype=tconfig.floatX) * (lower + (upper / 5.)))

    def set_vars(self, bounds_dict):
        """
        Set variable bounds to given bounds.
        """
        for variable, bounds in bounds_dict.items():
            if variable in self.priors.keys():
                param = self.priors[variable]
                param.lower = num.atleast_1d(bounds[0])
                param.upper = num.atleast_1d(bounds[1])
                param.testvalue = num.atleast_1d(num.mean(bounds))
            else:
                logger.warning('Prior for variable %s does not exist!'
                               ' Bounds not updated!' % variable)

    def select_variables(self):
        """
        Return model variables depending on problem config.
        """

        if self.mode not in modes_catalog.keys():
            raise ValueError('Problem mode %s not implemented' % self.mode)

        vars_catalog = modes_catalog[self.mode]

        variables = []
        for datatype in self.datatypes:
            if datatype in vars_catalog.keys():
                if self.mode == 'geometry':
                    if self.source_type in vars_catalog[datatype].keys():
                        source = vars_catalog[datatype][self.source_type]
                        svars = set(source.keys())

                        if isinstance(source(),
                                      (PyrockoRS, gf.ExplosionSource)):
                            svars.discard('magnitude')

                        variables += utility.weed_input_rvs(
                            svars, self.mode, datatype)
                    else:
                        raise ValueError('Source Type not supported for type'
                                         ' of problem, and datatype!')

                    if datatype == 'seismic':
                        if self.stf_type in stf_catalog.keys():
                            stf = stf_catalog[self.stf_type]
                            variables += utility.weed_input_rvs(
                                set(stf.keys()), self.mode, datatype)
                else:
                    variables += vars_catalog[datatype]
            else:
                raise ValueError(
                    'Datatype %s not supported for type of'
                    ' problem! Supported datatype are: %s' %
                    (datatype, ', '.join('"%s"' % d
                                         for d in vars_catalog.keys())))

        unique_variables = utility.unique_list(variables)

        if len(unique_variables) == 0:
            raise Exception('Mode and datatype combination not implemented'
                            ' or not resolvable with given datatypes.')

        return unique_variables

    def get_slip_variables(self):
        """
        Return a list of slip variable names defined in the ProblemConfig.
        """
        if self.mode == 'ffi':
            return [
                var for var in static_dist_vars if var in self.priors.keys()
            ]
        elif self.mode == 'geometry':
            return [
                var for var in ['slip', 'magnitude']
                if var in self.priors.keys()
            ]
        elif self.mode == 'interseismic':
            return ['bl_amplitude']

    def set_decimation_factor(self):
        """
        Determines the reduction of discretization of an extended source.
        Influences yet only the RectangularSource.
        """
        if self.source_type == 'RectangularSource':
            self.decimation_factors = {}
            for datatype in self.datatypes:
                self.decimation_factors[datatype] = \
                    default_decimation_factors[datatype]
        else:
            self.decimation_factors = None

    def validate_priors(self):
        """
        Check if priors and their test values do not contradict!
        """
        for param in self.priors.itervalues():
            param.validate_bounds()

        logger.info('All parameter-priors ok!')

    def validate_hypers(self):
        """
        Check if hyperparameters and their test values do not contradict!
        """
        if self.hyperparameters is not None:
            for hp in self.hyperparameters.itervalues():
                hp.validate_bounds()

            logger.info('All hyper-parameters ok!')

        else:
            logger.info('No hyper-parameters defined!')

    def validate_hierarchicals(self):
        """
        Check if hierarchicals and their test values do not contradict!
        """
        if self.hierarchicals is not None:
            for hp in self.hierarchicals.itervalues():
                hp.validate_bounds()

            logger.info('All hierarchical-parameters ok!')

        else:
            logger.info('No hyper-parameters defined!')

    def get_test_point(self):
        """
        Returns dict with test point
        """
        test_point = {}
        for varname, var in self.priors.items():
            test_point[varname] = var.testvalue

        for varname, var in self.hyperparameters.items():
            test_point[varname] = var.testvalue

        for varname, var in self.hierarchicals.items():
            test_point[varname] = var.testvalue

        return test_point
Exemple #29
0
class Map(Object):
    lat = Float.T(optional=True)
    lon = Float.T(optional=True)
    radius = Float.T(optional=True)
    width = Float.T(default=20.)
    height = Float.T(default=14.)
    margins = List.T(Float.T())
    illuminate = Bool.T(default=True)
    skip_feature_factor = Float.T(default=0.02)
    show_grid = Bool.T(default=False)
    show_topo = Bool.T(default=True)
    show_scale = Bool.T(default=False)
    show_topo_scale = Bool.T(default=False)
    show_center_mark = Bool.T(default=False)
    show_rivers = Bool.T(default=True)
    show_plates = Bool.T(default=False)
    show_boundaries = Bool.T(default=False)
    illuminate_factor_land = Float.T(default=0.5)
    illuminate_factor_ocean = Float.T(default=0.25)
    color_wet = Tuple.T(3, Int.T(), default=(216, 242, 254))
    color_dry = Tuple.T(3, Int.T(), default=(172, 208, 165))
    color_boundaries = Tuple.T(3, Int.T(), default=(1, 1, 1))
    topo_resolution_min = Float.T(
        default=40.,
        help='minimum resolution of topography [dpi]')
    topo_resolution_max = Float.T(
        default=200.,
        help='maximum resolution of topography [dpi]')
    replace_topo_color_only = FloatTile.T(
        optional=True,
        help='replace topo color while keeping topographic shading')
    topo_cpt_wet = String.T(default='light_sea')
    topo_cpt_dry = String.T(default='light_land')
    axes_layout = String.T(optional=True)
    custom_cities = List.T(City.T())
    gmt_config = Dict.T(String.T(), String.T())
    comment = String.T(optional=True)

    def __init__(self, gmtversion='newest', **kwargs):
        Object.__init__(self, **kwargs)
        self._gmt = None
        self._scaler = None
        self._widget = None
        self._corners = None
        self._wesn = None
        self._minarea = None
        self._coastline_resolution = None
        self._rivers = None
        self._dems = None
        self._have_topo_land = None
        self._have_topo_ocean = None
        self._jxyr = None
        self._prep_topo_have = None
        self._labels = []
        self._area_labels = []
        self._gmtversion = gmtversion

    def save(self, outpath, resolution=75., oversample=2., size=None,
             width=None, height=None, psconvert=False):

        '''
        Save the image.

        Save the image to ``outpath``. The format is determined by the filename
        extension. Formats are handled as follows: ``'.eps'`` and ``'.ps'``
        produce EPS and PS, respectively, directly with GMT. If the file name
        ends with ``'.pdf'``, GMT output is fed through ``gmtpy-epstopdf`` to
        create a PDF file. For any other filename extension, output is first
        converted to PDF with ``gmtpy-epstopdf``, then with ``pdftocairo`` to
        PNG with a resolution oversampled by the factor ``oversample`` and
        finally the PNG is downsampled and converted to the target format with
        ``convert``. The resolution of rasterized target image can be
        controlled either by ``resolution`` in DPI or by specifying ``width``
        or ``height`` or ``size``, where the latter fits the image into a
        square with given side length. To save transparency use
        ``psconvert=True``.
        '''

        gmt = self.gmt
        self.draw_labels()
        self.draw_axes()
        if self.show_topo and self.show_topo_scale:
            self._draw_topo_scale()

        gmt.save(outpath, resolution=resolution, oversample=oversample,
                 size=size, width=width, height=height, psconvert=psconvert)

    @property
    def scaler(self):
        if self._scaler is None:
            self._setup_geometry()

        return self._scaler

    @property
    def wesn(self):
        if self._wesn is None:
            self._setup_geometry()

        return self._wesn

    @property
    def widget(self):
        if self._widget is None:
            self._setup()

        return self._widget

    @property
    def layout(self):
        if self._layout is None:
            self._setup()

        return self._layout

    @property
    def jxyr(self):
        if self._jxyr is None:
            self._setup()

        return self._jxyr

    @property
    def pxyr(self):
        if self._pxyr is None:
            self._setup()

        return self._pxyr

    @property
    def gmt(self):
        if self._gmt is None:
            self._setup()

        if self._have_topo_ocean is None:
            self._draw_background()

        return self._gmt

    def _setup(self):
        if not self._widget:
            self._setup_geometry()

        self._setup_lod()
        self._setup_gmt()

    def _setup_geometry(self):
        wpage, hpage = self.width, self.height
        ml, mr, mt, mb = self._expand_margins()
        wpage -= ml + mr
        hpage -= mt + mb

        wreg = self.radius * 2.0
        hreg = self.radius * 2.0
        if wpage >= hpage:
            wreg *= wpage/hpage
        else:
            hreg *= hpage/wpage

        self._wreg = wreg
        self._hreg = hreg

        self._corners = corners(self.lon, self.lat, wreg, hreg)
        west, east, south, north = extent(self.lon, self.lat, wreg, hreg, 10)

        x, y, z = ((west, east), (south, north), (-6000., 4500.))

        xax = gmtpy.Ax(mode='min-max', approx_ticks=4.)
        yax = gmtpy.Ax(mode='min-max', approx_ticks=4.)
        zax = gmtpy.Ax(mode='min-max', inc=1000., label='Height',
                       scaled_unit='km', scaled_unit_factor=0.001)

        scaler = gmtpy.ScaleGuru(data_tuples=[(x, y, z)], axes=(xax, yax, zax))

        par = scaler.get_params()

        west = par['xmin']
        east = par['xmax']
        south = par['ymin']
        north = par['ymax']

        self._wesn = west, east, south, north
        self._scaler = scaler

    def _setup_lod(self):
        w, e, s, n = self._wesn
        if self.radius > 1500.*km:
            coastline_resolution = 'i'
            rivers = False
        else:
            coastline_resolution = 'f'
            rivers = True

        self._minarea = (self.skip_feature_factor * self.radius/km)**2

        self._coastline_resolution = coastline_resolution
        self._rivers = rivers

        self._prep_topo_have = {}
        self._dems = {}

        cm2inch = gmtpy.cm/gmtpy.inch

        dmin = 2.0 * self.radius * m2d / (self.topo_resolution_max *
                                          (self.height * cm2inch))
        dmax = 2.0 * self.radius * m2d / (self.topo_resolution_min *
                                          (self.height * cm2inch))

        for k in ['ocean', 'land']:
            self._dems[k] = topo.select_dem_names(k, dmin, dmax, self._wesn)
            if self._dems[k]:
                logger.debug('using topography dataset %s for %s'
                             % (','.join(self._dems[k]), k))

    def _expand_margins(self):
        if len(self.margins) == 0 or len(self.margins) > 4:
            ml = mr = mt = mb = 2.0
        elif len(self.margins) == 1:
            ml = mr = mt = mb = self.margins[0]
        elif len(self.margins) == 2:
            ml = mr = self.margins[0]
            mt = mb = self.margins[1]
        elif len(self.margins) == 4:
            ml, mr, mt, mb = self.margins

        return ml, mr, mt, mb

    def _setup_gmt(self):
        w, h = self.width, self.height
        scaler = self._scaler

        if gmtpy.is_gmt5(self._gmtversion):
            gmtconf = dict(
                MAP_TICK_PEN_PRIMARY='1.25p',
                MAP_TICK_PEN_SECONDARY='1.25p',
                MAP_TICK_LENGTH_PRIMARY='0.2c',
                MAP_TICK_LENGTH_SECONDARY='0.6c',
                FONT_ANNOT_PRIMARY='12p,1,black',
                FONT_LABEL='12p,1,black',
                PS_CHAR_ENCODING='ISOLatin1+',
                MAP_FRAME_TYPE='fancy',
                FORMAT_GEO_MAP='D',
                PS_MEDIA='Custom_%ix%i' % (
                    w*gmtpy.cm,
                    h*gmtpy.cm),
                PS_PAGE_ORIENTATION='portrait',
                MAP_GRID_PEN_PRIMARY='thinnest,0/50/0',
                MAP_ANNOT_OBLIQUE='6')
        else:
            gmtconf = dict(
                TICK_PEN='1.25p',
                TICK_LENGTH='0.2c',
                ANNOT_FONT_PRIMARY='1',
                ANNOT_FONT_SIZE_PRIMARY='12p',
                LABEL_FONT='1',
                LABEL_FONT_SIZE='12p',
                CHAR_ENCODING='ISOLatin1+',
                BASEMAP_TYPE='fancy',
                PLOT_DEGREE_FORMAT='D',
                PAPER_MEDIA='Custom_%ix%i' % (
                    w*gmtpy.cm,
                    h*gmtpy.cm),
                GRID_PEN_PRIMARY='thinnest/0/50/0',
                DOTS_PR_INCH='1200',
                OBLIQUE_ANNOTATION='6')

        gmtconf.update(
            (k.upper(), v) for (k, v) in self.gmt_config.items())

        gmt = gmtpy.GMT(config=gmtconf, version=self._gmtversion)

        layout = gmt.default_layout()

        layout.set_fixed_margins(*[x*cm for x in self._expand_margins()])

        widget = layout.get_widget()
        widget['P'] = widget['J']
        widget['J'] = ('-JA%g/%g' % (self.lon, self.lat)) + '/%(width)gp'
        scaler['R'] = '-R%g/%g/%g/%gr' % self._corners

        # aspect = gmtpy.aspect_for_projection(
        #     gmt.installation['version'], *(widget.J() + scaler.R()))

        aspect = self._map_aspect(jr=widget.J() + scaler.R())
        widget.set_aspect(aspect)

        self._gmt = gmt
        self._layout = layout
        self._widget = widget
        self._jxyr = self._widget.JXY() + self._scaler.R()
        self._pxyr = self._widget.PXY() + [
            '-R%g/%g/%g/%g' % (0, widget.width(), 0, widget.height())]
        self._have_drawn_axes = False
        self._have_drawn_labels = False

    def _draw_background(self):
        self._have_topo_land = False
        self._have_topo_ocean = False
        if self.show_topo:
            self._have_topo = self._draw_topo()

        self._draw_basefeatures()

    def _get_topo_tile(self, k):
        t = None
        demname = None
        for dem in self._dems[k]:
            t = topo.get(dem, self._wesn)
            demname = dem
            if t is not None:
                break

        if not t:
            raise NoTopo()

        return t, demname

    def _prep_topo(self, k):
        gmt = self._gmt
        t, demname = self._get_topo_tile(k)

        if demname not in self._prep_topo_have:

            grdfile = gmt.tempfilename()

            is_flat = num.all(t.data[0] == t.data)

            gmtpy.savegrd(
                t.x(), t.y(), t.data, filename=grdfile, naming='lonlat')

            if self.illuminate and not is_flat:
                if k == 'ocean':
                    factor = self.illuminate_factor_ocean
                else:
                    factor = self.illuminate_factor_land

                ilumfn = gmt.tempfilename()
                gmt.grdgradient(
                    grdfile,
                    N='e%g' % factor,
                    A=-45,
                    G=ilumfn,
                    out_discard=True)

                ilumargs = ['-I%s' % ilumfn]
            else:
                ilumargs = []

            if self.replace_topo_color_only:
                t2 = self.replace_topo_color_only
                grdfile2 = gmt.tempfilename()

                gmtpy.savegrd(
                    t2.x(), t2.y(), t2.data, filename=grdfile2,
                    naming='lonlat')

                if gmt.is_gmt5():
                    gmt.grdsample(
                        grdfile2,
                        G=grdfile,
                        n='l',
                        I='%g/%g' % (t.dx, t.dy),  # noqa
                        R=grdfile,
                        out_discard=True)
                else:
                    gmt.grdsample(
                        grdfile2,
                        G=grdfile,
                        Q='l',
                        I='%g/%g' % (t.dx, t.dy),  # noqa
                        R=grdfile,
                        out_discard=True)

                gmt.grdmath(
                    grdfile, '0.0', 'AND', '=', grdfile2,
                    out_discard=True)

                grdfile = grdfile2

            self._prep_topo_have[demname] = grdfile, ilumargs

        return self._prep_topo_have[demname]

    def _draw_topo(self):
        widget = self._widget
        scaler = self._scaler
        gmt = self._gmt
        cres = self._coastline_resolution
        minarea = self._minarea

        JXY = widget.JXY()
        R = scaler.R()

        try:
            grdfile, ilumargs = self._prep_topo('ocean')
            gmt.pscoast(D=cres, S='c', A=minarea, *(JXY+R))
            gmt.grdimage(grdfile, C=topo.cpt(self.topo_cpt_wet),
                         *(ilumargs+JXY+R))
            gmt.pscoast(Q=True, *(JXY+R))
            self._have_topo_ocean = True
        except NoTopo:
            self._have_topo_ocean = False

        try:
            grdfile, ilumargs = self._prep_topo('land')
            gmt.pscoast(D=cres, G='c', A=minarea, *(JXY+R))
            gmt.grdimage(grdfile, C=topo.cpt(self.topo_cpt_dry),
                         *(ilumargs+JXY+R))
            gmt.pscoast(Q=True, *(JXY+R))
            self._have_topo_land = True
        except NoTopo:
            self._have_topo_land = False

    def _draw_topo_scale(self, label='Elevation [km]'):
        dry = read_cpt(topo.cpt(self.topo_cpt_dry))
        wet = read_cpt(topo.cpt(self.topo_cpt_wet))
        combi = cpt_merge_wet_dry(wet, dry)
        for level in combi.levels:
            level.vmin /= km
            level.vmax /= km

        topo_cpt = self.gmt.tempfilename() + '.cpt'
        write_cpt(combi, topo_cpt)

        (w, h), (xo, yo) = self.widget.get_size()
        self.gmt.psscale(
            D='%gp/%gp/%gp/%gph' % (xo + 0.5*w, yo - 2.0*gmtpy.cm, w,
                                    0.5*gmtpy.cm),
            C=topo_cpt,
            B='1:%s:' % label)

    def _draw_basefeatures(self):
        gmt = self._gmt
        cres = self._coastline_resolution
        rivers = self._rivers
        minarea = self._minarea

        color_wet = self.color_wet
        color_dry = self.color_dry

        if self.show_rivers and rivers:
            rivers = ['-Ir/0.25p,%s' % gmtpy.color(self.color_wet)]
        else:
            rivers = []

        fill = {}
        if not self._have_topo_land:
            fill['G'] = color_dry

        if not self._have_topo_ocean:
            fill['S'] = color_wet

        if self.show_boundaries:
            fill['N'] = '1/1p,%s,%s' % (
                gmtpy.color(self.color_boundaries), 'solid')

        gmt.pscoast(
            D=cres,
            W='thinnest,%s' % gmtpy.color(darken(gmtpy.color_tup(color_dry))),
            A=minarea,
            *(rivers+self._jxyr), **fill)

        if self.show_plates:
            self.draw_plates()

    def _draw_axes(self):
        gmt = self._gmt
        scaler = self._scaler
        widget = self._widget

        if self.axes_layout is None:
            if self.lat > 0.0:
                axes_layout = 'WSen'
            else:
                axes_layout = 'WseN'
        else:
            axes_layout = self.axes_layout

        scale_km = gmtpy.nice_value(self.radius/5.) / 1000.

        if self.show_center_mark:
            gmt.psxy(
                in_rows=[[self.lon, self.lat]],
                S='c20p', W='2p,black',
                *self._jxyr)

        if self.show_grid:
            btmpl = ('%(xinc)gg%(xinc)g:%(xlabel)s:/'
                     '%(yinc)gg%(yinc)g:%(ylabel)s:')
        else:
            btmpl = '%(xinc)g:%(xlabel)s:/%(yinc)g:%(ylabel)s:'

        if self.show_scale:
            scale = 'x%gp/%gp/%g/%g/%gk' % (
                    6./7*widget.width(),
                    widget.height()/7.,
                    self.lon,
                    self.lat,
                    scale_km)
        else:
            scale = False

        gmt.psbasemap(
            B=(btmpl % scaler.get_params())+axes_layout,
            L=scale,
            *self._jxyr)

        if self.comment:
            font_size = self.gmt.label_font_size()

            _, east, south, _ = self._wesn
            if gmt.is_gmt5():
                row = [
                    1, 0,
                    '%gp,%s,%s' % (font_size, 0, 'black'), 'BR',
                    self.comment]

                farg = ['-F+f+j']
            else:
                row = [1, 0, font_size, 0, 0, 'BR', self.comment]
                farg = []

            gmt.pstext(
                in_rows=[row],
                N=True,
                R=(0, 1, 0, 1),
                D='%gp/%gp' % (-font_size*0.2, font_size*0.3),
                *(widget.PXY() + farg))

    def draw_axes(self):
        if not self._have_drawn_axes:
            self._draw_axes()
            self._have_drawn_axes = True

    def _have_coastlines(self):
        gmt = self._gmt
        cres = self._coastline_resolution
        minarea = self._minarea

        checkfile = gmt.tempfilename()

        gmt.pscoast(
            M=True,
            D=cres,
            W='thinnest,black',
            A=minarea,
            out_filename=checkfile,
            *self._jxyr)

        points = []
        with open(checkfile, 'r') as f:
            for line in f:
                ls = line.strip()
                if ls.startswith('#') or ls.startswith('>') or ls == '':
                    continue
                plon, plat = [float(x) for x in ls.split()]
                points.append((plat, plon))

        points = num.array(points, dtype=num.float)
        return num.any(points_in_region(points, self._wesn))

    def have_coastlines(self):
        self.gmt
        return self._have_coastlines()

    def project(self, lats, lons, jr=None):
        onepoint = False
        if isinstance(lats, float) and isinstance(lons, float):
            lats = [lats]
            lons = [lons]
            onepoint = True

        if jr is not None:
            j, r = jr
            gmt = gmtpy.GMT(version=self._gmtversion)
        else:
            j, _, _, r = self.jxyr
            gmt = self.gmt

        f = BytesIO()
        gmt.mapproject(j, r, in_columns=(lons, lats), out_stream=f, D='p')
        f.seek(0)
        data = num.loadtxt(f, ndmin=2)
        xs, ys = data.T
        if onepoint:
            xs = xs[0]
            ys = ys[0]
        return xs, ys

    def _map_box(self, jr=None):
        ll_lon, ll_lat, ur_lon, ur_lat = self._corners

        xs_corner, ys_corner = self.project(
            (ll_lat, ur_lat), (ll_lon, ur_lon), jr=jr)

        w = xs_corner[1] - xs_corner[0]
        h = ys_corner[1] - ys_corner[0]

        return w, h

    def _map_aspect(self, jr=None):
        w, h = self._map_box(jr=jr)
        return h/w

    def _draw_labels(self):
        points_taken = []
        regions_taken = []

        def no_points_in_rect(xs, ys, xmin, ymin, xmax, ymax):
            xx = not num.any(la(la(xmin < xs, xs < xmax),
                                la(ymin < ys, ys < ymax)))
            return xx

        def roverlaps(a, b):
            return (a[0] < b[2] and b[0] < a[2] and
                    a[1] < b[3] and b[1] < a[3])

        w, h = self._map_box()

        label_font_size = self.gmt.label_font_size()

        if self._labels:

            n = len(self._labels)

            lons, lats, texts, sx, sy, colors, fonts, font_sizes, styles = \
                list(zip(*self._labels))

            font_sizes = [
                (font_size or label_font_size) for font_size in font_sizes]

            sx = num.array(sx, dtype=num.float)
            sy = num.array(sy, dtype=num.float)

            xs, ys = self.project(lats, lons)

            points_taken.append((xs, ys))

            dxs = num.zeros(n)
            dys = num.zeros(n)

            for i in range(n):
                dx, dy = gmtpy.text_box(
                    texts[i],
                    font=fonts[i],
                    font_size=font_sizes[i],
                    **styles[i])

                dxs[i] = dx
                dys[i] = dy

            la = num.logical_and
            anchors_ok = (
                la(xs + sx + dxs < w, ys + sy + dys < h),
                la(xs - sx - dxs > 0., ys - sy - dys > 0.),
                la(xs + sx + dxs < w, ys - sy - dys > 0.),
                la(xs - sx - dxs > 0., ys + sy + dys < h),
            )

            arects = [
                (xs, ys, xs + sx + dxs, ys + sy + dys),
                (xs - sx - dxs, ys - sy - dys, xs, ys),
                (xs, ys - sy - dys, xs + sx + dxs, ys),
                (xs - sx - dxs, ys, xs, ys + sy + dys)]

            for i in range(n):
                for ianch in range(4):
                    anchors_ok[ianch][i] &= no_points_in_rect(
                        xs, ys, *[xxx[i] for xxx in arects[ianch]])

            anchor_choices = []
            anchor_take = []
            for i in range(n):
                choices = [ianch for ianch in range(4)
                           if anchors_ok[ianch][i]]
                anchor_choices.append(choices)
                if choices:
                    anchor_take.append(choices[0])
                else:
                    anchor_take.append(None)

            def cost(anchor_take):
                noverlaps = 0
                for i in range(n):
                    for j in range(n):
                        if i != j:
                            i_take = anchor_take[i]
                            j_take = anchor_take[j]
                            if i_take is None or j_take is None:
                                continue
                            r_i = [xxx[i] for xxx in arects[i_take]]
                            r_j = [xxx[j] for xxx in arects[j_take]]
                            if roverlaps(r_i, r_j):
                                noverlaps += 1

                return noverlaps

            cur_cost = cost(anchor_take)
            imax = 30
            while cur_cost != 0 and imax > 0:
                for i in range(n):
                    for t in anchor_choices[i]:
                        anchor_take_new = list(anchor_take)
                        anchor_take_new[i] = t
                        new_cost = cost(anchor_take_new)
                        if new_cost < cur_cost:
                            anchor_take = anchor_take_new
                            cur_cost = new_cost

                imax -= 1

            while cur_cost != 0:
                for i in range(n):
                    anchor_take_new = list(anchor_take)
                    anchor_take_new[i] = None
                    new_cost = cost(anchor_take_new)
                    if new_cost < cur_cost:
                        anchor_take = anchor_take_new
                        cur_cost = new_cost
                        break

            anchor_strs = ['BL', 'TR', 'TL', 'BR']

            for i in range(n):
                ianchor = anchor_take[i]
                color = colors[i]
                if color is None:
                    color = 'black'

                if ianchor is not None:
                    regions_taken.append([xxx[i] for xxx in arects[ianchor]])

                    anchor = anchor_strs[ianchor]

                    yoff = [-sy[i], sy[i]][anchor[0] == 'B']
                    xoff = [-sx[i], sx[i]][anchor[1] == 'L']
                    if self.gmt.is_gmt5():
                        row = (
                            lons[i], lats[i],
                            '%i,%s,%s' % (font_sizes[i], fonts[i], color),
                            anchor,
                            texts[i])

                        farg = ['-F+f+j']
                    else:
                        row = (
                            lons[i], lats[i],
                            font_sizes[i], 0, fonts[i], anchor,
                            texts[i])
                        farg = ['-G%s' % color]

                    self.gmt.pstext(
                        in_rows=[row],
                        D='%gp/%gp' % (xoff, yoff),
                        *(self.jxyr + farg),
                        **styles[i])

        if self._area_labels:

            for lons, lats, text, color, font, font_size, style in \
                    self._area_labels:

                if font_size is None:
                    font_size = label_font_size

                if color is None:
                    color = 'black'

                if self.gmt.is_gmt5():
                    farg = ['-F+f+j']
                else:
                    farg = ['-G%s' % color]

                xs, ys = self.project(lats, lons)
                dx, dy = gmtpy.text_box(
                    text, font=font, font_size=font_size, **style)

                rects = [xs-0.5*dx, ys-0.5*dy, xs+0.5*dx, ys+0.5*dy]

                locs_ok = num.ones(xs.size, dtype=num.bool)

                for iloc in range(xs.size):
                    rcandi = [xxx[iloc] for xxx in rects]

                    locs_ok[iloc] = True
                    locs_ok[iloc] &= (
                        0 < rcandi[0] and rcandi[2] < w
                        and 0 < rcandi[1] and rcandi[3] < h)

                    overlap = False
                    for r in regions_taken:
                        if roverlaps(r, rcandi):
                            overlap = True
                            break

                    locs_ok[iloc] &= not overlap

                    for xs_taken, ys_taken in points_taken:
                        locs_ok[iloc] &= no_points_in_rect(
                            xs_taken, ys_taken, *rcandi)

                        if not locs_ok[iloc]:
                            break

                rows = []
                for iloc, (lon, lat) in enumerate(zip(lons, lats)):
                    if not locs_ok[iloc]:
                        continue

                    if self.gmt.is_gmt5():
                        row = (
                            lon, lat,
                            '%i,%s,%s' % (font_size, font, color),
                            'MC',
                            text)

                    else:
                        row = (
                            lon, lat,
                            font_size, 0, font, 'MC',
                            text)

                    rows.append(row)

                    regions_taken.append([xxx[iloc] for xxx in rects])
                    break

                self.gmt.pstext(
                    in_rows=rows,
                    *(self.jxyr + farg),
                    **style)

    def draw_labels(self):
        self.gmt
        if not self._have_drawn_labels:
            self._draw_labels()
            self._have_drawn_labels = True

    def add_label(
            self, lat, lon, text,
            offset_x=5., offset_y=5.,
            color=None,
            font='1',
            font_size=None,
            style={}):

        if 'G' in style:
            style = style.copy()
            color = style.pop('G')

        self._labels.append(
            (lon, lat, text, offset_x, offset_y, color, font, font_size,
             style))

    def add_area_label(
            self, lat, lon, text,
            color=None,
            font='3',
            font_size=None,
            style={}):

        self._area_labels.append(
            (lon, lat, text, color, font, font_size, style))

    def cities_in_region(self):
        from pyrocko.dataset import geonames
        cities = geonames.get_cities_region(region=self.wesn, minpop=0)
        cities.extend(self.custom_cities)
        cities.sort(key=lambda x: x.population)
        return cities

    def draw_cities(self,
                    exact=None,
                    include=[],
                    exclude=[],
                    nmax_soft=10,
                    psxy_style=dict(S='s5p', G='black')):

        cities = self.cities_in_region()

        if exact is not None:
            cities = [c for c in cities if c.name in exact]
            minpop = None
        else:
            cities = [c for c in cities if c.name not in exclude]
            minpop = 10**3
            for minpop_new in [1e3, 3e3, 1e4, 3e4, 1e5, 3e5, 1e6, 3e6, 1e7]:
                cities_new = [
                    c for c in cities
                    if c.population > minpop_new or c.name in include]

                if len(cities_new) == 0 or (
                        len(cities_new) < 3 and len(cities) < nmax_soft*2):
                    break

                cities = cities_new
                minpop = minpop_new
                if len(cities) <= nmax_soft:
                    break

        if cities:
            lats = [c.lat for c in cities]
            lons = [c.lon for c in cities]

            self.gmt.psxy(
                in_columns=(lons, lats),
                *self.jxyr, **psxy_style)

            for c in cities:
                try:
                    text = c.name.encode('iso-8859-1').decode('iso-8859-1')
                except UnicodeEncodeError:
                    text = c.asciiname

                self.add_label(c.lat, c.lon, text)

        self._cities_minpop = minpop

    def add_stations(self, stations, psxy_style=dict()):

        default_psxy_style = {
            'S': 't8p',
            'G': 'black'
        }
        default_psxy_style.update(psxy_style)

        lats, lons = zip(*[od.ne_to_latlon(
                                s.lat, s.lon, s.north_shift, s.east_shift)
                           for s in stations])

        self.gmt.psxy(
            in_columns=(lons, lats),
            *self.jxyr, **default_psxy_style)

        for station in stations:
            self.add_label(station.lat, station.lon, '.'.join(
                x for x in (station.network, station.station) if x))

    def add_kite_scene(self, scene):
        tile = FloatTile(
            scene.frame.llLon,
            scene.frame.llLat,
            scene.frame.dLon,
            scene.frame.dLat,
            scene.displacement)

        return tile

    def add_gnss_campaign(self, campaign, psxy_style=None, offset_scale=None,
                          labels=True, vertical=False, fontsize=10):

        stations = campaign.stations

        if offset_scale is None:
            offset_scale = num.zeros(campaign.nstations)
            for ista, sta in enumerate(stations):
                for comp in sta.components.values():
                    offset_scale[ista] += comp.shift
            offset_scale = num.sqrt(offset_scale**2).max()

        size = math.sqrt(self.height**2 + self.width**2)
        scale = (size/10.) / offset_scale
        logger.debug('GNSS: Using offset scale %f, map scale %f',
                     offset_scale, scale)

        lats, lons = zip(
            *[od.ne_to_latlon(s.lat, s.lon, s.north_shift, s.east_shift)
              for s in stations])

        if vertical:
            rows = [[lons[ista], lats[ista],
                    0., s.up.shift,
                    (s.east.sigma + s.north.sigma) if s.east.sigma else 0.,
                    s.up.sigma, 0.,
                    s.code if labels else None]
                    for ista, s in enumerate(stations)
                    if s.up is not None]

        else:
            rows = [[lons[ista], lats[ista],
                     s.east.shift, s.north.shift,
                     s.east.sigma, s.north.sigma, s.correlation_ne,
                     s.code if labels else None]
                    for ista, s in enumerate(stations)
                    if s.east is not None or s.north is not None]

        default_psxy_style = {
            'h': 0,
            'W': '1.5p,black',
            'G': 'black',
            'L': True,
            'S': 'e%dc/0.95/%d' % (scale, fontsize),
        }

        if not labels:
            for row in rows:
                row.pop(-1)

        if psxy_style is not None:
            default_psxy_style.update(psxy_style)

        self.gmt.psvelo(
            in_rows=rows,
            *self.jxyr,
            **default_psxy_style)

    def draw_plates(self):
        from pyrocko.dataset import tectonics

        neast = 20
        nnorth = max(1, int(round(num.round(self._hreg/self._wreg * neast))))
        norths = num.linspace(-self._hreg*0.5, self._hreg*0.5, nnorth)
        easts = num.linspace(-self._wreg*0.5, self._wreg*0.5, neast)
        norths2 = num.repeat(norths, neast)
        easts2 = num.tile(easts, nnorth)
        lats, lons = od.ne_to_latlon(
            self.lat, self.lon, norths2, easts2)

        bird = tectonics.PeterBird2003()
        plates = bird.get_plates()

        color_plates = gmtpy.color('aluminium5')
        color_velocities = gmtpy.color('skyblue1')
        color_velocities_lab = gmtpy.color(darken(gmtpy.color_tup('skyblue1')))

        points = num.vstack((lats, lons)).T
        used = []
        for plate in plates:
            mask = plate.contains_points(points)
            if num.any(mask):
                used.append((plate, mask))

        if len(used) > 1:

            candi_fixed = {}

            label_data = []
            for plate, mask in used:

                mean_north = num.mean(norths2[mask])
                mean_east = num.mean(easts2[mask])
                iorder = num.argsort(num.sqrt(
                        (norths2[mask] - mean_north)**2 +
                        (easts2[mask] - mean_east)**2))

                lat_candis = lats[mask][iorder]
                lon_candis = lons[mask][iorder]

                candi_fixed[plate.name] = lat_candis.size

                label_data.append((
                    lat_candis, lon_candis, plate, color_plates))

            boundaries = bird.get_boundaries()

            size = 2

            psxy_kwargs = []

            for boundary in boundaries:
                if num.any(points_in_region(boundary.points, self._wesn)):
                    for typ, part in boundary.split_types(
                            [['SUB'],
                             ['OSR', 'OTF', 'OCB', 'CTF', 'CCB', 'CRB']]):

                        lats, lons = part.T

                        kwargs = {}
                        if typ[0] == 'SUB':
                            if boundary.kind == '\\':
                                kwargs['S'] = 'f%g/%gp+t+r' % (
                                    0.45*size, 3.*size)
                            elif boundary.kind == '/':
                                kwargs['S'] = 'f%g/%gp+t+l' % (
                                    0.45*size, 3.*size)

                            kwargs['G'] = color_plates

                        kwargs['in_columns'] = (lons, lats)
                        kwargs['W'] = '%gp,%s' % (size, color_plates),

                        psxy_kwargs.append(kwargs)

                        if boundary.kind == '\\':
                            if boundary.name2 in candi_fixed:
                                candi_fixed[boundary.name2] += neast*nnorth

                        elif boundary.kind == '/':
                            if boundary.name1 in candi_fixed:
                                candi_fixed[boundary.name1] += neast*nnorth

            candi_fixed = [name for name in sorted(
                list(candi_fixed.keys()), key=lambda name: -candi_fixed[name])]

            candi_fixed.append(None)

            gsrm = tectonics.GSRM1()

            for name in candi_fixed:
                if name not in gsrm.plate_names() \
                        and name not in gsrm.plate_alt_names():

                    continue

                lats, lons, vnorth, veast, vnorth_err, veast_err, corr = \
                    gsrm.get_velocities(name, region=self._wesn)

                fixed_plate_name = name

                self.gmt.psvelo(
                    in_columns=(
                        lons, lats, veast, vnorth, veast_err, vnorth_err,
                        corr),
                    W='0.25p,%s' % color_velocities,
                    A='9p+e+g%s' % color_velocities,
                    S='e0.2p/0.95/10',
                    *self.jxyr)

                for _ in range(len(lons) // 50 + 1):
                    ii = random.randint(0, len(lons)-1)
                    v = math.sqrt(vnorth[ii]**2 + veast[ii]**2)
                    self.add_label(
                        lats[ii], lons[ii], '%.0f' % v,
                        font_size=0.7*self.gmt.label_font_size(),
                        style=dict(
                            G=color_velocities_lab))

                break

            for (lat_candis, lon_candis, plate, color) in label_data:
                full_name = bird.full_name(plate.name)
                if plate.name == fixed_plate_name:
                    full_name = '@_' + full_name + '@_'

                self.add_area_label(
                    lat_candis, lon_candis,
                    full_name,
                    color=color,
                    font='3')

            for kwargs in psxy_kwargs:
                self.gmt.psxy(*self.jxyr, **kwargs)
Exemple #30
0
class SeismosizerData(DataGenerator):
    fn_sources = String.T(
        help='filename containing pyrocko.gf.seismosizer.Source instances')
    fn_targets = String.T(
        help='filename containing pyrocko.gf.seismosizer.Target instances',
        optional=True)
    fn_stations = String.T(
        help='filename containing pyrocko.model.Station instances. Will be\
                converted to Target instances',
        optional=True)

    store_id = String.T(optional=True)
    center_sources = Bool.T(
        default=False,
        help='Transform the center of sources to the center of stations')

    engine = LocalEngine.T()
    onset_phase = String.T(default='first(p|P)')

    def setup(self):
        self.sources = guts.load(filename=self.fn_sources)
        self.targets = []

        if self.fn_targets:
            self.targets.extend(guts.load(filename=self.fn_targets))

        if self.fn_stations:
            stats = load_stations(self.fn_stations)
            self.targets.extend(self.cast_stations_to_targets(stats))

        if self.store_id:
            for t in self.targets:
                t.store_id = self.store_id

        if self.center_sources:
            self.move_sources_to_station_center()

        self.config.channels = [t.codes for t in self.targets]
        store_ids = [t.store_id for t in self.targets]
        store_id = set(store_ids)
        assert len(store_id) == 1, 'More than one store used. Not \
                implemented yet'

        self.store = self.engine.get_store(store_id.pop())

        self.sources = filter_oob(self.sources, self.targets,
                                  self.store.config)
        print('xxx', len(self.sources))

        dt = self.config.deltat_want or self.store.config.deltat
        self.n_samples = int(
            (self.config.sample_length + self.config.tpad) / dt)

    def move_sources_to_station_center(self):
        '''Transform the center of sources to the center of stations.'''
        lat, lon = orthodrome.geographic_midpoint_locations(self.targets)
        for s in self.sources:
            s.lat = lat
            s.lon = lon

    def cast_stations_to_targets(self, stations):
        targets = []
        channels = 'ENZ'
        for s in stations:
            targets.extend([
                Target(
                    codes=(s.network, s.station, s.location, c),
                    lat=s.lat,
                    lon=s.lon,
                    elevation=s.elevation,
                ) for c in channels
            ])

        return targets

    def extract_labels(self, source):
        if not self.labeled:
            return UNLABELED
        return (source.north_shift, source.east_shift, source.depth)

    def iter_examples_and_labels(self):
        ensure_list(self.sources)
        ensure_list(self.targets)

        response = self.engine.process(sources=self.sources,
                                       targets=self.targets)

        for isource, source in enumerate(response.request.sources):
            traces = [x.trace.pyrocko_trace() for x in \
                    response.results_list[isource]]

            for tr in traces:
                self.preprocess(tr)
            arrivals = [
                self.store.t(self.onset_phase,
                             (source.depth, source.distance_to(t)))
                for t in self.targets
            ]
            tref = min([a for a in arrivals if a is not None])
            chunk = self.get_raw_data_chunk(self.tensor_shape)

            self.fit_data_into_chunk(traces,
                                     chunk=chunk,
                                     tref=tref + source.time)

            label = self.extract_labels(source)

            yield chunk, label