class NoiseAnalyserConfig(AnalyserConfig): """Configuration parameters for the pre-event noise analysis.""" nwindows = Int.T(default=1, help='number of windows for trace splitting') pre_event_noise_duration = Float.T( default=0., help='Total length of noise trace in the analysis') phase_def = String.T( default='P', help='Onset of phase_def used for upper limit of window') check_events = Bool.T(default=False, help='check the GlobalCMT for M>5 earthquakes' ' that produce phase arrivals' ' contaminating and affecting the noise analysis') statistic = StringChoice.T( choices=('var', 'std'), default='var', help='Set weight to inverse of noise variance (var) or standard ' 'deviation (std).') mode = StringChoice.T( choices=('weighting', 'weeding'), default='weighting', help='Generate weights based on inverse of noise measure (weighting), ' 'or discrete on/off style in combination with cutoff value ' '(weeding).') cutoff = Float.T( optional=True, help='Set weight to zero, when noise level exceeds median by the ' 'given cutoff factor.') cutoff_exception_on_high_snr = Float.T( optional=True, help='Exclude from cutoff when max amplitude exceeds standard ' 'deviation times this factor.') def get_analyser(self): return NoiseAnalyser( nwindows=self.nwindows, pre_event_noise_duration=self.pre_event_noise_duration, check_events=self.check_events, phase_def=self.phase_def, statistic=self.statistic, mode=self.mode, cutoff=self.cutoff, cutoff_exception_on_high_snr=self.cutoff_exception_on_high_snr)
class GeodeticConfig(Object): """ Config for geodetic data optimization related parameters. """ datadir = String.T(default='./') names = List.T(String.T(), default=['Data prefix filenames here ...']) blacklist = List.T(String.T(), optional=True, default=['placeholder'], help='Station name for station to be thrown out.') types = List.T(String.T(), default=['SAR'], help='Types of geodetic data, i.e. SAR, GPS, ...') calc_data_cov = Bool.T( default=True, help='Flag for calculating the data covariance matrix based on the' ' pre P arrival data trace noise.') interpolation = StringChoice.T( choices=['nearest_neighbor', 'multilinear'], default='multilinear', help='GF interpolation scheme during synthetics generation') fit_plane = Bool.T( default=False, help='Flag for inverting for additional plane parameters on each' ' SAR datatype') gf_config = GFConfig.T(default=GeodeticGFConfig.D()) def get_hypernames(self): return ['_'.join(('h', typ)) for typ in self.types]
class GNSSComponent(Object): ''' Component of a GNSSStation ''' unit = StringChoice.T(choices=['mm', 'cm', 'm'], help='Unit of displacement', default='m') shift = Float.T(default=0., help='Component\'s shift in unit') sigma = Float.T(default=0., help='One sigma uncertainty of the measurement') def __add__(self, other): if not isinstance(other, self.__class__): raise AttributeError('Other has to be of instance %s' % self.__class__) comp = self.__class__() comp.shift = self.shift + other.shift comp.sigma = math.sqrt(self.sigma**2 + other.sigma**2) return comp def __iadd__(self, other): self.shift += other.shift self.sigma = math.sqrt(self.sigma**2 + other.sigma**2) return self
class CMTProblemConfig(ProblemConfig): ranges = Dict.T(String.T(), gf.Range.T()) distance_min = Float.T(default=0.0) mt_type = StringChoice.T(choices=['full', 'deviatoric']) def get_problem(self, event, target_groups, targets): if event.depth is None: event.depth = 0. base_source = gf.MTSource.from_pyrocko_event(event) base_source.stf = gf.HalfSinusoidSTF(duration=event.duration or 0.0) subs = dict(event_name=event.name, event_time=util.time_to_str(event.time)) problem = CMTProblem(name=expand_template(self.name_template, subs), base_source=base_source, target_groups=target_groups, targets=targets, ranges=self.ranges, distance_min=self.distance_min, mt_type=self.mt_type, norm_exponent=self.norm_exponent) return problem
class SeismicNoiseAnalyserConfig(Object): structure = StringChoice.T( choices=['identity', 'exponential', 'import', 'non-toeplitz'], default='identity', help='Determines data-covariance matrix structure.') pre_arrival_time = Float.T( default=5., help='Time [s] before synthetic P-wave arrival until ' 'variance is estimated')
class WaveformFitConfig(Object): """ Config for specific parameters that are applied to post-process a specific type of waveform and calculate the misfit. """ include = Bool.T(default=True, help='Flag to include waveform into optimization.') name = String.T('any_P') channels = List.T(String.T(), default=['Z']) filterer = Filter.T(default=Filter.D()) distances = Tuple.T(2, Float.T(), default=(30., 90.)) interpolation = StringChoice.T(choices=['nearest_neighbor', 'multilinear'], default='multilinear', help='GF interpolation sceme') arrival_taper = trace.Taper.T( default=ArrivalTaper.D(), help='Taper a,b/c,d time [s] before/after wave arrival')
class Meta(Object): """Meta configuration for ``Scene``.""" scene_title = String.T(default="Unnamed Scene", help="Scene title") scene_id = String.T(default="None", help="Scene identification") satellite_name = String.T( default="Undefined Mission", help="Satellite mission name" ) wavelength = Float.T(optional=True, help="Wavelength in [m]") orbital_node = StringChoice.T( choices=["Ascending", "Descending", "Undefined"], default="Undefined", help="Orbital direction, ascending/descending", ) time_master = Timestamp.T( default=1481116161.930574, help="Timestamp for master acquisition" ) time_slave = Timestamp.T( default=1482239325.482, help="Timestamp for slave acquisition" ) extra = Dict.T(default={}, help="Extra header information") filename = String.T(optional=True) def __init__(self, *args, **kwargs): self.old_import = False mapping = {"orbit_direction": "orbital_node"} for old, new in mapping.items(): if old in kwargs.keys(): kwargs[new] = kwargs.pop(old, None) self.old_import = True Object.__init__(self, *args, **kwargs) @property def time_separation(self): """ :getter: Absolute time difference between ``time_master`` and ``time_slave`` :type: timedelta """ return dt.fromtimestamp(self.time_slave) - dt.fromtimestamp(self.time_master)
class FrameConfig(Object): """Config object holding :class:`kite.scene.Scene` configuration""" llLat = Float.T(default=0.0, help="Scene latitude of lower left corner") llLon = Float.T(default=0.0, help="Scene longitude of lower left corner") dN = Float.T(default=25.0, help="Scene pixel spacing in north, give [m] or [deg]") dE = Float.T(default=25.0, help="Scene pixel spacing in east, give [m] or [deg]") spacing = StringChoice.T( choices=("degree", "meter"), default="meter", help="Unit of pixel space" ) def __init__(self, *args, **kwargs): self.old_import = False mapping = {"dE": "dLon", "dN": "dLat"} for new, old in mapping.items(): if old in kwargs: kwargs[new] = kwargs.pop(old) kwargs["spacing"] = "degree" self.old_import = True Object.__init__(self, *args, **kwargs)
class FrameConfig(Object): """Config object holding :class:`kite.scene.Scene` configuration """ llLat = Float.T(default=0., help='Scene latitude of lower left corner') llLon = Float.T(default=0., help='Scene longitude of lower left corner') dN = Float.T(default=25., help='Scene pixel spacing in north, give [m] or [deg]') dE = Float.T(default=25., help='Scene pixel spacing in east, give [m] or [deg]') spacing = StringChoice.T(choices=('degree', 'meter'), default='meter', help='Unit of pixel space') def __init__(self, *args, **kwargs): self.old_import = False mapping = {'dE': 'dLon', 'dN': 'dLat'} for new, old in mapping.items(): if old in kwargs: kwargs[new] = kwargs.pop(old) kwargs['spacing'] = 'degree' self.old_import = True Object.__init__(self, *args, **kwargs)
class GeodeticConfig(Object): """ Config for geodetic data optimization related parameters. """ datadir = String.T(default='./') names = List.T(String.T(), default=['Data prefix filenames here ...']) blacklist = List.T(String.T(), optional=True, default=[], help='GPS station name or scene name to be thrown out.') types = List.T(String.T(), default=['SAR'], help='Types of geodetic data, i.e. SAR, GPS, ...') calc_data_cov = Bool.T( default=True, help='Flag for calculating the data covariance matrix, ' 'outsourced to "kite"') interpolation = StringChoice.T( choices=['nearest_neighbor', 'multilinear'], default='multilinear', help='GF interpolation scheme during synthetics generation') fit_plane = Bool.T( default=False, help='Flag for inverting for additional plane parameters on each' ' SAR datatype') gf_config = GFConfig.T(default=GeodeticGFConfig.D()) def get_hypernames(self): return ['_'.join(('h', typ)) for typ in self.types] def get_hierarchical_names(self): if self.fit_plane: return [name + '_ramp' for name in self.names] else: return []
class GNSSStation(Location): ''' Representation of a GNSS station during a campaign measurement For more information see http://kb.unavco.org/kb/assets/660/UNAVCO_Campaign_GPS_GNSS_Handbook.pdf ''' code = String.T(help='Four letter station code', optional=True) style = StringChoice.T(choices=['static', 'rapid_static', 'kinematic'], default='static') survey_start = DateTimestamp.T(optional=True) survey_end = DateTimestamp.T(optional=True) north = GNSSComponent.T(default=GNSSComponent.D()) east = GNSSComponent.T(default=GNSSComponent.D()) up = GNSSComponent.T(default=GNSSComponent.D()) def __init__(self, *args, **kwargs): Location.__init__(self, *args, **kwargs)
class X1(Object): xmltagname = 'root' m = Union.T(members=[Int.T(), StringChoice.T(['small', 'large'])])
class U(Union): members = [Int.T(), StringChoice.T(['small', 'large'])]
class X(Object): m = StringChoice.T(['a', 'b'])
class X(Object): m = StringChoice.T(['a', 'b'], ignore_case=True)
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 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 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 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() 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, history.get_primary_chain_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 GNSSStation(Location): ''' Representation of a GNSS station during a campaign measurement For more information see http://kb.unavco.org/kb/assets/660/UNAVCO_Campaign_GPS_GNSS_Handbook.pdf ''' code = String.T(help='Four letter station code', optional=True) style = StringChoice.T(choices=['static', 'rapid_static', 'kinematic'], default='static') survey_start = DateTimestamp.T(optional=True, help='Survey start time') survey_end = DateTimestamp.T(optional=True, help='Survey end time') correlation_ne = Float.T(default=0., help='North-East component correlation') correlation_eu = Float.T(default=0., help='East-Up component correlation') correlation_nu = Float.T(default=0., help='North-Up component correlation') north = GNSSComponent.T(optional=True) east = GNSSComponent.T(optional=True) up = GNSSComponent.T(optional=True) def __eq__(self, other): try: return self.code == other.code except AttributeError: return False def get_covariance_matrix(self): components = self.components.values() ncomponents = self.ncomponents covar = num.zeros((ncomponents, ncomponents)) for ic1, comp1 in enumerate(components): for ic2, comp2 in enumerate(components): corr = self._get_comp_correlation(comp1, comp2) covar[ic1, ic2] = corr * comp1.sigma * comp2.sigma # This floating point operation is inaccurate: # corr * comp1.sigma * comp2.sigma != corr * comp2.sigma * comp1.sigma # # Hence this identity covar[num.tril_indices_from(covar, k=-1)] = \ covar[num.triu_indices_from(covar, k=1)] return covar def get_correlation_matrix(self): components = self.components.values() ncomponents = self.ncomponents corr = num.zeros((ncomponents, ncomponents)) corr[num.diag_indices_from(corr)] = num.array( [c.sigma for c in components]) for ic1, comp1 in enumerate(components): for ic2, comp2 in enumerate(components): if comp1 is comp2: continue corr[ic1, ic2] = self._get_comp_correlation(comp1, comp2) # See comment at get_covariance_matrix corr[num.tril_indices_from(corr, k=-1)] = \ corr[num.triu_indices_from(corr, k=1)] return corr def get_displacement_data(self): return num.array([c.shift for c in self.components.values()]) def get_component_mask(self): return num.array([ False if self.__getattribute__(name) is None else True for name in ('north', 'east', 'up') ], dtype=num.bool) @property def components(self): return OrderedDict([(name, self.__getattribute__(name)) for name in ('north', 'east', 'up') if self.__getattribute__(name) is not None]) @property def ncomponents(self): return len(self.components) def _get_comp_correlation(self, comp1, comp2): if comp1 is comp2: return 1. s = self correlation_map = { (s.north, s.east): s.correlation_ne, (s.east, s.up): s.correlation_eu, (s.north, s.up): s.correlation_nu } return correlation_map.get((comp1, comp2), correlation_map.get((comp2, comp1), False))
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']) hyperparameters = Dict.T( help='Hyperparameters to weight different types of datatypes.') priors = Dict.T(help='Priors of the variables in question.') 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): """ 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 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!')
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., 12.)) 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') fit = StringChoice.T( default='best', choices=['best', 'mean'], help='Show the \'best\' or \'mean\' fits and source model from the' ' ensamble.') show_topo = Bool.T( default=True, help='Drape displacements over the topography.') displacement_unit = StringChoice.T( default='m', choices=['m', 'mm', 'cm', 'rad'], help="Show results in 'm', 'cm', 'mm' or 'rad' for radians.") show_leaf_centres = Bool.T( default=True, help='show the center points of Quadtree leaves') source_outline_color = String.T( default='grey', help='Choose color of source outline from named matplotlib Colors') common_color_scale = Bool.T( default=True, help='Results shown with common color scale for all satellite ' 'data sets (based on the data)') map_limits = Tuple.T( 4, Float.T(), optional=True, help='Overwrite map limits in native coordinates. ' 'Use (xmin, xmax, ymin, ymax)') nticks_x = Int.T( optional=True, help='Number of ticks on the x-axis.') 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_mpl( self, self.draw_static_fits(ds, history, optimiser), title=u'InSAR Displacements', section='fits', feather_icon='navigation', description=u''' Maps showing subsampled surface displacements as observed, modelled and the residual (observed minus modelled). The displacement values predicted by the orbit-ambiguity ramps are added to the modelled displacements (middle panels). The color shows the LOS displacement values associated with, and the extent of, every quadtree box. The light grey dots show the focal point of pixels combined in the quadtree box. This point corresponds to the position of the modelled data point. The large dark grey dot shows the reference source position. The grey filled box shows the surface projection of the modelled source, with the thick-lined edge marking the upper fault edge. 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) if self.fit == 'best': source = history.get_best_source() model = history.get_best_model() elif self.fit == 'mean': source = history.get_mean_source() model = history.get_mean_model() results = problem.evaluate(model, targets=sat_targets) def init_axes(ax, scene, title, last_axes=False): ax.set_title(title, fontsize=self.font_size) ax.tick_params(length=2) if scene.frame.isMeter(): import utm ax.set_xlabel('Easting [km]', fontsize=self.font_size) scale_x = dict(scale=1./km) scale_y = dict(scale=1./km) utm_E, utm_N, utm_zone, utm_zone_letter =\ utm.from_latlon(source.effective_lat, source.effective_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) ax.set_aspect('equal') elif scene.frame.isDegree(): scale_x = dict(scale=1., suffix='°') scale_y = dict(scale=1., suffix='°') scale_x['offset'] = source.effective_lon scale_y['offset'] = source.effective_lat ax.set_aspect(1./num.cos(source.effective_lat*d2r)) if self.relative_coordinates: scale_x['offset'] = 0. scale_y['offset'] = 0. nticks_x = 4 if abs(scene.frame.llLon) >= 100 else 5 ax.xaxis.set_major_locator(MaxNLocator(self.nticks_x or nticks_x)) ax.yaxis.set_major_locator(MaxNLocator(5)) ax.scale_x = scale_x ax.scale_y = scale_y scale_axes(ax.get_xaxis(), **scale_x) scale_axes(ax.get_yaxis(), **scale_y) def draw_source(ax, scene): if scene.frame.isMeter(): fn, fe = source.outline(cs='xy').T fn -= fn.mean() fe -= fe.mean() elif scene.frame.isDegree(): fn, fe = source.outline(cs='latlon').T fn -= source.effective_lat fe -= source.effective_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=self.source_outline_color, alpha=0.7) ax.plot(fe[0:2], fn[0:2], 'k', linewidth=1.3) def get_displacement_rgba(displacements, scene, mappable): arr = num.full_like(scene.displacement, fill_value=num.nan) qt = scene.quadtree for syn_v, leaf in zip(displacements, qt.leaves): arr[leaf._slice_rows, leaf._slice_cols] = syn_v arr[scene.displacement_mask] = num.nan if not self.common_color_scale \ and not self.displacement_unit == 'rad': abs_displ = num.abs(displacements).max() mappable.set_clim(-abs_displ, abs_displ) if self.show_topo: try: elevation = scene.get_elevation() return drape_displacements(arr, elevation, mappable) except Exception as e: logger.warning('could not plot hillshaded topo') logger.exception(e) return mappable.to_rgba(arr) def draw_leaves(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) if self.show_leaf_centres: ax.scatter(scene.quadtree.leaf_coordinates[:, 0] - offset_e, scene.quadtree.leaf_coordinates[:, 1] - offset_n, s=.25, c='black', alpha=.1) def add_arrow(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.effective_lat, source.effective_lon)) if target.scene.frame.isDegree(): off_n = source.effective_lat - target.scene.frame.llLat off_e = source.effective_lon - target.scene.frame.llLon turE, turN, tllE, tllN = zip( *[(leaf.gridE.max()-off_e, leaf.gridN.max()-off_n, leaf.gridE.min()-off_e, leaf.gridN.min()-off_n) for leaf 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=.15, 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. (Left) the input data, (center) the modelled data and (right) the model residual. ''') stat_obs = result.statics_obs['displacement.los'] 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.effective_lat, source.effective_lon)) elif scene.frame.isDegree(): offset_n = source.effective_lat - scene.frame.llLat offset_e = source.effective_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) if self.displacement_unit == 'rad': wavelength = scene.meta.wavelength if wavelength is None: raise AttributeError( 'The satellite\'s wavelength is not set') stat_obs = displ2rad(stat_obs, wavelength) stat_syn = displ2rad(stat_syn, wavelength) res = displ2rad(res, wavelength) self.colormap = 'hsv' data_range = (0., num.pi) else: abs_displ = num.abs([stat_obs.min(), stat_obs.max(), stat_syn.min(), stat_syn.max(), res.min(), res.max()]).max() data_range = (-abs_displ, abs_displ) cmw = cm.ScalarMappable(cmap=self.colormap) cmw.set_clim(*data_range) 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( get_displacement_rgba(stat_obs, scene, cmw), extent=im_extent, origin='lower') draw_leaves(ax, scene, offset_e, offset_n) draw_source(ax, scene) add_arrow(ax, scene) init_axes(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.isMeter(): ax.set_ylabel('Northing [km]', fontsize=self.font_size) ax = axes[1] ax.imshow( get_displacement_rgba(stat_syn, scene, cmw), extent=im_extent, origin='lower') draw_leaves(ax, scene, offset_e, offset_n) draw_source(ax, scene) add_arrow(ax, scene) init_axes(ax, scene, 'Model') ax.get_yaxis().set_visible(False) ax = axes[2] ax.imshow( get_displacement_rgba(res, scene, cmw), extent=im_extent, origin='lower') draw_leaves(ax, scene, offset_e, offset_n) draw_source(ax, scene) add_arrow(ax, scene) init_axes(ax, scene, 'Residual', last_axes=True) ax.get_yaxis().set_visible(False) for ax in axes: ax.set_xlim(*im_extent[:2]) ax.set_ylim(*im_extent[2:]) 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.effective_lat fe -= source.effective_lon if fn.size > 1: off_n = (fn[0] + fn[1]) / 2 off_e = (fe[0] + fe[1]) / 2 else: off_n = fn[0] off_e = fe[0] fault_size = 2*num.sqrt(max(abs(fn-off_n))**2 + max(abs(fe-off_e))**2) fault_size *= self.map_scale if fault_size == 0.0: extent = (scene.frame.N[-1] + scene.frame.E[-1]) / 2 fault_size = extent * .25 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) if self.map_limits is not None: xmin, xmax, ymin, ymax = self.map_limits assert xmin < xmax, 'bad map_limits xmin > xmax' assert ymin < ymax, 'bad map_limits ymin > ymax' for ax in axes: ax.set_xlim( xmin/ax.scale_x['scale'] - ax.scale_x['offset'], xmax/ax.scale_x['scale'] - ax.scale_x['offset'],) ax.set_ylim( ymin/ax.scale_y['scale'] - ax.scale_y['offset'], ymax/ax.scale_y['scale'] - ax.scale_y['offset']) if self.displacement_unit == 'm': def cfmt(x, p): return '%.2f' % x elif self.displacement_unit == 'cm': def cfmt(x, p): return '%.1f' % (x * 1e2) elif self.displacement_unit == 'mm': def cfmt(x, p): return '%.0f' % (x * 1e3) elif self.displacement_unit == 'rad': def cfmt(x, p): return '%.2f' % x else: raise AttributeError( 'unknown displacement unit %s' % self.displacement_unit) cbar_args = dict( orientation='horizontal', format=FuncFormatter(cfmt), use_gridspec=True) cbar_label = 'LOS Displacement [%s]' % self.displacement_unit if self.common_color_scale: cax = fig.add_subplot(gs[1, 1]) cax.set_aspect(.05) cbar = fig.colorbar(cmw, cax=cax, **cbar_args) cbar.set_label(cbar_label) else: for idata, data in enumerate((stat_syn, stat_obs, res)): cax = fig.add_subplot(gs[1, idata]) cax.set_aspect(.05) if not self.displacement_unit == 'rad': abs_displ = num.abs(data).max() cmw.set_clim(-abs_displ, abs_displ) cbar = fig.colorbar(cmw, cax=cax, **cbar_args) cbar.set_label(cbar_label) return (item, fig) for ifig, (sat_target, result) in enumerate(zip(sat_targets, results)): yield generate_plot(sat_target, result, ifig)
class TemplateMatchingIFC(IFC): template_event_path = common.Path.T( help='Event parameters of the template') template_markers_path = common.Path.T( optional=False, help='File with markers defining the template') sum_square = Bool.T( default=False, help='Sum square of correlation') normalization = StringChoice.T( default='gliding', choices=['off', 'normal', 'gliding']) downsample_rate = Float.T( optional=True, help='If set, downsample to this sampling rate before processing [Hz]') use_fft = Bool.T( optional=True, default=False, help='If set, correlate traces in the spectral domain') def get_tpad(self): tmin_masters = min(tr.tmin for tr in self.masters.values()) tmax_masters = max(tr.tmax for tr in self.masters.values()) tmaster = tmax_masters - tmin_masters return tmaster def get_template_origin(self): event = model.load_one_event(self.expand_path( self.template_event_path)) origin = geo.Point( lat=event.lat, lon=event.lon, z=event.depth) return origin def get_table(self, grid, receivers): origin = self.get_template_origin() return self.shifter.get_offset_table(grid, receivers, origin) def extract_template(self, p): markers = gui_util.load_markers(self.expand_path( self.template_markers_path)) def trace_selector_global(tr): return True period_highpass = 1./self.fmin tpad = 2 * period_highpass master_traces = [] for marker in markers: if marker.tmin == marker.tmax: logger.warn('tmin == tmax in template marker %s' % marker) if not marker.nslc_ids: trace_selector = trace_selector_global else: def trace_selector(tr): return ( marker.match_nslc(tr.nslc_id) and trace_selector_global(tr)) master_traces.extend(p.all( tmin=marker.tmin, tmax=marker.tmax, trace_selector=trace_selector, tpad=tpad)) masters = {} for xtr in master_traces: tr = xtr.copy() if self.downsample_rate is not None: downsample(tr, 1./self.downsample_rate) tr.highpass(4, self.fmin, demean=False) tr.lowpass(4, self.fmax, demean=False) smin = round(xtr.wmin / tr.deltat) * tr.deltat smax = round(xtr.wmax / tr.deltat) * tr.deltat tr.chop(smin, smax) if tr.nslc_id in masters: raise common.LassieError( 'more than one waveform selected on trace with id "%s"' % '.'.join(tr.nslc_id)) masters[tr.nslc_id] = tr return masters def prescan(self, p): self.masters = self.extract_template(p) def deltat_cf_is_available(self, deltat_cf): return False def preprocess(self, trs, wmin, wmax, tpad_new, deltat_cf): tmin_masters = min(tr.tmin for tr in self.masters.values()) tmax_masters = max(tr.tmax for tr in self.masters.values()) tmaster = tmax_masters - tmin_masters tref = tmin_masters nsl_to_traces = defaultdict(list) for orig_b in trs: b = orig_b.copy() nslc = b.nslc_id a = self.masters.get(nslc, False) if not a: continue if self.downsample_rate is not None: downsample(b, 1./self.downsample_rate) b.highpass(4, self.fmin, demean=False) b.lowpass(4, self.fmax, demean=False) smin = round((wmin - tmaster) / b.deltat) * b.deltat smax = round((wmax + tmaster) / b.deltat) * b.deltat b.chop(smin, smax) normalization = self.normalization if normalization == 'off': normalization = None c = trace.correlate( a, b, mode='valid', normalization=normalization, use_fft=self.use_fft) c.shift(-c.tmin + b.tmin - (a.tmin - tref)) c.meta = {'tabu': True} if self.sum_square: c.ydata = c.ydata**2 c.chop(wmin - tpad_new, wmax + tpad_new) nsl_to_traces[nslc[:3]].append(c) dataset = [] for nsl, cs in nsl_to_traces.items(): csum = cs[0] for c in cs[1:]: csum.add(c) dataset.append((nsl, csum)) return dataset
class GNSSStation(Location): ''' Representation of a GNSS station during a campaign measurement For more information see http://kb.unavco.org/kb/assets/660/UNAVCO_Campaign_GPS_GNSS_Handbook.pdf ''' code = String.T( help='Four letter station code', optional=True) style = StringChoice.T( choices=['static', 'rapid_static', 'kinematic'], default='static') survey_start = DateTimestamp.T( optional=True, help='Survey start time') survey_end = DateTimestamp.T( optional=True, help='Survey end time') correlation_ne = Float.T( optional=True, help='North-East component correlation') correlation_eu = Float.T( optional=True, help='East-Up component correlation') correlation_nu = Float.T( optional=True, help='North-Up component correlation') north = GNSSComponent.T( default=GNSSComponent.D()) east = GNSSComponent.T( default=GNSSComponent.D()) up = GNSSComponent.T( default=GNSSComponent.D()) def __init__(self, *args, **kwargs): Location.__init__(self, *args, **kwargs) def get_covariance_matrix(self, full=True): s = self covar = num.zeros((3, 3)) covar[num.diag_indices_from(covar)] = num.array( [c.sigma**2 for c in (s.north, s.east, s.up)]) if s.correlation_ne is not None: covar[0, 1] = s.correlation_ne * s.north.sigma * s.east.sigma if s.correlation_nu is not None: covar[0, 2] = s.correlation_nu * s.north.sigma * s.up.sigma if s.correlation_eu is not None: covar[1, 2] = s.correlation_eu * s.east.sigma * s.up.sigma if full: covar[num.tril_indices_from(covar, k=-1)] = \ covar[num.triu_indices_from(covar, k=1)] return covar def get_correlation_matrix(self, full=True): s = self corr = num.zeros((3, 3)) corr[num.diag_indices_from(corr)] = num.array( [c.sigma for c in (s.north, s.east, s.up)]) if s.correlation_ne is not None: corr[0, 1] = s.correlation_ne if s.correlation_nu is not None: corr[0, 2] = s.correlation_nu if s.correlation_eu is not None: corr[1, 2] = s.correlation_eu if full: corr[num.tril_indices_from(corr, k=-1)] = \ corr[num.triu_indices_from(corr, k=1)] return corr
class SequencePlot(PlotConfig): ''' Draws all parameter values evaluated during the optimisation The sequence of all the parameter values is either a function of the optimisation in progress or of the misfit from high to low. This plot can be used to check on convergence or see if model parameters push the given bounds. The color always shows the relative misfit. Relatively high misfits are in cold blue colors and relatively low misfits in red. The last panel gives the corresponding misfit values. ''' name = 'sequence' size_cm = Tuple.T(2, Float.T(), default=(14., 6.)) misfit_cutoff = Float.T(optional=True) ibootstrap = Int.T(optional=True) sort_by = StringChoice.T(choices=['iteration', 'misfit'], default='iteration') subplot_layout = Tuple.T(2, Int.T(), default=(1, 1)) marker_size = Float.T(default=1.5) show_reference = Bool.T(default=True) def make(self, environ): cm = environ.get_plot_collection_manager() history = environ.get_history() optimiser = environ.get_optimiser() mpl_init(fontsize=self.font_size) cm.create_group_mpl(self, self.draw_figures(history, optimiser), title=u'Sequence Plots', section='optimiser', description=u''' Sequence plots for all parameters of the optimisation. The sequence of all the parameter values is either a function of the optimisation in progress or of the misfit from high to low. This plot can be used to check on convergence or to see if model parameters push the given bounds. The color always shows the relative misfit. Relatively high misfits are in cold blue colors and relatively low misfits in red. The last panel gives the corresponding misfit values. ''', feather_icon='fast-forward') def draw_figures(self, history, optimiser): misfit_cutoff = self.misfit_cutoff sort_by = self.sort_by problem = history.problem models = history.models npar = problem.nparameters ndep = problem.ndependants fontsize = self.font_size nfx, nfy = self.subplot_layout imodels = num.arange(history.nmodels) bounds = problem.get_combined_bounds() xref = problem.get_reference_model() gms = history.get_primary_chain_misfits() gms_softclip = num.where(gms > 1.0, 0.2 * num.log10(gms) + 1.0, gms) isort = num.argsort(gms)[::-1] if sort_by == 'iteration': imodels = imodels[isort] elif sort_by == 'misfit': imodels = num.arange(imodels.size) else: assert False gms = gms[isort] gms_softclip = gms_softclip[isort] models = models[isort, :] iorder = num.empty_like(isort) iorder = num.arange(iorder.size) if misfit_cutoff is None: ibest = num.ones(gms.size, dtype=num.bool) else: ibest = gms < misfit_cutoff def config_axes(axes, nfx, nfy, impl, iplot, nplots): if (impl - 1) % nfx != nfx - 1: axes.get_yaxis().tick_left() if (impl - 1) >= (nfx * (nfy - 1)) or iplot >= nplots - nfx: axes.set_xlabel('Iteration') if not (impl - 1) // nfx == 0: axes.get_xaxis().tick_bottom() elif (impl - 1) // nfx == 0: axes.get_xaxis().tick_top() axes.set_xticklabels([]) else: axes.get_xaxis().set_visible(False) # nfz = (npar + ndep + 1 - 1) / (nfx*nfy) + 1 cmap = cm.YlOrRd cmap = cm.jet msize = self.marker_size axes = None fig = None item_fig = None nfigs = 0 alpha = 0.5 for ipar in range(npar): impl = ipar % (nfx * nfy) + 1 if impl == 1: if item_fig: yield item_fig nfigs += 1 fig = plt.figure(figsize=self.size_inch) labelpos = mpl_margins(fig, nw=nfx, nh=nfy, left=7., right=2., top=1., bottom=5., wspace=7., hspace=2., units=fontsize) item = PlotItem(name='fig_%i' % (nfigs + 1)) item.attributes['parameters'] = [] item_fig = (item, fig) par = problem.parameters[ipar] item_fig[0].attributes['parameters'].append(par.name) axes = fig.add_subplot(nfy, nfx, impl) labelpos(axes, 2.5, 2.0) axes.set_ylabel(par.get_label()) axes.get_yaxis().set_major_locator(plt.MaxNLocator(4)) config_axes(axes, nfx, nfy, impl, ipar, npar + ndep + 1) axes.set_ylim(*fixlim(*par.scaled(bounds[ipar]))) axes.set_xlim(0, history.nmodels) axes.scatter(imodels[ibest], par.scaled(models[ibest, ipar]), s=msize, c=iorder[ibest], edgecolors='none', cmap=cmap, alpha=alpha, rasterized=True) if self.show_reference: axes.axhline(par.scaled(xref[ipar]), color='black', alpha=0.3) for idep in range(ndep): # ifz, ify, ifx = num.unravel_index(ipar, (nfz, nfy, nfx)) impl = (npar + idep) % (nfx * nfy) + 1 if impl == 1: if item_fig: yield item_fig nfigs += 1 fig = plt.figure(figsize=self.size_inch) labelpos = mpl_margins(fig, nw=nfx, nh=nfy, left=7., right=2., top=1., bottom=5., wspace=7., hspace=2., units=fontsize) item = PlotItem(name='fig_%i' % (nfigs + 1)) item.attributes['parameters'] = [] item_fig = (item, fig) par = problem.dependants[idep] item_fig[0].attributes['parameters'].append(par.name) axes = fig.add_subplot(nfy, nfx, impl) labelpos(axes, 2.5, 2.0) axes.set_ylabel(par.get_label()) axes.get_yaxis().set_major_locator(plt.MaxNLocator(4)) config_axes(axes, nfx, nfy, impl, npar + idep, npar + ndep + 1) axes.set_ylim(*fixlim(*par.scaled(bounds[npar + idep]))) axes.set_xlim(0, history.nmodels) ys = problem.make_dependant(models[ibest, :], par.name) axes.scatter(imodels[ibest], par.scaled(ys), s=msize, c=iorder[ibest], edgecolors='none', cmap=cmap, alpha=alpha, rasterized=True) if self.show_reference: y = problem.make_dependant(xref, par.name) axes.axhline(par.scaled(y), color='black', alpha=0.3) impl = (npar + ndep) % (nfx * nfy) + 1 if impl == 1: if item_fig: yield item_fig nfigs += 1 fig = plt.figure(figsize=self.size_inch) labelpos = mpl_margins(fig, nw=nfx, nh=nfy, left=7., right=2., top=1., bottom=5., wspace=7., hspace=2., units=fontsize) item = PlotItem(name='fig_%i' % (nfigs + 1)) item.attributes['parameters'] = [] item_fig = (item, fig) axes = fig.add_subplot(nfy, nfx, impl) labelpos(axes, 2.5, 2.0) config_axes(axes, nfx, nfy, impl, npar + ndep, npar + ndep + 1) axes.set_ylim(0., 1.5) axes.set_yticks([0., 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4]) axes.set_yticklabels( ['0.0', '0.2', '0.4', '0.6', '0.8', '1', '10', '100']) axes.scatter(imodels[ibest], gms_softclip[ibest], c=iorder[ibest], s=msize, edgecolors='none', cmap=cmap, alpha=alpha) axes.axhspan(1.0, 1.5, color=(0.8, 0.8, 0.8), alpha=0.2) axes.axhline(1.0, color=(0.5, 0.5, 0.5), zorder=2) axes.set_xlim(0, history.nmodels) axes.set_xlabel('Iteration') axes.set_ylabel('Misfit') yield item_fig nfigs += 1
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.') def get_stations(self): return self.station_generator.get_stations() def get_targets(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, depth=station.depth, store_id=self.store_id, optimization='enable', interpolation='nearest_neighbor', azimuth=c_azi, dip=c_dip) targets.append(target) return 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 = {} for source in sources: for target in self.get_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_waveforms(self, engine, sources, tmin=None, tmax=None): trs = {} 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 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('Calculating waveforms between %s - %s...' % (tts(tmin, format='%Y-%m-%d_%H-%M-%S'), tts(tmax, format='%Y-%m-%d_%H-%M-%S'))) for source in sources: targets = self.get_targets() resp = engine.process(source, targets) for _, target, tr in resp.iter_results(): resp = self.get_transfer_function(target.codes) if resp: tr = tr.transfer(transfer_function=resp) trs[target.codes].add(tr) return list(trs.values()) 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 NotImplemented() def dump_data(self, engine, sources, path, tmin=None, tmax=None, overwrite=False): fns = [] fns.extend( self.dump_waveforms(engine, sources, path, tmin, tmax, overwrite)) fns.extend(self.dump_responses(path)) return fns def dump_waveforms(self, engine, sources, path, tmin=None, tmax=None, overwrite=False): path_waveforms = op.join(path, 'waveforms') util.ensuredir(path_waveforms) 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.get_useful_time_increment(engine, sources) tmin = math.floor(tmin / tinc) * tinc tmax = math.ceil(tmax / tinc) * tinc nwin = int(round((tmax - tmin) / tinc)) for iwin in range(nwin): tmin_win = max(tmin, tmin + iwin * tinc) tmax_win = min(tmax, tmin + (iwin + 1) * tinc) if tmax_win <= tmin_win: continue trs = self.get_waveforms(engine, sources, tmin_win, tmax_win) try: 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')), overwrite=overwrite) except FileSaveError as e: logger.debug('Waveform exists %s' % e) return [path_waveforms] def dump_responses(self, path): from pyrocko.io import stationxml logger.debug('Writing out StationXML...') path_responses = op.join(path, 'meta') util.ensuredir(path_responses) fn_stationxml = op.join(path_responses, 'stations.xml') 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) return [path_responses] def add_map_artists(self, engine, sources, automap): automap.add_stations(self.get_stations())
class MTLocationPlot(SectionPlotConfig): ''' MT location plot of the best solutions in three cross-sections. ''' name = 'location_mt' beachball_type = StringChoice.T( choices=['full', 'deviatoric', 'dc'], default='dc') normalisation_gamma = Float.T( default=3., help='Normalisation of colors and alpha as :math:`x^\\gamma`.' 'A linear colormap/alpha with :math:`\\gamma=1`.') def make(self, environ): environ.setup_modelling() cm = environ.get_plot_collection_manager() history = environ.get_history(subset='harvest') mpl_init(fontsize=self.font_size) self._to_be_closed = [] cm.create_group_mpl( self, self.draw_figures(history), title=u'MT Location', section='solution', feather_icon='target', description=u''' Location plot of the ensemble of best solutions in three cross-sections. The coordinate range is defined by the search space given in the config file. Symbols show best double-couple mechanisms, and colors indicate low (red) and high (blue) misfit. ''') for obj in self._to_be_closed: obj.close() def draw_figures(self, history, color_p_axis=False): from matplotlib import colors color = 'black' fontsize = self.font_size markersize = fontsize * 1.5 beachballsize_small = markersize * 0.5 beachball_type = self.beachball_type problem = history.problem sp = SectionPlot(config=self) self._to_be_closed.append(sp) fig = sp.fig axes_en = sp.axes_xy axes_dn = sp.axes_zy axes_ed = sp.axes_xz bounds = problem.get_combined_bounds() models = history.get_sorted_primary_models()[::-1] iorder = num.arange(history.nmodels) for parname, set_label, set_lim in [ ['east_shift', sp.set_xlabel, sp.set_xlim], ['north_shift', sp.set_ylabel, sp.set_ylim], ['depth', sp.set_zlabel, sp.set_zlim]]: ipar = problem.name_to_index(parname) par = problem.combined[ipar] set_label(par.get_label()) xmin, xmax = fixlim(*par.scaled(bounds[ipar])) set_lim(xmin, xmax) if 'volume_change' in problem.parameter_names: volumes = models[:, problem.name_to_index('volume_change')] volume_max = volumes.max() volume_min = volumes.min() def scale_size(source): if not hasattr(source, 'volume_change'): return beachballsize_small volume_change = source.volume_change fac = (volume_change - volume_min) / (volume_max - volume_min) return markersize * .25 + markersize * .5 * fac for axes, xparname, yparname in [ (axes_en, 'east_shift', 'north_shift'), (axes_dn, 'depth', 'north_shift'), (axes_ed, 'east_shift', 'depth')]: ixpar = problem.name_to_index(xparname) iypar = problem.name_to_index(yparname) xpar = problem.combined[ixpar] ypar = problem.combined[iypar] xmin, xmax = fixlim(*xpar.scaled(bounds[ixpar])) ymin, ymax = fixlim(*ypar.scaled(bounds[iypar])) try: axes.set_facecolor(mpl_color('aluminium1')) except AttributeError: axes.patch.set_facecolor(mpl_color('aluminium1')) rect = patches.Rectangle( (xmin, ymin), xmax-xmin, ymax-ymin, facecolor=mpl_color('white'), edgecolor=mpl_color('aluminium2')) axes.add_patch(rect) # fxs = xpar.scaled(problem.extract(models, ixpar)) # fys = ypar.scaled(problem.extract(models, iypar)) # axes.set_xlim(*fixlim(num.min(fxs), num.max(fxs))) # axes.set_ylim(*fixlim(num.min(fys), num.max(fys))) cmap = cm.ScalarMappable( norm=colors.PowerNorm( gamma=self.normalisation_gamma, vmin=iorder.min(), vmax=iorder.max()), cmap=plt.get_cmap('coolwarm')) for ix, x in enumerate(models): source = problem.get_source(x) mt = source.pyrocko_moment_tensor( store=problem.get_gf_store(problem.targets[0]), target=problem.targets[0]) fx = problem.extract(x, ixpar) fy = problem.extract(x, iypar) sx, sy = xpar.scaled(fx), ypar.scaled(fy) # TODO: Add rotation in cross-sections color = cmap.to_rgba(iorder[ix]) alpha = (iorder[ix] - iorder.min()) / \ float(iorder.max() - iorder.min()) alpha = alpha**self.normalisation_gamma try: beachball.plot_beachball_mpl( mt, axes, beachball_type=beachball_type, position=(sx, sy), size=scale_size(source), color_t=color, color_p=color if color_p_axis else 'white', alpha=alpha, zorder=1, linewidth=0.25) except beachball.BeachballError as e: logger.warn(str(e)) item = PlotItem(name='main') return [[item, fig]]
class MTLocationPlot(PlotConfig): ''' Map of moment tensor location results ''' name = 'location_mt' size_cm = Tuple.T(2, Float.T(), default=(17.5, 17.5 * (3. / 4.))) beachball_type = StringChoice.T(choices=['full', 'deviatoric', 'dc'], default='dc') 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'Moment Tensor Location', section='solution', feather_icon='target', description=u'Location plots of the best ensemble of solutions.') def draw_figures(self, history): from matplotlib import colors color = 'black' fontsize = self.font_size markersize = fontsize * 1.5 beachballsize_small = markersize * 0.5 beachball_type = self.beachball_type problem = history.problem fig = plt.figure(figsize=self.size_inch) axes_en = fig.add_subplot(2, 2, 1) axes_dn = fig.add_subplot(2, 2, 2) axes_ed = fig.add_subplot(2, 2, 3) bounds = problem.get_combined_bounds() gms = problem.combine_misfits(history.misfits) isort = num.argsort(gms)[::-1] gms = gms[isort] models = history.models[isort, :] iorder = num.arange(history.nmodels) for axes, xparname, yparname in [(axes_en, 'east_shift', 'north_shift'), (axes_dn, 'depth', 'north_shift'), (axes_ed, 'east_shift', 'depth')]: ixpar = problem.name_to_index(xparname) iypar = problem.name_to_index(yparname) xpar = problem.combined[ixpar] ypar = problem.combined[iypar] axes.set_xlabel(xpar.get_label()) axes.set_ylabel(ypar.get_label()) xmin, xmax = fixlim(*xpar.scaled(bounds[ixpar])) ymin, ymax = fixlim(*ypar.scaled(bounds[iypar])) axes.set_aspect(1.0) axes.set_xlim(xmin, xmax) axes.set_ylim(ymin, ymax) # fxs = xpar.scaled(problem.extract(models, ixpar)) # fys = ypar.scaled(problem.extract(models, iypar)) # axes.set_xlim(*fixlim(num.min(fxs), num.max(fxs))) # axes.set_ylim(*fixlim(num.min(fys), num.max(fys))) cmap = cm.ScalarMappable(norm=colors.Normalize( vmin=num.min(iorder), vmax=num.max(iorder)), cmap=plt.get_cmap('coolwarm')) for ix, x in enumerate(models): source = problem.get_source(x) mt = source.pyrocko_moment_tensor() fx = problem.extract(x, ixpar) fy = problem.extract(x, iypar) sx, sy = xpar.scaled(fx), ypar.scaled(fy) color = cmap.to_rgba(iorder[ix]) alpha = (iorder[ix] - num.min(iorder)) / \ float(num.max(iorder) - num.min(iorder)) try: beachball.plot_beachball_mpl(mt, axes, beachball_type=beachball_type, position=(sx, sy), size=beachballsize_small, color_t=color, alpha=alpha, zorder=1, linewidth=0.25) except beachball.BeachballError as e: logger.warn(str(e)) item = PlotItem(name='main', title='Moment Tensor Location') return [[item, fig]]
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 best_source = history.get_best_source() mean_source = history.get_mean_source() 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 FFIConfig(ModeConfig): regularization = StringChoice.T( default='none', choices=['laplacian', 'trans-dimensional', 'none'], help='Flag for regularization in distributed slip-optimization.')
class CMTProblem(Problem): problem_parameters = [ Parameter('time', 's', label='Time'), Parameter('north_shift', 'm', label='Northing', **as_km), Parameter('east_shift', 'm', label='Easting', **as_km), Parameter('depth', 'm', label='Depth', **as_km), Parameter('magnitude', label='Magnitude'), Parameter('rmnn', label='$m_{nn} / M_0$'), Parameter('rmee', label='$m_{ee} / M_0$'), Parameter('rmdd', label='$m_{dd} / M_0$'), Parameter('rmne', label='$m_{ne} / M_0$'), Parameter('rmnd', label='$m_{nd} / M_0$'), Parameter('rmed', label='$m_{ed} / M_0$'), Parameter('duration', 's', label='Duration') ] dependants = [ Parameter('strike1', u'\u00b0', label='Strike 1'), Parameter('dip1', u'\u00b0', label='Dip 1'), Parameter('rake1', u'\u00b0', label='Rake 1'), Parameter('strike2', u'\u00b0', label='Strike 2'), Parameter('dip2', u'\u00b0', label='Dip 2'), Parameter('rake2', u'\u00b0', label='Rake 2'), Parameter('rel_moment_iso', label='$M_{0}^{ISO}/M_{0}$'), Parameter('rel_moment_clvd', label='$M_{0}^{CLVD}/M_{0}$') ] distance_min = Float.T(default=0.0) mt_type = StringChoice.T(default='full', choices=['full', 'deviatoric', 'dc']) def __init__(self, **kwargs): Problem.__init__(self, **kwargs) self.deps_cache = {} def get_source(self, x): d = self.get_parameter_dict(x) rm6 = num.array([d.rmnn, d.rmee, d.rmdd, d.rmne, d.rmnd, d.rmed], dtype=num.float) m0 = mtm.magnitude_to_moment(d.magnitude) m6 = rm6 * m0 p = {} for k in self.base_source.keys(): if k in d: p[k] = float(self.ranges[k].make_relative( self.base_source[k], d[k])) stf = gf.HalfSinusoidSTF(duration=float(d.duration)) source = self.base_source.clone(m6=m6, stf=stf, **p) return source def make_dependant(self, xs, pname): cache = self.deps_cache if xs.ndim == 1: return self.make_dependant(xs[num.newaxis, :], pname)[0] if pname not in self.dependant_names: raise KeyError(pname) mt = self.base_source.pyrocko_moment_tensor() sdrs_ref = mt.both_strike_dip_rake() y = num.zeros(xs.shape[0]) for i, x in enumerate(xs): k = tuple(x.tolist()) if k not in cache: source = self.get_source(x) mt = source.pyrocko_moment_tensor() res = mt.standard_decomposition() sdrs = mt.both_strike_dip_rake() if sdrs_ref: sdrs = mtm.order_like(sdrs, sdrs_ref) cache[k] = mt, res, sdrs mt, res, sdrs = cache[k] if pname == 'rel_moment_iso': ratio_iso, m_iso = res[0][1:3] y[i] = ratio_iso * num.sign(m_iso[0, 0]) elif pname == 'rel_moment_clvd': ratio_clvd, m_clvd = res[2][1:3] evals, evecs = mtm.eigh_check(m_clvd) ii = num.argmax(num.abs(evals)) y[i] = ratio_clvd * num.sign(evals[ii]) else: isdr = {'strike': 0, 'dip': 1, 'rake': 2}[pname[:-1]] y[i] = sdrs[int(pname[-1]) - 1][isdr] return y def pack(self, source): m6 = source.m6 mt = source.pyrocko_moment_tensor() rm6 = m6 / mt.scalar_moment() x = num.array([ source.time - self.base_source.time, source.north_shift, source.east_shift, source.depth, mt.moment_magnitude(), ] + rm6.tolist() + [source.stf.duration], dtype=num.float) return x def random_uniform(self, xbounds): x = num.zeros(self.nparameters) for i in range(self.nparameters): x[i] = num.random.uniform(xbounds[i, 0], xbounds[i, 1]) x[5:11] = mtm.random_m6() return x.tolist() def preconstrain(self, x): d = self.get_parameter_dict(x) m6 = num.array([d.rmnn, d.rmee, d.rmdd, d.rmne, d.rmnd, d.rmed], dtype=num.float) m9 = mtm.symmat6(*m6) if self.mt_type == 'deviatoric': trace_m = num.trace(m9) m_iso = num.diag([trace_m / 3., trace_m / 3., trace_m / 3.]) m9 -= m_iso elif self.mt_type == 'dc': mt = mtm.MomentTensor(m=m9) m9 = mt.standard_decomposition()[1][2] m0_unscaled = math.sqrt(num.sum(m9.A**2)) / math.sqrt(2.) m9 /= m0_unscaled m6 = mtm.to6(m9) d.rmnn, d.rmee, d.rmdd, d.rmne, d.rmnd, d.rmed = m6 x = self.get_parameter_array(d) source = self.get_source(x) for t in self.targets: if (self.distance_min > num.asarray(t.distance_to(source))).any(): raise Forbidden() return x def get_dependant_bounds(self): out = [(0., 360.), (0., 90.), (-180., 180.), (0., 360.), (0., 90.), (-180., 180.), (-1., 1.), (-1., 1.)] return out @classmethod def get_plot_classes(cls): from .. import plot plots = super(CMTProblem, cls).get_plot_classes() plots.extend([ plot.HudsonPlot, plot.MTDecompositionPlot, plot.MTLocationPlot, plot.MTFuzzyPlot ]) return plots