def model_prediction_sensitivity(engine, *args, **kwargs): ''' Calculate the model prediction Covariance Sensitivity Kernel. (numerical derivation with respect to the input source parameter(s)) Following Duputel et al. 2014 :Input: :py:class:'engine' source_parms = list of parameters with respect to which the kernel is being calculated e.g. ['strike', 'dip', 'depth'] !!! NEEDS to have seismosizer source object parameter variable name convention !!! (see seismosizer.source.keys()) calculate_model_prediction_sensitivity(request, source_params, **kwargs) calculate_model_prediction_sensitivity(sources, targets, source_params, **kwargs) Returns traces in a list[parameter][targets] for each station and channel as specified in the targets. The location code of each trace is placed to show the respective source parameter. ''' if len(args) not in (0, 1, 2, 3): raise gf.BadRequest('invalid arguments') if len(args) == 2: kwargs['request'] = args[0] kwargs['source_params'] = args[1] elif len(args) == 3: kwargs.update(gf.Request.args2kwargs(args[0:1])) kwargs['source_params'] = args[2] request = kwargs.pop('request', None) nprocs = kwargs.pop('nprocs', 1) source_params = kwargs.pop('source_params', None) h = kwargs.pop('h', None) if request is None: request = gf.Request(**kwargs) if h is None: h = num.ones(len(source_params)) * 1e-1 # create results list sensitivity_param_list = [] sensitivity_param_trcs = [] for i in xrange(len(source_params)): sensitivity_param_list.append([0] * len(request.targets)) sensitivity_param_trcs.append([0] * len(request.targets)) for ref_source in request.sources: par_count = 0 for param in source_params: print param, 'with h = ', h[par_count] calc_source_p2h = ref_source.clone() calc_source_ph = ref_source.clone() calc_source_mh = ref_source.clone() calc_source_m2h = ref_source.clone() setattr(calc_source_p2h, param, ref_source[param] + (2 * h[par_count])) setattr(calc_source_ph, param, ref_source[param] + (h[par_count])) setattr(calc_source_mh, param, ref_source[param] - (h[par_count])) setattr(calc_source_m2h, param, ref_source[param] - (2 * h[par_count])) calc_sources = [ calc_source_p2h, calc_source_ph, calc_source_mh, calc_source_m2h ] response = engine.process(sources=calc_sources, targets=request.targets, nprocs=nprocs) for k in xrange(len(request.targets)): # zero padding if necessary trc_lengths = num.array( [len(response.results_list[i][k].trace.data) for i in \ range(len(response.results_list))]) Id = num.where(trc_lengths != trc_lengths.max()) for l in Id[0]: response.results_list[l][k].trace.data = num.concatenate( (response.results_list[l][k].trace.data, num.zeros(trc_lengths.max() - trc_lengths[l]))) # calculate numerical partial derivative for # each source and target sensitivity_param_list[par_count][k] = ( sensitivity_param_list[par_count][k] + (\ - response.results_list[0][k].trace.data + \ 8 * response.results_list[1][k].trace.data - \ 8 * response.results_list[2][k].trace.data + \ response.results_list[3][k].trace.data) / \ (12 * h[par_count]) ) par_count = par_count + 1 # form traces from sensitivities par_count = 0 for param in source_params: for k in xrange(len(request.targets)): sensitivity_param_trcs[par_count][k] = trace.Trace( network=request.targets[k].codes[0], station=request.targets[k].codes[1], ydata=sensitivity_param_list[par_count][k], deltat=response.results_list[0][k].trace.deltat, tmin=response.results_list[0][k].trace.tmin, channel=request.targets[k].codes[3], location=param) par_count = par_count + 1 return sensitivity_param_trcs
def call(self): '''Main work routine of the snuffling.''' self.cleanup() # get time range visible in viewer viewer = self.get_viewer() event = viewer.get_active_event() if event: event, stations = self.get_active_event_and_stations( missing='warn') else: # event = model.Event(lat=self.lat, lon=self.lon) event = model.Event(lat=0., lon=0.) stations = [] stations = self.get_stations() s2c = {} for traces in self.chopper_selected_traces(fallback=True, mode='visible'): for tr in traces: net, sta, loc, cha = tr.nslc_id ns = net, sta if ns not in s2c: s2c[ns] = set() s2c[ns].add((loc, cha)) if not stations: stations = [] for (lat, lon) in [(5., 0.), (-5., 0.)]: s = model.Station(station='(%g, %g)' % (lat, lon), lat=lat, lon=lon) stations.append(s) viewer.add_stations(stations) for s in stations: ns = s.nsl()[:2] if ns not in s2c: s2c[ns] = set() for cha in 'NEZ': s2c[ns].add(('', cha)) source = gf.RectangularSource(time=event.time + self.time, lat=event.lat, lon=event.lon, north_shift=self.north_km * km, east_shift=self.east_km * km, depth=self.depth_km * km, magnitude=self.magnitude, strike=self.strike, dip=self.dip, rake=self.rake, length=self.length, width=self.width, nucleation_x=self.nucleation_x, velocity=self.velocity, stf=self.get_stf()) source.regularize() m = EventMarker(source.pyrocko_event()) self.add_marker(m) targets = [] if self.store_id == '<not loaded yet>': self.fail('Select a GF Store first') for station in stations: nsl = station.nsl() if nsl[:2] not in s2c: continue for loc, cha in s2c[nsl[:2]]: target = gf.Target(codes=(station.network, station.station, loc + '-syn', cha), quantity='displacement', lat=station.lat, lon=station.lon, depth=station.depth, store_id=self.store_id, optimization='enable', interpolation='nearest_neighbor') _, bazi = source.azibazi_to(target) if cha.endswith('T'): dip = 0. azi = bazi + 270. elif cha.endswith('R'): dip = 0. azi = bazi + 180. elif cha.endswith('1'): dip = 0. azi = 0. elif cha.endswith('2'): dip = 0. azi = 90. else: dip = None azi = None target.azimuth = azi target.dip = dip targets.append(target) req = gf.Request(sources=[source], targets=targets) req.regularize() try: resp = self.get_engine().process(req) except (gf.meta.OutOfBounds, gf.store_ext.StoreExtError) as e: self.fail(e) traces = resp.pyrocko_traces() if self.waveform_type.startswith('Velocity'): for tr in traces: tr.set_ydata(num.diff(tr.ydata) / tr.deltat) elif self.waveform_type.startswith('Acceleration'): for tr in traces: tr.set_ydata(num.diff(num.diff(tr.ydata)) / tr.deltat**2) if self.waveform_type.endswith('[nm]') or \ self.waveform_type.endswith('[nm/s]') or \ self.waveform_type.endswith('[nm/s^2]'): for tr in traces: tr.set_ydata(tr.ydata * 1e9) self.add_traces(traces)