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
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
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)
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
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
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]]
class OkadaSegment(OkadaSource): enabled = Bool.T(default=True, optional=True)
class ReportInfo(Object): title = Unicode.T(optional=True) description = Unicode.T(optional=True) version_info = info.VersionInfo.T() have_archive = Bool.T(optional=True)
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)
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())
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
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
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'))
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
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
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
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.')
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]]
class PolygonMaskConfig(PluginConfig): polygons = Dict.T(optional=True, default={}) applied = Bool.T(default=True)
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))
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]
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)
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
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)
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
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)
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