Ejemplo n.º 1
0
/*!
 \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);
}
Ejemplo n.º 2
0
/*!
 \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());
}