/*! \brief Save the VS Magnitude in the preferred origin of the event Update the preferred origin object of the event and send a message to the master so the magnitude can be saved to the database. \param event The sc3 event to which the magnitude belongs \param mag The magnitude to be added to the event \param stacnt Station Count (number of stations used to calculate the magnitude) */ void VsMagnitude::updateVSMagnitude(Event *event, VsEvent *vsevt) { SEISCOMP_DEBUG( "%s: %s: VS = %.4f", event->publicID().c_str(), _currentTime.toString("%FT%T.%fZ").c_str(), *vsevt->vsMagnitude); SEISCOMP_DEBUG("Update number is %d", vsevt->update); OriginPtr org = _cache.get<Origin>(event->preferredOriginID()); if ( org == NULL ) { SEISCOMP_WARNING("Object %s not found in cache\nIs the cache size big enough?\n" "Have you subscribed to all necessary message groups?", event->preferredOriginID().c_str()); return; } Notifier::SetEnabled(true); _creationInfo.setCreationTime(_currentTime); _creationInfo.setModificationTime(Core::None); _creationInfo.setVersion(Core::toString(vsevt->update)); MagnitudePtr nmag = Magnitude::Create(); nmag->setMagnitude(RealQuantity(*vsevt->vsMagnitude)); nmag->setType("MVS"); nmag->setStationCount(vsevt->vsStationCount); nmag->setCreationInfo(_creationInfo); org->add(nmag.get()); for (StaMagArray::iterator it = vsevt->staMags.begin(); it != vsevt->staMags.end(); ++it) { const DataModel::StationMagnitude *staMag = (*it).get(); nmag->add(new StationMagnitudeContribution(staMag->publicID(),staMag->magnitude().value()- *vsevt->vsMagnitude,1.0)); } vsevt->staMags.clear(); /// set a comment containing the update number /// if the update numbers of two successive comments for the /// same event are identical signals the logging script that /// the event processing has finished setComments(nmag.get(), "update", vsevt->update); /// set the likelihood of the estimate as a comment setComments(nmag.get(), "likelihood", vsevt->likelihood); /// send the message containing the vs magnitude and the /// two comments to scmaster Core::MessagePtr msg = Notifier::GetMessage(); if ( connection() && msg ) connection()->send(msg.get()); Notifier::SetEnabled(false); }
/*! \brief Process one event This is called by processEvents() when iterating over all events in the cache */ void VsMagnitude::process(VsEvent *evt, Event *event) { if ( evt->stations.empty() ) return; Client::Inventory *inv = Client::Inventory::Instance(); double stmag; double distdg, epicdist, azi1, azi2; WaveformStreamID wid; ReturnCode ret; Timeline::StationList unused; evt->allThresholdStationsCount = 0; vs.seteqlat(evt->lat); vs.seteqlon(evt->lon); vector<VsInput> inputs; SEISCOMP_LOG(_processingInfoChannel, "Start logging for event: %s", event->publicID().c_str()); SEISCOMP_LOG(_processingInfoChannel, "update number: %d", evt->update); OriginPtr org = _cache.get<Origin>(event->preferredOriginID()); if ( !org ) { SEISCOMP_WARNING("Object %s not found in cache\nIs the cache size big enough?\n" "Have you subscribed to all necessary message groups?", event->preferredOriginID().c_str()); return; } evt->staMags.clear(); VsWindows::iterator it; for ( it = evt->stations.begin(); it != evt->stations.end(); ++it ) { Envelope venv, henv; Core::Time vtime, htime; string locationCode, channelCode; ret = _timeline.maxmimum(it->first, it->second.startTime(), it->second.endTime(), it->second.pickTime(), venv, vtime, henv, htime, locationCode, channelCode); if ( no_data == ret ) { SEISCOMP_WARNING("No data available for %s.%s.%s", it->first.first.c_str(), it->first.second.c_str(),locationCode.c_str()); unused.insert(it->first); continue; } DataModel::SensorLocation *loc; loc = inv->getSensorLocation(it->first.first, it->first.second, locationCode, it->second.pickTime()); if ( loc == NULL ) { SEISCOMP_WARNING( "%s.%s.%s: sensor location not in inventory: ignoring", it->first.first.c_str(), it->first.second.c_str(), locationCode.c_str()); continue; } Math::Geo::delazi(evt->lat, evt->lon, loc->latitude(), loc->longitude(), &distdg, &azi1, &azi2); epicdist = Math::Geo::deg2km(distdg); // if data is clipped or not enough to use it for magnitude // computation add the station to the overall count and then continue if ( not_enough_data == ret || clipped_data == ret) { SEISCOMP_WARNING("Not enough data available for %s.%s.%s", it->first.first.c_str(), it->first.second.c_str(),locationCode.c_str()); unused.insert(it->first); continue; } // catch remaining errors if ( index_error == ret || undefined_problem == ret) continue; if ( _maxepicdist > 0 ) { if( epicdist > _maxepicdist ) continue; } inputs.resize(inputs.size() + 1); VsInput &input = inputs.back(); input.lat = (float) loc->latitude(); input.lon = (float) loc->longitude(); SoilClass soilClass; float ca = siteEffect(input.lat, input.lon, std::max(venv.values[Acceleration], henv.values[Acceleration]), Acceleration, soilClass); float cv = siteEffect(input.lat, input.lon, std::max(venv.values[Velocity], henv.values[Velocity]), Velocity, soilClass); float cd = siteEffect(input.lat, input.lon, std::max(venv.values[Displacement], henv.values[Displacement]), Displacement, soilClass); // Convert from m to cm and apply site effect correction input.ZA = (float) (venv.values[Acceleration] / ca) * 100; input.ZV = (float) (venv.values[Velocity] / cv) * 100; input.ZD = (float) (venv.values[Displacement] / cd) * 100; input.HA = (float) (henv.values[Acceleration] / ca) * 100; input.HV = (float) (henv.values[Velocity] / cv) * 100; input.HD = (float) (henv.values[Displacement] / cd) * 100; input.PSclass = vs.psclass(input.ZA, input.ZV, input.HA, input.HV) == 0 ? P_Wave : S_Wave; input.SOILclass = soilClass; input.mest = vs.mest(vs.ground_motion_ratio(input.ZA, input.ZD), input.PSclass); // Record single station magnitudes Notifier::SetEnabled(true); _creationInfo.setCreationTime(_currentTime); _creationInfo.setModificationTime(Core::None); DataModel::StationMagnitudePtr staMag = DataModel::StationMagnitude::Create(); staMag->setMagnitude(RealQuantity(input.mest)); staMag->setType("MVS"); staMag->setCreationInfo(_creationInfo); wid.setNetworkCode(it->first.first); wid.setStationCode(it->first.second); wid.setLocationCode(locationCode); wid.setChannelCode(channelCode); staMag->setWaveformID(wid); org->add(staMag.get()); evt->staMags.push_back(staMag); Notifier::SetEnabled(false); // Logging string resultstr; ostringstream out; out.precision(2); out.setf(ios::fixed, ios::floatfield); out << "Sensor: " << it->first.first << "." << locationCode << "."; out << it->first.second << "." << channelCode << "; "; out << "Wavetype: " << std::string(input.PSclass.toString()) << "; "; out << "Soil class: " << std::string(input.SOILclass.toString()) << "; "; out << "Magnitude: " << input.mest; resultstr = out.str(); out.str(""); SEISCOMP_LOG(_processingInfoChannel, "%s", resultstr.c_str()); out.precision(2); out.setf(ios::fixed, ios::floatfield); out << "station lat: " << input.lat << "; station lon: " << input.lon; out << "; epicentral distance: " << epicdist << ";"; resultstr = out.str(); out.str(""); SEISCOMP_LOG(_processingInfoChannel, "%s", resultstr.c_str()); out.precision(2); out.setf(ios::scientific, ios::floatfield); out << "PGA(Z): " << input.ZA / 100. << "; PGV(Z): " << input.ZV / 100.; out << "; PGD(Z): " << input.ZD / 100.; resultstr = out.str(); out.str(""); SEISCOMP_LOG(_processingInfoChannel, "%s", resultstr.c_str()); out << "PGA(H): " << input.HA / 100. << "; PGV(H): " << input.HV / 100.; out << "; PGD(H): " << input.HD / 100.; resultstr = out.str(); SEISCOMP_LOG(_processingInfoChannel, "%s", resultstr.c_str()); } if ( inputs.empty() ) { SEISCOMP_LOG(_processingInfoChannel, "End logging for event: %s", event->publicID().c_str()); return; } // Grid search float mag = 0.5f; float minL = -1.0f; float minMag = mag; while ( mag <= 9.0f ) { float L = 0.0f; for ( size_t i = 0; i < inputs.size(); ++i ) { const VsInput &input = inputs[i]; // Likelihood vs.setmag(mag); L += vs.likelihood(input.ZA, input.ZV, input.ZD, input.HA, input.HV, input.HD, input.PSclass, input.SOILclass, input.lat, input.lon); } if ( minL < 0 || minL > L ) { minL = L; minMag = mag; } mag += 0.01f; } // calculate the median of all station magnitudes size_t size = inputs.size(); double *mestarray = new double[size]; for ( size_t i = 0; i < size; ++i ) { const VsInput &input = inputs[i]; mestarray[i] = input.mest; } nth_element(mestarray, mestarray + size / 2, mestarray + size); stmag = mestarray[size / 2]; delete[] mestarray; // TODO: Define errors evt->vsMagnitude = minMag; evt->vsStationCount = inputs.size(); if ( _timeline.pollbuffer(evt->lat, evt->lon,evt->dthresh,evt->allThresholdStationsCount) != no_problem) { SEISCOMP_WARNING("Problems in the buffer polling function."); return; } // Use quality control functions to decide if the event is valid evt->isValid = false; double deltamag; double deltapick; if ( isEventValid(stmag, evt, evt->likelihood, deltamag, deltapick) ) { evt->isValid = true; } // logging string resultstr; ostringstream out; out.precision(2); out.setf(ios::fixed, ios::floatfield); out << "VS-mag: " << minMag << "; median single-station-mag: " << stmag; out << "; lat: " << evt->lat << "; lon: " << evt->lon; out << "; depth : " << evt->dep << " km"; resultstr = out.str(); out.str(""); SEISCOMP_LOG(_processingInfoChannel, "%s", resultstr.c_str()); out << "creation time: " << _currentTime.toString("%FT%T.%2fZ"); out << "; origin time: " << evt->time.toString("%FT%T.%2fZ"); Core::TimeSpan difftime = _currentTime - evt->time; Core::Time now = Core::Time::GMT(); Core::TimeSpan difftime_oa = now - evt->originArrivalTime; Core::TimeSpan difftime_ct = now - evt->originCreationTime; out << "; t-diff: " << difftime.length(); out.precision(3); out << "; time since origin arrival: " << difftime_oa.length(); out << "; time since origin creation: " << difftime_ct.length(); resultstr = out.str(); out.str(""); SEISCOMP_LOG(_processingInfoChannel, "%s", resultstr.c_str()); out << "# picked stations: " << evt->pickedStationsCount; // all stations with picks out << "; # envelope streams: " << _timeline.StreamCount(); // all stations with envelope streams resultstr = out.str(); out.str(""); SEISCOMP_LOG(_processingInfoChannel, "%s", resultstr.c_str()); // distance threshold for delta-pick quality criteria out.precision(2); out << "Distance threshold (dt): " << Math::Geo::deg2km(evt->dthresh) << " km"; out << "; # picked stations < dt: " << evt->pickedThresholdStationsCount; out << "; # envelope streams < dt: " << evt->allThresholdStationsCount; resultstr = out.str(); out.str(""); SEISCOMP_LOG(_processingInfoChannel, "%s", resultstr.c_str()); if (evt->pickedStationsCount > evt->vsStationCount) { out << "Stations not used for VS-mag: "; // find picked stations that don't contribute to the VS magnitude Timeline::StationList &sl = evt->pickedStations; for (Timeline::StationList::iterator it=sl.begin(); it!=sl.end(); ++it) { if ( evt->stations.find(*it) == evt->stations.end() || unused.find(*it) != unused.end()) { out << (*it).first << '.' << (*it).second << ' '; } } resultstr = out.str(); out.str(""); SEISCOMP_LOG(_processingInfoChannel, "%s", resultstr.c_str()); } out.precision(3); out << "Magnitude check: " << deltamag << "; Arrivals check: " << deltapick; out << "; Azimuthal gap: " << evt->azGap; resultstr = out.str(); out.str(""); SEISCOMP_LOG(_processingInfoChannel, "%s", resultstr.c_str()); out.precision(2); out << "likelihood: " << evt->likelihood; resultstr = out.str(); out.str(""); SEISCOMP_LOG(_processingInfoChannel, "%s", resultstr.c_str()); SEISCOMP_LOG(_processingInfoChannel, "End logging for event: %s", event->publicID().c_str()); }